HomeProblemsTheoryUVM
Beginner3 min readChapter 3

UVM Phases

Learn the UVM phase lifecycle — build, connect, run, and cleanup — and why the ordering matters.

UVM Phases

UVM uses a phased execution model to ensure that all components are built, connected, and configured before simulation begins. Understanding phases is fundamental to writing correct UVM code.


Phase Categories

CategoryPhasesExecution Order
Build-timebuild, connect, end_of_elaborationTop-down (parent first)
Run-timerun (+ sub-phases)All components in parallel
Cleanupextract, check, report, finalBottom-up (child first)

The Complete Phase Sequence

text
build_phase ← Create child components (top → down) connect_phase ← Wire up ports/exports (bottom → up) end_of_elaboration ← Final configuration adjustments start_of_simulation ← Print topology, banners run_phase ← Main simulation (all run in parallel) ├── reset_phase ├── configure_phase ├── main_phase └── shutdown_phase extract_phase ← Collect results (bottom → up) check_phase ← Verify results report_phase ← Print summaries final_phase ← Cleanup

The Three Most Important Phases

1. build_phase — Create Components

This is where you create child components using the factory. It runs top-down (parent before child), so a parent exists before its children.

systemverilog
function void build_phase(uvm_phase phase); super.build_phase(phase); // Create sub-components via the factory driver = my_driver::type_id::create("driver", this); monitor = my_monitor::type_id::create("monitor", this); endfunction

Rule: Never create components outside of build_phase.


2. connect_phase — Wire Up Ports

This is where you connect TLM ports and exports. It runs bottom-up (children first), so all children are fully built.

systemverilog
function void connect_phase(uvm_phase phase); // Connect driver's port to sequencer's export driver.seq_item_port.connect(sequencer.seq_item_export); // Connect monitor's analysis port to scoreboard monitor.ap.connect(scoreboard.analysis_export); endfunction

Rule: Never connect ports in build_phase — the children may not exist yet.


3. run_phase — Run the Simulation

This is where actual simulation happens. All components' run_phase tasks execute concurrently.

systemverilog
task run_phase(uvm_phase phase); phase.raise_objection(this); // "Don't end yet!" // Drive 100 clock cycles of stimulus repeat(100) @(posedge vif.clk); phase.drop_objection(this); // "I'm done." endtask

The Objection Mechanism

The run_phase doesn't end automatically — it ends when all objections are dropped.

systemverilog
// In the test: task run_phase(uvm_phase phase); my_sequence seq; phase.raise_objection(this); // Keep simulation alive seq = my_sequence::type_id::create("seq"); seq.start(env.agent.sequencer); phase.drop_objection(this); // Done — simulation can end endtask

What happens if you forget raise_objection? The simulation ends immediately at time 0 — no stimulus is generated!

Best practice: Raise objections in the test or sequence, not in drivers or monitors.


report_phase — Print Results

systemverilog
function void report_phase(uvm_phase phase); `uvm_info("REPORT", $sformatf( "Total: %0d | Pass: %0d | Fail: %0d", total, pass_count, fail_count ), UVM_NONE) endfunction

Common Mistakes

MistakeWhat Goes Wrong
Creating components outside build_phaseNull references, crashes
Connecting ports in build_phaseChildren don't exist yet
Forgetting raise_objectionSimulation ends at time 0
Using blocking calls in function phasesOnly run_phase is a task
Calling super.build_phase() too lateFactory overrides don't apply

Try It Yourself

Add a report_phase to a scoreboard component that prints the total number of transactions checked.