diff --git a/configs/common/Simulation.py b/configs/common/Simulation.py index d678c57a0..6f0e3cee0 100644 --- a/configs/common/Simulation.py +++ b/configs/common/Simulation.py @@ -242,10 +242,7 @@ def repeatSwitch(testsys, repeat_switch_cpu_list, maxtick, switch_freq): if exit_cause != "simulate() limit reached": return exit_event - print "draining the system" - m5.drain(testsys) - m5.switchCpus(repeat_switch_cpu_list) - m5.resume(testsys) + m5.switchCpus(testsys, repeat_switch_cpu_list) tmp_cpu_list = [] for old_cpu, new_cpu in repeat_switch_cpu_list: @@ -436,15 +433,7 @@ def run(options, root, testsys, cpu_class): exit_event = m5.simulate(10000) 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" % \ @@ -458,9 +447,7 @@ def run(options, root, testsys, cpu_class): 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) + 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 diff --git a/src/python/m5/simulate.py b/src/python/m5/simulate.py index a30648929..3583e8264 100644 --- a/src/python/m5/simulate.py +++ b/src/python/m5/simulate.py @@ -60,6 +60,11 @@ from util import attrdict # define a MaxTick parameter MaxTick = 2**63 - 1 +_memory_modes = { + "atomic" : objects.params.atomic, + "timing" : objects.params.timing, + } + # The final hook to generate .ini files. Called from the user script # once the config is built. def instantiate(ckpt_dir=None): @@ -202,7 +207,7 @@ def checkpoint(dir): internal.core.serializeAll(dir) resume(root) -def changeMemoryMode(system, mode): +def _changeMemoryMode(system, mode): if not isinstance(system, (objects.Root, objects.System)): raise TypeError, "Parameter of type '%s'. Must be type %s or %s." % \ (type(system), objects.Root, objects.System) @@ -212,15 +217,26 @@ def changeMemoryMode(system, mode): else: print "System already in target mode. Memory mode unchanged." -def changeToAtomic(system, **kwargs): - print "Changing memory mode to atomic" - changeMemoryMode(system, objects.params.atomic, **kwargs) +def switchCpus(system, cpuList, do_drain=True): + """Switch CPUs in a system. -def changeToTiming(system, **kwargs): - print "Changing memory mode to timing" - changeMemoryMode(system, objects.params.timing, **kwargs) + By default, this method drains and resumes the system. This + behavior can be disabled by setting the keyword argument + 'do_drain' to false, which might be desirable if multiple + operations requiring a drained system are going to be performed in + sequence. -def switchCpus(cpuList): + Note: This method may switch the memory mode of the system if that + is required by the CPUs. It may also flush all caches in the + system. + + Arguments: + system -- Simulated system. + cpuList -- (old_cpu, new_cpu) tuples + + Keyword Arguments: + do_drain -- Perform a drain/resume of the system when switching. + """ print "switching cpus" if not isinstance(cpuList, list): raise RuntimeError, "Must pass a list to this function" @@ -228,7 +244,10 @@ def switchCpus(cpuList): if not isinstance(item, tuple) or len(item) != 2: raise RuntimeError, "List must have tuples of (oldCPU,newCPU)" - old_cpu_set = set([old_cpu for old_cpu, new_cpu in cpuList]) + old_cpus = [old_cpu for old_cpu, new_cpu in cpuList] + new_cpus = [new_cpu for old_cpu, new_cpu in cpuList] + old_cpu_set = set(old_cpus) + memory_mode_name = new_cpus[0].memory_mode() for old_cpu, new_cpu in cpuList: if not isinstance(old_cpu, objects.BaseCPU): raise TypeError, "%s is not of type BaseCPU" % old_cpu @@ -240,15 +259,41 @@ def switchCpus(cpuList): if not new_cpu.switchedOut(): raise RuntimeError, \ "New CPU (%s) is already active." % (new_cpu,) + if not new_cpu.support_take_over(): + raise RuntimeError, \ + "New CPU (%s) does not support CPU handover." % (old_cpu,) + if new_cpu.memory_mode() != memory_mode_name: + raise RuntimeError, \ + "%s and %s require different memory modes." % (new_cpu, + new_cpus[0]) if old_cpu.switchedOut(): raise RuntimeError, \ "Old CPU (%s) is inactive." % (new_cpu,) + if not old_cpu.support_take_over(): + raise RuntimeError, \ + "Old CPU (%s) does not support CPU handover." % (old_cpu,) + + try: + memory_mode = _memory_modes[memory_mode_name] + except KeyError: + raise RuntimeError, "Invalid memory mode (%s)" % memory_mode_name + + if do_drain: + drain(system) # Now all of the CPUs are ready to be switched out for old_cpu, new_cpu in cpuList: old_cpu.switchOut() + # Change the memory mode if required. We check if this is needed + # to avoid printing a warning if no switch was performed. + if system.getMemoryMode() != memory_mode: + _changeMemoryMode(system, memory_mode) + for old_cpu, new_cpu in cpuList: new_cpu.takeOverFrom(old_cpu) + if do_drain: + resume(system) + from internal.core import disableAllListeners diff --git a/tests/configs/switcheroo.py b/tests/configs/switcheroo.py index dadf8db03..4b2dd9a69 100644 --- a/tests/configs/switcheroo.py +++ b/tests/configs/switcheroo.py @@ -40,12 +40,6 @@ from m5.objects import * m5.util.addToPath('../configs/common') from Caches import * -def _memMode(cclass): - if cclass == AtomicSimpleCPU: - return "atomic", m5.objects.params.atomic - else: - return "timing", m5.objects.params.timing - class Sequential: """Sequential CPU switcher. @@ -104,7 +98,7 @@ def run_test(root, switcher=None, freq=1000): current_cpu = switcher.first() system = root.system - system.mem_mode = _memMode(type(current_cpu))[0] + system.mem_mode = type(current_cpu).memory_mode() # instantiate configuration m5.instantiate() @@ -122,9 +116,9 @@ def run_test(root, switcher=None, freq=1000): print "Switching CPUs..." print "Next CPU: %s" % type(next_cpu) m5.drain(system) - system.setMemoryMode(_memMode(type(next_cpu))[1]) if current_cpu != next_cpu: - m5.switchCpus([ (current_cpu, next_cpu) ]) + m5.switchCpus(system, [ (current_cpu, next_cpu) ], + do_drain=False) else: print "Source CPU and destination CPU are the same, skipping..." m5.resume(system)