90+ free clock domain crossing questions, from metastability and synchronizers to async FIFOs, reconvergence, RDC and silicon debugging. Includes 18 worked answers with code, 8 solved numericals, a CDC tool report walkthrough and an MTBF calculator.
580 questions with interview-ready answers and tips, 20 coding exercises with solutions, 25 solved numericals and 30 debugging scenarios.
Clock domain crossing bugs are some of the hardest in chip design: they pass RTL simulation, then show up as rare hangs in the lab or in the field. That's why CDC appears in RTL, verification, SoC, FPGA and even STA interviews, and why interviewers keep asking "and then what?" after "use two flops".
This page is built around that. Start with the formula sheet, the MTBF calculator and the technique guide, browse 50 questions across ten topics, then work through the worked answers, numericals and the CDC report walkthrough. Try each one aloud, or on paper for the coding ones, before opening the answer.
Metastability, MTBF, why two flops, same-frequency trick questions, pulses into slower domains. Often with a quick code-writing task: a synchronizer or a safe edge detector.
Why not bit by bit, handshakes, MUX-enable, Gray code, drawing the async FIFO and its flags, and sizing it. This is the core of most RTL and DV rounds.
Reconvergence, RDC, constraints, waivers, CDC tool reports, metastability injection, and "the chip hangs once a week at high temperature". They're testing method.
Level or pulse? One bit or many? Occasional or streaming? Related or asynchronous clocks? The technique follows from the answers, so say them aloud.
The universal recipe for multi-bit data: hold the bus stable and synchronize a single qualifier, or use Gray code so only one bit ever changes.
Synchronizers lower the failure rate, they don't remove it. Saying how resolution time, frequency and stage count change MTBF is a strong signal.
Glitches from combinational logic before a synchronizer can be captured as real pulses. Always drive a crossing from a flop in the source domain.
Constraints control timing analysis; they don't make a crossing safe. Clock groups for async clocks, bounded max-delay for Gray buses, real synchronizers in RTL.
Structural CDC tool, reviewed waivers, assertions on protocols, metastability injection in simulation, and a check that implementation didn't break the synchronizers.
The relationships behind synchronizers, handshakes and FIFOs.
Add a stage or lower the clock and watch MTBF jump. Values update as you type.
The question behind most CDC interview questions. Classify the signal, then choose.
| TECHNIQUE | CROSSES | LATENCY | THROUGHPUT | KEY RULE |
|---|---|---|---|---|
| Two-flop synchronizer | 1-bit level | 2–3 dest cycles | Slow changes only | Registered source, no logic between stages |
| Toggle (pulse) synchronizer | 1-bit event | 2–3 dest cycles | 1 event per ~3 cycles | Respect minimum event spacing |
| Four-phase handshake | Multi-bit value | ~4–6 cycles each side | Low | Hold data until acknowledged |
| MUX-enable / qualifier | Multi-bit value | ~3 cycles | Low–medium | Synchronize only the enable |
| Gray-coded counter | Counter or pointer | 2–3 cycles | Every cycle | Only ±1 steps; bound bus skew |
| Asynchronous FIFO | Data stream | ~3–5 cycles | Full rate | Gray pointers, conservative flags |
| Reset synchronizer | Reset release | 2–3 cycles | — | Assert async, de-assert sync, one per domain |
A sample of what interviewers draw from most. The full 580-question ebook covers each topic in much more depth, with every question answered.
What a crossing is and why it's dangerous.
The physics and the numbers.
The two-flop synchronizer and its rules.
ASYNC_REG attribute (or a dedicated sync cell) do?Crossing a single-cycle event without losing it.
Where most real CDC bugs live.
The most-asked CDC design question.
When correct synchronizers still give wrong results.
Resets cross domains too.
Keeping synchronizers intact through the flow.
set_max_delay -datapath_only: when do you use each?Proving it, and the crossings in real SoCs.
580 questions across 27 chapters, all answered: 20 coding exercises with solutions, 25 solved numericals, 30 debugging scenarios, plus RDC, glitch-free clock switching, FPGA and low-power CDC.
Try each one out loud (or on paper for the coding ones) first, then open it. Every answer shows what a strong reply covers, what the interviewer is really listening for, and the mistake that costs candidates the most.
A CDC is any signal launched by a flop on one clock and captured by a flop on another clock that has no fixed, known phase relationship to it. STA can't guarantee setup and hold because the capture edge can land anywhere relative to the data change.
None of these show up in ordinary RTL simulation, because the simulator has no metastability and resolves every edge cleanly. That's why CDC needs its own design rules and its own verification.
A flop's storage element is a pair of cross-coupled inverters with two stable states and one unstable balance point. If the input changes inside the setup–hold window, the latch can be left near that balance point. Positive feedback eventually pushes it to 0 or 1, but the time it takes is random: the probability of still being unresolved after time t falls as e−t/τ.
MTBF = e^(t_r / τ) / (T0 · f_clk · f_data)
Because tr is in the exponent, a little more resolution time buys an enormous MTBF gain, while frequency only acts linearly.
// src_level must come straight from a flop in the source domain
(* ASYNC_REG = "TRUE" *) logic [1:0] sync;
always_ff @(posedge clk_dst or negedge rst_dst_n)
if (!rst_dst_n) sync <= '0;
else sync <= {sync[0], src_level};
assign dst_level = sync[1];A one-cycle pulse at 500 MHz lasts 2 ns. A 50 MHz destination samples every 20 ns, so it will usually miss it. Options:
// source domain
always_ff @(posedge clk_src) if (pulse_src) tog <= ~tog;
// destination domain
always_ff @(posedge clk_dst) s <= {s[1:0], tog}; // s[0], s[1] synchronize
assign pulse_dst = s[2] ^ s[1];Each bit has its own routing delay and its own metastability resolution, so when several bits change at once, some may be captured this cycle and others next cycle. The destination can see a value that never existed in the source.
Example: a counter goes from 0111 to 1000. All four bits change. The destination might capture 1111, 0000 or any mix for one cycle.
The fix follows one principle: either only one bit changes at a time, or the data is held stable while a single control bit is synchronized. That gives Gray code, handshakes, MUX-enable schemes and async FIFOs.
The one exception is a Gray-coded pointer, where per-bit synchronization is safe precisely because only one bit changes per step.
always_ff @(posedge clk or negedge arst_n)
if (!arst_n) rs <= 2'b00; // assert immediately
else rs <= {rs[0], 1'b1}; // release synchronously
assign rst_n_sync = rs[1];Reset asserts asynchronously, so the domain is reset even without a running clock. It de-asserts synchronously: the release passes through two flops, so every flop in the domain sees reset end on the same clock edge with valid recovery and removal timing.
Each clock domain needs its own reset synchronizer. If some flops leave reset a cycle before others, an FSM can start in a corrupt state.
Only REQ and ACK are synchronized; the data bus is held stable, so it can be captured safely. Each transfer costs two synchronizer delays in each direction, so throughput is low. That's fine for configuration values or occasional status words; for streams use an async FIFO.
A two-phase handshake uses transitions instead of levels, halving the round trips at the cost of slightly more complex edge detection.
rgray == wgray_sync.wgray == {~rgray_sync[N:N-1], rgray_sync[N-2:0]}.The synchronized pointer is a few cycles old. That makes the flags pessimistic: empty stays set a little after data arrives, full stays set a little after space frees up. They never allow overflow or underflow, which is what matters.
Reconvergence happens when two or more signals cross separately, each through its own synchronizer, and then combine in the destination. Each synchronizer can resolve in a different cycle, so for one cycle the destination can see a combination the source never produced.
Example: an FSM's one-hot state bits are synchronized individually and decoded in the destination. During a state change, the decoder can briefly see two hot bits or none and take a wrong branch.
Fixes:
# Truly asynchronous clocks: stop STA timing the crossings
set_clock_groups -asynchronous -group {clk_a} -group {clk_b}
# Gray-coded pointers: bound skew instead of ignoring the path
set_max_delay -datapath_only 2.0 \
-from [get_cells wptr_gray_reg*] -to [get_cells wptr_sync1_reg*]Constraints never make a crossing safe; the RTL scheme does. They just make timing analysis honest.
RDC happens when a flop with one asynchronous reset drives a flop with a different reset (or none), even if both use the same clock. If the source is reset while the destination isn't, the source's output changes at an arbitrary time relative to the clock, so the destination can go metastable or capture a corrupt value.
Typical example: a block is reset by software while a neighbour on the same clock keeps running and samples its outputs.
Fixes:
A plain mux on two asynchronous clocks can produce a runt pulse when the select changes, clocking some flops and not others. A glitch-free clock mux makes the switch in two steps:
(clk0 & en0) | (clk1 & en1).So the old clock stops cleanly on its low phase before the new one starts on its own low phase. Both clocks must be running during the switch, or the mux can hang; designs that might switch away from a dead clock need a bypass or a timeout.
ASYNC_REG and are placed next to each other.Then put an ILA on flags and pointers, triggered on the corruption.
Treat the claim as something to prove, not accept:
Measure the worst-case event rate against the destination clock. If events can arrive close together, use a counter crossed with Gray code, or an async FIFO, instead of single events. For gated domains, use sticky levels or wake the clock first.
Protect synchronizers with dedicated sync cells or dont_touch/ASYNC_REG attributes, exclude them from retiming, and re-run CDC analysis on the synthesized netlist, not only on RTL.
A binary counter changes many bits at once (think 0x...FFFF → 0x...0000), so it can't be synchronized bit by bit. Options:
Quick calculations come up in CDC rounds and design reviews. Work each one on paper first, then tap to check.
T = 1.25 ns → t_r = 1.25 − 0.25 = 1.00 ns Denominator = 50e-12 × 800e6 × 20e6 = 8 × 10^5 MTBF = e^(1.0/0.04) / 8e5 = e^25 / 8e5 ≈ 9.0 × 10^4 s ≈ 25 hours Third stage: t_r = 2.25 ns → e^56.25 / 8e5 ≈ 3.4 × 10^18 s
Note: one extra cycle of latency takes MTBF from about a day to roughly 100 billion years. These are the default values in the calculator above.
Failure rates add: Chip MTBF = 10^13 / 5,000 = 2 × 10^9 s ≈ 63 years Fleet MTBF = 2 × 10^9 / 10^6 = 2,000 s ≈ 33 minutes
Note: a chip that looks safe on its own still fails somewhere in the field every half hour at that volume. That's why MTBF targets are set per product, not per synchronizer.
Dest period = 12.5 ns → hold ≥ 1.5 × 12.5 = 18.75 ns Source period = 2.5 ns → 18.75 / 2.5 = 7.5 → 8 cycles (20 ns)
Note: add margin if the destination frequency can drop (DVFS or clock tolerance).
Minimum spacing = 3 × 20 ns = 60 ns Max rate = 1 / 60 ns ≈ 16.7 million events per second
Note: faster bursts need a counter crossed with Gray code, or a FIFO, instead of single events.
REQ↑ into dest : 2–3 dest cycles ≈ 25 ns ACK↑ into src : 2–3 src cycles ≈ 12.5 ns REQ↓ into dest : ≈ 25 ns ACK↓ into src : ≈ 12.5 ns Round trip ≈ 75 ns → 64 bits / 75 ns ≈ 0.85 Gb/s
Note: an async FIFO on the same 64-bit bus could move a word every destination cycle: 6.4 Gb/s.
Write rate = 300 × 0.8 = 240 M words/s Burst time = 1,200 / 240 M = 5.0 µs Words read = 5.0 µs × 150 M = 750 Depth ≥ 1,200 − 750 = 450 → choose 512
Note: 512 leaves 62 words for synchronizer latency on the pointers and reader start-up.
Binary → Gray: g = b ^ (b >> 1) 1011 ^ 0101 = 1110 Gray → binary (MSB down): b3 = 1 b2 = b3 ^ g2 = 1 ^ 0 = 1 b1 = b2 ^ g1 = 1 ^ 0 = 1 b0 = b1 ^ g0 = 1 ^ 1 = 0 → 1110
Note: Gray to binary is a chain of XORs, so it's slower; do it after the synchronizer, not before.
Worst-case difference = 150 + 150 = 300 ppm = 3 × 10^-4 Slip every 1 / 3e-4 ≈ 3,333 symbols
Note: protocols insert skip or idle symbols more often than that, so the buffer can drop or repeat them to stay centred.
"Here's our CDC report. What would you look at?" is a common senior question. This summary has three real violations hiding among the numbers. Generic, tool-neutral format.
CDC Analysis Summary Design: soc_periph_top Mode: functional Clock domains: 6 Asynchronous clock pairs: 9 Reset domains: 4 Category Count -------------------------------------------------- Crossings analysed 648 (1) Recognised synchronizers (2/3-flop) 402 Recognised FIFOs / handshakes 61 Waived 158 (2) Violations 27 No synchronizer 9 (3) Combinational logic before synchronizer 6 (4) Multi-bit bus synchronized bit by bit 5 (5) Reconvergence of synchronized signals 4 (6) Async reset crossing (RDC) 3 (7) -------------------------------------------------- Clocks inferred (not in constraints) 2 (8) Unclocked registers 14
The setup: the 2 inferred clocks and 14 unclocked registers. Until every clock is defined with the right relationships, the counts above can't be trusted, and crossings may be missing from the report entirely. Then the 9 missing synchronizers and the 6 combinational-logic cases, which are usually real bugs.
Not automatically, but it's high. Review them by category: quasi-static configuration registers and properly documented static signals are fine with an assertion; broad waivers by module or by wildcard are not. Every waiver should name the signal, the reason, who approved it and how the assumption is checked.
For each one, ask whether only one bit can change at a time (Gray), or whether it's truly static while the destination is active. If either can be proven, waive precisely and add an assertion. Otherwise redesign with a handshake, a qualifier with held data, or a FIFO.
Not yet. The analysis also has to run on the synthesized netlist (synthesis can break synchronizers), constraints must bound the Gray and handshake data paths, RDC needs its own sign-off, and protocols should be checked with assertions and metastability-injection simulation. The ebook walks through a detailed CDC tool report in Chapter 18.
Screening rounds often open with fast checks like these. Aim to answer each in under ten seconds, then tap to check.
No. Their phase drifts, so treat them as asynchronous.
No. It makes failure unlikely; MTBF measures how unlikely.
Resolution time, because it's in the exponent.
Combinational logic can glitch, and the glitch can be captured.
Very fast clocks or strict MTBF targets.
Marks synchronizer flops so they're kept together and protected from optimisation.
Toggle synchronizer, pulse stretching or handshake.
Hold a level at least ~1.5 destination periods.
Synchronize one control bit; hold the data stable.
Only one bit changes per increment.
Read Gray pointer equals synchronized write Gray pointer.
No, pessimistic: they release late, never early.
Separately synchronized signals combining with different latencies.
Assert asynchronously, de-assert synchronously.
At least one per clock domain.
A crossing between flops with different resets, even on one clock.
set_clock_groups -asynchronous.
set_max_delay -datapath_only of about one destination period.
No. It only stops timing analysis.
Randomly delaying synchronizer outputs in simulation to model real resolution.
Two-flop synchronizers are for single-bit levels. Pulses get lost and buses become incoherent. Classify the signal first.
A decode or mux of several source flops can glitch. Register the crossing signal in the source domain.
The first flop may be metastable. Detect edges on the synchronized output and its delayed copy.
A false path or clock group silences STA. It doesn't make the crossing safe, and on Gray buses it leaves skew unbounded.
One reset synchronizer shared across domains, or a block reset while its neighbour keeps running, causes RDC bugs that CDC reports don't catch.
RTL simulation has no metastability. Use CDC tools, reviewed waivers, assertions and metastability injection.
Almost all digital roles: RTL design, design verification, SoC integration, FPGA and often STA. Freshers get synchronizer and multi-bit questions; experienced candidates get async FIFOs, reconvergence, RDC, constraints and debugging scenarios.
Only for a single-bit level that changes slowly compared with the destination clock. Pulses need a toggle synchronizer or handshake, multi-bit data needs Gray code, a handshake, MUX-enable or an async FIFO, and very fast clocks may need a third stage for enough MTBF.
Normal RTL simulation can't model metastability, so most CDC bugs pass it. Teams use structural CDC tools, metastability injection in simulation, assertions and formal checks, plus correct constraints for timing.
No. A false path or clock group only tells STA not to time the path; it does nothing to make the crossing safe. The RTL still needs a correct synchronization scheme, and Gray-pointer crossings should get a bounded set_max_delay -datapath_only instead of a plain false path.
Reset domain crossing happens when a flop reset by one reset drives a flop with a different reset, even on the same clock. An asynchronous reset asserting in the source can change its output at any time relative to the destination clock, causing metastability or corruption. It needs its own analysis and fixes.
Learn metastability and MTBF, the two-flop synchronizer, why buses can't be synchronized bit by bit, toggle synchronizers, handshakes, Gray code and the async FIFO. Practise writing the synchronizer, edge detector and Gray converter, and drawing the async FIFO from memory. Then try our RTL design questions.
Book a 1:1 mock interview with a mentor working in this exact domain.
Book a mock interview