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:
sc_timestores normalized simulation time.sc_eventowns notification state and knows whether it has immediate, delta, or timed notification pending.sc_simcontextadvances 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
| Notification | When it can wake a process | Typical use |
|---|---|---|
notify() | current evaluation phase | rare; use carefully because it can create immediate loops |
notify(SC_ZERO_TIME) | next delta cycle | model combinational propagation without advancing time |
notify(delay) | future simulation time | timers, 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_TIMEexpress 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