| # 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) 2005 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 | |
| # 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: Nathan Binkert | |
| # Steve Reinhardt | |
| import atexit | |
| import os | |
| import sys | |
| # import the SWIG-wrapped main C++ functions | |
| import internal | |
| import core | |
| import stats | |
| import SimObject | |
| import ticks | |
| import objects | |
| from m5.util.dot_writer import do_dot | |
| from m5.internal.stats import updateEvents as updateStatEvents | |
| from util import fatal | |
| from util import attrdict | |
| # define a MaxTick parameter, unsigned 64 bit | |
| MaxTick = 2**64 - 1 | |
| _memory_modes = { | |
| "atomic" : objects.params.atomic, | |
| "timing" : objects.params.timing, | |
| "atomic_noncaching" : objects.params.atomic_noncaching, | |
| } | |
| # The final hook to generate .ini files. Called from the user script | |
| # once the config is built. | |
| def instantiate(ckpt_dir=None): | |
| from m5 import options | |
| root = objects.Root.getInstance() | |
| if not root: | |
| fatal("Need to instantiate Root() before calling instantiate()") | |
| # we need to fix the global frequency | |
| ticks.fixGlobalFrequency() | |
| # Make sure SimObject-valued params are in the configuration | |
| # hierarchy so we catch them with future descendants() walks | |
| for obj in root.descendants(): obj.adoptOrphanParams() | |
| # Unproxy in sorted order for determinism | |
| for obj in root.descendants(): obj.unproxyParams() | |
| if options.dump_config: | |
| ini_file = file(os.path.join(options.outdir, options.dump_config), 'w') | |
| # Print ini sections in sorted order for easier diffing | |
| for obj in sorted(root.descendants(), key=lambda o: o.path()): | |
| obj.print_ini(ini_file) | |
| ini_file.close() | |
| if options.json_config: | |
| try: | |
| import json | |
| json_file = file(os.path.join(options.outdir, options.json_config), 'w') | |
| d = root.get_config_as_dict() | |
| json.dump(d, json_file, indent=4) | |
| json_file.close() | |
| except ImportError: | |
| pass | |
| do_dot(root, options.outdir, options.dot_config) | |
| # Initialize the global statistics | |
| stats.initSimStats() | |
| # Create the C++ sim objects and connect ports | |
| for obj in root.descendants(): obj.createCCObject() | |
| for obj in root.descendants(): obj.connectPorts() | |
| # Do a second pass to finish initializing the sim objects | |
| for obj in root.descendants(): obj.init() | |
| # Do a third pass to initialize statistics | |
| for obj in root.descendants(): obj.regStats() | |
| # Do a fourth pass to initialize probe points | |
| for obj in root.descendants(): obj.regProbePoints() | |
| # Do a fifth pass to connect probe listeners | |
| for obj in root.descendants(): obj.regProbeListeners() | |
| # We're done registering statistics. Enable the stats package now. | |
| stats.enable() | |
| # Restore checkpoint (if any) | |
| if ckpt_dir: | |
| ckpt = internal.core.getCheckpoint(ckpt_dir) | |
| internal.core.unserializeGlobals(ckpt); | |
| for obj in root.descendants(): obj.loadState(ckpt) | |
| need_resume.append(root) | |
| else: | |
| for obj in root.descendants(): obj.initState() | |
| # Check to see if any of the stat events are in the past after resuming from | |
| # a checkpoint, If so, this call will shift them to be at a valid time. | |
| updateStatEvents() | |
| need_resume = [] | |
| need_startup = True | |
| def simulate(*args, **kwargs): | |
| global need_resume, need_startup | |
| if need_startup: | |
| root = objects.Root.getInstance() | |
| for obj in root.descendants(): obj.startup() | |
| need_startup = False | |
| # Python exit handlers happen in reverse order. | |
| # We want to dump stats last. | |
| atexit.register(stats.dump) | |
| # register our C++ exit callback function with Python | |
| atexit.register(internal.core.doExitCleanup) | |
| # Reset to put the stats in a consistent state. | |
| stats.reset() | |
| for root in need_resume: | |
| resume(root) | |
| need_resume = [] | |
| return internal.event.simulate(*args, **kwargs) | |
| # Export curTick to user script. | |
| def curTick(): | |
| return internal.core.curTick() | |
| # Drain the system in preparation of a checkpoint or memory mode | |
| # switch. | |
| def drain(root): | |
| # Try to drain all objects. Draining might not be completed unless | |
| # all objects return that they are drained on the first call. This | |
| # is because as objects drain they may cause other objects to no | |
| # longer be drained. | |
| def _drain(): | |
| all_drained = False | |
| dm = internal.drain.createDrainManager() | |
| unready_objs = sum(obj.drain(dm) for obj in root.descendants()) | |
| # If we've got some objects that can't drain immediately, then simulate | |
| if unready_objs > 0: | |
| dm.setCount(unready_objs) | |
| #WARNING: if a valid exit event occurs while draining, it will not | |
| # get returned to the user script | |
| exit_event = simulate() | |
| while exit_event.getCause() != 'Finished drain': | |
| exit_event = simulate() | |
| else: | |
| all_drained = True | |
| internal.drain.cleanupDrainManager(dm) | |
| return all_drained | |
| all_drained = _drain() | |
| while (not all_drained): | |
| all_drained = _drain() | |
| def memWriteback(root): | |
| for obj in root.descendants(): | |
| obj.memWriteback() | |
| def memInvalidate(root): | |
| for obj in root.descendants(): | |
| obj.memInvalidate() | |
| def resume(root): | |
| for obj in root.descendants(): obj.drainResume() | |
| def checkpoint(dir): | |
| root = objects.Root.getInstance() | |
| if not isinstance(root, objects.Root): | |
| raise TypeError, "Checkpoint must be called on a root object." | |
| drain(root) | |
| memWriteback(root) | |
| print "Writing checkpoint" | |
| internal.core.serializeAll(dir) | |
| resume(root) | |
| 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) | |
| if system.getMemoryMode() != mode: | |
| drain(system) | |
| system.setMemoryMode(mode) | |
| else: | |
| print "System already in target mode. Memory mode unchanged." | |
| def switchCpus(system, cpuList, do_drain=True, verbose=True): | |
| """Switch CPUs in a system. | |
| 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. | |
| 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. | |
| """ | |
| if verbose: | |
| print "switching cpus" | |
| if not isinstance(cpuList, list): | |
| raise RuntimeError, "Must pass a list to this function" | |
| for item in cpuList: | |
| if not isinstance(item, tuple) or len(item) != 2: | |
| raise RuntimeError, "List must have tuples of (oldCPU,newCPU)" | |
| 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 | |
| if not isinstance(new_cpu, objects.BaseCPU): | |
| raise TypeError, "%s is not of type BaseCPU" % new_cpu | |
| if new_cpu in old_cpu_set: | |
| raise RuntimeError, \ | |
| "New CPU (%s) is in the list of old CPUs." % (old_cpu,) | |
| 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: | |
| # Flush the memory system if we are switching to a memory mode | |
| # that disables caches. This typically happens when switching to a | |
| # hardware virtualized CPU. | |
| if memory_mode == objects.params.atomic_noncaching: | |
| memWriteback(system) | |
| memInvalidate(system) | |
| _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 |