Part 6 · Agents & Protocol IP · Intermediate
build_phase Conditional: Deterministic Active/Passive Construction
Implement robust mode-gated construction in build_phase, including cfg precedence, instance-specific mode control, and topology assertions.
Mode resolution strategy
Resolve mode exactly once in build_phase from cfg (or explicit override policy), then build the topology accordingly. Do not recompute mode ad hoc in later phases.
[UVM][AGT] mode precedence pattern
1) cfg-provided mode (preferred)
2) explicit top-level override (if project policy allows)
3) default mode with warning
once resolved:
freeze topology decisions for this instance[AGT] why one-time resolution
if mode shifts after build:
child topology no longer matches behavior assumptions
null-handle/runtime contention bugs appearTreat mode as structural decision, not dynamic runtime toggle.
Log effective mode for every agent instance.
Document precedence policy clearly in wrapper code.
Conditional build implementation
function void build_phase(uvm_phase phase);
super.build_phase(phase);
if (!uvm_config_db#(axi_cfg)::get(this, "", "cfg", cfg))
`uvm_fatal("NOCFG", "axi_cfg missing")
is_active = cfg.is_active;
ap = new("ap", this);
mon = axi_monitor::type_id::create("mon", this);
uvm_config_db#(axi_cfg)::set(this, "mon", "cfg", cfg);
if (is_active == UVM_ACTIVE) begin
sqr = axi_sequencer::type_id::create("sqr", this);
drv = axi_driver::type_id::create("drv", this);
uvm_config_db#(axi_cfg)::set(this, "drv", "cfg", cfg);
end
else begin
sqr = null;
drv = null;
end
`uvm_info("AGT_MODE",
$sformatf("%s mode=%s", get_full_name(),
(is_active == UVM_ACTIVE) ? "ACTIVE" : "PASSIVE"),
UVM_LOW)
endfunction[UVM][AGT] topology assertions
ACTIVE:
sqr != null && drv != null && mon != null
PASSIVE:
sqr == null && drv == null && mon != nullExplicitly null absent handles in passive mode.
Forward cfg only to children that exist.
Use deterministic mode logs for triage.
Mixed-instance deployment
One env often hosts mixed mode instances of the same agent class. Each instance gets independent cfg object and mode value.
function void build_phase(uvm_phase phase);
super.build_phase(phase);
i2c_cfg host_cfg = i2c_cfg::type_id::create("host_cfg");
i2c_cfg dev_cfg = i2c_cfg::type_id::create("dev_cfg");
void'(uvm_config_db#(virtual i2c_if)::get(this, "", "host_vif", host_cfg.vif));
void'(uvm_config_db#(virtual i2c_if)::get(this, "", "dev_vif", dev_cfg.vif));
host_cfg.is_active = UVM_ACTIVE;
dev_cfg.is_active = UVM_PASSIVE;
uvm_config_db#(i2c_cfg)::set(this, "host_agt", "cfg", host_cfg);
uvm_config_db#(i2c_cfg)::set(this, "dev_agt", "cfg", dev_cfg);
host_agt = i2c_agent::type_id::create("host_agt", this);
dev_agt = i2c_agent::type_id::create("dev_agt", this);
endfunction[AGT] mixed deployment outcome
host_agt:
drives + observes
dev_agt:
observes only
same class reused with instance-specific cfgKey takeaways
Mode-gated build should be deterministic and instance-specific.
Structural decisions belong in build_phase and remain stable thereafter.
Mixed active/passive deployment is a first-class architecture pattern.
Topology assertions catch mode bugs before run-phase traffic.
Common pitfalls
Using one shared cfg across multiple instances and leaking mode values.
Creating active children unconditionally then disabling behavior later.
Re-evaluating mode in run_phase and diverging from built topology.
Skipping mode logs and losing quick visibility in regressions.