In short: Robust UVM reset handling means designing drivers, monitors and sequencers that detect reset assertion, abort or flush in-flight transactions, resynchronize cleanly on de-assertion, and are exercised by on-the-fly reset sequences coordinated through phase jumps or a dedicated reset-handling thread.
Ask a verification engineer what breaks first on a new SoC and the honest answer is usually "the reset." A well-written UVM environment can pass thousands of random transactions and still fall apart the moment a watchdog or power-management block asserts an asynchronous reset in the middle of a burst. Reset handling is one of the most common interview topics for a reason: it exposes whether you understand UVM's object lifetime, phasing, and the boundary between the testbench and the DUT. This article walks through reset-aware components, phase jumps, and on-the-fly reset testing with concrete patterns you can drop into a real environment.
Why reset is hard in a class-based testbench
The RTL handles reset in one line: always @(posedge clk or negedge rst_n). The testbench has no such luxury. UVM components are long-lived objects; a driver's run_phase is a forever loop that pulls items from the sequencer and wiggles pins. When reset fires asynchronously, three things happen at once and all of them are your problem:
- The driver may be mid-handshake — it has driven
valid=1and is waiting forreadythat will now never come. - The monitor is halfway through reconstructing a transaction and is about to emit a corrupt object to the scoreboard.
- The sequencer still has a queue of items and a sequence blocked inside
finish_item().
If you ignore any of these, you get phantom protocol errors, scoreboard mismatches on transactions that never really completed, and hung simulations where a sequence waits forever on an item that reset silently killed.
A useful mental model: the DUT forgets everything on reset in zero time, but your testbench objects remember. Reset handling is the discipline of forcing the testbench to forget in lock-step with the hardware.
Reset-aware drivers: fork, join_any, disable fork
The classic pattern makes the driver run two threads concurrently — one that drives transactions, one that watches for reset — and lets reset win the race. The fork...join_any plus disable fork idiom is the idiomatic UVM way to do this:
task run_phase(uvm_phase phase);
forever begin
@(negedge vif.rst_n); // wait until out of reset region
fork
get_and_drive(); // normal stimulus loop
@(negedge vif.rst_n) ; // reset watchdog
join_any
disable fork; // kill whichever thread lost
reset_signals(); // park pins to safe default
end
endtask
The key ideas: join_any returns the instant either thread finishes, reset asserts and the disable fork terminates the half-driven get_and_drive() thread, and reset_signals() drives all outputs back to their idle values (for example valid=0, wdata='x) so the DUT sees a clean interface. Without the parking step, a stale valid can get sampled on the first post-reset clock and inject a ghost transaction.
join_any + disable fork let reset abort an in-flight item, then pins are parked.Reset-aware monitors and sequences
Monitors need the same reset guard. A monitor should discard any partially-sampled transaction on reset rather than send it onward — otherwise your scoreboard sees a truncated packet and flags a false mismatch. Wrap the collection loop with the identical fork/join_any structure and, on reset, simply continue without calling analysis_port.write().
Sequences deserve attention too. A sequence blocked in finish_item() when reset hits will hang unless the sequencer's items are flushed. Modern UVM gives you uvm_sequencer::stop_sequences() to clear the queue, but the cleaner approach is a top-level reset sequence that calls it, or a virtual sequence that uses p_sequencer to coordinate a graceful drain.
Rule of thumb for on-the-fly reset: the number of transactions the scoreboard expects to complete must exactly match what survived reset. Count aborted items on both the driver and monitor side, or your end-of-test check will be off by the in-flight depth.
Phase jumping: resetting the testbench, not just the pins
Sometimes you want the whole environment to re-run its configuration — re-randomize registers, re-establish a link-training handshake — after a hard reset. That is what UVM phase jumps are for. From inside a phase you can call:
phase.jump(uvm_pre_reset_phase::get());
This rewinds the run-time phase schedule back to pre_reset, so the ordered reset / configure / main / shutdown phases execute again. Phase jumps are powerful but blunt: every component's runtime phase is affected, and objections must be managed carefully or the jump will not take. Use them for full environment restarts (think a DUT that re-enumerates on USB bus reset), and use the local driver/monitor fork pattern for routine mid-test resets. The two techniques solve different granularities of the same problem — a distinction worth rehearsing before an interview and covered in depth in our UVM phases explained guide.
| Technique | Scope | Use when |
|---|---|---|
| fork/join_any + disable fork | Single component (driver/monitor) | Routine mid-burst reset; stimulus resumes |
| Reset sequence + stop_sequences() | Sequencer + sequences | Flush queued items cleanly |
| phase.jump(pre_reset) | Whole run-time phase schedule | Full re-configuration / re-enumeration |
| Separate reset agent | Dedicated reset driver | Programmable reset timing / randomized pulses |
pre_reset, so the ordered phases execute again.On-the-fly reset testing as a live signal
The payoff of all this plumbing is the on-the-fly reset test: a background sequence that asserts reset at random, legal points during active traffic and verifies the DUT and testbench both recover. The animation below shows the essence — transactions streaming across the bus until a reset pulse arrives and flushes the pipeline back to idle.
Simulators treat this consistently across the big three — Synopsys VCS, Cadence Xcelium and Siemens Questa all support UVM 1.2/IEEE 1800.2 phasing and the disable fork semantics shown here — but watch for subtle differences in how each handles objection draining during a phase jump. If you are building these flows as part of structured training, our partners at ChipXpert VLSI training run hands-on reset-testing labs on exactly these three tools.
Common pitfalls
- Forgetting to park pins. A stale
validorreqsampled on the first post-reset clock creates a ghost transaction. - Driving during reset. Some protocols (AXI, APB) require master outputs to be stable/idle while reset is low; driving live data violates the spec and can mask real bugs.
- Scoreboard double-counting. If the monitor emits a partial item that reset also aborts in the DUT, expected and actual counts diverge.
- Objection deadlock on phase jump. Raised objections not dropped before
jump()stall the schedule.
Want to build these components from scratch? CourseTron's Advanced UVM course has a full module on reset-aware agents, phase jumps and on-the-fly reset regressions, with graded labs on VCS and Questa. Explore it on our courses page.
Frequently asked questions
What is the difference between a reset-aware driver and a reset agent?
A reset-aware driver guards its own stimulus loop against reset using fork/join_any so it can abort in-flight items. A reset agent is a separate UVM agent whose only job is to drive the reset signal itself, often with randomized pulse timing, so you can program when reset happens. Large environments use both.
When should I use phase jumping instead of a local reset guard?
Use a local fork/disable-fork guard for routine mid-test resets where stimulus simply resumes. Use phase.jump(pre_reset) when the whole environment must re-run its configuration — register programming, link training, or device re-enumeration — after a hard reset.
How do I stop sequences hanging when reset hits?
Call stop_sequences() on the affected sequencer (typically from a coordinating reset sequence or virtual sequence) to flush queued items, and make sure any sequence blocked in finish_item() has a reset-handling branch so it does not wait forever on an item reset has killed.
Why does my scoreboard mismatch only on reset tests?
Almost always an in-flight accounting error: the monitor emitted a partially-sampled transaction, or the driver's aborted item was counted as complete. Make both sides drop partial items on reset and reconcile the expected count against what actually finished.
Reset handling ties together everything UVM teaches about object lifetime and phasing. Master it and you will debug far faster across the whole environment — and if your project needs a verified reset methodology built for you, Avecas design and verification services delivers reset-robust UVM environments for production silicon. For a deeper dive into the adjacent topic, read our UVM virtual sequencer guide.