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
  • Factory & Configuration15
    • UVM Factory: Registration, Overrides and Debug
    • config_db and Resources
      • The set/get Contract: Four Arguments That Must Agree
      • Instance Paths and Wildcards: Who Can See a Setting
      • config_db Resolution: Who Wins at Build and After
      • Config Objects: One Typed Handle Instead of Twenty Knobs
      • config_db Performance and Anti-Patterns
      • Auto-Config and Field Macros on Components
      • Configuring Sequences
  • 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

‹config_db and Resources
  1. Topics
  2. UVM Verification
  3. Factory & Configuration
  4. config_db and Resources
  5. Auto-Config and Field Macros on Components

Part 3 · Factory & Configuration · Intermediate

Auto-Config and Field Macros on Components

How super.build_phase applies configuration to field-macro members (apply_config_settings, use_automatic_config in IEEE 1800.2), how uvm_agent reads is_active, why skipping super.build_phase breaks both, and how +uvm_set_config_int is typed.

On this page

    Someone tidies axis_agent::build_phase and deletes the super.build_phase(phase) line, because "the base class doesn't do anything". The smoke test now fails with scoreboard mismatches on the output side. The topology explains it: out_agt has a driver and a sequencer. The env still sets is_active to UVM_PASSIVE for it, and nobody reads that setting anymore. The base class did something after all.

    The idea: UVM reads some notes for you

    Most config is pulled explicitly: a component calls get for what it needs. Auto-config is the exception: for members you declared with field macros (` uvm_field_int `, uvm_field_enum `, uvm_field_object ` and so on, inside uvm_component_utils_begin ` / uvm_component_utils_end `), the base uvm_component` looks up a setting with the member's name at the component's own path and, if one exists, assigns it. It is like a secretary who sorts your mail into labelled trays before you arrive, but only for the trays you put on your desk, and only if you let the secretary into the office.

    Letting the secretary in is super.build_phase(phase). uvm_component::build_phase calls build(), which calls apply_config_settings() (in IEEE 1800.2, only when use_automatic_config() returns 1, the default). uvm_agent adds one more lookup of its own in its build_phase: is_active, accepted as the enum, an int, an int unsigned, a bitstream or a string. Skip super.build_phase and none of it happens.

    The picture

    Sequence diagram: the agent's build_phase calls super, which reaches uvm_component's build and apply_config_settings, then uvm_agent's is_active lookup, before the agent creates its children.
    What super.build_phase does in an agent. The component base applies auto-config to field-macro members; uvm_agent then looks up is_active. Only after that does the agent decide what to build.

    Build it: is_active, field macros and the command line

    The bench's agent: super first, then decide

    Both bench agents call super.build_phase first, and then ask get_is_active(). The comment in the APB agent says why:

    design.sv
    1
    2
    3
    4
    5
    6
    7
    8
    9
    // snippet: from apb_agent::build_phase in the bench
    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

    Field macros on a component

    Field macros are legal on components, inside the _begin/_end form of the registration macro. Each declared member becomes auto-configurable under its own name. A driver variant with a configurable idle cap:

    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: a component member configured by auto-config
    class axis_capped_driver extends axis_driver;
      int max_idle = 8;
      `uvm_component_utils_begin(axis_capped_driver)
        `uvm_field_int(max_idle, UVM_DEFAULT)
      `uvm_component_utils_end
    
      function new(string name, uvm_component parent);
        super.new(name, parent);
      endfunction
    
      function void build_phase(uvm_phase phase);
        super.build_phase(phase);      // max_idle is assigned here if a setting exists
        `uvm_info("CFG", $sformatf("max_idle=%0d", max_idle), UVM_MEDIUM)
      endfunction
    
      function axis_item pre_drive(axis_item it);
        axis_item b;
        if (it.idle <= max_idle) return it;
        $cast(b, it.clone());
        b.idle = max_idle;
        return b;
      endfunction
    endclass
    
    // In a test's build_phase:
    uvm_config_db#(int)::set(this, "env.in_agt.drv", "max_idle", 2);

    No explicit get anywhere. Auto-config for an integral field accepts settings of type int, int unsigned, uvm_integral_t and uvm_bitstream_t, which is more forgiving than an explicit get. A member flagged UVM_READONLY is skipped by auto-config.

    +uvm_set_config_int is a bitstream

    The command-line forms +uvm_set_config_int=<path>,<field>,<value> and +uvm_set_config_string become config_db sets from the top during build. The integer form stores a uvm_bitstream_t. Who sees it:

    Reader

    Sees +uvm_set_config_int?

    Auto-config of a uvm_field_int member

    Yes

    uvm_agent's is_active lookup

    Yes

    uvm_config_db#(uvm_bitstream_t)::get

    Yes

    uvm_config_db#(int)::get

    No: config_db matches the exact type

    So +uvm_set_config_int=uvm_test_top.env.in_agt.drv,max_idle,0 configures the driver above with no code change, while the same plusarg aimed at a member read with config_db#(int)::get does nothing. The command-line forms are covered in Command-line overrides.

    Note

    In IEEE 1800.2: a component can override use_automatic_config() to return 0. Its build() then skips apply_config_settings(), which saves the lookups in large benches; its field-macro members are no longer configured automatically, while uvm_agent's is_active lookup still runs. UVM 1.2 has no such switch: build() always applies config settings.

    Timing follows from where auto-config runs. It happens at the start of the component's own build, so a setting made by the parent in the parent's build_phase, before it creates the child, arrives in time. An initial value given in the declaration or the constructor is only a default: if a setting exists, auto-config overwrites it during super.build_phase, and code after that line sees the configured value.

    Should you rely on auto-config?

    Auto-config is convenient and quiet, and the quiet part is the problem. An explicit get is greppable, typed, and can fail loudly when a required setting is missing; auto-config never reports a miss, and it costs one lookup per field-macro member per component during build. Many teams therefore allow field macros on components only for a few simple, optional knobs, and use explicit, checked gets (usually of one config object) for anything that matters. The DMA bench takes that line: no component uses field macros, and every required setting is a checked get. Whatever your team decides, uvm_agent::is_active is read by the base class either way, so the super.build_phase rule applies to every agent.

    Watch it run

    Auto-config's effect on the shipped bench is in the topology printed at the start of every run: out_agt holds only mon and its analysis port, because uvm_agent::build_phase read UVM_PASSIVE before the agent decided what to build. The run then checks every word:

    Diagram
    +UVM_TESTNAME=dma_smoke_test
    UVM_INFO dma_tb.sv(411) @ 655000: uvm_test_top.env.sb [SB] 8 words matched, 0 mismatched
    UVM_ERROR :    0
    UVM_FATAL :    0

    To see auto-config decisions themselves, set the static bit uvm_component::print_config_matches = 1 (UVM 1.2) before build; components then report each setting they apply.

    What breaks: the missing super, twice

    Back to the opening, in detail. Without super.build_phase, is_active keeps its declared default, UVM_ACTIVE, so out_agt builds a sequencer and a driver. The driver starts by driving TVALID low on the output interface, the port the DUT drives. The output monitor no longer sees the DUT's words cleanly, and the scoreboard reports mismatches or leftover predictions. The env's setting was correct the whole time; nobody read it.

    The same bug has a quieter twin. A team's scoreboard had a field-macro member strict_mode configured by tests. A later refactor gave the scoreboard its own build_phase that created two FIFOs and forgot the super call. Every test that turned strict_mode off silently ran strict, and a class of legitimately reordered transactions started failing intermittently. The fix both times is the rule: every `build_phase` override starts with `super.build_phase(phase)`, even when the parent "does nothing" today. Lint for it.

    Interview

    "What does super.build_phase actually do? Can you skip it?"

    A weak answer: "It calls the parent's build_phase, which is empty for most components, so skipping it is usually harmless."

    A strong answer: "uvm_component::build_phase calls build(), which applies auto-config: for every field-macro member it looks up a setting with that name at the component's path and assigns it. In 1800.2 that happens only if use_automatic_config() returns 1, the default. uvm_agent's build_phase also reads is_active. Skip super and field-macro members keep their defaults and an agent configured passive builds a driver. +uvm_set_config_int stores a uvm_bitstream_t, so auto-config and is_active see it but config_db#(int)::get doesn't."

    Follow-ups: Are field macros allowed on components? (yes, inside uvm_component_utils_begin/end; they drive auto-config) and How do you turn auto-config off? (override use_automatic_config in 1800.2).

    Check yourself The env sets is_active with uvm_config_db#(int)::set(this, "out_agt", "is_active", 0) instead of the enum. Is out_agt passive?

    Yes. uvm_agent's lookup accepts is_active as the enum, int, int unsigned, a bitstream or a string. 0 is UVM_PASSIVE. An explicit uvm_config_db#(uvm_active_passive_enum)::get would have missed the int setting, but the agent does not use one.

    Check yourself axis_capped_driver declares max_idle with a field macro, and the test sets it with uvm_config_db#(int unsigned)::set. Does auto-config apply it? Would uvm_config_db#(int)::get in the driver see it?

    Auto-config applies it: integral field macros accept int, int unsigned, uvm_integral_t and uvm_bitstream_t settings. An explicit uvm_config_db#(int)::get would miss it, because config_db requires the identical type.

    Check yourself A regression passes +uvm_set_config_int=uvm_test_top.env.in_agt,is_active,0. What happens to in_agt, and which setting would it beat?

    in_agt becomes passive: the plusarg is a bitstream set from the top during build (precedence 1000), and uvm_agent accepts bitstreams for is_active. It would beat any is_active set made by the test (999) or the env (998) during build. With no driver, the input stream gets no stimulus and the test's sequences cannot start on in_agt.sqr.

    Next: components read config by path. Sequences have no place in the hierarchy, yet they need knobs too. Configuring sequences shows how they borrow a path.

    Related topics

    • The set/get contract

    • Command-line overrides

    • Field macros on objects

    • The is_active knob

    • Phase signatures and super calls

    Previous

    58 / 280

    Next: Configuring Sequences

    Up next

    Up nextConfiguring Sequences

    Sign in to save progress

    Practice this lessonReport an error

    See also

    • The set/get Contract: Four Arguments That Must Agree
    • Instance Paths and Wildcards: Who Can See a Setting
    • config_db Resolution: Who Wins at Build and After