msi_caches.py revision 13774
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 3913774Sandreas.sandberg@arm.comfrom __future__ import print_function 4013774Sandreas.sandberg@arm.comfrom __future__ import absolute_import 4113774Sandreas.sandberg@arm.com 4212609Sjason@lowepower.comimport math 4312609Sjason@lowepower.com 4412609Sjason@lowepower.comfrom m5.defines import buildEnv 4512609Sjason@lowepower.comfrom m5.util import fatal, panic 4612609Sjason@lowepower.com 4712609Sjason@lowepower.comfrom m5.objects import * 4812609Sjason@lowepower.com 4912609Sjason@lowepower.comclass MyCacheSystem(RubySystem): 5012609Sjason@lowepower.com 5112609Sjason@lowepower.com def __init__(self): 5212609Sjason@lowepower.com if buildEnv['PROTOCOL'] != 'MSI': 5312609Sjason@lowepower.com fatal("This system assumes MSI from learning gem5!") 5412609Sjason@lowepower.com 5512609Sjason@lowepower.com super(MyCacheSystem, self).__init__() 5612609Sjason@lowepower.com 5712609Sjason@lowepower.com def setup(self, system, cpus, mem_ctrls): 5812609Sjason@lowepower.com """Set up the Ruby cache subsystem. Note: This can't be done in the 5912609Sjason@lowepower.com constructor because many of these items require a pointer to the 6012609Sjason@lowepower.com ruby system (self). This causes infinite recursion in initialize() 6112609Sjason@lowepower.com if we do this in the __init__. 6212609Sjason@lowepower.com """ 6312609Sjason@lowepower.com # Ruby's global network. 6412609Sjason@lowepower.com self.network = MyNetwork(self) 6512609Sjason@lowepower.com 6612609Sjason@lowepower.com # MSI uses 3 virtual networks. One for requests (lowest priority), one 6712609Sjason@lowepower.com # for responses (highest priority), and one for "forwards" or 6812609Sjason@lowepower.com # cache-to-cache requests. See *.sm files for details. 6912609Sjason@lowepower.com self.number_of_virtual_networks = 3 7012609Sjason@lowepower.com self.network.number_of_virtual_networks = 3 7112609Sjason@lowepower.com 7212609Sjason@lowepower.com # There is a single global list of all of the controllers to make it 7312609Sjason@lowepower.com # easier to connect everything to the global network. This can be 7412609Sjason@lowepower.com # customized depending on the topology/network requirements. 7512609Sjason@lowepower.com # Create one controller for each L1 cache (and the cache mem obj.) 7612609Sjason@lowepower.com # Create a single directory controller (Really the memory cntrl) 7712609Sjason@lowepower.com self.controllers = \ 7812609Sjason@lowepower.com [L1Cache(system, self, cpu) for cpu in cpus] + \ 7912609Sjason@lowepower.com [DirController(self, system.mem_ranges, mem_ctrls)] 8012609Sjason@lowepower.com 8112609Sjason@lowepower.com # Create one sequencer per CPU. In many systems this is more 8212609Sjason@lowepower.com # complicated since you have to create sequencers for DMA controllers 8312609Sjason@lowepower.com # and other controllers, too. 8412609Sjason@lowepower.com self.sequencers = [RubySequencer(version = i, 8512609Sjason@lowepower.com # I/D cache is combined and grab from ctrl 8612609Sjason@lowepower.com icache = self.controllers[i].cacheMemory, 8712609Sjason@lowepower.com dcache = self.controllers[i].cacheMemory, 8812609Sjason@lowepower.com clk_domain = self.controllers[i].clk_domain, 8912609Sjason@lowepower.com ) for i in range(len(cpus))] 9012609Sjason@lowepower.com 9112609Sjason@lowepower.com # We know that we put the controllers in an order such that the first 9212609Sjason@lowepower.com # N of them are the L1 caches which need a sequencer pointer 9312609Sjason@lowepower.com for i,c in enumerate(self.controllers[0:len(self.sequencers)]): 9412609Sjason@lowepower.com c.sequencer = self.sequencers[i] 9512609Sjason@lowepower.com 9612609Sjason@lowepower.com self.num_of_sequencers = len(self.sequencers) 9712609Sjason@lowepower.com 9812609Sjason@lowepower.com # Create the network and connect the controllers. 9912609Sjason@lowepower.com # NOTE: This is quite different if using Garnet! 10012609Sjason@lowepower.com self.network.connectControllers(self.controllers) 10112609Sjason@lowepower.com self.network.setup_buffers() 10212609Sjason@lowepower.com 10312609Sjason@lowepower.com # Set up a proxy port for the system_port. Used for load binaries and 10412609Sjason@lowepower.com # other functional-only things. 10512609Sjason@lowepower.com self.sys_port_proxy = RubyPortProxy() 10612609Sjason@lowepower.com system.system_port = self.sys_port_proxy.slave 10712609Sjason@lowepower.com 10812609Sjason@lowepower.com # Connect the cpu's cache, interrupt, and TLB ports to Ruby 10912609Sjason@lowepower.com for i,cpu in enumerate(cpus): 11012609Sjason@lowepower.com cpu.icache_port = self.sequencers[i].slave 11112609Sjason@lowepower.com cpu.dcache_port = self.sequencers[i].slave 11212609Sjason@lowepower.com isa = buildEnv['TARGET_ISA'] 11312609Sjason@lowepower.com if isa == 'x86': 11412609Sjason@lowepower.com cpu.interrupts[0].pio = self.sequencers[i].master 11512609Sjason@lowepower.com cpu.interrupts[0].int_master = self.sequencers[i].slave 11612609Sjason@lowepower.com cpu.interrupts[0].int_slave = self.sequencers[i].master 11712609Sjason@lowepower.com if isa == 'x86' or isa == 'arm': 11812609Sjason@lowepower.com cpu.itb.walker.port = self.sequencers[i].slave 11912609Sjason@lowepower.com cpu.dtb.walker.port = self.sequencers[i].slave 12012609Sjason@lowepower.com 12112609Sjason@lowepower.com 12212609Sjason@lowepower.comclass L1Cache(L1Cache_Controller): 12312609Sjason@lowepower.com 12412609Sjason@lowepower.com _version = 0 12512609Sjason@lowepower.com @classmethod 12612609Sjason@lowepower.com def versionCount(cls): 12712609Sjason@lowepower.com cls._version += 1 # Use count for this particular type 12812609Sjason@lowepower.com return cls._version - 1 12912609Sjason@lowepower.com 13012609Sjason@lowepower.com def __init__(self, system, ruby_system, cpu): 13112609Sjason@lowepower.com """CPUs are needed to grab the clock domain and system is needed for 13212609Sjason@lowepower.com the cache block size. 13312609Sjason@lowepower.com """ 13412609Sjason@lowepower.com super(L1Cache, self).__init__() 13512609Sjason@lowepower.com 13612609Sjason@lowepower.com self.version = self.versionCount() 13712609Sjason@lowepower.com # This is the cache memory object that stores the cache data and tags 13812609Sjason@lowepower.com self.cacheMemory = RubyCache(size = '16kB', 13912609Sjason@lowepower.com assoc = 8, 14012609Sjason@lowepower.com start_index_bit = self.getBlockSizeBits(system)) 14112609Sjason@lowepower.com self.clk_domain = cpu.clk_domain 14212609Sjason@lowepower.com self.send_evictions = self.sendEvicts(cpu) 14312609Sjason@lowepower.com self.ruby_system = ruby_system 14412609Sjason@lowepower.com self.connectQueues(ruby_system) 14512609Sjason@lowepower.com 14612609Sjason@lowepower.com def getBlockSizeBits(self, system): 14712609Sjason@lowepower.com bits = int(math.log(system.cache_line_size, 2)) 14812609Sjason@lowepower.com if 2**bits != system.cache_line_size.value: 14912609Sjason@lowepower.com panic("Cache line size not a power of 2!") 15012609Sjason@lowepower.com return bits 15112609Sjason@lowepower.com 15212609Sjason@lowepower.com def sendEvicts(self, cpu): 15312609Sjason@lowepower.com """True if the CPU model or ISA requires sending evictions from caches 15412609Sjason@lowepower.com to the CPU. Two scenarios warrant forwarding evictions to the CPU: 15512609Sjason@lowepower.com 1. The O3 model must keep the LSQ coherent with the caches 15612609Sjason@lowepower.com 2. The x86 mwait instruction is built on top of coherence 15712609Sjason@lowepower.com 3. The local exclusive monitor in ARM systems 15812609Sjason@lowepower.com """ 15912609Sjason@lowepower.com if type(cpu) is DerivO3CPU or \ 16012609Sjason@lowepower.com buildEnv['TARGET_ISA'] in ('x86', 'arm'): 16112609Sjason@lowepower.com return True 16212609Sjason@lowepower.com return False 16312609Sjason@lowepower.com 16412609Sjason@lowepower.com def connectQueues(self, ruby_system): 16512609Sjason@lowepower.com """Connect all of the queues for this controller. 16612609Sjason@lowepower.com """ 16712609Sjason@lowepower.com # mandatoryQueue is a special variable. It is used by the sequencer to 16812609Sjason@lowepower.com # send RubyRequests from the CPU (or other processor). It isn't 16912609Sjason@lowepower.com # explicitly connected to anything. 17012609Sjason@lowepower.com self.mandatoryQueue = MessageBuffer() 17112609Sjason@lowepower.com 17212609Sjason@lowepower.com # All message buffers must be created and connected to the general 17312609Sjason@lowepower.com # Ruby network. In this case, "slave/master" don't mean the same thing 17412609Sjason@lowepower.com # as normal gem5 ports. If a MessageBuffer is a "to" buffer (i.e., out) 17512609Sjason@lowepower.com # then you use the "master", otherwise, the slave. 17612609Sjason@lowepower.com self.requestToDir = MessageBuffer(ordered = True) 17712609Sjason@lowepower.com self.requestToDir.master = ruby_system.network.slave 17812609Sjason@lowepower.com self.responseToDirOrSibling = MessageBuffer(ordered = True) 17912609Sjason@lowepower.com self.responseToDirOrSibling.master = ruby_system.network.slave 18012609Sjason@lowepower.com self.forwardFromDir = MessageBuffer(ordered = True) 18112609Sjason@lowepower.com self.forwardFromDir.slave = ruby_system.network.master 18212609Sjason@lowepower.com self.responseFromDirOrSibling = MessageBuffer(ordered = True) 18312609Sjason@lowepower.com self.responseFromDirOrSibling.slave = ruby_system.network.master 18412609Sjason@lowepower.com 18512609Sjason@lowepower.comclass DirController(Directory_Controller): 18612609Sjason@lowepower.com 18712609Sjason@lowepower.com _version = 0 18812609Sjason@lowepower.com @classmethod 18912609Sjason@lowepower.com def versionCount(cls): 19012609Sjason@lowepower.com cls._version += 1 # Use count for this particular type 19112609Sjason@lowepower.com return cls._version - 1 19212609Sjason@lowepower.com 19312609Sjason@lowepower.com def __init__(self, ruby_system, ranges, mem_ctrls): 19412609Sjason@lowepower.com """ranges are the memory ranges assigned to this controller. 19512609Sjason@lowepower.com """ 19612609Sjason@lowepower.com if len(mem_ctrls) > 1: 19712609Sjason@lowepower.com panic("This cache system can only be connected to one mem ctrl") 19812609Sjason@lowepower.com super(DirController, self).__init__() 19912609Sjason@lowepower.com self.version = self.versionCount() 20012609Sjason@lowepower.com self.addr_ranges = ranges 20112609Sjason@lowepower.com self.ruby_system = ruby_system 20212609Sjason@lowepower.com self.directory = RubyDirectoryMemory() 20312609Sjason@lowepower.com # Connect this directory to the memory side. 20412609Sjason@lowepower.com self.memory = mem_ctrls[0].port 20512609Sjason@lowepower.com self.connectQueues(ruby_system) 20612609Sjason@lowepower.com 20712609Sjason@lowepower.com def connectQueues(self, ruby_system): 20812609Sjason@lowepower.com self.requestFromCache = MessageBuffer(ordered = True) 20912609Sjason@lowepower.com self.requestFromCache.slave = ruby_system.network.master 21012609Sjason@lowepower.com self.responseFromCache = MessageBuffer(ordered = True) 21112609Sjason@lowepower.com self.responseFromCache.slave = ruby_system.network.master 21212609Sjason@lowepower.com 21312609Sjason@lowepower.com self.responseToCache = MessageBuffer(ordered = True) 21412609Sjason@lowepower.com self.responseToCache.master = ruby_system.network.slave 21512609Sjason@lowepower.com self.forwardToCache = MessageBuffer(ordered = True) 21612609Sjason@lowepower.com self.forwardToCache.master = ruby_system.network.slave 21712609Sjason@lowepower.com 21812609Sjason@lowepower.com # This is another special message buffer. It is used to send replies 21912609Sjason@lowepower.com # from memory back to the controller. Any messages received on the 22012609Sjason@lowepower.com # memory port (see self.memory above) will be directed to this 22112609Sjason@lowepower.com # message buffer. 22212609Sjason@lowepower.com self.responseFromMemory = MessageBuffer() 22312609Sjason@lowepower.com 22412609Sjason@lowepower.comclass MyNetwork(SimpleNetwork): 22512609Sjason@lowepower.com """A simple point-to-point network. This doesn't not use garnet. 22612609Sjason@lowepower.com """ 22712609Sjason@lowepower.com 22812609Sjason@lowepower.com def __init__(self, ruby_system): 22912609Sjason@lowepower.com super(MyNetwork, self).__init__() 23012609Sjason@lowepower.com self.netifs = [] 23112609Sjason@lowepower.com self.ruby_system = ruby_system 23212609Sjason@lowepower.com 23312609Sjason@lowepower.com def connectControllers(self, controllers): 23412609Sjason@lowepower.com """Connect all of the controllers to routers and connec the routers 23512609Sjason@lowepower.com together in a point-to-point network. 23612609Sjason@lowepower.com """ 23712609Sjason@lowepower.com # Create one router/switch per controller in the system 23812609Sjason@lowepower.com self.routers = [Switch(router_id = i) for i in range(len(controllers))] 23912609Sjason@lowepower.com 24012609Sjason@lowepower.com # Make a link from each controller to the router. The link goes 24112609Sjason@lowepower.com # externally to the network. 24212609Sjason@lowepower.com self.ext_links = [SimpleExtLink(link_id=i, ext_node=c, 24312609Sjason@lowepower.com int_node=self.routers[i]) 24412609Sjason@lowepower.com for i, c in enumerate(controllers)] 24512609Sjason@lowepower.com 24612609Sjason@lowepower.com # Make an "internal" link (internal to the network) between every pair 24712609Sjason@lowepower.com # of routers. 24812609Sjason@lowepower.com link_count = 0 24912609Sjason@lowepower.com self.int_links = [] 25012609Sjason@lowepower.com for ri in self.routers: 25112609Sjason@lowepower.com for rj in self.routers: 25212609Sjason@lowepower.com if ri == rj: continue # Don't connect a router to itself! 25312609Sjason@lowepower.com link_count += 1 25412609Sjason@lowepower.com self.int_links.append(SimpleIntLink(link_id = link_count, 25512609Sjason@lowepower.com src_node = ri, 25612609Sjason@lowepower.com dst_node = rj)) 257