From: Andreas Hansson Date: Tue, 23 Oct 2012 08:49:33 +0000 (-0400) Subject: dev: Remove zero-time loop in DMA timing send X-Git-Url: https://git.libre-soc.org/?a=commitdiff_plain;h=69e82539fd81299751e1000c0dac49f2eddbbdb6;p=gem5.git dev: Remove zero-time loop in DMA timing send This patch removes the zero-time loop used to send items from the DMA port transmit list. Instead of having a loop, the DMA port now uses an event to schedule sending of a single packet. Ultimately this patch serves to ease the transition to a blocking 4-phase handshake. A follow-on patch will update the regression statistics. --- diff --git a/src/dev/dma_device.cc b/src/dev/dma_device.cc index 43d45146f..1aa4a8647 100644 --- a/src/dev/dma_device.cc +++ b/src/dev/dma_device.cc @@ -49,8 +49,8 @@ #include "sim/system.hh" DmaPort::DmaPort(MemObject *dev, System *s) - : MasterPort(dev->name() + ".dma", dev), device(dev), sys(s), - masterId(s->getMasterId(dev->name())), + : MasterPort(dev->name() + ".dma", dev), device(dev), sendEvent(this), + sys(s), masterId(s->getMasterId(dev->name())), pendingCount(0), drainEvent(NULL), inRetry(false) { } @@ -152,24 +152,7 @@ void DmaPort::recvRetry() { assert(transmitList.size()); - bool result = true; - do { - PacketPtr pkt = transmitList.front(); - DPRINTF(DMA, "Retry on %s addr %#x\n", - pkt->cmdString(), pkt->getAddr()); - result = sendTimingReq(pkt); - if (result) { - DPRINTF(DMA, "-- Done\n"); - transmitList.pop_front(); - inRetry = false; - } else { - inRetry = true; - DPRINTF(DMA, "-- Failed, queued\n"); - } - } while (result && transmitList.size()); - - DPRINTF(DMA, "TransmitList: %d, inRetry: %d\n", - transmitList.size(), inRetry); + trySendTimingReq(); } void @@ -198,6 +181,11 @@ DmaPort::dmaAction(Packet::Command cmd, Addr addr, int size, Event *event, gen.size()); queueDma(pkt); } + + // in zero time also initiate the sending of the packets we have + // just created, for atomic this involves actually completing all + // the requests + sendDma(); } void @@ -208,8 +196,35 @@ DmaPort::queueDma(PacketPtr pkt) // remember that we have another packet pending, this will only be // decremented once a response comes back pendingCount++; +} - sendDma(); +void +DmaPort::trySendTimingReq() +{ + // send the first packet on the transmit list and schedule the + // following send if it is successful + PacketPtr pkt = transmitList.front(); + + DPRINTF(DMA, "Trying to send %s addr %#x\n", pkt->cmdString(), + pkt->getAddr()); + + inRetry = !sendTimingReq(pkt); + if (!inRetry) { + transmitList.pop_front(); + 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); } void @@ -219,37 +234,29 @@ DmaPort::sendDma() // more work is going to have to be done to make // switching actually work assert(transmitList.size()); - PacketPtr pkt = transmitList.front(); Enums::MemoryMode state = sys->getMemoryMode(); if (state == Enums::timing) { - if (inRetry) { - DPRINTF(DMA, "Can't send immediately, waiting for retry\n"); + // 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; } - DPRINTF(DMA, "Attempting to send %s addr %#x\n", - pkt->cmdString(), pkt->getAddr()); - - bool result; - do { - result = sendTimingReq(pkt); - if (result) { - transmitList.pop_front(); - DPRINTF(DMA, "-- Done\n"); - } else { - inRetry = true; - DPRINTF(DMA, "-- Failed: queued\n"); - } - } while (result && transmitList.size()); + trySendTimingReq(); } else if (state == Enums::atomic) { - transmitList.pop_front(); + // send everything there is to send in zero time + while (!transmitList.empty()) { + PacketPtr pkt = 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); + DPRINTF(DMA, "Sending DMA for addr: %#x size: %d\n", + pkt->req->getPaddr(), pkt->req->getSize()); + Tick lat = sendAtomic(pkt); - handleResp(pkt, lat); + handleResp(pkt, lat); + } } else panic("Unknown memory mode."); } diff --git a/src/dev/dma_device.hh b/src/dev/dma_device.hh index c46fbfd76..cd328f3d6 100644 --- a/src/dev/dma_device.hh +++ b/src/dev/dma_device.hh @@ -51,7 +51,37 @@ class DmaPort : public MasterPort { - protected: + private: + + /** + * Take the first packet of the transmit list and attempt to send + * it as a timing request. 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(); + + /** + * 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 handleResp(PacketPtr pkt, Tick delay = 0); + struct DmaReqState : public Packet::SenderState { /** Event to call on the device when this transaction (all packets) @@ -59,30 +89,34 @@ class DmaPort : public MasterPort Event *completionEvent; /** Total number of bytes that this transaction involves. */ - Addr totBytes; + const Addr totBytes; /** Number of bytes that have been acked for this transaction. */ Addr numBytes; /** Amount to delay completion of dma by */ - Tick delay; + const Tick delay; DmaReqState(Event *ce, Addr tb, Tick _delay) : completionEvent(ce), totBytes(tb), numBytes(0), delay(_delay) {} }; + /** The device that owns this port. */ MemObject *device; - /** Use a deque as we never to any insertion or removal in the middle */ + /** Use a deque as we never do any insertion or removal in the middle */ std::deque transmitList; + /** Event used to schedule a future sending from the transmit list. */ + EventWrapper sendEvent; + /** The system that device/port are in. This is used to select which mode * we are currently operating in. */ System *sys; /** Id for all requests */ - MasterID masterId; + const MasterID masterId; /** Number of outstanding packets the dma port has. */ uint32_t pendingCount; @@ -95,23 +129,12 @@ class DmaPort : public MasterPort * send whatever it is that it's sending. */ bool inRetry; - /** - * 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 handleResp(PacketPtr pkt, Tick delay = 0); + protected: bool recvTimingResp(PacketPtr pkt); void recvRetry() ; void queueDma(PacketPtr pkt); - void sendDma(); public: @@ -148,7 +171,7 @@ class DmaDevice : public PioDevice dmaPort.dmaAction(MemCmd::ReadReq, addr, size, event, data, delay); } - bool dmaPending() { return dmaPort.dmaPending(); } + bool dmaPending() const { return dmaPort.dmaPending(); } virtual void init(); @@ -159,7 +182,6 @@ class DmaDevice : public PioDevice virtual BaseMasterPort &getMasterPort(const std::string &if_name, PortID idx = InvalidPortID); - friend class DmaPort; }; #endif // __DEV_DMA_DEVICE_HH__