systemc: Implement the sensitivity mechanism.

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>
This commit is contained in:
Gabe Black
2018-07-16 16:14:33 -07:00
parent d7755ec828
commit f2ab5e7a9e
12 changed files with 523 additions and 26 deletions

View File

@@ -0,0 +1,46 @@
/*
* 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__

View File

@@ -32,6 +32,8 @@
#include <cstring>
#include <utility>
#include "base/logging.hh"
#include "sim/core.hh"
#include "systemc/core/module.hh"
#include "systemc/core/scheduler.hh"
@@ -41,7 +43,7 @@ namespace sc_gem5
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();
@@ -81,6 +83,9 @@ Event::~Event()
EventsIt it = findEvent(_name);
std::swap(*it, allEvents.back());
allEvents.pop_back();
if (delayedNotifyEvent.scheduled())
scheduler.deschedule(&delayedNotifyEvent);
}
const std::string &
@@ -110,16 +115,34 @@ Event::getParentObject() const
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

View File

@@ -30,9 +30,11 @@
#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"
@@ -50,6 +52,8 @@ namespace sc_gem5
typedef std::vector<sc_core::sc_event *> Events;
class Sensitivity;
class Event
{
public:
@@ -88,6 +92,9 @@ class Event
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;
@@ -97,6 +104,10 @@ class Event
sc_core::sc_object *parent;
EventsIt parentIt;
EventWrapper<Event, &Event::notify> delayedNotify;
mutable std::set<Sensitivity *> sensitivities;
};
extern Events topLevelEvents;

View File

@@ -56,7 +56,7 @@ Kernel::t0Handler()
// 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

View File

@@ -28,11 +28,84 @@
*/
#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:
@@ -190,6 +263,23 @@ Process::syncResetOff(bool inc_kids)
_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()
{
@@ -206,15 +296,31 @@ 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;

View File

@@ -31,17 +31,218 @@
#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:
@@ -78,12 +279,17 @@ class Process : public ::sc_core::sc_object, public ListNode
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; }
@@ -93,7 +299,12 @@ class Process : public ::sc_core::sc_object, public ListNode
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;
@@ -113,8 +324,26 @@ class Process : public ::sc_core::sc_object, public ListNode
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__

View File

@@ -28,17 +28,25 @@
*/
#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 &)
{

View File

@@ -28,6 +28,8 @@
*/
#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
@@ -36,30 +38,34 @@ 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;
}

View File

@@ -38,14 +38,52 @@ namespace sc_gem5
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

View File

@@ -30,6 +30,8 @@
#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"
@@ -58,7 +60,7 @@ typedef NodeList<Channel> ChannelList;
* 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.
@@ -115,11 +117,14 @@ class Scheduler
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();
// Put a process on the list of processes to be initialized.
void init(Process *p) { initList.pushLast(p); }
// Register a process with the scheduler.
void reg(Process *p);
// Tell the scheduler not to initialize a process.
void dontInitialize(Process *p);
// Run the next process, if there is one.
void yield();
@@ -162,7 +167,10 @@ class Scheduler
Process *_current;
bool initReady;
ProcessList initList;
ProcessList toFinalize;
ProcessList readyList;
ChannelList updateList;

View File

@@ -39,6 +39,8 @@ namespace sc_gem5
{
class Event;
class SensitivityEventAndList;
class SensitivityEventOrList;
}
@@ -100,6 +102,7 @@ class sc_event_and_list
private:
friend class sc_event_and_expr;
friend class sc_gem5::SensitivityEventAndList;
explicit sc_event_and_list(bool auto_delete);
@@ -131,6 +134,7 @@ class sc_event_or_list
private:
friend class sc_event_or_expr;
friend class sc_gem5::SensitivityEventOrList;
explicit sc_event_or_list(bool auto_delete);

View File

@@ -30,9 +30,19 @@
#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
{
@@ -48,11 +58,13 @@ enum sc_port_policy
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 &);
@@ -61,6 +73,12 @@ class sc_port_base : public sc_object
// 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>