| /* | |
| * 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<SysBridgeSenderState *>( | |
| 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<SysBridgeSenderState *>( | |
| 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__ |