Giacomo Travaglini has uploaded this change for review. ( https://gem5-review.googlesource.com/c/public/gem5/+/44968 )

Change subject: dev, base: Factor functionalities out of DmaPort
......................................................................

dev, base: Factor functionalities out of DmaPort

The DmaPort contains mainly packet tx functionalities which are not
specific to a DMA device. For this reason we have been using it in the
Arm TableWalker.

This patch is cleaning things up by explicitly providing a base MemPort
class, implementing the generic memory requests handling discussed
above.

Change-Id: Ia56dd2ebda77e72f42f8c86c3e47ab2645dce755
Signed-off-by: Giacomo Travaglini <[email protected]>
---
M src/base/SConscript
A src/base/mem_port.cc
A src/base/mem_port.hh
M src/dev/dma_device.cc
M src/dev/dma_device.hh
5 files changed, 522 insertions(+), 372 deletions(-)



diff --git a/src/base/SConscript b/src/base/SConscript
index 64c2237..9b44834 100644
--- a/src/base/SConscript
+++ b/src/base/SConscript
@@ -64,6 +64,7 @@
 GTest('logging.test', 'logging.test.cc', 'logging.cc', 'hostinfo.cc',
     'cprintf.cc', 'gtest/logging.cc', skip_lib=True)
 Source('match.cc')
+Source('mem_port.cc')
 GTest('match.test', 'match.test.cc', 'match.cc', 'str.cc')
 Source('output.cc')
 Source('pixel.cc')
diff --git a/src/base/mem_port.cc b/src/base/mem_port.cc
new file mode 100644
index 0000000..1e82025
--- /dev/null
+++ b/src/base/mem_port.cc
@@ -0,0 +1,322 @@
+/*
+ * Copyright (c) 2012, 2015, 2017, 2019, 2021 Arm Limited
+ * All rights reserved.
+ *
+ * The license below extends only to copyright in the software and shall
+ * not be construed as granting a license to any other intellectual
+ * property including but not limited to intellectual property relating
+ * to a hardware implementation of the functionality of the software
+ * licensed hereunder.  You may use the software subject to the license
+ * terms below provided that you ensure that this notice is replicated
+ * unmodified and in its entirety in all distributions of the software,
+ * modified or unmodified, in source code or in binary form.
+ *
+ * Copyright (c) 2006 The Regents of The University of Michigan
+ * All rights reserved.
+ *
+ * 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.
+ */
+
+#include "base/mem_port.hh"
+
+#include "debug/Drain.hh"
+#include "sim/clocked_object.hh"
+#include "sim/system.hh"
+
+MemPort::MemPort(ClockedObject *dev, System *s,
+    const std::string &_name, const Debug::SimpleFlag &_dflag)
+    : RequestPort(dev->name() + "." + _name, dev),
+      device(dev), sys(s), requestorId(s->getRequestorId(dev)),
+      sendEvent([this]{ sendReq(); }, dev->name()),
+      cacheLineSize(s->cacheLineSize()),
+      dflag(_dflag)
+{ }
+
+void
+MemPort::handleRespPacket(PacketPtr pkt, Tick delay)
+{
+    // Should always see a response with a sender state.
+    assert(pkt->isResponse());
+
+    // Get the memory request sender state.
+    auto *state = dynamic_cast<ReqState*>(pkt->senderState);
+    assert(state);
+
+    handleResp(state, pkt->getAddr(), pkt->req->getSize(), delay);
+
+    delete pkt;
+}
+
+void
+MemPort::handleResp(ReqState *state, Addr addr, Addr size, Tick delay)
+{
+ DPRINTFV(dflag, "Received response %s for addr: %#x size: %d nb: %d," \
+             " tot: %d sched %d\n",
+             MemCmd(state->cmd).toString(), addr, size,
+             state->numBytes, state->totBytes,
+             state->completionEvent ?
+             state->completionEvent->scheduled() : 0);
+
+    // Update the number of bytes received based on the request rather
+    // than the packet as the latter could be rounded up to line sizes.
+    state->numBytes += size;
+    assert(state->totBytes >= state->numBytes);
+
+    // If we have reached the total number of bytes for this block request,
+    // then signal the completion and delete the sate.
+    if (state->totBytes == state->numBytes) {
+        assert(pendingCount != 0);
+        pendingCount--;
+        if (state->completionEvent) {
+            delay += state->delay;
+            device->schedule(state->completionEvent, curTick() + delay);
+        }
+        delete state;
+    }
+
+    // We might be drained at this point, if so signal the drain event.
+    if (pendingCount == 0)
+        signalDrainDone();
+}
+
+PacketPtr
+MemPort::ReqState::createPacket()
+{
+    RequestPtr req = std::make_shared<Request>(
+            gen.addr(), gen.size(), flags, id);
+    req->taskId(ContextSwitchTaskId::DMA);
+
+    PacketPtr pkt = new Packet(req, cmd);
+
+    if (data)
+        pkt->dataStatic(data + gen.complete());
+
+    pkt->senderState = this;
+    return pkt;
+}
+
+bool
+MemPort::recvTimingResp(PacketPtr pkt)
+{
+    // We shouldn't ever get a cacheable block in Modified state.
+    assert(pkt->req->isUncacheable() ||
+           !(pkt->cacheResponding() && !pkt->hasSharers()));
+
+    handleRespPacket(pkt);
+
+    return true;
+}
+
+DrainState
+MemPort::drain()
+{
+    if (pendingCount == 0) {
+        return DrainState::Drained;
+    } else {
+        DPRINTF(Drain, "MemPort not drained\n");
+        return DrainState::Draining;
+    }
+}
+
+void
+MemPort::recvReqRetry()
+{
+    assert(transmitList.size());
+    trySendTimingReq();
+}
+
+void
+MemPort::trySendTimingReq()
+{
+    // Send the next packet for the first request on the transmit list,
+    // and schedule the following send if it is successful
+    ReqState *state = transmitList.front();
+
+    PacketPtr pkt = inRetry ? inRetry : state->createPacket();
+    inRetry = nullptr;
+
+    DPRINTFV(dflag, "Trying to send %s addr %#x\n", pkt->cmdString(),
+             pkt->getAddr());
+
+ // Check if this was the last packet now, since hypothetically the packet
+    // response may come immediately, and state may be deleted.
+    bool last = state->gen.last();
+    if (!sendTimingReq(pkt))
+        inRetry = pkt;
+    if (!inRetry) {
+ // If that was the last packet from this request, pop it from the list.
+        if (last)
+            transmitList.pop_front();
+        else
+            state->gen.next();
+        DPRINTFV(dflag, "-- Done\n");
+        // If there is more to do, then do so.
+        if (!transmitList.empty()) {
+            // This should ultimately wait for as many cycles as the device
+ // needs to send the packet, but currently the port does not have
+            // any known width so simply wait a single cycle.
+            device->schedule(sendEvent, device->clockEdge(Cycles(1)));
+        }
+    } else {
+        DPRINTFV(dflag, "-- Failed, waiting for retry\n");
+    }
+
+    DPRINTFV(dflag, "TransmitList: %d, inRetry: %d\n",
+             transmitList.size(), inRetry ? 1 : 0);
+}
+
+bool
+MemPort::sendAtomicReq(ReqState *state)
+{
+    PacketPtr pkt = state->createPacket();
+    DPRINTFV(dflag, "Sending memory request for addr: %#x size: %d\n",
+             state->gen.addr(), state->gen.size());
+    Tick lat = sendAtomic(pkt);
+
+    // Check if we're done, since handleResp may delete state.
+    bool done = !state->gen.next();
+    handleRespPacket(pkt, lat);
+    return done;
+}
+
+bool
+MemPort::sendAtomicBdReq(ReqState *state)
+{
+    bool done = false;
+
+    auto bd_it = memBackdoors.contains(state->gen.addr());
+    if (bd_it == memBackdoors.end()) {
+        // We don't have a backdoor for this address, so use a packet.
+
+        PacketPtr pkt = state->createPacket();
+        DPRINTFV(dflag, "Sending memory request for addr: %#x size: %d\n",
+                 state->gen.addr(), state->gen.size());
+
+        MemBackdoorPtr bd = nullptr;
+        Tick lat = sendAtomicBackdoor(pkt, bd);
+
+        // If we got a backdoor, record it.
+ if (bd && memBackdoors.insert(bd->range(), bd) != memBackdoors.end()) { + // Invalidation callback which finds this backdoor and removes it.
+            auto callback = [this](const MemBackdoor &backdoor) {
+                for (auto it = memBackdoors.begin();
+                        it != memBackdoors.end(); it++) {
+                    if (it->second == &backdoor) {
+                        memBackdoors.erase(it);
+                        return;
+                    }
+                }
+                panic("Got invalidation for unknown memory backdoor.");
+            };
+            bd->addInvalidationCallback(callback);
+        }
+
+        // Check if we're done now, since handleResp may delete state.
+        done = !state->gen.next();
+        handleRespPacket(pkt, lat);
+    } else {
+ // We have a backdoor that can at least partially satisfy this request.
+        DPRINTFV(dflag, "Handling memory request for addr: "
+                 "%#x size %d through backdoor\n",
+                 state->gen.addr(), state->gen.size());
+
+        const auto *bd = bd_it->second;
+        // Offset of this access into the backdoor.
+        const Addr offset = state->gen.addr() - bd->range().start();
+        // How many bytes we still need.
+        const Addr remaining = state->totBytes - state->gen.complete();
+        // How many bytes this backdoor can provide, starting from offset.
+        const Addr available = bd->range().size() - offset;
+
+        // How many bytes we're going to handle through this backdoor.
+        const Addr handled = std::min(remaining, available);
+
+        // If there's a buffer for data, read/write it.
+        if (state->data) {
+            uint8_t *bd_data = bd->ptr() + offset;
+            uint8_t *state_data = state->data + state->gen.complete();
+            if (MemCmd(state->cmd).isRead())
+                memcpy(state_data, bd_data, handled);
+            else
+                memcpy(bd_data, state_data, handled);
+        }
+
+        // Advance the chunk generator past this region of memory.
+        state->gen.setNext(state->gen.addr() + handled);
+
+        // Check if we're done now, since handleResp may delete state.
+        done = !state->gen.next();
+        handleResp(state, state->gen.addr(), handled);
+    }
+
+    return done;
+}
+
+void
+MemPort::sendReq(ReqState *req)
+{
+    // One request sender state for every action, that is then
+    // split into many requests and packets based on the block size,
+    // i.e. cache line size.
+    transmitList.push_back(req);
+    pendingCount++;
+
+ // In zero time, also initiate the sending of the packets for the request + // we have just created. For atomic this involves actually completing all
+    // the requests.
+    sendReq();
+}
+
+void
+MemPort::sendReq()
+{
+    // Some kind of selection between access methods. More work is going to
+    // have to be done to make switching actually work.
+    assert(transmitList.size());
+
+    if (sys->isTimingMode()) {
+        // If we are either waiting for a retry or are still waiting after
+        // sending the last packet, then do not proceed.
+        if (inRetry || sendEvent.scheduled()) {
+            DPRINTFV(dflag, "Can't send immediately, waiting to send\n");
+            return;
+        }
+
+        trySendTimingReq();
+    } else if (sys->isAtomicMode()) {
+        const bool bypass = sys->bypassCaches();
+
+        // Send everything there is to send in zero time.
+        while (!transmitList.empty()) {
+            ReqState *state = transmitList.front();
+            transmitList.pop_front();
+
+            bool done = state->gen.done();
+            while (!done)
+ done = bypass ? sendAtomicBdReq(state) : sendAtomicReq(state);
+        }
+    } else {
+        panic("Unknown memory mode.");
+    }
+}
diff --git a/src/base/mem_port.hh b/src/base/mem_port.hh
new file mode 100644
index 0000000..3d028b5
--- /dev/null
+++ b/src/base/mem_port.hh
@@ -0,0 +1,185 @@
+/*
+ * Copyright (c) 2012-2013, 2015, 2017, 2019,2021 Arm Limited
+ * All rights reserved.
+ *
+ * The license below extends only to copyright in the software and shall
+ * not be construed as granting a license to any other intellectual
+ * property including but not limited to intellectual property relating
+ * to a hardware implementation of the functionality of the software
+ * licensed hereunder.  You may use the software subject to the license
+ * terms below provided that you ensure that this notice is replicated
+ * unmodified and in its entirety in all distributions of the software,
+ * modified or unmodified, in source code or in binary form.
+ *
+ * Copyright (c) 2004-2005 The Regents of The University of Michigan
+ * All rights reserved.
+ *
+ * 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 __BASE_MEM_PORT_HH__
+#define __BASE_MEM_PORT_HH__
+
+#include <deque>
+
+#include "base/addr_range_map.hh"
+#include "base/chunk_generator.hh"
+#include "mem/packet.hh"
+#include "mem/port.hh"
+#include "sim/drain.hh"
+#include "sim/eventq.hh"
+
+class ClockedObject;
+class System;
+
+class MemPort : public RequestPort, public Drainable
+{
+  public:
+    struct ReqState : public Packet::SenderState
+    {
+        /** Event to call on the device when this transaction (all packets)
+         * complete. */
+        Event *completionEvent;
+
+        /** Total number of bytes that this transaction involves. */
+        const Addr totBytes;
+
+        /** Number of bytes that have been acked for this transaction. */
+        Addr numBytes = 0;
+
+        /** Amount to delay completion of dma by */
+        const Tick delay;
+
+        /** Object to track what chunks of bytes to send at a time. */
+        ChunkGenerator gen;
+
+        /** Pointer to a buffer for the data. */
+        uint8_t *const data = nullptr;
+
+        /** The flags to use for requests. */
+        const Request::Flags flags;
+
+        /** The requestor ID to use for requests. */
+        const RequestorID id;
+
+        /** Command for the request. */
+        const Packet::Command cmd;
+
+        ReqState(Packet::Command _cmd, Addr addr, Addr chunk_sz, Addr tb,
+                 uint8_t *_data, Request::Flags _flags, RequestorID _id,
+                 Event *ce, Tick _delay)
+            : completionEvent(ce), totBytes(tb), delay(_delay),
+              gen(addr, tb, chunk_sz), data(_data), flags(_flags), id(_id),
+              cmd(_cmd)
+        {}
+
+        virtual PacketPtr createPacket();
+    };
+
+  private:
+    AddrRangeMap<MemBackdoorPtr, 1> memBackdoors;
+
+    /**
+ * Take the first request on the transmit list and attempt to send a timing + * packet from it. If it is successful, schedule the sending of the next
+     * packet. Otherwise remember that we are waiting for a retry.
+     */
+    void trySendTimingReq();
+
+    /**
+     * For timing, attempt to send the first item on the transmit
+     * list, and if it is successful and there are more packets
+     * waiting, then schedule the sending of the next packet. For
+     * atomic, simply send and process everything on the transmit
+     * list.
+     */
+    void sendReq();
+
+
+    /** Send the next packet from a memory request in atomic mode. */
+    bool sendAtomicReq(ReqState *state);
+    /**
+ * Send the next packet from a memory request in atomic mode, and request
+     * and/or use memory backdoors if possible.
+     */
+    bool sendAtomicBdReq(ReqState *state);
+
+    /**
+     * Handle a response packet by updating the corresponding
+     * request state to reflect the bytes received, and also update
+     * the pending request counter. If the request that this
+     * packet is part of is complete, then signal the completion event
+     * if present, potentially with a delay added to it.
+     *
+     * @param pkt Response packet to handler
+     * @param delay Additional delay for scheduling the completion event
+     */
+    void handleRespPacket(PacketPtr pkt, Tick delay=0);
+    void handleResp(ReqState *state, Addr addr, Addr size, Tick delay=0);
+
+  public:
+    /** The device that owns this port. */
+    ClockedObject *const device;
+
+ /** The system that device/port are in. This is used to select which mode
+     * we are currently operating in. */
+    System *const sys;
+
+    /** Id for all requests */
+    const RequestorID requestorId;
+
+  protected:
+ /** Use a deque as we never do any insertion or removal in the middle */
+    std::deque<ReqState *> transmitList;
+
+    /** Event used to schedule a future sending from the transmit list. */
+    EventFunctionWrapper sendEvent;
+
+    /** Number of outstanding packets the dma port has. */
+    uint32_t pendingCount = 0;
+
+    /** The packet (if any) waiting for a retry to send. */
+    PacketPtr inRetry = nullptr;
+
+    const int cacheLineSize;
+
+  protected:
+    const Debug::SimpleFlag dflag;
+
+  protected:
+    bool recvTimingResp(PacketPtr pkt) override;
+    void recvReqRetry() override;
+
+  public:
+    MemPort(ClockedObject *dev, System *s, const std::string &_name,
+            const Debug::SimpleFlag &_flag);
+
+    void sendReq(ReqState *req);
+
+    bool memPending() const { return pendingCount > 0; }
+
+    DrainState drain() override;
+};
+
+#endif
diff --git a/src/dev/dma_device.cc b/src/dev/dma_device.cc
index 9aebb7b..684a6d1 100644
--- a/src/dev/dma_device.cc
+++ b/src/dev/dma_device.cc
@@ -48,95 +48,24 @@
 #include "base/logging.hh"
 #include "base/trace.hh"
 #include "debug/DMA.hh"
-#include "debug/Drain.hh"
 #include "sim/clocked_object.hh"
 #include "sim/system.hh"

 DmaPort::DmaPort(ClockedObject *dev, System *s,
                  uint32_t sid, uint32_t ssid)
-    : RequestPort(dev->name() + ".dma", dev),
-      device(dev), sys(s), requestorId(s->getRequestorId(dev)),
-      sendEvent([this]{ sendDma(); }, dev->name()),
-      defaultSid(sid), defaultSSid(ssid), cacheLineSize(s->cacheLineSize())
+ : MemPort(dev, s, "dma", Debug::DMA), defaultSid(sid), defaultSSid(ssid)
 { }

-void
-DmaPort::handleRespPacket(PacketPtr pkt, Tick delay)
-{
-    // Should always see a response with a sender state.
-    assert(pkt->isResponse());
-
-    // Get the DMA sender state.
-    auto *state = dynamic_cast<DmaReqState*>(pkt->senderState);
-    assert(state);
-
-    handleResp(state, pkt->getAddr(), pkt->req->getSize(), delay);
-
-    delete pkt;
-}
-
-void
-DmaPort::handleResp(DmaReqState *state, Addr addr, Addr size, Tick delay)
-{
-    DPRINTF(DMA, "Received response %s for addr: %#x size: %d nb: %d,"  \
-            " tot: %d sched %d\n",
-            MemCmd(state->cmd).toString(), addr, size,
-            state->numBytes, state->totBytes,
-            state->completionEvent ?
-            state->completionEvent->scheduled() : 0);
-
-    // Update the number of bytes received based on the request rather
-    // than the packet as the latter could be rounded up to line sizes.
-    state->numBytes += size;
-    assert(state->totBytes >= state->numBytes);
-
-    // If we have reached the total number of bytes for this DMA request,
-    // then signal the completion and delete the sate.
-    if (state->totBytes == state->numBytes) {
-        assert(pendingCount != 0);
-        pendingCount--;
-        if (state->completionEvent) {
-            delay += state->delay;
-            device->schedule(state->completionEvent, curTick() + delay);
-        }
-        delete state;
-    }
-
-    // We might be drained at this point, if so signal the drain event.
-    if (pendingCount == 0)
-        signalDrainDone();
-}
-
 PacketPtr
 DmaPort::DmaReqState::createPacket()
 {
-    RequestPtr req = std::make_shared<Request>(
-            gen.addr(), gen.size(), flags, id);
-    req->setStreamId(sid);
-    req->setSubstreamId(ssid);
-    req->taskId(ContextSwitchTaskId::DMA);
+    auto pkt = ReqState::createPacket();
+    pkt->req->setStreamId(sid);
+    pkt->req->setSubstreamId(ssid);

-    PacketPtr pkt = new Packet(req, cmd);
-
-    if (data)
-        pkt->dataStatic(data + gen.complete());
-
-    pkt->senderState = this;
     return pkt;
 }

-bool
-DmaPort::recvTimingResp(PacketPtr pkt)
-{
-    // We shouldn't ever get a cacheable block in Modified state.
-    assert(pkt->req->isUncacheable() ||
-           !(pkt->cacheResponding() && !pkt->hasSharers()));
-
-    handleRespPacket(pkt);
-
-    return true;
-}
-
 DmaDevice::DmaDevice(const Params &p)
     : PioDevice(p), dmaPort(this, sys, p.sid, p.ssid)
 { }
@@ -149,24 +78,6 @@
     PioDevice::init();
 }

-DrainState
-DmaPort::drain()
-{
-    if (pendingCount == 0) {
-        return DrainState::Drained;
-    } else {
-        DPRINTF(Drain, "DmaPort not drained\n");
-        return DrainState::Draining;
-    }
-}
-
-void
-DmaPort::recvReqRetry()
-{
-    assert(transmitList.size());
-    trySendTimingReq();
-}
-
 void
 DmaPort::dmaAction(Packet::Command cmd, Addr addr, int size, Event *event,
                    uint8_t *data, uint32_t sid, uint32_t ssid, Tick delay,
@@ -175,18 +86,10 @@
DPRINTF(DMA, "Starting DMA for addr: %#x size: %d sched: %d\n", addr, size,
             event ? event->scheduled() : -1);

-    // One DMA request sender state for every action, that is then
-    // split into many requests and packets based on the block size,
-    // i.e. cache line size.
-    transmitList.push_back(
-            new DmaReqState(cmd, addr, cacheLineSize, size,
-                data, flag, requestorId, sid, ssid, event, delay));
-    pendingCount++;
+    auto state = new DmaReqState(cmd, addr, cacheLineSize, size,
+        data, flag, requestorId, sid, ssid, event, delay);

- // In zero time, also initiate the sending of the packets for the request - // we have just created. For atomic this involves actually completing all
-    // the requests.
-    sendDma();
+    sendReq(state);
 }

 void
@@ -197,165 +100,6 @@
               defaultSid, defaultSSid, delay, flag);
 }

-void
-DmaPort::trySendTimingReq()
-{
-    // Send the next packet for the first DMA request on the transmit list,
-    // and schedule the following send if it is successful
-    DmaReqState *state = transmitList.front();
-
-    PacketPtr pkt = inRetry ? inRetry : state->createPacket();
-    inRetry = nullptr;
-
-    DPRINTF(DMA, "Trying to send %s addr %#x\n", pkt->cmdString(),
-            pkt->getAddr());
-
- // Check if this was the last packet now, since hypothetically the packet
-    // response may come immediately, and state may be deleted.
-    bool last = state->gen.last();
-    if (!sendTimingReq(pkt))
-        inRetry = pkt;
-    if (!inRetry) {
- // If that was the last packet from this request, pop it from the list.
-        if (last)
-            transmitList.pop_front();
-        else
-            state->gen.next();
-        DPRINTF(DMA, "-- Done\n");
-        // If there is more to do, then do so.
-        if (!transmitList.empty()) {
-            // This should ultimately wait for as many cycles as the device
- // needs to send the packet, but currently the port does not have
-            // any known width so simply wait a single cycle.
-            device->schedule(sendEvent, device->clockEdge(Cycles(1)));
-        }
-    } else {
-        DPRINTF(DMA, "-- Failed, waiting for retry\n");
-    }
-
-    DPRINTF(DMA, "TransmitList: %d, inRetry: %d\n",
-            transmitList.size(), inRetry ? 1 : 0);
-}
-
-bool
-DmaPort::sendAtomicReq(DmaReqState *state)
-{
-    PacketPtr pkt = state->createPacket();
-    DPRINTF(DMA, "Sending  DMA for addr: %#x size: %d\n",
-            state->gen.addr(), state->gen.size());
-    Tick lat = sendAtomic(pkt);
-
-    // Check if we're done, since handleResp may delete state.
-    bool done = !state->gen.next();
-    handleRespPacket(pkt, lat);
-    return done;
-}
-
-bool
-DmaPort::sendAtomicBdReq(DmaReqState *state)
-{
-    bool done = false;
-
-    auto bd_it = memBackdoors.contains(state->gen.addr());
-    if (bd_it == memBackdoors.end()) {
-        // We don't have a backdoor for this address, so use a packet.
-
-        PacketPtr pkt = state->createPacket();
-        DPRINTF(DMA, "Sending DMA for addr: %#x size: %d\n",
-                state->gen.addr(), state->gen.size());
-
-        MemBackdoorPtr bd = nullptr;
-        Tick lat = sendAtomicBackdoor(pkt, bd);
-
-        // If we got a backdoor, record it.
- if (bd && memBackdoors.insert(bd->range(), bd) != memBackdoors.end()) { - // Invalidation callback which finds this backdoor and removes it.
-            auto callback = [this](const MemBackdoor &backdoor) {
-                for (auto it = memBackdoors.begin();
-                        it != memBackdoors.end(); it++) {
-                    if (it->second == &backdoor) {
-                        memBackdoors.erase(it);
-                        return;
-                    }
-                }
-                panic("Got invalidation for unknown memory backdoor.");
-            };
-            bd->addInvalidationCallback(callback);
-        }
-
-        // Check if we're done now, since handleResp may delete state.
-        done = !state->gen.next();
-        handleRespPacket(pkt, lat);
-    } else {
- // We have a backdoor that can at least partially satisfy this request. - DPRINTF(DMA, "Handling DMA for addr: %#x size %d through backdoor\n",
-                state->gen.addr(), state->gen.size());
-
-        const auto *bd = bd_it->second;
-        // Offset of this access into the backdoor.
-        const Addr offset = state->gen.addr() - bd->range().start();
-        // How many bytes we still need.
-        const Addr remaining = state->totBytes - state->gen.complete();
-        // How many bytes this backdoor can provide, starting from offset.
-        const Addr available = bd->range().size() - offset;
-
-        // How many bytes we're going to handle through this backdoor.
-        const Addr handled = std::min(remaining, available);
-
-        // If there's a buffer for data, read/write it.
-        if (state->data) {
-            uint8_t *bd_data = bd->ptr() + offset;
-            uint8_t *state_data = state->data + state->gen.complete();
-            if (MemCmd(state->cmd).isRead())
-                memcpy(state_data, bd_data, handled);
-            else
-                memcpy(bd_data, state_data, handled);
-        }
-
-        // Advance the chunk generator past this region of memory.
-        state->gen.setNext(state->gen.addr() + handled);
-
-        // Check if we're done now, since handleResp may delete state.
-        done = !state->gen.next();
-        handleResp(state, state->gen.addr(), handled);
-    }
-
-    return done;
-}
-
-void
-DmaPort::sendDma()
-{
-    // Some kind of selection between access methods. More work is going to
-    // have to be done to make switching actually work.
-    assert(transmitList.size());
-
-    if (sys->isTimingMode()) {
-        // If we are either waiting for a retry or are still waiting after
-        // sending the last packet, then do not proceed.
-        if (inRetry || sendEvent.scheduled()) {
-            DPRINTF(DMA, "Can't send immediately, waiting to send\n");
-            return;
-        }
-
-        trySendTimingReq();
-    } else if (sys->isAtomicMode()) {
-        const bool bypass = sys->bypassCaches();
-
-        // Send everything there is to send in zero time.
-        while (!transmitList.empty()) {
-            DmaReqState *state = transmitList.front();
-            transmitList.pop_front();
-
-            bool done = state->gen.done();
-            while (!done)
- done = bypass ? sendAtomicBdReq(state) : sendAtomicReq(state);
-        }
-    } else {
-        panic("Unknown memory mode.");
-    }
-}
-
 Port &
 DmaDevice::getPort(const std::string &if_name, PortID idx)
 {
diff --git a/src/dev/dma_device.hh b/src/dev/dma_device.hh
index 330be1a..40ba115 100644
--- a/src/dev/dma_device.hh
+++ b/src/dev/dma_device.hh
@@ -47,6 +47,7 @@
 #include "base/addr_range_map.hh"
 #include "base/chunk_generator.hh"
 #include "base/circlebuf.hh"
+#include "base/mem_port.hh"
 #include "dev/io_device.hh"
 #include "mem/backdoor.hh"
 #include "params/DmaDevice.hh"
@@ -55,132 +56,33 @@

 class ClockedObject;

-class DmaPort : public RequestPort, public Drainable
+class DmaPort : public MemPort
 {
   private:
-    AddrRangeMap<MemBackdoorPtr, 1> memBackdoors;
-
-    /**
- * Take the first request on the transmit list and attempt to send a timing - * packet from it. If it is successful, schedule the sending of the next
-     * packet. Otherwise remember that we are waiting for a retry.
-     */
-    void trySendTimingReq();
-
-    /**
-     * For timing, attempt to send the first item on the transmit
-     * list, and if it is successful and there are more packets
-     * waiting, then schedule the sending of the next packet. For
-     * atomic, simply send and process everything on the transmit
-     * list.
-     */
-    void sendDma();
-
-    struct DmaReqState : public Packet::SenderState
+    struct DmaReqState : public ReqState
     {
-        /** Event to call on the device when this transaction (all packets)
-         * complete. */
-        Event *completionEvent;
-
-        /** Total number of bytes that this transaction involves. */
-        const Addr totBytes;
-
-        /** Number of bytes that have been acked for this transaction. */
-        Addr numBytes = 0;
-
-        /** Amount to delay completion of dma by */
-        const Tick delay;
-
-        /** Object to track what chunks of bytes to send at a time. */
-        ChunkGenerator gen;
-
-        /** Pointer to a buffer for the data. */
-        uint8_t *const data = nullptr;
-
-        /** The flags to use for requests. */
-        const Request::Flags flags;
-
-        /** The requestor ID to use for requests. */
-        const RequestorID id;
-
         /** Stream IDs. */
         const uint32_t sid;
         const uint32_t ssid;

-        /** Command for the request. */
-        const Packet::Command cmd;
-
DmaReqState(Packet::Command _cmd, Addr addr, Addr chunk_sz, Addr tb,
                     uint8_t *_data, Request::Flags _flags, RequestorID _id,
                     uint32_t _sid, uint32_t _ssid, Event *ce, Tick _delay)
-            : completionEvent(ce), totBytes(tb), delay(_delay),
-              gen(addr, tb, chunk_sz), data(_data), flags(_flags), id(_id),
-              sid(_sid), ssid(_ssid), cmd(_cmd)
+            : ReqState(_cmd, addr, chunk_sz, tb, _data, _flags, _id,
+                       ce, _delay), sid(_sid), ssid(_ssid)
         {}

-        PacketPtr createPacket();
+        PacketPtr createPacket() override;
     };

-    /** Send the next packet from a DMA request in atomic mode. */
-    bool sendAtomicReq(DmaReqState *state);
-    /**
-     * Send the next packet from a DMA request in atomic mode, and request
-     * and/or use memory backdoors if possible.
-     */
-    bool sendAtomicBdReq(DmaReqState *state);
-
-    /**
-     * Handle a response packet by updating the corresponding DMA
-     * request state to reflect the bytes received, and also update
-     * the pending request counter. If the DMA request that this
-     * packet is part of is complete, then signal the completion event
-     * if present, potentially with a delay added to it.
-     *
-     * @param pkt Response packet to handler
-     * @param delay Additional delay for scheduling the completion event
-     */
-    void handleRespPacket(PacketPtr pkt, Tick delay=0);
- void handleResp(DmaReqState *state, Addr addr, Addr size, Tick delay=0);
-
-  public:
-    /** The device that owns this port. */
-    ClockedObject *const device;
-
- /** The system that device/port are in. This is used to select which mode
-     * we are currently operating in. */
-    System *const sys;
-
-    /** Id for all requests */
-    const RequestorID requestorId;
-
   protected:
- /** Use a deque as we never do any insertion or removal in the middle */
-    std::deque<DmaReqState *> transmitList;
-
-    /** Event used to schedule a future sending from the transmit list. */
-    EventFunctionWrapper sendEvent;
-
-    /** Number of outstanding packets the dma port has. */
-    uint32_t pendingCount = 0;
-
-    /** The packet (if any) waiting for a retry to send. */
-    PacketPtr inRetry = nullptr;
-
     /** Default streamId */
     const uint32_t defaultSid;

     /** Default substreamId */
     const uint32_t defaultSSid;

-    const int cacheLineSize;
-
-  protected:
-
-    bool recvTimingResp(PacketPtr pkt) override;
-    void recvReqRetry() override;
-
   public:
-
DmaPort(ClockedObject *dev, System *s, uint32_t sid=0, uint32_t ssid=0);

     void
@@ -191,10 +93,6 @@
     dmaAction(Packet::Command cmd, Addr addr, int size, Event *event,
               uint8_t *data, uint32_t sid, uint32_t ssid, Tick delay,
               Request::Flags flag=0);
-
-    bool dmaPending() const { return pendingCount > 0; }
-
-    DrainState drain() override;
 };

 class DmaDevice : public PioDevice
@@ -235,7 +133,7 @@
         dmaPort.dmaAction(MemCmd::ReadReq, addr, size, event, data, delay);
     }

-    bool dmaPending() const { return dmaPort.dmaPending(); }
+    bool dmaPending() const { return dmaPort.memPending(); }

     void init() override;


--
To view, visit https://gem5-review.googlesource.com/c/public/gem5/+/44968
To unsubscribe, or for help writing mail filters, visit https://gem5-review.googlesource.com/settings

Gerrit-Project: public/gem5
Gerrit-Branch: develop
Gerrit-Change-Id: Ia56dd2ebda77e72f42f8c86c3e47ab2645dce755
Gerrit-Change-Number: 44968
Gerrit-PatchSet: 1
Gerrit-Owner: Giacomo Travaglini <[email protected]>
Gerrit-MessageType: newchange
_______________________________________________
gem5-dev mailing list -- [email protected]
To unsubscribe send an email to [email protected]
%(web_page_url)slistinfo%(cgiext)s/%(_internal_name)s

Reply via email to