mem: Make MemCtrl a ClockedObject
[gem5.git] / configs / example / memtest.py
index 6c1e657e461af529f66ffb36e6909d6beadbded4..ef536c66cfb7abd04664af382c50d5c911c2485f 100644 (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
 # 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: Ron Dreslinski
-#          Andreas Hansson
+
+from __future__ import print_function
+from __future__ import absolute_import
 
 import optparse
+import random
 import sys
 
 import m5
 from m5.objects import *
 
+# This example script stress tests the memory system by creating false
+# sharing in a tree topology. At the bottom of the tree is a shared
+# memory, and then at each level a number of testers are attached,
+# along with a number of caches that them selves fan out to subtrees
+# of testers and caches. Thus, it is possible to create a system with
+# arbitrarily deep cache hierarchies, sharing or no sharing of caches,
+# and testers not only at the L1s, but also at the L2s, L3s etc.
+
 parser = optparse.OptionParser()
 
 parser.add_option("-a", "--atomic", action="store_true",
@@ -57,14 +66,6 @@ parser.add_option("-m", "--maxtick", type="int", default=m5.MaxTick,
                   metavar="T",
                   help="Stop after T ticks")
 
-# This example script stress tests the memory system by creating false
-# sharing in a tree topology. At the bottom of the tree is a shared
-# memory, and then at each level a number of testers are attached,
-# along with a number of caches that them selves fan out to subtrees
-# of testers and caches. Thus, it is possible to create a system with
-# arbitrarily deep cache hierarchies, sharing or no sharing of caches,
-# and testers not only at the L1s, but also at the L2s, L3s etc.
-#
 # The tree specification consists of two colon-separated lists of one
 # or more integers, one for the caches, and one for the testers. The
 # first integer is the number of caches/testers closest to main
@@ -80,20 +81,23 @@ 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 "
                   "[default: %default]")
-parser.add_option("-f", "--functional", type="int", default=0,
+parser.add_option("-f", "--functional", type="int", default=10,
                   metavar="PCT",
                   help="Target percentage of functional accesses "
                   "[default: %default]")
-parser.add_option("-u", "--uncacheable", type="int", default=0,
+parser.add_option("-u", "--uncacheable", type="int", default=10,
                   metavar="PCT",
                   help="Target percentage of uncacheable accesses "
                   "[default: %default]")
-
-parser.add_option("--progress", type="int", default=10000,
+parser.add_option("-r", "--random", action="store_true",
+                  help="Generate a random tree topology")
+parser.add_option("--progress", type="int", default=100000,
                   metavar="NLOADS",
                   help="Progress message interval "
                   "[default: %default]")
@@ -105,63 +109,78 @@ parser.add_option("--sys-clock", action="store", type="string",
 (options, args) = parser.parse_args()
 
 if args:
-     print "Error: script doesn't take any positional arguments"
+     print("Error: script doesn't take any positional arguments")
      sys.exit(1)
 
+# Get the total number of testers
+def numtesters(cachespec, testerspec):
+     # Determine the tester multiplier for each level as the
+     # elements are per subsystem and it fans out
+     multiplier = [1]
+     for c in cachespec:
+          multiplier.append(multiplier[-1] * c)
+
+     total = 0
+     for t, m in zip(testerspec, multiplier):
+          total += t * m
+
+     return total
+
 block_size = 64
 
 # Start by parsing the command line options and do some basic sanity
 # checking
-try:
-     cachespec = [int(x) for x in options.caches.split(':')]
-     testerspec = [int(x) for x in options.testers.split(':')]
-except:
-     print "Error: Unable to parse caches or testers option"
-     sys.exit(1)
-
-if len(cachespec) < 1:
-     print "Error: Must have at least one level of caches"
-     sys.exit(1)
-
-if len(cachespec) != len(testerspec) - 1:
-     print "Error: Testers must have one element more than caches"
-     sys.exit(1)
+if options.random:
+     # Generate a tree with a valid number of testers
+     while True:
+          tree_depth = random.randint(1, 4)
+          cachespec = [random.randint(1, 3) for i in range(tree_depth)]
+          testerspec = [random.randint(1, 3) for i in range(tree_depth + 1)]
+          if numtesters(cachespec, testerspec) < block_size:
+               break
+
+     print("Generated random tree -c", ':'.join(map(str, cachespec)),
+         "-t", ':'.join(map(str, testerspec)))
+else:
+     try:
+          cachespec = [int(x) for x in options.caches.split(':')]
+          testerspec = [int(x) for x in options.testers.split(':')]
+     except:
+          print("Error: Unable to parse caches or testers option")
+          sys.exit(1)
 
-if testerspec[-1] == 0:
-     print "Error: Must have testers at the uppermost level"
-     sys.exit(1)
+     if len(cachespec) < 1:
+          print("Error: Must have at least one level of caches")
+          sys.exit(1)
 
-for t in testerspec:
-     if t < 0:
-          print "Error: Cannot have a negative number of testers"
+     if len(cachespec) != len(testerspec) - 1:
+          print("Error: Testers must have one element more than caches")
           sys.exit(1)
 
-for c in cachespec:
-     if c < 1:
-          print "Error: Must have 1 or more caches at each level"
+     if testerspec[-1] == 0:
+          print("Error: Must have testers at the uppermost level")
           sys.exit(1)
 
-# Determine the tester multiplier for each level as the string
-# elements are per subsystem and it fans out
-multiplier = [1]
-for c in cachespec:
-     if c < 1:
-          print "Error: Must have at least one cache per level"
-     multiplier.append(multiplier[-1] * c)
-
-numtesters = 0
-for t, m in zip(testerspec, multiplier):
-     numtesters += t * m
-
-if numtesters > block_size:
-     print "Error: Number of testers limited to %s because of false sharing" \
-           % (block_size)
-     sys.exit(1)
+     for t in testerspec:
+          if t < 0:
+               print("Error: Cannot have a negative number of testers")
+               sys.exit(1)
+
+     for c in cachespec:
+          if c < 1:
+               print("Error: Must have 1 or more caches at each level")
+               sys.exit(1)
+
+     if numtesters(cachespec, testerspec) > block_size:
+          print("Error: Limited to %s testers because of false sharing"
+              % (block_size))
+          sys.exit(1)
 
 # Define a prototype L1 cache that we scale for all successive levels
-proto_l1 = BaseCache(size = '32kB', assoc = 4,
-                     hit_latency = 1, response_latency = 1,
-                     tgts_per_mshr = 8, is_top_level = True)
+proto_l1 = Cache(size = '32kB', assoc = 4,
+                 tag_latency = 1, data_latency = 1, response_latency = 1,
+                 tgts_per_mshr = 8, clusivity = 'mostly_incl',
+                 writeback_clean = True)
 
 if options.blocking:
      proto_l1.mshrs = 1
@@ -177,11 +196,21 @@ for scale in cachespec[:-1]:
      prev = cache_proto[0]
      next = prev()
      next.size = prev.size * scale
-     next.hit_latency = prev.hit_latency * 10
+     next.tag_latency = prev.tag_latency * 10
+     next.data_latency = prev.data_latency * 10
      next.response_latency = prev.response_latency * 10
      next.assoc = prev.assoc * scale
      next.mshrs = prev.mshrs * scale
-     next.is_top_level = False
+
+     # Swap the inclusivity/exclusivity at each level. L2 is mostly
+     # exclusive with respect to L1, L3 mostly inclusive, L4 mostly
+     # exclusive etc.
+     next.writeback_clean = not prev.writeback_clean
+     if (prev.clusivity.value == 'mostly_incl'):
+          next.clusivity = 'mostly_excl'
+     else:
+          next.clusivity = 'mostly_incl'
+
      cache_proto.insert(0, next)
 
 # Make a prototype for the tester to be used throughout
@@ -223,17 +252,17 @@ def make_cache_level(ncaches, prototypes, level, next_cache):
      # and also make the interval of packet injection longer for the
      # testers closer to the memory (larger level) to prevent them
      # hogging all the bandwidth
-     limit = (len(cachespec) - level + 1) * 10000000
+     limit = (len(cachespec) - level + 1) * 100000000
      testers = [proto_tester(interval = 10 * (level * level + 1),
                              progress_check = limit) \
-                     for i in xrange(ntesters)]
+                     for i in range(ntesters)]
      if ntesters:
           subsys.tester = testers
 
      if level != 0:
           # Create a crossbar and add it to the subsystem, note that
           # we do this even with a single element on this level
-          xbar = CoherentXBar(width = 32)
+          xbar = L2XBar()
           subsys.xbar = xbar
           if next_cache:
                xbar.master = next_cache.cpu_side
@@ -241,8 +270,8 @@ def make_cache_level(ncaches, prototypes, level, next_cache):
           # Create and connect the caches, both the ones fanning out
           # to create the tree, and the ones used to connect testers
           # on this level
-          tree_caches = [prototypes[0]() for i in xrange(ncaches[0])]
-          tester_caches = [proto_l1() for i in xrange(ntesters)]
+          tree_caches = [prototypes[0]() for i in range(ncaches[0])]
+          tester_caches = [proto_l1() for i in range(ntesters)]
 
           subsys.cache = tester_caches + tree_caches
           for cache in tree_caches:
@@ -253,12 +282,12 @@ def make_cache_level(ncaches, prototypes, level, next_cache):
                cache.mem_side = xbar.slave
      else:
           if not next_cache:
-               print "Error: No next-level cache at top level"
+               print("Error: No next-level cache at top level")
                sys.exit(1)
 
           if ntesters > 1:
                # Create a crossbar and add it to the subsystem
-               xbar = CoherentXBar(width = 32)
+               xbar = L2XBar()
                subsys.xbar = xbar
                xbar.master = next_cache.cpu_side
                for tester in testers:
@@ -270,9 +299,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:
@@ -290,4 +329,4 @@ m5.instantiate()
 # Simulate until program terminates
 exit_event = m5.simulate(options.maxtick)
 
-print 'Exiting @ tick', m5.curTick(), 'because', exit_event.getCause()
+print('Exiting @ tick', m5.curTick(), 'because', exit_event.getCause())