#include "master_transactor.hh"
#include "params/ExternalMaster.hh"
+#include "sc_ext.hh"
#include "sc_master_port.hh"
#include "sim/system.hh"
peq(this, &SCMasterPort::peq_cb),
waitForRetry(false),
pendingRequest(nullptr),
+ pendingPacket(nullptr),
needToSendRetry(false),
responseInProgress(false),
transactor(nullptr),
}
// ... and queue the valid transaction
+ trans.acquire();
peq.notify(trans, phase, delay);
return tlm::TLM_ACCEPTED;
}
{
sc_assert(!waitForRetry);
sc_assert(pendingRequest == nullptr);
+ sc_assert(pendingPacket == nullptr);
trans.acquire();
- auto pkt = generatePacket(trans);
+
+ PacketPtr pkt = nullptr;
+
+ Gem5Extension* extension = nullptr;
+ trans.get_extension(extension);
+
+ // If there is an extension, this transaction was initiated by the gem5
+ // world and we can pipe through the original packet. Otherwise, we
+ // generate a new packet based on the transaction.
+ if (extension != nullptr) {
+ extension->setPipeThrough();
+ pkt = extension->getPacket();
+ } else {
+ pkt = generatePacket(trans);
+ }
auto tlmSenderState = new TlmSenderState(trans);
pkt->pushSenderState(tlmSenderState);
if (sendTimingReq(pkt)) { // port is free -> send END_REQ immediately
sendEndReq(trans);
+ trans.release();
} else { // port is blocked -> wait for retry before sending END_REQ
waitForRetry = true;
pendingRequest = &trans;
+ pendingPacket = pkt;
}
}
SCMasterPort::b_transport(tlm::tlm_generic_payload& trans,
sc_core::sc_time& t)
{
- auto pkt = generatePacket(trans);
+ Gem5Extension* extension = nullptr;
+ trans.get_extension(extension);
+
+ PacketPtr pkt = nullptr;
+
+ // If there is an extension, this transaction was initiated by the gem5
+ // world and we can pipe through the original packet.
+ if (extension != nullptr) {
+ extension->setPipeThrough();
+ pkt = extension->getPacket();
+ } else {
+ pkt = generatePacket(trans);
+ }
- // send an atomic request to gem5
Tick ticks = sendAtomic(pkt);
- panic_if(!pkt->isResponse(), "Packet sending failed!\n");
+
+ // send an atomic request to gem5
+ panic_if(pkt->needsResponse() && !pkt->isResponse(),
+ "Packet sending failed!\n");
// one tick is a pico second
auto delay =
// update time
t += delay;
- destroyPacket(pkt);
+ if (extension != nullptr)
+ destroyPacket(pkt);
trans.set_response_status(tlm::TLM_OK_RESPONSE);
}
unsigned int
SCMasterPort::transport_dbg(tlm::tlm_generic_payload& trans)
{
- auto pkt = generatePacket(trans);
-
- sendFunctional(pkt);
-
- destroyPacket(pkt);
+ Gem5Extension* extension = nullptr;
+ trans.get_extension(extension);
+
+ // If there is an extension, this transaction was initiated by the gem5
+ // world and we can pipe through the original packet.
+ if (extension != nullptr) {
+ extension->setPipeThrough();
+ sendFunctional(extension->getPacket());
+ } else {
+ auto pkt = generatePacket(trans);
+ sendFunctional(pkt);
+ destroyPacket(pkt);
+ }
return trans.get_data_length();
}
sc_core::sc_time::from_value(pkt->payloadDelay + pkt->headerDelay);
auto tlmSenderState = dynamic_cast<TlmSenderState*>(pkt->popSenderState());
+ sc_assert(tlmSenderState != nullptr);
+
auto& trans = tlmSenderState->trans;
+ Gem5Extension* extension = nullptr;
+ trans.get_extension(extension);
+
// clean up
delete tlmSenderState;
- destroyPacket(pkt);
+
+ // If there is an extension the packet was piped through and we must not
+ // delete it. The packet travels back with the transaction.
+ if (extension == nullptr)
+ destroyPacket(pkt);
+ else
+ sc_assert(extension->isPipeThrough());
sendBeginResp(trans, delay);
trans.release();
{
sc_assert(waitForRetry);
sc_assert(pendingRequest != nullptr);
+ sc_assert(pendingPacket != nullptr);
- auto& trans = *pendingRequest;
+ if (sendTimingReq(pendingPacket)) {
+ waitForRetry = false;
+ pendingPacket = nullptr;
- waitForRetry = false;
- pendingRequest = nullptr;
+ auto& trans = *pendingRequest;
+ sendEndReq(trans);
+ trans.release();
- // retry
- handleBeginReq(trans);
+ pendingRequest = nullptr;
+ }
}
void
* added as a sender state to the gem5 packet. This way the payload can be
* restored when the response packet arrives at the port.
*
+ * Special care is required, when the TLM transaction originates from a
+ * SCSlavePort (i.e. it is a gem5 packet that enters back into the gem5 world).
+ * This is a common scenario, when multiple gem5 CPUs communicate via a SystemC
+ * interconnect. In this case, the master port restores the original packet
+ * from the payload extension (added by the SCSlavePort) and forwards it to the
+ * gem5 world. Throughout the code, this mechanism is called 'pipe through'.
+ *
* If gem5 operates in atomic mode, the master port registers the TLM blocking
* interface and automatically translates non-blocking requests to blocking.
* If gem5 operates in timing mode, the transactor registers the non-blocking
bool waitForRetry;
tlm::tlm_generic_payload* pendingRequest;
+ PacketPtr pendingPacket;
bool needToSendRetry;
{
CAUGHT_UP;
- PacketPtr packet = Gem5Extension::getExtension(trans).getPacket();
+ auto& extension = Gem5Extension::getExtension(trans);
+ auto packet = extension.getPacket();
sc_assert(!blockingResponse);
- bool need_retry;
- if (packet->needsResponse()) {
+ bool need_retry = false;
+
+ /*
+ * If the packet was piped through and needs a response, we don't need
+ * to touch the packet and can forward it directly as a response.
+ * Otherwise, we need to make a response and send the transformed
+ * packet.
+ */
+ if (extension.isPipeThrough()) {
+ if (packet->isResponse()) {
+ need_retry = !sendTimingResp(packet);
+ }
+ } else if (packet->needsResponse()) {
packet->makeResponse();
need_retry = !sendTimingResp(packet);
- } else {
- need_retry = false;
}
if (need_retry) {
trans.release();
}
}
- } else {
- SC_REPORT_FATAL("SCSlavePort", "Invalid protocol phase in pec");
}
delete pe;
}