From 2cc2ad509728192ccc1c8d3e1f200adb6e857dfc Mon Sep 17 00:00:00 2001 From: Pranith Kumar Date: Mon, 5 Feb 2024 22:44:19 -0500 Subject: [PATCH] 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__