X-Git-Url: https://git.libre-soc.org/?a=blobdiff_plain;ds=sidebyside;f=src%2Fmem%2Fcache%2Fmshr.hh;h=40eb970c0f413402650ae68f82b06f82effb5deb;hb=fc0678daf202b9e7d7e4280389ce81abe00b8700;hp=bed7012b069aba3980f842102968af74654bb5c7;hpb=89a7fb03934b3e38c7d8b2c4818794b3ec874fdf;p=gem5.git diff --git a/src/mem/cache/mshr.hh b/src/mem/cache/mshr.hh index bed7012b0..40eb970c0 100644 --- a/src/mem/cache/mshr.hh +++ b/src/mem/cache/mshr.hh @@ -1,4 +1,16 @@ /* + * Copyright (c) 2012-2013, 2015-2016, 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) 2002-2005 The Regents of The University of Michigan * All rights reserved. * @@ -26,6 +38,7 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * Authors: Erik Hallnor + * Nikos Nikoleris */ /** @@ -33,27 +46,85 @@ * Miss Status and Handling Register (MSHR) declaration. */ -#ifndef __MSHR_HH__ -#define __MSHR_HH__ +#ifndef __MEM_CACHE_MSHR_HH__ +#define __MEM_CACHE_MSHR_HH__ +#include +#include #include +#include +#include #include "base/printable.hh" +#include "base/types.hh" +#include "mem/cache/queue_entry.hh" #include "mem/packet.hh" +#include "mem/request.hh" +#include "sim/core.hh" -class CacheBlk; -class MSHRQueue; +class BaseCache; /** * Miss Status and handling Register. This class keeps all the information * needed to handle a cache miss including a list of target requests. + * @sa \ref gem5MemorySystem "gem5 Memory System" */ -class MSHR : public Packet::SenderState, public Printable +class MSHR : public QueueEntry, public Printable { + /** + * Consider the queues friends to avoid making everything public. + */ + template + friend class Queue; + friend class MSHRQueue; + + private: + + /** Flag set by downstream caches */ + bool downstreamPending; + + /** + * Here we use one flag to track both if: + * + * 1. We are going to become owner or not, i.e., we will get the + * block in an ownership state (Owned or Modified) with BlkDirty + * set. This determines whether or not we are going to become the + * responder and ordering point for future requests that we snoop. + * + * 2. We know that we are going to get a writable block, i.e. we + * will get the block in writable state (Exclusive or Modified + * state) with BlkWritable set. That determines whether additional + * targets with needsWritable set will be able to be satisfied, or + * if not should be put on the deferred list to possibly wait for + * another request that does give us writable access. + * + * Condition 2 is actually just a shortcut that saves us from + * possibly building a deferred target list and calling + * promoteWritable() every time we get a writable block. Condition + * 1, tracking ownership, is what is important. However, we never + * receive ownership without marking the block dirty, and + * consequently use pendingModified to track both ownership and + * writability rather than having separate pendingDirty and + * pendingWritable flags. + */ + bool pendingModified; + + /** Did we snoop an invalidate while waiting for data? */ + bool postInvalidate; + + /** Did we snoop a read while waiting for data? */ + bool postDowngrade; + public: - class Target { + /** Track if we sent this as a whole line write or not */ + bool wasWholeLineWrite; + + /** True if the entry is just a simple forward from an upper level */ + bool isForward; + + class Target : public QueueEntry::Target { public: enum Source { @@ -62,90 +133,260 @@ class MSHR : public Packet::SenderState, public Printable FromPrefetcher }; - Tick recvTime; //!< Time when request was received (for stats) - Tick readyTime; //!< Time when request is ready to be serviced - Counter order; //!< Global order (for memory consistency mgmt) - PacketPtr pkt; //!< Pending request packet. - Source source; //!< Did request come from cpu, memory, or prefetcher? - bool markedPending; //!< Did we mark upstream MSHR - //!< as downstreamPending? + const Source source; //!< Request from cpu, memory, or prefetcher? + + /** + * We use this flag to track whether we have cleared the + * downstreamPending flag for the MSHR of the cache above + * where this packet originates from and guard noninitial + * attempts to clear it. + * + * The flag markedPending needs to be updated when the + * TargetList is in service which can be: + * 1) during the Target instantiation if the MSHR is in + * service and the target is not deferred, + * 2) when the MSHR becomes in service if the target is not + * deferred, + * 3) or when the TargetList is promoted (deferredTargets -> + * targets). + */ + bool markedPending; + + const bool allocOnFill; //!< Should the response servicing this + //!< target list allocate in the cache? Target(PacketPtr _pkt, Tick _readyTime, Counter _order, - Source _source, bool _markedPending) - : recvTime(curTick), readyTime(_readyTime), order(_order), - pkt(_pkt), source(_source), markedPending(_markedPending) + Source _source, bool _markedPending, bool alloc_on_fill) + : QueueEntry::Target(_pkt, _readyTime, _order), source(_source), + markedPending(_markedPending), allocOnFill(alloc_on_fill) {} }; class TargetList : public std::list { - /** Target list iterator. */ - typedef std::list::iterator Iterator; - typedef std::list::const_iterator ConstIterator; public: - bool needsExclusive; + bool needsWritable; bool hasUpgrade; + /** Set when the response should allocate on fill */ + bool allocOnFill; + /** + * Determine whether there was at least one non-snooping + * target coming from another cache. + */ + bool hasFromCache; TargetList(); - void resetFlags() { needsExclusive = hasUpgrade = false; } - bool isReset() { return !needsExclusive && !hasUpgrade; } + + /** + * Use the provided packet and the source to update the + * flags of this TargetList. + * + * @param pkt Packet considered for the flag update + * @param source Indicates the source of the packet + * @param alloc_on_fill Whether the pkt would allocate on a fill + */ + void updateFlags(PacketPtr pkt, Target::Source source, + bool alloc_on_fill); + + /** + * Reset state + * + * @param blk_addr Address of the cache block + * @param blk_size Size of the cache block + */ + void init(Addr blk_addr, Addr blk_size) { + blkAddr = blk_addr; + blkSize = blk_size; + writesBitmap.resize(blk_size); + + resetFlags(); + } + + void resetFlags() { + canMergeWrites = true; + std::fill(writesBitmap.begin(), writesBitmap.end(), false); + + needsWritable = false; + hasUpgrade = false; + allocOnFill = false; + hasFromCache = false; + } + + /** + * Goes through the list of targets and uses them to populate + * the flags of this TargetList. When the function returns the + * flags are consistent with the properties of packets in the + * list. + */ + void populateFlags(); + + /** + * Add the specified packet in the TargetList. This function + * stores information related to the added packet and updates + * accordingly the flags. + * + * @param pkt Packet considered for adding + */ + void + updateWriteFlags(PacketPtr pkt) + { + // if we have already seen writes for the full block stop + // here, this might be a full line write followed by + // other compatible requests (e.g., reads) + if (!isWholeLineWrite()) { + // Avoid merging requests with special flags (e.g., + // strictly ordered) + const Request::FlagsType no_merge_flags = + Request::UNCACHEABLE | Request::STRICT_ORDER | + Request::MMAPPED_IPR | Request::PRIVILEGED | + Request::LLSC | Request::MEM_SWAP | + Request::MEM_SWAP_COND | Request::SECURE; + const auto &req_flags = pkt->req->getFlags(); + bool compat_write = pkt->isWrite() && + !req_flags.isSet(no_merge_flags); + canMergeWrites &= compat_write; + + // if this request is the first target in this list + // and additionally a whole-line write, we need to + // service it as a whole-line even if we won't allow + // any further merging (e.g., SECURE whole line + // write). + bool first_write = pkt->isWrite() && (size() == 0); + if (first_write || compat_write) { + auto offset = pkt->getOffset(blkSize); + auto begin = writesBitmap.begin() + offset; + std::fill(begin, begin + pkt->getSize(), true); + } + } + } + + /** + * Tests if the flags of this TargetList have their default + * values. + * + * @return True if the TargetList are reset, false otherwise. + */ + bool isReset() const { + return !needsWritable && !hasUpgrade && !allocOnFill && + !hasFromCache && canMergeWrites; + } + + /** + * Add the specified packet in the TargetList. This function + * stores information related to the added packet and updates + * accordingly the flags. + * + * @param pkt Packet considered for adding + * @param readTime Tick at which the packet is processed by this cache + * @param order A counter giving a unique id to each target + * @param source Indicates the source agent of the packet + * @param markPending Set for deferred targets or pending MSHRs + * @param alloc_on_fill Whether it should allocate on a fill + */ void add(PacketPtr pkt, Tick readyTime, Counter order, - Target::Source source, bool markPending); + Target::Source source, bool markPending, bool alloc_on_fill); + + /** + * Convert upgrades to the equivalent request if the cache line they + * refer to would have been invalid (Upgrade -> ReadEx, SC* -> Fail). + * Used to rejig ordering between targets waiting on an MSHR. */ void replaceUpgrades(); + void clearDownstreamPending(); - bool checkFunctional(PacketPtr pkt); + void clearDownstreamPending(iterator begin, iterator end); + bool trySatisfyFunctional(PacketPtr pkt); void print(std::ostream &os, int verbosity, const std::string &prefix) const; + + /** + * Check if this list contains writes that cover an entire + * cache line. This is used as part of the miss-packet + * creation. Note that new requests may arrive after a + * miss-packet has been created, and for the corresponding + * fill we use the wasWholeLineWrite field. + */ + bool isWholeLineWrite() const + { + return std::all_of(writesBitmap.begin(), writesBitmap.end(), + [](bool i) { return i; }); + } + + private: + /** Address of the cache block for this list of targets. */ + Addr blkAddr; + + /** Size of the cache block. */ + Addr blkSize; + + /** Indicates whether we can merge incoming write requests */ + bool canMergeWrites; + + // NOTE: std::vector might not meet satisfy the + // ForwardIterator requirement and therefore cannot be used + // for writesBitmap. + /** + * Track which bytes are written by requests in this target + * list. + */ + std::vector writesBitmap; }; /** A list of MSHRs. */ typedef std::list List; /** MSHR list iterator. */ typedef List::iterator Iterator; - /** MSHR list const_iterator. */ - typedef List::const_iterator ConstIterator; - - /** Pointer to queue containing this MSHR. */ - MSHRQueue *queue; - - /** Cycle when ready to issue */ - Tick readyTime; - /** Order number assigned by the miss queue. */ - Counter order; - - /** Address of the request. */ - Addr addr; - - /** Size of the request. */ - int size; + /** The pending* and post* flags are only valid if inService is + * true. Using the accessor functions lets us detect if these + * flags are accessed improperly. + */ - /** True if the request has been sent to the bus. */ - bool inService; + /** True if we need to get a writable copy of the block. */ + bool needsWritable() const { return targets.needsWritable; } - /** True if the request is just a simple forward from an upper level */ - bool isForward; + bool isCleaning() const { + PacketPtr pkt = targets.front().pkt; + return pkt->isClean(); + } - /** True if we need to get an exclusive copy of the block. */ - bool needsExclusive() const { return targets->needsExclusive; } + bool isPendingModified() const { + assert(inService); return pendingModified; + } - /** True if the request is uncacheable */ - bool _isUncacheable; + bool hasPostInvalidate() const { + assert(inService); return postInvalidate; + } - bool downstreamPending; + bool hasPostDowngrade() const { + assert(inService); return postDowngrade; + } - bool pendingInvalidate; - bool pendingShared; + bool sendPacket(BaseCache &cache) override; - /** Thread number of the miss. */ - short threadNum; - /** The number of currently allocated targets. */ - short ntargets; + bool allocOnFill() const { + return targets.allocOnFill; + } + /** + * Determine if there are non-deferred requests from other caches + * + * @return true if any of the targets is from another cache + */ + bool hasFromCache() const { + return targets.hasFromCache; + } - /** Data buffer (if needed). Currently used only for pending - * upgrade handling. */ - uint8_t *data; + private: + /** + * Promotes deferred targets that satisfy a predicate + * + * Deferred targets are promoted to the target list if they + * satisfy a given condition. The operation stops at the first + * deferred target that doesn't satisfy the condition. + * + * @param pred A condition on a Target + */ + void promoteIf(const std::function& pred); /** * Pointer to this MSHR on the ready list. @@ -159,28 +400,36 @@ class MSHR : public Packet::SenderState, public Printable */ Iterator allocIter; -private: /** List of all requests that match the address */ - TargetList *targets; + TargetList targets; - TargetList *deferredTargets; + TargetList deferredTargets; -public: - - bool isUncacheable() { return _isUncacheable; } + public: + /** + * Check if this MSHR contains only compatible writes, and if they + * span the entire cache line. This is used as part of the + * miss-packet creation. Note that new requests may arrive after a + * miss-packet has been created, and for the fill we therefore use + * the wasWholeLineWrite field. + */ + bool isWholeLineWrite() const { + return targets.isWholeLineWrite(); + } /** * Allocate a miss to this MSHR. - * @param cmd The requesting command. - * @param addr The address of the miss. - * @param asid The address space id of the miss. - * @param size The number of bytes to request. - * @param pkt The original miss. + * @param blk_addr The address of the block. + * @param blk_size The number of bytes to request. + * @param pkt The original miss. + * @param when_ready When should the MSHR be ready to act upon. + * @param _order The logical order of this MSHR + * @param alloc_on_fill Should the cache allocate a block on fill */ - void allocate(Addr addr, int size, PacketPtr pkt, - Tick when, Counter _order); + void allocate(Addr blk_addr, unsigned blk_size, PacketPtr pkt, + Tick when_ready, Counter _order, bool alloc_on_fill); - bool markInService(); + void markInService(bool pending_modified_resp); void clearDownstreamPending(); @@ -193,40 +442,48 @@ public: * Add a request to the list of targets. * @param target The target. */ - void allocateTarget(PacketPtr target, Tick when, Counter order); + void allocateTarget(PacketPtr target, Tick when, Counter order, + bool alloc_on_fill); bool handleSnoop(PacketPtr target, Counter order); /** A simple constructor. */ MSHR(); - /** A simple destructor. */ - ~MSHR(); /** * Returns the current number of allocated targets. * @return The current number of allocated targets. */ - int getNumTargets() const { return ntargets; } + int getNumTargets() const + { return targets.size() + deferredTargets.size(); } /** - * Returns a pointer to the target list. - * @return a pointer to the target list. + * Extracts the subset of the targets that can be serviced given a + * received response. This function returns the targets list + * unless the response is a ReadRespWithInvalidate. The + * ReadRespWithInvalidate is only invalidating response that its + * invalidation was not expected when the request (a + * ReadSharedReq) was sent out. For ReadRespWithInvalidate we can + * safely service only the first FromCPU target and all FromSnoop + * targets (inform all snoopers that we no longer have the block). + * + * @param pkt The response from the downstream memory */ - TargetList *getTargetList() { return targets; } + TargetList extractServiceableTargets(PacketPtr pkt); /** * Returns true if there are targets left. * @return true if there are targets */ - bool hasTargets() const { return !targets->empty(); } + bool hasTargets() const { return !targets.empty(); } /** * Returns a reference to the first target. * @return A pointer to the first target. */ - Target *getTarget() const + QueueEntry::Target *getTarget() override { assert(hasTargets()); - return &targets->front(); + return &targets.front(); } /** @@ -234,30 +491,59 @@ public: */ void popTarget() { - --ntargets; - targets->pop_front(); - } - - bool isForwardNoResponse() const - { - if (getNumTargets() != 1) - return false; - Target *tgt = getTarget(); - return tgt->source == Target::FromCPU && !tgt->pkt->needsResponse(); + targets.pop_front(); } bool promoteDeferredTargets(); - void handleFill(Packet *pkt, CacheBlk *blk); + /** + * Promotes deferred targets that do not require writable + * + * Move targets from the deferred targets list to the target list + * starting from the first deferred target until the first target + * that is a cache maintenance operation or needs a writable copy + * of the block + */ + void promoteReadable(); + + /** + * Promotes deferred targets that do not require writable + * + * Requests in the deferred target list are moved to the target + * list up until the first target that is a cache maintenance + * operation or needs a writable copy of the block + */ + void promoteWritable(); + + bool trySatisfyFunctional(PacketPtr pkt); - bool checkFunctional(PacketPtr pkt); + /** + * Adds a delay relative to the current tick to the current MSHR + * @param delay_ticks the desired delay in ticks + */ + void delay(Tick delay_ticks) + { + assert(readyTime <= curTick()); + readyTime = curTick() + delay_ticks; + } /** * Prints the contents of this MSHR for debugging. */ void print(std::ostream &os, int verbosity = 0, - const std::string &prefix = "") const; + const std::string &prefix = "") const override; + /** + * A no-args wrapper of print(std::ostream...) meant to be + * invoked from DPRINTFs avoiding string overheads in fast mode + * + * @return string with mshr fields + [deferred]targets + */ + std::string print() const; + + bool matchBlockAddr(const Addr addr, const bool is_secure) const override; + bool matchBlockAddr(const PacketPtr pkt) const override; + bool conflictAddr(const QueueEntry* entry) const override; }; -#endif //__MSHR_HH__ +#endif // __MEM_CACHE_MSHR_HH__