
Clock domain crossing checks sit on almost every sign-off list. Reset domain crossing checks often do not, even though an RDC needs no second clock at all. Two flops on one clock, each with its own asynchronous reset, joined by a wire, are enough. Assert the launching flop's reset and its output changes at a moment that has nothing to do with the clock.
This post is for RTL and verification engineers. It shows how these bugs arise, why they get past CDC, static timing and simulation, which circuit fixes work, and what to check before you sign off.
One clock, two resets
The block reset here can fire while the always-on logic keeps running.
module rdc_fail (
input logic clk,
input logic blk_rst_n, // block reset, asserts asynchronously
input logic top_rst_n, // always-on reset
input logic req_in,
input logic en,
output logic seen_q,
output logic armed_q // design rule: armed_q implies seen_q
);
logic req_q;
always_ff @(posedge clk or negedge blk_rst_n)
if (!blk_rst_n) req_q <= 1'b0;
else req_q <= req_in;
always_ff @(posedge clk or negedge top_rst_n)
if (!top_rst_n) begin
seen_q <= 1'b0;
armed_q <= 1'b0;
end else begin
seen_q <= req_q; // RDC: same clock, different reset
armed_q <= req_q & en; // RDC with a longer path
end
endmodule
If blk_rst_n falls while req_q is high, req_q drops without reference to clk. If that edge reaches a receiving flop inside its setup and hold window, and the receiver is not in reset, the receiver can go metastable. Yossi Mirsky of Intel set out this mechanism in his DVCon paper, and added a useful detail: with a reset value of 0, only a launching flop holding 1 can cause trouble.
The same paper and others describe less obvious forms:
- Receivers with no reset. A flop with no reset pin behaves like a receiver whose reset is never asserted, so it is just as exposed.
- Side inputs. Two flops can share a clock and a reset, and still have an RDC if a signal from another domain gates the receiver's reset.
- Combined resets. Where a flop's reset is a function of several resets, no single reset domain describes it, and each pairing of transmit and receive resets needs its own analysis.
- Asynchronous FIFOs used synchronously. Shi, You and Takara report a dual-port FIFO that raised nothing in CDC because it was used synchronously. RDC analysis flagged it because the two resets had no declared relationship.
- Short reset pulses. Viswanathan and colleagues show a reset delayed through registers that assumes a pulse longer than three cycles. With a two-cycle pulse, the downstream flop leaves reset and is corrupted by a flop that has not yet entered it.
Our post on metastability in flip-flops covers the underlying physics if you want a refresher.
Why it gets past the usual checks
| Check | Why it misses the bug |
|---|---|
| CDC tools | They look at crossings between asynchronous clocks. In the module above there is only one clock, so there is nothing to report. A crossing that also changes clock domain will probably be flagged, but Mirsky warns it can be legitimately waived for CDC and still be an RDC bug. For background, see this CDC verification module. |
| Static timing | The path from the reset through req_q to the receiver is an asynchronous event, which Mirsky describes as a path STA does not take into account, or cannot handle without heavy noise. |
| RTL simulation | Shi and colleagues name three conditions: the reset and clock timing must line up, metastable behaviour must be enabled, and the bad value must reach something the testbench checks. Zero-delay RTL has no path delays, so by our reading it will usually show a clean result. The problem is close to X propagation in verification, where a passing simulation says little about the real circuit. |
Mirsky's field evidence is the strongest argument for the check. In 2014, a network chip of about 2.5 million flops, with 13 clock domains and 8 reset domains, was basically clean on lint, CDC and simulation and close to tape-out. A trial RDC run then found several serious bugs that needed RTL or design fixes. A smaller project in the same division declined the trial, and an RDC bug that could easily have been detected turned up in its silicon. RDC checks then became part of that division's sign-off.
Arm and Siemens authors report one 24-core configuration with 629,628 CDC paths and 13,317,354 RDC paths, about 21 RDC paths for each CDC path. Only about 5,000 of them overlapped.
Worked example: a failing reset sequence
Every number here is an illustrative assumption, not data from any library or product.
| Assumption | Value |
|---|---|
| Clock | 500 MHz, so a period of 2,000 ps |
Reset pin to req_q output | 100 ps |
Path from req_q to seen_q D | 200 ps |
Path from req_q to armed_q D | 350 ps |
| Setup and hold at the receivers | 50 ps and 20 ps |
| State before reset | req_q, seen_q, armed_q and en all 1 |
Let t be the time the reset reaches req_q, measured from one clock edge. The change reaches seen_q at t + 300 ps and armed_q at t + 450 ps. For the next edge at 2,000 ps, the danger window runs from 1,950 ps to 2,020 ps.
| t (ps) | What happens at the 2,000 ps edge | Share of period |
|---|---|---|
| Up to 1,500 | Both capture 0, as intended | Safe |
| 1,500 to 1,570 | armed_q D changes inside its window, so it may go metastable | 3.5% |
| 1,570 to 1,650 | seen_q captures 0 and armed_q keeps 1: an illegal state, with no metastability at all | 4.0% |
| 1,650 to 1,720 | seen_q D changes inside its window, so it may go metastable | 3.5% |
| 1,720 to 2,000 | Both capture 1, and both capture 0 one cycle later | Safe |
Try t = 1,600 ps. seen_q D falls at 1,900 ps, before the 1,950 ps cut-off. armed_q D falls at 2,050 ps, after the window closes. The result is armed_q = 1 with seen_q = 0, which the RTL can never produce. An RTL assertion of that rule would pass, because the two paths are identical at the cycle level.
The danger zone is 70 + 80 + 70 = 220 ps, or 11% of the period. Now assume reset arrival is uniform across the clock period, and that req_q is high at 1% of block resets, since blocks are usually idle by then.
- One lab board, 20 resets a day: 20 × 0.01 × 0.11 = 0.022 hits a day, or one every 45 days or so.
- A fleet of 100,000 units, one reset a day each: 100,000 × 0.01 = 1,000 exposed resets a day. Of those, 4% or 40 give the illegal state, and 7% or 70 enter a window.
That is 110 hits a day, or one roughly every 13 minutes across the fleet, and almost none on the bench. This is the pattern to take from the example. If a flop launches the reset on the same clock, t is fixed at a given voltage and temperature, so by our reasoning the fault may appear only at certain corners.
For the metastable share, use the MTBF expression in Ran Ginosar's note, eS/τ / (W × FC × FD). Assume τ = 25 ps, W = 70 ps, FC = 500 MHz and settling time S = 200 ps, with one receiver. FD here is the rate of reset-induced changes: 0.01 a day is about 1.157 × 10-7 per second.
- The denominator is 70 × 10-12 × 5 × 108 × 1.157 × 10-7 = 4.05 × 10-9 per second.
- S/τ = 8, and e8 is about 2,981, so MTBF is about 7.36 × 1011 seconds, or roughly 23,300 years for one unit.
- Ginosar's note divides MTBF by the number of synchronisers. Treating each unit as one, 100,000 units gives about 85 days.
- Two flops, with S = 1,850 ps (the period less 50 ps setup and an assumed 100 ps clock to output), give e74, about 1.37 × 1032, which takes MTBF far beyond any product's life.
Our conclusion is that an MTBF figure alone can flatter an RDC. In this model the split capture happens 40 times a day across the fleet, while the unresolved metastable event happens about once in 85 days across the fleet. Divergent paths deserve as much attention as the flop's τ.
The circuit fixes
| Fix | How it works | The catch |
|---|---|---|
| Synchronous reset on the launch side | Mirsky calls this the best way to avoid the problem. Tools do not report an RDC when the source reset is synchronous. | Often not realistic, given the area, timing and power reasons that favour asynchronous resets |
| Fewer reset domains | Resets that always assert together can be declared one domain | The relationship has to be true in both directions |
| Reset ordering | The receiver is already held in reset when the launching reset asserts, so nothing can be sampled | Does not suit an always-on receiver that must keep working |
| Two-flop synchroniser | Gives the receiving flop time to settle, and gives downstream logic one clean copy | Adds latency and cells at each crossing, and needs the receive clock and reset |
| Data isolation | A qualifier blocks or holds the path before the reset asserts | Needs an early warning of the reset, and gating on every crossing |
| Clock isolation | The receiving clock stops before the reset asserts, so no edge can sample the change | The enable has to be issued ahead of the reset, and the whole receiver stops |
Here is the two-flop synchroniser. Feed both seen_q and armed_q from its output, and the split capture goes away too.
logic [1:0] req_sync_q; // receive clock, receive reset
always_ff @(posedge clk or negedge top_rst_n)
if (!top_rst_n) req_sync_q <= '0;
else req_sync_q <= {req_sync_q[0], req_q};
// use req_sync_q[1] in place of req_q
Isolation suits buses and latency-sensitive paths. In this version the receiver holds its value. Mirsky points out that many designs already make a pre-reset signal for clean shutdown, and it can be reused here.
module rdc_isolated (
input logic clk,
input logic blk_rst_n,
input logic top_rst_n,
input logic blk_rst_req, // synchronous to clk, leads blk_rst_n
input logic req_in,
output logic busy_q
);
logic req_q, qual_q;
always_ff @(posedge clk or negedge blk_rst_n)
if (!blk_rst_n) req_q <= 1'b0;
else req_q <= req_in;
// qualifier lives in the receive domain
always_ff @(posedge clk or negedge top_rst_n)
if (!top_rst_n) qual_q <= 1'b0;
else qual_q <= !blk_rst_req;
always_ff @(posedge clk or negedge top_rst_n)
if (!top_rst_n) busy_q <= 1'b0;
else if (qual_q) busy_q <= req_q; // otherwise hold
endmodule
The reset controller must raise blk_rst_req at least two rising clock edges before blk_rst_n falls, and keep it high until the block reset has been released, including after power-on. If the request comes from another clock, synchronise it first. This checker, which is not for synthesis, tests that contract:
assert property (@(posedge clk) disable iff (!top_rst_n)
$fell(blk_rst_n) |-> $past(!qual_q));
assert property (@(posedge clk) disable iff (!top_rst_n)
!blk_rst_n |-> !qual_q);
One thing is not a fix. A reset synchroniser, which asserts asynchronously and releases on the clock, deals with recovery time and with flops leaving reset in different cycles. Cummings, Mills and Golson cover it in their SNUG paper. The assertion edge still arrives asynchronously, and the RDC remains.
A practical sign-off checklist
- Reset table. List each reset with its source, type, polarity, reset value, who can assert it and when, and any early warning signal. Viswanathan and colleagues put domains, ordering and isolation in the design specification, before RTL.
- Reset network clean first. Run structural checks for glitches before crossing analysis. Shi and colleagues describe a reset decoded from FSM state bits that could pulse during a state change, found very late in their project. Drive resets from flops, not from decoded state.
- Top-level flat run. Mirsky found most issues in the connectivity between blocks, so block-level runs alone are not enough.
- All receivers in scope. Include non-resettable flops, side inputs and combined resets.
- A class for every crossing. Each path should be same domain, ordered, synchronised, isolated or reviewed and waived. Nothing stays uninspected.
- Constraints proven, not assumed. Static tools cannot confirm a reset order you declare, and some accept qualifiers as given. Shi and colleagues advise an assertion, proven formally or in simulation, for each ordering constraint.
- Order against filter. Mirsky shows that order constraints are transitive and can hide real paths, for example when a middle power domain is off. Use a path filter when you mean only that pair.
- Waiver discipline. Quigley and colleagues describe teams avoiding wildcards, tying waivers to tickets, and failing continuous integration on any uninspected path.
- Every configuration. For configurable IP, a block is not clean until its higher-level instances are. The Arm design ranged from 12 to 63 asynchronous reset domains at top level.
- Reset pulse width. Confirm each pulse is longer than any register-delayed reset structure assumes.
- Repeat on each RTL release. Track results against the test plan, so closure is a number and not an opinion.
Our SoC lifecycle on The Semiconductor Hub shows where verification sign-off falls in a programme.
IP hand-off now has a standard
Much of the risk sits at integration, when an IP verified with one tool lands in an SoC checked with another. Accellera's Standard for IP Abstraction for Clock and Reset Domain Crossing Integration 1.0, announced on 2 March 2026, addresses this. Its RDC clause adds a reset_group domain, where resets in one group have no RDC violations between them, and a reset_assertion_sequence domain for required order. Port attributes such as -rdc_control and -blocking_polarity describe isolation. The clause is mandatory for tools that support the standard's RDC features, and its stated aim is integration-level checking without reading the IP's RTL.
Ask your vendor which tools have implemented it.
Sources
- Yossi Mirsky (Intel), "Comprehensive and Automated Static Tool Based Strategies for the Detection and Resolution of Reset Domain Crossings", DVCon US. The proceedings archive lists 2017, while later papers cite it as 2016.
- Quigley, Niven (Arm) and Handover (Siemens), "Resetting RDC Expectations: A Systematic Approach to Verifying Complex Configurable Designs", DVCon proceedings, 2021. Co-authored by a tool vendor.
- Shi (Big Fish Semiconductor), You and Takara (Mentor), "Build Reliable and Efficient Reset Networks with a Comprehensive Reset Domain Crossing Verification Solution", DVCon proceedings, 2020. Co-authored by a tool vendor.
- Viswanathan, Takara, Kwok and Ahmed (Mentor), "A Specification-Driven Methodology for the Design and Verification of Reset Domain Crossing Logic", DVCon US, 2018. Vendor material.
- Cummings, Mills and Golson, "Asynchronous & Synchronous Reset Design Techniques - Part Deux", SNUG Boston, 2003.
- Accellera, "Standard for IP Abstraction for Clock and Reset Domain Crossing Integration", Version 1.0, March 2026, and press release, 2 March 2026.
- Ran Ginosar (Technion), "MTBF of a Multi-Synchronizer System on Chip", undated technical note.
The worked example uses illustrative numbers. The code was written for this post as teaching sketches, so check it against your own library and flow before reuse.


Your comments will be moderated before it appears here.