These functions can descend to the children of a terminated process, but should have no effect on that process itself. Change-Id: I6e4bdec8c492dd03d05bc1397aa080e8a51397c1 Reviewed-on: https://gem5-review.googlesource.com/c/14917 Reviewed-by: Matthias Jung <jungma@eit.uni-kl.de> Maintainer: Gabe Black <gabeblack@google.com>
442 lines
11 KiB
C++
442 lines
11 KiB
C++
/*
|
|
* 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
|
|
*/
|
|
|
|
#include "systemc/core/process.hh"
|
|
|
|
#include "systemc/core/event.hh"
|
|
#include "systemc/core/port.hh"
|
|
#include "systemc/core/scheduler.hh"
|
|
#include "systemc/ext/core/messages.hh"
|
|
#include "systemc/ext/core/sc_join.hh"
|
|
#include "systemc/ext/core/sc_main.hh"
|
|
#include "systemc/ext/core/sc_process_handle.hh"
|
|
#include "systemc/ext/utils/sc_report_handler.hh"
|
|
|
|
namespace sc_gem5
|
|
{
|
|
|
|
class UnwindExceptionReset : public ::sc_core::sc_unwind_exception
|
|
{
|
|
public:
|
|
UnwindExceptionReset() { _isReset = true; }
|
|
};
|
|
|
|
class UnwindExceptionKill : public ::sc_core::sc_unwind_exception
|
|
{
|
|
public:
|
|
UnwindExceptionKill() {}
|
|
};
|
|
|
|
template <typename T>
|
|
struct BuiltinExceptionWrapper : public ExceptionWrapperBase
|
|
{
|
|
public:
|
|
T t;
|
|
void throw_it() override { throw t; }
|
|
};
|
|
|
|
BuiltinExceptionWrapper<UnwindExceptionReset> resetException;
|
|
BuiltinExceptionWrapper<UnwindExceptionKill> killException;
|
|
|
|
|
|
void
|
|
Process::forEachKid(const std::function<void(Process *)> &work)
|
|
{
|
|
for (auto &kid: get_child_objects()) {
|
|
Process *p_kid = dynamic_cast<Process *>(kid);
|
|
if (p_kid)
|
|
work(p_kid);
|
|
}
|
|
}
|
|
|
|
void
|
|
Process::suspend(bool inc_kids)
|
|
{
|
|
if (inc_kids)
|
|
forEachKid([](Process *p) { p->suspend(true); });
|
|
|
|
if (!_suspended && !_terminated) {
|
|
_suspended = true;
|
|
_suspendedReady = scheduler.suspend(this);
|
|
|
|
if (procKind() != ::sc_core::SC_METHOD_PROC_ &&
|
|
scheduler.current() == this) {
|
|
// This isn't in the spec, but Accellera says that a thread that
|
|
// self suspends should be marked ready immediately when it's
|
|
// resumed.
|
|
_suspendedReady = true;
|
|
scheduler.yield();
|
|
}
|
|
}
|
|
}
|
|
|
|
void
|
|
Process::resume(bool inc_kids)
|
|
{
|
|
if (inc_kids)
|
|
forEachKid([](Process *p) { p->resume(true); });
|
|
|
|
if (_suspended && !_terminated) {
|
|
_suspended = false;
|
|
if (_suspendedReady)
|
|
scheduler.resume(this);
|
|
_suspendedReady = false;
|
|
}
|
|
}
|
|
|
|
void
|
|
Process::disable(bool inc_kids)
|
|
{
|
|
if (inc_kids)
|
|
forEachKid([](Process *p) { p->disable(true); });
|
|
|
|
if (!::sc_core::sc_allow_process_control_corners &&
|
|
timeoutEvent.scheduled()) {
|
|
std::string message("attempt to disable a thread with timeout wait: ");
|
|
message += name();
|
|
SC_REPORT_ERROR(sc_core::SC_ID_PROCESS_CONTROL_CORNER_CASE_,
|
|
message.c_str());
|
|
}
|
|
|
|
if (!_terminated)
|
|
_disabled = true;
|
|
}
|
|
|
|
void
|
|
Process::enable(bool inc_kids)
|
|
{
|
|
|
|
if (inc_kids)
|
|
forEachKid([](Process *p) { p->enable(true); });
|
|
|
|
if (!_terminated)
|
|
_disabled = false;
|
|
}
|
|
|
|
void
|
|
Process::kill(bool inc_kids)
|
|
{
|
|
if (::sc_core::sc_get_status() != ::sc_core::SC_RUNNING) {
|
|
SC_REPORT_ERROR(sc_core::SC_ID_KILL_PROCESS_WHILE_UNITIALIZED_,
|
|
name());
|
|
}
|
|
|
|
// Propogate the kill to our children no matter what happens to us.
|
|
if (inc_kids)
|
|
forEachKid([](Process *p) { p->kill(true); });
|
|
|
|
// If we're unwinding or terminated, ignore the kill request.
|
|
if (_isUnwinding || _terminated)
|
|
return;
|
|
|
|
// Update our state.
|
|
terminate();
|
|
_isUnwinding = true;
|
|
|
|
// Make sure this process isn't marked ready
|
|
popListNode();
|
|
|
|
// Inject the kill exception into this process if it's started.
|
|
if (!_needsStart)
|
|
injectException(killException);
|
|
}
|
|
|
|
void
|
|
Process::reset(bool inc_kids)
|
|
{
|
|
if (::sc_core::sc_get_status() != ::sc_core::SC_RUNNING) {
|
|
SC_REPORT_ERROR(sc_core::SC_ID_RESET_PROCESS_WHILE_NOT_RUNNING_,
|
|
name());
|
|
}
|
|
|
|
// Propogate the reset to our children no matter what happens to us.
|
|
if (inc_kids)
|
|
forEachKid([](Process *p) { p->reset(true); });
|
|
|
|
// If we're already unwinding or terminated, ignore the reset request.
|
|
if (_isUnwinding || _terminated)
|
|
return;
|
|
|
|
// Clear suspended ready since we're about to run regardless.
|
|
_suspendedReady = false;
|
|
|
|
_resetEvent.notify();
|
|
|
|
if (_needsStart) {
|
|
scheduler.runNow(this);
|
|
} else {
|
|
_isUnwinding = true;
|
|
injectException(resetException);
|
|
}
|
|
}
|
|
|
|
void
|
|
Process::throw_it(ExceptionWrapperBase &exc, bool inc_kids)
|
|
{
|
|
if (::sc_core::sc_get_status() != ::sc_core::SC_RUNNING)
|
|
SC_REPORT_ERROR(sc_core::SC_ID_THROW_IT_WHILE_NOT_RUNNING_, name());
|
|
|
|
if (inc_kids)
|
|
forEachKid([&exc](Process *p) { p->throw_it(exc, true); });
|
|
|
|
if (_needsStart || _terminated ||
|
|
procKind() == ::sc_core::SC_METHOD_PROC_) {
|
|
SC_REPORT_WARNING(sc_core::SC_ID_THROW_IT_IGNORED_, name());
|
|
return;
|
|
}
|
|
|
|
injectException(exc);
|
|
}
|
|
|
|
void
|
|
Process::injectException(ExceptionWrapperBase &exc)
|
|
{
|
|
excWrapper = &exc;
|
|
scheduler.runNow(this);
|
|
};
|
|
|
|
void
|
|
Process::syncResetOn(bool inc_kids)
|
|
{
|
|
if (inc_kids)
|
|
forEachKid([](Process *p) { p->syncResetOn(true); });
|
|
|
|
_syncReset = true;
|
|
}
|
|
|
|
void
|
|
Process::syncResetOff(bool inc_kids)
|
|
{
|
|
if (inc_kids)
|
|
forEachKid([](Process *p) { p->syncResetOff(true); });
|
|
|
|
_syncReset = false;
|
|
}
|
|
|
|
void
|
|
Process::signalReset(bool set, bool sync)
|
|
{
|
|
if (set) {
|
|
waitCount(0);
|
|
if (sync) {
|
|
syncResetCount++;
|
|
} else {
|
|
asyncResetCount++;
|
|
cancelTimeout();
|
|
clearDynamic();
|
|
scheduler.runNext(this);
|
|
}
|
|
} else {
|
|
if (sync)
|
|
syncResetCount--;
|
|
else
|
|
asyncResetCount--;
|
|
}
|
|
}
|
|
|
|
void
|
|
Process::run()
|
|
{
|
|
bool reset;
|
|
do {
|
|
reset = false;
|
|
try {
|
|
func->call();
|
|
} catch(ScHalt) {
|
|
std::cout << "Terminating process " << name() << std::endl;
|
|
} catch(const ::sc_core::sc_unwind_exception &exc) {
|
|
reset = exc.is_reset();
|
|
_isUnwinding = false;
|
|
} catch (...) {
|
|
throw;
|
|
}
|
|
} while (reset);
|
|
needsStart(true);
|
|
}
|
|
|
|
void
|
|
Process::addStatic(StaticSensitivity *s)
|
|
{
|
|
staticSensitivities.push_back(s);
|
|
}
|
|
|
|
void
|
|
Process::setDynamic(DynamicSensitivity *s)
|
|
{
|
|
if (dynamicSensitivity) {
|
|
dynamicSensitivity->clear();
|
|
delete dynamicSensitivity;
|
|
}
|
|
dynamicSensitivity = s;
|
|
}
|
|
|
|
void
|
|
Process::addReset(Reset *reset)
|
|
{
|
|
resets.push_back(reset);
|
|
}
|
|
|
|
void
|
|
Process::cancelTimeout()
|
|
{
|
|
if (timeoutEvent.scheduled())
|
|
scheduler.deschedule(&timeoutEvent);
|
|
}
|
|
|
|
void
|
|
Process::setTimeout(::sc_core::sc_time t)
|
|
{
|
|
cancelTimeout();
|
|
scheduler.schedule(&timeoutEvent, t);
|
|
}
|
|
|
|
void
|
|
Process::timeout()
|
|
{
|
|
// A process is considered timed_out only if it was also waiting for an
|
|
// event but got a timeout instead.
|
|
_timedOut = (dynamicSensitivity != nullptr);
|
|
|
|
setDynamic(nullptr);
|
|
if (disabled())
|
|
return;
|
|
|
|
ready();
|
|
}
|
|
|
|
void
|
|
Process::satisfySensitivity(Sensitivity *s)
|
|
{
|
|
if (_waitCount) {
|
|
_waitCount--;
|
|
return;
|
|
}
|
|
|
|
// If there's a dynamic sensitivity and this wasn't it, ignore.
|
|
if ((dynamicSensitivity || timeoutEvent.scheduled()) &&
|
|
dynamicSensitivity != s) {
|
|
return;
|
|
}
|
|
|
|
_timedOut = false;
|
|
// This sensitivity should already be cleared by this point, or the event
|
|
// which triggered it will take care of it.
|
|
delete dynamicSensitivity;
|
|
dynamicSensitivity = nullptr;
|
|
cancelTimeout();
|
|
ready();
|
|
}
|
|
|
|
void
|
|
Process::ready()
|
|
{
|
|
if (disabled())
|
|
return;
|
|
if (suspended())
|
|
_suspendedReady = true;
|
|
else if (!scheduled())
|
|
scheduler.ready(this);
|
|
}
|
|
|
|
void
|
|
Process::lastReport(::sc_core::sc_report *report)
|
|
{
|
|
if (report) {
|
|
_lastReport = std::unique_ptr<::sc_core::sc_report>(
|
|
new ::sc_core::sc_report(*report));
|
|
} else {
|
|
_lastReport = nullptr;
|
|
}
|
|
}
|
|
|
|
::sc_core::sc_report *Process::lastReport() const { return _lastReport.get(); }
|
|
|
|
Process::Process(const char *name, ProcessFuncWrapper *func, bool internal) :
|
|
::sc_core::sc_process_b(name), excWrapper(nullptr),
|
|
timeoutEvent([this]() { this->timeout(); }),
|
|
func(func), _internal(internal), _timedOut(false), _dontInitialize(false),
|
|
_needsStart(true), _isUnwinding(false), _terminated(false),
|
|
_scheduled(false), _suspended(false), _disabled(false),
|
|
_syncReset(false), syncResetCount(0), asyncResetCount(0), _waitCount(0),
|
|
refCount(0), stackSize(::Fiber::DefaultStackSize),
|
|
dynamicSensitivity(nullptr)
|
|
{
|
|
_dynamic =
|
|
(::sc_core::sc_get_status() >
|
|
::sc_core::SC_BEFORE_END_OF_ELABORATION);
|
|
_newest = this;
|
|
}
|
|
|
|
void
|
|
Process::terminate()
|
|
{
|
|
_terminated = true;
|
|
_suspendedReady = false;
|
|
_suspended = false;
|
|
_syncReset = false;
|
|
clearDynamic();
|
|
cancelTimeout();
|
|
for (auto s: staticSensitivities) {
|
|
s->clear();
|
|
delete s;
|
|
}
|
|
staticSensitivities.clear();
|
|
|
|
_terminatedEvent.notify();
|
|
|
|
for (auto jw: joinWaiters)
|
|
jw->signal();
|
|
joinWaiters.clear();
|
|
}
|
|
|
|
Process *Process::_newest;
|
|
|
|
void
|
|
throw_it_wrapper(Process *p, ExceptionWrapperBase &exc, bool inc_kids)
|
|
{
|
|
p->throw_it(exc, inc_kids);
|
|
}
|
|
|
|
void
|
|
newReset(const sc_core::sc_port_base *pb, Process *p, bool s, bool v)
|
|
{
|
|
Port *port = Port::fromPort(pb);
|
|
port->addReset(new Reset(p, s, v));
|
|
}
|
|
|
|
void
|
|
newReset(const sc_core::sc_signal_in_if<bool> *sig, Process *p, bool s, bool v)
|
|
{
|
|
Reset *reset = new Reset(p, s, v);
|
|
if (!reset->install(sig))
|
|
delete reset;
|
|
}
|
|
|
|
} // namespace sc_gem5
|