Part 7 · Environment & Tests · Intermediate

build_phase Plusarg Read: Parse Before Env Consumes cfg

Lifecycle timing for plusarg parsing so cfg is fully resolved before env and agent build_phase get paths run.

Parse window and ordering

Plusargs must be resolved in base_test::build_phase before env creation and before config_db consumers read cfg. Late parsing causes silent defaults or race-like mismatches.

diagram
[TEST][UVM][ENV] build_phase order (required)

1) super.build_phase(phase)
2) cfg = env_cfg::type_id::create()
3) cfg.apply_defaults()
4) configure_from_plusargs()     <-- CLI resolved here
5) optional cfg.randomize()
6) uvm_config_db::set(this, "env*", "cfg", cfg)
7) env = my_env::type_id::create("env", this)

env.build_phase -> get cfg (already complete)
systemverilog
function void build_phase(uvm_phase phase);
  super.build_phase(phase);
  cfg = env_cfg::type_id::create("cfg");
  cfg.apply_defaults();
  configure_from_plusargs();
  if (!cfg.randomize())
    `uvm_fatal("CFG", "cfg randomize failed")
  uvm_config_db#(env_cfg)::set(this, "env*", "cfg", cfg);
  log_resolved_cfg();
  env = my_env::type_id::create("env", this);
endfunction

Key takeaways

  • Parsing after env creation is too late — components may read stale cfg.

  • apply_defaults() first, then plusargs override defaults explicitly.

  • Log cfg snapshot immediately after parse for triage.

Common pitfalls

  • Creating env before config_db set completes.

  • Parsing plusargs in run_phase when build_phase already consumed cfg.

  • Child test overriding build_phase without preserving parse order.


connect_phase and run_phase boundaries

After build_phase, treat cfg as immutable unless you have a documented late-override policy.

Immutability policy

diagram
[TEST] cfg lifecycle

build_phase:   cfg writable (defaults + plusargs + randomize)
connect_phase: cfg read-only for env/agents
run_phase:     scenario hooks only; no CLI re-parse
  • If a knob must change at run_phase, use a virtual hook — not new plusarg reads.

  • Env should copy cfg fields it needs in build_phase, not hold a mutable handle.

  • Document any exception paths (e.g., debug plusarg dump) explicitly.

Late-read anti-pattern

systemverilog
// BAD: agent reads plusarg directly in connect_phase
function void connect_phase(uvm_phase phase);
  string s;
  if ($value$plusargs("SEQ_COUNT=%d", cfg.seq_count)) ; // too late, wrong layer
endfunction
systemverilog
// GOOD: agent uses cfg from config_db in build_phase
function void build_phase(uvm_phase phase);
  super.build_phase(phase);
  if (!uvm_config_db#(env_cfg)::get(this, "", "cfg", cfg))
    `uvm_fatal("CFG", "missing env_cfg")
endfunction

Common pitfalls

  • Agents calling $value$plusargs — breaks single-parser policy.

  • Mutable cfg handles shared without copy-on-build semantics.

  • Randomize after env creation — env sees pre-random values.