X-Git-Url: https://git.libre-soc.org/?a=blobdiff_plain;f=src%2Fmem%2Fbus.cc;h=d29422593214fd3971b8a5c6410988c1afa0a459;hb=8573a69d8f7bf7b3f074e3e0ac64994801c551be;hp=d4157e14d1c361468dea90fc85ae1e190a85d6af;hpb=43ca8415e8747145cb1a410d4672d4cd2247c695;p=gem5.git diff --git a/src/mem/bus.cc b/src/mem/bus.cc index d4157e14d..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 @@ -57,7 +57,9 @@ BaseBus::BaseBus(const BaseBusParams *p) : MemObject(p), headerCycles(p->header_cycles), width(p->width), - defaultPortID(InvalidPortID), + gotAddrRanges(p->port_default_connection_count + + p->port_master_connection_count, false), + gotAllAddrRanges(false), defaultPortID(InvalidPortID), useDefaultRange(p->use_default_range), blockSize(p->block_size) {} @@ -98,13 +100,13 @@ BaseBus::init() blockSize = peer_block_size; // check if the block size is a value known to work - if (blockSize != 16 || blockSize != 32 || blockSize != 64 || - blockSize != 128) + if (!(blockSize == 16 || blockSize == 32 || blockSize == 64 || + blockSize == 128)) warn_once("Block size is neither 16, 32, 64 or 128 bytes.\n"); } -MasterPort & -BaseBus::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 @@ -116,8 +118,8 @@ BaseBus::getMasterPort(const std::string &if_name, int idx) } } -SlavePort & -BaseBus::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 @@ -127,39 +129,37 @@ BaseBus::getSlavePort(const std::string &if_name, int idx) } } -Tick +void BaseBus::calcPacketTiming(PacketPtr pkt) { - // determine the current time rounded to the closest following - // clock edge - Tick now = nextCycle(); - - 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; } template -BaseBus::Layer::Layer(BaseBus& _bus, const std::string& _name, - Tick _clock) : - bus(_bus), _name(_name), state(IDLE), clock(_clock), drainEvent(NULL), +BaseBus::Layer::Layer(BaseBus& _bus, const std::string& _name) : + Drainable(), + bus(_bus), _name(_name), state(IDLE), drainManager(NULL), releaseEvent(this) { } @@ -264,12 +264,12 @@ BaseBus::Layer::releaseLayer() // busy, and in the latter case the bus may be released before // we see a retry from the destination retryWaiting(); - } else if (drainEvent) { - DPRINTF(Drain, "Bus done draining, processing drain event\n"); + } else if (drainManager) { + DPRINTF(Drain, "Bus done draining, signaling drain manager\n"); //If we weren't able to drain before, do it now. - drainEvent->process(); + drainManager->signalDrainDone(); // Clear the drain event once we're done with it. - drainEvent = NULL; + drainManager = NULL; } } @@ -303,11 +303,8 @@ BaseBus::Layer::retryWaiting() // snoop responses state = BUSY; - // determine the current time rounded to the closest following - // clock edge - Tick now = bus.nextCycle(); - - occupyLayer(now + clock); + // occupy the bus layer until the next cycle ends + occupyLayer(bus.clockEdge(Cycles(1))); } } @@ -335,28 +332,29 @@ BaseBus::Layer::recvRetry() PortID BaseBus::findPort(Addr addr) { - /* An interval tree would be a better way to do this. --ali. */ + // we should never see any address lookups before we've got the + // ranges of all connected slave modules + assert(gotAllAddrRanges); + + // Check the cache PortID dest_id = checkPortCache(addr); if (dest_id != InvalidPortID) return dest_id; - // Check normal port ranges - PortMapConstIter 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) { - AddrRangeConstIter a_end = defaultRange.end(); - for (AddrRangeConstIter i = defaultRange.begin(); i != a_end; i++) { - if (*i == addr) { - DPRINTF(BusAddrRanges, " 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 != InvalidPortID) { DPRINTF(BusAddrRanges, "Unable to find destination for addr %#llx, " @@ -374,51 +372,64 @@ BaseBus::findPort(Addr addr) void BaseBus::recvRangeChange(PortID master_port_id) { - AddrRangeIter iter; - - if (inRecvRangeChange.count(master_port_id)) - return; - inRecvRangeChange.insert(master_port_id); - - DPRINTF(BusAddrRanges, "received RangeChange from device id %d\n", - master_port_id); + 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"); + } - clearPortCache(); + // 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) { - defaultRange.clear(); - // Only try to update these ranges if the user set a default responder. + // only update if we are indeed checking ranges for the + // default port since the port might not have a valid range + // otherwise if (useDefaultRange) { - // get the address ranges of the connected slave port - AddrRangeList ranges = - masterPorts[master_port_id]->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); - } - } - } else { + AddrRangeList ranges = masterPorts[master_port_id]->getAddrRanges(); - assert(master_port_id < masterPorts.size() && master_port_id >= 0); - MasterPort *port = masterPorts[master_port_id]; + if (ranges.size() != 1) + fatal("Bus %s may only have a single default range", + name()); - // Clean out any previously existent ids - for (PortMapIter portIter = portMap.begin(); - portIter != portMap.end(); ) { - if (portIter->second == master_port_id) - portMap.erase(portIter++); - else - portIter++; + 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++; + } } - // get the address ranges of the connected slave port - AddrRangeList ranges = port->getAddrRanges(); + AddrRangeList ranges = masterPorts[master_port_id]->getAddrRanges(); - for (iter = ranges.begin(); iter != ranges.end(); iter++) { - DPRINTF(BusAddrRanges, "Adding range %#llx - %#llx for id %d\n", - iter->start, iter->end, master_port_id); - if (portMap.insert(*iter, master_port_id) == portMap.end()) { - PortID conflict_id = portMap.find(*iter)->second; + 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(), @@ -426,56 +437,117 @@ BaseBus::recvRangeChange(PortID master_port_id) } } } - 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(); + // 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(); - inRecvRangeChange.erase(master_port_id); -} + // start out with the default range + if (useDefaultRange) { + if (!gotAddrRanges[defaultPortID]) + fatal("Bus %s uses default range, but none provided", + name()); -AddrRangeList -BaseBus::getAddrRanges() const -{ - AddrRangeList ranges; + busRanges.push_back(defaultRange); + DPRINTF(BusAddrRanges, "-- Adding default %s\n", + defaultRange.to_string()); + } - DPRINTF(BusAddrRanges, "received address range request, returning:\n"); + // 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()); + } + } + } - for (AddrRangeConstIter 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 (PortMapConstIter portIter = portMap.begin(); - portIter != portMap.end(); portIter++) { - bool subset = false; - for (AddrRangeConstIter 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 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()); } } - if (!subset) { - ranges.push_back(portIter->first); - DPRINTF(BusAddrRanges, " -- %#llx : %#llx\n", - portIter->first.start, portIter->first.end); + + // 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()); + } } + + // tell all our neighbouring master ports that our address + // ranges have changed + for (SlavePortConstIter s = slavePorts.begin(); s != slavePorts.end(); + ++s) + (*s)->sendRangeChange(); } - return ranges; + clearPortCache(); +} + +AddrRangeList +BaseBus::getAddrRanges() const +{ + // 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); + + // 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 + + DPRINTF(BusAddrRanges, "Received address range request\n"); + + return busRanges; } unsigned @@ -486,14 +558,14 @@ BaseBus::deviceBlockSize() const template unsigned int -BaseBus::Layer::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() || state != IDLE) { DPRINTF(Drain, "Bus not drained\n"); - drainEvent = de; + drainManager = dm; return 1; } return 0;