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:

  1. 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.

  2. 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.

  3. Oscillation: In rare cases, internal feedback in the flip-flop may cause temporary oscillation before settling.

  4. 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:

  1. Missing Synchronizers: Direct use of async signals without synchronization
  2. Reconvergence: Multiple synchronized versions of same signal causing glitches
  3. Data/Control Correlation: Data and control signals from different domains
  4. 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

  1. Use proven synchronizer structures (2-FF or 3-FF) for single-bit signals
  2. Never synchronize multi-bit buses directly (except Gray-coded counters)
  3. Protect synchronizers from synthesis optimization with appropriate attributes
  4. Use asynchronous FIFOs for high-throughput multi-bit CDC
  5. Apply proper timing constraints to CDC paths
  6. Run static CDC analysis tools during implementation
  7. Document all CDC crossings in your design specification
  8. 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
Did you find what you were looking for?

Quiz

No quiz questions available for this article.