simulate.py revision 11991
17008Snate@binkert.org# Copyright (c) 2012 ARM Limited
27008Snate@binkert.org# All rights reserved.
37008Snate@binkert.org#
47008Snate@binkert.org# The license below extends only to copyright in the software and shall
57008Snate@binkert.org# not be construed as granting a license to any other intellectual
67008Snate@binkert.org# property including but not limited to intellectual property relating
77008Snate@binkert.org# to a hardware implementation of the functionality of the software
87008Snate@binkert.org# licensed hereunder.  You may use the software subject to the license
97008Snate@binkert.org# terms below provided that you ensure that this notice is replicated
107008Snate@binkert.org# unmodified and in its entirety in all distributions of the software,
117008Snate@binkert.org# modified or unmodified, in source code or in binary form.
127008Snate@binkert.org#
137008Snate@binkert.org# Copyright (c) 2005 The Regents of The University of Michigan
147008Snate@binkert.org# Copyright (c) 2010 Advanced Micro Devices, Inc.
157008Snate@binkert.org# All rights reserved.
167008Snate@binkert.org#
177008Snate@binkert.org# Redistribution and use in source and binary forms, with or without
187008Snate@binkert.org# modification, are permitted provided that the following conditions are
197008Snate@binkert.org# met: redistributions of source code must retain the above copyright
207008Snate@binkert.org# notice, this list of conditions and the following disclaimer;
217008Snate@binkert.org# redistributions in binary form must reproduce the above copyright
227008Snate@binkert.org# notice, this list of conditions and the following disclaimer in the
237008Snate@binkert.org# documentation and/or other materials provided with the distribution;
247008Snate@binkert.org# neither the name of the copyright holders nor the names of its
257008Snate@binkert.org# contributors may be used to endorse or promote products derived from
267008Snate@binkert.org# this software without specific prior written permission.
277008Snate@binkert.org#
286285Snate@binkert.org# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
297039Snate@binkert.org# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
307039Snate@binkert.org# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
316285Snate@binkert.org# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
327055Snate@binkert.org# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
337055Snate@binkert.org# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
346876Ssteve.reinhardt@amd.com# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
359745Snilay@cs.wisc.edu# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
368341Snilay@cs.wisc.edu# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
376506Spdudnik@gmail.com# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
387055Snate@binkert.org# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
398436SBrad.Beckmann@amd.com#
409497Snilay@cs.wisc.edu# Authors: Nathan Binkert
4110301Snilay@cs.wisc.edu#          Steve Reinhardt
4210301Snilay@cs.wisc.edu
436881SBrad.Beckmann@amd.comimport atexit
4410301Snilay@cs.wisc.eduimport os
459364Snilay@cs.wisc.eduimport sys
467055Snate@binkert.org
479465Snilay@cs.wisc.edu# import the wrapped C++ functions
486285Snate@binkert.orgimport _m5.drain
496285Snate@binkert.orgimport _m5.core
506285Snate@binkert.orgfrom _m5.stats import updateEvents as updateStatEvents
519465Snilay@cs.wisc.edu
527039Snate@binkert.orgimport stats
537039Snate@binkert.orgimport SimObject
546876Ssteve.reinhardt@amd.comimport ticks
558436SBrad.Beckmann@amd.comimport objects
569496Snilay@cs.wisc.edufrom m5.util.dot_writer import do_dot, do_dvfs_dot
578257SBrad.Beckmann@amd.com
589745Snilay@cs.wisc.edufrom util import fatal
5910078Snilay@cs.wisc.edufrom util import attrdict
6010078Snilay@cs.wisc.edu
6110078Snilay@cs.wisc.edu# define a MaxTick parameter, unsigned 64 bit
629819Snilay@cs.wisc.eduMaxTick = 2**64 - 1
639819Snilay@cs.wisc.edu
649819Snilay@cs.wisc.edu_memory_modes = {
659819Snilay@cs.wisc.edu    "atomic" : objects.params.atomic,
669819Snilay@cs.wisc.edu    "timing" : objects.params.timing,
679819Snilay@cs.wisc.edu    "atomic_noncaching" : objects.params.atomic_noncaching,
687039Snate@binkert.org    }
698531Snilay@cs.wisc.edu
706285Snate@binkert.org_drain_manager = _m5.drain.DrainManager.instance()
717055Snate@binkert.org
727039Snate@binkert.org# The final hook to generate .ini files.  Called from the user script
7310012Snilay@cs.wisc.edu# once the config is built.
7410012Snilay@cs.wisc.edudef instantiate(ckpt_dir=None):
759745Snilay@cs.wisc.edu    from m5 import options
768683Snilay@cs.wisc.edu
778683Snilay@cs.wisc.edu    root = objects.Root.getInstance()
789302Snilay@cs.wisc.edu
799302Snilay@cs.wisc.edu    if not root:
809302Snilay@cs.wisc.edu        fatal("Need to instantiate Root() before calling instantiate()")
819302Snilay@cs.wisc.edu
829302Snilay@cs.wisc.edu    # we need to fix the global frequency
839302Snilay@cs.wisc.edu    ticks.fixGlobalFrequency()
8410522Snilay@cs.wisc.edu
859302Snilay@cs.wisc.edu    # Make sure SimObject-valued params are in the configuration
869302Snilay@cs.wisc.edu    # hierarchy so we catch them with future descendants() walks
879302Snilay@cs.wisc.edu    for obj in root.descendants(): obj.adoptOrphanParams()
8810522Snilay@cs.wisc.edu
899363Snilay@cs.wisc.edu    # Unproxy in sorted order for determinism
909363Snilay@cs.wisc.edu    for obj in root.descendants(): obj.unproxyParams()
919363Snilay@cs.wisc.edu
929363Snilay@cs.wisc.edu    if options.dump_config:
939364Snilay@cs.wisc.edu        ini_file = file(os.path.join(options.outdir, options.dump_config), 'w')
949745Snilay@cs.wisc.edu        # Print ini sections in sorted order for easier diffing
959745Snilay@cs.wisc.edu        for obj in sorted(root.descendants(), key=lambda o: o.path()):
969745Snilay@cs.wisc.edu            obj.print_ini(ini_file)
979745Snilay@cs.wisc.edu        ini_file.close()
989745Snilay@cs.wisc.edu
999745Snilay@cs.wisc.edu    if options.json_config:
10010311Snilay@cs.wisc.edu        try:
10110311Snilay@cs.wisc.edu            import json
10210311Snilay@cs.wisc.edu            json_file = file(os.path.join(options.outdir, options.json_config), 'w')
1039496Snilay@cs.wisc.edu            d = root.get_config_as_dict()
1049496Snilay@cs.wisc.edu            json.dump(d, json_file, indent=4)
1059496Snilay@cs.wisc.edu            json_file.close()
10610012Snilay@cs.wisc.edu        except ImportError:
10710012Snilay@cs.wisc.edu            pass
10810012Snilay@cs.wisc.edu
1099497Snilay@cs.wisc.edu    do_dot(root, options.outdir, options.dot_config)
1109496Snilay@cs.wisc.edu
1119496Snilay@cs.wisc.edu    # Initialize the global statistics
1129496Snilay@cs.wisc.edu    stats.initSimStats()
1139497Snilay@cs.wisc.edu
1149507Snilay@cs.wisc.edu    # Create the C++ sim objects and connect ports
1159496Snilay@cs.wisc.edu    for obj in root.descendants(): obj.createCCObject()
1169596Snilay@cs.wisc.edu    for obj in root.descendants(): obj.connectPorts()
1179596Snilay@cs.wisc.edu
1189596Snilay@cs.wisc.edu    # Do a second pass to finish initializing the sim objects
1199596Snilay@cs.wisc.edu    for obj in root.descendants(): obj.init()
1209596Snilay@cs.wisc.edu
1219364Snilay@cs.wisc.edu    # Do a third pass to initialize statistics
1229364Snilay@cs.wisc.edu    for obj in root.descendants(): obj.regStats()
12310005Snilay@cs.wisc.edu
12410005Snilay@cs.wisc.edu    # Do a fourth pass to initialize probe points
12510005Snilay@cs.wisc.edu    for obj in root.descendants(): obj.regProbePoints()
1269364Snilay@cs.wisc.edu
1279364Snilay@cs.wisc.edu    # Do a fifth pass to connect probe listeners
1289364Snilay@cs.wisc.edu    for obj in root.descendants(): obj.regProbeListeners()
12910087Snilay@cs.wisc.edu
1309364Snilay@cs.wisc.edu    # We want to generate the DVFS diagram for the system. This can only be
1319364Snilay@cs.wisc.edu    # done once all of the CPP objects have been created and initialised so
1329364Snilay@cs.wisc.edu    # that we are able to figure out which object belongs to which domain.
13310087Snilay@cs.wisc.edu    if options.dot_dvfs_config:
1349996Snilay@cs.wisc.edu        do_dvfs_dot(root, options.outdir, options.dot_dvfs_config)
1359996Snilay@cs.wisc.edu
1369364Snilay@cs.wisc.edu    # We're done registering statistics.  Enable the stats package now.
13710005Snilay@cs.wisc.edu    stats.enable()
13810096Snilay@cs.wisc.edu
13910005Snilay@cs.wisc.edu    # Restore checkpoint (if any)
1409496Snilay@cs.wisc.edu    if ckpt_dir:
1419496Snilay@cs.wisc.edu        _drain_manager.preCheckpointRestore()
1429496Snilay@cs.wisc.edu        ckpt = _m5.core.getCheckpoint(ckpt_dir)
14310012Snilay@cs.wisc.edu        _m5.core.unserializeGlobals(ckpt);
1449497Snilay@cs.wisc.edu        for obj in root.descendants(): obj.loadState(ckpt)
1459497Snilay@cs.wisc.edu    else:
1469497Snilay@cs.wisc.edu        for obj in root.descendants(): obj.initState()
14710012Snilay@cs.wisc.edu
14810012Snilay@cs.wisc.edu    # Check to see if any of the stat events are in the past after resuming from
1499745Snilay@cs.wisc.edu    # a checkpoint, If so, this call will shift them to be at a valid time.
1509745Snilay@cs.wisc.edu    updateStatEvents()
1519745Snilay@cs.wisc.edu
1529745Snilay@cs.wisc.eduneed_startup = True
1539745Snilay@cs.wisc.edudef simulate(*args, **kwargs):
1549745Snilay@cs.wisc.edu    global need_startup
1559745Snilay@cs.wisc.edu
1569745Snilay@cs.wisc.edu    if need_startup:
1579745Snilay@cs.wisc.edu        root = objects.Root.getInstance()
1589745Snilay@cs.wisc.edu        for obj in root.descendants(): obj.startup()
15910012Snilay@cs.wisc.edu        need_startup = False
1609745Snilay@cs.wisc.edu
1619745Snilay@cs.wisc.edu        # Python exit handlers happen in reverse order.
1626285Snate@binkert.org        # We want to dump stats last.
1636285Snate@binkert.org        atexit.register(stats.dump)
1647039Snate@binkert.org
165        # register our C++ exit callback function with Python
166        atexit.register(_m5.core.doExitCleanup)
167
168        # Reset to put the stats in a consistent state.
169        stats.reset()
170
171    if _drain_manager.isDrained():
172        _drain_manager.resume()
173
174    return _m5.event.simulate(*args, **kwargs)
175
176def drain():
177    """Drain the simulator in preparation of a checkpoint or memory mode
178    switch.
179
180    This operation is a no-op if the simulator is already in the
181    Drained state.
182
183    """
184
185    # Try to drain all objects. Draining might not be completed unless
186    # all objects return that they are drained on the first call. This
187    # is because as objects drain they may cause other objects to no
188    # longer be drained.
189    def _drain():
190        # Try to drain the system. The drain is successful if all
191        # objects are done without simulation. We need to simulate
192        # more if not.
193        if _drain_manager.tryDrain():
194            return True
195
196        # WARNING: if a valid exit event occurs while draining, it
197        # will not get returned to the user script
198        exit_event = _m5.event.simulate()
199        while exit_event.getCause() != 'Finished drain':
200            exit_event = simulate()
201
202        return False
203
204    # Don't try to drain a system that is already drained
205    is_drained = _drain_manager.isDrained()
206    while not is_drained:
207        is_drained = _drain()
208
209    assert _drain_manager.isDrained(), "Drain state inconsistent"
210
211def memWriteback(root):
212    for obj in root.descendants():
213        obj.memWriteback()
214
215def memInvalidate(root):
216    for obj in root.descendants():
217        obj.memInvalidate()
218
219def checkpoint(dir):
220    root = objects.Root.getInstance()
221    if not isinstance(root, objects.Root):
222        raise TypeError, "Checkpoint must be called on a root object."
223
224    drain()
225    memWriteback(root)
226    print "Writing checkpoint"
227    _m5.core.serializeAll(dir)
228
229def _changeMemoryMode(system, mode):
230    if not isinstance(system, (objects.Root, objects.System)):
231        raise TypeError, "Parameter of type '%s'.  Must be type %s or %s." % \
232              (type(system), objects.Root, objects.System)
233    if system.getMemoryMode() != mode:
234        system.setMemoryMode(mode)
235    else:
236        print "System already in target mode. Memory mode unchanged."
237
238def switchCpus(system, cpuList, verbose=True):
239    """Switch CPUs in a system.
240
241    Note: This method may switch the memory mode of the system if that
242    is required by the CPUs. It may also flush all caches in the
243    system.
244
245    Arguments:
246      system -- Simulated system.
247      cpuList -- (old_cpu, new_cpu) tuples
248    """
249
250    if verbose:
251        print "switching cpus"
252
253    if not isinstance(cpuList, list):
254        raise RuntimeError, "Must pass a list to this function"
255    for item in cpuList:
256        if not isinstance(item, tuple) or len(item) != 2:
257            raise RuntimeError, "List must have tuples of (oldCPU,newCPU)"
258
259    old_cpus = [old_cpu for old_cpu, new_cpu in cpuList]
260    new_cpus = [new_cpu for old_cpu, new_cpu in cpuList]
261    old_cpu_set = set(old_cpus)
262    memory_mode_name = new_cpus[0].memory_mode()
263    for old_cpu, new_cpu in cpuList:
264        if not isinstance(old_cpu, objects.BaseCPU):
265            raise TypeError, "%s is not of type BaseCPU" % old_cpu
266        if not isinstance(new_cpu, objects.BaseCPU):
267            raise TypeError, "%s is not of type BaseCPU" % new_cpu
268        if new_cpu in old_cpu_set:
269            raise RuntimeError, \
270                "New CPU (%s) is in the list of old CPUs." % (old_cpu,)
271        if not new_cpu.switchedOut():
272            raise RuntimeError, \
273                "New CPU (%s) is already active." % (new_cpu,)
274        if not new_cpu.support_take_over():
275            raise RuntimeError, \
276                "New CPU (%s) does not support CPU handover." % (old_cpu,)
277        if new_cpu.memory_mode() != memory_mode_name:
278            raise RuntimeError, \
279                "%s and %s require different memory modes." % (new_cpu,
280                                                               new_cpus[0])
281        if old_cpu.switchedOut():
282            raise RuntimeError, \
283                "Old CPU (%s) is inactive." % (new_cpu,)
284        if not old_cpu.support_take_over():
285            raise RuntimeError, \
286                "Old CPU (%s) does not support CPU handover." % (old_cpu,)
287
288    try:
289        memory_mode = _memory_modes[memory_mode_name]
290    except KeyError:
291        raise RuntimeError, "Invalid memory mode (%s)" % memory_mode_name
292
293    drain()
294
295    # Now all of the CPUs are ready to be switched out
296    for old_cpu, new_cpu in cpuList:
297        old_cpu.switchOut()
298
299    # Change the memory mode if required. We check if this is needed
300    # to avoid printing a warning if no switch was performed.
301    if system.getMemoryMode() != memory_mode:
302        # Flush the memory system if we are switching to a memory mode
303        # that disables caches. This typically happens when switching to a
304        # hardware virtualized CPU.
305        if memory_mode == objects.params.atomic_noncaching:
306            memWriteback(system)
307            memInvalidate(system)
308
309        _changeMemoryMode(system, memory_mode)
310
311    for old_cpu, new_cpu in cpuList:
312        new_cpu.takeOverFrom(old_cpu)
313
314def notifyFork(root):
315    for obj in root.descendants():
316        obj.notifyFork()
317
318fork_count = 0
319def fork(simout="%(parent)s.f%(fork_seq)i"):
320    """Fork the simulator.
321
322    This function forks the simulator. After forking the simulator,
323    the child process gets its output files redirected to a new output
324    directory. The default name of the output directory is the same as
325    the parent with the suffix ".fN" added where N is the fork
326    sequence number. The name of the output directory can be
327    overridden using the simout keyword argument.
328
329    Output file formatting dictionary:
330      parent -- Path to the parent process's output directory.
331      fork_seq -- Fork sequence number.
332      pid -- PID of the child process.
333
334    Keyword Arguments:
335      simout -- New simulation output directory.
336
337    Return Value:
338      pid of the child process or 0 if running in the child.
339    """
340    from m5 import options
341    global fork_count
342
343    if not _m5.core.listenersDisabled():
344        raise RuntimeError, "Can not fork a simulator with listeners enabled"
345
346    drain()
347
348    try:
349        pid = os.fork()
350    except OSError, e:
351        raise e
352
353    if pid == 0:
354        # In child, notify objects of the fork
355        root = objects.Root.getInstance()
356        notifyFork(root)
357        # Setup a new output directory
358        parent = options.outdir
359        options.outdir = simout % {
360                "parent" : parent,
361                "fork_seq" : fork_count,
362                "pid" : os.getpid(),
363                }
364        _m5.core.setOutputDir(options.outdir)
365    else:
366        fork_count += 1
367
368    return pid
369
370from _m5.core import disableAllListeners, listenersDisabled
371from _m5.core import curTick
372