diff options
author | Jason Lowe-Power <jason@lowepower.com> | 2018-03-09 12:10:54 -0800 |
---|---|---|
committer | Jason Lowe-Power <jason@lowepower.com> | 2018-03-23 15:50:28 +0000 |
commit | 173227b390895636947c7c1bf5cbcd60d3c40ee7 (patch) | |
tree | e9882fd5be1f208203b6dbdb76b53a3d68ac9894 | |
parent | d9f9b80dc9b7f1d892f9b977a1342e98ebc75b00 (diff) | |
download | gem5-173227b390895636947c7c1bf5cbcd60d3c40ee7.tar.xz |
learning_gem5: Add a simple config for MI_example
Adds a new config script to configure the MI_example protocol. This script
closely follows the script used for MSI, but instead supports the
MI_example protocol. This script works with the simple_ruby runscript and
can be included instead of msi_caches.
Change-Id: I8be0be67bf51369763ba103a5f101cfc01ad8859
Signed-off-by: Jason Lowe-Power <jason@lowepower.com>
Reviewed-on: https://gem5-review.googlesource.com/8945
Reviewed-by: Nikos Nikoleris <nikos.nikoleris@arm.com>
-rw-r--r-- | configs/learning_gem5/part3/ruby_caches_MI_example.py | 239 | ||||
-rw-r--r-- | configs/learning_gem5/part3/simple_ruby.py | 2 |
2 files changed, 241 insertions, 0 deletions
diff --git a/configs/learning_gem5/part3/ruby_caches_MI_example.py b/configs/learning_gem5/part3/ruby_caches_MI_example.py new file mode 100644 index 000000000..104f0dff9 --- /dev/null +++ b/configs/learning_gem5/part3/ruby_caches_MI_example.py @@ -0,0 +1,239 @@ +# -*- 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 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 +You can change simple_ruby to import from this file instead of from msi_caches +to use the MI_example protocol instead of MSI. + +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'] != 'MI_example': + fatal("This system assumes MI_example!") + + 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) + + # MI example uses 5 virtual networks + self.number_of_virtual_networks = 5 + self.network.number_of_virtual_networks = 5 + + # 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))] + + for i,c in enumerate(self.controllers[0:len(cpus)]): + 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. + """ + self.mandatoryQueue = MessageBuffer() + self.requestFromCache = MessageBuffer(ordered = True) + self.requestFromCache.master = ruby_system.network.slave + self.responseFromCache = MessageBuffer(ordered = True) + self.responseFromCache.master = ruby_system.network.slave + self.forwardToCache = MessageBuffer(ordered = True) + self.forwardToCache.slave = ruby_system.network.master + self.responseToCache = MessageBuffer(ordered = True) + self.responseToCache.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.requestToDir = MessageBuffer(ordered = True) + self.requestToDir.slave = ruby_system.network.master + self.dmaRequestToDir = MessageBuffer(ordered = True) + self.dmaRequestToDir.slave = ruby_system.network.master + + self.responseFromDir = MessageBuffer() + self.responseFromDir.master = ruby_system.network.slave + self.dmaResponseFromDir = MessageBuffer(ordered = True) + self.dmaResponseFromDir.master = ruby_system.network.slave + self.forwardFromDir = MessageBuffer() + self.forwardFromDir.master = ruby_system.network.slave + 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 index bf21be9e1..9b89b78fd 100644 --- a/configs/learning_gem5/part3/simple_ruby.py +++ b/configs/learning_gem5/part3/simple_ruby.py @@ -44,6 +44,8 @@ import m5 # import all of the SimObjects from m5.objects import * +# You can import ruby_caches_MI_example to use the MI_example protocol instead +# of the MSI protocol from msi_caches import MyCacheSystem # create the system we are going to simulate |