QP/C++ Real-Time Embedded Framework (RTEF) The model is used to generate the whole QP/C++ source code. Copyright (C) 2005 Quantum Leaps, LLC <state-machine.com>. SPDX-License-Identifier: GPL-3.0-or-later OR LicenseRef-QL-commercial This software is dual-licensed under the terms of the open source GNU General Public License version 3 (or any later version), or alternatively, under the terms of one of the closed source Quantum Leaps commercial licenses. The terms of the open source GNU General Public License version 3 can be found at: <www.gnu.org/licenses/gpl-3.0> The terms of the closed source Quantum Leaps commercial licenses can be found at: <www.state-machine.com/licensing> Redistributions in source code must retain this copyright notice. Plagiarizing this software to sidestep the license obligations is illegal. Contact information: <www.state-machine.com/licensing> <info@state-machine.com> = int; = int; = float; = double; {QP_VERSION_STR}; = std::uint8_t; = std::uint16_t; = std::uint32_t; {0xE0U}; : std::uint8_t { DYNAMIC }; noexcept Q_UNUSED_PAR(dummy); return this; noexcept : sig(s), refCtr_(0U), evtTag_(MARKER) = delete noexcept return (e != nullptr) && ((e->evtTag_ & 0xF0U) == MARKER); const noexcept return static_cast<std::uint8_t>(evtTag_) & 0x0FU; = std::uint_fast8_t; = QState (*)(void * const me, QEvt const * const e); = QState (*)(void * const me); // forward declaration = void (*)(QXThread * const me); { QMState const * superstate; QStateHandler const stateHandler; QActionHandler const entryAction; QActionHandler const exitAction; QActionHandler const initAction; }; { QMState const * target; QActionHandler const act[1]; }; { QStateHandler fun; QActionHandler act; QXThreadHandler thr; QMState const *obj; QMTranActTable const *tatbl; #ifndef Q_UNSAFE std::uintptr_t uint; #endif constexpr QAsmAttr() : fun(nullptr) {} }; {4}; Abstract State Machine //! All possible return values from state-handlers //! @note //! The order is important for algorithmic correctness. : QState { // unhandled and need to "bubble up" Q_RET_SUPER, //!< event passed to superstate to handle Q_RET_SUPER_SUB, //!< event passed to submachine superstate Q_RET_UNHANDLED, //!< event unhandled due to a guard // handled and do not need to "bubble up" Q_RET_HANDLED, //!< event handled (internal transition) Q_RET_IGNORED, //!< event silently ignored (bubbled up to top) // entry/exit Q_RET_ENTRY, //!< state entry action executed Q_RET_EXIT, //!< state exit action executed // no side effects Q_RET_NULL, //!< return value without any effect // transitions need to execute transition-action table in QP::QMsm Q_RET_TRAN, //!< regular transition Q_RET_TRAN_INIT, //!< initial transition in a state or submachine Q_RET_TRAN_EP, //!< entry-point transition into a submachine // transitions that additionally clobber QHsm.m_state Q_RET_TRAN_HIST, //!< transition to history of a given state Q_RET_TRAN_XP //!< exit-point transition out of a submachine }; //! Reserved signals by the QP-framework. : QSignal { Q_EMPTY_SIG, //!< signal to execute the default case Q_ENTRY_SIG, //!< signal for entry actions Q_EXIT_SIG, //!< signal for exit actions Q_INIT_SIG //!< signal for nested initial transitions }; noexcept : m_state(), m_temp () noexcept // empty = 0 this->init(nullptr, qsId); = 0 noexcept static_cast<void>(state); return false; const noexcept return m_state.fun; const noexcept return m_state.obj; noexcept return m_state.fun; noexcept static_cast<void>(me); static_cast<void>(e); return Q_RET_IGNORED; // the top state ignores all events noexcept m_temp.fun = target; return Q_RET_TRAN; noexcept m_temp.fun = hist; return Q_RET_TRAN_HIST; noexcept m_temp.fun = superstate; return Q_RET_SUPER; noexcept m_temp.tatbl = static_cast<QP::QMTranActTable const *>(tatbl); return Q_RET_TRAN; noexcept m_temp.tatbl = static_cast<QP::QMTranActTable const *>(tatbl); return Q_RET_TRAN_INIT; noexcept m_state.obj = hist; m_temp.tatbl = static_cast<QP::QMTranActTable const *>(tatbl); return Q_RET_TRAN_HIST; noexcept m_temp.tatbl = static_cast<QP::QMTranActTable const *>(tatbl); return Q_RET_TRAN_EP; noexcept m_state.act = xp; m_temp.tatbl = static_cast<QP::QMTranActTable const *>(tatbl); return Q_RET_TRAN_XP; noexcept m_temp.obj = s; return Q_RET_ENTRY; noexcept static_cast<void>(s); // unused parameter return Q_RET_ENTRY; noexcept m_temp.obj = s; return Q_RET_EXIT; noexcept static_cast<void>(s); // unused parameter return Q_RET_EXIT; noexcept m_temp.obj = s; return Q_RET_EXIT; noexcept m_temp.obj = s; return Q_RET_SUPER_SUB; Human-generated State Machine {6}; noexcept : QAsm() m_state.fun = Q_STATE_CAST(&top); m_temp.fun = initial; override QF_CRIT_STAT #ifdef Q_SPY QS_CRIT_ENTRY(); QS_MEM_SYS(); if ((QS::priv_.flags & 0x01U) == 0U) { QS::priv_.flags |= 0x01U; QS_MEM_APP(); QS_CRIT_EXIT(); QS_FUN_DICTIONARY(&QP::QHsm::top); } else { QS_MEM_APP(); QS_CRIT_EXIT(); } #else Q_UNUSED_PAR(qsId); #endif QStateHandler t = m_state.fun; QF_CRIT_ENTRY(); Q_REQUIRE_INCRIT(200, (m_temp.fun != nullptr) && (t == Q_STATE_CAST(&top))); QF_CRIT_EXIT(); // execute the top-most initial tran. QState r = (*m_temp.fun)(this, Q_EVT_CAST(QEvt)); QF_CRIT_ENTRY(); // the top-most initial tran. must be taken Q_ASSERT_INCRIT(210, r == Q_RET_TRAN); QS_MEM_SYS(); QS_BEGIN_PRE_(QS_QEP_STATE_INIT, qsId) QS_OBJ_PRE_(this); // this state machine object QS_FUN_PRE_(t); // the source state QS_FUN_PRE_(m_temp.fun); // the target of the initial tran. QS_END_PRE_() QS_MEM_APP(); QF_CRIT_EXIT(); // drill down into the state hierarchy with initial transitions... std::int_fast8_t limit = MAX_NEST_DEPTH_; // loop hard limit do { QStateHandler path[MAX_NEST_DEPTH_]; // tran entry path array std::int_fast8_t ip = 0; // tran entry path index path[0] = m_temp.fun; static_cast<void>(QHSM_RESERVED_EVT_(m_temp.fun, Q_EMPTY_SIG)); while (m_temp.fun != t) { ++ip; path[ip] = m_temp.fun; static_cast<void>(QHSM_RESERVED_EVT_(m_temp.fun, Q_EMPTY_SIG)); } QF_CRIT_ENTRY(); // The initial transition source state must be reached // Too many state nesting levels or "malformed" HSM. Q_ASSERT_INCRIT(220, m_temp.fun == t); QF_CRIT_EXIT(); m_temp.fun = path[0]; // retrace the entry path in reverse (desired) order... do { // enter path[ip] if (QHSM_RESERVED_EVT_(path[ip], Q_ENTRY_SIG) == Q_RET_HANDLED) { QS_STATE_ENTRY_(path[ip], qsId); } --ip; } while (ip >= 0); t = path[0]; // current state becomes the new source r = QHSM_RESERVED_EVT_(t, Q_INIT_SIG); // execute initial tran. #ifdef Q_SPY if (r == Q_RET_TRAN) { QS_CRIT_ENTRY(); QS_MEM_SYS(); QS_BEGIN_PRE_(QS_QEP_STATE_INIT, qsId) QS_OBJ_PRE_(this); // this state machine object QS_FUN_PRE_(t); // the source state QS_FUN_PRE_(m_temp.fun); // the target of the initial tran. QS_END_PRE_() QS_MEM_APP(); QS_CRIT_EXIT(); } #endif // Q_SPY --limit; } while ((r == Q_RET_TRAN) && (limit > 0)); QF_CRIT_ENTRY(); // Loop limit must not be reached. // Too many state nesting levels or likely "malformed" HSM Q_ENSURE_INCRIT(290, limit > 0); QS_MEM_SYS(); QS_BEGIN_PRE_(QS_QEP_INIT_TRAN, qsId) QS_TIME_PRE_(); // time stamp QS_OBJ_PRE_(this); // this state machine object QS_FUN_PRE_(t); // the new active state QS_END_PRE_() QS_MEM_APP(); QF_CRIT_EXIT(); m_state.fun = t; // change the current active state #ifndef Q_UNSAFE m_temp.uint = ~m_state.uint; #endif override this->init(nullptr, qsId); override #ifndef Q_SPY Q_UNUSED_PAR(qsId); #endif QStateHandler s = m_state.fun; QStateHandler t = s; QF_CRIT_STAT QF_CRIT_ENTRY(); Q_REQUIRE_INCRIT(300, (s != Q_STATE_CAST(0)) && (m_state.uint == static_cast<std::uintptr_t>(~m_temp.uint))); Q_REQUIRE_INCRIT(302, QEvt::verify_(e)); QS_MEM_SYS(); QS_BEGIN_PRE_(QS_QEP_DISPATCH, qsId) QS_TIME_PRE_(); // time stamp QS_SIG_PRE_(e->sig); // the signal of the event QS_OBJ_PRE_(this); // this state machine object QS_FUN_PRE_(s); // the current state QS_END_PRE_() QS_MEM_APP(); QF_CRIT_EXIT(); // process the event hierarchically... QState r; m_temp.fun = s; std::int_fast8_t limit = MAX_NEST_DEPTH_; // loop hard limit do { s = m_temp.fun; r = (*s)(this, e); // invoke state handler s if (r == Q_RET_UNHANDLED) { // unhandled due to a guard? QS_CRIT_ENTRY(); QS_MEM_SYS(); QS_BEGIN_PRE_(QS_QEP_UNHANDLED, qsId) QS_SIG_PRE_(e->sig); // the signal of the event QS_OBJ_PRE_(this); // this state machine object QS_FUN_PRE_(s); // the current state QS_END_PRE_() QS_MEM_APP(); QS_CRIT_EXIT(); r = QHSM_RESERVED_EVT_(s, Q_EMPTY_SIG); // superstate of s } --limit; } while ((r == Q_RET_SUPER) && (limit > 0)); QF_CRIT_ENTRY(); Q_ASSERT_INCRIT(310, limit > 0); QF_CRIT_EXIT(); if (r >= Q_RET_TRAN) { // regular tran. taken? QStateHandler path[MAX_NEST_DEPTH_]; path[0] = m_temp.fun; // tran. target path[1] = t; // current state path[2] = s; // tran. source // exit current state to tran. source s... limit = MAX_NEST_DEPTH_; // loop hard limit for (; (t != s) && (limit > 0); t = m_temp.fun) { // exit from t if (QHSM_RESERVED_EVT_(t, Q_EXIT_SIG) == Q_RET_HANDLED) { QS_STATE_EXIT_(t, qsId); // find superstate of t static_cast<void>(QHSM_RESERVED_EVT_(t, Q_EMPTY_SIG)); } --limit; } QF_CRIT_ENTRY(); Q_ASSERT_INCRIT(320, limit > 0); QF_CRIT_EXIT(); std::int_fast8_t ip = hsm_tran(path, qsId); // take the tran. #ifdef Q_SPY if (r == Q_RET_TRAN_HIST) { QS_CRIT_ENTRY(); QS_MEM_SYS(); QS_BEGIN_PRE_(QS_QEP_TRAN_HIST, qsId) QS_OBJ_PRE_(this); // this state machine object QS_FUN_PRE_(t); // the source of the transition QS_FUN_PRE_(path[0]); // the target of the tran. to history QS_END_PRE_() QS_MEM_APP(); QS_CRIT_EXIT(); } #endif // Q_SPY // execute state entry actions in the desired order... for (; ip >= 0; --ip) { // enter path[ip] if (QHSM_RESERVED_EVT_(path[ip], Q_ENTRY_SIG) == Q_RET_HANDLED) { QS_STATE_ENTRY_(path[ip], qsId); } } t = path[0]; // stick the target into register m_temp.fun = t; // update the next state // drill into the target hierarchy... while (QHSM_RESERVED_EVT_(t, Q_INIT_SIG) == Q_RET_TRAN) { QS_CRIT_ENTRY(); QS_MEM_SYS(); QS_BEGIN_PRE_(QS_QEP_STATE_INIT, qsId) QS_OBJ_PRE_(this); // this state machine object QS_FUN_PRE_(t); // the source (pseudo)state QS_FUN_PRE_(m_temp.fun); // the target of the tran. QS_END_PRE_() QS_MEM_APP(); QS_CRIT_EXIT(); ip = 0; path[0] = m_temp.fun; // find superstate static_cast<void>(QHSM_RESERVED_EVT_(m_temp.fun, Q_EMPTY_SIG)); while ((m_temp.fun != t) && (ip < (MAX_NEST_DEPTH_ - 1))) { ++ip; path[ip] = m_temp.fun; // find superstate static_cast<void>( QHSM_RESERVED_EVT_(m_temp.fun, Q_EMPTY_SIG)); } QF_CRIT_ENTRY(); // The initial transition source state must be reached. // Too many state nesting levels or "malformed" HSM. Q_ASSERT_INCRIT(330, m_temp.fun == t); QF_CRIT_EXIT(); m_temp.fun = path[0]; // retrace the entry path in reverse (correct) order... do { // enter path[ip] if (QHSM_RESERVED_EVT_(path[ip], Q_ENTRY_SIG) == Q_RET_HANDLED) { QS_STATE_ENTRY_(path[ip], qsId); } --ip; } while (ip >= 0); t = path[0]; // current state becomes the new source } QS_CRIT_ENTRY(); QS_MEM_SYS(); QS_BEGIN_PRE_(QS_QEP_TRAN, qsId) QS_TIME_PRE_(); // time stamp QS_SIG_PRE_(e->sig); // the signal of the event QS_OBJ_PRE_(this); // this state machine object QS_FUN_PRE_(s); // the source of the tran. QS_FUN_PRE_(t); // the new active state QS_END_PRE_() QS_MEM_APP(); QS_CRIT_EXIT(); } #ifdef Q_SPY else if (r == Q_RET_HANDLED) { QS_CRIT_ENTRY(); QS_MEM_SYS(); QS_BEGIN_PRE_(QS_QEP_INTERN_TRAN, qsId) QS_TIME_PRE_(); // time stamp QS_SIG_PRE_(e->sig); // the signal of the event QS_OBJ_PRE_(this); // this state machine object QS_FUN_PRE_(s); // the source state QS_END_PRE_() QS_MEM_APP(); QS_CRIT_EXIT(); } else { QS_CRIT_ENTRY(); QS_MEM_SYS(); QS_BEGIN_PRE_(QS_QEP_IGNORED, qsId) QS_TIME_PRE_(); // time stamp QS_SIG_PRE_(e->sig); // the signal of the event QS_OBJ_PRE_(this); // this state machine object QS_FUN_PRE_(m_state.fun); // the current state QS_END_PRE_() QS_MEM_APP(); QS_CRIT_EXIT(); } #endif // Q_SPY m_state.fun = t; // change the current active state #ifndef Q_UNSAFE m_temp.uint = ~m_state.uint; #endif noexcept override QF_CRIT_STAT QF_CRIT_ENTRY(); Q_REQUIRE_INCRIT(602, m_state.uint == static_cast<std::uintptr_t>(~m_temp.uint)); QF_CRIT_EXIT(); bool inState = false; // assume that this HSM is not in 'state' // scan the state hierarchy bottom-up QStateHandler s = m_state.fun; std::int_fast8_t limit = MAX_NEST_DEPTH_ + 1; // loop hard limit QState r = Q_RET_SUPER; for (; (r != Q_RET_IGNORED) && (limit > 0); --limit) { if (s == state) { // do the states match? inState = true; // 'true' means that match found break; // break out of the for-loop } else { r = QHSM_RESERVED_EVT_(s, Q_EMPTY_SIG); s = m_temp.fun; } } QF_CRIT_ENTRY(); Q_ENSURE_INCRIT(690, limit > 0); QF_CRIT_EXIT(); #ifndef Q_UNSAFE m_temp.uint = ~m_state.uint; #endif return inState; // return the status noexcept QStateHandler child = m_state.fun; // start with the current state bool isFound = false; // start with the child not found // establish stable state configuration m_temp.fun = child; QState r; do { // is this the parent of the current child? if (m_temp.fun == parent) { isFound = true; // child is found r = Q_RET_IGNORED; // break out of the loop } else { child = m_temp.fun; r = QHSM_RESERVED_EVT_(m_temp.fun, Q_EMPTY_SIG); } } while (r != Q_RET_IGNORED); // QHsm::top() state not reached #ifndef Q_UNSAFE m_temp.uint = ~m_state.uint; #endif QF_CRIT_STAT QF_CRIT_ENTRY(); Q_ASSERT_INCRIT(890, isFound); QF_CRIT_EXIT(); return child; // return the child noexcept override return m_state.fun; #ifndef Q_SPY Q_UNUSED_PAR(qsId); #endif std::int_fast8_t ip = -1; // tran. entry path index QStateHandler t = path[0]; QStateHandler const s = path[2]; QF_CRIT_STAT // (a) check source==target (tran. to self)... if (s == t) { // exit source s if (QHSM_RESERVED_EVT_(s, Q_EXIT_SIG) == Q_RET_HANDLED) { QS_STATE_EXIT_(s, qsId); } ip = 0; // enter the target } else { // find superstate of target static_cast<void>(QHSM_RESERVED_EVT_(t, Q_EMPTY_SIG)); t = m_temp.fun; // (b) check source==target->super... if (s == t) { ip = 0; // enter the target } else { // find superstate of src static_cast<void>(QHSM_RESERVED_EVT_(s, Q_EMPTY_SIG)); // (c) check source->super==target->super... if (m_temp.fun == t) { // exit source s if (QHSM_RESERVED_EVT_(s, Q_EXIT_SIG) == Q_RET_HANDLED) { QS_STATE_EXIT_(s, qsId); } ip = 0; // enter the target } else { // (d) check source->super==target... if (m_temp.fun == path[0]) { // exit source s if (QHSM_RESERVED_EVT_(s, Q_EXIT_SIG) == Q_RET_HANDLED) { QS_STATE_EXIT_(s, qsId); } } else { // (e) check rest of source==target->super->super.. // and store the entry path along the way std::int_fast8_t iq = 0; // indicate that LCA was found ip = 1; // enter target and its superstate path[1] = t; // save the superstate of target t = m_temp.fun; // save source->super // find target->super->super... QState r = QHSM_RESERVED_EVT_(path[1], Q_EMPTY_SIG); while ((r == Q_RET_SUPER) && (ip < (MAX_NEST_DEPTH_ - 1))) { ++ip; path[ip] = m_temp.fun; // store the entry path if (m_temp.fun == s) { // is it the source? iq = 1; // indicate that the LCA found --ip; // do not enter the source r = Q_RET_HANDLED; // terminate the loop } else { // it is not the source, keep going up r = QHSM_RESERVED_EVT_(m_temp.fun, Q_EMPTY_SIG); } } QF_CRIT_ENTRY(); // Tran. source must be found within the nesting depth // Too many state nesting levels or "malformed" HSM. Q_ASSERT_INCRIT(510, r != Q_RET_SUPER); QF_CRIT_EXIT(); // the LCA not found yet? if (iq == 0) { // exit source s if (QHSM_RESERVED_EVT_(s, Q_EXIT_SIG) == Q_RET_HANDLED) { QS_STATE_EXIT_(s, qsId); } // (f) check the rest of source->super // == target->super->super... iq = ip; r = Q_RET_IGNORED; // indicate that the LCA NOT found do { if (t == path[iq]) { // is this the LCA? r = Q_RET_HANDLED; // indicate the LCA found ip = iq - 1; // do not enter the LCA iq = -1; // cause termination of the loop } else { --iq; // try lower superstate of target } } while (iq >= 0); // the LCA not found yet? if (r != Q_RET_HANDLED) { // (g) check each source->super->... // for each target->super... r = Q_RET_IGNORED; // keep looping std::int_fast8_t limit = MAX_NEST_DEPTH_; do { // exit from t if (QHSM_RESERVED_EVT_(t, Q_EXIT_SIG) == Q_RET_HANDLED) { QS_STATE_EXIT_(t, qsId); // find superstate of t static_cast<void>( QHSM_RESERVED_EVT_(t, Q_EMPTY_SIG)); } t = m_temp.fun; // set to super of t iq = ip; do { // is this the LCA? if (t == path[iq]) { ip = iq - 1; // do not enter the LCA iq = -1; // break out of inner loop r = Q_RET_HANDLED; // break outer loop } else { --iq; } } while (iq >= 0); --limit; } while ((r != Q_RET_HANDLED) && (limit > 0)); QF_CRIT_ENTRY(); Q_ASSERT_INCRIT(530, limit > 0); QF_CRIT_EXIT(); } } } } } } QF_CRIT_ENTRY(); Q_ENSURE_INCRIT(590, ip < MAX_NEST_DEPTH_); QF_CRIT_EXIT(); return ip; Machine-generated State Machine noexcept : QAsm() m_state.obj = &l_msm_top_s; // the current state (top) m_temp.fun = initial; // the initial tran. handler override #ifndef Q_SPY Q_UNUSED_PAR(qsId); #endif QF_CRIT_STAT QF_CRIT_ENTRY(); Q_REQUIRE_INCRIT(200, (m_temp.fun != nullptr) && (m_state.obj == &l_msm_top_s)); QF_CRIT_EXIT(); // execute the top-most initial tran. QState r = (*m_temp.fun)(this, Q_EVT_CAST(QEvt)); QF_CRIT_ENTRY(); // the top-most initial tran. must be taken Q_ASSERT_INCRIT(210, r == Q_RET_TRAN_INIT); QS_MEM_SYS(); QS_BEGIN_PRE_(QS_QEP_STATE_INIT, qsId) QS_OBJ_PRE_(this); // this state machine object QS_FUN_PRE_(m_state.obj->stateHandler); // source state QS_FUN_PRE_(m_temp.tatbl->target->stateHandler); // target state QS_END_PRE_() QS_MEM_APP(); QF_CRIT_EXIT(); // set state to the last tran. target m_state.obj = m_temp.tatbl->target; // drill down into the state hierarchy with initial transitions... std::int_fast8_t limit = MAX_NEST_DEPTH_; // loop hard limit do { // execute the tran. table r = execTatbl_(m_temp.tatbl, qsId); --limit; } while ((r >= Q_RET_TRAN_INIT) && (limit > 0)); QF_CRIT_ENTRY(); Q_ENSURE_INCRIT(290, limit > 0); QS_MEM_SYS(); QS_BEGIN_PRE_(QS_QEP_INIT_TRAN, qsId) QS_TIME_PRE_(); // time stamp QS_OBJ_PRE_(this); // this state machine object QS_FUN_PRE_(m_state.obj->stateHandler); // the new current state QS_END_PRE_() QS_MEM_APP(); QF_CRIT_EXIT(); #ifndef Q_UNSAFE m_temp.uint = ~m_state.uint; #endif override this->init(nullptr, qsId); override #ifndef Q_SPY Q_UNUSED_PAR(qsId); #endif QMState const *s = m_state.obj; // store the current state QMState const *t = s; QF_CRIT_STAT QF_CRIT_ENTRY(); Q_REQUIRE_INCRIT(300, (s != nullptr) && (m_state.uint == static_cast<std::uintptr_t>(~m_temp.uint))); Q_REQUIRE_INCRIT(302, QEvt::verify_(e)); QS_MEM_SYS(); QS_BEGIN_PRE_(QS_QEP_DISPATCH, qsId) QS_TIME_PRE_(); // time stamp QS_SIG_PRE_(e->sig); // the signal of the event QS_OBJ_PRE_(this); // this state machine object QS_FUN_PRE_(s->stateHandler); // the current state handler QS_END_PRE_() QS_MEM_APP(); QF_CRIT_EXIT(); // scan the state hierarchy up to the top state... QState r; std::int_fast8_t limit = MAX_NEST_DEPTH_; // loop hard limit do { r = (*t->stateHandler)(this, e); // call state handler function // event handled? (the most frequent case) if (r >= Q_RET_HANDLED) { break; // done scanning the state hierarchy } // event unhandled and passed to the superstate? else if (r == Q_RET_SUPER) { t = t->superstate; // advance to the superstate } // event unhandled and passed to a submachine superstate? else if (r == Q_RET_SUPER_SUB) { t = m_temp.obj; // current host state of the submachie } else { // event unhandled due to a guard? QF_CRIT_ENTRY(); // event must be unhandled due to a guard evaluating to 'false' Q_ASSERT_INCRIT(310, r == Q_RET_UNHANDLED); QS_MEM_SYS(); QS_BEGIN_PRE_(QS_QEP_UNHANDLED, qsId) QS_SIG_PRE_(e->sig); // the signal of the event QS_OBJ_PRE_(this); // this state machine object QS_FUN_PRE_(t->stateHandler); // the current state QS_END_PRE_() QS_MEM_APP(); QF_CRIT_EXIT(); t = t->superstate; // advance to the superstate } --limit; } while ((t != nullptr) && (limit > 0)); QF_CRIT_ENTRY(); Q_ASSERT_INCRIT(310, limit > 0); QF_CRIT_EXIT(); if (r >= Q_RET_TRAN) { // any kind of tran. taken? #ifdef Q_SPY QMState const * const ts = t; // tran. source for QS tracing QF_CRIT_ENTRY(); // the tran. source state must not be nullptr Q_ASSERT_INCRIT(320, ts != nullptr); QF_CRIT_EXIT(); #endif // Q_SPY limit = MAX_NEST_DEPTH_; // loop hard limit do { // save the tran-action table before it gets clobbered QMTranActTable const * const tatbl = m_temp.tatbl; QAsmAttr tmp; // temporary to save intermediate values // was TRAN, TRAN_INIT, or TRAN_EP taken? if (r <= Q_RET_TRAN_EP) { m_temp.obj = nullptr; // clear exitToTranSource_(s, t, qsId); r = execTatbl_(tatbl, qsId); s = m_state.obj; } // was a tran. segment to history taken? else if (r == Q_RET_TRAN_HIST) { tmp.obj = m_state.obj; // save history m_state.obj = s; // restore the original state exitToTranSource_(s, t, qsId); static_cast<void>(execTatbl_(tatbl, qsId)); r = enterHistory_(tmp.obj, qsId); s = m_state.obj; } else { QF_CRIT_ENTRY(); // must be tran. to exit point Q_ASSERT_INCRIT(340, r == Q_RET_TRAN_XP); QF_CRIT_EXIT(); tmp.act = m_state.act; // save XP action m_state.obj = s; // restore the original state r = (*tmp.act)(this); // execute the XP action if (r == Q_RET_TRAN) { // XP -> TRAN ? #ifdef Q_SPY tmp.tatbl = m_temp.tatbl; // save m_temp #endif // Q_SPY exitToTranSource_(s, t, qsId); // take the tran-to-XP segment inside submachine static_cast<void>(execTatbl_(tatbl, qsId)); s = m_state.obj; #ifdef Q_SPY m_temp.tatbl = tmp.tatbl; // restore m_temp #endif // Q_SPY } else if (r == Q_RET_TRAN_HIST) { // XP -> HIST ? tmp.obj = m_state.obj; // save the history m_state.obj = s; // restore the original state #ifdef Q_SPY s = m_temp.obj; // save m_temp #endif // Q_SPY exitToTranSource_(m_state.obj, t, qsId); // take the tran-to-XP segment inside submachine static_cast<void>(execTatbl_(tatbl, qsId)); #ifdef Q_SPY m_temp.obj = s; // restore m_temp #endif // Q_SPY s = m_state.obj; m_state.obj = tmp.obj; // restore the history } else { QF_CRIT_ENTRY(); // TRAN_XP must NOT be followed by any other tran type Q_ASSERT_INCRIT(330, r < Q_RET_TRAN); QF_CRIT_EXIT(); } } t = s; // set target to the current state --limit; } while ((r >= Q_RET_TRAN) && (limit > 0)); QF_CRIT_ENTRY(); Q_ASSERT_INCRIT(320, limit > 0); QS_MEM_SYS(); QS_BEGIN_PRE_(QS_QEP_TRAN, qsId) QS_TIME_PRE_(); // time stamp QS_SIG_PRE_(e->sig); // the signal of the event QS_OBJ_PRE_(this); // this state machine object QS_FUN_PRE_(ts->stateHandler); // the tran. source QS_FUN_PRE_(s->stateHandler); // the new active state QS_END_PRE_() QS_MEM_APP(); QF_CRIT_EXIT(); } #ifdef Q_SPY // was the event handled? else if (r == Q_RET_HANDLED) { QF_CRIT_ENTRY(); // internal tran. source can't be nullptr Q_ASSERT_INCRIT(340, t != nullptr); QS_MEM_SYS(); QS_BEGIN_PRE_(QS_QEP_INTERN_TRAN, qsId) QS_TIME_PRE_(); // time stamp QS_SIG_PRE_(e->sig); // the signal of the event QS_OBJ_PRE_(this); // this state machine object QS_FUN_PRE_(t->stateHandler); // the source state QS_END_PRE_() QS_MEM_APP(); QF_CRIT_EXIT(); } // event bubbled to the 'top' state? else if (t == nullptr) { QS_CRIT_ENTRY(); QS_MEM_SYS(); QS_BEGIN_PRE_(QS_QEP_IGNORED, qsId) QS_TIME_PRE_(); // time stamp QS_SIG_PRE_(e->sig); // the signal of the event QS_OBJ_PRE_(this); // this state machine object QS_FUN_PRE_(s->stateHandler); // the current state QS_END_PRE_() QS_MEM_APP(); QS_CRIT_EXIT(); } #endif // Q_SPY else { // empty } #ifndef Q_UNSAFE m_temp.uint = ~m_state.uint; #endif noexcept override return m_state.obj->stateHandler; noexcept override bool inState = false; // assume that this SM is not in 'state' QMState const *s = m_state.obj; std::int_fast8_t limit = MAX_NEST_DEPTH_; // loop hard limit for (; (s != nullptr) && (limit > 0); --limit) { if (s->stateHandler == state) { // match found? inState = true; break; } else { s = s->superstate; // advance to the superstate } } QF_CRIT_STAT QF_CRIT_ENTRY(); Q_ENSURE_INCRIT(490, limit > 0); QF_CRIT_EXIT(); return inState; const noexcept //! @deprecated instead use: QMsm::isIn() bool inState = false; // assume that this SM is not in 'state' QMState const *s = m_state.obj; std::int_fast8_t limit = MAX_NEST_DEPTH_; // loop hard limit for (; (s != nullptr) && (limit > 0); --limit) { if (s == stateObj) { // match found? inState = true; break; } else { s = s->superstate; // advance to the superstate } } QF_CRIT_STAT QF_CRIT_ENTRY(); Q_ENSURE_INCRIT(590, limit > 0); QF_CRIT_EXIT(); return inState; const noexcept QMState const *child = m_state.obj; bool isFound = false; // start with the child not found QMState const *s; std::int_fast8_t limit = MAX_NEST_DEPTH_; // loop hard limit for (s = m_state.obj; (s != nullptr) && (limit > 0); s = s->superstate) { if (s == parent) { isFound = true; // child is found break; } else { child = s; } --limit; } QF_CRIT_STAT QF_CRIT_ENTRY(); Q_ASSERT_INCRIT(610, limit > 0); QF_CRIT_EXIT(); if (!isFound) { // still not found? limit = MAX_NEST_DEPTH_; // loop hard limit for (s = m_temp.obj; (s != nullptr) && (limit > 0); s = s->superstate) { if (s == parent) { isFound = true; // child is found break; } else { child = s; } --limit; } } QF_CRIT_ENTRY(); Q_ENSURE_INCRIT(690, isFound && (limit > 0)); QF_CRIT_EXIT(); return child; // return the child #ifndef Q_SPY Q_UNUSED_PAR(qsId); #endif QF_CRIT_STAT QF_CRIT_ENTRY(); // precondition: // - the tran-action table pointer must not be NULL Q_REQUIRE_INCRIT(700, tatbl != nullptr); QF_CRIT_EXIT(); QState r = Q_RET_NULL; std::int_fast8_t limit = MAX_TRAN_LENGTH_; // loop hard limit QActionHandler const *a = &tatbl->act[0]; for (; (*a != nullptr) && (limit > 0); ++a) { r = (*(*a))(this); // call the action through the 'a' pointer #ifdef Q_SPY QS_CRIT_ENTRY(); QS_MEM_SYS(); if (r == Q_RET_ENTRY) { QS_BEGIN_PRE_(QS_QEP_STATE_ENTRY, qsId) QS_OBJ_PRE_(this); // this state machine object QS_FUN_PRE_(m_temp.obj->stateHandler); // entered state QS_END_PRE_() } else if (r == Q_RET_EXIT) { QS_BEGIN_PRE_(QS_QEP_STATE_EXIT, qsId) QS_OBJ_PRE_(this); // this state machine object QS_FUN_PRE_(m_temp.obj->stateHandler); // exited state QS_END_PRE_() } else if (r == Q_RET_TRAN_INIT) { QS_BEGIN_PRE_(QS_QEP_STATE_INIT, qsId) QS_OBJ_PRE_(this); // this state machine object QS_FUN_PRE_(tatbl->target->stateHandler); // source QS_FUN_PRE_(m_temp.tatbl->target->stateHandler); // target QS_END_PRE_() } else if (r == Q_RET_TRAN_EP) { QS_BEGIN_PRE_(QS_QEP_TRAN_EP, qsId) QS_OBJ_PRE_(this); // this state machine object QS_FUN_PRE_(tatbl->target->stateHandler); // source QS_FUN_PRE_(m_temp.tatbl->target->stateHandler); // target QS_END_PRE_() } else if (r == Q_RET_TRAN_XP) { QS_BEGIN_PRE_(QS_QEP_TRAN_XP, qsId) QS_OBJ_PRE_(this); // this state machine object QS_FUN_PRE_(tatbl->target->stateHandler); // source QS_FUN_PRE_(m_temp.tatbl->target->stateHandler); // target QS_END_PRE_() } else { // empty } QS_MEM_APP(); QS_CRIT_EXIT(); #endif // Q_SPY --limit; } QF_CRIT_ENTRY(); Q_ENSURE_INCRIT(790, *a == nullptr); QF_CRIT_EXIT(); m_state.obj = (r >= Q_RET_TRAN) ? m_temp.tatbl->target : tatbl->target; return r; #ifndef Q_SPY Q_UNUSED_PAR(qsId); #endif QF_CRIT_STAT // exit states from the current state to the tran. source state QMState const *s = cs; std::int_fast8_t limit = MAX_NEST_DEPTH_; // loop hard limit for (; (s != ts) && (limit > 0); --limit) { // exit action provided in state 's'? if (s->exitAction != nullptr) { // execute the exit action static_cast<void>((*s->exitAction)(this)); QS_CRIT_ENTRY(); QS_MEM_SYS(); QS_BEGIN_PRE_(QS_QEP_STATE_EXIT, qsId) QS_OBJ_PRE_(this); // this state machine object QS_FUN_PRE_(s->stateHandler); // the exited state handler QS_END_PRE_() QS_MEM_APP(); QS_CRIT_EXIT(); } s = s->superstate; // advance to the superstate if (s == nullptr) { // reached the top of a submachine? s = m_temp.obj; // the superstate from QM_SM_EXIT() QF_CRIT_ENTRY(); Q_ASSERT_INCRIT(810, s != nullptr); // must be valid QF_CRIT_EXIT(); } } QF_CRIT_ENTRY(); Q_ENSURE_INCRIT(890, limit > 0); QF_CRIT_EXIT(); #ifndef Q_SPY Q_UNUSED_PAR(qsId); #endif QMState const *s = hist; QMState const *ts = m_state.obj; // tran. source QMState const *epath[MAX_ENTRY_DEPTH_]; QF_CRIT_STAT QS_CRIT_ENTRY(); QS_MEM_SYS(); QS_BEGIN_PRE_(QS_QEP_TRAN_HIST, qsId) QS_OBJ_PRE_(this); // this state machine object QS_FUN_PRE_(ts->stateHandler); // source state handler QS_FUN_PRE_(hist->stateHandler); // target state handler QS_END_PRE_() QS_MEM_APP(); QS_CRIT_EXIT(); std::int_fast8_t i = 0; // tran. entry path index while ((s != ts) && (i < MAX_ENTRY_DEPTH_)) { if (s->entryAction != nullptr) { epath[i] = s; ++i; } s = s->superstate; if (s == nullptr) { ts = s; // force exit from the for-loop } } QF_CRIT_ENTRY(); Q_ASSERT_INCRIT(910, s == ts); QF_CRIT_EXIT(); // retrace the entry path in reverse (desired) order... while (i > 0) { --i; (*epath[i]->entryAction)(this); // run entry action in epath[i] QS_CRIT_ENTRY(); QS_MEM_SYS(); QS_BEGIN_PRE_(QS_QEP_STATE_ENTRY, qsId) QS_OBJ_PRE_(this); QS_FUN_PRE_(epath[i]->stateHandler); // entered state handler QS_END_PRE_() QS_MEM_APP(); QS_CRIT_EXIT(); } m_state.obj = hist; // set current state to the tran. target // initial tran. present? QState r; if (hist->initAction != nullptr) { r = (*hist->initAction)(this); // execute the tran. action } else { r = Q_RET_NULL; } return r; const noexcept return &l_msm_top_s; \ QP::QState state_ ## _h(QP::QEvt const * const e); \ static QP::QState state_(void * const me, QP::QEvt const * const e) \ QP::QState subclass_::state_(void * const me, QP::QEvt const * const e) { \ return static_cast<subclass_ *>(me)->state_ ## _h(e); } \ QP::QState subclass_::state_ ## _h(QP::QEvt const * const e) (Q_RET_HANDLED) (Q_RET_UNHANDLED) (static_cast<subclass_ const *>(e)) \ (reinterpret_cast<QP::QStateHandler>(handler_)) \ QP::QState state_ ## _h(QP::QEvt const * const e); \ static QP::QState state_(void * const me, QP::QEvt const * const e); \ static QP::QMState const state_ ## _s \ QP::QState state_ ## _h(QP::QEvt const * const e);\ static QP::QState state_(void * const me, QP::QEvt const * const e); \ static SM_ ## subm_ const state_ ## _s \ QP::QState action_ ## _h(); \ static QP::QState action_(void * const me) \ QP::QState subclass_::state_(void * const me, QP::QEvt const * const e) {\ return static_cast<subclass_ *>(me)->state_ ## _h(e); } \ QP::QState subclass_::state_ ## _h(QP::QEvt const * const e) \ QP::QState subclass_::action_(void * const me) { \ return static_cast<subclass_ *>(me)->action_ ## _h(); } \ QP::QState subclass_::action_ ## _h() (Q_RET_HANDLED) (Q_RET_HANDLED) (Q_RET_SUPER) (nullptr) (nullptr) (static_cast<void>(par_)) (sizeof(array_) / sizeof((array_)[0U])) (reinterpret_cast<type_ *>(uint_)) init((qsId_)) init(0U) dispatch((e_), (qsId_)) dispatch((e_), 0U) = std::uint16_t; = std::uint8_t; = std::uint16_t; = std::uint32_t; = std::uint8_t; = std::uint16_t; = std::uint32_t; noexcept static std::uint8_t const log2LUT[16] = { 0U, 1U, 2U, 2U, 3U, 3U, 3U, 3U, 4U, 4U, 4U, 4U, 4U, 4U, 4U, 4U }; std::uint_fast8_t n = 0U; QP::QPSetBits t; #if (QF_MAX_ACTIVE > 16U) t = static_cast<QP::QPSetBits>(x >> 16U); if (t != 0U) { n += 16U; x = t; } #endif #if (QF_MAX_ACTIVE > 8U) t = (x >> 8U); if (t != 0U) { n += 8U; x = t; } #endif t = (x >> 4U); if (t != 0U) { n += 4U; x = t; } return n + log2LUT[x]; noexcept m_bits[0] = 0U; #if (QF_MAX_ACTIVE > 32) m_bits[1] = 0U; #endif const noexcept #if (QF_MAX_ACTIVE <= 32U) return (m_bits[0] == 0U); #else return (m_bits[0] == 0U) ? (m_bits[1] == 0U) : false; #endif const noexcept #if (QF_MAX_ACTIVE <= 32U) return (m_bits[0] != 0U); #else return (m_bits[0] != 0U) ? true : (m_bits[1] != 0U); #endif const noexcept #if (QF_MAX_ACTIVE <= 32U) return (m_bits[0] & (static_cast<QPSetBits>(1U) << (n - 1U))) != 0U; #else return (n <= 32U) ? ((m_bits[0] & (static_cast<QPSetBits>(1U) << (n - 1U))) != 0U) : ((m_bits[1] & (static_cast<QPSetBits>(1U) << (n - 33U))) != 0U); #endif noexcept #if (QF_MAX_ACTIVE <= 32U) m_bits[0] = (m_bits[0] | (static_cast<QPSetBits>(1U) << (n - 1U))); #else if (n <= 32U) { m_bits[0] = (m_bits[0] | (static_cast<QPSetBits>(1U) << (n - 1U))); } else { m_bits[1] = (m_bits[1] | (static_cast<QPSetBits>(1U) << (n - 33U))); } #endif noexcept #if (QF_MAX_ACTIVE <= 32U) m_bits[0] = (m_bits[0] & static_cast<QPSetBits>(~(1U << (n - 1U)))); #else if (n <= 32U) { (m_bits[0] = (m_bits[0] & ~(static_cast<QPSetBits>(1U) << (n - 1U)))); } else { (m_bits[1] = (m_bits[1] & ~(static_cast<QPSetBits>(1U) << (n - 33U)))); } #endif const noexcept #if (QF_MAX_ACTIVE <= 32U) return QF_LOG2(m_bits[0]); #else return (m_bits[1] != 0U) ? (QF_LOG2(m_bits[1]) + 32U) : (QF_LOG2(m_bits[0])); #endif const noexcept dis->m_bits[0] = ~m_bits[0]; #if (QF_MAX_ACTIVE > 32U) dis->m_bits[1] = ~m_bits[1]; #endif const noexcept #if (QF_MAX_ACTIVE <= 32U) return m_bits[0] == static_cast<QPSetBits>(~dis->m_bits[0]); #else return (m_bits[0] == static_cast<QPSetBits>(~dis->m_bits[0])) && (m_bits[1] == static_cast<QPSetBits>(~dis->m_bits[1])); #endif // friends... // friends... noexcept : QAsm(), m_prio(0U), m_pthre(0U) m_state.fun = Q_STATE_CAST(&top); m_temp.fun = initial; #ifndef Q_UNSAFE m_prio_dis = static_cast<std::uint8_t>(~m_prio); m_pthre_dis = static_cast<std::uint8_t>(~m_pthre); #endif override reinterpret_cast<QHsm *>(this)->QHsm::init(e, qsId); override this->init(nullptr, qsId); override reinterpret_cast<QHsm *>(this)->QHsm::dispatch(e, qsId); noexcept override return reinterpret_cast<QHsm *>(this)->QHsm::isIn(state); noexcept return reinterpret_cast<QHsm *>(this)->QHsm::childState(parent); this->start(prioSpec, qSto, qLen, stkSto, stkSize, nullptr); noexcept QF_CRIT_STAT QF_CRIT_ENTRY(); QF_MEM_SYS(); if (m_pthre == 0U) { // preemption-threshold not defined? m_pthre = m_prio; // apply the default } #ifndef Q_UNSAFE Q_REQUIRE_INCRIT(100, (0U < m_prio) && (m_prio <= QF_MAX_ACTIVE) && (registry_[m_prio] == nullptr) && (m_prio <= m_pthre)); std::uint8_t prev_thre = m_pthre; std::uint8_t next_thre = m_pthre; std::uint_fast8_t p; for (p = static_cast<std::uint_fast8_t>(m_prio) - 1U; p > 0U; --p) { if (registry_[p] != nullptr) { prev_thre = registry_[p]->m_pthre; break; } } for (p = static_cast<std::uint_fast8_t>(m_prio) + 1U; p <= QF_MAX_ACTIVE; ++p) { if (registry_[p] != nullptr) { next_thre = registry_[p]->m_pthre; break; } } Q_ASSERT_INCRIT(190, (prev_thre <= m_pthre) && (m_pthre <= next_thre)); m_prio_dis = static_cast<std::uint8_t>(~m_prio); m_pthre_dis = static_cast<std::uint8_t>(~m_pthre); #endif // Q_UNSAFE // register the AO at the QF-prio. registry_[m_prio] = this; QF_MEM_APP(); QF_CRIT_EXIT(); noexcept std::uint_fast8_t const p = static_cast<std::uint_fast8_t>(m_prio); QF_CRIT_STAT QF_CRIT_ENTRY(); QF_MEM_SYS(); Q_REQUIRE_INCRIT(200, (0U < p) && (p <= QF_MAX_ACTIVE) && (registry_[p] == this)); registry_[p] = nullptr; // free-up the priority level m_state.fun = nullptr; // invalidate the state QF_MEM_APP(); QF_CRIT_EXIT(); noexcept #ifndef Q_SPY Q_UNUSED_PAR(sender); #endif #ifdef Q_UTEST // test? #if Q_UTEST != 0 // testing QP-stub? if (m_temp.fun == Q_STATE_CAST(0)) { // QActiveDummy? return static_cast<QActiveDummy *>(this)->fakePost(e, margin, sender); } #endif #endif QF_CRIT_STAT QF_CRIT_ENTRY(); QF_MEM_SYS(); #ifndef Q_UNSAFE std::uint8_t const pcopy = static_cast<std::uint8_t>(~m_prio_dis); Q_REQUIRE_INCRIT(102, (QEvt::verify_(e)) && (m_prio == pcopy)); #endif QEQueueCtr nFree = m_eQueue.m_nFree; // get volatile into temporary // test-probe#1 for faking queue overflow QS_TEST_PROBE_DEF(&QActive::post_) QS_TEST_PROBE_ID(1, nFree = 0U; ) bool status; if (margin == QF::NO_MARGIN) { if (nFree > 0U) { status = true; // can post } else { status = false; // cannot post Q_ERROR_INCRIT(190); // must be able to post the event } } else if (nFree > static_cast<QEQueueCtr>(margin)) { status = true; // can post } else { status = false; // cannot post, but don't assert } // is it a mutable event? if (e->getPoolNum_() != 0U) { QEvt_refCtr_inc_(e); // increment the reference counter } if (status) { // can post the event? --nFree; // one free entry just used up m_eQueue.m_nFree = nFree; // update the original if (m_eQueue.m_nMin > nFree) { m_eQueue.m_nMin = nFree; // update minimum so far } QS_BEGIN_PRE_(QS_QF_ACTIVE_POST, m_prio) QS_TIME_PRE_(); // timestamp QS_OBJ_PRE_(sender); // the sender object QS_SIG_PRE_(e->sig); // the signal of the event QS_OBJ_PRE_(this); // this active object QS_2U8_PRE_(e->getPoolNum_(), e->refCtr_); // poolNum & refCtr QS_EQC_PRE_(nFree); // # free entries QS_EQC_PRE_(m_eQueue.m_nMin); // min # free entries QS_END_PRE_() #ifdef Q_UTEST // callback to examine the posted event under the same conditions // as producing the #QS_QF_ACTIVE_POST trace record, which are: // the local filter for this AO ('m_prio') is set if (QS_LOC_CHECK_(m_prio)) { QS::onTestPost(sender, this, e, status); } #endif if (m_eQueue.m_frontEvt == nullptr) { // empty queue? m_eQueue.m_frontEvt = e; // deliver event directly #ifdef QXK_HPP_ if (m_state.act == Q_ACTION_CAST(0)) { // eXtended thread? QXTHREAD_EQUEUE_SIGNAL_(this); // signal the event queue } else { QACTIVE_EQUEUE_SIGNAL_(this); // signal the event queue } #else QACTIVE_EQUEUE_SIGNAL_(this); // signal the event queue #endif } // queue is not empty, insert event into the ring-buffer else { // insert event into the ring buffer (FIFO) m_eQueue.m_ring[m_eQueue.m_head] = e; if (m_eQueue.m_head == 0U) { // need to wrap head? m_eQueue.m_head = m_eQueue.m_end; // wrap around } // advance the head (counter clockwise) m_eQueue.m_head = (m_eQueue.m_head - 1U); } QF_MEM_APP(); QF_CRIT_EXIT(); } else { // cannot post the event QS_BEGIN_PRE_(QS_QF_ACTIVE_POST_ATTEMPT, m_prio) QS_TIME_PRE_(); // timestamp QS_OBJ_PRE_(sender); // the sender object QS_SIG_PRE_(e->sig); // the signal of the event QS_OBJ_PRE_(this); // this active object QS_2U8_PRE_(e->getPoolNum_(), e->refCtr_); // poolNum & refCtr QS_EQC_PRE_(nFree); // # free entries QS_EQC_PRE_(margin); // margin requested QS_END_PRE_() #ifdef Q_UTEST // callback to examine the posted event under the same conditions // as producing the #QS_QF_ACTIVE_POST trace record, which are: // the local filter for this AO ('m_prio') is set if (QS_LOC_CHECK_(m_prio)) { QS::onTestPost(sender, this, e, status); } #endif QF_MEM_APP(); QF_CRIT_EXIT(); #if (QF_MAX_EPOOL > 0U) QF::gc(e); // recycle the event to avoid a leak #endif } return status; noexcept #ifdef Q_UTEST // test? #if Q_UTEST != 0 // testing QP-stub? if (m_temp.fun == Q_STATE_CAST(0)) { // QActiveDummy? static_cast<QActiveDummy *>(this)->QActiveDummy::fakePostLIFO(e); return; } #endif #endif QF_CRIT_STAT QF_CRIT_ENTRY(); QF_MEM_SYS(); #ifndef Q_UNSAFE std::uint8_t const pcopy = static_cast<std::uint8_t>(~m_prio_dis); Q_REQUIRE_INCRIT(202, (QEvt::verify_(e)) && (m_prio == pcopy)); #endif #ifdef QXK_HPP_ Q_REQUIRE_INCRIT(200, m_state.act != Q_ACTION_CAST(0)); #endif QEQueueCtr nFree = m_eQueue.m_nFree; // get volatile into temporary // test-probe#1 for faking queue overflow QS_TEST_PROBE_DEF(&QActive::postLIFO) QS_TEST_PROBE_ID(1, nFree = 0U; ) Q_REQUIRE_INCRIT(201, nFree != 0U); if (e->getPoolNum_() != 0U) { // is it a mutable event? QEvt_refCtr_inc_(e); // increment the reference counter } --nFree; // one free entry just used up m_eQueue.m_nFree = nFree; // update the original if (m_eQueue.m_nMin > nFree) { m_eQueue.m_nMin = nFree; // update minimum so far } QS_BEGIN_PRE_(QS_QF_ACTIVE_POST_LIFO, m_prio) QS_TIME_PRE_(); // timestamp QS_SIG_PRE_(e->sig); // the signal of this event QS_OBJ_PRE_(this); // this active object QS_2U8_PRE_(e->getPoolNum_(), e->refCtr_); // poolNum & refCtr QS_EQC_PRE_(nFree); // # free entries QS_EQC_PRE_(m_eQueue.m_nMin); // min # free entries QS_END_PRE_() #ifdef Q_UTEST // callback to examine the posted event under the same conditions // as producing the #QS_QF_ACTIVE_POST trace record, which are: // the local filter for this AO ('m_prio') is set if (QS_LOC_CHECK_(m_prio)) { QS::onTestPost(nullptr, this, e, true); } #endif QEvt const * const frontEvt = m_eQueue.m_frontEvt; m_eQueue.m_frontEvt = e; // deliver the event directly to the front if (frontEvt == nullptr) { // was the queue empty? QACTIVE_EQUEUE_SIGNAL_(this); // signal the event queue } else { // queue was not empty, leave the event in the ring-buffer m_eQueue.m_tail = (m_eQueue.m_tail + 1U); if (m_eQueue.m_tail == m_eQueue.m_end) { // need to wrap the tail? m_eQueue.m_tail = 0U; // wrap around } m_eQueue.m_ring[m_eQueue.m_tail] = frontEvt; } QF_MEM_APP(); QF_CRIT_EXIT(); noexcept QF_CRIT_STAT QF_CRIT_ENTRY(); QF_MEM_SYS(); QACTIVE_EQUEUE_WAIT_(this); // wait for event to arrive directly // always remove evt from the front QEvt const * const e = m_eQueue.m_frontEvt; QEQueueCtr const nFree = m_eQueue.m_nFree + 1U; // get volatile into tmp m_eQueue.m_nFree = nFree; // update the # free if (nFree <= m_eQueue.m_end) { // any events in the ring buffer? // remove event from the tail m_eQueue.m_frontEvt = m_eQueue.m_ring[m_eQueue.m_tail]; if (m_eQueue.m_tail == 0U) { // need to wrap the tail? m_eQueue.m_tail = m_eQueue.m_end; // wrap around } m_eQueue.m_tail = (m_eQueue.m_tail - 1U); QS_BEGIN_PRE_(QS_QF_ACTIVE_GET, m_prio) QS_TIME_PRE_(); // timestamp QS_SIG_PRE_(e->sig); // the signal of this event QS_OBJ_PRE_(this); // this active object QS_2U8_PRE_(e->getPoolNum_(), e->refCtr_); // poolNum & refCtr QS_EQC_PRE_(nFree); // # free entries QS_END_PRE_() } else { m_eQueue.m_frontEvt = nullptr; // the queue becomes empty // all entries in the queue must be free (+1 for fronEvt) Q_ASSERT_INCRIT(310, nFree == (m_eQueue.m_end + 1U)); QS_BEGIN_PRE_(QS_QF_ACTIVE_GET_LAST, m_prio) QS_TIME_PRE_(); // timestamp QS_SIG_PRE_(e->sig); // the signal of this event QS_OBJ_PRE_(this); // this active object QS_2U8_PRE_(e->getPoolNum_(), e->refCtr_); // poolNum & refCtr QS_END_PRE_() } QF_MEM_APP(); QF_CRIT_EXIT(); return e; noexcept QF_CRIT_STAT QF_CRIT_ENTRY(); Q_REQUIRE_INCRIT(400, (prio <= QF_MAX_ACTIVE) && (QActive::registry_[prio] != nullptr)); std::uint_fast16_t const min = static_cast<std::uint_fast16_t>( QActive::registry_[prio]->m_eQueue.getNMin()); QF_CRIT_EXIT(); return min; noexcept subscrList_ = subscrSto; maxPubSignal_ = maxSignal; // initialize the subscriber list for (enum_t sig = 0; sig < maxSignal; ++sig) { subscrSto[sig].m_set.setEmpty(); #ifndef Q_UNSAFE subscrSto[sig].m_set.update_(&subscrSto[sig].m_set_dis); #endif } noexcept #ifndef Q_SPY Q_UNUSED_PAR(sender); Q_UNUSED_PAR(qsId); #endif QSignal const sig = e->sig; QF_CRIT_STAT QF_CRIT_ENTRY(); QF_MEM_SYS(); Q_REQUIRE_INCRIT(200, sig < static_cast<QSignal>(maxPubSignal_)); Q_REQUIRE_INCRIT(202, subscrList_[sig].m_set.verify_(&subscrList_[sig].m_set_dis)); QS_BEGIN_PRE_(QS_QF_PUBLISH, qsId) QS_TIME_PRE_(); // the timestamp QS_OBJ_PRE_(sender); // the sender object QS_SIG_PRE_(e->sig); // the signal of the event QS_2U8_PRE_(e->getPoolNum_(), e->refCtr_); // poolNum & refCtr QS_END_PRE_() // is it a mutable event? if (e->getPoolNum_() != 0U) { // NOTE: The reference counter of a mutable event is incremented to // prevent premature recycling of the event while the multicasting // is still in progress. At the end of the function, the garbage // collector step (QF::gc()) decrements the reference counter and // recycles the event if the counter drops to zero. This covers the // case when the event was published without any subscribers. QEvt_refCtr_inc_(e); } // make a local, modifiable copy of the subscriber set QPSet subscrSet = subscrList_[sig].m_set; QF_MEM_APP(); QF_CRIT_EXIT(); if (subscrSet.notEmpty()) { // any subscribers? // highest-prio subscriber std::uint_fast8_t p = subscrSet.findMax(); QF_CRIT_ENTRY(); QF_MEM_SYS(); QActive *a = registry_[p]; // the AO must be registered with the framework Q_ASSERT_INCRIT(210, a != nullptr); QF_MEM_APP(); QF_CRIT_EXIT(); QF_SCHED_STAT_ QF_SCHED_LOCK_(p); // lock the scheduler up to AO's prio std::uint_fast8_t limit = QF_MAX_ACTIVE + 1U; do { // loop over all subscribers --limit; // POST() asserts internally if the queue overflows a->POST(e, sender); subscrSet.remove(p); // remove the handled subscriber if (subscrSet.notEmpty()) { // still more subscribers? p = subscrSet.findMax(); // highest-prio subscriber QF_CRIT_ENTRY(); QF_MEM_SYS(); a = registry_[p]; // the AO must be registered with the framework Q_ASSERT_INCRIT(220, a != nullptr); QF_MEM_APP(); QF_CRIT_EXIT(); } else { p = 0U; // no more subscribers } } while ((p != 0U) && (limit > 0U)); QF_CRIT_ENTRY(); Q_ENSURE_INCRIT(290, p == 0U); QF_CRIT_EXIT(); QF_SCHED_UNLOCK_(); // unlock the scheduler } // The following garbage collection step decrements the reference counter // and recycles the event if the counter drops to zero. This covers both // cases when the event was published with or without any subscribers. #if (QF_MAX_EPOOL > 0U) QF::gc(e); #endif const noexcept std::uint_fast8_t const p = static_cast<std::uint_fast8_t>(m_prio); QF_CRIT_STAT QF_CRIT_ENTRY(); QF_MEM_SYS(); Q_REQUIRE_INCRIT(300, (Q_USER_SIG <= sig) && (sig < maxPubSignal_) && (0U < p) && (p <= QF_MAX_ACTIVE) && (registry_[p] == this)); Q_REQUIRE_INCRIT(302, subscrList_[sig].m_set.verify_(&subscrList_[sig].m_set_dis)); QS_BEGIN_PRE_(QS_QF_ACTIVE_SUBSCRIBE, m_prio) QS_TIME_PRE_(); // timestamp QS_SIG_PRE_(sig); // the signal of this event QS_OBJ_PRE_(this); // this active object QS_END_PRE_() // insert the prio. into the subscriber set subscrList_[sig].m_set.insert(p); #ifndef Q_UNSAFE subscrList_[sig].m_set.update_(&subscrList_[sig].m_set_dis); #endif QF_MEM_APP(); QF_CRIT_EXIT(); const noexcept std::uint_fast8_t const p = static_cast<std::uint_fast8_t>(m_prio); QF_CRIT_STAT QF_CRIT_ENTRY(); QF_MEM_SYS(); Q_REQUIRE_INCRIT(400, (Q_USER_SIG <= sig) && (sig < maxPubSignal_) && (0U < p) && (p <= QF_MAX_ACTIVE) && (registry_[p] == this)); Q_REQUIRE_INCRIT(402, subscrList_[sig].m_set.verify_(&subscrList_[sig].m_set_dis)); QS_BEGIN_PRE_(QS_QF_ACTIVE_UNSUBSCRIBE, m_prio) QS_TIME_PRE_(); // timestamp QS_SIG_PRE_(sig); // the signal of this event QS_OBJ_PRE_(this); // this active object QS_END_PRE_() // remove the prio. from the subscriber set subscrList_[sig].m_set.remove(p); #ifndef Q_UNSAFE subscrList_[sig].m_set.update_(&subscrList_[sig].m_set_dis); #endif QF_MEM_APP(); QF_CRIT_EXIT(); const noexcept QF_CRIT_STAT QF_CRIT_ENTRY(); QF_MEM_SYS(); std::uint_fast8_t const p = static_cast<std::uint_fast8_t>(m_prio); Q_REQUIRE_INCRIT(500, (0U < p) && (p <= QF_MAX_ACTIVE) && (registry_[p] == this)); enum_t const maxPubSig = maxPubSignal_; QF_MEM_APP(); QF_CRIT_EXIT(); for (enum_t sig = Q_USER_SIG; sig < maxPubSig; ++sig) { QF_CRIT_ENTRY(); QF_MEM_SYS(); if (subscrList_[sig].m_set.hasElement(p)) { subscrList_[sig].m_set.remove(p); #ifndef Q_UNSAFE subscrList_[sig].m_set.update_(&subscrList_[sig].m_set_dis); #endif QS_BEGIN_PRE_(QS_QF_ACTIVE_UNSUBSCRIBE, m_prio) QS_TIME_PRE_(); // timestamp QS_SIG_PRE_(sig); // the signal of this event QS_OBJ_PRE_(this); // this active object QS_END_PRE_() } QF_MEM_APP(); QF_CRIT_EXIT(); QF_CRIT_EXIT_NOP(); // prevent merging critical sections } const noexcept bool const status = eq->post(e, 0U, m_prio); QS_CRIT_STAT QS_CRIT_ENTRY(); QS_MEM_SYS(); QS_BEGIN_PRE_(QS_QF_ACTIVE_DEFER, m_prio) QS_TIME_PRE_(); // time stamp QS_OBJ_PRE_(this); // this active object QS_OBJ_PRE_(eq); // the deferred queue QS_SIG_PRE_(e->sig); // the signal of the event QS_2U8_PRE_(e->getPoolNum_(), e->refCtr_); // poolNum & refCtr QS_END_PRE_() QS_MEM_APP(); QS_CRIT_EXIT(); return status; noexcept QEvt const * const e = eq->get(m_prio); // get evt from deferred queue QF_CRIT_STAT bool recalled; if (e != nullptr) { // event available? postLIFO(e); // post it to the _front_ of the AO's queue QF_CRIT_ENTRY(); QF_MEM_SYS(); if (e->getPoolNum_() != 0U) { // is it a mutable event? // after posting to the AO's queue the event must be referenced // at least twice: once in the deferred event queue (eq->get() // did NOT decrement the reference counter) and once in the // AO's event queue. Q_ASSERT_INCRIT(210, e->refCtr_ >= 2U); // we need to decrement the reference counter once, to account // for removing the event from the deferred event queue. QEvt_refCtr_dec_(e); // decrement the reference counter } QS_BEGIN_PRE_(QS_QF_ACTIVE_RECALL, m_prio) QS_TIME_PRE_(); // time stamp QS_OBJ_PRE_(this); // this active object QS_OBJ_PRE_(eq); // the deferred queue QS_SIG_PRE_(e->sig); // the signal of the event QS_2U8_PRE_(e->getPoolNum_(), e->refCtr_); // poolNum & refCtr QS_END_PRE_() QF_MEM_APP(); QF_CRIT_EXIT(); recalled = true; } else { QS_CRIT_ENTRY(); QS_MEM_SYS(); QS_BEGIN_PRE_(QS_QF_ACTIVE_RECALL_ATTEMPT, m_prio) QS_TIME_PRE_(); // time stamp QS_OBJ_PRE_(this); // this active object QS_OBJ_PRE_(eq); // the deferred queue QS_END_PRE_() QS_MEM_APP(); QS_CRIT_EXIT(); recalled = false; } return recalled; const noexcept std::uint_fast16_t n = 0U; while (n < num) { QEvt const * const e = eq->get(m_prio); if (e != nullptr) { ++n; // count one more flushed event #if (QF_MAX_EPOOL > 0U) QF::gc(e); // garbage collect #endif } else { break; } } return n; const noexcept return static_cast<std::uint_fast8_t>(m_prio); noexcept m_prio = static_cast<std::uint8_t>(prio & 0xFFU); m_pthre = static_cast<std::uint8_t>(prio >> 8U); const noexcept return static_cast<std::uint_fast8_t>(m_pthre); const noexcept return m_eQueue; const noexcept return m_osObject; const noexcept return m_thread; m_thread = thr; noexcept noexcept noexcept : QActive(initial) m_state.obj = reinterpret_cast<QMsm *>(this)->topQMState(); m_temp.fun = initial; override reinterpret_cast<QMsm *>(this)->QMsm::init(e, qsId); override this->init(nullptr, qsId); override reinterpret_cast<QMsm *>(this)->QMsm::dispatch(e, qsId); noexcept override return reinterpret_cast<QMsm *>(this)->QMsm::isIn(state); noexcept override return reinterpret_cast<QMsm *>(this)->QMsm::getStateHandler(); const noexcept return reinterpret_cast<QMsm const *>(this)->QMsm::isInState(st); const noexcept return reinterpret_cast<QMsm const *>(this) ->QMsm::childStateObj(parent); noexcept : QEvt(sig), m_next(nullptr), m_act(act), m_ctr(0U), m_interval(0U) QF_CRIT_STAT QF_CRIT_ENTRY(); Q_REQUIRE_INCRIT(300, (sig != 0U) && (tickRate < QF_MAX_TICK_RATE)); QF_CRIT_EXIT(); // The refCtr_ attribute is not used in time events, so it is // reused to hold the tickRate as well as other information refCtr_ = static_cast<std::uint8_t>(tickRate); noexcept std::uint8_t const tickRate = refCtr_ & TE_TICK_RATE; QTimeEvtCtr const ctr = m_ctr; #ifdef Q_SPY std::uint_fast8_t const qsId = static_cast<QActive const *>(m_act)->m_prio; #endif QF_CRIT_STAT QF_CRIT_ENTRY(); QF_MEM_SYS(); Q_REQUIRE_INCRIT(400, (m_act != nullptr) && (ctr == 0U) && (nTicks != 0U) && (tickRate < static_cast<std::uint8_t>(QF_MAX_TICK_RATE)) && (sig >= static_cast<QSignal>(Q_USER_SIG))); #ifdef Q_UNSAFE Q_UNUSED_PAR(ctr); #endif m_ctr = nTicks; m_interval = interval; // is the time event unlinked? // NOTE: For the duration of a single clock tick of the specified tick // rate a time event can be disarmed and yet still linked into the list // because un-linking is performed exclusively in the QF_tickX() function. if (static_cast<std::uint_fast8_t>( static_cast<std::uint_fast8_t>(refCtr_) & TE_IS_LINKED) == 0U) { // mark as linked refCtr_ = static_cast<std::uint8_t>(refCtr_ | TE_IS_LINKED); // The time event is initially inserted into the separate // "freshly armed" list based on timeEvtHead_[tickRate].act. // Only later, inside QTimeEvt::tick(), the "freshly armed" // list is appended to the main list of armed time events based on // timeEvtHead_[tickRate].next. Again, this is to keep any // changes to the main list exclusively inside QTimeEvt::tick(). m_next = timeEvtHead_[tickRate].toTimeEvt(); timeEvtHead_[tickRate].m_act = this; } QS_BEGIN_PRE_(QS_QF_TIMEEVT_ARM, qsId) QS_TIME_PRE_(); // timestamp QS_OBJ_PRE_(this); // this time event object QS_OBJ_PRE_(m_act); // the active object QS_TEC_PRE_(nTicks); // the # ticks QS_TEC_PRE_(interval); // the interval QS_U8_PRE_(tickRate); // tick rate QS_END_PRE_() QF_MEM_APP(); QF_CRIT_EXIT(); noexcept #ifdef Q_SPY std::uint_fast8_t const qsId = static_cast<QActive *>(m_act)->m_prio; #endif QF_CRIT_STAT QF_CRIT_ENTRY(); QF_MEM_SYS(); // is the time event actually armed? bool wasArmed; if (m_ctr != 0U) { wasArmed = true; refCtr_ = static_cast<std::uint8_t>(refCtr_ | TE_WAS_DISARMED); QS_BEGIN_PRE_(QS_QF_TIMEEVT_DISARM, qsId) QS_TIME_PRE_(); // timestamp QS_OBJ_PRE_(this); // this time event object QS_OBJ_PRE_(m_act); // the target AO QS_TEC_PRE_(m_ctr); // the # ticks QS_TEC_PRE_(m_interval); // the interval QS_U8_PRE_(refCtr_& TE_TICK_RATE); // tick rate QS_END_PRE_() m_ctr = 0U; // schedule removal from the list } else { // the time event was already disarmed automatically wasArmed = false; refCtr_ = static_cast<std::uint8_t>(refCtr_ & static_cast<std::uint8_t>(~TE_WAS_DISARMED)); QS_BEGIN_PRE_(QS_QF_TIMEEVT_DISARM_ATTEMPT, qsId) QS_TIME_PRE_(); // timestamp QS_OBJ_PRE_(this); // this time event object QS_OBJ_PRE_(m_act); // the target AO QS_U8_PRE_(refCtr_& TE_TICK_RATE); // tick rate QS_END_PRE_() } QF_MEM_APP(); QF_CRIT_EXIT(); return wasArmed; noexcept std::uint8_t const tickRate = refCtr_ & TE_TICK_RATE; #ifdef Q_SPY std::uint_fast8_t const qsId = static_cast<QActive *>(m_act)->m_prio; #endif QF_CRIT_STAT QF_CRIT_ENTRY(); QF_MEM_SYS(); Q_REQUIRE_INCRIT(600, (m_act != nullptr) && (tickRate < static_cast<std::uint8_t>(QF_MAX_TICK_RATE)) && (nTicks != 0U) && (sig >= static_cast<QSignal>(Q_USER_SIG))); // is the time evt not running? bool wasArmed; if (m_ctr == 0U) { wasArmed = false; // NOTE: For a duration of a single clock tick of the specified // tick rate a time event can be disarmed and yet still linked into // the list, because unlinking is performed exclusively in the // QTimeEvt::tickX() function. // is the time event unlinked? if (static_cast<std::uint8_t>(refCtr_ & TE_IS_LINKED) == 0U) { // mark as linked refCtr_ = static_cast<std::uint8_t>(refCtr_ | TE_IS_LINKED); // The time event is initially inserted into the separate // "freshly armed" list based on timeEvtHead_[tickRate].act. // Only later, inside QTimeEvt::tick(), the "freshly armed" // list is appended to the main list of armed time events based on // timeEvtHead_[tickRate].next. Again, this is to keep any // changes to the main list exclusively inside QTimeEvt::tick(). m_next = timeEvtHead_[tickRate].toTimeEvt(); timeEvtHead_[tickRate].m_act = this; } } else { // the time event was armed wasArmed = true; } m_ctr = nTicks; // re-load the tick counter (shift the phasing) QS_BEGIN_PRE_(QS_QF_TIMEEVT_REARM, qsId) QS_TIME_PRE_(); // timestamp QS_OBJ_PRE_(this); // this time event object QS_OBJ_PRE_(m_act); // the target AO QS_TEC_PRE_(m_ctr); // the # ticks QS_TEC_PRE_(m_interval); // the interval QS_2U8_PRE_(tickRate, (wasArmed ? 1U : 0U)); QS_END_PRE_() QF_MEM_APP(); QF_CRIT_EXIT(); return wasArmed; noexcept QF_CRIT_STAT QF_CRIT_ENTRY(); QF_MEM_SYS(); std::uint8_t const isDisarmed = refCtr_ & TE_WAS_DISARMED; refCtr_ = static_cast<std::uint8_t>(refCtr_ | TE_WAS_DISARMED); QF_MEM_APP(); QF_CRIT_EXIT(); return isDisarmed != 0U; const noexcept return m_act; const noexcept return m_ctr; const noexcept return m_interval; noexcept #ifndef Q_SPY Q_UNUSED_PAR(sender); #endif QF_CRIT_STAT QF_CRIT_ENTRY(); QF_MEM_SYS(); Q_REQUIRE_INCRIT(100, tickRate < Q_DIM(timeEvtHead_)); QTimeEvt *prev = &timeEvtHead_[tickRate]; QS_BEGIN_PRE_(QS_QF_TICK, 0U) prev->m_ctr = (prev->m_ctr + 1U); QS_TEC_PRE_(prev->m_ctr); // tick ctr QS_U8_PRE_(tickRate); // tick rate QS_END_PRE_() // scan the linked-list of time events at this rate... std::uint_fast8_t limit = 2U*QF_MAX_ACTIVE; // loop hard limit for (; limit > 0U; --limit) { QTimeEvt *e = prev->m_next; // advance down the time evt. list if (e == nullptr) { // end of the list? // any new time events armed since the last run of tick()? if (timeEvtHead_[tickRate].m_act != nullptr) { // sanity check Q_ASSERT_INCRIT(110, prev != nullptr); prev->m_next = timeEvtHead_[tickRate].toTimeEvt(); timeEvtHead_[tickRate].m_act = nullptr; e = prev->m_next; // switch to the new list } else { // all currently armed time events are processed break; // terminate the for-loop } } // the time event 'e' must be valid Q_ASSERT_INCRIT(112, QEvt::verify_(e)); if (e->m_ctr == 0U) { // time event scheduled for removal? prev->m_next = e->m_next; // mark time event 'e' as NOT linked e->refCtr_ = static_cast<std::uint8_t>(e->refCtr_ & static_cast<std::uint8_t>(~TE_IS_LINKED)); // do NOT advance the prev pointer QF_MEM_APP(); QF_CRIT_EXIT(); // exit crit. section to reduce latency // NOTE: prevent merging critical sections // In some QF ports the critical section exit takes effect only // on the next machine instruction. If the next instruction is // another entry to a critical section, the critical section // might not be really exited, but rather the two adjacent // critical sections would be MERGED. The QF_CRIT_EXIT_NOP() // macro contains minimal code required to prevent such merging // of critical sections in QF ports, in which it can occur. QF_CRIT_EXIT_NOP(); } else { e->m_ctr = (e->m_ctr - 1U); if (e->m_ctr == 0U) { // is time evt about to expire? QActive * const act = e->toActive(); if (e->m_interval != 0U) { // periodic time evt? e->m_ctr = e->m_interval; // rearm the time event prev = e; // advance to this time event } else { // one-shot time event: automatically disarm prev->m_next = e->m_next; // mark time event 'e' as NOT linked e->refCtr_ = static_cast<std::uint8_t>(e->refCtr_ & static_cast<std::uint8_t>(~TE_IS_LINKED)); // do NOT advance the prev pointer QS_BEGIN_PRE_(QS_QF_TIMEEVT_AUTO_DISARM, act->m_prio) QS_OBJ_PRE_(e); // this time event object QS_OBJ_PRE_(act); // the target AO QS_U8_PRE_(tickRate); // tick rate QS_END_PRE_() } QS_BEGIN_PRE_(QS_QF_TIMEEVT_POST, act->m_prio) QS_TIME_PRE_(); // timestamp QS_OBJ_PRE_(e); // the time event object QS_SIG_PRE_(e->sig); // signal of this time event QS_OBJ_PRE_(act); // the target AO QS_U8_PRE_(tickRate); // tick rate QS_END_PRE_() #ifdef QXK_HPP_ if (e->sig < Q_USER_SIG) { QXThread::timeout_(act); QF_MEM_APP(); QF_CRIT_EXIT(); } else { QF_MEM_APP(); QF_CRIT_EXIT(); // exit crit. section before posting // act->POST() asserts if the queue overflows act->POST(e, sender); } #else QF_MEM_APP(); QF_CRIT_EXIT(); // exit crit. section before posting // act->POST() asserts if the queue overflows act->POST(e, sender); #endif } else { prev = e; // advance to this time event QF_MEM_APP(); QF_CRIT_EXIT(); // exit crit. section to reduce latency // prevent merging critical sections, see NOTE above QF_CRIT_EXIT_NOP(); } } QF_CRIT_ENTRY(); // re-enter crit. section to continue the loop QF_MEM_SYS(); } Q_ENSURE_INCRIT(190, limit > 0U); QF_MEM_APP(); QF_CRIT_EXIT(); noexcept noexcept QF_CRIT_STAT QF_CRIT_ENTRY(); Q_REQUIRE_INCRIT(800, tickRate < QF_MAX_TICK_RATE); QF_CRIT_EXIT(); bool inactive; if (timeEvtHead_[tickRate].m_next != nullptr) { inactive = false; } else if (timeEvtHead_[tickRate].m_act != nullptr) { inactive = false; } else { inactive = true; } return inactive; noexcept return static_cast<QActive *>(m_act); noexcept return static_cast<QTimeEvt *>(m_act); noexcept : QEvt(0U), m_next(nullptr), m_act(nullptr), m_ctr(0U), m_interval(0U) // The refCtr_ attribute is not used in time events, so it is // reused to hold the tickRate as well as other information refCtr_ = 0U; // default rate 0 = delete = delete noexcept : QActive(nullptr) // reuse m_head for tick-rate m_eQueue.m_head = static_cast<QEQueueCtr>(tickRate); override Q_UNUSED_PAR(e); Q_UNUSED_PAR(qsId); QF_CRIT_STAT QF_CRIT_ENTRY(); QF_MEM_SYS(); m_eQueue.m_tail = 0U; QF_MEM_APP(); QF_CRIT_EXIT(); override this->init(nullptr, qsId); override Q_UNUSED_PAR(e); Q_UNUSED_PAR(qsId); QF_CRIT_STAT QF_CRIT_ENTRY(); QF_MEM_SYS(); QEQueueCtr nTicks = m_eQueue.m_tail; // save # of ticks m_eQueue.m_tail = 0U; // clear the # ticks QF_MEM_APP(); QF_CRIT_EXIT(); for (; nTicks > 0U; --nTicks) { QTimeEvt::tick(static_cast<std::uint_fast8_t>(m_eQueue.m_head), this); } noexcept #ifndef Q_SPY Q_UNUSED_PAR(sender); #endif QF_CRIT_STAT QF_CRIT_ENTRY(); QF_MEM_SYS(); if (m_eQueue.m_frontEvt == nullptr) { static QEvt const tickEvt(0U); // immutable event m_eQueue.m_frontEvt = &tickEvt; // deliver event directly m_eQueue.m_nFree = (m_eQueue.m_nFree - 1U); // one less free event QACTIVE_EQUEUE_SIGNAL_(this); // signal the event queue } // account for one more tick event m_eQueue.m_tail = (m_eQueue.m_tail + 1U); QS_BEGIN_PRE_(QS_QF_ACTIVE_POST, m_prio) QS_TIME_PRE_(); // timestamp QS_OBJ_PRE_(sender); // the sender object QS_SIG_PRE_(0U); // the signal of the event QS_OBJ_PRE_(this); // this active object QS_2U8_PRE_(0U, 0U); // poolNum & refCtr QS_EQC_PRE_(0U); // # free entries QS_EQC_PRE_(0U); // min # free entries QS_END_PRE_() QF_MEM_APP(); QF_CRIT_EXIT(); // friends... noexcept : m_frontEvt(nullptr), m_ring(nullptr), m_end(0U), m_head(0U), m_tail(0U), m_nFree(0U), m_nMin(0U) noexcept m_frontEvt = nullptr; // no events in the queue m_ring = &qSto[0]; m_end = static_cast<QEQueueCtr>(qLen); if (qLen > 0U) { m_head = 0U; m_tail = 0U; } m_nFree = static_cast<QEQueueCtr>(qLen + 1U); //+1 for frontEvt m_nMin = m_nFree; noexcept #ifndef Q_SPY Q_UNUSED_PAR(qsId); #endif QF_CRIT_STAT QF_CRIT_ENTRY(); QF_MEM_SYS(); Q_REQUIRE_INCRIT(200, e != nullptr); QEQueueCtr nFree = m_nFree; // get volatile into temporary // required margin available? bool status; if (((margin == QF::NO_MARGIN) && (nFree > 0U)) || (nFree > static_cast<QEQueueCtr>(margin))) { // is it a mutable event? if (e->getPoolNum_() != 0U) { QEvt_refCtr_inc_(e); // increment the reference counter } --nFree; // one free entry just used up m_nFree = nFree; // update the original if (m_nMin > nFree) { m_nMin = nFree; // update minimum so far } QS_BEGIN_PRE_(QS_QF_EQUEUE_POST, qsId) QS_TIME_PRE_(); // timestamp QS_SIG_PRE_(e->sig); // the signal of this event QS_OBJ_PRE_(this); // this queue object QS_2U8_PRE_(e->getPoolNum_(), e->refCtr_); // poolNum & refCtr QS_EQC_PRE_(nFree); // # free entries QS_EQC_PRE_(m_nMin); // min # free entries QS_END_PRE_() if (m_frontEvt == nullptr) { // was the queue empty? m_frontEvt = e; // deliver event directly } else { // queue was not empty, insert event into the ring-buffer // insert event into the ring buffer (FIFO) m_ring[m_head] = e; // insert e into buffer // need to wrap? if (m_head == 0U) { m_head = m_end; // wrap around } m_head = (m_head - 1U); } status = true; // event posted successfully } else { // event cannot be posted // dropping events must be acceptable Q_ASSERT_INCRIT(210, margin != QF::NO_MARGIN); QS_BEGIN_PRE_(QS_QF_EQUEUE_POST_ATTEMPT, qsId) QS_TIME_PRE_(); // timestamp QS_SIG_PRE_(e->sig); // the signal of this event QS_OBJ_PRE_(this); // this queue object QS_2U8_PRE_(e->getPoolNum_(), e->refCtr_); // poolNum & refCtr QS_EQC_PRE_(nFree); // # free entries QS_EQC_PRE_(margin); // margin requested QS_END_PRE_() status = false; // event not posted } QF_MEM_APP(); QF_CRIT_EXIT(); return status; noexcept #ifndef Q_SPY Q_UNUSED_PAR(qsId); #endif QF_CRIT_STAT QF_CRIT_ENTRY(); QF_MEM_SYS(); QEQueueCtr nFree = m_nFree; // get volatile into temporary Q_REQUIRE_INCRIT(300, nFree != 0U); if (e->getPoolNum_() != 0U) { // is it a mutable event? QEvt_refCtr_inc_(e); // increment the reference counter } --nFree; // one free entry just used up m_nFree = nFree; // update the original if (m_nMin > nFree) { m_nMin = nFree; // update minimum so far } QS_BEGIN_PRE_(QS_QF_EQUEUE_POST_LIFO, qsId) QS_TIME_PRE_(); // timestamp QS_SIG_PRE_(e->sig); // the signal of this event QS_OBJ_PRE_(this); // this queue object QS_2U8_PRE_(e->getPoolNum_(), e->refCtr_); // poolNum & refCtr QS_EQC_PRE_(nFree); // # free entries QS_EQC_PRE_(m_nMin); // min # free entries QS_END_PRE_() QEvt const * const frontEvt = m_frontEvt; // read into temporary m_frontEvt = e; // deliver event directly to the front of the queue if (frontEvt != nullptr) { // was the queue not empty? m_tail = (m_tail + 1U); if (m_tail == m_end) { // need to wrap the tail? m_tail = 0U; // wrap around } m_ring[m_tail] = frontEvt; // buffer the old front evt } QF_MEM_APP(); QF_CRIT_EXIT(); noexcept #ifndef Q_SPY Q_UNUSED_PAR(qsId); #endif QF_CRIT_STAT QF_CRIT_ENTRY(); QF_MEM_SYS(); QEvt const * const e = m_frontEvt; // always remove evt from the front if (e != nullptr) { // was the queue not empty? QEQueueCtr const nFree = m_nFree + 1U; m_nFree = nFree; // update the # free // any events in the the ring buffer? if (nFree <= m_end) { m_frontEvt = m_ring[m_tail]; // remove from the tail if (m_tail == 0U) { // need to wrap? m_tail = m_end; // wrap around } m_tail = (m_tail - 1U); QS_BEGIN_PRE_(QS_QF_EQUEUE_GET, qsId) QS_TIME_PRE_(); // timestamp QS_SIG_PRE_(e->sig); // the signal of this event QS_OBJ_PRE_(this); // this queue object QS_2U8_PRE_(e->getPoolNum_(), e->refCtr_); // poolNum & refCtr QS_EQC_PRE_(nFree); // # free entries QS_END_PRE_() } else { m_frontEvt = nullptr; // queue becomes empty // all entries in the queue must be free (+1 for fronEvt) Q_ASSERT_INCRIT(410, nFree == (m_end + 1U)); QS_BEGIN_PRE_(QS_QF_EQUEUE_GET_LAST, qsId) QS_TIME_PRE_(); // timestamp QS_SIG_PRE_(e->sig); // the signal of this event QS_OBJ_PRE_(this); // this queue object QS_2U8_PRE_(e->getPoolNum_(), e->refCtr_); // poolNum & refCtr QS_END_PRE_() } } QF_MEM_APP(); QF_CRIT_EXIT(); return e; const noexcept return m_nFree; const noexcept return m_nMin; const noexcept return m_frontEvt == nullptr; = delete = delete : m_start(nullptr), m_end(nullptr), m_free_head(nullptr), m_blockSize(0U), m_nTot(0U), m_nFree(0U), m_nMin(0U) noexcept QF_CRIT_STAT QF_CRIT_ENTRY(); QF_MEM_SYS(); Q_REQUIRE_INCRIT(100, (poolSto != nullptr) && (poolSize >= static_cast<std::uint_fast32_t>(sizeof(QFreeBlock))) && (static_cast<std::uint_fast16_t>(blockSize + sizeof(QFreeBlock)) > blockSize)); m_free_head = static_cast<QFreeBlock *>(poolSto); // find # free blocks in a memory block, NO DIVISION m_blockSize = static_cast<QMPoolSize>(sizeof(QFreeBlock)); std::uint_fast16_t nblocks = 1U; while (m_blockSize < static_cast<QMPoolSize>(blockSize)) { m_blockSize += static_cast<QMPoolSize>(sizeof(QFreeBlock)); ++nblocks; } // the pool buffer must fit at least one rounded-up block Q_ASSERT_INCRIT(110, poolSize >= m_blockSize); // start at the head of the free list QFreeBlock *fb = m_free_head; m_nTot = 1U; // the last block already in the list // chain all blocks together in a free-list... for (std::uint_fast32_t size = poolSize - m_blockSize; size >= static_cast<std::uint_fast32_t>(m_blockSize); size -= static_cast<std::uint_fast32_t>(m_blockSize)) { fb->m_next = &fb[nblocks]; // point next link to next block #ifndef Q_UNSAFE fb->m_next_dis = ~Q_UINTPTR_CAST_(fb->m_next); #endif fb = fb->m_next; // advance to the next block ++m_nTot; // one more free block in the pool } fb->m_next = nullptr; // the last link points to NULL #ifndef Q_UNSAFE fb->m_next_dis = ~Q_UINTPTR_CAST_(fb->m_next); #endif fb->m_next = nullptr; // the last link points to NULL m_nFree = m_nTot; // all blocks are free m_nMin = m_nTot; // the minimum # free blocks m_start = static_cast<QFreeBlock *>(poolSto); // original start m_end = fb; // the last block in this pool QF_MEM_APP(); QF_CRIT_EXIT(); noexcept #ifndef Q_SPY Q_UNUSED_PAR(qsId); #endif QF_CRIT_STAT QF_CRIT_ENTRY(); QF_MEM_SYS(); // have more free blocks than the requested margin? QFreeBlock *fb; if (m_nFree > static_cast<QMPoolCtr>(margin)) { fb = m_free_head; // get a free block // a free block must be valid Q_ASSERT_INCRIT(300, fb != nullptr); QFreeBlock * const fb_next = fb->m_next; // the free block must have integrity (duplicate inverse storage) Q_ASSERT_INCRIT(302, Q_UINTPTR_CAST_(fb_next) == static_cast<std::uintptr_t>(~fb->m_next_dis)); m_nFree = (m_nFree - 1U); // one free block less if (m_nFree == 0U) { // is the pool becoming empty? // pool is becoming empty, so the next free block must be NULL Q_ASSERT_INCRIT(320, fb_next == nullptr); m_nMin = 0U; // remember that the pool got empty } else { // invariant: // The pool is not empty, so the next free-block pointer, // so the next free block must be in range. // NOTE: The next free block pointer can fall out of range // when the client code writes past the memory block, thus // corrupting the next block. Q_ASSERT_INCRIT(330, QF_PTR_RANGE_(fb_next, m_start, m_end)); // is the # free blocks the new minimum so far? if (m_nMin > m_nFree) { m_nMin = m_nFree; // remember the minimum so far } } m_free_head = fb_next; // set the head to the next free block QS_BEGIN_PRE_(QS_QF_MPOOL_GET, qsId) QS_TIME_PRE_(); // timestamp QS_OBJ_PRE_(this); // this memory pool QS_MPC_PRE_(m_nFree); // # of free blocks in the pool QS_MPC_PRE_(m_nMin); // min # free blocks ever in the pool QS_END_PRE_() } else { // don't have enough free blocks at this point fb = nullptr; QS_BEGIN_PRE_(QS_QF_MPOOL_GET_ATTEMPT, qsId) QS_TIME_PRE_(); // timestamp QS_OBJ_PRE_(this); // this memory pool QS_MPC_PRE_(m_nFree); // # of free blocks in the pool QS_MPC_PRE_(margin); // the requested margin QS_END_PRE_() } QF_MEM_APP(); QF_CRIT_EXIT(); return fb; // return the block or nullptr to the caller noexcept #ifndef Q_SPY Q_UNUSED_PAR(qsId); #endif QFreeBlock * const fb = static_cast<QFreeBlock *>(block); QF_CRIT_STAT QF_CRIT_ENTRY(); QF_MEM_SYS(); Q_REQUIRE_INCRIT(200, (m_nFree < m_nTot) && QF_PTR_RANGE_(fb, m_start, m_end)); fb->m_next = m_free_head; // link into list #ifndef Q_UNSAFE fb->m_next_dis = static_cast<std::uintptr_t>( ~Q_UINTPTR_CAST_(fb->m_next)); #endif // set as new head of the free list m_free_head = static_cast<QFreeBlock *>(block); m_nFree = m_nFree + 1U; // one more free block in this pool QS_BEGIN_PRE_(QS_QF_MPOOL_PUT, qsId) QS_TIME_PRE_(); // timestamp QS_OBJ_PRE_(this); // this memory pool QS_MPC_PRE_(m_nFree); // the # free blocks in the pool QS_END_PRE_() QF_MEM_APP(); QF_CRIT_EXIT(); const noexcept return m_blockSize; const noexcept return m_nMin; const noexcept return m_nFree; = delete = delete noexcept noexcept noexcept std::uint8_t *ptr = static_cast<std::uint8_t *>(start); for (std::uint_fast16_t n = len; n > 0U; --n) { *ptr = 0U; ++ptr; } noexcept //! @deprecated QActive::psInit(subscrSto, maxSignal); noexcept //! @deprecated QActive::publish_(e, sender, qsId); noexcept //! @deprecated QTimeEvt::tick(tickRate, sender); noexcept //! @deprecated return QActive::getQueueMin(prio); {0xFFFFU}; noexcept std::uint_fast8_t const poolNum = priv_.maxPool_; // see precondition{qf_dyn,200} and precondition{qf_dyn,201} QF_CRIT_STAT QF_CRIT_ENTRY(); QF_MEM_SYS(); Q_REQUIRE_INCRIT(200, poolNum < QF_MAX_EPOOL); if (poolNum > 0U) { Q_REQUIRE_INCRIT(201, QF_EPOOL_EVENT_SIZE_(priv_.ePool_[poolNum - 1U]) < evtSize); } priv_.maxPool_ = poolNum + 1U; // one more pool QF_MEM_APP(); QF_CRIT_EXIT(); // perform the port-dependent initialization of the event-pool QF_EPOOL_INIT_(priv_.ePool_[poolNum], poolSto, poolSize, evtSize); #ifdef Q_SPY // generate the object-dictionary entry for the initialized pool { std::uint8_t obj_name[9] = "EvtPool?"; obj_name[7] = static_cast<std::uint8_t>( static_cast<std::uint8_t>('0') + static_cast<std::uint8_t>(poolNum + 1U)); QS::obj_dict_pre_(&priv_.ePool_[poolNum], reinterpret_cast<char *>(&obj_name[0])); } #endif // Q_SPY noexcept QF_CRIT_STAT QF_CRIT_ENTRY(); QF_MEM_SYS(); std::uint_fast16_t const max_size = QF_EPOOL_EVENT_SIZE_(priv_.ePool_[priv_.maxPool_ - 1U]); QF_MEM_APP(); QF_CRIT_EXIT(); return max_size; noexcept QF_CRIT_STAT QF_CRIT_ENTRY(); QF_MEM_SYS(); Q_REQUIRE_INCRIT(400, (poolNum <= QF_MAX_EPOOL) && (0U < poolNum) && (poolNum <= priv_.maxPool_)); std::uint_fast16_t const min = static_cast<std::uint_fast16_t>( priv_.ePool_[poolNum - 1U].getNMin()); QF_MEM_APP(); QF_CRIT_EXIT(); return min; noexcept QF_CRIT_STAT QF_CRIT_ENTRY(); QF_MEM_SYS(); // find the pool id that fits the requested event size... std::uint_fast8_t poolNum = 0U; // zero-based poolNum initially for (; poolNum < priv_.maxPool_; ++poolNum) { if (evtSize <= QF_EPOOL_EVENT_SIZE_(priv_.ePool_[poolNum])) { break; } } // precondition: // - cannot run out of registered pools Q_REQUIRE_INCRIT(300, poolNum < priv_.maxPool_); ++poolNum; // convert to 1-based poolNum QF_MEM_APP(); QF_CRIT_EXIT(); // get event e (port-dependent)... QEvt *e; #ifdef Q_SPY QF_EPOOL_GET_(priv_.ePool_[poolNum - 1U], e, ((margin != NO_MARGIN) ? margin : 0U), static_cast<std::uint_fast8_t>(QS_EP_ID) + poolNum); #else QF_EPOOL_GET_(priv_.ePool_[poolNum - 1U], e, ((margin != NO_MARGIN) ? margin : 0U), 0U); #endif if (e != nullptr) { // was e allocated correctly? e->sig = static_cast<QSignal>(sig); // set the signal e->refCtr_ = 0U; // initialize the reference counter to 0 e->evtTag_ = static_cast<std::uint8_t>(QEvt::MARKER | poolNum); QS_CRIT_ENTRY(); QS_MEM_SYS(); QS_BEGIN_PRE_(QS_QF_NEW, static_cast<std::uint_fast8_t>(QS_EP_ID) + poolNum) QS_TIME_PRE_(); // timestamp QS_EVS_PRE_(evtSize); // the size of the event QS_SIG_PRE_(sig); // the signal of the event QS_END_PRE_() QS_MEM_APP(); QS_CRIT_EXIT(); } else { // event was not allocated QF_CRIT_ENTRY(); // This assertion means that the event allocation failed, // and this failure cannot be tolerated. The most frequent // reason is an event leak in the application. Q_ASSERT_INCRIT(320, margin != NO_MARGIN); QS_MEM_SYS(); QS_BEGIN_PRE_(QS_QF_NEW_ATTEMPT, static_cast<std::uint_fast8_t>(QS_EP_ID) + poolNum) QS_TIME_PRE_(); // timestamp QS_EVS_PRE_(evtSize); // the size of the event QS_SIG_PRE_(sig); // the signal of the event QS_END_PRE_() QS_MEM_APP(); QF_CRIT_EXIT(); } // the returned event e is guaranteed to be valid (not NULL) // if we can't tolerate failed allocation return e; noexcept QF_CRIT_STAT QF_CRIT_ENTRY(); Q_REQUIRE_INCRIT(402, QEvt::verify_(e)); std::uint_fast8_t const poolNum = e->getPoolNum_(); if (poolNum != 0U) { // is it a pool event (mutable)? QF_MEM_SYS(); if (e->refCtr_ > 1U) { // isn't this the last reference? QS_BEGIN_PRE_(QS_QF_GC_ATTEMPT, static_cast<std::uint_fast8_t>(QS_EP_ID) + poolNum) QS_TIME_PRE_(); // timestamp QS_SIG_PRE_(e->sig); // the signal of the event QS_2U8_PRE_(poolNum, e->refCtr_); // poolNum & refCtr QS_END_PRE_() QEvt_refCtr_dec_(e); // decrement the ref counter QF_MEM_APP(); QF_CRIT_EXIT(); } else { // this is the last reference to this event, recycle it QS_BEGIN_PRE_(QS_QF_GC, static_cast<std::uint_fast8_t>(QS_EP_ID) + poolNum) QS_TIME_PRE_(); // timestamp QS_SIG_PRE_(e->sig); // the signal of the event QS_2U8_PRE_(poolNum, e->refCtr_); // poolNum & refCtr QS_END_PRE_() // pool number must be in range Q_ASSERT_INCRIT(410, (poolNum <= priv_.maxPool_) && (poolNum <= QF_MAX_EPOOL)); QF_MEM_APP(); QF_CRIT_EXIT(); // NOTE: casting 'const' away is legit because it's a pool event #ifdef Q_SPY QF_EPOOL_PUT_(priv_.ePool_[poolNum - 1U], QF_CONST_CAST_(QEvt*, e), static_cast<std::uint_fast8_t>(QS_EP_ID) + poolNum); #else QF_EPOOL_PUT_(priv_.ePool_[poolNum - 1U], QF_CONST_CAST_(QEvt*, e), 0U); #endif } } else { QF_CRIT_EXIT(); } noexcept #ifdef Q_UNSAFE Q_UNUSED_PAR(evtRef); #endif QF_CRIT_STAT QF_CRIT_ENTRY(); Q_REQUIRE_INCRIT(502, QEvt::verify_(e)); std::uint_fast8_t const poolNum = e->getPoolNum_(); Q_REQUIRE_INCRIT(500, (poolNum != 0U) && (evtRef == nullptr)); QEvt_refCtr_inc_(e); // increments the ref counter QS_MEM_SYS(); QS_BEGIN_PRE_(QS_QF_NEW_REF, static_cast<std::uint_fast8_t>(QS_EP_ID) + poolNum) QS_TIME_PRE_(); // timestamp QS_SIG_PRE_(e->sig); // the signal of the event QS_2U8_PRE_(poolNum, e->refCtr_); // poolNum & refCtr QS_END_PRE_() QS_MEM_APP(); QF_CRIT_EXIT(); return e; noexcept QEvt const * const e = evtRef; QF_CRIT_STAT QF_CRIT_ENTRY(); Q_REQUIRE_INCRIT(602, QEvt::verify_(e)); #ifdef Q_SPY std::uint_fast8_t const poolNum = e->getPoolNum_(); #endif QS_MEM_SYS(); QS_BEGIN_PRE_(QS_QF_DELETE_REF, static_cast<std::uint_fast8_t>(QS_EP_ID) + poolNum) QS_TIME_PRE_(); // timestamp QS_SIG_PRE_(e->sig); // the signal of the event QS_2U8_PRE_(poolNum, e->refCtr_); // poolNum & refCtr QS_END_PRE_() QS_MEM_APP(); QF_CRIT_EXIT(); #if (QF_MAX_EPOOL > 0U) gc(evtRef); // recycle the referenced event #endif noexcept noexcept \ (static_cast<QP::QPrioSpec>((prio_) | (pthre_) << 8U)) (static_cast<evtT_ *>( \ QP::QF::newX_(sizeof(evtT_), QP::QF::NO_MARGIN, (sig_)))) ( static_cast<evtT_ *>( \ QP::QF::newX_(sizeof(evtT_), QP::QF::NO_MARGIN, (sig_)))->ctor(__VA_ARGS__)) (static_cast<evtT_ *>( \ QP::QF::newX_(sizeof(evtT_), (margin_), (sig_)))) ( static_cast<evtT_ *>( \ QP::QF::newX_(sizeof(evtT_), (margin_), (sig_)))->ctor(__VA_ARGS__)) \ ((evtRef_) = static_cast<evtT_ const *>(QP::QF::newRef_(e, (evtRef_)))) do { \ QP::QF::deleteRef_((evtRef_)); \ (evtRef_) = 0U; \ } while (false) \ publish_((e_), (sender_), (sender_)->getPrio()) publish_((e_), nullptr, 0U) post_((e_), QP::QF::NO_MARGIN, (sender_)) post_((e_), QP::QF::NO_MARGIN, nullptr) \ post_((e_), (margin_), (sender_)) post_((e_), (margin_), nullptr) tick((tickRate_), (sender_)) tick((tickRate_), nullptr) TICK_X(0U, (sender_)) trig_((sender_)) trig_(nullptr) (static_cast<void>(0)) (static_cast<void>(0)) (static_cast<void>(0)) Native QF event pool QMPool Native QF event pool initialization \ (p_).init((poolSto_), (poolSize_), (evtSize_)) Native QF event pool event-size getter ((p_).getBlockSize()) Native QF event pool get-event \ ((e_) = static_cast<QEvt *>((p_).get((m_), (qsId_)))) Native QF event pool put-event ((p_).put((e_), (qsId_))) QF_CRIT_STAT QF_CRIT_ENTRY(); QF_MEM_SYS(); Q_ASSERT_INCRIT(102, priv_.schedCeil == static_cast<std::uint_fast8_t>(~priv_.schedCeil_dis)); if (ceiling > priv_.schedCeil) { // raising the scheduler ceiling? QS_BEGIN_PRE_(QS_SCHED_LOCK, 0U) QS_TIME_PRE_(); // timestamp // the previous sched ceiling & new sched ceiling QS_2U8_PRE_(static_cast<std::uint8_t>(priv_.schedCeil), static_cast<std::uint8_t>(ceiling)); QS_END_PRE_() priv_.schedCeil = ceiling; #ifndef Q_UNSAFE priv_.schedCeil_dis = static_cast<std::uint_fast8_t>(~ceiling); #endif } QF_MEM_APP(); QF_CRIT_EXIT(); QF_CRIT_STAT QF_CRIT_ENTRY(); QF_MEM_SYS(); Q_ASSERT_INCRIT(202, priv_.schedCeil == static_cast<std::uint_fast8_t>(~priv_.schedCeil_dis)); if (priv_.schedCeil != 0U) { // actually enabling the scheduler? QS_BEGIN_PRE_(QS_SCHED_UNLOCK, 0U) QS_TIME_PRE_(); // timestamp // current sched ceiling (old), previous sched ceiling (new) QS_2U8_PRE_(static_cast<std::uint8_t>(priv_.schedCeil), 0U); QS_END_PRE_() priv_.schedCeil = 0U; #ifndef Q_UNSAFE priv_.schedCeil_dis = ~static_cast<std::uint_fast8_t>(0U); #endif } QF_MEM_APP(); QF_CRIT_EXIT(); bzero_(&QF::priv_, sizeof(QF::priv_)); bzero_(&QV::priv_, sizeof(QV::priv_)); bzero_(&QActive::registry_[0], sizeof(QActive::registry_)); #ifndef Q_UNSAFE QV::priv_.readySet.update_(&QV::priv_.readySet_dis); QV::priv_.schedCeil_dis = ~static_cast<std::uint_fast8_t>(0U); #endif #ifdef QV_INIT QV_INIT(); // port-specific initialization of the QV kernel #endif onCleanup(); // cleanup callback // nothing else to do for the QV kernel #ifdef Q_SPY // produce the QS_QF_RUN trace record QF_INT_DISABLE(); QF_MEM_SYS(); QS::beginRec_(QS_REC_NUM_(QS_QF_RUN)); QS::endRec_(); QF_MEM_APP(); QF_INT_ENABLE(); #endif // Q_SPY onStartup(); // application-specific startup callback QF_INT_DISABLE(); QF_MEM_SYS(); #ifdef QV_START QV_START(); // port-specific startup of the QV kernel #endif #if (defined QF_ON_CONTEXT_SW) || (defined Q_SPY) std::uint_fast8_t pprev = 0U; // previous prio. #endif for (;;) { // QV event loop... // check internal integrity (duplicate inverse storage) Q_ASSERT_INCRIT(302, QV::priv_.readySet.verify_(&QV::priv_.readySet_dis)); // check internal integrity (duplicate inverse storage) Q_ASSERT_INCRIT(303, QV::priv_.schedCeil == static_cast<std::uint_fast8_t>(~QV::priv_.schedCeil_dis)); // find the maximum prio. AO ready to run std::uint_fast8_t const p = (QV::priv_.readySet.notEmpty() ? QV::priv_.readySet.findMax() : 0U); if (p > QV::priv_.schedCeil) { // is it above the sched ceiling? QActive * const a = QActive::registry_[p]; #if (defined QF_ON_CONTEXT_SW) || (defined Q_SPY) QS_BEGIN_PRE_(QS_SCHED_NEXT, p) QS_TIME_PRE_(); // timestamp QS_2U8_PRE_(static_cast<std::uint8_t>(p), static_cast<std::uint8_t>(pprev)); QS_END_PRE_() #ifdef QF_ON_CONTEXT_SW QF_onContextSw(((pprev != 0U) ? QActive::registry_[pprev] : nullptr), a); #endif // QF_ON_CONTEXT_SW pprev = p; // update previous prio. #endif // (defined QF_ON_CONTEXT_SW) || (defined Q_SPY) QF_MEM_APP(); QF_INT_ENABLE(); QEvt const * const e = a->get_(); // NOTE QActive::get_() performs QF_MEM_APP() before return // dispatch event (virtual call) a->dispatch(e, a->getPrio()); #if (QF_MAX_EPOOL > 0U) gc(e); #endif QF_INT_DISABLE(); QF_MEM_SYS(); if (a->getEQueue().isEmpty()) { // empty queue? QV::priv_.readySet.remove(p); #ifndef Q_UNSAFE QV::priv_.readySet.update_(&QV::priv_.readySet_dis); #endif } } else { // no AO ready to run --> idle #if (defined QF_ON_CONTEXT_SW) || (defined Q_SPY) if (pprev != 0U) { QS_BEGIN_PRE_(QS_SCHED_IDLE, pprev) QS_TIME_PRE_(); // timestamp QS_U8_PRE_(static_cast<std::uint8_t>(pprev)); QS_END_PRE_() #ifdef QF_ON_CONTEXT_SW QF_onContextSw(QActive::registry_[pprev], nullptr); #endif // QF_ON_CONTEXT_SW pprev = 0U; // update previous prio } #endif // (defined QF_ON_CONTEXT_SW) || (defined Q_SPY) QF_MEM_APP(); // QV::onIdle() must be called with interrupts DISABLED // because the determination of the idle condition (all event // queues empty) can change at any time by an interrupt posting // events to a queue. // // NOTE: QV::onIdle() MUST enable interrupts internally, // ideally at the same time as putting the CPU into a power- // saving mode. QV::onIdle(); QF_INT_DISABLE(); QF_MEM_SYS(); } } #ifdef __GNUC__ // GNU compiler? return 0; #endif Q_UNUSED_PAR(stkSto); // not needed in QV Q_UNUSED_PAR(stkSize); // not needed in QV QF_CRIT_STAT QF_CRIT_ENTRY(); Q_REQUIRE_INCRIT(300, stkSto == nullptr); QF_CRIT_EXIT(); m_prio = static_cast<std::uint8_t>(prioSpec & 0xFFU); // QF-prio. m_pthre = 0U; // not used register_(); // make QF aware of this AO m_eQueue.init(qSto, qLen); // initialize QEQueue of this AO this->init(par, m_prio); // take the top-most initial tran. (virtual) QS_FLUSH(); // flush the trace buffer to the host (static_cast<void>(0)) (static_cast<void>(0)) \ Q_ASSERT_INCRIT(310, (me_)->m_eQueue.m_frontEvt != nullptr) \ QV::priv_.readySet.insert((me_)->m_prio); \ QV::priv_.readySet.update_(&QV::priv_.readySet_dis) \ (QV::priv_.readySet.insert((me_)->m_prio)) = std::uint_fast8_t; noexcept QF_CRIT_STAT QF_CRIT_ENTRY(); QF_MEM_SYS(); Q_REQUIRE_INCRIT(100, !QK_ISR_CONTEXT_()); Q_REQUIRE_INCRIT(102, QK_priv_.lockCeil == static_cast<std::uint_fast8_t>(~QK_priv_.lockCeil_dis)); // first store the previous lock prio QSchedStatus stat; if (ceiling > QK_priv_.lockCeil) { // raising the lock ceiling? QS_BEGIN_PRE_(QS_SCHED_LOCK, QK_priv_.actPrio) QS_TIME_PRE_(); // timestamp // the previous lock ceiling & new lock ceiling QS_2U8_PRE_(static_cast<std::uint8_t>(QK_priv_.lockCeil), static_cast<std::uint8_t>(ceiling)); QS_END_PRE_() // previous status of the lock stat = static_cast<QSchedStatus>(QK_priv_.lockCeil); // new status of the lock QK_priv_.lockCeil = ceiling; #ifndef Q_UNSAFE QK_priv_.lockCeil_dis = static_cast<std::uint_fast8_t>(~ceiling); #endif } else { stat = 0xFFU; // scheduler not locked } QF_MEM_APP(); QF_CRIT_EXIT(); return stat; // return the status to be saved in a stack variable noexcept // has the scheduler been actually locked by the last QK::schedLock()? if (prevCeil != 0xFFU) { QF_CRIT_STAT QF_CRIT_ENTRY(); QF_MEM_SYS(); Q_REQUIRE_INCRIT(202, QK_priv_.lockCeil == static_cast<std::uint_fast8_t>(~QK_priv_.lockCeil_dis)); Q_REQUIRE_INCRIT(210, (!QK_ISR_CONTEXT_()) && (QK_priv_.lockCeil > prevCeil)); QS_BEGIN_PRE_(QS_SCHED_UNLOCK, QK_priv_.actPrio) QS_TIME_PRE_(); // timestamp // current lock ceiling (old), previous lock ceiling (new) QS_2U8_PRE_(static_cast<std::uint8_t>(QK_priv_.lockCeil), static_cast<std::uint8_t>(prevCeil)); QS_END_PRE_() // restore the previous lock ceiling QK_priv_.lockCeil = prevCeil; #ifndef Q_UNSAFE QK_priv_.lockCeil_dis = static_cast<std::uint_fast8_t>(~prevCeil); #endif // find if any AOs should be run after unlocking the scheduler if (QK_sched_() != 0U) { // preemption needed? QK_activate_(); // activate any unlocked AOs } QF_MEM_APP(); QF_CRIT_EXIT(); } bzero_(&QF::priv_, sizeof(QF::priv_)); bzero_(&QK_priv_, sizeof(QK_priv_)); bzero_(&QActive::registry_[0], sizeof(QActive::registry_)); // setup the QK scheduler as initially locked and not running QK_priv_.lockCeil = (QF_MAX_ACTIVE + 1U); // scheduler locked #ifndef Q_UNSAFE QK_priv_.readySet.update_(&QK_priv_.readySet_dis); QK_priv_.actPrio_dis = static_cast<std::uint_fast8_t>(~QK_priv_.actPrio); QK_priv_.nextPrio_dis = static_cast<std::uint_fast8_t>(~QK_priv_.nextPrio); QK_priv_.actThre_dis = static_cast<std::uint_fast8_t>(~QK_priv_.actThre); QK_priv_.lockCeil_dis = static_cast<std::uint_fast8_t>(~QK_priv_.lockCeil); #endif #ifdef QK_INIT QK_INIT(); // port-specific initialization of the QK kernel #endif onCleanup(); // cleanup callback // nothing else to do for the QK preemptive kernel #ifdef Q_SPY // produce the QS_QF_RUN trace record QF_INT_DISABLE(); QF_MEM_SYS(); QS::beginRec_(QS_REC_NUM_(QS_QF_RUN)); QS::endRec_(); QF_MEM_APP(); QF_INT_ENABLE(); #endif // Q_SPY onStartup(); // application-specific startup callback QF_INT_DISABLE(); QF_MEM_SYS(); #ifdef QK_START QK_START(); // port-specific startup of the QK kernel #endif QK_priv_.lockCeil = 0U; // unlock the QK scheduler #ifndef Q_UNSAFE QK_priv_.lockCeil_dis = static_cast<std::uint_fast8_t>(~QK_priv_.lockCeil); #endif // activate AOs to process events posted so far if (QK_sched_() != 0U) { QK_activate_(); } QF_MEM_APP(); QF_INT_ENABLE(); for (;;) { // QK idle loop... QK::onIdle(); // application-specific QK on-idle callback } #ifdef __GNUC__ // GNU compiler? return 0; #endif Q_UNUSED_PAR(stkSto); // not needed in QK Q_UNUSED_PAR(stkSize); // not needed in QK QF_CRIT_STAT QF_CRIT_ENTRY(); QF_MEM_SYS(); Q_REQUIRE_INCRIT(300, (!QK_ISR_CONTEXT_()) && (stkSto == nullptr)); QF_MEM_APP(); QF_CRIT_EXIT(); m_prio = static_cast<std::uint8_t>(prioSpec & 0xFFU); // QF-prio. m_pthre = static_cast<std::uint8_t>(prioSpec >> 8U); // preemption-thre. register_(); // make QF aware of this AO m_eQueue.init(qSto, qLen); // init the built-in queue // top-most initial tran. (virtual call) this->init(par, m_prio); QS_FLUSH(); // flush the trace buffer to the host // See if this AO needs to be scheduled if QK is already running QF_CRIT_ENTRY(); QF_MEM_SYS(); if (QK_sched_() != 0U) { // activation needed? QK_activate_(); } QF_MEM_APP(); QF_CRIT_EXIT(); noexcept // NOTE: this function is entered with interrupts DISABLED Q_REQUIRE_INCRIT(402, QK_priv_.readySet.verify_(&QK_priv_.readySet_dis)); std::uint_fast8_t p; if (QK_priv_.readySet.isEmpty()) { p = 0U; // no activation needed } else { // find the highest-prio AO with non-empty event queue p = QK_priv_.readySet.findMax(); Q_ASSERT_INCRIT(412, QK_priv_.actThre == static_cast<std::uint_fast8_t>(~QK_priv_.actThre_dis)); // is the AO's prio. below the active preemption-threshold? if (p <= QK_priv_.actThre) { p = 0U; // no activation needed } else { Q_ASSERT_INCRIT(422, QK_priv_.lockCeil == static_cast<std::uint_fast8_t>(~QK_priv_.lockCeil_dis)); // is the AO's prio. below the lock-ceiling? if (p <= QK_priv_.lockCeil) { p = 0U; // no activation needed } else { Q_ASSERT_INCRIT(432, QK_priv_.nextPrio == static_cast<std::uint_fast8_t>(~QK_priv_.nextPrio_dis)); QK_priv_.nextPrio = p; // next AO to run #ifndef Q_UNSAFE QK_priv_.nextPrio_dis = static_cast<std::uint_fast8_t>(~QK_priv_.nextPrio); #endif } } } return p; noexcept // NOTE: this function is entered with interrupts DISABLED std::uint_fast8_t const prio_in = QK_priv_.actPrio; // save initial prio. std::uint_fast8_t p = QK_priv_.nextPrio; // next prio to run Q_REQUIRE_INCRIT(502, (prio_in == static_cast<std::uint_fast8_t>(~QK_priv_.actPrio_dis)) && (p == static_cast<std::uint_fast8_t>(~QK_priv_.nextPrio_dis))); Q_REQUIRE_INCRIT(510, (prio_in <= QF_MAX_ACTIVE) && (0U < p) && (p <= QF_MAX_ACTIVE)); #if (defined QF_ON_CONTEXT_SW) || (defined Q_SPY) std::uint_fast8_t pprev = prio_in; #endif // QF_ON_CONTEXT_SW || Q_SPY QK_priv_.nextPrio = 0U; // clear for the next time #ifndef Q_UNSAFE QK_priv_.nextPrio_dis = static_cast<std::uint_fast8_t>(~QK_priv_.nextPrio); #endif std::uint_fast8_t pthre_in; QP::QActive *a; if (prio_in == 0U) { // preempting the idle thread? pthre_in = 0U; } else { a = QP::QActive::registry_[prio_in]; Q_ASSERT_INCRIT(510, a != nullptr); pthre_in = static_cast<std::uint_fast8_t>(a->getPThre()); Q_ASSERT_INCRIT(511, pthre_in == static_cast<std::uint_fast8_t>( ~static_cast<std::uint_fast8_t>(a->m_pthre_dis) & 0xFFU)); } // loop until no more ready-to-run AOs of higher pthre than the initial do { a = QP::QActive::registry_[p]; // obtain the pointer to the AO Q_ASSERT_INCRIT(520, a != nullptr); // the AO must be registered std::uint_fast8_t const pthre = static_cast<std::uint_fast8_t>(a->getPThre()); Q_ASSERT_INCRIT(522, pthre == static_cast<std::uint_fast8_t>( ~static_cast<std::uint_fast8_t>(a->m_pthre_dis) & 0xFFU)); // set new active prio. and preemption-threshold QK_priv_.actPrio = p; QK_priv_.actThre = pthre; #ifndef Q_UNSAFE QK_priv_.actPrio_dis = static_cast<std::uint_fast8_t>(~p); QK_priv_.actThre_dis = static_cast<std::uint_fast8_t>(~pthre); #endif #if (defined QF_ON_CONTEXT_SW) || (defined Q_SPY) if (p != pprev) { // changing threads? QS_BEGIN_PRE_(QP::QS_SCHED_NEXT, p) QS_TIME_PRE_(); // timestamp QS_2U8_PRE_(p, // prio. of the scheduled AO pprev); // previous prio. QS_END_PRE_() #ifdef QF_ON_CONTEXT_SW QF_onContextSw(QP::QActive::registry_[pprev], a); #endif // QF_ON_CONTEXT_SW pprev = p; // update previous prio. } #endif // QF_ON_CONTEXT_SW || Q_SPY QF_INT_ENABLE(); // unconditionally enable interrupts QP::QEvt const * const e = a->get_(); // NOTE QActive_get_() performs QF_MEM_APP() before return // dispatch event (virtual call) a->dispatch(e, a->getPrio()); #if (QF_MAX_EPOOL > 0U) QP::QF::gc(e); #endif // determine the next highest-prio. AO ready to run... QF_INT_DISABLE(); // unconditionally disable interrupts QF_MEM_SYS(); // internal integrity check (duplicate inverse storage) Q_ASSERT_INCRIT(532, QK_priv_.readySet.verify_(&QK_priv_.readySet_dis)); if (a->getEQueue().isEmpty()) { // empty queue? QK_priv_.readySet.remove(p); #ifndef Q_UNSAFE QK_priv_.readySet.update_(&QK_priv_.readySet_dis); #endif } if (QK_priv_.readySet.isEmpty()) { p = 0U; // no activation needed } else { // find new highest-prio AO ready to run... p = QK_priv_.readySet.findMax(); // is the new prio. below the initial preemption-threshold? if (p <= pthre_in) { p = 0U; // no activation needed } else { Q_ASSERT_INCRIT(542, QK_priv_.lockCeil == ~QK_priv_.lockCeil_dis); // is the AO's prio. below the lock preemption-threshold? if (p <= QK_priv_.lockCeil) { p = 0U; // no activation needed } else { Q_ASSERT_INCRIT(550, p <= QF_MAX_ACTIVE); } } } } while (p != 0U); // restore the active prio. and preemption-threshold QK_priv_.actPrio = prio_in; QK_priv_.actThre = pthre_in; #ifndef Q_UNSAFE QK_priv_.actPrio_dis = static_cast<std::uint_fast8_t>(~QK_priv_.actPrio); QK_priv_.actThre_dis = static_cast<std::uint_fast8_t>(~QK_priv_.actThre); #endif #if (defined QF_ON_CONTEXT_SW) || (defined Q_SPY) if (prio_in != 0U) { // resuming an active object? a = QP::QActive::registry_[prio_in]; // pointer to preempted AO QS_BEGIN_PRE_(QP::QS_SCHED_NEXT, prio_in) QS_TIME_PRE_(); // timestamp // prio. of the resumed AO, previous prio. QS_2U8_PRE_(prio_in, pprev); QS_END_PRE_() } else { // resuming prio.==0 --> idle a = nullptr; // QK idle loop QS_BEGIN_PRE_(QP::QS_SCHED_IDLE, pprev) QS_TIME_PRE_(); // timestamp QS_U8_PRE_(pprev); // previous prio. QS_END_PRE_() } #ifdef QF_ON_CONTEXT_SW QF_onContextSw(QP::QActive::registry_[pprev], a); #endif // QF_ON_CONTEXT_SW #endif // QF_ON_CONTEXT_SW || Q_SPY QSchedStatus lockStat_; do { \ if (QK_ISR_CONTEXT_()) { \ lockStat_ = 0xFFU; \ } else { \ lockStat_ = QK::schedLock((ceil_)); \ } \ } while (false) do { \ if (lockStat_ != 0xFFU) { \ QK::schedUnlock(lockStat_); \ } \ } while (false) \ Q_ASSERT_INCRIT(320, (me_)->m_eQueue.m_frontEvt != nullptr) do { \ QK_priv_.readySet.insert( \ static_cast<std::uint_fast8_t>((me_)->m_prio)); \ QK_priv_.readySet.update_(&QK_priv_.readySet_dis); \ if (!QK_ISR_CONTEXT_()) { \ if (QK_sched_() != 0U) { \ QK_activate_(); \ } \ } \ } while (false) do { \ QK_priv_.readySet.insert( \ static_cast<std::uint_fast8_t>((me_)->m_prio)); \ if (!QK_ISR_CONTEXT_()) { \ if (QK_sched_() != 0U) { \ QK_activate_(); \ } \ } \ } while (false) = std::uint_fast16_t; {0U}; noexcept QF_CRIT_STAT QF_CRIT_ENTRY(); QF_MEM_SYS(); Q_REQUIRE_INCRIT(100, !QXK_ISR_CONTEXT_()); QSchedStatus stat; // saved lock status to be returned // is the lock ceiling being raised? if (ceiling > QXK_priv_.lockCeil) { QS_BEGIN_PRE_(QS_SCHED_LOCK, 0U) QS_TIME_PRE_(); // timestamp // the previous lock ceiling & new lock ceiling QS_2U8_PRE_(static_cast<std::uint8_t>(QXK_priv_.lockCeil), static_cast<std::uint8_t>(ceiling)); QS_END_PRE_() // previous status of the lock stat = static_cast<QSchedStatus>(QXK_priv_.lockHolder); stat |= static_cast<QSchedStatus>(QXK_priv_.lockCeil) << 8U; // new status of the lock QXK_priv_.lockHolder = (QXK_priv_.curr != nullptr) ? QXK_priv_.curr->getPrio() : 0U; QXK_priv_.lockCeil = ceiling; } else { stat = 0xFFU; // scheduler not locked } QF_MEM_APP(); QF_CRIT_EXIT(); return stat; // return the status to be saved in a stack variable noexcept // has the scheduler been actually locked by the last QXK::schedLock()? if (stat != 0xFFU) { std::uint8_t const prevCeil = static_cast<std::uint8_t>(stat >> 8U); QF_CRIT_STAT QF_CRIT_ENTRY(); QF_MEM_SYS(); Q_REQUIRE_INCRIT(200, !QXK_ISR_CONTEXT_()); Q_REQUIRE_INCRIT(201, QXK_priv_.lockCeil > prevCeil); QS_BEGIN_PRE_(QS_SCHED_UNLOCK, 0U) QS_TIME_PRE_(); // timestamp // ceiling before unlocking & prio after unlocking QS_2U8_PRE_(QXK_priv_.lockCeil, prevCeil); QS_END_PRE_() // restore the previous lock ceiling and lock holder QXK_priv_.lockCeil = prevCeil; QXK_priv_.lockHolder = (stat & 0xFFU); // find if any threads should be run after unlocking the scheduler if (QXK_sched_() != 0U) { // activation needed? QXK_activate_(); // synchronously activate basic-thred(s) } QF_MEM_APP(); QF_CRIT_EXIT(); } noexcept QF_CRIT_STAT QF_CRIT_ENTRY(); QF_MEM_SYS(); Q_REQUIRE_INCRIT(600, QXK_priv_.lockCeil <= QF_MAX_ACTIVE); QP::QActive *curr = QXK_priv_.curr; if (curr == nullptr) { // basic thread? curr = QP::QActive::registry_[QXK_priv_.actPrio]; } Q_ASSERT_INCRIT(690, curr != nullptr); QF_MEM_APP(); QF_CRIT_EXIT(); return curr; {0U}; noexcept : QActive(Q_STATE_CAST(handler)), m_timeEvt(this, static_cast<QSignal>(QXK::DELAY_SIG), tickRate) m_state.act = nullptr; // mark as extended thread override Q_UNUSED_PAR(e); Q_UNUSED_PAR(qsId); Q_ERROR_INCRIT(110); override this->init(nullptr, qsId); override Q_UNUSED_PAR(e); Q_UNUSED_PAR(qsId); Q_ERROR_INCRIT(120); const noexcept return &m_timeEvt; noexcept QF_CRIT_STAT QF_CRIT_ENTRY(); QF_MEM_SYS(); QXThread * const thr = QXK_PTR_CAST_(QXThread*, QXK_priv_.curr); // precondition, this function: // - must NOT be called from an ISR; // - number of ticks cannot be zero // - be called from an extended thread; // - the thread must NOT be already blocked on any object. Q_REQUIRE_INCRIT(800, (!QXK_ISR_CONTEXT_()) && (nTicks != 0U) && (thr != nullptr) && (thr->m_temp.obj == nullptr)); // - the thread must NOT be holding a scheduler lock. Q_REQUIRE_INCRIT(801, QXK_priv_.lockHolder != thr->m_prio); // remember the blocking object thr->m_temp.obj = QXK_PTR_CAST_(QMState const*, &thr->m_timeEvt); thr->teArm_(static_cast<enum_t>(QXK::DELAY_SIG), nTicks); thr->block_(); QF_MEM_APP(); QF_CRIT_EXIT(); QF_CRIT_EXIT_NOP(); // BLOCK here // after unblocking... QF_CRIT_ENTRY(); QF_MEM_SYS(); // the blocking object must be the time event Q_ASSERT_INCRIT(890, thr->m_temp.obj == QXK_PTR_CAST_(QMState*, &thr->m_timeEvt)); thr->m_temp.obj = nullptr; // clear QF_MEM_APP(); QF_CRIT_EXIT(); // signal of zero means that the time event was posted without // being canceled. return (thr->m_timeEvt.sig == 0U); noexcept QF_CRIT_STAT QF_CRIT_ENTRY(); QF_MEM_SYS(); bool wasArmed; if (m_temp.obj == QXK_PTR_CAST_(QMState*, &m_timeEvt)) { wasArmed = teDisarm_(); unblock_(); } else { wasArmed = false; } QF_MEM_APP(); QF_CRIT_EXIT(); return wasArmed; noexcept QF_CRIT_STAT QF_CRIT_ENTRY(); QF_MEM_SYS(); QXThread * const thr = QXTHREAD_CAST_(QXK_priv_.curr); // precondition, this function: // - must NOT be called from an ISR; // - be called from an extended thread; // - the thread must NOT be already blocked on any object. Q_REQUIRE_INCRIT(500, (!QXK_ISR_CONTEXT_()) && (thr != nullptr) && (thr->m_temp.obj == nullptr)); // - the thread must NOT be holding a scheduler lock. Q_REQUIRE_INCRIT(501, QXK_priv_.lockHolder != thr->m_prio); // is the queue empty? if (thr->m_eQueue.m_frontEvt == nullptr) { // remember the blocking object (the thread's queue) thr->m_temp.obj = QXK_PTR_CAST_(QMState*, &thr->m_eQueue); thr->teArm_(static_cast<enum_t>(QXK::TIMEOUT_SIG), nTicks); QXK_priv_.readySet.remove( static_cast<std::uint_fast8_t>(thr->m_prio)); #ifndef Q_UNSAFE QXK_priv_.readySet.update_(&QXK_priv_.readySet_dis); #endif static_cast<void>(QXK_sched_()); // synchronous scheduling QF_MEM_APP(); QF_CRIT_EXIT(); QF_CRIT_EXIT_NOP(); // BLOCK here // after unblocking... QF_CRIT_ENTRY(); QF_MEM_SYS(); // the blocking object must be this queue Q_ASSERT_INCRIT(510, thr->m_temp.obj == QXK_PTR_CAST_(QMState *, &thr->m_eQueue)); thr->m_temp.obj = nullptr; // clear } // is the queue not empty? QEvt const *e; if (thr->m_eQueue.m_frontEvt != nullptr) { e = thr->m_eQueue.m_frontEvt; // remove from the front QEQueueCtr const nFree = thr->m_eQueue.m_nFree + 1U; thr->m_eQueue.m_nFree = nFree; // update the # free // any events in the ring buffer? if (nFree <= thr->m_eQueue.m_end) { // remove event from the tail thr->m_eQueue.m_frontEvt = thr->m_eQueue.m_ring[thr->m_eQueue.m_tail]; if (thr->m_eQueue.m_tail == 0U) { thr->m_eQueue.m_tail = thr->m_eQueue.m_end; // wrap } // advance the tail (counter clockwise) thr->m_eQueue.m_tail = (thr->m_eQueue.m_tail - 1U); QS_BEGIN_PRE_(QS_QF_ACTIVE_GET, thr->m_prio) QS_TIME_PRE_(); // timestamp QS_SIG_PRE_(e->sig); // the signal of this event QS_OBJ_PRE_(&thr); // this active object QS_2U8_PRE_(e->getPoolNum_(), e->refCtr_); // poolNum & refCtr QS_EQC_PRE_(nFree); // # free entries QS_END_PRE_() } else { thr->m_eQueue.m_frontEvt = nullptr; // empty queue // all entries in the queue must be free (+1 for fronEvt) Q_ASSERT_INCRIT(520, nFree == (thr->m_eQueue.m_end + 1U)); QS_BEGIN_PRE_(QS_QF_ACTIVE_GET_LAST, thr->m_prio) QS_TIME_PRE_(); // timestamp QS_SIG_PRE_(e->sig); // the signal of this event QS_OBJ_PRE_(&thr); // this active object QS_2U8_PRE_(e->getPoolNum_(), e->refCtr_); // poolNum & refCtr QS_END_PRE_() } } else { // the queue is still empty -- the timeout must have fired e = nullptr; } QF_MEM_APP(); QF_CRIT_EXIT(); return e; const noexcept // NOTE: must be called IN a critical section Q_REQUIRE_INCRIT(600, (QXK_priv_.lockHolder != m_prio)); QXK_priv_.readySet.remove(static_cast<std::uint_fast8_t>(m_prio)); #ifndef Q_UNSAFE QXK_priv_.readySet.update_(&QXK_priv_.readySet_dis); #endif static_cast<void>(QXK_sched_()); // schedule other threads const noexcept // NOTE: must be called IN a critical section QXK_priv_.readySet.insert(static_cast<std::uint_fast8_t>(m_prio)); #ifndef Q_UNSAFE QXK_priv_.readySet.update_(&QXK_priv_.readySet_dis); #endif if ((!QXK_ISR_CONTEXT_()) // not inside ISR? && (QActive::registry_[0] != nullptr)) // kernel started? { static_cast<void>(QXK_sched_()); // schedule other threads } // NOTE: must be called IN a critical section // the private time event is now disarmed and not in any queue, // so it is safe to change its signal. The signal of 0 means // that the time event has expired. QXTHREAD_CAST_(act)->m_timeEvt.sig = 0U; QXTHREAD_CAST_(act)->unblock_(); noexcept // NOTE: must be called IN a critical section // precondition: // - the time event must be unused Q_REQUIRE_INCRIT(700, m_timeEvt.m_ctr == 0U); m_timeEvt.sig = static_cast<QSignal>(sig); if (nTicks != QXTHREAD_NO_TIMEOUT) { m_timeEvt.m_ctr = static_cast<QTimeEvtCtr>(nTicks); m_timeEvt.m_interval = 0U; // is the time event unlinked? // NOTE: For the duration of a single clock tick of the specified tick // rate a time event can be disarmed and yet still linked in the list, // because un-linking is performed exclusively in QTimeEvt::tickX(). if (static_cast<std::uint8_t>(m_timeEvt.refCtr_ & TE_IS_LINKED) == 0U) { std::uint_fast8_t const tickRate = static_cast<std::uint_fast8_t>(m_timeEvt.refCtr_); Q_ASSERT_INCRIT(710, tickRate < QF_MAX_TICK_RATE); // mark as linked m_timeEvt.refCtr_ = static_cast<std::uint8_t>( m_timeEvt.refCtr_ | TE_IS_LINKED); // The time event is initially inserted into the separate // "freshly armed" list based on timeEvtHead_[tickRate].act. // Only later, inside QTimeEvt::tick(), the "freshly armed" // list is appended to the main list of armed time events based on // timeEvtHead_[tickRate].next. Again, this is to keep any // changes to the main list exclusively inside QTimeEvt::tick(). m_timeEvt.m_next = QXK_PTR_CAST_(QTimeEvt*, QTimeEvt::timeEvtHead_[tickRate].m_act); QTimeEvt::timeEvtHead_[tickRate].m_act = &m_timeEvt; } } noexcept // NOTE: must be called IN a critical section bool wasArmed; // is the time evt running? if (m_timeEvt.m_ctr != 0U) { wasArmed = true; // schedule removal from list m_timeEvt.m_ctr = 0U; } // the time event was already automatically disarmed else { wasArmed = false; } return wasArmed; noexcept noexcept QF_CRIT_STAT QF_CRIT_ENTRY(); QF_MEM_SYS(); Q_REQUIRE_INCRIT(100, (count <= max_count) && (0U < max_count) && (max_count <= 0xFFU)); m_count = static_cast<std::uint8_t>(count); m_max_count = static_cast<std::uint8_t>(max_count); m_waitSet.setEmpty(); QF_MEM_APP(); QF_CRIT_EXIT(); noexcept QF_CRIT_STAT QF_CRIT_ENTRY(); QF_MEM_SYS(); QXThread * const curr = QXK_PTR_CAST_(QXThread*, QXK_priv_.curr); // precondition, this function: // - must NOT be called from an ISR; // - the semaphore must be initialized // - be called from an extended thread; // - the thread must NOT be already blocked on any object. Q_REQUIRE_INCRIT(200, (!QXK_ISR_CONTEXT_()) && (m_max_count > 0U) && (curr != nullptr) && (curr->m_temp.obj == nullptr)); // - the thread must NOT be holding a scheduler lock. Q_REQUIRE_INCRIT(201, QXK_priv_.lockHolder != curr->m_prio); bool taken = true; // assume that the semaphore will be signaled if (m_count > 0U) { m_count = m_count - 1U; // semaphore taken: decrement the count QS_BEGIN_PRE_(QS_SEM_TAKE, curr->m_prio) QS_TIME_PRE_(); // timestamp QS_OBJ_PRE_(this); // this semaphore QS_2U8_PRE_(curr->m_prio, m_count); QS_END_PRE_() } else { // semaphore not available -- BLOCK the thread std::uint_fast8_t const p = static_cast<std::uint_fast8_t>(curr->m_prio); // remove the curr prio from the ready set (will block) // and insert to the waiting set on this semaphore QXK_priv_.readySet.remove(p); #ifndef Q_UNSAFE QXK_priv_.readySet.update_(&QXK_priv_.readySet_dis); #endif m_waitSet.insert(p); // remember the blocking object (this semaphore) curr->m_temp.obj = QXK_PTR_CAST_(QMState*, this); curr->teArm_(static_cast<enum_t>(QXK::TIMEOUT_SIG), nTicks); QS_BEGIN_PRE_(QS_SEM_BLOCK, curr->m_prio) QS_TIME_PRE_(); // timestamp QS_OBJ_PRE_(this); // this semaphore QS_2U8_PRE_(curr->m_prio, m_count); QS_END_PRE_() // schedule the next thread if multitasking started static_cast<void>(QXK_sched_()); // schedule other threads QF_MEM_APP(); QF_CRIT_EXIT(); QF_CRIT_EXIT_NOP(); // BLOCK here !!! QF_CRIT_ENTRY(); // AFTER unblocking... QF_MEM_SYS(); // the blocking object must be this semaphore Q_ASSERT_INCRIT(240, curr->m_temp.obj == QXK_PTR_CAST_(QMState*, this)); // did the blocking time-out? (signal of zero means that it did) if (curr->m_timeEvt.sig == 0U) { if (m_waitSet.hasElement(p)) { // still waiting? m_waitSet.remove(p); // remove unblocked thread taken = false; // the semaphore was NOT taken } } else { // blocking did NOT time out // the thread must NOT be waiting on this semaphore Q_ASSERT_INCRIT(250, !m_waitSet.hasElement(p)); } curr->m_temp.obj = nullptr; // clear blocking obj. } QF_MEM_APP(); QF_CRIT_EXIT(); return taken; noexcept QF_CRIT_STAT QF_CRIT_ENTRY(); QF_MEM_SYS(); // precondition: // - the semaphore must be initialized Q_REQUIRE_INCRIT(300, m_max_count > 0U); #ifdef Q_SPY QActive const * const curr = QXK_PTR_CAST_(QActive*, QXK_priv_.curr); #endif // Q_SPY bool taken; // is the semaphore available? if (m_count > 0U) { m_count = m_count - 1U; // semaphore signaled: decrement taken = true; QS_BEGIN_PRE_(QS_SEM_TAKE, curr->m_prio) QS_TIME_PRE_(); // timestamp QS_OBJ_PRE_(this); // this semaphore QS_2U8_PRE_(curr->m_prio, m_count); QS_END_PRE_() } else { // the semaphore is NOT available (would block) taken = false; QS_BEGIN_PRE_(QS_SEM_BLOCK_ATTEMPT, curr->m_prio) QS_TIME_PRE_(); // timestamp QS_OBJ_PRE_(this); // this semaphore QS_2U8_PRE_(curr->m_prio, m_count); QS_END_PRE_() } QF_MEM_APP(); QF_CRIT_EXIT(); return taken; noexcept bool signaled = true; // assume that the semaphore will be signaled QF_CRIT_STAT QF_CRIT_ENTRY(); QF_MEM_SYS(); // precondition: // - the semaphore must be initialized Q_REQUIRE_INCRIT(400, m_max_count > 0U); // any threads blocked on this semaphore? if (m_waitSet.notEmpty()) { // find the highest-prio. thread waiting on this semaphore std::uint_fast8_t const p = m_waitSet.findMax(); QXThread * const thr = QXK_PTR_CAST_(QXThread*, QActive::registry_[p]); // assert that the thread: // - must be registered in QF; // - must be extended; and // - must be blocked on this semaphore; Q_ASSERT_INCRIT(410, (thr != nullptr) && (thr->m_osObject != nullptr) && (thr->m_temp.obj == QXK_PTR_CAST_(QMState*, this))); // disarm the internal time event static_cast<void>(thr->teDisarm_()); // make the thread ready to run and remove from the wait-list QXK_priv_.readySet.insert(p); #ifndef Q_UNSAFE QXK_priv_.readySet.update_(&QXK_priv_.readySet_dis); #endif m_waitSet.remove(p); QS_BEGIN_PRE_(QS_SEM_TAKE, thr->m_prio) QS_TIME_PRE_(); // timestamp QS_OBJ_PRE_(this); // this semaphore QS_2U8_PRE_(thr->m_prio, m_count); QS_END_PRE_() if (!QXK_ISR_CONTEXT_()) { // not inside ISR? static_cast<void>(QXK_sched_()); // schedule other threads } } else if (m_count < m_max_count) { m_count = m_count + 1U; // semaphore signaled: increment QS_BEGIN_PRE_(QS_SEM_SIGNAL, 0U) QS_TIME_PRE_(); // timestamp QS_OBJ_PRE_(this); // this semaphore QS_2U8_PRE_(0U, m_count); QS_END_PRE_() } else { signaled = false; // semaphore NOT signaled } QF_MEM_APP(); QF_CRIT_EXIT(); return signaled; : m_ao(Q_STATE_CAST(0)) noexcept QF_CRIT_STAT QF_CRIT_ENTRY(); QF_MEM_SYS(); Q_REQUIRE_INCRIT(100, (prioSpec & 0xFF00U) == 0U); m_ao.m_prio = static_cast<std::uint8_t>(prioSpec & 0xFFU); // QF-prio. m_ao.m_pthre = 0U; // not used QActive &ao = m_ao; QF_MEM_APP(); QF_CRIT_EXIT(); ao.register_(); // register this mutex as AO noexcept QF_CRIT_STAT QF_CRIT_ENTRY(); QF_MEM_SYS(); QXThread * const curr = QXK_PTR_CAST_(QXThread*, QXK_priv_.curr); // precondition, this mutex operation must: // - NOT be called from an ISR; // - be called from an eXtended thread; // - the mutex-prio. must be in range; // - the thread must NOT be already blocked on any object. Q_REQUIRE_INCRIT(200, (!QXK_ISR_CONTEXT_()) && (curr != nullptr) && (m_ao.m_prio <= QF_MAX_ACTIVE) && (curr->m_temp.obj == nullptr)); // also: the thread must NOT be holding a scheduler lock. Q_REQUIRE_INCRIT(201, QXK_priv_.lockHolder != curr->m_prio); // is the mutex available? bool locked = true; // assume that the mutex will be locked if (m_ao.m_eQueue.m_nFree == 0U) { m_ao.m_eQueue.m_nFree = 1U; // mutex lock nesting // also: the newly locked mutex must have no holder yet Q_REQUIRE_INCRIT(203, m_ao.m_osObject == nullptr); // set the new mutex holder to the curr thread and // save the thread's prio in the mutex // NOTE: reuse the otherwise unused eQueue data member. m_ao.m_osObject = curr; m_ao.m_eQueue.m_head = static_cast<QEQueueCtr>(curr->m_prio); QS_BEGIN_PRE_(QS_MTX_LOCK, curr->m_prio) QS_TIME_PRE_(); // timestamp QS_OBJ_PRE_(this); // this mutex QS_U8_PRE_(static_cast<std::uint8_t>(m_ao.m_eQueue.m_head)); QS_U8_PRE_(static_cast<std::uint8_t>(m_ao.m_eQueue.m_nFree)); QS_END_PRE_() if (m_ao.m_prio != 0U) { // prio.-ceiling protocol used? // the holder prio. must be lower than that of the mutex // and the prio. slot must be occupied by this mutex Q_ASSERT_INCRIT(210, (curr->m_prio < m_ao.m_prio) && (QActive::registry_[m_ao.m_prio] == &m_ao)); // remove the thread's original prio from the ready set // and insert the mutex's prio into the ready set QXK_priv_.readySet.remove( static_cast<std::uint_fast8_t>(m_ao.m_eQueue.m_head)); QXK_priv_.readySet.insert( static_cast<std::uint_fast8_t>(m_ao.m_prio)); #ifndef Q_UNSAFE QXK_priv_.readySet.update_(&QXK_priv_.readySet_dis); #endif // put the thread into the AO registry in place of the mutex QActive::registry_[m_ao.m_prio] = curr; // set thread's prio to that of the mutex curr->m_prio = m_ao.m_prio; #ifndef Q_UNSAFE curr->m_prio_dis = static_cast<std::uint8_t>(~curr->m_prio); #endif } } // is the mutex locked by this thread already (nested locking)? else if (m_ao.m_osObject == curr) { // the nesting level beyond the arbitrary but high limit // most likely means cyclic or recursive locking of a mutex. Q_ASSERT_INCRIT(220, m_ao.m_eQueue.m_nFree < 0xFFU); // lock one more level m_ao.m_eQueue.m_nFree = m_ao.m_eQueue.m_nFree + 1U; QS_BEGIN_PRE_(QS_MTX_LOCK, curr->m_prio) QS_TIME_PRE_(); // timestamp QS_OBJ_PRE_(this); // this mutex QS_U8_PRE_(static_cast<std::uint8_t>(m_ao.m_eQueue.m_head)); QS_U8_PRE_(static_cast<std::uint8_t>(m_ao.m_eQueue.m_nFree)); QS_END_PRE_() } else { // the mutex is already locked by a different thread // the mutex holder must be valid Q_ASSERT_INCRIT(230, m_ao.m_osObject != nullptr); if (m_ao.m_prio != 0U) { // prio.-ceiling protocol used? // the prio slot must be occupied by the thr. holding the mutex Q_ASSERT_INCRIT(240, QActive::registry_[m_ao.m_prio] == QXK_PTR_CAST_(QActive const *, m_ao.m_osObject)); } // remove the curr thread's prio from the ready set (will block) // and insert it to the waiting set on this mutex std::uint_fast8_t const p = static_cast<std::uint_fast8_t>(curr->m_prio); QXK_priv_.readySet.remove(p); #ifndef Q_UNSAFE QXK_priv_.readySet.update_(&QXK_priv_.readySet_dis); #endif m_waitSet.insert(p); // set the blocking object (this mutex) curr->m_temp.obj = QXK_PTR_CAST_(QMState*, this); curr->teArm_(static_cast<enum_t>(QXK::TIMEOUT_SIG), nTicks); QS_BEGIN_PRE_(QS_MTX_BLOCK, curr->m_prio) QS_TIME_PRE_(); // timestamp QS_OBJ_PRE_(this); // this mutex QS_2U8_PRE_(static_cast<std::uint8_t>(m_ao.m_eQueue.m_head), curr->m_prio); QS_END_PRE_() // schedule the next thread if multitasking started static_cast<void>(QXK_sched_()); // schedule other threads QF_MEM_APP(); QF_CRIT_EXIT(); QF_CRIT_EXIT_NOP(); // BLOCK here !!! // AFTER unblocking... QF_CRIT_ENTRY(); QF_MEM_SYS(); // the blocking object must be this mutex Q_ASSERT_INCRIT(250, curr->m_temp.obj == QXK_PTR_CAST_(QMState*, this)); // did the blocking time-out? (signal of zero means that it did) if (curr->m_timeEvt.sig == 0U) { if (m_waitSet.hasElement(p)) { // still waiting? m_waitSet.remove(p); // remove unblocked thread locked = false; // the mutex was NOT locked } } else { // blocking did NOT time out // the thread must NOT be waiting on this mutex Q_ASSERT_INCRIT(260, !m_waitSet.hasElement(p)); } curr->m_temp.obj = nullptr; // clear blocking obj. } QF_MEM_APP(); QF_CRIT_EXIT(); return locked; noexcept QF_CRIT_STAT QF_CRIT_ENTRY(); QF_MEM_SYS(); QActive *curr = QXK_priv_.curr; if (curr == nullptr) { // called from a basic thread? curr = QActive::registry_[QXK_priv_.actPrio]; } // precondition, this mutex must: // - NOT be called from an ISR; // - the calling thread must be valid; // - the mutex-prio. must be in range Q_REQUIRE_INCRIT(300, (!QXK_ISR_CONTEXT_()) && (curr != nullptr) && (m_ao.m_prio <= QF_MAX_ACTIVE)); // also: the thread must NOT be holding a scheduler lock. Q_REQUIRE_INCRIT(301, QXK_priv_.lockHolder != static_cast<std::uint_fast8_t>(curr->m_prio)); // is the mutex available? if (m_ao.m_eQueue.m_nFree == 0U) { m_ao.m_eQueue.m_nFree = 1U; // mutex lock nesting // also the newly locked mutex must have no holder yet Q_REQUIRE_INCRIT(303, m_ao.m_osObject == nullptr); // set the new mutex holder to the curr thread and // save the thread's prio in the mutex // NOTE: reuse the otherwise unused eQueue data member. m_ao.m_osObject = curr; m_ao.m_eQueue.m_head = static_cast<QEQueueCtr>(curr->m_prio); QS_BEGIN_PRE_(QS_MTX_LOCK, curr->m_prio) QS_TIME_PRE_(); // timestamp QS_OBJ_PRE_(this); // this mutex QS_U8_PRE_(static_cast<std::uint8_t>(m_ao.m_eQueue.m_head)); QS_U8_PRE_(static_cast<std::uint8_t>(m_ao.m_eQueue.m_nFree)); QS_END_PRE_() if (m_ao.m_prio != 0U) { // prio.-ceiling protocol used? // the holder prio. must be lower than that of the mutex // and the prio. slot must be occupied by this mutex Q_ASSERT_INCRIT(310, (curr->m_prio < m_ao.m_prio) && (QActive::registry_[m_ao.m_prio] == &m_ao)); // remove the thread's original prio from the ready set // and insert the mutex's prio into the ready set QXK_priv_.readySet.remove( static_cast<std::uint_fast8_t>(m_ao.m_eQueue.m_head)); QXK_priv_.readySet.insert( static_cast<std::uint_fast8_t>(m_ao.m_prio)); #ifndef Q_UNSAFE QXK_priv_.readySet.update_(&QXK_priv_.readySet_dis); #endif // put the thread into the AO registry in place of the mutex QActive::registry_[m_ao.m_prio] = curr; // set thread's prio to that of the mutex curr->m_prio = m_ao.m_prio; #ifndef Q_UNSAFE curr->m_prio_dis = static_cast<std::uint8_t>(~curr->m_prio); #endif } } // is the mutex locked by this thread already (nested locking)? else if (m_ao.m_osObject == curr) { // the nesting level must not exceed the specified limit Q_ASSERT_INCRIT(320, m_ao.m_eQueue.m_nFree < 0xFFU); // lock one more level m_ao.m_eQueue.m_nFree = m_ao.m_eQueue.m_nFree + 1U; QS_BEGIN_PRE_(QS_MTX_LOCK, curr->m_prio) QS_TIME_PRE_(); // timestamp QS_OBJ_PRE_(this); // this mutex QS_U8_PRE_(static_cast<std::uint8_t>(m_ao.m_eQueue.m_head)); QS_U8_PRE_(static_cast<std::uint8_t>(m_ao.m_eQueue.m_nFree)); QS_END_PRE_() } else { // the mutex is already locked by a different thread if (m_ao.m_prio != 0U) { // prio.-ceiling protocol used? // the prio slot must be occupied by the thr. holding the mutex Q_ASSERT_INCRIT(330, QActive::registry_[m_ao.m_prio] == QXK_PTR_CAST_(QActive const *, m_ao.m_osObject)); } QS_BEGIN_PRE_(QS_MTX_BLOCK_ATTEMPT, curr->m_prio) QS_TIME_PRE_(); // timestamp QS_OBJ_PRE_(this); // this mutex QS_2U8_PRE_(static_cast<std::uint8_t>(m_ao.m_eQueue.m_head), curr->m_prio); // trying thread prio QS_END_PRE_() curr = nullptr; // means that mutex is NOT available } QF_MEM_APP(); QF_CRIT_EXIT(); return curr != nullptr; noexcept QF_CRIT_STAT QF_CRIT_ENTRY(); QF_MEM_SYS(); QActive *curr = QXK_priv_.curr; if (curr == nullptr) { // called from a basic thread? curr = QActive::registry_[QXK_priv_.actPrio]; } Q_REQUIRE_INCRIT(400, (!QXK_ISR_CONTEXT_()) && (curr != nullptr)); Q_REQUIRE_INCRIT(401, m_ao.m_eQueue.m_nFree > 0U); Q_REQUIRE_INCRIT(403, m_ao.m_osObject == curr); // is this the last nesting level? if (m_ao.m_eQueue.m_nFree == 1U) { if (m_ao.m_prio != 0U) { // prio.-ceiling protocol used? Q_ASSERT_INCRIT(410, m_ao.m_prio < QF_MAX_ACTIVE); // restore the holding thread's prio from the mutex curr->m_prio = static_cast<std::uint8_t>(m_ao.m_eQueue.m_head); #ifndef Q_UNSAFE curr->m_prio_dis = static_cast<std::uint8_t>(~curr->m_prio); #endif // put the mutex back into the AO registry QActive::registry_[m_ao.m_prio] = &m_ao; // remove the mutex' prio from the ready set // and insert the original thread's prio. QXK_priv_.readySet.remove( static_cast<std::uint_fast8_t>(m_ao.m_prio)); QXK_priv_.readySet.insert( static_cast<std::uint_fast8_t>(m_ao.m_eQueue.m_head)); #ifndef Q_UNSAFE QXK_priv_.readySet.update_(&QXK_priv_.readySet_dis); #endif } QS_BEGIN_PRE_(QS_MTX_UNLOCK, curr->m_prio) QS_TIME_PRE_(); // timestamp QS_OBJ_PRE_(this); // this mutex QS_2U8_PRE_(static_cast<std::uint8_t>(m_ao.m_eQueue.m_head), 0U); QS_END_PRE_() // are any other threads waiting on this mutex? if (m_waitSet.notEmpty()) { // find the highest-prio. thread waiting on this mutex std::uint_fast8_t const p = m_waitSet.findMax(); // remove this thread from waiting on the mutex // and insert it into the ready set. m_waitSet.remove(p); QXK_priv_.readySet.insert(p); #ifndef Q_UNSAFE QXK_priv_.readySet.update_(&QXK_priv_.readySet_dis); #endif QXThread * const thr = QXK_PTR_CAST_(QXThread*, QActive::registry_[p]); // the waiting thread must: // - be registered in QF // - have the prio. corresponding to the registration // - be an extended thread // - be blocked on this mutex Q_ASSERT_INCRIT(420, (thr != nullptr) && (thr->m_prio == static_cast<std::uint8_t>(p)) && (thr->m_state.act == Q_ACTION_CAST(0)) && (thr->m_temp.obj == QXK_PTR_CAST_(QMState*, this))); // disarm the internal time event static_cast<void>(thr->teDisarm_()); // set the new mutex holder to the curr thread and // save the thread's prio in the mutex // NOTE: reuse the otherwise unused eQueue data member. m_ao.m_osObject = thr; m_ao.m_eQueue.m_head = static_cast<QEQueueCtr>(thr->m_prio); QS_BEGIN_PRE_(QS_MTX_LOCK, thr->m_prio) QS_TIME_PRE_(); // timestamp QS_OBJ_PRE_(this); // this mutex QS_U8_PRE_(static_cast<std::uint8_t>(m_ao.m_eQueue.m_head)); QS_U8_PRE_(static_cast<std::uint8_t>(m_ao.m_eQueue.m_nFree)); QS_END_PRE_() if (m_ao.m_prio != 0U) { // prio.-ceiling protocol used? // the holder prio. must be lower than that of the mutex Q_ASSERT_INCRIT(430, (m_ao.m_prio < QF_MAX_ACTIVE) && (thr->m_prio < m_ao.m_prio)); // put the thread into AO registry in place of the mutex QActive::registry_[m_ao.m_prio] = thr; } } else { // no threads are waiting for this mutex m_ao.m_eQueue.m_nFree = 0U; // free up the nesting count // the mutex no longer held by any thread m_ao.m_osObject = nullptr; m_ao.m_eQueue.m_head = 0U; m_ao.m_eQueue.m_tail = 0U; if (m_ao.m_prio != 0U) { // prio.-ceiling protocol used? // the AO priority must be in range Q_ASSERT_INCRIT(440, m_ao.m_prio < QF_MAX_ACTIVE); // put the mutex back at the original mutex slot QActive::registry_[m_ao.m_prio] = &m_ao; } } // schedule the next thread if multitasking started if (QXK_sched_() != 0U) { // activation needed? QXK_activate_(); // synchronously activate basic-thred(s) } } else { // releasing one level of nested mutex lock // unlock one level m_ao.m_eQueue.m_nFree = m_ao.m_eQueue.m_nFree - 1U; QS_BEGIN_PRE_(QS_MTX_UNLOCK_ATTEMPT, curr->m_prio) QS_TIME_PRE_(); // timestamp QS_OBJ_PRE_(this); // this mutex QS_U8_PRE_(static_cast<std::uint8_t>(m_ao.m_eQueue.m_head)); QS_U8_PRE_(static_cast<std::uint8_t>(m_ao.m_eQueue.m_nFree)); QS_END_PRE_() } QF_MEM_APP(); QF_CRIT_EXIT(); bzero_(&QF::priv_, sizeof(QF::priv_)); bzero_(&QXK_priv_, sizeof(QXK_priv_)); bzero_(&QActive::registry_[0], sizeof(QActive::registry_)); #ifndef Q_UNSAFE QXK_priv_.readySet.update_(&QXK_priv_.readySet_dis); #endif // setup the QXK scheduler as initially locked and not running QXK_priv_.lockCeil = (QF_MAX_ACTIVE + 1U); // scheduler locked // storage capable for holding a blank QActive object (const in ROM) static void* const idle_ao[((sizeof(QActive) + sizeof(void*)) - 1U) / sizeof(void*)] = { nullptr }; // register the idle AO object (cast 'const' away) QActive::registry_[0] = QF_CONST_CAST_(QActive*, reinterpret_cast<QActive const*>(idle_ao)); #ifdef QXK_INIT QXK_INIT(); // port-specific initialization of the QXK kernel #endif onCleanup(); // cleanup callback // nothing else to do for the QXK preemptive kernel #ifdef Q_SPY QS_SIG_DICTIONARY(QXK::DELAY_SIG, nullptr); QS_SIG_DICTIONARY(QXK::TIMEOUT_SIG, nullptr); // produce the QS_QF_RUN trace record QF_INT_DISABLE(); QF_MEM_SYS(); QS::beginRec_(QS_REC_NUM_(QS_QF_RUN)); QS::endRec_(); QF_MEM_APP(); QF_INT_ENABLE(); #endif // Q_SPY onStartup(); // application-specific startup callback QF_INT_DISABLE(); QF_MEM_SYS(); #ifdef QXK_START QXK_START(); // port-specific startup of the QXK kernel #endif QXK_priv_.lockCeil = 0U; // unlock the QXK scheduler // activate AOs to process events posted so far if (QXK_sched_() != 0U) { QXK_activate_(); } QF_MEM_APP(); QF_INT_ENABLE(); for (;;) { // QXK idle loop... QXK::onIdle(); // application-specific QXK idle callback } #ifdef __GNUC__ // GNU compiler? return 0; #endif QF_CRIT_STAT QF_CRIT_ENTRY(); Q_REQUIRE_INCRIT(300, (!QXK_ISR_CONTEXT_()) && ((prioSpec & 0xFF00U) == 0U)); QF_CRIT_EXIT(); m_prio = static_cast<std::uint8_t>(prioSpec & 0xFFU); // QF-prio. m_pthre = 0U; // not used register_(); // make QF aware of this QActive/QXThread if (stkSto == nullptr) { // starting basic thread (AO)? m_eQueue.init(qSto, qLen); // init the built-in queue m_osObject = nullptr; // no private stack for AO this->init(par, m_prio); // top-most initial tran. (virtual call) QS_FLUSH(); // flush the trace buffer to the host // see if this AO needs to be scheduled if QXK is already running QF_CRIT_ENTRY(); QF_MEM_SYS(); if (QXK_priv_.lockCeil <= QF_MAX_ACTIVE) { // scheduler running? if (QXK_sched_() != 0U) { // activation needed? QXK_activate_(); // synchronously activate basic-thred(s) } } QF_MEM_APP(); QF_CRIT_EXIT(); } else { // starting QXThread // is storage for the queue buffer provided? if (qSto != nullptr) { m_eQueue.init(qSto, qLen); } // extended threads provide their thread function in place of // the top-most initial tran. 'm_temp.act' QXK_PTR_CAST_(QXThread*, this)->stackInit_(m_temp.thr, stkSto, stkSize); // the new thread is not blocked on any object m_temp.obj = nullptr; QF_CRIT_ENTRY(); QF_MEM_SYS(); // extended-thread becomes ready immediately QXK_priv_.readySet.insert(static_cast<std::uint_fast8_t>(m_prio)); #ifndef Q_UNSAFE QXK_priv_.readySet.update_(&QXK_priv_.readySet_dis); #endif // see if this thread needs to be scheduled in case QXK is running if (QXK_priv_.lockCeil <= QF_MAX_ACTIVE) { static_cast<void>(QXK_sched_()); // schedule other threads } QF_MEM_APP(); QF_CRIT_EXIT(); } noexcept Q_REQUIRE_INCRIT(402, QXK_priv_.readySet.verify_(&QXK_priv_.readySet_dis)); std::uint_fast8_t p; if (QXK_priv_.readySet.isEmpty()) { p = 0U; // no activation needed } else { // find the highest-prio thread ready to run p = QXK_priv_.readySet.findMax(); if (p <= QXK_priv_.lockCeil) { // prio. of the thread holding the lock p = static_cast<std::uint_fast8_t>( QP::QActive::registry_[QXK_priv_.lockHolder]->getPrio()); if (p != 0U) { Q_ASSERT_INCRIT(410, QXK_priv_.readySet.hasElement(p)); } } } QP::QActive const * const curr = QXK_priv_.curr; QP::QActive * const next = QP::QActive::registry_[p]; // the next thread found must be registered in QF Q_ASSERT_INCRIT(420, next != nullptr); // is the current thread a basic-thread? if (curr == nullptr) { // is the new prio. above the active prio.? if (p > QXK_priv_.actPrio) { QXK_priv_.next = next; // set the next AO to activate if (next->getOsObject() != nullptr) { // is next extended? QXK_CONTEXT_SWITCH_(); p = 0U; // no activation needed } } else { // below the active prio. QXK_priv_.next = nullptr; p = 0U; // no activation needed } } else { // currently executing an extended-thread // is the current thread different from the next? if (curr != next) { QXK_priv_.next = next; QXK_CONTEXT_SWITCH_(); } else { // current is the same as next QXK_priv_.next = nullptr; // no need to context-switch } p = 0U; // no activation needed } return p; noexcept std::uint_fast8_t const prio_in = QXK_priv_.actPrio; QP::QActive *next = QXK_priv_.next; // the next AO (basic-thread) to run Q_REQUIRE_INCRIT(500, (next != nullptr) && (prio_in <= QF_MAX_ACTIVE)); // QXK Context switch callback defined or QS tracing enabled? #if (defined QF_ON_CONTEXT_SW) || (defined Q_SPY) QXK_contextSw_(next); #endif // QF_ON_CONTEXT_SW || Q_SPY QXK_priv_.next = nullptr; // clear the next AO QXK_priv_.curr = nullptr; // current is basic-thread // prio. of the next thread std::uint_fast8_t p = next->getPrio(); // loop until no more ready-to-run AOs of higher prio than the initial do { QXK_priv_.actPrio = p; // next active prio QF_INT_ENABLE(); // unconditionally enable interrupts QP::QEvt const * const e = next->get_(); // NOTE QActive::get_() performs QS_MEM_APP() before return // dispatch event (virtual call) next->dispatch(e, next->getPrio()); #if (QF_MAX_EPOOL > 0U) QP::QF::gc(e); #endif QF_INT_DISABLE(); // unconditionally disable interrupts QF_MEM_SYS(); // check internal integrity (duplicate inverse storage) Q_ASSERT_INCRIT(502, QXK_priv_.readySet.verify_(&QXK_priv_.readySet_dis)); if (next->getEQueue().isEmpty()) { // empty queue? QXK_priv_.readySet.remove(p); #ifndef Q_UNSAFE QXK_priv_.readySet.update_(&QXK_priv_.readySet_dis); #endif } if (QXK_priv_.readySet.isEmpty()) { QXK_priv_.next = nullptr; next = QP::QActive::registry_[0]; p = 0U; // no activation needed } else { // find next highest-prio below the lock ceiling p = QXK_priv_.readySet.findMax(); if (p <= QXK_priv_.lockCeil) { p = QXK_priv_.lockHolder; if (p != 0U) { Q_ASSERT_INCRIT(510, QXK_priv_.readySet.hasElement(p)); } } // set the next thread and ensure that it is registered next = QP::QActive::registry_[p]; Q_ASSERT_INCRIT(520, next != nullptr); // is next a basic thread? if (next->getOsObject() == nullptr) { // is the next prio. above the initial prio.? if (p > QP::QActive::registry_[prio_in]->getPrio()) { #if (defined QF_ON_CONTEXT_SW) || (defined Q_SPY) if (p != QXK_priv_.actPrio) { // changing threads? QXK_contextSw_(next); } #endif // QF_ON_CONTEXT_SW || Q_SPY QXK_priv_.next = next; } else { QXK_priv_.next = nullptr; p = 0U; // no activation needed } } else { // next is the extended-thread QXK_priv_.next = next; QXK_CONTEXT_SWITCH_(); p = 0U; // no activation needed } } } while (p != 0U); // while activation needed // restore the active prio. QXK_priv_.actPrio = prio_in; #if (defined QF_ON_CONTEXT_SW) || (defined Q_SPY) if (next->getOsObject() == nullptr) { QXK_contextSw_((prio_in == 0U) ? nullptr : QP::QActive::registry_[prio_in]); } #endif // QF_ON_CONTEXT_SW || Q_SPY #ifdef Q_SPY std::uint_fast8_t const prev_prio = (QXK_priv_.prev != nullptr) ? QXK_priv_.prev->getPrio() : 0U; if (next != nullptr) { // next is NOT idle? std::uint_fast8_t const next_prio = next->getPrio(); QS_BEGIN_PRE_(QP::QS_SCHED_NEXT, next_prio) QS_TIME_PRE_(); // timestamp QS_2U8_PRE_(next_prio, prev_prio); QS_END_PRE_() } else { // going to idle QS_BEGIN_PRE_(QP::QS_SCHED_IDLE, prev_prio) QS_TIME_PRE_(); // timestamp QS_U8_PRE_(prev_prio); QS_END_PRE_() } #endif // Q_SPY #ifdef QF_ON_CONTEXT_SW QF_onContextSw(QXK_priv_.prev, next); #endif // QF_ON_CONTEXT_SW QXK_priv_.prev = next; // update the previous thread QF_CRIT_STAT QF_CRIT_ENTRY(); QP::QXThread const * const thr = QXTHREAD_CAST_(QXK_priv_.curr); Q_REQUIRE_INCRIT(900, (!QXK_ISR_CONTEXT_()) && (thr != nullptr)); // current thread must be extended Q_REQUIRE_INCRIT(901, QXK_priv_.lockHolder != thr->getPrio()); std::uint_fast8_t const p = static_cast<std::uint_fast8_t>(thr->getPrio()); QF_MEM_SYS(); QP::QActive::registry_[p] = nullptr; QXK_priv_.readySet.remove(p); #ifndef Q_UNSAFE QXK_priv_.readySet.update_(&QXK_priv_.readySet_dis); #endif static_cast<void>(QXK_sched_()); // schedule other threads QF_MEM_APP(); QF_CRIT_EXIT(); QSchedStatus lockStat_; do { \ if (QXK_ISR_CONTEXT_()) { \ lockStat_ = 0xFFU; \ } else { \ lockStat_ = QXK::schedLock((ceil_)); \ } \ } while (false) do { \ if (lockStat_ != 0xFFU) { \ QXK::schedUnlock(lockStat_); \ } \ } while (false) // QXK native event queue waiting \ Q_ASSERT_INCRIT(310, (me_)->m_eQueue.m_frontEvt != nullptr) // QXK native event queue signalling do { \ QXK_priv_.readySet.insert( \ static_cast<std::uint_fast8_t>((me_)->m_prio)); \ QXK_priv_.readySet.update_(&QXK_priv_.readySet_dis); \ if (!QXK_ISR_CONTEXT_()) { \ if (QXK_sched_() != 0U) { \ QXK_activate_(); \ } \ } \ } while (false) // QXK native event queue signalling do { \ QXK_priv_.readySet.insert( \ static_cast<std::uint_fast8_t>((me_)->m_prio)); \ if (!QXK_ISR_CONTEXT_()) { \ if (QXK_sched_() != 0U) { \ QXK_activate_(); \ } \ } \ } while (false) do { \ if ((me_)->m_temp.obj == QXK_PTR_CAST_(QMState*, &(me_)->m_eQueue)) { \ static_cast<void>(QXTHREAD_CAST_(me_)->teDisarm_()); \ QXK_priv_.readySet.insert( \ static_cast<std::uint_fast8_t>((me_)->m_prio)); \ QXK_priv_.readySet.update_(&QXK_priv_.readySet_dis); \ if (!QXK_ISR_CONTEXT_()) { \ static_cast<void>(QXK_sched_()); \ } \ } \ } while (false) do { \ if ((me_)->m_temp.obj == QXK_PTR_CAST_(QMState*, &(me_)->m_eQueue)) { \ static_cast<void>(QXTHREAD_CAST_(me_)->teDisarm_()); \ QXK_priv_.readySet.insert( \ static_cast<std::uint_fast8_t>((me_)->m_prio)); \ if (!QXK_ISR_CONTEXT_()) { \ static_cast<void>(QXK_sched_()); \ } \ } \ } while (false) (static_cast<QP::QXThread *>(ptr_)) <type_> (reinterpret_cast<type_>(ptr_)) //! pre-defined QS record IDs : std::int8_t { // [0] QS session (not maskable) QS_EMPTY, //!< QS record for cleanly starting a session // [1] SM records QS_QEP_STATE_ENTRY, //!< a state was entered QS_QEP_STATE_EXIT, //!< a state was exited QS_QEP_STATE_INIT, //!< an initial transition was taken in a state QS_QEP_INIT_TRAN, //!< the top-most initial transition was taken QS_QEP_INTERN_TRAN, //!< an internal transition was taken QS_QEP_TRAN, //!< a regular transition was taken QS_QEP_IGNORED, //!< an event was ignored (silently discarded) QS_QEP_DISPATCH, //!< an event was dispatched (begin of RTC step) QS_QEP_UNHANDLED, //!< an event was un-handled due to a guard // [10] Active Object (AO) records QS_QF_ACTIVE_DEFER, //!< AO deferred an event QS_QF_ACTIVE_RECALL, //!< AO recalled an event QS_QF_ACTIVE_SUBSCRIBE, //!< an AO subscribed to an event QS_QF_ACTIVE_UNSUBSCRIBE, //!< an AO unsubscribed to an event QS_QF_ACTIVE_POST, //!< an event was posted (FIFO) directly to AO QS_QF_ACTIVE_POST_LIFO, //!< an event was posted (LIFO) directly to AO QS_QF_ACTIVE_GET, //!< AO got an event and its queue is not empty QS_QF_ACTIVE_GET_LAST,//!< AO got an event and its queue is empty QS_QF_ACTIVE_RECALL_ATTEMPT, //!< AO attempted to recall an event // [19] Event Queue (EQ) records QS_QF_EQUEUE_POST, //!< an event was posted (FIFO) to a raw queue QS_QF_EQUEUE_POST_LIFO, //!< an event was posted (LIFO) to a raw queue QS_QF_EQUEUE_GET, //!< get an event and queue still not empty QS_QF_EQUEUE_GET_LAST,//!< get the last event from the queue // [23] Framework (QF) records QS_QF_NEW_ATTEMPT, //!< an attempt to allocate an event failed // [24] Memory Pool (MP) records QS_QF_MPOOL_GET, //!< a memory block was removed from memory pool QS_QF_MPOOL_PUT, //!< a memory block was returned to memory pool // [26] Additional Framework (QF) records QS_QF_PUBLISH, //!< an event was published to active objects QS_QF_NEW_REF, //!< new event reference was created QS_QF_NEW, //!< new event was created QS_QF_GC_ATTEMPT, //!< garbage collection attempt QS_QF_GC, //!< garbage collection QS_QF_TICK, //!< QTimeEvt tick was called // [32] Time Event (TE) records QS_QF_TIMEEVT_ARM, //!< a time event was armed QS_QF_TIMEEVT_AUTO_DISARM, //!< a time event expired and was disarmed QS_QF_TIMEEVT_DISARM_ATTEMPT,//!< attempt to disarm a disarmed QTimeEvt QS_QF_TIMEEVT_DISARM, //!< true disarming of an armed time event QS_QF_TIMEEVT_REARM, //!< rearming of a time event QS_QF_TIMEEVT_POST, //!< a time event posted itself directly to an AO // [38] Additional Framework (QF) records QS_QF_DELETE_REF, //!< an event reference is about to be deleted QS_QF_CRIT_ENTRY, //!< critical section was entered QS_QF_CRIT_EXIT, //!< critical section was exited QS_QF_ISR_ENTRY, //!< an ISR was entered QS_QF_ISR_EXIT, //!< an ISR was exited QS_QF_INT_DISABLE, //!< interrupts were disabled QS_QF_INT_ENABLE, //!< interrupts were enabled // [45] Additional Active Object (AO) records QS_QF_ACTIVE_POST_ATTEMPT,//!< attempt to post an evt to AO failed // [46] Additional Event Queue (EQ) records QS_QF_EQUEUE_POST_ATTEMPT,//!< attempt to post evt to QEQueue failed // [47] Additional Memory Pool (MP) records QS_QF_MPOOL_GET_ATTEMPT, //!< attempt to get a memory block failed // [48] Scheduler (SC) records QS_SCHED_PREEMPT, //!< scheduler asynchronously preempted a task QS_SCHED_RESTORE, //!< scheduler restored preempted task QS_SCHED_LOCK, //!< scheduler was locked QS_SCHED_UNLOCK, //!< scheduler was unlocked QS_SCHED_NEXT, //!< scheduler started next task QS_SCHED_IDLE, //!< scheduler restored the idle task // [54] Miscellaneous QS records (not maskable) QS_ENUM_DICT, //!< enumeration dictionary entry // [55] Additional QEP records QS_QEP_TRAN_HIST, //!< a tran to history was taken QS_QEP_TRAN_EP, //!< a tran to entry point into a submachine QS_QEP_TRAN_XP, //!< a tran to exit point out of a submachine // [58] Miscellaneous QS records (not maskable) QS_TEST_PAUSED, //!< test has been paused QS_TEST_PROBE_GET, //!< reports that Test-Probe has been used QS_SIG_DICT, //!< signal dictionary entry QS_OBJ_DICT, //!< object dictionary entry QS_FUN_DICT, //!< function dictionary entry QS_USR_DICT, //!< user QS record dictionary entry QS_TARGET_INFO, //!< reports the Target information QS_TARGET_DONE, //!< reports completion of a user callback QS_RX_STATUS, //!< reports QS data receive status QS_QUERY_DATA, //!< reports the data from "current object" query QS_PEEK_DATA, //!< reports the data from the PEEK query QS_ASSERT_FAIL, //!< assertion failed in the code QS_QF_RUN, //!< QF_run() was entered // [71] Semaphore (SEM) records QS_SEM_TAKE, //!< a semaphore was taken by a thread QS_SEM_BLOCK, //!< a semaphore blocked a thread QS_SEM_SIGNAL, //!< a semaphore was signaled QS_SEM_BLOCK_ATTEMPT, //!< a semaphore blocked was attempted // [75] Mutex (MTX) records QS_MTX_LOCK, //!< a mutex was locked QS_MTX_BLOCK, //!< a mutex blocked a thread QS_MTX_UNLOCK, //!< a mutex was unlocked QS_MTX_LOCK_ATTEMPT, //!< a mutex lock was attempted QS_MTX_BLOCK_ATTEMPT, //!< a mutex blocking was attempted QS_MTX_UNLOCK_ATTEMPT,//!< a mutex unlock was attempted // [81] QS_PRE_MAX //!< the # predefined signals }; //! QS-TX record groups for QS_GLB_FILTER() : std::int16_t { QS_ALL_RECORDS = 0xF0,//!< all maskable QS records QS_SM_RECORDS, //!< State Machine QS records QS_AO_RECORDS, //!< Active Object QS records QS_EQ_RECORDS, //!< Event Queues QS records QS_MP_RECORDS, //!< Memory Pools QS records QS_TE_RECORDS, //!< Time Events QS records QS_QF_RECORDS, //!< QF QS records QS_SC_RECORDS, //!< Scheduler QS records QS_SEM_RECORDS, //!< Semaphore QS records QS_MTX_RECORDS, //!< Mutex QS records QS_U0_RECORDS, //!< User Group 100-104 records QS_U1_RECORDS, //!< User Group 105-109 records QS_U2_RECORDS, //!< User Group 110-114 records QS_U3_RECORDS, //!< User Group 115-119 records QS_U4_RECORDS, //!< User Group 120-124 records QS_UA_RECORDS //!< All User records }; //! QS user record group offsets for QS_GLB_FILTER() : std::int16_t { QS_USER = 100, //!< the first record available to QS users QS_USER0 = QS_USER, //!< offset for User Group 0 QS_USER1 = QS_USER0 + 5, //!< offset for User Group 1 QS_USER2 = QS_USER1 + 5, //!< offset for User Group 2 QS_USER3 = QS_USER2 + 5, //!< offset for User Group 3 QS_USER4 = QS_USER3 + 5 //!< offset for User Group 4 }; //! QS ID offsets for QS_LOC_FILTER() : std::int16_t { QS_AO_ID = 0, //!< offset for AO priorities QS_EP_ID = 64, //!< offset for event-pool IDs QS_EQ_ID = 80, //!< offset for event-queue IDs QS_AP_ID = 96 //!< offset for Application-specific IDs }; //! QS ID groups for QS_LOC_FILTER() : std::int16_t { QS_ALL_IDS = 0xF0, //!< all QS IDs QS_AO_IDS = 0x80 + QS_AO_ID, //!< AO IDs (priorities) QS_EP_IDS = 0x80 + QS_EP_ID, //!< event-pool IDs QS_EQ_IDS = 0x80 + QS_EQ_ID, //!< event-queue IDs QS_AP_IDS = 0x80 + QS_AP_ID //!< Application-specific IDs }; { std::uint8_t m_prio; //!< prio. (qsId) for the QS "local filter" // get the prio. (qsId) from the QSpyId object std::uint_fast8_t getPrio() const noexcept { return static_cast<std::uint_fast8_t>(m_prio); } }; = std::uint16_t; = std::uint32_t; = std::uint64_t; = std::uint16_t; = std::uint32_t; = std::uint64_t; = void (*)(); = std::uint16_t; = std::uint32_t; = std::uint16_t; = std::uint32_t; //! Enumerates data elements for app-specific trace records : std::uint8_t { I8_ENUM_T, //!< signed 8-bit integer or enum format U8_T, //!< unsigned 8-bit integer format I16_T, //!< signed 16-bit integer format U16_T, //!< unsigned 16-bit integer format I32_T, //!< signed 32-bit integer format U32_T, //!< unsigned 32-bit integer format F32_T, //!< 32-bit floating point format F64_T, //!< 64-bit floating point format STR_T, //!< zero-terminated ASCII string format MEM_T, //!< up to 255-bytes memory block format SIG_T, //!< event signal format OBJ_T, //!< object pointer format FUN_T, //!< function pointer format I64_T, //!< signed 64-bit integer format U64_T //!< unsigned 64-bit integer format }; union TCast { T_IN in; T_OUT out; } u = { in }; return u.out; noexcept priv_.buf = sto; priv_.end = static_cast<QSCtr>(stoSize); priv_.head = 0U; priv_.tail = 0U; priv_.used = 0U; priv_.seq = 0U; priv_.chksum = 0U; priv_.critNest = 0U; glbFilter_(-static_cast<enum_t>(QS_ALL_RECORDS));// all global filters OFF locFilter_(static_cast<enum_t>(QS_ALL_IDS)); // all local filters ON priv_.locFilter_AP = nullptr; // deprecated "AP-filter" // produce an empty record to "flush" the QS trace buffer beginRec_(QS_REC_NUM_(QS_EMPTY)); endRec_(); // produce the reset record to inform QSPY of a new session target_info_pre_(0xFFU); // hold off flushing after successful initialization (see QS_INIT()) noexcept // NOTE: Must be called IN critical section. // Also requires system-level memory access (QF_MEM_SYS()). std::uint16_t ret; if (priv_.used == 0U) { ret = QS_EOD; // set End-Of-Data } else { std::uint8_t const * const buf = priv_.buf; // put in a temporary QSCtr tail = priv_.tail; // put in a temporary (register) ret = static_cast<std::uint16_t>(buf[tail]); // set the byte to return ++tail; // advance the tail if (tail == priv_.end) { // tail wrap around? tail = 0U; } priv_.tail = tail; // update the tail priv_.used = (priv_.used - 1U); // one less byte used } return ret; // return the byte or EOD noexcept // NOTE: Must be called IN critical section. // Also requires system-level memory access (QF_MEM_SYS()). QSCtr const used = priv_.used; // put in a temporary (register) std::uint8_t *buf; // any bytes used in the ring buffer? if (used != 0U) { QSCtr tail = priv_.tail; // put in a temporary (register) QSCtr const end = priv_.end; // put in a temporary (register) QSCtr n = static_cast<QSCtr>(end - tail); if (n > used) { n = used; } if (n > static_cast<QSCtr>(*pNbytes)) { n = static_cast<QSCtr>(*pNbytes); } *pNbytes = static_cast<std::uint16_t>(n); // n-bytes available buf = priv_.buf; buf = &buf[tail]; // the bytes are at the tail priv_.used = static_cast<QSCtr>(used - n); tail += n; if (tail == end) { tail = 0U; } priv_.tail = tail; } else { // no bytes available *pNbytes = 0U; // no bytes available right now buf = nullptr; // no bytes available right now } return buf; //! Kinds of objects used QS-RX : std::uint8_t { SM_OBJ, //!< state machine object AO_OBJ, //!< active object MP_OBJ, //!< event pool object EQ_OBJ, //!< raw queue object TE_OBJ, //!< time event object AP_OBJ, //!< generic Application-specific object MAX_OBJ }; //! Object combinations for QS-RX : std::uint8_t { SM_AO_OBJ = MAX_OBJ //!< combination of SM and AO }; noexcept rxPriv_.buf = &sto[0]; rxPriv_.end = static_cast<QSCtr>(stoSize); rxPriv_.head = 0U; rxPriv_.tail = 0U; rxPriv_.currObj[QS::SM_OBJ] = nullptr; rxPriv_.currObj[QS::AO_OBJ] = nullptr; rxPriv_.currObj[QS::MP_OBJ] = nullptr; rxPriv_.currObj[QS::EQ_OBJ] = nullptr; rxPriv_.currObj[QS::TE_OBJ] = nullptr; rxPriv_.currObj[QS::AP_OBJ] = nullptr; tran_(WAIT4_SEQ); QP::QS::rxPriv_.esc = 0U; QP::QS::rxPriv_.seq = 0U; QP::QS::rxPriv_.chksum = 0U; beginRec_(static_cast<std::uint_fast8_t>(QS_OBJ_DICT)); QS_OBJ_PRE_(&rxPriv_); QS_STR_PRE_("QS_RX"); endRec_(); // no QS_REC_DONE(), because QS is not running yet #ifdef Q_UTEST tstPriv_.tpNum = 0U; tstPriv_.testTime = 0U; #endif // Q_UTEST noexcept // NOTE: does not need critical section // But requires system-level memory access (QF_MEM_SYS()). QSCtr head = rxPriv_.head + 1U; if (head == rxPriv_.end) { head = 0U; } if (head != rxPriv_.tail) { // buffer NOT full? rxPriv_.buf[rxPriv_.head] = b; rxPriv_.head = head; return true; // byte placed in the buffer } else { return false; // byte NOT placed in the buffer } QSCtr tail = rxPriv_.tail; while (rxPriv_.head != tail) { // QS-RX buffer NOT empty? std::uint8_t b = rxPriv_.buf[tail]; ++tail; if (tail == rxPriv_.end) { tail = 0U; } rxPriv_.tail = tail; // update the tail to a *valid* index if (QP::QS::rxPriv_.esc != 0U) { // escaped byte arrived? QP::QS::rxPriv_.esc = 0U; b ^= QS_ESC_XOR; QP::QS::rxPriv_.chksum += b; rxParseData_(b); } else if (b == QS_ESC) { QP::QS::rxPriv_.esc = 1U; } else if (b == QS_FRAME) { // get ready for the next frame b = QP::QS::rxPriv_.state; // save the current state in b QP::QS::rxPriv_.esc = 0U; tran_(WAIT4_SEQ); if (QP::QS::rxPriv_.chksum == QS_GOOD_CHKSUM) { QP::QS::rxPriv_.chksum = 0U; rxHandleGoodFrame_(b); } else { // bad checksum QP::QS::rxPriv_.chksum = 0U; rxReportError_(0x41U); rxHandleBadFrame_(b); } } else { QP::QS::rxPriv_.chksum += b; rxParseData_(b); } } QS_CRIT_STAT QS_CRIT_ENTRY(); Q_REQUIRE_INCRIT(300, obj_kind < Q_DIM(rxPriv_.currObj)); QS_MEM_SYS(); rxPriv_.currObj[obj_kind] = obj_ptr; QS_MEM_APP(); QS_CRIT_EXIT(); noexcept // NOTE: Must be called IN critical section. // Also requires system-level memory access (QF_MEM_SYS()). QSCtr const head = rxPriv_.head; std::uint16_t nFree; if (head == rxPriv_.tail) { // buffer empty? nFree = static_cast<std::uint16_t>(rxPriv_.end - 1U); } else if (head < rxPriv_.tail) { nFree = static_cast<std::uint16_t>(rxPriv_.tail - head - 1U); } else { nFree = static_cast<std::uint16_t>(rxPriv_.end + rxPriv_.tail - head - 1U); } return nFree; { QSFun addr; std::uint32_t data; std::uint8_t idx; }; // Clear the internal QF variables, so that the framework can start // correctly even if the startup code fails to clear the uninitialized // data (as is required by the C++ Standard). bzero_(&QF::priv_, sizeof(QF::priv_)); bzero_(&QS::tstPriv_, sizeof(QS::tstPriv_)); bzero_(&QActive::registry_[0], sizeof(QActive::registry_)); #ifndef Q_UNSAFE QS::tstPriv_.readySet.update_(&QS::tstPriv_.readySet_dis); #endif QS::onReset(); QS_CRIT_STAT QS_CRIT_ENTRY(); QS_MEM_SYS(); // function dictionaries for the standard API QS_FUN_DICTIONARY(&QActive::post_); QS_FUN_DICTIONARY(&QActive::postLIFO); QS_FUN_DICTIONARY(&QS::processTestEvts_); // produce the QS_QF_RUN trace record QS_BEGIN_PRE_(QS_QF_RUN, 0U) QS_END_PRE_() QS_MEM_APP(); QS_CRIT_EXIT(); QS::processTestEvts_(); // process all events posted so far QS::onTestLoop(); // run the unit test QS::onCleanup(); // application cleanup return 0; // return no error Q_UNUSED_PAR(stkSto); Q_UNUSED_PAR(stkSize); m_prio = static_cast<std::uint8_t>(prioSpec & 0xFFU); // QF-priol m_pthre = static_cast<std::uint8_t>(prioSpec >> 8U); // preemption-thre. register_(); // make QF aware of this AO m_eQueue.init(qSto, qLen); // initialize QEQueue of this AO this->init(par, m_prio); // take the top-most initial tran. (virtual) unsubscribeAll(); // unsubscribe from all events unregister_(); // remove this object from QF QS_CRIT_STAT QS_CRIT_ENTRY(); QS_MEM_SYS(); QTimeEvt *prev = &QTimeEvt::timeEvtHead_[tickRate]; QS_BEGIN_PRE_(QS_QF_TICK, 0U) prev->m_ctr = (prev->m_ctr + 1U); QS_TEC_PRE_(prev->m_ctr); // tick ctr QS_U8_PRE_(tickRate); // tick rate QS_END_PRE_() // is current Time Event object provided? QTimeEvt *t = static_cast<QTimeEvt *>(QS::rxPriv_.currObj[QS::TE_OBJ]); if (t != nullptr) { // the time event must be armed Q_ASSERT_INCRIT(810, t->m_ctr != 0U); QActive * const act = static_cast<QActive *>(t->m_act); // the recipient AO must be provided Q_ASSERT_INCRIT(820, act != nullptr); // periodic time evt? if (t->m_interval != 0U) { t->m_ctr = t->m_interval; // rearm the time event } else { // one-shot time event: automatically disarm t->m_ctr = 0U; // auto-disarm // mark time event 't' as NOT linked t->refCtr_ = static_cast<std::uint8_t>(t->refCtr_ & static_cast<std::uint8_t>(~TE_IS_LINKED)); QS_BEGIN_PRE_(QS_QF_TIMEEVT_AUTO_DISARM, act->m_prio) QS_OBJ_PRE_(t); // this time event object QS_OBJ_PRE_(act); // the target AO QS_U8_PRE_(tickRate); // tick rate QS_END_PRE_() } QS_BEGIN_PRE_(QS_QF_TIMEEVT_POST, act->m_prio) QS_TIME_PRE_(); // timestamp QS_OBJ_PRE_(t); // the time event object QS_SIG_PRE_(t->sig); // signal of this time event QS_OBJ_PRE_(act); // the target AO QS_U8_PRE_(tickRate); // tick rate QS_END_PRE_() QS_MEM_APP(); QS_CRIT_EXIT(); // exit critical section before posting // asserts if queue overflows static_cast<void>(act->POST(t, sender)); QS_CRIT_ENTRY(); QS_MEM_SYS(); } // update the linked list of time events for (;;) { t = prev->m_next; // advance down the time evt. list // end of the list? if (t == nullptr) { // any new time events armed since the last run of tick()? if (QTimeEvt::timeEvtHead_[tickRate].m_act != nullptr) { // sanity check Q_ASSERT_INCRIT(830, prev != nullptr); prev->m_next = QTimeEvt::timeEvtHead_[tickRate].toTimeEvt(); QTimeEvt::timeEvtHead_[tickRate].m_act = nullptr; t = prev->m_next; // switch to the new list } else { break; // all currently armed time evts. processed } } // time event scheduled for removal? if (t->m_ctr == 0U) { prev->m_next = t->m_next; // mark time event 't' as NOT linked t->refCtr_ = static_cast<std::uint8_t>(t->refCtr_ & static_cast<std::uint8_t>(~TE_IS_LINKED)); // do NOT advance the prev pointer QS_MEM_APP(); QS_CRIT_EXIT(); // exit crit. section to reduce latency } else { prev = t; // advance to this time event QS_MEM_APP(); QS_CRIT_EXIT(); // exit crit. section to reduce latency } QS_CRIT_ENTRY(); // re-enter crit. section to continue QS_MEM_SYS(); } QS_MEM_APP(); QS_CRIT_EXIT(); : QAsm() override Q_UNUSED_PAR(e); #ifdef Q_SPY if ((QS::priv_.flags & 0x01U) == 0U) { QS::priv_.flags |= 0x01U; QS_FUN_DICTIONARY(&QP::QHsm::top); } #else Q_UNUSED_PAR(qsId); #endif QS_CRIT_STAT QS_CRIT_ENTRY(); QS_MEM_SYS(); QS_BEGIN_PRE_(QS_QEP_STATE_INIT, qsId) QS_OBJ_PRE_(this); // this state machine object QS_FUN_PRE_(m_state.fun); // the source state QS_FUN_PRE_(m_temp.fun); // the target of the initial tran. QS_END_PRE_() QS_MEM_APP(); QS_CRIT_EXIT(); override QS_CRIT_STAT QS_CRIT_ENTRY(); QS_MEM_SYS(); QS_BEGIN_PRE_(QS_QEP_DISPATCH, qsId) QS_TIME_PRE_(); // time stamp QS_SIG_PRE_(e->sig); // the signal of the event QS_OBJ_PRE_(this); // this state machine object QS_FUN_PRE_(m_state.fun); // the current state QS_END_PRE_() QS_MEM_APP(); QS_CRIT_EXIT(); : QActive(nullptr) override Q_UNUSED_PAR(e); Q_UNUSED_PAR(qsId); #ifdef Q_SPY if ((QS::priv_.flags & 0x01U) == 0U) { QS::priv_.flags |= 0x01U; QS_FUN_DICTIONARY(&QP::QHsm::top); } #endif QS_CRIT_STAT QS_CRIT_ENTRY(); QS_MEM_SYS(); QS_BEGIN_PRE_(QS_QEP_STATE_INIT, m_prio) QS_OBJ_PRE_(this); // this state machine object QS_FUN_PRE_(m_state.fun); // the source state QS_FUN_PRE_(m_temp.fun); // the target of the initial tran. QS_END_PRE_() QS_MEM_APP(); QS_CRIT_EXIT(); override QS_CRIT_STAT QS_CRIT_ENTRY(); QS_MEM_SYS(); QS_BEGIN_PRE_(QS_QEP_DISPATCH, qsId) QS_TIME_PRE_(); // time stamp QS_SIG_PRE_(e->sig); // the signal of the event QS_OBJ_PRE_(this); // this state machine object QS_FUN_PRE_(m_state.fun); // the current state QS_END_PRE_() QS_MEM_APP(); QS_CRIT_EXIT(); noexcept QS_TEST_PROBE_DEF(&QActive::post_) QF_CRIT_STAT QF_CRIT_ENTRY(); QF_MEM_SYS(); // test-probe#1 for faking queue overflow bool status = true; QS_TEST_PROBE_ID(1, status = false; if (margin == QF::NO_MARGIN) { // fake assertion Mod=qf_actq,Loc=110 Q_onError("qf_actq", 110); } ) // is it a mutable event? if (e->getPoolNum_() != 0U) { QEvt_refCtr_inc_(e); } std::uint_fast8_t const rec = (status ? static_cast<std::uint8_t>(QS_QF_ACTIVE_POST) : static_cast<std::uint8_t>(QS_QF_ACTIVE_POST_ATTEMPT)); QS_BEGIN_PRE_(rec, m_prio) QS_TIME_PRE_(); // timestamp QS_OBJ_PRE_(sender); // the sender object QS_SIG_PRE_(e->sig); // the signal of the event QS_OBJ_PRE_(this); // this active object QS_2U8_PRE_(e->getPoolNum_(), e->refCtr_); // poolNum & refCtr QS_EQC_PRE_(0U); // # free entries QS_EQC_PRE_(margin); // margin requested QS_END_PRE_() // callback to examine the posted event under the same conditions // as producing the #QS_QF_ACTIVE_POST trace record, which are: // the local filter for this AO ('m_prio') is set if (QS_LOC_CHECK_(m_prio)) { QS::onTestPost(sender, this, e, status); } QF_MEM_APP(); QF_CRIT_EXIT(); // recycle the event immediately, because it was not really posted #if (QF_MAX_EPOOL > 0U) QF::gc(e); #endif return status; noexcept QS_TEST_PROBE_DEF(&QActive::postLIFO) QF_CRIT_STAT QF_CRIT_ENTRY(); QF_MEM_SYS(); // test-probe#1 for faking queue overflow QS_TEST_PROBE_ID(1, // fake assertion Mod=qf_actq,Loc=210 Q_onError("qf_actq", 210); ) // is it a mutable event? if (e->getPoolNum_() != 0U) { QEvt_refCtr_inc_(e); } QS_BEGIN_PRE_(QS_QF_ACTIVE_POST_LIFO, m_prio) QS_TIME_PRE_(); // timestamp QS_SIG_PRE_(e->sig); // the signal of this event QS_OBJ_PRE_(this); // this active object QS_2U8_PRE_(e->getPoolNum_(), e->refCtr_); // poolNum & refCtr QS_EQC_PRE_(0U); // # free entries QS_EQC_PRE_(0U); // min # free entries QS_END_PRE_() // callback to examine the posted event under the same conditions // as producing the #QS_QF_ACTIVE_POST trace record, which are: // the local filter for this AO ('m_prio') is set if (QS_LOC_CHECK_(m_prio)) { QS::onTestPost(nullptr, this, e, true); } QF_MEM_APP(); QF_CRIT_EXIT(); // recycle the event immediately, because it was not really posted #if (QF_MAX_EPOOL > 0U) QF::gc(e); #endif (QP::QS::onStartup(arg_)) (QP::QS::onCleanup()) (QP::QS::doOutput()) (QP::QS::doInput()) \ (QP::QS::glbFilter_(static_cast<std::int_fast16_t>(rec_))) \ (QP::QS::locFilter_(static_cast<std::int_fast16_t>(qsId_))) \ if (QS_GLB_CHECK_(rec_) && QS_LOC_CHECK_(qsId_)) { \ QS_CRIT_STAT \ QS_CRIT_ENTRY(); \ QS_MEM_SYS(); \ QP::QS::beginRec_(static_cast<std::uint_fast8_t>(rec_)); \ QS_TIME_PRE_(); { } \ QP::QS::endRec_(); \ QS_MEM_APP(); \ QS_CRIT_EXIT(); \ } (QP::QS::onFlush()) \ if (QS_GLB_CHECK_(rec_) && QS_LOC_CHECK_(qsId_)) { \ QP::QS::beginRec_(rec_); \ QS_TIME_PRE_(); { } \ QP::QS::endRec_(); \ } \ ((static_cast<std::uint_fast8_t>(QP::QS::filt_.glb[ \ static_cast<std::uint_fast8_t>(rec_) >> 3U]) \ & (static_cast<std::uint_fast8_t>(1U) \ << (static_cast<std::uint_fast8_t>(rec_) & 7U))) != 0U) \ ((static_cast<std::uint_fast8_t>(QP::QS::filt_.loc \ [static_cast<std::uint_fast8_t>(qsId_) >> 3U]) \ & (static_cast<std::uint_fast8_t>(1U) \ << (static_cast<std::uint_fast8_t>(qsId_) & 7U))) != 0U) (static_cast<void>(0)) \ (QP::QS::u8_fmt_(static_cast<std::uint8_t>( \ (static_cast<std::uint8_t>(((width_) << 4U) & 0x7U)) \ | static_cast<std::uint8_t>(QP::QS::I8_ENUM_T)), (data_))) \ (QP::QS::u8_fmt_(static_cast<std::uint8_t>( \ (static_cast<std::uint8_t>((width_) << 4U)) \ | static_cast<std::uint8_t>(QP::QS::U8_T)), (data_))) \ (QP::QS::u16_fmt_(static_cast<std::uint8_t>( \ (static_cast<std::uint8_t>((width_) << 4U)) \ | static_cast<std::uint8_t>(QP::QS::I16_T)), (data_))) \ (QP::QS::u16_fmt_(static_cast<std::uint8_t>((((width_) << 4U)) \ | static_cast<std::uint8_t>(QP::QS::U16_T)), (data_))) \ (QP::QS::u32_fmt_( \ static_cast<std::uint8_t>((static_cast<std::uint8_t>((width_) << 4U)) \ | static_cast<std::uint8_t>(QP::QS::I32_T)), (data_))) \ (QP::QS::u32_fmt_(static_cast<std::uint8_t>( \ (static_cast<std::uint8_t>((width_) << 4U)) \ | static_cast<std::uint8_t>(QP::QS::U32_T)), (data_))) \ (QP::QS::u64_fmt_(static_cast<std::uint8_t>( \ (static_cast<std::uint8_t>((width_) << 4U)) \ | static_cast<std::uint8_t>(QP::QS::I64_T)), (data_))) \ (QP::QS::u64_fmt_(static_cast<std::uint8_t>( \ (static_cast<std::uint8_t>((width_) << 4U)) \ | static_cast<std::uint8_t>(QP::QS::U64_T)), (data_))) \ (QP::QS::f32_fmt_(static_cast<std::uint8_t>( \ (static_cast<std::uint8_t>((width_) << 4U)) \ | static_cast<std::uint8_t>(QP::QS::F32_T)), (data_))) \ (QP::QS::f64_fmt_(static_cast<std::uint8_t>( \ (static_cast<std::uint8_t>((width_) << 4U)) \ | static_cast<std::uint8_t>(QP::QS::F64_T)), (data_))) (QP::QS::str_fmt_(str_)) (QP::QS::mem_fmt_((mem_), (size_))) \ (QP::QS::u8_fmt_(static_cast<std::uint8_t>(0x80U | ((group_) << 4U)) \ | static_cast<std::uint8_t>(QP::QS::I8_ENUM_T),\ static_cast<std::uint8_t>(value_))) (QP::QS::u16_raw_(QP::QS::onGetTime())) (QP::QS::u32_raw_(QP::QS::onGetTime())) (QP::QS::u16_fmt_(QP::QS::OBJ_T, \ reinterpret_cast<std::uint16_t>(obj_))) (QP::QS::u32_fmt_(QP::QS::OBJ_T, \ reinterpret_cast<std::uint32_t>(obj_))) (QP::QS::u64_fmt_(QP::QS::OBJ_T, \ reinterpret_cast<std::uint64_t>(obj_))) (QP::QS::u16_fmt_(QP::QS::FUN_T, \ reinterpret_cast<std::uint16_t>(fun_))) (QP::QS::u32_fmt_(QP::QS::FUN_T, \ reinterpret_cast<std::uint32_t>(fun_))) (QP::QS::u64_fmt_(QP::QS::FUN_T, \ reinterpret_cast<std::uint64_t>(fun_))) \ QP::QS::u8_fmt_(QP::QS::SIG_T, static_cast<std::uint8_t>(sig_)); \ QP::QS::obj_raw_(obj_) \ QP::QS::u16_fmt_(QP::QS::SIG_T, static_cast<std::uint16_t>(sig_)); \ QP::QS::obj_raw_(obj_) \ QP::QS::u32_fmt_(QP::QS::SIG_T, static_cast<std::uint32_t>(sig_)); \ QP::QS::obj_raw_(obj_) \ (QP::QS::sig_dict_pre_((sig_), (obj_), #sig_)) \ (QP::QS::obj_dict_pre_((obj_), #obj_)) \ (QP::QS::obj_arr_dict_pre_((obj_), (idx_), #obj_)) \ (QP::QS::fun_dict_pre_( \ QP::QS::force_cast<void (*)()>(fun_), #fun_)) \ (QP::QS::usr_dict_pre_((rec_), #rec_)) \ (QP::QS::enum_dict_pre_((value_), (group_), #value_)) (QP::QS::crit_entry_pre_()) (QP::QS::crit_exit_pre_()) \ (QP::QS::isr_entry_pre_((isrnest_), (prio_))) \ (QP::QS::isr_exit_pre_((isrnest_), (prio_))) (code_) \ (QP::QS::assertion_pre_((module_), (id_), (delay_))) (static_cast<std::uint16_t>(0xFFFFU)) (static_cast<std::uint8_t>(7U)) (static_cast<std::uint8_t>(0x0FU)) QF_CRIT_STAT QF_CRIT_ENTRY() QF_CRIT_EXIT() QF_MEM_SYS() QF_MEM_APP() #ifndef QP_HPP_ #define QP_HPP_ //============================================================================ #define QP_VERSION 733U #define QP_VERSION_STR "7.3.3" //! Encrypted current QP release (7.3.3) and date (2024-03-01) #define QP_RELEASE 0x70C4F752U //============================================================================ //! @cond INTERNAL #ifndef Q_SIGNAL_SIZE #define Q_SIGNAL_SIZE 2U #endif // ndef Q_SIGNAL_SIZE #ifndef QF_MAX_ACTIVE #define QF_MAX_ACTIVE 32U #endif #if (QF_MAX_ACTIVE > 64U) #error QF_MAX_ACTIVE exceeds the maximum of 64U; #endif #ifndef QF_MAX_TICK_RATE #define QF_MAX_TICK_RATE 1U #endif #if (QF_MAX_TICK_RATE > 15U) #error QF_MAX_TICK_RATE exceeds the maximum of 15U; #endif #ifndef QF_MAX_EPOOL #define QF_MAX_EPOOL 3U #endif #if (QF_MAX_EPOOL > 15U) #error QF_MAX_EPOOL exceeds the maximum of 15U; #endif #ifndef QF_TIMEEVT_CTR_SIZE #define QF_TIMEEVT_CTR_SIZE 4U #endif #if (QF_TIMEEVT_CTR_SIZE > 4U) #error QF_TIMEEVT_CTR_SIZE defined incorrectly, expected 1U, 2U, or 4U; #endif #ifndef QF_EVENT_SIZ_SIZE #define QF_EVENT_SIZ_SIZE 2U #endif #if (QF_EVENT_SIZ_SIZE > 4U) #error QF_EVENT_SIZ_SIZE defined incorrectly, expected 1U, 2U, or 4U; #endif //! @endcond //============================================================================ $declare ${glob-types} $declare ${QEP} $declare ${QEP-macros} $declare ${QF::types} $declare ${QF::QActive} $declare ${QF::QMActive} $declare ${QF::QTimeEvt} $declare ${QF::QTicker} $declare ${QF::QF-base} $declare ${QF::QF-dyn} extern "C" { $declare ${QF-extern-C} } // extern "C" $declare ${QF-macros} #endif // QP_HPP_ #ifndef QP_PKG_HPP_ #define QP_PKG_HPP_ $declare ${QF::QF-pkg} #define QF_CONST_CAST_(type_, ptr_) const_cast<type_>(ptr_) #define QF_PTR_RANGE_(x_, min_, max_) (((min_) <= (x_)) && ((x_) <= (max_))) #define Q_UINTPTR_CAST_(ptr_) (reinterpret_cast<std::uintptr_t>(ptr_)) #define Q_ACTION_CAST(act_) (reinterpret_cast<QP::QActionHandler>(act_)) namespace QP { // Bitmasks are for the QTimeEvt::refCtr_ attribute (inherited from QEvt). // In QTimeEvt this attribute is NOT used for reference counting. constexpr std::uint8_t TE_IS_LINKED = 1U << 7U; // flag constexpr std::uint8_t TE_WAS_DISARMED = 1U << 6U; // flag constexpr std::uint8_t TE_TICK_RATE = 0x0FU; // bitmask inline void QEvt_refCtr_inc_(QEvt const * const e) noexcept { (QF_CONST_CAST_(QEvt*, e))->refCtr_ = e->refCtr_ + 1U; } inline void QEvt_refCtr_dec_(QEvt const * const e) noexcept { (QF_CONST_CAST_(QEvt*, e))->refCtr_ = e->refCtr_ - 1U; } } // namespace QP #endif // QP_PKG_HPP_ #ifndef QEQUEUE_HPP_ #define QEQUEUE_HPP_ #ifndef QF_EQUEUE_CTR_SIZE #define QF_EQUEUE_CTR_SIZE 1U #endif namespace QP { #if (QF_EQUEUE_CTR_SIZE == 1U) using QEQueueCtr = std::uint8_t; #elif (QF_EQUEUE_CTR_SIZE == 2U) using QEQueueCtr = std::uint16_t; #elif (QF_EQUEUE_CTR_SIZE == 4U) using QEQueueCtr = std::uint32_t; #else #error "QF_EQUEUE_CTR_SIZE defined incorrectly, expected 1U, 2U, or 4U" #endif class QEvt; // forward declaration } // namespace QP $declare ${QF::QEQueue} #endif // QEQUEUE_HPP_ #ifndef QMPOOL_HPP_ #define QMPOOL_HPP_ #ifndef QF_MPOOL_SIZ_SIZE #define QF_MPOOL_SIZ_SIZE 2U #endif #ifndef QF_MPOOL_CTR_SIZE #define QF_MPOOL_CTR_SIZE 2U #endif namespace QP { #if (QF_MPOOL_SIZ_SIZE == 1U) using QMPoolSize = std::uint8_t; #elif (QF_MPOOL_SIZ_SIZE == 2U) using QMPoolSize = std::uint16_t; #elif (QF_MPOOL_SIZ_SIZE == 4U) using QMPoolSize = std::uint32_t; #else #error "QF_MPOOL_SIZ_SIZE defined incorrectly, expected 1U, 2U, or 4U" #endif #if (QF_MPOOL_CTR_SIZE == 1U) using QMPoolCtr = std::uint8_t; #elif (QF_MPOOL_CTR_SIZE == 2U) using QMPoolCtr = std::uint16_t; #elif (QF_MPOOL_CTR_SIZE == 4U) using QMPoolCtr = std::uint32_t; #else #error "QF_MPOOL_CTR_SIZE defined incorrectly, expected 1U, 2U, or 4U" #endif } // namespace QP #define QF_MPOOL_EL(evType_) struct { \ QP::QFreeBlock sto_[((sizeof(evType_) - 1U) \ / sizeof(QP::QFreeBlock)) + 1U]; } $declare ${QF::QFreeBlock} $declare ${QF::QMPool} #endif // QMPOOL_HPP_ #ifndef QV_HPP_ #define QV_HPP_ $declare ${QV::QV-base} //============================================================================ // interface used only for internal implementation, but not in applications #ifdef QP_IMPL $declare ${QV-impl} $declare ${QF_EPOOL-impl} #endif // QP_IMPL #endif // QV_HPP_ #ifndef QK_HPP_ #define QK_HPP_ $declare ${QK::QSchedStatus} $declare ${QK::QK-base} extern "C" { $declare ${QK-extern-C} } // extern "C" //============================================================================ // interface used only for internal implementation, but not in applications #ifdef QP_IMPL $declare ${QK-impl} $declare ${QF_EPOOL-impl} #endif // QP_IMPL #endif // QK_HPP_ #ifndef QXK_HPP_ #define QXK_HPP_ $declare ${QXK::QSchedStatus} $declare ${QXK::QXTHREAD_NO_TIMEOUT} $declare ${QXK::QXK-base} $declare ${QXK::QXThread} $declare ${QXK::QXSemaphore} $declare ${QXK::QXMutex} extern "C" { $declare ${QXK-extern-C} } // extern "C" //============================================================================ // interface used only for internal implementation, but not in applications #ifdef QP_IMPL $declare ${QXK-impl} $declare ${QF_EPOOL-impl} namespace QP { namespace QXK { enum TimeoutSigs : QSignal { DELAY_SIG = 1U, TIMEOUT_SIG }; } // namespace QXK } // namespace QP #endif // QP_IMPL #endif // QXK_HPP_ #ifndef QS_HPP_ #define QS_HPP_ #ifndef Q_SPY #error "Q_SPY must be defined to include qs.hpp" #endif //============================================================================ //! @cond INTERNAL #ifndef QS_CTR_SIZE #define QS_CTR_SIZE 2U #endif #ifndef QS_TIME_SIZE #define QS_TIME_SIZE 4U #endif //! @endcond //============================================================================ $declare ${QS::types} $declare ${QS::filters} $declare ${QS-macros} //============================================================================ //! @cond INTERNAL namespace QP { namespace QS { struct Attr { void const * locFilter_AP; //!< @deprecated std::uint8_t * buf; QSCtr end; QSCtr volatile head; QSCtr volatile tail; QSCtr volatile used; std::uint8_t volatile seq; std::uint8_t volatile chksum; std::uint8_t volatile critNest; std::uint8_t flags; }; extern Attr priv_; void glbFilter_(std::int_fast16_t const filter) noexcept; void locFilter_(std::int_fast16_t const filter) noexcept; void beginRec_(std::uint_fast8_t const rec) noexcept; void endRec_() noexcept; void u8_raw_(std::uint8_t const d) noexcept; void u8u8_raw_(std::uint8_t const d1, std::uint8_t const d2) noexcept; void u16_raw_(std::uint16_t d) noexcept; void u32_raw_(std::uint32_t d) noexcept; void u64_raw_(std::uint64_t d) noexcept; void obj_raw_(void const * const obj) noexcept; void str_raw_(char const * s) noexcept; void u8_fmt_(std::uint8_t const format, std::uint8_t const d) noexcept; void u16_fmt_(std::uint8_t format, std::uint16_t d) noexcept; void u32_fmt_(std::uint8_t format, std::uint32_t d) noexcept; void u64_fmt_(std::uint8_t format, std::uint64_t d) noexcept; void f32_fmt_(std::uint8_t format, float32_t f) noexcept; void f64_fmt_(std::uint8_t format, float64_t d) noexcept; void str_fmt_(char const * s) noexcept; void mem_fmt_(std::uint8_t const * blk, std::uint8_t size) noexcept; void sig_dict_pre_(QSignal const sig, void const * const obj, char const * const name) noexcept; void obj_dict_pre_(void const * const obj, char const * const name) noexcept; void obj_arr_dict_pre_(void const * const obj, std::uint_fast16_t const idx, char const * const name) noexcept; void fun_dict_pre_(QSpyFunPtr fun, char const * const name) noexcept; void usr_dict_pre_(enum_t const rec, char const * const name) noexcept; void enum_dict_pre_(enum_t const value, std::uint8_t const group, char const * const name) noexcept; void assertion_pre_(char const * const module, int_t const id, std::uint32_t const delay) noexcept; void crit_entry_pre_() noexcept; void crit_exit_pre_() noexcept; void isr_entry_pre_(std::uint8_t const isrnest, std::uint8_t const prio) noexcept; void isr_exit_pre_(std::uint8_t const isrnest, std::uint8_t const prio) noexcept; void target_info_pre_(std::uint8_t const isReset); } // namespace QS } // namespace QP //! @endcond //============================================================================ $declare ${QS::QS-TX} //============================================================================ #ifdef Q_UTEST $declare ${QS::QUTest} #define QUTEST_ON_POST 124 //============================================================================ //! @cond INTERNAL namespace QP { namespace QS { struct TestAttr { TProbe tpBuf[16]; std::uint8_t tpNum; QSTimeCtr testTime; QPSet readySet; QPSet readySet_dis; std::uint_fast8_t intLock; }; extern TestAttr tstPriv_; void test_pause_(); std::uint32_t getTestProbe_(QSpyFunPtr const api) noexcept; } // namespace QS } // namespace QP //! @endcond //============================================================================ // QP-stub for QUTest // NOTE: The QP-stub is needed for unit testing QP applications, // but might NOT be needed for testing QP itself. #if (Q_UTEST != 0) $declare ${QS::QUTest-stub::QHsmDummy} $declare ${QS::QUTest-stub::QActiveDummy} #endif // Q_UTEST != 0 #define QS_TEST_PROBE_DEF(fun_) \ std::uint32_t const qs_tp_ = \ QP::QS::getTestProbe_(QP::QS::force_cast<void (*)()>(fun_)); #define QS_TEST_PROBE(code_) \ if (qs_tp_ != 0U) { code_ } #define QS_TEST_PROBE_ID(id_, code_) \ if (qs_tp_ == static_cast<std::uint32_t>(id_)) { code_ } #define QS_TEST_PAUSE() (QP::QS::test_pause_()) #else // Q_UTEST not defined // dummy definitions when not building for QUTEST #define QS_TEST_PROBE_DEF(fun_) #define QS_TEST_PROBE(code_) #define QS_TEST_PROBE_ID(id_, code_) #define QS_TEST_PAUSE() (static_cast<void>(0)) #endif // Q_UTEST //============================================================================ //! @cond INTERNAL namespace QP { namespace QS { //............................................................................ struct CmdVar { std::uint32_t param1; std::uint32_t param2; std::uint32_t param3; std::uint8_t idx; std::uint8_t cmdId; }; struct TickVar { std::uint_fast8_t rate; }; struct PeekVar { std::uint16_t offs; std::uint8_t size; std::uint8_t num; std::uint8_t idx; }; struct PokeVar { std::uint32_t data; std::uint16_t offs; std::uint8_t size; std::uint8_t num; std::uint8_t idx; std::uint8_t fill; }; struct FltVar { std::uint8_t data[16]; std::uint8_t idx; std::uint8_t recId; // global/local }; struct ObjVar { QSObj addr; std::uint8_t idx; std::uint8_t kind; // see qs.hpp, enum QSpyObjKind std::uint8_t recId; }; struct EvtVar { QP::QEvt *e; std::uint8_t *p; QP::QSignal sig; std::uint16_t len; std::uint8_t prio; std::uint8_t idx; }; struct RxAttr { void * currObj[8]; std::uint8_t * buf; QSCtr end; QSCtr volatile head; QSCtr volatile tail; std::uint8_t state; std::uint8_t esc; std::uint8_t seq; std::uint8_t chksum; #ifdef Q_UTEST bool inTestLoop; #endif union Variant { CmdVar cmd; TickVar tick; PeekVar peek; PokeVar poke; FltVar flt; ObjVar obj; EvtVar evt; #ifdef Q_UTEST QP::QS::TProbe tp; #endif // Q_UTEST } var; } ; extern RxAttr rxPriv_; } // namespace QS } // namespace QP //! @endcond //============================================================================ $declare ${QS::QS-RX} #endif // QS_HPP_ #ifndef QS_DUMMY_HPP_ #define QS_DUMMY_HPP_ #ifdef Q_SPY #error "Q_SPY must NOT be defined to include qs_dummy.hpp" #endif #define QS_INIT(arg_) (true) #define QS_EXIT() static_cast<void>(0) #define QS_DUMP() static_cast<void>(0) #define QS_GLB_FILTER(rec_) static_cast<void>(0) #define QS_LOC_FILTER(qsId_) static_cast<void>(0) #define QS_GET_BYTE(pByte_) (0xFFFFU) #define QS_GET_BLOCK(pSize_) (nullptr) #define QS_BEGIN_ID(rec_, qsId_) if (false) { #define QS_END() } #define QS_BEGIN_INCRIT(rec_, qsId_) if (false) { #define QS_END_INCRIT() } #define QS_I8(width_, data_) static_cast<void>(0) #define QS_U8(width_, data_) static_cast<void>(0) #define QS_I16(width_, data_) static_cast<void>(0) #define QS_U16(width_, data_) static_cast<void>(0) #define QS_I32(width_, data_) static_cast<void>(0) #define QS_U32(width_, data_) static_cast<void>(0) #define QS_F32(width_, data_) static_cast<void>(0) #define QS_F64(width_, data_) static_cast<void>(0) #define QS_I64(width_, data_) static_cast<void>(0) #define QS_U64(width_, data_) static_cast<void>(0) #define QS_ENUM(group_, value_) static_cast<void>(0) #define QS_STR(str_) static_cast<void>(0) #define QS_MEM(mem_, size_) static_cast<void>(0) #define QS_SIG(sig_, obj_) static_cast<void>(0) #define QS_OBJ(obj_) static_cast<void>(0) #define QS_FUN(fun_) static_cast<void>(0) #define QS_SIG_DICTIONARY(sig_, obj_) static_cast<void>(0) #define QS_OBJ_DICTIONARY(obj_) static_cast<void>(0) #define QS_OBJ_ARR_DICTIONARY(obj_, idx_) static_cast<void>(0) #define QS_FUN_DICTIONARY(fun_) static_cast<void>(0) #define QS_USR_DICTIONARY(rec_) static_cast<void>(0) #define QS_ENUM_DICTIONARY(value_, group_) static_cast<void>(0) #define QS_ASSERTION(module_, loc_, delay_) static_cast<void>(0) #define QS_FLUSH() static_cast<void>(0) #define QS_TEST_PROBE_DEF(fun_) #define QS_TEST_PROBE(code_) #define QS_TEST_PROBE_ID(id_, code_) #define QS_TEST_PAUSE() static_cast<void>(0) #define QS_OUTPUT() static_cast<void>(0) #define QS_RX_INPUT() static_cast<void>(0) #define QS_ONLY(code_) static_cast<void>(0) //============================================================================ // interface used only for internal implementation, but not in applications #ifdef QP_IMPL // predefined QS trace records #define QS_BEGIN_PRE_(rec_, qsId_) if (false) { #define QS_END_PRE_() } #define QS_BEGIN_PRE_(rec_, qsId_) if (false) { #define QS_END_PRE_() } #define QS_U8_PRE_(data_) static_cast<void>(0) #define QS_2U8_PRE_(data1_, data2_) static_cast<void>(0) #define QS_U16_PRE_(data_) static_cast<void>(0) #define QS_U32_PRE_(data_) static_cast<void>(0) #define QS_TIME_PRE_() static_cast<void>(0) #define QS_SIG_PRE_(sig_) static_cast<void>(0) #define QS_EVS_PRE_(size_) static_cast<void>(0) #define QS_OBJ_PRE_(obj_) static_cast<void>(0) #define QS_FUN_PRE_(fun_) static_cast<void>(0) #define QS_EQC_PRE_(ctr_) static_cast<void>(0) #define QS_MPC_PRE_(ctr_) static_cast<void>(0) #define QS_MPS_PRE_(size_) static_cast<void>(0) #define QS_TEC_PRE_(ctr_) static_cast<void>(0) #define QS_CRIT_STAT #define QS_CRIT_ENTRY() static_cast<void>(0) #define QS_CRIT_EXIT() static_cast<void>(0) #define QS_MEM_SYS() static_cast<void>(0) #define QS_MEM_APP() static_cast<void>(0) #define QS_TR_CRIT_ENTRY() static_cast<void>(0) #define QS_TR_CRIT_EXIT() static_cast<void>(0) #define QS_TR_ISR_ENTRY(isrnest_, prio_) static_cast<void>(0) #define QS_Tr_ISR_EXIT(isrnest_, prio_) static_cast<void>(0) #endif // QP_IMPL #endif // QS_DUMMY_HPP_ #ifndef QS_PKG_HPP_ #define QS_PKG_HPP_ //============================================================================ //! @cond INTERNAL namespace QP { //! QS received record types (RX channel) enum QSpyRxRecords : std::uint8_t { QS_RX_INFO, //!< query Target info (ver, config, tstamp) QS_RX_COMMAND, //!< execute a user-defined command in the Target QS_RX_RESET, //!< reset the Target QS_RX_TICK, //!< call system clock tick in the Target QS_RX_PEEK, //!< peek Target memory QS_RX_POKE, //!< poke Target memory QS_RX_FILL, //!< fill Target memory QS_RX_TEST_SETUP, //!< test setup QS_RX_TEST_TEARDOWN, //!< test teardown QS_RX_TEST_PROBE, //!< set a Test-Probe in the Target QS_RX_GLB_FILTER, //!< set global filters in the Target QS_RX_LOC_FILTER, //!< set local filters in the Target QS_RX_AO_FILTER, //!< set local AO filter in the Target QS_RX_CURR_OBJ, //!< set the "current-object" in the Target QS_RX_TEST_CONTINUE, //!< continue a test after QS_TEST_PAUSE() QS_RX_QUERY_CURR, //!< query the "current object" in the Target QS_RX_EVENT //!< inject an event to the Target }; constexpr std::uint8_t QS_FRAME {0x7EU}; constexpr std::uint8_t QS_ESC {0x7DU}; constexpr std::uint8_t QS_ESC_XOR {0x20U}; constexpr std::uint8_t QS_GOOD_CHKSUM {0xFFU}; } // namespace QP //---------------------------------------------------------------------------- #define QS_BEGIN_PRE_(rec_, qsId_) \ if (QS_GLB_CHECK_(rec_) && QS_LOC_CHECK_(qsId_)) { \ QP::QS::beginRec_(static_cast<std::uint_fast8_t>(rec_)); #define QS_END_PRE_() QP::QS::endRec_(); } #define QS_U8_PRE_(data_) \ (QP::QS::u8_raw_(static_cast<std::uint8_t>(data_))) #define QS_2U8_PRE_(data1_, data2_) \ (QP::QS::u8u8_raw_(static_cast<std::uint8_t>(data1_), \ static_cast<std::uint8_t>(data2_))) #define QS_U16_PRE_(data_) \ (QP::QS::u16_raw_(static_cast<std::uint16_t>(data_))) #define QS_U32_PRE_(data_) \ (QP::QS::u32_raw_(static_cast<std::uint32_t>(data_))) #define QS_STR_PRE_(msg_) (QP::QS::str_raw_(msg_)) #define QS_OBJ_PRE_(obj_) (QP::QS::obj_raw_(obj_)) #if (!defined Q_SIGNAL_SIZE || (Q_SIGNAL_SIZE == 1U)) #define QS_SIG_PRE_(sig_) \ (QP::QS::u8_raw_(static_cast<std::uint8_t>(sig_))) #elif (Q_SIGNAL_SIZE == 2U) #define QS_SIG_PRE_(sig_) \ (QP::QS::u16_raw_(static_cast<std::uint16_t>(sig_))) #elif (Q_SIGNAL_SIZE == 4U) #define QS_SIG_PRE_(sig_) \ (QP::QS::u32_raw_(static_cast<std::uint32_t>(sig_))) #endif #if (!defined QS_FUN_PTR_SIZE || (QS_FUN_PTR_SIZE == 2U)) #define QS_FUN_PRE_(fun_) \ (QP::QS::u16_raw_(reinterpret_cast<std::uint16_t>(fun_))) #elif (QS_FUN_PTR_SIZE == 4U) #define QS_FUN_PRE_(fun_) \ (QP::QS::u32_raw_(reinterpret_cast<std::uint32_t>(fun_))) #elif (QS_FUN_PTR_SIZE == 8U) #define QS_FUN_PRE_(fun_) \ (QP::QS::u64_raw_(reinterpret_cast<std::uint64_t>(fun_))) #else #define QS_FUN_PRE_(fun_) \ (QP::QS::u32_raw_(reinterpret_cast<std::uint32_t>(fun_))) #endif //---------------------------------------------------------------------------- #if (!defined QF_EQUEUE_CTR_SIZE || (QF_EQUEUE_CTR_SIZE == 1U)) #define QS_EQC_PRE_(ctr_) \ QP::QS::u8_raw_(static_cast<std::uint8_t>(ctr_)) #elif (QF_EQUEUE_CTR_SIZE == 2U) #define QS_EQC_PRE_(ctr_) \ QP::QS::u16_raw_(static_cast<std::uint16_t>(ctr_)) #elif (QF_EQUEUE_CTR_SIZE == 4U) #define QS_EQC_PRE_(ctr_) \ QP::QS::u32_raw_(static_cast<std::uint32_t>(ctr_)) #else #error "QF_EQUEUE_CTR_SIZE not defined" #endif #if (!defined QF_EVENT_SIZ_SIZE || (QF_EVENT_SIZ_SIZE == 1U)) #define QS_EVS_PRE_(size_) \ QP::QS::u8_raw_(static_cast<std::uint8_t>(size_)) #elif (QF_EVENT_SIZ_SIZE == 2U) #define QS_EVS_PRE_(size_) \ QP::QS::u16_raw_(static_cast<std::uint16_t>(size_)) #elif (QF_EVENT_SIZ_SIZE == 4U) #define QS_EVS_PRE_(size_) \ QP::QS::u32_raw_(static_cast<std::uint32_t>(size_)) #endif #if (!defined QF_MPOOL_SIZ_SIZE || (QF_MPOOL_SIZ_SIZE == 1U)) #define QS_MPS_PRE_(size_) \ QP::QS::u8_raw_(static_cast<std::uint8_t>(size_)) #elif (QF_MPOOL_SIZ_SIZE == 2U) #define QS_MPS_PRE_(size_) \ QP::QS::u16_raw_(static_cast<std::uint16_t>(size_)) #elif (QF_MPOOL_SIZ_SIZE == 4U) #define QS_MPS_PRE_(size_) \ QP::QS::u32_raw_(static_cast<std::uint32_t>(size_)) #endif #if (!defined QF_MPOOL_CTR_SIZE || (QF_MPOOL_CTR_SIZE == 1U)) #define QS_MPC_PRE_(ctr_) \ QP::QS::u8_raw_(static_cast<std::uint8_t>(ctr_)) #elif (QF_MPOOL_CTR_SIZE == 2U) #define QS_MPC_PRE_(ctr_) \ QP::QS::u16_raw_(static_cast<std::uint16_t>(ctr_)) #elif (QF_MPOOL_CTR_SIZE == 4U) #define QS_MPC_PRE_(ctr_) \ QP::QS::u32_raw_(static_cast<std::uint32_t>(ctr_)) #endif #if (!defined QF_TIMEEVT_CTR_SIZE || (QF_TIMEEVT_CTR_SIZE == 1U)) #define QS_TEC_PRE_(ctr_) \ QP::QS::u8_raw_(static_cast<std::uint8_t>(ctr_)) #elif (QF_TIMEEVT_CTR_SIZE == 2U) #define QS_TEC_PRE_(ctr_) \ QP::QS::u16_raw_(static_cast<std::uint16_t>(ctr_)) #elif (QF_TIMEEVT_CTR_SIZE == 4U) #define QS_TEC_PRE_(ctr_) \ QP::QS::u32_raw_(static_cast<std::uint32_t>(ctr_)) #endif #define QS_REC_NUM_(enum_) (static_cast<std::uint_fast8_t>(enum_)) //---------------------------------------------------------------------------- #define QS_INSERT_BYTE_(b_) \ buf[head] = (b_); \ ++head; \ if (head == end) { \ head = 0U; \ } #define QS_INSERT_ESC_BYTE_(b_) \ chksum = static_cast<std::uint8_t>(chksum + (b_)); \ if (((b_) != QS_FRAME) && ((b_) != QS_ESC)) { \ QS_INSERT_BYTE_(b_) \ } \ else { \ QS_INSERT_BYTE_(QS_ESC) \ QS_INSERT_BYTE_(static_cast<std::uint8_t>((b_) ^ QS_ESC_XOR)) \ priv_.used = priv_.used + 1U; \ } //---------------------------------------------------------------------------- #if (defined Q_UTEST) && (Q_UTEST != 0) namespace QP { namespace QS { void processTestEvts_(); } // namespace QS } // namespace QP #endif // Q_UTEST != 0 //! @endcond //============================================================================ #endif // QS_PKG_HPP_ #ifndef QSTAMP_HPP_ #define QSTAMP_HPP_ namespace QP { extern char const BUILD_DATE[12]; extern char const BUILD_TIME[9]; } // namespace QP #endif // QSTAMP_HPP_ #ifndef QPCPP_HPP_ #define QPCPP_HPP_ //============================================================================ #include "qp_port.hpp" // QP port from the port directory #include "qsafe.h" // QP Functional Safety (FuSa) Subsystem #ifdef Q_SPY // software tracing enabled? #include "qs_port.hpp" // QS/C++ port from the port directory #else #include "qs_dummy.hpp" // QS/C++ dummy (inactive) interface #endif //============================================================================ #ifndef QP_API_VERSION #define QP_API_VERSION 0 #endif // QP_API_VERSION //============================================================================ // QP/C++ API compatibility layer... #if (QP_API_VERSION < 730) //! @deprecated plain 'char' is no longer forbidden in MISRA/AUTOSAR-C++ using char_t = char; //! @deprecated assertion failure handler //! Use Q_onError() instead. #define Q_onAssert(module_, id_) Q_onError(module_, id_) //! @deprecated #Q_NASSERT preprocessor switch to disable QP assertions #ifdef Q_NASSERT // #Q_UNSAFE now replaces the functionality of Q_NASSERT #define Q_UNSAFE //! @deprecated general purpose assertion with user-specified ID //! number that **always** evaluates the `expr_` expression. #define Q_ALLEGE_ID(id_, expr_) ((void)(expr_)) #else // QP FuSa Subsystem enabled //! @deprecated general purpose assertion with user-specified ID //! number that **always** evaluates the `expr_` expression. //! @note //! The use of this macro is no longer recommended. #define Q_ALLEGE_ID(id_, expr_) if (!(expr_)) { \ QF_CRIT_STAT \ QF_CRIT_ENTRY(); \ Q_onError(&Q_this_module_[0], (id_)); \ QF_CRIT_EXIT(); \ } else ((void)0) #endif //! @deprecated general purpose assertion without ID number //! that **always** evaluates the `expr_` expression. //! Instead of ID number, this macro is based on the standard //! `__LINE__` macro. //! //! @note The use of this macro is no longer recommended. #define Q_ALLEGE(expr_) Q_ALLEGE_ID(__LINE__, (expr_)) //! Static (compile-time) assertion. //! //! @deprecated //! Use Q_ASSERT_STATIC() or better yet `static_assert()` instead. //! #define Q_ASSERT_COMPILE(expr_) Q_ASSERT_STATIC(expr_) //! @deprecated use QP::QF::NO_MARGIN instead #define QF_NO_MARGIN QP::QF::NO_MARGIN //============================================================================ #if (QP_API_VERSION < 691) //! @deprecated enable the QS global filter #define QS_FILTER_ON(rec_) QS_GLB_FILTER((rec_)) //! @deprecated disable the QS global filter #define QS_FILTER_OFF(rec_) QS_GLB_FILTER(-(rec_)) //! @deprecated enable the QS local filter for SM (state machine) object #define QS_FILTER_SM_OBJ(obj_) (static_cast<void>(0)) //! @deprecated enable the QS local filter for AO (active objects) #define QS_FILTER_AO_OBJ(obj_) (static_cast<void>(0)) //! @deprecated enable the QS local filter for MP (memory pool) object #define QS_FILTER_MP_OBJ(obj_) (static_cast<void>(0)) //! @deprecated enable the QS local filter for EQ (event queue) object #define QS_FILTER_EQ_OBJ(obj_) (static_cast<void>(0)) //! @deprecated enable the QS local filter for TE (time event) object #define QS_FILTER_TE_OBJ(obj_) (static_cast<void>(0)) #ifdef Q_SPY //! @deprecated local Filter for a generic application object `obj_`. #define QS_FILTER_AP_OBJ(obj_) \ (QP::QS::filt_.loc_AP = (obj_)) //! @deprecated begin of a user QS record, instead use QS_BEGIN_ID() #define QS_BEGIN(rec_, obj_) \ if (QS_GLB_FILTER_(rec_) && \ ((QP::QS::filt_.loc[QP::QS::AP_OBJ] == nullptr) \ || (QP::QS::filt_.loc_AP == (obj_)))) \ { \ QS_CRIT_STAT \ QS_CRIT_ENTRY(); \ QP::QS::beginRec_(static_cast<std::uint_fast8_t>(rec_)); \ QS_TIME_PRE_(); //! @deprecated output hex-formatted std::uint32_t to the QS record #define QS_U32_HEX(width_, data_) \ (QP::QS::u32_fmt_(static_cast<std::uint8_t>( \ (static_cast<std::uint8_t>((width_) << 4)) | QS_HEX_FMT), (data_))) #else #define QS_FILTER_AP_OBJ(obj_) (static_cast<void>(0)) #define QS_BEGIN(rec_, obj_) if (false) { #define QS_U32_HEX(width_, data_) (Q_UNUSED_PAR(0)) #endif // def Q_SPY //============================================================================ #if (QP_API_VERSION < 680) //! @deprecated //! Macro to specify a tran. in the "me->" impl-strategy. //! Instead use the new impl-strategy without the "me->" pointer, where //! you call tran(Q_STATE_CAST(target_)). #define Q_TRAN(target_) (me->tran(Q_STATE_CAST(target_))) //! @deprecated //! Macro to specify a tran-to-history in the "me->" impl-strategy. //! Instead use the new impl-strategy without the "me->" pointer, where //! you call tran_hist(Q_STATE_CAST(hist_)). #define Q_TRAN_HIST(hist_) (me->tran_hist((hist_))) //! @deprecated //! Macro to specify the superstate in the "me->" impl-strategy. //! Instead use the new impl-strategy without the "me->" pointer, where //! you call super(state_)). #define Q_SUPER(state_) (me->super(Q_STATE_CAST(state_))) //! @deprecated //! Macro to call in a QM state entry-handler. Applicable only to QMSMs. //! Instead use the new impl-strategy without the "me->" pointer, where //! the QM-generated code calls qm_entry(Q_STATE_CAST(state_)). #define QM_ENTRY(state_) (me->qm_entry((state_))) //! @deprecated //! Macro to call in a QM state exit-handler. Applicable only to QMSMs. //! Instead use the new impl-strategy without the "me->" pointer, where //! the QM-generated code calls qm_exit(Q_STATE_CAST(state_)). #define QM_EXIT(state_) (me->qm_exit((state_))) //! @deprecated //! Macro to call in a QM submachine exit-handler. Applicable only to QMSMs. //! Instead use the new impl-strategy without the "me->" pointer, where //! the QM-generated code calls qm_sm_exit(Q_STATE_CAST(state_)). #define QM_SM_EXIT(state_) (me->qm_sm_exit((state_))) //! @deprecated //! Macro to call in a QM state-handler when it executes a tran. //! Instead use the new impl-strategy without the "me->" pointer, where //! the QM-generated code calls qm_tran((tatbl_)). #define QM_TRAN(tatbl_) (me->qm_tran((tatbl_))) //! @deprecated //! Macro to call in a QM state-handler when it executes an initial tran. //! Instead use the new impl-strategy without the "me->" pointer, where //! the QM-generated code calls qm_tran_init((tatbl_)). #define QM_TRAN_INIT(tatbl_) (me->qm_tran_init((tatbl_))) //! @deprecated //! Macro to call in a QM state-handler when it executes a tran-to-history. //! Instead use the new impl-strategy without the "me->" pointer, where //! the QM-generated code calls qm_tran_hist((history_), (tatbl_)). #define QM_TRAN_HIST(history_, tatbl_) \ (me->qm_tran_hist((history_), (tatbl_))) //! @deprecated //! Macro to call in a QM state-handler when it executes an initial tran. //! Instead use the new impl-strategy without the "me->" pointer, where //! the QM-generated code calls qm_tran_ep((tatbl_)). #define QM_TRAN_EP(tatbl_) (me->qm_tran_ep((tatbl_))) //! @deprecated //! Macro to call in a QM state-handler when it executes a tran-to-exit-point. //! Instead use the new impl-strategy without the "me->" pointer, where //! the QM-generated code calls qm_tran_xp((xp_), (tatbl_)). #define QM_TRAN_XP(xp_, tatbl_) (me->qm_tran_xp((xp_), (tatbl_))) //! @deprecated //! Designates the superstate of a given state in a subclass of QP::QMsm. //! Instead use the new impl-strategy without the "me->" pointer, where //! the QM-generated code calls qm_super_sub((state_)). #define QM_SUPER_SUB(state_) (me->qm_super_sub((state_))) #endif // QP_API_VERSION < 680 #endif // QP_API_VERSION < 691 #endif // QP_API_VERSION < 700 #endif // QPCPP_HPP_ #define QP_IMPL // this is QP implementation #include "qp_port.hpp" // QP port #include "qp_pkg.hpp" // QP package-scope interface #include "qsafe.h" // QP Functional Safety (FuSa) Subsystem #ifdef Q_SPY // QS software tracing enabled? #include "qs_port.hpp" // QS port #include "qs_pkg.hpp" // QS facilities for pre-defined trace records #else #include "qs_dummy.hpp" // disable the QS software tracing #endif // Q_SPY //============================================================================ //! @cond INTERNAL // unnamed namespace for local definitions with internal linkage namespace { Q_DEFINE_THIS_MODULE("qep_hsm") // immutable events corresponding to the reserved signals. static QP::QEvt const l_reservedEvt_[4] { QP::QEvt(static_cast<QP::QSignal>(QP::QHsm::Q_EMPTY_SIG)), QP::QEvt(static_cast<QP::QSignal>(QP::QHsm::Q_ENTRY_SIG)), QP::QEvt(static_cast<QP::QSignal>(QP::QHsm::Q_EXIT_SIG)), QP::QEvt(static_cast<QP::QSignal>(QP::QHsm::Q_INIT_SIG)) }; } // unnamed namespace // helper macro to handle reserved event in an QHsm #define QHSM_RESERVED_EVT_(state_, sig_) \ ((*(state_))(this, &l_reservedEvt_[(sig_)])) // helper macro to trace state entry #define QS_STATE_ENTRY_(state_, qsId_) \ QS_CRIT_ENTRY(); \ QS_MEM_SYS(); \ QS_BEGIN_PRE_(QS_QEP_STATE_ENTRY, (qsId_)) \ QS_OBJ_PRE_(this); \ QS_FUN_PRE_(state_); \ QS_END_PRE_() \ QS_MEM_APP(); \ QS_CRIT_EXIT() // helper macro to trace state exit #define QS_STATE_EXIT_(state_, qsId_) \ QS_CRIT_ENTRY(); \ QS_MEM_SYS(); \ QS_BEGIN_PRE_(QS_QEP_STATE_EXIT, (qsId_)) \ QS_OBJ_PRE_(this); \ QS_FUN_PRE_(state_); \ QS_END_PRE_() \ QS_MEM_APP(); \ QS_CRIT_EXIT() //! @endcond //============================================================================ $define ${QEP::versionStr[]} $define ${QEP::QHsm} #define QP_IMPL // this is QP implementation #include "qp_port.hpp" // QP port #include "qp_pkg.hpp" // QP package-scope interface #include "qsafe.h" // QP Functional Safety (FuSa) Subsystem #ifdef Q_SPY // QS software tracing enabled? #include "qs_port.hpp" // QS port #include "qs_pkg.hpp" // QS facilities for pre-defined trace records #else #include "qs_dummy.hpp" // disable the QS software tracing #endif // Q_SPY //============================================================================ //! @cond INTERNAL // unnamed namespace for local definitions with internal linkage namespace { Q_DEFINE_THIS_MODULE("qep_msm") // maximum depth of state nesting in a QMsm (including the top level) static constexpr std::int_fast8_t MAX_NEST_DEPTH_ {6}; // maximum length of transition-action array static constexpr std::int_fast8_t MAX_TRAN_LENGTH_ {3*MAX_NEST_DEPTH_}; // maximum depth of entry levels in a MSM for tran. to history. static constexpr std::int_fast8_t MAX_ENTRY_DEPTH_ {4}; // top-state object for QMsm-style state machines QP::QMState const l_msm_top_s = { nullptr, nullptr, nullptr, nullptr, nullptr }; } // unnamed namespace //! @endcond //============================================================================ $define ${QEP::QMsm} #define QP_IMPL // this is QP implementation #include "qp_port.hpp" // QP port #include "qp_pkg.hpp" // QP package-scope interface #include "qsafe.h" // QP Functional Safety (FuSa) Subsystem #ifdef Q_SPY // QS software tracing enabled? #include "qs_port.hpp" // QS port #include "qs_pkg.hpp" // QS facilities for pre-defined trace records #else #include "qs_dummy.hpp" // disable the QS software tracing #endif // Q_SPY // unnamed namespace for local definitions with internal linkage namespace { //Q_DEFINE_THIS_MODULE("qf_act") } // unnamed namespace $define ${QF::QActive::registry_[QF_MAX_ACTIVE + 1U]} $define ${QF::QF-pkg} $define ${QF::types::QF_LOG2} #define QP_IMPL // this is QP implementation #include "qp_port.hpp" // QP port #include "qp_pkg.hpp" // QP package-scope interface #include "qsafe.h" // QP Functional Safety (FuSa) Subsystem #ifdef Q_SPY // QS software tracing enabled? #include "qs_port.hpp" // QS port #include "qs_pkg.hpp" // QS facilities for pre-defined trace records #else #include "qs_dummy.hpp" // disable the QS software tracing #endif // Q_SPY //============================================================================ // unnamed namespace for local definitions with internal linkage namespace { Q_DEFINE_THIS_MODULE("qf_actq") } // unnamed namespace $define ${QF::QActive::post_} $define ${QF::QActive::postLIFO} $define ${QF::QActive::get_} $define ${QF::QTicker} #define QP_IMPL // this is QP implementation #include "qp_port.hpp" // QP port #include "qp_pkg.hpp" // QP package-scope interface #include "qsafe.h" // QP Functional Safety (FuSa) Subsystem #ifdef Q_SPY // QS software tracing enabled? #include "qs_port.hpp" // QS port #include "qs_pkg.hpp" // QS facilities for pre-defined trace records #else #include "qs_dummy.hpp" // disable the QS software tracing #endif // Q_SPY // unnamed namespace for local definitions with internal linkage namespace { Q_DEFINE_THIS_MODULE("qf_defer") } // unnamed namespace $define ${QF::QActive::defer} $define ${QF::QActive::recall} $define ${QF::QActive::flushDeferred} #define QP_IMPL // this is QP implementation #include "qp_port.hpp" // QP port #include "qp_pkg.hpp" // QP package-scope interface #include "qsafe.h" // QP Functional Safety (FuSa) Subsystem #ifdef Q_SPY // QS software tracing enabled? #include "qs_port.hpp" // QS port #include "qs_pkg.hpp" // QS facilities for pre-defined trace records #else #include "qs_dummy.hpp" // disable the QS software tracing #endif // Q_SPY #if (QF_MAX_EPOOL > 0U) // mutable events configured? // unnamed namespace for local definitions with internal linkage namespace { Q_DEFINE_THIS_MODULE("qf_dyn") } // unnamed namespace $define ${QEP::QEvt::ctor} $define ${QF::QF-dyn} #endif // (QF_MAX_EPOOL > 0U) mutable events configured #define QP_IMPL // this is QP implementation #include "qp_port.hpp" // QP port #include "qp_pkg.hpp" // QP package-scope interface #include "qsafe.h" // QP Functional Safety (FuSa) Subsystem #ifdef Q_SPY // QS software tracing enabled? #include "qs_port.hpp" // QS port #include "qs_pkg.hpp" // QS facilities for pre-defined trace records #else #include "qs_dummy.hpp" // disable the QS software tracing #endif // Q_SPY // unnamed namespace for local definitions with internal linkage namespace { Q_DEFINE_THIS_MODULE("qf_mem") } // unnamed namespace $define ${QF::QMPool} #define QP_IMPL // this is QP implementation #include "qp_port.hpp" // QP port #include "qp_pkg.hpp" // QP package-scope interface #include "qsafe.h" // QP Functional Safety (FuSa) Subsystem #ifdef Q_SPY // QS software tracing enabled? #include "qs_port.hpp" // QS port #include "qs_pkg.hpp" // QS facilities for pre-defined trace records #else #include "qs_dummy.hpp" // disable the QS software tracing #endif // Q_SPY // unnamed namespace for local definitions with internal linkage namespace { Q_DEFINE_THIS_MODULE("qf_qact") } // unnamed namespace $define ${QF::QActive::QActive} $define ${QF::QActive::register_} $define ${QF::QActive::unregister_} #define QP_IMPL // this is QP implementation #include "qp_port.hpp" // QP port #include "qp_pkg.hpp" // QP package-scope interface #include "qsafe.h" // QP Functional Safety (FuSa) Subsystem #ifdef Q_SPY // QS software tracing enabled? #include "qs_port.hpp" // QS port #include "qs_pkg.hpp" // QS facilities for pre-defined trace records #else #include "qs_dummy.hpp" // disable the QS software tracing #endif // Q_SPY // unnamed namespace for local definitions with internal linkage namespace { //Q_DEFINE_THIS_MODULE("qf_qmact") } // unnamed namespace $define ${QF::QMActive} #define QP_IMPL // this is QP implementation #include "qp_port.hpp" // QP port #include "qp_pkg.hpp" // QP package-scope interface #include "qsafe.h" // QP Functional Safety (FuSa) Subsystem #ifdef Q_SPY // QS software tracing enabled? #include "qs_port.hpp" // QS port #include "qs_pkg.hpp" // QS facilities for pre-defined trace records #else #include "qs_dummy.hpp" // disable the QS software tracing #endif // Q_SPY // unnamed namespace for local definitions with internal linkage namespace { Q_DEFINE_THIS_MODULE("qf_qeq") } // unnamed namespace $define ${QF::QEQueue} #define QP_IMPL // this is QP implementation #include "qp_port.hpp" // QP port #include "qp_pkg.hpp" // QP package-scope interface #include "qsafe.h" // QP Functional Safety (FuSa) Subsystem #ifdef Q_SPY // QS software tracing enabled? #include "qs_port.hpp" // QS port #include "qs_pkg.hpp" // QS facilities for pre-defined trace records #else #include "qs_dummy.hpp" // disable the QS software tracing #endif // Q_SPY // unnamed namespace for local definitions with internal linkage namespace { Q_DEFINE_THIS_MODULE("qf_ps") } // unnamed namespace $define ${QF::QActive::subscrList_} $define ${QF::QActive::maxPubSignal_} $define ${QF::QActive::psInit} $define ${QF::QActive::publish_} $define ${QF::QActive::subscribe} $define ${QF::QActive::unsubscribe} $define ${QF::QActive::unsubscribeAll} #define QP_IMPL // this is QP implementation #include "qp_port.hpp" // QP port #include "qp_pkg.hpp" // QP package-scope interface #include "qsafe.h" // QP Functional Safety (FuSa) Subsystem #ifdef Q_SPY // QS software tracing enabled? #include "qs_port.hpp" // QS port #include "qs_pkg.hpp" // QS facilities for pre-defined trace records #else #include "qs_dummy.hpp" // disable the QS software tracing #endif // Q_SPY // unnamed namespace for local definitions with internal linkage namespace { Q_DEFINE_THIS_MODULE("qf_time") } // unnamed namespace $define ${QF::QTimeEvt} #define QP_IMPL // this is QP implementation #include "qp_port.hpp" // QP port #include "qp_pkg.hpp" // QP package-scope interface #include "qsafe.h" // QP Functional Safety (FuSa) Subsystem #ifdef Q_SPY // QS software tracing enabled? #include "qs_port.hpp" // QS port #include "qs_pkg.hpp" // QS facilities for pre-defined trace records #else #include "qs_dummy.hpp" // disable the QS software tracing #endif // Q_SPY // protection against including this source file in a wrong project #ifndef QV_HPP_ #error "Source file included in a project NOT based on the QV kernel" #endif // QV_HPP_ // unnamed namespace for local definitions with internal linkage namespace { Q_DEFINE_THIS_MODULE("qv") } // unnamed namespace $define ${QV::QV-base} $define ${QV::QF-cust} $define ${QV::QActive} #define QP_IMPL // this is QP implementation #include "qp_port.hpp" // QP port #include "qp_pkg.hpp" // QP package-scope interface #include "qsafe.h" // QP Functional Safety (FuSa) Subsystem #ifdef Q_SPY // QS software tracing enabled? #include "qs_port.hpp" // QS port #include "qs_pkg.hpp" // QS facilities for pre-defined trace records #else #include "qs_dummy.hpp" // disable the QS software tracing #endif // Q_SPY // protection against including this source file in a wrong project #ifndef QK_HPP_ #error "Source file included in a project NOT based on the QK kernel" #endif // QK_HPP_ // unnamed namespace for local definitions with internal linkage namespace { Q_DEFINE_THIS_MODULE("qk") } // unnamed namespace $define ${QK::QK-base} extern "C" { $define ${QK-extern-C} } // extern "C" $define ${QK::QF-cust} $define ${QK::QActive} #define QP_IMPL // this is QP implementation #include "qp_port.hpp" // QP port #include "qp_pkg.hpp" // QP package-scope interface #include "qsafe.h" // QP Functional Safety (FuSa) Subsystem #ifdef Q_SPY // QS software tracing enabled? #include "qs_port.hpp" // QS port #include "qs_pkg.hpp" // QS facilities for pre-defined trace records #else #include "qs_dummy.hpp" // disable the QS software tracing #endif // Q_SPY // protection against including this source file in a wrong project #ifndef QXK_HPP_ #error "Source file included in a project NOT based on the QXK kernel" #endif // QXK_HPP_ // unnamed namespace for local definitions with internal linkage namespace { Q_DEFINE_THIS_MODULE("qxk") } // unnamed namespace $define ${QXK::QXK-base} extern "C" { $define ${QXK-extern-C} } // extern "C" $define ${QXK::QF-cust} $define ${QXK::QActive} #define QP_IMPL // this is QP implementation #include "qp_port.hpp" // QP port #include "qp_pkg.hpp" // QP package-scope interface #include "qsafe.h" // QP Functional Safety (FuSa) Subsystem #ifdef Q_SPY // QS software tracing enabled? #include "qs_port.hpp" // QS port #include "qs_pkg.hpp" // QS facilities for pre-defined trace records #else #include "qs_dummy.hpp" // disable the QS software tracing #endif // Q_SPY // protection against including this source file in a wrong project #ifndef QXK_HPP_ #error "Source file included in a project NOT based on the QXK kernel" #endif // QXK_HPP_ // unnamed namespace for local definitions with internal linkage namespace { Q_DEFINE_THIS_MODULE("qxk_mutex") } // unnamed namespace $define ${QXK::QXMutex} #define QP_IMPL // this is QP implementation #include "qp_port.hpp" // QP port #include "qp_pkg.hpp" // QP package-scope interface #include "qsafe.h" // QP Functional Safety (FuSa) Subsystem #ifdef Q_SPY // QS software tracing enabled? #include "qs_port.hpp" // QS port #include "qs_pkg.hpp" // QS facilities for pre-defined trace records #else #include "qs_dummy.hpp" // disable the QS software tracing #endif // Q_SPY // protection against including this source file in a wrong project #ifndef QXK_HPP_ #error "Source file included in a project NOT based on the QXK kernel" #endif // QXK_HPP_ // unnamed namespace for local definitions with internal linkage namespace { Q_DEFINE_THIS_MODULE("qxk_sema") } // unnamed namespace $define ${QXK::QXSemaphore} #define QP_IMPL // this is QP implementation #include "qp_port.hpp" // QP port #include "qp_pkg.hpp" // QP package-scope interface #include "qsafe.h" // QP Functional Safety (FuSa) Subsystem #ifdef Q_SPY // QS software tracing enabled? #include "qs_port.hpp" // QS port #include "qs_pkg.hpp" // QS facilities for pre-defined trace records #else #include "qs_dummy.hpp" // disable the QS software tracing #endif // Q_SPY // protection against including this source file in a wrong project #ifndef QXK_HPP_ #error "Source file included in a project NOT based on the QXK kernel" #endif // QXK_HPP_ // unnamed namespace for local definitions with internal linkage namespace { Q_DEFINE_THIS_MODULE("qxk_xthr") } // unnamed namespace $define ${QXK::QXThread} #define QP_IMPL // this is QP implementation #include "qs_port.hpp" // QS port #include "qs_pkg.hpp" // QS package-scope internal interface #include "qstamp.hpp" // QP time-stamp #include "qsafe.h" // QP Functional Safety (FuSa) Subsystem // unnamed namespace for local definitions with internal linkage namespace { Q_DEFINE_THIS_MODULE("qs") } // unnamed namespace $define ${QS::QS-TX} #ifndef QF_MEM_ISOLATE $define ${QS::filters} #endif //============================================================================ //! @cond INTERNAL namespace QP { namespace QS { //............................................................................ Attr priv_; //............................................................................ void glbFilter_(std::int_fast16_t const filter) noexcept { bool const isRemove = (filter < 0); std::uint16_t const rec = isRemove ? static_cast<std::uint16_t>(-filter) : static_cast<std::uint16_t>(filter); switch (rec) { case QS_ALL_RECORDS: { std::uint8_t const tmp = (isRemove ? 0x00U : 0xFFU); std::uint_fast8_t i; // set all global filters (partially unrolled loop) for (i = 0U; i < Q_DIM(filt_.glb); i += 4U) { filt_.glb[i ] = tmp; filt_.glb[i + 1U] = tmp; filt_.glb[i + 2U] = tmp; filt_.glb[i + 3U] = tmp; } if (isRemove) { // leave the "not maskable" filters enabled, // see qs.h, Miscellaneous QS records (not maskable) // filt_.glb[0] = 0x01U; filt_.glb[6] = 0x40U; filt_.glb[7] = 0xFCU; filt_.glb[8] = 0x7FU; } else { // never turn the last 3 records on (0x7D, 0x7E, 0x7F) filt_.glb[15] = 0x1FU; } break; } case QS_SM_RECORDS: if (isRemove) { filt_.glb[0] &= static_cast<std::uint8_t>(~0xFEU & 0xFFU); filt_.glb[1] &= static_cast<std::uint8_t>(~0x03U & 0xFFU); filt_.glb[6] &= static_cast<std::uint8_t>(~0x80U & 0xFFU); filt_.glb[7] &= static_cast<std::uint8_t>(~0x03U & 0xFFU); } else { filt_.glb[0] |= 0xFEU; filt_.glb[1] |= 0x03U; filt_.glb[6] |= 0x80U; filt_.glb[7] |= 0x03U; } break; case QS_AO_RECORDS: if (isRemove) { filt_.glb[1] &= static_cast<std::uint8_t>(~0xFCU & 0xFFU); filt_.glb[2] &= static_cast<std::uint8_t>(~0x07U & 0xFFU); filt_.glb[5] &= static_cast<std::uint8_t>(~0x20U & 0xFFU); } else { filt_.glb[1] |= 0xFCU; filt_.glb[2] |= 0x07U; filt_.glb[5] |= 0x20U; } break; case QS_EQ_RECORDS: if (isRemove) { filt_.glb[2] &= static_cast<std::uint8_t>(~0x78U & 0xFFU); filt_.glb[5] &= static_cast<std::uint8_t>(~0x40U & 0xFFU); } else { filt_.glb[2] |= 0x78U; filt_.glb[5] |= 0x40U; } break; case QS_MP_RECORDS: if (isRemove) { filt_.glb[3] &= static_cast<std::uint8_t>(~0x03U & 0xFFU); filt_.glb[5] &= static_cast<std::uint8_t>(~0x80U & 0xFFU); } else { filt_.glb[3] |= 0x03U; filt_.glb[5] |= 0x80U; } break; case QS_QF_RECORDS: if (isRemove) { filt_.glb[2] &= static_cast<std::uint8_t>(~0x80U & 0xFFU); filt_.glb[3] &= static_cast<std::uint8_t>(~0xFCU & 0xFFU); filt_.glb[4] &= static_cast<std::uint8_t>(~0xC0U & 0xFFU); filt_.glb[5] &= static_cast<std::uint8_t>(~0x1FU & 0xFFU); } else { filt_.glb[2] |= 0x80U; filt_.glb[3] |= 0xFCU; filt_.glb[4] |= 0xC0U; filt_.glb[5] |= 0x1FU; } break; case QS_TE_RECORDS: if (isRemove) { filt_.glb[4] &= static_cast<std::uint8_t>(~0x3FU & 0xFFU); } else { filt_.glb[4] |= 0x3FU; } break; case QS_SC_RECORDS: if (isRemove) { filt_.glb[6] &= static_cast<std::uint8_t>(~0x3FU & 0xFFU); } else { filt_.glb[6] |= 0x3FU; } break; case QS_SEM_RECORDS: if (isRemove) { filt_.glb[8] &= static_cast<std::uint8_t>(~0x80U & 0xFFU); filt_.glb[9] &= static_cast<std::uint8_t>(~0x07U & 0xFFU); } else { filt_.glb[8] |= 0x80U; filt_.glb[9] |= 0x07U; } break; case QS_MTX_RECORDS: if (isRemove) { filt_.glb[9] &= static_cast<std::uint8_t>(~0xF8U & 0xFFU); filt_.glb[10] &= static_cast<std::uint8_t>(~0x01U & 0xFFU); } else { filt_.glb[9] |= 0xF8U; filt_.glb[10] |= 0x01U; } break; case QS_U0_RECORDS: if (isRemove) { filt_.glb[12] &= static_cast<std::uint8_t>(~0xF0U & 0xFFU); filt_.glb[13] &= static_cast<std::uint8_t>(~0x01U & 0xFFU); } else { filt_.glb[12] |= 0xF0U; filt_.glb[13] |= 0x01U; } break; case QS_U1_RECORDS: if (isRemove) { filt_.glb[13] &= static_cast<std::uint8_t>(~0x3EU & 0xFFU); } else { filt_.glb[13] |= 0x3EU; } break; case QS_U2_RECORDS: if (isRemove) { filt_.glb[13] &= static_cast<std::uint8_t>(~0xC0U & 0xFFU); filt_.glb[14] &= static_cast<std::uint8_t>(~0x07U & 0xFFU); } else { filt_.glb[13] |= 0xC0U; filt_.glb[14] |= 0x07U; } break; case QS_U3_RECORDS: if (isRemove) { filt_.glb[14] &= static_cast<std::uint8_t>(~0xF8U & 0xFFU); } else { filt_.glb[14] |= 0xF8U; } break; case QS_U4_RECORDS: if (isRemove) { filt_.glb[15] &= static_cast<std::uint8_t>(~0x1FU & 0xFFU); } else { filt_.glb[15] |= 0x1FU; } break; case QS_UA_RECORDS: if (isRemove) { filt_.glb[12] &= static_cast<std::uint8_t>(~0xF0U & 0xFFU); filt_.glb[13] = 0U; filt_.glb[14] = 0U; filt_.glb[15] &= static_cast<std::uint8_t>(~0x1FU & 0xFFU); } else { filt_.glb[12] |= 0xF0U; filt_.glb[13] |= 0xFFU; filt_.glb[14] |= 0xFFU; filt_.glb[15] |= 0x1FU; } break; default: { QS_CRIT_STAT QS_CRIT_ENTRY(); // QS rec number must be below 0x7D, so no need for escaping Q_ASSERT_INCRIT(210, rec < 0x7DU); QS_CRIT_EXIT(); if (isRemove) { filt_.glb[rec >> 3U] &= static_cast<std::uint8_t>(~(1U << (rec & 7U)) & 0xFFU); } else { filt_.glb[rec >> 3U] |= static_cast<std::uint8_t>(1U << (rec & 7U)); // never turn the last 3 records on (0x7D, 0x7E, 0x7F) filt_.glb[15] &= 0x1FU; } break; } } } //............................................................................ void locFilter_(std::int_fast16_t const filter) noexcept { bool const isRemove = (filter < 0); std::uint16_t const qsId = isRemove ? static_cast<std::uint16_t>(-filter) : static_cast<std::uint16_t>(filter); std::uint8_t const tmp = (isRemove ? 0x00U : 0xFFU); std::uint_fast8_t i; switch (qsId) { case QS_ALL_IDS: // set all local filters (partially unrolled loop) for (i = 0U; i < Q_DIM(filt_.loc); i += 4U) { filt_.loc[i ] = tmp; filt_.loc[i + 1U] = tmp; filt_.loc[i + 2U] = tmp; filt_.loc[i + 3U] = tmp; } break; case QS_AO_IDS: for (i = 0U; i < 8U; i += 4U) { filt_.loc[i ] = tmp; filt_.loc[i + 1U] = tmp; filt_.loc[i + 2U] = tmp; filt_.loc[i + 3U] = tmp; } break; case QS_EP_IDS: i = 8U; filt_.loc[i ] = tmp; filt_.loc[i + 1U] = tmp; break; case QS_AP_IDS: i = 12U; filt_.loc[i ] = tmp; filt_.loc[i + 1U] = tmp; filt_.loc[i + 2U] = tmp; filt_.loc[i + 3U] = tmp; break; default: { QS_CRIT_STAT QS_CRIT_ENTRY(); // qsId must be in range Q_ASSERT_INCRIT(310, qsId < 0x7FU); QS_CRIT_EXIT(); if (isRemove) { filt_.loc[qsId >> 3U] &= static_cast<std::uint8_t>( ~(1U << (qsId & 7U)) & 0xFFU); } else { filt_.loc[qsId >> 3U] |= (1U << (qsId & 7U)); } break; } } filt_.loc[0] |= 0x01U; // leave QS_ID == 0 always on } //............................................................................ void beginRec_(std::uint_fast8_t const rec) noexcept { std::uint8_t const b = priv_.seq + 1U; std::uint8_t chksum = 0U; // reset the checksum std::uint8_t * const buf = priv_.buf; // put in a temporary (register) QSCtr head = priv_.head; // put in a temporary (register) QSCtr const end = priv_.end; // put in a temporary (register) priv_.seq = b; // store the incremented sequence num priv_.used = (priv_.used + 2U); // 2 bytes about to be added QS_INSERT_ESC_BYTE_(b) chksum += static_cast<std::uint8_t>(rec); QS_INSERT_BYTE_(static_cast<std::uint8_t>(rec)) // no need for escaping priv_.head = head; // save the head priv_.chksum = chksum; // save the checksum } //............................................................................ void endRec_() noexcept { std::uint8_t * const buf = priv_.buf; // put in a temporary (register) QSCtr head = priv_.head; QSCtr const end = priv_.end; std::uint8_t b = priv_.chksum; b ^= 0xFFU; // invert the bits in the checksum priv_.used = (priv_.used + 2U); // 2 bytes about to be added if ((b != QS_FRAME) && (b != QS_ESC)) { QS_INSERT_BYTE_(b) } else { QS_INSERT_BYTE_(QS_ESC) QS_INSERT_BYTE_(b ^ QS_ESC_XOR) priv_.used = (priv_.used + 1U); // account for the ESC byte } QS_INSERT_BYTE_(QS_FRAME) // do not escape this QS_FRAME priv_.head = head; // save the head if (priv_.used > end) { // overrun over the old data? priv_.used = end; // the whole buffer is used priv_.tail = head; // shift the tail to the old data } } //............................................................................ void u8_raw_(std::uint8_t const d) noexcept { std::uint8_t chksum = priv_.chksum; // put in a temporary (register) std::uint8_t * const buf = priv_.buf; // put in a temporary (register) QSCtr head = priv_.head; // put in a temporary (register) QSCtr const end = priv_.end; // put in a temporary (register) priv_.used = (priv_.used + 1U); // 1 byte about to be added QS_INSERT_ESC_BYTE_(d) priv_.head = head; // save the head priv_.chksum = chksum; // save the checksum } //............................................................................ void u8u8_raw_( std::uint8_t const d1, std::uint8_t const d2) noexcept { std::uint8_t chksum = priv_.chksum; // put in a temporary (register) std::uint8_t * const buf = priv_.buf; // put in a temporary (register) QSCtr head = priv_.head; // put in a temporary (register) QSCtr const end = priv_.end; // put in a temporary (register) priv_.used = (priv_.used + 2U); // 2 bytes about to be added QS_INSERT_ESC_BYTE_(d1) QS_INSERT_ESC_BYTE_(d2) priv_.head = head; // save the head priv_.chksum = chksum; // save the checksum } //............................................................................ void u16_raw_(std::uint16_t d) noexcept { std::uint8_t b = static_cast<std::uint8_t>(d); std::uint8_t chksum = priv_.chksum; // put in a temporary (register) std::uint8_t * const buf = priv_.buf; // put in a temporary (register) QSCtr head = priv_.head; // put in a temporary (register) QSCtr const end = priv_.end; // put in a temporary (register) priv_.used = (priv_.used + 2U); // 2 bytes about to be added QS_INSERT_ESC_BYTE_(b) d >>= 8U; b = static_cast<std::uint8_t>(d); QS_INSERT_ESC_BYTE_(b) priv_.head = head; // save the head priv_.chksum = chksum; // save the checksum } //............................................................................ void u32_raw_(std::uint32_t d) noexcept { std::uint8_t chksum = priv_.chksum; // put in a temporary (register) std::uint8_t * const buf = priv_.buf; // put in a temporary (register) QSCtr head = priv_.head; // put in a temporary (register) QSCtr const end = priv_.end; // put in a temporary (register) priv_.used = (priv_.used + 4U); // 4 bytes about to be added for (std::uint_fast8_t i = 4U; i != 0U; --i) { std::uint8_t const b = static_cast<std::uint8_t>(d); QS_INSERT_ESC_BYTE_(b) d >>= 8U; } priv_.head = head; // save the head priv_.chksum = chksum; // save the checksum } //............................................................................ void obj_raw_(void const * const obj) noexcept { #if (QS_OBJ_PTR_SIZE == 1U) u8_raw_(reinterpret_cast<std::uint8_t>(obj)); #elif (QS_OBJ_PTR_SIZE == 2U) u16_raw_(reinterpret_cast<std::uint16_t>(obj)); #elif (QS_OBJ_PTR_SIZE == 4U) u32_raw_(reinterpret_cast<std::uint32_t>(obj)); #elif (QS_OBJ_PTR_SIZE == 8U) u64_raw_(reinterpret_cast<std::uint64_t>(obj)); #else u32_raw_(reinterpret_cast<std::uint32_t>(obj)); #endif } //............................................................................ void str_raw_(char const * s) noexcept { std::uint8_t b = static_cast<std::uint8_t>(*s); std::uint8_t chksum = priv_.chksum; // put in a temporary (register) std::uint8_t * const buf = priv_.buf; // put in a temporary (register) QSCtr head = priv_.head; // put in a temporary (register) QSCtr const end = priv_.end; // put in a temporary (register) QSCtr used = priv_.used; // put in a temporary (register) while (b != 0U) { chksum += b; // update checksum QS_INSERT_BYTE_(b) // ASCII characters don't need escaping ++s; b = static_cast<std::uint8_t>(*s); ++used; } QS_INSERT_BYTE_(0U) // zero-terminate the string ++used; priv_.head = head; // save the head priv_.chksum = chksum; // save the checksum priv_.used = used; // save # of used buffer space } //............................................................................ void u8_fmt_( std::uint8_t const format, std::uint8_t const d) noexcept { std::uint8_t chksum = priv_.chksum; // put in a temporary (register) std::uint8_t *const buf = priv_.buf; // put in a temporary (register) QSCtr head = priv_.head; // put in a temporary (register) QSCtr const end = priv_.end; // put in a temporary (register) priv_.used = (priv_.used + 2U); // 2 bytes about to be added QS_INSERT_ESC_BYTE_(format) QS_INSERT_ESC_BYTE_(d) priv_.head = head; // save the head priv_.chksum = chksum; // save the checksum } //............................................................................ void u16_fmt_( std::uint8_t format, std::uint16_t d) noexcept { std::uint8_t chksum = priv_.chksum; // put in a temporary (register) std::uint8_t * const buf = priv_.buf; // put in a temporary (register) QSCtr head = priv_.head; // put in a temporary (register) QSCtr const end = priv_.end; // put in a temporary (register) priv_.used = (priv_.used + 3U); // 3 bytes about to be added QS_INSERT_ESC_BYTE_(format) format = static_cast<std::uint8_t>(d); QS_INSERT_ESC_BYTE_(format) d >>= 8U; format = static_cast<std::uint8_t>(d); QS_INSERT_ESC_BYTE_(format) priv_.head = head; // save the head priv_.chksum = chksum; // save the checksum } //............................................................................ void u32_fmt_( std::uint8_t format, std::uint32_t d) noexcept { std::uint8_t chksum = priv_.chksum; // put in a temporary (register) std::uint8_t * const buf = priv_.buf; // put in a temporary (register) QSCtr head = priv_.head; // put in a temporary (register) QSCtr const end = priv_.end; // put in a temporary (register) priv_.used = (priv_.used + 5U); // 5 bytes about to be added QS_INSERT_ESC_BYTE_(format) // insert the format byte for (std::uint_fast8_t i = 4U; i != 0U; --i) { format = static_cast<std::uint8_t>(d); QS_INSERT_ESC_BYTE_(format) d >>= 8U; } priv_.head = head; // save the head priv_.chksum = chksum; // save the checksum } //............................................................................ void str_fmt_(char const * s) noexcept { std::uint8_t b = static_cast<std::uint8_t>(*s); std::uint8_t chksum = static_cast<std::uint8_t>( priv_.chksum + static_cast<std::uint8_t>(STR_T)); std::uint8_t * const buf = priv_.buf; // put in a temporary (register) QSCtr head = priv_.head; // put in a temporary (register) QSCtr const end = priv_.end; // put in a temporary (register) QSCtr used = priv_.used; // put in a temporary (register) used += 2U; // the format byte and the terminating-0 QS_INSERT_BYTE_(static_cast<std::uint8_t>(STR_T)) while (b != 0U) { // ASCII characters don't need escaping chksum += b; // update checksum QS_INSERT_BYTE_(b) ++s; b = static_cast<std::uint8_t>(*s); ++used; } QS_INSERT_BYTE_(0U) // zero-terminate the string priv_.head = head; // save the head priv_.chksum = chksum; // save the checksum priv_.used = used; // save # of used buffer space } //............................................................................ void mem_fmt_( std::uint8_t const * blk, std::uint8_t size) noexcept { std::uint8_t b = static_cast<std::uint8_t>(MEM_T); std::uint8_t chksum = priv_.chksum + b; std::uint8_t * const buf = priv_.buf; // put in a temporary (register) QSCtr head = priv_.head; // put in a temporary (register) QSCtr const end = priv_.end; // put in a temporary (register) priv_.used = (priv_.used + size + 2U); // size+2 bytes to be added QS_INSERT_BYTE_(b) QS_INSERT_ESC_BYTE_(size) // output the 'size' number of bytes for (; size != 0U; --size) { b = *blk; QS_INSERT_ESC_BYTE_(b) ++blk; } priv_.head = head; // save the head priv_.chksum = chksum; // save the checksum } //............................................................................ void sig_dict_pre_( QSignal const sig, void const * const obj, char const * const name) noexcept { QS_CRIT_STAT QS_CRIT_ENTRY(); QS_MEM_SYS(); beginRec_(static_cast<std::uint_fast8_t>(QS_SIG_DICT)); QS_SIG_PRE_(sig); QS_OBJ_PRE_(obj); QS_STR_PRE_((*name == '&') ? &name[1] : name); endRec_(); QS_MEM_APP(); QS_CRIT_EXIT(); onFlush(); } //............................................................................ void obj_dict_pre_( void const * const obj, char const * const name) noexcept { QS_CRIT_STAT QS_CRIT_ENTRY(); QS_MEM_SYS(); beginRec_(static_cast<std::uint_fast8_t>(QS_OBJ_DICT)); QS_OBJ_PRE_(obj); QS_STR_PRE_((*name == '&') ? &name[1] : name); endRec_(); QS_MEM_APP(); QS_CRIT_EXIT(); onFlush(); } //............................................................................ void obj_arr_dict_pre_( void const * const obj, std::uint_fast16_t const idx, char const * const name) noexcept { QS_CRIT_STAT QS_CRIT_ENTRY(); Q_REQUIRE_INCRIT(400, idx < 1000U); QS_CRIT_EXIT(); // format idx into a char buffer as "xxx\0" std::uint8_t idx_str[4]; std::uint_fast16_t tmp = idx; std::uint8_t i; idx_str[3] = 0U; // zero-terminate idx_str[2] = static_cast<std::uint8_t>( static_cast<std::uint8_t>('0') + (tmp % 10U)); tmp /= 10U; idx_str[1] = static_cast<std::uint8_t>( static_cast<std::uint8_t>('0') + (tmp % 10U)); if (idx_str[1] == static_cast<std::uint8_t>('0')) { i = 2U; } else { tmp /= 10U; idx_str[0] = static_cast<std::uint8_t>( static_cast<std::uint8_t>('0') + (tmp % 10U)); if (idx_str[0] == static_cast<std::uint8_t>('0')) { i = 1U; } else { i = 0U; } } std::uint8_t j = ((*name == '&') ? 1U : 0U); QS_CRIT_ENTRY(); QS_MEM_SYS(); beginRec_(static_cast<std::uint_fast8_t>(QS_OBJ_DICT)); QS_OBJ_PRE_(obj); for (; name[j] != '\0'; ++j) { QS_U8_PRE_(name[j]); if (name[j] == '[') { ++j; break; } } for (; idx_str[i] != 0U; ++i) { QS_U8_PRE_(idx_str[i]); } // skip chars until ']' for (; name[j] != '\0'; ++j) { if (name[j] == ']') { break; } } for (; name[j] != '\0'; ++j) { QS_U8_PRE_(name[j]); } QS_U8_PRE_(0U); // zero-terminate endRec_(); QS_MEM_APP(); QS_CRIT_EXIT(); onFlush(); } //............................................................................ void fun_dict_pre_( QSpyFunPtr fun, char const * const name) noexcept { QS_CRIT_STAT QS_CRIT_ENTRY(); QS_MEM_SYS(); beginRec_(static_cast<std::uint_fast8_t>(QS_FUN_DICT)); QS_FUN_PRE_(fun); QS_STR_PRE_((*name == '&') ? &name[1] : name); endRec_(); QS_MEM_APP(); QS_CRIT_EXIT(); onFlush(); } //............................................................................ void usr_dict_pre_( enum_t const rec, char const * const name) noexcept { QS_CRIT_STAT QS_CRIT_ENTRY(); QS_MEM_SYS(); beginRec_(static_cast<std::uint_fast8_t>(QS_USR_DICT)); QS_U8_PRE_(rec); QS_STR_PRE_(name); endRec_(); QS_MEM_APP(); QS_CRIT_EXIT(); onFlush(); } //............................................................................ void enum_dict_pre_( enum_t const value, std::uint8_t const group, char const * const name) noexcept { QS_CRIT_STAT QS_CRIT_ENTRY(); QS_MEM_SYS(); beginRec_(static_cast<std::uint_fast8_t>(QS_ENUM_DICT)); QS_2U8_PRE_(static_cast<std::uint8_t>(value), group); QS_STR_PRE_(name); endRec_(); QS_MEM_APP(); QS_CRIT_EXIT(); onFlush(); } //............................................................................ void assertion_pre_( char const * const module, int_t const id, std::uint32_t const delay) noexcept { // NOTE: called in a critical section beginRec_(static_cast<std::uint_fast8_t>(QS_ASSERT_FAIL)); QS_TIME_PRE_(); QS_U16_PRE_(id); QS_STR_PRE_((module != nullptr) ? module : "?"); endRec_(); onFlush(); // busy-wait until all QS data makes it over to the host for (std::uint32_t volatile ctr = delay; ctr > 0U; ) { ctr = (ctr - 1U); } QS::onCleanup(); } //............................................................................ void crit_entry_pre_() noexcept { beginRec_(static_cast<std::uint_fast8_t>(QS_QF_CRIT_ENTRY)); QS_TIME_PRE_(); priv_.critNest = (priv_.critNest + 1U); QS_U8_PRE_(priv_.critNest); endRec_(); } //............................................................................ void crit_exit_pre_() noexcept { beginRec_(static_cast<std::uint_fast8_t>(QS_QF_CRIT_EXIT)); QS_TIME_PRE_(); QS_U8_PRE_(QS::priv_.critNest); priv_.critNest = (priv_.critNest - 1U); endRec_(); } //............................................................................ void isr_entry_pre_( std::uint8_t const isrnest, std::uint8_t const prio) noexcept { beginRec_(static_cast<std::uint_fast8_t>(QS_QF_ISR_ENTRY)); QS_TIME_PRE_(); QS_U8_PRE_(isrnest); QS_U8_PRE_(prio); endRec_(); } //............................................................................ void isr_exit_pre_( std::uint8_t const isrnest, std::uint8_t const prio) noexcept { beginRec_(static_cast<std::uint_fast8_t>(QS_QF_ISR_EXIT)); QS_TIME_PRE_(); QS_U8_PRE_(isrnest); QS_U8_PRE_(prio); endRec_(); } //............................................................................ void target_info_pre_(std::uint8_t const isReset) { // NOTE: called in a critical section static constexpr std::uint8_t ZERO = static_cast<std::uint8_t>('0'); static std::uint8_t const * const TIME = reinterpret_cast<std::uint8_t const *>(&BUILD_TIME[0]); static std::uint8_t const * const DATE = reinterpret_cast<std::uint8_t const *>(&BUILD_DATE[0]); beginRec_(static_cast<std::uint_fast8_t>(QS_TARGET_INFO)); u8_raw_(isReset); static union { std::uint16_t u16; std::uint8_t u8[2]; } endian_test; endian_test.u16 = 0x0102U; // big endian ? add the 0x8000U flag QS_U16_PRE_(((endian_test.u8[0] == 0x01U) ? (0x8000U | QP_VERSION) : QP_VERSION)); // target endianness + version number // send the object sizes... u8_raw_(Q_SIGNAL_SIZE | static_cast<std::uint8_t>(QF_EVENT_SIZ_SIZE << 4U)); #ifdef QF_EQUEUE_CTR_SIZE u8_raw_(QF_EQUEUE_CTR_SIZE | static_cast<std::uint8_t>(QF_TIMEEVT_CTR_SIZE << 4U)); #else QS::u8_raw_(static_cast<std::uint8_t>(QF_TIMEEVT_CTR_SIZE << 4U)); #endif // ifdef QF_EQUEUE_CTR_SIZE #ifdef QF_MPOOL_CTR_SIZE QS::u8_raw_(QF_MPOOL_SIZ_SIZE | static_cast<std::uint8_t>(QF_MPOOL_CTR_SIZE << 4U)); #else QS::u8_raw_(0U); #endif // ifdef QF_MPOOL_CTR_SIZE QS::u8_raw_(QS_OBJ_PTR_SIZE | (QS_FUN_PTR_SIZE << 4U)); QS::u8_raw_(QS_TIME_SIZE); // send the limits... QS::u8_raw_(QF_MAX_ACTIVE); QS::u8_raw_(QF_MAX_EPOOL | (QF_MAX_TICK_RATE << 4U)); // send the build time in three bytes (sec, min, hour)... QS::u8_raw_((10U * (TIME[6] - ZERO)) + (TIME[7] - ZERO)); QS::u8_raw_((10U * (TIME[3] - ZERO)) + (TIME[4] - ZERO)); if (BUILD_TIME[0] == static_cast<std::uint8_t>(' ')) { QS::u8_raw_(TIME[1] - ZERO); } else { QS::u8_raw_((10U * (TIME[0] - ZERO)) + (TIME[1] - ZERO)); } // send the build date in three bytes (day, month, year) ... if (BUILD_DATE[4] == static_cast<std::uint8_t>(' ')) { QS::u8_raw_(DATE[5] - ZERO); } else { QS::u8_raw_((10U * (DATE[4] - ZERO)) + (DATE[5] - ZERO)); } // convert the 3-letter month to a number 1-12 ... std::uint8_t b; switch (DATE[0] + DATE[1] + DATE[2]) { case 'J' + 'a' +'n': b = 1U; break; case 'F' + 'e' + 'b': b = 2U; break; case 'M' + 'a' +'r': b = 3U; break; case 'A' + 'p' + 'r': b = 4U; break; case 'M' + 'a' + 'y': b = 5U; break; case 'J' + 'u' + 'n': b = 6U; break; case 'J' + 'u' + 'l': b = 7U; break; case 'A' + 'u' + 'g': b = 8U; break; case 'S' + 'e' + 'p': b = 9U; break; case 'O' + 'c' + 't': b = 10U; break; case 'N' + 'o' + 'v': b = 11U; break; case 'D' + 'e' + 'c': b = 12U; break; default: b = 0U; break; } QS::u8_raw_(b); // store the month QS::u8_raw_((10U * (DATE[9] - ZERO)) + (DATE[10] - ZERO)); QS::endRec_(); } } // namespace QS } // namespace QP //! @endcond #define QP_IMPL // this is QF/QK implementation #include "qs_port.hpp" // QS port #include "qs_pkg.hpp" // QS package-scope internal interface //============================================================================ //! @cond INTERNAL namespace QP { namespace QS { //${QS::QS-tx-64bit::u64_raw_} ............................................... void u64_raw_(std::uint64_t d) noexcept { std::uint8_t chksum = priv_.chksum; std::uint8_t * const buf = priv_.buf; QSCtr head = priv_.head; QSCtr const end = priv_.end; priv_.used = (priv_.used + 8U); // 8 bytes are about to be added for (std::int_fast8_t i = 8U; i != 0U; --i) { std::uint8_t const b = static_cast<std::uint8_t>(d); QS_INSERT_ESC_BYTE_(b) d >>= 8U; } priv_.head = head; // save the head priv_.chksum = chksum; // save the checksum } //${QS::QS-tx-64bit::u64_fmt_} ............................................... void u64_fmt_( std::uint8_t format, std::uint64_t d) noexcept { std::uint8_t chksum = priv_.chksum; std::uint8_t * const buf = priv_.buf; QSCtr head = priv_.head; QSCtr const end = priv_.end; priv_.used = (priv_.used + 9U); // 9 bytes are about to be added QS_INSERT_ESC_BYTE_(format) // insert the format byte for (std::int_fast8_t i = 8U; i != 0U; --i) { format = static_cast<std::uint8_t>(d); QS_INSERT_ESC_BYTE_(format) d >>= 8U; } priv_.head = head; // save the head priv_.chksum = chksum; // save the checksum } } // namespace QS } // namespace QP //! @endcond #define QP_IMPL // this is QF/QK implementation #include "qs_port.hpp" // QS port #include "qs_pkg.hpp" // QS package-scope internal interface //============================================================================ //! @cond INTERNAL namespace QP { namespace QS { //${QS::QS-tx-fp::f32_fmt_} .................................................. void f32_fmt_( std::uint8_t format, float32_t f) noexcept { union F32Rep { float32_t f; std::uint32_t u; } fu32; // the internal binary representation std::uint8_t chksum = priv_.chksum; // put in a temporary (register) std::uint8_t * const buf = priv_.buf; // put in a temporary (register) QSCtr head = priv_.head; // put in a temporary (register) QSCtr const end = priv_.end; // put in a temporary (register) fu32.f = f; // assign the binary representation priv_.used = (priv_.used + 5U); // 5 bytes about to be added QS_INSERT_ESC_BYTE_(format) // insert the format byte for (std::uint_fast8_t i = 4U; i != 0U; --i) { format = static_cast<std::uint8_t>(fu32.u); QS_INSERT_ESC_BYTE_(format) fu32.u >>= 8U; } priv_.head = head; // save the head priv_.chksum = chksum; // save the checksum } //${QS::QS-tx-fp::f64_fmt_} .................................................. void f64_fmt_( std::uint8_t format, float64_t d) noexcept { union F64Rep { float64_t d; std::uint32_t u[2]; } fu64; // the internal binary representation std::uint8_t chksum = priv_.chksum; std::uint8_t * const buf = priv_.buf; QSCtr head = priv_.head; QSCtr const end = priv_.end; std::uint32_t i; // static constant untion to detect endianness of the machine static union U32Rep { std::uint32_t u32; std::uint8_t u8; } const endian = { 1U }; fu64.d = d; // assign the binary representation // is this a big-endian machine? if (endian.u8 == 0U) { // swap fu64.u[0] <-> fu64.u[1]... i = fu64.u[0]; fu64.u[0] = fu64.u[1]; fu64.u[1] = i; } priv_.used = (priv_.used + 9U); // 9 bytes about to be added QS_INSERT_ESC_BYTE_(format) // insert the format byte // output 4 bytes from fu64.u[0]... for (i = 4U; i != 0U; --i) { QS_INSERT_ESC_BYTE_(static_cast<std::uint8_t>(fu64.u[0])) fu64.u[0] >>= 8U; } // output 4 bytes from fu64.u[1]... for (i = 4U; i != 0U; --i) { QS_INSERT_ESC_BYTE_(static_cast<std::uint8_t>(fu64.u[1])) fu64.u[1] >>= 8U; } priv_.head = head; // update the head priv_.chksum = chksum; // update the checksum } } // namespace QS } // namespace QP //! @endcond #define QP_IMPL // this is QP implementation #include "qs_port.hpp" // QS port #include "qs_pkg.hpp" // QS package-scope internal interface #include "qp_pkg.hpp" // QP package-scope interface #include "qsafe.h" // QP Functional Safety (FuSa) Subsystem //============================================================================ //! @cond INTERNAL namespace { // unnamed local namespace Q_DEFINE_THIS_MODULE("qs_rx") enum RxStateEnum : std::uint8_t { ERROR_STATE, WAIT4_SEQ, WAIT4_REC, WAIT4_INFO_FRAME, WAIT4_CMD_ID, WAIT4_CMD_PARAM1, WAIT4_CMD_PARAM2, WAIT4_CMD_PARAM3, WAIT4_CMD_FRAME, WAIT4_RESET_FRAME, WAIT4_TICK_RATE, WAIT4_TICK_FRAME, WAIT4_PEEK_OFFS, WAIT4_PEEK_SIZE, WAIT4_PEEK_NUM, WAIT4_PEEK_FRAME, WAIT4_POKE_OFFS, WAIT4_POKE_SIZE, WAIT4_POKE_NUM, WAIT4_POKE_DATA, WAIT4_POKE_FRAME, WAIT4_FILL_DATA, WAIT4_FILL_FRAME, WAIT4_FILTER_LEN, WAIT4_FILTER_DATA, WAIT4_FILTER_FRAME, WAIT4_OBJ_KIND, WAIT4_OBJ_ADDR, WAIT4_OBJ_FRAME, WAIT4_QUERY_KIND, WAIT4_QUERY_FRAME, WAIT4_EVT_PRIO, WAIT4_EVT_SIG, WAIT4_EVT_LEN, WAIT4_EVT_PAR, WAIT4_EVT_FRAME #ifdef Q_UTEST , WAIT4_TEST_SETUP_FRAME, WAIT4_TEST_TEARDOWN_FRAME, WAIT4_TEST_PROBE_DATA, WAIT4_TEST_PROBE_ADDR, WAIT4_TEST_PROBE_FRAME, WAIT4_TEST_CONTINUE_FRAME #endif // Q_UTEST }; // internal helper functions... static void rxParseData_(std::uint8_t const b) noexcept; static void rxHandleGoodFrame_(std::uint8_t const state); static void rxHandleBadFrame_(std::uint8_t const state) noexcept; static void rxReportAck_(enum QP::QSpyRxRecords const recId) noexcept; static void rxReportError_(std::uint8_t const code) noexcept; static void rxReportDone_(enum QP::QSpyRxRecords const recId) noexcept; static void queryCurrObj_(std::uint8_t obj_kind) noexcept; static void rxPoke_() noexcept; // Internal QS-RX function to take a tran. in the QS-RX FSM static inline void tran_(RxStateEnum const target) noexcept { QP::QS::rxPriv_.state = static_cast<std::uint8_t>(target); } } // unnamed namespace #ifndef QF_MEM_ISOLATE namespace QP { namespace QS { RxAttr rxPriv_; } // namespace QS } // namespace QP #endif // QF_MEM_ISOLATE //! @endcond //============================================================================ $define ${QS::QS-RX} //============================================================================ //! @cond INTERNAL namespace { // unnamed local namespace //............................................................................ static void rxParseData_(std::uint8_t const b) noexcept { switch (QP::QS::rxPriv_.state) { case WAIT4_SEQ: { ++QP::QS::rxPriv_.seq; if (QP::QS::rxPriv_.seq != b) { // not the expected sequence? rxReportError_(0x42U); QP::QS::rxPriv_.seq = b; // update the sequence } tran_(WAIT4_REC); break; } case WAIT4_REC: { switch (b) { case QP::QS_RX_INFO: tran_(WAIT4_INFO_FRAME); break; case QP::QS_RX_COMMAND: tran_(WAIT4_CMD_ID); break; case QP::QS_RX_RESET: tran_(WAIT4_RESET_FRAME); break; case QP::QS_RX_TICK: tran_(WAIT4_TICK_RATE); break; case QP::QS_RX_PEEK: if (QP::QS::rxPriv_.currObj[QP::QS::AP_OBJ] != nullptr) { QP::QS::rxPriv_.var.peek.offs = 0U; QP::QS::rxPriv_.var.peek.idx = 0U; tran_(WAIT4_PEEK_OFFS); } else { rxReportError_( static_cast<std::uint8_t>(QP::QS_RX_PEEK)); tran_(ERROR_STATE); } break; case QP::QS_RX_POKE: case QP::QS_RX_FILL: QP::QS::rxPriv_.var.poke.fill = (b == static_cast<std::uint8_t>(QP::QS_RX_FILL)) ? 1U : 0U; if (QP::QS::rxPriv_.currObj[QP::QS::AP_OBJ] != nullptr) { QP::QS::rxPriv_.var.poke.offs = 0U; QP::QS::rxPriv_.var.poke.idx = 0U; tran_(WAIT4_POKE_OFFS); } else { rxReportError_( (QP::QS::rxPriv_.var.poke.fill != 0U) ? static_cast<std::uint8_t>(QP::QS_RX_FILL) : static_cast<std::uint8_t>(QP::QS_RX_POKE)); tran_(ERROR_STATE); } break; case QP::QS_RX_GLB_FILTER: // intentionally fall-through case QP::QS_RX_LOC_FILTER: QP::QS::rxPriv_.var.flt.recId = b; tran_(WAIT4_FILTER_LEN); break; case QP::QS_RX_AO_FILTER: // intentionally fall-through case QP::QS_RX_CURR_OBJ: QP::QS::rxPriv_.var.obj.recId = b; tran_(WAIT4_OBJ_KIND); break; case QP::QS_RX_QUERY_CURR: QP::QS::rxPriv_.var.obj.recId = static_cast<std::uint8_t>(QP::QS_RX_QUERY_CURR); tran_(WAIT4_QUERY_KIND); break; case QP::QS_RX_EVENT: tran_(WAIT4_EVT_PRIO); break; #ifdef Q_UTEST case QP::QS_RX_TEST_SETUP: tran_(WAIT4_TEST_SETUP_FRAME); break; case QP::QS_RX_TEST_TEARDOWN: tran_(WAIT4_TEST_TEARDOWN_FRAME); break; case QP::QS_RX_TEST_CONTINUE: tran_(WAIT4_TEST_CONTINUE_FRAME); break; case QP::QS_RX_TEST_PROBE: if (QP::QS::tstPriv_.tpNum < static_cast<std::uint8_t>( (sizeof(QP::QS::tstPriv_.tpBuf) / sizeof(QP::QS::tstPriv_.tpBuf[0])))) { QP::QS::rxPriv_.var.tp.data = 0U; QP::QS::rxPriv_.var.tp.idx = 0U; tran_(WAIT4_TEST_PROBE_DATA); } else { // the # Test-Probes exceeded rxReportError_( static_cast<std::uint8_t>(QP::QS_RX_TEST_PROBE)); tran_(ERROR_STATE); } break; #endif // Q_UTEST default: rxReportError_(0x43U); tran_(ERROR_STATE); break; } break; } case WAIT4_INFO_FRAME: { // keep ignoring the data until a frame is collected break; } case WAIT4_CMD_ID: { QP::QS::rxPriv_.var.cmd.cmdId = b; QP::QS::rxPriv_.var.cmd.idx = 0U; QP::QS::rxPriv_.var.cmd.param1 = 0U; QP::QS::rxPriv_.var.cmd.param2 = 0U; QP::QS::rxPriv_.var.cmd.param3 = 0U; tran_(WAIT4_CMD_PARAM1); break; } case WAIT4_CMD_PARAM1: { QP::QS::rxPriv_.var.cmd.param1 |= (static_cast<std::uint32_t>(b) << QP::QS::rxPriv_.var.cmd.idx); QP::QS::rxPriv_.var.cmd.idx += 8U; if (QP::QS::rxPriv_.var.cmd.idx == (8U * 4U)) { QP::QS::rxPriv_.var.cmd.idx = 0U; tran_(WAIT4_CMD_PARAM2); } break; } case WAIT4_CMD_PARAM2: { QP::QS::rxPriv_.var.cmd.param2 |= static_cast<std::uint32_t>(b) << QP::QS::rxPriv_.var.cmd.idx; QP::QS::rxPriv_.var.cmd.idx += 8U; if (QP::QS::rxPriv_.var.cmd.idx == (8U * 4U)) { QP::QS::rxPriv_.var.cmd.idx = 0U; tran_(WAIT4_CMD_PARAM3); } break; } case WAIT4_CMD_PARAM3: { QP::QS::rxPriv_.var.cmd.param3 |= static_cast<std::uint32_t>(b) << QP::QS::rxPriv_.var.cmd.idx; QP::QS::rxPriv_.var.cmd.idx += 8U; if (QP::QS::rxPriv_.var.cmd.idx == (8U * 4U)) { QP::QS::rxPriv_.var.cmd.idx = 0U; tran_(WAIT4_CMD_FRAME); } break; } case WAIT4_CMD_FRAME: { // keep ignoring the data until a frame is collected break; } case WAIT4_RESET_FRAME: { // keep ignoring the data until a frame is collected break; } case WAIT4_TICK_RATE: { QP::QS::rxPriv_.var.tick.rate = static_cast<std::uint_fast8_t>(b); tran_(WAIT4_TICK_FRAME); break; } case WAIT4_TICK_FRAME: { // keep ignoring the data until a frame is collected break; } case WAIT4_PEEK_OFFS: { if (QP::QS::rxPriv_.var.peek.idx == 0U) { QP::QS::rxPriv_.var.peek.offs = static_cast<std::uint16_t>(b); QP::QS::rxPriv_.var.peek.idx += 8U; } else { QP::QS::rxPriv_.var.peek.offs |= static_cast<std::uint16_t>( static_cast<std::uint16_t>(b) << 8U); tran_(WAIT4_PEEK_SIZE); } break; } case WAIT4_PEEK_SIZE: { if ((b == 1U) || (b == 2U) || (b == 4U)) { QP::QS::rxPriv_.var.peek.size = b; tran_(WAIT4_PEEK_NUM); } else { rxReportError_(static_cast<std::uint8_t>(QP::QS_RX_PEEK)); tran_(ERROR_STATE); } break; } case WAIT4_PEEK_NUM: { QP::QS::rxPriv_.var.peek.num = b; tran_(WAIT4_PEEK_FRAME); break; } case WAIT4_PEEK_FRAME: { // keep ignoring the data until a frame is collected break; } case WAIT4_POKE_OFFS: { if (QP::QS::rxPriv_.var.poke.idx == 0U) { QP::QS::rxPriv_.var.poke.offs = static_cast<std::uint16_t>(b); QP::QS::rxPriv_.var.poke.idx = 1U; } else { QP::QS::rxPriv_.var.poke.offs |= static_cast<std::uint16_t>( static_cast<std::uint16_t>(b) << 8U); tran_(WAIT4_POKE_SIZE); } break; } case WAIT4_POKE_SIZE: { if ((b == 1U) || (b == 2U) || (b == 4U)) { QP::QS::rxPriv_.var.poke.size = b; tran_(WAIT4_POKE_NUM); } else { rxReportError_((QP::QS::rxPriv_.var.poke.fill != 0U) ? static_cast<std::uint8_t>(QP::QS_RX_FILL) : static_cast<std::uint8_t>(QP::QS_RX_POKE)); tran_(ERROR_STATE); } break; } case WAIT4_POKE_NUM: { if (b > 0U) { QP::QS::rxPriv_.var.poke.num = b; QP::QS::rxPriv_.var.poke.data = 0U; QP::QS::rxPriv_.var.poke.idx = 0U; tran_((QP::QS::rxPriv_.var.poke.fill != 0U) ? WAIT4_FILL_DATA : WAIT4_POKE_DATA); } else { rxReportError_((QP::QS::rxPriv_.var.poke.fill != 0U) ? static_cast<std::uint8_t>(QP::QS_RX_FILL) : static_cast<std::uint8_t>(QP::QS_RX_POKE)); tran_(ERROR_STATE); } break; } case WAIT4_FILL_DATA: { QP::QS::rxPriv_.var.poke.data |= static_cast<std::uint32_t>(b) << QP::QS::rxPriv_.var.poke.idx; QP::QS::rxPriv_.var.poke.idx += 8U; if ((QP::QS::rxPriv_.var.poke.idx >> 3U) == QP::QS::rxPriv_.var.poke.size) { tran_(WAIT4_FILL_FRAME); } break; } case WAIT4_POKE_DATA: { QP::QS::rxPriv_.var.poke.data |= static_cast<std::uint32_t>(b) << QP::QS::rxPriv_.var.poke.idx; QP::QS::rxPriv_.var.poke.idx += 8U; if ((QP::QS::rxPriv_.var.poke.idx >> 3U) == QP::QS::rxPriv_.var.poke.size) { rxPoke_(); --QP::QS::rxPriv_.var.poke.num; if (QP::QS::rxPriv_.var.poke.num == 0U) { tran_(WAIT4_POKE_FRAME); } } break; } case WAIT4_FILL_FRAME: { // keep ignoring the data until a frame is collected break; } case WAIT4_POKE_FRAME: { // keep ignoring the data until a frame is collected break; } case WAIT4_FILTER_LEN: { if (b == static_cast<std::uint8_t>(sizeof(QP::QS::rxPriv_.var.flt.data))) { QP::QS::rxPriv_.var.flt.idx = 0U; tran_(WAIT4_FILTER_DATA); } else { rxReportError_(QP::QS::rxPriv_.var.flt.recId); tran_(ERROR_STATE); } break; } case WAIT4_FILTER_DATA: { QP::QS::rxPriv_.var.flt.data[QP::QS::rxPriv_.var.flt.idx] = b; ++QP::QS::rxPriv_.var.flt.idx; if (QP::QS::rxPriv_.var.flt.idx == sizeof(QP::QS::rxPriv_.var.flt.data)) { tran_(WAIT4_FILTER_FRAME); } break; } case WAIT4_FILTER_FRAME: { // keep ignoring the data until a frame is collected break; } case WAIT4_OBJ_KIND: { if (b <= static_cast<std::uint8_t>(QP::QS::SM_AO_OBJ)) { QP::QS::rxPriv_.var.obj.kind = b; QP::QS::rxPriv_.var.obj.addr = 0U; QP::QS::rxPriv_.var.obj.idx = 0U; tran_(WAIT4_OBJ_ADDR); } else { rxReportError_(QP::QS::rxPriv_.var.obj.recId); tran_(ERROR_STATE); } break; } case WAIT4_OBJ_ADDR: { QP::QS::rxPriv_.var.obj.addr |= static_cast<QP::QSObj>(b) << QP::QS::rxPriv_.var.obj.idx; QP::QS::rxPriv_.var.obj.idx += 8U; if (QP::QS::rxPriv_.var.obj.idx == (8U * static_cast<unsigned>(QS_OBJ_PTR_SIZE))) { tran_(WAIT4_OBJ_FRAME); } break; } case WAIT4_OBJ_FRAME: { // keep ignoring the data until a frame is collected break; } case WAIT4_QUERY_KIND: { if (b < static_cast<std::uint8_t>(QP::QS::MAX_OBJ)) { QP::QS::rxPriv_.var.obj.kind = b; tran_(WAIT4_QUERY_FRAME); } else { rxReportError_(QP::QS::rxPriv_.var.obj.recId); tran_(ERROR_STATE); } break; } case WAIT4_QUERY_FRAME: { // keep ignoring the data until a frame is collected break; } case WAIT4_EVT_PRIO: { QP::QS::rxPriv_.var.evt.prio = b; QP::QS::rxPriv_.var.evt.sig = 0U; QP::QS::rxPriv_.var.evt.idx = 0U; tran_(WAIT4_EVT_SIG); break; } case WAIT4_EVT_SIG: { QP::QS::rxPriv_.var.evt.sig |= static_cast<QP::QSignal>( static_cast<std::uint32_t>(b) << QP::QS::rxPriv_.var.evt.idx); QP::QS::rxPriv_.var.evt.idx += 8U; if (QP::QS::rxPriv_.var.evt.idx == (8U *static_cast<unsigned>(Q_SIGNAL_SIZE))) { QP::QS::rxPriv_.var.evt.len = 0U; QP::QS::rxPriv_.var.evt.idx = 0U; tran_(WAIT4_EVT_LEN); } break; } case WAIT4_EVT_LEN: { QP::QS::rxPriv_.var.evt.len |= static_cast<std::uint16_t>( static_cast<unsigned>(b) << QP::QS::rxPriv_.var.evt.idx); QP::QS::rxPriv_.var.evt.idx += 8U; if (QP::QS::rxPriv_.var.evt.idx == (8U * 2U)) { if ((QP::QS::rxPriv_.var.evt.len + sizeof(QP::QEvt)) <= QP::QF::poolGetMaxBlockSize()) { // report Ack before generating any other QS records rxReportAck_(QP::QS_RX_EVENT); QP::QS::rxPriv_.var.evt.e = QP::QF::newX_( (static_cast<std::uint_fast16_t>(QP::QS::rxPriv_.var.evt.len) + sizeof(QP::QEvt)), 0U, // margin static_cast<enum_t>(QP::QS::rxPriv_.var.evt.sig)); // event allocated? if (QP::QS::rxPriv_.var.evt.e != nullptr) { QP::QS::rxPriv_.var.evt.p = reinterpret_cast<std::uint8_t *>(QP::QS::rxPriv_.var.evt.e); QP::QS::rxPriv_.var.evt.p = &QP::QS::rxPriv_.var.evt.p[sizeof(QP::QEvt)]; if (QP::QS::rxPriv_.var.evt.len > 0U) { tran_(WAIT4_EVT_PAR); } else { tran_(WAIT4_EVT_FRAME); } } else { rxReportError_( static_cast<std::uint8_t>(QP::QS_RX_EVENT)); tran_(ERROR_STATE); } } else { rxReportError_( static_cast<std::uint8_t>(QP::QS_RX_EVENT)); tran_(ERROR_STATE); } } break; } case WAIT4_EVT_PAR: { // event parameters *QP::QS::rxPriv_.var.evt.p = b; ++QP::QS::rxPriv_.var.evt.p; --QP::QS::rxPriv_.var.evt.len; if (QP::QS::rxPriv_.var.evt.len == 0U) { tran_(WAIT4_EVT_FRAME); } break; } case WAIT4_EVT_FRAME: { // keep ignoring the data until a frame is collected break; } #ifdef Q_UTEST case WAIT4_TEST_SETUP_FRAME: { // keep ignoring the data until a frame is collected break; } case WAIT4_TEST_TEARDOWN_FRAME: { // keep ignoring the data until a frame is collected break; } case WAIT4_TEST_CONTINUE_FRAME: { // keep ignoring the data until a frame is collected break; } case WAIT4_TEST_PROBE_DATA: { QP::QS::rxPriv_.var.tp.data |= (static_cast<QP::QSFun>(b) << QP::QS::rxPriv_.var.tp.idx); QP::QS::rxPriv_.var.tp.idx += 8U; if (QP::QS::rxPriv_.var.tp.idx == (8U * sizeof(std::uint32_t))) { QP::QS::rxPriv_.var.tp.addr = 0U; QP::QS::rxPriv_.var.tp.idx = 0U; tran_(WAIT4_TEST_PROBE_ADDR); } break; } case WAIT4_TEST_PROBE_ADDR: { QP::QS::rxPriv_.var.tp.addr |= (static_cast<std::uint32_t>(b) << QP::QS::rxPriv_.var.tp.idx); QP::QS::rxPriv_.var.tp.idx += 8U; if (QP::QS::rxPriv_.var.tp.idx == (8U * static_cast<unsigned>(QS_FUN_PTR_SIZE))) { tran_(WAIT4_TEST_PROBE_FRAME); } break; } case WAIT4_TEST_PROBE_FRAME: { // keep ignoring the data until a frame is collected break; } #endif // Q_UTEST case ERROR_STATE: { // keep ignoring the data until a good frame is collected break; } default: { // unexpected or unimplemented state rxReportError_(0x45U); tran_(ERROR_STATE); break; } } } //............................................................................ void rxHandleGoodFrame_(std::uint8_t const state) { std::uint8_t i; std::uint8_t *ptr; QS_CRIT_STAT switch (state) { case WAIT4_INFO_FRAME: { // no need to report Ack or Done QS_CRIT_ENTRY(); QS_MEM_SYS(); QP::QS::target_info_pre_(0U); // send only Target info QS_MEM_APP(); QS_CRIT_EXIT(); break; } case WAIT4_RESET_FRAME: { // no need to report Ack or Done, because Target resets QP::QS::onReset(); // reset the Target break; } case WAIT4_CMD_PARAM1: // intentionally fall-through case WAIT4_CMD_PARAM2: // intentionally fall-through case WAIT4_CMD_PARAM3: // intentionally fall-through case WAIT4_CMD_FRAME: { rxReportAck_(QP::QS_RX_COMMAND); QP::QS::onCommand(QP::QS::rxPriv_.var.cmd.cmdId, QP::QS::rxPriv_.var.cmd.param1, QP::QS::rxPriv_.var.cmd.param2, QP::QS::rxPriv_.var.cmd.param3); #ifdef Q_UTEST #if Q_UTEST != 0 QP::QS::processTestEvts_(); // process all events produced #endif // Q_UTEST != 0 #endif // Q_UTEST rxReportDone_(QP::QS_RX_COMMAND); break; } case WAIT4_TICK_FRAME: { rxReportAck_(QP::QS_RX_TICK); #ifdef Q_UTEST QP::QTimeEvt::tick1_( static_cast<std::uint_fast8_t>(QP::QS::rxPriv_.var.tick.rate), &QP::QS::rxPriv_); #if Q_UTEST != 0 QP::QS::processTestEvts_(); // process all events produced #endif // Q_UTEST != 0 #else QP::QTimeEvt::tick( static_cast<std::uint_fast8_t>(QP::QS::rxPriv_.var.tick.rate), &QP::QS::rxPriv_); #endif // Q_UTEST rxReportDone_(QP::QS_RX_TICK); break; } case WAIT4_PEEK_FRAME: { // no need to report Ack or Done QS_CRIT_ENTRY(); QS_MEM_SYS(); QP::QS::beginRec_(static_cast<std::uint_fast8_t>( QP::QS_PEEK_DATA)); ptr = static_cast<std::uint8_t*>( QP::QS::rxPriv_.currObj[QP::QS::AP_OBJ]); ptr = &ptr[QP::QS::rxPriv_.var.peek.offs]; QS_TIME_PRE_(); // timestamp QS_U16_PRE_(QP::QS::rxPriv_.var.peek.offs); // data offset QS_U8_PRE_(QP::QS::rxPriv_.var.peek.size); // data size QS_U8_PRE_(QP::QS::rxPriv_.var.peek.num); // number of data items for (i = 0U; i < QP::QS::rxPriv_.var.peek.num; ++i) { switch (QP::QS::rxPriv_.var.peek.size) { case 1: QS_U8_PRE_(ptr[i]); break; case 2: QS_U16_PRE_( reinterpret_cast<std::uint16_t*>(ptr)[i]); break; case 4: QS_U32_PRE_( reinterpret_cast<std::uint32_t*>(ptr)[i]); break; default: // intentionally empty break; } } QP::QS::endRec_(); QS_MEM_APP(); QS_CRIT_EXIT(); QS_REC_DONE(); // user callback (if defined) break; } case WAIT4_POKE_DATA: { // received less than expected poke data items rxReportError_(static_cast<std::uint8_t>(QP::QS_RX_POKE)); break; } case WAIT4_POKE_FRAME: { rxReportAck_(QP::QS_RX_POKE); // no need to report done break; } case WAIT4_FILL_FRAME: { rxReportAck_(QP::QS_RX_FILL); ptr = static_cast<std::uint8_t *>( QP::QS::rxPriv_.currObj[QP::QS::AP_OBJ]); ptr = &ptr[QP::QS::rxPriv_.var.poke.offs]; for (i = 0U; i < QP::QS::rxPriv_.var.poke.num; ++i) { switch (QP::QS::rxPriv_.var.poke.size) { case 1: ptr[i] = static_cast<std::uint8_t>(QP::QS::rxPriv_.var.poke.data); break; case 2: reinterpret_cast<std::uint16_t *>(ptr)[i] = static_cast<std::uint16_t>(QP::QS::rxPriv_.var.poke.data); break; case 4: reinterpret_cast<std::uint32_t *>(ptr)[i] = QP::QS::rxPriv_.var.poke.data; break; default: // intentionally empty break; } } break; } case WAIT4_FILTER_FRAME: { rxReportAck_(static_cast<enum QP::QSpyRxRecords>( QP::QS::rxPriv_.var.flt.recId)); // apply the received filters if (QP::QS::rxPriv_.var.flt.recId == static_cast<std::uint8_t>(QP::QS_RX_GLB_FILTER)) { for (i = 0U; i < static_cast<std::uint8_t>(sizeof(QP::QS::filt_.glb)); ++i) { QP::QS::filt_.glb[i] = QP::QS::rxPriv_.var.flt.data[i]; } // leave the "not maskable" filters enabled, // see qs.hpp, Miscellaneous QS records (not maskable) QP::QS::filt_.glb[0] |= 0x01U; QP::QS::filt_.glb[7] |= 0xFCU; QP::QS::filt_.glb[8] |= 0x7FU; // never enable the last 3 records (0x7D, 0x7E, 0x7F) QP::QS::filt_.glb[15] &= 0x1FU; } else if (QP::QS::rxPriv_.var.flt.recId == static_cast<std::uint8_t>(QP::QS_RX_LOC_FILTER)) { for (i = 0U; i < Q_DIM(QP::QS::filt_.loc); ++i) { QP::QS::filt_.loc[i] = QP::QS::rxPriv_.var.flt.data[i]; } // leave QS_ID == 0 always on QP::QS::filt_.loc[0] |= 0x01U; } else { rxReportError_(QP::QS::rxPriv_.var.flt.recId); } // no need to report Done break; } case WAIT4_OBJ_FRAME: { i = QP::QS::rxPriv_.var.obj.kind; if (i < static_cast<std::uint8_t>(QP::QS::MAX_OBJ)) { if (QP::QS::rxPriv_.var.obj.recId == static_cast<std::uint8_t>(QP::QS_RX_CURR_OBJ)) { QP::QS::rxPriv_.currObj[i] = reinterpret_cast<void *>(QP::QS::rxPriv_.var.obj.addr); rxReportAck_(QP::QS_RX_CURR_OBJ); } else if (QP::QS::rxPriv_.var.obj.recId == static_cast<std::uint8_t>(QP::QS_RX_AO_FILTER)) { if (QP::QS::rxPriv_.var.obj.addr != 0U) { std::int_fast16_t const filter = static_cast<std::int_fast16_t>( reinterpret_cast<QP::QActive *>( QP::QS::rxPriv_.var.obj.addr)->getPrio()); QP::QS::locFilter_((i == 0) ? filter :-filter); rxReportAck_(QP::QS_RX_AO_FILTER); } else { rxReportError_(static_cast<enum_t>( QP::QS_RX_AO_FILTER)); } } else { rxReportError_(QP::QS::rxPriv_.var.obj.recId); } } // both SM and AO else if (i == static_cast<std::uint8_t>(QP::QS::SM_AO_OBJ)) { if (QP::QS::rxPriv_.var.obj.recId == static_cast<std::uint8_t>(QP::QS_RX_CURR_OBJ)) { QP::QS::rxPriv_.currObj[QP::QS::SM_OBJ] = reinterpret_cast<void *>(QP::QS::rxPriv_.var.obj.addr); QP::QS::rxPriv_.currObj[QP::QS::AO_OBJ] = reinterpret_cast<void *>(QP::QS::rxPriv_.var.obj.addr); } rxReportAck_( static_cast<enum QP::QSpyRxRecords>(QP::QS::rxPriv_.var.obj.recId)); } else { rxReportError_(QP::QS::rxPriv_.var.obj.recId); } break; } case WAIT4_QUERY_FRAME: { queryCurrObj_(QP::QS::rxPriv_.var.obj.kind); break; } case WAIT4_EVT_FRAME: { // NOTE: Ack was already reported in the WAIT4_EVT_LEN state #ifdef Q_UTEST QP::QS::onTestEvt(QP::QS::rxPriv_.var.evt.e); // "massage" the event #endif // Q_UTEST // use 'i' as status, 0 == success,no-recycle i = 0U; if (QP::QS::rxPriv_.var.evt.prio == 0U) { // publish QP::QActive::publish_(QP::QS::rxPriv_.var.evt.e, &QP::QS::rxPriv_, 0U); } else if (QP::QS::rxPriv_.var.evt.prio < QF_MAX_ACTIVE) { if (!QP::QActive::registry_[QP::QS::rxPriv_.var.evt.prio]->POST_X( QP::QS::rxPriv_.var.evt.e, 0U, // margin &QP::QS::rxPriv_)) { // failed QACTIVE_POST() recycles the event i = 0x80U; // failure status, no recycle } } else if (QP::QS::rxPriv_.var.evt.prio == 255U) { // dispatch to the current SM object if (QP::QS::rxPriv_.currObj[QP::QS::SM_OBJ] != nullptr) { // increment the ref-ctr to simulate the situation // when the event is just retrieved from a queue. // This is expected for the following QF::gc() call. QP::QEvt_refCtr_inc_(QP::QS::rxPriv_.var.evt.e); static_cast<QP::QAsm *>( QP::QS::rxPriv_.currObj[QP::QS::SM_OBJ]) ->dispatch(QP::QS::rxPriv_.var.evt.e, 0U); i = 0x01U; // success, recycle } else { i = 0x81U; // failure, recycle } } else if (QP::QS::rxPriv_.var.evt.prio == 254U) { // init the current SM object" if (QP::QS::rxPriv_.currObj[QP::QS::SM_OBJ] != nullptr) { // increment the ref-ctr to simulate the situation // when the event is just retrieved from a queue. // This is expected for the following QF::gc() call. QP::QEvt_refCtr_inc_(QP::QS::rxPriv_.var.evt.e); static_cast<QP::QAsm *>( QP::QS::rxPriv_.currObj[QP::QS::SM_OBJ]) ->init(QP::QS::rxPriv_.var.evt.e, 0U); i = 0x01U; // success, recycle } else { i = 0x81U; // failure, recycle } } else if (QP::QS::rxPriv_.var.evt.prio == 253U) { // post to the current AO if (QP::QS::rxPriv_.currObj[QP::QS::AO_OBJ] != nullptr) { if (!static_cast<QP::QActive *>( QP::QS::rxPriv_.currObj[QP::QS::AO_OBJ])->POST_X( QP::QS::rxPriv_.var.evt.e, 0U, // margin &QP::QS::rxPriv_)) { // failed QACTIVE_POST() recycles the event i = 0x80U; // failure, no recycle } } else { i = 0x81U; // failure, recycle } } else { i = 0x81U; // failure, recycle } #if (QF_MAX_EPOOL > 0U) // recycle needed? if ((i & 1U) != 0U) { QP::QF::gc(QP::QS::rxPriv_.var.evt.e); } #endif // failure? if ((i & 0x80U) != 0U) { rxReportError_(static_cast<std::uint8_t>(QP::QS_RX_EVENT)); } else { #ifdef Q_UTEST #if Q_UTEST != 0 QP::QS::processTestEvts_(); // process all events produced #endif // Q_UTEST != 0 #endif // Q_UTEST rxReportDone_(QP::QS_RX_EVENT); } break; } #ifdef Q_UTEST case WAIT4_TEST_SETUP_FRAME: { rxReportAck_(QP::QS_RX_TEST_SETUP); QP::QS::tstPriv_.tpNum = 0U; // clear Test-Probes QP::QS::tstPriv_.testTime = 0U; //clear time tick // don't clear current objects QP::QS::onTestSetup(); // application-specific test setup // no need to report Done break; } case WAIT4_TEST_TEARDOWN_FRAME: { rxReportAck_(QP::QS_RX_TEST_TEARDOWN); QP::QS::onTestTeardown(); // application-specific test teardown // no need to report Done break; } case WAIT4_TEST_CONTINUE_FRAME: { rxReportAck_(QP::QS_RX_TEST_CONTINUE); QP::QS::rxPriv_.inTestLoop = false; // exit the QUTest loop // no need to report Done break; } case WAIT4_TEST_PROBE_FRAME: { rxReportAck_(QP::QS_RX_TEST_PROBE); Q_ASSERT_INCRIT(815, QP::QS::tstPriv_.tpNum < (sizeof(QP::QS::tstPriv_.tpBuf) / sizeof(QP::QS::tstPriv_.tpBuf[0]))); QP::QS::tstPriv_.tpBuf[QP::QS::tstPriv_.tpNum] = QP::QS::rxPriv_.var.tp; ++QP::QS::tstPriv_.tpNum; // no need to report Done break; } #endif // Q_UTEST case ERROR_STATE: { // keep ignoring all bytes until new frame break; } default: { rxReportError_(0x47U); break; } } } //............................................................................ static void rxHandleBadFrame_(std::uint8_t const state) noexcept { rxReportError_(0x50U); // error for all bad frames switch (state) { case WAIT4_EVT_FRAME: { QS_CRIT_STAT QS_CRIT_ENTRY(); Q_ASSERT_INCRIT(910, QP::QS::rxPriv_.var.evt.e != nullptr); QS_CRIT_EXIT(); #if (QF_MAX_EPOOL > 0U) QP::QF::gc(QP::QS::rxPriv_.var.evt.e); // don't leak allocated evt #endif break; } default: { // intentionally empty break; } } } //............................................................................ static void rxReportAck_(enum QP::QSpyRxRecords const recId) noexcept { QS_CRIT_STAT QS_CRIT_ENTRY(); QS_MEM_SYS(); QP::QS::beginRec_(static_cast<std::uint_fast8_t>(QP::QS_RX_STATUS)); QS_U8_PRE_(recId); // record ID QP::QS::endRec_(); QS_MEM_APP(); QS_CRIT_EXIT(); QS_REC_DONE(); // user callback (if defined) } //............................................................................ static void rxReportError_(std::uint8_t const code) noexcept { QS_CRIT_STAT QS_CRIT_ENTRY(); QS_MEM_SYS(); QP::QS::beginRec_(static_cast<std::uint_fast8_t>(QP::QS_RX_STATUS)); QS_U8_PRE_(0x80U | code); // error code QP::QS::endRec_(); QS_MEM_APP(); QS_CRIT_EXIT(); QS_REC_DONE(); // user callback (if defined) } //............................................................................ static void rxReportDone_(enum QP::QSpyRxRecords const recId) noexcept { QS_CRIT_STAT QS_CRIT_ENTRY(); QS_MEM_SYS(); QP::QS::beginRec_(static_cast<std::uint_fast8_t>(QP::QS_TARGET_DONE)); QS_TIME_PRE_(); // timestamp QS_U8_PRE_(recId); // record ID QP::QS::endRec_(); QS_MEM_APP(); QS_CRIT_EXIT(); QS_REC_DONE(); // user callback (if defined) } //............................................................................ static void queryCurrObj_(std::uint8_t obj_kind) noexcept { QS_CRIT_STAT QS_CRIT_ENTRY(); Q_REQUIRE_INCRIT(800, obj_kind < Q_DIM(QP::QS::rxPriv_.currObj)); QS_CRIT_EXIT(); if (QP::QS::rxPriv_.currObj[obj_kind] != nullptr) { QS_CRIT_ENTRY(); QS_MEM_SYS(); QP::QS::beginRec_(static_cast<std::uint_fast8_t>(QP::QS_QUERY_DATA)); QS_TIME_PRE_(); // timestamp QS_U8_PRE_(obj_kind); // object kind QS_OBJ_PRE_(QP::QS::rxPriv_.currObj[obj_kind]); // object pointer switch (obj_kind) { case QP::QS::SM_OBJ: // intentionally fall through case QP::QS::AO_OBJ: QS_FUN_PRE_(reinterpret_cast<QP::QHsm *>( QP::QS::rxPriv_.currObj[obj_kind])->getStateHandler()); break; case QP::QS::MP_OBJ: QS_MPC_PRE_(reinterpret_cast<QP::QMPool *>( QP::QS::rxPriv_.currObj[obj_kind])->getNFree()); QS_MPC_PRE_(reinterpret_cast<QP::QMPool *>( QP::QS::rxPriv_.currObj[obj_kind])->getNMin()); break; case QP::QS::EQ_OBJ: QS_EQC_PRE_(reinterpret_cast<QP::QEQueue *>( QP::QS::rxPriv_.currObj[obj_kind])->getNFree()); QS_EQC_PRE_(reinterpret_cast<QP::QEQueue *>( QP::QS::rxPriv_.currObj[obj_kind])->getNMin()); break; case QP::QS::TE_OBJ: QS_OBJ_PRE_(reinterpret_cast<QP::QTimeEvt *>( QP::QS::rxPriv_.currObj[obj_kind])->getAct()); QS_TEC_PRE_(reinterpret_cast<QP::QTimeEvt *>( QP::QS::rxPriv_.currObj[obj_kind])->getCtr()); QS_TEC_PRE_(reinterpret_cast<QP::QTimeEvt *>( QP::QS::rxPriv_.currObj[obj_kind])->getInterval()); QS_SIG_PRE_(reinterpret_cast<QP::QTimeEvt *>( QP::QS::rxPriv_.currObj[obj_kind])->sig); QS_U8_PRE_ (reinterpret_cast<QP::QTimeEvt *>( QP::QS::rxPriv_.currObj[obj_kind])->refCtr_); break; default: // intentionally empty break; } QP::QS::endRec_(); QS_MEM_APP(); QS_CRIT_EXIT(); QS_REC_DONE(); // user callback (if defined) } else { rxReportError_(static_cast<std::uint8_t>(QP::QS_RX_AO_FILTER)); } } //............................................................................ static void rxPoke_() noexcept { std::uint8_t * ptr = static_cast<std::uint8_t *>(QP::QS::rxPriv_.currObj[QP::QS::AP_OBJ]); ptr = &ptr[QP::QS::rxPriv_.var.poke.offs]; switch (QP::QS::rxPriv_.var.poke.size) { case 1: *ptr = static_cast<std::uint8_t>(QP::QS::rxPriv_.var.poke.data); break; case 2: *reinterpret_cast<std::uint16_t *>(ptr) = static_cast<std::uint16_t>(QP::QS::rxPriv_.var.poke.data); break; case 4: *reinterpret_cast<std::uint32_t *>(ptr) = QP::QS::rxPriv_.var.poke.data; break; default: Q_ERROR_ID(900); break; } QP::QS::rxPriv_.var.poke.data = 0U; QP::QS::rxPriv_.var.poke.idx = 0U; QP::QS::rxPriv_.var.poke.offs += static_cast<std::uint16_t>(QP::QS::rxPriv_.var.poke.size); } } // unnamed namespace //! @endcond // only build when Q_UTEST is defined #ifdef Q_UTEST #define QP_IMPL // this is QP implementation #include "qp_port.hpp" // QP port #include "qp_pkg.hpp" // QP package-scope interface #include "qsafe.h" // QP Functional Safety (FuSa) Subsystem #include "qs_port.hpp" // QS port #include "qs_pkg.hpp" // QS package-scope internal interface $define ${QS::QUTest} //============================================================================ //! @cond INTERNAL namespace QP { namespace QS { TestAttr tstPriv_; //............................................................................ void test_pause_() { beginRec_(static_cast<std::uint_fast8_t>(QS_TEST_PAUSED)); endRec_(); onTestLoop(); } //............................................................................ std::uint32_t getTestProbe_(QSpyFunPtr const api) noexcept { std::uint32_t data = 0U; for (std::uint8_t i = 0U; i < tstPriv_.tpNum; ++i) { if (tstPriv_.tpBuf[i].addr == reinterpret_cast<QSFun>(api)) { data = tstPriv_.tpBuf[i].data; QS_CRIT_STAT QS_CRIT_ENTRY(); QS_MEM_SYS(); QS::beginRec_(static_cast<std::uint_fast8_t>(QS_TEST_PROBE_GET)); QS_TIME_PRE_(); // timestamp QS_FUN_PRE_(api); // the calling API QS_U32_PRE_(data); // the Test-Probe data QS::endRec_(); QS_REC_DONE(); // user callback (if defined) --tstPriv_.tpNum; // one less Test-Probe // move all remaining entries in the buffer up by one for (std::uint8_t j = i; j < tstPriv_.tpNum; ++j) { tstPriv_.tpBuf[j] = tstPriv_.tpBuf[j + 1U]; } QS_MEM_APP(); QS_CRIT_EXIT(); break; // we are done (Test-Probe retrieved) } } return data; } //............................................................................ QSTimeCtr onGetTime() { return (++tstPriv_.testTime); } } // namespace QS } // namespace QP //............................................................................ extern "C" { Q_NORETURN Q_onError( char const * const module, int_t const id) { // NOTE: called in a critical section QP::QS::beginRec_(static_cast<std::uint_fast8_t>(QP::QS_ASSERT_FAIL)); QS_TIME_PRE_(); QS_U16_PRE_(id); QS_STR_PRE_((module != nullptr) ? module : "?"); QP::QS::endRec_(); QP::QS::onFlush(); // flush the assertion record to the host QP::QS::onCleanup(); // cleanup after the failure QP::QS::onReset(); // reset the target to prevent it from continuing for (;;) { // onReset() should not return, but to ensure no-return... } } } // extern "C" //! @endcond //============================================================================ // QP-stub for QUTest // NOTE: The QP-stub is needed for unit testing QP applications, but might // NOT be needed for testing QP itself. In that case, the build process // can define Q_UTEST=0 to exclude the QP-stub from the build. #if Q_UTEST != 0 // unnamed namespace for local definitions with internal linkage namespace { Q_DEFINE_THIS_MODULE("qutest") } // unnamed namespace namespace QP { namespace QS { void processTestEvts_() { QS_TEST_PROBE_DEF(&QS::processTestEvts_) // return immediately (do nothing) for Test Probe != 0 QS_TEST_PROBE(return;) while (tstPriv_.readySet.notEmpty()) { std::uint_fast8_t const p = tstPriv_.readySet.findMax(); QActive * const a = QActive::registry_[p]; QEvt const * const e = a->get_(); a->dispatch(e, a->getPrio()); #if (QF_MAX_EPOOL > 0U) QF::gc(e); #endif if (a->getEQueue().isEmpty()) { // empty queue? tstPriv_.readySet.remove(p); #ifndef Q_UNSAFE tstPriv_.readySet.update_(&tstPriv_.readySet_dis); #endif } } } } // namespace QS } // namespace QP $define ${QS::QUTest-stub} #endif // Q_UTEST != 0 #endif // def Q_UTEST #include "qstamp.hpp" namespace QP { char const BUILD_DATE[12] = __DATE__; char const BUILD_TIME[9] = __TIME__; } // namespace QP