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