stdlib: Create BaseCPUCore type
This separates the idea of a SimpleCore and a BaseCPUCore. A SimpleCore selects the correct BaseCPU subclass based on user-specified CPUTypes and target ISA. The new BaseCPUCore type simply wraps any BaseCPU core for usage in the stdlib. Much of the code previously handled in SimpleCore has been moved to BaseCPUCore. The `cpu_simobject_factory` method has been moved from AbstractCore to SimpleCore; a more logical location for this function. Change-Id: I29ce9e381e7d5e8fe57e0db5deb04ad976b7dab9 Reviewed-on: https://gem5-review.googlesource.com/c/public/gem5/+/62292 Maintainer: Jason Lowe-Power <power.jg@gmail.com> Tested-by: kokoro <noreply+kokoro@google.com> Reviewed-by: Jason Lowe-Power <power.jg@gmail.com>
This commit is contained in:
committed by
Bobby Bruce
parent
d023d8a3dd
commit
d9b6a7ff9e
@@ -200,6 +200,8 @@ PySource('gem5.components.processors',
|
||||
'gem5/components/processors/random_generator.py')
|
||||
PySource('gem5.components.processors',
|
||||
'gem5/components/processors/simple_core.py')
|
||||
PySource('gem5.components.processors',
|
||||
'gem5/components/processors/base_cpu_core.py')
|
||||
PySource('gem5.components.processors',
|
||||
'gem5/components/processors/simple_processor.py')
|
||||
PySource('gem5.components.processors',
|
||||
|
||||
@@ -26,12 +26,8 @@
|
||||
|
||||
from abc import ABCMeta, abstractmethod
|
||||
from typing import Optional
|
||||
import importlib
|
||||
import platform
|
||||
|
||||
from .cpu_types import CPUTypes
|
||||
from ...isas import ISA
|
||||
from ...utils.requires import requires
|
||||
|
||||
from m5.objects import BaseMMU, Port, SubSystem
|
||||
|
||||
@@ -105,84 +101,3 @@ class AbstractCore(SubSystem):
|
||||
This is used in the board to setup system-specific MMU settings.
|
||||
"""
|
||||
raise NotImplementedError
|
||||
|
||||
@classmethod
|
||||
def cpu_simobject_factory(cls, cpu_type: CPUTypes, isa: ISA, core_id: int):
|
||||
"""
|
||||
A factory used to return the SimObject core object given the cpu type,
|
||||
and ISA target. An exception will be thrown if there is an
|
||||
incompatibility.
|
||||
|
||||
:param cpu_type: The target CPU type.
|
||||
:param isa: The target ISA.
|
||||
:param core_id: The id of the core to be returned.
|
||||
"""
|
||||
requires(isa_required=isa)
|
||||
|
||||
_isa_string_map = {
|
||||
ISA.X86: "X86",
|
||||
ISA.ARM: "Arm",
|
||||
ISA.RISCV: "Riscv",
|
||||
ISA.SPARC: "Sparc",
|
||||
ISA.POWER: "Power",
|
||||
ISA.MIPS: "Mips",
|
||||
}
|
||||
|
||||
_cpu_types_string_map = {
|
||||
CPUTypes.ATOMIC: "AtomicSimpleCPU",
|
||||
CPUTypes.O3: "O3CPU",
|
||||
CPUTypes.TIMING: "TimingSimpleCPU",
|
||||
CPUTypes.KVM: "KvmCPU",
|
||||
CPUTypes.MINOR: "MinorCPU",
|
||||
}
|
||||
|
||||
if isa not in _isa_string_map:
|
||||
raise NotImplementedError(
|
||||
f"ISA '{isa.name}' does not have an"
|
||||
"entry in `AbstractCore.cpu_simobject_factory._isa_string_map`"
|
||||
)
|
||||
|
||||
if cpu_type not in _cpu_types_string_map:
|
||||
raise NotImplementedError(
|
||||
f"CPUType '{cpu_type.name}' "
|
||||
"does not have an entry in "
|
||||
"`AbstractCore.cpu_simobject_factory._cpu_types_string_map`"
|
||||
)
|
||||
|
||||
if cpu_type == CPUTypes.KVM:
|
||||
# For some reason, the KVM CPU is under "m5.objects" not the
|
||||
# "m5.objects.{ISA}CPU".
|
||||
module_str = f"m5.objects"
|
||||
else:
|
||||
module_str = f"m5.objects.{_isa_string_map[isa]}CPU"
|
||||
|
||||
# GEM5 compiles two versions of KVM for ARM depending upon the host CPU
|
||||
# : ArmKvmCPU and ArmV8KvmCPU for 32 bit (Armv7l) and 64 bit (Armv8)
|
||||
# respectively.
|
||||
|
||||
if (
|
||||
isa.name == "ARM"
|
||||
and cpu_type == CPUTypes.KVM
|
||||
and platform.architecture()[0] == "64bit"
|
||||
):
|
||||
cpu_class_str = (
|
||||
f"{_isa_string_map[isa]}V8"
|
||||
f"{_cpu_types_string_map[cpu_type]}"
|
||||
)
|
||||
else:
|
||||
cpu_class_str = (
|
||||
f"{_isa_string_map[isa]}" f"{_cpu_types_string_map[cpu_type]}"
|
||||
)
|
||||
|
||||
try:
|
||||
to_return_cls = getattr(
|
||||
importlib.import_module(module_str), cpu_class_str
|
||||
)
|
||||
except ImportError:
|
||||
raise Exception(
|
||||
f"Cannot find CPU type '{cpu_type.name}' for '{isa.name}' "
|
||||
"ISA. Please ensure you have compiled the correct version of "
|
||||
"gem5."
|
||||
)
|
||||
|
||||
return to_return_cls(cpu_id=core_id)
|
||||
|
||||
117
src/python/gem5/components/processors/base_cpu_core.py
Normal file
117
src/python/gem5/components/processors/base_cpu_core.py
Normal file
@@ -0,0 +1,117 @@
|
||||
# Copyright (c) 2022 The 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.
|
||||
|
||||
from typing import Optional
|
||||
from ...utils.requires import requires
|
||||
from .abstract_core import AbstractCore
|
||||
|
||||
from ...isas import ISA
|
||||
from ...runtime import get_runtime_isa
|
||||
from ...utils.override import overrides
|
||||
from ...utils.requires import requires
|
||||
|
||||
from m5.objects import BaseMMU, Port, BaseCPU, Process
|
||||
|
||||
|
||||
class BaseCPUCore(AbstractCore):
|
||||
"""
|
||||
An stdlib AbstractCore subclass which wraps a BaseCPU SimObject type.
|
||||
"""
|
||||
|
||||
def __init__(self, core: BaseCPU, isa: Optional[ISA] = None):
|
||||
super().__init__()
|
||||
|
||||
# There is some annoying redundancy here. The BaseCPU type already
|
||||
# defines the ISA, so here we are defining it twice. However, there
|
||||
# currently isn't a good way to get the ISA from the BaseCPU Type.
|
||||
if isa:
|
||||
requires(isa_required=isa)
|
||||
self._isa = isa
|
||||
else:
|
||||
self._isa = get_runtime_isa()
|
||||
|
||||
self.core = core
|
||||
self.core.createThreads()
|
||||
|
||||
def get_simobject(self) -> BaseCPU:
|
||||
return self.core
|
||||
|
||||
@overrides(AbstractCore)
|
||||
def get_isa(self) -> ISA:
|
||||
return self._isa
|
||||
|
||||
@overrides(AbstractCore)
|
||||
def connect_icache(self, port: Port) -> None:
|
||||
self.core.icache_port = port
|
||||
|
||||
@overrides(AbstractCore)
|
||||
def connect_dcache(self, port: Port) -> None:
|
||||
self.core.dcache_port = port
|
||||
|
||||
@overrides(AbstractCore)
|
||||
def connect_walker_ports(self, port1: Port, port2: Port) -> None:
|
||||
if self.get_isa() == ISA.ARM:
|
||||
|
||||
# Unlike X86 and RISCV MMU, the ARM MMU has two L1 TLB walker ports
|
||||
# named `walker` and `stage2_walker` for both data and instruction.
|
||||
# The gem5 standard library currently supports one TLB walker port
|
||||
# per cache level. Therefore, we are explicitly setting the walker
|
||||
# ports and not setting the stage2_walker ports for ARM systems.
|
||||
|
||||
self.core.mmu.itb_walker.port = port1
|
||||
self.core.mmu.dtb_walker.port = port2
|
||||
else:
|
||||
self.core.mmu.connectWalkerPorts(port1, port2)
|
||||
|
||||
@overrides(AbstractCore)
|
||||
def set_workload(self, process: Process) -> None:
|
||||
self.core.workload = process
|
||||
|
||||
@overrides(AbstractCore)
|
||||
def set_switched_out(self, value: bool) -> None:
|
||||
self.core.switched_out = value
|
||||
|
||||
@overrides(AbstractCore)
|
||||
def connect_interrupt(
|
||||
self,
|
||||
interrupt_requestor: Optional[Port] = None,
|
||||
interrupt_responce: Optional[Port] = None,
|
||||
) -> None:
|
||||
|
||||
# TODO: This model assumes that we will only create an interrupt
|
||||
# controller as we require it. Not sure how true this is in all cases.
|
||||
self.core.createInterruptController()
|
||||
|
||||
if self.get_isa().value == ISA.X86.value:
|
||||
if interrupt_requestor != None:
|
||||
self.core.interrupts[0].pio = interrupt_requestor
|
||||
self.core.interrupts[0].int_responder = interrupt_requestor
|
||||
if interrupt_responce != None:
|
||||
self.core.interrupts[0].int_requestor = interrupt_responce
|
||||
|
||||
@overrides(AbstractCore)
|
||||
def get_mmu(self) -> BaseMMU:
|
||||
return self.core.mmu
|
||||
@@ -26,18 +26,15 @@
|
||||
|
||||
from typing import Optional
|
||||
from ...utils.requires import requires
|
||||
from ..processors.abstract_core import AbstractCore
|
||||
|
||||
from .base_cpu_core import BaseCPUCore
|
||||
from .cpu_types import CPUTypes
|
||||
from ...isas import ISA
|
||||
from ...runtime import get_runtime_isa
|
||||
from ...utils.override import overrides
|
||||
from ...utils.requires import requires
|
||||
|
||||
from m5.objects import BaseMMU, Port, BaseCPU, Process
|
||||
import importlib
|
||||
import platform
|
||||
|
||||
|
||||
class SimpleCore(AbstractCore):
|
||||
class SimpleCore(BaseCPUCore):
|
||||
"""
|
||||
A SimpleCore instantiates a core based on the CPUType enum pass. The
|
||||
SimpleCore creates a single SimObject of that type.
|
||||
@@ -46,81 +43,95 @@ class SimpleCore(AbstractCore):
|
||||
def __init__(
|
||||
self, cpu_type: CPUTypes, core_id: int, isa: Optional[ISA] = None
|
||||
):
|
||||
super().__init__()
|
||||
super().__init__(
|
||||
core=SimpleCore.cpu_simobject_factory(
|
||||
isa=isa, cpu_type=cpu_type, core_id=core_id
|
||||
),
|
||||
isa=isa,
|
||||
)
|
||||
|
||||
self._cpu_type = cpu_type
|
||||
if cpu_type == CPUTypes.KVM:
|
||||
requires(kvm_required=True)
|
||||
|
||||
if isa:
|
||||
requires(isa_required=isa)
|
||||
self._isa = isa
|
||||
else:
|
||||
self._isa = get_runtime_isa()
|
||||
self.core = AbstractCore.cpu_simobject_factory(
|
||||
isa=self._isa, cpu_type=cpu_type, core_id=core_id
|
||||
)
|
||||
self.core.createThreads()
|
||||
|
||||
def get_simobject(self) -> BaseCPU:
|
||||
return self.core
|
||||
|
||||
def get_type(self) -> CPUTypes:
|
||||
return self._cpu_type
|
||||
|
||||
@overrides(AbstractCore)
|
||||
def get_isa(self) -> ISA:
|
||||
return self._isa
|
||||
@classmethod
|
||||
def cpu_simobject_factory(cls, cpu_type: CPUTypes, isa: ISA, core_id: int):
|
||||
"""
|
||||
A factory used to return the SimObject core object given the cpu type,
|
||||
and ISA target. An exception will be thrown if there is an
|
||||
incompatibility.
|
||||
|
||||
@overrides(AbstractCore)
|
||||
def connect_icache(self, port: Port) -> None:
|
||||
self.core.icache_port = port
|
||||
:param cpu_type: The target CPU type.
|
||||
:param isa: The target ISA.
|
||||
:param core_id: The id of the core to be returned.
|
||||
"""
|
||||
requires(isa_required=isa)
|
||||
|
||||
@overrides(AbstractCore)
|
||||
def connect_dcache(self, port: Port) -> None:
|
||||
self.core.dcache_port = port
|
||||
_isa_string_map = {
|
||||
ISA.X86: "X86",
|
||||
ISA.ARM: "Arm",
|
||||
ISA.RISCV: "Riscv",
|
||||
ISA.SPARC: "Sparc",
|
||||
ISA.POWER: "Power",
|
||||
ISA.MIPS: "Mips",
|
||||
}
|
||||
|
||||
@overrides(AbstractCore)
|
||||
def connect_walker_ports(self, port1: Port, port2: Port) -> None:
|
||||
if self.get_isa() == ISA.ARM:
|
||||
_cpu_types_string_map = {
|
||||
CPUTypes.ATOMIC: "AtomicSimpleCPU",
|
||||
CPUTypes.O3: "O3CPU",
|
||||
CPUTypes.TIMING: "TimingSimpleCPU",
|
||||
CPUTypes.KVM: "KvmCPU",
|
||||
CPUTypes.MINOR: "MinorCPU",
|
||||
}
|
||||
|
||||
# Unlike X86 and RISCV MMU, the ARM MMU has two L1 TLB walker ports
|
||||
# named `walker` and `stage2_walker` for both data and instruction.
|
||||
# The gem5 standard library currently supports one TLB walker port
|
||||
# per cache level. Therefore, we are explicitly setting the walker
|
||||
# ports and not setting the stage2_walker ports for ARM systems.
|
||||
if isa not in _isa_string_map:
|
||||
raise NotImplementedError(
|
||||
f"ISA '{isa.name}' does not have an"
|
||||
"entry in `AbstractCore.cpu_simobject_factory._isa_string_map`"
|
||||
)
|
||||
|
||||
self.core.mmu.itb_walker.port = port1
|
||||
self.core.mmu.dtb_walker.port = port2
|
||||
if cpu_type not in _cpu_types_string_map:
|
||||
raise NotImplementedError(
|
||||
f"CPUType '{cpu_type.name}' "
|
||||
"does not have an entry in "
|
||||
"`AbstractCore.cpu_simobject_factory._cpu_types_string_map`"
|
||||
)
|
||||
|
||||
if cpu_type == CPUTypes.KVM:
|
||||
# For some reason, the KVM CPU is under "m5.objects" not the
|
||||
# "m5.objects.{ISA}CPU".
|
||||
module_str = f"m5.objects"
|
||||
else:
|
||||
self.core.mmu.connectWalkerPorts(port1, port2)
|
||||
module_str = f"m5.objects.{_isa_string_map[isa]}CPU"
|
||||
|
||||
@overrides(AbstractCore)
|
||||
def set_workload(self, process: Process) -> None:
|
||||
self.core.workload = process
|
||||
# GEM5 compiles two versions of KVM for ARM depending upon the host CPU
|
||||
# : ArmKvmCPU and ArmV8KvmCPU for 32 bit (Armv7l) and 64 bit (Armv8)
|
||||
# respectively.
|
||||
|
||||
@overrides(AbstractCore)
|
||||
def set_switched_out(self, value: bool) -> None:
|
||||
self.core.switched_out = value
|
||||
if (
|
||||
isa.name == "ARM"
|
||||
and cpu_type == CPUTypes.KVM
|
||||
and platform.architecture()[0] == "64bit"
|
||||
):
|
||||
cpu_class_str = (
|
||||
f"{_isa_string_map[isa]}V8"
|
||||
f"{_cpu_types_string_map[cpu_type]}"
|
||||
)
|
||||
else:
|
||||
cpu_class_str = (
|
||||
f"{_isa_string_map[isa]}" f"{_cpu_types_string_map[cpu_type]}"
|
||||
)
|
||||
|
||||
@overrides(AbstractCore)
|
||||
def connect_interrupt(
|
||||
self,
|
||||
interrupt_requestor: Optional[Port] = None,
|
||||
interrupt_responce: Optional[Port] = None,
|
||||
) -> None:
|
||||
try:
|
||||
to_return_cls = getattr(
|
||||
importlib.import_module(module_str), cpu_class_str
|
||||
)
|
||||
except ImportError:
|
||||
raise Exception(
|
||||
f"Cannot find CPU type '{cpu_type.name}' for '{isa.name}' "
|
||||
"ISA. Please ensure you have compiled the correct version of "
|
||||
"gem5."
|
||||
)
|
||||
|
||||
# TODO: This model assumes that we will only create an interrupt
|
||||
# controller as we require it. Not sure how true this is in all cases.
|
||||
self.core.createInterruptController()
|
||||
|
||||
if self.get_isa() == ISA.X86:
|
||||
if interrupt_requestor != None:
|
||||
self.core.interrupts[0].pio = interrupt_requestor
|
||||
self.core.interrupts[0].int_responder = interrupt_requestor
|
||||
if interrupt_responce != None:
|
||||
self.core.interrupts[0].int_requestor = interrupt_responce
|
||||
|
||||
@overrides(AbstractCore)
|
||||
def get_mmu(self) -> BaseMMU:
|
||||
return self.core.mmu
|
||||
return to_return_cls(cpu_id=core_id)
|
||||
|
||||
Reference in New Issue
Block a user