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