Chapter 7: SystemC 1666-2023 LRM
LRM Bridge: Ports, Exports, Interfaces, and Channels
The standard communication model: sc_interface, sc_port, sc_export, sc_prim_channel, sc_channel, and binding policies.
SystemC hardware communication is strictly interface-based, utilizing a separation of concerns defined by the LRM.
The Four Pillars of Communication
sc_interface: An abstract C++ class defining what can be done (e.g.,read(),write()). It contains no implementation.- Channel (
sc_channelorsc_prim_channel): A module that implements the interface. It contains the actual state and logic. sc_port: An outward-facing connection on a module that requires an interface to function.sc_export: An inward-facing connection on a module boundary that provides an interface implemented by a child module.
End-to-End Interface and Channel Example
This fully compliant IEEE 1666 example demonstrates defining an interface, implementing a hierarchical channel, and binding a port to it.
#include <systemc>
// 1. Define the Interface (inheriting from sc_interface)
struct RegisterIf : virtual public sc_core::sc_interface {
virtual uint32_t read(uint32_t offset) = 0;
virtual void write(uint32_t offset, uint32_t data) = 0;
};
// 2. Implement the Channel (inheriting from sc_channel and the interface)
class RegisterBank : public sc_core::sc_channel, public RegisterIf {
private:
uint32_t memory[256];
public:
SC_HAS_PROCESS(RegisterBank);
RegisterBank(sc_core::sc_module_name name) : sc_core::sc_channel(name) {
for (int i = 0; i < 256; i++) memory[i] = 0;
}
uint32_t read(uint32_t offset) override {
if (offset < 256) {
std::cout << "@" << sc_core::sc_time_stamp() << " [Bank] Read 0x"
<< std::hex << memory[offset] << " from " << offset << std::endl;
return memory[offset];
}
return 0;
}
void write(uint32_t offset, uint32_t data) override {
if (offset < 256) {
std::cout << "@" << sc_core::sc_time_stamp() << " [Bank] Wrote 0x"
<< std::hex << data << " to " << offset << std::endl;
memory[offset] = data;
}
}
};
// 3. Define a Module requiring the interface via a Port
SC_MODULE(CPU_Model) {
// Requires a RegisterIf implementation
sc_core::sc_port<RegisterIf> regs{"regs"};
SC_CTOR(CPU_Model) {
SC_THREAD(execute_logic);
}
void execute_logic() {
wait(10, sc_core::SC_NS);
regs->write(0x10, 0xDEADBEEF); // Accesses the channel via the port interface
wait(10, sc_core::SC_NS);
uint32_t val = regs->read(0x10);
}
};
// 4. Encapsulate with an Export
SC_MODULE(Subsystem) {
// Exposes the internal RegisterBank to the outside world
sc_core::sc_export<RegisterIf> target_export{"target_export"};
RegisterBank regs{"regs"};
SC_CTOR(Subsystem) {
// Bind the export to the internal channel
target_export.bind(regs);
}
};
int sc_main(int argc, char* argv[]) {
CPU_Model cpu("cpu");
Subsystem subsys("subsys");
// Bind the CPU's port to the Subsystem's export
cpu.regs.bind(subsys.target_export);
sc_core::sc_start(50, sc_core::SC_NS);
return 0;
}Primitive vs Hierarchical Channels
- Primitive Channels (
sc_prim_channel): Used for fundamental data types (likesc_signal). They hook directly into the Evaluate-Update paradigm using therequest_update()andupdate()virtual methods. They cannot have structural hierarchy (child modules) or SC_THREADs. - Hierarchical Channels (
sc_channel): Modules that implement an interface (as shown in the example above). They can contain internal processes (SC_THREAD), ports, and child modules.
Binding Policies
The LRM enforces strict binding constraints during the elaboration phase. sc_port can accept a binding policy:
SC_ONE_OR_MORE_BOUND: The port must be bound at least once (default).SC_ZERO_OR_MORE_BOUND: The port may remain unbound (useful for optional interrupts).SC_ALL_BOUND: All elements of a multi-port array must be bound.
If a connection executes behavior, model it as an interface and channel. If a connection crosses structural hierarchy, expose it cleanly with an export.
Comments and Corrections