Difficulty: Medium · Puzzle 2 of 6 · Topic: Digital Logic Puzzles
A 1-cycle-wide pulse in a 100 MHz domain (clk_f) must be detected in a 29 MHz domain (clk_s, period 34 ns). Why does a plain 2-flop synchronizer on the pulse lose events, and what is the minimum circuit that never loses one (pulses at least 3 slow cycles apart)?
// naive: pulse is only 10 ns wide, clk_s samples every 34 ns
always_ff @(posedge clk_s) {s2, s1} <= {s1, pulse_f};
// toggle synchroniser
always_ff @(posedge clk_f) if (pulse_f) tog <= ~tog; // source domain
always_ff @(posedge clk_s) {t3, t2, t1} <= {t2, t1, tog}; // dest domain
assign pulse_s = t2 ^ t3; // one clk_s cycle per toggleA level lasting less than one destination period can fall between two destination edges. Convert the pulse into a level change.
1) The pulse is 10 ns wide; the destination samples every 34 ns. Unless a clk_s edge lands inside the pulse, the synchroniser never sees it. Zero-delay simulation of 10 pulses slid across the slow-clock phase detected only 4 of them.
2) The 2-flop synchroniser only fixes metastability of a sampled level; it cannot create a level that was never there at a sampling edge.
3) Fix: turn the event into a level change in the source domain (toggle a flop on every pulse), cross that level through the 2-flop synchroniser, and detect the edge in the destination with an XOR of the last two synchronised values.
4) The same simulation with the toggle synchroniser detected 10 of 10.
5) Limits: a toggle that flips twice within one destination period cancels out, so pulses must be at least about 3 destination cycles apart (2 for the synchroniser plus 1 margin). Faster events need a request/acknowledge handshake or an async FIFO with gray-coded pointers.
6) Rule of thumb for the naive version: the pulse must be wider than 1.5 destination periods.Answer: A narrow pulse can miss every slow-clock edge; convert it to a toggle in the source domain, synchronise the level, and XOR the last two synchroniser stages in the destination.
Checked by running the code in Icarus Verilog 13; the printed output supports the answer above. A zero-delay Icarus simulation of 10 pulses (10 ns wide, 100 MHz) slid across the 34 ns slow clock: the naive 2-flop synchroniser catches 4, the toggle synchroniser catches all 10.
Believing more synchroniser flops fix a lost pulse. MTBF math (previous puzzle) protects against metastability, not against an event that is never sampled.
What if pulses can arrive less than 3 slow-clock cycles apart? Model answer: the toggle synchroniser merges or cancels them, so use a four-phase req/ack handshake (rate-limited, each event needs a round trip) or an asynchronous FIFO with gray-code pointers (full throughput, absorbs bursts).
A 10 nanosecond pulse in the fast domain can fall entirely between two edges of the slow clock, which samples every 34 nanoseconds, so a plain two-flop synchroniser can miss it. The synchroniser only protects against metastability of a level that was sampled; it cannot create a level that was never there. So I turn the pulse into a level change by toggling a flop in the source domain, synchronise that level, and XOR the last two synchroniser outputs in the destination to get a one-cycle pulse. The limit is that pulses must be about three slow cycles apart, and the trap is thinking more synchroniser flops fix a lost pulse.