UVM-SystemC Bridge: Verification Architecture
How UVM-SystemC layers standard Universal Verification Methodology on top of the SystemC simulation kernel.
UVM-SystemC Bridge: Verification Architecture
UVM-SystemC brings the Universal Verification Methodology (UVM)—the gold standard for hardware verification in SystemVerilog—directly into the C++ SystemC ecosystem.
While SystemC provides the fundamental discrete-event simulation kernel and modeling paradigms (like TLM), UVM-SystemC provides the verification structure. It introduces standardized architectures for testbenches, phasing, configuration, and reporting, ensuring that verification environments are highly reusable and predictable.
The UVM-SystemC Architecture
According to the Accellera UVM-SystemC Language Reference Manual, the methodology is built on a few core pillars:
uvm_object: The base class for dynamic, transient data (like transactions and sequences).uvm_component: The base class for structural, permanent hierarchy (like drivers, monitors, and scoreboards).- Phasing: A standardized execution flow (
build_phase,connect_phase,run_phase, etc.) that coordinates the entire testbench. - The Factory: A mechanism allowing types to be overridden at runtime, enabling tests to replace generic transactions or components with specialized ones without touching the original source code.
- Configuration DB: A centralized database (
uvm_config_db) for passing parameters and interface handles down the component hierarchy.
Typical Component Hierarchy
A UVM-SystemC environment strictly separates the Design Under Test (DUT) from the verification logic. The testbench hierarchy typically looks like this:
uvm_test: The top-level block. It configures the environment and starts the stimulus (sequences).uvm_env: The environment grouping agents and scoreboards.uvm_agent: A reusable block encapsulating a specific protocol (e.g., AXI, UART).uvm_sequencer: Arbitrates and feeds transactions to the driver.uvm_driver: Translates transactions into pin wiggles or TLM calls.uvm_monitor: Observes the bus and publishes observed transactions.
uvm_scoreboard: Compares observed transactions against expected behavior.
Complete Example: The Top-Level UVM Architecture
The following complete, compilable example demonstrates the absolute baseline architecture of a UVM-SystemC simulation. It defines a component hierarchy, implements the standard UVM phases, and uses uvm::run_test() to bootstrap the verification environment inside sc_main.
#include <systemc>
#include <uvm>
// 1. A dummy Driver Component
class my_driver : public uvm::uvm_driver<uvm::uvm_sequence_item> {
public:
UVM_COMPONENT_UTILS(my_driver); // Register with the UVM factory
my_driver(uvm::uvm_component_name name) : uvm::uvm_driver<uvm::uvm_sequence_item>(name) {}
// The run_phase consumes time and drives the simulation
void run_phase(uvm::uvm_phase& phase) override {
UVM_INFO("DRIVER", "Driver is starting execution...", uvm::UVM_LOW);
// Raise an objection to prevent the simulation from ending
phase.raise_objection(this);
sc_core::wait(100, sc_core::SC_NS);
UVM_INFO("DRIVER", "Driving transaction 1...", uvm::UVM_LOW);
sc_core::wait(100, sc_core::SC_NS);
UVM_INFO("DRIVER", "Driving transaction 2...", uvm::UVM_LOW);
// Drop objection to allow simulation to finish
phase.drop_objection(this);
}
};
// 2. An Agent grouping the driver
class my_agent : public uvm::uvm_agent {
public:
UVM_COMPONENT_UTILS(my_agent);
my_driver* driver;
my_agent(uvm::uvm_component_name name) : uvm::uvm_agent(name), driver(nullptr) {}
// The build_phase constructs children top-down
void build_phase(uvm::uvm_phase& phase) override {
uvm::uvm_agent::build_phase(phase);
UVM_INFO("AGENT", "Building driver...", uvm::UVM_MEDIUM);
// Create the driver using the UVM Factory
driver = my_driver::type_id::create("driver", this);
}
};
// 3. The Environment grouping agents and scoreboards
class my_env : public uvm::uvm_env {
public:
UVM_COMPONENT_UTILS(my_env);
my_agent* agent;
my_env(uvm::uvm_component_name name) : uvm::uvm_env(name), agent(nullptr) {}
void build_phase(uvm::uvm_phase& phase) override {
uvm::uvm_env::build_phase(phase);
UVM_INFO("ENV", "Building agent...", uvm::UVM_MEDIUM);
agent = my_agent::type_id::create("agent", this);
}
};
// 4. The Top-Level Test
class my_test : public uvm::uvm_test {
public:
UVM_COMPONENT_UTILS(my_test);
my_env* env;
my_test(uvm::uvm_component_name name) : uvm::uvm_test(name), env(nullptr) {}
void build_phase(uvm::uvm_phase& phase) override {
uvm::uvm_test::build_phase(phase);
UVM_INFO("TEST", "Building environment...", uvm::UVM_MEDIUM);
env = my_env::type_id::create("env", this);
}
// Check phase runs after simulation finishes
void check_phase(uvm::uvm_phase& phase) override {
UVM_INFO("TEST", "Performing final end-of-test checks.", uvm::UVM_LOW);
}
};
// 5. The sc_main Entry Point
int sc_main(int argc, char* argv[]) {
// Instead of explicitly instantiating components and calling sc_start(),
// UVM-SystemC takes control of the simulation execution.
// run_test() automatically creates the test component specified by
// the +UVM_TESTNAME command-line argument (or passed dynamically).
// It then automatically executes the UVM phases and invokes sc_start() internally.
uvm::run_test("my_test");
return 0;
}Key Differences from Pure SystemC
- Instantiation: You do not use standard C++
neworSC_MODULEconstructors for the hierarchy. Instead, components are instantiated in thebuild_phaseusing the factory (type_id::create). This is what makes UVM reusable. - Execution Control:
sc_start()is completely hidden.uvm::run_test()manages the SystemC kernel for you. - Objections: The simulation stops automatically when all
run_phaseobjections are dropped, rather than running indefinitely or stopping via a hardcoded time limit.
In the next tutorial, we will explore the uvm_object, the factory, and how to write reusable transaction data structures.
Comments and Corrections