| /* |
| * Copyright (c) 2012-2018 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) 2020 Inria |
| * Copyright (c) 2003-2005 The Regents of The University of Michigan |
| * 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. |
| */ |
| |
| /** @file |
| * Definitions of a simple cache block class. |
| */ |
| |
| #ifndef __MEM_CACHE_CACHE_BLK_HH__ |
| #define __MEM_CACHE_CACHE_BLK_HH__ |
| |
| #include <cassert> |
| #include <cstdint> |
| #include <iosfwd> |
| #include <list> |
| #include <string> |
| |
| #include "base/printable.hh" |
| #include "base/types.hh" |
| #include "mem/cache/tags/tagged_entry.hh" |
| #include "mem/packet.hh" |
| #include "mem/request.hh" |
| #include "sim/cur_tick.hh" |
| |
| namespace gem5 |
| { |
| |
| /** |
| * A Basic Cache block. |
| * Contains information regarding its coherence, prefetching status, as |
| * well as a pointer to its data. |
| */ |
| class CacheBlk : public TaggedEntry |
| { |
| public: |
| /** |
| * Cache block's enum listing the supported coherence bits. The valid |
| * bit is not defined here because it is part of a TaggedEntry. |
| */ |
| enum CoherenceBits : unsigned |
| { |
| /** write permission */ |
| WritableBit = 0x02, |
| /** |
| * Read permission. Note that a block can be valid but not readable |
| * if there is an outstanding write upgrade miss. |
| */ |
| ReadableBit = 0x04, |
| /** dirty (modified) */ |
| DirtyBit = 0x08, |
| |
| /** |
| * Helper enum value that includes all other bits. Whenever a new |
| * bits is added, this should be updated. |
| */ |
| AllBits = 0x0E, |
| }; |
| |
| /** |
| * Contains a copy of the data in this block for easy access. This is used |
| * for efficient execution when the data could be actually stored in |
| * another format (COW, compressed, sub-blocked, etc). In all cases the |
| * data stored here should be kept consistant with the actual data |
| * referenced by this block. |
| */ |
| uint8_t *data; |
| |
| /** |
| * Which curTick() will this block be accessible. Its value is only |
| * meaningful if the block is valid. |
| */ |
| Tick whenReady; |
| |
| protected: |
| /** |
| * Represents that the indicated thread context has a "lock" on |
| * the block, in the LL/SC sense. |
| */ |
| class Lock |
| { |
| public: |
| ContextID contextId; // locking context |
| Addr lowAddr; // low address of lock range |
| Addr highAddr; // high address of lock range |
| |
| // check for matching execution context, and an address that |
| // is within the lock |
| bool matches(const RequestPtr &req) const |
| { |
| Addr req_low = req->getPaddr(); |
| Addr req_high = req_low + req->getSize() -1; |
| return (contextId == req->contextId()) && |
| (req_low >= lowAddr) && (req_high <= highAddr); |
| } |
| |
| // check if a request is intersecting and thus invalidating the lock |
| bool intersects(const RequestPtr &req) const |
| { |
| Addr req_low = req->getPaddr(); |
| Addr req_high = req_low + req->getSize() - 1; |
| |
| return (req_low <= highAddr) && (req_high >= lowAddr); |
| } |
| |
| Lock(const RequestPtr &req) |
| : contextId(req->contextId()), |
| lowAddr(req->getPaddr()), |
| highAddr(lowAddr + req->getSize() - 1) |
| { |
| } |
| }; |
| |
| /** List of thread contexts that have performed a load-locked (LL) |
| * on the block since the last store. */ |
| std::list<Lock> lockList; |
| |
| public: |
| CacheBlk() : TaggedEntry(), data(nullptr), _tickInserted(0) |
| { |
| invalidate(); |
| } |
| |
| CacheBlk(const CacheBlk&) = delete; |
| CacheBlk& operator=(const CacheBlk&) = delete; |
| CacheBlk(const CacheBlk&&) = delete; |
| /** |
| * Move assignment operator. |
| * This should only be used to move an existing valid entry into an |
| * invalid one, not to create a new entry. In the end the valid entry |
| * will become invalid, and the invalid, valid. All location related |
| * variables will remain the same, that is, an entry cannot move its |
| * data, just its metadata contents. |
| */ |
| virtual CacheBlk& |
| operator=(CacheBlk&& other) |
| { |
| // Copying an entry into a valid one would imply in skipping all |
| // replacement steps, so it cannot be allowed |
| assert(!isValid()); |
| assert(other.isValid()); |
| |
| insert(other.getTag(), other.isSecure()); |
| |
| if (other.wasPrefetched()) { |
| setPrefetched(); |
| } |
| setCoherenceBits(other.coherence); |
| setTaskId(other.getTaskId()); |
| setWhenReady(curTick()); |
| setRefCount(other.getRefCount()); |
| setSrcRequestorId(other.getSrcRequestorId()); |
| std::swap(lockList, other.lockList); |
| |
| other.invalidate(); |
| |
| return *this; |
| } |
| virtual ~CacheBlk() {}; |
| |
| /** |
| * Invalidate the block and clear all state. |
| */ |
| virtual void invalidate() override |
| { |
| TaggedEntry::invalidate(); |
| |
| clearPrefetched(); |
| clearCoherenceBits(AllBits); |
| |
| setTaskId(context_switch_task_id::Unknown); |
| setWhenReady(MaxTick); |
| setRefCount(0); |
| setSrcRequestorId(Request::invldRequestorId); |
| lockList.clear(); |
| } |
| |
| /** |
| * Sets the corresponding coherence bits. |
| * |
| * @param bits The coherence bits to be set. |
| */ |
| void |
| setCoherenceBits(unsigned bits) |
| { |
| assert(isValid()); |
| coherence |= bits; |
| } |
| |
| /** |
| * Clear the corresponding coherence bits. |
| * |
| * @param bits The coherence bits to be cleared. |
| */ |
| void clearCoherenceBits(unsigned bits) { coherence &= ~bits; } |
| |
| /** |
| * Checks the given coherence bits are set. |
| * |
| * @return True if the block is readable. |
| */ |
| bool |
| isSet(unsigned bits) const |
| { |
| return isValid() && (coherence & bits); |
| } |
| |
| /** |
| * Check if this block was the result of a hardware prefetch, yet to |
| * be touched. |
| * @return True if the block was a hardware prefetch, unaccesed. |
| */ |
| bool wasPrefetched() const { return _prefetched; } |
| |
| /** |
| * Clear the prefetching bit. Either because it was recently used, or due |
| * to the block being invalidated. |
| */ |
| void clearPrefetched() { _prefetched = false; } |
| |
| /** Marks this blocks as a recently prefetched block. */ |
| void setPrefetched() { _prefetched = true; } |
| |
| /** |
| * Get tick at which block's data will be available for access. |
| * |
| * @return Data ready tick. |
| */ |
| Tick getWhenReady() const |
| { |
| assert(whenReady != MaxTick); |
| return whenReady; |
| } |
| |
| /** |
| * Set tick at which block's data will be available for access. The new |
| * tick must be chronologically sequential with respect to previous |
| * accesses. |
| * |
| * @param tick New data ready tick. |
| */ |
| void setWhenReady(const Tick tick) |
| { |
| assert(tick >= _tickInserted); |
| whenReady = tick; |
| } |
| |
| /** Get the task id associated to this block. */ |
| uint32_t getTaskId() const { return _taskId; } |
| |
| /** Get the requestor id associated to this block. */ |
| uint32_t getSrcRequestorId() const { return _srcRequestorId; } |
| |
| /** Get the number of references to this block since insertion. */ |
| unsigned getRefCount() const { return _refCount; } |
| |
| /** Get the number of references to this block since insertion. */ |
| void increaseRefCount() { _refCount++; } |
| |
| /** |
| * Get the block's age, that is, the number of ticks since its insertion. |
| * |
| * @return The block's age. |
| */ |
| Tick |
| getAge() const |
| { |
| assert(_tickInserted <= curTick()); |
| return curTick() - _tickInserted; |
| } |
| |
| /** |
| * Set member variables when a block insertion occurs. Resets reference |
| * count to 1 (the insertion counts as a reference), and touch block if |
| * it hadn't been touched previously. Sets the insertion tick to the |
| * current tick. Marks the block valid. |
| * |
| * @param tag Block address tag. |
| * @param is_secure Whether the block is in secure space or not. |
| * @param src_requestor_ID The source requestor ID. |
| * @param task_ID The new task ID. |
| */ |
| void insert(const Addr tag, const bool is_secure, |
| const int src_requestor_ID, const uint32_t task_ID); |
| using TaggedEntry::insert; |
| |
| /** |
| * Track the fact that a local locked was issued to the |
| * block. Invalidate any previous LL to the same address. |
| */ |
| void trackLoadLocked(PacketPtr pkt) |
| { |
| assert(pkt->isLLSC()); |
| auto l = lockList.begin(); |
| while (l != lockList.end()) { |
| if (l->intersects(pkt->req)) |
| l = lockList.erase(l); |
| else |
| ++l; |
| } |
| |
| lockList.emplace_front(pkt->req); |
| } |
| |
| /** |
| * Clear the any load lock that intersect the request, and is from |
| * a different context. |
| */ |
| void clearLoadLocks(const RequestPtr &req) |
| { |
| auto l = lockList.begin(); |
| while (l != lockList.end()) { |
| if (l->intersects(req) && l->contextId != req->contextId()) { |
| l = lockList.erase(l); |
| } else { |
| ++l; |
| } |
| } |
| } |
| |
| /** |
| * Pretty-print tag, set and way, and interpret state bits to readable form |
| * including mapping to a MOESI state. |
| * |
| * @return string with basic state information |
| */ |
| std::string |
| print() const override |
| { |
| /** |
| * state M O E S I |
| * writable 1 0 1 0 0 |
| * dirty 1 1 0 0 0 |
| * valid 1 1 1 1 0 |
| * |
| * state writable dirty valid |
| * M 1 1 1 |
| * O 0 1 1 |
| * E 1 0 1 |
| * S 0 0 1 |
| * I 0 0 0 |
| * |
| * Note that only one cache ever has a block in Modified or |
| * Owned state, i.e., only one cache owns the block, or |
| * equivalently has the DirtyBit bit set. However, multiple |
| * caches on the same path to memory can have a block in the |
| * Exclusive state (despite the name). Exclusive means this |
| * cache has the only copy at this level of the hierarchy, |
| * i.e., there may be copies in caches above this cache (in |
| * various states), but there are no peers that have copies on |
| * this branch of the hierarchy, and no caches at or above |
| * this level on any other branch have copies either. |
| **/ |
| unsigned state = |
| isSet(WritableBit) << 2 | isSet(DirtyBit) << 1 | isValid(); |
| char s = '?'; |
| switch (state) { |
| case 0b111: s = 'M'; break; |
| case 0b011: s = 'O'; break; |
| case 0b101: s = 'E'; break; |
| case 0b001: s = 'S'; break; |
| case 0b000: s = 'I'; break; |
| default: s = 'T'; break; // @TODO add other types |
| } |
| return csprintf("state: %x (%c) writable: %d readable: %d " |
| "dirty: %d prefetched: %d | %s", coherence, s, |
| isSet(WritableBit), isSet(ReadableBit), isSet(DirtyBit), |
| wasPrefetched(), TaggedEntry::print()); |
| } |
| |
| /** |
| * Handle interaction of load-locked operations and stores. |
| * @return True if write should proceed, false otherwise. Returns |
| * false only in the case of a failed store conditional. |
| */ |
| bool checkWrite(PacketPtr pkt) |
| { |
| assert(pkt->isWrite()); |
| |
| // common case |
| if (!pkt->isLLSC() && lockList.empty()) |
| return true; |
| |
| const RequestPtr &req = pkt->req; |
| |
| if (pkt->isLLSC()) { |
| // it's a store conditional... have to check for matching |
| // load locked. |
| bool success = false; |
| |
| auto l = lockList.begin(); |
| while (!success && l != lockList.end()) { |
| if (l->matches(pkt->req)) { |
| // it's a store conditional, and as far as the |
| // memory system can tell, the requesting |
| // context's lock is still valid. |
| success = true; |
| lockList.erase(l); |
| } else { |
| ++l; |
| } |
| } |
| |
| req->setExtraData(success ? 1 : 0); |
| // clear any intersected locks from other contexts (our LL |
| // should already have cleared them) |
| clearLoadLocks(req); |
| return success; |
| } else { |
| // a normal write, if there is any lock not from this |
| // context we clear the list, thus for a private cache we |
| // never clear locks on normal writes |
| clearLoadLocks(req); |
| return true; |
| } |
| } |
| |
| protected: |
| /** The current coherence status of this block. @sa CoherenceBits */ |
| unsigned coherence; |
| |
| // The following setters have been marked as protected because their |
| // respective variables should only be modified at 2 moments: |
| // invalidation and insertion. Because of that, they shall only be |
| // called by the functions that perform those actions. |
| |
| /** Set the task id value. */ |
| void setTaskId(const uint32_t task_id) { _taskId = task_id; } |
| |
| /** Set the source requestor id. */ |
| void setSrcRequestorId(const uint32_t id) { _srcRequestorId = id; } |
| |
| /** Set the number of references to this block since insertion. */ |
| void setRefCount(const unsigned count) { _refCount = count; } |
| |
| /** Set the current tick as this block's insertion tick. */ |
| void setTickInserted() { _tickInserted = curTick(); } |
| |
| private: |
| /** Task Id associated with this block */ |
| uint32_t _taskId; |
| |
| /** holds the source requestor ID for this block. */ |
| int _srcRequestorId; |
| |
| /** Number of references to this block since it was brought in. */ |
| unsigned _refCount; |
| |
| /** |
| * Tick on which the block was inserted in the cache. Its value is only |
| * meaningful if the block is valid. |
| */ |
| Tick _tickInserted; |
| |
| /** Whether this block is an unaccessed hardware prefetch. */ |
| bool _prefetched; |
| }; |
| |
| /** |
| * Special instance of CacheBlk for use with tempBlk that deals with its |
| * block address regeneration. |
| * @sa Cache |
| */ |
| class TempCacheBlk final : public CacheBlk |
| { |
| private: |
| /** |
| * Copy of the block's address, used to regenerate tempBlock's address. |
| */ |
| Addr _addr; |
| |
| public: |
| /** |
| * Creates a temporary cache block, with its own storage. |
| * @param size The size (in bytes) of this cache block. |
| */ |
| TempCacheBlk(unsigned size) : CacheBlk() |
| { |
| data = new uint8_t[size]; |
| } |
| TempCacheBlk(const TempCacheBlk&) = delete; |
| TempCacheBlk& operator=(const TempCacheBlk&) = delete; |
| ~TempCacheBlk() { delete [] data; }; |
| |
| /** |
| * Invalidate the block and clear all state. |
| */ |
| void invalidate() override { |
| CacheBlk::invalidate(); |
| |
| _addr = MaxAddr; |
| } |
| |
| void |
| insert(const Addr addr, const bool is_secure) override |
| { |
| CacheBlk::insert(addr, is_secure); |
| _addr = addr; |
| } |
| |
| /** |
| * Get block's address. |
| * |
| * @return addr Address value. |
| */ |
| Addr getAddr() const |
| { |
| return _addr; |
| } |
| }; |
| |
| /** |
| * Simple class to provide virtual print() method on cache blocks |
| * without allocating a vtable pointer for every single cache block. |
| * Just wrap the CacheBlk object in an instance of this before passing |
| * to a function that requires a Printable object. |
| */ |
| class CacheBlkPrintWrapper : public Printable |
| { |
| CacheBlk *blk; |
| public: |
| CacheBlkPrintWrapper(CacheBlk *_blk) : blk(_blk) {} |
| virtual ~CacheBlkPrintWrapper() {} |
| void print(std::ostream &o, int verbosity = 0, |
| const std::string &prefix = "") const; |
| }; |
| |
| } // namespace gem5 |
| |
| #endif //__MEM_CACHE_CACHE_BLK_HH__ |