/* * Copyright 2021 Google, Inc. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer; * redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution; * neither the name of the copyright holders nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef __MEM_SYS_BRIDGE_HH__ #define __MEM_SYS_BRIDGE_HH__ #include "base/trace.hh" #include "base/types.hh" #include "debug/SysBridge.hh" #include "mem/port.hh" #include "params/SysBridge.hh" #include "sim/sim_object.hh" namespace gem5 { /** * Each System object in gem5 is responsible for a set of RequestorIDs which * identify different sources for memory requests within that System. Each * object within the memory system is responsible for requesting an ID if it * needs one, and then using that ID in the requests it sends out. * * When a simulation has multiple System objects within it, components * registered with different Systems may be able to interact through the memory * system. If an object uses a RequestorID it got from its parent System, and * that ends up being handled by an object which is using a different parent, * the target object may misinterpret or misattribute that ID, or if the number * of IDs in the two systems are different, it might even index an array based * on the requestor out of bounds. * * This SysBridge object helps handle that situation by translating requests * going across it in either direction, upstream or downstream, so that they * always have a RequestorID which is valid in the current System. They * register themselves as a Requestor in each System, and use that ID in the * foreign System, restoring the original ID as the request makes its way back * to its source. * * Example: * # One System with a CPU in it. * sys1 = System(...) * sys1.cpu = CPU(...) * * # One System with memory in it. * sys2 = System(...) * sys2.memory = Memory(...) * * # A SysBridge for crossing from sys1 to sys2. * sys2.sys_bridge = SysBridge( * source=sys1, * target=sys2, * target_port=sys2.memory.port, * source_port=sys1.cpu.port) */ class SysBridge : public SimObject { private: class SysBridgeTargetPort; class SysBridgeSourcePort; // A structure for whatever we need to keep when bridging a packet. struct PacketData { RequestPtr req; }; class SysBridgeSenderState : public Packet::SenderState { private: PacketData pData; public: SysBridgeSenderState(const PacketData &data) : pData(data) {} const PacketData &data() const { return pData; } }; class BridgingPort { protected: RequestorID id; // Replace the requestor ID in pkt, and return any scratch data we'll // need going back the other way. PacketData replaceReqID(PacketPtr pkt); // Restore pkt to use its original requestor ID. static void restoreReqID(PacketPtr pkt, const PacketData &data) { pkt->req = data.req; } static void restoreReqID(PacketPtr pkt, const PacketData &data, PacketData &backup) { backup.req = pkt->req; restoreReqID(pkt, data); } BridgingPort(RequestorID _id) : id(_id) {} }; class SysBridgeTargetPort : public RequestPort, public BridgingPort { private: SysBridgeSourcePort *sourcePort; public: SysBridgeTargetPort(const std::string &_name, SysBridgeSourcePort *source_port, RequestorID _id) : RequestPort(_name), BridgingPort(_id), sourcePort(source_port) { DPRINTF(SysBridge, "Target side requestor ID = %s.\n", _id); } private: void recvRangeChange() override { sourcePort->sendRangeChange(); } Tick recvAtomicSnoop(PacketPtr pkt) override { DPRINTF(SysBridge, "recvAtomicSnoop incoming ID %d.\n", pkt->requestorId()); auto data = replaceReqID(pkt); DPRINTF(SysBridge, "recvAtomicSnoop outgoing ID %d.\n", pkt->requestorId()); Tick tick = sourcePort->sendAtomicSnoop(pkt); restoreReqID(pkt, data); DPRINTF(SysBridge, "recvAtomicSnoop restored ID %d.\n", pkt->requestorId()); return tick; } bool recvTimingResp(PacketPtr pkt) override { auto *state = dynamic_cast( pkt->popSenderState()); PacketData backup; DPRINTF(SysBridge, "recvTimingResp incoming ID %d.\n", pkt->requestorId()); restoreReqID(pkt, state->data(), backup); DPRINTF(SysBridge, "recvTimingResp restored ID %d.\n", pkt->requestorId()); if (!sourcePort->sendTimingResp(pkt)) { restoreReqID(pkt, backup); DPRINTF(SysBridge, "recvTimingResp un-restored ID %d.\n", pkt->requestorId()); pkt->pushSenderState(state); return false; } else { delete state; return true; } } void recvTimingSnoopReq(PacketPtr pkt) override { DPRINTF(SysBridge, "recvTimingSnoopReq incoming ID %d.\n", pkt->requestorId()); auto *state = new SysBridgeSenderState(replaceReqID(pkt)); pkt->pushSenderState(state); DPRINTF(SysBridge, "recvTimingSnoopReq outgoing ID %d.\n", pkt->requestorId()); sourcePort->sendTimingSnoopReq(pkt); } void recvReqRetry() override { sourcePort->sendRetryReq(); } void recvRetrySnoopResp() override { sourcePort->sendRetrySnoopResp(); } void recvFunctionalSnoop(PacketPtr pkt) override { DPRINTF(SysBridge, "recvFunctionalSnoop incoming ID %d.\n", pkt->requestorId()); auto data = replaceReqID(pkt); DPRINTF(SysBridge, "recvFunctionalSnoop outgoing ID %d.\n", pkt->requestorId()); sourcePort->sendFunctionalSnoop(pkt); restoreReqID(pkt, data); DPRINTF(SysBridge, "recvFunctionalSnoop restored ID %d.\n", pkt->requestorId()); } }; class SysBridgeSourcePort : public ResponsePort, public BridgingPort { private: SysBridgeTargetPort *targetPort; public: SysBridgeSourcePort(const std::string &_name, SysBridgeTargetPort *target_port, RequestorID _id) : ResponsePort(_name), BridgingPort(_id), targetPort(target_port) { DPRINTF(SysBridge, "Source side requestor ID = %s.\n", _id); } private: bool recvTimingReq(PacketPtr pkt) override { DPRINTF(SysBridge, "recvTimingReq incoming ID %d.\n", pkt->requestorId()); auto *state = new SysBridgeSenderState(replaceReqID(pkt)); pkt->pushSenderState(state); DPRINTF(SysBridge, "recvTimingReq outgoing ID %d.\n", pkt->requestorId()); if (!targetPort->sendTimingReq(pkt)) { restoreReqID(pkt, state->data()); DPRINTF(SysBridge, "recvTimingReq restored ID %d.\n", pkt->requestorId()); pkt->popSenderState(); delete state; return false; } else { return true; } } bool tryTiming(PacketPtr pkt) override { // Since tryTiming shouldn't actually send the packet, we should // be able to clean up inline like we would for atomic methods. // This may not actually be necessary at all, but it's a little // safer. DPRINTF(SysBridge, "tryTiming incoming ID %d.\n", pkt->requestorId()); auto data = replaceReqID(pkt); DPRINTF(SysBridge, "tryTiming outgoing ID %d.\n", pkt->requestorId()); bool ret = targetPort->tryTiming(pkt); restoreReqID(pkt, data); DPRINTF(SysBridge, "tryTiming restored ID %d.\n", pkt->requestorId()); return ret; } bool recvTimingSnoopResp(PacketPtr pkt) override { auto *state = dynamic_cast( pkt->popSenderState()); DPRINTF(SysBridge, "recvTimingSnoopResp incoming ID %d.\n", pkt->requestorId()); restoreReqID(pkt, state->data()); DPRINTF(SysBridge, "recvTimingSnoopResp restored ID %d.\n", pkt->requestorId()); return targetPort->sendTimingSnoopResp(pkt); } void recvRespRetry() override { targetPort->sendRetryResp(); } Tick recvAtomic(PacketPtr pkt) override { DPRINTF(SysBridge, "recvAtomic incoming ID %d.\n", pkt->requestorId()); auto data = replaceReqID(pkt); DPRINTF(SysBridge, "recvAtomic outgoing ID %d.\n", pkt->requestorId()); Tick tick = targetPort->sendAtomic(pkt); restoreReqID(pkt, data); DPRINTF(SysBridge, "recvAtomic restored ID %d.\n", pkt->requestorId()); return tick; } Tick recvAtomicBackdoor(PacketPtr pkt, MemBackdoorPtr &backdoor) override { DPRINTF(SysBridge, "recvAtomicBackdoor incoming ID %d.\n", pkt->requestorId()); auto data = replaceReqID(pkt); DPRINTF(SysBridge, "recvAtomicBackdoor outgoing ID %d.\n", pkt->requestorId()); Tick tick = targetPort->sendAtomicBackdoor(pkt, backdoor); restoreReqID(pkt, data); DPRINTF(SysBridge, "recvAtomicBackdoor restored ID %d.\n", pkt->requestorId()); return tick; } void recvFunctional(PacketPtr pkt) override { DPRINTF(SysBridge, "recvFunctional incoming ID %d.\n", pkt->requestorId()); auto data = replaceReqID(pkt); DPRINTF(SysBridge, "recvFunctional outgoing ID %d.\n", pkt->requestorId()); targetPort->sendFunctional(pkt); restoreReqID(pkt, data); DPRINTF(SysBridge, "recvFunctional restored ID %d.\n", pkt->requestorId()); } void recvMemBackdoorReq(const MemBackdoorReq &req, MemBackdoorPtr &backdoor) override { targetPort->sendMemBackdoorReq(req, backdoor); } AddrRangeList getAddrRanges() const override { return targetPort->getAddrRanges(); } }; SysBridgeSourcePort sourcePort; SysBridgeTargetPort targetPort; public: Port &getPort(const std::string &if_name, PortID idx=InvalidPortID) override; SysBridge(const SysBridgeParams &p); }; } // namespace gem5 #endif //__MEM_SYS_BRIDGE_HH__