1# Copyright (c) 2012-2013, 2017-2018 ARM Limited
2# All rights reserved.
3#
4# The license below extends only to copyright in the software and shall
5# not be construed as granting a license to any other intellectual
6# property including but not limited to intellectual property relating
7# to a hardware implementation of the functionality of the software
8# licensed hereunder.  You may use the software subject to the license
9# terms below provided that you ensure that this notice is replicated
10# unmodified and in its entirety in all distributions of the software,
11# modified or unmodified, in source code or in binary form.
12#
13# Redistribution and use in source and binary forms, with or without
14# modification, are permitted provided that the following conditions are
15# met: redistributions of source code must retain the above copyright
16# notice, this list of conditions and the following disclaimer;
17# redistributions in binary form must reproduce the above copyright
18# notice, this list of conditions and the following disclaimer in the
19# documentation and/or other materials provided with the distribution;
20# neither the name of the copyright holders nor the names of its
21# contributors may be used to endorse or promote products derived from
22# this software without specific prior written permission.
23#
24# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
25# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
26# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
27# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
28# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
29# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
30# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
31# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
32# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
33# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
34# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
35#
36# Authors: Andreas Sandberg
37#          Andreas Hansson
38
39from abc import ABCMeta, abstractmethod
40import optparse
41import m5
42from m5.objects import *
43from m5.proxy import *
44m5.util.addToPath('../configs/')
45from common import FSConfig
46from common import Options
47from common.Caches import *
48from ruby import Ruby
49
50_have_kvm_support = 'BaseKvmCPU' in globals()
51
52class BaseSystem(object):
53    """Base system builder.
54
55    This class provides some basic functionality for creating an ARM
56    system with the usual peripherals (caches, GIC, etc.). It allows
57    customization by defining separate methods for different parts of
58    the initialization process.
59    """
60
61    __metaclass__ = ABCMeta
62
63    def __init__(self, mem_mode='timing', mem_class=SimpleMemory,
64                 cpu_class=TimingSimpleCPU, num_cpus=1, num_threads=1,
65                 checker=False, mem_size=None, use_ruby=False):
66        """Initialize a simple base system.
67
68        Keyword Arguments:
69          mem_mode -- String describing the memory mode (timing or atomic)
70          mem_class -- Memory controller class to use
71          cpu_class -- CPU class to use
72          num_cpus -- Number of CPUs to instantiate
73          checker -- Set to True to add checker CPUs
74          mem_size -- Override the default memory size
75          use_ruby -- Set to True to use ruby memory
76        """
77        self.mem_mode = mem_mode
78        self.mem_class = mem_class
79        self.cpu_class = cpu_class
80        self.num_cpus = num_cpus
81        self.num_threads = num_threads
82        self.checker = checker
83        self.use_ruby = use_ruby
84
85    def create_cpus(self, cpu_clk_domain):
86        """Return a list of CPU objects to add to a system."""
87        cpus = [ self.cpu_class(clk_domain=cpu_clk_domain,
88                                numThreads=self.num_threads,
89                                cpu_id=i)
90                 for i in range(self.num_cpus) ]
91        if self.checker:
92            for c in cpus:
93                c.addCheckerCpu()
94        return cpus
95
96    def create_caches_private(self, cpu):
97        """Add private caches to a CPU.
98
99        Arguments:
100          cpu -- CPU instance to work on.
101        """
102        cpu.addPrivateSplitL1Caches(L1_ICache(size='32kB', assoc=1),
103                                    L1_DCache(size='32kB', assoc=4))
104
105    def create_caches_shared(self, system):
106        """Add shared caches to a system.
107
108        Arguments:
109          system -- System to work on.
110
111        Returns:
112          A bus that CPUs should use to connect to the shared cache.
113        """
114        system.toL2Bus = L2XBar(clk_domain=system.cpu_clk_domain)
115        system.l2c = L2Cache(clk_domain=system.cpu_clk_domain,
116                             size='4MB', assoc=8)
117        system.l2c.cpu_side = system.toL2Bus.master
118        system.l2c.mem_side = system.membus.slave
119        return system.toL2Bus
120
121    def init_cpu(self, system, cpu, sha_bus):
122        """Initialize a CPU.
123
124        Arguments:
125          system -- System to work on.
126          cpu -- CPU to initialize.
127        """
128        if not cpu.switched_out:
129            self.create_caches_private(cpu)
130            cpu.createInterruptController()
131            cpu.connectAllPorts(sha_bus if sha_bus != None else system.membus,
132                                system.membus)
133
134    def init_kvm(self, system):
135        """Do KVM-specific system initialization.
136
137        Arguments:
138          system -- System to work on.
139        """
140        system.vm = KvmVM()
141
142    def init_system(self, system):
143        """Initialize a system.
144
145        Arguments:
146          system -- System to initialize.
147        """
148        self.create_clk_src(system)
149        system.cpu = self.create_cpus(system.cpu_clk_domain)
150
151        if _have_kvm_support and \
152                any([isinstance(c, BaseKvmCPU) for c in system.cpu]):
153            self.init_kvm(system)
154
155        if self.use_ruby:
156            # Add the ruby specific and protocol specific options
157            parser = optparse.OptionParser()
158            Options.addCommonOptions(parser)
159            Ruby.define_options(parser)
160            (options, args) = parser.parse_args()
161
162            # Set the default cache size and associativity to be very
163            # small to encourage races between requests and writebacks.
164            options.l1d_size="32kB"
165            options.l1i_size="32kB"
166            options.l2_size="4MB"
167            options.l1d_assoc=4
168            options.l1i_assoc=2
169            options.l2_assoc=8
170            options.num_cpus = self.num_cpus
171            options.num_dirs = 2
172
173            bootmem = getattr(system, 'bootmem', None)
174            Ruby.create_system(options, True, system, system.iobus,
175                               system._dma_ports, bootmem)
176
177            # Create a seperate clock domain for Ruby
178            system.ruby.clk_domain = SrcClockDomain(
179                clock = options.ruby_clock,
180                voltage_domain = system.voltage_domain)
181            for i, cpu in enumerate(system.cpu):
182                if not cpu.switched_out:
183                    cpu.createInterruptController()
184                    cpu.connectCachedPorts(system.ruby._cpu_ports[i])
185        else:
186            sha_bus = self.create_caches_shared(system)
187            for cpu in system.cpu:
188                self.init_cpu(system, cpu, sha_bus)
189
190
191    def create_clk_src(self,system):
192        # Create system clock domain. This provides clock value to every
193        # clocked object that lies beneath it unless explicitly overwritten
194        # by a different clock domain.
195        system.voltage_domain = VoltageDomain()
196        system.clk_domain = SrcClockDomain(clock = '1GHz',
197                                           voltage_domain =
198                                           system.voltage_domain)
199
200        # Create a seperate clock domain for components that should
201        # run at CPUs frequency
202        system.cpu_clk_domain = SrcClockDomain(clock = '2GHz',
203                                               voltage_domain =
204                                               system.voltage_domain)
205
206    @abstractmethod
207    def create_system(self):
208        """Create an return an initialized system."""
209        pass
210
211    @abstractmethod
212    def create_root(self):
213        """Create and return a simulation root using the system
214        defined by this class."""
215        pass
216
217class BaseSESystem(BaseSystem):
218    """Basic syscall-emulation builder."""
219
220    def __init__(self, **kwargs):
221        BaseSystem.__init__(self, **kwargs)
222
223    def init_system(self, system):
224        BaseSystem.init_system(self, system)
225
226    def create_system(self):
227        system = System(physmem = self.mem_class(),
228                        membus = SystemXBar(),
229                        mem_mode = self.mem_mode,
230                        multi_thread = (self.num_threads > 1))
231        if not self.use_ruby:
232            system.system_port = system.membus.slave
233        system.physmem.port = system.membus.master
234        self.init_system(system)
235        return system
236
237    def create_root(self):
238        system = self.create_system()
239        m5.ticks.setGlobalFrequency('1THz')
240        return Root(full_system=False, system=system)
241
242class BaseSESystemUniprocessor(BaseSESystem):
243    """Basic syscall-emulation builder for uniprocessor systems.
244
245    Note: This class is only really needed to provide backwards
246    compatibility in existing test cases.
247    """
248
249    def __init__(self, **kwargs):
250        BaseSESystem.__init__(self, **kwargs)
251
252    def create_caches_private(self, cpu):
253        # The atomic SE configurations do not use caches
254        if self.mem_mode == "timing":
255            # @todo We might want to revisit these rather enthusiastic L1 sizes
256            cpu.addTwoLevelCacheHierarchy(L1_ICache(size='128kB'),
257                                          L1_DCache(size='256kB'),
258                                          L2Cache(size='2MB'))
259
260    def create_caches_shared(self, system):
261        return None
262
263class BaseFSSystem(BaseSystem):
264    """Basic full system builder."""
265
266    def __init__(self, **kwargs):
267        BaseSystem.__init__(self, **kwargs)
268
269    def init_system(self, system):
270        BaseSystem.init_system(self, system)
271
272        if self.use_ruby:
273            # Connect the ruby io port to the PIO bus,
274            # assuming that there is just one such port.
275            system.iobus.master = system.ruby._io_port.slave
276        else:
277            # create the memory controllers and connect them, stick with
278            # the physmem name to avoid bumping all the reference stats
279            system.physmem = [self.mem_class(range = r)
280                              for r in system.mem_ranges]
281            system.llc = [NoncoherentCache(addr_ranges = [r],
282                                           size = '4kB',
283                                           assoc = 2,
284                                           mshrs = 128,
285                                           tag_latency = 10,
286                                           data_latency = 10,
287                                           sequential_access = True,
288                                           response_latency = 20,
289                                           tgts_per_mshr = 8)
290                          for r in system.mem_ranges]
291            for i in range(len(system.physmem)):
292                system.physmem[i].port = system.llc[i].mem_side
293                system.llc[i].cpu_side = system.membus.master
294
295            # create the iocache, which by default runs at the system clock
296            system.iocache = IOCache(addr_ranges=system.mem_ranges)
297            system.iocache.cpu_side = system.iobus.master
298            system.iocache.mem_side = system.membus.slave
299
300    def create_root(self):
301        system = self.create_system()
302        m5.ticks.setGlobalFrequency('1THz')
303        return Root(full_system=True, system=system)
304
305class BaseFSSystemUniprocessor(BaseFSSystem):
306    """Basic full system builder for uniprocessor systems.
307
308    Note: This class is only really needed to provide backwards
309    compatibility in existing test cases.
310    """
311
312    def __init__(self, **kwargs):
313        BaseFSSystem.__init__(self, **kwargs)
314
315    def create_caches_private(self, cpu):
316        cpu.addTwoLevelCacheHierarchy(L1_ICache(size='32kB', assoc=1),
317                                      L1_DCache(size='32kB', assoc=4),
318                                      L2Cache(size='4MB', assoc=8))
319
320    def create_caches_shared(self, system):
321        return None
322
323class BaseFSSwitcheroo(BaseFSSystem):
324    """Uniprocessor system prepared for CPU switching"""
325
326    def __init__(self, cpu_classes, **kwargs):
327        BaseFSSystem.__init__(self, **kwargs)
328        self.cpu_classes = tuple(cpu_classes)
329
330    def create_cpus(self, cpu_clk_domain):
331        cpus = [ cclass(clk_domain = cpu_clk_domain,
332                        cpu_id=0,
333                        switched_out=True)
334                 for cclass in self.cpu_classes ]
335        cpus[0].switched_out = False
336        return cpus
337