The short answer: CDC verification proves that every clock domain crossing in a design is safe. Teams first describe all clocks and resets, then run structural CDC tools that find every crossing and check it has a recognised synchroniser. Real issues are fixed, intentional exceptions are waived with a reason, protocols like handshakes and Gray pointers are checked with assertions or formal, and the timing tools get matching constraints. Simulation alone cannot do this, because it does not model metastability.
This post combines my older posts on CDC tools and CDC constraints, because in practice they are one job: describing the clocks correctly and then proving every crossing is handled. Here is how it works in a real project.
- The CDC sign-off flow
- Step 1: describe clocks and resets
- Step 2: structural CDC checks
- Step 3: review, fix and waive
- Step 4: functional and formal checks
- Step 5: matching STA constraints
- Common tools
Table of Contents
Part 1: The CDC sign-off flow

Part 2: Step 1: describe clocks and resets
The tool needs to know every clock, which clocks are related (for example derived by division) and which are asynchronous to each other, plus every reset and its domain. Most flows reuse the SDC timing constraints for clocks and add CDC-specific setup on top. If this step is wrong, everything after it is wrong too, so I would spend real time here.
Part 3: Step 2: structural CDC checks
Structural analysis walks the netlist or RTL and finds every path from one clock domain to another. It then checks each crossing for problems such as:
- No synchroniser at all on an asynchronous crossing.
- Combinational logic before the synchroniser, which can glitch.
- A multi-bit bus synchronised bit by bit.
- Reconvergence: separately synchronised signals combined later.
- Fan-out of an unsynchronised signal to several synchronisers.
Part 4: Step 3: review, fix and waive
Tools report many crossings, and some are intentionally safe, for example a configuration register that is written once while the destination is held in reset. Each such case should be waived with a clear written reason, ideally reviewed by a second engineer. Undocumented waivers are one of the most common causes of escaped CDC bugs.
Part 5: Step 4: functional and formal checks
- Assertions that data stays stable while a handshake is in progress.
- Assertions that Gray-coded pointers change by only one bit at a time.
- Formal checks of handshake and FIFO protocols.
- Some flows add metastability injection in simulation to check that the design tolerates a synchroniser resolving a cycle late.
Part 6: Step 5: matching STA constraints
Timing tools must not try to time asynchronous paths as if they were synchronous. Typical constraints are:
# Declare clocks that have no phase relationship
set_clock_groups -asynchronous -group {clk_a} -group {clk_b}
# For Gray-coded FIFO pointers, still bound the skew between bits
set_max_delay 2.0 -datapath_only \
-from [get_cells wptr_gray_reg*] -to [get_cells rq1_wgray_reg*]
The second constraint matters: declaring the clocks asynchronous removes the timing check entirely, but Gray-code pointers only work if all their bits arrive within about one destination clock period. Command details vary slightly between tools.
Part 7: Common tools
The main commercial CDC and RDC tools are Siemens Questa CDC, Synopsys VC SpyGlass CDC, and Cadence’s CDC solutions within its Conformal and Jasper families. Most companies use one of these as the sign-off check.
Practise this on VLSI Forge
I built VLSI Forge so you can write RTL in your browser, run it on a real simulator and check every signal in the waveform. Free, nothing to install.
FAQ
Why can’t simulation find CDC bugs?
Normal RTL simulation does not model metastability or the random extra cycle a synchroniser may add, so a design with a missing synchroniser usually still passes.
What is a CDC waiver?
A documented decision that a reported crossing is safe, with the reason written down. Every waiver should be reviewed.
Should I use set_false_path or set_clock_groups for async clocks?
Either can remove timing between unrelated clocks; set_clock_groups -asynchronous is clearer for whole clock groups. Remember to add max-delay constraints where bits must arrive together, such as Gray pointers.
When should CDC checks run?
Early and often: as soon as RTL is stable enough, and again whenever clocks or major blocks change. Finding CDC issues late is expensive.
