Skip to content

UVM

Phase Ordering

How UVM sequences the whole schedule — the fixed phase order, top-down vs bottom-up traversal, the per-phase barrier across the tree, and run-time sub-phase synchronization.

UVM Phasing · Module 5 · Page 5.11

The Engineering Problem

You've now seen every phase on its own. This capstone steps back to the ordering — how the framework sequences the whole schedule across a whole tree of components, and the guarantees that ordering provides. Because the value of phasing is not any single phase; it's the coordinated order in which the framework runs all of them on all components, so that independently-written components compose into one synchronised environment.

That ordering rests on three rules worth stating precisely, plus one subtlety. (1) Sequence: the phases run in a fixed order (build → connect → … → final). (2) Direction: within a phase, the tree is traversed top-down (build) or bottom-up (connect, cleanup). (3) Barrier: each phase completes for the entire tree before the next phase begins — a phase-wide synchronisation point. The subtlety is the run phase: it's concurrent (all components run in parallel, Module 5.6), but the run-time sub-phases (reset/configure/main/shutdown) impose their own barriers, synchronising all components through the run. Getting the ordering model right — especially that run_phase and the run-time sub-phases run in parallel over the same time — is what lets you reason about when anything happens. This chapter consolidates phasing into one ordered picture.

How does UVM order the whole phase schedule across the component tree — the fixed sequence, the top-down/bottom-up directions, and the per-phase barriers — and how do the run-time sub-phases synchronise components during the otherwise-concurrent run?

Motivation — why the ordering rules matter

The three ordering rules (plus the run-phase model) are what make a multi-component environment work and reason-about-able:

  • The sequence gives every activity a definite "when." Because the phases always run in the same order, you know build happens before connect, connect before run, run before report. This fixed sequence is what lets you place each activity in a phase and trust when it runs relative to everything else.
  • The direction encodes dependency. Build is top-down because a parent creates its children; connect and cleanup are bottom-up because children must be wired/finished before parents act across them. The traversal direction isn't arbitrary — it follows what each phase needs, which is why a parent can configure children in build and aggregate them in report.
  • The barrier synchronises independent components. Each phase completing across the whole tree before the next begins means all components are always "in the same phase" — every component is built before any connects, every component connects before any runs. This barrier is what lets independently-written components compose: none races ahead into a phase others haven't reached.
  • The run-phase model prevents timing bugs. The run is concurrent (no ordering between components), but the run-time sub-phases add barriers (all components finish reset before any configures). Knowing that run_phase runs in parallel with these sub-phases — not before or after them — is what prevents the subtle ordering bugs of mixing them.

The motivation, in one line: phasing's power is the coordinated order — a fixed sequence, dependency-driven directions, and per-phase barriers — that synchronises a whole tree of independent components, with the run phase's concurrency tamed by the run-time sub-phase barriers.

Mental Model

Hold phase ordering as a synchronised group tour with a fixed itinerary:

Phase ordering is a group tour where everyone moves through the itinerary together, stop by stop. The itinerary is fixed (the phase sequence) — every tour does the same stops in the same order. At each stop, there's a direction people move (top-down or bottom-up — board the bus front-to-back, exit back-to-front). And critically, there's a barrier at every stop: the group doesn't leave a stop until everyone has finished it — nobody races ahead to the next stop while others are still at this one. So at any moment, the whole group is "at the same stop" (all components in the same phase). The one exception is the main activity (the run phase): there, everyone does their own thing simultaneously (concurrent), but even that has sub-stops (reset, then configure, then the main event, then wind-down) with their own barriers — everyone finishes resetting before anyone configures. The tour guide (the framework) enforces the itinerary, the direction, and the barriers, so the group stays coordinated from start to finish.

So to reason about when anything happens, ask: which stop (phase) is it in? — the fixed itinerary tells you the order. Which direction does that stop run? — top-down or bottom-up. And trust the barrier: everyone finishes a phase before anyone starts the next. The run is the simultaneous activity, sub-divided by its own barriers.

Visual Explanation — the three ordering rules

Phase ordering is three rules working together: a fixed sequence of phases, a direction of traversal within each, and a barrier between them. Together they coordinate the whole tree.

Phase ordering rules: fixed sequence, top-down/bottom-up direction, per-phase barrier, run-time sub-phase synchronizationSequence + direction + barrierSequence + direction + barrierSequence — fixed phase orderbuild → connect → end_of_elaboration → start_of_simulation → run → extract → check → report → finalbuild → connect → end_of_elaboration → start_of_simulation → run → extract → check → report → finalDirection — top-down or bottom-upbuild is top-down (parents create children); connect + cleanup are bottom-up (children first)build is top-down (parents create children); connect + cleanup are bottom-up (children first)Barrier — whole tree per phaseeach phase completes for every component before the next begins: all components stay in the same phaseeach phase completes for every component before the next begins: all components stay in the same phaseRun — concurrent, with sub-phase barriersrun_phase forks all components in parallel; run-time sub-phases (reset/configure/main/shutdown) synchronise themrun_phase forks all components in parallel; run-time sub-phases (reset/configure/main/shutdown) synchronise them
Figure 1 — the three phase-ordering rules. Sequence: phases run in a fixed order (build → connect → … → final). Direction: within a phase, the tree is traversed top-down (build) or bottom-up (connect, cleanup). Barrier: each phase completes across the entire tree before the next begins, so all components are synchronised in the same phase. The run phase is concurrent, with the run-time sub-phases providing their own barriers. These rules make a tree of independent components run in coordinated order.

These three rules, plus the run model, are the whole of phase ordering. Sequence fixes the order of the nine common phases, so build always precedes connect, run always precedes report. Direction sets how the tree is walked within a phase — top-down for build (a parent must exist to create its child), bottom-up for connect and the cleanup phases (children wire and finish before parents act across them). Barrier is the synchronisation: the framework completes a phase for the entire tree before starting the next, so no component is ever "ahead" — every build finishes before any connect starts. And the run is the exception to the simple per-component picture: it's concurrent (all run tasks in parallel), but its sub-phases (reset → configure → main → shutdown) are themselves barriers. Master these and you can answer "when does X happen?" for anything in a UVM environment.

RTL / Simulation Perspective — the ordering in one walkthrough

A small tree makes the ordering concrete: the framework runs each phase across all components (in that phase's direction), completing it everywhere before advancing to the next phase.

phase ordering across a tree — sequence, direction, barrier
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// Tree:  test → env → agent → (driver, monitor)
//
// The framework runs phases in SEQUENCE, each completing across the WHOLE tree (BARRIER)
// before the next, in each phase's DIRECTION:
//
// build_phase    (TOP-DOWN):    test → env → agent → driver, monitor   // parents create children
//   << barrier: every build_phase done before any connect_phase >>
// connect_phase  (BOTTOM-UP):   driver, monitor → agent → env → test   // children wired first
//   << barrier >>
// end_of_elaboration, start_of_simulation  (BOTTOM-UP)   // validate, announce
//   << barrier >>
// run_phase      (CONCURRENT):  driver ∥ monitor ∥ ... all run in parallel
//                run-time sub-phases (reset→configure→main→shutdown) synchronise them
//   << barrier: run fully ends before extract >>
// extract_phase  (BOTTOM-UP):   driver, monitor → agent → env → test   // gather, bottom-up
// check_phase, report_phase, final_phase  (BOTTOM-UP)    // judge, announce, teardown

This walkthrough is the three rules in action. Sequence: the comment lists the phases in their fixed order, top to bottom. Direction: each phase notes its traversal — build_phase top-down (test → env → agent → leaves, because parents create children), connect_phase and the cleanup phases bottom-up (leaves → … → test, because children act first). Barrier: the << barrier >> markers show that each phase completes for every component before the next phase starts anywhere — so the driver's connect_phase never runs before the env's build_phase finished; all builds complete first. And the run is marked concurrent: the driver and monitor run tasks execute in parallel (Module 5.6), with the run-time sub-phases synchronising them. Reading any UVM environment's timing is reading this structure: what phase, what direction, and the barrier guaranteeing all components are in step.

Verification Perspective — the barrier synchronises independent components

The barrier rule — each phase completing across the whole tree before the next begins — is what makes independently-written components compose. It guarantees that when a component runs a phase, every component is in that same phase, so cross-component assumptions about phase progress hold.

All components complete build_phase, then a barrier, then all start connect_phase: no component races aheadall builds donethen all connectbuild_phase: ALL componentstest, env, agent, driver, monitorBARRIERnext phase waits for everycomponentconnect_phase: ALL componentsonly after every build completed12
Figure 2 — the phase barrier synchronises the tree. The framework runs a phase (say build_phase) on every component, and only when all of them have completed it does it start the next phase (connect_phase) on any component. So there's a synchronisation barrier between phases: no component races ahead. This is why connect can rely on every component being built (all builds finished at the barrier), and why cleanup phases can aggregate across components (all are in the same cleanup phase). Independent components stay in lockstep.

The barrier is the synchronisation that makes phasing reliable across many components. Without it, components written by different people could be at different points — one still building while another tries to connect — and nothing would compose. With it, the framework guarantees that all components complete a phase before any starts the next, so every component is always "in the same phase" as every other. This is precisely why each phase's contract holds: connect_phase can assume every component is built (the build barrier guaranteed it, Module 5.3); check_phase can aggregate across components (all are in the cleanup phases together); a parent's report can summarise children (the bottom-up direction plus the barrier ensure children reported first). The barrier is the synchronisation half of phase ordering — sequence and direction say what order, and the barrier says all together — and it's what lets independently-authored components run as one coordinated environment.

Runtime / Execution Flow — the run-time sub-phases synchronise the concurrent run

The run is the one concurrent phase, but the run-time sub-phases (reset → configure → main → shutdown, each with pre_/post_) impose barriers within the run — synchronising all components through a structured sequence — while run_phase itself runs in parallel with them.

run_phase runs concurrently while run-time sub-phases reset, configure, main, shutdown synchronise all components with barriersrun_phase (concurrent) ∥ run-time sub-phases (barriers)run_phase (concurrent) ∥ run-time sub-phases (barriers)1reset (barrier)all components complete reset_phase before any advances —synchronised reset across the tree.2configure (barrier)all complete configure_phase before any starts main — synchronisedconfiguration.3main (barrier)all run main_phase; the barrier holds until every component's mainis done.4shutdown (barrier)synchronised wind-down; run_phase runs in PARALLEL with all ofthese over the same time.
Figure 3 — the run-time sub-phases as barriers within the concurrent run. run_phase forks all components and runs them in parallel. In parallel with it, the run-time sub-phases run in sequence with barriers: all components complete reset (objections dropped) before any starts configure, all complete configure before main, and so on. So the sub-phases synchronise the components through reset→configure→main→shutdown, even though run_phase is concurrent. Use the sub-phases when you need this cross-component run-time ordering; run_phase runs alongside them.

The run-time sub-phases are how UVM adds ordering within the concurrent run. run_phase itself is concurrent and unsynchronised (Module 5.6) — all components' run tasks fork together with no ordering. But the twelve run-time sub-phases (reset, configure, main, shutdown, each with pre_ and post_) run in sequence with barriers: the framework holds each sub-phase open until every component has completed it (its objections dropped), so all components finish reset_phase before any begins configure_phase, all configure before any does main_phase, and so on. This gives you a synchronised reset→configure→main→shutdown sequence across the whole tree, for environments that need it (coordinated bring-up). The crucial subtlety: run_phase and the run-time sub-phases run in parallel, over the same simulation time — the sub-phases are not before or after run_phase, they overlap it. So a component using run_phase is not synchronised to the sub-phase sequence (it runs concurrently with all of it), which is exactly the mixing bug in the DebugLab. Use the sub-phases for cross-component run-time ordering; use run_phase for unsynchronised behaviour; don't expect ordering between the two.

Waveform Perspective — the sub-phases synchronise; run_phase runs alongside

On a timeline, the run-time sub-phases advance in a synchronised sequence (reset → configure → main → shutdown) while run_phase runs in parallel across the whole span — the concurrency and the sub-phase barriers visible together.

Run-time sub-phases advance in sequence (barriers); run_phase runs in parallel

12 cycles
Run-time sub-phases advance in sequence (barriers); run_phase runs in parallelreset sub-phase (barrier): all components finish reset before any configuresreset sub-phase (barri…main sub-phase: stimulus flows; run_phase (run_active) runs in PARALLEL with the sub-phasesmain sub-phase: stimul…shutdown: synchronised wind-down — the sub-phases order the run, run_phase overlaps themshutdown: synchronised…clksub_phaseRSTRSTCFGCFGMAINMAINMAINMAINSHUTSHUT------run_activevalidt0t1t2t3t4t5t6t7t8t9t10t11
Figure 4 — during the run, the run-time sub-phases advance through reset → configure → main → shutdown as synchronised barriers (the sub_phase track): all components finish each before any advances. In parallel, over the same time, run_phase runs concurrently (run_active high throughout) and stimulus flows during main. The sub-phases impose cross-component ordering; run_phase overlaps them all. A component using run_phase is not synchronised to the sub-phase sequence — they run alongside each other.

The sub_phase track advancing RST → CFG → MAIN → SHUT shows the run-time sub-phases as synchronised barriers — each holds until every component completes it, so the whole tree moves through bring-up in step. Meanwhile run_active is high across the entire span: run_phase runs concurrently with all the sub-phases, over the same time, not before or after them. That overlap is the key insight the waveform makes visible — the sub-phases and run_phase are parallel time-consuming activities, and a component using one is not ordered relative to a component using the other. The valid activity during MAIN shows stimulus flowing in the main sub-phase. This single trace captures the run's dual nature: concurrent (run_phase, all components in parallel) yet optionally synchronised (the sub-phase barriers, for coordinated cross-component ordering) — both running over the same simulation time.

DebugLab — mixing run_phase and the run-time sub-phases

A reset that didn't precede stimulus — one component used run_phase, another used main_phase

Symptom

An environment used the run-time sub-phases for ordering: a reset driver ran in reset_phase, and stimulus ran in main_phase, expecting reset to fully complete before stimulus began. But intermittently, stimulus appeared during reset — the ordering the sub-phases were supposed to guarantee didn't hold for one component. The reset component and the stimulus were sometimes overlapping when they should have been strictly sequenced.

Root cause

The stimulus component used run_phase, not main_phase — and run_phase runs in parallel with the run-time sub-phases, so it was not synchronised to the reset→main sequence:

why the run_phase component ignored the sub-phase ordering
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
reset driver:     reset_phase  (a run-time SUB-PHASE — synchronised barrier)
stimulus comp:    run_phase    (the MAIN run phase — runs in PARALLEL with ALL sub-phases)
expectation:      reset (sub-phase) completes before main → stimulus waits for reset
reality:          run_phase runs concurrently with reset/configure/main/shutdown — NOT after reset
result:           the run_phase stimulus started immediately, overlapping reset_phase → race
fix:              use the SAME mechanism for ordered components — put stimulus in main_phase
                  (a sub-phase), so the reset→main barrier synchronises it; OR sequence both in run_phase

The sub-phases synchronise components that use them — but run_phase is not one of the sub-phases; it runs in parallel with the whole reset→configure→main→shutdown sequence. So a component in run_phase is not ordered relative to components in the sub-phases: it starts when the run begins, concurrently with reset_phase, ignoring the barrier that would have made it wait for reset. The reset ordering held among the sub-phased components but not for the run_phase one — mixing the two mechanisms broke the expected sequence.

Diagnosis

The tell is sub-phase ordering that holds for some components but not others — a mix of run_phase and run-time sub-phases. Diagnose run-time ordering bugs:

  1. Check whether ordered components use the same mechanism. Components meant to be sequenced (reset before main) must all use the run-time sub-phases; one in run_phase runs in parallel with the sub-phases and ignores their barriers.
  2. Remember run_phase overlaps the sub-phases. run_phase is not before or after the sub-phases — it runs concurrently with the entire reset→configure→main→shutdown sequence. A run_phase component is unsynchronised to that sequence.
  3. Look for intermittent overlap. Stimulus appearing during reset (when it should follow it) intermittently signals a run_phase component racing the sub-phased reset — a mixing bug.
Prevention

Use one run-time mechanism consistently for components that must be ordered:

  1. Don't mix run_phase and the sub-phases for ordered behaviour. Components that must be sequenced (reset → configure → main) should all use the run-time sub-phases, so the sub-phase barriers synchronise them. Mixing in a run_phase component breaks the ordering.
  2. Use the sub-phases when you need cross-component run-time ordering. Put reset behaviour in reset_phase, stimulus in main_phase, etc., so the barriers enforce reset-before-main across the tree.
  3. Use run_phase alone when you don't need that ordering. If components don't need the reset/configure/main/shutdown structure, use run_phase for all of them and synchronise explicitly where needed — just don't expect run_phase to be ordered relative to the sub-phases.

The one-sentence lesson: run_phase runs in parallel with the run-time sub-phases (reset/configure/main/shutdown), not before or after them — so components that must be ordered must all use the sub-phases (or all use run_phase); mixing them lets a run_phase component race the sub-phase sequence.

Common Mistakes

  • Mixing run_phase and the run-time sub-phases for ordered components. run_phase runs in parallel with the sub-phases, so a run_phase component isn't synchronised to reset→configure→main→shutdown. Use one mechanism consistently for components that must be sequenced.
  • Assuming run_phase ordering between components. The run is concurrent — no ordering between components' run_phase tasks (Module 5.6). For cross-component run-time ordering, use the sub-phase barriers, not assumptions.
  • Expecting build to be bottom-up (or connect/cleanup top-down). Build is top-down (parents create children); connect and the cleanup phases are bottom-up. Wrong-direction assumptions cause null references or premature aggregation.
  • Forgetting the per-phase barrier. Each phase completes across the whole tree before the next begins — no component races ahead. Code that assumes a component is in a later phase than the barrier allows is wrong.
  • Thinking the sub-phases run before or after run_phase. They run in parallel with it, over the same time — not sequentially around it. The sub-phases overlap run_phase.
  • Over-using the sub-phases. For simple environments that don't need coordinated bring-up, run_phase alone is fine; the twelve sub-phases add structure you only need when cross-component run-time ordering matters.

Senior Design Review Notes

Interview Insights

By three rules. First, sequence: the phases always run in a fixed order — build, connect, end_of_elaboration, start_of_simulation, run, extract, check, report, final. Second, direction: within each phase, the framework traverses the component tree in a defined direction — top-down for build (a parent must exist to create its children) and bottom-up for connect and the cleanup phases (children act before parents aggregate across them). Third, barrier: each phase completes for the entire tree before the next phase begins anywhere — a synchronisation point, so every component finishes build before any starts connect, and so on. These three together mean all components stay "in the same phase" as each other, which is what lets independently-written components compose into one coordinated environment. The one exception to the simple per-component picture is the run phase, which is concurrent — all components' run tasks fork and run in parallel — but even there, the run-time sub-phases (reset, configure, main, shutdown) impose their own barriers to synchronise components through a structured bring-up sequence, while run_phase itself runs in parallel with those sub-phases.

Exercises

  1. State the rules. Write the three phase-ordering rules (sequence, direction, barrier), and for direction, state which phases are top-down and which bottom-up.
  2. Trace a tree. For test → env → agent → (driver, monitor), give the order components run build_phase and connect_phase, noting the direction and the barrier between them.
  3. Diagnose the overlap. Stimulus in main_phase sometimes overlaps reset in reset_phase for one component. State the cause and the fix in terms of run_phase vs sub-phases.
  4. Parallel or sequential? State whether each pair runs sequentially or in parallel: (a) build_phase and connect_phase; (b) run_phase and main_phase; (c) reset_phase and configure_phase; (d) two components' run_phase tasks.

Summary

  • Phase ordering rests on three rules: sequence (phases run in a fixed order, build → … → final), direction (top-down for build — parents create children; bottom-up for connect and cleanup — children act first), and barrier (each phase completes across the whole tree before the next begins, synchronising all components).
  • The barrier is what makes independently-written components compose: every component is always "in the same phase," so each phase's contract holds (build done before connect, children reported before parents aggregate).
  • The run phase is concurrent (all components' run tasks in parallel, no ordering), but the run-time sub-phases (reset → configure → main → shutdown, with pre_/post_) are barriers that synchronise components through a structured bring-up — and they run in parallel with run_phase, over the same time.
  • The key subtlety: run_phase overlaps the run-time sub-phases, not before or after them — so a run_phase component is not ordered relative to sub-phased components. Mixing the two for behaviour that must be sequenced lets a run_phase component race the sub-phase barriers.
  • The durable rule of thumb: read phasing as sequence + direction + barrier — fixed order, dependency-driven traversal, all-components-in-step — and for run-time ordering use the sub-phase barriers consistently, remembering run_phase runs in parallel with them, not in sequence.

Next — Phase Debugging: you've seen the whole schedule and its ordering — and met a phase bug in nearly every chapter. The next chapter is the practical counterpart: how to debug phasing problems with +UVM_PHASE_TRACE, +UVM_OBJECTION_TRACE, and print_topology, and the symptom-to-phase signatures that localize them.