This change lets processes be sensitive to events, timeouts, etc.
Change-Id: If30a256dfa8a2e92192c1f9c96b48e2aa28ec27e
Reviewed-on: https://gem5-review.googlesource.com/11713
Reviewed-by: Gabe Black <gabeblack@google.com>
Maintainer: Gabe Black <gabeblack@google.com>
--- /dev/null
+/*
+ * Copyright 2018 Google, Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met: redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer;
+ * redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution;
+ * neither the name of the copyright holders nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * Authors: Gabe Black
+ */
+
+#ifndef __SYSTEMC_CORE_BINDINFO_HH__
+#define __SYSTEMC_CORE_BINDINFO_HH__
+
+#include "systemc/ext/core/sc_interface.hh"
+
+namespace sc_gem5
+{
+
+class BindInfo
+{
+ public:
+ ::sc_core::sc_interface *interface;
+};
+
+} // namespace sc_gem5
+
+#endif // __SYSTEMC_CORE_BINDINFO_HH__
#include <cstring>
#include <utility>
+#include "base/logging.hh"
+#include "sim/core.hh"
#include "systemc/core/module.hh"
#include "systemc/core/scheduler.hh"
Event::Event(sc_core::sc_event *_sc_event) : Event(_sc_event, "") {}
Event::Event(sc_core::sc_event *_sc_event, const char *_basename) :
- _sc_event(_sc_event), _basename(_basename)
+ _sc_event(_sc_event), _basename(_basename), delayedNotify(this)
{
Module *p = currentModule();
EventsIt it = findEvent(_name);
std::swap(*it, allEvents.back());
allEvents.pop_back();
+
+ if (delayedNotifyEvent.scheduled())
+ scheduler.deschedule(&delayedNotifyEvent);
}
const std::string &
void
Event::notify()
{
+ auto local_sensitivities = sensitivities;
+ for (auto s: local_sensitivities)
+ s->notify(this);
}
void
Event::notify(const sc_core::sc_time &t)
{
+ //XXX We're assuming the systemc time resolution is in ps.
+ Tick new_tick = t.value() * SimClock::Int::ps +
+ scheduler.eventQueue().getCurTick();
+ if (delayedNotify.scheduled()) {
+ Tick old_tick = delayedNotify.when();
+
+ if (new_tick >= old_tick)
+ return;
+
+ scheduler.eventQueue().deschedule(&delayedNotify);
+ }
+
+ scheduler.eventQueue().schedule(&delayedNotify, new_tick);
}
void
Event::cancel()
{
+ if (delayedNotify.scheduled())
+ scheduler.eventQueue().deschedule(&delayedNotify);
}
bool
#ifndef __SYSTEMC_CORE_EVENT_HH__
#define __SYSTEMC_CORE_EVENT_HH__
+#include <set>
#include <string>
#include <vector>
+#include "sim/eventq.hh"
#include "systemc/core/list.hh"
#include "systemc/core/object.hh"
#include "systemc/ext/core/sc_prim.hh"
typedef std::vector<sc_core::sc_event *> Events;
+class Sensitivity;
+
class Event
{
public:
return e->_gem5_event;
}
+ void addSensitivity(Sensitivity *s) const { sensitivities.insert(s); }
+ void delSensitivity(Sensitivity *s) const { sensitivities.erase(s); }
+
private:
sc_core::sc_event *_sc_event;
sc_core::sc_object *parent;
EventsIt parentIt;
+
+ EventWrapper<Event, &Event::notify> delayedNotify;
+
+ mutable std::set<Sensitivity *> sensitivities;
};
extern Events topLevelEvents;
// happen before them, honoring the ordering for the initialization phase
// in the spec. The delta phase will happen at normal priority, and then
// the event which runs the processes which is at a lower priority.
- ::sc_gem5::scheduler.initToReady();
+ ::sc_gem5::scheduler.prepareForInit();
}
} // namespace SystemC
*/
#include "systemc/core/process.hh"
+
+#include "base/logging.hh"
+#include "systemc/core/event.hh"
#include "systemc/core/scheduler.hh"
namespace sc_gem5
{
+void
+Sensitivity::satisfy()
+{
+ warn_once("Ignoring suspended status for now.\n");
+ process->setDynamic(nullptr);
+ scheduler.ready(process);
+}
+
+SensitivityTimeout::SensitivityTimeout(Process *p, ::sc_core::sc_time t) :
+ Sensitivity(p), timeoutEvent(this), timeout(t)
+{
+ Tick when = scheduler.eventQueue().getCurTick() + timeout.value();
+ scheduler.eventQueue().schedule(&timeoutEvent, when);
+}
+
+SensitivityTimeout::~SensitivityTimeout()
+{
+ if (timeoutEvent.scheduled())
+ scheduler.eventQueue().deschedule(&timeoutEvent);
+}
+
+SensitivityEvent::SensitivityEvent(
+ Process *p, const ::sc_core::sc_event *e) : Sensitivity(p), event(e)
+{
+ Event::getFromScEvent(event)->addSensitivity(this);
+}
+
+SensitivityEvent::~SensitivityEvent()
+{
+ Event::getFromScEvent(event)->delSensitivity(this);
+}
+
+SensitivityEventAndList::SensitivityEventAndList(
+ Process *p, const ::sc_core::sc_event_and_list *list) :
+ Sensitivity(p), list(list), count(0)
+{
+ for (auto e: list->events)
+ Event::getFromScEvent(e)->addSensitivity(this);
+}
+
+SensitivityEventAndList::~SensitivityEventAndList()
+{
+ for (auto e: list->events)
+ Event::getFromScEvent(e)->delSensitivity(this);
+}
+
+void
+SensitivityEventAndList::notifyWork(Event *e)
+{
+ e->delSensitivity(this);
+ count++;
+ if (count == list->events.size())
+ satisfy();
+}
+
+SensitivityEventOrList::SensitivityEventOrList(
+ Process *p, const ::sc_core::sc_event_or_list *list) :
+ Sensitivity(p), list(list)
+{
+ for (auto e: list->events)
+ Event::getFromScEvent(e)->addSensitivity(this);
+}
+
+SensitivityEventOrList::~SensitivityEventOrList()
+{
+ for (auto e: list->events)
+ Event::getFromScEvent(e)->delSensitivity(this);
+}
+
+
class UnwindExceptionReset : public ::sc_core::sc_unwind_exception
{
public:
_syncReset = false;
}
+void
+Process::dontInitialize()
+{
+ scheduler.dontInitialize(this);
+}
+
+void
+Process::finalize()
+{
+ for (auto &s: pendingStaticSensitivities) {
+ s->finalize(staticSensitivities);
+ delete s;
+ s = nullptr;
+ }
+ pendingStaticSensitivities.clear();
+};
+
void
Process::run()
{
_running = false;
}
+void
+Process::addStatic(PendingSensitivity *s)
+{
+ pendingStaticSensitivities.push_back(s);
+}
+
+void
+Process::setDynamic(Sensitivity *s)
+{
+ delete dynamicSensitivity;
+ dynamicSensitivity = s;
+}
+
Process::Process(const char *name, ProcessFuncWrapper *func, bool _dynamic) :
::sc_core::sc_object(name), excWrapper(nullptr), func(func),
_running(false), _dynamic(_dynamic), _isUnwinding(false),
_terminated(false), _suspended(false), _disabled(false),
- _syncReset(false), refCount(0), stackSize(::Fiber::DefaultStackSize)
+ _syncReset(false), refCount(0), stackSize(::Fiber::DefaultStackSize),
+ dynamicSensitivity(nullptr)
{
_newest = this;
- if (!_dynamic)
- scheduler.init(this);
+ if (_dynamic)
+ finalize();
+ else
+ scheduler.reg(this);
}
Process *Process::_newest;
#define __SYSTEMC_CORE_PROCESS_HH__
#include <functional>
+#include <vector>
#include "base/fiber.hh"
+#include "sim/eventq.hh"
+#include "systemc/core/bindinfo.hh"
#include "systemc/core/list.hh"
#include "systemc/core/object.hh"
#include "systemc/ext/core/sc_event.hh"
+#include "systemc/ext/core/sc_interface.hh"
#include "systemc/ext/core/sc_module.hh"
+#include "systemc/ext/core/sc_port.hh"
#include "systemc/ext/core/sc_process_handle.hh"
namespace sc_gem5
{
+class Sensitivity
+{
+ protected:
+ Process *process;
+ void satisfy();
+
+ public:
+ Sensitivity(Process *p) : process(p) {}
+ virtual ~Sensitivity() {}
+
+ virtual void notifyWork(Event *e) { satisfy(); }
+ void notify(Event *e);
+ void notify() { notify(nullptr); }
+
+ const std::string name();
+};
+
+class SensitivityTimeout : virtual public Sensitivity
+{
+ private:
+ EventWrapper<Sensitivity, &Sensitivity::notify> timeoutEvent;
+ ::sc_core::sc_time timeout;
+
+ public:
+ SensitivityTimeout(Process *p, ::sc_core::sc_time t);
+ ~SensitivityTimeout();
+};
+
+class SensitivityEvent : virtual public Sensitivity
+{
+ private:
+ const ::sc_core::sc_event *event;
+
+ public:
+ SensitivityEvent(Process *p, const ::sc_core::sc_event *e);
+ ~SensitivityEvent();
+};
+
+//XXX This sensitivity can't be reused. To reset it, it has to be deleted and
+//recreated. That works for dynamic sensitivities, but not for static.
+//Fortunately processes can't be statically sensitive to sc_event_and_lists.
+class SensitivityEventAndList : virtual public Sensitivity
+{
+ private:
+ const ::sc_core::sc_event_and_list *list;
+ int count;
+
+ public:
+ SensitivityEventAndList(
+ Process *p, const ::sc_core::sc_event_and_list *list);
+ ~SensitivityEventAndList();
+
+ virtual void notifyWork(Event *e) override;
+};
+
+class SensitivityEventOrList : virtual public Sensitivity
+{
+ private:
+ const ::sc_core::sc_event_or_list *list;
+
+ public:
+ SensitivityEventOrList(
+ Process *p, const ::sc_core::sc_event_or_list *list);
+ ~SensitivityEventOrList();
+};
+
+// Combined sensitivities. These trigger when any of their parts do.
+
+class SensitivityTimeoutAndEvent :
+ public SensitivityTimeout, public SensitivityEvent
+{
+ public:
+ SensitivityTimeoutAndEvent(
+ Process *p, ::sc_core::sc_time t, const ::sc_core::sc_event *e) :
+ Sensitivity(p), SensitivityTimeout(p, t), SensitivityEvent(p, e)
+ {}
+};
+
+class SensitivityTimeoutAndEventAndList :
+ public SensitivityTimeout, public SensitivityEventAndList
+{
+ public:
+ SensitivityTimeoutAndEventAndList(
+ Process *p, ::sc_core::sc_time t,
+ const ::sc_core::sc_event_and_list *eal) :
+ Sensitivity(p), SensitivityTimeout(p, t),
+ SensitivityEventAndList(p, eal)
+ {}
+};
+
+class SensitivityTimeoutAndEventOrList :
+ public SensitivityTimeout, public SensitivityEventOrList
+{
+ public:
+ SensitivityTimeoutAndEventOrList(
+ Process *p, ::sc_core::sc_time t,
+ const ::sc_core::sc_event_or_list *eol) :
+ Sensitivity(p), SensitivityTimeout(p, t),
+ SensitivityEventOrList(p, eol)
+ {}
+};
+
+typedef std::vector<Sensitivity *> Sensitivities;
+
+
+/*
+ * Pending sensitivities. These are records of sensitivities to install later,
+ * once all the information to configure them is available.
+ */
+
+class PendingSensitivity
+{
+ protected:
+ Process *process;
+
+ public:
+ virtual void finalize(Sensitivities &s) = 0;
+ PendingSensitivity(Process *p) : process(p) {}
+ virtual ~PendingSensitivity() {}
+};
+
+class PendingSensitivityEvent : public PendingSensitivity
+{
+ private:
+ const sc_core::sc_event *event;
+
+ public:
+ PendingSensitivityEvent(Process *p, const sc_core::sc_event *e) :
+ PendingSensitivity(p), event(e) {}
+
+ void
+ finalize(Sensitivities &s) override
+ {
+ s.push_back(new SensitivityEvent(process, event));
+ }
+};
+
+class PendingSensitivityInterface : public PendingSensitivity
+{
+ private:
+ const sc_core::sc_interface *interface;
+
+ public:
+ PendingSensitivityInterface(Process *p, const sc_core::sc_interface *i) :
+ PendingSensitivity(p), interface(i)
+ {}
+
+ void
+ finalize(Sensitivities &s) override
+ {
+ s.push_back(new SensitivityEvent(process,
+ &interface->default_event()));
+ }
+};
+
+class PendingSensitivityPort : public PendingSensitivity
+{
+ private:
+ const sc_core::sc_port_base *port;
+
+ public:
+ PendingSensitivityPort(Process *p, const sc_core::sc_port_base *pb) :
+ PendingSensitivity(p), port(pb)
+ {}
+
+ void
+ finalize(Sensitivities &s) override
+ {
+ for (int i = 0; i < port->size(); i++) {
+ const ::sc_core::sc_event *e =
+ &port->_gem5BindInfo[i]->interface->default_event();
+ s.push_back(new SensitivityEvent(process, e));
+ }
+ }
+};
+
+class PendingSensitivityFinder : public PendingSensitivity
+{
+ private:
+ const sc_core::sc_event_finder *finder;
+
+ public:
+ PendingSensitivityFinder(Process *p, const sc_core::sc_event_finder *f) :
+ PendingSensitivity(p), finder(f)
+ {}
+
+ void
+ finalize(Sensitivities &s) override
+ {
+ s.push_back(new SensitivityEvent(process, &finder->find_event()));
+ }
+};
+
+typedef std::vector<PendingSensitivity *> PendingSensitivities;
+
+
class Process : public ::sc_core::sc_object, public ListNode
{
public:
const ::sc_core::sc_event &terminatedEvent() { return _terminatedEvent; }
// This should only be called before initialization.
- void dontInitialize() { popListNode(); }
+ void dontInitialize();
void setStackSize(size_t size) { stackSize = size; }
+ void finalize();
+
void run();
+ void addStatic(PendingSensitivity *);
+ void setDynamic(Sensitivity *);
+
virtual Fiber *fiber() { return Fiber::primaryFiber(); }
static Process *newest() { return _newest; }
static Process *_newest;
- virtual ~Process() { delete func; }
+ virtual ~Process()
+ {
+ delete func;
+ for (auto s: staticSensitivities)
+ delete s;
+ }
::sc_core::sc_event _resetEvent;
::sc_core::sc_event _terminatedEvent;
int refCount;
size_t stackSize;
+
+ Sensitivities staticSensitivities;
+ PendingSensitivities pendingStaticSensitivities;
+
+ Sensitivity *dynamicSensitivity;
};
+inline void
+Sensitivity::notify(Event *e)
+{
+ if (!process->disabled())
+ notifyWork(e);
+}
+
+inline const std::string
+Sensitivity::name()
+{
+ return std::string(process->name()) + ".timeout";
+}
+
} // namespace sc_gem5
#endif //__SYSTEMC_CORE_PROCESS_HH__
*/
#include "base/logging.hh"
+#include "systemc/core/bindinfo.hh"
#include "systemc/ext/core/sc_port.hh"
namespace sc_core
{
+sc_port_base::sc_port_base(const char *name, int n, sc_port_policy p) :
+ sc_object(name)
+{}
+
void
sc_port_base::warn_unimpl(const char *func) const
{
warn("%s not implemented.\n", func);
}
+int sc_port_base::maxSize() const { return _maxSize; }
+int sc_port_base::size() const { return _gem5BindInfo.size(); }
+
void
sc_port_base::bind(sc_interface &)
{
*/
#include "base/logging.hh"
+#include "systemc/core/process.hh"
+#include "systemc/ext/core/sc_interface.hh"
#include "systemc/ext/core/sc_sensitive.hh"
namespace sc_core
sc_sensitive::sc_sensitive() : currentProcess(nullptr) {}
sc_sensitive &
-sc_sensitive::operator << (const sc_event &)
+sc_sensitive::operator << (const sc_event &e)
{
- warn("%s not implemented.\n", __PRETTY_FUNCTION__);
+ currentProcess->addStatic(
+ new sc_gem5::PendingSensitivityEvent(currentProcess, &e));
return *this;
}
sc_sensitive &
-sc_sensitive::operator << (const sc_interface &)
+sc_sensitive::operator << (const sc_interface &i)
{
- warn("%s not implemented.\n", __PRETTY_FUNCTION__);
+ currentProcess->addStatic(
+ new sc_gem5::PendingSensitivityInterface(currentProcess, &i));
return *this;
}
sc_sensitive &
-sc_sensitive::operator << (const sc_port_base &)
+sc_sensitive::operator << (const sc_port_base &b)
{
- warn("%s not implemented.\n", __PRETTY_FUNCTION__);
+ currentProcess->addStatic(
+ new sc_gem5::PendingSensitivityPort(currentProcess, &b));
return *this;
}
sc_sensitive &
-sc_sensitive::operator << (sc_event_finder &)
+sc_sensitive::operator << (sc_event_finder &f)
{
- warn("%s not implemented.\n", __PRETTY_FUNCTION__);
+ currentProcess->addStatic(
+ new sc_gem5::PendingSensitivityFinder(currentProcess, &f));
return *this;
}
Scheduler::Scheduler() :
eq(nullptr), readyEvent(this, false, EventBase::Default_Pri + 1),
- _numCycles(0), _current(nullptr)
+ _numCycles(0), _current(nullptr), initReady(false)
{}
void
-Scheduler::initToReady()
+Scheduler::prepareForInit()
{
- while (!initList.empty())
- ready(initList.getNext());
+ for (Process *p = toFinalize.getNext(); p; p = toFinalize.getNext()) {
+ p->finalize();
+ p->popListNode();
+ }
+
+ for (Process *p = initList.getNext(); p; p = initList.getNext()) {
+ p->finalize();
+ ready(p);
+ }
+
+ initReady = true;
+}
+
+void
+Scheduler::reg(Process *p)
+{
+ if (initReady) {
+ // If we're past initialization, finalize static sensitivity.
+ p->finalize();
+ // Mark the process as ready.
+ ready(p);
+ } else {
+ // Otherwise, record that this process should be initialized once we
+ // get there.
+ initList.pushLast(p);
+ }
+}
+
+void
+Scheduler::dontInitialize(Process *p)
+{
+ if (initReady) {
+ // Pop this process off of the ready list.
+ p->popListNode();
+ } else {
+ // Push this process onto the list of processes which still need
+ // their static sensitivity to be finalized. That implicitly pops it
+ // off the list of processes to be initialized/marked ready.
+ toFinalize.pushLast(p);
+ }
}
void
#ifndef __SYSTEMC_CORE_SCHEDULER_HH__
#define __SYSTEMC_CORE_SCHEDULER_HH__
+#include <vector>
+
#include "sim/eventq.hh"
#include "systemc/core/channel.hh"
#include "systemc/core/list.hh"
* 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
+ * When that t0 event happens, it calls the schedulers prepareForInit 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.
uint64_t numCycles() { return _numCycles; }
Process *current() { return _current; }
- // Mark processes that need to be initialized as ready.
- void initToReady();
+ // Prepare for initialization.
+ void prepareForInit();
+
+ // Register a process with the scheduler.
+ void reg(Process *p);
- // Put a process on the list of processes to be initialized.
- void init(Process *p) { initList.pushLast(p); }
+ // Tell the scheduler not to initialize a process.
+ void dontInitialize(Process *p);
// Run the next process, if there is one.
void yield();
Process *_current;
+ bool initReady;
+
ProcessList initList;
+ ProcessList toFinalize;
ProcessList readyList;
ChannelList updateList;
{
class Event;
+class SensitivityEventAndList;
+class SensitivityEventOrList;
}
private:
friend class sc_event_and_expr;
+ friend class sc_gem5::SensitivityEventAndList;
explicit sc_event_and_list(bool auto_delete);
private:
friend class sc_event_or_expr;
+ friend class sc_gem5::SensitivityEventOrList;
explicit sc_event_or_list(bool auto_delete);
#ifndef __SYSTEMC_EXT_CORE_SC_PORT_HH__
#define __SYSTEMC_EXT_CORE_SC_PORT_HH__
+#include <vector>
+
#include "sc_module.hh" // for sc_gen_unique_name
#include "sc_object.hh"
+namespace sc_gem5
+{
+
+class BindInfo;
+class PendingSensitivityPort;
+
+};
+
namespace sc_core
{
class sc_port_base : public sc_object
{
public:
- sc_port_base(const char *name, int n, sc_port_policy p) : sc_object(name)
- {}
+ sc_port_base(const char *name, int n, sc_port_policy p);
void warn_unimpl(const char *func) const;
+ int maxSize() const;
+ int size() const;
+
protected:
// Implementation defined, but depended on by the tests.
void bind(sc_interface &);
// Implementation defined, but depended on by the tests.
virtual int vbind(sc_interface &) = 0;
virtual int vbind(sc_port_base &) = 0;
+
+ private:
+ friend class ::sc_gem5::PendingSensitivityPort;
+
+ std::vector<::sc_gem5::BindInfo *> _gem5BindInfo;
+ int _maxSize;
};
template <class IF>