Part 1 · Foundations · Intermediate

Components vs Objects: Hierarchy, Lifetime, and Phases

The fundamental split between uvm_component (persistent, phased, tree-resident) and uvm_object (transient data that flows through the tree) — with constructors, lifetimes, and ownership rules.

Two hierarchies, two lifetimes

A UVM testbench is built from exactly two kinds of class. Components form a static tree that exists for the whole simulation: env, agent, driver, monitor, scoreboard, test. Objects are transient data — sequence_items, sequences, and configuration objects — that are created, passed around, and discarded while the components run.

diagram
COMPONENTS (built once at build_phase, live for the whole sim)
  test
   └ env
      └ agent
         ├ driver      <-- a sequence_item OBJECT flows in here each transfer
         ├ monitor
         └ sequencer

OBJECTS (created and destroyed constantly during run_phase)
  my_item #42  ->  my_item #43  ->  my_item #44  ->  ...  (transient)

Side by side in code

systemverilog
// COMPONENT: has a parent, lives in the tree, participates in phases
class my_driver extends uvm_driver #(my_item);
  `uvm_component_utils(my_driver)
  function new(string name, uvm_component parent);  // name + parent
    super.new(name, parent);
  endfunction
  task run_phase(uvm_phase phase);                  // phase callback
    forever begin
      seq_item_port.get_next_item(req);
      // drive req onto the pins ...
      seq_item_port.item_done();
    end
  endtask
endclass

// OBJECT: no parent, no phases, just data + randomization
class my_item extends uvm_sequence_item;
  `uvm_object_utils(my_item)
  rand bit [31:0] addr;
  rand bit [31:0] data;
  rand bit        write;
  function new(string name = "my_item");            // name only
    super.new(name);
  endfunction
endclass
  • Components: env, agent, driver, monitor, sequencer, scoreboard, subscriber, test.

  • Objects: sequence_items (transactions), sequences, config objects, register models.

  • Only components receive phase callbacks; objects are manipulated inside those callbacks.


Lifetime and ownership

Component lifetime

A component is constructed exactly once during build_phase and persists until the simulation ends. Its parent owns it; its full hierarchical name (e.g. uvm_test_top.env.agt.drv) is fixed and is the key used by config_db and the factory.

Object lifetime

An object is created on demand, often thousands of times per test. SystemVerilog's automatic garbage collection reclaims it once no handle refers to it. Because objects are cheap and transient, you clone them rather than share a single handle when you need an independent copy.

systemverilog
my_item original = my_item::type_id::create("original");
my_item snapshot;
original.randomize();
$cast(snapshot, original.clone());   // deep copy — independent lifetime

Choosing the right base class

Use this decision rule whenever you create a new class:

  1. Does it have a fixed place in the testbench and need phases? → uvm_component.

  2. Is it data that flows between components or gets randomized? → uvm_object (usually uvm_sequence_item).

  3. Is it a knob bundle handed down via config_db? → uvm_object (a config class).

Key takeaways

  • If it has a simulation lifecycle role and a tree position, it is a component.

  • If it is payload, configuration, or ephemeral algorithm state, it is an object.

  • Components are built once and live forever; objects are created and discarded constantly.

Common pitfalls

  • Extending uvm_component for pure data bags — use uvm_object or a struct instead.

  • Storing component handles inside sequence_items without a clear ownership story, creating dangling references.

  • Sharing one sequence_item handle across consumers instead of cloning, so a later mutation corrupts earlier data.