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