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:
@@ -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!")
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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):
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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()
|
||||
|
||||
Reference in New Issue
Block a user