From d67672facce023e4364f71b815de5a97018ac4a6 Mon Sep 17 00:00:00 2001 From: Giacomo Travaglini Date: Tue, 19 Mar 2024 10:34:10 +0000 Subject: [PATCH 1/9] 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 e6637fc852f5083c006d27534fe55bc15255fc35 Mon Sep 17 00:00:00 2001 From: Giacomo Travaglini Date: Mon, 18 Mar 2024 09:33:42 +0000 Subject: [PATCH 2/9] 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 efe397ca92f6e74c31cc161cff5581a247ec27c1 Mon Sep 17 00:00:00 2001 From: Giacomo Travaglini Date: Mon, 25 Mar 2024 16:32:31 +0000 Subject: [PATCH 3/9] 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 4b98551aafa31aff80153252fe4f9d760dee4eb0 Mon Sep 17 00:00:00 2001 From: Giacomo Travaglini Date: Fri, 5 Apr 2024 08:53:07 +0100 Subject: [PATCH 4/9] 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 From 2cc2ad509728192ccc1c8d3e1f200adb6e857dfc Mon Sep 17 00:00:00 2001 From: Pranith Kumar Date: Mon, 5 Feb 2024 22:44:19 -0500 Subject: [PATCH 5/9] misc: Add a generic cache library Add a generic cache library modeled after AssociativeSet that can be used for constructing internal caching structures. Change-Id: I1767309ed01f52672b32810636a09142ff23242f --- src/base/cache/associative_cache.hh | 325 ++++++++++++++++++++++++++++ src/base/cache/cache_entry.hh | 136 ++++++++++++ 2 files changed, 461 insertions(+) create mode 100644 src/base/cache/associative_cache.hh create mode 100644 src/base/cache/cache_entry.hh diff --git a/src/base/cache/associative_cache.hh b/src/base/cache/associative_cache.hh new file mode 100644 index 0000000000..113709d583 --- /dev/null +++ b/src/base/cache/associative_cache.hh @@ -0,0 +1,325 @@ +/* + * Copyright (c) 2024 Pranith Kumar + * Copyright (c) 2018 Metempsy Technology Consulting + * 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. + */ + +#ifndef __BASE_CACHE_ASSOCIATIVE_CACHE_HH__ +#define __BASE_CACHE_ASSOCIATIVE_CACHE_HH__ + +#include +#include + +#include "base/cache/cache_entry.hh" +#include "base/intmath.hh" +#include "base/logging.hh" +#include "base/named.hh" +#include "base/types.hh" +#include "mem/cache/replacement_policies/base.hh" +#include "mem/cache/replacement_policies/replaceable_entry.hh" +#include "mem/cache/tags/indexing_policies/base.hh" + +namespace gem5 +{ + +template +class AssociativeCache : public Named +{ + static_assert(std::is_base_of_v, + "Entry should be derived from CacheEntry"); + + typedef replacement_policy::Base BaseReplacementPolicy; + + protected: + + /** Associativity of the cache. */ + size_t associativity; + + /** The replacement policy of the cache. */ + BaseReplacementPolicy *replPolicy; + + /** Indexing policy of the cache */ + BaseIndexingPolicy *indexingPolicy; + + /** The entries */ + std::vector entries; + + private: + + void + initParams(size_t _num_entries, size_t _assoc) + { + fatal_if((_num_entries % _assoc) != 0, "The number of entries of an " + "AssociativeCache<> must be a multiple of its associativity"); + for (auto entry_idx = 0; entry_idx < _num_entries; entry_idx++) { + Entry *entry = &entries[entry_idx]; + indexingPolicy->setEntry(entry, entry_idx); + entry->replacementData = replPolicy->instantiateEntry(); + } + } + + public: + + /** + * Empty constructor - need to call init() later with all args + */ + AssociativeCache(const char *name) : Named(std::string(name)) {} + + /** + * Public constructor + * @param name Name of the cache + * @param num_entries total number of entries of the container, the number + * of sets can be calculated dividing this balue by the 'assoc' value + * @param associativity number of elements in each associative set + * @param repl_policy replacement policy + * @param indexing_policy indexing policy + */ + AssociativeCache(const char *name, const size_t num_entries, + const size_t associativity_, + BaseReplacementPolicy *repl_policy, + BaseIndexingPolicy *indexing_policy, + Entry const &init_val = Entry()) + : Named(std::string(name)), + associativity(associativity_), + replPolicy(repl_policy), + indexingPolicy(indexing_policy), + entries(num_entries, init_val) + { + initParams(num_entries, associativity); + } + + /** + * Default destructor + */ + ~AssociativeCache() = default; + + /** + * Disable copy and assignment + */ + AssociativeCache(const AssociativeCache&) = delete; + AssociativeCache& operator=(const AssociativeCache&) = delete; + + /** + * Clear the entries in the cache. + */ + void + clear() + { + for (auto &entry : entries) { + invalidate(&entry); + } + } + + void + init(const size_t num_entries, + const size_t associativity_, + BaseReplacementPolicy *_repl_policy, + BaseIndexingPolicy *_indexing_policy, + Entry const &init_val = Entry()) + { + associativity = associativity_; + replPolicy = _repl_policy; + indexingPolicy = _indexing_policy; + entries.resize(num_entries, init_val); + + initParams(num_entries, associativity); + } + + /** + * Get the tag for the addr + * @param addr Addr to get the tag for + * @return Tag for the address + */ + virtual Addr + getTag(const Addr addr) const + { + return indexingPolicy->extractTag(addr); + } + + /** + * Do an access to the entry if it exists. + * This is required to update the replacement information data. + * @param addr key to the entry + * @return The entry if it exists + */ + virtual Entry* + accessEntryByAddr(const Addr addr) + { + auto entry = findEntry(addr); + + if (entry) { + accessEntry(entry); + } + + return entry; + } + + /** + * Update the replacement information for an entry + * @param Entry to access and upate + */ + virtual void + accessEntry(Entry *entry) + { + replPolicy->touch(entry->replacementData); + } + + /** + * Find an entry within the set + * @param addr key element + * @return returns a pointer to the wanted entry or nullptr if it does not + * exist. + */ + virtual Entry* + findEntry(const Addr addr) const + { + auto tag = getTag(addr); + + auto candidates = indexingPolicy->getPossibleEntries(addr); + + for (auto candidate : candidates) { + Entry *entry = static_cast(candidate); + if (entry->matchTag(tag)) { + return entry; + } + } + + return nullptr; + } + + /** + * Find a victim to be replaced + * @param addr key to select the possible victim + * @result entry to be victimized + */ + virtual Entry* + findVictim(const Addr addr) + { + auto candidates = indexingPolicy->getPossibleEntries(addr); + + auto victim = static_cast(replPolicy->getVictim(candidates)); + + invalidate(victim); + + return victim; + } + + /** + * Invalidate an entry and its respective replacement data. + * + * @param entry Entry to be invalidated. + */ + virtual void + invalidate(Entry *entry) + { + entry->invalidate(); + replPolicy->invalidate(entry->replacementData); + } + + /** + * Indicate that an entry has just been inserted + * @param addr key of the container + * @param entry pointer to the container entry to be inserted + */ + virtual void + insertEntry(const Addr addr, Entry *entry) + { + entry->insert(indexingPolicy->extractTag(addr)); + replPolicy->reset(entry->replacementData); + } + + /** + * Find the set of entries that could be replaced given + * that we want to add a new entry with the provided key + * @param addr key to select the set of entries + * @result vector of candidates matching with the provided key + */ + std::vector + getPossibleEntries(const Addr addr) const + { + std::vector selected_entries = + indexingPolicy->getPossibleEntries(addr); + + std::vector entries; + + std::transform(selected_entries.begin(), selected_entries.end(), + std::back_inserter(entries), [](auto &entry) { + return static_cast(entry); + }); + + return entries; + } + + /** Iterator types */ + using const_iterator = typename std::vector::const_iterator; + using iterator = typename std::vector::iterator; + + /** + * Returns an iterator to the first entry of the dictionary + * @result iterator to the first element + */ + iterator + begin() + { + return entries.begin(); + } + + /** + * Returns an iterator pointing to the end of the the dictionary + * (placeholder element, should not be accessed) + * @result iterator to the end element + */ + iterator + end() + { + return entries.end(); + } + + /** + * Returns an iterator to the first entry of the dictionary + * @result iterator to the first element + */ + const_iterator + begin() const + { + return entries.begin(); + } + + /** + * Returns an iterator pointing to the end of the the dictionary + * (placeholder element, should not be accessed) + * @result iterator to the end element + */ + const_iterator + end() const + { + return entries.end(); + } +}; + +} + +#endif diff --git a/src/base/cache/cache_entry.hh b/src/base/cache/cache_entry.hh new file mode 100644 index 0000000000..9466da3fc1 --- /dev/null +++ b/src/base/cache/cache_entry.hh @@ -0,0 +1,136 @@ +/** + * Copyright (c) 2024 - Pranith Kumar + * Copyright (c) 2020 Inria + * 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. + */ + +#ifndef __BASE_CACHE_CACHE_ENTRY_HH__ +#define __BASE_CACHE_CACHE_ENTRY_HH__ + +#include + +#include "base/cprintf.hh" +#include "base/types.hh" +#include "mem/cache/replacement_policies/replaceable_entry.hh" + +namespace gem5 +{ + +/** + * A CacheEntry is an entry containing a tag. A tagged entry's contents + * are only relevant if it is marked as valid. + */ +class CacheEntry : public ReplaceableEntry +{ + public: + CacheEntry() = default; + ~CacheEntry() = default; + + /** + * Checks if the entry is valid. + * + * @return True if the entry is valid. + */ + virtual bool isValid() const { return valid; } + + /** + * Get tag associated to this block. + * + * @return The tag value. + */ + virtual Addr getTag() const { return tag; } + + /** + * Checks if the given tag information corresponds to this entry's. + * + * @param tag The tag value to compare to. + * @return True if the tag information match this entry's. + */ + virtual bool + matchTag(const Addr tag) const + { + return isValid() && (getTag() == tag); + } + + /** + * Insert the block by assigning it a tag and marking it valid. Touches + * block if it hadn't been touched previously. + * + * @param tag The tag value. + */ + virtual void + insert(const Addr tag) + { + setValid(); + setTag(tag); + } + + /** Invalidate the block. Its contents are no longer valid. */ + virtual void + invalidate() + { + valid = false; + setTag(MaxAddr); + } + + std::string + print() const override + { + return csprintf("tag: %#x valid: %d | %s", getTag(), + isValid(), ReplaceableEntry::print()); + } + + protected: + /** + * Set tag associated to this block. + * + * @param tag The tag value. + */ + virtual void setTag(Addr _tag) { tag = _tag; } + + /** Set valid bit. The block must be invalid beforehand. */ + virtual void + setValid() + { + assert(!isValid()); + valid = true; + } + + private: + /** + * Valid bit. The contents of this entry are only valid if this bit is set. + * @sa invalidate() + * @sa insert() + */ + bool valid{false}; + + /** The entry's tag. */ + Addr tag{MaxAddr}; +}; + +} // namespace gem5 + +#endif //__CACHE_ENTRY_HH__ From 2c7d4bed660edc97cd462806588058a3b16b68f7 Mon Sep 17 00:00:00 2001 From: Pranith Kumar Date: Sat, 6 Jan 2024 19:22:47 -0500 Subject: [PATCH 6/9] mem-cache: Implement VFT tables using cache library The frequency table is better built using the generic cache library instead of the AssociativeSet since the secure bit is not needed for this structure. Change-Id: Ie3b6442235daec7b350c608ad1380bed58f5ccf4 --- src/mem/cache/compressors/frequent_values.cc | 15 ++++++++------- src/mem/cache/compressors/frequent_values.hh | 10 +++++----- 2 files changed, 13 insertions(+), 12 deletions(-) diff --git a/src/mem/cache/compressors/frequent_values.cc b/src/mem/cache/compressors/frequent_values.cc index b5eca3b096..6930feedd8 100644 --- a/src/mem/cache/compressors/frequent_values.cc +++ b/src/mem/cache/compressors/frequent_values.cc @@ -51,8 +51,9 @@ FrequentValues::FrequentValues(const Params &p) codeGenerationTicks(p.code_generation_ticks), checkSaturation(p.check_saturation), numVFTEntries(p.vft_entries), numSamples(p.num_samples), takenSamples(0), phase(SAMPLING), - VFT(p.vft_assoc, p.vft_entries, p.vft_indexing_policy, - p.vft_replacement_policy, VFTEntry(counterBits)), + VFT((name() + ".VFT").c_str(), + p.vft_entries, p.vft_assoc, p.vft_replacement_policy, + p.vft_indexing_policy, VFTEntry(counterBits)), codeGenerationEvent([this]{ phase = COMPRESSING; }, name()) { fatal_if((numVFTEntries - 1) > mask(chunkSizeBits), @@ -75,7 +76,7 @@ FrequentValues::compress(const std::vector& chunks, Cycles& comp_lat, encoder::Code code; int length = 0; if (phase == COMPRESSING) { - VFTEntry* entry = VFT.findEntry(chunk, false); + VFTEntry* entry = VFT.findEntry(chunk); // Theoretically, the code would be the index of the entry; // however, there is no practical need to do so, and we simply @@ -159,7 +160,7 @@ FrequentValues::decompress(const CompressionData* comp_data, uint64_t* data) // The value at the given VFT entry must match the one stored, // if it is not the uncompressed value assert((comp_chunk.code.code == uncompressedValue) || - VFT.findEntry(comp_chunk.value, false)); + VFT.findEntry(comp_chunk.value)); } } @@ -178,7 +179,7 @@ FrequentValues::sampleValues(const std::vector &data, { const std::vector chunks = toChunks(data.data()); for (const Chunk& chunk : chunks) { - VFTEntry* entry = VFT.findEntry(chunk, false); + VFTEntry* entry = VFT.findEntry(chunk); bool saturated = false; if (!is_invalidation) { // If a VFT hit, increase new value's counter; otherwise, insert @@ -187,7 +188,7 @@ FrequentValues::sampleValues(const std::vector &data, entry = VFT.findVictim(chunk); assert(entry != nullptr); entry->value = chunk; - VFT.insertEntry(chunk, false, entry); + VFT.insertEntry(chunk, entry); } else { VFT.accessEntry(entry); } @@ -234,7 +235,7 @@ FrequentValues::generateCodes() // representing uncompressed values assert(uncompressed_values.size() >= 1); uncompressedValue = *uncompressed_values.begin(); - assert(VFT.findEntry(uncompressedValue, false) == nullptr); + assert(VFT.findEntry(uncompressedValue) == nullptr); if (useHuffmanEncoding) { // Populate the queue, adding each entry as a tree with one node. diff --git a/src/mem/cache/compressors/frequent_values.hh b/src/mem/cache/compressors/frequent_values.hh index 41103ce594..a5a53c7c8f 100644 --- a/src/mem/cache/compressors/frequent_values.hh +++ b/src/mem/cache/compressors/frequent_values.hh @@ -34,13 +34,13 @@ #include #include +#include "base/cache/associative_cache.hh" #include "base/sat_counter.hh" #include "base/types.hh" #include "mem/cache/base.hh" #include "mem/cache/cache_probe_arg.hh" #include "mem/cache/compressors/base.hh" #include "mem/cache/compressors/encoders/huffman.hh" -#include "mem/cache/prefetch/associative_set.hh" #include "sim/eventq.hh" #include "sim/probe/probe.hh" @@ -112,7 +112,7 @@ class FrequentValues : public Base enum Phase {SAMPLING, CODE_GENERATION, COMPRESSING}; Phase phase; - class VFTEntry : public TaggedEntry + class VFTEntry : public CacheEntry { public: /** @@ -130,14 +130,14 @@ class FrequentValues : public Base SatCounter32 counter; VFTEntry(std::size_t num_bits) - : TaggedEntry(), value(0), counter(num_bits) + : CacheEntry(), value(0), counter(num_bits) { } void invalidate() override { - TaggedEntry::invalidate(); + CacheEntry::invalidate(); value = 0; counter.reset(); } @@ -147,7 +147,7 @@ class FrequentValues : public Base * The Value Frequency Table, a small cache that keeps track and estimates * the frequency distribution of values in the cache. */ - AssociativeSet VFT; + AssociativeCache VFT; /** * A pseudo value is used as the representation of uncompressed values. From 8fb361161443a5ace9513a558a2b9a75c06efe21 Mon Sep 17 00:00:00 2001 From: Pranith Kumar Date: Sat, 6 Jan 2024 19:23:23 -0500 Subject: [PATCH 7/9] mem-cache: prefetch: Implement DCPT tables using cache library The DCPT table is better built using the generic cache library since we do not need the secure bit. Change-Id: I8a4a8d3dab7fbc3bbc816107492978ac7f3f5934 --- .../delta_correlating_prediction_tables.cc | 14 +++++++------- .../delta_correlating_prediction_tables.hh | 8 ++++---- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/src/mem/cache/prefetch/delta_correlating_prediction_tables.cc b/src/mem/cache/prefetch/delta_correlating_prediction_tables.cc index ea59bea3c0..9df252c1a6 100644 --- a/src/mem/cache/prefetch/delta_correlating_prediction_tables.cc +++ b/src/mem/cache/prefetch/delta_correlating_prediction_tables.cc @@ -42,15 +42,16 @@ namespace prefetch DeltaCorrelatingPredictionTables::DeltaCorrelatingPredictionTables( const DeltaCorrelatingPredictionTablesParams &p) : SimObject(p), deltaBits(p.delta_bits), deltaMaskBits(p.delta_mask_bits), - table(p.table_assoc, p.table_entries, p.table_indexing_policy, - p.table_replacement_policy, DCPTEntry(p.deltas_per_entry)) + table((name() + "DCPT").c_str(), p.table_entries, + p.table_assoc, p.table_replacement_policy, + p.table_indexing_policy, DCPTEntry(p.deltas_per_entry)) { } void DeltaCorrelatingPredictionTables::DCPTEntry::invalidate() { - TaggedEntry::invalidate(); + CacheEntry::invalidate(); deltas.flush(); while (!deltas.full()) { @@ -134,9 +135,8 @@ DeltaCorrelatingPredictionTables::calculatePrefetch( } Addr address = pfi.getAddr(); Addr pc = pfi.getPC(); - // Look up table entry, is_secure is unused in findEntry because we - // index using the pc - DCPTEntry *entry = table.findEntry(pc, false /* unused */); + // Look up table entry + DCPTEntry *entry = table.findEntry(pc); if (entry != nullptr) { entry->addAddress(address, deltaBits); //Delta correlating @@ -144,7 +144,7 @@ DeltaCorrelatingPredictionTables::calculatePrefetch( } else { entry = table.findVictim(pc); - table.insertEntry(pc, false /* unused */, entry); + table.insertEntry(pc, entry); entry->lastAddress = address; } diff --git a/src/mem/cache/prefetch/delta_correlating_prediction_tables.hh b/src/mem/cache/prefetch/delta_correlating_prediction_tables.hh index 7280c96733..482a8807e6 100644 --- a/src/mem/cache/prefetch/delta_correlating_prediction_tables.hh +++ b/src/mem/cache/prefetch/delta_correlating_prediction_tables.hh @@ -29,8 +29,8 @@ #ifndef __MEM_CACHE_PREFETCH_DELTA_CORRELATING_PREDICTION_TABLES_HH_ #define __MEM_CACHE_PREFETCH_DELTA_CORRELATING_PREDICTION_TABLES_HH_ +#include "base/cache/associative_cache.hh" #include "base/circular_queue.hh" -#include "mem/cache/prefetch/associative_set.hh" #include "mem/cache/prefetch/queued.hh" namespace gem5 @@ -65,7 +65,7 @@ class DeltaCorrelatingPredictionTables : public SimObject const unsigned int deltaMaskBits; /** DCPT Table entry datatype */ - struct DCPTEntry : public TaggedEntry + struct DCPTEntry : public CacheEntry { /** Last accessed address */ Addr lastAddress; @@ -77,7 +77,7 @@ class DeltaCorrelatingPredictionTables : public SimObject * @param num_deltas number of deltas stored in the entry */ DCPTEntry(unsigned int num_deltas) - : TaggedEntry(), lastAddress(0), deltas(num_deltas) + : CacheEntry(), lastAddress(0), deltas(num_deltas) { } @@ -103,7 +103,7 @@ class DeltaCorrelatingPredictionTables : public SimObject }; /** The main table */ - AssociativeSet table; + AssociativeCache table; public: DeltaCorrelatingPredictionTables( From f3bc10c1687cbf3b5581080d9ecde3183b4f0761 Mon Sep 17 00:00:00 2001 From: Pranith Kumar Date: Sat, 6 Jan 2024 20:50:55 -0500 Subject: [PATCH 8/9] mem-cache: Derive tagged entry from cache entry The tagged entry can be derived from the generic cache entry and add the secure flag that it needs. This reduces code duplication. Change-Id: I7ff0bddc40604a8a789036a6300eabda40339a0f --- src/mem/cache/tags/tagged_entry.hh | 68 +++++++++++------------------- 1 file changed, 24 insertions(+), 44 deletions(-) diff --git a/src/mem/cache/tags/tagged_entry.hh b/src/mem/cache/tags/tagged_entry.hh index f6166f6fbe..8ff12d7e20 100644 --- a/src/mem/cache/tags/tagged_entry.hh +++ b/src/mem/cache/tags/tagged_entry.hh @@ -31,7 +31,9 @@ #include +#include "base/cache/cache_entry.hh" #include "base/cprintf.hh" +#include "base/logging.hh" #include "base/types.hh" #include "mem/cache/replacement_policies/replaceable_entry.hh" @@ -43,19 +45,12 @@ namespace gem5 * secure bit, which informs whether it belongs to a secure address space. * A tagged entry's contents are only relevant if it is marked as valid. */ -class TaggedEntry : public ReplaceableEntry +class TaggedEntry : public CacheEntry { public: - TaggedEntry() : _valid(false), _secure(false), _tag(MaxAddr) {} + TaggedEntry() : CacheEntry(), _secure(false) {} ~TaggedEntry() = default; - /** - * Checks if the entry is valid. - * - * @return True if the entry is valid. - */ - virtual bool isValid() const { return _valid; } - /** * Check if this block holds data from the secure memory space. * @@ -63,13 +58,6 @@ class TaggedEntry : public ReplaceableEntry */ bool isSecure() const { return _secure; } - /** - * Get tag associated to this block. - * - * @return The tag value. - */ - virtual Addr getTag() const { return _tag; } - /** * Checks if the given tag information corresponds to this entry's. * @@ -100,10 +88,10 @@ class TaggedEntry : public ReplaceableEntry } /** Invalidate the block. Its contents are no longer valid. */ - virtual void invalidate() + void + invalidate() override { - _valid = false; - setTag(MaxAddr); + CacheEntry::invalidate(); clearSecure(); } @@ -114,44 +102,36 @@ class TaggedEntry : public ReplaceableEntry isSecure(), isValid(), ReplaceableEntry::print()); } - protected: - /** - * Set tag associated to this block. - * - * @param tag The tag value. - */ - virtual void setTag(Addr tag) { _tag = tag; } + bool + matchTag(const Addr tag) const override + { + panic("Need is_secure arg"); + return false; + } + void + insert(const Addr tag) override + { + panic("Need is_secure arg"); + return; + } + protected: /** Set secure bit. */ virtual void setSecure() { _secure = true; } - /** Set valid bit. The block must be invalid beforehand. */ - virtual void - setValid() - { - assert(!isValid()); - _valid = true; - } - private: - /** - * Valid bit. The contents of this entry are only valid if this bit is set. - * @sa invalidate() - * @sa insert() - */ - bool _valid; - /** * Secure bit. Marks whether this entry refers to an address in the secure * memory space. Must always be modified along with the tag. */ bool _secure; - /** The entry's tag. */ - Addr _tag; - /** Clear secure bit. Should be only used by the invalidation function. */ void clearSecure() { _secure = false; } + + /** Do not use API without is_secure flag. */ + using CacheEntry::matchTag; + using CacheEntry::insert; }; } // namespace gem5 From 769f750eb993d3ba17f6c6370a23407d67245eb8 Mon Sep 17 00:00:00 2001 From: Pranith Kumar Date: Fri, 12 Jan 2024 21:54:51 -0500 Subject: [PATCH 9/9] mem-cache: Implement AssociativeSet from AssociativeCache AssociativeSet can reuse most of the generic cache library code with the addition of a secure bit. This reduces duplicated code. Change-Id: I008ef79b0dd5f95418a3fb79396aeb0a6c601784 --- .../prefetch/access_map_pattern_matching.cc | 6 +- src/mem/cache/prefetch/associative_set.hh | 112 ++++-------------- .../cache/prefetch/associative_set_impl.hh | 84 +++---------- src/mem/cache/prefetch/indirect_memory.cc | 20 ++-- src/mem/cache/prefetch/indirect_memory.hh | 4 +- .../cache/prefetch/irregular_stream_buffer.cc | 18 +-- src/mem/cache/prefetch/pif.cc | 13 +- src/mem/cache/prefetch/pif.hh | 3 +- src/mem/cache/prefetch/signature_path.cc | 20 ++-- src/mem/cache/prefetch/signature_path.hh | 3 +- src/mem/cache/prefetch/signature_path_v2.cc | 7 +- src/mem/cache/prefetch/signature_path_v2.hh | 2 +- .../spatio_temporal_memory_streaming.cc | 20 ++-- .../spatio_temporal_memory_streaming.hh | 2 +- src/mem/cache/prefetch/stride.cc | 17 ++- src/mem/cache/prefetch/stride.hh | 4 +- 16 files changed, 116 insertions(+), 219 deletions(-) diff --git a/src/mem/cache/prefetch/access_map_pattern_matching.cc b/src/mem/cache/prefetch/access_map_pattern_matching.cc index f53b77868f..f90a7d1eb0 100644 --- a/src/mem/cache/prefetch/access_map_pattern_matching.cc +++ b/src/mem/cache/prefetch/access_map_pattern_matching.cc @@ -51,9 +51,11 @@ AccessMapPatternMatching::AccessMapPatternMatching( lowCacheHitThreshold(p.low_cache_hit_threshold), epochCycles(p.epoch_cycles), offChipMemoryLatency(p.offchip_memory_latency), - accessMapTable(p.access_map_table_assoc, p.access_map_table_entries, - p.access_map_table_indexing_policy, + accessMapTable("AccessMapTable", + p.access_map_table_entries, + p.access_map_table_assoc, p.access_map_table_replacement_policy, + p.access_map_table_indexing_policy, AccessMapEntry(hotZoneSize / blkSize)), numGoodPrefetches(0), numTotalPrefetches(0), numRawCacheMisses(0), numRawCacheHits(0), degree(startDegree), usefulDegree(startDegree), diff --git a/src/mem/cache/prefetch/associative_set.hh b/src/mem/cache/prefetch/associative_set.hh index 7fbbf879e4..b73939fd5d 100644 --- a/src/mem/cache/prefetch/associative_set.hh +++ b/src/mem/cache/prefetch/associative_set.hh @@ -29,6 +29,9 @@ #ifndef __CACHE_PREFETCH_ASSOCIATIVE_SET_HH__ #define __CACHE_PREFETCH_ASSOCIATIVE_SET_HH__ +#include + +#include "base/cache/associative_cache.hh" #include "mem/cache/replacement_policies/base.hh" #include "mem/cache/tags/indexing_policies/base.hh" #include "mem/cache/tags/tagged_entry.hh" @@ -42,38 +45,27 @@ namespace gem5 * bool value is used as an additional tag data of the entry. */ template -class AssociativeSet +class AssociativeSet : public AssociativeCache { static_assert(std::is_base_of_v, "Entry must derive from TaggedEntry"); - /** Associativity of the container */ - const int associativity; - /** - * Total number of entries, entries are organized in sets of the provided - * associativity. The number of associative sets is obtained by dividing - * numEntries by associativity. - */ - const int numEntries; - /** Pointer to the indexing policy */ - BaseIndexingPolicy* const indexingPolicy; - /** Pointer to the replacement policy */ - replacement_policy::Base* const replacementPolicy; - /** Vector containing the entries of the container */ - std::vector entries; - public: /** * Public constructor - * @param assoc number of elements in each associative set + * @param name Name of the cache * @param num_entries total number of entries of the container, the number - * of sets can be calculated dividing this balue by the 'assoc' value - * @param idx_policy indexing policy + * of sets can be calculated dividing this balue by the 'assoc' value + * @param assoc number of elements in each associative set * @param rpl_policy replacement policy + * @param idx_policy indexing policy * @param init_val initial value of the elements of the set */ - AssociativeSet(int assoc, int num_entries, BaseIndexingPolicy *idx_policy, - replacement_policy::Base *rpl_policy, Entry const &init_val = Entry()); + AssociativeSet(const char *name, const size_t num_entries, + const size_t associativity_, + replacement_policy::Base *repl_policy, + BaseIndexingPolicy *indexing_policy, + Entry const &init_val = Entry()); /** * Find an entry within the set @@ -84,28 +76,6 @@ class AssociativeSet */ Entry* findEntry(Addr addr, bool is_secure) const; - /** - * Do an access to the entry, this is required to - * update the replacement information data. - * @param entry the accessed entry - */ - void accessEntry(Entry *entry); - - /** - * Find a victim to be replaced - * @param addr key to select the possible victim - * @result entry to be victimized - */ - Entry* findVictim(Addr addr); - - /** - * Find the set of entries that could be replaced given - * that we want to add a new entry with the provided key - * @param addr key to select the set of entries - * @result vector of candidates matching with the provided key - */ - std::vector getPossibleEntries(const Addr addr) const; - /** * Indicate that an entry has just been inserted * @param addr key of the container @@ -114,54 +84,16 @@ class AssociativeSet */ void insertEntry(Addr addr, bool is_secure, Entry* entry); - /** - * Invalidate an entry and its respective replacement data. - * - * @param entry Entry to be invalidated. - */ - void invalidate(Entry* entry); + private: + // The following APIs are excluded since they lack the secure bit + using AssociativeCache::getTag; + using AssociativeCache::accessEntryByAddr; + using AssociativeCache::findEntry; + using AssociativeCache::insertEntry; + using AssociativeCache::getPossibleEntries; - /** Iterator types */ - using const_iterator = typename std::vector::const_iterator; - using iterator = typename std::vector::iterator; - - /** - * Returns an iterator to the first entry of the dictionary - * @result iterator to the first element - */ - iterator begin() - { - return entries.begin(); - } - - /** - * Returns an iterator pointing to the end of the the dictionary - * (placeholder element, should not be accessed) - * @result iterator to the end element - */ - iterator end() - { - return entries.end(); - } - - /** - * Returns an iterator to the first entry of the dictionary - * @result iterator to the first element - */ - const_iterator begin() const - { - return entries.begin(); - } - - /** - * Returns an iterator pointing to the end of the the dictionary - * (placeholder element, should not be accessed) - * @result iterator to the end element - */ - const_iterator end() const - { - return entries.end(); - } + using AssociativeCache::replPolicy; + using AssociativeCache::indexingPolicy; }; } // namespace gem5 diff --git a/src/mem/cache/prefetch/associative_set_impl.hh b/src/mem/cache/prefetch/associative_set_impl.hh index c53b19a98a..7385c34207 100644 --- a/src/mem/cache/prefetch/associative_set_impl.hh +++ b/src/mem/cache/prefetch/associative_set_impl.hh @@ -35,93 +35,41 @@ namespace gem5 { -template -AssociativeSet::AssociativeSet(int assoc, int num_entries, - BaseIndexingPolicy *idx_policy, replacement_policy::Base *rpl_policy, - Entry const &init_value) - : associativity(assoc), numEntries(num_entries), indexingPolicy(idx_policy), - replacementPolicy(rpl_policy), entries(numEntries, init_value) +template +AssociativeSet::AssociativeSet(const char *name, + const size_t num_entries, + const size_t associativity_, + replacement_policy::Base *repl_policy, + BaseIndexingPolicy *indexing_policy, + Entry const &init_val) + : AssociativeCache(name, num_entries, associativity_, + repl_policy, indexing_policy, init_val) { - fatal_if(!isPowerOf2(num_entries), "The number of entries of an " - "AssociativeSet<> must be a power of 2"); - fatal_if(!isPowerOf2(assoc), "The associativity of an AssociativeSet<> " - "must be a power of 2"); - for (unsigned int entry_idx = 0; entry_idx < numEntries; entry_idx += 1) { - Entry* entry = &entries[entry_idx]; - indexingPolicy->setEntry(entry, entry_idx); - entry->replacementData = replacementPolicy->instantiateEntry(); - } } -template +template Entry* AssociativeSet::findEntry(Addr addr, bool is_secure) const { Addr tag = indexingPolicy->extractTag(addr); - const std::vector selected_entries = - indexingPolicy->getPossibleEntries(addr); + auto candidates = indexingPolicy->getPossibleEntries(addr); - for (const auto& location : selected_entries) { - Entry* entry = static_cast(location); - if ((entry->getTag() == tag) && entry->isValid() && - entry->isSecure() == is_secure) { + for (auto candidate : candidates) { + Entry* entry = static_cast(candidate); + if (entry->matchTag(tag, is_secure)) { return entry; } } + return nullptr; } -template -void -AssociativeSet::accessEntry(Entry *entry) -{ - replacementPolicy->touch(entry->replacementData); -} - -template -Entry* -AssociativeSet::findVictim(Addr addr) -{ - // Get possible entries to be victimized - const std::vector selected_entries = - indexingPolicy->getPossibleEntries(addr); - Entry* victim = static_cast(replacementPolicy->getVictim( - selected_entries)); - // There is only one eviction for this replacement - invalidate(victim); - return victim; -} - - -template -std::vector -AssociativeSet::getPossibleEntries(const Addr addr) const -{ - std::vector selected_entries = - indexingPolicy->getPossibleEntries(addr); - std::vector entries(selected_entries.size(), nullptr); - - unsigned int idx = 0; - for (auto &entry : selected_entries) { - entries[idx++] = static_cast(entry); - } - return entries; -} - template void AssociativeSet::insertEntry(Addr addr, bool is_secure, Entry* entry) { entry->insert(indexingPolicy->extractTag(addr), is_secure); - replacementPolicy->reset(entry->replacementData); -} - -template -void -AssociativeSet::invalidate(Entry* entry) -{ - entry->invalidate(); - replacementPolicy->invalidate(entry->replacementData); + replPolicy->reset(entry->replacementData); } } // namespace gem5 diff --git a/src/mem/cache/prefetch/indirect_memory.cc b/src/mem/cache/prefetch/indirect_memory.cc index 780879bc6a..f689bff38c 100644 --- a/src/mem/cache/prefetch/indirect_memory.cc +++ b/src/mem/cache/prefetch/indirect_memory.cc @@ -44,11 +44,15 @@ IndirectMemory::IndirectMemory(const IndirectMemoryPrefetcherParams &p) shiftValues(p.shift_values), prefetchThreshold(p.prefetch_threshold), streamCounterThreshold(p.stream_counter_threshold), streamingDistance(p.streaming_distance), - prefetchTable(p.pt_table_assoc, p.pt_table_entries, - p.pt_table_indexing_policy, p.pt_table_replacement_policy, + prefetchTable((name() + ".PrefetchTable").c_str(), + p.pt_table_entries, + p.pt_table_assoc, + p.pt_table_replacement_policy, + p.pt_table_indexing_policy, PrefetchTableEntry(p.num_indirect_counter_bits)), - ipd(p.ipd_table_assoc, p.ipd_table_entries, p.ipd_table_indexing_policy, + ipd((name() + ".IPD").c_str(), p.ipd_table_entries, p.ipd_table_assoc, p.ipd_table_replacement_policy, + p.ipd_table_indexing_policy, IndirectPatternDetectorEntry(p.addr_array_len, shiftValues.size())), ipdEntryTrackingMisses(nullptr), byteOrder(p.sys->getGuestByteOrder()) { @@ -81,8 +85,7 @@ IndirectMemory::calculatePrefetch(const PrefetchInfo &pfi, } } else { // if misses are not being tracked, attempt to detect stream accesses - PrefetchTableEntry *pt_entry = - prefetchTable.findEntry(pc, false /* unused */); + PrefetchTableEntry *pt_entry = prefetchTable.findEntry(pc); if (pt_entry != nullptr) { prefetchTable.accessEntry(pt_entry); @@ -156,7 +159,7 @@ IndirectMemory::calculatePrefetch(const PrefetchInfo &pfi, } else { pt_entry = prefetchTable.findVictim(pc); assert(pt_entry != nullptr); - prefetchTable.insertEntry(pc, false /* unused */, pt_entry); + prefetchTable.insertEntry(pc, pt_entry); pt_entry->address = addr; pt_entry->secure = is_secure; } @@ -169,8 +172,7 @@ IndirectMemory::allocateOrUpdateIPDEntry( { // The address of the pt_entry is used to index the IPD Addr ipd_entry_addr = (Addr) pt_entry; - IndirectPatternDetectorEntry *ipd_entry = ipd.findEntry(ipd_entry_addr, - false/* unused */); + IndirectPatternDetectorEntry *ipd_entry = ipd.findEntry(ipd_entry_addr); if (ipd_entry != nullptr) { ipd.accessEntry(ipd_entry); if (!ipd_entry->secondIndexSet) { @@ -187,7 +189,7 @@ IndirectMemory::allocateOrUpdateIPDEntry( } else { ipd_entry = ipd.findVictim(ipd_entry_addr); assert(ipd_entry != nullptr); - ipd.insertEntry(ipd_entry_addr, false /* unused */, ipd_entry); + ipd.insertEntry(ipd_entry_addr, ipd_entry); ipd_entry->idx1 = index; ipdEntryTrackingMisses = ipd_entry; } diff --git a/src/mem/cache/prefetch/indirect_memory.hh b/src/mem/cache/prefetch/indirect_memory.hh index 877e55d63d..1062545301 100644 --- a/src/mem/cache/prefetch/indirect_memory.hh +++ b/src/mem/cache/prefetch/indirect_memory.hh @@ -121,7 +121,7 @@ class IndirectMemory : public Queued } }; /** Prefetch table */ - AssociativeSet prefetchTable; + AssociativeCache prefetchTable; /** Indirect Pattern Detector entrt */ struct IndirectPatternDetectorEntry : public TaggedEntry @@ -160,7 +160,7 @@ class IndirectMemory : public Queued } }; /** Indirect Pattern Detector (IPD) table */ - AssociativeSet ipd; + AssociativeCache ipd; /** Entry currently tracking misses */ IndirectPatternDetectorEntry *ipdEntryTrackingMisses; diff --git a/src/mem/cache/prefetch/irregular_stream_buffer.cc b/src/mem/cache/prefetch/irregular_stream_buffer.cc index bf81ebedc5..7a912ac8b4 100644 --- a/src/mem/cache/prefetch/irregular_stream_buffer.cc +++ b/src/mem/cache/prefetch/irregular_stream_buffer.cc @@ -44,19 +44,23 @@ IrregularStreamBuffer::IrregularStreamBuffer( chunkSize(p.chunk_size), prefetchCandidatesPerEntry(p.prefetch_candidates_per_entry), degree(p.degree), - trainingUnit(p.training_unit_assoc, p.training_unit_entries, - p.training_unit_indexing_policy, - p.training_unit_replacement_policy), - psAddressMappingCache(p.address_map_cache_assoc, + trainingUnit((name() + ".TrainingUnit").c_str(), + p.training_unit_entries, + p.training_unit_assoc, + p.training_unit_replacement_policy, + p.training_unit_indexing_policy), + psAddressMappingCache((name() + ".PSAddressMappingCache").c_str(), p.address_map_cache_entries, - p.ps_address_map_cache_indexing_policy, + p.address_map_cache_assoc, p.ps_address_map_cache_replacement_policy, + p.ps_address_map_cache_indexing_policy, AddressMappingEntry(prefetchCandidatesPerEntry, p.num_counter_bits)), - spAddressMappingCache(p.address_map_cache_assoc, + spAddressMappingCache((name() + ".SPAddressMappingCache").c_str(), p.address_map_cache_entries, - p.sp_address_map_cache_indexing_policy, + p.address_map_cache_assoc, p.sp_address_map_cache_replacement_policy, + p.sp_address_map_cache_indexing_policy, AddressMappingEntry(prefetchCandidatesPerEntry, p.num_counter_bits)), structuralAddressCounter(0) diff --git a/src/mem/cache/prefetch/pif.cc b/src/mem/cache/prefetch/pif.cc index 581831491f..138c89f08e 100644 --- a/src/mem/cache/prefetch/pif.cc +++ b/src/mem/cache/prefetch/pif.cc @@ -46,8 +46,9 @@ PIF::PIF(const PIFPrefetcherParams &p) succSize(p.succ_spatial_region_bits), maxCompactorEntries(p.compactor_entries), historyBuffer(p.history_buffer_size), - index(p.index_assoc, p.index_entries, p.index_indexing_policy, - p.index_replacement_policy), + index((name() + ".PIFIndex").c_str(), p.index_entries, p.index_assoc, + p.index_replacement_policy, + p.index_indexing_policy), streamAddressBuffer(p.stream_address_buffer_entries), listenersPC() { @@ -176,15 +177,13 @@ PIF::notifyRetiredInst(const Addr pc) // the 'iterator' table to point to the new entry historyBuffer.push_back(spatialCompactor); - IndexEntry *idx_entry = - index.findEntry(spatialCompactor.trigger, false); + auto idx_entry = index.findEntry(spatialCompactor.trigger); if (idx_entry != nullptr) { index.accessEntry(idx_entry); } else { idx_entry = index.findVictim(spatialCompactor.trigger); assert(idx_entry != nullptr); - index.insertEntry(spatialCompactor.trigger, false, - idx_entry); + index.insertEntry(spatialCompactor.trigger, idx_entry); } idx_entry->historyIt = historyBuffer.getIterator(historyBuffer.tail()); @@ -220,7 +219,7 @@ PIF::calculatePrefetch(const PrefetchInfo &pfi, // Check if a valid entry in the 'index' table is found and allocate a new // active prediction stream - IndexEntry *idx_entry = index.findEntry(pc, /* unused */ false); + IndexEntry *idx_entry = index.findEntry(pc); if (idx_entry != nullptr) { index.accessEntry(idx_entry); diff --git a/src/mem/cache/prefetch/pif.hh b/src/mem/cache/prefetch/pif.hh index ecb97db78d..3592397804 100644 --- a/src/mem/cache/prefetch/pif.hh +++ b/src/mem/cache/prefetch/pif.hh @@ -138,11 +138,12 @@ class PIF : public Queued { HistoryBuffer::iterator historyIt; }; + /** * The index table is a small cache-like structure that facilitates * fast search of the history buffer. */ - AssociativeSet index; + AssociativeCache index; /** * A Stream Address Buffer (SAB) tracks a window of consecutive diff --git a/src/mem/cache/prefetch/signature_path.cc b/src/mem/cache/prefetch/signature_path.cc index 6d3f05bf17..122c498be7 100644 --- a/src/mem/cache/prefetch/signature_path.cc +++ b/src/mem/cache/prefetch/signature_path.cc @@ -48,12 +48,16 @@ SignaturePath::SignaturePath(const SignaturePathPrefetcherParams &p) signatureBits(p.signature_bits), prefetchConfidenceThreshold(p.prefetch_confidence_threshold), lookaheadConfidenceThreshold(p.lookahead_confidence_threshold), - signatureTable(p.signature_table_assoc, p.signature_table_entries, - p.signature_table_indexing_policy, - p.signature_table_replacement_policy), - patternTable(p.pattern_table_assoc, p.pattern_table_entries, - p.pattern_table_indexing_policy, + signatureTable((name() + ".SignatureTable").c_str(), + p.signature_table_entries, + p.signature_table_assoc, + p.signature_table_replacement_policy, + p.signature_table_indexing_policy), + patternTable((name() + ".PatternTable").c_str(), + p.pattern_table_entries, + p.pattern_table_assoc, p.pattern_table_replacement_policy, + p.pattern_table_indexing_policy, PatternEntry(stridesPerPatternEntry, p.num_counter_bits)) { fatal_if(prefetchConfidenceThreshold < 0, @@ -188,7 +192,7 @@ SignaturePath::getSignatureEntry(Addr ppn, bool is_secure, SignaturePath::PatternEntry & SignaturePath::getPatternEntry(Addr signature) { - PatternEntry* pattern_entry = patternTable.findEntry(signature, false); + PatternEntry* pattern_entry = patternTable.findEntry(signature); if (pattern_entry != nullptr) { // Signature found patternTable.accessEntry(pattern_entry); @@ -197,7 +201,7 @@ SignaturePath::getPatternEntry(Addr signature) pattern_entry = patternTable.findVictim(signature); assert(pattern_entry != nullptr); - patternTable.insertEntry(signature, false, pattern_entry); + patternTable.insertEntry(signature, pattern_entry); } return *pattern_entry; } @@ -273,7 +277,7 @@ SignaturePath::calculatePrefetch(const PrefetchInfo &pfi, // confidence, these are prefetch candidates // - select the entry with the highest counter as the "lookahead" PatternEntry *current_pattern_entry = - patternTable.findEntry(current_signature, false); + patternTable.findEntry(current_signature); PatternStrideEntry const *lookahead = nullptr; if (current_pattern_entry != nullptr) { unsigned long max_counter = 0; diff --git a/src/mem/cache/prefetch/signature_path.hh b/src/mem/cache/prefetch/signature_path.hh index a561cda063..d5e1ea18ab 100644 --- a/src/mem/cache/prefetch/signature_path.hh +++ b/src/mem/cache/prefetch/signature_path.hh @@ -146,8 +146,9 @@ class SignaturePath : public Queued */ PatternStrideEntry &getStrideEntry(stride_t stride); }; + /** Pattern table */ - AssociativeSet patternTable; + AssociativeCache patternTable; /** * Generates a new signature from an existing one and a new stride diff --git a/src/mem/cache/prefetch/signature_path_v2.cc b/src/mem/cache/prefetch/signature_path_v2.cc index b50721ca69..518fc3e0e7 100644 --- a/src/mem/cache/prefetch/signature_path_v2.cc +++ b/src/mem/cache/prefetch/signature_path_v2.cc @@ -42,10 +42,11 @@ namespace prefetch SignaturePathV2::SignaturePathV2(const SignaturePathPrefetcherV2Params &p) : SignaturePath(p), - globalHistoryRegister(p.global_history_register_entries, + globalHistoryRegister((name() + ".GlobalHistoryRegister").c_str(), p.global_history_register_entries, - p.global_history_register_indexing_policy, + p.global_history_register_entries, p.global_history_register_replacement_policy, + p.global_history_register_indexing_policy, GlobalHistoryEntry()) { } @@ -124,7 +125,7 @@ SignaturePathV2::handlePageCrossingLookahead(signature_t signature, GlobalHistoryEntry *gh_entry = globalHistoryRegister.findVictim(0); assert(gh_entry != nullptr); // Any address value works, as it is never used - globalHistoryRegister.insertEntry(0, false, gh_entry); + globalHistoryRegister.insertEntry(0, gh_entry); gh_entry->signature = signature; gh_entry->lastBlock = last_offset; diff --git a/src/mem/cache/prefetch/signature_path_v2.hh b/src/mem/cache/prefetch/signature_path_v2.hh index 417b7ec540..ee9e2cdc3c 100644 --- a/src/mem/cache/prefetch/signature_path_v2.hh +++ b/src/mem/cache/prefetch/signature_path_v2.hh @@ -66,7 +66,7 @@ class SignaturePathV2 : public SignaturePath delta(0) {} }; /** Global History Register */ - AssociativeSet globalHistoryRegister; + AssociativeCache globalHistoryRegister; double calculateLookaheadConfidence(PatternEntry const &sig, PatternStrideEntry const &lookahead) const override; diff --git a/src/mem/cache/prefetch/spatio_temporal_memory_streaming.cc b/src/mem/cache/prefetch/spatio_temporal_memory_streaming.cc index 406e444be0..c2f936b176 100644 --- a/src/mem/cache/prefetch/spatio_temporal_memory_streaming.cc +++ b/src/mem/cache/prefetch/spatio_temporal_memory_streaming.cc @@ -42,16 +42,18 @@ STeMS::STeMS(const STeMSPrefetcherParams &p) : Queued(p), spatialRegionSize(p.spatial_region_size), spatialRegionSizeBits(floorLog2(p.spatial_region_size)), reconstructionEntries(p.reconstruction_entries), - activeGenerationTable(p.active_generation_table_assoc, + activeGenerationTable((name() + ".ActiveGenerationTable").c_str(), p.active_generation_table_entries, - p.active_generation_table_indexing_policy, + p.active_generation_table_assoc, p.active_generation_table_replacement_policy, + p.active_generation_table_indexing_policy, ActiveGenerationTableEntry( spatialRegionSize / blkSize)), - patternSequenceTable(p.pattern_sequence_table_assoc, + patternSequenceTable((name() + ".PatternSequenceTable").c_str(), p.pattern_sequence_table_entries, - p.pattern_sequence_table_indexing_policy, + p.pattern_sequence_table_assoc, p.pattern_sequence_table_replacement_policy, + p.pattern_sequence_table_indexing_policy, ActiveGenerationTableEntry( spatialRegionSize / blkSize)), rmob(p.region_miss_order_buffer_entries), @@ -90,15 +92,12 @@ STeMS::checkForActiveGenerationsEnd(const CacheAccessor &cache) } if (generation_ended) { // PST is indexed using the PC (secure bit is unused) - ActiveGenerationTableEntry *pst_entry = - patternSequenceTable.findEntry(pst_addr, - false /*unused*/); + auto pst_entry = patternSequenceTable.findEntry(pst_addr); if (pst_entry == nullptr) { // Tipically an entry will not exist pst_entry = patternSequenceTable.findVictim(pst_addr); assert(pst_entry != nullptr); - patternSequenceTable.insertEntry(pst_addr, - false /*unused*/, pst_entry); + patternSequenceTable.insertEntry(pst_addr, pst_entry); } else { patternSequenceTable.accessEntry(pst_entry); } @@ -222,8 +221,7 @@ STeMS::reconstructSequence( idx = 0; for (auto it = rmob_it; it != rmob.end() && (idx < reconstructionEntries); it++) { - ActiveGenerationTableEntry *pst_entry = - patternSequenceTable.findEntry(it->pstAddress, false /* unused */); + auto pst_entry = patternSequenceTable.findEntry(it->pstAddress); if (pst_entry != nullptr) { patternSequenceTable.accessEntry(pst_entry); for (auto &seq_entry : pst_entry->sequence) { diff --git a/src/mem/cache/prefetch/spatio_temporal_memory_streaming.hh b/src/mem/cache/prefetch/spatio_temporal_memory_streaming.hh index c6cd2f72d1..753c6a79b4 100644 --- a/src/mem/cache/prefetch/spatio_temporal_memory_streaming.hh +++ b/src/mem/cache/prefetch/spatio_temporal_memory_streaming.hh @@ -155,7 +155,7 @@ class STeMS : public Queued /** Active Generation Table (AGT) */ AssociativeSet activeGenerationTable; /** Pattern Sequence Table (PST) */ - AssociativeSet patternSequenceTable; + AssociativeCache patternSequenceTable; /** Data type of the Region Miss Order Buffer entry */ struct RegionMissOrderBufferEntry diff --git a/src/mem/cache/prefetch/stride.cc b/src/mem/cache/prefetch/stride.cc index e4a552b912..6335819a96 100644 --- a/src/mem/cache/prefetch/stride.cc +++ b/src/mem/cache/prefetch/stride.cc @@ -86,7 +86,7 @@ Stride::Stride(const StridePrefetcherParams &p) degree(p.degree), distance(p.distance), pcTableInfo(p.table_assoc, p.table_entries, p.table_indexing_policy, - p.table_replacement_policy) + p.table_replacement_policy) { } @@ -105,16 +105,21 @@ Stride::findTable(int context) Stride::PCTable* Stride::allocateNewContext(int context) { + std::string table_name = name() + ".PCTable" + std::to_string(context); // Create new table - auto insertion_result = pcTables.insert(std::make_pair(context, - PCTable(pcTableInfo.assoc, pcTableInfo.numEntries, - pcTableInfo.indexingPolicy, pcTableInfo.replacementPolicy, - StrideEntry(initConfidence)))); + auto ins_result = pcTables.emplace(std::piecewise_construct, + std::forward_as_tuple(context), + std::forward_as_tuple(table_name.c_str(), + pcTableInfo.numEntries, + pcTableInfo.assoc, + pcTableInfo.replacementPolicy, + pcTableInfo.indexingPolicy, + StrideEntry(initConfidence))); DPRINTF(HWPrefetch, "Adding context %i with stride entries\n", context); // Get iterator to new pc table, and then return a pointer to the new table - return &(insertion_result.first->second); + return &(ins_result.first->second); } void diff --git a/src/mem/cache/prefetch/stride.hh b/src/mem/cache/prefetch/stride.hh index cb5d79fd0e..f610503338 100644 --- a/src/mem/cache/prefetch/stride.hh +++ b/src/mem/cache/prefetch/stride.hh @@ -125,8 +125,8 @@ class Stride : public Queued replacement_policy::Base* const replacementPolicy; PCTableInfo(int assoc, int num_entries, - BaseIndexingPolicy* indexing_policy, - replacement_policy::Base* repl_policy) + BaseIndexingPolicy* indexing_policy, + replacement_policy::Base* repl_policy) : assoc(assoc), numEntries(num_entries), indexingPolicy(indexing_policy), replacementPolicy(repl_policy) {