X-Git-Url: https://git.libre-soc.org/?a=blobdiff_plain;f=src%2Fmem%2Fcache%2Fbase.hh;h=4b89be967f916c9b6a4687b556ee6b6ff7ae7b3f;hb=0054f1ad53f00621df82d201b38c03f088786d27;hp=d1ddedbd4534c436ac592e9c8c7f993b8e91303e;hpb=39a055645f77e0fa7bf49406635dba6bd65e361f;p=gem5.git diff --git a/src/mem/cache/base.hh b/src/mem/cache/base.hh index d1ddedbd4..4b89be967 100644 --- a/src/mem/cache/base.hh +++ b/src/mem/cache/base.hh @@ -1,4 +1,16 @@ /* + * Copyright (c) 2012-2013, 2015-2016 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) 2003-2005 The Regents of The University of Michigan * All rights reserved. * @@ -35,8 +47,8 @@ * Declares a basic cache interface BaseCache. */ -#ifndef __BASE_CACHE_HH__ -#define __BASE_CACHE_HH__ +#ifndef __MEM_CACHE_BASE_HH__ +#define __MEM_CACHE_BASE_HH__ #include #include @@ -47,22 +59,26 @@ #include "base/statistics.hh" #include "base/trace.hh" #include "base/types.hh" -#include "config/full_system.hh" +#include "debug/Cache.hh" +#include "debug/CachePort.hh" #include "mem/cache/mshr_queue.hh" +#include "mem/cache/write_queue.hh" #include "mem/mem_object.hh" #include "mem/packet.hh" +#include "mem/qport.hh" #include "mem/request.hh" -#include "mem/tport.hh" #include "params/BaseCache.hh" #include "sim/eventq.hh" +#include "sim/full_system.hh" #include "sim/sim_exit.hh" +#include "sim/system.hh" -class MSHR; /** * A basic cache interface. Implements some common functions for speed. */ class BaseCache : public MemObject { + protected: /** * Indexes to enumerate the MSHR queues. */ @@ -71,6 +87,7 @@ class BaseCache : public MemObject MSHRQueue_WriteBuffer }; + public: /** * Reasons for caches to be blocked. */ @@ -81,70 +98,92 @@ class BaseCache : public MemObject NUM_BLOCKED_CAUSES }; - public: + protected: + /** - * Reasons for cache to request a bus. + * A cache master port is used for the memory-side port of the + * cache, and in addition to the basic timing port that only sends + * response packets through a transmit list, it also offers the + * ability to schedule and send request packets (requests & + * writebacks). The send event is scheduled through schedSendEvent, + * and the sendDeferredPacket of the timing port is modified to + * consider both the transmit list and the requests from the MSHR. */ - enum RequestCause { - Request_MSHR = MSHRQueue_MSHRs, - Request_WB = MSHRQueue_WriteBuffer, - Request_PF, - NUM_REQUEST_CAUSES - }; - - private: - - class CachePort : public SimpleTimingPort + class CacheMasterPort : public QueuedMasterPort { - public: - BaseCache *cache; - protected: - CachePort(const std::string &_name, BaseCache *_cache, - const std::string &_label); - - virtual void recvStatusChange(Status status); + public: - virtual unsigned deviceBlockSize() const; + /** + * Schedule a send of a request packet (from the MSHR). Note + * that we could already have a retry outstanding. + */ + void schedSendEvent(Tick time) + { + DPRINTF(CachePort, "Scheduling send event at %llu\n", time); + reqQueue.schedSendEvent(time); + } - bool recvRetryCommon(); + protected: - typedef EventWrapper - SendRetryEvent; + CacheMasterPort(const std::string &_name, BaseCache *_cache, + ReqPacketQueue &_reqQueue, + SnoopRespPacketQueue &_snoopRespQueue) : + QueuedMasterPort(_name, _cache, _reqQueue, _snoopRespQueue) + { } + + /** + * Memory-side port always snoops. + * + * @return always true + */ + virtual bool isSnooping() const { return true; } + }; - const std::string label; + /** + * A cache slave port is used for the CPU-side port of the cache, + * and it is basically a simple timing port that uses a transmit + * list for responses to the CPU (or connected master). In + * addition, it has the functionality to block the port for + * incoming requests. If blocked, the port will issue a retry once + * unblocked. + */ + class CacheSlavePort : public QueuedSlavePort + { public: - void setOtherPort(CachePort *_otherPort) { otherPort = _otherPort; } + /** Do not accept any new requests. */ void setBlocked(); + /** Return to normal operation and accept new requests. */ void clearBlocked(); - bool checkFunctional(PacketPtr pkt); + bool isBlocked() const { return blocked; } + + protected: + + CacheSlavePort(const std::string &_name, BaseCache *_cache, + const std::string &_label); - CachePort *otherPort; + /** A normal packet queue used to store responses. */ + RespPacketQueue queue; bool blocked; bool mustSendRetry; - void requestBus(RequestCause cause, Tick time) - { - DPRINTF(CachePort, "Asserting bus request for cause %d\n", cause); - if (!waitingOnRetry) { - schedSendEvent(time); - } - } + private: + + void processSendRetry(); + + EventWrapper sendRetryEvent; - void respond(PacketPtr pkt, Tick time) { - schedSendTiming(pkt, time); - } }; - public: //Made public so coherence can get at it. - CachePort *cpuSidePort; - CachePort *memSidePort; + CacheSlavePort *cpuSidePort; + CacheMasterPort *memSidePort; protected: @@ -152,41 +191,101 @@ class BaseCache : public MemObject MSHRQueue mshrQueue; /** Write/writeback buffer */ - MSHRQueue writeBuffer; + WriteQueue writeBuffer; - MSHR *allocateBufferInternal(MSHRQueue *mq, Addr addr, int size, - PacketPtr pkt, Tick time, bool requestBus) + /** + * Mark a request as in service (sent downstream in the memory + * system), effectively making this MSHR the ordering point. + */ + void markInService(MSHR *mshr, bool pending_modified_resp) { - MSHR *mshr = mq->allocate(addr, size, pkt, time, order++); - - if (mq->isFull()) { - setBlocked((BlockedCause)mq->index); - } + bool wasFull = mshrQueue.isFull(); + mshrQueue.markInService(mshr, pending_modified_resp); - if (requestBus) { - requestMemSideBus((RequestCause)mq->index, time); + if (wasFull && !mshrQueue.isFull()) { + clearBlocked(Blocked_NoMSHRs); } - - return mshr; } - void markInServiceInternal(MSHR *mshr, PacketPtr pkt) + void markInService(WriteQueueEntry *entry) { - MSHRQueue *mq = mshr->queue; - bool wasFull = mq->isFull(); - mq->markInService(mshr, pkt); - if (wasFull && !mq->isFull()) { - clearBlocked((BlockedCause)mq->index); + bool wasFull = writeBuffer.isFull(); + writeBuffer.markInService(entry); + + if (wasFull && !writeBuffer.isFull()) { + clearBlocked(Blocked_NoWBBuffers); } } + /** + * Determine if we should allocate on a fill or not. + * + * @param cmd Packet command being added as an MSHR target + * + * @return Whether we should allocate on a fill or not + */ + virtual bool allocOnFill(MemCmd cmd) const = 0; + + /** + * Write back dirty blocks in the cache using functional accesses. + */ + virtual void memWriteback() = 0; + /** + * Invalidates all blocks in the cache. + * + * @warn Dirty cache lines will not be written back to + * memory. Make sure to call functionalWriteback() first if you + * want the to write them to memory. + */ + virtual void memInvalidate() = 0; + /** + * Determine if there are any dirty blocks in the cache. + * + * \return true if at least one block is dirty, false otherwise. + */ + virtual bool isDirty() const = 0; + + /** + * Determine if an address is in the ranges covered by this + * cache. This is useful to filter snoops. + * + * @param addr Address to check against + * + * @return If the address in question is in range + */ + bool inRange(Addr addr) const; + /** Block size of this cache */ const unsigned blkSize; /** - * The latency of a hit in this device. + * The latency of tag lookup of a cache. It occurs when there is + * an access to the cache. + */ + const Cycles lookupLatency; + + /** + * The latency of data access of a cache. It occurs when there is + * an access to the cache. + */ + const Cycles dataLatency; + + /** + * This is the forward latency of the cache. It occurs when there + * is a cache miss and a request is forwarded downstream, in + * particular an outbound miss. + */ + const Cycles forwardLatency; + + /** The latency to fill a cache block */ + const Cycles fillLatency; + + /** + * The latency of sending reponse to its upper level cache/core on + * a linefill. The responseLatency parameter captures this + * latency. */ - int hitLatency; + const Cycles responseLatency; /** The number of targets for each MSHR. */ const int numTarget; @@ -194,10 +293,13 @@ class BaseCache : public MemObject /** Do we forward snoops from mem side port through to cpu side port? */ bool forwardSnoops; - /** Is this cache a toplevel cache (e.g. L1, I/O cache). If so we should - * never try to forward ownership and similar optimizations to the cpu - * side */ - bool isTopLevel; + /** + * Is this cache read only, for example the instruction cache, or + * table-walker cache. A cache that is read only should never see + * any writes, and should never get any dirty data (and hence + * never have to do any writebacks). + */ + const bool isReadOnly; /** * Bit vector of the blocking reasons for the access path. @@ -209,7 +311,7 @@ class BaseCache : public MemObject uint64_t order; /** Stores time the cache blocked for statistics. */ - Tick blockedCycle; + Cycles blockedCycle; /** Pointer to the MSHR that has no targets. */ MSHR *noTargetMSHR; @@ -217,33 +319,31 @@ class BaseCache : public MemObject /** The number of misses to trigger an exit event. */ Counter missCount; - /** The drain event. */ - Event *drainEvent; - /** * The address range to which the cache responds on the CPU side. * Normally this is all possible memory addresses. */ - Range addrRange; - - /** number of cpus sharing this cache - from config file */ - int _numCpus; + const AddrRangeList addrRanges; public: - int numCpus() { return _numCpus; } + /** System we are currently operating in. */ + System *system; + // Statistics /** * @addtogroup CacheStatistics * @{ */ - /** Number of hits per thread for each type of command. @sa Packet::Command */ + /** Number of hits per thread for each type of command. + @sa Packet::Command */ Stats::Vector hits[MemCmd::NUM_MEM_CMDS]; /** Number of hits for demand accesses. */ Stats::Formula demandHits; /** Number of hit for all accesses. */ Stats::Formula overallHits; - /** Number of misses per thread for each type of command. @sa Packet::Command */ + /** Number of misses per thread for each type of command. + @sa Packet::Command */ Stats::Vector misses[MemCmd::NUM_MEM_CMDS]; /** Number of misses for demand accesses. */ Stats::Formula demandMisses; @@ -289,11 +389,8 @@ class BaseCache : public MemObject /** The average number of cycles blocked for each blocked cause. */ Stats::Formula avg_blocked; - /** The number of fast writes (WH64) performed. */ - Stats::Scalar fastWrites; - - /** The number of cache copies performed. */ - Stats::Scalar cacheCopies; + /** The number of times a HW-prefetched block is evicted w/o reference. */ + Stats::Scalar unusedPrefetches; /** Number of blocks written back per thread. */ Stats::Vector writebacks; @@ -357,13 +454,6 @@ class BaseCache : public MemObject /** The average overall latency of an MSHR miss. */ Stats::Formula overallAvgMshrUncacheableLatency; - /** The number of times a thread hit its MSHR cap. */ - Stats::Vector mshr_cap_events; - /** The number of times software prefetches caused the MSHR to block. */ - Stats::Vector soft_prefetch_mshr_full; - - Stats::Scalar mshr_no_allocate_misses; - /** * @} */ @@ -374,12 +464,16 @@ class BaseCache : public MemObject virtual void regStats(); public: - typedef BaseCacheParams Params; - BaseCache(const Params *p); + BaseCache(const BaseCacheParams *p, unsigned blk_size); ~BaseCache() {} virtual void init(); + virtual BaseMasterPort &getMasterPort(const std::string &if_name, + PortID idx = InvalidPortID); + virtual BaseSlavePort &getSlavePort(const std::string &if_name, + PortID idx = InvalidPortID); + /** * Query block size of a cache. * @return The block size @@ -394,37 +488,53 @@ class BaseCache : public MemObject Addr blockAlign(Addr addr) const { return (addr & ~(Addr(blkSize - 1))); } - const Range &getAddrRange() const { return addrRange; } + const AddrRangeList &getAddrRanges() const { return addrRanges; } - MSHR *allocateMissBuffer(PacketPtr pkt, Tick time, bool requestBus) + MSHR *allocateMissBuffer(PacketPtr pkt, Tick time, bool sched_send = true) { - assert(!pkt->req->isUncacheable()); - return allocateBufferInternal(&mshrQueue, - blockAlign(pkt->getAddr()), blkSize, - pkt, time, requestBus); - } + MSHR *mshr = mshrQueue.allocate(blockAlign(pkt->getAddr()), blkSize, + pkt, time, order++, + allocOnFill(pkt->cmd)); - MSHR *allocateWriteBuffer(PacketPtr pkt, Tick time, bool requestBus) - { - assert(pkt->isWrite() && !pkt->isRead()); - return allocateBufferInternal(&writeBuffer, - pkt->getAddr(), pkt->getSize(), - pkt, time, requestBus); + if (mshrQueue.isFull()) { + setBlocked((BlockedCause)MSHRQueue_MSHRs); + } + + if (sched_send) { + // schedule the send + schedMemSideSendEvent(time); + } + + return mshr; } - MSHR *allocateUncachedReadBuffer(PacketPtr pkt, Tick time, bool requestBus) + void allocateWriteBuffer(PacketPtr pkt, Tick time) { - assert(pkt->req->isUncacheable()); - assert(pkt->isRead()); - return allocateBufferInternal(&mshrQueue, - pkt->getAddr(), pkt->getSize(), - pkt, time, requestBus); + // should only see writes or clean evicts here + assert(pkt->isWrite() || pkt->cmd == MemCmd::CleanEvict); + + Addr blk_addr = blockAlign(pkt->getAddr()); + + WriteQueueEntry *wq_entry = + writeBuffer.findMatch(blk_addr, pkt->isSecure()); + if (wq_entry && !wq_entry->inService) { + DPRINTF(Cache, "Potential to merge writeback %s", pkt->print()); + } + + writeBuffer.allocate(blk_addr, blkSize, pkt, time, order++); + + if (writeBuffer.isFull()) { + setBlocked((BlockedCause)MSHRQueue_WriteBuffer); + } + + // schedule the send + schedMemSideSendEvent(time); } /** * Returns true if the cache is blocked for accesses. */ - bool isBlocked() + bool isBlocked() const { return blocked != 0; } @@ -439,7 +549,7 @@ class BaseCache : public MemObject uint8_t flag = 1 << cause; if (blocked == 0) { blocked_causes[cause]++; - blockedCycle = curTick(); + blockedCycle = curCycle(); cpuSidePort->setBlocked(); } blocked |= flag; @@ -459,90 +569,46 @@ class BaseCache : public MemObject blocked &= ~flag; DPRINTF(Cache,"Unblocking for cause %d, mask=%d\n", cause, blocked); if (blocked == 0) { - blocked_cycles[cause] += curTick() - blockedCycle; + blocked_cycles[cause] += curCycle() - blockedCycle; cpuSidePort->clearBlocked(); } } /** - * Request the master bus for the given cause and time. - * @param cause The reason for the request. - * @param time The time to make the request. + * Schedule a send event for the memory-side port. If already + * scheduled, this may reschedule the event at an earlier + * time. When the specified time is reached, the port is free to + * send either a response, a request, or a prefetch request. + * + * @param time The time when to attempt sending a packet. */ - void requestMemSideBus(RequestCause cause, Tick time) + void schedMemSideSendEvent(Tick time) { - memSidePort->requestBus(cause, time); + memSidePort->schedSendEvent(time); } - /** - * Clear the master bus request for the given cause. - * @param cause The request reason to clear. - */ - void deassertMemSideBusRequest(RequestCause cause) - { - // Obsolete... we no longer signal bus requests explicitly so - // we can't deassert them. Leaving this in as a no-op since - // the prefetcher calls it to indicate that it no longer wants - // to request a prefetch, and someday that might be - // interesting again. - } + virtual bool inCache(Addr addr, bool is_secure) const = 0; - virtual unsigned int drain(Event *de); + virtual bool inMissQueue(Addr addr, bool is_secure) const = 0; - virtual bool inCache(Addr addr) = 0; - - virtual bool inMissQueue(Addr addr) = 0; - - void incMissCount(PacketPtr pkt, int id) + void incMissCount(PacketPtr pkt) { - - if (pkt->cmd == MemCmd::Writeback) { - assert(id == -1); - misses[pkt->cmdToIndex()][0]++; - /* same thing for writeback hits as misses - no context id - * available, meanwhile writeback hit/miss stats are not used - * in any aggregate hit/miss calculations, so just lump them all - * in bucket 0 */ -#if FULL_SYSTEM - } else if (id == -1) { - // Device accesses have id -1 - // lump device accesses into their own bucket - misses[pkt->cmdToIndex()][_numCpus]++; -#endif - } else { - misses[pkt->cmdToIndex()][id % _numCpus]++; - } - + assert(pkt->req->masterId() < system->maxMasters()); + misses[pkt->cmdToIndex()][pkt->req->masterId()]++; + pkt->req->incAccessDepth(); if (missCount) { --missCount; if (missCount == 0) exitSimLoop("A cache reached the maximum miss count"); } } - void incHitCount(PacketPtr pkt, int id) + void incHitCount(PacketPtr pkt) { + assert(pkt->req->masterId() < system->maxMasters()); + hits[pkt->cmdToIndex()][pkt->req->masterId()]++; - /* Writeback requests don't have a context id associated with - * them, so attributing a hit to a -1 context id is obviously a - * problem. I've noticed in the stats that hits are split into - * demand and non-demand hits - neither of which include writeback - * hits, so here, I'll just put the writeback hits into bucket 0 - * since it won't mess with any other stats -hsul */ - if (pkt->cmd == MemCmd::Writeback) { - assert(id == -1); - hits[pkt->cmdToIndex()][0]++; -#if FULL_SYSTEM - } else if (id == -1) { - // Device accesses have id -1 - // lump device accesses into their own bucket - hits[pkt->cmdToIndex()][_numCpus]++; -#endif - } else { - /* the % is necessary in case there are switch cpus */ - hits[pkt->cmdToIndex()][id % _numCpus]++; - } } }; -#endif //__BASE_CACHE_HH__ +#endif //__MEM_CACHE_BASE_HH__