scheduler.cc revision 13154:f86c71dac456
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)
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    if (_stopNow)
193        return;
194
195    // Clump methods together to minimize context switching.
196    static bool cluster_methods = false;
197
198    if (cluster_methods && p->procKind() == ::sc_core::SC_METHOD_PROC_)
199        readyList.pushFirst(p);
200    else
201        readyList.pushLast(p);
202
203    scheduleReadyEvent();
204}
205
206void
207Scheduler::resume(Process *p)
208{
209    if (initDone)
210        ready(p);
211    else
212        initList.pushLast(p);
213}
214
215bool
216Scheduler::suspend(Process *p)
217{
218    if (initDone) {
219        // After initialization, the only list we can be on is the ready list.
220        bool was_ready = (p->nextListNode != nullptr);
221        p->popListNode();
222        return was_ready;
223    } else {
224        bool was_ready = false;
225        // Check the ready list to see if we find this process.
226        ListNode *n = readyList.nextListNode;
227        while (n != &readyList) {
228            if (n == p) {
229                was_ready = true;
230                break;
231            }
232        }
233        if (was_ready)
234            toFinalize.pushLast(p);
235        return was_ready;
236    }
237}
238
239void
240Scheduler::requestUpdate(Channel *c)
241{
242    updateList.pushLast(c);
243    scheduleReadyEvent();
244}
245
246void
247Scheduler::scheduleReadyEvent()
248{
249    // Schedule the evaluate and update phases.
250    if (!readyEvent.scheduled()) {
251        schedule(&readyEvent);
252        if (starvationEvent.scheduled())
253            deschedule(&starvationEvent);
254    }
255}
256
257void
258Scheduler::scheduleStarvationEvent()
259{
260    if (!starvationEvent.scheduled()) {
261        schedule(&starvationEvent);
262        if (readyEvent.scheduled())
263            deschedule(&readyEvent);
264    }
265}
266
267void
268Scheduler::runReady()
269{
270    bool empty = readyList.empty();
271    lastReadyTick = getCurTick();
272
273    // The evaluation phase.
274    do {
275        yield();
276    } while (!readyList.empty());
277
278    if (!empty) {
279        _numCycles++;
280        _changeStamp++;
281    }
282
283    if (_stopNow)
284        return;
285
286    // The update phase.
287    update();
288
289    // The delta phase.
290    while (!deltas.empty())
291        deltas.front()->run();
292
293    if (!runToTime && starved())
294        scheduleStarvationEvent();
295
296    if (runOnce)
297        schedulePause();
298}
299
300void
301Scheduler::update()
302{
303    Channel *channel = updateList.getNext();
304    while (channel) {
305        channel->popListNode();
306        channel->update();
307        channel = updateList.getNext();
308    }
309}
310
311void
312Scheduler::pause()
313{
314    _paused = true;
315    kernel->status(::sc_core::SC_PAUSED);
316    runOnce = false;
317    scMain->run();
318}
319
320void
321Scheduler::stop()
322{
323    _stopped = true;
324    kernel->stop();
325
326    clear();
327
328    runOnce = false;
329    scMain->run();
330}
331
332void
333Scheduler::start(Tick max_tick, bool run_to_time)
334{
335    // We should be running from sc_main. Keep track of that Fiber to return
336    // to later.
337    scMain = Fiber::currentFiber();
338
339    _started = true;
340    _paused = false;
341    _stopped = false;
342    runToTime = run_to_time;
343
344    maxTick = max_tick;
345    lastReadyTick = getCurTick();
346
347    if (initDone) {
348        if (!runToTime && starved())
349            scheduleStarvationEvent();
350        kernel->status(::sc_core::SC_RUNNING);
351    }
352
353    schedule(&maxTickEvent, maxTick);
354
355    // Return to gem5 to let it run events, etc.
356    Fiber::primaryFiber()->run();
357
358    if (pauseEvent.scheduled())
359        deschedule(&pauseEvent);
360    if (stopEvent.scheduled())
361        deschedule(&stopEvent);
362    if (maxTickEvent.scheduled())
363        deschedule(&maxTickEvent);
364    if (starvationEvent.scheduled())
365        deschedule(&starvationEvent);
366}
367
368void
369Scheduler::oneCycle()
370{
371    runOnce = true;
372    scheduleReadyEvent();
373    start(::MaxTick, false);
374}
375
376void
377Scheduler::schedulePause()
378{
379    if (pauseEvent.scheduled())
380        return;
381
382    schedule(&pauseEvent);
383}
384
385void
386Scheduler::scheduleStop(bool finish_delta)
387{
388    if (stopEvent.scheduled())
389        return;
390
391    if (!finish_delta) {
392        _stopNow = true;
393        // If we're not supposed to finish the delta cycle, flush all
394        // pending activity.
395        clear();
396    }
397    schedule(&stopEvent);
398}
399
400Scheduler scheduler;
401
402} // namespace sc_gem5
403