Part 2 · Phases & Lifecycle · Intermediate
end_of_elaboration Checks: Topology Audit and Validation
Canonical end_of_elaboration usage — print_topology, connection audits, agent counts, and bottom-up structural validation.
Last structural checkpoint
end_of_elaboration_phase is the final build-time audit . Every component exists, every connect call has run — now validate that the assembly matches intent.
function void end_of_elaboration_phase(uvm_phase phase);
super.end_of_elaboration_phase(phase);
// Global topology audit — do this at test or env level
`uvm_info("ELAB", "=== Component Topology ===", UVM_LOW)
uvm_top.print_topology();
// Local structural validation
if (cfg.num_agents != agents.size())
`uvm_error("ELAB", $sformatf("expected %0d agents, have %0d",
cfg.num_agents, agents.size()))
if (cfg.enable_scoreboard && sb == null)
`uvm_fatal("ELAB", "scoreboard enabled in cfg but sb not built")
endfunction[PHASE][UVM] end_of_elaboration bottom-up flow
leaf components validate local state
→ agents check drv/sqr presence vs active mode
→ env checks agent count, sb/cov presence
→ test prints global topology summaryKey takeaways
print_topology is the single highest-value elaboration diagnostic.
Assert cfg-driven counts match built topology.
Bottom-up order lets children self-check before parent aggregates.
Common pitfalls
Only printing topology without asserting invariants.
Fatal on optional components disabled in cfg.
Doing topology print at UVM_NONE — lost in noisy logs.
Validation patterns
Layer checks from agent internals up to env integration for maximum debug signal.
Agent-level checks
class my_agent extends uvm_agent;
function void end_of_elaboration_phase(uvm_phase phase);
super.end_of_elaboration_phase(phase);
if (mon == null)
`uvm_fatal("ELAB", $sformatf("%s: monitor not built", get_full_name()))
if (cfg.is_active == UVM_ACTIVE && (drv == null || sqr == null))
`uvm_fatal("ELAB", $sformatf("%s: active but drv/sqr missing", get_full_name()))
if (cfg.is_active == UVM_PASSIVE && (drv != null || sqr != null))
`uvm_warning("ELAB", $sformatf("%s: passive but drv/sqr exist", get_full_name()))
endfunction
endclass[PHASE] agent elaboration checklist
[ ] mon exists always
[ ] active → drv and sqr exist
[ ] passive → drv and sqr null
[ ] vif handle non-null
[ ] cfg object non-nullEnv-level aggregation
Compare cfg.agent_en bitmap against agents[] membership.
Verify scoreboard imps exist when analysis connect expected.
Check virtual sequencer handles for every active agent.
function void end_of_elaboration_phase(uvm_phase phase);
super.end_of_elaboration_phase(phase);
foreach (cfg.agent_en[i])
if (cfg.agent_en[i] && !agents.exists(i))
`uvm_error("ELAB", $sformatf("agent[%0d] enabled but missing", i))
if (v_sqr.tx_sqr == null && agents[TX].cfg.is_active == UVM_ACTIVE)
`uvm_error("ELAB", "v_sqr.tx_sqr not mapped")
endfunction[PHASE][UVM] elaboration severity guide
uvm_info → topology print, audit banners
uvm_warning → suspicious but non-fatal (passive with drv)
uvm_error → mismatch worth failing regression
uvm_fatal → guaranteed broken run if continuedCommon pitfalls
Duplicating same assert in end_of_elaboration and connect — pick one owner.
Error flood from loop asserts — summarize counts instead.
Checking TLM bind internals — UVM does not expose connect graph easily.