Clock Domain Crossing (CDC) and Metastability
Why signals crossing clock domains go metastable, how the two-flop synchronizer works, and when you must use gray codes, handshakes or async FIFOs instead — with a diagram and RTL.
Modern SoCs have dozens of asynchronous clocks. Any signal that travels from one clock domain to another can be sampled while it is still changing — and that is where designs quietly break. Clock Domain Crossing (CDC) is a favourite interview topic because it separates people who memorised RTL from people who understand timing.
Why crossing clocks is dangerous
When a receiving flip-flop samples a signal that violates its setup/hold window (which is unavoidable across asynchronous clocks), its output can hover between 0 and 1 for a short, unpredictable time. This is metastability. If downstream logic reads that half-settled value, different gates may interpret it differently and the design fails intermittently — the worst kind of bug.
The two-flop synchronizer
For a single-bit, level-encoded signal, two back-to-back flip-flops clocked by the destination clock are the standard fix. The first flop may go metastable, but it has an entire destination clock period to resolve before the second flop samples it. This pushes the mean-time-between-failures (MTBF) out to effectively astronomical values.
module two_ff_sync (input logic dclk, rst_n, async_in,
output logic sync_out);
logic meta;
always_ff @(posedge dclk or negedge rst_n)
if (!rst_n) {sync_out, meta} <= 2'b00;
else {sync_out, meta} <= {meta, async_in};
endmoduleWhen two flops are not enough
A two-flop synchronizer only works for one bit. Push a multi-bit bus through parallel synchronizers and the bits can settle on different clock edges, so the receiver briefly sees a value that never existed. You need a technique that guarantees coherency.
| What you're crossing | Use this |
|---|---|
| Single-bit level/flag | Two-flop synchronizer |
| Multi-bit counter/pointer | Gray code (only one bit changes per step) |
| Multi-bit data word | Handshake (req/ack) or an async FIFO |
| Bulk data stream | Asynchronous FIFO with gray-coded pointers |
| Narrow pulse | Toggle/pulse synchronizer |
Watch out. Never run a multi-bit bus through independent parallel two-flop synchronizers. The bits will arrive on different cycles and the receiver can latch a value that was never driven.
Common interview questions
- What is metastability and why can't you eliminate it entirely?
- How does a two-flop synchronizer improve MTBF?
- Why can't you synchronize a multi-bit bus with parallel 2-FF synchronizers?
- Why are FIFO pointers gray-coded?
- Design a pulse synchronizer between a fast and a slow clock.
Put this into practice
Drill RTL / Micro-architecture Design questions from real interview loops, then book a mock with a mentor who runs them.