diff --git a/src/python/gem5/components/cachehierarchies/abstract_cache_hierarchy.py b/src/python/gem5/components/cachehierarchies/abstract_cache_hierarchy.py index 930c6e7cff..b0435543af 100644 --- a/src/python/gem5/components/cachehierarchies/abstract_cache_hierarchy.py +++ b/src/python/gem5/components/cachehierarchies/abstract_cache_hierarchy.py @@ -1,3 +1,15 @@ +# Copyright (c) 2024 Arm Limited +# All rights reserved. +# +# The license below extends only to copyright in the software and shall +# not be construed as granting a license to any other intellectual +# property including but not limited to intellectual property relating +# to a hardware implementation of the functionality of the software +# licensed hereunder. You may use the software subject to the license +# terms below provided that you ensure that this notice is replicated +# unmodified and in its entirety in all distributions of the software, +# modified or unmodified, in source code or in binary form. +# # Copyright (c) 2021 The Regents of the University of California # All rights reserved. # @@ -28,17 +40,72 @@ from abc import ( ABCMeta, abstractmethod, ) +from typing import Callable from m5.objects import SubSystem +from m5.util.fdthelper import * from ..boards.abstract_board import AbstractBoard +class CacheNode: + def __init__( + self, + name: str, + cache: SimObject, + next_level: "CacheNode", + hierarchy: "AbstractCacheHierarchy", + ): + self.name = name + self.cache = cache + self.next_level = next_level + self.hierarchy = hierarchy + + self.prev_levels = [] + + # Need to assign this to a SimObject + if cache is not None: + setattr(hierarchy, self.name, cache) + + def add_child(self, name: str, cache: SimObject) -> "CacheNode": + """ + Add a child node to the current node provided a cache object and + its name. Because of the intrinsic topology of caches, children will be + one level higher than their parent in the hierarchy. + This means the chain of insertions to the tree will be something + like: + l3.add_child("l2", l2).add_child("l1", l1) + + :param name: The name of the cache + :param cache: The cache SimObject + :returns: The new child node being generated + """ + new_node = CacheNode(name, cache, self, self.hierarchy) + self.prev_levels.append(new_node) + return new_node + + def generate_dtb_entry(self, state, level): + node = FdtNode(f"{self.name}") + node.append(FdtPropertyStrings("compatible", ["cache"])) + node.append(FdtPropertyWords("cache-level", int(level))) + node.append(FdtPropertyWords("cache-size", int(self.cache.size))) + if self.next_level: + node.append( + FdtPropertyWords( + "next-level-cache", state.phandle(self.next_level.cache) + ) + ) + + node.appendPhandle(self.cache) + return node + + class AbstractCacheHierarchy(SubSystem): __metaclass__ = ABCMeta def __init__(self): super().__init__() + self._root = CacheNode("root", None, None, self) """ A Cache Hierarchy incorporates any system components which manages @@ -75,3 +142,46 @@ class AbstractCacheHierarchy(SubSystem): def _post_instantiate(self): """Called to set up anything needed after ``m5.instantiate``.""" pass + + def add_root_child(self, *args, **kwargs): + """This adds the LLC to the root node""" + return self._root.add_child(*args, **kwargs) + + def traverse( + self, node: CacheNode, visit: Callable[[CacheNode, int], None] + ) -> int: + """ + Traverse the tree in post-order. Return the level of the + current node passed as an argument. The method accepts + a visit function to be called at each node + + :param node: starting node for traversal + :param visit: visiting function to be called at each node + + :returns: level of the node passed as an argument + """ + if not node.prev_levels: + level = 1 + else: + for prev in node.prev_levels: + level = self.traverse(prev, visit) + + visit(node, level) + + return level + 1 + + def generateDeviceTree(self, state): + dt_entries = [] + + def add_dt_entry(node, level): + # Do not generate a DTB entry for the root node + # as it does not point to a real cache (node.cache = None) + # and for the L1I and L1D caches as their data should be + # part of the CPU node as described by: + # https://devicetree-specification.readthedocs.io/en/stable/devicenodes.html + if node.cache is not None and level != 1: + dt_entries.append(node.generate_dtb_entry(state, level)) + + self.traverse(self._root, add_dt_entry) + + yield from dt_entries diff --git a/src/python/gem5/components/cachehierarchies/classic/private_l1_private_l2_cache_hierarchy.py b/src/python/gem5/components/cachehierarchies/classic/private_l1_private_l2_cache_hierarchy.py index e0f918a7d5..78981a6f2b 100644 --- a/src/python/gem5/components/cachehierarchies/classic/private_l1_private_l2_cache_hierarchy.py +++ b/src/python/gem5/components/cachehierarchies/classic/private_l1_private_l2_cache_hierarchy.py @@ -127,35 +127,29 @@ class PrivateL1PrivateL2CacheHierarchy( for _, port in board.get_memory().get_mem_ports(): self.membus.mem_side_ports = port - self.l1icaches = [ - L1ICache(size=self._l1i_size) - for i in range(board.get_processor().get_num_cores()) - ] - self.l1dcaches = [ - L1DCache(size=self._l1d_size) - for i in range(board.get_processor().get_num_cores()) - ] self.l2buses = [ L2XBar() for i in range(board.get_processor().get_num_cores()) ] - self.l2caches = [ - L2Cache(size=self._l2_size) - for i in range(board.get_processor().get_num_cores()) - ] - - if board.has_coherent_io(): - self._setup_io_cache(board) for i, cpu in enumerate(board.get_processor().get_cores()): - cpu.connect_icache(self.l1icaches[i].cpu_side) - cpu.connect_dcache(self.l1dcaches[i].cpu_side) + l2_node = self.add_root_child( + f"l2-cache-{i}", L2Cache(size=self._l2_size) + ) + l1i_node = l2_node.add_child( + f"l1i-cache-{i}", L1ICache(size=self._l1i_size) + ) + l1d_node = l2_node.add_child( + f"l1d-cache-{i}", L1DCache(size=self._l1d_size) + ) - self.l1icaches[i].mem_side = self.l2buses[i].cpu_side_ports - self.l1dcaches[i].mem_side = self.l2buses[i].cpu_side_ports + self.l2buses[i].mem_side_ports = l2_node.cache.cpu_side + self.membus.cpu_side_ports = l2_node.cache.mem_side - self.l2buses[i].mem_side_ports = self.l2caches[i].cpu_side + l1i_node.cache.mem_side = self.l2buses[i].cpu_side_ports + l1d_node.cache.mem_side = self.l2buses[i].cpu_side_ports - self.membus.cpu_side_ports = self.l2caches[i].mem_side + cpu.connect_icache(l1i_node.cache.cpu_side) + cpu.connect_dcache(l1d_node.cache.cpu_side) self._connect_table_walker(i, cpu) @@ -166,6 +160,9 @@ class PrivateL1PrivateL2CacheHierarchy( else: cpu.connect_interrupt() + if board.has_coherent_io(): + self._setup_io_cache(board) + def _connect_table_walker(self, cpu_id: int, cpu: BaseCPU) -> None: cpu.connect_walker_ports( self.membus.cpu_side_ports, self.membus.cpu_side_ports