From 1664625c915ffb135dea21ebf690297e57c970d2 Mon Sep 17 00:00:00 2001 From: Giacomo Travaglini Date: Tue, 19 Mar 2024 10:34:10 +0000 Subject: [PATCH 1/4] stdlib: Add tree structure to the AbstractCacheHierarchy One of things we miss in gem5 is the capability to neatly compose the cache hierarchy of CPUs and clusters of CPUs. The BaseCPU addPrivateSplitL1Caches and addTwoLevelCacheHierarchy APIs have historically been used to bind cache levels together. These APIs have been superseeded by the introduction of the Cache hierarchy abstraction in the standard library. The standard library makes it cleaner for a user to quickly instantiate a hierarchy of caches with few lines of code. While this removes a lot of complexity for a user, the Hierarchy objects still have little information about their internal topology. To address this problem, this patch adds a tree data structure to the AbstractCacheHierarchy class, where every node of the tree represent a cache in the hierarchy. In this way we will expose APIs for traversing and querying the tree. For example a 2 CPUs system with private L1, private L2 and shared L3 will contain the following tree: [root] | [L3] /\ / \ [L2] [L2] | | [L1] [L1] Change-Id: I78fe6ad094f0938ff9bed191fb10b9e841418692 Signed-off-by: Giacomo Travaglini --- .../abstract_cache_hierarchy.py | 78 +++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/python/gem5/components/cachehierarchies/abstract_cache_hierarchy.py b/src/python/gem5/components/cachehierarchies/abstract_cache_hierarchy.py index 930c6e7cff..a03f0e0c9a 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,56 @@ from abc import ( ABCMeta, abstractmethod, ) +from typing import Callable from m5.objects import SubSystem from ..boards.abstract_board import AbstractBoard +class CacheNode: + def __init__( + self, + name: str, + cache: SimObject, + next_level: int, + 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 + + 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 +126,30 @@ 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 From be1cac6c211b06955a32d1431db8064ddaf17957 Mon Sep 17 00:00:00 2001 From: Giacomo Travaglini Date: Mon, 18 Mar 2024 09:33:42 +0000 Subject: [PATCH 2/4] stdlib: Use newly defined tree for PrivateL1PrivateL2 hierarchy Change-Id: I803c6118c4df62484018f9e4d995026adb1bbc2c Signed-off-by: Giacomo Travaglini --- .../private_l1_private_l2_cache_hierarchy.py | 39 +++++++++---------- 1 file changed, 18 insertions(+), 21 deletions(-) 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 From 42fb1d657c0ca20e110d0b1f19b5f527304c643d Mon Sep 17 00:00:00 2001 From: Giacomo Travaglini Date: Mon, 25 Mar 2024 16:32:31 +0000 Subject: [PATCH 3/4] stdlib: Add DTB generation capabilites to AbstractCacheHierarchy Now that we are able to provide a view of the cache hierarchy from the python world, we can start generating DTB entries for caches and more specifically to properly fill the next-level-cache and cache-level properties Change-Id: Iba9ea08fe605f77a353c9e64d62b04b80478b4e2 Signed-off-by: Giacomo Travaglini --- .../abstract_cache_hierarchy.py | 32 +++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/src/python/gem5/components/cachehierarchies/abstract_cache_hierarchy.py b/src/python/gem5/components/cachehierarchies/abstract_cache_hierarchy.py index a03f0e0c9a..13b4d4f2a2 100644 --- a/src/python/gem5/components/cachehierarchies/abstract_cache_hierarchy.py +++ b/src/python/gem5/components/cachehierarchies/abstract_cache_hierarchy.py @@ -43,6 +43,7 @@ from abc import ( from typing import Callable from m5.objects import SubSystem +from m5.util.fdthelper import * from ..boards.abstract_board import AbstractBoard @@ -83,6 +84,21 @@ class CacheNode: 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 @@ -153,3 +169,19 @@ class AbstractCacheHierarchy(SubSystem): 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 From 6c2ac8e641116c15333b9ebad4a60ce65db8b955 Mon Sep 17 00:00:00 2001 From: Giacomo Travaglini Date: Fri, 5 Apr 2024 08:53:07 +0100 Subject: [PATCH 4/4] Update src/python/gem5/components/cachehierarchies/abstract_cache_hierarchy.py Co-authored-by: Bobby R. Bruce --- .../components/cachehierarchies/abstract_cache_hierarchy.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/python/gem5/components/cachehierarchies/abstract_cache_hierarchy.py b/src/python/gem5/components/cachehierarchies/abstract_cache_hierarchy.py index 13b4d4f2a2..b0435543af 100644 --- a/src/python/gem5/components/cachehierarchies/abstract_cache_hierarchy.py +++ b/src/python/gem5/components/cachehierarchies/abstract_cache_hierarchy.py @@ -53,7 +53,7 @@ class CacheNode: self, name: str, cache: SimObject, - next_level: int, + next_level: "CacheNode", hierarchy: "AbstractCacheHierarchy", ): self.name = name