scheduler.cc revision 13180:79e680f62779
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
38namespace sc_gem5
39{
40
41Scheduler::Scheduler() :
42    eq(nullptr), readyEvent(this, false, ReadyPriority),
43    pauseEvent(this, false, PausePriority),
44    stopEvent(this, false, StopPriority),
45    scMain(nullptr),
46    starvationEvent(this, false, StarvationPriority),
47    _started(false), _paused(false), _stopped(false), _stopNow(false),
48    maxTickEvent(this, false, MaxTickPriority),
49    _numCycles(0), _changeStamp(0), _current(nullptr), initDone(false),
50    runOnce(false), readyList(nullptr)
51{}
52
53Scheduler::~Scheduler()
54{
55    // Clear out everything that belongs to us to make sure nobody tries to
56    // clear themselves out after the scheduler goes away.
57    clear();
58}
59
60void
61Scheduler::clear()
62{
63    // Delta notifications.
64    while (!deltas.empty())
65        deltas.front()->deschedule();
66
67    // Timed notifications.
68    for (auto &tsp: timeSlots) {
69        TimeSlot *&ts = tsp.second;
70        while (!ts->events.empty())
71            ts->events.front()->deschedule();
72        deschedule(ts);
73    }
74    timeSlots.clear();
75
76    // gem5 events.
77    if (readyEvent.scheduled())
78        deschedule(&readyEvent);
79    if (pauseEvent.scheduled())
80        deschedule(&pauseEvent);
81    if (stopEvent.scheduled())
82        deschedule(&stopEvent);
83    if (starvationEvent.scheduled())
84        deschedule(&starvationEvent);
85    if (maxTickEvent.scheduled())
86        deschedule(&maxTickEvent);
87
88    Process *p;
89    while ((p = toFinalize.getNext()))
90        p->popListNode();
91    while ((p = initList.getNext()))
92        p->popListNode();
93    while ((p = readyListMethods.getNext()))
94        p->popListNode();
95    while ((p = readyListThreads.getNext()))
96        p->popListNode();
97
98    Channel *c;
99    while ((c = updateList.getNext()))
100        c->popListNode();
101}
102
103void
104Scheduler::initPhase()
105{
106    for (Process *p = toFinalize.getNext(); p; p = toFinalize.getNext()) {
107        p->finalize();
108        p->popListNode();
109
110        if (!p->hasStaticSensitivities() && !p->internal()) {
111            SC_REPORT_WARNING(
112                    "(W558) disable() or dont_initialize() called on process "
113                    "with no static sensitivity, it will be orphaned",
114                    p->name());
115        }
116    }
117
118    for (Process *p = initList.getNext(); p; p = initList.getNext()) {
119        p->finalize();
120        p->popListNode();
121        p->ready();
122    }
123
124    update();
125
126    while (!deltas.empty())
127        deltas.front()->run();
128
129    for (auto ets: eventsToSchedule)
130        eq->schedule(ets.first, ets.second);
131    eventsToSchedule.clear();
132
133    if (_started) {
134        if (!runToTime && starved())
135            scheduleStarvationEvent();
136        kernel->status(::sc_core::SC_RUNNING);
137    }
138
139    initDone = true;
140}
141
142void
143Scheduler::reg(Process *p)
144{
145    if (initDone) {
146        // If we're past initialization, finalize static sensitivity.
147        p->finalize();
148        // Mark the process as ready.
149        p->ready();
150    } else {
151        // Otherwise, record that this process should be initialized once we
152        // get there.
153        initList.pushLast(p);
154    }
155}
156
157void
158Scheduler::dontInitialize(Process *p)
159{
160    if (initDone) {
161        // Pop this process off of the ready list.
162        p->popListNode();
163    } else {
164        // Push this process onto the list of processes which still need
165        // their static sensitivity to be finalized. That implicitly pops it
166        // off the list of processes to be initialized/marked ready.
167        toFinalize.pushLast(p);
168    }
169}
170
171void
172Scheduler::yield()
173{
174    // Pull a process from the active list.
175    _current = readyList->getNext();
176    if (!_current) {
177        // There are no more processes, so return control to evaluate.
178        Fiber::primaryFiber()->run();
179    } else {
180        _current->popListNode();
181        // Switch to whatever Fiber is supposed to run this process. All
182        // Fibers which aren't running should be parked at this line.
183        _current->fiber()->run();
184        // If the current process needs to be manually started, start it.
185        if (_current && _current->needsStart()) {
186            _current->needsStart(false);
187            _current->run();
188        }
189    }
190    if (_current && _current->excWrapper) {
191        // Make sure this isn't a method process.
192        assert(!_current->needsStart());
193        auto ew = _current->excWrapper;
194        _current->excWrapper = nullptr;
195        ew->throw_it();
196    }
197}
198
199void
200Scheduler::ready(Process *p)
201{
202    if (_stopNow)
203        return;
204
205    if (p->procKind() == ::sc_core::SC_METHOD_PROC_)
206        readyListMethods.pushLast(p);
207    else
208        readyListThreads.pushLast(p);
209
210    scheduleReadyEvent();
211}
212
213void
214Scheduler::resume(Process *p)
215{
216    if (initDone)
217        ready(p);
218    else
219        initList.pushLast(p);
220}
221
222bool
223listContains(ListNode *list, ListNode *target)
224{
225    ListNode *n = list->nextListNode;
226    while (n != list)
227        if (n == target)
228            return true;
229    return false;
230}
231
232bool
233Scheduler::suspend(Process *p)
234{
235    bool was_ready;
236    if (initDone) {
237        // After initialization, the only list we can be on is the ready list.
238        was_ready = (p->nextListNode != nullptr);
239        p->popListNode();
240    } else {
241        // Check the ready lists to see if we find this process.
242        was_ready = listContains(&readyListMethods, p) ||
243            listContains(&readyListThreads, p);
244        if (was_ready)
245            toFinalize.pushLast(p);
246    }
247    return was_ready;
248}
249
250void
251Scheduler::requestUpdate(Channel *c)
252{
253    updateList.pushLast(c);
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    bool empty = readyListMethods.empty() && readyListThreads.empty();
282    lastReadyTick = getCurTick();
283
284    // The evaluation phase.
285    do {
286        // We run methods and threads in two seperate passes to emulate how
287        // Accellera orders things, but without having to scan through a
288        // unified list to find the next process of the correct type.
289        readyList = &readyListMethods;
290        while (!readyListMethods.empty())
291            yield();
292
293        readyList = &readyListThreads;
294        while (!readyListThreads.empty())
295            yield();
296
297        // We already know that readyListThreads is empty at this point.
298    } while (!readyListMethods.empty());
299
300    if (!empty) {
301        _numCycles++;
302        _changeStamp++;
303    }
304
305    if (_stopNow)
306        return;
307
308    // The update phase.
309    update();
310
311    // The delta phase.
312    while (!deltas.empty())
313        deltas.front()->run();
314
315    if (!runToTime && starved())
316        scheduleStarvationEvent();
317
318    if (runOnce)
319        schedulePause();
320}
321
322void
323Scheduler::update()
324{
325    Channel *channel = updateList.getNext();
326    while (channel) {
327        channel->popListNode();
328        channel->update();
329        channel = updateList.getNext();
330    }
331}
332
333void
334Scheduler::pause()
335{
336    _paused = true;
337    kernel->status(::sc_core::SC_PAUSED);
338    runOnce = false;
339    scMain->run();
340}
341
342void
343Scheduler::stop()
344{
345    _stopped = true;
346    kernel->stop();
347
348    clear();
349
350    runOnce = false;
351    scMain->run();
352}
353
354void
355Scheduler::start(Tick max_tick, bool run_to_time)
356{
357    // We should be running from sc_main. Keep track of that Fiber to return
358    // to later.
359    scMain = Fiber::currentFiber();
360
361    _started = true;
362    _paused = false;
363    _stopped = false;
364    runToTime = run_to_time;
365
366    maxTick = max_tick;
367    lastReadyTick = getCurTick();
368
369    if (initDone) {
370        if (!runToTime && starved())
371            scheduleStarvationEvent();
372        kernel->status(::sc_core::SC_RUNNING);
373    }
374
375    schedule(&maxTickEvent, maxTick);
376
377    // Return to gem5 to let it run events, etc.
378    Fiber::primaryFiber()->run();
379
380    if (pauseEvent.scheduled())
381        deschedule(&pauseEvent);
382    if (stopEvent.scheduled())
383        deschedule(&stopEvent);
384    if (maxTickEvent.scheduled())
385        deschedule(&maxTickEvent);
386    if (starvationEvent.scheduled())
387        deschedule(&starvationEvent);
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::scheduleStop(bool finish_delta)
409{
410    if (stopEvent.scheduled())
411        return;
412
413    if (!finish_delta) {
414        _stopNow = true;
415        // If we're not supposed to finish the delta cycle, flush all
416        // pending activity.
417        clear();
418    }
419    schedule(&stopEvent);
420}
421
422Scheduler scheduler;
423
424} // namespace sc_gem5
425