Introduction to Clock Domain Crossing
In modern digital systems, it's common to have multiple clock domains operating at different frequencies or with different phase relationships. A Clock Domain Crossing (CDC) occurs whenever a signal generated in one clock domain is used in another clock domain. While this seems straightforward, CDC is one of the most challenging aspects of digital design and a frequent source of subtle, hard-to-debug failures.
The fundamental problem is that when a signal changes near the active edge of a clock in the receiving domain, the receiving flip-flop may enter an unstable state called metastability. Understanding and properly handling CDC is essential for creating reliable digital systems.
Understanding Metastability
What is Metastability?
A flip-flop has strict timing requirements: the input data must be stable for a setup time before the clock edge and hold time after the clock edge. When these requirements are violated, the flip-flop may enter metastability—a state where the output voltage hovers between logic 0 and logic 1, unable to settle to a valid logic level.
Valid Operation:
Data: ___/‾‾‾‾‾‾‾‾‾‾‾
Setup | Hold
Clk: _____|‾‾‾|_____
↑
Clean capture
Metastable Condition:
Data: _____/‾‾‾‾‾‾‾‾
Clk: _____|‾‾‾|_____
↑
Data changes during
setup/hold window
Why Metastability Matters
In metastability, several problems can occur:
Prolonged Decision Time: The flip-flop takes much longer than normal to settle to a valid logic level, potentially violating timing requirements for downstream logic.
Invalid Logic Level: The output might settle to a voltage in the "forbidden zone" between valid 0 and 1 levels, causing different gates to interpret it differently.
Oscillation: In rare cases, internal feedback in the flip-flop may cause temporary oscillation before settling.
Non-deterministic Output: The final settled value may be unpredictable, appearing random between successive occurrences.
The Mathematics of Metastability
The probability of metastability persisting for time t decreases exponentially:
P(metastability) = (T₀ / Tclk) × e^(-t/τ)
Where:
- T₀ = window of vulnerability (setup + hold time, ~10-100ps)
- Tclk = clock period
- t = available resolution time
- τ = metastability time constant (~10-100ps, process-dependent)
This equation reveals a crucial insight: we cannot eliminate metastability, but we can reduce its probability to acceptably low levels by providing sufficient resolution time.
Synchronizers: The Foundation of CDC
Two-Flip-Flop Synchronizer
The most common CDC solution is the two-flip-flop (2-FF) synchronizer:
module two_ff_synchronizer (
input wire clk_dest, // Destination clock
input wire rst_n,
input wire async_data, // Asynchronous input
output wire sync_data // Synchronized output
);
reg sync_ff1, sync_ff2;
always @(posedge clk_dest or negedge rst_n) begin
if (!rst_n) begin
sync_ff1 <= 1'b0;
sync_ff2 <= 1'b0;
end else begin
sync_ff1 <= async_data; // May go metastable
sync_ff2 <= sync_ff1; // Very unlikely to be metastable
end
end
assign sync_data = sync_ff2;
endmodule
How It Works:
First FF (sync_ff1): Captures the asynchronous input and may enter metastability if the input changes near the clock edge.
Second FF (sync_ff2): Samples the output of the first FF. By providing a full clock cycle for the first FF to resolve metastability, the probability of the second FF also going metastable becomes negligibly small (typically <10^-20 failures per second).
Critical Design Rules for Synchronizers
Rule 1: Never Use Synchronizer Outputs in Same Cycle
// WRONG - Using sync output immediately
always @(posedge clk) begin
sync_ff1 <= async_in;
result <= sync_ff1 & other_signal; // Timing violation!
end
// CORRECT - Give full cycle for resolution
always @(posedge clk) begin
sync_ff1 <= async_in;
sync_ff2 <= sync_ff1;
result <= sync_ff2 & other_signal; // Safe
end
Rule 2: Protect Synchronizer Flip-Flops from Optimization
Synthesis tools might optimize away or duplicate synchronizer flip-flops, breaking the synchronizer. Use synthesis attributes:
(* ASYNC_REG = "TRUE" *) reg sync_ff1;
(* ASYNC_REG = "TRUE" *) reg sync_ff2;
// Or synthesis directives
// synthesis syn_keep = 1
// synthesis preserve
Rule 3: One Bit at a Time
Never synchronize multi-bit buses directly with simple synchronizers—bits may arrive at different times due to different metastability resolution, causing glitches:
// WRONG - Multi-bit synchronizer (gray code exception noted later)
always @(posedge clk_dest) begin
sync_ff1 <= multi_bit_bus[7:0]; // BAD!
sync_ff2 <= sync_ff1;
end
Three-FF Synchronizers
For extremely high-reliability applications or very fast clocks, use three flip-flops:
always @(posedge clk_dest or negedge rst_n) begin
if (!rst_n) begin
sync_ff1 <= 1'b0;
sync_ff2 <= 1'b0;
sync_ff3 <= 1'b0;
end else begin
sync_ff1 <= async_data;
sync_ff2 <= sync_ff1;
sync_ff3 <= sync_ff2;
end
end
This reduces metastability probability to effectively zero (<<10^-30 failures per second) at the cost of additional latency.
Multi-Bit CDC Techniques
Gray Code for Counters
When crossing multi-bit counters between domains, use Gray code encoding where only one bit changes at a time:
// Binary to Gray code converter
function [3:0] bin_to_gray;
input [3:0] bin;
begin
bin_to_gray = bin ^ (bin >> 1);
end
endfunction
// Source domain
reg [3:0] counter_bin;
wire [3:0] counter_gray;
always @(posedge clk_src) begin
counter_bin <= counter_bin + 1;
end
assign counter_gray = bin_to_gray(counter_bin);
// Destination domain - safe to synchronize
reg [3:0] counter_gray_sync1, counter_gray_sync2;
always @(posedge clk_dest) begin
counter_gray_sync1 <= counter_gray;
counter_gray_sync2 <= counter_gray_sync1;
end
// Gray to binary converter for use
function [3:0] gray_to_bin;
input [3:0] gray;
integer i;
begin
gray_to_bin[3] = gray[3];
for (i = 2; i >= 0; i = i - 1)
gray_to_bin[i] = gray_to_bin[i+1] ^ gray[i];
end
endfunction
wire [3:0] counter_bin_dest = gray_to_bin(counter_gray_sync2);
Since only one Gray code bit changes at a time, even if metastability causes that bit to resolve incorrectly, the received value differs from the correct value by only ±1, which is often acceptable for counters.
Handshake Protocols
For arbitrary multi-bit data, use a handshake protocol:
// Source domain
reg [7:0] data_src;
reg req;
always @(posedge clk_src) begin
if (new_data_available && !req) begin
data_src <= new_data;
req <= 1'b1;
end else if (ack_sync) begin
req <= 1'b0;
end
end
// Synchronize req to destination
wire req_sync;
two_ff_synchronizer sync_req (
.clk_dest(clk_dest),
.async_data(req),
.sync_data(req_sync)
);
// Destination domain
reg [7:0] data_dest;
reg ack;
reg req_sync_d;
always @(posedge clk_dest) begin
req_sync_d <= req_sync;
// Detect rising edge of req
if (req_sync && !req_sync_d) begin
data_dest <= data_src; // Stable during req assertion
ack <= 1'b1;
end else if (!req_sync) begin
ack <= 1'b0;
end
end
// Synchronize ack back to source
wire ack_sync;
two_ff_synchronizer sync_ack (
.clk_dest(clk_src),
.async_data(ack),
.sync_data(ack_sync)
);
The handshake ensures data stability during transfer, but at the cost of latency and reduced throughput.
FIFO-Based CDC
For high-throughput multi-bit CDC, asynchronous FIFOs are the gold standard.
Dual-Clock FIFO Architecture
Write Domain (clk_wr) Read Domain (clk_rd)
[Data In] → [Write Logic] [Read Logic] → [Data Out]
↓ ↑
[Write Pointer] [Read Pointer]
↓ ↑
====================Memory====================
↓ ↑
[Gray Code Write Ptr] → [Sync] → [Gray Code]
[Gray Code Read Ptr] ← [Sync] ← [Gray Code]
Key FIFO CDC Concepts
Gray Code Pointers: Write and read pointers are maintained in Gray code and synchronized to the opposite domain to compute empty/full flags safely.
Full Detection: In the write domain, compare the synchronized read pointer (Gray) with the write pointer. Full when:
write_ptr_gray == {~read_ptr_sync[MSB], ~read_ptr_sync[MSB-1],
read_ptr_sync[MSB-2:0]}
Empty Detection: In the read domain, empty when:
read_ptr_gray == write_ptr_sync
Memory Structure: Dual-port RAM with separate read/write ports:
reg [7:0] fifo_mem [0:15];
// Write port
always @(posedge clk_wr) begin
if (wr_en && !full)
fifo_mem[wr_addr] <= wr_data;
end
// Read port
always @(posedge clk_rd) begin
rd_data <= fifo_mem[rd_addr];
end
FIFO Depth Considerations
FIFO depth must account for:
- Burst sizes from producer
- Synchronization latency (2-3 cycles)
- Clock frequency ratios
- Tolerance for back-pressure
A general rule: depth ≥ max_burst_size + (sync_latency × freq_ratio) + margin
Analyzing CDC in Synthesis
Static CDC Analysis
Modern EDA tools perform static CDC analysis to identify potential violations:
Common CDC Violations Detected:
- Missing Synchronizers: Direct use of async signals without synchronization
- Reconvergence: Multiple synchronized versions of same signal causing glitches
- Data/Control Correlation: Data and control signals from different domains
- Insufficient Synchronization: Single FF used where two needed
CDC Constraints for Synthesis
Inform synthesis tools about CDC structures:
# Mark synchronizer registers
set_false_path -through [get_pins sync_ff1/D]
# Define clock domain crossings
set_clock_groups -asynchronous \
-group [get_clocks clk_domain_a] \
-group [get_clocks clk_domain_b]
# Set max delay for CDC paths (for tool analysis)
set_max_delay 5.0 -from [get_clocks clk_src] \
-to [get_clocks clk_dest] \
-through [get_pins sync_ff1/D]
Verification Strategies
Simulation: CDC issues rarely appear in simulation because:
- Metastability resolves randomly
- RTL simulations use ideal timing
- Problems may require millions of cycles to manifest
Formal Verification: Use formal CDC verification tools that exhaustively check CDC structures against known-safe patterns.
Silicon Testing: Include PRBS (Pseudo-Random Bit Sequence) generators and checkers across CDC boundaries for production testing.
CDC Best Practices Summary
- Use proven synchronizer structures (2-FF or 3-FF) for single-bit signals
- Never synchronize multi-bit buses directly (except Gray-coded counters)
- Protect synchronizers from synthesis optimization with appropriate attributes
- Use asynchronous FIFOs for high-throughput multi-bit CDC
- Apply proper timing constraints to CDC paths
- Run static CDC analysis tools during implementation
- Document all CDC crossings in your design specification
- Consider MTBF requirements for your application (consumer vs. safety-critical)
Conclusion
Clock Domain Crossing is a critical aspect of reliable digital design. Metastability cannot be eliminated but can be managed to acceptably low probabilities using proper synchronization techniques. Single-bit signals require multi-stage synchronizers, while multi-bit transfers need Gray coding, handshakes, or asynchronous FIFOs depending on throughput requirements.
Always treat CDC with respect—improper handling leads to intermittent failures that are extremely difficult to debug. Use static analysis tools, follow proven design patterns, and never assume a CDC is "simple enough" to skip proper synchronization. The effort invested in correct CDC design pays dividends in system reliability and debug time saved.
Key Takeaways:
- Metastability is inevitable at CDC points but can be managed probabilistically
- Two-FF synchronizers are the foundation for single-bit CDC
- Multi-bit CDC requires Gray coding, handshakes, or async FIFOs
- Static CDC analysis tools are essential for catching violations
- Proper constraints inform synthesis tools about CDC structures