size=options.l2_size,
assoc=options.l2_assoc)
- system.tol2bus = CoherentBus(clk_domain = system.cpu_clk_domain,
- width = 32)
+ system.tol2bus = CoherentXBar(clk_domain = system.cpu_clk_domain,
+ width = 32)
system.l2.cpu_side = system.tol2bus.master
system.l2.mem_side = system.membus.slave
def childImage(self, ci):
self.image.child.image_file = ci
-class MemBus(CoherentBus):
+class MemBus(CoherentXBar):
badaddr_responder = BadAddr()
default = Self.badaddr_responder.pio
self.tsunami = BaseTsunami()
# Create the io bus to connect all device ports
- self.iobus = NoncoherentBus()
+ self.iobus = NoncoherentXBar()
self.tsunami.attachIO(self.iobus)
self.tsunami.ide.pio = self.iobus.master
# generic system
mdesc = SysConfig()
self.readfile = mdesc.script()
- self.iobus = NoncoherentBus()
+ self.iobus = NoncoherentXBar()
self.membus = MemBus()
self.bridge = Bridge(delay='50ns')
self.t1000 = T1000()
mdesc = SysConfig()
self.readfile = mdesc.script()
- self.iobus = NoncoherentBus()
+ self.iobus = NoncoherentXBar()
self.membus = MemBus()
self.membus.badaddr_responder.warn_access = "warn"
self.bridge = Bridge(delay='50ns')
# generic system
mdesc = SysConfig()
self.readfile = mdesc.script()
- self.iobus = NoncoherentBus()
+ self.iobus = NoncoherentXBar()
self.membus = MemBus()
self.bridge = Bridge(delay='50ns')
self.mem_ranges = [AddrRange('1GB')]
x86_sys.membus = MemBus()
# North Bridge
- x86_sys.iobus = NoncoherentBus()
+ x86_sys.iobus = NoncoherentXBar()
x86_sys.bridge = Bridge(delay='50ns')
x86_sys.bridge.master = x86_sys.iobus.slave
x86_sys.bridge.slave = x86_sys.membus.master
def connectX86RubySystem(x86_sys):
# North Bridge
- x86_sys.iobus = NoncoherentBus()
+ x86_sys.iobus = NoncoherentXBar()
# add the ide to the list of dma devices that later need to attach to
# dma controllers
# and address mapping
# start with the system itself, using a multi-layer 1.5 GHz
-# bus/crossbar, delivering 64 bytes / 5 cycles (one header cycle)
+# crossbar, delivering 64 bytes / 5 cycles (one header cycle)
# which amounts to 19.2 GByte/s per layer and thus per port
-system = System(membus = NoncoherentBus(width = 16))
+system = System(membus = NoncoherentXBar(width = 16))
system.clk_domain = SrcClockDomain(clock = '1.5GHz',
voltage_domain =
VoltageDomain(voltage = '1V'))
# system simulated
system = System(funcmem = SimpleMemory(in_addr_map = False),
- funcbus = NoncoherentBus(),
+ funcbus = NoncoherentXBar(),
physmem = SimpleMemory(latency = "100ns"),
cache_line_size = block_size)
parent = attach_obj # use attach obj as config parent too
if len(spec) > 1 and (fanout > 1 or options.force_bus):
port = getattr(attach_obj, attach_port)
- new_bus = CoherentBus(width=16)
+ new_bus = CoherentXBar(width=16)
if (port.role == 'MASTER'):
new_bus.slave = port
attach_port = "master"
system = System(cpu = cpus,
funcmem = SimpleMemory(in_addr_map = False),
- funcbus = NoncoherentBus(),
+ funcbus = NoncoherentXBar(),
physmem = SimpleMemory(),
clk_domain = SrcClockDomain(clock = options.sys_clock),
mem_ranges = [AddrRange(options.mem_size)])
system.cpu[i].dtb.walker.port = ruby_port.slave
else:
MemClass = Simulation.setMemClass(options)
- system.membus = CoherentBus()
+ system.membus = CoherentXBar()
system.system_port = system.membus.slave
CacheConfig.config_cache(options, system)
MemConfig.config_mem(options, system)
for j in xrange(options.numclusters):
clusters[j].id = j
for cluster in clusters:
- cluster.clusterbus = CoherentBus(clock=busFrequency)
+ cluster.clusterbus = CoherentXBar(clock=busFrequency)
all_l1buses += [cluster.clusterbus]
cluster.cpus = [TimingSimpleCPU(cpu_id = i + cluster.id,
clock=options.frequency)
for j in xrange(options.numclusters):
clusters[j].id = j
for cluster in clusters:
- cluster.clusterbus = CoherentBus(clock=busFrequency)
+ cluster.clusterbus = CoherentXBar(clock=busFrequency)
all_l1buses += [cluster.clusterbus]
cluster.cpus = [DerivO3CPU(cpu_id = i + cluster.id,
clock=options.frequency)
for j in xrange(options.numclusters):
clusters[j].id = j
for cluster in clusters:
- cluster.clusterbus = CoherentBus(clock=busFrequency)
+ cluster.clusterbus = CoherentXBar(clock=busFrequency)
all_l1buses += [cluster.clusterbus]
cluster.cpus = [AtomicSimpleCPU(cpu_id = i + cluster.id,
clock=options.frequency)
# ----------------------
system = System(cpu = all_cpus, l1_ = all_l1s, l1bus_ = all_l1buses,
physmem = SimpleMemory(),
- membus = CoherentBus(clock = busFrequency))
+ membus = CoherentXBar(clock = busFrequency))
system.clock = '1GHz'
-system.toL2bus = CoherentBus(clock = busFrequency)
+system.toL2bus = CoherentXBar(clock = busFrequency)
system.l2 = L2(size = options.l2size, assoc = 8)
# ----------------------
# Create a system, and add system wide objects
# ----------------------
system = System(cpu = cpus, physmem = SimpleMemory(),
- membus = CoherentBus(clock = busFrequency))
+ membus = CoherentXBar(clock = busFrequency))
system.clock = '1GHz'
-system.toL2bus = CoherentBus(clock = busFrequency)
+system.toL2bus = CoherentXBar(clock = busFrequency)
system.l2 = L2(size = options.l2size, assoc = 8)
# ----------------------
assert(!read);
// @todo someone should pay for this
- pkt->busFirstWordDelay = pkt->busLastWordDelay = 0;
+ pkt->firstWordDelay = pkt->lastWordDelay = 0;
state = nextState;
nextState = Ready;
from m5.params import *
from m5.proxy import *
-from Bus import CoherentBus
+from XBar import CoherentXBar
from InstTracer import InstTracer
from ExeTracer import ExeTracer
from MemObject import MemObject
self.itb_walker_cache = iwc
self.dtb_walker_cache = dwc
if buildEnv['TARGET_ISA'] in ['arm']:
- self.itb_walker_cache_bus = CoherentBus()
- self.dtb_walker_cache_bus = CoherentBus()
+ self.itb_walker_cache_bus = CoherentXBar()
+ self.dtb_walker_cache_bus = CoherentXBar()
self.itb_walker_cache_bus.master = iwc.cpu_side
self.dtb_walker_cache_bus.master = dwc.cpu_side
self.itb.walker.port = self.itb_walker_cache_bus.slave
# Set a width of 32 bytes (256-bits), which is four times that
# of the default bus. The clock of the CPU is inherited by
# default.
- self.toL2Bus = CoherentBus(width = 32)
+ self.toL2Bus = CoherentXBar(width = 32)
self.connectCachedPorts(self.toL2Bus)
self.l2cache = l2c
self.toL2Bus.master = self.l2cache.cpu_side
*/
#include "base/trace.hh"
-#include "debug/BusAddrRanges.hh"
+#include "debug/AddrRanges.hh"
#include "dev/io_device.hh"
#include "sim/system.hh"
PioPort::recvAtomic(PacketPtr pkt)
{
// @todo: We need to pay for this and not just zero it out
- pkt->busFirstWordDelay = pkt->busLastWordDelay = 0;
+ pkt->firstWordDelay = pkt->lastWordDelay = 0;
return pkt->isRead() ? device->read(pkt) : device->write(pkt);
}
{
assert(pioSize != 0);
AddrRangeList ranges;
- DPRINTF(BusAddrRanges, "registering range: %#x-%#x\n", pioAddr, pioSize);
+ DPRINTF(AddrRanges, "registering range: %#x-%#x\n", pioAddr, pioSize);
ranges.push_back(RangeSize(pioAddr, pioSize));
return ranges;
}
assert(pkt->getAddr() >= configAddr &&
pkt->getAddr() < configAddr + PCI_CONFIG_SIZE);
// @todo someone should pay for this
- pkt->busFirstWordDelay = pkt->busLastWordDelay = 0;
+ pkt->firstWordDelay = pkt->lastWordDelay = 0;
return pkt->isRead() ? device->readConfig(pkt) : device->writeConfig(pkt);
}
Tick recvMessage(PacketPtr pkt)
{
// @todo someone should pay for this
- pkt->busFirstWordDelay = pkt->busLastWordDelay = 0;
+ pkt->firstWordDelay = pkt->lastWordDelay = 0;
return device->recvMessage(pkt);
}
};
+++ /dev/null
-# Copyright (c) 2012 ARM Limited
-# All rights reserved.
-#
-# The license below extends only to copyright in the software and shall
-# not be construed as granting a license to any other intellectual
-# property including but not limited to intellectual property relating
-# to a hardware implementation of the functionality of the software
-# licensed hereunder. You may use the software subject to the license
-# terms below provided that you ensure that this notice is replicated
-# unmodified and in its entirety in all distributions of the software,
-# modified or unmodified, in source code or in binary form.
-#
-# Copyright (c) 2005-2008 The Regents of The University of Michigan
-# All rights reserved.
-#
-# Redistribution and use in source and binary forms, with or without
-# modification, are permitted provided that the following conditions are
-# met: redistributions of source code must retain the above copyright
-# notice, this list of conditions and the following disclaimer;
-# redistributions in binary form must reproduce the above copyright
-# notice, this list of conditions and the following disclaimer in the
-# documentation and/or other materials provided with the distribution;
-# neither the name of the copyright holders nor the names of its
-# contributors may be used to endorse or promote products derived from
-# this software without specific prior written permission.
-#
-# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-# 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: Nathan Binkert
-# Andreas Hansson
-
-from MemObject import MemObject
-from System import System
-from m5.params import *
-from m5.proxy import *
-from m5.SimObject import SimObject
-
-class BaseBus(MemObject):
- type = 'BaseBus'
- abstract = True
- cxx_header = "mem/bus.hh"
- slave = VectorSlavePort("vector port for connecting masters")
- master = VectorMasterPort("vector port for connecting slaves")
- header_cycles = Param.Cycles(1, "cycles of overhead per transaction")
- width = Param.Unsigned(8, "bus width (bytes)")
-
- # The default port can be left unconnected, or be used to connect
- # a default slave port
- default = MasterPort("Port for connecting an optional default slave")
-
- # The default port can be used unconditionally, or based on
- # address range, in which case it may overlap with other
- # ports. The default range is always checked first, thus creating
- # a two-level hierarchical lookup. This is useful e.g. for the PCI
- # bus configuration.
- use_default_range = Param.Bool(False, "Perform address mapping for " \
- "the default port")
-
-class NoncoherentBus(BaseBus):
- type = 'NoncoherentBus'
- cxx_header = "mem/noncoherent_bus.hh"
-
-class CoherentBus(BaseBus):
- type = 'CoherentBus'
- cxx_header = "mem/coherent_bus.hh"
-
- system = Param.System(Parent.any, "System that the bus belongs to.")
- snoop_filter = Param.SnoopFilter(NULL, "Selected snoop filter for the bus.")
-
-class SnoopFilter(SimObject):
- type = 'SnoopFilter'
- cxx_header = "mem/snoop_filter.hh"
- lookup_latency = Param.Cycles(3, "lookup latency (cycles)")
-
- system = Param.System(Parent.any, "System that the bus belongs to.")
SimObject('AbstractMemory.py')
SimObject('AddrMapper.py')
SimObject('Bridge.py')
-SimObject('Bus.py')
SimObject('DRAMCtrl.py')
SimObject('MemObject.py')
SimObject('SimpleMemory.py')
+SimObject('XBar.py')
Source('abstract_mem.cc')
Source('addr_mapper.cc')
Source('bridge.cc')
-Source('bus.cc')
-Source('coherent_bus.cc')
+Source('coherent_xbar.cc')
Source('dram_ctrl.cc')
Source('mem_object.cc')
Source('mport.cc')
-Source('noncoherent_bus.cc')
+Source('noncoherent_xbar.cc')
Source('packet.cc')
Source('port.cc')
Source('packet_queue.cc')
-Source('tport.cc')
Source('port_proxy.cc')
-Source('simple_mem.cc')
Source('physical.cc')
+Source('simple_mem.cc')
Source('snoop_filter.cc')
+Source('tport.cc')
+Source('xbar.cc')
if env['TARGET_ISA'] != 'null':
Source('fs_translating_port_proxy.cc')
Source('dramsim2_wrapper.cc')
Source('dramsim2.cc')
-DebugFlag('BaseBus')
-DebugFlag('BusAddrRanges')
-DebugFlag('CoherentBus')
-DebugFlag('NoncoherentBus')
+DebugFlag('AddrRanges')
+DebugFlag('BaseXBar')
+DebugFlag('CoherentXBar')
+DebugFlag('NoncoherentXBar')
DebugFlag('SnoopFilter')
-CompoundFlag('Bus', ['BaseBus', 'BusAddrRanges', 'CoherentBus',
- 'NoncoherentBus', 'SnoopFilter'])
+CompoundFlag('XBar', ['BaseXBar', 'CoherentXBar', 'NoncoherentXBar',
+ 'SnoopFilter'])
DebugFlag('Bridge')
DebugFlag('CommMonitor')
--- /dev/null
+# Copyright (c) 2012 ARM Limited
+# All rights reserved.
+#
+# The license below extends only to copyright in the software and shall
+# not be construed as granting a license to any other intellectual
+# property including but not limited to intellectual property relating
+# to a hardware implementation of the functionality of the software
+# licensed hereunder. You may use the software subject to the license
+# terms below provided that you ensure that this notice is replicated
+# unmodified and in its entirety in all distributions of the software,
+# modified or unmodified, in source code or in binary form.
+#
+# Copyright (c) 2005-2008 The Regents of The University of Michigan
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met: redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer;
+# redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution;
+# neither the name of the copyright holders nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# 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: Nathan Binkert
+# Andreas Hansson
+
+from MemObject import MemObject
+from System import System
+from m5.params import *
+from m5.proxy import *
+from m5.SimObject import SimObject
+
+class BaseXBar(MemObject):
+ type = 'BaseXBar'
+ abstract = True
+ cxx_header = "mem/xbar.hh"
+ slave = VectorSlavePort("vector port for connecting masters")
+ master = VectorMasterPort("vector port for connecting slaves")
+ header_cycles = Param.Cycles(1, "cycles of overhead per transaction")
+ width = Param.Unsigned(8, "xbar width (bytes)")
+
+ # The default port can be left unconnected, or be used to connect
+ # a default slave port
+ default = MasterPort("Port for connecting an optional default slave")
+
+ # The default port can be used unconditionally, or based on
+ # address range, in which case it may overlap with other
+ # ports. The default range is always checked first, thus creating
+ # a two-level hierarchical lookup. This is useful e.g. for the PCI
+ # xbar configuration.
+ use_default_range = Param.Bool(False, "Perform address mapping for " \
+ "the default port")
+
+class NoncoherentXBar(BaseXBar):
+ type = 'NoncoherentXBar'
+ cxx_header = "mem/noncoherent_xbar.hh"
+
+class CoherentXBar(BaseXBar):
+ type = 'CoherentXBar'
+ cxx_header = "mem/coherent_xbar.hh"
+
+ system = Param.System(Parent.any, "System that the crossbar belongs to.")
+ snoop_filter = Param.SnoopFilter(NULL, "Selected snoop filter.")
+
+class SnoopFilter(SimObject):
+ type = 'SnoopFilter'
+ cxx_header = "mem/snoop_filter.hh"
+ lookup_latency = Param.Cycles(3, "lookup latency (cycles)")
+
+ system = Param.System(Parent.any, "System that the crossbar belongs to.")
/**
* @file
- * Implementation of a memory-mapped bus bridge that connects a master
+ * Implementation of a memory-mapped bridge that connects a master
* and a slave through a request and response queue.
*/
{
// make sure both sides are connected and have the same block size
if (!slavePort.isConnected() || !masterPort.isConnected())
- fatal("Both ports of bus bridge are not connected to a bus.\n");
+ fatal("Both ports of a bridge must be connected.\n");
// notify the master side of our address ranges
slavePort.sendRangeChange();
DPRINTF(Bridge, "Request queue size: %d\n", transmitList.size());
// @todo: We need to pay for this and not just zero it out
- pkt->busFirstWordDelay = pkt->busLastWordDelay = 0;
+ pkt->firstWordDelay = pkt->lastWordDelay = 0;
slavePort.schedTimingResp(pkt, bridge.clockEdge(delay));
if (!retryReq) {
// @todo: We need to pay for this and not just zero it out
- pkt->busFirstWordDelay = pkt->busLastWordDelay = 0;
+ pkt->firstWordDelay = pkt->lastWordDelay = 0;
masterPort.schedTimingReq(pkt, bridge.clockEdge(delay));
}
{
// If we expect to see a response, we need to restore the source
// and destination field that is potentially changed by a second
- // bus
+ // crossbar
if (!pkt->memInhibitAsserted() && pkt->needsResponse()) {
// Update the sender state so we can deal with the response
// appropriately
pkt->setDest(req_state->origSrc);
delete req_state;
- // the bridge assumes that at least one bus has set the
+ // the bridge assumes that at least one crossbar has set the
// destination field of the packet
assert(pkt->isDestValid());
DPRINTF(Bridge, "response, new dest %d\n", pkt->getDest());
/**
* @file
- * Declaration of a memory-mapped bus bridge that connects a master
+ * Declaration of a memory-mapped bridge that connects a master
* and a slave through a request and response queue.
*/
#include "params/Bridge.hh"
/**
- * A bridge is used to interface two different busses (or in general a
+ * A bridge is used to interface two different crossbars (or in general a
* memory-mapped master and slave), with buffering for requests and
* responses. The bridge has a fixed delay for packets passing through
* it and responds to a fixed set of address ranges.
Bridge& bridge;
/**
- * Master port on the other side of the bridge (connected to
- * the other bus).
+ * Master port on the other side of the bridge.
*/
BridgeMasterPort& masterPort;
Bridge& bridge;
/**
- * The slave port on the other side of the bridge (connected
- * to the other bus).
+ * The slave port on the other side of the bridge.
*/
BridgeSlavePort& slavePort;
Bridge(Params *p);
};
-#endif //__MEM_BUS_HH__
+#endif //__MEM_BRIDGE_HH__
+++ /dev/null
-/*
- * Copyright (c) 2011-2013 ARM Limited
- * All rights reserved
- *
- * The license below extends only to copyright in the software and shall
- * not be construed as granting a license to any other intellectual
- * property including but not limited to intellectual property relating
- * to a hardware implementation of the functionality of the software
- * licensed hereunder. You may use the software subject to the license
- * terms below provided that you ensure that this notice is replicated
- * unmodified and in its entirety in all distributions of the software,
- * modified or unmodified, in source code or in binary form.
- *
- * Copyright (c) 2006 The Regents of The University of Michigan
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are
- * met: redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer;
- * redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution;
- * neither the name of the copyright holders nor the names of its
- * contributors may be used to endorse or promote products derived from
- * this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * 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
- */
-
-/**
- * @file
- * Definition of a bus object.
- */
-
-#include "base/misc.hh"
-#include "base/trace.hh"
-#include "debug/Bus.hh"
-#include "debug/BusAddrRanges.hh"
-#include "debug/Drain.hh"
-#include "mem/bus.hh"
-
-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)
-{}
-
-BaseBus::~BaseBus()
-{
- for (MasterPortIter m = masterPorts.begin(); m != masterPorts.end();
- ++m) {
- delete *m;
- }
-
- for (SlavePortIter s = slavePorts.begin(); s != slavePorts.end();
- ++s) {
- delete *s;
- }
-}
-
-void
-BaseBus::init()
-{
-}
-
-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];
- } else {
- return MemObject::getMasterPort(if_name, 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
- return *slavePorts[idx];
- } else {
- return MemObject::getSlavePort(if_name, idx);
- }
-}
-
-void
-BaseBus::calcPacketTiming(PacketPtr pkt)
-{
- // 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 = 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 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 <typename SrcType, typename DstType>
-BaseBus::Layer<SrcType,DstType>::Layer(DstType& _port, BaseBus& _bus,
- const std::string& _name) :
- port(_port), bus(_bus), _name(_name), state(IDLE), drainManager(NULL),
- waitingForPeer(NULL), releaseEvent(this)
-{
-}
-
-template <typename SrcType, typename DstType>
-void BaseBus::Layer<SrcType,DstType>::occupyLayer(Tick until)
-{
- // ensure the state is busy 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 == BUSY);
-
- // until should never be 0 as express snoops never occupy the bus
- assert(until != 0);
- bus.schedule(releaseEvent, until);
-
- // account for the occupied ticks
- occupancy += until - curTick();
-
- DPRINTF(BaseBus, "The bus is now busy from tick %d to %d\n",
- curTick(), until);
-}
-
-template <typename SrcType, typename DstType>
-bool
-BaseBus::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
- // this state again in zero time if the peer does not immediately
- // call the bus when receiving the retry
-
- // first we see if the layer is busy, next we check if the
- // destination port is already engaged in a transaction waiting
- // for a retry from the peer
- if (state == BUSY || waitingForPeer != NULL) {
- // the port should not be waiting already
- assert(std::find(waitingForLayer.begin(), waitingForLayer.end(),
- src_port) == waitingForLayer.end());
-
- // put the port at the end of the retry list waiting for the
- // layer to be freed up (and in the case of a busy peer, for
- // that transaction to go through, and then the bus to free
- // up)
- waitingForLayer.push_back(src_port);
- return false;
- }
-
- // update the state to busy
- state = BUSY;
-
- return true;
-}
-
-template <typename SrcType, typename DstType>
-void
-BaseBus::Layer<SrcType,DstType>::succeededTiming(Tick busy_time)
-{
- // we should have gone from idle or retry to busy in the tryTiming
- // test
- assert(state == BUSY);
-
- // occupy the bus accordingly
- occupyLayer(busy_time);
-}
-
-template <typename SrcType, typename DstType>
-void
-BaseBus::Layer<SrcType,DstType>::failedTiming(SrcType* src_port,
- Tick busy_time)
-{
- // ensure no one got in between and tried to send something to
- // this port
- assert(waitingForPeer == NULL);
-
- // if the source port is the current retrying one or not, we have
- // failed in forwarding and should track that we are now waiting
- // for the peer to send a retry
- waitingForPeer = src_port;
-
- // we should have gone from idle or retry to busy in the tryTiming
- // test
- assert(state == BUSY);
-
- // occupy the bus accordingly
- occupyLayer(busy_time);
-}
-
-template <typename SrcType, typename DstType>
-void
-BaseBus::Layer<SrcType,DstType>::releaseLayer()
-{
- // releasing the bus means we should now be idle
- assert(state == BUSY);
- assert(!releaseEvent.scheduled());
-
- // update the state
- state = IDLE;
-
- // bus layer is now idle, so if someone is waiting we can retry
- if (!waitingForLayer.empty()) {
- // there is no point in sending a retry if someone is still
- // waiting for the peer
- if (waitingForPeer == NULL)
- retryWaiting();
- } else if (waitingForPeer == NULL && 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.
- drainManager = NULL;
- }
-}
-
-template <typename SrcType, typename DstType>
-void
-BaseBus::Layer<SrcType,DstType>::retryWaiting()
-{
- // this should never be called with no one waiting
- assert(!waitingForLayer.empty());
-
- // we always go to retrying from idle
- assert(state == IDLE);
-
- // update the state
- state = RETRY;
-
- // set the retrying port to the front of the retry list and pop it
- // off the list
- SrcType* retryingPort = waitingForLayer.front();
- waitingForLayer.pop_front();
-
- // tell the port to retry, which in some cases ends up calling the
- // bus
- retryingPort->sendRetry();
-
- // If the bus is still in the retry state, sendTiming wasn't
- // called in zero time (e.g. the cache does this), burn a cycle
- 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 bus layer until the next cycle ends
- occupyLayer(bus.clockEdge(Cycles(1)));
- }
-}
-
-template <typename SrcType, typename DstType>
-void
-BaseBus::Layer<SrcType,DstType>::recvRetry()
-{
- // we should never get a retry without having failed to forward
- // something to this port
- assert(waitingForPeer != NULL);
-
- // add the port where the failed packet originated to the front of
- // the waiting ports for the layer, this allows us to call retry
- // on the port immediately if the bus layer is idle
- waitingForLayer.push_front(waitingForPeer);
-
- // we are no longer waiting for the peer
- waitingForPeer = NULL;
-
- // if the bus layer is idle, retry this port straight away, if we
- // are busy, then simply let the port wait for its turn
- if (state == IDLE) {
- retryWaiting();
- } else {
- assert(state == BUSY);
- }
-}
-
-PortID
-BaseBus::findPort(Addr addr)
-{
- // 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 the address map interval tree
- PortMapConstIter i = portMap.find(addr);
- if (i != portMap.end()) {
- dest_id = i->second;
- updatePortCache(dest_id, i->first);
- return dest_id;
- }
-
- // Check if this matches the default range
- if (useDefaultRange) {
- 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, "
- "will use default port\n", addr);
- 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 bus %s\n", addr,
- name());
-}
-
-/** Function called by the port when the bus is receiving a range change.*/
-void
-BaseBus::recvRangeChange(PortID 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<bool>::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 (ranges.size() != 1)
- fatal("Bus %s may only have a single default range",
- name());
-
- 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++;
- }
- }
-
- AddrRangeList ranges = masterPorts[master_port_id]->getAddrRanges();
-
- 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());
- }
- }
- }
-
- // 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();
-
- // start out with the default range
- if (useDefaultRange) {
- if (!gotAddrRanges[defaultPortID])
- fatal("Bus %s uses default range, but none provided",
- name());
-
- busRanges.push_back(defaultRange);
- DPRINTF(BusAddrRanges, "-- Adding default %s\n",
- defaultRange.to_string());
- }
-
- // merge all interleaved ranges and add any range that is not
- // a subset of the default range
- std::vector<AddrRange> intlv_ranges;
- for (AddrRangeMap<PortID>::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());
- }
- }
- }
-
- // 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());
- }
- }
-
- // 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();
- }
-
- 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;
-}
-
-void
-BaseBus::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
- for (int i = 0; i < MemCmd::NUM_MEM_CMDS; i++) {
- MemCmd cmd(i);
- const std::string &cstr = cmd.toString();
- transDist.subname(i, cstr);
- }
-
- pktCount
- .init(slavePorts.size(), masterPorts.size())
- .name(name() + ".pkt_count")
- .desc("Packet count per connected master and slave (bytes)")
- .flags(total | nozero | nonan);
-
- totPktSize
- .init(slavePorts.size(), masterPorts.size())
- .name(name() + ".tot_pkt_size")
- .desc("Cumulative packet size per connected master and slave (bytes)")
- .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());
- totPktSize.subname(i, slavePorts[i]->getMasterPort().name());
- for (int j = 0; j < masterPorts.size(); j++) {
- pktCount.ysubname(j, masterPorts[j]->getSlavePort().name());
- totPktSize.ysubname(j, masterPorts[j]->getSlavePort().name());
- }
- }
-}
-
-template <typename SrcType, typename DstType>
-unsigned int
-BaseBus::Layer<SrcType,DstType>::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 (state != IDLE) {
- DPRINTF(Drain, "Bus not drained\n");
- drainManager = dm;
- return 1;
- }
- return 0;
-}
-
-template <typename SrcType, typename DstType>
-void
-BaseBus::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;
-}
-
-/**
- * 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<SlavePort,MasterPort>;
-template class BaseBus::Layer<MasterPort,SlavePort>;
+++ /dev/null
-/*
- * Copyright (c) 2011-2013 ARM Limited
- * All rights reserved
- *
- * The license below extends only to copyright in the software and shall
- * not be construed as granting a license to any other intellectual
- * property including but not limited to intellectual property relating
- * to a hardware implementation of the functionality of the software
- * licensed hereunder. You may use the software subject to the license
- * terms below provided that you ensure that this notice is replicated
- * unmodified and in its entirety in all distributions of the software,
- * modified or unmodified, in source code or in binary form.
- *
- * Copyright (c) 2002-2005 The Regents of The University of Michigan
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are
- * met: redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer;
- * redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution;
- * neither the name of the copyright holders nor the names of its
- * contributors may be used to endorse or promote products derived from
- * this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * 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: Ron Dreslinski
- * Ali Saidi
- * Andreas Hansson
- * William Wang
- */
-
-/**
- * @file
- * Declaration of an abstract bus base class.
- */
-
-#ifndef __MEM_BUS_HH__
-#define __MEM_BUS_HH__
-
-#include <deque>
-
-#include "base/addr_range_map.hh"
-#include "base/types.hh"
-#include "mem/mem_object.hh"
-#include "params/BaseBus.hh"
-#include "sim/stats.hh"
-
-/**
- * The base bus contains the common elements of the non-coherent and
- * coherent bus. It is an abstract class that does not have any of the
- * functionality relating to the actual reception and transmission of
- * packets, as this is left for the subclasses.
- *
- * The BaseBus is responsible for the basic flow control (busy or
- * not), the administration of retries, and the address decoding.
- */
-class BaseBus : public MemObject
-{
-
- protected:
-
- /**
- * A bus layer is an internal bus structure with its own flow
- * control and arbitration. Hence, a single-layer bus mimics a
- * traditional off-chip tri-state bus (like PCI), where only one
- * set of wires are shared. For on-chip buses, a good starting
- * point is to have three layers, for requests, responses, and
- * snoop responses respectively (snoop requests are instantaneous
- * and do not need any flow control or arbitration). This case is
- * similar to AHB and some OCP configurations.
- *
- * As a further extensions beyond the three-layer bus, a future
- * multi-layer bus has with one layer per connected slave port
- * provides a full or partial crossbar, like AXI, OCP, PCIe etc.
- *
- * The template parameter, PortClass, indicates the destination
- * port type for the bus. The retry list holds either master ports
- * or slave ports, depending on the direction of the layer. Thus,
- * a request layer has a retry list containing slave ports,
- * whereas a response layer holds master ports.
- */
- template <typename SrcType, typename DstType>
- class Layer : public Drainable
- {
-
- public:
-
- /**
- * Create a bus layer and give it a name. The bus layer uses
- * the bus an event manager.
- *
- * @param _port destination port the layer converges at
- * @param _bus the bus this layer belongs to
- * @param _name the layer's name
- */
- Layer(DstType& _port, BaseBus& _bus, const std::string& _name);
-
- /**
- * Drain according to the normal semantics, so that the bus
- * can tell the layer to drain, and pass an event to signal
- * back when drained.
- *
- * @param de drain event to call once drained
- *
- * @return 1 if busy or waiting to retry, or 0 if idle
- */
- unsigned int drain(DrainManager *dm);
-
- /**
- * Get the bus layer's name
- */
- const std::string name() const { return bus.name() + _name; }
-
-
- /**
- * Determine if the bus layer 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 layer is
- * updated accordingly.
- *
- * @param port Source port presenting the packet
- *
- * @return True if the bus layer accepts the packet
- */
- bool tryTiming(SrcType* src_port);
-
- /**
- * Deal with a destination port accepting a packet by potentially
- * removing the source port from the retry list (if retrying) and
- * occupying the bus layer accordingly.
- *
- * @param busy_time Time to spend as a result of a successful send
- */
- 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 layer
- * accordingly.
- *
- * @param src_port Source port
- * @param busy_time Time to spend as a result of a failed send
- */
- void failedTiming(SrcType* src_port, Tick busy_time);
-
- /** Occupy the bus layer until until */
- void occupyLayer(Tick until);
-
- /**
- * Send a retry to the port at the head of waitingForLayer. The
- * caller must ensure that the list is not empty.
- */
- void retryWaiting();
-
- /**
- * Handle a retry from a neighbouring module. This wraps
- * retryWaiting by verifying that there are ports waiting
- * before calling retryWaiting.
- */
- void recvRetry();
-
- /**
- * Register stats for the layer
- */
- void regStats();
-
- private:
-
- /** The destination port this layer converges at. */
- DstType& port;
-
- /** The bus this layer is a part of. */
- BaseBus& bus;
-
- /** A name for this layer. */
- std::string _name;
-
- /**
- * We declare an enum to track the state of the bus layer. The
- * starting point is an idle state where the bus layer is
- * waiting for a packet to arrive. Upon arrival, the bus layer
- * transitions to the busy state, where it remains either
- * until the packet transfer is done, or the header time is
- * spent. Once the bus layer leaves the busy state, it can
- * either go back to idle, if no packets have arrived while it
- * was busy, or the bus layer goes on to retry the first port
- * in waitingForLayer. A similar transition takes place from
- * idle to retry if the bus layer 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 layer, 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 layer */
- State state;
-
- /** manager to signal when drained */
- DrainManager *drainManager;
-
- /**
- * A deque of ports that retry should be called on because
- * the original send was delayed due to a busy layer.
- */
- std::deque<SrcType*> waitingForLayer;
-
- /**
- * Track who is waiting for the retry when receiving it from a
- * peer. If no port is waiting NULL is stored.
- */
- SrcType* waitingForPeer;
-
- /**
- * Release the bus layer after being occupied and return to an
- * idle state where we proceed to send a retry to any
- * potential waiting port, or drain if asked to do so.
- */
- void releaseLayer();
-
- /** event used to schedule a release of the layer */
- EventWrapper<Layer, &Layer::releaseLayer> releaseEvent;
-
- /**
- * Stats for occupancy and utilization. These stats capture
- * the time the bus spends in the busy state and are thus only
- * relevant when the memory system is in timing mode.
- */
- Stats::Scalar occupancy;
- Stats::Formula utilization;
-
- };
-
- /** cycles of overhead per transaction */
- const Cycles headerCycles;
- /** the width of the bus in bytes */
- const uint32_t width;
-
- typedef AddrRangeMap<PortID>::iterator PortMapIter;
- typedef AddrRangeMap<PortID>::const_iterator PortMapConstIter;
- AddrRangeMap<PortID> portMap;
-
- /** all contigous ranges seen by this bus */
- AddrRangeList busRanges;
-
- AddrRange defaultRange;
-
- /**
- * Function called by the port when the bus is recieving a range change.
- *
- * @param master_port_id id of the port that received the change
- */
- void recvRangeChange(PortID master_port_id);
-
- /** Find which port connected to this bus (if any) should be given a packet
- * with this address.
- * @param addr Address to find port for.
- * @return id of port that the packet should be sent out of.
- */
- PortID findPort(Addr addr);
-
- // Cache for the findPort function storing recently used ports from portMap
- struct PortCache {
- bool valid;
- PortID id;
- AddrRange range;
- };
-
- PortCache portCache[3];
-
- // Checks the cache and returns the id of the port that has the requested
- // address within its range
- inline PortID checkPortCache(Addr addr) const {
- if (portCache[0].valid && portCache[0].range.contains(addr)) {
- return portCache[0].id;
- }
- if (portCache[1].valid && portCache[1].range.contains(addr)) {
- return portCache[1].id;
- }
- if (portCache[2].valid && portCache[2].range.contains(addr)) {
- return portCache[2].id;
- }
-
- return InvalidPortID;
- }
-
- // Clears the earliest entry of the cache and inserts a new port entry
- inline void updatePortCache(short id, const AddrRange& range) {
- portCache[2].valid = portCache[1].valid;
- portCache[2].id = portCache[1].id;
- portCache[2].range = portCache[1].range;
-
- portCache[1].valid = portCache[0].valid;
- portCache[1].id = portCache[0].id;
- portCache[1].range = portCache[0].range;
-
- portCache[0].valid = true;
- portCache[0].id = id;
- portCache[0].range = range;
- }
-
- // Clears the cache. Needs to be called in constructor.
- inline void clearPortCache() {
- portCache[2].valid = false;
- portCache[1].valid = false;
- portCache[0].valid = false;
- }
-
- /**
- * Return the address ranges the bus is responsible for.
- *
- * @return a list of non-overlapping address ranges
- */
- AddrRangeList getAddrRanges() const;
-
- /**
- * Calculate the timing parameters for the packet. Updates the
- * busFirstWordDelay and busLastWordDelay fields of the packet
- * object with the relative number of ticks required to transmit
- * the header and the first word, and the last word, respectively.
- */
- void calcPacketTiming(PacketPtr pkt);
-
- /**
- * Remember for each of the master ports of the bus if we got an
- * address range from the connected slave. For convenience, also
- * keep track of if we got ranges from all the slave modules or
- * not.
- */
- std::vector<bool> gotAddrRanges;
- bool gotAllAddrRanges;
-
- /** The master and slave ports of the bus */
- std::vector<SlavePort*> slavePorts;
- std::vector<MasterPort*> masterPorts;
-
- /** Convenience typedefs. */
- typedef std::vector<SlavePort*>::iterator SlavePortIter;
- typedef std::vector<MasterPort*>::iterator MasterPortIter;
- typedef std::vector<SlavePort*>::const_iterator SlavePortConstIter;
- typedef std::vector<MasterPort*>::const_iterator MasterPortConstIter;
-
- /** Port that handles requests that don't match any of the interfaces.*/
- PortID defaultPortID;
-
- /** If true, use address range provided by default device. Any
- address not handled by another port and not in default device's
- range will cause a fatal error. If false, just send all
- addresses not handled by another port to default device. */
- const bool useDefaultRange;
-
- BaseBus(const BaseBusParams *p);
-
- virtual ~BaseBus();
-
- /**
- * Stats for transaction distribution and data passing through the
- * bus. The transaction distribution is globally counting
- * different types of commands. The packet count and total packet
- * size are two-dimensional vectors that are indexed by the bus
- * slave port and master port id (thus the neighbouring master and
- * neighbouring slave), summing up both directions (request and
- * response).
- */
- Stats::Formula throughput;
- Stats::Vector transDist;
- Stats::Vector2d pktCount;
- Stats::Vector2d totPktSize;
-
- public:
-
- virtual void init();
-
- /** A function used to return the port associated with this bus object. */
- BaseMasterPort& getMasterPort(const std::string& if_name,
- PortID idx = InvalidPortID);
- BaseSlavePort& getSlavePort(const std::string& if_name,
- PortID idx = InvalidPortID);
-
- virtual unsigned int drain(DrainManager *dm) = 0;
-
- virtual void regStats();
-
-};
-
-#endif //__MEM_BUS_HH__
pkt->setDest(rec->prevSrc);
delete rec;
// @todo someone should pay for this
- pkt->busFirstWordDelay = pkt->busLastWordDelay = 0;
+ pkt->firstWordDelay = pkt->lastWordDelay = 0;
memSidePort->schedTimingSnoopResp(pkt, time);
}
Packet *snoopPkt = new Packet(pkt, true); // clear flags
// also reset the bus time that the original packet has
// not yet paid for
- snoopPkt->busFirstWordDelay = snoopPkt->busLastWordDelay = 0;
+ snoopPkt->firstWordDelay = snoopPkt->lastWordDelay = 0;
snoopPkt->setExpressSnoop();
snoopPkt->assertMemInhibit();
bool M5_VAR_USED success = memSidePort->sendTimingReq(snoopPkt);
uncacheableFlush(pkt);
// @todo: someone should pay for this
- pkt->busFirstWordDelay = pkt->busLastWordDelay = 0;
+ pkt->firstWordDelay = pkt->lastWordDelay = 0;
// writes go in write buffer, reads use MSHR,
// prefetches are acknowledged (responded to) and dropped
if (needsResponse) {
pkt->makeTimingResponse();
// @todo: Make someone pay for this
- pkt->busFirstWordDelay = pkt->busLastWordDelay = 0;
+ pkt->firstWordDelay = pkt->lastWordDelay = 0;
cpuSidePort->schedTimingResp(pkt, clockEdge(lat));
} else {
/// @todo nominally we should just delete the packet here,
// miss
// @todo: Make someone pay for this
- pkt->busFirstWordDelay = pkt->busLastWordDelay = 0;
+ pkt->firstWordDelay = pkt->lastWordDelay = 0;
if (blk && blk->isValid() && (blk->status & BlkCanGoExclusive) &&
pkt->isWrite() && (pkt->cmd != MemCmd::WriteInvalidateReq)) {
// from lower level caches/memory to an upper level cache or
// the core.
completion_time = clockEdge(responseLatency) +
- (transfer_offset ? pkt->busLastWordDelay :
- pkt->busFirstWordDelay);
+ (transfer_offset ? pkt->lastWordDelay :
+ pkt->firstWordDelay);
assert(!target->pkt->req->isUncacheable());
// from lower level caches/memory to an upper level cache or
// the core.
completion_time = clockEdge(responseLatency) +
- pkt->busLastWordDelay;
+ pkt->lastWordDelay;
target->pkt->req->setExtraData(0);
} else if (pkt->cmd == MemCmd::WriteInvalidateResp) {
if (blk) {
// will occur for its impatience (since it will think it
// has dirty data), but it really can't be helped.
completion_time = clockEdge(responseLatency) +
- pkt->busLastWordDelay;
+ pkt->lastWordDelay;
} else {
// not a cache fill, just forwarding response
// responseLatency is the latency of the return path
// from lower level cahces/memory to the core.
completion_time = clockEdge(responseLatency) +
- pkt->busLastWordDelay;
+ pkt->lastWordDelay;
if (pkt->isRead() && !is_error) {
target->pkt->setData(pkt->getPtr<uint8_t>());
}
target->pkt->getAddr());
}
// reset the bus additional time as it is now accounted for
- target->pkt->busFirstWordDelay = target->pkt->busLastWordDelay = 0;
+ target->pkt->firstWordDelay = target->pkt->lastWordDelay = 0;
cpuSidePort->schedTimingResp(target->pkt, completion_time);
break;
mq = mshr->queue;
mq->markPending(mshr);
requestMemSideBus((RequestCause)mq->index, clockEdge() +
- pkt->busLastWordDelay);
+ pkt->lastWordDelay);
} else {
mq->deallocate(mshr);
if (wasFull && !mq->isFull()) {
}
blk->whenReady = clockEdge() + responseLatency * clockPeriod() +
- pkt->busLastWordDelay;
+ pkt->lastWordDelay;
return blk;
}
pkt->allocate();
pkt->makeTimingResponse();
// @todo Make someone pay for this
- pkt->busFirstWordDelay = pkt->busLastWordDelay = 0;
+ pkt->firstWordDelay = pkt->lastWordDelay = 0;
if (pkt->isRead()) {
pkt->setDataFromBlock(blk_data, blkSize);
}
snoopPkt.pushSenderState(new ForwardResponseRecord(pkt->getSrc()));
// the snoop packet does not need to wait any additional
// time
- snoopPkt.busFirstWordDelay = snoopPkt.busLastWordDelay = 0;
+ snoopPkt.firstWordDelay = snoopPkt.lastWordDelay = 0;
cpuSidePort->sendTimingSnoopReq(&snoopPkt);
if (snoopPkt.memInhibitAsserted()) {
// cache-to-cache response from some upper cache
+++ /dev/null
-/*
- * Copyright (c) 2011-2013 ARM Limited
- * All rights reserved
- *
- * The license below extends only to copyright in the software and shall
- * not be construed as granting a license to any other intellectual
- * property including but not limited to intellectual property relating
- * to a hardware implementation of the functionality of the software
- * licensed hereunder. You may use the software subject to the license
- * terms below provided that you ensure that this notice is replicated
- * unmodified and in its entirety in all distributions of the software,
- * modified or unmodified, in source code or in binary form.
- *
- * Copyright (c) 2006 The Regents of The University of Michigan
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are
- * met: redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer;
- * redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution;
- * neither the name of the copyright holders nor the names of its
- * contributors may be used to endorse or promote products derived from
- * this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * 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
- */
-
-/**
- * @file
- * Definition of a bus object.
- */
-
-#include "base/misc.hh"
-#include "base/trace.hh"
-#include "debug/BusAddrRanges.hh"
-#include "debug/CoherentBus.hh"
-#include "mem/coherent_bus.hh"
-#include "sim/system.hh"
-
-CoherentBus::CoherentBus(const CoherentBusParams *p)
- : BaseBus(p), system(p->system), snoopFilter(p->snoop_filter)
-{
- // 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.master[%d]", name(), i);
- MasterPort* bp = new CoherentBusMasterPort(portName, *this, i);
- masterPorts.push_back(bp);
- reqLayers.push_back(new ReqLayer(*bp, *this,
- csprintf(".reqLayer%d", i)));
- snoopLayers.push_back(new SnoopLayer(*bp, *this,
- csprintf(".snoopLayer%d", i)));
- }
-
- // 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 = name() + ".default";
- MasterPort* bp = new CoherentBusMasterPort(portName, *this,
- defaultPortID);
- masterPorts.push_back(bp);
- reqLayers.push_back(new ReqLayer(*bp, *this, csprintf(".reqLayer%d",
- defaultPortID)));
- snoopLayers.push_back(new SnoopLayer(*bp, *this,
- csprintf(".snoopLayer%d",
- defaultPortID)));
- }
-
- // 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.slave[%d]", name(), i);
- SlavePort* bp = new CoherentBusSlavePort(portName, *this, i);
- slavePorts.push_back(bp);
- respLayers.push_back(new RespLayer(*bp, *this,
- csprintf(".respLayer%d", i)));
- snoopRespPorts.push_back(new SnoopRespPort(*bp, *this));
- }
-
- if (snoopFilter)
- snoopFilter->setSlavePorts(slavePorts);
-
- clearPortCache();
-}
-
-CoherentBus::~CoherentBus()
-{
- for (auto l = reqLayers.begin(); l != reqLayers.end(); ++l)
- delete *l;
- for (auto l = respLayers.begin(); l != respLayers.end(); ++l)
- delete *l;
- for (auto l = snoopLayers.begin(); l != snoopLayers.end(); ++l)
- delete *l;
- for (auto p = snoopRespPorts.begin(); p != snoopRespPorts.end(); ++p)
- delete *p;
-}
-
-void
-CoherentBus::init()
-{
- // the base class is responsible for determining the block size
- BaseBus::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) {
- // check if the connected master port is snooping
- if ((*p)->isSnooping()) {
- DPRINTF(BusAddrRanges, "Adding snooping master %s\n",
- (*p)->getMasterPort().name());
- snoopPorts.push_back(*p);
- }
- }
-
- if (snoopPorts.empty())
- warn("CoherentBus %s has no snooping ports attached!\n", name());
-}
-
-bool
-CoherentBus::recvTimingReq(PacketPtr pkt, PortID slave_port_id)
-{
- // 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();
-
- // determine the destination based on the address
- PortID master_port_id = findPort(pkt->getAddr());
-
- // test if the bus should be considered occupied for the current
- // port, and exclude express snoops from the check
- if (!is_express_snoop && !reqLayers[master_port_id]->tryTiming(src_port)) {
- DPRINTF(CoherentBus, "recvTimingReq: src %s %s 0x%x BUS BUSY\n",
- src_port->name(), pkt->cmdString(), pkt->getAddr());
- return false;
- }
-
- DPRINTF(CoherentBus, "recvTimingReq: src %s %s expr %d 0x%x\n",
- src_port->name(), pkt->cmdString(), is_express_snoop,
- pkt->getAddr());
-
- // store size and command as they might be modified when
- // forwarding the packet
- unsigned int pkt_size = pkt->hasData() ? pkt->getSize() : 0;
- unsigned int pkt_cmd = pkt->cmdToIndex();
-
- // set the source port for routing of the response
- pkt->setSrc(slave_port_id);
-
- calcPacketTiming(pkt);
- Tick packetFinishTime = pkt->busLastWordDelay + curTick();
-
- // uncacheable requests need never be snooped
- if (!pkt->req->isUncacheable() && !system->bypassCaches()) {
- // the packet is a memory-mapped request and should be
- // broadcasted to our snoopers but the source
- if (snoopFilter) {
- // check with the snoop filter where to forward this packet
- auto sf_res = snoopFilter->lookupRequest(pkt, *src_port);
- packetFinishTime += sf_res.second * clockPeriod();
- DPRINTF(CoherentBus, "recvTimingReq: src %s %s 0x%x"\
- " SF size: %i lat: %i\n", src_port->name(),
- pkt->cmdString(), pkt->getAddr(), sf_res.first.size(),
- sf_res.second);
- forwardTiming(pkt, slave_port_id, sf_res.first);
- } else {
- forwardTiming(pkt, slave_port_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 = !is_express_snoop && 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);
- }
-
- // Note: Cannot create a copy of the full packet, here.
- MemCmd orig_cmd(pkt->cmd);
-
- // since it is a normal request, attempt to send the packet
- bool success = masterPorts[master_port_id]->sendTimingReq(pkt);
-
- if (snoopFilter && !pkt->req->isUncacheable()
- && !system->bypassCaches()) {
- // The packet may already be overwritten by the sendTimingReq function.
- // The snoop filter needs to see the original request *and* the return
- // status of the send operation, so we need to recreate the original
- // request. Atomic mode does not have the issue, as there the send
- // operation and the response happen instantaneously and don't need two
- // phase tracking.
- MemCmd tmp_cmd(pkt->cmd);
- pkt->cmd = orig_cmd;
- // Let the snoop filter know about the success of the send operation
- snoopFilter->updateRequest(pkt, *src_port, !success);
- pkt->cmd = tmp_cmd;
- }
-
- // if this is an express snoop, we are done at this point
- if (is_express_snoop) {
- assert(success);
- snoopDataThroughBus += pkt_size;
- snoopsThroughBus++;
- } 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);
-
- // undo the calculation so we can check for 0 again
- pkt->busFirstWordDelay = pkt->busLastWordDelay = 0;
-
- 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
- reqLayers[master_port_id]->failedTiming(src_port,
- clockEdge(headerCycles));
- } else {
- // update the bus state and schedule an idle event
- reqLayers[master_port_id]->succeededTiming(packetFinishTime);
- dataThroughBus += pkt_size;
- }
- }
-
- // stats updates only consider packets that were successfully sent
- if (success) {
- pktCount[slave_port_id][master_port_id]++;
- totPktSize[slave_port_id][master_port_id] += pkt_size;
- transDist[pkt_cmd]++;
- }
-
- return success;
-}
-
-bool
-CoherentBus::recvTimingResp(PacketPtr pkt, PortID master_port_id)
-{
- // determine the source port based on the id
- MasterPort *src_port = masterPorts[master_port_id];
-
- // determine the destination based on what is stored in the packet
- PortID slave_port_id = pkt->getDest();
-
- // test if the bus should be considered occupied for the current
- // port
- if (!respLayers[slave_port_id]->tryTiming(src_port)) {
- DPRINTF(CoherentBus, "recvTimingResp: src %s %s 0x%x BUSY\n",
- src_port->name(), pkt->cmdString(), pkt->getAddr());
- return false;
- }
-
- DPRINTF(CoherentBus, "recvTimingResp: src %s %s 0x%x\n",
- src_port->name(), pkt->cmdString(), pkt->getAddr());
-
- // store size and command as they might be modified when
- // forwarding the packet
- unsigned int pkt_size = pkt->hasData() ? pkt->getSize() : 0;
- unsigned int pkt_cmd = pkt->cmdToIndex();
-
- calcPacketTiming(pkt);
- Tick packetFinishTime = pkt->busLastWordDelay + curTick();
-
- // 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());
-
- if (snoopFilter && !pkt->req->isUncacheable() && !system->bypassCaches()) {
- // let the snoop filter inspect the response and update its state
- snoopFilter->updateResponse(pkt, *slavePorts[slave_port_id]);
- }
-
- // remove it as outstanding
- outstandingReq.erase(pkt->req);
-
- // send the packet through the destination slave port
- bool success M5_VAR_USED = slavePorts[slave_port_id]->sendTimingResp(pkt);
-
- // currently it is illegal to block responses... can lead to
- // deadlock
- assert(success);
-
- respLayers[slave_port_id]->succeededTiming(packetFinishTime);
-
- // stats updates
- dataThroughBus += pkt_size;
- pktCount[slave_port_id][master_port_id]++;
- totPktSize[slave_port_id][master_port_id] += pkt_size;
- transDist[pkt_cmd]++;
-
- return true;
-}
-
-void
-CoherentBus::recvTimingSnoopReq(PacketPtr pkt, PortID master_port_id)
-{
- DPRINTF(CoherentBus, "recvTimingSnoopReq: src %s %s 0x%x\n",
- masterPorts[master_port_id]->name(), pkt->cmdString(),
- pkt->getAddr());
-
- // update stats here as we know the forwarding will succeed
- transDist[pkt->cmdToIndex()]++;
- snoopDataThroughBus += pkt->hasData() ? pkt->getSize() : 0;
- snoopsThroughBus++;
-
- // we should only see express snoops from caches
- assert(pkt->isExpressSnoop());
-
- // set the source port for routing of the response
- pkt->setSrc(master_port_id);
-
- if (snoopFilter) {
- // let the Snoop Filter work its magic and guide probing
- auto sf_res = snoopFilter->lookupSnoop(pkt);
- // No timing here: packetFinishTime += sf_res.second * clockPeriod();
- DPRINTF(CoherentBus, "recvTimingSnoopReq: src %s %s 0x%x"\
- " SF size: %i lat: %i\n", masterPorts[master_port_id]->name(),
- pkt->cmdString(), pkt->getAddr(), sf_res.first.size(),
- sf_res.second);
-
- // forward to all snoopers
- forwardTiming(pkt, InvalidPortID, sf_res.first);
- } else {
- forwardTiming(pkt, InvalidPortID);
- }
-
- // 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(master_port_id == findPort(pkt->getAddr()));
-}
-
-bool
-CoherentBus::recvTimingSnoopResp(PacketPtr pkt, PortID slave_port_id)
-{
- // determine the source port based on the id
- SlavePort* src_port = slavePorts[slave_port_id];
-
- // get the destination from the packet
- PortID dest_port_id = pkt->getDest();
-
- // 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
- bool forwardAsSnoop = outstandingReq.find(pkt->req) ==
- outstandingReq.end();
-
- // test if the bus should be considered occupied for the current
- // port, note that the check is bypassed if the response is being
- // passed on as a normal response since this is occupying the
- // response layer rather than the snoop response layer
- if (forwardAsSnoop) {
- if (!snoopLayers[dest_port_id]->tryTiming(src_port)) {
- DPRINTF(CoherentBus, "recvTimingSnoopResp: src %s %s 0x%x BUSY\n",
- src_port->name(), pkt->cmdString(), pkt->getAddr());
- return false;
- }
- } else {
- // get the master port that mirrors this slave port internally
- MasterPort* snoop_port = snoopRespPorts[slave_port_id];
- if (!respLayers[dest_port_id]->tryTiming(snoop_port)) {
- DPRINTF(CoherentBus, "recvTimingSnoopResp: src %s %s 0x%x BUSY\n",
- snoop_port->name(), pkt->cmdString(), pkt->getAddr());
- return false;
- }
- }
-
- DPRINTF(CoherentBus, "recvTimingSnoopResp: src %s %s 0x%x\n",
- src_port->name(), pkt->cmdString(), pkt->getAddr());
-
- // store size and command as they might be modified when
- // forwarding the packet
- unsigned int pkt_size = pkt->hasData() ? pkt->getSize() : 0;
- unsigned int pkt_cmd = pkt->cmdToIndex();
-
- // responses are never express snoops
- assert(!pkt->isExpressSnoop());
-
- calcPacketTiming(pkt);
- Tick packetFinishTime = pkt->busLastWordDelay + curTick();
-
- // forward it either as a snoop response or a normal response
- if (forwardAsSnoop) {
- // this is a snoop response to a snoop request we forwarded,
- // e.g. coming from the L1 and going to the L2, and it should
- // be forwarded as a snoop response
-
- if (snoopFilter) {
- // update the probe filter so that it can properly track the line
- snoopFilter->updateSnoopForward(pkt, *slavePorts[slave_port_id],
- *masterPorts[dest_port_id]);
- }
-
- bool success M5_VAR_USED =
- masterPorts[dest_port_id]->sendTimingSnoopResp(pkt);
- pktCount[slave_port_id][dest_port_id]++;
- totPktSize[slave_port_id][dest_port_id] += pkt_size;
- assert(success);
-
- snoopLayers[dest_port_id]->succeededTiming(packetFinishTime);
- } 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(slave_port_id != dest_port_id);
-
- if (snoopFilter) {
- // update the probe filter so that it can properly track the line
- snoopFilter->updateSnoopResponse(pkt, *slavePorts[slave_port_id],
- *slavePorts[dest_port_id]);
- }
-
- DPRINTF(CoherentBus, "recvTimingSnoopResp: src %s %s 0x%x"\
- " FWD RESP\n", src_port->name(), pkt->cmdString(),
- pkt->getAddr());
-
- // as a normal response, it should go back to a master through
- // one of our slave ports, at this point we are ignoring the
- // fact that the response layer could be busy and do not touch
- // its state
- bool success M5_VAR_USED =
- slavePorts[dest_port_id]->sendTimingResp(pkt);
-
- // @todo Put the response in an internal FIFO and pass it on
- // to the response layer from there
-
- // currently it is illegal to block responses... can lead
- // to deadlock
- assert(success);
-
- respLayers[dest_port_id]->succeededTiming(packetFinishTime);
- }
-
- // stats updates
- transDist[pkt_cmd]++;
- snoopDataThroughBus += pkt_size;
- snoopsThroughBus++;
-
- return true;
-}
-
-
-void
-CoherentBus::forwardTiming(PacketPtr pkt, PortID exclude_slave_port_id,
- const std::vector<SlavePort*>& dests)
-{
- DPRINTF(CoherentBus, "%s for %s address %x size %d\n", __func__,
- pkt->cmdString(), pkt->getAddr(), pkt->getSize());
-
- // snoops should only happen if the system isn't bypassing caches
- assert(!system->bypassCaches());
-
- unsigned fanout = 0;
-
- for (SlavePortConstIter s = dests.begin(); s != dests.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 == InvalidPortID ||
- p->getId() != exclude_slave_port_id) {
- // cache is not allowed to refuse snoop
- p->sendTimingSnoopReq(pkt);
- fanout++;
- }
- }
-
- // Stats for fanout of this forward operation
- snoopFanout.sample(fanout);
-}
-
-void
-CoherentBus::recvRetry(PortID master_port_id)
-{
- // responses and snoop responses never block on forwarding them,
- // so the retry will always be coming from a port to which we
- // tried to forward a request
- reqLayers[master_port_id]->recvRetry();
-}
-
-Tick
-CoherentBus::recvAtomic(PacketPtr pkt, PortID slave_port_id)
-{
- DPRINTF(CoherentBus, "recvAtomic: packet src %s addr 0x%x cmd %s\n",
- slavePorts[slave_port_id]->name(), pkt->getAddr(),
- pkt->cmdString());
-
- // add the request data
- dataThroughBus += pkt->hasData() ? pkt->getSize() : 0;
-
- MemCmd snoop_response_cmd = MemCmd::InvalidCmd;
- Tick snoop_response_latency = 0;
-
- // uncacheable requests need never be snooped
- if (!pkt->req->isUncacheable() && !system->bypassCaches()) {
- // forward to all snoopers but the source
- std::pair<MemCmd, Tick> snoop_result;
- if (snoopFilter) {
- // check with the snoop filter where to forward this packet
- auto sf_res =
- snoopFilter->lookupRequest(pkt, *slavePorts[slave_port_id]);
- snoop_response_latency += sf_res.second * clockPeriod();
- DPRINTF(CoherentBus, "%s: src %s %s 0x%x"\
- " SF size: %i lat: %i\n", __func__,
- slavePorts[slave_port_id]->name(), pkt->cmdString(),
- pkt->getAddr(), sf_res.first.size(), sf_res.second);
- snoop_result = forwardAtomic(pkt, slave_port_id, InvalidPortID,
- sf_res.first);
- } else {
- snoop_result = forwardAtomic(pkt, slave_port_id);
- }
- snoop_response_cmd = snoop_result.first;
- 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
- PortID dest_id = findPort(pkt->getAddr());
-
- // forward the request to the appropriate destination
- Tick response_latency = masterPorts[dest_id]->sendAtomic(pkt);
-
- // Lower levels have replied, tell the snoop filter
- if (snoopFilter && !pkt->req->isUncacheable() && !system->bypassCaches() &&
- pkt->isResponse()) {
- snoopFilter->updateResponse(pkt, *slavePorts[slave_port_id]);
- }
-
- // 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;
- }
-
- // add the response data
- if (pkt->isResponse())
- dataThroughBus += pkt->hasData() ? pkt->getSize() : 0;
-
- // @todo: Not setting first-word time
- pkt->busLastWordDelay = response_latency;
- return response_latency;
-}
-
-Tick
-CoherentBus::recvAtomicSnoop(PacketPtr pkt, PortID master_port_id)
-{
- DPRINTF(CoherentBus, "recvAtomicSnoop: packet src %s addr 0x%x cmd %s\n",
- masterPorts[master_port_id]->name(), pkt->getAddr(),
- pkt->cmdString());
-
- // add the request snoop data
- snoopDataThroughBus += pkt->hasData() ? pkt->getSize() : 0;
- snoopsThroughBus++;
-
- // forward to all snoopers
- std::pair<MemCmd, Tick> snoop_result;
- Tick snoop_response_latency = 0;
- if (snoopFilter) {
- auto sf_res = snoopFilter->lookupSnoop(pkt);
- snoop_response_latency += sf_res.second * clockPeriod();
- DPRINTF(CoherentBus, "%s: src %s %s 0x%x SF size: %i lat: %i\n",
- __func__, masterPorts[master_port_id]->name(), pkt->cmdString(),
- pkt->getAddr(), sf_res.first.size(), sf_res.second);
- snoop_result = forwardAtomic(pkt, InvalidPortID, master_port_id,
- sf_res.first);
- } else {
- snoop_result = forwardAtomic(pkt, InvalidPortID);
- }
- MemCmd snoop_response_cmd = snoop_result.first;
- snoop_response_latency += snoop_result.second;
-
- if (snoop_response_cmd != MemCmd::InvalidCmd)
- pkt->cmd = snoop_response_cmd;
-
- // add the response snoop data
- if (pkt->isResponse()) {
- snoopDataThroughBus += pkt->hasData() ? pkt->getSize() : 0;
- snoopsThroughBus++;
- }
-
- // @todo: Not setting first-word time
- pkt->busLastWordDelay = snoop_response_latency;
- return snoop_response_latency;
-}
-
-std::pair<MemCmd, Tick>
-CoherentBus::forwardAtomic(PacketPtr pkt, PortID exclude_slave_port_id,
- PortID source_master_port_id,
- const std::vector<SlavePort*>& dests)
-{
- // the packet may be changed on snoops, record the original
- // command to enable us to restore it between snoops so that
- // additional snoops can take place properly
- MemCmd orig_cmd = pkt->cmd;
- MemCmd snoop_response_cmd = MemCmd::InvalidCmd;
- Tick snoop_response_latency = 0;
-
- // snoops should only happen if the system isn't bypassing caches
- assert(!system->bypassCaches());
-
- unsigned fanout = 0;
-
- for (SlavePortConstIter s = dests.begin(); s != dests.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 != InvalidPortID &&
- p->getId() == exclude_slave_port_id)
- continue;
-
- Tick latency = p->sendAtomicSnoop(pkt);
- fanout++;
-
- // 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())
- continue;
-
- // 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;
-
- if (snoopFilter) {
- // Handle responses by the snoopers and differentiate between
- // responses to requests from above and snoops from below
- if (source_master_port_id != InvalidPortID) {
- // Getting a response for a snoop from below
- assert(exclude_slave_port_id == InvalidPortID);
- snoopFilter->updateSnoopForward(pkt, *p,
- *masterPorts[source_master_port_id]);
- } else {
- // Getting a response for a request from above
- assert(source_master_port_id == InvalidPortID);
- snoopFilter->updateSnoopResponse(pkt, *p,
- *slavePorts[exclude_slave_port_id]);
- }
- }
- // restore original packet state for remaining snoopers
- pkt->cmd = orig_cmd;
- }
-
- // Stats for fanout
- snoopFanout.sample(fanout);
-
- // 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
-CoherentBus::recvFunctional(PacketPtr pkt, PortID slave_port_id)
-{
- if (!pkt->isPrint()) {
- // don't do DPRINTFs on PrintReq as it clutters up the output
- DPRINTF(CoherentBus,
- "recvFunctional: packet src %s addr 0x%x cmd %s\n",
- slavePorts[slave_port_id]->name(), pkt->getAddr(),
- pkt->cmdString());
- }
-
- // uncacheable requests need never be snooped
- if (!pkt->req->isUncacheable() && !system->bypassCaches()) {
- // forward to all snoopers but the source
- forwardFunctional(pkt, slave_port_id);
- }
-
- // 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()) {
- PortID dest_id = findPort(pkt->getAddr());
-
- masterPorts[dest_id]->sendFunctional(pkt);
- }
-}
-
-void
-CoherentBus::recvFunctionalSnoop(PacketPtr pkt, PortID master_port_id)
-{
- if (!pkt->isPrint()) {
- // don't do DPRINTFs on PrintReq as it clutters up the output
- DPRINTF(CoherentBus,
- "recvFunctionalSnoop: packet src %s addr 0x%x cmd %s\n",
- masterPorts[master_port_id]->name(), pkt->getAddr(),
- pkt->cmdString());
- }
-
- // forward to all snoopers
- forwardFunctional(pkt, InvalidPortID);
-}
-
-void
-CoherentBus::forwardFunctional(PacketPtr pkt, PortID exclude_slave_port_id)
-{
- // snoops should only happen if the system isn't bypassing caches
- assert(!system->bypassCaches());
-
- 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 == InvalidPortID ||
- p->getId() != exclude_slave_port_id)
- p->sendFunctionalSnoop(pkt);
-
- // if we get a response we are done
- if (pkt->isResponse()) {
- break;
- }
- }
-}
-
-unsigned int
-CoherentBus::drain(DrainManager *dm)
-{
- // sum up the individual layers
- unsigned int total = 0;
- for (auto l = reqLayers.begin(); l != reqLayers.end(); ++l)
- total += (*l)->drain(dm);
- for (auto l = respLayers.begin(); l != respLayers.end(); ++l)
- total += (*l)->drain(dm);
- for (auto l = snoopLayers.begin(); l != snoopLayers.end(); ++l)
- total += (*l)->drain(dm);
- return total;
-}
-
-void
-CoherentBus::regStats()
-{
- // register the stats of the base class and our three bus layers
- BaseBus::regStats();
- for (auto l = reqLayers.begin(); l != reqLayers.end(); ++l)
- (*l)->regStats();
- for (auto l = respLayers.begin(); l != respLayers.end(); ++l)
- (*l)->regStats();
- for (auto l = snoopLayers.begin(); l != snoopLayers.end(); ++l)
- (*l)->regStats();
-
- dataThroughBus
- .name(name() + ".data_through_bus")
- .desc("Total data (bytes)")
- ;
-
- snoopDataThroughBus
- .name(name() + ".snoop_data_through_bus")
- .desc("Total snoop data (bytes)")
- ;
-
- snoopsThroughBus
- .name(name() + ".snoops_through_bus")
- .desc("Total snoops (count)")
- ;
-
- snoopFanout
- .init(0, snoopPorts.size(), 1)
- .name(name() + ".snoop_fanout")
- .desc("Request fanout histogram")
- ;
-
- throughput
- .name(name() + ".throughput")
- .desc("Throughput (bytes/s)")
- .precision(0)
- ;
-
- throughput = (dataThroughBus + snoopDataThroughBus) / simSeconds;
-}
-
-CoherentBus *
-CoherentBusParams::create()
-{
- return new CoherentBus(this);
-}
+++ /dev/null
-/*
- * Copyright (c) 2011-2013 ARM Limited
- * All rights reserved
- *
- * The license below extends only to copyright in the software and shall
- * not be construed as granting a license to any other intellectual
- * property including but not limited to intellectual property relating
- * to a hardware implementation of the functionality of the software
- * licensed hereunder. You may use the software subject to the license
- * terms below provided that you ensure that this notice is replicated
- * unmodified and in its entirety in all distributions of the software,
- * modified or unmodified, in source code or in binary form.
- *
- * Copyright (c) 2002-2005 The Regents of The University of Michigan
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are
- * met: redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer;
- * redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution;
- * neither the name of the copyright holders nor the names of its
- * contributors may be used to endorse or promote products derived from
- * this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * 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: Ron Dreslinski
- * Ali Saidi
- * Andreas Hansson
- * William Wang
- */
-
-/**
- * @file
- * Declaration of a coherent bus.
- */
-
-#ifndef __MEM_COHERENT_BUS_HH__
-#define __MEM_COHERENT_BUS_HH__
-
-#include "base/hashmap.hh"
-#include "mem/bus.hh"
-#include "mem/snoop_filter.hh"
-#include "params/CoherentBus.hh"
-
-/**
- * A coherent bus connects a number of (potentially) snooping masters
- * and slaves, and routes the request and response packets based on
- * the address, and also forwards all requests to the snoopers and
- * deals with the snoop responses.
- *
- * The coherent bus can be used as a template for modelling QPI,
-* HyperTransport, ACE and coherent OCP buses, and is typically used
- * for the L1-to-L2 buses and as the main system interconnect.
- * @sa \ref gem5MemorySystem "gem5 Memory System"
- */
-class CoherentBus : public BaseBus
-{
-
- protected:
-
- /**
- * Declare the layers of this bus, one vector for requests, one
- * for responses, and one for snoop responses
- */
- typedef Layer<SlavePort,MasterPort> ReqLayer;
- typedef Layer<MasterPort,SlavePort> RespLayer;
- typedef Layer<SlavePort,MasterPort> SnoopLayer;
- std::vector<ReqLayer*> reqLayers;
- std::vector<RespLayer*> respLayers;
- std::vector<SnoopLayer*> snoopLayers;
-
- /**
- * Declaration of the coherent bus slave port type, one will be
- * instantiated for each of the master ports connecting to the
- * bus.
- */
- class CoherentBusSlavePort : public SlavePort
- {
-
- private:
-
- /** A reference to the bus to which this port belongs. */
- CoherentBus &bus;
-
- public:
-
- CoherentBusSlavePort(const std::string &_name,
- CoherentBus &_bus, PortID _id)
- : SlavePort(_name, &_bus, _id), bus(_bus)
- { }
-
- protected:
-
- /**
- * When receiving a timing request, pass it to the bus.
- */
- virtual bool recvTimingReq(PacketPtr pkt)
- { return bus.recvTimingReq(pkt, id); }
-
- /**
- * When receiving a timing snoop response, pass it to the bus.
- */
- virtual bool recvTimingSnoopResp(PacketPtr pkt)
- { return bus.recvTimingSnoopResp(pkt, id); }
-
- /**
- * When receiving an atomic request, pass it to the bus.
- */
- virtual Tick recvAtomic(PacketPtr pkt)
- { return bus.recvAtomic(pkt, id); }
-
- /**
- * When receiving a functional request, pass it to the bus.
- */
- virtual void recvFunctional(PacketPtr pkt)
- { bus.recvFunctional(pkt, id); }
-
- /**
- * When receiving a retry, pass it to the bus.
- */
- virtual void recvRetry()
- { panic("Bus slave ports always succeed and should never retry.\n"); }
-
- /**
- * Return the union of all adress ranges seen by this bus.
- */
- virtual AddrRangeList getAddrRanges() const
- { return bus.getAddrRanges(); }
-
- };
-
- /**
- * Declaration of the coherent bus master port type, one will be
- * instantiated for each of the slave interfaces connecting to the
- * bus.
- */
- class CoherentBusMasterPort : public MasterPort
- {
- private:
- /** A reference to the bus to which this port belongs. */
- CoherentBus &bus;
-
- public:
-
- CoherentBusMasterPort(const std::string &_name,
- CoherentBus &_bus, PortID _id)
- : MasterPort(_name, &_bus, _id), bus(_bus)
- { }
-
- protected:
-
- /**
- * Determine if this port should be considered a snooper. For
- * a coherent bus master port this is always true.
- *
- * @return a boolean that is true if this port is snooping
- */
- virtual bool isSnooping() const
- { return true; }
-
- /**
- * When receiving a timing response, pass it to the bus.
- */
- virtual bool recvTimingResp(PacketPtr pkt)
- { return bus.recvTimingResp(pkt, id); }
-
- /**
- * When receiving a timing snoop request, pass it to the bus.
- */
- virtual void recvTimingSnoopReq(PacketPtr pkt)
- { return bus.recvTimingSnoopReq(pkt, id); }
-
- /**
- * When receiving an atomic snoop request, pass it to the bus.
- */
- virtual Tick recvAtomicSnoop(PacketPtr pkt)
- { return bus.recvAtomicSnoop(pkt, id); }
-
- /**
- * When receiving a functional snoop request, pass it to the bus.
- */
- virtual void recvFunctionalSnoop(PacketPtr pkt)
- { bus.recvFunctionalSnoop(pkt, id); }
-
- /** When reciving a range change from the peer port (at id),
- pass it to the bus. */
- virtual void recvRangeChange()
- { bus.recvRangeChange(id); }
-
- /** When reciving a retry from the peer port (at id),
- pass it to the bus. */
- virtual void recvRetry()
- { bus.recvRetry(id); }
-
- };
-
- /**
- * Internal class to bridge between an incoming snoop response
- * from a slave port and forwarding it through an outgoing slave
- * port. It is effectively a dangling master port.
- */
- class SnoopRespPort : public MasterPort
- {
-
- private:
-
- /** The port which we mirror internally. */
- SlavePort& slavePort;
-
- public:
-
- /**
- * Create a snoop response port that mirrors a given slave port.
- */
- SnoopRespPort(SlavePort& slave_port, CoherentBus& _bus) :
- MasterPort(slave_port.name() + ".snoopRespPort", &_bus),
- slavePort(slave_port) { }
-
- /**
- * Override the sending of retries and pass them on through
- * the mirrored slave port.
- */
- void sendRetry() {
- slavePort.sendRetry();
- }
-
- /**
- * Provided as necessary.
- */
- void recvRetry() { panic("SnoopRespPort should never see retry\n"); }
-
- /**
- * Provided as necessary.
- */
- bool recvTimingResp(PacketPtr pkt)
- {
- panic("SnoopRespPort should never see timing response\n");
- return false;
- }
-
- };
-
- std::vector<SnoopRespPort*> snoopRespPorts;
-
- std::vector<SlavePort*> snoopPorts;
-
- /**
- * Store the outstanding requests so we can determine which ones
- * we generated and which ones were merely forwarded. This is used
- * in the coherent bus when coherency responses come back.
- */
- m5::hash_set<RequestPtr> outstandingReq;
-
- /**
- * Keep a pointer to the system to be allow to querying memory system
- * properties.
- */
- System *system;
-
- /** A snoop filter that tracks cache line residency and can restrict the
- * broadcast needed for probes. NULL denotes an absent filter. */
- SnoopFilter *snoopFilter;
-
- /** Function called by the port when the bus is recieving a Timing
- request packet.*/
- bool recvTimingReq(PacketPtr pkt, PortID slave_port_id);
-
- /** Function called by the port when the bus is recieving a Timing
- response packet.*/
- bool recvTimingResp(PacketPtr pkt, PortID master_port_id);
-
- /** Function called by the port when the bus is recieving a timing
- snoop request.*/
- void recvTimingSnoopReq(PacketPtr pkt, PortID master_port_id);
-
- /** Function called by the port when the bus is recieving a timing
- snoop response.*/
- bool recvTimingSnoopResp(PacketPtr pkt, PortID slave_port_id);
-
- /** Timing function called by port when it is once again able to process
- * requests. */
- void recvRetry(PortID master_port_id);
-
- /**
- * Forward a timing packet to our snoopers, potentially excluding
- * one of the connected coherent masters to avoid sending a packet
- * back to where it came from.
- *
- * @param pkt Packet to forward
- * @param exclude_slave_port_id Id of slave port to exclude
- */
- void forwardTiming(PacketPtr pkt, PortID exclude_slave_port_id) {
- forwardTiming(pkt, exclude_slave_port_id, snoopPorts);
- }
-
- /**
- * Forward a timing packet to a selected list of snoopers, potentially
- * excluding one of the connected coherent masters to avoid sending a packet
- * back to where it came from.
- *
- * @param pkt Packet to forward
- * @param exclude_slave_port_id Id of slave port to exclude
- * @param dests Vector of destination ports for the forwarded pkt
- */
- void forwardTiming(PacketPtr pkt, PortID exclude_slave_port_id,
- const std::vector<SlavePort*>& dests);
-
- /** Function called by the port when the bus is recieving a Atomic
- transaction.*/
- Tick recvAtomic(PacketPtr pkt, PortID slave_port_id);
-
- /** Function called by the port when the bus is recieving an
- atomic snoop transaction.*/
- Tick recvAtomicSnoop(PacketPtr pkt, PortID master_port_id);
-
- /**
- * Forward an atomic packet to our snoopers, potentially excluding
- * one of the connected coherent masters to avoid sending a packet
- * back to where it came from.
- *
- * @param pkt Packet to forward
- * @param exclude_slave_port_id Id of slave port to exclude
- *
- * @return a pair containing the snoop response and snoop latency
- */
- std::pair<MemCmd, Tick> forwardAtomic(PacketPtr pkt,
- PortID exclude_slave_port_id)
- {
- return forwardAtomic(pkt, exclude_slave_port_id, InvalidPortID, snoopPorts);
- }
-
- /**
- * Forward an atomic packet to a selected list of snoopers, potentially
- * excluding one of the connected coherent masters to avoid sending a packet
- * back to where it came from.
- *
- * @param pkt Packet to forward
- * @param exclude_slave_port_id Id of slave port to exclude
- * @param source_master_port_id Id of the master port for snoops from below
- * @param dests Vector of destination ports for the forwarded pkt
- *
- * @return a pair containing the snoop response and snoop latency
- */
- std::pair<MemCmd, Tick> forwardAtomic(PacketPtr pkt,
- PortID exclude_slave_port_id,
- PortID source_master_port_id,
- const std::vector<SlavePort*>& dests);
-
- /** Function called by the port when the bus is recieving a Functional
- transaction.*/
- void recvFunctional(PacketPtr pkt, PortID slave_port_id);
-
- /** Function called by the port when the bus is recieving a functional
- snoop transaction.*/
- void recvFunctionalSnoop(PacketPtr pkt, PortID master_port_id);
-
- /**
- * Forward a functional packet to our snoopers, potentially
- * excluding one of the connected coherent masters to avoid
- * sending a packet back to where it came from.
- *
- * @param pkt Packet to forward
- * @param exclude_slave_port_id Id of slave port to exclude
- */
- void forwardFunctional(PacketPtr pkt, PortID exclude_slave_port_id);
-
- Stats::Scalar dataThroughBus;
- Stats::Scalar snoopDataThroughBus;
- Stats::Scalar snoopsThroughBus;
- Stats::Distribution snoopFanout;
-
- public:
-
- virtual void init();
-
- CoherentBus(const CoherentBusParams *p);
-
- virtual ~CoherentBus();
-
- unsigned int drain(DrainManager *dm);
-
- virtual void regStats();
-};
-
-#endif //__MEM_COHERENT_BUS_HH__
--- /dev/null
+/*
+ * Copyright (c) 2011-2014 ARM Limited
+ * All rights reserved
+ *
+ * The license below extends only to copyright in the software and shall
+ * not be construed as granting a license to any other intellectual
+ * property including but not limited to intellectual property relating
+ * to a hardware implementation of the functionality of the software
+ * licensed hereunder. You may use the software subject to the license
+ * terms below provided that you ensure that this notice is replicated
+ * unmodified and in its entirety in all distributions of the software,
+ * modified or unmodified, in source code or in binary form.
+ *
+ * Copyright (c) 2006 The Regents of The University of Michigan
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met: redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer;
+ * redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution;
+ * neither the name of the copyright holders nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * 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
+ */
+
+/**
+ * @file
+ * Definition of a crossbar object.
+ */
+
+#include "base/misc.hh"
+#include "base/trace.hh"
+#include "debug/AddrRanges.hh"
+#include "debug/CoherentXBar.hh"
+#include "mem/coherent_xbar.hh"
+#include "sim/system.hh"
+
+CoherentXBar::CoherentXBar(const CoherentXBarParams *p)
+ : BaseXBar(p), system(p->system), snoopFilter(p->snoop_filter)
+{
+ // 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.master[%d]", name(), i);
+ MasterPort* bp = new CoherentXBarMasterPort(portName, *this, i);
+ masterPorts.push_back(bp);
+ reqLayers.push_back(new ReqLayer(*bp, *this,
+ csprintf(".reqLayer%d", i)));
+ snoopLayers.push_back(new SnoopLayer(*bp, *this,
+ csprintf(".snoopLayer%d", i)));
+ }
+
+ // 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 = name() + ".default";
+ MasterPort* bp = new CoherentXBarMasterPort(portName, *this,
+ defaultPortID);
+ masterPorts.push_back(bp);
+ reqLayers.push_back(new ReqLayer(*bp, *this, csprintf(".reqLayer%d",
+ defaultPortID)));
+ snoopLayers.push_back(new SnoopLayer(*bp, *this,
+ csprintf(".snoopLayer%d",
+ defaultPortID)));
+ }
+
+ // 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.slave[%d]", name(), i);
+ SlavePort* bp = new CoherentXBarSlavePort(portName, *this, i);
+ slavePorts.push_back(bp);
+ respLayers.push_back(new RespLayer(*bp, *this,
+ csprintf(".respLayer%d", i)));
+ snoopRespPorts.push_back(new SnoopRespPort(*bp, *this));
+ }
+
+ if (snoopFilter)
+ snoopFilter->setSlavePorts(slavePorts);
+
+ clearPortCache();
+}
+
+CoherentXBar::~CoherentXBar()
+{
+ for (auto l: reqLayers)
+ delete l;
+ for (auto l: respLayers)
+ delete l;
+ for (auto l: snoopLayers)
+ delete l;
+ for (auto p: snoopRespPorts)
+ delete p;
+}
+
+void
+CoherentXBar::init()
+{
+ // the base class is responsible for determining the block size
+ BaseXBar::init();
+
+ // iterate over our slave ports and determine which of our
+ // neighbouring master ports are snooping and add them as snoopers
+ for (const auto& p: slavePorts) {
+ // check if the connected master port is snooping
+ if (p->isSnooping()) {
+ DPRINTF(AddrRanges, "Adding snooping master %s\n",
+ p->getMasterPort().name());
+ snoopPorts.push_back(p);
+ }
+ }
+
+ if (snoopPorts.empty())
+ warn("CoherentXBar %s has no snooping ports attached!\n", name());
+}
+
+bool
+CoherentXBar::recvTimingReq(PacketPtr pkt, PortID slave_port_id)
+{
+ // 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();
+
+ // determine the destination based on the address
+ PortID master_port_id = findPort(pkt->getAddr());
+
+ // test if the crossbar should be considered occupied for the current
+ // port, and exclude express snoops from the check
+ if (!is_express_snoop && !reqLayers[master_port_id]->tryTiming(src_port)) {
+ DPRINTF(CoherentXBar, "recvTimingReq: src %s %s 0x%x BUSY\n",
+ src_port->name(), pkt->cmdString(), pkt->getAddr());
+ return false;
+ }
+
+ DPRINTF(CoherentXBar, "recvTimingReq: src %s %s expr %d 0x%x\n",
+ src_port->name(), pkt->cmdString(), is_express_snoop,
+ pkt->getAddr());
+
+ // store size and command as they might be modified when
+ // forwarding the packet
+ unsigned int pkt_size = pkt->hasData() ? pkt->getSize() : 0;
+ unsigned int pkt_cmd = pkt->cmdToIndex();
+
+ // set the source port for routing of the response
+ pkt->setSrc(slave_port_id);
+
+ calcPacketTiming(pkt);
+ Tick packetFinishTime = pkt->lastWordDelay + curTick();
+
+ // uncacheable requests need never be snooped
+ if (!pkt->req->isUncacheable() && !system->bypassCaches()) {
+ // the packet is a memory-mapped request and should be
+ // broadcasted to our snoopers but the source
+ if (snoopFilter) {
+ // check with the snoop filter where to forward this packet
+ auto sf_res = snoopFilter->lookupRequest(pkt, *src_port);
+ packetFinishTime += sf_res.second * clockPeriod();
+ DPRINTF(CoherentXBar, "recvTimingReq: src %s %s 0x%x"\
+ " SF size: %i lat: %i\n", src_port->name(),
+ pkt->cmdString(), pkt->getAddr(), sf_res.first.size(),
+ sf_res.second);
+ forwardTiming(pkt, slave_port_id, sf_res.first);
+ } else {
+ forwardTiming(pkt, slave_port_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 = !is_express_snoop && 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);
+ }
+
+ // Note: Cannot create a copy of the full packet, here.
+ MemCmd orig_cmd(pkt->cmd);
+
+ // since it is a normal request, attempt to send the packet
+ bool success = masterPorts[master_port_id]->sendTimingReq(pkt);
+
+ if (snoopFilter && !pkt->req->isUncacheable()
+ && !system->bypassCaches()) {
+ // The packet may already be overwritten by the sendTimingReq function.
+ // The snoop filter needs to see the original request *and* the return
+ // status of the send operation, so we need to recreate the original
+ // request. Atomic mode does not have the issue, as there the send
+ // operation and the response happen instantaneously and don't need two
+ // phase tracking.
+ MemCmd tmp_cmd(pkt->cmd);
+ pkt->cmd = orig_cmd;
+ // Let the snoop filter know about the success of the send operation
+ snoopFilter->updateRequest(pkt, *src_port, !success);
+ pkt->cmd = tmp_cmd;
+ }
+
+ // if this is an express snoop, we are done at this point
+ if (is_express_snoop) {
+ assert(success);
+ snoops++;
+ } 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);
+
+ // undo the calculation so we can check for 0 again
+ pkt->firstWordDelay = pkt->lastWordDelay = 0;
+
+ DPRINTF(CoherentXBar, "recvTimingReq: src %s %s 0x%x RETRY\n",
+ src_port->name(), pkt->cmdString(), pkt->getAddr());
+
+ // update the layer state and schedule an idle event
+ reqLayers[master_port_id]->failedTiming(src_port,
+ clockEdge(headerCycles));
+ } else {
+ // update the layer state and schedule an idle event
+ reqLayers[master_port_id]->succeededTiming(packetFinishTime);
+ }
+ }
+
+ // stats updates only consider packets that were successfully sent
+ if (success) {
+ pktCount[slave_port_id][master_port_id]++;
+ pktSize[slave_port_id][master_port_id] += pkt_size;
+ transDist[pkt_cmd]++;
+ }
+
+ return success;
+}
+
+bool
+CoherentXBar::recvTimingResp(PacketPtr pkt, PortID master_port_id)
+{
+ // determine the source port based on the id
+ MasterPort *src_port = masterPorts[master_port_id];
+
+ // determine the destination based on what is stored in the packet
+ PortID slave_port_id = pkt->getDest();
+
+ // test if the crossbar should be considered occupied for the
+ // current port
+ if (!respLayers[slave_port_id]->tryTiming(src_port)) {
+ DPRINTF(CoherentXBar, "recvTimingResp: src %s %s 0x%x BUSY\n",
+ src_port->name(), pkt->cmdString(), pkt->getAddr());
+ return false;
+ }
+
+ DPRINTF(CoherentXBar, "recvTimingResp: src %s %s 0x%x\n",
+ src_port->name(), pkt->cmdString(), pkt->getAddr());
+
+ // store size and command as they might be modified when
+ // forwarding the packet
+ unsigned int pkt_size = pkt->hasData() ? pkt->getSize() : 0;
+ unsigned int pkt_cmd = pkt->cmdToIndex();
+
+ calcPacketTiming(pkt);
+ Tick packetFinishTime = pkt->lastWordDelay + curTick();
+
+ // the packet is a normal response to a request that we should
+ // have seen passing through the crossbar
+ assert(outstandingReq.find(pkt->req) != outstandingReq.end());
+
+ if (snoopFilter && !pkt->req->isUncacheable() && !system->bypassCaches()) {
+ // let the snoop filter inspect the response and update its state
+ snoopFilter->updateResponse(pkt, *slavePorts[slave_port_id]);
+ }
+
+ // remove it as outstanding
+ outstandingReq.erase(pkt->req);
+
+ // send the packet through the destination slave port
+ bool success M5_VAR_USED = slavePorts[slave_port_id]->sendTimingResp(pkt);
+
+ // currently it is illegal to block responses... can lead to
+ // deadlock
+ assert(success);
+
+ respLayers[slave_port_id]->succeededTiming(packetFinishTime);
+
+ // stats updates
+ pktCount[slave_port_id][master_port_id]++;
+ pktSize[slave_port_id][master_port_id] += pkt_size;
+ transDist[pkt_cmd]++;
+
+ return true;
+}
+
+void
+CoherentXBar::recvTimingSnoopReq(PacketPtr pkt, PortID master_port_id)
+{
+ DPRINTF(CoherentXBar, "recvTimingSnoopReq: src %s %s 0x%x\n",
+ masterPorts[master_port_id]->name(), pkt->cmdString(),
+ pkt->getAddr());
+
+ // update stats here as we know the forwarding will succeed
+ transDist[pkt->cmdToIndex()]++;
+ snoops++;
+
+ // we should only see express snoops from caches
+ assert(pkt->isExpressSnoop());
+
+ // set the source port for routing of the response
+ pkt->setSrc(master_port_id);
+
+ if (snoopFilter) {
+ // let the Snoop Filter work its magic and guide probing
+ auto sf_res = snoopFilter->lookupSnoop(pkt);
+ // No timing here: packetFinishTime += sf_res.second * clockPeriod();
+ DPRINTF(CoherentXBar, "recvTimingSnoopReq: src %s %s 0x%x"\
+ " SF size: %i lat: %i\n", masterPorts[master_port_id]->name(),
+ pkt->cmdString(), pkt->getAddr(), sf_res.first.size(),
+ sf_res.second);
+
+ // forward to all snoopers
+ forwardTiming(pkt, InvalidPortID, sf_res.first);
+ } else {
+ forwardTiming(pkt, InvalidPortID);
+ }
+
+ // 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(master_port_id == findPort(pkt->getAddr()));
+}
+
+bool
+CoherentXBar::recvTimingSnoopResp(PacketPtr pkt, PortID slave_port_id)
+{
+ // determine the source port based on the id
+ SlavePort* src_port = slavePorts[slave_port_id];
+
+ // get the destination from the packet
+ PortID dest_port_id = pkt->getDest();
+
+ // 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
+ bool forwardAsSnoop = outstandingReq.find(pkt->req) ==
+ outstandingReq.end();
+
+ // test if the crossbar should be considered occupied for the
+ // current port, note that the check is bypassed if the response
+ // is being passed on as a normal response since this is occupying
+ // the response layer rather than the snoop response layer
+ if (forwardAsSnoop) {
+ if (!snoopLayers[dest_port_id]->tryTiming(src_port)) {
+ DPRINTF(CoherentXBar, "recvTimingSnoopResp: src %s %s 0x%x BUSY\n",
+ src_port->name(), pkt->cmdString(), pkt->getAddr());
+ return false;
+ }
+ } else {
+ // get the master port that mirrors this slave port internally
+ MasterPort* snoop_port = snoopRespPorts[slave_port_id];
+ if (!respLayers[dest_port_id]->tryTiming(snoop_port)) {
+ DPRINTF(CoherentXBar, "recvTimingSnoopResp: src %s %s 0x%x BUSY\n",
+ snoop_port->name(), pkt->cmdString(), pkt->getAddr());
+ return false;
+ }
+ }
+
+ DPRINTF(CoherentXBar, "recvTimingSnoopResp: src %s %s 0x%x\n",
+ src_port->name(), pkt->cmdString(), pkt->getAddr());
+
+ // store size and command as they might be modified when
+ // forwarding the packet
+ unsigned int pkt_size = pkt->hasData() ? pkt->getSize() : 0;
+ unsigned int pkt_cmd = pkt->cmdToIndex();
+
+ // responses are never express snoops
+ assert(!pkt->isExpressSnoop());
+
+ calcPacketTiming(pkt);
+ Tick packetFinishTime = pkt->lastWordDelay + curTick();
+
+ // forward it either as a snoop response or a normal response
+ if (forwardAsSnoop) {
+ // this is a snoop response to a snoop request we forwarded,
+ // e.g. coming from the L1 and going to the L2, and it should
+ // be forwarded as a snoop response
+
+ if (snoopFilter) {
+ // update the probe filter so that it can properly track the line
+ snoopFilter->updateSnoopForward(pkt, *slavePorts[slave_port_id],
+ *masterPorts[dest_port_id]);
+ }
+
+ bool success M5_VAR_USED =
+ masterPorts[dest_port_id]->sendTimingSnoopResp(pkt);
+ pktCount[slave_port_id][dest_port_id]++;
+ pktSize[slave_port_id][dest_port_id] += pkt_size;
+ assert(success);
+
+ snoopLayers[dest_port_id]->succeededTiming(packetFinishTime);
+ } else {
+ // we got a snoop response on one of our slave ports,
+ // i.e. from a coherent master connected to the crossbar, 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 crossbar 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(slave_port_id != dest_port_id);
+
+ if (snoopFilter) {
+ // update the probe filter so that it can properly track the line
+ snoopFilter->updateSnoopResponse(pkt, *slavePorts[slave_port_id],
+ *slavePorts[dest_port_id]);
+ }
+
+ DPRINTF(CoherentXBar, "recvTimingSnoopResp: src %s %s 0x%x"\
+ " FWD RESP\n", src_port->name(), pkt->cmdString(),
+ pkt->getAddr());
+
+ // as a normal response, it should go back to a master through
+ // one of our slave ports, at this point we are ignoring the
+ // fact that the response layer could be busy and do not touch
+ // its state
+ bool success M5_VAR_USED =
+ slavePorts[dest_port_id]->sendTimingResp(pkt);
+
+ // @todo Put the response in an internal FIFO and pass it on
+ // to the response layer from there
+
+ // currently it is illegal to block responses... can lead
+ // to deadlock
+ assert(success);
+
+ respLayers[dest_port_id]->succeededTiming(packetFinishTime);
+ }
+
+ // stats updates
+ transDist[pkt_cmd]++;
+ snoops++;
+
+ return true;
+}
+
+
+void
+CoherentXBar::forwardTiming(PacketPtr pkt, PortID exclude_slave_port_id,
+ const std::vector<SlavePort*>& dests)
+{
+ DPRINTF(CoherentXBar, "%s for %s address %x size %d\n", __func__,
+ pkt->cmdString(), pkt->getAddr(), pkt->getSize());
+
+ // snoops should only happen if the system isn't bypassing caches
+ assert(!system->bypassCaches());
+
+ unsigned fanout = 0;
+
+ for (const auto& p: dests) {
+ // 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 == InvalidPortID ||
+ p->getId() != exclude_slave_port_id) {
+ // cache is not allowed to refuse snoop
+ p->sendTimingSnoopReq(pkt);
+ fanout++;
+ }
+ }
+
+ // Stats for fanout of this forward operation
+ snoopFanout.sample(fanout);
+}
+
+void
+CoherentXBar::recvRetry(PortID master_port_id)
+{
+ // responses and snoop responses never block on forwarding them,
+ // so the retry will always be coming from a port to which we
+ // tried to forward a request
+ reqLayers[master_port_id]->recvRetry();
+}
+
+Tick
+CoherentXBar::recvAtomic(PacketPtr pkt, PortID slave_port_id)
+{
+ DPRINTF(CoherentXBar, "recvAtomic: packet src %s addr 0x%x cmd %s\n",
+ slavePorts[slave_port_id]->name(), pkt->getAddr(),
+ pkt->cmdString());
+
+ unsigned int pkt_size = pkt->hasData() ? pkt->getSize() : 0;
+ unsigned int pkt_cmd = pkt->cmdToIndex();
+
+ MemCmd snoop_response_cmd = MemCmd::InvalidCmd;
+ Tick snoop_response_latency = 0;
+
+ // uncacheable requests need never be snooped
+ if (!pkt->req->isUncacheable() && !system->bypassCaches()) {
+ // forward to all snoopers but the source
+ std::pair<MemCmd, Tick> snoop_result;
+ if (snoopFilter) {
+ // check with the snoop filter where to forward this packet
+ auto sf_res =
+ snoopFilter->lookupRequest(pkt, *slavePorts[slave_port_id]);
+ snoop_response_latency += sf_res.second * clockPeriod();
+ DPRINTF(CoherentXBar, "%s: src %s %s 0x%x"\
+ " SF size: %i lat: %i\n", __func__,
+ slavePorts[slave_port_id]->name(), pkt->cmdString(),
+ pkt->getAddr(), sf_res.first.size(), sf_res.second);
+ snoop_result = forwardAtomic(pkt, slave_port_id, InvalidPortID,
+ sf_res.first);
+ } else {
+ snoop_result = forwardAtomic(pkt, slave_port_id);
+ }
+ snoop_response_cmd = snoop_result.first;
+ 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
+ PortID master_port_id = findPort(pkt->getAddr());
+
+ // stats updates for the request
+ pktCount[slave_port_id][master_port_id]++;
+ pktSize[slave_port_id][master_port_id] += pkt_size;
+ transDist[pkt_cmd]++;
+
+ // forward the request to the appropriate destination
+ Tick response_latency = masterPorts[master_port_id]->sendAtomic(pkt);
+
+ // Lower levels have replied, tell the snoop filter
+ if (snoopFilter && !pkt->req->isUncacheable() && !system->bypassCaches() &&
+ pkt->isResponse()) {
+ snoopFilter->updateResponse(pkt, *slavePorts[slave_port_id]);
+ }
+
+ // 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;
+ }
+
+ // add the response data
+ if (pkt->isResponse()) {
+ pkt_size = pkt->hasData() ? pkt->getSize() : 0;
+ pkt_cmd = pkt->cmdToIndex();
+
+ // stats updates
+ pktCount[slave_port_id][master_port_id]++;
+ pktSize[slave_port_id][master_port_id] += pkt_size;
+ transDist[pkt_cmd]++;
+ }
+
+ // @todo: Not setting first-word time
+ pkt->lastWordDelay = response_latency;
+ return response_latency;
+}
+
+Tick
+CoherentXBar::recvAtomicSnoop(PacketPtr pkt, PortID master_port_id)
+{
+ DPRINTF(CoherentXBar, "recvAtomicSnoop: packet src %s addr 0x%x cmd %s\n",
+ masterPorts[master_port_id]->name(), pkt->getAddr(),
+ pkt->cmdString());
+
+ // add the request snoop data
+ snoops++;
+
+ // forward to all snoopers
+ std::pair<MemCmd, Tick> snoop_result;
+ Tick snoop_response_latency = 0;
+ if (snoopFilter) {
+ auto sf_res = snoopFilter->lookupSnoop(pkt);
+ snoop_response_latency += sf_res.second * clockPeriod();
+ DPRINTF(CoherentXBar, "%s: src %s %s 0x%x SF size: %i lat: %i\n",
+ __func__, masterPorts[master_port_id]->name(), pkt->cmdString(),
+ pkt->getAddr(), sf_res.first.size(), sf_res.second);
+ snoop_result = forwardAtomic(pkt, InvalidPortID, master_port_id,
+ sf_res.first);
+ } else {
+ snoop_result = forwardAtomic(pkt, InvalidPortID);
+ }
+ MemCmd snoop_response_cmd = snoop_result.first;
+ snoop_response_latency += snoop_result.second;
+
+ if (snoop_response_cmd != MemCmd::InvalidCmd)
+ pkt->cmd = snoop_response_cmd;
+
+ // add the response snoop data
+ if (pkt->isResponse()) {
+ snoops++;
+ }
+
+ // @todo: Not setting first-word time
+ pkt->lastWordDelay = snoop_response_latency;
+ return snoop_response_latency;
+}
+
+std::pair<MemCmd, Tick>
+CoherentXBar::forwardAtomic(PacketPtr pkt, PortID exclude_slave_port_id,
+ PortID source_master_port_id,
+ const std::vector<SlavePort*>& dests)
+{
+ // the packet may be changed on snoops, record the original
+ // command to enable us to restore it between snoops so that
+ // additional snoops can take place properly
+ MemCmd orig_cmd = pkt->cmd;
+ MemCmd snoop_response_cmd = MemCmd::InvalidCmd;
+ Tick snoop_response_latency = 0;
+
+ // snoops should only happen if the system isn't bypassing caches
+ assert(!system->bypassCaches());
+
+ unsigned fanout = 0;
+
+ for (const auto& p: dests) {
+ // 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 != InvalidPortID &&
+ p->getId() == exclude_slave_port_id)
+ continue;
+
+ Tick latency = p->sendAtomicSnoop(pkt);
+ fanout++;
+
+ // 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())
+ continue;
+
+ // 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;
+
+ if (snoopFilter) {
+ // Handle responses by the snoopers and differentiate between
+ // responses to requests from above and snoops from below
+ if (source_master_port_id != InvalidPortID) {
+ // Getting a response for a snoop from below
+ assert(exclude_slave_port_id == InvalidPortID);
+ snoopFilter->updateSnoopForward(pkt, *p,
+ *masterPorts[source_master_port_id]);
+ } else {
+ // Getting a response for a request from above
+ assert(source_master_port_id == InvalidPortID);
+ snoopFilter->updateSnoopResponse(pkt, *p,
+ *slavePorts[exclude_slave_port_id]);
+ }
+ }
+ // restore original packet state for remaining snoopers
+ pkt->cmd = orig_cmd;
+ }
+
+ // Stats for fanout
+ snoopFanout.sample(fanout);
+
+ // 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
+CoherentXBar::recvFunctional(PacketPtr pkt, PortID slave_port_id)
+{
+ if (!pkt->isPrint()) {
+ // don't do DPRINTFs on PrintReq as it clutters up the output
+ DPRINTF(CoherentXBar,
+ "recvFunctional: packet src %s addr 0x%x cmd %s\n",
+ slavePorts[slave_port_id]->name(), pkt->getAddr(),
+ pkt->cmdString());
+ }
+
+ // uncacheable requests need never be snooped
+ if (!pkt->req->isUncacheable() && !system->bypassCaches()) {
+ // forward to all snoopers but the source
+ forwardFunctional(pkt, slave_port_id);
+ }
+
+ // 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()) {
+ PortID dest_id = findPort(pkt->getAddr());
+
+ masterPorts[dest_id]->sendFunctional(pkt);
+ }
+}
+
+void
+CoherentXBar::recvFunctionalSnoop(PacketPtr pkt, PortID master_port_id)
+{
+ if (!pkt->isPrint()) {
+ // don't do DPRINTFs on PrintReq as it clutters up the output
+ DPRINTF(CoherentXBar,
+ "recvFunctionalSnoop: packet src %s addr 0x%x cmd %s\n",
+ masterPorts[master_port_id]->name(), pkt->getAddr(),
+ pkt->cmdString());
+ }
+
+ // forward to all snoopers
+ forwardFunctional(pkt, InvalidPortID);
+}
+
+void
+CoherentXBar::forwardFunctional(PacketPtr pkt, PortID exclude_slave_port_id)
+{
+ // snoops should only happen if the system isn't bypassing caches
+ assert(!system->bypassCaches());
+
+ for (const auto& p: snoopPorts) {
+ // 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 == InvalidPortID ||
+ p->getId() != exclude_slave_port_id)
+ p->sendFunctionalSnoop(pkt);
+
+ // if we get a response we are done
+ if (pkt->isResponse()) {
+ break;
+ }
+ }
+}
+
+unsigned int
+CoherentXBar::drain(DrainManager *dm)
+{
+ // sum up the individual layers
+ unsigned int total = 0;
+ for (auto l: reqLayers)
+ total += l->drain(dm);
+ for (auto l: respLayers)
+ total += l->drain(dm);
+ for (auto l: snoopLayers)
+ total += l->drain(dm);
+ return total;
+}
+
+void
+CoherentXBar::regStats()
+{
+ // register the stats of the base class and our layers
+ BaseXBar::regStats();
+ for (auto l: reqLayers)
+ l->regStats();
+ for (auto l: respLayers)
+ l->regStats();
+ for (auto l: snoopLayers)
+ l->regStats();
+
+ snoops
+ .name(name() + ".snoops")
+ .desc("Total snoops (count)")
+ ;
+
+ snoopFanout
+ .init(0, snoopPorts.size(), 1)
+ .name(name() + ".snoop_fanout")
+ .desc("Request fanout histogram")
+ ;
+}
+
+CoherentXBar *
+CoherentXBarParams::create()
+{
+ return new CoherentXBar(this);
+}
--- /dev/null
+/*
+ * Copyright (c) 2011-2014 ARM Limited
+ * All rights reserved
+ *
+ * The license below extends only to copyright in the software and shall
+ * not be construed as granting a license to any other intellectual
+ * property including but not limited to intellectual property relating
+ * to a hardware implementation of the functionality of the software
+ * licensed hereunder. You may use the software subject to the license
+ * terms below provided that you ensure that this notice is replicated
+ * unmodified and in its entirety in all distributions of the software,
+ * modified or unmodified, in source code or in binary form.
+ *
+ * Copyright (c) 2002-2005 The Regents of The University of Michigan
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met: redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer;
+ * redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution;
+ * neither the name of the copyright holders nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * 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: Ron Dreslinski
+ * Ali Saidi
+ * Andreas Hansson
+ * William Wang
+ */
+
+/**
+ * @file
+ * Declaration of a coherent crossbar.
+ */
+
+#ifndef __MEM_COHERENT_XBAR_HH__
+#define __MEM_COHERENT_XBAR_HH__
+
+#include "base/hashmap.hh"
+#include "mem/snoop_filter.hh"
+#include "mem/xbar.hh"
+#include "params/CoherentXBar.hh"
+
+/**
+ * A coherent crossbar connects a number of (potentially) snooping
+ * masters and slaves, and routes the request and response packets
+ * based on the address, and also forwards all requests to the
+ * snoopers and deals with the snoop responses.
+ *
+ * The coherent crossbar can be used as a template for modelling QPI,
+ * HyperTransport, ACE and coherent OCP buses, and is typically used
+ * for the L1-to-L2 buses and as the main system interconnect. @sa
+ * \ref gem5MemorySystem "gem5 Memory System"
+ */
+class CoherentXBar : public BaseXBar
+{
+
+ protected:
+
+ /**
+ * Declare the layers of this crossbar, one vector for requests,
+ * one for responses, and one for snoop responses
+ */
+ typedef Layer<SlavePort,MasterPort> ReqLayer;
+ typedef Layer<MasterPort,SlavePort> RespLayer;
+ typedef Layer<SlavePort,MasterPort> SnoopLayer;
+ std::vector<ReqLayer*> reqLayers;
+ std::vector<RespLayer*> respLayers;
+ std::vector<SnoopLayer*> snoopLayers;
+
+ /**
+ * Declaration of the coherent crossbar slave port type, one will
+ * be instantiated for each of the master ports connecting to the
+ * crossbar.
+ */
+ class CoherentXBarSlavePort : public SlavePort
+ {
+
+ private:
+
+ /** A reference to the crossbar to which this port belongs. */
+ CoherentXBar &xbar;
+
+ public:
+
+ CoherentXBarSlavePort(const std::string &_name,
+ CoherentXBar &_xbar, PortID _id)
+ : SlavePort(_name, &_xbar, _id), xbar(_xbar)
+ { }
+
+ protected:
+
+ /**
+ * When receiving a timing request, pass it to the crossbar.
+ */
+ virtual bool recvTimingReq(PacketPtr pkt)
+ { return xbar.recvTimingReq(pkt, id); }
+
+ /**
+ * When receiving a timing snoop response, pass it to the crossbar.
+ */
+ virtual bool recvTimingSnoopResp(PacketPtr pkt)
+ { return xbar.recvTimingSnoopResp(pkt, id); }
+
+ /**
+ * When receiving an atomic request, pass it to the crossbar.
+ */
+ virtual Tick recvAtomic(PacketPtr pkt)
+ { return xbar.recvAtomic(pkt, id); }
+
+ /**
+ * When receiving a functional request, pass it to the crossbar.
+ */
+ virtual void recvFunctional(PacketPtr pkt)
+ { xbar.recvFunctional(pkt, id); }
+
+ /**
+ * When receiving a retry, pass it to the crossbar.
+ */
+ virtual void recvRetry()
+ { panic("Crossbar slave ports should never retry.\n"); }
+
+ /**
+ * Return the union of all adress ranges seen by this crossbar.
+ */
+ virtual AddrRangeList getAddrRanges() const
+ { return xbar.getAddrRanges(); }
+
+ };
+
+ /**
+ * Declaration of the coherent crossbar master port type, one will be
+ * instantiated for each of the slave interfaces connecting to the
+ * crossbar.
+ */
+ class CoherentXBarMasterPort : public MasterPort
+ {
+ private:
+ /** A reference to the crossbar to which this port belongs. */
+ CoherentXBar &xbar;
+
+ public:
+
+ CoherentXBarMasterPort(const std::string &_name,
+ CoherentXBar &_xbar, PortID _id)
+ : MasterPort(_name, &_xbar, _id), xbar(_xbar)
+ { }
+
+ protected:
+
+ /**
+ * Determine if this port should be considered a snooper. For
+ * a coherent crossbar master port this is always true.
+ *
+ * @return a boolean that is true if this port is snooping
+ */
+ virtual bool isSnooping() const
+ { return true; }
+
+ /**
+ * When receiving a timing response, pass it to the crossbar.
+ */
+ virtual bool recvTimingResp(PacketPtr pkt)
+ { return xbar.recvTimingResp(pkt, id); }
+
+ /**
+ * When receiving a timing snoop request, pass it to the crossbar.
+ */
+ virtual void recvTimingSnoopReq(PacketPtr pkt)
+ { return xbar.recvTimingSnoopReq(pkt, id); }
+
+ /**
+ * When receiving an atomic snoop request, pass it to the crossbar.
+ */
+ virtual Tick recvAtomicSnoop(PacketPtr pkt)
+ { return xbar.recvAtomicSnoop(pkt, id); }
+
+ /**
+ * When receiving a functional snoop request, pass it to the crossbar.
+ */
+ virtual void recvFunctionalSnoop(PacketPtr pkt)
+ { xbar.recvFunctionalSnoop(pkt, id); }
+
+ /** When reciving a range change from the peer port (at id),
+ pass it to the crossbar. */
+ virtual void recvRangeChange()
+ { xbar.recvRangeChange(id); }
+
+ /** When reciving a retry from the peer port (at id),
+ pass it to the crossbar. */
+ virtual void recvRetry()
+ { xbar.recvRetry(id); }
+
+ };
+
+ /**
+ * Internal class to bridge between an incoming snoop response
+ * from a slave port and forwarding it through an outgoing slave
+ * port. It is effectively a dangling master port.
+ */
+ class SnoopRespPort : public MasterPort
+ {
+
+ private:
+
+ /** The port which we mirror internally. */
+ SlavePort& slavePort;
+
+ public:
+
+ /**
+ * Create a snoop response port that mirrors a given slave port.
+ */
+ SnoopRespPort(SlavePort& slave_port, CoherentXBar& _xbar) :
+ MasterPort(slave_port.name() + ".snoopRespPort", &_xbar),
+ slavePort(slave_port) { }
+
+ /**
+ * Override the sending of retries and pass them on through
+ * the mirrored slave port.
+ */
+ void sendRetry() {
+ slavePort.sendRetry();
+ }
+
+ /**
+ * Provided as necessary.
+ */
+ void recvRetry() { panic("SnoopRespPort should never see retry\n"); }
+
+ /**
+ * Provided as necessary.
+ */
+ bool recvTimingResp(PacketPtr pkt)
+ {
+ panic("SnoopRespPort should never see timing response\n");
+ return false;
+ }
+
+ };
+
+ std::vector<SnoopRespPort*> snoopRespPorts;
+
+ std::vector<SlavePort*> snoopPorts;
+
+ /**
+ * Store the outstanding requests so we can determine which ones
+ * we generated and which ones were merely forwarded. This is used
+ * in the coherent crossbar when coherency responses come back.
+ */
+ m5::hash_set<RequestPtr> outstandingReq;
+
+ /**
+ * Keep a pointer to the system to be allow to querying memory system
+ * properties.
+ */
+ System *system;
+
+ /** A snoop filter that tracks cache line residency and can restrict the
+ * broadcast needed for probes. NULL denotes an absent filter. */
+ SnoopFilter *snoopFilter;
+
+ /** Function called by the port when the crossbar is recieving a Timing
+ request packet.*/
+ bool recvTimingReq(PacketPtr pkt, PortID slave_port_id);
+
+ /** Function called by the port when the crossbar is recieving a Timing
+ response packet.*/
+ bool recvTimingResp(PacketPtr pkt, PortID master_port_id);
+
+ /** Function called by the port when the crossbar is recieving a timing
+ snoop request.*/
+ void recvTimingSnoopReq(PacketPtr pkt, PortID master_port_id);
+
+ /** Function called by the port when the crossbar is recieving a timing
+ snoop response.*/
+ bool recvTimingSnoopResp(PacketPtr pkt, PortID slave_port_id);
+
+ /** Timing function called by port when it is once again able to process
+ * requests. */
+ void recvRetry(PortID master_port_id);
+
+ /**
+ * Forward a timing packet to our snoopers, potentially excluding
+ * one of the connected coherent masters to avoid sending a packet
+ * back to where it came from.
+ *
+ * @param pkt Packet to forward
+ * @param exclude_slave_port_id Id of slave port to exclude
+ */
+ void forwardTiming(PacketPtr pkt, PortID exclude_slave_port_id) {
+ forwardTiming(pkt, exclude_slave_port_id, snoopPorts);
+ }
+
+ /**
+ * Forward a timing packet to a selected list of snoopers, potentially
+ * excluding one of the connected coherent masters to avoid sending a packet
+ * back to where it came from.
+ *
+ * @param pkt Packet to forward
+ * @param exclude_slave_port_id Id of slave port to exclude
+ * @param dests Vector of destination ports for the forwarded pkt
+ */
+ void forwardTiming(PacketPtr pkt, PortID exclude_slave_port_id,
+ const std::vector<SlavePort*>& dests);
+
+ /** Function called by the port when the crossbar is recieving a Atomic
+ transaction.*/
+ Tick recvAtomic(PacketPtr pkt, PortID slave_port_id);
+
+ /** Function called by the port when the crossbar is recieving an
+ atomic snoop transaction.*/
+ Tick recvAtomicSnoop(PacketPtr pkt, PortID master_port_id);
+
+ /**
+ * Forward an atomic packet to our snoopers, potentially excluding
+ * one of the connected coherent masters to avoid sending a packet
+ * back to where it came from.
+ *
+ * @param pkt Packet to forward
+ * @param exclude_slave_port_id Id of slave port to exclude
+ *
+ * @return a pair containing the snoop response and snoop latency
+ */
+ std::pair<MemCmd, Tick> forwardAtomic(PacketPtr pkt,
+ PortID exclude_slave_port_id)
+ {
+ return forwardAtomic(pkt, exclude_slave_port_id, InvalidPortID, snoopPorts);
+ }
+
+ /**
+ * Forward an atomic packet to a selected list of snoopers, potentially
+ * excluding one of the connected coherent masters to avoid sending a packet
+ * back to where it came from.
+ *
+ * @param pkt Packet to forward
+ * @param exclude_slave_port_id Id of slave port to exclude
+ * @param source_master_port_id Id of the master port for snoops from below
+ * @param dests Vector of destination ports for the forwarded pkt
+ *
+ * @return a pair containing the snoop response and snoop latency
+ */
+ std::pair<MemCmd, Tick> forwardAtomic(PacketPtr pkt,
+ PortID exclude_slave_port_id,
+ PortID source_master_port_id,
+ const std::vector<SlavePort*>& dests);
+
+ /** Function called by the port when the crossbar is recieving a Functional
+ transaction.*/
+ void recvFunctional(PacketPtr pkt, PortID slave_port_id);
+
+ /** Function called by the port when the crossbar is recieving a functional
+ snoop transaction.*/
+ void recvFunctionalSnoop(PacketPtr pkt, PortID master_port_id);
+
+ /**
+ * Forward a functional packet to our snoopers, potentially
+ * excluding one of the connected coherent masters to avoid
+ * sending a packet back to where it came from.
+ *
+ * @param pkt Packet to forward
+ * @param exclude_slave_port_id Id of slave port to exclude
+ */
+ void forwardFunctional(PacketPtr pkt, PortID exclude_slave_port_id);
+
+ Stats::Scalar snoops;
+ Stats::Distribution snoopFanout;
+
+ public:
+
+ virtual void init();
+
+ CoherentXBar(const CoherentXBarParams *p);
+
+ virtual ~CoherentXBar();
+
+ unsigned int drain(DrainManager *dm);
+
+ virtual void regStats();
+};
+
+#endif //__MEM_COHERENT_XBAR_HH__
assert(pkt->isResponse());
// @todo someone should pay for this
- pkt->busFirstWordDelay = pkt->busLastWordDelay = 0;
+ pkt->firstWordDelay = pkt->lastWordDelay = 0;
// queue the packet in the response queue to be sent out after
// the static latency has passed
assert(pkt->isResponse());
// @todo someone should pay for this
- pkt->busFirstWordDelay = pkt->busLastWordDelay = 0;
+ pkt->firstWordDelay = pkt->lastWordDelay = 0;
DPRINTF(DRAMSim2, "Queuing response for address %lld\n",
pkt->getAddr());
+++ /dev/null
-/*
- * Copyright (c) 2011-2013 ARM Limited
- * All rights reserved
- *
- * The license below extends only to copyright in the software and shall
- * not be construed as granting a license to any other intellectual
- * property including but not limited to intellectual property relating
- * to a hardware implementation of the functionality of the software
- * licensed hereunder. You may use the software subject to the license
- * terms below provided that you ensure that this notice is replicated
- * unmodified and in its entirety in all distributions of the software,
- * modified or unmodified, in source code or in binary form.
- *
- * Copyright (c) 2006 The Regents of The University of Michigan
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are
- * met: redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer;
- * redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution;
- * neither the name of the copyright holders nor the names of its
- * contributors may be used to endorse or promote products derived from
- * this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * 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
- */
-
-/**
- * @file
- * Definition of a bus object.
- */
-
-#include "base/misc.hh"
-#include "base/trace.hh"
-#include "debug/Bus.hh"
-#include "debug/BusAddrRanges.hh"
-#include "debug/NoncoherentBus.hh"
-#include "mem/noncoherent_bus.hh"
-
-NoncoherentBus::NoncoherentBus(const NoncoherentBusParams *p)
- : BaseBus(p)
-{
- // 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.master[%d]", name(), i);
- MasterPort* bp = new NoncoherentBusMasterPort(portName, *this, i);
- masterPorts.push_back(bp);
- reqLayers.push_back(new ReqLayer(*bp, *this,
- csprintf(".reqLayer%d", i)));
- }
-
- // 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 = name() + ".default";
- MasterPort* bp = new NoncoherentBusMasterPort(portName, *this,
- defaultPortID);
- masterPorts.push_back(bp);
- reqLayers.push_back(new ReqLayer(*bp, *this, csprintf(".reqLayer%d",
- defaultPortID)));
- }
-
- // 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.slave[%d]", name(), i);
- SlavePort* bp = new NoncoherentBusSlavePort(portName, *this, i);
- slavePorts.push_back(bp);
- respLayers.push_back(new RespLayer(*bp, *this,
- csprintf(".respLayer%d", i)));
- }
-
- clearPortCache();
-}
-
-NoncoherentBus::~NoncoherentBus()
-{
- for (auto l = reqLayers.begin(); l != reqLayers.end(); ++l)
- delete *l;
- for (auto l = respLayers.begin(); l != respLayers.end(); ++l)
- delete *l;
-}
-
-bool
-NoncoherentBus::recvTimingReq(PacketPtr pkt, PortID slave_port_id)
-{
- // determine the source port based on the id
- SlavePort *src_port = slavePorts[slave_port_id];
-
- // we should never see express snoops on a non-coherent bus
- assert(!pkt->isExpressSnoop());
-
- // determine the destination based on the address
- PortID master_port_id = findPort(pkt->getAddr());
-
- // test if the bus should be considered occupied for the current
- // port
- if (!reqLayers[master_port_id]->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\n",
- src_port->name(), pkt->cmdString(), pkt->getAddr());
-
- // store size and command as they might be modified when
- // forwarding the packet
- unsigned int pkt_size = pkt->hasData() ? pkt->getSize() : 0;
- unsigned int pkt_cmd = pkt->cmdToIndex();
-
- // set the source port for routing of the response
- pkt->setSrc(slave_port_id);
-
- calcPacketTiming(pkt);
- Tick packetFinishTime = pkt->busLastWordDelay + curTick();
-
- // since it is a normal request, attempt to send the packet
- bool success = masterPorts[master_port_id]->sendTimingReq(pkt);
-
- if (!success) {
- // inhibited packets should never be forced to retry
- assert(!pkt->memInhibitAsserted());
-
- DPRINTF(NoncoherentBus, "recvTimingReq: src %s %s 0x%x RETRY\n",
- src_port->name(), pkt->cmdString(), pkt->getAddr());
-
- // undo the calculation so we can check for 0 again
- pkt->busFirstWordDelay = pkt->busLastWordDelay = 0;
-
- // occupy until the header is sent
- reqLayers[master_port_id]->failedTiming(src_port,
- clockEdge(headerCycles));
-
- return false;
- }
-
- reqLayers[master_port_id]->succeededTiming(packetFinishTime);
-
- // stats updates
- dataThroughBus += pkt_size;
- pktCount[slave_port_id][master_port_id]++;
- totPktSize[slave_port_id][master_port_id] += pkt_size;
- transDist[pkt_cmd]++;
-
- return true;
-}
-
-bool
-NoncoherentBus::recvTimingResp(PacketPtr pkt, PortID master_port_id)
-{
- // determine the source port based on the id
- MasterPort *src_port = masterPorts[master_port_id];
-
- // determine the destination based on what is stored in the packet
- PortID slave_port_id = pkt->getDest();
-
- // test if the bus should be considered occupied for the current
- // port
- if (!respLayers[slave_port_id]->tryTiming(src_port)) {
- DPRINTF(NoncoherentBus, "recvTimingResp: src %s %s 0x%x BUSY\n",
- src_port->name(), pkt->cmdString(), pkt->getAddr());
- return false;
- }
-
- DPRINTF(NoncoherentBus, "recvTimingResp: src %s %s 0x%x\n",
- src_port->name(), pkt->cmdString(), pkt->getAddr());
-
- // store size and command as they might be modified when
- // forwarding the packet
- unsigned int pkt_size = pkt->hasData() ? pkt->getSize() : 0;
- unsigned int pkt_cmd = pkt->cmdToIndex();
-
- calcPacketTiming(pkt);
- Tick packetFinishTime = pkt->busLastWordDelay + curTick();
-
- // send the packet through the destination slave port
- bool success M5_VAR_USED = slavePorts[slave_port_id]->sendTimingResp(pkt);
-
- // currently it is illegal to block responses... can lead to
- // deadlock
- assert(success);
-
- respLayers[slave_port_id]->succeededTiming(packetFinishTime);
-
- // stats updates
- dataThroughBus += pkt_size;
- pktCount[slave_port_id][master_port_id]++;
- totPktSize[slave_port_id][master_port_id] += pkt_size;
- transDist[pkt_cmd]++;
-
- return true;
-}
-
-void
-NoncoherentBus::recvRetry(PortID master_port_id)
-{
- // responses never block on forwarding them, so the retry will
- // always be coming from a port to which we tried to forward a
- // request
- reqLayers[master_port_id]->recvRetry();
-}
-
-Tick
-NoncoherentBus::recvAtomic(PacketPtr pkt, PortID slave_port_id)
-{
- DPRINTF(NoncoherentBus, "recvAtomic: packet src %s addr 0x%x cmd %s\n",
- slavePorts[slave_port_id]->name(), pkt->getAddr(),
- pkt->cmdString());
-
- // add the request data
- dataThroughBus += pkt->hasData() ? pkt->getSize() : 0;
-
- // determine the destination port
- PortID dest_id = findPort(pkt->getAddr());
-
- // forward the request to the appropriate destination
- Tick response_latency = masterPorts[dest_id]->sendAtomic(pkt);
-
- // add the response data
- if (pkt->isResponse())
- dataThroughBus += pkt->hasData() ? pkt->getSize() : 0;
-
- // @todo: Not setting first-word time
- pkt->busLastWordDelay = response_latency;
- return response_latency;
-}
-
-void
-NoncoherentBus::recvFunctional(PacketPtr pkt, PortID slave_port_id)
-{
- if (!pkt->isPrint()) {
- // don't do DPRINTFs on PrintReq as it clutters up the output
- DPRINTF(NoncoherentBus,
- "recvFunctional: packet src %s addr 0x%x cmd %s\n",
- slavePorts[slave_port_id]->name(), pkt->getAddr(),
- pkt->cmdString());
- }
-
- // determine the destination port
- PortID dest_id = findPort(pkt->getAddr());
-
- // forward the request to the appropriate destination
- masterPorts[dest_id]->sendFunctional(pkt);
-}
-
-unsigned int
-NoncoherentBus::drain(DrainManager *dm)
-{
- // sum up the individual layers
- unsigned int total = 0;
- for (auto l = reqLayers.begin(); l != reqLayers.end(); ++l)
- total += (*l)->drain(dm);
- for (auto l = respLayers.begin(); l != respLayers.end(); ++l)
- total += (*l)->drain(dm);
- return total;
-}
-
-NoncoherentBus*
-NoncoherentBusParams::create()
-{
- return new NoncoherentBus(this);
-}
-
-void
-NoncoherentBus::regStats()
-{
- // register the stats of the base class and our two bus layers
- BaseBus::regStats();
- for (auto l = reqLayers.begin(); l != reqLayers.end(); ++l)
- (*l)->regStats();
- for (auto l = respLayers.begin(); l != respLayers.end(); ++l)
- (*l)->regStats();
-
- dataThroughBus
- .name(name() + ".data_through_bus")
- .desc("Total data (bytes)")
- ;
-
- throughput
- .name(name() + ".throughput")
- .desc("Throughput (bytes/s)")
- .precision(0)
- ;
-
- throughput = dataThroughBus / simSeconds;
-}
+++ /dev/null
-/*
- * Copyright (c) 2011-2013 ARM Limited
- * All rights reserved
- *
- * The license below extends only to copyright in the software and shall
- * not be construed as granting a license to any other intellectual
- * property including but not limited to intellectual property relating
- * to a hardware implementation of the functionality of the software
- * licensed hereunder. You may use the software subject to the license
- * terms below provided that you ensure that this notice is replicated
- * unmodified and in its entirety in all distributions of the software,
- * modified or unmodified, in source code or in binary form.
- *
- * Copyright (c) 2002-2005 The Regents of The University of Michigan
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are
- * met: redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer;
- * redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution;
- * neither the name of the copyright holders nor the names of its
- * contributors may be used to endorse or promote products derived from
- * this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * 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: Ron Dreslinski
- * Ali Saidi
- * Andreas Hansson
- * William Wang
- */
-
-/**
- * @file
- * Declaration of a non-coherent bus.
- */
-
-#ifndef __MEM_NONCOHERENT_BUS_HH__
-#define __MEM_NONCOHERENT_BUS_HH__
-
-#include "mem/bus.hh"
-#include "params/NoncoherentBus.hh"
-
-/**
- * A non-coherent bus connects a number of non-snooping masters and
- * slaves, and routes the request and response packets based on the
- * address. The request packets issued by the master connected to a
- * non-coherent bus could still snoop in caches attached to a coherent
- * bus, as is the case with the I/O bus and memory bus in most system
- * configurations. No snoops will, however, reach any master on the
- * non-coherent bus itself.
- *
- * The non-coherent bus can be used as a template for modelling PCI,
- * PCIe, and non-coherent AMBA and OCP buses, and is typically used
- * for the I/O buses.
- */
-class NoncoherentBus : public BaseBus
-{
-
- protected:
-
- /**
- * Declare the layers of this bus, one vector for requests and one
- * for responses.
- */
- typedef Layer<SlavePort,MasterPort> ReqLayer;
- typedef Layer<MasterPort,SlavePort> RespLayer;
- std::vector<ReqLayer*> reqLayers;
- std::vector<RespLayer*> respLayers;
-
- /**
- * Declaration of the non-coherent bus slave port type, one will
- * be instantiated for each of the master ports connecting to the
- * bus.
- */
- class NoncoherentBusSlavePort : public SlavePort
- {
- private:
-
- /** A reference to the bus to which this port belongs. */
- NoncoherentBus &bus;
-
- public:
-
- NoncoherentBusSlavePort(const std::string &_name,
- NoncoherentBus &_bus, PortID _id)
- : SlavePort(_name, &_bus, _id), bus(_bus)
- { }
-
- protected:
-
- /**
- * When receiving a timing request, pass it to the bus.
- */
- virtual bool recvTimingReq(PacketPtr pkt)
- { return bus.recvTimingReq(pkt, id); }
-
- /**
- * When receiving an atomic request, pass it to the bus.
- */
- virtual Tick recvAtomic(PacketPtr pkt)
- { return bus.recvAtomic(pkt, id); }
-
- /**
- * When receiving a functional request, pass it to the bus.
- */
- virtual void recvFunctional(PacketPtr pkt)
- { bus.recvFunctional(pkt, id); }
-
- /**
- * When receiving a retry, pass it to the bus.
- */
- virtual void recvRetry()
- { panic("Bus slave ports always succeed and should never retry.\n"); }
-
- /**
- * Return the union of all adress ranges seen by this bus.
- */
- virtual AddrRangeList getAddrRanges() const
- { return bus.getAddrRanges(); }
-
- };
-
- /**
- * Declaration of the bus master port type, one will be
- * instantiated for each of the slave ports connecting to the
- * bus.
- */
- class NoncoherentBusMasterPort : public MasterPort
- {
- private:
-
- /** A reference to the bus to which this port belongs. */
- NoncoherentBus &bus;
-
- public:
-
- NoncoherentBusMasterPort(const std::string &_name,
- NoncoherentBus &_bus, PortID _id)
- : MasterPort(_name, &_bus, _id), bus(_bus)
- { }
-
- protected:
-
- /**
- * When receiving a timing response, pass it to the bus.
- */
- virtual bool recvTimingResp(PacketPtr pkt)
- { return bus.recvTimingResp(pkt, id); }
-
- /** When reciving a range change from the peer port (at id),
- pass it to the bus. */
- virtual void recvRangeChange()
- { bus.recvRangeChange(id); }
-
- /** When reciving a retry from the peer port (at id),
- pass it to the bus. */
- virtual void recvRetry()
- { bus.recvRetry(id); }
-
- };
-
- /** Function called by the port when the bus is recieving a Timing
- request packet.*/
- virtual bool recvTimingReq(PacketPtr pkt, PortID slave_port_id);
-
- /** Function called by the port when the bus is recieving a Timing
- response packet.*/
- virtual bool recvTimingResp(PacketPtr pkt, PortID master_port_id);
-
- /** Timing function called by port when it is once again able to process
- * requests. */
- void recvRetry(PortID master_port_id);
-
- /** Function called by the port when the bus is recieving a Atomic
- transaction.*/
- Tick recvAtomic(PacketPtr pkt, PortID slave_port_id);
-
- /** Function called by the port when the bus is recieving a Functional
- transaction.*/
- void recvFunctional(PacketPtr pkt, PortID slave_port_id);
-
- public:
-
- NoncoherentBus(const NoncoherentBusParams *p);
-
- virtual ~NoncoherentBus();
-
- unsigned int drain(DrainManager *dm);
-
- /**
- * stats
- */
- virtual void regStats();
- Stats::Scalar dataThroughBus;
-};
-
-#endif //__MEM_NONCOHERENT_BUS_HH__
--- /dev/null
+/*
+ * Copyright (c) 2011-2014 ARM Limited
+ * All rights reserved
+ *
+ * The license below extends only to copyright in the software and shall
+ * not be construed as granting a license to any other intellectual
+ * property including but not limited to intellectual property relating
+ * to a hardware implementation of the functionality of the software
+ * licensed hereunder. You may use the software subject to the license
+ * terms below provided that you ensure that this notice is replicated
+ * unmodified and in its entirety in all distributions of the software,
+ * modified or unmodified, in source code or in binary form.
+ *
+ * Copyright (c) 2006 The Regents of The University of Michigan
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met: redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer;
+ * redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution;
+ * neither the name of the copyright holders nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * 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
+ */
+
+/**
+ * @file
+ * Definition of a non-coherent crossbar object.
+ */
+
+#include "base/misc.hh"
+#include "base/trace.hh"
+#include "debug/NoncoherentXBar.hh"
+#include "debug/XBar.hh"
+#include "mem/noncoherent_xbar.hh"
+
+NoncoherentXBar::NoncoherentXBar(const NoncoherentXBarParams *p)
+ : BaseXBar(p)
+{
+ // 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.master[%d]", name(), i);
+ MasterPort* bp = new NoncoherentXBarMasterPort(portName, *this, i);
+ masterPorts.push_back(bp);
+ reqLayers.push_back(new ReqLayer(*bp, *this,
+ csprintf(".reqLayer%d", i)));
+ }
+
+ // 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 = name() + ".default";
+ MasterPort* bp = new NoncoherentXBarMasterPort(portName, *this,
+ defaultPortID);
+ masterPorts.push_back(bp);
+ reqLayers.push_back(new ReqLayer(*bp, *this, csprintf(".reqLayer%d",
+ defaultPortID)));
+ }
+
+ // 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.slave[%d]", name(), i);
+ SlavePort* bp = new NoncoherentXBarSlavePort(portName, *this, i);
+ slavePorts.push_back(bp);
+ respLayers.push_back(new RespLayer(*bp, *this,
+ csprintf(".respLayer%d", i)));
+ }
+
+ clearPortCache();
+}
+
+NoncoherentXBar::~NoncoherentXBar()
+{
+ for (auto l: reqLayers)
+ delete l;
+ for (auto l: respLayers)
+ delete l;
+}
+
+bool
+NoncoherentXBar::recvTimingReq(PacketPtr pkt, PortID slave_port_id)
+{
+ // determine the source port based on the id
+ SlavePort *src_port = slavePorts[slave_port_id];
+
+ // we should never see express snoops on a non-coherent crossbar
+ assert(!pkt->isExpressSnoop());
+
+ // determine the destination based on the address
+ PortID master_port_id = findPort(pkt->getAddr());
+
+ // test if the layer should be considered occupied for the current
+ // port
+ if (!reqLayers[master_port_id]->tryTiming(src_port)) {
+ DPRINTF(NoncoherentXBar, "recvTimingReq: src %s %s 0x%x BUSY\n",
+ src_port->name(), pkt->cmdString(), pkt->getAddr());
+ return false;
+ }
+
+ DPRINTF(NoncoherentXBar, "recvTimingReq: src %s %s 0x%x\n",
+ src_port->name(), pkt->cmdString(), pkt->getAddr());
+
+ // store size and command as they might be modified when
+ // forwarding the packet
+ unsigned int pkt_size = pkt->hasData() ? pkt->getSize() : 0;
+ unsigned int pkt_cmd = pkt->cmdToIndex();
+
+ // set the source port for routing of the response
+ pkt->setSrc(slave_port_id);
+
+ calcPacketTiming(pkt);
+ Tick packetFinishTime = pkt->lastWordDelay + curTick();
+
+ // since it is a normal request, attempt to send the packet
+ bool success = masterPorts[master_port_id]->sendTimingReq(pkt);
+
+ if (!success) {
+ // inhibited packets should never be forced to retry
+ assert(!pkt->memInhibitAsserted());
+
+ DPRINTF(NoncoherentXBar, "recvTimingReq: src %s %s 0x%x RETRY\n",
+ src_port->name(), pkt->cmdString(), pkt->getAddr());
+
+ // undo the calculation so we can check for 0 again
+ pkt->firstWordDelay = pkt->lastWordDelay = 0;
+
+ // occupy until the header is sent
+ reqLayers[master_port_id]->failedTiming(src_port,
+ clockEdge(headerCycles));
+
+ return false;
+ }
+
+ reqLayers[master_port_id]->succeededTiming(packetFinishTime);
+
+ // stats updates
+ pktCount[slave_port_id][master_port_id]++;
+ pktSize[slave_port_id][master_port_id] += pkt_size;
+ transDist[pkt_cmd]++;
+
+ return true;
+}
+
+bool
+NoncoherentXBar::recvTimingResp(PacketPtr pkt, PortID master_port_id)
+{
+ // determine the source port based on the id
+ MasterPort *src_port = masterPorts[master_port_id];
+
+ // determine the destination based on what is stored in the packet
+ PortID slave_port_id = pkt->getDest();
+
+ // test if the layer should be considered occupied for the current
+ // port
+ if (!respLayers[slave_port_id]->tryTiming(src_port)) {
+ DPRINTF(NoncoherentXBar, "recvTimingResp: src %s %s 0x%x BUSY\n",
+ src_port->name(), pkt->cmdString(), pkt->getAddr());
+ return false;
+ }
+
+ DPRINTF(NoncoherentXBar, "recvTimingResp: src %s %s 0x%x\n",
+ src_port->name(), pkt->cmdString(), pkt->getAddr());
+
+ // store size and command as they might be modified when
+ // forwarding the packet
+ unsigned int pkt_size = pkt->hasData() ? pkt->getSize() : 0;
+ unsigned int pkt_cmd = pkt->cmdToIndex();
+
+ calcPacketTiming(pkt);
+ Tick packetFinishTime = pkt->lastWordDelay + curTick();
+
+ // send the packet through the destination slave port
+ bool success M5_VAR_USED = slavePorts[slave_port_id]->sendTimingResp(pkt);
+
+ // currently it is illegal to block responses... can lead to
+ // deadlock
+ assert(success);
+
+ respLayers[slave_port_id]->succeededTiming(packetFinishTime);
+
+ // stats updates
+ pktCount[slave_port_id][master_port_id]++;
+ pktSize[slave_port_id][master_port_id] += pkt_size;
+ transDist[pkt_cmd]++;
+
+ return true;
+}
+
+void
+NoncoherentXBar::recvRetry(PortID master_port_id)
+{
+ // responses never block on forwarding them, so the retry will
+ // always be coming from a port to which we tried to forward a
+ // request
+ reqLayers[master_port_id]->recvRetry();
+}
+
+Tick
+NoncoherentXBar::recvAtomic(PacketPtr pkt, PortID slave_port_id)
+{
+ DPRINTF(NoncoherentXBar, "recvAtomic: packet src %s addr 0x%x cmd %s\n",
+ slavePorts[slave_port_id]->name(), pkt->getAddr(),
+ pkt->cmdString());
+
+ unsigned int pkt_size = pkt->hasData() ? pkt->getSize() : 0;
+ unsigned int pkt_cmd = pkt->cmdToIndex();
+
+ // determine the destination port
+ PortID master_port_id = findPort(pkt->getAddr());
+
+ // stats updates for the request
+ pktCount[slave_port_id][master_port_id]++;
+ pktSize[slave_port_id][master_port_id] += pkt_size;
+ transDist[pkt_cmd]++;
+
+ // forward the request to the appropriate destination
+ Tick response_latency = masterPorts[master_port_id]->sendAtomic(pkt);
+
+ // add the response data
+ if (pkt->isResponse()) {
+ pkt_size = pkt->hasData() ? pkt->getSize() : 0;
+ pkt_cmd = pkt->cmdToIndex();
+
+ // stats updates
+ pktCount[slave_port_id][master_port_id]++;
+ pktSize[slave_port_id][master_port_id] += pkt_size;
+ transDist[pkt_cmd]++;
+ }
+
+ // @todo: Not setting first-word time
+ pkt->lastWordDelay = response_latency;
+ return response_latency;
+}
+
+void
+NoncoherentXBar::recvFunctional(PacketPtr pkt, PortID slave_port_id)
+{
+ if (!pkt->isPrint()) {
+ // don't do DPRINTFs on PrintReq as it clutters up the output
+ DPRINTF(NoncoherentXBar,
+ "recvFunctional: packet src %s addr 0x%x cmd %s\n",
+ slavePorts[slave_port_id]->name(), pkt->getAddr(),
+ pkt->cmdString());
+ }
+
+ // determine the destination port
+ PortID dest_id = findPort(pkt->getAddr());
+
+ // forward the request to the appropriate destination
+ masterPorts[dest_id]->sendFunctional(pkt);
+}
+
+unsigned int
+NoncoherentXBar::drain(DrainManager *dm)
+{
+ // sum up the individual layers
+ unsigned int total = 0;
+ for (auto l: reqLayers)
+ total += l->drain(dm);
+ for (auto l: respLayers)
+ total += l->drain(dm);
+ return total;
+}
+
+NoncoherentXBar*
+NoncoherentXBarParams::create()
+{
+ return new NoncoherentXBar(this);
+}
+
+void
+NoncoherentXBar::regStats()
+{
+ // register the stats of the base class and our layers
+ BaseXBar::regStats();
+ for (auto l: reqLayers)
+ l->regStats();
+ for (auto l: respLayers)
+ l->regStats();
+}
--- /dev/null
+/*
+ * Copyright (c) 2011-2014 ARM Limited
+ * All rights reserved
+ *
+ * The license below extends only to copyright in the software and shall
+ * not be construed as granting a license to any other intellectual
+ * property including but not limited to intellectual property relating
+ * to a hardware implementation of the functionality of the software
+ * licensed hereunder. You may use the software subject to the license
+ * terms below provided that you ensure that this notice is replicated
+ * unmodified and in its entirety in all distributions of the software,
+ * modified or unmodified, in source code or in binary form.
+ *
+ * Copyright (c) 2002-2005 The Regents of The University of Michigan
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met: redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer;
+ * redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution;
+ * neither the name of the copyright holders nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * 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: Ron Dreslinski
+ * Ali Saidi
+ * Andreas Hansson
+ * William Wang
+ */
+
+/**
+ * @file
+ * Declaration of a non-coherent crossbar.
+ */
+
+#ifndef __MEM_NONCOHERENT_XBAR_HH__
+#define __MEM_NONCOHERENT_XBAR_HH__
+
+#include "mem/xbar.hh"
+#include "params/NoncoherentXBar.hh"
+
+/**
+ * A non-coherent crossbar connects a number of non-snooping masters
+ * and slaves, and routes the request and response packets based on
+ * the address. The request packets issued by the master connected to
+ * a non-coherent crossbar could still snoop in caches attached to a
+ * coherent crossbar, as is the case with the I/O bus and memory bus
+ * in most system configurations. No snoops will, however, reach any
+ * master on the non-coherent crossbar itself.
+ *
+ * The non-coherent crossbar can be used as a template for modelling
+ * PCIe, and non-coherent AMBA and OCP buses, and is typically used
+ * for the I/O buses.
+ */
+class NoncoherentXBar : public BaseXBar
+{
+
+ protected:
+
+ /**
+ * Declare the layers of this crossbar, one vector for requests
+ * and one for responses.
+ */
+ typedef Layer<SlavePort,MasterPort> ReqLayer;
+ typedef Layer<MasterPort,SlavePort> RespLayer;
+ std::vector<ReqLayer*> reqLayers;
+ std::vector<RespLayer*> respLayers;
+
+ /**
+ * Declaration of the non-coherent crossbar slave port type, one
+ * will be instantiated for each of the master ports connecting to
+ * the crossbar.
+ */
+ class NoncoherentXBarSlavePort : public SlavePort
+ {
+ private:
+
+ /** A reference to the crossbar to which this port belongs. */
+ NoncoherentXBar &xbar;
+
+ public:
+
+ NoncoherentXBarSlavePort(const std::string &_name,
+ NoncoherentXBar &_xbar, PortID _id)
+ : SlavePort(_name, &_xbar, _id), xbar(_xbar)
+ { }
+
+ protected:
+
+ /**
+ * When receiving a timing request, pass it to the crossbar.
+ */
+ virtual bool recvTimingReq(PacketPtr pkt)
+ { return xbar.recvTimingReq(pkt, id); }
+
+ /**
+ * When receiving an atomic request, pass it to the crossbar.
+ */
+ virtual Tick recvAtomic(PacketPtr pkt)
+ { return xbar.recvAtomic(pkt, id); }
+
+ /**
+ * When receiving a functional request, pass it to the crossbar.
+ */
+ virtual void recvFunctional(PacketPtr pkt)
+ { xbar.recvFunctional(pkt, id); }
+
+ /**
+ * When receiving a retry, pass it to the crossbar.
+ */
+ virtual void recvRetry()
+ { panic("Crossbar slave ports should never retry.\n"); }
+
+ /**
+ * Return the union of all adress ranges seen by this crossbar.
+ */
+ virtual AddrRangeList getAddrRanges() const
+ { return xbar.getAddrRanges(); }
+
+ };
+
+ /**
+ * Declaration of the crossbar master port type, one will be
+ * instantiated for each of the slave ports connecting to the
+ * crossbar.
+ */
+ class NoncoherentXBarMasterPort : public MasterPort
+ {
+ private:
+
+ /** A reference to the crossbar to which this port belongs. */
+ NoncoherentXBar &xbar;
+
+ public:
+
+ NoncoherentXBarMasterPort(const std::string &_name,
+ NoncoherentXBar &_xbar, PortID _id)
+ : MasterPort(_name, &_xbar, _id), xbar(_xbar)
+ { }
+
+ protected:
+
+ /**
+ * When receiving a timing response, pass it to the crossbar.
+ */
+ virtual bool recvTimingResp(PacketPtr pkt)
+ { return xbar.recvTimingResp(pkt, id); }
+
+ /** When reciving a range change from the peer port (at id),
+ pass it to the crossbar. */
+ virtual void recvRangeChange()
+ { xbar.recvRangeChange(id); }
+
+ /** When reciving a retry from the peer port (at id),
+ pass it to the crossbar. */
+ virtual void recvRetry()
+ { xbar.recvRetry(id); }
+
+ };
+
+ /** Function called by the port when the crossbar is recieving a Timing
+ request packet.*/
+ virtual bool recvTimingReq(PacketPtr pkt, PortID slave_port_id);
+
+ /** Function called by the port when the crossbar is recieving a Timing
+ response packet.*/
+ virtual bool recvTimingResp(PacketPtr pkt, PortID master_port_id);
+
+ /** Timing function called by port when it is once again able to process
+ * requests. */
+ void recvRetry(PortID master_port_id);
+
+ /** Function called by the port when the crossbar is recieving a Atomic
+ transaction.*/
+ Tick recvAtomic(PacketPtr pkt, PortID slave_port_id);
+
+ /** Function called by the port when the crossbar is recieving a Functional
+ transaction.*/
+ void recvFunctional(PacketPtr pkt, PortID slave_port_id);
+
+ public:
+
+ NoncoherentXBar(const NoncoherentXBarParams *p);
+
+ virtual ~NoncoherentXBar();
+
+ unsigned int drain(DrainManager *dm);
+
+ /**
+ * stats
+ */
+ virtual void regStats();
+ Stats::Scalar totPktSize;
+};
+
+#endif //__MEM_NONCOHERENT_XBAR_HH__
/**
* Source port identifier set on a request packet to enable
* appropriate routing of the responses. The source port
- * identifier is set by any multiplexing component, e.g. a bus, as
- * the timing responses need this information to be routed back to
- * the appropriate port at a later point in time. The field can be
- * updated (over-written) as the request packet passes through
- * additional multiplexing components, and it is their
- * responsibility to remember the original source port identifier,
- * for example by using an appropriate sender state. The latter is
- * done in the cache and bridge.
+ * identifier is set by any multiplexing component, e.g. a
+ * crossbar, as the timing responses need this information to be
+ * routed back to the appropriate port at a later point in
+ * time. The field can be updated (over-written) as the request
+ * packet passes through additional multiplexing components, and
+ * it is their responsibility to remember the original source port
+ * identifier, for example by using an appropriate sender
+ * state. The latter is done in the cache and bridge.
*/
PortID src;
* packets that passed through a multiplexing component as a
* request packet. The source port identifier is turned into a
* destination port identifier when the packet is turned into a
- * response, and the destination is used, e.g. by the bus, to
+ * response, and the destination is used, e.g. by the crossbar, to
* select the appropriate path through the interconnect.
*/
PortID dest;
/**
* The extra delay from seeing the packet until the first word is
- * transmitted by the bus that provided it (if any). This delay is
- * used to communicate the bus waiting time to the neighbouring
- * object (e.g. a cache) that actually makes the packet wait. As
- * the delay is relative, a 32-bit unsigned should be sufficient.
+ * transmitted. This delay is used to communicate the crossbar
+ * forwarding latency to the neighbouring object (e.g. a cache)
+ * that actually makes the packet wait. As the delay is relative,
+ * a 32-bit unsigned should be sufficient.
*/
- uint32_t busFirstWordDelay;
+ uint32_t firstWordDelay;
/**
- * The extra delay from seeing the packet until the last word is
- * transmitted by the bus that provided it (if any). Similar to
- * the first word time, this is used to make up for the fact that
- * the bus does not make the packet wait. As the delay is relative,
- * a 32-bit unsigned should be sufficient.
+ * The extra pipelining delay from seeing the packet until the
+ * last word is transmitted by the component that provided it (if
+ * any). This includes the first word delay. Similar to the first
+ * word delay, this is used to make up for the fact that the
+ * crossbar does not make the packet wait. As the delay is
+ * relative, a 32-bit unsigned should be sufficient.
*/
- uint32_t busLastWordDelay;
+ uint32_t lastWordDelay;
/**
* A virtual base opaque structure used to hold state associated
PortID getSrc() const { assert(isSrcValid()); return src; }
/// Accessor function to set the source index of the packet.
void setSrc(PortID _src) { src = _src; }
- /// Reset source field, e.g. to retransmit packet on different bus.
- void clearSrc() { src = InvalidPortID; }
bool isDestValid() const { return dest != InvalidPortID; }
/// Accessor function for the destination index of the packet.
: cmd(_cmd), req(_req), data(nullptr), addr(0), _isSecure(false),
src(InvalidPortID), dest(InvalidPortID),
bytesValidStart(0), bytesValidEnd(0),
- busFirstWordDelay(0), busLastWordDelay(0),
+ firstWordDelay(0), lastWordDelay(0),
senderState(NULL)
{
if (req->hasPaddr()) {
: cmd(_cmd), req(_req), data(nullptr), addr(0), _isSecure(false),
src(InvalidPortID), dest(InvalidPortID),
bytesValidStart(0), bytesValidEnd(0),
- busFirstWordDelay(0), busLastWordDelay(0),
+ firstWordDelay(0), lastWordDelay(0),
senderState(NULL)
{
if (req->hasPaddr()) {
src(pkt->src), dest(pkt->dest),
bytesValidStart(pkt->bytesValidStart),
bytesValidEnd(pkt->bytesValidEnd),
- busFirstWordDelay(pkt->busFirstWordDelay),
- busLastWordDelay(pkt->busLastWordDelay),
+ firstWordDelay(pkt->firstWordDelay),
+ lastWordDelay(pkt->lastWordDelay),
senderState(pkt->senderState)
{
if (!clearFlags)
dest = InvalidPortID;
bytesValidStart = 0;
bytesValidEnd = 0;
- busFirstWordDelay = 0;
- busLastWordDelay = 0;
+ firstWordDelay = 0;
+ lastWordDelay = 0;
flags.set(VALID_ADDR|VALID_SIZE);
deleteData();
flags.clear(EXPRESS_SNOOP);
dest = src;
- clearSrc();
+ src = InvalidPortID;
}
void
#include <string>
#include "base/trace.hh"
-#include "debug/BusAddrRanges.hh"
+#include "debug/AddrRanges.hh"
#include "debug/Checkpoint.hh"
#include "mem/abstract_mem.hh"
#include "mem/physical.hh"
fatal("Memory address range for %s is overlapping\n",
(*m)->name());
} else {
- DPRINTF(BusAddrRanges,
+ DPRINTF(AddrRanges,
"Skipping memory %s that is not in global address map\n",
(*m)->name());
// this type of memory is used e.g. as reference memory by
range.to_string());
// perform the actual mmap
- DPRINTF(BusAddrRanges, "Creating backing store for range %s with size %d\n",
+ DPRINTF(AddrRanges, "Creating backing store for range %s with size %d\n",
range.to_string(), range.size());
int map_flags = MAP_ANON | MAP_PRIVATE;
uint8_t* pmem = (uint8_t*) mmap(NULL, range.size(),
// point the memories to their backing store
for (vector<AbstractMemory*>::const_iterator m = _memories.begin();
m != _memories.end(); ++m) {
- DPRINTF(BusAddrRanges, "Mapping memory %s to backing store\n",
+ DPRINTF(AddrRanges, "Mapping memory %s to backing store\n",
(*m)->name());
(*m)->setBackingStore(pmem);
}
}
// @todo someone should pay for this
- pkt->busFirstWordDelay = pkt->busLastWordDelay = 0;
+ pkt->firstWordDelay = pkt->lastWordDelay = 0;
// update the release time according to the bandwidth limit, and
// do so with respect to the time it takes to finish this request
--- /dev/null
+/*
+ * Copyright (c) 2011-2014 ARM Limited
+ * All rights reserved
+ *
+ * The license below extends only to copyright in the software and shall
+ * not be construed as granting a license to any other intellectual
+ * property including but not limited to intellectual property relating
+ * to a hardware implementation of the functionality of the software
+ * licensed hereunder. You may use the software subject to the license
+ * terms below provided that you ensure that this notice is replicated
+ * unmodified and in its entirety in all distributions of the software,
+ * modified or unmodified, in source code or in binary form.
+ *
+ * Copyright (c) 2006 The Regents of The University of Michigan
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met: redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer;
+ * redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution;
+ * neither the name of the copyright holders nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * 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
+ */
+
+/**
+ * @file
+ * Definition of a crossbar object.
+ */
+
+#include "base/misc.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),
+ gotAllAddrRanges(false), defaultPortID(InvalidPortID),
+ useDefaultRange(p->use_default_range)
+{}
+
+BaseXBar::~BaseXBar()
+{
+ for (auto m: masterPorts)
+ delete m;
+
+ for (auto s: slavePorts)
+ delete s;
+}
+
+void
+BaseXBar::init()
+{
+}
+
+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);
+ }
+}
+
+BaseSlavePort &
+BaseXBar::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
+ return *slavePorts[idx];
+ } else {
+ return MemObject::getSlavePort(if_name, idx);
+ }
+}
+
+void
+BaseXBar::calcPacketTiming(PacketPtr pkt)
+{
+ // 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;
+
+ // Note that currently lastWordDelay can be smaller than
+ // firstWordDelay if the packet has no data
+ pkt->lastWordDelay = (headerCycles + dataCycles) * clockPeriod() +
+ offset;
+}
+
+template <typename SrcType, typename DstType>
+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)
+{
+}
+
+template <typename SrcType, typename DstType>
+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
+ // packet to prevent any follow-on calls to sendTiming seeing an
+ // unoccupied layer
+ assert(state == BUSY);
+
+ // until should never be 0 as express snoops never occupy the layer
+ assert(until != 0);
+ xbar.schedule(releaseEvent, until);
+
+ // account for the occupied ticks
+ occupancy += until - curTick();
+
+ DPRINTF(BaseXBar, "The crossbar layer is now busy from tick %d to %d\n",
+ curTick(), until);
+}
+
+template <typename SrcType, typename DstType>
+bool
+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
+ // this state again in zero time if the peer does not immediately
+ // call the layer when receiving the retry
+
+ // first we see if the layer is busy, next we check if the
+ // destination port is already engaged in a transaction waiting
+ // for a retry from the peer
+ if (state == BUSY || waitingForPeer != NULL) {
+ // the port should not be waiting already
+ assert(std::find(waitingForLayer.begin(), waitingForLayer.end(),
+ src_port) == waitingForLayer.end());
+
+ // put the port at the end of the retry list waiting for the
+ // layer to be freed up (and in the case of a busy peer, for
+ // that transaction to go through, and then the layer to free
+ // up)
+ waitingForLayer.push_back(src_port);
+ return false;
+ }
+
+ state = BUSY;
+
+ return true;
+}
+
+template <typename SrcType, typename DstType>
+void
+BaseXBar::Layer<SrcType,DstType>::succeededTiming(Tick busy_time)
+{
+ // we should have gone from idle or retry to busy in the tryTiming
+ // test
+ assert(state == BUSY);
+
+ // occupy the layer accordingly
+ occupyLayer(busy_time);
+}
+
+template <typename SrcType, typename DstType>
+void
+BaseXBar::Layer<SrcType,DstType>::failedTiming(SrcType* src_port,
+ Tick busy_time)
+{
+ // ensure no one got in between and tried to send something to
+ // this port
+ assert(waitingForPeer == NULL);
+
+ // if the source port is the current retrying one or not, we have
+ // failed in forwarding and should track that we are now waiting
+ // for the peer to send a retry
+ waitingForPeer = src_port;
+
+ // we should have gone from idle or retry to busy in the tryTiming
+ // test
+ assert(state == BUSY);
+
+ // occupy the bus accordingly
+ occupyLayer(busy_time);
+}
+
+template <typename SrcType, typename DstType>
+void
+BaseXBar::Layer<SrcType,DstType>::releaseLayer()
+{
+ // releasing the bus means we should now be idle
+ assert(state == BUSY);
+ assert(!releaseEvent.scheduled());
+
+ // update the state
+ state = IDLE;
+
+ // bus layer is now idle, so if someone is waiting we can retry
+ if (!waitingForLayer.empty()) {
+ // there is no point in sending a retry if someone is still
+ // waiting for the peer
+ if (waitingForPeer == NULL)
+ retryWaiting();
+ } else if (waitingForPeer == NULL && drainManager) {
+ 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;
+ }
+}
+
+template <typename SrcType, typename DstType>
+void
+BaseXBar::Layer<SrcType,DstType>::retryWaiting()
+{
+ // this should never be called with no one waiting
+ assert(!waitingForLayer.empty());
+
+ // we always go to retrying from idle
+ assert(state == IDLE);
+
+ // update the state
+ state = RETRY;
+
+ // set the retrying port to the front of the retry list and pop it
+ // off the list
+ SrcType* retryingPort = waitingForLayer.front();
+ waitingForLayer.pop_front();
+
+ // tell the port to retry, which in some cases ends up calling the
+ // layer again
+ retryingPort->sendRetry();
+
+ // 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
+ 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)));
+ }
+}
+
+template <typename SrcType, typename DstType>
+void
+BaseXBar::Layer<SrcType,DstType>::recvRetry()
+{
+ // we should never get a retry without having failed to forward
+ // something to this port
+ assert(waitingForPeer != NULL);
+
+ // add the port where the failed packet originated to the front of
+ // the waiting ports for the layer, this allows us to call retry
+ // on the port immediately if the crossbar layer is idle
+ waitingForLayer.push_front(waitingForPeer);
+
+ // we are no longer waiting for the peer
+ waitingForPeer = NULL;
+
+ // if the layer is idle, retry this port straight away, if we
+ // are busy, then simply let the port wait for its turn
+ if (state == IDLE) {
+ retryWaiting();
+ } else {
+ assert(state == BUSY);
+ }
+}
+
+PortID
+BaseXBar::findPort(Addr addr)
+{
+ // 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 the address map interval tree
+ auto i = portMap.find(addr);
+ if (i != portMap.end()) {
+ dest_id = i->second;
+ updatePortCache(dest_id, i->first);
+ return dest_id;
+ }
+
+ // Check if this matches the default range
+ if (useDefaultRange) {
+ if (defaultRange.contains(addr)) {
+ DPRINTF(AddrRanges, " found addr %#llx on default\n",
+ addr);
+ return defaultPortID;
+ }
+ } else if (defaultPortID != InvalidPortID) {
+ DPRINTF(AddrRanges, "Unable to find destination for addr %#llx, "
+ "will use default port\n", addr);
+ 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,
+ name());
+}
+
+/** Function called by the port when the crossbar is receiving a range change.*/
+void
+BaseXBar::recvRangeChange(PortID master_port_id)
+{
+ DPRINTF(AddrRanges, "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<bool>::const_iterator r = gotAddrRanges.begin();
+ while (gotAllAddrRanges && r != gotAddrRanges.end()) {
+ gotAllAddrRanges &= *r++;
+ }
+ if (gotAllAddrRanges)
+ DPRINTF(AddrRanges, "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 (ranges.size() != 1)
+ fatal("Crossbar %s may only have a single default range",
+ name());
+
+ 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 (auto 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++;
+ }
+ }
+
+ AddrRangeList ranges = masterPorts[master_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",
+ name(),
+ masterPorts[master_port_id]->getSlavePort().name(),
+ masterPorts[conflict_id]->getSlavePort().name());
+ }
+ }
+ }
+
+ // 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(AddrRanges, "Aggregating address ranges\n");
+ xbarRanges.clear();
+
+ // start out with the default range
+ if (useDefaultRange) {
+ if (!gotAddrRanges[defaultPortID])
+ fatal("Crossbar %s uses default range, but none provided",
+ name());
+
+ xbarRanges.push_back(defaultRange);
+ DPRINTF(AddrRanges, "-- Adding default %s\n",
+ defaultRange.to_string());
+ }
+
+ // merge all interleaved ranges and add any range that is not
+ // a subset of the default range
+ std::vector<AddrRange> intlv_ranges;
+ for (const auto& r: portMap) {
+ // 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(AddrRanges, "-- 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))) {
+ xbarRanges.push_back(merged_range);
+ DPRINTF(AddrRanges, "-- 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))) {
+ xbarRanges.push_back(r.first);
+ DPRINTF(AddrRanges, "-- Adding range %s\n",
+ r.first.to_string());
+ }
+ }
+ }
+
+ // if there is still interleaved ranges waiting to be merged,
+ // go ahead and do it
+ if (!intlv_ranges.empty()) {
+ DPRINTF(AddrRanges, "-- Merging range from %d ranges\n",
+ intlv_ranges.size());
+ AddrRange merged_range(intlv_ranges);
+ if (!(useDefaultRange && merged_range.isSubset(defaultRange))) {
+ xbarRanges.push_back(merged_range);
+ DPRINTF(AddrRanges, "-- Adding merged range %s\n",
+ merged_range.to_string());
+ }
+ }
+
+ // 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 (const auto& r: xbarRanges) {
+ // 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 (const auto& s: slavePorts)
+ s->sendRangeChange();
+ }
+
+ clearPortCache();
+}
+
+AddrRangeList
+BaseXBar::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 crossbar
+ // (CPU, cache, bridge etc) actually care about the ranges of the
+ // ports they are connected to
+
+ DPRINTF(AddrRanges, "Received address range request\n");
+
+ return xbarRanges;
+}
+
+void
+BaseXBar::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
+ for (int i = 0; i < MemCmd::NUM_MEM_CMDS; i++) {
+ MemCmd cmd(i);
+ const std::string &cstr = cmd.toString();
+ transDist.subname(i, cstr);
+ }
+
+ pktCount
+ .init(slavePorts.size(), masterPorts.size())
+ .name(name() + ".pkt_count")
+ .desc("Packet count per connected master and slave (bytes)")
+ .flags(total | nozero | nonan);
+
+ pktSize
+ .init(slavePorts.size(), masterPorts.size())
+ .name(name() + ".pkt_size")
+ .desc("Cumulative packet size per connected master and slave (bytes)")
+ .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());
+ }
+ }
+}
+
+template <typename SrcType, typename DstType>
+unsigned int
+BaseXBar::Layer<SrcType,DstType>::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 (state != IDLE) {
+ DPRINTF(Drain, "Crossbar not drained\n");
+ drainManager = dm;
+ return 1;
+ }
+ 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.
+ */
+template class BaseXBar::Layer<SlavePort,MasterPort>;
+template class BaseXBar::Layer<MasterPort,SlavePort>;
--- /dev/null
+/*
+ * Copyright (c) 2011-2014 ARM Limited
+ * All rights reserved
+ *
+ * The license below extends only to copyright in the software and shall
+ * not be construed as granting a license to any other intellectual
+ * property including but not limited to intellectual property relating
+ * to a hardware implementation of the functionality of the software
+ * licensed hereunder. You may use the software subject to the license
+ * terms below provided that you ensure that this notice is replicated
+ * unmodified and in its entirety in all distributions of the software,
+ * modified or unmodified, in source code or in binary form.
+ *
+ * Copyright (c) 2002-2005 The Regents of The University of Michigan
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met: redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer;
+ * redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution;
+ * neither the name of the copyright holders nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * 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: Ron Dreslinski
+ * Ali Saidi
+ * Andreas Hansson
+ * William Wang
+ */
+
+/**
+ * @file
+ * Declaration of an abstract crossbar base class.
+ */
+
+#ifndef __MEM_XBAR_HH__
+#define __MEM_XBAR_HH__
+
+#include <deque>
+
+#include "base/addr_range_map.hh"
+#include "base/types.hh"
+#include "mem/mem_object.hh"
+#include "params/BaseXBar.hh"
+#include "sim/stats.hh"
+
+/**
+ * The base crossbar contains the common elements of the non-coherent
+ * and coherent crossbar. It is an abstract class that does not have
+ * any of the functionality relating to the actual reception and
+ * transmission of packets, as this is left for the subclasses.
+ *
+ * The BaseXBar is responsible for the basic flow control (busy or
+ * not), the administration of retries, and the address decoding.
+ */
+class BaseXBar : public MemObject
+{
+
+ protected:
+
+ /**
+ * A layer is an internal crossbar arbitration point with its own
+ * flow control. Each layer is a converging multiplexer tree. By
+ * instantiating one layer per destination port (and per packet
+ * type, i.e. request, response, snoop request and snoop
+ * response), we model full crossbar structures like AXI, ACE,
+ * PCIe, etc.
+ *
+ * The template parameter, PortClass, indicates the destination
+ * port type for the layer. The retry list holds either master
+ * ports or slave ports, depending on the direction of the
+ * layer. Thus, a request layer has a retry list containing slave
+ * ports, whereas a response layer holds master ports.
+ */
+ template <typename SrcType, typename DstType>
+ class Layer : public Drainable
+ {
+
+ public:
+
+ /**
+ * Create a layer and give it a name. The layer uses
+ * the crossbar an event manager.
+ *
+ * @param _port destination port the layer converges at
+ * @param _xbar the crossbar this layer belongs to
+ * @param _name the layer's name
+ */
+ Layer(DstType& _port, BaseXBar& _xbar, const std::string& _name);
+
+ /**
+ * Drain according to the normal semantics, so that the crossbar
+ * can tell the layer to drain, and pass an event to signal
+ * back when drained.
+ *
+ * @param de drain event to call once drained
+ *
+ * @return 1 if busy or waiting to retry, or 0 if idle
+ */
+ unsigned int drain(DrainManager *dm);
+
+ /**
+ * Get the crossbar layer's name
+ */
+ const std::string name() const { return xbar.name() + _name; }
+
+
+ /**
+ * Determine if the layer 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 layer is
+ * updated accordingly.
+ *
+ * @param port Source port presenting the packet
+ *
+ * @return True if the layer accepts the packet
+ */
+ bool tryTiming(SrcType* src_port);
+
+ /**
+ * Deal with a destination port accepting a packet by potentially
+ * removing the source port from the retry list (if retrying) and
+ * occupying the layer accordingly.
+ *
+ * @param busy_time Time to spend as a result of a successful send
+ */
+ 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 layer
+ * accordingly.
+ *
+ * @param src_port Source port
+ * @param busy_time Time to spend as a result of a failed send
+ */
+ void failedTiming(SrcType* src_port, Tick busy_time);
+
+ /** Occupy the layer until until */
+ void occupyLayer(Tick until);
+
+ /**
+ * Send a retry to the port at the head of waitingForLayer. The
+ * caller must ensure that the list is not empty.
+ */
+ void retryWaiting();
+
+ /**
+ * Handle a retry from a neighbouring module. This wraps
+ * retryWaiting by verifying that there are ports waiting
+ * before calling retryWaiting.
+ */
+ void recvRetry();
+
+ /**
+ * Register stats for the layer
+ */
+ void regStats();
+
+ private:
+
+ /** The destination port this layer converges at. */
+ DstType& port;
+
+ /** The crossbar this layer is a part of. */
+ BaseXBar& xbar;
+
+ /** A name for this layer. */
+ std::string _name;
+
+ /**
+ * We declare an enum to track the state of the layer. The
+ * starting point is an idle state where the layer is waiting
+ * for a packet to arrive. Upon arrival, the layer
+ * transitions to the busy state, where it remains either
+ * until the packet transfer is done, or the header time is
+ * spent. Once the layer leaves the busy state, it can
+ * either go back to idle, if no packets have arrived while it
+ * was busy, or the layer goes on to retry the first port
+ * in waitingForLayer. A similar transition takes place from
+ * idle to retry if the layer 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
+ * layer, 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 layer */
+ State state;
+
+ /** manager to signal when drained */
+ DrainManager *drainManager;
+
+ /**
+ * A deque of ports that retry should be called on because
+ * the original send was delayed due to a busy layer.
+ */
+ std::deque<SrcType*> waitingForLayer;
+
+ /**
+ * Track who is waiting for the retry when receiving it from a
+ * peer. If no port is waiting NULL is stored.
+ */
+ SrcType* waitingForPeer;
+
+ /**
+ * Release the layer after being occupied and return to an
+ * idle state where we proceed to send a retry to any
+ * potential waiting port, or drain if asked to do so.
+ */
+ void releaseLayer();
+
+ /** event used to schedule a release of the layer */
+ EventWrapper<Layer, &Layer::releaseLayer> releaseEvent;
+
+ /**
+ * Stats for occupancy and utilization. These stats capture
+ * the time the layer spends in the busy state and are thus only
+ * relevant when the memory system is in timing mode.
+ */
+ Stats::Scalar occupancy;
+ Stats::Formula utilization;
+
+ };
+
+ /** cycles of overhead per transaction */
+ const Cycles headerCycles;
+ /** the width of the xbar in bytes */
+ const uint32_t width;
+
+ AddrRangeMap<PortID> portMap;
+
+ /** all contigous ranges seen by this crossbar */
+ AddrRangeList xbarRanges;
+
+ AddrRange defaultRange;
+
+ /**
+ * Function called by the port when the crossbar is recieving a
+ * range change.
+ *
+ * @param master_port_id id of the port that received the change
+ */
+ void recvRangeChange(PortID master_port_id);
+
+ /** Find which port connected to this crossbar (if any) should be
+ * given a packet with this address.
+ *
+ * @param addr Address to find port for.
+ * @return id of port that the packet should be sent out of.
+ */
+ PortID findPort(Addr addr);
+
+ // Cache for the findPort function storing recently used ports from portMap
+ struct PortCache {
+ bool valid;
+ PortID id;
+ AddrRange range;
+ };
+
+ PortCache portCache[3];
+
+ // Checks the cache and returns the id of the port that has the requested
+ // address within its range
+ inline PortID checkPortCache(Addr addr) const {
+ if (portCache[0].valid && portCache[0].range.contains(addr)) {
+ return portCache[0].id;
+ }
+ if (portCache[1].valid && portCache[1].range.contains(addr)) {
+ return portCache[1].id;
+ }
+ if (portCache[2].valid && portCache[2].range.contains(addr)) {
+ return portCache[2].id;
+ }
+
+ return InvalidPortID;
+ }
+
+ // Clears the earliest entry of the cache and inserts a new port entry
+ inline void updatePortCache(short id, const AddrRange& range) {
+ portCache[2].valid = portCache[1].valid;
+ portCache[2].id = portCache[1].id;
+ portCache[2].range = portCache[1].range;
+
+ portCache[1].valid = portCache[0].valid;
+ portCache[1].id = portCache[0].id;
+ portCache[1].range = portCache[0].range;
+
+ portCache[0].valid = true;
+ portCache[0].id = id;
+ portCache[0].range = range;
+ }
+
+ // Clears the cache. Needs to be called in constructor.
+ inline void clearPortCache() {
+ portCache[2].valid = false;
+ portCache[1].valid = false;
+ portCache[0].valid = false;
+ }
+
+ /**
+ * Return the address ranges the crossbar is responsible for.
+ *
+ * @return a list of non-overlapping address ranges
+ */
+ AddrRangeList getAddrRanges() const;
+
+ /**
+ * Calculate the timing parameters for the packet. Updates the
+ * firstWordDelay and lastWordDelay fields of the packet
+ * object with the relative number of ticks required to transmit
+ * the header and the first word, and the last word, respectively.
+ */
+ void calcPacketTiming(PacketPtr pkt);
+
+ /**
+ * Remember for each of the master ports of the crossbar if we got
+ * an address range from the connected slave. For convenience,
+ * also keep track of if we got ranges from all the slave modules
+ * or not.
+ */
+ std::vector<bool> gotAddrRanges;
+ bool gotAllAddrRanges;
+
+ /** The master and slave ports of the crossbar */
+ std::vector<SlavePort*> slavePorts;
+ std::vector<MasterPort*> masterPorts;
+
+ /** Port that handles requests that don't match any of the interfaces.*/
+ PortID defaultPortID;
+
+ /** If true, use address range provided by default device. Any
+ address not handled by another port and not in default device's
+ range will cause a fatal error. If false, just send all
+ addresses not handled by another port to default device. */
+ const bool useDefaultRange;
+
+ BaseXBar(const BaseXBarParams *p);
+
+ virtual ~BaseXBar();
+
+ /**
+ * Stats for transaction distribution and data passing through the
+ * crossbar. The transaction distribution is globally counting
+ * different types of commands. The packet count and total packet
+ * size are two-dimensional vectors that are indexed by the
+ * slave port and master port id (thus the neighbouring master and
+ * neighbouring slave), summing up both directions (request and
+ * response).
+ */
+ Stats::Vector transDist;
+ Stats::Vector2d pktCount;
+ Stats::Vector2d pktSize;
+
+ public:
+
+ virtual void init();
+
+ /** A function used to return the port associated with this object. */
+ BaseMasterPort& getMasterPort(const std::string& if_name,
+ PortID idx = InvalidPortID);
+ BaseSlavePort& getSlavePort(const std::string& if_name,
+ PortID idx = InvalidPortID);
+
+ virtual unsigned int drain(DrainManager *dm) = 0;
+
+ virtual void regStats();
+
+};
+
+#endif //__MEM_XBAR_HH__
raise TypeError, 'wrong number of arguments'
# VectorPort description object. Like Port, but represents a vector
-# of connections (e.g., as on a Bus).
+# of connections (e.g., as on a XBar).
class VectorPort(Port):
def __init__(self, *args):
self.isVec = True
class NodeType:
SYS = 0
CPU = 1
- BUS = 2
+ XBAR = 2
MEM = 3
DEV = 4
OTHER = 5
elif 'PioDevice' in dir(m5.objects) and \
isinstance(simNode, m5.objects.PioDevice):
return NodeType.DEV
- elif isinstance(simNode, m5.objects.BaseBus):
- return NodeType.BUS
+ elif isinstance(simNode, m5.objects.BaseXBar):
+ return NodeType.XBAR
elif isinstance(simNode, m5.objects.AbstractMemory):
return NodeType.MEM
else:
return (228, 231, 235)
elif nodeType == NodeType.CPU:
return (187, 198, 217)
- elif nodeType == NodeType.BUS:
+ elif nodeType == NodeType.XBAR:
return (111, 121, 140)
elif nodeType == NodeType.MEM:
return (94, 89, 88)
Returns:
A bus that CPUs should use to connect to the shared cache.
"""
- system.toL2Bus = CoherentBus(clk_domain=system.cpu_clk_domain)
+ system.toL2Bus = CoherentXBar(clk_domain=system.cpu_clk_domain)
system.l2c = L2Cache(clk_domain=system.cpu_clk_domain,
size='4MB', assoc=8)
system.l2c.cpu_side = system.toL2Bus.master
def create_system(self):
system = System(physmem = self.mem_class(),
- membus = CoherentBus(),
+ membus = CoherentXBar(),
mem_mode = self.mem_mode)
system.system_port = system.membus.slave
system.physmem.port = system.membus.master
# system simulated
system = System(cpu = cpus, funcmem = SimpleMemory(in_addr_map = False),
- funcbus = NoncoherentBus(),
+ funcbus = NoncoherentXBar(),
physmem = SimpleMemory(),
- membus = CoherentBus(width=16, snoop_filter = SnoopFilter()))
+ membus = CoherentXBar(width=16, snoop_filter = SnoopFilter()))
# Dummy voltage domain for all our clock domains
system.voltage_domain = VoltageDomain()
system.clk_domain = SrcClockDomain(clock = '1GHz',
system.cpu_clk_domain = SrcClockDomain(clock = '2GHz',
voltage_domain = system.voltage_domain)
-system.toL2Bus = CoherentBus(clk_domain = system.cpu_clk_domain, width=16,
- snoop_filter = SnoopFilter())
+system.toL2Bus = CoherentXBar(clk_domain = system.cpu_clk_domain, width=16,
+ snoop_filter = SnoopFilter())
system.l2c = L2Cache(clk_domain = system.cpu_clk_domain, size='64kB', assoc=8)
system.l2c.cpu_side = system.toL2Bus.master
system = System(cpu = cpus,
funcmem = SimpleMemory(in_addr_map = False),
physmem = SimpleMemory(null = True),
- funcbus = NoncoherentBus())
+ funcbus = NoncoherentXBar())
# Dummy voltage domain for all our clock domains
system.voltage_domain = VoltageDomain()
system.clk_domain = SrcClockDomain(clock = '1GHz',
# system simulated
system = System(cpu = cpus, funcmem = SimpleMemory(in_addr_map = False),
- funcbus = NoncoherentBus(),
+ funcbus = NoncoherentXBar(),
physmem = SimpleMemory(),
- membus = CoherentBus(width=16))
+ membus = CoherentXBar(width=16))
# Dummy voltage domain for all our clock domains
system.voltage_domain = VoltageDomain()
system.clk_domain = SrcClockDomain(clock = '1GHz',
system.cpu_clk_domain = SrcClockDomain(clock = '2GHz',
voltage_domain = system.voltage_domain)
-system.toL2Bus = CoherentBus(clk_domain = system.cpu_clk_domain, width=16)
+system.toL2Bus = CoherentXBar(clk_domain = system.cpu_clk_domain, width=16)
system.l2c = L2Cache(clk_domain = system.cpu_clk_domain, size='64kB', assoc=8)
system.l2c.cpu_side = system.toL2Bus.master
ruby_memory = ruby_config.generate("TwoLevel_SplitL1UnifiedL2.rb", nb_cores)
# system simulated
-system = System(cpu = cpus, physmem = ruby_memory, membus = CoherentBus(),
+system = System(cpu = cpus, physmem = ruby_memory, membus = CoherentXBar(),
mem_mode = "timing",
clk_domain = SrcClockDomain(clock = '1GHz'))
system = System(cpu = cpu,
physmem = ruby_memory,
- membus = CoherentBus(),
+ membus = CoherentXBar(),
mem_mode = "timing",
clk_domain = SrcClockDomain(clock = '1GHz'))
ruby_memory = ruby_config.generate("TwoLevel_SplitL1UnifiedL2.rb", nb_cores)
# system simulated
-system = System(cpu = cpus, physmem = ruby_memory, membus = CoherentBus(),
+system = System(cpu = cpus, physmem = ruby_memory, membus = CoherentXBar(),
clk_domain = SrcClockDomain(clock = '1GHz'))
# Create a seperate clock domain for components that should run at
# system simulated
system = System(cpu = cpu, physmem = DDR3_1600_x64(),
- membus = NoncoherentBus(width = 16),
+ membus = NoncoherentXBar(width = 16),
clk_domain = SrcClockDomain(clock = '1GHz',
voltage_domain =
VoltageDomain()))
# system simulated
system = System(cpu = cpu, physmem = SimpleMemory(),
- membus = NoncoherentBus(width = 16),
+ membus = NoncoherentXBar(width = 16),
clk_domain = SrcClockDomain(clock = '1GHz',
voltage_domain =
VoltageDomain()))