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 do_dot(root
, options
.outdir
, options
.dot_config
)
111 do_ruby_dot(root
, options
.outdir
, options
.dot_config
)
113 # Initialize the global statistics
116 # Create the C++ sim objects and connect ports
117 for obj
in root
.descendants(): obj
.createCCObject()
118 for obj
in root
.descendants(): obj
.connectPorts()
120 # Do a second pass to finish initializing the sim objects
121 for obj
in root
.descendants(): obj
.init()
123 # Do a third pass to initialize statistics
124 stats
._bindStatHierarchy
(root
)
127 # Do a fourth pass to initialize probe points
128 for obj
in root
.descendants(): obj
.regProbePoints()
130 # Do a fifth pass to connect probe listeners
131 for obj
in root
.descendants(): obj
.regProbeListeners()
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
)
139 # We're done registering statistics. Enable the stats package now.
142 # Restore checkpoint (if any)
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
)
149 for obj
in root
.descendants(): obj
.initState()
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.
156 def simulate(*args
, **kwargs
):
160 root
= objects
.Root
.getInstance()
161 for obj
in root
.descendants(): obj
.startup()
164 # Python exit handlers happen in reverse order.
165 # We want to dump stats last.
166 atexit
.register(stats
.dump
)
168 # register our C++ exit callback function with Python
169 atexit
.register(_m5
.core
.doExitCleanup
)
171 # Reset to put the stats in a consistent state.
174 if _drain_manager
.isDrained():
175 _drain_manager
.resume()
177 return _m5
.event
.simulate(*args
, **kwargs
)
180 """Drain the simulator in preparation of a checkpoint or memory mode
183 This operation is a no-op if the simulator is already in the
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
193 # Try to drain the system. The drain is successful if all
194 # objects are done without simulation. We need to simulate
196 if _drain_manager
.tryDrain():
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()
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()
212 assert _drain_manager
.isDrained(), "Drain state inconsistent"
214 def memWriteback(root
):
215 for obj
in root
.descendants():
218 def memInvalidate(root
):
219 for obj
in root
.descendants():
223 root
= objects
.Root
.getInstance()
224 if not isinstance(root
, objects
.Root
):
225 raise TypeError("Checkpoint must be called on a root object.")
229 print("Writing checkpoint")
230 _m5
.core
.serializeAll(dir)
232 def _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
)
239 print("System already in target mode. Memory mode unchanged.")
241 def switchCpus(system
, cpuList
, verbose
=True):
242 """Switch CPUs in a system.
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
249 system -- Simulated system.
250 cpuList -- (old_cpu, new_cpu) tuples
254 print("switching cpus")
256 if not isinstance(cpuList
, list):
257 raise RuntimeError("Must pass a list to this function")
259 if not isinstance(item
, tuple) or len(item
) != 2:
260 raise RuntimeError("List must have tuples of (oldCPU,newCPU)")
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
:
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():
278 "New CPU (%s) does not support CPU handover." % (old_cpu
,))
279 if new_cpu
.memory_mode() != memory_mode_name
:
281 "%s and %s require different memory modes." % (new_cpu
,
283 if old_cpu
.switchedOut():
284 raise RuntimeError("Old CPU (%s) is inactive." % (new_cpu
,))
285 if not old_cpu
.support_take_over():
287 "Old CPU (%s) does not support CPU handover." % (old_cpu
,))
290 memory_mode
= _memory_modes
[memory_mode_name
]
292 raise RuntimeError("Invalid memory mode (%s)" % memory_mode_name
)
296 # Now all of the CPUs are ready to be switched out
297 for old_cpu
, new_cpu
in cpuList
:
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
:
308 memInvalidate(system
)
310 _changeMemoryMode(system
, memory_mode
)
312 for old_cpu
, new_cpu
in cpuList
:
313 new_cpu
.takeOverFrom(old_cpu
)
315 def notifyFork(root
):
316 for obj
in root
.descendants():
320 def fork(simout
="%(parent)s.f%(fork_seq)i"):
321 """Fork the simulator.
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.
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.
336 simout -- New simulation output directory.
339 pid of the child process or 0 if running in the child.
341 from m5
import options
344 if not _m5
.core
.listenersDisabled():
345 raise RuntimeError("Can not fork a simulator with listeners enabled")
355 # In child, notify objects of the fork
356 root
= objects
.Root
.getInstance()
358 # Setup a new output directory
359 parent
= options
.outdir
360 options
.outdir
= simout
% {
362 "fork_seq" : fork_count
,
365 _m5
.core
.setOutputDir(options
.outdir
)
371 from _m5
.core
import disableAllListeners
, listenersDisabled
372 from _m5
.core
import listenersLoopbackOnly
373 from _m5
.core
import curTick