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