scheduler.hh 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#ifndef __SYSTEMC_CORE_SCHEDULER_HH__ 31#define __SYSTEMC_CORE_SCHEDULER_HH__ 32 33#include <functional> 34#include <map> 35#include <set> 36#include <vector> 37 38#include "base/logging.hh" 39#include "sim/core.hh" 40#include "sim/eventq.hh" 41#include "systemc/core/channel.hh" 42#include "systemc/core/list.hh" 43#include "systemc/core/process.hh" 44#include "systemc/core/sched_event.hh" 45 46class Fiber; 47 48namespace sc_gem5 49{ 50 51typedef NodeList<Process> ProcessList; 52typedef NodeList<Channel> ChannelList; 53 54/* 55 * The scheduler supports three different mechanisms, the initialization phase, 56 * delta cycles, and timed notifications. 57 * 58 * INITIALIZATION PHASE 59 * 60 * The initialization phase has three parts: 61 * 1. Run requested channel updates. 62 * 2. Make processes which need to initialize runnable (methods and threads 63 * which didn't have dont_initialize called on them). 64 * 3. Process delta notifications. 65 * 66 * First, the Kernel SimObject calls the update() method during its startup() 67 * callback which handles the requested channel updates. The Kernel also 68 * schedules an event to be run at time 0 with a slightly elevated priority 69 * so that it happens before any "normal" event. 70 * 71 * When that t0 event happens, it calls the schedulers prepareForInit method 72 * which performs step 2 above. That indirectly causes the scheduler's 73 * readyEvent to be scheduled with slightly lowered priority, ensuring it 74 * happens after any "normal" event. 75 * 76 * Because delta notifications are scheduled at the standard priority, all 77 * of those events will happen next, performing step 3 above. Once they finish, 78 * if the readyEvent was scheduled above, there shouldn't be any higher 79 * priority events in front of it. When it runs, it will start the first 80 * evaluate phase of the first delta cycle. 81 * 82 * DELTA CYCLE 83 * 84 * A delta cycle has three phases within it. 85 * 1. The evaluate phase where runnable processes are allowed to run. 86 * 2. The update phase where requested channel updates hapen. 87 * 3. The delta notification phase where delta notifications happen. 88 * 89 * The readyEvent runs all three steps of the delta cycle. It first goes 90 * through the list of runnable processes and executes them until the set is 91 * empty, and then immediately runs the update phase. Since these are all part 92 * of the same event, there's no chance for other events to intervene and 93 * break the required order above. 94 * 95 * During the update phase above, the spec forbids any action which would make 96 * a process runnable. That means that once the update phase finishes, the set 97 * of runnable processes will be empty. There may, however, have been some 98 * delta notifications/timeouts which will have been scheduled during either 99 * the evaluate or update phase above. Those will have been accumulated in the 100 * scheduler, and are now all executed. 101 * 102 * If any processes became runnable during the delta notification phase, the 103 * readyEvent will have been scheduled and will be waiting and ready to run 104 * again, effectively starting the next delta cycle. 105 * 106 * TIMED NOTIFICATION PHASE 107 * 108 * If no processes became runnable, the event queue will continue to process 109 * events until it comes across an event which represents all the timed 110 * notifications which are supposed to happen at a particular time. The object 111 * which tracks them will execute all those notifications, and then destroy 112 * itself. If the readyEvent is now ready to run, the next delta cycle will 113 * start. 114 * 115 * PAUSE/STOP 116 * 117 * To inject a pause from sc_pause which should happen after the current delta 118 * cycle's delta notification phase, an event is scheduled with a lower than 119 * normal priority, but higher than the readyEvent. That ensures that any 120 * delta notifications which are scheduled with normal priority will happen 121 * first, since those are part of the current delta cycle. Then the pause 122 * event will happen before the next readyEvent which would start the next 123 * delta cycle. All of these events are scheduled for the current time, and so 124 * would happen before any timed notifications went off. 125 * 126 * To inject a stop from sc_stop, the delta cycles should stop before even the 127 * delta notifications have happened, but after the evaluate and update phases. 128 * For that, a stop event with slightly higher than normal priority will be 129 * scheduled so that it happens before any of the delta notification events 130 * which are at normal priority. 131 * 132 * MAX RUN TIME 133 * 134 * When sc_start is called, it's possible to pass in a maximum time the 135 * simulation should run to, at which point sc_pause is implicitly called. The 136 * simulation is supposed to run up to the latest timed notification phase 137 * which is less than or equal to the maximum time. In other words it should 138 * run timed notifications at the maximum time, but not the subsequent evaluate 139 * phase. That's implemented by scheduling an event at the max time with a 140 * priority which is lower than all the others except the ready event. Timed 141 * notifications will happen before it fires, but it will override any ready 142 * event and prevent the evaluate phase from starting. 143 */ 144 145class Scheduler 146{ 147 public: 148 typedef std::list<ScEvent *> ScEvents; 149 150 class TimeSlot : public ::Event 151 { 152 public: 153 TimeSlot() : ::Event(Default_Pri, AutoDelete) {} 154 155 ScEvents events; 156 void process(); 157 }; 158 159 typedef std::map<Tick, TimeSlot *> TimeSlots; 160 161 Scheduler(); 162 ~Scheduler(); 163 164 void clear(); 165 166 const std::string name() const { return "systemc_scheduler"; } 167 168 uint64_t numCycles() { return _numCycles; } 169 Process *current() { return _current; } 170 171 void initPhase(); 172 173 // Register a process with the scheduler. 174 void reg(Process *p); 175 176 // Run the next process, if there is one. 177 void yield(); 178 179 // Put a process on the ready list. 180 void ready(Process *p); 181 182 // Mark a process as ready if init is finished, or put it on the list of 183 // processes to be initialized. 184 void resume(Process *p); 185 186 // Remove a process from the ready/init list if it was on one of them, and 187 // return if it was. 188 bool suspend(Process *p); 189 190 // Schedule an update for a given channel. 191 void requestUpdate(Channel *c); 192 193 // Run the given process immediately, preempting whatever may be running. 194 void 195 runNow(Process *p) 196 { 197 // This function may put a process on the wrong list, ie a thread 198 // the method list. That's fine since that's just a performance 199 // optimization, and the important thing here is how the processes are 200 // ordered. 201 202 // If a process is running, schedule it/us to run again. 203 if (_current) 204 readyListMethods.pushFirst(_current); 205 // Schedule p to run first. 206 readyListMethods.pushFirst(p); 207 yield(); 208 } 209 210 // Set an event queue for scheduling events. 211 void setEventQueue(EventQueue *_eq) { eq = _eq; } 212 213 // Get the current time according to gem5. 214 Tick getCurTick() { return eq ? eq->getCurTick() : 0; } 215 216 Tick 217 delayed(const ::sc_core::sc_time &delay) 218 { 219 //XXX We're assuming the systemc time resolution is in ps. 220 return getCurTick() + delay.value() * SimClock::Int::ps; 221 } 222 223 // For scheduling delayed/timed notifications/timeouts. 224 void 225 schedule(ScEvent *event, const ::sc_core::sc_time &delay) 226 { 227 Tick tick = delayed(delay); 228 if (tick < getCurTick()) 229 tick = getCurTick(); 230 231 // Delta notification/timeout. 232 if (delay.value() == 0) { 233 event->schedule(deltas, tick); 234 if (!inEvaluate() && !inUpdate()) 235 scheduleReadyEvent(); 236 return; 237 } 238 239 // Timed notification/timeout. 240 TimeSlot *&ts = timeSlots[tick]; 241 if (!ts) { 242 ts = new TimeSlot; 243 schedule(ts, tick); 244 } 245 event->schedule(ts->events, tick); 246 } 247 248 // For descheduling delayed/timed notifications/timeouts. 249 void 250 deschedule(ScEvent *event) 251 { 252 ScEvents *on = event->scheduledOn(); 253 254 if (on == &deltas) { 255 event->deschedule(); 256 return; 257 } 258 259 // Timed notification/timeout. 260 auto tsit = timeSlots.find(event->when()); 261 panic_if(tsit == timeSlots.end(), 262 "Descheduling event at time with no events."); 263 TimeSlot *ts = tsit->second; 264 ScEvents &events = ts->events; 265 assert(on == &events); 266 event->deschedule(); 267 268 // If no more events are happening at this time slot, get rid of it. 269 if (events.empty()) { 270 deschedule(ts); 271 timeSlots.erase(tsit); 272 } 273 } 274 275 void 276 completeTimeSlot(TimeSlot *ts) 277 { 278 _changeStamp++; 279 assert(ts == timeSlots.begin()->second); 280 timeSlots.erase(timeSlots.begin()); 281 if (!runToTime && starved()) 282 scheduleStarvationEvent(); 283 } 284 285 // Pending activity ignores gem5 activity, much like how a systemc 286 // simulation wouldn't know about asynchronous external events (socket IO 287 // for instance) that might happen before time advances in a pure 288 // systemc simulation. Also the spec lists what specific types of pending 289 // activity needs to be counted, which obviously doesn't include gem5 290 // events. 291 292 // Return whether there's pending systemc activity at this time. 293 bool 294 pendingCurr() 295 { 296 return !readyListMethods.empty() || !readyListThreads.empty() || 297 !updateList.empty() || !deltas.empty(); 298 } 299 300 // Return whether there are pending timed notifications or timeouts. 301 bool 302 pendingFuture() 303 { 304 return !timeSlots.empty(); 305 } 306 307 // Return how many ticks there are until the first pending event, if any. 308 Tick 309 timeToPending() 310 { 311 if (pendingCurr()) 312 return 0; 313 if (pendingFuture()) 314 return timeSlots.begin()->first - getCurTick(); 315 return MaxTick - getCurTick(); 316 } 317 318 // Run scheduled channel updates. 319 void runUpdate(); 320 321 // Run delta events. 322 void runDelta(); 323 324 void setScMainFiber(Fiber *sc_main) { scMain = sc_main; } 325 326 void start(Tick max_tick, bool run_to_time); 327 void oneCycle(); 328 329 void schedulePause(); 330 void scheduleStop(bool finish_delta); 331 332 enum Status 333 { 334 StatusOther = 0, 335 StatusEvaluate, 336 StatusUpdate, 337 StatusDelta, 338 StatusTiming, 339 StatusPaused, 340 StatusStopped 341 }; 342 343 bool elaborationDone() { return _elaborationDone; } 344 void elaborationDone(bool b) { _elaborationDone = b; } 345 346 bool paused() { return status() == StatusPaused; } 347 bool stopped() { return status() == StatusStopped; } 348 bool inEvaluate() { return status() == StatusEvaluate; } 349 bool inUpdate() { return status() == StatusUpdate; } 350 bool inDelta() { return status() == StatusDelta; } 351 bool inTiming() { return status() == StatusTiming; } 352 353 uint64_t changeStamp() { return _changeStamp; } 354 355 void throwToScMain(const ::sc_core::sc_report *r=nullptr); 356 357 Status status() { return _status; } 358 void status(Status s) { _status = s; } 359 360 private: 361 typedef const EventBase::Priority Priority; 362 static Priority DefaultPriority = EventBase::Default_Pri; 363 364 static Priority StopPriority = DefaultPriority - 1; 365 static Priority PausePriority = DefaultPriority + 1; 366 static Priority MaxTickPriority = DefaultPriority + 2; 367 static Priority ReadyPriority = DefaultPriority + 3; 368 static Priority StarvationPriority = ReadyPriority; 369 370 EventQueue *eq; 371 372 // For gem5 style events. 373 void 374 schedule(::Event *event, Tick tick) 375 { 376 if (initDone) 377 eq->schedule(event, tick); 378 else 379 eventsToSchedule[event] = tick; 380 } 381 382 void schedule(::Event *event) { schedule(event, getCurTick()); } 383 384 void 385 deschedule(::Event *event) 386 { 387 if (initDone) 388 eq->deschedule(event); 389 else 390 eventsToSchedule.erase(event); 391 } 392 393 ScEvents deltas; 394 TimeSlots timeSlots; 395 396 Process * 397 getNextReady() 398 { 399 Process *p = readyListMethods.getNext(); 400 return p ? p : readyListThreads.getNext(); 401 } 402 403 void runReady(); 404 EventWrapper<Scheduler, &Scheduler::runReady> readyEvent; 405 void scheduleReadyEvent(); 406 407 void pause(); 408 void stop(); 409 EventWrapper<Scheduler, &Scheduler::pause> pauseEvent; 410 EventWrapper<Scheduler, &Scheduler::stop> stopEvent; 411 412 Fiber *scMain; 413 const ::sc_core::sc_report *_throwToScMain; 414 415 bool 416 starved() 417 { 418 return (readyListMethods.empty() && readyListThreads.empty() && 419 updateList.empty() && deltas.empty() && 420 (timeSlots.empty() || timeSlots.begin()->first > maxTick) && 421 initList.empty()); 422 } 423 EventWrapper<Scheduler, &Scheduler::pause> starvationEvent; 424 void scheduleStarvationEvent(); 425 426 bool _elaborationDone; 427 bool _started; 428 bool _stopNow; 429 430 Status _status; 431 432 Tick maxTick; 433 Tick lastReadyTick; 434 void 435 maxTickFunc() 436 { 437 if (lastReadyTick != getCurTick()) 438 _changeStamp++; 439 pause(); 440 } 441 EventWrapper<Scheduler, &Scheduler::maxTickFunc> maxTickEvent; 442 443 uint64_t _numCycles; 444 uint64_t _changeStamp; 445 446 Process *_current; 447 448 bool initDone; 449 bool runToTime; 450 bool runOnce; 451 452 ProcessList initList; 453 454 ProcessList readyListMethods; 455 ProcessList readyListThreads; 456 457 ChannelList updateList; 458 459 std::map<::Event *, Tick> eventsToSchedule; 460}; 461 462extern Scheduler scheduler; 463 464inline void 465Scheduler::TimeSlot::process() 466{ 467 scheduler.status(StatusTiming); 468 469 try { 470 while (!events.empty()) 471 events.front()->run(); 472 } catch (...) { 473 if (events.empty()) 474 scheduler.completeTimeSlot(this); 475 else 476 scheduler.schedule(this); 477 scheduler.throwToScMain(); 478 } 479 480 scheduler.status(StatusOther); 481 scheduler.completeTimeSlot(this); 482} 483 484const ::sc_core::sc_report *reportifyException(); 485 486} // namespace sc_gem5 487 488#endif // __SYSTEMC_CORE_SCHEDULER_H__ 489