UVM Phasing, Objections, and the SystemC Kernel
How UVM phases run on top of SystemC, why objections control phase completion, and how run-time behavior maps to processes.
How to Read This Lesson
UVM-SystemC is methodology in C++ clothing. Keep the verification intent in view: reusable components, controlled stimulus, reporting, and phase-aware execution.
UVM Phasing, Objections, and the SystemC Kernel
UVM phasing gives a verification environment a shared lifecycle. SystemC gives it the simulation kernel. UVM-SystemC sits on top of SystemC and organizes verification behavior into phases. According to the IEEE 1800.2 standard, the UVM phasing mechanism is a sophisticated state machine layered over the native SystemC scheduler.
Source and LRM Trail
For UVM-SystemC, use Docs/LRMs/uvm-systemc-language-reference-manual.pdf as the methodology contract. In source, inspect .codex-src/uvm-systemc/src/uvmsc: components, phases, factory macros, sequences, sequencers, TLM ports, reporting, and configuration helpers.
Phase Families
Common phase ideas include:
- build
- connect
- end of elaboration
- start of simulation
- run
- extract
- check
- report
Build and connect are structural. Run is time-consuming. Check and report summarize results.
Under the Hood: C++ Implementation in Accellera UVM-SystemC
The bridge between UVM phasing and the SystemC kernel is a fascinating piece of engineering found in src/uvmsc/phasing/uvm_phase.* and src/uvmsc/base/uvm_root.*.
- SystemC Callbacks: The singleton
uvm_rootregisters itself with the SystemC kernel's standard callbacks. During SystemC'send_of_elaboration,uvm_rootinitiates thebuild_phase,connect_phase, andend_of_elaboration_phasetraversals across the UVM component tree. sc_spawnthe Run Phase: Whensc_start()is called and SystemC enters the evaluation phase, the UVM phase state machine transitions to therun_phase. To allow multiple components to consume time independently, the UVM kernel iterates over everyuvm_componentand uses SystemC's dynamic process generationsc_spawnto turn their virtualrun_phase()methods into independentSC_THREADs.- Objection State Machine: Objections are implemented as atomic reference counters (
uvm_objection). The UVM kernel spawns a dedicated monitor process that sleeps usingsc_core::wait()on an event that fires whenever an objection drops to zero. When all objections hit zero, this monitor process wakes up, forcibly terminates all spawnedrun_phasethreads usingsc_process_handle::kill(), and transitions the state machine to theextract_phase.
Objections
Because the run_phase executes concurrently in every component across the hierarchy (using sc_spawn under the hood), UVM requires a mechanism to determine when the entire simulation is "done".
Objections prevent a run-time phase from ending while useful activity is still happening. If every component drops its objection, the phase can complete.
Complete Objections Example
Below is a fully compilable sc_main example showing two components running concurrently in the run_phase. Notice how the test waits until both components drop their objections before moving to the report_phase.
#include <systemc>
#include <uvm>
// 1. Fast Component: Finishes its work quickly
class fast_worker : public uvm::uvm_component {
public:
UVM_COMPONENT_UTILS(fast_worker);
fast_worker(uvm::uvm_component_name name) : uvm::uvm_component(name) {}
void run_phase(uvm::uvm_phase& phase) override {
phase.raise_objection(this);
UVM_INFO("FAST", "Starting fast work (takes 20 ns)", uvm::UVM_LOW);
sc_core::wait(20, sc_core::SC_NS);
UVM_INFO("FAST", "Finished fast work, dropping objection", uvm::UVM_LOW);
phase.drop_objection(this);
}
};
// 2. Slow Component: Keeps the simulation alive longer
class slow_worker : public uvm::uvm_component {
public:
UVM_COMPONENT_UTILS(slow_worker);
slow_worker(uvm::uvm_component_name name) : uvm::uvm_component(name) {}
void run_phase(uvm::uvm_phase& phase) override {
phase.raise_objection(this);
UVM_INFO("SLOW", "Starting slow work (takes 100 ns)", uvm::UVM_LOW);
sc_core::wait(100, sc_core::SC_NS);
UVM_INFO("SLOW", "Finished slow work, dropping objection", uvm::UVM_LOW);
phase.drop_objection(this);
}
};
// 3. Test Environment
class my_test : public uvm::uvm_test {
public:
fast_worker* fast;
slow_worker* slow;
UVM_COMPONENT_UTILS(my_test);
my_test(uvm::uvm_component_name name) : uvm::uvm_test(name) {}
void build_phase(uvm::uvm_phase& phase) override {
uvm::uvm_test::build_phase(phase);
fast = fast_worker::type_id::create("fast", this);
slow = slow_worker::type_id::create("slow", this);
}
void report_phase(uvm::uvm_phase& phase) override {
// This is called automatically in zero-time AFTER all run_phase
// objections have been dropped.
UVM_INFO("TEST", "Report Phase reached. Total simulation time: "
+ sc_core::sc_time_stamp().to_string(), uvm::UVM_LOW);
}
};
int sc_main(int argc, char* argv[]) {
uvm::run_test("my_test");
return 0;
}Source Implementation Shape
Useful official source paths include:
src/uvmsc/phasing/uvm_phase.*src/uvmsc/phasing/uvm_common_phases.*src/uvmsc/phasing/uvm_objection.*src/uvmsc/base/uvm_root.*
The phasing system traverses component hierarchy and calls phase callbacks. Run-time phases interact with SystemC processes and simulation time.
Common Mistake
Starting a sequence without raising an objection can let the phase end too early.
Raising an objection and never dropping it can make the simulation appear hung. This happens frequently if an exception is thrown or a state machine gets deadlocked before reaching the drop_objection call.
Both failures are lifecycle bugs, not TLM bugs.
Best Practice
Raise objections at the level that owns the activity (e.g., inside sequences or tests, rather than deep in monitors). Drop them in all exit paths. Use reports around objections when debugging phase completion.
For small examples, keep the objection pattern obvious rather than clever.
Comments and Corrections