systemc

SystemC/TLM Integration Bridge โ€” IEEE 1666 interoperability layer

๐Ÿ“ 267 files ๐Ÿ“ 31.6K LOC ๐Ÿ“ IEEE 1666 ๐Ÿ’ป C++
TLM 2.0 gem5 โ†” SystemC Bridge Port Wrappers SC Kernel Channel Model
~11 min read
๐ŸŒ‰ Analogy: The SystemC bridge is like a universal translator between two different simulation languages. gem5 speaks in "Packets" (memory read/write requests), while SystemC speaks in "TLM Generic Payloads" (abstract transactions). The Gem5ToTlmBridge and TlmToGem5Bridge perform real-time translation so components from both worlds can communicate seamlessly โ€” like two diplomats with an interpreter between them.
Table of Contents

Architecture Overview

The systemc module provides a complete SystemC/TLM 2.0 integration layer for gem5, enabling mixed-language simulation. It contains three main components: the TLM bridge for bidirectional transaction translation, a SystemC kernel implementation that schedules SC processes within gem5's event-driven framework, and port wrappers that adapt gem5's port model to SystemC interfaces.

flowchart LR
    subgraph gem5["gem5 Domain"]
        CPU["CPU / Device"]
        RP["ResponsePort"]
        RQP["RequestPort"]
    end

    subgraph Bridge["Translation Layer"]
        G2T["Gem5ToTlmBridge"]
        T2G["TlmToGem5Bridge"]
    end

    subgraph SC["SystemC Domain"]
        IS["Initiator Socket"]
        TS["Target Socket"]
        SCM["SC Module"]
    end

    CPU --> RP
    RP --> G2T
    G2T --> IS
    IS --> SCM

    SCM --> TS
    TS --> T2G
    T2G --> RQP
    RQP --> CPU

The architecture enables four key use cases: (1) gem5 CPU accessing SystemC memory/peripherals, (2) SystemC initiators accessing gem5 memory, (3) mixed SystemC/gem5 Cache Hierarchy | gem5 Wiki" href="../mem_doc/cache/index.html">cache hierarchies, and (4) co-simulation with external SystemC IP blocks.

TLM Bridge Layer

The TLM bridge provides bidirectional translation between gem5's packet-based memory protocol and SystemC's TLM 2.0 generic payload protocol. Two template classes handle the two directions:

flowchart TD
    subgraph G2T["Gem5ToTlmBridge<BITWIDTH>"]
        BRP["BridgeResponsePort\n(gem5::ResponsePort)"]
        Conv1["packet2payload()"]
        SIS["simple_initiator_socket"]
    end

    subgraph T2G["TlmToGem5Bridge<BITWIDTH>"]
        STS["simple_target_socket"]
        Conv2["payload2packet()"]
        BRQP["BridgeRequestPort\n(gem5::RequestPort)"]
    end

    PKT["gem5::PacketPtr"] --> BRP
    BRP --> Conv1
    Conv1 --> GP["tlm_generic_payload"]
    GP --> SIS

    GP2["tlm_generic_payload"] --> STS
    STS --> Conv2
    Conv2 --> PKT2["gem5::PacketPtr"]
    PKT2 --> BRQP
BridgeDirectionInputOutput
Gem5ToTlmBridge<BITWIDTH>gem5 โ†’ SystemCgem5 System | gem5 Wiki" class="xref">PacketTLM Generic Payload
TlmToGem5Bridge<BITWIDTH>SystemC โ†’ gem5TLM Generic Payloadgem5 Packet

Both bridges are parameterized by BITWIDTH, which specifies the address bus width. The conversion is extensible via addPacketToPayloadConversionStep() and addPayloadToPacketConversionStep(), allowing custom address translation, command mapping, and data transformation hooks.

Conversion Pipeline

sequenceDiagram
    participant G as gem5 CPU
    participant BP as BridgeResponsePort
    participant G2T as Gem5ToTlmBridge
    participant SC as SystemC Target

    G->>BP: sendTimingReq(PacketPtr)
    BP->>G2T: recvTimingReq(PacketPtr)
    G2T->>G2T: packet2payload(pkt)
    Note over G2T: Map MemCmd โ†’ TLM command
    Note over G2T: Copy address, data, size
    Note over G2T: Run conversion steps
    G2T->>SC: nb_transport_fw(payload, phase, delay)
    SC-->>G2T: TLM_ACCEPTED / TLM_COMPLETED
    G2T->>G2T: payload โ†’ response packet
    G2T->>BP: sendTimingResp(PacketPtr)
    BP->>G: recvTimingResp

Command Mapping

gem5 MemCmdTLM CommandNotes
ReadReqTLM_READ_COMMANDStandard read transaction
WriteReqTLM_WRITE_COMMANDStandard write transaction
ReadExReqTLM_READ_COMMANDExclusive read (coherence)
WritebackDirtyTLM_WRITE_COMMANDCache writeback
OtherTLM_IGNORE_COMMANDUnmapped commands

Key Classes

ClassParentFileDescription
Gem5ToTlmBridgeBasesc_core::sc_moduletlm_bridge/gem5_to_tlm.hhBase class for gem5โ†’TLM bridge; non-template common logic
Gem5ToTlmBridge<BW>Gem5ToTlmBridgeBasetlm_bridge/gem5_to_tlm.hhTemplate bridge: gem5 Packet โ†’ TLM payload via initiator socket
TlmToGem5BridgeBasesc_core::sc_moduletlm_bridge/tlm_to_gem5.hhBase class for TLMโ†’gem5 bridge; non-template common logic
TlmToGem5Bridge<BW>TlmToGem5BridgeBasetlm_bridge/tlm_to_gem5.hhTemplate bridge: TLM payload โ†’ gem5 Packet via request port
ScPortWrapper<IF>gem5::Portsc_port_wrapper.hhWraps sc_port for gem5 port binding system
ScInterfaceWrapper<IF>gem5::Portsc_port_wrapper.hhWraps SC interface for gem5 binding
ScExportWrapper<IF>gem5::Portsc_port_wrapper.hhWraps SC export for bidirectional binding
Processsc_process_bcore/process.hhSC process execution: threads and methods
Schedulerโ€”core/scheduler.hhDelta-cycle scheduler coordinating SC processes and events
Channelโ€”core/channel.hhSC value channel implementation (signals, FIFOs)
sc_signalโ€”channel/sc_signal.ccSignal channel for inter-process communication
sc_clockโ€”channel/sc_clock.ccClock signal generator
sc_mutexโ€”channel/sc_mutex.ccMutual exclusion primitive

Deep Dive: Bridge Mechanics

Gem5ToTlmBridge Internals

The Gem5ToTlmBridge contains an inner BridgeResponsePort that receives gem5 packets. On each timing request:

  1. The response port calls packet2payload() to convert the gem5 Packet to a TLM generic payload
  2. Address, data pointer, data length, and command are mapped to TLM equivalents
  3. Custom conversion steps are applied (registered via addPacketToPayloadConversionStep())
  4. The payload is sent via nb_transport_fw() on the Atomic and Timing Simple CPUs โ€” gem5 Wiki" href="../cpu_doc/simple/index.html">simple initiator socket
  5. On response, the payload is converted back and sent via sendTimingResp()

TlmToGem5Bridge Internals

The reverse bridge uses a simple_target_socket to receive TLM transactions and a BridgeRequestPort to inject gem5 packets:

  1. Incoming TLM transactions arrive at nb_transport_fw() callback
  2. payload2packet() creates a gem5 Packet with proper MemCmd and data
  3. A RequestorID is assigned for gem5's coherence tracking
  4. The packet is sent via sendTimingReq() on the request port
  5. Responses trigger nb_transport_bw() with TLM completion phase

Extensible Conversion

Both bridges support a pipeline of conversion steps, enabling custom protocol extensions:

// Register a custom conversion step
bridge->addPacketToPayloadConversionStep(
    [](PacketPtr pkt, tlm_generic_payload &trans) {
        // Custom address remapping
        trans.set_address(pkt->getAddr() - base_offset);
        // Add extension data
        auto *ext = new MyExtension(pkt->req->contextId());
        trans.set_auto_extension(ext);
    }
);

Deep Dive: SystemC Kernel

The core/ subdirectory implements a complete SystemC kernel that runs within gem5's event-driven simulation engine. This enables native SystemC modules to execute alongside gem5 components.

Scheduler Phases

flowchart TD
    INIT["Initialization Phase"]
    EVAL["Evaluate Phase"]
    UPD["Update Phase"]
    DELTA["Delta Notification"]
    TIME["Timed Notification"]
    CHECK{More deltas?}

    INIT --> EVAL
    EVAL --> UPD
    UPD --> DELTA
    DELTA --> CHECK
    CHECK -->|"Yes"| EVAL
    CHECK -->|"No"| TIME
    TIME --> EVAL
PhaseDescription
InitializationExecute SC_METHOD/SC_THREAD initialization, perform channel updates, process delta notifications
EvaluateRun all runnable processes (methods and threads), collect channel update requests
UpdateApply pending channel updates atomically (signal new values, FIFO writes)
Delta NotificationProcess zero-delay event notifications, may trigger new evaluations
Timed NotificationAdvance to next timed event; integrates with gem5 tick via sc_time โ†” Tick conversion

Process Model

The Process class manages SC_THREAD and SC_METHOD execution using gem5's Fiber-based context switching. Key state management:

Deep Dive: Port Wrappers

The port wrapper layer adapts SystemC's port/export/interface model to gem5's Port base class, enabling the gem5 port binding system to connect to SystemC components.

classDiagram
    class Port {
        <<gem5::Port>>
        +bind(Port)
        +unbind()
    }
    class ScPortWrapper~IF~ {
        -sc_port~IF~ &port
        +bind(Port)
    }
    class ScInterfaceWrapper~IF~ {
        -IF &interface
        +bind(Port)
    }
    class ScExportWrapper~IF~ {
        -sc_export~IF~ &exp
        +bind(Port)
    }
    Port <|-- ScPortWrapper
    Port <|-- ScInterfaceWrapper
    Port <|-- ScExportWrapper

The binding rules enforce type safety:

Cross-Module Interactions

DependencyModulePurpose
Memory protocolmemPacket, RequestPort, ResponsePort, Backdoor, RequestorID
Simulation coresimEventQueue, Port base class, tick management
Context switchingbase/fiberFiber-based coroutines for SC_THREAD implementation
SystemC standardsystemc/extIEEE 1666 headers: core, TLM, channels, data types
LoggingbaseTypes, logging, trace, panic/fatal
Python configpythonTlm.py SimObject configuration wrapper

Directory Map

src/systemc/ โ”œโ”€โ”€ tlm_bridge/ โ€” Transaction translation (2.3K LOC) โ”‚ โ”œโ”€โ”€ gem5_to_tlm.hh/cc โ€” gem5 Packet โ†’ TLM payload bridge โ”‚ โ”œโ”€โ”€ tlm_to_gem5.hh/cc โ€” TLM payload โ†’ gem5 Packet bridge โ”‚ โ”œโ”€โ”€ sc_ext.hh/cc โ€” SystemC extensions and utilities โ”‚ โ”œโ”€โ”€ sc_mm.hh/cc โ€” Memory management for payloads โ”‚ โ””โ”€โ”€ TlmBridge.py โ€” Python configuration interface โ”œโ”€โ”€ core/ โ€” SystemC kernel (9.2K LOC) โ”‚ โ”œโ”€โ”€ scheduler.hh/cc โ€” Delta-cycle event scheduler โ”‚ โ”œโ”€โ”€ process.hh/cc โ€” SC_THREAD / SC_METHOD execution โ”‚ โ”œโ”€โ”€ module.hh/cc โ€” Module instantiation and hierarchy โ”‚ โ”œโ”€โ”€ channel.hh/cc โ€” Channel update management โ”‚ โ”œโ”€โ”€ event.hh/cc โ€” Event notification system โ”‚ โ”œโ”€โ”€ port.hh/cc โ€” Port binding infrastructure โ”‚ โ”œโ”€โ”€ sensitivity.hh/cc โ€” Process sensitivity lists โ”‚ โ”œโ”€โ”€ sc_time.hh/cc โ€” Time handling (SC โ†” gem5 ticks) โ”‚ โ””โ”€โ”€ sc_*.cc โ€” Individual SC type implementations โ”œโ”€โ”€ ext/ โ€” SystemC/TLM standard headers โ”‚ โ”œโ”€โ”€ core/ โ€” SC core interfaces (sc_module, sc_port, etc.) โ”‚ โ”œโ”€โ”€ tlm/ โ€” TLM 2.0 definitions โ”‚ โ”œโ”€โ”€ channel/ โ€” Signal, mutex, semaphore interfaces โ”‚ โ”œโ”€โ”€ dt/ โ€” Data types (sc_int, sc_fixed, etc.) โ”‚ โ”œโ”€โ”€ systemc.h โ€” Main SystemC include โ”‚ โ””โ”€โ”€ tlm.h โ€” Main TLM include โ”œโ”€โ”€ channel/ โ€” Channel implementations โ”‚ โ”œโ”€โ”€ sc_signal.cc โ€” Signal channel โ”‚ โ”œโ”€โ”€ sc_clock.cc โ€” Clock signal generator โ”‚ โ”œโ”€โ”€ sc_event_queue.cc โ€” Event queue channel โ”‚ โ”œโ”€โ”€ sc_mutex.cc โ€” Mutual exclusion โ”‚ โ””โ”€โ”€ sc_semaphore.cc โ€” Counting semaphore โ”œโ”€โ”€ tlm_core/ โ€” TLM core implementation โ”œโ”€โ”€ tlm_utils/ โ€” TLM utility classes โ”œโ”€โ”€ utils/ โ€” Internal utilities โ”œโ”€โ”€ dt/ โ€” Data type implementations โ”œโ”€โ”€ sc_port_wrapper.hh โ€” gem5::Port โ†” sc_port adapter โ”œโ”€โ”€ tlm_port_wrapper.hh โ€” gem5::Port โ†” TLM socket adapter โ”œโ”€โ”€ Tlm.py โ€” Top-level Python config โ””โ”€โ”€ tests/ โ€” SystemC integration tests

Configuration Parameters

ParameterClassDescription
tlm_bridge_bitwidthGem5ToTlmBridge / TlmToGem5BridgeAddress bus width template parameter (32, 64)
systemTlmBridgeReference to parent System object
cpu_side_portGem5ToTlmBridgegem5 ResponsePort for incoming requests
mem_side_portTlmToGem5Bridgegem5 RequestPort for outgoing requests
addr_rangesBoth bridgesAddress ranges handled by this bridge instance

Python Configuration Example

from m5.objects import Gem5ToTlmBridge64, TlmToGem5Bridge64

# Create bridge from gem5 CPU to SystemC memory
g2t_bridge = Gem5ToTlmBridge64()
g2t_bridge.cpu_side_port = system.membus.mem_side_ports

# Create bridge from SystemC DMA to gem5 memory
t2g_bridge = TlmToGem5Bridge64()
t2g_bridge.mem_side_port = system.membus.cpu_side_ports

Code Examples

Gem5-to-TLM Packet Conversion

// tlm_bridge/gem5_to_tlm.cc โ€” Convert gem5 packet to TLM payload
template <unsigned int BITWIDTH>
void Gem5ToTlmBridge<BITWIDTH>::packet2payload(
    PacketPtr pkt, tlm_generic_payload &trans)
{
    trans.set_address(pkt->getAddr());

    // Map gem5 command to TLM command
    if (pkt->isRead())
        trans.set_command(tlm::TLM_READ_COMMAND);
    else if (pkt->isWrite())
        trans.set_command(tlm::TLM_WRITE_COMMAND);
    else
        trans.set_command(tlm::TLM_IGNORE_COMMAND);

    // Set data pointer and length
    trans.set_data_ptr(pkt->getPtr<unsigned char>());
    trans.set_data_length(pkt->getSize());
    trans.set_streaming_width(pkt->getSize());

    // Run registered conversion steps
    for (auto &step : conversionSteps)
        step(pkt, trans);
}

Port Wrapper Binding

// sc_port_wrapper.hh โ€” Type-safe port adapter
template <typename IF>
class ScPortWrapper : public gem5::Port
{
    sc_core::sc_port<IF> &port_;
  public:
    ScPortWrapper(sc_core::sc_port<IF> &p, const std::string &name)
        : gem5::Port(name), port_(p) {}

    void bind(Port &peer) override
    {
        // Bind to another ScPortWrapper or ScInterfaceWrapper
        auto *iface = dynamic_cast<ScInterfaceWrapper<IF>*>(&peer);
        if (iface) {
            port_.bind(iface->interface());
            return;
        }
        auto *other = dynamic_cast<ScPortWrapper<IF>*>(&peer);
        if (other) {
            port_.bind(other->port());
            return;
        }
        gem5::fatal("Incompatible port binding");
    }

    void unbind() override { gem5::panic("Cannot unbind sc_port"); }
};

SystemC Process Lifecycle

// core/process.hh โ€” Process state management
class Process : public sc_core::sc_process_b
{
  public:
    bool needsStart() const;     // Not yet started
    bool dynamic() const;        // Dynamically spawned
    bool scheduled() const;      // In runnable list

    void suspend(bool inc_kids);
    void resume(bool inc_kids);
    void disable(bool inc_kids);
    void enable(bool inc_kids);
    void kill(bool inc_kids);
    void reset(bool inc_kids);

    // Reference counting for safe lifecycle
    void incref() { refCount++; }
    void decref() { if (--refCount == 0) delete this; }

    // Reset signal handling
    void signalReset(bool active, bool async);
    void syncResetOn(bool inc_kids);
    void syncResetOff(bool inc_kids);
};