misc: Adding multi-channel memory to components library
This change adds source code for multi-channel memory in the components library. Change-Id: I52b5462939d4d2d1657c85394bd83afdb509a0b0 Reviewed-on: https://gem5-review.googlesource.com/c/public/gem5/+/51287 Reviewed-by: Bobby R. Bruce <bbruce@ucdavis.edu> Maintainer: Bobby R. Bruce <bbruce@ucdavis.edu> Tested-by: kokoro <noreply+kokoro@google.com>
This commit is contained in:
@@ -115,6 +115,7 @@ PySource('gem5.components.memory', 'gem5/components/memory/abstract_memory_syste
|
||||
PySource('gem5.components.memory', 'gem5/components/memory/dramsim_3.py')
|
||||
PySource('gem5.components.memory', 'gem5/components/memory/single_channel.py')
|
||||
PySource('gem5.components.memory', 'gem5/components/memory/simple.py')
|
||||
PySource('gem5.components.memory', 'gem5/components/memory/multi_channel.py')
|
||||
PySource('gem5.components.memory.dram_interfaces',
|
||||
'gem5/components/memory/dram_interfaces/__init__.py')
|
||||
PySource('gem5.components.memory.dram_interfaces',
|
||||
|
||||
@@ -71,6 +71,8 @@ class AbstractBoard(System):
|
||||
self.memory = memory
|
||||
self.cache_hierarchy = cache_hierarchy
|
||||
|
||||
self.setup_memory_ranges()
|
||||
|
||||
def get_processor(self) -> "AbstractProcessor":
|
||||
"""Get the processor connected to the board.
|
||||
|
||||
@@ -188,9 +190,9 @@ class AbstractBoard(System):
|
||||
"""
|
||||
Set the memory ranges for this board.
|
||||
|
||||
This is called by `connect_things`. It can query the board's memory
|
||||
to determine the size and the set the memory ranges on the memory if
|
||||
it needs to move the memory devices.
|
||||
This is called by at the end of the constructor. It can query the
|
||||
board's memory to determine the size and the set the memory ranges on
|
||||
the memory if it needs to move the memory devices.
|
||||
|
||||
The simplest implementation just sets the board's memory range to be
|
||||
the size of memory and memory's memory range to be the same as the
|
||||
|
||||
@@ -92,9 +92,6 @@ class SimpleBoard(AbstractBoard):
|
||||
|
||||
@overrides(AbstractBoard)
|
||||
def connect_things(self) -> None:
|
||||
# Before incorporating the memory, set up the memory ranges
|
||||
self.setup_memory_ranges()
|
||||
|
||||
# Incorporate the cache hierarchy for the motherboard.
|
||||
self.get_cache_hierarchy().incorporate_cache(self)
|
||||
|
||||
|
||||
@@ -72,14 +72,12 @@ class TestBoard(AbstractBoard):
|
||||
self.system_port = port
|
||||
|
||||
def connect_things(self) -> None:
|
||||
self.setup_memory_ranges()
|
||||
|
||||
self.get_cache_hierarchy().incorporate_cache(self)
|
||||
|
||||
self.get_processor().incorporate_processor(self)
|
||||
|
||||
self.get_memory().incorporate_memory(self)
|
||||
|
||||
self.get_cache_hierarchy().incorporate_cache(self)
|
||||
|
||||
def get_clock_domain(self) -> ClockDomain:
|
||||
return self.clk_domain
|
||||
|
||||
|
||||
@@ -253,10 +253,6 @@ class X86Board(SimpleBoard):
|
||||
# This board is a bit particular about the order that things are
|
||||
# connected together.
|
||||
|
||||
# Before incorporating the memory or creating the I/O devices figure
|
||||
# out the memory ranges.
|
||||
self.setup_memory_ranges()
|
||||
|
||||
# Set up all of the I/O before we incorporate anything else.
|
||||
self._setup_io_devices()
|
||||
|
||||
|
||||
170
src/python/gem5/components/memory/multi_channel.py
Normal file
170
src/python/gem5/components/memory/multi_channel.py
Normal file
@@ -0,0 +1,170 @@
|
||||
# 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.
|
||||
|
||||
"""Multi channel "generic" DDR memory controllers
|
||||
"""
|
||||
|
||||
import enum
|
||||
from math import log
|
||||
from ...utils.override import overrides
|
||||
from m5.util.convert import toMemorySize
|
||||
from ..boards.abstract_board import AbstractBoard
|
||||
from .abstract_memory_system import AbstractMemorySystem
|
||||
from typing import Type, Sequence, Tuple, List, Optional
|
||||
from m5.objects import AddrRange, DRAMInterface, MemCtrl, Port
|
||||
|
||||
|
||||
def _isPow2(num):
|
||||
log_num = int(log(num, 2))
|
||||
if 2 ** log_num != num:
|
||||
return False
|
||||
else:
|
||||
return True
|
||||
|
||||
class MultiChannelMemory(AbstractMemorySystem):
|
||||
"""A class to implement multi-channel memory system
|
||||
|
||||
This class can take a DRAM Interface as a parameter to model a multi
|
||||
channel DDR DRAM memory system.
|
||||
"""
|
||||
def __init__(
|
||||
self,
|
||||
dram_interface_class: Type[DRAMInterface],
|
||||
num_channels: int,
|
||||
interleaving_size: int,
|
||||
size: Optional[str] = None,
|
||||
addr_mapping: Optional[str] = None,
|
||||
) -> None:
|
||||
"""
|
||||
:param dram_interface_class: The DRAM interface type to create with
|
||||
this memory controller
|
||||
:param num_channels: The number of channels that needs to be
|
||||
simulated
|
||||
:param size: Optionally specify the size of the DRAM controller's
|
||||
address space. By default, it starts at 0 and ends at the size of
|
||||
the DRAM device specified
|
||||
:param add_mapping: Defines the address mapping scheme to be used.
|
||||
By default, it is RoRaBaChCo
|
||||
:param interleaving_size: Defines the interleaving size of the multi-
|
||||
channel memory system. By default, it is equivalent to the atom
|
||||
size, i.e., 64.
|
||||
"""
|
||||
super().__init__()
|
||||
self._dram_class = dram_interface_class
|
||||
self._num_channels = num_channels
|
||||
|
||||
if not _isPow2(interleaving_size):
|
||||
raise ValueError("Memory interleaving size should be a power of 2")
|
||||
self._intlv_size = interleaving_size
|
||||
|
||||
if addr_mapping:
|
||||
self._addr_mapping = addr_mapping
|
||||
else:
|
||||
self._addr_mapping = self._dram_class.addr_mapping.value
|
||||
|
||||
if size:
|
||||
self._size = toMemorySize(size)
|
||||
else:
|
||||
self._size = self._get_dram_size(num_channels, self._dram_class)
|
||||
|
||||
self._dram = [
|
||||
self._dram_class(addr_mapping=self._addr_mapping)
|
||||
for _ in range(num_channels)
|
||||
]
|
||||
self.mem_ctrl = [
|
||||
MemCtrl(dram=self._dram[i]) for i in range(num_channels)
|
||||
]
|
||||
|
||||
|
||||
def _get_dram_size(self, num_channels: int, dram: DRAMInterface) -> int:
|
||||
return num_channels * (
|
||||
dram.device_size.value
|
||||
* dram.devices_per_rank.value
|
||||
* dram.ranks_per_channel.value
|
||||
)
|
||||
|
||||
def _interleave_addresses(self):
|
||||
print(f"Memory is interleaving the address range {self._mem_range}"
|
||||
f" using {self._intlv_size} as interleaving size.")
|
||||
if self._addr_mapping == "RoRaBaChCo":
|
||||
rowbuffer_size = (
|
||||
self._dram_class.device_rowbuffer_size.value
|
||||
* self._dram_class.devices_per_rank.value
|
||||
)
|
||||
intlv_low_bit = log(rowbuffer_size, 2)
|
||||
elif self._addr_mapping in ["RoRaBaCoCh", "RoCoRaBaCh"]:
|
||||
intlv_low_bit = log(self._intlv_size, 2)
|
||||
else:
|
||||
raise ValueError(
|
||||
"Only these address mappings are supported: "
|
||||
"RoRaBaChCo, RoRaBaCoCh, RoCoRaBaCh"
|
||||
)
|
||||
|
||||
intlv_bits = log(self._num_channels, 2)
|
||||
for i, ctrl in enumerate(self.mem_ctrl):
|
||||
ctrl.dram.range = AddrRange(
|
||||
start=self._mem_range.start,
|
||||
end=self._mem_range.size(),
|
||||
intlvHighBit = intlv_low_bit + intlv_bits - 1,
|
||||
xorHighBit=0,
|
||||
intlvBits=intlv_bits,
|
||||
intlvMatch=i,
|
||||
)
|
||||
|
||||
@overrides(AbstractMemorySystem)
|
||||
def incorporate_memory(self, board: AbstractBoard) -> None:
|
||||
if self._intlv_size < int(board.get_cache_line_size()):
|
||||
raise ValueError("Memory interleaving size can not be smaller than"
|
||||
" board's cache line size.\nBoard's cache line size: "
|
||||
f"{board.get_cache_line_size()}\n, This memory's interleaving "
|
||||
f"size: {self._intlv_size}")
|
||||
|
||||
@overrides(AbstractMemorySystem)
|
||||
def get_mem_ports(self) -> Sequence[Tuple[AddrRange, Port]]:
|
||||
return [(ctrl.dram.range, ctrl.port) for ctrl in self.mem_ctrl]
|
||||
|
||||
@overrides(AbstractMemorySystem)
|
||||
def get_memory_controllers(self) -> List[MemCtrl]:
|
||||
return [ctrl for ctrl in self.mem_ctrl]
|
||||
|
||||
@overrides(AbstractMemorySystem)
|
||||
def get_size(self) -> int:
|
||||
return self._size
|
||||
|
||||
@overrides(AbstractMemorySystem)
|
||||
def set_memory_range(self, ranges: List[AddrRange]) -> None:
|
||||
"""Need to add support for non-contiguous non overlapping ranges in
|
||||
the future.
|
||||
"""
|
||||
if len(ranges) != 1 or ranges[0].size() != self._size:
|
||||
raise Exception(
|
||||
"Multi channel memory controller requires a single range "
|
||||
"which matches the memory's size.\n"
|
||||
f"The range size: {range[0].size()}\n"
|
||||
f"This memory's size: {self._size}"
|
||||
)
|
||||
self._mem_range = ranges[0]
|
||||
self._interleave_addresses()
|
||||
Reference in New Issue
Block a user