1/* 2 * Copyright (c) 2014 ARM Limited 3 * All rights reserved 4 * 5 * The license below extends only to copyright in the software and shall 6 * not be construed as granting a license to any other intellectual 7 * property including but not limited to intellectual property relating 8 * to a hardware implementation of the functionality of the software 9 * licensed hereunder. You may use the software subject to the license 10 * terms below provided that you ensure that this notice is replicated 11 * unmodified and in its entirety in all distributions of the software, 12 * modified or unmodified, in source code or in binary form. 13 * 14 * Redistribution and use in source and binary forms, with or without 15 * modification, are permitted provided that the following conditions are 16 * met: redistributions of source code must retain the above copyright 17 * notice, this list of conditions and the following disclaimer; 18 * redistributions in binary form must reproduce the above copyright 19 * notice, this list of conditions and the following disclaimer in the 20 * documentation and/or other materials provided with the distribution; 21 * neither the name of the copyright holders nor the names of its 22 * contributors may be used to endorse or promote products derived from 23 * this software without specific prior written permission. 24 * 25 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 26 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 27 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 28 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 29 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 30 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 31 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 32 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 33 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 34 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 35 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 36 * 37 * Authors: Andrew Bardsley 38 * Abdul Mutaal Ahmad 39 */ 40 41/** 42 * @file 43 * 44 * Example top level file for SystemC integration with C++-only 45 * instantiation. 46 * 47 * Build with something like: 48 * 49 * scons --without-python build/ARM/libgem5_opt.so 50 * 51 * g++ -std=c++0x -Ibuild/ARM -Isrc -DTRACING_ON \ 52 * -o gem5cxx.opt -Lbuild/ARM -lgem5_opt \ 53 * src/sim/sc_main_cxx.cc src/sim/cxx_stats.cc \ 54 * src/sim/sc_module.cc src/sim/sc_logger.cc 55 */ 56 57#include <cstdlib> 58#include <iostream> 59#include <sstream> 60#include <systemc> 61 62#include "base/statistics.hh" 63#include "base/str.hh" 64#include "base/trace.hh" 65#include "cpu/base.hh" 66#include "sc_logger.hh" 67#include "sc_module.hh" 68#include "sim/cxx_config_ini.hh" 69#include "sim/cxx_manager.hh" 70#include "sim/init_signals.hh" 71#include "sim/serialize.hh" 72#include "sim/simulate.hh" 73#include "sim/stat_control.hh" 74#include "sim/system.hh" 75#include "stats.hh" 76 77// Defining global string variable decalred in stats.hh 78std::string filename; 79 80void 81usage(const std::string &prog_name) 82{ 83 std::cerr << "Usage: " << prog_name << ( 84 " <config_file.ini> [ <option> ]\n\n" 85 "OPTIONS:\n" 86 " -p <object> <param> <value> -- set a parameter\n" 87 " -v <object> <param> <values> -- set a vector parameter from" 88 " a comma\n" 89 " separated values string\n" 90 " -d <flag> -- set a debug flag (-<flag>\n" 91 " clear a flag)\n" 92 " -s <dir> <ticks> -- save checkpoint to dir after" 93 " the given\n" 94 " number of ticks\n" 95 " -r <dir> -- restore checkpoint to dir\n" 96 " -c <from> <to> <ticks> -- switch from cpu 'from' to cpu" 97 " 'to' after\n" 98 " the given number of ticks\n" 99 " -n <#cpus> the number of cpus to switch\n" 100 " (appended to 'to' and 'from'" 101 " cpus above)\n" 102 "\n" 103 ); 104 105 std::exit(EXIT_FAILURE); 106} 107 108class SimControl : public Gem5SystemC::Module 109{ 110 protected: 111 int argc; 112 char **argv; 113 CxxConfigManager *config_manager; 114 Gem5SystemC::Logger logger; 115 116 bool checkpoint_restore; 117 bool checkpoint_save; 118 bool switch_cpus; 119 std::string checkpoint_dir; 120 std::string from_cpu; 121 std::string to_cpu; 122 Tick pre_run_time; 123 Tick pre_switch_time; 124 unsigned num_switch_cpus; 125 126 public: 127 SC_HAS_PROCESS(SimControl); 128 129 SimControl(sc_core::sc_module_name name, int argc_, char **argv_); 130 131 void run(); 132 private: 133 /** 134 * Switch a single CPU 135 * 136 * If numTotalCpus is greater than 1, the CPU index will be appended to 137 * the object name when searching config manager for the CPU name 138 * 139 * @param The CPU number to switch 140 * @param The total number of CPUs in the system 141 */ 142 void switchCpu(unsigned cpuNum, unsigned numTotalCpus); 143 144}; 145 146SimControl::SimControl(sc_core::sc_module_name name, 147 int argc_, char **argv_) : 148 Gem5SystemC::Module(name), 149 argc(argc_), 150 argv(argv_) 151{ 152 SC_THREAD(run); 153 154 std::string prog_name(argv[0]); 155 unsigned int arg_ptr = 1; 156 157 if (argc == 1) 158 usage(prog_name); 159 160 cxxConfigInit(); 161 162 /* Pass DPRINTF messages to SystemC */ 163 Trace::setDebugLogger(&logger); 164 165 /* @todo need this as an option */ 166 Gem5SystemC::setTickFrequency(); 167 168 /* Make a SystemC-synchronising event queue and install it as the 169 * sole top level gem5 EventQueue */ 170 Gem5SystemC::Module::setupEventQueues(*this); 171 172 if (sc_core::sc_get_time_resolution() != 173 sc_core::sc_time(1, sc_core::SC_PS)) 174 { 175 fatal("Time resolution must be set to 1 ps for gem5 to work"); 176 } 177 178 /* Enable keyboard interrupt, async I/O etc. */ 179 initSignals(); 180 181 /* Enable stats */ 182 Stats::initSimStats(); 183 Stats::registerHandlers(CxxConfig::statsReset, CxxConfig::statsDump); 184 185 Trace::enable(); 186 setDebugFlag("Terminal"); 187 188 checkpoint_restore = false; 189 checkpoint_save = false; 190 switch_cpus = false; 191 checkpoint_dir = ""; 192 from_cpu = ""; 193 to_cpu = ""; 194 pre_run_time = 1000000; 195 pre_switch_time = 1000000; 196 num_switch_cpus = 1; 197 198 const std::string config_file(argv[arg_ptr]); 199 200 CxxConfigFileBase *conf = new CxxIniFile(); 201 202 if (!conf->load(config_file.c_str())) 203 fatal("Can't open config file: %s", config_file); 204 205 arg_ptr++; 206 207 config_manager = new CxxConfigManager(*conf); 208 209 try { 210 while (arg_ptr < argc) { 211 std::string option(argv[arg_ptr]); 212 arg_ptr++; 213 unsigned num_args = argc - arg_ptr; 214 215 if (option == "-p") { 216 if (num_args < 3) 217 usage(prog_name); 218 config_manager->setParam(argv[arg_ptr], argv[arg_ptr + 1], 219 argv[arg_ptr + 2]); 220 arg_ptr += 3; 221 } else if (option == "-v") { 222 std::vector<std::string> values; 223 224 if (num_args < 3) 225 usage(prog_name); 226 tokenize(values, argv[2], ','); 227 config_manager->setParamVector(argv[arg_ptr], 228 argv[arg_ptr], values); 229 arg_ptr += 3; 230 } else if (option == "-d") { 231 if (num_args < 1) 232 usage(prog_name); 233 if (argv[arg_ptr][0] == '-') 234 clearDebugFlag(argv[arg_ptr] + 1); 235 else 236 setDebugFlag(argv[arg_ptr]); 237 arg_ptr++; 238 } else if (option == "-r") { 239 if (num_args < 1) 240 usage(prog_name); 241 checkpoint_dir = argv[arg_ptr]; 242 checkpoint_restore = true; 243 arg_ptr++; 244 } else if (option == "-s") { 245 if (num_args < 2) 246 usage(prog_name); 247 checkpoint_dir = argv[arg_ptr]; 248 std::istringstream(argv[arg_ptr + 1]) >> pre_run_time; 249 checkpoint_save = true; 250 arg_ptr += 2; 251 } else if (option == "-c") { 252 if (num_args < 3) 253 usage(prog_name); 254 switch_cpus = true; 255 from_cpu = argv[arg_ptr]; 256 to_cpu = argv[arg_ptr + 1]; 257 std::istringstream(argv[arg_ptr + 2]) >> pre_switch_time; 258 arg_ptr += 3; 259 } else if (option == "-n") { 260 if (num_args < 1) 261 usage(prog_name); 262 std::istringstream(argv[arg_ptr]) >> num_switch_cpus; 263 arg_ptr++; 264 } else { 265 usage(prog_name); 266 } 267 } 268 } catch (CxxConfigManager::Exception &e) { 269 fatal("Config problem in sim object %s: %s", e.name, e.message); 270 } 271 272 if (checkpoint_save && checkpoint_restore) { 273 fatal("Don't try to save and restore a checkpoint in the same" 274 "run"); 275 } 276 277 CxxConfig::statsEnable(); 278 getEventQueue(0)->dump(); 279 280 try { 281 config_manager->instantiate(); 282 } catch (CxxConfigManager::Exception &e) { 283 fatal("Config problem in sim object %s: %s", e.name, e.message); 284 } 285} 286 287void SimControl::run() 288{ 289 EventQueue *eventq = getEventQueue(0); 290 GlobalSimLoopExitEvent *exit_event = NULL; 291 292 /* There *must* be no scheduled events yet */ 293 fatal_if(!eventq->empty(), "There must be no posted events" 294 " before SimControl::run"); 295 296 try { 297 if (checkpoint_restore) { 298 std::cerr << "Restoring checkpoint\n"; 299 300 CheckpointIn *checkpoint = new CheckpointIn(checkpoint_dir, 301 config_manager->getSimObjectResolver()); 302 303 /* Catch SystemC up with gem5 after checkpoint restore. 304 * Note that gem5 leading SystemC is always a violation of the 305 * required relationship between the two, hence this careful 306 * catchup */ 307 308 DrainManager::instance().preCheckpointRestore(); 309 Serializable::unserializeGlobals(*checkpoint); 310 311 Tick systemc_time = sc_core::sc_time_stamp().value(); 312 if (curTick() > systemc_time) { 313 Tick wait_period = curTick() - systemc_time; 314 315 std::cerr << "Waiting for " << wait_period << "ps for" 316 " SystemC to catch up to gem5\n"; 317 wait(sc_core::sc_time::from_value(wait_period)); 318 } 319 320 config_manager->loadState(*checkpoint); 321 config_manager->startup(); 322 config_manager->drainResume(); 323 324 std::cerr << "Restored from Checkpoint\n"; 325 } else { 326 config_manager->initState(); 327 config_manager->startup(); 328 } 329 } catch (CxxConfigManager::Exception &e) { 330 fatal("Config problem in sim object %s: %s", e.name, e.message); 331 } 332 333 fatal_if(eventq->empty(), "No events to process after system startup"); 334 335 if (checkpoint_save) { 336 exit_event = simulate(pre_run_time); 337 338 unsigned int drain_count = 1; 339 do { 340 drain_count = config_manager->drain(); 341 342 std::cerr << "Draining " << drain_count << '\n'; 343 344 if (drain_count > 0) { 345 exit_event = simulate(); 346 } 347 } while (drain_count > 0); 348 349 std::cerr << "Simulation stop at tick " << curTick() 350 << ", cause: " << exit_event->getCause() << '\n'; 351 352 std::cerr << "Checkpointing\n"; 353 354 /* FIXME, this should really be serialising just for 355 * config_manager rather than using serializeAll's ugly 356 * SimObject static object list */ 357 Serializable::serializeAll(checkpoint_dir); 358 359 std::cerr << "Completed checkpoint\n"; 360 361 config_manager->drainResume(); 362 } 363 364 if (switch_cpus) { 365 exit_event = simulate(pre_switch_time); 366 367 unsigned int drain_count = 1; 368 do { 369 drain_count = config_manager->drain(); 370 371 std::cerr << "Draining " << drain_count << '\n'; 372 373 if (drain_count > 0) { 374 exit_event = simulate(); 375 } 376 } while (drain_count > 0); 377 378 for (unsigned cpu_num = 0; cpu_num < num_switch_cpus; ++cpu_num) { 379 switchCpu(cpu_num, num_switch_cpus); 380 } 381 382 config_manager->drainResume(); 383 384 } 385 386 exit_event = simulate(); 387 388 std::cerr << "Exit at tick " << curTick() 389 << ", cause: " << exit_event->getCause() << '\n'; 390 391 getEventQueue(0)->dump(); 392 393#if TRY_CLEAN_DELETE 394 config_manager->deleteObjects(); 395#endif 396} 397 398int 399sc_main(int argc, char **argv) 400{ 401 SimControl sim_control("gem5", argc, argv); 402 403 filename = "m5out/stats-systemc.txt"; 404 405 sc_core::sc_start(); 406 407 CxxConfig::statsDump(); 408 409 return EXIT_SUCCESS; 410} 411 412void 413SimControl::switchCpu(unsigned cpuNum, unsigned numTotalCpus) { 414 assert(cpuNum < numTotalCpus); 415 std::ostringstream from_cpu_name; 416 std::ostringstream to_cpu_name; 417 418 from_cpu_name << from_cpu; 419 to_cpu_name << to_cpu; 420 421 if (numTotalCpus > 1) { 422 from_cpu_name << cpuNum; 423 to_cpu_name << cpuNum; 424 } 425 426 std::cerr << "Switching CPU "<< cpuNum << "(from='" << from_cpu_name.str() 427 <<"' to '"<< to_cpu_name.str() << "')\n"; 428 429 /* Assume the system is called system */ 430 System &system = config_manager->getObject<System>("system"); 431 BaseCPU &old_cpu = config_manager->getObject<BaseCPU>(from_cpu_name.str()); 432 BaseCPU &new_cpu = config_manager->getObject<BaseCPU>(to_cpu_name.str()); 433 434 old_cpu.switchOut(); 435 436 // I'm not sure if this can be called before old_cpu.switchOut(). If so, 437 // it is best to just move this call before the switchCpu loop in run() 438 // where it previously was 439 if (cpuNum == 0) 440 system.setMemoryMode(Enums::timing); 441 442 new_cpu.takeOverFrom(&old_cpu); 443 444 445 std::cerr << "Switched CPU"<<cpuNum<<"\n"; 446} 447