/*
- * Copyright (c) 2011-2014 ARM Limited
+ * Copyright (c) 2011-2015, 2018-2020 ARM Limited
* All rights reserved
*
* The license below extends only to copyright in the software and shall
* 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
- * William Wang
*/
/**
* Definition of a crossbar object.
*/
-#include "base/misc.hh"
+#include "mem/xbar.hh"
+
+#include "base/logging.hh"
#include "base/trace.hh"
#include "debug/AddrRanges.hh"
#include "debug/Drain.hh"
#include "debug/XBar.hh"
-#include "mem/xbar.hh"
-BaseXBar::BaseXBar(const BaseXBarParams *p)
- : MemObject(p),
- headerCycles(p->header_cycles), width(p->width),
- gotAddrRanges(p->port_default_connection_count +
- p->port_master_connection_count, false),
+BaseXBar::BaseXBar(const BaseXBarParams &p)
+ : ClockedObject(p),
+ frontendLatency(p.frontend_latency),
+ forwardLatency(p.forward_latency),
+ responseLatency(p.response_latency),
+ headerLatency(p.header_latency),
+ width(p.width),
+ gotAddrRanges(p.port_default_connection_count +
+ p.port_mem_side_ports_connection_count, false),
gotAllAddrRanges(false), defaultPortID(InvalidPortID),
- useDefaultRange(p->use_default_range)
-{}
+ useDefaultRange(p.use_default_range),
-BaseXBar::~BaseXBar()
+ transDist(this, "trans_dist", "Transaction distribution"),
+ pktCount(this, "pkt_count",
+ "Packet count per connected requestor and responder (bytes)"),
+ pktSize(this, "pkt_size", "Cumulative packet size per connected "
+ "requestor and responder (bytes)")
{
- for (auto m: masterPorts)
- delete m;
-
- for (auto s: slavePorts)
- delete s;
}
-void
-BaseXBar::init()
+BaseXBar::~BaseXBar()
{
-}
+ for (auto port: memSidePorts)
+ delete port;
-BaseMasterPort &
-BaseXBar::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];
- } else {
- return MemObject::getMasterPort(if_name, idx);
- }
+ for (auto port: cpuSidePorts)
+ delete port;
}
-BaseSlavePort &
-BaseXBar::getSlavePort(const std::string &if_name, PortID idx)
+Port &
+BaseXBar::getPort(const std::string &if_name, PortID idx)
{
- if (if_name == "slave" && idx < slavePorts.size()) {
- // the slave port index translates directly to the vector position
- return *slavePorts[idx];
+ if (if_name == "mem_side_ports" && idx < memSidePorts.size()) {
+ // the memory-side ports index translates directly to the vector
+ // position
+ return *memSidePorts[idx];
+ } else if (if_name == "default") {
+ return *memSidePorts[defaultPortID];
+ } else if (if_name == "cpu_side_ports" && idx < cpuSidePorts.size()) {
+ // the CPU-side ports index translates directly to the vector position
+ return *cpuSidePorts[idx];
} else {
- return MemObject::getSlavePort(if_name, idx);
+ return ClockedObject::getPort(if_name, idx);
}
}
void
-BaseXBar::calcPacketTiming(PacketPtr pkt)
+BaseXBar::calcPacketTiming(PacketPtr pkt, Tick header_delay)
{
// the crossbar 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 = clockEdge() - 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 crossbar has been accounted for
- if (pkt->firstWordDelay != 0 || pkt->lastWordDelay != 0)
- panic("Packet %s already has delay (%d, %d) that should be "
- "accounted for.\n", pkt->cmdString(), pkt->firstWordDelay,
- pkt->lastWordDelay);
-
- // The first word will be delivered on the cycle after the header.
- pkt->firstWordDelay = (headerCycles + 1) * clockPeriod() + offset;
+ // the header delay depends on the path through the crossbar, and
+ // we therefore rely on the caller to provide the actual
+ // value
+ pkt->headerDelay += offset + header_delay;
+
+ // note that we add the header delay to the existing value, and
+ // align it to the crossbar clock
+
+ // do a quick sanity check to ensure the timings are not being
+ // ignored, note that this specific value may cause problems for
+ // slower interconnects
+ panic_if(pkt->headerDelay > SimClock::Int::us,
+ "Encountered header delay exceeding 1 us\n");
+
+ if (pkt->hasData()) {
+ // the payloadDelay takes into account the relative time to
+ // deliver the payload of the packet, after the header delay,
+ // we take the maximum since the payload delay could already
+ // be longer than what this parcitular crossbar enforces.
+ pkt->payloadDelay = std::max<Tick>(pkt->payloadDelay,
+ divCeil(pkt->getSize(), width) *
+ clockPeriod());
+ }
- // Note that currently lastWordDelay can be smaller than
- // firstWordDelay if the packet has no data
- pkt->lastWordDelay = (headerCycles + dataCycles) * clockPeriod() +
- offset;
+ // the payload delay is not paying for the clock offset as that is
+ // already done using the header delay, and the payload delay is
+ // also used to determine how long the crossbar layer is busy and
+ // thus regulates throughput
}
template <typename SrcType, typename DstType>
-BaseXBar::Layer<SrcType,DstType>::Layer(DstType& _port, BaseXBar& _xbar,
+BaseXBar::Layer<SrcType, DstType>::Layer(DstType& _port, BaseXBar& _xbar,
const std::string& _name) :
- port(_port), xbar(_xbar), _name(_name), state(IDLE), drainManager(NULL),
- waitingForPeer(NULL), releaseEvent(this)
+ Stats::Group(&_xbar, _name.c_str()),
+ port(_port), xbar(_xbar), _name(xbar.name() + "." + _name), state(IDLE),
+ waitingForPeer(NULL), releaseEvent([this]{ releaseLayer(); }, name()),
+ ADD_STAT(occupancy, "Layer occupancy (ticks)"),
+ ADD_STAT(utilization, "Layer utilization (%)")
{
+ occupancy
+ .flags(Stats::nozero);
+
+ utilization
+ .precision(1)
+ .flags(Stats::nozero);
+
+ utilization = 100 * occupancy / simTicks;
}
template <typename SrcType, typename DstType>
-void BaseXBar::Layer<SrcType,DstType>::occupyLayer(Tick until)
+void BaseXBar::Layer<SrcType, DstType>::occupyLayer(Tick until)
{
// ensure the state is busy at this point, as the layer should
// transition from idle as soon as it has decided to forward the
template <typename SrcType, typename DstType>
bool
-BaseXBar::Layer<SrcType,DstType>::tryTiming(SrcType* src_port)
+BaseXBar::Layer<SrcType, DstType>::tryTiming(SrcType* src_port)
{
// if we are in the retry state, we will not see anything but the
// retrying port (or in the case of the snoop ports the snoop
- // response port that mirrors the actual slave port) as we leave
+ // response port that mirrors the actual CPU-side port) as we leave
// this state again in zero time if the peer does not immediately
// call the layer when receiving the retry
template <typename SrcType, typename DstType>
void
-BaseXBar::Layer<SrcType,DstType>::succeededTiming(Tick busy_time)
+BaseXBar::Layer<SrcType, DstType>::succeededTiming(Tick busy_time)
{
// we should have gone from idle or retry to busy in the tryTiming
// test
template <typename SrcType, typename DstType>
void
-BaseXBar::Layer<SrcType,DstType>::failedTiming(SrcType* src_port,
+BaseXBar::Layer<SrcType, DstType>::failedTiming(SrcType* src_port,
Tick busy_time)
{
// ensure no one got in between and tried to send something to
template <typename SrcType, typename DstType>
void
-BaseXBar::Layer<SrcType,DstType>::releaseLayer()
+BaseXBar::Layer<SrcType, DstType>::releaseLayer()
{
// releasing the bus means we should now be idle
assert(state == BUSY);
// waiting for the peer
if (waitingForPeer == NULL)
retryWaiting();
- } else if (waitingForPeer == NULL && drainManager) {
+ } else if (waitingForPeer == NULL && drainState() == DrainState::Draining) {
DPRINTF(Drain, "Crossbar 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.
- drainManager = NULL;
+ signalDrainDone();
}
}
template <typename SrcType, typename DstType>
void
-BaseXBar::Layer<SrcType,DstType>::retryWaiting()
+BaseXBar::Layer<SrcType, DstType>::retryWaiting()
{
// this should never be called with no one waiting
assert(!waitingForLayer.empty());
// tell the port to retry, which in some cases ends up calling the
// layer again
- retryingPort->sendRetry();
+ sendRetry(retryingPort);
// If the layer is still in the retry state, sendTiming wasn't
- // called in zero time (e.g. the cache does this), burn a cycle
+ // called in zero time (e.g. the cache does this when a writeback
+ // is squashed)
if (state == RETRY) {
// update the state to busy and reset the retrying port, we
// have done our bit and sent the retry
state = BUSY;
- // occupy the crossbar layer until the next cycle ends
- occupyLayer(xbar.clockEdge(Cycles(1)));
+ // occupy the crossbar layer until the next clock edge
+ occupyLayer(xbar.clockEdge());
}
}
template <typename SrcType, typename DstType>
void
-BaseXBar::Layer<SrcType,DstType>::recvRetry()
+BaseXBar::Layer<SrcType, DstType>::recvRetry()
{
// we should never get a retry without having failed to forward
// something to this port
}
PortID
-BaseXBar::findPort(Addr addr)
+BaseXBar::findPort(AddrRange addr_range)
{
// we should never see any address lookups before we've got the
- // ranges of all connected slave modules
+ // ranges of all connected CPU-side-port modules
assert(gotAllAddrRanges);
- // Check the cache
- PortID dest_id = checkPortCache(addr);
- if (dest_id != InvalidPortID)
- return dest_id;
-
// Check the address map interval tree
- auto i = portMap.find(addr);
+ auto i = portMap.contains(addr_range);
if (i != portMap.end()) {
- dest_id = i->second;
- updatePortCache(dest_id, i->first);
- return dest_id;
+ return i->second;
}
// Check if this matches the default range
if (useDefaultRange) {
- if (defaultRange.contains(addr)) {
- DPRINTF(AddrRanges, " found addr %#llx on default\n",
- addr);
+ if (addr_range.isSubset(defaultRange)) {
+ DPRINTF(AddrRanges, " found addr %s on default\n",
+ addr_range.to_string());
return defaultPortID;
}
} else if (defaultPortID != InvalidPortID) {
- DPRINTF(AddrRanges, "Unable to find destination for addr %#llx, "
- "will use default port\n", addr);
+ DPRINTF(AddrRanges, "Unable to find destination for %s, "
+ "will use default port\n", addr_range.to_string());
return defaultPortID;
}
// we should use the range for the default port and it did not
// match, or the default port is not set
- fatal("Unable to find destination for addr %#llx on %s\n", addr,
+ fatal("Unable to find destination for %s on %s\n", addr_range.to_string(),
name());
}
/** Function called by the port when the crossbar is receiving a range change.*/
void
-BaseXBar::recvRangeChange(PortID master_port_id)
+BaseXBar::recvRangeChange(PortID mem_side_port_id)
{
- DPRINTF(AddrRanges, "Received range change from slave port %s\n",
- masterPorts[master_port_id]->getSlavePort().name());
+ DPRINTF(AddrRanges, "Received range change from cpu_side_ports %s\n",
+ memSidePorts[mem_side_port_id]->getPeer());
- // remember that we got a range from this master port and thus the
- // connected slave module
- gotAddrRanges[master_port_id] = true;
+ // remember that we got a range from this memory-side port and thus the
+ // connected CPU-side-port module
+ gotAddrRanges[mem_side_port_id] = true;
// update the global flag
if (!gotAllAddrRanges) {
gotAllAddrRanges &= *r++;
}
if (gotAllAddrRanges)
- DPRINTF(AddrRanges, "Got address ranges from all slaves\n");
+ DPRINTF(AddrRanges, "Got address ranges from all responders\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) {
+ if (mem_side_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();
+ AddrRangeList ranges = memSidePorts[mem_side_port_id]->
+ getAddrRanges();
if (ranges.size() != 1)
fatal("Crossbar %s may only have a single default range",
} else {
// the ports are allowed to update their address ranges
// dynamically, so remove any existing entries
- if (gotAddrRanges[master_port_id]) {
+ if (gotAddrRanges[mem_side_port_id]) {
for (auto p = portMap.begin(); p != portMap.end(); ) {
- if (p->second == master_port_id)
+ if (p->second == mem_side_port_id)
// erasing invalidates the iterator, so advance it
// before the deletion takes place
portMap.erase(p++);
}
}
- AddrRangeList ranges = masterPorts[master_port_id]->getAddrRanges();
+ AddrRangeList ranges = memSidePorts[mem_side_port_id]->
+ getAddrRanges();
for (const auto& r: ranges) {
DPRINTF(AddrRanges, "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",
+ r.to_string(), mem_side_port_id);
+ if (portMap.insert(r, mem_side_port_id) == portMap.end()) {
+ PortID conflict_id = portMap.intersects(r)->second;
+ fatal("%s has two ports responding within range "
+ "%s:\n\t%s\n\t%s\n",
name(),
- masterPorts[master_port_id]->getSlavePort().name(),
- masterPorts[conflict_id]->getSlavePort().name());
+ r.to_string(),
+ memSidePorts[mem_side_port_id]->getPeer(),
+ memSidePorts[conflict_id]->getPeer());
}
}
}
- // if we have received ranges from all our neighbouring slave
- // modules, go ahead and tell our connected master modules in
+ // if we have received ranges from all our neighbouring CPU-side-port
+ // modules, go ahead and tell our connected memory-side-port modules in
// turn, this effectively assumes a tree structure of the system
if (gotAllAddrRanges) {
DPRINTF(AddrRanges, "Aggregating address ranges\n");
}
}
- // also check that no range partially overlaps with the
+ // also check that no range partially intersects 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
}
}
- // tell all our neighbouring master ports that our address
+ // tell all our neighbouring memory-side ports that our address
// ranges have changed
- for (const auto& s: slavePorts)
- s->sendRangeChange();
+ for (const auto& port: cpuSidePorts)
+ port->sendRangeChange();
}
-
- clearPortCache();
}
AddrRangeList
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 crossbar
+ // the range queries and no devices on the memory side of a crossbar
// (CPU, cache, bridge etc) actually care about the ranges of the
// ports they are connected to
void
BaseXBar::regStats()
{
+ ClockedObject::regStats();
+
using namespace Stats;
transDist
.init(MemCmd::NUM_MEM_CMDS)
- .name(name() + ".trans_dist")
- .desc("Transaction distribution")
.flags(nozero);
// get the string representation of the commands
}
pktCount
- .init(slavePorts.size(), masterPorts.size())
- .name(name() + ".pkt_count")
- .desc("Packet count per connected master and slave (bytes)")
+ .init(cpuSidePorts.size(), memSidePorts.size())
.flags(total | nozero | nonan);
pktSize
- .init(slavePorts.size(), masterPorts.size())
- .name(name() + ".pkt_size")
- .desc("Cumulative packet size per connected master and slave (bytes)")
+ .init(cpuSidePorts.size(), memSidePorts.size())
.flags(total | nozero | nonan);
// both the packet count and total size are two-dimensional
- // vectors, indexed by slave port id and master port id, thus the
- // neighbouring master and slave, they do not differentiate what
- // came from the master and was forwarded to the slave (requests
- // and snoop responses) and what came from the slave and was
- // forwarded to the master (responses and snoop requests)
- for (int i = 0; i < slavePorts.size(); i++) {
- pktCount.subname(i, slavePorts[i]->getMasterPort().name());
- pktSize.subname(i, slavePorts[i]->getMasterPort().name());
- for (int j = 0; j < masterPorts.size(); j++) {
- pktCount.ysubname(j, masterPorts[j]->getSlavePort().name());
- pktSize.ysubname(j, masterPorts[j]->getSlavePort().name());
+ // vectors, indexed by CPU-side port id and memory-side port id, thus the
+ // neighbouring memory-side ports and CPU-side ports, they do not
+ // differentiate what came from the memory-side ports and was forwarded to
+ // the CPU-side ports (requests and snoop responses) and what came from
+ // the CPU-side ports and was forwarded to the memory-side ports (responses
+ // and snoop requests)
+ for (int i = 0; i < cpuSidePorts.size(); i++) {
+ pktCount.subname(i, cpuSidePorts[i]->getPeer().name());
+ pktSize.subname(i, cpuSidePorts[i]->getPeer().name());
+ for (int j = 0; j < memSidePorts.size(); j++) {
+ pktCount.ysubname(j, memSidePorts[j]->getPeer().name());
+ pktSize.ysubname(j, memSidePorts[j]->getPeer().name());
}
}
}
template <typename SrcType, typename DstType>
-unsigned int
-BaseXBar::Layer<SrcType,DstType>::drain(DrainManager *dm)
+DrainState
+BaseXBar::Layer<SrcType, DstType>::drain()
{
//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 (state != IDLE) {
DPRINTF(Drain, "Crossbar not drained\n");
- drainManager = dm;
- return 1;
+ return DrainState::Draining;
+ } else {
+ return DrainState::Drained;
}
- return 0;
-}
-
-template <typename SrcType, typename DstType>
-void
-BaseXBar::Layer<SrcType,DstType>::regStats()
-{
- using namespace Stats;
-
- occupancy
- .name(name() + ".occupancy")
- .desc("Layer occupancy (ticks)")
- .flags(nozero);
-
- utilization
- .name(name() + ".utilization")
- .desc("Layer utilization (%)")
- .precision(1)
- .flags(nozero);
-
- utilization = 100 * occupancy / simTicks;
}
/**
* Crossbar 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.
+ * file, but since there are only two given options (RequestPort and
+ * ResponsePort) it seems a bit excessive at this point.
*/
-template class BaseXBar::Layer<SlavePort,MasterPort>;
-template class BaseXBar::Layer<MasterPort,SlavePort>;
+template class BaseXBar::Layer<ResponsePort, RequestPort>;
+template class BaseXBar::Layer<RequestPort, ResponsePort>;