scheduler.cc revision 13244:deedec45898f
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#include "systemc/ext/utils/sc_report.hh" 38#include "systemc/ext/utils/sc_report_handler.hh" 39 40namespace sc_gem5 41{ 42 43Scheduler::Scheduler() : 44 eq(nullptr), readyEvent(this, false, ReadyPriority), 45 pauseEvent(this, false, PausePriority), 46 stopEvent(this, false, StopPriority), 47 scMain(nullptr), _throwToScMain(nullptr), 48 starvationEvent(this, false, StarvationPriority), 49 _elaborationDone(false), _started(false), _stopNow(false), 50 _status(StatusOther), maxTickEvent(this, false, MaxTickPriority), 51 _numCycles(0), _changeStamp(0), _current(nullptr), initDone(false), 52 runOnce(false) 53{} 54 55Scheduler::~Scheduler() 56{ 57 // Clear out everything that belongs to us to make sure nobody tries to 58 // clear themselves out after the scheduler goes away. 59 clear(); 60} 61 62void 63Scheduler::clear() 64{ 65 // Delta notifications. 66 while (!deltas.empty()) 67 deltas.front()->deschedule(); 68 69 // Timed notifications. 70 for (auto &tsp: timeSlots) { 71 TimeSlot *&ts = tsp.second; 72 while (!ts->events.empty()) 73 ts->events.front()->deschedule(); 74 deschedule(ts); 75 } 76 timeSlots.clear(); 77 78 // gem5 events. 79 if (readyEvent.scheduled()) 80 deschedule(&readyEvent); 81 if (pauseEvent.scheduled()) 82 deschedule(&pauseEvent); 83 if (stopEvent.scheduled()) 84 deschedule(&stopEvent); 85 if (starvationEvent.scheduled()) 86 deschedule(&starvationEvent); 87 if (maxTickEvent.scheduled()) 88 deschedule(&maxTickEvent); 89 90 Process *p; 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 = initList.getNext(); p; p = initList.getNext()) { 107 p->popListNode(); 108 109 if (p->dontInitialize()) { 110 if (!p->hasStaticSensitivities() && !p->internal()) { 111 SC_REPORT_WARNING( 112 "(W558) disable() or dont_initialize() called on " 113 "process with no static sensitivity, it will be " 114 "orphaned", p->name()); 115 } 116 } else { 117 p->ready(); 118 } 119 } 120 121 runUpdate(); 122 runDelta(); 123 124 for (auto ets: eventsToSchedule) 125 eq->schedule(ets.first, ets.second); 126 eventsToSchedule.clear(); 127 128 if (_started) { 129 if (!runToTime && starved()) 130 scheduleStarvationEvent(); 131 kernel->status(::sc_core::SC_RUNNING); 132 } 133 134 initDone = true; 135 136 status(StatusOther); 137} 138 139void 140Scheduler::reg(Process *p) 141{ 142 if (initDone) { 143 // If not marked as dontInitialize, mark as ready. 144 if (!p->dontInitialize()) 145 p->ready(); 146 } else { 147 // Otherwise, record that this process should be initialized once we 148 // get there. 149 initList.pushLast(p); 150 } 151} 152 153void 154Scheduler::yield() 155{ 156 // Pull a process from the active list. 157 _current = getNextReady(); 158 if (!_current) { 159 // There are no more processes, so return control to evaluate. 160 Fiber::primaryFiber()->run(); 161 } else { 162 _current->popListNode(); 163 // Switch to whatever Fiber is supposed to run this process. All 164 // Fibers which aren't running should be parked at this line. 165 _current->fiber()->run(); 166 // If the current process needs to be manually started, start it. 167 if (_current && _current->needsStart()) { 168 _current->needsStart(false); 169 try { 170 _current->run(); 171 } catch (...) { 172 throwToScMain(); 173 } 174 } 175 } 176 if (_current && _current->excWrapper) { 177 // Make sure this isn't a method process. 178 assert(!_current->needsStart()); 179 auto ew = _current->excWrapper; 180 _current->excWrapper = nullptr; 181 ew->throw_it(); 182 } 183} 184 185void 186Scheduler::ready(Process *p) 187{ 188 if (_stopNow) 189 return; 190 191 if (p->procKind() == ::sc_core::SC_METHOD_PROC_) 192 readyListMethods.pushLast(p); 193 else 194 readyListThreads.pushLast(p); 195 196 if (!inEvaluate()) 197 scheduleReadyEvent(); 198} 199 200void 201Scheduler::resume(Process *p) 202{ 203 if (initDone) 204 ready(p); 205 else 206 initList.pushLast(p); 207} 208 209bool 210listContains(ListNode *list, ListNode *target) 211{ 212 ListNode *n = list->nextListNode; 213 while (n != list) 214 if (n == target) 215 return true; 216 return false; 217} 218 219bool 220Scheduler::suspend(Process *p) 221{ 222 bool was_ready; 223 if (initDone) { 224 // After initialization, check if we're on a ready list. 225 was_ready = (p->nextListNode != nullptr); 226 p->popListNode(); 227 } else { 228 // Nothing is ready before init. 229 was_ready = false; 230 } 231 return was_ready; 232} 233 234void 235Scheduler::requestUpdate(Channel *c) 236{ 237 updateList.pushLast(c); 238 if (!inEvaluate()) 239 scheduleReadyEvent(); 240} 241 242void 243Scheduler::scheduleReadyEvent() 244{ 245 // Schedule the evaluate and update phases. 246 if (!readyEvent.scheduled()) { 247 schedule(&readyEvent); 248 if (starvationEvent.scheduled()) 249 deschedule(&starvationEvent); 250 } 251} 252 253void 254Scheduler::scheduleStarvationEvent() 255{ 256 if (!starvationEvent.scheduled()) { 257 schedule(&starvationEvent); 258 if (readyEvent.scheduled()) 259 deschedule(&readyEvent); 260 } 261} 262 263void 264Scheduler::runReady() 265{ 266 bool empty = readyListMethods.empty() && readyListThreads.empty(); 267 lastReadyTick = getCurTick(); 268 269 // The evaluation phase. 270 do { 271 yield(); 272 } while (getNextReady()); 273 274 if (!empty) { 275 _numCycles++; 276 _changeStamp++; 277 } 278 279 if (_stopNow) { 280 status(StatusOther); 281 return; 282 } 283 284 runUpdate(); 285 runDelta(); 286 287 if (!runToTime && starved()) 288 scheduleStarvationEvent(); 289 290 if (runOnce) 291 schedulePause(); 292 293 status(StatusOther); 294} 295 296void 297Scheduler::runUpdate() 298{ 299 status(StatusUpdate); 300 301 try { 302 Channel *channel = updateList.getNext(); 303 while (channel) { 304 channel->popListNode(); 305 channel->update(); 306 channel = updateList.getNext(); 307 } 308 } catch (...) { 309 throwToScMain(); 310 } 311} 312 313void 314Scheduler::runDelta() 315{ 316 status(StatusDelta); 317 318 try { 319 while (!deltas.empty()) 320 deltas.front()->run(); 321 } catch (...) { 322 throwToScMain(); 323 } 324} 325 326void 327Scheduler::pause() 328{ 329 status(StatusPaused); 330 kernel->status(::sc_core::SC_PAUSED); 331 runOnce = false; 332 if (scMain && !scMain->finished()) 333 scMain->run(); 334} 335 336void 337Scheduler::stop() 338{ 339 status(StatusStopped); 340 kernel->stop(); 341 342 clear(); 343 344 runOnce = false; 345 if (scMain && !scMain->finished()) 346 scMain->run(); 347} 348 349void 350Scheduler::start(Tick max_tick, bool run_to_time) 351{ 352 // We should be running from sc_main. Keep track of that Fiber to return 353 // to later. 354 scMain = Fiber::currentFiber(); 355 356 _started = true; 357 status(StatusOther); 358 runToTime = run_to_time; 359 360 maxTick = max_tick; 361 lastReadyTick = getCurTick(); 362 363 if (initDone) { 364 if (!runToTime && starved()) 365 scheduleStarvationEvent(); 366 kernel->status(::sc_core::SC_RUNNING); 367 } 368 369 schedule(&maxTickEvent, maxTick); 370 371 // Return to gem5 to let it run events, etc. 372 Fiber::primaryFiber()->run(); 373 374 if (pauseEvent.scheduled()) 375 deschedule(&pauseEvent); 376 if (stopEvent.scheduled()) 377 deschedule(&stopEvent); 378 if (maxTickEvent.scheduled()) 379 deschedule(&maxTickEvent); 380 if (starvationEvent.scheduled()) 381 deschedule(&starvationEvent); 382 383 if (_throwToScMain) { 384 const ::sc_core::sc_report *to_throw = _throwToScMain; 385 _throwToScMain = nullptr; 386 throw *to_throw; 387 } 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::throwToScMain(const ::sc_core::sc_report *r) 409{ 410 if (!r) 411 r = reportifyException(); 412 _throwToScMain = r; 413 status(StatusOther); 414 scMain->run(); 415} 416 417void 418Scheduler::scheduleStop(bool finish_delta) 419{ 420 if (stopEvent.scheduled()) 421 return; 422 423 if (!finish_delta) { 424 _stopNow = true; 425 // If we're not supposed to finish the delta cycle, flush all 426 // pending activity. 427 clear(); 428 } 429 schedule(&stopEvent); 430} 431 432Scheduler scheduler; 433 434namespace { 435 436void 437throwingReportHandler(const ::sc_core::sc_report &r, 438 const ::sc_core::sc_actions &) 439{ 440 throw r; 441} 442 443} // anonymous namespace 444 445const ::sc_core::sc_report * 446reportifyException() 447{ 448 ::sc_core::sc_report_handler_proc old_handler = 449 ::sc_core::sc_report_handler::get_handler(); 450 ::sc_core::sc_report_handler::set_handler(&throwingReportHandler); 451 452 try { 453 try { 454 // Rethrow the current exception so we can catch it and throw an 455 // sc_report instead if it's not a type we recognize/can handle. 456 throw; 457 } catch (const ::sc_core::sc_report &) { 458 // It's already a sc_report, so nothing to do. 459 throw; 460 } catch (const ::sc_core::sc_unwind_exception &) { 461 panic("Kill/reset exception escaped a Process::run()"); 462 } catch (const std::exception &e) { 463 SC_REPORT_ERROR("uncaught exception", e.what()); 464 } catch (const char *msg) { 465 SC_REPORT_ERROR("uncaught exception", msg); 466 } catch (...) { 467 SC_REPORT_ERROR("uncaught exception", "UNKNOWN EXCEPTION"); 468 } 469 } catch (const ::sc_core::sc_report &r) { 470 ::sc_core::sc_report_handler::set_handler(old_handler); 471 return &r; 472 } 473 panic("No exception thrown in reportifyException."); 474} 475 476} // namespace sc_gem5 477