HDLbits
Start Practicing

RTL Design Patterns/Clock Domain Crossing

Pulse Synchronizer

hard
cdcsynchronizerpulse

The problem with synchronizing a pulse directly

A 2-flop synchronizer (see that problem in this category) is only safe for signals that stay stable long enough to be reliably sampled. A single-cycle pulse in the source domain is the opposite of that: if the destination clock is slower than the source clock, or even just unluckily phased, a plain 2-flop synchronizer can sample 0 on either side of the pulse and miss it entirely. You cannot fix this by just making the flops faster — the pulse’s existence itself is too brief relative to the destination’s sampling rate.

The fix: toggle, synchronize the level, then edge-detect

Instead of trying to synchronize the pulse, synchronize a level that is guaranteed to still be there on the next destination clock edge:

  1. In the source domain, flip a toggle bit every time src_pulse fires. This turns a one-shot pulse into a persistent level change — exactly the job of the Toggle Synchronizer problem elsewhere in this category, which this module builds on directly.
  2. 2-flop synchronize toggle into the destination domain (meta, sync).
  3. In the destination domain, XOR the synchronized toggle against its own previous value (sync_d). Since the toggle changes exactly once per source pulse, this XOR produces exactly one destination-domain clock cycle of 1 per event — a clean, reconstructed pulse.

Cycle-by-cycle example

src_clk cycle src_pulse toggle … dst_clk cycle sync sync_d dst_pulse
0 1 0→1 0 0 0 0
1 0 1 1 0 0 0
2 1 0 1
3 1 1 0

Correctness constraints

  • Pulse spacing: consecutive src_pulse assertions must be separated by enough dst_clk cycles for the toggle change to propagate through both synchronizer flops and be edge-detected — in practice, at least 3 destination clock cycles of margin, more if you want additional metastability-resolution headroom. Violating this drops pulses (a second toggle arriving before the first has been detected can even cancel out and produce zero output pulses for two input pulses).
  • dst_pulse must be exactly one dst_clk cycle wide, regardless of how the two clocks are related in frequency or phase.
  • Every src_pulse (respecting the spacing constraint) must produce exactly one dst_pulse — no drops, no duplicates.
  • The toggle flip-flop must live entirely in the source domain, and the synchronizer + edge detector must live entirely in the destination domain; sync_d must not be combinationally derived from anything but sync, or the edge detection becomes unreliable.