simulate.py revision 9521
1# Copyright (c) 2012 ARM Limited
2# All rights reserved.
3#
4# The license below extends only to copyright in the software and shall
5# not be construed as granting a license to any other intellectual
6# property including but not limited to intellectual property relating
7# to a hardware implementation of the functionality of the software
8# licensed hereunder.  You may use the software subject to the license
9# terms below provided that you ensure that this notice is replicated
10# unmodified and in its entirety in all distributions of the software,
11# modified or unmodified, in source code or in binary form.
12#
13# Copyright (c) 2005 The Regents of The University of Michigan
14# Copyright (c) 2010 Advanced Micro Devices, Inc.
15# All rights reserved.
16#
17# Redistribution and use in source and binary forms, with or without
18# modification, are permitted provided that the following conditions are
19# met: redistributions of source code must retain the above copyright
20# notice, this list of conditions and the following disclaimer;
21# redistributions in binary form must reproduce the above copyright
22# notice, this list of conditions and the following disclaimer in the
23# documentation and/or other materials provided with the distribution;
24# neither the name of the copyright holders nor the names of its
25# contributors may be used to endorse or promote products derived from
26# this software without specific prior written permission.
27#
28# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
29# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
30# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
31# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
32# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
33# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
34# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
35# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
36# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
37# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
38# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
39#
40# Authors: Nathan Binkert
41#          Steve Reinhardt
42
43import atexit
44import os
45import sys
46
47# import the SWIG-wrapped main C++ functions
48import internal
49import core
50import stats
51import SimObject
52import ticks
53import objects
54from m5.util.dot_writer import do_dot
55from m5.internal.stats import updateEvents as updateStatEvents
56
57from util import fatal
58from util import attrdict
59
60# define a MaxTick parameter
61MaxTick = 2**63 - 1
62
63_memory_modes = {
64    "atomic" : objects.params.atomic,
65    "timing" : objects.params.timing,
66    }
67
68# The final hook to generate .ini files.  Called from the user script
69# once the config is built.
70def instantiate(ckpt_dir=None):
71    from m5 import options
72
73    root = objects.Root.getInstance()
74
75    if not root:
76        fatal("Need to instantiate Root() before calling instantiate()")
77
78    # we need to fix the global frequency
79    ticks.fixGlobalFrequency()
80
81    # Make sure SimObject-valued params are in the configuration
82    # hierarchy so we catch them with future descendants() walks
83    for obj in root.descendants(): obj.adoptOrphanParams()
84
85    # Unproxy in sorted order for determinism
86    for obj in root.descendants(): obj.unproxyParams()
87
88    if options.dump_config:
89        ini_file = file(os.path.join(options.outdir, options.dump_config), 'w')
90        # Print ini sections in sorted order for easier diffing
91        for obj in sorted(root.descendants(), key=lambda o: o.path()):
92            obj.print_ini(ini_file)
93        ini_file.close()
94
95    if options.json_config:
96        try:
97            import json
98            json_file = file(os.path.join(options.outdir, options.json_config), 'w')
99            d = root.get_config_as_dict()
100            json.dump(d, json_file, indent=4)
101            json_file.close()
102        except ImportError:
103            pass
104
105    do_dot(root, options.outdir, options.dot_config)
106
107    # Initialize the global statistics
108    stats.initSimStats()
109
110    # Create the C++ sim objects and connect ports
111    for obj in root.descendants(): obj.createCCObject()
112    for obj in root.descendants(): obj.connectPorts()
113
114    # Do a second pass to finish initializing the sim objects
115    for obj in root.descendants(): obj.init()
116
117    # Do a third pass to initialize statistics
118    for obj in root.descendants(): obj.regStats()
119
120    # We're done registering statistics.  Enable the stats package now.
121    stats.enable()
122
123    # Restore checkpoint (if any)
124    if ckpt_dir:
125        ckpt = internal.core.getCheckpoint(ckpt_dir)
126        internal.core.unserializeGlobals(ckpt);
127        for obj in root.descendants(): obj.loadState(ckpt)
128        need_resume.append(root)
129    else:
130        for obj in root.descendants(): obj.initState()
131
132    # Check to see if any of the stat events are in the past after resuming from
133    # a checkpoint, If so, this call will shift them to be at a valid time.
134    updateStatEvents()
135
136    # Reset to put the stats in a consistent state.
137    stats.reset()
138
139need_resume = []
140need_startup = True
141def simulate(*args, **kwargs):
142    global need_resume, need_startup
143
144    if need_startup:
145        root = objects.Root.getInstance()
146        for obj in root.descendants(): obj.startup()
147        need_startup = False
148
149    for root in need_resume:
150        resume(root)
151    need_resume = []
152
153    return internal.event.simulate(*args, **kwargs)
154
155# Export curTick to user script.
156def curTick():
157    return internal.core.curTick()
158
159# Python exit handlers happen in reverse order.  We want to dump stats last.
160atexit.register(stats.dump)
161
162# register our C++ exit callback function with Python
163atexit.register(internal.core.doExitCleanup)
164
165# Drain the system in preparation of a checkpoint or memory mode
166# switch.
167def drain(root):
168    # Try to drain all objects. Draining might not be completed unless
169    # all objects return that they are drained on the first call. This
170    # is because as objects drain they may cause other objects to no
171    # longer be drained.
172    def _drain():
173        all_drained = False
174        dm = internal.drain.createDrainManager()
175        unready_objs = sum(obj.drain(dm) for obj in root.descendants())
176        # If we've got some objects that can't drain immediately, then simulate
177        if unready_objs > 0:
178            dm.setCount(unready_objs)
179            simulate()
180        else:
181            all_drained = True
182        internal.drain.cleanupDrainManager(dm)
183        return all_drained
184
185    all_drained = _drain()
186    while (not all_drained):
187        all_drained = _drain()
188
189def memWriteback(root):
190    for obj in root.descendants():
191        obj.memWriteback()
192
193def memInvalidate(root):
194    for obj in root.descendants():
195        obj.memInvalidate()
196
197def resume(root):
198    for obj in root.descendants(): obj.drainResume()
199
200def checkpoint(dir):
201    root = objects.Root.getInstance()
202    if not isinstance(root, objects.Root):
203        raise TypeError, "Checkpoint must be called on a root object."
204    drain(root)
205    memWriteback(root)
206    print "Writing checkpoint"
207    internal.core.serializeAll(dir)
208    resume(root)
209
210def _changeMemoryMode(system, mode):
211    if not isinstance(system, (objects.Root, objects.System)):
212        raise TypeError, "Parameter of type '%s'.  Must be type %s or %s." % \
213              (type(system), objects.Root, objects.System)
214    if system.getMemoryMode() != mode:
215        drain(system)
216        system.setMemoryMode(mode)
217    else:
218        print "System already in target mode. Memory mode unchanged."
219
220def switchCpus(system, cpuList, do_drain=True):
221    """Switch CPUs in a system.
222
223    By default, this method drains and resumes the system. This
224    behavior can be disabled by setting the keyword argument
225    'do_drain' to false, which might be desirable if multiple
226    operations requiring a drained system are going to be performed in
227    sequence.
228
229    Note: This method may switch the memory mode of the system if that
230    is required by the CPUs. It may also flush all caches in the
231    system.
232
233    Arguments:
234      system -- Simulated system.
235      cpuList -- (old_cpu, new_cpu) tuples
236
237    Keyword Arguments:
238      do_drain -- Perform a drain/resume of the system when switching.
239    """
240    print "switching cpus"
241    if not isinstance(cpuList, list):
242        raise RuntimeError, "Must pass a list to this function"
243    for item in cpuList:
244        if not isinstance(item, tuple) or len(item) != 2:
245            raise RuntimeError, "List must have tuples of (oldCPU,newCPU)"
246
247    old_cpus = [old_cpu for old_cpu, new_cpu in cpuList]
248    new_cpus = [new_cpu for old_cpu, new_cpu in cpuList]
249    old_cpu_set = set(old_cpus)
250    memory_mode_name = new_cpus[0].memory_mode()
251    for old_cpu, new_cpu in cpuList:
252        if not isinstance(old_cpu, objects.BaseCPU):
253            raise TypeError, "%s is not of type BaseCPU" % old_cpu
254        if not isinstance(new_cpu, objects.BaseCPU):
255            raise TypeError, "%s is not of type BaseCPU" % new_cpu
256        if new_cpu in old_cpu_set:
257            raise RuntimeError, \
258                "New CPU (%s) is in the list of old CPUs." % (old_cpu,)
259        if not new_cpu.switchedOut():
260            raise RuntimeError, \
261                "New CPU (%s) is already active." % (new_cpu,)
262        if not new_cpu.support_take_over():
263            raise RuntimeError, \
264                "New CPU (%s) does not support CPU handover." % (old_cpu,)
265        if new_cpu.memory_mode() != memory_mode_name:
266            raise RuntimeError, \
267                "%s and %s require different memory modes." % (new_cpu,
268                                                               new_cpus[0])
269        if old_cpu.switchedOut():
270            raise RuntimeError, \
271                "Old CPU (%s) is inactive." % (new_cpu,)
272        if not old_cpu.support_take_over():
273            raise RuntimeError, \
274                "Old CPU (%s) does not support CPU handover." % (old_cpu,)
275
276    try:
277        memory_mode = _memory_modes[memory_mode_name]
278    except KeyError:
279        raise RuntimeError, "Invalid memory mode (%s)" % memory_mode_name
280
281    if do_drain:
282        drain(system)
283
284    # Now all of the CPUs are ready to be switched out
285    for old_cpu, new_cpu in cpuList:
286        old_cpu.switchOut()
287
288    # Change the memory mode if required. We check if this is needed
289    # to avoid printing a warning if no switch was performed.
290    if system.getMemoryMode() != memory_mode:
291        _changeMemoryMode(system, memory_mode)
292
293    for old_cpu, new_cpu in cpuList:
294        new_cpu.takeOverFrom(old_cpu)
295
296    if do_drain:
297        resume(system)
298
299from internal.core import disableAllListeners
300