/*
- * Copyright (c) 2011-2015 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),
- frontendLatency(p->frontend_latency),
- forwardLatency(p->forward_latency),
- responseLatency(p->response_latency),
- 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)
-{}
-
-BaseXBar::~BaseXBar()
+ useDefaultRange(p.use_default_range),
+
+ ADD_STAT(transDist, UNIT_COUNT, "Transaction distribution"),
+ ADD_STAT(pktCount, UNIT_COUNT,
+ "Packet count per connected requestor and responder"),
+ ADD_STAT(pktSize, UNIT_BYTE,
+ "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);
}
}
}
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, UNIT_TICK, "Layer occupancy (ticks)"),
+ ADD_STAT(utilization, UNIT_RATIO, "Layer utilization")
{
+ occupancy
+ .flags(Stats::nozero);
+
+ utilization
+ .precision(1)
+ .flags(Stats::nozero);
+
+ utilization = 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());
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 responding within range %s:\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(),
r.to_string(),
- masterPorts[master_port_id]->getSlavePort().name(),
- masterPorts[conflict_id]->getSlavePort().name());
+ 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>;