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
      • The Factory: Swapping Classes Without Editing the Env
      • Registration Macros: What uvm_component_utils Writes for You
      • The type_id::create Pattern
      • Override Resolution: Who Wins
      • Override Timing, Safety and Debug
      • Factory Advanced: Parameterized, Abstract and By-Name
      • Command-Line Overrides
      • Overriding Items and Sequences
    • config_db and Resources
  • 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 Factory: Registration, Overrides and Debug
  1. Topics
  2. UVM Verification
  3. Factory & Configuration
  4. UVM Factory: Registration, Overrides and Debug
  5. Factory Advanced: Parameterized, Abstract and By-Name

Part 3 · Factory & Configuration · Intermediate

Factory Advanced: Parameterized, Abstract and By-Name

Override chaining in practice, parameterized classes and why they cannot be overridden by name, abstract registration in IEEE 1800.2, by-name overrides, the factory handle, and the inherited type_id trap.

On this page

    An interviewer at a VIP company asks: "Your AXI-Stream agent is parameterized on data width. A test wants to replace the 64-bit driver with an error-injecting one, from the command line. How?" The honest first answer is "you can't, not directly", and the interesting part is why. This lesson covers the factory corners that separate a user from an owner: parameterized and abstract classes, by-name overrides, and a trap that silently builds the wrong class.

    The idea: the factory knows classes by card, and some cards are blank

    Every registered class gives the factory a card with a type handle and, usually, a name string. Two kinds of lookup use those cards: by type (set_type_override_by_type, type_id::create) and by name (set_type_override_by_name, +UVM_TESTNAME, +uvm_set_type_override). A parameterized class registers each specialization by type, but with no usable name, because the macro cannot turn axis_driver_p #(64) into a string. A class without its own macro has no card at all and silently borrows its parent's. Most advanced factory behaviour follows from those two facts.

    The picture

    Diagram: named classes chained by type overrides; two specializations of a parameterized driver, only the 64-bit one overridden, by type.
    Named classes accept by-type and by-name overrides, and chains resolve through them. Each specialization of a parameterized class is a separate type with no name string, so it can be overridden only by type, one specialization at a time.

    Build it: five advanced cases

    1. Chaining: refine an override you did not write

    dma_truncate_test overrides axis_driver with axis_truncating_driver. A derived test that also wants logging does not need to fight that override; it extends the truncating driver and overrides it. The factory resolves the winner again, so the chain is followed (the rules):

    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
    // snippet: a derived test refining the base test's override
    class axis_trunc_log_driver extends axis_truncating_driver;
      `uvm_component_utils(axis_trunc_log_driver)
      function new(string name, uvm_component parent);
        super.new(name, parent);
      endfunction
      function axis_item pre_drive(axis_item it);
        axis_item b = super.pre_drive(it);
        if (b == null) `uvm_info("TRUNC", "beat dropped", UVM_HIGH)
        return b;
      endfunction
    endclass
    
    class dma_truncate_log_test extends dma_truncate_test;
      `uvm_component_utils(dma_truncate_log_test)
      function new(string name, uvm_component parent);
        super.new(name, parent);
      endfunction
      function void build_phase(uvm_phase phase);
        super.build_phase(phase);   // axis_driver -> axis_truncating_driver
        axis_truncating_driver::type_id::set_type_override(axis_trunc_log_driver::get_type());
      endfunction
    endclass

    The order of the two type overrides does not matter here, because they are for different original types, and the drivers are created later, in the agent's build_phase.

    2. Parameterized classes: by type, per specialization

    A parameterized class registers with ` uvm_component_param_utils ` (or uvm_object_param_utils `). Each specialization is a distinct type with its own proxy, and none has a name string. Three consequences: overrides must name the exact specialization; by-name overrides and +uvm_set_type_override cannot find it; and get_type_name()` does not return a useful name unless you supply one.

    design.sv
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    // snippet: a width-parameterized driver and a by-type override
    class axis_driver_p #(int W = 32) extends uvm_driver #(axis_item_p #(W));
      `uvm_component_param_utils(axis_driver_p #(W))
      function new(string name, uvm_component parent);
        super.new(name, parent);
      endfunction
    endclass
    
    class axis_err_driver_64 extends axis_driver_p #(64);
      `uvm_component_utils(axis_err_driver_64)        // not parameterized: has a name
      function new(string name, uvm_component parent);
        super.new(name, parent);
      endfunction
    endclass
    
    // In the test: only the 64-bit specialization is replaced; #(32) is untouched.
    axis_driver_p #(64)::type_id::set_type_override(axis_err_driver_64::get_type());

    That is the answer to the opening question. The usual workaround for command-line control is a non-parameterized, registered subclass per specialization you care about (class axis_driver_64 extends axis_driver_p #(64) with ` uvm_component_utils `), which the env instantiates. Then by-name overrides work on axis_driver_64`.

    3. Abstract classes

    A virtual class cannot be constructed, so the normal registration macro does not compile on it in UVM 1.2: the generated proxy calls new. In UVM 1.2 you either leave the abstract base unregistered and register only concrete subclasses, or make the base concrete with methods that report an error if not overridden.

    Note

    In IEEE 1800.2: ` uvm_object_abstract_utils(T) ` and uvm_component_abstract_utils(T) ` (and _param_ variants) register a virtual class. You can call T::type_id::create and set overrides on it; if no override resolves to a concrete class, create reports UVM/ABST_RGTRY/CREATE_ABSTRACT_OBJ` and returns null. This makes "the test must choose an implementation" an enforced rule.

    4. By-name overrides and the factory handle

    Every by-type call has a by-name twin that takes strings, which is what the command line and data-driven test configs use. You reach the factory through the core service, or through the uvm_factory::get() shortcut:

    design.sv
    1
    2
    3
    4
    5
    6
    // snippet: by-name overrides through the factory handle
    uvm_coreservice_t cs = uvm_coreservice_t::get();
    uvm_factory       f  = cs.get_factory();          // same object as uvm_factory::get()
    f.set_type_override_by_name("axis_driver", "axis_truncating_driver");
    f.set_inst_override_by_name("axis_driver", "axis_truncating_driver",
                                "uvm_test_top.env.in_agt.drv");

    By-name calls are checked at run time, not compile time. A typo in a type name does not fail the build; the override simply never matches (or the factory warns that the type is not registered). Prefer by-type calls in code and keep by-name for the command line.

    5. The inherited type_id trap

    A class that extends a registered class but has no macro of its own still has a type_id: the parent's, inherited like any static member. So child::type_id::create(...) compiles, and builds the parent. Nothing fails until behaviour is missing.

    design.sv
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    // bad-example: axis_err_item forgot its registration macro
    class axis_err_item extends axis_item;     // no uvm_object_utils here
      function new(string name = "axis_err_item");
        super.new(name);
      endfunction
    endclass
    
    axis_item it = axis_err_item::type_id::create("it");  // compiles; builds an axis_item
    // it.get_type_name() returns "axis_item", and an override to axis_err_item
    // cannot work: axis_err_item::get_type() is axis_item's proxy.

    The compile error you might hope for appears only if you assign the result to an axis_err_item handle (the returned type is axis_item). The fix is the macro. A lint rule "every class extending a UVM class has a utils macro" catches it.

    Override, callback or config knob?

    Senior interviews often end the factory discussion with "when would you not use an override?" A short comparison helps:

    Mechanism

    Good for

    Cost

    Factory override

    Replacing a whole behaviour for a test: a truncating driver, an end-of-test scoreboard.

    One class per variation; the base class must expose virtual methods or hooks.

    Callback

    Adding small, stackable behaviours to VIP you cannot edit, several at once.

    The VIP must provide callback hooks; ordering between callbacks needs care.

    Config knob

    Tuning a value the class already supports: idle cap, error rate, is_active.

    Every knob is code in the base class; too many knobs make it unreadable.

    The bench uses an override for truncation because cutting packets is a different behaviour, not a different number. Callbacks versus factory overrides goes deeper.

    Watch it run

    The only advanced case the bench runs as written is the plain type override; its result is the familiar truncation line. Chaining adds a second hop with the same path and the same log ID:

    Diagram
    UVM_INFO dma_tb.sv(520) @ 275000: uvm_test_top.env.in_agt.drv [TRUNC] TLAST forced on beat 3

    When you add the chain, read the topology: the drv row must show axis_trunc_log_driver. If it shows axis_truncating_driver, the second override was not registered (or was registered for the wrong original type).

    What breaks: the error item that was never an error item

    A team built an error-injection campaign: axis_err_item with a constraint that occasionally flips a parity bit, swapped in by a type override from axis_item. Coverage on the error bit stayed at zero for three weeks. Everyone suspected the constraint. The real cause was the missing macro: axis_err_item::get_type() returned axis_item's proxy, so the override said "replace axis_item with axis_item", and UVM printed a TYPDUP warning ("original and override type arguments are identical") that nobody read in a log of thousands of lines. One macro line fixed it. The team now fails the regression on any TYPDUP warning.

    Interview

    "How do you override a parameterized class?"

    A weak answer: "Same as any class, set_type_override with the new type."

    A strong answer: "Each specialization is its own type, registered with param_utils by type only, with no name string. So I override the exact specialization by type, axis_driver_p#(64)::type_id::set_type_override(...), and other widths are unaffected. By-name overrides and +uvm_set_type_override can't find it. If I need command-line control I put a named, non-parameterized subclass per width in the env. In 1800.2 abstract bases can be registered with abstract_utils so tests are forced to pick a concrete class."

    Follow-ups: What happens if a derived class has no macro? (it inherits the parent's type_id and builds the parent) and How do you get the factory handle in 1800.2? (uvm_coreservice_t::get().get_factory(), or uvm_factory::get()).

    Check yourself You type-override axis_driver_p #(32) with an error driver. The bench also has 64-bit agents. Which drivers change?

    Only the 32-bit ones. axis_driver_p #(32) and axis_driver_p #(64) are distinct types with separate proxies; an override on one does not touch the other.

    Check yourself A test calls f.set_type_override_by_name("axis_drvier", "axis_truncating_driver") (note the typo). What happens?

    It compiles, because the names are just strings. At run time no requested type is ever called axis_drvier, so the override never matches and the normal driver is built. Depending on the version the factory may warn that the type is not registered, which is easy to miss. That is why code uses by-type overrides.

    Check yourself A log shows TYPDUP: Original and override type arguments are identical: axis_item, from a line that overrides axis_item with axis_err_item. What is the bug?

    axis_err_item has no registration macro, so axis_err_item::get_type() is inherited and returns axis_item's proxy. The override maps a type to itself. Add ` uvm_object_utils(axis_err_item) ``.

    Next: most of these overrides are more useful when a regression can apply them without recompiling. Command-line overrides shows how, and the ordering surprise between command line and test code.

    Related topics

    • Override resolution: who wins

    • Registration macros

    • Command-line overrides

    • VIP parameterization

    Previous

    50 / 280

    Next: Command-Line Overrides

    Up next

    Up nextCommand-Line Overrides

    Sign in to save progress

    Practice this lessonReport an error

    See also

    • The Factory: Swapping Classes Without Editing the Env
    • Registration Macros: What uvm_component_utils Writes for You
    • The type_id::create Pattern