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
41from __future__ import print_function
42from __future__ import absolute_import
43
44import math
45
46from m5.defines import buildEnv
47from m5.util import fatal, panic
48
49from m5.objects import *
50
51class MyCacheSystem(RubySystem):
52
53    def __init__(self):
54        if buildEnv['PROTOCOL'] != 'MI_example':
55            fatal("This system assumes MI_example!")
56
57        super(MyCacheSystem, self).__init__()
58
59    def setup(self, system, cpus, mem_ctrls):
60        """Set up the Ruby cache subsystem. Note: This can't be done in the
61           constructor because many of these items require a pointer to the
62           ruby system (self). This causes infinite recursion in initialize()
63           if we do this in the __init__.
64        """
65        # Ruby's global network.
66        self.network = MyNetwork(self)
67
68        # MI example uses 5 virtual networks
69        self.number_of_virtual_networks = 5
70        self.network.number_of_virtual_networks = 5
71
72        # There is a single global list of all of the controllers to make it
73        # easier to connect everything to the global network. This can be
74        # customized depending on the topology/network requirements.
75        # Create one controller for each L1 cache (and the cache mem obj.)
76        # Create a single directory controller (Really the memory cntrl)
77        self.controllers = \
78            [L1Cache(system, self, cpu) for cpu in cpus] + \
79            [DirController(self, system.mem_ranges, mem_ctrls)]
80
81        # Create one sequencer per CPU. In many systems this is more
82        # complicated since you have to create sequencers for DMA controllers
83        # and other controllers, too.
84        self.sequencers = [RubySequencer(version = i,
85                                # I/D cache is combined and grab from ctrl
86                                icache = self.controllers[i].cacheMemory,
87                                dcache = self.controllers[i].cacheMemory,
88                                clk_domain = self.controllers[i].clk_domain,
89                                ) for i in range(len(cpus))]
90
91        for i,c in enumerate(self.controllers[0:len(cpus)]):
92            c.sequencer = self.sequencers[i]
93
94        self.num_of_sequencers = len(self.sequencers)
95
96        # Create the network and connect the controllers.
97        # NOTE: This is quite different if using Garnet!
98        self.network.connectControllers(self.controllers)
99        self.network.setup_buffers()
100
101        # Set up a proxy port for the system_port. Used for load binaries and
102        # other functional-only things.
103        self.sys_port_proxy = RubyPortProxy()
104        system.system_port = self.sys_port_proxy.slave
105
106        # Connect the cpu's cache, interrupt, and TLB ports to Ruby
107        for i,cpu in enumerate(cpus):
108            cpu.icache_port = self.sequencers[i].slave
109            cpu.dcache_port = self.sequencers[i].slave
110            isa = buildEnv['TARGET_ISA']
111            if isa == 'x86':
112                cpu.interrupts[0].pio = self.sequencers[i].master
113                cpu.interrupts[0].int_master = self.sequencers[i].slave
114                cpu.interrupts[0].int_slave = self.sequencers[i].master
115            if isa == 'x86' or isa == 'arm':
116                cpu.itb.walker.port = self.sequencers[i].slave
117                cpu.dtb.walker.port = self.sequencers[i].slave
118
119class L1Cache(L1Cache_Controller):
120
121    _version = 0
122    @classmethod
123    def versionCount(cls):
124        cls._version += 1 # Use count for this particular type
125        return cls._version - 1
126
127    def __init__(self, system, ruby_system, cpu):
128        """CPUs are needed to grab the clock domain and system is needed for
129           the cache block size.
130        """
131        super(L1Cache, self).__init__()
132
133        self.version = self.versionCount()
134        # This is the cache memory object that stores the cache data and tags
135        self.cacheMemory = RubyCache(size = '16kB',
136                               assoc = 8,
137                               start_index_bit = self.getBlockSizeBits(system))
138        self.clk_domain = cpu.clk_domain
139        self.send_evictions = self.sendEvicts(cpu)
140        self.ruby_system = ruby_system
141        self.connectQueues(ruby_system)
142
143    def getBlockSizeBits(self, system):
144        bits = int(math.log(system.cache_line_size, 2))
145        if 2**bits != system.cache_line_size.value:
146            panic("Cache line size not a power of 2!")
147        return bits
148
149    def sendEvicts(self, cpu):
150        """True if the CPU model or ISA requires sending evictions from caches
151           to the CPU. Two scenarios warrant forwarding evictions to the CPU:
152           1. The O3 model must keep the LSQ coherent with the caches
153           2. The x86 mwait instruction is built on top of coherence
154           3. The local exclusive monitor in ARM systems
155        """
156        if type(cpu) is DerivO3CPU or \
157           buildEnv['TARGET_ISA'] in ('x86', 'arm'):
158            return True
159        return False
160
161    def connectQueues(self, ruby_system):
162        """Connect all of the queues for this controller.
163        """
164        self.mandatoryQueue = MessageBuffer()
165        self.requestFromCache = MessageBuffer(ordered = True)
166        self.requestFromCache.master = ruby_system.network.slave
167        self.responseFromCache = MessageBuffer(ordered = True)
168        self.responseFromCache.master = ruby_system.network.slave
169        self.forwardToCache = MessageBuffer(ordered = True)
170        self.forwardToCache.slave = ruby_system.network.master
171        self.responseToCache = MessageBuffer(ordered = True)
172        self.responseToCache.slave = ruby_system.network.master
173
174class DirController(Directory_Controller):
175
176    _version = 0
177    @classmethod
178    def versionCount(cls):
179        cls._version += 1 # Use count for this particular type
180        return cls._version - 1
181
182    def __init__(self, ruby_system, ranges, mem_ctrls):
183        """ranges are the memory ranges assigned to this controller.
184        """
185        if len(mem_ctrls) > 1:
186            panic("This cache system can only be connected to one mem ctrl")
187        super(DirController, self).__init__()
188        self.version = self.versionCount()
189        self.addr_ranges = ranges
190        self.ruby_system = ruby_system
191        self.directory = RubyDirectoryMemory()
192        # Connect this directory to the memory side.
193        self.memory = mem_ctrls[0].port
194        self.connectQueues(ruby_system)
195
196    def connectQueues(self, ruby_system):
197        self.requestToDir = MessageBuffer(ordered = True)
198        self.requestToDir.slave = ruby_system.network.master
199        self.dmaRequestToDir = MessageBuffer(ordered = True)
200        self.dmaRequestToDir.slave = ruby_system.network.master
201
202        self.responseFromDir = MessageBuffer()
203        self.responseFromDir.master = ruby_system.network.slave
204        self.dmaResponseFromDir = MessageBuffer(ordered = True)
205        self.dmaResponseFromDir.master = ruby_system.network.slave
206        self.forwardFromDir = MessageBuffer()
207        self.forwardFromDir.master = ruby_system.network.slave
208        self.responseFromMemory = MessageBuffer()
209
210class MyNetwork(SimpleNetwork):
211    """A simple point-to-point network. This doesn't not use garnet.
212    """
213
214    def __init__(self, ruby_system):
215        super(MyNetwork, self).__init__()
216        self.netifs = []
217        self.ruby_system = ruby_system
218
219    def connectControllers(self, controllers):
220        """Connect all of the controllers to routers and connec the routers
221           together in a point-to-point network.
222        """
223        # Create one router/switch per controller in the system
224        self.routers = [Switch(router_id = i) for i in range(len(controllers))]
225
226        # Make a link from each controller to the router. The link goes
227        # externally to the network.
228        self.ext_links = [SimpleExtLink(link_id=i, ext_node=c,
229                                        int_node=self.routers[i])
230                          for i, c in enumerate(controllers)]
231
232        # Make an "internal" link (internal to the network) between every pair
233        # of routers.
234        link_count = 0
235        self.int_links = []
236        for ri in self.routers:
237            for rj in self.routers:
238                if ri == rj: continue # Don't connect a router to itself!
239                link_count += 1
240                self.int_links.append(SimpleIntLink(link_id = link_count,
241                                                    src_node = ri,
242                                                    dst_node = rj))
243