#include <utility>
-#include "base/chunk_generator.hh"
#include "debug/DMA.hh"
#include "debug/Drain.hh"
#include "sim/clocked_object.hh"
void
DmaPort::handleResp(PacketPtr pkt, Tick delay)
{
- // should always see a response with a sender state
+ // Should always see a response with a sender state.
assert(pkt->isResponse());
- // get the DMA sender state
+ // Get the DMA sender state.
auto *state = dynamic_cast<DmaReqState*>(pkt->senderState);
assert(state);
state->completionEvent ?
state->completionEvent->scheduled() : 0);
- assert(pendingCount != 0);
- pendingCount--;
-
- // update the number of bytes received based on the request rather
- // than the packet as the latter could be rounded up to line sizes
+ // 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 += pkt->req->getSize();
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 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;
}
- // delete the packet
delete pkt;
- // we might be drained at this point, if so signal the drain event
+ // 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);
+
+ 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
+ // We shouldn't ever get a cacheable block in Modified state.
assert(pkt->req->isUncacheable() ||
!(pkt->cacheResponding() && !pkt->hasSharers()));
uint8_t *data, uint32_t sid, uint32_t ssid, Tick delay,
Request::Flags flag)
{
- // 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
- DmaReqState *reqState = new DmaReqState(event, size, delay);
-
- RequestPtr req = nullptr;
-
DPRINTF(DMA, "Starting DMA for addr: %#x size: %d sched: %d\n", addr, size,
event ? event->scheduled() : -1);
- for (ChunkGenerator gen(addr, size, cacheLineSize);
- !gen.done(); gen.next()) {
-
- req = std::make_shared<Request>(
- gen.addr(), gen.size(), flag, requestorId);
-
- req->setStreamId(sid);
- req->setSubstreamId(ssid);
-
- req->taskId(ContextSwitchTaskId::DMA);
- PacketPtr pkt = new Packet(req, cmd);
-
- // Increment the data pointer on a write
- if (data)
- pkt->dataStatic(data + gen.complete());
- pkt->senderState = reqState;
-
- DPRINTF(DMA, "--Queuing DMA for addr: %#x size: %d\n", gen.addr(),
- gen.size());
- queueDma(pkt);
- }
+ // 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++;
- // in zero time also initiate the sending of the packets we have
- // just created, for atomic this involves actually completing all
- // the requests
+ // 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();
}
defaultSid, defaultSSid, delay, flag);
}
-void
-DmaPort::queueDma(PacketPtr pkt)
-{
- transmitList.push_back(pkt);
-
- // remember that we have another packet pending, this will only be
- // decremented once a response comes back
- pendingCount++;
-}
-
void
DmaPort::trySendTimingReq()
{
- // send the first packet on the transmit list and schedule the
- // following send if it is successful
- PacketPtr pkt = transmitList.front();
+ // 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());
- inRetry = !sendTimingReq(pkt);
+ // 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) {
- transmitList.pop_front();
+ // 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
+ // 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);
+ transmitList.size(), inRetry ? 1 : 0);
}
void
DmaPort::sendDma()
{
- // some kind of selcetion between access methods
- // more work is going to have to be done to make
- // switching actually work
+ // 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()) {
} else if (sys->isAtomicMode()) {
// send everything there is to send in zero time
while (!transmitList.empty()) {
- PacketPtr pkt = transmitList.front();
+ DmaReqState *state = transmitList.front();
transmitList.pop_front();
- DPRINTF(DMA, "Sending DMA for addr: %#x size: %d\n",
- pkt->req->getPaddr(), pkt->req->getSize());
- Tick lat = sendAtomic(pkt);
-
- handleResp(pkt, lat);
+ bool done = state->gen.done();
+ while (!done) {
+ PacketPtr pkt = state->createPacket();
+ DPRINTF(DMA, "Sending DMA for addr: %#x size: %d\n",
+ pkt->req->getPaddr(), pkt->req->getSize());
+ Tick lat = sendAtomic(pkt);
+
+ // Check if we're done now, since handleResp may delete state.
+ done = !state->gen.next();
+ handleResp(pkt, lat);
+ }
}
} else {
panic("Unknown memory mode.");
#include <deque>
#include <memory>
+#include "base/chunk_generator.hh"
#include "base/circlebuf.hh"
#include "dev/io_device.hh"
#include "mem/port_proxy.hh"
/** Amount to delay completion of dma by */
const Tick delay;
- DmaReqState(Event *ce, Addr tb, Tick _delay)
- : completionEvent(ce), totBytes(tb), delay(_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)
{}
+
+ PacketPtr createPacket();
};
public:
protected:
/** Use a deque as we never do any insertion or removal in the middle */
- std::deque<PacketPtr> transmitList;
+ 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;
- /** If the port is currently waiting for a retry before it can
- * send whatever it is that it's sending. */
- bool inRetry = false;
+ /** The packet (if any) waiting for a retry to send. */
+ PacketPtr inRetry = nullptr;
/** Default streamId */
const uint32_t defaultSid;
bool recvTimingResp(PacketPtr pkt) override;
void recvReqRetry() override;
- void queueDma(PacketPtr pkt);
-
public:
DmaPort(ClockedObject *dev, System *s, uint32_t sid=0, uint32_t ssid=0);