OpenCores
URL https://opencores.org/ocsvn/open8_urisc/open8_urisc/trunk

Subversion Repositories open8_urisc

[/] [open8_urisc/] [trunk/] [VHDL/] [o8_trig_delay.vhd] - Rev 292

Compare with Previous | Blame | View Log

-- Copyright (c)2020 Jeremy Seth Henry
-- All rights reserved.
--
-- Redistribution and use in source and binary forms, with or without
-- modification, are permitted provided that the following conditions are met:
--     * Redistributions of source code must retain the above copyright
--       notice, this list of conditions and the following disclaimer.
--     * Redistributions in binary form must reproduce the above copyright
--       notice, this list of conditions and the following disclaimer in the
--       documentation and/or other materials provided with the distribution,
--       where applicable (as part of a user interface, debugging port, etc.)
--
-- THIS SOFTWARE IS PROVIDED BY JEREMY SETH HENRY ``AS IS'' AND ANY
-- EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
-- WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
-- DISCLAIMED. IN NO EVENT SHALL JEREMY SETH HENRY BE LIABLE FOR ANY
-- DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
-- (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
-- LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
-- ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-- (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
-- SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
--
-- VHDL Entity: o8_trig_delay
-- Description: Programmable delay timer with time-base selection. Allows both
--               the delay after triggering and pulse width to be set by
--               software. Output may either be routed to a pin or used to
--               trigger an interrupt.
--
-- Register Map:
-- Offset  Bitfield Description                        Read/Write
--   0x0   AAAAAAAA Delay Time  Byte 0                     (RW)
--   0x1   AAAAAAAA Delay Time  Byte 1                     (RW)
--   0x2   AAAAAAAA Delay Time  Byte 2                     (RW)
--   0x3   AAAAAAAA Pulse Width Byte 0                     (RW)
--   0x4   AAAAAAAA Pulse Width Byte 1                     (RW)
--   0x5   AAAAAAAA Pulse Width Byte 2                     (RW)
--   0x6   FEDCBBA- Timer Configuration                    (RW*)
--                  A: Global Interrupt Enable
--                  B: Interrupt Select
--                   00 - Interrupt on trigger input (pre-arm check)
--                   01 - Interrupt on trigger event (post-arm check)
--                   10 - Interrupt on delay done
--                   11 - Interrupt on pulse done
--                  C: Trigger Edge
--                    0 - Trigger on falling edge
--                    1 - Trigger on rising edge
--                  D: Automatic Re-Arm (enabled if 1)
--                  E: Time base locked (okay if 1)     (read-only)
--                  F: Time base source
--                    0 - Use the internal uSec_Tick pulse
--                    1 - Use an external clock source
--   0x7   FEDCB--A Timer Control                          (RW*)
--                  A: External Trigger Input State     (read-only)
--                  B: Issue Internal Trigger           (one-shot)
--                     Returns '0' on read
--                  C: Current output level             (read-only)
--                  D: Clear/Re-Arm on '1'              (one-shot)
--                     Trigger event status on read
--                  E: Disable/Safe Trigger             (one-shot)
--                     Returns '0' on read
--                  F: Enable/Arm Trigger               (one-shot)
--                     Trigger armed status on read
--
-- Revision History
-- Author          Date     Change
------------------ -------- ---------------------------------------------------
-- Seth Henry      05/14/20 Design start
-- Seth Henry      05/18/20 Added write qualification input
-- Seth Henry      05/27/21 Added internal trigger function
-- Seth Henry      05/27/21 Moved the arming logic to later in the trigger to
--                           allow premature trigger detection
-- Seth Henry      06/15/21 Added a global interrupt enable and modified the
--                           interrupt to use either the pre- or post-arm
--                           trigger input
 
library ieee;
use ieee.std_logic_1164.all;
  use ieee.std_logic_unsigned.all;
  use ieee.std_logic_arith.all;
  use ieee.std_logic_misc.all;
 
library work;
  use work.open8_pkg.all;
 
entity o8_trig_delay is
generic(
  Default_Delay              : std_logic_vector(23 downto 0) := x"000000";
  Default_Width              : std_logic_vector(23 downto 0) := x"000000";
  Default_Timebase           : std_logic := '0';
  Default_Auto_ReArm         : std_logic := '0';
  Default_Trigger_Edge       : std_logic := '1';
  Default_Int_Source         : std_logic_vector(1 downto 0) := "00";
  Default_Int_Enable         : std_logic := '0';
  Address                    : ADDRESS_TYPE
);
port(
  Open8_Bus                  : in  OPEN8_BUS_TYPE;
  Write_Qual                 : in  std_logic := '1';
  Rd_Data                    : out DATA_TYPE;
  Interrupt                  : out std_logic;
  --
  Time_Base_Clock            : in  std_logic := '0';
  Time_Base_Locked           : in  std_logic := '1';
  --
  Ext_Trig                   : in  std_logic;
  Timer_Out                  : out std_logic
);
end entity;
 
architecture behave of o8_trig_delay is
 
  alias Clock                is Open8_Bus.Clock;
  alias Reset                is Open8_Bus.Reset;
  alias uSec_Tick            is Open8_Bus.uSec_Tick;
  alias Wr_Data_d            is Open8_Bus.Wr_Data;
 
  constant User_Addr         : std_logic_vector(15 downto 3) :=
                                Address(15 downto 3);
  alias  Comp_Addr           is Open8_Bus.Address(15 downto 3);
  alias  Reg_Sel_d           is Open8_Bus.Address(2 downto 0);
 
  signal Addr_Match          : std_logic := '0';
  signal Reg_Sel_q           : std_logic_vector(2 downto 0) := "000";
  signal Wr_En_d             : std_logic := '0';
  signal Wr_En_q             : std_logic := '0';
  signal Wr_Data_q           : DATA_TYPE := x"00";
  signal Rd_En_d             : std_logic := '0';
  signal Rd_En_q             : std_logic := '0';
 
  -- Configuration Registers
 
  signal Pulse_Delay         : std_logic_vector(23 downto 0) := x"000000";
  alias  Pulse_Delay_B0      is Pulse_Delay( 7 downto  0);
  alias  Pulse_Delay_B1      is Pulse_Delay(15 downto  8);
  alias  Pulse_Delay_B2      is Pulse_Delay(23 downto  16);
 
  signal Pulse_Width         : std_logic_vector(23 downto 0) := x"000000";
  alias  Pulse_Width_B0      is Pulse_Width( 7 downto  0);
  alias  Pulse_Width_B1      is Pulse_Width(15 downto  8);
  alias  Pulse_Width_B2      is Pulse_Width(23 downto  16);
 
  signal Time_Base_Source    : std_logic := '0';
  signal Time_Base_Status    : std_logic := '0';
  signal Auto_ReArm          : std_logic := '0';
  signal Trigger_Edge        : std_logic := '0';
  signal Interrupt_Select    : std_logic_vector(1 downto 0);
  signal Interrupt_Enable    : std_logic := '0';
 
  signal Interrupt_Src       : std_logic := '0';
 
  signal Int_Trig            : std_logic := '0';
  signal Arm_Timer           : std_logic := '0';
  signal Safe_Timer          : std_logic := '0';
  signal Clear_Trigd         : std_logic := '0';
 
  -- Time Base signals
  signal Ext_TBC_SR          : std_logic_vector(3 downto 0) := "0000";
  signal Ext_TBL_SR          : std_logic_vector(3 downto 0) := "0000";
 
  signal Timer_Tick          : std_logic := '0';
 
  -- Trigger signals
  signal Ext_Trig_SR         : std_logic_vector(3 downto 0) := "0000";
  signal Trig_RE             : std_logic := '0';
  signal Trig_FE             : std_logic := '0';
  signal Trigger_In          : std_logic := '0';
  signal Trigger_Armed       : std_logic := '0';
  signal Trigger_Event       : std_logic := '0';
  signal Trigger_Event_q     : std_logic := '0';
  signal Delay_Trig          : std_logic := '0';
 
  -- Delay Timer signals
  signal Delay_Pending       : std_logic := '0';
  signal Delay_Tmr           : std_logic_vector(23 downto 0) := x"000000";
  signal Delay_Tmr_SR        : std_logic_vector(1 downto 0);
  signal Width_Trig          : std_logic := '0';
 
  -- Pulse Timer signals
  signal Width_Tmr           : std_logic_vector(23 downto 0) := x"000000";
  signal Width_Tmr_SR        : std_logic_vector(1 downto 0);
  signal Pulse_Out           : std_logic := '0';
  signal Pulse_Done          : std_logic := '0';
 
begin
 
  Timer_Out                  <= Pulse_Out;
 
  Addr_Match                 <= '1' when Comp_Addr = User_Addr else '0';
  Wr_En_d                    <= Addr_Match and Open8_Bus.Wr_En;
  Rd_En_d                    <= Addr_Match and Open8_Bus.Rd_En;
 
  io_reg: process( Clock, Reset )
  begin
    if( Reset = Reset_Level )then
      Reg_Sel_q              <= (others => '0');
      Wr_En_q                <= '0';
      Wr_Data_q              <= (others => '0');
      Rd_En_q                <= '0';
      Rd_Data                <= OPEN8_NULLBUS;
      Pulse_Delay            <= Default_Delay;
      Pulse_Width            <= Default_Width;
      Time_Base_Source       <= Default_Timebase;
      Auto_ReArm             <= Default_Auto_ReArm;
      Trigger_Edge           <= Default_Trigger_Edge;
      Interrupt_Select       <= Default_Int_Source;
      Interrupt_Enable       <= Default_Int_Enable;
      Int_Trig               <= '0';
      Arm_Timer              <= '0';
      Safe_Timer             <= '0';
      Clear_Trigd            <= '0';
      Interrupt              <= '0';
    elsif( rising_edge( Clock ) )then
      Reg_Sel_q              <= Reg_Sel_d;
 
      Wr_En_q                <= Wr_En_d;
      Wr_Data_q              <= Wr_Data_d;
 
      Int_Trig               <= '0';
      Arm_Timer              <= '0';
      Safe_Timer             <= '0';
      Clear_Trigd            <= '0';
 
      if( Wr_En_q = '1' and Write_Qual = '1' )then
        case( Reg_Sel_q )is
          when "000" =>
            Pulse_Delay_B0   <= Wr_Data_q;
          when "001" =>
            Pulse_Delay_B1   <= Wr_Data_q;
          when "010" =>
            Pulse_Delay_B2   <= Wr_Data_q;
 
          when "011" =>
            Pulse_Width_B0   <= Wr_Data_q;
          when "100" =>
            Pulse_Width_B1   <= Wr_Data_q;
          when "101" =>
            Pulse_Width_B2   <= Wr_Data_q;
 
          when "110" =>
            Time_Base_Source <= Wr_Data_q(7);
            -- Reserved for status bit
            Auto_ReArm       <= Wr_Data_q(5);
            Trigger_Edge     <= Wr_Data_q(4);
            Interrupt_Select <= Wr_Data_q(3 downto 2);
            Interrupt_Enable <= Wr_Data_q(1);
 
          when "111" =>
            Arm_Timer        <= Wr_Data_q(7);
            Safe_Timer       <= Wr_Data_q(6);
            Clear_Trigd      <= Wr_Data_q(5);
            Int_Trig         <= Wr_Data_q(3);
          when others => null;
        end case;
      end if;
 
      Rd_Data                <= OPEN8_NULLBUS;
      Rd_En_q                <= Rd_En_d;
      if( Rd_En_q = '1' )then
        case( Reg_Sel_q )is
          when "000" =>
            Rd_Data          <= Pulse_Delay_B0;
          when "001" =>
            Rd_Data          <= Pulse_Delay_B1;
          when "010" =>
            Rd_Data          <= Pulse_Delay_B2;
 
          when "011" =>
            Rd_Data          <= Pulse_Width_B0;
          when "100" =>
            Rd_Data          <= Pulse_Width_B1;
          when "101" =>
            Rd_Data          <= Pulse_Width_B2;
 
          when "110" =>
            Rd_Data          <= Time_Base_Source & -- Bit 7
                                Time_Base_Status & -- Bit 6
                                Auto_ReArm &       -- Bit 5
                                Trigger_Edge &     -- Bit 4
                                Interrupt_Select & -- Bits 3:2
                                "00";              -- Bits 1:0
          when "111" =>
            Rd_Data          <= Trigger_Armed &    -- Bit 7
                                '0' &              -- Bit 6
                                Trigger_Event &    -- Bit 5
                                Pulse_Out &        -- Bit 4
                                "000" &            -- Bits 3:1
                                Ext_Trig_SR(3);    -- Bit 0
          when others => null;
        end case;
      end if;
 
      case( Interrupt_Select )is
        when "00" =>
          Interrupt_Src      <= Trigger_In;
        when "01" =>
          Interrupt_Src      <= Delay_Trig;
        when "10" =>
          Interrupt_Src      <= Width_Trig;
        when "11" =>
          Interrupt_Src      <= Pulse_Done;
        when others =>
          null;
      end case;
 
      Interrupt              <= Interrupt_Src and Interrupt_Enable;
 
    end if;
  end process;
 
  Time_Base_proc: process( Clock, Reset )
  begin
    if( Reset = Reset_Level )then
      Ext_TBC_SR             <= (others => '0');
      Ext_TBL_SR             <= (others => '0');
      Time_Base_Status       <= '0';
      Timer_Tick             <= '0';
    elsif( rising_edge(Clock) )then
      Ext_TBC_SR             <= Ext_TBC_SR(2 downto 0) & Time_Base_Clock;
      Ext_TBL_SR             <= Ext_TBL_SR(2 downto 0) & Time_Base_Locked;
 
      Time_Base_Status       <= '1';
      Timer_Tick             <= uSec_Tick;
      if( Time_Base_Source = '1' )then
        Time_Base_Status     <= Ext_TBL_SR(3);
        Timer_Tick           <= Ext_TBC_SR(2) and not Ext_TBC_SR(3);
      end if;
 
    end if;
  end process;
 
  Trigger_proc: process( Clock, Reset )
  begin
    if( Reset = Reset_Level )then
      Ext_Trig_SR            <= (others => '0');
      Trig_RE                <= '0';
      Trig_FE                <= '0';
      Trigger_In             <= '0';
      Trigger_Armed          <= '0';
      Trigger_Event          <= '0';
      Trigger_Event_q        <= '0';         
      Delay_Trig             <= '0';
    elsif( rising_edge(Clock) )then
      Ext_Trig_SR            <= Ext_Trig_SR(2 downto 0) & Ext_Trig;
 
      Trig_RE                <= Ext_Trig_SR(2) and not Ext_Trig_SR(3);
      Trig_FE                <= Ext_Trig_SR(3) and not Ext_Trig_SR(2);
 
      Trigger_In             <= ((Trig_FE and not Trigger_Edge) or
                                 (Trig_RE and Trigger_Edge) or
                                  Int_Trig
                                );
 
      if( Arm_Timer = '1' )then
        Trigger_Armed        <= '1';
      elsif( Safe_Timer = '1' )then
        Trigger_Armed        <= '0';
      end if;
 
      if( Trigger_In = '1' )then
        Trigger_Event        <= Trigger_Armed;
      elsif( Clear_Trigd = '1' or Auto_ReArm = '1' )then
        Trigger_Event        <= '0';
      end if;
 
      -- Trigger on rising edge only
      Trigger_Event_q        <= Trigger_Event;
      Delay_Trig             <= Trigger_Event and (not Trigger_Event_q);
    end if;
  end process;
 
  Delay_proc: process( Clock, Reset )
  begin
    if( Reset = Reset_Level )then
      Delay_Pending          <= '0';
      Delay_Tmr              <= (others => '0');
      Delay_Tmr_SR           <= (others => '0');
      Width_Trig             <= '0';
    elsif( rising_edge(Clock) )then
 
      if( Delay_Trig = '1' )then
        Delay_Pending        <= '1';
      elsif( Timer_Tick = '1' and Delay_Pending = '1' )then
        Delay_Pending        <= '0';
      end if;
 
      Delay_Tmr              <= Delay_Tmr - Timer_Tick;
      if( Timer_Tick = '1' and Delay_Pending = '1' )then
        Delay_Tmr            <= Pulse_Delay;
      elsif( Delay_Tmr = 0 )then
        Delay_Tmr            <= (others => '0');
      end if;
 
      Delay_Tmr_SR           <= Delay_Tmr_SR(0) & nor_reduce(Delay_Tmr);
 
      -- If the pulse delay is set to zero, trigger the pulse output
      --  immediately (overriding the timer logic) otherwise, trigger when
      --  the delay timer crosses from 1 to 0
      Width_Trig             <= (Delay_Trig and nor_reduce(Pulse_Delay)) or
                                (Delay_Tmr_SR(0) and not Delay_Tmr_SR(1));
 
    end if;
  end process;
 
  Timer_proc: process( Clock, Reset )
  begin
    if( Reset = Reset_Level )then
      Width_Tmr              <= (others => '0');
      Pulse_Done             <= '0';
      Pulse_Out              <= '0';
    elsif( rising_edge(Clock) )then
      Width_Tmr              <= Width_Tmr - Timer_Tick;
      if( Width_Trig = '1' )then
        Width_Tmr            <= Pulse_Width;
      elsif( Width_Tmr = 0 )then
        Width_Tmr            <= (others => '0');
      end if;
 
      Width_Tmr_SR           <= Width_Tmr_SR(0) & nor_reduce(Width_Tmr);
 
      Pulse_Done             <= (Width_Trig and nor_reduce(Pulse_Width)) or
                                (Width_Tmr_SR(0) and not Width_Tmr_SR(1));
 
      if( Width_Trig = '1' )then
        Pulse_Out            <= '1';
      elsif( Pulse_Done = '1' )then
        Pulse_Out            <= '0';
      end if;
 
    end if;
  end process;
 
end architecture;

Compare with Previous | Blame | View Log

powered by: WebSVN 2.1.0

© copyright 1999-2024 OpenCores.org, equivalent to Oliscience, all rights reserved. OpenCores®, registered trademark.