void
Process::run()
{
- _running = true;
bool reset;
do {
reset = false;
reset = exc.is_reset();
}
} while (reset);
- _running = false;
+ _terminated = true;
}
void
scheduler.ready(this);
}
-Process::Process(const char *name, ProcessFuncWrapper *func, bool _dynamic) :
+Process::Process(const char *name, ProcessFuncWrapper *func,
+ bool _dynamic, bool needs_start) :
::sc_core::sc_object(name), excWrapper(nullptr), func(func),
- _running(false), _dynamic(_dynamic), _isUnwinding(false),
+ _needsStart(needs_start), _dynamic(_dynamic), _isUnwinding(false),
_terminated(false), _suspended(false), _disabled(false),
_syncReset(false), refCount(0), stackSize(::Fiber::DefaultStackSize),
dynamicSensitivity(nullptr)
{
public:
virtual ::sc_core::sc_curr_proc_kind procKind() const = 0;
- bool running() const { return _running; }
+ bool needsStart() const { return _needsStart; }
bool dynamic() const { return _dynamic; }
bool isUnwinding() const { return _isUnwinding; }
bool terminated() const { return _terminated; }
static Process *newest() { return _newest; }
protected:
- Process(const char *name, ProcessFuncWrapper *func, bool _dynamic);
+ Process(const char *name, ProcessFuncWrapper *func, bool _dynamic,
+ bool needs_start);
static Process *_newest;
ProcessFuncWrapper *func;
sc_core::sc_curr_proc_kind _procKind;
- bool _running;
+ bool _needsStart;
bool _dynamic;
bool _isUnwinding;
bool _terminated;
{
public:
Method(const char *name, ProcessFuncWrapper *func, bool _dynamic=false) :
- Process(name, func, _dynamic)
+ Process(name, func, _dynamic, true)
{}
const char *kind() const override { return "sc_method_process"; }
{
public:
Thread(const char *name, ProcessFuncWrapper *func, bool _dynamic=false) :
- Process(name, func, _dynamic), ctx(nullptr)
+ Process(name, func, _dynamic, false), ctx(nullptr)
{}
~Thread() { delete ctx; }
sc_stop_mode _stop_mode = SC_STOP_FINISH_DELTA;
sc_status _status = SC_ELABORATION;
-Tick _max_tick = MaxTick;
-sc_starvation_policy _starvation = SC_EXIT_ON_STARVATION;
-
} // anonymous namespace
int
void
sc_start()
{
- _max_tick = MaxTick;
- _starvation = SC_EXIT_ON_STARVATION;
-
- // Switch back gem5.
- Fiber::primaryFiber()->run();
+ Tick now = curEventQueue() ? curEventQueue()->getCurTick() : 0;
+ sc_start(sc_time::from_value(MaxTick - now), SC_EXIT_ON_STARVATION);
}
void
sc_pause()
{
- warn("%s not implemented.\n", __PRETTY_FUNCTION__);
+ if (_status == SC_RUNNING)
+ ::sc_gem5::scheduler.schedulePause();
}
void
sc_start(const sc_time &time, sc_starvation_policy p)
{
+ _status = SC_RUNNING;
+
Tick now = curEventQueue() ? curEventQueue()->getCurTick() : 0;
- _max_tick = now + time.value();
- _starvation = p;
+ ::sc_gem5::scheduler.start(now + time.value(), p == SC_RUN_TO_TIME);
- // Switch back to gem5.
- Fiber::primaryFiber()->run();
+ if (::sc_gem5::scheduler.paused())
+ _status = SC_PAUSED;
+ else if (::sc_gem5::scheduler.stopped())
+ _status = SC_STOPPED;
}
void
void
sc_stop()
{
- warn("%s not implemented.\n", __PRETTY_FUNCTION__);
+ if (_status == SC_STOPPED)
+ return;
+
+ if (sc_is_running()) {
+ bool finish_delta = (_stop_mode == SC_STOP_FINISH_DELTA);
+ ::sc_gem5::scheduler.scheduleStop(finish_delta);
+ } else {
+ //XXX Should stop if in one of the various elaboration callbacks.
+ }
}
const sc_time &
{
Scheduler::Scheduler() :
- eq(nullptr), readyEvent(this, false, EventBase::Default_Pri + 1),
+ eq(nullptr), _pendingCurr(0), _pendingFuture(0),
+ readyEvent(this, false, ReadyPriority),
+ pauseEvent(this, false, PausePriority),
+ stopEvent(this, false, StopPriority),
+ scMain(nullptr), _started(false), _paused(false), _stopped(false),
+ maxTickEvent(this, false, MaxTickPriority),
_numCycles(0), _current(nullptr), initReady(false)
{}
p->ready();
}
+ if (_started)
+ eq->schedule(&maxTickEvent, maxTick);
+
initReady = true;
}
// Switch to whatever Fiber is supposed to run this process. All
// Fibers which aren't running should be parked at this line.
_current->fiber()->run();
- // If the current process hasn't been started yet, start it. This
- // should always be true for methods, but may not be true for threads.
- if (_current && !_current->running())
+ // If the current process needs to be manually started, start it.
+ if (_current && _current->needsStart())
_current->run();
}
}
}
}
+void
+Scheduler::pause()
+{
+ _paused = true;
+ scMain->run();
+}
+
+void
+Scheduler::stop()
+{
+ _stopped = true;
+ scMain->run();
+}
+
+void
+Scheduler::start(Tick max_tick, bool run_to_time)
+{
+ // We should be running from sc_main. Keep track of that Fiber to return
+ // to later.
+ scMain = Fiber::currentFiber();
+
+ _started = true;
+ _paused = false;
+ _stopped = false;
+
+ maxTick = max_tick;
+
+ if (initReady)
+ eq->schedule(&maxTickEvent, maxTick);
+
+ // Return to gem5 to let it run events, etc.
+ Fiber::primaryFiber()->run();
+
+ if (pauseEvent.scheduled())
+ eq->deschedule(&pauseEvent);
+ if (stopEvent.scheduled())
+ eq->deschedule(&stopEvent);
+ if (maxTickEvent.scheduled())
+ eq->deschedule(&maxTickEvent);
+}
+
+void
+Scheduler::schedulePause()
+{
+ if (pauseEvent.scheduled())
+ return;
+
+ eq->schedule(&pauseEvent, eq->getCurTick());
+}
+
+void
+Scheduler::scheduleStop(bool finish_delta)
+{
+ if (stopEvent.scheduled())
+ return;
+
+ if (!finish_delta) {
+ // If we're not supposed to finish the delta cycle, flush the list
+ // of ready processes and scheduled updates.
+ Process *p;
+ while ((p = readyList.getNext()))
+ p->popListNode();
+ Channel *c;
+ while ((c = updateList.getNext()))
+ c->popListNode();
+ }
+ eq->schedule(&stopEvent, eq->getCurTick());
+}
+
Scheduler scheduler;
} // namespace sc_gem5
#include <vector>
+#include "base/logging.hh"
#include "sim/eventq.hh"
#include "systemc/core/channel.hh"
#include "systemc/core/list.hh"
#include "systemc/core/process.hh"
+class Fiber;
+
namespace sc_gem5
{
* 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.
+ *
+ * PAUSE/STOP
+ *
+ * To inject a pause from sc_pause which should happen after the current delta
+ * cycle's delta notification phase, an event is scheduled with a lower than
+ * normal priority, but higher than the readyEvent. That ensures that any
+ * delta notifications which are scheduled with normal priority will happen
+ * first, since those are part of the current delta cycle. Then the pause
+ * event will happen before the next readyEvent which would start the next
+ * delta cycle. All of these events are scheduled for the current time, and so
+ * would happen before any timed notifications went off.
+ *
+ * To inject a stop from sc_stop, the delta cycles should stop before even the
+ * delta notifications have happened, but after the evaluate and update phases.
+ * For that, a stop event with slightly higher than normal priority will be
+ * scheduled so that it happens before any of the delta notification events
+ * which are at normal priority.
+ *
+ * MAX RUN TIME
+ *
+ * When sc_start is called, it's possible to pass in a maximum time the
+ * simulation should run to, at which point sc_pause is implicitly called.
+ * That's implemented by scheduling an event at the max time with a priority
+ * which is lower than all the others so that it happens only if time would
+ * advance. When that event triggers, it calls the same function as the pause
+ * event.
*/
class Scheduler
// Run scheduled channel updates.
void update();
+ void setScMainFiber(Fiber *sc_main) { scMain = sc_main; }
+
+ void start(Tick max_tick, bool run_to_time);
+
+ void schedulePause();
+ void scheduleStop(bool finish_delta);
+
+ bool paused() { return _paused; }
+ bool stopped() { return _stopped; }
+
private:
+ typedef const EventBase::Priority Priority;
+ static Priority DefaultPriority = EventBase::Default_Pri;
+
+ static Priority StopPriority = DefaultPriority - 1;
+ static Priority PausePriority = DefaultPriority + 1;
+ static Priority ReadyPriority = DefaultPriority + 2;
+ static Priority MaxTickPriority = DefaultPriority + 3;
+
EventQueue *eq;
void runReady();
EventWrapper<Scheduler, &Scheduler::runReady> readyEvent;
void scheduleReadyEvent();
+ void pause();
+ void stop();
+ EventWrapper<Scheduler, &Scheduler::pause> pauseEvent;
+ EventWrapper<Scheduler, &Scheduler::stop> stopEvent;
+ Fiber *scMain;
+
+ bool _started;
+ bool _paused;
+ bool _stopped;
+
+ Tick maxTick;
+ EventWrapper<Scheduler, &Scheduler::pause> maxTickEvent;
+
uint64_t _numCycles;
Process *_current;