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 put together by Andreas
Hansson <andreas.hansson@arm.com>

Change-Id: I41f7f9c2b8c7fa5ec23712a4446e8adb1c9a336a
Reviewed-on: https://gem5-review.googlesource.com/8291
Maintainer: Nikos Nikoleris <nikos.nikoleris@arm.com>
Reviewed-by: Daniel Carvalho <odanrc@yahoo.com.br>
This commit is contained in:
Nikos Nikoleris
2018-02-05 09:45:20 +00:00
parent 7d990bd25b
commit 51056cec69
8 changed files with 556 additions and 17 deletions

View File

@@ -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
@@ -83,6 +83,8 @@ parser.add_option("-c", "--caches", type="string", default="2:2:1",
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 "
@@ -299,10 +301,19 @@ def make_cache_level(ncaches, prototypes, level, next_cache):
# 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:

View File

@@ -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
@@ -120,3 +120,13 @@ class BaseCache(MemObject):
class Cache(BaseCache):
type = 'Cache'
cxx_header = 'mem/cache/cache.hh'
class NoncoherentCache(BaseCache):
type = 'NoncoherentCache'
cxx_header = 'mem/cache/noncoherent_cache.hh'
# This is typically a last level cache and any clean
# writebacks would be unnecessary traffic to the main memory.
writeback_clean = False

View File

@@ -33,10 +33,11 @@ Import('*')
SimObject('Cache.py')
Source('base.cc')
Source('cache.cc')
Source('blk.cc')
Source('cache.cc')
Source('mshr.cc')
Source('mshr_queue.cc')
Source('noncoherent_cache.cc')
Source('write_queue.cc')
Source('write_queue_entry.cc')

View File

@@ -310,11 +310,6 @@ MSHR::allocateTarget(PacketPtr pkt, Tick whenReady, Counter _order,
// outstanding miss
assert(pkt->cmd != MemCmd::HardPFReq);
// uncacheable accesses always allocate a new MSHR, and cacheable
// accesses ignore any uncacheable MSHRs, thus we should never
// have targets addded if originally allocated uncacheable
assert(!_isUncacheable);
// if there's a request already in service for this MSHR, we will
// have to defer the new target until after the response if any of
// the following are true:

375
src/mem/cache/noncoherent_cache.cc vendored Normal file
View File

@@ -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);
}

133
src/mem/cache/noncoherent_cache.hh vendored Normal file
View File

@@ -0,0 +1,133 @@
/*
* Copyright (c) 2012-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
* Nikos Nikoleris
*/
/**
* @file
* Specifies a non-coherent cache. The non-coherent cache is expected
* to be located below the point of coherency. All valid blocks in the
* non-coherent cache can always be written to without any prior
* invalidations or snoops.
*/
#ifndef __MEM_CACHE_NONCOHERENT_CACHE_HH__
#define __MEM_CACHE_NONCOHERENT_CACHE_HH__
#include "base/logging.hh"
#include "base/types.hh"
#include "mem/cache/base.hh"
#include "mem/packet.hh"
class CacheBlk;
class MSHR;
struct NoncoherentCacheParams;
/**
* A non-coherent cache
*/
class NoncoherentCache : public BaseCache
{
protected:
bool access(PacketPtr pkt, CacheBlk *&blk, Cycles &lat,
PacketList &writebacks) override;
void handleTimingReqMiss(PacketPtr pkt, CacheBlk *blk,
Tick forward_time,
Tick request_time) override;
void recvTimingReq(PacketPtr pkt) override;
void doWritebacks(PacketList& writebacks,
Tick forward_time) override;
void doWritebacksAtomic(PacketList& writebacks) override;
void serviceMSHRTargets(MSHR *mshr, const PacketPtr pkt,
CacheBlk *blk, PacketList& writebacks) override;
void recvTimingResp(PacketPtr pkt) override;
void recvTimingSnoopReq(PacketPtr pkt) override {
panic("Unexpected timing snoop request %s", pkt->print());
}
void recvTimingSnoopResp(PacketPtr pkt) override {
panic("Unexpected timing snoop response %s", pkt->print());
}
Cycles handleAtomicReqMiss(PacketPtr pkt, CacheBlk *blk,
PacketList &writebacks) override;
Tick recvAtomic(PacketPtr pkt) override;
Tick recvAtomicSnoop(PacketPtr pkt) override {
panic("Unexpected atomic snoop request %s", pkt->print());
}
void functionalAccess(PacketPtr pkt, bool from_cpu_side) override;
void satisfyRequest(PacketPtr pkt, CacheBlk *blk,
bool deferred_response = false,
bool pending_downgrade = false) override;
/*
* Creates a new packet with the request to be send to the memory
* below. The noncoherent cache is below the point of coherence
* and therefore all fills bring in writable, therefore the
* needs_writeble parameter is ignored.
*/
PacketPtr createMissPacket(PacketPtr cpu_pkt, CacheBlk *blk,
bool needs_writable) const override;
M5_NODISCARD PacketPtr evictBlock(CacheBlk *blk) override;
void evictBlock(CacheBlk *blk, PacketList &writebacks) override;
public:
NoncoherentCache(const NoncoherentCacheParams *p);
};
#endif // __MEM_CACHE_NONCOHERENTCACHE_HH__

View File

@@ -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 @@ class Queue : public Drainable
}
/**
* 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 @@ class Queue : public Drainable
// 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;
}
}

View File

@@ -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
@@ -278,8 +278,19 @@ class BaseFSSystem(BaseSystem):
# 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 = '4kB',
assoc = 2,
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)