simulate.py revision 10912:b99a6662d7c2
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_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. 73def 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 145need_startup = True 146def 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. 170def curTick(): 171 return internal.core.curTick() 172 173def 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 208def memWriteback(root): 209 for obj in root.descendants(): 210 obj.memWriteback() 211 212def memInvalidate(root): 213 for obj in root.descendants(): 214 obj.memInvalidate() 215 216def 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 226def _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 235def 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 311from internal.core import disableAllListeners 312