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. What super.build_phase() Really Does

Part 2 · Phases & Lifecycle · Intermediate

What super.build_phase() Really Does

The library's base phase methods: automatic configuration in build(), uvm_agent's is_active lookup, and the silent failure when super is skipped.

On this page

    Here is a test that passes: 8 words matched, 0 mismatched, UVM_ERROR : 0. Its topology shows something it should not: the output agent, configured as passive, has a driver and a sequencer sitting on a port the DUT drives. The only change from the smoke test is one missing line in the agent's build_phase: super.build_phase(phase).

    The idea: the base class does real work in build

    It is tempting to think of super.build_phase(phase) as politeness, since most library phase methods are empty. build_phase is the exception. Think of a new employee's first-day checklist that HR runs before the manager's own onboarding: badge, laptop, access rights. Skip the HR part and the manager's onboarding still runs, and the new hire still shows up, but nothing they need is switched on.

    Two things happen in the base calls of an agent's build:

    • uvm_component::build_phase calls the old build() hook, and build() calls apply_config_settings(). That is automatic configuration: every field registered with the field macros (uvm_field_int and friends) is filled from matching config_db settings. In IEEE 1800.2 it happens only when use_automatic_config() returns 1, which is the default.

    • uvm_agent::build_phase calls that, then looks up is_active for this agent in the configuration database. This lookup works whether or not your agent uses field macros.

    Skip super.build_phase in your agent and neither happens. Field-macro fields keep their default values, and is_active keeps its default, UVM_ACTIVE.

    The picture: the call chain behind one super call

    Flowchart: the APB agent's build calls uvm_agent's build, which calls uvm_component's build, which calls build() and, when automatic configuration is on, apply_config_settings; uvm_agent then looks up is_active; finally the agent creates its children based on get_is_active.
    What runs inside the APB agent's super.build_phase call. The is_active lookup and automatic configuration both happen there, before the agent creates its children.

    Note

    In UVM 1.2, build() calls apply_config_settings() unconditionally; there is no use_automatic_config() to override. In IEEE 1800.2, a component can override use_automatic_config() to return 0 and skip it, which saves time in large benches that do not use field macros.

    Build it: the bench agent that does it right

    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
    class apb_agent extends uvm_agent;
      `uvm_component_utils(apb_agent)
      apb_sequencer sqr;
      apb_driver    drv;
      apb_monitor   mon;
      uvm_analysis_port #(apb_item) ap;   // the agent's public output
    
      function new(string name, uvm_component parent);
        super.new(name, parent);
      endfunction
    
      function void build_phase(uvm_phase phase);
        super.build_phase(phase);         // reads is_active from config_db
        mon = apb_monitor::type_id::create("mon", this);
        if (get_is_active() == UVM_ACTIVE) begin
          sqr = apb_sequencer::type_id::create("sqr", this);
          drv = apb_driver::type_id::create("drv", this);
        end
      endfunction
    
      function void connect_phase(uvm_phase phase);
        ap = mon.ap;
        if (get_is_active() == UVM_ACTIVE)
          drv.seq_item_port.connect(sqr.seq_item_export);
      endfunction
    endclass

    The comment on super.build_phase(phase) says it plainly: that line reads is_active from config_db. Everything after it relies on that: get_is_active() returns the configured value only because the base build ran first. The env's uvm_config_db#(uvm_active_passive_enum)::set(this, "out_agt", "is_active", UVM_PASSIVE) is read in exactly this call for the out_agt instance.

    Automatic configuration is a larger topic with its own trade-offs (field macros, the cost of matching every field against the database, and why many teams switch it off). This lesson only needs the fact that it lives in build(). The full story is in automatic configuration in the Factory & Configuration section.

    is_active itself is an ordinary public field of uvm_agent, defaulting to UVM_ACTIVE, and get_is_active() simply returns it. The value is only as good as the lookup that filled it. The library treats the setting as if it were declared with a field macro, so a config_db set of type uvm_active_passive_enum, int or string is accepted; the bench uses the enum, which is the clearest.

    What the other base phase methods do

    build_phase is not the only base method with content. In the Accellera library:

    Class

    Method

    What the base does

    uvm_component

    build_phase

    Calls build(), which applies automatic configuration.

    uvm_agent

    build_phase

    Calls super, then looks up is_active.

    uvm_sequencer (its param base)

    build_phase, connect_phase

    Sets up and connects the sequencer's response FIFO behind rsp_export.

    uvm_driver

    end_of_elaboration_phase

    Warns DRVCONNECT if seq_item_port is unconnected.

    uvm_component

    connect_phase through report_phase

    Call the old no-argument hooks (connect(), run(), check()...), empty by default.

    uvm_component

    final_phase, the twelve runtime phases

    Nothing.

    So skipping super.connect_phase in a custom sequencer breaks responses, and skipping super.end_of_elaboration_phase in a driver loses a useful warning. The bench's agents and env skip super.connect_phase; that is harmless today because uvm_agent and uvm_env add nothing there, but it is the kind of shortcut that breaks when a team-wide base class is inserted between them and the library.

    Watch it run: the passive agent that wasn't

    A copy of the smoke test overrides axis_agent with a version whose build_phase omits the super call and is otherwise identical. The test passes:

    Diagram
    out_agt                dma_skipsuper_agent     -     @272
            drv                  axis_driver             -     @809
    ...
            mon                  axis_monitor            -     @669
    ...
            sqr                  uvm_sequencer           -     @686
    ...
    UVM_INFO dma_tb.sv(411) @ 655000: uvm_test_top.env.sb [SB] 8 words matched, 0 mismatched
    UVM_ERROR :    0
    UVM_FATAL :    0
    • out_agt now has a driver and a sequencer: is_active was never read, so it stayed UVM_ACTIVE.

    • The extra driver drives tvalid <= 0 once on the output interface, then waits forever for items that never come. On this run that was harmless; on another simulator, or with a DUT that samples its own outputs, it fights the DUT for the bus.

    • No error, no warning. The only evidence is the topology, which is why reading it is a habit, not a formality.

    What breaks: the refactor that turned SoC agents active

    At SoC level, a team reused block-level agents in passive mode to watch internal buses. A cleanup pass created a shared base_agent class whose build_phase started with the agent's own creates and "didn't need super, since uvm_agent's build is empty". Every passive agent in the chip bench quietly became active. On most buses the new drivers sat idle, but on one shared bus an idle driver held its valid low against the real master, and a week of failures was blamed on the interconnect RTL.

    The fix was one line, super.build_phase(phase), in base_agent. The team also added an end_of_elaboration check that every agent configured passive reports get_is_active() == UVM_PASSIVE, so the next refactor would fail at time 0 instead of in the lab.

    Common pitfall

    Copying the configured value into is_active yourself (is_active = cfg.is_active;) after skipping super appears to work, and then breaks the day someone sets is_active through config_db as the env in this bench does. Call super, then add your own logic.

    Interview

    "What happens if an agent's build_phase doesn't call super.build_phase()?"

    A weak answer: "It's bad style, but nothing breaks, because the base build_phase is empty." That is the answer that turns passive agents active.

    A strong answer: "Two things are lost. uvm_component::build_phase calls build(), which runs apply_config_settings, the automatic configuration of field-macro fields; in 1800.2 only if use_automatic_config() returns 1. And uvm_agent::build_phase looks up is_active from config_db. Without super, is_active stays UVM_ACTIVE, so a passive agent builds a driver and sequencer, silently. I'd catch it in the topology print or with an end_of_elaboration check on get_is_active()."

    Follow-ups: Which other library classes have non-empty phase methods? (the sequencer's build and connect, the driver's end_of_elaboration warning) and Where does automatic configuration happen in UVM 1.2? (in build(), unconditionally).

    Check yourself An agent's field num_retries is registered with uvm_field_int and set through config_db by the env. The agent's build_phase skips super. What value does num_retries have?

    Its default. apply_config_settings runs inside build(), called from uvm_component::build_phase, so without the super chain no field-macro field is filled from config_db.

    Check yourself A component never uses field macros. Is it safe for it to skip super.build_phase?

    Safe today only if no class between it and uvm_component adds work in build_phase, and it is not an agent. It costs nothing to call super, and it protects against base classes added later. If the goal is to avoid the cost of automatic configuration, override use_automatic_config() to return 0 instead (IEEE 1800.2).

    Check yourself How would you prove, at time 0, that every passive agent really is passive?

    Read the topology (a passive agent has only a monitor), or add an end_of_elaboration check that compares each agent's get_is_active() with its intended mode and reports an error. Both run before any traffic.

    Next: the last phase question is the oldest one: how does it all end? run_test, Timeouts and How a Simulation Ends covers every exit, including the one that skips report_phase.

    Related topics

    • run_test, Timeouts and How a Simulation Ends

    • Automatic configuration

    • Signatures, Super Calls, and Debug

    Previous

    31 / 280

    Next: run_test, Timeouts and How a Simulation Ends

    Up next

    Up nextrun_test, Timeouts and How a Simulation Ends

    Sign in to save progress

    Practice this lessonReport an error

    See also

    • Why build_phase Is Top-Down
    • config_db Gets and Conditional Construction
    • Connect Phase: Bottom-Up TLM Wiring