diff options
Diffstat (limited to 'src/mem/bridge.cc')
-rw-r--r-- | src/mem/bridge.cc | 244 |
1 files changed, 198 insertions, 46 deletions
diff --git a/src/mem/bridge.cc b/src/mem/bridge.cc index b787f79ca..9509aea38 100644 --- a/src/mem/bridge.cc +++ b/src/mem/bridge.cc @@ -43,20 +43,25 @@ Bridge::BridgePort::BridgePort(const std::string &_name, Bridge *_bridge, BridgePort *_otherPort, - int _delay, int _queueLimit) + int _delay, int _nack_delay, int _req_limit, + int _resp_limit, bool fix_partial_write) : Port(_name), bridge(_bridge), otherPort(_otherPort), - delay(_delay), outstandingResponses(0), - queueLimit(_queueLimit), sendEvent(this) + delay(_delay), nackDelay(_nack_delay), fixPartialWrite(fix_partial_write), + outstandingResponses(0), queuedRequests(0), inRetry(false), + reqQueueLimit(_req_limit), respQueueLimit(_resp_limit), sendEvent(this) { } -Bridge::Bridge(const std::string &n, int qsa, int qsb, - Tick _delay, int write_ack) - : MemObject(n), - portA(n + "-portA", this, &portB, _delay, qsa), - portB(n + "-portB", this, &portA, _delay, qsa), - ackWrites(write_ack) +Bridge::Bridge(Params *p) + : MemObject(p->name), + portA(p->name + "-portA", this, &portB, p->delay, p->nack_delay, + p->req_size_a, p->resp_size_a, p->fix_partial_write_a), + portB(p->name + "-portB", this, &portA, p->delay, p->nack_delay, + p->req_size_b, p->resp_size_b, p->fix_partial_write_b), + ackWrites(p->write_ack), _params(p) { + if (ackWrites) + panic("No support for acknowledging writes\n"); } Port * @@ -82,35 +87,118 @@ Bridge::init() { // Make sure that both sides are connected to. if (portA.getPeer() == NULL || portB.getPeer() == NULL) - panic("Both ports of bus bridge are not connected to a bus.\n"); + fatal("Both ports of bus bridge are not connected to a bus.\n"); + + if (portA.peerBlockSize() != portB.peerBlockSize()) + fatal("Busses don't have the same block size... Not supported.\n"); +} + +bool +Bridge::BridgePort::respQueueFull() +{ + assert(outstandingResponses >= 0 && outstandingResponses <= respQueueLimit); + return outstandingResponses >= respQueueLimit; } +bool +Bridge::BridgePort::reqQueueFull() +{ + assert(queuedRequests >= 0 && queuedRequests <= reqQueueLimit); + return queuedRequests >= reqQueueLimit; +} /** Function called by the port when the bus is receiving a Timing * transaction.*/ bool Bridge::BridgePort::recvTiming(PacketPtr pkt) { - if (pkt->flags & SNOOP_COMMIT) { - DPRINTF(BusBridge, "recvTiming: src %d dest %d addr 0x%x\n", + if (!(pkt->flags & SNOOP_COMMIT)) + return true; + + + DPRINTF(BusBridge, "recvTiming: src %d dest %d addr 0x%x\n", pkt->getSrc(), pkt->getDest(), pkt->getAddr()); - return otherPort->queueForSendTiming(pkt); + DPRINTF(BusBridge, "Local queue size: %d outreq: %d outresp: %d\n", + sendQueue.size(), queuedRequests, outstandingResponses); + DPRINTF(BusBridge, "Remove queue size: %d outreq: %d outresp: %d\n", + otherPort->sendQueue.size(), otherPort->queuedRequests, + otherPort->outstandingResponses); + + if (pkt->isRequest() && otherPort->reqQueueFull() && pkt->result != + Packet::Nacked) { + DPRINTF(BusBridge, "Remote queue full, nacking\n"); + nackRequest(pkt); + return true; } - else { - // Else it's just a snoop, properly return if we are blocking - return !queueFull(); + + if (pkt->needsResponse() && pkt->result != Packet::Nacked) + if (respQueueFull()) { + DPRINTF(BusBridge, "Local queue full, no space for response, nacking\n"); + DPRINTF(BusBridge, "queue size: %d outreq: %d outstanding resp: %d\n", + sendQueue.size(), queuedRequests, outstandingResponses); + nackRequest(pkt); + return true; + } else { + DPRINTF(BusBridge, "Request Needs response, reserving space\n"); + ++outstandingResponses; + } + + otherPort->queueForSendTiming(pkt); + + return true; +} + +void +Bridge::BridgePort::nackRequest(PacketPtr pkt) +{ + // Nack the packet + pkt->result = Packet::Nacked; + pkt->setDest(pkt->getSrc()); + + //put it on the list to send + Tick readyTime = curTick + nackDelay; + PacketBuffer *buf = new PacketBuffer(pkt, readyTime, true); + + // nothing on the list, add it and we're done + if (sendQueue.empty()) { + assert(!sendEvent.scheduled()); + sendEvent.schedule(readyTime); + sendQueue.push_back(buf); + return; + } + + assert(sendEvent.scheduled() || inRetry); + + // does it go at the end? + if (readyTime >= sendQueue.back()->ready) { + sendQueue.push_back(buf); + return; + } + + // ok, somewhere in the middle, fun + std::list<PacketBuffer*>::iterator i = sendQueue.begin(); + std::list<PacketBuffer*>::iterator end = sendQueue.end(); + std::list<PacketBuffer*>::iterator begin = sendQueue.begin(); + bool done = false; + + while (i != end && !done) { + if (readyTime < (*i)->ready) { + if (i == begin) + sendEvent.reschedule(readyTime); + sendQueue.insert(i,buf); + done = true; + } + i++; } + assert(done); } -bool +void Bridge::BridgePort::queueForSendTiming(PacketPtr pkt) { - if (queueFull()) - return false; - - if (pkt->isResponse()) { + if (pkt->isResponse() || pkt->result == Packet::Nacked) { // This is a response for a request we forwarded earlier. The // corresponding PacketBuffer should be stored in the packet's // senderState field. @@ -119,12 +207,26 @@ Bridge::BridgePort::queueForSendTiming(PacketPtr pkt) // set up new packet dest & senderState based on values saved // from original request buf->fixResponse(pkt); + + // Check if this packet was expecting a response and it's a nacked + // packet, in which case we will never being seeing it + if (buf->expectResponse && pkt->result == Packet::Nacked) + --outstandingResponses; + + DPRINTF(BusBridge, "restoring sender state: %#X, from packet buffer: %#X\n", pkt->senderState, buf); DPRINTF(BusBridge, " is response, new dest %d\n", pkt->getDest()); delete buf; } + + if (pkt->isRequest() && pkt->result != Packet::Nacked) { + ++queuedRequests; + } + + + Tick readyTime = curTick + delay; PacketBuffer *buf = new PacketBuffer(pkt, readyTime); DPRINTF(BusBridge, "old sender state: %#X, new sender state: %#X\n", @@ -137,10 +239,7 @@ Bridge::BridgePort::queueForSendTiming(PacketPtr pkt) if (sendQueue.empty()) { sendEvent.schedule(readyTime); } - sendQueue.push_back(buf); - - return true; } void @@ -148,28 +247,38 @@ Bridge::BridgePort::trySend() { assert(!sendQueue.empty()); - bool was_full = queueFull(); - PacketBuffer *buf = sendQueue.front(); assert(buf->ready <= curTick); PacketPtr pkt = buf->pkt; + pkt->flags &= ~SNOOP_COMMIT; //CLear it if it was set + + // Ugly! @todo When multilevel coherence works this will be removed + if (pkt->cmd == MemCmd::WriteInvalidateReq && fixPartialWrite && + pkt->result != Packet::Nacked) { + PacketPtr funcPkt = new Packet(pkt->req, MemCmd::WriteReq, + Packet::Broadcast); + funcPkt->dataStatic(pkt->getPtr<uint8_t>()); + sendFunctional(funcPkt); + pkt->cmd = MemCmd::WriteReq; + delete funcPkt; + } + DPRINTF(BusBridge, "trySend: origSrc %d dest %d addr 0x%x\n", buf->origSrc, pkt->getDest(), pkt->getAddr()); - pkt->flags &= ~SNOOP_COMMIT; //CLear it if it was set + bool wasReq = pkt->isRequest(); + bool wasNacked = pkt->result == Packet::Nacked; + if (sendTiming(pkt)) { // send successful sendQueue.pop_front(); buf->pkt = NULL; // we no longer own packet, so it's not safe to look at it if (buf->expectResponse) { - // Must wait for response. We just need to count outstanding - // responses (in case we want to cap them); PacketBuffer - // pointer will be recovered on response. - ++outstandingResponses; + // Must wait for response DPRINTF(BusBridge, " successful: awaiting response (%d)\n", outstandingResponses); } else { @@ -178,27 +287,37 @@ Bridge::BridgePort::trySend() delete buf; } + if (!wasNacked) { + if (wasReq) + --queuedRequests; + else + --outstandingResponses; + } + // If there are more packets to send, schedule event to try again. if (!sendQueue.empty()) { buf = sendQueue.front(); + DPRINTF(BusBridge, "Scheduling next send\n"); sendEvent.schedule(std::max(buf->ready, curTick + 1)); } - // Let things start sending again - if (was_full) { - DPRINTF(BusBridge, "Queue was full, sending retry\n"); - otherPort->sendRetry(); - } - } else { DPRINTF(BusBridge, " unsuccessful\n"); + inRetry = true; } + DPRINTF(BusBridge, "trySend: queue size: %d outreq: %d outstanding resp: %d\n", + sendQueue.size(), queuedRequests, outstandingResponses); } void Bridge::BridgePort::recvRetry() { - trySend(); + inRetry = false; + Tick nextReady = sendQueue.front()->ready; + if (nextReady <= curTick) + trySend(); + else + sendEvent.schedule(nextReady); } /** Function called by the port when the bus is receiving a Atomic @@ -206,7 +325,18 @@ Bridge::BridgePort::recvRetry() Tick Bridge::BridgePort::recvAtomic(PacketPtr pkt) { - return otherPort->sendAtomic(pkt) + delay; + // fix partial atomic writes... similar to the timing code that does the + // same... will be removed once our code gets this right + if (pkt->cmd == MemCmd::WriteInvalidateReq && fixPartialWrite) { + + PacketPtr funcPkt = new Packet(pkt->req, MemCmd::WriteReq, + Packet::Broadcast); + funcPkt->dataStatic(pkt->getPtr<uint8_t>()); + otherPort->sendFunctional(funcPkt); + delete funcPkt; + pkt->cmd = MemCmd::WriteReq; + } + return delay + otherPort->sendAtomic(pkt); } /** Function called by the port when the bus is receiving a Functional @@ -244,26 +374,48 @@ Bridge::BridgePort::getDeviceAddressRanges(AddrRangeList &resp, BEGIN_DECLARE_SIM_OBJECT_PARAMS(Bridge) - Param<int> queue_size_a; - Param<int> queue_size_b; + Param<int> req_size_a; + Param<int> req_size_b; + Param<int> resp_size_a; + Param<int> resp_size_b; Param<Tick> delay; + Param<Tick> nack_delay; Param<bool> write_ack; + Param<bool> fix_partial_write_a; + Param<bool> fix_partial_write_b; END_DECLARE_SIM_OBJECT_PARAMS(Bridge) BEGIN_INIT_SIM_OBJECT_PARAMS(Bridge) - INIT_PARAM(queue_size_a, "The size of the queue for data coming into side a"), - INIT_PARAM(queue_size_b, "The size of the queue for data coming into side b"), + INIT_PARAM(req_size_a, "The size of the queue for requests coming into side a"), + INIT_PARAM(req_size_b, "The size of the queue for requests coming into side b"), + INIT_PARAM(resp_size_a, "The size of the queue for responses coming into side a"), + INIT_PARAM(resp_size_b, "The size of the queue for responses coming into side b"), INIT_PARAM(delay, "The miminum delay to cross this bridge"), - INIT_PARAM(write_ack, "Acknowledge any writes that are received.") + INIT_PARAM(nack_delay, "The minimum delay to nack a packet"), + INIT_PARAM(write_ack, "Acknowledge any writes that are received."), + INIT_PARAM(fix_partial_write_a, "Fixup any partial block writes that are received"), + INIT_PARAM(fix_partial_write_b, "Fixup any partial block writes that are received") END_INIT_SIM_OBJECT_PARAMS(Bridge) CREATE_SIM_OBJECT(Bridge) { - return new Bridge(getInstanceName(), queue_size_a, queue_size_b, delay, - write_ack); + Bridge::Params *p = new Bridge::Params; + p->name = getInstanceName(); + p->req_size_a = req_size_a; + p->req_size_b = req_size_b; + p->resp_size_a = resp_size_a; + p->resp_size_b = resp_size_b; + p->delay = delay; + p->nack_delay = nack_delay; + p->write_ack = write_ack; + p->fix_partial_write_a = fix_partial_write_a; + p->fix_partial_write_b = fix_partial_write_b; + return new Bridge(p); } REGISTER_SIM_OBJECT("Bridge", Bridge) + + |