scheduler.cc revision 13180:79e680f62779
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), readyList(nullptr) 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 = readyListMethods.getNext())) 94 p->popListNode(); 95 while ((p = readyListThreads.getNext())) 96 p->popListNode(); 97 98 Channel *c; 99 while ((c = updateList.getNext())) 100 c->popListNode(); 101} 102 103void 104Scheduler::initPhase() 105{ 106 for (Process *p = toFinalize.getNext(); p; p = toFinalize.getNext()) { 107 p->finalize(); 108 p->popListNode(); 109 110 if (!p->hasStaticSensitivities() && !p->internal()) { 111 SC_REPORT_WARNING( 112 "(W558) disable() or dont_initialize() called on process " 113 "with no static sensitivity, it will be orphaned", 114 p->name()); 115 } 116 } 117 118 for (Process *p = initList.getNext(); p; p = initList.getNext()) { 119 p->finalize(); 120 p->popListNode(); 121 p->ready(); 122 } 123 124 update(); 125 126 while (!deltas.empty()) 127 deltas.front()->run(); 128 129 for (auto ets: eventsToSchedule) 130 eq->schedule(ets.first, ets.second); 131 eventsToSchedule.clear(); 132 133 if (_started) { 134 if (!runToTime && starved()) 135 scheduleStarvationEvent(); 136 kernel->status(::sc_core::SC_RUNNING); 137 } 138 139 initDone = true; 140} 141 142void 143Scheduler::reg(Process *p) 144{ 145 if (initDone) { 146 // If we're past initialization, finalize static sensitivity. 147 p->finalize(); 148 // Mark the process as ready. 149 p->ready(); 150 } else { 151 // Otherwise, record that this process should be initialized once we 152 // get there. 153 initList.pushLast(p); 154 } 155} 156 157void 158Scheduler::dontInitialize(Process *p) 159{ 160 if (initDone) { 161 // Pop this process off of the ready list. 162 p->popListNode(); 163 } else { 164 // Push this process onto the list of processes which still need 165 // their static sensitivity to be finalized. That implicitly pops it 166 // off the list of processes to be initialized/marked ready. 167 toFinalize.pushLast(p); 168 } 169} 170 171void 172Scheduler::yield() 173{ 174 // Pull a process from the active list. 175 _current = readyList->getNext(); 176 if (!_current) { 177 // There are no more processes, so return control to evaluate. 178 Fiber::primaryFiber()->run(); 179 } else { 180 _current->popListNode(); 181 // Switch to whatever Fiber is supposed to run this process. All 182 // Fibers which aren't running should be parked at this line. 183 _current->fiber()->run(); 184 // If the current process needs to be manually started, start it. 185 if (_current && _current->needsStart()) { 186 _current->needsStart(false); 187 _current->run(); 188 } 189 } 190 if (_current && _current->excWrapper) { 191 // Make sure this isn't a method process. 192 assert(!_current->needsStart()); 193 auto ew = _current->excWrapper; 194 _current->excWrapper = nullptr; 195 ew->throw_it(); 196 } 197} 198 199void 200Scheduler::ready(Process *p) 201{ 202 if (_stopNow) 203 return; 204 205 if (p->procKind() == ::sc_core::SC_METHOD_PROC_) 206 readyListMethods.pushLast(p); 207 else 208 readyListThreads.pushLast(p); 209 210 scheduleReadyEvent(); 211} 212 213void 214Scheduler::resume(Process *p) 215{ 216 if (initDone) 217 ready(p); 218 else 219 initList.pushLast(p); 220} 221 222bool 223listContains(ListNode *list, ListNode *target) 224{ 225 ListNode *n = list->nextListNode; 226 while (n != list) 227 if (n == target) 228 return true; 229 return false; 230} 231 232bool 233Scheduler::suspend(Process *p) 234{ 235 bool was_ready; 236 if (initDone) { 237 // After initialization, the only list we can be on is the ready list. 238 was_ready = (p->nextListNode != nullptr); 239 p->popListNode(); 240 } else { 241 // Check the ready lists to see if we find this process. 242 was_ready = listContains(&readyListMethods, p) || 243 listContains(&readyListThreads, p); 244 if (was_ready) 245 toFinalize.pushLast(p); 246 } 247 return was_ready; 248} 249 250void 251Scheduler::requestUpdate(Channel *c) 252{ 253 updateList.pushLast(c); 254 scheduleReadyEvent(); 255} 256 257void 258Scheduler::scheduleReadyEvent() 259{ 260 // Schedule the evaluate and update phases. 261 if (!readyEvent.scheduled()) { 262 schedule(&readyEvent); 263 if (starvationEvent.scheduled()) 264 deschedule(&starvationEvent); 265 } 266} 267 268void 269Scheduler::scheduleStarvationEvent() 270{ 271 if (!starvationEvent.scheduled()) { 272 schedule(&starvationEvent); 273 if (readyEvent.scheduled()) 274 deschedule(&readyEvent); 275 } 276} 277 278void 279Scheduler::runReady() 280{ 281 bool empty = readyListMethods.empty() && readyListThreads.empty(); 282 lastReadyTick = getCurTick(); 283 284 // The evaluation phase. 285 do { 286 // We run methods and threads in two seperate passes to emulate how 287 // Accellera orders things, but without having to scan through a 288 // unified list to find the next process of the correct type. 289 readyList = &readyListMethods; 290 while (!readyListMethods.empty()) 291 yield(); 292 293 readyList = &readyListThreads; 294 while (!readyListThreads.empty()) 295 yield(); 296 297 // We already know that readyListThreads is empty at this point. 298 } while (!readyListMethods.empty()); 299 300 if (!empty) { 301 _numCycles++; 302 _changeStamp++; 303 } 304 305 if (_stopNow) 306 return; 307 308 // The update phase. 309 update(); 310 311 // The delta phase. 312 while (!deltas.empty()) 313 deltas.front()->run(); 314 315 if (!runToTime && starved()) 316 scheduleStarvationEvent(); 317 318 if (runOnce) 319 schedulePause(); 320} 321 322void 323Scheduler::update() 324{ 325 Channel *channel = updateList.getNext(); 326 while (channel) { 327 channel->popListNode(); 328 channel->update(); 329 channel = updateList.getNext(); 330 } 331} 332 333void 334Scheduler::pause() 335{ 336 _paused = true; 337 kernel->status(::sc_core::SC_PAUSED); 338 runOnce = false; 339 scMain->run(); 340} 341 342void 343Scheduler::stop() 344{ 345 _stopped = true; 346 kernel->stop(); 347 348 clear(); 349 350 runOnce = false; 351 scMain->run(); 352} 353 354void 355Scheduler::start(Tick max_tick, bool run_to_time) 356{ 357 // We should be running from sc_main. Keep track of that Fiber to return 358 // to later. 359 scMain = Fiber::currentFiber(); 360 361 _started = true; 362 _paused = false; 363 _stopped = false; 364 runToTime = run_to_time; 365 366 maxTick = max_tick; 367 lastReadyTick = getCurTick(); 368 369 if (initDone) { 370 if (!runToTime && starved()) 371 scheduleStarvationEvent(); 372 kernel->status(::sc_core::SC_RUNNING); 373 } 374 375 schedule(&maxTickEvent, maxTick); 376 377 // Return to gem5 to let it run events, etc. 378 Fiber::primaryFiber()->run(); 379 380 if (pauseEvent.scheduled()) 381 deschedule(&pauseEvent); 382 if (stopEvent.scheduled()) 383 deschedule(&stopEvent); 384 if (maxTickEvent.scheduled()) 385 deschedule(&maxTickEvent); 386 if (starvationEvent.scheduled()) 387 deschedule(&starvationEvent); 388} 389 390void 391Scheduler::oneCycle() 392{ 393 runOnce = true; 394 scheduleReadyEvent(); 395 start(::MaxTick, false); 396} 397 398void 399Scheduler::schedulePause() 400{ 401 if (pauseEvent.scheduled()) 402 return; 403 404 schedule(&pauseEvent); 405} 406 407void 408Scheduler::scheduleStop(bool finish_delta) 409{ 410 if (stopEvent.scheduled()) 411 return; 412 413 if (!finish_delta) { 414 _stopNow = true; 415 // If we're not supposed to finish the delta cycle, flush all 416 // pending activity. 417 clear(); 418 } 419 schedule(&stopEvent); 420} 421 422Scheduler scheduler; 423 424} // namespace sc_gem5 425