HDLbits
Start Practicing

RTL Design Patterns/Clock Domain Crossing

Toggle Synchronizer

medium
cdcsynchronizertoggle

The building block behind toggle-encoded CDC

A raw 2-flop synchronizer (see the 2-Flop Synchronizer problem in this category) safely resynchronizes a single bit, but only if that bit changes slowly relative to the destination clock and is a genuine level (something the destination is meant to keep sampling, not a discrete one-shot event). A toggle-encoded signal is a natural fit: instead of pulsing high for one cycle whenever “something happened,” the source domain flips a single bit’s state every time an event occurs. Because it’s toggle-encoded, it’s guaranteed to change by exactly one bit-flip per event and to hold that new value until the next event — exactly the kind of signal a 2-flop synchronizer is built to cross safely.

This module packages that pattern as a standalone, reusable primitive: generate the toggle in the source domain from an event pulse, and 2-flop-synchronize the resulting level into the destination domain. The output, dst_toggle, is still toggle-encoded — it is deliberately not edge-detected into a clean one-cycle pulse here.

Relationship to the Pulse Synchronizer

The Pulse Synchronizer problem (elsewhere in this category) builds on exactly this structure, adding one more register stage in the destination domain that XORs dst_toggle against its own previous sample to produce a clean single-cycle dst_pulse. Use this Toggle Synchronizer directly when the destination-domain consumer wants the raw toggle level — for example, to compare parity against its own local toggle state, or to drive a level-sensitive downstream block — rather than react to a discrete edge. If you need an actual one-cycle pulse in the destination domain, either use the Pulse Synchronizer problem’s packaged solution, or add the edge-detector yourself downstream of this module:

reg dst_toggle_d;
always @(posedge dst_clk) dst_toggle_d <= dst_toggle;
wire dst_event_pulse = dst_toggle ^ dst_toggle_d;

Interface

Signal Domain Description
src_clk source Source-domain clock.
src_rst source Synchronous reset for the source domain.
src_event source Single-cycle pulse: flip the internal toggle.
dst_clk dest Destination-domain clock — may run at any frequency vs. src_clk.
dst_rst dest Synchronous reset for the destination domain.
dst_toggle dest Synchronized toggle-encoded level; changes state once per event.

Correctness constraints

  • src_event pulses must be spaced at least 2–3 dst_clk cycles apart. If a second event arrives before the destination domain has captured the toggle from the first one, the two toggles collapse into a net-zero (or ambiguous) change and an event is lost — this is a fundamental limit of toggle encoding, not an implementation bug.
  • The toggle must be generated with a genuine flip-flop in the source domain (~src_toggle, not a combinational signal) so it presents a stable, glitch-free level to the synchronizer.
  • Exactly 2 flip-flop stages must sit in the destination domain between src_toggle and dst_toggle — no fewer (metastability risk) and no combinational shortcuts.