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. The set/get Contract: Four Arguments That Must Agree

Part 3 · Factory & Configuration · Intermediate

The set/get Contract: Four Arguments That Must Agree

How a config_db set and get find each other: context plus instance name forms the scope, the field name must match, and the type must be identical. Checked gets and the top module's virtual interfaces.

On this page

    A teammate renames the DMA env's input agent from in_agt to s_agt to match the new naming guide. Everything compiles. At time 0 the run dies: UVM_FATAL ... uvm_test_top.env.s_agt.drv [NOVIF] axis_if not set for this driver. The interface is still in the top module, the set call is still there, and nothing about the driver changed. A set and a get that used to meet no longer do. Why?

    The idea: a notice board with addressed, labelled, typed notes

    config_db is a shared notice board. A set pins a note with three things written on it: who it is addressed to (a scope, possibly with wildcards), a label (the field name), and what kind of thing is inside (the type, fixed by the class parameter T). A get walks up to the board and looks for notes addressed to its own full name, with its label, of its type. All three must match. If any one differs, the reader finds nothing, and the board never tells the writer that the note went unread.

    Two consequences follow. The scope is a string, so renaming a component silently changes who a note reaches. And the type is part of the key, so virtual axis_if and virtual apb_if, or int and int unsigned, are different boards entirely.

    The picture

    Flow: the top's set creates a resource; the driver's get builds a key; they match only if scope, field name and type all agree.
    A set produces a resource with a scope pattern, a name and a type. A get builds its own key from its context and instance name. The lookup succeeds only when the scope pattern matches the getter's full name and the name and type are identical.

    Build it: the calls in the bench

    The four arguments

    Argument

    On set

    On get

    cntxt (a uvm_component or null)

    Where the path starts. null means the top (uvm_root, whose full name is empty).

    Usually this.

    inst_name (string)

    Appended to the context's full name with a dot; may contain wildcards.

    Usually "", so the key is the getter's own full name.

    field_name (string)

    The label.

    Must be the same label.

    value

    Stored under type T.

    Written only on success; left unchanged on a miss.

    The scope of a set is {cntxt.get_full_name(), ".", inst_name}, or just inst_name when the context is null (or just the context's full name when inst_name is empty). The get side builds its key the same way. Keep that formula in your head and most config bugs become string comparisons.

    The top module publishes the interfaces

    The top module has no component context, so it sets from null with absolute paths. Each set reaches every component under one agent:

    top.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
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    // snippet: from the course DMA bench
    module top;
      import uvm_pkg::*;
      import dma_pkg::*;
    
      logic clk = 0;
      always #5 clk = ~clk;                        // 100 MHz
      rst_if rst (clk);
      wire rst_n = rst.rst_n;
      initial begin
        rst.rst_n = 1'b0;
        repeat (4) @(posedge clk);
        rst.rst_n <= 1'b1;
      end
    
      apb_if  apb  (clk, rst_n);
      axis_if s_ax (clk, rst_n);
      axis_if m_ax (clk, rst_n);
      irq_if  irqi (clk);
    
      // The output side accepts every beat unless a test adds a reactive slave
      // (axis_sink_agent), whose driver then drives TREADY.
      initial m_ax.tready = 1'b1;
    
      dma_dut dut (
        .clk, .rst_n,
        .paddr(apb.paddr), .psel(apb.psel), .penable(apb.penable), .pwrite(apb.pwrite),
        .pwdata(apb.pwdata), .prdata(apb.prdata), .pready(apb.pready), .pslverr(apb.pslverr),
        .s_tdata(s_ax.tdata), .s_tvalid(s_ax.tvalid), .s_tlast(s_ax.tlast), .s_tready(s_ax.tready),
        .m_tdata(m_ax.tdata), .m_tvalid(m_ax.tvalid), .m_tlast(m_ax.tlast), .m_tready(m_ax.tready),
        .irq(irqi.irq)
      );
    
      initial begin
        uvm_config_db#(virtual apb_if)::set(null, "uvm_test_top.env.apb.*", "vif", apb);
        uvm_config_db#(virtual axis_if)::set(null, "uvm_test_top.env.in_agt.*", "vif", s_ax);
        uvm_config_db#(virtual axis_if)::set(null, "uvm_test_top.env.out_agt.*", "vif", m_ax);
        uvm_config_db#(virtual irq_if)::set(null, "uvm_test_top", "irq_vif", irqi);
        uvm_config_db#(virtual irq_if)::set(null, "uvm_test_top.env.irq.*", "vif", irqi);
        uvm_config_db#(virtual rst_if)::set(null, "uvm_test_top*", "rst_vif", rst);
        run_test("dma_smoke_test");
      end
    endmodule

    Drivers and monitors read them, and fail loudly

    A missing interface is not something to tolerate, so the bench's gets are checked: a miss is a fatal error with a message that says what was expected.

    design.sv
    1
    2
    3
    4
    5
    6
    // snippet: from axis_driver::build_phase in the bench
    function void build_phase(uvm_phase phase);
      super.build_phase(phase);
      if (!uvm_config_db#(virtual axis_if)::get(this, "", "vif", vif))
        `uvm_fatal("NOVIF", "axis_if not set for this driver")
    endfunction

    Optional settings use the other half of the contract: the value is untouched on a miss, so initialize the variable to its default and let a successful get overwrite it.

    To ask whether a setting exists without reading it, use uvm_config_db#(T)::exists(cntxt, inst_name, field_name, spell_chk). With spell_chk set to 1, a miss makes UVM suggest close matches for the field name, which is a cheap way to catch vif versus v_if in a component that has an optional setting.

    For a component, get(this, "", ...) and get(null, get_full_name(), ...) build the same key; the first is shorter and is the convention. The second form is what objects such as sequences use, because they cannot be a context (Configuring sequences).

    A component can configure its children

    The env tells the output agent to stay passive. It sets relative to itself, before creating the child, so the scope becomes uvm_test_top.env.out_agt:

    design.sv
    // snippet: from dma_env::build_phase in the bench
    uvm_config_db#(uvm_active_passive_enum)::set(this, "out_agt", "is_active", UVM_PASSIVE);
    out_agt = axis_agent::type_id::create("out_agt", this);

    The agent never calls get for is_active itself: uvm_agent's own build_phase, reached through super.build_phase, looks it up. That mechanism is auto-config.

    Watch it run

    When every set and get agree, nothing about config appears in the log, which is the point. The smoke test runs to completion:

    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

    And the topology at the start shows out_agt with only mon under it: the env's set reached the agent. When a get misses, the checked get turns silence into a time-0 fatal with the getter's full path, which is exactly the string you compare against the set's scope.

    What breaks: the rename that orphaned an interface

    Back to the opening. After the rename, the driver's key is uvm_test_top.env.s_agt.drv, but the top still sets scope uvm_test_top.env.in_agt.*. The glob does not match, the get returns 0, and the checked get fires. The set was not wrong when it was written; it encoded the env's internal names in the top module, two levels away from the code that chose them.

    Two fixes, from quick to robust. Quick: update the top's path. Robust: move knowledge of internal names next to the code that owns them. The top sets each interface once, on the env, under a distinct field name (for example set(null, "uvm_test_top.env", "s_vif", s_ax)), and the env passes it down to in_agt, by config_db or inside an agent config object. Then a rename inside the env is a change inside the env. Config objects shows the object version.

    Common pitfall

    Type mismatches fail the same silent way. uvm_config_db#(virtual axis_if)::set and uvm_config_db#(virtual axis_if.drv_mp)::get are different types, as are int and int unsigned, or a derived config class and its base. Set and get with the identical T.

    Interview

    "Explain the arguments of uvm_config_db set and get."

    A weak answer: "Context, path, name and value. You set it in the test and get it in the component."

    A strong answer: "T is part of the key and must be identical on both sides. Context plus instance name form the scope: the context's full name, a dot, then the instance string, which may have wildcards; null means uvm_root, so the string is absolute. The field name must match. Get builds its key the same way, usually this and an empty string, so it's the getter's full name. Get returns a bit and leaves the value unchanged on a miss, so required settings are checked with a fatal and optional ones keep a default."

    Follow-ups: Who wins when two sets match? (see the resolution lesson) and Why does the top set from null? (a module is not a component, so there is no context).

    Check yourself The env adds uvm_config_db#(int)::set(this, "in_agt.drv", "gap", 2) and the driver calls uvm_config_db#(int unsigned)::get(this, "", "gap", gap). Does the driver see 2?

    No. The scope and name match, but the types differ (int versus int unsigned), and config_db only finds resources of the identical type. The get returns 0 and gap keeps its initial value.

    Check yourself What scope does uvm_config_db#(int)::set(this, "", "n", 5) create when called from uvm_test_top.env, and who can read it?

    The scope is uvm_test_top.env (an empty instance name means the context itself). Only a get whose key is exactly uvm_test_top.env sees it, normally the env reading its own setting with get(this, "", "n", n). Its children do not, because their full names are longer.

    Check yourself Why does the bench check its interface gets with uvm_fatal instead of a warning?

    A driver without an interface fails later with a null-handle access deep in run_phase, far from the cause. A fatal at time 0 with the getter's full path and a clear message points straight at the mismatched set.

    Next: the scope pattern decides who can read a note. Instance paths and wildcards covers the patterns, including the one that excludes the very component you meant.

    Related topics

    • Instance paths and wildcards

    • Resolution order: who wins

    • config_db get in build_phase

    • Virtual interfaces

    Previous

    53 / 280

    Next: Instance Paths and Wildcards: Who Can See a Setting

    Up next

    Up nextInstance Paths and Wildcards: Who Can See a Setting

    Sign in to save progress

    Practice this lessonReport an error

    See also

    • config_db Resolution: Who Wins at Build and After
    • Config Objects: One Typed Handle Instead of Twenty Knobs
    • config_db Performance and Anti-Patterns