Move main control from C++ into Python.
authorSteve Reinhardt <stever@eecs.umich.edu>
Sat, 10 Jun 2006 03:01:31 +0000 (23:01 -0400)
committerSteve Reinhardt <stever@eecs.umich.edu>
Sat, 10 Jun 2006 03:01:31 +0000 (23:01 -0400)
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

51 files changed:
SConstruct
configs/test/test.py
src/SConscript
src/arch/SConscript
src/arch/alpha/ev5.cc
src/arch/alpha/isa/decoder.isa
src/cpu/base.cc
src/cpu/o3/alpha_cpu_impl.hh
src/cpu/trace/opt_cpu.cc
src/cpu/trace/trace_cpu.cc
src/python/SConscript
src/python/m5/__init__.py
src/python/m5/config.py
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
src/sim/eventq.cc
src/sim/eventq.hh
src/sim/host.hh
src/sim/main.cc
src/sim/pseudo_inst.cc
src/sim/root.cc
src/sim/serialize.cc
src/sim/sim_events.cc
src/sim/sim_events.hh
src/sim/sim_exit.hh
src/sim/syscall_emul.cc

index 5e2e2711d72cd9df2be9f39e994f8f1346d9ed6b..742bd932a1c687f2c7f11eb9c44227e5ba0cb8b7 100644 (file)
@@ -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:
index 75e832f5ef116469fc3985682730d48bea31e9d3..0c6359148696ff666a290e1381a11ba1492b1032 100644 (file)
@@ -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()
+
index 39e4b435a057eb9167d2a658b1f08c9e85d7009a..645d07e5d61924ee3c39995bfe7ce250b85bccf1 100644 (file)
@@ -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 = []
 
index dd616174c3d573bf8c27fb35008a0858910acac1..c90694a68b59295edd5a450f8198542f18f20685 100644 (file)
@@ -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')
index 12782d646b1a62ef2282ff317f57bf903e2aa00a..4d0482019fa94b2d75f02a892c31f5af87cfdba0 100644 (file)
@@ -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:
index eaa94a842c8c2daeb3294b3538e14284c64f5727..17132bbc2528f1f9d942069ebcd03ba77e2f3b0c 100644 (file)
@@ -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);
index 64d81ef4b37654095cfab38f9d45a08d1df31008..59324e5379281b026201f8eedbe6a4dd91880251 100644 (file)
@@ -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
index 446385eadb18c3031ce6e4708588fe0f2058b9cd..624fe859422d41d6d7c26f750cc75fffc60cc439 100644 (file)
@@ -292,7 +292,7 @@ AlphaFullCPU<Impl>::simPalCheck(int palFunc)
       case PAL::halt:
         halt();
         if (--System::numSystemsRunning == 0)
-            new SimExitEvent("all cpus halted");
+            exitSimLoop("all cpus halted");
         break;
 
       case PAL::bpt:
index 098031d4a075e557a9baf813515cd825e97557df..996e89f0137ecda101c9b3d3fb609914747df564 100644 (file)
@@ -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
index 4df47229fba77febce4f1e1d16d18df6250443c5..3c9da4849cfdc87b5f353f26ab0badb59660a684 100644 (file)
@@ -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));
         }
index 5a787cfdf5d1785da429f9b5fa0ab1598399b829..7b0f591eb83ff389093c6f7c7da2d32acf6eb08e 100644 (file)
@@ -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):
index c44a9ad1f0446fcdc7db0debad02164cc7e8ba6f..60a61d66eb553e96efe8c32eb57b4c721afcf52c 100644 (file)
 # 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 --<python var>=<string value>
-        try:
-            (var, val) = arg.split('=', 1)
-        except ValueError:
-            panic("Could not parse configuration argument '%s'\n"
-                  "Expecting --<variable>=<value>\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
index d1471c807e1b19c0797f06d458ba9cf5aad54488..f6a2a84fbeadda75ee1cc4ff1e2e8392769361d4 100644 (file)
@@ -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']
 
index 68e6089abd6539c6be0ea0be8d111fbb8e76d765..329b8c5bd4f36696fb149855762f04a130d1e4c9 100644 (file)
@@ -1,4 +1,4 @@
-from m5 import *
+from m5.config import *
 from Device import BasicPioDevice
 
 class AlphaConsole(BasicPioDevice):
index 48989d05788e9dfbbcc367a88108f2eb0567a28b..759ab3cc6b9b5479afc8360d5aa49ca79c402305 100644 (file)
@@ -1,4 +1,5 @@
-from m5 import *
+from m5 import build_env
+from m5.config import *
 from BaseCPU import BaseCPU
 
 class DerivAlphaFullCPU(BaseCPU):
index 5edf8e13d88956b90176be936d852567dd72ee1d..11c1792eedae01b8b3466dad1e5c2cc120c090fa 100644 (file)
@@ -1,4 +1,4 @@
-from m5 import *
+from m5.config import *
 class AlphaTLB(SimObject):
     type = 'AlphaTLB'
     abstract = True
index 9cb9a8f03e382d50b1dbee8291ed9544e7358a64..186b733fa1b6581e6e9ae43e03ba7161859f4ec3 100644 (file)
@@ -1,4 +1,4 @@
-from m5 import *
+from m5.config import *
 from Device import BasicPioDevice
 
 class BadDevice(BasicPioDevice):
index 49cb2a8f36d1cd30039a091a7583ccfe230948ba..2e78578dfe28254be324e1004afa6e23bb60aa7d 100644 (file)
@@ -1,4 +1,6 @@
-from m5 import *
+from m5 import build_env
+from m5.config import *
+
 class BaseCPU(SimObject):
     type = 'BaseCPU'
     abstract = True
index 79d21572a655aaa5075dfe645eae6a40b0f3f93d..33f44759bf96e5e6cfc6687a0a20c61549a90f14 100644 (file)
@@ -1,4 +1,4 @@
-from m5 import *
+from m5.config import *
 from BaseMem import BaseMem
 
 class Prefetch(Enum): vals = ['none', 'tagged', 'stride', 'ghb']
index ada715ce9817b0e9b7d89de59c5b730a73d38b2e..88053575527c1614520f25056c650fe7d8ad5942 100644 (file)
@@ -1,4 +1,4 @@
-from m5 import *
+from m5.config import *
 from MemObject import MemObject
 
 class Bridge(MemObject):
index 8c539728150cb373657b417d14edd61a683d9196..c37dab438ab8232370dd28e5551dc2dfd3643f79 100644 (file)
@@ -1,4 +1,4 @@
-from m5 import *
+from m5.config import *
 from MemObject import MemObject
 
 class Bus(MemObject):
index 7013000d60c336327021e546abf3c6ae8d3c779f..64b6cbacf5fd6c1c25800b1ad417fde36f8bbe2f 100644 (file)
@@ -1,4 +1,4 @@
-from m5 import *
+from m5.config import *
 class Coherence(Enum): vals = ['uni', 'msi', 'mesi', 'mosi', 'moesi']
 
 class CoherenceProtocol(SimObject):
index 2a71bbc6509ca0e61c6555d55bdef6555fa84b93..7798f5f048906d7a30dac0d2af7070b7d75570c4 100644 (file)
@@ -1,4 +1,4 @@
-from m5 import *
+from m5.config import *
 from MemObject import MemObject
 
 class PioDevice(MemObject):
index 0d55e9329893f2f4867abee8b9d3fcc232b138f6..70d8b2e454e429453f93124e2674b6bcd23acefd 100644 (file)
@@ -1,4 +1,4 @@
-from m5 import *
+from m5.config import *
 class DiskImage(SimObject):
     type = 'DiskImage'
     abstract = True
index 4286c71c8654b050078d5cc459903b8675d21e3c..4186705927b97e4a3981d7bdc64efa8ec2a0557e 100644 (file)
@@ -1,4 +1,5 @@
-from m5 import *
+from m5 import build_env
+from m5.config import *
 from Device import DmaDevice
 from Pci import PciDevice
 
index 2403e6d36909945f939dd24516d2d7660dece639..9ee578177db62831653ec1da35e0bec7bb1fbf5a 100644 (file)
@@ -1,4 +1,4 @@
-from m5 import *
+from m5.config import *
 from Pci import PciDevice
 
 class IdeID(Enum): vals = ['master', 'slave']
index 66c82c1822982d0e0177e5843926507fcbb235a2..514c3fc624f2ca89e42e9c62945b64dbbe9d5a82 100644 (file)
@@ -1,4 +1,4 @@
-from m5 import *
+from m5.config import *
 class IntrControl(SimObject):
     type = 'IntrControl'
     cpu = Param.BaseCPU(Parent.any, "the cpu")
index 4d68243e62ee36396d38f20bc40f305dba94cc32..d957dae17160c4384548e0eff8a2d962776cfe41 100644 (file)
@@ -1,4 +1,4 @@
-from m5 import *
+from m5.config import *
 
 class MemObject(SimObject):
     type = 'MemObject'
index 34299faf05004fa41a77200c0f968f9d4f2779f2..9916d7cb44daf2cd47c1dfb943258f613cece0e4 100644 (file)
@@ -1,4 +1,4 @@
-from m5 import *
+from m5.config import *
 class MemTest(SimObject):
     type = 'MemTest'
     cache = Param.BaseCache("L1 cache")
index 85cefcd4429b7dc92ab34ccfc846e41eb3e43daf..9e1e91b1306cf4e47789fcce967c69c16701d3f5 100644 (file)
@@ -1,4 +1,4 @@
-from m5 import *
+from m5.config import *
 from Device import BasicPioDevice, DmaDevice
 
 class PciConfigData(SimObject):
index c59910093e4c12d6b31ce03345d4069fd7c24edf..bed90d555918fe142005b1a3891d8e57ac2ae400 100644 (file)
@@ -1,4 +1,4 @@
-from m5 import *
+from m5.config import *
 from MemObject import *
 
 class PhysicalMemory(MemObject):
index 4da0ffab407cfaa03ebcd53a13815e511d0a3175..89fee9991edffb266a22f52e93ec6e0d6d3ee2ce 100644 (file)
@@ -1,4 +1,4 @@
-from m5 import *
+from m5.config import *
 class Platform(SimObject):
     type = 'Platform'
     abstract = True
index 60b00229e111e7f39115e226c2c757d1a8e97d01..0091d8654e7b824b2663d0634e17cfcc5bb4fee7 100644 (file)
@@ -1,4 +1,4 @@
-from m5 import *
+from m5.config import *
 class Process(SimObject):
     type = 'Process'
     abstract = True
index afd2560824ede7a39bd6ad417a7d9be189f7c75d..8e9f1094fb74900e44d546cd1a73d8d8e753e16e 100644 (file)
@@ -1,4 +1,4 @@
-from m5 import *
+from m5.config import *
 class Repl(SimObject):
     type = 'Repl'
     abstract = True
index 205a93c760a2fdbb74806b38903f5e67f517530b..373475a7a86c43a600dd0c1c7b48114fb4b0ef19 100644 (file)
@@ -1,4 +1,4 @@
-from m5 import *
+from m5.config import *
 from Serialize import Serialize
 from Statistics import Statistics
 from Trace import Trace
index df306190891b9304ca604edf0778a994a9982a04..9e1452c6d31369fd225948122ad46474fe2b81a8 100644 (file)
@@ -1,4 +1,4 @@
-from m5 import *
+from m5.config import *
 class ConsoleListener(SimObject):
     type = 'ConsoleListener'
     port = Param.TcpPort(3456, "listen port")
index e34155ace6f113fe28b6313492d4597aacfddf47..44ef709af4522420ddffd4e7a4d2b7f3e37b4468 100644 (file)
@@ -1,4 +1,4 @@
-from m5 import *
+from m5.config import *
 class SimpleDisk(SimObject):
     type = 'SimpleDisk'
     disk = Param.DiskImage("Disk Image")
index 622b5a8700d5011c5cfede249cfb4e49b5ffe87b..a8063a274f4241269d8d5f92a371fa0b1da7aaff 100644 (file)
@@ -1,4 +1,5 @@
-from m5 import *
+from m5 import build_env
+from m5.config import *
 
 class System(SimObject):
     type = 'System'
index 27ea0bce8e552dcd0dc553bcf38aa9438f6b63f9..4613571d8849c43fc623c152a604f6eeed8e09a4 100644 (file)
@@ -1,4 +1,4 @@
-from m5 import *
+from m5.config import *
 from Device import BasicPioDevice
 from Platform import Platform
 
index 54754aeb9306a243e09f731aa71b46370ed757f0..8e1fd1a372e121a507a4cf2d9953a181054dbec3 100644 (file)
@@ -1,4 +1,5 @@
-from m5 import *
+from m5 import build_env
+from m5.config import *
 from Device import BasicPioDevice
 
 class Uart(BasicPioDevice):
index d90d0923a6cc9ed15b8178f8225d44768eda4134..6ae838897b46f58e5054de7e061192d8416a3d5a 100644 (file)
@@ -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;
 }
 
 
index a5cc0c1b610d2cf59b5e98dd592de359643433c2..430473df32eadf1fef767e41ffe39166b88d3d27 100644 (file)
@@ -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,
index 84870714a4097e24456035c3770ebc60045c5d25..9c79580b1ff73d5bf4b0170fca05bf19c4872296 100644 (file)
@@ -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.
index 7728e258a0b7d92b7d19b31c9442e535fece7870..7419260564e1478e81f76a84473280ecedc6a3d8 100644 (file)
 #include <libgen.h>
 #include <stdlib.h>
 #include <signal.h>
+#include <unistd.h>
 
 #include <list>
 #include <string>
 #include <vector>
 
+#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 <path> prepends <path> 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<SimLoopExitEvent *>(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();
 }
index d9064bf73befc630fb6fd37355c016219115eb97..663e5745f7b14425f12734c539a4efb6dfcc5d32 100644 (file)
@@ -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
index 37b768bf01663ab01de8f233e7e4a1f2170cd089..ec5e2f7e2658c746f75b916193f20543bfc48354 100644 (file)
@@ -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);
index 3c86826a6fa31d494a72076e5fb9157d01de195c..5270802d1ee46fddba24be84e033af493bca621b 100644 (file)
@@ -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");
 
 }
 
index 2aa0508efb52a17268a3907ccd7e485ae632c6ee..b7901832dedbf220d4018fffa90042580fc32ed6 100644 (file)
@@ -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);
index 89bf83fc90702b4d48512717f3eb79cd2abf84fd..4f305ad382c8904b50176baab104744d56faef91 100644 (file)
@@ -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
 
index f1cf4b5fc5df5deab85bfb3e92ed13b9cd2671d2..545bf4ae066261fdc1077f5c45ca5ad38d2aff2f 100644 (file)
 
 #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__
index e37fea1b1d0061bd6f215f1b062b9d2fbce417ac..bc33625afe1eedcd5cc6f4a9c2430f5bd1bf2e61 100644 (file)
@@ -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;
 }