From: Steve Reinhardt Date: Sat, 10 Jun 2006 03:01:31 +0000 (-0400) Subject: Move main control from C++ into Python. X-Git-Tag: m5_2.0_beta1~36^2~84^2 X-Git-Url: https://git.libre-soc.org/?a=commitdiff_plain;h=29e34a739b991af8d8e1eafe75ecb0904c324dc8;p=gem5.git Move main control from C++ into Python. User script now invokes initialization and simulation loop after building configuration. These functions are exported from C++ to Python using SWIG. SConstruct: Set up SWIG builder & scanner. Set up symlinking of source files into build directory (by not disabling the default behavior). configs/test/test.py: Rewrite to use new script-driven interface. Include a sample option. src/SConscript: Set up symlinking of source files into build directory (by not disabling the default behavior). Add SWIG-generated main_wrap.cc to source list. src/arch/SConscript: Set up symlinking of source files into build directory (by not disabling the default behavior). src/arch/alpha/ev5.cc: src/arch/alpha/isa/decoder.isa: src/cpu/o3/alpha_cpu_impl.hh: src/cpu/trace/opt_cpu.cc: src/cpu/trace/trace_cpu.cc: src/sim/pseudo_inst.cc: src/sim/root.cc: src/sim/serialize.cc: src/sim/syscall_emul.cc: SimExit() is now exitSimLoop(). src/cpu/base.cc: SimExitEvent is now SimLoopExitEvent src/python/SConscript: Add SWIG build command for main.i. Use python/m5 in build dir as source for zip archive... easy now with file duplication enabled. src/python/m5/__init__.py: - Move copyright notice back to C++ so we can print it right away, even for interactive sessions. - Get rid of argument parsing code; just provide default option descriptors for user script to call optparse with. - Don't clutter m5 namespace by sucking in all of m5.config and m5.objects. - Move instantiate() function here from config.py. src/python/m5/config.py: - Move instantiate() function to __init__.py. - Param.Foo deferred type lookups must use m5.objects namespace now (not m5). src/python/m5/objects/AlphaConsole.py: src/python/m5/objects/AlphaFullCPU.py: src/python/m5/objects/AlphaTLB.py: src/python/m5/objects/BadDevice.py: src/python/m5/objects/BaseCPU.py: src/python/m5/objects/BaseCache.py: src/python/m5/objects/Bridge.py: src/python/m5/objects/Bus.py: src/python/m5/objects/CoherenceProtocol.py: src/python/m5/objects/Device.py: src/python/m5/objects/DiskImage.py: src/python/m5/objects/Ethernet.py: src/python/m5/objects/Ide.py: src/python/m5/objects/IntrControl.py: src/python/m5/objects/MemObject.py: src/python/m5/objects/MemTest.py: src/python/m5/objects/Pci.py: src/python/m5/objects/PhysicalMemory.py: src/python/m5/objects/Platform.py: src/python/m5/objects/Process.py: src/python/m5/objects/Repl.py: src/python/m5/objects/Root.py: src/python/m5/objects/SimConsole.py: src/python/m5/objects/SimpleDisk.py: src/python/m5/objects/System.py: src/python/m5/objects/Tsunami.py: src/python/m5/objects/Uart.py: Fix up imports (m5 namespace no longer includes m5.config). src/sim/eventq.cc: src/sim/eventq.hh: Support for Python-called simulate() function: - Use IsExitEvent flag to signal events that want to exit the simulation loop gracefully (instead of calling exit() to terminate the process). - Modify interface to hand exit event object back to caller so it can be inspected for cause. src/sim/host.hh: Add MaxTick constant. src/sim/main.cc: Move copyright notice back to C++ so we can print it right away, even for interactive sessions. Use PYTHONPATH environment var to set module path (instead of clunky code injection method). Move main control from here into Python: - Separate initialization code and simulation loop into separate functions callable from Python. - Make Python interpreter invocation more pure (more like directly invoking interpreter). Add -i and -p flags (only options on binary itself; other options processed by Python). Import readline package when using interactive mode. src/sim/sim_events.cc: SimExitEvent is now SimLoopExitEvent, and uses IsSimExit flag to terminate loop (instead of exiting simulator process). src/sim/sim_events.hh: SimExitEvent is now SimLoopExitEvent, and uses IsSimExit flag to terminate loop (instead of exiting simulator process). Get rid of a few unused constructors. src/sim/sim_exit.hh: SimExit() is now exitSimLoop(). Get rid of unused functions. Add comments. --HG-- extra : convert_revision : 280b0d671516b25545a6f24cefa64a68319ff3d4 --- diff --git a/SConstruct b/SConstruct index 5e2e2711d..742bd932a 100644 --- a/SConstruct +++ b/SConstruct @@ -158,6 +158,12 @@ env = Environment(ENV = os.environ, # inherit user's environment vars env.SConsignFile("sconsign") +# Default duplicate option is to use hard links, but this messes up +# when you use emacs to edit a file in the target dir, as emacs moves +# file to file~ then copies to file, breaking the link. Symbolic +# (soft) links work better. +env.SetOption('duplicate', 'soft-copy') + # I waffle on this setting... it does avoid a few painful but # unnecessary builds, but it also seems to make trivial builds take # noticeably longer. @@ -193,6 +199,19 @@ env.Append(LIBS = py_version_name) if sys.exec_prefix != '/usr': env.Append(LIBPATH = os.path.join(sys.exec_prefix, 'lib')) +# Set up SWIG flags & scanner + +env.Append(SWIGFLAGS=Split('-c++ -python -modern $_CPPINCFLAGS')) + +import SCons.Scanner + +swig_inc_re = '^[ \t]*[%,#][ \t]*(?:include|import)[ \t]*(<|")([^>"]+)(>|")' + +swig_scanner = SCons.Scanner.ClassicCPP("SwigScan", ".i", "CPPPATH", + swig_inc_re) + +env.Append(SCANNERS = swig_scanner) + # Other default libraries env.Append(LIBS=['z']) @@ -468,7 +487,7 @@ for build_path in build_paths: # to the configured options. It returns a list of environments, # one for each variant build (debug, opt, etc.) envList = SConscript('src/SConscript', build_dir = build_path, - exports = 'env', duplicate = False) + exports = 'env') # Set up the regression tests for each build. # for e in envList: diff --git a/configs/test/test.py b/configs/test/test.py index 75e832f5e..0c6359148 100644 --- a/configs/test/test.py +++ b/configs/test/test.py @@ -1,12 +1,40 @@ -from m5 import * +import os, optparse, sys +import m5 +from m5.objects import * -class HelloWorld(AlphaLiveProcess): - executable = '../configs/test/hello' - cmd = 'hello' +parser = optparse.OptionParser(option_list=m5.standardOptions) + +parser.add_option("-t", "--timing", action="store_true") + +(options, args) = parser.parse_args() + +if args: + print "Error: script doesn't take any positional arguments" + sys.exit(1) + +this_dir = os.path.dirname(__file__) + +process = AlphaLiveProcess() +process.executable = os.path.join(this_dir, 'hello') +process.cmd = 'hello' magicbus = Bus() mem = PhysicalMemory() -cpu = AtomicSimpleCPU(workload=HelloWorld(), mem=magicbus) -system = System(physmem=mem, cpu=cpu) -system.c1 = Connector(side_a=mem, side_b=magicbus) -root = Root(system=system) + +if options.timing: + cpu = TimingSimpleCPU() +else: + cpu = AtomicSimpleCPU() +cpu.workload = process +cpu.mem = magicbus + +system = System(physmem = mem, cpu = cpu) +system.c1 = Connector(side_a = mem, side_b = magicbus) +root = Root(system = system) + +m5.instantiate(root) + +exit_event = m5.simulate() + +print 'Exiting @', m5.curTick(), 'because', exit_event.getCause() + diff --git a/src/SConscript b/src/SConscript index 39e4b435a..645d07e5d 100644 --- a/src/SConscript +++ b/src/SConscript @@ -104,12 +104,12 @@ base_sources = Split(''' sim/eventq.cc sim/faults.cc sim/main.cc + python/swig/main_wrap.cc sim/param.cc sim/profile.cc sim/root.cc sim/serialize.cc sim/sim_events.cc - sim/sim_exit.cc sim/sim_object.cc sim/startup.cc sim/stat_context.cc @@ -281,14 +281,18 @@ memtest_sources = Split(''' cpu/memtest/memtest.cc ''') +# Include file paths are rooted in this directory. SCons will +# automatically expand '.' to refer to both the source directory and +# the corresponding build directory to pick up generated include +# files. +env.Append(CPPPATH=Dir('.')) + # Add a flag defining what THE_ISA should be for all compilation env.Append(CPPDEFINES=[('THE_ISA','%s_ISA' % env['TARGET_ISA'].upper())]) -arch_sources = SConscript('arch/SConscript', - exports = 'env', duplicate = False) +arch_sources = SConscript('arch/SConscript', exports = 'env') -cpu_sources = SConscript('cpu/SConscript', - exports = 'env', duplicate = False) +cpu_sources = SConscript('cpu/SConscript', exports = 'env') # This is outside of cpu/SConscript since the source directory isn't # underneath 'cpu'. @@ -323,7 +327,7 @@ env.Command(Split('base/traceflags.hh base/traceflags.cc'), 'base/traceflags.py', 'python $SOURCE $TARGET.base') -SConscript('python/SConscript', exports = ['env'], duplicate=0) +SConscript('python/SConscript', exports = ['env']) # This function adds the specified sources to the given build # environment, and returns a list of all the corresponding SCons @@ -346,12 +350,6 @@ def make_objs(sources, env): # ################################################### -# Include file paths are rooted in this directory. SCons will -# automatically expand '.' to refer to both the source directory and -# the corresponding build directory to pick up generated include -# files. -env.Append(CPPPATH='.') - # List of constructed environments to pass back to SConstruct envList = [] diff --git a/src/arch/SConscript b/src/arch/SConscript index dd616174c..c90694a68 100644 --- a/src/arch/SConscript +++ b/src/arch/SConscript @@ -146,7 +146,6 @@ env.Append(BUILDERS = { 'ISADesc' : isa_desc_builder }) isa = env['TARGET_ISA'] # someday this may be a list of ISAs # Let the target architecture define what additional sources it needs -sources += SConscript(os.path.join(isa, 'SConscript'), - exports = 'env', duplicate = False) +sources += SConscript(os.path.join(isa, 'SConscript'), exports = 'env') Return('sources') diff --git a/src/arch/alpha/ev5.cc b/src/arch/alpha/ev5.cc index 12782d646..4d0482019 100644 --- a/src/arch/alpha/ev5.cc +++ b/src/arch/alpha/ev5.cc @@ -564,7 +564,7 @@ CPUExecContext::simPalCheck(int palFunc) case PAL::halt: halt(); if (--System::numSystemsRunning == 0) - new SimExitEvent("all cpus halted"); + exitSimLoop("all cpus halted"); break; case PAL::bpt: diff --git a/src/arch/alpha/isa/decoder.isa b/src/arch/alpha/isa/decoder.isa index eaa94a842..17132bbc2 100644 --- a/src/arch/alpha/isa/decoder.isa +++ b/src/arch/alpha/isa/decoder.isa @@ -697,7 +697,7 @@ decode OPCODE default Unknown::unknown() { 0x00: decode PALFUNC { format EmulatedCallPal { 0x00: halt ({{ - SimExit(curTick, "halt instruction encountered"); + exitSimLoop(curTick, "halt instruction encountered"); }}, IsNonSpeculative); 0x83: callsys({{ xc->syscall(R0); diff --git a/src/cpu/base.cc b/src/cpu/base.cc index 64d81ef4b..59324e537 100644 --- a/src/cpu/base.cc +++ b/src/cpu/base.cc @@ -93,8 +93,8 @@ BaseCPU::BaseCPU(Params *p) // if (p->max_insts_any_thread != 0) for (int i = 0; i < number_of_threads; ++i) - new SimExitEvent(comInstEventQueue[i], p->max_insts_any_thread, - "a thread reached the max instruction count"); + new SimLoopExitEvent(comInstEventQueue[i], p->max_insts_any_thread, + "a thread reached the max instruction count"); if (p->max_insts_all_threads != 0) { // allocate & initialize shared downcounter: each event will @@ -118,8 +118,8 @@ BaseCPU::BaseCPU(Params *p) // if (p->max_loads_any_thread != 0) for (int i = 0; i < number_of_threads; ++i) - new SimExitEvent(comLoadEventQueue[i], p->max_loads_any_thread, - "a thread reached the max load count"); + new SimLoopExitEvent(comLoadEventQueue[i], p->max_loads_any_thread, + "a thread reached the max load count"); if (p->max_loads_all_threads != 0) { // allocate & initialize shared downcounter: each event will diff --git a/src/cpu/o3/alpha_cpu_impl.hh b/src/cpu/o3/alpha_cpu_impl.hh index 446385ead..624fe8594 100644 --- a/src/cpu/o3/alpha_cpu_impl.hh +++ b/src/cpu/o3/alpha_cpu_impl.hh @@ -292,7 +292,7 @@ AlphaFullCPU::simPalCheck(int palFunc) case PAL::halt: halt(); if (--System::numSystemsRunning == 0) - new SimExitEvent("all cpus halted"); + exitSimLoop("all cpus halted"); break; case PAL::bpt: diff --git a/src/cpu/trace/opt_cpu.cc b/src/cpu/trace/opt_cpu.cc index 098031d4a..996e89f01 100644 --- a/src/cpu/trace/opt_cpu.cc +++ b/src/cpu/trace/opt_cpu.cc @@ -176,7 +176,7 @@ OptCPU::tick() fprintf(stderr,"sys.cpu.misses %d #opt cache misses\n",misses); fprintf(stderr,"sys.cpu.hits %d #opt cache hits\n", hits); fprintf(stderr,"sys.cpu.accesses %d #opt cache acceses\n", references); - new SimExitEvent("Finshed Memory Trace"); + exitSimLoop("end of memory trace reached"); } void diff --git a/src/cpu/trace/trace_cpu.cc b/src/cpu/trace/trace_cpu.cc index 4df47229f..3c9da4849 100644 --- a/src/cpu/trace/trace_cpu.cc +++ b/src/cpu/trace/trace_cpu.cc @@ -108,7 +108,7 @@ TraceCPU::tick() if (!nextReq) { // No more requests to send. Finish trailing events and exit. if (mainEventQueue.empty()) { - new SimExitEvent("Finshed Memory Trace"); + exitSimLoop("end of memory trace reached"); } else { tickEvent.schedule(mainEventQueue.nextEventTime() + cycles(1)); } diff --git a/src/python/SConscript b/src/python/SConscript index 5a787cfdf..7b0f591eb 100644 --- a/src/python/SConscript +++ b/src/python/SConscript @@ -38,11 +38,8 @@ def join(*args): Import('env') -# This SConscript is in charge of collecting .py files and generating a zip archive that is appended to the m5 binary. - -# Copy .py source files here (relative to src/python in the build -# directory). -pyzip_root = 'zip' +# This SConscript is in charge of collecting .py files and generating +# a zip archive that is appended to the m5 binary. # List of files & directories to include in the zip file. To include # a package, list only the root directory of the package, not any @@ -58,7 +55,7 @@ pyzip_dep_files = [] # Add the specified package to the zip archive. Adds the directory to # pyzip_files and all included .py files to pyzip_dep_files. def addPkg(pkgdir): - pyzip_files.append(join(pyzip_root, pkgdir)) + pyzip_files.append(pkgdir) origdir = os.getcwd() srcdir = join(Dir('.').srcnode().abspath, pkgdir) os.chdir(srcdir) @@ -70,10 +67,7 @@ def addPkg(pkgdir): for f in files: if f.endswith('.py'): - source = join(pkgdir, path, f) - target = join(pyzip_root, source) - pyzip_dep_files.append(target) - env.CopyFile(target, source) + pyzip_dep_files.append(join(pkgdir, path, f)) os.chdir(origdir) @@ -81,19 +75,25 @@ def addPkg(pkgdir): # build_env flags. def MakeDefinesPyFile(target, source, env): f = file(str(target[0]), 'w') - print >>f, "import __main__" - print >>f, "__main__.m5_build_env = ", + print >>f, "m5_build_env = ", print >>f, source[0] f.close() optionDict = dict([(opt, env[opt]) for opt in env.ExportOptions]) -env.Command('defines.py', Value(optionDict), MakeDefinesPyFile) +env.Command('m5/defines.py', Value(optionDict), MakeDefinesPyFile) # Now specify the packages & files for the zip archive. addPkg('m5') -pyzip_files.append('defines.py') +pyzip_files.append('m5/defines.py') pyzip_files.append(join(env['ROOT'], 'util/pbs/jobfile.py')) +env.Command(['swig/main_wrap.cc', 'm5/main.py'], + 'swig/main.i', + '$SWIG $SWIGFLAGS -outdir ${TARGETS[1].dir} ' + '-o ${TARGETS[0]} $SOURCES') + +pyzip_dep_files.append('m5/main.py') + # Action function to build the zip archive. Uses the PyZipFile module # included in the standard Python library. def buildPyZip(target, source, env): diff --git a/src/python/m5/__init__.py b/src/python/m5/__init__.py index c44a9ad1f..60a61d66e 100644 --- a/src/python/m5/__init__.py +++ b/src/python/m5/__init__.py @@ -27,69 +27,26 @@ # Authors: Nathan Binkert # Steve Reinhardt -import sys, os, time +import sys, os, time, atexit, optparse -import __main__ +# import the SWIG-wrapped main C++ functions +import main +# import a few SWIG-wrapped items (those that are likely to be used +# directly by user scripts) completely into this module for +# convenience +from main import simulate, SimLoopExitEvent -briefCopyright = ''' -Copyright (c) 2001-2006 -The Regents of The University of Michigan -All Rights Reserved -''' - -fullCopyright = ''' -Copyright (c) 2001-2006 -The Regents of The University of Michigan -All Rights Reserved - -Permission is granted to use, copy, create derivative works and -redistribute this software and such derivative works for any purpose, -so long as the copyright notice above, this grant of permission, and -the disclaimer below appear in all copies made; and so long as the -name of The University of Michigan is not used in any advertising or -publicity pertaining to the use or distribution of this software -without specific, written prior authorization. - -THIS SOFTWARE IS PROVIDED AS IS, WITHOUT REPRESENTATION FROM THE -UNIVERSITY OF MICHIGAN AS TO ITS FITNESS FOR ANY PURPOSE, AND WITHOUT -WARRANTY BY THE UNIVERSITY OF MICHIGAN OF ANY KIND, EITHER EXPRESS OR -IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED WARRANTIES OF -MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE REGENTS OF -THE UNIVERSITY OF MICHIGAN SHALL NOT BE LIABLE FOR ANY DAMAGES, -INCLUDING DIRECT, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL -DAMAGES, WITH RESPECT TO ANY CLAIM ARISING OUT OF OR IN CONNECTION -WITH THE USE OF THE SOFTWARE, EVEN IF IT HAS BEEN OR IS HEREAFTER -ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. -''' - -def sayHello(f): - print >> f, "M5 Simulator System" - print >> f, briefCopyright - print >> f, "M5 compiled on", __main__.compileDate - hostname = os.environ.get('HOSTNAME') - if not hostname: - hostname = os.environ.get('HOST') - if hostname: - print >> f, "M5 executing on", hostname - print >> f, "M5 simulation started", time.ctime() - -sayHello(sys.stderr) +# import the m5 compile options +import defines # define this here so we can use it right away if necessary def panic(string): print >>sys.stderr, 'panic:', string sys.exit(1) -def m5execfile(f, global_dict): - # copy current sys.path - oldpath = sys.path[:] - # push file's directory onto front of path - sys.path.insert(0, os.path.abspath(os.path.dirname(f))) - execfile(f, global_dict) - # restore original path - sys.path = oldpath - -# Prepend given directory to system module search path. +# Prepend given directory to system module search path. We may not +# need this anymore if we can structure our config library more like a +# Python package. def AddToPath(path): # if it's a relative path and we know what directory the current # python script is in, make the path relative to that directory. @@ -100,84 +57,58 @@ def AddToPath(path): # so place the new dir right after that. sys.path.insert(1, path) -# find the m5 compile options: must be specified as a dict in -# __main__.m5_build_env. -import __main__ -if not hasattr(__main__, 'm5_build_env'): - panic("__main__ must define m5_build_env") + +# Callback to set trace flags. Not necessarily the best way to do +# things in the long run (particularly if we change how these global +# options are handled). +def setTraceFlags(option, opt_str, value, parser): + objects.Trace.flags = value + +# Standard optparse options. Need to be explicitly included by the +# user script when it calls optparse.OptionParser(). +standardOptions = [ + optparse.make_option("--traceflags", type="string", action="callback", + callback=setTraceFlags) + ] # make a SmartDict out of the build options for our local use import smartdict build_env = smartdict.SmartDict() -build_env.update(__main__.m5_build_env) +build_env.update(defines.m5_build_env) # make a SmartDict out of the OS environment too env = smartdict.SmartDict() env.update(os.environ) -# import the main m5 config code -from config import * - -# import the built-in object definitions -from objects import * - - -args_left = sys.argv[1:] -configfile_found = False - -while args_left: - arg = args_left.pop(0) - if arg.startswith('--'): - # if arg starts with '--', parse as a special python option - # of the format --= - try: - (var, val) = arg.split('=', 1) - except ValueError: - panic("Could not parse configuration argument '%s'\n" - "Expecting --=\n" % arg); - eval("%s = %s" % (var, repr(val))) - elif arg.startswith('-'): - # if the arg starts with '-', it should be a simulator option - # with a format similar to getopt. - optchar = arg[1] - if len(arg) > 2: - args_left.insert(0, arg[2:]) - if optchar == 'd': - outdir = args_left.pop(0) - elif optchar == 'h': - showBriefHelp(sys.stderr) - sys.exit(1) - elif optchar == 'E': - env_str = args_left.pop(0) - split_result = env_str.split('=', 1) - var = split_result[0] - if len(split_result == 2): - val = split_result[1] - else: - val = True - env[var] = val - elif optchar == 'I': - AddToPath(args_left.pop(0)) - elif optchar == 'P': - eval(args_left.pop(0)) - else: - showBriefHelp(sys.stderr) - panic("invalid argument '%s'\n" % arg_str) - else: - # In any other case, treat the option as a configuration file - # name and load it. - if not arg.endswith('.py'): - panic("Config file '%s' must end in '.py'\n" % arg) - configfile_found = True - m5execfile(arg, globals()) - - -if not configfile_found: - panic("no configuration file specified!") - -if globals().has_key('root') and isinstance(root, Root): +# The final hook to generate .ini files. Called from the user script +# once the config is built. +def instantiate(root): + config.ticks_per_sec = float(root.clock.frequency) + # ugly temporary hack to get output to config.ini sys.stdout = file('config.ini', 'w') - instantiate(root) -else: - print 'Instantiation skipped: no root object found.' - + root.print_ini() + sys.stdout.close() # close config.ini + sys.stdout = sys.__stdout__ # restore to original + main.initialize() # load config.ini into C++ and process it + noDot = True # temporary until we fix dot + if not noDot: + dot = pydot.Dot() + instance.outputDot(dot) + dot.orientation = "portrait" + dot.size = "8.5,11" + dot.ranksep="equally" + dot.rank="samerank" + dot.write("config.dot") + dot.write_ps("config.ps") + +# Export curTick to user script. +def curTick(): + return main.cvar.curTick + +# register our C++ exit callback function with Python +atexit.register(main.doExitCleanup) + +# This import allows user scripts to reference 'm5.objects.Foo' after +# just doing an 'import m5' (without an 'import m5.objects'). May not +# matter since most scripts will probably 'from m5.objects import *'. +import objects diff --git a/src/python/m5/config.py b/src/python/m5/config.py index d1471c807..f6a2a84fb 100644 --- a/src/python/m5/config.py +++ b/src/python/m5/config.py @@ -728,7 +728,7 @@ class ParamDesc(object): def __getattr__(self, attr): if attr == 'ptype': try: - ptype = eval(self.ptype_str, m5.__dict__) + ptype = eval(self.ptype_str, m5.objects.__dict__) if not isinstance(ptype, type): panic("Param qualifier is not a type: %s" % self.ptype) self.ptype = ptype @@ -1290,23 +1290,6 @@ AllMemory = AddrRange(0, MaxAddr) ##################################################################### -# The final hook to generate .ini files. Called from configuration -# script once config is built. -def instantiate(root): - global ticks_per_sec - ticks_per_sec = float(root.clock.frequency) - root.print_ini() - noDot = True # temporary until we fix dot - if not noDot: - dot = pydot.Dot() - instance.outputDot(dot) - dot.orientation = "portrait" - dot.size = "8.5,11" - dot.ranksep="equally" - dot.rank="samerank" - dot.write("config.dot") - dot.write_ps("config.ps") - # __all__ defines the list of symbols that get exported when # 'from config import *' is invoked. Try to keep this reasonably # short to avoid polluting other namespaces. @@ -1322,5 +1305,5 @@ __all__ = ['SimObject', 'ParamContext', 'Param', 'VectorParam', 'NetworkBandwidth', 'MemoryBandwidth', 'Range', 'AddrRange', 'MaxAddr', 'MaxTick', 'AllMemory', 'Null', 'NULL', - 'NextEthernetAddr', 'instantiate'] + 'NextEthernetAddr'] diff --git a/src/python/m5/objects/AlphaConsole.py b/src/python/m5/objects/AlphaConsole.py index 68e6089ab..329b8c5bd 100644 --- a/src/python/m5/objects/AlphaConsole.py +++ b/src/python/m5/objects/AlphaConsole.py @@ -1,4 +1,4 @@ -from m5 import * +from m5.config import * from Device import BasicPioDevice class AlphaConsole(BasicPioDevice): diff --git a/src/python/m5/objects/AlphaFullCPU.py b/src/python/m5/objects/AlphaFullCPU.py index 48989d057..759ab3cc6 100644 --- a/src/python/m5/objects/AlphaFullCPU.py +++ b/src/python/m5/objects/AlphaFullCPU.py @@ -1,4 +1,5 @@ -from m5 import * +from m5 import build_env +from m5.config import * from BaseCPU import BaseCPU class DerivAlphaFullCPU(BaseCPU): diff --git a/src/python/m5/objects/AlphaTLB.py b/src/python/m5/objects/AlphaTLB.py index 5edf8e13d..11c1792ee 100644 --- a/src/python/m5/objects/AlphaTLB.py +++ b/src/python/m5/objects/AlphaTLB.py @@ -1,4 +1,4 @@ -from m5 import * +from m5.config import * class AlphaTLB(SimObject): type = 'AlphaTLB' abstract = True diff --git a/src/python/m5/objects/BadDevice.py b/src/python/m5/objects/BadDevice.py index 9cb9a8f03..186b733fa 100644 --- a/src/python/m5/objects/BadDevice.py +++ b/src/python/m5/objects/BadDevice.py @@ -1,4 +1,4 @@ -from m5 import * +from m5.config import * from Device import BasicPioDevice class BadDevice(BasicPioDevice): diff --git a/src/python/m5/objects/BaseCPU.py b/src/python/m5/objects/BaseCPU.py index 49cb2a8f3..2e78578df 100644 --- a/src/python/m5/objects/BaseCPU.py +++ b/src/python/m5/objects/BaseCPU.py @@ -1,4 +1,6 @@ -from m5 import * +from m5 import build_env +from m5.config import * + class BaseCPU(SimObject): type = 'BaseCPU' abstract = True diff --git a/src/python/m5/objects/BaseCache.py b/src/python/m5/objects/BaseCache.py index 79d21572a..33f44759b 100644 --- a/src/python/m5/objects/BaseCache.py +++ b/src/python/m5/objects/BaseCache.py @@ -1,4 +1,4 @@ -from m5 import * +from m5.config import * from BaseMem import BaseMem class Prefetch(Enum): vals = ['none', 'tagged', 'stride', 'ghb'] diff --git a/src/python/m5/objects/Bridge.py b/src/python/m5/objects/Bridge.py index ada715ce9..880535755 100644 --- a/src/python/m5/objects/Bridge.py +++ b/src/python/m5/objects/Bridge.py @@ -1,4 +1,4 @@ -from m5 import * +from m5.config import * from MemObject import MemObject class Bridge(MemObject): diff --git a/src/python/m5/objects/Bus.py b/src/python/m5/objects/Bus.py index 8c5397281..c37dab438 100644 --- a/src/python/m5/objects/Bus.py +++ b/src/python/m5/objects/Bus.py @@ -1,4 +1,4 @@ -from m5 import * +from m5.config import * from MemObject import MemObject class Bus(MemObject): diff --git a/src/python/m5/objects/CoherenceProtocol.py b/src/python/m5/objects/CoherenceProtocol.py index 7013000d6..64b6cbacf 100644 --- a/src/python/m5/objects/CoherenceProtocol.py +++ b/src/python/m5/objects/CoherenceProtocol.py @@ -1,4 +1,4 @@ -from m5 import * +from m5.config import * class Coherence(Enum): vals = ['uni', 'msi', 'mesi', 'mosi', 'moesi'] class CoherenceProtocol(SimObject): diff --git a/src/python/m5/objects/Device.py b/src/python/m5/objects/Device.py index 2a71bbc65..7798f5f04 100644 --- a/src/python/m5/objects/Device.py +++ b/src/python/m5/objects/Device.py @@ -1,4 +1,4 @@ -from m5 import * +from m5.config import * from MemObject import MemObject class PioDevice(MemObject): diff --git a/src/python/m5/objects/DiskImage.py b/src/python/m5/objects/DiskImage.py index 0d55e9329..70d8b2e45 100644 --- a/src/python/m5/objects/DiskImage.py +++ b/src/python/m5/objects/DiskImage.py @@ -1,4 +1,4 @@ -from m5 import * +from m5.config import * class DiskImage(SimObject): type = 'DiskImage' abstract = True diff --git a/src/python/m5/objects/Ethernet.py b/src/python/m5/objects/Ethernet.py index 4286c71c8..418670592 100644 --- a/src/python/m5/objects/Ethernet.py +++ b/src/python/m5/objects/Ethernet.py @@ -1,4 +1,5 @@ -from m5 import * +from m5 import build_env +from m5.config import * from Device import DmaDevice from Pci import PciDevice diff --git a/src/python/m5/objects/Ide.py b/src/python/m5/objects/Ide.py index 2403e6d36..9ee578177 100644 --- a/src/python/m5/objects/Ide.py +++ b/src/python/m5/objects/Ide.py @@ -1,4 +1,4 @@ -from m5 import * +from m5.config import * from Pci import PciDevice class IdeID(Enum): vals = ['master', 'slave'] diff --git a/src/python/m5/objects/IntrControl.py b/src/python/m5/objects/IntrControl.py index 66c82c182..514c3fc62 100644 --- a/src/python/m5/objects/IntrControl.py +++ b/src/python/m5/objects/IntrControl.py @@ -1,4 +1,4 @@ -from m5 import * +from m5.config import * class IntrControl(SimObject): type = 'IntrControl' cpu = Param.BaseCPU(Parent.any, "the cpu") diff --git a/src/python/m5/objects/MemObject.py b/src/python/m5/objects/MemObject.py index 4d68243e6..d957dae17 100644 --- a/src/python/m5/objects/MemObject.py +++ b/src/python/m5/objects/MemObject.py @@ -1,4 +1,4 @@ -from m5 import * +from m5.config import * class MemObject(SimObject): type = 'MemObject' diff --git a/src/python/m5/objects/MemTest.py b/src/python/m5/objects/MemTest.py index 34299faf0..9916d7cb4 100644 --- a/src/python/m5/objects/MemTest.py +++ b/src/python/m5/objects/MemTest.py @@ -1,4 +1,4 @@ -from m5 import * +from m5.config import * class MemTest(SimObject): type = 'MemTest' cache = Param.BaseCache("L1 cache") diff --git a/src/python/m5/objects/Pci.py b/src/python/m5/objects/Pci.py index 85cefcd44..9e1e91b13 100644 --- a/src/python/m5/objects/Pci.py +++ b/src/python/m5/objects/Pci.py @@ -1,4 +1,4 @@ -from m5 import * +from m5.config import * from Device import BasicPioDevice, DmaDevice class PciConfigData(SimObject): diff --git a/src/python/m5/objects/PhysicalMemory.py b/src/python/m5/objects/PhysicalMemory.py index c59910093..bed90d555 100644 --- a/src/python/m5/objects/PhysicalMemory.py +++ b/src/python/m5/objects/PhysicalMemory.py @@ -1,4 +1,4 @@ -from m5 import * +from m5.config import * from MemObject import * class PhysicalMemory(MemObject): diff --git a/src/python/m5/objects/Platform.py b/src/python/m5/objects/Platform.py index 4da0ffab4..89fee9991 100644 --- a/src/python/m5/objects/Platform.py +++ b/src/python/m5/objects/Platform.py @@ -1,4 +1,4 @@ -from m5 import * +from m5.config import * class Platform(SimObject): type = 'Platform' abstract = True diff --git a/src/python/m5/objects/Process.py b/src/python/m5/objects/Process.py index 60b00229e..0091d8654 100644 --- a/src/python/m5/objects/Process.py +++ b/src/python/m5/objects/Process.py @@ -1,4 +1,4 @@ -from m5 import * +from m5.config import * class Process(SimObject): type = 'Process' abstract = True diff --git a/src/python/m5/objects/Repl.py b/src/python/m5/objects/Repl.py index afd256082..8e9f1094f 100644 --- a/src/python/m5/objects/Repl.py +++ b/src/python/m5/objects/Repl.py @@ -1,4 +1,4 @@ -from m5 import * +from m5.config import * class Repl(SimObject): type = 'Repl' abstract = True diff --git a/src/python/m5/objects/Root.py b/src/python/m5/objects/Root.py index 205a93c76..373475a7a 100644 --- a/src/python/m5/objects/Root.py +++ b/src/python/m5/objects/Root.py @@ -1,4 +1,4 @@ -from m5 import * +from m5.config import * from Serialize import Serialize from Statistics import Statistics from Trace import Trace diff --git a/src/python/m5/objects/SimConsole.py b/src/python/m5/objects/SimConsole.py index df3061908..9e1452c6d 100644 --- a/src/python/m5/objects/SimConsole.py +++ b/src/python/m5/objects/SimConsole.py @@ -1,4 +1,4 @@ -from m5 import * +from m5.config import * class ConsoleListener(SimObject): type = 'ConsoleListener' port = Param.TcpPort(3456, "listen port") diff --git a/src/python/m5/objects/SimpleDisk.py b/src/python/m5/objects/SimpleDisk.py index e34155ace..44ef709af 100644 --- a/src/python/m5/objects/SimpleDisk.py +++ b/src/python/m5/objects/SimpleDisk.py @@ -1,4 +1,4 @@ -from m5 import * +from m5.config import * class SimpleDisk(SimObject): type = 'SimpleDisk' disk = Param.DiskImage("Disk Image") diff --git a/src/python/m5/objects/System.py b/src/python/m5/objects/System.py index 622b5a870..a8063a274 100644 --- a/src/python/m5/objects/System.py +++ b/src/python/m5/objects/System.py @@ -1,4 +1,5 @@ -from m5 import * +from m5 import build_env +from m5.config import * class System(SimObject): type = 'System' diff --git a/src/python/m5/objects/Tsunami.py b/src/python/m5/objects/Tsunami.py index 27ea0bce8..4613571d8 100644 --- a/src/python/m5/objects/Tsunami.py +++ b/src/python/m5/objects/Tsunami.py @@ -1,4 +1,4 @@ -from m5 import * +from m5.config import * from Device import BasicPioDevice from Platform import Platform diff --git a/src/python/m5/objects/Uart.py b/src/python/m5/objects/Uart.py index 54754aeb9..8e1fd1a37 100644 --- a/src/python/m5/objects/Uart.py +++ b/src/python/m5/objects/Uart.py @@ -1,4 +1,5 @@ -from m5 import * +from m5 import build_env +from m5.config import * from Device import BasicPioDevice class Uart(BasicPioDevice): diff --git a/src/sim/eventq.cc b/src/sim/eventq.cc index d90d0923a..6ae838897 100644 --- a/src/sim/eventq.cc +++ b/src/sim/eventq.cc @@ -102,7 +102,7 @@ EventQueue::remove(Event *event) prev->next = curr->next; } -void +Event * EventQueue::serviceOne() { Event *event = head; @@ -110,13 +110,20 @@ EventQueue::serviceOne() head = event->next; // handle action - if (!event->squashed()) + if (!event->squashed()) { event->process(); - else + if (event->isExitEvent()) { + assert(!event->getFlags(Event::AutoDelete)); // would be silly + return event; + } + } else { event->clearFlags(Event::Squashed); + } if (event->getFlags(Event::AutoDelete) && !event->scheduled()) delete event; + + return NULL; } diff --git a/src/sim/eventq.hh b/src/sim/eventq.hh index a5cc0c1b6..430473df3 100644 --- a/src/sim/eventq.hh +++ b/src/sim/eventq.hh @@ -90,7 +90,8 @@ class Event : public Serializable, public FastAlloc Squashed = 0x1, Scheduled = 0x2, AutoDelete = 0x4, - AutoSerialize = 0x8 + AutoSerialize = 0x8, + IsExitEvent = 0x10 }; bool getFlags(Flags f) const { return (_flags & f) == f; } @@ -214,6 +215,9 @@ class Event : public Serializable, public FastAlloc /// Check whether the event is squashed bool squashed() { return getFlags(Squashed); } + /// See if this is a SimExitEvent (without resorting to RTTI) + bool isExitEvent() { return getFlags(IsExitEvent); } + /// Get the time that the event is scheduled Tick when() const { return _when; } @@ -298,7 +302,7 @@ class EventQueue : public Serializable void reschedule(Event *ev); Tick nextTick() { return head->when(); } - void serviceOne(); + Event *serviceOne(); // process all events up to the given timestamp. we inline a // quick test to see if there are any events to process; if so, diff --git a/src/sim/host.hh b/src/sim/host.hh index 84870714a..9c79580b1 100644 --- a/src/sim/host.hh +++ b/src/sim/host.hh @@ -56,6 +56,8 @@ typedef int64_t Counter; */ typedef int64_t Tick; +const Tick MaxTick = (1LL << 62); + /** * Address type * This will probably be moved somewhere else in the near future. diff --git a/src/sim/main.cc b/src/sim/main.cc index 7728e258a..741926056 100644 --- a/src/sim/main.cc +++ b/src/sim/main.cc @@ -41,11 +41,13 @@ #include #include #include +#include #include #include #include +#include "base/callback.hh" #include "base/inifile.hh" #include "base/misc.hh" #include "base/output.hh" @@ -111,50 +113,39 @@ abortHandler(int sigtype) #endif } -/// Simulator executable name -char *myProgName = ""; -/// -/// Echo the command line for posterity in such a way that it can be -/// used to rerun the same simulation (given the same .ini files). -/// +const char *briefCopyright = +"Copyright (c) 2001-2006\n" +"The Regents of The University of Michigan\n" +"All Rights Reserved\n"; + +/// Print welcome message. void -echoCommandLine(int argc, char **argv, ostream &out) +sayHello(ostream &out) { - out << "command line: " << argv[0]; - for (int i = 1; i < argc; i++) { - string arg(argv[i]); + extern const char *compileDate; // from date.cc - out << ' '; + ccprintf(out, "M5 Simulator System\n"); + // display copyright + ccprintf(out, "%s\n", briefCopyright); + ccprintf(out, "M5 compiled %d\n", compileDate); + ccprintf(out, "M5 started %s\n", Time::start); - // If the arg contains spaces, we need to quote it. - // The rest of this is overkill to make it look purty. + char *host = getenv("HOSTNAME"); + if (!host) + host = getenv("HOST"); - // print dashes first outside quotes - int non_dash_pos = arg.find_first_not_of("-"); - out << arg.substr(0, non_dash_pos); // print dashes - string body = arg.substr(non_dash_pos); // the rest + if (host) + ccprintf(out, "M5 executing on %s\n", host); +} - // if it's an assignment, handle the lhs & rhs separately - int eq_pos = body.find("="); - if (eq_pos == string::npos) { - out << quote(body); - } - else { - string lhs(body.substr(0, eq_pos)); - string rhs(body.substr(eq_pos + 1)); - out << quote(lhs) << "=" << quote(rhs); - } - } - out << endl << endl; -} +extern "C" { void init_main(); } int main(int argc, char **argv) { - // Save off program name - myProgName = argv[0]; + sayHello(cerr); signal(SIGFPE, SIG_IGN); // may occur on misspeculated paths signal(SIGTRAP, SIG_IGN); @@ -163,37 +154,108 @@ main(int argc, char **argv) signal(SIGINT, exitNowHandler); // dump final stats and exit signal(SIGABRT, abortHandler); - // Python embedded interpreter invocation Py_SetProgramName(argv[0]); - const char *fileName = Py_GetProgramFullPath(); + + // default path to m5 python code is the currently executing + // file... Python ZipImporter will find embedded zip archive + char *pythonpath = argv[0]; + + bool interactive = false; + bool getopt_done = false; + do { + switch (getopt(argc, argv, "+p:i")) { + // -p prepends to PYTHONPATH instead of + // using built-in zip archive. Useful when + // developing/debugging changes to built-in Python + // libraries, as the new Python can be tested without + // building a new m5 binary. + case 'p': + pythonpath = optarg; + break; + + // -i forces entry into interactive mode after the + // supplied script is executed (just like the -i option to + // the Python interpreter). + case 'i': + interactive = true; + break; + + case -1: + getopt_done = true; + break; + + default: + fatal("Unrecognized option %c\n", optopt); + } + } while (!getopt_done); + + // Fix up argc & argv to hide arguments we just processed. + // getopt() sets optind to the index of the first non-processed + // argv element. + argc -= optind; + argv += optind; + + // Set up PYTHONPATH to make sure the m5 module is found + string newpath(pythonpath); + + char *oldpath = getenv("PYTHONPATH"); + if (oldpath != NULL) { + newpath += ":"; + newpath += oldpath; + } + + if (setenv("PYTHONPATH", newpath.c_str(), true) == -1) + fatal("setenv: %s\n", strerror(errno)); + + // initialize embedded Python interpreter Py_Initialize(); PySys_SetArgv(argc, argv); - // loadSwigModules(); - - // Set Python module path to include current file to find embedded - // zip archive - if (PyRun_SimpleString("import sys") != 0) - panic("Python error importing 'sys' module\n"); - string pathCmd = csprintf("sys.path[1:1] = ['%s']", fileName); - if (PyRun_SimpleString(pathCmd.c_str()) != 0) - panic("Python error setting sys.path\n"); - - // Pass compile timestamp string to Python - extern const char *compileDate; // from date.cc - string setCompileDate = csprintf("compileDate = '%s'", compileDate); - if (PyRun_SimpleString(setCompileDate.c_str()) != 0) - panic("Python error setting compileDate\n"); - - // PyRun_InteractiveLoop(stdin, "stdin"); - // m5/__init__.py currently contains main argv parsing loop etc., - // and will write out config.ini file before returning. - if (PyImport_ImportModule("defines") == NULL) - panic("Python error importing 'defines.py'\n"); - if (PyImport_ImportModule("m5") == NULL) - panic("Python error importing 'm5' module\n"); + // initialize SWIG 'main' module + init_main(); + + if (argc > 0) { + // extra arg(s): first is script file, remaining ones are args + // to script file + char *filename = argv[0]; + FILE *fp = fopen(filename, "r"); + if (!fp) { + fatal("cannot open file '%s'\n", filename); + } + + PyRun_AnyFile(fp, filename); + } else { + // no script file argument... force interactive prompt + interactive = true; + } + + if (interactive) { + // The following code to import readline was copied from Python + // 2.4.3's Modules/main.c. + // Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006 + // Python Software Foundation; All Rights Reserved + // We should only enable this if we're actually using an + // interactive prompt. + PyObject *v; + v = PyImport_ImportModule("readline"); + if (v == NULL) + PyErr_Clear(); + else + Py_DECREF(v); + + PyRun_InteractiveLoop(stdin, "stdin"); + } + + // clean up Python intepreter. Py_Finalize(); +} + +/// Initialize C++ configuration. Exported to Python via SWIG; invoked +/// from m5.instantiate(). +void +initialize() +{ configStream = simout.find("config.out"); // The configuration database is now complete; start processing it. @@ -212,8 +274,7 @@ main(int argc, char **argv) ParamContext::parseAllContexts(inifile); ParamContext::checkAllContexts(); - // Echo command line and all parameter settings to stats file as well. - echoCommandLine(argc, argv, *outputStream); + // Echo all parameter settings to stats file as well. ParamContext::showAllContexts(*configStream); // Any objects that can't connect themselves until after construction should @@ -244,16 +305,61 @@ main(int argc, char **argv) // Reset to put the stats in a consistent state. Stats::reset(); - warn("Entering event queue. Starting simulation...\n"); SimStartup(); - while (!mainEventQueue.empty()) { +} + + +/** Simulate for num_cycles additional cycles. If num_cycles is -1 + * (the default), do not limit simulation; some other event must + * terminate the loop. Exported to Python via SWIG. + * @return The SimLoopExitEvent that caused the loop to exit. + */ +SimLoopExitEvent * +simulate(Tick num_cycles = -1) +{ + warn("Entering event queue @ %d. Starting simulation...\n", curTick); + + // Fix up num_cycles. Special default value -1 means simulate + // "forever"... schedule event at MaxTick just to be safe. + // Otherwise it's a delta for additional cycles to simulate past + // curTick, and thus must be non-negative. + if (num_cycles == -1) + num_cycles = MaxTick; + else if (num_cycles < 0) + fatal("simulate: num_cycles must be >= 0 (was %d)\n", num_cycles); + else + num_cycles = curTick + num_cycles; + + Event *limit_event = new SimLoopExitEvent(num_cycles, + "simulate() limit reached"); + + while (1) { + // there should always be at least one event (the SimLoopExitEvent + // we just scheduled) in the queue + assert(!mainEventQueue.empty()); assert(curTick <= mainEventQueue.nextTick() && "event scheduled in the past"); // forward current cycle to the time of the first event on the // queue curTick = mainEventQueue.nextTick(); - mainEventQueue.serviceOne(); + Event *exit_event = mainEventQueue.serviceOne(); + if (exit_event != NULL) { + // hit some kind of exit event; return to Python + // event must be subclass of SimLoopExitEvent... + SimLoopExitEvent *se_event = dynamic_cast(exit_event); + if (se_event == NULL) + panic("Bogus exit event class!"); + + // if we didn't hit limit_event, delete it + if (se_event != limit_event) { + assert(limit_event->scheduled()); + limit_event->deschedule(); + delete limit_event; + } + + return se_event; + } if (async_event) { async_event = false; @@ -273,7 +379,7 @@ main(int argc, char **argv) if (async_exit) { async_exit = false; - new SimExitEvent("User requested STOP"); + exitSimLoop("user interrupt received"); } if (async_io || async_alarm) { @@ -284,11 +390,37 @@ main(int argc, char **argv) } } - // This should never happen... every conceivable way for the - // simulation to terminate (hit max cycles/insts, signal, - // simulated system halts/exits) generates an exit event, so we - // should never run out of events on the queue. - exitNow("no events on event loop! All CPUs must be idle.", 1); + // not reached... only exit is return on SimLoopExitEvent +} + +/** + * Queue of C++ callbacks to invoke on simulator exit. + */ +CallbackQueue exitCallbacks; + +/** + * Register an exit callback. + */ +void +registerExitCallback(Callback *callback) +{ + exitCallbacks.add(callback); +} + +/** + * Do C++ simulator exit processing. Exported to SWIG to be invoked + * when simulator terminates via Python's atexit mechanism. + */ +void +doExitCleanup() +{ + exitCallbacks.process(); + exitCallbacks.clear(); + + cout.flush(); + + ParamContext::cleanupAllContexts(); - return 0; + // print simulation stats + Stats::DumpNow(); } diff --git a/src/sim/pseudo_inst.cc b/src/sim/pseudo_inst.cc index d9064bf73..663e5745f 100644 --- a/src/sim/pseudo_inst.cc +++ b/src/sim/pseudo_inst.cc @@ -134,14 +134,14 @@ namespace AlphaPseudo void m5exit_old(ExecContext *xc) { - SimExit(curTick, "m5_exit_old instruction encountered"); + exitSimLoop(curTick, "m5_exit_old instruction encountered"); } void m5exit(ExecContext *xc, Tick delay) { Tick when = curTick + delay * Clock::Int::ns; - SimExit(when, "m5_exit instruction encountered"); + exitSimLoop(when, "m5_exit instruction encountered"); } void diff --git a/src/sim/root.cc b/src/sim/root.cc index 37b768bf0..ec5e2f7e2 100644 --- a/src/sim/root.cc +++ b/src/sim/root.cc @@ -40,6 +40,7 @@ #include "sim/builder.hh" #include "sim/host.hh" #include "sim/sim_events.hh" +#include "sim/sim_exit.hh" #include "sim/sim_object.hh" #include "sim/root.hh" @@ -99,7 +100,7 @@ void Root::startup() { if (max_tick != 0) - new SimExitEvent(curTick + max_tick, "reached maximum cycle count"); + exitSimLoop(curTick + max_tick, "reached maximum cycle count"); if (progress_interval != 0) new ProgressEvent(&mainEventQueue, progress_interval); diff --git a/src/sim/serialize.cc b/src/sim/serialize.cc index 3c86826a6..5270802d1 100644 --- a/src/sim/serialize.cc +++ b/src/sim/serialize.cc @@ -248,7 +248,7 @@ Serializable::serializeAll() assert(Serializable::ckptPrevCount + 1 == Serializable::ckptCount); Serializable::ckptPrevCount++; if (ckptMaxCount && ++ckptCount >= ckptMaxCount) - SimExit(curTick + 1, "Maximum number of checkpoints dropped"); + exitSimLoop(curTick + 1, "Maximum number of checkpoints dropped"); } diff --git a/src/sim/sim_events.cc b/src/sim/sim_events.cc index 2aa0508ef..b7901832d 100644 --- a/src/sim/sim_events.cc +++ b/src/sim/sim_events.cc @@ -45,26 +45,37 @@ using namespace std; // handle termination event // void -SimExitEvent::process() +SimLoopExitEvent::process() { - // This event does not autodelete because exitNow may be called, - // and the function will never be allowed to finish. - if (theQueue() == &mainEventQueue) { - string _cause = cause; - int _code = code; - delete this; - exitNow(_cause, _code); - } else { - new SimExitEvent(cause, code); + // if this got scheduled on a different queue (e.g. the committed + // instruction queue) then make a corresponding event on the main + // queue. + if (theQueue() != &mainEventQueue) { + exitSimLoop(cause, code); delete this; } + + // otherwise do nothing... the IsExitEvent flag takes care of + // exiting the simulation loop and returning this object to Python } const char * -SimExitEvent::description() +SimLoopExitEvent::description() +{ + return "simulation loop exit"; +} + +void +exitSimLoop(Tick when, const std::string &message, int exit_code) +{ + new SimLoopExitEvent(when, message, exit_code); +} + +void +exitSimLoop(const std::string &message, int exit_code) { - return "simulation termination"; + exitSimLoop(curTick, message, exit_code); } // @@ -90,7 +101,7 @@ void CountedExitEvent::process() { if (--downCounter == 0) { - new SimExitEvent(cause, 0); + exitSimLoop(cause, 0); } } @@ -119,7 +130,7 @@ CheckSwapEvent::process() if (swap < 100) { cerr << "\a\aAborting Simulation! Inadequate swap space!\n\n"; - new SimExitEvent("Lack of swap space"); + exitSimLoop("Lack of swap space"); } schedule(curTick + interval); diff --git a/src/sim/sim_events.hh b/src/sim/sim_events.hh index 89bf83fc9..4f305ad38 100644 --- a/src/sim/sim_events.hh +++ b/src/sim/sim_events.hh @@ -36,7 +36,7 @@ // // Event to terminate simulation at a particular cycle/instruction // -class SimExitEvent : public Event +class SimLoopExitEvent : public Event { private: // string explaining why we're terminating @@ -44,24 +44,18 @@ class SimExitEvent : public Event int code; public: - SimExitEvent(const std::string &_cause, int c = 0) + SimLoopExitEvent(Tick _when, const std::string &_cause, int c = 0) : Event(&mainEventQueue, Sim_Exit_Pri), cause(_cause), code(c) - { schedule(curTick); } + { setFlags(IsExitEvent); schedule(_when); } - SimExitEvent(Tick _when, const std::string &_cause, int c = 0) - : Event(&mainEventQueue, Sim_Exit_Pri), cause(_cause), - code(c) - { schedule(_when); } - - SimExitEvent(EventQueue *q, const std::string &_cause, int c = 0) + SimLoopExitEvent(EventQueue *q, + Tick _when, const std::string &_cause, int c = 0) : Event(q, Sim_Exit_Pri), cause(_cause), code(c) - { schedule(curTick); } + { setFlags(IsExitEvent); schedule(_when); } - SimExitEvent(EventQueue *q, Tick _when, const std::string &_cause, - int c = 0) - : Event(q, Sim_Exit_Pri), cause(_cause), code(c) - { schedule(_when); } + std::string getCause() { return cause; } + int getCode() { return code; } void process(); // process event diff --git a/src/sim/sim_exit.hh b/src/sim/sim_exit.hh index f1cf4b5fc..545bf4ae0 100644 --- a/src/sim/sim_exit.hh +++ b/src/sim/sim_exit.hh @@ -36,12 +36,23 @@ #include "sim/host.hh" +// forward declaration class Callback; +/// Register a callback to be called when Python exits. Defined in +/// sim/main.cc. void registerExitCallback(Callback *); -void exitNow(const std::string &cause, int exit_code); -void exitNow(const char *cause, int exit_code); -void SimExit(Tick when, const char *message); +/// Schedule an event to exit the simulation loop (returning to +/// Python) at the indicated tick. The message and exit_code +/// parameters are saved in the SimLoopExitEvent to indicate why the +/// exit occurred. +void exitSimLoop(Tick when, const std::string &message, int exit_code = 0); + +/// Schedule an event to exit the simulation loop (returning to +/// Python) at the end of the current cycle (curTick). The message +/// and exit_code parameters are saved in the SimLoopExitEvent to +/// indicate why the exit occurred. +void exitSimLoop(const std::string &cause, int exit_code = 0); #endif // __SIM_EXIT_HH__ diff --git a/src/sim/syscall_emul.cc b/src/sim/syscall_emul.cc index e37fea1b1..bc33625af 100644 --- a/src/sim/syscall_emul.cc +++ b/src/sim/syscall_emul.cc @@ -43,7 +43,7 @@ #include "mem/page_table.hh" #include "sim/process.hh" -#include "sim/sim_events.hh" +#include "sim/sim_exit.hh" using namespace std; using namespace TheISA; @@ -91,7 +91,7 @@ SyscallReturn exitFunc(SyscallDesc *desc, int callnum, Process *process, ExecContext *xc) { - new SimExitEvent("target called exit()", xc->getSyscallArg(0) & 0xff); + exitSimLoop("target called exit()", xc->getSyscallArg(0) & 0xff); return 1; }