Chapter 7: SystemC 1666-2023 LRM

LRM Bridge: Events, Time, and Scheduler APIs

sc_event, event lists, sc_time, time resolution, sc_start, sc_pause, sc_stop, simulation status, and pending activity.

Events and time form the primary vocabulary for inter-process communication in the SystemC discrete-event scheduler.

sc_event

An sc_core::sc_event is a fundamental synchronization object. Processes can wait on it, and other processes or channels can notify it. Unlike software message queues, events do not queue or hold data. If a process is not actively waiting on an event when it is notified, that notification is permanently missed.

The LRM defines three notification types:

  1. Immediate (notify()): The event triggers in the current evaluation phase.
  2. Delta (notify(SC_ZERO_TIME)): The event triggers in the very next delta cycle.
  3. Timed (notify(10, SC_NS)): The event triggers at a future simulation time.

Event Lists (AND/OR semantics)

The IEEE 1666 standard provides event-list objects to allow processes to wake up on multiple, complex conditions.

  • wait(a | b): Wakes up if event a OR event b triggers.
  • wait(a & b): Suspends until BOTH event a AND event b have triggered.

Time and Simulation Control API

sc_core::sc_time represents absolute simulation time, distinct from physical wall-clock time. You must define the time resolution (e.g., sc_set_time_resolution(1, SC_PS)) before the first sc_time object is constructed, otherwise the kernel will throw a fatal error.

The kernel can be controlled dynamically using:

  • sc_start(sc_time): Runs the simulation until the specified time elapses.
  • sc_pause(): Pauses the simulation and returns control to sc_main().
  • sc_stop(): Permanently halts the simulation. Once stopped, you cannot call sc_start again.

End-to-End Scheduler API Example

This fully compliant sc_main example demonstrates event lists, immediate vs. timed notification, time resolution, and stepping the simulation via sc_start().

#include <systemc>
 
SC_MODULE(SchedulerAPI_Demo) {
    sc_core::sc_event ev_a;
    sc_core::sc_event ev_b;
 
    SC_CTOR(SchedulerAPI_Demo) {
        SC_THREAD(consumer_thread);
        SC_THREAD(producer_thread);
    }
 
    void consumer_thread() {
        while(true) {
            std::cout << "@" << sc_core::sc_time_stamp() 
                      << " delta=" << sc_core::sc_delta_count()
                      << " [Consumer] Waiting for Event A AND Event B..." << std::endl;
            
            // AND event list: Will wake up only after BOTH have fired.
            wait(ev_a & ev_b);
            
            std::cout << "@" << sc_core::sc_time_stamp() 
                      << " delta=" << sc_core::sc_delta_count()
                      << " [Consumer] Woke up! Both events received." << std::endl;
        }
    }
 
    void producer_thread() {
        wait(10, sc_core::SC_NS);
        std::cout << "@" << sc_core::sc_time_stamp() << " [Producer] Notifying Event A (Immediate)" << std::endl;
        ev_a.notify(); // Immediate notification
 
        wait(5, sc_core::SC_NS);
        std::cout << "@" << sc_core::sc_time_stamp() << " [Producer] Notifying Event B (Delta)" << std::endl;
        ev_b.notify(sc_core::SC_ZERO_TIME); // Delta notification
 
        wait(20, sc_core::SC_NS);
        std::cout << "@" << sc_core::sc_time_stamp() << " [Producer] Pausing Simulation." << std::endl;
        sc_core::sc_pause();
    }
};
 
int sc_main(int argc, char* argv[]) {
    // Must be called before any time objects are constructed
    sc_core::sc_set_time_resolution(1, sc_core::SC_PS);
 
    SchedulerAPI_Demo demo("demo");
 
    std::cout << "--- Starting Simulation for 100 ns ---" << std::endl;
    // We can step the simulation in chunks
    sc_core::sc_start(20, sc_core::SC_NS);
    
    std::cout << "--- Back in sc_main. Checking status ---" << std::endl;
    if (sc_core::sc_get_status() == sc_core::SC_PAUSED) {
        std::cout << "Simulation was paused. Resuming..." << std::endl;
        sc_core::sc_start(); // Continue indefinitely until sc_stop() or exhaustion
    }
 
    return 0;
}

Debugging Time Bugs

When diagnosing simulation hangs or zero-time loops, always print both the absolute time and the delta cycle count:

std::cout << sc_core::sc_time_stamp()
          << " delta=" << sc_core::sc_delta_count() << std::endl;

If sc_time_stamp() is constant but sc_delta_count() increments infinitely, you have an un-clocked combinatorial feedback loop (an infinite zero-time scheduling loop), and must insert a timed wait() to break it.

Comments and Corrections