Part 2 · Phases & Lifecycle · Intermediate
Phased Construction & Connection: Dependency Satisfaction
How build_phase and connect_phase enforce structural ordering — top-down creation, bottom-up TLM wiring, and why endpoints are never null.
Two structural phases, two directions
UVM splits structural setup into build_phase (top-down construction) and connect_phase (bottom-up wiring). The directions exist so dependencies are always satisfied.
[UVM][PHASE] structural phases
build_phase (top-down):
parent creates children
children then build themselves
connect_phase (bottom-up):
leaf ports exist first
parents wire siblings and exports
invariant after build:
every component in topology exists
invariant after connect:
every planned TLM link attempted with live endpointsclass my_env extends uvm_env;
`uvm_component_utils(my_env)
axi_agent axi;
apb_agent apb;
my_scoreboard scb;
function void build_phase(uvm_phase phase);
super.build_phase(phase);
axi = axi_agent::type_id::create("axi", this);
apb = apb_agent::type_id::create("apb", this);
scb = my_scoreboard::type_id::create("scb", this);
endfunction
function void connect_phase(uvm_phase phase);
super.connect_phase(phase);
axi.mon.ap.connect(scb.axi_imp);
apb.mon.ap.connect(scb.apb_imp);
endfunction
endclassKey takeaways
build_phase answers 'what exists?'; connect_phase answers 'how is it wired?'
Top-down build ensures parents can create children before child build runs.
Bottom-up connect ensures leaf ports exist before parent-level wiring.
Common pitfalls
Connecting TLM in build_phase — peer components may not exist yet.
Lazy-creating children in run_phase — breaks connect and print_topology.
Assuming connect is top-down — it is bottom-up by design.
Traversal example on a realistic tree
Visit order
tree: build (TD) connect (BU)
test 1 test 6 test
└ env 2 env 5 env
└ axi_agent 3 axi_agent 4 axi_agent
├ driver 4 driver 1 driver
├ monitor 5 monitor 2 monitor
└ sequencer 6 sequencer 3 sequencer
TD = top-down, BU = bottom-up
[PHASE] only cross-phase order is portable — not sibling order within a phasefunction void build_phase(uvm_phase phase);
super.build_phase(phase);
`uvm_info("PHASE", $sformatf("BUILD %s", get_full_name()), UVM_LOW)
endfunction
function void connect_phase(uvm_phase phase);
super.connect_phase(phase);
`uvm_info("PHASE", $sformatf("CONNECT %s", get_full_name()), UVM_LOW)
endfunctionFactory override timing
Overrides must register before the create that should pick them up. Top-down build_phase on the test enables this naturally:
function void build_phase(uvm_phase phase);
my_driver::type_id::set_type_override(err_driver::get_type());
super.build_phase(phase); // env->agent->driver create sees override
endfunctionKey takeaways
Watching PHASE logs is the fastest way to internalize traversal direction.
Override-before-super.build is a direct consequence of top-down build.
Structural phases are zero-time — no hardware events belong here.
Common pitfalls
Registering overrides after super.build_phase — too late for child create.
Creating components with new() — overrides and topology tracking break.
Using #delay in build to 'wait for reset' — move to run_phase.