Oct 7, 2026

UVM : UVM_Component

UVM Component Study

UVM Component Study

A Comprehensive Guide to the Building Blocks of UVM Verification

1. What is a UVM Component?

A UVM component is the fundamental building block of a UVM testbench. It represents a structural element of the verification environment that has:

  • A lifecycle (managed by phases).
  • Access to the configuration database.
  • The ability to interact with other components via factory overrides and communication interfaces.
Key Rule: Components are structural and control elements. They do not carry data. Data is carried by UVM Objects (e.g., transactions, sequences).

2. UVM Component Hierarchy

The UVM class library provides several specialized component classes. The basic hierarchy looks like this:

uvm_component (abstract base) ├── uvm_env │ ├── uvm_scoreboard │ ├── uvm_coverage_collector │ └── ... ├── uvm_agent │ ├── uvm_sequencer │ ├── uvm_driver │ ├── uvm_monitor │ └── uvm_scoreboard (optional) ├── uvm_subscriber └── uvm_test

Core Component Classes

Class Purpose
uvm_component Base class for all UVM components. Abstract; cannot be instantiated directly.
uvm_env Container for agents, scoreboards, and other components. Represents the verification environment.
uvm_agent Encapsulates a self-contained verification IP (driver, sequencer, monitor, optional scoreboard).
uvm_sequencer Receives sequences and passes them to the driver.
uvm_driver Drives the DUT interface based on items from the sequencer.
uvm_monitor Monitors the DUT interface and broadcasts observed transactions.
uvm_scoreboard Compares expected vs. actual results.
uvm_test Top-level component that configures the environment and initiates the test.

3. UVM Component Lifecycle (Phases)

UVM components follow a strict phase-based execution model. The main phases are:

Phase Description
build_phase Instantiate and configure sub-components. Configure the configuration database.
connect_phase Connect components (e.g., connect analyzers to scoreboard).
end_of_elaboration_phase Final setup after all components are built and connected.
start_of_simulation_phase Pre-run setup (e.g., reset signals).
run_phase Main simulation phase. Can be forked into sub-phases (pre_reset, reset, post_reset, main, shutdown, etc.).
extract_phase Post-simulation analysis (e.g., collect coverage).
check_phase Verify final DUT state or results.
report_phase Generate final reports (e.g., number of mismatches).
final_phase Clean up (e.g., close files).

Phase Execution Order

  1. Top-down for build_phase and connect_phase.
  2. Bottom-up for end_of_elaboration_phase, start_of_simulation_phase, and all subsequent phases.
Important: The run_phase is concurrent. All components enter run_phase simultaneously and run until the test ends.

4. Key Features of UVM Components

4.1 Name and Parent

Every component has a unique name in the hierarchy, set during instantiation:

my_driver = my_driver::type_id::create("my_driver", this);

The name is used for:

  • Logging (via uvm_info, uvm_error, etc.)
  • Configuration database lookup
  • Hierarchical queries

4.2 Factory

UVM components are created via the factory mechanism, which allows for runtime overrides:

// Override a component in the factory
uvm_config_db#(uvm_object_wrapper)::set(null, "*", "type_override", my_driver::get_type());

// Or use a factory override
my_driver = my_driver::type_id::create("my_driver", this); // Uses factory

Factory overrides are critical for:

  • Replacing a driver with a debug driver
  • Substituting a scoreboard with a different implementation
  • Enabling/disabling components via configuration

4.3 Configuration Database (uvm_config_db)

The uvm_config_db is a type-safe, hierarchical database for passing configuration data from parent to child components.

Common Uses:

  • Passing virtual interfaces
  • Passing parameters (e.g., number of ports, latency)
  • Enabling/disabling features

Example: Setting a Virtual Interface

// In the top-level test or env
virtual my_if vif;

function void build_phase(uvm_phase phase);
    super.build_phase(phase);
    uvm_config_db#(virtual my_if)::set(this, "*", "vif", vif);
endfunction
// In the driver
virtual my_if vif;

function void build_phase(uvm_phase phase);
    super.build_phase(phase);
    if (!uvm_config_db#(virtual my_if)::get(this, "", "vif", vif))
        `uvm_fatal(get_type_name(), "Failed to get virtual interface")
endfunction

Key Methods:

  • uvm_config_db#(T)::set(scope, inst_name, field_name, value)
  • uvm_config_db#(T)::get(scope, inst_name, field_name, value)
  • uvm_config_db#(T)::exists(scope, inst_name, field_name)

4.4 Reporting

Components use the UVM reporting mechanism to log messages:

`uvm_info(get_type_name(), "Simulation started", UVM_LOW)
`uvm_error(get_type_name(), "Invalid transaction")
`uvm_fatal(get_type_name(), "Critical error in driver")

4.5 Analysis Ports and Export

Components can communicate via analysis ports (broadcast) and analysis exports (subscribe).

// In monitor
uvm_analysis_port #(my_txn) ap;

function void new(string name, uvm_component parent);
    super.new(name, parent);
    ap = new("ap", this);
endfunction

task run_phase(uvm_phase phase);
    // ... monitor logic ...
    ap.write(txn); // Broadcast transaction
endtask
// In scoreboard
uvm_analysis_imp #(my_txn, my_scoreboard) imp;

function void new(string name, uvm_component parent);
    super.new(name, parent);
    imp = new("imp", this);
endfunction

function void write(my_txn t);
    // Handle received transaction
endfunction

In connect_phase:

function void connect_phase(uvm_phase phase);
    super.connect_phase(phase);
    agent.monitor.ap.connect(scoreboard.imp);
endfunction

5. Common Component Patterns

5.1 Agent

An agent encapsulates a DUT interface and typically contains:

  • Driver: Drives the interface.
  • Sequencer: Interfaces with sequences.
  • Monitor: Monitors the interface.
  • Scoreboard (optional): If the agent is responsible for checking its own IP.
class my_agent extends uvm_agent;
    my_driver     drv;
    my_sequencer  sqr;
    my_monitor    mon;

    function void build_phase(uvm_phase phase);
        super.build_phase(phase);
        if (is_active == UVM_ACTIVE) begin
            drv = my_driver::type_id::create("drv", this);
            sqr = my_sequencer::type_id::create("sqr", this);
        end
        mon = my_monitor::type_id::create("mon", this);
    endfunction

    function void connect_phase(uvm_phase phase);
        super.connect_phase(phase);
        if (is_active == UVM_ACTIVE)
            sqr.seq_item_export.connect(drv.seq_item_fifo);
    endfunction
endclass

5.2 Environment

The environment is the top-level container that holds agents, scoreboards, and coverage collectors.

class my_env extends uvm_env;
    my_agent     agent;
    my_scoreboard scoreboard;
    my_coverage coverage;

    function void build_phase(uvm_phase phase);
        super.build_phase(phase);
        agent = my_agent::type_id::create("agent", this);
        scoreboard = my_scoreboard::type_id::create("scoreboard", this);
        coverage = my_coverage::type_id::create("coverage", this);
    endfunction

    function void connect_phase(uvm_phase phase);
        super.connect_phase(phase);
        agent.mon.ap.connect(scoreboard.imp);
        agent.mon.ap.connect(coverage.imp);
    endfunction
endclass

5.3 Test

The test is the entry point of the simulation. It creates the environment and starts the simulation.

class my_test extends uvm_test;
    my_env env;

    function void build_phase(uvm_phase phase);
        super.build_phase(phase);
        env = my_env::type_id::create("env", this);
    endfunction

    function void connect_phase(uvm_phase phase);
        super.connect_phase(phase);
        // Additional top-level connections
    endfunction
endclass

6. Best Practices

  1. Use type_id::create for instantiation:
    my_driver = my_driver::type_id::create("my_driver", this);
    Never use new for UVM components.
  2. Keep components lightweight:
    • Components should handle control and configuration.
    • Data should be passed via objects (transactions).
  3. Use uvm_config_db for configuration:
    • Avoid global variables or direct member access for configuration.
    • Use unique field names.
  4. Phase discipline:
    • Do heavy work in run_phase, not build_phase.
    • Use fork-join in run_phase for parallel tasks.
  5. Logging:
    • Use uvm_info, uvm_warning, uvm_error, uvm_fatal appropriately.
    • Use UVM_HIGH for verbose debugging, UVM_LOW for critical info.
  6. Factory Overrides:
    • Design components to be replaceable.
    • Use factory overrides for debugging or alternate implementations.
  7. Analysis Ports:
    • Use analysis ports for broadcast communication (monitor → scoreboard/coverage).
    • Use TLM sockets for more complex communication (e.g., FIFO, interface).
  8. Avoid Cyclic Dependencies:
    • Ensure the component hierarchy is a tree, not a graph.
    • Use analysis ports/exports to decouple components.

7. Debugging Components

7.1 UVM Phase Trace

To see which phase is executing:

`uvm_info("PHASE", $sformatf("In %s phase", phase.get_name()), UVM_MEDIUM)

7.2 Configuration Database Debug

To check what is in the config DB:

uvm_config_db#(virtual my_if)::exists(this, "", "vif");

7.3 Component Hierarchy

To print the component hierarchy:

`uvm_info("HIERARCHY", "Printing component hierarchy", UVM_LOW)
uvm_top.print_topology();

7.4 Sequence/Driver Debug

  • Use sqr.get_seq_item() and sqr.item_done() to step through sequences.
  • Use drv.req_port.try_next_item() to inspect driver state.

8. Common Pitfalls

Pitfall Solution
Forgetting to call super.build_phase() Always call super in all phase methods.
Incorrect type_id::create name Use a unique, descriptive name.
Configuration not set before build_phase Set config in the parent's build_phase before creating children.
Mixing up uvm_object and uvm_component Components are structural; objects are data.
Hardcoding configurations Use uvm_config_db and factory overrides.
Ignoring is_active in agents Check is_active before creating driver/sequencer.

9. Summary

  • UVM Components are the structural backbone of the verification environment.
  • They follow a strict phase-based lifecycle.
  • They communicate via the configuration database, analysis ports, and TLM.
  • They are created via the factory to enable flexibility and overrides.
  • Agents, Environments, and Tests are the most common component types.
  • Best practices include using type_id::create, proper configuration, and clean phase discipline.

Understanding UVM components thoroughly is essential for building scalable, maintainable, and reusable verification environments.

UVM Component Study Guide © 2023

No comments:

Post a Comment

SiteMap