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
- List every clock in the design.
- Mark every signal that crosses.
- Pick a named pattern (2FF / handshake / FIFO) — no “drive it and pray”.
- Keep combo logic off async inputs before the first sync FF when possible.