| # Copyright (c) 2021 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 abc import ABCMeta, abstractmethod |
| |
| from .mem_mode import MemMode, mem_mode_to_string |
| |
| from m5.objects import ( |
| System, |
| Port, |
| IOXBar, |
| ClockDomain, |
| SrcClockDomain, |
| VoltageDomain, |
| ) |
| |
| from typing import List, final |
| |
| |
| class AbstractBoard(System): |
| """The abstract board interface. |
| |
| Boards are used as the object which can connect together all other |
| components. This abstract class defines the external interface that other |
| boards must provide. Boards can be specialized for different ISAs or system |
| designs (e.g., core counts, cache types, memory channels, I/O devices, etc) |
| |
| In addition to providing the place that system components are connected, |
| the board also exposes an interface for the caches, processor, and memory |
| to interact. |
| |
| The board also exposes an interface to set up I/O devices which needs to be |
| specialized for each ISA and/or platform. |
| |
| Board inherits from System and can therefore be used as a System simobject |
| when required. |
| """ |
| |
| __metaclass__ = ABCMeta |
| |
| def __init__( |
| self, |
| clk_freq: str, |
| processor: "AbstractProcessor", |
| memory: "AbstractMemory", |
| cache_hierarchy: "AbstractCacheHierarchy", |
| ) -> None: |
| super().__init__() |
| """ |
| :param clk_freq: The clock frequency for this board. |
| :param processor: The processor for this board. |
| :param memory: The memory for this board. |
| :param cache_hierarchy: The Cachie Hierarchy for this board. |
| """ |
| |
| # Set up the clock domain and the voltage domain. |
| self.clk_domain = SrcClockDomain() |
| self.clk_domain.clock = clk_freq |
| self.clk_domain.voltage_domain = VoltageDomain() |
| |
| # Set the processor, memory, and cache hierarchy. |
| self.processor = processor |
| self.memory = memory |
| self.cache_hierarchy = cache_hierarchy |
| |
| # 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() |
| |
| # Connect the memory, processor, and cache hierarchy. |
| self._connect_things() |
| |
| def get_processor(self) -> "AbstractProcessor": |
| """Get the processor connected to the board. |
| |
| :returns: The processor. |
| """ |
| return self.processor |
| |
| def get_memory(self) -> "AbstractMemory": |
| """Get the memory (RAM) connected to the board. |
| |
| :returns: The memory system. |
| """ |
| return self.memory |
| |
| def get_cache_hierarchy(self) -> "AbstractCacheHierarchy": |
| """Get the cache hierarchy connected to the board. |
| |
| :returns: The cache hierarchy. |
| """ |
| return self.cache_hierarchy |
| |
| def get_cache_line_size(self) -> int: |
| """Get the size of the cache line. |
| |
| :returns: The size of the cache line size. |
| """ |
| return self.cache_line_size |
| |
| def connect_system_port(self, port: Port) -> None: |
| self.system_port = port |
| |
| def set_mem_mode(self, mem_mode: MemMode) -> None: |
| """ |
| Set the memory mode of the board. |
| |
| :param mem_mode: The memory mode the board is to be set to. |
| """ |
| self.mem_mode = mem_mode_to_string(mem_mode=mem_mode) |
| |
| def get_clock_domain(self) -> ClockDomain: |
| """Get the clock domain. |
| :returns: The clock domain. |
| """ |
| return self.clk_domain |
| |
| @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. |
| """ |
| raise NotImplementedError |
| |
| # Technically `get_dma_ports` returns a list. This list could be empty to |
| # indicate the presense of dma ports. Though I quite like having this |
| # boolean to quickly check a board. |
| @abstractmethod |
| def has_dma_ports(self) -> bool: |
| """Determine whether the board has DMA ports or not. |
| |
| :returns: True if the board has DMA ports, otherwise False. |
| """ |
| raise NotImplementedError |
| |
| @abstractmethod |
| def get_dma_ports(self) -> List[Port]: |
| """Get the board's Direct Memory Access ports. |
| This abstract method must be implemented within the subclasses if they |
| support DMA and/or full system simulation. |
| |
| :returns: A List of the Direct Memory Access ports. |
| |
| """ |
| raise NotImplementedError |
| |
| @abstractmethod |
| def has_io_bus(self) -> bool: |
| """Determine whether the board has an IO bus or not. |
| |
| :returns: True if the board has an IO bus, otherwise False. |
| """ |
| raise NotImplementedError |
| |
| @abstractmethod |
| def get_io_bus(self) -> IOXBar: |
| """Get the board's IO Bus. |
| This abstract method must be implemented within the subclasses if they |
| support DMA and/or full system simulation. |
| |
| The I/O bus is a non-coherent bus (in the classic caches). On the CPU |
| side, it accepts requests meant for I/O devices. On the memory side, it |
| forwards these requests to the devices (e.g., the interrupt |
| controllers on each core). |
| |
| :returns: The I/O Bus. |
| """ |
| raise NotImplementedError |
| |
| @abstractmethod |
| def has_coherent_io(self) -> bool: |
| """Determine whether the board needs coherent I/O |
| |
| :returns: True if the board needs coherent I/O, false otherwise |
| """ |
| raise NotImplementedError |
| |
| @abstractmethod |
| def get_mem_side_coherent_io_port(self): |
| """Get the memory-side coherent I/O port. |
| This abstract method must be implemented if has_coherent_io is true. |
| |
| This returns a *port* (not a bus) that should be connected to a |
| CPU-side port for which coherent I/O (DMA) is issued. |
| """ |
| raise NotImplementedError |
| |
| @abstractmethod |
| def _setup_memory_ranges(self) -> None: |
| """ |
| 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. |
| |
| 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 |
| system implementations will likely need something more complicated. |
| |
| Notes |
| ----- |
| * This *must* be called prior to the incorporation of the cache |
| hierarchy (via `_connect_things`) as cache hierarchies depend upon |
| knowing the memory system's ranges. |
| """ |
| raise NotImplementedError |
| |
| @final |
| def _connect_things(self) -> None: |
| """Connects all the components to the board. |
| |
| The order of this board is always: |
| |
| 1. Connect the memory. |
| 2. Connect the cache hierarchy. |
| 3. Connect the processor. |
| |
| Developers may build upon this assumption when creating components. |
| |
| Notes |
| ----- |
| |
| * The processor is incorporated after the cache hierarchy due to a bug |
| noted here: https://gem5.atlassian.net/browse/GEM5-1113. Until this |
| bug is fixed, this ordering must be maintained. |
| """ |
| |
| # Incorporate the memory into the motherboard. |
| self.get_memory().incorporate_memory(self) |
| |
| # Incorporate the cache hierarchy for the motherboard. |
| self.get_cache_hierarchy().incorporate_cache(self) |
| |
| # Incorporate the processor into the motherboard. |
| self.get_processor().incorporate_processor(self) |