scheduler.cc revision 13140:ecd8a58f3884
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    for (auto &e: deltas)
65        e->deschedule();
66    deltas.clear();
67
68    // Timed notifications.
69    for (auto &tsp: timeSlots) {
70        TimeSlot *&ts = tsp.second;
71        for (auto &e: ts->events)
72            e->deschedule();
73        deschedule(ts);
74    }
75    timeSlots.clear();
76
77    // gem5 events.
78    if (readyEvent.scheduled())
79        deschedule(&readyEvent);
80    if (pauseEvent.scheduled())
81        deschedule(&pauseEvent);
82    if (stopEvent.scheduled())
83        deschedule(&stopEvent);
84    if (starvationEvent.scheduled())
85        deschedule(&starvationEvent);
86    if (maxTickEvent.scheduled())
87        deschedule(&maxTickEvent);
88
89    Process *p;
90    while ((p = toFinalize.getNext()))
91        p->popListNode();
92    while ((p = initList.getNext()))
93        p->popListNode();
94    while ((p = readyList.getNext()))
95        p->popListNode();
96
97    Channel *c;
98    while ((c = updateList.getNext()))
99        c->popListNode();
100}
101
102void
103Scheduler::initPhase()
104{
105    for (Process *p = toFinalize.getNext(); p; p = toFinalize.getNext()) {
106        p->finalize();
107        p->popListNode();
108    }
109
110    for (Process *p = initList.getNext(); p; p = initList.getNext()) {
111        p->finalize();
112        p->popListNode();
113        p->ready();
114    }
115
116    update();
117
118    for (auto &e: deltas)
119        e->run();
120    deltas.clear();
121
122    for (auto ets: eventsToSchedule)
123        eq->schedule(ets.first, ets.second);
124    eventsToSchedule.clear();
125
126    if (_started) {
127        if (!runToTime && starved())
128            scheduleStarvationEvent();
129        kernel->status(::sc_core::SC_RUNNING);
130    }
131
132    initDone = true;
133}
134
135void
136Scheduler::reg(Process *p)
137{
138    if (initDone) {
139        // If we're past initialization, finalize static sensitivity.
140        p->finalize();
141        // Mark the process as ready.
142        p->ready();
143    } else {
144        // Otherwise, record that this process should be initialized once we
145        // get there.
146        initList.pushLast(p);
147    }
148}
149
150void
151Scheduler::dontInitialize(Process *p)
152{
153    if (initDone) {
154        // Pop this process off of the ready list.
155        p->popListNode();
156    } else {
157        // Push this process onto the list of processes which still need
158        // their static sensitivity to be finalized. That implicitly pops it
159        // off the list of processes to be initialized/marked ready.
160        toFinalize.pushLast(p);
161    }
162}
163
164void
165Scheduler::yield()
166{
167    _current = readyList.getNext();
168    if (!_current) {
169        // There are no more processes, so return control to evaluate.
170        Fiber::primaryFiber()->run();
171    } else {
172        _current->popListNode();
173        // Switch to whatever Fiber is supposed to run this process. All
174        // Fibers which aren't running should be parked at this line.
175        _current->fiber()->run();
176        // If the current process needs to be manually started, start it.
177        if (_current && _current->needsStart()) {
178            _current->needsStart(false);
179            _current->run();
180        }
181    }
182    if (_current && _current->excWrapper) {
183        // Make sure this isn't a method process.
184        assert(!_current->needsStart());
185        auto ew = _current->excWrapper;
186        _current->excWrapper = nullptr;
187        ew->throw_it();
188    }
189}
190
191void
192Scheduler::ready(Process *p)
193{
194    // Clump methods together to minimize context switching.
195    if (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    for (auto &e: deltas)
285        e->run();
286    deltas.clear();
287
288    if (!runToTime && starved())
289        scheduleStarvationEvent();
290
291    if (runOnce)
292        schedulePause();
293}
294
295void
296Scheduler::update()
297{
298    Channel *channel = updateList.getNext();
299    while (channel) {
300        channel->popListNode();
301        channel->update();
302        channel = updateList.getNext();
303    }
304}
305
306void
307Scheduler::pause()
308{
309    _paused = true;
310    kernel->status(::sc_core::SC_PAUSED);
311    runOnce = false;
312    scMain->run();
313}
314
315void
316Scheduler::stop()
317{
318    _stopped = true;
319    kernel->stop();
320
321    clear();
322
323    runOnce = false;
324    scMain->run();
325}
326
327void
328Scheduler::start(Tick max_tick, bool run_to_time)
329{
330    // We should be running from sc_main. Keep track of that Fiber to return
331    // to later.
332    scMain = Fiber::currentFiber();
333
334    _started = true;
335    _paused = false;
336    _stopped = false;
337    runToTime = run_to_time;
338
339    maxTick = max_tick;
340    lastReadyTick = getCurTick();
341
342    if (initDone) {
343        if (!runToTime && starved())
344            scheduleStarvationEvent();
345        kernel->status(::sc_core::SC_RUNNING);
346    }
347
348    schedule(&maxTickEvent, maxTick);
349
350    // Return to gem5 to let it run events, etc.
351    Fiber::primaryFiber()->run();
352
353    if (pauseEvent.scheduled())
354        deschedule(&pauseEvent);
355    if (stopEvent.scheduled())
356        deschedule(&stopEvent);
357    if (maxTickEvent.scheduled())
358        deschedule(&maxTickEvent);
359    if (starvationEvent.scheduled())
360        deschedule(&starvationEvent);
361}
362
363void
364Scheduler::oneCycle()
365{
366    runOnce = true;
367    scheduleReadyEvent();
368    start(::MaxTick, false);
369}
370
371void
372Scheduler::schedulePause()
373{
374    if (pauseEvent.scheduled())
375        return;
376
377    schedule(&pauseEvent);
378}
379
380void
381Scheduler::scheduleStop(bool finish_delta)
382{
383    if (stopEvent.scheduled())
384        return;
385
386    if (!finish_delta) {
387        // If we're not supposed to finish the delta cycle, flush all
388        // pending activity.
389        clear();
390    }
391    schedule(&stopEvent);
392}
393
394Scheduler scheduler;
395
396} // namespace sc_gem5
397