From 211e3902a16b1775e059f71dcf2f0ab3e01d9e27 Mon Sep 17 00:00:00 2001 From: rs <> Date: Tue, 11 Nov 2025 22:29:04 -0600 Subject: [PATCH] Clean up clock synchronization utilities --- libraries/nexys2/usb.vhd | 56 ++++++++++++------- libraries/utility/fifo_xclk.vhd | 24 ++++---- libraries/utility/power_on_reset_opt.vhd | 32 +++++++---- libraries/utility/sync_rst.vhd | 32 ----------- libraries/utility/sync_sig.vhd | 63 +++++++++++++++++++++ libraries/utility/sync_vec.vhd | 62 +++++++++++++++++++++ libraries/utility/tests/test_sync_rst.vhd | 47 ---------------- libraries/utility/tests/test_sync_sig.vhd | 68 +++++++++++++++++++++++ libraries/utility/tests/test_sync_vec.vhd | 52 +++++++++++++++++ libraries/utility/xclk_sig.vhd | 33 ----------- libraries/utility/xclk_vec.vhd | 35 ------------ 11 files changed, 316 insertions(+), 188 deletions(-) delete mode 100644 libraries/utility/sync_rst.vhd create mode 100644 libraries/utility/sync_sig.vhd create mode 100644 libraries/utility/sync_vec.vhd delete mode 100644 libraries/utility/tests/test_sync_rst.vhd create mode 100644 libraries/utility/tests/test_sync_sig.vhd create mode 100644 libraries/utility/tests/test_sync_vec.vhd delete mode 100644 libraries/utility/xclk_sig.vhd delete mode 100644 libraries/utility/xclk_vec.vhd diff --git a/libraries/nexys2/usb.vhd b/libraries/nexys2/usb.vhd index 6f0a002..50cdc5f 100644 --- a/libraries/nexys2/usb.vhd +++ b/libraries/nexys2/usb.vhd @@ -18,13 +18,13 @@ entity usb_cypress is EppDSTB_DstmFLAGB: in std_logic; EppWAIT_DstmSLRD: out std_logic; DstmIFCLK: in std_logic; - DstmSLCS: in std_logic; + DstmSLCS: in std_logic; -- STMEN DstmADR: out std_logic_vector(1 downto 0); DstmSLWR: out std_logic; DstmSLOE: out std_logic; DstmPKTEND: out std_logic; - UsbMode: in std_logic; - UsbRdy: in std_logic; + UsbMode: in std_logic; -- I think this is EPPEN, which is referenced in documentation but not defined anywhere + UsbRdy: in std_logic; -- High when USB is plugged in and the controller is ready -- EPP-Wishbone interface epp_clk_i: in std_logic; @@ -106,28 +106,28 @@ begin ------------------------------------------------------------------------ -- Synchronize reset signal to all clock domains - e_sync_rst_ifclk: entity utility.sync_rst + e_sync_rst_ifclk: entity utility.sync_sig generic map (SYNC_STAGES => SYNC_STAGES+1) port map ( rst_i => rst_i, clk_i => DstmIFCLK, - rst_o => ifclk_rst + sig_o => ifclk_rst ); - e_sync_rst_epp: entity utility.sync_rst + e_sync_rst_epp: entity utility.sync_sig generic map (SYNC_STAGES => SYNC_STAGES+1) port map ( rst_i => rst_i, clk_i => epp_clk_i, - rst_o => epp_rst + sig_o => epp_rst ); - e_sync_rst_stm: entity utility.sync_rst + e_sync_rst_stm: entity utility.sync_sig generic map (SYNC_STAGES => SYNC_STAGES+1) port map ( rst_i => rst_i, clk_i => stm_clk_i, - rst_o => stm_rst + sig_o => stm_rst ); @@ -176,21 +176,39 @@ begin -- EPP Interface -- EPP clock domain crossing - e_xclk_eppen: entity utility.xclk_sig + e_xclk_eppen: entity utility.sync_sig generic map (SYNC_STAGES => SYNC_STAGES, INIT => '0') - port map (a_sig_i => epp_en, b_clk_i => epp_clk_i, b_sig_o => xclk_eppen); + port map ( + rst_i => epp_rst, + clk_i => epp_clk_i, + sig_i => epp_en, + sig_o => xclk_eppen + ); - e_xclk_astb: entity utility.xclk_sig - generic map (SYNC_STAGES => SYNC_STAGES, INIT => '1') - port map (a_sig_i => epp_astb, b_clk_i => epp_clk_i, b_sig_o => xclk_astb); + e_xclk_astb: entity utility.sync_sig + generic map (SYNC_STAGES => SYNC_STAGES) + port map ( + clk_i => epp_clk_i, + sig_i => epp_astb, + sig_o => xclk_astb + ); - e_xclk_dstb: entity utility.xclk_sig - generic map (SYNC_STAGES => SYNC_STAGES, INIT => '1') - port map (a_sig_i => epp_dstb, b_clk_i => epp_clk_i, b_sig_o => xclk_dstb); + e_xclk_dstb: entity utility.sync_sig + generic map (SYNC_STAGES => SYNC_STAGES) + port map ( + clk_i => epp_clk_i, + sig_i => epp_dstb, + sig_o => xclk_dstb + ); - e_xclk_wait: entity utility.xclk_sig + e_xclk_wait: entity utility.sync_sig generic map (SYNC_STAGES => SYNC_STAGES, INIT => '0') -- FIXME: is this the best initial value for WAIT? - port map (a_sig_i => xclk_wait, b_clk_i => DstmIFCLK, b_sig_o => epp_wait); + port map ( + rst_i => ifclk_rst, + clk_i => DstmIFCLK, + sig_i => xclk_wait, + sig_o => epp_wait + ); -- These signals are stable during the time that they're validated by the -- synchronized handshaking signals, so no clock synchronizing is needed diff --git a/libraries/utility/fifo_xclk.vhd b/libraries/utility/fifo_xclk.vhd index 4709d93..0969178 100644 --- a/libraries/utility/fifo_xclk.vhd +++ b/libraries/utility/fifo_xclk.vhd @@ -49,20 +49,20 @@ begin -- Head logic - e_head_reset: entity utility.sync_rst + e_head_reset: entity utility.sync_sig generic map (SYNC_STAGES => SYNC_STAGES+1) port map ( rst_i => rst_i, clk_i => head_clk_i, - rst_o => head_rst + sig_o => head_rst ); - e_xclk_tail: entity utility.xclk_vec + e_xclk_tail: entity utility.sync_vec generic map (SYNC_STAGES => SYNC_STAGES) port map ( - a_sig_i => std_logic_vector(tail_adr_reg), - b_clk_i => head_clk_i, - b_sig_o => tail_adr_xclk + clk_i => head_clk_i, + sig_i => std_logic_vector(tail_adr_reg), + sig_o => tail_adr_xclk ); is_full <= '1' when std_logic_vector(head_adr_next) = tail_adr_xclk else '0'; @@ -85,20 +85,20 @@ begin -- Tail logic - e_tail_reset: entity utility.sync_rst + e_tail_reset: entity utility.sync_sig generic map (SYNC_STAGES => SYNC_STAGES+1) port map ( rst_i => rst_i, clk_i => tail_clk_i, - rst_o => tail_rst + sig_o => tail_rst ); - e_xclk_head: entity utility.xclk_vec + e_xclk_head: entity utility.sync_vec generic map (SYNC_STAGES => SYNC_STAGES) port map ( - a_sig_i => std_logic_vector(head_adr_reg), - b_clk_i => tail_clk_i, - b_sig_o => head_adr_xclk + clk_i => tail_clk_i, + sig_i => std_logic_vector(head_adr_reg), + sig_o => head_adr_xclk ); is_empty <= '1' when std_logic_vector(tail_adr_reg) = head_adr_xclk else '0'; diff --git a/libraries/utility/power_on_reset_opt.vhd b/libraries/utility/power_on_reset_opt.vhd index 73908d7..a1d5513 100644 --- a/libraries/utility/power_on_reset_opt.vhd +++ b/libraries/utility/power_on_reset_opt.vhd @@ -1,10 +1,25 @@ +-------------------------------------------------------------------------------- +-- power_on_reset_opt - power-on-reset generator and external reset synchronizer +-- +-- This holds a design in reset after initial configuration and also +-- synchronizes an active-high external reset. +-- +-- After synchronizing the external reset, the reset output is held for an +-- additional 16 clock cycles to give SRLs time to flush. +-------------------------------------------------------------------------------- + library ieee; use ieee.std_logic_1164.all; use ieee.numeric_std.all; use ieee.std_logic_misc.all; +library work; + entity power_on_reset_opt is + generic ( + SYNC_STAGES: positive := 2 + ); port ( rst_i: in std_logic := '0'; clk_i: in std_logic; @@ -15,7 +30,6 @@ end power_on_reset_opt; architecture behavioral of power_on_reset_opt is - signal shift_reg: std_logic_vector(1 downto 0) := (others => '1'); signal rst_sync: std_logic; signal count_reg: std_logic_vector(3 downto 0) := (others => '1'); @@ -24,15 +38,13 @@ architecture behavioral of power_on_reset_opt is begin -- Synchronize external asynchronous reset to internal clock - process (rst_i, clk_i, shift_reg) - begin - if rst_i = '1' then - shift_reg <= (others => '1'); - elsif rising_edge(clk_i) then - shift_reg <= '0' & shift_reg(shift_reg'high downto 1); - end if; - end process; - rst_sync <= shift_reg(0); + e_sync_rst: entity work.sync_sig + generic map (SYNC_STAGES => SYNC_STAGES) + port map ( + rst_i => rst_i, + clk_i => clk_i, + sig_o => rst_sync + ); -- Continue to hold reset for 16 clock cycles to allow SRLs to flush process (rst_sync, clk_i, count_reg, count_running) diff --git a/libraries/utility/sync_rst.vhd b/libraries/utility/sync_rst.vhd deleted file mode 100644 index 42b6731..0000000 --- a/libraries/utility/sync_rst.vhd +++ /dev/null @@ -1,32 +0,0 @@ -library ieee; -use ieee.std_logic_1164.all; - - -entity sync_rst is - generic (SYNC_STAGES: positive := 2); - port ( - rst_i: in std_logic; - clk_i: in std_logic; - rst_o: out std_logic - ); -end sync_rst; - - -architecture behavioral of sync_rst is - - signal shift_reg: std_logic_vector(SYNC_STAGES-1 downto 0) := (others => '1'); - -begin - - rst_o <= shift_reg(0); - - process (rst_i, clk_i) - begin - if rst_i = '1' then - shift_reg <= (others => '1'); - elsif rising_edge(clk_i) then - shift_reg <= '0' & shift_reg(SYNC_STAGES-1 downto 1); - end if; - end process; - -end behavioral; diff --git a/libraries/utility/sync_sig.vhd b/libraries/utility/sync_sig.vhd new file mode 100644 index 0000000..ae761f0 --- /dev/null +++ b/libraries/utility/sync_sig.vhd @@ -0,0 +1,63 @@ +-------------------------------------------------------------------------------- +-- sync_sig - synchronize a signal into a clock domain +-- +-- Generics: +-- SYNC_STAGES - number of shift register stages to use +-- INIT - initial and reset signal value +-- +-- Ports: +-- rst_i - asynchronous active high reset to force to INIT value +-- clk_i - the destination clock domain +-- sig_i - the source signal +-- sig_o - the synchronized signal +-- +-- This can be used to both cross clock domains and synchronize reset signals. +-- +-- To synchronize an active high reset into a clock domain: +-- INIT => '1' (default) +-- rst_i => active high reset signal to synchronize +-- sig_i => '0' (default) +-- +-- For signals leaving the design, INIT and rst_i should be used. +-- +-- For signals entering or within the design, reset can be elided as long as the +-- rest of the design is left in reset for at least SYNC_STAGES cycles to allow +-- the synchronizing registers to flush. +-------------------------------------------------------------------------------- + +library ieee; +use ieee.std_logic_1164.all; + + +entity sync_sig is + generic ( + SYNC_STAGES: positive := 2; + INIT: std_logic := '1' + ); + port ( + rst_i: in std_logic := '0'; + clk_i: in std_logic; + + sig_i: in std_logic := '0'; + sig_o: out std_logic + ); +end sync_sig; + + +architecture behavioral of sync_sig is + + signal shift_reg: std_logic_vector(SYNC_STAGES-1 downto 0) := (others => INIT); + +begin + + process (clk_i, sig_i, shift_reg) + begin + if rst_i = '1' then + shift_reg <= (others => INIT); + elsif rising_edge(clk_i) then + shift_reg <= sig_i & shift_reg(SYNC_STAGES-1 downto 1); + end if; + end process; + sig_o <= shift_reg(0); + +end behavioral; diff --git a/libraries/utility/sync_vec.vhd b/libraries/utility/sync_vec.vhd new file mode 100644 index 0000000..fa75268 --- /dev/null +++ b/libraries/utility/sync_vec.vhd @@ -0,0 +1,62 @@ +-------------------------------------------------------------------------------- +-- sync_vec - synchronize a std_logic_vector into a clock domain +-- +-- Generics: +-- SYNC_STAGES - number of shift register stages to use +-- +-- Ports: +-- clk_i - the destination clock domain +-- sig_i - the source signal +-- sig_o - the synchronized signal +-- +-- This is probably not what you want. This should only be used when sending +-- a Gray-coded counter value across clock domains for things like dual-clocked +-- FIFOs, not for synchronizing data busses. Data busses should be held stable +-- in their source domain while synchronized handshaking signals negotiate the +-- crossing. +-- +-- When sending a Gray-coded value across domains, make sure the value is +-- registered! Combinational logic to generate a Gray-code value for an integer +-- can have glitches, which would defeat the purpose of this block if unreggied. +-- +-- There is no reset logic, so the rest of the design should be held in reset +-- for at least SYNC_STAGES cycles to flush the synchronizing registers. +-------------------------------------------------------------------------------- + +library ieee; +use ieee.std_logic_1164.all; + + +entity sync_vec is + generic ( + SYNC_STAGES: positive := 2 + ); + port ( + clk_i: in std_logic; + + sig_i: in std_logic_vector; + sig_o: out std_logic_vector + ); +end sync_vec; + + +architecture behavioral of sync_vec is + + type vec_array is array(natural range <>) of std_logic_vector(sig_i'high downto 0); + + signal shift_reg: vec_array(SYNC_STAGES-1 downto 0); + +begin + + process (clk_i, sig_i, shift_reg) + begin + if rising_edge(clk_i) then + shift_reg(SYNC_STAGES-1) <= sig_i; + for i in SYNC_STAGES-2 downto 0 loop + shift_reg(i) <= shift_reg(i+1); + end loop; + end if; + end process; + sig_o <= shift_reg(0); + +end behavioral; diff --git a/libraries/utility/tests/test_sync_rst.vhd b/libraries/utility/tests/test_sync_rst.vhd deleted file mode 100644 index 8529d93..0000000 --- a/libraries/utility/tests/test_sync_rst.vhd +++ /dev/null @@ -1,47 +0,0 @@ -library ieee; -use ieee.std_logic_1164.all; - -library work; - - -entity test_sync_rst is -end test_sync_rst; - - -architecture behavior of test_sync_rst is - - signal rst_i: std_logic; - signal clk_i: std_logic; - signal rst_o: std_logic; - -begin - - p_test: process - begin - rst_i <= '0'; - wait until rst_o = '0'; - wait for 40 ns; - rst_i <= '1'; - wait for 2 ns; - rst_i <= '0'; - - wait; - end process; - - e_uut: entity work.sync_rst - generic map (SYNC_STAGES => 5) - port map ( - rst_i => rst_i, - clk_i => clk_i, - rst_o => rst_o - ); - - p_clk: process - begin - clk_i <= '0'; - wait for 10 ns; - clk_i <= '1'; - wait for 10 ns; - end process; - -end; diff --git a/libraries/utility/tests/test_sync_sig.vhd b/libraries/utility/tests/test_sync_sig.vhd new file mode 100644 index 0000000..6379607 --- /dev/null +++ b/libraries/utility/tests/test_sync_sig.vhd @@ -0,0 +1,68 @@ +library ieee; +use ieee.std_logic_1164.all; + +library work; + + +entity test_sync_sig is +end test_sync_sig; + + +architecture behavior of test_sync_sig is + + constant CLK_I_PERIOD: time := 20 ns; + + signal rst_i: std_logic; + signal clk_i: std_logic; + signal sig_i: std_logic; + signal sig_o: std_logic; + +begin + + p_test: process + begin + rst_i <= '0'; + sig_i <= '0'; + + wait until sig_o = '0'; + wait for CLK_I_PERIOD*3; + + sig_i <= '1'; + wait until sig_o = '1'; + wait for CLK_I_PERIOD*3; + + sig_i <= '0'; + wait until sig_o = '0'; + wait for CLK_I_PERIOD*3; + + rst_i <= '1'; + wait for 1 ns; + rst_i <= '0'; + wait for 1 ns; + wait until sig_o = '0'; + wait for CLK_I_PERIOD*3; + + wait; + end process; + + e_uut: entity work.sync_sig + generic map ( + SYNC_STAGES => 5, -- For testing purposes. Should probably be 2 or maybe 3 in a design. + INIT => '1' + ) + port map ( + rst_i => rst_i, + clk_i => clk_i, + sig_i => sig_i, + sig_o => sig_o + ); + + p_clk: process + begin + clk_i <= '0'; + wait for CLK_I_PERIOD/2; + clk_i <= '1'; + wait for CLK_I_PERIOD/2; + end process; + +end; diff --git a/libraries/utility/tests/test_sync_vec.vhd b/libraries/utility/tests/test_sync_vec.vhd new file mode 100644 index 0000000..8658890 --- /dev/null +++ b/libraries/utility/tests/test_sync_vec.vhd @@ -0,0 +1,52 @@ +library ieee; +use ieee.std_logic_1164.all; + +library work; + + +entity test_sync_vec is +end test_sync_vec; + + +architecture behavior of test_sync_vec is + + constant CLK_I_PERIOD: time := 20 ns; + + signal clk_i: std_logic; + signal sig_i: std_logic_vector(7 downto 0); + signal sig_o: std_logic_vector(7 downto 0); + +begin + + p_test: process + begin + sig_i <= x"a5"; + wait until sig_o = x"a5"; + wait for CLK_I_PERIOD*3; + + sig_i <= x"c3"; + wait until sig_o = x"c3"; + wait for CLK_I_PERIOD*3; + + wait; + end process; + + e_uut: entity work.sync_vec + generic map ( + SYNC_STAGES => 5 -- For testing purposes. Should probably be 2 or maybe 3 in a design. + ) + port map ( + clk_i => clk_i, + sig_i => sig_i, + sig_o => sig_o + ); + + p_clk: process + begin + clk_i <= '0'; + wait for CLK_I_PERIOD/2; + clk_i <= '1'; + wait for CLK_I_PERIOD/2; + end process; + +end; diff --git a/libraries/utility/xclk_sig.vhd b/libraries/utility/xclk_sig.vhd deleted file mode 100644 index f253156..0000000 --- a/libraries/utility/xclk_sig.vhd +++ /dev/null @@ -1,33 +0,0 @@ -library ieee; -use ieee.std_logic_1164.all; - - -entity xclk_sig is - generic ( - SYNC_STAGES: positive := 2; - INIT: std_logic := '0' - ); - port ( - a_sig_i: in std_logic; - - b_clk_i: in std_logic; - b_sig_o: out std_logic - ); -end xclk_sig; - - -architecture behavioral of xclk_sig is - - signal sync_regs: std_logic_vector(SYNC_STAGES-1 downto 0) := (others => INIT); - -begin - - process (b_clk_i, a_sig_i, sync_regs) - begin - if rising_edge(b_clk_i) then - sync_regs <= a_sig_i & sync_regs(SYNC_STAGES-1 downto 1); - end if; - end process; - b_sig_o <= sync_regs(0); - -end behavioral; diff --git a/libraries/utility/xclk_vec.vhd b/libraries/utility/xclk_vec.vhd deleted file mode 100644 index a1662da..0000000 --- a/libraries/utility/xclk_vec.vhd +++ /dev/null @@ -1,35 +0,0 @@ -library ieee; -use ieee.std_logic_1164.all; - - -entity xclk_vec is - generic (SYNC_STAGES: positive := 2); - port ( - a_sig_i: in std_logic_vector; - - b_clk_i: in std_logic; - b_sig_o: out std_logic_vector - ); -end xclk_vec; - - -architecture behavioral of xclk_vec is - - type value_array is array(natural range <>) of std_logic_vector(a_sig_i'high downto 0); - - signal sync_regs: value_array(SYNC_STAGES-1 downto 0); - -begin - - process (b_clk_i, a_sig_i, sync_regs) - begin - if rising_edge(b_clk_i) then - sync_regs(SYNC_STAGES-1) <= a_sig_i; - for i in SYNC_STAGES-2 downto 0 loop - sync_regs(i) <= sync_regs(i+1); - end loop; - end if; - end process; - b_sig_o <= sync_regs(0); - -end behavioral; -- 2.43.0