Chapter 7: SystemC 1666-2023 LRM

LRM Bridge: TLM Reference Map

Blocking transport, non-blocking transport, generic payload, phases, sockets, DMI, debug transport, and temporal decoupling.

Transaction Level Modeling (TLM 2.0), defined in the IEEE 1666 standard, allows models to communicate through abstract C++ data structures (tlm_generic_payload) rather than individual pin toggles. This dramatically increases simulation speed.

Complete TLM 2.0 Base Protocol Example

This sc_main example demonstrates the essential components of TLM: the generic payload, socket binding, blocking transport, and correct target response mechanics.

#include <systemc>
#include <tlm>
#include <tlm_utils/simple_initiator_socket.h>
#include <tlm_utils/simple_target_socket.h>
 
// 1. TLM Initiator
SC_MODULE(TLM_Initiator) {
    tlm_utils::simple_initiator_socket<TLM_Initiator> socket;
    
    SC_CTOR(TLM_Initiator) : socket("socket") {
        SC_THREAD(run);
    }
 
    void run() {
        tlm::tlm_generic_payload trans;
        sc_core::sc_time delay = sc_core::SC_ZERO_TIME;
        uint32_t data = 0x12345678;
 
        // Populate the Generic Payload (Strict LRM rules)
        trans.set_command(tlm::TLM_WRITE_COMMAND);
        trans.set_address(0x1000);
        trans.set_data_ptr(reinterpret_cast<unsigned char*>(&data));
        trans.set_data_length(4);
        trans.set_streaming_width(4); 
        trans.set_byte_enable_ptr(0); // 0 means all bytes enabled
        trans.set_dmi_allowed(false);
        trans.set_response_status(tlm::TLM_INCOMPLETE_RESPONSE);
 
        std::cout << "@" << sc_core::sc_time_stamp() << " [Init] Sending WRITE to 0x1000" << std::endl;
        
        // Blocking Transport Call
        socket->b_transport(trans, delay);
 
        // Check Response
        if (trans.is_response_error()) {
            SC_REPORT_ERROR("TLM", "Transaction returned with error response!");
        } else {
            std::cout << "@" << sc_core::sc_time_stamp() << " [Init] WRITE Success. Delay returned: " 
                      << delay << std::endl;
            wait(delay); // Yield to the scheduler to account for annotated delay
        }
    }
};
 
// 2. TLM Target
SC_MODULE(TLM_Target) {
    tlm_utils::simple_target_socket<TLM_Target> socket;
    
    SC_CTOR(TLM_Target) : socket("socket") {
        socket.register_b_transport(this, &TLM_Target::b_transport);
    }
 
    void b_transport(tlm::tlm_generic_payload& trans, sc_core::sc_time& delay) {
        if (trans.get_address() == 0x1000 && trans.get_command() == tlm::TLM_WRITE_COMMAND) {
            // Annotate processing delay
            delay += sc_core::sc_time(10, sc_core::SC_NS);
            
            // MANDATORY: Target must explicitly set the response status
            trans.set_response_status(tlm::TLM_OK_RESPONSE);
        } else {
            trans.set_response_status(tlm::TLM_ADDRESS_ERROR_RESPONSE);
        }
    }
};
 
int sc_main(int argc, char* argv[]) {
    TLM_Initiator init("init");
    TLM_Target target("target");
    
    init.socket.bind(target.socket);
    
    sc_core::sc_start();
    return 0;
}

Transport Interfaces

Blocking Transport (b_transport)

Used for Loosely Timed (LT) modeling where a single C++ function call represents the complete operation from request to response. It passes time dynamically via the delay argument.

Non-Blocking Transport (nb_transport_fw / nb_transport_bw)

Used for Approximately Timed (AT) models. Splits a transaction into explicit hardware phases (e.g., BEGIN_REQ, END_REQ, BEGIN_RESP, END_RESP). This supports deep pipelining and complex interconnect arbitration but is significantly slower to simulate.

Advanced Interfaces

Direct Memory Interface (DMI)

DMI allows an initiator to request a direct C++ pointer to a target's memory array (get_direct_mem_ptr). If granted, the initiator bypasses the b_transport socket entirely for subsequent reads/writes, improving simulation speed by orders of magnitude. The LRM requires strict invalidation strategies (using invalidate_direct_mem_ptr) when memory maps change.

Debug Transport (transport_dbg)

Debug transport is a side-effect-free access path. It is used exclusively by debuggers, GDB stubs, or memory preloaders. A debug read to a FIFO must not pop the FIFO, and it does not advance simulation time.

Temporal Decoupling

Temporal decoupling allows initiators to run ahead of global simulation time locally within a "quantum". It drastically reduces scheduler context switches, offering the highest possible simulation speed, trading off cycle-accurate hardware synchronization.

Comments and Corrections