Nikos Nikoleris has uploaded this change for review. ( https://gem5-review.googlesource.com/8291

Change subject: mem-cache: Add a non-coherent cache
......................................................................

mem-cache: Add a non-coherent cache

The class re-uses the existing MSHR and write queue. At the moment
every single access is handled by the cache, even uncacheable
accesses, and nothing is forwarded.

This is a modified version of a changeset originally put together by
Andreas Hansson <andreas.hans...@arm.com>

Change-Id: I41f7f9c2b8c7fa5ec23712a4446e8adb1c9a336a
---
M configs/example/memtest.py
M src/mem/cache/Cache.py
M src/mem/cache/SConscript
A src/mem/cache/noncoherent_cache.cc
A src/mem/cache/noncoherent_cache.hh
M src/mem/cache/queue.hh
M tests/configs/base_config.py
7 files changed, 1,515 insertions(+), 12 deletions(-)



diff --git a/configs/example/memtest.py b/configs/example/memtest.py
index d6f940e..fea5a6e 100644
--- a/configs/example/memtest.py
+++ b/configs/example/memtest.py
@@ -1,4 +1,4 @@
-# Copyright (c) 2015 ARM Limited
+# Copyright (c) 2015, 2018 ARM Limited
 # All rights reserved.
 #
 # The license below extends only to copyright in the software and shall
@@ -81,6 +81,8 @@
                   help="Colon-separated cache hierarchy specification, "
                   "see script comments for details "
                   "[default: %default]")
+parser.add_option("--noncoherent-cache", action="store_true",
+                  help="Adds a non-coherent, last-level cache")
 parser.add_option("-t", "--testers", type="string", default="1:1:0:2",
                   help="Colon-separated tester hierarchy specification, "
                   "see script comments for details "
@@ -297,10 +299,19 @@
 # Top level call to create the cache hierarchy, bottom up
 make_cache_level(cachespec, cache_proto, len(cachespec), None)

-# Connect the lowest level crossbar to the memory
+# Connect the lowest level crossbar to the last-level cache and memory
+# controller
 last_subsys = getattr(system, 'l%dsubsys0' % len(cachespec))
-last_subsys.xbar.master = system.physmem.port
 last_subsys.xbar.point_of_coherency = True
+if options.noncoherent_cache:
+ system.llc = NoncoherentCache(size = '16MB', assoc = 16, tag_latency = 10, + data_latency = 10, sequential_access = True, + response_latency = 20, tgts_per_mshr = 8,
+                                   mshrs = 64)
+     last_subsys.xbar.master = system.llc.cpu_side
+     system.llc.mem_side = system.physmem.port
+else:
+     last_subsys.xbar.master = system.physmem.port

 root = Root(full_system = False, system = system)
 if options.atomic:
diff --git a/src/mem/cache/Cache.py b/src/mem/cache/Cache.py
index bac6c73..d07c5ff 100644
--- a/src/mem/cache/Cache.py
+++ b/src/mem/cache/Cache.py
@@ -1,4 +1,4 @@
-# Copyright (c) 2012-2013, 2015 ARM Limited
+# Copyright (c) 2012-2013, 2015, 2018 ARM Limited
 # All rights reserved.
 #
 # The license below extends only to copyright in the software and shall
@@ -113,3 +113,7 @@
     # this should be set to True for anything but the last-level
     # cache.
     writeback_clean = Param.Bool(False, "Writeback clean lines")
+
+class NoncoherentCache(BaseCache):
+    type = 'NoncoherentCache'
+    cxx_header = 'mem/cache/noncoherent_cache.hh'
diff --git a/src/mem/cache/SConscript b/src/mem/cache/SConscript
index 1c9b002..9df175e 100644
--- a/src/mem/cache/SConscript
+++ b/src/mem/cache/SConscript
@@ -32,11 +32,12 @@

 SimObject('Cache.py')

+Source('blk.cc')
 Source('base.cc')
 Source('cache.cc')
-Source('blk.cc')
 Source('mshr.cc')
 Source('mshr_queue.cc')
+Source('noncoherent_cache.cc')
 Source('write_queue.cc')
 Source('write_queue_entry.cc')

diff --git a/src/mem/cache/noncoherent_cache.cc b/src/mem/cache/noncoherent_cache.cc
new file mode 100644
index 0000000..641e72d
--- /dev/null
+++ b/src/mem/cache/noncoherent_cache.cc
@@ -0,0 +1,1135 @@
+/*
+ * 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 "base/logging.hh"
+#include "base/types.hh"
+#include "debug/Cache.hh"
+#include "debug/CachePort.hh"
+#include "debug/CacheTags.hh"
+#include "debug/CacheVerbose.hh"
+#include "mem/cache/blk.hh"
+#include "mem/cache/mshr.hh"
+#include "mem/cache/prefetch/base.hh"
+#include "sim/sim_exit.hh"
+
+NoncoherentCache::NoncoherentCache(const NoncoherentCacheParams *p)
+    : BaseCache(p, p->system->cacheLineSize()),
+      writebackTempBlockAtomicEvent([this]{ writebackTempBlockAtomic(); },
+                                    name(), false,
+                                    EventBase::Delayed_Writeback_Pri)
+{
+    cpuSidePort = new CpuSidePort(p->name + ".cpu_side", this,
+                                  "CpuSidePort");
+    memSidePort = new MemSidePort(p->name + ".mem_side", this,
+                                  "MemSidePort");
+}
+
+NoncoherentCache::~NoncoherentCache()
+{
+    delete cpuSidePort;
+    delete memSidePort;
+}
+
+void
+NoncoherentCache::regStats()
+{
+    BaseCache::regStats();
+}
+
+void
+NoncoherentCache::satisfyRequest(PacketPtr pkt, CacheBlk *blk)
+{
+    assert(pkt->isRequest());
+
+    assert(blk && blk->isValid() && blk->isReadable());
+    // 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->needsWritable() || blk->isWritable());
+    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()) {
+        // we have the block in a writable state and can go ahead,
+        // note that the line may be also be considered writable in
+        // downstream caches along the path to memory, but always
+        // Exclusive, and never Modified
+        assert(blk->isWritable());
+ // Write or WriteLine at the first cache with block in writable state
+        if (blk->checkWrite(pkt)) {
+            pkt->writeDataToBlock(blk->data, blkSize);
+        }
+        // Always mark the line as dirty (and thus transition to the
+        // Modified state) even if we are a failed StoreCond so we
+        // supply data to any snoops that have appended themselves to
+        // this cache before knowing the store will fail.
+        blk->status |= BlkDirty;
+ DPRINTF(CacheVerbose, "%s for %s (write)\n", __func__, pkt->print());
+    } else if (pkt->isRead()) {
+        if (pkt->isLLSC()) {
+            blk->trackLoadLocked(pkt);
+        }
+
+        // all read responses have a data payload
+        assert(pkt->hasRespData());
+        pkt->setDataFromBlock(blk->data, blkSize);
+    } else {
+        panic("Unexpected packet: %s", pkt->print());
+    }
+}
+
+/////////////////////////////////////////////////////
+//
+// Access path: requests coming in from the CPU side
+//
+/////////////////////////////////////////////////////
+
+bool
+NoncoherentCache::access(PacketPtr pkt, CacheBlk *&blk, Cycles &lat,
+                         PacketList &writebacks)
+{
+    // sanity check
+    assert(pkt->isRequest());
+
+    chatty_assert(!(isReadOnly && pkt->isWrite()),
+                  "Should never see a write in a read-only cache %s\n",
+                  name());
+
+    DPRINTF(CacheVerbose, "%s for %s\n", __func__, pkt->print());
+
+    // Here lat is the value passed as parameter to accessBlock() function
+    // that can modify its value.
+    blk = tags->accessBlock(pkt->getAddr(), pkt->isSecure(), lat);
+
+    DPRINTF(Cache, "%s %s\n", pkt->print(),
+            blk ? "hit " + blk->print() : "miss");
+
+    assert(!pkt->req->isCacheMaintenance());
+
+    if (pkt->isEviction()) {
+        // 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.
+        WriteQueueEntry *wb_entry = writeBuffer.findMatch(pkt->getAddr(),
+                                                          pkt->isSecure());
+        if (wb_entry) {
+            assert(wb_entry->getNumTargets() == 1);
+            PacketPtr wbPkt = wb_entry->getTarget()->pkt;
+            assert(wbPkt->isWriteback());
+
+            if (pkt->isCleanEviction()) {
+                // The CleanEvict and WritebackClean snoops into other
+                // peer caches of the same level while traversing the
+                // crossbar. If a copy of the block is found, the
+                // packet is deleted in the crossbar. Hence, 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;
+            } else {
+                assert(pkt->cmd == MemCmd::WritebackDirty);
+                // Dirty writeback from above trumps our clean
+                // writeback... discard here
+ // Note: markInService will remove entry from writeback buffer.
+                markInService(wb_entry);
+                delete wbPkt;
+            }
+        }
+    }
+
+    // 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->isWriteback()) {
+        assert(blkSize == pkt->getSize());
+
+        // we could get a clean writeback while we are having
+        // outstanding accesses to a block, do the simple thing for
+        // now and drop the clean writeback so that we do not upset
+        // any ordering/decisions about ownership already taken
+        if (pkt->cmd == MemCmd::WritebackClean &&
+            mshrQueue.findMatch(pkt->getAddr(), pkt->isSecure())) {
+            DPRINTF(Cache, "Clean writeback %#llx to block with MSHR, "
+                    "dropping\n", pkt->getAddr());
+            return true;
+        }
+
+        if (!blk) {
+            // need to do a replacement
+ blk = allocateBlock(pkt->getAddr(), pkt->isSecure(), writebacks);
+            if (!blk) {
+ // no replaceable block available: give up, fwd to next level.
+                incMissCount(pkt);
+                return false;
+            }
+            tags->insertBlock(pkt, blk);
+
+            blk->status = (BlkValid | BlkReadable);
+            if (pkt->isSecure()) {
+                blk->status |= BlkSecure;
+            }
+        }
+        // only mark the block dirty if we got a writeback command,
+        // and leave it as is for a clean writeback
+        if (pkt->cmd == MemCmd::WritebackDirty) {
+            blk->status |= BlkDirty;
+        }
+        blk->status |= BlkWritable;
+        // nothing else to do; writeback doesn't expect response
+        assert(!pkt->needsResponse());
+        std::memcpy(blk->data, pkt->getConstPtr<uint8_t>(), blkSize);
+        DPRINTF(Cache, "%s new state is %s\n", __func__, blk->print());
+        incHitCount(pkt);
+        // populate the time when the block will be ready to access.
+        blk->whenReady = clockEdge(fillLatency) + pkt->headerDelay +
+            pkt->payloadDelay;
+        return true;
+    } else if (pkt->cmd == MemCmd::CleanEvict) {
+        // 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;
+    } else if (pkt->cmd == MemCmd::WriteClean) {
+        // WriteClean handling is a special case. We can allocate a
+        // block directly if it doesn't exist and we can update the
+        // block immediately. The WriteClean transfers the ownership
+        // of the block as well.
+        assert(blkSize == pkt->getSize());
+
+        if (!blk) {
+            if (pkt->writeThrough()) {
+                // if this is a write through packet, we don't try to
+                // allocate if the block is not present
+                return false;
+            } else {
+                // a writeback that misses needs to allocate a new block
+                blk = allocateBlock(pkt->getAddr(), pkt->isSecure(),
+                                    writebacks);
+                if (!blk) {
+                    // no replaceable block available: give up, fwd to
+                    // next level.
+                    incMissCount(pkt);
+                    return false;
+                }
+                tags->insertBlock(pkt, blk);
+
+                // All blocks in a non-coherent cache are writeable
+                blk->status = (BlkValid | BlkReadable | BlkWritable);
+                if (pkt->isSecure()) {
+                    blk->status |= BlkSecure;
+                }
+            }
+        }
+
+        // at this point either this is a writeback or a write-through
+        // write clean operation and the block is already in this
+        // cache, we need to update the data and the block flags
+        assert(blk);
+        if (!pkt->writeThrough()) {
+            blk->status |= BlkDirty;
+        }
+        // nothing else to do; writeback doesn't expect response
+        assert(!pkt->needsResponse());
+        std::memcpy(blk->data, pkt->getConstPtr<uint8_t>(), blkSize);
+        DPRINTF(Cache, "%s new state is %s\n", __func__, blk->print());
+
+        incHitCount(pkt);
+        // populate the time when the block will be ready to access.
+        blk->whenReady = clockEdge(fillLatency) + pkt->headerDelay +
+            pkt->payloadDelay;
+        // if this a write-through packet it will be sent to cache
+        // below
+        return !pkt->writeThrough();
+    } else if (blk && (pkt->needsWritable() ? blk->isWritable() :
+                       blk->isReadable())) {
+        // OK to satisfy access
+        incHitCount(pkt);
+        satisfyRequest(pkt, blk);
+
+        return true;
+    }
+
+    // Can't satisfy access normally... either no block (blk == nullptr)
+    // or have block but need writable
+
+    incMissCount(pkt);
+
+    if (!blk && pkt->isLLSC() && pkt->isWrite()) {
+        // complete miss on store conditional... just give up now
+        pkt->req->setExtraData(0);
+        return true;
+    }
+
+    return false;
+}
+
+void
+NoncoherentCache::doWritebacks(PacketList& writebacks, Tick forward_time)
+{
+    while (!writebacks.empty()) {
+        PacketPtr wbPkt = writebacks.front();
+        allocateWriteBuffer(wbPkt, forward_time);
+        writebacks.pop_front();
+    }
+}
+
+void
+NoncoherentCache::doWritebacksAtomic(PacketList& writebacks)
+{
+    while (!writebacks.empty()) {
+        PacketPtr wbPkt = writebacks.front();
+        memSidePort->sendAtomic(wbPkt);
+        writebacks.pop_front();
+        delete wbPkt;
+    }
+}
+
+void
+NoncoherentCache::recvTimingReq(PacketPtr pkt)
+{
+    DPRINTF(CacheTags, "%s tags: %s\n", __func__, tags->print());
+
+    assert(pkt->isRequest());
+
+    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");
+
+    // anything that is merely forwarded pays for the forward latency and
+    // the delay provided by the crossbar
+    Tick forward_time = clockEdge(forwardLatency) + pkt->headerDelay;
+
+    // We use lookupLatency here because it is used to specify the latency
+    // to access.
+    Cycles lat = lookupLatency;
+    CacheBlk *blk = nullptr;
+    bool satisfied = false;
+    {
+        PacketList writebacks;
+        // Note that lat is passed by reference here. The function
+        // access() calls accessBlock() which can modify lat value.
+        satisfied = access(pkt, blk, lat, writebacks);
+
+        // copy writebacks to write buffer here to ensure they logically
+        // proceed anything happening below
+        doWritebacks(writebacks, forward_time);
+    }
+
+    // Here we charge the headerDelay that takes into account the latencies
+    // of the bus, if the packet comes from it.
+ // The latency charged it is just lat that is the value of lookupLatency
+    // modified by access() function, or if not just lookupLatency.
+    // In case of a hit we are neglecting response latency.
+    // In case of a miss we are neglecting forward latency.
+    Tick request_time = clockEdge(lat) + pkt->headerDelay;
+    // Here we reset the timing of the packet.
+    pkt->headerDelay = pkt->payloadDelay = 0;
+
+    // track time of availability of next prefetch, if any
+    Tick next_pf_time = MaxTick;
+
+    bool needsResponse = pkt->needsResponse();
+
+    if (satisfied) {
+        // hit (for all other request types)
+        if (prefetcher && (prefetchOnAccess ||
+                           (blk && blk->wasPrefetched()))) {
+            if (blk)
+                blk->status &= ~BlkHWPrefetched;
+
+            // Don't notify on SWPrefetch
+            if (!pkt->cmd.isSWPrefetch()) {
+                next_pf_time = prefetcher->notify(pkt);
+            }
+        }
+
+        if (needsResponse) {
+            pkt->makeTimingResponse();
+            // @todo: Make someone pay for this
+            pkt->headerDelay = pkt->payloadDelay = 0;
+
+            // In this case we are considering request_time that takes
+            // into account the delay of the xbar, if any, and just
+            // lat, neglecting responseLatency, modelling hit latency
+            // just as lookupLatency or or the value of lat overriden
+            // by access(), that calls accessBlock() function.
+            cpuSidePort->schedTimingResp(pkt, request_time, true);
+        } else {
+ DPRINTF(Cache, "%s satisfied %s, no response needed\n", __func__,
+                    pkt->print());
+            // we delete the packet and the request when the fill
+            // occurs
+            assert(pkt->isWrite());
+        }
+    } else {
+        // miss
+        Addr blk_addr = pkt->getBlockAddr(blkSize);
+        MSHR *mshr = mshrQueue.findMatch(blk_addr, pkt->isSecure(), false);
+        if (mshr) {
+            /// MSHR hit
+            /// @note writebacks will be checked in getNextMSHR()
+            /// for any conflicting requests to the same block
+
+            //@todo remove hw_pf here
+
+            // Coalesce
+            if (pkt) {
+                // CleanEvicts corresponding to blocks which have
+                // outstanding requests in MSHRs are simply sunk here
+                if (pkt->cmd == MemCmd::CleanEvict) {
+                    pendingDelete.reset(pkt);
+                } else {
+                    DPRINTF(Cache, "%s coalescing MSHR for %s\n", __func__,
+                            pkt->print());
+
+                    assert(pkt->req->masterId() < system->maxMasters());
+                    mshr_hits[pkt->cmdToIndex()][pkt->req->masterId()]++;
+                    // 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++,
+                                         allocOnFill(pkt->cmd));
+                    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 + // satisfied (potentially because of a prior write to the same + // cache line. So, even when not satisfied, tehre is an MSHR + // already allocated for this, we need to let the prefetcher
+                // know about the request
+                if (prefetcher) {
+                    // Don't notify on SWPrefetch
+                    if (!pkt->cmd.isSWPrefetch())
+                        next_pf_time = prefetcher->notify(pkt);
+                }
+            }
+        } else {
+            // no MSHR
+            assert(pkt->req->masterId() < system->maxMasters());
+            mshr_misses[pkt->cmdToIndex()][pkt->req->masterId()]++;
+
+            assert(!pkt->isEviction() && pkt->cmd != MemCmd::WriteClean);
+            // We can always write to a non coherent cache if the
+            // block is present.
+            assert(!blk || !blk->isValid());
+            // Here we are using forward_time, modelling the latency of
+            // a miss (outbound) just as forwardLatency, neglecting the
+            // lookupLatency component.
+            allocateMissBuffer(pkt, forward_time);
+
+            if (prefetcher) {
+                // Don't notify on SWPrefetch
+                if (!pkt->cmd.isSWPrefetch())
+                    next_pf_time = prefetcher->notify(pkt);
+            }
+        }
+    }
+
+    if (next_pf_time != MaxTick)
+        schedMemSideSendEvent(next_pf_time);
+}
+
+PacketPtr
+NoncoherentCache::createMissPacket(PacketPtr cpu_pkt) const
+{
+    // We also fill for writebacks from the coherent caches above us,
+    // and they do not need responses
+    assert(cpu_pkt->needsResponse());
+
+    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;
+}
+
+
+Tick
+NoncoherentCache::recvAtomic(PacketPtr pkt)
+{
+    // We are in atomic mode so we pay just for lookupLatency here.
+    Cycles lat = lookupLatency;
+
+    // should assert here that there are no outstanding MSHRs or
+    // writebacks... that would mean that someone used an atomic
+    // access in timing mode
+
+    CacheBlk *blk = nullptr;
+    PacketList writebacks;
+    bool satisfied = access(pkt, blk, lat, writebacks);
+
+    // handle writebacks resulting from the access here to ensure they
+    // logically proceed anything happening below
+    doWritebacksAtomic(writebacks);
+
+    if (!satisfied) {
+        // MISS
+        assert(!blk);
+        PacketPtr bus_pkt = createMissPacket(pkt);
+        DPRINTF(Cache, "Sending an atomic %s\n", bus_pkt->print());
+
+        lat += ticksToCycles(memSidePort->sendAtomic(bus_pkt));
+
+        assert(bus_pkt->isResponse());
+
+        // We are now dealing with the response handling
+        DPRINTF(Cache, "Receive response: %s\n", bus_pkt->print());
+        bool is_error = bus_pkt->isError();
+        if (!is_error) {
+            DPRINTF(Cache, "Block for addr %#llx being updated in Cache\n",
+                    bus_pkt->getAddr());
+            blk = handleFill(bus_pkt, writebacks);
+            assert(blk);
+        }
+        satisfyRequest(pkt, 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;
+    }
+
+    // Note that we don't invoke the prefetcher at all in atomic mode.
+    // It's not clear how to do it properly, particularly for
+    // prefetchers that aggressively generate prefetch candidates and
+    // rely on bandwidth contention to throttle them; these will tend
+    // to pollute the cache in atomic mode since there is no bandwidth
+    // contention.  If we ever do want to enable prefetching in atomic
+    // mode, though, this is the place to do it... see timingAccess()
+    // for an example (though we'd want to issue the prefetch(es)
+    // immediately rather than calling requestMemSideBus() as we do
+    // there).
+
+    // do any writebacks resulting from the response handling
+    doWritebacksAtomic(writebacks);
+
+    // if we used temp block, check to see if its valid and if so
+    // clear it out, but only do so after the call to recvAtomic is
+    // finished so that any downstream observers (such as a snoop
+    // filter), first see the fill, and only then see the eviction
+    if (blk == tempBlock && tempBlock->isValid()) {
+        // the atomic CPU calls recvAtomic for fetch and load/store
+        // sequentuially, and we may already have a tempBlock
+        // writeback from the fetch that we have not yet sent
+        if (tempBlockWriteback) {
+            // if that is the case, write the prevoius one back, and
+            // do not schedule any new event
+            writebackTempBlockAtomic();
+        } else {
+            // the writeback/clean eviction happens after the call to
+            // recvAtomic has finished (but before any successive
+            // calls), so that the response handling from the fill is
+            // allowed to happen first
+            schedule(writebackTempBlockAtomicEvent, curTick());
+        }
+
+        if (blk->isDirty()) {
+            tempBlockWriteback = writebackBlk(blk);
+        }
+        invalidateBlock(blk);
+    }
+
+    if (pkt->needsResponse()) {
+        pkt->makeAtomicResponse();
+    }
+
+    return lat * clockPeriod();
+}
+
+
+void
+NoncoherentCache::functionalAccess(PacketPtr pkt)
+{
+    Addr blk_addr = pkt->getBlockAddr(blkSize);
+    bool is_secure = pkt->isSecure();
+    CacheBlk *blk = tags->findBlock(pkt->getAddr(), is_secure);
+
+    pkt->pushLabel(name());
+
+    CacheBlkPrintWrapper cbpw(blk);
+
+    bool have_data = blk && blk->isValid()
+        && pkt->checkFunctional(&cbpw, blk_addr, is_secure, blkSize,
+                                blk->data);
+
+    bool done = have_data
+        || cpuSidePort->checkFunctional(pkt)
+        || mshrQueue.checkFunctional(pkt, blk_addr)
+        || writeBuffer.checkFunctional(pkt, blk_addr)
+        || memSidePort->checkFunctional(pkt);
+
+    DPRINTF(CacheVerbose, "functional %s %s%s%s\n", pkt->cmdString(),
+            (blk && blk->isValid()) ? "valid " : "",
+            have_data ? "data " : "", done ? "done " : "");
+
+    // We're leaving the cache, so pop cache->name() label
+    pkt->popLabel();
+
+    if (done) {
+        pkt->makeResponse();
+    } else {
+        memSidePort->sendFunctional(pkt);
+    }
+}
+
+
+/////////////////////////////////////////////////////
+//
+// Response handling: responses from the memory side
+//
+/////////////////////////////////////////////////////
+
+void
+NoncoherentCache::recvTimingResp(PacketPtr pkt)
+{
+    assert(pkt->isResponse());
+    assert(pkt->isRead());
+    assert(pkt->cmd != MemCmd::UpgradeResp);
+
+    // all header delay should be paid for by the crossbar, unless
+    // this is a prefetch response from above
+    panic_if(pkt->headerDelay != 0 && pkt->cmd != MemCmd::HardPFResp,
+             "%s saw a non-zero packet delay\n", name());
+
+    bool is_error = pkt->isError();
+
+    if (is_error) {
+ DPRINTF(Cache, "Cache received packet with error %s\n", pkt->print());
+    }
+
+    DPRINTF(Cache, "Handling response %s\n", pkt->print());
+
+    // we have dealt with any (uncacheable) writes above, from here on
+    // we know we are dealing with an MSHR due to a miss or a prefetch
+    MSHR *mshr = dynamic_cast<MSHR*>(pkt->popSenderState());
+    assert(mshr);
+    assert(!mshr->isForward);
+    assert(pkt->isRead());
+
+    if (mshr == noTargetMSHR) {
+        // we always clear at least one target
+        clearBlocked(Blocked_NoTargets);
+        noTargetMSHR = nullptr;
+    }
+
+    // Initial target is used just for stats
+    MSHR::Target *initial_tgt = mshr->getTarget();
+    int stats_cmd_idx = initial_tgt->pkt->cmdToIndex();
+    Tick miss_latency = curTick() - initial_tgt->recvTime;
+
+    assert(pkt->req->masterId() < system->maxMasters());
+    mshr_miss_latency[stats_cmd_idx][pkt->req->masterId()] += miss_latency;
+
+    bool wasFull = mshrQueue.isFull();
+
+    PacketList writebacks;
+
+    Tick forward_time = clockEdge(forwardLatency) + pkt->headerDelay;
+
+    // the response has no sharers, and is thus passing writable
+    assert(!pkt->hasSharers());
+    mshr->promoteWritable();
+
+    CacheBlk *blk = tags->findBlock(pkt->getAddr(), pkt->isSecure());
+    assert(!blk);
+    if (!is_error) {
+        DPRINTF(Cache, "Block for addr %#llx being updated in Cache\n",
+                pkt->getAddr());
+
+        blk = handleFill(pkt, writebacks);
+        assert(blk);
+    }
+
+    assert(!pkt->isInvalidate());
+
+    // First offset for critical word first calculations
+    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:
+            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;
+            assert(tgt_pkt->cmd != MemCmd::WriteLineReq);
+            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 (is_error)
+                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:
+            assert(tgt_pkt->cmd == MemCmd::HardPFReq);
+            if (blk)
+                blk->status |= BlkHWPrefetched;
+            delete tgt_pkt->req;
+            delete tgt_pkt;
+            break;
+
+          default:
+            panic("Illegal target->source enum %d\n", target.source);
+        }
+    }
+
+    // time to stop pretending and set the inService flag again
+    mshr->inService = true;
+    mshrQueue.deallocate(mshr);
+    if (wasFull && !mshrQueue.isFull()) {
+        clearBlocked(Blocked_NoMSHRs);
+    }
+
+    // Request the bus for a prefetch if this deallocation freed enough
+    // MSHRs for a prefetch to take place
+    if (prefetcher && mshrQueue.canPrefetch()) {
+        Tick next_pf_time = std::max(prefetcher->nextPrefetchReadyTime(),
+                                     clockEdge());
+        if (next_pf_time != MaxTick)
+            schedMemSideSendEvent(next_pf_time);
+    }
+
+    // reset the xbar additional timinig  as it is now accounted for
+    pkt->headerDelay = pkt->payloadDelay = 0;
+
+    // copy writebacks to write buffer
+    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()) {
+            PacketPtr wbPkt = writebackBlk(blk);
+            allocateWriteBuffer(wbPkt, forward_time);
+        }
+        invalidateBlock(blk);
+    }
+
+    DPRINTF(CacheVerbose, "%s: Leaving with %s\n", __func__, pkt->print());
+    delete pkt;
+}
+
+PacketPtr
+NoncoherentCache::writebackBlk(CacheBlk *blk)
+{
+    assert(blk && blk->isValid() && blk->isDirty());
+
+    writebacks[Request::wbMasterId]++;
+
+    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);
+
+    PacketPtr pkt = new Packet(req,  MemCmd::WritebackDirty);
+
+    DPRINTF(Cache, "Create %s writable: %d, dirty: %d\n",
+            pkt->print(), blk->isWritable(), blk->isDirty());
+
+    // make sure the block is not marked dirty
+    blk->status &= ~BlkDirty;
+
+    pkt->allocate();
+    std::memcpy(pkt->getPtr<uint8_t>(), blk->data, blkSize);
+
+    return pkt;
+}
+
+CacheBlk*
+NoncoherentCache::allocateBlock(Addr addr, bool is_secure,
+                                PacketList &writebacks)
+{
+    CacheBlk *blk = tags->findVictim(addr);
+
+    // It is valid to return nullptr if there is no victim
+    if (!blk)
+        return nullptr;
+
+    if (blk->isValid()) {
+        Addr repl_addr = tags->regenerateBlkAddr(blk->tag, blk->set);
+        MSHR *repl_mshr = mshrQueue.findMatch(repl_addr, blk->isSecure());
+        if (repl_mshr) {
+            // must be an outstanding upgrade request
+            // on a block we're about to replace...
+            assert(!blk->isWritable() || blk->isDirty());
+            assert(repl_mshr->needsWritable());
+            // too hard to replace block with transient state
+            // allocation failed, block not inserted
+            return nullptr;
+        } else {
+            DPRINTF(Cache, "replacement: replacing %#llx (%s) with %#llx "
+                    "(%s): %s\n", repl_addr, blk->isSecure() ? "s" : "ns",
+                    addr, is_secure ? "s" : "ns",
+                    blk->isDirty() ? "writeback" : "clean");
+
+            if (blk->wasPrefetched()) {
+                unusedPrefetches++;
+            }
+            // 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));
+            }
+        }
+    }
+
+    return blk;
+}
+
+// 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).
+CacheBlk*
+NoncoherentCache::handleFill(PacketPtr pkt, PacketList &writebacks)
+{
+    assert(pkt->isResponse());
+    Addr addr = pkt->getAddr();
+    bool is_secure = pkt->isSecure();
+
+    // When handling a fill, we should have no writes to this line.
+    assert(addr == pkt->getBlockAddr(blkSize));
+    assert(!writeBuffer.findMatch(addr, is_secure, false));
+
+    // better have read new data...
+    assert(pkt->hasData() && pkt->isRead());
+
+    CacheBlk *blk = allocateBlock(addr, is_secure, writebacks);
+    if (!blk) {
+        // No replaceable block... 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);
+        tempBlock->tag = tags->extractTag(addr);
+        DPRINTF(Cache, "using temp block for %#llx (%s)\n", addr,
+                is_secure ? "s" : "ns");
+    } else {
+        tags->insertBlock(pkt, blk);
+    }
+
+    if (is_secure)
+        blk->status |= BlkSecure;
+    blk->status |= BlkValid | BlkReadable | BlkWritable;
+
+    DPRINTF(Cache, "Block addr %#llx (%s) moving to state %s\n",
+            addr, is_secure ? "s" : "ns", blk->print());
+
+    // sanity checks
+    assert(pkt->getSize() == blkSize);
+    std::memcpy(blk->data, pkt->getConstPtr<uint8_t>(), blkSize);
+
+    // We pay for fillLatency here.
+    blk->whenReady = clockEdge() + fillLatency * clockPeriod() +
+        pkt->payloadDelay;
+
+    return blk;
+}
+
+bool
+NoncoherentCache::sendMSHRQueuePacket(MSHR* mshr)
+{
+    assert(mshr);
+
+    // use request from 1st target
+    PacketPtr tgt_pkt = mshr->getTarget()->pkt;
+
+    DPRINTF(Cache, "%s: MSHR %s\n", __func__, tgt_pkt->print());
+
+    // either a prefetch that is not present upstream, or a normal
+    // MSHR request, proceed to get the packet to send downstream
+    PacketPtr pkt = createMissPacket(tgt_pkt);
+    mshr->isForward = false;
+
+    // play it safe and append (rather than set) the sender state,
+    // as forwarded packets may already have existing state
+    pkt->pushSenderState(mshr);
+
+    if (!memSidePort->sendTimingReq(pkt)) {
+        // we are awaiting a retry, but we
+        // delete the packet and will be creating a new packet
+        // when we get the opportunity
+        delete pkt;
+
+        // note that we have now masked any requestBus and
+        // schedSendEvent (we will wait for a retry before
+        // doing anything), and this is so even if we do not
+        // care about this packet and might override it before
+        // it gets retried
+        return true;
+    } else {
+        // As part of the call to sendTimingReq the packet is
+        // forwarded to all neighbouring caches (and any caches
+        // above them) as a snoop. Thus at this point we know if
+        // any of the neighbouring caches are responding, and if
+        // so, we know it is dirty, and we can determine if it is
+        // being passed as Modified, making our MSHR the ordering
+        // point
+        bool pending_modified_resp = !pkt->hasSharers() &&
+            pkt->cacheResponding();
+        markInService(mshr, pending_modified_resp);
+        return false;
+    }
+}
+
+bool
+NoncoherentCache::sendWriteQueuePacket(WriteQueueEntry* wq_entry)
+{
+    assert(wq_entry);
+
+    // always a single target for write queue entries
+    PacketPtr tgt_pkt = wq_entry->getTarget()->pkt;
+
+    DPRINTF(Cache, "%s write %s\n", __func__, tgt_pkt->print());
+
+    // forward as is, both for evictions and uncacheable writes
+    if (!memSidePort->sendTimingReq(tgt_pkt)) {
+        // note that we have now masked any requestBus and
+        // schedSendEvent (we will wait for a retry before
+        // doing anything), and this is so even if we do not
+        // care about this packet and might override it before
+        // it gets retried
+        return true;
+    } else {
+        markInService(wq_entry);
+        return false;
+    }
+}
+
+///////////////
+//
+// CpuSidePort
+//
+///////////////
+
+AddrRangeList
+NoncoherentCache::CpuSidePort::getAddrRanges() const
+{
+    return cache->getAddrRanges();
+}
+
+bool
+NoncoherentCache::CpuSidePort::tryTiming(PacketPtr pkt)
+{
+    if (blocked || mustSendRetry) {
+        // either already committed to send a retry, or blocked
+        mustSendRetry = true;
+        return false;
+    }
+    mustSendRetry = false;
+    return true;
+}
+
+bool
+NoncoherentCache::CpuSidePort::recvTimingReq(PacketPtr pkt)
+{
+    if (tryTiming(pkt)) {
+        cache->recvTimingReq(pkt);
+        return true;
+    }
+    return false;
+}
+
+Tick
+NoncoherentCache::CpuSidePort::recvAtomic(PacketPtr pkt)
+{
+    return cache->recvAtomic(pkt);
+}
+
+void
+NoncoherentCache::CpuSidePort::recvFunctional(PacketPtr pkt)
+{
+    // functional request
+    cache->functionalAccess(pkt);
+}
+
+NoncoherentCache::
+CpuSidePort::CpuSidePort(const std::string &_name, NoncoherentCache *_cache,
+                         const std::string &_label)
+    : BaseCache::CacheSlavePort(_name, _cache, _label), cache(_cache)
+{
+}
+
+NoncoherentCache*
+NoncoherentCacheParams::create()
+{
+    assert(tags);
+
+    return new NoncoherentCache(this);
+}
+
+///////////////
+//
+// MemSidePort
+//
+///////////////
+
+bool
+NoncoherentCache::MemSidePort::recvTimingResp(PacketPtr pkt)
+{
+    cache->recvTimingResp(pkt);
+    return true;
+}
+
+void
+NoncoherentCache::CacheReqPacketQueue::sendDeferredPacket()
+{
+    // sanity check
+    assert(!waitingOnRetry);
+
+    // there should never be any deferred request packets in the
+    // queue, instead we resly on the cache to provide the packets
+    // from the MSHR queue or write queue
+    assert(deferredPacketReadyTime() == MaxTick);
+
+    // check for request packets (requests & writebacks)
+    QueueEntry* entry = cache.getNextQueueEntry();
+
+    if (!entry) {
+        // 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.
+    } else {
+        // let our snoop responses go first if there are responses to
+        // the same addresses
+        if (checkConflictingSnoop(entry->blkAddr)) {
+            return;
+        }
+        waitingOnRetry = entry->sendPacket(cache);
+    }
+
+    // if we succeeded and are not waiting for a retry, schedule the
+    // next send considering when the next queue is ready, note that
+    // snoop responses have their own packet queue and thus schedule
+    // their own events
+    if (!waitingOnRetry) {
+        schedSendEvent(cache.nextQueueReadyTime());
+    }
+}
+
+NoncoherentCache::
+MemSidePort::MemSidePort(const std::string &_name, NoncoherentCache *_cache,
+                         const std::string &_label)
+ : BaseCache::CacheMasterPort(_name, _cache, _reqQueue, _snoopRespQueue),
+      _reqQueue(*_cache, *this, _snoopRespQueue, _label),
+      _snoopRespQueue(*_cache, *this, _label), cache(_cache)
+{
+}
diff --git a/src/mem/cache/noncoherent_cache.hh b/src/mem/cache/noncoherent_cache.hh
new file mode 100644
index 0000000..10fb92d
--- /dev/null
+++ b/src/mem/cache/noncoherent_cache.hh
@@ -0,0 +1,338 @@
+/*
+ * Copyright (c) 2012-2016, 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
+ * 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
+ *          Steve Reinhardt
+ *          Ron Dreslinski
+ *          Andreas Hansson
+ */
+
+/**
+ * @file
+ * Describes a cache based on template policies.
+ */
+
+#ifndef __MEM_CACHE_NONCOHERENT_CACHE_HH__
+#define __MEM_CACHE_NONCOHERENT_CACHE_HH__
+
+#include "mem/cache/base.hh"
+#include "params/NoncoherentCache.hh"
+
+/**
+ * A non-coherent cache
+ */
+class NoncoherentCache : public BaseCache
+{
+  protected:
+
+    /**
+     * The CPU-side port extends the base cache slave port with access
+     * functions for functional, atomic and timing requests.
+     */
+    class CpuSidePort : public CacheSlavePort
+    {
+      private:
+
+        // a pointer to our specific cache implementation
+        NoncoherentCache *cache;
+
+      protected:
+
+        virtual bool tryTiming(PacketPtr pkt) override;
+
+        virtual bool recvTimingReq(PacketPtr pkt) override;
+
+        virtual Tick recvAtomic(PacketPtr pkt) override;
+
+        virtual void recvFunctional(PacketPtr pkt) override;
+
+        virtual AddrRangeList getAddrRanges() const override;
+
+      public:
+
+        CpuSidePort(const std::string &_name, NoncoherentCache *_cache,
+                    const std::string &_label);
+
+    };
+
+    /**
+     * Override the default behaviour of sendDeferredPacket to enable
+     * the memory-side cache port to also send requests based on the
+     * current MSHR status. This queue has a pointer to our specific
+     * cache implementation and is used by the MemSidePort.
+     */
+    class CacheReqPacketQueue : public ReqPacketQueue
+    {
+
+      protected:
+
+        NoncoherentCache &cache;
+        SnoopRespPacketQueue &snoopRespQueue;
+
+      public:
+
+        CacheReqPacketQueue(NoncoherentCache &cache, MasterPort &port,
+                            SnoopRespPacketQueue &snoop_resp_queue,
+                            const std::string &label) :
+            ReqPacketQueue(cache, port, label), cache(cache),
+            snoopRespQueue(snoop_resp_queue) { }
+
+        /**
+         * Override the normal sendDeferredPacket and do not only
+         * consider the transmit list (used for responses), but also
+         * requests.
+         */
+        virtual void sendDeferredPacket() override;
+
+        /**
+         * Check if there is a conflicting snoop response about to be
+         * send out, and if so simply stall any requests, and schedule
+         * a send event at the same time as the next snoop response is
+         * being sent out.
+         */
+        bool checkConflictingSnoop(Addr addr)
+        {
+            if (snoopRespQueue.hasAddr(addr)) {
+                DPRINTF(CachePort, "Waiting for snoop response to be "
+                        "sent\n");
+                Tick when = snoopRespQueue.deferredPacketReadyTime();
+                schedSendEvent(when);
+                return true;
+            }
+            return false;
+        }
+    };
+
+    /**
+     * The memory-side port extends the base cache master port with
+     * access functions for functional, atomic and timing snoops.
+     */
+    class MemSidePort : public CacheMasterPort
+    {
+      private:
+
+        /** The cache-specific queue. */
+        CacheReqPacketQueue _reqQueue;
+
+        SnoopRespPacketQueue _snoopRespQueue;
+
+        // a pointer to our specific cache implementation
+        NoncoherentCache *cache;
+
+      protected:
+
+        virtual bool recvTimingResp(PacketPtr pkt) override;
+
+      public:
+
+        MemSidePort(const std::string &_name, NoncoherentCache *_cache,
+                    const std::string &_label);
+    };
+
+    /**
+     * Upstream caches need this packet until true is returned, so
+     * hold it for deletion until a subsequent call
+     */
+    std::unique_ptr<Packet> pendingDelete;
+
+    /**
+     * Writebacks from the tempBlock, resulting on the response path
+     * in atomic mode, must happen after the call to recvAtomic has
+     * finished (for the right ordering of the packets). We therefore
+     * need to hold on to the packets, and have a method and an event
+     * to send them.
+     */
+    PacketPtr tempBlockWriteback;
+
+    /**
+     * Send the outstanding tempBlock writeback. To be called after
+     * recvAtomic finishes in cases where the block we filled is in
+     * fact the tempBlock, and now needs to be written back.
+     */
+    void writebackTempBlockAtomic() {
+        assert(tempBlockWriteback != nullptr);
+        PacketList writebacks{tempBlockWriteback};
+        doWritebacksAtomic(writebacks);
+        tempBlockWriteback = nullptr;
+    }
+
+    /**
+     * An event to writeback the tempBlock after recvAtomic
+     * finishes. To avoid other calls to recvAtomic getting in
+     * between, we create this event with a higher priority.
+     */
+    EventFunctionWrapper writebackTempBlockAtomicEvent;
+
+    /**
+     * Does all the processing necessary to perform the provided request.
+     * @param pkt The memory request to perform.
+     * @param blk The cache block to be updated.
+     * @param lat The latency of the access.
+     * @param writebacks List for any writebacks that need to be performed.
+     * @return Boolean indicating whether the request was satisfied.
+     */
+    bool access(PacketPtr pkt, CacheBlk *&blk,
+                Cycles &lat, PacketList &writebacks);
+
+    /**
+     * Find a block frame for new block at address addr targeting the
+     * given security space, assuming that the block is not currently
+     * in the cache.  Append writebacks if any to provided packet
+     * list.  Return free block frame.  May return nullptr if there are
+     * no replaceable blocks at the moment.
+     */
+    CacheBlk *allocateBlock(Addr addr, bool is_secure,
+                            PacketList &writebacks);
+
+    /**
+     * Populates a cache block and handles all outstanding requests for the
+     * satisfied fill request.
+     *
+     * @param pkt The memory request with the fill data.
+     * @param writebacks List for any writebacks that need to be performed.
+     *
+     * @return Pointer to the new cache block.
+     */
+    CacheBlk *handleFill(PacketPtr pkt, PacketList &writebacks);
+
+    /**
+     * Determine whether we should allocate on a fill or not.
+     *
+     * @param cmd Command of the incoming requesting packet
+     * @return Whether we should allocate on the fill
+     */
+    inline bool allocOnFill(MemCmd cmd) const override
+    {
+        return true;
+    }
+
+    /**
+     * Performs the access specified by the request.
+     * @param pkt The request to perform.
+     */
+    void recvTimingReq(PacketPtr pkt);
+
+    /**
+     * Insert writebacks into the write buffer
+     */
+    void doWritebacks(PacketList& writebacks, Tick forward_time);
+
+    /**
+     * Send writebacks down the memory hierarchy in atomic mode
+     */
+    void doWritebacksAtomic(PacketList& writebacks);
+
+    /**
+     * Handling the special case of uncacheable write responses to
+     * make recvTimingResp less cluttered.
+     */
+    void handleUncacheableWriteResp(PacketPtr pkt);
+
+    /**
+     * Handles a response (cache line fill/write ack) from the bus.
+     * @param pkt The response packet
+     */
+    void recvTimingResp(PacketPtr pkt);
+
+    /**
+     * Performs the access specified by the request.
+     * @param pkt The request to perform.
+     * @return The number of ticks required for the access.
+     */
+    Tick recvAtomic(PacketPtr pkt);
+
+    /**
+     * Performs the access specified by the request.
+     * @param pkt The request to perform.
+     */
+    void functionalAccess(PacketPtr pkt);
+
+    void satisfyRequest(PacketPtr pkt, CacheBlk *blk);
+
+    /**
+     * Create a writeback request for the given block.
+     * @param blk The block to writeback.
+     * @return The writeback request for the block.
+     */
+    PacketPtr writebackBlk(CacheBlk *blk);
+
+    /**
+     * Create an appropriate downstream bus request packet for the
+     * given parameters.
+     * @param cpu_pkt  The miss that needs to be satisfied.
+     * @return A new Packet containing the request, or nullptr if the
+     * current request in cpu_pkt should just be forwarded on.
+     */
+    PacketPtr createMissPacket(PacketPtr cpu_pkt) const;
+
+  public:
+    /** Instantiates a basic cache object. */
+    NoncoherentCache(const NoncoherentCacheParams *p);
+
+    /** Non-default destructor is needed to deallocate memory. */
+    virtual ~NoncoherentCache();
+
+    /**
+     * Register stats for this object.
+     */
+    void regStats() override;
+
+    /**
+     * Take an MSHR, turn it into a suitable downstream packet, and
+ * send it out. This construct allows a queue entry to choose a suitable
+     * approach based on its type.
+     *
+     * @param mshr The MSHR to turn into a packet and send
+     * @return True if the port is waiting for a retry
+     */
+    bool sendMSHRQueuePacket(MSHR* mshr) override;
+
+    /**
+     * Similar to sendMSHR, but for a write-queue entry
+     * instead. Create the packet, and send it, and if successful also
+     * mark the entry in service.
+     *
+     * @param wq_entry The write-queue entry to turn into a packet and send
+     * @return True if the port is waiting for a retry
+     */
+    bool sendWriteQueuePacket(WriteQueueEntry* wq_entry) override;
+};
+
+#endif // __MEM_CACHE_NONCOHERENTCACHE_HH__
diff --git a/src/mem/cache/queue.hh b/src/mem/cache/queue.hh
index f6941e6..f603ea8 100644
--- a/src/mem/cache/queue.hh
+++ b/src/mem/cache/queue.hh
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2012-2013, 2015-2016 ARM Limited
+ * Copyright (c) 2012-2013, 2015-2016, 2018 ARM Limited
  * All rights reserved.
  *
  * The license below extends only to copyright in the software and shall
@@ -148,12 +148,15 @@
     }

     /**
-     * Find the first WriteQueueEntry that matches the provided address.
+     * Find the first entry that matches the provided address.
+     *
      * @param blk_addr The block address to find.
      * @param is_secure True if the target memory space is secure.
+     * @param ignore_uncacheable Should uncacheables be ignored or not
      * @return Pointer to the matching WriteQueueEntry, null if not found.
      */
-    Entry* findMatch(Addr blk_addr, bool is_secure) const
+    Entry* findMatch(Addr blk_addr, bool is_secure,
+                     bool ignore_uncacheable = true) const
     {
         for (const auto& entry : allocatedList) {
             // we ignore any entries allocated for uncacheable
@@ -162,8 +165,8 @@
             // uncacheable entries, and we do not want normal
             // cacheable accesses being added to an WriteQueueEntry
             // serving an uncacheable access
-            if (!entry->isUncacheable() && entry->blkAddr == blk_addr &&
-                entry->isSecure == is_secure) {
+            if (!(ignore_uncacheable && entry->isUncacheable()) &&
+ entry->blkAddr == blk_addr && entry->isSecure == is_secure) {
                 return entry;
             }
         }
diff --git a/tests/configs/base_config.py b/tests/configs/base_config.py
index 2ec041c..6d77c75 100644
--- a/tests/configs/base_config.py
+++ b/tests/configs/base_config.py
@@ -1,4 +1,4 @@
-# Copyright (c) 2012-2013, 2017 ARM Limited
+# Copyright (c) 2012-2013, 2017-2018 ARM Limited
 # All rights reserved.
 #
 # The license below extends only to copyright in the software and shall
@@ -277,8 +277,19 @@
             # the physmem name to avoid bumping all the reference stats
             system.physmem = [self.mem_class(range = r)
                               for r in system.mem_ranges]
+            system.llc = [NoncoherentCache(addr_ranges = [r],
+                                           size = '8MB',
+                                           assoc = 16,
+                                           mshrs = 128,
+                                           tag_latency = 10,
+                                           data_latency = 10,
+                                           sequential_access = True,
+                                           response_latency = 20,
+                                           tgts_per_mshr = 8)
+                          for r in system.mem_ranges]
             for i in xrange(len(system.physmem)):
-                system.physmem[i].port = system.membus.master
+                system.physmem[i].port = system.llc[i].mem_side
+                system.llc[i].cpu_side = system.membus.master

             # create the iocache, which by default runs at the system clock
             system.iocache = IOCache(addr_ranges=system.mem_ranges)

--
To view, visit https://gem5-review.googlesource.com/8291
To unsubscribe, or for help writing mail filters, visit https://gem5-review.googlesource.com/settings

Gerrit-Project: public/gem5
Gerrit-Branch: master
Gerrit-MessageType: newchange
Gerrit-Change-Id: I41f7f9c2b8c7fa5ec23712a4446e8adb1c9a336a
Gerrit-Change-Number: 8291
Gerrit-PatchSet: 1
Gerrit-Owner: Nikos Nikoleris <nikos.nikole...@arm.com>
_______________________________________________
gem5-dev mailing list
gem5-dev@gem5.org
http://m5sim.org/mailman/listinfo/gem5-dev

Reply via email to