Router DMI and Debug Transport
Optimizing VP performance with Direct Memory Interface (DMI) and backdoor Debug Transport.
Router DMI and Debug Transport
A Loosely Timed (LT) Virtual Platform booting an operating system executes millions of memory instructions (Instruction Fetches, Stack Pushes). If every single memory access has to allocate a tlm_generic_payload, traverse the router, decode the address, and execute a b_transport function call, the simulation will be devastatingly slow.
The IEEE 1666 standard provides Direct Memory Interface (DMI) to bypass the socket transport entirely.
Direct Memory Interface (DMI)
DMI allows an initiator to request a direct C++ pointer to the target's physical memory array.
- CPU attempts an access.
- CPU asks the Router for DMI permissions for that address region.
- Router forwards the request to RAM.
- RAM returns a
tlm_dmistruct containing the rawunsigned char*pointer and the allowed address range. - CPU caches this pointer and executes millions of future reads/writes using direct array indexing (
ptr[offset]), achieving native execution speed.
Complete DMI Target Example
This sc_main example implements a DMI-compliant RAM module.
#include <systemc>
#include <tlm>
#include <tlm_utils/simple_target_socket.h>
class DMI_RAM : public sc_core::sc_module {
public:
tlm_utils::simple_target_socket<DMI_RAM> socket;
unsigned char* memory;
unsigned int size;
SC_HAS_PROCESS(DMI_RAM);
DMI_RAM(sc_core::sc_module_name name, unsigned int size_bytes)
: sc_core::sc_module(name), size(size_bytes) {
memory = new unsigned char[size];
memset(memory, 0, size);
// Standard Transport
socket.register_b_transport(this, &DMI_RAM::b_transport);
// Register DMI Hook
socket.register_get_direct_mem_ptr(this, &DMI_RAM::get_direct_mem_ptr);
}
~DMI_RAM() { delete[] memory; }
private:
void b_transport(tlm::tlm_generic_payload& trans, sc_core::sc_time& delay) {
// Standard transport logic omitted...
// Hint to the initiator that DMI is available for this region
trans.set_dmi_allowed(true);
trans.set_response_status(tlm::TLM_OK_RESPONSE);
}
// LRM DMI Method
bool get_direct_mem_ptr(tlm::tlm_generic_payload& trans, tlm::tlm_dmi& dmi_data) {
// Provide the direct C++ pointer to the memory array
dmi_data.set_dmi_ptr(memory);
// Define the valid physical range for this pointer
dmi_data.set_start_address(0x00000000);
dmi_data.set_end_address(size - 1);
// Allow both read and write
dmi_data.set_granted_access(tlm::tlm_dmi::DMI_ACCESS_READ_WRITE);
// Set latencies so the initiator can accurately accumulate time
dmi_data.set_read_latency(sc_core::sc_time(10, sc_core::SC_NS));
dmi_data.set_write_latency(sc_core::sc_time(10, sc_core::SC_NS));
std::cout << "@" << sc_core::sc_time_stamp() << " [RAM] Granted DMI Access." << std::endl;
return true;
}
};
int sc_main(int argc, char* argv[]) {
DMI_RAM ram("ram", 0x10000); // 64KB RAM
// Initiator omitted for brevity
return 0;
}Debug Transport (transport_dbg)
When a debugger (like GDB attached to the Virtual Platform) inspects memory, it must NOT alter the hardware state. Reading a UART FIFO should not pop the FIFO.
The LRM provides transport_dbg, a completely side-effect-free, non-blocking path. It does not take an sc_time argument, and it executes instantaneously in zero simulation time. Targets must implement transport_dbg to support architectural inspection utilities.
Comments and Corrections