| # 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 wrapped C++ functions | 
 | import _m5.drain | 
 | import _m5.core | 
 | from _m5.stats import updateEvents as updateStatEvents | 
 |  | 
 | import stats | 
 | import SimObject | 
 | import ticks | 
 | import objects | 
 | from m5.util.dot_writer import do_dot, do_dvfs_dot | 
 |  | 
 | 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, | 
 |     } | 
 |  | 
 | _drain_manager = _m5.drain.DrainManager.instance() | 
 |  | 
 | # 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 want to generate the DVFS diagram for the system. This can only be | 
 |     # done once all of the CPP objects have been created and initialised so | 
 |     # that we are able to figure out which object belongs to which domain. | 
 |     if options.dot_dvfs_config: | 
 |         do_dvfs_dot(root, options.outdir, options.dot_dvfs_config) | 
 |  | 
 |     # We're done registering statistics.  Enable the stats package now. | 
 |     stats.enable() | 
 |  | 
 |     # Restore checkpoint (if any) | 
 |     if ckpt_dir: | 
 |         _drain_manager.preCheckpointRestore() | 
 |         ckpt = _m5.core.getCheckpoint(ckpt_dir) | 
 |         _m5.core.unserializeGlobals(ckpt); | 
 |         for obj in root.descendants(): obj.loadState(ckpt) | 
 |     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_startup = True | 
 | def simulate(*args, **kwargs): | 
 |     global 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(_m5.core.doExitCleanup) | 
 |  | 
 |         # Reset to put the stats in a consistent state. | 
 |         stats.reset() | 
 |  | 
 |     if _drain_manager.isDrained(): | 
 |         _drain_manager.resume() | 
 |  | 
 |     return _m5.event.simulate(*args, **kwargs) | 
 |  | 
 | def drain(): | 
 |     """Drain the simulator in preparation of a checkpoint or memory mode | 
 |     switch. | 
 |  | 
 |     This operation is a no-op if the simulator is already in the | 
 |     Drained state. | 
 |  | 
 |     """ | 
 |  | 
 |     # 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(): | 
 |         # Try to drain the system. The drain is successful if all | 
 |         # objects are done without simulation. We need to simulate | 
 |         # more if not. | 
 |         if _drain_manager.tryDrain(): | 
 |             return True | 
 |  | 
 |         # WARNING: if a valid exit event occurs while draining, it | 
 |         # will not get returned to the user script | 
 |         exit_event = _m5.event.simulate() | 
 |         while exit_event.getCause() != 'Finished drain': | 
 |             exit_event = simulate() | 
 |  | 
 |         return False | 
 |  | 
 |     # Don't try to drain a system that is already drained | 
 |     is_drained = _drain_manager.isDrained() | 
 |     while not is_drained: | 
 |         is_drained = _drain() | 
 |  | 
 |     assert _drain_manager.isDrained(), "Drain state inconsistent" | 
 |  | 
 | def memWriteback(root): | 
 |     for obj in root.descendants(): | 
 |         obj.memWriteback() | 
 |  | 
 | def memInvalidate(root): | 
 |     for obj in root.descendants(): | 
 |         obj.memInvalidate() | 
 |  | 
 | def checkpoint(dir): | 
 |     root = objects.Root.getInstance() | 
 |     if not isinstance(root, objects.Root): | 
 |         raise TypeError, "Checkpoint must be called on a root object." | 
 |  | 
 |     drain() | 
 |     memWriteback(root) | 
 |     print "Writing checkpoint" | 
 |     _m5.core.serializeAll(dir) | 
 |  | 
 | 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: | 
 |         system.setMemoryMode(mode) | 
 |     else: | 
 |         print "System already in target mode. Memory mode unchanged." | 
 |  | 
 | def switchCpus(system, cpuList, verbose=True): | 
 |     """Switch CPUs in a system. | 
 |  | 
 |     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 | 
 |     """ | 
 |  | 
 |     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 | 
 |  | 
 |     drain() | 
 |  | 
 |     # 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) | 
 |  | 
 | def notifyFork(root): | 
 |     for obj in root.descendants(): | 
 |         obj.notifyFork() | 
 |  | 
 | fork_count = 0 | 
 | def fork(simout="%(parent)s.f%(fork_seq)i"): | 
 |     """Fork the simulator. | 
 |  | 
 |     This function forks the simulator. After forking the simulator, | 
 |     the child process gets its output files redirected to a new output | 
 |     directory. The default name of the output directory is the same as | 
 |     the parent with the suffix ".fN" added where N is the fork | 
 |     sequence number. The name of the output directory can be | 
 |     overridden using the simout keyword argument. | 
 |  | 
 |     Output file formatting dictionary: | 
 |       parent -- Path to the parent process's output directory. | 
 |       fork_seq -- Fork sequence number. | 
 |       pid -- PID of the child process. | 
 |  | 
 |     Keyword Arguments: | 
 |       simout -- New simulation output directory. | 
 |  | 
 |     Return Value: | 
 |       pid of the child process or 0 if running in the child. | 
 |     """ | 
 |     from m5 import options | 
 |     global fork_count | 
 |  | 
 |     if not _m5.core.listenersDisabled(): | 
 |         raise RuntimeError, "Can not fork a simulator with listeners enabled" | 
 |  | 
 |     drain() | 
 |  | 
 |     try: | 
 |         pid = os.fork() | 
 |     except OSError, e: | 
 |         raise e | 
 |  | 
 |     if pid == 0: | 
 |         # In child, notify objects of the fork | 
 |         root = objects.Root.getInstance() | 
 |         notifyFork(root) | 
 |         # Setup a new output directory | 
 |         parent = options.outdir | 
 |         options.outdir = simout % { | 
 |                 "parent" : parent, | 
 |                 "fork_seq" : fork_count, | 
 |                 "pid" : os.getpid(), | 
 |                 } | 
 |         _m5.core.setOutputDir(options.outdir) | 
 |     else: | 
 |         fork_count += 1 | 
 |  | 
 |     return pid | 
 |  | 
 | from _m5.core import disableAllListeners, listenersDisabled | 
 | from _m5.core import listenersLoopbackOnly | 
 | from _m5.core import curTick |