Forum Discussion

Altera_Forum's avatar
Altera_Forum
Icon for Honored Contributor rankHonored Contributor
11 years ago

Deglitch Logic Metastability

I'm trying to determine if the way this deglitching filter was written will incur a metastability issue:

sig_filt : process (i_clk)
    variable abc       : std_logic_vector(2 downto 0) := "000";
    variable abc_last  : std_logic_vector(2 downto 0) := "000";
    variable abc_count : integer := 0;
begin
    if rising_edge(i_clk) then
        -- filter 'a', 'b', 'c' signals as a group
        -- all signals need to be in a stable state
        -- for FILT_COUNTS clk periods before transitions
        -- are passed up to the state decoder
        abc(2) := a;
        abc(1) := b;
        abc(0) := c;
        
        if abc /= abc_last then
            abc_count := 0;
            abc_last  := abc;        
        elsif abc_count < FILT_COUNTS then
            abc_count := abc_count + 1;
        else
                filt_a <= a;
            filt_b <= b;
            filt_c <= c;
        end if;
    end if;
end process;

The way I think this should be written:

sig_filt : process (i_clk)
    variable abc       : std_logic_vector(2 downto 0) := "000";
    variable abc_last  : std_logic_vector(2 downto 0) := "000";
    variable abc_count : integer := 0;
begin
    if rising_edge(i_clk) then
        -- filter 'a', 'b', 'c' signals as a group
        -- all signals need to be in a stable state
        -- for FILT_COUNTS clk periods before transitions
        -- are passed up to the state decoder
        abc(2) := a;
        abc(1) := b;
        abc(0) := c;
        
        if abc /= abc_last then
            abc_count := 0;
                
        elsif abc_count < FILT_COUNTS then
            abc_count := abc_count + 1;
        else
                filt_a <= abc_last(2);
            filt_b <= abc_last(1);
            filt_c <= abc_last(0);
        end if;
abc_last  := abc;    
    end if;
end process;

or at least:

sig_filt : process (i_clk)
    variable abc       : std_logic_vector(2 downto 0) := "000";
    variable abc_last  : std_logic_vector(2 downto 0) := "000";
    variable abc_count : integer := 0;
begin
    if rising_edge(i_clk) then
        -- filter 'a', 'b', 'c' signals as a group
        -- all signals need to be in a stable state
        -- for FILT_COUNTS clk periods before transitions
        -- are passed up to the state decoder
        abc(2) := a;
        abc(1) := b;
        abc(0) := c;
        
        if abc /= abc_last then
            abc_count := 0;
            abc_last  := abc;   
        elsif abc_count < FILT_COUNTS then
            abc_count := abc_count + 1;
        else
                filt_a <= abc(2);
                filt_b <= abc(1);
                filt_c <= abc(0);
        end if;
 
    end if;
end process;

What I'm thinking could happen in the first snippet is while the abc and abc_last match for the edge, the input signals a, b, and c, could glitch right before they are latched into filt_a, filt_b, or filt_c signals. By using the abc_last values, this is mitigated.

Any thoughts? Am I over thinking this? Trying to resolve a once in rare occurence issue, which in my mind could be attributed to this filtering logic which seems a bit off.

1 Reply

  • Altera_Forum's avatar
    Altera_Forum
    Icon for Honored Contributor rankHonored Contributor

    Hi,

    if a, b and c are asynchronous to i_clk, and you really want to prevent metastability, I'd say none of the three variants are safe.

    Both in the first and the third code snippet, the filt_* signals are assigned directly from the asynchronous input signals a/b/c. If a/b/c happen to arrive too close to a clock edge of i_clk, the registers that hold the corresponding filt_* signal will go metastable, making subsequent logic fed by filt_* error-prone.

    In the second case you have better metastability protection, but quite not 100%. Try to synthesize this code and open it in the RTL viewer. You'll see that the asynchronous a/b/c signals are registered (the abc_last register). Then there is a mux (which decides whether to feed abc_last into filt_* or not), and then the registers that hold filt_*.

    The rule to prevent metastability is to have two registers in a rwo, with no logic in between. The first register can go metastable, indeed, but the metastable state will (with a huge probability) have settled once the signal is latched in the second register. If you have any logic in between, this logic will delay the metastable signal, making the probability higher that the signal arrives in an intermediate (metastable) state at the second register.

    What I would recommend: use two registers to properly synchronize the a/b/c signals, and then chose any of your thee examples to do the deglitching.

    Regarding your question whether or not this is over-thinking: for a small just-for-fun FPGA project I would not expect any problems ever with the second example. For a real-world FPGA project I'd say better safe than sorry. If you're designing an ASIC then you probably would even use three synchronization registers instead of only two.

    Best regards,

    GooGooCluster