Part 2 · Phases & Lifecycle · Intermediate

Parallel vs Serial: What Runs Together During Run-Time

How function phases run serially tree-wide, how run_phase and runtime sub-phases run in parallel, and what synchronization exists between them.

Serial function phases

All function phases are globally serial : every component finishes build before any component begins connect. The scheduler does not advance until the current function phase completes tree-wide.

diagram
[UVM][PHASE] serial strips

build (all nodes) -> barrier -> connect (all nodes) -> barrier -> ...

each barrier = zero simulated time
entire tree must finish phase N before phase N+1
  • Serial execution is why connect never sees unbuilt children.

  • Serial cleanup phases aggregate child results before parents.

  • There is no parallel build_phase across unrelated branches.

Key takeaways

  • Function phases are synchronous barriers across the whole testbench.

  • Cross-phase serial order is the core UVM ordering guarantee.

  • Parallelism begins when run-time task phases start.

Common pitfalls

  • Expecting parallel build across agents for performance — not supported.

  • Long-running computations in report_phase blocking others — keep it short.

  • Assuming extract on env runs while agent run_phase still active — it does not.


Parallel run-time

When run_phase begins, the scheduler forks every component's run_phase and the

12 runtime sub-phase tasks. They all advance simulation time concurrently.

diagram
[PHASE][RUN] parallel lanes

Lane 1: uvm_test_top.run_phase
Lane 2: env.run_phase
Lane 3: agent.run_phase
Lane 4: driver.run_phase
...
Lane N: reset_phase / configure_phase / main_phase / ...

scheduler advances time until:
  all tasks complete their sync points AND objections resolve
systemverilog
// These run concurrently after build-time barriers complete
task run_phase(uvm_phase phase);
  super.run_phase(phase);
  fork
    driver_loop();
    monitor_loop();
  join
endtask

task main_phase(uvm_phase phase);
  // parallel with run_phase above
  seq.start(sqr);
endtask

Implications for your code

  1. Do not assume your run_phase finishes before env.run_phase.

  2. Use runtime sub-phases for reset/configure ordering inside parallel run.

  3. Use TLM and objections for cross-component run-time sync.

  4. Avoid busy-wait race fixes with ad-hoc #delays.

Key takeaways

  • Run-time is intentionally parallel — embrace objections and sub-phases.

  • main_phase is not 'after' run_phase — it overlaps.

  • Cross-component ordering at run-time needs explicit protocol, not phase serialism.

Common pitfalls

  • Sequencing reset in run_phase while stimulus starts in main_phase without coordination.

  • Assuming scoreboard idle because run_phase returned — check objections.

  • Using global variables as implicit barriers between parallel tasks.