summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/mem/abstract_mem.cc12
-rw-r--r--src/mem/cache/cache.hh18
-rw-r--r--src/mem/cache/cache_impl.hh434
-rw-r--r--src/mem/cache/prefetch/base.cc1
-rw-r--r--src/mem/coherent_xbar.cc19
-rw-r--r--src/mem/coherent_xbar.hh7
-rw-r--r--src/mem/dram_ctrl.cc7
-rw-r--r--src/mem/packet.cc2
-rw-r--r--src/mem/packet.hh23
-rwxr-xr-xsrc/mem/snoop_filter.cc12
10 files changed, 405 insertions, 130 deletions
diff --git a/src/mem/abstract_mem.cc b/src/mem/abstract_mem.cc
index ec1be04e1..4690a5d80 100644
--- a/src/mem/abstract_mem.cc
+++ b/src/mem/abstract_mem.cc
@@ -322,15 +322,21 @@ AbstractMemory::checkLockedAddrList(PacketPtr pkt)
void
AbstractMemory::access(PacketPtr pkt)
{
- assert(AddrRange(pkt->getAddr(),
- pkt->getAddr() + pkt->getSize() - 1).isSubset(range));
-
if (pkt->memInhibitAsserted()) {
DPRINTF(MemoryAccess, "mem inhibited on 0x%x: not responding\n",
pkt->getAddr());
return;
}
+ if (pkt->cmd == MemCmd::CleanEvict) {
+ DPRINTF(MemoryAccess, "CleanEvict on 0x%x: not responding\n",
+ pkt->getAddr());
+ return;
+ }
+
+ assert(AddrRange(pkt->getAddr(),
+ pkt->getAddr() + (pkt->getSize() - 1)).isSubset(range));
+
uint8_t *hostAddr = pmemAddr + pkt->getAddr() - range.start();
if (pkt->cmd == MemCmd::SwapReq) {
diff --git a/src/mem/cache/cache.hh b/src/mem/cache/cache.hh
index 24a067ece..4c70d3a40 100644
--- a/src/mem/cache/cache.hh
+++ b/src/mem/cache/cache.hh
@@ -246,6 +246,11 @@ class Cache : public BaseCache
bool recvTimingReq(PacketPtr pkt);
/**
+ * Insert writebacks into the write buffer
+ */
+ void doWritebacks(PacketList& writebacks, Tick forward_time);
+
+ /**
* Handles a response (cache line fill/write ack) from the bus.
* @param pkt The response packet
*/
@@ -308,6 +313,13 @@ class Cache : public BaseCache
*/
PacketPtr writebackBlk(CacheBlk *blk);
+ /**
+ * Create a CleanEvict request for the given block.
+ * @param blk The block to evict.
+ * @return The CleanEvict request for the block.
+ */
+ PacketPtr cleanEvictBlk(CacheBlk *blk);
+
void memWriteback();
void memInvalidate();
@@ -359,6 +371,12 @@ class Cache : public BaseCache
MSHR *getNextMSHR();
/**
+ * Send up a snoop request and find cached copies. If cached copies are
+ * found, set the BLOCK_CACHED flag in pkt.
+ */
+ bool isCachedAbove(const PacketPtr pkt) const;
+
+ /**
* Selects an outstanding request to service. Called when the
* cache gets granted the downstream bus in timing mode.
* @return The request to service, NULL if none found.
diff --git a/src/mem/cache/cache_impl.hh b/src/mem/cache/cache_impl.hh
index 9c5070ffa..117596d9b 100644
--- a/src/mem/cache/cache_impl.hh
+++ b/src/mem/cache/cache_impl.hh
@@ -334,6 +334,36 @@ Cache::access(PacketPtr pkt, CacheBlk *&blk, Cycles &lat,
pkt->getAddr(), pkt->getSize(), pkt->isSecure() ? "s" : "ns",
blk ? "hit " + blk->print() : "miss");
+
+ if (pkt->evictingBlock()) {
+ // We check for presence of block in above caches before issuing
+ // Writeback or CleanEvict to write buffer. Therefore the only
+ // possible cases can be of a CleanEvict packet coming from above
+ // encountering a Writeback generated in this cache peer cache and
+ // waiting in the write buffer. Cases of upper level peer caches
+ // generating CleanEvict and Writeback or simply CleanEvict and
+ // CleanEvict almost simultaneously will be caught by snoops sent out
+ // by crossbar.
+ std::vector<MSHR *> outgoing;
+ if (writeBuffer.findMatches(pkt->getAddr(), pkt->isSecure(),
+ outgoing)) {
+ assert(outgoing.size() == 1);
+ PacketPtr wbPkt = outgoing[0]->getTarget()->pkt;
+ assert(pkt->cmd == MemCmd::CleanEvict &&
+ wbPkt->cmd == MemCmd::Writeback);
+ // As the CleanEvict is coming from above, it would have snooped
+ // into other peer caches of the same level while traversing the
+ // crossbar. If a copy of the block had been found, the CleanEvict
+ // would have been deleted in the crossbar. Now that the
+ // CleanEvict is here we can be sure none of the other upper level
+ // caches connected to this cache have the block, so we can clear
+ // the BLOCK_CACHED flag in the Writeback if set and discard the
+ // CleanEvict by returning true.
+ wbPkt->clearBlockCached();
+ return true;
+ }
+ }
+
// Writeback handling is special case. We can write the block into
// the cache without having a writeable copy (or any copy at all).
if (pkt->cmd == MemCmd::Writeback) {
@@ -363,6 +393,19 @@ Cache::access(PacketPtr pkt, CacheBlk *&blk, Cycles &lat,
DPRINTF(Cache, "%s new state is %s\n", __func__, blk->print());
incHitCount(pkt);
return true;
+ } else if (pkt->cmd == MemCmd::CleanEvict) {
+ if (blk != NULL) {
+ // Found the block in the tags, need to stop CleanEvict from
+ // propagating further down the hierarchy. Returning true will
+ // treat the CleanEvict like a satisfied write request and delete
+ // it.
+ return true;
+ }
+ // We didn't find the block here, propagate the CleanEvict further
+ // down the memory hierarchy. Returning false will treat the CleanEvict
+ // like a Writeback which could not find a replaceable block so has to
+ // go to next level.
+ return false;
} else if ((blk != NULL) &&
(pkt->needsExclusive() ? blk->isWritable()
: blk->isReadable())) {
@@ -395,6 +438,41 @@ class ForwardResponseRecord : public Packet::SenderState
};
void
+Cache::doWritebacks(PacketList& writebacks, Tick forward_time)
+{
+ while (!writebacks.empty()) {
+ PacketPtr wbPkt = writebacks.front();
+ // We use forwardLatency here because we are copying writebacks to
+ // write buffer. Call isCachedAbove for both Writebacks and
+ // CleanEvicts. If isCachedAbove returns true we set BLOCK_CACHED flag
+ // in Writebacks and discard CleanEvicts.
+ if (isCachedAbove(wbPkt)) {
+ if (wbPkt->cmd == MemCmd::CleanEvict) {
+ // Delete CleanEvict because cached copies exist above. The
+ // packet destructor will delete the request object because
+ // this is a non-snoop request packet which does not require a
+ // response.
+ delete wbPkt;
+ } else {
+ // Set BLOCK_CACHED flag in Writeback and send below, so that
+ // the Writeback does not reset the bit corresponding to this
+ // address in the snoop filter below.
+ wbPkt->setBlockCached();
+ allocateWriteBuffer(wbPkt, forward_time, true);
+ }
+ } else {
+ // If the block is not cached above, send packet below. Both
+ // CleanEvict and Writeback with BLOCK_CACHED flag cleared will
+ // reset the bit corresponding to this address in the snoop filter
+ // below.
+ allocateWriteBuffer(wbPkt, forward_time, true);
+ }
+ writebacks.pop_front();
+ }
+}
+
+
+void
Cache::recvTimingSnoopResp(PacketPtr pkt)
{
DPRINTF(Cache, "%s for %s addr %#llx size %d\n", __func__,
@@ -510,7 +588,7 @@ Cache::recvTimingReq(PacketPtr pkt)
/// @todo nominally we should just delete the packet here,
/// however, until 4-phase stuff we can't because sending
- /// cache is still relying on it
+ /// cache is still relying on it.
pendingDelete.push_back(pkt);
// no need to take any action in this particular cache as the
@@ -537,13 +615,7 @@ Cache::recvTimingReq(PacketPtr pkt)
// copy writebacks to write buffer here to ensure they logically
// proceed anything happening below
- while (!writebacks.empty()) {
- PacketPtr wbPkt = writebacks.front();
- // We use forwardLatency here because we are copying
- // writebacks to write buffer.
- allocateWriteBuffer(wbPkt, forward_time, true);
- writebacks.pop_front();
- }
+ doWritebacks(writebacks, forward_time);
}
// Here we charge the headerDelay that takes into account the latencies
@@ -591,8 +663,10 @@ Cache::recvTimingReq(PacketPtr pkt)
cpuSidePort->schedTimingResp(pkt, request_time);
} else {
/// @todo nominally we should just delete the packet here,
- /// however, until 4-phase stuff we can't because sending
- /// cache is still relying on it
+ /// however, until 4-phase stuff we can't because sending cache is
+ /// still relying on it. If the block is found in access(),
+ /// CleanEvict and Writeback messages will be deleted here as
+ /// well.
pendingDelete.push_back(pkt);
}
} else {
@@ -660,31 +734,38 @@ Cache::recvTimingReq(PacketPtr pkt)
// Coalesce unless it was a software prefetch (see above).
if (pkt) {
- DPRINTF(Cache, "%s coalescing MSHR for %s addr %#llx size %d\n",
- __func__, pkt->cmdString(), pkt->getAddr(),
- pkt->getSize());
-
- assert(pkt->req->masterId() < system->maxMasters());
- mshr_hits[pkt->cmdToIndex()][pkt->req->masterId()]++;
- if (mshr->threadNum != 0/*pkt->req->threadId()*/) {
- mshr->threadNum = -1;
- }
- // We use forward_time here because it is the same
- // considering new targets. We have multiple requests for the
- // same address here. It specifies the latency to allocate an
- // internal buffer and to schedule an event to the queued
- // port and also takes into account the additional delay of
- // the xbar.
- mshr->allocateTarget(pkt, forward_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);
+ assert(pkt->cmd != MemCmd::Writeback);
+ // CleanEvicts corresponding to blocks which have outstanding
+ // requests in MSHRs can be deleted here.
+ if (pkt->cmd == MemCmd::CleanEvict) {
+ pendingDelete.push_back(pkt);
+ } else {
+ DPRINTF(Cache, "%s coalescing MSHR for %s addr %#llx size %d\n",
+ __func__, pkt->cmdString(), pkt->getAddr(),
+ pkt->getSize());
+
+ assert(pkt->req->masterId() < system->maxMasters());
+ mshr_hits[pkt->cmdToIndex()][pkt->req->masterId()]++;
+ if (mshr->threadNum != 0/*pkt->req->threadId()*/) {
+ mshr->threadNum = -1;
+ }
+ // We use forward_time here because it is the same
+ // considering new targets. We have multiple
+ // requests for the same address here. It
+ // specifies the latency to allocate an internal
+ // buffer and to schedule an event to the queued
+ // port and also takes into account the additional
+ // delay of the xbar.
+ mshr->allocateTarget(pkt, forward_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);
+ }
}
-
// We should call the prefetcher reguardless if the request is
// satisfied or not, reguardless if the request is in the MSHR or
// not. The request could be a ReadReq hit, but still not
@@ -707,7 +788,7 @@ Cache::recvTimingReq(PacketPtr pkt)
mshr_misses[pkt->cmdToIndex()][pkt->req->masterId()]++;
}
- if (pkt->cmd == MemCmd::Writeback ||
+ if (pkt->evictingBlock() ||
(pkt->req->isUncacheable() && pkt->isWrite())) {
// We use forward_time here because there is an
// uncached memory write, forwarded to WriteBuffer. It
@@ -782,7 +863,8 @@ Cache::getBusPacket(PacketPtr cpu_pkt, CacheBlk *blk,
}
if (!blkValid &&
- (cpu_pkt->cmd == MemCmd::Writeback || cpu_pkt->isUpgrade())) {
+ (cpu_pkt->isUpgrade() ||
+ cpu_pkt->evictingBlock())) {
// Writebacks that weren't allocated in access() and upgrades
// from upper-level caches that missed completely just go
// through.
@@ -834,8 +916,9 @@ Cache::getBusPacket(PacketPtr cpu_pkt, CacheBlk *blk,
assert(pkt->getAddr() == blockAlign(pkt->getAddr()));
pkt->allocate();
- DPRINTF(Cache, "%s created %s addr %#llx size %d\n",
- __func__, pkt->cmdString(), pkt->getAddr(), pkt->getSize());
+ DPRINTF(Cache, "%s created %s from %s for addr %#llx size %d\n",
+ __func__, pkt->cmdString(), cpu_pkt->cmdString(), pkt->getAddr(),
+ pkt->getSize());
return pkt;
}
@@ -1302,19 +1385,28 @@ Cache::recvTimingResp(PacketPtr pkt)
pkt->headerDelay = pkt->payloadDelay = 0;
// copy writebacks to write buffer
- while (!writebacks.empty()) {
- PacketPtr wbPkt = writebacks.front();
- allocateWriteBuffer(wbPkt, clockEdge(forwardLatency), true);
- writebacks.pop_front();
- }
- // if we used temp block, clear it out
- if (blk == tempBlock) {
+ doWritebacks(writebacks, forward_time);
+
+ // if we used temp block, check to see if its valid and then clear it out
+ if (blk == tempBlock && tempBlock->isValid()) {
+ // We use forwardLatency here because we are copying
+ // Writebacks/CleanEvicts to write buffer. It specifies the latency to
+ // allocate an internal buffer and to schedule an event to the
+ // queued port.
if (blk->isDirty()) {
- // We use forwardLatency here because we are copying
- // writebacks to write buffer. It specifies the latency to
- // allocate an internal buffer and to schedule an event to the
- // queued port.
- allocateWriteBuffer(writebackBlk(blk), forward_time, true);
+ PacketPtr wbPkt = writebackBlk(blk);
+ allocateWriteBuffer(wbPkt, forward_time, true);
+ // Set BLOCK_CACHED flag if cached above.
+ if (isCachedAbove(wbPkt))
+ wbPkt->setBlockCached();
+ } else {
+ PacketPtr wcPkt = cleanEvictBlk(blk);
+ // Check to see if block is cached above. If not allocate
+ // write buffer
+ if (isCachedAbove(wcPkt))
+ delete wcPkt;
+ else
+ allocateWriteBuffer(wcPkt, forward_time, true);
}
blk->invalidate();
}
@@ -1352,6 +1444,30 @@ Cache::writebackBlk(CacheBlk *blk)
return writeback;
}
+PacketPtr
+Cache::cleanEvictBlk(CacheBlk *blk)
+{
+ assert(blk && blk->isValid() && !blk->isDirty());
+ // Creating a zero sized write, a message to the snoop filter
+ Request *req =
+ new Request(tags->regenerateBlkAddr(blk->tag, blk->set), blkSize, 0,
+ Request::wbMasterId);
+ if (blk->isSecure())
+ req->setFlags(Request::SECURE);
+
+ req->taskId(blk->task_id);
+ blk->task_id = ContextSwitchTaskId::Unknown;
+ blk->tickInserted = curTick();
+
+ PacketPtr pkt = new Packet(req, MemCmd::CleanEvict);
+ pkt->allocate();
+ DPRINTF(Cache, "%s%s %x Create CleanEvict\n", pkt->cmdString(),
+ pkt->req->isInstFetch() ? " (ifetch)" : "",
+ pkt->getAddr());
+
+ return pkt;
+}
+
void
Cache::memWriteback()
{
@@ -1434,9 +1550,13 @@ Cache::allocateBlock(Addr addr, bool is_secure, PacketList &writebacks)
addr, is_secure ? "s" : "ns",
blk->isDirty() ? "writeback" : "clean");
+ // Will send up Writeback/CleanEvict snoops via isCachedAbove
+ // when pushing this writeback list into the write buffer.
if (blk->isDirty()) {
// Save writeback packet for handling by caller
writebacks.push_back(writebackBlk(blk));
+ } else {
+ writebacks.push_back(cleanEvictBlk(blk));
}
}
}
@@ -1460,6 +1580,12 @@ Cache::handleFill(PacketPtr pkt, CacheBlk *blk, PacketList &writebacks)
CacheBlk::State old_state = blk ? blk->status : 0;
#endif
+ // When handling a fill, discard any CleanEvicts for the
+ // same address in write buffer.
+ Addr M5_VAR_USED blk_addr = blockAlign(pkt->getAddr());
+ std::vector<MSHR *> M5_VAR_USED wbs;
+ assert (!writeBuffer.findMatches(blk_addr, is_secure, wbs));
+
if (blk == NULL) {
// better have read new data...
assert(pkt->hasData());
@@ -1633,9 +1759,9 @@ Cache::handleSnoop(PacketPtr pkt, CacheBlk *blk, bool is_timing,
if (snoopPkt.sharedAsserted()) {
pkt->assertShared();
}
- // If this request is a prefetch or clean evict and an
- // upper level signals block present, make sure to
- // propagate the block presence to the requester.
+ // If this request is a prefetch or clean evict and an upper level
+ // signals block present, make sure to propagate the block
+ // presence to the requester.
if (snoopPkt.isBlockCached()) {
pkt->setBlockCached();
}
@@ -1674,9 +1800,9 @@ Cache::handleSnoop(PacketPtr pkt, CacheBlk *blk, bool is_timing,
// MemCmd::HardPFReq is only observed by upstream caches. After missing
// above and in it's own cache, a new MemCmd::ReadReq is created that
// downstream caches observe.
- if (pkt->cmd == MemCmd::HardPFReq) {
- DPRINTF(Cache, "Squashing prefetch from lower cache %#x\n",
- pkt->getAddr());
+ if (pkt->mustCheckAbove()) {
+ DPRINTF(Cache, "Found addr %#llx in upper level cache for snoop %s from"
+ " lower cache\n", pkt->getAddr(), pkt->cmdString());
pkt->setBlockCached();
return;
}
@@ -1754,7 +1880,7 @@ Cache::recvTimingSnoopReq(PacketPtr pkt)
assert(!system->bypassCaches());
// no need to snoop writebacks or requests that are not in range
- if (pkt->cmd == MemCmd::Writeback || !inRange(pkt->getAddr())) {
+ if (!inRange(pkt->getAddr())) {
return;
}
@@ -1764,11 +1890,12 @@ Cache::recvTimingSnoopReq(PacketPtr pkt)
Addr blk_addr = blockAlign(pkt->getAddr());
MSHR *mshr = mshrQueue.findMatch(blk_addr, is_secure);
- // Squash any prefetch requests from below on MSHR hits
- if (mshr && pkt->cmd == MemCmd::HardPFReq) {
- DPRINTF(Cache, "Setting block present to squash prefetch from"
+ // Inform request(Prefetch, CleanEvict or Writeback) from below of
+ // MSHR hit, set setBlockCached.
+ if (mshr && pkt->mustCheckAbove()) {
+ DPRINTF(Cache, "Setting block cached for %s from"
"lower cache on mshr hit %#x\n",
- pkt->getAddr());
+ pkt->cmdString(), pkt->getAddr());
pkt->setBlockCached();
return;
}
@@ -1795,28 +1922,60 @@ Cache::recvTimingSnoopReq(PacketPtr pkt)
// We should only ever find a single match
assert(writebacks.size() == 1);
MSHR *wb_entry = writebacks[0];
+ // Expect to see only Writebacks and/or CleanEvicts here, both of
+ // which should not be generated for uncacheable data.
assert(!wb_entry->isUncacheable());
+ // There should only be a single request responsible for generating
+ // Writebacks/CleanEvicts.
assert(wb_entry->getNumTargets() == 1);
PacketPtr wb_pkt = wb_entry->getTarget()->pkt;
- assert(wb_pkt->cmd == MemCmd::Writeback);
+ assert(wb_pkt->evictingBlock());
+
+ if (pkt->evictingBlock()) {
+ // if the block is found in the write queue, set the BLOCK_CACHED
+ // flag for Writeback/CleanEvict snoop. On return the snoop will
+ // propagate the BLOCK_CACHED flag in Writeback packets and prevent
+ // any CleanEvicts from travelling down the memory hierarchy.
+ pkt->setBlockCached();
+ DPRINTF(Cache, "Squashing %s from lower cache on writequeue hit"
+ " %#x\n", pkt->cmdString(), pkt->getAddr());
+ return;
+ }
- assert(!pkt->memInhibitAsserted());
- pkt->assertMemInhibit();
- if (!pkt->needsExclusive()) {
- pkt->assertShared();
- // the writeback is no longer the exclusive copy in the system
- wb_pkt->clearSupplyExclusive();
+ if (wb_pkt->cmd == MemCmd::Writeback) {
+ assert(!pkt->memInhibitAsserted());
+ pkt->assertMemInhibit();
+ if (!pkt->needsExclusive()) {
+ pkt->assertShared();
+ // the writeback is no longer the exclusive copy in
+ // the system
+ wb_pkt->clearSupplyExclusive();
+ } 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());
+ }
+ doTimingSupplyResponse(pkt, wb_pkt->getConstPtr<uint8_t>(),
+ false, false);
} 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());
+ assert(wb_pkt->cmd == MemCmd::CleanEvict);
+ // The cache technically holds the block until the
+ // corresponding CleanEvict message reaches the crossbar
+ // below. Therefore when a snoop encounters a CleanEvict
+ // message we must set assertShared (just like when it
+ // encounters a Writeback) to avoid the snoop filter
+ // prematurely clearing the holder bit in the crossbar
+ // below
+ if (!pkt->needsExclusive())
+ pkt->assertShared();
+ else
+ assert(pkt->isInvalidate());
}
- doTimingSupplyResponse(pkt, wb_pkt->getConstPtr<uint8_t>(),
- false, false);
if (pkt->isInvalidate()) {
// Invalidation trumps our writeback... discard here
+ // Note: markInService will remove entry from writeback buffer.
markInService(wb_entry, false);
delete wb_pkt;
}
@@ -1844,8 +2003,11 @@ Cache::recvAtomicSnoop(PacketPtr pkt)
// Snoops shouldn't happen when bypassing caches
assert(!system->bypassCaches());
- // no need to snoop writebacks or requests that are not in range
- if (pkt->cmd == MemCmd::Writeback || !inRange(pkt->getAddr())) {
+ // no need to snoop writebacks or requests that are not in range. In
+ // atomic we have no Writebacks/CleanEvicts queued and no prefetches,
+ // hence there is no need to snoop upwards and determine if they are
+ // present above.
+ if (pkt->evictingBlock() || !inRange(pkt->getAddr())) {
return 0;
}
@@ -1938,6 +2100,29 @@ Cache::getNextMSHR()
return NULL;
}
+bool
+Cache::isCachedAbove(const PacketPtr pkt) const
+{
+ if (isTopLevel)
+ return false;
+ // Mirroring the flow of HardPFReqs, the cache sends CleanEvict and
+ // Writeback snoops into upper level caches to check for copies of the
+ // same block. Using the BLOCK_CACHED flag with the Writeback/CleanEvict
+ // packet, the cache can inform the crossbar below of presence or absence
+ // of the block.
+
+ Packet snoop_pkt(pkt, true, false);
+ snoop_pkt.setExpressSnoop();
+ // Assert that packet is either Writeback or CleanEvict and not a prefetch
+ // request because prefetch requests need an MSHR and may generate a snoop
+ // response.
+ assert(pkt->evictingBlock());
+ snoop_pkt.senderState = NULL;
+ cpuSidePort->sendTimingSnoopReq(&snoop_pkt);
+ // Writeback/CleanEvict snoops do not generate a separate snoop response.
+ assert(!(snoop_pkt.memInhibitAsserted()));
+ return snoop_pkt.isBlockCached();
+}
PacketPtr
Cache::getTimingPacket()
@@ -1955,62 +2140,69 @@ Cache::getTimingPacket()
DPRINTF(CachePort, "%s %s for addr %#llx size %d\n", __func__,
tgt_pkt->cmdString(), tgt_pkt->getAddr(), tgt_pkt->getSize());
- if (mshr->isForwardNoResponse()) {
- // no response expected, just forward packet as it is
- assert(tags->findBlock(mshr->blkAddr, mshr->isSecure) == NULL);
- pkt = tgt_pkt;
- } else {
- CacheBlk *blk = tags->findBlock(mshr->blkAddr, mshr->isSecure);
-
- if (tgt_pkt->cmd == MemCmd::HardPFReq && forwardSnoops) {
- // We need to check the caches above us to verify that
- // they don't have a copy of this block in the dirty state
- // at the moment. Without this check we could get a stale
- // copy from memory that might get used in place of the
- // dirty one.
- Packet snoop_pkt(tgt_pkt, true, false);
- snoop_pkt.setExpressSnoop();
- snoop_pkt.senderState = mshr;
- cpuSidePort->sendTimingSnoopReq(&snoop_pkt);
-
- // Check to see if the prefetch was squashed by an upper cache (to
- // prevent us from grabbing the line) or if a Check to see if a
- // writeback arrived between the time the prefetch was placed in
- // the MSHRs and when it was selected to be sent or if the
- // prefetch was squashed by an upper cache.
-
- // It is important to check msmInhibitAsserted before
- // prefetchSquashed. If another cache has asserted MEM_INGIBIT, it
- // will be sending a response which will arrive at the MSHR
- // allocated ofr this request. Checking the prefetchSquash first
- // may result in the MSHR being prematurely deallocated.
-
- if (snoop_pkt.memInhibitAsserted()) {
- // If we are getting a non-shared response it is dirty
- bool pending_dirty_resp = !snoop_pkt.sharedAsserted();
- markInService(mshr, pending_dirty_resp);
- DPRINTF(Cache, "Upward snoop of prefetch for addr"
- " %#x (%s) hit\n",
- tgt_pkt->getAddr(), tgt_pkt->isSecure()? "s": "ns");
- return NULL;
- }
+ CacheBlk *blk = tags->findBlock(mshr->blkAddr, mshr->isSecure);
+
+ if (tgt_pkt->cmd == MemCmd::HardPFReq && forwardSnoops) {
+ // We need to check the caches above us to verify that
+ // they don't have a copy of this block in the dirty state
+ // at the moment. Without this check we could get a stale
+ // copy from memory that might get used in place of the
+ // dirty one.
+ Packet snoop_pkt(tgt_pkt, true, false);
+ snoop_pkt.setExpressSnoop();
+ snoop_pkt.senderState = mshr;
+ cpuSidePort->sendTimingSnoopReq(&snoop_pkt);
+
+ // Check to see if the prefetch was squashed by an upper cache (to
+ // prevent us from grabbing the line) or if a Check to see if a
+ // writeback arrived between the time the prefetch was placed in
+ // the MSHRs and when it was selected to be sent or if the
+ // prefetch was squashed by an upper cache.
+
+ // It is important to check memInhibitAsserted before
+ // prefetchSquashed. If another cache has asserted MEM_INGIBIT, it
+ // will be sending a response which will arrive at the MSHR
+ // allocated ofr this request. Checking the prefetchSquash first
+ // may result in the MSHR being prematurely deallocated.
+
+ if (snoop_pkt.memInhibitAsserted()) {
+ // If we are getting a non-shared response it is dirty
+ bool pending_dirty_resp = !snoop_pkt.sharedAsserted();
+ markInService(mshr, pending_dirty_resp);
+ DPRINTF(Cache, "Upward snoop of prefetch for addr"
+ " %#x (%s) hit\n",
+ tgt_pkt->getAddr(), tgt_pkt->isSecure()? "s": "ns");
+ return NULL;
+ }
- if (snoop_pkt.isBlockCached() || blk != NULL) {
- DPRINTF(Cache, "Block present, prefetch squashed by cache. "
- "Deallocating mshr target %#x.\n",
- mshr->blkAddr);
+ if (snoop_pkt.isBlockCached() || blk != NULL) {
+ DPRINTF(Cache, "Block present, prefetch squashed by cache. "
+ "Deallocating mshr target %#x.\n",
+ mshr->blkAddr);
- // Deallocate the mshr target
+ // Deallocate the mshr target
+ if (tgt_pkt->cmd != MemCmd::Writeback) {
if (mshr->queue->forceDeallocateTarget(mshr)) {
// Clear block if this deallocation resulted freed an
// mshr when all had previously been utilized
clearBlocked((BlockedCause)(mshr->queue->index));
}
return NULL;
+ } else {
+ // If this is a Writeback, and the snoops indicate that the blk
+ // is cached above, set the BLOCK_CACHED flag in the Writeback
+ // packet, so that it does not reset the bits corresponding to
+ // this block in the snoop filter below.
+ tgt_pkt->setBlockCached();
}
-
}
+ }
+ if (mshr->isForwardNoResponse()) {
+ // no response expected, just forward packet as it is
+ assert(tags->findBlock(mshr->blkAddr, mshr->isSecure) == NULL);
+ pkt = tgt_pkt;
+ } else {
pkt = getBusPacket(tgt_pkt, blk, mshr->needsExclusive());
mshr->isForward = (pkt == NULL);
diff --git a/src/mem/cache/prefetch/base.cc b/src/mem/cache/prefetch/base.cc
index 3ab2d76a6..de4eaca01 100644
--- a/src/mem/cache/prefetch/base.cc
+++ b/src/mem/cache/prefetch/base.cc
@@ -93,6 +93,7 @@ BasePrefetcher::observeAccess(const PacketPtr &pkt) const
if (!fetch && read && !onRead) return false;
if (!fetch && !read && !onWrite) return false;
if (!fetch && !read && inv) return false;
+ if (pkt->cmd == MemCmd::CleanEvict) return false;
if (onMiss) {
return !inCache(addr, is_secure) &&
diff --git a/src/mem/coherent_xbar.cc b/src/mem/coherent_xbar.cc
index cefad3161..b58511db6 100644
--- a/src/mem/coherent_xbar.cc
+++ b/src/mem/coherent_xbar.cc
@@ -138,6 +138,12 @@ CoherentXBar::init()
bool
CoherentXBar::recvTimingReq(PacketPtr pkt, PortID slave_port_id)
{
+ // @todo temporary hack to deal with memory corruption issue until
+ // 4-phase transactions are complete
+ for (int x = 0; x < pendingDelete.size(); x++)
+ delete pendingDelete[x];
+ pendingDelete.clear();
+
// determine the source port based on the id
SlavePort *src_port = slavePorts[slave_port_id];
@@ -201,6 +207,19 @@ CoherentXBar::recvTimingReq(PacketPtr pkt, PortID slave_port_id)
}
}
+ // forwardTiming snooped into peer caches of the sender, and if
+ // this is a clean evict, but the packet is found in a cache, do
+ // not forward it
+ if (pkt->cmd == MemCmd::CleanEvict && pkt->isBlockCached()) {
+ DPRINTF(CoherentXBar, "recvTimingReq: Clean evict 0x%x still cached, "
+ "not forwarding\n", pkt->getAddr());
+
+ // update the layer state and schedule an idle event
+ reqLayers[master_port_id]->succeededTiming(packetFinishTime);
+ pendingDelete.push_back(pkt);
+ return true;
+ }
+
// remember if the packet will generate a snoop response
const bool expect_snoop_resp = !is_inhibited && pkt->memInhibitAsserted();
const bool expect_response = pkt->needsResponse() &&
diff --git a/src/mem/coherent_xbar.hh b/src/mem/coherent_xbar.hh
index 3cf10689c..24506d22b 100644
--- a/src/mem/coherent_xbar.hh
+++ b/src/mem/coherent_xbar.hh
@@ -275,6 +275,13 @@ class CoherentXBar : public BaseXBar
/** Cycles of snoop response latency.*/
const Cycles snoopResponseLatency;
+ /**
+ * @todo this is a temporary workaround until the 4-phase code is committed.
+ * upstream caches need this packet until true is returned, so hold it for
+ * deletion until a subsequent call
+ */
+ std::vector<PacketPtr> pendingDelete;
+
/** Function called by the port when the crossbar is recieving a Timing
request packet.*/
bool recvTimingReq(PacketPtr pkt, PortID slave_port_id);
diff --git a/src/mem/dram_ctrl.cc b/src/mem/dram_ctrl.cc
index 733a7390f..196f599af 100644
--- a/src/mem/dram_ctrl.cc
+++ b/src/mem/dram_ctrl.cc
@@ -643,9 +643,10 @@ DRAMCtrl::recvTimingReq(PacketPtr pkt)
DPRINTF(DRAM, "recvTimingReq: request %s addr %lld size %d\n",
pkt->cmdString(), pkt->getAddr(), pkt->getSize());
- // simply drop inhibited packets for now
- if (pkt->memInhibitAsserted()) {
- DPRINTF(DRAM, "Inhibited packet -- Dropping it now\n");
+ // simply drop inhibited packets and clean evictions
+ if (pkt->memInhibitAsserted() ||
+ pkt->cmd == MemCmd::CleanEvict) {
+ DPRINTF(DRAM, "Inhibited packet or clean evict -- Dropping it now\n");
pendingDelete.push_back(pkt);
return true;
}
diff --git a/src/mem/packet.cc b/src/mem/packet.cc
index ecc2feb26..68a5e6dc2 100644
--- a/src/mem/packet.cc
+++ b/src/mem/packet.cc
@@ -86,6 +86,8 @@ MemCmd::commandInfo[] =
/* Writeback */
{ SET4(IsWrite, NeedsExclusive, IsRequest, HasData),
InvalidCmd, "Writeback" },
+ /* CleanEvict */
+ { SET2(IsWrite, IsRequest), InvalidCmd, "CleanEvict" },
/* SoftPFReq */
{ SET4(IsRead, IsRequest, IsSWPrefetch, NeedsResponse),
SoftPFResp, "SoftPFReq" },
diff --git a/src/mem/packet.hh b/src/mem/packet.hh
index 3f10458e7..192136aba 100644
--- a/src/mem/packet.hh
+++ b/src/mem/packet.hh
@@ -87,6 +87,7 @@ class MemCmd
WriteReq,
WriteResp,
Writeback,
+ CleanEvict,
SoftPFReq,
HardPFReq,
SoftPFResp,
@@ -508,6 +509,7 @@ class Packet : public Printable
bool suppressFuncError() const { return flags.isSet(SUPPRESS_FUNC_ERROR); }
void setBlockCached() { flags.set(BLOCK_CACHED); }
bool isBlockCached() const { return flags.isSet(BLOCK_CACHED); }
+ void clearBlockCached() { flags.clear(BLOCK_CACHED); }
// Network error conditions... encapsulate them as methods since
// their encoding keeps changing (from result field to command
@@ -937,6 +939,27 @@ class Packet : public Printable
}
/**
+ * Is this request notification of a clean or dirty eviction from the cache.
+ **/
+ bool
+ evictingBlock() const
+ {
+ return (cmd == MemCmd::Writeback ||
+ cmd == MemCmd::CleanEvict);
+ }
+
+ /**
+ * Does the request need to check for cached copies of the same block
+ * in the memory hierarchy above.
+ **/
+ bool
+ mustCheckAbove() const
+ {
+ return (cmd == MemCmd::HardPFReq ||
+ evictingBlock());
+ }
+
+ /**
* Check a functional request against a memory value represented
* by a base/size pair and an associated data array. If the
* current packet is a read, it may be satisfied by the memory
diff --git a/src/mem/snoop_filter.cc b/src/mem/snoop_filter.cc
index 9ff591065..48587c8ee 100755
--- a/src/mem/snoop_filter.cc
+++ b/src/mem/snoop_filter.cc
@@ -134,7 +134,8 @@ SnoopFilter::updateRequest(const Packet* cpkt, const SlavePort& slave_port,
// Writebacks -> the sender does not have the line anymore
sf_item.holder &= ~req_port;
} else {
- assert(0 == "Handle non-writeback, here");
+ // @todo Add CleanEvicts
+ assert(cpkt->cmd == MemCmd::CleanEvict);
}
DPRINTF(SnoopFilter, "%s: new SF value %x.%x\n",
__func__, sf_item.requested, sf_item.holder);
@@ -174,8 +175,13 @@ SnoopFilter::lookupSnoop(const Packet* cpkt)
else
hitMultiSnoops++;
}
-
- assert(cpkt->isInvalidate() == cpkt->needsExclusive());
+ // ReadEx and Writes require both invalidation and exlusivity, while reads
+ // require neither. Writebacks on the other hand require exclusivity but
+ // not the invalidation. Previously Writebacks did not generate upward
+ // snoops so this was never an aissue. Now that Writebacks generate snoops
+ // we need to special case for Writebacks.
+ assert(cpkt->cmd == MemCmd::Writeback ||
+ (cpkt->isInvalidate() == cpkt->needsExclusive()));
if (cpkt->isInvalidate() && !sf_item.requested) {
// Early clear of the holder, if no other request is currently going on
// @todo: This should possibly be updated even though we do not filter