scheduler.cc revision 13145
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),
48    maxTickEvent(this, false, MaxTickPriority),
49    _numCycles(0), _changeStamp(0), _current(nullptr), initDone(false),
50    runOnce(false)
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 = readyList.getNext()))
94        p->popListNode();
95
96    Channel *c;
97    while ((c = updateList.getNext()))
98        c->popListNode();
99}
100
101void
102Scheduler::initPhase()
103{
104    for (Process *p = toFinalize.getNext(); p; p = toFinalize.getNext()) {
105        p->finalize();
106        p->popListNode();
107    }
108
109    for (Process *p = initList.getNext(); p; p = initList.getNext()) {
110        p->finalize();
111        p->popListNode();
112        p->ready();
113    }
114
115    update();
116
117    while (!deltas.empty())
118        deltas.front()->run();
119
120    for (auto ets: eventsToSchedule)
121        eq->schedule(ets.first, ets.second);
122    eventsToSchedule.clear();
123
124    if (_started) {
125        if (!runToTime && starved())
126            scheduleStarvationEvent();
127        kernel->status(::sc_core::SC_RUNNING);
128    }
129
130    initDone = true;
131}
132
133void
134Scheduler::reg(Process *p)
135{
136    if (initDone) {
137        // If we're past initialization, finalize static sensitivity.
138        p->finalize();
139        // Mark the process as ready.
140        p->ready();
141    } else {
142        // Otherwise, record that this process should be initialized once we
143        // get there.
144        initList.pushLast(p);
145    }
146}
147
148void
149Scheduler::dontInitialize(Process *p)
150{
151    if (initDone) {
152        // Pop this process off of the ready list.
153        p->popListNode();
154    } else {
155        // Push this process onto the list of processes which still need
156        // their static sensitivity to be finalized. That implicitly pops it
157        // off the list of processes to be initialized/marked ready.
158        toFinalize.pushLast(p);
159    }
160}
161
162void
163Scheduler::yield()
164{
165    _current = readyList.getNext();
166    if (!_current) {
167        // There are no more processes, so return control to evaluate.
168        Fiber::primaryFiber()->run();
169    } else {
170        _current->popListNode();
171        // Switch to whatever Fiber is supposed to run this process. All
172        // Fibers which aren't running should be parked at this line.
173        _current->fiber()->run();
174        // If the current process needs to be manually started, start it.
175        if (_current && _current->needsStart()) {
176            _current->needsStart(false);
177            _current->run();
178        }
179    }
180    if (_current && _current->excWrapper) {
181        // Make sure this isn't a method process.
182        assert(!_current->needsStart());
183        auto ew = _current->excWrapper;
184        _current->excWrapper = nullptr;
185        ew->throw_it();
186    }
187}
188
189void
190Scheduler::ready(Process *p)
191{
192    // Clump methods together to minimize context switching.
193    static bool cluster_methods = false;
194
195    if (cluster_methods && p->procKind() == ::sc_core::SC_METHOD_PROC_)
196        readyList.pushFirst(p);
197    else
198        readyList.pushLast(p);
199
200    scheduleReadyEvent();
201}
202
203void
204Scheduler::resume(Process *p)
205{
206    if (initDone)
207        ready(p);
208    else
209        initList.pushLast(p);
210}
211
212bool
213Scheduler::suspend(Process *p)
214{
215    if (initDone) {
216        // After initialization, the only list we can be on is the ready list.
217        bool was_ready = (p->nextListNode != nullptr);
218        p->popListNode();
219        return was_ready;
220    } else {
221        bool was_ready = false;
222        // Check the ready list to see if we find this process.
223        ListNode *n = readyList.nextListNode;
224        while (n != &readyList) {
225            if (n == p) {
226                was_ready = true;
227                break;
228            }
229        }
230        if (was_ready)
231            toFinalize.pushLast(p);
232        return was_ready;
233    }
234}
235
236void
237Scheduler::requestUpdate(Channel *c)
238{
239    updateList.pushLast(c);
240    scheduleReadyEvent();
241}
242
243void
244Scheduler::scheduleReadyEvent()
245{
246    // Schedule the evaluate and update phases.
247    if (!readyEvent.scheduled()) {
248        schedule(&readyEvent);
249        if (starvationEvent.scheduled())
250            deschedule(&starvationEvent);
251    }
252}
253
254void
255Scheduler::scheduleStarvationEvent()
256{
257    if (!starvationEvent.scheduled()) {
258        schedule(&starvationEvent);
259        if (readyEvent.scheduled())
260            deschedule(&readyEvent);
261    }
262}
263
264void
265Scheduler::runReady()
266{
267    bool empty = readyList.empty();
268    lastReadyTick = getCurTick();
269
270    // The evaluation phase.
271    do {
272        yield();
273    } while (!readyList.empty());
274
275    if (!empty) {
276        _numCycles++;
277        _changeStamp++;
278    }
279
280    // The update phase.
281    update();
282
283    // The delta phase.
284    while (!deltas.empty())
285        deltas.front()->run();
286
287    if (!runToTime && starved())
288        scheduleStarvationEvent();
289
290    if (runOnce)
291        schedulePause();
292}
293
294void
295Scheduler::update()
296{
297    Channel *channel = updateList.getNext();
298    while (channel) {
299        channel->popListNode();
300        channel->update();
301        channel = updateList.getNext();
302    }
303}
304
305void
306Scheduler::pause()
307{
308    _paused = true;
309    kernel->status(::sc_core::SC_PAUSED);
310    runOnce = false;
311    scMain->run();
312}
313
314void
315Scheduler::stop()
316{
317    _stopped = true;
318    kernel->stop();
319
320    clear();
321
322    runOnce = false;
323    scMain->run();
324}
325
326void
327Scheduler::start(Tick max_tick, bool run_to_time)
328{
329    // We should be running from sc_main. Keep track of that Fiber to return
330    // to later.
331    scMain = Fiber::currentFiber();
332
333    _started = true;
334    _paused = false;
335    _stopped = false;
336    runToTime = run_to_time;
337
338    maxTick = max_tick;
339    lastReadyTick = getCurTick();
340
341    if (initDone) {
342        if (!runToTime && starved())
343            scheduleStarvationEvent();
344        kernel->status(::sc_core::SC_RUNNING);
345    }
346
347    schedule(&maxTickEvent, maxTick);
348
349    // Return to gem5 to let it run events, etc.
350    Fiber::primaryFiber()->run();
351
352    if (pauseEvent.scheduled())
353        deschedule(&pauseEvent);
354    if (stopEvent.scheduled())
355        deschedule(&stopEvent);
356    if (maxTickEvent.scheduled())
357        deschedule(&maxTickEvent);
358    if (starvationEvent.scheduled())
359        deschedule(&starvationEvent);
360}
361
362void
363Scheduler::oneCycle()
364{
365    runOnce = true;
366    scheduleReadyEvent();
367    start(::MaxTick, false);
368}
369
370void
371Scheduler::schedulePause()
372{
373    if (pauseEvent.scheduled())
374        return;
375
376    schedule(&pauseEvent);
377}
378
379void
380Scheduler::scheduleStop(bool finish_delta)
381{
382    if (stopEvent.scheduled())
383        return;
384
385    if (!finish_delta) {
386        // If we're not supposed to finish the delta cycle, flush all
387        // pending activity.
388        clear();
389    }
390    schedule(&stopEvent);
391}
392
393Scheduler scheduler;
394
395} // namespace sc_gem5
396