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
- The input must be glitch-free and held long enough to be sampled: at least one destination clock period plus setup.
- Never synchronize the bits of a bus independently - the bits can arrive in different cycles.
// 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
-- 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 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.
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.