Part 11 · Senior Prep · Intermediate

run_phase & drain_time Interview Q&A

Model answers on when run_phase ends, parallel task execution, drain_time tuning, and run_phase debug patterns.

run_phase ending mechanics

run_phase is a parallel task phase — all components' run_phase tasks start together and end on shared objection quiescence.

Q: When does run_phase end?

diagram
[INT][SENIOR][UVM] MODEL ANSWER

Q: When does run_phase end?

A:
  MECHANISM:  run_phase ends when all objections dropped, drain_time elapsed, and
              phase_ready_to_end callbacks complete without re-raise.
  MOTIVATION:  Parallel agents finish at different times — objection model waits for
              last active worker, not a fixed delay or first-completed sequence.
  WHEN:       Sequences raise in run_phase, drop after body() and all forks join.
              Test may extend drain_time for in-flight bus transactions.
  PITFALL:    Assuming run_phase ends when main sequence returns — forked threads
              still running keep implicit activity even without objection.
  EXAMPLE:    vseq drops objection; driver still processing last item — drain_time
              absorbs pipeline before extract_phase begins.

Q: Is run_phase parallel across components?

diagram
[INT][SENIOR][UVM] MODEL ANSWER

Q: Is run_phase parallel?

A:
  MECHANISM:  run_phase is a task phase — scheduler forks all component run_phase
              tasks; they execute concurrently subject to simulation time advancement.
  MOTIVATION:  TX agent, RX agent, scoreboard, and test all need simultaneous activity
              during stimulus — sequential run_phase would deadlock the TB.
  WHEN:       Always — every component's run_phase runs in parallel with others in
              the same phase domain.
  PITFALL:    Blocking call in env run_phase that waits for test — circular wait
              because test run_phase also waits for env activity.
  EXAMPLE:    driver run_phase loops get_next_item while monitor run_phase samples
              pins concurrently; test run_phase starts sequences in parallel.

Q: How do you debug a hung run_phase?

diagram
[INT][SENIOR][UVM] MODEL ANSWER

Q: Hung run_phase — debug order?

A:
  MECHANISM:  Hang = run_phase task never returns — objection leak, driver handshake
              stall, or infinite wait in component run_phase.
  MOTIVATION:  Systematic triage beats random waveform scrolling — localize which
              component blocks phase completion.
  WHEN:       Step 1: display_objections(). Step 2: probe driver get_next_item /
              item_done. Step 3: check reset and vif. Step 4: UVM_PHASE_TRACE.
  PITFALL:    Opening waves before objection trace — wastes 30 minutes on healthy
              components while one sequencer stall holds the phase.
  EXAMPLE:    display_objections shows test holds objection; virtual sequence fork
              never joined — add join before drop_objection.

Q: What is the relationship between run_phase and runtime sub-phases?

diagram
[INT][SENIOR][UVM] MODEL ANSWER

Q: run_phase vs runtime sub-phases?

A:
  MECHANISM:  run_phase executes in parallel with 12 runtime sub-phases (pre_reset
              through post_shutdown) — all are sibling task phases in the default domain.
  MOTIVATION:  Legacy UVM kept run_phase for backward compatibility while sub-phases
              provide finer-grained reset/configure/main/shutdown orchestration.
  WHEN:       Modern TBs use sub-phases for structured bring-up; run_phase for general
              stimulus. Many teams raise objections in main_phase instead of run_phase.
  PITFALL:    Raising objection only in run_phase while sequences run in main_phase —
              phase ends before main_phase stimulus completes (or vice versa).
  EXAMPLE:    Test raises/drops in main_phase; run_phase task empty — objections must
              match the sub-phase where sequences actually execute.

Key takeaways

  • run_phase ends on objections + drain_time — not sequence return alone.

  • All component run_phase tasks execute in parallel.

  • Debug order: objections → driver handshake → vif/config → phase trace.

Common pitfalls

  • Empty run_phase in test but sequences run in main_phase without matching objections.

  • Circular wait between env and test run_phase tasks.


drain_time and run_phase policy

Q: How do you set drain_time correctly for a protocol?

diagram
[INT][SENIOR][UVM] MODEL ANSWER

Q: Setting drain_time for a protocol?

A:
  MECHANISM:  phase.phase_done.set_drain_time(component, duration) adds post-count-zero
              wait before phase_done triggers for that component's subtree context.
  MOTIVATION:  Last transaction may have response beats after driver drops objection;
              drain_time absorbs protocol pipeline without keeping objection raised.
  WHEN:       Set to max expected trailing latency: AXI R/B after last AW, PCIe CPL
              after last TLP, interrupt pulse after last MMIO write.
  PITFALL:    Copy-pasting 1us drain_time from one protocol to another — either too
              short (truncated checks) or too long (farm time waste).
  EXAMPLE:    PCIe env sets 2us drain_time on run_phase; scoreboard clears retry queue
              during drain window before extract_phase.

Q: Should the test or agent own run_phase objection policy?

diagram
[INT][SENIOR][UVM] MODEL ANSWER

Q: Who owns run_phase objections?

A:
  MECHANISM:  Test (or base_test) typically raises umbrella objection for scenario
              duration; sequences may raise sub-window objections for fine control.
  MOTIVATION:  Centralized test policy ensures consistent end criteria across scenarios;
              agent-level raises are for agent-internal background tasks (periodic monitor).
  WHEN:       base_test raises at run_phase entry, drops after wait_for_quiescence().
              Sequences raise only if test delegates per-sequence control.
  PITFALL:    Both test and every sequence raise independently — hard to balance,
              double-count confusion, premature or never-ending phase.
  EXAMPLE:    base_test owns objection; child sequences run without raising — test
              drops after scoreboard pending count hits zero.
systemverilog
// run_phase ending — interview pattern
task run_phase(uvm_phase phase);
  phase.raise_objection(this, "test start");
  phase.phase_done.set_drain_time(this, 500ns);
  run_main_sequence();
  wait_for_scoreboard_empty();
  phase.drop_objection(this, "test done");
endtask

// debug
phase.phase_done.display_objections();

Q: extract_phase vs run_phase end — what happens between them?

diagram
[INT][SENIOR][UVM] MODEL ANSWER

Q: What happens after run_phase ends?

A:
  MECHANISM:  run_phase completes  extract_phase (function) collects final state 
              check_phase validates  report_phase summarizes  final_phase cleanup.
  MOTIVATION:  Separates timed activity from zero-time post-run bookkeeping — scoreboard
              final compare, coverage report, objection audit happen in cleanup.
  WHEN:       extract for scoreboard drain summaries; check for fatal-on-error policy;
              report for uvm_report_summary and test status.
  PITFALL:    Starting new stimulus in extract_phase — function phase, no time advance,
              and stimulus should have completed in run/main phase.
  EXAMPLE:    extract_phase: scoreboard.report_unmatched(); check_phase: fatal if
              pending count > 0; report_phase: print coverage merge directive.

Key takeaways

  • drain_time should match protocol trailing latency — tune per project.

  • Centralize objection policy in base_test for regression consistency.

  • Cleanup phases (extract/check/report) are function phases — no new stimulus.

Common pitfalls

  • Multiple competing objection owners without documented policy.

  • Treating extract_phase as a second run_phase for late stimulus.