simulate.py revision 9980:cc02ad629b36
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, unsigned 64 bit
61MaxTick = 2**64 - 1
62
63_memory_modes = {
64    "atomic" : objects.params.atomic,
65    "timing" : objects.params.timing,
66    "atomic_noncaching" : objects.params.atomic_noncaching,
67    }
68
69# The final hook to generate .ini files.  Called from the user script
70# once the config is built.
71def instantiate(ckpt_dir=None):
72    from m5 import options
73
74    root = objects.Root.getInstance()
75
76    if not root:
77        fatal("Need to instantiate Root() before calling instantiate()")
78
79    # we need to fix the global frequency
80    ticks.fixGlobalFrequency()
81
82    # Make sure SimObject-valued params are in the configuration
83    # hierarchy so we catch them with future descendants() walks
84    for obj in root.descendants(): obj.adoptOrphanParams()
85
86    # Unproxy in sorted order for determinism
87    for obj in root.descendants(): obj.unproxyParams()
88
89    if options.dump_config:
90        ini_file = file(os.path.join(options.outdir, options.dump_config), 'w')
91        # Print ini sections in sorted order for easier diffing
92        for obj in sorted(root.descendants(), key=lambda o: o.path()):
93            obj.print_ini(ini_file)
94        ini_file.close()
95
96    if options.json_config:
97        try:
98            import json
99            json_file = file(os.path.join(options.outdir, options.json_config), 'w')
100            d = root.get_config_as_dict()
101            json.dump(d, json_file, indent=4)
102            json_file.close()
103        except ImportError:
104            pass
105
106    do_dot(root, options.outdir, options.dot_config)
107
108    # Initialize the global statistics
109    stats.initSimStats()
110
111    # Create the C++ sim objects and connect ports
112    for obj in root.descendants(): obj.createCCObject()
113    for obj in root.descendants(): obj.connectPorts()
114
115    # Do a second pass to finish initializing the sim objects
116    for obj in root.descendants(): obj.init()
117
118    # Do a third pass to initialize statistics
119    for obj in root.descendants(): obj.regStats()
120
121    # We're done registering statistics.  Enable the stats package now.
122    stats.enable()
123
124    # Restore checkpoint (if any)
125    if ckpt_dir:
126        ckpt = internal.core.getCheckpoint(ckpt_dir)
127        internal.core.unserializeGlobals(ckpt);
128        for obj in root.descendants(): obj.loadState(ckpt)
129        need_resume.append(root)
130    else:
131        for obj in root.descendants(): obj.initState()
132
133    # Check to see if any of the stat events are in the past after resuming from
134    # a checkpoint, If so, this call will shift them to be at a valid time.
135    updateStatEvents()
136
137    # Reset to put the stats in a consistent state.
138    stats.reset()
139
140need_resume = []
141need_startup = True
142def simulate(*args, **kwargs):
143    global need_resume, need_startup
144
145    if need_startup:
146        root = objects.Root.getInstance()
147        for obj in root.descendants(): obj.startup()
148        need_startup = False
149
150    for root in need_resume:
151        resume(root)
152    need_resume = []
153
154    return internal.event.simulate(*args, **kwargs)
155
156# Export curTick to user script.
157def curTick():
158    return internal.core.curTick()
159
160# Python exit handlers happen in reverse order.  We want to dump stats last.
161atexit.register(stats.dump)
162
163# register our C++ exit callback function with Python
164atexit.register(internal.core.doExitCleanup)
165
166# Drain the system in preparation of a checkpoint or memory mode
167# switch.
168def drain(root):
169    # Try to drain all objects. Draining might not be completed unless
170    # all objects return that they are drained on the first call. This
171    # is because as objects drain they may cause other objects to no
172    # longer be drained.
173    def _drain():
174        all_drained = False
175        dm = internal.drain.createDrainManager()
176        unready_objs = sum(obj.drain(dm) for obj in root.descendants())
177        # If we've got some objects that can't drain immediately, then simulate
178        if unready_objs > 0:
179            dm.setCount(unready_objs)
180            simulate()
181        else:
182            all_drained = True
183        internal.drain.cleanupDrainManager(dm)
184        return all_drained
185
186    all_drained = _drain()
187    while (not all_drained):
188        all_drained = _drain()
189
190def memWriteback(root):
191    for obj in root.descendants():
192        obj.memWriteback()
193
194def memInvalidate(root):
195    for obj in root.descendants():
196        obj.memInvalidate()
197
198def resume(root):
199    for obj in root.descendants(): obj.drainResume()
200
201def checkpoint(dir):
202    root = objects.Root.getInstance()
203    if not isinstance(root, objects.Root):
204        raise TypeError, "Checkpoint must be called on a root object."
205    drain(root)
206    memWriteback(root)
207    print "Writing checkpoint"
208    internal.core.serializeAll(dir)
209    resume(root)
210
211def _changeMemoryMode(system, mode):
212    if not isinstance(system, (objects.Root, objects.System)):
213        raise TypeError, "Parameter of type '%s'.  Must be type %s or %s." % \
214              (type(system), objects.Root, objects.System)
215    if system.getMemoryMode() != mode:
216        drain(system)
217        system.setMemoryMode(mode)
218    else:
219        print "System already in target mode. Memory mode unchanged."
220
221def switchCpus(system, cpuList, do_drain=True, verbose=True):
222    """Switch CPUs in a system.
223
224    By default, this method drains and resumes the system. This
225    behavior can be disabled by setting the keyword argument
226    'do_drain' to false, which might be desirable if multiple
227    operations requiring a drained system are going to be performed in
228    sequence.
229
230    Note: This method may switch the memory mode of the system if that
231    is required by the CPUs. It may also flush all caches in the
232    system.
233
234    Arguments:
235      system -- Simulated system.
236      cpuList -- (old_cpu, new_cpu) tuples
237
238    Keyword Arguments:
239      do_drain -- Perform a drain/resume of the system when switching.
240    """
241
242    if verbose:
243        print "switching cpus"
244
245    if not isinstance(cpuList, list):
246        raise RuntimeError, "Must pass a list to this function"
247    for item in cpuList:
248        if not isinstance(item, tuple) or len(item) != 2:
249            raise RuntimeError, "List must have tuples of (oldCPU,newCPU)"
250
251    old_cpus = [old_cpu for old_cpu, new_cpu in cpuList]
252    new_cpus = [new_cpu for old_cpu, new_cpu in cpuList]
253    old_cpu_set = set(old_cpus)
254    memory_mode_name = new_cpus[0].memory_mode()
255    for old_cpu, new_cpu in cpuList:
256        if not isinstance(old_cpu, objects.BaseCPU):
257            raise TypeError, "%s is not of type BaseCPU" % old_cpu
258        if not isinstance(new_cpu, objects.BaseCPU):
259            raise TypeError, "%s is not of type BaseCPU" % new_cpu
260        if new_cpu in old_cpu_set:
261            raise RuntimeError, \
262                "New CPU (%s) is in the list of old CPUs." % (old_cpu,)
263        if not new_cpu.switchedOut():
264            raise RuntimeError, \
265                "New CPU (%s) is already active." % (new_cpu,)
266        if not new_cpu.support_take_over():
267            raise RuntimeError, \
268                "New CPU (%s) does not support CPU handover." % (old_cpu,)
269        if new_cpu.memory_mode() != memory_mode_name:
270            raise RuntimeError, \
271                "%s and %s require different memory modes." % (new_cpu,
272                                                               new_cpus[0])
273        if old_cpu.switchedOut():
274            raise RuntimeError, \
275                "Old CPU (%s) is inactive." % (new_cpu,)
276        if not old_cpu.support_take_over():
277            raise RuntimeError, \
278                "Old CPU (%s) does not support CPU handover." % (old_cpu,)
279
280    try:
281        memory_mode = _memory_modes[memory_mode_name]
282    except KeyError:
283        raise RuntimeError, "Invalid memory mode (%s)" % memory_mode_name
284
285    if do_drain:
286        drain(system)
287
288    # Now all of the CPUs are ready to be switched out
289    for old_cpu, new_cpu in cpuList:
290        old_cpu.switchOut()
291
292    # Change the memory mode if required. We check if this is needed
293    # to avoid printing a warning if no switch was performed.
294    if system.getMemoryMode() != memory_mode:
295        # Flush the memory system if we are switching to a memory mode
296        # that disables caches. This typically happens when switching to a
297        # hardware virtualized CPU.
298        if memory_mode == objects.params.atomic_noncaching:
299            memWriteback(system)
300            memInvalidate(system)
301
302        _changeMemoryMode(system, memory_mode)
303
304    for old_cpu, new_cpu in cpuList:
305        new_cpu.takeOverFrom(old_cpu)
306
307    if do_drain:
308        resume(system)
309
310from internal.core import disableAllListeners
311