Skip to content

UVM

uvm_component

The base class for persistent structure — hierarchy, phasing, configuration, and reporting added over uvm_object, the (name, parent) constructor, and why structure extends it.

UVM Base Classes · Module 4 · Page 4.3

The Engineering Problem

The previous chapter covered uvm_object — the base class for data. This one covers its counterpart, uvm_component — the base class for structure. Where a uvm_object is transient data that flows through the testbench, a uvm_component is a permanent node of the testbench: the test, the env, the agent, the driver, the monitor, the scoreboard — every structural piece you've built extends uvm_component.

The engineering question is precise: what does uvm_component add over uvm_object? Because a component is an object (it inherits the whole data toolkit), but it adds four things that make it structural rather than data: a place in the hierarchy (a parent, children, a full name), participation in phasing (build, connect, run…), integration with the configuration database (scoped to its path), and reporting (messages tagged with its name). And it adds a fifth, implicit property: it persists for the entire simulation, while objects come and go. Getting the distinction right is foundational — it determines which base class you extend, which constructor you write, and whether a piece participates in phasing at all. Extend the wrong one, or write an object's constructor on a component, and it won't build.

What is uvm_component, what does it add over uvm_object (hierarchy, phasing, configuration, reporting, lifetime), and why does everything structural extend it — including the constructor that must differ from an object's?

Motivation — why structure needs a different base class

The four additions of uvm_component are exactly what a structural node requires and a data object must not have:

  • Hierarchy makes it addressable and composable. A component has a parent and children and a full path (Module 3.3), so it can be configured, found, and composed into a tree. Data has no business in the tree; structure has no business without one. The hierarchy is the difference.
  • Phasing makes it execute. Components are phased — the framework calls build_phase, connect_phase, run_phase on every component in the tree. This is what makes a component do things in an orchestrated order; objects have no phases and never execute on their own.
  • Configuration makes it adaptable. A component participates in the config database scoped to its path, so the test can configure it from above (Module 3.5). This is the mechanism of reuse, and it is keyed on the component's place in the hierarchy.
  • Reporting makes it accountable. A component is a uvm_report_object, so its messages ( `uvm_info, `uvm_error, `uvm_fatal) are tagged with its name and routed through UVM's reporting system — which is how you trace which of dozens of components produced a message.

The motivation, in one line: data and structure are fundamentally different kinds of thing, so they have different base classes — and uvm_component is the one that gives a piece a place in the tree, a phased lifecycle, configurability, and a reporting identity, which is precisely what a structural node needs and a data object must not have.

Mental Model

Hold uvm_component as a building with an address, a schedule, and a nameplate:

A uvm_component is a uvm_object that has moved into the building permanently and gotten an address, a daily schedule, a mailbox, and a nameplate. It still has the object's data toolkit (it inherits copy/compare/print, though it rarely uses them), but now it also has: an address (its place in the hierarchy — a parent, a full path), a schedule (phases — it shows up for build, then connect, then run, in lockstep with every other component), a mailbox (the config database — the test sends it settings scoped to its address), and a nameplate (reporting — every message it sends is signed with its name). And unlike the transient objects that visit (transactions passing through), the component lives there for the whole simulation. A data object is a visitor; a component is a resident.

So the model for choosing a base class is: is this a permanent resident of the testbench (a node that builds, connects, runs, and persists) or a transient visitor (data that flows through)? Residents extend uvm_component; visitors extend uvm_object. And residents get the two-argument constructor — a name and the address (parent) they move into.

Visual Explanation — what uvm_component adds over uvm_object

uvm_component inherits everything from uvm_object and layers structural capabilities on top. The picture is one of addition: the data toolkit at the base, the structural machinery above it.

uvm_component layers hierarchy, phasing, configuration, and reporting over the inherited uvm_object data toolkitWhat uvm_component adds over uvm_objectWhat uvm_component adds over uvm_objectReportinguvm_info / uvm_warning / uvm_error / uvm_fatal — messages tagged with the component's nameuvm_info / uvm_warning / uvm_error / uvm_fatal — messages tagged with the component's nameConfigurationparticipates in the config database, scoped to its hierarchical path — configured from aboveparticipates in the config database, scoped to its hierarchical path — configured from abovePhasingbuild_phase, connect_phase, run_phase, … — the framework phases every component in the treebuild_phase, connect_phase, run_phase, … — the framework phases every component in the treeHierarchyparent, children, get_full_name — a place in the tree; persists for the whole simulationparent, children, get_full_name — a place in the tree; persists for the whole simulationuvm_object (inherited)the data toolkit — copy/compare/print/pack — present but rarely used by componentsthe data toolkit — copy/compare/print/pack — present but rarely used by components
Figure 1 — uvm_component = uvm_object + structural machinery. The base is the uvm_object data toolkit (copy/compare/print/pack), inherited but rarely used by components. On top, uvm_component adds hierarchy (parent, children, full name), phasing (build/connect/run/…), configuration (the config database scoped to its path), and reporting (uvm_info/error/fatal tagged with its name) — plus the implicit property of persisting for the whole simulation. Everything structural extends this layered class.

Read the stack as inheritance plus addition. The base (greyed) is the uvm_object data toolkit — a component has it but a driver almost never copies or compares itself. Above it, the four structural layers are what make it a component: hierarchy (it's a node with an address), phasing (it executes in orchestrated phases), configuration (the test adapts it from above), and reporting (it has a logging identity). The implicit fifth property — persistence — runs through all of them: because it's in the tree and phased, it exists from build_phase to the end of simulation. This is the precise answer to "what does uvm_component add": four capabilities and a lifetime, layered over the inherited object.

RTL / Simulation Perspective — a component uses all four additions

A single component touches all four structural capabilities, and the code shows each — including the constructor that distinguishes a component from an object.

uvm_component — hierarchy, phasing, configuration, reporting
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
class my_driver extends uvm_component;       // a component (uvm_driver extends uvm_component too)
  `uvm_component_utils(my_driver)            // factory registration for a COMPONENT
  virtual my_if vif;
 
  // CONSTRUCTOR: name AND parent — this is the component signature (objects take name only)
  function new(string name, uvm_component parent);
    super.new(name, parent);                 // hand the parent up → placed in the tree
  endfunction
 
  // PHASING: the framework calls this (objects have no phases)
  function void build_phase(uvm_phase phase);
    super.build_phase(phase);
    // CONFIGURATION: the config database, scoped to THIS component's path
    if (!uvm_config_db#(virtual my_if)::get(this, "", "vif", vif))
      `uvm_fatal("NOVIF", "virtual interface not set")   // REPORTING: tagged with this component
    // HIERARCHY: ask the component its own address
    `uvm_info("DRV", $sformatf("built at %s", get_full_name()), UVM_LOW)
  endfunction
 
  task run_phase(uvm_phase phase);
    // ... persists and runs for the whole simulation ...
  endtask
endclass

Every line maps to one of the additions. The constructor new(string name, uvm_component parent) is the component signature — note the parent argument, absent from an object's constructor, which is what places the component in the tree via super.new(name, parent). Phasing: build_phase and run_phase are framework-called methods an object doesn't have. Configuration: uvm_config_db::get(this, ...) retrieves a setting scoped to this component's path. Reporting: `uvm_fatal and `uvm_info emit messages tagged with this component (it's a uvm_report_object). Hierarchy: get_full_name() returns its address in the tree. The factory registration is `uvm_component_utils (note: component, not object). One class, all four structural capabilities — which is what being a uvm_component means.

Verification Perspective — component vs object, side by side

The clearest way to understand uvm_component is directly against uvm_object: same library, opposite roles. The contrast determines which base class you extend for anything you build.

Comparison of uvm_object (transient data, no phases, 1-arg constructor) and uvm_component (persistent structure, phased, configurable, 2-arg constructor)used forused foruvm_objecttransient data · no tree · no phases· new(name)transactions, configs,sequencescreated, used, discardeduvm_componentpersistent structure · in the tree ·phased · configurable · new(name,parent)test, env, agent, driver,monitor, scoreboardbuilt once, persist all sim12
Figure 2 — uvm_component versus uvm_object, the two halves of the class library. uvm_object: transient data, no hierarchy, no phases, a 1-argument constructor (name), used for transactions/configs/sequences. uvm_component: persistent structure, a place in the tree, phased, configurable, a reporting identity, a 2-argument constructor (name, parent), used for test/env/agent/driver/monitor/scoreboard. Choosing a base class is choosing which column a thing belongs in.

This side-by-side is the decision you make every time you write a class. If the thing is data — it carries values, flows through the environment, gets randomised/copied/compared, and is discarded — it extends uvm_object (or a data subtype like uvm_sequence_item), takes a one-argument constructor new(string name = "..."), and has no phases. If the thing is structure — it occupies a place in the testbench, builds children, connects, runs, is configured from above, and persists — it extends uvm_component (or a subtype like uvm_driver), takes the two-argument constructor new(string name, uvm_component parent), and is phased. The two columns never blur: a transaction is never phased, a driver is never randomised. Picking the base class is picking the column, and the column dictates the constructor, the lifecycle, and the capabilities.

Runtime / Execution Flow — a component's phased lifetime

A component's defining behaviour over time is its phased lifetime: it is constructed in build_phase, wired in connect_phase, runs in run_phase, reports in report_phase, and exists continuously throughout — a sharp contrast with an object's create-use-discard.

Component lifetime: build_phase constructs, connect_phase wires, run_phase executes, report_phase reports, persisting throughoutBuilt once, phased, persistentBuilt once, phased, persistent1build_phase — constructedcreated with its parent (placed in the tree); builds its ownchildren. Zero time.2connect_phase — wiredits ports/exports connected to others. Still zero time; thestructure is now complete.3run_phase — executes & persistsdoes its work over simulation time; the component is alivethroughout the run.4report_phase — reportsemits results, tagged with its name; then teardown. It existed frombuild to end.
Figure 3 — a component's phased lifetime. Constructed in build_phase (with its parent, placing it in the tree), wired in connect_phase, executing in run_phase (where it persists and does its work), and reporting in report_phase. Throughout, the component exists continuously — it is built once and lives until the simulation ends. This phased, persistent lifecycle is what uvm_component adds; a uvm_object, by contrast, is simply created, used, and discarded with no phases.

This lifetime is the runtime meaning of "persistent structure." Unlike an object, which is created on demand and dropped when no longer referenced, a component is created once at build_phase and remains — phased through connect, run, and report, present the entire time. That persistence is why a component can hold long-lived state (a driver's interface handle, a scoreboard's expected queue), participate in connections (it must exist for the whole run to send and receive), and accumulate results to report at the end. The phased lifecycle also enforces the construct-then-connect-then-run ordering (Module 2.6): because the framework phases the whole tree of persistent components in lockstep, every component is built before any is connected, and connected before any runs. The component's phased, persistent lifetime is the structural backbone the transient objects flow through.

Waveform Perspective — the resident and the visitors

The component-versus-object distinction is visible in time: the component is alive the whole simulation while transaction objects flow through it and are discarded. The resident persists; the visitors come and go.

The component persists for the whole run; transaction objects come and go

10 cycles
The component persists for the whole run; transaction objects come and goThe component (uvm_component) is alive for the ENTIRE simulation — persistent structureThe component (uvm_com…transaction objects (uvm_object) flow through it and are discarded — transient datatransaction objects (u…the resident persists; the visitors come and go — component vs object, in timethe resident persists;…clkcomp_alivetxnvaliddata00A0A100B0B100000000t0t1t2t3t4t5t6t7t8t9
Figure 4 — the component (built in build_phase) is alive for the entire simulation — its existence spans the whole trace. Transaction objects, by contrast, flow through it transiently: each is created, used to drive the pins, and discarded (the txn pulses). The persistent resident (uvm_component) and the transient visitors (uvm_object) are the two halves of the class library, seen on one timeline — structure that endures, data that passes through.

The contrast between comp_alive (high the whole trace) and txn (brief pulses) is the entire chapter on one timeline. The component is the resident: built once, it persists from build_phase to the end, holding state and participating in connections throughout — which is why uvm_component needs hierarchy, phasing, configuration, and reporting, all of which are about a long-lived participant in the testbench. The transactions are the visitors: each uvm_object is created, used to drive the pins, and dropped, needing only the data toolkit. Seeing them together makes the base-class choice obvious: anything that must persist and participate (hold state, connect, be phased) is a uvm_component; anything that merely carries data through is a uvm_object.

DebugLab — the component that wouldn't build (wrong constructor)

A component with an object's constructor — the factory couldn't create it

Symptom

An engineer wrote a new monitor and, copying the pattern from a transaction class they'd just written, gave it the constructor function new(string name = "my_monitor"); super.new(name); endfunction. It would not work: depending on the simulator it failed to compile, or the factory's type_id::create("mon", this) errored because it could not pass the parent. The class looked like a component — it extended uvm_monitor, had `uvm_component_utils — but it could not be created in the tree.

Root cause

The wrong constructor signature — an object's constructor on a component. A uvm_component must take a name and a parent and pass both to super.new; the engineer used the object form (name only):

object constructor vs component constructor
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
uvm_object   constructor:  function new(string name = "x");          super.new(name);
uvm_component constructor:  function new(string name, uvm_component parent);  super.new(name, parent);
 
what was written (on a component):  new(string name = "my_monitor"); super.new(name);   // OBJECT form
the factory does:                   type_id::create("mon", this);    // needs new(name, PARENT)
result:                             no matching 2-arg constructor → create fails / won't compile
fix:                                new(string name, uvm_component parent); super.new(name, parent);

The factory creates a component by calling its two-argument constructor with the name and parent, so a component must provide new(string name, uvm_component parent) and pass the parent to super.new. The single-argument object constructor has no parent to place the component in the tree, so the framework cannot construct it as a component at all.

Diagnosis

The tell is a class that extends a component base yet fails to be created — a constructor mismatch. Diagnose base-class/constructor problems by checking the signature against the base:

  1. Match the constructor to the base class. A uvm_component (or any subtype: driver, monitor, agent, env, test) needs new(string name, uvm_component parent); a uvm_object (or subtype) needs new(string name = "..."). A 1-arg constructor on a component is the bug.
  2. Confirm super.new passes the parent. Even with the right signature, super.new(name) (dropping the parent) fails to place the component. It must be super.new(name, parent).
  3. Read the _utils macro. `uvm_component_utils expects a component constructor; `uvm_object_utils expects an object one. A mismatch between the macro and the constructor signals copied-from-the-wrong-base code.
Prevention

Match the base class, its constructor, and its utils macro — they go together:

  1. Components take (name, parent); objects take (name). Memorise the two constructor forms and use the right one for the base class you extend — super.new(name, parent) for components, super.new(name) for objects.
  2. Don't copy a constructor across the object/component line. When you reuse a template, check whether the source was a component or an object; the constructor and the _utils macro must both match the base you're extending.
  3. Let the tooling/snippets default correctly. Use editor snippets or templates that pair the right base class, constructor, and `uvm_*_utils macro, so the signature is correct by construction.

The one-sentence lesson: a uvm_component constructor takes a name and a parent and passes both to super.new — using an object's name-only constructor on a component means the factory cannot place it in the tree, so it won't build.

Common Mistakes

  • Object constructor on a component. A uvm_component needs new(string name, uvm_component parent) and super.new(name, parent); the object form new(string name="x") makes the factory unable to create it. Match the constructor to the base class.
  • Mismatched _utils macro. `uvm_component_utils for components, `uvm_object_utils for objects. Using the object macro on a component (or vice versa) registers the wrong kind of class and breaks creation.
  • Extending the wrong base for the role. Structure (test, env, agent, driver, monitor, scoreboard) extends uvm_component (or a subtype); data (transactions, configs, sequences) extends uvm_object (or a subtype). Extending uvm_object for structure loses phasing and hierarchy.
  • Forgetting super.build_phase/super.new. Omitting the super call skips base-class setup (placement, phase bookkeeping). Call super.new(name, parent) and super.build_phase(phase).
  • Treating a component like data. Components are not meant to be randomised, copied, or compared as values — that's the object's role. Don't clone a component or put it in an analysis port; pass transactions (objects), not components.
  • Building components outside build_phase. A component's children are created in build_phase (with the parent), so they're phased; constructing them elsewhere or without a parent makes orphans (Module 3.3).

Senior Design Review Notes

Interview Insights

uvm_component is the base class for all persistent structure in UVM — the test, environment, agent, driver, monitor, sequencer, and scoreboard all extend it (directly or via subtypes like uvm_driver). It inherits from uvm_object, so it has the data toolkit (copy, compare, print), but it adds the things that make a thing a structural node: a place in the hierarchy (a parent, children, and a full hierarchical name), participation in phasing (the framework calls build_phase, connect_phase, run_phase, etc.), integration with the configuration database (scoped to its path, so it can be configured from above), and reporting (it's a uvm_report_object, so its messages are tagged with its name). It also persists for the entire simulation — built once and alive throughout — whereas a uvm_object is transient data (a transaction, config, or sequence) that is created, used, and discarded with no hierarchy and no phases. The practical difference shows up immediately in the constructor: a component takes new(string name, uvm_component parent) while an object takes new(string name = "..."). So the rule is: structure that must persist, be phased, and participate in the tree is a uvm_component; transient data is a uvm_object.

Exercises

  1. Object or component? For each, choose the base class and write its constructor signature: (a) a bus transaction; (b) a scoreboard; (c) an agent configuration; (d) a monitor; (e) a sequence.
  2. List the additions. Name the four capabilities uvm_component adds over uvm_object, and for each, one line of code in a driver that uses it.
  3. Fix the constructor. A monitor extends uvm_monitor but has new(string name="mon"); super.new(name); and fails to create. State the root cause and write the corrected constructor.
  4. State placement. A teammate stores a scoreboard's running list of expected transactions as a field of the transaction class. Explain why this is wrong in terms of persistence, and where the list belongs.

Summary

  • uvm_component is the base class for all persistent structure — test, env, agent, driver, monitor, sequencer, scoreboard. It inherits the uvm_object data toolkit and adds the machinery of a structural node.
  • Over uvm_object it adds four capabilities — hierarchy (parent/children/full name), phasing (build/connect/run/…), configuration (the config database scoped to its path), and reporting (messages tagged with its name) — plus a fifth, implicit property: it persists for the whole simulation.
  • Its constructor differs: new(string name, uvm_component parent) (and super.new(name, parent)), because the parent is what places it in the tree — versus an object's new(string name = "..."). The factory creates components via this two-argument constructor, so getting it wrong means it won't build.
  • The component-vs-object choice is the structure-vs-data choice: residents that build, connect, run, hold state, and persist are components; transient data that flows through is objects. The two halves never blur — components aren't randomised, objects aren't phased.
  • The durable rule of thumb: extend uvm_component for anything that must live in the tree, be phased, be configured, and persist — give it the (name, parent) constructor and `uvm_component_utils — and keep it in its lane: structure persists and participates, data flows through.

Next — uvm_transaction: with both halves of the library in view — uvm_object (data) and uvm_component (structure) — the next chapter zooms into the data side, to uvm_transaction: the uvm_object subtype that adds transaction identity and timing, and the base from which sequence items are built.