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
This commit is contained in:
parent
5802c46c3b
commit
29e34a739b
51 changed files with 453 additions and 327 deletions
21
SConstruct
21
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:
|
||||
|
|
|
@ -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()
|
||||
|
||||
|
|
|
@ -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 = []
|
||||
|
||||
|
|
|
@ -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')
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
|
|
|
@ -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):
|
||||
|
|
|
@ -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 --<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
|
||||
|
|
|
@ -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']
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
from m5 import *
|
||||
from m5.config import *
|
||||
from Device import BasicPioDevice
|
||||
|
||||
class AlphaConsole(BasicPioDevice):
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
from m5 import *
|
||||
from m5 import build_env
|
||||
from m5.config import *
|
||||
from BaseCPU import BaseCPU
|
||||
|
||||
class DerivAlphaFullCPU(BaseCPU):
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
from m5 import *
|
||||
from m5.config import *
|
||||
class AlphaTLB(SimObject):
|
||||
type = 'AlphaTLB'
|
||||
abstract = True
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
from m5 import *
|
||||
from m5.config import *
|
||||
from Device import BasicPioDevice
|
||||
|
||||
class BadDevice(BasicPioDevice):
|
||||
|
|
|
@ -1,4 +1,6 @@
|
|||
from m5 import *
|
||||
from m5 import build_env
|
||||
from m5.config import *
|
||||
|
||||
class BaseCPU(SimObject):
|
||||
type = 'BaseCPU'
|
||||
abstract = True
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
from m5 import *
|
||||
from m5.config import *
|
||||
from BaseMem import BaseMem
|
||||
|
||||
class Prefetch(Enum): vals = ['none', 'tagged', 'stride', 'ghb']
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
from m5 import *
|
||||
from m5.config import *
|
||||
from MemObject import MemObject
|
||||
|
||||
class Bridge(MemObject):
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
from m5 import *
|
||||
from m5.config import *
|
||||
from MemObject import MemObject
|
||||
|
||||
class Bus(MemObject):
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
from m5 import *
|
||||
from m5.config import *
|
||||
class Coherence(Enum): vals = ['uni', 'msi', 'mesi', 'mosi', 'moesi']
|
||||
|
||||
class CoherenceProtocol(SimObject):
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
from m5 import *
|
||||
from m5.config import *
|
||||
from MemObject import MemObject
|
||||
|
||||
class PioDevice(MemObject):
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
from m5 import *
|
||||
from m5.config import *
|
||||
class DiskImage(SimObject):
|
||||
type = 'DiskImage'
|
||||
abstract = True
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
from m5 import *
|
||||
from m5 import build_env
|
||||
from m5.config import *
|
||||
from Device import DmaDevice
|
||||
from Pci import PciDevice
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
from m5 import *
|
||||
from m5.config import *
|
||||
from Pci import PciDevice
|
||||
|
||||
class IdeID(Enum): vals = ['master', 'slave']
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
from m5 import *
|
||||
from m5.config import *
|
||||
class IntrControl(SimObject):
|
||||
type = 'IntrControl'
|
||||
cpu = Param.BaseCPU(Parent.any, "the cpu")
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
from m5 import *
|
||||
from m5.config import *
|
||||
|
||||
class MemObject(SimObject):
|
||||
type = 'MemObject'
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
from m5 import *
|
||||
from m5.config import *
|
||||
class MemTest(SimObject):
|
||||
type = 'MemTest'
|
||||
cache = Param.BaseCache("L1 cache")
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
from m5 import *
|
||||
from m5.config import *
|
||||
from Device import BasicPioDevice, DmaDevice
|
||||
|
||||
class PciConfigData(SimObject):
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
from m5 import *
|
||||
from m5.config import *
|
||||
from MemObject import *
|
||||
|
||||
class PhysicalMemory(MemObject):
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
from m5 import *
|
||||
from m5.config import *
|
||||
class Platform(SimObject):
|
||||
type = 'Platform'
|
||||
abstract = True
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
from m5 import *
|
||||
from m5.config import *
|
||||
class Process(SimObject):
|
||||
type = 'Process'
|
||||
abstract = True
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
from m5 import *
|
||||
from m5.config import *
|
||||
class Repl(SimObject):
|
||||
type = 'Repl'
|
||||
abstract = True
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
from m5 import *
|
||||
from m5.config import *
|
||||
from Serialize import Serialize
|
||||
from Statistics import Statistics
|
||||
from Trace import Trace
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
from m5 import *
|
||||
from m5.config import *
|
||||
class ConsoleListener(SimObject):
|
||||
type = 'ConsoleListener'
|
||||
port = Param.TcpPort(3456, "listen port")
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
from m5 import *
|
||||
from m5.config import *
|
||||
class SimpleDisk(SimObject):
|
||||
type = 'SimpleDisk'
|
||||
disk = Param.DiskImage("Disk Image")
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
from m5 import *
|
||||
from m5 import build_env
|
||||
from m5.config import *
|
||||
|
||||
class System(SimObject):
|
||||
type = 'System'
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
from m5 import *
|
||||
from m5.config import *
|
||||
from Device import BasicPioDevice
|
||||
from Platform import Platform
|
||||
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
from m5 import *
|
||||
from m5 import build_env
|
||||
from m5.config import *
|
||||
from Device import BasicPioDevice
|
||||
|
||||
class Uart(BasicPioDevice):
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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.
|
||||
|
|
268
src/sim/main.cc
268
src/sim/main.cc
|
@ -41,11 +41,13 @@
|
|||
#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 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;
|
||||
if (host)
|
||||
ccprintf(out, "M5 executing on %s\n", host);
|
||||
}
|
||||
|
||||
|
||||
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();
|
||||
// initialize SWIG 'main' module
|
||||
init_main();
|
||||
|
||||
// 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");
|
||||
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);
|
||||
}
|
||||
|
||||
// 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_AnyFile(fp, filename);
|
||||
} else {
|
||||
// no script file argument... force interactive prompt
|
||||
interactive = true;
|
||||
}
|
||||
|
||||
// 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");
|
||||
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);
|
||||
|
||||
return 0;
|
||||
// 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();
|
||||
|
||||
// print simulation stats
|
||||
Stats::DumpNow();
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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");
|
||||
|
||||
}
|
||||
|
||||
|
|
|
@ -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 termination";
|
||||
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)
|
||||
{
|
||||
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);
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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__
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue