diff options
Diffstat (limited to 'src/systemc/core/scheduler.hh')
-rw-r--r-- | src/systemc/core/scheduler.hh | 108 |
1 files changed, 91 insertions, 17 deletions
diff --git a/src/systemc/core/scheduler.hh b/src/systemc/core/scheduler.hh index a7216231a..e1ad21a57 100644 --- a/src/systemc/core/scheduler.hh +++ b/src/systemc/core/scheduler.hh @@ -30,6 +30,8 @@ #ifndef __SYSTEMC_CORE_SCHEDULER_HH__ #define __SYSTEMC_CORE_SCHEDULER_HH__ +#include "sim/eventq.hh" +#include "systemc/core/channel.hh" #include "systemc/core/list.hh" #include "systemc/core/process.hh" @@ -37,20 +39,84 @@ namespace sc_gem5 { typedef NodeList<Process> ProcessList; +typedef NodeList<Channel> ChannelList; + +/* + * The scheduler supports three different mechanisms, the initialization phase, + * delta cycles, and timed notifications. + * + * INITIALIZATION PHASE + * + * The initialization phase has three parts: + * 1. Run requested channel updates. + * 2. Make processes which need to initialize runnable (methods and threads + * which didn't have dont_initialize called on them). + * 3. Process delta notifications. + * + * First, the Kernel SimObject calls the update() method during its startup() + * callback which handles the requested channel updates. The Kernel also + * schedules an event to be run at time 0 with a slightly elevated priority + * so that it happens before any "normal" event. + * + * When that t0 event happens, it calls the schedulers initToReady method + * which performs step 2 above. That indirectly causes the scheduler's + * readyEvent to be scheduled with slightly lowered priority, ensuring it + * happens after any "normal" event. + * + * Because delta notifications are scheduled at the standard priority, all + * of those events will happen next, performing step 3 above. Once they finish, + * if the readyEvent was scheduled above, there shouldn't be any higher + * priority events in front of it. When it runs, it will start the first + * evaluate phase of the first delta cycle. + * + * DELTA CYCLE + * + * A delta cycle has three phases within it. + * 1. The evaluate phase where runnable processes are allowed to run. + * 2. The update phase where requested channel updates hapen. + * 3. The delta notification phase where delta notifications happen. + * + * The readyEvent runs the first two steps of the delta cycle. It first goes + * through the list of runnable processes and executes them until the set is + * empty, and then immediately runs the update phase. Since these are all part + * of the same event, there's no chance for other events to intervene and + * break the required order above. + * + * During the update phase above, the spec forbids any action which would make + * a process runnable. That means that once the update phase finishes, the set + * of runnable processes will be empty. There may, however, have been some + * delta notifications/timeouts which will have been scheduled during either + * the evaluate or update phase above. Because those are scheduled at the + * normal priority, they will now happen together until there aren't any + * delta events left. + * + * If any processes became runnable during the delta notification phase, the + * readyEvent will have been scheduled and will have been waiting patiently + * behind the delta notification events. That will now run, effectively + * starting the next delta cycle. + * + * TIMED NOTIFICATION PHASE + * + * If no processes became runnable, the event queue will continue to process + * events until it comes across a timed notification, aka a notification + * scheduled to happen in the future. Like delta notification events, those + * will all happen together since the readyEvent priority is lower, + * potentially marking new processes as ready. Once these events finish, the + * readyEvent may run, starting the next delta cycle. + */ class Scheduler { public: Scheduler(); + const std::string name() const { return "systemc_scheduler"; } + uint64_t numCycles() { return _numCycles; } Process *current() { return _current; } - // Run the initialization phase. - void initialize(); - - // Run delta cycles until time needs to advance. - void runCycles(); + // Mark processes that need to be initialized as ready. + void initToReady(); // Put a process on the list of processes to be initialized. void init(Process *p) { initList.pushLast(p); } @@ -59,15 +125,10 @@ class Scheduler void yield(); // Put a process on the ready list. - void - ready(Process *p) - { - // Clump methods together to minimize context switching. - if (p->procKind() == ::sc_core::SC_METHOD_PROC_) - readyList.pushFirst(p); - else - readyList.pushLast(p); - } + void ready(Process *p); + + // Schedule an update for a given channel. + void requestUpdate(Channel *c); // Run the given process immediately, preempting whatever may be running. void @@ -81,7 +142,22 @@ class Scheduler yield(); } + // Set an event queue for scheduling events. + void setEventQueue(EventQueue *_eq) { eq = _eq; } + + // Retrieve the event queue. + EventQueue &eventQueue() const { return *eq; } + + // Run scheduled channel updates. + void update(); + private: + EventQueue *eq; + + void runReady(); + EventWrapper<Scheduler, &Scheduler::runReady> readyEvent; + void scheduleReadyEvent(); + uint64_t _numCycles; Process *_current; @@ -89,9 +165,7 @@ class Scheduler ProcessList initList; ProcessList readyList; - void evaluate(); - void update(); - void delta(); + ChannelList updateList; }; extern Scheduler scheduler; |