stdlib: Extend AbstractBoard pre_instantiation functionality (#1497)

* Deprecates the setting of FS/SE mode via the `Simulator` module.
* Moved the creation of the `Root` object from the `Simulator` to the
board.
* Moved the setting of `sim_quantum` from the `Simulator` to the
processor.
* Allows for easier development of boards which support both SE and FS
mode simulation by moving board setup function calls to occur after the
set_workload function is call which sets a boards stats `is_fs` status.
This commit is contained in:
Ivana Mitrovic
2024-10-14 10:12:41 -07:00
committed by GitHub
15 changed files with 164 additions and 77 deletions

View File

@@ -110,8 +110,7 @@ board.set_kernel_disk_workload(
# Begin running of the simulation.
print("Running with ISA: " + processor.get_isa().name)
print()
root = Root(full_system=True, system=board)
board._pre_instantiate()
root = board._pre_instantiate()
m5.instantiate()
print("Beginning simulation!")

View File

@@ -41,6 +41,7 @@ from m5.objects import (
ClockDomain,
IOXBar,
Port,
Root,
SrcClockDomain,
System,
VoltageDomain,
@@ -117,12 +118,6 @@ class AbstractBoard:
# Simulator module.
self._checkpoint = None
# Setup the board and memory system's memory ranges.
self._setup_memory_ranges()
# Setup board properties unique to the board being constructed.
self._setup_board()
# A private variable to record whether `_connect_things` has been
# been called.
self._connect_things_called = False
@@ -194,6 +189,9 @@ class AbstractBoard:
"""
self._is_fs = is_fs
self._setup_memory_ranges()
self._setup_board()
def is_fullsystem(self) -> bool:
"""
Returns ``True`` if the board is to be run in FS mode. Otherwise the board
@@ -252,11 +250,14 @@ class AbstractBoard:
@abstractmethod
def _setup_board(self) -> None:
"""
This function is called in the AbstractBoard constructor, before the
memory, processor, and cache hierarchy components are incorporated via
``_connect_thing()``, but after the ``_setup_memory_ranges()`` function.
This function should be overridden by boards to specify components,
connections unique to that board.
This function is called at the end of `_set_fullsystem`. The reason for
this is the board's configuraiton varies significantly depending on
whether it is to be run in FS or SE mode. This function is therefore
called when a workload is set --- after construction but before
`_pre_instantiate` is called.
As `_setup_memory_ranges()` is set in the constructor, this function
can be considered to have been called prior to `_setup_board
"""
raise NotImplementedError
@@ -330,10 +331,18 @@ class AbstractBoard:
"""
Set the memory ranges for this board and memory system.
This is called in the constructor, prior to ``_setup_board`` and
``_connect_things``. It should query the board's memory to determine the
size and the set the memory ranges on the memory system and on the
board.
This is called at the end of the `_set_fullsystem` function but before
`_setup_board`. `_set_fullsystem` is called when the workload is
declared. It is before `_pre_instantiate` (but, obviously after
construction).
It should query the board's memory
to determine the size and the set the memory ranges on the memory
system and on the board.
As thisis called at the end of `_set_fullsystem`, the board's memory
can be setup differently depending on whether the board is to be run in
FS or SE mode.
The simplest implementation sets the board's memory range to the size
of memory and memory system's range to be the same as the board. Full
@@ -391,13 +400,42 @@ class AbstractBoard:
self.get_cache_hierarchy()._post_instantiate()
self.get_memory()._post_instantiate()
def _pre_instantiate(self):
def _pre_instantiate(self, full_system: Optional[bool] = None) -> Root:
"""To be called immediately before ``m5.instantiate``. This is where
``_connect_things`` is executed by default."""
``_connect_things`` is executed by default and the root object is Root
object is created and returned.
# Connect the memory, processor, and cache hierarchy.
:param full_system: Used to pass the full system flag to the board from
the Simulator module. **Note**: This was
implemented solely to maintain backawards
compatibility with while the Simululator module's
`full_system` flag is in state of deprecation. This
parameter will be removed when it is. When this
occurs whether a simulation is to be run in FS or
SE mode will be determined by the board set."""
# 1. Connect the memory, processor, and cache hierarchy.
self._connect_things()
# 2. Create the root object
root = Root(
full_system=(
full_system
if full_system is not None
else self.is_fullsystem()
),
board=self,
)
# 3. Call any of the components' `_pre_instantiate` functions.
self.get_processor()._pre_instantiate(root)
self.get_memory()._pre_instantiate(root)
if self.get_cache_hierarchy():
self.get_cache_hierarchy()._pre_instantiate(root)
# 4. Return the root object.
return root
def _connect_things_check(self):
"""
Here we check that connect things has been called and throw an

View File

@@ -28,6 +28,7 @@ import os
from abc import ABCMeta
from typing import (
List,
Optional,
Sequence,
Tuple,
)
@@ -331,8 +332,8 @@ class ArmBoard(ArmSystem, AbstractBoard, KernelDiskWorkload):
self.system_port = port
@overrides(AbstractBoard)
def _pre_instantiate(self):
super()._pre_instantiate()
def _pre_instantiate(self, full_system: Optional[bool] = None) -> None:
super()._pre_instantiate(full_system=full_system)
# Add the PCI devices.
self.pci_devices = self._pci_devices

View File

@@ -26,7 +26,10 @@
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
import os
from typing import List
from typing import (
List,
Optional,
)
import m5
from m5.objects import (
@@ -498,7 +501,7 @@ class RiscvBoard(AbstractSystemBoard, KernelDiskWorkload):
return "/dev/vda"
@overrides(AbstractSystemBoard)
def _pre_instantiate(self):
def _pre_instantiate(self, full_system: Optional[bool] = None):
if len(self._bootloader) > 0:
self.workload.bootloader_addr = 0x0
self.workload.bootloader_filename = self._bootloader[0]
@@ -507,7 +510,7 @@ class RiscvBoard(AbstractSystemBoard, KernelDiskWorkload):
else:
self.workload.kernel_addr = 0x0
self.workload.entry_point = 0x80000000
self._connect_things()
super()._pre_instantiate(full_system=full_system)
@overrides(KernelDiskWorkload)
def _add_disk_to_board(self, disk_image: AbstractResource):

View File

@@ -42,7 +42,10 @@ from abc import (
)
from typing import Callable
from m5.objects import SubSystem
from m5.objects import (
Root,
SubSystem,
)
from m5.util.fdthelper import *
from ..boards.abstract_board import AbstractBoard
@@ -139,6 +142,18 @@ class AbstractCacheHierarchy(SubSystem):
"""
raise NotImplementedError
def _pre_instantiate(self, root: Root) -> None:
"""Called in the `AbstractBoard`'s `_pre_instantiate` method. This is
called after `connect_things`, after the creation of the root object
(which is passed in as an argument), but before `m5.instantiate`).
Subclasses should override this method to set up any connections.
At present there is no general task that must be specified here and is
default or applicable to all cache hierarchies.
"""
pass
def _post_instantiate(self):
"""Called to set up anything needed after ``m5.instantiate``."""
pass

View File

@@ -38,6 +38,7 @@ from m5.objects import (
AddrRange,
MemCtrl,
Port,
Root,
SubSystem,
)
@@ -50,6 +51,18 @@ class AbstractMemorySystem(SubSystem):
def __init__(self) -> None:
super().__init__()
def _pre_instantiate(self, root: Root) -> None:
"""Called in the `AbstractBoard`'s `_pre_instantiate` method. This is
called after `connect_things`, after the creation of the root object
(which is passed in as an argument), but before `m5.instantiate`).
Subclasses should override this method to set up any connections.
At present there is no general task that must be specified here and is
default or applicable to all memory systems.
"""
pass
@abstractmethod
def incorporate_memory(self, board: AbstractBoard) -> None:
"""This function completes all of the necessary steps to add this

View File

@@ -33,7 +33,10 @@ from typing import (
Optional,
)
from m5.objects import SubSystem
from m5.objects import (
Root,
SubSystem,
)
from ...isas import ISA
from ...utils.requires import requires
@@ -83,3 +86,12 @@ class AbstractProcessor(SubSystem):
def _post_instantiate(self) -> None:
"""Called to set up anything needed after ``m5.instantiate``."""
pass
def _pre_instantiate(self, root: Root) -> None:
"""Called in the `AbstractBoard`'s `_pre_instantiate` method. This is
called after `connect_things`, after the creation of the root object
(which is passed in as an argument), but before `m5.instantiate`).
Subclasses should override this method to set up any connections.
"""
pass

View File

@@ -27,12 +27,14 @@
from typing import List
import m5
from m5.objects import (
BaseAtomicSimpleCPU,
BaseMinorCPU,
BaseNonCachingSimpleCPU,
BaseO3CPU,
BaseTimingSimpleCPU,
Root,
)
from m5.util import warn
@@ -99,3 +101,9 @@ class BaseCPUProcessor(AbstractProcessor):
board.set_mem_mode(MemMode.ATOMIC)
else:
raise NotImplementedError
def _pre_instantiate(self, root: Root) -> None:
super()._pre_instantiate(root)
if any(core.is_kvm_core() for core in self.get_cores()):
m5.ticks.fixGlobalFrequency()
root.sim_quantum = m5.ticks.fromSeconds(0.001)

View File

@@ -31,6 +31,7 @@ from typing import (
)
import m5
from m5.objects import Root
from ...utils.override import *
from ..boards.abstract_board import AbstractBoard
@@ -155,3 +156,24 @@ class SwitchableProcessor(AbstractProcessor):
# Ensure the current processor is updated.
self._current_cores = to_switch
def _pre_instantiate(self, root: Root) -> None:
super()._pre_instantiate(root)
# The following is a bit of a hack. If a simulation is to use a KVM
# core then the `sim_quantum` value must be set. However, in the
# case of using a SwitchableProcessor the KVM cores may be
# switched out and therefore not accessible via `get_cores()`.
# This is the reason for the `isinstance` check.
#
# We cannot set the `sim_quantum` value in every simulation as
# setting it causes the scheduling of exits to be off by the
# `sim_quantum` value (something necessary if we are using KVM
# cores). Ergo we only set the value of KVM cores are present.
#
# There is still a bug here in that if the user is switching to and
# from KVM and non-KVM cores via the SwitchableProcessor then the
# scheduling of exits for the non-KVM cores will be incorrect. This
# will be fixed at a later date.
if self._prepare_kvm:
m5.ticks.fixGlobalFrequency()
root.sim_quantum = m5.ticks.fromSeconds(0.001)

View File

@@ -313,7 +313,7 @@ class RISCVMatchedBoard(
memory.set_memory_range(self.mem_ranges)
@overrides(AbstractSystemBoard)
def _pre_instantiate(self):
def _pre_instantiate(self, full_system: Optional[bool] = None) -> None:
if self._fs:
if len(self._bootloader) > 0:
self.workload.bootloader_addr = 0x0
@@ -326,7 +326,7 @@ class RISCVMatchedBoard(
self.workload.kernel_addr = 0x0
self.workload.entry_point = 0x80000000
self._connect_things()
super()._pre_instantiate(full_system=full_system)
def generate_device_tree(self, outdir: str) -> None:
"""Creates the ``dtb`` and ``dts`` files.

View File

@@ -117,6 +117,10 @@ class Simulator:
behavior. If not set, whether or not to run in FS
mode will be determined via the board's
``is_fullsystem()`` function.
**Warning: This parameter is deprecated. The board
determines if the simulation is full system or not.
This parameter will be removed in a future gem5
release.**
:param on_exit_event: An optional map to specify what to execute on
each exit event. There are three possibilities here:
a generator, a list of functions, or a single function.
@@ -291,6 +295,15 @@ class Simulator:
"""
if full_system is not None:
warn(
"Setting the full_system parameter via the Simulator "
"constructor is deprecated and will be removed in future "
"releases of gem5. "
"The board determines if the simulation is full system or not "
"via it's `is_fullsystem` method."
)
self.set_max_ticks(max_ticks)
if id:
@@ -651,45 +664,12 @@ class Simulator:
if not self._instantiated:
# Before anything else we run the AbstractBoard's
# `_pre_instantiate` function.
self._board._pre_instantiate()
root = Root(
full_system=(
self._full_system
if self._full_system is not None
else self._board.is_fullsystem()
),
board=self._board,
# `_pre_instantiate` function. This returns the root object which
# is required for instantiation.
self._root = self._board._pre_instantiate(
full_system=self._full_system
)
# We take a copy of the Root in case it's required elsewhere
# (for example, in `get_stats()`).
self._root = root
# The following is a bit of a hack. If a simulation is to use a KVM
# core then the `sim_quantum` value must be set. However, in the
# case of using a SwitchableProcessor the KVM cores may be
# switched out and therefore not accessible via `get_cores()`.
# This is the reason for the `isinstance` check.
#
# We cannot set the `sim_quantum` value in every simulation as
# setting it causes the scheduling of exits to be off by the
# `sim_quantum` value (something necessary if we are using KVM
# cores). Ergo we only set the value of KVM cores are present.
#
# There is still a bug here in that if the user is switching to and
# from KVM and non-KVM cores via the SwitchableProcessor then the
# scheduling of exits for the non-KVM cores will be incorrect. This
# will be fixed at a later date.
processor = self._board.processor
if any(core.is_kvm_core() for core in processor.get_cores()) or (
isinstance(processor, SwitchableProcessor)
and any(core.is_kvm_core() for core in processor._all_cores())
):
m5.ticks.fixGlobalFrequency()
root.sim_quantum = m5.ticks.fromSeconds(0.001)
# m5.instantiate() takes a parameter specifying the path to the
# checkpoint directory. If the parameter is None, no checkpoint
# will be restored.

View File

@@ -207,15 +207,15 @@ print("Running with ISA: " + processor.get_isa().name)
print("Running with protocol: " + get_runtime_coherence_protocol().name)
print()
root = Root(full_system=True, system=motherboard)
# Disable the gdb ports. Required for forking.
m5.disableAllListeners()
root = motherboard._pre_instantiate()
# TODO: This of annoying. Is there a way to fix this to happen
# automatically when running KVM?
root.sim_quantum = int(1e9)
# Disable the gdb ports. Required for forking.
m5.disableAllListeners()
motherboard._pre_instantiate()
m5.instantiate()
# Simulate the inital boot with the starting KVM cpu

View File

@@ -83,9 +83,8 @@ motherboard = TestBoard(
memory=memory,
cache_hierarchy=cache_hierarchy,
)
root = Root(full_system=False, system=motherboard)
motherboard._pre_instantiate()
root = motherboard._pre_instantiate()
m5.instantiate()
generator.start_traffic()

View File

@@ -83,9 +83,8 @@ motherboard = TestBoard(
memory=memory,
cache_hierarchy=cache_hierarchy,
)
root = Root(full_system=False, system=motherboard)
motherboard._pre_instantiate()
root = motherboard._pre_instantiate()
m5.instantiate()
generator.start_traffic()

View File

@@ -202,9 +202,7 @@ motherboard = TestBoard(
cache_hierarchy=cache_hierarchy,
)
root = Root(full_system=False, system=motherboard)
motherboard._pre_instantiate()
root = motherboard._pre_instantiate()
m5.instantiate()
generator.start_traffic()