scheduler.cc revision 13207:034ca389a810
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->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 = readyList->getNext(); 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 // We run methods and threads in two seperate passes to emulate how 270 // Accellera orders things, but without having to scan through a 271 // unified list to find the next process of the correct type. 272 readyList = &readyListMethods; 273 while (!readyListMethods.empty()) 274 yield(); 275 276 readyList = &readyListThreads; 277 while (!readyListThreads.empty()) 278 yield(); 279 280 // We already know that readyListThreads is empty at this point. 281 } while (!readyListMethods.empty()); 282 283 if (!empty) { 284 _numCycles++; 285 _changeStamp++; 286 } 287 288 if (_stopNow) 289 return; 290 291 runUpdate(); 292 runDelta(); 293 294 if (!runToTime && starved()) 295 scheduleStarvationEvent(); 296 297 if (runOnce) 298 schedulePause(); 299 300 status(StatusOther); 301} 302 303void 304Scheduler::runUpdate() 305{ 306 status(StatusUpdate); 307 308 try { 309 Channel *channel = updateList.getNext(); 310 while (channel) { 311 channel->popListNode(); 312 channel->update(); 313 channel = updateList.getNext(); 314 } 315 } catch (...) { 316 throwToScMain(); 317 } 318} 319 320void 321Scheduler::runDelta() 322{ 323 status(StatusDelta); 324 325 try { 326 while (!deltas.empty()) 327 deltas.front()->run(); 328 } catch (...) { 329 throwToScMain(); 330 } 331} 332 333void 334Scheduler::pause() 335{ 336 status(StatusPaused); 337 kernel->status(::sc_core::SC_PAUSED); 338 runOnce = false; 339 if (scMain && !scMain->finished()) 340 scMain->run(); 341} 342 343void 344Scheduler::stop() 345{ 346 status(StatusStopped); 347 kernel->stop(); 348 349 clear(); 350 351 runOnce = false; 352 if (scMain && !scMain->finished()) 353 scMain->run(); 354} 355 356void 357Scheduler::start(Tick max_tick, bool run_to_time) 358{ 359 // We should be running from sc_main. Keep track of that Fiber to return 360 // to later. 361 scMain = Fiber::currentFiber(); 362 363 _started = true; 364 status(StatusOther); 365 runToTime = run_to_time; 366 367 maxTick = max_tick; 368 lastReadyTick = getCurTick(); 369 370 if (initDone) { 371 if (!runToTime && starved()) 372 scheduleStarvationEvent(); 373 kernel->status(::sc_core::SC_RUNNING); 374 } 375 376 schedule(&maxTickEvent, maxTick); 377 378 // Return to gem5 to let it run events, etc. 379 Fiber::primaryFiber()->run(); 380 381 if (pauseEvent.scheduled()) 382 deschedule(&pauseEvent); 383 if (stopEvent.scheduled()) 384 deschedule(&stopEvent); 385 if (maxTickEvent.scheduled()) 386 deschedule(&maxTickEvent); 387 if (starvationEvent.scheduled()) 388 deschedule(&starvationEvent); 389 390 if (_throwToScMain) { 391 const ::sc_core::sc_report *to_throw = _throwToScMain; 392 _throwToScMain = nullptr; 393 throw *to_throw; 394 } 395} 396 397void 398Scheduler::oneCycle() 399{ 400 runOnce = true; 401 scheduleReadyEvent(); 402 start(::MaxTick, false); 403} 404 405void 406Scheduler::schedulePause() 407{ 408 if (pauseEvent.scheduled()) 409 return; 410 411 schedule(&pauseEvent); 412} 413 414void 415Scheduler::throwToScMain(const ::sc_core::sc_report *r) 416{ 417 if (!r) 418 r = reportifyException(); 419 _throwToScMain = r; 420 status(StatusOther); 421 scMain->run(); 422} 423 424void 425Scheduler::scheduleStop(bool finish_delta) 426{ 427 if (stopEvent.scheduled()) 428 return; 429 430 if (!finish_delta) { 431 _stopNow = true; 432 // If we're not supposed to finish the delta cycle, flush all 433 // pending activity. 434 clear(); 435 } 436 schedule(&stopEvent); 437} 438 439Scheduler scheduler; 440 441namespace { 442 443void 444throwingReportHandler(const ::sc_core::sc_report &r, 445 const ::sc_core::sc_actions &) 446{ 447 throw r; 448} 449 450} // anonymous namespace 451 452const ::sc_core::sc_report * 453reportifyException() 454{ 455 ::sc_core::sc_report_handler_proc old_handler = 456 ::sc_core::sc_report_handler::get_handler(); 457 ::sc_core::sc_report_handler::set_handler(&throwingReportHandler); 458 459 try { 460 try { 461 // Rethrow the current exception so we can catch it and throw an 462 // sc_report instead if it's not a type we recognize/can handle. 463 throw; 464 } catch (const ::sc_core::sc_report &) { 465 // It's already a sc_report, so nothing to do. 466 throw; 467 } catch (const ::sc_core::sc_unwind_exception &) { 468 panic("Kill/reset exception escaped a Process::run()"); 469 } catch (const std::exception &e) { 470 SC_REPORT_ERROR("uncaught exception", e.what()); 471 } catch (const char *msg) { 472 SC_REPORT_ERROR("uncaught exception", msg); 473 } catch (...) { 474 SC_REPORT_ERROR("uncaught exception", "UNKNOWN EXCEPTION"); 475 } 476 } catch (const ::sc_core::sc_report &r) { 477 ::sc_core::sc_report_handler::set_handler(old_handler); 478 return &r; 479 } 480 panic("No exception thrown in reportifyException."); 481} 482 483} // namespace sc_gem5 484