scheduler.cc revision 13209:aad30faa966b
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 scheduleReadyEvent(); 197} 198 199void 200Scheduler::resume(Process *p) 201{ 202 if (initDone) 203 ready(p); 204 else 205 initList.pushLast(p); 206} 207 208bool 209listContains(ListNode *list, ListNode *target) 210{ 211 ListNode *n = list->nextListNode; 212 while (n != list) 213 if (n == target) 214 return true; 215 return false; 216} 217 218bool 219Scheduler::suspend(Process *p) 220{ 221 bool was_ready; 222 if (initDone) { 223 // After initialization, check if we're on a ready list. 224 was_ready = (p->nextListNode != nullptr); 225 p->popListNode(); 226 } else { 227 // Nothing is ready before init. 228 was_ready = false; 229 } 230 return was_ready; 231} 232 233void 234Scheduler::requestUpdate(Channel *c) 235{ 236 updateList.pushLast(c); 237 scheduleReadyEvent(); 238} 239 240void 241Scheduler::scheduleReadyEvent() 242{ 243 // Schedule the evaluate and update phases. 244 if (!readyEvent.scheduled()) { 245 schedule(&readyEvent); 246 if (starvationEvent.scheduled()) 247 deschedule(&starvationEvent); 248 } 249} 250 251void 252Scheduler::scheduleStarvationEvent() 253{ 254 if (!starvationEvent.scheduled()) { 255 schedule(&starvationEvent); 256 if (readyEvent.scheduled()) 257 deschedule(&readyEvent); 258 } 259} 260 261void 262Scheduler::runReady() 263{ 264 bool empty = readyListMethods.empty() && readyListThreads.empty(); 265 lastReadyTick = getCurTick(); 266 267 // The evaluation phase. 268 do { 269 yield(); 270 } while (getNextReady()); 271 272 if (!empty) { 273 _numCycles++; 274 _changeStamp++; 275 } 276 277 if (_stopNow) 278 return; 279 280 runUpdate(); 281 runDelta(); 282 283 if (!runToTime && starved()) 284 scheduleStarvationEvent(); 285 286 if (runOnce) 287 schedulePause(); 288 289 status(StatusOther); 290} 291 292void 293Scheduler::runUpdate() 294{ 295 status(StatusUpdate); 296 297 try { 298 Channel *channel = updateList.getNext(); 299 while (channel) { 300 channel->popListNode(); 301 channel->update(); 302 channel = updateList.getNext(); 303 } 304 } catch (...) { 305 throwToScMain(); 306 } 307} 308 309void 310Scheduler::runDelta() 311{ 312 status(StatusDelta); 313 314 try { 315 while (!deltas.empty()) 316 deltas.front()->run(); 317 } catch (...) { 318 throwToScMain(); 319 } 320} 321 322void 323Scheduler::pause() 324{ 325 status(StatusPaused); 326 kernel->status(::sc_core::SC_PAUSED); 327 runOnce = false; 328 if (scMain && !scMain->finished()) 329 scMain->run(); 330} 331 332void 333Scheduler::stop() 334{ 335 status(StatusStopped); 336 kernel->stop(); 337 338 clear(); 339 340 runOnce = false; 341 if (scMain && !scMain->finished()) 342 scMain->run(); 343} 344 345void 346Scheduler::start(Tick max_tick, bool run_to_time) 347{ 348 // We should be running from sc_main. Keep track of that Fiber to return 349 // to later. 350 scMain = Fiber::currentFiber(); 351 352 _started = true; 353 status(StatusOther); 354 runToTime = run_to_time; 355 356 maxTick = max_tick; 357 lastReadyTick = getCurTick(); 358 359 if (initDone) { 360 if (!runToTime && starved()) 361 scheduleStarvationEvent(); 362 kernel->status(::sc_core::SC_RUNNING); 363 } 364 365 schedule(&maxTickEvent, maxTick); 366 367 // Return to gem5 to let it run events, etc. 368 Fiber::primaryFiber()->run(); 369 370 if (pauseEvent.scheduled()) 371 deschedule(&pauseEvent); 372 if (stopEvent.scheduled()) 373 deschedule(&stopEvent); 374 if (maxTickEvent.scheduled()) 375 deschedule(&maxTickEvent); 376 if (starvationEvent.scheduled()) 377 deschedule(&starvationEvent); 378 379 if (_throwToScMain) { 380 const ::sc_core::sc_report *to_throw = _throwToScMain; 381 _throwToScMain = nullptr; 382 throw *to_throw; 383 } 384} 385 386void 387Scheduler::oneCycle() 388{ 389 runOnce = true; 390 scheduleReadyEvent(); 391 start(::MaxTick, false); 392} 393 394void 395Scheduler::schedulePause() 396{ 397 if (pauseEvent.scheduled()) 398 return; 399 400 schedule(&pauseEvent); 401} 402 403void 404Scheduler::throwToScMain(const ::sc_core::sc_report *r) 405{ 406 if (!r) 407 r = reportifyException(); 408 _throwToScMain = r; 409 status(StatusOther); 410 scMain->run(); 411} 412 413void 414Scheduler::scheduleStop(bool finish_delta) 415{ 416 if (stopEvent.scheduled()) 417 return; 418 419 if (!finish_delta) { 420 _stopNow = true; 421 // If we're not supposed to finish the delta cycle, flush all 422 // pending activity. 423 clear(); 424 } 425 schedule(&stopEvent); 426} 427 428Scheduler scheduler; 429 430namespace { 431 432void 433throwingReportHandler(const ::sc_core::sc_report &r, 434 const ::sc_core::sc_actions &) 435{ 436 throw r; 437} 438 439} // anonymous namespace 440 441const ::sc_core::sc_report * 442reportifyException() 443{ 444 ::sc_core::sc_report_handler_proc old_handler = 445 ::sc_core::sc_report_handler::get_handler(); 446 ::sc_core::sc_report_handler::set_handler(&throwingReportHandler); 447 448 try { 449 try { 450 // Rethrow the current exception so we can catch it and throw an 451 // sc_report instead if it's not a type we recognize/can handle. 452 throw; 453 } catch (const ::sc_core::sc_report &) { 454 // It's already a sc_report, so nothing to do. 455 throw; 456 } catch (const ::sc_core::sc_unwind_exception &) { 457 panic("Kill/reset exception escaped a Process::run()"); 458 } catch (const std::exception &e) { 459 SC_REPORT_ERROR("uncaught exception", e.what()); 460 } catch (const char *msg) { 461 SC_REPORT_ERROR("uncaught exception", msg); 462 } catch (...) { 463 SC_REPORT_ERROR("uncaught exception", "UNKNOWN EXCEPTION"); 464 } 465 } catch (const ::sc_core::sc_report &r) { 466 ::sc_core::sc_report_handler::set_handler(old_handler); 467 return &r; 468 } 469 panic("No exception thrown in reportifyException."); 470} 471 472} // namespace sc_gem5 473