mem: Add explicit Cache subclass and make BaseCache abstract
[gem5.git] / src / mem / cache / base.hh
index c245fecd24e6235ab319069b2c106a55dd76c5d2..3baec36d90d7c99cb357b117115da75c29f2c17c 100644 (file)
@@ -1,4 +1,16 @@
 /*
+ * Copyright (c) 2012-2013, 2015 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.
  *
  * 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 <vector>
-#include <string>
-#include <list>
 #include <algorithm>
+#include <list>
+#include <string>
+#include <vector>
 
 #include "base/misc.hh"
 #include "base/statistics.hh"
 #include "base/trace.hh"
 #include "base/types.hh"
+#include "debug/Cache.hh"
+#include "debug/CachePort.hh"
 #include "mem/cache/mshr_queue.hh"
 #include "mem/mem_object.hh"
 #include "mem/packet.hh"
-#include "mem/tport.hh"
+#include "mem/qport.hh"
 #include "mem/request.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;
 /**
@@ -70,6 +86,7 @@ class BaseCache : public MemObject
         MSHRQueue_WriteBuffer
     };
 
+  public:
     /**
      * Reasons for caches to be blocked.
      */
@@ -80,70 +97,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<Port, &Port::sendRetry>
-            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<CacheSlavePort,
+                     &CacheSlavePort::processSendRetry> 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:
 
@@ -153,45 +192,115 @@ class BaseCache : public MemObject
     /** Write/writeback buffer */
     MSHRQueue writeBuffer;
 
+    /**
+     * Allocate a buffer, passing the time indicating when schedule an
+     * event to the queued port to go and ask the MSHR and write queue
+     * if they have packets to send.
+     *
+     * allocateBufferInternal() function is called in:
+     * - MSHR allocateWriteBuffer (unchached write forwarded to WriteBuffer);
+     * - MSHR allocateMissBuffer (miss in MSHR queue);
+     */
     MSHR *allocateBufferInternal(MSHRQueue *mq, Addr addr, int size,
-                                 PacketPtr pkt, Tick time, bool requestBus)
+                                 PacketPtr pkt, Tick time,
+                                 bool sched_send)
     {
+        // check that the address is block aligned since we rely on
+        // this in a number of places when checking for matches and
+        // overlap
+        assert(addr == blockAlign(addr));
+
         MSHR *mshr = mq->allocate(addr, size, pkt, time, order++);
 
         if (mq->isFull()) {
             setBlocked((BlockedCause)mq->index);
         }
 
-        if (requestBus) {
-            requestMemSideBus((RequestCause)mq->index, time);
-        }
+        if (sched_send)
+            // schedule the send
+            schedMemSideSendEvent(time);
 
         return mshr;
     }
 
-    void markInServiceInternal(MSHR *mshr)
+    void markInServiceInternal(MSHR *mshr, bool pending_dirty_resp)
     {
         MSHRQueue *mq = mshr->queue;
         bool wasFull = mq->isFull();
-        mq->markInService(mshr);
+        mq->markInService(mshr, pending_dirty_resp);
         if (wasFull && !mq->isFull()) {
             clearBlocked((BlockedCause)mq->index);
         }
     }
 
+    /**
+     * 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.
      */
-    int hitLatency;
+    const Cycles lookupLatency;
+
+    /**
+     * 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.
+     */
+    const Cycles responseLatency;
 
     /** The number of targets for each MSHR. */
     const int numTarget;
 
     /** Do we forward snoops from mem side port through to cpu side port? */
-    bool forwardSnoops;
+    const bool forwardSnoops;
+
+    /**
+     * 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.
@@ -203,7 +312,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;
@@ -211,15 +320,15 @@ 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<Addr> addrRange;
+    const AddrRangeList addrRanges;
 
   public:
+    /** System we are currently operating in. */
+    System *system;
+
     // Statistics
     /**
      * @addtogroup CacheStatistics
@@ -319,12 +428,14 @@ class BaseCache : public MemObject
     /** Total cycle latency of overall MSHR misses. */
     Stats::Formula overallMshrUncacheableLatency;
 
+#if 0
     /** The total number of MSHR accesses per command and thread. */
     Stats::Formula mshrAccesses[MemCmd::NUM_MEM_CMDS];
     /** The total number of demand MSHR accesses. */
     Stats::Formula demandMshrAccesses;
     /** The total number of MSHR accesses. */
     Stats::Formula overallMshrAccesses;
+#endif
 
     /** The miss rate in the MSHRs pre command and thread. */
     Stats::Formula mshrMissRate[MemCmd::NUM_MEM_CMDS];
@@ -362,12 +473,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
@@ -382,37 +497,29 @@ class BaseCache : public MemObject
     Addr blockAlign(Addr addr) const { return (addr & ~(Addr(blkSize - 1))); }
 
 
-    const Range<Addr> &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);
+                                      pkt, time, sched_send);
     }
 
-    MSHR *allocateWriteBuffer(PacketPtr pkt, Tick time, bool requestBus)
+    MSHR *allocateWriteBuffer(PacketPtr pkt, Tick time)
     {
+        // should only see clean evictions in a read-only cache
+        assert(!isReadOnly || pkt->cmd == MemCmd::CleanEvict);
         assert(pkt->isWrite() && !pkt->isRead());
         return allocateBufferInternal(&writeBuffer,
-                                      pkt->getAddr(), pkt->getSize(),
-                                      pkt, time, requestBus);
-    }
-
-    MSHR *allocateUncachedReadBuffer(PacketPtr pkt, Tick time, bool requestBus)
-    {
-        assert(pkt->req->isUncacheable());
-        assert(pkt->isRead());
-        return allocateBufferInternal(&mshrQueue,
-                                      pkt->getAddr(), pkt->getSize(),
-                                      pkt, time, requestBus);
+                                      blockAlign(pkt->getAddr()), blkSize,
+                                      pkt, time, true);
     }
 
     /**
      * Returns true if the cache is blocked for accesses.
      */
-    bool isBlocked()
+    bool isBlocked() const
     {
         return blocked != 0;
     }
@@ -427,7 +534,7 @@ class BaseCache : public MemObject
         uint8_t flag = 1 << cause;
         if (blocked == 0) {
             blocked_causes[cause]++;
-            blockedCycle = curTick;
+            blockedCycle = curCycle();
             cpuSidePort->setBlocked();
         }
         blocked |= flag;
@@ -447,51 +554,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 unsigned int drain(Event *de);
-
-    virtual bool inCache(Addr addr) = 0;
+    virtual bool inCache(Addr addr, bool is_secure) const = 0;
 
-    virtual bool inMissQueue(Addr addr) = 0;
+    virtual bool inMissQueue(Addr addr, bool is_secure) const = 0;
 
     void incMissCount(PacketPtr pkt)
     {
-        misses[pkt->cmdToIndex()][0/*pkt->req->threadId()*/]++;
-
+        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)
+    {
+        assert(pkt->req->masterId() < system->maxMasters());
+        hits[pkt->cmdToIndex()][pkt->req->masterId()]++;
+
+    }
 
 };
 
-#endif //__BASE_CACHE_HH__
+#endif //__MEM_CACHE_BASE_HH__