simulate.py revision 11418:0aeca8f47eac
14479Sbinkertn@umich.edu# Copyright (c) 2012 ARM Limited
24479Sbinkertn@umich.edu# All rights reserved.
34479Sbinkertn@umich.edu#
44479Sbinkertn@umich.edu# The license below extends only to copyright in the software and shall
54479Sbinkertn@umich.edu# not be construed as granting a license to any other intellectual
64479Sbinkertn@umich.edu# property including but not limited to intellectual property relating
74479Sbinkertn@umich.edu# to a hardware implementation of the functionality of the software
84479Sbinkertn@umich.edu# licensed hereunder.  You may use the software subject to the license
94479Sbinkertn@umich.edu# terms below provided that you ensure that this notice is replicated
104479Sbinkertn@umich.edu# unmodified and in its entirety in all distributions of the software,
114479Sbinkertn@umich.edu# modified or unmodified, in source code or in binary form.
124479Sbinkertn@umich.edu#
134479Sbinkertn@umich.edu# Copyright (c) 2005 The Regents of The University of Michigan
144479Sbinkertn@umich.edu# Copyright (c) 2010 Advanced Micro Devices, Inc.
154479Sbinkertn@umich.edu# All rights reserved.
164479Sbinkertn@umich.edu#
174479Sbinkertn@umich.edu# Redistribution and use in source and binary forms, with or without
184479Sbinkertn@umich.edu# modification, are permitted provided that the following conditions are
194479Sbinkertn@umich.edu# met: redistributions of source code must retain the above copyright
204479Sbinkertn@umich.edu# notice, this list of conditions and the following disclaimer;
214479Sbinkertn@umich.edu# redistributions in binary form must reproduce the above copyright
224479Sbinkertn@umich.edu# notice, this list of conditions and the following disclaimer in the
234479Sbinkertn@umich.edu# documentation and/or other materials provided with the distribution;
244479Sbinkertn@umich.edu# neither the name of the copyright holders nor the names of its
254479Sbinkertn@umich.edu# contributors may be used to endorse or promote products derived from
264479Sbinkertn@umich.edu# this software without specific prior written permission.
274479Sbinkertn@umich.edu#
286498Snate@binkert.org# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
294479Sbinkertn@umich.edu# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
304479Sbinkertn@umich.edu# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
314479Sbinkertn@umich.edu# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
324479Sbinkertn@umich.edu# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
334479Sbinkertn@umich.edu# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
344479Sbinkertn@umich.edu# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
354479Sbinkertn@umich.edu# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
364479Sbinkertn@umich.edu# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
374479Sbinkertn@umich.edu# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
384479Sbinkertn@umich.edu# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
394479Sbinkertn@umich.edu#
404479Sbinkertn@umich.edu# Authors: Nathan Binkert
414479Sbinkertn@umich.edu#          Steve Reinhardt
424479Sbinkertn@umich.edu
434479Sbinkertn@umich.eduimport atexit
444479Sbinkertn@umich.eduimport os
454479Sbinkertn@umich.eduimport sys
464479Sbinkertn@umich.edu
474479Sbinkertn@umich.edu# import the SWIG-wrapped main C++ functions
484479Sbinkertn@umich.eduimport internal
494479Sbinkertn@umich.eduimport core
504479Sbinkertn@umich.eduimport stats
514479Sbinkertn@umich.eduimport SimObject
524479Sbinkertn@umich.eduimport ticks
534479Sbinkertn@umich.eduimport objects
544479Sbinkertn@umich.edufrom m5.util.dot_writer import do_dot, do_dvfs_dot
554479Sbinkertn@umich.edufrom m5.internal.stats import updateEvents as updateStatEvents
564479Sbinkertn@umich.edu
574479Sbinkertn@umich.edufrom util import fatal
584479Sbinkertn@umich.edufrom util import attrdict
594479Sbinkertn@umich.edu
604479Sbinkertn@umich.edu# define a MaxTick parameter, unsigned 64 bit
614479Sbinkertn@umich.eduMaxTick = 2**64 - 1
624479Sbinkertn@umich.edu
634479Sbinkertn@umich.edu_memory_modes = {
644479Sbinkertn@umich.edu    "atomic" : objects.params.atomic,
654479Sbinkertn@umich.edu    "timing" : objects.params.timing,
664479Sbinkertn@umich.edu    "atomic_noncaching" : objects.params.atomic_noncaching,
674479Sbinkertn@umich.edu    }
684479Sbinkertn@umich.edu
694479Sbinkertn@umich.edu_drain_manager = internal.drain.DrainManager.instance()
704479Sbinkertn@umich.edu
714479Sbinkertn@umich.edu# The final hook to generate .ini files.  Called from the user script
724479Sbinkertn@umich.edu# once the config is built.
734479Sbinkertn@umich.edudef instantiate(ckpt_dir=None):
744479Sbinkertn@umich.edu    from m5 import options
754479Sbinkertn@umich.edu
764479Sbinkertn@umich.edu    root = objects.Root.getInstance()
774479Sbinkertn@umich.edu
784479Sbinkertn@umich.edu    if not root:
794479Sbinkertn@umich.edu        fatal("Need to instantiate Root() before calling instantiate()")
804479Sbinkertn@umich.edu
816498Snate@binkert.org    # we need to fix the global frequency
824479Sbinkertn@umich.edu    ticks.fixGlobalFrequency()
834479Sbinkertn@umich.edu
844479Sbinkertn@umich.edu    # Make sure SimObject-valued params are in the configuration
854479Sbinkertn@umich.edu    # hierarchy so we catch them with future descendants() walks
866498Snate@binkert.org    for obj in root.descendants(): obj.adoptOrphanParams()
874479Sbinkertn@umich.edu
884479Sbinkertn@umich.edu    # Unproxy in sorted order for determinism
894479Sbinkertn@umich.edu    for obj in root.descendants(): obj.unproxyParams()
904479Sbinkertn@umich.edu
914479Sbinkertn@umich.edu    if options.dump_config:
924479Sbinkertn@umich.edu        ini_file = file(os.path.join(options.outdir, options.dump_config), 'w')
934479Sbinkertn@umich.edu        # Print ini sections in sorted order for easier diffing
944479Sbinkertn@umich.edu        for obj in sorted(root.descendants(), key=lambda o: o.path()):
954479Sbinkertn@umich.edu            obj.print_ini(ini_file)
964479Sbinkertn@umich.edu        ini_file.close()
974479Sbinkertn@umich.edu
984479Sbinkertn@umich.edu    if options.json_config:
994479Sbinkertn@umich.edu        try:
1004479Sbinkertn@umich.edu            import json
1014479Sbinkertn@umich.edu            json_file = file(os.path.join(options.outdir, options.json_config), 'w')
1024479Sbinkertn@umich.edu            d = root.get_config_as_dict()
1034479Sbinkertn@umich.edu            json.dump(d, json_file, indent=4)
1044479Sbinkertn@umich.edu            json_file.close()
1054479Sbinkertn@umich.edu        except ImportError:
1064479Sbinkertn@umich.edu            pass
1074479Sbinkertn@umich.edu
1084479Sbinkertn@umich.edu    do_dot(root, options.outdir, options.dot_config)
1094479Sbinkertn@umich.edu
1104479Sbinkertn@umich.edu    # Initialize the global statistics
1114479Sbinkertn@umich.edu    stats.initSimStats()
1124479Sbinkertn@umich.edu
1134479Sbinkertn@umich.edu    # Create the C++ sim objects and connect ports
1144479Sbinkertn@umich.edu    for obj in root.descendants(): obj.createCCObject()
1154479Sbinkertn@umich.edu    for obj in root.descendants(): obj.connectPorts()
1164479Sbinkertn@umich.edu
1174479Sbinkertn@umich.edu    # Do a second pass to finish initializing the sim objects
1184479Sbinkertn@umich.edu    for obj in root.descendants(): obj.init()
1194479Sbinkertn@umich.edu
1204479Sbinkertn@umich.edu    # Do a third pass to initialize statistics
1214479Sbinkertn@umich.edu    for obj in root.descendants(): obj.regStats()
1224479Sbinkertn@umich.edu
1234479Sbinkertn@umich.edu    # Do a fourth pass to initialize probe points
1244479Sbinkertn@umich.edu    for obj in root.descendants(): obj.regProbePoints()
1254479Sbinkertn@umich.edu
1264479Sbinkertn@umich.edu    # Do a fifth pass to connect probe listeners
1274479Sbinkertn@umich.edu    for obj in root.descendants(): obj.regProbeListeners()
1284479Sbinkertn@umich.edu
1294479Sbinkertn@umich.edu    # We want to generate the DVFS diagram for the system. This can only be
1304479Sbinkertn@umich.edu    # done once all of the CPP objects have been created and initialised so
1314479Sbinkertn@umich.edu    # that we are able to figure out which object belongs to which domain.
1324479Sbinkertn@umich.edu    do_dvfs_dot(root, options.outdir, options.dot_dvfs_config)
1334479Sbinkertn@umich.edu
1344479Sbinkertn@umich.edu    # We're done registering statistics.  Enable the stats package now.
1354479Sbinkertn@umich.edu    stats.enable()
1364479Sbinkertn@umich.edu
1374479Sbinkertn@umich.edu    # Restore checkpoint (if any)
1384479Sbinkertn@umich.edu    if ckpt_dir:
1394479Sbinkertn@umich.edu        _drain_manager.preCheckpointRestore()
1404479Sbinkertn@umich.edu        ckpt = internal.core.getCheckpoint(ckpt_dir)
1414479Sbinkertn@umich.edu        internal.core.unserializeGlobals(ckpt);
1424479Sbinkertn@umich.edu        for obj in root.descendants(): obj.loadState(ckpt)
1434479Sbinkertn@umich.edu    else:
1444479Sbinkertn@umich.edu        for obj in root.descendants(): obj.initState()
1454479Sbinkertn@umich.edu
1464479Sbinkertn@umich.edu    # Check to see if any of the stat events are in the past after resuming from
1474479Sbinkertn@umich.edu    # a checkpoint, If so, this call will shift them to be at a valid time.
1484479Sbinkertn@umich.edu    updateStatEvents()
1494479Sbinkertn@umich.edu
1504479Sbinkertn@umich.eduneed_startup = True
1514479Sbinkertn@umich.edudef simulate(*args, **kwargs):
1524479Sbinkertn@umich.edu    global need_startup
1534479Sbinkertn@umich.edu
1544479Sbinkertn@umich.edu    if need_startup:
1554479Sbinkertn@umich.edu        root = objects.Root.getInstance()
1564479Sbinkertn@umich.edu        for obj in root.descendants(): obj.startup()
1574479Sbinkertn@umich.edu        need_startup = False
1584479Sbinkertn@umich.edu
1594479Sbinkertn@umich.edu        # Python exit handlers happen in reverse order.
1604479Sbinkertn@umich.edu        # We want to dump stats last.
1614479Sbinkertn@umich.edu        atexit.register(stats.dump)
1624479Sbinkertn@umich.edu
1634479Sbinkertn@umich.edu        # register our C++ exit callback function with Python
1644479Sbinkertn@umich.edu        atexit.register(internal.core.doExitCleanup)
1654479Sbinkertn@umich.edu
1664479Sbinkertn@umich.edu        # Reset to put the stats in a consistent state.
1674479Sbinkertn@umich.edu        stats.reset()
1686498Snate@binkert.org
1694479Sbinkertn@umich.edu    if _drain_manager.isDrained():
1704479Sbinkertn@umich.edu        _drain_manager.resume()
1714479Sbinkertn@umich.edu
1724479Sbinkertn@umich.edu    return internal.event.simulate(*args, **kwargs)
1734479Sbinkertn@umich.edu
1744479Sbinkertn@umich.edu# Export curTick to user script.
1754479Sbinkertn@umich.edudef curTick():
1766498Snate@binkert.org    return internal.core.curTick()
1776498Snate@binkert.org
1784479Sbinkertn@umich.edudef drain():
1796498Snate@binkert.org    """Drain the simulator in preparation of a checkpoint or memory mode
1804479Sbinkertn@umich.edu    switch.
1814479Sbinkertn@umich.edu
1824479Sbinkertn@umich.edu    This operation is a no-op if the simulator is already in the
1834479Sbinkertn@umich.edu    Drained state.
1844479Sbinkertn@umich.edu
1854479Sbinkertn@umich.edu    """
1864479Sbinkertn@umich.edu
1874479Sbinkertn@umich.edu    # Try to drain all objects. Draining might not be completed unless
1884479Sbinkertn@umich.edu    # all objects return that they are drained on the first call. This
1894479Sbinkertn@umich.edu    # is because as objects drain they may cause other objects to no
1904479Sbinkertn@umich.edu    # longer be drained.
1914479Sbinkertn@umich.edu    def _drain():
1924479Sbinkertn@umich.edu        # Try to drain the system. The drain is successful if all
1934479Sbinkertn@umich.edu        # objects are done without simulation. We need to simulate
1944479Sbinkertn@umich.edu        # more if not.
1954479Sbinkertn@umich.edu        if _drain_manager.tryDrain():
1964479Sbinkertn@umich.edu            return True
1974479Sbinkertn@umich.edu
1984479Sbinkertn@umich.edu        # WARNING: if a valid exit event occurs while draining, it
1994479Sbinkertn@umich.edu        # will not get returned to the user script
2004479Sbinkertn@umich.edu        exit_event = internal.event.simulate()
2014479Sbinkertn@umich.edu        while exit_event.getCause() != 'Finished drain':
2024479Sbinkertn@umich.edu            exit_event = simulate()
2034479Sbinkertn@umich.edu
2044479Sbinkertn@umich.edu        return False
2054479Sbinkertn@umich.edu
2064479Sbinkertn@umich.edu    # Don't try to drain a system that is already drained
2074479Sbinkertn@umich.edu    is_drained = _drain_manager.isDrained()
2084479Sbinkertn@umich.edu    while not is_drained:
2094479Sbinkertn@umich.edu        is_drained = _drain()
2104479Sbinkertn@umich.edu
2114479Sbinkertn@umich.edu    assert _drain_manager.isDrained(), "Drain state inconsistent"
2124479Sbinkertn@umich.edu
2134479Sbinkertn@umich.edudef memWriteback(root):
2144479Sbinkertn@umich.edu    for obj in root.descendants():
2154479Sbinkertn@umich.edu        obj.memWriteback()
2164479Sbinkertn@umich.edu
2174479Sbinkertn@umich.edudef memInvalidate(root):
218    for obj in root.descendants():
219        obj.memInvalidate()
220
221def checkpoint(dir):
222    root = objects.Root.getInstance()
223    if not isinstance(root, objects.Root):
224        raise TypeError, "Checkpoint must be called on a root object."
225
226    drain()
227    memWriteback(root)
228    print "Writing checkpoint"
229    internal.core.serializeAll(dir)
230
231def _changeMemoryMode(system, mode):
232    if not isinstance(system, (objects.Root, objects.System)):
233        raise TypeError, "Parameter of type '%s'.  Must be type %s or %s." % \
234              (type(system), objects.Root, objects.System)
235    if system.getMemoryMode() != mode:
236        system.setMemoryMode(mode)
237    else:
238        print "System already in target mode. Memory mode unchanged."
239
240def switchCpus(system, cpuList, verbose=True):
241    """Switch CPUs in a system.
242
243    Note: This method may switch the memory mode of the system if that
244    is required by the CPUs. It may also flush all caches in the
245    system.
246
247    Arguments:
248      system -- Simulated system.
249      cpuList -- (old_cpu, new_cpu) tuples
250    """
251
252    if verbose:
253        print "switching cpus"
254
255    if not isinstance(cpuList, list):
256        raise RuntimeError, "Must pass a list to this function"
257    for item in cpuList:
258        if not isinstance(item, tuple) or len(item) != 2:
259            raise RuntimeError, "List must have tuples of (oldCPU,newCPU)"
260
261    old_cpus = [old_cpu for old_cpu, new_cpu in cpuList]
262    new_cpus = [new_cpu for old_cpu, new_cpu in cpuList]
263    old_cpu_set = set(old_cpus)
264    memory_mode_name = new_cpus[0].memory_mode()
265    for old_cpu, new_cpu in cpuList:
266        if not isinstance(old_cpu, objects.BaseCPU):
267            raise TypeError, "%s is not of type BaseCPU" % old_cpu
268        if not isinstance(new_cpu, objects.BaseCPU):
269            raise TypeError, "%s is not of type BaseCPU" % new_cpu
270        if new_cpu in old_cpu_set:
271            raise RuntimeError, \
272                "New CPU (%s) is in the list of old CPUs." % (old_cpu,)
273        if not new_cpu.switchedOut():
274            raise RuntimeError, \
275                "New CPU (%s) is already active." % (new_cpu,)
276        if not new_cpu.support_take_over():
277            raise RuntimeError, \
278                "New CPU (%s) does not support CPU handover." % (old_cpu,)
279        if new_cpu.memory_mode() != memory_mode_name:
280            raise RuntimeError, \
281                "%s and %s require different memory modes." % (new_cpu,
282                                                               new_cpus[0])
283        if old_cpu.switchedOut():
284            raise RuntimeError, \
285                "Old CPU (%s) is inactive." % (new_cpu,)
286        if not old_cpu.support_take_over():
287            raise RuntimeError, \
288                "Old CPU (%s) does not support CPU handover." % (old_cpu,)
289
290    try:
291        memory_mode = _memory_modes[memory_mode_name]
292    except KeyError:
293        raise RuntimeError, "Invalid memory mode (%s)" % memory_mode_name
294
295    drain()
296
297    # Now all of the CPUs are ready to be switched out
298    for old_cpu, new_cpu in cpuList:
299        old_cpu.switchOut()
300
301    # Change the memory mode if required. We check if this is needed
302    # to avoid printing a warning if no switch was performed.
303    if system.getMemoryMode() != memory_mode:
304        # Flush the memory system if we are switching to a memory mode
305        # that disables caches. This typically happens when switching to a
306        # hardware virtualized CPU.
307        if memory_mode == objects.params.atomic_noncaching:
308            memWriteback(system)
309            memInvalidate(system)
310
311        _changeMemoryMode(system, memory_mode)
312
313    for old_cpu, new_cpu in cpuList:
314        new_cpu.takeOverFrom(old_cpu)
315
316def notifyFork(root):
317    for obj in root.descendants():
318        obj.notifyFork()
319
320fork_count = 0
321def fork(simout="%(parent)s.f%(fork_seq)i"):
322    """Fork the simulator.
323
324    This function forks the simulator. After forking the simulator,
325    the child process gets its output files redirected to a new output
326    directory. The default name of the output directory is the same as
327    the parent with the suffix ".fN" added where N is the fork
328    sequence number. The name of the output directory can be
329    overridden using the simout keyword argument.
330
331    Output file formatting dictionary:
332      parent -- Path to the parent process's output directory.
333      fork_seq -- Fork sequence number.
334      pid -- PID of the child process.
335
336    Keyword Arguments:
337      simout -- New simulation output directory.
338
339    Return Value:
340      pid of the child process or 0 if running in the child.
341    """
342    from m5 import options
343    global fork_count
344
345    if not internal.core.listenersDisabled():
346        raise RuntimeError, "Can not fork a simulator with listeners enabled"
347
348    drain()
349
350    try:
351        pid = os.fork()
352    except OSError, e:
353        raise e
354
355    if pid == 0:
356        # In child, notify objects of the fork
357        root = objects.Root.getInstance()
358        notifyFork(root)
359        # Setup a new output directory
360        parent = options.outdir
361        options.outdir = simout % {
362                "parent" : parent,
363                "fork_seq" : fork_count,
364                "pid" : os.getpid(),
365                }
366        core.setOutputDir(options.outdir)
367    else:
368        fork_count += 1
369
370    return pid
371
372from internal.core import disableAllListeners
373from internal.core import listenersDisabled
374