| /* |
| * Copyright (c) 2003 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. |
| */ |
| |
| /// |
| /// @file sim/main.cc |
| /// |
| #include <sys/types.h> |
| #include <sys/stat.h> |
| #include <stdlib.h> |
| #include <signal.h> |
| |
| #include <string> |
| #include <vector> |
| |
| #include "base/copyright.hh" |
| #include "base/inifile.hh" |
| #include "base/misc.hh" |
| #include "base/pollevent.hh" |
| #include "base/statistics.hh" |
| #include "base/time.hh" |
| #include "cpu/base_cpu.hh" |
| #include "cpu/full_cpu/smt.hh" |
| #include "sim/async.hh" |
| #include "sim/builder.hh" |
| #include "sim/configfile.hh" |
| #include "sim/host.hh" |
| #include "sim/sim_events.hh" |
| #include "sim/sim_exit.hh" |
| #include "sim/sim_init.hh" |
| #include "sim/sim_object.hh" |
| #include "sim/sim_stats.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; |
| } |
| |
| /// Simulator executable name |
| const char *myProgName = ""; |
| |
| /// Show brief help message. |
| static void |
| showBriefHelp(ostream &out) |
| { |
| out << "Usage: " << myProgName |
| << " [-hn] [-Dname[=def]] [-Uname] [-I[dir]] " |
| << "[--<section>:<param>=<value>] [<config file> ...]" << endl |
| << " -h: print long help (including parameter listing)" << endl |
| << " -n: don't load default.ini" << endl |
| << " -u: don't quit on unreferenced parameters" << endl |
| << " -D,-U,-I: passed to cpp for preprocessing .ini files" << endl; |
| } |
| |
| /// Show verbose help message. Includes parameter listing from |
| /// showBriefHelp(), plus an exhaustive list of ini-file parameters |
| /// and SimObjects (with their parameters). |
| static void |
| showLongHelp(ostream &out) |
| { |
| showBriefHelp(out); |
| |
| out << endl |
| << endl |
| << "-----------------" << endl |
| << "Global Parameters" << endl |
| << "-----------------" << endl |
| << endl; |
| |
| ParamContext::describeAllContexts(out); |
| |
| out << endl |
| << endl |
| << "-----------------" << endl |
| << "Simulator Objects" << endl |
| << "-----------------" << endl |
| << endl; |
| |
| SimObjectClass::describeAllClasses(out); |
| } |
| |
| /// Print welcome message. |
| static void |
| sayHello(ostream &out) |
| { |
| extern const char *compileDate; // from date.cc |
| |
| ccprintf(out, "M5 Simulator System\n"); |
| // display copyright |
| ccprintf(out, "%s\n", briefCopyright); |
| ccprintf(out, "M5 compiled on %d\n", compileDate); |
| |
| char *host = getenv("HOSTNAME"); |
| if (!host) |
| host = getenv("HOST"); |
| |
| if (host) |
| ccprintf(out, "M5 executing on %s\n", host); |
| |
| ccprintf(out, "M5 simulation started %s\n", Time::start); |
| } |
| |
| /// |
| /// 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). |
| /// |
| static void |
| echoCommandLine(int argc, char **argv, ostream &out) |
| { |
| out << "command line: " << argv[0]; |
| for (int i = 1; i < argc; i++) { |
| string arg(argv[i]); |
| |
| out << ' '; |
| |
| // If the arg contains spaces, we need to quote it. |
| // The rest of this is overkill to make it look purty. |
| |
| // 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; |
| } |
| |
| |
| /// |
| /// The simulator configuration database. This is the union of all |
| /// specified .ini files. This shouldn't need to be visible outside |
| /// this file, as it is passed as a parameter to all the param-parsing |
| /// routines. |
| /// |
| static IniFile simConfigDB; |
| |
| /// Check for a default.ini file and load it if necessary. |
| static void |
| handleDefaultIni(bool &loadIt, vector<char *> &cppArgs) |
| { |
| struct stat sb; |
| |
| if (loadIt) { |
| if (stat("default.ini", &sb) == 0) { |
| if (!simConfigDB.loadCPP("default.ini", cppArgs)) { |
| cout << "Error processing file default.ini" << endl; |
| exit(1); |
| } |
| } |
| |
| // set this whether it actually was found or not, so we don't |
| // bother to check again next time |
| loadIt = false; |
| } |
| } |
| |
| |
| /// M5 entry point. |
| int |
| main(int argc, char **argv) |
| { |
| // Save off program name |
| myProgName = argv[0]; |
| |
| 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 |
| |
| sayHello(cerr); |
| |
| // Initialize statistics database |
| initBaseStats(); |
| |
| vector<char *> cppArgs; |
| |
| // Should we use default.ini if it exists? By default, yes. (Use |
| // -n to override.) |
| bool loadDefaultIni = true; |
| |
| // Should we quit if there are unreferenced parameters? By |
| // default, yes... it's a good way of catching typos in |
| // section/parameter names (which otherwise go by silently). Use |
| // -u to override. |
| bool quitOnUnreferenced = true; |
| |
| // Parse command-line options. The tricky part here is figuring |
| // out whether to look for & load default.ini, and if needed, |
| // doing so at the right time w.r.t. processing the other |
| // parameters. |
| // |
| // Since most of the complex options are handled through the |
| // config database, we don't mess with getopts, and just parse |
| // manually. |
| for (int i = 1; i < argc; ++i) { |
| char *arg_str = argv[i]; |
| |
| // if arg starts with '-', parse as option, |
| // else treat it as a configuration file name and load it |
| if (arg_str[0] == '-') { |
| |
| // switch on second char |
| switch (arg_str[1]) { |
| case 'h': |
| // -h: show help |
| showLongHelp(cerr); |
| exit(1); |
| |
| case 'n': |
| // -n: don't load default.ini |
| if (!loadDefaultIni) { |
| cerr << "Warning: -n option needs to precede any " |
| << "explicit configuration file name " << endl |
| << " or command-line configuration parameter." |
| << endl; |
| } |
| loadDefaultIni = false; |
| break; |
| |
| case 'u': |
| // -u: don't quit on unreferenced parameters |
| quitOnUnreferenced = false; |
| break; |
| |
| case 'D': |
| case 'U': |
| case 'I': |
| // cpp options: record & pass to cpp. Note that these |
| // cannot have spaces, i.e., '-Dname=val' is OK, but |
| // '-D name=val' is not. I don't consider this a |
| // problem, since even though gnu cpp accepts the |
| // latter, other cpp implementations do not (Tru64, |
| // for one). |
| cppArgs.push_back(arg_str); |
| break; |
| |
| case '-': |
| // command-line configuration parameter: |
| // '--<section>:<parameter>=<value>' |
| |
| // Load default.ini if necessary -- see comment in |
| // else clause below. |
| handleDefaultIni(loadDefaultIni, cppArgs); |
| |
| if (!simConfigDB.add(arg_str + 2)) { |
| // parse error |
| ccprintf(cerr, |
| "Could not parse configuration argument '%s'\n" |
| "Expecting --<section>:<parameter>=<value>\n", |
| arg_str); |
| exit(0); |
| } |
| break; |
| |
| default: |
| showBriefHelp(cerr); |
| ccprintf(cerr, "Fatal: invalid argument '%s'\n", arg_str); |
| exit(0); |
| } |
| } |
| else { |
| // no '-', treat as config file name |
| |
| // If we haven't loaded default.ini yet, and we want to, |
| // now is the time. Can't do it sooner because we need to |
| // look for '-n', can't do it later since we want |
| // default.ini loaded first (so that any other settings |
| // override it). |
| handleDefaultIni(loadDefaultIni, cppArgs); |
| |
| if (!simConfigDB.loadCPP(arg_str, cppArgs)) { |
| cprintf("Error processing file %s\n", arg_str); |
| exit(1); |
| } |
| } |
| } |
| |
| // Final check for default.ini, in case no config files or |
| // command-line config parameters were given. |
| handleDefaultIni(loadDefaultIni, cppArgs); |
| |
| // The configuration database is now complete; start processing it. |
| |
| // Parse and check all non-config-hierarchy parameters. |
| ParamContext::parseAllContexts(simConfigDB); |
| ParamContext::checkAllContexts(); |
| |
| // Print header info into stats file. Can't do this sooner since |
| // the stat file name is set via a .ini param... thus it just got |
| // opened above during ParamContext::checkAllContexts(). |
| |
| // Print hello message to stats file if it's actually a file. If |
| // it's not (i.e. it's cout or cerr) then we already did it above. |
| if (statStreamIsFile) |
| sayHello(*statStream); |
| |
| // Echo command line and all parameter settings to stats file as well. |
| echoCommandLine(argc, argv, *statStream); |
| ParamContext::showAllContexts(*statStream); |
| |
| // Now process the configuration hierarchy and create the SimObjects. |
| ConfigHierarchy configHierarchy(simConfigDB); |
| configHierarchy.build(); |
| configHierarchy.createSimObjects(); |
| |
| // Restore checkpointed state, if any. |
| configHierarchy.unserializeSimObjects(); |
| |
| // Done processing the configuration database. |
| // Check for unreferenced entries. |
| if (simConfigDB.printUnreferenced() && quitOnUnreferenced) { |
| cerr << "Fatal: unreferenced .ini sections/entries." << endl |
| << "If this is not an error, add 'unref_section_ok=y' or " |
| << "'unref_entries_ok=y' to the appropriate sections " |
| << "to suppress this message." << endl; |
| exit(1); |
| } |
| |
| SimObject::regAllStats(); |
| |
| // uncomment the following to get PC-based execution-time profile |
| #ifdef DO_PROFILE |
| init_profile((char *)&_init, (char *)&_fini); |
| #endif |
| |
| // Check to make sure that the stats package is properly initialized |
| Statistics::check(); |
| |
| // Reset to put the stats in a consistent state. |
| Statistics::reset(); |
| |
| // Nothing to simulate if we don't have at least one CPU somewhere. |
| if (BaseCPU::numSimulatedCPUs() == 0) { |
| cerr << "Fatal: no CPUs to simulate." << endl; |
| exit(1); |
| } |
| |
| SimInit(); |
| |
| while (!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(); |
| |
| if (async_event) { |
| async_event = false; |
| if (async_dump) { |
| async_dump = false; |
| |
| using namespace Statistics; |
| SetupEvent(Dump, curTick); |
| } |
| |
| if (async_dumpreset) { |
| async_dumpreset = false; |
| |
| using namespace Statistics; |
| SetupEvent(Dump | Reset, curTick); |
| } |
| |
| if (async_exit) { |
| async_exit = false; |
| new SimExitEvent("User requested STOP"); |
| } |
| |
| if (async_io || async_alarm) { |
| async_io = false; |
| async_alarm = false; |
| pollQueue.service(); |
| } |
| } |
| } |
| |
| // 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; |
| } |