summaryrefslogtreecommitdiff
path: root/src/systemc/core/scheduler.hh
diff options
context:
space:
mode:
authorGabe Black <gabeblack@google.com>2018-08-16 18:59:06 -0700
committerGabe Black <gabeblack@google.com>2018-09-25 23:49:15 +0000
commit54f3a76afe055fa62a818b8355dd8c6f8bd39856 (patch)
treed4041cffe05907b049012fe73d13b3aa440ca13d /src/systemc/core/scheduler.hh
parent9556aa3232ac6f782d715c15d2b4b62f5e7cfb9e (diff)
downloadgem5-54f3a76afe055fa62a818b8355dd8c6f8bd39856.tar.xz
systemc: Rework how delta and timed notifications/timeouts are tracked.
Rather than delegating them entirely to the gem5 event queue and using priorities to ensure the right thing happens, this change adds a few new structures which keep track of them and give the scheduler more control over what happens and in what order. The old scheme was mostly correct, but there were some competing situations which made it next to impossible to make everything happen at the right time. Change-Id: I43f4dd6ddfa488a31073c0318bb41369b1a6117d Reviewed-on: https://gem5-review.googlesource.com/12213 Reviewed-by: Gabe Black <gabeblack@google.com> Maintainer: Gabe Black <gabeblack@google.com>
Diffstat (limited to 'src/systemc/core/scheduler.hh')
-rw-r--r--src/systemc/core/scheduler.hh155
1 files changed, 102 insertions, 53 deletions
diff --git a/src/systemc/core/scheduler.hh b/src/systemc/core/scheduler.hh
index 661a36b78..b221e67d9 100644
--- a/src/systemc/core/scheduler.hh
+++ b/src/systemc/core/scheduler.hh
@@ -30,13 +30,18 @@
#ifndef __SYSTEMC_CORE_SCHEDULER_HH__
#define __SYSTEMC_CORE_SCHEDULER_HH__
+#include <functional>
+#include <map>
+#include <set>
#include <vector>
#include "base/logging.hh"
+#include "sim/core.hh"
#include "sim/eventq.hh"
#include "systemc/core/channel.hh"
#include "systemc/core/list.hh"
#include "systemc/core/process.hh"
+#include "systemc/core/sched_event.hh"
class Fiber;
@@ -81,7 +86,7 @@ typedef NodeList<Channel> ChannelList;
* 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
+ * The readyEvent runs all three 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
@@ -91,23 +96,21 @@ typedef NodeList<Channel> ChannelList;
* 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.
+ * the evaluate or update phase above. Those will have been accumulated in the
+ * scheduler, and are now all executed.
*
* 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.
+ * readyEvent will have been scheduled and will be waiting and ready to run
+ * again, 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.
+ * events until it comes across an event which represents all the timed
+ * notifications which are supposed to happen at a particular time. The object
+ * which tracks them will execute all those notifications, and then destroy
+ * itself. If the readyEvent is now ready to run, the next delta cycle will
+ * start.
*
* PAUSE/STOP
*
@@ -142,6 +145,19 @@ typedef NodeList<Channel> ChannelList;
class Scheduler
{
public:
+ typedef std::set<ScEvent *> ScEvents;
+
+ class TimeSlot : public ::Event
+ {
+ public:
+ TimeSlot() : ::Event(Default_Pri, AutoDelete) {}
+
+ ScEvents events;
+ void process();
+ };
+
+ typedef std::map<Tick, TimeSlot *> TimeSlots;
+
Scheduler();
const std::string name() const { return "systemc_scheduler"; }
@@ -185,42 +201,74 @@ class Scheduler
// Get the current time according to gem5.
Tick getCurTick() { return eq ? eq->getCurTick() : 0; }
+ Tick
+ delayed(const ::sc_core::sc_time &delay)
+ {
+ //XXX We're assuming the systemc time resolution is in ps.
+ return getCurTick() + delay.value() * SimClock::Int::ps;
+ }
+
// For scheduling delayed/timed notifications/timeouts.
void
- schedule(::Event *event, Tick tick)
+ schedule(ScEvent *event, const ::sc_core::sc_time &delay)
{
- pendingTicks[tick]++;
+ Tick tick = delayed(delay);
+ event->schedule(tick);
+
+ // Delta notification/timeout.
+ if (delay.value() == 0) {
+ deltas.insert(event);
+ scheduleReadyEvent();
+ return;
+ }
- if (initReady)
- eq->schedule(event, tick);
- else
- eventsToSchedule[event] = tick;
+ // Timed notification/timeout.
+ TimeSlot *&ts = timeSlots[tick];
+ if (!ts) {
+ ts = new TimeSlot;
+ if (initReady)
+ eq->schedule(ts, tick);
+ else
+ eventsToSchedule[ts] = tick;
+ }
+ ts->events.insert(event);
}
// For descheduling delayed/timed notifications/timeouts.
void
- deschedule(::Event *event)
+ deschedule(ScEvent *event)
{
- auto it = pendingTicks.find(event->when());
- if (--it->second == 0)
- pendingTicks.erase(it);
-
- if (initReady)
- eq->deschedule(event);
- else
- eventsToSchedule.erase(event);
+ if (event->when() == getCurTick()) {
+ // Remove from delta notifications.
+ deltas.erase(event);
+ event->deschedule();
+ return;
+ }
+
+ // Timed notification/timeout.
+ auto tsit = timeSlots.find(event->when());
+ panic_if(tsit == timeSlots.end(),
+ "Descheduling event at time with no events.");
+ TimeSlot *ts = tsit->second;
+ ScEvents &events = ts->events;
+ events.erase(event);
+ event->deschedule();
+
+ // If no more events are happening at this time slot, get rid of it.
+ if (events.empty()) {
+ if (initReady)
+ eq->deschedule(ts);
+ else
+ eventsToSchedule.erase(ts);
+ timeSlots.erase(tsit);
+ }
}
- // Tell the scheduler than an event fired for bookkeeping purposes.
void
- eventHappened()
+ completeTimeSlot(TimeSlot *ts)
{
- auto it = pendingTicks.begin();
- if (--it->second == 0)
- pendingTicks.erase(it);
-
- if (starved() && !runToTime)
- scheduleStarvationEvent();
+ assert(ts == timeSlots.begin()->second);
+ timeSlots.erase(timeSlots.begin());
}
// Pending activity ignores gem5 activity, much like how a systemc
@@ -234,33 +282,25 @@ class Scheduler
bool
pendingCurr()
{
- if (!readyList.empty() || !updateList.empty())
- return true;
- return pendingTicks.size() &&
- pendingTicks.begin()->first == getCurTick();
+ return !readyList.empty() || !updateList.empty() || !deltas.empty();
}
// Return whether there are pending timed notifications or timeouts.
bool
pendingFuture()
{
- switch (pendingTicks.size()) {
- case 0: return false;
- case 1: return pendingTicks.begin()->first > getCurTick();
- default: return true;
- }
+ return !timeSlots.empty();
}
// Return how many ticks there are until the first pending event, if any.
Tick
timeToPending()
{
- if (!readyList.empty() || !updateList.empty())
+ if (pendingCurr())
return 0;
- else if (pendingTicks.size())
- return pendingTicks.begin()->first - getCurTick();
- else
- return MaxTick - getCurTick();
+ if (pendingFuture())
+ return timeSlots.begin()->first - getCurTick();
+ return MaxTick - getCurTick();
}
// Run scheduled channel updates.
@@ -288,7 +328,9 @@ class Scheduler
static Priority StarvationPriority = ReadyPriority;
EventQueue *eq;
- std::map<Tick, int> pendingTicks;
+
+ ScEvents deltas;
+ TimeSlots timeSlots;
void runReady();
EventWrapper<Scheduler, &Scheduler::runReady> readyEvent;
@@ -303,9 +345,8 @@ class Scheduler
bool
starved()
{
- return (readyList.empty() && updateList.empty() &&
- (pendingTicks.empty() ||
- pendingTicks.begin()->first > maxTick) &&
+ return (readyList.empty() && updateList.empty() && deltas.empty() &&
+ (timeSlots.empty() || timeSlots.begin()->first > maxTick) &&
initList.empty());
}
EventWrapper<Scheduler, &Scheduler::pause> starvationEvent;
@@ -337,6 +378,14 @@ class Scheduler
extern Scheduler scheduler;
+inline void
+Scheduler::TimeSlot::process()
+{
+ for (auto &e: events)
+ e->run();
+ scheduler.completeTimeSlot(this);
+}
+
} // namespace sc_gem5
#endif // __SYSTEMC_CORE_SCHEDULER_H__