| /* |
| * Copyright (c) 2000-2005 The Regents of The University of Michigan |
| * All rights reserved. |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions are |
| * met: redistributions of source code must retain the above copyright |
| * notice, this list of conditions and the following disclaimer; |
| * redistributions in binary form must reproduce the above copyright |
| * notice, this list of conditions and the following disclaimer in the |
| * documentation and/or other materials provided with the distribution; |
| * neither the name of the copyright holders nor the names of its |
| * contributors may be used to endorse or promote products derived from |
| * this software without specific prior written permission. |
| * |
| * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
| * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
| * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
| * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
| * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
| * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
| * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
| * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
| * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
| * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| * |
| * Authors: Steve Raasch |
| * Nathan Binkert |
| * Steve Reinhardt |
| */ |
| |
| /// |
| /// @file sim/main.cc |
| /// |
| #include <Python.h> // must be before system headers... see Python docs |
| |
| #include <sys/types.h> |
| #include <sys/stat.h> |
| #include <errno.h> |
| #include <libgen.h> |
| #include <stdlib.h> |
| #include <signal.h> |
| #include <getopt.h> |
| |
| #include <list> |
| #include <string> |
| #include <vector> |
| |
| #include "base/callback.hh" |
| #include "base/inifile.hh" |
| #include "base/misc.hh" |
| #include "base/output.hh" |
| #include "base/pollevent.hh" |
| #include "base/statistics.hh" |
| #include "base/str.hh" |
| #include "base/time.hh" |
| #include "config/pythonhome.hh" |
| #include "cpu/base.hh" |
| #include "cpu/smt.hh" |
| #include "mem/mem_object.hh" |
| #include "mem/port.hh" |
| #include "python/swig/init.hh" |
| #include "sim/async.hh" |
| #include "sim/builder.hh" |
| #include "sim/host.hh" |
| #include "sim/serialize.hh" |
| #include "sim/sim_events.hh" |
| #include "sim/sim_exit.hh" |
| #include "sim/sim_object.hh" |
| #include "sim/system.hh" |
| #include "sim/stat_control.hh" |
| #include "sim/stats.hh" |
| #include "sim/root.hh" |
| |
| using namespace std; |
| |
| // See async.h. |
| volatile bool async_event = false; |
| volatile bool async_dump = false; |
| volatile bool async_dumpreset = false; |
| volatile bool async_exit = false; |
| volatile bool async_io = false; |
| volatile bool async_alarm = false; |
| |
| /// Stats signal handler. |
| void |
| dumpStatsHandler(int sigtype) |
| { |
| async_event = true; |
| async_dump = true; |
| } |
| |
| void |
| dumprstStatsHandler(int sigtype) |
| { |
| async_event = true; |
| async_dumpreset = true; |
| } |
| |
| /// Exit signal handler. |
| void |
| exitNowHandler(int sigtype) |
| { |
| async_event = true; |
| async_exit = true; |
| } |
| |
| /// Abort signal handler. |
| void |
| abortHandler(int sigtype) |
| { |
| cerr << "Program aborted at cycle " << curTick << endl; |
| |
| #if TRACING_ON |
| // dump trace buffer, if there is one |
| Trace::theLog.dump(cerr); |
| #endif |
| } |
| |
| int |
| main(int argc, char **argv) |
| { |
| signal(SIGFPE, SIG_IGN); // may occur on misspeculated paths |
| signal(SIGTRAP, SIG_IGN); |
| signal(SIGUSR1, dumpStatsHandler); // dump intermediate stats |
| signal(SIGUSR2, dumprstStatsHandler); // dump and reset stats |
| signal(SIGINT, exitNowHandler); // dump final stats and exit |
| signal(SIGABRT, abortHandler); |
| |
| Py_SetProgramName(argv[0]); |
| |
| // default path to m5 python code is the currently executing |
| // file... Python ZipImporter will find embedded zip archive. |
| // The M5_ARCHIVE environment variable can be used to override this. |
| char *m5_archive = getenv("M5_ARCHIVE"); |
| string pythonpath = m5_archive ? m5_archive : argv[0]; |
| |
| char *oldpath = getenv("PYTHONPATH"); |
| if (oldpath != NULL) { |
| pythonpath += ":"; |
| pythonpath += oldpath; |
| } |
| |
| if (setenv("PYTHONPATH", pythonpath.c_str(), true) == -1) |
| fatal("setenv: %s\n", strerror(errno)); |
| |
| char *python_home = getenv("PYTHONHOME"); |
| if (!python_home) |
| python_home = PYTHONHOME; |
| Py_SetPythonHome(python_home); |
| |
| // initialize embedded Python interpreter |
| Py_Initialize(); |
| PySys_SetArgv(argc, argv); |
| |
| // initialize SWIG modules |
| init_swig(); |
| |
| PyRun_SimpleString("import m5.main"); |
| PyRun_SimpleString("m5.main.main()"); |
| |
| // clean up Python intepreter. |
| Py_Finalize(); |
| } |
| |
| |
| void |
| setOutputDir(const string &dir) |
| { |
| simout.setDirectory(dir); |
| } |
| |
| |
| IniFile inifile; |
| |
| SimObject * |
| createSimObject(const string &name) |
| { |
| return SimObjectClass::createObject(inifile, name); |
| } |
| |
| |
| /** |
| * Pointer to the Python function that maps names to SimObjects. |
| */ |
| PyObject *resolveFunc = NULL; |
| |
| /** |
| * Convert a pointer to the Python object that SWIG wraps around a C++ |
| * SimObject pointer back to the actual C++ pointer. See main.i. |
| */ |
| extern "C" SimObject *convertSwigSimObjectPtr(PyObject *); |
| |
| |
| SimObject * |
| resolveSimObject(const string &name) |
| { |
| PyObject *pyPtr = PyEval_CallFunction(resolveFunc, "(s)", name.c_str()); |
| if (pyPtr == NULL) { |
| PyErr_Print(); |
| panic("resolveSimObject: failure on call to Python for %s", name); |
| } |
| |
| SimObject *simObj = convertSwigSimObjectPtr(pyPtr); |
| if (simObj == NULL) |
| panic("resolveSimObject: failure on pointer conversion for %s", name); |
| |
| return simObj; |
| } |
| |
| |
| /** |
| * Load config.ini into C++ database. Exported to Python via SWIG; |
| * invoked from m5.instantiate(). |
| */ |
| void |
| loadIniFile(PyObject *_resolveFunc) |
| { |
| resolveFunc = _resolveFunc; |
| configStream = simout.find("config.out"); |
| |
| // The configuration database is now complete; start processing it. |
| inifile.load(simout.resolve("config.ini")); |
| |
| // Initialize statistics database |
| Stats::InitSimStats(); |
| } |
| |
| |
| /** |
| * Look up a MemObject port. Helper function for connectPorts(). |
| */ |
| Port * |
| lookupPort(SimObject *so, const std::string &name, int i) |
| { |
| MemObject *mo = dynamic_cast<MemObject *>(so); |
| if (mo == NULL) { |
| warn("error casting SimObject %s to MemObject", so->name()); |
| return NULL; |
| } |
| |
| Port *p = mo->getPort(name, i); |
| if (p == NULL) |
| warn("error looking up port %s on object %s", name, so->name()); |
| return p; |
| } |
| |
| |
| /** |
| * Connect the described MemObject ports. Called from Python via SWIG. |
| */ |
| int |
| connectPorts(SimObject *o1, const std::string &name1, int i1, |
| SimObject *o2, const std::string &name2, int i2) |
| { |
| Port *p1 = lookupPort(o1, name1, i1); |
| Port *p2 = lookupPort(o2, name2, i2); |
| |
| if (p1 == NULL || p2 == NULL) { |
| warn("connectPorts: port lookup error"); |
| return 0; |
| } |
| |
| p1->setPeer(p2); |
| p2->setPeer(p1); |
| |
| return 1; |
| } |
| |
| /** |
| * Do final initialization steps after object construction but before |
| * start of simulation. |
| */ |
| void |
| finalInit() |
| { |
| // Parse and check all non-config-hierarchy parameters. |
| ParamContext::parseAllContexts(inifile); |
| ParamContext::checkAllContexts(); |
| |
| // Echo all parameter settings to stats file as well. |
| ParamContext::showAllContexts(*configStream); |
| |
| // Do a second pass to finish initializing the sim objects |
| SimObject::initAll(); |
| |
| // Restore checkpointed state, if any. |
| #if 0 |
| configHierarchy.unserializeSimObjects(); |
| #endif |
| |
| SimObject::regAllStats(); |
| |
| // Check to make sure that the stats package is properly initialized |
| Stats::check(); |
| |
| // Reset to put the stats in a consistent state. |
| Stats::reset(); |
| |
| SimStartup(); |
| } |
| |
| |
| /** 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 = MaxTick) |
| { |
| warn("Entering event queue @ %d. Starting simulation...\n", curTick); |
| |
| if (num_cycles < 0) |
| fatal("simulate: num_cycles must be >= 0 (was %d)\n", num_cycles); |
| else if (curTick + num_cycles < 0) //Overflow |
| num_cycles = MaxTick; |
| else |
| num_cycles = curTick + num_cycles; |
| |
| Event *limit_event = schedExitSimLoop("simulate() limit reached", |
| num_cycles); |
| |
| 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(); |
| 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_dump) { |
| async_dump = false; |
| |
| using namespace Stats; |
| SetupEvent(Dump, curTick); |
| } |
| |
| if (async_dumpreset) { |
| async_dumpreset = false; |
| |
| using namespace Stats; |
| SetupEvent(Dump | Reset, curTick); |
| } |
| |
| if (async_exit) { |
| async_exit = false; |
| exitSimLoop("user interrupt received"); |
| } |
| |
| if (async_io || async_alarm) { |
| async_io = false; |
| async_alarm = false; |
| pollQueue.service(); |
| } |
| } |
| } |
| |
| // not reached... only exit is return on SimLoopExitEvent |
| } |
| |
| Event * |
| createCountedDrain() |
| { |
| return new CountedDrainEvent(); |
| } |
| |
| void |
| cleanupCountedDrain(Event *counted_drain) |
| { |
| CountedDrainEvent *event = |
| dynamic_cast<CountedDrainEvent *>(counted_drain); |
| if (event == NULL) { |
| fatal("Called cleanupCountedDrain() on an event that was not " |
| "a CountedDrainEvent."); |
| } |
| assert(event->getCount() == 0); |
| delete event; |
| } |
| |
| void |
| serializeAll(const std::string &cpt_dir) |
| { |
| Serializable::serializeAll(cpt_dir); |
| } |
| |
| void |
| unserializeAll(const std::string &cpt_dir) |
| { |
| Serializable::unserializeAll(cpt_dir); |
| } |
| |
| /** |
| * Queue of C++ callbacks to invoke on simulator exit. |
| */ |
| CallbackQueue& |
| exitCallbacks() |
| { |
| static CallbackQueue theQueue; |
| return theQueue; |
| } |
| |
| /** |
| * Register an exit callback. |
| */ |
| void |
| registerExitCallback(Callback *callback) |
| { |
| exitCallbacks().add(callback); |
| } |
| |
| BaseCPU * |
| convertToBaseCPUPtr(SimObject *obj) |
| { |
| BaseCPU *ptr = dynamic_cast<BaseCPU *>(obj); |
| |
| if (ptr == NULL) |
| warn("Casting to BaseCPU pointer failed"); |
| return ptr; |
| } |
| |
| System * |
| convertToSystemPtr(SimObject *obj) |
| { |
| System *ptr = dynamic_cast<System *>(obj); |
| |
| if (ptr == NULL) |
| warn("Casting to System pointer failed"); |
| return ptr; |
| } |
| |
| |
| /** |
| * 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(); |
| } |