QP/C Real-Time Embedded Framework (RTEF) This 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> \ static char const Q_this_module_[] = name_; \ ((expr_) ? ((void)0) : Q_onError(&Q_this_module_[0], (id_))) \ (Q_onError(&Q_this_module_[0], (id_))) do { \ QF_CRIT_STAT \ QF_CRIT_ENTRY(); \ (expr_) ? ((void)0) : Q_onError(&Q_this_module_[0], (id_)); \ QF_CRIT_EXIT(); \ } while (false) do { \ QF_CRIT_STAT \ QF_CRIT_ENTRY(); \ Q_onError(&Q_this_module_[0], (id_)); \ QF_CRIT_EXIT(); \ } while (false) ((void)0) ((void)0) ((void)0) ((void)0) Q_DEFINE_THIS_MODULE(__FILE__) Q_ASSERT_ID(__LINE__, (expr_)) Q_ERROR_ID(__LINE__) Q_ASSERT_ID((id_), (expr_)) Q_ASSERT(expr_) Q_ASSERT_INCRIT((id_), (expr_)) Q_ASSERT_ID((id_), (expr_)) Q_ASSERT(expr_) Q_ASSERT_INCRIT((id_), (expr_)) Q_ASSERT_ID((id_), (expr_)) Q_ASSERT(expr_) extern char Q_static_assert_[(expr_) ? 1 : -1] _Noreturn void (sizeof(array_) / sizeof((array_)[0U])) //! the current QP version number string in ROM, based on #QP_VERSION_STR = QP_VERSION_STR; 0xE0U 0U // ! @class QEvt //! @public @memberof QEvt //! @private @memberof QEvt //! @private @memberof QEvt = { QEVT_INITIALIZER(Q_EMPTY_SIG), QEVT_INITIALIZER(Q_ENTRY_SIG), QEVT_INITIALIZER(Q_EXIT_SIG), QEVT_INITIALIZER(Q_INIT_SIG) }; //! @public @memberof QEvt //! @public @memberof QEvt if (sig != QEVT_DYNAMIC) { me->sig = (QSignal)sig; me->refCtr_ = 0U; me->evtTag_ = QEVT_MARKER; } return me; const //! @private @memberof QEvt //! @private @memberof QEvt return (me != (QEvt const *)0) && ((me->evtTag_ & 0xF0U) == QEVT_MARKER); const //! @private @memberof QEvt //! @private @memberof QEvt return (uint_fast8_t)me->evtTag_ & 0x0FU; //! All possible values returned from state/action handlers //! @note //! The order of enumeration matters for algorithmic correctness. { // 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 ::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 me->state Q_RET_TRAN_HIST, //!< transition to history of a given state Q_RET_TRAN_XP //!< exit-point transition out of a submachine }; )(void * const me, QEvt const * const e); )(void * const me); // forward declaration )(struct QXThread * const me); { struct QMState const *superstate; //!< @private @memberof QMState QStateHandler const stateHandler; //!< @private @memberof QMState QActionHandler const entryAction; //!< @private @memberof QMState QActionHandler const exitAction; //!< @private @memberof QMState QActionHandler const initAction; //!< @private @memberof QMState } QMState; { QMState const *target; //!< @private @memberof QMTranActTable QActionHandler const act[1]; //!< @private @memberof QMTranActTable } QMTranActTable; //! Reserved signals by the QHsm-style state machine implementation { Q_EMPTY_SIG, //!< signal to execute the default case Q_ENTRY_SIG, //!< signal for coding entry actions Q_EXIT_SIG, //!< signal for coding exit actions Q_INIT_SIG, //!< signal for coding initial transitions Q_USER_SIG //!< offset for the user signals (QP Application) }; { QStateHandler fun; //!< @private @memberof QAsmAttr QActionHandler act; //!< @private @memberof QAsmAttr QXThreadHandler thr; //!< @private @memberof QAsmAttr QMTranActTable const *tatbl; //!< @private @memberof QAsmAttr struct QMState const *obj; //!< @private @memberof QAsmAttr #ifndef Q_UNSAFE uintptr_t uint; //!< @private @memberof QAsmAttr #endif }; //! @class QAsm //! @protected @memberof QAsm //! @protected @memberof QAsm //! @protected @memberof QAsm //! @protected @memberof QAsm //! @protected @memberof QAsm me->vptr = (QAsmVtable *)0; me->state.fun = Q_STATE_CAST(0); me->temp.fun = Q_STATE_CAST(0); { void (*init)(QAsm * const me, void const * const e, uint_fast8_t const qs_id); void (*dispatch)(QAsm * const me, QEvt const * const e, uint_fast8_t const qs_id); #ifdef Q_SPY QStateHandler (*getStateHandler)(QAsm * const me); #endif // Q_SPY }; //! @class QHsm //! @extends QAsm Human-generated State Machine //! @protected @memberof QHsm //! @protected @memberof QHsm static struct QAsmVtable const vtable = { // QAsm virtual table &QHsm_init_, &QHsm_dispatch_ #ifdef Q_SPY ,&QHsm_getStateHandler_ #endif }; // do not call the QAsm_ctor() here me->super.vptr = &vtable; me->super.state.fun = Q_STATE_CAST(&QHsm_top); me->super.temp.fun = initial; //! @private @memberof QHsm //! @private @memberof QHsm QF_CRIT_STAT #ifdef Q_SPY QS_CRIT_ENTRY(); QS_MEM_SYS(); if ((QS_priv_.flags & 0x01U) == 0U) { QS_priv_.flags |= 0x01U; QS_FUN_DICTIONARY(&QHsm_top); } QS_MEM_APP(); QS_CRIT_EXIT(); #else Q_UNUSED_PAR(qs_id); #endif QStateHandler t = me->state.fun; QF_CRIT_ENTRY(); Q_REQUIRE_INCRIT(200, (me->vptr != (struct QAsmVtable *)0) && (me->temp.fun != Q_STATE_CAST(0)) && (t == Q_STATE_CAST(&QHsm_top))); QF_CRIT_EXIT(); // execute the top-most initial tran. QState r = (*me->temp.fun)(me, 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, qs_id) QS_OBJ_PRE_(me); // this state machine object QS_FUN_PRE_(t); // the source state QS_FUN_PRE_(me->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... do { QStateHandler path[QHSM_MAX_NEST_DEPTH_]; // tran entry path array int_fast8_t ip = 0; // tran entry path index path[0] = me->temp.fun; (void)QHSM_RESERVED_EVT_(me->temp.fun, Q_EMPTY_SIG); while (me->temp.fun != t) { ++ip; QF_CRIT_ENTRY(); Q_ASSERT_INCRIT(220, ip < QHSM_MAX_NEST_DEPTH_); QF_CRIT_EXIT(); path[ip] = me->temp.fun; (void)QHSM_RESERVED_EVT_(me->temp.fun, Q_EMPTY_SIG); } me->temp.fun = path[0]; // nested initial tran.; drill into the target hierarchy... do { // enter path[ip] if (QHSM_RESERVED_EVT_(path[ip], Q_ENTRY_SIG) == Q_RET_HANDLED) { QS_STATE_ENTRY_(path[ip], qs_id); } --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, qs_id) QS_OBJ_PRE_(me); // this state machine object QS_FUN_PRE_(t); // the source state QS_FUN_PRE_(me->temp.fun); // the target of the initial tran. QS_END_PRE_() QS_MEM_APP(); QS_CRIT_EXIT(); } #endif // Q_SPY } while (r == Q_RET_TRAN); QS_CRIT_ENTRY(); QS_MEM_SYS(); QS_BEGIN_PRE_(QS_QEP_INIT_TRAN, qs_id) QS_TIME_PRE_(); // time stamp QS_OBJ_PRE_(me); // this state machine object QS_FUN_PRE_(t); // the new active state QS_END_PRE_() QS_MEM_APP(); QS_CRIT_EXIT(); me->state.fun = t; // change the current active state #ifndef Q_UNSAFE me->temp.uint = ~me->state.uint; #endif //! @private @memberof QHsm //! @private @memberof QHsm #ifndef Q_SPY Q_UNUSED_PAR(qs_id); #endif QStateHandler s = me->state.fun; QStateHandler t = s; QF_CRIT_STAT QF_CRIT_ENTRY(); Q_REQUIRE_INCRIT(300, (s != Q_STATE_CAST(0)) && (me->state.uint == (uintptr_t)(~me->temp.uint))); Q_REQUIRE_INCRIT(302, QEvt_verify_(e)); QS_MEM_SYS(); QS_BEGIN_PRE_(QS_QEP_DISPATCH, qs_id) QS_TIME_PRE_(); // time stamp QS_SIG_PRE_(e->sig); // the signal of the event QS_OBJ_PRE_(me); // 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; me->temp.fun = s; do { s = me->temp.fun; r = (*s)(me, 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, qs_id) QS_SIG_PRE_(e->sig); // the signal of the event QS_OBJ_PRE_(me); // 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 } } while (r == Q_RET_SUPER); if (r >= Q_RET_TRAN) { // regular tran. taken? QStateHandler path[QHSM_MAX_NEST_DEPTH_]; path[0] = me->temp.fun; // tran. target path[1] = t; // current state path[2] = s; // tran. source // exit current state to tran. source s... for (; t != s; t = me->temp.fun) { // exit from t if (QHSM_RESERVED_EVT_(t, Q_EXIT_SIG) == Q_RET_HANDLED) { QS_STATE_EXIT_(t, qs_id); // find superstate of t (void)QHSM_RESERVED_EVT_(t, Q_EMPTY_SIG); } } int_fast8_t ip = QHsm_tran_(me, path, qs_id); // 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, qs_id) QS_OBJ_PRE_(me); // this state machine object QS_FUN_PRE_(t); // the source of the tran. 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], qs_id); } } t = path[0]; // stick the target into register me->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, qs_id) QS_OBJ_PRE_(me); // this state machine object QS_FUN_PRE_(t); // the source (pseudo)state QS_FUN_PRE_(me->temp.fun); // the target of the tran. QS_END_PRE_() QS_MEM_APP(); QS_CRIT_EXIT(); ip = 0; path[0] = me->temp.fun; // find superstate (void)QHSM_RESERVED_EVT_(me->temp.fun, Q_EMPTY_SIG); while (me->temp.fun != t) { ++ip; path[ip] = me->temp.fun; // find superstate (void)QHSM_RESERVED_EVT_(me->temp.fun, Q_EMPTY_SIG); } me->temp.fun = path[0]; // entry path must not overflow QF_CRIT_ENTRY(); Q_ASSERT_INCRIT(410, ip < QHSM_MAX_NEST_DEPTH_); QF_CRIT_EXIT(); // 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], qs_id); } --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, qs_id) QS_TIME_PRE_(); // time stamp QS_SIG_PRE_(e->sig); // the signal of the event QS_OBJ_PRE_(me); // 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, qs_id) QS_TIME_PRE_(); // time stamp QS_SIG_PRE_(e->sig); // the signal of the event QS_OBJ_PRE_(me); // 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, qs_id) QS_TIME_PRE_(); // time stamp QS_SIG_PRE_(e->sig); // the signal of the event QS_OBJ_PRE_(me); // this state machine object QS_FUN_PRE_(me->state.fun); // the current state QS_END_PRE_() QS_MEM_APP(); QS_CRIT_EXIT(); } #endif // Q_SPY me->state.fun = t; // change the current active state #ifndef Q_UNSAFE me->temp.uint = ~me->state.uint; #endif //! @private @memberof QHsm //! @private @memberof QHsm return me->state.fun; //! @public @memberof QHsm //! @public @memberof QHsm QF_CRIT_STAT QF_CRIT_ENTRY(); Q_REQUIRE_INCRIT(602, me->super.state.uint == (uintptr_t)(~me->super.temp.uint)); QF_CRIT_EXIT(); bool inState = false; // assume that this HSM is not in 'state' // scan the state hierarchy bottom-up QState r; do { // do the states match? if (me->super.temp.fun == state) { inState = true; // 'true' means that match found r = Q_RET_IGNORED; // cause breaking out of the loop } else { r = QHSM_RESERVED_EVT_(me->super.temp.fun, Q_EMPTY_SIG); } } while (r != Q_RET_IGNORED); // QHsm_top() state not reached #ifndef Q_UNSAFE me->super.temp.uint = ~me->super.state.uint; #endif return inState; // return the status const //! @public @memberof QHsm //! @public @memberof QHsm return me->super.state.fun; //! @public @memberof QHsm //! @public @memberof QHsm QStateHandler child = me->super.state.fun; // start with current state bool isFound = false; // start with the child not found // establish stable state configuration me->super.temp.fun = child; QState r; do { // is this the parent of the current child? if (me->super.temp.fun == parent) { isFound = true; // child is found r = Q_RET_IGNORED; // break out of the loop } else { child = me->super.temp.fun; r = QHSM_RESERVED_EVT_(me->super.temp.fun, Q_EMPTY_SIG); } } while (r != Q_RET_IGNORED); // the top state not reached #ifndef Q_UNSAFE me->super.temp.uint = ~me->super.state.uint; #endif QF_CRIT_STAT QF_CRIT_ENTRY(); Q_ASSERT_INCRIT(890, isFound); QF_CRIT_EXIT(); return child; // return the child //! @private @memberof QHsm //! @private @memberof QHsm #ifndef Q_SPY Q_UNUSED_PAR(qs_id); #endif 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, qs_id); } ip = 0; // enter the target } else { // find superstate of target (void)QHSM_RESERVED_EVT_(t, Q_EMPTY_SIG); t = me->temp.fun; // (b) check source==target->super... if (s == t) { ip = 0; // enter the target } else { // find superstate of src (void)QHSM_RESERVED_EVT_(s, Q_EMPTY_SIG); // (c) check source->super==target->super... if (me->temp.fun == t) { // exit source s if (QHSM_RESERVED_EVT_(s, Q_EXIT_SIG) == Q_RET_HANDLED) { QS_STATE_EXIT_(s, qs_id); } ip = 0; // enter the target } else { // (d) check source->super==target... if (me->temp.fun == path[0]) { // exit source s if (QHSM_RESERVED_EVT_(s, Q_EXIT_SIG) == Q_RET_HANDLED) { QS_STATE_EXIT_(s, qs_id); } } else { // (e) check rest of source==target->super->super.. // and store the entry path along the way 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 = me->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; path[ip] = me->temp.fun; // store the entry path if (me->temp.fun == s) { // is it the source? iq = 1; // indicate that the LCA found // entry path must not overflow QF_CRIT_ENTRY(); Q_ASSERT_INCRIT(510, ip < QHSM_MAX_NEST_DEPTH_); QF_CRIT_EXIT(); --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_(me->temp.fun, Q_EMPTY_SIG); } } // the LCA not found yet? if (iq == 0) { // entry path must not overflow QF_CRIT_ENTRY(); Q_ASSERT_INCRIT(520, ip < QHSM_MAX_NEST_DEPTH_); QF_CRIT_EXIT(); // exit source s if (QHSM_RESERVED_EVT_(s, Q_EXIT_SIG) == Q_RET_HANDLED) { QS_STATE_EXIT_(s, qs_id); } // (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 do { // exit from t if (QHSM_RESERVED_EVT_(t, Q_EXIT_SIG) == Q_RET_HANDLED) { QS_STATE_EXIT_(t, qs_id); // find superstate of t (void)QHSM_RESERVED_EVT_(t, Q_EMPTY_SIG); } t = me->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); } while (r != Q_RET_HANDLED); } } } } } } return ip; const //! @protected @memberof QAsm //! @protected @memberof QAsm Q_UNUSED_PAR(me); Q_UNUSED_PAR(e); return Q_RET_IGNORED; // the top state ignores all events //! @class QMsm //! @extends QAsm Machine-generated State Machine //! @protected @memberof QMsm //! @protected @memberof QMsm static struct QAsmVtable const vtable = { // QAsm virtual table &QMsm_init_, &QMsm_dispatch_ #ifdef Q_SPY ,&QMsm_getStateHandler_ #endif }; // do not call the QAsm_ctor() here me->super.vptr = &vtable; me->super.state.obj = &l_msm_top_s; // the current state (top) me->super.temp.fun = initial; // the initial tran. handler //! @private @memberof QMsm //! @private @memberof QMsm #ifndef Q_SPY Q_UNUSED_PAR(qs_id); #endif QF_CRIT_STAT QF_CRIT_ENTRY(); Q_REQUIRE_INCRIT(200, (me->vptr != (struct QAsmVtable *)0) && (me->temp.fun != Q_STATE_CAST(0)) && (me->state.obj == &l_msm_top_s)); QF_CRIT_EXIT(); // execute the top-most initial tran. QState r = (*me->temp.fun)(me, 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, qs_id) QS_OBJ_PRE_(me); // this state machine object QS_FUN_PRE_(me->state.obj->stateHandler); // source state QS_FUN_PRE_(me->temp.tatbl->target->stateHandler); // target state QS_END_PRE_() QS_MEM_APP(); QF_CRIT_EXIT(); // set state to the last tran. target me->state.obj = me->temp.tatbl->target; // drill down into the state hierarchy with initial transitions... do { // execute the tran. table r = QMsm_execTatbl_(me, me->temp.tatbl, qs_id); } while (r >= Q_RET_TRAN_INIT); QS_CRIT_ENTRY(); QS_MEM_SYS(); QS_BEGIN_PRE_(QS_QEP_INIT_TRAN, qs_id) QS_TIME_PRE_(); // time stamp QS_OBJ_PRE_(me); // this state machine object QS_FUN_PRE_(me->state.obj->stateHandler); // the new current state QS_END_PRE_() QS_MEM_APP(); QS_CRIT_EXIT(); #ifndef Q_UNSAFE me->temp.uint = ~me->state.uint; #endif //! @private @memberof QMsm //! @private @memberof QMsm #ifndef Q_SPY Q_UNUSED_PAR(qs_id); #endif QMState const *s = me->state.obj; // store the current state QMState const *t = s; QF_CRIT_STAT QF_CRIT_ENTRY(); Q_REQUIRE_INCRIT(300, (s != (QMState *)0) && (me->state.uint == (uintptr_t)(~me->temp.uint))); Q_REQUIRE_INCRIT(302, QEvt_verify_(e)); QS_MEM_SYS(); QS_BEGIN_PRE_(QS_QEP_DISPATCH, qs_id) QS_TIME_PRE_(); // time stamp QS_SIG_PRE_(e->sig); // the signal of the event QS_OBJ_PRE_(me); // 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; do { r = (*t->stateHandler)(me, 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 = me->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, qs_id) QS_SIG_PRE_(e->sig); // the signal of the event QS_OBJ_PRE_(me); // 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 } } while (t != (QMState *)0); 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 NULL Q_ASSERT_INCRIT(320, ts != (QMState *)0); QF_CRIT_EXIT(); #endif // Q_SPY do { // save the tran-action table before it gets clobbered struct QMTranActTable const * const tatbl = me->temp.tatbl; union QAsmAttr tmp; // temporary to save intermediate values // was TRAN, TRAN_INIT, or TRAN_EP taken? if (r <= Q_RET_TRAN_EP) { me->temp.obj = (QMState *)0; // clear QMsm_exitToTranSource_(me, s, t, qs_id); r = QMsm_execTatbl_(me, tatbl, qs_id); s = me->state.obj; } // was a tran. segment to history taken? else if (r == Q_RET_TRAN_HIST) { tmp.obj = me->state.obj; // save history me->state.obj = s; // restore the original state QMsm_exitToTranSource_(me, s, t, qs_id); (void)QMsm_execTatbl_(me, tatbl, qs_id); r = QMsm_enterHistory_(me, tmp.obj, qs_id); s = me->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 = me->state.act; // save XP action me->state.obj = s; // restore the original state r = (*tmp.act)(me); // execute the XP action if (r == Q_RET_TRAN) { // XP -> TRAN ? #ifdef Q_SPY tmp.tatbl = me->temp.tatbl; // save me->temp #endif // Q_SPY QMsm_exitToTranSource_(me, s, t, qs_id); // take the tran-to-XP segment inside submachine (void)QMsm_execTatbl_(me, tatbl, qs_id); s = me->state.obj; #ifdef Q_SPY me->temp.tatbl = tmp.tatbl; // restore me->temp #endif // Q_SPY } else if (r == Q_RET_TRAN_HIST) { // XP -> HIST ? tmp.obj = me->state.obj; // save the history me->state.obj = s; // restore the original state s = me->temp.obj; // save me->temp QMsm_exitToTranSource_(me, me->state.obj, t, qs_id); // take the tran-to-XP segment inside submachine (void)QMsm_execTatbl_(me, tatbl, qs_id); #ifdef Q_SPY me->temp.obj = s; // restore me->temp #endif // Q_SPY s = me->state.obj; me->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 } while (r >= Q_RET_TRAN); QS_CRIT_ENTRY(); QS_MEM_SYS(); QS_BEGIN_PRE_(QS_QEP_TRAN, qs_id) QS_TIME_PRE_(); // time stamp QS_SIG_PRE_(e->sig); // the signal of the event QS_OBJ_PRE_(me); // 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(); QS_CRIT_EXIT(); } #ifdef Q_SPY // was the event handled? else if (r == Q_RET_HANDLED) { QF_CRIT_ENTRY(); // internal tran. source can't be NULL Q_ASSERT_INCRIT(340, t != (QMState *)0); QS_MEM_SYS(); QS_BEGIN_PRE_(QS_QEP_INTERN_TRAN, qs_id) QS_TIME_PRE_(); // time stamp QS_SIG_PRE_(e->sig); // the signal of the event QS_OBJ_PRE_(me); // 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 == (QMState *)0) { QS_CRIT_ENTRY(); QS_MEM_SYS(); QS_BEGIN_PRE_(QS_QEP_IGNORED, qs_id) QS_TIME_PRE_(); // time stamp QS_SIG_PRE_(e->sig); // the signal of the event QS_OBJ_PRE_(me); // 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 me->temp.uint = ~me->state.uint; #endif //! @public @memberof QMsm //! @public @memberof QMsm return me->state.obj->stateHandler; const //! @public @memberof QMsm //! @public @memberof QMsm bool inState = false; // assume that this SM is not in 'state' for (QMState const *s = me->super.state.obj; s != (QMState *)0; s = s->superstate) { if (s == stateObj) { inState = true; // match found, return 'true' break; } } return inState; //! @public @memberof QMsm //! @public @memberof QMsm return me->super.state.obj; const //! @public @memberof QMsm //! @public @memberof QMsm QMState const *child = me->super.state.obj; bool isFound = false; // start with the child not found QMState const *s; for (s = me->super.state.obj; s != (QMState *)0; s = s->superstate) { if (s == parent) { isFound = true; // child is found break; } else { child = s; } } if (!isFound) { // still not found? for (s = me->super.temp.obj; s != (QMState *)0; s = s->superstate) { if (s == parent) { isFound = true; // child is found break; } else { child = s; } } } QF_CRIT_STAT QF_CRIT_ENTRY(); Q_ASSERT_INCRIT(890, isFound); QF_CRIT_EXIT(); return child; // return the child //! @private @memberof QMsm //! @private @memberof QMsm #ifndef Q_SPY Q_UNUSED_PAR(qs_id); #endif QF_CRIT_STAT QF_CRIT_ENTRY(); // precondition: // - the tran-action table pointer must not be NULL Q_REQUIRE_INCRIT(400, tatbl != (struct QMTranActTable *)0); QF_CRIT_EXIT(); QState r = Q_RET_NULL; for (QActionHandler const *a = &tatbl->act[0]; *a != Q_ACTION_CAST(0); ++a) { r = (*(*a))(me); // 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, qs_id) QS_OBJ_PRE_(me); // this state machine object QS_FUN_PRE_(me->temp.obj->stateHandler); // entered state QS_END_PRE_() } else if (r == Q_RET_EXIT) { QS_BEGIN_PRE_(QS_QEP_STATE_EXIT, qs_id) QS_OBJ_PRE_(me); // this state machine object QS_FUN_PRE_(me->temp.obj->stateHandler); // exited state QS_END_PRE_() } else if (r == Q_RET_TRAN_INIT) { QS_BEGIN_PRE_(QS_QEP_STATE_INIT, qs_id) QS_OBJ_PRE_(me); // this state machine object QS_FUN_PRE_(tatbl->target->stateHandler); // source QS_FUN_PRE_(me->temp.tatbl->target->stateHandler); // target QS_END_PRE_() } else if (r == Q_RET_TRAN_EP) { QS_BEGIN_PRE_(QS_QEP_TRAN_EP, qs_id) QS_OBJ_PRE_(me); // this state machine object QS_FUN_PRE_(tatbl->target->stateHandler); // source QS_FUN_PRE_(me->temp.tatbl->target->stateHandler); // target QS_END_PRE_() } else if (r == Q_RET_TRAN_XP) { QS_BEGIN_PRE_(QS_QEP_TRAN_XP, qs_id) QS_OBJ_PRE_(me); // this state machine object QS_FUN_PRE_(tatbl->target->stateHandler); // source QS_FUN_PRE_(me->temp.tatbl->target->stateHandler); // target QS_END_PRE_() } else { // empty } QS_MEM_APP(); QS_CRIT_EXIT(); #endif // Q_SPY } me->state.obj = (r >= Q_RET_TRAN) ? me->temp.tatbl->target : tatbl->target; return r; //! @private @memberof QMsm //! @private @memberof QMsm #ifndef Q_SPY Q_UNUSED_PAR(qs_id); #endif QF_CRIT_STAT // exit states from the current state to the tran. source state QMState const *s = cs; while (s != ts) { // exit action provided in state 's'? if (s->exitAction != Q_ACTION_CAST(0)) { // execute the exit action (void)(*s->exitAction)(me); QS_CRIT_ENTRY(); QS_MEM_SYS(); QS_BEGIN_PRE_(QS_QEP_STATE_EXIT, qs_id) QS_OBJ_PRE_(me); // 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 == (QMState *)0) { // reached the top of a submachine? s = me->temp.obj; // the superstate from QM_SM_EXIT() QF_CRIT_ENTRY(); Q_ASSERT_INCRIT(510, s != (QMState *)0); // must be valid QF_CRIT_EXIT(); } } //! @private @memberof QMsm //! @private @memberof QMsm #ifndef Q_SPY Q_UNUSED_PAR(qs_id); #endif QMState const *s = hist; QMState const *ts = me->state.obj; // tran. source QMState const *epath[QMSM_MAX_ENTRY_DEPTH_]; QF_CRIT_STAT QS_CRIT_ENTRY(); QS_MEM_SYS(); QS_BEGIN_PRE_(QS_QEP_TRAN_HIST, qs_id) QS_OBJ_PRE_(me); // 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(); int_fast8_t i = 0; // tran. entry path index while (s != ts) { if (s->entryAction != Q_ACTION_CAST(0)) { QF_CRIT_ENTRY(); Q_ASSERT_INCRIT(620, i < QMSM_MAX_ENTRY_DEPTH_); QF_CRIT_EXIT(); epath[i] = s; ++i; } s = s->superstate; if (s == (QMState *)0) { ts = s; // force exit from the for-loop } } // retrace the entry path in reverse (desired) order... while (i > 0) { --i; (void)(*epath[i]->entryAction)(me); // run entry action in epath[i] QS_CRIT_ENTRY(); QS_MEM_SYS(); QS_BEGIN_PRE_(QS_QEP_STATE_ENTRY, qs_id) QS_OBJ_PRE_(me); QS_FUN_PRE_(epath[i]->stateHandler); // entered state handler QS_END_PRE_() QS_MEM_APP(); QS_CRIT_EXIT(); } me->state.obj = hist; // set current state to the tran. target // initial tran. present? QState r; if (hist->initAction != Q_ACTION_CAST(0)) { r = (*hist->initAction)(me); // execute the tran. action } else { r = Q_RET_NULL; } return r; { (QSignal)(sig_), 0U, QEVT_MARKER } do { \ Q_ASSERT(((QAsm *)(me_))->vptr); \ (*((QAsm *)(me_))->vptr->init)((QAsm *)(me_), (par_), (qs_id_)); \ } while (false) do { \ Q_ASSERT(((QAsm *)(me_))->vptr); \ (*((QAsm *)(me_))->vptr->init)((QAsm *)(me_), (par_), 0); \ } while (false) \ (*((QAsm *)(me_))->vptr->dispatch)((QAsm *)(me_), (e_), (qs_id_)) \ (*((QAsm *)(me_))->vptr->dispatch)((QAsm *)(me_), (e_), 0U) ((QAsm *)(ptr_)) ((QHsm *)(ptr_)) ((QMsm *)(ptr_)) \ ((Q_ASM_UPCAST(me))->temp.fun = Q_STATE_CAST(target_), \ (QState)Q_RET_TRAN) \ ((Q_ASM_UPCAST(me))->temp.fun = (hist_), \ (QState)Q_RET_TRAN_HIST) \ ((Q_ASM_UPCAST(me))->temp.fun = Q_STATE_CAST(super_), \ (QState)Q_RET_SUPER) ((QState)Q_RET_HANDLED) ((QState)Q_RET_UNHANDLED) / ((QActionHandler)0) ((class_ const *)(e)) ((QStateHandler)(handler_)) ((QActionHandler)(action_)) ((void)(par_)) (sizeof(array_) / sizeof((array_)[0U])) ((type_ *)(uint_)) \ ((Q_ASM_UPCAST(me))->temp.obj = (state_), \ (QState)Q_RET_ENTRY) ((QState)Q_RET_ENTRY) \ ((Q_ASM_UPCAST(me))->temp.obj = (state_), \ (QState)Q_RET_EXIT) ((QState)Q_RET_EXIT) \ ((Q_ASM_UPCAST(me))->temp.obj = (state_), \ (QState)Q_RET_EXIT) ((Q_ASM_UPCAST(me))->temp.tatbl \ = (struct QMTranActTable const *)(tatbl_), \ (QState)Q_RET_TRAN) ((Q_ASM_UPCAST(me))->temp.tatbl \ = (struct QMTranActTable const *)(tatbl_), \ (QState)Q_RET_TRAN_INIT) \ ((((Q_ASM_UPCAST(me))->state.obj = (history_)), \ ((Q_ASM_UPCAST(me))->temp.tatbl = \ (struct QMTranActTable const *)(tatbl_))), \ (QState)Q_RET_TRAN_HIST) ((Q_ASM_UPCAST(me))->temp.tatbl \ = (struct QMTranActTable const *)(tatbl_), \ (QState)Q_RET_TRAN_EP) \ ((((Q_ASM_UPCAST(me))->state.act = (xp_)), \ ((Q_ASM_UPCAST(me))->temp.tatbl = \ (struct QMTranActTable const *)(tatbl_))), \ (QState)Q_RET_TRAN_XP) ((QState)Q_RET_HANDLED) ((QState)Q_RET_UNHANDLED) ((QState)Q_RET_SUPER) \ ((Q_ASM_UPCAST(me))->temp.obj = (host_), \ (QState)Q_RET_SUPER_SUB) ((QMState *)0) // @class QF { //! @cond INTERNAL uint8_t dummy; //! @endcond } QV; const static uint8_t const log2LUT[16] = { 0U, 1U, 2U, 2U, 3U, 3U, 3U, 3U, 4U, 4U, 4U, 4U, 4U, 4U, 4U, 4U }; uint_fast8_t n = 0U; QPSetBits tmp; QPSetBits x = bitmask; #if (QF_MAX_ACTIVE > 16U) tmp = (x >> 16U); if (tmp != 0U) { n += 16U; x = tmp; } #endif #if (QF_MAX_ACTIVE > 8U) tmp = (x >> 8U); if (tmp != 0U) { n += 8U; x = tmp; } #endif tmp = (x >> 4U); if (tmp != 0U) { n += 4U; x = tmp; } return n + log2LUT[x]; //! @class QPSet //! @private @memberof QPSet //! @public @memberof QPSet //! @public @memberof QPSet me->bits[0] = 0U; #if (QF_MAX_ACTIVE > 32) me->bits[1] = 0U; #endif const //! @public @memberof QPSet //! @public @memberof QPSet #if (QF_MAX_ACTIVE <= 32U) return (me->bits[0] == 0U); #else return (me->bits[0] == 0U) ? (me->bits[1] == 0U) : false; #endif const //! @public @memberof QPSet //! @public @memberof QPSet #if (QF_MAX_ACTIVE <= 32U) return (me->bits[0] != 0U); #else return (me->bits[0] != 0U) ? true : (me->bits[1] != 0U); #endif const //! @public @memberof QPSet //! @public @memberof QPSet #if (QF_MAX_ACTIVE <= 32U) return (me->bits[0] & ((QPSetBits)1U << (n - 1U))) != 0U; #else return (n <= 32U) ? ((me->bits[0] & ((QPSetBits)1U << (n - 1U))) != 0U) : ((me->bits[1] & ((QPSetBits)1U << (n - 33U))) != 0U); #endif //! @public @memberof QPSet //! @public @memberof QPSet #if (QF_MAX_ACTIVE <= 32U) me->bits[0] = (me->bits[0] | ((QPSetBits)1U << (n - 1U))); #else if (n <= 32U) { me->bits[0] = (me->bits[0] | ((QPSetBits)1U << (n - 1U))); } else { me->bits[1] = (me->bits[1] | ((QPSetBits)1U << (n - 33U))); } #endif //! @public @memberof QPSet //! @public @memberof QPSet #if (QF_MAX_ACTIVE <= 32U) me->bits[0] = (me->bits[0] & (QPSetBits)(~((QPSetBits)1U << (n - 1U)))); #else if (n <= 32U) { (me->bits[0] = (me->bits[0] & ~((QPSetBits)1U << (n - 1U)))); } else { (me->bits[1] = (me->bits[1] & ~((QPSetBits)1U << (n - 33U)))); } #endif const //! @public @memberof QPSet //! @public @memberof QPSet #if (QF_MAX_ACTIVE <= 32U) return QF_LOG2(me->bits[0]); #else return (me->bits[1] != 0U) ? (QF_LOG2(me->bits[1]) + 32U) : (QF_LOG2(me->bits[0])); #endif const //! @private @memberof QPSet //! @private @memberof QPSet dis->bits[0] = ~me->bits[0]; #if (QF_MAX_ACTIVE > 32U) dis->bits[1] = ~me->bits[1]; #endif const //! @private @memberof QPSet //! @private @memberof QPSet #if (QF_MAX_ACTIVE <= 32U) return me->bits[0] == (QPSetBits)(~dis->bits[0]); #else return (me->bits[0] == (QPSetBits)(~dis->bits[0])) && (me->bits[1] == (QPSetBits)(~dis->bits[1])); #endif // @struct QSubscrList //! @private @memberof QSubscrList //! @private @memberof QSubscrList //! @class QActive //! @extends QAsm //! @private @memberof QActive //! @private @memberof QActive //! @private @memberof QActive //! @private @memberof QActive //! @private @memberof QActive //! @static @private @memberof QActive //! @static @private @memberof QActive //! @static @private @memberof QActive //! @protected @memberof QActive //! @protected @memberof QActive // clear the whole QActive object, 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). QF_bzero_(me, sizeof(*me)); // NOTE: QActive inherits the abstract QAsm class, but it calls the // constructor of the QHsm subclass. This is because QActive inherits // the behavior from the QHsm subclass. QHsm_ctor((QHsm *)(me), initial); // NOTE: this vtable is identical as QHsm, but is provided // for the QActive subclass to provide a UNIQUE vptr to distinguish // subclasses of QActive (e.g., in the debugger). static struct QAsmVtable const vtable = { // QActive virtual table &QHsm_init_, &QHsm_dispatch_ #ifdef Q_SPY ,&QHsm_getStateHandler_ #endif }; me->super.vptr = &vtable; // hook vptr to QActive vtable //! @public @memberof QActive //! @public @memberof QActive //! @private @memberof QActive //! @private @memberof QActive //! @protected @memberof QActive //! @protected @memberof QActive //! @private @memberof QActive //! @private @memberof QActive QF_CRIT_STAT QF_CRIT_ENTRY(); QF_MEM_SYS(); if (me->pthre == 0U) { // preemption-threshold not defined? me->pthre = me->prio; // apply the default } #ifndef Q_UNSAFE Q_REQUIRE_INCRIT(100, (0U < me->prio) && (me->prio <= QF_MAX_ACTIVE) && (QActive_registry_[me->prio] == (QActive *)0) && (me->prio <= me->pthre)); uint8_t prev_thre = me->pthre; uint8_t next_thre = me->pthre; uint_fast8_t p; for (p = (uint_fast8_t)me->prio - 1U; p > 0U; --p) { if (QActive_registry_[p] != (QActive *)0) { prev_thre = QActive_registry_[p]->pthre; break; } } for (p = (uint_fast8_t)me->prio + 1U; p <= QF_MAX_ACTIVE; ++p) { if (QActive_registry_[p] != (QActive *)0) { next_thre = QActive_registry_[p]->pthre; break; } } Q_ASSERT_INCRIT(190, (prev_thre <= me->pthre) && (me->pthre <= next_thre)); #endif // Q_UNSAFE // register the AO at the QF-prio. QActive_registry_[me->prio] = me; QF_MEM_APP(); QF_CRIT_EXIT(); //! @private @memberof QActive //! @private @memberof QActive uint_fast8_t const p = (uint_fast8_t)me->prio; QF_CRIT_STAT QF_CRIT_ENTRY(); QF_MEM_SYS(); Q_REQUIRE_INCRIT(200, (0U < p) && (p <= QF_MAX_ACTIVE) && (QActive_registry_[p] == me)); QActive_registry_[p] = (QActive *)0; // free-up the prio. level me->super.state.fun = Q_STATE_CAST(0); // invalidate the state QF_MEM_APP(); QF_CRIT_EXIT(); //! @private @memberof QActive //! @private @memberof QActive #ifndef Q_SPY Q_UNUSED_PAR(sender); #endif #ifdef Q_UTEST // test? #if Q_UTEST != 0 // testing QP-stub? if (me->super.temp.fun == Q_STATE_CAST(0)) { // QActiveDummy? return QActiveDummy_fakePost_(me, e, margin, sender); } #endif #endif QF_CRIT_STAT QF_CRIT_ENTRY(); QF_MEM_SYS(); Q_REQUIRE_INCRIT(102, QEvt_verify_(e)); QEQueueCtr nFree = me->eQueue.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 > (QEQueueCtr)margin) { status = true; // can post } else { status = false; // cannot post, but don't assert } // is it a mutable event? if (QEvt_getPoolId_(e) != 0U) { QEvt_refCtr_inc_(e); // increment the reference counter } if (status) { // can post the event? --nFree; // one free entry just used up me->eQueue.nFree = nFree; // update the original if (me->eQueue.nMin > nFree) { me->eQueue.nMin = nFree; // increase minimum so far } QS_BEGIN_PRE_(QS_QF_ACTIVE_POST, me->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_(me); // this active object (recipient) QS_2U8_PRE_(QEvt_getPoolId_(e), e->refCtr_); // poolId & refCtr QS_EQC_PRE_(nFree); // # free entries QS_EQC_PRE_(me->eQueue.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 ('me->prio') is set if (QS_LOC_CHECK_(me->prio)) { QS_onTestPost(sender, me, e, status); } #endif if (me->eQueue.frontEvt == (QEvt *)0) { // empty queue? me->eQueue.frontEvt = e; // deliver event directly #ifdef QXK_H_ if (me->super.state.act == Q_ACTION_CAST(0)) { // eXtended? QXTHREAD_EQUEUE_SIGNAL_(me); // signal the event queue } else { QACTIVE_EQUEUE_SIGNAL_(me); // signal the event queue } #else QACTIVE_EQUEUE_SIGNAL_(me); // signal the event queue #endif } // queue is not empty, insert event into the ring-buffer else { // insert event into the ring buffer (FIFO) me->eQueue.ring[me->eQueue.head] = e; if (me->eQueue.head == 0U) { // need to wrap head? me->eQueue.head = me->eQueue.end; // wrap around } --me->eQueue.head; // advance the head (counter clockwise) } QF_MEM_APP(); QF_CRIT_EXIT(); } else { // cannot post the event QS_BEGIN_PRE_(QS_QF_ACTIVE_POST_ATTEMPT, me->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_(me); // this active object (recipient) QS_2U8_PRE_(QEvt_getPoolId_(e), e->refCtr_); // poolId & 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 ('me->prio') is set if (QS_LOC_CHECK_(me->prio)) { QS_onTestPost(sender, me, 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; //! @private @memberof QActive //! @private @memberof QActive #ifdef Q_UTEST // test? #if Q_UTEST != 0 // testing QP-stub? if (me->super.temp.fun == Q_STATE_CAST(0)) { // QActiveDummy? QActiveDummy_fakePostLIFO_(me, e); return; } #endif #endif QF_CRIT_STAT QF_CRIT_ENTRY(); QF_MEM_SYS(); Q_REQUIRE_INCRIT(202, QEvt_verify_(e)); #ifdef QXK_H_ Q_REQUIRE_INCRIT(200, me->super.state.act != Q_ACTION_CAST(0)); #endif QEQueueCtr nFree = me->eQueue.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 (QEvt_getPoolId_(e) != 0U) { // is it a mutable event? QEvt_refCtr_inc_(e); // increment the reference counter } --nFree; // one free entry just used up me->eQueue.nFree = nFree; // update the original if (me->eQueue.nMin > nFree) { me->eQueue.nMin = nFree; // update minimum so far } QS_BEGIN_PRE_(QS_QF_ACTIVE_POST_LIFO, me->prio) QS_TIME_PRE_(); // timestamp QS_SIG_PRE_(e->sig); // the signal of this event QS_OBJ_PRE_(me); // this active object QS_2U8_PRE_(QEvt_getPoolId_(e), e->refCtr_);// poolId & refCtr QS_EQC_PRE_(nFree); // # free entries QS_EQC_PRE_(me->eQueue.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 ('me->prio') is set if (QS_LOC_CHECK_(me->prio)) { QS_onTestPost((QActive *)0, me, e, true); } #endif QEvt const * const frontEvt = me->eQueue.frontEvt; me->eQueue.frontEvt = e; // deliver the event directly to the front if (frontEvt == (QEvt *)0) { // was the queue empty? QACTIVE_EQUEUE_SIGNAL_(me); // signal the event queue } else { // queue was not empty, leave the event in the ring-buffer ++me->eQueue.tail; if (me->eQueue.tail == me->eQueue.end) { // need to wrap the tail? me->eQueue.tail = 0U; // wrap around } me->eQueue.ring[me->eQueue.tail] = frontEvt; } QF_MEM_APP(); QF_CRIT_EXIT(); //! @private @memberof QActive //! @private @memberof QActive QF_CRIT_STAT QF_CRIT_ENTRY(); QF_MEM_SYS(); QACTIVE_EQUEUE_WAIT_(me); // wait for event to arrive directly // always remove event from the front QEvt const * const e = me->eQueue.frontEvt; QEQueueCtr const nFree = me->eQueue.nFree + 1U; // get volatile into tmp me->eQueue.nFree = nFree; // update the # free if (nFree <= me->eQueue.end) { // any events in the ring buffer? // remove event from the tail me->eQueue.frontEvt = me->eQueue.ring[me->eQueue.tail]; if (me->eQueue.tail == 0U) { // need to wrap the tail? me->eQueue.tail = me->eQueue.end; // wrap around } --me->eQueue.tail; QS_BEGIN_PRE_(QS_QF_ACTIVE_GET, me->prio) QS_TIME_PRE_(); // timestamp QS_SIG_PRE_(e->sig); // the signal of this event QS_OBJ_PRE_(me); // this active object QS_2U8_PRE_(QEvt_getPoolId_(e), e->refCtr_); // poolId & refCtr QS_EQC_PRE_(nFree); // # free entries QS_END_PRE_() } else { me->eQueue.frontEvt = (QEvt *)0; // queue becomes empty // all entries in the queue must be free (+1 for fronEvt) Q_ASSERT_INCRIT(310, nFree == (me->eQueue.end + 1U)); QS_BEGIN_PRE_(QS_QF_ACTIVE_GET_LAST, me->prio) QS_TIME_PRE_(); // timestamp QS_SIG_PRE_(e->sig); // the signal of this event QS_OBJ_PRE_(me); // this active object QS_2U8_PRE_(QEvt_getPoolId_(e), e->refCtr_); // poolId & refCtr QS_END_PRE_() } QF_MEM_APP(); QF_CRIT_EXIT(); return e; //! @static @public @memberof QActive //! @static @public @memberof QActive QActive_subscrList_ = subscrSto; QActive_maxPubSignal_ = maxSignal; // initialize the subscriber list for (enum_t sig = 0; sig < maxSignal; ++sig) { QPSet_setEmpty(&subscrSto[sig].set); #ifndef Q_UNSAFE QPSet_update_(&subscrSto[sig].set, &subscrSto[sig].set_dis); #endif } //! @static @private @memberof QActive //! @static @private @memberof QActive #ifndef Q_SPY Q_UNUSED_PAR(sender); Q_UNUSED_PAR(qs_id); #endif QSignal const sig = e->sig; QF_CRIT_STAT QF_CRIT_ENTRY(); QF_MEM_SYS(); Q_REQUIRE_INCRIT(200, sig < (QSignal)QActive_maxPubSignal_); Q_REQUIRE_INCRIT(202, QPSet_verify_(&QActive_subscrList_[sig].set, &QActive_subscrList_[sig].set_dis)); QS_BEGIN_PRE_(QS_QF_PUBLISH, qs_id) QS_TIME_PRE_(); // the timestamp QS_OBJ_PRE_(sender); // the sender object QS_SIG_PRE_(sig); // the signal of the event QS_2U8_PRE_(QEvt_getPoolId_(e), e->refCtr_); // poolId & refCtr QS_END_PRE_() // is it a mutable event? if (QEvt_getPoolId_(e) != 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 = QActive_subscrList_[sig].set; QF_MEM_APP(); QF_CRIT_EXIT(); if (QPSet_notEmpty(&subscrSet)) { // any subscribers? // highest-prio subscriber uint_fast8_t p = QPSet_findMax(&subscrSet); QF_CRIT_ENTRY(); QF_MEM_SYS(); QActive *a = QActive_registry_[p]; // the AO must be registered with the framework Q_ASSERT_INCRIT(210, a != (QActive *)0); QF_MEM_APP(); QF_CRIT_EXIT(); QF_SCHED_STAT_ QF_SCHED_LOCK_(p); // lock the scheduler up to AO's prio do { // loop over all subscribers // QACTIVE_POST() asserts internally if the queue overflows QACTIVE_POST(a, e, sender); QPSet_remove(&subscrSet, p); // remove the handled subscriber if (QPSet_notEmpty(&subscrSet)) { // still more subscribers? p = QPSet_findMax(&subscrSet); // highest-prio subscriber QF_CRIT_ENTRY(); QF_MEM_SYS(); a = QActive_registry_[p]; // the AO must be registered with the framework Q_ASSERT_INCRIT(220, a != (QActive *)0); QF_MEM_APP(); QF_CRIT_EXIT(); } else { p = 0U; // no more subscribers } } while (p != 0U); 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); // recycle the event to avoid a leak #endif const //! @protected @memberof QActive //! @protected @memberof QActive uint_fast8_t const p = (uint_fast8_t)me->prio; QF_CRIT_STAT QF_CRIT_ENTRY(); QF_MEM_SYS(); Q_REQUIRE_INCRIT(300, ((enum_t)Q_USER_SIG <= sig) && (sig < QActive_maxPubSignal_) && (0U < p) && (p <= QF_MAX_ACTIVE) && (QActive_registry_[p] == me)); Q_REQUIRE_INCRIT(302, QPSet_verify_(&QActive_subscrList_[sig].set, &QActive_subscrList_[sig].set_dis)); QS_BEGIN_PRE_(QS_QF_ACTIVE_SUBSCRIBE, p) QS_TIME_PRE_(); // timestamp QS_SIG_PRE_(sig); // the signal of this event QS_OBJ_PRE_(me); // this active object QS_END_PRE_() // insert the prio. into the subscriber set QPSet_insert(&QActive_subscrList_[sig].set, p); #ifndef Q_UNSAFE QPSet_update_(&QActive_subscrList_[sig].set, &QActive_subscrList_[sig].set_dis); #endif QF_MEM_APP(); QF_CRIT_EXIT(); const //! @protected @memberof QActive //! @protected @memberof QActive uint_fast8_t const p = (uint_fast8_t)me->prio; QF_CRIT_STAT QF_CRIT_ENTRY(); QF_MEM_SYS(); Q_REQUIRE_INCRIT(400, ((enum_t)Q_USER_SIG <= sig) && (sig < QActive_maxPubSignal_) && (0U < p) && (p <= QF_MAX_ACTIVE) && (QActive_registry_[p] == me)); Q_REQUIRE_INCRIT(402, QPSet_verify_(&QActive_subscrList_[sig].set, &QActive_subscrList_[sig].set_dis)); QS_BEGIN_PRE_(QS_QF_ACTIVE_UNSUBSCRIBE, p) QS_TIME_PRE_(); // timestamp QS_SIG_PRE_(sig); // the signal of this event QS_OBJ_PRE_(me); // this active object QS_END_PRE_() // remove the prio. from the subscriber set QPSet_remove(&QActive_subscrList_[sig].set, p); #ifndef Q_UNSAFE QPSet_update_(&QActive_subscrList_[sig].set, &QActive_subscrList_[sig].set_dis); #endif QF_MEM_APP(); QF_CRIT_EXIT(); const //! @protected @memberof QActive //! @protected @memberof QActive QF_CRIT_STAT QF_CRIT_ENTRY(); QF_MEM_SYS(); uint_fast8_t const p = (uint_fast8_t)me->prio; Q_REQUIRE_INCRIT(500, (0U < p) && (p <= QF_MAX_ACTIVE) && (QActive_registry_[p] == me)); enum_t const maxPubSig = QActive_maxPubSignal_; QF_MEM_APP(); QF_CRIT_EXIT(); for (enum_t sig = (enum_t)Q_USER_SIG; sig < maxPubSig; ++sig) { QF_CRIT_ENTRY(); QF_MEM_SYS(); if (QPSet_hasElement(&QActive_subscrList_[sig].set, p)) { QPSet_remove(&QActive_subscrList_[sig].set, p); #ifndef Q_UNSAFE QPSet_update_(&QActive_subscrList_[sig].set, &QActive_subscrList_[sig].set_dis); #endif QS_BEGIN_PRE_(QS_QF_ACTIVE_UNSUBSCRIBE, p) QS_TIME_PRE_(); // timestamp QS_SIG_PRE_(sig); // the signal of this event QS_OBJ_PRE_(me); // this active object QS_END_PRE_() } QF_MEM_APP(); QF_CRIT_EXIT(); QF_CRIT_EXIT_NOP(); // prevent merging critical sections } const //! @protected @memberof QActive //! @protected @memberof QActive bool const status = QEQueue_post(eq, e, 0U, me->prio); QS_CRIT_STAT QS_CRIT_ENTRY(); QS_MEM_SYS(); QS_BEGIN_PRE_(QS_QF_ACTIVE_DEFER, me->prio) QS_TIME_PRE_(); // time stamp QS_OBJ_PRE_(me); // this active object QS_OBJ_PRE_(eq); // the deferred queue QS_SIG_PRE_(e->sig); // the signal of the event QS_2U8_PRE_(QEvt_getPoolId_(e), e->refCtr_); // poolId & refCtr QS_END_PRE_() QS_MEM_APP(); QS_CRIT_EXIT(); return status; //! @protected @memberof QActive //! @protected @memberof QActive QEvt const * const e = QEQueue_get(eq, me->prio); QF_CRIT_STAT bool recalled; if (e != (QEvt *)0) { // event available? QACTIVE_POST_LIFO(me, e); // post it to the front of the AO's queue QF_CRIT_ENTRY(); QF_MEM_SYS(); if (QEvt_getPoolId_(e) != 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, me->prio) QS_TIME_PRE_(); // time stamp QS_OBJ_PRE_(me); // this active object QS_OBJ_PRE_(eq); // the deferred queue QS_SIG_PRE_(e->sig); // the signal of the event QS_2U8_PRE_(QEvt_getPoolId_(e), e->refCtr_); // poolId & 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, me->prio) QS_TIME_PRE_(); // time stamp QS_OBJ_PRE_(me); // this active object QS_OBJ_PRE_(eq); // the deferred queue QS_END_PRE_() QS_MEM_APP(); QS_CRIT_EXIT(); recalled = false; } return recalled; const //! @protected @memberof QActive //! @protected @memberof QActive uint_fast16_t n = 0U; for (QEvt const *e = QEQueue_get(eq, me->prio); e != (QEvt *)0; e = QEQueue_get(eq, me->prio)) { ++n; // count the flushed event #if (QF_MAX_EPOOL > 0U) QF_gc(e); // garbage collect #endif } return n; //! @private @memberof QActive //! @private @memberof QActive //! @class QMActive //! @extends QActive //! @protected @memberof QMActive //! @protected @memberof QMActive // clear the whole QMActive object, 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). QF_bzero_(me, sizeof(*me)); // NOTE: QActive inherits the QActvie class, but it calls the // constructor of the QMsm subclass. This is because QMActive inherits // the behavior from the QMsm subclass. QMsm_ctor((QMsm *)(me), initial); // NOTE: this vtable is identical as QMsm, but is provided // for the QMActive subclass to provide a UNIQUE vptr to distinguish // subclasses of QActive (e.g., in the debugger). static struct QAsmVtable const vtable = { // QMActive virtual table &QMsm_init_, &QMsm_dispatch_ #ifdef Q_SPY ,&QMsm_getStateHandler_ #endif }; me->super.super.vptr = &vtable; // hook vptr to QMActive vtable //! @class QTimeEvt // @extends QEvt //! @private @memberof QTimeEvt //! @private @memberof QTimeEvt //! @private @memberof QTimeEvt //! @private @memberof QTimeEvt //! @static @private @memberof QTimeEvt //! @public @memberof QTimeEvt //! @public @memberof QTimeEvt me->next = (QTimeEvt *)0; me->act = act; me->ctr = 0U; me->interval = 0U; QF_CRIT_STAT QF_CRIT_ENTRY(); Q_REQUIRE_INCRIT(300, (sig != 0) && (tickRate < QF_MAX_TICK_RATE)); QF_CRIT_EXIT(); // This default event constructor initializes the event // as NOT allocated from any event-pool, which must be // the case for Time Events. (void)QEvt_ctor(&me->super, sig); // The refCtr_ attribute is not used in time events, so it is // reused to hold the tickRate as well as other information me->super.refCtr_ = (uint8_t)tickRate; //! @public @memberof QTimeEvt //! @public @memberof QTimeEvt uint_fast8_t const tickRate = ((uint_fast8_t)me->super.refCtr_ & QTE_TICK_RATE); QTimeEvtCtr const ctr = me->ctr; #ifdef Q_SPY uint_fast8_t const qs_id = ((QActive *)(me->act))->prio; #endif QF_CRIT_STAT QF_CRIT_ENTRY(); QF_MEM_SYS(); Q_REQUIRE_INCRIT(400, (me->act != (void *)0) && (ctr == 0U) && (nTicks != 0U) && (tickRate < (uint_fast8_t)QF_MAX_TICK_RATE) && (me->super.sig >= (QSignal)Q_USER_SIG)); #ifdef Q_UNSAFE Q_UNUSED_PAR(ctr); #endif me->ctr = nTicks; me->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 QTimeEvt_tick_(). if ((me->super.refCtr_ & QTE_IS_LINKED) == 0U) { // mark as linked me->super.refCtr_ |= QTE_IS_LINKED; // The time event is initially inserted into the separate // "freshly armed" link list based on QTimeEvt_timeEvtHead_[tickRate].act. // Only later, inside the QTimeEvt_tick_() function, the "freshly armed" // list is appended to the main list of armed time events based on // QTimeEvt_timeEvtHead_[tickRate].next. Again, this is to keep any // changes to the main list exclusively inside the QTimeEvt_tick_(). me->next = (QTimeEvt *)QTimeEvt_timeEvtHead_[tickRate].act; QTimeEvt_timeEvtHead_[tickRate].act = me; } QS_BEGIN_PRE_(QS_QF_TIMEEVT_ARM, qs_id) QS_TIME_PRE_(); // timestamp QS_OBJ_PRE_(me); // this time event object QS_OBJ_PRE_(me->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(); //! @public @memberof QTimeEvt //! @public @memberof QTimeEvt #ifdef Q_SPY uint_fast8_t const qs_id = QACTIVE_CAST_(me->act)->prio; #endif QF_CRIT_STAT QF_CRIT_ENTRY(); QF_MEM_SYS(); // is the time event actually armed? bool wasArmed; if (me->ctr != 0U) { wasArmed = true; me->super.refCtr_ |= QTE_WAS_DISARMED; QS_BEGIN_PRE_(QS_QF_TIMEEVT_DISARM, qs_id) QS_TIME_PRE_(); // timestamp QS_OBJ_PRE_(me); // this time event object QS_OBJ_PRE_(me->act); // the target AO QS_TEC_PRE_(me->ctr); // the # ticks QS_TEC_PRE_(me->interval); // the interval QS_U8_PRE_(me->super.refCtr_ & QTE_TICK_RATE); // tick rate QS_END_PRE_() me->ctr = 0U; // schedule removal from the list } else { // the time event was already disarmed automatically wasArmed = false; me->super.refCtr_ &= (uint8_t)(~QTE_WAS_DISARMED & 0xFFU); QS_BEGIN_PRE_(QS_QF_TIMEEVT_DISARM_ATTEMPT, qs_id) QS_TIME_PRE_(); // timestamp QS_OBJ_PRE_(me); // this time event object QS_OBJ_PRE_(me->act); // the target AO QS_U8_PRE_(me->super.refCtr_ & QTE_TICK_RATE); // tick rate QS_END_PRE_() } QF_MEM_APP(); QF_CRIT_EXIT(); return wasArmed; //! @public @memberof QTimeEvt //! @public @memberof QTimeEvt uint_fast8_t const tickRate = (uint_fast8_t)me->super.refCtr_ & QTE_TICK_RATE; #ifdef Q_SPY uint_fast8_t const qs_id = ((QActive *)(me->act))->prio; #endif QF_CRIT_STAT QF_CRIT_ENTRY(); QF_MEM_SYS(); Q_REQUIRE_INCRIT(600, (me->act != (void *)0) && (tickRate < QF_MAX_TICK_RATE) && (nTicks != 0U) && (me->super.sig >= (QSignal)Q_USER_SIG)); // is the time evt not running? bool wasArmed; if (me->ctr == 0U) { wasArmed = false; // 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 unlinking is performed exclusively in the // QTimeEvt_tick_() function. // is the time event unlinked? if ((me->super.refCtr_ & QTE_IS_LINKED) == 0U) { // mark as linked me->super.refCtr_ |= QTE_IS_LINKED; // The time event is initially inserted into the separate // "freshly armed" list based on QTimeEvt_timeEvtHead_[tickRate].act. // Only later, inside the QTimeEvt_tick_() function, the "freshly // armed" list is appended to the main list of armed time events // based on QTimeEvt_timeEvtHead_[tickRate].next. Again, this is // to keep any changes to the main list exclusively inside the // QTimeEvt_tick_(). me->next = (QTimeEvt *)QTimeEvt_timeEvtHead_[tickRate].act; QTimeEvt_timeEvtHead_[tickRate].act = me; } } else { // the time event was armed wasArmed = true; } me->ctr = nTicks; // re-load the tick counter (shift the phasing) QS_BEGIN_PRE_(QS_QF_TIMEEVT_REARM, qs_id) QS_TIME_PRE_(); // timestamp QS_OBJ_PRE_(me); // this time event object QS_OBJ_PRE_(me->act); // the target AO QS_TEC_PRE_(me->ctr); // the # ticks QS_TEC_PRE_(me->interval); // the interval QS_2U8_PRE_(tickRate, (wasArmed ? 1U : 0U)); QS_END_PRE_() QF_MEM_APP(); QF_CRIT_EXIT(); return wasArmed; //! @public @memberof QTimeEvt //! @public @memberof QTimeEvt QF_CRIT_STAT QF_CRIT_ENTRY(); QF_MEM_SYS(); uint8_t const wasDisarmed = (me->super.refCtr_ & QTE_WAS_DISARMED); me->super.refCtr_ |= QTE_WAS_DISARMED; // mark as disarmed QF_MEM_APP(); QF_CRIT_EXIT(); return wasDisarmed != 0U; const //! @public @memberof QTimeEvt //! @public @memberof QTimeEvt QF_CRIT_STAT QF_CRIT_ENTRY(); QTimeEvtCtr const ctr = me->ctr; QF_CRIT_EXIT(); return ctr; //! @static @private @memberof QTimeEvt //! @static @private @memberof QTimeEvt #ifndef Q_SPY Q_UNUSED_PAR(sender); #endif QTimeEvt *prev = &QTimeEvt_timeEvtHead_[tickRate]; QF_CRIT_STAT QF_CRIT_ENTRY(); QF_MEM_SYS(); QS_BEGIN_PRE_(QS_QF_TICK, 0U) ++prev->ctr; QS_TEC_PRE_(prev->ctr); // tick ctr QS_U8_PRE_(tickRate); // tick rate QS_END_PRE_() // scan the linked-list of time events at this rate... for (;;) { QTimeEvt *t = prev->next; // advance down the time evt. list if (t == (QTimeEvt *)0) { // end of the list? // any new time events armed since the last QTimeEvt_tick_()? if (QTimeEvt_timeEvtHead_[tickRate].act != (void *)0) { // sanity check Q_ASSERT_INCRIT(110, prev != (QTimeEvt *)0); prev->next = (QTimeEvt *)QTimeEvt_timeEvtHead_[tickRate].act; QTimeEvt_timeEvtHead_[tickRate].act = (void *)0; t = prev->next; // switch to the new list } else { break; // all currently armed time evts. processed } } if (t->ctr == 0U) { // time event scheduled for removal? prev->next = t->next; // mark time event 't' as NOT linked t->super.refCtr_ &= (uint8_t)(~QTE_IS_LINKED & 0xFFU); // 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 { --t->ctr; if (t->ctr == 0U) { // is time event about to expire? QActive * const act = (QActive *)t->act; if (t->interval != 0U) { // periodic time evt? t->ctr = t->interval; // rearm the time event prev = t; // advance to this time event } else { // one-shot time event: automatically disarm prev->next = t->next; // mark time event 't' as NOT linked t->super.refCtr_ &= (uint8_t)(~QTE_IS_LINKED & 0xFFU); // do NOT advance the prev pointer QS_BEGIN_PRE_(QS_QF_TIMEEVT_AUTO_DISARM, act->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->prio) QS_TIME_PRE_(); // timestamp QS_OBJ_PRE_(t); // the time event object QS_SIG_PRE_(t->super.sig); // signal of this time event QS_OBJ_PRE_(act); // the target AO QS_U8_PRE_(tickRate); // tick rate QS_END_PRE_() #ifdef QXK_H_ if (t->super.sig < Q_USER_SIG) { QXThread_timeout_(act); } else { QF_MEM_APP(); QF_CRIT_EXIT(); // exit crit. section before posting // QACTIVE_POST() asserts if the queue overflows QACTIVE_POST(act, &t->super, sender); } #else QF_MEM_APP(); QF_CRIT_EXIT(); // exit crit. section before posting // QACTIVE_POST() asserts if the queue overflows QACTIVE_POST(act, &t->super, sender); #endif } else { prev = t; // 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(); } QF_MEM_APP(); QF_CRIT_EXIT(); //! @static @private @memberof QTimeEvt //! @static @private @memberof QTimeEvt //! @static @public @memberof QTimeEvt //! @static @public @memberof QTimeEvt QF_CRIT_STAT QF_CRIT_ENTRY(); Q_REQUIRE_INCRIT(800, tickRate < QF_MAX_TICK_RATE); QF_CRIT_EXIT(); bool inactive; if (QTimeEvt_timeEvtHead_[tickRate].next != (QTimeEvt *)0) { inactive = false; } else if ((QTimeEvt_timeEvtHead_[tickRate].act != (void *)0)) { inactive = false; } else { inactive = true; } return inactive; //! @class QTicker // @extends QActive //! @public @memberof QTicker //! @public @memberof QTicker QActive_ctor(&me->super, Q_STATE_CAST(0)); // superclass' ctor static struct QAsmVtable const vtable = { // QTicker virtual table &QTicker_init_, &QTicker_dispatch_ #ifdef Q_SPY ,&QHsm_getStateHandler_ #endif }; me->super.super.vptr = &vtable; // hook the vptr // reuse eQueue.head for tick-rate me->super.eQueue.head = (QEQueueCtr)tickRate; //! @private @memberof QTicker //! @private @memberof QTicker Q_UNUSED_PAR(me); Q_UNUSED_PAR(par); Q_UNUSED_PAR(qs_id); QF_CRIT_STAT QF_CRIT_ENTRY(); QF_MEM_SYS(); QACTIVE_CAST_(me)->eQueue.tail = 0U; QF_MEM_APP(); QF_CRIT_EXIT(); //! @private @memberof QTicker //! @private @memberof QTicker Q_UNUSED_PAR(e); Q_UNUSED_PAR(qs_id); QF_CRIT_STAT QF_CRIT_ENTRY(); QF_MEM_SYS(); QEQueueCtr nTicks = QACTIVE_CAST_(me)->eQueue.tail; // save # of ticks QACTIVE_CAST_(me)->eQueue.tail = 0U; // clear # ticks QF_MEM_APP(); QF_CRIT_EXIT(); for (; nTicks > 0U; --nTicks) { QTimeEvt_tick_((uint_fast8_t)QACTIVE_CAST_(me)->eQueue.head, me); } //! @private @memberof QTicker //! @private @memberof QTicker #ifndef Q_SPY Q_UNUSED_PAR(sender); #endif QF_CRIT_STAT QF_CRIT_ENTRY(); QF_MEM_SYS(); if (me->eQueue.frontEvt == (QEvt *)0) { static QEvt const tickEvt = QEVT_INITIALIZER(0); me->eQueue.frontEvt = &tickEvt; // deliver event directly --me->eQueue.nFree; // one less free event QACTIVE_EQUEUE_SIGNAL_(me); // signal the event queue } ++me->eQueue.tail; // account for one more tick event QS_BEGIN_PRE_(QS_QF_ACTIVE_POST, me->prio) QS_TIME_PRE_(); // timestamp QS_OBJ_PRE_(sender); // the sender object QS_SIG_PRE_(0U); // the signal of the event QS_OBJ_PRE_(me); // this active object QS_2U8_PRE_(0U, 0U); // poolId & refCtr QS_EQC_PRE_(0U); // # free entries QS_EQC_PRE_(0U); // min # free entries QS_END_PRE_() QF_MEM_APP(); QF_CRIT_EXIT(); //! @class QEQueue //! @private @memberof QEQueue //! @private @memberof QEQueue //! @private @memberof QEQueue //! @private @memberof QEQueue //! @private @memberof QEQueue //! @private @memberof QEQueue //! @private @memberof QEQueue dummy static member to force generation of 'struct QEQueue' //! @public @memberof QEQueue //! @public @memberof QEQueue me->frontEvt = (QEvt *)0; // no events in the queue me->ring = qSto; // the beginning of the ring buffer me->end = (QEQueueCtr)qLen; if (qLen != 0U) { me->head = 0U; me->tail = 0U; } me->nFree = (QEQueueCtr)(qLen + 1U); // +1 for frontEvt me->nMin = me->nFree; //! @public @memberof QEQueue //! @public @memberof QEQueue #ifndef Q_SPY Q_UNUSED_PAR(qs_id); #endif QF_CRIT_STAT QF_CRIT_ENTRY(); QF_MEM_SYS(); Q_REQUIRE_INCRIT(200, e != (QEvt *)0); QEQueueCtr nFree = me->nFree; // get volatile into temporary // required margin available? bool status; if (((margin == QF_NO_MARGIN) && (nFree > 0U)) || (nFree > (QEQueueCtr)margin)) { // is it a mutable event? if (QEvt_getPoolId_(e) != 0U) { QEvt_refCtr_inc_(e); // increment the reference counter } --nFree; // one free entry just used up me->nFree = nFree; // update the original if (me->nMin > nFree) { me->nMin = nFree; // update minimum so far } QS_BEGIN_PRE_(QS_QF_EQUEUE_POST, qs_id) QS_TIME_PRE_(); // timestamp QS_SIG_PRE_(e->sig); // the signal of this event QS_OBJ_PRE_(me); // this queue object QS_2U8_PRE_(QEvt_getPoolId_(e), e->refCtr_); // poolId & refCtr QS_EQC_PRE_(nFree); // # free entries QS_EQC_PRE_(me->nMin); // min # free entries QS_END_PRE_() if (me->frontEvt == (QEvt *)0) { // was the queue empty? me->frontEvt = e; // deliver event directly } else { // queue was not empty, insert event into the ring-buffer // insert event into the ring buffer (FIFO)... me->ring[me->head] = e; // insert e into buffer // need to wrap the head? if (me->head == 0U) { me->head = me->end; // wrap around } --me->head; } 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, qs_id) QS_TIME_PRE_(); // timestamp QS_SIG_PRE_(e->sig); // the signal of this event QS_OBJ_PRE_(me); // this queue object QS_2U8_PRE_(QEvt_getPoolId_(e), e->refCtr_); // poolId & 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; //! @public @memberof QEQueue //! @public @memberof QEQueue #ifndef Q_SPY Q_UNUSED_PAR(qs_id); #endif QF_CRIT_STAT QF_CRIT_ENTRY(); QF_MEM_SYS(); QEQueueCtr nFree = me->nFree; // get volatile into temporary Q_REQUIRE_INCRIT(300, nFree != 0U); if (QEvt_getPoolId_(e) != 0U) { // is it a mutable event? QEvt_refCtr_inc_(e); // increment the reference counter } --nFree; // one free entry just used up me->nFree = nFree; // update the original if (me->nMin > nFree) { me->nMin = nFree; // update minimum so far } QS_BEGIN_PRE_(QS_QF_EQUEUE_POST_LIFO, qs_id) QS_TIME_PRE_(); // timestamp QS_SIG_PRE_(e->sig); // the signal of this event QS_OBJ_PRE_(me); // this queue object QS_2U8_PRE_(QEvt_getPoolId_(e), e->refCtr_); // poolId & refCtr QS_EQC_PRE_(nFree); // # free entries QS_EQC_PRE_(me->nMin); // min # free entries QS_END_PRE_() QEvt const * const frontEvt = me->frontEvt; // read into temporary me->frontEvt = e; // deliver event directly to the front of the queue if (frontEvt != (QEvt *)0) { // was the queue not empty? ++me->tail; if (me->tail == me->end) { // need to wrap the tail? me->tail = 0U; // wrap around } me->ring[me->tail] = frontEvt; // save old front evt } QF_MEM_APP(); QF_CRIT_EXIT(); //! @public @memberof QEQueue //! @public @memberof QEQueue #ifndef Q_SPY Q_UNUSED_PAR(qs_id); #endif QF_CRIT_STAT QF_CRIT_ENTRY(); QF_MEM_SYS(); QEvt const * const e = me->frontEvt; // always remove evt from the front if (e != (QEvt *)0) { // was the queue not empty? // use a temporary variable to increment me->nFree QEQueueCtr const nFree = me->nFree + 1U; me->nFree = nFree; // update the # free // any events in the ring buffer? if (nFree <= me->end) { me->frontEvt = me->ring[me->tail]; // get from tail if (me->tail == 0U) { // need to wrap the tail? me->tail = me->end; // wrap around } --me->tail; QS_BEGIN_PRE_(QS_QF_EQUEUE_GET, qs_id) QS_TIME_PRE_(); // timestamp QS_SIG_PRE_(e->sig); // the signal of this event QS_OBJ_PRE_(me); // this queue object QS_2U8_PRE_(QEvt_getPoolId_(e), e->refCtr_); // poolId & refCtr QS_EQC_PRE_(nFree); // # free entries QS_END_PRE_() } else { me->frontEvt = (QEvt *)0; // queue becomes empty // all entries in the queue must be free (+1 for fronEvt) Q_ASSERT_INCRIT(410, nFree == (me->end + 1U)); QS_BEGIN_PRE_(QS_QF_EQUEUE_GET_LAST, qs_id) QS_TIME_PRE_(); // timestamp QS_SIG_PRE_(e->sig); // the signal of this event QS_OBJ_PRE_(me); // this queue object QS_2U8_PRE_(QEvt_getPoolId_(e), e->refCtr_); // poolId & refCtr QS_END_PRE_() } } QF_MEM_APP(); QF_CRIT_EXIT(); return e; const //! @public @memberof QEQueue //! @public @memberof QEQueue return me->nFree; const //! @public @memberof QEQueue //! @public @memberof QEQueue return me->nMin; const //! @public @memberof QEQueue //! @public @memberof QEQueue return me->frontEvt == (struct QEvt *)0; //! @struct QFreeBlock //! @private @memberof QFreeBlock //! @private @memberof QFreeBlock //! @class QMPool //! @private @memberof QMPool //! @private @memberof QMPool //! @private @memberof QMPool //! @private @memberof QMPool //! @private @memberof QMPool //! @private @memberof QMPool //! @private @memberof QMPool //! @public @memberof QMPool //! @public @memberof QMPool QF_CRIT_STAT QF_CRIT_ENTRY(); QF_MEM_SYS(); Q_REQUIRE_INCRIT(100, (poolSto != (void *)0) && (poolSize >= (uint_fast32_t)sizeof(QFreeBlock)) && ((uint_fast16_t)(blockSize + sizeof(QFreeBlock)) > blockSize)); me->free_head = (QFreeBlock *)poolSto; // find # free blocks in a memory block, NO DIVISION me->blockSize = (QMPoolSize)sizeof(QFreeBlock); uint_fast16_t nblocks = 1U; while (me->blockSize < (QMPoolSize)blockSize) { me->blockSize += (QMPoolSize)sizeof(QFreeBlock); ++nblocks; } // the pool buffer must fit at least one rounded-up block Q_ASSERT_INCRIT(110, poolSize >= me->blockSize); // start at the head of the free list QFreeBlock *fb = me->free_head; me->nTot = 1U; // the last block already in the list // chain all blocks together in a free-list... for (uint_fast32_t size = poolSize - me->blockSize; size >= (uint_fast32_t)me->blockSize; size -= (uint_fast32_t)me->blockSize) { fb->next = &fb[nblocks]; // point next link to next block #ifndef Q_UNSAFE fb->next_dis = (uintptr_t)(~Q_UINTPTR_CAST_(fb->next)); #endif fb = fb->next; // advance to the next block ++me->nTot; // one more free block in the pool } fb->next = (QFreeBlock *)0; // the last link points to NULL #ifndef Q_UNSAFE fb->next_dis = (uintptr_t)(~Q_UINTPTR_CAST_(fb->next)); #endif me->nFree = me->nTot; // all blocks are free me->nMin = me->nTot; // the minimum # free blocks me->start = poolSto; // the original start this pool buffer me->end = fb; // the last block in this pool QF_MEM_APP(); QF_CRIT_EXIT(); //! @public @memberof QMPool //! @public @memberof QMPool #ifndef Q_SPY Q_UNUSED_PAR(qs_id); #endif QF_CRIT_STAT QF_CRIT_ENTRY(); QF_MEM_SYS(); // have more free blocks than the requested margin? QFreeBlock *fb; if (me->nFree > (QMPoolCtr)margin) { fb = me->free_head; // get a free block // a free block must be valid Q_ASSERT_INCRIT(300, fb != (QFreeBlock *)0); QFreeBlock * const fb_next = fb->next; // fast temporary // the free block must have integrity (duplicate inverse storage) Q_ASSERT_INCRIT(302, Q_UINTPTR_CAST_(fb_next) == (uintptr_t)~fb->next_dis); --me->nFree; // one less free block if (me->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 == (QFreeBlock *)0); me->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, (me->start <= fb_next) && (fb_next <= me->end)); // is the # free blocks the new minimum so far? if (me->nMin > me->nFree) { me->nMin = me->nFree; // remember the new minimum } } me->free_head = fb_next; // set the head to the next free block QS_BEGIN_PRE_(QS_QF_MPOOL_GET, qs_id) QS_TIME_PRE_(); // timestamp QS_OBJ_PRE_(me); // this memory pool QS_MPC_PRE_(me->nFree); // # of free blocks in the pool QS_MPC_PRE_(me->nMin); // min # free blocks ever in the pool QS_END_PRE_() } else { // don't have enough free blocks at this point fb = (QFreeBlock *)0; QS_BEGIN_PRE_(QS_QF_MPOOL_GET_ATTEMPT, qs_id) QS_TIME_PRE_(); // timestamp QS_OBJ_PRE_(me); // this memory pool QS_MPC_PRE_(me->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 NULL pointer to the caller //! @public @memberof QMPool //! @public @memberof QMPool #ifndef Q_SPY Q_UNUSED_PAR(qs_id); #endif QFreeBlock * const fb = (QFreeBlock *)block; QF_CRIT_STAT QF_CRIT_ENTRY(); QF_MEM_SYS(); Q_REQUIRE_INCRIT(200, (me->nFree < me->nTot) && (me->start <= fb) && (fb <= me->end)); fb->next = me->free_head; // link into list #ifndef Q_UNSAFE fb->next_dis = (uintptr_t)(~Q_UINTPTR_CAST_(fb->next)); #endif // set as new head of the free list me->free_head = block; ++me->nFree; // one more free block in this pool QS_BEGIN_PRE_(QS_QF_MPOOL_PUT, qs_id) QS_TIME_PRE_(); // timestamp QS_OBJ_PRE_(me); // this memory pool QS_MPC_PRE_(me->nFree); // the # free blocks in the pool QS_END_PRE_() QF_MEM_APP(); QF_CRIT_EXIT(); //! @class QF_Attr //! @private @memberof QF_Attr //! @private @memberof QF_Attr //! @private @memberof QF_Attr //! @static @private @memberof QF //! @static @private @memberof QF //! @static @private @memberof QF uint8_t *ptr = (uint8_t *)start; for (uint_fast16_t n = len; n > 0U; --n) { *ptr = 0U; ++ptr; } //! @static @public @memberof QF //! @static @public @memberof QF //! @static @public @memberof QF //! @static @public @memberof QF //! @static @public @memberof QF //! @static @public @memberof QF //! @static @public @memberof QF //! @static @public @memberof QF QF_CRIT_STAT QF_CRIT_ENTRY(); Q_REQUIRE_INCRIT(400, (prio <= QF_MAX_ACTIVE) && (QActive_registry_[prio] != (QActive *)0)); uint_fast16_t const min = (uint_fast16_t)QActive_registry_[prio]->eQueue.nMin; QF_CRIT_EXIT(); return min; //! @static @public @memberof QF //! @static @public @memberof QF //! @static @public @memberof QF //! @static @public @memberof QF //! @static @public @memberof QF //! @static @public @memberof QF //! @static @public @memberof QF //! @static @public @memberof QF uint_fast8_t const poolId = QF_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, poolId < QF_MAX_EPOOL); if (poolId > 0U) { Q_REQUIRE_INCRIT(201, QF_EPOOL_EVENT_SIZE_(QF_priv_.ePool_[poolId - 1U]) < evtSize); } QF_priv_.maxPool_ = poolId + 1U; // one more pool QF_MEM_APP(); QF_CRIT_EXIT(); // perform the port-dependent initialization of the event-pool QF_EPOOL_INIT_(QF_priv_.ePool_[poolId], poolSto, poolSize, evtSize); #ifdef Q_SPY // generate the object-dictionary entry for the initialized pool { uint8_t obj_name[9] = "EvtPool?"; obj_name[7] = (uint8_t)((uint8_t)'0' + poolId + 1U); QF_CRIT_ENTRY(); QF_MEM_SYS(); QS_obj_dict_pre_(&QF_priv_.ePool_[poolId], (char const *)obj_name); QF_MEM_APP(); QF_CRIT_EXIT(); } #endif // Q_SPY //! @static @public @memberof QF //! @static @public @memberof QF return QF_EPOOL_EVENT_SIZE_(QF_priv_.ePool_[QF_priv_.maxPool_ - 1U]); //! @static @public @memberof QF //! @static @public @memberof QF QF_CRIT_STAT QF_CRIT_ENTRY(); QF_MEM_SYS(); Q_REQUIRE_INCRIT(400, (poolId <= QF_MAX_EPOOL) && (0U < poolId) && (poolId <= QF_priv_.maxPool_)); uint_fast16_t const min = (uint_fast16_t)QF_priv_.ePool_[poolId - 1U].nMin; QF_MEM_APP(); QF_CRIT_EXIT(); return min; //! @static @private @memberof QF //! @static @private @memberof QF QF_CRIT_STAT QF_CRIT_ENTRY(); QF_MEM_SYS(); // find the pool id that fits the requested event size... uint_fast8_t poolId = 0U; // zero-based poolId initially for (; poolId < QF_priv_.maxPool_; ++poolId) { if (evtSize <= QF_EPOOL_EVENT_SIZE_(QF_priv_.ePool_[poolId])) { break; } } // precondition: // - cannot run out of registered pools Q_REQUIRE_INCRIT(300, poolId < QF_priv_.maxPool_); ++poolId; // convert to 1-based poolId QF_MEM_APP(); QF_CRIT_EXIT(); // get event e (port-dependent)... QEvt *e; #ifdef Q_SPY QF_EPOOL_GET_(QF_priv_.ePool_[poolId - 1U], e, ((margin != QF_NO_MARGIN) ? margin : 0U), (uint_fast8_t)QS_EP_ID + poolId); #else QF_EPOOL_GET_(QF_priv_.ePool_[poolId - 1U], e, ((margin != QF_NO_MARGIN) ? margin : 0U), 0U); #endif if (e != (QEvt *)0) { // was e allocated correctly? e->sig = (QSignal)sig; // set the signal e->refCtr_ = 0U; // initialize the reference counter to 0 e->evtTag_ = (uint8_t)(QEVT_MARKER | poolId); QS_CRIT_ENTRY(); QS_MEM_SYS(); QS_BEGIN_PRE_(QS_QF_NEW, (uint_fast8_t)QS_EP_ID + poolId) 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 != QF_NO_MARGIN); QS_MEM_SYS(); QS_BEGIN_PRE_(QS_QF_NEW_ATTEMPT, (uint_fast8_t)QS_EP_ID + poolId) 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; //! @static @public @memberof QF //! @static @public @memberof QF QF_CRIT_STAT QF_CRIT_ENTRY(); Q_REQUIRE_INCRIT(402, QEvt_verify_(e)); uint_fast8_t const poolId = QEvt_getPoolId_(e); if (poolId != 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, (uint_fast8_t)QS_EP_ID + poolId) QS_TIME_PRE_(); // timestamp QS_SIG_PRE_(e->sig); // the signal of the event QS_2U8_PRE_(poolId, e->refCtr_); // poolId & 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, (uint_fast8_t)QS_EP_ID + poolId) QS_TIME_PRE_(); // timestamp QS_SIG_PRE_(e->sig); // the signal of the event QS_2U8_PRE_(poolId, e->refCtr_); // poolId & refCtr QS_END_PRE_() // pool number must be in range Q_ASSERT_INCRIT(410, (poolId <= QF_priv_.maxPool_) && (poolId <= 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_(QF_priv_.ePool_[poolId - 1U], (QEvt *)e, (uint_fast8_t)QS_EP_ID + poolId); #else QF_EPOOL_PUT_(QF_priv_.ePool_[poolId - 1U], (QEvt *)e, 0U); #endif } } else { QF_CRIT_EXIT(); } //! @static @private @memberof QF //! @static @private @memberof QF #ifdef Q_UNSAFE Q_UNUSED_PAR(evtRef); #endif QF_CRIT_STAT QF_CRIT_ENTRY(); Q_REQUIRE_INCRIT(502, QEvt_verify_(e)); uint_fast8_t const poolId = QEvt_getPoolId_(e); Q_REQUIRE_INCRIT(500, (poolId != 0U) && (evtRef == (void *)0)); QEvt_refCtr_inc_(e); // increments the ref counter QS_MEM_SYS(); QS_BEGIN_PRE_(QS_QF_NEW_REF, (uint_fast8_t)QS_EP_ID + poolId) QS_TIME_PRE_(); // timestamp QS_SIG_PRE_(e->sig); // the signal of the event QS_2U8_PRE_(poolId, e->refCtr_); // poolId & refCtr QS_END_PRE_() QS_MEM_APP(); QF_CRIT_EXIT(); return e; //! @static @private @memberof QF //! @static @private @memberof QF QEvt const * const e = evtRef; QF_CRIT_STAT QF_CRIT_ENTRY(); Q_REQUIRE_INCRIT(602, QEvt_verify_(e)); #ifdef Q_SPY uint_fast8_t const poolId = QEvt_getPoolId_(e); #endif QS_MEM_SYS(); QS_BEGIN_PRE_(QS_QF_DELETE_REF, (uint_fast8_t)QS_EP_ID + poolId) QS_TIME_PRE_(); // timestamp QS_SIG_PRE_(e->sig); // the signal of the event QS_2U8_PRE_(poolId, e->refCtr_); // poolId & refCtr QS_END_PRE_() QS_MEM_APP(); QF_CRIT_EXIT(); #if (QF_MAX_EPOOL > 0U) QF_gc(e); // recycle the referenced event #endif //! @static @public @memberof QF //! @static @public @memberof QF ((uint_fast16_t)0xFFFFU) ((QPrioSpec)((prio_) | ((pthre_) << 8U))) ((evtT_ *)QF_newX_((uint_fast16_t)sizeof(evtT_), \ QF_NO_MARGIN, (enum_t)(sig_))) \ (evtT_##_ctor((evtT_ *)QF_newX_((uint_fast16_t)sizeof(evtT_), \ QF_NO_MARGIN, (sig_)), __VA_ARGS__)) \ ((evtT_ *)QF_newX_((uint_fast16_t)sizeof(evtT_), \ (margin_), (enum_t)(sig_))) \ (evtT_##_ctor((evtT_ *)QF_newX_((uint_fast16_t)sizeof(evtT_), \ (margin_), (sig_)), __VA_ARGS__)) \ ((evtRef_) = (evtT_ const *)QF_newRef_(e, (evtRef_))) do { \ QF_deleteRef_((evtRef_)); \ (evtRef_) = (void *)0; \ } while (false) \ (QActive_start_((QActive *)(me_), (prioSpec_), \ (qSto_), (qLen_), (stkSto_), (stkSize_), (par_))) \ ((void)QActive_post_((me_), (e_), QF_NO_MARGIN, (sender_))) \ ((void)QActive_post_((me_), (e_), QF_NO_MARGIN, (void *)0)) \ (QActive_post_((me_), (e_), (margin_), (sender_))) \ (QActive_post_((me_), (e_), (margin_), (void *)0)) \ (QActive_postLIFO_((me_), (e_))) \ (QActive_publish_((e_), (void const *)(sender_), (sender_)->prio)) (QActive_publish_((e_), (void *)0, 0U)) (QTimeEvt_tick_((tickRate_), (sender_))) (QTimeEvt_tick_((tickRate_), (void *)0)) QTIMEEVT_TICK_X(0U, (sender_)) (QTicker_trig_((ticker_), (sender_))) (QTicker_trig_((ticker_), (void *)0)) ((void)0) QTIMEEVT_TICK_X((tickRate_), (sender_)) QTIMEEVT_TICK(sender_) QACTIVE_PUBLISH((e_), (sender_)) ((void)0) ((void)0) QMPool \ (QMPool_init(&(p_), (poolSto_), (poolSize_), (evtSize_))) ((uint_fast16_t)(p_).blockSize) \ ((e_) = (QEvt *)QMPool_get(&(p_), (m_), (qs_id_))) \ (QMPool_put(&(p_), (e_), (qs_id_))) // @class QV { //! @cond INTERNAL uint8_t dummy; //! @endcond } QV; //! @class QV_Attr //! @memberof QV_Attr //! @memberof QV_Attr //! @static @private @memberof QV //! @static @public @memberof QV //! @static @public @memberof QV //! @static @public @memberof QF //! @static @public @memberof QF QF_bzero_(&QF_priv_, sizeof(QF_priv_)); QF_bzero_(&QV_priv_, sizeof(QV_priv_)); QF_bzero_(&QTimeEvt_timeEvtHead_[0], sizeof(QTimeEvt_timeEvtHead_)); QF_bzero_(&QActive_registry_[0], sizeof(QActive_registry_)); #ifndef Q_UNSAFE QPSet_update_(&QV_priv_.readySet, &QV_priv_.readySet_dis); #endif #ifdef QV_INIT QV_INIT(); // port-specific initialization of the QV kernel #endif //! @static @public @memberof QF //! @static @public @memberof QF QF_onCleanup(); // application-specific cleanup callback // nothing else to do for the cooperative QV kernel //! @static @public @memberof QF //! @static @public @memberof QF #ifdef Q_SPY // produce the QS_QF_RUN trace record QF_INT_DISABLE(); QF_MEM_SYS(); QS_beginRec_((uint_fast8_t)QS_QF_RUN); QS_endRec_(); QF_MEM_APP(); QF_INT_ENABLE(); #endif // Q_SPY QF_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) uint8_t pprev = 0U; // previously used prio. #endif for (;;) { // QV event loop... // check internal integrity (duplicate inverse storage) Q_ASSERT_INCRIT(202, QPSet_verify_(&QV_priv_.readySet, &QV_priv_.readySet_dis)); // find the maximum prio. AO ready to run if (QPSet_notEmpty(&QV_priv_.readySet)) { uint8_t const p = (uint8_t)QPSet_findMax(&QV_priv_.readySet); 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_(p, pprev); // scheduled prio & previous prio QS_END_PRE_() #ifdef QF_ON_CONTEXT_SW QF_onContextSw(((pprev != 0U) ? QActive_registry_[pprev] : (QActive *)0), 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 = QActive_get_(a); // NOTE QActive_get_() performs QS_MEM_APP() before return // dispatch event (virtual call) (*a->super.vptr->dispatch)(&a->super, e, p); #if (QF_MAX_EPOOL > 0U) QF_gc(e); #endif QF_INT_DISABLE(); QF_MEM_SYS(); if (a->eQueue.frontEvt == (QEvt *)0) { // empty queue? QPSet_remove(&QV_priv_.readySet, p); #ifndef Q_UNSAFE QPSet_update_(&QV_priv_.readySet, &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_(pprev); // previous prio QS_END_PRE_() #ifdef QF_ON_CONTEXT_SW QF_onContextSw(QActive_registry_[pprev], (QActive *)0); #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(); // disable interrupts before looping back QF_MEM_SYS(); } } #ifdef __GNUC__ // GNU compiler? return 0; #endif //! QActive active object class customization for QV //! @public @memberof QActive //! @public @memberof QActive 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 == (void *)0); QF_CRIT_EXIT(); me->prio = (uint8_t)(prioSpec & 0xFFU); // QF-prio. of the AO me->pthre = (uint8_t)(prioSpec >> 8U); // preemption-threshold QActive_register_(me); // make QF aware of this active object QEQueue_init(&me->eQueue, qSto, qLen); // init the built-in queue // top-most initial tran. (virtual call) (*me->super.vptr->init)(&me->super, par, me->prio); QS_FLUSH(); // flush the trace buffer to the host ((void)0) ((void)0) \ Q_ASSERT_INCRIT(310, (me_)->eQueue.frontEvt != (QEvt *)0) \ QPSet_insert(&QV_priv_.readySet, (uint_fast8_t)(me_)->prio); \ QPSet_update_(&QV_priv_.readySet, &QV_priv_.readySet_dis) \ QPSet_insert(&QV_priv_.readySet, (uint_fast8_t)(me_)->prio) // @class QK { //! @cond INTERNAL uint8_t dummy; //! @endcond } QK; //! @class QK_Attr //! @memberof QK_Attr //! @memberof QK_Attr //! @memberof QK_Attr //! @memberof QK_Attr //! @memberof QK_Attr //! @memberof QK_Attr //! @memberof QK_Attr //! @memberof QK_Attr //! @static @private @memberof QK //! @static @public @memberof QK //! @static @public @memberof QK QF_CRIT_STAT QF_CRIT_ENTRY(); QF_MEM_SYS(); Q_REQUIRE_INCRIT(100, !QK_ISR_CONTEXT_()); // first store the previous lock prio QSchedStatus stat; if (ceiling > QK_priv_.lockCeil) { // raising the lock ceiling? QS_BEGIN_PRE_(QS_SCHED_LOCK, 0U) QS_TIME_PRE_(); // timestamp // the previous lock ceiling & new lock ceiling QS_2U8_PRE_((uint8_t)QK_priv_.lockCeil, (uint8_t)ceiling); QS_END_PRE_() // previous status of the lock stat = (QSchedStatus)QK_priv_.lockHolder; stat |= (QSchedStatus)QK_priv_.lockCeil << 8U; // new status of the lock QK_priv_.lockHolder = QK_priv_.actPrio; QK_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 //! @static @public @memberof QK //! @static @public @memberof QK // has the scheduler been actually locked by the last QK_schedLock()? if (stat != 0xFFU) { QF_CRIT_STAT QF_CRIT_ENTRY(); QF_MEM_SYS(); uint_fast8_t const lockCeil = QK_priv_.lockCeil; uint_fast8_t const prevCeil = (stat >> 8U); Q_REQUIRE_INCRIT(200, (!QK_ISR_CONTEXT_()) && (lockCeil > prevCeil)); QS_BEGIN_PRE_(QS_SCHED_UNLOCK, 0U) QS_TIME_PRE_(); // timestamp // current lock ceiling (old), previous lock ceiling (new) QS_2U8_PRE_((uint8_t)lockCeil, (uint8_t)prevCeil); QS_END_PRE_() // restore the previous lock ceiling and lock holder QK_priv_.lockCeil = prevCeil; QK_priv_.lockHolder = (stat & 0xFFU); // 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(); } //! @static @public @memberof QK //! @static @public @memberof QK //! @static @private @memberof QK //! @static @private @memberof QK Q_REQUIRE_INCRIT(400, QPSet_verify_(&QK_priv_.readySet, &QK_priv_.readySet_dis)); uint_fast8_t p; if (QPSet_isEmpty(&QK_priv_.readySet)) { p = 0U; // no activation needed } else { // find the highest-prio AO with non-empty event queue p = QPSet_findMax(&QK_priv_.readySet); // is the AO's prio. below the active preemption-threshold? if (p <= QK_priv_.actThre) { p = 0U; // no activation needed } // is the AO's prio. below the lock-ceiling? else if (p <= QK_priv_.lockCeil) { p = 0U; // no activation needed } else { QK_priv_.nextPrio = p; // next AO to run } } return p; //! @static @private @memberof QK //! @static @private @memberof QK uint_fast8_t const prio_in = QK_priv_.actPrio; // save initial prio. uint_fast8_t p = QK_priv_.nextPrio; // next prio to run QK_priv_.nextPrio = 0U; // clear for the next time Q_REQUIRE_INCRIT(500, (prio_in <= QF_MAX_ACTIVE) && (0U < p) && (p <= QF_MAX_ACTIVE)); #if (defined QF_ON_CONTEXT_SW) || (defined Q_SPY) uint_fast8_t pprev = prio_in; #endif // QF_ON_CONTEXT_SW || Q_SPY // loop until no more ready-to-run AOs of higher pthre than the initial QActive *a; do { a = QActive_registry_[p]; // obtain the pointer to the AO Q_ASSERT_INCRIT(505, a != (QActive *)0); // the AO must be registered // set new active prio. and preemption-threshold QK_priv_.actPrio = p; QK_priv_.actThre = a->pthre; #if (defined QF_ON_CONTEXT_SW) || (defined Q_SPY) if (p != pprev) { // changing threads? QS_BEGIN_PRE_(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(((pprev != 0U) ? QActive_registry_[pprev] : (QActive *)0), a); #endif // QF_ON_CONTEXT_SW pprev = p; // update previous prio. } #endif // QF_ON_CONTEXT_SW || Q_SPY QF_INT_ENABLE(); // unconditionally enable interrupts QEvt const * const e = QActive_get_(a); // NOTE QActive_get_() performs QF_MEM_APP() before return // dispatch event (virtual call) (*a->super.vptr->dispatch)(&a->super, e, p); #if (QF_MAX_EPOOL > 0U) 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(502, QPSet_verify_(&QK_priv_.readySet, &QK_priv_.readySet_dis)); if (a->eQueue.frontEvt == (QEvt *)0) { // empty queue? QPSet_remove(&QK_priv_.readySet, p); #ifndef Q_UNSAFE QPSet_update_(&QK_priv_.readySet, &QK_priv_.readySet_dis); #endif } if (QPSet_isEmpty(&QK_priv_.readySet)) { p = 0U; // no activation needed } else { // find new highest-prio AO ready to run... p = (uint8_t)QPSet_findMax(&QK_priv_.readySet); // is the new prio. below the initial preemption-threshold? if (p <= QActive_registry_[prio_in]->pthre) { p = 0U; // no activation needed } // is the AO's prio. below the lock preemption-threshold? else if (p <= QK_priv_.lockCeil) { p = 0U; // no activation needed } else { Q_ASSERT_INCRIT(510, p <= QF_MAX_ACTIVE); } } } while (p != 0U); // restore the active prio. and preemption-threshold QK_priv_.actPrio = prio_in; QK_priv_.actThre = (uint_fast8_t)QActive_registry_[prio_in]->pthre; #if (defined QF_ON_CONTEXT_SW) || (defined Q_SPY) if (prio_in != 0U) { // resuming an active object? a = QActive_registry_[prio_in]; // pointer to preempted AO QS_BEGIN_PRE_(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 = (QActive *)0; // QK idle loop QS_BEGIN_PRE_(QS_SCHED_IDLE, pprev) QS_TIME_PRE_(); // timestamp QS_U8_PRE_(pprev); // previous prio. QS_END_PRE_() } #ifdef QF_ON_CONTEXT_SW QF_onContextSw(QActive_registry_[pprev], a); #endif // QF_ON_CONTEXT_SW #endif // QF_ON_CONTEXT_SW || Q_SPY //! @static @public @memberof QF //! @static @public @memberof QF QF_bzero_(&QF_priv_, sizeof(QF_priv_)); QF_bzero_(&QK_priv_, sizeof(QK_priv_)); QF_bzero_(&QTimeEvt_timeEvtHead_[0], sizeof(QTimeEvt_timeEvtHead_)); QF_bzero_(&QActive_registry_[0], sizeof(QActive_registry_)); #ifndef Q_UNSAFE QPSet_update_(&QK_priv_.readySet, &QK_priv_.readySet_dis); #endif // setup the QK scheduler as initially locked and not running QK_priv_.lockCeil = (QF_MAX_ACTIVE + 1U); // scheduler locked // QK idle AO object (const in ROM) static QActive const idle_ao = { (struct QAsmVtable const *)0 }; // register the idle AO object (cast 'const' away) QActive_registry_[0] = QACTIVE_CAST_(&idle_ao); #ifdef QK_INIT QK_INIT(); // port-specific initialization of the QK kernel #endif //! @static @public @memberof QF //! @static @public @memberof QF QF_onCleanup(); // application-specific cleanup callback // nothing else to do for the preemptive QK kernel //! @static @public @memberof QF //! @static @public @memberof QF #ifdef Q_SPY // produce the QS_QF_RUN trace record QF_INT_DISABLE(); QF_MEM_SYS(); QS_beginRec_((uint_fast8_t)QS_QF_RUN); QS_endRec_(); QF_MEM_APP(); QF_INT_ENABLE(); #endif // Q_SPY QF_onStartup(); // application-specific startup callback QF_INT_DISABLE(); QF_MEM_SYS(); QK_priv_.lockCeil = 0U; // unlock the QK scheduler // activate AOs to process events posted so far if (QK_sched_() != 0U) { QK_activate_(); } #ifdef QK_START QK_START(); // port-specific startup of the QK kernel #endif QF_MEM_APP(); QF_INT_ENABLE(); for (;;) { // QK idle loop... QK_onIdle(); // application-specific QK on-idle callback } #ifdef __GNUC__ return 0; #endif // QActive class customization for QK //! @public @memberof QActive //! @public @memberof QActive 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 == (void *)0)); QF_MEM_APP(); QF_CRIT_EXIT(); me->prio = (uint8_t)(prioSpec & 0xFFU); // QF-prio. of the AO me->pthre = (uint8_t)(prioSpec >> 8U); // preemption-threshold QActive_register_(me); // make QF aware of this active object QEQueue_init(&me->eQueue, qSto, qLen); // init the built-in queue // top-most initial tran. (virtual call) (*me->super.vptr->init)(&me->super, par, me->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(); 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_)->eQueue.frontEvt != (QEvt *)0) do { \ QPSet_insert(&QK_priv_.readySet, (uint_fast8_t)(me_)->prio); \ QPSet_update_(&QK_priv_.readySet, &QK_priv_.readySet_dis); \ if (!QK_ISR_CONTEXT_()) { \ if (QK_sched_() != 0U) { \ QK_activate_(); \ } \ } \ } while (false) do { \ QPSet_insert(&QK_priv_.readySet, (uint_fast8_t)(me_)->prio); \ if (!QK_ISR_CONTEXT_()) { \ if (QK_sched_() != 0U) { \ QK_activate_(); \ } \ } \ } while (false) // @class QXK { //! @cond INTERNAL uint8_t dummy; //! @endcond } QXK; ((QTimeEvtCtr)0) //! @class QXK_Attr //! @memberof QXK_Attr //! @memberof QXK_Attr //! @memberof QXK_Attr //! @memberof QXK_Attr //! @memberof QXK_Attr //! @memberof QXK_Attr //! @memberof QXK_Attr //! @memberof QXK_Attr //! @static @private @memberof QXK //! @static @public @memberof QXK //! @static @public @memberof QXK //! @static @public @memberof QXK //! @static @public @memberof QXK 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_((uint8_t)QXK_priv_.lockCeil, (uint8_t)ceiling); QS_END_PRE_() // previous status of the lock stat = (QSchedStatus)QXK_priv_.lockHolder; stat |= (QSchedStatus)QXK_priv_.lockCeil << 8U; // new status of the lock QXK_priv_.lockHolder = (QXK_priv_.curr != (QActive *)0) ? (uint_fast8_t)QXK_priv_.curr->prio : 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 //! @static @public @memberof QXK //! @static @public @memberof QXK // has the scheduler been actually locked by the last QXK_schedLock()? if (stat != 0xFFU) { uint8_t const prevCeil = (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_((uint8_t)QXK_priv_.lockCeil, (uint8_t)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(); } //! @static @public @memberof QXK //! @static @public @memberof QXK QF_CRIT_STAT QF_CRIT_ENTRY(); QF_MEM_SYS(); Q_REQUIRE_INCRIT(600, QXK_priv_.lockCeil <= QF_MAX_ACTIVE); struct QActive *curr = QXK_priv_.curr; if (curr == (QActive *)0) { // basic thread? curr = QActive_registry_[QXK_priv_.actPrio]; } Q_ASSERT_INCRIT(690, curr != (QActive *)0); QF_MEM_APP(); QF_CRIT_EXIT(); return curr; //! @static @private @memberof QXK //! @static @private @memberof QXK Q_REQUIRE_INCRIT(402, QPSet_verify_(&QXK_priv_.readySet, &QXK_priv_.readySet_dis)); uint_fast8_t p; if (QPSet_isEmpty(&QXK_priv_.readySet)) { p = 0U; // no activation needed } else { // find the highest-prio thread ready to run p = QPSet_findMax(&QXK_priv_.readySet); if (p <= QXK_priv_.lockCeil) { // prio. of the thread holding the lock p = (uint_fast8_t)QActive_registry_[QXK_priv_.lockHolder]->prio; if (p != 0U) { Q_ASSERT_INCRIT(410, QPSet_hasElement(&QXK_priv_.readySet, p)); } } } QActive const * const curr = QXK_priv_.curr; QActive * const next = QActive_registry_[p]; // the next thread found must be registered in QF Q_ASSERT_INCRIT(420, next != (QActive *)0); // is the current thread a basic-thread? if (curr == (QActive *)0) { // 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->osObject != (void *)0) { // is next extended? QXK_CONTEXT_SWITCH_(); p = 0U; // no activation needed } } else { // below the pre-thre QXK_priv_.next = (QActive *)0; 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 = (QActive *)0; // no need to context-switch } p = 0U; // no activation needed } return p; //! @static @private @memberof QXK //! @static @private @memberof QXK uint_fast8_t const prio_in = QXK_priv_.actPrio; QActive *next = QXK_priv_.next; // the next AO (basic-thread) to run Q_REQUIRE_INCRIT(500, (next != (QActive *)0) && (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 = (QActive *)0; // clear the next AO QXK_priv_.curr = (QActive *)0; // current is basic-thread // prio. of the next thread uint_fast8_t p = (uint_fast8_t)next->prio; // 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 QEvt const * const e = QActive_get_(next); // NOTE QActive_get_() performs QS_MEM_APP() before return // dispatch event (virtual call) (*next->super.vptr->dispatch)(&next->super, e, p); #if (QF_MAX_EPOOL > 0U) QF_gc(e); #endif QF_INT_DISABLE(); // unconditionally disable interrupts QF_MEM_SYS(); // check internal integrity (duplicate inverse storage) Q_ASSERT_INCRIT(502, QPSet_verify_(&QXK_priv_.readySet, &QXK_priv_.readySet_dis)); if (next->eQueue.frontEvt == (QEvt *)0) { // empty queue? QPSet_remove(&QXK_priv_.readySet, p); #ifndef Q_UNSAFE QPSet_update_(&QXK_priv_.readySet, &QXK_priv_.readySet_dis); #endif } if (QPSet_isEmpty(&QXK_priv_.readySet)) { QXK_priv_.next = (QActive *)0; next = QActive_registry_[0]; p = 0U; // no activation needed } else { // find next highest-prio below the lock ceiling p = (uint8_t)QPSet_findMax(&QXK_priv_.readySet); if (p <= QXK_priv_.lockCeil) { p = QXK_priv_.lockHolder; // thread holding lock if (p != 0U) { Q_ASSERT_INCRIT(510, QPSet_hasElement(&QXK_priv_.readySet, p)); } } // set the next thread and ensure that it is registered next = QActive_registry_[p]; Q_ASSERT_INCRIT(520, next != (QActive *)0); // is next a basic thread? if (next->osObject == (void *)0) { // is the next prio. above the initial prio.? if (p > QActive_registry_[prio_in]->prio) { #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 = (QActive *)0; 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->osObject == (void *)0) { QXK_contextSw_((prio_in == 0U) ? (QActive *)0 : QActive_registry_[prio_in]); } #endif // QF_ON_CONTEXT_SW || Q_SPY __attribute__(( used )) //! @static @public @memberof QXK //! @static @public @memberof QXK #ifdef Q_SPY uint8_t const prev_prio = (QXK_priv_.prev != (QActive *)0) ? QXK_priv_.prev->prio : 0U; if (next != (QActive *)0) { // next is NOT idle? QS_BEGIN_PRE_(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_(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 //! @static @private @memberof QXK //! @static @private @memberof QXK QF_CRIT_STAT QF_CRIT_ENTRY(); QXThread const * const thr = QXTHREAD_CAST_(QXK_priv_.curr); Q_REQUIRE_INCRIT(900, (!QXK_ISR_CONTEXT_()) && (thr != (QXThread *)0)); // current thread must be extended Q_REQUIRE_INCRIT(901, QXK_priv_.lockHolder != (uint_fast8_t)thr->super.prio); uint_fast8_t const p = (uint_fast8_t)thr->super.prio; QF_MEM_SYS(); QActive_registry_[p] = (QActive *)0; QPSet_remove(&QXK_priv_.readySet, p); #ifndef Q_UNSAFE QPSet_update_(&QXK_priv_.readySet, &QXK_priv_.readySet_dis); #endif (void)QXK_sched_(); // schedule other threads QF_MEM_APP(); QF_CRIT_EXIT(); //! @static @public @memberof QF //! @static @public @memberof QF QF_bzero_(&QF_priv_, sizeof(QF_priv_)); QF_bzero_(&QXK_priv_, sizeof(QXK_priv_)); QF_bzero_(&QTimeEvt_timeEvtHead_[0], sizeof(QTimeEvt_timeEvtHead_)); QF_bzero_(&QActive_registry_[0], sizeof(QActive_registry_)); #ifndef Q_UNSAFE QPSet_update_(&QXK_priv_.readySet, &QXK_priv_.readySet_dis); #endif // setup the QXK scheduler as initially locked and not running QXK_priv_.lockCeil = (QF_MAX_ACTIVE + 1U); // scheduler locked // QXK idle AO object (const in ROM) static QActive const idle_ao = { (struct QAsmVtable const *)0 }; // register the idle AO object (cast 'const' away) QActive_registry_[0] = QACTIVE_CAST_(&idle_ao); QXK_priv_.prev = QActive_registry_[0]; #ifdef QXK_INIT QXK_INIT(); // port-specific initialization of the QXK kernel #endif //! @static @public @memberof QF //! @static @public @memberof QF QF_onCleanup(); // application-specific cleanup callback // nothing else to do for the dual-mode QXK kernel //! @static @public @memberof QF //! @static @public @memberof QF #ifdef Q_SPY QS_SIG_DICTIONARY(QXK_DELAY_SIG, (void *)0); QS_SIG_DICTIONARY(QXK_TIMEOUT_SIG, (void *)0); // produce the QS_QF_RUN trace record QF_INT_DISABLE(); QF_MEM_SYS(); QS_beginRec_((uint_fast8_t)QS_QF_RUN); QS_endRec_(); QF_MEM_APP(); QF_INT_ENABLE(); #endif // Q_SPY QF_onStartup(); // application-specific startup callback QF_INT_DISABLE(); QF_MEM_SYS(); QXK_priv_.lockCeil = 0U; // unlock the QXK scheduler // activate AOs to process events posted so far if (QXK_sched_() != 0U) { QXK_activate_(); } #ifdef QXK_START QXK_START(); // port-specific startup of the QXK kernel #endif QF_MEM_APP(); QF_INT_ENABLE(); for (;;) { // QXK idle loop... QXK_onIdle(); // application-specific QXK idle callback } #ifdef __GNUC__ // GNU compiler? return 0; #endif // QActive active object class customization for QK //! @public @memberof QActive //! @public @memberof QActive QF_CRIT_STAT QF_CRIT_ENTRY(); Q_REQUIRE_INCRIT(300, (!QXK_ISR_CONTEXT_()) && ((prioSpec & 0xFF00U) == 0U)); QF_CRIT_EXIT(); me->prio = (uint8_t)(prioSpec & 0xFFU); // QF-prio. of the AO me->pthre = 0U; // preemption-threshold NOT used QActive_register_(me); // make QF aware of this active object if (stkSto == (void *)0) { // starting basic thread (AO)? QEQueue_init(&me->eQueue, qSto, qLen); // init the built-in queue me->osObject = (void *)0; // no private stack for AO // top-most initial tran. (virtual call) (*me->super.vptr->init)(&me->super, par, me->prio); 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 != (QEvt const **)0) { QEQueue_init(&me->eQueue, qSto, qLen); } // extended thread constructor puts the thread handler in place of // the top-most initial tran. 'me->super.temp.act' QXThread_stackInit_(me, me->super.temp.thr, stkSto, stkSize); // the new thread is not blocked on any object me->super.temp.obj = (QMState *)0; QF_CRIT_ENTRY(); QF_MEM_SYS(); // extended-thread becomes ready immediately QPSet_insert(&QXK_priv_.readySet, (uint_fast8_t)me->prio); #ifndef Q_UNSAFE QPSet_update_(&QXK_priv_.readySet, &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) { (void)QXK_sched_(); // schedule other threads } QF_MEM_APP(); QF_CRIT_EXIT(); } //! @class QXThread //! @extends QActive //! @private @memberof QXThread //! @static @private @memberof QXThread //! dummy static member to force QM to generate 'struct QXThread' //! @public @memberof QXThread //! @public @memberof QXThread union QAsmAttr tmp; tmp.thr = handler; QActive_ctor(&me->super, tmp.fun); // superclass' ctor me->super.super.state.act = Q_ACTION_CAST(0); // mark as extended thread // instantiate the time-event member in the QXThread class QTimeEvt_ctorX(&me->timeEvt, &me->super, (enum_t)QXK_DELAY_SIG, tickRate); //! @public @memberof QXThread //! @public @memberof QXThread 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; // - 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 != (QXThread *)0) && (thr->super.super.temp.obj == (QMState *)0)); // - the thread must NOT be holding a scheduler lock. Q_REQUIRE_INCRIT(801, QXK_priv_.lockHolder != (uint_fast8_t)thr->super.prio); // remember the blocking object thr->super.super.temp.obj = QXK_PTR_CAST_(QMState const*, &thr->timeEvt); QXThread_teArm_(thr, (enum_t)QXK_DELAY_SIG, nTicks); QXThread_block_(thr); 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->super.super.temp.obj == QXK_PTR_CAST_(QMState const*, &thr->timeEvt)); thr->super.super.temp.obj = (QMState *)0; // clear QF_MEM_APP(); QF_CRIT_EXIT(); // signal of zero means that the time event was posted without // being canceled. return thr->timeEvt.super.sig == 0U; //! @public @memberof QXThread //! @public @memberof QXThread QF_CRIT_STAT QF_CRIT_ENTRY(); QF_MEM_SYS(); bool wasArmed; if (me->super.super.temp.obj == QXK_PTR_CAST_(QMState*, &me->timeEvt)) { wasArmed = QXThread_teDisarm_(me); QXThread_unblock_(me); } else { wasArmed = false; } QF_MEM_APP(); QF_CRIT_EXIT(); return wasArmed; //! @static @public @memberof QXThread //! @static @public @memberof QXThread 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 != (QXThread *)0) && (thr->super.super.temp.obj == (QMState *)0)); // - the thread must NOT be holding a scheduler lock. Q_REQUIRE_INCRIT(501, QXK_priv_.lockHolder != (uint_fast8_t)thr->super.prio); // is the queue empty? if (thr->super.eQueue.frontEvt == (QEvt *)0) { // remember the blocking object (the thread's queue) thr->super.super.temp.obj = QXK_PTR_CAST_(QMState const*, &thr->super.eQueue); QXThread_teArm_(thr, (enum_t)QXK_TIMEOUT_SIG, nTicks); QPSet_remove(&QXK_priv_.readySet, (uint_fast8_t)thr->super.prio); #ifndef Q_UNSAFE QPSet_update_(&QXK_priv_.readySet, &QXK_priv_.readySet_dis); #endif (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 queue Q_ASSERT_INCRIT(510, thr->super.super.temp.obj == QXK_PTR_CAST_(QMState const*, &thr->super.eQueue)); thr->super.super.temp.obj = (QMState *)0; // clear } // is the queue not empty? QEvt const *e; if (thr->super.eQueue.frontEvt != (QEvt *)0) { e = thr->super.eQueue.frontEvt; // remove from the front QEQueueCtr const nFree= thr->super.eQueue.nFree + 1U; thr->super.eQueue.nFree = nFree; // update the # free // any events in the ring buffer? if (nFree <= thr->super.eQueue.end) { // remove event from the tail thr->super.eQueue.frontEvt = thr->super.eQueue.ring[thr->super.eQueue.tail]; if (thr->super.eQueue.tail == 0U) { // need to wrap? thr->super.eQueue.tail = thr->super.eQueue.end; // wrap } --thr->super.eQueue.tail; QS_BEGIN_PRE_(QS_QF_ACTIVE_GET, thr->super.prio) QS_TIME_PRE_(); // timestamp QS_SIG_PRE_(e->sig); // the signal of this event QS_OBJ_PRE_(&thr->super); // this active object QS_2U8_PRE_(QEvt_getPoolId_(e), e->refCtr_); // poolId & refCtr QS_EQC_PRE_(nFree); // # free entries QS_END_PRE_() } else { thr->super.eQueue.frontEvt = (QEvt *)0; // empty queue // all entries in the queue must be free (+1 for fronEvt) Q_ASSERT_INCRIT(520, nFree == (thr->super.eQueue.end + 1U)); QS_BEGIN_PRE_(QS_QF_ACTIVE_GET_LAST, thr->super.prio) QS_TIME_PRE_(); // timestamp QS_SIG_PRE_(e->sig); // the signal of this event QS_OBJ_PRE_(&thr->super); // this active object QS_2U8_PRE_(QEvt_getPoolId_(e), e->refCtr_); // poolId & refCtr QS_END_PRE_() } } else { // the queue is still empty -- the timeout must have fired e = (QEvt *)0; } QF_MEM_APP(); QF_CRIT_EXIT(); return e; const //! @private @memberof QXThread //! @private @memberof QXThread // NOTE: must be called IN a critical section Q_REQUIRE_INCRIT(600, QXK_priv_.lockHolder != (uint_fast8_t)me->super.prio); QPSet_remove(&QXK_priv_.readySet, (uint_fast8_t)me->super.prio); #ifndef Q_UNSAFE QPSet_update_(&QXK_priv_.readySet, &QXK_priv_.readySet_dis); #endif (void)QXK_sched_(); // schedule other threads const //! @private @memberof QXThread //! @private @memberof QXThread // NOTE: must be called IN a critical section QPSet_insert(&QXK_priv_.readySet, (uint_fast8_t)me->super.prio); #ifndef Q_UNSAFE QPSet_update_(&QXK_priv_.readySet, &QXK_priv_.readySet_dis); #endif if ((!QXK_ISR_CONTEXT_()) // not inside ISR? && (QActive_registry_[0] != (QActive *)0)) // kernel started? { (void)QXK_sched_(); // schedule other threads } //! @private @memberof QXThread //! @private @memberof QXThread // 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)->timeEvt.super.sig = 0U; QXThread_unblock_(QXTHREAD_CAST_(act)); //! @private @memberof QXThread //! @private @memberof QXThread // NOTE: must be called IN a critical section // precondition: // - the time event must be unused Q_REQUIRE_INCRIT(700, me->timeEvt.ctr == 0U); me->timeEvt.super.sig = (QSignal)sig; if (nTicks != QXTHREAD_NO_TIMEOUT) { me->timeEvt.ctr = (QTimeEvtCtr)nTicks; me->timeEvt.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_tick_(). if ((me->timeEvt.super.refCtr_ & QTE_IS_LINKED) == 0U) { uint_fast8_t const tickRate = ((uint_fast8_t)me->timeEvt.super.refCtr_ & QTE_TICK_RATE); Q_ASSERT_INCRIT(710, tickRate < QF_MAX_TICK_RATE); // mark as linked me->timeEvt.super.refCtr_ |= QTE_IS_LINKED; // The time event is initially inserted into the separate // "freshly armed" list based on QTimeEvt_timeEvtHead_[tickRate].act. // Only later, inside the QTimeEvt_tick_() function, the "freshly // armed" list is appended to the main list of armed time events // based on QTimeEvt_timeEvtHead_[tickRate].next. Again, this is // to keep any changes to the main list exclusively inside // QTimeEvt_tick_(). me->timeEvt.next = QXK_PTR_CAST_(QTimeEvt*, QTimeEvt_timeEvtHead_[tickRate].act); QTimeEvt_timeEvtHead_[tickRate].act = &me->timeEvt; } } //! @private @memberof QXThread //! @private @memberof QXThread // NOTE: must be called IN a critical section bool wasArmed; // is the time evt running? if (me->timeEvt.ctr != 0U) { wasArmed = true; me->timeEvt.ctr = 0U; // schedule removal from list } // the time event was already automatically disarmed else { wasArmed = false; } return wasArmed; //! @private @memberof QXThread //! @private @memberof QXThread //! @class QXSemaphore //! @private @memberof QXSemaphore //! @private @memberof QXSemaphore //! @private @memberof QXSemaphore //! @public @memberof QXSemaphore //! @public @memberof QXSemaphore QF_CRIT_STAT QF_CRIT_ENTRY(); QF_MEM_SYS(); Q_REQUIRE_INCRIT(100, (count <= max_count) && (0U < max_count) && (max_count <= 0xFFU)); me->count = (uint8_t)count; me->max_count = (uint8_t)max_count; QPSet_setEmpty(&me->waitSet); QF_MEM_APP(); QF_CRIT_EXIT(); //! @public @memberof QXSemaphore //! @public @memberof QXSemaphore 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_()) && (me->max_count > 0U) && (curr != (QXThread *)0) && (curr->super.super.temp.obj == (QMState *)0)); // - the thread must NOT be holding a scheduler lock. Q_REQUIRE_INCRIT(201, QXK_priv_.lockHolder != (uint_fast8_t)curr->super.prio); bool taken = true; // assume that the semaphore will be signaled if (me->count > 0U) { --me->count; // semaphore taken: decrement the count QS_BEGIN_PRE_(QS_SEM_TAKE, curr->super.prio) QS_TIME_PRE_(); // timestamp QS_OBJ_PRE_(me); // this semaphore QS_2U8_PRE_(curr->super.prio, me->count); QS_END_PRE_() } else { // semaphore not available -- BLOCK the thread uint_fast8_t const p = (uint_fast8_t)curr->super.prio; // remove the curr prio from the ready set (will block) // and insert to the waiting set on this semaphore QPSet_remove(&QXK_priv_.readySet, p); #ifndef Q_UNSAFE QPSet_update_(&QXK_priv_.readySet, &QXK_priv_.readySet_dis); #endif QPSet_insert(&me->waitSet, p); // remember the blocking object (this semaphore) curr->super.super.temp.obj = QXK_PTR_CAST_(QMState*, me); QXThread_teArm_(curr, (enum_t)QXK_TIMEOUT_SIG, nTicks); QS_BEGIN_PRE_(QS_SEM_BLOCK, curr->super.prio) QS_TIME_PRE_(); // timestamp QS_OBJ_PRE_(me); // this semaphore QS_2U8_PRE_(curr->super.prio, me->count); QS_END_PRE_() // schedule the next thread if multitasking started (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->super.super.temp.obj == QXK_PTR_CAST_(QMState*, me)); // did the blocking time-out? (signal of zero means that it did) if (curr->timeEvt.super.sig == 0U) { if (QPSet_hasElement(&me->waitSet, p)) { // still waiting? QPSet_remove(&me->waitSet, 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, !QPSet_hasElement(&me->waitSet, p)); } curr->super.super.temp.obj = (QMState *)0; // clear blocking obj. } QF_MEM_APP(); QF_CRIT_EXIT(); return taken; //! @public @memberof QXSemaphore //! @public @memberof QXSemaphore QF_CRIT_STAT QF_CRIT_ENTRY(); QF_MEM_SYS(); // precondition: // - the semaphore must be initialized Q_REQUIRE_INCRIT(300, me->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 (me->count > 0U) { --me->count; taken = true; QS_BEGIN_PRE_(QS_SEM_TAKE, curr->prio) QS_TIME_PRE_(); // timestamp QS_OBJ_PRE_(me); // this semaphore QS_2U8_PRE_(curr->prio, me->count); QS_END_PRE_() } else { // the semaphore is NOT available (would block) taken = false; QS_BEGIN_PRE_(QS_SEM_BLOCK_ATTEMPT, curr->prio) QS_TIME_PRE_(); // timestamp QS_OBJ_PRE_(me); // this semaphore QS_2U8_PRE_(curr->prio, me->count); QS_END_PRE_() } QF_MEM_APP(); QF_CRIT_EXIT(); return taken; //! @public @memberof QXSemaphore //! @public @memberof QXSemaphore 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, me->max_count > 0U); // any threads blocked on this semaphore? if (QPSet_notEmpty(&me->waitSet)) { // find the highest-prio. thread waiting on this semaphore uint_fast8_t const p = QPSet_findMax(&me->waitSet); QXThread * const thr = QXK_PTR_CAST_(QXThread*, QActive_registry_[p]); // assert that the tread: // - must be registered in QF; // - must be extended; and // - must be blocked on this semaphore; Q_ASSERT_INCRIT(410, (thr != (QXThread *)0) && (thr->super.osObject != (void *)0) && (thr->super.super.temp.obj == QXK_PTR_CAST_(QMState*, me))); // disarm the internal time event (void)QXThread_teDisarm_(thr); // make the thread ready to run and remove from the wait-list QPSet_insert(&QXK_priv_.readySet, p); #ifndef Q_UNSAFE QPSet_update_(&QXK_priv_.readySet, &QXK_priv_.readySet_dis); #endif QPSet_remove(&me->waitSet, p); QS_BEGIN_PRE_(QS_SEM_TAKE, thr->super.prio) QS_TIME_PRE_(); // timestamp QS_OBJ_PRE_(me); // this semaphore QS_2U8_PRE_(thr->super.prio, me->count); QS_END_PRE_() if (!QXK_ISR_CONTEXT_()) { // not inside ISR? (void)QXK_sched_(); // schedule other threads } } else if (me->count < me->max_count) { ++me->count; // increment the semaphore count QS_BEGIN_PRE_(QS_SEM_SIGNAL, 0U) QS_TIME_PRE_(); // timestamp QS_OBJ_PRE_(me); // this semaphore QS_2U8_PRE_(0U, me->count); QS_END_PRE_() } else { signaled = false; // semaphore NOT signaled } QF_MEM_APP(); QF_CRIT_EXIT(); return signaled; //! @class QXMutex //! @private @memberof QXMutex //! @private @memberof QXMutex //! @public @memberof QXMutex //! @public @memberof QXMutex QF_CRIT_STAT QF_CRIT_ENTRY(); QF_MEM_SYS(); Q_REQUIRE_INCRIT(100, (prioSpec & 0xFF00U) == 0U); me->ao.prio = (uint8_t)(prioSpec & 0xFFU); // QF-prio. me->ao.pthre = 0U; // preemption-threshold (not used) QActive * const ao = &me->ao; QF_MEM_APP(); QF_CRIT_EXIT(); QActive_register_(ao); // register this mutex as AO //! @public @memberof QXMutex //! @public @memberof QXMutex 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 != (QXThread *)0) && (me->ao.prio <= QF_MAX_ACTIVE) && (curr->super.super.temp.obj == (QMState *)0)); // also: the thread must NOT be holding a scheduler lock. Q_REQUIRE_INCRIT(201, QXK_priv_.lockHolder != (uint_fast8_t)curr->super.prio); // is the mutex available? bool locked = true; // assume that the mutex will be locked if (me->ao.eQueue.nFree == 0U) { me->ao.eQueue.nFree = 1U; // mutex lock nesting // also: the newly locked mutex must have no holder yet Q_REQUIRE_INCRIT(203, me->ao.osObject == (void *)0); // 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. me->ao.osObject = curr; me->ao.eQueue.head = (QEQueueCtr)curr->super.prio; QS_BEGIN_PRE_(QS_MTX_LOCK, curr->super.prio) QS_TIME_PRE_(); // timestamp QS_OBJ_PRE_(me); // this mutex QS_U8_PRE_((uint8_t)me->ao.eQueue.head); // holder prio QS_U8_PRE_((uint8_t)me->ao.eQueue.nFree); // nesting QS_END_PRE_() if (me->ao.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->super.prio < me->ao.prio) && (QActive_registry_[me->ao.prio] == &me->ao)); // remove the thread's original prio from the ready set // and insert the mutex's prio into the ready set QPSet_remove(&QXK_priv_.readySet, (uint_fast8_t)me->ao.eQueue.head); QPSet_insert(&QXK_priv_.readySet, (uint_fast8_t)me->ao.prio); #ifndef Q_UNSAFE QPSet_update_(&QXK_priv_.readySet, &QXK_priv_.readySet_dis); #endif // put the thread into the AO registry in place of the mutex QActive_registry_[me->ao.prio] = &curr->super; // set thread's prio to that of the mutex curr->super.prio = me->ao.prio; } } // is the mutex locked by this thread already (nested locking)? else if (me->ao.osObject == &curr->super) { // the nesting level beyond the arbitrary but high limit // most likely means cyclic or recursive locking of a mutex. Q_ASSERT_INCRIT(220, me->ao.eQueue.nFree < 0xFFU); ++me->ao.eQueue.nFree; // lock one more level QS_BEGIN_PRE_(QS_MTX_LOCK, curr->super.prio) QS_TIME_PRE_(); // timestamp QS_OBJ_PRE_(me); // this mutex QS_U8_PRE_((uint8_t)me->ao.eQueue.head); // holder prio QS_U8_PRE_((uint8_t)me->ao.eQueue.nFree); // nesting QS_END_PRE_() } else { // the mutex is already locked by a different thread // the mutex holder must be valid Q_ASSERT_INCRIT(230, me->ao.osObject != (void *)0); if (me->ao.prio != 0U) { // prio.-ceiling protocol used? // the prio slot must be occupied by the thr. holding the mutex Q_ASSERT_INCRIT(240, QActive_registry_[me->ao.prio] == QACTIVE_CAST_(me->ao.osObject)); } // remove the curr thread's prio from the ready set (will block) // and insert it to the waiting set on this mutex uint_fast8_t const p = (uint_fast8_t)curr->super.prio; QPSet_remove(&QXK_priv_.readySet, p); #ifndef Q_UNSAFE QPSet_update_(&QXK_priv_.readySet, &QXK_priv_.readySet_dis); #endif QPSet_insert(&me->waitSet, p); // set the blocking object (this mutex) curr->super.super.temp.obj = QXK_PTR_CAST_(QMState*, me); QXThread_teArm_(curr, (enum_t)QXK_TIMEOUT_SIG, nTicks); QS_BEGIN_PRE_(QS_MTX_BLOCK, curr->super.prio) QS_TIME_PRE_(); // timestamp QS_OBJ_PRE_(me); // this mutex QS_2U8_PRE_((uint8_t)me->ao.eQueue.head, // holder prio curr->super.prio); // blocked thread prio QS_END_PRE_() // schedule the next thread if multitasking started (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->super.super.temp.obj == QXK_PTR_CAST_(QMState*, me)); // did the blocking time-out? (signal of zero means that it did) if (curr->timeEvt.super.sig == 0U) { if (QPSet_hasElement(&me->waitSet, p)) { // still waiting? QPSet_remove(&me->waitSet, 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, !QPSet_hasElement(&me->waitSet, p)); } curr->super.super.temp.obj = (QMState *)0; // clear blocking obj. } QF_MEM_APP(); QF_CRIT_EXIT(); return locked; //! @public @memberof QXMutex //! @public @memberof QXMutex QF_CRIT_STAT QF_CRIT_ENTRY(); QF_MEM_SYS(); QActive *curr = QXK_priv_.curr; if (curr == (QActive *)0) { // 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 != (QActive *)0) && (me->ao.prio <= QF_MAX_ACTIVE)); // also: the thread must NOT be holding a scheduler lock. Q_REQUIRE_INCRIT(301, QXK_priv_.lockHolder != (uint_fast8_t)curr->prio); // is the mutex available? if (me->ao.eQueue.nFree == 0U) { me->ao.eQueue.nFree = 1U; // mutex lock nesting // also the newly locked mutex must have no holder yet Q_REQUIRE_INCRIT(303, me->ao.osObject == (void *)0); // 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. me->ao.osObject = curr; me->ao.eQueue.head = (QEQueueCtr)curr->prio; QS_BEGIN_PRE_(QS_MTX_LOCK, curr->prio) QS_TIME_PRE_(); // timestamp QS_OBJ_PRE_(me); // this mutex QS_U8_PRE_((uint8_t)me->ao.eQueue.head); // holder prio QS_U8_PRE_((uint8_t)me->ao.eQueue.nFree); // nesting QS_END_PRE_() if (me->ao.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->prio < me->ao.prio) && (QActive_registry_[me->ao.prio] == &me->ao)); // remove the thread's original prio from the ready set // and insert the mutex's prio into the ready set QPSet_remove(&QXK_priv_.readySet, (uint_fast8_t)me->ao.eQueue.head); QPSet_insert(&QXK_priv_.readySet, (uint_fast8_t)me->ao.prio); #ifndef Q_UNSAFE QPSet_update_(&QXK_priv_.readySet, &QXK_priv_.readySet_dis); #endif // put the thread into the AO registry in place of the mutex QActive_registry_[me->ao.prio] = curr; // set thread's prio to that of the mutex curr->prio = me->ao.prio; } } // is the mutex locked by this thread already (nested locking)? else if (me->ao.osObject == curr) { // the nesting level must not exceed the specified limit Q_ASSERT_INCRIT(320, me->ao.eQueue.nFree < 0xFFU); ++me->ao.eQueue.nFree; // lock one more level QS_BEGIN_PRE_(QS_MTX_LOCK, curr->prio) QS_TIME_PRE_(); // timestamp QS_OBJ_PRE_(me); // this mutex QS_U8_PRE_((uint8_t)me->ao.eQueue.head); // holder prio QS_U8_PRE_((uint8_t)me->ao.eQueue.nFree); // nesting QS_END_PRE_() } else { // the mutex is already locked by a different thread if (me->ao.prio != 0U) { // prio.-ceiling protocol used? // the prio slot must be occupied by the thr. holding the mutex Q_ASSERT_INCRIT(330, QActive_registry_[me->ao.prio] == QACTIVE_CAST_(me->ao.osObject)); } QS_BEGIN_PRE_(QS_MTX_BLOCK_ATTEMPT, curr->prio) QS_TIME_PRE_(); // timestamp QS_OBJ_PRE_(me); // this mutex QS_2U8_PRE_((uint8_t)me->ao.eQueue.head, // holder prio curr->prio); // trying thread prio QS_END_PRE_() curr = (QActive *)0; // means that mutex is NOT available } QF_MEM_APP(); QF_CRIT_EXIT(); return curr != (QActive *)0; //! @public @memberof QXMutex //! @public @memberof QXMutex QF_CRIT_STAT QF_CRIT_ENTRY(); QF_MEM_SYS(); QActive *curr = QXK_priv_.curr; if (curr == (QActive *)0) { // called from a basic thread? curr = QActive_registry_[QXK_priv_.actPrio]; } Q_REQUIRE_INCRIT(400, (!QXK_ISR_CONTEXT_()) && (curr != (QActive *)0)); Q_REQUIRE_INCRIT(401, me->ao.eQueue.nFree > 0U); Q_REQUIRE_INCRIT(403, me->ao.osObject == curr); // is this the last nesting level? if (me->ao.eQueue.nFree == 1U) { if (me->ao.prio != 0U) { // prio.-ceiling protocol used? // prio. must be in range Q_ASSERT_INCRIT(410, me->ao.prio < QF_MAX_ACTIVE); // restore the holding thread's prio from the mutex curr->prio = (uint8_t)me->ao.eQueue.head; // put the mutex back into the AO registry QActive_registry_[me->ao.prio] = &me->ao; // remove the mutex' prio from the ready set // and insert the original thread's prio. QPSet_remove(&QXK_priv_.readySet, (uint_fast8_t)me->ao.prio); QPSet_insert(&QXK_priv_.readySet, (uint_fast8_t)me->ao.eQueue.head); #ifndef Q_UNSAFE QPSet_update_(&QXK_priv_.readySet, &QXK_priv_.readySet_dis); #endif } QS_BEGIN_PRE_(QS_MTX_UNLOCK, curr->prio) QS_TIME_PRE_(); // timestamp QS_OBJ_PRE_(me); // this mutex QS_2U8_PRE_((uint8_t)me->ao.eQueue.head, // holder prio 0U); // nesting QS_END_PRE_() // are any other threads waiting on this mutex? if (QPSet_notEmpty(&me->waitSet)) { // find the highest-prio. thread waiting on this mutex uint_fast8_t const p = QPSet_findMax(&me->waitSet); // remove this thread from waiting on the mutex // and insert it into the ready set. QPSet_remove(&me->waitSet, p); QPSet_insert(&QXK_priv_.readySet, p); #ifndef Q_UNSAFE QPSet_update_(&QXK_priv_.readySet, &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 != (QXThread *)0) && (thr->super.prio == (uint8_t)p) && (thr->super.super.state.act == Q_ACTION_CAST(0)) && (thr->super.super.temp.obj == QXK_PTR_CAST_(QMState*, me))); // disarm the internal time event (void)QXThread_teDisarm_(thr); // 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. me->ao.osObject = thr; me->ao.eQueue.head = (QEQueueCtr)thr->super.prio; QS_BEGIN_PRE_(QS_MTX_LOCK, thr->super.prio) QS_TIME_PRE_(); // timestamp QS_OBJ_PRE_(me); // this mutex QS_U8_PRE_((uint8_t)me->ao.eQueue.head); // holder prio QS_U8_PRE_((uint8_t)me->ao.eQueue.nFree); // nesting QS_END_PRE_() if (me->ao.prio != 0U) { // prio.-ceiling protocol used? // the holder prio. must be lower than that of the mutex Q_ASSERT_INCRIT(430, (me->ao.prio < QF_MAX_ACTIVE) && (thr->super.prio < me->ao.prio)); // put the thread into AO registry in place of the mutex QActive_registry_[me->ao.prio] = &thr->super; } } else { // no threads are waiting for this mutex me->ao.eQueue.nFree = 0U; // free up the nesting count // the mutex no longer held by any thread me->ao.osObject = (void *)0; me->ao.eQueue.head = 0U; me->ao.eQueue.tail = 0U; if (me->ao.prio != 0U) { // prio.-ceiling protocol used? // put the mutex back at the original mutex slot QActive_registry_[me->ao.prio] = QXK_PTR_CAST_(QActive*, me); } } // 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 --me->ao.eQueue.nFree; // unlock one level QS_BEGIN_PRE_(QS_MTX_UNLOCK_ATTEMPT, curr->prio) QS_TIME_PRE_(); // timestamp QS_OBJ_PRE_(me); // this mutex QS_U8_PRE_((uint8_t)me->ao.eQueue.head); // holder prio QS_U8_PRE_((uint8_t)me->ao.eQueue.nFree); // nesting QS_END_PRE_() } QF_MEM_APP(); QF_CRIT_EXIT(); QACTIVE_START((me_), (prioSpec_), (qSto_), (qLen_), \ (stkSto_), (stkSize_), (par_)) \ QACTIVE_POST_X(&(me_)->super, (e_), (margin_), (sender_)) 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) \ Q_ASSERT_INCRIT(310, (me_)->eQueue.frontEvt != (QEvt *)0) do { \ QPSet_insert(&QXK_priv_.readySet, (uint_fast8_t)(me_)->prio); \ QPSet_update_(&QXK_priv_.readySet, &QXK_priv_.readySet_dis); \ if (!QXK_ISR_CONTEXT_()) { \ if (QXK_sched_() != 0U) { \ QXK_activate_(); \ } \ } \ } while (false) do { \ QPSet_insert(&QXK_priv_.readySet, (uint_fast8_t)(me_)->prio); \ if (!QXK_ISR_CONTEXT_()) { \ if (QXK_sched_() != 0U) { \ QXK_activate_(); \ } \ } \ } while (false) do { \ if (me->super.temp.obj == QXK_PTR_CAST_(QMState*, &me->eQueue)) { \ (void)QXThread_teDisarm_(QXTHREAD_CAST_(me)); \ QPSet_insert(&QXK_priv_.readySet, (uint_fast8_t)me->prio); \ QPSet_update_(&QXK_priv_.readySet, &QXK_priv_.readySet_dis); \ if (!QXK_ISR_CONTEXT_()) { \ (void)QXK_sched_(); \ } \ } \ } while (false) do { \ if (me->super.temp.obj == QXK_PTR_CAST_(QMState*, &me->eQueue)) { \ (void)QXThread_teDisarm_(QXTHREAD_CAST_(me)); \ QPSet_insert(&QXK_priv_.readySet, (uint_fast8_t)me->prio); \ if (!QXK_ISR_CONTEXT_()) { \ (void)QXK_sched_(); \ } \ } \ } while (false) <type_> ((type_)(ptr_)) ((QXThread *)(ptr_)) //! @class QS { //! @cond INTERNAL uint8_t dummy; //! @endcond } QS; //! @static @public @memberof QS //! pre-defined QS record IDs { // [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 }; //! @static @public @memberof QS //! QS-TX record groups for QS_GLB_FILTER() { 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 }; //! @static @public @memberof QS //! QS user record group offsets for QS_GLB_FILTER() { QS_USER = 100, //!< the first record available to QS users QS_USER0 = QS_USER, //!< offset for User Group 0 QS_USER1 = (enum_t)QS_USER0 + 5, //!< offset for User Group 1 QS_USER2 = (enum_t)QS_USER1 + 5, //!< offset for User Group 2 QS_USER3 = (enum_t)QS_USER2 + 5, //!< offset for User Group 3 QS_USER4 = (enum_t)QS_USER3 + 5 //!< offset for User Group 4 }; //! @static @public @memberof QS //! QS ID offsets for QS_LOC_FILTER() { 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 }; //! @static @public @memberof QS //! QS ID groups for QS_LOC_FILTER() { QS_ALL_IDS = 0xF0, //!< all QS IDs QS_AO_IDS = 0x80 + (enum_t)QS_AO_ID, //!< AO IDs (priorities) QS_EP_IDS = 0x80 + (enum_t)QS_EP_ID, //!< event-pool IDs QS_EQ_IDS = 0x80 + (enum_t)QS_EQ_ID, //!< event-queue IDs QS_AP_IDS = 0x80 + (enum_t)QS_AP_ID //!< Application-specific IDs }; //! @struct QSpyId //! @static @private @memberof QS )(void); //! @struct QS_Filter //! @static @private @memberof QS //! Enumerates data elements for app-specific trace records { QS_I8_ENUM_T, //!< signed 8-bit integer or enum format QS_U8_T, //!< unsigned 8-bit integer format QS_I16_T, //!< signed 16-bit integer format QS_U16_T, //!< unsigned 16-bit integer format QS_I32_T, //!< signed 32-bit integer format QS_U32_T, //!< unsigned 32-bit integer format QS_F32_T, //!< 32-bit floating point format QS_F64_T, //!< 64-bit floating point format QS_STR_T, //!< zero-terminated ASCII string format QS_MEM_T, //!< up to 255-bytes memory block format QS_SIG_T, //!< event signal format QS_OBJ_T, //!< object pointer format QS_FUN_T, //!< function pointer format QS_I64_T, //!< signed 64-bit integer format QS_U64_T //!< unsigned 64-bit integer format }; //! @static @public @memberof QS //! @static @public @memberof QS QS_priv_.buf = &sto[0]; QS_priv_.end = (QSCtr)stoSize; QS_priv_.head = 0U; QS_priv_.tail = 0U; QS_priv_.used = 0U; QS_priv_.seq = 0U; QS_priv_.chksum = 0U; QS_priv_.critNest = 0U; QS_glbFilter_(-(int_fast16_t)QS_ALL_RECORDS); // all global filters OFF QS_locFilter_((int_fast16_t)QS_ALL_IDS); // all local filters ON QS_priv_.locFilter_AP = (void *)0; // deprecated "AP-filter" // produce an empty record to "flush" the QS trace buffer QS_beginRec_((uint_fast8_t)QS_EMPTY); QS_endRec_(); // produce the reset record to inform QSPY of a new session QS_target_info_pre_(0xFFU); // hold off flushing after successfull initialization (see QS_INIT()) //! @static @public @memberof QS //! @static @public @memberof QS uint16_t ret; if (QS_priv_.used == 0U) { ret = QS_EOD; // set End-Of-Data } else { uint8_t const * const buf = QS_priv_.buf; // put in a temporary QSCtr tail = QS_priv_.tail; // put in a temporary (register) ret = (uint16_t)buf[tail]; // set the byte to return ++tail; // advance the tail if (tail == QS_priv_.end) { // tail wrap around? tail = 0U; } QS_priv_.tail = tail; // update the tail --QS_priv_.used; // one less byte used } return ret; // return the byte or EOD //! @static @public @memberof QS //! @static @public @memberof QS QSCtr const used = QS_priv_.used; // put in a temporary (register) uint8_t const *buf; // any bytes used in the ring buffer? if (used != 0U) { QSCtr tail = QS_priv_.tail; // put in a temporary (register) QSCtr const end = QS_priv_.end; // put in a temporary (register) QSCtr n = (QSCtr)(end - tail); if (n > used) { n = used; } if (n > (QSCtr)(*pNbytes)) { n = (QSCtr)(*pNbytes); } *pNbytes = (uint16_t)n; // n-bytes available buf = &QS_priv_.buf[tail]; // the bytes are at the tail QS_priv_.used = (QSCtr)(used - n); tail += n; if (tail == end) { tail = 0U; } QS_priv_.tail = tail; } else { // no bytes available *pNbytes = 0U; // no bytes available right now buf = (uint8_t *)0; // no bytes available right now } return buf; //! @static @public @memberof QS //! @static @public @memberof QS //! @static @public @memberof QS //! @static @public @memberof QS //! @static @public @memberof QS //! @static @public @memberof QS //! @static @public @memberof QS //! @static @public @memberof QS //! @static @public @memberof QS //! @static @public @memberof QS //! @static @public @memberof QS //! Kinds of objects used in QS-RX { 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 }; //! @static @public @memberof QS //! Object combinations for QS-RX { SM_AO_OBJ = (enum_t)MAX_OBJ //!< combination of SM and AO }; //! @static @public @memberof QS //! @static @public @memberof QS QS_rxPriv_.buf = &sto[0]; QS_rxPriv_.end = (QSCtr)stoSize; QS_rxPriv_.head = 0U; QS_rxPriv_.tail = 0U; QS_rxPriv_.currObj[SM_OBJ] = (void *)0; QS_rxPriv_.currObj[AO_OBJ] = (void *)0; QS_rxPriv_.currObj[MP_OBJ] = (void *)0; QS_rxPriv_.currObj[EQ_OBJ] = (void *)0; QS_rxPriv_.currObj[TE_OBJ] = (void *)0; QS_rxPriv_.currObj[AP_OBJ] = (void *)0; QS_RX_TRAN_(WAIT4_SEQ); l_rx.esc = 0U; l_rx.seq = 0U; l_rx.chksum = 0U; QS_beginRec_((uint_fast8_t)QS_OBJ_DICT); QS_OBJ_PRE_(&QS_rxPriv_); QS_STR_PRE_("QS_RX"); QS_endRec_(); // no QS_REC_DONE(), because QS is not running yet #ifdef Q_UTEST QS_tstPriv_.tpNum = 0U; QS_tstPriv_.testTime = 0U; #endif // Q_UTEST //! @static @public @memberof QS //! @static @public @memberof QS // NOTE: does not need critical section // But requires system-level memory access (QF_MEM_SYS()). QSCtr head = QS_rxPriv_.head + 1U; if (head == QS_rxPriv_.end) { head = 0U; } if (head != QS_rxPriv_.tail) { // buffer NOT full? QS_rxPriv_.buf[QS_rxPriv_.head] = b; QS_rxPriv_.head = head; // update the head to a *valid* index return true; // byte placed in the buffer } else { return false; // byte NOT placed in the buffer } //! @static @public @memberof QS //! @static @public @memberof QS // NOTE: Must be called IN critical section. // Also requires system-level memory access (QF_MEM_SYS()). QSCtr tail = QS_rxPriv_.tail; while (QS_rxPriv_.head != tail) { // QS-RX buffer NOT empty? uint8_t b = QS_rxPriv_.buf[tail]; ++tail; if (tail == QS_rxPriv_.end) { tail = 0U; } QS_rxPriv_.tail = tail; // update the tail to a *valid* index if (l_rx.esc != 0U) { // escaped byte arrived? l_rx.esc = 0U; b ^= QS_ESC_XOR; l_rx.chksum += b; QS_rxParseData_(b); } else if (b == QS_ESC) { l_rx.esc = 1U; } else if (b == QS_FRAME) { // get ready for the next frame b = l_rx.state; // save the current state in b l_rx.esc = 0U; QS_RX_TRAN_(WAIT4_SEQ); if (l_rx.chksum == QS_GOOD_CHKSUM) { l_rx.chksum = 0U; QS_rxHandleGoodFrame_(b); } else { // bad checksum l_rx.chksum = 0U; QS_rxReportError_(0x41); QS_rxHandleBadFrame_(b); } } else { l_rx.chksum += b; QS_rxParseData_(b); } } //! @static @public @memberof QS //! @static @public @memberof QS // NOTE: Must be called IN critical section. // Also requires system-level memory access (QF_MEM_SYS()). QSCtr const head = QS_rxPriv_.head; uint16_t nFree; if (head == QS_rxPriv_.tail) { // buffer empty? nFree = (uint16_t)(QS_rxPriv_.end - 1U); } else if (head < QS_rxPriv_.tail) { nFree = (uint16_t)(QS_rxPriv_.tail - (head + 1U)); } else { nFree = (uint16_t)((head + 1U) - QS_rxPriv_.tail); nFree = (uint16_t)(QS_rxPriv_.end - nFree); } return nFree; //! @static @public @memberof QS //! @static @public @memberof QS //! @static @public @memberof QS //! @static @public @memberof QS //! @static @public @memberof QS //! @static @public @memberof QS // @struct TProbe { QSFun addr; uint32_t data; uint8_t idx; }; //! @static @public @memberof QS //! @static @public @memberof QS //! @static @public @memberof QS //! @static @public @memberof QS //! @static @public @memberof QS //! @static @public @memberof QS //! @static @public @memberof QS //! @static @public @memberof QS //! @static @public @memberof QS //! @static @public @memberof QS //! QF_init() stub for QUTest // 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). QF_bzero_(&QF_priv_, sizeof(QF_priv_)); QF_bzero_(&QS_tstPriv_, sizeof(QS_tstPriv_)); QF_bzero_(&QActive_registry_[0], sizeof(QActive_registry_)); #ifndef Q_UNSAFE QPSet_update_(&QS_tstPriv_.readySet, &QS_tstPriv_.readySet_dis); #endif //! QF_stop() stub for QUTest QS_onReset(); //! QF_run() stub for QUTest 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 test loop QS_onCleanup(); // application cleanup return 0; // return no error // QActive stub for QUTest //! @private @memberof QActive //! @private @memberof QActive Q_UNUSED_PAR(stkSto); Q_UNUSED_PAR(stkSize); me->prio = (uint8_t)(prioSpec & 0xFFU); // QF-prio. of the AO me->pthre = (uint8_t)(prioSpec >> 8U); // preemption-threshold QActive_register_(me); // make QF aware of this active object QEQueue_init(&me->eQueue, qSto, qLen); // top-most initial tran. (virtual call) (*me->super.vptr->init)(&me->super, par, me->prio); //! @protected @memberof QActive //! @protected @memberof QActive QActive_unsubscribeAll(me); // unsubscribe from all events QActive_unregister_(me); // un-register this active object // QTimeEvt stub for QUTest //! @private @memberof QTimeEvt //! @private @memberof QTimeEvt QS_CRIT_STAT QS_CRIT_ENTRY(); QS_MEM_SYS(); QTimeEvt *prev = &QTimeEvt_timeEvtHead_[tickRate]; QS_BEGIN_PRE_(QS_QF_TICK, 0U) ++prev->ctr; QS_TEC_PRE_(prev->ctr); // tick ctr QS_U8_PRE_(tickRate); // tick rate QS_END_PRE_() // is current Time Event object provided? QTimeEvt *t = (QTimeEvt *)QS_rxPriv_.currObj[TE_OBJ]; if (t != (QTimeEvt *)0) { // the time event must be armed Q_ASSERT_INCRIT(810, t->ctr != 0U); QActive * const act = (QActive *)(t->act); // the recipient AO must be provided Q_ASSERT_INCRIT(820, act != (QActive *)0); // periodic time evt? if (t->interval != 0U) { t->ctr = t->interval; // rearm the time event } else { // one-shot time event: automatically disarm t->ctr = 0U; // auto-disarm // mark time event 't' as NOT linked t->super.refCtr_ &= (uint8_t)(~(uint8_t)QTE_IS_LINKED); QS_BEGIN_PRE_(QS_QF_TIMEEVT_AUTO_DISARM, act->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->prio) QS_TIME_PRE_(); // timestamp QS_OBJ_PRE_(t); // the time event object QS_SIG_PRE_(t->super.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 QACTIVE_POST(act, &t->super, sender); // asserts if queue overflows QS_CRIT_ENTRY(); QS_MEM_SYS(); } // update the linked list of time events for (;;) { t = prev->next; // advance down the time evt. list // end of the list? if (t == (QTimeEvt *)0) { // any new time events armed since the last QTimeEvt_tick_()? if (QTimeEvt_timeEvtHead_[tickRate].act != (void *)0) { // sanity check Q_ASSERT_INCRIT(830, prev != (QTimeEvt *)0); prev->next = (QTimeEvt *)QTimeEvt_timeEvtHead_[tickRate].act; QTimeEvt_timeEvtHead_[tickRate].act = (void *)0; t = prev->next; // switch to the new list } else { break; // all currently armed time evts. processed } } // time event scheduled for removal? if (t->ctr == 0U) { prev->next = t->next; // mark time event 't' as NOT linked t->super.refCtr_ &= (uint8_t)(~(uint8_t)QTE_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(); //! @class QHsmDummy //! @extends QHsm //! @public @memberof QHsmDummy //! @public @memberof QHsmDummy static struct QAsmVtable const vtable = { &QHsmDummy_init_, &QHsmDummy_dispatch_ #ifdef Q_SPY ,&QHsm_getStateHandler_ #endif }; me->super.vptr = &vtable; // hook the vptr //! @private @memberof QHsmDummy //! @private @memberof QHsmDummy Q_UNUSED_PAR(par); #ifdef Q_SPY if ((QS_priv_.flags & 0x01U) == 0U) { QS_priv_.flags |= 0x01U; QS_FUN_DICTIONARY(&QHsm_top); } #endif QS_CRIT_STAT QS_CRIT_ENTRY(); QS_MEM_SYS(); QS_BEGIN_PRE_(QS_QEP_STATE_INIT, qs_id) QS_OBJ_PRE_(me); // this state machine object QS_FUN_PRE_(me->state.fun); // the source state QS_FUN_PRE_(me->temp.fun); // the target of the initial tran. QS_END_PRE_() QS_MEM_APP(); QS_CRIT_EXIT(); //! @private @memberof QHsmDummy //! @private @memberof QHsmDummy QS_CRIT_STAT QS_CRIT_ENTRY(); QS_MEM_SYS(); QS_BEGIN_PRE_(QS_QEP_DISPATCH, qs_id) QS_TIME_PRE_(); // time stamp QS_SIG_PRE_(e->sig); // the signal of the event QS_OBJ_PRE_(me); // this state machine object QS_FUN_PRE_(me->state.fun); // the current state QS_END_PRE_() QS_MEM_APP(); QS_CRIT_EXIT(); //! @class QActiveDummy //! @extends QActive //! @public @memberof QActiveDummy //! @public @memberof QActiveDummy // superclass' ctor QActive_ctor(&me->super, Q_STATE_CAST(0)); static struct QAsmVtable const vtable = { &QActiveDummy_init_, &QActiveDummy_dispatch_ #ifdef Q_SPY ,&QHsm_getStateHandler_ #endif }; me->super.super.vptr = &vtable; // hook the vptr //! @private @memberof QActiveDummy //! @private @memberof QActiveDummy Q_UNUSED_PAR(qs_id); QHsmDummy_init_(me, par, ((QActive const *)me)->prio); //! @private @memberof QActiveDummy //! @private @memberof QActiveDummy Q_UNUSED_PAR(qs_id); QHsmDummy_dispatch_(me, e, ((QActive const *)me)->prio); //! @private @memberof QActiveDummy //! @private @memberof QActiveDummy 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 (QEvt_getPoolId_(e) != 0U) { QEvt_refCtr_inc_(e); } uint_fast8_t const rec = (status ? (uint_fast8_t)QS_QF_ACTIVE_POST : (uint_fast8_t)QS_QF_ACTIVE_POST_ATTEMPT); QS_BEGIN_PRE_(rec, me->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_(me); // this active object QS_2U8_PRE_(QEvt_getPoolId_(e), e->refCtr_); // poolId & 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 ('me->prio') is set if (QS_LOC_CHECK_(me->prio)) { QS_onTestPost(sender, me, 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; // the event is "posted" correctly //! @private @memberof QActiveDummy //! @private @memberof QActiveDummy 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 (QEvt_getPoolId_(e) != 0U) { QEvt_refCtr_inc_(e); } QS_BEGIN_PRE_(QS_QF_ACTIVE_POST_LIFO, me->prio) QS_TIME_PRE_(); // timestamp QS_SIG_PRE_(e->sig); // the signal of this event QS_OBJ_PRE_(me); // this active object QS_2U8_PRE_(QEvt_getPoolId_(e), e->refCtr_); // poolId & 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 ('me->prio') is set if (QS_LOC_CHECK_(me->prio)) { QS_onTestPost((QActive *)0, me, 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 (QS_onStartup(arg_)) (QS_onCleanup()) (QS_output()) (QS_rx_input()) (QS_glbFilter_((int_fast16_t)(rec_))) (QS_locFilter_((int_fast16_t)(qs_id_))) \ if (QS_GLB_CHECK_(rec_) && QS_LOC_CHECK_(qs_id_)) { \ QS_CRIT_STAT \ QS_CRIT_ENTRY(); \ QS_MEM_SYS(); \ QS_beginRec_((uint_fast8_t)(rec_)); \ QS_TIME_PRE_(); { } \ QS_endRec_(); \ QS_MEM_APP(); \ QS_CRIT_EXIT(); \ } (QS_onFlush()) \ if (QS_GLB_CHECK_(rec_) && QS_LOC_CHECK_(qs_id_)) { \ QS_beginRec_((uint_fast8_t)(rec_)); \ QS_TIME_PRE_(); { } \ QS_endRec_(); \ } \ (((uint_fast8_t)QS_filt_.glb[(uint_fast8_t)(rec_) >> 3U] \ & ((uint_fast8_t)1U << ((uint_fast8_t)(rec_) & 7U))) != 0U) \ (((uint_fast8_t)QS_filt_.loc[(uint_fast8_t)(qs_id_) >> 3U] \ & ((uint_fast8_t)1U << ((uint_fast8_t)(qs_id_) & 7U))) != 0U) ((void)0) \ (QS_u8_fmt_((uint8_t)(((width_) << 4U) & 0x7U) | (uint8_t)QS_I8_ENUM_T, \ (data_))) \ (QS_u8_fmt_((uint8_t)(((width_) << 4)) | (uint8_t)QS_U8_T, (data_))) \ (QS_u16_fmt_((uint8_t)(((width_) << 4)) | (uint8_t)QS_I16_T, (data_))) \ (QS_u16_fmt_((uint8_t)(((width_) << 4)) | (uint8_t)QS_U16_T, (data_))) \ (QS_u32_fmt_((uint8_t)(((width_) << 4)) | (uint8_t)QS_I32_T, (data_))) \ (QS_u32_fmt_((uint8_t)(((width_) << 4)) | (uint8_t)QS_U32_T, (data_))) \ (QS_u64_fmt_((uint8_t)(((width_) << 4)) | (uint8_t)QS_I64_T, (data_))) \ (QS_u64_fmt_((uint8_t)(((width_) << 4)) | (uint8_t)QS_U64_T, (data_))) \ (QS_f32_fmt_((uint8_t)(((width_) << 4)) | (uint8_t)QS_F32_T, (data_))) \ (QS_f64_fmt_((uint8_t)(((width_) << 4)) | (uint8_t)QS_F64_T, (data_))) (QS_str_fmt_((str_))) (QS_mem_fmt_((mem_), (size_))) \ (QS_u8_fmt_((uint8_t)(0x80U | ((group_) << 4U)) | (uint8_t)QS_I8_ENUM_T,\ (uint8_t)(value_))) (QS_u16_raw_(QS_onGetTime())) (QS_u32_raw_(QS_onGetTime())) (QS_u16_fmt_(QS_OBJ_T, (uint16_t)(obj_))) (QS_u32_fmt_(QS_OBJ_T, (uint32_t)(obj_))) (QS_u64_fmt_(QS_OBJ_T, (uint64_t)(obj_))) (QS_u16_fmt_(QS_FUN_T, (uint16_t)(fun_))) (QS_u32_fmt_(QS_FUN_T, (uint32_t)(fun_))) (QS_u64_fmt_(QS_FUN_T, (uint64_t)(fun_))) \ QS_u8_fmt_(QS_SIG_T, (sig_)); \ QS_obj_raw_(obj_) \ QS_u16_fmt_(QS_SIG_T, (sig_)); \ QS_obj_raw_(obj_) \ QS_u32_fmt_(QS_SIG_T, (sig_)); \ QS_obj_raw_(obj_) \ (QS_sig_dict_pre_((QSignal)(sig_), (obj_), #sig_)) \ (QS_obj_dict_pre_((obj_), #obj_)) \ (QS_obj_arr_dict_pre_((obj_), (idx_), #obj_)) \ (QS_fun_dict_pre_((void (*)(void))(fun_), #fun_)) \ (QS_usr_dict_pre_((rec_), #rec_)) \ (QS_enum_dict_pre_((value_), (group_), #value_)) (QS_rxPut((b_))) do { \ QS_BEGIN_PRE_(QS_QF_ISR_ENTRY, 0U) \ QS_TIME_PRE_(); \ QS_2u8_raw_(isrnest, prio); \ QS_END_PRE_() \ } do { \ QS_BEGIN_PRE_(QS_QF_ISR_EXIT, 0U) \ QS_TIME_PRE_(); \ QS_2u8_raw_(isrnest, prio); \ QS_END_PRE_() \ } while (false) (code_) \ (QS_assertion_pre_((module_), (id_), (delay_))) ((uint16_t)0xFFFFU) ((uint8_t)7U) ((uint8_t)0x0FU) QF_CRIT_STAT QF_CRIT_ENTRY() QF_CRIT_EXIT() QF_MEM_SYS() QF_MEM_APP() #ifndef QSAFE_H_ #define QSAFE_H_ #ifdef __cplusplus extern "C" { #endif // QF-FuSa enabled =========================================================== #ifndef Q_UNSAFE #ifndef QF_CRIT_STAT #define QF_CRIT_STAT #endif #ifndef QF_CRIT_ENTRY #define QF_CRIT_ENTRY() ((void)0) #endif #ifndef QF_CRIT_EXIT #define QF_CRIT_EXIT() ((void)0) #endif $declare ${QP-FuSa::enabled} // QF-FuSa disabled ========================================================== #else $declare ${QP-FuSa::disabled} #endif //============================================================================ $declare1 ${QP-FuSa} #ifdef __cplusplus } #endif #endif // QSAFE_H_ #ifndef QP_H_ #define QP_H_ //============================================================================ #define QP_VERSION 730U #define QP_VERSION_STR "7.3.0" //! Encrypted current QP release (7.3.0) and date (2023-09-12) #define QP_RELEASE 0x765D9D25U //============================================================================ //! @cond INTERNAL #ifndef Q_SIGNAL_SIZE #define Q_SIGNAL_SIZE 2U #endif #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} $declare ${QF-macros} #endif // QP_H_ #ifndef QP_PKG_H_ #define QP_PKG_H_ $declare ${QF::QF-pkg} // Bitmasks are for the QTimeEvt::refCtr_ attribute (inherited from ::QEvt). // In ::QTimeEvt this attribute is NOT used for reference counting. #define QTE_IS_LINKED (1U << 7U) #define QTE_WAS_DISARMED (1U << 6U) #define QTE_TICK_RATE 0x0FU //! @private @memberof QEvt static inline void QEvt_refCtr_inc_(QEvt const *me) { ++((QEvt *)me)->refCtr_; } //! @private @memberof QEvt static inline void QEvt_refCtr_dec_(QEvt const *me) { --((QEvt *)me)->refCtr_; } #define QACTIVE_CAST_(ptr_) ((QActive *)(ptr_)) #define Q_UINTPTR_CAST_(ptr_) ((uintptr_t)(ptr_)) #endif // QP_PKG_H_ #ifndef QEQUEUE_H_ #define QEQUEUE_H_ #ifndef QF_EQUEUE_CTR_SIZE #define QF_EQUEUE_CTR_SIZE 1U #endif #if (QF_EQUEUE_CTR_SIZE == 1U) typedef uint8_t QEQueueCtr; #elif (QF_EQUEUE_CTR_SIZE == 2U) typedef uint16_t QEQueueCtr; #elif (QF_EQUEUE_CTR_SIZE == 4U) typedef uint32_t QEQueueCtr; #else #error "QF_EQUEUE_CTR_SIZE defined incorrectly, expected 1U, 2U, or 4U" #endif struct QEvt; // forward declaration $declare ${QF::QEQueue} #endif // QEQUEUE_H_ #ifndef QMPOOL_H_ #define QMPOOL_H_ #ifndef QF_MPOOL_SIZ_SIZE #define QF_MPOOL_SIZ_SIZE 2U #endif #ifndef QF_MPOOL_CTR_SIZE #define QF_MPOOL_CTR_SIZE 2U #endif #if (QF_MPOOL_SIZ_SIZE == 1U) typedef uint8_t QMPoolSize; #elif (QF_MPOOL_SIZ_SIZE == 2U) typedef uint16_t QMPoolSize; #elif (QF_MPOOL_SIZ_SIZE == 4U) typedef uint32_t QMPoolSize; #else #error "QF_MPOOL_SIZ_SIZE defined incorrectly, expected 1U, 2U, or 4U" #endif #if (QF_MPOOL_CTR_SIZE == 1U) typedef uint8_t QMPoolCtr; #elif (QF_MPOOL_CTR_SIZE == 2U) typedef uint16_t QMPoolCtr; #elif (QF_MPOOL_CTR_SIZE == 4U) typedef uint32_t QMPoolCtr; #else #error "QF_MPOOL_CTR_SIZE defined incorrectly, expected 1U, 2U, or 4U" #endif #define QF_MPOOL_EL(evType_) struct { \ QFreeBlock sto_[((sizeof(evType_) - 1U) \ / sizeof(QFreeBlock)) + 1U]; } $declare ${QF::QFreeBlock} $declare ${QF::QMPool} #endif // QMPOOL_H_ #ifndef QV_H_ #define QV_H_ $declare ${QV::QV} $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_H_ #ifndef QK_H_ #define QK_H_ $declare ${QK::QK} $declare ${QK::QSchedStatus} $declare ${QK::QK-base} //============================================================================ // 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_H_ #ifndef QXK_H_ #define QXK_H_ $declare ${QXK::QXK} $declare ${QXK::QSchedStatus} $declare ${QXK::QXTHREAD_NO_TIMEOUT} $declare ${QXK::QXK-base} $declare ${QXK::QXThread} $declare ${QXK::QXSemaphore} $declare ${QXK::QXMutex} $declare ${QXK-macros} //============================================================================ // interface used only for internal implementation, but not in applications #ifdef QP_IMPL $declare ${QXK-impl} $declare ${QF_EPOOL-impl} enum QXK_TimeoutSigs { QXK_DELAY_SIG = 1, QXK_TIMEOUT_SIG }; #endif // QP_IMPL #endif // QXK_H_ #ifndef QS_H_ #define QS_H_ #ifndef Q_SPY #error "Q_SPY must be defined to include qs.h" #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 typedef struct { void const * locFilter_AP; //!< @deprecated uint8_t * buf; QSCtr end; QSCtr volatile head; QSCtr volatile tail; QSCtr volatile used; uint8_t volatile seq; uint8_t volatile chksum; uint8_t volatile critNest; uint8_t flags; } QS_Attr; extern QS_Attr QS_priv_; void QS_glbFilter_(int_fast16_t const filter); void QS_locFilter_(int_fast16_t const filter); void QS_beginRec_(uint_fast8_t const rec); void QS_endRec_(void); void QS_u8_raw_(uint8_t const d); void QS_2u8_raw_( uint8_t const d1, uint8_t const d2); void QS_u16_raw_(uint16_t const d); void QS_u32_raw_(uint32_t const d); void QS_u64_raw_(uint64_t const d); void QS_obj_raw_(void const * const obj); void QS_str_raw_(char const * const str); void QS_u8_fmt_( uint8_t const format, uint8_t const d); void QS_u16_fmt_( uint8_t const format, uint16_t const d); void QS_u32_fmt_( uint8_t const format, uint32_t const d); void QS_u64_fmt_( uint8_t const format, uint64_t const d); void QS_f32_fmt_( uint8_t const format, float32_t const f); void QS_f64_fmt_( uint8_t const format, float64_t const d); void QS_str_fmt_(char const * const str); void QS_mem_fmt_( uint8_t const * const blk, uint8_t const size); void QS_sig_dict_pre_( QSignal const sig, void const * const obj, char const * const name); void QS_obj_dict_pre_( void const * const obj, char const * const name); void QS_obj_arr_dict_pre_( void const * const obj, uint_fast16_t const idx, char const * const name); void QS_fun_dict_pre_( QSpyFunPtr const fun, char const * const name); void QS_usr_dict_pre_( enum_t const rec, char const * const name); void QS_enum_dict_pre_( enum_t const value, uint8_t const group, char const * const name); void QS_assertion_pre_( char const * const module, int_t const id, uint32_t const delay); void QS_target_info_pre_(uint8_t const isReset); //! @endcond //============================================================================ $declare ${QS::QS-TX} //============================================================================ //! @cond INTERNAL typedef struct { void * currObj[8]; uint8_t * buf; QSCtr end; QSCtr volatile head; QSCtr volatile tail; #ifdef Q_UTEST bool inTestLoop; #endif } QS_RxAttr; //! @static @private @memberof QS extern QS_RxAttr QS_rxPriv_; //! @endcond //============================================================================ $declare ${QS::QS-RX} //============================================================================ #ifdef Q_UTEST $declare ${QS::QUTest} #define QUTEST_ON_POST 124 //============================================================================ //! @cond INTERNAL typedef struct { struct QS_TProbe tpBuf[16]; uint8_t tpNum; QSTimeCtr testTime; QPSet readySet; QPSet readySet_dis; uint_fast8_t intLock; } QSTestAttr; extern QSTestAttr QS_tstPriv_; void QS_test_pause_(void); uint32_t QS_getTestProbe_(QSpyFunPtr const api); //! @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_) \ uint32_t const qs_tp_ = QS_getTestProbe_((void (*)(void))(fun_)); #define QS_TEST_PROBE(code_) \ if (qs_tp_ != 0U) { code_ } #define QS_TEST_PROBE_ID(id_, code_) \ if (qs_tp_ == (uint32_t)(id_)) { code_ } #define QS_TEST_PAUSE() (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() ((void)0) #endif // Q_UTEST #endif // QS_H_ #ifndef QS_DUMMY_H_ #define QS_DUMMY_H_ #ifdef Q_SPY #error "Q_SPY must NOT be defined to include qs_dummy.h" #endif #ifdef Q_UTEST #error "Q_UTEST must NOT be defined to include qs_dummy.h" #endif #define QS_INIT(arg_) ((uint8_t)1U) #define QS_EXIT() ((void)0) #define QS_DUMP() ((void)0) #define QS_GLB_FILTER(rec_) ((void)0) #define QS_LOC_FILTER(qs_id_) ((void)0) #define QS_GET_BYTE(pByte_) ((uint16_t)0xFFFFU) #define QS_GET_BLOCK(pSize_) ((uint8_t *)0) #define QS_BEGIN_ID(rec_, qs_id_) if (false) { #define QS_END() } #define QS_BEGIN_INCRIT(rec_, qs_id_) if (false) { #define QS_END_INCRIT() } #define QS_I8(width_, data_) ((void)0) #define QS_U8(width_, data_) ((void)0) #define QS_I16(width_, data_) ((void)0) #define QS_U16(width_, data_) ((void)0) #define QS_I32(width_, data_) ((void)0) #define QS_U32(width_, data_) ((void)0) #define QS_F32(width_, data_) ((void)0) #define QS_F64(width_, data_) ((void)0) #define QS_I64(width_, data_) ((void)0) #define QS_U64(width_, data_) ((void)0) #define QS_ENUM(group_, value_) ((void)0) #define QS_STR(str_) ((void)0) #define QS_MEM(mem_, size_) ((void)0) #define QS_SIG(sig_, obj_) ((void)0) #define QS_OBJ(obj_) ((void)0) #define QS_FUN(fun_) ((void)0) #define QS_SIG_DICTIONARY(sig_, obj_) ((void)0) #define QS_OBJ_DICTIONARY(obj_) ((void)0) #define QS_OBJ_ARR_DICTIONARY(obj_, idx_) ((void)0) #define QS_FUN_DICTIONARY(fun_) ((void)0) #define QS_USR_DICTIONARY(rec_) ((void)0) #define QS_ENUM_DICTIONARY(value_, group_) ((void)0) #define QS_ASSERTION(module_, loc_, delay_) ((void)0) #define QS_FLUSH() ((void)0) #define QS_TEST_PROBE_DEF(fun_) #define QS_TEST_PROBE(code_) #define QS_TEST_PROBE_ID(id_, code_) #define QS_TEST_PAUSE() ((void)0) #define QS_OUTPUT() ((void)0) #define QS_RX_INPUT() ((void)0) #define QS_ONLY(code_) ((void)0) //============================================================================ // interface used only for internal implementation, but not in applications #ifdef QP_IMPL // predefined QS trace records #define QS_BEGIN_PRE_(rec_, qs_id_) if (false) { #define QS_END_PRE_() } #define QS_U8_PRE_(data_) ((void)0) #define QS_2U8_PRE_(data1_, data2_) ((void)0) #define QS_U16_PRE_(data_) ((void)0) #define QS_U32_PRE_(data_) ((void)0) #define QS_TIME_PRE_() ((void)0) #define QS_SIG_PRE_(sig_) ((void)0) #define QS_EVS_PRE_(size_) ((void)0) #define QS_OBJ_PRE_(obj_) ((void)0) #define QS_FUN_PRE_(fun_) ((void)0) #define QS_EQC_PRE_(ctr_) ((void)0) #define QS_MPC_PRE_(ctr_) ((void)0) #define QS_MPS_PRE_(size_) ((void)0) #define QS_TEC_PRE_(ctr_) ((void)0) #define QS_CRIT_STAT #define QS_CRIT_ENTRY() ((void)0) #define QS_CRIT_EXIT() ((void)0) #define QS_MEM_SYS() ((void)0) #define QS_MEM_APP() ((void)0) #define QS_TR_CRIT_ENTRY() ((void)0) #define QS_TR_CRIT_EXIT() ((void)0) #define QS_TR_ISR_ENTRY(isrnest_, prio_) ((void)0) #define QS_TR_ISR_EXIT(isrnest_, prio_) ((void)0) #endif // QP_IMPL #endif // QS_DUMMY_H_ #ifndef QS_PKG_H_ #define QS_PKG_H_ //============================================================================ //! @cond INTERNAL //! QS received record types (RX channel) enum QSpyRxRecords { 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 }; //---------------------------------------------------------------------------- #define QS_FRAME 0x7EU #define QS_ESC 0x7DU #define QS_ESC_XOR 0x20U #define QS_GOOD_CHKSUM 0xFFU //---------------------------------------------------------------------------- #define QS_BEGIN_PRE_(rec_, qs_id_) \ if (QS_GLB_CHECK_(rec_) && QS_LOC_CHECK_(qs_id_)) { \ QS_beginRec_((uint_fast8_t)(rec_)); #define QS_END_PRE_() QS_endRec_(); } #define QS_U8_PRE_(data_) (QS_u8_raw_((uint8_t)(data_))) #define QS_2U8_PRE_(data1_, data2_) \ (QS_2u8_raw_((uint8_t)(data1_), (uint8_t)(data2_))) #define QS_U16_PRE_(data_) (QS_u16_raw_((uint16_t)(data_))) #define QS_U32_PRE_(data_) (QS_u32_raw_((uint32_t)(data_))) #define QS_STR_PRE_(msg_) (QS_str_raw_((msg_))) #define QS_OBJ_PRE_(obj_) (QS_obj_raw_(obj_)) #if (!defined Q_SIGNAL_SIZE || (Q_SIGNAL_SIZE == 1U)) #define QS_SIG_PRE_(sig_) (QS_u8_raw_((uint8_t)sig_)) #elif (Q_SIGNAL_SIZE == 2U) #define QS_SIG_PRE_(sig_) (QS_u16_raw_((uint16_t)sig_)) #elif (Q_SIGNAL_SIZE == 4U) #define QS_SIG_PRE_(sig_) (QS_u32_raw_((uint32_t)sig_)) #endif #if (!defined QS_FUN_PTR_SIZE || (QS_FUN_PTR_SIZE == 2U)) #define QS_FUN_PRE_(fun_) (QS_u16_raw_((uint16_t)(fun_))) #elif (QS_FUN_PTR_SIZE == 4U) #define QS_FUN_PRE_(fun_) (QS_u32_raw_((uint32_t)(fun_))) #elif (QS_FUN_PTR_SIZE == 8U) #define QS_FUN_PRE_(fun_) (QS_u64_raw_((uint64_t)(fun_))) #else #define QS_FUN_PRE_(fun_) (QS_u32_raw_((uint32_t)(fun_))) #endif //---------------------------------------------------------------------------- #if (!defined QF_EQUEUE_CTR_SIZE || (QF_EQUEUE_CTR_SIZE == 1U)) #define QS_EQC_PRE_(ctr_) QS_u8_raw_((uint8_t)(ctr_)) #elif (QF_EQUEUE_CTR_SIZE == 2U) #define QS_EQC_PRE_(ctr_) QS_u16_raw_((uint16_t)(ctr_)) #elif (QF_EQUEUE_CTR_SIZE == 4U) #define QS_EQC_PRE_(ctr_) QS_u32_raw_((uint32_t)(ctr_)) #endif #if (!defined QF_EVENT_SIZ_SIZE || (QF_EVENT_SIZ_SIZE == 1U)) #define QS_EVS_PRE_(size_) QS_u8_raw_((uint8_t)(size_)) #elif (QF_EVENT_SIZ_SIZE == 2U) #define QS_EVS_PRE_(size_) QS_u16_raw_((uint16_t)(size_)) #elif (QF_EVENT_SIZ_SIZE == 4U) #define QS_EVS_PRE_(size_) QS_u32_raw_((uint32_t)(size_)) #endif #if (!defined QF_MPOOL_SIZ_SIZE || (QF_MPOOL_SIZ_SIZE == 1U)) #define QS_MPS_PRE_(size_) QS_u8_raw_((uint8_t)(size_)) #elif (QF_MPOOL_SIZ_SIZE == 2U) #define QS_MPS_PRE_(size_) QS_u16_raw_((uint16_t)(size_)) #elif (QF_MPOOL_SIZ_SIZE == 4U) #define QS_MPS_PRE_(size_) QS_u32_raw_((uint32_t)(size_)) #endif #if (!defined QF_MPOOL_CTR_SIZE || (QF_MPOOL_CTR_SIZE == 1U)) #define QS_MPC_PRE_(ctr_) QS_u8_raw_((uint8_t)(ctr_)) #elif (QF_MPOOL_CTR_SIZE == 2U) #define QS_MPC_PRE_(ctr_) QS_u16_raw_((uint16_t)(ctr_)) #elif (QF_MPOOL_CTR_SIZE == 4U) #define QS_MPC_PRE_(ctr_) QS_u32_raw_((uint16_t)(ctr_)) #endif #if (!defined QF_TIMEEVT_CTR_SIZE || (QF_TIMEEVT_CTR_SIZE == 1U)) #define QS_TEC_PRE_(ctr_) QS_u8_raw_((uint8_t)(ctr_)) #elif (QF_TIMEEVT_CTR_SIZE == 2U) #define QS_TEC_PRE_(ctr_) QS_u16_raw_((uint16_t)(ctr_)) #elif (QF_TIMEEVT_CTR_SIZE == 4U) #define QS_TEC_PRE_(ctr_) QS_u32_raw_((uint32_t)(ctr_)) #endif //---------------------------------------------------------------------------- #define QS_INSERT_BYTE_(b_) \ buf[head] = (b_); \ ++head; \ if (head == end) { \ head = 0U; \ } #define QS_INSERT_ESC_BYTE_(b_) \ chksum = (uint8_t)(chksum + (b_)); \ if (((b_) != QS_FRAME) && ((b_) != QS_ESC)) { \ QS_INSERT_BYTE_(b_) \ } \ else { \ QS_INSERT_BYTE_(QS_ESC) \ QS_INSERT_BYTE_((uint8_t)((b_) ^ QS_ESC_XOR))\ ++QS_priv_.used; \ } //---------------------------------------------------------------------------- #if (defined Q_UTEST) && (Q_UTEST != 0) void QS_processTestEvts_(void); #endif // Q_UTEST != 0 //! @endcond //============================================================================ #endif // QS_PKG_H_ #ifndef QSTAMP_H_ #define QSTAMP_H_ extern char const Q_BUILD_DATE[12]; extern char const Q_BUILD_TIME[9]; #endif // QSTAMP_H_ #ifndef QPC_H_ #define QPC_H_ #ifdef __cplusplus extern "C" { #endif //============================================================================ #include "qp_port.h" // QP port from the port directory #include "qsafe.h" // QP Functional Safety (FuSa) Subsystem #ifdef Q_SPY // software tracing enabled? #include "qs_port.h" // QS/C port from the port directory #else #include "qs_dummy.h" // QS/C dummy (inactive) interface #endif //============================================================================ #ifndef QP_API_VERSION #define QP_API_VERSION 0 #endif // #ifndef QP_API_VERSION //============================================================================ // QP API compatibility layer... #if (QP_API_VERSION < 730) //! @deprecated plain 'char' is no longer forbidden in MISRA-C:2023 typedef char char_t; //! @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_) //! @static @public @memberof QF //! @deprecated static inline void QF_psInit( QSubscrList * const subscrSto, enum_t const maxSignal) { QActive_psInit(subscrSto, maxSignal); } //! @deprecated instead use: QASM_INIT() #define QHSM_INIT(me_, par_, qs_id_) QASM_INIT((me_), (par_), (qs_id_)) //! @deprecated instead use: QASM_DISPATCH() #define QHSM_DISPATCH(me_, e_, qs_id_) QASM_DISPATCH((me_), (e_), (qs_id_)) //============================================================================ #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_) ((void)0) //! @deprecated enable the QS local filter for AO (active objects) #define QS_FILTER_AO_OBJ(obj_) ((void)0) //! @deprecated enable the QS local filter for MP (memory pool) object #define QS_FILTER_MP_OBJ(obj_) ((void)0) //! @deprecated enable the QS local filter for EQ (event queue) object #define QS_FILTER_EQ_OBJ(obj_) ((void)0) //! @deprecated enable the QS local filter for TE (time event) object #define QS_FILTER_TE_OBJ(obj_) ((void)0) #ifdef Q_SPY //! @deprecated local Filter for a generic application object `obj_`. #define QS_FILTER_AP_OBJ(obj_) (QS_filt_.loc_AP = (obj_)) //! @deprecated begin of a user QS record, instead use QS_BEGIN_ID() #define QS_BEGIN(rec_, obj_) \ if (((QS_filt_.glb[(uint_fast8_t)(rec_) >> 3U] \ & (1U << ((uint_fast8_t)(rec_) & 7U))) != 0U) \ && ((QS_priv_.locFilter_AP == (void *)0) \ || (QS_priv_.locFilter_AP == (obj_)))) \ { \ QS_CRIT_STAT \ QS_CRIT_ENTRY(); \ QS_beginRec_((uint_fast8_t)(rec_)); \ QS_TIME_PRE_(); { //! @deprecated Output formatted uint32_t to the QS record #define QS_U32_HEX(width_, data_) \ (QS_u32_fmt_((uint8_t)(((width_) << 4)) | QS_HEX_FMT, (data_))) #else #define QS_FILTER_AP_OBJ(obj_) ((void)0) #define QS_BEGIN(rec_, obj_) if (false) { #define QS_U32_HEX(width_, data_) ((void)0) #endif //============================================================================ #if (QP_API_VERSION < 660) //! @deprecated casting to QXThreadHandler //! instead use: the new signature of QXThreadHandler and don't cast #define Q_XTHREAD_CAST(handler_) ((QXThreadHandler)(handler_)) //============================================================================ #if (QP_API_VERSION < 580) //! @deprecated instead use: QASM_INIT() #define QMSM_INIT(me_, par_, qs_id_) QASM_INIT((me_), (par_), (qs_id_)) //! @deprecated instead use: QASM_DISPATCH() #define QMSM_DISPATCH(me_, e_, qs_id_) QASM_DISPATCH((me_), (e_), (qs_id_)) #endif // QP_API_VERSION < 580 #endif // QP_API_VERSION < 660 #endif // QP_API_VERSION < 691 #endif // QP_API_VERSION < 700 #ifdef __cplusplus } #endif #endif // QPC_H_ #define QP_IMPL // this is QP implementation #include "qp_port.h" // QP port #include "qp_pkg.h" // QP package-scope interface #include "qsafe.h" // QP Functional Safety (FuSa) Subsystem #ifdef Q_SPY // QS software tracing enabled? #include "qs_port.h" // QS port #include "qs_pkg.h" // QS facilities for pre-defined trace records #else #include "qs_dummy.h" // disable the QS software tracing #endif // Q_SPY Q_DEFINE_THIS_MODULE("qep_hsm") $define ${QEP::QP_versionStr[8]} //============================================================================ //! @cond INTERNAL $define ${QEP::QEvt::reserved_[4]} enum { // maximum depth of state nesting in a HSM (including the top level), // must be >= 3 QHSM_MAX_NEST_DEPTH_ = 6 }; // helper macro to handle reserved event in an QHsm #define QHSM_RESERVED_EVT_(state_, sig_) \ ((*(state_))(me, &QEvt_reserved_[(sig_)])) // helper macro to trace state entry #define QS_STATE_ENTRY_(state_, qs_id_) \ QS_CRIT_ENTRY(); \ QS_MEM_SYS(); \ QS_BEGIN_PRE_(QS_QEP_STATE_ENTRY, (qs_id_)) \ QS_OBJ_PRE_(me); \ QS_FUN_PRE_(state_); \ QS_END_PRE_() \ QS_MEM_APP(); \ QS_CRIT_EXIT() // helper macro to trace state exit #define QS_STATE_EXIT_(state_, qs_id_) \ QS_CRIT_ENTRY(); \ QS_MEM_SYS(); \ QS_BEGIN_PRE_(QS_QEP_STATE_EXIT, (qs_id_)) \ QS_OBJ_PRE_(me); \ QS_FUN_PRE_(state_); \ QS_END_PRE_() \ QS_MEM_APP(); \ QS_CRIT_EXIT() //! @endcond $define ${QEP::QHsm} #define QP_IMPL // this is QP implementation #include "qp_port.h" // QP port #include "qp_pkg.h" // QP package-scope interface #include "qsafe.h" // QP Functional Safety (FuSa) Subsystem #ifdef Q_SPY // QS software tracing enabled? #include "qs_port.h" // QS port #include "qs_pkg.h" // QS facilities for pre-defined trace records #else #include "qs_dummy.h" // disable the QS software tracing #endif // Q_SPY //============================================================================ //! @cond INTERNAL Q_DEFINE_THIS_MODULE("qep_msm") // top-state object for QMsm-style state machines static struct QMState const l_msm_top_s = { (struct QMState *)0, Q_STATE_CAST(0), Q_ACTION_CAST(0), Q_ACTION_CAST(0), Q_ACTION_CAST(0) }; enum { // maximum depth of entry levels in a MSM for tran. to history. QMSM_MAX_ENTRY_DEPTH_ = 4 }; //! @endcond //============================================================================ $define ${QEP::QMsm} #define QP_IMPL // this is QP implementation #include "qp_port.h" // QP port #include "qp_pkg.h" // QP package-scope interface #include "qsafe.h" // QP Functional Safety (FuSa) Subsystem #ifdef Q_SPY // QS software tracing enabled? #include "qs_port.h" // QS port #include "qs_pkg.h" // QS facilities for pre-defined trace records #else #include "qs_dummy.h" // disable the QS software tracing #endif // Q_SPY //Q_DEFINE_THIS_MODULE("qf_act") $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.h" // QP port #include "qp_pkg.h" // QP package-scope interface #include "qsafe.h" // QP Functional Safety (FuSa) Subsystem #ifdef Q_SPY // QS software tracing enabled? #include "qs_port.h" // QS port #include "qs_pkg.h" // QS facilities for pre-defined trace records #else #include "qs_dummy.h" // disable the QS software tracing #endif // Q_SPY Q_DEFINE_THIS_MODULE("qf_actq") //============================================================================ $define ${QF::QActive::post_} $define ${QF::QActive::postLIFO_} $define ${QF::QActive::get_} $define ${QF::QF-base::getQueueMin} $define ${QF::QTicker} #define QP_IMPL // this is QP implementation #include "qp_port.h" // QP port #include "qp_pkg.h" // QP package-scope interface #include "qsafe.h" // QP Functional Safety (FuSa) Subsystem #ifdef Q_SPY // QS software tracing enabled? #include "qs_port.h" // QS port #include "qs_pkg.h" // QS facilities for pre-defined trace records #else #include "qs_dummy.h" // disable the QS software tracing #endif // Q_SPY Q_DEFINE_THIS_MODULE("qf_defer") $define ${QF::QActive::defer} $define ${QF::QActive::recall} $define ${QF::QActive::flushDeferred} #define QP_IMPL // this is QP implementation #include "qp_port.h" // QP port #include "qp_pkg.h" // QP package-scope interface #include "qsafe.h" // QP Functional Safety (FuSa) Subsystem #ifdef Q_SPY // QS software tracing enabled? #include "qs_port.h" // QS port #include "qs_pkg.h" // QS facilities for pre-defined trace records #else #include "qs_dummy.h" // disable the QS software tracing #endif // Q_SPY #if (QF_MAX_EPOOL > 0U) // mutable events configured? Q_DEFINE_THIS_MODULE("qf_dyn") $define ${QF::QF-dyn} #endif // (QF_MAX_EPOOL > 0U) mutable events configured #define QP_IMPL // this is QP implementation #include "qp_port.h" // QP port #include "qp_pkg.h" // QP package-scope interface #include "qsafe.h" // QP Functional Safety (FuSa) Subsystem #ifdef Q_SPY // QS software tracing enabled? #include "qs_port.h" // QS port #include "qs_pkg.h" // QS facilities for pre-defined trace records #else #include "qs_dummy.h" // disable the QS software tracing #endif // Q_SPY Q_DEFINE_THIS_MODULE("qf_mem") $define ${QF::QMPool} #define QP_IMPL // this is QP implementation #include "qp_port.h" // QP port #include "qp_pkg.h" // QP package-scope interface #include "qsafe.h" // QP Functional Safety (FuSa) Subsystem #ifdef Q_SPY // QS software tracing enabled? #include "qs_port.h" // QS port #include "qs_pkg.h" // QS facilities for pre-defined trace records #else #include "qs_dummy.h" // disable the QS software tracing #endif // Q_SPY Q_DEFINE_THIS_MODULE("qf_qact") $define ${QF::QActive::ctor} $define ${QF::QActive::register_} $define ${QF::QActive::unregister_} #define QP_IMPL // this is QP implementation #include "qp_port.h" // QP port #include "qp_pkg.h" // QP package-scope interface #include "qsafe.h" // QP Functional Safety (FuSa) Subsystem #ifdef Q_SPY // QS software tracing enabled? #include "qs_port.h" // QS port #include "qs_pkg.h" // QS facilities for pre-defined trace records #else #include "qs_dummy.h" // disable the QS software tracing #endif // Q_SPY //Q_DEFINE_THIS_MODULE("qf_qmact") $define ${QF::QMActive} #define QP_IMPL // this is QP implementation #include "qp_port.h" // QP port #include "qp_pkg.h" // QP package-scope interface #include "qsafe.h" // QP Functional Safety (FuSa) Subsystem #ifdef Q_SPY // QS software tracing enabled? #include "qs_port.h" // QS port #include "qs_pkg.h" // QS facilities for pre-defined trace records #else #include "qs_dummy.h" // disable the QS software tracing #endif // Q_SPY Q_DEFINE_THIS_MODULE("qf_qeq") $define ${QF::QEQueue} #define QP_IMPL // this is QP implementation #include "qp_port.h" // QP port #include "qp_pkg.h" // QP package-scope interface #include "qsafe.h" // QP Functional Safety (FuSa) Subsystem #ifdef Q_SPY // QS software tracing enabled? #include "qs_port.h" // QS port #include "qs_pkg.h" // QS facilities for pre-defined trace records #else #include "qs_dummy.h" // disable the QS software tracing #endif // Q_SPY Q_DEFINE_THIS_MODULE("qf_ps") $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.h" // QP port #include "qp_pkg.h" // QP package-scope interface #include "qsafe.h" // QP Functional Safety (FuSa) Subsystem #ifdef Q_SPY // QS software tracing enabled? #include "qs_port.h" // QS port #include "qs_pkg.h" // QS facilities for pre-defined trace records #else #include "qs_dummy.h" // disable the QS software tracing #endif // Q_SPY Q_DEFINE_THIS_MODULE("qf_time") $define ${QF::QTimeEvt} #define QP_IMPL // this is QP implementation #include "qp_port.h" // QP port #include "qp_pkg.h" // QP package-scope internal interface #include "qsafe.h" // QP Functional Safety (FuSa) Subsystem #ifdef Q_SPY // QS software tracing enabled? #include "qs_port.h" // QS port #include "qs_pkg.h" // QS facilities for pre-defined trace records #else #include "qs_dummy.h" // disable the QS software tracing #endif // Q_SPY // protection against including this source file in a wrong project #ifndef QV_H_ #error "Source file included in a project NOT based on the QV kernel" #endif // QV_H_ Q_DEFINE_THIS_MODULE("qv") $define ${QV::QV-base} $define ${QV::QF-cust} $define ${QV::QActive} #define QP_IMPL // this is QP implementation #include "qp_port.h" // QP port #include "qp_pkg.h" // QP package-scope internal interface #include "qsafe.h" // QP Functional Safety (FuSa) Subsystem #ifdef Q_SPY // QS software tracing enabled? #include "qs_port.h" // QS port #include "qs_pkg.h" // QS facilities for pre-defined trace records #else #include "qs_dummy.h" // disable the QS software tracing #endif // Q_SPY // protection against including this source file in a wrong project #ifndef QK_H_ #error "Source file included in a project NOT based on the QK kernel" #endif // QK_H_ Q_DEFINE_THIS_MODULE("qk") $define ${QK::QK-base} $define ${QK::QF-cust} $define ${QK::QActive} #define QP_IMPL // this is QP implementation #include "qp_port.h" // QP port #include "qp_pkg.h" // QP package-scope interface #include "qsafe.h" // QP Functional Safety (FuSa) Subsystem #ifdef Q_SPY // QS software tracing enabled? #include "qs_port.h" // QS port #include "qs_pkg.h" // QS facilities for pre-defined trace records #else #include "qs_dummy.h" // disable the QS software tracing #endif // Q_SPY // protection against including this source file in a wrong project #ifndef QXK_H_ #error "Source file included in a project NOT based on the QXK kernel" #endif // QXK_H_ Q_DEFINE_THIS_MODULE("qxk") $define ${QXK::QXK-base} $define ${QXK::QF-cust} $define ${QXK::QActive} #define QP_IMPL // this is QP implementation #include "qp_port.h" // QP port #include "qp_pkg.h" // QP package-scope interface #include "qsafe.h" // QP Functional Safety (FuSa) Subsystem #ifdef Q_SPY // QS software tracing enabled? #include "qs_port.h" // QS port #include "qs_pkg.h" // QS facilities for pre-defined trace records #else #include "qs_dummy.h" // disable the QS software tracing #endif // Q_SPY // protection against including this source file in a wrong project #ifndef QXK_H_ #error "Source file included in a project NOT based on the QXK kernel" #endif // QXK_H_ Q_DEFINE_THIS_MODULE("qxk_mutex") $define ${QXK::QXMutex} #define QP_IMPL // this is QP implementation #include "qp_port.h" // QP port #include "qp_pkg.h" // QP package-scope interface #include "qsafe.h" // QP Functional Safety (FuSa) Subsystem #ifdef Q_SPY // QS software tracing enabled? #include "qs_port.h" // QS port #include "qs_pkg.h" // QS facilities for pre-defined trace records #else #include "qs_dummy.h" // disable the QS software tracing #endif // Q_SPY // protection against including this source file in a wrong project #ifndef QXK_H_ #error "Source file included in a project NOT based on the QXK kernel" #endif // QXK_H_ Q_DEFINE_THIS_MODULE("qxk_sema") $define ${QXK::QXSemaphore} #define QP_IMPL // this is QP implementation #include "qp_port.h" // QP port #include "qp_pkg.h" // QP package-scope interface #include "qsafe.h" // QP Functional Safety (FuSa) Subsystem #ifdef Q_SPY // QS software tracing enabled? #include "qs_port.h" // QS port #include "qs_pkg.h" // QS facilities for pre-defined trace records #else #include "qs_dummy.h" // disable the QS software tracing #endif // Q_SPY // protection against including this source file in a wrong project #ifndef QXK_H_ #error "Source file included in a project NOT based on the QXK kernel" #endif // QXK_H_ Q_DEFINE_THIS_MODULE("qxk_xthr") $define ${QXK::QXThread} #define QP_IMPL // this is QP implementation #include "qs_port.h" // QS port #include "qs_pkg.h" // QS package-scope interface #include "qstamp.h" // QP time-stamp #include "qsafe.h" // QP Functional Safety (FuSa) Subsystem Q_DEFINE_THIS_MODULE("qs") // ensure that the predefined records don't overlap the // user records (application-specific). Q_ASSERT_STATIC((enum_t)QS_PRE_MAX <= (enum_t)QS_USER); $define ${QS::QS-TX} #ifndef QF_MEM_ISOLATE $define ${QS::filters} #endif //============================================================================ //! @cond INTERNAL //! @static @private @memberof QS QS_Attr QS_priv_; //............................................................................ void QS_glbFilter_(int_fast16_t const filter) { bool const isRemove = (filter < 0); uint8_t const rec = isRemove ? (uint8_t)(-filter) : (uint8_t)filter; switch (rec) { case (uint8_t)QS_ALL_RECORDS: { uint8_t const tmp = (isRemove ? 0x00U : 0xFFU); // set all global filters (partially unrolled loop) for (uint_fast8_t i = 0U; i < Q_DIM(QS_filt_.glb); i += 4U) { QS_filt_.glb[i ] = tmp; QS_filt_.glb[i + 1U] = tmp; QS_filt_.glb[i + 2U] = tmp; QS_filt_.glb[i + 3U] = tmp; } if (isRemove) { // leave the "not maskable" filters enabled, // see qs.h, Miscellaneous QS records (not maskable) QS_filt_.glb[0] = 0x01U; QS_filt_.glb[6] = 0x40U; QS_filt_.glb[7] = 0xFCU; QS_filt_.glb[8] = 0x7FU; } else { // never turn the last 3 records on (0x7D, 0x7E, 0x7F) QS_filt_.glb[15] = 0x1FU; } break; } case (uint8_t)QS_SM_RECORDS: if (isRemove) { QS_filt_.glb[0] &= (uint8_t)(~0xFEU & 0xFFU); QS_filt_.glb[1] &= (uint8_t)(~0x03U & 0xFFU); QS_filt_.glb[6] &= (uint8_t)(~0x80U & 0xFFU); QS_filt_.glb[7] &= (uint8_t)(~0x03U & 0xFFU); } else { QS_filt_.glb[0] |= 0xFEU; QS_filt_.glb[1] |= 0x03U; QS_filt_.glb[6] |= 0x80U; QS_filt_.glb[7] |= 0x03U; } break; case (uint8_t)QS_AO_RECORDS: if (isRemove) { QS_filt_.glb[1] &= (uint8_t)(~0xFCU & 0xFFU); QS_filt_.glb[2] &= (uint8_t)(~0x07U & 0xFFU); QS_filt_.glb[5] &= (uint8_t)(~0x20U & 0xFFU); } else { QS_filt_.glb[1] |= 0xFCU; QS_filt_.glb[2] |= 0x07U; QS_filt_.glb[5] |= 0x20U; } break; case (uint8_t)QS_EQ_RECORDS: if (isRemove) { QS_filt_.glb[2] &= (uint8_t)(~0x78U & 0xFFU); QS_filt_.glb[5] &= (uint8_t)(~0x40U & 0xFFU); } else { QS_filt_.glb[2] |= 0x78U; QS_filt_.glb[5] |= 0x40U; } break; case (uint8_t)QS_MP_RECORDS: if (isRemove) { QS_filt_.glb[3] &= (uint8_t)(~0x03U & 0xFFU); QS_filt_.glb[5] &= (uint8_t)(~0x80U & 0xFFU); } else { QS_filt_.glb[3] |= 0x03U; QS_filt_.glb[5] |= 0x80U; } break; case (uint8_t)QS_QF_RECORDS: if (isRemove) { QS_filt_.glb[2] &= (uint8_t)(~0x80U & 0xFFU); QS_filt_.glb[3] &= (uint8_t)(~0xFCU & 0xFFU); QS_filt_.glb[4] &= (uint8_t)(~0xC0U & 0xFFU); QS_filt_.glb[5] &= (uint8_t)(~0x1FU & 0xFFU); } else { QS_filt_.glb[2] |= 0x80U; QS_filt_.glb[3] |= 0xFCU; QS_filt_.glb[4] |= 0xC0U; QS_filt_.glb[5] |= 0x1FU; } break; case (uint8_t)QS_TE_RECORDS: if (isRemove) { QS_filt_.glb[4] &= (uint8_t)(~0x3FU & 0xFFU); } else { QS_filt_.glb[4] |= 0x3FU; } break; case (uint8_t)QS_SC_RECORDS: if (isRemove) { QS_filt_.glb[6] &= (uint8_t)(~0x3FU & 0xFFU); } else { QS_filt_.glb[6] |= 0x3FU; } break; case (uint8_t)QS_SEM_RECORDS: if (isRemove) { QS_filt_.glb[8] &= (uint8_t)(~0x80U & 0xFFU); QS_filt_.glb[9] &= (uint8_t)(~0x07U & 0xFFU); } else { QS_filt_.glb[8] |= 0x80U; QS_filt_.glb[9] |= 0x07U; } break; case (uint8_t)QS_MTX_RECORDS: if (isRemove) { QS_filt_.glb[9] &= (uint8_t)(~0xF8U & 0xFFU); QS_filt_.glb[10] &= (uint8_t)(~0x01U & 0xFFU); } else { QS_filt_.glb[9] |= 0xF8U; QS_filt_.glb[10] |= 0x01U; } break; case (uint8_t)QS_U0_RECORDS: if (isRemove) { QS_filt_.glb[12] &= (uint8_t)(~0xF0U & 0xFFU); QS_filt_.glb[13] &= (uint8_t)(~0x01U & 0xFFU); } else { QS_filt_.glb[12] |= 0xF0U; QS_filt_.glb[13] |= 0x01U; } break; case (uint8_t)QS_U1_RECORDS: if (isRemove) { QS_filt_.glb[13] &= (uint8_t)(~0x3EU & 0xFFU); } else { QS_filt_.glb[13] |= 0x3EU; } break; case (uint8_t)QS_U2_RECORDS: if (isRemove) { QS_filt_.glb[13] &= (uint8_t)(~0xC0U & 0xFFU); QS_filt_.glb[14] &= (uint8_t)(~0x07U & 0xFFU); } else { QS_filt_.glb[13] |= 0xC0U; QS_filt_.glb[14] |= 0x07U; } break; case (uint8_t)QS_U3_RECORDS: if (isRemove) { QS_filt_.glb[14] &= (uint8_t)(~0xF8U & 0xFFU); } else { QS_filt_.glb[14] |= 0xF8U; } break; case (uint8_t)QS_U4_RECORDS: if (isRemove) { QS_filt_.glb[15] &= (uint8_t)(~0x1FU & 0xFFU); } else { QS_filt_.glb[15] |= 0x1FU; } break; case (uint8_t)QS_UA_RECORDS: if (isRemove) { QS_filt_.glb[12] &= (uint8_t)(~0xF0U & 0xFFU); QS_filt_.glb[13] = 0U; QS_filt_.glb[14] = 0U; QS_filt_.glb[15] &= (uint8_t)(~0x1FU & 0xFFU); } else { QS_filt_.glb[12] |= 0xF0U; QS_filt_.glb[13] |= 0xFFU; QS_filt_.glb[14] |= 0xFFU; QS_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) { QS_filt_.glb[rec >> 3U] &= (uint8_t)(~(1U << (rec & 7U)) & 0xFFU); } else { QS_filt_.glb[rec >> 3U] |= (1U << (rec & 7U)); // never turn the last 3 records on (0x7D, 0x7E, 0x7F) QS_filt_.glb[15] &= 0x1FU; } break; } } } //............................................................................ void QS_locFilter_(int_fast16_t const filter) { bool const isRemove = (filter < 0); uint8_t const qs_id = isRemove ? (uint8_t)(-filter) : (uint8_t)filter; uint8_t const tmp = (isRemove ? 0x00U : 0xFFU); uint_fast8_t i; switch (qs_id) { case (uint8_t)QS_ALL_IDS: // set all local filters (partially unrolled loop) for (i = 0U; i < Q_DIM(QS_filt_.loc); i += 4U) { QS_filt_.loc[i ] = tmp; QS_filt_.loc[i + 1U] = tmp; QS_filt_.loc[i + 2U] = tmp; QS_filt_.loc[i + 3U] = tmp; } break; case (uint8_t)QS_AO_IDS: for (i = 0U; i < 8U; i += 4U) { QS_filt_.loc[i ] = tmp; QS_filt_.loc[i + 1U] = tmp; QS_filt_.loc[i + 2U] = tmp; QS_filt_.loc[i + 3U] = tmp; } break; case (uint8_t)QS_EP_IDS: i = 8U; QS_filt_.loc[i ] = tmp; QS_filt_.loc[i + 1U] = tmp; break; case (uint8_t)QS_AP_IDS: i = 12U; QS_filt_.loc[i ] = tmp; QS_filt_.loc[i + 1U] = tmp; QS_filt_.loc[i + 2U] = tmp; QS_filt_.loc[i + 3U] = tmp; break; default: { QS_CRIT_STAT QS_CRIT_ENTRY(); // qs_id must be in range Q_ASSERT_INCRIT(310, qs_id < 0x7FU); QS_CRIT_EXIT(); if (isRemove) { QS_filt_.loc[qs_id >> 3U] &= (uint8_t)(~(1U << (qs_id & 7U)) & 0xFFU); } else { QS_filt_.loc[qs_id >> 3U] |= (1U << (qs_id & 7U)); } break; } } QS_filt_.loc[0] |= 0x01U; // leave QS_ID == 0 always on } //............................................................................ void QS_beginRec_(uint_fast8_t const rec) { uint8_t const b = (uint8_t)(QS_priv_.seq + 1U); uint8_t chksum = 0U; // reset the checksum uint8_t * const buf = QS_priv_.buf; // put in a temporary (register) QSCtr head = QS_priv_.head; // put in a temporary (register) QSCtr const end = QS_priv_.end; // put in a temporary (register) QS_priv_.seq = b; // store the incremented sequence num QS_priv_.used += 2U; // 2 bytes about to be added QS_INSERT_ESC_BYTE_(b) chksum = (uint8_t)(chksum + rec); // update checksum QS_INSERT_BYTE_((uint8_t)rec) // rec byte does not need escaping QS_priv_.head = head; // save the head QS_priv_.chksum = chksum; // save the checksum } //............................................................................ void QS_endRec_(void) { uint8_t * const buf = QS_priv_.buf; // put in a temporary (register) QSCtr head = QS_priv_.head; QSCtr const end = QS_priv_.end; uint8_t b = QS_priv_.chksum; b ^= 0xFFU; // invert the bits in the checksum QS_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) ++QS_priv_.used; // account for the ESC byte } QS_INSERT_BYTE_(QS_FRAME) // do not escape this QS_FRAME QS_priv_.head = head; // save the head // overrun over the old data? if (QS_priv_.used > end) { QS_priv_.used = end; // the whole buffer is used QS_priv_.tail = head; // shift the tail to the old data } } //............................................................................ void QS_u8_raw_(uint8_t const d) { uint8_t chksum = QS_priv_.chksum; // put in a temporary (register) uint8_t * const buf = QS_priv_.buf; // put in a temporary (register) QSCtr head = QS_priv_.head; // put in a temporary (register) QSCtr const end = QS_priv_.end; // put in a temporary (register) QS_priv_.used += 1U; // 1 byte about to be added QS_INSERT_ESC_BYTE_(d) QS_priv_.head = head; // save the head QS_priv_.chksum = chksum; // save the checksum } //............................................................................ void QS_2u8_raw_( uint8_t const d1, uint8_t const d2) { uint8_t chksum = QS_priv_.chksum; // put in a temporary (register) uint8_t * const buf = QS_priv_.buf; // put in a temporary (register) QSCtr head = QS_priv_.head; // put in a temporary (register) QSCtr const end = QS_priv_.end; // put in a temporary (register) QS_priv_.used += 2U; // 2 bytes are about to be added QS_INSERT_ESC_BYTE_(d1) QS_INSERT_ESC_BYTE_(d2) QS_priv_.head = head; // save the head QS_priv_.chksum = chksum; // save the checksum } //............................................................................ void QS_u16_raw_(uint16_t const d) { uint8_t chksum = QS_priv_.chksum; // put in a temporary (register) uint8_t * const buf = QS_priv_.buf; // put in a temporary (register) QSCtr head = QS_priv_.head; // put in a temporary (register) QSCtr const end = QS_priv_.end; // put in a temporary (register) uint16_t x = d; QS_priv_.used += 2U; // 2 bytes are about to be added QS_INSERT_ESC_BYTE_((uint8_t)x) x >>= 8U; QS_INSERT_ESC_BYTE_((uint8_t)x) QS_priv_.head = head; // save the head QS_priv_.chksum = chksum; // save the checksum } //............................................................................ void QS_u32_raw_(uint32_t const d) { uint8_t chksum = QS_priv_.chksum; // put in a temporary (register) uint8_t * const buf = QS_priv_.buf; // put in a temporary (register) QSCtr head = QS_priv_.head; // put in a temporary (register) QSCtr const end = QS_priv_.end; // put in a temporary (register) uint32_t x = d; QS_priv_.used += 4U; // 4 bytes are about to be added for (uint_fast8_t i = 4U; i != 0U; --i) { QS_INSERT_ESC_BYTE_((uint8_t)x) x >>= 8U; } QS_priv_.head = head; // save the head QS_priv_.chksum = chksum; // save the checksum } //............................................................................ void QS_obj_raw_(void const * const obj) { #if (QS_OBJ_PTR_SIZE == 1U) QS_u8_raw_((uint8_t)obj); #elif (QS_OBJ_PTR_SIZE == 2U) QS_u16_raw_((uint16_t)obj); #elif (QS_OBJ_PTR_SIZE == 4U) QS_u32_raw_((uint32_t)obj); #elif (QS_OBJ_PTR_SIZE == 8U) QS_u64_raw_((uint64_t)obj); #else QS_u32_raw_((uint32_t)obj); #endif } //............................................................................ void QS_str_raw_(char const * const str) { uint8_t chksum = QS_priv_.chksum; // put in a temporary (register) uint8_t * const buf = QS_priv_.buf; // put in a temporary (register) QSCtr head = QS_priv_.head; // put in a temporary (register) QSCtr const end = QS_priv_.end; // put in a temporary (register) QSCtr used = QS_priv_.used; // put in a temporary (register) for (char const *s = str; *s != '\0'; ++s) { chksum += (uint8_t)*s; // update checksum QS_INSERT_BYTE_((uint8_t)*s) // ASCII char doesn't need escaping ++used; } QS_INSERT_BYTE_((uint8_t)'\0') // zero-terminate the string ++used; QS_priv_.head = head; // save the head QS_priv_.chksum = chksum; // save the checksum QS_priv_.used = used; // save # of used buffer space } //............................................................................ void QS_u8_fmt_( uint8_t const format, uint8_t const d) { uint8_t chksum = QS_priv_.chksum; // put in a temporary (register) uint8_t * const buf = QS_priv_.buf; // put in a temporary (register) QSCtr head = QS_priv_.head; // put in a temporary (register) QSCtr const end = QS_priv_.end; // put in a temporary (register) QS_priv_.used += 2U; // 2 bytes about to be added QS_INSERT_ESC_BYTE_(format) QS_INSERT_ESC_BYTE_(d) QS_priv_.head = head; // save the head QS_priv_.chksum = chksum; // save the checksum } //............................................................................ void QS_u16_fmt_( uint8_t const format, uint16_t const d) { uint8_t chksum = QS_priv_.chksum; // put in a temporary (register) uint8_t * const buf = QS_priv_.buf; // put in a temporary (register) QSCtr head = QS_priv_.head; // put in a temporary (register) QSCtr const end = QS_priv_.end; // put in a temporary (register) uint8_t b = (uint8_t)d; QS_priv_.used += 3U; // 3 bytes about to be added QS_INSERT_ESC_BYTE_(format) QS_INSERT_ESC_BYTE_(b) b = (uint8_t)(d >> 8U); QS_INSERT_ESC_BYTE_(b) QS_priv_.head = head; // save the head QS_priv_.chksum = chksum; // save the checksum } //............................................................................ void QS_u32_fmt_( uint8_t const format, uint32_t const d) { uint8_t chksum = QS_priv_.chksum; // put in a temporary (register) uint8_t * const buf = QS_priv_.buf; // put in a temporary (register) QSCtr head = QS_priv_.head; // put in a temporary (register) QSCtr const end = QS_priv_.end; // put in a temporary (register) uint32_t x = d; QS_priv_.used += 5U; // 5 bytes about to be added QS_INSERT_ESC_BYTE_(format) // insert the format byte // insert 4 bytes... for (uint_fast8_t i = 4U; i != 0U; --i) { QS_INSERT_ESC_BYTE_((uint8_t)x) x >>= 8U; } QS_priv_.head = head; // save the head QS_priv_.chksum = chksum; // save the checksum } //............................................................................ void QS_str_fmt_(char const * const str) { uint8_t chksum = QS_priv_.chksum; uint8_t * const buf = QS_priv_.buf; // put in a temporary (register) QSCtr head = QS_priv_.head; // put in a temporary (register) QSCtr const end = QS_priv_.end; // put in a temporary (register) QSCtr used = QS_priv_.used; // put in a temporary (register) used += 2U; // account for the format byte and the terminating-0 QS_INSERT_BYTE_((uint8_t)QS_STR_T) chksum += (uint8_t)QS_STR_T; for (char const *s = str; *s != '\0'; ++s) { QS_INSERT_BYTE_((uint8_t)*s) // ASCII char doesn't need escaping chksum += (uint8_t)*s; // update checksum ++used; } QS_INSERT_BYTE_(0U) // zero-terminate the string QS_priv_.head = head; // save the head QS_priv_.chksum = chksum; // save the checksum QS_priv_.used = used; // save # of used buffer space } //............................................................................ void QS_mem_fmt_( uint8_t const * const blk, uint8_t const size) { uint8_t chksum = QS_priv_.chksum; uint8_t * const buf = QS_priv_.buf; // put in a temporary (register) QSCtr head = QS_priv_.head; // put in a temporary (register) QSCtr const end = QS_priv_.end; // put in a temporary (register) uint8_t const *pb = blk; QS_priv_.used += ((QSCtr)size + 2U); // size+2 bytes to be added QS_INSERT_BYTE_((uint8_t)QS_MEM_T) chksum += (uint8_t)QS_MEM_T; QS_INSERT_ESC_BYTE_(size) // output the 'size' # bytes for (uint8_t len = size; len > 0U; --len) { QS_INSERT_ESC_BYTE_(*pb) ++pb; } QS_priv_.head = head; // save the head QS_priv_.chksum = chksum; // save the checksum } //............................................................................ void QS_sig_dict_pre_( QSignal const sig, void const * const obj, char const * const name) { QS_CRIT_STAT QS_CRIT_ENTRY(); QS_MEM_SYS(); QS_beginRec_((uint_fast8_t)QS_SIG_DICT); QS_SIG_PRE_(sig); QS_OBJ_PRE_(obj); QS_STR_PRE_((*name == '&') ? &name[1] : name); QS_endRec_(); QS_MEM_APP(); QS_CRIT_EXIT(); QS_onFlush(); } //............................................................................ void QS_obj_dict_pre_( void const * const obj, char const * const name) { QS_CRIT_STAT QS_CRIT_ENTRY(); QS_MEM_SYS(); QS_beginRec_((uint_fast8_t)QS_OBJ_DICT); QS_OBJ_PRE_(obj); QS_STR_PRE_((*name == '&') ? &name[1] : name); QS_endRec_(); QS_MEM_APP(); QS_CRIT_EXIT(); QS_onFlush(); } //............................................................................ void QS_obj_arr_dict_pre_( void const * const obj, uint_fast16_t const idx, char const * const name) { QS_CRIT_STAT QS_CRIT_ENTRY(); Q_REQUIRE_INCRIT(400, idx < 1000U); QS_CRIT_EXIT(); // format idx into a char buffer as "xxx\0" uint8_t idx_str[4]; uint_fast16_t tmp = idx; uint8_t i; idx_str[3] = 0U; // zero-terminate idx_str[2] = (uint8_t)((uint8_t)'0' + (tmp % 10U)); tmp /= 10U; idx_str[1] = (uint8_t)((uint8_t)'0' + (tmp % 10U)); if (idx_str[1] == (uint8_t)'0') { i = 2U; } else { tmp /= 10U; idx_str[0] = (uint8_t)((uint8_t)'0' + (tmp % 10U)); if (idx_str[0] == (uint8_t)'0') { i = 1U; } else { i = 0U; } } uint8_t j = ((*name == '&') ? 1U : 0U); QS_CRIT_ENTRY(); QS_MEM_SYS(); QS_beginRec_((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 QS_endRec_(); QS_MEM_APP(); QS_CRIT_EXIT(); QS_onFlush(); } //............................................................................ void QS_fun_dict_pre_( QSpyFunPtr const fun, char const * const name) { QS_CRIT_STAT QS_CRIT_ENTRY(); QS_MEM_SYS(); QS_beginRec_((uint_fast8_t)QS_FUN_DICT); QS_FUN_PRE_(fun); QS_STR_PRE_((*name == '&') ? &name[1] : name); QS_endRec_(); QS_MEM_APP(); QS_CRIT_EXIT(); QS_onFlush(); } //............................................................................ void QS_usr_dict_pre_( enum_t const rec, char const * const name) { QS_CRIT_STAT QS_CRIT_ENTRY(); QS_MEM_SYS(); QS_beginRec_((uint_fast8_t)QS_USR_DICT); QS_U8_PRE_(rec); QS_STR_PRE_(name); QS_endRec_(); QS_MEM_APP(); QS_CRIT_EXIT(); QS_onFlush(); } //............................................................................ void QS_enum_dict_pre_( enum_t const value, uint8_t const group, char const * const name) { QS_CRIT_STAT QS_CRIT_ENTRY(); QS_MEM_SYS(); QS_beginRec_((uint_fast8_t)QS_ENUM_DICT); QS_2U8_PRE_(value, group); QS_STR_PRE_(name); QS_endRec_(); QS_MEM_APP(); QS_CRIT_EXIT(); QS_onFlush(); } //............................................................................ void QS_assertion_pre_( char const * const module, int_t const id, uint32_t const delay) { // NOTE: called in a critical section QS_beginRec_((uint_fast8_t)QS_ASSERT_FAIL); QS_TIME_PRE_(); QS_U16_PRE_(id); QS_STR_PRE_((module != (char *)0) ? module : "?"); QS_endRec_(); QS_onFlush(); for (uint32_t volatile delay_ctr = delay; delay_ctr > 0U; --delay_ctr) {} QS_onCleanup(); } //............................................................................ void QS_target_info_pre_(uint8_t const isReset) { // NOTE: called in a critical section static uint8_t const ZERO = (uint8_t)'0'; static uint8_t const * const TIME = (uint8_t const *)&Q_BUILD_TIME[0]; static uint8_t const * const DATE = (uint8_t const *)&Q_BUILD_DATE[0]; static union { uint16_t u16; uint8_t u8[2]; } endian_test; endian_test.u16 = 0x0102U; QS_beginRec_((uint_fast8_t)QS_TARGET_INFO); QS_U8_PRE_(isReset); QS_U16_PRE_(((endian_test.u8[0] == 0x01U) // big endian? ? (0x8000U | QP_VERSION) : QP_VERSION)); // target endianness + version number // send the object sizes... QS_U8_PRE_(Q_SIGNAL_SIZE | (QF_EVENT_SIZ_SIZE << 4U)); #ifdef QF_EQUEUE_CTR_SIZE QS_U8_PRE_(QF_EQUEUE_CTR_SIZE | (QF_TIMEEVT_CTR_SIZE << 4U)); #else QS_U8_PRE_(QF_TIMEEVT_CTR_SIZE << 4U); #endif // QF_EQUEUE_CTR_SIZE #ifdef QF_MPOOL_CTR_SIZE QS_U8_PRE_(QF_MPOOL_SIZ_SIZE | (QF_MPOOL_CTR_SIZE << 4U)); #else QS_U8_PRE_(0U); #endif // QF_MPOOL_CTR_SIZE QS_U8_PRE_(QS_OBJ_PTR_SIZE | (QS_FUN_PTR_SIZE << 4U)); QS_U8_PRE_(QS_TIME_SIZE); // send the limits... QS_U8_PRE_(QF_MAX_ACTIVE); QS_U8_PRE_(QF_MAX_EPOOL | (QF_MAX_TICK_RATE << 4U)); // send the build time in three bytes (sec, min, hour)... QS_U8_PRE_((10U * (uint8_t)(TIME[6] - ZERO)) + (uint8_t)(TIME[7] - ZERO)); QS_U8_PRE_((10U * (uint8_t)(TIME[3] - ZERO)) + (uint8_t)(TIME[4] - ZERO)); if (Q_BUILD_TIME[0] == ' ') { QS_U8_PRE_(TIME[1] - ZERO); } else { QS_U8_PRE_((10U * (uint8_t)(TIME[0] - ZERO)) + (uint8_t)(TIME[1] - ZERO)); } // send the build date in three bytes (day, month, year) ... if (Q_BUILD_DATE[4] == ' ') { QS_U8_PRE_(DATE[5] - ZERO); } else { QS_U8_PRE_((10U * (uint8_t)(DATE[4] - ZERO)) + (uint8_t)(DATE[5] - ZERO)); } // convert the 3-letter month to a number 1-12 ... uint8_t b; switch ((int_t)DATE[0] + (int_t)DATE[1] + (int_t)DATE[2]) { case (int_t)'J' + (int_t)'a' + (int_t)'n': b = 1U; break; case (int_t)'F' + (int_t)'e' + (int_t)'b': b = 2U; break; case (int_t)'M' + (int_t)'a' + (int_t)'r': b = 3U; break; case (int_t)'A' + (int_t)'p' + (int_t)'r': b = 4U; break; case (int_t)'M' + (int_t)'a' + (int_t)'y': b = 5U; break; case (int_t)'J' + (int_t)'u' + (int_t)'n': b = 6U; break; case (int_t)'J' + (int_t)'u' + (int_t)'l': b = 7U; break; case (int_t)'A' + (int_t)'u' + (int_t)'g': b = 8U; break; case (int_t)'S' + (int_t)'e' + (int_t)'p': b = 9U; break; case (int_t)'O' + (int_t)'c' + (int_t)'t': b = 10U; break; case (int_t)'N' + (int_t)'o' + (int_t)'v': b = 11U; break; case (int_t)'D' + (int_t)'e' + (int_t)'c': b = 12U; break; default: b = 0U; break; } QS_U8_PRE_(b); // store the month QS_U8_PRE_((10U * (uint8_t)(DATE[9] - ZERO)) + (uint8_t)(DATE[10] - ZERO)); QS_endRec_(); } //! @endcond #define QP_IMPL // this is QP implementation #include "qs_port.h" // QS port #include "qs_pkg.h" // QS package-scope internal interface //============================================================================ //! @cond INTERNAL //! @static @private @memberof QS void QS_u64_raw_(uint64_t const d) { uint8_t chksum = QS_priv_.chksum; uint8_t * const buf = QS_priv_.buf; QSCtr head = QS_priv_.head; QSCtr const end = QS_priv_.end; QS_priv_.used += 8U; // 8 bytes are about to be added uint64_t u64 = d; for (uint_fast8_t i = 8U; i != 0U; --i) { uint8_t const b = (uint8_t)u64; QS_INSERT_ESC_BYTE_(b) u64 >>= 8U; } QS_priv_.head = head; // save the head QS_priv_.chksum = chksum; // save the checksum } //! @static @private @memberof QS void QS_u64_fmt_( uint8_t const format, uint64_t const d) { uint8_t chksum = QS_priv_.chksum; uint8_t * const buf = QS_priv_.buf; QSCtr head = QS_priv_.head; QSCtr const end = QS_priv_.end; QS_priv_.used += 9U; // 9 bytes are about to be added QS_INSERT_ESC_BYTE_(format) // insert the format byte // output 8 bytes of data... uint64_t u64 = d; for (uint_fast8_t i = 8U; i != 0U; --i) { uint8_t const b = (uint8_t)u64; QS_INSERT_ESC_BYTE_(b) u64 >>= 8U; } QS_priv_.head = head; // save the head QS_priv_.chksum = chksum; // save the checksum } //! @endcond #define QP_IMPL // this is QP implementation #include "qs_port.h" // QS port #include "qs_pkg.h" // QS package-scope internal interface //============================================================================ //! @cond INTERNAL //! @static @private @memberof QS void QS_f32_fmt_( uint8_t const format, float32_t const f) { union F32Rep { float32_t f; uint32_t u; } fu32; // the internal binary representation uint8_t chksum = QS_priv_.chksum; // put in a temporary (register) uint8_t * const buf = QS_priv_.buf; QSCtr head = QS_priv_.head; QSCtr const end = QS_priv_.end; uint_fast8_t i; fu32.f = f; // assign the binary representation QS_priv_.used += 5U; // 5 bytes about to be added QS_INSERT_ESC_BYTE_(format) // insert the format byte // insert 4 bytes... for (i = 4U; i != 0U; --i) { QS_INSERT_ESC_BYTE_((uint8_t)fu32.u) fu32.u >>= 8U; } QS_priv_.head = head; // save the head QS_priv_.chksum = chksum; // save the checksum } //! @static @private @memberof QS void QS_f64_fmt_( uint8_t const format, float64_t const d) { union F64Rep { float64_t d; uint32_t u[2]; } fu64; // the internal binary representation uint8_t chksum = QS_priv_.chksum; uint8_t * const buf = QS_priv_.buf; QSCtr head = QS_priv_.head; QSCtr const end = QS_priv_.end; uint32_t i; // static constant untion to detect endianness of the machine static union U32Rep { uint32_t u32; 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; } QS_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_((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_((uint8_t)fu64.u[1]) fu64.u[1] >>= 8U; } QS_priv_.head = head; // save the head QS_priv_.chksum = chksum; // save the checksum } //! @endcond #define QP_IMPL // this is QP implementation #include "qs_port.h" // QS port #include "qs_pkg.h" // QS package-scope interface #include "qsafe.h" // QP Functional Safety (FuSa) Subsystem //============================================================================ //! @cond INTERNAL Q_DEFINE_THIS_MODULE("qs_rx") #if (QS_OBJ_PTR_SIZE == 1U) typedef uint8_t QSObj; #elif (QS_OBJ_PTR_SIZE == 2U) typedef uint16_t QSObj; #elif (QS_OBJ_PTR_SIZE == 4U) typedef uint32_t QSObj; #elif (QS_OBJ_PTR_SIZE == 8U) typedef uint64_t QSObj; #endif typedef struct { uint32_t param1; uint32_t param2; uint32_t param3; uint8_t idx; uint8_t cmdId; } CmdVar; typedef struct { uint_fast8_t rate; } TickVar; typedef struct { uint16_t offs; uint8_t size; uint8_t num; uint8_t idx; } PeekVar; typedef struct { uint32_t data; uint16_t offs; uint8_t size; uint8_t num; uint8_t idx; uint8_t fill; } PokeVar; typedef struct { uint8_t data[16]; uint8_t idx; int8_t recId; // global/local } FltVar; typedef struct { QSObj addr; uint8_t idx; uint8_t kind; // see qs.h, enum QSpyObjKind int8_t recId; } ObjVar; typedef struct { QEvt *e; uint8_t *p; QSignal sig; uint16_t len; uint8_t prio; uint8_t idx; } EvtVar; //! extended-state variables for the current state //! //! @trace //! - @tr{DVR-QS-MC4-R19_02} static struct { union Variant { CmdVar cmd; TickVar tick; PeekVar peek; PokeVar poke; FltVar flt; ObjVar obj; EvtVar evt; #ifdef Q_UTEST struct QS_TProbe tp; #endif // Q_UTEST } var; uint8_t state; uint8_t esc; uint8_t seq; uint8_t chksum; } l_rx; enum { 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 }; // static helper functions... static void QS_rxParseData_(uint8_t const b); static void QS_rxHandleGoodFrame_(uint8_t const state); static void QS_rxHandleBadFrame_(uint8_t const state); static void QS_rxReportAck_(int8_t const recId); static void QS_rxReportError_(int8_t const code); static void QS_rxReportDone_(int8_t const recId); static void QS_queryCurrObj(uint8_t const obj_kind); static void QS_rxPoke_(void); //! Internal QS-RX macro to encapsulate tran. in the QS-RX FSM #define QS_RX_TRAN_(target_) (l_rx.state = (uint8_t)(target_)) QS_RxAttr QS_rxPriv_; //! @endcond //============================================================================ $define ${QS::QS-RX} //============================================================================ //! @cond INTERNAL static void QS_rxParseData_(uint8_t const b) { switch (l_rx.state) { case (uint8_t)WAIT4_SEQ: { ++l_rx.seq; if (l_rx.seq != b) { QS_rxReportError_(0x42); l_rx.seq = b; // update the sequence } QS_RX_TRAN_(WAIT4_REC); break; } case (uint8_t)WAIT4_REC: { switch (b) { case (uint8_t)QS_RX_INFO: QS_RX_TRAN_(WAIT4_INFO_FRAME); break; case (uint8_t)QS_RX_COMMAND: QS_RX_TRAN_(WAIT4_CMD_ID); break; case (uint8_t)QS_RX_RESET: QS_RX_TRAN_(WAIT4_RESET_FRAME); break; case (uint8_t)QS_RX_TICK: QS_RX_TRAN_(WAIT4_TICK_RATE); break; case (uint8_t)QS_RX_PEEK: if (QS_rxPriv_.currObj[AP_OBJ] != (void *)0) { l_rx.var.peek.offs = 0U; l_rx.var.peek.idx = 0U; QS_RX_TRAN_(WAIT4_PEEK_OFFS); } else { QS_rxReportError_((int8_t)QS_RX_PEEK); QS_RX_TRAN_(ERROR_STATE); } break; case (uint8_t)QS_RX_POKE: // intentionally fall-through case (uint8_t)QS_RX_FILL: l_rx.var.poke.fill = ((b == (uint8_t)QS_RX_FILL) ? 1U : 0U); if (QS_rxPriv_.currObj[AP_OBJ] != (void *)0) { l_rx.var.poke.offs = 0U; l_rx.var.poke.idx = 0U; QS_RX_TRAN_(WAIT4_POKE_OFFS); } else { QS_rxReportError_((l_rx.var.poke.fill != 0U) ? (int8_t)QS_RX_FILL : (int8_t)QS_RX_POKE); QS_RX_TRAN_(ERROR_STATE); } break; case (uint8_t)QS_RX_GLB_FILTER: // intentionally fall-through case (uint8_t)QS_RX_LOC_FILTER: l_rx.var.flt.recId = (int8_t)b; QS_RX_TRAN_(WAIT4_FILTER_LEN); break; case (uint8_t)QS_RX_AO_FILTER: // intentionally fall-through case (uint8_t)QS_RX_CURR_OBJ: l_rx.var.obj.recId = (int8_t)b; QS_RX_TRAN_(WAIT4_OBJ_KIND); break; case (uint8_t)QS_RX_QUERY_CURR: l_rx.var.obj.recId = (int8_t)QS_RX_QUERY_CURR; QS_RX_TRAN_(WAIT4_QUERY_KIND); break; case (uint8_t)QS_RX_EVENT: QS_RX_TRAN_(WAIT4_EVT_PRIO); break; #ifdef Q_UTEST case (uint8_t)QS_RX_TEST_SETUP: QS_RX_TRAN_(WAIT4_TEST_SETUP_FRAME); break; case (uint8_t)QS_RX_TEST_TEARDOWN: QS_RX_TRAN_(WAIT4_TEST_TEARDOWN_FRAME); break; case (uint8_t)QS_RX_TEST_CONTINUE: QS_RX_TRAN_(WAIT4_TEST_CONTINUE_FRAME); break; case (uint8_t)QS_RX_TEST_PROBE: if (QS_tstPriv_.tpNum < (uint8_t)(sizeof(QS_tstPriv_.tpBuf) / sizeof(QS_tstPriv_.tpBuf[0]))) { l_rx.var.tp.data = 0U; l_rx.var.tp.idx = 0U; QS_RX_TRAN_(WAIT4_TEST_PROBE_DATA); } else { // the # Test-Probes exceeded QS_rxReportError_((int8_t)QS_RX_TEST_PROBE); QS_RX_TRAN_(ERROR_STATE); } break; #endif // Q_UTEST default: QS_rxReportError_(0x43); QS_RX_TRAN_(ERROR_STATE); break; } break; } case (uint8_t)WAIT4_INFO_FRAME: { // keep ignoring the data until a frame is collected break; } case (uint8_t)WAIT4_CMD_ID: { l_rx.var.cmd.cmdId = b; l_rx.var.cmd.idx = 0U; l_rx.var.cmd.param1 = 0U; l_rx.var.cmd.param2 = 0U; l_rx.var.cmd.param3 = 0U; QS_RX_TRAN_(WAIT4_CMD_PARAM1); break; } case (uint8_t)WAIT4_CMD_PARAM1: { l_rx.var.cmd.param1 |= ((uint32_t)b << l_rx.var.cmd.idx); l_rx.var.cmd.idx += 8U; if (l_rx.var.cmd.idx == (8U * 4U)) { l_rx.var.cmd.idx = 0U; QS_RX_TRAN_(WAIT4_CMD_PARAM2); } break; } case (uint8_t)WAIT4_CMD_PARAM2: { l_rx.var.cmd.param2 |= ((uint32_t)b << l_rx.var.cmd.idx); l_rx.var.cmd.idx += 8U; if (l_rx.var.cmd.idx == (8U * 4U)) { l_rx.var.cmd.idx = 0U; QS_RX_TRAN_(WAIT4_CMD_PARAM3); } break; } case (uint8_t)WAIT4_CMD_PARAM3: { l_rx.var.cmd.param3 |= ((uint32_t)b << l_rx.var.cmd.idx); l_rx.var.cmd.idx += 8U; if (l_rx.var.cmd.idx == (8U * 4U)) { l_rx.var.cmd.idx = 0U; QS_RX_TRAN_(WAIT4_CMD_FRAME); } break; } case (uint8_t)WAIT4_CMD_FRAME: { // keep ignoring the data until a frame is collected break; } case (uint8_t)WAIT4_RESET_FRAME: { // keep ignoring the data until a frame is collected break; } case (uint8_t)WAIT4_TICK_RATE: { l_rx.var.tick.rate = (uint_fast8_t)b; QS_RX_TRAN_(WAIT4_TICK_FRAME); break; } case (uint8_t)WAIT4_TICK_FRAME: { // keep ignoring the data until a frame is collected break; } case (uint8_t)WAIT4_PEEK_OFFS: { if (l_rx.var.peek.idx == 0U) { l_rx.var.peek.offs = (uint16_t)b; l_rx.var.peek.idx += 8U; } else { l_rx.var.peek.offs |= (uint16_t)((uint16_t)b << 8U); QS_RX_TRAN_(WAIT4_PEEK_SIZE); } break; } case (uint8_t)WAIT4_PEEK_SIZE: { if ((b == 1U) || (b == 2U) || (b == 4U)) { l_rx.var.peek.size = b; QS_RX_TRAN_(WAIT4_PEEK_NUM); } else { QS_rxReportError_((int8_t)QS_RX_PEEK); QS_RX_TRAN_(ERROR_STATE); } break; } case (uint8_t)WAIT4_PEEK_NUM: { l_rx.var.peek.num = b; QS_RX_TRAN_(WAIT4_PEEK_FRAME); break; } case (uint8_t)WAIT4_PEEK_FRAME: { // keep ignoring the data until a frame is collected break; } case (uint8_t)WAIT4_POKE_OFFS: { if (l_rx.var.poke.idx == 0U) { l_rx.var.poke.offs = (uint16_t)b; l_rx.var.poke.idx = 1U; } else { l_rx.var.poke.offs |= (uint16_t)((uint16_t)b << 8U); QS_RX_TRAN_(WAIT4_POKE_SIZE); } break; } case (uint8_t)WAIT4_POKE_SIZE: { if ((b == 1U) || (b == 2U) || (b == 4U)) { l_rx.var.poke.size = b; QS_RX_TRAN_(WAIT4_POKE_NUM); } else { QS_rxReportError_((l_rx.var.poke.fill != 0U) ? (int8_t)QS_RX_FILL : (int8_t)QS_RX_POKE); QS_RX_TRAN_(ERROR_STATE); } break; } case (uint8_t)WAIT4_POKE_NUM: { if (b > 0U) { l_rx.var.poke.num = b; l_rx.var.poke.data = 0U; l_rx.var.poke.idx = 0U; QS_RX_TRAN_((l_rx.var.poke.fill != 0U) ? WAIT4_FILL_DATA : WAIT4_POKE_DATA); } else { QS_rxReportError_((l_rx.var.poke.fill != 0U) ? (int8_t)QS_RX_FILL : (int8_t)QS_RX_POKE); QS_RX_TRAN_(ERROR_STATE); } break; } case (uint8_t)WAIT4_FILL_DATA: { l_rx.var.poke.data |= ((uint32_t)b << l_rx.var.poke.idx); l_rx.var.poke.idx += 8U; if ((uint8_t)(l_rx.var.poke.idx >> 3U) == l_rx.var.poke.size) { QS_RX_TRAN_(WAIT4_FILL_FRAME); } break; } case (uint8_t)WAIT4_POKE_DATA: { l_rx.var.poke.data |= ((uint32_t)b << l_rx.var.poke.idx); l_rx.var.poke.idx += 8U; if ((uint8_t)(l_rx.var.poke.idx >> 3U) == l_rx.var.poke.size) { QS_rxPoke_(); --l_rx.var.poke.num; if (l_rx.var.poke.num == 0U) { QS_RX_TRAN_(WAIT4_POKE_FRAME); } } break; } case (uint8_t)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 (uint8_t)WAIT4_FILTER_LEN: { if (b == sizeof(l_rx.var.flt.data)) { l_rx.var.flt.idx = 0U; QS_RX_TRAN_(WAIT4_FILTER_DATA); } else { QS_rxReportError_(l_rx.var.flt.recId); QS_RX_TRAN_(ERROR_STATE); } break; } case (uint8_t)WAIT4_FILTER_DATA: { l_rx.var.flt.data[l_rx.var.flt.idx] = b; ++l_rx.var.flt.idx; if (l_rx.var.flt.idx == sizeof(l_rx.var.flt.data)) { QS_RX_TRAN_(WAIT4_FILTER_FRAME); } break; } case (uint8_t)WAIT4_FILTER_FRAME: { // keep ignoring the data until a frame is collected break; } case (uint8_t)WAIT4_OBJ_KIND: { if (b <= (uint8_t)SM_AO_OBJ) { l_rx.var.obj.kind = b; l_rx.var.obj.addr = 0U; l_rx.var.obj.idx = 0U; QS_RX_TRAN_(WAIT4_OBJ_ADDR); } else { QS_rxReportError_(l_rx.var.obj.recId); QS_RX_TRAN_(ERROR_STATE); } break; } case (uint8_t)WAIT4_OBJ_ADDR: { l_rx.var.obj.addr |= ((QSObj)b << l_rx.var.obj.idx); l_rx.var.obj.idx += 8U; if (l_rx.var.obj.idx == (uint8_t)(8U * QS_OBJ_PTR_SIZE)) { QS_RX_TRAN_(WAIT4_OBJ_FRAME); } break; } case (uint8_t)WAIT4_OBJ_FRAME: { // keep ignoring the data until a frame is collected break; } case (uint8_t)WAIT4_QUERY_KIND: { if (b < (uint8_t)MAX_OBJ) { l_rx.var.obj.kind = b; QS_RX_TRAN_(WAIT4_QUERY_FRAME); } else { QS_rxReportError_(l_rx.var.obj.recId); QS_RX_TRAN_(ERROR_STATE); } break; } case (uint8_t)WAIT4_QUERY_FRAME: { // keep ignoring the data until a frame is collected break; } case (uint8_t)WAIT4_EVT_PRIO: { l_rx.var.evt.prio = b; l_rx.var.evt.sig = 0U; l_rx.var.evt.idx = 0U; QS_RX_TRAN_(WAIT4_EVT_SIG); break; } case (uint8_t)WAIT4_EVT_SIG: { l_rx.var.evt.sig |= (QSignal)((uint32_t)b << l_rx.var.evt.idx); l_rx.var.evt.idx += 8U; if (l_rx.var.evt.idx == (uint8_t)(8U * Q_SIGNAL_SIZE)) { l_rx.var.evt.len = 0U; l_rx.var.evt.idx = 0U; QS_RX_TRAN_(WAIT4_EVT_LEN); } break; } case (uint8_t)WAIT4_EVT_LEN: { l_rx.var.evt.len |= (uint16_t)((uint32_t)b << l_rx.var.evt.idx); l_rx.var.evt.idx += 8U; if (l_rx.var.evt.idx == (8U * 2U)) { if ((l_rx.var.evt.len + sizeof(QEvt)) <= QF_poolGetMaxBlockSize()) { // report Ack before generating any other QS records QS_rxReportAck_((int8_t)QS_RX_EVENT); l_rx.var.evt.e = QF_newX_( ((uint_fast16_t)l_rx.var.evt.len + sizeof(QEvt)), 0U, // margin (enum_t)l_rx.var.evt.sig); if (l_rx.var.evt.e != (QEvt *)0) { // evt allocated? l_rx.var.evt.p = (uint8_t *)l_rx.var.evt.e; l_rx.var.evt.p = &l_rx.var.evt.p[sizeof(QEvt)]; if (l_rx.var.evt.len > 0U) { QS_RX_TRAN_(WAIT4_EVT_PAR); } else { QS_RX_TRAN_(WAIT4_EVT_FRAME); } } else { QS_rxReportError_((int8_t)QS_RX_EVENT); QS_RX_TRAN_(ERROR_STATE); } } else { QS_rxReportError_((int8_t)QS_RX_EVENT); QS_RX_TRAN_(ERROR_STATE); } } break; } case (uint8_t)WAIT4_EVT_PAR: { // event parameters *l_rx.var.evt.p = b; ++l_rx.var.evt.p; --l_rx.var.evt.len; if (l_rx.var.evt.len == 0U) { QS_RX_TRAN_(WAIT4_EVT_FRAME); } break; } case (uint8_t)WAIT4_EVT_FRAME: { // keep ignoring the data until a frame is collected break; } #ifdef Q_UTEST case (uint8_t)WAIT4_TEST_SETUP_FRAME: { // keep ignoring the data until a frame is collected break; } case (uint8_t)WAIT4_TEST_TEARDOWN_FRAME: { // keep ignoring the data until a frame is collected break; } case (uint8_t)WAIT4_TEST_CONTINUE_FRAME: { // keep ignoring the data until a frame is collected break; } case (uint8_t)WAIT4_TEST_PROBE_DATA: { l_rx.var.tp.data |= ((uint32_t)b << l_rx.var.tp.idx); l_rx.var.tp.idx += 8U; if (l_rx.var.tp.idx == (uint8_t)(8U * sizeof(uint32_t))) { l_rx.var.tp.addr = 0U; l_rx.var.tp.idx = 0U; QS_RX_TRAN_(WAIT4_TEST_PROBE_ADDR); } break; } case (uint8_t)WAIT4_TEST_PROBE_ADDR: { l_rx.var.tp.addr |= ((QSFun)b << l_rx.var.tp.idx); l_rx.var.tp.idx += 8U; if (l_rx.var.tp.idx == (uint8_t)(8U * QS_FUN_PTR_SIZE)) { QS_RX_TRAN_(WAIT4_TEST_PROBE_FRAME); } break; } case (uint8_t)WAIT4_TEST_PROBE_FRAME: { // keep ignoring the data until a frame is collected break; } #endif // Q_UTEST case (uint8_t)ERROR_STATE: { // keep ignoring the data until a good frame is collected break; } default: { // unexpected or unimplemented state QS_rxReportError_(0x45); QS_RX_TRAN_(ERROR_STATE); break; } } } //............................................................................ static void QS_rxHandleGoodFrame_(uint8_t const state) { uint8_t i; uint8_t *ptr; switch (state) { case WAIT4_INFO_FRAME: { // no need to report Ack or Done QS_target_info_pre_(0U); // send only Target info break; } case WAIT4_RESET_FRAME: { // no need to report Ack or Done, because Target resets 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: { QS_rxReportAck_((int8_t)QS_RX_COMMAND); QS_onCommand(l_rx.var.cmd.cmdId, l_rx.var.cmd.param1, l_rx.var.cmd.param2, l_rx.var.cmd.param3); #ifdef Q_UTEST #if Q_UTEST != 0 QS_processTestEvts_(); // process all events produced #endif // Q_UTEST != 0 #endif // Q_UTEST QS_rxReportDone_((int8_t)QS_RX_COMMAND); break; } case WAIT4_TICK_FRAME: { QS_rxReportAck_((int8_t)QS_RX_TICK); #ifdef Q_UTEST QTimeEvt_tick1_((uint_fast8_t)l_rx.var.tick.rate, &QS_rxPriv_); #if Q_UTEST != 0 QS_processTestEvts_(); // process all events produced #endif // Q_UTEST != 0 #else QTimeEvt_tick_((uint_fast8_t)l_rx.var.tick.rate, &QS_rxPriv_); #endif // Q_UTEST QS_rxReportDone_((int8_t)QS_RX_TICK); break; } case WAIT4_PEEK_FRAME: { // no need to report Ack or Done QS_beginRec_((uint_fast8_t)QS_PEEK_DATA); ptr = (uint8_t *)QS_rxPriv_.currObj[AP_OBJ]; ptr = &ptr[l_rx.var.peek.offs]; QS_TIME_PRE_(); // timestamp QS_U16_PRE_(l_rx.var.peek.offs); // data offset QS_U8_PRE_(l_rx.var.peek.size); // data size QS_U8_PRE_(l_rx.var.peek.num); // # data items for (i = 0U; i < l_rx.var.peek.num; ++i) { switch (l_rx.var.peek.size) { case 1: QS_U8_PRE_(ptr[i]); break; case 2: QS_U16_PRE_(((uint16_t *)ptr)[i]); break; case 4: QS_U32_PRE_(((uint32_t *)ptr)[i]); break; default: // intentionally empty break; } } QS_endRec_(); QS_REC_DONE(); // user callback (if defined) break; } case WAIT4_POKE_DATA: { // received less than expected poke data items QS_rxReportError_((int8_t)QS_RX_POKE); break; } case WAIT4_POKE_FRAME: { QS_rxReportAck_((int8_t)QS_RX_POKE); // no need to report done break; } case WAIT4_FILL_FRAME: { QS_rxReportAck_((int8_t)QS_RX_FILL); ptr = (uint8_t *)QS_rxPriv_.currObj[AP_OBJ]; ptr = &ptr[l_rx.var.poke.offs]; for (i = 0U; i < l_rx.var.poke.num; ++i) { switch (l_rx.var.poke.size) { case 1: ptr[i] = (uint8_t)l_rx.var.poke.data; break; case 2: ((uint16_t *)ptr)[i] = (uint16_t)l_rx.var.poke.data; break; case 4: ((uint32_t *)ptr)[i] = l_rx.var.poke.data; break; default: // intentionally empty break; } } break; } case WAIT4_FILTER_FRAME: { QS_rxReportAck_(l_rx.var.flt.recId); // apply the received filters if (l_rx.var.flt.recId == (int8_t)QS_RX_GLB_FILTER) { for (i = 0U; i < Q_DIM(QS_filt_.glb); ++i) { QS_filt_.glb[i] = l_rx.var.flt.data[i]; } // leave the "not maskable" filters enabled, // see qs.h, Miscellaneous QS records (not maskable) QS_filt_.glb[0] |= 0x01U; QS_filt_.glb[7] |= 0xFCU; QS_filt_.glb[8] |= 0x7FU; // never enable the last 3 records (0x7D, 0x7E, 0x7F) QS_filt_.glb[15] &= 0x1FU; } else if (l_rx.var.flt.recId == (int8_t)QS_RX_LOC_FILTER) { for (i = 0U; i < Q_DIM(QS_filt_.loc); ++i) { QS_filt_.loc[i] = l_rx.var.flt.data[i]; } // leave QS_ID == 0 always on QS_filt_.loc[0] |= 0x01U; } else { QS_rxReportError_(l_rx.var.flt.recId); } // no need to report Done break; } case WAIT4_OBJ_FRAME: { i = l_rx.var.obj.kind; if (i < (uint8_t)MAX_OBJ) { if (l_rx.var.obj.recId == (int8_t)QS_RX_CURR_OBJ) { QS_rxPriv_.currObj[i] = (void *)l_rx.var.obj.addr; QS_rxReportAck_((int8_t)QS_RX_CURR_OBJ); } else if (l_rx.var.obj.recId == (int8_t)QS_RX_AO_FILTER) { if (l_rx.var.obj.addr != 0U) { int_fast16_t const filter = (int_fast16_t)((QActive *)l_rx.var.obj.addr)->prio; QS_locFilter_((i == 0U) ? filter :-filter); QS_rxReportAck_((int8_t)QS_RX_AO_FILTER); } else { QS_rxReportError_((int8_t)QS_RX_AO_FILTER); } } else { QS_rxReportError_(l_rx.var.obj.recId); } } // both SM and AO else if (i == (uint8_t)SM_AO_OBJ) { if (l_rx.var.obj.recId == (int8_t)QS_RX_CURR_OBJ) { QS_rxPriv_.currObj[SM_OBJ] = (void *)l_rx.var.obj.addr; QS_rxPriv_.currObj[AO_OBJ] = (void *)l_rx.var.obj.addr; } QS_rxReportAck_(l_rx.var.obj.recId); } else { QS_rxReportError_(l_rx.var.obj.recId); } break; } case WAIT4_QUERY_FRAME: { QS_queryCurrObj(l_rx.var.obj.kind); break; } case WAIT4_EVT_FRAME: { // NOTE: Ack was already reported in the WAIT4_EVT_LEN state #ifdef Q_UTEST QS_onTestEvt(l_rx.var.evt.e); // adjust the event, if needed #endif // Q_UTEST i = 0U; // use 'i' as status, 0 == success,no-recycle if (l_rx.var.evt.prio == 0U) { // publish QActive_publish_(l_rx.var.evt.e, &QS_rxPriv_, 0U); } else if (l_rx.var.evt.prio < QF_MAX_ACTIVE) { if (!QACTIVE_POST_X(QActive_registry_[l_rx.var.evt.prio], l_rx.var.evt.e, 0U, // margin &QS_rxPriv_)) { // failed QACTIVE_POST() recycles the event i = 0x80U; // failure status, no recycle } } else if (l_rx.var.evt.prio == 255U) { // special prio // dispatch to the current SM object if (QS_rxPriv_.currObj[SM_OBJ] != (void *)0) { // increment the ref-ctr to simulate the situation // when the event is just retreived from a queue. // This is expected for the following QF_gc() call. ++l_rx.var.evt.e->refCtr_; QAsm * const sm = (QAsm *)QS_rxPriv_.currObj[SM_OBJ]; (*sm->vptr->dispatch)(sm, l_rx.var.evt.e, 0U); i = 0x01U; // success status, recycle needed } else { i = 0x81U; // failure status, recycle needed } } else if (l_rx.var.evt.prio == 254U) { // special prio // init the current SM object" if (QS_rxPriv_.currObj[SM_OBJ] != (void *)0) { // increment the ref-ctr to simulate the situation // when the event is just retreived from a queue. // This is expected for the following QF_gc() call. ++l_rx.var.evt.e->refCtr_; QAsm * const sm = (QAsm *)QS_rxPriv_.currObj[SM_OBJ]; (*sm->vptr->init)(sm, l_rx.var.evt.e, 0U); i = 0x01U; // success status, recycle needed } else { i = 0x81U; // failure status, recycle needed } } else if (l_rx.var.evt.prio == 253U) { // special prio // post to the current AO if (QS_rxPriv_.currObj[AO_OBJ] != (void *)0) { if (!QACTIVE_POST_X( (QActive *)QS_rxPriv_.currObj[AO_OBJ], l_rx.var.evt.e, 0U, // margin &QS_rxPriv_)) { // failed QACTIVE_POST() recycles the event i = 0x80U; // failure status, no recycle } } else { i = 0x81U; // failure status, recycle needed } } else { i = 0x81U; // failure status, recycle needed } #if (QF_MAX_EPOOL > 0U) if ((i & 0x01U) != 0U) { // recycle needed? QF_gc(l_rx.var.evt.e); } #endif if ((i & 0x80U) != 0U) { // failure? QS_rxReportError_((int8_t)QS_RX_EVENT); } else { #ifdef Q_UTEST #if Q_UTEST != 0 QS_processTestEvts_(); // process all events produced #endif // Q_UTEST != 0 #endif // Q_UTEST QS_rxReportDone_((int8_t)QS_RX_EVENT); } break; } #ifdef Q_UTEST case WAIT4_TEST_SETUP_FRAME: { QS_rxReportAck_((int8_t)QS_RX_TEST_SETUP); QS_tstPriv_.tpNum = 0U; // clear the Test-Probes QS_tstPriv_.testTime = 0U; // clear the time tick // don't clear current objects QS_onTestSetup(); // application-specific test setup // no need to report Done break; } case WAIT4_TEST_TEARDOWN_FRAME: { QS_rxReportAck_((int8_t)QS_RX_TEST_TEARDOWN); QS_onTestTeardown(); // application-specific test teardown // no need to report Done break; } case WAIT4_TEST_CONTINUE_FRAME: { QS_rxReportAck_((int8_t)QS_RX_TEST_CONTINUE); QS_rxPriv_.inTestLoop = false; // exit the QUTest loop // no need to report Done break; } case WAIT4_TEST_PROBE_FRAME: { QS_rxReportAck_((int8_t)QS_RX_TEST_PROBE); Q_ASSERT_INCRIT(815, QS_tstPriv_.tpNum < (sizeof(QS_tstPriv_.tpBuf) / sizeof(QS_tstPriv_.tpBuf[0]))); QS_tstPriv_.tpBuf[QS_tstPriv_.tpNum] = l_rx.var.tp; ++QS_tstPriv_.tpNum; // no need to report Done break; } #endif // Q_UTEST case ERROR_STATE: { // keep ignoring all bytes until new frame break; } default: { QS_rxReportError_(0x47); break; } } } //............................................................................ static void QS_rxHandleBadFrame_(uint8_t const state) { QS_rxReportError_(0x50); // report error for all bad frames switch (state) { case WAIT4_EVT_FRAME: { Q_ASSERT_INCRIT(910, l_rx.var.evt.e != (QEvt *)0); #if (QF_MAX_EPOOL > 0U) QF_gc(l_rx.var.evt.e); // don't leak an allocated event #endif break; } default: { // intentionally empty break; } } } //............................................................................ static void QS_rxReportAck_(int8_t const recId) { QS_beginRec_((uint_fast8_t)QS_RX_STATUS); QS_U8_PRE_(recId); // record ID QS_endRec_(); QS_REC_DONE(); // user callback (if defined) } //............................................................................ static void QS_rxReportError_(int8_t const code) { QS_beginRec_((uint_fast8_t)QS_RX_STATUS); QS_U8_PRE_(0x80U | (uint8_t)code); // error code QS_endRec_(); QS_REC_DONE(); // user callback (if defined) } //............................................................................ static void QS_rxReportDone_(int8_t const recId) { QS_beginRec_((uint_fast8_t)QS_TARGET_DONE); QS_TIME_PRE_(); // timestamp QS_U8_PRE_(recId); // record ID QS_endRec_(); QS_REC_DONE(); // user callback (if defined) } //............................................................................ static void QS_queryCurrObj(uint8_t const obj_kind) { if (QS_rxPriv_.currObj[obj_kind] != (void *)0) { QS_beginRec_((uint_fast8_t)QS_QUERY_DATA); QS_TIME_PRE_(); // timestamp QS_U8_PRE_(obj_kind); // object kind QS_OBJ_PRE_(QS_rxPriv_.currObj[obj_kind]); switch (obj_kind) { case (uint8_t)SM_OBJ: // intentionally fall through case (uint8_t)AO_OBJ: QS_FUN_PRE_((*((QAsm *)QS_rxPriv_.currObj[obj_kind])->vptr ->getStateHandler)( ((QAsm *)QS_rxPriv_.currObj[obj_kind]))); break; case (uint8_t)MP_OBJ: QS_MPC_PRE_(((QMPool *)QS_rxPriv_.currObj[obj_kind]) ->nFree); QS_MPC_PRE_(((QMPool *)QS_rxPriv_.currObj[obj_kind]) ->nMin); break; case (uint8_t)EQ_OBJ: QS_EQC_PRE_(((QEQueue *)QS_rxPriv_.currObj[obj_kind]) ->nFree); QS_EQC_PRE_(((QEQueue *)QS_rxPriv_.currObj[obj_kind]) ->nMin); break; case (uint8_t)TE_OBJ: QS_OBJ_PRE_(((QTimeEvt *)QS_rxPriv_.currObj[obj_kind]) ->act); QS_TEC_PRE_(((QTimeEvt *)QS_rxPriv_.currObj[obj_kind]) ->ctr); QS_TEC_PRE_(((QTimeEvt *)QS_rxPriv_.currObj[obj_kind]) ->interval); QS_SIG_PRE_(((QTimeEvt *)QS_rxPriv_.currObj[obj_kind]) ->super.sig); QS_U8_PRE_ (((QTimeEvt *)QS_rxPriv_.currObj[obj_kind]) ->super.refCtr_); break; default: // intentionally empty break; } QS_endRec_(); QS_REC_DONE(); // user callback (if defined) } else { QS_rxReportError_((int8_t)QS_RX_QUERY_CURR); } } //............................................................................ static void QS_rxPoke_(void) { uint8_t *ptr = (uint8_t *)QS_rxPriv_.currObj[AP_OBJ]; ptr = &ptr[l_rx.var.poke.offs]; switch (l_rx.var.poke.size) { case 1: *ptr = (uint8_t)l_rx.var.poke.data; break; case 2: *(uint16_t *)ptr = (uint16_t)l_rx.var.poke.data; break; case 4: *(uint32_t *)ptr = l_rx.var.poke.data; break; default: { Q_ERROR_INCRIT(900); break; } } l_rx.var.poke.data = 0U; l_rx.var.poke.idx = 0U; l_rx.var.poke.offs += (uint16_t)l_rx.var.poke.size; } //! @endcond // only build when Q_UTEST is defined #ifdef Q_UTEST #define QP_IMPL // this is QP implementation #include "qp_port.h" // QP port #include "qp_pkg.h" // QP package-scope interface #include "qsafe.h" // QP Functional Safety (FuSa) Subsystem #include "qs_port.h" // include QS port #include "qs_pkg.h" // QS facilities for pre-defined trace records //============================================================================ // QUTest unit testing harness $define ${QS::QUTest} //============================================================================ //! @cond INTERNAL QSTestAttr QS_tstPriv_; //............................................................................ void QS_test_pause_(void) { QS_beginRec_((uint_fast8_t)QS_TEST_PAUSED); QS_endRec_(); QS_onTestLoop(); } //............................................................................ uint32_t QS_getTestProbe_(QSpyFunPtr const api) { uint32_t data = 0U; for (uint_fast8_t i = 0U; i < QS_tstPriv_.tpNum; ++i) { if (QS_tstPriv_.tpBuf[i].addr == (QSFun)api) { data = QS_tstPriv_.tpBuf[i].data; QS_CRIT_STAT QS_CRIT_ENTRY(); QS_MEM_SYS(); QS_beginRec_((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) --QS_tstPriv_.tpNum; // one less Test-Probe // move all remaining entries in the buffer up by one for (uint_fast8_t j = i; j < QS_tstPriv_.tpNum; ++j) { QS_tstPriv_.tpBuf[j] = QS_tstPriv_.tpBuf[j + 1U]; } QS_MEM_APP(); QS_CRIT_EXIT(); break; // we are done (Test-Probe retreived) } } return data; } //............................................................................ QSTimeCtr QS_onGetTime(void) { return (++QS_tstPriv_.testTime); } //............................................................................ Q_NORETURN Q_onError( char const * const module, int_t const id) { QS_beginRec_((uint_fast8_t)QS_ASSERT_FAIL); QS_TIME_PRE_(); QS_U16_PRE_(id); QS_STR_PRE_((module != (char *)0) ? module : "?"); QS_endRec_(); QS_onFlush(); // flush the assertion record to the host QS_onCleanup(); // cleanup after the failure QS_onReset(); // reset the target to prevent the code from continuing for (;;) { // QS_onReset() should not return, but to ensure no-return } } //! @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) Q_DEFINE_THIS_MODULE("qutest") //............................................................................ void QS_processTestEvts_(void) { QS_TEST_PROBE_DEF(&QS_processTestEvts_) // return immediately (do nothing) for Test Probe != 0 QS_TEST_PROBE(return;) while (QPSet_notEmpty(&QS_tstPriv_.readySet)) { uint_fast8_t const p = QPSet_findMax(&QS_tstPriv_.readySet); QActive * const a = QActive_registry_[p]; QEvt const * const e = QActive_get_(a); QASM_DISPATCH(a, e, a->prio); #if (QF_MAX_EPOOL > 0U) QF_gc(e); #endif if (a->eQueue.frontEvt == (QEvt *)0) { // empty queue? QPSet_remove(&QS_tstPriv_.readySet, p); #ifndef Q_UNSAFE QPSet_update_(&QS_tstPriv_.readySet, &QS_tstPriv_.readySet_dis); #endif } } } $define ${QS::QUTest-stub} #endif // Q_UTEST != 0 #endif // Q_UTEST #include "qstamp.h" //! the calendar date of the last translation of the form: "Mmm dd yyyy" char const Q_BUILD_DATE[12] = __DATE__; //! the time of the last translation of the form: "hh:mm:ss" char const Q_BUILD_TIME[9] = __TIME__;