Part 2 · Phases & Lifecycle · Intermediate

Super-Call Discipline: super.phase(phase) Every Time

Why super.build_phase(phase) and friends are mandatory, what base-class work they perform, and ordering conventions for super-first vs super-last.

What super calls do

Calling super.<phase>_phase(phase) executes base-class implementation — field automation hooks, registered callbacks, and internal UVM bookkeeping. Skipping it is a silent correctness bug.

  • super.build_phase triggers config and child phase registration logic.

  • super.connect_phase may complete automated connections.

  • super.run_phase participates in objection and phase state machinery.

  • super.report_phase contributes to global report summary.

diagram
[UVM][PHASE] super call impact

without super.build:
  - factory automation may not run
  - callback registration missed

without super.run:
  - library run scaffolding skipped

without super.report:
  - aggregated report incomplete

Key takeaways

  • super is not ceremonial — it is functional base-class behavior.

  • Missing super is silent until something subtle breaks later.

  • Default convention: super first in function phases unless documented otherwise.

Common pitfalls

  • Removing super to 'avoid double create' — fix duplicate logic instead.

  • Never calling super in intermediate abstract base classes.

  • Calling super twice — usually indicates copy/paste error.


Ordering conventions

Function phases: super first (default)

systemverilog
function void build_phase(uvm_phase phase);
  super.build_phase(phase);
  // now safe to create children and get config
  mon = my_monitor::type_id::create("mon", this);
endfunction

function void connect_phase(uvm_phase phase);
  super.connect_phase(phase);
  mon.ap.connect(scb.imp);
endfunction

When super-last is intentional

Rarely, a child wants parent cleanup after child work — e.g. super.report_phase after local detail prints. Document super-last explicitly in class comments.

systemverilog
function void report_phase(uvm_phase phase);
  `uvm_info("RPT", $sformatf("local_errors=%0d", local_errs), UVM_LOW)
  super.report_phase(phase); // parent aggregates after local detail
endfunction

Inheritance chains

diagram
[PHASE] three-level chain

base_agent.build -> mid_agent.build -> axi_agent.build

each level:
  super.build_phase(phase);  // calls immediate parent
  // then level-specific work

omitting super in mid_agent breaks chain for axi_agent subtree
systemverilog
class mid_agent extends base_agent;
  function void build_phase(uvm_phase phase);
    super.build_phase(phase);
    cfg = agent_cfg::type_id::create("cfg");
  endfunction
endclass

class axi_agent extends mid_agent;
  function void build_phase(uvm_phase phase);
    super.build_phase(phase); // must include mid_agent + base_agent work
    drv = axi_driver::type_id::create("drv", this);
  endfunction
endclass

Key takeaways

  • super propagates up the inheritance chain one level per call.

  • Every class in the hierarchy must call super unless it is uvm_component itself.

  • super-last is exceptional — comment why when used.

Common pitfalls

  • Abstract base with pure virtual build and no super — breaks children.

  • Mixin-style multiple inheritance workarounds that skip super.

  • Assuming macro utils replaces super — it does not.