CDC Synchronizer Generator

Generate the standard clock-domain-crossing primitives in Verilog and VHDL: N-flop bit synchronizer, toggle-based pulse synchronizer, reset synchronizer (async assert / sync deassert) and a button debouncer - each with ASYNC_REG attributes and the XDC constraint to go with it.

Results

Primitive
2-flop bit synchronizer
Latency
2 to 3 destination cycles
Use for
single-bit LEVEL signals only

Notes

Verilog
// 2-flop bit synchronizer for a LEVEL signal crossing clock domains.
// NOT for multi-bit buses - use gray code, a handshake or an async FIFO.
// Generated by libfpga.com/tools/cdc-synchronizer
module sync_bit #(
    parameter integer STAGES = 2
) (
    input  wire clk_dst,
    input  wire d_async,
    output wire q_sync
);
    (* ASYNC_REG = "TRUE" *) reg [STAGES-1:0] sync_ff;

    always @(posedge clk_dst)
        sync_ff <= {sync_ff[STAGES-2:0], d_async};

    assign q_sync = sync_ff[STAGES-1];
endmodule
VHDL
-- 2-flop bit synchronizer for a LEVEL signal crossing clock domains.
-- Generated by libfpga.com/tools/cdc-synchronizer
library ieee;
use ieee.std_logic_1164.all;

entity sync_bit is
    generic (STAGES : integer := 2);
    port (
        clk_dst : in  std_logic;
        d_async : in  std_logic;
        q_sync  : out std_logic
    );
end entity;

architecture rtl of sync_bit is
    signal sync_ff : std_logic_vector(STAGES-1 downto 0) := (others => '0');
    attribute async_reg : string;
    attribute async_reg of sync_ff : signal is "TRUE";
begin
    process (clk_dst) begin
        if rising_edge(clk_dst) then
            sync_ff <= sync_ff(STAGES-2 downto 0) & d_async;
        end if;
    end process;
    q_sync <= sync_ff(STAGES-1);
end architecture;
XDC constraints
# XDC for sync_bit: bound the crossing, keep flops adjacent.
# Use -datapath_only so clock skew between unrelated clocks is ignored.
set_max_delay -datapath_only 8.0 \
    -from [get_cells -hier -regexp .*src_reg.*] \
    -to   [get_cells -hier -regexp .*sync_bit.*/sync_ff_reg\[0\]]
# ASYNC_REG in the RTL already forces placement adjacency in Vivado.

This exact result is bookmarkable — the URL contains all your inputs. Need it in a script? Append &format=json (API docs).

About this tool

A flip-flop sampling a signal from another clock domain can go metastable — resolve to a wrong or wobbling value for a while. The cure is never exotic: give the signal one or more extra flops to settle (bit synchronizer), turn events into level toggles before crossing (pulse synchronizer), or release reset synchronously so every flop leaves reset in the same cycle (reset synchronizer). The part most tutorials skip is that the RTL is only half the fix: synthesis must keep the flops together (ASYNC_REG) and timing analysis must know the path is asynchronous (set_max_delay -datapath_only, or a false path). This generator emits both halves. One rule above all: a multi-bit value must never cross through parallel bit synchronizers — use a Gray-coded counter, a handshake, or an async FIFO.

Related tools