ruby_caches_MI_example.py revision 12611
112611Sjason@lowepower.com# -*- coding: utf-8 -*- 212611Sjason@lowepower.com# Copyright (c) 2015 Jason Power 312611Sjason@lowepower.com# All rights reserved. 412611Sjason@lowepower.com# 512611Sjason@lowepower.com# Redistribution and use in source and binary forms, with or without 612611Sjason@lowepower.com# modification, are permitted provided that the following conditions are 712611Sjason@lowepower.com# met: redistributions of source code must retain the above copyright 812611Sjason@lowepower.com# notice, this list of conditions and the following disclaimer; 912611Sjason@lowepower.com# redistributions in binary form must reproduce the above copyright 1012611Sjason@lowepower.com# notice, this list of conditions and the following disclaimer in the 1112611Sjason@lowepower.com# documentation and/or other materials provided with the distribution; 1212611Sjason@lowepower.com# neither the name of the copyright holders nor the names of its 1312611Sjason@lowepower.com# contributors may be used to endorse or promote products derived from 1412611Sjason@lowepower.com# this software without specific prior written permission. 1512611Sjason@lowepower.com# 1612611Sjason@lowepower.com# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 1712611Sjason@lowepower.com# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 1812611Sjason@lowepower.com# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 1912611Sjason@lowepower.com# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 2012611Sjason@lowepower.com# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 2112611Sjason@lowepower.com# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 2212611Sjason@lowepower.com# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 2312611Sjason@lowepower.com# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 2412611Sjason@lowepower.com# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 2512611Sjason@lowepower.com# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 2612611Sjason@lowepower.com# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 2712611Sjason@lowepower.com# 2812611Sjason@lowepower.com# Authors: Jason Power 2912611Sjason@lowepower.com 3012611Sjason@lowepower.com""" This file creates a set of Ruby caches, the Ruby network, and a simple 3112611Sjason@lowepower.compoint-to-point topology. 3212611Sjason@lowepower.comSee Part 3 in the Learning gem5 book: learning.gem5.org/book/part3 3312611Sjason@lowepower.comYou can change simple_ruby to import from this file instead of from msi_caches 3412611Sjason@lowepower.comto use the MI_example protocol instead of MSI. 3512611Sjason@lowepower.com 3612611Sjason@lowepower.comIMPORTANT: If you modify this file, it's likely that the Learning gem5 book 3712611Sjason@lowepower.com also needs to be updated. For now, email Jason <jason@lowepower.com> 3812611Sjason@lowepower.com 3912611Sjason@lowepower.com""" 4012611Sjason@lowepower.com 4112611Sjason@lowepower.comimport math 4212611Sjason@lowepower.com 4312611Sjason@lowepower.comfrom m5.defines import buildEnv 4412611Sjason@lowepower.comfrom m5.util import fatal, panic 4512611Sjason@lowepower.com 4612611Sjason@lowepower.comfrom m5.objects import * 4712611Sjason@lowepower.com 4812611Sjason@lowepower.comclass MyCacheSystem(RubySystem): 4912611Sjason@lowepower.com 5012611Sjason@lowepower.com def __init__(self): 5112611Sjason@lowepower.com if buildEnv['PROTOCOL'] != 'MI_example': 5212611Sjason@lowepower.com fatal("This system assumes MI_example!") 5312611Sjason@lowepower.com 5412611Sjason@lowepower.com super(MyCacheSystem, self).__init__() 5512611Sjason@lowepower.com 5612611Sjason@lowepower.com def setup(self, system, cpus, mem_ctrls): 5712611Sjason@lowepower.com """Set up the Ruby cache subsystem. Note: This can't be done in the 5812611Sjason@lowepower.com constructor because many of these items require a pointer to the 5912611Sjason@lowepower.com ruby system (self). This causes infinite recursion in initialize() 6012611Sjason@lowepower.com if we do this in the __init__. 6112611Sjason@lowepower.com """ 6212611Sjason@lowepower.com # Ruby's global network. 6312611Sjason@lowepower.com self.network = MyNetwork(self) 6412611Sjason@lowepower.com 6512611Sjason@lowepower.com # MI example uses 5 virtual networks 6612611Sjason@lowepower.com self.number_of_virtual_networks = 5 6712611Sjason@lowepower.com self.network.number_of_virtual_networks = 5 6812611Sjason@lowepower.com 6912611Sjason@lowepower.com # There is a single global list of all of the controllers to make it 7012611Sjason@lowepower.com # easier to connect everything to the global network. This can be 7112611Sjason@lowepower.com # customized depending on the topology/network requirements. 7212611Sjason@lowepower.com # Create one controller for each L1 cache (and the cache mem obj.) 7312611Sjason@lowepower.com # Create a single directory controller (Really the memory cntrl) 7412611Sjason@lowepower.com self.controllers = \ 7512611Sjason@lowepower.com [L1Cache(system, self, cpu) for cpu in cpus] + \ 7612611Sjason@lowepower.com [DirController(self, system.mem_ranges, mem_ctrls)] 7712611Sjason@lowepower.com 7812611Sjason@lowepower.com # Create one sequencer per CPU. In many systems this is more 7912611Sjason@lowepower.com # complicated since you have to create sequencers for DMA controllers 8012611Sjason@lowepower.com # and other controllers, too. 8112611Sjason@lowepower.com self.sequencers = [RubySequencer(version = i, 8212611Sjason@lowepower.com # I/D cache is combined and grab from ctrl 8312611Sjason@lowepower.com icache = self.controllers[i].cacheMemory, 8412611Sjason@lowepower.com dcache = self.controllers[i].cacheMemory, 8512611Sjason@lowepower.com clk_domain = self.controllers[i].clk_domain, 8612611Sjason@lowepower.com ) for i in range(len(cpus))] 8712611Sjason@lowepower.com 8812611Sjason@lowepower.com for i,c in enumerate(self.controllers[0:len(cpus)]): 8912611Sjason@lowepower.com c.sequencer = self.sequencers[i] 9012611Sjason@lowepower.com 9112611Sjason@lowepower.com self.num_of_sequencers = len(self.sequencers) 9212611Sjason@lowepower.com 9312611Sjason@lowepower.com # Create the network and connect the controllers. 9412611Sjason@lowepower.com # NOTE: This is quite different if using Garnet! 9512611Sjason@lowepower.com self.network.connectControllers(self.controllers) 9612611Sjason@lowepower.com self.network.setup_buffers() 9712611Sjason@lowepower.com 9812611Sjason@lowepower.com # Set up a proxy port for the system_port. Used for load binaries and 9912611Sjason@lowepower.com # other functional-only things. 10012611Sjason@lowepower.com self.sys_port_proxy = RubyPortProxy() 10112611Sjason@lowepower.com system.system_port = self.sys_port_proxy.slave 10212611Sjason@lowepower.com 10312611Sjason@lowepower.com # Connect the cpu's cache, interrupt, and TLB ports to Ruby 10412611Sjason@lowepower.com for i,cpu in enumerate(cpus): 10512611Sjason@lowepower.com cpu.icache_port = self.sequencers[i].slave 10612611Sjason@lowepower.com cpu.dcache_port = self.sequencers[i].slave 10712611Sjason@lowepower.com isa = buildEnv['TARGET_ISA'] 10812611Sjason@lowepower.com if isa == 'x86': 10912611Sjason@lowepower.com cpu.interrupts[0].pio = self.sequencers[i].master 11012611Sjason@lowepower.com cpu.interrupts[0].int_master = self.sequencers[i].slave 11112611Sjason@lowepower.com cpu.interrupts[0].int_slave = self.sequencers[i].master 11212611Sjason@lowepower.com if isa == 'x86' or isa == 'arm': 11312611Sjason@lowepower.com cpu.itb.walker.port = self.sequencers[i].slave 11412611Sjason@lowepower.com cpu.dtb.walker.port = self.sequencers[i].slave 11512611Sjason@lowepower.com 11612611Sjason@lowepower.comclass L1Cache(L1Cache_Controller): 11712611Sjason@lowepower.com 11812611Sjason@lowepower.com _version = 0 11912611Sjason@lowepower.com @classmethod 12012611Sjason@lowepower.com def versionCount(cls): 12112611Sjason@lowepower.com cls._version += 1 # Use count for this particular type 12212611Sjason@lowepower.com return cls._version - 1 12312611Sjason@lowepower.com 12412611Sjason@lowepower.com def __init__(self, system, ruby_system, cpu): 12512611Sjason@lowepower.com """CPUs are needed to grab the clock domain and system is needed for 12612611Sjason@lowepower.com the cache block size. 12712611Sjason@lowepower.com """ 12812611Sjason@lowepower.com super(L1Cache, self).__init__() 12912611Sjason@lowepower.com 13012611Sjason@lowepower.com self.version = self.versionCount() 13112611Sjason@lowepower.com # This is the cache memory object that stores the cache data and tags 13212611Sjason@lowepower.com self.cacheMemory = RubyCache(size = '16kB', 13312611Sjason@lowepower.com assoc = 8, 13412611Sjason@lowepower.com start_index_bit = self.getBlockSizeBits(system)) 13512611Sjason@lowepower.com self.clk_domain = cpu.clk_domain 13612611Sjason@lowepower.com self.send_evictions = self.sendEvicts(cpu) 13712611Sjason@lowepower.com self.ruby_system = ruby_system 13812611Sjason@lowepower.com self.connectQueues(ruby_system) 13912611Sjason@lowepower.com 14012611Sjason@lowepower.com def getBlockSizeBits(self, system): 14112611Sjason@lowepower.com bits = int(math.log(system.cache_line_size, 2)) 14212611Sjason@lowepower.com if 2**bits != system.cache_line_size.value: 14312611Sjason@lowepower.com panic("Cache line size not a power of 2!") 14412611Sjason@lowepower.com return bits 14512611Sjason@lowepower.com 14612611Sjason@lowepower.com def sendEvicts(self, cpu): 14712611Sjason@lowepower.com """True if the CPU model or ISA requires sending evictions from caches 14812611Sjason@lowepower.com to the CPU. Two scenarios warrant forwarding evictions to the CPU: 14912611Sjason@lowepower.com 1. The O3 model must keep the LSQ coherent with the caches 15012611Sjason@lowepower.com 2. The x86 mwait instruction is built on top of coherence 15112611Sjason@lowepower.com 3. The local exclusive monitor in ARM systems 15212611Sjason@lowepower.com """ 15312611Sjason@lowepower.com if type(cpu) is DerivO3CPU or \ 15412611Sjason@lowepower.com buildEnv['TARGET_ISA'] in ('x86', 'arm'): 15512611Sjason@lowepower.com return True 15612611Sjason@lowepower.com return False 15712611Sjason@lowepower.com 15812611Sjason@lowepower.com def connectQueues(self, ruby_system): 15912611Sjason@lowepower.com """Connect all of the queues for this controller. 16012611Sjason@lowepower.com """ 16112611Sjason@lowepower.com self.mandatoryQueue = MessageBuffer() 16212611Sjason@lowepower.com self.requestFromCache = MessageBuffer(ordered = True) 16312611Sjason@lowepower.com self.requestFromCache.master = ruby_system.network.slave 16412611Sjason@lowepower.com self.responseFromCache = MessageBuffer(ordered = True) 16512611Sjason@lowepower.com self.responseFromCache.master = ruby_system.network.slave 16612611Sjason@lowepower.com self.forwardToCache = MessageBuffer(ordered = True) 16712611Sjason@lowepower.com self.forwardToCache.slave = ruby_system.network.master 16812611Sjason@lowepower.com self.responseToCache = MessageBuffer(ordered = True) 16912611Sjason@lowepower.com self.responseToCache.slave = ruby_system.network.master 17012611Sjason@lowepower.com 17112611Sjason@lowepower.comclass DirController(Directory_Controller): 17212611Sjason@lowepower.com 17312611Sjason@lowepower.com _version = 0 17412611Sjason@lowepower.com @classmethod 17512611Sjason@lowepower.com def versionCount(cls): 17612611Sjason@lowepower.com cls._version += 1 # Use count for this particular type 17712611Sjason@lowepower.com return cls._version - 1 17812611Sjason@lowepower.com 17912611Sjason@lowepower.com def __init__(self, ruby_system, ranges, mem_ctrls): 18012611Sjason@lowepower.com """ranges are the memory ranges assigned to this controller. 18112611Sjason@lowepower.com """ 18212611Sjason@lowepower.com if len(mem_ctrls) > 1: 18312611Sjason@lowepower.com panic("This cache system can only be connected to one mem ctrl") 18412611Sjason@lowepower.com super(DirController, self).__init__() 18512611Sjason@lowepower.com self.version = self.versionCount() 18612611Sjason@lowepower.com self.addr_ranges = ranges 18712611Sjason@lowepower.com self.ruby_system = ruby_system 18812611Sjason@lowepower.com self.directory = RubyDirectoryMemory() 18912611Sjason@lowepower.com # Connect this directory to the memory side. 19012611Sjason@lowepower.com self.memory = mem_ctrls[0].port 19112611Sjason@lowepower.com self.connectQueues(ruby_system) 19212611Sjason@lowepower.com 19312611Sjason@lowepower.com def connectQueues(self, ruby_system): 19412611Sjason@lowepower.com self.requestToDir = MessageBuffer(ordered = True) 19512611Sjason@lowepower.com self.requestToDir.slave = ruby_system.network.master 19612611Sjason@lowepower.com self.dmaRequestToDir = MessageBuffer(ordered = True) 19712611Sjason@lowepower.com self.dmaRequestToDir.slave = ruby_system.network.master 19812611Sjason@lowepower.com 19912611Sjason@lowepower.com self.responseFromDir = MessageBuffer() 20012611Sjason@lowepower.com self.responseFromDir.master = ruby_system.network.slave 20112611Sjason@lowepower.com self.dmaResponseFromDir = MessageBuffer(ordered = True) 20212611Sjason@lowepower.com self.dmaResponseFromDir.master = ruby_system.network.slave 20312611Sjason@lowepower.com self.forwardFromDir = MessageBuffer() 20412611Sjason@lowepower.com self.forwardFromDir.master = ruby_system.network.slave 20512611Sjason@lowepower.com self.responseFromMemory = MessageBuffer() 20612611Sjason@lowepower.com 20712611Sjason@lowepower.comclass MyNetwork(SimpleNetwork): 20812611Sjason@lowepower.com """A simple point-to-point network. This doesn't not use garnet. 20912611Sjason@lowepower.com """ 21012611Sjason@lowepower.com 21112611Sjason@lowepower.com def __init__(self, ruby_system): 21212611Sjason@lowepower.com super(MyNetwork, self).__init__() 21312611Sjason@lowepower.com self.netifs = [] 21412611Sjason@lowepower.com self.ruby_system = ruby_system 21512611Sjason@lowepower.com 21612611Sjason@lowepower.com def connectControllers(self, controllers): 21712611Sjason@lowepower.com """Connect all of the controllers to routers and connec the routers 21812611Sjason@lowepower.com together in a point-to-point network. 21912611Sjason@lowepower.com """ 22012611Sjason@lowepower.com # Create one router/switch per controller in the system 22112611Sjason@lowepower.com self.routers = [Switch(router_id = i) for i in range(len(controllers))] 22212611Sjason@lowepower.com 22312611Sjason@lowepower.com # Make a link from each controller to the router. The link goes 22412611Sjason@lowepower.com # externally to the network. 22512611Sjason@lowepower.com self.ext_links = [SimpleExtLink(link_id=i, ext_node=c, 22612611Sjason@lowepower.com int_node=self.routers[i]) 22712611Sjason@lowepower.com for i, c in enumerate(controllers)] 22812611Sjason@lowepower.com 22912611Sjason@lowepower.com # Make an "internal" link (internal to the network) between every pair 23012611Sjason@lowepower.com # of routers. 23112611Sjason@lowepower.com link_count = 0 23212611Sjason@lowepower.com self.int_links = [] 23312611Sjason@lowepower.com for ri in self.routers: 23412611Sjason@lowepower.com for rj in self.routers: 23512611Sjason@lowepower.com if ri == rj: continue # Don't connect a router to itself! 23612611Sjason@lowepower.com link_count += 1 23712611Sjason@lowepower.com self.int_links.append(SimpleIntLink(link_id = link_count, 23812611Sjason@lowepower.com src_node = ri, 23912611Sjason@lowepower.com dst_node = rj)) 240