ruby_caches_MI_example.py revision 12611
1# -*- coding: utf-8 -*- 2# Copyright (c) 2015 Jason Power 3# All rights reserved. 4# 5# Redistribution and use in source and binary forms, with or without 6# modification, are permitted provided that the following conditions are 7# met: redistributions of source code must retain the above copyright 8# notice, this list of conditions and the following disclaimer; 9# redistributions in binary form must reproduce the above copyright 10# notice, this list of conditions and the following disclaimer in the 11# documentation and/or other materials provided with the distribution; 12# neither the name of the copyright holders nor the names of its 13# contributors may be used to endorse or promote products derived from 14# this software without specific prior written permission. 15# 16# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 17# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 18# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 19# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 20# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 21# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 22# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 23# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 24# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 25# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 26# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27# 28# Authors: Jason Power 29 30""" This file creates a set of Ruby caches, the Ruby network, and a simple 31point-to-point topology. 32See Part 3 in the Learning gem5 book: learning.gem5.org/book/part3 33You can change simple_ruby to import from this file instead of from msi_caches 34to use the MI_example protocol instead of MSI. 35 36IMPORTANT: If you modify this file, it's likely that the Learning gem5 book 37 also needs to be updated. For now, email Jason <jason@lowepower.com> 38 39""" 40 41import math 42 43from m5.defines import buildEnv 44from m5.util import fatal, panic 45 46from m5.objects import * 47 48class MyCacheSystem(RubySystem): 49 50 def __init__(self): 51 if buildEnv['PROTOCOL'] != 'MI_example': 52 fatal("This system assumes MI_example!") 53 54 super(MyCacheSystem, self).__init__() 55 56 def setup(self, system, cpus, mem_ctrls): 57 """Set up the Ruby cache subsystem. Note: This can't be done in the 58 constructor because many of these items require a pointer to the 59 ruby system (self). This causes infinite recursion in initialize() 60 if we do this in the __init__. 61 """ 62 # Ruby's global network. 63 self.network = MyNetwork(self) 64 65 # MI example uses 5 virtual networks 66 self.number_of_virtual_networks = 5 67 self.network.number_of_virtual_networks = 5 68 69 # There is a single global list of all of the controllers to make it 70 # easier to connect everything to the global network. This can be 71 # customized depending on the topology/network requirements. 72 # Create one controller for each L1 cache (and the cache mem obj.) 73 # Create a single directory controller (Really the memory cntrl) 74 self.controllers = \ 75 [L1Cache(system, self, cpu) for cpu in cpus] + \ 76 [DirController(self, system.mem_ranges, mem_ctrls)] 77 78 # Create one sequencer per CPU. In many systems this is more 79 # complicated since you have to create sequencers for DMA controllers 80 # and other controllers, too. 81 self.sequencers = [RubySequencer(version = i, 82 # I/D cache is combined and grab from ctrl 83 icache = self.controllers[i].cacheMemory, 84 dcache = self.controllers[i].cacheMemory, 85 clk_domain = self.controllers[i].clk_domain, 86 ) for i in range(len(cpus))] 87 88 for i,c in enumerate(self.controllers[0:len(cpus)]): 89 c.sequencer = self.sequencers[i] 90 91 self.num_of_sequencers = len(self.sequencers) 92 93 # Create the network and connect the controllers. 94 # NOTE: This is quite different if using Garnet! 95 self.network.connectControllers(self.controllers) 96 self.network.setup_buffers() 97 98 # Set up a proxy port for the system_port. Used for load binaries and 99 # other functional-only things. 100 self.sys_port_proxy = RubyPortProxy() 101 system.system_port = self.sys_port_proxy.slave 102 103 # Connect the cpu's cache, interrupt, and TLB ports to Ruby 104 for i,cpu in enumerate(cpus): 105 cpu.icache_port = self.sequencers[i].slave 106 cpu.dcache_port = self.sequencers[i].slave 107 isa = buildEnv['TARGET_ISA'] 108 if isa == 'x86': 109 cpu.interrupts[0].pio = self.sequencers[i].master 110 cpu.interrupts[0].int_master = self.sequencers[i].slave 111 cpu.interrupts[0].int_slave = self.sequencers[i].master 112 if isa == 'x86' or isa == 'arm': 113 cpu.itb.walker.port = self.sequencers[i].slave 114 cpu.dtb.walker.port = self.sequencers[i].slave 115 116class L1Cache(L1Cache_Controller): 117 118 _version = 0 119 @classmethod 120 def versionCount(cls): 121 cls._version += 1 # Use count for this particular type 122 return cls._version - 1 123 124 def __init__(self, system, ruby_system, cpu): 125 """CPUs are needed to grab the clock domain and system is needed for 126 the cache block size. 127 """ 128 super(L1Cache, self).__init__() 129 130 self.version = self.versionCount() 131 # This is the cache memory object that stores the cache data and tags 132 self.cacheMemory = RubyCache(size = '16kB', 133 assoc = 8, 134 start_index_bit = self.getBlockSizeBits(system)) 135 self.clk_domain = cpu.clk_domain 136 self.send_evictions = self.sendEvicts(cpu) 137 self.ruby_system = ruby_system 138 self.connectQueues(ruby_system) 139 140 def getBlockSizeBits(self, system): 141 bits = int(math.log(system.cache_line_size, 2)) 142 if 2**bits != system.cache_line_size.value: 143 panic("Cache line size not a power of 2!") 144 return bits 145 146 def sendEvicts(self, cpu): 147 """True if the CPU model or ISA requires sending evictions from caches 148 to the CPU. Two scenarios warrant forwarding evictions to the CPU: 149 1. The O3 model must keep the LSQ coherent with the caches 150 2. The x86 mwait instruction is built on top of coherence 151 3. The local exclusive monitor in ARM systems 152 """ 153 if type(cpu) is DerivO3CPU or \ 154 buildEnv['TARGET_ISA'] in ('x86', 'arm'): 155 return True 156 return False 157 158 def connectQueues(self, ruby_system): 159 """Connect all of the queues for this controller. 160 """ 161 self.mandatoryQueue = MessageBuffer() 162 self.requestFromCache = MessageBuffer(ordered = True) 163 self.requestFromCache.master = ruby_system.network.slave 164 self.responseFromCache = MessageBuffer(ordered = True) 165 self.responseFromCache.master = ruby_system.network.slave 166 self.forwardToCache = MessageBuffer(ordered = True) 167 self.forwardToCache.slave = ruby_system.network.master 168 self.responseToCache = MessageBuffer(ordered = True) 169 self.responseToCache.slave = ruby_system.network.master 170 171class DirController(Directory_Controller): 172 173 _version = 0 174 @classmethod 175 def versionCount(cls): 176 cls._version += 1 # Use count for this particular type 177 return cls._version - 1 178 179 def __init__(self, ruby_system, ranges, mem_ctrls): 180 """ranges are the memory ranges assigned to this controller. 181 """ 182 if len(mem_ctrls) > 1: 183 panic("This cache system can only be connected to one mem ctrl") 184 super(DirController, self).__init__() 185 self.version = self.versionCount() 186 self.addr_ranges = ranges 187 self.ruby_system = ruby_system 188 self.directory = RubyDirectoryMemory() 189 # Connect this directory to the memory side. 190 self.memory = mem_ctrls[0].port 191 self.connectQueues(ruby_system) 192 193 def connectQueues(self, ruby_system): 194 self.requestToDir = MessageBuffer(ordered = True) 195 self.requestToDir.slave = ruby_system.network.master 196 self.dmaRequestToDir = MessageBuffer(ordered = True) 197 self.dmaRequestToDir.slave = ruby_system.network.master 198 199 self.responseFromDir = MessageBuffer() 200 self.responseFromDir.master = ruby_system.network.slave 201 self.dmaResponseFromDir = MessageBuffer(ordered = True) 202 self.dmaResponseFromDir.master = ruby_system.network.slave 203 self.forwardFromDir = MessageBuffer() 204 self.forwardFromDir.master = ruby_system.network.slave 205 self.responseFromMemory = MessageBuffer() 206 207class MyNetwork(SimpleNetwork): 208 """A simple point-to-point network. This doesn't not use garnet. 209 """ 210 211 def __init__(self, ruby_system): 212 super(MyNetwork, self).__init__() 213 self.netifs = [] 214 self.ruby_system = ruby_system 215 216 def connectControllers(self, controllers): 217 """Connect all of the controllers to routers and connec the routers 218 together in a point-to-point network. 219 """ 220 # Create one router/switch per controller in the system 221 self.routers = [Switch(router_id = i) for i in range(len(controllers))] 222 223 # Make a link from each controller to the router. The link goes 224 # externally to the network. 225 self.ext_links = [SimpleExtLink(link_id=i, ext_node=c, 226 int_node=self.routers[i]) 227 for i, c in enumerate(controllers)] 228 229 # Make an "internal" link (internal to the network) between every pair 230 # of routers. 231 link_count = 0 232 self.int_links = [] 233 for ri in self.routers: 234 for rj in self.routers: 235 if ri == rj: continue # Don't connect a router to itself! 236 link_count += 1 237 self.int_links.append(SimpleIntLink(link_id = link_count, 238 src_node = ri, 239 dst_node = rj)) 240