Part 2 · Phases & Lifecycle · Intermediate

Structural Sanity: Invariants Before Time 0

Assertion patterns for agent counts, mandatory connections, config presence, and passive/active consistency at elaboration.

What to assert at elaboration

Structural sanity checks catch integration mistakes before run_phase spends simulation time. Focus on existence, counts, and cfg consistency — not protocol behavior.

diagram
[PHASE][UVM] structural invariant categories

EXISTENCE:   required components built (mon, sb, v_sqr)
COUNT:       cfg.num_agents == built agents
MODE:        active/passive consistent with handles
CONFIG:      mandatory cfg/vif fields non-null
MAPPING:     v_sqr sub-sequencer handles set for active agents
systemverilog
function void assert_structural_sanity();
  if (cfg == null)
    `uvm_fatal("SANITY", "cfg null at elaboration")
  if (vif == null)
    `uvm_fatal("SANITY", "vif null at elaboration")
  if (cfg.is_active == UVM_ACTIVE && sqr == null)
    `uvm_fatal("SANITY", "active agent without sequencer")
endfunction

function void end_of_elaboration_phase(uvm_phase phase);
  super.end_of_elaboration_phase(phase);
  assert_structural_sanity();
endfunction

Key takeaways

  • Assert structure, not protocol — protocol checks belong in run_phase.

  • Fatal early on null cfg/vif — cheapest failures to diagnose.

  • Keep sanity functions reusable across agent variants.

Common pitfalls

  • Asserting transaction content at elaboration — no transactions yet.

  • Same fatal message for different failures — ambiguous triage.

  • Sanity only in test — agent problems found too late.


Sanity library patterns

Centralize common checks in env base classes and agent base classes for consistent regression behavior.

Reusable sanity package

systemverilog
class uvm_structural_sanity;
  static function void check_agent(uvm_agent agt, uvm_active_passive_e expected);
    if (agt == null) `uvm_fatal("SANITY", "agent null")
    // delegate to agent-specific checks via virtual method
    agt.check_elaboration_state(expected);
  endfunction
endclass
diagram
[PHASE] sanity ownership

agent.check_elaboration_state:
  mon/drv/sqr vs active mode

env.check_elaboration_state:
  agent array vs cfg bitmap
  sb/cov optional presence

test.check_elaboration_state:
  topology print
  global override audit

Severity policy

  • Fatal: null mandatory handle, active agent missing driver.

  • Error: count mismatch, unexpected optional component.

  • Warning: passive agent with unexpected drv (legacy code path).

systemverilog
virtual function void check_elaboration_state(uvm_active_passive_e expected);
  if (cfg.is_active != expected)
    `uvm_warning("SANITY",
      $sformatf("%s: cfg mode %s != expected %s",
        get_full_name(), cfg.is_active.name(), expected.name()))
endfunction
diagram
[PHASE][UVM] sanity  debug correlation

SANITY fatal "vif null"
   build_phase config_db get failure
   check top module set and agent get path

SANITY fatal "active without sqr"
   build conditional branch bug
   check cfg.is_active at build vs elaboration

Common pitfalls

  • Over-asserting VIP internals — breaks reuse across projects.

  • Sanity that depends on run_phase state — wrong phase.

  • No severity policy — all warnings ignored in CI.