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.

diagram
[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 endpoints
systemverilog
class 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
endclass

Key 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

diagram
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 phase
systemverilog
function 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)
endfunction

Factory override timing

Overrides must register before the create that should pick them up. Top-down build_phase on the test enables this naturally:

systemverilog
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
endfunction

Key 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.