Clock-Domain Crossing
A clock-domain crossing, or CDC, happens when a signal created in one clock domain is sampled by logic in another clock domain. CDC bugs are difficult because they can pass simulation, pass timing in each domain, and still fail randomly in hardware.
Learning Objectives
You will learn to:
- identify clock-domain crossings in an FPGA design;
- explain metastability in practical engineering terms;
- use a two-flop synchronizer for a single-bit level signal;
- choose safe methods for pulses and multi-bit data;
- review asynchronous FIFO and reset crossings.
What Counts as a Clock Domain?
A clock domain is a group of registers that use the same clock edge and timing relationship. These are separate domains:
- unrelated oscillators;
- a board clock and a recovered communication clock;
- two PLL outputs that the timing constraints do not define as related;
- logic before and after a divided or gated fabric clock;
- an external asynchronous input such as a button, interrupt, or sensor pin.
If the timing relationship is unknown or intentionally asynchronous, treat the crossing as CDC.
Metastability
If a signal changes too close to a receiving clock edge, the first receiving flip-flop can enter a temporary analog state between valid 0 and 1. It eventually resolves, but the question is whether it resolves before downstream logic samples it.
title "Illustrative CDC hazard"
time start=0 end=10 unit=ns divisions=10
CLK_A: square label="source clock" low=0 high=1 duty=50 cycles=2 unit=logic color=#2563eb
CLK_B: square label="destination clock" low=0 high=1 duty=50 cycles=3 phase=20 unit=logic color=#7c3aed
SIG_A: step label="signal from clk A" low=0 high=1 at=4.7 unit=logic color=#dc2626
SYNCED: step label="safe after sync latency" low=0 high=1 at=7 unit=logic color=#16a34a
marker RISK at=5 label="near edge"
This waveform is explanatory. Metastability is not a digital X that simulation can model reliably.
Two-Flop Synchronizer
Use a two-flop synchronizer for a single-bit level signal that changes slowly compared with the destination clock.
reg sync1;
reg sync2;
always @(posedge dst_clk) begin
sync1 <= async_signal;
sync2 <= sync1;
end
assign safe_signal = sync2;
The first flop may become metastable. The second flop gives the first more time to resolve before the signal fans out. For high-reliability or high-speed systems, a third flop may be used, but it adds latency.
Do not put combinational logic between synchronizer stages. Keep the synchronizer flops close together using vendor attributes when needed.
Pulse Crossing
A one-cycle pulse in the source domain may be missed by the destination domain, especially if the destination clock is slower.
Safe options:
- stretch the pulse long enough for the destination clock;
- convert the pulse to a toggle, synchronize the toggle, then edge-detect it;
- use a request/acknowledge handshake;
- use a FIFO when the pulse represents data movement.
Multi-Bit Data
Never synchronize an ordinary multi-bit bus bit-by-bit. Different bits can settle on different destination clock edges, producing a value that never existed in the source domain.
| Crossing type | Safe method |
|---|---|
| Single-bit level | Two-flop synchronizer |
| Single pulse | Toggle synchronizer or handshake |
| Configuration word | Hold data stable, cross valid, acknowledge receipt |
| Streaming data | Asynchronous FIFO |
| Counter pointer | Gray code plus pointer synchronization |
| Reset release | Synchronizer in each destination domain |
Worked Example: Button Input
A mechanical button is asynchronous to the FPGA clock and also bounces. A safe path is:
The synchronizer handles clock-domain safety. The debounce counter handles mechanical contact bounce. The edge detector creates a clean one-cycle event inside the FPGA clock domain.
Worked Example: Byte Transfer Between Domains
Suppose a UART receiver runs in uart_clk and a processor bus runs in sys_clk. Do not cross rx_data[7:0] and rx_valid directly.
Better choices:
- small asynchronous FIFO if bytes can arrive while the bus is busy;
- valid/ready handshake if the source can wait;
- vendor FIFO IP for production designs unless you have reviewed the FIFO implementation carefully.
CDC Review Checklist
For every module boundary:
- list source and destination clock domains;
- mark single-bit controls, pulses, and multi-bit data separately;
- verify synchronizers have no combinational logic between stages;
- verify buses use handshake, gray-coded pointers, or FIFO structures;
- verify resets release synchronously per domain;
- verify CDC paths are not hidden by false-path constraints without matching RTL structures;
- inspect tool CDC reports when available.
Common Mistakes
- Assuming simulation proves CDC safety.
- Synchronizing a multi-bit bus one bit at a time.
- Crossing a one-cycle pulse directly.
- Adding a false path without adding a synchronizer or handshake.
- Treating asynchronous reset release as harmless.
- Feeding a raw button or connector signal directly into a state machine.
Summary
CDC failures are intermittent because clock edges do not keep a fixed safe relationship. Use two-flop synchronizers for single-bit levels, pulse/toggle or handshake schemes for events, and asynchronous FIFOs for data streams. Review every crossing explicitly; do not let timing exceptions become a substitute for CDC design.
Next: Block RAM, ROM, and FIFOs.
Further Reading
- FPGA vendor CDC methodology and report documentation
- Clifford Cummings papers on metastability and asynchronous FIFO design
- AMD UltraFast Design Methodology Guide CDC guidance
- Intel FPGA reset and CDC design recommendations