changeset b5bef3c8e070 in /z/repo/gem5 details: http://repo.gem5.org/gem5?cmd=changeset;node=b5bef3c8e070 description: mem: write streaming support via WriteInvalidate promotion
Support full-block writes directly rather than requiring RMW: * a cache line is allocated in the cache upon receipt of a WriteInvalidateReq, not the WriteInvalidateResp. * only top-level caches allocate the line; the others just pass the request along and invalidate as necessary. * to close a timing window between the *Req and the *Resp, a new metadata bit tracks whether another cache has read a copy of the new line before the writeback to memory. diffstat: src/mem/cache/base.cc | 6 +- src/mem/cache/base.hh | 3 + src/mem/cache/blk.hh | 5 +- src/mem/cache/cache.hh | 5 + src/mem/cache/cache_impl.hh | 238 +++++++++++++++++++++++++++++++------------ src/mem/packet.cc | 4 +- src/mem/packet.hh | 8 - 7 files changed, 190 insertions(+), 79 deletions(-) diffs (truncated from 499 to 300 lines): diff -r fa9ef374075f -r b5bef3c8e070 src/mem/cache/base.cc --- a/src/mem/cache/base.cc Wed Sep 03 07:42:50 2014 -0400 +++ b/src/mem/cache/base.cc Fri Jun 27 12:29:00 2014 -0500 @@ -93,9 +93,9 @@ // if we already scheduled a retry in this cycle, but it has not yet // happened, cancel it if (sendRetryEvent.scheduled()) { - owner.deschedule(sendRetryEvent); - DPRINTF(CachePort, "Cache port %s deschedule retry\n", name()); - mustSendRetry = true; + owner.deschedule(sendRetryEvent); + DPRINTF(CachePort, "Cache port %s deschedule retry\n", name()); + mustSendRetry = true; } } diff -r fa9ef374075f -r b5bef3c8e070 src/mem/cache/base.hh --- a/src/mem/cache/base.hh Wed Sep 03 07:42:50 2014 -0400 +++ b/src/mem/cache/base.hh Fri Jun 27 12:29:00 2014 -0500 @@ -94,6 +94,7 @@ Blocked_NoMSHRs = MSHRQueue_MSHRs, Blocked_NoWBBuffers = MSHRQueue_WriteBuffer, Blocked_NoTargets, + Blocked_PendingWriteInvalidate, NUM_BLOCKED_CAUSES }; @@ -168,6 +169,8 @@ /** Return to normal operation and accept new requests. */ void clearBlocked(); + bool isBlocked() const { return blocked; } + protected: CacheSlavePort(const std::string &_name, BaseCache *_cache, diff -r fa9ef374075f -r b5bef3c8e070 src/mem/cache/blk.hh --- a/src/mem/cache/blk.hh Wed Sep 03 07:42:50 2014 -0400 +++ b/src/mem/cache/blk.hh Fri Jun 27 12:29:00 2014 -0500 @@ -72,7 +72,10 @@ /** block was a hardware prefetch yet unaccessed*/ BlkHWPrefetched = 0x20, /** block holds data from the secure memory space */ - BlkSecure = 0x40 + BlkSecure = 0x40, + /** can the block transition to E? (hasn't been shared with another cache) + * used to close a timing gap when handling WriteInvalidate packets */ + BlkCanGoExclusive = 0x80 }; /** diff -r fa9ef374075f -r b5bef3c8e070 src/mem/cache/cache.hh --- a/src/mem/cache/cache.hh Wed Sep 03 07:42:50 2014 -0400 +++ b/src/mem/cache/cache.hh Fri Jun 27 12:29:00 2014 -0500 @@ -181,6 +181,11 @@ const bool doFastWrites; /** + * Turn line-sized writes into WriteInvalidate transactions. + */ + void promoteWholeLineWrites(PacketPtr pkt); + + /** * Notify the prefetcher on every access, not just misses. */ const bool prefetchOnAccess; diff -r fa9ef374075f -r b5bef3c8e070 src/mem/cache/cache_impl.hh --- a/src/mem/cache/cache_impl.hh Wed Sep 03 07:42:50 2014 -0400 +++ b/src/mem/cache/cache_impl.hh Fri Jun 27 12:29:00 2014 -0500 @@ -312,30 +312,20 @@ pkt->getAddr(), pkt->isSecure() ? "s" : "ns", blk ? "hit" : "miss", blk ? blk->print() : ""); - if (blk != NULL) { - - if (pkt->needsExclusive() ? blk->isWritable() : blk->isReadable()) { - // OK to satisfy access - incHitCount(pkt); - satisfyCpuSideRequest(pkt, blk); - return true; - } - } - - // Can't satisfy access normally... either no block (blk == NULL) - // or have block but need exclusive & only have shared. - // 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) { + // all). Like writebacks, we write into the cache upon initial + // receipt of a write-invalidate packets as well. + if ((pkt->cmd == MemCmd::Writeback) || + ((pkt->cmd == MemCmd::WriteInvalidateReq) && isTopLevel)) { assert(blkSize == pkt->getSize()); if (blk == NULL) { // need to do a replacement blk = allocateBlock(pkt->getAddr(), pkt->isSecure(), writebacks); if (blk == NULL) { // no replaceable block available, give up. - // writeback will be forwarded to next level. + // Writeback will be forwarded to next level, + // WriteInvalidate will be retried. incMissCount(pkt); return false; } @@ -347,17 +337,41 @@ } } std::memcpy(blk->data, pkt->getPtr<uint8_t>(), blkSize); - blk->status |= BlkDirty; - if (pkt->isSupplyExclusive()) { - blk->status |= BlkWritable; + if (pkt->cmd == MemCmd::Writeback) { + blk->status |= BlkDirty; + if (pkt->isSupplyExclusive()) { + blk->status |= BlkWritable; + } + // nothing else to do; writeback doesn't expect response + assert(!pkt->needsResponse()); + } else if (pkt->cmd == MemCmd::WriteInvalidateReq) { + assert(blk->isReadable()); // implicitly checks for Valid bit also + blk->status |= (BlkDirty | BlkCanGoExclusive); + blk->status &= ~BlkWritable; + ++fastWrites; } - // nothing else to do; writeback doesn't expect response - assert(!pkt->needsResponse()); DPRINTF(Cache, "%s new state is %s\n", __func__, blk->print()); incHitCount(pkt); return true; + } else if ((pkt->cmd == MemCmd::WriteInvalidateReq) && !isTopLevel) { + if (blk != NULL) { + assert(blk != tempBlock); + tags->invalidate(blk); + blk->invalidate(); + } + return true; + } else if ((blk != NULL) && + (pkt->needsExclusive() ? blk->isWritable() + : blk->isReadable())) { + // OK to satisfy access + incHitCount(pkt); + satisfyCpuSideRequest(pkt, blk); + return true; } + // Can't satisfy access normally... either no block (blk == NULL) + // or have block but need exclusive & only have shared. + incMissCount(pkt); if (blk == NULL && pkt->isLLSC() && pkt->isWrite()) { @@ -414,6 +428,19 @@ } template<class TagStore> +void +Cache<TagStore>::promoteWholeLineWrites(PacketPtr pkt) +{ + // Cache line clearing instructions + if (doFastWrites && (pkt->cmd == MemCmd::WriteReq) && + (pkt->getSize() == blkSize) && (pkt->getOffset(blkSize) == 0)) { + pkt->cmd = MemCmd::WriteInvalidateReq; + DPRINTF(Cache, "packet promoted from Write to WriteInvalidate\n"); + assert(isTopLevel); // should only happen at L1 or I/O cache + } +} + +template<class TagStore> bool Cache<TagStore>::recvTimingReq(PacketPtr pkt) { @@ -439,6 +466,8 @@ return true; } + promoteWholeLineWrites(pkt); + if (pkt->memInhibitAsserted()) { DPRINTF(Cache, "mem inhibited on 0x%x (%s): not responding\n", pkt->getAddr(), pkt->isSecure() ? "s" : "ns"); @@ -496,35 +525,26 @@ bool satisfied = access(pkt, blk, lat, writebacks); -#if 0 - /** @todo make the fast write alloc (wh64) work with coherence. */ - - // If this is a block size write/hint (WH64) allocate the block here - // if the coherence protocol allows it. - if (!blk && pkt->getSize() >= blkSize && coherence->allowFastWrites() && - (pkt->cmd == MemCmd::WriteReq - || pkt->cmd == MemCmd::WriteInvalidateReq) ) { - // not outstanding misses, can do this - MSHR *outstanding_miss = mshrQueue.findMatch(pkt->getAddr(), - pkt->isSecure()); - if (pkt->cmd == MemCmd::WriteInvalidateReq || !outstanding_miss) { - if (outstanding_miss) { - warn("WriteInv doing a fastallocate" - "with an outstanding miss to the same address\n"); - } - blk = handleFill(NULL, pkt, BlkValid | BlkWritable, - writebacks); - ++fastWrites; - } - } -#endif - // track time of availability of next prefetch, if any Tick next_pf_time = 0; bool needsResponse = pkt->needsResponse(); + if (pkt->cmd == MemCmd::WriteInvalidateReq) { + if (!satisfied && isTopLevel) { + // access() tried to allocate a block but it could not; abort. + setBlocked(Blocked_PendingWriteInvalidate); + return false; + } + satisfied = false; + // we need to take the miss path (allocate MSHR, etc.) for + // WriteInvalidates because they always need to propagate + // throughout the memory system + } + if (satisfied) { + // hit (for all other request types) + if (prefetcher && (prefetchOnAccess || (blk && blk->wasPrefetched()))) { if (blk) blk->status &= ~BlkHWPrefetched; @@ -551,6 +571,16 @@ // @todo: Make someone pay for this pkt->busFirstWordDelay = pkt->busLastWordDelay = 0; + if (blk && blk->isValid() && (blk->status & BlkCanGoExclusive) && + pkt->isWrite() && (pkt->cmd != MemCmd::WriteInvalidateReq)) { + // Packet is a Write (needs exclusive) should be delayed because + // a WriteInvalidate is pending. Instead of going the MSHR route, + // the Packet should be replayed, since if the block transitions + // to Exclusive the write can complete immediately. + setBlocked(Blocked_PendingWriteInvalidate); + return false; + } + Addr blk_addr = blockAlign(pkt->getAddr()); MSHR *mshr = mshrQueue.findMatch(blk_addr, pkt->isSecure()); @@ -639,7 +669,10 @@ if (pkt->cmd == MemCmd::Writeback) { allocateWriteBuffer(pkt, time, true); } else { - if (blk && blk->isValid()) { + if (pkt->cmd == MemCmd::WriteInvalidateReq) { + // a WriteInvalidate is not a normal write miss; + // the assertions below are not applicable. + } else if (blk && blk->isValid()) { // If we have a write miss to a valid block, we // need to mark the block non-readable. Otherwise // if we allow reads while there's an outstanding @@ -655,7 +688,8 @@ // internally, and have a sufficiently weak memory // model, this is probably unnecessary, but at some // point it must have seemed like we needed it... - assert(pkt->needsExclusive() && !blk->isWritable()); + assert(pkt->needsExclusive()); + assert(!blk->isWritable()); blk->status &= ~BlkReadable; } @@ -697,6 +731,12 @@ return NULL; } + // WriteInvalidates for cache line clearing instructions don't + // require a read; just send directly to the bus. + if (cpu_pkt->cmd == MemCmd::WriteInvalidateReq) { + return NULL; + } + if (!blkValid && (cpu_pkt->cmd == MemCmd::Writeback || cpu_pkt->isUpgrade())) { // Writebacks that weren't allocated in access() and upgrades @@ -716,7 +756,8 @@ if (blkValid && useUpgrades) { // only reason to be here is that blk is shared // (read-only) and we need exclusive - assert(needsExclusive && !blk->isWritable()); + assert(needsExclusive); + assert(!blk->isWritable()); cmd = cpu_pkt->isLLSC() ? MemCmd::SCUpgradeReq : MemCmd::UpgradeReq; } else if (cpu_pkt->cmd == MemCmd::SCUpgradeFailReq || cpu_pkt->cmd == MemCmd::StoreCondFailReq) { @@ -751,6 +792,8 @@ if (system->bypassCaches()) return ticksToCycles(memSidePort->sendAtomic(pkt)); + promoteWholeLineWrites(pkt); + _______________________________________________ gem5-dev mailing list gem5-dev@gem5.org http://m5sim.org/mailman/listinfo/gem5-dev