simulate.py revision 10909:db1b5b20096f
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# The final hook to generate .ini files. Called from the user script 70# once the config is built. 71def instantiate(ckpt_dir=None): 72 from m5 import options 73 74 root = objects.Root.getInstance() 75 76 if not root: 77 fatal("Need to instantiate Root() before calling instantiate()") 78 79 # we need to fix the global frequency 80 ticks.fixGlobalFrequency() 81 82 # Make sure SimObject-valued params are in the configuration 83 # hierarchy so we catch them with future descendants() walks 84 for obj in root.descendants(): obj.adoptOrphanParams() 85 86 # Unproxy in sorted order for determinism 87 for obj in root.descendants(): obj.unproxyParams() 88 89 if options.dump_config: 90 ini_file = file(os.path.join(options.outdir, options.dump_config), 'w') 91 # Print ini sections in sorted order for easier diffing 92 for obj in sorted(root.descendants(), key=lambda o: o.path()): 93 obj.print_ini(ini_file) 94 ini_file.close() 95 96 if options.json_config: 97 try: 98 import json 99 json_file = file(os.path.join(options.outdir, options.json_config), 'w') 100 d = root.get_config_as_dict() 101 json.dump(d, json_file, indent=4) 102 json_file.close() 103 except ImportError: 104 pass 105 106 do_dot(root, options.outdir, options.dot_config) 107 108 # Initialize the global statistics 109 stats.initSimStats() 110 111 # Create the C++ sim objects and connect ports 112 for obj in root.descendants(): obj.createCCObject() 113 for obj in root.descendants(): obj.connectPorts() 114 115 # Do a second pass to finish initializing the sim objects 116 for obj in root.descendants(): obj.init() 117 118 # Do a third pass to initialize statistics 119 for obj in root.descendants(): obj.regStats() 120 121 # Do a fourth pass to initialize probe points 122 for obj in root.descendants(): obj.regProbePoints() 123 124 # Do a fifth pass to connect probe listeners 125 for obj in root.descendants(): obj.regProbeListeners() 126 127 # We're done registering statistics. Enable the stats package now. 128 stats.enable() 129 130 # Restore checkpoint (if any) 131 if ckpt_dir: 132 ckpt = internal.core.getCheckpoint(ckpt_dir) 133 internal.core.unserializeGlobals(ckpt); 134 for obj in root.descendants(): obj.loadState(ckpt) 135 need_resume.append(root) 136 else: 137 for obj in root.descendants(): obj.initState() 138 139 # Check to see if any of the stat events are in the past after resuming from 140 # a checkpoint, If so, this call will shift them to be at a valid time. 141 updateStatEvents() 142 143need_resume = [] 144need_startup = True 145def simulate(*args, **kwargs): 146 global need_resume, need_startup 147 148 if need_startup: 149 root = objects.Root.getInstance() 150 for obj in root.descendants(): obj.startup() 151 need_startup = False 152 153 # Python exit handlers happen in reverse order. 154 # We want to dump stats last. 155 atexit.register(stats.dump) 156 157 # register our C++ exit callback function with Python 158 atexit.register(internal.core.doExitCleanup) 159 160 # Reset to put the stats in a consistent state. 161 stats.reset() 162 163 for root in need_resume: 164 resume(root) 165 need_resume = [] 166 167 return internal.event.simulate(*args, **kwargs) 168 169# Export curTick to user script. 170def curTick(): 171 return internal.core.curTick() 172 173# Drain the system in preparation of a checkpoint or memory mode 174# switch. 175def drain(root): 176 # Try to drain all objects. Draining might not be completed unless 177 # all objects return that they are drained on the first call. This 178 # is because as objects drain they may cause other objects to no 179 # longer be drained. 180 def _drain(): 181 all_drained = False 182 dm = internal.drain.createDrainManager() 183 unready_objs = sum(obj.drain(dm) for obj in root.descendants()) 184 # If we've got some objects that can't drain immediately, then simulate 185 if unready_objs > 0: 186 dm.setCount(unready_objs) 187 #WARNING: if a valid exit event occurs while draining, it will not 188 # get returned to the user script 189 exit_event = simulate() 190 while exit_event.getCause() != 'Finished drain': 191 exit_event = simulate() 192 else: 193 all_drained = True 194 internal.drain.cleanupDrainManager(dm) 195 return all_drained 196 197 all_drained = _drain() 198 while (not all_drained): 199 all_drained = _drain() 200 201def memWriteback(root): 202 for obj in root.descendants(): 203 obj.memWriteback() 204 205def memInvalidate(root): 206 for obj in root.descendants(): 207 obj.memInvalidate() 208 209def resume(root): 210 for obj in root.descendants(): obj.drainResume() 211 212def checkpoint(dir): 213 root = objects.Root.getInstance() 214 if not isinstance(root, objects.Root): 215 raise TypeError, "Checkpoint must be called on a root object." 216 drain(root) 217 memWriteback(root) 218 print "Writing checkpoint" 219 internal.core.serializeAll(dir) 220 resume(root) 221 222def _changeMemoryMode(system, mode): 223 if not isinstance(system, (objects.Root, objects.System)): 224 raise TypeError, "Parameter of type '%s'. Must be type %s or %s." % \ 225 (type(system), objects.Root, objects.System) 226 if system.getMemoryMode() != mode: 227 system.setMemoryMode(mode) 228 else: 229 print "System already in target mode. Memory mode unchanged." 230 231def switchCpus(system, cpuList, do_drain=True, verbose=True): 232 """Switch CPUs in a system. 233 234 By default, this method drains and resumes the system. This 235 behavior can be disabled by setting the keyword argument 236 'do_drain' to false, which might be desirable if multiple 237 operations requiring a drained system are going to be performed in 238 sequence. 239 240 Note: This method may switch the memory mode of the system if that 241 is required by the CPUs. It may also flush all caches in the 242 system. 243 244 Arguments: 245 system -- Simulated system. 246 cpuList -- (old_cpu, new_cpu) tuples 247 248 Keyword Arguments: 249 do_drain -- Perform a drain/resume of the system when switching. 250 """ 251 252 if verbose: 253 print "switching cpus" 254 255 if not isinstance(cpuList, list): 256 raise RuntimeError, "Must pass a list to this function" 257 for item in cpuList: 258 if not isinstance(item, tuple) or len(item) != 2: 259 raise RuntimeError, "List must have tuples of (oldCPU,newCPU)" 260 261 old_cpus = [old_cpu for old_cpu, new_cpu in cpuList] 262 new_cpus = [new_cpu for old_cpu, new_cpu in cpuList] 263 old_cpu_set = set(old_cpus) 264 memory_mode_name = new_cpus[0].memory_mode() 265 for old_cpu, new_cpu in cpuList: 266 if not isinstance(old_cpu, objects.BaseCPU): 267 raise TypeError, "%s is not of type BaseCPU" % old_cpu 268 if not isinstance(new_cpu, objects.BaseCPU): 269 raise TypeError, "%s is not of type BaseCPU" % new_cpu 270 if new_cpu in old_cpu_set: 271 raise RuntimeError, \ 272 "New CPU (%s) is in the list of old CPUs." % (old_cpu,) 273 if not new_cpu.switchedOut(): 274 raise RuntimeError, \ 275 "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, \ 285 "Old CPU (%s) is inactive." % (new_cpu,) 286 if not old_cpu.support_take_over(): 287 raise RuntimeError, \ 288 "Old CPU (%s) does not support CPU handover." % (old_cpu,) 289 290 try: 291 memory_mode = _memory_modes[memory_mode_name] 292 except KeyError: 293 raise RuntimeError, "Invalid memory mode (%s)" % memory_mode_name 294 295 if do_drain: 296 drain(system) 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 317 if do_drain: 318 resume(system) 319 320from internal.core import disableAllListeners 321