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:
- In the source domain, flip a
togglebit every timesrc_pulsefires. 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-flop synchronize
toggleinto the destination domain (meta,sync). - 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 of1per 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_pulseassertions must be separated by enoughdst_clkcycles 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_pulsemust be exactly onedst_clkcycle wide, regardless of how the two clocks are related in frequency or phase.- Every
src_pulse(respecting the spacing constraint) must produce exactly onedst_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_dmust not be combinationally derived from anything butsync, or the edge detection becomes unreliable.