system.l2 = L2Cache(size = options.l2_size, assoc = options.l2_assoc,
block_size=options.cacheline_size)
- system.tol2bus = Bus()
+ system.tol2bus = CoherentBus()
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(Bus):
+class MemBus(CoherentBus):
badaddr_responder = BadAddr()
default = Self.badaddr_responder.pio
# generic system
mdesc = SysConfig()
self.readfile = mdesc.script()
- self.iobus = Bus(bus_id=0)
- self.membus = MemBus(bus_id=1)
+ self.iobus = NoncoherentBus()
+ self.membus = MemBus()
# By default the bridge responds to all addresses above the I/O
# base address (including the PCI config space)
self.bridge = Bridge(delay='50ns', nack_delay='4ns',
self.readfile = mdesc.script()
# Create pio bus to connect all device pio ports to rubymem's pio port
- self.piobus = Bus(bus_id=0)
+ self.piobus = NoncoherentBus()
#
# Pio functional accesses from devices need direct access to memory
# generic system
mdesc = SysConfig()
self.readfile = mdesc.script()
- self.iobus = Bus(bus_id=0)
- self.membus = MemBus(bus_id=1)
+ self.iobus = NoncoherentBus()
+ self.membus = MemBus()
self.bridge = Bridge(delay='50ns', nack_delay='4ns')
self.t1000 = T1000()
self.t1000.attachOnChipIO(self.membus)
mdesc = SysConfig()
self.readfile = mdesc.script()
- self.iobus = Bus(bus_id=0)
- self.membus = MemBus(bus_id=1)
+ self.iobus = NoncoherentBus()
+ self.membus = MemBus()
self.membus.badaddr_responder.warn_access = "warn"
self.bridge = Bridge(delay='50ns', nack_delay='4ns')
self.bridge.master = self.iobus.slave
# generic system
mdesc = SysConfig()
self.readfile = mdesc.script()
- self.iobus = Bus(bus_id=0)
- self.membus = MemBus(bus_id=1)
+ self.iobus = NoncoherentBus()
+ self.membus = MemBus()
self.bridge = Bridge(delay='50ns', nack_delay='4ns')
self.physmem = SimpleMemory(range = AddrRange('1GB'))
self.bridge.master = self.iobus.slave
interrupts_address_space_base = 0xa000000000000000
APIC_range_size = 1 << 12;
- x86_sys.membus = MemBus(bus_id=1)
+ x86_sys.membus = MemBus()
x86_sys.physmem.port = x86_sys.membus.master
# North Bridge
- x86_sys.iobus = Bus(bus_id=0)
+ x86_sys.iobus = NoncoherentBus()
x86_sys.bridge = Bridge(delay='50ns', nack_delay='4ns')
x86_sys.bridge.master = x86_sys.iobus.slave
x86_sys.bridge.slave = x86_sys.membus.master
def connectX86RubySystem(x86_sys):
# North Bridge
- x86_sys.piobus = Bus(bus_id=0)
+ x86_sys.piobus = NoncoherentBus()
#
# Pio functional accesses from devices need direct access to memory
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 = Bus(clock="500MHz", width=16)
+ new_bus = CoherentBus(clock="500MHz", width=16)
if (port.role == 'MASTER'):
new_bus.slave = port
attach_port = "master"
system = System(cpu = [CPUClass(cpu_id=i) for i in xrange(np)],
physmem = SimpleMemory(range=AddrRange("512MB")),
- membus = Bus(), mem_mode = test_mem_mode)
+ membus = CoherentBus(), mem_mode = test_mem_mode)
# Sanity check
if options.fastmem and (options.caches or options.l2cache):
for j in xrange(options.numclusters):
clusters[j].id = j
for cluster in clusters:
- cluster.clusterbus = Bus(clock=busFrequency)
+ cluster.clusterbus = CoherentBus(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 = Bus(clock=busFrequency)
+ cluster.clusterbus = CoherentBus(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 = Bus(clock=busFrequency)
+ cluster.clusterbus = CoherentBus(clock=busFrequency)
all_l1buses += [cluster.clusterbus]
cluster.cpus = [AtomicSimpleCPU(cpu_id = i + cluster.id,
clock=options.frequency)
# Create a system, and add system wide objects
# ----------------------
system = System(cpu = all_cpus, l1_ = all_l1s, l1bus_ = all_l1buses,
- physmem = SimpleMemory(), membus = Bus(clock = busFrequency))
+ physmem = SimpleMemory(),
+ membus = CoherentBus(clock = busFrequency))
-system.toL2bus = Bus(clock = busFrequency)
+system.toL2bus = CoherentBus(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 = Bus(clock = busFrequency))
+ membus = CoherentBus(clock = busFrequency))
-system.toL2bus = Bus(clock = busFrequency)
+system.toL2bus = CoherentBus(clock = busFrequency)
system.l2 = L2(size = options.l2size, assoc = 8)
# ----------------------
from m5.params import *
from m5.proxy import *
-from Bus import Bus
+from Bus import CoherentBus
from InstTracer import InstTracer
from ExeTracer import ExeTracer
from MemObject import MemObject
def addTwoLevelCacheHierarchy(self, ic, dc, l2c, iwc = None, dwc = None):
self.addPrivateSplitL1Caches(ic, dc, iwc, dwc)
- self.toL2Bus = Bus()
+ self.toL2Bus = CoherentBus()
self.connectCachedPorts(self.toL2Bus)
self.l2cache = l2c
self.toL2Bus.master = self.l2cache.cpu_side
+# 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.
#
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#
# Authors: Nathan Binkert
+# Andreas Hansson
-from m5.defines import buildEnv
-from m5.params import *
-from m5.proxy import *
from MemObject import MemObject
+from m5.params import *
-class Bus(MemObject):
- type = 'Bus'
+class BaseBus(MemObject):
+ type = 'BaseBus'
+ abstract = True
slave = VectorSlavePort("vector port for connecting masters")
master = VectorMasterPort("vector port for connecting slaves")
- bus_id = Param.Int(0, "blah")
clock = Param.Clock("1GHz", "bus clock speed")
header_cycles = Param.Int(1, "cycles of overhead per transaction")
width = Param.Int(64, "bus width (bytes)")
- block_size = Param.Int(64, "The default block size if one isn't set by a device attached to the bus.")
- default = MasterPort("Default port for requests that aren't handled " \
- "by a device.")
- use_default_range = \
- Param.Bool(False, "Query default port device for legal range.")
+ block_size = Param.Int(64, "The default block size if not set by " \
+ "any connected module")
+
+ # 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'
+
+class CoherentBus(BaseBus):
+ type = 'CoherentBus'
Source('bridge.cc')
Source('bus.cc')
+Source('coherent_bus.cc')
Source('comm_monitor.cc')
Source('mem_object.cc')
Source('mport.cc')
+Source('noncoherent_bus.cc')
Source('packet.cc')
Source('port.cc')
Source('packet_queue.cc')
Source('page_table.cc')
Source('physical.cc')
-DebugFlag('Bus')
+DebugFlag('BaseBus')
DebugFlag('BusAddrRanges')
+DebugFlag('CoherentBus')
+DebugFlag('NoncoherentBus')
+CompoundFlag('Bus', ['BaseBus', 'BusAddrRanges', 'CoherentBus',
+ 'NoncoherentBus'])
+
DebugFlag('BusBridge')
DebugFlag('CommMonitor')
DebugFlag('LLSC')
#include "debug/BusAddrRanges.hh"
#include "mem/bus.hh"
-Bus::Bus(const BusParams *p)
+BaseBus::BaseBus(const BaseBusParams *p)
: MemObject(p), clock(p->clock),
headerCycles(p->header_cycles), width(p->width), tickNextIdle(0),
drainEvent(NULL), busIdleEvent(this), inRetry(false),
fatal("Bus clock period must be positive\n");
if (headerCycles <= 0)
fatal("Number of header cycles must be positive\n");
+}
- // create the ports based on the size of the master and slave
- // vector ports, and the presence of the default port, the ports
- // are enumerated starting from zero
- for (int i = 0; i < p->port_master_connection_count; ++i) {
- std::string portName = csprintf("%s-p%d", name(), i);
- MasterPort* bp = new BusMasterPort(portName, this, i);
- masterPorts.push_back(bp);
- }
-
- // see if we have a default slave device connected and if so add
- // our corresponding master port
- if (p->port_default_connection_count) {
- defaultPortID = masterPorts.size();
- std::string portName = csprintf("%s-default", name());
- MasterPort* bp = new BusMasterPort(portName, this, defaultPortID);
- masterPorts.push_back(bp);
+BaseBus::~BaseBus()
+{
+ for (MasterPortIter m = masterPorts.begin(); m != masterPorts.end();
+ ++m) {
+ delete *m;
}
- // create the slave ports, once again starting at zero
- for (int i = 0; i < p->port_slave_connection_count; ++i) {
- std::string portName = csprintf("%s-p%d", name(), i);
- SlavePort* bp = new BusSlavePort(portName, this, i);
- slavePorts.push_back(bp);
+ for (SlavePortIter s = slavePorts.begin(); s != slavePorts.end();
+ ++s) {
+ delete *s;
}
-
- clearPortCache();
}
MasterPort &
-Bus::getMasterPort(const std::string &if_name, int idx)
+BaseBus::getMasterPort(const std::string &if_name, int idx)
{
if (if_name == "master" && idx < masterPorts.size()) {
// the master port index translates directly to the vector position
}
SlavePort &
-Bus::getSlavePort(const std::string &if_name, int idx)
+BaseBus::getSlavePort(const std::string &if_name, int idx)
{
if (if_name == "slave" && idx < slavePorts.size()) {
// the slave port index translates directly to the vector position
}
}
-void
-Bus::init()
-{
- // iterate over our slave ports and determine which of our
- // neighbouring master ports are snooping and add them as snoopers
- for (SlavePortConstIter p = slavePorts.begin(); p != slavePorts.end();
- ++p) {
- if ((*p)->getMasterPort().isSnooping()) {
- DPRINTF(BusAddrRanges, "Adding snooping neighbour %s\n",
- (*p)->getMasterPort().name());
- snoopPorts.push_back(*p);
- }
- }
-}
-
Tick
-Bus::calcPacketTiming(PacketPtr pkt)
+BaseBus::calcPacketTiming(PacketPtr pkt)
{
// determine the current time rounded to the closest following
// clock edge
return headerTime;
}
-void Bus::occupyBus(Tick until)
+void BaseBus::occupyBus(Tick until)
{
if (until == 0) {
// shortcut for express snoop packets
tickNextIdle = until;
reschedule(busIdleEvent, tickNextIdle, true);
- DPRINTF(Bus, "The bus is now occupied from tick %d to %d\n",
+ DPRINTF(BaseBus, "The bus is now occupied from tick %d to %d\n",
curTick(), tickNextIdle);
}
bool
-Bus::isOccupied(Port* port)
+BaseBus::isOccupied(Port* port)
{
// first we see if the next idle tick is in the future, next the
// bus is considered occupied if there are ports on the retry list
return false;
}
-bool
-Bus::recvTimingReq(PacketPtr pkt, PortID slave_port_id)
-{
- // determine the source port based on the id
- SlavePort *src_port = slavePorts[slave_port_id];
-
- // test if the bus should be considered occupied for the current
- // port, and exclude express snoops from the check
- if (!pkt->isExpressSnoop() && isOccupied(src_port)) {
- DPRINTF(Bus, "recvTimingReq: src %s %s 0x%x BUSY\n",
- src_port->name(), pkt->cmdString(), pkt->getAddr());
- return false;
- }
-
- DPRINTF(Bus, "recvTimingReq: src %s %s 0x%x\n",
- src_port->name(), pkt->cmdString(), pkt->getAddr());
-
- // set the source port for routing of the response
- pkt->setSrc(slave_port_id);
-
- Tick headerFinishTime = pkt->isExpressSnoop() ? 0 : calcPacketTiming(pkt);
- Tick packetFinishTime = pkt->isExpressSnoop() ? 0 : pkt->finishTime;
-
- // uncacheable requests need never be snooped
- if (!pkt->req->isUncacheable()) {
- // the packet is a memory-mapped request and should be
- // broadcasted to our snoopers but the source
- 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 = !pkt->isExpressSnoop() && pkt->needsResponse();
-
- // keep track that we have an outstanding request packet
- // matching this request, this is used by the coherency
- // mechanism in determining what to do with snoop responses
- // (in recvTimingSnoop)
- if (add_outstanding) {
- // we should never have an exsiting request outstanding
- assert(outstandingReq.find(pkt->req) == outstandingReq.end());
- outstandingReq.insert(pkt->req);
- }
-
- // since it is a normal request, determine the destination
- // based on the address and attempt to send the packet
- bool success = masterPorts[findPort(pkt->getAddr())]->sendTimingReq(pkt);
-
- if (!success) {
- // inhibited packets should never be forced to retry
- assert(!pkt->memInhibitAsserted());
-
- // if it was added as outstanding and the send failed, then
- // erase it again
- if (add_outstanding)
- outstandingReq.erase(pkt->req);
-
- DPRINTF(Bus, "recvTimingReq: src %s %s 0x%x RETRY\n",
- src_port->name(), pkt->cmdString(), pkt->getAddr());
-
- addToRetryList(src_port);
- occupyBus(headerFinishTime);
-
- return false;
- }
-
- succeededTiming(packetFinishTime);
-
- return true;
-}
-
-bool
-Bus::recvTimingResp(PacketPtr pkt, PortID master_port_id)
-{
- // determine the source port based on the id
- MasterPort *src_port = masterPorts[master_port_id];
-
- // test if the bus should be considered occupied for the current
- // port
- if (isOccupied(src_port)) {
- DPRINTF(Bus, "recvTimingResp: src %s %s 0x%x BUSY\n",
- src_port->name(), pkt->cmdString(), pkt->getAddr());
- return false;
- }
-
- DPRINTF(Bus, "recvTimingResp: src %s %s 0x%x\n",
- src_port->name(), pkt->cmdString(), pkt->getAddr());
-
- calcPacketTiming(pkt);
- Tick packetFinishTime = pkt->finishTime;
-
- // the packet is a normal response to a request that we should
- // have seen passing through the bus
- assert(outstandingReq.find(pkt->req) != outstandingReq.end());
-
- // remove it as outstanding
- outstandingReq.erase(pkt->req);
-
- // send the packet to the destination through one of our slave
- // ports, as determined by the destination field
- bool success M5_VAR_USED = slavePorts[pkt->getDest()]->sendTimingResp(pkt);
-
- // currently it is illegal to block responses... can lead to
- // deadlock
- assert(success);
-
- succeededTiming(packetFinishTime);
-
- return true;
-}
-
void
-Bus::recvTimingSnoopReq(PacketPtr pkt, PortID master_port_id)
-{
- DPRINTF(Bus, "recvTimingSnoopReq: src %s %s 0x%x\n",
- masterPorts[master_port_id]->name(), pkt->cmdString(),
- pkt->getAddr());
-
- // 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);
-
- // forward to all snoopers
- 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()));
-
- // this is an express snoop and is never forced to retry
- assert(!inRetry);
-}
-
-bool
-Bus::recvTimingSnoopResp(PacketPtr pkt, PortID slave_port_id)
-{
- // determine the source port based on the id
- SlavePort* src_port = slavePorts[slave_port_id];
-
- // test if the bus should be considered occupied for the current
- // port
- if (isOccupied(src_port)) {
- DPRINTF(Bus, "recvTimingSnoopResp: src %s %s 0x%x BUSY\n",
- src_port->name(), pkt->cmdString(), pkt->getAddr());
- return false;
- }
-
- DPRINTF(Bus, "recvTimingSnoop: src %s %s 0x%x\n",
- src_port->name(), pkt->cmdString(), pkt->getAddr());
-
- // get the destination from the packet
- PortID dest = pkt->getDest();
-
- // responses are never express snoops
- assert(!pkt->isExpressSnoop());
-
- calcPacketTiming(pkt);
- Tick packetFinishTime = pkt->finishTime;
-
- // determine if the response is from a snoop request we
- // created as the result of a normal request (in which case it
- // should be in the outstandingReq), or if we merely forwarded
- // someone else's snoop request
- if (outstandingReq.find(pkt->req) == outstandingReq.end()) {
- // this is a snoop response to a snoop request we
- // forwarded, e.g. coming from the L1 and going to the L2
- // this should be forwarded as a snoop response
- bool success M5_VAR_USED = masterPorts[dest]->sendTimingSnoopResp(pkt);
- assert(success);
- } else {
- // we got a snoop response on one of our slave ports,
- // i.e. from a coherent master connected to the bus, and
- // since we created the snoop request as part of
- // recvTiming, this should now be a normal response again
- outstandingReq.erase(pkt->req);
-
- // this is a snoop response from a coherent master, with a
- // destination field set on its way through the bus as
- // request, hence it should never go back to where the
- // snoop response came from, but instead to where the
- // original request came from
- assert(slave_port_id != dest);
-
- // as a normal response, it should go back to a master
- // through one of our slave ports
- bool success M5_VAR_USED = slavePorts[dest]->sendTimingResp(pkt);
-
- // currently it is illegal to block responses... can lead
- // to deadlock
- assert(success);
- }
-
- succeededTiming(packetFinishTime);
-
- return true;
-}
-
-
-void
-Bus::succeededTiming(Tick busy_time)
+BaseBus::succeededTiming(Tick busy_time)
{
// occupy the bus accordingly
occupyBus(busy_time);
// if a retrying port succeeded, also take it off the retry list
if (inRetry) {
- DPRINTF(Bus, "Remove retry from list %s\n",
+ DPRINTF(BaseBus, "Remove retry from list %s\n",
retryList.front()->name());
retryList.pop_front();
inRetry = false;
}
void
-Bus::forwardTiming(PacketPtr pkt, PortID exclude_slave_port_id)
-{
- for (SlavePortIter s = snoopPorts.begin(); s != snoopPorts.end(); ++s) {
- SlavePort *p = *s;
- // we could have gotten this request from a snooping master
- // (corresponding to our own slave port that is also in
- // snoopPorts) and should not send it back to where it came
- // from
- if (exclude_slave_port_id == InvalidPortID ||
- p->getId() != exclude_slave_port_id) {
- // cache is not allowed to refuse snoop
- p->sendTimingSnoopReq(pkt);
- }
- }
-}
-
-void
-Bus::releaseBus()
+BaseBus::releaseBus()
{
// releasing the bus means we should now be idle
assert(curTick() >= tickNextIdle);
}
void
-Bus::retryWaiting()
+BaseBus::retryWaiting()
{
// this should never be called with an empty retry list
assert(!retryList.empty());
}
void
-Bus::recvRetry()
+BaseBus::recvRetry()
{
// we got a retry from a peer that we tried to send something to
// and failed, but we sent it on the account of someone else, and
}
PortID
-Bus::findPort(Addr addr)
+BaseBus::findPort(Addr addr)
{
/* An interval tree would be a better way to do this. --ali. */
PortID dest_id = checkPortCache(addr);
AddrRangeIter a_end = defaultRange.end();
for (AddrRangeIter i = defaultRange.begin(); i != a_end; i++) {
if (*i == addr) {
- DPRINTF(Bus, " found addr %#llx on default\n", addr);
+ DPRINTF(BusAddrRanges, " found addr %#llx on default\n",
+ addr);
return defaultPortID;
}
}
} else if (defaultPortID != InvalidPortID) {
- DPRINTF(Bus, "Unable to find destination for addr %#llx, "
+ DPRINTF(BusAddrRanges, "Unable to find destination for addr %#llx, "
"will use default port\n", addr);
return defaultPortID;
}
name());
}
-Tick
-Bus::recvAtomic(PacketPtr pkt, PortID slave_port_id)
-{
- DPRINTF(Bus, "recvAtomic: packet src %s addr 0x%x cmd %s\n",
- slavePorts[slave_port_id]->name(), pkt->getAddr(),
- pkt->cmdString());
-
- MemCmd snoop_response_cmd = MemCmd::InvalidCmd;
- Tick snoop_response_latency = 0;
-
- // uncacheable requests need never be snooped
- if (!pkt->req->isUncacheable()) {
- // forward to all snoopers but the source
- std::pair<MemCmd, Tick> 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);
-
- // if we got a response from a snooper, restore it here
- if (snoop_response_cmd != MemCmd::InvalidCmd) {
- // no one else should have responded
- assert(!pkt->isResponse());
- pkt->cmd = snoop_response_cmd;
- response_latency = snoop_response_latency;
- }
-
- pkt->finishTime = curTick() + response_latency;
- return response_latency;
-}
-
-Tick
-Bus::recvAtomicSnoop(PacketPtr pkt, PortID master_port_id)
-{
- DPRINTF(Bus, "recvAtomicSnoop: packet src %s addr 0x%x cmd %s\n",
- masterPorts[master_port_id]->name(), pkt->getAddr(),
- pkt->cmdString());
-
- // forward to all snoopers
- std::pair<MemCmd, Tick> snoop_result =
- forwardAtomic(pkt, InvalidPortID);
- MemCmd snoop_response_cmd = snoop_result.first;
- Tick snoop_response_latency = snoop_result.second;
-
- if (snoop_response_cmd != MemCmd::InvalidCmd)
- pkt->cmd = snoop_response_cmd;
-
- pkt->finishTime = curTick() + snoop_response_latency;
- return snoop_response_latency;
-}
-
-std::pair<MemCmd, Tick>
-Bus::forwardAtomic(PacketPtr pkt, PortID exclude_slave_port_id)
-{
- // 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;
-
- 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) {
- Tick latency = p->sendAtomicSnoop(pkt);
- // in contrast to a functional access, we have to keep on
- // going as all snoopers must be updated even if we get a
- // response
- if (pkt->isResponse()) {
- // response from snoop agent
- assert(pkt->cmd != orig_cmd);
- assert(pkt->memInhibitAsserted());
- // should only happen once
- assert(snoop_response_cmd == MemCmd::InvalidCmd);
- // save response state
- snoop_response_cmd = pkt->cmd;
- snoop_response_latency = latency;
- // restore original packet state for remaining snoopers
- pkt->cmd = orig_cmd;
- }
- }
- }
-
- // the packet is restored as part of the loop and any potential
- // snoop response is part of the returned pair
- return std::make_pair(snoop_response_cmd, snoop_response_latency);
-}
-
-void
-Bus::recvFunctional(PacketPtr pkt, PortID slave_port_id)
-{
- if (!pkt->isPrint()) {
- // don't do DPRINTFs on PrintReq as it clutters up the output
- DPRINTF(Bus,
- "recvFunctional: packet src %s addr 0x%x cmd %s\n",
- slavePorts[slave_port_id]->name(), pkt->getAddr(),
- pkt->cmdString());
- }
-
- // uncacheable requests need never be snooped
- if (!pkt->req->isUncacheable()) {
- // 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
-Bus::recvFunctionalSnoop(PacketPtr pkt, PortID master_port_id)
-{
- if (!pkt->isPrint()) {
- // don't do DPRINTFs on PrintReq as it clutters up the output
- DPRINTF(Bus,
- "recvFunctionalSnoop: packet src %s addr 0x%x cmd %s\n",
- masterPorts[master_port_id]->name(), pkt->getAddr(),
- pkt->cmdString());
- }
-
- // forward to all snoopers
- forwardFunctional(pkt, InvalidPortID);
-}
-
-void
-Bus::forwardFunctional(PacketPtr pkt, PortID exclude_slave_port_id)
-{
- for (SlavePortIter s = snoopPorts.begin(); s != snoopPorts.end(); ++s) {
- SlavePort *p = *s;
- // we could have gotten this request from a snooping master
- // (corresponding to our own slave port that is also in
- // snoopPorts) and should not send it back to where it came
- // from
- if (exclude_slave_port_id == InvalidPortID ||
- p->getId() != exclude_slave_port_id)
- p->sendFunctionalSnoop(pkt);
-
- // if we get a response we are done
- if (pkt->isResponse()) {
- break;
- }
- }
-}
-
/** Function called by the port when the bus is receiving a range change.*/
void
-Bus::recvRangeChange(PortID master_port_id)
+BaseBus::recvRangeChange(PortID master_port_id)
{
AddrRangeList ranges;
AddrRangeIter iter;
}
AddrRangeList
-Bus::getAddrRanges()
+BaseBus::getAddrRanges()
{
AddrRangeList ranges;
return ranges;
}
-bool
-Bus::isSnooping() const
-{
- // in essence, answer the question if there are snooping ports
- return !snoopPorts.empty();
-}
-
unsigned
-Bus::findBlockSize()
+BaseBus::findBlockSize()
{
if (cachedBlockSizeValid)
return cachedBlockSize;
unsigned max_bs = 0;
- PortIter p_end = portMap.end();
- for (PortIter p_iter = portMap.begin(); p_iter != p_end; p_iter++) {
- unsigned tmp_bs = masterPorts[p_iter->second]->peerBlockSize();
+ for (MasterPortConstIter m = masterPorts.begin(); m != masterPorts.end();
+ ++m) {
+ unsigned tmp_bs = (*m)->peerBlockSize();
if (tmp_bs > max_bs)
max_bs = tmp_bs;
}
- for (SlavePortConstIter s = snoopPorts.begin(); s != snoopPorts.end();
+ for (SlavePortConstIter s = slavePorts.begin(); s != slavePorts.end();
++s) {
unsigned tmp_bs = (*s)->peerBlockSize();
if (tmp_bs > max_bs)
unsigned int
-Bus::drain(Event * de)
+BaseBus::drain(Event * de)
{
//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
}
void
-Bus::startup()
+BaseBus::startup()
{
if (tickNextIdle < curTick())
tickNextIdle = (curTick() / clock) * clock + clock;
}
-
-Bus *
-BusParams::create()
-{
- return new Bus(this);
-}
/**
* @file
- * Declaration of a bus object.
+ * Declaration of an abstract bus base class.
*/
#ifndef __MEM_BUS_HH__
#include <list>
#include <set>
-#include <string>
#include "base/range.hh"
#include "base/range_map.hh"
#include "base/types.hh"
#include "mem/mem_object.hh"
-#include "mem/packet.hh"
-#include "mem/port.hh"
-#include "params/Bus.hh"
-#include "sim/eventq.hh"
+#include "params/BaseBus.hh"
-class Bus : public MemObject
+/**
+ * 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
{
- /**
- * Declaration of the bus slave port type, one will be
- * instantiated for each of the master interfaces connecting to
- * the bus.
- */
- class BusSlavePort : public SlavePort
- {
- private:
- /** A pointer to the bus to which this port belongs. */
- Bus *bus;
-
- public:
-
- /** Constructor for the BusSlavePort.*/
- BusSlavePort(const std::string &_name, Bus *_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"); }
-
- // This should return all the 'owned' addresses that are
- // downstream from this bus, yes? That is, the union of all
- // the 'owned' address ranges of all the other interfaces on
- // this bus...
- virtual AddrRangeList getAddrRanges()
- { return bus->getAddrRanges(); }
-
- // Ask the bus to ask everyone on the bus what their block size is and
- // take the max of it. This might need to be changed a bit if we ever
- // support multiple block sizes.
- virtual unsigned deviceBlockSize() const
- { return bus->findBlockSize(); }
-
- };
-
- /**
- * Declaration of the bus master port type, one will be
- * instantiated for each of the slave interfaces connecting to the
- * bus.
- */
- class BusMasterPort : public MasterPort
- {
- private:
- /** A pointer to the bus to which this port belongs. */
- Bus *bus;
-
- public:
-
- /** Constructor for the BusMasterPort.*/
- BusMasterPort(const std::string &_name, Bus *_bus, PortID _id)
- : MasterPort(_name, _bus, _id), bus(_bus)
- { }
-
- /**
- * Determine if this port should be considered a snooper. This
- * is determined by the bus.
- *
- * @return a boolean that is true if this port is snooping
- */
- virtual bool isSnooping() const
- { return bus->isSnooping(); }
-
- protected:
-
- /**
- * 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(); }
-
- // Ask the bus to ask everyone on the bus what their block size is and
- // take the max of it. This might need to be changed a bit if we ever
- // support multiple block sizes.
- virtual unsigned deviceBlockSize() const
- { return bus->findBlockSize(); }
-
- };
+ protected:
/** the clock speed for the bus */
int clock;
AddrRangeList defaultRange;
- 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.
- */
- std::set<RequestPtr> outstandingReq;
-
- /** 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);
-
- /**
- * 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);
-
/**
* Determine if the bus is to be considered occupied when being
* presented with a packet from a specific port. If so, the port
*/
void succeededTiming(Tick busy_time);
- /** 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);
-
- /** 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);
-
/** Timing function called by port when it is once again able to process
* requests. */
void recvRetry();
*/
AddrRangeList getAddrRanges();
- /**
- * Determine if the bus port is snooping or not.
- *
- * @return a boolean indicating if this port is snooping or not
- */
- bool isSnooping() const;
-
/** Calculate the timing parameters for the packet. Updates the
* firstWordTime and finishTime fields of the packet object.
* Returns the tick at which the packet header is completed (which
unsigned findBlockSize();
// event used to schedule a release of the bus
- EventWrapper<Bus, &Bus::releaseBus> busIdleEvent;
+ EventWrapper<BaseBus, &BaseBus::releaseBus> busIdleEvent;
bool inRetry;
std::set<PortID> inRecvRangeChange;
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;
/** An array of pointers to ports that retry should be called on because the
* original send failed for whatever reason.*/
unsigned cachedBlockSize;
bool cachedBlockSizeValid;
+ BaseBus(const BaseBusParams *p);
+
+ virtual ~BaseBus();
+
public:
/** A function used to return the port associated with this bus object. */
virtual MasterPort& getMasterPort(const std::string& if_name, int idx = -1);
virtual SlavePort& getSlavePort(const std::string& if_name, int idx = -1);
- virtual void init();
virtual void startup();
unsigned int drain(Event *de);
- Bus(const BusParams *p);
};
#endif //__MEM_BUS_HH__
--- /dev/null
+/*
+ * Copyright (c) 2011-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) 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"
+
+CoherentBus::CoherentBus(const CoherentBusParams *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-p%d", name(), i);
+ MasterPort* bp = new CoherentBusMasterPort(portName, *this, i);
+ masterPorts.push_back(bp);
+ }
+
+ // see if we have a default slave device connected and if so add
+ // our corresponding master port
+ if (p->port_default_connection_count) {
+ defaultPortID = masterPorts.size();
+ std::string portName = csprintf("%s-default", name());
+ MasterPort* bp = new CoherentBusMasterPort(portName, *this,
+ defaultPortID);
+ masterPorts.push_back(bp);
+ }
+
+ // create the slave ports, once again starting at zero
+ for (int i = 0; i < p->port_slave_connection_count; ++i) {
+ std::string portName = csprintf("%s-p%d", name(), i);
+ SlavePort* bp = new CoherentBusSlavePort(portName, *this, i);
+ slavePorts.push_back(bp);
+ }
+
+ clearPortCache();
+}
+
+void
+CoherentBus::init()
+{
+ // iterate over our slave ports and determine which of our
+ // neighbouring master ports are snooping and add them as snoopers
+ for (SlavePortConstIter p = slavePorts.begin(); p != slavePorts.end();
+ ++p) {
+ if ((*p)->getMasterPort().isSnooping()) {
+ DPRINTF(BusAddrRanges, "Adding snooping 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];
+
+ // test if the bus should be considered occupied for the current
+ // port, and exclude express snoops from the check
+ if (!pkt->isExpressSnoop() && isOccupied(src_port)) {
+ DPRINTF(CoherentBus, "recvTimingReq: src %s %s 0x%x BUSY\n",
+ src_port->name(), pkt->cmdString(), pkt->getAddr());
+ return false;
+ }
+
+ DPRINTF(CoherentBus, "recvTimingReq: src %s %s 0x%x\n",
+ src_port->name(), pkt->cmdString(), pkt->getAddr());
+
+ // set the source port for routing of the response
+ pkt->setSrc(slave_port_id);
+
+ Tick headerFinishTime = pkt->isExpressSnoop() ? 0 : calcPacketTiming(pkt);
+ Tick packetFinishTime = pkt->isExpressSnoop() ? 0 : pkt->finishTime;
+
+ // uncacheable requests need never be snooped
+ if (!pkt->req->isUncacheable()) {
+ // the packet is a memory-mapped request and should be
+ // broadcasted to our snoopers but the source
+ 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 = !pkt->isExpressSnoop() && pkt->needsResponse();
+
+ // keep track that we have an outstanding request packet
+ // matching this request, this is used by the coherency
+ // mechanism in determining what to do with snoop responses
+ // (in recvTimingSnoop)
+ if (add_outstanding) {
+ // we should never have an exsiting request outstanding
+ assert(outstandingReq.find(pkt->req) == outstandingReq.end());
+ outstandingReq.insert(pkt->req);
+ }
+
+ // since it is a normal request, determine the destination
+ // based on the address and attempt to send the packet
+ bool success = masterPorts[findPort(pkt->getAddr())]->sendTimingReq(pkt);
+
+ if (!success) {
+ // inhibited packets should never be forced to retry
+ assert(!pkt->memInhibitAsserted());
+
+ // if it was added as outstanding and the send failed, then
+ // erase it again
+ if (add_outstanding)
+ outstandingReq.erase(pkt->req);
+
+ DPRINTF(CoherentBus, "recvTimingReq: src %s %s 0x%x RETRY\n",
+ src_port->name(), pkt->cmdString(), pkt->getAddr());
+
+ addToRetryList(src_port);
+ occupyBus(headerFinishTime);
+
+ return false;
+ }
+
+ succeededTiming(packetFinishTime);
+
+ return true;
+}
+
+bool
+CoherentBus::recvTimingResp(PacketPtr pkt, PortID master_port_id)
+{
+ // determine the source port based on the id
+ MasterPort *src_port = masterPorts[master_port_id];
+
+ // test if the bus should be considered occupied for the current
+ // port
+ if (isOccupied(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());
+
+ calcPacketTiming(pkt);
+ Tick packetFinishTime = pkt->finishTime;
+
+ // the packet is a normal response to a request that we should
+ // have seen passing through the bus
+ assert(outstandingReq.find(pkt->req) != outstandingReq.end());
+
+ // remove it as outstanding
+ outstandingReq.erase(pkt->req);
+
+ // send the packet to the destination through one of our slave
+ // ports, as determined by the destination field
+ bool success M5_VAR_USED = slavePorts[pkt->getDest()]->sendTimingResp(pkt);
+
+ // currently it is illegal to block responses... can lead to
+ // deadlock
+ assert(success);
+
+ succeededTiming(packetFinishTime);
+
+ 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());
+
+ // 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);
+
+ // forward to all snoopers
+ 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()));
+
+ // this is an express snoop and is never forced to retry
+ assert(!inRetry);
+}
+
+bool
+CoherentBus::recvTimingSnoopResp(PacketPtr pkt, PortID slave_port_id)
+{
+ // determine the source port based on the id
+ SlavePort* src_port = slavePorts[slave_port_id];
+
+ // test if the bus should be considered occupied for the current
+ // port
+ if (isOccupied(src_port)) {
+ DPRINTF(CoherentBus, "recvTimingSnoopResp: src %s %s 0x%x BUSY\n",
+ src_port->name(), pkt->cmdString(), pkt->getAddr());
+ return false;
+ }
+
+ DPRINTF(CoherentBus, "recvTimingSnoop: src %s %s 0x%x\n",
+ src_port->name(), pkt->cmdString(), pkt->getAddr());
+
+ // get the destination from the packet
+ PortID dest = pkt->getDest();
+
+ // responses are never express snoops
+ assert(!pkt->isExpressSnoop());
+
+ calcPacketTiming(pkt);
+ Tick packetFinishTime = pkt->finishTime;
+
+ // determine if the response is from a snoop request we
+ // created as the result of a normal request (in which case it
+ // should be in the outstandingReq), or if we merely forwarded
+ // someone else's snoop request
+ if (outstandingReq.find(pkt->req) == outstandingReq.end()) {
+ // this is a snoop response to a snoop request we
+ // forwarded, e.g. coming from the L1 and going to the L2
+ // this should be forwarded as a snoop response
+ bool success M5_VAR_USED = masterPorts[dest]->sendTimingSnoopResp(pkt);
+ assert(success);
+ } else {
+ // we got a snoop response on one of our slave ports,
+ // i.e. from a coherent master connected to the bus, and
+ // since we created the snoop request as part of
+ // recvTiming, this should now be a normal response again
+ outstandingReq.erase(pkt->req);
+
+ // this is a snoop response from a coherent master, with a
+ // destination field set on its way through the bus as
+ // request, hence it should never go back to where the
+ // snoop response came from, but instead to where the
+ // original request came from
+ assert(slave_port_id != dest);
+
+ // as a normal response, it should go back to a master
+ // through one of our slave ports
+ bool success M5_VAR_USED = slavePorts[dest]->sendTimingResp(pkt);
+
+ // currently it is illegal to block responses... can lead
+ // to deadlock
+ assert(success);
+ }
+
+ succeededTiming(packetFinishTime);
+
+ return true;
+}
+
+
+void
+CoherentBus::forwardTiming(PacketPtr pkt, PortID exclude_slave_port_id)
+{
+ for (SlavePortIter s = snoopPorts.begin(); s != snoopPorts.end(); ++s) {
+ SlavePort *p = *s;
+ // we could have gotten this request from a snooping master
+ // (corresponding to our own slave port that is also in
+ // snoopPorts) and should not send it back to where it came
+ // from
+ if (exclude_slave_port_id == InvalidPortID ||
+ p->getId() != exclude_slave_port_id) {
+ // cache is not allowed to refuse snoop
+ p->sendTimingSnoopReq(pkt);
+ }
+ }
+}
+
+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());
+
+ MemCmd snoop_response_cmd = MemCmd::InvalidCmd;
+ Tick snoop_response_latency = 0;
+
+ // uncacheable requests need never be snooped
+ if (!pkt->req->isUncacheable()) {
+ // forward to all snoopers but the source
+ std::pair<MemCmd, Tick> 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);
+
+ // if we got a response from a snooper, restore it here
+ if (snoop_response_cmd != MemCmd::InvalidCmd) {
+ // no one else should have responded
+ assert(!pkt->isResponse());
+ pkt->cmd = snoop_response_cmd;
+ response_latency = snoop_response_latency;
+ }
+
+ pkt->finishTime = curTick() + response_latency;
+ return response_latency;
+}
+
+Tick
+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());
+
+ // forward to all snoopers
+ std::pair<MemCmd, Tick> snoop_result =
+ forwardAtomic(pkt, InvalidPortID);
+ MemCmd snoop_response_cmd = snoop_result.first;
+ Tick snoop_response_latency = snoop_result.second;
+
+ if (snoop_response_cmd != MemCmd::InvalidCmd)
+ pkt->cmd = snoop_response_cmd;
+
+ pkt->finishTime = curTick() + snoop_response_latency;
+ return snoop_response_latency;
+}
+
+std::pair<MemCmd, Tick>
+CoherentBus::forwardAtomic(PacketPtr pkt, PortID exclude_slave_port_id)
+{
+ // 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;
+
+ 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) {
+ Tick latency = p->sendAtomicSnoop(pkt);
+ // in contrast to a functional access, we have to keep on
+ // going as all snoopers must be updated even if we get a
+ // response
+ if (pkt->isResponse()) {
+ // response from snoop agent
+ assert(pkt->cmd != orig_cmd);
+ assert(pkt->memInhibitAsserted());
+ // should only happen once
+ assert(snoop_response_cmd == MemCmd::InvalidCmd);
+ // save response state
+ snoop_response_cmd = pkt->cmd;
+ snoop_response_latency = latency;
+ // restore original packet state for remaining snoopers
+ pkt->cmd = orig_cmd;
+ }
+ }
+ }
+
+ // 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()) {
+ // 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)
+{
+ 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;
+ }
+ }
+}
+
+CoherentBus *
+CoherentBusParams::create()
+{
+ return new CoherentBus(this);
+}
--- /dev/null
+/*
+ * Copyright (c) 2011-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) 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 "mem/bus.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.
+ */
+class CoherentBus : public BaseBus
+{
+
+ protected:
+
+ /**
+ * 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()
+ { return bus.getAddrRanges(); }
+
+ /**
+ * Get the maximum block size as seen by the bus.
+ */
+ virtual unsigned deviceBlockSize() const
+ { return bus.findBlockSize(); }
+
+ };
+
+ /**
+ * 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(); }
+
+ // Ask the bus to ask everyone on the bus what their block size is and
+ // take the max of it. This might need to be changed a bit if we ever
+ // support multiple block sizes.
+ virtual unsigned deviceBlockSize() const
+ { return bus.findBlockSize(); }
+
+ };
+
+ 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.
+ */
+ std::set<RequestPtr> outstandingReq;
+
+ /** 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);
+
+ /**
+ * 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);
+
+ /** 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);
+
+ /** 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);
+
+ public:
+
+ virtual void init();
+
+ CoherentBus(const CoherentBusParams *p);
+};
+
+#endif //__MEM_COHERENT_BUS_HH__
--- /dev/null
+/*
+ * Copyright (c) 2011-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) 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-p%d", name(), i);
+ MasterPort* bp = new NoncoherentBusMasterPort(portName, *this, i);
+ masterPorts.push_back(bp);
+ }
+
+ // see if we have a default slave device connected and if so add
+ // our corresponding master port
+ if (p->port_default_connection_count) {
+ defaultPortID = masterPorts.size();
+ std::string portName = csprintf("%s-default", name());
+ MasterPort* bp = new NoncoherentBusMasterPort(portName, *this,
+ defaultPortID);
+ masterPorts.push_back(bp);
+ }
+
+ // create the slave ports, once again starting at zero
+ for (int i = 0; i < p->port_slave_connection_count; ++i) {
+ std::string portName = csprintf("%s-p%d", name(), i);
+ SlavePort* bp = new NoncoherentBusSlavePort(portName, *this, i);
+ slavePorts.push_back(bp);
+ }
+
+ clearPortCache();
+}
+
+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());
+
+ // test if the bus should be considered occupied for the current
+ // port
+ if (isOccupied(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());
+
+ // set the source port for routing of the response
+ pkt->setSrc(slave_port_id);
+
+ Tick headerFinishTime = calcPacketTiming(pkt);
+ Tick packetFinishTime = pkt->finishTime;
+
+ // since it is a normal request, determine the destination
+ // based on the address and attempt to send the packet
+ bool success = masterPorts[findPort(pkt->getAddr())]->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());
+
+ addToRetryList(src_port);
+ occupyBus(headerFinishTime);
+
+ return false;
+ }
+
+ succeededTiming(packetFinishTime);
+
+ 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];
+
+ // test if the bus should be considered occupied for the current
+ // port
+ if (isOccupied(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());
+
+ calcPacketTiming(pkt);
+ Tick packetFinishTime = pkt->finishTime;
+
+ // send the packet to the destination through one of our slave
+ // ports, as determined by the destination field
+ bool success M5_VAR_USED = slavePorts[pkt->getDest()]->sendTimingResp(pkt);
+
+ // currently it is illegal to block responses... can lead to
+ // deadlock
+ assert(success);
+
+ succeededTiming(packetFinishTime);
+
+ return true;
+}
+
+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());
+
+ // 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);
+
+ pkt->finishTime = curTick() + 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);
+}
+
+NoncoherentBus*
+NoncoherentBusParams::create()
+{
+ return new NoncoherentBus(this);
+}
--- /dev/null
+/*
+ * Copyright (c) 2011-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) 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:
+
+ /**
+ * 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()
+ { return bus.getAddrRanges(); }
+
+ /**
+ * Get the maximum block size as seen by the bus.
+ */
+ virtual unsigned deviceBlockSize() const
+ { return bus.findBlockSize(); }
+
+ };
+
+ /**
+ * 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(); }
+
+ /**
+ * Get the maximum block size as seen by the bus.
+ */
+ virtual unsigned deviceBlockSize() const
+ { return bus.findBlockSize(); }
+
+ };
+
+ /** 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 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);
+
+};
+
+#endif //__MEM_NONCOHERENT_BUS_HH__
system = System(cpu = cpu,
physmem = SimpleMemory(),
- membus = Bus())
+ membus = CoherentBus())
system.system_port = system.membus.slave
system.physmem.port = system.membus.master
# create the interrupt controller
# system simulated
system = System(cpu = cpus, funcmem = SimpleMemory(in_addr_map = False),
physmem = SimpleMemory(),
- membus = Bus(clock="500GHz", width=16))
+ membus = CoherentBus(clock="500GHz", width=16))
# l2cache & bus
-system.toL2Bus = Bus(clock="500GHz", width=16)
+system.toL2Bus = CoherentBus(clock="500GHz", width=16)
system.l2c = L2(size='64kB', assoc=8)
system.l2c.cpu_side = system.toL2Bus.master
system = System(cpu = cpu,
physmem = SimpleMemory(),
- membus = Bus())
+ membus = CoherentBus())
system.system_port = system.membus.slave
system.physmem.port = system.membus.master
cpu.connectAllPorts(system.membus)
ruby_memory = ruby_config.generate("TwoLevel_SplitL1UnifiedL2.rb", nb_cores)
# system simulated
-system = System(cpu = cpus, physmem = ruby_memory, membus = Bus())
+system = System(cpu = cpus, physmem = ruby_memory, membus = CoherentBus())
for cpu in cpus:
# create the interrupt controller
cpus = [ DerivO3CPU(cpu_id=i) for i in xrange(nb_cores) ]
# system simulated
-system = System(cpu = cpus, physmem = SimpleMemory(), membus = Bus())
+system = System(cpu = cpus, physmem = SimpleMemory(), membus = CoherentBus())
# l2cache & bus
-system.toL2Bus = Bus()
+system.toL2Bus = CoherentBus()
system.l2c = L2(size='4MB', assoc=8)
system.l2c.cpu_side = system.toL2Bus.master
system = System(cpu = cpu,
physmem = ruby_memory,
- membus = Bus())
+ membus = CoherentBus())
system.physmem.port = system.membus.master
# create the interrupt controller
cpu.createInterruptController()
system = System(cpu = cpu,
physmem = SimpleMemory(),
- membus = Bus())
+ membus = CoherentBus())
system.system_port = system.membus.slave
system.physmem.port = system.membus.master
# create the interrupt controller
system.cpu = cpu
#create the l1/l2 bus
-system.toL2Bus = Bus()
+system.toL2Bus = CoherentBus()
#connect up the l2 cache
system.l2c = L2(size='4MB', assoc=8)
system.cpu = cpu
#create the l1/l2 bus
-system.toL2Bus = Bus()
+system.toL2Bus = CoherentBus()
#connect up the l2 cache
system.l2c = L2(size='4MB', assoc=8)
system.cpu = cpu
#create the l1/l2 bus
-system.toL2Bus = Bus()
+system.toL2Bus = CoherentBus()
system.iocache = IOCache()
system.iocache.cpu_side = system.iobus.master
system.iocache.mem_side = system.membus.slave
system.cpu = cpu
#create the l1/l2 bus
-system.toL2Bus = Bus()
+system.toL2Bus = CoherentBus()
system.iocache = IOCache()
system.iocache.cpu_side = system.iobus.master
system.iocache.mem_side = system.membus.slave
system.cpu = cpus
#create the l1/l2 bus
-system.toL2Bus = Bus()
+system.toL2Bus = CoherentBus()
#connect up the l2 cache
system.l2c = L2(size='4MB', assoc=8)
system.cpu = cpu
#create the l1/l2 bus
-system.toL2Bus = Bus()
+system.toL2Bus = CoherentBus()
system.iocache = IOCache()
system.iocache.cpu_side = system.iobus.master
system.iocache.mem_side = system.membus.slave
system.cpu = cpus
#create the l1/l2 bus
-system.toL2Bus = Bus()
+system.toL2Bus = CoherentBus()
#connect up the l2 cache
system.l2c = L2(size='4MB', assoc=8)
system.cpu = cpu
#create the l1/l2 bus
-system.toL2Bus = Bus()
+system.toL2Bus = CoherentBus()
#connect up the l2 cache
system.l2c = L2(size='4MB', assoc=8)
system.cpu = cpus
#create the l1/l2 bus
-system.toL2Bus = Bus()
+system.toL2Bus = CoherentBus()
#connect up the l2 cache
system.l2c = L2(size='4MB', assoc=8)
system.cpu = cpu
#create the l1/l2 bus
-system.toL2Bus = Bus()
+system.toL2Bus = CoherentBus()
system.iocache = IOCache()
system.iocache.cpu_side = system.iobus.master
system.iocache.mem_side = system.membus.slave
system = System(cpu = AtomicSimpleCPU(cpu_id=0),
physmem = SimpleMemory(),
- membus = Bus())
+ membus = CoherentBus())
system.system_port = system.membus.slave
system.physmem.port = system.membus.master
system.cpu.addCheckerCpu()
ruby_memory = ruby_config.generate("TwoLevel_SplitL1UnifiedL2.rb", nb_cores)
# system simulated
-system = System(cpu = cpus, physmem = ruby_memory, membus = Bus())
+system = System(cpu = cpus, physmem = ruby_memory, membus = CoherentBus())
# add L1 caches
for cpu in cpus:
# system simulated
system = System(cpu = cpus,
physmem = SimpleMemory(range = AddrRange('1024MB')),
- membus = Bus())
+ membus = CoherentBus())
# l2cache & bus
-system.toL2Bus = Bus()
+system.toL2Bus = CoherentBus()
system.l2c = L2(size='4MB', assoc=8)
system.l2c.cpu_side = system.toL2Bus.master
system = System(cpu = AtomicSimpleCPU(cpu_id=0),
physmem = SimpleMemory(),
- membus = Bus())
+ membus = CoherentBus())
system.system_port = system.membus.slave
system.physmem.port = system.membus.master
# create the interrupt controller
cpus = [ TimingSimpleCPU(cpu_id=i) for i in xrange(nb_cores) ]
# system simulated
-system = System(cpu = cpus, physmem = SimpleMemory(), membus = Bus())
+system = System(cpu = cpus, physmem = SimpleMemory(), membus = CoherentBus())
# l2cache & bus
-system.toL2Bus = Bus()
+system.toL2Bus = CoherentBus()
system.l2c = L2(size='4MB', assoc=8)
system.l2c.cpu_side = system.toL2Bus.master
MyCache(size = '2MB', latency='10ns'))
system = System(cpu = cpu,
physmem = SimpleMemory(),
- membus = Bus())
+ membus = CoherentBus())
system.system_port = system.membus.slave
system.physmem.port = system.membus.master
# create the interrupt controller
system.cpu = cpu
#create the l1/l2 bus
-system.toL2Bus = Bus()
+system.toL2Bus = CoherentBus()
system.iocache = IOCache()
system.iocache.cpu_side = system.iobus.master
system.iocache.mem_side = system.membus.slave
system.cpu = cpus
#create the l1/l2 bus
-system.toL2Bus = Bus()
+system.toL2Bus = CoherentBus()
system.iocache = IOCache()
system.iocache.cpu_side = system.iobus.master
system.iocache.mem_side = system.membus.slave
system.cpu = cpu
#create the l1/l2 bus
-system.toL2Bus = Bus()
+system.toL2Bus = CoherentBus()
system.iocache = IOCache()
system.iocache.cpu_side = system.iobus.master
system.iocache.mem_side = system.membus.slave
system.cpu = cpus
#create the l1/l2 bus
-system.toL2Bus = Bus()
+system.toL2Bus = CoherentBus()
#connect up the l2 cache
system.l2c = L2(size='4MB', assoc=8)
system.cpu = cpu
#create the l1/l2 bus
-system.toL2Bus = Bus()
+system.toL2Bus = CoherentBus()
#connect up the l2 cache
system.l2c = L2(size='4MB', assoc=8)
system.cpu = cpus
#create the l1/l2 bus
-system.toL2Bus = Bus()
+system.toL2Bus = CoherentBus()
#connect up the l2 cache
system.l2c = L2(size='4MB', assoc=8)
system.cpu = cpu
#create the l1/l2 bus
-system.toL2Bus = Bus()
+system.toL2Bus = CoherentBus()
system.iocache = IOCache()
system.iocache.cpu_side = system.iobus.master
system.iocache.mem_side = system.membus.slave