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. Registration Macros: What uvm_component_utils Writes for You

Part 3/Factory & ConfigurationIntermediate

Registration Macros: What uvm_component_utils Writes for You

What the component and object utils macros generate, why the constructor must match, and which macro variant fits which class.

On this page

    You add dma_scoreboard_eot to the bench, compile, and get an error deep inside uvm_registry.svh about a call to new with the wrong arguments. Your own file looks fine. The cause is one word: you typed ` uvm_object_utils `` on a component. To read that error in seconds instead of an hour, you need to know what the macro actually expands to.

    The idea: a business card for the factory

    A registration macro is a business card the class hands to the factory. The card says three things: my name (a string, for by-name lookups and printing), my type (a unique handle, for by-type lookups), and how to build me (a constructor call with the right arguments). The factory never sees your class directly; it only ever holds the card, which UVM calls a proxy or registry object.

    Because the card includes how to build me, the macro must match the class kind. A component is built with a name and a parent; an object is built with a name only. Pick the wrong card and the generated constructor call does not compile.

    The picture

    Flow: the component utils macro produces type_id, get_type and get_type_name; type_id registers with the factory, which later calls new(name, parent).
    One macro line generates the proxy typedef, two lookup functions and the registration. When the factory builds the class, the proxy calls the constructor with the arguments for that class kind.

    Build it: what the macro expands to

    Here is roughly what ` uvm_component_utils(dma_scoreboard_eot) `` puts into the class. The real expansion has more plumbing, but these are the members you call:

    design.sv
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    // snippet: approximately what the component macro generates
    typedef uvm_component_registry #(dma_scoreboard_eot, "dma_scoreboard_eot") type_id;
    
    static function type_id get_type();
      return type_id::get();          // the single proxy object for this class
    endfunction
    
    virtual function uvm_object_wrapper get_object_type();
      return type_id::get();
    endfunction
    
    const static string type_name = "dma_scoreboard_eot";
    virtual function string get_type_name();
      return type_name;
    endfunction

    type_id is a specialization of uvm_component_registry. Its create(name, parent) asks the factory which class to build, and the registry for the winning class finally calls that class's new(name, parent). The object macro generates the same members with uvm_object_registry, whose create calls new(name) or new(), plus a create() method that clone() relies on.

    Constructors: the half of the contract you write

    The macro assumes a constructor of a fixed shape. The bench follows it everywhere; two examples:

    design.sv
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    // snippet: the two constructor shapes from the bench
    class dma_scoreboard_eot extends dma_scoreboard;        // a component
      `uvm_component_utils(dma_scoreboard_eot)
      function new(string name, uvm_component parent);
        super.new(name, parent);
      endfunction
      // ...
    endclass
    
    class dma_job_cfg extends uvm_object;                   // an object
      `uvm_object_utils(dma_job_cfg)
      function new(string name = "dma_job_cfg");
        super.new(name);
      endfunction
      // ...
    endclass

    The default value on the object's name matters: the generated create() calls new() with no argument when no name is given, so an object constructor without a default fails to compile as soon as the macro is added.

    Which macro for which class

    Macro

    Use it on

    Notes

    uvm_component_utils(T)

    Drivers, monitors, agents, scoreboards, envs, tests

    Constructor new(string name, uvm_component parent).

    uvm_object_utils(T)

    Sequence items, sequences, config objects

    Constructor new(string name = "...").

    uvm_component_utils_begin(T) ... uvm_component_utils_end

    Components with field macros

    Field macros on a component drive auto-config; see Auto-config and field macros.

    uvm_object_utils_begin(T) ... uvm_object_utils_end

    Objects with field macros

    Generates copy, compare, print and pack; see Field macros.

    uvm_component_param_utils(T) / uvm_object_param_utils(T)

    Parameterized classes

    Registers by type only, with no name string. See Factory advanced.

    uvm_object_abstract_utils(T) / uvm_component_abstract_utils(T)

    Virtual (abstract) classes

    IEEE 1800.2 only. See Factory advanced.

    Sequences are objects too, so they use the object macro and the object constructor, even though they run for a long time and talk to a sequencer. Only classes that live in the component tree (they have a parent and take part in phases) use the component macro. When in doubt, ask whether the class is created in a build_phase with a parent: if yes, component; if it can be created many times at run time, object.

    The name string, and two classes that share it

    The second argument of the registry, the name string, is what every string-based lookup uses: +UVM_TESTNAME, set_type_override_by_name, the command-line overrides and the factory's print. Names are global across the whole compile, not scoped by package. If two packages each define a class called driver (two VIPs from different vendors, say), the second registration produces a TPRGED warning: the type name is already registered, and string-based lookup cannot tell the two apart. By-type overrides still work, because each class has its own proxy; by-name overrides and command-line overrides become ambiguous.

    That is why VIP classes carry a protocol prefix (apb_driver, axis_driver) even inside their own package. A TPRGED warning in a log is worth fixing on sight: it means some by-name lookup in the bench is a coin toss.

    Watch it run

    You can see registration at work in two places in any bench run. The topology table printed by print_topology lists each component with the string from get_type_name():

    Diagram
    uvm_test_top               dma_smoke_test          -     @172
      env                      dma_env                 -     @242
        apb                    apb_agent               -     @256
          drv                  apb_driver              -     @458

    And +UVM_TESTNAME=dma_drain_test works only because dma_drain_test registered its name string: run_test hands that string to the factory, which finds the proxy by name and builds the test. A test class without a registration macro cannot be selected by name at all.

    What breaks: the wrong macro, and the error far from your code

    Back to the opening. With ` uvm_object_utils(dma_scoreboard_eot) ` on a component, the object registry tries to compile new(name) against a constructor that requires a parent. The error points into uvm_registry.svh or the macro expansion, not at your line, which is why it is confusing. The opposite mistake, uvm_component_utils ` on an item, generates new(name, parent)` against an item constructor that takes only a name, and fails the same way.

    Either way it is a compile error, not a run-time misbehaviour. When you see a registry error mentioning new, look at the macro and the constructor of the class you just touched. A second classic: a class with no macro at all that extends a registered class. That one compiles and silently builds the parent class; it is the inherited type_id trap in Factory advanced.

    "What does uvm_component_utils actually do?"

    A weak answer: "It registers the component with the factory so you can use it."

    A strong answer: "It declares type_id as a uvm_component_registry specialization for the class, adds get_type and get_type_name, and registers the proxy with the factory by type and by name. type_id::create goes through the factory, and the winning class's registry calls new(name, parent), which is why components need that exact constructor and why using the object macro on a component is a compile error. The name string is what +UVM_TESTNAME and by-name overrides use; parameterized classes don't get one."

    Follow-ups: Why do object constructors need a default name? (the generated create calls new() with no argument) and What happens if a derived class forgets its macro? (it inherits the parent's type_id and silently builds the parent).

    Check yourself You write a new sequence item, axis_err_item extends axis_item, add ` uvm_object_utils(axis_err_item) `, and give it function new(string name); super.new(name); endfunction`. Why does it fail to compile?

    The macro's generated create() calls new() with no argument when no name is passed. Without a default value on name, that call has a missing argument. Add a default: function new(string name = "axis_err_item");.

    Check yourself Why can +UVM_TESTNAME=dma_rand_test select a test that is never mentioned in the top module?

    dma_rand_test registered its name string through uvm_component_utils. run_test reads the plusarg and asks the factory to create a component by that name, so any registered test in the compiled code can be selected. The string passed to run_test("dma_smoke_test") in the top module is only the default.

    Check yourself The topology prints drv apb_driver. Where does that string come from, and could it lie?

    From get_type_name(), generated by the macro from the class name. It lies in one case: a derived class with no macro of its own inherits the parent's get_type_name() and reports the parent's name. That is one more reason every class that may be created or printed carries its own macro.

    Next: with registration understood, The type_id::create pattern covers the call side: create for components and objects, and the factory methods underneath.

    Related topics

    • The factory: swapping classes

    • The type_id::create pattern

    • Field macros

    • Components vs objects

    Previous

    46 / 280

    Next: The type_id::create Pattern

    Up next

    Up nextThe type_id::create Pattern

    Sign in to save progress

    Practice this lessonReport an error

    See also

    • Override Resolution: Who Wins
    • Override Timing, Safety and Debug
    • Factory Advanced: Parameterized, Abstract and By-Name