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