URL
https://opencores.org/ocsvn/open8_urisc/open8_urisc/trunk
Subversion Repositories open8_urisc
[/] [open8_urisc/] [trunk/] [VHDL/] [o8_trig_delay.vhd] - Rev 309
Go to most recent revision | 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;
Go to most recent revision | Compare with Previous | Blame | View Log