Chapter 11: Advanced Core Semantics

Source Deep Dive: sc_time and Timed Event Queues

How SystemC represents time, schedules timed notifications, and advances simulation time without losing precision.

How to Read This Lesson

If delta cycles explain "what happens before time moves," sc_time explains what it means for time to move at all. Read this lesson as the bridge between the LRM scheduling algorithm and the C++ data structures that hold future work.

Source and LRM Trail

Use Docs/LRMs/SystemC_LRM_1666-2023.pdf for time resolution, sc_time, timed notification, and scheduler semantics. In source, inspect .codex-src/systemc/src/sysc/kernel/sc_time.*, sc_event.*, and sc_simcontext.*.

Why SystemC Time Is Not Just a double

Hardware simulation cannot afford floating-point drift. If one process waits for 0.1 ns ten times and another waits for 1 ns once, the simulator must make a deterministic decision about whether those events meet at the same time.

So SystemC represents simulation time using an integer value scaled by a global time resolution. User code can construct time values with units like SC_NS or SC_PS, but internally the simulator compares integer ticks.

The practical consequence: choose time resolution before simulation starts, and do not expect arbitrary real-number precision after the model is elaborated.

Minimal Example: Timed Notification

#include <systemc>
using namespace sc_core;
 
SC_MODULE(TimerDemo) {
  sc_event later;
 
  SC_CTOR(TimerDemo) {
    SC_THREAD(producer);
    SC_THREAD(consumer);
  }
 
  void producer() {
    later.notify(sc_time(10, SC_NS));
  }
 
  void consumer() {
    wait(later);
    std::cout << "woke at " << sc_time_stamp() << "\n";
  }
};
 
int sc_main(int, char*[]) {
  TimerDemo top{"top"};
  sc_start();
  return 0;
}

The producer does not sleep. It inserts a future notification into the kernel's timed-event structure. The consumer sleeps on the event. When no runnable process remains, the scheduler advances time to the earliest timed event and wakes the sensitive process.

How the Source Makes This Work

The implementation has three responsibilities:

  1. sc_time stores normalized simulation time.
  2. sc_event owns notification state and knows whether it has immediate, delta, or timed notification pending.
  3. sc_simcontext advances to the next timed notification only when the runnable queue and update work are empty.

That separation matters. A timed notification cannot interrupt the current evaluation phase. The kernel first completes evaluation/update/delta work at the current timestamp. Only then can physical simulation time advance.

Immediate, Delta, and Timed Notification

NotificationWhen it can wake a processTypical use
notify()current evaluation phaserare; use carefully because it can create immediate loops
notify(SC_ZERO_TIME)next delta cyclemodel combinational propagation without advancing time
notify(delay)future simulation timetimers, clocks, loosely timed delays

If a model depends on exact order between two processes woken at the same timestamp, it is probably relying on non-portable behavior. The LRM defines the scheduling phases, but process order inside a phase is not a design contract.

Review Checklist

  • Is time resolution set before time objects are created?
  • Are timed notifications used only when real simulation time should advance?
  • Would SC_ZERO_TIME express the intent better than a tiny nonzero delay?
  • Does the model avoid relying on ordering between processes woken at the same time?
  • Are local-time tricks in TLM clearly separated from global sc_time_stamp()?

Comments and Corrections