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
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.
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'])
# 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:
-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()
+
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
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'.
'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
#
###################################################
-# 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 = []
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')
case PAL::halt:
halt();
if (--System::numSystemsRunning == 0)
- new SimExitEvent("all cpus halted");
+ exitSimLoop("all cpus halted");
break;
case PAL::bpt:
0x00: decode PALFUNC {
format EmulatedCallPal {
0x00: halt ({{
- SimExit(curTick, "halt instruction encountered");
+ exitSimLoop(curTick, "halt instruction encountered");
}}, IsNonSpeculative);
0x83: callsys({{
xc->syscall(R0);
//
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
//
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
case PAL::halt:
halt();
if (--System::numSystemsRunning == 0)
- new SimExitEvent("all cpus halted");
+ exitSimLoop("all cpus halted");
break;
case PAL::bpt:
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
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));
}
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
# 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)
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)
# 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):
# 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.
# 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
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
#####################################################################
-# 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.
'NetworkBandwidth', 'MemoryBandwidth',
'Range', 'AddrRange', 'MaxAddr', 'MaxTick', 'AllMemory',
'Null', 'NULL',
- 'NextEthernetAddr', 'instantiate']
+ 'NextEthernetAddr']
-from m5 import *
+from m5.config import *
from Device import BasicPioDevice
class AlphaConsole(BasicPioDevice):
-from m5 import *
+from m5 import build_env
+from m5.config import *
from BaseCPU import BaseCPU
class DerivAlphaFullCPU(BaseCPU):
-from m5 import *
+from m5.config import *
class AlphaTLB(SimObject):
type = 'AlphaTLB'
abstract = True
-from m5 import *
+from m5.config import *
from Device import BasicPioDevice
class BadDevice(BasicPioDevice):
-from m5 import *
+from m5 import build_env
+from m5.config import *
+
class BaseCPU(SimObject):
type = 'BaseCPU'
abstract = True
-from m5 import *
+from m5.config import *
from BaseMem import BaseMem
class Prefetch(Enum): vals = ['none', 'tagged', 'stride', 'ghb']
-from m5 import *
+from m5.config import *
from MemObject import MemObject
class Bridge(MemObject):
-from m5 import *
+from m5.config import *
from MemObject import MemObject
class Bus(MemObject):
-from m5 import *
+from m5.config import *
class Coherence(Enum): vals = ['uni', 'msi', 'mesi', 'mosi', 'moesi']
class CoherenceProtocol(SimObject):
-from m5 import *
+from m5.config import *
from MemObject import MemObject
class PioDevice(MemObject):
-from m5 import *
+from m5.config import *
class DiskImage(SimObject):
type = 'DiskImage'
abstract = True
-from m5 import *
+from m5 import build_env
+from m5.config import *
from Device import DmaDevice
from Pci import PciDevice
-from m5 import *
+from m5.config import *
from Pci import PciDevice
class IdeID(Enum): vals = ['master', 'slave']
-from m5 import *
+from m5.config import *
class IntrControl(SimObject):
type = 'IntrControl'
cpu = Param.BaseCPU(Parent.any, "the cpu")
-from m5 import *
+from m5.config import *
class MemObject(SimObject):
type = 'MemObject'
-from m5 import *
+from m5.config import *
class MemTest(SimObject):
type = 'MemTest'
cache = Param.BaseCache("L1 cache")
-from m5 import *
+from m5.config import *
from Device import BasicPioDevice, DmaDevice
class PciConfigData(SimObject):
-from m5 import *
+from m5.config import *
from MemObject import *
class PhysicalMemory(MemObject):
-from m5 import *
+from m5.config import *
class Platform(SimObject):
type = 'Platform'
abstract = True
-from m5 import *
+from m5.config import *
class Process(SimObject):
type = 'Process'
abstract = True
-from m5 import *
+from m5.config import *
class Repl(SimObject):
type = 'Repl'
abstract = True
-from m5 import *
+from m5.config import *
from Serialize import Serialize
from Statistics import Statistics
from Trace import Trace
-from m5 import *
+from m5.config import *
class ConsoleListener(SimObject):
type = 'ConsoleListener'
port = Param.TcpPort(3456, "listen port")
-from m5 import *
+from m5.config import *
class SimpleDisk(SimObject):
type = 'SimpleDisk'
disk = Param.DiskImage("Disk Image")
-from m5 import *
+from m5 import build_env
+from m5.config import *
class System(SimObject):
type = 'System'
-from m5 import *
+from m5.config import *
from Device import BasicPioDevice
from Platform import Platform
-from m5 import *
+from m5 import build_env
+from m5.config import *
from Device import BasicPioDevice
class Uart(BasicPioDevice):
prev->next = curr->next;
}
-void
+Event *
EventQueue::serviceOne()
{
Event *event = head;
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;
}
Squashed = 0x1,
Scheduled = 0x2,
AutoDelete = 0x4,
- AutoSerialize = 0x8
+ AutoSerialize = 0x8,
+ IsExitEvent = 0x10
};
bool getFlags(Flags f) const { return (_flags & f) == f; }
/// 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; }
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,
*/
typedef int64_t Tick;
+const Tick MaxTick = (1LL << 62);
+
/**
* Address type
* This will probably be moved somewhere else in the near future.
#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"
#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);
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.
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
// 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;
if (async_exit) {
async_exit = false;
- new SimExitEvent("User requested STOP");
+ exitSimLoop("user interrupt received");
}
if (async_io || async_alarm) {
}
}
- // 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();
}
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
#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"
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);
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");
}
// 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);
}
//
CountedExitEvent::process()
{
if (--downCounter == 0) {
- new SimExitEvent(cause, 0);
+ exitSimLoop(cause, 0);
}
}
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);
//
// Event to terminate simulation at a particular cycle/instruction
//
-class SimExitEvent : public Event
+class SimLoopExitEvent : public Event
{
private:
// string explaining why we're terminating
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
#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__
#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;
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;
}