Change-Id: I85839880db588b3b92064b8fcbf053c1811a1fdc Reviewed-on: https://gem5-review.googlesource.com/c/public/gem5/+/52080 Reviewed-by: Daniel Carvalho <odanrc@yahoo.com.br> Maintainer: Gabe Black <gabe.black@gmail.com> Tested-by: kokoro <noreply+kokoro@google.com>
326 lines
14 KiB
Python
326 lines
14 KiB
Python
# Copyright (c) 2012-2013, 2015-2017 ARM Limited
|
|
# Copyright (c) 2020 Barkhausen Institut
|
|
# 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.
|
|
#
|
|
# Copyright (c) 2005-2008 The Regents of The University of Michigan
|
|
# Copyright (c) 2011 Regents of the University of California
|
|
# All rights reserved.
|
|
#
|
|
# 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.
|
|
|
|
import sys
|
|
|
|
from m5.SimObject import *
|
|
from m5.defines import buildEnv
|
|
from m5.params import *
|
|
from m5.proxy import *
|
|
from m5.util.fdthelper import *
|
|
|
|
from m5.objects.ClockedObject import ClockedObject
|
|
from m5.objects.XBar import L2XBar
|
|
from m5.objects.InstTracer import InstTracer
|
|
from m5.objects.CPUTracers import ExeTracer
|
|
from m5.objects.SubSystem import SubSystem
|
|
from m5.objects.ClockDomain import *
|
|
from m5.objects.Platform import Platform
|
|
|
|
default_tracer = ExeTracer()
|
|
|
|
if buildEnv['TARGET_ISA'] == 'sparc':
|
|
from m5.objects.SparcMMU import SparcMMU as ArchMMU
|
|
from m5.objects.SparcInterrupts import SparcInterrupts as ArchInterrupts
|
|
from m5.objects.SparcISA import SparcISA as ArchISA
|
|
from m5.objects.SparcDecoder import SparcDecoder as ArchDecoder
|
|
elif buildEnv['TARGET_ISA'] == 'x86':
|
|
from m5.objects.X86MMU import X86MMU as ArchMMU
|
|
from m5.objects.X86LocalApic import X86LocalApic as ArchInterrupts
|
|
from m5.objects.X86ISA import X86ISA as ArchISA
|
|
from m5.objects.X86Decoder import X86Decoder as ArchDecoder
|
|
elif buildEnv['TARGET_ISA'] == 'mips':
|
|
from m5.objects.MipsMMU import MipsMMU as ArchMMU
|
|
from m5.objects.MipsInterrupts import MipsInterrupts as ArchInterrupts
|
|
from m5.objects.MipsISA import MipsISA as ArchISA
|
|
from m5.objects.MipsDecoder import MipsDecoder as ArchDecoder
|
|
elif buildEnv['TARGET_ISA'] == 'arm':
|
|
from m5.objects.ArmMMU import ArmMMU as ArchMMU
|
|
from m5.objects.ArmInterrupts import ArmInterrupts as ArchInterrupts
|
|
from m5.objects.ArmISA import ArmISA as ArchISA
|
|
from m5.objects.ArmDecoder import ArmDecoder as ArchDecoder
|
|
elif buildEnv['TARGET_ISA'] == 'power':
|
|
from m5.objects.PowerMMU import PowerMMU as ArchMMU
|
|
from m5.objects.PowerInterrupts import PowerInterrupts as ArchInterrupts
|
|
from m5.objects.PowerISA import PowerISA as ArchISA
|
|
from m5.objects.PowerDecoder import PowerDecoder as ArchDecoder
|
|
elif buildEnv['TARGET_ISA'] == 'riscv':
|
|
from m5.objects.RiscvMMU import RiscvMMU as ArchMMU
|
|
from m5.objects.RiscvInterrupts import RiscvInterrupts as ArchInterrupts
|
|
from m5.objects.RiscvISA import RiscvISA as ArchISA
|
|
from m5.objects.RiscvDecoder import RiscvDecoder as ArchDecoder
|
|
else:
|
|
print("Don't know what object types to use for ISA %s" %
|
|
buildEnv['TARGET_ISA'])
|
|
sys.exit(1)
|
|
|
|
class BaseCPU(ClockedObject):
|
|
type = 'BaseCPU'
|
|
abstract = True
|
|
cxx_header = "cpu/base.hh"
|
|
cxx_class = 'gem5::BaseCPU'
|
|
|
|
cxx_exports = [
|
|
PyBindMethod("switchOut"),
|
|
PyBindMethod("takeOverFrom"),
|
|
PyBindMethod("switchedOut"),
|
|
PyBindMethod("flushTLBs"),
|
|
PyBindMethod("totalInsts"),
|
|
PyBindMethod("scheduleInstStop"),
|
|
PyBindMethod("getCurrentInstCount"),
|
|
]
|
|
|
|
@classmethod
|
|
def memory_mode(cls):
|
|
"""Which memory mode does this CPU require?"""
|
|
return 'invalid'
|
|
|
|
@classmethod
|
|
def require_caches(cls):
|
|
"""Does the CPU model require caches?
|
|
|
|
Some CPU models might make assumptions that require them to
|
|
have caches.
|
|
"""
|
|
return False
|
|
|
|
@classmethod
|
|
def support_take_over(cls):
|
|
"""Does the CPU model support CPU takeOverFrom?"""
|
|
return False
|
|
|
|
def takeOverFrom(self, old_cpu):
|
|
self._ccObject.takeOverFrom(old_cpu._ccObject)
|
|
|
|
|
|
system = Param.System(Parent.any, "system object")
|
|
cpu_id = Param.Int(-1, "CPU identifier")
|
|
socket_id = Param.Unsigned(0, "Physical Socket identifier")
|
|
numThreads = Param.Unsigned(1, "number of HW thread contexts")
|
|
pwr_gating_latency = Param.Cycles(300,
|
|
"Latency to enter power gating state when all contexts are suspended")
|
|
|
|
power_gating_on_idle = Param.Bool(False, "Control whether the core goes "\
|
|
"to the OFF power state after all thread are disabled for "\
|
|
"pwr_gating_latency cycles")
|
|
|
|
function_trace = Param.Bool(False, "Enable function trace")
|
|
function_trace_start = Param.Tick(0, "Tick to start function trace")
|
|
|
|
checker = Param.BaseCPU(NULL, "checker CPU")
|
|
|
|
syscallRetryLatency = Param.Cycles(10000, "Cycles to wait until retry")
|
|
|
|
do_checkpoint_insts = Param.Bool(True,
|
|
"enable checkpoint pseudo instructions")
|
|
do_statistics_insts = Param.Bool(True,
|
|
"enable statistics pseudo instructions")
|
|
|
|
workload = VectorParam.Process([], "processes to run")
|
|
|
|
mmu = Param.BaseMMU(ArchMMU(), "CPU memory management unit")
|
|
interrupts = VectorParam.BaseInterrupts([], "Interrupt Controller")
|
|
isa = VectorParam.BaseISA([], "ISA instance")
|
|
decoder = VectorParam.InstDecoder([], "Decoder instance")
|
|
|
|
max_insts_all_threads = Param.Counter(0,
|
|
"terminate when all threads have reached this inst count")
|
|
max_insts_any_thread = Param.Counter(0,
|
|
"terminate when any thread reaches this inst count")
|
|
simpoint_start_insts = VectorParam.Counter([],
|
|
"starting instruction counts of simpoints")
|
|
progress_interval = Param.Frequency('0Hz',
|
|
"frequency to print out the progress message")
|
|
|
|
switched_out = Param.Bool(False,
|
|
"Leave the CPU switched out after startup (used when switching " \
|
|
"between CPU models)")
|
|
|
|
tracer = Param.InstTracer(default_tracer, "Instruction tracer")
|
|
|
|
icache_port = RequestPort("Instruction Port")
|
|
dcache_port = RequestPort("Data Port")
|
|
_cached_ports = ['icache_port', 'dcache_port']
|
|
|
|
_uncached_interrupt_response_ports = []
|
|
_uncached_interrupt_request_ports = []
|
|
|
|
def createInterruptController(self):
|
|
self.interrupts = [ArchInterrupts() for i in range(self.numThreads)]
|
|
|
|
def connectCachedPorts(self, in_ports):
|
|
for p in self._cached_ports:
|
|
exec('self.%s = in_ports' % p)
|
|
|
|
def connectUncachedPorts(self, in_ports, out_ports):
|
|
for p in self._uncached_interrupt_response_ports:
|
|
exec('self.%s = out_ports' % p)
|
|
for p in self._uncached_interrupt_request_ports:
|
|
exec('self.%s = in_ports' % p)
|
|
|
|
def connectAllPorts(self, cached_in, uncached_in, uncached_out):
|
|
self.connectCachedPorts(cached_in)
|
|
self.connectUncachedPorts(uncached_in, uncached_out)
|
|
|
|
def connectBus(self, bus):
|
|
self.connectAllPorts(bus.cpu_side_ports,
|
|
bus.cpu_side_ports, bus.mem_side_ports)
|
|
|
|
def addPrivateSplitL1Caches(self, ic, dc, iwc = None, dwc = None):
|
|
self.icache = ic
|
|
self.dcache = dc
|
|
self.icache_port = ic.cpu_side
|
|
self.dcache_port = dc.cpu_side
|
|
self._cached_ports = ['icache.mem_side', 'dcache.mem_side']
|
|
if iwc and dwc:
|
|
self.itb_walker_cache = iwc
|
|
self.dtb_walker_cache = dwc
|
|
self.mmu.connectWalkerPorts(
|
|
iwc.cpu_side, dwc.cpu_side)
|
|
self._cached_ports += ["itb_walker_cache.mem_side", \
|
|
"dtb_walker_cache.mem_side"]
|
|
else:
|
|
self._cached_ports += ArchMMU.walkerPorts()
|
|
|
|
# Checker doesn't need its own tlb caches because it does
|
|
# functional accesses only
|
|
if self.checker != NULL:
|
|
self._cached_ports += [ "checker." + port
|
|
for port in ArchMMU.walkerPorts() ]
|
|
|
|
def addTwoLevelCacheHierarchy(self, ic, dc, l2c, iwc=None, dwc=None,
|
|
xbar=None):
|
|
self.addPrivateSplitL1Caches(ic, dc, iwc, dwc)
|
|
self.toL2Bus = xbar if xbar else L2XBar()
|
|
self.connectCachedPorts(self.toL2Bus.cpu_side_ports)
|
|
self.l2cache = l2c
|
|
self.toL2Bus.mem_side_ports = self.l2cache.cpu_side
|
|
self._cached_ports = ['l2cache.mem_side']
|
|
|
|
def createThreads(self):
|
|
# If no ISAs have been created, assume that the user wants the
|
|
# default ISA.
|
|
if len(self.isa) == 0:
|
|
self.isa = list([ ArchISA() for i in range(self.numThreads) ])
|
|
else:
|
|
if len(self.isa) != int(self.numThreads):
|
|
raise RuntimeError("Number of ISA instances doesn't "
|
|
"match thread count")
|
|
if len(self.decoder) != 0:
|
|
raise RuntimeError("Decoders should not be set up manually")
|
|
self.decoder = list([ ArchDecoder(isa=isa) for isa in self.isa ])
|
|
if self.checker != NULL:
|
|
self.checker.createThreads()
|
|
|
|
def addCheckerCpu(self):
|
|
pass
|
|
|
|
def createPhandleKey(self, thread):
|
|
# This method creates a unique key for this cpu as a function of a
|
|
# certain thread
|
|
return 'CPU-%d-%d-%d' % (self.socket_id, self.cpu_id, thread)
|
|
|
|
#Generate simple CPU Device Tree structure
|
|
def generateDeviceTree(self, state):
|
|
"""Generate cpu nodes for each thread and the corresponding part of the
|
|
cpu-map node. Note that this implementation does not support clusters
|
|
of clusters. Note that GEM5 is not compatible with the official way of
|
|
numbering cores as defined in the Device Tree documentation. Where the
|
|
cpu_id needs to reset to 0 for each cluster by specification, GEM5
|
|
expects the cpu_id to be globally unique and incremental. This
|
|
generated node adheres the GEM5 way of doing things."""
|
|
if bool(self.switched_out):
|
|
return
|
|
|
|
cpus_node = FdtNode('cpus')
|
|
cpus_node.append(state.CPUCellsProperty())
|
|
#Special size override of 0
|
|
cpus_node.append(FdtPropertyWords('#size-cells', [0]))
|
|
|
|
# Generate cpu nodes
|
|
for i in range(int(self.numThreads)):
|
|
reg = (int(self.socket_id)<<8) + int(self.cpu_id) + i
|
|
node = FdtNode("cpu@%x" % reg)
|
|
node.append(FdtPropertyStrings("device_type", "cpu"))
|
|
node.appendCompatible(["gem5,arm-cpu"])
|
|
node.append(FdtPropertyWords("reg", state.CPUAddrCells(reg)))
|
|
platform, found = self.system.unproxy(self).find_any(Platform)
|
|
if found:
|
|
platform.annotateCpuDeviceNode(node, state)
|
|
else:
|
|
warn("Platform not found for device tree generation; " \
|
|
"system or multiple CPUs may not start")
|
|
|
|
freq = int(self.clk_domain.unproxy(self).clock[0].frequency)
|
|
node.append(FdtPropertyWords("clock-frequency", freq))
|
|
|
|
# Unique key for this CPU
|
|
phandle_key = self.createPhandleKey(i)
|
|
node.appendPhandle(phandle_key)
|
|
cpus_node.append(node)
|
|
|
|
yield cpus_node
|
|
|
|
# Generate nodes from the BaseCPU children (hence under the root node,
|
|
# and don't add them as subnode). Please note: this is mainly needed
|
|
# for the ISA class, to generate the PMU entry in the DTB.
|
|
for child_node in self.recurseDeviceTree(state):
|
|
yield child_node
|
|
|
|
def __init__(self, **kwargs):
|
|
super().__init__(**kwargs)
|
|
self.power_state.possible_states=['ON', 'CLK_GATED', 'OFF']
|
|
|
|
self._cached_ports = self._cached_ports + ArchMMU.walkerPorts()
|
|
|
|
# Practically speaking, these ports will exist on the x86 interrupt
|
|
# controller class.
|
|
if "pio" in ArchInterrupts._ports:
|
|
self._uncached_interrupt_response_ports = \
|
|
self._uncached_interrupt_response_ports + ["interrupts[0].pio"]
|
|
if "int_responder" in ArchInterrupts._ports:
|
|
self._uncached_interrupt_response_ports = \
|
|
self._uncached_interrupt_response_ports + [
|
|
"interrupts[0].int_responder"]
|
|
if "int_requestor" in ArchInterrupts._ports:
|
|
self._uncached_interrupt_request_ports = \
|
|
self._uncached_interrupt_request_ports + [
|
|
"interrupts[0].int_requestor"]
|