/* * Copyright (c) 2012 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. * * Authors: Ali Saidi * Andreas Hansson */ #include "debug/PacketQueue.hh" #include "mem/packet_queue.hh" using namespace std; PacketQueue::PacketQueue(EventManager& _em, const std::string& _label) : em(_em), sendEvent(this), drainEvent(NULL), label(_label), waitingOnRetry(false) { } PacketQueue::~PacketQueue() { } void PacketQueue::retry() { DPRINTF(PacketQueue, "Queue %s received retry\n", name()); assert(waitingOnRetry); sendDeferredPacket(); } bool PacketQueue::checkFunctional(PacketPtr pkt) { pkt->pushLabel(label); DeferredPacketIterator i = transmitList.begin(); DeferredPacketIterator end = transmitList.end(); bool found = false; while (!found && i != end) { // If the buffered packet contains data, and it overlaps the // current packet, then update data found = pkt->checkFunctional(i->pkt); ++i; } pkt->popLabel(); return found; } void PacketQueue::schedSendEvent(Tick when) { // if we are waiting on a retry, do not schedule a send event, and // instead rely on retry being called if (waitingOnRetry) { assert(!sendEvent.scheduled()); return; } if (!sendEvent.scheduled()) { em.schedule(&sendEvent, when); } else if (sendEvent.when() > when) { em.reschedule(&sendEvent, when); } } void PacketQueue::schedSendTiming(PacketPtr pkt, Tick when, bool send_as_snoop) { assert(when > curTick()); // nothing on the list, or earlier than current front element, // schedule an event if (transmitList.empty() || when < transmitList.front().tick) { // note that currently we ignore a potentially outstanding retry // and could in theory put a new packet at the head of the // transmit list before retrying the existing packet transmitList.push_front(DeferredPacket(when, pkt, send_as_snoop)); schedSendEvent(when); return; } // list is non-empty and this belongs at the end if (when >= transmitList.back().tick) { transmitList.push_back(DeferredPacket(when, pkt, send_as_snoop)); return; } // this belongs in the middle somewhere, insertion sort DeferredPacketIterator i = transmitList.begin(); ++i; // already checked for insertion at front while (i != transmitList.end() && when >= i->tick) ++i; transmitList.insert(i, DeferredPacket(when, pkt, send_as_snoop)); } void PacketQueue::trySendTiming() { assert(deferredPacketReady()); // take the next packet off the list here, as we might return to // ourselves through the sendTiming call below DeferredPacket dp = transmitList.front(); transmitList.pop_front(); // use the appropriate implementation of sendTiming based on the // type of port associated with the queue, and whether the packet // is to be sent as a snoop or not waitingOnRetry = !sendTiming(dp.pkt, dp.sendAsSnoop); if (waitingOnRetry) { // put the packet back at the front of the list (packet should // not have changed since it wasn't accepted) assert(!sendEvent.scheduled()); transmitList.push_front(dp); } } void PacketQueue::scheduleSend(Tick time) { // the next ready time is either determined by the next deferred packet, // or in the cache through the MSHR ready time Tick nextReady = std::min(deferredPacketReadyTime(), time); if (nextReady != MaxTick) { // if the sendTiming caused someone else to call our // recvTiming we could already have an event scheduled, check if (!sendEvent.scheduled()) em.schedule(&sendEvent, std::max(nextReady, curTick() + 1)); } else { // no more to send, so if we're draining, we may be done if (drainEvent && !sendEvent.scheduled()) { drainEvent->process(); drainEvent = NULL; } } } void PacketQueue::sendDeferredPacket() { // try to send what is on the list, this will set waitingOnRetry // accordingly trySendTiming(); // if we succeeded and are not waiting for a retry, schedule the // next send if (!waitingOnRetry) { scheduleSend(); } } void PacketQueue::processSendEvent() { assert(!waitingOnRetry); sendDeferredPacket(); } unsigned int PacketQueue::drain(Event *de) { if (transmitList.empty() && !sendEvent.scheduled()) return 0; drainEvent = de; return 1; } MasterPacketQueue::MasterPacketQueue(EventManager& _em, MasterPort& _masterPort, const std::string _label) : PacketQueue(_em, _label), masterPort(_masterPort) { } bool MasterPacketQueue::sendTiming(PacketPtr pkt, bool send_as_snoop) { // attempt to send the packet and return according to the outcome if (!send_as_snoop) return masterPort.sendTimingReq(pkt); else return masterPort.sendTimingSnoopResp(pkt); } SlavePacketQueue::SlavePacketQueue(EventManager& _em, SlavePort& _slavePort, const std::string _label) : PacketQueue(_em, _label), slavePort(_slavePort) { } bool SlavePacketQueue::sendTiming(PacketPtr pkt, bool send_as_snoop) { // we should never have queued snoop requests assert(!send_as_snoop); return slavePort.sendTimingResp(pkt); }