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