Chapter 3: Communication

Signals, Clocks, and Primitive Channels

How sc_signal stores values, delays updates, notifies readers, and models hardware-like behavior.

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.

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:

  1. Processes evaluate and request channel updates.
  2. Primitive channels update.
  3. Events from those updates wake dependent processes.
  4. 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.

Comments and Corrections