summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndreas Hansson <andreas.hansson@arm.com>2015-07-03 10:14:39 -0400
committerAndreas Hansson <andreas.hansson@arm.com>2015-07-03 10:14:39 -0400
commit893533a1264bb369b47f74493adf30ce22829f34 (patch)
tree07c750519f5ac1b972be47a0ca6f68ee517d9f07
parenta262908acc0a641700a03fcea89c48133f0467cd (diff)
downloadgem5-893533a1264bb369b47f74493adf30ce22829f34.tar.xz
mem: Allow read-only caches and check compliance
This patch adds a parameter to the BaseCache to enable a read-only cache, for example for the instruction cache, or table-walker cache (not for x86). A number of checks are put in place in the code to ensure a read-only cache does not end up with dirty data. A follow-on patch adds suitable read requests to allow a read-only cache to explicitly ask for clean data.
-rw-r--r--configs/common/CacheConfig.py2
-rw-r--r--configs/common/Caches.py11
-rw-r--r--configs/common/O3_ARM_v7a.py2
-rw-r--r--src/mem/cache/BaseCache.py1
-rw-r--r--src/mem/cache/base.cc1
-rw-r--r--src/mem/cache/base.hh10
-rw-r--r--src/mem/cache/cache_impl.hh21
-rw-r--r--tests/configs/base_config.py12
-rw-r--r--tests/configs/x86_generic.py8
9 files changed, 56 insertions, 12 deletions
diff --git a/configs/common/CacheConfig.py b/configs/common/CacheConfig.py
index 899090af5..d54df7490 100644
--- a/configs/common/CacheConfig.py
+++ b/configs/common/CacheConfig.py
@@ -64,7 +64,7 @@ def config_cache(options, system):
O3_ARM_v7a_DCache, O3_ARM_v7a_ICache, O3_ARM_v7aL2
else:
dcache_class, icache_class, l2_cache_class = \
- L1Cache, L1Cache, L2Cache
+ L1_DCache, L1_ICache, L2Cache
# Set the cache line size of the system
system.cache_line_size = options.cacheline_size
diff --git a/configs/common/Caches.py b/configs/common/Caches.py
index 6687a967c..2bdffc6c7 100644
--- a/configs/common/Caches.py
+++ b/configs/common/Caches.py
@@ -54,6 +54,12 @@ class L1Cache(BaseCache):
tgts_per_mshr = 20
is_top_level = True
+class L1_ICache(L1Cache):
+ is_read_only = True
+
+class L1_DCache(L1Cache):
+ pass
+
class L2Cache(BaseCache):
assoc = 8
hit_latency = 20
@@ -81,3 +87,8 @@ class PageTableWalkerCache(BaseCache):
tgts_per_mshr = 12
forward_snoops = False
is_top_level = True
+ # the x86 table walker actually writes to the table-walker cache
+ if buildEnv['TARGET_ISA'] == 'x86':
+ is_read_only = False
+ else:
+ is_read_only = True
diff --git a/configs/common/O3_ARM_v7a.py b/configs/common/O3_ARM_v7a.py
index c291525ea..b4b66df9c 100644
--- a/configs/common/O3_ARM_v7a.py
+++ b/configs/common/O3_ARM_v7a.py
@@ -151,6 +151,7 @@ class O3_ARM_v7a_ICache(BaseCache):
assoc = 2
is_top_level = True
forward_snoops = False
+ is_read_only = True
# Data Cache
class O3_ARM_v7a_DCache(BaseCache):
@@ -175,6 +176,7 @@ class O3_ARM_v7aWalkCache(BaseCache):
write_buffers = 16
is_top_level = True
forward_snoops = False
+ is_read_only = True
# L2 Cache
class O3_ARM_v7aL2(BaseCache):
diff --git a/src/mem/cache/BaseCache.py b/src/mem/cache/BaseCache.py
index fdb41bf75..4d6766456 100644
--- a/src/mem/cache/BaseCache.py
+++ b/src/mem/cache/BaseCache.py
@@ -65,6 +65,7 @@ class BaseCache(MemObject):
forward_snoops = Param.Bool(True,
"Forward snoops from mem side to cpu side")
is_top_level = Param.Bool(False, "Is this cache at the top level (e.g. L1)")
+ is_read_only = Param.Bool(False, "Is this cache read only (e.g. inst)")
prefetcher = Param.BasePrefetcher(NULL,"Prefetcher attached to cache")
prefetch_on_access = Param.Bool(False,
diff --git a/src/mem/cache/base.cc b/src/mem/cache/base.cc
index c2068496c..af504d9bc 100644
--- a/src/mem/cache/base.cc
+++ b/src/mem/cache/base.cc
@@ -79,6 +79,7 @@ BaseCache::BaseCache(const Params *p)
numTarget(p->tgts_per_mshr),
forwardSnoops(p->forward_snoops),
isTopLevel(p->is_top_level),
+ isReadOnly(p->is_read_only),
blocked(0),
order(0),
noTargetMSHR(NULL),
diff --git a/src/mem/cache/base.hh b/src/mem/cache/base.hh
index aaf0ea691..d2cb11f33 100644
--- a/src/mem/cache/base.hh
+++ b/src/mem/cache/base.hh
@@ -310,6 +310,14 @@ class BaseCache : public MemObject
const bool isTopLevel;
/**
+ * Is this cache read only, for example the instruction cache, or
+ * table-walker cache. A cache that is read only should never see
+ * any writes, and should never get any dirty data (and hence
+ * never have to do any writebacks).
+ */
+ const bool isReadOnly;
+
+ /**
* Bit vector of the blocking reasons for the access path.
* @sa #BlockedCause
*/
@@ -516,6 +524,8 @@ class BaseCache : public MemObject
MSHR *allocateWriteBuffer(PacketPtr pkt, Tick time, bool requestBus)
{
+ // should only see clean evictions in a read-only cache
+ assert(!isReadOnly || pkt->cmd == MemCmd::CleanEvict);
assert(pkt->isWrite() && !pkt->isRead());
return allocateBufferInternal(&writeBuffer,
blockAlign(pkt->getAddr()), blkSize,
diff --git a/src/mem/cache/cache_impl.hh b/src/mem/cache/cache_impl.hh
index 117596d9b..5a9205894 100644
--- a/src/mem/cache/cache_impl.hh
+++ b/src/mem/cache/cache_impl.hh
@@ -299,8 +299,13 @@ Cache::access(PacketPtr pkt, CacheBlk *&blk, Cycles &lat,
// sanity check
assert(pkt->isRequest());
+ chatty_assert(!(isReadOnly && pkt->isWrite()),
+ "Should never see a write in a read-only cache %s\n",
+ name());
+
DPRINTF(Cache, "%s for %s addr %#llx size %d\n", __func__,
pkt->cmdString(), pkt->getAddr(), pkt->getSize());
+
if (pkt->req->isUncacheable()) {
DPRINTF(Cache, "%s%s addr %#llx uncacheable\n", pkt->cmdString(),
pkt->req->isInstFetch() ? " (ifetch)" : "",
@@ -1419,6 +1424,7 @@ Cache::recvTimingResp(PacketPtr pkt)
PacketPtr
Cache::writebackBlk(CacheBlk *blk)
{
+ chatty_assert(!isReadOnly, "Writeback from read-only cache");
assert(blk && blk->isValid() && blk->isDirty());
writebacks[Request::wbMasterId]++;
@@ -1627,7 +1633,12 @@ Cache::handleFill(PacketPtr pkt, CacheBlk *blk, PacketList &writebacks)
blk->status |= BlkValid | BlkReadable;
if (!pkt->sharedAsserted()) {
+ // we could get non-shared responses from memory (rather than
+ // a cache) even in a read-only cache, note that we set this
+ // bit even for a read-only cache as we use it to represent
+ // the exclusive state
blk->status |= BlkWritable;
+
// If we got this via cache-to-cache transfer (i.e., from a
// cache that was an owner) and took away that owner's copy,
// then we need to write it back. Normally this happens
@@ -1635,8 +1646,12 @@ Cache::handleFill(PacketPtr pkt, CacheBlk *blk, PacketList &writebacks)
// there are cases (such as failed store conditionals or
// compare-and-swaps) where we'll demand an exclusive copy but
// end up not writing it.
- if (pkt->memInhibitAsserted())
+ if (pkt->memInhibitAsserted()) {
blk->status |= BlkDirty;
+
+ chatty_assert(!isReadOnly, "Should never see dirty snoop response "
+ "in read-only cache %s\n", name());
+ }
}
DPRINTF(Cache, "Block addr %#llx (%s) moving from state %x to %s\n",
@@ -1785,6 +1800,10 @@ Cache::handleSnoop(PacketPtr pkt, CacheBlk *blk, bool is_timing,
pkt->getAddr(), pkt->getSize(), blk->print());
}
+ chatty_assert(!(isReadOnly && blk->isDirty()),
+ "Should never have a dirty block in a read-only cache %s\n",
+ name());
+
// we may end up modifying both the block state and the packet (if
// we respond in atomic mode), so just figure out what to do now
// and then do it later. If we find dirty data while snooping for a
diff --git a/tests/configs/base_config.py b/tests/configs/base_config.py
index c440d48d9..a3e1e0271 100644
--- a/tests/configs/base_config.py
+++ b/tests/configs/base_config.py
@@ -92,8 +92,8 @@ class BaseSystem(object):
Arguments:
cpu -- CPU instance to work on.
"""
- cpu.addPrivateSplitL1Caches(L1Cache(size='32kB', assoc=1),
- L1Cache(size='32kB', assoc=4))
+ cpu.addPrivateSplitL1Caches(L1_ICache(size='32kB', assoc=1),
+ L1_DCache(size='32kB', assoc=4))
def create_caches_shared(self, system):
"""Add shared caches to a system.
@@ -212,8 +212,8 @@ class BaseSESystemUniprocessor(BaseSESystem):
# The atomic SE configurations do not use caches
if self.mem_mode == "timing":
# @todo We might want to revisit these rather enthusiastic L1 sizes
- cpu.addTwoLevelCacheHierarchy(L1Cache(size='128kB'),
- L1Cache(size='256kB'),
+ cpu.addTwoLevelCacheHierarchy(L1_ICache(size='128kB'),
+ L1_DCache(size='256kB'),
L2Cache(size='2MB'))
def create_caches_shared(self, system):
@@ -256,8 +256,8 @@ class BaseFSSystemUniprocessor(BaseFSSystem):
BaseFSSystem.__init__(self, **kwargs)
def create_caches_private(self, cpu):
- cpu.addTwoLevelCacheHierarchy(L1Cache(size='32kB', assoc=1),
- L1Cache(size='32kB', assoc=4),
+ cpu.addTwoLevelCacheHierarchy(L1_ICache(size='32kB', assoc=1),
+ L1_DCache(size='32kB', assoc=4),
L2Cache(size='4MB', assoc=8))
def create_caches_shared(self, system):
diff --git a/tests/configs/x86_generic.py b/tests/configs/x86_generic.py
index 5dc8702ba..ad3ea31bf 100644
--- a/tests/configs/x86_generic.py
+++ b/tests/configs/x86_generic.py
@@ -81,8 +81,8 @@ class LinuxX86FSSystem(LinuxX86SystemBuilder,
LinuxX86SystemBuilder.__init__(self)
def create_caches_private(self, cpu):
- cpu.addPrivateSplitL1Caches(L1Cache(size='32kB', assoc=1),
- L1Cache(size='32kB', assoc=4),
+ cpu.addPrivateSplitL1Caches(L1_ICache(size='32kB', assoc=1),
+ L1_DCache(size='32kB', assoc=4),
PageTableWalkerCache(),
PageTableWalkerCache())
@@ -100,8 +100,8 @@ class LinuxX86FSSystemUniprocessor(LinuxX86SystemBuilder,
LinuxX86SystemBuilder.__init__(self)
def create_caches_private(self, cpu):
- cpu.addTwoLevelCacheHierarchy(L1Cache(size='32kB', assoc=1),
- L1Cache(size='32kB', assoc=4),
+ cpu.addTwoLevelCacheHierarchy(L1_ICache(size='32kB', assoc=1),
+ L1_DCache(size='32kB', assoc=4),
L2Cache(size='4MB', assoc=8),
PageTableWalkerCache(),
PageTableWalkerCache())