msi_caches.py revision 13774
16019SN/A# -*- coding: utf-8 -*- 26019SN/A# Copyright (c) 2017 Jason Power 37110SN/A# All rights reserved. 47110SN/A# 57110SN/A# Redistribution and use in source and binary forms, with or without 67110SN/A# modification, are permitted provided that the following conditions are 77110SN/A# met: redistributions of source code must retain the above copyright 87110SN/A# notice, this list of conditions and the following disclaimer; 97110SN/A# redistributions in binary form must reproduce the above copyright 107110SN/A# notice, this list of conditions and the following disclaimer in the 117110SN/A# documentation and/or other materials provided with the distribution; 127110SN/A# neither the name of the copyright holders nor the names of its 137110SN/A# contributors may be used to endorse or promote products derived from 147110SN/A# this software without specific prior written permission. 156019SN/A# 166019SN/A# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 176019SN/A# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 186019SN/A# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 196019SN/A# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 206019SN/A# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 216019SN/A# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 226019SN/A# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 236019SN/A# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 246019SN/A# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 256019SN/A# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 266019SN/A# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 276019SN/A# 286019SN/A# Authors: Jason Power 296019SN/A 306019SN/A""" This file creates a set of Ruby caches, the Ruby network, and a simple 316019SN/Apoint-to-point topology. 326019SN/ASee Part 3 in the Learning gem5 book: learning.gem5.org/book/part3 336019SN/A 346019SN/AIMPORTANT: If you modify this file, it's likely that the Learning gem5 book 356019SN/A also needs to be updated. For now, email Jason <jason@lowepower.com> 366019SN/A 376019SN/A""" 386019SN/A 396019SN/Afrom __future__ import print_function 406019SN/Afrom __future__ import absolute_import 416019SN/A 426019SN/Aimport math 436019SN/A 446019SN/Afrom m5.defines import buildEnv 456019SN/Afrom m5.util import fatal, panic 466019SN/A 476019SN/Afrom m5.objects import * 486243SN/A 497422Sgblack@eecs.umich.educlass MyCacheSystem(RubySystem): 507422Sgblack@eecs.umich.edu 516243SN/A def __init__(self): 526243SN/A if buildEnv['PROTOCOL'] != 'MSI': 537138Sgblack@eecs.umich.edu fatal("This system assumes MSI from learning gem5!") 547138Sgblack@eecs.umich.edu 557138Sgblack@eecs.umich.edu super(MyCacheSystem, self).__init__() 567138Sgblack@eecs.umich.edu 577138Sgblack@eecs.umich.edu def setup(self, system, cpus, mem_ctrls): 587138Sgblack@eecs.umich.edu """Set up the Ruby cache subsystem. Note: This can't be done in the 597138Sgblack@eecs.umich.edu constructor because many of these items require a pointer to the 607138Sgblack@eecs.umich.edu ruby system (self). This causes infinite recursion in initialize() 617138Sgblack@eecs.umich.edu if we do this in the __init__. 627138Sgblack@eecs.umich.edu """ 637138Sgblack@eecs.umich.edu # Ruby's global network. 647138Sgblack@eecs.umich.edu self.network = MyNetwork(self) 657138Sgblack@eecs.umich.edu 667138Sgblack@eecs.umich.edu # MSI uses 3 virtual networks. One for requests (lowest priority), one 677138Sgblack@eecs.umich.edu # for responses (highest priority), and one for "forwards" or 687138Sgblack@eecs.umich.edu # cache-to-cache requests. See *.sm files for details. 697138Sgblack@eecs.umich.edu self.number_of_virtual_networks = 3 707138Sgblack@eecs.umich.edu self.network.number_of_virtual_networks = 3 717138Sgblack@eecs.umich.edu 727138Sgblack@eecs.umich.edu # There is a single global list of all of the controllers to make it 737138Sgblack@eecs.umich.edu # easier to connect everything to the global network. This can be 747138Sgblack@eecs.umich.edu # customized depending on the topology/network requirements. 757138Sgblack@eecs.umich.edu # Create one controller for each L1 cache (and the cache mem obj.) 767138Sgblack@eecs.umich.edu # Create a single directory controller (Really the memory cntrl) 777138Sgblack@eecs.umich.edu self.controllers = \ 787138Sgblack@eecs.umich.edu [L1Cache(system, self, cpu) for cpu in cpus] + \ 797138Sgblack@eecs.umich.edu [DirController(self, system.mem_ranges, mem_ctrls)] 807138Sgblack@eecs.umich.edu 817138Sgblack@eecs.umich.edu # Create one sequencer per CPU. In many systems this is more 827138Sgblack@eecs.umich.edu # complicated since you have to create sequencers for DMA controllers 837138Sgblack@eecs.umich.edu # and other controllers, too. 847138Sgblack@eecs.umich.edu self.sequencers = [RubySequencer(version = i, 857138Sgblack@eecs.umich.edu # I/D cache is combined and grab from ctrl 867138Sgblack@eecs.umich.edu icache = self.controllers[i].cacheMemory, 877138Sgblack@eecs.umich.edu dcache = self.controllers[i].cacheMemory, 887138Sgblack@eecs.umich.edu clk_domain = self.controllers[i].clk_domain, 897138Sgblack@eecs.umich.edu ) for i in range(len(cpus))] 907138Sgblack@eecs.umich.edu 917138Sgblack@eecs.umich.edu # We know that we put the controllers in an order such that the first 927138Sgblack@eecs.umich.edu # N of them are the L1 caches which need a sequencer pointer 937138Sgblack@eecs.umich.edu for i,c in enumerate(self.controllers[0:len(self.sequencers)]): 947138Sgblack@eecs.umich.edu c.sequencer = self.sequencers[i] 957138Sgblack@eecs.umich.edu 967138Sgblack@eecs.umich.edu self.num_of_sequencers = len(self.sequencers) 977138Sgblack@eecs.umich.edu 987138Sgblack@eecs.umich.edu # Create the network and connect the controllers. 997138Sgblack@eecs.umich.edu # NOTE: This is quite different if using Garnet! 1007138Sgblack@eecs.umich.edu self.network.connectControllers(self.controllers) 1017138Sgblack@eecs.umich.edu self.network.setup_buffers() 1027138Sgblack@eecs.umich.edu 1037138Sgblack@eecs.umich.edu # Set up a proxy port for the system_port. Used for load binaries and 1047138Sgblack@eecs.umich.edu # other functional-only things. 1057138Sgblack@eecs.umich.edu self.sys_port_proxy = RubyPortProxy() 1067138Sgblack@eecs.umich.edu system.system_port = self.sys_port_proxy.slave 1077138Sgblack@eecs.umich.edu 1087138Sgblack@eecs.umich.edu # Connect the cpu's cache, interrupt, and TLB ports to Ruby 1097138Sgblack@eecs.umich.edu for i,cpu in enumerate(cpus): 1107138Sgblack@eecs.umich.edu cpu.icache_port = self.sequencers[i].slave 1117138Sgblack@eecs.umich.edu cpu.dcache_port = self.sequencers[i].slave 1127138Sgblack@eecs.umich.edu isa = buildEnv['TARGET_ISA'] 1137138Sgblack@eecs.umich.edu if isa == 'x86': 1147138Sgblack@eecs.umich.edu cpu.interrupts[0].pio = self.sequencers[i].master 1157138Sgblack@eecs.umich.edu cpu.interrupts[0].int_master = self.sequencers[i].slave 1167138Sgblack@eecs.umich.edu cpu.interrupts[0].int_slave = self.sequencers[i].master 1177138Sgblack@eecs.umich.edu if isa == 'x86' or isa == 'arm': 1187138Sgblack@eecs.umich.edu cpu.itb.walker.port = self.sequencers[i].slave 1197138Sgblack@eecs.umich.edu cpu.dtb.walker.port = self.sequencers[i].slave 1207138Sgblack@eecs.umich.edu 1217138Sgblack@eecs.umich.edu 1227138Sgblack@eecs.umich.educlass L1Cache(L1Cache_Controller): 1237138Sgblack@eecs.umich.edu 1247138Sgblack@eecs.umich.edu _version = 0 1257138Sgblack@eecs.umich.edu @classmethod 1267138Sgblack@eecs.umich.edu def versionCount(cls): 1277138Sgblack@eecs.umich.edu cls._version += 1 # Use count for this particular type 1287138Sgblack@eecs.umich.edu return cls._version - 1 1296019SN/A 1306019SN/A def __init__(self, system, ruby_system, cpu): 1316019SN/A """CPUs are needed to grab the clock domain and system is needed for 1326019SN/A the cache block size. 1336271SN/A """ 1346271SN/A super(L1Cache, self).__init__() 1356019SN/A 1366019SN/A self.version = self.versionCount() 1376019SN/A # This is the cache memory object that stores the cache data and tags 1386243SN/A self.cacheMemory = RubyCache(size = '16kB', 1396019SN/A assoc = 8, 1406243SN/A start_index_bit = self.getBlockSizeBits(system)) 1416019SN/A self.clk_domain = cpu.clk_domain 1426019SN/A self.send_evictions = self.sendEvicts(cpu) 1436019SN/A self.ruby_system = ruby_system 1446019SN/A self.connectQueues(ruby_system) 1457597Sminkyu.jeong@arm.com 1467597Sminkyu.jeong@arm.com def getBlockSizeBits(self, system): 1476019SN/A bits = int(math.log(system.cache_line_size, 2)) 1486019SN/A if 2**bits != system.cache_line_size.value: 1497646Sgene.wu@arm.com panic("Cache line size not a power of 2!") 1507646Sgene.wu@arm.com return bits 1517408Sgblack@eecs.umich.edu 1527408Sgblack@eecs.umich.edu def sendEvicts(self, cpu): 1537408Sgblack@eecs.umich.edu """True if the CPU model or ISA requires sending evictions from caches 1546019SN/A to the CPU. Two scenarios warrant forwarding evictions to the CPU: 1556019SN/A 1. The O3 model must keep the LSQ coherent with the caches 1566019SN/A 2. The x86 mwait instruction is built on top of coherence 1576019SN/A 3. The local exclusive monitor in ARM systems 1586265SN/A """ 1596265SN/A if type(cpu) is DerivO3CPU or \ 1606265SN/A buildEnv['TARGET_ISA'] in ('x86', 'arm'): 1616265SN/A return True 1626265SN/A return False 1636265SN/A 1646265SN/A def connectQueues(self, ruby_system): 1656265SN/A """Connect all of the queues for this controller. 1666265SN/A """ 1676265SN/A # mandatoryQueue is a special variable. It is used by the sequencer to 1686265SN/A # send RubyRequests from the CPU (or other processor). It isn't 1696265SN/A # explicitly connected to anything. 1706265SN/A self.mandatoryQueue = MessageBuffer() 1716265SN/A 1726270SN/A # All message buffers must be created and connected to the general 1736270SN/A # Ruby network. In this case, "slave/master" don't mean the same thing 1746270SN/A # as normal gem5 ports. If a MessageBuffer is a "to" buffer (i.e., out) 1756270SN/A # then you use the "master", otherwise, the slave. 1766270SN/A self.requestToDir = MessageBuffer(ordered = True) 1776270SN/A self.requestToDir.master = ruby_system.network.slave 178 self.responseToDirOrSibling = MessageBuffer(ordered = True) 179 self.responseToDirOrSibling.master = ruby_system.network.slave 180 self.forwardFromDir = MessageBuffer(ordered = True) 181 self.forwardFromDir.slave = ruby_system.network.master 182 self.responseFromDirOrSibling = MessageBuffer(ordered = True) 183 self.responseFromDirOrSibling.slave = ruby_system.network.master 184 185class DirController(Directory_Controller): 186 187 _version = 0 188 @classmethod 189 def versionCount(cls): 190 cls._version += 1 # Use count for this particular type 191 return cls._version - 1 192 193 def __init__(self, ruby_system, ranges, mem_ctrls): 194 """ranges are the memory ranges assigned to this controller. 195 """ 196 if len(mem_ctrls) > 1: 197 panic("This cache system can only be connected to one mem ctrl") 198 super(DirController, self).__init__() 199 self.version = self.versionCount() 200 self.addr_ranges = ranges 201 self.ruby_system = ruby_system 202 self.directory = RubyDirectoryMemory() 203 # Connect this directory to the memory side. 204 self.memory = mem_ctrls[0].port 205 self.connectQueues(ruby_system) 206 207 def connectQueues(self, ruby_system): 208 self.requestFromCache = MessageBuffer(ordered = True) 209 self.requestFromCache.slave = ruby_system.network.master 210 self.responseFromCache = MessageBuffer(ordered = True) 211 self.responseFromCache.slave = ruby_system.network.master 212 213 self.responseToCache = MessageBuffer(ordered = True) 214 self.responseToCache.master = ruby_system.network.slave 215 self.forwardToCache = MessageBuffer(ordered = True) 216 self.forwardToCache.master = ruby_system.network.slave 217 218 # This is another special message buffer. It is used to send replies 219 # from memory back to the controller. Any messages received on the 220 # memory port (see self.memory above) will be directed to this 221 # message buffer. 222 self.responseFromMemory = MessageBuffer() 223 224class MyNetwork(SimpleNetwork): 225 """A simple point-to-point network. This doesn't not use garnet. 226 """ 227 228 def __init__(self, ruby_system): 229 super(MyNetwork, self).__init__() 230 self.netifs = [] 231 self.ruby_system = ruby_system 232 233 def connectControllers(self, controllers): 234 """Connect all of the controllers to routers and connec the routers 235 together in a point-to-point network. 236 """ 237 # Create one router/switch per controller in the system 238 self.routers = [Switch(router_id = i) for i in range(len(controllers))] 239 240 # Make a link from each controller to the router. The link goes 241 # externally to the network. 242 self.ext_links = [SimpleExtLink(link_id=i, ext_node=c, 243 int_node=self.routers[i]) 244 for i, c in enumerate(controllers)] 245 246 # Make an "internal" link (internal to the network) between every pair 247 # of routers. 248 link_count = 0 249 self.int_links = [] 250 for ri in self.routers: 251 for rj in self.routers: 252 if ri == rj: continue # Don't connect a router to itself! 253 link_count += 1 254 self.int_links.append(SimpleIntLink(link_id = link_count, 255 src_node = ri, 256 dst_node = rj)) 257