#include "debug/Cache.hh"
#include "debug/CachePort.hh"
#include "enums/Clusivity.hh"
-#include "mem/cache/blk.hh"
+#include "mem/cache/cache_blk.hh"
#include "mem/cache/mshr_queue.hh"
#include "mem/cache/tags/base.hh"
#include "mem/cache/write_queue.hh"
#include "mem/packet_queue.hh"
#include "mem/qport.hh"
#include "mem/request.hh"
+#include "params/WriteAllocator.hh"
#include "sim/eventq.hh"
#include "sim/serialize.hh"
#include "sim/sim_exit.hh"
*/
const bool prefetchOnAccess;
+ /**
+ * The writeAllocator drive optimizations for streaming writes.
+ * It first determines whether a WriteReq MSHR should be delayed,
+ * thus ensuring that we wait longer in cases when we are write
+ * coalescing and allowing all the bytes of the line to be written
+ * before the MSHR packet is sent downstream. This works in unison
+ * with the tracking in the MSHR to check if the entire line is
+ * written. The write mode also affects the behaviour on filling
+ * any whole-line writes. Normally the cache allocates the line
+ * when receiving the InvalidateResp, but after seeing enough
+ * consecutive lines we switch to using the tempBlock, and thus
+ * end up not allocating the line, and instead turning the
+ * whole-line write into a writeback straight away.
+ */
+ WriteAllocator * const writeAllocator;
+
/**
* Temporary cache block for occasional transitory use. We use
* the tempBlock to fill when allocation fails (e.g., when there
* is an outstanding request that accesses the victim block) or
* when we want to avoid allocation (e.g., exclusive caches)
*/
- CacheBlk *tempBlock;
+ TempCacheBlk *tempBlock;
/**
* Upstream caches need this packet until true is returned, so
cmd.isLLSC();
}
+ /**
+ * Regenerate block address using tags.
+ * Block address regeneration depends on whether we're using a temporary
+ * block or not.
+ *
+ * @param blk The block to regenerate address.
+ * @return The block's address.
+ */
+ Addr regenerateBlkAddr(CacheBlk* blk);
+
/**
* Does all the processing necessary to perform the provided request.
* @param pkt The memory request to perform.
* @param writebacks A list with packets for any performed writebacks
* @return Cycles for handling the request
*/
- virtual Cycles handleAtomicReqMiss(PacketPtr pkt, CacheBlk *blk,
+ virtual Cycles handleAtomicReqMiss(PacketPtr pkt, CacheBlk *&blk,
PacketList &writebacks) = 0;
/**
* @param blk The referenced block, can be nullptr.
* @param needs_writable Indicates that the block must be writable
* even if the request in cpu_pkt doesn't indicate that.
+ * @param is_whole_line_write True if there are writes for the
+ * whole line
* @return A packet send to the memory below
*/
virtual PacketPtr createMissPacket(PacketPtr cpu_pkt, CacheBlk *blk,
- bool needs_writable) const = 0;
+ bool needs_writable,
+ bool is_whole_line_write) const = 0;
/**
* Determine if clean lines should be written back or not. In
*
* Find a victim block and if necessary prepare writebacks for any
* existing data. May return nullptr if there are no replaceable
- * blocks.
+ * blocks. If a replaceable block is found, it inserts the new block in
+ * its place. The new block, however, is not set as valid yet.
*
- * @param addr Physical address of the new block
- * @param is_secure Set if the block should be secure
+ * @param pkt Packet holding the address to update
* @param writebacks A list of writeback packets for the evicted blocks
* @return the allocated block
*/
- CacheBlk *allocateBlock(Addr addr, bool is_secure, PacketList &writebacks);
+ CacheBlk *allocateBlock(const PacketPtr pkt, PacketList &writebacks);
/**
* Evict a cache block.
*
* @param blk Block to invalidate
* @param writebacks Return a list of packets with writebacks
*/
- virtual void evictBlock(CacheBlk *blk, PacketList &writebacks) = 0;
+ void evictBlock(CacheBlk *blk, PacketList &writebacks);
/**
* Invalidate a cache block.
/**
* Cache block visitor that writes back dirty cache blocks using
* functional writes.
- *
- * @return Always returns true.
*/
- bool writebackVisitor(CacheBlk &blk);
+ void writebackVisitor(CacheBlk &blk);
/**
* Cache block visitor that invalidates all blocks in the cache.
*
* @warn Dirty cache lines will not be written back to memory.
- *
- * @return Always returns true.
*/
- bool invalidateVisitor(CacheBlk &blk);
+ void invalidateVisitor(CacheBlk &blk);
/**
* Take an MSHR, turn it into a suitable downstream packet, and
};
/**
- * Wrap a method and present it as a cache block visitor.
+ * The write allocator inspects write packets and detects streaming
+ * patterns. The write allocator supports a single stream where writes
+ * are expected to access consecutive locations and keeps track of
+ * size of the area covered by the concecutive writes in byteCount.
+ *
+ * 1) When byteCount has surpassed the coallesceLimit the mode
+ * switches from ALLOCATE to COALESCE where writes should be delayed
+ * until the whole block is written at which point a single packet
+ * (whole line write) can service them.
*
- * For example the forEachBlk method in the tag arrays expects a
- * callable object/function as their parameter. This class wraps a
- * method in an object and presents callable object that adheres to
- * the cache block visitor protocol.
+ * 2) When byteCount has also exceeded the noAllocateLimit (whole
+ * line) we switch to NO_ALLOCATE when writes should not allocate in
+ * the cache but rather send a whole line write to the memory below.
*/
-class CacheBlkVisitorWrapper : public CacheBlkVisitor
-{
+class WriteAllocator : public SimObject {
public:
- typedef bool (BaseCache::*VisitorPtr)(CacheBlk &blk);
-
- CacheBlkVisitorWrapper(BaseCache &_cache, VisitorPtr _visitor)
- : cache(_cache), visitor(_visitor) {}
+ WriteAllocator(const WriteAllocatorParams *p) :
+ SimObject(p),
+ coalesceLimit(p->coalesce_limit * p->block_size),
+ noAllocateLimit(p->no_allocate_limit * p->block_size),
+ delayThreshold(p->delay_threshold)
+ {
+ reset();
+ }
- bool operator()(CacheBlk &blk) override {
- return (cache.*visitor)(blk);
+ /**
+ * Should writes be coalesced? This is true if the mode is set to
+ * NO_ALLOCATE.
+ *
+ * @return return true if the cache should coalesce writes.
+ */
+ bool coalesce() const {
+ return mode != WriteMode::ALLOCATE;
}
- private:
- BaseCache &cache;
- VisitorPtr visitor;
-};
+ /**
+ * Should writes allocate?
+ *
+ * @return return true if the cache should not allocate for writes.
+ */
+ bool allocate() const {
+ return mode != WriteMode::NO_ALLOCATE;
+ }
-/**
- * Cache block visitor that determines if there are dirty blocks in a
- * cache.
- *
- * Use with the forEachBlk method in the tag array to determine if the
- * array contains dirty blocks.
- */
-class CacheBlkIsDirtyVisitor : public CacheBlkVisitor
-{
- public:
- CacheBlkIsDirtyVisitor()
- : _isDirty(false) {}
+ /**
+ * Reset the write allocator state, meaning that it allocates for
+ * writes and has not recorded any information about qualifying
+ * writes that might trigger a switch to coalescing and later no
+ * allocation.
+ */
+ void reset() {
+ mode = WriteMode::ALLOCATE;
+ byteCount = 0;
+ nextAddr = 0;
+ }
- bool operator()(CacheBlk &blk) override {
- if (blk.isDirty()) {
- _isDirty = true;
- return false;
- } else {
+ /**
+ * Access whether we need to delay the current write.
+ *
+ * @param blk_addr The block address the packet writes to
+ * @return true if the current packet should be delayed
+ */
+ bool delay(Addr blk_addr) {
+ if (delayCtr[blk_addr] > 0) {
+ --delayCtr[blk_addr];
return true;
+ } else {
+ return false;
}
}
/**
- * Does the array contain a dirty line?
+ * Clear delay counter for the input block
*
- * @return true if yes, false otherwise.
+ * @param blk_addr The accessed cache block
*/
- bool isDirty() const { return _isDirty; };
+ void resetDelay(Addr blk_addr) {
+ delayCtr.erase(blk_addr);
+ }
+
+ /**
+ * Update the write mode based on the current write
+ * packet. This method compares the packet's address with any
+ * current stream, and updates the tracking and the mode
+ * accordingly.
+ *
+ * @param write_addr Start address of the write request
+ * @param write_size Size of the write request
+ * @param blk_addr The block address that this packet writes to
+ */
+ void updateMode(Addr write_addr, unsigned write_size, Addr blk_addr);
private:
- bool _isDirty;
+ /**
+ * The current mode for write coalescing and allocation, either
+ * normal operation (ALLOCATE), write coalescing (COALESCE), or
+ * write coalescing without allocation (NO_ALLOCATE).
+ */
+ enum class WriteMode : char {
+ ALLOCATE,
+ COALESCE,
+ NO_ALLOCATE,
+ };
+ WriteMode mode;
+
+ /** Address to match writes against to detect streams. */
+ Addr nextAddr;
+
+ /**
+ * Bytes written contiguously. Saturating once we no longer
+ * allocate.
+ */
+ uint32_t byteCount;
+
+ /**
+ * Limits for when to switch between the different write modes.
+ */
+ const uint32_t coalesceLimit;
+ const uint32_t noAllocateLimit;
+ /**
+ * The number of times the allocator will delay an WriteReq MSHR.
+ */
+ const uint32_t delayThreshold;
+
+ /**
+ * Keep track of the number of times the allocator has delayed an
+ * WriteReq MSHR.
+ */
+ std::unordered_map<Addr, Counter> delayCtr;
};
#endif //__MEM_CACHE_BASE_HH__