Skip to content
DenseChip
Learn
PracticeCode labInterview

DenseChip

Navigate

Menu

LearnPracticeCode labInterview

Tools

All toolsSV editorCode repoResume
DenseChip

Practice, courses, and chip-design tools in one focused workspace.

LearnPracticeCode labInterviewToolsLearning paths

© 2026 DenseChip

AboutContactPrivacyTerms
Skip to lesson

Course contents

  • Start Here4
  • UVM Foundations14
  • UVM Phases & Simulation Lifecycle26
    • The Phase System
    • UVM Phases: Build, Connect, Elaborate and Run
      • Why build_phase Is Top-Down
      • config_db Gets and Conditional Construction
      • Connect Phase: Bottom-Up TLM Wiring
      • Elaboration, Start and the Pre-Time-Zero Contract
      • The Run Phase: Parallel Execution and Duration
      • Runtime Sub-Phases: Reset, Configure, Main and Shutdown
      • What super.build_phase() Really Does
      • run_test, Timeouts and How a Simulation Ends
    • Objections Deep Dive: Drain Time and Propagation
    • Phase Debugging and End-of-Test Signoff
  • Factory & Configuration15
  • Transaction-Level Modeling (TLM)13
  • UVM Sequences & Stimulus21
  • UVM Agents & Protocol VIP21
  • Environment & Test Classes11
  • Checking & Coverage in UVM21
  • UVM Register Abstraction Layer (RAL)22
  • Advanced UVM in Practice13
  • Debug & Regression10
  • Coding Practice47
  • Verification in Industry Practice10
  • Senior Prep: Architecture, Leadership and Interviews32
Loading lesson

‹UVM Phases: Build, Connect, Elaborate and Run
  1. Topics
  2. UVM Verification
  3. UVM Phases & Simulation Lifecycle
  4. UVM Phases: Build, Connect, Elaborate and Run
  5. Why build_phase Is Top-Down

Part 2/Phases & LifecycleIntermediate

Why build_phase Is Top-Down

create() constructs, the phaser builds later: what that means for factory overrides, measured on the DMA bench.

On this page

    Every UVM course teaches the rule "register factory overrides before super.build_phase". The DMA bench follows it: dma_truncate_test overrides the stream driver on the line above super.build_phase(phase). In an interview, you are asked: "and if the override came after super, would the test still work?" Most candidates say no. For this test, the answer is yes, and the reason is the most useful thing to understand about build.

    The idea: signing the offer is not the first day of work

    A manager who hires someone signs an offer letter today. The new hire's first day, when they set up their desk and hire their team, comes later, after the manager has finished their own work. In UVM, type_id::create() is the offer letter: it asks the factory (the registry that maps a requested type to the type actually built) for an object and runs its constructor. The child's build_phase, where it creates its children, is the first day, and the phaser schedules it later in the top-down walk.

    A factory override changes what future create() calls return. It never changes an object that already exists. So the only question about an override's placement is: has the create I want to change already happened? Because each level's children are created in the next level's build, a test's build_phase runs before almost every create in the tree.

    The picture: the truncate test's build, step by step

    Sequence diagram: the test registers an override and creates env; later the phaser builds env, which creates in_agt; later still in_agt creates the driver, and the factory returns the truncating driver.
    The phaser runs the test's build_phase, then env's, then in_agt's. The driver is created in in_agt's build, long after the test registered the override, whichever side of super the override is on.

    Build it: the override in the truncate test

    design.sv
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    // snippet: from the course DMA bench
    // Factory override: the stream driver cuts each packet short, so the DMA must
    // stop early and report ERR. No sequence or env file changes.
    class dma_truncate_test extends dma_base_test;
      `uvm_component_utils(dma_truncate_test)
    
      function new(string name, uvm_component parent);
        super.new(name, parent);
      endfunction
    
      function void build_phase(uvm_phase phase);
        axis_driver::type_id::set_type_override(axis_truncating_driver::get_type());
        super.build_phase(phase);                // the env is created after the override
      endfunction
    
      task run_phase(uvm_phase phase);
        dma_job_cfg job = dma_job_cfg::type_id::create("job");
        bit [31:0] status;
        phase.raise_objection(this);
        if (!job.randomize() with { len == 8; }) `uvm_fatal("RAND", "dma_job_cfg randomize failed")
        run_job(job, 8, status);
        if (status[2] !== 1'b1) `uvm_error("STATUS", $sformatf("ERR not set after a short packet: STATUS=%08h", status))
        else `uvm_info("TRUNC", "DMA reported ERR for the short packet, as expected", UVM_LOW)
        #100ns;
        phase.drop_objection(this);
      endtask
    endclass

    super.build_phase(phase) runs dma_base_test::build_phase, which creates env. That is the only create that happens inside the test's own build. The driver is created two levels further down, in in_agt's build, which the phaser runs after the test's build has returned. So the override reaches the driver from either position. The bench's comment, "the env is created after the override", names the one create that would be affected by the order.

    Where the override is registered

    Affects

    Too late for

    Test build_phase, before super.build_phase

    Every create in the tree, including env

    Nothing

    Test build_phase, after super.build_phase

    Everything below env: agents, drivers, scoreboard

    env itself, created inside super

    Env build_phase, after it created in_agt

    The agent's children (built later)

    in_agt itself

    connect_phase or later

    Nothing in this tree

    Everything: build is over

    Test hierarchies add one twist. When dma_truncate_test calls super.build_phase, it runs dma_base_test's build, which creates the env. If a mid-level test sat between them and registered an override after its own super call, that override would arrive after the env was created, exactly like the second row. Each level's "after super" is after every create its ancestors make.

    "Before super" is still the right habit, because it is never wrong and it keeps working when a base test starts creating more things. But knowing why lets you read a test and say precisely what an override can reach.

    Instance overrides follow the same rule

    A type override replaces a type everywhere it is created. An instance override replaces it only at one path, which is usually what a scenario test wants: truncate the input stream, leave every other stream driver alone. The timing rule is identical, because both are consulted at create() time. Only the path needs care:

    design.sv
    1
    2
    3
    4
    // snippet: inside dma_truncate_test::build_phase
    // Component method: the path is relative to this component (uvm_test_top).
    set_inst_override_by_type("env.in_agt.drv",
                              axis_driver::get_type(), axis_truncating_driver::get_type());

    The component method set_inst_override_by_type takes a path relative to the component that calls it, so "env.in_agt.drv" from the test means uvm_test_top.env.in_agt.drv. The factory's own method of the same name takes a full path from uvm_test_top. Mixing the two forms is the most common reason an instance override silently matches nothing.

    create, not new

    Calling new() on a component is legal SystemVerilog; component constructors are public. It simply skips the factory, so no override can ever replace that instance. type_id::create(name, parent) asks the factory first. Every child in the bench is created that way, which is why the truncate test can swap the driver without editing the agent. The factory section covers create-time resolution in depth.

    Watch it run

    The truncate test's topology shows the substituted type, and its log shows the substituted behaviour:

    Diagram
    in_agt                 axis_agent              -     @264
            drv                  axis_truncating_driver  -     @636
    ...
    UVM_INFO dma_tb.sv(520) @ 275000: uvm_test_top.env.in_agt.drv [TRUNC] TLAST forced on beat 3
    UVM_INFO dma_tb.sv(761) @ 385000: uvm_test_top [TRUNC] DMA reported ERR for the short packet, as expected
    UVM_INFO dma_tb.sv(411) @ 485000: uvm_test_top.env.sb [SB] 4 words matched, 0 mismatched

    A copy of the test with the override moved below super.build_phase(phase) produces the same three lines, with the same axis_truncating_driver in the topology. That is the measured answer to the interview question.

    What breaks: the env override that arrived late

    A team derived dma_env2 from dma_env with an extra coverage collector, and wrote a test that extends the smoke test with super.build_phase(phase) first, then dma_env::type_id::set_type_override(dma_env2::get_type()), following the "super first" rule from the signatures lesson. Coverage stayed at zero. A log line added after the override told the story:

    Diagram
    UVM_INFO dma_tb.sv(...) @ 0: uvm_test_top [ENV] env type after build: dma_env
    ...
        env                      dma_env                 -     @242

    The env is the one component created inside super.build_phase. By the time the override was registered, env already existed as a dma_env, and an override never changes an existing object. Moving the override above super.build_phase fixed it. The general rule: an override must be registered before the create it targets, and in a test the creates that happen inside super are the ones at risk.

    "It compiles and the topology prints" proves nothing about an override. Always check the type column of the topology (or print the factory with uvm_factory::get().print()) for the component you meant to replace.

    "Must a factory override in a test come before super.build_phase()?"

    A weak answer: "Yes, otherwise the components are already built with the original types." That is true for one component and false for the rest.

    A strong answer: "It must come before the create it targets. Inside a test, super.build_phase usually creates only the env; everything below is created in later build_phase calls that the phaser runs after the test's build returns, because build is top-down. So an override of the env must precede super; an override of a driver works either way. I still put overrides first, because it's never wrong, and I check the type in the topology print."

    Follow-ups: Where is an override too late for everything? (connect_phase or later) and What does `new()` on a component do to overrides? (bypasses the factory; legal, but nothing can replace that instance).

    Check yourself A test overrides dma_scoreboard with dma_scoreboard_eot on the line after super.build_phase(phase). Does the env get the new scoreboard?

    Yes. sb is created in the env's build_phase, which the phaser runs after the test's build_phase returns. Only env itself is created inside the test's super call, so only an override of the env type would be too late.

    Check yourself In dma_env::build_phase, the line in_agt = axis_agent::type_id::create(...) is followed by an override of axis_driver. Which drivers are affected?

    in_agt's driver. Each agent creates its driver in its own build_phase, which runs after the env's build returns, so the override is already in place when in_agt creates its driver, even though in_agt itself was created before the override. out_agt is passive and builds no driver, and the APB agent creates an apb_driver, which this override does not mention.

    Check yourself Why does env.in_agt read as null on the line right after the test creates env?

    Because creating env only ran env's constructor. env's build_phase, which creates in_agt, runs later, after the test's build_phase returns. Top-down means the parent's whole build finishes before any child's build begins.

    Next: the other thing a parent hands down in build is configuration. config_db Gets and Conditional Construction shows how the DMA bench passes its interfaces and its passive setting, and the wildcard that catches everyone once.

    Related topics

    • config_db Gets and Conditional Construction

    • Traversal Order Rules

    • Factory overview

    Previous

    25 / 280

    Next: config_db Gets and Conditional Construction

    Up next

    Up nextconfig_db Gets and Conditional Construction

    Sign in to save progress

    Practice this lessonReport an error

    See also

    • Connect Phase: Bottom-Up TLM Wiring
    • Elaboration, Start and the Pre-Time-Zero Contract
    • The Run Phase: Parallel Execution and Duration