summaryrefslogtreecommitdiff
path: root/src/mem/cache/noncoherent_cache.cc
diff options
context:
space:
mode:
Diffstat (limited to 'src/mem/cache/noncoherent_cache.cc')
-rw-r--r--src/mem/cache/noncoherent_cache.cc375
1 files changed, 375 insertions, 0 deletions
diff --git a/src/mem/cache/noncoherent_cache.cc b/src/mem/cache/noncoherent_cache.cc
new file mode 100644
index 000000000..bf75be4d6
--- /dev/null
+++ b/src/mem/cache/noncoherent_cache.cc
@@ -0,0 +1,375 @@
+/*
+ * 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 <cassert>
+
+#include "base/logging.hh"
+#include "base/trace.hh"
+#include "base/types.hh"
+#include "debug/Cache.hh"
+#include "mem/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) 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);
+ 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, PacketList &writebacks)
+{
+ 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, true);
+ 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->req;
+ 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;
+}
+
+void
+NoncoherentCache::evictBlock(CacheBlk *blk, PacketList &writebacks)
+{
+ PacketPtr pkt = evictBlock(blk);
+ if (pkt) {
+ writebacks.push_back(pkt);
+ }
+}
+
+NoncoherentCache*
+NoncoherentCacheParams::create()
+{
+ assert(tags);
+ assert(replacement_policy);
+
+ return new NoncoherentCache(this);
+}