scheduler.cc revision 13703:bedf6da29c9b
1/*
2 * Copyright 2018 Google, Inc.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are
6 * met: redistributions of source code must retain the above copyright
7 * notice, this list of conditions and the following disclaimer;
8 * redistributions in binary form must reproduce the above copyright
9 * notice, this list of conditions and the following disclaimer in the
10 * documentation and/or other materials provided with the distribution;
11 * neither the name of the copyright holders nor the names of its
12 * contributors may be used to endorse or promote products derived from
13 * this software without specific prior written permission.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
16 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
17 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
18 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
19 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
20 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
21 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
22 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
23 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
25 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 *
27 * Authors: Gabe Black
28 */
29
30#include "systemc/core/scheduler.hh"
31
32#include "base/fiber.hh"
33#include "base/logging.hh"
34#include "sim/eventq.hh"
35#include "sim/sim_exit.hh"
36#include "systemc/core/kernel.hh"
37#include "systemc/core/sc_main_fiber.hh"
38#include "systemc/ext/core/messages.hh"
39#include "systemc/ext/core/sc_main.hh"
40#include "systemc/ext/utils/sc_report.hh"
41#include "systemc/ext/utils/sc_report_handler.hh"
42#include "systemc/utils/report.hh"
43#include "systemc/utils/tracefile.hh"
44
45namespace sc_gem5
46{
47
48Scheduler::Scheduler() :
49    eq(nullptr), readyEvent(this, false, ReadyPriority),
50    pauseEvent(this, false, PausePriority),
51    stopEvent(this, false, StopPriority), _throwUp(nullptr),
52    starvationEvent(this, false, StarvationPriority),
53    _elaborationDone(false), _started(false), _stopNow(false),
54    _status(StatusOther), maxTick(::MaxTick),
55    maxTickEvent(this, false, MaxTickPriority),
56    timeAdvancesEvent(this, false, TimeAdvancesPriority), _numCycles(0),
57    _changeStamp(0), _current(nullptr), initDone(false), runToTime(true),
58    runOnce(false)
59{}
60
61Scheduler::~Scheduler()
62{
63    // Clear out everything that belongs to us to make sure nobody tries to
64    // clear themselves out after the scheduler goes away.
65    clear();
66}
67
68void
69Scheduler::clear()
70{
71    // Delta notifications.
72    while (!deltas.empty())
73        deltas.front()->deschedule();
74
75    // Timed notifications.
76    for (auto &tsp: timeSlots) {
77        TimeSlot *&ts = tsp.second;
78        while (!ts->events.empty())
79            ts->events.front()->deschedule();
80        deschedule(ts);
81    }
82    timeSlots.clear();
83
84    // gem5 events.
85    if (readyEvent.scheduled())
86        deschedule(&readyEvent);
87    if (pauseEvent.scheduled())
88        deschedule(&pauseEvent);
89    if (stopEvent.scheduled())
90        deschedule(&stopEvent);
91    if (starvationEvent.scheduled())
92        deschedule(&starvationEvent);
93    if (maxTickEvent.scheduled())
94        deschedule(&maxTickEvent);
95    if (timeAdvancesEvent.scheduled())
96        deschedule(&timeAdvancesEvent);
97
98    Process *p;
99    while ((p = initList.getNext()))
100        p->popListNode();
101    while ((p = readyListMethods.getNext()))
102        p->popListNode();
103    while ((p = readyListThreads.getNext()))
104        p->popListNode();
105
106    Channel *c;
107    while ((c = updateList.getNext()))
108        c->popListNode();
109}
110
111void
112Scheduler::initPhase()
113{
114    runUpdate();
115
116    for (Process *p = initList.getNext(); p; p = initList.getNext()) {
117        p->popListNode();
118
119        if (p->dontInitialize()) {
120            if (!p->hasStaticSensitivities() && !p->internal()) {
121                SC_REPORT_WARNING(sc_core::SC_ID_DISABLE_WILL_ORPHAN_PROCESS_,
122                        p->name());
123            }
124        } else {
125            p->ready();
126        }
127    }
128
129    runDelta();
130
131    for (auto ets: eventsToSchedule)
132        eq->schedule(ets.first, ets.second);
133    eventsToSchedule.clear();
134
135    if (_started) {
136        if (!runToTime && starved())
137            scheduleStarvationEvent();
138        kernel->status(::sc_core::SC_RUNNING);
139    }
140
141    initDone = true;
142
143    status(StatusOther);
144
145    scheduleTimeAdvancesEvent();
146}
147
148void
149Scheduler::reg(Process *p)
150{
151    if (initDone) {
152        // If not marked as dontInitialize, mark as ready.
153        if (!p->dontInitialize())
154            p->ready();
155    } else {
156        // Otherwise, record that this process should be initialized once we
157        // get there.
158        initList.pushLast(p);
159    }
160}
161
162void
163Scheduler::yield()
164{
165    // Pull a process from the active list.
166    _current = getNextReady();
167    if (!_current) {
168        // There are no more processes, so return control to evaluate.
169        Fiber::primaryFiber()->run();
170    } else {
171        _current->popListNode();
172        _current->scheduled(false);
173        // Switch to whatever Fiber is supposed to run this process. All
174        // Fibers which aren't running should be parked at this line.
175        _current->fiber()->run();
176        // If the current process needs to be manually started, start it.
177        if (_current && _current->needsStart()) {
178            _current->needsStart(false);
179            // If a process hasn't started yet, "resetting" it just starts it
180            // and signals its reset event.
181            if (_current->inReset())
182                _current->resetEvent().notify();
183            try {
184                _current->run();
185            } catch (...) {
186                throwUp();
187            }
188        }
189    }
190    if (_current && !_current->needsStart()) {
191        if (_current->excWrapper) {
192            auto ew = _current->excWrapper;
193            _current->excWrapper = nullptr;
194            ew->throw_it();
195        } else if (_current->inReset()) {
196            _current->reset(false);
197        }
198    }
199}
200
201void
202Scheduler::ready(Process *p)
203{
204    if (_stopNow)
205        return;
206
207    p->scheduled(true);
208
209    if (p->procKind() == ::sc_core::SC_METHOD_PROC_)
210        readyListMethods.pushLast(p);
211    else
212        readyListThreads.pushLast(p);
213
214    if (!inEvaluate())
215        scheduleReadyEvent();
216}
217
218void
219Scheduler::resume(Process *p)
220{
221    if (initDone)
222        ready(p);
223    else
224        initList.pushLast(p);
225}
226
227bool
228listContains(ListNode *list, ListNode *target)
229{
230    ListNode *n = list->nextListNode;
231    while (n != list)
232        if (n == target)
233            return true;
234    return false;
235}
236
237bool
238Scheduler::suspend(Process *p)
239{
240    bool was_ready;
241    if (initDone) {
242        // After initialization, check if we're on a ready list.
243        was_ready = (p->nextListNode != nullptr);
244        p->popListNode();
245    } else {
246        // Nothing is ready before init.
247        was_ready = false;
248    }
249    return was_ready;
250}
251
252void
253Scheduler::requestUpdate(Channel *c)
254{
255    updateList.pushLast(c);
256    if (!inEvaluate())
257        scheduleReadyEvent();
258}
259
260void
261Scheduler::scheduleReadyEvent()
262{
263    // Schedule the evaluate and update phases.
264    if (!readyEvent.scheduled()) {
265        schedule(&readyEvent);
266        if (starvationEvent.scheduled())
267            deschedule(&starvationEvent);
268    }
269}
270
271void
272Scheduler::scheduleStarvationEvent()
273{
274    if (!starvationEvent.scheduled()) {
275        schedule(&starvationEvent);
276        if (readyEvent.scheduled())
277            deschedule(&readyEvent);
278    }
279}
280
281void
282Scheduler::runReady()
283{
284    scheduleTimeAdvancesEvent();
285
286    bool empty = readyListMethods.empty() && readyListThreads.empty();
287    lastReadyTick = getCurTick();
288
289    // The evaluation phase.
290    status(StatusEvaluate);
291    do {
292        yield();
293    } while (getNextReady());
294    _current = nullptr;
295
296    if (!empty) {
297        _numCycles++;
298        _changeStamp++;
299    }
300
301    if (_stopNow) {
302        status(StatusOther);
303        return;
304    }
305
306    runUpdate();
307    if (!traceFiles.empty())
308        trace(true);
309    runDelta();
310
311    if (!runToTime && starved())
312        scheduleStarvationEvent();
313
314    if (runOnce)
315        schedulePause();
316
317    status(StatusOther);
318}
319
320void
321Scheduler::runUpdate()
322{
323    status(StatusUpdate);
324
325    try {
326        Channel *channel = updateList.getNext();
327        while (channel) {
328            channel->popListNode();
329            channel->update();
330            channel = updateList.getNext();
331        }
332    } catch (...) {
333        throwUp();
334    }
335}
336
337void
338Scheduler::runDelta()
339{
340    status(StatusDelta);
341
342    try {
343        while (!deltas.empty())
344            deltas.back()->run();
345    } catch (...) {
346        throwUp();
347    }
348}
349
350void
351Scheduler::pause()
352{
353    status(StatusPaused);
354    kernel->status(::sc_core::SC_PAUSED);
355    runOnce = false;
356    if (scMainFiber.called()) {
357        if (!scMainFiber.finished())
358            scMainFiber.run();
359    } else {
360        if (scMainFiber.finished())
361            fatal("Pausing systemc after sc_main completed.");
362        else
363            exitSimLoopNow("systemc pause");
364    }
365}
366
367void
368Scheduler::stop()
369{
370    status(StatusStopped);
371    kernel->stop();
372
373    clear();
374
375    runOnce = false;
376    if (scMainFiber.called()) {
377        if (!scMainFiber.finished())
378            scMainFiber.run();
379    } else {
380        if (scMainFiber.finished())
381            fatal("Stopping systemc after sc_main completed.");
382        else
383            exitSimLoopNow("systemc stop");
384    }
385}
386
387void
388Scheduler::start(Tick max_tick, bool run_to_time)
389{
390    _started = true;
391    status(StatusOther);
392    runToTime = run_to_time;
393
394    maxTick = max_tick;
395    lastReadyTick = getCurTick();
396
397    if (initDone) {
398        if (!runToTime && starved())
399            scheduleStarvationEvent();
400        kernel->status(::sc_core::SC_RUNNING);
401    }
402
403    schedule(&maxTickEvent, maxTick);
404    scheduleTimeAdvancesEvent();
405
406    // Return to gem5 to let it run events, etc.
407    Fiber::primaryFiber()->run();
408
409    if (pauseEvent.scheduled())
410        deschedule(&pauseEvent);
411    if (stopEvent.scheduled())
412        deschedule(&stopEvent);
413    if (maxTickEvent.scheduled())
414        deschedule(&maxTickEvent);
415    if (starvationEvent.scheduled())
416        deschedule(&starvationEvent);
417
418    if (_throwUp) {
419        const ::sc_core::sc_report *to_throw = _throwUp;
420        _throwUp = nullptr;
421        throw *to_throw;
422    }
423}
424
425void
426Scheduler::oneCycle()
427{
428    runOnce = true;
429    scheduleReadyEvent();
430    start(::MaxTick, false);
431}
432
433void
434Scheduler::schedulePause()
435{
436    if (pauseEvent.scheduled())
437        return;
438
439    schedule(&pauseEvent);
440}
441
442void
443Scheduler::throwUp()
444{
445    if (scMainFiber.called() && !scMainFiber.finished()) {
446        ::sc_core::sc_report report = reportifyException();
447        _throwUp = &report;
448        status(StatusOther);
449        scMainFiber.run();
450    } else {
451        reportHandlerProc(reportifyException(),
452                ::sc_core::sc_report_handler::get_catch_actions());
453    }
454}
455
456void
457Scheduler::scheduleStop(bool finish_delta)
458{
459    if (stopEvent.scheduled())
460        return;
461
462    if (!finish_delta) {
463        _stopNow = true;
464        // If we're not supposed to finish the delta cycle, flush all
465        // pending activity.
466        clear();
467    }
468    schedule(&stopEvent);
469}
470
471void
472Scheduler::trace(bool delta)
473{
474    for (auto tf: traceFiles)
475        tf->trace(delta);
476}
477
478Scheduler scheduler;
479Process *getCurrentProcess() { return scheduler.current(); }
480
481namespace {
482
483void
484throwingReportHandler(const ::sc_core::sc_report &r,
485                      const ::sc_core::sc_actions &)
486{
487    throw r;
488}
489
490} // anonymous namespace
491
492const ::sc_core::sc_report
493reportifyException()
494{
495    ::sc_core::sc_report_handler_proc old_handler = reportHandlerProc;
496    ::sc_core::sc_report_handler::set_handler(&throwingReportHandler);
497
498    try {
499        try {
500            // Rethrow the current exception so we can catch it and throw an
501            // sc_report instead if it's not a type we recognize/can handle.
502            throw;
503        } catch (const ::sc_core::sc_report &) {
504            // It's already a sc_report, so nothing to do.
505            throw;
506        } catch (const ::sc_core::sc_unwind_exception &) {
507            panic("Kill/reset exception escaped a Process::run()");
508        } catch (const std::exception &e) {
509            SC_REPORT_ERROR(
510                    sc_core::SC_ID_SIMULATION_UNCAUGHT_EXCEPTION_, e.what());
511        } catch (const char *msg) {
512            SC_REPORT_ERROR(
513                    sc_core::SC_ID_SIMULATION_UNCAUGHT_EXCEPTION_, msg);
514        } catch (...) {
515            SC_REPORT_ERROR(
516                    sc_core::SC_ID_SIMULATION_UNCAUGHT_EXCEPTION_,
517                    "UNKNOWN EXCEPTION");
518        }
519    } catch (const ::sc_core::sc_report &r) {
520        ::sc_core::sc_report_handler::set_handler(old_handler);
521        return r;
522    }
523    panic("No exception thrown in reportifyException.");
524}
525
526} // namespace sc_gem5
527