Chapter 10: UVM-SystemC

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.*.

  1. SystemC Callbacks: The singleton uvm_root registers itself with the SystemC kernel's standard callbacks. During SystemC's end_of_elaboration, uvm_root initiates the build_phase, connect_phase, and end_of_elaboration_phase traversals across the UVM component tree.
  2. sc_spawn the Run Phase: When sc_start() is called and SystemC enters the evaluation phase, the UVM phase state machine transitions to the run_phase. To allow multiple components to consume time independently, the UVM kernel iterates over every uvm_component and uses SystemC's dynamic process generation sc_spawn to turn their virtual run_phase() methods into independent SC_THREADs.
  3. Objection State Machine: Objections are implemented as atomic reference counters (uvm_objection). The UVM kernel spawns a dedicated monitor process that sleeps using sc_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 spawned run_phase threads using sc_process_handle::kill(), and transitions the state machine to the extract_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