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