[ESL Modelling] TLM (1)

SystemC was originally designed for detailed net-level hardware modeling but is now primarily used for various purposes, including:

  • Architectural Exploration: SystemC enables quick and straightforward creation of high-level models for exploring the performance impact of different SoC architectures, such as bus width and cache memory size.
  • Transaction-Level ESL Models (TLM): This approach replaces the detailed handshaking protocols and net-level modeling of hardware with higher-level subroutine calls between models, streamlining system modeling.
  • Synthesis: SystemC source code can be synthesized into RTL, either by using SystemC’s RTL structure or through high-level synthesis.

Two Coding Styles

The Open SystemC Initiative (OSCI) recommended two coding styles: 1.0 and 2.0. While the 1.0 standard is somewhat easier to understand, the 2.0 standard is now more widely adopted due to its enhanced interoperability. Both standards are worth mentioning because the 1.0 standard introduces important concepts.

Both styles support blocking and non-blocking transactions, but the 2.0 library facilitates interaction between these styles and offers additional features like back channels. The 2.0 release also introduced the sc_export extension, allowing a parent module to inherit an interface from one of its child modules. This is particularly useful when the connected module is not at the top level. Furthermore, the same binding mechanism used to connect components in a structural netlist can also be used to link TLM calls.

Example: Transactional Protocol

An example of a transactional protocol implemented at both the net-level and TLM level demonstrates simplex data flow. This example illustrates that the TLM initiator can act as either the source or sink of the data, showcasing the flexibility of TLM.

Thanks to these various styles and features, SystemC has established itself as a highly versatile and powerful tool in hardware and system design, widely used for everything from high-level modeling to detailed synthesis.

Blocking vs. Non-blocking in TLM

Blocking and non-blocking are critical concepts in hardware modeling and programming, especially in Transaction-Level Modeling (TLM). These concepts define how data transfers are handled and can significantly impact a system’s performance and behavior.

Blocking

In a blocking approach, a process halts until the data transfer is complete. This means the process cannot perform other tasks while waiting for the transaction to finish.

  • Characteristics:
    • Synchronous: The process waits for data transfer to complete, resulting in sequential execution.
    • Simple Code Structure: The sequential execution makes the code easier to understand and maintain.
    • Lower Parallel Processing Efficiency: The need to wait for the transaction to complete can reduce the overall parallel processing efficiency of the system.
#include <systemc>
#include <tlm>
#include <tlm_utils/simple_target_socket.h>
#include <tlm_utils/simple_initiator_socket.h>

using namespace sc_core;
using namespace sc_dt;
using namespace std;

// Initiator Module
struct Initiator : sc_module {
    tlm_utils::simple_initiator_socket<Initiator> socket;

    SC_CTOR(Initiator) {
        SC_THREAD(run);
    }

    void run() {
        tlm::tlm_generic_payload trans;
        sc_time delay = SC_ZERO_TIME;
        int data = 42; // Example data

        // Set up the transaction
        trans.set_command(tlm::TLM_WRITE_COMMAND);
        trans.set_address(0);
        trans.set_data_ptr(reinterpret_cast<unsigned char*>(&data));
        trans.set_data_length(4);

        // Blocking transport call
        socket->b_transport(trans, delay);

        if (trans.is_response_error()) {
            SC_REPORT_ERROR("TLM-2", "Response error from b_transport");
        }
    }
};

// Target Module
struct Target : sc_module {
    tlm_utils::simple_target_socket<Target> socket;

    SC_CTOR(Target) {
        socket.register_b_transport(this, &Target::b_transport);
    }

    void b_transport(tlm::tlm_generic_payload& trans, sc_time& delay) {
        int* data = reinterpret_cast<int*>(trans.get_data_ptr());

        // Print received data
        cout << "Received data: " << *data << endl;

        // Add processing delay
        delay += sc_time(10, SC_NS);

        // Set response status
        trans.set_response_status(tlm::TLM_OK_RESPONSE);
    }
};

// Top-level Module
SC_MODULE(Top) {
    Initiator initiator;
    Target target;

    SC_CTOR(Top) : initiator("Initiator"), target("Target") {
        initiator.socket.bind(target.socket);
    }
};

int sc_main(int argc, char* argv[]) {
    Top top("Top");
    sc_start();
    return 0;
}

Download: https://zeah.tistory.com/106

Non-blocking

In contrast, a non-blocking approach allows the process to continue executing other tasks while the transaction is still in progress. The transaction proceeds asynchronously.

  • Characteristics:
    • Asynchronous: The process can perform other tasks while the transaction is ongoing.
    • Higher Parallel Processing Efficiency: Multiple tasks can proceed simultaneously, increasing the system’s parallel processing efficiency.
    • Complex Code Structure: The asynchronous nature can lead to more complex code and potential synchronization issues.
#include <systemc>
#include <tlm>
#include <tlm_utils/simple_target_socket.h>
#include <tlm_utils/simple_initiator_socket.h>

using namespace sc_core;
using namespace sc_dt;
using namespace std;

// Initiator Module
struct Initiator : sc_module {
    tlm_utils::simple_initiator_socket<Initiator> socket;

    SC_CTOR(Initiator) {
        SC_THREAD(run);
    }

    void run() {
        tlm::tlm_generic_payload trans;
        sc_time delay = SC_ZERO_TIME;
        int data = 42; // Example data

        // Set up the transaction
        trans.set_command(tlm::TLM_WRITE_COMMAND);
        trans.set_address(0);
        trans.set_data_ptr(reinterpret_cast<unsigned char*>(&data));
        trans.set_data_length(4);

        // Non-blocking transport call
        while (socket->nb_transport_fw(trans, delay) != tlm::TLM_COMPLETED) {
            wait(delay);
            delay = SC_ZERO_TIME; // Reset delay for the next attempt
        }

        if (trans.is_response_error()) {
            SC_REPORT_ERROR("TLM-2", "Response error from nb_transport_fw");
        }
    }
};

// Target Module
struct Target : sc_module {
    tlm_utils::simple_target_socket<Target> socket;

    SC_CTOR(Target) {
        socket.register_nb_transport_fw(this, &Target::nb_transport_fw);
    }

    tlm::tlm_sync_enum nb_transport_fw(tlm::tlm_generic_payload& trans, sc_time& delay) {
        int* data = reinterpret_cast<int*>(trans.get_data_ptr());

        // Print received data
        cout << "Received data: " << *data << endl;

        // Simulate processing delay
        wait(10, SC_NS);

        // Set response status
        trans.set_response_status(tlm::TLM_OK_RESPONSE);

        return tlm::TLM_COMPLETED;
    }
};

// Top-level Module
SC_MODULE(Top) {
    Initiator initiator;
    Target target;

    SC_CTOR(Top) : initiator("Initiator"), target("Target") {
        initiator.socket.bind(target.socket);
    }
};

int sc_main(int argc, char* argv[]) {
    Top top("Top");
    sc_start();
    return 0;
}

Download: https://zeah.tistory.com/107

Blocking transactions are simpler but can hinder parallel processing efficiency, while non-blocking transactions enhance efficiency but may complicate the code. The choice between these two approaches depends on the system’s requirements. For instance, if performance is critical, non-blocking methods can maximize parallel processing, while blocking methods may be preferable for simplicity and ease of understanding.

The OSCI TLM 1.0 Standard primarily employs the concept of multiple inheritance in C++. When a SC_MODULE implements an interface, it inherits that interface using C++ inheritance mechanisms. Below, we’ll discuss the key concepts, challenges, and improvements associated with TLM 1.0.

1. Basic Concepts of TLM 1.0

  • Interfaces and Inheritance: In TLM 1.0, interfaces for hardware operations like read and write are defined using C++ virtual classes. Each interface may contain one or more callable methods. For instance, a write interface might include two methods for data transfer.
  • Differences between Net-level and TLM: Net-level implementation uses data buses and handshake signals for data transfer and flow control. In contrast, TLM replaces these signals with method calls, where actions like sending or receiving data are handled through method invocations and returns.
  • Blocking and Non-blocking Transactions: TLM 1.0 supports both blocking and non-blocking transactions. Blocking methods should be called from an SC_THREAD, while non-blocking methods can be called from an SC_METHOD.

2. Advantages of TLM 1.0

  • Speed: TLM 1.0 is significantly faster than net-level modeling. It avoids the Event-Driven Simulation (EDS) kernel and computed jumps, preventing pipeline stalls. As a result, TLM can be up to 1000 times faster than net-level modeling.

3. Drawbacks and Issues of TLM 1.0

  • Lack of Time and Energy Modeling: TLM 1.0 lacks a concept of time, making it impossible to dynamically model energy consumption due to changes in the network during transactions. Concepts such as system clocks and clock trees are also absent at this level of modeling.
  • Absence of Standardized Payloads: TLM 1.0 does not provide standardized payload structures, leading to varied bus structures adopted by different companies. This lack of standardization can cause compatibility issues between IP block models when assembling an SoC.
  • Multiple Interface Instances: In hardware design, it is common for a component to instantiate the same type of interface multiple times. However, TLM 1.0 does not support this functionality. A workaround involved adding dummy type parameters to the interface specifications, which was inefficient and unsightly.
  • Blocking and Non-blocking Selection Issues: It was necessary to decide whether a method should be blocking or non-blocking, and the initiator and target had to agree on the same style. Alternatively, the target needed to provide both styles, increasing coding complexity.

4. Improvements in TLM 2.0

TLM 2.0 introduced the concept of TLM sockets to address several issues from TLM 1.0. TLM sockets are implemented with lightweight library code, resolving differences between blocking and non-blocking methods, lack of standardized payload structures, and difficulties with multiple interface instantiation.

Overall, TLM 1.0 is an efficient standard for modeling hardware using C++ multiple inheritance, particularly in terms of speed. However, it has some limitations, including challenges in time and energy modeling, issues with multiple interface instances, and complexities in handling blocking and non-blocking transactions. TLM 2.0 addressed these problems by introducing TLM sockets, significantly enhancing modeling convenience and compatibility.

This refined explanation maintains the core content while presenting it in a more structured and polished manner.

Similar Posts