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