simulate.py revision 11991
15124Sgblack@eecs.umich.edu# Copyright (c) 2012 ARM Limited 25124Sgblack@eecs.umich.edu# All rights reserved. 35124Sgblack@eecs.umich.edu# 45124Sgblack@eecs.umich.edu# The license below extends only to copyright in the software and shall 55124Sgblack@eecs.umich.edu# not be construed as granting a license to any other intellectual 65124Sgblack@eecs.umich.edu# property including but not limited to intellectual property relating 75124Sgblack@eecs.umich.edu# to a hardware implementation of the functionality of the software 85124Sgblack@eecs.umich.edu# licensed hereunder. You may use the software subject to the license 95124Sgblack@eecs.umich.edu# terms below provided that you ensure that this notice is replicated 105124Sgblack@eecs.umich.edu# unmodified and in its entirety in all distributions of the software, 115124Sgblack@eecs.umich.edu# modified or unmodified, in source code or in binary form. 125124Sgblack@eecs.umich.edu# 135124Sgblack@eecs.umich.edu# Copyright (c) 2005 The Regents of The University of Michigan 145124Sgblack@eecs.umich.edu# Copyright (c) 2010 Advanced Micro Devices, Inc. 155124Sgblack@eecs.umich.edu# All rights reserved. 165124Sgblack@eecs.umich.edu# 175124Sgblack@eecs.umich.edu# Redistribution and use in source and binary forms, with or without 185124Sgblack@eecs.umich.edu# modification, are permitted provided that the following conditions are 195124Sgblack@eecs.umich.edu# met: redistributions of source code must retain the above copyright 205124Sgblack@eecs.umich.edu# notice, this list of conditions and the following disclaimer; 215124Sgblack@eecs.umich.edu# redistributions in binary form must reproduce the above copyright 225124Sgblack@eecs.umich.edu# notice, this list of conditions and the following disclaimer in the 235124Sgblack@eecs.umich.edu# documentation and/or other materials provided with the distribution; 245124Sgblack@eecs.umich.edu# neither the name of the copyright holders nor the names of its 255124Sgblack@eecs.umich.edu# contributors may be used to endorse or promote products derived from 265124Sgblack@eecs.umich.edu# this software without specific prior written permission. 275124Sgblack@eecs.umich.edu# 285124Sgblack@eecs.umich.edu# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 295124Sgblack@eecs.umich.edu# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 305124Sgblack@eecs.umich.edu# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 315124Sgblack@eecs.umich.edu# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 325124Sgblack@eecs.umich.edu# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 335124Sgblack@eecs.umich.edu# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 345124Sgblack@eecs.umich.edu# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 355124Sgblack@eecs.umich.edu# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 365124Sgblack@eecs.umich.edu# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 375124Sgblack@eecs.umich.edu# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 385124Sgblack@eecs.umich.edu# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 395124Sgblack@eecs.umich.edu# 405124Sgblack@eecs.umich.edu# Authors: Nathan Binkert 415124Sgblack@eecs.umich.edu# Steve Reinhardt 425124Sgblack@eecs.umich.edu 435124Sgblack@eecs.umich.eduimport atexit 445124Sgblack@eecs.umich.eduimport os 455124Sgblack@eecs.umich.eduimport sys 465124Sgblack@eecs.umich.edu 475124Sgblack@eecs.umich.edu# import the wrapped C++ functions 485124Sgblack@eecs.umich.eduimport _m5.drain 495124Sgblack@eecs.umich.eduimport _m5.core 505124Sgblack@eecs.umich.edufrom _m5.stats import updateEvents as updateStatEvents 515124Sgblack@eecs.umich.edu 525124Sgblack@eecs.umich.eduimport stats 535124Sgblack@eecs.umich.eduimport SimObject 545124Sgblack@eecs.umich.eduimport ticks 555124Sgblack@eecs.umich.eduimport objects 565124Sgblack@eecs.umich.edufrom m5.util.dot_writer import do_dot, do_dvfs_dot 575124Sgblack@eecs.umich.edu 585124Sgblack@eecs.umich.edufrom util import fatal 595124Sgblack@eecs.umich.edufrom util import attrdict 605124Sgblack@eecs.umich.edu 615124Sgblack@eecs.umich.edu# define a MaxTick parameter, unsigned 64 bit 625124Sgblack@eecs.umich.eduMaxTick = 2**64 - 1 635124Sgblack@eecs.umich.edu 645124Sgblack@eecs.umich.edu_memory_modes = { 655124Sgblack@eecs.umich.edu "atomic" : objects.params.atomic, 665124Sgblack@eecs.umich.edu "timing" : objects.params.timing, 675124Sgblack@eecs.umich.edu "atomic_noncaching" : objects.params.atomic_noncaching, 685124Sgblack@eecs.umich.edu } 695124Sgblack@eecs.umich.edu 705124Sgblack@eecs.umich.edu_drain_manager = _m5.drain.DrainManager.instance() 715124Sgblack@eecs.umich.edu 725124Sgblack@eecs.umich.edu# The final hook to generate .ini files. Called from the user script 735124Sgblack@eecs.umich.edu# once the config is built. 745124Sgblack@eecs.umich.edudef instantiate(ckpt_dir=None): 755124Sgblack@eecs.umich.edu from m5 import options 765124Sgblack@eecs.umich.edu 775124Sgblack@eecs.umich.edu root = objects.Root.getInstance() 785124Sgblack@eecs.umich.edu 795124Sgblack@eecs.umich.edu if not root: 805124Sgblack@eecs.umich.edu fatal("Need to instantiate Root() before calling instantiate()") 815124Sgblack@eecs.umich.edu 825124Sgblack@eecs.umich.edu # we need to fix the global frequency 835124Sgblack@eecs.umich.edu ticks.fixGlobalFrequency() 845124Sgblack@eecs.umich.edu 855124Sgblack@eecs.umich.edu # Make sure SimObject-valued params are in the configuration 865124Sgblack@eecs.umich.edu # hierarchy so we catch them with future descendants() walks 875124Sgblack@eecs.umich.edu for obj in root.descendants(): obj.adoptOrphanParams() 885681Sgblack@eecs.umich.edu 895124Sgblack@eecs.umich.edu # Unproxy in sorted order for determinism 905124Sgblack@eecs.umich.edu for obj in root.descendants(): obj.unproxyParams() 915124Sgblack@eecs.umich.edu 925124Sgblack@eecs.umich.edu if options.dump_config: 935124Sgblack@eecs.umich.edu ini_file = file(os.path.join(options.outdir, options.dump_config), 'w') 945124Sgblack@eecs.umich.edu # Print ini sections in sorted order for easier diffing 955124Sgblack@eecs.umich.edu for obj in sorted(root.descendants(), key=lambda o: o.path()): 965124Sgblack@eecs.umich.edu obj.print_ini(ini_file) 975237Sgblack@eecs.umich.edu ini_file.close() 985237Sgblack@eecs.umich.edu 995124Sgblack@eecs.umich.edu if options.json_config: 1005124Sgblack@eecs.umich.edu try: 1015124Sgblack@eecs.umich.edu import json 1025124Sgblack@eecs.umich.edu json_file = file(os.path.join(options.outdir, options.json_config), 'w') 1035124Sgblack@eecs.umich.edu d = root.get_config_as_dict() 1045858Sgblack@eecs.umich.edu json.dump(d, json_file, indent=4) 1055124Sgblack@eecs.umich.edu json_file.close() 1065909Sgblack@eecs.umich.edu except ImportError: 1075909Sgblack@eecs.umich.edu pass 1085681Sgblack@eecs.umich.edu 1095681Sgblack@eecs.umich.edu do_dot(root, options.outdir, options.dot_config) 1105681Sgblack@eecs.umich.edu 1115681Sgblack@eecs.umich.edu # Initialize the global statistics 1125858Sgblack@eecs.umich.edu stats.initSimStats() 1135858Sgblack@eecs.umich.edu 1145858Sgblack@eecs.umich.edu # Create the C++ sim objects and connect ports 1155858Sgblack@eecs.umich.edu for obj in root.descendants(): obj.createCCObject() 1165858Sgblack@eecs.umich.edu for obj in root.descendants(): obj.connectPorts() 1175681Sgblack@eecs.umich.edu 1185681Sgblack@eecs.umich.edu # Do a second pass to finish initializing the sim objects 1195681Sgblack@eecs.umich.edu for obj in root.descendants(): obj.init() 1205681Sgblack@eecs.umich.edu 1215909Sgblack@eecs.umich.edu # Do a third pass to initialize statistics 1225857Sgblack@eecs.umich.edu for obj in root.descendants(): obj.regStats() 1235858Sgblack@eecs.umich.edu 1245858Sgblack@eecs.umich.edu # Do a fourth pass to initialize probe points 1255858Sgblack@eecs.umich.edu for obj in root.descendants(): obj.regProbePoints() 1265858Sgblack@eecs.umich.edu 1275858Sgblack@eecs.umich.edu # Do a fifth pass to connect probe listeners 1285858Sgblack@eecs.umich.edu for obj in root.descendants(): obj.regProbeListeners() 1295858Sgblack@eecs.umich.edu 1305858Sgblack@eecs.umich.edu # We want to generate the DVFS diagram for the system. This can only be 1315858Sgblack@eecs.umich.edu # done once all of the CPP objects have been created and initialised so 1325857Sgblack@eecs.umich.edu # that we are able to figure out which object belongs to which domain. 1335857Sgblack@eecs.umich.edu if options.dot_dvfs_config: 1345681Sgblack@eecs.umich.edu do_dvfs_dot(root, options.outdir, options.dot_dvfs_config) 1355681Sgblack@eecs.umich.edu 1365124Sgblack@eecs.umich.edu # We're done registering statistics. Enable the stats package now. 1375909Sgblack@eecs.umich.edu stats.enable() 1385909Sgblack@eecs.umich.edu 1395909Sgblack@eecs.umich.edu # Restore checkpoint (if any) 1405909Sgblack@eecs.umich.edu if ckpt_dir: 1415909Sgblack@eecs.umich.edu _drain_manager.preCheckpointRestore() 1425909Sgblack@eecs.umich.edu ckpt = _m5.core.getCheckpoint(ckpt_dir) 1435909Sgblack@eecs.umich.edu _m5.core.unserializeGlobals(ckpt); 1445909Sgblack@eecs.umich.edu for obj in root.descendants(): obj.loadState(ckpt) 1455909Sgblack@eecs.umich.edu else: 1465909Sgblack@eecs.umich.edu for obj in root.descendants(): obj.initState() 1475909Sgblack@eecs.umich.edu 1485909Sgblack@eecs.umich.edu # Check to see if any of the stat events are in the past after resuming from 1495858Sgblack@eecs.umich.edu # a checkpoint, If so, this call will shift them to be at a valid time. 1505858Sgblack@eecs.umich.edu updateStatEvents() 1515858Sgblack@eecs.umich.edu 1525858Sgblack@eecs.umich.eduneed_startup = True 1535858Sgblack@eecs.umich.edudef simulate(*args, **kwargs): 1545858Sgblack@eecs.umich.edu global need_startup 1555858Sgblack@eecs.umich.edu 1565858Sgblack@eecs.umich.edu if need_startup: 1575858Sgblack@eecs.umich.edu root = objects.Root.getInstance() 1585858Sgblack@eecs.umich.edu for obj in root.descendants(): obj.startup() 1595858Sgblack@eecs.umich.edu need_startup = False 1605858Sgblack@eecs.umich.edu 1615858Sgblack@eecs.umich.edu # Python exit handlers happen in reverse order. 1625858Sgblack@eecs.umich.edu # We want to dump stats last. 1635858Sgblack@eecs.umich.edu atexit.register(stats.dump) 1645858Sgblack@eecs.umich.edu 1655858Sgblack@eecs.umich.edu # register our C++ exit callback function with Python 1665858Sgblack@eecs.umich.edu atexit.register(_m5.core.doExitCleanup) 1675858Sgblack@eecs.umich.edu 1685858Sgblack@eecs.umich.edu # Reset to put the stats in a consistent state. 1695858Sgblack@eecs.umich.edu stats.reset() 1705858Sgblack@eecs.umich.edu 1715858Sgblack@eecs.umich.edu if _drain_manager.isDrained(): 1725858Sgblack@eecs.umich.edu _drain_manager.resume() 1735858Sgblack@eecs.umich.edu 1745858Sgblack@eecs.umich.edu return _m5.event.simulate(*args, **kwargs) 1755858Sgblack@eecs.umich.edu 1765858Sgblack@eecs.umich.edudef drain(): 1775858Sgblack@eecs.umich.edu """Drain the simulator in preparation of a checkpoint or memory mode 1785858Sgblack@eecs.umich.edu switch. 1795237Sgblack@eecs.umich.edu 1805909Sgblack@eecs.umich.edu This operation is a no-op if the simulator is already in the 1815909Sgblack@eecs.umich.edu Drained state. 1825909Sgblack@eecs.umich.edu 1835909Sgblack@eecs.umich.edu """ 1845909Sgblack@eecs.umich.edu 1855909Sgblack@eecs.umich.edu # Try to drain all objects. Draining might not be completed unless 1865909Sgblack@eecs.umich.edu # all objects return that they are drained on the first call. This 1875909Sgblack@eecs.umich.edu # is because as objects drain they may cause other objects to no 1886048Sgblack@eecs.umich.edu # longer be drained. 1896048Sgblack@eecs.umich.edu def _drain(): 1906048Sgblack@eecs.umich.edu # Try to drain the system. The drain is successful if all 1916048Sgblack@eecs.umich.edu # objects are done without simulation. We need to simulate 1926048Sgblack@eecs.umich.edu # more if not. 1936048Sgblack@eecs.umich.edu if _drain_manager.tryDrain(): 1946048Sgblack@eecs.umich.edu return True 1956048Sgblack@eecs.umich.edu 1966048Sgblack@eecs.umich.edu # WARNING: if a valid exit event occurs while draining, it 1976048Sgblack@eecs.umich.edu # will not get returned to the user script 1986048Sgblack@eecs.umich.edu exit_event = _m5.event.simulate() 1996048Sgblack@eecs.umich.edu while exit_event.getCause() != 'Finished drain': 2006048Sgblack@eecs.umich.edu exit_event = simulate() 2016048Sgblack@eecs.umich.edu 2026048Sgblack@eecs.umich.edu return False 2036048Sgblack@eecs.umich.edu 2046048Sgblack@eecs.umich.edu # Don't try to drain a system that is already drained 2056048Sgblack@eecs.umich.edu is_drained = _drain_manager.isDrained() 2066048Sgblack@eecs.umich.edu while not is_drained: 2076048Sgblack@eecs.umich.edu is_drained = _drain() 2086048Sgblack@eecs.umich.edu 2096048Sgblack@eecs.umich.edu assert _drain_manager.isDrained(), "Drain state inconsistent" 2106048Sgblack@eecs.umich.edu 2116048Sgblack@eecs.umich.edudef memWriteback(root): 2126048Sgblack@eecs.umich.edu for obj in root.descendants(): 2136048Sgblack@eecs.umich.edu obj.memWriteback() 2146048Sgblack@eecs.umich.edu 2156048Sgblack@eecs.umich.edudef memInvalidate(root): 2166048Sgblack@eecs.umich.edu for obj in root.descendants(): 2176048Sgblack@eecs.umich.edu obj.memInvalidate() 2186048Sgblack@eecs.umich.edu 2196048Sgblack@eecs.umich.edudef checkpoint(dir): 2206048Sgblack@eecs.umich.edu root = objects.Root.getInstance() 2216048Sgblack@eecs.umich.edu if not isinstance(root, objects.Root): 2226048Sgblack@eecs.umich.edu raise TypeError, "Checkpoint must be called on a root object." 2236048Sgblack@eecs.umich.edu 2246048Sgblack@eecs.umich.edu drain() 2256048Sgblack@eecs.umich.edu memWriteback(root) 2266048Sgblack@eecs.umich.edu print "Writing checkpoint" 2276048Sgblack@eecs.umich.edu _m5.core.serializeAll(dir) 2286048Sgblack@eecs.umich.edu 2296048Sgblack@eecs.umich.edudef _changeMemoryMode(system, mode): 2306048Sgblack@eecs.umich.edu if not isinstance(system, (objects.Root, objects.System)): 2316048Sgblack@eecs.umich.edu raise TypeError, "Parameter of type '%s'. Must be type %s or %s." % \ 2326048Sgblack@eecs.umich.edu (type(system), objects.Root, objects.System) 2336048Sgblack@eecs.umich.edu if system.getMemoryMode() != mode: 2346048Sgblack@eecs.umich.edu system.setMemoryMode(mode) 2356048Sgblack@eecs.umich.edu else: 2366048Sgblack@eecs.umich.edu print "System already in target mode. Memory mode unchanged." 2376048Sgblack@eecs.umich.edu 2386048Sgblack@eecs.umich.edudef switchCpus(system, cpuList, verbose=True): 2396048Sgblack@eecs.umich.edu """Switch CPUs in a system. 2406048Sgblack@eecs.umich.edu 2416048Sgblack@eecs.umich.edu Note: This method may switch the memory mode of the system if that 2426048Sgblack@eecs.umich.edu is required by the CPUs. It may also flush all caches in the 2436048Sgblack@eecs.umich.edu system. 2446048Sgblack@eecs.umich.edu 2456048Sgblack@eecs.umich.edu Arguments: 2466048Sgblack@eecs.umich.edu system -- Simulated system. 2476048Sgblack@eecs.umich.edu cpuList -- (old_cpu, new_cpu) tuples 2486048Sgblack@eecs.umich.edu """ 2496048Sgblack@eecs.umich.edu 2506048Sgblack@eecs.umich.edu if verbose: 2516048Sgblack@eecs.umich.edu print "switching cpus" 2526048Sgblack@eecs.umich.edu 2536048Sgblack@eecs.umich.edu if not isinstance(cpuList, list): 2546048Sgblack@eecs.umich.edu raise RuntimeError, "Must pass a list to this function" 2556048Sgblack@eecs.umich.edu for item in cpuList: 2566048Sgblack@eecs.umich.edu if not isinstance(item, tuple) or len(item) != 2: 2576048Sgblack@eecs.umich.edu raise RuntimeError, "List must have tuples of (oldCPU,newCPU)" 2586048Sgblack@eecs.umich.edu 2596048Sgblack@eecs.umich.edu old_cpus = [old_cpu for old_cpu, new_cpu in cpuList] 2606048Sgblack@eecs.umich.edu new_cpus = [new_cpu for old_cpu, new_cpu in cpuList] 2616048Sgblack@eecs.umich.edu old_cpu_set = set(old_cpus) 2626048Sgblack@eecs.umich.edu memory_mode_name = new_cpus[0].memory_mode() 2636048Sgblack@eecs.umich.edu for old_cpu, new_cpu in cpuList: 2646048Sgblack@eecs.umich.edu if not isinstance(old_cpu, objects.BaseCPU): 2656048Sgblack@eecs.umich.edu raise TypeError, "%s is not of type BaseCPU" % old_cpu 2666048Sgblack@eecs.umich.edu if not isinstance(new_cpu, objects.BaseCPU): 2676048Sgblack@eecs.umich.edu raise TypeError, "%s is not of type BaseCPU" % new_cpu 2686048Sgblack@eecs.umich.edu if new_cpu in old_cpu_set: 2696048Sgblack@eecs.umich.edu raise RuntimeError, \ 2706048Sgblack@eecs.umich.edu "New CPU (%s) is in the list of old CPUs." % (old_cpu,) 2716048Sgblack@eecs.umich.edu if not new_cpu.switchedOut(): 2726048Sgblack@eecs.umich.edu raise RuntimeError, \ 2736048Sgblack@eecs.umich.edu "New CPU (%s) is already active." % (new_cpu,) 2746048Sgblack@eecs.umich.edu if not new_cpu.support_take_over(): 2756048Sgblack@eecs.umich.edu raise RuntimeError, \ 2766048Sgblack@eecs.umich.edu "New CPU (%s) does not support CPU handover." % (old_cpu,) 2776048Sgblack@eecs.umich.edu if new_cpu.memory_mode() != memory_mode_name: 2786048Sgblack@eecs.umich.edu raise RuntimeError, \ 2796048Sgblack@eecs.umich.edu "%s and %s require different memory modes." % (new_cpu, 2806048Sgblack@eecs.umich.edu new_cpus[0]) 2816048Sgblack@eecs.umich.edu if old_cpu.switchedOut(): 2826048Sgblack@eecs.umich.edu raise RuntimeError, \ 2836048Sgblack@eecs.umich.edu "Old CPU (%s) is inactive." % (new_cpu,) 2846048Sgblack@eecs.umich.edu if not old_cpu.support_take_over(): 2855124Sgblack@eecs.umich.edu raise RuntimeError, \ 2865124Sgblack@eecs.umich.edu "Old CPU (%s) does not support CPU handover." % (old_cpu,) 2875124Sgblack@eecs.umich.edu 288 try: 289 memory_mode = _memory_modes[memory_mode_name] 290 except KeyError: 291 raise RuntimeError, "Invalid memory mode (%s)" % memory_mode_name 292 293 drain() 294 295 # Now all of the CPUs are ready to be switched out 296 for old_cpu, new_cpu in cpuList: 297 old_cpu.switchOut() 298 299 # Change the memory mode if required. We check if this is needed 300 # to avoid printing a warning if no switch was performed. 301 if system.getMemoryMode() != memory_mode: 302 # Flush the memory system if we are switching to a memory mode 303 # that disables caches. This typically happens when switching to a 304 # hardware virtualized CPU. 305 if memory_mode == objects.params.atomic_noncaching: 306 memWriteback(system) 307 memInvalidate(system) 308 309 _changeMemoryMode(system, memory_mode) 310 311 for old_cpu, new_cpu in cpuList: 312 new_cpu.takeOverFrom(old_cpu) 313 314def notifyFork(root): 315 for obj in root.descendants(): 316 obj.notifyFork() 317 318fork_count = 0 319def fork(simout="%(parent)s.f%(fork_seq)i"): 320 """Fork the simulator. 321 322 This function forks the simulator. After forking the simulator, 323 the child process gets its output files redirected to a new output 324 directory. The default name of the output directory is the same as 325 the parent with the suffix ".fN" added where N is the fork 326 sequence number. The name of the output directory can be 327 overridden using the simout keyword argument. 328 329 Output file formatting dictionary: 330 parent -- Path to the parent process's output directory. 331 fork_seq -- Fork sequence number. 332 pid -- PID of the child process. 333 334 Keyword Arguments: 335 simout -- New simulation output directory. 336 337 Return Value: 338 pid of the child process or 0 if running in the child. 339 """ 340 from m5 import options 341 global fork_count 342 343 if not _m5.core.listenersDisabled(): 344 raise RuntimeError, "Can not fork a simulator with listeners enabled" 345 346 drain() 347 348 try: 349 pid = os.fork() 350 except OSError, e: 351 raise e 352 353 if pid == 0: 354 # In child, notify objects of the fork 355 root = objects.Root.getInstance() 356 notifyFork(root) 357 # Setup a new output directory 358 parent = options.outdir 359 options.outdir = simout % { 360 "parent" : parent, 361 "fork_seq" : fork_count, 362 "pid" : os.getpid(), 363 } 364 _m5.core.setOutputDir(options.outdir) 365 else: 366 fork_count += 1 367 368 return pid 369 370from _m5.core import disableAllListeners, listenersDisabled 371from _m5.core import curTick 372