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