DigiFlop

HomeDebug Guides › CDC Debug Guide
RTL Debug · CDC Verification
Debugging Clock Domain Crossing Violations, Not Just Reading Them
A violation report is not a bug list — telling the two apart is most of the job.
Most RTL bugs get caught in a waveform. Most CDC bugs don't — they're caught (or missed) by a dedicated static/formal CDC verification tool run over the whole design, and its report reads nothing like a simulation failure. This guide walks through what that report is actually telling you: which categories point at a real structural bug in your RTL, which point at a broken or missing setup assumption in the CDC run itself, and how reset domain crossing fits in as a related but distinct check most teams under-invest in. No specific verification tool is assumed — report formats and rule names vary, the underlying failure mechanisms don't.

Quick Cheat Sheet

What a CDC report line usually means, sorted by how often it actually shows up on a real signoff run. Skim this before triaging a fresh report.

CategoryWhat the report showsWhere to look
StructuralSignal crosses domains with no synchronizer flops at allThe source register's fanout — was it wired straight into destination-domain logic instead of a synchronizer?
StructuralCombinational logic reported between synchronizer stagesAny gate or mux inserted between the first and second synchronizer flop — remove it, route register-to-register only
StructuralTwo "safe" synchronized signals still cause a functional bug downstreamReconvergence — both signals traced back to one source, recombined in a gate after synchronizing independently
StructuralA narrow pulse from a fast domain occasionally disappearsFast-to-slow crossing with a plain flop instead of a pulse synchronizer or toggle/ack scheme
StructuralMulti-bit bus flagged as a single unsynchronized crossingA bus double-flopped as a group instead of gray-coded or handshaked — check whether more than one bit can change per transfer
Structural (RDC)Async reset release flagged as unsynchronizedReset de-assertion edge, not assertion — needs a reset synchronizer so release lines up with a clock edge
Structural (RDC)Reset domain crossing on a signal you didn't know was a resetA soft/internally-generated reset (register or IP output used to reset a sub-block) — these don't look like a reset tree structurally
MethodologyReport is dominated by reset, config, or scan signalsSignal should likely be declared quasi-static / a functional false path, not left as a live async crossing
MethodologyA crossing you know can't happen is still flaggedStructural analysis can't see arbitration/mux exclusivity — needs an explicit waiver or "through"-point constraint, not a code fix
MethodologyHuge volume of noise from one third-party blockMissing or overly pessimistic black-box timing/clock model at that block's boundary
MethodologyBlock-level run was clean, integration-level run isn'tBlock and top-level clock-grouping assumptions disagree — re-check which clocks were declared asynchronous at each level
AdvancedNew crossings appear only after power intent is addedIsolation/retention cell control pins and DVFS domains create new async relationships — re-run CDC with power intent included, not gate-level only

Two Very Different Kinds of "CDC Violation"

The single biggest source of wasted debug time on a CDC report is treating every line the same way. A CDC signoff run produces two fundamentally different classes of finding, and they need opposite responses.

Structural violations describe an actual defect in the RTL — a missing synchronizer, a glitch path, a reconvergence hazard. These get fixed by changing the design.

Methodology findings describe a problem with how the tool was set up to analyze the design — a clock pair that should have been declared asynchronous and wasn't (or vice versa), a black-boxed block with no timing model, a functionally-impossible path the tool can't see is impossible. These get fixed by changing a constraint, a waiver, or a black-box model — not the RTL.

A high violation count usually means a setup problem, not a hundred real bugs. A first CDC run on a fresh block routinely reports far more crossings than are real, structural hazards — most of it is noise from unclassified reset/config signals and unmodeled exclusivity. Separating structural findings from methodology noise before touching any RTL is the highest-leverage first step on any report.


Structural Violations: What the Report Is Telling You About Your RTL

SOURCE DOMAIN src_ff DESTINATION DOMAIN sync_a FF2 sync_b FF2 combine each path safe alone — may arrive 1 cycle apart

These are the findings that describe an actual defect the tool found in your RTL. Each one has a fixed, well-known cure — the work is recognizing which pattern you're looking at.

Violation typeWhat it meansFix
Missing synchronizerA signal crosses into a different, unrelated clock domain and lands directly on a destination-domain register or logic with no synchronizing flops in betweenAdd a 2-flop (or 3-flop) synchronizer for a single control bit; gray-code + handshake for multi-bit data
Combinational logic in the synchronizer pathA gate or mux sits between the first and second synchronizer stage, so a glitch on that logic can be sampled and latched as a false valueNothing but a direct register-to-register connection is allowed between synchronizer stages — move any logic before the first flop or after the last
ReconvergenceTwo or more signals, each individually synchronized correctly, trace back to a common source and recombine in downstream logic — since each resolved independently, they can momentarily disagree even though both look validSynchronize the combined/decided value once, not each input separately, or add explicit handshaking so the destination domain knows both arrived together
Divergent fanoutA single synchronizer flop's output fans out to multiple independent downstream loads with different logic — under a rare timing corner, different loads can effectively see different resolved valuesBuffer the synchronizer output once and fan the buffered copy out, rather than letting every load read the synchronizer flop directly
Fast-to-slow pulse lossA single-cycle pulse generated in a fast domain is narrower than the destination clock period and can be sampled as if it never happenedStretch the pulse, or replace it with a toggle-and-resynchronize scheme, or a full request/acknowledge handshake
Bus / multi-bit coherencyA multi-bit value is synchronized as a plain group of flops; if more than one bit changes at once, the destination can sample an intermediate value that was never actually writtenGray-code the value before crossing (bounds the ambiguity to one bit) or use a full handshake so the whole word is guaranteed stable when sampled
Handshake protocol violationA request/acknowledge crossing where the request can drop before the acknowledge could possibly have propagated back, or the data isn't held stable for the full handshake windowHold request (and data) until acknowledge returns; verify with an explicit assertion on pulse width and request-to-acknowledge latency, not just flop count

Reset Domain Crossing (RDC): CDC's Often-Skipped Sibling

A reset domain crossing happens when a source register's asynchronous reset differs from its destination register's reset — or the destination has no reset at all. It is not caught by standard CDC checks, which mostly look at data and clock relationships, and most teams run it as a separate check, if they run it at all.

RDC issueWhy it's dangerousFix
Unsynchronized reset releaseReset assertion is inherently safe (it forces a known state), but if de-assertion (release) happens close to a destination clock edge, it can violate recovery/removal timing on that flop just like a data setup/hold violationAssert asynchronously, release synchronously — route the reset through a small reset synchronizer at each domain boundary
Soft / internally-generated resetsA register, FSM, or IP output used to reset part of the design (common for selective block restarts) doesn't look like a reset structurally, so it's easy to miss entirely during reset-tree reviewTreat any signal driving another flop's reset pin as part of the reset tree, regardless of where it was generated, and run it through the same RDC analysis as primary resets
Dual-synchronicity reset useThe same reset signal resets one flop synchronously and a different flop asynchronously elsewhere in the design — an easy, easy-to-miss inconsistency introduced during ECOsKeep reset usage consistent per domain; if you can't, treat the crossing explicitly rather than assuming both flops behave the same way
Reset used as a data signalAn async set/reset pin gets wired to carry functional data instead of a true reset — while reset is asserted, the "data" value can't be controlled, corrupting whatever depends on itNever reuse a reset/set pin as a data input; if the behavior is intentional, model it explicitly rather than through the reset pin

Not every reset-domain difference is a real hazard. If the source flop's reset is itself generated synchronously within its own clock domain, the path can behave like an ordinary synchronous timing path rather than a metastability risk — in which case static timing analysis, not a synchronizer, may already cover it. Blanket-treating every reset-domain mismatch as equally dangerous is one of the biggest sources of RDC report noise.


Setup & Methodology Issues: When the Report Itself Is Wrong

A large share of a first CDC report is not describing bugs in your RTL at all — it's describing gaps or mistakes in how the tool was told to analyze the design. Getting these right is what turns a report with thousands of lines into one with a manageable, trustworthy list of real findings.

IssueWhat happensFix
Missing or wrong asynchronous clock-group declarationTwo clocks that are actually unrelated get treated as synchronous, so real crossings between them are silently skipped — a false negative that can hide a genuine bug all the way to siliconReview every clock pair's declared relationship explicitly; don't inherit a default assumption from an unrelated block or an earlier project
Over-broad asynchronous declarationA clock pair that's actually related (e.g. same source, integer-divided) gets marked asynchronous, flooding the report with crossings that aren't real hazardsModel true clock relationships (divided, generated, gated) accurately instead of defaulting everything unfamiliar to "asynchronous"
Un-declared quasi-static signalsConfiguration, mode, and one-time-event signals get analyzed as if they toggle every cycle, dominating the report with noise that has no real metastability riskExplicitly declare genuinely static/quasi-static signals with a constraint, and verify they really are stable for long enough before doing so
Functionally-false pathsStructural analysis reports a crossing that can never actually occur — e.g. between two peripherals on a shared bus where the arbiter guarantees only one is ever active — because the tool can't see that functional guaranteeAdd an explicit "through"-point exception or documented waiver; don't rely on the tool inferring exclusivity it has no visibility into
Incomplete black-box / third-party IP modelsA block with no accurate internal timing or clock model forces the tool into pessimistic worst-case assumptions at its boundary, generating a wall of noise that swamps real findings elsewhereProvide an accurate boundary/timing abstraction for black-boxed IP, or exclude the block's internals once it's independently signed off and focus only on its interface crossings
Stale or blanket waiversA waiver written for one version of the RTL keeps suppressing a report line after the code around it changed — silently hiding a newly introduced real bug behind an old justificationTie waivers to the specific RTL revision that justified them and re-review at every significant design change, not just at final signoff
Block-clean, integration-level violationA block signs off clean in isolation, but a real crossing only appears once it's integrated into the full chip — usually because a clock assumed synchronous at block level turns out not to be at the top levelKeep block-level and top-level clock-grouping declarations consistent, and re-verify crossings at every integration boundary, not just within each block

When Design Complexity Raises the Stakes

A few trends in current chip design make CDC verification meaningfully harder than the classic two-clock picture above, and are worth checking for explicitly rather than assuming an old flow still covers them.

TrendNew CDC exposure
Power-managed domains (isolation / retention cells)An isolation cell's enable signal drives the destination register combinationally — if that enable is asynchronous to the destination clock, its own assertion/de-assertion edge can cause a hazard, separate from the data path it's protecting. Retention cells' save/restore control pins create their own new crossings that a pre-power-aware CDC run won't see at all.
Dynamic voltage and frequency scaling (DVFS)Once a domain's voltage can change, its clock's phase relationship to nominally-related sibling clocks becomes non-deterministic — registers that share a clock name can still need full CDC treatment once one side is on a variable-voltage rail. This is easy to miss because the clock names match.
Many-clock-domain SoCs (GALS-style, AI-accelerator-class designs)Designs built from many independently-clocked tiles or cores multiply both the ordinary CDC surface and the power-domain-crossing surface at once — the volume of crossings to classify grows much faster than the design's overall complexity.
Multi-die / chiplet integrationEach die can run its own independent clock domain, and swapping or adding a die can force re-verification of synchronization across the whole assembled system, not just within one die.

The practical takeaway: run CDC (and RDC) analysis with power intent included, at RTL, rather than waiting for gate-level netlists — by the time these crossings show up in a gate-level-only flow, they're far more expensive to fix.


Triage Tactics: Working a Violation Report Without Losing a Week

  1. Split structural findings from methodology findings before touching any RTL. They need completely different responses, and mixing them together is the single biggest cause of wasted debug time on a fresh report.
  2. Start with reset-related crossings. They're usually the majority of raw report volume, and disproportionately contain both the most noise (uninteresting quasi-static resets) and the most dangerous real bugs (unsynchronized reset release) — sort them out early rather than last.
  3. Check which clock groups were auto-inferred versus user-declared before trusting the crossing count. An auto-inferred "asynchronous" relationship is a good place to look first for both false positives and missed real relationships.
  4. Sign off reusable blocks once, at the block boundary — carry forward the block's synchronizer structure and interface contract instead of re-flattening and re-classifying its internals at every level of integration.
  5. Tie every waiver to the RTL revision that justified it. A waiver that silently survives an unrelated code change is a bug hiding behind an old sign-off, not a solved problem.
  6. For power-managed or DVFS domains, don't trust the clock name alone. Check the voltage-domain relationship explicitly — same clock name across a voltage boundary is not the same guarantee it is on a single-rail design.
  7. When a reset-domain-difference finding shows up alone, check whether the source reset is itself generated synchronously in that clock domain before assuming it needs a full synchronizer fix — it may already be covered by ordinary timing analysis.

Violation category names, thresholds, and report formats vary between CDC verification tools and methodologies; the underlying failure mechanisms described here — missing or broken synchronization, reconvergence, unsynchronized reset release, and misclassified clock relationships — are common across the industry's static and formal CDC signoff practice.

Read the CDC concept guide → AXI / APB / AHB debug guide → CDC interview questions → ← All Debug Guides STA debug guide →