X-Git-Url: https://git.libre-soc.org/?a=blobdiff_plain;f=src%2Fmem%2Fbus.cc;h=d29422593214fd3971b8a5c6410988c1afa0a459;hb=8573a69d8f7bf7b3f074e3e0ac64994801c551be;hp=911276f75454ec3d64de29791d6a1e5345b2ea0e;hpb=beed20d7bc31512cc36304bd64876fe4af014641;p=gem5.git diff --git a/src/mem/bus.cc b/src/mem/bus.cc index 911276f75..d29422593 100644 --- a/src/mem/bus.cc +++ b/src/mem/bus.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011-2012 ARM Limited + * Copyright (c) 2011-2013 ARM Limited * All rights reserved * * The license below extends only to copyright in the software and shall @@ -51,68 +51,75 @@ #include "base/trace.hh" #include "debug/Bus.hh" #include "debug/BusAddrRanges.hh" +#include "debug/Drain.hh" #include "mem/bus.hh" -Bus::Bus(const BusParams *p) - : MemObject(p), clock(p->clock), - headerCycles(p->header_cycles), width(p->width), tickNextIdle(0), - drainEvent(NULL), busIdleEvent(this), inRetry(false), - defaultPortId(Port::INVALID_PORT_ID), +BaseBus::BaseBus(const BaseBusParams *p) + : MemObject(p), + headerCycles(p->header_cycles), width(p->width), + gotAddrRanges(p->port_default_connection_count + + p->port_master_connection_count, false), + gotAllAddrRanges(false), defaultPortID(InvalidPortID), useDefaultRange(p->use_default_range), - defaultBlockSize(p->block_size), - cachedBlockSize(0), cachedBlockSizeValid(false) + blockSize(p->block_size) +{} + +BaseBus::~BaseBus() { - //width, clock period, and header cycles must be positive - if (width <= 0) - fatal("Bus width must be positive\n"); - if (clock <= 0) - fatal("Bus clock period must be positive\n"); - if (headerCycles <= 0) - fatal("Number of header cycles must be positive\n"); - - // create the ports based on the size of the master and slave - // vector ports, and the presence of the default port, the ports - // are enumerated starting from zero - for (int i = 0; i < p->port_master_connection_count; ++i) { - std::string portName = csprintf("%s-p%d", name(), i); - MasterPort* bp = new BusMasterPort(portName, this, i); - masterPorts.push_back(bp); + for (MasterPortIter m = masterPorts.begin(); m != masterPorts.end(); + ++m) { + delete *m; } - // see if we have a default slave device connected and if so add - // our corresponding master port - if (p->port_default_connection_count) { - defaultPortId = masterPorts.size(); - std::string portName = csprintf("%s-default", name()); - MasterPort* bp = new BusMasterPort(portName, this, defaultPortId); - masterPorts.push_back(bp); + for (SlavePortIter s = slavePorts.begin(); s != slavePorts.end(); + ++s) { + delete *s; + } +} + +void +BaseBus::init() +{ + // determine the maximum peer block size, look at both the + // connected master and slave modules + uint32_t peer_block_size = 0; + + for (MasterPortConstIter m = masterPorts.begin(); m != masterPorts.end(); + ++m) { + peer_block_size = std::max((*m)->peerBlockSize(), peer_block_size); } - // create the slave ports, once again starting at zero - for (int i = 0; i < p->port_slave_connection_count; ++i) { - std::string portName = csprintf("%s-p%d", name(), i); - SlavePort* bp = new BusSlavePort(portName, this, i); - slavePorts.push_back(bp); + for (SlavePortConstIter s = slavePorts.begin(); s != slavePorts.end(); + ++s) { + peer_block_size = std::max((*s)->peerBlockSize(), peer_block_size); } - clearPortCache(); + // if the peers do not have a block size, use the default value + // set through the bus parameters + if (peer_block_size != 0) + blockSize = peer_block_size; + + // check if the block size is a value known to work + if (!(blockSize == 16 || blockSize == 32 || blockSize == 64 || + blockSize == 128)) + warn_once("Block size is neither 16, 32, 64 or 128 bytes.\n"); } -MasterPort & -Bus::getMasterPort(const std::string &if_name, int idx) +BaseMasterPort & +BaseBus::getMasterPort(const std::string &if_name, PortID idx) { if (if_name == "master" && idx < masterPorts.size()) { // the master port index translates directly to the vector position return *masterPorts[idx]; } else if (if_name == "default") { - return *masterPorts[defaultPortId]; + return *masterPorts[defaultPortID]; } else { return MemObject::getMasterPort(if_name, idx); } } -SlavePort & -Bus::getSlavePort(const std::string &if_name, int idx) +BaseSlavePort & +BaseBus::getSlavePort(const std::string &if_name, PortID idx) { if (if_name == "slave" && idx < slavePorts.size()) { // the slave port index translates directly to the vector position @@ -123,299 +130,132 @@ Bus::getSlavePort(const std::string &if_name, int idx) } void -Bus::init() -{ - // iterate over our slave ports and determine which of our - // neighbouring master ports are snooping and add them as snoopers - for (SlavePortConstIter p = slavePorts.begin(); p != slavePorts.end(); - ++p) { - if ((*p)->getMasterPort().isSnooping()) { - DPRINTF(BusAddrRanges, "Adding snooping neighbour %s\n", - (*p)->getMasterPort().name()); - snoopPorts.push_back(*p); - } - } -} - -Tick -Bus::calcPacketTiming(PacketPtr pkt) +BaseBus::calcPacketTiming(PacketPtr pkt) { - // determine the current time rounded to the closest following - // clock edge - Tick now = curTick(); - if (now % clock != 0) { - now = ((now / clock) + 1) * clock; - } - - Tick headerTime = now + headerCycles * clock; - - // The packet will be sent. Figure out how long it occupies the bus, and - // how much of that time is for the first "word", aka bus width. - int numCycles = 0; - if (pkt->hasData()) { - // If a packet has data, it needs ceil(size/width) cycles to send it - int dataSize = pkt->getSize(); - numCycles += dataSize/width; - if (dataSize % width) - numCycles++; - } - - // The first word will be delivered after the current tick, the delivery - // of the address if any, and one bus cycle to deliver the data - pkt->firstWordTime = headerTime + clock; - - pkt->finishTime = headerTime + numCycles * clock; - - return headerTime; + // the bus will be called at a time that is not necessarily + // coinciding with its own clock, so start by determining how long + // until the next clock edge (could be zero) + Tick offset = nextCycle() - curTick(); + + // determine how many cycles are needed to send the data + unsigned dataCycles = pkt->hasData() ? divCeil(pkt->getSize(), width) : 0; + + // before setting the bus delay fields of the packet, ensure that + // the delay from any previous bus has been accounted for + if (pkt->busFirstWordDelay != 0 || pkt->busLastWordDelay != 0) + panic("Packet %s already has bus delay (%d, %d) that should be " + "accounted for.\n", pkt->cmdString(), pkt->busFirstWordDelay, + pkt->busLastWordDelay); + + // The first word will be delivered on the cycle after the header. + pkt->busFirstWordDelay = (headerCycles + 1) * clockPeriod() + offset; + + // Note that currently busLastWordDelay can be smaller than + // busFirstWordDelay if the packet has no data + pkt->busLastWordDelay = (headerCycles + dataCycles) * clockPeriod() + + offset; } -void Bus::occupyBus(Tick until) +template +BaseBus::Layer::Layer(BaseBus& _bus, const std::string& _name) : + Drainable(), + bus(_bus), _name(_name), state(IDLE), drainManager(NULL), + releaseEvent(this) { - if (until == 0) { - // shortcut for express snoop packets - return; - } - - tickNextIdle = until; - reschedule(busIdleEvent, tickNextIdle, true); - - DPRINTF(Bus, "The bus is now occupied from tick %d to %d\n", - curTick(), tickNextIdle); } -bool -Bus::isOccupied(PacketPtr pkt, Port* port) +template +void BaseBus::Layer::occupyLayer(Tick until) { - // 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; - } - return false; + // 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); + bus.schedule(releaseEvent, until); + + DPRINTF(BaseBus, "The bus is now busy from tick %d to %d\n", + curTick(), until); } +template bool -Bus::recvTiming(PacketPtr pkt) +BaseBus::Layer::tryTiming(PortClass* port) { - // get the source id - Packet::NodeID src_id = pkt->getSrc(); - - // determine the source port based on the id and direction - Port *src_port = NULL; - if (pkt->isRequest()) - src_port = slavePorts[src_id]; - else - src_port = masterPorts[src_id]; - - // test if the bus should be considered occupied for the current - // packet, and exclude express snoops from the check - if (!pkt->isExpressSnoop() && isOccupied(pkt, src_port)) { - DPRINTF(Bus, "recvTiming: src %s %s 0x%x BUSY\n", - src_port->name(), pkt->cmdString(), pkt->getAddr()); + // 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; } - DPRINTF(Bus, "recvTiming: src %s %s 0x%x\n", - src_port->name(), pkt->cmdString(), pkt->getAddr()); - - Tick headerFinishTime = pkt->isExpressSnoop() ? 0 : calcPacketTiming(pkt); - Tick packetFinishTime = pkt->isExpressSnoop() ? 0 : pkt->finishTime; - - // decide what to do based on the direction - if (pkt->isRequest()) { - // the packet is a memory-mapped request and should be - // broadcasted to our snoopers but the source - forwardTiming(pkt, src_id); - - // remember if we add an outstanding req so we can undo it if - // 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(); - - // keep track that we have an outstanding request packet - // matching this request, this is used by the coherency - // mechanism in determining what to do with snoop responses - // (in recvTimingSnoop) - if (add_outstanding) { - // we should never have an exsiting request outstanding - assert(outstandingReq.find(pkt->req) == outstandingReq.end()); - outstandingReq.insert(pkt->req); - } - - // since it is a normal request, determine the destination - // based on the address and attempt to send the packet - bool success = masterPorts[findPort(pkt->getAddr())]->sendTiming(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(Bus, "recvTiming: src %s %s 0x%x RETRY\n", - src_port->name(), pkt->cmdString(), pkt->getAddr()); - - addToRetryList(src_port); - occupyBus(headerFinishTime); - - return false; - } - } else { - // the packet is a normal response to a request that we should - // have seen passing through the bus - assert(outstandingReq.find(pkt->req) != outstandingReq.end()); - - // remove it as outstanding - outstandingReq.erase(pkt->req); - - // send the packet to the destination through one of our slave - // ports, as determined by the destination field - bool success M5_VAR_USED = slavePorts[pkt->getDest()]->sendTiming(pkt); - - // currently it is illegal to block responses... can lead to - // deadlock - assert(success); - } - - succeededTiming(packetFinishTime); + // 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; } -bool -Bus::recvTimingSnoop(PacketPtr pkt) -{ - // get the source id - Packet::NodeID src_id = pkt->getSrc(); - - if (pkt->isRequest()) { - DPRINTF(Bus, "recvTimingSnoop: src %d %s 0x%x\n", - src_id, pkt->cmdString(), pkt->getAddr()); - - // the packet is an express snoop request and should be - // broadcasted to our snoopers - assert(pkt->isExpressSnoop()); - - // forward to all snoopers - forwardTiming(pkt, Port::INVALID_PORT_ID); - - // a snoop request came from a connected slave device (one of - // our master ports), and if it is not coming from the slave - // device responsible for the address range something is - // wrong, hence there is nothing further to do as the packet - // would be going back to where it came from - assert(src_id == findPort(pkt->getAddr())); - - // this is an express snoop and is never forced to retry - assert(!inRetry); - - return true; - } else { - // determine the source port based on the id - SlavePort* src_port = slavePorts[src_id]; - - if (isOccupied(pkt, src_port)) { - DPRINTF(Bus, "recvTimingSnoop: src %s %s 0x%x BUSY\n", - src_port->name(), pkt->cmdString(), pkt->getAddr()); - return false; - } - - DPRINTF(Bus, "recvTimingSnoop: src %s %s 0x%x\n", - src_port->name(), pkt->cmdString(), pkt->getAddr()); - - // get the destination from the packet - Packet::NodeID dest = pkt->getDest(); - - // responses are never express snoops - assert(!pkt->isExpressSnoop()); - - calcPacketTiming(pkt); - Tick packetFinishTime = pkt->finishTime; - - // determine if the response is from a snoop request we - // created as the result of a normal request (in which case it - // should be in the outstandingReq), or if we merely forwarded - // someone else's snoop request - if (outstandingReq.find(pkt->req) == outstandingReq.end()) { - // this is a snoop response to a snoop request we - // forwarded, e.g. coming from the L1 and going to the L2 - // this should be forwarded as a snoop response - bool success M5_VAR_USED = masterPorts[dest]->sendTimingSnoop(pkt); - assert(success); - } else { - // we got a snoop response on one of our slave ports, - // i.e. from a coherent master connected to the bus, and - // since we created the snoop request as part of - // recvTiming, this should now be a normal response again - outstandingReq.erase(pkt->req); - - // this is a snoop response from a coherent master, with a - // destination field set on its way through the bus as - // request, hence it should never go back to where the - // snoop response came from, but instead to where the - // original request came from - assert(src_id != dest); - - // as a normal response, it should go back to a master - // through one of our slave ports - bool success M5_VAR_USED = slavePorts[dest]->sendTiming(pkt); - - // currently it is illegal to block responses... can lead - // to deadlock - assert(success); - } - - succeededTiming(packetFinishTime); - - return true; - } -} - +template void -Bus::succeededTiming(Tick busy_time) +BaseBus::Layer::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) { - DPRINTF(Bus, "Remove retry from list %s\n", + 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 + occupyLayer(busy_time); } +template void -Bus::forwardTiming(PacketPtr pkt, int exclude_slave_port_id) +BaseBus::Layer::failedTiming(PortClass* port, Tick busy_time) { - for (SlavePortIter s = snoopPorts.begin(); s != snoopPorts.end(); ++s) { - SlavePort *p = *s; - // we could have gotten this request from a snooping master - // (corresponding to our own slave port that is also in - // snoopPorts) and should not send it back to where it came - // from - if (exclude_slave_port_id == Port::INVALID_PORT_ID || - p->getId() != exclude_slave_port_id) { - // cache is not allowed to refuse snoop - bool success M5_VAR_USED = p->sendTimingSnoop(pkt); - assert(success); - } + // 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 + occupyLayer(busy_time); } +template void -Bus::releaseBus() +BaseBus::Layer::releaseLayer() { // releasing the bus means we should now be idle - assert(curTick() >= tickNextIdle); + assert(state == BUSY); + assert(!releaseEvent.scheduled()); + + // update the state + state = IDLE; // bus is now idle, so if someone is waiting we can retry if (!retryList.empty()) { @@ -424,98 +264,102 @@ Bus::releaseBus() // 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) { - drainEvent->process(); + } else if (drainManager) { + DPRINTF(Drain, "Bus done draining, signaling drain manager\n"); + //If we weren't able to drain before, do it now. + drainManager->signalDrainDone(); // Clear the drain event once we're done with it. - drainEvent = NULL; + drainManager = NULL; } } +template void -Bus::retryWaiting() +BaseBus::Layer::retryWaiting() { // 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 // destination called retry on the bus 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; + + // occupy the bus layer until the next cycle ends + occupyLayer(bus.clockEdge(Cycles(1))); } } +template void -Bus::recvRetry(Port::PortId id) +BaseBus::Layer::recvRetry() { // we got a retry from a peer that we tried to send something to // and failed, but we sent it on the account of someone else, and // that source port should be on our retry list, however if the - // bus is released before this happens and the retry (from the bus - // point of view) is successful then this no longer holds and we - // could in fact have an empty retry list + // bus layer is released before this happens and the retry (from + // the bus point of view) is successful then this no longer holds + // and we could in fact have an empty retry list if (retryList.empty()) return; - // if the bus isn't busy - if (curTick() >= tickNextIdle) { + // if the bus layer 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(); } } -int -Bus::findPort(Addr addr) +PortID +BaseBus::findPort(Addr addr) { - /* An interval tree would be a better way to do this. --ali. */ - int dest_id; + // we should never see any address lookups before we've got the + // ranges of all connected slave modules + assert(gotAllAddrRanges); - dest_id = checkPortCache(addr); - if (dest_id != Port::INVALID_PORT_ID) + // Check the cache + PortID dest_id = checkPortCache(addr); + if (dest_id != InvalidPortID) return dest_id; - // Check normal port ranges - PortIter i = portMap.find(RangeSize(addr,1)); + // Check the address map interval tree + PortMapConstIter i = portMap.find(addr); if (i != portMap.end()) { dest_id = i->second; - updatePortCache(dest_id, i->first.start, i->first.end); + updatePortCache(dest_id, i->first); return dest_id; } // Check if this matches the default range if (useDefaultRange) { - AddrRangeIter a_end = defaultRange.end(); - for (AddrRangeIter i = defaultRange.begin(); i != a_end; i++) { - if (*i == addr) { - DPRINTF(Bus, " found addr %#llx on default\n", addr); - return defaultPortId; - } + if (defaultRange.contains(addr)) { + DPRINTF(BusAddrRanges, " found addr %#llx on default\n", + addr); + return defaultPortID; } - } else if (defaultPortId != Port::INVALID_PORT_ID) { - DPRINTF(Bus, "Unable to find destination for addr %#llx, " + } else if (defaultPortID != InvalidPortID) { + DPRINTF(BusAddrRanges, "Unable to find destination for addr %#llx, " "will use default port\n", addr); - return defaultPortId; + return defaultPortID; } // we should use the range for the default port and it did not @@ -524,339 +368,213 @@ Bus::findPort(Addr addr) name()); } -Tick -Bus::recvAtomic(PacketPtr pkt) -{ - DPRINTF(Bus, "recvAtomic: packet src %s addr 0x%x cmd %s\n", - slavePorts[pkt->getSrc()]->name(), pkt->getAddr(), - pkt->cmdString()); - - // we should always see a request routed based on the address - assert(pkt->isRequest()); - - // forward to all snoopers but the source - std::pair snoop_result = forwardAtomic(pkt, pkt->getSrc()); - MemCmd snoop_response_cmd = snoop_result.first; - Tick snoop_response_latency = snoop_result.second; - - // even if we had a snoop response, we must continue and also - // perform the actual request at the destination - int dest_id = findPort(pkt->getAddr()); - - // forward the request to the appropriate destination - Tick response_latency = masterPorts[dest_id]->sendAtomic(pkt); - - // if we got a response from a snooper, restore it here - if (snoop_response_cmd != MemCmd::InvalidCmd) { - // no one else should have responded - assert(!pkt->isResponse()); - pkt->cmd = snoop_response_cmd; - response_latency = snoop_response_latency; - } - - pkt->finishTime = curTick() + response_latency; - return response_latency; -} - -Tick -Bus::recvAtomicSnoop(PacketPtr pkt) +/** Function called by the port when the bus is receiving a range change.*/ +void +BaseBus::recvRangeChange(PortID master_port_id) { - DPRINTF(Bus, "recvAtomicSnoop: packet src %s addr 0x%x cmd %s\n", - masterPorts[pkt->getSrc()]->name(), pkt->getAddr(), - pkt->cmdString()); - - // we should always see a request routed based on the address - assert(pkt->isRequest()); - - // forward to all snoopers - std::pair snoop_result = - forwardAtomic(pkt, Port::INVALID_PORT_ID); - MemCmd snoop_response_cmd = snoop_result.first; - Tick snoop_response_latency = snoop_result.second; + DPRINTF(BusAddrRanges, "Received range change from slave port %s\n", + masterPorts[master_port_id]->getSlavePort().name()); + + // remember that we got a range from this master port and thus the + // connected slave module + gotAddrRanges[master_port_id] = true; + + // update the global flag + if (!gotAllAddrRanges) { + // take a logical AND of all the ports and see if we got + // ranges from everyone + gotAllAddrRanges = true; + std::vector::const_iterator r = gotAddrRanges.begin(); + while (gotAllAddrRanges && r != gotAddrRanges.end()) { + gotAllAddrRanges &= *r++; + } + if (gotAllAddrRanges) + DPRINTF(BusAddrRanges, "Got address ranges from all slaves\n"); + } + + // note that we could get the range from the default port at any + // point in time, and we cannot assume that the default range is + // set before the other ones are, so we do additional checks once + // all ranges are provided + if (master_port_id == defaultPortID) { + // only update if we are indeed checking ranges for the + // default port since the port might not have a valid range + // otherwise + if (useDefaultRange) { + AddrRangeList ranges = masterPorts[master_port_id]->getAddrRanges(); - if (snoop_response_cmd != MemCmd::InvalidCmd) - pkt->cmd = snoop_response_cmd; + if (ranges.size() != 1) + fatal("Bus %s may only have a single default range", + name()); - pkt->finishTime = curTick() + snoop_response_latency; - return snoop_response_latency; -} - -std::pair -Bus::forwardAtomic(PacketPtr pkt, int exclude_slave_port_id) -{ - // the packet may be changed on snoops, record the original source - // and command to enable us to restore it between snoops so that - // additional snoops can take place properly - Packet::NodeID orig_src_id = pkt->getSrc(); - MemCmd orig_cmd = pkt->cmd; - MemCmd snoop_response_cmd = MemCmd::InvalidCmd; - Tick snoop_response_latency = 0; - - for (SlavePortIter s = snoopPorts.begin(); s != snoopPorts.end(); ++s) { - SlavePort *p = *s; - // we could have gotten this request from a snooping master - // (corresponding to our own slave port that is also in - // snoopPorts) and should not send it back to where it came - // from - if (exclude_slave_port_id == Port::INVALID_PORT_ID || - p->getId() != exclude_slave_port_id) { - Tick latency = p->sendAtomicSnoop(pkt); - // in contrast to a functional access, we have to keep on - // going as all snoopers must be updated even if we get a - // response - if (pkt->isResponse()) { - // response from snoop agent - assert(pkt->cmd != orig_cmd); - assert(pkt->memInhibitAsserted()); - // should only happen once - assert(snoop_response_cmd == MemCmd::InvalidCmd); - // save response state - snoop_response_cmd = pkt->cmd; - snoop_response_latency = latency; - // restore original packet state for remaining snoopers - pkt->cmd = orig_cmd; - pkt->setSrc(orig_src_id); - pkt->clearDest(); + defaultRange = ranges.front(); + } + } else { + // the ports are allowed to update their address ranges + // dynamically, so remove any existing entries + if (gotAddrRanges[master_port_id]) { + for (PortMapIter p = portMap.begin(); p != portMap.end(); ) { + if (p->second == master_port_id) + // erasing invalidates the iterator, so advance it + // before the deletion takes place + portMap.erase(p++); + else + p++; } } - } - - // the packet is restored as part of the loop and any potential - // snoop response is part of the returned pair - return std::make_pair(snoop_response_cmd, snoop_response_latency); -} - -void -Bus::recvFunctional(PacketPtr pkt) -{ - if (!pkt->isPrint()) { - // don't do DPRINTFs on PrintReq as it clutters up the output - DPRINTF(Bus, - "recvFunctional: packet src %s addr 0x%x cmd %s\n", - slavePorts[pkt->getSrc()]->name(), pkt->getAddr(), - pkt->cmdString()); - } - - // we should always see a request routed based on the address - assert(pkt->isRequest()); - // forward to all snoopers but the source - forwardFunctional(pkt, pkt->getSrc()); + AddrRangeList ranges = masterPorts[master_port_id]->getAddrRanges(); - // there is no need to continue if the snooping has found what we - // were looking for and the packet is already a response - if (!pkt->isResponse()) { - int dest_id = findPort(pkt->getAddr()); - - masterPorts[dest_id]->sendFunctional(pkt); - } -} - -void -Bus::recvFunctionalSnoop(PacketPtr pkt) -{ - if (!pkt->isPrint()) { - // don't do DPRINTFs on PrintReq as it clutters up the output - DPRINTF(Bus, - "recvFunctionalSnoop: packet src %s addr 0x%x cmd %s\n", - masterPorts[pkt->getSrc()]->name(), pkt->getAddr(), - pkt->cmdString()); - } - - // we should always see a request routed based on the address - assert(pkt->isRequest()); - - // forward to all snoopers - forwardFunctional(pkt, Port::INVALID_PORT_ID); -} - -void -Bus::forwardFunctional(PacketPtr pkt, int exclude_slave_port_id) -{ - for (SlavePortIter s = snoopPorts.begin(); s != snoopPorts.end(); ++s) { - SlavePort *p = *s; - // we could have gotten this request from a snooping master - // (corresponding to our own slave port that is also in - // snoopPorts) and should not send it back to where it came - // from - if (exclude_slave_port_id == Port::INVALID_PORT_ID || - p->getId() != exclude_slave_port_id) - p->sendFunctionalSnoop(pkt); - - // if we get a response we are done - if (pkt->isResponse()) { - break; + for (AddrRangeConstIter r = ranges.begin(); r != ranges.end(); ++r) { + DPRINTF(BusAddrRanges, "Adding range %s for id %d\n", + r->to_string(), master_port_id); + if (portMap.insert(*r, master_port_id) == portMap.end()) { + PortID conflict_id = portMap.find(*r)->second; + fatal("%s has two ports with same range:\n\t%s\n\t%s\n", + name(), + masterPorts[master_port_id]->getSlavePort().name(), + masterPorts[conflict_id]->getSlavePort().name()); + } } } -} -/** Function called by the port when the bus is receiving a range change.*/ -void -Bus::recvRangeChange(Port::PortId id) -{ - AddrRangeList ranges; - AddrRangeIter iter; + // if we have received ranges from all our neighbouring slave + // modules, go ahead and tell our connected master modules in + // turn, this effectively assumes a tree structure of the system + if (gotAllAddrRanges) { + DPRINTF(BusAddrRanges, "Aggregating bus ranges\n"); + busRanges.clear(); - if (inRecvRangeChange.count(id)) - return; - inRecvRangeChange.insert(id); + // start out with the default range + if (useDefaultRange) { + if (!gotAddrRanges[defaultPortID]) + fatal("Bus %s uses default range, but none provided", + name()); - DPRINTF(BusAddrRanges, "received RangeChange from device id %d\n", id); + busRanges.push_back(defaultRange); + DPRINTF(BusAddrRanges, "-- Adding default %s\n", + defaultRange.to_string()); + } - clearPortCache(); - if (id == defaultPortId) { - defaultRange.clear(); - // Only try to update these ranges if the user set a default responder. - if (useDefaultRange) { - AddrRangeList ranges = - masterPorts[id]->getSlavePort().getAddrRanges(); - for(iter = ranges.begin(); iter != ranges.end(); iter++) { - defaultRange.push_back(*iter); - DPRINTF(BusAddrRanges, "Adding range %#llx - %#llx for default range\n", - iter->start, iter->end); + // merge all interleaved ranges and add any range that is not + // a subset of the default range + std::vector intlv_ranges; + for (AddrRangeMap::const_iterator r = portMap.begin(); + r != portMap.end(); ++r) { + // if the range is interleaved then save it for now + if (r->first.interleaved()) { + // if we already got interleaved ranges that are not + // part of the same range, then first do a merge + // before we add the new one + if (!intlv_ranges.empty() && + !intlv_ranges.back().mergesWith(r->first)) { + DPRINTF(BusAddrRanges, "-- Merging range from %d ranges\n", + intlv_ranges.size()); + AddrRange merged_range(intlv_ranges); + // next decide if we keep the merged range or not + if (!(useDefaultRange && + merged_range.isSubset(defaultRange))) { + busRanges.push_back(merged_range); + DPRINTF(BusAddrRanges, "-- Adding merged range %s\n", + merged_range.to_string()); + } + intlv_ranges.clear(); + } + intlv_ranges.push_back(r->first); + } else { + // keep the current range if not a subset of the default + if (!(useDefaultRange && + r->first.isSubset(defaultRange))) { + busRanges.push_back(r->first); + DPRINTF(BusAddrRanges, "-- Adding range %s\n", + r->first.to_string()); + } } } - } else { - - assert(id < masterPorts.size() && id >= 0); - MasterPort *port = masterPorts[id]; - // Clean out any previously existent ids - for (PortIter portIter = portMap.begin(); - portIter != portMap.end(); ) { - if (portIter->second == id) - portMap.erase(portIter++); - else - portIter++; + // if there is still interleaved ranges waiting to be merged, + // go ahead and do it + if (!intlv_ranges.empty()) { + DPRINTF(BusAddrRanges, "-- Merging range from %d ranges\n", + intlv_ranges.size()); + AddrRange merged_range(intlv_ranges); + if (!(useDefaultRange && merged_range.isSubset(defaultRange))) { + busRanges.push_back(merged_range); + DPRINTF(BusAddrRanges, "-- Adding merged range %s\n", + merged_range.to_string()); + } } - ranges = port->getSlavePort().getAddrRanges(); - - for (iter = ranges.begin(); iter != ranges.end(); iter++) { - DPRINTF(BusAddrRanges, "Adding range %#llx - %#llx for id %d\n", - iter->start, iter->end, id); - if (portMap.insert(*iter, id) == portMap.end()) { - int conflict_id = portMap.find(*iter)->second; - fatal("%s has two ports with same range:\n\t%s\n\t%s\n", - name(), masterPorts[id]->getSlavePort().name(), - masterPorts[conflict_id]->getSlavePort().name()); + // also check that no range partially overlaps with the + // default range, this has to be done after all ranges are set + // as there are no guarantees for when the default range is + // update with respect to the other ones + if (useDefaultRange) { + for (AddrRangeConstIter r = busRanges.begin(); + r != busRanges.end(); ++r) { + // see if the new range is partially + // overlapping the default range + if (r->intersects(defaultRange) && + !r->isSubset(defaultRange)) + fatal("Range %s intersects the " \ + "default range of %s but is not a " \ + "subset\n", r->to_string(), name()); } } - } - DPRINTF(BusAddrRanges, "port list has %d entries\n", portMap.size()); - // tell all our neighbouring master ports that our address range - // has changed - for (SlavePortConstIter p = slavePorts.begin(); p != slavePorts.end(); - ++p) - (*p)->sendRangeChange(); + // tell all our neighbouring master ports that our address + // ranges have changed + for (SlavePortConstIter s = slavePorts.begin(); s != slavePorts.end(); + ++s) + (*s)->sendRangeChange(); + } - inRecvRangeChange.erase(id); + clearPortCache(); } AddrRangeList -Bus::getAddrRanges(Port::PortId id) +BaseBus::getAddrRanges() const { - AddrRangeList ranges; + // we should never be asked without first having sent a range + // change, and the latter is only done once we have all the ranges + // of the connected devices + assert(gotAllAddrRanges); - DPRINTF(BusAddrRanges, "received address range request, returning:\n"); + // at the moment, this never happens, as there are no cycles in + // the range queries and no devices on the master side of a bus + // (CPU, cache, bridge etc) actually care about the ranges of the + // ports they are connected to - for (AddrRangeIter dflt_iter = defaultRange.begin(); - dflt_iter != defaultRange.end(); dflt_iter++) { - ranges.push_back(*dflt_iter); - DPRINTF(BusAddrRanges, " -- Dflt: %#llx : %#llx\n",dflt_iter->start, - dflt_iter->end); - } - for (PortIter portIter = portMap.begin(); - portIter != portMap.end(); portIter++) { - bool subset = false; - for (AddrRangeIter dflt_iter = defaultRange.begin(); - dflt_iter != defaultRange.end(); dflt_iter++) { - if ((portIter->first.start < dflt_iter->start && - portIter->first.end >= dflt_iter->start) || - (portIter->first.start < dflt_iter->end && - portIter->first.end >= dflt_iter->end)) - fatal("Devices can not set ranges that itersect the default set\ - but are not a subset of the default set.\n"); - if (portIter->first.start >= dflt_iter->start && - portIter->first.end <= dflt_iter->end) { - subset = true; - DPRINTF(BusAddrRanges, " -- %#llx : %#llx is a SUBSET\n", - portIter->first.start, portIter->first.end); - } - } - if (portIter->second != id && !subset) { - ranges.push_back(portIter->first); - DPRINTF(BusAddrRanges, " -- %#llx : %#llx\n", - portIter->first.start, portIter->first.end); - } - } + DPRINTF(BusAddrRanges, "Received address range request\n"); - return ranges; -} - -bool -Bus::isSnooping(Port::PortId id) const -{ - // in essence, answer the question if there are snooping ports - return !snoopPorts.empty(); + return busRanges; } unsigned -Bus::findBlockSize(Port::PortId id) +BaseBus::deviceBlockSize() const { - if (cachedBlockSizeValid) - return cachedBlockSize; - - unsigned max_bs = 0; - - PortIter p_end = portMap.end(); - for (PortIter p_iter = portMap.begin(); p_iter != p_end; p_iter++) { - unsigned tmp_bs = masterPorts[p_iter->second]->peerBlockSize(); - if (tmp_bs > max_bs) - max_bs = tmp_bs; - } - - for (SlavePortConstIter s = snoopPorts.begin(); s != snoopPorts.end(); - ++s) { - unsigned tmp_bs = (*s)->peerBlockSize(); - if (tmp_bs > max_bs) - max_bs = tmp_bs; - } - if (max_bs == 0) - max_bs = defaultBlockSize; - - if (max_bs != 64) - warn_once("Blocksize found to not be 64... hmm... probably not.\n"); - cachedBlockSize = max_bs; - cachedBlockSizeValid = true; - return max_bs; + return blockSize; } - +template unsigned int -Bus::drain(Event * de) +BaseBus::Layer::drain(DrainManager *dm) { //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())) { - drainEvent = de; + if (!retryList.empty() || state != IDLE) { + DPRINTF(Drain, "Bus not drained\n"); + drainManager = dm; return 1; } return 0; } -void -Bus::startup() -{ - if (tickNextIdle < curTick()) - tickNextIdle = (curTick() / clock) * clock + clock; -} - -Bus * -BusParams::create() -{ - return new Bus(this); -} +/** + * Bus layer template instantiations. Could be removed with _impl.hh + * file, but since there are only two given options (MasterPort and + * SlavePort) it seems a bit excessive at this point. + */ +template class BaseBus::Layer; +template class BaseBus::Layer;