Almost every block has the same two crossings: configuration flowing in from software (slow, infrequent, must never tear) and status flowing out (flags, counters, event reports). Three standard patterns cover both directions; picking the wrong one is behind a large share of control-path CDC bugs.
Pattern | How it works | Fits | Costs |
|---|---|---|---|
Handshake | Hold the value stable, cross a request, capture on arrival, return an acknowledge. | Any multi-bit value that can change at any time; the general answer. | Round-trip latency on every update. |
Shadow register with load pulse | Software writes a shadow copy freely, then a single synced load pulse copies it to the live register at once. | Configuration written in bursts, applied atomically. | Double the flops; the load pulse itself needs a pulse-crossing structure. |
Qualified capture | The destination samples the bus only when the source guarantees it is stable (idle, held, or version-guarded). | Quasi-static signals that provably do not change while in use (see the quasi-static topic). | The proof obligation: stability must be guaranteed, not hoped for. |
// snippet
// shadow register with a load pulse: the live register updates atomically
always_ff @(posedge clk_cfg) begin
if (wr_en) shadow <= wr_data; // software writes freely
if (load_q) live <= shadow; // one synced pulse copies everything at once
endThe snippet shows the destination side: ordinary software writes land in shadow, and a single synchronised load_q pulse commits the whole word to live in one cycle. Nothing the datapath uses ever sees a half-written value.
Status kind | Crossing |
|---|---|
Single independent flags (idle, error latched) | One two-flop synchroniser per flag; they have no timing relationship to keep. |
A counter or event count | Gray code if it only increments; handshake or FIFO if software reads must not skip values. |
A multi-bit measurement sampled with a “ready” strobe | Hold the measurement, cross the strobe as an event, capture on arrival: the same handshake shape as configuration. |
For every configuration register: which pattern, and where the stability or acknowledge guarantee is written. For every status path: what software observes if it reads mid-update, and why that observation is acceptable. “Software retries” is not an answer unless the retry protocol is specified with the register.
The datapath can sample the bus mid-update and compare against a torn value. A shadow register with a load pulse fixes it: software writes the shadow, one synced pulse commits the live copy atomically.