Clocks, Resets & Clock Domain Crossing

When a signal from another clock domain (or an asynchronous input) changes near the sampling edge, a flip-flop can go metastable (an intermediate voltage that resolves unpredictably).

27.1 Metastability

When a signal from another clock domain (or an asynchronous input) changes near the sampling edge, a flip-flop can go metastable (an intermediate voltage that resolves unpredictably). It cannot be eliminated — only made improbable with synchronizers (MTBF analysis).

27.2 Two-flip-flop synchronizer (single bit)

Use 2–3 stages. Only for single-bit, slowly changing signals (levels).

signal sync_ff : std_logic_vector(1 downto 0) := "00";
attribute ASYNC_REG : string;
attribute ASYNC_REG of sync_ff : signal is "TRUE";     -- keep FFs close together (Xilinx)

process (clk_dst)
begin
  if rising_edge(clk_dst) then
    sync_ff <= sync_ff(0) & async_in;
  end if;
end process;
sync_out <= sync_ff(1);

27.3 Pulse synchronizer (toggle method)

-- Source domain: convert pulse to toggle
process (clk_src) begin
  if rising_edge(clk_src) then
    if pulse_src = '1' then toggle <= not toggle; end if;
  end if;
end process;
-- Destination: synchronize toggle and detect edges
process (clk_dst) begin
  if rising_edge(clk_dst) then
    sync <= sync(1 downto 0) & toggle;
  end if;
end process;
pulse_dst <= sync(2) xor sync(1);

27.4 Multi-bit CDC techniques

Handshake (req/ack with synchronizers) infrequent multi-bit transfers

Asynchronous FIFO (Gray-coded pointers) streaming data between domains Gray code counters counters crossing domains (only one bit changes)

MUX recirculation / enable synchronization data held stable while a synchronized enable crosses Vendor CDC macros

xpm_cdc_single, xpm_cdc_handshake, xpm_cdc_gray, xpm_fifo_async

Never synchronize a multi-bit bus bit-by-bit — bits can arrive in different cycles (data incoherency).

-- Binary ↔ Gray conversion
function bin2gray(b : unsigned) return unsigned is
begin
  return b xor shift_right(b, 1);
end function;

function gray2bin(g : unsigned) return unsigned is
  variable b : unsigned(g'range);
begin
  b(b'high) := g(g'high);
  for i in g'high - 1 downto g'low loop
    b(i) := b(i + 1) xor g(i);
  end loop;
  return b;
end function;

27.5 Reset strategies

Synchronous reset: simple timing, recommended by Xilinx for most logic.

Asynchronous reset: works without a clock; requires a reset synchronizer (assert async, de-assert sync):

Reset only what needs resetting (control state, valid flags), not data paths — saves routing and enables SRL/BRAM inference.

Hold resets until PLLs lock; sequence resets across domains.

process (clk, arst_n)
begin
  if arst_n = '0' then
    rst_sr <= (others => '0');
  elsif rising_edge(clk) then
    rst_sr <= rst_sr(rst_sr'high-1 downto 0) & '1';
  end if;
end process;
rst_n_sync <= rst_sr(rst_sr'high);

27.6 Clock management

Use PLL/MMCM for frequency synthesis and phase shifting, global buffers for distribution, avoid logic-generated clocks, constrain generated clocks, and analyze CDC with tools (Vivado report_cdc, Questa CDC, SpyGlass).