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_eventpulses must be spaced at least 2–3dst_clkcycles 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_toggleanddst_toggle— no fewer (metastability risk) and no combinational shortcuts.