Part 2 · Phases & Lifecycle · Intermediate

Ordering Bug Patterns: Build, Connect, Run Placement

Common ordering mistakes — null connect handles, config_db races, TLM wiring before create, and run_phase activity in build.

Build/connect ordering

UVM guarantees build_phase runs top-down and connect_phase runs bottom-up. Violations usually come from testbench code fighting the schedule — not from UVM itself.

diagram
[PHASE][UVM] ordering guarantees vs common breaks

GUARANTEED:
  parent build before child build (top-down)
  all builds done before any connect (bottom-up connect)

COMMON BREAKS:
  config_db::set after child already read in build
  connect to component never created (active/passive branch)
  get() in connect for handle set in sibling's build

Pattern: null handle in connect_phase

systemverilog
// BUG: agent not created when is_active==UVM_PASSIVE
function void build_phase(uvm_phase phase);
  super.build_phase(phase);
  if (cfg.is_active == UVM_ACTIVE)
    driver = driver_c::type_id::create("driver", this);
endfunction

function void connect_phase(uvm_phase phase);
  super.connect_phase(phase);
  driver.seq_item_port.connect(sequencer.seq_item_export);
  // NULL when passive — crash
endfunction
systemverilog
// FIX: guard connect
function void connect_phase(uvm_phase phase);
  super.connect_phase(phase);
  if (driver != null)
    driver.seq_item_port.connect(sequencer.seq_item_export);
endfunction
  • print_topology shows whether the component exists.

  • Active/passive create branches are the #1 null-connect cause.

  • Factory override registered too late — child already built.


Pattern: config_db race

systemverilog
// BUG: child build runs before parent set()
function void build_phase(uvm_phase phase);
  super.build_phase(phase);   // children build here — child get() fails
  uvm_config_db#(int)::set(this, "*", "mode", 1);
endfunction
systemverilog
// FIX: set before super.build_phase
function void build_phase(uvm_phase phase);
  uvm_config_db#(int)::set(this, "*", "mode", 1);
  super.build_phase(phase);
endfunction

Symptom → fix table

  1. Null in connect → component not created; check active/passive branch.

  2. config_db get failed → set() after super.build_phase; reorder.

  3. Override ignored → register before super.build_phase creates target.

  4. TLM no traffic → connect_phase never called super; child ports unbound.

Key takeaways

  • Null connect = missing create or guarded branch without connect guard.

  • set() config_db before super.build_phase for child visibility.

  • print_topology is the fastest build/connect sanity check.

Common pitfalls

  • Fixing null connect with delay instead of create/guard.

  • Assuming sibling build order — not guaranteed without explicit config.