Part 2 · Phases & Lifecycle · Intermediate

Pre-Time-Zero Contract: What Is Legal Before run_phase

Strict contract for function phases before run — zero time, frozen structure, allowed diagnostics, and common violations.

The zero-time boundary

From build through start_of_simulation, simulation time must not advance . These function phases set up structure and documentation; run_phase is the first common task phase that may consume time.

diagram
[PHASE][UVM] function phase timeline (0 time)

build_phase ──────────── construct tree
connect_phase ────────── wire TLM
end_of_elaboration ───── validate tree
start_of_simulation ──── document intent
───────────────────────────────────────── time = 0, structure frozen
run_phase ────────────── time may advance
systemverilog
// ILLEGAL in any pre-run function phase:
function void start_of_simulation_phase(uvm_phase phase);
  super.start_of_simulation_phase(phase);
  #(100ns);                    // ERROR — consumes time
  wait(vif.rst_n == 1'b1);     // ERROR — waits for event
  seq.start(env.v_sqr);        // ERROR — run_phase activity
endfunction

Key takeaways

  • Function phases before run are zero-time by contract.

  • Structure is frozen after build — connect only wires it.

  • Stimulus, waits, and delays belong exclusively to run_phase.

Common pitfalls

  • Waiting for PLL lock in start_of_simulation.

  • Forking threads in end_of_elaboration that consume time.

  • Using run_phase callbacks registered to fire before time 0.


Contract enforcement in review

Use this checklist in code review and CI lint rules to catch phase violations before they reach regressions.

Per-phase allowed operations

diagram
[PHASE][UVM] pre-run contract matrix

Phase                  Create  Connect  Assert  Print  Wait
─────────────────────────────────────────────────────────────
build_phase            YES     NO       YES     YES    NO
connect_phase          NO      YES      YES     YES    NO
end_of_elaboration     NO      NO       YES     YES    NO
start_of_simulation    NO      NO       YES     YES    NO
run_phase              NO*     NO       YES     YES    YES

* run_phase: no new uvm_components — structure frozen

Migration guide for violations

  1. Identify #delay or wait in function phase — move to run_phase task.

  2. Identify sequence.start in elaboration — move to run_phase.

  3. Identify create in connect/elaboration — move to build_phase.

  4. Identify pin drive before run — move to driver run_phase or interface init.

  5. Add elaboration assert where violation was masking missing component.

diagram
[PHASE] common violation sources

legacy SV testbench init block code copied into start_of_simulation
reset task called from end_of_elaboration "for early readiness"
config randomize after build "to save time"
  • Interface initial blocks may drive static reset — separate from UVM phases.

  • DUT reset sequencing belongs in driver/monitor run_phase or SV interface.

  • Document any intentional exception with a comment and review ticket.

Common pitfalls

  • Assuming pre_run reset is OK because 'hardware needs it'.

  • Using package init blocks that consume time before UVM phases.

  • Mixing module initial and UVM phase responsibilities without docs.