scheduler.cc revision 13145:5291e0747c7c
112837Sgabeblack@google.com/*
212837Sgabeblack@google.com * Copyright 2018 Google, Inc.
312837Sgabeblack@google.com *
412837Sgabeblack@google.com * Redistribution and use in source and binary forms, with or without
512837Sgabeblack@google.com * modification, are permitted provided that the following conditions are
612837Sgabeblack@google.com * met: redistributions of source code must retain the above copyright
712837Sgabeblack@google.com * notice, this list of conditions and the following disclaimer;
812837Sgabeblack@google.com * redistributions in binary form must reproduce the above copyright
912837Sgabeblack@google.com * notice, this list of conditions and the following disclaimer in the
1012837Sgabeblack@google.com * documentation and/or other materials provided with the distribution;
1112837Sgabeblack@google.com * neither the name of the copyright holders nor the names of its
1212837Sgabeblack@google.com * contributors may be used to endorse or promote products derived from
1312837Sgabeblack@google.com * this software without specific prior written permission.
1412837Sgabeblack@google.com *
1512837Sgabeblack@google.com * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
1612837Sgabeblack@google.com * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
1712837Sgabeblack@google.com * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
1812837Sgabeblack@google.com * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
1912837Sgabeblack@google.com * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
2012837Sgabeblack@google.com * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
2112837Sgabeblack@google.com * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
2212837Sgabeblack@google.com * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
2312837Sgabeblack@google.com * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
2412837Sgabeblack@google.com * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
2512837Sgabeblack@google.com * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
2612837Sgabeblack@google.com *
2712837Sgabeblack@google.com * Authors: Gabe Black
2812837Sgabeblack@google.com */
2912837Sgabeblack@google.com
3012837Sgabeblack@google.com#include "systemc/core/scheduler.hh"
3113053Sgabeblack@google.com
3213207Sgabeblack@google.com#include "base/fiber.hh"
3313203Sgabeblack@google.com#include "base/logging.hh"
3413203Sgabeblack@google.com#include "sim/eventq.hh"
3512837Sgabeblack@google.com#include "systemc/core/kernel.hh"
3612837Sgabeblack@google.com#include "systemc/ext/core/sc_main.hh"
3712837Sgabeblack@google.com
3812837Sgabeblack@google.comnamespace sc_gem5
3912837Sgabeblack@google.com{
4013203Sgabeblack@google.com
4113203Sgabeblack@google.comScheduler::Scheduler() :
4213203Sgabeblack@google.com    eq(nullptr), readyEvent(this, false, ReadyPriority),
4313203Sgabeblack@google.com    pauseEvent(this, false, PausePriority),
4413203Sgabeblack@google.com    stopEvent(this, false, StopPriority),
4513203Sgabeblack@google.com    scMain(nullptr),
4613203Sgabeblack@google.com    starvationEvent(this, false, StarvationPriority),
4713203Sgabeblack@google.com    _started(false), _paused(false), _stopped(false),
4813203Sgabeblack@google.com    maxTickEvent(this, false, MaxTickPriority),
4913203Sgabeblack@google.com    _numCycles(0), _changeStamp(0), _current(nullptr), initDone(false),
5013203Sgabeblack@google.com    runOnce(false)
5113203Sgabeblack@google.com{}
5213203Sgabeblack@google.com
5313203Sgabeblack@google.comScheduler::~Scheduler()
5413203Sgabeblack@google.com{
5513203Sgabeblack@google.com    // Clear out everything that belongs to us to make sure nobody tries to
5613203Sgabeblack@google.com    // clear themselves out after the scheduler goes away.
5713203Sgabeblack@google.com    clear();
5812957Sgabeblack@google.com}
5913207Sgabeblack@google.com
6013053Sgabeblack@google.comvoid
6113203Sgabeblack@google.comScheduler::clear()
6213203Sgabeblack@google.com{
6313203Sgabeblack@google.com    // Delta notifications.
6413203Sgabeblack@google.com    while (!deltas.empty())
6513203Sgabeblack@google.com        deltas.front()->deschedule();
6613203Sgabeblack@google.com
6713203Sgabeblack@google.com    // Timed notifications.
6813203Sgabeblack@google.com    for (auto &tsp: timeSlots) {
6913203Sgabeblack@google.com        TimeSlot *&ts = tsp.second;
7013053Sgabeblack@google.com        while (!ts->events.empty())
7113203Sgabeblack@google.com            ts->events.front()->deschedule();
7213203Sgabeblack@google.com        deschedule(ts);
7313203Sgabeblack@google.com    }
7413203Sgabeblack@google.com    timeSlots.clear();
7513203Sgabeblack@google.com
7613203Sgabeblack@google.com    // gem5 events.
7713053Sgabeblack@google.com    if (readyEvent.scheduled())
7812957Sgabeblack@google.com        deschedule(&readyEvent);
7913207Sgabeblack@google.com    if (pauseEvent.scheduled())
8013207Sgabeblack@google.com        deschedule(&pauseEvent);
8113207Sgabeblack@google.com    if (stopEvent.scheduled())
8213207Sgabeblack@google.com        deschedule(&stopEvent);
8313207Sgabeblack@google.com    if (starvationEvent.scheduled())
8412837Sgabeblack@google.com        deschedule(&starvationEvent);
8512842Sgabeblack@google.com    if (maxTickEvent.scheduled())
8612837Sgabeblack@google.com        deschedule(&maxTickEvent);
8712837Sgabeblack@google.com
8812837Sgabeblack@google.com    Process *p;
8912837Sgabeblack@google.com    while ((p = toFinalize.getNext()))
9013207Sgabeblack@google.com        p->popListNode();
9113207Sgabeblack@google.com    while ((p = initList.getNext()))
9212957Sgabeblack@google.com        p->popListNode();
9313207Sgabeblack@google.com    while ((p = readyList.getNext()))
9413207Sgabeblack@google.com        p->popListNode();
9512938Sgabeblack@google.com
9612837Sgabeblack@google.com    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