Part 7 · Environment & Tests · Intermediate

Env-Owned Virtual Sequencer: Lifecycle and Reuse Discipline

Own virtual sequencer lifecycle in env for repeatable build/connect behavior and easy reuse across test classes.

Why Env Ownership

Env ownership ensures one canonical construction path. Tests remain policy-focused and cannot accidentally diverge infrastructure setup.

diagram
[TEST][UVM][ENV] ownership tradeoff

test-owned v_sqr:
  many construction variants
  repeated boilerplate
  inconsistent diagnostics

env-owned v_sqr:
  one lifecycle contract
  one mapping location
  one health-report path
systemverilog
class soc_env extends uvm_env;
  `uvm_component_utils(soc_env)
  soc_virtual_sequencer v_sqr;
  apb_agent apb_agt;
  axi_agent axi_agt;
  irq_agent irq_agt;

  function void build_phase(uvm_phase phase);
    super.build_phase(phase);
    v_sqr = soc_virtual_sequencer::type_id::create("v_sqr", this);
    apb_agt = apb_agent::type_id::create("apb_agt", this);
    axi_agt = axi_agent::type_id::create("axi_agt", this);
    irq_agt = irq_agent::type_id::create("irq_agt", this);
  endfunction
endclass

Key takeaways

  • Centralized ownership produces consistent startup behavior.

  • Tests stay thin and focus on scenario-level intent.

  • Reuse improves when infrastructure setup is not duplicated in tests.

Common pitfalls

  • Constructing v_sqr in individual tests.

  • Conditionally creating v_sqr based on scenario name.

  • Spreading sequencer mapping across multiple components.


Lifecycle Contract

diagram
[TEST][UVM] phase contract

build_phase:
  create v_sqr and child agents

connect_phase:
  map child sequencer handles
  null inactive handles explicitly

end_of_elaboration:
  enforce required-handle contract

run_phase:
  tests start vseq on env.v_sqr
systemverilog
function void connect_phase(uvm_phase phase);
  super.connect_phase(phase);
  v_sqr.apb_sqr_h = cfg.apb_active ? apb_agt.sqr : null;
  v_sqr.axi_sqr_h = cfg.axi_active ? axi_agt.sqr : null;
  v_sqr.irq_sqr_h = cfg.irq_active ? irq_agt.sqr : null;
endfunction
systemverilog
function void end_of_elaboration_phase(uvm_phase phase);
  super.end_of_elaboration_phase(phase);
  if (cfg.require_apb && v_sqr.apb_sqr_h == null)
    `uvm_fatal("ENV_VSQR", "APB required but unmapped")
  if (cfg.require_axi && v_sqr.axi_sqr_h == null)
    `uvm_fatal("ENV_VSQR", "AXI required but unmapped")
endfunction
  • Keep lifecycle rules explicit and documented near env code.

  • Fail fast on required-handle mismatch.

  • Avoid runtime remapping unless deliberately designed and tested.


Applied Patterns

diagram
[TEST][UVM][ENV] env-owned v_sqr checklist

construction:
  create once in env.build_phase
  keep factory override-friendly

mapping:
  connect_phase only
  active/passive aware
  clear null assignment for inactive handles

validation:
  end_of_elaboration fatal checks
  scenario-level optional checks in vseq.pre_start

observability:
  startup handle map log
  report_phase health summary
systemverilog
function void report_phase(uvm_phase phase);
  `uvm_info("ENV_VSQR_MAP",
    $sformatf("apb=%0d axi=%0d irq=%0d",
      v_sqr.apb_sqr_h != null,
      v_sqr.axi_sqr_h != null,
      v_sqr.irq_sqr_h != null),
    UVM_LOW)
endfunction
  • One owner and one lifecycle avoid ambiguous startup behavior.

  • Explicit mapping logs cut triage time for null-handle failures.

  • Env ownership scales better as agents are added.