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