Signals, Clocks, and Primitive Channels
How sc_signal stores values, delays updates, notifies readers, and models hardware-like behavior.
How to Read This Lesson
Think of this chapter as wiring discipline. Ports, exports, interfaces, and channels are not decorative; they are how the model states its contract before time starts moving.
sc_signal<T> is the everyday channel for value communication. It implements signal interfaces, stores a current value, accepts writes, and notifies readers when the value changes.
Standard and source context
The standard contract lives in Docs/LRMs/SystemC_LRM_1666-2023.pdf around interfaces, ports, exports, primitive channels, hierarchical channels, and predefined channels. The implementation trail is Accellera SystemC GitHub repository: sc_port, sc_export, sc_interface, sc_prim_channel, sc_signal, sc_fifo, and the writer policy helpers.
Read and Write
#include <systemc>
using namespace sc_core;
SC_MODULE(Writer) {
sc_out<int> out{"out"};
SC_CTOR(Writer) { SC_THREAD(run); }
void run() {
out.write(42);
wait(10, SC_NS);
}
};
int sc_main(int, char*[]) {
sc_signal<int> data{"data"};
Writer w("writer");
w.out(data);
data.write(42);
int old_or_new = data.read();
sc_start(20, SC_NS);
return 0;
}The tricky part is timing. A write does not necessarily become visible immediately to all other processes. The signal requests an update from the kernel. During the update phase, the current value changes and value-change events are notified.
Why Delayed Update Exists
Hardware does not usually behave like a sequence of software assignments. If two processes evaluate during the same simulated moment, the final state should not depend on an arbitrary function-call order.
Delayed update gives SystemC a hardware-like discipline:
- Processes evaluate and request channel updates.
- Primitive channels update.
- Events from those updates wake dependent processes.
- More delta cycles run if necessary.
This is the evaluate-update rhythm behind many SystemC semantics.
Clocks
sc_clock is a predefined channel that toggles over time:
#include <systemc>
using namespace sc_core;
SC_MODULE(ClockedModule) {
sc_in<bool> clk{"clk"};
SC_CTOR(ClockedModule) {
SC_METHOD(tick);
sensitive << clk.pos();
dont_initialize();
}
void tick() {
std::cout << "Tick at " << sc_time_stamp() << std::endl;
}
};
int sc_main(int, char*[]) {
sc_clock clk{"clk", 10, SC_NS};
ClockedModule mod("mod");
mod.clk(clk);
sc_start(30, SC_NS);
return 0;
}dont_initialize() prevents the method from running once at time zero before the first triggering edge.
Writer Policies
Signals can enforce writer rules. A common bug is accidentally driving the same signal from multiple processes. SystemC has writer-policy machinery to detect or allow different cases, depending on the signal type and configuration.
Resolved signals exist for cases such as tri-state or multi-driver logic, but you should not use them to hide accidental architecture problems. If a signal has multiple writers, make that a conscious design choice.
Primitive Channels
Signals are primitive channels. They participate directly in the kernel update phase through request_update() and an update() callback. That source-code shape explains why writing a signal from a process is not the same as assigning a C++ variable.
The public API is small. The behavior comes from how the channel cooperates with the scheduler.
Under the Hood: Evaluate and Update Phases in sc_signal
sc_signal<T> inherits from sc_prim_channel. This is crucial for the Evaluate-Update paradigm.
Inside sc_signal, there are two member variables representing state: m_cur_val (current value) and m_new_val (next value).
When a process writes to a signal (sig.write(val)), m_new_val is updated, and the channel calls request_update() (sysc/kernel/sc_simcontext.cpp). This registers the signal in the scheduler's m_update_list.
During the Evaluate phase, all runnable processes execute.
During the Update phase, the scheduler loops over m_update_list and calls update() on each primitive channel. sc_signal::update() assigns m_cur_val = m_new_val and, if the value changed, fires the value_changed_event, which schedules sensitive processes for the next delta cycle.
Can you answer these clearly?
Keep moving when you can answer each question without looking back at the lesson.
Comments and Corrections