return (T)1 << ceilLog2(n);
}
-template <class T>
+template <class T, class U>
inline T
-divCeil(T a, T b)
+divCeil(const T& a, const U& b)
{
return (a + b - 1) / b;
}
* Definition of a bus object.
*/
+#include "base/intmath.hh"
#include "base/misc.hh"
#include "base/trace.hh"
#include "debug/Bus.hh"
#include "mem/bus.hh"
BaseBus::BaseBus(const BaseBusParams *p)
- : MemObject(p), clock(p->clock),
- headerCycles(p->header_cycles), width(p->width), tickNextIdle(0),
- drainEvent(NULL), busIdleEvent(this), inRetry(false),
+ : MemObject(p), state(IDLE), clock(p->clock),
+ headerCycles(p->header_cycles), width(p->width),
+ drainEvent(NULL), busIdleEvent(this),
defaultPortID(InvalidPortID),
useDefaultRange(p->use_default_range),
defaultBlockSize(p->block_size),
{
// determine the current time rounded to the closest following
// clock edge
- Tick now = curTick();
- if (now % clock != 0) {
- now = ((now / clock) + 1) * clock;
- }
+ Tick now = divCeil(curTick(), clock) * clock;
Tick headerTime = now + headerCycles * clock;
void BaseBus::occupyBus(Tick until)
{
- if (until == 0) {
- // shortcut for express snoop packets
- return;
- }
-
- tickNextIdle = until;
- reschedule(busIdleEvent, tickNextIdle, true);
-
- DPRINTF(BaseBus, "The bus is now occupied from tick %d to %d\n",
- curTick(), tickNextIdle);
+ // ensure the state is busy or in retry and never idle at this
+ // point, as the bus should transition from idle as soon as it has
+ // decided to forward the packet to prevent any follow-on calls to
+ // sendTiming seeing an unoccupied bus
+ assert(state != IDLE);
+
+ // note that we do not change the bus state here, if we are going
+ // from idle to busy it is handled by tryTiming, and if we
+ // are in retry we should remain in retry such that
+ // succeededTiming still sees the accurate state
+
+ // until should never be 0 as express snoops never occupy the bus
+ assert(until != 0);
+ schedule(busIdleEvent, until);
+
+ DPRINTF(BaseBus, "The bus is now busy from tick %d to %d\n",
+ curTick(), until);
}
bool
-BaseBus::isOccupied(Port* port)
+BaseBus::tryTiming(Port* port)
{
- // first we see if the next idle tick is in the future, next the
- // bus is considered occupied if there are ports on the retry list
- // and we are not in a retry with the current port
- if (tickNextIdle > curTick() ||
- (!retryList.empty() && !(inRetry && port == retryList.front()))) {
- addToRetryList(port);
- return true;
+ // first we see if the bus is busy, next we check if we are in a
+ // retry with a port other than the current one
+ if (state == BUSY || (state == RETRY && port != retryList.front())) {
+ // put the port at the end of the retry list
+ retryList.push_back(port);
+ return false;
}
- return false;
+
+ // update the state which is shared for request, response and
+ // snoop responses, if we were idle we are now busy, if we are in
+ // a retry, then do not change
+ if (state == IDLE)
+ state = BUSY;
+
+ return true;
}
void
BaseBus::succeededTiming(Tick busy_time)
{
- // occupy the bus accordingly
- occupyBus(busy_time);
-
// if a retrying port succeeded, also take it off the retry list
- if (inRetry) {
+ if (state == RETRY) {
DPRINTF(BaseBus, "Remove retry from list %s\n",
retryList.front()->name());
retryList.pop_front();
- inRetry = false;
+ state = BUSY;
+ }
+
+ // we should either have gone from idle to busy in the
+ // tryTiming test, or just gone from a retry to busy
+ assert(state == BUSY);
+
+ // occupy the bus accordingly
+ occupyBus(busy_time);
+}
+
+void
+BaseBus::failedTiming(SlavePort* port, Tick busy_time)
+{
+ // if we are not in a retry, i.e. busy (but never idle), or we are
+ // in a retry but not for the current port, then add the port at
+ // the end of the retry list
+ if (state != RETRY || port != retryList.front()) {
+ retryList.push_back(port);
}
+
+ // even if we retried the current one and did not succeed,
+ // we are no longer retrying but instead busy
+ state = BUSY;
+
+ // occupy the bus accordingly
+ occupyBus(busy_time);
}
void
BaseBus::releaseBus()
{
// releasing the bus means we should now be idle
- assert(curTick() >= tickNextIdle);
+ assert(state == BUSY);
+ assert(!busIdleEvent.scheduled());
+
+ // update the state
+ state = IDLE;
// bus is now idle, so if someone is waiting we can retry
if (!retryList.empty()) {
// busy, and in the latter case the bus may be released before
// we see a retry from the destination
retryWaiting();
- }
-
- //If we weren't able to drain before, we might be able to now.
- if (drainEvent && retryList.empty() && curTick() >= tickNextIdle) {
+ } else if (drainEvent) {
+ //If we weren't able to drain before, do it now.
drainEvent->process();
// Clear the drain event once we're done with it.
drainEvent = NULL;
// this should never be called with an empty retry list
assert(!retryList.empty());
- // send a retry to the port at the head of the retry list
- inRetry = true;
+ // we always go to retrying from idle
+ assert(state == IDLE);
+
+ // update the state which is shared for request, response and
+ // snoop responses
+ state = RETRY;
// note that we might have blocked on the receiving port being
// busy (rather than the bus itself) and now call retry before the
else
(dynamic_cast<MasterPort*>(retryList.front()))->sendRetry();
- // If inRetry is still true, sendTiming wasn't called in zero time
- // (e.g. the cache does this)
- if (inRetry) {
+ // If the bus is still in the retry state, sendTiming wasn't
+ // called in zero time (e.g. the cache does this)
+ if (state == RETRY) {
retryList.pop_front();
- inRetry = false;
-
- //Bring tickNextIdle up to the present
- while (tickNextIdle < curTick())
- tickNextIdle += clock;
//Burn a cycle for the missed grant.
- tickNextIdle += clock;
- reschedule(busIdleEvent, tickNextIdle, true);
+ // update the state which is shared for request, response and
+ // snoop responses
+ state = BUSY;
+
+ // determine the current time rounded to the closest following
+ // clock edge
+ Tick now = divCeil(curTick(), clock) * clock;
+
+ occupyBus(now + clock);
}
}
if (retryList.empty())
return;
- // if the bus isn't busy
- if (curTick() >= tickNextIdle) {
+ // if the bus is idle
+ if (state == IDLE) {
// note that we do not care who told us to retry at the moment, we
// merely let the first one on the retry list go
retryWaiting();
//We should check that we're not "doing" anything, and that noone is
//waiting. We might be idle but have someone waiting if the device we
//contacted for a retry didn't actually retry.
- if (!retryList.empty() || (curTick() < tickNextIdle &&
- busIdleEvent.scheduled())) {
+ if (!retryList.empty() || state != IDLE) {
drainEvent = de;
return 1;
}
return 0;
}
-
-void
-BaseBus::startup()
-{
- if (tickNextIdle < curTick())
- tickNextIdle = (curTick() / clock) * clock + clock;
-}
protected:
+ /**
+ * We declare an enum to track the state of the bus. The starting
+ * point is an idle state where the bus is waiting for a packet to
+ * arrive. Upon arrival, the bus transitions to the busy state,
+ * where it remains either until the packet transfer is done, or
+ * the header time is spent. Once the bus leaves the busy state,
+ * it can either go back to idle, if no packets have arrived while
+ * it was busy, or the bus goes on to retry the first port on the
+ * retryList. A similar transition takes place from idle to retry
+ * if the bus receives a retry from one of its connected
+ * ports. The retry state lasts until the port in questions calls
+ * sendTiming and returns control to the bus, or goes to a busy
+ * state if the port does not immediately react to the retry by
+ * calling sendTiming.
+ */
+ enum State { IDLE, BUSY, RETRY };
+
+ /** track the state of the bus */
+ State state;
+
/** the clock speed for the bus */
int clock;
/** cycles of overhead per transaction */
int headerCycles;
/** the width of the bus in bytes */
int width;
- /** the next tick at which the bus will be idle */
- Tick tickNextIdle;
Event * drainEvent;
AddrRangeList defaultRange;
/**
- * Determine if the bus is to be considered occupied when being
- * presented with a packet from a specific port. If so, the port
- * in question is also added to the retry list.
+ * Determine if the bus accepts a packet from a specific port. If
+ * not, the port in question is also added to the retry list. In
+ * either case the state of the bus is updated accordingly.
*
* @param port Source port on the bus presenting the packet
*
- * @return True if the bus is to be considered occupied
+ * @return True if the bus accepts the packet
*/
- bool isOccupied(Port* port);
+ bool tryTiming(Port* port);
/**
* Deal with a destination port accepting a packet by potentially
*/
void succeededTiming(Tick busy_time);
+ /**
+ * Deal with a destination port not accepting a packet by
+ * potentially adding the source port to the retry list (if
+ * not already at the front) and occupying the bus accordingly.
+ *
+ * @param busy_time Time to spend as a result of a failed send
+ */
+ void failedTiming(SlavePort* port, Tick busy_time);
+
/** Timing function called by port when it is once again able to process
* requests. */
void recvRetry();
// event used to schedule a release of the bus
EventWrapper<BaseBus, &BaseBus::releaseBus> busIdleEvent;
- bool inRetry;
std::set<PortID> inRecvRangeChange;
/** The master and slave ports of the bus */
* original send failed for whatever reason.*/
std::list<Port*> retryList;
- void addToRetryList(Port* port)
- {
- if (!inRetry) {
- // The device wasn't retrying a packet, or wasn't at an
- // appropriate time.
- retryList.push_back(port);
- } else {
- if (!retryList.empty() && port == retryList.front()) {
- // The device was retrying a packet. It didn't work,
- // so we'll leave it at the head of the retry list.
- inRetry = false;
- } else {
- // We are in retry, but not for this port, put it at
- // the end.
- retryList.push_back(port);
- }
- }
- }
-
/** Port that handles requests that don't match any of the interfaces.*/
PortID defaultPortID;
virtual MasterPort& getMasterPort(const std::string& if_name, int idx = -1);
virtual SlavePort& getSlavePort(const std::string& if_name, int idx = -1);
- virtual void startup();
-
unsigned int drain(Event *de);
};
// determine the source port based on the id
SlavePort *src_port = slavePorts[slave_port_id];
+ // remember if the packet is an express snoop
+ bool is_express_snoop = pkt->isExpressSnoop();
+
// test if the bus should be considered occupied for the current
// port, and exclude express snoops from the check
- if (!pkt->isExpressSnoop() && isOccupied(src_port)) {
+ if (!is_express_snoop && !tryTiming(src_port)) {
DPRINTF(CoherentBus, "recvTimingReq: src %s %s 0x%x BUSY\n",
src_port->name(), pkt->cmdString(), pkt->getAddr());
return false;
}
- DPRINTF(CoherentBus, "recvTimingReq: src %s %s 0x%x\n",
- src_port->name(), pkt->cmdString(), pkt->getAddr());
+ DPRINTF(CoherentBus, "recvTimingReq: src %s %s expr %d 0x%x\n",
+ src_port->name(), pkt->cmdString(), is_express_snoop,
+ pkt->getAddr());
// set the source port for routing of the response
pkt->setSrc(slave_port_id);
- Tick headerFinishTime = pkt->isExpressSnoop() ? 0 : calcPacketTiming(pkt);
- Tick packetFinishTime = pkt->isExpressSnoop() ? 0 : pkt->finishTime;
+ Tick headerFinishTime = is_express_snoop ? 0 : calcPacketTiming(pkt);
+ Tick packetFinishTime = is_express_snoop ? 0 : pkt->finishTime;
// uncacheable requests need never be snooped
if (!pkt->req->isUncacheable()) {
// necessary, if the packet needs a response, we should add it
// as outstanding and express snoops never fail so there is
// not need to worry about them
- bool add_outstanding = !pkt->isExpressSnoop() && pkt->needsResponse();
+ bool add_outstanding = !is_express_snoop && pkt->needsResponse();
// keep track that we have an outstanding request packet
// matching this request, this is used by the coherency
// based on the address and attempt to send the packet
bool success = masterPorts[findPort(pkt->getAddr())]->sendTimingReq(pkt);
- if (!success) {
- // inhibited packets should never be forced to retry
- assert(!pkt->memInhibitAsserted());
-
- // if it was added as outstanding and the send failed, then
- // erase it again
- if (add_outstanding)
- outstandingReq.erase(pkt->req);
-
- DPRINTF(CoherentBus, "recvTimingReq: src %s %s 0x%x RETRY\n",
- src_port->name(), pkt->cmdString(), pkt->getAddr());
-
- addToRetryList(src_port);
- occupyBus(headerFinishTime);
-
- return false;
+ // if this is an express snoop, we are done at this point
+ if (is_express_snoop) {
+ assert(success);
+ } else {
+ // for normal requests, check if successful
+ if (!success) {
+ // inhibited packets should never be forced to retry
+ assert(!pkt->memInhibitAsserted());
+
+ // if it was added as outstanding and the send failed, then
+ // erase it again
+ if (add_outstanding)
+ outstandingReq.erase(pkt->req);
+
+ DPRINTF(CoherentBus, "recvTimingReq: src %s %s 0x%x RETRY\n",
+ src_port->name(), pkt->cmdString(), pkt->getAddr());
+
+ // update the bus state and schedule an idle event
+ failedTiming(src_port, headerFinishTime);
+ } else {
+ // update the bus state and schedule an idle event
+ succeededTiming(packetFinishTime);
+ }
}
- succeededTiming(packetFinishTime);
-
- return true;
+ return success;
}
bool
// test if the bus should be considered occupied for the current
// port
- if (isOccupied(src_port)) {
+ if (!tryTiming(src_port)) {
DPRINTF(CoherentBus, "recvTimingResp: src %s %s 0x%x BUSY\n",
src_port->name(), pkt->cmdString(), pkt->getAddr());
return false;
// wrong, hence there is nothing further to do as the packet
// would be going back to where it came from
assert(master_port_id == findPort(pkt->getAddr()));
-
- // this is an express snoop and is never forced to retry
- assert(!inRetry);
}
bool
// test if the bus should be considered occupied for the current
// port
- if (isOccupied(src_port)) {
+ if (!tryTiming(src_port)) {
DPRINTF(CoherentBus, "recvTimingSnoopResp: src %s %s 0x%x BUSY\n",
src_port->name(), pkt->cmdString(), pkt->getAddr());
return false;
// test if the bus should be considered occupied for the current
// port
- if (isOccupied(src_port)) {
+ if (!tryTiming(src_port)) {
DPRINTF(NoncoherentBus, "recvTimingReq: src %s %s 0x%x BUSY\n",
src_port->name(), pkt->cmdString(), pkt->getAddr());
return false;
DPRINTF(NoncoherentBus, "recvTimingReq: src %s %s 0x%x RETRY\n",
src_port->name(), pkt->cmdString(), pkt->getAddr());
- addToRetryList(src_port);
- occupyBus(headerFinishTime);
+ failedTiming(src_port, headerFinishTime);
return false;
}
// test if the bus should be considered occupied for the current
// port
- if (isOccupied(src_port)) {
+ if (!tryTiming(src_port)) {
DPRINTF(NoncoherentBus, "recvTimingResp: src %s %s 0x%x BUSY\n",
src_port->name(), pkt->cmdString(), pkt->getAddr());
return false;