| # Copyright (c) 2012-2013, 2017-2018, 2020 ARM Limited |
| # All rights reserved. |
| # |
| # The license below extends only to copyright in the software and shall |
| # not be construed as granting a license to any other intellectual |
| # property including but not limited to intellectual property relating |
| # to a hardware implementation of the functionality of the software |
| # licensed hereunder. You may use the software subject to the license |
| # terms below provided that you ensure that this notice is replicated |
| # unmodified and in its entirety in all distributions of the software, |
| # modified or unmodified, in source code or in binary form. |
| # |
| # Redistribution and use in source and binary forms, with or without |
| # modification, are permitted provided that the following conditions are |
| # met: redistributions of source code must retain the above copyright |
| # notice, this list of conditions and the following disclaimer; |
| # redistributions in binary form must reproduce the above copyright |
| # notice, this list of conditions and the following disclaimer in the |
| # documentation and/or other materials provided with the distribution; |
| # neither the name of the copyright holders nor the names of its |
| # contributors may be used to endorse or promote products derived from |
| # this software without specific prior written permission. |
| # |
| # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
| # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
| # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
| # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
| # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
| # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
| # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
| # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
| # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
| # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| |
| from abc import ABCMeta, abstractmethod |
| import argparse |
| import m5 |
| from m5.objects import * |
| from m5.proxy import * |
| from common import FSConfig |
| from common import Options |
| from common.Caches import * |
| from ruby import Ruby |
| |
| _have_kvm_support = 'BaseKvmCPU' in globals() |
| |
| class BaseSystem(object, metaclass=ABCMeta): |
| """Base system builder. |
| |
| This class provides some basic functionality for creating an ARM |
| system with the usual peripherals (caches, GIC, etc.). It allows |
| customization by defining separate methods for different parts of |
| the initialization process. |
| """ |
| |
| def __init__(self, mem_mode='timing', mem_class=SimpleMemory, |
| cpu_class=TimingSimpleCPU, num_cpus=1, num_threads=1, |
| checker=False, mem_size=None, use_ruby=False): |
| """Initialize a simple base system. |
| |
| Keyword Arguments: |
| mem_mode -- String describing the memory mode (timing or atomic) |
| mem_class -- Memory controller class to use |
| cpu_class -- CPU class to use |
| num_cpus -- Number of CPUs to instantiate |
| checker -- Set to True to add checker CPUs |
| mem_size -- Override the default memory size |
| use_ruby -- Set to True to use ruby memory |
| """ |
| self.mem_mode = mem_mode |
| self.mem_class = mem_class |
| self.cpu_class = cpu_class |
| self.num_cpus = num_cpus |
| self.num_threads = num_threads |
| self.checker = checker |
| self.use_ruby = use_ruby |
| |
| def create_cpus(self, cpu_clk_domain): |
| """Return a list of CPU objects to add to a system.""" |
| cpus = [ self.cpu_class(clk_domain=cpu_clk_domain, |
| numThreads=self.num_threads, |
| cpu_id=i) |
| for i in range(self.num_cpus) ] |
| if self.checker: |
| for c in cpus: |
| c.addCheckerCpu() |
| return cpus |
| |
| def create_caches_private(self, cpu): |
| """Add private caches to a CPU. |
| |
| Arguments: |
| cpu -- CPU instance to work on. |
| """ |
| cpu.addPrivateSplitL1Caches(L1_ICache(size='32kB', assoc=1), |
| L1_DCache(size='32kB', assoc=4)) |
| |
| def create_caches_shared(self, system): |
| """Add shared caches to a system. |
| |
| Arguments: |
| system -- System to work on. |
| |
| Returns: |
| A bus that CPUs should use to connect to the shared cache. |
| """ |
| system.toL2Bus = L2XBar(clk_domain=system.cpu_clk_domain) |
| system.l2c = L2Cache(clk_domain=system.cpu_clk_domain, |
| size='4MB', assoc=8) |
| system.l2c.cpu_side = system.toL2Bus.mem_side_ports |
| system.l2c.mem_side = system.membus.cpu_side_ports |
| return system.toL2Bus |
| |
| def init_cpu(self, system, cpu, sha_bus): |
| """Initialize a CPU. |
| |
| Arguments: |
| system -- System to work on. |
| cpu -- CPU to initialize. |
| """ |
| if not cpu.switched_out: |
| self.create_caches_private(cpu) |
| cpu.createInterruptController() |
| cached_bus = sha_bus if sha_bus != None else system.membus |
| cpu.connectAllPorts(cached_bus.cpu_side_ports, |
| system.membus.cpu_side_ports, |
| system.membus.mem_side_ports) |
| |
| def init_kvm_cpus(self, cpus): |
| """ |
| Assign KVM CPUs to their own event queues / threads. This |
| has to be done after creating caches and other child objects |
| since these mustn't inherit the CPU event queue. |
| |
| Arguments: |
| cpus -- List of cpus |
| """ |
| if len(cpus) > 1: |
| device_eq = 0 |
| first_cpu_eq = 1 |
| for idx, cpu in enumerate(cpus): |
| # Child objects usually inherit the parent's event |
| # queue. Override that and use the same event queue for |
| # all devices. |
| for obj in cpu.descendants(): |
| obj.eventq_index = device_eq |
| cpu.eventq_index = first_cpu_eq + idx |
| |
| def init_kvm(self, system): |
| """Do KVM-specific system initialization. |
| |
| Arguments: |
| system -- System to work on. |
| """ |
| system.kvm_vm = KvmVM() |
| |
| def init_system(self, system): |
| """Initialize a system. |
| |
| Arguments: |
| system -- System to initialize. |
| """ |
| self.create_clk_src(system) |
| system.cpu = self.create_cpus(system.cpu_clk_domain) |
| |
| if self.use_ruby: |
| # Add the ruby specific and protocol specific options |
| parser = argparse.ArgumentParser() |
| Options.addCommonOptions(parser) |
| Ruby.define_options(parser) |
| args, extra = parser.parse_known_args() |
| |
| # Set the default cache size and associativity to be very |
| # small to encourage races between requests and writebacks. |
| args.l1d_size="32kB" |
| args.l1i_size="32kB" |
| args.l2_size="4MB" |
| args.l1d_assoc=4 |
| args.l1i_assoc=2 |
| args.l2_assoc=8 |
| args.num_cpus = self.num_cpus |
| args.num_dirs = 2 |
| |
| bootmem = getattr(system, '_bootmem', None) |
| Ruby.create_system(args, True, system, system.iobus, |
| system._dma_ports, bootmem) |
| |
| # Create a seperate clock domain for Ruby |
| system.ruby.clk_domain = SrcClockDomain( |
| clock = args.ruby_clock, |
| voltage_domain = system.voltage_domain) |
| for i, cpu in enumerate(system.cpu): |
| if not cpu.switched_out: |
| cpu.createInterruptController() |
| cpu.connectCachedPorts(system.ruby._cpu_ports[i].in_ports) |
| else: |
| sha_bus = self.create_caches_shared(system) |
| for cpu in system.cpu: |
| self.init_cpu(system, cpu, sha_bus) |
| |
| if _have_kvm_support and \ |
| any([isinstance(c, BaseKvmCPU) for c in system.cpu]): |
| self.init_kvm(system) |
| self.init_kvm_cpus(system.cpu) |
| |
| def create_clk_src(self,system): |
| # Create system clock domain. This provides clock value to every |
| # clocked object that lies beneath it unless explicitly overwritten |
| # by a different clock domain. |
| system.voltage_domain = VoltageDomain() |
| system.clk_domain = SrcClockDomain(clock = '1GHz', |
| voltage_domain = |
| system.voltage_domain) |
| |
| # Create a seperate clock domain for components that should |
| # run at CPUs frequency |
| system.cpu_clk_domain = SrcClockDomain(clock = '2GHz', |
| voltage_domain = |
| system.voltage_domain) |
| |
| @abstractmethod |
| def create_system(self): |
| """Create an return an initialized system.""" |
| pass |
| |
| @abstractmethod |
| def create_root(self): |
| """Create and return a simulation root using the system |
| defined by this class.""" |
| pass |
| |
| class BaseSESystem(BaseSystem): |
| """Basic syscall-emulation builder.""" |
| |
| def __init__(self, **kwargs): |
| super(BaseSESystem, self).__init__(**kwargs) |
| |
| def init_system(self, system): |
| super(BaseSESystem, self).init_system(system) |
| |
| def create_system(self): |
| if issubclass(self.mem_class, m5.objects.DRAMInterface): |
| mem_ctrl = MemCtrl() |
| mem_ctrl.dram = self.mem_class() |
| else: |
| mem_ctrl = self.mem_class() |
| system = System(physmem = mem_ctrl, |
| membus = SystemXBar(), |
| mem_mode = self.mem_mode, |
| multi_thread = (self.num_threads > 1)) |
| if not self.use_ruby: |
| system.system_port = system.membus.cpu_side_ports |
| system.physmem.port = system.membus.mem_side_ports |
| self.init_system(system) |
| return system |
| |
| def create_root(self): |
| system = self.create_system() |
| m5.ticks.setGlobalFrequency('1THz') |
| return Root(full_system=False, system=system) |
| |
| class BaseSESystemUniprocessor(BaseSESystem): |
| """Basic syscall-emulation builder for uniprocessor systems. |
| |
| Note: This class is only really needed to provide backwards |
| compatibility in existing test cases. |
| """ |
| |
| def __init__(self, **kwargs): |
| super(BaseSESystemUniprocessor, self).__init__(**kwargs) |
| |
| def create_caches_private(self, cpu): |
| # The atomic SE configurations do not use caches |
| if self.mem_mode == "timing": |
| # @todo We might want to revisit these rather enthusiastic L1 sizes |
| cpu.addTwoLevelCacheHierarchy(L1_ICache(size='128kB'), |
| L1_DCache(size='256kB'), |
| L2Cache(size='2MB')) |
| |
| def create_caches_shared(self, system): |
| return None |
| |
| class BaseFSSystem(BaseSystem): |
| """Basic full system builder.""" |
| |
| def __init__(self, **kwargs): |
| super(BaseFSSystem, self).__init__(**kwargs) |
| |
| def init_system(self, system): |
| super(BaseFSSystem, self).init_system(system) |
| |
| if self.use_ruby: |
| # Connect the ruby io port to the PIO bus, |
| # assuming that there is just one such port. |
| system.iobus.mem_side_ports = system.ruby._io_port.in_ports |
| else: |
| # create the memory controllers and connect them, stick with |
| # the physmem name to avoid bumping all the reference stats |
| if issubclass(self.mem_class, m5.objects.DRAMInterface): |
| mem_ctrls = [] |
| for r in system.mem_ranges: |
| mem_ctrl = MemCtrl() |
| mem_ctrl.dram = self.mem_class(range = r) |
| mem_ctrls.append(mem_ctrl) |
| system.physmem = mem_ctrls |
| else: |
| system.physmem = [self.mem_class(range = r) |
| for r in system.mem_ranges] |
| for i in range(len(system.physmem)): |
| system.physmem[i].port = system.membus.mem_side_ports |
| |
| # create the iocache, which by default runs at the system clock |
| system.iocache = IOCache(addr_ranges=system.mem_ranges) |
| system.iocache.cpu_side = system.iobus.mem_side_ports |
| system.iocache.mem_side = system.membus.cpu_side_ports |
| |
| def create_root(self): |
| system = self.create_system() |
| m5.ticks.setGlobalFrequency('1THz') |
| return Root(full_system=True, system=system) |
| |
| class BaseFSSystemUniprocessor(BaseFSSystem): |
| """Basic full system builder for uniprocessor systems. |
| |
| Note: This class is only really needed to provide backwards |
| compatibility in existing test cases. |
| """ |
| |
| def __init__(self, **kwargs): |
| super(BaseFSSystemUniprocessor, self).__init__(**kwargs) |
| |
| def create_caches_private(self, cpu): |
| cpu.addTwoLevelCacheHierarchy(L1_ICache(size='32kB', assoc=1), |
| L1_DCache(size='32kB', assoc=4), |
| L2Cache(size='4MB', assoc=8)) |
| |
| def create_caches_shared(self, system): |
| return None |
| |
| class BaseFSSwitcheroo(BaseFSSystem): |
| """Uniprocessor system prepared for CPU switching""" |
| |
| def __init__(self, cpu_classes, **kwargs): |
| super(BaseFSSwitcheroo, self).__init__(**kwargs) |
| self.cpu_classes = tuple(cpu_classes) |
| |
| def create_cpus(self, cpu_clk_domain): |
| cpus = [ cclass(clk_domain = cpu_clk_domain, |
| cpu_id=0, |
| switched_out=True) |
| for cclass in self.cpu_classes ] |
| cpus[0].switched_out = False |
| return cpus |