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 <giacomo.travaglini@arm.com>
This commit is contained in:
Giacomo Travaglini
2024-03-19 10:34:10 +00:00
committed by Pranith Kumar
parent 3af15a535e
commit d67672facc

View File

@@ -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