/* * Copyright (c) 2010-2018 ARM Limited * All rights reserved. * * The license below extends only to copyright in the software and shall * not be construed as granting a license to any other intellectual * property including but not limited to intellectual property relating * to a hardware implementation of the functionality of the software * licensed hereunder. You may use the software subject to the license * terms below provided that you ensure that this notice is replicated * unmodified and in its entirety in all distributions of the software, * modified or unmodified, in source code or in binary form. * * Copyright (c) 2002-2005 The Regents of The University of Michigan * Copyright (c) 2010,2015 Advanced Micro Devices, Inc. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer; * redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution; * neither the name of the copyright holders nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * Authors: Erik Hallnor * Dave Greene * Nathan Binkert * Steve Reinhardt * Ron Dreslinski * Andreas Sandberg * Nikos Nikoleris */ /** * @file * Cache definitions. */ #include "mem/cache/noncoherent_cache.hh" #include #include "base/logging.hh" #include "base/trace.hh" #include "base/types.hh" #include "debug/Cache.hh" #include "mem/cache/cache_blk.hh" #include "mem/cache/mshr.hh" #include "params/NoncoherentCache.hh" NoncoherentCache::NoncoherentCache(const NoncoherentCacheParams *p) : BaseCache(p, p->system->cacheLineSize()) { } void NoncoherentCache::satisfyRequest(PacketPtr pkt, CacheBlk *blk, bool, bool) { // As this a non-coherent cache located below the point of // coherency, we do not expect requests that are typically used to // keep caches coherent (e.g., InvalidateReq or UpdateReq). assert(pkt->isRead() || pkt->isWrite()); BaseCache::satisfyRequest(pkt, blk); } bool NoncoherentCache::access(PacketPtr pkt, CacheBlk *&blk, Cycles &lat, PacketList &writebacks) { bool success = BaseCache::access(pkt, blk, lat, writebacks); if (pkt->isWriteback() || pkt->cmd == MemCmd::WriteClean) { assert(blk && blk->isValid()); // Writeback and WriteClean can allocate and fill even if the // referenced block was not present or it was invalid. If that // is the case, make sure that the new block is marked as // writable blk->status |= BlkWritable; } return success; } void NoncoherentCache::doWritebacks(PacketList& writebacks, Tick forward_time) { while (!writebacks.empty()) { PacketPtr wb_pkt = writebacks.front(); allocateWriteBuffer(wb_pkt, forward_time); writebacks.pop_front(); } } void NoncoherentCache::doWritebacksAtomic(PacketList& writebacks) { while (!writebacks.empty()) { PacketPtr wb_pkt = writebacks.front(); memSidePort.sendAtomic(wb_pkt); writebacks.pop_front(); delete wb_pkt; } } void NoncoherentCache::handleTimingReqMiss(PacketPtr pkt, CacheBlk *blk, Tick forward_time, Tick request_time) { // miss Addr blk_addr = pkt->getBlockAddr(blkSize); MSHR *mshr = mshrQueue.findMatch(blk_addr, pkt->isSecure(), false); // We can always write to a non coherent cache if the block is // present and therefore if we have reached this point then the // block should not be in the cache. assert(mshr || !blk || !blk->isValid()); BaseCache::handleTimingReqMiss(pkt, mshr, blk, forward_time, request_time); } void NoncoherentCache::recvTimingReq(PacketPtr pkt) { panic_if(pkt->cacheResponding(), "Should not see packets where cache " "is responding"); panic_if(!(pkt->isRead() || pkt->isWrite()), "Should only see read and writes at non-coherent cache\n"); BaseCache::recvTimingReq(pkt); } PacketPtr NoncoherentCache::createMissPacket(PacketPtr cpu_pkt, CacheBlk *blk, bool needs_writable, bool is_whole_line_write) const { // We also fill for writebacks from the coherent caches above us, // and they do not need responses assert(cpu_pkt->needsResponse()); // A miss can happen only due to missing block assert(!blk || !blk->isValid()); PacketPtr pkt = new Packet(cpu_pkt->req, MemCmd::ReadReq, blkSize); // the packet should be block aligned assert(pkt->getAddr() == pkt->getBlockAddr(blkSize)); pkt->allocate(); DPRINTF(Cache, "%s created %s from %s\n", __func__, pkt->print(), cpu_pkt->print()); return pkt; } Cycles NoncoherentCache::handleAtomicReqMiss(PacketPtr pkt, CacheBlk *&blk, PacketList &writebacks) { PacketPtr bus_pkt = createMissPacket(pkt, blk, true, pkt->isWholeLineWrite(blkSize)); DPRINTF(Cache, "Sending an atomic %s\n", bus_pkt->print()); Cycles latency = ticksToCycles(memSidePort.sendAtomic(bus_pkt)); assert(bus_pkt->isResponse()); // At the moment the only supported downstream requests we issue // are ReadReq and therefore here we should only see the // corresponding responses assert(bus_pkt->isRead()); assert(pkt->cmd != MemCmd::UpgradeResp); assert(!bus_pkt->isInvalidate()); assert(!bus_pkt->hasSharers()); // We are now dealing with the response handling DPRINTF(Cache, "Receive response: %s\n", bus_pkt->print()); if (!bus_pkt->isError()) { // Any reponse that does not have an error should be filling, // afterall it is a read response DPRINTF(Cache, "Block for addr %#llx being updated in Cache\n", bus_pkt->getAddr()); blk = handleFill(bus_pkt, blk, writebacks, allocOnFill(bus_pkt->cmd)); assert(blk); } satisfyRequest(pkt, blk); maintainClusivity(true, blk); // Use the separate bus_pkt to generate response to pkt and // then delete it. if (!pkt->isWriteback() && pkt->cmd != MemCmd::WriteClean) { assert(pkt->needsResponse()); pkt->makeAtomicResponse(); if (bus_pkt->isError()) { pkt->copyError(bus_pkt); } } delete bus_pkt; return latency; } Tick NoncoherentCache::recvAtomic(PacketPtr pkt) { panic_if(pkt->cacheResponding(), "Should not see packets where cache " "is responding"); panic_if(!(pkt->isRead() || pkt->isWrite()), "Should only see read and writes at non-coherent cache\n"); return BaseCache::recvAtomic(pkt); } void NoncoherentCache::functionalAccess(PacketPtr pkt, bool from_cpu_side) { panic_if(!from_cpu_side, "Non-coherent cache received functional snoop" " request\n"); BaseCache::functionalAccess(pkt, from_cpu_side); } void NoncoherentCache::serviceMSHRTargets(MSHR *mshr, const PacketPtr pkt, CacheBlk *blk) { MSHR::Target *initial_tgt = mshr->getTarget(); // First offset for critical word first calculations const int initial_offset = initial_tgt->pkt->getOffset(blkSize); MSHR::TargetList targets = mshr->extractServiceableTargets(pkt); for (auto &target: targets) { Packet *tgt_pkt = target.pkt; switch (target.source) { case MSHR::Target::FromCPU: // handle deferred requests comming from a cache or core // above Tick completion_time; // Here we charge on completion_time the delay of the xbar if the // packet comes from it, charged on headerDelay. completion_time = pkt->headerDelay; satisfyRequest(tgt_pkt, blk); // How many bytes past the first request is this one int transfer_offset; transfer_offset = tgt_pkt->getOffset(blkSize) - initial_offset; if (transfer_offset < 0) { transfer_offset += blkSize; } // If not critical word (offset) return payloadDelay. // responseLatency is the latency of the return path // from lower level caches/memory to an upper level cache or // the core. completion_time += clockEdge(responseLatency) + (transfer_offset ? pkt->payloadDelay : 0); assert(tgt_pkt->req->masterId() < system->maxMasters()); missLatency[tgt_pkt->cmdToIndex()][tgt_pkt->req->masterId()] += completion_time - target.recvTime; tgt_pkt->makeTimingResponse(); if (pkt->isError()) tgt_pkt->copyError(pkt); // Reset the bus additional time as it is now accounted for tgt_pkt->headerDelay = tgt_pkt->payloadDelay = 0; cpuSidePort.schedTimingResp(tgt_pkt, completion_time); break; case MSHR::Target::FromPrefetcher: // handle deferred requests comming from a prefetcher // attached to this cache assert(tgt_pkt->cmd == MemCmd::HardPFReq); if (blk) blk->status |= BlkHWPrefetched; // We have filled the block and the prefetcher does not // require responses. delete tgt_pkt; break; default: // we should never see FromSnoop Targets as this is a // non-coherent cache panic("Illegal target->source enum %d\n", target.source); } } // Reponses are filling and bring in writable blocks, therefore // there should be no deferred targets and all the non-deferred // targets are now serviced. assert(mshr->getNumTargets() == 0); } void NoncoherentCache::recvTimingResp(PacketPtr pkt) { assert(pkt->isResponse()); // At the moment the only supported downstream requests we issue // are ReadReq and therefore here we should only see the // corresponding responses assert(pkt->isRead()); assert(pkt->cmd != MemCmd::UpgradeResp); assert(!pkt->isInvalidate()); // This cache is non-coherent and any memories below are // non-coherent too (non-coherent caches or the main memory), // therefore the fetched block can be marked as writable. assert(!pkt->hasSharers()); BaseCache::recvTimingResp(pkt); } PacketPtr NoncoherentCache::evictBlock(CacheBlk *blk) { // A dirty block is always written back. // A clean block can we written back, if we turned on writebacks // for clean blocks. This could be useful if there is a cache // below and we want to make sure the block is cached but if the // memory below is the main memory WritebackCleans are // unnecessary. // If we clean writebacks are not enabled, we do not take any // further action for evictions of clean blocks (i.e., CleanEvicts // are unnecessary). PacketPtr pkt = (blk->isDirty() || writebackClean) ? writebackBlk(blk) : nullptr; invalidateBlock(blk); return pkt; } NoncoherentCache* NoncoherentCacheParams::create() { assert(tags); assert(replacement_policy); return new NoncoherentCache(this); }