RTL Studio Open IDE

Wiki Timing & CDC Clock-domain crossing

Clock-domain crossing

Synchronizers, handshakes, and FIFO-based CDC.

A signal is a CDC when the sampler’s clock is asynchronous (or plesiochronous) to the source. Crossing incorrectly causes metastability and data incoherence.

Single-bit: 2FF synchronizer

For a level that changes slowly relative to the destination clock:

always_ff @(posedge clk_dst) begin
  sync_ff1 <= async_in;
  sync_ff2 <= sync_ff1;
end
assign sync_out = sync_ff2;

Rules:

  • Synchronize one bit this way — not a multi-bit bus (bits can skew).
  • Do not synchronize the same async net with two independent chains and then combine them.
  • Pulse → level: stretch or use a toggle synchronizer / handshake.

Multi-bit: don’t 2FF each bit

Use one of:

Method Use case
Handshake (req/ack) Occasional messages
Async FIFO + gray pointers Streaming data
Shared synchronous domain Preferable if you can avoid CDC

Gray codes & pointers

Async FIFO write/read pointers are often gray-encoded so only one bit changes when incrementing — a single-bit synchronizer can track them safely (with care for full/empty).

Metastability

The first sync FF can go metastable; the second gives it time to resolve. You reduce MTBF with more stages or slower async toggle rates — you never get probability zero.

Checklist

  1. List every clock in the design.
  2. Mark every signal that crosses.
  3. Pick a named pattern (2FF / handshake / FIFO) — no “drive it and pray”.
  4. Keep combo logic off async inputs before the first sync FF when possible.