Part 2 · Phases & Lifecycle · Intermediate
build_phase Debug: Symptom-First Construction Triage
Deterministic debug workflow for factory misses, config_db failures, null children, and topology surprises at build time.
Symptom matrix
Most build failures fall into four buckets. Start with the symptom, not the innermost component.
[PHASE][UVM] build-phase triage matrix
SYMPTOM LIKELY CAUSE
─────────────────────────────────────────────────────────
uvm_fatal NOCFG / NOVIF config_db path/type/name mismatch
wrong component type in tree override too late or wrong path
null child at connect conditional build branch mismatch
duplicate component name two creates with same instance name
build_phase never entered method signature typo (not an override)
sim hangs in build infinite create loop / recursion// Signature typo — this is NOT an override, build_phase never runs:
function void build(uvm_phase phase); // WRONG NAME
super.build_phase(phase);
endfunctionKey takeaways
config_db dump() is the first tool for any get failure.
Factory print() confirms active overrides before create.
print_topology in end_of_elaboration validates build results.
Common pitfalls
Debugging connect-phase nulls without revisiting build-phase branches.
Adding uvm_fatal bypass with default cfg — hides real integration bug.
Changing randomize in build_phase — non-deterministic structure.
Deterministic debug toolkit
Keep these diagnostics cheap enough to leave enabled in base_test and env base classes.
Instrumentation primitives
function void build_phase(uvm_phase phase);
super.build_phase(phase);
build_enter_count++;
`uvm_info("BUILD_DBG",
$sformatf("comp=%s cfg_ok=%0d vif_ok=%0d",
get_full_name(), cfg != null, vif != null), UVM_LOW)
endfunction
function void report_phase(uvm_phase phase);
super.report_phase(phase);
`uvm_info("BUILD_SUMMARY",
$sformatf("build_enter_count=%0d", build_enter_count), UVM_NONE)
endfunction[PHASE] debug command recipe
simv +UVM_TESTNAME=smoke_test \
+UVM_PHASE_TRACE \
+UVM_CONFIG_DB_TRACE \
+UVM_VERBOSITY=UVM_MEDIUM
Then:
1) grep NOCFG / NOVIF / FACTORY
2) uvm_config_db::dump() at failure point
3) factory.print(1)
4) uvm_top.print_topology() in end_of_elaborationReproduction checklist
Reproduce with one seed and UVM_MEDIUM verbosity.
Capture build_phase log order from test to failing component.
Validate config_db set paths against get_full_name() of target.
Confirm factory overrides precede the create that should use them.
Print topology — verify conditional branches match cfg intent.
Only then inspect sequence or run_phase behavior.
Keep a smoke_test that builds env with zero stimulus for fast iteration.
Diff topology print between passing and failing cfg settings.
Track build_enter_count — unexpected zero means override never ran.
Key takeaways
Boundary-first debug: config → factory → topology → connect.
A smoke build-only test isolates construction in under one second.
Persistent low-cost build logging pays off across the whole project.
Common pitfalls
Enabling UVM_FULL before establishing baseline MEDIUM logs.
Fixing symptoms in connect_phase when root cause is build branching.
Skipping post-fix rerun with the exact failing plusarg combination.