UVM Phases in Order: build, connect, run and Objections (With Real Logs)
In short: UVM runs every component through the same phases, in a fixed order: build, connect, end_of_elaboration, start_of_simulation, run, extract, check, report, final. build_phase (and final_phase) go top-down, parent before child, because a parent creates its children. All other function phases go bottom-up. run_phase is the only common phase that takes time, and it runs in all components at once, alongside 12 optional run-time sub-phases. It ends when no objections are raised, so a test that never raises one ends at time 0.
Every UVM testbench does the same thing in the same order.
Once you see that order, “where does this code go?” answers itself.
Every log on this page is real: Synopsys VCS X-2025.06-SP1 with UVM 1.2.
Think of it like putting on a theatre show
- build: the director hires stage managers, and each stage manager hires their own crew. Top-down: you can’t hire a crew before their boss exists.
- connect: the crew run cables and headsets between stations. Everyone already exists, so order doesn’t matter much.
- end_of_elaboration / start_of_simulation: the final walk-through, then “places, everyone”.
- run: the show. Everyone works at the same time: lights, sound, actors.
- objections: every actor with a scene still to play keeps a hand up. The curtain falls only when every hand is down.
- drain time: the applause after the last line, before the lights come up.
- extract, check, report: count the tickets, check the props, write the review.
- final: lock up the theatre.
The phases at a glance
| Phase | Takes time? | Order | Put here | Theatre picture |
|---|---|---|---|---|
build_phase | No (function) | Top-down | type_id::create, config_db::get | Hire the crew |
connect_phase | No | Bottom-up | Port and export connections | Run the cables |
end_of_elaboration_phase | No | Bottom-up | Print the topology, last tweaks | Final walk-through |
start_of_simulation_phase | No | Bottom-up | Banners, set verbosity, catchers | “Places, everyone” |
run_phase | Yes (task) | All in parallel | Drive, monitor, start sequences | The show |
extract_phase | No | Bottom-up | Pull final counts from scoreboards | Count the tickets |
check_phase | No | Bottom-up | End-of-test checks (queues empty?) | Check the props |
report_phase | No | Bottom-up | Print pass/fail and summaries | Write the review |
final_phase | No | Top-down | Close files | Lock up |
run_phase also has 12 run-time sub-phases that run alongside it: pre_reset, reset, post_reset, pre_configure, configure, post_configure, pre_main, main, post_main, pre_shutdown, shutdown, post_shutdown. They get their own full section below, with what each one is for, the rules they follow, 4 real runs, and when (and when not) to use them.
Example 1: watch the order happen
One component class prints its full name in every phase. The test builds env, and env builds env.drv.
// Every component prints its full name in every phase
class noisy extends uvm_component;
`uvm_component_utils(noisy)
noisy kid;
bit make_kid;
function new(string name, uvm_component parent); super.new(name, parent); endfunction
function void say(string ph); $display("%4t %-14s %s", $time, ph, get_full_name()); endfunction
function void build_phase(uvm_phase phase);
say("build");
if (make_kid) kid = noisy::type_id::create("drv", this); // child created here
endfunction
function void connect_phase(uvm_phase phase); say("connect"); endfunction
function void end_of_elaboration_phase(uvm_phase phase); say("end_of_elab"); endfunction
function void start_of_simulation_phase(uvm_phase phase); say("start_of_sim"); endfunction
task run_phase(uvm_phase phase); say("run"); endtask
task main_phase(uvm_phase phase); say("main"); endtask
function void extract_phase(uvm_phase phase); say("extract"); endfunction
function void check_phase(uvm_phase phase); say("check"); endfunction
function void report_phase(uvm_phase phase); say("report"); endfunction
function void final_phase(uvm_phase phase); say("final"); endfunction
endclass
The test’s run_phase raises an objection, waits 10 ns, and drops it.
Run 1: ./simv +UVM_TESTNAME=order_test
UVM_INFO @ 0: reporter [RNTST] Running test order_test...
0 build uvm_test_top.env
0 build uvm_test_top.env.drv
0 connect uvm_test_top.env.drv
0 connect uvm_test_top.env
0 end_of_elab uvm_test_top.env.drv
0 end_of_elab uvm_test_top.env
0 start_of_sim uvm_test_top.env.drv
0 start_of_sim uvm_test_top.env
0 run uvm_test_top.env.drv
0 run uvm_test_top.env
0 main uvm_test_top.env.drv
0 main uvm_test_top.env
10 test drops its objection
UVM_INFO .../uvm_objection.svh(1276) @ 10: reporter [TEST_DONE] 'run' phase is ready to proceed to the 'extract' phase
10 extract uvm_test_top.env.drv
10 extract uvm_test_top.env
10 check uvm_test_top.env.drv
10 check uvm_test_top.env
10 report uvm_test_top.env.drv
10 report uvm_test_top.env
10 final uvm_test_top.env
10 final uvm_test_top.env.drv
What the log shows (UVM library paths shortened to ...):
- build:
envfirst, thenenv.drv. Top-down:drvdoesn’t exist untilenv‘s build creates it. - connect, end_of_elab, start_of_sim:
env.drvfirst, thenenv. Bottom-up: children finish first. - run and main: both start at time 0, in both components. They run side by side, not one after the other.
- At 10 ns the test drops its objection, UVM prints
[TEST_DONE] 'run' phase is ready to proceed, and extract, check and report all happen at 10 ns, bottom-up again. - final:
envfirst, thenenv.drv. Top-down, the only other phase that walks that way.

Objections: how run_phase knows when to stop
run_phase has no natural end. A driver loops forever; a monitor watches forever.
So UVM counts raised hands. Each raise_objection puts a hand up; each drop_objection puts one down. When the count reaches zero, run_phase ends for every component, and UVM moves on to extract.
Example 2: forget the objection
// Test 2: forget the objection
class no_objection_test extends uvm_test;
`uvm_component_utils(no_objection_test)
function new(string name, uvm_component parent); super.new(name, parent); endfunction
task run_phase(uvm_phase phase);
$display("%4t run_phase started, sending 3 packets", $time);
repeat (3) #10 $display("%4t packet sent", $time);
endtask
function void report_phase(uvm_phase phase);
$display("%4t report_phase", $time);
endfunction
endclass
Run 2: ./simv +UVM_TESTNAME=no_objection_test
UVM_INFO @ 0: reporter [RNTST] Running test no_objection_test...
0 run_phase started, sending 3 packets
0 report_phase
run_phase started at time 0, then not one packet, and report_phase at time 0. The whole test lasted 0 ns. Nobody raised a hand, so the curtain fell the moment the show started. No error, no warning: the report summary shows a clean run. That’s what makes this mistake so easy to miss.
Example 3: drain time
// Test 3: drain time keeps the phase open after the last drop
class drain_test extends uvm_test;
`uvm_component_utils(drain_test)
function new(string name, uvm_component parent); super.new(name, parent); endfunction
task run_phase(uvm_phase phase);
uvm_objection obj = phase.get_objection();
obj.set_drain_time(this, 25); // stay open 25 ns after the last drop
phase.raise_objection(this);
repeat (3) #10 $display("%4t packet sent", $time);
phase.drop_objection(this);
$display("%4t objection dropped", $time);
endtask
function void report_phase(uvm_phase phase);
$display("%4t report_phase", $time);
endfunction
endclass
Run 3: ./simv +UVM_TESTNAME=drain_test
UVM_INFO @ 0: reporter [RNTST] Running test drain_test...
10 packet sent
20 packet sent
30 packet sent
30 objection dropped
UVM_INFO .../uvm_objection.svh(1276) @ 55: reporter [TEST_DONE] 'run' phase is ready to proceed to the 'extract' phase
55 report_phase
Packets at 10, 20 and 30, the drop at 30, and report_phase at 55, not 30: exactly 25 ns of drain. Drain time is the applause: it gives the last response time to come back through the monitor and reach the scoreboard.

Run 4, see every hand go up and down: +UVM_OBJECTION_TRACE
UVM_INFO @ 0: run [OBJTN_TRC] Object uvm_test_top raised 1 objection(s): count=1 total=1
UVM_INFO @ 0: run [OBJTN_TRC] Object uvm_top added 1 objection(s) to its total (raised from source object uvm_test_top): count=0 total=1
10 packet sent
20 packet sent
30 packet sent
UVM_INFO @ 30: run [OBJTN_TRC] Object uvm_test_top dropped 1 objection(s): count=0 total=0
30 objection dropped
UVM_INFO @ 55: run [OBJTN_TRC] Object uvm_test_top all_dropped 1 objection(s): count=0 total=0
UVM_INFO @ 55: run [OBJTN_TRC] Object uvm_top subtracted 1 objection(s) from its total (dropped from source object uvm_test_top): count=0 total=0
UVM_INFO @ 55: run [OBJTN_TRC] Object uvm_top subtracted 1 objection(s) from its total (all_dropped from source object uvm_test_top): count=0 total=0
UVM_INFO .../uvm_objection.svh(1276) @ 55: reporter [TEST_DONE] 'run' phase is ready to proceed to the 'extract' phase
55 report_phase
How to read it:
- At 0,
uvm_test_topraises one objection, and it passes up touvm_top: the total there goes to 1. - At 30, the test drops its objection. Its own count is 0, but **
uvm_topdoesn’t let go yet**: that’s the drain clock running. - At 55,
all_droppedfires anduvm_top‘s total reaches 0. Only then does run end.
When a test hangs or ends too early, this switch is the first thing to try. Look for the component whose raise has no matching drop (a hang), or a run with no raise at all (time 0).
The 12 run-time sub-phases
run_phase is one long task from start to finish. The UVM library also gives you a second, finer schedule that runs at the same time: 12 task phases in four groups of three.
pre_resetstarts at the same moment asrun_phase.post_shutdownends together withrun_phase.- In between they go strictly in order, one after another.
Think of it like the stage manager’s running order
In the theatre picture, run_phase is the stage manager’s headset: on from the moment the house opens until the last person leaves.
The sub-phases are the running order on the stage manager’s clipboard:
| Group | Sub-phase | What UVM says it’s for | Done when | Theatre picture |
|---|---|---|---|---|
| Reset | pre_reset | Before reset is asserted: wait for power-good, set clocks and outputs to safe values | Ready to assert reset | House lights dim |
| Reset | reset | Reset asserted: drive idle values, start clocks, then de-assert reset | Reset released, clock stable | Everyone to opening marks |
| Reset | post_reset | Just after reset: idle traffic, link training | DUT in a known, active state | Curtain up |
| Configure | pre_configure | Decide the DUT configuration | Configuration defined | Read the set list |
| Configure | configure | Program the DUT: register writes, memory loads | DUT configured | Dress the stage for act 1 |
| Configure | post_configure | Wait for it to settle, enable the DUT, sample config coverage | DUT fully enabled | Lighting check |
| Main | pre_main | Wait for training or rate negotiation | Everyone ready for traffic | “Standby” |
| Main | main | The primary stimulus: your data sequences | Test goal reached | The performance |
| Main | post_main | Included for symmetry; rarely used | n/a | Final bow |
| Shutdown | pre_shutdown | Included for symmetry; rarely used | n/a | Curtain closes |
| Shutdown | shutdown | Let data drain out of the DUT | Interfaces idle | Audience leaves |
| Shutdown | post_shutdown | Last run-time checks that need the DUT: read counters, dump memories | Ready to end | Stage manager’s final checks |
The “what it’s for” and “done when” columns are paraphrased from the UVM 1.2 class reference for the run-time phases.
Five rules the sub-phases follow
- Each sub-phase has its own objections. It ends when its own hands go down. A sub-phase where nobody raises a hand ends at once.
- Everyone moves together. No component starts
configureuntil every component in the same domain has finishedreset. The fastest waits for the slowest. (Example 4.) - Threads die at the end of their sub-phase. Anything you fork inside
configure_phaseis killed whenconfigureends. (Example 5.) - You can jump back.
phase.jump(uvm_reset_phase::get())sends everyone back toreset: reset-on-the-fly testing. (Example 6.) - Domains break the lockstep. A component in its own
uvm_domainfollows its own running order through the 12 sub-phases. (Example 7.)
In the theatre: no department moves to the next cue until every department calls “ready” (rule 2). A crew member hired “for this scene only” leaves when the scene ends (rule 3). “Stop the show, back to opening positions!” (rule 4). The orchestra pit following its own conductor (rule 5).
The demo for examples 4 to 7
// A component that spends a set time in reset, configure, main and shutdown
class worker extends uvm_component;
`uvm_component_utils(worker)
int rst_d = 10, cfg_d = 10, main_d = 10, shut_d = 10; // ns spent in each sub-phase
function new(string name, uvm_component parent); super.new(name, parent); endfunction
function void say(string ph, string what); $display("%4t %-4s %-10s %s", $time, get_name(), ph, what); endfunction
task busy(uvm_phase phase, string ph, int d);
phase.raise_objection(this);
say(ph, "start");
#d;
say(ph, "end");
phase.drop_objection(this);
endtask
task run_phase(uvm_phase phase); say("run", "start (runs alongside all sub-phases)"); endtask
task reset_phase(uvm_phase phase); busy(phase, "reset", rst_d); endtask
task configure_phase(uvm_phase phase); busy(phase, "configure", cfg_d); endtask
task main_phase(uvm_phase phase); busy(phase, "main", main_d); endtask
task shutdown_phase(uvm_phase phase); busy(phase, "shutdown", shut_d); endtask
function void extract_phase(uvm_phase phase); say("extract", ""); endfunction
endclass
Example 4: the fastest waits for the slowest
sync_test builds two workers. drv spends 20 ns in reset; mon spends 5 ns.
Run 5: ./simv +UVM_TESTNAME=sync_test
UVM_INFO @ 0: reporter [RNTST] Running test sync_test...
0 drv run start (runs alongside all sub-phases)
0 mon run start (runs alongside all sub-phases)
0 drv reset start
0 mon reset start
5 mon reset end
20 drv reset end
20 drv configure start
20 mon configure start
30 drv configure end
30 mon configure end
30 drv main start
30 mon main start
40 drv main end
40 mon main end
40 drv shutdown start
40 mon shutdown start
50 drv shutdown end
50 mon shutdown end
50 drv extract
50 mon extract
mon finished reset at 5 ns, then sat idle for 15 ns. Its configure started at 20 ns, the same moment as drv‘s, because drv was still in reset. From then on both moved in lockstep: configure 20 to 30, main 30 to 40, shutdown 40 to 50, extract at 50. Meanwhile run_phase started at 0 for both and ran underneath the whole time. In the theatre: no department moves to the next cue until every department calls “ready”.

Example 5: a thread that dies with its sub-phase
// Test 2: a background thread started in configure_phase
class kill_test extends uvm_test;
`uvm_component_utils(kill_test)
function new(string name, uvm_component parent); super.new(name, parent); endfunction
task configure_phase(uvm_phase phase);
phase.raise_objection(this);
fork
forever #5 $display("%4t heartbeat (started in configure_phase)", $time);
join_none
#12 phase.drop_objection(this);
endtask
task main_phase(uvm_phase phase);
phase.raise_objection(this);
$display("%4t main_phase start", $time);
#20 $display("%4t main_phase end", $time);
phase.drop_objection(this);
endtask
endclass
Run 6: ./simv +UVM_TESTNAME=kill_test
UVM_INFO @ 0: reporter [RNTST] Running test kill_test...
5 heartbeat (started in configure_phase)
10 heartbeat (started in configure_phase)
12 main_phase start
32 main_phase end
Heartbeats at 5 and 10 ns. configure ended at 12 ns when the test dropped its objection, and the heartbeat never printed again: not at 15, 20, 25 or 30, all through main_phase (12 to 32 ns), even though the loop says forever. No error, no warning. UVM silently killed everything configure_phase started. That’s why a background sequence (a responder, a traffic generator that should run all test long) belongs in run_phase.

Example 6: reset in the middle of the test
// Test 3: jump back to reset once, as if the DUT was reset mid-test
class jump_test extends uvm_test;
`uvm_component_utils(jump_test)
worker dut_agent;
bit jumped;
function new(string name, uvm_component parent); super.new(name, parent); endfunction
function void build_phase(uvm_phase phase);
dut_agent = worker::type_id::create("agt", this);
endfunction
task main_phase(uvm_phase phase);
phase.raise_objection(this);
#5;
if (!jumped) begin
jumped = 1;
$display("%4t test: reset on the fly, jumping back to reset_phase", $time);
phase.jump(uvm_reset_phase::get());
end
phase.drop_objection(this);
endtask
endclass
Run 7: ./simv +UVM_TESTNAME=jump_test
UVM_INFO @ 0: reporter [RNTST] Running test jump_test...
0 agt run start (runs alongside all sub-phases)
0 agt reset start
10 agt reset end
10 agt configure start
20 agt configure end
20 agt main start
25 test: reset on the fly, jumping back to reset_phase
UVM_INFO .../uvm_phase.svh(1558) @ 25: reporter [PH_JUMP] phase main (schedule uvm_sched, domain uvm) is jumping to phase reset
UVM_WARNING @ 25: main_objection [OBJTN_CLEAR] Object 'uvm_top' cleared objection counts for main_objection
25 agt reset start
35 agt reset end
35 agt configure start
45 agt configure end
45 agt main start
55 agt main end
55 agt shutdown start
65 agt shutdown end
65 agt extract
- The first
mainstarted at 20 ns. At 25 the test jumped, andagt‘s firstmainnever printed “end”: it was cut off mid-way. - UVM announces the jump (
[PH_JUMP] phase main ... is jumping to phase reset) and warns that it cleared the main_phase objections ([OBJTN_CLEAR]). That warning is expected on every jump; it’s UVM telling you the hands that were up no longer count. - Then reset (25 to 35), configure (35 to 45) and main (45 to 55) ran a second time, followed by shutdown and extract at 65.
- In the theatre: “Stop the show, back to opening positions!”
This is the one job sub-phases do better than anything else: checking that the DUT recovers from a reset in the middle of traffic. More in reset testing using phase jumps.

Example 7: a component on its own clock (domains)
// Test 4: same as test 1, but mon lives in its own domain
class domain_test extends sync_test;
`uvm_component_utils(domain_test)
function new(string name, uvm_component parent); super.new(name, parent); endfunction
function void build_phase(uvm_phase phase);
uvm_domain mon_domain;
super.build_phase(phase);
mon_domain = new("mon_domain");
mon.set_domain(mon_domain); // mon no longer waits for drv
endfunction
endclass
Run 8: ./simv +UVM_TESTNAME=domain_test
UVM_INFO @ 0: reporter [RNTST] Running test domain_test...
0 drv run start (runs alongside all sub-phases)
0 mon run start (runs alongside all sub-phases)
0 mon reset start
0 drv reset start
5 mon reset end
5 mon configure start
15 mon configure end
15 mon main start
20 drv reset end
20 drv configure start
25 mon main end
25 mon shutdown start
30 drv configure end
30 drv main start
35 mon shutdown end
40 drv main end
40 drv shutdown start
50 drv shutdown end
50 drv extract
50 mon extract
- Same delays as example 4, but now
monstartedconfigureat 5 ns, right after its own reset, and raced ahead: main at 15, shutdown at 25, done at 35 ns, whiledrvwas only halfway. drvkept its own pace: reset to 20, configure to 30, main to 40, shutdown to 50.- extract still happened at 50 ns for both. Domains only split the 12 sub-phases; the common phases (build, connect, extract, report and so on) stay shared, so everyone waits for the slowest domain before the test ends.
- In the theatre: the orchestra pit following its own conductor, but everyone still takes the final bow together.
Real use: two parts of a chip that reset or power up independently, such as a block on its own power island.

So when should you use sub-phases?
The experts on Verification Academy are almost unanimous: default to run_phase with sequences, and reach for sub-phases only with a clear reason.
Why they warn against them:
- No shared meaning. The standard doesn’t pin down what belongs in each sub-phase, so two VIPs from two teams may use
configurefor different things, and mixing them gets hard to debug (cgales, 2016; Dave Rich, 2019). - They force themselves on you. A VIP that uses sub-phases makes every testbench it lands in use them too (chr_sue, in the same 2016 thread).
- The use model was never settled. The Mentor/Siemens UVM Cookbook has advised avoiding them since early UVM 1.x (mperyer, 2013).
- Threads die at phase ends (example 5), which surprises people with background sequences (2019).
When they earn their place:
| Situation | Use sub-phases? | Why |
|---|---|---|
| Reusable VIP or agent you’ll share | No | Stick to run_phase so it fits any testbench |
| Drivers, monitors, scoreboards | No | They must see every transaction from start to end |
| Reset in the middle of a test | Yes | phase.jump() back to reset is built for this (example 6) |
| Blocks that reset or power up independently | Yes, with domains | Each domain keeps its own running order (example 7) |
| One self-contained testbench, team agrees on the meaning of each phase | Maybe | Clear stages in the test; keep them in the test and env only |
| You just want reset, then config, then traffic | No | Three sequences in a row in run_phase do the same job |
The pattern most teams use instead:
task run_phase(uvm_phase phase);
phase.raise_objection(this);
reset_seq.start(env.agt.sqr); // what reset_phase would do
cfg_seq.start(env.agt.sqr); // what configure_phase would do
main_seq.start(env.agt.sqr); // what main_phase would do
phase.drop_objection(this);
endtask
Same order, no surprises, and drivers and monitors stay in run_phase the whole time.
Go deeper
Each part of phasing has its own page on this site:
- How phasing starts: what
run_test()does and how the phases get triggered. How UVM phasing is triggered. - Ending a test: objections in detail, and where to raise them. How to terminate a UVM test with objections.
- Objections without writing them: let a sequence raise and drop its own. Raise and drop objections automatically.
- Reset in the middle of a test: jump back to the reset phase. Reset testing using phase jumps.
Common mistakes (and the fix for each)
These come from real Verification Academy threads, retold in my own words with links.
1. A test that ends at time 0
- What happened: in 2018 an engineer started a sequence in
run_phaseand the simulation ended at time 0 (Verification Academy, 2018). - Why: no objection raised, so
run_phaseended immediately. Example 2 shows exactly this. - Fix:
phase.raise_objection(this);beforeseq.start(...),phase.drop_objection(this);after.
2. Raising objections everywhere
- What happened: in 2018 someone asked why people don’t raise objections in every phase and every class (Verification Academy, 2018), and in another thread whether every sequence needs its own (Verification Academy, 2018).
- Why: objections only matter in phases that take time. Build and connect are functions; they can’t wait. And a driver that raises inside its
foreverloop can hang the test, because the drop is never reached while it waits for the next item. - Fix: raise and drop in the test’s
run_phase, around the top-level sequence. The test knows when the test is done.
3. Expecting a “construction phase”
- What happened: in 2019 an engineer asked when constructors run compared to
build_phase(Verification Academy, 2019). - Why: there’s no construction phase. Each child is constructed inside its parent’s
build_phase, withtype_id::create. That’s why build has to go top-down. - Fix: create children in
build_phase, never in the constructor.
4. Relying on sibling build order
- What happened: in 2014 someone asked who calls
build_phaseand in what order (Verification Academy, 2014). - Why: parents build before children, but the order between siblings (two agents in one env) isn’t promised.
- Fix: never let one sibling’s build depend on another’s. Share settings through
config_dbfrom the parent.
5. Thinking final_phase is bottom-up
- What happened: in 2014 an engineer asked whether
final_phaseis top-down or bottom-up (Verification Academy, 2014). - Why:
build_phaseandfinal_phaseare top-down; every other function phase is bottom-up. Example 1’s log shows both. - Fix: put end-of-test reporting in
report_phase; keepfinal_phasefor closing files.
6. Starting a background sequence in a sub-phase
- What happened: in 2019 an engineer asked whether always-on background sequences should start in
configure_phaseinstead ofrun_phase(Verification Academy, 2019). - Why: anything started in a sub-phase is killed when that sub-phase ends (example 5). cgales called this the main reason sub-phases aren’t recommended.
- Fix: start background sequences in
run_phase, forked, so they live as long as the test.
7. Mixing main_phase in one component with run_phase in another
- What happened: in 2017 an engineer needed a monitor’s
run_phaseto wait for a driver’smain_phase(Verification Academy, 2017); another asked whethermain_phasecan replacerun_phase(Verification Academy). - Why:
main_phaseis not a synonym forrun_phase. It starts only after reset and configure finish, and ends before shutdown. As soon as anyone adds time to an earlier sub-phase, everything keyed tomain_phaseshifts. - Fix: pick one style per testbench. If you must, chr_sue’s advice: use sub-phases in all components, or in none.
FAQ
What is the order of UVM phases?
build, connect, end_of_elaboration, start_of_simulation, run, extract, check, report, final.
Which UVM phases are top-down?
build_phase and final_phase. The other function phases are bottom-up.
Which UVM phase consumes simulation time?
Only run_phase and its 12 run-time sub-phases. Everything else is a function.
Why does my UVM test end at time 0?
Nothing raised an objection in run_phase. See example 2.
What is drain time?
Extra time run_phase stays open after the last objection drops, so late responses can still be checked. See example 3.
What are the UVM run-time sub-phases?
Twelve task phases that run alongside run_phase, in order: pre_reset, reset, post_reset, pre_configure, configure, post_configure, pre_main, main, post_main, pre_shutdown, shutdown, post_shutdown.
Do I need an objection in every sub-phase?
Yes, in every sub-phase where you want time to pass. Each one ends when its own objections drop; with none raised it ends at once.
Should I use main_phase or run_phase?
run_phase, unless you have a specific reason such as reset-on-the-fly testing. Most experts recommend run_phase with reset, configure and main sequences.
What does phase.jump() do?
It sends the phase schedule to another phase, typically back to reset_phase, killing the threads of the current phase and clearing its objections. See example 6.
References
- IEEE 1800.2-2020, UVM standard: the phasing and common-phases clauses, and
uvm_objection. - UVM 1.2 class reference: run-time phases (uvm_runtime_phases.svh).
- Verification Academy forum threads linked in the mistakes and sub-phase sections (accessed 11 Nov 2026).
- All 8 runs by Hardik on Synopsys VCS X-2025.06-SP1 with UVM 1.2, 11 Nov 2026.
Part of the learning path UVM methodology
