process.cc revision 13328:d5f4e801436a
112839Sgabeblack@google.com/*
212839Sgabeblack@google.com * Copyright 2018 Google, Inc.
312839Sgabeblack@google.com *
412839Sgabeblack@google.com * Redistribution and use in source and binary forms, with or without
512839Sgabeblack@google.com * modification, are permitted provided that the following conditions are
612839Sgabeblack@google.com * met: redistributions of source code must retain the above copyright
712839Sgabeblack@google.com * notice, this list of conditions and the following disclaimer;
812839Sgabeblack@google.com * redistributions in binary form must reproduce the above copyright
912839Sgabeblack@google.com * notice, this list of conditions and the following disclaimer in the
1012839Sgabeblack@google.com * documentation and/or other materials provided with the distribution;
1112839Sgabeblack@google.com * neither the name of the copyright holders nor the names of its
1212839Sgabeblack@google.com * contributors may be used to endorse or promote products derived from
1312839Sgabeblack@google.com * this software without specific prior written permission.
1412839Sgabeblack@google.com *
1512839Sgabeblack@google.com * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
1612839Sgabeblack@google.com * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
1712839Sgabeblack@google.com * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
1812839Sgabeblack@google.com * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
1912839Sgabeblack@google.com * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
2012839Sgabeblack@google.com * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
2112839Sgabeblack@google.com * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
2212839Sgabeblack@google.com * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
2312839Sgabeblack@google.com * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
2412839Sgabeblack@google.com * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
2512839Sgabeblack@google.com * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
2612839Sgabeblack@google.com *
2712839Sgabeblack@google.com * Authors: Gabe Black
2812839Sgabeblack@google.com */
2912839Sgabeblack@google.com
3012839Sgabeblack@google.com#include "systemc/core/process.hh"
3112993Sgabeblack@google.com
3212993Sgabeblack@google.com#include "base/logging.hh"
3312993Sgabeblack@google.com#include "systemc/core/event.hh"
3413129Sgabeblack@google.com#include "systemc/core/port.hh"
3512993Sgabeblack@google.com#include "systemc/core/scheduler.hh"
3612839Sgabeblack@google.com#include "systemc/ext/core/messages.hh"
3712839Sgabeblack@google.com#include "systemc/ext/core/sc_join.hh"
3812993Sgabeblack@google.com#include "systemc/ext/core/sc_main.hh"
3912993Sgabeblack@google.com#include "systemc/ext/core/sc_process_handle.hh"
4012993Sgabeblack@google.com#include "systemc/ext/utils/sc_report_handler.hh"
4112993Sgabeblack@google.com
4212993Sgabeblack@google.comnamespace sc_gem5
4312993Sgabeblack@google.com{
4412993Sgabeblack@google.com
4512993Sgabeblack@google.comclass UnwindExceptionReset : public ::sc_core::sc_unwind_exception
4612993Sgabeblack@google.com{
4712993Sgabeblack@google.com  public:
4812993Sgabeblack@google.com    UnwindExceptionReset() { _isReset = true; }
4912993Sgabeblack@google.com};
5012993Sgabeblack@google.com
5112993Sgabeblack@google.comclass UnwindExceptionKill : public ::sc_core::sc_unwind_exception
5212993Sgabeblack@google.com{
5312993Sgabeblack@google.com  public:
5412993Sgabeblack@google.com    UnwindExceptionKill() {}
5512993Sgabeblack@google.com};
5612993Sgabeblack@google.com
5712993Sgabeblack@google.comtemplate <typename T>
5812993Sgabeblack@google.comstruct BuiltinExceptionWrapper : public ExceptionWrapperBase
5912993Sgabeblack@google.com{
6012993Sgabeblack@google.com  public:
6112993Sgabeblack@google.com    T t;
6212993Sgabeblack@google.com    void throw_it() override { throw t; }
6312993Sgabeblack@google.com};
6412993Sgabeblack@google.com
6513131Sgabeblack@google.comBuiltinExceptionWrapper<UnwindExceptionReset> resetException;
6612993Sgabeblack@google.comBuiltinExceptionWrapper<UnwindExceptionKill> killException;
6713131Sgabeblack@google.com
6812993Sgabeblack@google.com
6912993Sgabeblack@google.comvoid
7012993Sgabeblack@google.comProcess::forEachKid(const std::function<void(Process *)> &work)
7112993Sgabeblack@google.com{
7212993Sgabeblack@google.com    for (auto &kid: get_child_objects()) {
7312993Sgabeblack@google.com        Process *p_kid = dynamic_cast<Process *>(kid);
7412993Sgabeblack@google.com        if (p_kid)
7512993Sgabeblack@google.com            work(p_kid);
7612993Sgabeblack@google.com    }
7712993Sgabeblack@google.com}
7812993Sgabeblack@google.com
7912993Sgabeblack@google.comvoid
8012993Sgabeblack@google.comProcess::suspend(bool inc_kids)
8112993Sgabeblack@google.com{
8212993Sgabeblack@google.com    if (inc_kids)
8312993Sgabeblack@google.com        forEachKid([](Process *p) { p->suspend(true); });
8412993Sgabeblack@google.com
8512993Sgabeblack@google.com    if (!_suspended) {
8613180Sgabeblack@google.com        _suspended = true;
8713180Sgabeblack@google.com        _suspendedReady = scheduler.suspend(this);
8813180Sgabeblack@google.com
8913180Sgabeblack@google.com        if (procKind() != ::sc_core::SC_METHOD_PROC_ &&
9013180Sgabeblack@google.com                scheduler.current() == this) {
9113180Sgabeblack@google.com            // This isn't in the spec, but Accellera says that a thread that
9213180Sgabeblack@google.com            // self suspends should be marked ready immediately when it's
9313180Sgabeblack@google.com            // resumed.
9413180Sgabeblack@google.com            _suspendedReady = true;
9513180Sgabeblack@google.com            scheduler.yield();
9612993Sgabeblack@google.com        }
9712993Sgabeblack@google.com    }
9812993Sgabeblack@google.com}
9912993Sgabeblack@google.com
10012993Sgabeblack@google.comvoid
10112993Sgabeblack@google.comProcess::resume(bool inc_kids)
10212993Sgabeblack@google.com{
10312993Sgabeblack@google.com    if (inc_kids)
10412993Sgabeblack@google.com        forEachKid([](Process *p) { p->resume(true); });
10512993Sgabeblack@google.com
10612839Sgabeblack@google.com    if (_suspended) {
10712839Sgabeblack@google.com        _suspended = false;
10812839Sgabeblack@google.com        if (_suspendedReady)
10912993Sgabeblack@google.com            scheduler.resume(this);
11012993Sgabeblack@google.com        _suspendedReady = false;
11112993Sgabeblack@google.com    }
11212839Sgabeblack@google.com}
11312839Sgabeblack@google.com
11412839Sgabeblack@google.comvoid
11512839Sgabeblack@google.comProcess::disable(bool inc_kids)
11612839Sgabeblack@google.com{
11712993Sgabeblack@google.com    if (inc_kids)
11812839Sgabeblack@google.com        forEachKid([](Process *p) { p->disable(true); });
11912839Sgabeblack@google.com
12012839Sgabeblack@google.com    if (!::sc_core::sc_allow_process_control_corners &&
12112839Sgabeblack@google.com            timeoutEvent.scheduled()) {
12212839Sgabeblack@google.com        std::string message("attempt to disable a thread with timeout wait: ");
12312993Sgabeblack@google.com        message += name();
12412839Sgabeblack@google.com        SC_REPORT_ERROR(sc_core::SC_ID_PROCESS_CONTROL_CORNER_CASE_,
12512839Sgabeblack@google.com                message.c_str());
12612839Sgabeblack@google.com    }
12712993Sgabeblack@google.com
12812839Sgabeblack@google.com    _disabled = true;
12912993Sgabeblack@google.com}
13012839Sgabeblack@google.com
13112839Sgabeblack@google.comvoid
13212839Sgabeblack@google.comProcess::enable(bool inc_kids)
13312839Sgabeblack@google.com{
13412993Sgabeblack@google.com
13512839Sgabeblack@google.com    if (inc_kids)
13612993Sgabeblack@google.com        forEachKid([](Process *p) { p->enable(true); });
13712839Sgabeblack@google.com
13812839Sgabeblack@google.com    _disabled = false;
13912839Sgabeblack@google.com}
14012993Sgabeblack@google.com
14112839Sgabeblack@google.comvoid
14212993Sgabeblack@google.comProcess::kill(bool inc_kids)
14312839Sgabeblack@google.com{
14412839Sgabeblack@google.com    if (::sc_core::sc_get_status() != ::sc_core::SC_RUNNING) {
14512839Sgabeblack@google.com        SC_REPORT_ERROR(sc_core::SC_ID_KILL_PROCESS_WHILE_UNITIALIZED_,
14612993Sgabeblack@google.com                name());
14712839Sgabeblack@google.com    }
14812993Sgabeblack@google.com
14912839Sgabeblack@google.com    // Propogate the kill to our children no matter what happens to us.
15012839Sgabeblack@google.com    if (inc_kids)
15112839Sgabeblack@google.com        forEachKid([](Process *p) { p->kill(true); });
15212993Sgabeblack@google.com
15312839Sgabeblack@google.com    // If we're in the middle of unwinding, ignore the kill request.
15412993Sgabeblack@google.com    if (_isUnwinding)
15512839Sgabeblack@google.com        return;
15612839Sgabeblack@google.com
15712839Sgabeblack@google.com    // Update our state.
15812993Sgabeblack@google.com    terminate();
15912839Sgabeblack@google.com    _isUnwinding = true;
16012993Sgabeblack@google.com
16112839Sgabeblack@google.com    // Make sure this process isn't marked ready
16212839Sgabeblack@google.com    popListNode();
16312839Sgabeblack@google.com
16412839Sgabeblack@google.com    // Inject the kill exception into this process if it's started.
16512839Sgabeblack@google.com    if (!_needsStart)
16612839Sgabeblack@google.com        injectException(killException);
16712839Sgabeblack@google.com}
16812839Sgabeblack@google.com
16912839Sgabeblack@google.comvoid
17012839Sgabeblack@google.comProcess::reset(bool inc_kids)
17112839Sgabeblack@google.com{
17212839Sgabeblack@google.com    if (::sc_core::sc_get_status() != ::sc_core::SC_RUNNING) {
17312839Sgabeblack@google.com        SC_REPORT_ERROR(sc_core::SC_ID_RESET_PROCESS_WHILE_NOT_RUNNING_,
17412839Sgabeblack@google.com                name());
17512839Sgabeblack@google.com    }
17612839Sgabeblack@google.com
17712839Sgabeblack@google.com    // Propogate the reset to our children no matter what happens to us.
17812839Sgabeblack@google.com    if (inc_kids)
17912839Sgabeblack@google.com        forEachKid([](Process *p) { p->reset(true); });
18012839Sgabeblack@google.com
18112839Sgabeblack@google.com    // If we're in the middle of unwinding, ignore the reset request.
18212839Sgabeblack@google.com    if (_isUnwinding)
18312839Sgabeblack@google.com        return;
18412839Sgabeblack@google.com
18512839Sgabeblack@google.com    // Clear suspended ready since we're about to run regardless.
18612839Sgabeblack@google.com    _suspendedReady = false;
18712839Sgabeblack@google.com
18812839Sgabeblack@google.com    _resetEvent.notify();
18912839Sgabeblack@google.com
19012839Sgabeblack@google.com    if (_needsStart) {
19112839Sgabeblack@google.com        scheduler.runNow(this);
19212839Sgabeblack@google.com    } else {
19312839Sgabeblack@google.com        _isUnwinding = true;
19412839Sgabeblack@google.com        injectException(resetException);
19512839Sgabeblack@google.com    }
19612839Sgabeblack@google.com}
19712839Sgabeblack@google.com
19812839Sgabeblack@google.comvoid
19912839Sgabeblack@google.comProcess::throw_it(ExceptionWrapperBase &exc, bool inc_kids)
20012839Sgabeblack@google.com{
20112839Sgabeblack@google.com    if (::sc_core::sc_get_status() != ::sc_core::SC_RUNNING)
20212839Sgabeblack@google.com        SC_REPORT_ERROR(sc_core::SC_ID_THROW_IT_WHILE_NOT_RUNNING_, name());
20312839Sgabeblack@google.com
20412839Sgabeblack@google.com    if (inc_kids)
20512839Sgabeblack@google.com        forEachKid([&exc](Process *p) { p->throw_it(exc, true); });
20612839Sgabeblack@google.com
20712839Sgabeblack@google.com    if (_needsStart || _terminated ||
20812839Sgabeblack@google.com            procKind() == ::sc_core::SC_METHOD_PROC_) {
20912839Sgabeblack@google.com        SC_REPORT_WARNING(sc_core::SC_ID_THROW_IT_IGNORED_, name());
21012839Sgabeblack@google.com        return;
21112839Sgabeblack@google.com    }
21212839Sgabeblack@google.com
21312839Sgabeblack@google.com    injectException(exc);
21412839Sgabeblack@google.com}
21512839Sgabeblack@google.com
21612839Sgabeblack@google.comvoid
21712839Sgabeblack@google.comProcess::injectException(ExceptionWrapperBase &exc)
21812839Sgabeblack@google.com{
21912839Sgabeblack@google.com    excWrapper = &exc;
22012839Sgabeblack@google.com    scheduler.runNow(this);
22112839Sgabeblack@google.com};
22212839Sgabeblack@google.com
22312839Sgabeblack@google.comvoid
22412839Sgabeblack@google.comProcess::syncResetOn(bool inc_kids)
22512839Sgabeblack@google.com{
22612839Sgabeblack@google.com    if (inc_kids)
22712839Sgabeblack@google.com        forEachKid([](Process *p) { p->syncResetOn(true); });
22812839Sgabeblack@google.com
22912839Sgabeblack@google.com    _syncReset = true;
23012839Sgabeblack@google.com}
23112839Sgabeblack@google.com
23212839Sgabeblack@google.comvoid
23312839Sgabeblack@google.comProcess::syncResetOff(bool inc_kids)
23412839Sgabeblack@google.com{
23512839Sgabeblack@google.com    if (inc_kids)
236        forEachKid([](Process *p) { p->syncResetOff(true); });
237
238    _syncReset = false;
239}
240
241void
242Process::signalReset(bool set, bool sync)
243{
244    if (set) {
245        waitCount(0);
246        if (sync) {
247            syncResetCount++;
248        } else {
249            asyncResetCount++;
250            cancelTimeout();
251            clearDynamic();
252            scheduler.runNext(this);
253        }
254    } else {
255        if (sync)
256            syncResetCount--;
257        else
258            asyncResetCount--;
259    }
260}
261
262void
263Process::run()
264{
265    bool reset;
266    do {
267        reset = false;
268        try {
269            func->call();
270        } catch(ScHalt) {
271            std::cout << "Terminating process " << name() << std::endl;
272        } catch(const ::sc_core::sc_unwind_exception &exc) {
273            reset = exc.is_reset();
274            _isUnwinding = false;
275        } catch (...) {
276            throw;
277        }
278    } while (reset);
279    needsStart(true);
280}
281
282void
283Process::addStatic(StaticSensitivity *s)
284{
285    staticSensitivities.push_back(s);
286}
287
288void
289Process::setDynamic(DynamicSensitivity *s)
290{
291    if (dynamicSensitivity) {
292        dynamicSensitivity->clear();
293        delete dynamicSensitivity;
294    }
295    dynamicSensitivity = s;
296}
297
298void
299Process::addReset(Reset *reset)
300{
301    resets.push_back(reset);
302}
303
304void
305Process::cancelTimeout()
306{
307    if (timeoutEvent.scheduled())
308        scheduler.deschedule(&timeoutEvent);
309}
310
311void
312Process::setTimeout(::sc_core::sc_time t)
313{
314    cancelTimeout();
315    scheduler.schedule(&timeoutEvent, t);
316}
317
318void
319Process::timeout()
320{
321    // A process is considered timed_out only if it was also waiting for an
322    // event but got a timeout instead.
323    _timedOut = (dynamicSensitivity != nullptr);
324
325    setDynamic(nullptr);
326    if (disabled())
327        return;
328
329    ready();
330}
331
332void
333Process::satisfySensitivity(Sensitivity *s)
334{
335    if (_waitCount) {
336        _waitCount--;
337        return;
338    }
339
340    // If there's a dynamic sensitivity and this wasn't it, ignore.
341    if ((dynamicSensitivity || timeoutEvent.scheduled()) &&
342            dynamicSensitivity != s) {
343        return;
344    }
345
346    _timedOut = false;
347    // This sensitivity should already be cleared by this point, or the event
348    // which triggered it will take care of it.
349    delete dynamicSensitivity;
350    dynamicSensitivity = nullptr;
351    cancelTimeout();
352    ready();
353}
354
355void
356Process::ready()
357{
358    if (disabled())
359        return;
360    if (suspended())
361        _suspendedReady = true;
362    else if (!scheduled())
363        scheduler.ready(this);
364}
365
366void
367Process::lastReport(::sc_core::sc_report *report)
368{
369    if (report) {
370        _lastReport = std::unique_ptr<::sc_core::sc_report>(
371                new ::sc_core::sc_report(*report));
372    } else {
373        _lastReport = nullptr;
374    }
375}
376
377::sc_core::sc_report *Process::lastReport() const { return _lastReport.get(); }
378
379Process::Process(const char *name, ProcessFuncWrapper *func, bool internal) :
380    ::sc_core::sc_process_b(name), excWrapper(nullptr),
381    timeoutEvent([this]() { this->timeout(); }),
382    func(func), _internal(internal), _timedOut(false), _dontInitialize(false),
383    _needsStart(true), _isUnwinding(false), _terminated(false),
384    _scheduled(false), _suspended(false), _disabled(false),
385    _syncReset(false), syncResetCount(0), asyncResetCount(0), _waitCount(0),
386    refCount(0), stackSize(::Fiber::DefaultStackSize),
387    dynamicSensitivity(nullptr)
388{
389    _dynamic =
390            (::sc_core::sc_get_status() >
391             ::sc_core::SC_BEFORE_END_OF_ELABORATION);
392    _newest = this;
393}
394
395void
396Process::terminate()
397{
398    _terminated = true;
399    _suspendedReady = false;
400    _suspended = false;
401    _syncReset = false;
402    clearDynamic();
403    cancelTimeout();
404    for (auto s: staticSensitivities) {
405        s->clear();
406        delete s;
407    }
408    staticSensitivities.clear();
409
410    _terminatedEvent.notify();
411
412    for (auto jw: joinWaiters)
413        jw->signal();
414    joinWaiters.clear();
415}
416
417Process *Process::_newest;
418
419void
420throw_it_wrapper(Process *p, ExceptionWrapperBase &exc, bool inc_kids)
421{
422    p->throw_it(exc, inc_kids);
423}
424
425void
426newReset(const sc_core::sc_port_base *pb, Process *p, bool s, bool v)
427{
428    Port *port = Port::fromPort(pb);
429    port->addReset(new Reset(p, s, v));
430}
431
432void
433newReset(const sc_core::sc_signal_in_if<bool> *sig, Process *p, bool s, bool v)
434{
435    Reset *reset = new Reset(p, s, v);
436    if (!reset->install(sig))
437        delete reset;
438}
439
440} // namespace sc_gem5
441