Clock Domain Crossing: The Hidden Challenge in Multi-Clock Designs
Clock Domain Crossing (CDC) verification is critical for any design with multiple clock domains. Signals crossing between asynchronous clocks risk metastability, data corruption, and functional failures that may only appear intermittently—making them extremely difficult to debug in silicon.
Why CDC Verification Matters
- Metastability: Registers can enter undefined states
- Data Loss: Fast-to-slow transitions may lose samples
- Data Corruption: Multi-bit buses can become incoherent
- Silicon Failures: Issues may appear only under specific conditions
- Simulation Blind Spots: RTL simulation cannot catch CDC bugs
Synchronizer Types
1. Two Flip-Flop Synchronizer
Basic synchronizer for single-bit signals:
// Two-FF synchronizer module sync_2ff ( input clk_dest, input rst_n, input async_in, output sync_out ); reg [1:0] sync_reg; always_ff @(posedge clk_dest or negedge rst_n) if (!rst_n) sync_reg <= 2'b0; else sync_reg <= {sync_reg[0], async_in}; assign sync_out = sync_reg[1]; endmodule
2. Pulse Synchronizer
For single-cycle pulse signals:
// Pulse synchronizer (toggle-based) module pulse_sync ( input clk_src, clk_dest, input rst_n, input pulse_in, output pulse_out ); reg toggle_src; reg [2:0] sync_reg; // Source domain: toggle on pulse always_ff @(posedge clk_src or negedge rst_n) if (!rst_n) toggle_src <= 1'b0; else if (pulse_in) toggle_src <= ~toggle_src; // Destination domain: sync and edge detect always_ff @(posedge clk_dest or negedge rst_n) if (!rst_n) sync_reg <= 3'b0; else sync_reg <= {sync_reg[1:0], toggle_src}; assign pulse_out = sync_reg[2] ^ sync_reg[1]; endmodule
3. Gray Code Synchronizer
For multi-bit counters (FIFO pointers):
- Only one bit changes per transition
- Safe to synchronize multi-bit value
- Used for async FIFO full/empty detection
4. Handshake Synchronizer
For multi-bit data with ready/valid:
- Data held stable while handshake completes
- Request/acknowledge protocol spans domains
- Lower throughput but safe for any data width
5. Async FIFO
Best for streaming data between domains:
- Gray-coded pointers for full/empty
- Can buffer speed differences
- Highest throughput solution
Common CDC Issues
1. Missing Synchronizer
Problem
// WRONG: Direct connection across domains always_ff @(posedge clk_b) reg_b <= signal_from_clk_a; // Metastability risk!
2. Multi-Bit CDC Without Encoding
Problem
// WRONG: Binary counter synchronized directly reg [3:0] counter_a; // In clk_a domain reg [3:0] sync_counter; // In clk_b domain // If counter changes 0111 → 1000, any bit combination possible! sync_counter <= counter_a; // Data coherency risk!
3. Reconvergent CDC Paths
When synchronized signal feeds multiple destinations:
- Different paths may see different values
- Creates inconsistent state
- Must ensure single synchronization point
4. CDC Reset Issues
Reset assertion/deassertion across domains:
- Async assert, sync deassert pattern
- Reset synchronizers needed
- Reset order dependencies
CDC Verification Flow
Static CDC Analysis
Automated structural checks:
CDC Verification Flow:
┌──────────────────┐
│ RTL Design │
└────────┬─────────┘
│
▼
┌──────────────────┐
│ Clock Domain │ ← Define clock relationships
│ Specification │
└────────┬─────────┘
│
▼
┌──────────────────┐
│ Static CDC │ ← Identify all crossings
│ Analysis │ ← Check synchronizer presence
└────────┬─────────┘ ← Verify synchronizer type
│
▼
┌──────────────────┐
│ CDC Protocol │ ← Formal verification of
│ Verification │ handshake protocols
└────────┬─────────┘
│
▼
┌──────────────────┐
│ Metastability │ ← Simulation with random
│ Injection │ metastability injection
└──────────────────┘
CDC Assertions
CDC Protocol Assertions
// Data stability during handshake property p_data_stable_during_handshake; @(posedge clk_src) (req && !ack) |-> $stable(data); endproperty // Gray code single-bit change property p_gray_single_bit; @(posedge clk) $onehot(gray_ptr ^ $past(gray_ptr)) || (gray_ptr == $past(gray_ptr)); endproperty // Pulse width sufficient for sampling property p_pulse_width; @(posedge clk_src) $rose(signal) |-> signal[*2]; // At least 2 cycles endproperty
CDC Verification Tools
Commercial Tools
- Synopsys SpyGlass CDC: Industry-leading static CDC
- Cadence Conformal CDC: Comprehensive CDC solution
- Siemens Questa CDC: Integrated with simulation
- Real Intent Meridian CDC: Advanced protocol verification
Tool Capabilities
| Check Type | Description |
|---|---|
| Synchronizer Detection | Identify proper synchronizer structures |
| Reconvergence | Find paths that diverge and reconverge |
| Multi-bit CDC | Detect unsafe multi-bit crossings |
| Reset CDC | Verify reset synchronization |
| Protocol Verification | Formal proof of handshake protocols |
CDC Design Best Practices
Design Guidelines
- Minimize clock domain crossings
- Use proven synchronizer cells from library
- Document all intentional CDC paths
- Keep synchronized signals simple (single bits when possible)
- Use async FIFOs for streaming data
Verification Guidelines
- Run CDC analysis early and continuously
- Define clock relationships explicitly
- Review all CDC warnings, not just errors
- Use formal verification for protocol correctness
- Perform metastability injection testing
Conclusion
Clock domain crossing issues are among the most difficult bugs to find and fix in silicon. Proper synchronization structures, combined with rigorous static and formal CDC verification, are essential for reliable multi-clock designs. Don't rely on simulation alone—CDC bugs often escape to silicon.
Vcores designs all multi-clock IP with CDC-clean architectures and provides comprehensive CDC verification as part of our deliverables. Our synchronizer libraries and CDC assertions are proven across multiple silicon implementations.