Part 7 · Environment & Tests · Intermediate

Shared run_phase: Common Flow With Safe Extension Hooks

Designing one shared run_phase in base_test so all scenarios inherit consistent objections, timeout guards, and execution sequencing.

Single run-phase policy

Keep objection control and timeout ownership in one place: base_test::run_phase. Child tests should customize through hooks only.

diagram
[TEST][UVM][ENV] shared run flow

raise objection
  -> pre_main()
  -> run_main_sequence()
  -> wait_for_quiescence()
  -> post_main()
drop objection

timeout guard wraps full flow
systemverilog
virtual task run_phase(uvm_phase phase);
  time start_t;
  start_t = $time;
  phase.raise_objection(this);
  fork
    begin
      pre_main();
      run_main_sequence();
      wait_for_quiescence();
      post_main();
    end
    begin
      #(cfg.default_timeout_ns * 1ns);
      `uvm_fatal("TEST_TIMEOUT", "Shared run_phase timed out")
    end
  join_any
  disable fork;
  phase.drop_objection(this);
  `uvm_info("TEST_DONE", $sformatf("elapsed=%0t", $time - start_t), UVM_LOW)
endtask

Key takeaways

  • Shared lifecycle makes regressions comparable across scenarios.

  • Timeout policy should fail loudly and consistently.

  • Child tests gain flexibility through narrow virtual hooks.

Common pitfalls

  • Child tests opening/dropping objections independently.

  • Multiple timeout mechanisms fighting each other.

  • No quiescence criteria, causing flaky test completion.


Extension hook design

Design hooks by responsibility: setup, stimulus, convergence, teardown.

Hook catalog

systemverilog
virtual task pre_main();
  // program scoreboard modes, reset counters
endtask

virtual task run_main_sequence();
  default_vseq seq = default_vseq::type_id::create("seq");
  seq.start(env.v_sqr);
endtask

virtual task wait_for_quiescence();
  wait (env.sb.outstanding_count() == 0);
endtask

virtual task post_main();
  env.sb.dump_summary();
endtask
  • Every hook should be optional and safe to call by default.

  • Avoid hooks that expose internal component handles.

  • Prefer declarative cfg changes over imperative side effects.

Determinism checks

diagram
[TEST] determinism smoke

seed N:
  run base_test hook order logging enabled
seed N again:
  verify identical hook timeline + pass/fail

if mismatch:
  inspect hook side effects and async waits
diagram
[ENV] quiescence signals

- scoreboard pending txn count
- monitor publish counter stopped
- coverage collector flush complete
- no pending RAL accesses

Common pitfalls

  • Waiting on unstable conditions in wait_for_quiescence().

  • Post-main cleanup mutating checker state before report.

  • Hidden forked threads not joined before dropping objection.