UVM-SystemC Bridge: Components, Phasing, and Configuration
uvm_component hierarchy, build/connect/run behavior, configuration/resource patterns, and test structure.
UVM-SystemC Bridge: Components, Phasing, and Configuration
While SystemC relies on sc_core::sc_module and SC_CTOR to build structural hierarchy, UVM-SystemC introduces the uvm_component class and an advanced, multi-stage Phasing mechanism to orchestrate testbenches uniformly.
UVM Phasing
Standard SystemC has a limited elaboration phase followed directly by simulation (sc_start).
UVM-SystemC expands this significantly to enforce a standard lifecycle for all verification components:
build_phase: Components instantiate their children from the top-down.connect_phase: Components connect their TLM ports, exports, and interfaces from the bottom-up.end_of_elaboration_phase: Final topology checks before simulation starts.start_of_simulation_phase: Printing topologies, configuring banners.run_phase: The only time-consuming phase. Stimulus generation and signal driving happen here.extract_phase: Data is gathered from scoreboards and coverage collectors.check_phase: Final pass/fail assertions are evaluated.report_phase: Test results and logs are dumped to the terminal or file.
By strictly adhering to these phases, UVM components can be plugged together reliably without initialization race conditions.
The Configuration Database (uvm_config_db)
The uvm_config_db is a type-safe, hierarchical registry used to pass configuration data down the component tree. Instead of passing long lists of constructor arguments, a parent component (like a Test) can place a configuration object in the database, and a deeply nested child (like a Driver) can retrieve it during its build_phase.
Complete Example: Phasing and uvm_config_db
This complete example demonstrates the exact execution order of UVM phases and shows how a top-level test configures a child driver's behavior using the uvm_config_db.
#include <systemc>
#include <uvm>
#include <string>
// 1. A Reusable Driver Component
class my_driver : public uvm::uvm_component {
public:
UVM_COMPONENT_UTILS(my_driver);
int max_transactions;
my_driver(uvm::uvm_component_name name)
: uvm::uvm_component(name), max_transactions(1) {} // Default is 1
// Phase 1: Build Phase
void build_phase(uvm::uvm_phase& phase) override {
uvm::uvm_component::build_phase(phase);
UVM_INFO("DRIVER", "build_phase executing...", uvm::UVM_LOW);
// Attempt to retrieve 'max_transactions' from the config DB
// The first argument 'this' provides the hierarchical path context.
if (!uvm::uvm_config_db<int>::get(this, "", "max_transactions", max_transactions)) {
UVM_WARNING("DRIVER", "No config found for max_transactions. Using default.");
} else {
UVM_INFO("DRIVER", "Config retrieved successfully!", uvm::UVM_LOW);
}
}
// Phase 2: Connect Phase
void connect_phase(uvm::uvm_phase& phase) override {
UVM_INFO("DRIVER", "connect_phase executing...", uvm::UVM_LOW);
}
// Phase 5: Run Phase (Time Consuming)
void run_phase(uvm::uvm_phase& phase) override {
phase.raise_objection(this);
UVM_INFO("DRIVER", "run_phase started.", uvm::UVM_LOW);
for (int i = 0; i < max_transactions; ++i) {
sc_core::wait(10, sc_core::SC_NS);
UVM_INFO("DRIVER", "Driving transaction...", uvm::UVM_LOW);
}
phase.drop_objection(this);
}
// Phase 7: Check Phase
void check_phase(uvm::uvm_phase& phase) override {
UVM_INFO("DRIVER", "check_phase executing...", uvm::UVM_LOW);
}
};
// 2. The Top-Level Test
class config_test : public uvm::uvm_test {
public:
UVM_COMPONENT_UTILS(config_test);
my_driver* driver;
config_test(uvm::uvm_component_name name) : uvm::uvm_test(name) {}
void build_phase(uvm::uvm_phase& phase) override {
uvm::uvm_test::build_phase(phase);
UVM_INFO("TEST", "build_phase executing...", uvm::UVM_LOW);
// Place a configuration value into the DB for the driver.
// We target the exact hierarchical path of the driver ("driver").
uvm::uvm_config_db<int>::set(this, "driver", "max_transactions", 3);
// Instantiate the driver AFTER setting the config, so the driver
// can retrieve it during its own build_phase.
driver = my_driver::type_id::create("driver", this);
}
void report_phase(uvm::uvm_phase& phase) override {
UVM_INFO("TEST", "report_phase: Test completed successfully.", uvm::UVM_NONE);
}
};
// 3. Simulation Entry Point
int sc_main(int argc, char* argv[]) {
// Start the UVM test.
// This will automatically sequence the phases:
// build -> connect -> end_of_elaboration -> start_of_simulation -> run -> extract -> check -> report
uvm::run_test("config_test");
return 0;
}Best Practices
- Top-Down Build:
build_phaseexecutes top-down. The parent builds first, allowing it to set configurations in theuvm_config_dbbefore the child'sbuild_phaseruns. - Bottom-Up Connect:
connect_phaseexecutes bottom-up. You must not attempt to send TLM transactions or use ports duringbuild_phasebecause they are not connected yet. - Objections: The
run_phaseoperates concurrently across all components in the hierarchy. The simulation only ends when all components have dropped their objections viaphase.drop_objection(this). Do not forget to drop your objection, or the simulation will hang forever.
Comments and Corrections