scheduler.cc revision 13275:67a279e54b7a
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"
313877Sbinkertn@umich.edu
323877Sbinkertn@umich.edu#include "base/fiber.hh"
332147SN/A#include "base/logging.hh"
348221Snate@binkert.org#include "sim/eventq.hh"
358221Snate@binkert.org#include "systemc/core/kernel.hh"
368221Snate@binkert.org#include "systemc/ext/core/sc_main.hh"
378221Snate@binkert.org#include "systemc/ext/utils/sc_report.hh"
388221Snate@binkert.org#include "systemc/ext/utils/sc_report_handler.hh"
398221Snate@binkert.org#include "systemc/utils/tracefile.hh"
408221Snate@binkert.org
418221Snate@binkert.orgnamespace sc_gem5
428221Snate@binkert.org{
438221Snate@binkert.org
448221Snate@binkert.orgScheduler::Scheduler() :
452SN/A    eq(nullptr), readyEvent(this, false, ReadyPriority),
462SN/A    pauseEvent(this, false, PausePriority),
472SN/A    stopEvent(this, false, StopPriority),
488221Snate@binkert.org    scMain(nullptr), _throwToScMain(nullptr),
498221Snate@binkert.org    starvationEvent(this, false, StarvationPriority),
508221Snate@binkert.org    _elaborationDone(false), _started(false), _stopNow(false),
518221Snate@binkert.org    _status(StatusOther), maxTickEvent(this, false, MaxTickPriority),
527866Snate@binkert.org    timeAdvancesEvent(this, false, TimeAdvancesPriority), _numCycles(0),
532SN/A    _changeStamp(0), _current(nullptr), initDone(false), runOnce(false)
542SN/A{}
557057Snate@binkert.org
567057Snate@binkert.orgScheduler::~Scheduler()
577057Snate@binkert.org{
582SN/A    // Clear out everything that belongs to us to make sure nobody tries to
597057Snate@binkert.org    // clear themselves out after the scheduler goes away.
602SN/A    clear();
612SN/A}
628221Snate@binkert.org
638221Snate@binkert.orgvoid
648221Snate@binkert.orgScheduler::clear()
658221Snate@binkert.org{
668221Snate@binkert.org    // Delta notifications.
678221Snate@binkert.org    while (!deltas.empty())
688221Snate@binkert.org        deltas.front()->deschedule();
692SN/A
708221Snate@binkert.org    // Timed notifications.
718221Snate@binkert.org    for (auto &tsp: timeSlots) {
728221Snate@binkert.org        TimeSlot *&ts = tsp.second;
738221Snate@binkert.org        while (!ts->events.empty())
748221Snate@binkert.org            ts->events.front()->deschedule();
758221Snate@binkert.org        deschedule(ts);
768221Snate@binkert.org    }
778221Snate@binkert.org    timeSlots.clear();
788221Snate@binkert.org
798221Snate@binkert.org    // gem5 events.
802SN/A    if (readyEvent.scheduled())
812SN/A        deschedule(&readyEvent);
828221Snate@binkert.org    if (pauseEvent.scheduled())
832SN/A        deschedule(&pauseEvent);
848221Snate@binkert.org    if (stopEvent.scheduled())
858221Snate@binkert.org        deschedule(&stopEvent);
868221Snate@binkert.org    if (starvationEvent.scheduled())
872SN/A        deschedule(&starvationEvent);
882SN/A    if (maxTickEvent.scheduled())
892SN/A        deschedule(&maxTickEvent);
908221Snate@binkert.org    if (timeAdvancesEvent.scheduled())
918221Snate@binkert.org        deschedule(&timeAdvancesEvent);
928221Snate@binkert.org
938221Snate@binkert.org    Process *p;
948221Snate@binkert.org    while ((p = initList.getNext()))
958221Snate@binkert.org        p->popListNode();
968221Snate@binkert.org    while ((p = readyListMethods.getNext()))
978221Snate@binkert.org        p->popListNode();
988221Snate@binkert.org    while ((p = readyListThreads.getNext()))
998221Snate@binkert.org        p->popListNode();
1008221Snate@binkert.org
1018221Snate@binkert.org    Channel *c;
1028221Snate@binkert.org    while ((c = updateList.getNext()))
1038221Snate@binkert.org        c->popListNode();
1048221Snate@binkert.org}
1052SN/A
1062SN/Avoid
1072SN/AScheduler::initPhase()
1081078SN/A{
1098221Snate@binkert.org    for (Process *p = initList.getNext(); p; p = initList.getNext()) {
1108221Snate@binkert.org        p->popListNode();
1112SN/A
1122SN/A        if (p->dontInitialize()) {
1138221Snate@binkert.org            if (!p->hasStaticSensitivities() && !p->internal()) {
1148221Snate@binkert.org                SC_REPORT_WARNING(
1158221Snate@binkert.org                        "(W558) disable() or dont_initialize() called on "
1168221Snate@binkert.org                        "process with no static sensitivity, it will be "
1178221Snate@binkert.org                        "orphaned", p->name());
1188221Snate@binkert.org            }
1198221Snate@binkert.org        } else {
1208221Snate@binkert.org            p->ready();
1211114SN/A        }
1222SN/A    }
1232SN/A
1242SN/A    runUpdate();
1252SN/A    runDelta();
1268221Snate@binkert.org
1278221Snate@binkert.org    for (auto ets: eventsToSchedule)
1288221Snate@binkert.org        eq->schedule(ets.first, ets.second);
1298221Snate@binkert.org    eventsToSchedule.clear();
1308221Snate@binkert.org
1318221Snate@binkert.org    if (_started) {
1328221Snate@binkert.org        if (!runToTime && starved())
1338221Snate@binkert.org            scheduleStarvationEvent();
1341114SN/A        kernel->status(::sc_core::SC_RUNNING);
1352SN/A    }
1362SN/A
1372SN/A    initDone = true;
1388221Snate@binkert.org
1398221Snate@binkert.org    status(StatusOther);
1408221Snate@binkert.org
1418221Snate@binkert.org    scheduleTimeAdvancesEvent();
1428221Snate@binkert.org}
1438221Snate@binkert.org
1441114SN/Avoid
1458221Snate@binkert.orgScheduler::reg(Process *p)
1468221Snate@binkert.org{
1478221Snate@binkert.org    if (initDone) {
1488221Snate@binkert.org        // If not marked as dontInitialize, mark as ready.
1498221Snate@binkert.org        if (!p->dontInitialize())
1508221Snate@binkert.org            p->ready();
1518221Snate@binkert.org    } else {
1521114SN/A        // Otherwise, record that this process should be initialized once we
1531114SN/A        // get there.
1548221Snate@binkert.org        initList.pushLast(p);
1558221Snate@binkert.org    }
1568221Snate@binkert.org}
1572SN/A
1588221Snate@binkert.orgvoid
1598221Snate@binkert.orgScheduler::yield()
1602SN/A{
1618221Snate@binkert.org    // Pull a process from the active list.
1628221Snate@binkert.org    _current = getNextReady();
1638221Snate@binkert.org    if (!_current) {
164502SN/A        // There are no more processes, so return control to evaluate.
1658221Snate@binkert.org        Fiber::primaryFiber()->run();
1668221Snate@binkert.org    } else {
1672SN/A        _current->popListNode();
1682SN/A        // Switch to whatever Fiber is supposed to run this process. All
1698221Snate@binkert.org        // Fibers which aren't running should be parked at this line.
1708221Snate@binkert.org        _current->fiber()->run();
1718221Snate@binkert.org        // If the current process needs to be manually started, start it.
1728221Snate@binkert.org        if (_current && _current->needsStart()) {
1738221Snate@binkert.org            _current->needsStart(false);
1747866Snate@binkert.org            try {
1758221Snate@binkert.org                _current->run();
1768221Snate@binkert.org            } catch (...) {
1777866Snate@binkert.org                throwToScMain();
1788221Snate@binkert.org            }
1798221Snate@binkert.org        }
1807866Snate@binkert.org    }
1812SN/A    if (_current && !_current->needsStart()) {
1828221Snate@binkert.org        if (_current->excWrapper) {
1833877Sbinkertn@umich.edu            auto ew = _current->excWrapper;
1848221Snate@binkert.org            _current->excWrapper = nullptr;
1858221Snate@binkert.org            ew->throw_it();
1863877Sbinkertn@umich.edu        } else if (_current->inReset()) {
1871114SN/A            _current->reset(false);
1882SN/A        }
1898221Snate@binkert.org    }
1902SN/A}
1918221Snate@binkert.org
1928221Snate@binkert.orgvoid
1932SN/AScheduler::ready(Process *p)
1942SN/A{
1952SN/A    if (_stopNow)
1968221Snate@binkert.org        return;
1972SN/A
1988220Snate@binkert.org    if (p->procKind() == ::sc_core::SC_METHOD_PROC_)
1992SN/A        readyListMethods.pushLast(p);
2002SN/A    else
2018221Snate@binkert.org        readyListThreads.pushLast(p);
2028221Snate@binkert.org
2032SN/A    if (!inEvaluate())
2048220Snate@binkert.org        scheduleReadyEvent();
2052SN/A}
2062SN/A
2078221Snate@binkert.orgvoid
2088221Snate@binkert.orgScheduler::resume(Process *p)
2092SN/A{
2108220Snate@binkert.org    if (initDone)
2112SN/A        ready(p);
2122SN/A    else
2138221Snate@binkert.org        initList.pushLast(p);
2142SN/A}
2158220Snate@binkert.org
2162SN/Abool
2172SN/AlistContains(ListNode *list, ListNode *target)
2188221Snate@binkert.org{
2198221Snate@binkert.org    ListNode *n = list->nextListNode;
2202SN/A    while (n != list)
2218220Snate@binkert.org        if (n == target)
2222SN/A            return true;
2232SN/A    return false;
2248221Snate@binkert.org}
2258221Snate@binkert.org
2262SN/Abool
2278220Snate@binkert.orgScheduler::suspend(Process *p)
2282SN/A{
2292SN/A    bool was_ready;
2303877Sbinkertn@umich.edu    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    _current = nullptr;
284
285    if (!empty) {
286        _numCycles++;
287        _changeStamp++;
288    }
289
290    if (_stopNow) {
291        status(StatusOther);
292        return;
293    }
294
295    runUpdate();
296    if (!traceFiles.empty())
297        trace(true);
298    runDelta();
299
300    if (!runToTime && starved())
301        scheduleStarvationEvent();
302
303    if (runOnce)
304        schedulePause();
305
306    status(StatusOther);
307}
308
309void
310Scheduler::runUpdate()
311{
312    status(StatusUpdate);
313
314    try {
315        Channel *channel = updateList.getNext();
316        while (channel) {
317            channel->popListNode();
318            channel->update();
319            channel = updateList.getNext();
320        }
321    } catch (...) {
322        throwToScMain();
323    }
324}
325
326void
327Scheduler::runDelta()
328{
329    status(StatusDelta);
330
331    try {
332        while (!deltas.empty())
333            deltas.front()->run();
334    } catch (...) {
335        throwToScMain();
336    }
337}
338
339void
340Scheduler::pause()
341{
342    status(StatusPaused);
343    kernel->status(::sc_core::SC_PAUSED);
344    runOnce = false;
345    if (scMain && !scMain->finished())
346        scMain->run();
347}
348
349void
350Scheduler::stop()
351{
352    status(StatusStopped);
353    kernel->stop();
354
355    clear();
356
357    runOnce = false;
358    if (scMain && !scMain->finished())
359        scMain->run();
360}
361
362void
363Scheduler::start(Tick max_tick, bool run_to_time)
364{
365    // We should be running from sc_main. Keep track of that Fiber to return
366    // to later.
367    scMain = Fiber::currentFiber();
368
369    _started = true;
370    status(StatusOther);
371    runToTime = run_to_time;
372
373    maxTick = max_tick;
374    lastReadyTick = getCurTick();
375
376    if (initDone) {
377        if (!runToTime && starved())
378            scheduleStarvationEvent();
379        kernel->status(::sc_core::SC_RUNNING);
380    }
381
382    schedule(&maxTickEvent, maxTick);
383    scheduleTimeAdvancesEvent();
384
385    // Return to gem5 to let it run events, etc.
386    Fiber::primaryFiber()->run();
387
388    if (pauseEvent.scheduled())
389        deschedule(&pauseEvent);
390    if (stopEvent.scheduled())
391        deschedule(&stopEvent);
392    if (maxTickEvent.scheduled())
393        deschedule(&maxTickEvent);
394    if (starvationEvent.scheduled())
395        deschedule(&starvationEvent);
396
397    if (_throwToScMain) {
398        const ::sc_core::sc_report *to_throw = _throwToScMain;
399        _throwToScMain = nullptr;
400        throw *to_throw;
401    }
402}
403
404void
405Scheduler::oneCycle()
406{
407    runOnce = true;
408    scheduleReadyEvent();
409    start(::MaxTick, false);
410}
411
412void
413Scheduler::schedulePause()
414{
415    if (pauseEvent.scheduled())
416        return;
417
418    schedule(&pauseEvent);
419}
420
421void
422Scheduler::throwToScMain()
423{
424    ::sc_core::sc_report report = reportifyException();
425    _throwToScMain = &report;
426    status(StatusOther);
427    scMain->run();
428}
429
430void
431Scheduler::scheduleStop(bool finish_delta)
432{
433    if (stopEvent.scheduled())
434        return;
435
436    if (!finish_delta) {
437        _stopNow = true;
438        // If we're not supposed to finish the delta cycle, flush all
439        // pending activity.
440        clear();
441    }
442    schedule(&stopEvent);
443}
444
445void
446Scheduler::trace(bool delta)
447{
448    for (auto tf: traceFiles)
449        tf->trace(delta);
450}
451
452Scheduler scheduler;
453
454namespace {
455
456void
457throwingReportHandler(const ::sc_core::sc_report &r,
458                      const ::sc_core::sc_actions &)
459{
460    throw r;
461}
462
463} // anonymous namespace
464
465const ::sc_core::sc_report
466reportifyException()
467{
468    ::sc_core::sc_report_handler_proc old_handler =
469        ::sc_core::sc_report_handler::get_handler();
470    ::sc_core::sc_report_handler::set_handler(&throwingReportHandler);
471
472    try {
473        try {
474            // Rethrow the current exception so we can catch it and throw an
475            // sc_report instead if it's not a type we recognize/can handle.
476            throw;
477        } catch (const ::sc_core::sc_report &) {
478            // It's already a sc_report, so nothing to do.
479            throw;
480        } catch (const ::sc_core::sc_unwind_exception &) {
481            panic("Kill/reset exception escaped a Process::run()");
482        } catch (const std::exception &e) {
483            SC_REPORT_ERROR("uncaught exception", e.what());
484        } catch (const char *msg) {
485            SC_REPORT_ERROR("uncaught exception", msg);
486        } catch (...) {
487            SC_REPORT_ERROR("uncaught exception", "UNKNOWN EXCEPTION");
488        }
489    } catch (const ::sc_core::sc_report &r) {
490        ::sc_core::sc_report_handler::set_handler(old_handler);
491        return r;
492    }
493    panic("No exception thrown in reportifyException.");
494}
495
496} // namespace sc_gem5
497