Search code examples
vhdlverilog

What is the best way to detect pulses between two clock domains?


I want to transfer a pulse from a clock domain clk1 to another clock domain clk2, but we don't know which one is faster than the other! What is the best way to do that?

Thanks,


Solution

  • You need a strobe synchronizer.

    A strobe synchronizer translates a rising edge on input to a level change (T-FF). This level change is transferred to the second clock domain via 2-FF synchronizer. The information is then restored by an XOR gate. (Note: A T-FF is a D-FF with a XOR to invert the state on every high input.)

    In addition, a busy state can be calculated to notify the sending clock domain about the state of the overall circuit. This busy signal can be used to lock the input's rising edge detection.

    The circuit looks like this:
    enter image description here

    Source Code for multiple bits at once:

    library IEEE;
    use     IEEE.STD_LOGIC_1164.all;
    use     IEEE.NUMERIC_STD.all;
    
    library PoC;
    use     PoC.sync.all;
    
    
    entity sync_Strobe is
        generic (
            BITS                : positive            := 1;                       -- number of bit to be synchronized
            GATED_INPUT_BY_BUSY : boolean             := TRUE;                    -- use gated input (by busy signal)
            SYNC_DEPTH          : T_MISC_SYNC_DEPTH   := T_MISC_SYNC_DEPTH'low    -- generate SYNC_DEPTH many stages, at least 2
        );
        port (
            Clock1              : in  std_logic;                            -- <Clock>  input clock domain
            Clock2              : in  std_logic;                            -- <Clock>  output clock domain
            Input               : in  std_logic_vector(BITS - 1 downto 0);  -- @Clock1:  input bits
            Output              : out std_logic_vector(BITS - 1 downto 0);  -- @Clock2:  output bits
            Busy                : out  std_logic_vector(BITS - 1 downto 0)  -- @Clock1:  busy bits
        );
    end entity;
    
    
    architecture rtl of sync_Strobe is
        attribute SHREG_EXTRACT : string;
    
        signal syncClk1_In      : std_logic_vector(BITS - 1 downto 0);
        signal syncClk1_Out     : std_logic_vector(BITS - 1 downto 0);
        signal syncClk2_In      : std_logic_vector(BITS - 1 downto 0);
        signal syncClk2_Out     : std_logic_vector(BITS - 1 downto 0);
    
    begin
        gen : for i in 0 to BITS - 1 generate
            signal D0             : std_logic      := '0';
            signal T1             : std_logic      := '0';
            signal D2             : std_logic      := '0';
    
            signal Changed_Clk1   : std_logic;
            signal Changed_Clk2   : std_logic;
            signal Busy_i         : std_logic;
    
            -- Prevent XST from translating two FFs into SRL plus FF
            attribute SHREG_EXTRACT of D0  : signal is "NO";
            attribute SHREG_EXTRACT of T1  : signal is "NO";
            attribute SHREG_EXTRACT of D2  : signal is "NO";
    
        begin
            process(Clock1)
            begin
                if rising_edge(Clock1) then
                    -- input delay for rising edge detection
                    D0    <= Input(i);
    
                    -- T-FF to converts a strobe to a flag signal
                    if GATED_INPUT_BY_BUSY then
                        T1  <= (Changed_Clk1 and not Busy_i) xor T1;
                    else
                        T1  <= Changed_Clk1 xor T1;
                    end if;
                end if;
            end process;
    
            -- D-FF for level change detection (both edges)
            D2  <= syncClk2_Out(i) when rising_edge(Clock2);
    
            -- assign syncClk*_In signals
            syncClk2_In(i)  <= T1;
            syncClk1_In(i)  <= syncClk2_Out(i);         -- D2
    
            Changed_Clk1    <= not D0 and Input(i);     -- rising edge detection
            Changed_Clk2    <= syncClk2_Out(i) xor D2;  -- level change detection; restore strobe signal from flag
            Busy_i          <= T1 xor syncClk1_Out(i);  -- calculate busy signal
    
            -- output signals
            Output(i)        <= Changed_Clk2;
            Busy(i)          <= Busy_i;
        end generate;
    
        syncClk2 : entity PoC.sync_Bits
            generic map (
                BITS        => BITS,          -- number of bit to be synchronized
                SYNC_DEPTH  => SYNC_DEPTH
            )
            port map (
                Clock       => Clock2,        -- <Clock>  output clock domain
                Input       => syncClk2_In,   -- @async:  input bits
                Output      => syncClk2_Out   -- @Clock:  output bits
            );
    
        syncClk1 : entity PoC.sync_Bits
            generic map (
                BITS        => BITS,          -- number of bit to be synchronized
                SYNC_DEPTH  => SYNC_DEPTH
            )
            port map (
                Clock       => Clock1,        -- <Clock>  output clock domain
                Input       => syncClk1_In,   -- @async:  input bits
                Output      => syncClk1_Out   -- @Clock:  output bits
            );
    end architecture;
    

    The source code can be found here: PoC.misc.sync.Strobe