scheduler.cc revision 13312:a7685ffbead8
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 "systemc/core/kernel.hh"
36#include "systemc/ext/core/sc_main.hh"
37#include "systemc/ext/utils/sc_report.hh"
38#include "systemc/ext/utils/sc_report_handler.hh"
39#include "systemc/utils/report.hh"
40#include "systemc/utils/tracefile.hh"
41
42namespace sc_gem5
43{
44
45Scheduler::Scheduler() :
46    eq(nullptr), readyEvent(this, false, ReadyPriority),
47    pauseEvent(this, false, PausePriority),
48    stopEvent(this, false, StopPriority),
49    scMain(nullptr), _throwToScMain(nullptr),
50    starvationEvent(this, false, StarvationPriority),
51    _elaborationDone(false), _started(false), _stopNow(false),
52    _status(StatusOther), maxTickEvent(this, false, MaxTickPriority),
53    timeAdvancesEvent(this, false, TimeAdvancesPriority), _numCycles(0),
54    _changeStamp(0), _current(nullptr), initDone(false), runOnce(false)
55{}
56
57Scheduler::~Scheduler()
58{
59    // Clear out everything that belongs to us to make sure nobody tries to
60    // clear themselves out after the scheduler goes away.
61    clear();
62}
63
64void
65Scheduler::clear()
66{
67    // Delta notifications.
68    while (!deltas.empty())
69        deltas.front()->deschedule();
70
71    // Timed notifications.
72    for (auto &tsp: timeSlots) {
73        TimeSlot *&ts = tsp.second;
74        while (!ts->events.empty())
75            ts->events.front()->deschedule();
76        deschedule(ts);
77    }
78    timeSlots.clear();
79
80    // gem5 events.
81    if (readyEvent.scheduled())
82        deschedule(&readyEvent);
83    if (pauseEvent.scheduled())
84        deschedule(&pauseEvent);
85    if (stopEvent.scheduled())
86        deschedule(&stopEvent);
87    if (starvationEvent.scheduled())
88        deschedule(&starvationEvent);
89    if (maxTickEvent.scheduled())
90        deschedule(&maxTickEvent);
91    if (timeAdvancesEvent.scheduled())
92        deschedule(&timeAdvancesEvent);
93
94    Process *p;
95    while ((p = initList.getNext()))
96        p->popListNode();
97    while ((p = readyListMethods.getNext()))
98        p->popListNode();
99    while ((p = readyListThreads.getNext()))
100        p->popListNode();
101
102    Channel *c;
103    while ((c = updateList.getNext()))
104        c->popListNode();
105}
106
107void
108Scheduler::initPhase()
109{
110    for (Process *p = initList.getNext(); p; p = initList.getNext()) {
111        p->popListNode();
112
113        if (p->dontInitialize()) {
114            if (!p->hasStaticSensitivities() && !p->internal()) {
115                SC_REPORT_WARNING(
116                        "(W558) disable() or dont_initialize() called on "
117                        "process with no static sensitivity, it will be "
118                        "orphaned", p->name());
119            }
120        } else {
121            p->ready();
122        }
123    }
124
125    runUpdate();
126    runDelta();
127
128    for (auto ets: eventsToSchedule)
129        eq->schedule(ets.first, ets.second);
130    eventsToSchedule.clear();
131
132    if (_started) {
133        if (!runToTime && starved())
134            scheduleStarvationEvent();
135        kernel->status(::sc_core::SC_RUNNING);
136    }
137
138    initDone = true;
139
140    status(StatusOther);
141
142    scheduleTimeAdvancesEvent();
143}
144
145void
146Scheduler::reg(Process *p)
147{
148    if (initDone) {
149        // If not marked as dontInitialize, mark as ready.
150        if (!p->dontInitialize())
151            p->ready();
152    } else {
153        // Otherwise, record that this process should be initialized once we
154        // get there.
155        initList.pushLast(p);
156    }
157}
158
159void
160Scheduler::yield()
161{
162    // Pull a process from the active list.
163    _current = getNextReady();
164    if (!_current) {
165        // There are no more processes, so return control to evaluate.
166        Fiber::primaryFiber()->run();
167    } else {
168        _current->popListNode();
169        // Switch to whatever Fiber is supposed to run this process. All
170        // Fibers which aren't running should be parked at this line.
171        _current->fiber()->run();
172        // If the current process needs to be manually started, start it.
173        if (_current && _current->needsStart()) {
174            _current->needsStart(false);
175            // If a process hasn't started yet, "resetting" it just starts it
176            // and signals its reset event.
177            if (_current->inReset())
178                _current->resetEvent().notify();
179            try {
180                _current->run();
181            } catch (...) {
182                throwToScMain();
183            }
184        }
185    }
186    if (_current && !_current->needsStart()) {
187        if (_current->excWrapper) {
188            auto ew = _current->excWrapper;
189            _current->excWrapper = nullptr;
190            ew->throw_it();
191        } else if (_current->inReset()) {
192            _current->reset(false);
193        }
194    }
195}
196
197void
198Scheduler::ready(Process *p)
199{
200    if (_stopNow)
201        return;
202
203    if (p->procKind() == ::sc_core::SC_METHOD_PROC_)
204        readyListMethods.pushLast(p);
205    else
206        readyListThreads.pushLast(p);
207
208    if (!inEvaluate())
209        scheduleReadyEvent();
210}
211
212void
213Scheduler::resume(Process *p)
214{
215    if (initDone)
216        ready(p);
217    else
218        initList.pushLast(p);
219}
220
221bool
222listContains(ListNode *list, ListNode *target)
223{
224    ListNode *n = list->nextListNode;
225    while (n != list)
226        if (n == target)
227            return true;
228    return false;
229}
230
231bool
232Scheduler::suspend(Process *p)
233{
234    bool was_ready;
235    if (initDone) {
236        // After initialization, check if we're on a ready list.
237        was_ready = (p->nextListNode != nullptr);
238        p->popListNode();
239    } else {
240        // Nothing is ready before init.
241        was_ready = false;
242    }
243    return was_ready;
244}
245
246void
247Scheduler::requestUpdate(Channel *c)
248{
249    updateList.pushLast(c);
250    if (!inEvaluate())
251        scheduleReadyEvent();
252}
253
254void
255Scheduler::scheduleReadyEvent()
256{
257    // Schedule the evaluate and update phases.
258    if (!readyEvent.scheduled()) {
259        schedule(&readyEvent);
260        if (starvationEvent.scheduled())
261            deschedule(&starvationEvent);
262    }
263}
264
265void
266Scheduler::scheduleStarvationEvent()
267{
268    if (!starvationEvent.scheduled()) {
269        schedule(&starvationEvent);
270        if (readyEvent.scheduled())
271            deschedule(&readyEvent);
272    }
273}
274
275void
276Scheduler::runReady()
277{
278    scheduleTimeAdvancesEvent();
279
280    bool empty = readyListMethods.empty() && readyListThreads.empty();
281    lastReadyTick = getCurTick();
282
283    // The evaluation phase.
284    status(StatusEvaluate);
285    do {
286        yield();
287    } while (getNextReady());
288    _current = nullptr;
289
290    if (!empty) {
291        _numCycles++;
292        _changeStamp++;
293    }
294
295    if (_stopNow) {
296        status(StatusOther);
297        return;
298    }
299
300    runUpdate();
301    if (!traceFiles.empty())
302        trace(true);
303    runDelta();
304
305    if (!runToTime && starved())
306        scheduleStarvationEvent();
307
308    if (runOnce)
309        schedulePause();
310
311    status(StatusOther);
312}
313
314void
315Scheduler::runUpdate()
316{
317    status(StatusUpdate);
318
319    try {
320        Channel *channel = updateList.getNext();
321        while (channel) {
322            channel->popListNode();
323            channel->update();
324            channel = updateList.getNext();
325        }
326    } catch (...) {
327        throwToScMain();
328    }
329}
330
331void
332Scheduler::runDelta()
333{
334    status(StatusDelta);
335
336    try {
337        while (!deltas.empty())
338            deltas.back()->run();
339    } catch (...) {
340        throwToScMain();
341    }
342}
343
344void
345Scheduler::pause()
346{
347    status(StatusPaused);
348    kernel->status(::sc_core::SC_PAUSED);
349    runOnce = false;
350    if (scMain && !scMain->finished())
351        scMain->run();
352}
353
354void
355Scheduler::stop()
356{
357    status(StatusStopped);
358    kernel->stop();
359
360    clear();
361
362    runOnce = false;
363    if (scMain && !scMain->finished())
364        scMain->run();
365}
366
367void
368Scheduler::start(Tick max_tick, bool run_to_time)
369{
370    // We should be running from sc_main. Keep track of that Fiber to return
371    // to later.
372    scMain = Fiber::currentFiber();
373
374    _started = true;
375    status(StatusOther);
376    runToTime = run_to_time;
377
378    maxTick = max_tick;
379    lastReadyTick = getCurTick();
380
381    if (initDone) {
382        if (!runToTime && starved())
383            scheduleStarvationEvent();
384        kernel->status(::sc_core::SC_RUNNING);
385    }
386
387    schedule(&maxTickEvent, maxTick);
388    scheduleTimeAdvancesEvent();
389
390    // Return to gem5 to let it run events, etc.
391    Fiber::primaryFiber()->run();
392
393    if (pauseEvent.scheduled())
394        deschedule(&pauseEvent);
395    if (stopEvent.scheduled())
396        deschedule(&stopEvent);
397    if (maxTickEvent.scheduled())
398        deschedule(&maxTickEvent);
399    if (starvationEvent.scheduled())
400        deschedule(&starvationEvent);
401
402    if (_throwToScMain) {
403        const ::sc_core::sc_report *to_throw = _throwToScMain;
404        _throwToScMain = nullptr;
405        throw *to_throw;
406    }
407}
408
409void
410Scheduler::oneCycle()
411{
412    runOnce = true;
413    scheduleReadyEvent();
414    start(::MaxTick, false);
415}
416
417void
418Scheduler::schedulePause()
419{
420    if (pauseEvent.scheduled())
421        return;
422
423    schedule(&pauseEvent);
424}
425
426void
427Scheduler::throwToScMain()
428{
429    ::sc_core::sc_report report = reportifyException();
430    _throwToScMain = &report;
431    status(StatusOther);
432    scMain->run();
433}
434
435void
436Scheduler::scheduleStop(bool finish_delta)
437{
438    if (stopEvent.scheduled())
439        return;
440
441    if (!finish_delta) {
442        _stopNow = true;
443        // If we're not supposed to finish the delta cycle, flush all
444        // pending activity.
445        clear();
446    }
447    schedule(&stopEvent);
448}
449
450void
451Scheduler::trace(bool delta)
452{
453    for (auto tf: traceFiles)
454        tf->trace(delta);
455}
456
457Scheduler scheduler;
458
459namespace {
460
461void
462throwingReportHandler(const ::sc_core::sc_report &r,
463                      const ::sc_core::sc_actions &)
464{
465    throw r;
466}
467
468} // anonymous namespace
469
470const ::sc_core::sc_report
471reportifyException()
472{
473    ::sc_core::sc_report_handler_proc old_handler = reportHandlerProc;
474    ::sc_core::sc_report_handler::set_handler(&throwingReportHandler);
475
476    try {
477        try {
478            // Rethrow the current exception so we can catch it and throw an
479            // sc_report instead if it's not a type we recognize/can handle.
480            throw;
481        } catch (const ::sc_core::sc_report &) {
482            // It's already a sc_report, so nothing to do.
483            throw;
484        } catch (const ::sc_core::sc_unwind_exception &) {
485            panic("Kill/reset exception escaped a Process::run()");
486        } catch (const std::exception &e) {
487            SC_REPORT_ERROR("uncaught exception", e.what());
488        } catch (const char *msg) {
489            SC_REPORT_ERROR("uncaught exception", msg);
490        } catch (...) {
491            SC_REPORT_ERROR("uncaught exception", "UNKNOWN EXCEPTION");
492        }
493    } catch (const ::sc_core::sc_report &r) {
494        ::sc_core::sc_report_handler::set_handler(old_handler);
495        return r;
496    }
497    panic("No exception thrown in reportifyException.");
498}
499
500} // namespace sc_gem5
501