scheduler.cc revision 13244:deedec45898f
12SN/A/*
21762SN/A * Copyright 2018 Google, Inc.
32SN/A *
42SN/A * Redistribution and use in source and binary forms, with or without
52SN/A * modification, are permitted provided that the following conditions are
62SN/A * met: redistributions of source code must retain the above copyright
72SN/A * notice, this list of conditions and the following disclaimer;
82SN/A * redistributions in binary form must reproduce the above copyright
92SN/A * notice, this list of conditions and the following disclaimer in the
102SN/A * documentation and/or other materials provided with the distribution;
112SN/A * neither the name of the copyright holders nor the names of its
122SN/A * contributors may be used to endorse or promote products derived from
132SN/A * this software without specific prior written permission.
142SN/A *
152SN/A * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
162SN/A * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
172SN/A * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
182SN/A * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
192SN/A * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
202SN/A * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
212SN/A * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
222SN/A * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
232SN/A * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
242SN/A * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
252SN/A * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
262SN/A *
272665Ssaidi@eecs.umich.edu * Authors: Gabe Black
282665Ssaidi@eecs.umich.edu */
292SN/A
302SN/A#include "systemc/core/scheduler.hh"
312SN/A
327840Snate@binkert.org#include "base/fiber.hh"
332SN/A#include "base/logging.hh"
34400SN/A#include "sim/eventq.hh"
357840Snate@binkert.org#include "systemc/core/kernel.hh"
367862Sgblack@eecs.umich.edu#include "systemc/ext/core/sc_main.hh"
377870Sgblack@eecs.umich.edu#include "systemc/ext/utils/sc_report.hh"
382SN/A#include "systemc/ext/utils/sc_report_handler.hh"
392SN/A
402SN/Anamespace sc_gem5
417840Snate@binkert.org{
427840Snate@binkert.org
432SN/AScheduler::Scheduler() :
447840Snate@binkert.org    eq(nullptr), readyEvent(this, false, ReadyPriority),
457840Snate@binkert.org    pauseEvent(this, false, PausePriority),
467840Snate@binkert.org    stopEvent(this, false, StopPriority),
47400SN/A    scMain(nullptr), _throwToScMain(nullptr),
487840Snate@binkert.org    starvationEvent(this, false, StarvationPriority),
497840Snate@binkert.org    _elaborationDone(false), _started(false), _stopNow(false),
507840Snate@binkert.org    _status(StatusOther), maxTickEvent(this, false, MaxTickPriority),
51400SN/A    _numCycles(0), _changeStamp(0), _current(nullptr), initDone(false),
52400SN/A    runOnce(false)
537862Sgblack@eecs.umich.edu{}
547862Sgblack@eecs.umich.edu
557862Sgblack@eecs.umich.eduScheduler::~Scheduler()
567862Sgblack@eecs.umich.edu{
577862Sgblack@eecs.umich.edu    // Clear out everything that belongs to us to make sure nobody tries to
587862Sgblack@eecs.umich.edu    // clear themselves out after the scheduler goes away.
597862Sgblack@eecs.umich.edu    clear();
607862Sgblack@eecs.umich.edu}
617862Sgblack@eecs.umich.edu
627862Sgblack@eecs.umich.eduvoid
637862Sgblack@eecs.umich.eduScheduler::clear()
647862Sgblack@eecs.umich.edu{
657862Sgblack@eecs.umich.edu    // Delta notifications.
66400SN/A    while (!deltas.empty())
677840Snate@binkert.org        deltas.front()->deschedule();
68400SN/A
697840Snate@binkert.org    // Timed notifications.
70400SN/A    for (auto &tsp: timeSlots) {
71400SN/A        TimeSlot *&ts = tsp.second;
72400SN/A        while (!ts->events.empty())
733918Ssaidi@eecs.umich.edu            ts->events.front()->deschedule();
747840Snate@binkert.org        deschedule(ts);
753918Ssaidi@eecs.umich.edu    }
76400SN/A    timeSlots.clear();
773918Ssaidi@eecs.umich.edu
78400SN/A    // gem5 events.
79400SN/A    if (readyEvent.scheduled())
802SN/A        deschedule(&readyEvent);
812SN/A    if (pauseEvent.scheduled())
82400SN/A        deschedule(&pauseEvent);
83400SN/A    if (stopEvent.scheduled())
84400SN/A        deschedule(&stopEvent);
85400SN/A    if (starvationEvent.scheduled())
862SN/A        deschedule(&starvationEvent);
877840Snate@binkert.org    if (maxTickEvent.scheduled())
887840Snate@binkert.org        deschedule(&maxTickEvent);
89400SN/A
907840Snate@binkert.org    Process *p;
917840Snate@binkert.org    while ((p = initList.getNext()))
927840Snate@binkert.org        p->popListNode();
937840Snate@binkert.org    while ((p = readyListMethods.getNext()))
947840Snate@binkert.org        p->popListNode();
957840Snate@binkert.org    while ((p = readyListThreads.getNext()))
967840Snate@binkert.org        p->popListNode();
977840Snate@binkert.org
987840Snate@binkert.org    Channel *c;
997840Snate@binkert.org    while ((c = updateList.getNext()))
1007840Snate@binkert.org        c->popListNode();
1017840Snate@binkert.org}
1027840Snate@binkert.org
1037840Snate@binkert.orgvoid
1047840Snate@binkert.orgScheduler::initPhase()
1057840Snate@binkert.org{
1067840Snate@binkert.org    for (Process *p = initList.getNext(); p; p = initList.getNext()) {
1077840Snate@binkert.org        p->popListNode();
1087840Snate@binkert.org
1097840Snate@binkert.org        if (p->dontInitialize()) {
1107840Snate@binkert.org            if (!p->hasStaticSensitivities() && !p->internal()) {
1117840Snate@binkert.org                SC_REPORT_WARNING(
1127840Snate@binkert.org                        "(W558) disable() or dont_initialize() called on "
1137840Snate@binkert.org                        "process with no static sensitivity, it will be "
1147840Snate@binkert.org                        "orphaned", p->name());
115400SN/A            }
1162SN/A        } else {
1177840Snate@binkert.org            p->ready();
1187870Sgblack@eecs.umich.edu        }
1197870Sgblack@eecs.umich.edu    }
1207870Sgblack@eecs.umich.edu
1217870Sgblack@eecs.umich.edu    runUpdate();
1227870Sgblack@eecs.umich.edu    runDelta();
1237870Sgblack@eecs.umich.edu
1247870Sgblack@eecs.umich.edu    for (auto ets: eventsToSchedule)
1257870Sgblack@eecs.umich.edu        eq->schedule(ets.first, ets.second);
1267870Sgblack@eecs.umich.edu    eventsToSchedule.clear();
1277870Sgblack@eecs.umich.edu
1287870Sgblack@eecs.umich.edu    if (_started) {
1297870Sgblack@eecs.umich.edu        if (!runToTime && starved())
1307870Sgblack@eecs.umich.edu            scheduleStarvationEvent();
1317870Sgblack@eecs.umich.edu        kernel->status(::sc_core::SC_RUNNING);
1327870Sgblack@eecs.umich.edu    }
1337870Sgblack@eecs.umich.edu
1347870Sgblack@eecs.umich.edu    initDone = true;
1357870Sgblack@eecs.umich.edu
1367870Sgblack@eecs.umich.edu    status(StatusOther);
1377840Snate@binkert.org}
138400SN/A
1397840Snate@binkert.orgvoid
1407840Snate@binkert.orgScheduler::reg(Process *p)
1417840Snate@binkert.org{
1427840Snate@binkert.org    if (initDone) {
1437840Snate@binkert.org        // If not marked as dontInitialize, mark as ready.
1447840Snate@binkert.org        if (!p->dontInitialize())
1457840Snate@binkert.org            p->ready();
146400SN/A    } 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 = getNextReady();
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    if (!inEvaluate())
197        scheduleReadyEvent();
198}
199
200void
201Scheduler::resume(Process *p)
202{
203    if (initDone)
204        ready(p);
205    else
206        initList.pushLast(p);
207}
208
209bool
210listContains(ListNode *list, ListNode *target)
211{
212    ListNode *n = list->nextListNode;
213    while (n != list)
214        if (n == target)
215            return true;
216    return false;
217}
218
219bool
220Scheduler::suspend(Process *p)
221{
222    bool was_ready;
223    if (initDone) {
224        // After initialization, check if we're on a ready list.
225        was_ready = (p->nextListNode != nullptr);
226        p->popListNode();
227    } else {
228        // Nothing is ready before init.
229        was_ready = false;
230    }
231    return was_ready;
232}
233
234void
235Scheduler::requestUpdate(Channel *c)
236{
237    updateList.pushLast(c);
238    if (!inEvaluate())
239        scheduleReadyEvent();
240}
241
242void
243Scheduler::scheduleReadyEvent()
244{
245    // Schedule the evaluate and update phases.
246    if (!readyEvent.scheduled()) {
247        schedule(&readyEvent);
248        if (starvationEvent.scheduled())
249            deschedule(&starvationEvent);
250    }
251}
252
253void
254Scheduler::scheduleStarvationEvent()
255{
256    if (!starvationEvent.scheduled()) {
257        schedule(&starvationEvent);
258        if (readyEvent.scheduled())
259            deschedule(&readyEvent);
260    }
261}
262
263void
264Scheduler::runReady()
265{
266    bool empty = readyListMethods.empty() && readyListThreads.empty();
267    lastReadyTick = getCurTick();
268
269    // The evaluation phase.
270    do {
271        yield();
272    } while (getNextReady());
273
274    if (!empty) {
275        _numCycles++;
276        _changeStamp++;
277    }
278
279    if (_stopNow) {
280        status(StatusOther);
281        return;
282    }
283
284    runUpdate();
285    runDelta();
286
287    if (!runToTime && starved())
288        scheduleStarvationEvent();
289
290    if (runOnce)
291        schedulePause();
292
293    status(StatusOther);
294}
295
296void
297Scheduler::runUpdate()
298{
299    status(StatusUpdate);
300
301    try {
302        Channel *channel = updateList.getNext();
303        while (channel) {
304            channel->popListNode();
305            channel->update();
306            channel = updateList.getNext();
307        }
308    } catch (...) {
309        throwToScMain();
310    }
311}
312
313void
314Scheduler::runDelta()
315{
316    status(StatusDelta);
317
318    try {
319        while (!deltas.empty())
320            deltas.front()->run();
321    } catch (...) {
322        throwToScMain();
323    }
324}
325
326void
327Scheduler::pause()
328{
329    status(StatusPaused);
330    kernel->status(::sc_core::SC_PAUSED);
331    runOnce = false;
332    if (scMain && !scMain->finished())
333        scMain->run();
334}
335
336void
337Scheduler::stop()
338{
339    status(StatusStopped);
340    kernel->stop();
341
342    clear();
343
344    runOnce = false;
345    if (scMain && !scMain->finished())
346        scMain->run();
347}
348
349void
350Scheduler::start(Tick max_tick, bool run_to_time)
351{
352    // We should be running from sc_main. Keep track of that Fiber to return
353    // to later.
354    scMain = Fiber::currentFiber();
355
356    _started = true;
357    status(StatusOther);
358    runToTime = run_to_time;
359
360    maxTick = max_tick;
361    lastReadyTick = getCurTick();
362
363    if (initDone) {
364        if (!runToTime && starved())
365            scheduleStarvationEvent();
366        kernel->status(::sc_core::SC_RUNNING);
367    }
368
369    schedule(&maxTickEvent, maxTick);
370
371    // Return to gem5 to let it run events, etc.
372    Fiber::primaryFiber()->run();
373
374    if (pauseEvent.scheduled())
375        deschedule(&pauseEvent);
376    if (stopEvent.scheduled())
377        deschedule(&stopEvent);
378    if (maxTickEvent.scheduled())
379        deschedule(&maxTickEvent);
380    if (starvationEvent.scheduled())
381        deschedule(&starvationEvent);
382
383    if (_throwToScMain) {
384        const ::sc_core::sc_report *to_throw = _throwToScMain;
385        _throwToScMain = nullptr;
386        throw *to_throw;
387    }
388}
389
390void
391Scheduler::oneCycle()
392{
393    runOnce = true;
394    scheduleReadyEvent();
395    start(::MaxTick, false);
396}
397
398void
399Scheduler::schedulePause()
400{
401    if (pauseEvent.scheduled())
402        return;
403
404    schedule(&pauseEvent);
405}
406
407void
408Scheduler::throwToScMain(const ::sc_core::sc_report *r)
409{
410    if (!r)
411        r = reportifyException();
412    _throwToScMain = r;
413    status(StatusOther);
414    scMain->run();
415}
416
417void
418Scheduler::scheduleStop(bool finish_delta)
419{
420    if (stopEvent.scheduled())
421        return;
422
423    if (!finish_delta) {
424        _stopNow = true;
425        // If we're not supposed to finish the delta cycle, flush all
426        // pending activity.
427        clear();
428    }
429    schedule(&stopEvent);
430}
431
432Scheduler scheduler;
433
434namespace {
435
436void
437throwingReportHandler(const ::sc_core::sc_report &r,
438                      const ::sc_core::sc_actions &)
439{
440    throw r;
441}
442
443} // anonymous namespace
444
445const ::sc_core::sc_report *
446reportifyException()
447{
448    ::sc_core::sc_report_handler_proc old_handler =
449        ::sc_core::sc_report_handler::get_handler();
450    ::sc_core::sc_report_handler::set_handler(&throwingReportHandler);
451
452    try {
453        try {
454            // Rethrow the current exception so we can catch it and throw an
455            // sc_report instead if it's not a type we recognize/can handle.
456            throw;
457        } catch (const ::sc_core::sc_report &) {
458            // It's already a sc_report, so nothing to do.
459            throw;
460        } catch (const ::sc_core::sc_unwind_exception &) {
461            panic("Kill/reset exception escaped a Process::run()");
462        } catch (const std::exception &e) {
463            SC_REPORT_ERROR("uncaught exception", e.what());
464        } catch (const char *msg) {
465            SC_REPORT_ERROR("uncaught exception", msg);
466        } catch (...) {
467            SC_REPORT_ERROR("uncaught exception", "UNKNOWN EXCEPTION");
468        }
469    } catch (const ::sc_core::sc_report &r) {
470        ::sc_core::sc_report_handler::set_handler(old_handler);
471        return &r;
472    }
473    panic("No exception thrown in reportifyException.");
474}
475
476} // namespace sc_gem5
477