scheduler.hh revision 13245
14159Sgblack@eecs.umich.edu/* 24159Sgblack@eecs.umich.edu * Copyright 2018 Google, Inc. 34159Sgblack@eecs.umich.edu * 44159Sgblack@eecs.umich.edu * Redistribution and use in source and binary forms, with or without 57087Snate@binkert.org * modification, are permitted provided that the following conditions are 67087Snate@binkert.org * met: redistributions of source code must retain the above copyright 77087Snate@binkert.org * notice, this list of conditions and the following disclaimer; 87087Snate@binkert.org * redistributions in binary form must reproduce the above copyright 97087Snate@binkert.org * notice, this list of conditions and the following disclaimer in the 107087Snate@binkert.org * documentation and/or other materials provided with the distribution; 117087Snate@binkert.org * neither the name of the copyright holders nor the names of its 127087Snate@binkert.org * contributors may be used to endorse or promote products derived from 134159Sgblack@eecs.umich.edu * this software without specific prior written permission. 147087Snate@binkert.org * 157087Snate@binkert.org * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 167087Snate@binkert.org * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 177087Snate@binkert.org * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 187087Snate@binkert.org * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 197087Snate@binkert.org * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 207087Snate@binkert.org * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 217087Snate@binkert.org * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 224159Sgblack@eecs.umich.edu * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 237087Snate@binkert.org * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 244159Sgblack@eecs.umich.edu * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 254159Sgblack@eecs.umich.edu * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 264159Sgblack@eecs.umich.edu * 274159Sgblack@eecs.umich.edu * Authors: Gabe Black 284159Sgblack@eecs.umich.edu */ 294159Sgblack@eecs.umich.edu 304159Sgblack@eecs.umich.edu#ifndef __SYSTEMC_CORE_SCHEDULER_HH__ 314159Sgblack@eecs.umich.edu#define __SYSTEMC_CORE_SCHEDULER_HH__ 324159Sgblack@eecs.umich.edu 334159Sgblack@eecs.umich.edu#include <functional> 344159Sgblack@eecs.umich.edu#include <map> 354159Sgblack@eecs.umich.edu#include <set> 364159Sgblack@eecs.umich.edu#include <vector> 374159Sgblack@eecs.umich.edu 384159Sgblack@eecs.umich.edu#include "base/logging.hh" 394159Sgblack@eecs.umich.edu#include "sim/core.hh" 404159Sgblack@eecs.umich.edu#include "sim/eventq.hh" 418229Snate@binkert.org#include "systemc/core/channel.hh" 424159Sgblack@eecs.umich.edu#include "systemc/core/list.hh" 434159Sgblack@eecs.umich.edu#include "systemc/core/process.hh" 444159Sgblack@eecs.umich.edu#include "systemc/core/sched_event.hh" 458229Snate@binkert.org 464159Sgblack@eecs.umich.educlass Fiber; 474159Sgblack@eecs.umich.edu 484159Sgblack@eecs.umich.edunamespace sc_gem5 494159Sgblack@eecs.umich.edu{ 504159Sgblack@eecs.umich.edu 514159Sgblack@eecs.umich.educlass TraceFile; 524159Sgblack@eecs.umich.edu 535567Snate@binkert.orgtypedef NodeList<Process> ProcessList; 544159Sgblack@eecs.umich.edutypedef NodeList<Channel> ChannelList; 554159Sgblack@eecs.umich.edu 564159Sgblack@eecs.umich.edu/* 574159Sgblack@eecs.umich.edu * The scheduler supports three different mechanisms, the initialization phase, 584159Sgblack@eecs.umich.edu * delta cycles, and timed notifications. 594159Sgblack@eecs.umich.edu * 604159Sgblack@eecs.umich.edu * INITIALIZATION PHASE 614159Sgblack@eecs.umich.edu * 624159Sgblack@eecs.umich.edu * The initialization phase has three parts: 634159Sgblack@eecs.umich.edu * 1. Run requested channel updates. 644159Sgblack@eecs.umich.edu * 2. Make processes which need to initialize runnable (methods and threads 654159Sgblack@eecs.umich.edu * which didn't have dont_initialize called on them). 664159Sgblack@eecs.umich.edu * 3. Process delta notifications. 674159Sgblack@eecs.umich.edu * 684159Sgblack@eecs.umich.edu * First, the Kernel SimObject calls the update() method during its startup() 694159Sgblack@eecs.umich.edu * callback which handles the requested channel updates. The Kernel also 704159Sgblack@eecs.umich.edu * schedules an event to be run at time 0 with a slightly elevated priority 714159Sgblack@eecs.umich.edu * so that it happens before any "normal" event. 724159Sgblack@eecs.umich.edu * 734159Sgblack@eecs.umich.edu * When that t0 event happens, it calls the schedulers prepareForInit method 744159Sgblack@eecs.umich.edu * which performs step 2 above. That indirectly causes the scheduler's 754159Sgblack@eecs.umich.edu * readyEvent to be scheduled with slightly lowered priority, ensuring it 764159Sgblack@eecs.umich.edu * happens after any "normal" event. 774159Sgblack@eecs.umich.edu * 784159Sgblack@eecs.umich.edu * Because delta notifications are scheduled at the standard priority, all 794159Sgblack@eecs.umich.edu * of those events will happen next, performing step 3 above. Once they finish, 804159Sgblack@eecs.umich.edu * if the readyEvent was scheduled above, there shouldn't be any higher 814159Sgblack@eecs.umich.edu * priority events in front of it. When it runs, it will start the first 824159Sgblack@eecs.umich.edu * evaluate phase of the first delta cycle. 83 * 84 * DELTA CYCLE 85 * 86 * A delta cycle has three phases within it. 87 * 1. The evaluate phase where runnable processes are allowed to run. 88 * 2. The update phase where requested channel updates hapen. 89 * 3. The delta notification phase where delta notifications happen. 90 * 91 * The readyEvent runs all three steps of the delta cycle. It first goes 92 * through the list of runnable processes and executes them until the set is 93 * empty, and then immediately runs the update phase. Since these are all part 94 * of the same event, there's no chance for other events to intervene and 95 * break the required order above. 96 * 97 * During the update phase above, the spec forbids any action which would make 98 * a process runnable. That means that once the update phase finishes, the set 99 * of runnable processes will be empty. There may, however, have been some 100 * delta notifications/timeouts which will have been scheduled during either 101 * the evaluate or update phase above. Those will have been accumulated in the 102 * scheduler, and are now all executed. 103 * 104 * If any processes became runnable during the delta notification phase, the 105 * readyEvent will have been scheduled and will be waiting and ready to run 106 * again, effectively starting the next delta cycle. 107 * 108 * TIMED NOTIFICATION PHASE 109 * 110 * If no processes became runnable, the event queue will continue to process 111 * events until it comes across an event which represents all the timed 112 * notifications which are supposed to happen at a particular time. The object 113 * which tracks them will execute all those notifications, and then destroy 114 * itself. If the readyEvent is now ready to run, the next delta cycle will 115 * start. 116 * 117 * PAUSE/STOP 118 * 119 * To inject a pause from sc_pause which should happen after the current delta 120 * cycle's delta notification phase, an event is scheduled with a lower than 121 * normal priority, but higher than the readyEvent. That ensures that any 122 * delta notifications which are scheduled with normal priority will happen 123 * first, since those are part of the current delta cycle. Then the pause 124 * event will happen before the next readyEvent which would start the next 125 * delta cycle. All of these events are scheduled for the current time, and so 126 * would happen before any timed notifications went off. 127 * 128 * To inject a stop from sc_stop, the delta cycles should stop before even the 129 * delta notifications have happened, but after the evaluate and update phases. 130 * For that, a stop event with slightly higher than normal priority will be 131 * scheduled so that it happens before any of the delta notification events 132 * which are at normal priority. 133 * 134 * MAX RUN TIME 135 * 136 * When sc_start is called, it's possible to pass in a maximum time the 137 * simulation should run to, at which point sc_pause is implicitly called. The 138 * simulation is supposed to run up to the latest timed notification phase 139 * which is less than or equal to the maximum time. In other words it should 140 * run timed notifications at the maximum time, but not the subsequent evaluate 141 * phase. That's implemented by scheduling an event at the max time with a 142 * priority which is lower than all the others except the ready event. Timed 143 * notifications will happen before it fires, but it will override any ready 144 * event and prevent the evaluate phase from starting. 145 */ 146 147class Scheduler 148{ 149 public: 150 typedef std::list<ScEvent *> ScEvents; 151 152 class TimeSlot : public ::Event 153 { 154 public: 155 TimeSlot() : ::Event(Default_Pri, AutoDelete) {} 156 157 ScEvents events; 158 void process(); 159 }; 160 161 typedef std::map<Tick, TimeSlot *> TimeSlots; 162 163 Scheduler(); 164 ~Scheduler(); 165 166 void clear(); 167 168 const std::string name() const { return "systemc_scheduler"; } 169 170 uint64_t numCycles() { return _numCycles; } 171 Process *current() { return _current; } 172 173 void initPhase(); 174 175 // Register a process with the scheduler. 176 void reg(Process *p); 177 178 // Run the next process, if there is one. 179 void yield(); 180 181 // Put a process on the ready list. 182 void ready(Process *p); 183 184 // Mark a process as ready if init is finished, or put it on the list of 185 // processes to be initialized. 186 void resume(Process *p); 187 188 // Remove a process from the ready/init list if it was on one of them, and 189 // return if it was. 190 bool suspend(Process *p); 191 192 // Schedule an update for a given channel. 193 void requestUpdate(Channel *c); 194 195 // Run the given process immediately, preempting whatever may be running. 196 void 197 runNow(Process *p) 198 { 199 // This function may put a process on the wrong list, ie a thread 200 // the method list. That's fine since that's just a performance 201 // optimization, and the important thing here is how the processes are 202 // ordered. 203 204 // If a process is running, schedule it/us to run again. 205 if (_current) 206 readyListMethods.pushFirst(_current); 207 // Schedule p to run first. 208 readyListMethods.pushFirst(p); 209 yield(); 210 } 211 212 // Set an event queue for scheduling events. 213 void setEventQueue(EventQueue *_eq) { eq = _eq; } 214 215 // Get the current time according to gem5. 216 Tick getCurTick() { return eq ? eq->getCurTick() : 0; } 217 218 Tick 219 delayed(const ::sc_core::sc_time &delay) 220 { 221 //XXX We're assuming the systemc time resolution is in ps. 222 return getCurTick() + delay.value() * SimClock::Int::ps; 223 } 224 225 // For scheduling delayed/timed notifications/timeouts. 226 void 227 schedule(ScEvent *event, const ::sc_core::sc_time &delay) 228 { 229 Tick tick = delayed(delay); 230 if (tick < getCurTick()) 231 tick = getCurTick(); 232 233 // Delta notification/timeout. 234 if (delay.value() == 0) { 235 event->schedule(deltas, tick); 236 if (!inEvaluate() && !inUpdate()) 237 scheduleReadyEvent(); 238 return; 239 } 240 241 // Timed notification/timeout. 242 TimeSlot *&ts = timeSlots[tick]; 243 if (!ts) { 244 ts = new TimeSlot; 245 schedule(ts, tick); 246 } 247 event->schedule(ts->events, tick); 248 } 249 250 // For descheduling delayed/timed notifications/timeouts. 251 void 252 deschedule(ScEvent *event) 253 { 254 ScEvents *on = event->scheduledOn(); 255 256 if (on == &deltas) { 257 event->deschedule(); 258 return; 259 } 260 261 // Timed notification/timeout. 262 auto tsit = timeSlots.find(event->when()); 263 panic_if(tsit == timeSlots.end(), 264 "Descheduling event at time with no events."); 265 TimeSlot *ts = tsit->second; 266 ScEvents &events = ts->events; 267 assert(on == &events); 268 event->deschedule(); 269 270 // If no more events are happening at this time slot, get rid of it. 271 if (events.empty()) { 272 deschedule(ts); 273 timeSlots.erase(tsit); 274 } 275 } 276 277 void 278 completeTimeSlot(TimeSlot *ts) 279 { 280 _changeStamp++; 281 assert(ts == timeSlots.begin()->second); 282 timeSlots.erase(timeSlots.begin()); 283 if (!runToTime && starved()) 284 scheduleStarvationEvent(); 285 scheduleTimeAdvancesEvent(); 286 } 287 288 // Pending activity ignores gem5 activity, much like how a systemc 289 // simulation wouldn't know about asynchronous external events (socket IO 290 // for instance) that might happen before time advances in a pure 291 // systemc simulation. Also the spec lists what specific types of pending 292 // activity needs to be counted, which obviously doesn't include gem5 293 // events. 294 295 // Return whether there's pending systemc activity at this time. 296 bool 297 pendingCurr() 298 { 299 return !readyListMethods.empty() || !readyListThreads.empty() || 300 !updateList.empty() || !deltas.empty(); 301 } 302 303 // Return whether there are pending timed notifications or timeouts. 304 bool 305 pendingFuture() 306 { 307 return !timeSlots.empty(); 308 } 309 310 // Return how many ticks there are until the first pending event, if any. 311 Tick 312 timeToPending() 313 { 314 if (pendingCurr()) 315 return 0; 316 if (pendingFuture()) 317 return timeSlots.begin()->first - getCurTick(); 318 return MaxTick - getCurTick(); 319 } 320 321 // Run scheduled channel updates. 322 void runUpdate(); 323 324 // Run delta events. 325 void runDelta(); 326 327 void setScMainFiber(Fiber *sc_main) { scMain = sc_main; } 328 329 void start(Tick max_tick, bool run_to_time); 330 void oneCycle(); 331 332 void schedulePause(); 333 void scheduleStop(bool finish_delta); 334 335 enum Status 336 { 337 StatusOther = 0, 338 StatusEvaluate, 339 StatusUpdate, 340 StatusDelta, 341 StatusTiming, 342 StatusPaused, 343 StatusStopped 344 }; 345 346 bool elaborationDone() { return _elaborationDone; } 347 void elaborationDone(bool b) { _elaborationDone = b; } 348 349 bool paused() { return status() == StatusPaused; } 350 bool stopped() { return status() == StatusStopped; } 351 bool inEvaluate() { return status() == StatusEvaluate; } 352 bool inUpdate() { return status() == StatusUpdate; } 353 bool inDelta() { return status() == StatusDelta; } 354 bool inTiming() { return status() == StatusTiming; } 355 356 uint64_t changeStamp() { return _changeStamp; } 357 358 void throwToScMain(const ::sc_core::sc_report *r=nullptr); 359 360 Status status() { return _status; } 361 void status(Status s) { _status = s; } 362 363 void registerTraceFile(TraceFile *tf) { traceFiles.insert(tf); } 364 void unregisterTraceFile(TraceFile *tf) { traceFiles.erase(tf); } 365 366 private: 367 typedef const EventBase::Priority Priority; 368 static Priority DefaultPriority = EventBase::Default_Pri; 369 370 static Priority StopPriority = DefaultPriority - 1; 371 static Priority PausePriority = DefaultPriority + 1; 372 static Priority MaxTickPriority = DefaultPriority + 2; 373 static Priority ReadyPriority = DefaultPriority + 3; 374 static Priority StarvationPriority = ReadyPriority; 375 static Priority TimeAdvancesPriority = EventBase::Maximum_Pri; 376 377 EventQueue *eq; 378 379 // For gem5 style events. 380 void 381 schedule(::Event *event, Tick tick) 382 { 383 if (initDone) 384 eq->schedule(event, tick); 385 else 386 eventsToSchedule[event] = tick; 387 } 388 389 void schedule(::Event *event) { schedule(event, getCurTick()); } 390 391 void 392 deschedule(::Event *event) 393 { 394 if (initDone) 395 eq->deschedule(event); 396 else 397 eventsToSchedule.erase(event); 398 } 399 400 ScEvents deltas; 401 TimeSlots timeSlots; 402 403 Process * 404 getNextReady() 405 { 406 Process *p = readyListMethods.getNext(); 407 return p ? p : readyListThreads.getNext(); 408 } 409 410 void runReady(); 411 EventWrapper<Scheduler, &Scheduler::runReady> readyEvent; 412 void scheduleReadyEvent(); 413 414 void pause(); 415 void stop(); 416 EventWrapper<Scheduler, &Scheduler::pause> pauseEvent; 417 EventWrapper<Scheduler, &Scheduler::stop> stopEvent; 418 419 Fiber *scMain; 420 const ::sc_core::sc_report *_throwToScMain; 421 422 bool 423 starved() 424 { 425 return (readyListMethods.empty() && readyListThreads.empty() && 426 updateList.empty() && deltas.empty() && 427 (timeSlots.empty() || timeSlots.begin()->first > maxTick) && 428 initList.empty()); 429 } 430 EventWrapper<Scheduler, &Scheduler::pause> starvationEvent; 431 void scheduleStarvationEvent(); 432 433 bool _elaborationDone; 434 bool _started; 435 bool _stopNow; 436 437 Status _status; 438 439 Tick maxTick; 440 Tick lastReadyTick; 441 void 442 maxTickFunc() 443 { 444 if (lastReadyTick != getCurTick()) 445 _changeStamp++; 446 pause(); 447 } 448 EventWrapper<Scheduler, &Scheduler::maxTickFunc> maxTickEvent; 449 450 void timeAdvances() { trace(false); } 451 EventWrapper<Scheduler, &Scheduler::timeAdvances> timeAdvancesEvent; 452 void 453 scheduleTimeAdvancesEvent() 454 { 455 if (!traceFiles.empty() && !timeAdvancesEvent.scheduled()) 456 schedule(&timeAdvancesEvent); 457 } 458 459 uint64_t _numCycles; 460 uint64_t _changeStamp; 461 462 Process *_current; 463 464 bool initDone; 465 bool runToTime; 466 bool runOnce; 467 468 ProcessList initList; 469 470 ProcessList readyListMethods; 471 ProcessList readyListThreads; 472 473 ChannelList updateList; 474 475 std::map<::Event *, Tick> eventsToSchedule; 476 477 std::set<TraceFile *> traceFiles; 478 479 void trace(bool delta); 480}; 481 482extern Scheduler scheduler; 483 484inline void 485Scheduler::TimeSlot::process() 486{ 487 scheduler.status(StatusTiming); 488 489 try { 490 while (!events.empty()) 491 events.front()->run(); 492 } catch (...) { 493 if (events.empty()) 494 scheduler.completeTimeSlot(this); 495 else 496 scheduler.schedule(this); 497 scheduler.throwToScMain(); 498 } 499 500 scheduler.status(StatusOther); 501 scheduler.completeTimeSlot(this); 502} 503 504const ::sc_core::sc_report *reportifyException(); 505 506} // namespace sc_gem5 507 508#endif // __SYSTEMC_CORE_SCHEDULER_H__ 509