In short: uvm phases are the ordered, callback-driven steps every UVM component executes so that a testbench is built, connected, and run in a predictable sequence across all components. The build/connect/end_of_elaboration phases construct and wire the hierarchy top-down, while the time-consuming run phase simulates the stimulus and uses objections to decide when the test ends.
In the Universal Verification Methodology, uvm phases are a fixed set of synchronization points that the simulator walks through for every component derived from uvm_component. Instead of each engineer inventing their own order for building objects, wiring ports, and launching stimulus, UVM defines a common schedule. The base class library calls virtual methods such as build_phase(), connect_phase(), and run_phase() on your components at exactly the right moment, so a monitor, driver, and scoreboard written by three different people still assemble and start in a coherent way.
Phases fall into two families. Function phases consume zero simulation time and execute top-down or bottom-up through the hierarchy. The run phase is a task that consumes simulation time and runs all components in parallel. This separation is what lets UVM guarantee that nothing starts driving pins before the entire environment has been built and connected.
UVM ships a pre-defined list of common phases, invoked in this order:
Because build_phase is top-down, the environment is created before its agents, and agents before their drivers. The direction flips for connect_phase so the leaves are ready to be wired into their parents.
build_phase runs top-down (parent before child) while connect_phase and every post-run cleanup phase run bottom-up (child before parent). Getting this direction wrong is the single most common cause of null-handle crashes in a new UVM environment.Without a shared schedule, verification components would race each other. A driver might try to get a transaction from a sequencer that has not been constructed yet, or a scoreboard's analysis export might be null when a monitor tries to write to it. Phasing removes those races by contract: every component is guaranteed to be fully built and connected before any of them begins driving stimulus.
Phasing also makes reuse possible. A verification IP block dropped into a new project behaves identically because the integrator cannot accidentally change when its internals come to life. The methodology, not the user, owns the ordering.
Only the run phase (and the optional run-time sub-phases) can consume simulation time, which raises the question: how does UVM know when to stop? The answer is objections. A component that needs the simulation to keep running calls phase.raise_objection(this) at the start of its activity and phase.drop_objection(this) when finished. The run phase continues as long as at least one objection is outstanding; when the count returns to zero, UVM ends the phase and advances to extract/check/report.
Typically a sequence or the top-level test raises an objection before starting stimulus and drops it after the last transaction drains. A set_drain_time() can hold the phase open briefly so trailing responses are captured. Forgetting to raise an objection makes a test finish at time zero; forgetting to drop one makes it hang until the timeout.
Alongside the single run_phase, UVM defines twelve finer-grained runtime phases that run in parallel schedules: reset, configure, main, and shutdown, each with pre_ and post_ variants. These let teams coordinate activity, for example keeping a reset driver in the reset phase while register configuration waits for the configure phase. In practice many teams use only run_phase for simplicity and reserve the sub-phases for complex, multi-interface environments. Engineers can also define custom phases and domains to synchronize independent clock or power domains.
Real projects and industry tool flows, with certification.
Explore CourseTron Courses →Only the run phase and its runtime sub-phases (reset, configure, main, shutdown and their pre_/post_ variants) are tasks that consume time. All other phases — build, connect, end_of_elaboration, start_of_simulation, extract, check, report, and final — are functions that execute in zero time.
Construction must flow from parent to child so a parent can create its children and pass configuration down before the child builds. Connection flows the other way because a parent can only bind to ports and exports that its children have already created, so the children must be fully built first.
The run phase ends immediately at time zero because UVM sees nothing keeping it alive. The simulation jumps straight to the extract/check/report phases, usually producing an empty or failing test. You raise an objection to hold the phase open and drop it when your stimulus is complete.
Yes. end_of_elaboration_phase is the correct place to inspect or lightly adjust the completed topology and print it, and start_of_simulation_phase is where you print initial state or banners just before the run phase begins. Neither should construct new components.
Related: UVM testbench architecture · the VLSI Glossary · all courses.