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