scheduler.cc revision 13203:76ee4971fd9e
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), readyList(nullptr) 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->finalize(); 108 p->popListNode(); 109 110 if (p->dontInitialize()) { 111 if (!p->hasStaticSensitivities() && !p->internal()) { 112 SC_REPORT_WARNING( 113 "(W558) disable() or dont_initialize() called on " 114 "process with no static sensitivity, it will be " 115 "orphaned", p->name()); 116 } 117 } else { 118 p->ready(); 119 } 120 } 121 122 runUpdate(); 123 runDelta(); 124 125 for (auto ets: eventsToSchedule) 126 eq->schedule(ets.first, ets.second); 127 eventsToSchedule.clear(); 128 129 if (_started) { 130 if (!runToTime && starved()) 131 scheduleStarvationEvent(); 132 kernel->status(::sc_core::SC_RUNNING); 133 } 134 135 initDone = true; 136 137 status(StatusOther); 138} 139 140void 141Scheduler::reg(Process *p) 142{ 143 if (initDone) { 144 // If we're past initialization, finalize static sensitivity. 145 p->finalize(); 146 // If not marked as dontInitialize, mark as ready. 147 if (!p->dontInitialize()) 148 p->ready(); 149 } else { 150 // Otherwise, record that this process should be initialized once we 151 // get there. 152 initList.pushLast(p); 153 } 154} 155 156void 157Scheduler::yield() 158{ 159 // Pull a process from the active list. 160 _current = readyList->getNext(); 161 if (!_current) { 162 // There are no more processes, so return control to evaluate. 163 Fiber::primaryFiber()->run(); 164 } else { 165 _current->popListNode(); 166 // Switch to whatever Fiber is supposed to run this process. All 167 // Fibers which aren't running should be parked at this line. 168 _current->fiber()->run(); 169 // If the current process needs to be manually started, start it. 170 if (_current && _current->needsStart()) { 171 _current->needsStart(false); 172 try { 173 _current->run(); 174 } catch (...) { 175 throwToScMain(); 176 } 177 } 178 } 179 if (_current && _current->excWrapper) { 180 // Make sure this isn't a method process. 181 assert(!_current->needsStart()); 182 auto ew = _current->excWrapper; 183 _current->excWrapper = nullptr; 184 ew->throw_it(); 185 } 186} 187 188void 189Scheduler::ready(Process *p) 190{ 191 if (_stopNow) 192 return; 193 194 if (p->procKind() == ::sc_core::SC_METHOD_PROC_) 195 readyListMethods.pushLast(p); 196 else 197 readyListThreads.pushLast(p); 198 199 scheduleReadyEvent(); 200} 201 202void 203Scheduler::resume(Process *p) 204{ 205 if (initDone) 206 ready(p); 207 else 208 initList.pushLast(p); 209} 210 211bool 212listContains(ListNode *list, ListNode *target) 213{ 214 ListNode *n = list->nextListNode; 215 while (n != list) 216 if (n == target) 217 return true; 218 return false; 219} 220 221bool 222Scheduler::suspend(Process *p) 223{ 224 bool was_ready; 225 if (initDone) { 226 // After initialization, check if we're on a ready list. 227 was_ready = (p->nextListNode != nullptr); 228 p->popListNode(); 229 } else { 230 // Nothing is ready before init. 231 was_ready = false; 232 } 233 return was_ready; 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 = readyListMethods.empty() && readyListThreads.empty(); 268 lastReadyTick = getCurTick(); 269 270 // The evaluation phase. 271 do { 272 // We run methods and threads in two seperate passes to emulate how 273 // Accellera orders things, but without having to scan through a 274 // unified list to find the next process of the correct type. 275 readyList = &readyListMethods; 276 while (!readyListMethods.empty()) 277 yield(); 278 279 readyList = &readyListThreads; 280 while (!readyListThreads.empty()) 281 yield(); 282 283 // We already know that readyListThreads is empty at this point. 284 } while (!readyListMethods.empty()); 285 286 if (!empty) { 287 _numCycles++; 288 _changeStamp++; 289 } 290 291 if (_stopNow) 292 return; 293 294 runUpdate(); 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 381 // Return to gem5 to let it run events, etc. 382 Fiber::primaryFiber()->run(); 383 384 if (pauseEvent.scheduled()) 385 deschedule(&pauseEvent); 386 if (stopEvent.scheduled()) 387 deschedule(&stopEvent); 388 if (maxTickEvent.scheduled()) 389 deschedule(&maxTickEvent); 390 if (starvationEvent.scheduled()) 391 deschedule(&starvationEvent); 392 393 if (_throwToScMain) { 394 const ::sc_core::sc_report *to_throw = _throwToScMain; 395 _throwToScMain = nullptr; 396 throw *to_throw; 397 } 398} 399 400void 401Scheduler::oneCycle() 402{ 403 runOnce = true; 404 scheduleReadyEvent(); 405 start(::MaxTick, false); 406} 407 408void 409Scheduler::schedulePause() 410{ 411 if (pauseEvent.scheduled()) 412 return; 413 414 schedule(&pauseEvent); 415} 416 417void 418Scheduler::throwToScMain(const ::sc_core::sc_report *r) 419{ 420 if (!r) 421 r = reportifyException(); 422 _throwToScMain = r; 423 status(StatusOther); 424 scMain->run(); 425} 426 427void 428Scheduler::scheduleStop(bool finish_delta) 429{ 430 if (stopEvent.scheduled()) 431 return; 432 433 if (!finish_delta) { 434 _stopNow = true; 435 // If we're not supposed to finish the delta cycle, flush all 436 // pending activity. 437 clear(); 438 } 439 schedule(&stopEvent); 440} 441 442Scheduler scheduler; 443 444namespace { 445 446void 447throwingReportHandler(const ::sc_core::sc_report &r, 448 const ::sc_core::sc_actions &) 449{ 450 throw r; 451} 452 453} // anonymous namespace 454 455const ::sc_core::sc_report * 456reportifyException() 457{ 458 ::sc_core::sc_report_handler_proc old_handler = 459 ::sc_core::sc_report_handler::get_handler(); 460 ::sc_core::sc_report_handler::set_handler(&throwingReportHandler); 461 462 try { 463 try { 464 // Rethrow the current exception so we can catch it and throw an 465 // sc_report instead if it's not a type we recognize/can handle. 466 throw; 467 } catch (const ::sc_core::sc_report &) { 468 // It's already a sc_report, so nothing to do. 469 throw; 470 } catch (const ::sc_core::sc_unwind_exception &) { 471 panic("Kill/reset exception escaped a Process::run()"); 472 } catch (const std::exception &e) { 473 SC_REPORT_ERROR("uncaught exception", e.what()); 474 } catch (const char *msg) { 475 SC_REPORT_ERROR("uncaught exception", msg); 476 } catch (...) { 477 SC_REPORT_ERROR("uncaught exception", "UNKNOWN EXCEPTION"); 478 } 479 } catch (const ::sc_core::sc_report &r) { 480 ::sc_core::sc_report_handler::set_handler(old_handler); 481 return &r; 482 } 483 panic("No exception thrown in reportifyException."); 484} 485 486} // namespace sc_gem5 487