How UVM Phasing is triggered?

In short: UVM phasing is triggered by run_test(), the single call in your top module. run_test() builds the test component, then a phase controller walks every component through the phases in order, calling build_phase, connect_phase, run_phase, and the rest. You do not call the phases yourself; you only override the ones you need, and UVM invokes them for the whole component tree automatically.

It is easy to write UVM phase methods for months without ever asking who calls them. The answer is a small chain that starts from one line in your top module and ends with your build_phase and run_phase running in a guaranteed order across every component. This guide traces that chain end to end: run_test(), the phase controller, how the root component starts the schedule, how each phase is executed top down or bottom up, and where the command line picks the test. Every step has runnable code and a table, and we use one small top module as the running example.

A simple way to picture it

Think of the opening night of a theatre show. One person, the stage manager, calls the whole evening: houselights down, curtain up, act one, interval, act two, curtain down. Nobody on stage decides when the curtain rises; they just perform their part when the stage manager calls it. run_test() is that first cue from the front of house, and the UVM phase controller is the stage manager who then calls every phase in turn for every performer. Your components only need to know their lines (their phase methods); the calling order is handled for them.

It starts with run_test()

Everything begins in the top module. You call run_test() once inside an initial block, usually after generating the clock and setting the virtual interface into the config database. That single call is the trigger for the entire phasing sequence.

module top;
  bit clk;
  always #5 clk = ~clk;

  bus_if vif(clk);              // the interface to the design
  dut    u_dut(vif);           // the design under test

  initial begin
    // hand the interface to the testbench
    uvm_config_db#(virtual bus_if)::set(null, "*", "vif", vif);
    run_test();                // <== this triggers all phasing
  end
endmodule

run_test() does three things in order: it works out which test class to build, it asks the factory to create that test as the root of the component tree, and it hands control to the phase controller that walks the phases. From here on, you never call a phase yourself.

Choosing the test: argument or command line

run_test() picks the test class in one of two ways, and the command-line route is why the same compiled testbench can run many different tests.

Way to choose the testHowWhen to use
Argument to run_testrun_test(“my_test”);Quick bring-up or a fixed single test
Command-line plusarg+UVM_TESTNAME=my_testRegressions: pick the test without recompiling

The plusarg wins if both are present. In practice you leave run_test() empty and always pass +UVM_TESTNAME, so one build runs your whole suite by changing only the command line.

What run_test() sets in motion

Once the test is created, run_test() hands the root of the tree to the phasing machinery. The order of events is worth seeing as a list, because it explains why your build_phase runs before any stimulus.

  1. run_test() reads +UVM_TESTNAME (or its argument) to find the test class name.
  2. The factory creates that test as uvm_test_top, the root of the component tree.
  3. The phase controller starts the common phases, beginning with build_phase, top down.
  4. As build_phase runs, each component creates its children, so the tree grows downward.
  5. connect_phase and the other function phases run in their fixed order.
  6. run_phase and its twelve sub-phases run, consuming simulation time.
  7. The cleanup phases run, then run_test() returns and the simulation ends.

You override, UVM calls

The key mental model is that phases are callbacks. You never invoke build_phase or run_phase yourself; you define them, and the phase controller calls them for every component at the right moment. Because the base versions are virtual, your override is the one that runs.

class my_test extends uvm_test;
  `uvm_component_utils(my_test)
  bus_env env;
  function new(string n, uvm_component p); super.new(n,p); endfunction

  // UVM calls this for you, top down, right after run_test creates the test
  function void build_phase(uvm_phase phase);
    super.build_phase(phase);
    env = bus_env::type_id::create("env", this);
  endfunction

  // UVM calls this later, once everything is built and connected
  task run_phase(uvm_phase phase);
    my_seq seq = my_seq::type_id::create("seq");
    phase.raise_objection(this);
    seq.start(env.agt.sqr);
    phase.drop_objection(this);
  endtask
endclass

Notice you never wrote a line that says build_phase(); anywhere. Defining the method is enough. That inversion, where the framework calls your code rather than your code calling the framework, is what makes every UVM testbench start the same way.

Why the root matters

The test built by run_test() is called uvm_test_top, and it is the root of the whole component tree. Phasing walks that tree, so every component you create under the test, directly or indirectly, is automatically pulled into the same phase schedule. This is why you never register a component for phasing: being part of the tree under uvm_test_top is enough.

// the full name shows the tree that phasing walks
// uvm_test_top
//   env
//     agt
//       drv, sqr, mon
//     scb
// every one of these gets build_phase, connect_phase, run_phase, ... called

Function phases versus the run phase

The controller runs two kinds of phase differently, which is why some of your methods can wait on clocks and others cannot.

Phase kindCalled howCan consume time?
Function phases (build, connect, report)Called and returned in zero timeNo
run_phase and sub-phasesForked as tasks that run togetherYes

The controller runs every component run_phase in parallel and waits for all objections to drop before moving on. That is why run_phase is where real simulation time passes, while build_phase must finish instantly.

Common mistakes to avoid

  • Forgetting to call run_test() in the top module, so no phasing ever starts and nothing runs.
  • Calling a phase method directly, such as build_phase(). The controller already calls it; calling it yourself runs it twice.
  • Passing no test name and leaving run_test() empty, so UVM has no test to build and errors out.
  • Creating a component outside the uvm_test_top tree, so phasing never reaches it and its phases never run.
  • Expecting run_phase to run before build_phase. The order is fixed: everything is built and connected first.

Keep learning

This trigger is the front door to phasing, so read UVM phasing next for every phase in order and where your code goes. See how the run phase decides to end in UVM objections, and see the whole tree that phasing walks in the UVM testbench architecture guide. More topics are on the UVM category page, and this is a frequent interview question.

Frequently asked questions

Once you know how phases are triggered, see the mechanism used in practice for reset testing with a phase jump, where a run rewinds to an earlier phase on purpose.

What triggers UVM phasing?

The call to run_test() in your top module. It creates the test as the root component and hands control to the phase controller, which then calls build_phase, connect_phase, run_phase, and the rest for every component in order. Without run_test(), no phasing starts.

Do I ever call build_phase or run_phase myself?

No. You define these methods, and the phase controller calls them for you at the correct time for every component in the tree. Calling one yourself would run it twice, which usually causes duplicate components or other errors.

How does UVM know which test to run?

run_test() reads the +UVM_TESTNAME plusarg from the command line, or uses the name you passed as an argument. The plusarg takes priority, which lets one compiled testbench run many tests by changing only the command line.

What is uvm_test_top?

It is the name UVM gives the test component that run_test() creates. It is the root of the component tree, so phasing walks down from it, and every component built underneath is automatically included in the phase schedule.

Why does build_phase always run before run_phase?

Because the phase controller runs the function phases, including build and connect, before starting the run phase. Building first guarantees the whole component tree exists and is connected before any stimulus runs, which removes a whole class of ordering bugs.

Can I run several tests without recompiling?

Yes. Leave run_test() empty and select the test with +UVM_TESTNAME on the command line. Since the test name is read at run time, one build can run your entire regression suite by varying only that argument.

Similar Posts