Part 1 · Foundations · Intermediate

The UVM Class Library Map: Base Classes You Will Live In

A guided tour of the UVM base-class tree — uvm_void, uvm_object, uvm_component, and the report/sequence/TLM families — so you always know which class to extend and why.

Why the class tree matters

UVM ships hundreds of classes, but you only extend a dozen of them directly. Knowing the inheritance tree tells you which base class to pick, which methods you inherit for free, and why some classes get phases while others do not.

diagram
uvm_void                      (empty root — exists so everything shares a base)
  +-- uvm_object              data, identity, copy/compare/print/pack
        +-- uvm_transaction
        |     +-- uvm_sequence_item     a single transaction
        |           +-- uvm_sequence    generates streams of items
        +-- uvm_report_object
              +-- uvm_component         persistent, phased tree node
                    +-- uvm_driver
                    +-- uvm_monitor
                    +-- uvm_sequencer
                    +-- uvm_agent
                    +-- uvm_scoreboard
                    +-- uvm_env
                    +-- uvm_test

The single most important fork is between uvm_object and uvm_component. Everything that has a place in the testbench tree and receives phases descends from uvm_component; everything that is data flowing through the tree descends from uvm_object.


uvm_object: identity and data services

Every UVM class ultimately extends uvm_object. It provides the core data services that the field macros and the factory rely on: a name, and the do_copy/do_compare/do_print/do_pack hooks.

systemverilog
class my_cfg extends uvm_object;
  `uvm_object_utils(my_cfg)              // registers + enables factory create

  rand int unsigned num_txns;
  bit              checks_enable;

  function new(string name = "my_cfg");
    super.new(name);                     // uvm_object::new takes ONLY a name
  endfunction
endclass
  • uvm_object::new takes a single string name — there is no parent.

  • Use `uvm_object_utils (or _begin/_end with field macros) to register it.

  • Objects are created, cloned, and garbage-collected freely during the run.


uvm_component: a node in the persistent tree

A uvm_component adds two things on top of uvm_object: a permanent place in the hierarchy (a parent and a full path name) and participation in the phase schedule.

systemverilog
class my_monitor extends uvm_monitor;
  `uvm_component_utils(my_monitor)

  // NOTE the two-argument constructor: name AND parent
  function new(string name, uvm_component parent);
    super.new(name, parent);
  endfunction

  // components get phase callbacks; objects do not
  function void build_phase(uvm_phase phase);
    super.build_phase(phase);
  endfunction
endclass

The tell-tale constructor difference

  • uvm_object: function new(string name = "..."); — name only.

  • uvm_component: function new(string name, uvm_component parent); — name + parent.

  • If you see a two-argument constructor, you are looking at a component.


The specialized component families

Most of the component classes you extend are thin specializations that exist mainly to communicate intent and to provide a couple of convenience members.

  • uvm_driver #(REQ) — adds the seq_item_port used to pull items from a sequencer.

  • uvm_sequencer #(REQ) — arbitrates and hands sequence_items to the driver.

  • uvm_monitor — semantically marks an observe-only component (no extra members).

  • uvm_agent — adds the is_active knob (UVM_ACTIVE / UVM_PASSIVE).

  • uvm_scoreboard / uvm_subscriber #(T) — checking and coverage collection.

  • uvm_env / uvm_test — top-level containers; uvm_test is what run_test() instantiates.

Key takeaways

  • Everything extends uvm_object; components additionally extend uvm_report_object and gain a tree position + phases.

  • Pick uvm_object for data (items, sequences, configs) and uvm_component for structure.

  • The driver/monitor/agent/etc. classes are intent-revealing specializations of uvm_component.

Common pitfalls

  • Calling the wrong super.new signature — name-only on a component, or name+parent on an object, fails to compile.

  • Extending uvm_component for a pure data bag, which drags in phases and a tree slot you never use.

  • Assuming uvm_sequence is a component — it is a uvm_object and has no phases.