simulate.py (13714:35636064b7a1) simulate.py (13992:05f4102a536f)
1# Copyright (c) 2012 ARM Limited
1# Copyright (c) 2012,2019 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
43from __future__ import print_function
44
45import atexit
46import os
47import sys
48
49# import the wrapped C++ functions
50import _m5.drain
51import _m5.core
52from _m5.stats import updateEvents as updateStatEvents
53
54from . import stats
55from . import SimObject
56from . import ticks
57from . import objects
58from m5.util.dot_writer import do_dot, do_dvfs_dot
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
43from __future__ import print_function
44
45import atexit
46import os
47import sys
48
49# import the wrapped C++ functions
50import _m5.drain
51import _m5.core
52from _m5.stats import updateEvents as updateStatEvents
53
54from . import stats
55from . import SimObject
56from . import ticks
57from . import objects
58from m5.util.dot_writer import do_dot, do_dvfs_dot
59from m5.util.dot_writer_ruby import do_ruby_dot
59
60from .util import fatal
61from .util import attrdict
62
63# define a MaxTick parameter, unsigned 64 bit
64MaxTick = 2**64 - 1
65
66_memory_modes = {
67 "atomic" : objects.params.atomic,
68 "timing" : objects.params.timing,
69 "atomic_noncaching" : objects.params.atomic_noncaching,
70 }
71
72_drain_manager = _m5.drain.DrainManager.instance()
73
74# The final hook to generate .ini files. Called from the user script
75# once the config is built.
76def instantiate(ckpt_dir=None):
77 from m5 import options
78
79 root = objects.Root.getInstance()
80
81 if not root:
82 fatal("Need to instantiate Root() before calling instantiate()")
83
84 # we need to fix the global frequency
85 ticks.fixGlobalFrequency()
86
87 # Make sure SimObject-valued params are in the configuration
88 # hierarchy so we catch them with future descendants() walks
89 for obj in root.descendants(): obj.adoptOrphanParams()
90
91 # Unproxy in sorted order for determinism
92 for obj in root.descendants(): obj.unproxyParams()
93
94 if options.dump_config:
95 ini_file = open(os.path.join(options.outdir, options.dump_config), 'w')
96 # Print ini sections in sorted order for easier diffing
97 for obj in sorted(root.descendants(), key=lambda o: o.path()):
98 obj.print_ini(ini_file)
99 ini_file.close()
100
101 if options.json_config:
102 try:
103 import json
104 json_file = open(
105 os.path.join(options.outdir, options.json_config), 'w')
106 d = root.get_config_as_dict()
107 json.dump(d, json_file, indent=4)
108 json_file.close()
109 except ImportError:
110 pass
111
112 do_dot(root, options.outdir, options.dot_config)
60
61from .util import fatal
62from .util import attrdict
63
64# define a MaxTick parameter, unsigned 64 bit
65MaxTick = 2**64 - 1
66
67_memory_modes = {
68 "atomic" : objects.params.atomic,
69 "timing" : objects.params.timing,
70 "atomic_noncaching" : objects.params.atomic_noncaching,
71 }
72
73_drain_manager = _m5.drain.DrainManager.instance()
74
75# The final hook to generate .ini files. Called from the user script
76# once the config is built.
77def instantiate(ckpt_dir=None):
78 from m5 import options
79
80 root = objects.Root.getInstance()
81
82 if not root:
83 fatal("Need to instantiate Root() before calling instantiate()")
84
85 # we need to fix the global frequency
86 ticks.fixGlobalFrequency()
87
88 # Make sure SimObject-valued params are in the configuration
89 # hierarchy so we catch them with future descendants() walks
90 for obj in root.descendants(): obj.adoptOrphanParams()
91
92 # Unproxy in sorted order for determinism
93 for obj in root.descendants(): obj.unproxyParams()
94
95 if options.dump_config:
96 ini_file = open(os.path.join(options.outdir, options.dump_config), 'w')
97 # Print ini sections in sorted order for easier diffing
98 for obj in sorted(root.descendants(), key=lambda o: o.path()):
99 obj.print_ini(ini_file)
100 ini_file.close()
101
102 if options.json_config:
103 try:
104 import json
105 json_file = open(
106 os.path.join(options.outdir, options.json_config), 'w')
107 d = root.get_config_as_dict()
108 json.dump(d, json_file, indent=4)
109 json_file.close()
110 except ImportError:
111 pass
112
113 do_dot(root, options.outdir, options.dot_config)
114 do_ruby_dot(root, options.outdir, options.dot_config)
113
114 # Initialize the global statistics
115 stats.initSimStats()
116
117 # Create the C++ sim objects and connect ports
118 for obj in root.descendants(): obj.createCCObject()
119 for obj in root.descendants(): obj.connectPorts()
120
121 # Do a second pass to finish initializing the sim objects
122 for obj in root.descendants(): obj.init()
123
124 # Do a third pass to initialize statistics
125 for obj in root.descendants(): obj.regStats()
126
127 # Do a fourth pass to initialize probe points
128 for obj in root.descendants(): obj.regProbePoints()
129
130 # Do a fifth pass to connect probe listeners
131 for obj in root.descendants(): obj.regProbeListeners()
132
133 # We want to generate the DVFS diagram for the system. This can only be
134 # done once all of the CPP objects have been created and initialised so
135 # that we are able to figure out which object belongs to which domain.
136 if options.dot_dvfs_config:
137 do_dvfs_dot(root, options.outdir, options.dot_dvfs_config)
138
139 # We're done registering statistics. Enable the stats package now.
140 stats.enable()
141
142 # Restore checkpoint (if any)
143 if ckpt_dir:
144 _drain_manager.preCheckpointRestore()
145 ckpt = _m5.core.getCheckpoint(ckpt_dir)
146 _m5.core.unserializeGlobals(ckpt);
147 for obj in root.descendants(): obj.loadState(ckpt)
148 else:
149 for obj in root.descendants(): obj.initState()
150
151 # Check to see if any of the stat events are in the past after resuming from
152 # a checkpoint, If so, this call will shift them to be at a valid time.
153 updateStatEvents()
154
155need_startup = True
156def simulate(*args, **kwargs):
157 global need_startup
158
159 if need_startup:
160 root = objects.Root.getInstance()
161 for obj in root.descendants(): obj.startup()
162 need_startup = False
163
164 # Python exit handlers happen in reverse order.
165 # We want to dump stats last.
166 atexit.register(stats.dump)
167
168 # register our C++ exit callback function with Python
169 atexit.register(_m5.core.doExitCleanup)
170
171 # Reset to put the stats in a consistent state.
172 stats.reset()
173
174 if _drain_manager.isDrained():
175 _drain_manager.resume()
176
177 return _m5.event.simulate(*args, **kwargs)
178
179def drain():
180 """Drain the simulator in preparation of a checkpoint or memory mode
181 switch.
182
183 This operation is a no-op if the simulator is already in the
184 Drained state.
185
186 """
187
188 # Try to drain all objects. Draining might not be completed unless
189 # all objects return that they are drained on the first call. This
190 # is because as objects drain they may cause other objects to no
191 # longer be drained.
192 def _drain():
193 # Try to drain the system. The drain is successful if all
194 # objects are done without simulation. We need to simulate
195 # more if not.
196 if _drain_manager.tryDrain():
197 return True
198
199 # WARNING: if a valid exit event occurs while draining, it
200 # will not get returned to the user script
201 exit_event = _m5.event.simulate()
202 while exit_event.getCause() != 'Finished drain':
203 exit_event = simulate()
204
205 return False
206
207 # Don't try to drain a system that is already drained
208 is_drained = _drain_manager.isDrained()
209 while not is_drained:
210 is_drained = _drain()
211
212 assert _drain_manager.isDrained(), "Drain state inconsistent"
213
214def memWriteback(root):
215 for obj in root.descendants():
216 obj.memWriteback()
217
218def memInvalidate(root):
219 for obj in root.descendants():
220 obj.memInvalidate()
221
222def checkpoint(dir):
223 root = objects.Root.getInstance()
224 if not isinstance(root, objects.Root):
225 raise TypeError("Checkpoint must be called on a root object.")
226
227 drain()
228 memWriteback(root)
229 print("Writing checkpoint")
230 _m5.core.serializeAll(dir)
231
232def _changeMemoryMode(system, mode):
233 if not isinstance(system, (objects.Root, objects.System)):
234 raise TypeError("Parameter of type '%s'. Must be type %s or %s." % \
235 (type(system), objects.Root, objects.System))
236 if system.getMemoryMode() != mode:
237 system.setMemoryMode(mode)
238 else:
239 print("System already in target mode. Memory mode unchanged.")
240
241def switchCpus(system, cpuList, verbose=True):
242 """Switch CPUs in a system.
243
244 Note: This method may switch the memory mode of the system if that
245 is required by the CPUs. It may also flush all caches in the
246 system.
247
248 Arguments:
249 system -- Simulated system.
250 cpuList -- (old_cpu, new_cpu) tuples
251 """
252
253 if verbose:
254 print("switching cpus")
255
256 if not isinstance(cpuList, list):
257 raise RuntimeError("Must pass a list to this function")
258 for item in cpuList:
259 if not isinstance(item, tuple) or len(item) != 2:
260 raise RuntimeError("List must have tuples of (oldCPU,newCPU)")
261
262 old_cpus = [old_cpu for old_cpu, new_cpu in cpuList]
263 new_cpus = [new_cpu for old_cpu, new_cpu in cpuList]
264 old_cpu_set = set(old_cpus)
265 memory_mode_name = new_cpus[0].memory_mode()
266 for old_cpu, new_cpu in cpuList:
267 if not isinstance(old_cpu, objects.BaseCPU):
268 raise TypeError("%s is not of type BaseCPU" % old_cpu)
269 if not isinstance(new_cpu, objects.BaseCPU):
270 raise TypeError("%s is not of type BaseCPU" % new_cpu)
271 if new_cpu in old_cpu_set:
272 raise RuntimeError(
273 "New CPU (%s) is in the list of old CPUs." % (old_cpu,))
274 if not new_cpu.switchedOut():
275 raise RuntimeError("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("Old CPU (%s) is inactive." % (new_cpu,))
285 if not old_cpu.support_take_over():
286 raise RuntimeError(
287 "Old CPU (%s) does not support CPU handover." % (old_cpu,))
288
289 try:
290 memory_mode = _memory_modes[memory_mode_name]
291 except KeyError:
292 raise RuntimeError("Invalid memory mode (%s)" % memory_mode_name)
293
294 drain()
295
296 # Now all of the CPUs are ready to be switched out
297 for old_cpu, new_cpu in cpuList:
298 old_cpu.switchOut()
299
300 # Change the memory mode if required. We check if this is needed
301 # to avoid printing a warning if no switch was performed.
302 if system.getMemoryMode() != memory_mode:
303 # Flush the memory system if we are switching to a memory mode
304 # that disables caches. This typically happens when switching to a
305 # hardware virtualized CPU.
306 if memory_mode == objects.params.atomic_noncaching:
307 memWriteback(system)
308 memInvalidate(system)
309
310 _changeMemoryMode(system, memory_mode)
311
312 for old_cpu, new_cpu in cpuList:
313 new_cpu.takeOverFrom(old_cpu)
314
315def notifyFork(root):
316 for obj in root.descendants():
317 obj.notifyFork()
318
319fork_count = 0
320def fork(simout="%(parent)s.f%(fork_seq)i"):
321 """Fork the simulator.
322
323 This function forks the simulator. After forking the simulator,
324 the child process gets its output files redirected to a new output
325 directory. The default name of the output directory is the same as
326 the parent with the suffix ".fN" added where N is the fork
327 sequence number. The name of the output directory can be
328 overridden using the simout keyword argument.
329
330 Output file formatting dictionary:
331 parent -- Path to the parent process's output directory.
332 fork_seq -- Fork sequence number.
333 pid -- PID of the child process.
334
335 Keyword Arguments:
336 simout -- New simulation output directory.
337
338 Return Value:
339 pid of the child process or 0 if running in the child.
340 """
341 from m5 import options
342 global fork_count
343
344 if not _m5.core.listenersDisabled():
345 raise RuntimeError("Can not fork a simulator with listeners enabled")
346
347 drain()
348
349 try:
350 pid = os.fork()
351 except OSError as e:
352 raise e
353
354 if pid == 0:
355 # In child, notify objects of the fork
356 root = objects.Root.getInstance()
357 notifyFork(root)
358 # Setup a new output directory
359 parent = options.outdir
360 options.outdir = simout % {
361 "parent" : parent,
362 "fork_seq" : fork_count,
363 "pid" : os.getpid(),
364 }
365 _m5.core.setOutputDir(options.outdir)
366 else:
367 fork_count += 1
368
369 return pid
370
371from _m5.core import disableAllListeners, listenersDisabled
372from _m5.core import listenersLoopbackOnly
373from _m5.core import curTick
115
116 # Initialize the global statistics
117 stats.initSimStats()
118
119 # Create the C++ sim objects and connect ports
120 for obj in root.descendants(): obj.createCCObject()
121 for obj in root.descendants(): obj.connectPorts()
122
123 # Do a second pass to finish initializing the sim objects
124 for obj in root.descendants(): obj.init()
125
126 # Do a third pass to initialize statistics
127 for obj in root.descendants(): obj.regStats()
128
129 # Do a fourth pass to initialize probe points
130 for obj in root.descendants(): obj.regProbePoints()
131
132 # Do a fifth pass to connect probe listeners
133 for obj in root.descendants(): obj.regProbeListeners()
134
135 # We want to generate the DVFS diagram for the system. This can only be
136 # done once all of the CPP objects have been created and initialised so
137 # that we are able to figure out which object belongs to which domain.
138 if options.dot_dvfs_config:
139 do_dvfs_dot(root, options.outdir, options.dot_dvfs_config)
140
141 # We're done registering statistics. Enable the stats package now.
142 stats.enable()
143
144 # Restore checkpoint (if any)
145 if ckpt_dir:
146 _drain_manager.preCheckpointRestore()
147 ckpt = _m5.core.getCheckpoint(ckpt_dir)
148 _m5.core.unserializeGlobals(ckpt);
149 for obj in root.descendants(): obj.loadState(ckpt)
150 else:
151 for obj in root.descendants(): obj.initState()
152
153 # Check to see if any of the stat events are in the past after resuming from
154 # a checkpoint, If so, this call will shift them to be at a valid time.
155 updateStatEvents()
156
157need_startup = True
158def simulate(*args, **kwargs):
159 global need_startup
160
161 if need_startup:
162 root = objects.Root.getInstance()
163 for obj in root.descendants(): obj.startup()
164 need_startup = False
165
166 # Python exit handlers happen in reverse order.
167 # We want to dump stats last.
168 atexit.register(stats.dump)
169
170 # register our C++ exit callback function with Python
171 atexit.register(_m5.core.doExitCleanup)
172
173 # Reset to put the stats in a consistent state.
174 stats.reset()
175
176 if _drain_manager.isDrained():
177 _drain_manager.resume()
178
179 return _m5.event.simulate(*args, **kwargs)
180
181def drain():
182 """Drain the simulator in preparation of a checkpoint or memory mode
183 switch.
184
185 This operation is a no-op if the simulator is already in the
186 Drained state.
187
188 """
189
190 # Try to drain all objects. Draining might not be completed unless
191 # all objects return that they are drained on the first call. This
192 # is because as objects drain they may cause other objects to no
193 # longer be drained.
194 def _drain():
195 # Try to drain the system. The drain is successful if all
196 # objects are done without simulation. We need to simulate
197 # more if not.
198 if _drain_manager.tryDrain():
199 return True
200
201 # WARNING: if a valid exit event occurs while draining, it
202 # will not get returned to the user script
203 exit_event = _m5.event.simulate()
204 while exit_event.getCause() != 'Finished drain':
205 exit_event = simulate()
206
207 return False
208
209 # Don't try to drain a system that is already drained
210 is_drained = _drain_manager.isDrained()
211 while not is_drained:
212 is_drained = _drain()
213
214 assert _drain_manager.isDrained(), "Drain state inconsistent"
215
216def memWriteback(root):
217 for obj in root.descendants():
218 obj.memWriteback()
219
220def memInvalidate(root):
221 for obj in root.descendants():
222 obj.memInvalidate()
223
224def checkpoint(dir):
225 root = objects.Root.getInstance()
226 if not isinstance(root, objects.Root):
227 raise TypeError("Checkpoint must be called on a root object.")
228
229 drain()
230 memWriteback(root)
231 print("Writing checkpoint")
232 _m5.core.serializeAll(dir)
233
234def _changeMemoryMode(system, mode):
235 if not isinstance(system, (objects.Root, objects.System)):
236 raise TypeError("Parameter of type '%s'. Must be type %s or %s." % \
237 (type(system), objects.Root, objects.System))
238 if system.getMemoryMode() != mode:
239 system.setMemoryMode(mode)
240 else:
241 print("System already in target mode. Memory mode unchanged.")
242
243def switchCpus(system, cpuList, verbose=True):
244 """Switch CPUs in a system.
245
246 Note: This method may switch the memory mode of the system if that
247 is required by the CPUs. It may also flush all caches in the
248 system.
249
250 Arguments:
251 system -- Simulated system.
252 cpuList -- (old_cpu, new_cpu) tuples
253 """
254
255 if verbose:
256 print("switching cpus")
257
258 if not isinstance(cpuList, list):
259 raise RuntimeError("Must pass a list to this function")
260 for item in cpuList:
261 if not isinstance(item, tuple) or len(item) != 2:
262 raise RuntimeError("List must have tuples of (oldCPU,newCPU)")
263
264 old_cpus = [old_cpu for old_cpu, new_cpu in cpuList]
265 new_cpus = [new_cpu for old_cpu, new_cpu in cpuList]
266 old_cpu_set = set(old_cpus)
267 memory_mode_name = new_cpus[0].memory_mode()
268 for old_cpu, new_cpu in cpuList:
269 if not isinstance(old_cpu, objects.BaseCPU):
270 raise TypeError("%s is not of type BaseCPU" % old_cpu)
271 if not isinstance(new_cpu, objects.BaseCPU):
272 raise TypeError("%s is not of type BaseCPU" % new_cpu)
273 if new_cpu in old_cpu_set:
274 raise RuntimeError(
275 "New CPU (%s) is in the list of old CPUs." % (old_cpu,))
276 if not new_cpu.switchedOut():
277 raise RuntimeError("New CPU (%s) is already active." % (new_cpu,))
278 if not new_cpu.support_take_over():
279 raise RuntimeError(
280 "New CPU (%s) does not support CPU handover." % (old_cpu,))
281 if new_cpu.memory_mode() != memory_mode_name:
282 raise RuntimeError(
283 "%s and %s require different memory modes." % (new_cpu,
284 new_cpus[0]))
285 if old_cpu.switchedOut():
286 raise RuntimeError("Old CPU (%s) is inactive." % (new_cpu,))
287 if not old_cpu.support_take_over():
288 raise RuntimeError(
289 "Old CPU (%s) does not support CPU handover." % (old_cpu,))
290
291 try:
292 memory_mode = _memory_modes[memory_mode_name]
293 except KeyError:
294 raise RuntimeError("Invalid memory mode (%s)" % memory_mode_name)
295
296 drain()
297
298 # Now all of the CPUs are ready to be switched out
299 for old_cpu, new_cpu in cpuList:
300 old_cpu.switchOut()
301
302 # Change the memory mode if required. We check if this is needed
303 # to avoid printing a warning if no switch was performed.
304 if system.getMemoryMode() != memory_mode:
305 # Flush the memory system if we are switching to a memory mode
306 # that disables caches. This typically happens when switching to a
307 # hardware virtualized CPU.
308 if memory_mode == objects.params.atomic_noncaching:
309 memWriteback(system)
310 memInvalidate(system)
311
312 _changeMemoryMode(system, memory_mode)
313
314 for old_cpu, new_cpu in cpuList:
315 new_cpu.takeOverFrom(old_cpu)
316
317def notifyFork(root):
318 for obj in root.descendants():
319 obj.notifyFork()
320
321fork_count = 0
322def fork(simout="%(parent)s.f%(fork_seq)i"):
323 """Fork the simulator.
324
325 This function forks the simulator. After forking the simulator,
326 the child process gets its output files redirected to a new output
327 directory. The default name of the output directory is the same as
328 the parent with the suffix ".fN" added where N is the fork
329 sequence number. The name of the output directory can be
330 overridden using the simout keyword argument.
331
332 Output file formatting dictionary:
333 parent -- Path to the parent process's output directory.
334 fork_seq -- Fork sequence number.
335 pid -- PID of the child process.
336
337 Keyword Arguments:
338 simout -- New simulation output directory.
339
340 Return Value:
341 pid of the child process or 0 if running in the child.
342 """
343 from m5 import options
344 global fork_count
345
346 if not _m5.core.listenersDisabled():
347 raise RuntimeError("Can not fork a simulator with listeners enabled")
348
349 drain()
350
351 try:
352 pid = os.fork()
353 except OSError as e:
354 raise e
355
356 if pid == 0:
357 # In child, notify objects of the fork
358 root = objects.Root.getInstance()
359 notifyFork(root)
360 # Setup a new output directory
361 parent = options.outdir
362 options.outdir = simout % {
363 "parent" : parent,
364 "fork_seq" : fork_count,
365 "pid" : os.getpid(),
366 }
367 _m5.core.setOutputDir(options.outdir)
368 else:
369 fork_count += 1
370
371 return pid
372
373from _m5.core import disableAllListeners, listenersDisabled
374from _m5.core import listenersLoopbackOnly
375from _m5.core import curTick