Part 2 · Phases & Lifecycle · Intermediate

build_phase Walkthrough: Test to Agent Timeline

Step-by-step build_phase execution across test, env, and agent — with logging hooks and phase-trace interpretation.

Full build timeline

Follow one regression run from uvm_test_top through env and agent to see exactly when config flows and components appear.

diagram
[PHASE][UVM] annotated build timeline

T0  uvm_test_top.build_phase
      set cfg  config_db
      create env
T1    env.build_phase
        get cfg
        create agt_tx, agt_rx, sb, cov, v_sqr
T2      agt_tx.build_phase
          get cfg, get vif
          create mon, drv, sqr
T3        mon.build_phase (leaf)
T4        drv.build_phase (leaf)
T5        sqr.build_phase (leaf)
T6      agt_rx.build_phase
          ...
T7  build_phase complete  scheduler advances to connect_phase
systemverilog
function void build_phase(uvm_phase phase);
  super.build_phase(phase);
  `uvm_info("PHASE_BUILD",
    $sformatf("enter %s", get_full_name()), UVM_MEDIUM)
  // ... construction ...
  `uvm_info("PHASE_BUILD",
    $sformatf("exit  %s children=%0d", get_full_name(), get_num_children()), UVM_MEDIUM)
endfunction

Key takeaways

  • Phase logging at UVM_MEDIUM gives a build order transcript for free.

  • Leaf components build last within each subtree — deepest first among siblings varies.

  • build_phase ends only when every component in the tree has finished building.

Common pitfalls

  • Infinite recursion — component creates itself as child.

  • build_phase doing heavy computation — slows every regression.

  • Logging every field of cfg at HIGH — log flood obscures real errors.


Instrumented reference implementation

Use this scaffold as a teaching reference — each layer documents what it creates and what config it consumes.

Test layer

systemverilog
class base_test extends uvm_test;
  my_env env;
  env_cfg cfg;
  function void build_phase(uvm_phase phase);
    super.build_phase(phase);
    cfg = env_cfg::type_id::create("cfg");
    apply_defaults();
    uvm_config_db#(env_cfg)::set(this, "env", "cfg", cfg);
    env = my_env::type_id::create("env", this);
  endfunction
endclass

Env and agent layers

systemverilog
class my_env extends uvm_env;
  function void build_phase(uvm_phase phase);
    super.build_phase(phase);
    void'(uvm_config_db#(env_cfg)::get(this, "", "cfg", cfg));
    agt_tx = tx_agent::type_id::create("agt_tx", this);
    sb = my_scoreboard::type_id::create("sb", this);
  endfunction
endclass

class tx_agent extends uvm_agent;
  function void build_phase(uvm_phase phase);
    super.build_phase(phase);
    void'(uvm_config_db#(tx_cfg)::get(this, "", "cfg", cfg));
    void'(uvm_config_db#(virtual tx_if)::get(this, "", "vif", vif));
    mon = tx_monitor::type_id::create("mon", this);
    if (cfg.is_active == UVM_ACTIVE) begin
      drv = tx_driver::type_id::create("drv", this);
      sqr = tx_sequencer::type_id::create("sqr", this);
    end
  endfunction
endclass
diagram
[PHASE] +UVM_PHASE_TRACE interpretation

UVM_PHASE_TRACE prints:
  ENTER/EXIT <phase> <component_full_name>

Build debug recipe:
  1) run with +UVM_PHASE_TRACE
  2) grep PHASE_BUILD logs
  3) compare order to expected top-down tree
  4) first missing component  trace its parent's build_phase

Common pitfalls

  • Comparing phase trace across UVM versions — log format may differ.

  • Assuming alphabetical child order from trace — not guaranteed among siblings.

  • Stopping analysis at first error without capturing full build transcript.