This patch has been generated by applying flynt to the gem5 repo (ext has been excluded) JIRA: https://gem5.atlassian.net/browse/GEM5-831 Change-Id: I0935db6223d5426b99515959bde78e374cbadb04 Signed-off-by: Giacomo Travaglini <giacomo.travaglini@arm.com> Reviewed-on: https://gem5-review.googlesource.com/c/public/gem5/+/68957 Maintainer: Bobby Bruce <bbruce@ucdavis.edu> Tested-by: kokoro <noreply+kokoro@google.com> Reviewed-by: Bobby Bruce <bbruce@ucdavis.edu>
321 lines
12 KiB
Python
321 lines
12 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
|
|
from m5.objects.ResetPort import ResetResponsePort
|
|
|
|
default_tracer = ExeTracer()
|
|
|
|
|
|
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"),
|
|
PyBindMethod("scheduleSimpointsInstStop"),
|
|
PyBindMethod("scheduleInstStopAnyThread"),
|
|
]
|
|
|
|
@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(NULL, "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)",
|
|
)
|
|
|
|
model_reset = ResetResponsePort("Generic reset for the CPU")
|
|
|
|
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 = [
|
|
self.ArchInterrupts() for i in range(self.numThreads)
|
|
]
|
|
|
|
def connectCachedPorts(self, in_ports):
|
|
for p in self._cached_ports:
|
|
exec(f"self.{p} = in_ports")
|
|
|
|
def connectUncachedPorts(self, in_ports, out_ports):
|
|
for p in self._uncached_interrupt_response_ports:
|
|
exec(f"self.{p} = out_ports")
|
|
for p in self._uncached_interrupt_request_ports:
|
|
exec(f"self.{p} = in_ports")
|
|
|
|
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 += self.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 self.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 = [self.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([self.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(f"cpu@{reg:x}")
|
|
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 + self.ArchMMU.walkerPorts()
|
|
|
|
# Practically speaking, these ports will exist on the x86 interrupt
|
|
# controller class.
|
|
if "pio" in self.ArchInterrupts._ports:
|
|
self._uncached_interrupt_response_ports = (
|
|
self._uncached_interrupt_response_ports + ["interrupts[0].pio"]
|
|
)
|
|
if "int_responder" in self.ArchInterrupts._ports:
|
|
self._uncached_interrupt_response_ports = (
|
|
self._uncached_interrupt_response_ports
|
|
+ ["interrupts[0].int_responder"]
|
|
)
|
|
if "int_requestor" in self.ArchInterrupts._ports:
|
|
self._uncached_interrupt_request_ports = (
|
|
self._uncached_interrupt_request_ports
|
|
+ ["interrupts[0].int_requestor"]
|
|
)
|