Configs: Fix handling of maxtick and take_checkpoints
[gem5.git] / configs / common / Simulation.py
index 905e435d38acdc0479e3847cba91a91109835c99..cecf030adcb521aa6445ef0106b1bf2d32970194 100644 (file)
@@ -1,4 +1,17 @@
+# Copyright (c) 2012 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) 2006-2008 The Regents of The University of Michigan
+# Copyright (c) 2010 Advanced Micro Devices, Inc.
 # All rights reserved.
 #
 # Redistribution and use in source and binary forms, with or without
 #
 # Authors: Lisa Hsu
 
+import sys
 from os import getcwd
 from os.path import join as joinpath
 
+import CpuConfig
+
 import m5
 from m5.defines import buildEnv
 from m5.objects import *
@@ -36,37 +52,206 @@ from m5.util import *
 
 addToPath('../common')
 
+def getCPUClass(cpu_type):
+    """Returns the required cpu class and the mode of operation."""
+    cls = CpuConfig.get(cpu_type)
+    return cls, cls.memory_mode()
+
 def setCPUClass(options):
+    """Returns two cpu classes and the initial mode of operation.
 
-    atomic = False
-    if options.timing:
-        class TmpClass(TimingSimpleCPU): pass
-    elif options.detailed:
-        if not options.caches:
-            print "O3 CPU must be used with caches"
-            sys.exit(1)
-        class TmpClass(DerivO3CPU): pass
-    elif options.inorder:
-        if not options.caches:
-            print "InOrder CPU must be used with caches"
-            sys.exit(1)
-        class TmpClass(InOrderCPU): pass
-    else:
-        class TmpClass(AtomicSimpleCPU): pass
-        atomic = True
+       Restoring from a checkpoint or fast forwarding through a benchmark
+       can be done using one type of cpu, and then the actual
+       simulation can be carried out using another type. This function
+       returns these two types of cpus and the initial mode of operation
+       depending on the options provided.
+    """
 
+    TmpClass, test_mem_mode = getCPUClass(options.cpu_type)
     CPUClass = None
-    test_mem_mode = 'atomic'
+    if TmpClass.require_caches() and \
+            not options.caches and not options.ruby:
+        fatal("%s must be used with caches" % options.cpu_type)
 
-    if not atomic:
-        if options.checkpoint_restore != None or options.fast_forward:
+    if options.checkpoint_restore != None:
+        if options.restore_with_cpu != options.cpu_type:
             CPUClass = TmpClass
-            class TmpClass(AtomicSimpleCPU): pass
-        else:
-            test_mem_mode = 'timing'
+            TmpClass, test_mem_mode = getCPUClass(options.restore_with_cpu)
+    elif options.fast_forward:
+        CPUClass = TmpClass
+        TmpClass = AtomicSimpleCPU
+        test_mem_mode = 'atomic'
 
     return (TmpClass, test_mem_mode, CPUClass)
 
+def setWorkCountOptions(system, options):
+    if options.work_item_id != None:
+        system.work_item_id = options.work_item_id
+    if options.work_begin_cpu_id_exit != None:
+        system.work_begin_cpu_id_exit = options.work_begin_cpu_id_exit
+    if options.work_end_exit_count != None:
+        system.work_end_exit_count = options.work_end_exit_count
+    if options.work_end_checkpoint_count != None:
+        system.work_end_ckpt_count = options.work_end_checkpoint_count
+    if options.work_begin_exit_count != None:
+        system.work_begin_exit_count = options.work_begin_exit_count
+    if options.work_begin_checkpoint_count != None:
+        system.work_begin_ckpt_count = options.work_begin_checkpoint_count
+    if options.work_cpus_checkpoint_count != None:
+        system.work_cpus_ckpt_count = options.work_cpus_checkpoint_count
+
+def findCptDir(options, maxtick, cptdir, testsys):
+    """Figures out the directory from which the checkpointed state is read.
+
+    There are two different ways in which the directories holding checkpoints
+    can be named --
+    1. cpt.<benchmark name>.<instruction count when the checkpoint was taken>
+    2. cpt.<some number, usually the tick value when the checkpoint was taken>
+
+    This function parses through the options to figure out which one of the
+    above should be used for selecting the checkpoint, and then figures out
+    the appropriate directory.
+
+    It also sets the value of the maximum tick value till which the simulation
+    will run.
+    """
+
+    from os.path import isdir, exists
+    from os import listdir
+    import re
+
+    if not isdir(cptdir):
+        fatal("checkpoint dir %s does not exist!", cptdir)
+
+    if options.at_instruction or options.simpoint:
+        inst = options.checkpoint_restore
+        if options.simpoint:
+            # assume workload 0 has the simpoint
+            if testsys.cpu[0].workload[0].simpoint == 0:
+                fatal('Unable to find simpoint')
+            inst += int(testsys.cpu[0].workload[0].simpoint)
+
+        checkpoint_dir = joinpath(cptdir, "cpt.%s.%s" % (options.bench, inst))
+        if not exists(checkpoint_dir):
+            fatal("Unable to find checkpoint directory %s", checkpoint_dir)
+    else:
+        dirs = listdir(cptdir)
+        expr = re.compile('cpt\.([0-9]*)')
+        cpts = []
+        for dir in dirs:
+            match = expr.match(dir)
+            if match:
+                cpts.append(match.group(1))
+
+        cpts.sort(lambda a,b: cmp(long(a), long(b)))
+
+        cpt_num = options.checkpoint_restore
+        if cpt_num > len(cpts):
+            fatal('Checkpoint %d not found', cpt_num)
+
+        maxtick = maxtick - int(cpts[cpt_num - 1])
+        checkpoint_dir = joinpath(cptdir, "cpt.%s" % cpts[cpt_num - 1])
+
+    return maxtick, checkpoint_dir
+
+def scriptCheckpoints(options, maxtick, cptdir):
+    if options.at_instruction or options.simpoint:
+        checkpoint_inst = int(options.take_checkpoints)
+
+        # maintain correct offset if we restored from some instruction
+        if options.checkpoint_restore != None:
+            checkpoint_inst += options.checkpoint_restore
+
+        print "Creating checkpoint at inst:%d" % (checkpoint_inst)
+        exit_event = m5.simulate()
+        exit_cause = exit_event.getCause()
+        print "exit cause = %s" % exit_cause
+
+        # skip checkpoint instructions should they exist
+        while exit_cause == "checkpoint":
+            exit_event = m5.simulate()
+            exit_cause = exit_event.getCause()
+
+        if exit_cause == "a thread reached the max instruction count":
+            m5.checkpoint(joinpath(cptdir, "cpt.%s.%d" % \
+                    (options.bench, checkpoint_inst)))
+            print "Checkpoint written."
+
+    else:
+        when, period = options.take_checkpoints.split(",", 1)
+        when = int(when)
+        period = int(period)
+        num_checkpoints = 0
+
+        exit_event = m5.simulate(when - m5.curTick())
+        exit_cause = exit_event.getCause()
+        while exit_cause == "checkpoint":
+            exit_event = m5.simulate(when - m5.curTick())
+            exit_cause = exit_event.getCause()
+
+        if exit_cause == "simulate() limit reached":
+            m5.checkpoint(joinpath(cptdir, "cpt.%d"))
+            num_checkpoints += 1
+
+        sim_ticks = when
+        max_checkpoints = options.max_checkpoints
+
+        while num_checkpoints < max_checkpoints and \
+                exit_cause == "simulate() limit reached":
+            if (sim_ticks + period) > maxtick:
+                exit_event = m5.simulate(maxtick - sim_ticks)
+                exit_cause = exit_event.getCause()
+                break
+            else:
+                exit_event = m5.simulate(period)
+                exit_cause = exit_event.getCause()
+                sim_ticks += period
+                while exit_event.getCause() == "checkpoint":
+                    exit_event = m5.simulate(sim_ticks - m5.curTick())
+                if exit_event.getCause() == "simulate() limit reached":
+                    m5.checkpoint(joinpath(cptdir, "cpt.%d"))
+                    num_checkpoints += 1
+
+    return exit_event
+
+def benchCheckpoints(options, maxtick, cptdir):
+    exit_event = m5.simulate(maxtick - m5.curTick())
+    exit_cause = exit_event.getCause()
+
+    num_checkpoints = 0
+    max_checkpoints = options.max_checkpoints
+
+    while exit_cause == "checkpoint":
+        m5.checkpoint(joinpath(cptdir, "cpt.%d"))
+        num_checkpoints += 1
+        if num_checkpoints == max_checkpoints:
+            exit_cause = "maximum %d checkpoints dropped" % max_checkpoints
+            break
+
+        exit_event = m5.simulate(maxtick - m5.curTick())
+        exit_cause = exit_event.getCause()
+
+    return exit_event
+
+def repeatSwitch(testsys, repeat_switch_cpu_list, maxtick, switch_freq):
+    print "starting switch loop"
+    while True:
+        exit_event = m5.simulate(switch_freq)
+        exit_cause = exit_event.getCause()
+
+        if exit_cause != "simulate() limit reached":
+            return exit_event
+
+        m5.switchCpus(testsys, repeat_switch_cpu_list)
+
+        tmp_cpu_list = []
+        for old_cpu, new_cpu in repeat_switch_cpu_list:
+            tmp_cpu_list.append((new_cpu, old_cpu))
+        repeat_switch_cpu_list = tmp_cpu_list
+
+        if (maxtick - m5.curTick()) <= switch_freq:
+            exit_event = m5.simulate(maxtick - m5.curTick())
+            return exit_event
 
 def run(options, root, testsys, cpu_class):
     if options.maxtick:
@@ -91,50 +276,89 @@ def run(options, root, testsys, cpu_class):
     if options.standard_switch and not options.caches:
         fatal("Must specify --caches when using --standard-switch")
 
+    if options.standard_switch and options.repeat_switch:
+        fatal("Can't specify both --standard-switch and --repeat-switch")
+
+    if options.repeat_switch and options.take_checkpoints:
+        fatal("Can't specify both --repeat-switch and --take-checkpoints")
+
     np = options.num_cpus
-    max_checkpoints = options.max_checkpoints
     switch_cpus = None
 
-    if options.prog_intvl:
+    if options.prog_interval:
         for i in xrange(np):
-            testsys.cpu[i].progress_interval = options.prog_intvl
+            testsys.cpu[i].progress_interval = options.prog_interval
 
     if options.maxinsts:
         for i in xrange(np):
             testsys.cpu[i].max_insts_any_thread = options.maxinsts
 
     if cpu_class:
-        switch_cpus = [cpu_class(defer_registration=True, cpu_id=(np+i))
+        switch_cpus = [cpu_class(switched_out=True, cpu_id=(i))
                        for i in xrange(np)]
 
         for i in xrange(np):
             if options.fast_forward:
                 testsys.cpu[i].max_insts_any_thread = int(options.fast_forward)
             switch_cpus[i].system =  testsys
-            if not buildEnv['FULL_SYSTEM']:
-                switch_cpus[i].workload = testsys.cpu[i].workload
-            switch_cpus[i].clock = testsys.cpu[0].clock
+            switch_cpus[i].workload = testsys.cpu[i].workload
+            switch_cpus[i].clock = testsys.cpu[i].clock
             # simulation period
-            if options.max_inst:
-                switch_cpus[i].max_insts_any_thread = options.max_inst
+            if options.maxinsts:
+                switch_cpus[i].max_insts_any_thread = options.maxinsts
+            # Add checker cpu if selected
+            if options.checker:
+                switch_cpus[i].addCheckerCpu()
 
         testsys.switch_cpus = switch_cpus
         switch_cpu_list = [(testsys.cpu[i], switch_cpus[i]) for i in xrange(np)]
 
+    if options.repeat_switch:
+        switch_class = getCPUClass(options.cpu_type)[0]
+        if switch_class.require_caches() and \
+                not options.caches:
+            print "%s: Must be used with caches" % str(switch_class)
+            sys.exit(1)
+        if not switch_class.support_take_over():
+            print "%s: CPU switching not supported" % str(switch_class)
+            sys.exit(1)
+
+        repeat_switch_cpus = [switch_class(switched_out=True, \
+                                               cpu_id=(i)) for i in xrange(np)]
+
+        for i in xrange(np):
+            repeat_switch_cpus[i].system = testsys
+            repeat_switch_cpus[i].workload = testsys.cpu[i].workload
+            repeat_switch_cpus[i].clock = testsys.cpu[i].clock
+
+            if options.maxinsts:
+                repeat_switch_cpus[i].max_insts_any_thread = options.maxinsts
+
+            if options.checker:
+                repeat_switch_cpus[i].addCheckerCpu()
+
+        testsys.repeat_switch_cpus = repeat_switch_cpus
+
+        if cpu_class:
+            repeat_switch_cpu_list = [(switch_cpus[i], repeat_switch_cpus[i])
+                                      for i in xrange(np)]
+        else:
+            repeat_switch_cpu_list = [(testsys.cpu[i], repeat_switch_cpus[i])
+                                      for i in xrange(np)]
+
     if options.standard_switch:
-        switch_cpus = [TimingSimpleCPU(defer_registration=True, cpu_id=(np+i))
+        switch_cpus = [TimingSimpleCPU(switched_out=True, cpu_id=(i))
                        for i in xrange(np)]
-        switch_cpus_1 = [DerivO3CPU(defer_registration=True, cpu_id=(2*np+i))
+        switch_cpus_1 = [DerivO3CPU(switched_out=True, cpu_id=(i))
                         for i in xrange(np)]
 
         for i in xrange(np):
             switch_cpus[i].system =  testsys
             switch_cpus_1[i].system =  testsys
-            if not buildEnv['FULL_SYSTEM']:
-                switch_cpus[i].workload = testsys.cpu[i].workload
-                switch_cpus_1[i].workload = testsys.cpu[i].workload
-            switch_cpus[i].clock = testsys.cpu[0].clock
-            switch_cpus_1[i].clock = testsys.cpu[0].clock
+            switch_cpus[i].workload = testsys.cpu[i].workload
+            switch_cpus_1[i].workload = testsys.cpu[i].workload
+            switch_cpus[i].clock = testsys.cpu[i].clock
+            switch_cpus_1[i].clock = testsys.cpu[i].clock
 
             # if restoring, make atomic cpu simulate only a few instructions
             if options.checkpoint_restore != None:
@@ -157,18 +381,18 @@ def run(options, root, testsys, cpu_class):
                 switch_cpus[i].max_insts_any_thread =  options.warmup_insts
 
             # simulation period
-            if options.max_inst:
-                switch_cpus_1[i].max_insts_any_thread = options.max_inst
+            if options.maxinsts:
+                switch_cpus_1[i].max_insts_any_thread = options.maxinsts
 
-            if not options.caches:
-                # O3 CPU must have a cache to work.
-                print "O3 CPU must be used with caches"
-                sys.exit(1)
+            # attach the checker cpu if selected
+            if options.checker:
+                switch_cpus[i].addCheckerCpu()
+                switch_cpus_1[i].addCheckerCpu()
 
-            testsys.switch_cpus = switch_cpus
-            testsys.switch_cpus_1 = switch_cpus_1
-            switch_cpu_list = [(testsys.cpu[i], switch_cpus[i]) for i in xrange(np)]
-            switch_cpu_list1 = [(switch_cpus[i], switch_cpus_1[i]) for i in xrange(np)]
+        testsys.switch_cpus = switch_cpus
+        testsys.switch_cpus_1 = switch_cpus_1
+        switch_cpu_list = [(testsys.cpu[i], switch_cpus[i]) for i in xrange(np)]
+        switch_cpu_list1 = [(switch_cpus[i], switch_cpus_1[i]) for i in xrange(np)]
 
     # set the checkpoint in the cpu before m5.instantiate is called
     if options.take_checkpoints != None and \
@@ -190,64 +414,10 @@ def run(options, root, testsys, cpu_class):
             for i in xrange(np):
                 testsys.cpu[i].max_insts_any_thread = offset
 
-    m5.instantiate(root)
-
+    checkpoint_dir = None
     if options.checkpoint_restore != None:
-        from os.path import isdir, exists
-        from os import listdir
-        import re
-
-        if not isdir(cptdir):
-            fatal("checkpoint dir %s does not exist!", cptdir)
-
-        if options.at_instruction:
-            checkpoint_dir = joinpath(cptdir, "cpt.%s.%s" % \
-                    (options.bench, options.checkpoint_restore))
-            if not exists(checkpoint_dir):
-                fatal("Unable to find checkpoint directory %s", checkpoint_dir)
-
-            print "Restoring checkpoint ..."
-            m5.restoreCheckpoint(root, checkpoint_dir)
-            print "Done."
-        elif options.simpoint:
-            # assume workload 0 has the simpoint
-            if testsys.cpu[0].workload[0].simpoint == 0:
-                fatal('Unable to find simpoint')
-
-            options.checkpoint_restore += \
-                int(testsys.cpu[0].workload[0].simpoint)
-
-            checkpoint_dir = joinpath(cptdir, "cpt.%s.%d" % \
-                    (options.bench, options.checkpoint_restore))
-            if not exists(checkpoint_dir):
-                fatal("Unable to find checkpoint directory %s.%s",
-                      options.bench, options.checkpoint_restore)
-
-            print "Restoring checkpoint ..."
-            m5.restoreCheckpoint(root,checkpoint_dir)
-            print "Done."
-        else:
-            dirs = listdir(cptdir)
-            expr = re.compile('cpt\.([0-9]*)')
-            cpts = []
-            for dir in dirs:
-                match = expr.match(dir)
-                if match:
-                    cpts.append(match.group(1))
-
-            cpts.sort(lambda a,b: cmp(long(a), long(b)))
-
-            cpt_num = options.checkpoint_restore
-
-            if cpt_num > len(cpts):
-                fatal('Checkpoint %d not found', cpt_num)
-
-            ## Adjust max tick based on our starting tick
-            maxtick = maxtick - int(cpts[cpt_num - 1])
-
-            ## Restore the checkpoint
-            m5.restoreCheckpoint(root,
-                    joinpath(cptdir, "cpt.%s" % cpts[cpt_num - 1]))
+        maxtick, checkpoint_dir = findCptDir(options, maxtick, cptdir, testsys)
+    m5.instantiate(checkpoint_dir)
 
     if options.standard_switch or cpu_class:
         if options.standard_switch:
@@ -261,17 +431,9 @@ def run(options, root, testsys, cpu_class):
         else:
             print "Switch at curTick count:%s" % str(10000)
             exit_event = m5.simulate(10000)
-        print "Switched CPUS @ cycle = %s" % (m5.curTick())
+        print "Switched CPUS @ tick %s" % (m5.curTick())
 
-        # when you change to Timing (or Atomic), you halt the system
-        # given as argument.  When you are finished with the system
-        # changes (including switchCpus), you must resume the system
-        # manually.  You DON'T need to resume after just switching
-        # CPUs if you haven't changed anything on the system level.
-
-        m5.changeToTiming(testsys)
-        m5.switchCpus(switch_cpu_list)
-        m5.resume(testsys)
+        m5.switchCpus(testsys, switch_cpu_list)
 
         if options.standard_switch:
             print "Switch at instruction count:%d" % \
@@ -281,16 +443,11 @@ def run(options, root, testsys, cpu_class):
             if options.warmup_insts:
                 exit_event = m5.simulate()
             else:
-                exit_event = m5.simulate(options.warmup)
-            print "Switching CPUS @ cycle = %s" % (m5.curTick())
+                exit_event = m5.simulate(options.standard_switch)
+            print "Switching CPUS @ tick %s" % (m5.curTick())
             print "Simulation ends instruction count:%d" % \
                     (testsys.switch_cpus_1[0].max_insts_any_thread)
-            m5.drain(testsys)
-            m5.switchCpus(switch_cpu_list1)
-            m5.resume(testsys)
-
-    num_checkpoints = 0
-    exit_cause = ''
+            m5.switchCpus(testsys, switch_cpu_list1)
 
     # If we're taking and restoring checkpoints, use checkpoint_dir
     # option only for finding the checkpoints to restore from.  This
@@ -302,88 +459,28 @@ def run(options, root, testsys, cpu_class):
         else:
             cptdir = getcwd()
 
-    # Checkpoints being taken via the command line at <when> and at
-    # subsequent periods of <period>.  Checkpoint instructions
-    # received from the benchmark running are ignored and skipped in
-    # favor of command line checkpoint instructions.
     if options.take_checkpoints != None :
-        if options.at_instruction or options.simpoint:
-            checkpoint_inst = int(options.take_checkpoints)
-
-            # maintain correct offset if we restored from some instruction
-            if options.checkpoint_restore != None:
-                checkpoint_inst += options.checkpoint_restore
-
-            print "Creating checkpoint at inst:%d" % (checkpoint_inst)
-            exit_event = m5.simulate()
-            print "exit cause = %s" % (exit_event.getCause())
-
-            # skip checkpoint instructions should they exist
-            while exit_event.getCause() == "checkpoint":
-                exit_event = m5.simulate()
-
-            if exit_event.getCause() == \
-                   "a thread reached the max instruction count":
-                m5.checkpoint(root, joinpath(cptdir, "cpt.%s.%d" % \
-                        (options.bench, checkpoint_inst)))
-                print "Checkpoint written."
-                num_checkpoints += 1
-
-            if exit_event.getCause() == "user interrupt received":
-                exit_cause = exit_event.getCause();
-        else:
-            when, period = options.take_checkpoints.split(",", 1)
-            when = int(when)
-            period = int(period)
-
-            exit_event = m5.simulate(when)
-            while exit_event.getCause() == "checkpoint":
-                exit_event = m5.simulate(when - m5.curTick())
-
-            if exit_event.getCause() == "simulate() limit reached":
-                m5.checkpoint(root, joinpath(cptdir, "cpt.%d"))
-                num_checkpoints += 1
-
-            sim_ticks = when
-            exit_cause = "maximum %d checkpoints dropped" % max_checkpoints
-            while num_checkpoints < max_checkpoints and \
-                    exit_event.getCause() == "simulate() limit reached":
-                if (sim_ticks + period) > maxtick:
-                    exit_event = m5.simulate(maxtick - sim_ticks)
-                    exit_cause = exit_event.getCause()
-                    break
-                else:
-                    exit_event = m5.simulate(period)
-                    sim_ticks += period
-                    while exit_event.getCause() == "checkpoint":
-                        exit_event = m5.simulate(sim_ticks - m5.curTick())
-                    if exit_event.getCause() == "simulate() limit reached":
-                        m5.checkpoint(root, joinpath(cptdir, "cpt.%d"))
-                        num_checkpoints += 1
-
-            if exit_event.getCause() != "simulate() limit reached":
-                exit_cause = exit_event.getCause();
-
-    else: # no checkpoints being taken via this script
+        # Checkpoints being taken via the command line at <when> and at
+        # subsequent periods of <period>.  Checkpoint instructions
+        # received from the benchmark running are ignored and skipped in
+        # favor of command line checkpoint instructions.
+        exit_event = scriptCheckpoints(options, maxtick, cptdir)
+    else:
         if options.fast_forward:
             m5.stats.reset()
         print "**** REAL SIMULATION ****"
-        exit_event = m5.simulate(maxtick)
 
-        while exit_event.getCause() == "checkpoint":
-            m5.checkpoint(root, joinpath(cptdir, "cpt.%d"))
-            num_checkpoints += 1
-            if num_checkpoints == max_checkpoints:
-                exit_cause = "maximum %d checkpoints dropped" % max_checkpoints
-                break
-
-            exit_event = m5.simulate(maxtick - m5.curTick())
-            exit_cause = exit_event.getCause()
-
-    if exit_cause == '':
-        exit_cause = exit_event.getCause()
-    print 'Exiting @ cycle %i because %s' % (m5.curTick(), exit_cause)
+        # If checkpoints are being taken, then the checkpoint instruction
+        # will occur in the benchmark code it self.
+        if options.repeat_switch and maxtick > options.repeat_switch:
+            exit_event = repeatSwitch(testsys, repeat_switch_cpu_list,
+                                      maxtick, options.repeat_switch)
+        else:
+            exit_event = benchCheckpoints(options, maxtick, cptdir)
 
+    print 'Exiting @ tick %i because %s' % (m5.curTick(), exit_event.getCause())
     if options.checkpoint_at_end:
-        m5.checkpoint(root, joinpath(cptdir, "cpt.%d"))
+        m5.checkpoint(joinpath(cptdir, "cpt.%d"))
 
+    if not m5.options.interactive:
+        sys.exit(exit_event.getCode())