summaryrefslogtreecommitdiff
path: root/src/mem/cache/cache_impl.hh
diff options
context:
space:
mode:
Diffstat (limited to 'src/mem/cache/cache_impl.hh')
-rw-r--r--src/mem/cache/cache_impl.hh2083
1 files changed, 1101 insertions, 982 deletions
diff --git a/src/mem/cache/cache_impl.hh b/src/mem/cache/cache_impl.hh
index 4cd5ab004..d144266ed 100644
--- a/src/mem/cache/cache_impl.hh
+++ b/src/mem/cache/cache_impl.hh
@@ -28,6 +28,8 @@
* Authors: Erik Hallnor
* Dave Greene
* Nathan Binkert
+ * Steve Reinhardt
+ * Ron Dreslinski
*/
/**
@@ -35,17 +37,8 @@
* Cache definitions.
*/
-#include <assert.h>
-#include <math.h>
-
-#include <cassert>
-#include <iostream>
-#include <cstring>
-#include <string>
-
#include "sim/host.hh"
#include "base/misc.hh"
-#include "cpu/smt.hh"
#include "mem/cache/cache.hh"
#include "mem/cache/cache_blk.hh"
@@ -54,557 +47,375 @@
#include "sim/sim_exit.hh" // for SimExitEvent
-bool SIGNAL_NACK_HACK;
-
-template<class TagStore, class Coherence>
-void
-Cache<TagStore,Coherence>::
-recvStatusChange(Port::Status status, bool isCpuSide)
-{
-}
-
-
-template<class TagStore, class Coherence>
-Cache<TagStore,Coherence>::
-Cache(const std::string &_name,
- Cache<TagStore,Coherence>::Params &params)
+template<class TagStore>
+Cache<TagStore>::Cache(const std::string &_name,
+ Cache<TagStore>::Params &params)
: BaseCache(_name, params.baseParams),
prefetchAccess(params.prefetchAccess),
- tags(params.tags), missQueue(params.missQueue),
- coherence(params.coherence), prefetcher(params.prefetcher),
- hitLatency(params.hitLatency),
- compressionAlg(params.compressionAlg),
- blkSize(params.blkSize),
+ tags(params.tags),
+ prefetcher(params.prefetcher),
doFastWrites(params.doFastWrites),
- prefetchMiss(params.prefetchMiss),
- storeCompressed(params.storeCompressed),
- compressOnWriteback(params.compressOnWriteback),
- compLatency(params.compLatency),
- adaptiveCompression(params.adaptiveCompression),
- writebackCompressed(params.writebackCompressed)
+ prefetchMiss(params.prefetchMiss)
{
+ tempBlock = new BlkType();
+ tempBlock->data = new uint8_t[blkSize];
+
+ cpuSidePort = new CpuSidePort(_name + "-cpu_side_port", this);
+ memSidePort = new MemSidePort(_name + "-mem_side_port", this);
+ cpuSidePort->setOtherPort(memSidePort);
+ memSidePort->setOtherPort(cpuSidePort);
+
tags->setCache(this);
- missQueue->setCache(this);
- missQueue->setPrefetcher(prefetcher);
- coherence->setCache(this);
prefetcher->setCache(this);
- invalidateReq = new Request((Addr) NULL, blkSize, 0);
- invalidatePkt = new Packet(invalidateReq, MemCmd::InvalidateReq, 0);
}
-template<class TagStore, class Coherence>
+template<class TagStore>
void
-Cache<TagStore,Coherence>::regStats()
+Cache<TagStore>::regStats()
{
BaseCache::regStats();
tags->regStats(name());
- missQueue->regStats(name());
- coherence->regStats(name());
prefetcher->regStats(name());
}
-template<class TagStore, class Coherence>
-typename Cache<TagStore,Coherence>::BlkType*
-Cache<TagStore,Coherence>::handleAccess(PacketPtr &pkt, int & lat,
- PacketList & writebacks, bool update)
+template<class TagStore>
+Port *
+Cache<TagStore>::getPort(const std::string &if_name, int idx)
{
- // Set the block offset here
- int offset = tags->extractBlkOffset(pkt->getAddr());
-
- BlkType *blk = NULL;
- if (update) {
- blk = tags->findBlock(pkt->getAddr(), lat);
+ if (if_name == "" || if_name == "cpu_side") {
+ return cpuSidePort;
+ } else if (if_name == "mem_side") {
+ return memSidePort;
+ } else if (if_name == "functional") {
+ return new CpuSidePort(name() + "-cpu_side_funcport", this);
} else {
- blk = tags->findBlock(pkt->getAddr());
- lat = 0;
+ panic("Port name %s unrecognized\n", if_name);
}
- if (blk != NULL) {
-
- if (!update) {
-
- if (pkt->isWrite()){
- assert(offset < blkSize);
- assert(pkt->getSize() <= blkSize);
- assert(offset+pkt->getSize() <= blkSize);
- std::memcpy(blk->data + offset, pkt->getPtr<uint8_t>(),
- pkt->getSize());
- } else if (pkt->isReadWrite()) {
- cmpAndSwap(blk, pkt);
- } else if (!(pkt->flags & SATISFIED)) {
- pkt->flags |= SATISFIED;
- pkt->result = Packet::Success;
- assert(offset < blkSize);
- assert(pkt->getSize() <= blkSize);
- assert(offset + pkt->getSize() <=blkSize);
- std::memcpy(pkt->getPtr<uint8_t>(), blk->data + offset,
- pkt->getSize());
- }
- return blk;
- }
+}
- // Hit
- if (blk->isPrefetch()) {
- //Signal that this was a hit under prefetch (no need for
- //use prefetch (only can get here if true)
- DPRINTF(HWPrefetch, "Hit a block that was prefetched\n");
- blk->status &= ~BlkHWPrefetched;
- if (prefetchMiss) {
- //If we are using the miss stream, signal the
- //prefetcher otherwise the access stream would have
- //already signaled this hit
- prefetcher->handleMiss(pkt, curTick);
- }
- }
+template<class TagStore>
+void
+Cache<TagStore>::deletePortRefs(Port *p)
+{
+ if (cpuSidePort == p || memSidePort == p)
+ panic("Can only delete functional ports\n");
- if ((pkt->isReadWrite() && blk->isWritable()) ||
- (pkt->isWrite() && blk->isWritable()) ||
- (pkt->isRead() && blk->isValid())) {
+ delete p;
+}
- // We are satisfying the request
- pkt->flags |= SATISFIED;
- if (blk->isCompressed()) {
- // If the data is compressed, need to increase the latency
- lat += (compLatency/4);
- }
+template<class TagStore>
+void
+Cache<TagStore>::cmpAndSwap(BlkType *blk, PacketPtr pkt)
+{
+ uint64_t overwrite_val;
+ bool overwrite_mem;
+ uint64_t condition_val64;
+ uint32_t condition_val32;
- bool write_data = false;
+ int offset = tags->extractBlkOffset(pkt->getAddr());
+ uint8_t *blk_data = blk->data + offset;
+
+ assert(sizeof(uint64_t) >= pkt->getSize());
+
+ overwrite_mem = true;
+ // keep a copy of our possible write value, and copy what is at the
+ // memory address into the packet
+ pkt->writeData((uint8_t *)&overwrite_val);
+ pkt->setData(blk_data);
+
+ if (pkt->req->isCondSwap()) {
+ if (pkt->getSize() == sizeof(uint64_t)) {
+ condition_val64 = pkt->req->getExtraData();
+ overwrite_mem = !std::memcmp(&condition_val64, blk_data,
+ sizeof(uint64_t));
+ } else if (pkt->getSize() == sizeof(uint32_t)) {
+ condition_val32 = (uint32_t)pkt->req->getExtraData();
+ overwrite_mem = !std::memcmp(&condition_val32, blk_data,
+ sizeof(uint32_t));
+ } else
+ panic("Invalid size for conditional read/write\n");
+ }
- assert(verifyData(blk));
+ if (overwrite_mem)
+ std::memcpy(blk_data, &overwrite_val, pkt->getSize());
+}
- assert(offset < blkSize);
- assert(pkt->getSize() <= blkSize);
- assert(offset+pkt->getSize() <= blkSize);
- if (pkt->isWrite()) {
- if (blk->checkWrite(pkt->req)) {
- write_data = true;
- blk->status |= BlkDirty;
- std::memcpy(blk->data + offset, pkt->getPtr<uint8_t>(),
- pkt->getSize());
- }
- } else if (pkt->isReadWrite()) {
- cmpAndSwap(blk, pkt);
+template<class TagStore>
+void
+Cache<TagStore>::satisfyCpuSideRequest(PacketPtr pkt, BlkType *blk)
+{
+ assert(blk);
+ // Occasionally this is not true... if we are a lower-level cache
+ // satisfying a string of Read and ReadEx requests from
+ // upper-level caches, a Read will mark the block as shared but we
+ // can satisfy a following ReadEx anyway since we can rely on the
+ // Read requester(s) to have buffered the ReadEx snoop and to
+ // invalidate their blocks after receiving them.
+ // assert(pkt->needsExclusive() ? blk->isWritable() : blk->isValid());
+ assert(pkt->getOffset(blkSize) + pkt->getSize() <= blkSize);
+
+ // Check RMW operations first since both isRead() and
+ // isWrite() will be true for them
+ if (pkt->cmd == MemCmd::SwapReq) {
+ cmpAndSwap(blk, pkt);
+ } else if (pkt->isWrite()) {
+ if (blk->checkWrite(pkt)) {
+ blk->status |= BlkDirty;
+ pkt->writeDataToBlock(blk->data, blkSize);
+ }
+ } else if (pkt->isRead()) {
+ if (pkt->isLocked()) {
+ blk->trackLoadLocked(pkt);
+ }
+ pkt->setDataFromBlock(blk->data, blkSize);
+ if (pkt->getSize() == blkSize) {
+ // special handling for coherent block requests from
+ // upper-level caches
+ if (pkt->needsExclusive()) {
+ // on ReadExReq we give up our copy
+ tags->invalidateBlk(blk);
} else {
- assert(pkt->isRead());
- if (pkt->req->isLocked()) {
- blk->trackLoadLocked(pkt->req);
- }
- std::memcpy(pkt->getPtr<uint8_t>(), blk->data + offset,
- pkt->getSize());
- }
-
- if (write_data ||
- (adaptiveCompression && blk->isCompressed()))
- {
- // If we wrote data, need to update the internal block
- // data.
- updateData(blk, writebacks,
- !(adaptiveCompression &&
- blk->isReferenced()));
+ // on ReadReq we create shareable copies here and in
+ // the requester
+ pkt->assertShared();
+ blk->status &= ~BlkWritable;
}
- } else {
- // permission violation, treat it as a miss
- blk = NULL;
}
} else {
- // complete miss (no matching block)
- if (pkt->req->isLocked() && pkt->isWrite()) {
- // miss on store conditional... just give up now
- pkt->req->setExtraData(0);
- pkt->flags |= SATISFIED;
- }
+ // Not a read or write... must be an upgrade. it's OK
+ // to just ack those as long as we have an exclusive
+ // copy at this level.
+ assert(pkt->cmd == MemCmd::UpgradeReq);
+ tags->invalidateBlk(blk);
}
-
- return blk;
}
-template<class TagStore, class Coherence>
-void
-Cache<TagStore,Coherence>::cmpAndSwap(BlkType *blk, PacketPtr &pkt){
- uint64_t overwrite_val;
- bool overwrite_mem;
- uint64_t condition_val64;
- uint32_t condition_val32;
-
- int offset = tags->extractBlkOffset(pkt->getAddr());
-
- assert(sizeof(uint64_t) >= pkt->getSize());
-
- overwrite_mem = true;
- // keep a copy of our possible write value, and copy what is at the
- // memory address into the packet
- std::memcpy(&overwrite_val, pkt->getPtr<uint8_t>(), pkt->getSize());
- std::memcpy(pkt->getPtr<uint8_t>(), blk->data + offset,
- pkt->getSize());
-
- if (pkt->req->isCondSwap()) {
- if (pkt->getSize() == sizeof(uint64_t)) {
- condition_val64 = pkt->req->getExtraData();
- overwrite_mem = !std::memcmp(&condition_val64, blk->data + offset,
- sizeof(uint64_t));
- } else if (pkt->getSize() == sizeof(uint32_t)) {
- condition_val32 = (uint32_t)pkt->req->getExtraData();
- overwrite_mem = !std::memcmp(&condition_val32, blk->data + offset,
- sizeof(uint32_t));
- } else
- panic("Invalid size for conditional read/write\n");
- }
- if (overwrite_mem)
- std::memcpy(blk->data + offset,
- &overwrite_val, pkt->getSize());
+/////////////////////////////////////////////////////
+//
+// MSHR helper functions
+//
+/////////////////////////////////////////////////////
-}
-template<class TagStore, class Coherence>
-typename Cache<TagStore,Coherence>::BlkType*
-Cache<TagStore,Coherence>::handleFill(BlkType *blk, PacketPtr &pkt,
- CacheBlk::State new_state,
- PacketList & writebacks,
- PacketPtr target)
+template<class TagStore>
+void
+Cache<TagStore>::markInService(MSHR *mshr)
{
-#ifndef NDEBUG
- BlkType *tmp_blk = tags->findBlock(pkt->getAddr());
- assert(tmp_blk == blk);
+ markInServiceInternal(mshr);
+#if 0
+ if (mshr->originalCmd == MemCmd::HardPFReq) {
+ DPRINTF(HWPrefetch, "%s:Marking a HW_PF in service\n",
+ name());
+ //Also clear pending if need be
+ if (!prefetcher->havePending())
+ {
+ deassertMemSideBusRequest(Request_PF);
+ }
+ }
#endif
- blk = doReplacement(blk, pkt, new_state, writebacks);
-
-
- if (pkt->isRead()) {
- std::memcpy(blk->data, pkt->getPtr<uint8_t>(), blkSize);
- }
-
- blk->whenReady = pkt->finishTime;
-
- // Respond to target, if any
- if (target) {
+}
- target->flags |= SATISFIED;
- if (target->cmd == MemCmd::InvalidateReq) {
- tags->invalidateBlk(blk);
- blk = NULL;
- }
+template<class TagStore>
+void
+Cache<TagStore>::squash(int threadNum)
+{
+ bool unblock = false;
+ BlockedCause cause = NUM_BLOCKED_CAUSES;
- if (blk && ((target->isWrite() || target->isReadWrite()) ?
- blk->isWritable() : blk->isValid())) {
- assert(target->isWrite() || target->isReadWrite() || target->isRead());
- assert(target->getOffset(blkSize) + target->getSize() <= blkSize);
- if (target->isWrite()) {
- if (blk->checkWrite(pkt->req)) {
- blk->status |= BlkDirty;
- std::memcpy(blk->data + target->getOffset(blkSize),
- target->getPtr<uint8_t>(), target->getSize());
- }
- } else if (target->isReadWrite()) {
- cmpAndSwap(blk, target);
- } else {
- if (pkt->req->isLocked()) {
- blk->trackLoadLocked(pkt->req);
- }
- std::memcpy(target->getPtr<uint8_t>(),
- blk->data + target->getOffset(blkSize),
- target->getSize());
- }
- }
+ if (noTargetMSHR && noTargetMSHR->threadNum == threadNum) {
+ noTargetMSHR = NULL;
+ unblock = true;
+ cause = Blocked_NoTargets;
}
-
- if (blk) {
- // Need to write the data into the block
- updateData(blk, writebacks, !adaptiveCompression || true);
+ if (mshrQueue.isFull()) {
+ unblock = true;
+ cause = Blocked_NoMSHRs;
+ }
+ mshrQueue.squash(threadNum);
+ if (unblock && !mshrQueue.isFull()) {
+ clearBlocked(cause);
}
- return blk;
}
-template<class TagStore, class Coherence>
-typename Cache<TagStore,Coherence>::BlkType*
-Cache<TagStore,Coherence>::handleFill(BlkType *blk, MSHR * mshr,
- CacheBlk::State new_state,
- PacketList & writebacks, PacketPtr pkt)
-{
-/*
-#ifndef NDEBUG
- BlkType *tmp_blk = findBlock(mshr->pkt->getAddr());
- assert(tmp_blk == blk);
-#endif
- PacketPtr pkt = mshr->pkt;*/
- blk = doReplacement(blk, pkt, new_state, writebacks);
+/////////////////////////////////////////////////////
+//
+// Access path: requests coming in from the CPU side
+//
+/////////////////////////////////////////////////////
- if (pkt->isRead()) {
- std::memcpy(blk->data, pkt->getPtr<uint8_t>(), blkSize);
+template<class TagStore>
+bool
+Cache<TagStore>::access(PacketPtr pkt, BlkType *&blk, int &lat)
+{
+ if (pkt->req->isUncacheable()) {
+ blk = NULL;
+ lat = hitLatency;
+ return false;
}
- blk->whenReady = pkt->finishTime;
-
-
- // respond to MSHR targets, if any
+ bool satisfied = false; // assume the worst
+ blk = tags->findBlock(pkt->getAddr(), lat);
- // First offset for critical word first calculations
- int initial_offset = 0;
-
- if (mshr->hasTargets()) {
- initial_offset = mshr->getTarget()->getOffset(blkSize);
+ if (prefetchAccess) {
+ //We are determining prefetches on access stream, call prefetcher
+ prefetcher->handleMiss(pkt, curTick);
}
- while (mshr->hasTargets()) {
- PacketPtr target = mshr->getTarget();
-
- target->flags |= SATISFIED;
-
- // How many bytes pass the first request is this one
- int transfer_offset = target->getOffset(blkSize) - initial_offset;
- if (transfer_offset < 0) {
- transfer_offset += blkSize;
- }
-
- // If critical word (no offset) return first word time
- Tick completion_time = tags->getHitLatency() +
- transfer_offset ? pkt->finishTime : pkt->firstWordTime;
+ DPRINTF(Cache, "%s %x %s\n", pkt->cmdString(), pkt->getAddr(),
+ (blk) ? "hit" : "miss");
- if (target->cmd == MemCmd::InvalidateReq) {
- //Mark the blk as invalid now, if it hasn't been already
- if (blk) {
- tags->invalidateBlk(blk);
- blk = NULL;
+ if (blk != NULL) {
+ // HIT
+ if (blk->isPrefetch()) {
+ //Signal that this was a hit under prefetch (no need for
+ //use prefetch (only can get here if true)
+ DPRINTF(HWPrefetch, "Hit a block that was prefetched\n");
+ blk->status &= ~BlkHWPrefetched;
+ if (prefetchMiss) {
+ //If we are using the miss stream, signal the
+ //prefetcher otherwise the access stream would have
+ //already signaled this hit
+ prefetcher->handleMiss(pkt, curTick);
}
-
- //Also get rid of the invalidate
- mshr->popTarget();
-
- DPRINTF(Cache, "Popping off a Invalidate for addr %x\n",
- pkt->getAddr());
-
- continue;
}
- if (blk && ((target->isWrite() || target->isReadWrite()) ?
- blk->isWritable() : blk->isValid())) {
- assert(target->isWrite() || target->isRead() || target->isReadWrite() );
- assert(target->getOffset(blkSize) + target->getSize() <= blkSize);
- if (target->isWrite()) {
- if (blk->checkWrite(pkt->req)) {
- blk->status |= BlkDirty;
- std::memcpy(blk->data + target->getOffset(blkSize),
- target->getPtr<uint8_t>(), target->getSize());
- }
- } else if (target->isReadWrite()) {
- cmpAndSwap(blk, target);
- } else {
- if (target->req->isLocked()) {
- blk->trackLoadLocked(target->req);
- }
- std::memcpy(target->getPtr<uint8_t>(),
- blk->data + target->getOffset(blkSize),
- target->getSize());
- }
+ if (pkt->needsExclusive() ? blk->isWritable() : blk->isValid()) {
+ // OK to satisfy access
+ hits[pkt->cmdToIndex()][0/*pkt->req->getThreadNum()*/]++;
+ satisfied = true;
+ satisfyCpuSideRequest(pkt, blk);
+ } else if (pkt->cmd == MemCmd::Writeback) {
+ // special case: writeback to read-only block (e.g., from
+ // L1 into L2). since we're really just passing ownership
+ // from one cache to another, we can update this cache to
+ // be the owner without making the block writeable
+ assert(!blk->isWritable() /* && !blk->isDirty() */);
+ assert(blkSize == pkt->getSize());
+ std::memcpy(blk->data, pkt->getPtr<uint8_t>(), blkSize);
+ blk->status |= BlkDirty;
+ satisfied = true;
+ // nothing else to do; writeback doesn't expect response
+ assert(!pkt->needsResponse());
} else {
- // Invalid access, need to do another request
- // can occur if block is invalidated, or not correct
- // permissions
-// mshr->pkt = pkt;
- break;
+ // permission violation... nothing to do here, leave unsatisfied
+ // for statistics purposes this counts like a complete miss
+ incMissCount(pkt);
}
- respondToMiss(target, completion_time);
- mshr->popTarget();
- }
+ } else {
+ // complete miss (no matching block)
+ incMissCount(pkt);
- if (blk) {
- // Need to write the data into the block
- updateData(blk, writebacks, !adaptiveCompression || true);
+ if (pkt->isLocked() && pkt->isWrite()) {
+ // miss on store conditional... just give up now
+ pkt->req->setExtraData(0);
+ satisfied = true;
+ }
}
- return blk;
+ return satisfied;
}
-template<class TagStore, class Coherence>
-void
-Cache<TagStore,Coherence>::handleSnoop(BlkType *blk,
- CacheBlk::State new_state,
- PacketPtr &pkt)
+class ForwardResponseRecord : public Packet::SenderState
{
- //Must have the block to supply
- assert(blk);
- // Can only supply data, and if it hasn't already been supllied
- assert(pkt->isRead());
- assert(!(pkt->flags & SATISFIED));
- pkt->flags |= SATISFIED;
- Addr offset = pkt->getOffset(blkSize);
- assert(offset < blkSize);
- assert(pkt->getSize() <= blkSize);
- assert(offset + pkt->getSize() <=blkSize);
- std::memcpy(pkt->getPtr<uint8_t>(), blk->data + offset, pkt->getSize());
-
- handleSnoop(blk, new_state);
-}
-
-template<class TagStore, class Coherence>
-void
-Cache<TagStore,Coherence>::handleSnoop(BlkType *blk,
- CacheBlk::State new_state)
-{
- if (blk && blk->status != new_state) {
- if ((new_state && BlkValid) == 0) {
- tags->invalidateBlk(blk);
- } else {
- assert(new_state >= 0 && new_state < 128);
- blk->status = new_state;
- }
- }
-}
-
-template<class TagStore, class Coherence>
-PacketPtr
-Cache<TagStore,Coherence>::writebackBlk(BlkType *blk)
-{
- assert(blk && blk->isValid() && blk->isModified());
- int data_size = blkSize;
- data_size = blk->size;
- if (compressOnWriteback) {
- // not already compressed
- // need to compress to ship it
- assert(data_size == blkSize);
- uint8_t *tmp_data = new uint8_t[blkSize];
- data_size = compressionAlg->compress(tmp_data,blk->data,
- data_size);
- delete [] tmp_data;
+ Packet::SenderState *prevSenderState;
+ int prevSrc;
+#ifndef NDEBUG
+ BaseCache *cache;
+#endif
+ public:
+ ForwardResponseRecord(Packet *pkt, BaseCache *_cache)
+ : prevSenderState(pkt->senderState), prevSrc(pkt->getSrc())
+#ifndef NDEBUG
+ , cache(_cache)
+#endif
+ {}
+ void restore(Packet *pkt, BaseCache *_cache)
+ {
+ assert(_cache == cache);
+ pkt->senderState = prevSenderState;
+ pkt->setDest(prevSrc);
}
+};
-/* PacketPtr writeback =
- buildWritebackReq(tags->regenerateBlkAddr(blk->tag, blk->set),
- blk->asid, blkSize,
- blk->data, data_size);
-*/
-
- Request *writebackReq =
- new Request(tags->regenerateBlkAddr(blk->tag, blk->set), blkSize, 0);
- PacketPtr writeback = new Packet(writebackReq, MemCmd::Writeback, -1);
- writeback->allocate();
- std::memcpy(writeback->getPtr<uint8_t>(),blk->data,blkSize);
-
- blk->status &= ~BlkDirty;
- return writeback;
-}
-
-template<class TagStore, class Coherence>
+template<class TagStore>
bool
-Cache<TagStore,Coherence>::verifyData(BlkType *blk)
+Cache<TagStore>::timingAccess(PacketPtr pkt)
{
- bool retval;
- // The data stored in the blk
- uint8_t *blk_data = new uint8_t[blkSize];
- tags->readData(blk, blk_data);
- // Pointer for uncompressed data, assumed uncompressed
- uint8_t *tmp_data = blk_data;
- // The size of the data being stored, assumed uncompressed
- int data_size = blkSize;
-
- // If the block is compressed need to uncompress to access
- if (blk->isCompressed()){
- // Allocate new storage for the data
- tmp_data = new uint8_t[blkSize];
- data_size = compressionAlg->uncompress(tmp_data,blk_data, blk->size);
- assert(data_size == blkSize);
- // Don't need to keep blk_data around
- delete [] blk_data;
- } else {
- assert(blkSize == blk->size);
- }
-
- retval = std::memcmp(tmp_data, blk->data, blkSize) == 0;
- delete [] tmp_data;
- return retval;
-}
+//@todo Add back in MemDebug Calls
+// MemDebug::cacheAccess(pkt);
-template<class TagStore, class Coherence>
-void
-Cache<TagStore,Coherence>::updateData(BlkType *blk, PacketList &writebacks,
- bool compress_block)
-{
- if (storeCompressed && compress_block) {
- uint8_t *comp_data = new uint8_t[blkSize];
- int new_size = compressionAlg->compress(comp_data, blk->data, blkSize);
- if (new_size > (blkSize - tags->getSubBlockSize())){
- // no benefit to storing it compressed
- blk->status &= ~BlkCompressed;
- tags->writeData(blk, blk->data, blkSize,
- writebacks);
- } else {
- // Store the data compressed
- blk->status |= BlkCompressed;
- tags->writeData(blk, comp_data, new_size,
- writebacks);
- }
- delete [] comp_data;
- } else {
- blk->status &= ~BlkCompressed;
- tags->writeData(blk, blk->data, blkSize, writebacks);
+ // we charge hitLatency for doing just about anything here
+ Tick time = curTick + hitLatency;
+
+ if (pkt->isResponse()) {
+ // must be cache-to-cache response from upper to lower level
+ ForwardResponseRecord *rec =
+ dynamic_cast<ForwardResponseRecord *>(pkt->senderState);
+ assert(rec != NULL);
+ rec->restore(pkt, this);
+ delete rec;
+ memSidePort->respond(pkt, time);
+ return true;
}
-}
-template<class TagStore, class Coherence>
-typename Cache<TagStore,Coherence>::BlkType*
-Cache<TagStore,Coherence>::doReplacement(BlkType *blk, PacketPtr &pkt,
- CacheBlk::State new_state,
- PacketList &writebacks)
-{
- if (blk == NULL) {
- // need to do a replacement
- BlkList compress_list;
- blk = tags->findReplacement(pkt, writebacks, compress_list);
- while (adaptiveCompression && !compress_list.empty()) {
- updateData(compress_list.front(), writebacks, true);
- compress_list.pop_front();
+ assert(pkt->isRequest());
+
+ if (pkt->memInhibitAsserted()) {
+ DPRINTF(Cache, "mem inhibited on 0x%x: not responding\n",
+ pkt->getAddr());
+ assert(!pkt->req->isUncacheable());
+ // Special tweak for multilevel coherence: snoop downward here
+ // on invalidates since there may be other caches below here
+ // that have shared copies. Not necessary if we know that
+ // supplier had exclusive copy to begin with.
+ if (pkt->needsExclusive() && !pkt->isSupplyExclusive()) {
+ Packet *snoopPkt = new Packet(pkt, true); // clear flags
+ snoopPkt->setExpressSnoop();
+ snoopPkt->assertMemInhibit();
+ memSidePort->sendTiming(snoopPkt);
+ // main memory will delete snoopPkt
}
- if (blk->isValid()) {
- DPRINTF(Cache, "replacement: replacing %x with %x: %s\n",
- tags->regenerateBlkAddr(blk->tag,blk->set), pkt->getAddr(),
- (blk->isModified()) ? "writeback" : "clean");
+ return true;
+ }
- if (blk->isModified()) {
- // Need to write the data back
- writebacks.push_back(writebackBlk(blk));
- }
+ if (pkt->req->isUncacheable()) {
+ // writes go in write buffer, reads use MSHR
+ if (pkt->isWrite() && !pkt->isRead()) {
+ allocateWriteBuffer(pkt, time, true);
+ } else {
+ allocateUncachedReadBuffer(pkt, time, true);
}
- blk->tag = tags->extractTag(pkt->getAddr(), blk);
- } else {
- // must be a status change
- // assert(blk->status != new_state);
- if (blk->status == new_state) warn("Changing state to same value\n");
+ assert(pkt->needsResponse()); // else we should delete it here??
+ return true;
}
- blk->status = new_state;
- return blk;
-}
-
-
-template<class TagStore, class Coherence>
-bool
-Cache<TagStore,Coherence>::access(PacketPtr &pkt)
-{
-//@todo Add back in MemDebug Calls
-// MemDebug::cacheAccess(pkt);
- BlkType *blk = NULL;
- PacketList writebacks;
- int size = blkSize;
int lat = hitLatency;
- if (prefetchAccess) {
- //We are determining prefetches on access stream, call prefetcher
- prefetcher->handleMiss(pkt, curTick);
- }
+ bool satisfied = false;
Addr blk_addr = pkt->getAddr() & ~(Addr(blkSize-1));
-
- if (!pkt->req->isUncacheable()) {
- if (!missQueue->findMSHR(blk_addr)) {
- blk = handleAccess(pkt, lat, writebacks);
- }
- } else {
- size = pkt->getSize();
+ MSHR *mshr = mshrQueue.findMatch(blk_addr);
+
+ if (!mshr) {
+ // no outstanding access to this block, look up in cache
+ // (otherwise if we allow reads while there's an outstanding
+ // write miss, the read could return stale data out of the
+ // cache block... a more aggressive system could detect the
+ // overlap (if any) and forward data out of the MSHRs, but we
+ // don't do that yet)
+ BlkType *blk = NULL;
+ satisfied = access(pkt, blk, lat);
}
+
+#if 0
+ PacketList writebacks;
+
// If this is a block size write/hint (WH64) allocate the block here
// if the coherence protocol allows it.
/** @todo make the fast write alloc (wh64) work with coherence. */
@@ -613,7 +424,7 @@ Cache<TagStore,Coherence>::access(PacketPtr &pkt)
(pkt->cmd == MemCmd::WriteReq
|| pkt->cmd == MemCmd::WriteInvalidateReq) ) {
// not outstanding misses, can do this
- MSHR* outstanding_miss = missQueue->findMSHR(pkt->getAddr());
+ MSHR *outstanding_miss = mshrQueue.findMatch(pkt->getAddr());
if (pkt->cmd == MemCmd::WriteInvalidateReq || !outstanding_miss) {
if (outstanding_miss) {
warn("WriteInv doing a fastallocate"
@@ -624,683 +435,991 @@ Cache<TagStore,Coherence>::access(PacketPtr &pkt)
++fastWrites;
}
}
+
+ // copy writebacks to write buffer
while (!writebacks.empty()) {
PacketPtr wbPkt = writebacks.front();
- missQueue->doWriteback(wbPkt);
+ allocateWriteBuffer(wbPkt, time, true);
writebacks.pop_front();
- delete wbPkt;
- }
-
- if (!pkt->req->isUncacheable()) {
- DPRINTF(Cache, "%s %x %s\n", pkt->cmdString(), pkt->getAddr(),
- (blk) ? "hit" : "miss");
}
+#endif
- if (blk) {
- // Hit
- hits[pkt->cmdToIndex()][0/*pkt->req->getThreadNum()*/]++;
- // clear dirty bit if write through
- respond(pkt, curTick+lat);
- return true;
- }
+ bool needsResponse = pkt->needsResponse();
- // Miss
- if (!pkt->req->isUncacheable()) {
- misses[pkt->cmdToIndex()][0/*pkt->req->getThreadNum()*/]++;
- /** @todo Move miss count code into BaseCache */
- if (missCount) {
- --missCount;
- if (missCount == 0)
- exitSimLoop("A cache reached the maximum miss count");
+ if (satisfied) {
+ if (needsResponse) {
+ pkt->makeTimingResponse();
+ cpuSidePort->respond(pkt, curTick+lat);
+ } else {
+ delete pkt;
}
- }
-
- if (pkt->flags & SATISFIED) {
- // happens when a store conditional fails because it missed
- // the cache completely
- respond(pkt, curTick+lat);
} else {
- missQueue->handleMiss(pkt, size, curTick + hitLatency);
- }
+ // miss
+ if (prefetchMiss)
+ prefetcher->handleMiss(pkt, time);
- if (!pkt->needsResponse()) {
- //Need to clean up the packet on a writeback miss, but leave the request
- //for the next level.
- delete pkt;
+ if (mshr) {
+ // MSHR hit
+ //@todo remove hw_pf here
+ mshr_hits[pkt->cmdToIndex()][0/*pkt->req->getThreadNum()*/]++;
+ if (mshr->threadNum != 0/*pkt->req->getThreadNum()*/) {
+ mshr->threadNum = -1;
+ }
+ mshr->allocateTarget(pkt, time, order++);
+ if (mshr->getNumTargets() == numTarget) {
+ noTargetMSHR = mshr;
+ setBlocked(Blocked_NoTargets);
+ // need to be careful with this... if this mshr isn't
+ // ready yet (i.e. time > curTick_, we don't want to
+ // move it ahead of mshrs that are ready
+ // mshrQueue.moveToFront(mshr);
+ }
+ } else {
+ // no MSHR
+ mshr_misses[pkt->cmdToIndex()][0/*pkt->req->getThreadNum()*/]++;
+ // always mark as cache fill for now... if we implement
+ // no-write-allocate or bypass accesses this will have to
+ // be changed.
+ if (pkt->cmd == MemCmd::Writeback) {
+ allocateWriteBuffer(pkt, time, true);
+ } else {
+ allocateMissBuffer(pkt, time, true);
+ }
+ }
}
return true;
}
-template<class TagStore, class Coherence>
+template<class TagStore>
PacketPtr
-Cache<TagStore,Coherence>::getPacket()
+Cache<TagStore>::getBusPacket(PacketPtr cpu_pkt, BlkType *blk,
+ bool needsExclusive)
{
- assert(missQueue->havePending());
- PacketPtr pkt = missQueue->getPacket();
- if (pkt) {
- if (!pkt->req->isUncacheable()) {
- if (pkt->cmd == MemCmd::HardPFReq)
- misses[MemCmd::HardPFReq][0/*pkt->req->getThreadNum()*/]++;
- BlkType *blk = tags->findBlock(pkt->getAddr());
- MemCmd cmd =
- coherence->getBusCmd(pkt->cmd, (blk) ? blk->status : 0);
- missQueue->setBusCmd(pkt, cmd);
- }
+ bool blkValid = blk && blk->isValid();
+
+ if (cpu_pkt->req->isUncacheable()) {
+ assert(blk == NULL);
+ return NULL;
+ }
+
+ if (!blkValid &&
+ (cpu_pkt->cmd == MemCmd::Writeback ||
+ cpu_pkt->cmd == MemCmd::UpgradeReq)) {
+ // For now, writebacks from upper-level caches that
+ // completely miss in the cache just go through. If we had
+ // "fast write" support (where we could write the whole
+ // block w/o fetching new data) we might want to allocate
+ // on writeback misses instead.
+ return NULL;
}
- assert(!doMasterRequest() || missQueue->havePending());
- assert(!pkt || pkt->time <= curTick);
- SIGNAL_NACK_HACK = false;
+ assert(cpu_pkt->needsResponse());
+
+ MemCmd cmd;
+ // @TODO make useUpgrades a parameter.
+ // Note that ownership protocols require upgrade, otherwise a
+ // write miss on a shared owned block will generate a ReadExcl,
+ // which will clobber the owned copy.
+ const bool useUpgrades = true;
+ if (blkValid && useUpgrades) {
+ // only reason to be here is that blk is shared
+ // (read-only) and we need exclusive
+ assert(needsExclusive && !blk->isWritable());
+ cmd = MemCmd::UpgradeReq;
+ } else {
+ // block is invalid
+ cmd = needsExclusive ? MemCmd::ReadExReq : MemCmd::ReadReq;
+ }
+ PacketPtr pkt = new Packet(cpu_pkt->req, cmd, Packet::Broadcast, blkSize);
+
+ pkt->allocate();
return pkt;
}
-template<class TagStore, class Coherence>
-void
-Cache<TagStore,Coherence>::sendResult(PacketPtr &pkt, MSHR* mshr,
- bool success)
+
+template<class TagStore>
+Tick
+Cache<TagStore>::atomicAccess(PacketPtr pkt)
{
- if (success && !(SIGNAL_NACK_HACK)) {
- //Remember if it was an upgrade because writeback MSHR's are removed
- //in Mark in Service
- bool upgrade = (mshr->pkt && mshr->pkt->cmd == MemCmd::UpgradeReq);
+ int lat = hitLatency;
- missQueue->markInService(mshr->pkt, mshr);
+ // @TODO: make this a parameter
+ bool last_level_cache = false;
- //Temp Hack for UPGRADES
- if (upgrade) {
- assert(pkt); //Upgrades need to be fixed
- pkt->flags &= ~CACHE_LINE_FILL;
+ if (pkt->memInhibitAsserted()) {
+ assert(!pkt->req->isUncacheable());
+ // have to invalidate ourselves and any lower caches even if
+ // upper cache will be responding
+ if (pkt->isInvalidate()) {
BlkType *blk = tags->findBlock(pkt->getAddr());
- CacheBlk::State old_state = (blk) ? blk->status : 0;
- CacheBlk::State new_state = coherence->getNewState(pkt,old_state);
- //Set the state on the upgrade
- std::memcpy(pkt->getPtr<uint8_t>(), blk->data, blkSize);
- PacketList writebacks;
- handleFill(blk, mshr, new_state, writebacks, pkt);
- assert(writebacks.empty());
- if (old_state != new_state)
- DPRINTF(Cache, "Block addr %x moving from state "
- "%i to %i\n", pkt->getAddr(), old_state, new_state);
- missQueue->handleResponse(pkt, curTick + hitLatency);
+ if (blk && blk->isValid()) {
+ tags->invalidateBlk(blk);
+ DPRINTF(Cache, "rcvd mem-inhibited %s on 0x%x: invalidating\n",
+ pkt->cmdString(), pkt->getAddr());
+ }
+ if (!last_level_cache) {
+ DPRINTF(Cache, "forwarding mem-inhibited %s on 0x%x\n",
+ pkt->cmdString(), pkt->getAddr());
+ lat += memSidePort->sendAtomic(pkt);
+ }
+ } else {
+ DPRINTF(Cache, "rcvd mem-inhibited %s on 0x%x: not responding\n",
+ pkt->cmdString(), pkt->getAddr());
}
- } else if (pkt && !pkt->req->isUncacheable()) {
- pkt->flags &= ~NACKED_LINE;
- SIGNAL_NACK_HACK = false;
- pkt->flags &= ~SATISFIED;
- pkt->flags &= ~SNOOP_COMMIT;
-
-//Rmove copy from mshr
- delete mshr->pkt;
- mshr->pkt = pkt;
- missQueue->restoreOrigCmd(pkt);
+ return lat;
}
-}
-template<class TagStore, class Coherence>
-void
-Cache<TagStore,Coherence>::handleResponse(PacketPtr &pkt)
-{
+ // should assert here that there are no outstanding MSHRs or
+ // writebacks... that would mean that someone used an atomic
+ // access in timing mode
+
BlkType *blk = NULL;
- if (pkt->senderState) {
- //Delete temp copy in MSHR, restore it.
- delete ((MSHR*)pkt->senderState)->pkt;
- ((MSHR*)pkt->senderState)->pkt = pkt;
- if (pkt->result == Packet::Nacked) {
- //pkt->reinitFromRequest();
- warn("NACKs from devices not connected to the same bus "
- "not implemented\n");
- return;
- }
- if (pkt->result == Packet::BadAddress) {
- //Make the response a Bad address and send it
+
+ if (!access(pkt, blk, lat)) {
+ // MISS
+ PacketPtr busPkt = getBusPacket(pkt, blk, pkt->needsExclusive());
+
+ bool isCacheFill = (busPkt != NULL);
+
+ if (busPkt == NULL) {
+ // just forwarding the same request to the next level
+ // no local cache operation involved
+ busPkt = pkt;
}
-// MemDebug::cacheResponse(pkt);
- DPRINTF(Cache, "Handling response to %x\n", pkt->getAddr());
-
- if (pkt->isCacheFill() && !pkt->isNoAllocate()) {
- DPRINTF(Cache, "Block for addr %x being updated in Cache\n",
- pkt->getAddr());
- blk = tags->findBlock(pkt->getAddr());
- CacheBlk::State old_state = (blk) ? blk->status : 0;
+
+ DPRINTF(Cache, "Sending an atomic %s for %x\n",
+ busPkt->cmdString(), busPkt->getAddr());
+
+#if TRACING_ON
+ CacheBlk::State old_state = blk ? blk->status : 0;
+#endif
+
+ lat += memSidePort->sendAtomic(busPkt);
+
+ DPRINTF(Cache, "Receive response: %s for addr %x in state %i\n",
+ busPkt->cmdString(), busPkt->getAddr(), old_state);
+
+ if (isCacheFill) {
PacketList writebacks;
- CacheBlk::State new_state = coherence->getNewState(pkt,old_state);
- blk = handleFill(blk, (MSHR*)pkt->senderState,
- new_state, writebacks, pkt);
- if (old_state != new_state)
- DPRINTF(Cache, "Block addr %x moving from state %i to %i\n",
- pkt->getAddr(), old_state, new_state);
- while (!writebacks.empty()) {
+ blk = handleFill(busPkt, blk, writebacks);
+ satisfyCpuSideRequest(pkt, blk);
+ delete busPkt;
+
+ // Handle writebacks if needed
+ while (!writebacks.empty()){
PacketPtr wbPkt = writebacks.front();
- missQueue->doWriteback(wbPkt);
+ memSidePort->sendAtomic(wbPkt);
writebacks.pop_front();
delete wbPkt;
}
}
- missQueue->handleResponse(pkt, curTick + hitLatency);
}
-}
-template<class TagStore, class Coherence>
-PacketPtr
-Cache<TagStore,Coherence>::getCoherencePacket()
-{
- return coherence->getPacket();
+ // We now have the block one way or another (hit or completed miss)
+
+ if (pkt->needsResponse()) {
+ pkt->makeAtomicResponse();
+ }
+
+ return lat;
}
-template<class TagStore, class Coherence>
+
+template<class TagStore>
void
-Cache<TagStore,Coherence>::sendCoherenceResult(PacketPtr &pkt,
- MSHR *cshr,
- bool success)
+Cache<TagStore>::functionalAccess(PacketPtr pkt,
+ CachePort *otherSidePort)
{
- coherence->sendResult(pkt, cshr, success);
+ Addr blk_addr = pkt->getAddr() & ~(blkSize - 1);
+ BlkType *blk = tags->findBlock(pkt->getAddr());
+
+ if (blk && pkt->checkFunctional(blk_addr, blkSize, blk->data)) {
+ // request satisfied from block
+ return;
+ }
+
+ // Need to check for outstanding misses and writes; if neither one
+ // satisfies, then forward to other side of cache.
+ if (!(mshrQueue.checkFunctional(pkt, blk_addr) ||
+ writeBuffer.checkFunctional(pkt, blk_addr))) {
+ otherSidePort->checkAndSendFunctional(pkt);
+ }
}
-template<class TagStore, class Coherence>
+/////////////////////////////////////////////////////
+//
+// Response handling: responses from the memory side
+//
+/////////////////////////////////////////////////////
+
+
+template<class TagStore>
void
-Cache<TagStore,Coherence>::snoop(PacketPtr &pkt)
+Cache<TagStore>::handleResponse(PacketPtr pkt)
{
- if (pkt->req->isUncacheable()) {
- //Can't get a hit on an uncacheable address
- //Revisit this for multi level coherence
+ Tick time = curTick + hitLatency;
+ MSHR *mshr = dynamic_cast<MSHR*>(pkt->senderState);
+ assert(mshr);
+
+ if (pkt->wasNacked()) {
+ //pkt->reinitFromRequest();
+ warn("NACKs from devices not connected to the same bus "
+ "not implemented\n");
return;
}
+ assert(!pkt->isError());
+ DPRINTF(Cache, "Handling response to %x\n", pkt->getAddr());
- //Send a timing (true) invalidate up if the protocol calls for it
- if (coherence->propogateInvalidate(pkt, true)) {
- //Temp hack, we had a functional read hit in the L1, mark as success
- pkt->flags |= SATISFIED;
- pkt->result = Packet::Success;
- respondToSnoop(pkt, curTick + hitLatency);
- return;
+ MSHRQueue *mq = mshr->queue;
+ bool wasFull = mq->isFull();
+
+ if (mshr == noTargetMSHR) {
+ // we always clear at least one target
+ clearBlocked(Blocked_NoTargets);
+ noTargetMSHR = NULL;
}
- Addr blk_addr = pkt->getAddr() & ~(Addr(blkSize-1));
+ // Initial target is used just for stats
+ MSHR::Target *initial_tgt = mshr->getTarget();
BlkType *blk = tags->findBlock(pkt->getAddr());
- MSHR *mshr = missQueue->findMSHR(blk_addr);
- if (coherence->hasProtocol() || pkt->isInvalidate()) {
- //@todo Move this into handle bus req
- //If we find an mshr, and it is in service, we need to NACK or
- //invalidate
- if (mshr) {
- if (mshr->inService) {
- if ((mshr->pkt->isInvalidate() || !mshr->pkt->isCacheFill())
- && (pkt->cmd != MemCmd::InvalidateReq
- && pkt->cmd != MemCmd::WriteInvalidateReq)) {
- //If the outstanding request was an invalidate
- //(upgrade,readex,..) Then we need to ACK the request
- //until we get the data Also NACK if the outstanding
- //request is not a cachefill (writeback)
- assert(!(pkt->flags & SATISFIED));
- pkt->flags |= SATISFIED;
- pkt->flags |= NACKED_LINE;
- SIGNAL_NACK_HACK = true;
- ///@todo NACK's from other levels
- //warn("NACKs from devices not connected to the same bus "
- //"not implemented\n");
- //respondToSnoop(pkt, curTick + hitLatency);
- return;
- }
- else {
- //The supplier will be someone else, because we are
- //waiting for the data. This should cause this cache to
- //be forced to go to the shared state, not the exclusive
- //even though the shared line won't be asserted. But for
- //now we will just invlidate ourselves and allow the other
- //cache to go into the exclusive state. @todo Make it so
- //a read to a pending read doesn't invalidate. @todo Make
- //it so that a read to a pending read can't be exclusive
- //now.
-
- //Set the address so find match works
- //panic("Don't have invalidates yet\n");
- invalidatePkt->addrOverride(pkt->getAddr());
-
- //Append the invalidate on
- missQueue->addTarget(mshr,invalidatePkt);
- DPRINTF(Cache, "Appending Invalidate to addr: %x\n",
- pkt->getAddr());
- return;
+ int stats_cmd_idx = initial_tgt->pkt->cmdToIndex();
+ Tick miss_latency = curTick - initial_tgt->recvTime;
+ PacketList writebacks;
+
+ if (pkt->req->isUncacheable()) {
+ mshr_uncacheable_lat[stats_cmd_idx][0/*pkt->req->getThreadNum()*/] +=
+ miss_latency;
+ } else {
+ mshr_miss_latency[stats_cmd_idx][0/*pkt->req->getThreadNum()*/] +=
+ miss_latency;
+ }
+
+ if (mshr->isCacheFill) {
+ DPRINTF(Cache, "Block for addr %x being updated in Cache\n",
+ pkt->getAddr());
+
+ // give mshr a chance to do some dirty work
+ mshr->handleFill(pkt, blk);
+
+ blk = handleFill(pkt, blk, writebacks);
+ assert(blk != NULL);
+ }
+
+ // First offset for critical word first calculations
+ int initial_offset = 0;
+
+ if (mshr->hasTargets()) {
+ initial_offset = mshr->getTarget()->pkt->getOffset(blkSize);
+ }
+
+ while (mshr->hasTargets()) {
+ MSHR::Target *target = mshr->getTarget();
+
+ if (target->isCpuSide()) {
+ Tick completion_time;
+ if (blk != NULL) {
+ satisfyCpuSideRequest(target->pkt, blk);
+ // How many bytes past the first request is this one
+ int transfer_offset =
+ target->pkt->getOffset(blkSize) - initial_offset;
+ if (transfer_offset < 0) {
+ transfer_offset += blkSize;
}
- }
- }
- //We also need to check the writeback buffers and handle those
- std::vector<MSHR *> writebacks;
- if (missQueue->findWrites(blk_addr, writebacks)) {
- DPRINTF(Cache, "Snoop hit in writeback to addr: %x\n",
- pkt->getAddr());
-
- //Look through writebacks for any non-uncachable writes, use that
- for (int i=0; i<writebacks.size(); i++) {
- mshr = writebacks[i];
-
- if (!mshr->pkt->req->isUncacheable()) {
- if (pkt->isRead()) {
- //Only Upgrades don't get here
- //Supply the data
- assert(!(pkt->flags & SATISFIED));
- pkt->flags |= SATISFIED;
-
- //If we are in an exclusive protocol, make it ask again
- //to get write permissions (upgrade), signal shared
- pkt->flags |= SHARED_LINE;
-
- assert(pkt->isRead());
- Addr offset = pkt->getAddr() & (blkSize - 1);
- assert(offset < blkSize);
- assert(pkt->getSize() <= blkSize);
- assert(offset + pkt->getSize() <=blkSize);
- std::memcpy(pkt->getPtr<uint8_t>(), mshr->pkt->getPtr<uint8_t>() + offset, pkt->getSize());
-
- respondToSnoop(pkt, curTick + hitLatency);
- }
-
- if (pkt->isInvalidate()) {
- //This must be an upgrade or other cache will take
- //ownership
- missQueue->markInService(mshr->pkt, mshr);
- }
- return;
+
+ // If critical word (no offset) return first word time
+ completion_time = tags->getHitLatency() +
+ transfer_offset ? pkt->finishTime : pkt->firstWordTime;
+
+ assert(!target->pkt->req->isUncacheable());
+ missLatency[target->pkt->cmdToIndex()][0/*pkt->req->getThreadNum()*/] +=
+ completion_time - target->recvTime;
+ } else {
+ // not a cache fill, just forwarding response
+ completion_time = tags->getHitLatency() + pkt->finishTime;
+ if (pkt->isRead()) {
+ target->pkt->setData(pkt->getPtr<uint8_t>());
}
}
+ target->pkt->makeTimingResponse();
+ cpuSidePort->respond(target->pkt, completion_time);
+ } else {
+ // response to snoop request
+ DPRINTF(Cache, "processing deferred snoop...\n");
+ handleSnoop(target->pkt, blk, true, true);
}
- }
- CacheBlk::State new_state;
- bool satisfy = coherence->handleBusRequest(pkt,blk,mshr, new_state);
- if (blk && mshr && !mshr->inService && new_state == 0) {
- //There was a outstanding write to a shared block, not need ReadEx
- //not update, so change No Allocate param in MSHR
- mshr->pkt->flags &= ~NO_ALLOCATE;
+ mshr->popTarget();
}
- if (satisfy) {
- DPRINTF(Cache, "snooped a %s request for addr %x and "
- "now supplying data, new state is %i\n",
- pkt->cmdString(), blk_addr, new_state);
+ if (mshr->promoteDeferredTargets()) {
+ MSHRQueue *mq = mshr->queue;
+ mq->markPending(mshr);
+ requestMemSideBus((RequestCause)mq->index, pkt->finishTime);
+ } else {
+ mq->deallocate(mshr);
+ if (wasFull && !mq->isFull()) {
+ clearBlocked((BlockedCause)mq->index);
+ }
+ }
- handleSnoop(blk, new_state, pkt);
- respondToSnoop(pkt, curTick + hitLatency);
- return;
+ // copy writebacks to write buffer
+ while (!writebacks.empty()) {
+ PacketPtr wbPkt = writebacks.front();
+ allocateWriteBuffer(wbPkt, time, true);
+ writebacks.pop_front();
+ }
+ // if we used temp block, clear it out
+ if (blk == tempBlock) {
+ if (blk->isDirty()) {
+ allocateWriteBuffer(writebackBlk(blk), time, true);
+ }
+ tags->invalidateBlk(blk);
}
- if (blk)
- DPRINTF(Cache, "snooped a %s request for addr %x, "
- "new state is %i\n", pkt->cmdString(), blk_addr, new_state);
- handleSnoop(blk, new_state);
+ delete pkt;
}
-template<class TagStore, class Coherence>
-void
-Cache<TagStore,Coherence>::snoopResponse(PacketPtr &pkt)
+
+
+
+template<class TagStore>
+PacketPtr
+Cache<TagStore>::writebackBlk(BlkType *blk)
{
- //Need to handle the response, if NACKED
- if (pkt->flags & NACKED_LINE) {
- //Need to mark it as not in service, and retry for bus
- assert(0); //Yeah, we saw a NACK come through
-
- //For now this should never get called, we return false when we see a
- //NACK instead, by doing this we allow the bus_blocked mechanism to
- //handle the retry For now it retrys in just 2 cycles, need to figure
- //out how to change that Eventually we will want to also have success
- //come in as a parameter Need to make sure that we handle the
- //functionality that happens on successufl return of the sendAddr
- //function
- }
+ assert(blk && blk->isValid() && blk->isDirty());
+
+ writebacks[0/*pkt->req->getThreadNum()*/]++;
+
+ Request *writebackReq =
+ new Request(tags->regenerateBlkAddr(blk->tag, blk->set), blkSize, 0);
+ PacketPtr writeback = new Packet(writebackReq, MemCmd::Writeback, -1);
+ writeback->allocate();
+ std::memcpy(writeback->getPtr<uint8_t>(), blk->data, blkSize);
+
+ blk->status &= ~BlkDirty;
+ return writeback;
}
-/**
- * @todo Fix to not assume write allocate
- */
-template<class TagStore, class Coherence>
-Tick
-Cache<TagStore,Coherence>::probe(PacketPtr &pkt, bool update,
- CachePort* otherSidePort)
+// Note that the reason we return a list of writebacks rather than
+// inserting them directly in the write buffer is that this function
+// is called by both atomic and timing-mode accesses, and in atomic
+// mode we don't mess with the write buffer (we just perform the
+// writebacks atomically once the original request is complete).
+template<class TagStore>
+typename Cache<TagStore>::BlkType*
+Cache<TagStore>::handleFill(PacketPtr pkt, BlkType *blk,
+ PacketList &writebacks)
{
-// MemDebug::cacheProbe(pkt);
- if (!pkt->req->isUncacheable()) {
- if (pkt->isInvalidate() && !pkt->isRead() && !pkt->isWrite()) {
- //Upgrade or Invalidate, satisfy it, don't forward
- DPRINTF(Cache, "%s %x ?\n", pkt->cmdString(), pkt->getAddr());
- pkt->flags |= SATISFIED;
- return 0;
+ Addr addr = pkt->getAddr();
+#if TRACING_ON
+ CacheBlk::State old_state = blk ? blk->status : 0;
+#endif
+
+ if (blk == NULL) {
+ // better have read new data...
+ assert(pkt->isRead());
+
+ // need to do a replacement
+ blk = tags->findReplacement(addr, writebacks);
+ if (blk->isValid()) {
+ Addr repl_addr = tags->regenerateBlkAddr(blk->tag, blk->set);
+ MSHR *repl_mshr = mshrQueue.findMatch(repl_addr);
+ if (repl_mshr) {
+ // must be an outstanding upgrade request on block
+ // we're about to replace...
+ assert(!blk->isWritable());
+ assert(repl_mshr->needsExclusive());
+ // too hard to replace block with transient state;
+ // just use temporary storage to complete the current
+ // request and then get rid of it
+ assert(!tempBlock->isValid());
+ blk = tempBlock;
+ tempBlock->set = tags->extractSet(addr);
+ DPRINTF(Cache, "using temp block for %x\n", addr);
+ } else {
+ DPRINTF(Cache, "replacement: replacing %x with %x: %s\n",
+ repl_addr, addr,
+ blk->isDirty() ? "writeback" : "clean");
+
+ if (blk->isDirty()) {
+ // Save writeback packet for handling by caller
+ writebacks.push_back(writebackBlk(blk));
+ }
+ }
}
+
+ blk->tag = tags->extractTag(addr);
+ } else {
+ // existing block... probably an upgrade
+ assert(blk->tag == tags->extractTag(addr));
+ // either we're getting new data or the block should already be valid
+ assert(pkt->isRead() || blk->isValid());
}
- if (!update && (otherSidePort == cpuSidePort)) {
- // Still need to change data in all locations.
- otherSidePort->checkAndSendFunctional(pkt);
- if (pkt->isRead() && pkt->result == Packet::Success)
- return 0;
+ if (pkt->needsExclusive() || !pkt->sharedAsserted()) {
+ blk->status = BlkValid | BlkWritable;
+ } else {
+ blk->status = BlkValid;
}
- PacketList writebacks;
- int lat;
+ DPRINTF(Cache, "Block addr %x moving from state %i to %i\n",
+ addr, old_state, blk->status);
- BlkType *blk = handleAccess(pkt, lat, writebacks, update);
+ // if we got new data, copy it in
+ if (pkt->isRead()) {
+ std::memcpy(blk->data, pkt->getPtr<uint8_t>(), blkSize);
+ }
- DPRINTF(Cache, "%s %x %s\n", pkt->cmdString(),
- pkt->getAddr(), (blk) ? "hit" : "miss");
+ blk->whenReady = pkt->finishTime;
+ return blk;
+}
- // Need to check for outstanding misses and writes
- Addr blk_addr = pkt->getAddr() & ~(blkSize - 1);
- // There can only be one matching outstanding miss.
- MSHR* mshr = missQueue->findMSHR(blk_addr);
+/////////////////////////////////////////////////////
+//
+// Snoop path: requests coming in from the memory side
+//
+/////////////////////////////////////////////////////
- // There can be many matching outstanding writes.
- std::vector<MSHR*> writes;
- missQueue->findWrites(blk_addr, writes);
+template<class TagStore>
+void
+Cache<TagStore>::doTimingSupplyResponse(PacketPtr req_pkt,
+ uint8_t *blk_data,
+ bool already_copied)
+{
+ // timing-mode snoop responses require a new packet, unless we
+ // already made a copy...
+ PacketPtr pkt = already_copied ? req_pkt : new Packet(req_pkt, true);
+ if (!req_pkt->isInvalidate()) {
+ // note that we're ignoring the shared flag on req_pkt... it's
+ // basically irrelveant, as we'll always assert shared unless
+ // it's an exclusive request, in which case the shared line
+ // should never be asserted1
+ pkt->assertShared();
+ }
+ pkt->allocate();
+ pkt->makeTimingResponse();
+ if (pkt->isRead()) {
+ pkt->setDataFromBlock(blk_data, blkSize);
+ }
+ memSidePort->respond(pkt, curTick + hitLatency);
+}
- if (!update) {
- bool notDone = !(pkt->flags & SATISFIED); //Hit in cache (was a block)
- // Check for data in MSHR and writebuffer.
- if (mshr) {
- MSHR::TargetList *targets = mshr->getTargetList();
- MSHR::TargetList::iterator i = targets->begin();
- MSHR::TargetList::iterator end = targets->end();
- for (; i != end && notDone; ++i) {
- PacketPtr target = *i;
- // If the target contains data, and it overlaps the
- // probed request, need to update data
- if (target->intersect(pkt)) {
- DPRINTF(Cache, "Functional %s access to blk_addr %x intersects a MSHR\n",
- pkt->cmdString(), blk_addr);
- notDone = fixPacket(pkt, target);
- }
- }
- }
- for (int i = 0; i < writes.size() && notDone; ++i) {
- PacketPtr write = writes[i]->pkt;
- if (write->intersect(pkt)) {
- DPRINTF(Cache, "Functional %s access to blk_addr %x intersects a writeback\n",
- pkt->cmdString(), blk_addr);
- notDone = fixPacket(pkt, write);
- }
+template<class TagStore>
+void
+Cache<TagStore>::handleSnoop(PacketPtr pkt, BlkType *blk,
+ bool is_timing, bool is_deferred)
+{
+ assert(pkt->isRequest());
+
+ // first propagate snoop upward to see if anyone above us wants to
+ // handle it. save & restore packet src since it will get
+ // rewritten to be relative to cpu-side bus (if any)
+ bool alreadyResponded = pkt->memInhibitAsserted();
+ if (is_timing) {
+ Packet *snoopPkt = new Packet(pkt, true); // clear flags
+ snoopPkt->setExpressSnoop();
+ snoopPkt->senderState = new ForwardResponseRecord(pkt, this);
+ cpuSidePort->sendTiming(snoopPkt);
+ if (snoopPkt->memInhibitAsserted()) {
+ // cache-to-cache response from some upper cache
+ assert(!alreadyResponded);
+ pkt->assertMemInhibit();
+ } else {
+ delete snoopPkt->senderState;
}
- if (notDone && otherSidePort == memSidePort) {
- otherSidePort->checkAndSendFunctional(pkt);
- assert(pkt->result == Packet::Success);
+ if (snoopPkt->sharedAsserted()) {
+ pkt->assertShared();
}
- return 0;
- } else if (!blk && !(pkt->flags & SATISFIED)) {
- // update the cache state and statistics
- if (mshr || !writes.empty()){
- // Can't handle it, return request unsatisfied.
- panic("Atomic access ran into outstanding MSHR's or WB's!");
+ delete snoopPkt;
+ } else {
+ int origSrc = pkt->getSrc();
+ cpuSidePort->sendAtomic(pkt);
+ if (!alreadyResponded && pkt->memInhibitAsserted()) {
+ // cache-to-cache response from some upper cache:
+ // forward response to original requester
+ assert(pkt->isResponse());
}
- if (!pkt->req->isUncacheable() /*Uncacheables just go through*/
- && (pkt->cmd != MemCmd::Writeback)/*Writebacks on miss fall through*/) {
- // Fetch the cache block to fill
- BlkType *blk = tags->findBlock(pkt->getAddr());
- MemCmd temp_cmd =
- coherence->getBusCmd(pkt->cmd, (blk) ? blk->status : 0);
-
- PacketPtr busPkt = new Packet(pkt->req,temp_cmd, -1, blkSize);
-
- busPkt->allocate();
+ pkt->setSrc(origSrc);
+ }
- busPkt->time = curTick;
+ if (!blk || !blk->isValid()) {
+ return;
+ }
- DPRINTF(Cache, "Sending a atomic %s for %x\n",
- busPkt->cmdString(), busPkt->getAddr());
+ // we may end up modifying both the block state and the packet (if
+ // we respond in atomic mode), so just figure out what to do now
+ // and then do it later
+ bool respond = blk->isDirty() && pkt->needsResponse();
+ bool have_exclusive = blk->isWritable();
+ bool invalidate = pkt->isInvalidate();
+
+ if (pkt->isRead() && !pkt->isInvalidate()) {
+ assert(!pkt->needsExclusive());
+ pkt->assertShared();
+ int bits_to_clear = BlkWritable;
+ const bool haveOwnershipState = true; // for now
+ if (!haveOwnershipState) {
+ // if we don't support pure ownership (dirty && !writable),
+ // have to clear dirty bit here, assume memory snarfs data
+ // on cache-to-cache xfer
+ bits_to_clear |= BlkDirty;
+ }
+ blk->status &= ~bits_to_clear;
+ }
- lat = memSidePort->sendAtomic(busPkt);
+ if (respond) {
+ assert(!pkt->memInhibitAsserted());
+ pkt->assertMemInhibit();
+ if (have_exclusive) {
+ pkt->setSupplyExclusive();
+ }
+ if (is_timing) {
+ doTimingSupplyResponse(pkt, blk->data, is_deferred);
+ } else {
+ pkt->makeAtomicResponse();
+ pkt->setDataFromBlock(blk->data, blkSize);
+ }
+ }
- //Be sure to flip the response to a request for coherence
- if (busPkt->needsResponse()) {
- busPkt->makeAtomicResponse();
- }
+ // Do this last in case it deallocates block data or something
+ // like that
+ if (invalidate) {
+ tags->invalidateBlk(blk);
+ }
-/* if (!(busPkt->flags & SATISFIED)) {
-// blocked at a higher level, just return
-return 0;
+ DPRINTF(Cache, "snooped a %s request for addr %x, %snew state is %i\n",
+ pkt->cmdString(), blockAlign(pkt->getAddr()),
+ respond ? "responding, " : "", blk->status);
}
-*/ misses[pkt->cmdToIndex()][0/*pkt->req->getThreadNum()*/]++;
- CacheBlk::State old_state = (blk) ? blk->status : 0;
- CacheBlk::State new_state =
- coherence->getNewState(busPkt, old_state);
- DPRINTF(Cache, "Receive response: %s for addr %x in state %i\n",
- busPkt->cmdString(), busPkt->getAddr(), old_state);
+template<class TagStore>
+void
+Cache<TagStore>::snoopTiming(PacketPtr pkt)
+{
+ // Note that some deferred snoops don't have requests, since the
+ // original access may have already completed
+ if ((pkt->req && pkt->req->isUncacheable()) ||
+ pkt->cmd == MemCmd::Writeback) {
+ //Can't get a hit on an uncacheable address
+ //Revisit this for multi level coherence
+ return;
+ }
- handleFill(blk, busPkt, new_state, writebacks, pkt);
- if (old_state != new_state)
- DPRINTF(Cache, "Block addr %x moving from state "
- "%i to %i\n", busPkt->getAddr(), old_state, new_state);
- //Free the packet
- delete busPkt;
+ BlkType *blk = tags->findBlock(pkt->getAddr());
- // Handle writebacks if needed
- while (!writebacks.empty()){
- PacketPtr wbPkt = writebacks.front();
- memSidePort->sendAtomic(wbPkt);
- writebacks.pop_front();
- delete wbPkt;
+ Addr blk_addr = pkt->getAddr() & ~(Addr(blkSize-1));
+ MSHR *mshr = mshrQueue.findMatch(blk_addr);
+
+ // Let the MSHR itself track the snoop and decide whether we want
+ // to go ahead and do the regular cache snoop
+ if (mshr && mshr->handleSnoop(pkt, order++)) {
+ DPRINTF(Cache, "Deferring snoop on in-service MSHR to blk %x\n",
+ blk_addr);
+ if (mshr->getNumTargets() > numTarget)
+ warn("allocating bonus target for snoop"); //handle later
+ return;
+ }
+
+ //We also need to check the writeback buffers and handle those
+ std::vector<MSHR *> writebacks;
+ if (writeBuffer.findMatches(blk_addr, writebacks)) {
+ DPRINTF(Cache, "Snoop hit in writeback to addr: %x\n",
+ pkt->getAddr());
+
+ //Look through writebacks for any non-uncachable writes, use that
+ for (int i=0; i<writebacks.size(); i++) {
+ mshr = writebacks[i];
+ assert(!mshr->isUncacheable());
+ assert(mshr->getNumTargets() == 1);
+ PacketPtr wb_pkt = mshr->getTarget()->pkt;
+ assert(wb_pkt->cmd == MemCmd::Writeback);
+
+ assert(!pkt->memInhibitAsserted());
+ pkt->assertMemInhibit();
+ if (!pkt->needsExclusive()) {
+ pkt->assertShared();
+ } else {
+ // if we're not asserting the shared line, we need to
+ // invalidate our copy. we'll do that below as long as
+ // the packet's invalidate flag is set...
+ assert(pkt->isInvalidate());
}
- return lat + hitLatency;
- } else {
- return memSidePort->sendAtomic(pkt);
- }
- } else {
- if (blk) {
- // There was a cache hit.
- // Handle writebacks if needed
- while (!writebacks.empty()){
- PacketPtr wbPkt = writebacks.front();
- memSidePort->sendAtomic(wbPkt);
- writebacks.pop_front();
- delete wbPkt;
+ doTimingSupplyResponse(pkt, wb_pkt->getPtr<uint8_t>(), false);
+
+ if (pkt->isInvalidate()) {
+ // Invalidation trumps our writeback... discard here
+ markInService(mshr);
}
- hits[pkt->cmdToIndex()][0/*pkt->req->getThreadNum()*/]++;
+ // If this was a shared writeback, there may still be
+ // other shared copies above that require invalidation.
+ // We could be more selective and return here if the
+ // request is non-exclusive or if the writeback is
+ // exclusive.
+ break;
}
-
- return hitLatency;
}
- return 0;
+ handleSnoop(pkt, blk, true, false);
}
-template<class TagStore, class Coherence>
+
+template<class TagStore>
Tick
-Cache<TagStore,Coherence>::snoopProbe(PacketPtr &pkt)
+Cache<TagStore>::snoopAtomic(PacketPtr pkt)
{
- //Send a atomic (false) invalidate up if the protocol calls for it
- if (coherence->propogateInvalidate(pkt, false)) {
- //Temp hack, we had a functional read hit in the L1, mark as success
- pkt->flags |= SATISFIED;
- pkt->result = Packet::Success;
+ if (pkt->req->isUncacheable() || pkt->cmd == MemCmd::Writeback) {
+ // Can't get a hit on an uncacheable address
+ // Revisit this for multi level coherence
return hitLatency;
}
- Addr blk_addr = pkt->getAddr() & ~(Addr(blkSize-1));
BlkType *blk = tags->findBlock(pkt->getAddr());
- MSHR *mshr = missQueue->findMSHR(blk_addr);
- CacheBlk::State new_state = 0;
- bool satisfy = coherence->handleBusRequest(pkt,blk,mshr, new_state);
- if (satisfy) {
- DPRINTF(Cache, "Cache snooped a %s request for addr %x, "
- "supplying data, new state is %i\n",
- pkt->cmdString(), blk_addr, new_state);
-
- handleSnoop(blk, new_state, pkt);
- return hitLatency;
- }
- if (blk)
- DPRINTF(Cache, "Cache snooped a %s request for addr %x, "
- "new state is %i\n",
- pkt->cmdString(), blk_addr, new_state);
- handleSnoop(blk, new_state);
- return 0;
+ handleSnoop(pkt, blk, false, false);
+ return hitLatency;
}
-template<class TagStore, class Coherence>
-Port *
-Cache<TagStore,Coherence>::getPort(const std::string &if_name, int idx)
+
+template<class TagStore>
+MSHR *
+Cache<TagStore>::getNextMSHR()
{
- if (if_name == "" || if_name == "cpu_side")
- {
- if (cpuSidePort == NULL) {
- cpuSidePort = new CpuSidePort(name() + "-cpu_side_port", this);
- sendEvent = new ResponseEvent(cpuSidePort);
+ // Check both MSHR queue and write buffer for potential requests
+ MSHR *miss_mshr = mshrQueue.getNextMSHR();
+ MSHR *write_mshr = writeBuffer.getNextMSHR();
+
+ // Now figure out which one to send... some cases are easy
+ if (miss_mshr && !write_mshr) {
+ return miss_mshr;
+ }
+ if (write_mshr && !miss_mshr) {
+ return write_mshr;
+ }
+
+ if (miss_mshr && write_mshr) {
+ // We have one of each... normally we favor the miss request
+ // unless the write buffer is full
+ if (writeBuffer.isFull() && writeBuffer.inServiceEntries == 0) {
+ // Write buffer is full, so we'd like to issue a write;
+ // need to search MSHR queue for conflicting earlier miss.
+ MSHR *conflict_mshr =
+ mshrQueue.findPending(write_mshr->addr, write_mshr->size);
+
+ if (conflict_mshr && conflict_mshr->order < write_mshr->order) {
+ // Service misses in order until conflict is cleared.
+ return conflict_mshr;
+ }
+
+ // No conflicts; issue write
+ return write_mshr;
}
- return cpuSidePort;
+
+ // Write buffer isn't full, but need to check it for
+ // conflicting earlier writeback
+ MSHR *conflict_mshr =
+ writeBuffer.findPending(miss_mshr->addr, miss_mshr->size);
+ if (conflict_mshr) {
+ // not sure why we don't check order here... it was in the
+ // original code but commented out.
+
+ // The only way this happens is if we are
+ // doing a write and we didn't have permissions
+ // then subsequently saw a writeback (owned got evicted)
+ // We need to make sure to perform the writeback first
+ // To preserve the dirty data, then we can issue the write
+
+ // should we return write_mshr here instead? I.e. do we
+ // have to flush writes in order? I don't think so... not
+ // for Alpha anyway. Maybe for x86?
+ return conflict_mshr;
+ }
+
+ // No conflicts; issue read
+ return miss_mshr;
}
- else if (if_name == "functional")
- {
- return new CpuSidePort(name() + "-cpu_side_funcport", this);
+
+ // fall through... no pending requests. Try a prefetch.
+ assert(!miss_mshr && !write_mshr);
+ if (!mshrQueue.isFull()) {
+ // If we have a miss queue slot, we can try a prefetch
+ PacketPtr pkt = prefetcher->getPacket();
+ if (pkt) {
+ // Update statistic on number of prefetches issued
+ // (hwpf_mshr_misses)
+ mshr_misses[pkt->cmdToIndex()][0/*pkt->req->getThreadNum()*/]++;
+ // Don't request bus, since we already have it
+ return allocateMissBuffer(pkt, curTick, false);
+ }
}
- else if (if_name == "mem_side")
- {
- if (memSidePort != NULL)
- panic("Already have a mem side for this cache\n");
- memSidePort = new MemSidePort(name() + "-mem_side_port", this);
- memSendEvent = new ResponseEvent(memSidePort);
- return memSidePort;
+
+ return NULL;
+}
+
+
+template<class TagStore>
+PacketPtr
+Cache<TagStore>::getTimingPacket()
+{
+ MSHR *mshr = getNextMSHR();
+
+ if (mshr == NULL) {
+ return NULL;
+ }
+
+ // use request from 1st target
+ PacketPtr tgt_pkt = mshr->getTarget()->pkt;
+ PacketPtr pkt = NULL;
+
+ if (mshr->isSimpleForward()) {
+ // no response expected, just forward packet as it is
+ assert(tags->findBlock(mshr->addr) == NULL);
+ pkt = tgt_pkt;
+ } else {
+ BlkType *blk = tags->findBlock(mshr->addr);
+ pkt = getBusPacket(tgt_pkt, blk, mshr->needsExclusive());
+
+ mshr->isCacheFill = (pkt != NULL);
+
+ if (pkt == NULL) {
+ // not a cache block request, but a response is expected
+ assert(!mshr->isSimpleForward());
+ // make copy of current packet to forward, keep current
+ // copy for response handling
+ pkt = new Packet(tgt_pkt);
+ pkt->allocate();
+ if (pkt->isWrite()) {
+ pkt->setData(tgt_pkt->getPtr<uint8_t>());
+ }
+ }
}
- else panic("Port name %s unrecognized\n", if_name);
+
+ assert(pkt != NULL);
+ pkt->senderState = mshr;
+ return pkt;
}
-template<class TagStore, class Coherence>
+
+///////////////
+//
+// CpuSidePort
+//
+///////////////
+
+template<class TagStore>
void
-Cache<TagStore,Coherence>::deletePortRefs(Port *p)
+Cache<TagStore>::CpuSidePort::
+getDeviceAddressRanges(AddrRangeList &resp, bool &snoop)
{
- if (cpuSidePort == p || memSidePort == p)
- panic("Can only delete functional ports\n");
-
- delete p;
+ // CPU side port doesn't snoop; it's a target only.
+ bool dummy;
+ otherPort->getPeerAddressRanges(resp, dummy);
+ snoop = false;
}
-template<class TagStore, class Coherence>
+template<class TagStore>
bool
-Cache<TagStore,Coherence>::CpuSidePort::recvTiming(PacketPtr pkt)
+Cache<TagStore>::CpuSidePort::recvTiming(PacketPtr pkt)
{
- assert(pkt->result != Packet::Nacked);
-
- if (!pkt->req->isUncacheable()
- && pkt->isInvalidate()
- && !pkt->isRead() && !pkt->isWrite()) {
- //Upgrade or Invalidate
- //Look into what happens if two slave caches on bus
- DPRINTF(Cache, "%s %x ?\n", pkt->cmdString(), pkt->getAddr());
-
- assert(!(pkt->flags & SATISFIED));
- pkt->flags |= SATISFIED;
- //Invalidates/Upgrades need no response if they get the bus
- return true;
- }
-
- if (pkt->isRequest() && blocked)
- {
+ // illegal to block responses... can lead to deadlock
+ if (pkt->isRequest() && !pkt->memInhibitAsserted() && blocked) {
DPRINTF(Cache,"Scheduling a retry while blocked\n");
mustSendRetry = true;
return false;
}
- if (pkt->isWrite() && (pkt->req->isLocked())) {
- pkt->req->setExtraData(1);
- }
- myCache()->access(pkt);
+ myCache()->timingAccess(pkt);
return true;
}
-template<class TagStore, class Coherence>
+
+template<class TagStore>
Tick
-Cache<TagStore,Coherence>::CpuSidePort::recvAtomic(PacketPtr pkt)
+Cache<TagStore>::CpuSidePort::recvAtomic(PacketPtr pkt)
{
- myCache()->probe(pkt, true, NULL);
- //TEMP ALWAYS SUCCES FOR NOW
- pkt->result = Packet::Success;
- //Fix this timing info
- return myCache()->hitLatency;
+ return myCache()->atomicAccess(pkt);
}
-template<class TagStore, class Coherence>
+
+template<class TagStore>
void
-Cache<TagStore,Coherence>::CpuSidePort::recvFunctional(PacketPtr pkt)
+Cache<TagStore>::CpuSidePort::recvFunctional(PacketPtr pkt)
{
- if (checkFunctional(pkt)) {
- //TEMP USE CPU?THREAD 0 0
- pkt->req->setThreadContext(0,0);
-
- myCache()->probe(pkt, false, cache->memSidePort);
- //TEMP ALWAYS SUCCESFUL FOR NOW
- pkt->result = Packet::Success;
+ if (!checkFunctional(pkt)) {
+ myCache()->functionalAccess(pkt, cache->memSidePort);
}
}
-template<class TagStore, class Coherence>
+template<class TagStore>
+Cache<TagStore>::
+CpuSidePort::CpuSidePort(const std::string &_name,
+ Cache<TagStore> *_cache)
+ : BaseCache::CachePort(_name, _cache)
+{
+}
+
+///////////////
+//
+// MemSidePort
+//
+///////////////
+
+template<class TagStore>
+void
+Cache<TagStore>::MemSidePort::
+getDeviceAddressRanges(AddrRangeList &resp, bool &snoop)
+{
+ otherPort->getPeerAddressRanges(resp, snoop);
+ // Memory-side port always snoops, so unconditionally set flag for
+ // caller.
+ snoop = true;
+}
+
+
+template<class TagStore>
bool
-Cache<TagStore,Coherence>::MemSidePort::recvTiming(PacketPtr pkt)
+Cache<TagStore>::MemSidePort::recvTiming(PacketPtr pkt)
{
// this needs to be fixed so that the cache updates the mshr and sends the
// packet back out on the link, but it probably won't happen so until this
// gets fixed, just panic when it does
- if (pkt->result == Packet::Nacked)
+ if (pkt->wasNacked())
panic("Need to implement cache resending nacked packets!\n");
- if (pkt->isRequest() && blocked)
- {
+ if (pkt->isRequest() && blocked) {
DPRINTF(Cache,"Scheduling a retry while blocked\n");
mustSendRetry = true;
return false;
}
- if (pkt->isResponse())
+ if (pkt->isResponse()) {
myCache()->handleResponse(pkt);
- else {
- //Check if we should do the snoop
- if (pkt->flags & SNOOP_COMMIT)
- myCache()->snoop(pkt);
+ } else {
+ myCache()->snoopTiming(pkt);
}
return true;
}
-template<class TagStore, class Coherence>
+
+template<class TagStore>
Tick
-Cache<TagStore,Coherence>::MemSidePort::recvAtomic(PacketPtr pkt)
+Cache<TagStore>::MemSidePort::recvAtomic(PacketPtr pkt)
{
- if (pkt->isResponse())
- myCache()->handleResponse(pkt);
- else
- return myCache()->snoopProbe(pkt);
- //Fix this timing info
- return myCache()->hitLatency;
+ // in atomic mode, responses go back to the sender via the
+ // function return from sendAtomic(), not via a separate
+ // sendAtomic() from the responder. Thus we should never see a
+ // response packet in recvAtomic() (anywhere, not just here).
+ assert(!pkt->isResponse());
+ return myCache()->snoopAtomic(pkt);
}
-template<class TagStore, class Coherence>
+
+template<class TagStore>
void
-Cache<TagStore,Coherence>::MemSidePort::recvFunctional(PacketPtr pkt)
+Cache<TagStore>::MemSidePort::recvFunctional(PacketPtr pkt)
{
- myCache()->probe(pkt, false, cache->cpuSidePort);
- if (pkt->result != Packet::Success)
- checkFunctional(pkt);
+ if (!checkFunctional(pkt)) {
+ myCache()->functionalAccess(pkt, cache->cpuSidePort);
+ }
}
-template<class TagStore, class Coherence>
-Cache<TagStore,Coherence>::
-CpuSidePort::CpuSidePort(const std::string &_name,
- Cache<TagStore,Coherence> *_cache)
- : BaseCache::CachePort(_name, _cache, true)
+
+template<class TagStore>
+void
+Cache<TagStore>::MemSidePort::sendPacket()
{
+ // if we have responses that are ready, they take precedence
+ if (deferredPacketReady()) {
+ bool success = sendTiming(transmitList.front().pkt);
+
+ if (success) {
+ //send successful, remove packet
+ transmitList.pop_front();
+ }
+
+ waitingOnRetry = !success;
+ } else {
+ // check for non-response packets (requests & writebacks)
+ PacketPtr pkt = myCache()->getTimingPacket();
+ if (pkt == NULL) {
+ // can happen if e.g. we attempt a writeback and fail, but
+ // before the retry, the writeback is eliminated because
+ // we snoop another cache's ReadEx.
+ waitingOnRetry = false;
+ } else {
+ MSHR *mshr = dynamic_cast<MSHR*>(pkt->senderState);
+
+ bool success = sendTiming(pkt);
+ DPRINTF(CachePort,
+ "Address %x was %s in sending the timing request\n",
+ pkt->getAddr(), success ? "successful" : "unsuccessful");
+
+ waitingOnRetry = !success;
+ if (waitingOnRetry) {
+ DPRINTF(CachePort, "now waiting on a retry\n");
+ if (!mshr->isSimpleForward()) {
+ delete pkt;
+ }
+ } else {
+ myCache()->markInService(mshr);
+ }
+ }
+ }
+
+
+ // tried to send packet... if it was successful (no retry), see if
+ // we need to rerequest bus or not
+ if (!waitingOnRetry) {
+ Tick nextReady = std::min(deferredPacketReadyTime(),
+ myCache()->nextMSHRReadyTime());
+ // @TODO: need to facotr in prefetch requests here somehow
+ if (nextReady != MaxTick) {
+ DPRINTF(CachePort, "more packets to send @ %d\n", nextReady);
+ sendEvent->schedule(std::max(nextReady, curTick + 1));
+ } else {
+ // no more to send right now: if we're draining, we may be done
+ if (drainEvent) {
+ drainEvent->process();
+ drainEvent = NULL;
+ }
+ }
+ }
+}
+
+template<class TagStore>
+void
+Cache<TagStore>::MemSidePort::recvRetry()
+{
+ assert(waitingOnRetry);
+ sendPacket();
}
-template<class TagStore, class Coherence>
-Cache<TagStore,Coherence>::
-MemSidePort::MemSidePort(const std::string &_name,
- Cache<TagStore,Coherence> *_cache)
- : BaseCache::CachePort(_name, _cache, false)
+
+template<class TagStore>
+void
+Cache<TagStore>::MemSidePort::processSendEvent()
{
+ assert(!waitingOnRetry);
+ sendPacket();
}
+
+template<class TagStore>
+Cache<TagStore>::
+MemSidePort::MemSidePort(const std::string &_name, Cache<TagStore> *_cache)
+ : BaseCache::CachePort(_name, _cache)
+{
+ // override default send event from SimpleTimingPort
+ delete sendEvent;
+ sendEvent = new SendEvent(this);
+}