Part 2 · Phases & Lifecycle · Intermediate
Conditional Construction: Active, Passive, and Feature Gates
Building only what you need — active/passive agents, optional scoreboards, parameterized agent arrays, and connect-safe branching.
Structure follows config
build_phase is where cfg decides topology . The canonical pattern gates driver and sequencer creation on active mode, and omits optional checkers when disabled.
function void build_phase(uvm_phase phase);
super.build_phase(phase);
void'(uvm_config_db#(agent_cfg)::get(this, "", "cfg", cfg));
mon = my_monitor::type_id::create("mon", this);
if (cfg.is_active == UVM_ACTIVE) begin
drv = my_driver::type_id::create("drv", this);
sqr = my_sequencer::type_id::create("sqr", this);
end
endfunction[PHASE][UVM] active vs passive build
UVM_ACTIVE:
create mon + drv + sqr
UVM_PASSIVE:
create mon only
drv = null, sqr = null
connect_phase MUST mirror this branchKey takeaways
Every conditional create in build_phase needs a matching guard in connect_phase.
Passive agents still need monitor and vif; they skip driver/sequencer.
Feature flags (enable_sb, enable_cov) belong in build_phase branching.
Common pitfalls
Connecting drv.seq_item_port when drv was never created — null deref.
Changing is_active after build — too late, topology already fixed.
Creating optional components in connect_phase instead of build.
Parameterized and multi-instance builds
Large environments build agent arrays and optional subsystems from cfg fields — keep loops deterministic and named consistently.
Agent array pattern
function void build_phase(uvm_phase phase);
super.build_phase(phase);
void'(uvm_config_db#(env_cfg)::get(this, "", "cfg", cfg));
foreach (cfg.agent_en[i]) begin
if (cfg.agent_en[i]) begin
string agt_name = $sformatf("agt[%0d]", i);
agents[i] = my_agent::type_id::create(agt_name, this);
uvm_config_db#(agent_cfg)::set(this, {agt_name, "*"}, "cfg", cfg.agt_cfg[i]);
end
end
endfunction[PHASE] multi-instance naming
Good: agt[0], agt[1], agt[2]
Bad: agt0, agent_1, tx_agent (inconsistent override paths)
Factory inst_override paths depend on exact instance namesOptional subsystem gates
if (cfg.enable_scoreboard) sb = scoreboard::type_id::create(...)
if (cfg.enable_coverage) cov = coverage::type_id::create(...)
connect_phase checks null before wiring optional analysis paths.
function void connect_phase(uvm_phase phase);
super.connect_phase(phase);
agt.mon.ap.connect(sb.act_imp);
if (cfg.enable_coverage)
agt.mon.ap.connect(cov.analysis_export);
endfunction[PHASE][UVM] conditional build/connect parity
build: if (enable_X) create X
connect: if (enable_X) wire X
debug: print_topology shows only created instancesCommon pitfalls
Foreach loop index mismatch between cfg arrays and created agents.
Hardcoded agent count ignoring cfg.agent_en bitmap.
Optional checker disabled in cfg but still referenced in connect.