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