Generic Payload Extensions
How to attach protocol-specific metadata to TLM transactions without replacing tlm_generic_payload.
tlm_generic_payload covers common transaction fields, but real platforms often need extra metadata: privilege level, cache attributes, security state, transaction ID, burst information, debug flags, or initiator identity.
TLM extensions let you attach that metadata without inventing a new payload class, adhering to the IEEE 1666 LRM extension mechanism.
Extension Shape and Complete Example
An extension derives from tlm::tlm_extension<T>. The clone and copy functions matter because payloads can be reused, copied, or managed by memory managers.
Here is a complete, fully compilable example demonstrating how to declare, attach, and read a privilege extension across a blocking transport call.
#include <systemc>
#include <tlm>
#include <tlm_utils/simple_initiator_socket.h>
#include <tlm_utils/simple_target_socket.h>
using namespace sc_core;
// 1. Define the Extension
struct PrivilegeExtension : public tlm::tlm_extension<PrivilegeExtension> {
bool privileged = false;
tlm_extension_base* clone() const override {
return new PrivilegeExtension(*this);
}
void copy_from(tlm_extension_base const& ext) override {
privileged = static_cast<PrivilegeExtension const&>(ext).privileged;
}
};
// 2. The Target reading the Extension
SC_MODULE(TargetDevice) {
tlm_utils::simple_target_socket<TargetDevice> socket{"socket"};
SC_CTOR(TargetDevice) {
socket.register_b_transport(this, &TargetDevice::b_transport);
}
void b_transport(tlm::tlm_generic_payload& trans, sc_time& delay) {
PrivilegeExtension* priv_ext = nullptr;
trans.get_extension(priv_ext); // Attempt to retrieve the extension
if (priv_ext && priv_ext->privileged) {
std::cout << "[TARGET] Privileged access granted.\n";
trans.set_response_status(tlm::TLM_OK_RESPONSE);
} else {
std::cout << "[TARGET] Error: Unprivileged access denied!\n";
trans.set_response_status(tlm::TLM_COMMAND_ERROR_RESPONSE);
}
}
};
// 3. The Initiator attaching the Extension
SC_MODULE(CpuInitiator) {
tlm_utils::simple_initiator_socket<CpuInitiator> socket{"socket"};
SC_CTOR(CpuInitiator) { SC_THREAD(run); }
void run() {
tlm::tlm_generic_payload trans;
sc_time delay = SC_ZERO_TIME;
// Allocate and configure the extension
PrivilegeExtension* priv_ext = new PrivilegeExtension();
priv_ext->privileged = true;
trans.set_extension(priv_ext); // Attach to transaction
trans.set_command(tlm::TLM_READ_COMMAND);
socket->b_transport(trans, delay);
// Always clear extensions if managing memory manually, or if reusing payloads
trans.clear_extension(priv_ext);
delete priv_ext;
wait(delay);
}
};
int sc_main(int argc, char* argv[]) {
CpuInitiator cpu("cpu");
TargetDevice tgt("tgt");
cpu.socket.bind(tgt.socket);
sc_start();
return 0;
}For long-lived transactions or non-blocking payloads, use an ownership strategy that matches the payload lifetime. Do not attach a pointer to a stack object if the transaction may outlive the call (as is the case with non-blocking PEQ logic).
Targets and Absent Extensions
Targets should treat absent extensions deliberately. Either define a default behavior (e.g. assume unprivileged) or report an error for protocols that require the metadata.
Extension Hygiene
Extensions are powerful, but they can make models implicit. Keep them documented:
- Name every extension after the protocol concept it represents.
- Define who creates it, who reads it, and who owns it (memory management is key).
- State whether it is optional or required.
- Include it in transaction logs when relevant.
Good extension design lets a generic payload stay generic while preserving protocol-specific meaning.
Comments and Corrections