From ae500a849bcb106b30c1de74fabcf61107bc5f07 Mon Sep 17 00:00:00 2001 From: rs <> Date: Thu, 11 Dec 2025 02:43:51 -0600 Subject: [PATCH] Add metastability detector Forgot to commit this earlier... --- .../experimental/metastability_detector.vhd | 112 +++++++++++++ .../experimental/nexyx2_metastability.vhd | 150 ++++++++++++++++++ 2 files changed, 262 insertions(+) create mode 100644 projects/experimental/metastability_detector.vhd create mode 100644 projects/experimental/nexyx2_metastability.vhd diff --git a/projects/experimental/metastability_detector.vhd b/projects/experimental/metastability_detector.vhd new file mode 100644 index 0000000..87a98ba --- /dev/null +++ b/projects/experimental/metastability_detector.vhd @@ -0,0 +1,112 @@ +library ieee; +use ieee.std_logic_1164.all; +use ieee.numeric_std.all; + +library unisim; +use unisim.vcomponents.all; + + +entity metastability_detector is + port ( + clk_i: in std_logic; + sig_i: in std_logic; + + count_sync: out std_logic_vector(7 downto 0); + count_async: out std_logic_vector(7 downto 0) + ); +end metastability_detector; + + +architecture behavioral of metastability_detector is + + signal sig_a: std_logic; + signal sig_b: std_logic; + + signal is_metastable: std_logic; + signal sync_counter_reg: unsigned(7 downto 0); + + -- 5, 4 - Asynchronously set to zero by is_metastable, synchronize the rising edge out of reset + -- 3, 2 - Synchronize the falling edge into reset + -- 1, 0 - Edge detection + signal async_shift_reg: std_logic_vector(5 downto 0) := (others => '1'); + signal was_metastable: std_logic; + signal async_counter_reg: unsigned(7 downto 0); + +begin + + ------------------------------------------------------------------------ + -- Detector portion + -- + -- Two buffers buffering the same signal - their outputs should always + -- match, but might not if the input is in a metastable state. This + -- won't detect all metastabilty, just the metastability that actually + -- causes problems. + + e_buf_a: lut1 + generic map (INIT => "10") + port map ( + I0 => sig_i, + O => sig_a + ); + + e_buf_b: lut1 + generic map (INIT => "10") + port map ( + I0 => sig_i, + O => sig_b + ); + + is_metastable <= '1' when sig_a /= sig_b else '0'; + + ------------------------------------------------------------------------ + -- Event counters + -- + -- Two metastability event counters: + -- - Counter that counts events where metastability persists all the way + -- to the next clock edge + -- - Asyncrhonous trigger that detects all metastability events, even if + -- they settle out before the next clock edge + + -- Event counters + process (clk_i, is_metastable, was_metastable) + begin + if rising_edge(clk_i) then + if is_metastable = '1' then + sync_counter_reg <= sync_counter_reg + 1; + end if; + if was_metastable = '1' then + async_counter_reg <= async_counter_reg + 1; + end if; + end if; + end process; + + -- Asynchronous glitch detector + process (clk_i, is_metastable) + begin + -- First two stages, asynchronously reset to zero to detect short + -- metastability events, synchronously shift 1 back in through two + -- stages to prevent metastability on the rising edge when recovering + if is_metastable = '1' then + async_shift_reg(5 downto 4) <= "00"; + elsif rising_edge(clk_i) then + async_shift_reg(5 downto 4) <= '1' & async_shift_reg(5); + end if; + + -- Next two stages, deal with metastability on falling edges, since the + -- previous stages were asynchronously reset + if rising_edge(clk_i) then + async_shift_reg(3 downto 2) <= async_shift_reg(4 downto 3); + end if; + + -- Next two stages, all metastability has been dealt with and signals + -- are stable - detect rising edges as events + if rising_edge(clk_i) then + async_shift_reg(1 downto 0) <= async_shift_reg(2 downto 1); + end if; + end process; + was_metastable <= '1' when async_shift_reg(1 downto 0) = "10" else '0'; + + count_sync <= std_logic_vector(sync_counter_reg); + count_async <= std_logic_vector(async_counter_reg); + +end behavioral; diff --git a/projects/experimental/nexyx2_metastability.vhd b/projects/experimental/nexyx2_metastability.vhd new file mode 100644 index 0000000..3841924 --- /dev/null +++ b/projects/experimental/nexyx2_metastability.vhd @@ -0,0 +1,150 @@ +library ieee; +use ieee.std_logic_1164.all; +use ieee.numeric_std.all; +use ieee.std_logic_misc.all; + +library unisim; +use unisim.vcomponents.all; + +library utility; + +library nexys2_lib; + + +entity nexyx2_metastability is + port ( + clk_50: in std_logic; + DstmIFCLK: in std_logic; + seg: out std_logic_vector(6 downto 0); + dp: out std_logic; + an: out std_logic_vector(3 downto 0) + ); +end nexyx2_metastability; + + +architecture behavioral of nexyx2_metastability is + + -- Number of metastability detectors + constant INSTANCES: positive := 4096; + + -- Metastability creation and detection signals + signal metastable_reg: std_logic_vector(INSTANCES-1 downto 0); + signal a_buf: std_logic_vector(INSTANCES-1 downto 0); + signal b_buf: std_logic_vector(INSTANCES-1 downto 0); + signal glitch: std_logic_vector(INSTANCES-1 downto 0); + signal detect: std_logic_vector(INSTANCES-1 downto 0); + + signal count_reg: std_logic_vector(15 downto 0); + + -- Output-related signals + signal clk_div_reg: unsigned(15 downto 0); + signal seg_0: std_logic_vector(6 downto 0); + signal seg_1: std_logic_vector(6 downto 0); + signal seg_2: std_logic_vector(6 downto 0); + signal seg_3: std_logic_vector(6 downto 0); + +begin + + ------------------------------------------------------------------------ + -- Generate a potentially metastable test signal + + g_meta: for i in 0 to INSTANCES-1 generate + e_test_reg: fdce + port map ( + D => DstmIFCLK, + CE => '1', + C => clk_50, + CLR => '0', + Q => metastable_reg(i) + ); + + e_buf_a: lut1 + generic map (INIT => "10") + port map ( + I0 => metastable_reg(i), + O => a_buf(i) + ); + + e_buf_b: lut1 + generic map (INIT => "10") + port map ( + I0 => metastable_reg(i), + O => b_buf(i) + ); + + glitch(i) <= '1' when a_buf(i) /= b_buf(i) else '0'; + + e_detect: entity utility.glitch_detector + port map ( + clk_i => clk_50, + glitch => glitch(i), + rise => detect(i), + fall => open + ); + end generate g_meta; + + + ------------------------------------------------------------------------ + -- Count metastability events + + process (clk_50, detect, count_reg) + begin + if rising_edge(clk_50) then + if or_reduce(detect) = '1' then + count_reg <= std_logic_vector(unsigned(count_reg) + 1); + end if; + end if; + end process; + + + ------------------------------------------------------------------------ + -- Display results + + e_seg0: entity nexys2_lib.seven_seg_hex + port map ( + data_in => count_reg(3 downto 0), + seg_out => seg_0 + ); + + e_seg1: entity nexys2_lib.seven_seg_hex + port map ( + data_in => count_reg(7 downto 4), + seg_out => seg_1 + ); + + e_seg2: entity nexys2_lib.seven_seg_hex + port map ( + data_in => count_reg(11 downto 8), + seg_out => seg_2 + ); + + e_seg3: entity nexys2_lib.seven_seg_hex + port map ( + data_in => count_reg(15 downto 12), + seg_out => seg_3 + ); + + process (clk_50, clk_div_reg) + begin + if rising_edge(clk_50) then + clk_div_reg <= clk_div_reg + 1; + end if; + end process; + + e_sseg: entity nexys2_lib.seven_seg_mux + port map ( + clk_in => clk_div_reg(15), + clk_en => '1', + + seg_0_in => seg_0, + seg_1_in => seg_1, + seg_2_in => seg_2, + seg_3_in => seg_3, + dps_in => "1111", + + seg_out => seg, + dp_out => dp, + an_out => an + ); + +end behavioral; -- 2.43.0