diff -r 0b1e65b0c22e -r 6f4a19eeaea0 configs/common/CacheConfig.py --- a/configs/common/CacheConfig.py Wed Sep 10 08:55:58 2014 +0100 +++ b/configs/common/CacheConfig.py Wed Sep 10 08:56:12 2014 +0100 @@ -71,8 +71,8 @@ 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 diff -r 0b1e65b0c22e -r 6f4a19eeaea0 configs/common/FSConfig.py --- a/configs/common/FSConfig.py Wed Sep 10 08:55:58 2014 +0100 +++ b/configs/common/FSConfig.py Wed Sep 10 08:56:12 2014 +0100 @@ -50,7 +50,7 @@ def childImage(self, ci): self.image.child.image_file = ci -class MemBus(CoherentBus): +class MemBus(CoherentXBar): badaddr_responder = BadAddr() default = Self.badaddr_responder.pio @@ -71,7 +71,7 @@ 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 @@ -134,7 +134,7 @@ # 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() @@ -196,7 +196,7 @@ 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') @@ -299,7 +299,7 @@ # 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')] @@ -344,7 +344,7 @@ 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 @@ -379,7 +379,7 @@ 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 diff -r 0b1e65b0c22e -r 6f4a19eeaea0 configs/dram/sweep.py --- a/configs/dram/sweep.py Wed Sep 10 08:55:58 2014 +0100 +++ b/configs/dram/sweep.py Wed Sep 10 08:56:12 2014 +0100 @@ -82,9 +82,9 @@ # 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')) diff -r 0b1e65b0c22e -r 6f4a19eeaea0 configs/example/memtest.py --- a/configs/example/memtest.py Wed Sep 10 08:55:58 2014 +0100 +++ b/configs/example/memtest.py Wed Sep 10 08:56:12 2014 +0100 @@ -147,7 +147,7 @@ # system simulated system = System(funcmem = SimpleMemory(in_addr_map = False), - funcbus = NoncoherentBus(), + funcbus = NoncoherentXBar(), physmem = SimpleMemory(latency = "100ns"), cache_line_size = block_size) @@ -162,7 +162,7 @@ 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" diff -r 0b1e65b0c22e -r 6f4a19eeaea0 configs/example/ruby_mem_test.py --- a/configs/example/ruby_mem_test.py Wed Sep 10 08:55:58 2014 +0100 +++ b/configs/example/ruby_mem_test.py Wed Sep 10 08:56:12 2014 +0100 @@ -106,7 +106,7 @@ 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)]) diff -r 0b1e65b0c22e -r 6f4a19eeaea0 configs/example/se.py --- a/configs/example/se.py Wed Sep 10 08:55:58 2014 +0100 +++ b/configs/example/se.py Wed Sep 10 08:56:12 2014 +0100 @@ -255,7 +255,7 @@ 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) diff -r 0b1e65b0c22e -r 6f4a19eeaea0 configs/splash2/cluster.py --- a/configs/splash2/cluster.py Wed Sep 10 08:55:58 2014 +0100 +++ b/configs/splash2/cluster.py Wed Sep 10 08:56:12 2014 +0100 @@ -171,7 +171,7 @@ 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) @@ -184,7 +184,7 @@ 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) @@ -197,7 +197,7 @@ 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) @@ -211,10 +211,10 @@ # ---------------------- 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) # ---------------------- diff -r 0b1e65b0c22e -r 6f4a19eeaea0 configs/splash2/run.py --- a/configs/splash2/run.py Wed Sep 10 08:55:58 2014 +0100 +++ b/configs/splash2/run.py Wed Sep 10 08:56:12 2014 +0100 @@ -196,10 +196,10 @@ # 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) # ---------------------- diff -r 0b1e65b0c22e -r 6f4a19eeaea0 src/arch/x86/pagetable_walker.cc --- a/src/arch/x86/pagetable_walker.cc Wed Sep 10 08:55:58 2014 +0100 +++ b/src/arch/x86/pagetable_walker.cc Wed Sep 10 08:56:12 2014 +0100 @@ -597,7 +597,7 @@ assert(!read); // @todo someone should pay for this - pkt->busFirstWordDelay = pkt->busLastWordDelay = 0; + pkt->firstWordDelay = pkt->lastWordDelay = 0; state = nextState; nextState = Ready; diff -r 0b1e65b0c22e -r 6f4a19eeaea0 src/cpu/BaseCPU.py --- a/src/cpu/BaseCPU.py Wed Sep 10 08:55:58 2014 +0100 +++ b/src/cpu/BaseCPU.py Wed Sep 10 08:56:12 2014 +0100 @@ -47,7 +47,7 @@ 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 @@ -274,8 +274,8 @@ 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 @@ -308,7 +308,7 @@ # 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 diff -r 0b1e65b0c22e -r 6f4a19eeaea0 src/dev/io_device.cc --- a/src/dev/io_device.cc Wed Sep 10 08:55:58 2014 +0100 +++ b/src/dev/io_device.cc Wed Sep 10 08:56:12 2014 +0100 @@ -42,7 +42,7 @@ */ #include "base/trace.hh" -#include "debug/BusAddrRanges.hh" +#include "debug/AddrRanges.hh" #include "dev/io_device.hh" #include "sim/system.hh" @@ -55,7 +55,7 @@ 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); } @@ -113,7 +113,7 @@ { 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; } diff -r 0b1e65b0c22e -r 6f4a19eeaea0 src/dev/pcidev.cc --- a/src/dev/pcidev.cc Wed Sep 10 08:55:58 2014 +0100 +++ b/src/dev/pcidev.cc Wed Sep 10 08:56:12 2014 +0100 @@ -80,7 +80,7 @@ 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); } diff -r 0b1e65b0c22e -r 6f4a19eeaea0 src/dev/x86/intdev.hh --- a/src/dev/x86/intdev.hh Wed Sep 10 08:55:58 2014 +0100 +++ b/src/dev/x86/intdev.hh Wed Sep 10 08:56:12 2014 +0100 @@ -82,7 +82,7 @@ Tick recvMessage(PacketPtr pkt) { // @todo someone should pay for this - pkt->busFirstWordDelay = pkt->busLastWordDelay = 0; + pkt->firstWordDelay = pkt->lastWordDelay = 0; return device->recvMessage(pkt); } }; diff -r 0b1e65b0c22e -r 6f4a19eeaea0 src/mem/Bus.py --- a/src/mem/Bus.py Wed Sep 10 08:55:58 2014 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,85 +0,0 @@ -# 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.") diff -r 0b1e65b0c22e -r 6f4a19eeaea0 src/mem/SConscript --- a/src/mem/SConscript Wed Sep 10 08:55:58 2014 +0100 +++ b/src/mem/SConscript Wed Sep 10 08:56:12 2014 +0100 @@ -39,28 +39,28 @@ 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('port_proxy.cc') +Source('physical.cc') +Source('simple_mem.cc') +Source('snoop_filter.cc') Source('tport.cc') -Source('port_proxy.cc') -Source('simple_mem.cc') -Source('physical.cc') -Source('snoop_filter.cc') +Source('xbar.cc') if env['TARGET_ISA'] != 'null': Source('fs_translating_port_proxy.cc') @@ -74,13 +74,13 @@ 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') diff -r 0b1e65b0c22e -r 6f4a19eeaea0 src/mem/XBar.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/mem/XBar.py Wed Sep 10 08:56:12 2014 +0100 @@ -0,0 +1,85 @@ +# 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.") diff -r 0b1e65b0c22e -r 6f4a19eeaea0 src/mem/bridge.hh --- a/src/mem/bridge.hh Wed Sep 10 08:55:58 2014 +0100 +++ b/src/mem/bridge.hh Wed Sep 10 08:56:12 2014 +0100 @@ -44,7 +44,7 @@ /** * @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. */ @@ -58,7 +58,7 @@ #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. @@ -125,8 +125,7 @@ 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; @@ -241,8 +240,7 @@ 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; @@ -343,4 +341,4 @@ Bridge(Params *p); }; -#endif //__MEM_BUS_HH__ +#endif //__MEM_BRIDGE_HH__ diff -r 0b1e65b0c22e -r 6f4a19eeaea0 src/mem/bridge.cc --- a/src/mem/bridge.cc Wed Sep 10 08:55:58 2014 +0100 +++ b/src/mem/bridge.cc Wed Sep 10 08:56:12 2014 +0100 @@ -44,7 +44,7 @@ /** * @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. */ @@ -108,7 +108,7 @@ { // 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(); @@ -137,7 +137,7 @@ 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)); @@ -181,7 +181,7 @@ 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)); } @@ -209,7 +209,7 @@ { // 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 @@ -242,7 +242,7 @@ 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()); diff -r 0b1e65b0c22e -r 6f4a19eeaea0 src/mem/bus.hh --- a/src/mem/bus.hh Wed Sep 10 08:55:58 2014 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,401 +0,0 @@ -/* - * 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 - -#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 - 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 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 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::iterator PortMapIter; - typedef AddrRangeMap::const_iterator PortMapConstIter; - AddrRangeMap 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 gotAddrRanges; - bool gotAllAddrRanges; - - /** The master and slave ports of the bus */ - std::vector slavePorts; - std::vector masterPorts; - - /** Convenience typedefs. */ - typedef std::vector::iterator SlavePortIter; - typedef std::vector::iterator MasterPortIter; - typedef std::vector::const_iterator SlavePortConstIter; - typedef std::vector::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__ diff -r 0b1e65b0c22e -r 6f4a19eeaea0 src/mem/bus.cc --- a/src/mem/bus.cc Wed Sep 10 08:55:58 2014 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,622 +0,0 @@ -/* - * 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 -BaseBus::Layer::Layer(DstType& _port, BaseBus& _bus, - const std::string& _name) : - port(_port), bus(_bus), _name(_name), state(IDLE), drainManager(NULL), - waitingForPeer(NULL), releaseEvent(this) -{ -} - -template -void BaseBus::Layer::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 -bool -BaseBus::Layer::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 -void -BaseBus::Layer::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 -void -BaseBus::Layer::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 -void -BaseBus::Layer::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 -void -BaseBus::Layer::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 -void -BaseBus::Layer::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::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 intlv_ranges; - for (AddrRangeMap::const_iterator r = portMap.begin(); - r != portMap.end(); ++r) { - // if the range is interleaved then save it for now - if (r->first.interleaved()) { - // if we already got interleaved ranges that are not - // part of the same range, then first do a merge - // before we add the new one - if (!intlv_ranges.empty() && - !intlv_ranges.back().mergesWith(r->first)) { - DPRINTF(BusAddrRanges, "-- Merging range from %d ranges\n", - intlv_ranges.size()); - AddrRange merged_range(intlv_ranges); - // next decide if we keep the merged range or not - if (!(useDefaultRange && - merged_range.isSubset(defaultRange))) { - busRanges.push_back(merged_range); - DPRINTF(BusAddrRanges, "-- Adding merged range %s\n", - merged_range.to_string()); - } - intlv_ranges.clear(); - } - intlv_ranges.push_back(r->first); - } else { - // keep the current range if not a subset of the default - if (!(useDefaultRange && - r->first.isSubset(defaultRange))) { - busRanges.push_back(r->first); - DPRINTF(BusAddrRanges, "-- Adding range %s\n", - r->first.to_string()); - } - } - } - - // 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 -unsigned int -BaseBus::Layer::drain(DrainManager *dm) -{ - //We should check that we're not "doing" anything, and that noone is - //waiting. We might be idle but have someone waiting if the device we - //contacted for a retry didn't actually retry. - if (state != IDLE) { - DPRINTF(Drain, "Bus not drained\n"); - drainManager = dm; - return 1; - } - return 0; -} - -template -void -BaseBus::Layer::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; -template class BaseBus::Layer; diff -r 0b1e65b0c22e -r 6f4a19eeaea0 src/mem/cache/cache_impl.hh --- a/src/mem/cache/cache_impl.hh Wed Sep 10 08:55:58 2014 +0100 +++ b/src/mem/cache/cache_impl.hh Wed Sep 10 08:56:12 2014 +0100 @@ -423,7 +423,7 @@ 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); } @@ -480,7 +480,7 @@ 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(); memSidePort->sendTimingReq(snoopPkt); @@ -500,7 +500,7 @@ 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 @@ -557,7 +557,7 @@ 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, @@ -569,7 +569,7 @@ // 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)) { @@ -1110,8 +1110,8 @@ // 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()); @@ -1127,7 +1127,7 @@ // 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) { @@ -1160,13 +1160,13 @@ // 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()); } @@ -1186,7 +1186,7 @@ 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; @@ -1234,7 +1234,7 @@ mq = mshr->queue; mq->markPending(mshr); requestMemSideBus((RequestCause)mq->index, clockEdge() + - pkt->busLastWordDelay); + pkt->lastWordDelay); } else { mq->deallocate(mshr); if (wasFull && !mq->isFull()) { @@ -1490,7 +1490,7 @@ } blk->whenReady = clockEdge() + responseLatency * clockPeriod() + - pkt->busLastWordDelay; + pkt->lastWordDelay; return blk; } @@ -1517,7 +1517,7 @@ 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); } @@ -1567,7 +1567,7 @@ 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 diff -r 0b1e65b0c22e -r 6f4a19eeaea0 src/mem/coherent_bus.hh --- a/src/mem/coherent_bus.hh Wed Sep 10 08:55:58 2014 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,399 +0,0 @@ -/* - * 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 ReqLayer; - typedef Layer RespLayer; - typedef Layer SnoopLayer; - std::vector reqLayers; - std::vector respLayers; - std::vector 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 snoopRespPorts; - - std::vector 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 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& 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 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 forwardAtomic(PacketPtr pkt, - PortID exclude_slave_port_id, - PortID source_master_port_id, - const std::vector& 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__ diff -r 0b1e65b0c22e -r 6f4a19eeaea0 src/mem/coherent_bus.cc --- a/src/mem/coherent_bus.cc Wed Sep 10 08:55:58 2014 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,830 +0,0 @@ -/* - * 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& 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 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 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 -CoherentBus::forwardAtomic(PacketPtr pkt, PortID exclude_slave_port_id, - PortID source_master_port_id, - const std::vector& 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); -} diff -r 0b1e65b0c22e -r 6f4a19eeaea0 src/mem/coherent_xbar.hh --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/mem/coherent_xbar.hh Wed Sep 10 08:56:12 2014 +0100 @@ -0,0 +1,397 @@ +/* + * 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 ReqLayer; + typedef Layer RespLayer; + typedef Layer SnoopLayer; + std::vector reqLayers; + std::vector respLayers; + std::vector 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 snoopRespPorts; + + std::vector 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 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& 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 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 forwardAtomic(PacketPtr pkt, + PortID exclude_slave_port_id, + PortID source_master_port_id, + const std::vector& 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__ diff -r 0b1e65b0c22e -r 6f4a19eeaea0 src/mem/coherent_xbar.cc --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/mem/coherent_xbar.cc Wed Sep 10 08:56:12 2014 +0100 @@ -0,0 +1,813 @@ +/* + * 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& 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 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 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 +CoherentXBar::forwardAtomic(PacketPtr pkt, PortID exclude_slave_port_id, + PortID source_master_port_id, + const std::vector& 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); +} diff -r 0b1e65b0c22e -r 6f4a19eeaea0 src/mem/dram_ctrl.cc --- a/src/mem/dram_ctrl.cc Wed Sep 10 08:55:58 2014 +0100 +++ b/src/mem/dram_ctrl.cc Wed Sep 10 08:56:12 2014 +0100 @@ -819,7 +819,7 @@ 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 diff -r 0b1e65b0c22e -r 6f4a19eeaea0 src/mem/dramsim2.cc --- a/src/mem/dramsim2.cc Wed Sep 10 08:55:58 2014 +0100 +++ b/src/mem/dramsim2.cc Wed Sep 10 08:56:12 2014 +0100 @@ -268,7 +268,7 @@ 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()); diff -r 0b1e65b0c22e -r 6f4a19eeaea0 src/mem/noncoherent_bus.hh --- a/src/mem/noncoherent_bus.hh Wed Sep 10 08:55:58 2014 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,211 +0,0 @@ -/* - * 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 ReqLayer; - typedef Layer RespLayer; - std::vector reqLayers; - std::vector 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__ diff -r 0b1e65b0c22e -r 6f4a19eeaea0 src/mem/noncoherent_bus.cc --- a/src/mem/noncoherent_bus.cc Wed Sep 10 08:55:58 2014 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,306 +0,0 @@ -/* - * 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; -} diff -r 0b1e65b0c22e -r 6f4a19eeaea0 src/mem/noncoherent_xbar.hh --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/mem/noncoherent_xbar.hh Wed Sep 10 08:56:12 2014 +0100 @@ -0,0 +1,211 @@ +/* + * 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 ReqLayer; + typedef Layer RespLayer; + std::vector reqLayers; + std::vector 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__ diff -r 0b1e65b0c22e -r 6f4a19eeaea0 src/mem/noncoherent_xbar.cc --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/mem/noncoherent_xbar.cc Wed Sep 10 08:56:12 2014 +0100 @@ -0,0 +1,302 @@ +/* + * 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(); +} diff -r 0b1e65b0c22e -r 6f4a19eeaea0 src/mem/packet.hh --- a/src/mem/packet.hh Wed Sep 10 08:55:58 2014 +0100 +++ b/src/mem/packet.hh Wed Sep 10 08:56:12 2014 +0100 @@ -293,14 +293,14 @@ /** * 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; @@ -309,7 +309,7 @@ * 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; @@ -333,21 +333,22 @@ /** * 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 @@ -541,8 +542,6 @@ 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. @@ -604,7 +603,7 @@ : 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()) { @@ -627,7 +626,7 @@ : 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()) { @@ -653,8 +652,8 @@ 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) @@ -739,8 +738,8 @@ dest = InvalidPortID; bytesValidStart = 0; bytesValidEnd = 0; - busFirstWordDelay = 0; - busLastWordDelay = 0; + firstWordDelay = 0; + lastWordDelay = 0; flags.set(VALID_ADDR|VALID_SIZE); deleteData(); @@ -766,7 +765,7 @@ flags.clear(EXPRESS_SNOOP); dest = src; - clearSrc(); + src = InvalidPortID; } void diff -r 0b1e65b0c22e -r 6f4a19eeaea0 src/mem/physical.cc --- a/src/mem/physical.cc Wed Sep 10 08:55:58 2014 +0100 +++ b/src/mem/physical.cc Wed Sep 10 08:56:12 2014 +0100 @@ -51,7 +51,7 @@ #include #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" @@ -79,7 +79,7 @@ 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 @@ -144,7 +144,7 @@ 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(), @@ -164,7 +164,7 @@ // point the memories to their backing store for (vector::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); } diff -r 0b1e65b0c22e -r 6f4a19eeaea0 src/mem/simple_mem.cc --- a/src/mem/simple_mem.cc Wed Sep 10 08:55:58 2014 +0100 +++ b/src/mem/simple_mem.cc Wed Sep 10 08:56:12 2014 +0100 @@ -118,7 +118,7 @@ } // @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 diff -r 0b1e65b0c22e -r 6f4a19eeaea0 src/mem/xbar.hh --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/mem/xbar.hh Wed Sep 10 08:56:12 2014 +0100 @@ -0,0 +1,388 @@ +/* + * 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 + +#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 + 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 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 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 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 gotAddrRanges; + bool gotAllAddrRanges; + + /** The master and slave ports of the crossbar */ + std::vector slavePorts; + std::vector 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__ diff -r 0b1e65b0c22e -r 6f4a19eeaea0 src/mem/xbar.cc --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/mem/xbar.cc Wed Sep 10 08:56:12 2014 +0100 @@ -0,0 +1,614 @@ +/* + * 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 +BaseXBar::Layer::Layer(DstType& _port, BaseXBar& _xbar, + const std::string& _name) : + port(_port), xbar(_xbar), _name(_name), state(IDLE), drainManager(NULL), + waitingForPeer(NULL), releaseEvent(this) +{ +} + +template +void BaseXBar::Layer::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 +bool +BaseXBar::Layer::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 +void +BaseXBar::Layer::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 +void +BaseXBar::Layer::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 +void +BaseXBar::Layer::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 +void +BaseXBar::Layer::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 +void +BaseXBar::Layer::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::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 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 +unsigned int +BaseXBar::Layer::drain(DrainManager *dm) +{ + //We should check that we're not "doing" anything, and that noone is + //waiting. We might be idle but have someone waiting if the device we + //contacted for a retry didn't actually retry. + if (state != IDLE) { + DPRINTF(Drain, "Crossbar not drained\n"); + drainManager = dm; + return 1; + } + return 0; +} + +template +void +BaseXBar::Layer::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; +template class BaseXBar::Layer; diff -r 0b1e65b0c22e -r 6f4a19eeaea0 src/python/m5/params.py --- a/src/python/m5/params.py Wed Sep 10 08:55:58 2014 +0100 +++ b/src/python/m5/params.py Wed Sep 10 08:56:12 2014 +0100 @@ -1870,7 +1870,7 @@ 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 diff -r 0b1e65b0c22e -r 6f4a19eeaea0 src/python/m5/util/dot_writer.py --- a/src/python/m5/util/dot_writer.py Wed Sep 10 08:55:58 2014 +0100 +++ b/src/python/m5/util/dot_writer.py Wed Sep 10 08:56:12 2014 +0100 @@ -173,7 +173,7 @@ class NodeType: SYS = 0 CPU = 1 - BUS = 2 + XBAR = 2 MEM = 3 DEV = 4 OTHER = 5 @@ -190,8 +190,8 @@ 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: @@ -205,7 +205,7 @@ 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) diff -r 0b1e65b0c22e -r 6f4a19eeaea0 tests/configs/base_config.py --- a/tests/configs/base_config.py Wed Sep 10 08:55:58 2014 +0100 +++ b/tests/configs/base_config.py Wed Sep 10 08:56:12 2014 +0100 @@ -102,7 +102,7 @@ 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 @@ -184,7 +184,7 @@ 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 diff -r 0b1e65b0c22e -r 6f4a19eeaea0 tests/configs/memtest-filter.py --- a/tests/configs/memtest-filter.py Wed Sep 10 08:55:58 2014 +0100 +++ b/tests/configs/memtest-filter.py Wed Sep 10 08:56:12 2014 +0100 @@ -37,9 +37,9 @@ # 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', @@ -50,8 +50,8 @@ 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 diff -r 0b1e65b0c22e -r 6f4a19eeaea0 tests/configs/memtest-ruby.py --- a/tests/configs/memtest-ruby.py Wed Sep 10 08:55:58 2014 +0100 +++ b/tests/configs/memtest-ruby.py Wed Sep 10 08:56:12 2014 +0100 @@ -81,7 +81,7 @@ 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', diff -r 0b1e65b0c22e -r 6f4a19eeaea0 tests/configs/memtest.py --- a/tests/configs/memtest.py Wed Sep 10 08:55:58 2014 +0100 +++ b/tests/configs/memtest.py Wed Sep 10 08:56:12 2014 +0100 @@ -37,9 +37,9 @@ # 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', @@ -50,7 +50,7 @@ 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 diff -r 0b1e65b0c22e -r 6f4a19eeaea0 tests/configs/o3-timing-mp-ruby.py --- a/tests/configs/o3-timing-mp-ruby.py Wed Sep 10 08:55:58 2014 +0100 +++ b/tests/configs/o3-timing-mp-ruby.py Wed Sep 10 08:56:12 2014 +0100 @@ -38,7 +38,7 @@ 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')) diff -r 0b1e65b0c22e -r 6f4a19eeaea0 tests/configs/o3-timing-ruby.py --- a/tests/configs/o3-timing-ruby.py Wed Sep 10 08:55:58 2014 +0100 +++ b/tests/configs/o3-timing-ruby.py Wed Sep 10 08:56:12 2014 +0100 @@ -39,7 +39,7 @@ system = System(cpu = cpu, physmem = ruby_memory, - membus = CoherentBus(), + membus = CoherentXBar(), mem_mode = "timing", clk_domain = SrcClockDomain(clock = '1GHz')) diff -r 0b1e65b0c22e -r 6f4a19eeaea0 tests/configs/simple-atomic-mp-ruby.py --- a/tests/configs/simple-atomic-mp-ruby.py Wed Sep 10 08:55:58 2014 +0100 +++ b/tests/configs/simple-atomic-mp-ruby.py Wed Sep 10 08:56:12 2014 +0100 @@ -38,7 +38,7 @@ 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 diff -r 0b1e65b0c22e -r 6f4a19eeaea0 tests/configs/tgen-dram-ctrl.py --- a/tests/configs/tgen-dram-ctrl.py Wed Sep 10 08:55:58 2014 +0100 +++ b/tests/configs/tgen-dram-ctrl.py Wed Sep 10 08:56:12 2014 +0100 @@ -49,7 +49,7 @@ # 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())) diff -r 0b1e65b0c22e -r 6f4a19eeaea0 tests/configs/tgen-simple-mem.py --- a/tests/configs/tgen-simple-mem.py Wed Sep 10 08:55:58 2014 +0100 +++ b/tests/configs/tgen-simple-mem.py Wed Sep 10 08:56:12 2014 +0100 @@ -49,7 +49,7 @@ # system simulated system = System(cpu = cpu, physmem = SimpleMemory(), - membus = NoncoherentBus(width = 16), + membus = NoncoherentXBar(width = 16), clk_domain = SrcClockDomain(clock = '1GHz', voltage_domain = VoltageDomain()))