msi_caches.py revision 12609
112609Sjason@lowepower.com# -*- coding: utf-8 -*- 212609Sjason@lowepower.com# Copyright (c) 2017 Jason Power 312609Sjason@lowepower.com# All rights reserved. 412609Sjason@lowepower.com# 512609Sjason@lowepower.com# Redistribution and use in source and binary forms, with or without 612609Sjason@lowepower.com# modification, are permitted provided that the following conditions are 712609Sjason@lowepower.com# met: redistributions of source code must retain the above copyright 812609Sjason@lowepower.com# notice, this list of conditions and the following disclaimer; 912609Sjason@lowepower.com# redistributions in binary form must reproduce the above copyright 1012609Sjason@lowepower.com# notice, this list of conditions and the following disclaimer in the 1112609Sjason@lowepower.com# documentation and/or other materials provided with the distribution; 1212609Sjason@lowepower.com# neither the name of the copyright holders nor the names of its 1312609Sjason@lowepower.com# contributors may be used to endorse or promote products derived from 1412609Sjason@lowepower.com# this software without specific prior written permission. 1512609Sjason@lowepower.com# 1612609Sjason@lowepower.com# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 1712609Sjason@lowepower.com# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 1812609Sjason@lowepower.com# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 1912609Sjason@lowepower.com# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 2012609Sjason@lowepower.com# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 2112609Sjason@lowepower.com# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 2212609Sjason@lowepower.com# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 2312609Sjason@lowepower.com# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 2412609Sjason@lowepower.com# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 2512609Sjason@lowepower.com# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 2612609Sjason@lowepower.com# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 2712609Sjason@lowepower.com# 2812609Sjason@lowepower.com# Authors: Jason Power 2912609Sjason@lowepower.com 3012609Sjason@lowepower.com""" This file creates a set of Ruby caches, the Ruby network, and a simple 3112609Sjason@lowepower.compoint-to-point topology. 3212609Sjason@lowepower.comSee Part 3 in the Learning gem5 book: learning.gem5.org/book/part3 3312609Sjason@lowepower.com 3412609Sjason@lowepower.comIMPORTANT: If you modify this file, it's likely that the Learning gem5 book 3512609Sjason@lowepower.com also needs to be updated. For now, email Jason <jason@lowepower.com> 3612609Sjason@lowepower.com 3712609Sjason@lowepower.com""" 3812609Sjason@lowepower.com 3912609Sjason@lowepower.comimport math 4012609Sjason@lowepower.com 4112609Sjason@lowepower.comfrom m5.defines import buildEnv 4212609Sjason@lowepower.comfrom m5.util import fatal, panic 4312609Sjason@lowepower.com 4412609Sjason@lowepower.comfrom m5.objects import * 4512609Sjason@lowepower.com 4612609Sjason@lowepower.comclass MyCacheSystem(RubySystem): 4712609Sjason@lowepower.com 4812609Sjason@lowepower.com def __init__(self): 4912609Sjason@lowepower.com if buildEnv['PROTOCOL'] != 'MSI': 5012609Sjason@lowepower.com fatal("This system assumes MSI from learning gem5!") 5112609Sjason@lowepower.com 5212609Sjason@lowepower.com super(MyCacheSystem, self).__init__() 5312609Sjason@lowepower.com 5412609Sjason@lowepower.com def setup(self, system, cpus, mem_ctrls): 5512609Sjason@lowepower.com """Set up the Ruby cache subsystem. Note: This can't be done in the 5612609Sjason@lowepower.com constructor because many of these items require a pointer to the 5712609Sjason@lowepower.com ruby system (self). This causes infinite recursion in initialize() 5812609Sjason@lowepower.com if we do this in the __init__. 5912609Sjason@lowepower.com """ 6012609Sjason@lowepower.com # Ruby's global network. 6112609Sjason@lowepower.com self.network = MyNetwork(self) 6212609Sjason@lowepower.com 6312609Sjason@lowepower.com # MSI uses 3 virtual networks. One for requests (lowest priority), one 6412609Sjason@lowepower.com # for responses (highest priority), and one for "forwards" or 6512609Sjason@lowepower.com # cache-to-cache requests. See *.sm files for details. 6612609Sjason@lowepower.com self.number_of_virtual_networks = 3 6712609Sjason@lowepower.com self.network.number_of_virtual_networks = 3 6812609Sjason@lowepower.com 6912609Sjason@lowepower.com # There is a single global list of all of the controllers to make it 7012609Sjason@lowepower.com # easier to connect everything to the global network. This can be 7112609Sjason@lowepower.com # customized depending on the topology/network requirements. 7212609Sjason@lowepower.com # Create one controller for each L1 cache (and the cache mem obj.) 7312609Sjason@lowepower.com # Create a single directory controller (Really the memory cntrl) 7412609Sjason@lowepower.com self.controllers = \ 7512609Sjason@lowepower.com [L1Cache(system, self, cpu) for cpu in cpus] + \ 7612609Sjason@lowepower.com [DirController(self, system.mem_ranges, mem_ctrls)] 7712609Sjason@lowepower.com 7812609Sjason@lowepower.com # Create one sequencer per CPU. In many systems this is more 7912609Sjason@lowepower.com # complicated since you have to create sequencers for DMA controllers 8012609Sjason@lowepower.com # and other controllers, too. 8112609Sjason@lowepower.com self.sequencers = [RubySequencer(version = i, 8212609Sjason@lowepower.com # I/D cache is combined and grab from ctrl 8312609Sjason@lowepower.com icache = self.controllers[i].cacheMemory, 8412609Sjason@lowepower.com dcache = self.controllers[i].cacheMemory, 8512609Sjason@lowepower.com clk_domain = self.controllers[i].clk_domain, 8612609Sjason@lowepower.com ) for i in range(len(cpus))] 8712609Sjason@lowepower.com 8812609Sjason@lowepower.com # We know that we put the controllers in an order such that the first 8912609Sjason@lowepower.com # N of them are the L1 caches which need a sequencer pointer 9012609Sjason@lowepower.com for i,c in enumerate(self.controllers[0:len(self.sequencers)]): 9112609Sjason@lowepower.com c.sequencer = self.sequencers[i] 9212609Sjason@lowepower.com 9312609Sjason@lowepower.com self.num_of_sequencers = len(self.sequencers) 9412609Sjason@lowepower.com 9512609Sjason@lowepower.com # Create the network and connect the controllers. 9612609Sjason@lowepower.com # NOTE: This is quite different if using Garnet! 9712609Sjason@lowepower.com self.network.connectControllers(self.controllers) 9812609Sjason@lowepower.com self.network.setup_buffers() 9912609Sjason@lowepower.com 10012609Sjason@lowepower.com # Set up a proxy port for the system_port. Used for load binaries and 10112609Sjason@lowepower.com # other functional-only things. 10212609Sjason@lowepower.com self.sys_port_proxy = RubyPortProxy() 10312609Sjason@lowepower.com system.system_port = self.sys_port_proxy.slave 10412609Sjason@lowepower.com 10512609Sjason@lowepower.com # Connect the cpu's cache, interrupt, and TLB ports to Ruby 10612609Sjason@lowepower.com for i,cpu in enumerate(cpus): 10712609Sjason@lowepower.com cpu.icache_port = self.sequencers[i].slave 10812609Sjason@lowepower.com cpu.dcache_port = self.sequencers[i].slave 10912609Sjason@lowepower.com isa = buildEnv['TARGET_ISA'] 11012609Sjason@lowepower.com if isa == 'x86': 11112609Sjason@lowepower.com cpu.interrupts[0].pio = self.sequencers[i].master 11212609Sjason@lowepower.com cpu.interrupts[0].int_master = self.sequencers[i].slave 11312609Sjason@lowepower.com cpu.interrupts[0].int_slave = self.sequencers[i].master 11412609Sjason@lowepower.com if isa == 'x86' or isa == 'arm': 11512609Sjason@lowepower.com cpu.itb.walker.port = self.sequencers[i].slave 11612609Sjason@lowepower.com cpu.dtb.walker.port = self.sequencers[i].slave 11712609Sjason@lowepower.com 11812609Sjason@lowepower.com 11912609Sjason@lowepower.comclass L1Cache(L1Cache_Controller): 12012609Sjason@lowepower.com 12112609Sjason@lowepower.com _version = 0 12212609Sjason@lowepower.com @classmethod 12312609Sjason@lowepower.com def versionCount(cls): 12412609Sjason@lowepower.com cls._version += 1 # Use count for this particular type 12512609Sjason@lowepower.com return cls._version - 1 12612609Sjason@lowepower.com 12712609Sjason@lowepower.com def __init__(self, system, ruby_system, cpu): 12812609Sjason@lowepower.com """CPUs are needed to grab the clock domain and system is needed for 12912609Sjason@lowepower.com the cache block size. 13012609Sjason@lowepower.com """ 13112609Sjason@lowepower.com super(L1Cache, self).__init__() 13212609Sjason@lowepower.com 13312609Sjason@lowepower.com self.version = self.versionCount() 13412609Sjason@lowepower.com # This is the cache memory object that stores the cache data and tags 13512609Sjason@lowepower.com self.cacheMemory = RubyCache(size = '16kB', 13612609Sjason@lowepower.com assoc = 8, 13712609Sjason@lowepower.com start_index_bit = self.getBlockSizeBits(system)) 13812609Sjason@lowepower.com self.clk_domain = cpu.clk_domain 13912609Sjason@lowepower.com self.send_evictions = self.sendEvicts(cpu) 14012609Sjason@lowepower.com self.ruby_system = ruby_system 14112609Sjason@lowepower.com self.connectQueues(ruby_system) 14212609Sjason@lowepower.com 14312609Sjason@lowepower.com def getBlockSizeBits(self, system): 14412609Sjason@lowepower.com bits = int(math.log(system.cache_line_size, 2)) 14512609Sjason@lowepower.com if 2**bits != system.cache_line_size.value: 14612609Sjason@lowepower.com panic("Cache line size not a power of 2!") 14712609Sjason@lowepower.com return bits 14812609Sjason@lowepower.com 14912609Sjason@lowepower.com def sendEvicts(self, cpu): 15012609Sjason@lowepower.com """True if the CPU model or ISA requires sending evictions from caches 15112609Sjason@lowepower.com to the CPU. Two scenarios warrant forwarding evictions to the CPU: 15212609Sjason@lowepower.com 1. The O3 model must keep the LSQ coherent with the caches 15312609Sjason@lowepower.com 2. The x86 mwait instruction is built on top of coherence 15412609Sjason@lowepower.com 3. The local exclusive monitor in ARM systems 15512609Sjason@lowepower.com """ 15612609Sjason@lowepower.com if type(cpu) is DerivO3CPU or \ 15712609Sjason@lowepower.com buildEnv['TARGET_ISA'] in ('x86', 'arm'): 15812609Sjason@lowepower.com return True 15912609Sjason@lowepower.com return False 16012609Sjason@lowepower.com 16112609Sjason@lowepower.com def connectQueues(self, ruby_system): 16212609Sjason@lowepower.com """Connect all of the queues for this controller. 16312609Sjason@lowepower.com """ 16412609Sjason@lowepower.com # mandatoryQueue is a special variable. It is used by the sequencer to 16512609Sjason@lowepower.com # send RubyRequests from the CPU (or other processor). It isn't 16612609Sjason@lowepower.com # explicitly connected to anything. 16712609Sjason@lowepower.com self.mandatoryQueue = MessageBuffer() 16812609Sjason@lowepower.com 16912609Sjason@lowepower.com # All message buffers must be created and connected to the general 17012609Sjason@lowepower.com # Ruby network. In this case, "slave/master" don't mean the same thing 17112609Sjason@lowepower.com # as normal gem5 ports. If a MessageBuffer is a "to" buffer (i.e., out) 17212609Sjason@lowepower.com # then you use the "master", otherwise, the slave. 17312609Sjason@lowepower.com self.requestToDir = MessageBuffer(ordered = True) 17412609Sjason@lowepower.com self.requestToDir.master = ruby_system.network.slave 17512609Sjason@lowepower.com self.responseToDirOrSibling = MessageBuffer(ordered = True) 17612609Sjason@lowepower.com self.responseToDirOrSibling.master = ruby_system.network.slave 17712609Sjason@lowepower.com self.forwardFromDir = MessageBuffer(ordered = True) 17812609Sjason@lowepower.com self.forwardFromDir.slave = ruby_system.network.master 17912609Sjason@lowepower.com self.responseFromDirOrSibling = MessageBuffer(ordered = True) 18012609Sjason@lowepower.com self.responseFromDirOrSibling.slave = ruby_system.network.master 18112609Sjason@lowepower.com 18212609Sjason@lowepower.comclass DirController(Directory_Controller): 18312609Sjason@lowepower.com 18412609Sjason@lowepower.com _version = 0 18512609Sjason@lowepower.com @classmethod 18612609Sjason@lowepower.com def versionCount(cls): 18712609Sjason@lowepower.com cls._version += 1 # Use count for this particular type 18812609Sjason@lowepower.com return cls._version - 1 18912609Sjason@lowepower.com 19012609Sjason@lowepower.com def __init__(self, ruby_system, ranges, mem_ctrls): 19112609Sjason@lowepower.com """ranges are the memory ranges assigned to this controller. 19212609Sjason@lowepower.com """ 19312609Sjason@lowepower.com if len(mem_ctrls) > 1: 19412609Sjason@lowepower.com panic("This cache system can only be connected to one mem ctrl") 19512609Sjason@lowepower.com super(DirController, self).__init__() 19612609Sjason@lowepower.com self.version = self.versionCount() 19712609Sjason@lowepower.com self.addr_ranges = ranges 19812609Sjason@lowepower.com self.ruby_system = ruby_system 19912609Sjason@lowepower.com self.directory = RubyDirectoryMemory() 20012609Sjason@lowepower.com # Connect this directory to the memory side. 20112609Sjason@lowepower.com self.memory = mem_ctrls[0].port 20212609Sjason@lowepower.com self.connectQueues(ruby_system) 20312609Sjason@lowepower.com 20412609Sjason@lowepower.com def connectQueues(self, ruby_system): 20512609Sjason@lowepower.com self.requestFromCache = MessageBuffer(ordered = True) 20612609Sjason@lowepower.com self.requestFromCache.slave = ruby_system.network.master 20712609Sjason@lowepower.com self.responseFromCache = MessageBuffer(ordered = True) 20812609Sjason@lowepower.com self.responseFromCache.slave = ruby_system.network.master 20912609Sjason@lowepower.com 21012609Sjason@lowepower.com self.responseToCache = MessageBuffer(ordered = True) 21112609Sjason@lowepower.com self.responseToCache.master = ruby_system.network.slave 21212609Sjason@lowepower.com self.forwardToCache = MessageBuffer(ordered = True) 21312609Sjason@lowepower.com self.forwardToCache.master = ruby_system.network.slave 21412609Sjason@lowepower.com 21512609Sjason@lowepower.com # This is another special message buffer. It is used to send replies 21612609Sjason@lowepower.com # from memory back to the controller. Any messages received on the 21712609Sjason@lowepower.com # memory port (see self.memory above) will be directed to this 21812609Sjason@lowepower.com # message buffer. 21912609Sjason@lowepower.com self.responseFromMemory = MessageBuffer() 22012609Sjason@lowepower.com 22112609Sjason@lowepower.comclass MyNetwork(SimpleNetwork): 22212609Sjason@lowepower.com """A simple point-to-point network. This doesn't not use garnet. 22312609Sjason@lowepower.com """ 22412609Sjason@lowepower.com 22512609Sjason@lowepower.com def __init__(self, ruby_system): 22612609Sjason@lowepower.com super(MyNetwork, self).__init__() 22712609Sjason@lowepower.com self.netifs = [] 22812609Sjason@lowepower.com self.ruby_system = ruby_system 22912609Sjason@lowepower.com 23012609Sjason@lowepower.com def connectControllers(self, controllers): 23112609Sjason@lowepower.com """Connect all of the controllers to routers and connec the routers 23212609Sjason@lowepower.com together in a point-to-point network. 23312609Sjason@lowepower.com """ 23412609Sjason@lowepower.com # Create one router/switch per controller in the system 23512609Sjason@lowepower.com self.routers = [Switch(router_id = i) for i in range(len(controllers))] 23612609Sjason@lowepower.com 23712609Sjason@lowepower.com # Make a link from each controller to the router. The link goes 23812609Sjason@lowepower.com # externally to the network. 23912609Sjason@lowepower.com self.ext_links = [SimpleExtLink(link_id=i, ext_node=c, 24012609Sjason@lowepower.com int_node=self.routers[i]) 24112609Sjason@lowepower.com for i, c in enumerate(controllers)] 24212609Sjason@lowepower.com 24312609Sjason@lowepower.com # Make an "internal" link (internal to the network) between every pair 24412609Sjason@lowepower.com # of routers. 24512609Sjason@lowepower.com link_count = 0 24612609Sjason@lowepower.com self.int_links = [] 24712609Sjason@lowepower.com for ri in self.routers: 24812609Sjason@lowepower.com for rj in self.routers: 24912609Sjason@lowepower.com if ri == rj: continue # Don't connect a router to itself! 25012609Sjason@lowepower.com link_count += 1 25112609Sjason@lowepower.com self.int_links.append(SimpleIntLink(link_id = link_count, 25212609Sjason@lowepower.com src_node = ri, 25312609Sjason@lowepower.com dst_node = rj)) 254