1 # Copyright (c) 2012,2019 ARM Limited
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.
13 # Copyright (c) 2005 The Regents of The University of Michigan
14 # Copyright (c) 2010 Advanced Micro Devices, Inc.
15 # All rights reserved.
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.
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.
40 from __future__
import print_function
46 # import the wrapped C++ functions
49 from _m5
.stats
import updateEvents
as updateStatEvents
52 from . import SimObject
55 from m5
.util
.dot_writer
import do_dot
, do_dvfs_dot
56 from m5
.util
.dot_writer_ruby
import do_ruby_dot
58 from .util
import fatal
59 from .util
import attrdict
61 # define a MaxTick parameter, unsigned 64 bit
65 "atomic" : objects
.params
.atomic
,
66 "timing" : objects
.params
.timing
,
67 "atomic_noncaching" : objects
.params
.atomic_noncaching
,
70 _drain_manager
= _m5
.drain
.DrainManager
.instance()
72 # The final hook to generate .ini files. Called from the user script
73 # once the config is built.
74 def instantiate(ckpt_dir
=None):
75 from m5
import options
77 root
= objects
.Root
.getInstance()
80 fatal("Need to instantiate Root() before calling instantiate()")
82 # we need to fix the global frequency
83 ticks
.fixGlobalFrequency()
85 # Make sure SimObject-valued params are in the configuration
86 # hierarchy so we catch them with future descendants() walks
87 for obj
in root
.descendants(): obj
.adoptOrphanParams()
89 # Unproxy in sorted order for determinism
90 for obj
in root
.descendants(): obj
.unproxyParams()
92 if options
.dump_config
:
93 ini_file
= open(os
.path
.join(options
.outdir
, options
.dump_config
), 'w')
94 # Print ini sections in sorted order for easier diffing
95 for obj
in sorted(root
.descendants(), key
=lambda o
: o
.path()):
96 obj
.print_ini(ini_file
)
99 if options
.json_config
:
103 os
.path
.join(options
.outdir
, options
.json_config
), 'w')
104 d
= root
.get_config_as_dict()
105 json
.dump(d
, json_file
, indent
=4)
110 if options
.dot_config
:
111 do_dot(root
, options
.outdir
, options
.dot_config
)
112 do_ruby_dot(root
, options
.outdir
, options
.dot_config
)
114 # Initialize the global statistics
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()
121 # Do a second pass to finish initializing the sim objects
122 for obj
in root
.descendants(): obj
.init()
124 # Do a third pass to initialize statistics
125 stats
._bindStatHierarchy
(root
)
128 # Do a fourth pass to initialize probe points
129 for obj
in root
.descendants(): obj
.regProbePoints()
131 # Do a fifth pass to connect probe listeners
132 for obj
in root
.descendants(): obj
.regProbeListeners()
134 # We want to generate the DVFS diagram for the system. This can only be
135 # done once all of the CPP objects have been created and initialised so
136 # that we are able to figure out which object belongs to which domain.
137 if options
.dot_dvfs_config
:
138 do_dvfs_dot(root
, options
.outdir
, options
.dot_dvfs_config
)
140 # We're done registering statistics. Enable the stats package now.
143 # Restore checkpoint (if any)
145 _drain_manager
.preCheckpointRestore()
146 ckpt
= _m5
.core
.getCheckpoint(ckpt_dir
)
147 _m5
.core
.unserializeGlobals(ckpt
);
148 for obj
in root
.descendants(): obj
.loadState(ckpt
)
150 for obj
in root
.descendants(): obj
.initState()
152 # Check to see if any of the stat events are in the past after resuming from
153 # a checkpoint, If so, this call will shift them to be at a valid time.
157 def simulate(*args
, **kwargs
):
161 root
= objects
.Root
.getInstance()
162 for obj
in root
.descendants(): obj
.startup()
165 # Python exit handlers happen in reverse order.
166 # We want to dump stats last.
167 atexit
.register(stats
.dump
)
169 # register our C++ exit callback function with Python
170 atexit
.register(_m5
.core
.doExitCleanup
)
172 # Reset to put the stats in a consistent state.
175 if _drain_manager
.isDrained():
176 _drain_manager
.resume()
178 return _m5
.event
.simulate(*args
, **kwargs
)
181 """Drain the simulator in preparation of a checkpoint or memory mode
184 This operation is a no-op if the simulator is already in the
189 # Try to drain all objects. Draining might not be completed unless
190 # all objects return that they are drained on the first call. This
191 # is because as objects drain they may cause other objects to no
194 # Try to drain the system. The drain is successful if all
195 # objects are done without simulation. We need to simulate
197 if _drain_manager
.tryDrain():
200 # WARNING: if a valid exit event occurs while draining, it
201 # will not get returned to the user script
202 exit_event
= _m5
.event
.simulate()
203 while exit_event
.getCause() != 'Finished drain':
204 exit_event
= simulate()
208 # Don't try to drain a system that is already drained
209 is_drained
= _drain_manager
.isDrained()
210 while not is_drained
:
211 is_drained
= _drain()
213 assert _drain_manager
.isDrained(), "Drain state inconsistent"
215 def memWriteback(root
):
216 for obj
in root
.descendants():
219 def memInvalidate(root
):
220 for obj
in root
.descendants():
224 root
= objects
.Root
.getInstance()
225 if not isinstance(root
, objects
.Root
):
226 raise TypeError("Checkpoint must be called on a root object.")
230 print("Writing checkpoint")
231 _m5
.core
.serializeAll(dir)
233 def _changeMemoryMode(system
, mode
):
234 if not isinstance(system
, (objects
.Root
, objects
.System
)):
235 raise TypeError("Parameter of type '%s'. Must be type %s or %s." % \
236 (type(system
), objects
.Root
, objects
.System
))
237 if system
.getMemoryMode() != mode
:
238 system
.setMemoryMode(mode
)
240 print("System already in target mode. Memory mode unchanged.")
242 def switchCpus(system
, cpuList
, verbose
=True):
243 """Switch CPUs in a system.
245 Note: This method may switch the memory mode of the system if that
246 is required by the CPUs. It may also flush all caches in the
250 system -- Simulated system.
251 cpuList -- (old_cpu, new_cpu) tuples
255 print("switching cpus")
257 if not isinstance(cpuList
, list):
258 raise RuntimeError("Must pass a list to this function")
260 if not isinstance(item
, tuple) or len(item
) != 2:
261 raise RuntimeError("List must have tuples of (oldCPU,newCPU)")
263 old_cpus
= [old_cpu
for old_cpu
, new_cpu
in cpuList
]
264 new_cpus
= [new_cpu
for old_cpu
, new_cpu
in cpuList
]
265 old_cpu_set
= set(old_cpus
)
266 memory_mode_name
= new_cpus
[0].memory_mode()
267 for old_cpu
, new_cpu
in cpuList
:
268 if not isinstance(old_cpu
, objects
.BaseCPU
):
269 raise TypeError("%s is not of type BaseCPU" % old_cpu
)
270 if not isinstance(new_cpu
, objects
.BaseCPU
):
271 raise TypeError("%s is not of type BaseCPU" % new_cpu
)
272 if new_cpu
in old_cpu_set
:
274 "New CPU (%s) is in the list of old CPUs." % (old_cpu
,))
275 if not new_cpu
.switchedOut():
276 raise RuntimeError("New CPU (%s) is already active." % (new_cpu
,))
277 if not new_cpu
.support_take_over():
279 "New CPU (%s) does not support CPU handover." % (old_cpu
,))
280 if new_cpu
.memory_mode() != memory_mode_name
:
282 "%s and %s require different memory modes." % (new_cpu
,
284 if old_cpu
.switchedOut():
285 raise RuntimeError("Old CPU (%s) is inactive." % (new_cpu
,))
286 if not old_cpu
.support_take_over():
288 "Old CPU (%s) does not support CPU handover." % (old_cpu
,))
291 memory_mode
= _memory_modes
[memory_mode_name
]
293 raise RuntimeError("Invalid memory mode (%s)" % memory_mode_name
)
297 # Now all of the CPUs are ready to be switched out
298 for old_cpu
, new_cpu
in cpuList
:
301 # Change the memory mode if required. We check if this is needed
302 # to avoid printing a warning if no switch was performed.
303 if system
.getMemoryMode() != memory_mode
:
304 # Flush the memory system if we are switching to a memory mode
305 # that disables caches. This typically happens when switching to a
306 # hardware virtualized CPU.
307 if memory_mode
== objects
.params
.atomic_noncaching
:
309 memInvalidate(system
)
311 _changeMemoryMode(system
, memory_mode
)
313 for old_cpu
, new_cpu
in cpuList
:
314 new_cpu
.takeOverFrom(old_cpu
)
316 def notifyFork(root
):
317 for obj
in root
.descendants():
321 def fork(simout
="%(parent)s.f%(fork_seq)i"):
322 """Fork the simulator.
324 This function forks the simulator. After forking the simulator,
325 the child process gets its output files redirected to a new output
326 directory. The default name of the output directory is the same as
327 the parent with the suffix ".fN" added where N is the fork
328 sequence number. The name of the output directory can be
329 overridden using the simout keyword argument.
331 Output file formatting dictionary:
332 parent -- Path to the parent process's output directory.
333 fork_seq -- Fork sequence number.
334 pid -- PID of the child process.
337 simout -- New simulation output directory.
340 pid of the child process or 0 if running in the child.
342 from m5
import options
345 if not _m5
.core
.listenersDisabled():
346 raise RuntimeError("Can not fork a simulator with listeners enabled")
356 # In child, notify objects of the fork
357 root
= objects
.Root
.getInstance()
359 # Setup a new output directory
360 parent
= options
.outdir
361 options
.outdir
= simout
% {
363 "fork_seq" : fork_count
,
366 _m5
.core
.setOutputDir(options
.outdir
)
372 from _m5
.core
import disableAllListeners
, listenersDisabled
373 from _m5
.core
import listenersLoopbackOnly
374 from _m5
.core
import curTick