All guides
RTL Design8 min readUpdated Aug 8, 2026

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.

Two-flop clock-domain-crossing synchronizerA single-bit signal from clock domain A is resampled by two back-to-back flip-flops in clock domain B; the first flop may go metastable, the second lets it settle before use.Clock domain AClock domain B (2-flop synchronizer)DQSourceDQFF1DQFF2async crossingmay be metastablesyncedclk Aclk Bclk B
A two-flop synchronizer: the first flop in the destination domain may go metastable; the second gives it a full clock period to settle before any logic uses it.

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.

two_ff_sync.sv — 1-bit level synchronizer
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};
endmodule

When 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 crossingUse this
Single-bit level/flagTwo-flop synchronizer
Multi-bit counter/pointerGray code (only one bit changes per step)
Multi-bit data wordHandshake (req/ack) or an async FIFO
Bulk data streamAsynchronous FIFO with gray-coded pointers
Narrow pulseToggle/pulse synchronizer
Match the CDC technique to what you are crossing.

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.

Related guides