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