]> git.the-white-hart.net Git - vhdl/commitdiff
Add metastability detector
authorrs <>
Thu, 11 Dec 2025 08:43:51 +0000 (02:43 -0600)
committerrs <>
Thu, 11 Dec 2025 08:43:51 +0000 (02:43 -0600)
Forgot to commit this earlier...

projects/experimental/metastability_detector.vhd [new file with mode: 0644]
projects/experimental/nexyx2_metastability.vhd [new file with mode: 0644]

diff --git a/projects/experimental/metastability_detector.vhd b/projects/experimental/metastability_detector.vhd
new file mode 100644 (file)
index 0000000..87a98ba
--- /dev/null
@@ -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 (file)
index 0000000..3841924
--- /dev/null
@@ -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;