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.
This commit is contained in:
parent
37ded2c2cc
commit
69e82539fd
2 changed files with 116 additions and 87 deletions
|
@ -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.");
|
||||
}
|
||||
|
|
|
@ -51,49 +51,24 @@
|
|||
|
||||
class DmaPort : public MasterPort
|
||||
{
|
||||
protected:
|
||||
struct DmaReqState : public Packet::SenderState
|
||||
{
|
||||
/** Event to call on the device when this transaction (all packets)
|
||||
* complete. */
|
||||
Event *completionEvent;
|
||||
private:
|
||||
|
||||
/** Total number of bytes that this transaction involves. */
|
||||
Addr totBytes;
|
||||
/**
|
||||
* 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();
|
||||
|
||||
/** Number of bytes that have been acked for this transaction. */
|
||||
Addr numBytes;
|
||||
|
||||
/** Amount to delay completion of dma by */
|
||||
Tick delay;
|
||||
|
||||
DmaReqState(Event *ce, Addr tb, Tick _delay)
|
||||
: completionEvent(ce), totBytes(tb), numBytes(0), delay(_delay)
|
||||
{}
|
||||
};
|
||||
|
||||
MemObject *device;
|
||||
|
||||
/** Use a deque as we never to any insertion or removal in the middle */
|
||||
std::deque<PacketPtr> transmitList;
|
||||
|
||||
/** 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;
|
||||
|
||||
/** Number of outstanding packets the dma port has. */
|
||||
uint32_t pendingCount;
|
||||
|
||||
/** If we need to drain, keep the drain event around until we're done
|
||||
* here.*/
|
||||
Event *drainEvent;
|
||||
|
||||
/** If the port is currently waiting for a retry before it can
|
||||
* send whatever it is that it's sending. */
|
||||
bool inRetry;
|
||||
/**
|
||||
* 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
|
||||
|
@ -107,11 +82,59 @@ class DmaPort : public MasterPort
|
|||
*/
|
||||
void handleResp(PacketPtr pkt, Tick delay = 0);
|
||||
|
||||
struct DmaReqState : 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;
|
||||
|
||||
/** Amount to delay completion of dma by */
|
||||
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 do any insertion or removal in the middle */
|
||||
std::deque<PacketPtr> transmitList;
|
||||
|
||||
/** Event used to schedule a future sending from the transmit list. */
|
||||
EventWrapper<DmaPort, &DmaPort::sendDma> 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 */
|
||||
const MasterID masterId;
|
||||
|
||||
/** Number of outstanding packets the dma port has. */
|
||||
uint32_t pendingCount;
|
||||
|
||||
/** If we need to drain, keep the drain event around until we're done
|
||||
* here.*/
|
||||
Event *drainEvent;
|
||||
|
||||
/** If the port is currently waiting for a retry before it can
|
||||
* send whatever it is that it's sending. */
|
||||
bool inRetry;
|
||||
|
||||
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__
|
||||
|
|
Loading…
Reference in a new issue