diff options
Diffstat (limited to 'configs/learning_gem5/part3')
-rw-r--r-- | configs/learning_gem5/part3/msi_caches.py | 253 | ||||
-rw-r--r-- | configs/learning_gem5/part3/simple_ruby.py | 101 |
2 files changed, 354 insertions, 0 deletions
diff --git a/configs/learning_gem5/part3/msi_caches.py b/configs/learning_gem5/part3/msi_caches.py new file mode 100644 index 000000000..7bd24efe1 --- /dev/null +++ b/configs/learning_gem5/part3/msi_caches.py @@ -0,0 +1,253 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2017 Jason Power +# 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: Jason Power + +""" This file creates a set of Ruby caches, the Ruby network, and a simple +point-to-point topology. +See Part 3 in the Learning gem5 book: learning.gem5.org/book/part3 + +IMPORTANT: If you modify this file, it's likely that the Learning gem5 book + also needs to be updated. For now, email Jason <jason@lowepower.com> + +""" + +import math + +from m5.defines import buildEnv +from m5.util import fatal, panic + +from m5.objects import * + +class MyCacheSystem(RubySystem): + + def __init__(self): + if buildEnv['PROTOCOL'] != 'MSI': + fatal("This system assumes MSI from learning gem5!") + + super(MyCacheSystem, self).__init__() + + def setup(self, system, cpus, mem_ctrls): + """Set up the Ruby cache subsystem. Note: This can't be done in the + constructor because many of these items require a pointer to the + ruby system (self). This causes infinite recursion in initialize() + if we do this in the __init__. + """ + # Ruby's global network. + self.network = MyNetwork(self) + + # MSI uses 3 virtual networks. One for requests (lowest priority), one + # for responses (highest priority), and one for "forwards" or + # cache-to-cache requests. See *.sm files for details. + self.number_of_virtual_networks = 3 + self.network.number_of_virtual_networks = 3 + + # There is a single global list of all of the controllers to make it + # easier to connect everything to the global network. This can be + # customized depending on the topology/network requirements. + # Create one controller for each L1 cache (and the cache mem obj.) + # Create a single directory controller (Really the memory cntrl) + self.controllers = \ + [L1Cache(system, self, cpu) for cpu in cpus] + \ + [DirController(self, system.mem_ranges, mem_ctrls)] + + # Create one sequencer per CPU. In many systems this is more + # complicated since you have to create sequencers for DMA controllers + # and other controllers, too. + self.sequencers = [RubySequencer(version = i, + # I/D cache is combined and grab from ctrl + icache = self.controllers[i].cacheMemory, + dcache = self.controllers[i].cacheMemory, + clk_domain = self.controllers[i].clk_domain, + ) for i in range(len(cpus))] + + # We know that we put the controllers in an order such that the first + # N of them are the L1 caches which need a sequencer pointer + for i,c in enumerate(self.controllers[0:len(self.sequencers)]): + c.sequencer = self.sequencers[i] + + self.num_of_sequencers = len(self.sequencers) + + # Create the network and connect the controllers. + # NOTE: This is quite different if using Garnet! + self.network.connectControllers(self.controllers) + self.network.setup_buffers() + + # Set up a proxy port for the system_port. Used for load binaries and + # other functional-only things. + self.sys_port_proxy = RubyPortProxy() + system.system_port = self.sys_port_proxy.slave + + # Connect the cpu's cache, interrupt, and TLB ports to Ruby + for i,cpu in enumerate(cpus): + cpu.icache_port = self.sequencers[i].slave + cpu.dcache_port = self.sequencers[i].slave + isa = buildEnv['TARGET_ISA'] + if isa == 'x86': + cpu.interrupts[0].pio = self.sequencers[i].master + cpu.interrupts[0].int_master = self.sequencers[i].slave + cpu.interrupts[0].int_slave = self.sequencers[i].master + if isa == 'x86' or isa == 'arm': + cpu.itb.walker.port = self.sequencers[i].slave + cpu.dtb.walker.port = self.sequencers[i].slave + + +class L1Cache(L1Cache_Controller): + + _version = 0 + @classmethod + def versionCount(cls): + cls._version += 1 # Use count for this particular type + return cls._version - 1 + + def __init__(self, system, ruby_system, cpu): + """CPUs are needed to grab the clock domain and system is needed for + the cache block size. + """ + super(L1Cache, self).__init__() + + self.version = self.versionCount() + # This is the cache memory object that stores the cache data and tags + self.cacheMemory = RubyCache(size = '16kB', + assoc = 8, + start_index_bit = self.getBlockSizeBits(system)) + self.clk_domain = cpu.clk_domain + self.send_evictions = self.sendEvicts(cpu) + self.ruby_system = ruby_system + self.connectQueues(ruby_system) + + def getBlockSizeBits(self, system): + bits = int(math.log(system.cache_line_size, 2)) + if 2**bits != system.cache_line_size.value: + panic("Cache line size not a power of 2!") + return bits + + def sendEvicts(self, cpu): + """True if the CPU model or ISA requires sending evictions from caches + to the CPU. Two scenarios warrant forwarding evictions to the CPU: + 1. The O3 model must keep the LSQ coherent with the caches + 2. The x86 mwait instruction is built on top of coherence + 3. The local exclusive monitor in ARM systems + """ + if type(cpu) is DerivO3CPU or \ + buildEnv['TARGET_ISA'] in ('x86', 'arm'): + return True + return False + + def connectQueues(self, ruby_system): + """Connect all of the queues for this controller. + """ + # mandatoryQueue is a special variable. It is used by the sequencer to + # send RubyRequests from the CPU (or other processor). It isn't + # explicitly connected to anything. + self.mandatoryQueue = MessageBuffer() + + # All message buffers must be created and connected to the general + # Ruby network. In this case, "slave/master" don't mean the same thing + # as normal gem5 ports. If a MessageBuffer is a "to" buffer (i.e., out) + # then you use the "master", otherwise, the slave. + self.requestToDir = MessageBuffer(ordered = True) + self.requestToDir.master = ruby_system.network.slave + self.responseToDirOrSibling = MessageBuffer(ordered = True) + self.responseToDirOrSibling.master = ruby_system.network.slave + self.forwardFromDir = MessageBuffer(ordered = True) + self.forwardFromDir.slave = ruby_system.network.master + self.responseFromDirOrSibling = MessageBuffer(ordered = True) + self.responseFromDirOrSibling.slave = ruby_system.network.master + +class DirController(Directory_Controller): + + _version = 0 + @classmethod + def versionCount(cls): + cls._version += 1 # Use count for this particular type + return cls._version - 1 + + def __init__(self, ruby_system, ranges, mem_ctrls): + """ranges are the memory ranges assigned to this controller. + """ + if len(mem_ctrls) > 1: + panic("This cache system can only be connected to one mem ctrl") + super(DirController, self).__init__() + self.version = self.versionCount() + self.addr_ranges = ranges + self.ruby_system = ruby_system + self.directory = RubyDirectoryMemory() + # Connect this directory to the memory side. + self.memory = mem_ctrls[0].port + self.connectQueues(ruby_system) + + def connectQueues(self, ruby_system): + self.requestFromCache = MessageBuffer(ordered = True) + self.requestFromCache.slave = ruby_system.network.master + self.responseFromCache = MessageBuffer(ordered = True) + self.responseFromCache.slave = ruby_system.network.master + + self.responseToCache = MessageBuffer(ordered = True) + self.responseToCache.master = ruby_system.network.slave + self.forwardToCache = MessageBuffer(ordered = True) + self.forwardToCache.master = ruby_system.network.slave + + # This is another special message buffer. It is used to send replies + # from memory back to the controller. Any messages received on the + # memory port (see self.memory above) will be directed to this + # message buffer. + self.responseFromMemory = MessageBuffer() + +class MyNetwork(SimpleNetwork): + """A simple point-to-point network. This doesn't not use garnet. + """ + + def __init__(self, ruby_system): + super(MyNetwork, self).__init__() + self.netifs = [] + self.ruby_system = ruby_system + + def connectControllers(self, controllers): + """Connect all of the controllers to routers and connec the routers + together in a point-to-point network. + """ + # Create one router/switch per controller in the system + self.routers = [Switch(router_id = i) for i in range(len(controllers))] + + # Make a link from each controller to the router. The link goes + # externally to the network. + self.ext_links = [SimpleExtLink(link_id=i, ext_node=c, + int_node=self.routers[i]) + for i, c in enumerate(controllers)] + + # Make an "internal" link (internal to the network) between every pair + # of routers. + link_count = 0 + self.int_links = [] + for ri in self.routers: + for rj in self.routers: + if ri == rj: continue # Don't connect a router to itself! + link_count += 1 + self.int_links.append(SimpleIntLink(link_id = link_count, + src_node = ri, + dst_node = rj)) diff --git a/configs/learning_gem5/part3/simple_ruby.py b/configs/learning_gem5/part3/simple_ruby.py new file mode 100644 index 000000000..bf21be9e1 --- /dev/null +++ b/configs/learning_gem5/part3/simple_ruby.py @@ -0,0 +1,101 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2015 Jason Power +# 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: Jason Lowe-Power + +""" This file creates a system with Ruby caches and executes 'threads', a +simple multi-threaded application with false sharing to stress the Ruby +protocol. + +See Part 3 in the Learning gem5 book: learning.gem5.org/book/part3 + +IMPORTANT: If you modify this file, it's likely that the Learning gem5 book + also needs to be updated. For now, email Jason <jason@lowepower.com> + +""" +from __future__ import print_function + +# import the m5 (gem5) library created when gem5 is built +import m5 +# import all of the SimObjects +from m5.objects import * + +from msi_caches import MyCacheSystem + +# create the system we are going to simulate +system = System() + +# Set the clock fequency of the system (and all of its children) +system.clk_domain = SrcClockDomain() +system.clk_domain.clock = '1GHz' +system.clk_domain.voltage_domain = VoltageDomain() + +# Set up the system +system.mem_mode = 'timing' # Use timing accesses +system.mem_ranges = [AddrRange('512MB')] # Create an address range + +# Create a pair of simple CPUs +system.cpu = [TimingSimpleCPU() for i in range(2)] + +# Create a DDR3 memory controller and connect it to the membus +system.mem_ctrl = DDR3_1600_8x8() +system.mem_ctrl.range = system.mem_ranges[0] + +# create the interrupt controller for the CPU and connect to the membus +for cpu in system.cpu: + cpu.createInterruptController() + +# Create the Ruby System +system.caches = MyCacheSystem() +system.caches.setup(system, system.cpu, [system.mem_ctrl]) + +# get ISA for the binary to run. +isa = str(m5.defines.buildEnv['TARGET_ISA']).lower() + +# Run application and use the compiled ISA to find the binary +binary = 'tests/test-progs/threads/bin/' + isa + '/linux/threads' + +# Create a process for a simple "multi-threaded" application +process = Process() +# Set the command +# cmd is a list which begins with the executable (like argv) +process.cmd = [binary] +# Set the cpu to use the process as its workload and create thread contexts +for cpu in system.cpu: + cpu.workload = process + cpu.createThreads() + +# set up the root SimObject and start the simulation +root = Root(full_system = False, system = system) +# instantiate all of the objects we've created above +m5.instantiate() + +print("Beginning simulation!") +exit_event = m5.simulate() +print('Exiting @ tick {} because {}'.format( + m5.curTick(), exit_event.getCause()) + ) |