LRM Bridge: Predefined Channels
sc_signal, sc_buffer, resolved signals, sc_clock, sc_fifo, sc_mutex, and sc_semaphore as standard communication building blocks.
The IEEE 1666 LRM provides a set of predefined primitive and hierarchical channels that form the standard toolbox for modeling hardware communication.
End-to-End Predefined Channels Example
This complete example demonstrates how sc_signal, sc_clock, sc_fifo, and sc_mutex interoperate to model a multi-producer, clocked FIFO system.
#include <systemc>
SC_MODULE(HardwareSystem) {
// 1. Clock and Signals
sc_core::sc_in<bool> clk;
sc_core::sc_signal<bool> data_ready{"data_ready"};
// 2. FIFO (Hierarchical Channel)
// A thread-safe queue holding up to 4 integers
sc_core::sc_fifo<int> data_fifo{"data_fifo", 4};
// 3. Mutex (Primitive Channel)
// Ensures only one producer accesses the debug port at a time
sc_core::sc_mutex bus_mutex{"bus_mutex"};
SC_CTOR(HardwareSystem) {
SC_THREAD(producer_a);
sensitive << clk.pos();
SC_THREAD(producer_b);
sensitive << clk.pos();
SC_THREAD(consumer);
sensitive << clk.pos();
}
void producer_a() {
wait(2, sc_core::SC_NS); // Wait for initialization
while(true) {
wait(); // Wait for clock
// Lock the mutex before printing to the shared bus/console
bus_mutex.lock();
std::cout << "@" << sc_core::sc_time_stamp() << " [Prod A] Acquired bus lock." << std::endl;
// Blocking write. If FIFO is full, thread suspends here until space is available.
data_fifo.write(0xA);
data_ready.write(true); // Signal an update (Delta Cycle delay)
bus_mutex.unlock();
wait(20, sc_core::SC_NS); // Work delay
}
}
void producer_b() {
wait(5, sc_core::SC_NS);
while(true) {
wait();
bus_mutex.lock();
std::cout << "@" << sc_core::sc_time_stamp() << " [Prod B] Acquired bus lock." << std::endl;
// Non-blocking write. If full, it fails gracefully.
if (data_fifo.nb_write(0xB)) {
data_ready.write(true);
}
bus_mutex.unlock();
wait(15, sc_core::SC_NS);
}
}
void consumer() {
while(true) {
wait();
// Blocking read. If FIFO is empty, thread suspends here.
int val = data_fifo.read();
std::cout << "@" << sc_core::sc_time_stamp() << " [Consumer] Read value: 0x"
<< std::hex << val << std::endl;
data_ready.write(false);
}
}
};
int sc_main(int argc, char* argv[]) {
sc_core::sc_clock clk("clk", 10, sc_core::SC_NS);
HardwareSystem sys("sys");
sys.clk(clk);
sc_core::sc_start(100, sc_core::SC_NS);
return 0;
}Channel Semantics
sc_signal and sc_buffer
sc_signal<T> applies written values during the update phase. If the new value equals the old value, no event is triggered.
sc_buffer<T> is identical to sc_signal, but it triggers a value-changed event every time it is written to, regardless of whether the value actually changed. Use sc_buffer when the act of writing is semantically important.
Resolved Signals (sc_rv / sc_logic)
SystemC provides resolved signals for multi-writer logic modeling (e.g., tri-state buses, wired-OR). Use sc_core::sc_out_resolved to model true multi-driver electrical contention.
sc_clock
sc_clock provides periodic boolean-like toggling behavior. It provides strict timing stability and guarantees zero-skew edge events across the entire design.
sc_fifo
sc_fifo<T> models queued producer-consumer communication safely across process boundaries. It natively supports both blocking (read(), write()) and non-blocking (nb_read(), nb_write()) access, and provides events to wait on when it becomes full or empty.
sc_mutex and sc_semaphore
Mutexes and semaphores control shared resource access at an abstract level. A process that fails to lock a mutex will suspend and be automatically resumed by the scheduler when the mutex is unlocked.
Comments and Corrections