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