diff --git a/include/qequeue.hpp b/include/qequeue.hpp new file mode 100644 index 0000000..186a9a3 --- /dev/null +++ b/include/qequeue.hpp @@ -0,0 +1,119 @@ +//$file${include::qequeue.hpp} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv +// +// Model: qpcpp.qm +// File: ${include::qequeue.hpp} +// +// This code has been generated by QM 6.2.3 . +// DO NOT EDIT THIS FILE MANUALLY. All your changes will be lost. +// +// ____________________________________ +// / / +// / GGGGGGG PPPPPPPP LL / +// / GG GG PP PP LL / +// / GG PP PP LL / +// / GG GGGGG PPPPPPPP LL / +// / GG GG PP LL / +// / GGGGGGG PP LLLLLLL / +// /___________________________________/ +// +// Copyright (c) 2005 Quantum Leaps, LLC +// SPDX-License-Identifier: GPL-3.0-or-later +// +// This generated code is open-source software licensed under the GNU +// General Public License (GPL) as published by the Free Software Foundation +// (see ). +// +// NOTE: +// The GPL does NOT permit the incorporation of this code into proprietary +// programs. Please contact Quantum Leaps for commercial licensing options, +// which expressly supersede the GPL and are designed explicitly for licensees +// interested in retaining the proprietary status of the generated code. +// +// Quantum Leaps contact information: +// +// +// +//$endhead${include::qequeue.hpp} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +#ifndef QEQUEUE_HPP_ +#define QEQUEUE_HPP_ + +#ifndef QF_EQUEUE_CTR_SIZE + #define QF_EQUEUE_CTR_SIZE 1U +#endif + +namespace QP { + +#if (QF_EQUEUE_CTR_SIZE == 1U) + using QEQueueCtr = std::uint8_t; +#elif (QF_EQUEUE_CTR_SIZE == 2U) + using QEQueueCtr = std::uint16_t; +#elif (QF_EQUEUE_CTR_SIZE == 4U) + using QEQueueCtr = std::uint32_t; +#else + #error "QF_EQUEUE_CTR_SIZE defined incorrectly, expected 1U, 2U, or 4U" +#endif + +class QEvt; // forward declaration + +} // namespace QP + +//$declare${QF::QEQueue} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv +namespace QP { + +//${QF::QEQueue} ............................................................. +class QEQueue { +private: + QEvt const * volatile m_frontEvt; + QEvt const ** m_ring; + QEQueueCtr m_end; + QEQueueCtr volatile m_head; + QEQueueCtr volatile m_tail; + QEQueueCtr volatile m_nFree; + QEQueueCtr m_nMin; + + // friends... + friend class QActive; + friend class QTicker; + friend class QXMutex; + friend class QXThread; + +public: + QEQueue() noexcept + : m_frontEvt(nullptr), + m_ring(nullptr), + m_end(0U), + m_head(0U), + m_tail(0U), + m_nFree(0U), + m_nMin(0U) + {} + void init( + QEvt const * qSto[], + std::uint_fast16_t const qLen) noexcept; + bool post( + QEvt const * const e, + std::uint_fast16_t const margin, + std::uint_fast8_t const qs_id) noexcept; + void postLIFO( + QEvt const * const e, + std::uint_fast8_t const qs_id) noexcept; + QEvt const * get(std::uint_fast8_t const qs_id) noexcept; + QEQueueCtr getNFree() const noexcept { + return m_nFree; + } + QEQueueCtr getNMin() const noexcept { + return m_nMin; + } + bool isEmpty() const noexcept { + return m_frontEvt == nullptr; + } + +private: + QEQueue(QEQueue const & other) = delete; + QEQueue & operator=(QEQueue const & other) = delete; +}; // class QEQueue + +} // namespace QP +//$enddecl${QF::QEQueue} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +#endif // QEQUEUE_HPP_ diff --git a/include/qk.hpp b/include/qk.hpp new file mode 100644 index 0000000..39268a8 --- /dev/null +++ b/include/qk.hpp @@ -0,0 +1,175 @@ +//$file${include::qk.hpp} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv +// +// Model: qpcpp.qm +// File: ${include::qk.hpp} +// +// This code has been generated by QM 6.2.3 . +// DO NOT EDIT THIS FILE MANUALLY. All your changes will be lost. +// +// ____________________________________ +// / / +// / GGGGGGG PPPPPPPP LL / +// / GG GG PP PP LL / +// / GG PP PP LL / +// / GG GGGGG PPPPPPPP LL / +// / GG GG PP LL / +// / GGGGGGG PP LLLLLLL / +// /___________________________________/ +// +// Copyright (c) 2005 Quantum Leaps, LLC +// SPDX-License-Identifier: GPL-3.0-or-later +// +// This generated code is open-source software licensed under the GNU +// General Public License (GPL) as published by the Free Software Foundation +// (see ). +// +// NOTE: +// The GPL does NOT permit the incorporation of this code into proprietary +// programs. Please contact Quantum Leaps for commercial licensing options, +// which expressly supersede the GPL and are designed explicitly for licensees +// interested in retaining the proprietary status of the generated code. +// +// Quantum Leaps contact information: +// +// +// +//$endhead${include::qk.hpp} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +#ifndef QK_HPP_ +#define QK_HPP_ + +//$declare${QK::QSchedStatus} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv +namespace QP { + +//${QK::QSchedStatus} ........................................................ +using QSchedStatus = std::uint_fast16_t; + +} // namespace QP +//$enddecl${QK::QSchedStatus} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +//$declare${QK::QK-base} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv +namespace QP { +namespace QK { + +//${QK::QK-base::schedLock} .................................................. +QSchedStatus schedLock(std::uint_fast8_t const ceiling) noexcept; + +//${QK::QK-base::schedUnlock} ................................................ +void schedUnlock(QSchedStatus const stat) noexcept; + +//${QK::QK-base::onIdle} ..................................................... +void onIdle(); + +} // namespace QK +} // namespace QP +//$enddecl${QK::QK-base} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +extern "C" { +//$declare${QK-extern-C} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv + +//${QK-extern-C::QK_Attr} .................................................... +class QK_Attr { +public: + QP::QPSet readySet; + +#ifndef Q_UNSAFE + QP::QPSet readySet_dis; +#endif // ndef Q_UNSAFE + std::uint_fast8_t volatile actPrio; + std::uint_fast8_t volatile nextPrio; + std::uint_fast8_t volatile actThre; + std::uint_fast8_t volatile lockCeil; + std::uint_fast8_t volatile lockHolder; + std::uint_fast8_t volatile intNest; +}; // class QK_Attr + +//${QK-extern-C::QK_priv_} ................................................... +extern QK_Attr QK_priv_; + +//${QK-extern-C::QK_sched_} .................................................. +std::uint_fast8_t QK_sched_() noexcept; + +//${QK-extern-C::QK_activate_} ............................................... +void QK_activate_() noexcept; +//$enddecl${QK-extern-C} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +} // extern "C" + +//============================================================================ +// interface used only for internal implementation, but not in applications +#ifdef QP_IMPL + +//$declare${QK-impl} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv + +//${QK-impl::QF_SCHED_STAT_} ................................................. +#define QF_SCHED_STAT_ QSchedStatus lockStat_; + +//${QK-impl::QF_SCHED_LOCK_} ................................................. +#define QF_SCHED_LOCK_(ceil_) do { \ + if (QK_ISR_CONTEXT_()) { \ + lockStat_ = 0xFFU; \ + } else { \ + lockStat_ = QK::schedLock((ceil_)); \ + } \ +} while (false) + +//${QK-impl::QF_SCHED_UNLOCK_} ............................................... +#define QF_SCHED_UNLOCK_() do { \ + if (lockStat_ != 0xFFU) { \ + QK::schedUnlock(lockStat_); \ + } \ +} while (false) + +//${QK-impl::QACTIVE_EQUEUE_WAIT_} ........................................... +#define QACTIVE_EQUEUE_WAIT_(me_) \ + Q_ASSERT_INCRIT(320, (me_)->m_eQueue.m_frontEvt != nullptr) + +//${QK-impl::QACTIVE_EQUEUE_SIGNAL_} ......................................... +#ifndef Q_UNSAFE +#define QACTIVE_EQUEUE_SIGNAL_(me_) do { \ + QK_priv_.readySet.insert( \ + static_cast((me_)->m_prio)); \ + QK_priv_.readySet.update_(&QK_priv_.readySet_dis); \ + if (!QK_ISR_CONTEXT_()) { \ + if (QK_sched_() != 0U) { \ + QK_activate_(); \ + } \ + } \ +} while (false) +#endif // ndef Q_UNSAFE + +//${QK-impl::QACTIVE_EQUEUE_SIGNAL_} ......................................... +#ifdef Q_UNSAFE +#define QACTIVE_EQUEUE_SIGNAL_(me_) do { \ + QK_priv_.readySet.insert( \ + static_cast((me_)->m_prio)); \ + if (!QK_ISR_CONTEXT_()) { \ + if (QK_sched_() != 0U) { \ + QK_activate_(); \ + } \ + } \ +} while (false) +#endif // def Q_UNSAFE +//$enddecl${QK-impl} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +//$declare${QF_EPOOL-impl} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv + +//${QF_EPOOL-impl::QF_EPOOL_TYPE_} ........................................... +#define QF_EPOOL_TYPE_ QMPool + +//${QF_EPOOL-impl::QF_EPOOL_INIT_} ........................................... +#define QF_EPOOL_INIT_(p_, poolSto_, poolSize_, evtSize_) \ + (p_).init((poolSto_), (poolSize_), (evtSize_)) + +//${QF_EPOOL-impl::QF_EPOOL_EVENT_SIZE_} ..................................... +#define QF_EPOOL_EVENT_SIZE_(p_) ((p_).getBlockSize()) + +//${QF_EPOOL-impl::QF_EPOOL_GET_} ............................................ +#define QF_EPOOL_GET_(p_, e_, m_, qs_id_) \ + ((e_) = static_cast((p_).get((m_), (qs_id_)))) + +//${QF_EPOOL-impl::QF_EPOOL_PUT_} ............................................ +#define QF_EPOOL_PUT_(p_, e_, qs_id_) ((p_).put((e_), (qs_id_))) +//$enddecl${QF_EPOOL-impl} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +#endif // QP_IMPL + +#endif // QK_HPP_ diff --git a/include/qmpool.hpp b/include/qmpool.hpp new file mode 100644 index 0000000..7043731 --- /dev/null +++ b/include/qmpool.hpp @@ -0,0 +1,155 @@ +//$file${include::qmpool.hpp} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv +// +// Model: qpcpp.qm +// File: ${include::qmpool.hpp} +// +// This code has been generated by QM 6.2.3 . +// DO NOT EDIT THIS FILE MANUALLY. All your changes will be lost. +// +// ____________________________________ +// / / +// / GGGGGGG PPPPPPPP LL / +// / GG GG PP PP LL / +// / GG PP PP LL / +// / GG GGGGG PPPPPPPP LL / +// / GG GG PP LL / +// / GGGGGGG PP LLLLLLL / +// /___________________________________/ +// +// Copyright (c) 2005 Quantum Leaps, LLC +// SPDX-License-Identifier: GPL-3.0-or-later +// +// This generated code is open-source software licensed under the GNU +// General Public License (GPL) as published by the Free Software Foundation +// (see ). +// +// NOTE: +// The GPL does NOT permit the incorporation of this code into proprietary +// programs. Please contact Quantum Leaps for commercial licensing options, +// which expressly supersede the GPL and are designed explicitly for licensees +// interested in retaining the proprietary status of the generated code. +// +// Quantum Leaps contact information: +// +// +// +//$endhead${include::qmpool.hpp} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +#ifndef QMPOOL_HPP_ +#define QMPOOL_HPP_ + +#ifndef QF_MPOOL_SIZ_SIZE + #define QF_MPOOL_SIZ_SIZE 2U +#endif +#ifndef QF_MPOOL_CTR_SIZE + #define QF_MPOOL_CTR_SIZE 2U +#endif + +namespace QP { + +#if (QF_MPOOL_SIZ_SIZE == 1U) + using QMPoolSize = std::uint8_t; +#elif (QF_MPOOL_SIZ_SIZE == 2U) + using QMPoolSize = std::uint16_t; +#elif (QF_MPOOL_SIZ_SIZE == 4U) + using QMPoolSize = std::uint32_t; +#else + #error "QF_MPOOL_SIZ_SIZE defined incorrectly, expected 1U, 2U, or 4U" +#endif + +#if (QF_MPOOL_CTR_SIZE == 1U) + using QMPoolCtr = std::uint8_t; +#elif (QF_MPOOL_CTR_SIZE == 2U) + using QMPoolCtr = std::uint16_t; +#elif (QF_MPOOL_CTR_SIZE == 4U) + using QMPoolCtr = std::uint32_t; +#else + #error "QF_MPOOL_CTR_SIZE defined incorrectly, expected 1U, 2U, or 4U" +#endif + +} // namespace QP + +#define QF_MPOOL_EL(evType_) struct { \ + QP::QFreeBlock sto_[((sizeof(evType_) - 1U) \ + / sizeof(QP::QFreeBlock)) + 1U]; } +//$declare${QF::QFreeBlock} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv +namespace QP { + +//${QF::QFreeBlock} .......................................................... +class QFreeBlock { +private: + QFreeBlock * m_next; + +#ifndef Q_UNSAFE + std::uintptr_t m_next_dis; +#endif // ndef Q_UNSAFE + friend class QMPool; +}; // class QFreeBlock + +} // namespace QP +//$enddecl${QF::QFreeBlock} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +//$declare${QF::QMPool} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv +namespace QP { + +//${QF::QMPool} .............................................................. +class QMPool { +private: + QFreeBlock * m_start; + QFreeBlock * m_end; + QFreeBlock * volatile m_free_head; + QMPoolSize m_blockSize; + QMPoolCtr m_nTot; + QMPoolCtr volatile m_nFree; + QMPoolCtr m_nMin; + +public: + QMPool() + : m_start(nullptr), + m_end(nullptr), + m_free_head(nullptr), + m_blockSize(0U), + m_nTot(0U), + m_nFree(0U), + m_nMin(0U) + {} + void init( + void * const poolSto, + std::uint_fast32_t const poolSize, + std::uint_fast16_t const blockSize) noexcept; + void * get( + std::uint_fast16_t const margin, + std::uint_fast8_t const qs_id) noexcept; + void put( + void * const block, + std::uint_fast8_t const qs_id) noexcept; + QMPoolSize getBlockSize() const noexcept; + QMPoolCtr getNMin() const noexcept { + return m_nMin; + } + QMPoolCtr getNFree() const noexcept { + return m_nFree; + } + +private: + QMPool(QEQueue const & other) = delete; + QMPool & operator=(QMPool const & other) = delete; + +public: + +#ifdef QF_ISR_API + void * getFromISR( + std::uint_fast16_t const margin, + std::uint_fast8_t const qs_id) noexcept; +#endif // def QF_ISR_API + +#ifdef QF_ISR_API + void putFromISR( + void * const b, + std::uint_fast8_t const qs_id) noexcept; +#endif // def QF_ISR_API +}; // class QMPool + +} // namespace QP +//$enddecl${QF::QMPool} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +#endif // QMPOOL_HPP_ diff --git a/include/qp.hpp b/include/qp.hpp new file mode 100644 index 0000000..802ff37 --- /dev/null +++ b/include/qp.hpp @@ -0,0 +1,1244 @@ +//$file${include::qp.hpp} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv +// +// Model: qpcpp.qm +// File: ${include::qp.hpp} +// +// This code has been generated by QM 6.2.3 . +// DO NOT EDIT THIS FILE MANUALLY. All your changes will be lost. +// +// ____________________________________ +// / / +// / GGGGGGG PPPPPPPP LL / +// / GG GG PP PP LL / +// / GG PP PP LL / +// / GG GGGGG PPPPPPPP LL / +// / GG GG PP LL / +// / GGGGGGG PP LLLLLLL / +// /___________________________________/ +// +// Copyright (c) 2005 Quantum Leaps, LLC +// SPDX-License-Identifier: GPL-3.0-or-later +// +// This generated code is open-source software licensed under the GNU +// General Public License (GPL) as published by the Free Software Foundation +// (see ). +// +// NOTE: +// The GPL does NOT permit the incorporation of this code into proprietary +// programs. Please contact Quantum Leaps for commercial licensing options, +// which expressly supersede the GPL and are designed explicitly for licensees +// interested in retaining the proprietary status of the generated code. +// +// Quantum Leaps contact information: +// +// +// +//$endhead${include::qp.hpp} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +#ifndef QP_HPP_ +#define QP_HPP_ + +//============================================================================ +#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 // ndef Q_SIGNAL_SIZE + +#ifndef QF_MAX_ACTIVE +#define QF_MAX_ACTIVE 32U +#endif + +#if (QF_MAX_ACTIVE > 64U) +#error QF_MAX_ACTIVE exceeds the maximum of 64U; +#endif + +#ifndef QF_MAX_TICK_RATE +#define QF_MAX_TICK_RATE 1U +#endif + +#if (QF_MAX_TICK_RATE > 15U) +#error QF_MAX_TICK_RATE exceeds the maximum of 15U; +#endif + +#ifndef QF_MAX_EPOOL +#define QF_MAX_EPOOL 3U +#endif + +#if (QF_MAX_EPOOL > 15U) +#error QF_MAX_EPOOL exceeds the maximum of 15U; +#endif + +#ifndef QF_TIMEEVT_CTR_SIZE +#define QF_TIMEEVT_CTR_SIZE 4U +#endif + +#if (QF_TIMEEVT_CTR_SIZE > 4U) +#error QF_TIMEEVT_CTR_SIZE defined incorrectly, expected 1U, 2U, or 4U; +#endif + +#ifndef QF_EVENT_SIZ_SIZE +#define QF_EVENT_SIZ_SIZE 2U +#endif + +#if (QF_EVENT_SIZ_SIZE > 4U) +#error QF_EVENT_SIZ_SIZE defined incorrectly, expected 1U, 2U, or 4U; +#endif + +//! @endcond +//============================================================================ +#ifdef QEVT_DYN_CTOR +#include // for placement new +#include // for va_list +#endif // QEVT_DYN_CTOR + +//$declare${glob-types} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv + +//${glob-types::int_t} ....................................................... +using int_t = int; + +//${glob-types::enum_t} ...................................................... +using enum_t = int; + +//${glob-types::float32_t} ................................................... +using float32_t = float; + +//${glob-types::float64_t} ................................................... +using float64_t = double; +//$enddecl${glob-types} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +//$declare${QEP} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv +namespace QP { + +//${QEP::versionStr[]} ....................................................... +constexpr char const versionStr[] {QP_VERSION_STR}; + +//${QEP::QSignal} ............................................................ +#if (Q_SIGNAL_SIZE == 1U) +using QSignal = std::uint8_t; +#endif // (Q_SIGNAL_SIZE == 1U) + +//${QEP::QSignal} ............................................................ +#if (Q_SIGNAL_SIZE == 2U) +using QSignal = std::uint16_t; +#endif // (Q_SIGNAL_SIZE == 2U) + +//${QEP::QSignal} ............................................................ +#if (Q_SIGNAL_SIZE == 4U) +using QSignal = std::uint32_t; +#endif // (Q_SIGNAL_SIZE == 4U) + +//${QEP::QEvt} ............................................................... +class QEvt { +public: + QSignal sig; + std::uint8_t volatile refCtr_; + std::uint8_t evtTag_; + +public: + static constexpr std::uint8_t MARKER {0xE0U}; + +#ifdef QEVT_DYN_CTOR + enum DynEvt: std::uint8_t { DYNAMIC }; +#endif // def QEVT_DYN_CTOR + +public: + QEvt() = delete; + explicit constexpr QEvt(QSignal const s) noexcept + : sig(s), + refCtr_(0U), + evtTag_(MARKER) + {} + +#ifdef QEVT_DYN_CTOR + explicit QEvt(DynEvt dummy) noexcept { + static_cast(dummy); // unused parameter + // dynamic event already initialized in QP::QF::newX_() + } +#endif // def QEVT_DYN_CTOR + static bool verify_(QEvt const * const e) noexcept { + return (e != nullptr) + && ((e->evtTag_ & 0xF0U) == MARKER); + } + std::uint_fast8_t getPoolId_() const noexcept { + return static_cast(evtTag_) & 0x0FU; + } +}; // class QEvt + +//${QEP::QState} ............................................................. +using QState = std::uint_fast8_t; + +//${QEP::QStateHandler} ...................................................... +using QStateHandler = QState (*)(void * const me, QEvt const * const e); + +//${QEP::QActionHandler} ..................................................... +using QActionHandler = QState (*)(void * const me); + +//${QEP::QXThread} ........................................................... +// forward declaration +class QXThread; + +//${QEP::QXThreadHandler} .................................................... +using QXThreadHandler = void (*)(QXThread * const me); + +//${QEP::QMState} ............................................................ +struct QMState { + QMState const * superstate; + QStateHandler const stateHandler; + QActionHandler const entryAction; + QActionHandler const exitAction; + QActionHandler const initAction; +}; + +//${QEP::QMTranActTable} ..................................................... +struct QMTranActTable { + QMState const * target; + QActionHandler const act[1]; +}; + +//${QEP::QAsmAttr} ........................................................... +union QAsmAttr { + QStateHandler fun; + QActionHandler act; + QXThreadHandler thr; + QMState const *obj; + QMTranActTable const *tatbl; +#ifndef Q_UNSAFE + std::uintptr_t uint; +#endif +}; + +//${QEP::Q_USER_SIG} ......................................................... +constexpr enum_t Q_USER_SIG {4}; + +//${QEP::QAsm} ............................................................... +class QAsm { +protected: + QAsmAttr m_state; + QAsmAttr m_temp; + +public: + + //! All possible return values from state-handlers + //! @note + //! The order is important for algorithmic correctness. + enum QStateRet : QState { + // unhandled and need to "bubble up" + Q_RET_SUPER, //!< event passed to superstate to handle + Q_RET_SUPER_SUB, //!< event passed to submachine superstate + Q_RET_UNHANDLED, //!< event unhandled due to a guard + + // handled and do not need to "bubble up" + Q_RET_HANDLED, //!< event handled (internal transition) + Q_RET_IGNORED, //!< event silently ignored (bubbled up to top) + + // entry/exit + Q_RET_ENTRY, //!< state entry action executed + Q_RET_EXIT, //!< state exit action executed + + // no side effects + Q_RET_NULL, //!< return value without any effect + + // transitions need to execute transition-action table in QP::QMsm + Q_RET_TRAN, //!< regular transition + Q_RET_TRAN_INIT, //!< initial transition in a state or submachine + Q_RET_TRAN_EP, //!< entry-point transition into a submachine + + // transitions that additionally clobber QHsm.m_state + Q_RET_TRAN_HIST, //!< transition to history of a given state + Q_RET_TRAN_XP //!< exit-point transition out of a submachine + }; + + //! Reserved signals by the QP-framework. + enum ReservedSig : QSignal { + Q_EMPTY_SIG, //!< signal to execute the default case + Q_ENTRY_SIG, //!< signal for entry actions + Q_EXIT_SIG, //!< signal for exit actions + Q_INIT_SIG //!< signal for nested initial transitions + }; + +protected: + explicit QAsm() noexcept { + m_state.fun = nullptr; + m_temp.fun = nullptr; + } + +public: + +#ifdef QASM_XTOR + virtual ~QAsm() noexcept { + // empty + } +#endif // def QASM_XTOR + virtual void init( + void const * const e, + std::uint_fast8_t const qs_id) = 0; + virtual void init(std::uint_fast8_t const qs_id) { + this->init(nullptr, qs_id); + } + virtual void dispatch( + QEvt const * const e, + std::uint_fast8_t const qs_id) = 0; + +#ifdef Q_SPY + virtual QStateHandler getStateHandler() noexcept { + return m_state.fun; + } +#endif // def Q_SPY + static QState top( + void * const me, + QEvt const * const e) noexcept + { + static_cast(me); + static_cast(e); + return Q_RET_IGNORED; // the top state ignores all events + } + +protected: + QState tran(QStateHandler const target) noexcept { + m_temp.fun = target; + return Q_RET_TRAN; + } + QState tran_hist(QStateHandler const hist) noexcept { + m_temp.fun = hist; + return Q_RET_TRAN_HIST; + } + QState super(QStateHandler const superstate) noexcept { + m_temp.fun = superstate; + return Q_RET_SUPER; + } + QState qm_tran(void const * const tatbl) noexcept { + m_temp.tatbl = static_cast(tatbl); + return Q_RET_TRAN; + } + QState qm_tran_init(void const * const tatbl) noexcept { + m_temp.tatbl = static_cast(tatbl); + return Q_RET_TRAN_INIT; + } + QState qm_tran_hist( + QMState const * const hist, + void const * const tatbl) noexcept + { + m_state.obj = hist; + m_temp.tatbl = static_cast(tatbl); + return Q_RET_TRAN_HIST; + } + QState qm_tran_ep(void const * const tatbl) noexcept { + m_temp.tatbl = static_cast(tatbl); + return Q_RET_TRAN_EP; + } + QState qm_tran_xp( + QActionHandler const xp, + void const * const tatbl) noexcept + { + m_state.act = xp; + m_temp.tatbl = static_cast(tatbl); + return Q_RET_TRAN_XP; + } + +#ifdef Q_SPY + QState qm_entry(QMState const * const s) noexcept { + m_temp.obj = s; + return Q_RET_ENTRY; + } +#endif // def Q_SPY + +#ifndef Q_SPY + QState qm_entry(QMState const * const s) noexcept { + static_cast(s); // unused parameter + return Q_RET_ENTRY; + } +#endif // ndef Q_SPY + +#ifdef Q_SPY + QState qm_exit(QMState const * const s) noexcept { + m_temp.obj = s; + return Q_RET_EXIT; + } +#endif // def Q_SPY + +#ifndef Q_SPY + QState qm_exit(QMState const * const s) noexcept { + static_cast(s); // unused parameter + return Q_RET_EXIT; + } +#endif // ndef Q_SPY + QState qm_sm_exit(QMState const * const s) noexcept { + m_temp.obj = s; + return Q_RET_EXIT; + } + QState qm_super_sub(QMState const * const s) noexcept { + m_temp.obj = s; + return Q_RET_SUPER_SUB; + } +}; // class QAsm + +//${QEP::QHsm} ............................................................... +class QHsm : public QP::QAsm { +public: + static constexpr std::int_fast8_t MAX_NEST_DEPTH_{6}; + +protected: + explicit QHsm(QStateHandler const initial) noexcept; + +public: + void init( + void const * const e, + std::uint_fast8_t const qs_id) override; + void init(std::uint_fast8_t const qs_id) override { + this->init(nullptr, qs_id); + } + void dispatch( + QEvt const * const e, + std::uint_fast8_t const qs_id) override; + bool isIn(QStateHandler const state) noexcept; + QStateHandler state() const noexcept { + return m_state.fun; + } + QStateHandler childState(QStateHandler const parent) noexcept; + +#ifdef Q_SPY + QStateHandler getStateHandler() noexcept override { + return m_state.fun; + } +#endif // def Q_SPY + +private: + std::int_fast8_t hsm_tran( + QStateHandler (&path)[MAX_NEST_DEPTH_], + std::uint_fast8_t const qs_id); +}; // class QHsm + +//${QEP::QMsm} ............................................................... +class QMsm : public QP::QAsm { +protected: + explicit QMsm(QStateHandler const initial) noexcept; + +public: + void init( + void const * const e, + std::uint_fast8_t const qs_id) override; + void init(std::uint_fast8_t const qs_id) override { + this->init(nullptr, qs_id); + } + void dispatch( + QEvt const * const e, + std::uint_fast8_t const qs_id) override; + +#ifdef Q_SPY + QStateHandler getStateHandler() noexcept override { + return m_state.obj->stateHandler; + } +#endif // def Q_SPY + bool isInState(QMState const * const stateObj) const noexcept; + QMState const * stateObj() const noexcept { + return m_state.obj; + } + QMState const * childStateObj(QMState const * const parent) const noexcept; + +private: + QState execTatbl_( + QMTranActTable const * const tatbl, + std::uint_fast8_t const qs_id); + void exitToTranSource_( + QMState const * const cs, + QMState const * const ts, + std::uint_fast8_t const qs_id); + QState enterHistory_( + QMState const * const hist, + std::uint_fast8_t const qs_id); + +public: + QMState const * topQMState() const noexcept; +}; // class QMsm + +} // namespace QP +//$enddecl${QEP} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +//$declare${QEP-macros} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv + +//${QEP-macros::Q_STATE_DECL} ................................................ +#define Q_STATE_DECL(state_) \ + QP::QState state_ ## _h(QP::QEvt const * const e); \ + static QP::QState state_(void * const me, QP::QEvt const * const e) + +//${QEP-macros::Q_STATE_DEF} ................................................. +#define Q_STATE_DEF(subclass_, state_) \ + QP::QState subclass_::state_(void * const me, QP::QEvt const * const e) { \ + return static_cast(me)->state_ ## _h(e); } \ + QP::QState subclass_::state_ ## _h(QP::QEvt const * const e) + +//${QEP-macros::Q_HANDLED} ................................................... +#define Q_HANDLED() (Q_RET_HANDLED) + +//${QEP-macros::Q_UNHANDLED} ................................................. +#define Q_UNHANDLED() (Q_RET_UNHANDLED) + +//${QEP-macros::Q_EVT_CAST} .................................................. +#define Q_EVT_CAST(subclass_) (static_cast(e)) + +//${QEP-macros::Q_STATE_CAST} ................................................ +#define Q_STATE_CAST(handler_) \ + (reinterpret_cast(handler_)) + +//${QEP-macros::QM_STATE_DECL} ............................................... +#define QM_STATE_DECL(state_) \ + QP::QState state_ ## _h(QP::QEvt const * const e); \ + static QP::QState state_(void * const me, QP::QEvt const * const e); \ + static QP::QMState const state_ ## _s + +//${QEP-macros::QM_SM_STATE_DECL} ............................................ +#define QM_SM_STATE_DECL(subm_, state_) \ + QP::QState state_ ## _h(QP::QEvt const * const e);\ + static QP::QState state_(void * const me, QP::QEvt const * const e); \ + static SM_ ## subm_ const state_ ## _s + +//${QEP-macros::QM_ACTION_DECL} .............................................. +#define QM_ACTION_DECL(action_) \ + QP::QState action_ ## _h(); \ + static QP::QState action_(void * const me) + +//${QEP-macros::QM_STATE_DEF} ................................................ +#define QM_STATE_DEF(subclass_, state_) \ + QP::QState subclass_::state_(void * const me, QP::QEvt const * const e) {\ + return static_cast(me)->state_ ## _h(e); } \ + QP::QState subclass_::state_ ## _h(QP::QEvt const * const e) + +//${QEP-macros::QM_ACTION_DEF} ............................................... +#define QM_ACTION_DEF(subclass_, action_) \ + QP::QState subclass_::action_(void * const me) { \ + return static_cast(me)->action_ ## _h(); } \ + QP::QState subclass_::action_ ## _h() + +//${QEP-macros::QM_HANDLED} .................................................. +#define QM_HANDLED() (Q_RET_HANDLED) + +//${QEP-macros::QM_UNHANDLED} ................................................ +#define QM_UNHANDLED() (Q_RET_HANDLED) + +//${QEP-macros::QM_SUPER} .................................................... +#define QM_SUPER() (Q_RET_SUPER) + +//${QEP-macros::QM_STATE_NULL} ............................................... +#define QM_STATE_NULL (nullptr) + +//${QEP-macros::Q_ACTION_NULL} ............................................... +#define Q_ACTION_NULL (nullptr) + +//${QEP-macros::Q_UNUSED_PAR} ................................................ +#define Q_UNUSED_PAR(par_) (static_cast(par_)) + +//${QEP-macros::Q_DIM} ....................................................... +#define Q_DIM(array_) (sizeof(array_) / sizeof((array_)[0U])) + +//${QEP-macros::Q_UINT2PTR_CAST} ............................................. +#define Q_UINT2PTR_CAST(type_, uint_) (reinterpret_cast(uint_)) + +//${QEP-macros::INIT} ........................................................ +#ifdef Q_SPY +#define INIT(qs_id_) init((qs_id_)) +#endif // def Q_SPY + +//${QEP-macros::INIT} ........................................................ +#ifndef Q_SPY +#define INIT(dummy) init(0U) +#endif // ndef Q_SPY + +//${QEP-macros::DISPATCH} .................................................... +#ifdef Q_SPY +#define DISPATCH(e_, qs_id_) dispatch((e_), (qs_id_)) +#endif // def Q_SPY + +//${QEP-macros::DISPATCH} .................................................... +#ifndef Q_SPY +#define DISPATCH(e_, dummy) dispatch((e_), 0U) +#endif // ndef Q_SPY +//$enddecl${QEP-macros} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +//$declare${QF::types} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv +namespace QP { + +//${QF::types::QPrioSpec} .................................................... +using QPrioSpec = std::uint16_t; + +//${QF::types::QTimeEvtCtr} .................................................. +#if (QF_TIMEEVT_CTR_SIZE == 1U) +using QTimeEvtCtr = std::uint8_t; +#endif // (QF_TIMEEVT_CTR_SIZE == 1U) + +//${QF::types::QTimeEvtCtr} .................................................. +#if (QF_TIMEEVT_CTR_SIZE == 2U) +using QTimeEvtCtr = std::uint16_t; +#endif // (QF_TIMEEVT_CTR_SIZE == 2U) + +//${QF::types::QTimeEvtCtr} .................................................. +#if (QF_TIMEEVT_CTR_SIZE == 4U) +using QTimeEvtCtr = std::uint32_t; +#endif // (QF_TIMEEVT_CTR_SIZE == 4U) + +//${QF::types::QPSetBits} .................................................... +#if (QF_MAX_ACTIVE <= 8U) +using QPSetBits = std::uint8_t; +#endif // (QF_MAX_ACTIVE <= 8U) + +//${QF::types::QPSetBits} .................................................... +#if (8U < QF_MAX_ACTIVE) && (QF_MAX_ACTIVE <= 16U) +using QPSetBits = std::uint16_t; +#endif // (8U < QF_MAX_ACTIVE) && (QF_MAX_ACTIVE <= 16U) + +//${QF::types::QPSetBits} .................................................... +#if (16 < QF_MAX_ACTIVE) +using QPSetBits = std::uint32_t; +#endif // (16 < QF_MAX_ACTIVE) + +//${QF::types::QF_LOG2} ...................................................... +#ifndef QF_LOG2 +std::uint_fast8_t QF_LOG2(QP::QPSetBits x) noexcept; +#endif // ndef QF_LOG2 + +//${QF::types::QPSet} ........................................................ +class QPSet { +private: + QPSetBits m_bits[((QF_MAX_ACTIVE + (8U*sizeof(QPSetBits))) - 1U)/(8U*sizeof(QPSetBits))]; + +public: + void setEmpty() noexcept { + m_bits[0] = 0U; + #if (QF_MAX_ACTIVE > 32) + m_bits[1] = 0U; + #endif + } + bool isEmpty() const noexcept { + #if (QF_MAX_ACTIVE <= 32U) + return (m_bits[0] == 0U); + #else + return (m_bits[0] == 0U) ? (m_bits[1] == 0U) : false; + #endif + } + bool notEmpty() const noexcept { + #if (QF_MAX_ACTIVE <= 32U) + return (m_bits[0] != 0U); + #else + return (m_bits[0] != 0U) ? true : (m_bits[1] != 0U); + #endif + } + bool hasElement(std::uint_fast8_t const n) const noexcept { + #if (QF_MAX_ACTIVE <= 32U) + return (m_bits[0] & (static_cast(1U) << (n - 1U))) != 0U; + #else + return (n <= 32U) + ? ((m_bits[0] & (static_cast(1U) << (n - 1U))) != 0U) + : ((m_bits[1] & (static_cast(1U) << (n - 33U))) != 0U); + #endif + } + void insert(std::uint_fast8_t const n) noexcept { + #if (QF_MAX_ACTIVE <= 32U) + m_bits[0] = (m_bits[0] | (static_cast(1U) << (n - 1U))); + #else + if (n <= 32U) { + m_bits[0] = (m_bits[0] | (static_cast(1U) << (n - 1U))); + } + else { + m_bits[1] = (m_bits[1] | (static_cast(1U) << (n - 33U))); + } + #endif + } + void remove(std::uint_fast8_t const n) noexcept { + #if (QF_MAX_ACTIVE <= 32U) + m_bits[0] = (m_bits[0] & static_cast(~(1U << (n - 1U)))); + #else + if (n <= 32U) { + (m_bits[0] = (m_bits[0] & ~(static_cast(1U) << (n - 1U)))); + } + else { + (m_bits[1] = (m_bits[1] & ~(static_cast(1U) << (n - 33U)))); + } + #endif + } + std::uint_fast8_t findMax() const noexcept { + #if (QF_MAX_ACTIVE <= 32U) + return QF_LOG2(m_bits[0]); + #else + return (m_bits[1] != 0U) + ? (QF_LOG2(m_bits[1]) + 32U) + : (QF_LOG2(m_bits[0])); + #endif + } + +#ifndef Q_UNSAFE + void update_(QPSet * const dis) const noexcept { + dis->m_bits[0] = ~m_bits[0]; + #if (QF_MAX_ACTIVE > 32U) + dis->m_bits[1] = ~m_bits[1]; + #endif + } +#endif // ndef Q_UNSAFE + +#ifndef Q_UNSAFE + bool verify_(QPSet const * const dis) const noexcept { + #if (QF_MAX_ACTIVE <= 32U) + return m_bits[0] == static_cast(~dis->m_bits[0]); + #else + return (m_bits[0] == static_cast(~dis->m_bits[0])) + && (m_bits[1] == static_cast(~dis->m_bits[1])); + #endif + } +#endif // ndef Q_UNSAFE +}; // class QPSet + +//${QF::types::QSubscrList} .................................................. +class QSubscrList { +private: + QPSet m_set; + +#ifndef Q_UNSAFE + QPSet m_set_dis; +#endif // ndef Q_UNSAFE + + // friends... + friend class QActive; +}; // class QSubscrList + +//${QF::types::QEQueue} ...................................................... +class QEQueue; + +} // namespace QP +//$enddecl${QF::types} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +//$declare${QF::QActive} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv +namespace QP { + +//${QF::QActive} ............................................................. +class QActive : public QP::QAsm { +protected: + +#ifdef QACTIVE_EQUEUE_TYPE + QACTIVE_EQUEUE_TYPE m_eQueue; +#endif // def QACTIVE_EQUEUE_TYPE + +#ifdef QACTIVE_OS_OBJ_TYPE + QACTIVE_OS_OBJ_TYPE m_osObject; +#endif // def QACTIVE_OS_OBJ_TYPE + +#ifdef QACTIVE_THREAD_TYPE + QACTIVE_THREAD_TYPE m_thread; +#endif // def QACTIVE_THREAD_TYPE + std::uint8_t m_prio; + std::uint8_t m_pthre; + +public: + static QActive * registry_[QF_MAX_ACTIVE + 1U]; + static QSubscrList * subscrList_; + static enum_t maxPubSignal_; + + // friends... + friend class QTimeEvt; + friend class QTicker; + friend class QXThread; + friend class QXMutex; + friend class QXSemaphore; + friend class QActiveDummy; + friend class GuiQActive; + friend class GuiQMActive; + friend void schedLock(); + +protected: + QActive(QStateHandler const initial) noexcept; + +public: + void init( + void const * const e, + std::uint_fast8_t const qs_id) override + { + reinterpret_cast(this)->QHsm::init(e, qs_id); + } + void init(std::uint_fast8_t const qs_id) override { + this->init(nullptr, qs_id); + } + void dispatch( + QEvt const * const e, + std::uint_fast8_t const qs_id) override + { + reinterpret_cast(this)->QHsm::dispatch(e, qs_id); + } + void setAttr( + std::uint32_t attr1, + void const * attr2 = nullptr); + void start( + QPrioSpec const prioSpec, + QEvt const * * const qSto, + std::uint_fast16_t const qLen, + void * const stkSto, + std::uint_fast16_t const stkSize, + void const * const par); + void start( + QPrioSpec const prioSpec, + QEvt const * * const qSto, + std::uint_fast16_t const qLen, + void * const stkSto, + std::uint_fast16_t const stkSize) + { + this->start(prioSpec, qSto, qLen, stkSto, stkSize, nullptr); + } + +#ifdef QACTIVE_CAN_STOP + void stop(); +#endif // def QACTIVE_CAN_STOP + void register_() noexcept; + void unregister_() noexcept; + bool post_( + QEvt const * const e, + std::uint_fast16_t const margin, + void const * const sender) noexcept; + void postLIFO(QEvt const * const e) noexcept; + QEvt const * get_() noexcept; + static std::uint_fast16_t getQueueMin(std::uint_fast8_t const prio) noexcept; + static void psInit( + QSubscrList * const subscrSto, + enum_t const maxSignal) noexcept; + static void publish_( + QEvt const * const e, + void const * const sender, + std::uint_fast8_t const qs_id) noexcept; + void subscribe(enum_t const sig) const noexcept; + void unsubscribe(enum_t const sig) const noexcept; + void unsubscribeAll() const noexcept; + bool defer( + QEQueue * const eq, + QEvt const * const e) const noexcept; + bool recall(QEQueue * const eq) noexcept; + std::uint_fast16_t flushDeferred(QEQueue * const eq) const noexcept; + std::uint_fast8_t getPrio() const noexcept { + return static_cast(m_prio); + } + void setPrio(QPrioSpec const prio) noexcept { + m_prio = static_cast(prio & 0xFFU); + m_pthre = static_cast(prio >> 8U); + } + std::uint_fast8_t getPThre() const noexcept { + return static_cast(m_pthre); + } + +#ifdef QACTIVE_EQUEUE_TYPE + QACTIVE_EQUEUE_TYPE const & getEQueue() const noexcept { + return m_eQueue; + } +#endif // def QACTIVE_EQUEUE_TYPE + +#ifdef QACTIVE_OS_OBJ_TYPE + QACTIVE_OS_OBJ_TYPE const & getOsObject() const noexcept { + return m_osObject; + } +#endif // def QACTIVE_OS_OBJ_TYPE + +#ifdef QACTIVE_THREAD_TYPE + QACTIVE_THREAD_TYPE const & getThread() const noexcept { + return m_thread; + } +#endif // def QACTIVE_THREAD_TYPE + +#ifdef QACTIVE_THREAD_TYPE + void setThread(QACTIVE_THREAD_TYPE const & thr) { + m_thread = thr; + } +#endif // def QACTIVE_THREAD_TYPE + static void evtLoop_(QActive * act); + +#ifdef QF_ISR_API + virtual bool postFromISR( + QEvt const * const e, + std::uint_fast16_t const margin, + void * par, + void const * const sender) noexcept; +#endif // def QF_ISR_API + +#ifdef QF_ISR_API + static void publishFromISR( + QEvt const * e, + void * par, + void const * sender) noexcept; +#endif // def QF_ISR_API +}; // class QActive + +} // namespace QP +//$enddecl${QF::QActive} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +//$declare${QF::QMActive} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv +namespace QP { + +//${QF::QMActive} ............................................................ +class QMActive : public QP::QActive { +protected: + QMActive(QStateHandler const initial) noexcept; + +public: + void init( + void const * const e, + std::uint_fast8_t const qs_id) override + { + reinterpret_cast(this)->QMsm::init(e, qs_id); + } + void init(std::uint_fast8_t const qs_id) override { + this->init(nullptr, qs_id); + } + void dispatch( + QEvt const * const e, + std::uint_fast8_t const qs_id) override + { + reinterpret_cast(this)->QMsm::dispatch(e, qs_id); + } + +#ifdef Q_SPY + QStateHandler getStateHandler() noexcept override { + return reinterpret_cast(this)->QMsm::getStateHandler(); + } +#endif // def Q_SPY + bool isInState(QMState const * const st) const noexcept { + return reinterpret_cast(this)->QMsm::isInState(st); + } + QMState const * stateObj() const noexcept { + return m_state.obj; + } + QMState const * childStateObj(QMState const * const parent) const noexcept { + return reinterpret_cast(this) + ->QMsm::childStateObj(parent); + } +}; // class QMActive + +} // namespace QP +//$enddecl${QF::QMActive} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +//$declare${QF::QTimeEvt} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv +namespace QP { + +//${QF::QTimeEvt} ............................................................ +class QTimeEvt : public QP::QEvt { +private: + QTimeEvt * volatile m_next; + void * m_act; + QTimeEvtCtr volatile m_ctr; + QTimeEvtCtr m_interval; + +public: + static QTimeEvt timeEvtHead_[QF_MAX_TICK_RATE]; + +private: + friend class QXThread; + +public: + QTimeEvt( + QActive * const act, + QSignal const sig, + std::uint_fast8_t const tickRate = 0U) noexcept; + void armX( + QTimeEvtCtr const nTicks, + QTimeEvtCtr const interval = 0U) noexcept; + bool disarm() noexcept; + bool rearm(QTimeEvtCtr const nTicks) noexcept; + bool wasDisarmed() noexcept; + void const * getAct() const noexcept { + return m_act; + } + QTimeEvtCtr getCtr() const noexcept { + return m_ctr; + } + QTimeEvtCtr getInterval() const noexcept { + return m_interval; + } + static void tick( + std::uint_fast8_t const tickRate, + void const * const sender) noexcept; + +#ifdef Q_UTEST + static void tick1_( + std::uint_fast8_t const tickRate, + void const * const sender); +#endif // def Q_UTEST + +#ifdef QF_ISR_API + static void tickFromISR( + std::uint_fast8_t const tickRate, + void * par, + void const * sender) noexcept; +#endif // def QF_ISR_API + static bool noActive(std::uint_fast8_t const tickRate) noexcept; + QActive * toActive() noexcept { + return static_cast(m_act); + } + QTimeEvt * toTimeEvt() noexcept { + return static_cast(m_act); + } + +private: + QTimeEvt() noexcept; + QTimeEvt(QTimeEvt const & other) = delete; + QTimeEvt & operator=(QTimeEvt const & other) = delete; +}; // class QTimeEvt + +} // namespace QP +//$enddecl${QF::QTimeEvt} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +//$declare${QF::QTicker} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv +namespace QP { + +//${QF::QTicker} ............................................................. +class QTicker : public QP::QActive { +public: + explicit QTicker(std::uint_fast8_t const tickRate) noexcept; + void init( + void const * const e, + std::uint_fast8_t const qs_id) override; + void init(std::uint_fast8_t const qs_id) override { + this->init(nullptr, qs_id); + } + void dispatch( + QEvt const * const e, + std::uint_fast8_t const qs_id) override; + void trig_(void const * const sender) noexcept; +}; // class QTicker + +} // namespace QP +//$enddecl${QF::QTicker} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +//$declare${QF::QF-base} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv +namespace QP { +namespace QF { + +//${QF::QF-base::init} ....................................................... +void init(); + +//${QF::QF-base::stop} ....................................................... +void stop(); + +//${QF::QF-base::run} ........................................................ +int_t run(); + +//${QF::QF-base::onStartup} .................................................. +void onStartup(); + +//${QF::QF-base::onCleanup} .................................................. +void onCleanup(); + +//${QF::QF-base::psInit} ..................................................... +//! @deprecated +inline void psInit( + QSubscrList * const subscrSto, + enum_t const maxSignal) noexcept +{ + QActive::psInit(subscrSto, maxSignal); +} + +//${QF::QF-base::publish_} ................................................... +//! @deprecated +inline void publish_( + QEvt const * const e, + void const * const sender, + std::uint_fast8_t const qs_id) noexcept +{ + QActive::publish_(e, sender, qs_id); +} + +//${QF::QF-base::tick} ....................................................... +//! @deprecated +inline void tick( + std::uint_fast8_t const tickRate, + void const * const sender) noexcept +{ + QTimeEvt::tick(tickRate, sender); +} + +//${QF::QF-base::getQueueMin} ................................................ +//! @deprecated +inline std::uint_fast16_t getQueueMin(std::uint_fast8_t const prio) noexcept { + return QActive::getQueueMin(prio); +} + +//${QF::QF-base::NO_MARGIN} .................................................. +constexpr std::uint_fast16_t NO_MARGIN {0xFFFFU}; + +} // namespace QF +} // namespace QP +//$enddecl${QF::QF-base} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +//$declare${QF::QF-dyn} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv +namespace QP { +namespace QF { + +//${QF::QF-dyn::poolInit} .................................................... +void poolInit( + void * const poolSto, + std::uint_fast32_t const poolSize, + std::uint_fast16_t const evtSize) noexcept; + +//${QF::QF-dyn::poolGetMaxBlockSize} ......................................... +std::uint_fast16_t poolGetMaxBlockSize() noexcept; + +//${QF::QF-dyn::getPoolMin} .................................................. +std::uint_fast16_t getPoolMin(std::uint_fast8_t const poolId) noexcept; + +//${QF::QF-dyn::newX_} ....................................................... +QEvt * newX_( + std::uint_fast16_t const evtSize, + std::uint_fast16_t const margin, + enum_t const sig) noexcept; + +//${QF::QF-dyn::gc} .......................................................... +void gc(QEvt const * const e) noexcept; + +//${QF::QF-dyn::newRef_} ..................................................... +QEvt const * newRef_( + QEvt const * const e, + QEvt const * const evtRef) noexcept; + +//${QF::QF-dyn::deleteRef_} .................................................. +void deleteRef_(QEvt const * const evtRef) noexcept; + +//${QF::QF-dyn::newXfromISR_} ................................................ +#ifdef QF_ISR_API +QEvt * newXfromISR_( + std::uint_fast16_t const evtSize, + std::uint_fast16_t const margin, + enum_t const sig) noexcept; +#endif // def QF_ISR_API + +//${QF::QF-dyn::gcFromISR} ................................................... +#ifdef QF_ISR_API +void gcFromISR(QEvt const * e) noexcept; +#endif // def QF_ISR_API + +} // namespace QF +} // namespace QP +//$enddecl${QF::QF-dyn} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +extern "C" { +//$declare${QF-extern-C} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv + +//${QF-extern-C::QF_onContextSw} ............................................. +#ifdef QF_ON_CONTEXT_SW +void QF_onContextSw( + QP::QActive * prev, + QP::QActive * next); +#endif // def QF_ON_CONTEXT_SW +//$enddecl${QF-extern-C} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +} // extern "C" + +//$declare${QF-macros} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv + +//${QF-macros::Q_PRIO} ....................................................... +#define Q_PRIO(prio_, pthre_) \ + (static_cast((prio_) | (pthre_) << 8U)) + +//${QF-macros::Q_NEW} ........................................................ +#ifndef QEVT_DYN_CTOR +#define Q_NEW(evtT_, sig_) (static_cast( \ + QP::QF::newX_(sizeof(evtT_), QP::QF::NO_MARGIN, (sig_)))) +#endif // ndef QEVT_DYN_CTOR + +//${QF-macros::Q_NEW} ........................................................ +#ifdef QEVT_DYN_CTOR +#define Q_NEW(evtT_, sig_, ...) (static_cast( \ + new(QP::QF::newX_(sizeof(evtT_), QP::QF::NO_MARGIN, (sig_))) \ + evtT_(__VA_ARGS__))) +#endif // def QEVT_DYN_CTOR + +//${QF-macros::Q_NEW_X} ...................................................... +#ifndef QEVT_DYN_CTOR +#define Q_NEW_X(evtT_, margin_, sig_) (static_cast( \ + QP::QF::newX_(sizeof(evtT_), (margin_), (sig_)))) +#endif // ndef QEVT_DYN_CTOR + +//${QF-macros::Q_NEW_X} ...................................................... +#ifdef QEVT_DYN_CTOR +#define Q_NEW_X(evtT_, margin_, sig_, ...) (static_cast( \ + new(QP::QF::newX_(sizeof(evtT_), (margin_), (sig_))) \ + evtT_(__VA_ARGS__))) +#endif // def QEVT_DYN_CTOR + +//${QF-macros::Q_NEW_REF} .................................................... +#define Q_NEW_REF(evtRef_, evtT_) \ + ((evtRef_) = static_cast(QP::QF::newRef_(e, (evtRef_)))) + +//${QF-macros::Q_DELETE_REF} ................................................. +#define Q_DELETE_REF(evtRef_) do { \ + QP::QF::deleteRef_((evtRef_)); \ + (evtRef_) = 0U; \ +} while (false) + +//${QF-macros::PUBLISH} ...................................................... +#ifdef Q_SPY +#define PUBLISH(e_, sender_) \ + publish_((e_), (sender_), (sender_)->getPrio()) +#endif // def Q_SPY + +//${QF-macros::PUBLISH} ...................................................... +#ifndef Q_SPY +#define PUBLISH(e_, dummy) publish_((e_), nullptr, 0U) +#endif // ndef Q_SPY + +//${QF-macros::POST} ......................................................... +#ifdef Q_SPY +#define POST(e_, sender_) post_((e_), QP::QF::NO_MARGIN, (sender_)) +#endif // def Q_SPY + +//${QF-macros::POST} ......................................................... +#ifndef Q_SPY +#define POST(e_, dummy) post_((e_), QP::QF::NO_MARGIN, nullptr) +#endif // ndef Q_SPY + +//${QF-macros::POST_X} ....................................................... +#ifdef Q_SPY +#define POST_X(e_, margin_, sender_) \ + post_((e_), (margin_), (sender_)) +#endif // def Q_SPY + +//${QF-macros::POST_X} ....................................................... +#ifndef Q_SPY +#define POST_X(e_, margin_, dummy) post_((e_), (margin_), nullptr) +#endif // ndef Q_SPY + +//${QF-macros::TICK_X} ....................................................... +#ifdef Q_SPY +#define TICK_X(tickRate_, sender_) tick((tickRate_), (sender_)) +#endif // def Q_SPY + +//${QF-macros::TICK_X} ....................................................... +#ifndef Q_SPY +#define TICK_X(tickRate_, dummy) tick((tickRate_), nullptr) +#endif // ndef Q_SPY + +//${QF-macros::TICK} ......................................................... +#define TICK(sender_) TICK_X(0U, (sender_)) + +//${QF-macros::TRIG} ......................................................... +#ifdef Q_SPY +#define TRIG(sender_) trig_((sender_)) +#endif // def Q_SPY + +//${QF-macros::TRIG} ......................................................... +#ifndef Q_SPY +#define TRIG(sender_) trig_(nullptr) +#endif // ndef Q_SPY + +//${QF-macros::QF_CRIT_EXIT_NOP} ............................................. +#ifndef QF_CRIT_EXIT_NOP +#define QF_CRIT_EXIT_NOP() (static_cast(0)) +#endif // ndef QF_CRIT_EXIT_NOP + +//${QF-macros::QF_MEM_SYS} ................................................... +#ifndef QF_MEM_SYS +#define QF_MEM_SYS() (static_cast(0)) +#endif // ndef QF_MEM_SYS + +//${QF-macros::QF_MEM_APP} ................................................... +#ifndef QF_MEM_APP +#define QF_MEM_APP() (static_cast(0)) +#endif // ndef QF_MEM_APP +//$enddecl${QF-macros} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +#endif // QP_HPP_ diff --git a/include/qp_pkg.hpp b/include/qp_pkg.hpp new file mode 100644 index 0000000..0e858e9 --- /dev/null +++ b/include/qp_pkg.hpp @@ -0,0 +1,96 @@ +//$file${include::qp_pkg.hpp} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv +// +// Model: qpcpp.qm +// File: ${include::qp_pkg.hpp} +// +// This code has been generated by QM 6.2.3 . +// DO NOT EDIT THIS FILE MANUALLY. All your changes will be lost. +// +// ____________________________________ +// / / +// / GGGGGGG PPPPPPPP LL / +// / GG GG PP PP LL / +// / GG PP PP LL / +// / GG GGGGG PPPPPPPP LL / +// / GG GG PP LL / +// / GGGGGGG PP LLLLLLL / +// /___________________________________/ +// +// Copyright (c) 2005 Quantum Leaps, LLC +// SPDX-License-Identifier: GPL-3.0-or-later +// +// This generated code is open-source software licensed under the GNU +// General Public License (GPL) as published by the Free Software Foundation +// (see ). +// +// NOTE: +// The GPL does NOT permit the incorporation of this code into proprietary +// programs. Please contact Quantum Leaps for commercial licensing options, +// which expressly supersede the GPL and are designed explicitly for licensees +// interested in retaining the proprietary status of the generated code. +// +// Quantum Leaps contact information: +// +// +// +//$endhead${include::qp_pkg.hpp} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +#ifndef QP_PKG_HPP_ +#define QP_PKG_HPP_ + +//$declare${QF::QF-pkg} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv +namespace QP { +namespace QF { + +//${QF::QF-pkg::Attr} ........................................................ +class Attr { +public: + +#if (QF_MAX_EPOOL > 0U) + QF_EPOOL_TYPE_ ePool_[QF_MAX_EPOOL]; +#endif // (QF_MAX_EPOOL > 0U) + +#if (QF_MAX_EPOOL > 0U) + std::uint_fast8_t maxPool_; +#endif // (QF_MAX_EPOOL > 0U) + +#if (QF_MAX_EPOOL == 0U) + std::uint8_t dummy; +#endif // (QF_MAX_EPOOL == 0U) +}; // class Attr + +//${QF::QF-pkg::priv_} ....................................................... +extern QF::Attr priv_; + +//${QF::QF-pkg::bzero_} ...................................................... +void bzero_( + void * const start, + std::uint_fast16_t const len) noexcept; + +} // namespace QF +} // namespace QP +//$enddecl${QF::QF-pkg} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +#define QF_CONST_CAST_(type_, ptr_) const_cast(ptr_) +#define QF_PTR_RANGE_(x_, min_, max_) (((min_) <= (x_)) && ((x_) <= (max_))) +#define Q_UINTPTR_CAST_(ptr_) (reinterpret_cast(ptr_)) +#define Q_ACTION_CAST(act_) (reinterpret_cast(act_)) + +namespace QP { + +// Bitmasks are for the QTimeEvt::refCtr_ attribute (inherited from QEvt). +// In QTimeEvt this attribute is NOT used for reference counting. +constexpr std::uint8_t TE_IS_LINKED = 1U << 7U; // flag +constexpr std::uint8_t TE_WAS_DISARMED = 1U << 6U; // flag +constexpr std::uint8_t TE_TICK_RATE = 0x0FU; // bitmask + +inline void QEvt_refCtr_inc_(QEvt const * const e) noexcept { + (QF_CONST_CAST_(QEvt*, e))->refCtr_ = e->refCtr_ + 1U; +} + +inline void QEvt_refCtr_dec_(QEvt const * const e) noexcept { + (QF_CONST_CAST_(QEvt*, e))->refCtr_ = e->refCtr_ - 1U; +} + +} // namespace QP + +#endif // QP_PKG_HPP_ diff --git a/src/qpcpp.hpp b/include/qpcpp.hpp similarity index 64% rename from src/qpcpp.hpp rename to include/qpcpp.hpp index 6027fd1..9897eb0 100644 --- a/src/qpcpp.hpp +++ b/include/qpcpp.hpp @@ -3,52 +3,43 @@ // Model: qpcpp.qm // File: ${include::qpcpp.hpp} // -// This code has been generated by QM 5.2.5 . +// This code has been generated by QM 6.2.3 . // DO NOT EDIT THIS FILE MANUALLY. All your changes will be lost. // -// This code is covered by the following QP license: -// License # : LicenseRef-QL-dual -// Issued to : Any user of the QP/C++ real-time embedded framework -// Framework(s) : qpcpp -// Support ends : 2023-12-31 -// License scope: +// ____________________________________ +// / / +// / GGGGGGG PPPPPPPP LL / +// / GG GG PP PP LL / +// / GG PP PP LL / +// / GG GGGGG PPPPPPPP LL / +// / GG GG PP LL / +// / GGGGGGG PP LLLLLLL / +// /___________________________________/ // -// Copyright (C) 2005 Quantum Leaps, LLC . +// Copyright (c) 2005 Quantum Leaps, LLC +// SPDX-License-Identifier: GPL-3.0-or-later // -// SPDX-License-Identifier: GPL-3.0-or-later OR LicenseRef-QL-commercial +// This generated code is open-source software licensed under the GNU +// General Public License (GPL) as published by the Free Software Foundation +// (see ). // -// 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. +// NOTE: +// The GPL does NOT permit the incorporation of this code into proprietary +// programs. Please contact Quantum Leaps for commercial licensing options, +// which expressly supersede the GPL and are designed explicitly for licensees +// interested in retaining the proprietary status of the generated code. // -// The terms of the open source GNU General Public License version 3 -// can be found at: -// -// The terms of the closed source Quantum Leaps commercial licenses -// can be found at: -// -// Redistributions in source code must retain this top-level comment block. -// Plagiarizing this software to sidestep the license obligations is illegal. -// -// Contact information: +// Quantum Leaps contact information: // // // //$endhead${include::qpcpp.hpp} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -//! @file -//! @brief QP/C++ public interface including backwards-compatibility layer -//! -//! @details -//! This header file must be included directly or indirectly -//! in all application modules (*.cpp files) that use QP/C++. - #ifndef QPCPP_HPP_ - #define QPCPP_HPP_ + //============================================================================ -#include "qf_port.hpp" // QF/C++ port from the port directory -#include "qassert.h" // QP assertions +#include "qp_port.hpp" // QP port from the port directory +#include "qsafe.h" // QP Functional Safety (FuSa) Subsystem #ifdef Q_SPY // software tracing enabled? #include "qs_port.hpp" // QS/C++ port from the port directory #else @@ -58,33 +49,55 @@ //============================================================================ #ifndef QP_API_VERSION -//! Macro that specifies the backwards compatibility with the -//! QP/C++ API version. -//! -//! @details -//! For example, QP_API_VERSION=540 will cause generating the compatibility -//! layer with QP/C++ version 5.4.0 and newer, but not older than 5.4.0. -//! QP_API_VERSION=0 causes generation of the compatibility layer "from the -//! begining of time", which is the maximum backwards compatibilty. This is -//! the default.
-//! -//! Conversely, QP_API_VERSION=9999 means that no compatibility layer should -//! be generated. This setting is useful for checking if an application -//! complies with the latest QP/C++ API. #define QP_API_VERSION 0 #endif // QP_API_VERSION +//============================================================================ // QP/C++ API compatibility layer... -#if (QP_API_VERSION < 700) - -//! @deprecated use QP::QF::NO_MARGIN instead -#define QF_NO_MARGIN QP::QF::NO_MARGIN +#if (QP_API_VERSION < 730) //! @deprecated plain 'char' is no longer forbidden in MISRA/AUTOSAR-C++ using char_t = char; +//! @deprecated assertion failure handler +//! Use Q_onError() instead. +#define Q_onAssert(module_, id_) Q_onError(module_, id_) + +//! @deprecated #Q_NASSERT preprocessor switch to disable QP assertions +#ifdef Q_NASSERT + + // #Q_UNSAFE now replaces the functionality of Q_NASSERT + #define Q_UNSAFE + + //! @deprecated general purpose assertion with user-specified ID + //! number that **always** evaluates the `expr_` expression. + #define Q_ALLEGE_ID(id_, expr_) ((void)(expr_)) + +#else // QP FuSa Subsystem enabled + + //! @deprecated general purpose assertion with user-specified ID + //! number that **always** evaluates the `expr_` expression. + //! @note + //! The use of this macro is no longer recommended. + #define Q_ALLEGE_ID(id_, expr_) if (!(expr_)) { \ + QF_CRIT_STAT \ + QF_CRIT_ENTRY(); \ + Q_onError(&Q_this_module_[0], (id_)); \ + QF_CRIT_EXIT(); \ + } else ((void)0) + +#endif + +//! @deprecated general purpose assertion without ID number +//! that **always** evaluates the `expr_` expression. +//! Instead of ID number, this macro is based on the standard +//! `__LINE__` macro. +//! +//! @note The use of this macro is no longer recommended. +#define Q_ALLEGE(expr_) Q_ALLEGE_ID(__LINE__, (expr_)) + //! Static (compile-time) assertion. //! //! @deprecated @@ -92,6 +105,9 @@ using char_t = char; //! #define Q_ASSERT_COMPILE(expr_) Q_ASSERT_STATIC(expr_) +//! @deprecated use QP::QF::NO_MARGIN instead +#define QF_NO_MARGIN QP::QF::NO_MARGIN + //============================================================================ #if (QP_API_VERSION < 691) @@ -120,22 +136,22 @@ using char_t = char; //! @deprecated local Filter for a generic application object `obj_`. #define QS_FILTER_AP_OBJ(obj_) \ - (QP::QS::priv_.locFilter_AP = (obj_)) + (QP::QS::filt_.loc_AP = (obj_)) //! @deprecated begin of a user QS record, instead use QS_BEGIN_ID() -#define QS_BEGIN(rec_, obj_) \ - if (QS_GLB_FILTER_(rec_) && \ - ((QP::QS::priv_.locFilter[QP::QS::AP_OBJ] == nullptr) \ - || (QP::QS::priv_.locFilter_AP == (obj_)))) \ - { \ - QS_CRIT_STAT_ \ - QS_CRIT_E_(); \ +#define QS_BEGIN(rec_, obj_) \ + if (QS_GLB_FILTER_(rec_) && \ + ((QP::QS::filt_.loc[QP::QS::AP_OBJ] == nullptr) \ + || (QP::QS::filt_.loc_AP == (obj_)))) \ + { \ + QS_CRIT_STAT \ + QS_CRIT_ENTRY(); \ QP::QS::beginRec_(static_cast(rec_)); \ QS_TIME_PRE_(); //! @deprecated output hex-formatted std::uint32_t to the QS record -#define QS_U32_HEX(width_, data_) \ - (QP::QS::u32_fmt_(static_cast( \ +#define QS_U32_HEX(width_, data_) \ + (QP::QS::u32_fmt_(static_cast( \ (static_cast((width_) << 4)) | QS_HEX_FMT), (data_))) #else @@ -150,7 +166,7 @@ using char_t = char; #if (QP_API_VERSION < 680) //! @deprecated -//! Macro to specify a transition in the "me->" impl-strategy. +//! Macro to specify a tran. in the "me->" impl-strategy. //! Instead use the new impl-strategy without the "me->" pointer, where //! you call tran(Q_STATE_CAST(target_)). #define Q_TRAN(target_) (me->tran(Q_STATE_CAST(target_))) @@ -186,7 +202,7 @@ using char_t = char; #define QM_SM_EXIT(state_) (me->qm_sm_exit((state_))) //! @deprecated -//! Macro to call in a QM state-handler when it executes a transition. +//! Macro to call in a QM state-handler when it executes a tran. //! Instead use the new impl-strategy without the "me->" pointer, where //! the QM-generated code calls qm_tran((tatbl_)). #define QM_TRAN(tatbl_) (me->qm_tran((tatbl_))) @@ -226,4 +242,4 @@ using char_t = char; #endif // QP_API_VERSION < 691 #endif // QP_API_VERSION < 700 -#endif // QPCPP_HPP_ \ No newline at end of file +#endif // QPCPP_HPP_ diff --git a/include/qs.hpp b/include/qs.hpp new file mode 100644 index 0000000..d13c251 --- /dev/null +++ b/include/qs.hpp @@ -0,0 +1,993 @@ +//$file${include::qs.hpp} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv +// +// Model: qpcpp.qm +// File: ${include::qs.hpp} +// +// This code has been generated by QM 6.2.3 . +// DO NOT EDIT THIS FILE MANUALLY. All your changes will be lost. +// +// ____________________________________ +// / / +// / GGGGGGG PPPPPPPP LL / +// / GG GG PP PP LL / +// / GG PP PP LL / +// / GG GGGGG PPPPPPPP LL / +// / GG GG PP LL / +// / GGGGGGG PP LLLLLLL / +// /___________________________________/ +// +// Copyright (c) 2005 Quantum Leaps, LLC +// SPDX-License-Identifier: GPL-3.0-or-later +// +// This generated code is open-source software licensed under the GNU +// General Public License (GPL) as published by the Free Software Foundation +// (see ). +// +// NOTE: +// The GPL does NOT permit the incorporation of this code into proprietary +// programs. Please contact Quantum Leaps for commercial licensing options, +// which expressly supersede the GPL and are designed explicitly for licensees +// interested in retaining the proprietary status of the generated code. +// +// Quantum Leaps contact information: +// +// +// +//$endhead${include::qs.hpp} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +#ifndef QS_HPP_ +#define QS_HPP_ + +#ifndef Q_SPY +#error "Q_SPY must be defined to include qs.hpp" +#endif + +//============================================================================ +//! @cond INTERNAL + +#ifndef QS_CTR_SIZE +#define QS_CTR_SIZE 2U +#endif + +#ifndef QS_TIME_SIZE +#define QS_TIME_SIZE 4U +#endif + +//! @endcond +//============================================================================ + +//$declare${QS::types} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv +namespace QP { + +//${QS::types::QSpyPre} ...................................................... +//! pre-defined QS record IDs +enum QSpyPre : std::int8_t { + // [0] QS session (not maskable) + QS_EMPTY, //!< QS record for cleanly starting a session + + // [1] SM records + QS_QEP_STATE_ENTRY, //!< a state was entered + QS_QEP_STATE_EXIT, //!< a state was exited + QS_QEP_STATE_INIT, //!< an initial transition was taken in a state + QS_QEP_INIT_TRAN, //!< the top-most initial transition was taken + QS_QEP_INTERN_TRAN, //!< an internal transition was taken + QS_QEP_TRAN, //!< a regular transition was taken + QS_QEP_IGNORED, //!< an event was ignored (silently discarded) + QS_QEP_DISPATCH, //!< an event was dispatched (begin of RTC step) + QS_QEP_UNHANDLED, //!< an event was un-handled due to a guard + + // [10] Active Object (AO) records + QS_QF_ACTIVE_DEFER, //!< AO deferred an event + QS_QF_ACTIVE_RECALL, //!< AO recalled an event + QS_QF_ACTIVE_SUBSCRIBE, //!< an AO subscribed to an event + QS_QF_ACTIVE_UNSUBSCRIBE, //!< an AO unsubscribed to an event + QS_QF_ACTIVE_POST, //!< an event was posted (FIFO) directly to AO + QS_QF_ACTIVE_POST_LIFO, //!< an event was posted (LIFO) directly to AO + QS_QF_ACTIVE_GET, //!< AO got an event and its queue is not empty + QS_QF_ACTIVE_GET_LAST,//!< AO got an event and its queue is empty + QS_QF_ACTIVE_RECALL_ATTEMPT, //!< AO attempted to recall an event + + // [19] Event Queue (EQ) records + QS_QF_EQUEUE_POST, //!< an event was posted (FIFO) to a raw queue + QS_QF_EQUEUE_POST_LIFO, //!< an event was posted (LIFO) to a raw queue + QS_QF_EQUEUE_GET, //!< get an event and queue still not empty + QS_QF_EQUEUE_GET_LAST,//!< get the last event from the queue + + // [23] Framework (QF) records + QS_QF_NEW_ATTEMPT, //!< an attempt to allocate an event failed + + // [24] Memory Pool (MP) records + QS_QF_MPOOL_GET, //!< a memory block was removed from memory pool + QS_QF_MPOOL_PUT, //!< a memory block was returned to memory pool + + // [26] Additional Framework (QF) records + QS_QF_PUBLISH, //!< an event was published to active objects + QS_QF_NEW_REF, //!< new event reference was created + QS_QF_NEW, //!< new event was created + QS_QF_GC_ATTEMPT, //!< garbage collection attempt + QS_QF_GC, //!< garbage collection + QS_QF_TICK, //!< QTimeEvt tick was called + + // [32] Time Event (TE) records + QS_QF_TIMEEVT_ARM, //!< a time event was armed + QS_QF_TIMEEVT_AUTO_DISARM, //!< a time event expired and was disarmed + QS_QF_TIMEEVT_DISARM_ATTEMPT,//!< attempt to disarm a disarmed QTimeEvt + QS_QF_TIMEEVT_DISARM, //!< true disarming of an armed time event + QS_QF_TIMEEVT_REARM, //!< rearming of a time event + QS_QF_TIMEEVT_POST, //!< a time event posted itself directly to an AO + + // [38] Additional Framework (QF) records + QS_QF_DELETE_REF, //!< an event reference is about to be deleted + QS_QF_CRIT_ENTRY, //!< critical section was entered + QS_QF_CRIT_EXIT, //!< critical section was exited + QS_QF_ISR_ENTRY, //!< an ISR was entered + QS_QF_ISR_EXIT, //!< an ISR was exited + QS_QF_INT_DISABLE, //!< interrupts were disabled + QS_QF_INT_ENABLE, //!< interrupts were enabled + + // [45] Additional Active Object (AO) records + QS_QF_ACTIVE_POST_ATTEMPT,//!< attempt to post an evt to AO failed + + // [46] Additional Event Queue (EQ) records + QS_QF_EQUEUE_POST_ATTEMPT,//!< attempt to post evt to QEQueue failed + + // [47] Additional Memory Pool (MP) records + QS_QF_MPOOL_GET_ATTEMPT, //!< attempt to get a memory block failed + + // [48] Scheduler (SC) records + QS_SCHED_PREEMPT, //!< scheduler asynchronously preempted a task + QS_SCHED_RESTORE, //!< scheduler restored preempted task + QS_SCHED_LOCK, //!< scheduler was locked + QS_SCHED_UNLOCK, //!< scheduler was unlocked + QS_SCHED_NEXT, //!< scheduler started next task + QS_SCHED_IDLE, //!< scheduler restored the idle task + + // [54] Miscellaneous QS records (not maskable) + QS_ENUM_DICT, //!< enumeration dictionary entry + + // [55] Additional QEP records + QS_QEP_TRAN_HIST, //!< a tran to history was taken + QS_QEP_TRAN_EP, //!< a tran to entry point into a submachine + QS_QEP_TRAN_XP, //!< a tran to exit point out of a submachine + + // [58] Miscellaneous QS records (not maskable) + QS_TEST_PAUSED, //!< test has been paused + QS_TEST_PROBE_GET, //!< reports that Test-Probe has been used + QS_SIG_DICT, //!< signal dictionary entry + QS_OBJ_DICT, //!< object dictionary entry + QS_FUN_DICT, //!< function dictionary entry + QS_USR_DICT, //!< user QS record dictionary entry + QS_TARGET_INFO, //!< reports the Target information + QS_TARGET_DONE, //!< reports completion of a user callback + QS_RX_STATUS, //!< reports QS data receive status + QS_QUERY_DATA, //!< reports the data from "current object" query + QS_PEEK_DATA, //!< reports the data from the PEEK query + QS_ASSERT_FAIL, //!< assertion failed in the code + QS_QF_RUN, //!< QF_run() was entered + + // [71] Semaphore (SEM) records + QS_SEM_TAKE, //!< a semaphore was taken by a thread + QS_SEM_BLOCK, //!< a semaphore blocked a thread + QS_SEM_SIGNAL, //!< a semaphore was signaled + QS_SEM_BLOCK_ATTEMPT, //!< a semaphore blocked was attempted + + // [75] Mutex (MTX) records + QS_MTX_LOCK, //!< a mutex was locked + QS_MTX_BLOCK, //!< a mutex blocked a thread + QS_MTX_UNLOCK, //!< a mutex was unlocked + QS_MTX_LOCK_ATTEMPT, //!< a mutex lock was attempted + QS_MTX_BLOCK_ATTEMPT, //!< a mutex blocking was attempted + QS_MTX_UNLOCK_ATTEMPT,//!< a mutex unlock was attempted + + // [81] + QS_PRE_MAX //!< the # predefined signals +}; + +//${QS::types::QSpyGroups} ................................................... +//! QS-TX record groups for QS_GLB_FILTER() +enum QSpyGroups : std::int16_t { + QS_ALL_RECORDS = 0xF0,//!< all maskable QS records + QS_SM_RECORDS, //!< State Machine QS records + QS_AO_RECORDS, //!< Active Object QS records + QS_EQ_RECORDS, //!< Event Queues QS records + QS_MP_RECORDS, //!< Memory Pools QS records + QS_TE_RECORDS, //!< Time Events QS records + QS_QF_RECORDS, //!< QF QS records + QS_SC_RECORDS, //!< Scheduler QS records + QS_SEM_RECORDS, //!< Semaphore QS records + QS_MTX_RECORDS, //!< Mutex QS records + QS_U0_RECORDS, //!< User Group 100-104 records + QS_U1_RECORDS, //!< User Group 105-109 records + QS_U2_RECORDS, //!< User Group 110-114 records + QS_U3_RECORDS, //!< User Group 115-119 records + QS_U4_RECORDS, //!< User Group 120-124 records + QS_UA_RECORDS //!< All User records +}; + +//${QS::types::QSpyUserOffsets} .............................................. +//! QS user record group offsets for QS_GLB_FILTER() +enum QSpyUserOffsets : std::int16_t { + QS_USER = 100, //!< the first record available to QS users + QS_USER0 = QS_USER, //!< offset for User Group 0 + QS_USER1 = QS_USER0 + 5, //!< offset for User Group 1 + QS_USER2 = QS_USER1 + 5, //!< offset for User Group 2 + QS_USER3 = QS_USER2 + 5, //!< offset for User Group 3 + QS_USER4 = QS_USER3 + 5 //!< offset for User Group 4 +}; + +//${QS::types::QSpyIdOffsets} ................................................ +//! QS ID offsets for QS_LOC_FILTER() +enum QSpyIdOffsets : std::int16_t { + QS_AO_ID = 0, //!< offset for AO priorities + QS_EP_ID = 64, //!< offset for event-pool IDs + QS_EQ_ID = 80, //!< offset for event-queue IDs + QS_AP_ID = 96 //!< offset for Application-specific IDs +}; + +//${QS::types::QSpyIdGroups} ................................................. +//! QS ID groups for QS_LOC_FILTER() +enum QSpyIdGroups : std::int16_t { + QS_ALL_IDS = 0xF0, //!< all QS IDs + QS_AO_IDS = 0x80 + QS_AO_ID, //!< AO IDs (priorities) + QS_EP_IDS = 0x80 + QS_EP_ID, //!< event-pool IDs + QS_EQ_IDS = 0x80 + QS_EQ_ID, //!< event-queue IDs + QS_AP_IDS = 0x80 + QS_AP_ID //!< Application-specific IDs +}; + +//${QS::types::QSpyId} ....................................................... +struct QSpyId { + std::uint8_t m_prio; //!< prio. (qs_id) for the QS "local filter" + + // get the prio. (qs_id) from the QSpyId opbject + std::uint_fast8_t getPrio() const noexcept { + return static_cast(m_prio); + } +}; + +//${QS::types::QSpyFunPtr} ................................................... +using QSpyFunPtr = void (*)(); + +//${QS::types::QSCtr} ........................................................ +#if (QS_CTR_SIZE == 2U) +using QSCtr = std::uint16_t; +#endif // (QS_CTR_SIZE == 2U) + +//${QS::types::QSCtr} ........................................................ +#if (QS_CTR_SIZE == 4U) +using QSCtr = std::uint32_t; +#endif // (QS_CTR_SIZE == 4U) + +//${QS::types::QSTimeCtr} .................................................... +#if (QS_TIME_SIZE == 2U) +using QSTimeCtr = std::uint16_t; +#endif // (QS_TIME_SIZE == 2U) + +//${QS::types::QSTimeCtr} .................................................... +#if (QS_TIME_SIZE == 4U) +using QSTimeCtr = std::uint32_t; +#endif // (QS_TIME_SIZE == 4U) + +//${QS::types::QSFun} ........................................................ +#if (QS_FUN_PTR_SIZE == 2U) +using QSFun = std::uint16_t; +#endif // (QS_FUN_PTR_SIZE == 2U) + +//${QS::types::QSFun} ........................................................ +#if (QS_FUN_PTR_SIZE == 4U) +using QSFun = std::uint32_t; +#endif // (QS_FUN_PTR_SIZE == 4U) + +//${QS::types::QSFun} ........................................................ +#if (QS_FUN_PTR_SIZE == 8U) +using QSFun = std::uint64_t; +#endif // (QS_FUN_PTR_SIZE == 8U) + +} // namespace QP +//$enddecl${QS::types} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +//$declare${QS::filters} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv +namespace QP { +namespace QS { + +//${QS::filters::Filter} ..................................................... +class Filter { +public: + std::uint8_t glb[16]; + std::uint8_t loc[16]; +}; // class Filter + +//${QS::filters::filt_} ...................................................... +extern Filter filt_; + +} // namespace QS +} // namespace QP +//$enddecl${QS::filters} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +//$declare${QS-macros} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv + +//${QS-macros::QS_INIT} ...................................................... +#define QS_INIT(arg_) (QP::QS::onStartup(arg_)) + +//${QS-macros::QS_EXIT} ...................................................... +#define QS_EXIT() (QP::QS::onCleanup()) + +//${QS-macros::QS_OUTPUT} .................................................... +#define QS_OUTPUT() (QP::QS::doOutput()) + +//${QS-macros::QS_RX_INPUT} .................................................. +#define QS_RX_INPUT() (QP::QS::doInput()) + +//${QS-macros::QS_GLB_FILTER} ................................................ +#define QS_GLB_FILTER(rec_) \ + (QP::QS::glbFilter_(static_cast(rec_))) + +//${QS-macros::QS_LOC_FILTER} ................................................ +#define QS_LOC_FILTER(qs_id_) \ + (QP::QS::locFilter_(static_cast(qs_id_))) + +//${QS-macros::QS_BEGIN_ID} .................................................. +#define QS_BEGIN_ID(rec_, qs_id_) \ +if (QS_GLB_CHECK_(rec_) && QS_LOC_CHECK_(qs_id_)) { \ + QS_CRIT_STAT \ + QS_CRIT_ENTRY(); \ + QS_MEM_SYS(); \ + QP::QS::beginRec_(static_cast(rec_)); \ + QS_TIME_PRE_(); { + +//${QS-macros::QS_END} ....................................................... +#define QS_END() } \ + QP::QS::endRec_(); \ + QS_MEM_APP(); \ + QS_CRIT_EXIT(); \ +} + +//${QS-macros::QS_FLUSH} ..................................................... +#define QS_FLUSH() (QP::QS::onFlush()) + +//${QS-macros::QS_BEGIN_INCRIT} .............................................. +#define QS_BEGIN_INCRIT(rec_, qs_id_) \ +if (QS_GLB_CHECK_(rec_) && QS_LOC_CHECK_(qs_id_)) { \ + QP::QS::beginRec_(rec_); \ + QS_TIME_PRE_(); { + +//${QS-macros::QS_END_INCRIT} ................................................ +#define QS_END_INCRIT() } \ + QP::QS::endRec_(); \ +} + +//${QS-macros::QS_GLB_CHECK_} ................................................ +#define QS_GLB_CHECK_(rec_) \ +((static_cast(QP::QS::filt_.glb[ \ + static_cast(rec_) >> 3U]) \ + & (static_cast(1U) \ + << (static_cast(rec_) & 7U))) != 0U) + +//${QS-macros::QS_LOC_CHECK_} ................................................ +#define QS_LOC_CHECK_(qs_id_) \ +((static_cast(QP::QS::filt_.loc \ + [static_cast(qs_id_) >> 3U]) \ + & (static_cast(1U) \ + << (static_cast(qs_id_) & 7U))) != 0U) + +//${QS-macros::QS_REC_DONE} .................................................. +#ifndef QS_REC_DONE +#define QS_REC_DONE() (static_cast(0)) +#endif // ndef QS_REC_DONE + +//${QS-macros::QS_I8} ........................................................ +#define QS_I8(width_, data_) \ +(QP::QS::u8_fmt_(static_cast( \ + (static_cast(((width_) << 4U) & 0x7U)) \ + | static_cast(QP::QS::I8_ENUM_T)), (data_))) + +//${QS-macros::QS_U8} ........................................................ +#define QS_U8(width_, data_) \ +(QP::QS::u8_fmt_(static_cast( \ + (static_cast((width_) << 4U)) \ + | static_cast(QP::QS::U8_T)), (data_))) + +//${QS-macros::QS_I16} ....................................................... +#define QS_I16(width_, data_) \ +(QP::QS::u16_fmt_(static_cast( \ + (static_cast((width_) << 4U)) \ + | static_cast(QP::QS::I16_T)), (data_))) + +//${QS-macros::QS_U16} ....................................................... +#define QS_U16(width_, data_) \ +(QP::QS::u16_fmt_(static_cast((((width_) << 4U)) \ + | static_cast(QP::QS::U16_T)), (data_))) + +//${QS-macros::QS_I32} ....................................................... +#define QS_I32(width_, data_) \ +(QP::QS::u32_fmt_( \ + static_cast((static_cast((width_) << 4U)) \ + | static_cast(QP::QS::I32_T)), (data_))) + +//${QS-macros::QS_U32} ....................................................... +#define QS_U32(width_, data_) \ +(QP::QS::u32_fmt_(static_cast( \ + (static_cast((width_) << 4U)) \ + | static_cast(QP::QS::U32_T)), (data_))) + +//${QS-macros::QS_I64} ....................................................... +#define QS_I64(width_, data_) \ +(QP::QS::u64_fmt_(static_cast( \ + (static_cast((width_) << 4U)) \ + | static_cast(QP::QS::I64_T)), (data_))) + +//${QS-macros::QS_U64} ....................................................... +#define QS_U64(width_, data_) \ +(QP::QS::u64_fmt_(static_cast( \ + (static_cast((width_) << 4U)) \ + | static_cast(QP::QS::U64_T)), (data_))) + +//${QS-macros::QS_F32} ....................................................... +#define QS_F32(width_, data_) \ +(QP::QS::f32_fmt_(static_cast( \ + (static_cast((width_) << 4U)) \ + | static_cast(QP::QS::F32_T)), (data_))) + +//${QS-macros::QS_F64} ....................................................... +#define QS_F64(width_, data_) \ +(QP::QS::f64_fmt_(static_cast( \ + (static_cast((width_) << 4U)) \ + | static_cast(QP::QS::F64_T)), (data_))) + +//${QS-macros::QS_STR} ....................................................... +#define QS_STR(str_) (QP::QS::str_fmt_(str_)) + +//${QS-macros::QS_MEM} ....................................................... +#define QS_MEM(mem_, size_) (QP::QS::mem_fmt_((mem_), (size_))) + +//${QS-macros::QS_ENUM} ...................................................... +#define QS_ENUM(group_, value_) \ + (QP::QS::u8_fmt_(static_cast(0x80U | ((group_) << 4U)) \ + | static_cast(QP::QS::I8_ENUM_T),\ + static_cast(value_))) + +//${QS-macros::QS_TIME_PRE_} ................................................. +#if (QS_TIME_SIZE == 2U) +#define QS_TIME_PRE_() (QP::QS::u16_raw_(QP::QS::onGetTime())) +#endif // (QS_TIME_SIZE == 2U) + +//${QS-macros::QS_TIME_PRE_} ................................................. +#if (QS_TIME_SIZE == 4U) +#define QS_TIME_PRE_() (QP::QS::u32_raw_(QP::QS::onGetTime())) +#endif // (QS_TIME_SIZE == 4U) + +//${QS-macros::QS_OBJ} ....................................................... +#if (QS_OBJ_PTR_SIZE == 2U) +#define QS_OBJ(obj_) (QP::QS::u16_fmt_(QP::QS::OBJ_T, \ + reinterpret_cast(obj_))) +#endif // (QS_OBJ_PTR_SIZE == 2U) + +//${QS-macros::QS_OBJ} ....................................................... +#if (QS_OBJ_PTR_SIZE == 4U) +#define QS_OBJ(obj_) (QP::QS::u32_fmt_(QP::QS::OBJ_T, \ + reinterpret_cast(obj_))) +#endif // (QS_OBJ_PTR_SIZE == 4U) + +//${QS-macros::QS_OBJ} ....................................................... +#if (QS_OBJ_PTR_SIZE == 8U) +#define QS_OBJ(obj_) (QP::QS::u64_fmt_(QP::QS::OBJ_T, \ + reinterpret_cast(obj_))) +#endif // (QS_OBJ_PTR_SIZE == 8U) + +//${QS-macros::QS_FUN} ....................................................... +#if (QS_FUN_PTR_SIZE == 2U) +#define QS_FUN(fun_) (QP::QS::u16_fmt_(QP::QS::FUN_T, \ + reinterpret_cast(fun_))) +#endif // (QS_FUN_PTR_SIZE == 2U) + +//${QS-macros::QS_FUN} ....................................................... +#if (QS_FUN_PTR_SIZE == 4U) +#define QS_FUN(fun_) (QP::QS::u32_fmt_(QP::QS::FUN_T, \ + reinterpret_cast(fun_))) +#endif // (QS_FUN_PTR_SIZE == 4U) + +//${QS-macros::QS_FUN} ....................................................... +#if (QS_FUN_PTR_SIZE == 8U) +#define QS_FUN(fun_) (QP::QS::u64_fmt_(QP::QS::FUN_T, \ + reinterpret_cast(fun_))) +#endif // (QS_FUN_PTR_SIZE == 8U) + +//${QS-macros::QS_SIG} ....................................................... +#if (Q_SIGNAL_SIZE == 1U) +#define QS_SIG(sig_, obj_) \ + QP::QS::u8_fmt_(QP::QS::SIG_T, static_cast(sig_)); \ + QP::QS::obj_raw_(obj_) +#endif // (Q_SIGNAL_SIZE == 1U) + +//${QS-macros::QS_SIG} ....................................................... +#if (Q_SIGNAL_SIZE == 2U) +#define QS_SIG(sig_, obj_) \ + QP::QS::u16_fmt_(QP::QS::SIG_T, static_cast(sig_)); \ + QP::QS::obj_raw_(obj_) +#endif // (Q_SIGNAL_SIZE == 2U) + +//${QS-macros::QS_SIG} ....................................................... +#if (Q_SIGNAL_SIZE == 4U) +#define QS_SIG(sig_, obj_) \ + QP::QS::u32_fmt_(QP::QS::SIG_T, static_cast(sig_)); \ + QP::QS::obj_raw_(obj_) +#endif // (Q_SIGNAL_SIZE == 4U) + +//${QS-macros::QS_SIG_DICTIONARY} ............................................ +#define QS_SIG_DICTIONARY(sig_, obj_) \ + (QP::QS::sig_dict_pre_((sig_), (obj_), #sig_)) + +//${QS-macros::QS_OBJ_DICTIONARY} ............................................ +#define QS_OBJ_DICTIONARY(obj_) \ + (QP::QS::obj_dict_pre_((obj_), #obj_)) + +//${QS-macros::QS_OBJ_ARR_DICTIONARY} ........................................ +#define QS_OBJ_ARR_DICTIONARY(obj_, idx_) \ + (QP::QS::obj_arr_dict_pre_((obj_), (idx_), #obj_)) + +//${QS-macros::QS_FUN_DICTIONARY} ............................................ +#define QS_FUN_DICTIONARY(fun_) \ + (QP::QS::fun_dict_pre_( \ + QP::QS::force_cast(fun_), #fun_)) + +//${QS-macros::QS_USR_DICTIONARY} ............................................ +#define QS_USR_DICTIONARY(rec_) \ + (QP::QS::usr_dict_pre_((rec_), #rec_)) + +//${QS-macros::QS_ENUM_DICTIONARY} ........................................... +#define QS_ENUM_DICTIONARY(value_, group_) \ + (QP::QS::enum_dict_pre_((value_), (group_), #value_)) + +//${QS-macros::QS_QF_CRIT_ENTRY} ............................................. +#define QS_QF_CRIT_ENTRY() (QP::QS::crit_entry_pre_()) + +//${QS-macros::QS_QF_CRIT_EXIT} .............................................. +#define QS_QF_CRIT_EXIT() (QP::QS::crit_exit_pre_()) + +//${QS-macros::QS_QF_ISR_ENTRY} .............................................. +#define QS_QF_ISR_ENTRY(isrnest_, prio_) \ + (QP::QS::isr_entry_pre_((isrnest_), (prio_))) + +//${QS-macros::QS_QF_ISR_EXIT} ............................................... +#define QS_QF_ISR_EXIT(isrnest_, prio_) \ + (QP::QS::isr_exit_pre_((isrnest_), (prio_))) + +//${QS-macros::QS_ONLY} ...................................................... +#define QS_ONLY(code_) (code_) + +//${QS-macros::QS_ASSERTION} ................................................. +#define QS_ASSERTION(module_, id_, delay_) \ + (QP::QS::assertion_pre_((module_), (id_), (delay_))) + +//${QS-macros::QS_EOD} ....................................................... +#define QS_EOD (static_cast(0xFFFFU)) + +//${QS-macros::QS_CMD} ....................................................... +#define QS_CMD (static_cast(7U)) + +//${QS-macros::QS_HEX_FMT} ................................................... +#define QS_HEX_FMT (static_cast(0x0FU)) + +//${QS-macros::QS_CRIT_STAT} ................................................. +#ifndef QS_CRIT_STAT +#define QS_CRIT_STAT QF_CRIT_STAT +#endif // ndef QS_CRIT_STAT + +//${QS-macros::QS_CRIT_ENTRY} ................................................ +#ifndef QS_CRIT_ENTRY +#define QS_CRIT_ENTRY() QF_CRIT_ENTRY() +#endif // ndef QS_CRIT_ENTRY + +//${QS-macros::QS_CRIT_EXIT} ................................................. +#ifndef QS_CRIT_EXIT +#define QS_CRIT_EXIT() QF_CRIT_EXIT() +#endif // ndef QS_CRIT_EXIT + +//${QS-macros::QS_MEM_SYS} ................................................... +#ifndef QS_MEM_SYS +#define QS_MEM_SYS() QF_MEM_SYS() +#endif // ndef QS_MEM_SYS + +//${QS-macros::QS_MEM_APP} ................................................... +#ifndef QS_MEM_APP +#define QS_MEM_APP() QF_MEM_APP() +#endif // ndef QS_MEM_APP +//$enddecl${QS-macros} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +//============================================================================ +//! @cond INTERNAL + +namespace QP { +namespace QS { + +struct Attr { + void const * locFilter_AP; //!< @deprecated + std::uint8_t * buf; + QSCtr end; + QSCtr volatile head; + QSCtr volatile tail; + QSCtr volatile used; + std::uint8_t volatile seq; + std::uint8_t volatile chksum; + std::uint8_t volatile critNest; + std::uint8_t flags; +}; + +extern Attr priv_; + +void glbFilter_(std::int_fast16_t const filter) noexcept; +void locFilter_(std::int_fast16_t const filter) noexcept; + +void beginRec_(std::uint_fast8_t const rec) noexcept; +void endRec_() noexcept; + +void u8_raw_(std::uint8_t const d) noexcept; +void u8u8_raw_( + std::uint8_t const d1, + std::uint8_t const d2) noexcept; +void u16_raw_(std::uint16_t d) noexcept; +void u32_raw_(std::uint32_t d) noexcept; +void u64_raw_(std::uint64_t d) noexcept; +void obj_raw_(void const * const obj) noexcept; +void str_raw_(char const * s) noexcept; + +void u8_fmt_( + std::uint8_t const format, + std::uint8_t const d) noexcept; +void u16_fmt_( + std::uint8_t format, + std::uint16_t d) noexcept; +void u32_fmt_( + std::uint8_t format, + std::uint32_t d) noexcept; +void u64_fmt_( + std::uint8_t format, + std::uint64_t d) noexcept; +void f32_fmt_( + std::uint8_t format, + float32_t f) noexcept; +void f64_fmt_( + std::uint8_t format, + float64_t d) noexcept; +void str_fmt_(char const * s) noexcept; +void mem_fmt_( + std::uint8_t const * blk, + std::uint8_t size) noexcept; + +void sig_dict_pre_( + QSignal const sig, + void const * const obj, + char const * const name) noexcept; +void obj_dict_pre_( + void const * const obj, + char const * const name) noexcept; +void obj_arr_dict_pre_( + void const * const obj, + std::uint_fast16_t const idx, + char const * const name) noexcept; +void fun_dict_pre_( + QSpyFunPtr fun, + char const * const name) noexcept; +void usr_dict_pre_( + enum_t const rec, + char const * const name) noexcept; +void enum_dict_pre_( + enum_t const value, + std::uint8_t const group, + char const * const name) noexcept; + +void assertion_pre_( + char const * const module, + int_t const id, + std::uint32_t const delay) noexcept; +void crit_entry_pre_() noexcept; +void crit_exit_pre_() noexcept; +void isr_entry_pre_( + std::uint8_t const isrnest, + std::uint8_t const prio) noexcept; +void isr_exit_pre_( + std::uint8_t const isrnest, + std::uint8_t const prio) noexcept; + +void target_info_pre_(std::uint8_t const isReset); + +} // namespace QS +} // namespace QP + +//! @endcond +//============================================================================ + +//$declare${QS::QS-TX} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv +namespace QP { +namespace QS { + +//${QS::QS-TX::preType} ...................................................... +//! Enumerates data elements for app-specific trace records +enum preType : std::uint8_t { + I8_ENUM_T, //!< signed 8-bit integer or enum format + U8_T, //!< unsigned 8-bit integer format + I16_T, //!< signed 16-bit integer format + U16_T, //!< unsigned 16-bit integer format + I32_T, //!< signed 32-bit integer format + U32_T, //!< unsigned 32-bit integer format + F32_T, //!< 32-bit floating point format + F64_T, //!< 64-bit floating point format + STR_T, //!< zero-terminated ASCII string format + MEM_T, //!< up to 255-bytes memory block format + SIG_T, //!< event signal format + OBJ_T, //!< object pointer format + FUN_T, //!< function pointer format + I64_T, //!< signed 64-bit integer format + U64_T //!< unsigned 64-bit integer format +}; + +//${QS::QS-TX::force_cast} ................................................... +template +inline T_OUT force_cast(T_IN in) { + union TCast { + T_IN in; + T_OUT out; + } u = { in }; + return u.out; +} + +//${QS::QS-TX::initBuf} ...................................................... +void initBuf( + std::uint8_t * const sto, + std::uint_fast32_t const stoSize) noexcept; + +//${QS::QS-TX::getByte} ...................................................... +std::uint16_t getByte() noexcept; + +//${QS::QS-TX::getBlock} ..................................................... +std::uint8_t const * getBlock(std::uint16_t * const pNbytes) noexcept; + +//${QS::QS-TX::doOutput} ..................................................... +void doOutput(); + +//${QS::QS-TX::onStartup} .................................................... +bool onStartup(void const * arg); + +//${QS::QS-TX::onCleanup} .................................................... +void onCleanup(); + +//${QS::QS-TX::onFlush} ...................................................... +void onFlush(); + +//${QS::QS-TX::onGetTime} .................................................... +QSTimeCtr onGetTime(); + +} // namespace QS +} // namespace QP +//$enddecl${QS::QS-TX} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +//============================================================================ +//! @cond INTERNAL + +namespace QP { +namespace QS { + +struct RxAttr { + void * currObj[8]; + std::uint8_t * buf; + QSCtr end; + QSCtr volatile head; + QSCtr volatile tail; +#ifdef Q_UTEST + bool inTestLoop; +#endif +} ; + +extern RxAttr rxPriv_; + +} // namespace QS +} // namespace QP + +//! @endcond +//============================================================================ + +//$declare${QS::QS-RX} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv +namespace QP { +namespace QS { + +//${QS::QS-RX::QSpyObjKind} .................................................. +//! Kinds of objects used QS-RX +enum QSpyObjKind : std::uint8_t { + SM_OBJ, //!< state machine object + AO_OBJ, //!< active object + MP_OBJ, //!< event pool object + EQ_OBJ, //!< raw queue object + TE_OBJ, //!< time event object + AP_OBJ, //!< generic Application-specific object + MAX_OBJ +}; + +//${QS::QS-RX::OSpyObjCombnation} ............................................ +//! Object combinations for QS-RX +enum OSpyObjCombnation : std::uint8_t { + SM_AO_OBJ = MAX_OBJ //!< combination of SM and AO +}; + +//${QS::QS-RX::rxInitBuf} .................................................... +void rxInitBuf( + std::uint8_t * const sto, + std::uint16_t const stoSize) noexcept; + +//${QS::QS-RX::rxPut} ........................................................ +inline bool rxPut(std::uint8_t const b) noexcept { + // NOTE: does not need critical section + // But requires system-level memory access (QF_MEM_SYS()). + + QSCtr head = rxPriv_.head + 1U; + if (head == rxPriv_.end) { + head = 0U; + } + if (head != rxPriv_.tail) { // buffer NOT full? + rxPriv_.buf[rxPriv_.head] = b; + rxPriv_.head = head; + return true; // byte placed in the buffer + } + else { + return false; // byte NOT placed in the buffer + } +} + +//${QS::QS-RX::rxParse} ...................................................... +void rxParse(); + +//${QS::QS-RX::rxGetNfree} ................................................... +std::uint16_t rxGetNfree() noexcept; + +//${QS::QS-RX::doInput} ...................................................... +void doInput(); + +//${QS::QS-RX::onReset} ...................................................... +void onReset(); + +//${QS::QS-RX::onCommand} .................................................... +void onCommand( + std::uint8_t cmdId, + std::uint32_t param1, + std::uint32_t param2, + std::uint32_t param3); + +} // namespace QS +} // namespace QP +//$enddecl${QS::QS-RX} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +//============================================================================ +#ifdef Q_UTEST + +//$declare${QS::QUTest} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv +namespace QP { +namespace QS { + +//${QS::QUTest::TProbe} ...................................................... +struct TProbe { + QSFun addr; + std::uint32_t data; + std::uint8_t idx; +}; + +//${QS::QUTest::onTestSetup} ................................................. +void onTestSetup(); + +//${QS::QUTest::onTestTeardown} .............................................. +void onTestTeardown(); + +//${QS::QUTest::onTestEvt} ................................................... +void onTestEvt(QEvt * e); + +//${QS::QUTest::onTestPost} .................................................. +void onTestPost( + void const * sender, + QActive * recipient, + QEvt const * e, + bool status); + +//${QS::QUTest::onTestLoop} .................................................. +void onTestLoop(); + +} // namespace QS +} // namespace QP +//$enddecl${QS::QUTest} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +#define QUTEST_ON_POST 124 + +//============================================================================ +//! @cond INTERNAL + +namespace QP { +namespace QS { + +struct TestAttr { + TProbe tpBuf[16]; + std::uint8_t tpNum; + QSTimeCtr testTime; + QPSet readySet; + QPSet readySet_dis; + std::uint_fast8_t intLock; +}; + +extern TestAttr tstPriv_; + +void test_pause_(); +std::uint32_t getTestProbe_(QSpyFunPtr const api) noexcept; + +} // namespace QS +} // namespace QP + +//! @endcond +//============================================================================ + +// QP-stub for QUTest +// NOTE: The QP-stub is needed for unit testing QP applications, +// but might NOT be needed for testing QP itself. +#if (Q_UTEST != 0) + +//$declare${QS::QUTest-stub::QHsmDummy} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv +namespace QP { + +//${QS::QUTest-stub::QHsmDummy} .............................................. +class QHsmDummy : public QP::QAsm { +public: + QHsmDummy(); + void init( + void const * const e, + std::uint_fast8_t const qs_id) override; + void dispatch( + QEvt const * const e, + std::uint_fast8_t const qs_id) override; +}; // class QHsmDummy + +} // namespace QP +//$enddecl${QS::QUTest-stub::QHsmDummy} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +//$declare${QS::QUTest-stub::QActiveDummy} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv +namespace QP { + +//${QS::QUTest-stub::QActiveDummy} ........................................... +class QActiveDummy : public QP::QActive { +private: + friend class QActive; + +public: + QActiveDummy(); + void init( + void const * const e, + std::uint_fast8_t const qs_id) override; + void dispatch( + QEvt const * const e, + std::uint_fast8_t const qs_id) override; + +private: + bool fakePost( + QEvt const * const e, + std::uint_fast16_t const margin, + void const * const sender) noexcept; + void fakePostLIFO(QEvt const * const e) noexcept; +}; // class QActiveDummy + +} // namespace QP +//$enddecl${QS::QUTest-stub::QActiveDummy} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +#endif // Q_UTEST != 0 + +#define QS_TEST_PROBE_DEF(fun_) \ + std::uint32_t const qs_tp_ = \ + QP::QS::getTestProbe_(QP::QS::force_cast(fun_)); + +#define QS_TEST_PROBE(code_) \ + if (qs_tp_ != 0U) { code_ } + +#define QS_TEST_PROBE_ID(id_, code_) \ + if (qs_tp_ == static_cast(id_)) { code_ } + +#define QS_TEST_PAUSE() (QP::QS::test_pause_()) + +#else // Q_UTEST not defined + +// dummy definitions when not building for QUTEST +#define QS_TEST_PROBE_DEF(fun_) +#define QS_TEST_PROBE(code_) +#define QS_TEST_PROBE_ID(id_, code_) +#define QS_TEST_PAUSE() (static_cast(0)) + +#endif // Q_UTEST + +#endif // QS_HPP_ diff --git a/src/qs_dummy.hpp b/include/qs_dummy.hpp similarity index 51% rename from src/qs_dummy.hpp rename to include/qs_dummy.hpp index 0593e7c..de4e0d4 100644 --- a/src/qs_dummy.hpp +++ b/include/qs_dummy.hpp @@ -3,43 +3,37 @@ // Model: qpcpp.qm // File: ${include::qs_dummy.hpp} // -// This code has been generated by QM 5.2.5 . +// This code has been generated by QM 6.2.3 . // DO NOT EDIT THIS FILE MANUALLY. All your changes will be lost. // -// This code is covered by the following QP license: -// License # : LicenseRef-QL-dual -// Issued to : Any user of the QP/C++ real-time embedded framework -// Framework(s) : qpcpp -// Support ends : 2023-12-31 -// License scope: +// ____________________________________ +// / / +// / GGGGGGG PPPPPPPP LL / +// / GG GG PP PP LL / +// / GG PP PP LL / +// / GG GGGGG PPPPPPPP LL / +// / GG GG PP LL / +// / GGGGGGG PP LLLLLLL / +// /___________________________________/ // -// Copyright (C) 2005 Quantum Leaps, LLC . +// Copyright (c) 2005 Quantum Leaps, LLC +// SPDX-License-Identifier: GPL-3.0-or-later // -// SPDX-License-Identifier: GPL-3.0-or-later OR LicenseRef-QL-commercial +// This generated code is open-source software licensed under the GNU +// General Public License (GPL) as published by the Free Software Foundation +// (see ). // -// 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. +// NOTE: +// The GPL does NOT permit the incorporation of this code into proprietary +// programs. Please contact Quantum Leaps for commercial licensing options, +// which expressly supersede the GPL and are designed explicitly for licensees +// interested in retaining the proprietary status of the generated code. // -// The terms of the open source GNU General Public License version 3 -// can be found at: -// -// The terms of the closed source Quantum Leaps commercial licenses -// can be found at: -// -// Redistributions in source code must retain this top-level comment block. -// Plagiarizing this software to sidestep the license obligations is illegal. -// -// Contact information: +// Quantum Leaps contact information: // // // //$endhead${include::qs_dummy.hpp} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -//! @file -//! @brief Dummy definitions of the QS macros that avoid code generation from -//! the QS instrumentation. - #ifndef QS_DUMMY_HPP_ #define QS_DUMMY_HPP_ @@ -58,8 +52,8 @@ #define QS_BEGIN_ID(rec_, qs_id_) if (false) { #define QS_END() } -#define QS_BEGIN_NOCRIT(rec_, qs_id_) if (false) { -#define QS_END_NOCRIT() } +#define QS_BEGIN_INCRIT(rec_, qs_id_) if (false) { +#define QS_END_INCRIT() } #define QS_I8(width_, data_) static_cast(0) #define QS_U8(width_, data_) static_cast(0) @@ -94,63 +88,16 @@ #define QS_OUTPUT() static_cast(0) #define QS_RX_INPUT() static_cast(0) +#define QS_ONLY(code_) static_cast(0) //============================================================================ -//$declare${QS::QSpyIdOffsets} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv -namespace QP { - -//${QS::QSpyIdOffsets} ....................................................... -//! QS ID offsets for QS_LOC_FILTER() -enum QSpyIdOffsets : std::int16_t { - QS_AO_ID = 0, //!< offset for AO priorities - QS_EP_ID = 64, //!< offset for event-pool IDs - QS_EQ_ID = 80, //!< offset for event-queue IDs - QS_AP_ID = 96 //!< offset for Appl-spec IDs -}; - -} // namespace QP -//$enddecl${QS::QSpyIdOffsets} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -//$declare${QS::QSpyIdGroups} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv -namespace QP { - -//${QS::QSpyIdGroups} ........................................................ -//! QS ID groups for QS_LOC_FILTER() -enum QSpyIdGroups : std::int16_t { - QS_ALL_IDS = 0xF0, //!< all QS IDs - QS_AO_IDS = 0x80 + QS_AO_ID, //!< AO IDs (priorities) - QS_EP_IDS = 0x80 + QS_EP_ID, //!< event-pool IDs - QS_EQ_IDS = 0x80 + QS_EQ_ID, //!< event-queue IDs - QS_AP_IDS = 0x80 + QS_AP_ID //!< Application-specific IDs -}; - -} // namespace QP -//$enddecl${QS::QSpyIdGroups} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -//$declare${QS::QSpyId} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv -namespace QP { - -//${QS::QSpyId} .............................................................. -//! QS ID type for applying local filtering -struct QSpyId { - std::uint8_t m_prio; //!< "priority" (qs_id) for the QS "local filter" - - //! get the "priority" (qs_id) from the QSpyId opbject - std::uint_fast8_t getPrio() const noexcept { - return static_cast(m_prio); - } -}; - -} // namespace QP -//$enddecl${QS::QSpyId} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -//============================================================================ -// internal QS macros used only in the QP components - +// 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_BEGIN_NOCRIT_PRE_(rec_, qs_id_) if (false) { - #define QS_END_NOCRIT_PRE_() } + #define QS_BEGIN_PRE_(rec_, qs_id_) if (false) { + #define QS_END_PRE_() } #define QS_U8_PRE_(data_) static_cast(0) #define QS_2U8_PRE_(data1_, data2_) static_cast(0) #define QS_U16_PRE_(data_) static_cast(0) @@ -165,12 +112,17 @@ struct QSpyId { #define QS_MPS_PRE_(size_) static_cast(0) #define QS_TEC_PRE_(ctr_) static_cast(0) - #define QS_CRIT_STAT_ - #define QF_QS_CRIT_ENTRY() static_cast(0) - #define QF_QS_CRIT_EXIT() static_cast(0) - #define QF_QS_ISR_ENTRY(isrnest_, prio_) static_cast(0) - #define QF_QS_ISR_EXIT(isrnest_, prio_) static_cast(0) - #define QF_QS_ACTION(act_) static_cast(0) + #define QS_CRIT_STAT + #define QS_CRIT_ENTRY() static_cast(0) + #define QS_CRIT_EXIT() static_cast(0) + + #define QS_MEM_SYS() static_cast(0) + #define QS_MEM_APP() static_cast(0) + + #define QS_TR_CRIT_ENTRY() static_cast(0) + #define QS_TR_CRIT_EXIT() static_cast(0) + #define QS_TR_ISR_ENTRY(isrnest_, prio_) static_cast(0) + #define QS_Tr_ISR_EXIT(isrnest_, prio_) static_cast(0) #endif // QP_IMPL #endif // QS_DUMMY_HPP_ diff --git a/include/qs_pkg.hpp b/include/qs_pkg.hpp new file mode 100644 index 0000000..f2f485f --- /dev/null +++ b/include/qs_pkg.hpp @@ -0,0 +1,209 @@ +//$file${include::qs_pkg.hpp} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv +// +// Model: qpcpp.qm +// File: ${include::qs_pkg.hpp} +// +// This code has been generated by QM 6.2.3 . +// DO NOT EDIT THIS FILE MANUALLY. All your changes will be lost. +// +// ____________________________________ +// / / +// / GGGGGGG PPPPPPPP LL / +// / GG GG PP PP LL / +// / GG PP PP LL / +// / GG GGGGG PPPPPPPP LL / +// / GG GG PP LL / +// / GGGGGGG PP LLLLLLL / +// /___________________________________/ +// +// Copyright (c) 2005 Quantum Leaps, LLC +// SPDX-License-Identifier: GPL-3.0-or-later +// +// This generated code is open-source software licensed under the GNU +// General Public License (GPL) as published by the Free Software Foundation +// (see ). +// +// NOTE: +// The GPL does NOT permit the incorporation of this code into proprietary +// programs. Please contact Quantum Leaps for commercial licensing options, +// which expressly supersede the GPL and are designed explicitly for licensees +// interested in retaining the proprietary status of the generated code. +// +// Quantum Leaps contact information: +// +// +// +//$endhead${include::qs_pkg.hpp} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +#ifndef QS_PKG_HPP_ +#define QS_PKG_HPP_ + +//============================================================================ +//! @cond INTERNAL + +namespace QP { + +//! QS received record types (RX channel) +enum QSpyRxRecords : std::uint8_t { + QS_RX_INFO, //!< query Target info (ver, config, tstamp) + QS_RX_COMMAND, //!< execute a user-defined command in the Target + QS_RX_RESET, //!< reset the Target + QS_RX_TICK, //!< call system clock tick in the Target + QS_RX_PEEK, //!< peek Target memory + QS_RX_POKE, //!< poke Target memory + QS_RX_FILL, //!< fill Target memory + QS_RX_TEST_SETUP, //!< test setup + QS_RX_TEST_TEARDOWN, //!< test teardown + QS_RX_TEST_PROBE, //!< set a Test-Probe in the Target + QS_RX_GLB_FILTER, //!< set global filters in the Target + QS_RX_LOC_FILTER, //!< set local filters in the Target + QS_RX_AO_FILTER, //!< set local AO filter in the Target + QS_RX_CURR_OBJ, //!< set the "current-object" in the Target + QS_RX_TEST_CONTINUE, //!< continue a test after QS_TEST_PAUSE() + QS_RX_QUERY_CURR, //!< query the "current object" in the Target + QS_RX_EVENT //!< inject an event to the Target +}; + +constexpr std::uint8_t QS_FRAME {0x7EU}; +constexpr std::uint8_t QS_ESC {0x7DU}; +constexpr std::uint8_t QS_ESC_XOR {0x20U}; +constexpr std::uint8_t QS_GOOD_CHKSUM {0xFFU}; + +} // namespace QP + +//---------------------------------------------------------------------------- +#define QS_BEGIN_PRE_(rec_, qs_id_) \ + if (QS_GLB_CHECK_(rec_) && QS_LOC_CHECK_(qs_id_)) { \ + QP::QS::beginRec_(static_cast(rec_)); +#define QS_END_PRE_() QP::QS::endRec_(); } + +#define QS_U8_PRE_(data_) \ + (QP::QS::u8_raw_(static_cast(data_))) +#define QS_2U8_PRE_(data1_, data2_) \ + (QP::QS::u8u8_raw_(static_cast(data1_), \ + static_cast(data2_))) +#define QS_U16_PRE_(data_) \ + (QP::QS::u16_raw_(static_cast(data_))) +#define QS_U32_PRE_(data_) \ + (QP::QS::u32_raw_(static_cast(data_))) +#define QS_STR_PRE_(msg_) (QP::QS::str_raw_(msg_)) +#define QS_OBJ_PRE_(obj_) (QP::QS::obj_raw_(obj_)) + +#if (!defined Q_SIGNAL_SIZE || (Q_SIGNAL_SIZE == 1U)) + #define QS_SIG_PRE_(sig_) \ + (QP::QS::u8_raw_(static_cast(sig_))) +#elif (Q_SIGNAL_SIZE == 2U) + #define QS_SIG_PRE_(sig_) \ + (QP::QS::u16_raw_(static_cast(sig_))) +#elif (Q_SIGNAL_SIZE == 4U) + #define QS_SIG_PRE_(sig_) \ + (QP::QS::u32_raw_(static_cast(sig_))) +#endif + +#if (!defined QS_FUN_PTR_SIZE || (QS_FUN_PTR_SIZE == 2U)) + #define QS_FUN_PRE_(fun_) \ + (QP::QS::u16_raw_(reinterpret_cast(fun_))) +#elif (QS_FUN_PTR_SIZE == 4U) + #define QS_FUN_PRE_(fun_) \ + (QP::QS::u32_raw_(reinterpret_cast(fun_))) +#elif (QS_FUN_PTR_SIZE == 8U) + #define QS_FUN_PRE_(fun_) \ + (QP::QS::u64_raw_(reinterpret_cast(fun_))) +#else + #define QS_FUN_PRE_(fun_) \ + (QP::QS::u32_raw_(reinterpret_cast(fun_))) +#endif + +//---------------------------------------------------------------------------- +#if (!defined QF_EQUEUE_CTR_SIZE || (QF_EQUEUE_CTR_SIZE == 1U)) + #define QS_EQC_PRE_(ctr_) \ + QP::QS::u8_raw_(static_cast(ctr_)) +#elif (QF_EQUEUE_CTR_SIZE == 2U) + #define QS_EQC_PRE_(ctr_) \ + QP::QS::u16_raw_(static_cast(ctr_)) +#elif (QF_EQUEUE_CTR_SIZE == 4U) + #define QS_EQC_PRE_(ctr_) \ + QP::QS::u32_raw_(static_cast(ctr_)) +#else + #error "QF_EQUEUE_CTR_SIZE not defined" +#endif + +#if (!defined QF_EVENT_SIZ_SIZE || (QF_EVENT_SIZ_SIZE == 1U)) + #define QS_EVS_PRE_(size_) \ + QP::QS::u8_raw_(static_cast(size_)) +#elif (QF_EVENT_SIZ_SIZE == 2U) + #define QS_EVS_PRE_(size_) \ + QP::QS::u16_raw_(static_cast(size_)) +#elif (QF_EVENT_SIZ_SIZE == 4U) + #define QS_EVS_PRE_(size_) \ + QP::QS::u32_raw_(static_cast(size_)) +#endif + +#if (!defined QF_MPOOL_SIZ_SIZE || (QF_MPOOL_SIZ_SIZE == 1U)) + #define QS_MPS_PRE_(size_) \ + QP::QS::u8_raw_(static_cast(size_)) +#elif (QF_MPOOL_SIZ_SIZE == 2U) + #define QS_MPS_PRE_(size_) \ + QP::QS::u16_raw_(static_cast(size_)) +#elif (QF_MPOOL_SIZ_SIZE == 4U) + #define QS_MPS_PRE_(size_) \ + QP::QS::u32_raw_(static_cast(size_)) +#endif + +#if (!defined QF_MPOOL_CTR_SIZE || (QF_MPOOL_CTR_SIZE == 1U)) + #define QS_MPC_PRE_(ctr_) \ + QP::QS::u8_raw_(static_cast(ctr_)) +#elif (QF_MPOOL_CTR_SIZE == 2U) + #define QS_MPC_PRE_(ctr_) \ + QP::QS::u16_raw_(static_cast(ctr_)) +#elif (QF_MPOOL_CTR_SIZE == 4U) + #define QS_MPC_PRE_(ctr_) \ + QP::QS::u32_raw_(static_cast(ctr_)) +#endif + +#if (!defined QF_TIMEEVT_CTR_SIZE || (QF_TIMEEVT_CTR_SIZE == 1U)) + #define QS_TEC_PRE_(ctr_) \ + QP::QS::u8_raw_(static_cast(ctr_)) +#elif (QF_TIMEEVT_CTR_SIZE == 2U) + #define QS_TEC_PRE_(ctr_) \ + QP::QS::u16_raw_(static_cast(ctr_)) +#elif (QF_TIMEEVT_CTR_SIZE == 4U) + #define QS_TEC_PRE_(ctr_) \ + QP::QS::u32_raw_(static_cast(ctr_)) +#endif + +#define QS_REC_NUM_(enum_) (static_cast(enum_)) + +//---------------------------------------------------------------------------- +#define QS_INSERT_BYTE_(b_) \ + buf[head] = (b_); \ + ++head; \ + if (head == end) { \ + head = 0U; \ + } + +#define QS_INSERT_ESC_BYTE_(b_) \ + chksum = static_cast(chksum + (b_)); \ + if (((b_) != QS_FRAME) && ((b_) != QS_ESC)) { \ + QS_INSERT_BYTE_(b_) \ + } \ + else { \ + QS_INSERT_BYTE_(QS_ESC) \ + QS_INSERT_BYTE_(static_cast((b_) ^ QS_ESC_XOR)) \ + priv_.used = priv_.used + 1U; \ + } + +//---------------------------------------------------------------------------- +#if (defined Q_UTEST) && (Q_UTEST != 0) +namespace QP { +namespace QS { + +void processTestEvts_(); + +} // namespace QS +} // namespace QP +#endif // Q_UTEST != 0 + +//! @endcond +//============================================================================ + +#endif // QS_PKG_HPP_ diff --git a/include/qstamp.hpp b/include/qstamp.hpp new file mode 100644 index 0000000..556be63 --- /dev/null +++ b/include/qstamp.hpp @@ -0,0 +1,45 @@ +//$file${include::qstamp.hpp} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv +// +// Model: qpcpp.qm +// File: ${include::qstamp.hpp} +// +// This code has been generated by QM 6.2.3 . +// DO NOT EDIT THIS FILE MANUALLY. All your changes will be lost. +// +// ____________________________________ +// / / +// / GGGGGGG PPPPPPPP LL / +// / GG GG PP PP LL / +// / GG PP PP LL / +// / GG GGGGG PPPPPPPP LL / +// / GG GG PP LL / +// / GGGGGGG PP LLLLLLL / +// /___________________________________/ +// +// Copyright (c) 2005 Quantum Leaps, LLC +// SPDX-License-Identifier: GPL-3.0-or-later +// +// This generated code is open-source software licensed under the GNU +// General Public License (GPL) as published by the Free Software Foundation +// (see ). +// +// NOTE: +// The GPL does NOT permit the incorporation of this code into proprietary +// programs. Please contact Quantum Leaps for commercial licensing options, +// which expressly supersede the GPL and are designed explicitly for licensees +// interested in retaining the proprietary status of the generated code. +// +// Quantum Leaps contact information: +// +// +// +//$endhead${include::qstamp.hpp} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +#ifndef QSTAMP_HPP_ +#define QSTAMP_HPP_ + +namespace QP { +extern char const BUILD_DATE[12]; +extern char const BUILD_TIME[9]; +} // namespace QP + +#endif // QSTAMP_HPP_ diff --git a/include/qv.hpp b/include/qv.hpp new file mode 100644 index 0000000..db20a11 --- /dev/null +++ b/include/qv.hpp @@ -0,0 +1,119 @@ +//$file${include::qv.hpp} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv +// +// Model: qpcpp.qm +// File: ${include::qv.hpp} +// +// This code has been generated by QM 6.2.3 . +// DO NOT EDIT THIS FILE MANUALLY. All your changes will be lost. +// +// ____________________________________ +// / / +// / GGGGGGG PPPPPPPP LL / +// / GG GG PP PP LL / +// / GG PP PP LL / +// / GG GGGGG PPPPPPPP LL / +// / GG GG PP LL / +// / GGGGGGG PP LLLLLLL / +// /___________________________________/ +// +// Copyright (c) 2005 Quantum Leaps, LLC +// SPDX-License-Identifier: GPL-3.0-or-later +// +// This generated code is open-source software licensed under the GNU +// General Public License (GPL) as published by the Free Software Foundation +// (see ). +// +// NOTE: +// The GPL does NOT permit the incorporation of this code into proprietary +// programs. Please contact Quantum Leaps for commercial licensing options, +// which expressly supersede the GPL and are designed explicitly for licensees +// interested in retaining the proprietary status of the generated code. +// +// Quantum Leaps contact information: +// +// +// +//$endhead${include::qv.hpp} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +#ifndef QV_HPP_ +#define QV_HPP_ + +//$declare${QV::QV-base} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv +namespace QP { +namespace QV { + +//${QV::QV-base::Attr} ....................................................... +class Attr { +public: + QPSet readySet; + +#ifndef Q_UNSAFE + QPSet readySet_dis; +#endif // ndef Q_UNSAFE +}; // class Attr + +//${QV::QV-base::priv_} ...................................................... +extern QV::Attr priv_; + +//${QV::QV-base::onIdle} ..................................................... +void onIdle(); + +} // namespace QV +} // namespace QP +//$enddecl${QV::QV-base} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +//============================================================================ +// interface used only for internal implementation, but not in applications +#ifdef QP_IMPL + +//$declare${QV-impl} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv + +//${QV-impl::QF_SCHED_STAT_} ................................................. +#define QF_SCHED_STAT_ + +//${QV-impl::QF_SCHED_LOCK_} ................................................. +#define QF_SCHED_LOCK_(dummy) (static_cast(0)) + +//${QV-impl::QF_SCHED_UNLOCK_} ............................................... +#define QF_SCHED_UNLOCK_() (static_cast(0)) + +//${QV-impl::QACTIVE_EQUEUE_WAIT_} ........................................... +#define QACTIVE_EQUEUE_WAIT_(me_) \ + Q_ASSERT_INCRIT(310, (me_)->m_eQueue.m_frontEvt != nullptr) + +//${QV-impl::QACTIVE_EQUEUE_SIGNAL_} ......................................... +#ifndef Q_UNSAFE +#define QACTIVE_EQUEUE_SIGNAL_(me_) \ + QV::priv_.readySet.insert((me_)->m_prio); \ + QV::priv_.readySet.update_(&QV::priv_.readySet_dis) +#endif // ndef Q_UNSAFE + +//${QV-impl::QACTIVE_EQUEUE_SIGNAL_} ......................................... +#ifdef Q_UNSAFE +#define QACTIVE_EQUEUE_SIGNAL_(me_) \ + (QV::priv_.readySet.insert((me_)->m_prio)) +#endif // def Q_UNSAFE +//$enddecl${QV-impl} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +//$declare${QF_EPOOL-impl} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv + +//${QF_EPOOL-impl::QF_EPOOL_TYPE_} ........................................... +#define QF_EPOOL_TYPE_ QMPool + +//${QF_EPOOL-impl::QF_EPOOL_INIT_} ........................................... +#define QF_EPOOL_INIT_(p_, poolSto_, poolSize_, evtSize_) \ + (p_).init((poolSto_), (poolSize_), (evtSize_)) + +//${QF_EPOOL-impl::QF_EPOOL_EVENT_SIZE_} ..................................... +#define QF_EPOOL_EVENT_SIZE_(p_) ((p_).getBlockSize()) + +//${QF_EPOOL-impl::QF_EPOOL_GET_} ............................................ +#define QF_EPOOL_GET_(p_, e_, m_, qs_id_) \ + ((e_) = static_cast((p_).get((m_), (qs_id_)))) + +//${QF_EPOOL-impl::QF_EPOOL_PUT_} ............................................ +#define QF_EPOOL_PUT_(p_, e_, qs_id_) ((p_).put((e_), (qs_id_))) +//$enddecl${QF_EPOOL-impl} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +#endif // QP_IMPL + +#endif // QV_HPP_ diff --git a/include/qxk.hpp b/include/qxk.hpp new file mode 100644 index 0000000..421bb02 --- /dev/null +++ b/include/qxk.hpp @@ -0,0 +1,336 @@ +//$file${include::qxk.hpp} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv +// +// Model: qpcpp.qm +// File: ${include::qxk.hpp} +// +// This code has been generated by QM 6.2.3 . +// DO NOT EDIT THIS FILE MANUALLY. All your changes will be lost. +// +// ____________________________________ +// / / +// / GGGGGGG PPPPPPPP LL / +// / GG GG PP PP LL / +// / GG PP PP LL / +// / GG GGGGG PPPPPPPP LL / +// / GG GG PP LL / +// / GGGGGGG PP LLLLLLL / +// /___________________________________/ +// +// Copyright (c) 2005 Quantum Leaps, LLC +// SPDX-License-Identifier: GPL-3.0-or-later +// +// This generated code is open-source software licensed under the GNU +// General Public License (GPL) as published by the Free Software Foundation +// (see ). +// +// NOTE: +// The GPL does NOT permit the incorporation of this code into proprietary +// programs. Please contact Quantum Leaps for commercial licensing options, +// which expressly supersede the GPL and are designed explicitly for licensees +// interested in retaining the proprietary status of the generated code. +// +// Quantum Leaps contact information: +// +// +// +//$endhead${include::qxk.hpp} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +#ifndef QXK_HPP_ +#define QXK_HPP_ + +//$declare${QXK::QSchedStatus} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv +namespace QP { + +//${QXK::QSchedStatus} ....................................................... +using QSchedStatus = std::uint_fast16_t; + +} // namespace QP +//$enddecl${QXK::QSchedStatus} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +//$declare${QXK::QXTHREAD_NO_TIMEOUT} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv +namespace QP { + +//${QXK::QXTHREAD_NO_TIMEOUT} ................................................ +constexpr QTimeEvtCtr QXTHREAD_NO_TIMEOUT {0U}; + +} // namespace QP +//$enddecl${QXK::QXTHREAD_NO_TIMEOUT} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +//$declare${QXK::QXK-base} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv +namespace QP { +namespace QXK { + +//${QXK::QXK-base::onIdle} ................................................... +void onIdle(); + +//${QXK::QXK-base::schedLock} ................................................ +QSchedStatus schedLock(std::uint_fast8_t const ceiling) noexcept; + +//${QXK::QXK-base::schedUnlock} .............................................. +void schedUnlock(QSchedStatus const stat) noexcept; + +//${QXK::QXK-base::current} .................................................. +QP::QActive * current() noexcept; + +} // namespace QXK +} // namespace QP +//$enddecl${QXK::QXK-base} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +//$declare${QXK::QXThread} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv +namespace QP { + +//${QXK::QXThread} ........................................................... +class QXThread : public QP::QActive { +private: + QTimeEvt m_timeEvt; + +public: + friend class QActive; + friend class QTimeEvt; + friend class QXSemaphore; + friend class QXMutex; + +public: + static constexpr QTimeEvtCtr QXTHREAD_NO_TIMEOUT{0U}; + +public: + QXThread( + QXThreadHandler const handler, + std::uint_fast8_t const tickRate = 0U) noexcept; + void init( + void const * const e, + std::uint_fast8_t const qs_id) override; + void init(std::uint_fast8_t const qs_id) override { + this->init(nullptr, qs_id); + } + void dispatch( + QEvt const * const e, + std::uint_fast8_t const qs_id) override; + QTimeEvt const * getTimeEvt() const noexcept { + return &m_timeEvt; + } + static bool delay(QTimeEvtCtr const nTicks) noexcept; + bool delayCancel() noexcept; + static QEvt const * queueGet(QTimeEvtCtr const nTicks = QXTHREAD_NO_TIMEOUT) noexcept; + +private: + void block_() const noexcept; + void unblock_() const noexcept; + static void timeout_(QActive * const act); + void teArm_( + enum_t const sig, + QTimeEvtCtr const nTicks) noexcept; + bool teDisarm_() noexcept; + void stackInit_( + QP::QXThreadHandler const handler, + void * const stkSto, + std::uint_fast16_t const stkSize) noexcept; +}; // class QXThread + +} // namespace QP +//$enddecl${QXK::QXThread} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +//$declare${QXK::QXSemaphore} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv +namespace QP { + +//${QXK::QXSemaphore} ........................................................ +class QXSemaphore { +private: + QPSet m_waitSet; + std::uint8_t m_count; + std::uint8_t m_max_count; + +public: + void init( + std::uint_fast8_t const count, + std::uint_fast8_t const max_count = 0xFFU) noexcept; + bool wait(QTimeEvtCtr const nTicks = QXTHREAD_NO_TIMEOUT) noexcept; + bool tryWait() noexcept; + bool signal() noexcept; +}; // class QXSemaphore + +} // namespace QP +//$enddecl${QXK::QXSemaphore} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +//$declare${QXK::QXMutex} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv +namespace QP { + +//${QXK::QXMutex} ............................................................ +class QXMutex { +private: + QActive m_ao; + QPSet m_waitSet; + +public: + QXMutex(); + void init(QPrioSpec const prioSpec) noexcept; + bool lock(QTimeEvtCtr const nTicks = QXTHREAD_NO_TIMEOUT) noexcept; + bool tryLock() noexcept; + void unlock() noexcept; +}; // class QXMutex + +} // namespace QP +//$enddecl${QXK::QXMutex} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +extern "C" { +//$declare${QXK-extern-C} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv + +//${QXK-extern-C::QXK_Attr} .................................................. +class QXK_Attr { +public: + QP::QActive * volatile curr; + QP::QActive * volatile next; + QP::QActive * volatile prev; + std::uint_fast8_t volatile actPrio; + std::uint_fast8_t volatile lockCeil; + std::uint_fast8_t volatile lockHolder; + QP::QPSet readySet; + +#ifndef Q_UNSAFE + QP::QPSet readySet_dis; +#endif // ndef Q_UNSAFE +}; // class QXK_Attr + +//${QXK-extern-C::QXK_priv_} ................................................. +extern QXK_Attr QXK_priv_; + +//${QXK-extern-C::QXK_sched_} ................................................ +std::uint_fast8_t QXK_sched_() noexcept; + +//${QXK-extern-C::QXK_activate_} ............................................. +void QXK_activate_() noexcept; + +//${QXK-extern-C::QXK_contextSw_} ............................................ +void QXK_contextSw_(QP::QActive * const next); + +//${QXK-extern-C::QXK_threadExit_} ........................................... +void QXK_threadExit_(); +//$enddecl${QXK-extern-C} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +} // extern "C" + +//============================================================================ +// interface used only for internal implementation, but not in applications +#ifdef QP_IMPL + +//$declare${QXK-impl} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv + +//${QXK-impl::QF_SCHED_STAT_} ................................................ +#define QF_SCHED_STAT_ QSchedStatus lockStat_; + +//${QXK-impl::QF_SCHED_LOCK_} ................................................ +#define QF_SCHED_LOCK_(ceil_) do { \ + if (QXK_ISR_CONTEXT_()) { \ + lockStat_ = 0xFFU; \ + } else { \ + lockStat_ = QXK::schedLock((ceil_)); \ + } \ +} while (false) + +//${QXK-impl::QF_SCHED_UNLOCK_} .............................................. +#define QF_SCHED_UNLOCK_() do { \ + if (lockStat_ != 0xFFU) { \ + QXK::schedUnlock(lockStat_); \ + } \ +} while (false) + +//${QXK-impl::QACTIVE_EQUEUE_WAIT_} .......................................... +// QXK native event queue waiting +#define QACTIVE_EQUEUE_WAIT_(me_) \ + Q_ASSERT_INCRIT(310, (me_)->m_eQueue.m_frontEvt != nullptr) + +//${QXK-impl::QACTIVE_EQUEUE_SIGNAL_} ........................................ +#ifndef Q_UNSAFE +// QXK native event queue signalling +#define QACTIVE_EQUEUE_SIGNAL_(me_) do { \ + QXK_priv_.readySet.insert( \ + static_cast((me_)->m_prio)); \ + QXK_priv_.readySet.update_(&QXK_priv_.readySet_dis); \ + if (!QXK_ISR_CONTEXT_()) { \ + if (QXK_sched_() != 0U) { \ + QXK_activate_(); \ + } \ + } \ +} while (false) +#endif // ndef Q_UNSAFE + +//${QXK-impl::QACTIVE_EQUEUE_SIGNAL_} ........................................ +#ifdef Q_UNSAFE +// QXK native event queue signalling +#define QACTIVE_EQUEUE_SIGNAL_(me_) do { \ + QXK_priv_.readySet.insert( \ + static_cast((me_)->m_prio)); \ + if (!QXK_ISR_CONTEXT_()) { \ + if (QXK_sched_() != 0U) { \ + QXK_activate_(); \ + } \ + } \ +} while (false) +#endif // def Q_UNSAFE + +//${QXK-impl::QXTHREAD_EQUEUE_SIGNAL_} ....................................... +#ifndef Q_UNSAFE +#define QXTHREAD_EQUEUE_SIGNAL_(me_) do { \ + if ((me_)->m_temp.obj == QXK_PTR_CAST_(QMState*, &(me_)->m_eQueue)) { \ + static_cast(QXTHREAD_CAST_(me_)->teDisarm_()); \ + QXK_priv_.readySet.insert( \ + static_cast((me_)->m_prio)); \ + QXK_priv_.readySet.update_(&QXK_priv_.readySet_dis); \ + if (!QXK_ISR_CONTEXT_()) { \ + static_cast(QXK_sched_()); \ + } \ + } \ +} while (false) +#endif // ndef Q_UNSAFE + +//${QXK-impl::QXTHREAD_EQUEUE_SIGNAL_} ....................................... +#ifdef Q_UNSAFE +#define QXTHREAD_EQUEUE_SIGNAL_(me_) do { \ + if ((me_)->m_temp.obj == QXK_PTR_CAST_(QMState*, &(me_)->m_eQueue)) { \ + static_cast(QXTHREAD_CAST_(me_)->teDisarm_()); \ + QXK_priv_.readySet.insert( \ + static_cast((me_)->m_prio)); \ + if (!QXK_ISR_CONTEXT_()) { \ + static_cast(QXK_sched_()); \ + } \ + } \ +} while (false) +#endif // def Q_UNSAFE + +//${QXK-impl::QXTHREAD_CAST_} ................................................ +#define QXTHREAD_CAST_(ptr_) (static_cast(ptr_)) + +//${QXK-impl::QXK_PTR_CAST_} ................................................. +#define QXK_PTR_CAST_(type_, ptr_) (reinterpret_cast(ptr_)) +//$enddecl${QXK-impl} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +//$declare${QF_EPOOL-impl} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv + +//${QF_EPOOL-impl::QF_EPOOL_TYPE_} ........................................... +#define QF_EPOOL_TYPE_ QMPool + +//${QF_EPOOL-impl::QF_EPOOL_INIT_} ........................................... +#define QF_EPOOL_INIT_(p_, poolSto_, poolSize_, evtSize_) \ + (p_).init((poolSto_), (poolSize_), (evtSize_)) + +//${QF_EPOOL-impl::QF_EPOOL_EVENT_SIZE_} ..................................... +#define QF_EPOOL_EVENT_SIZE_(p_) ((p_).getBlockSize()) + +//${QF_EPOOL-impl::QF_EPOOL_GET_} ............................................ +#define QF_EPOOL_GET_(p_, e_, m_, qs_id_) \ + ((e_) = static_cast((p_).get((m_), (qs_id_)))) + +//${QF_EPOOL-impl::QF_EPOOL_PUT_} ............................................ +#define QF_EPOOL_PUT_(p_, e_, qs_id_) ((p_).put((e_), (qs_id_))) +//$enddecl${QF_EPOOL-impl} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +namespace QP { +namespace QXK { +enum TimeoutSigs : QSignal { + DELAY_SIG = 1U, + TIMEOUT_SIG +}; +} // namespace QXK +} // namespace QP + +#endif // QP_IMPL + +#endif // QXK_HPP_ diff --git a/qpcpp.qm b/qpcpp.qm new file mode 100644 index 0000000..6cc7cd6 --- /dev/null +++ b/qpcpp.qm @@ -0,0 +1,11980 @@ + + + QP/C++ Real-Time Embedded Framework (RTEF) +The model is used to generate the whole QP/C++ source code. + +Copyright (C) 2005 Quantum Leaps, LLC <state-machine.com>. + +SPDX-License-Identifier: GPL-3.0-or-later OR LicenseRef-QL-commercial + +This software is dual-licensed under the terms of the open source GNU +General Public License version 3 (or any later version), or alternatively, +under the terms of one of the closed source Quantum Leaps commercial +licenses. + +The terms of the open source GNU General Public License version 3 +can be found at: <www.gnu.org/licenses/gpl-3.0> + +The terms of the closed source Quantum Leaps commercial licenses +can be found at: <www.state-machine.com/licensing> + +Redistributions in source code must retain this copyright notice. +Plagiarizing this software to sidestep the license obligations is illegal. + +Contact information: +<www.state-machine.com/licensing> +<info@state-machine.com> + + + + + + + = int; + + + + = int; + + + + = float; + + + + = double; + + + + + + + {QP_VERSION_STR}; + + + + = std::uint8_t; + + + + = std::uint16_t; + + + + = std::uint32_t; + + + + + + + + + + + + {0xE0U}; + + + + : std::uint8_t { DYNAMIC }; + + + + = delete + + + + noexcept + + + : sig(s), + refCtr_(0U), + evtTag_(MARKER) + + + + noexcept + + + static_cast<void>(dummy); // unused parameter +// dynamic event already initialized in QP::QF::newX_() + + + + noexcept + + + return (e != nullptr) + && ((e->evtTag_ & 0xF0U) == MARKER); + + + + const noexcept + return static_cast<std::uint8_t>(evtTag_) & 0x0FU; + + + + + = std::uint_fast8_t; + + + + = QState (*)(void * const me, QEvt const * const e); + + + + = QState (*)(void * const me); + + + + // forward declaration + + + + = void (*)(QXThread * const me); + + + + { + QMState const * superstate; + QStateHandler const stateHandler; + QActionHandler const entryAction; + QActionHandler const exitAction; + QActionHandler const initAction; +}; + + + + { + QMState const * target; + QActionHandler const act[1]; +}; + + + + { + QStateHandler fun; + QActionHandler act; + QXThreadHandler thr; + QMState const *obj; + QMTranActTable const *tatbl; +#ifndef Q_UNSAFE + std::uintptr_t uint; +#endif +}; + + + + {4}; + + + + Abstract State Machine + + + + + + + //! All possible return values from state-handlers +//! @note +//! The order is important for algorithmic correctness. + : QState { + // unhandled and need to "bubble up" + Q_RET_SUPER, //!< event passed to superstate to handle + Q_RET_SUPER_SUB, //!< event passed to submachine superstate + Q_RET_UNHANDLED, //!< event unhandled due to a guard + + // handled and do not need to "bubble up" + Q_RET_HANDLED, //!< event handled (internal transition) + Q_RET_IGNORED, //!< event silently ignored (bubbled up to top) + + // entry/exit + Q_RET_ENTRY, //!< state entry action executed + Q_RET_EXIT, //!< state exit action executed + + // no side effects + Q_RET_NULL, //!< return value without any effect + + // transitions need to execute transition-action table in QP::QMsm + Q_RET_TRAN, //!< regular transition + Q_RET_TRAN_INIT, //!< initial transition in a state or submachine + Q_RET_TRAN_EP, //!< entry-point transition into a submachine + + // transitions that additionally clobber QHsm.m_state + Q_RET_TRAN_HIST, //!< transition to history of a given state + Q_RET_TRAN_XP //!< exit-point transition out of a submachine + }; + + + + //! Reserved signals by the QP-framework. + : QSignal { + Q_EMPTY_SIG, //!< signal to execute the default case + Q_ENTRY_SIG, //!< signal for entry actions + Q_EXIT_SIG, //!< signal for exit actions + Q_INIT_SIG //!< signal for nested initial transitions + }; + + + + noexcept + m_state.fun = nullptr; +m_temp.fun = nullptr; + + + + noexcept + // empty + + + + = 0 + + + + + + + + + + this->init(nullptr, qs_id); + + + + = 0 + + + + + + + + noexcept + return m_state.fun; + + + + noexcept + + + + + static_cast<void>(me); +static_cast<void>(e); +return Q_RET_IGNORED; // the top state ignores all events + + + + noexcept + + + m_temp.fun = target; +return Q_RET_TRAN; + + + + noexcept + + + m_temp.fun = hist; +return Q_RET_TRAN_HIST; + + + + noexcept + + + m_temp.fun = superstate; +return Q_RET_SUPER; + + + + noexcept + + + m_temp.tatbl = static_cast<QP::QMTranActTable const *>(tatbl); +return Q_RET_TRAN; + + + + noexcept + + + m_temp.tatbl = static_cast<QP::QMTranActTable const *>(tatbl); +return Q_RET_TRAN_INIT; + + + + noexcept + + + + + m_state.obj = hist; +m_temp.tatbl = static_cast<QP::QMTranActTable const *>(tatbl); +return Q_RET_TRAN_HIST; + + + + noexcept + + + m_temp.tatbl = static_cast<QP::QMTranActTable const *>(tatbl); +return Q_RET_TRAN_EP; + + + + noexcept + + + + + m_state.act = xp; +m_temp.tatbl = static_cast<QP::QMTranActTable const *>(tatbl); +return Q_RET_TRAN_XP; + + + + noexcept + + + m_temp.obj = s; +return Q_RET_ENTRY; + + + + noexcept + + + static_cast<void>(s); // unused parameter +return Q_RET_ENTRY; + + + + noexcept + + + m_temp.obj = s; +return Q_RET_EXIT; + + + + noexcept + + + static_cast<void>(s); // unused parameter +return Q_RET_EXIT; + + + + noexcept + + + m_temp.obj = s; +return Q_RET_EXIT; + + + + noexcept + + + m_temp.obj = s; +return Q_RET_SUPER_SUB; + + + + + Human-generated State Machine + + + {6}; + + + + noexcept + + + : QAsm() + +m_state.fun = Q_STATE_CAST(&top); +m_temp.fun = initial; + + + + override + + + + + QF_CRIT_STAT + +#ifdef Q_SPY +QS_CRIT_ENTRY(); +QS_MEM_SYS(); +if ((QS::priv_.flags & 0x01U) == 0U) { + QS::priv_.flags |= 0x01U; + QS_FUN_DICTIONARY(&QP::QHsm::top); +} +QS_MEM_APP(); +QS_CRIT_EXIT(); +#else +Q_UNUSED_PAR(qs_id); +#endif + +QStateHandler t = m_state.fun; + +QF_CRIT_ENTRY(); +Q_REQUIRE_INCRIT(200, (m_temp.fun != nullptr) + && (t == Q_STATE_CAST(&top))); +QF_CRIT_EXIT(); + +// execute the top-most initial tran. +QState r = (*m_temp.fun)(this, Q_EVT_CAST(QEvt)); + +QF_CRIT_ENTRY(); +// the top-most initial tran. must be taken +Q_ASSERT_INCRIT(210, r == Q_RET_TRAN); + +QS_MEM_SYS(); +QS_BEGIN_PRE_(QS_QEP_STATE_INIT, qs_id) + QS_OBJ_PRE_(this); // this state machine object + QS_FUN_PRE_(t); // the source state + QS_FUN_PRE_(m_temp.fun); // the target of the initial tran. +QS_END_PRE_() +QS_MEM_APP(); + +QF_CRIT_EXIT(); + +// drill down into the state hierarchy with initial transitions... +do { + QStateHandler path[MAX_NEST_DEPTH_]; // tran entry path array + std::int_fast8_t ip = 0; // entry path index + + path[0] = m_temp.fun; + static_cast<void>(QHSM_RESERVED_EVT_(m_temp.fun, Q_EMPTY_SIG)); + while (m_temp.fun != t) { + ++ip; + QF_CRIT_ENTRY(); + Q_ASSERT_INCRIT(220, ip < MAX_NEST_DEPTH_); + QF_CRIT_EXIT(); + path[ip] = m_temp.fun; + static_cast<void>(QHSM_RESERVED_EVT_(m_temp.fun, Q_EMPTY_SIG)); + } + m_temp.fun = path[0]; + + // retrace the entry path in reverse (desired) order... + do { + // enter path[ip] + if (QHSM_RESERVED_EVT_(path[ip], Q_ENTRY_SIG) + == Q_RET_HANDLED) + { + QS_STATE_ENTRY_(path[ip], 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_(this); // this state machine object + QS_FUN_PRE_(t); // the source state + QS_FUN_PRE_(m_temp.fun); // the target of the initial tran. + QS_END_PRE_() + QS_MEM_APP(); + QS_CRIT_EXIT(); + } +#endif // Q_SPY + +} 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_(this); // this state machine object + QS_FUN_PRE_(t); // the new active state +QS_END_PRE_() +QS_MEM_APP(); +QS_CRIT_EXIT(); + +m_state.fun = t; // change the current active state +#ifndef Q_UNSAFE +m_temp.uint = ~m_state.uint; +#endif + + + + override + + + this->init(nullptr, qs_id); + + + + override + + + + + #ifndef Q_SPY +Q_UNUSED_PAR(qs_id); +#endif + +QStateHandler s = m_state.fun; +QStateHandler t = s; +QF_CRIT_STAT + +QF_CRIT_ENTRY(); +Q_REQUIRE_INCRIT(300, (s != Q_STATE_CAST(0)) + && (m_state.uint == static_cast<std::uintptr_t>(~m_temp.uint))); +Q_REQUIRE_INCRIT(302, QEvt::verify_(e)); + +QS_MEM_SYS(); +QS_BEGIN_PRE_(QS_QEP_DISPATCH, qs_id) + QS_TIME_PRE_(); // time stamp + QS_SIG_PRE_(e->sig); // the signal of the event + QS_OBJ_PRE_(this); // this state machine object + QS_FUN_PRE_(s); // the current state +QS_END_PRE_() +QS_MEM_APP(); + +QF_CRIT_EXIT(); + +// process the event hierarchically... +QState r; +m_temp.fun = s; +do { + s = m_temp.fun; + r = (*s)(this, e); // invoke state handler s + + if (r == Q_RET_UNHANDLED) { // unhandled due to a guard? + + QS_CRIT_ENTRY(); + QS_MEM_SYS(); + QS_BEGIN_PRE_(QS_QEP_UNHANDLED, qs_id) + QS_SIG_PRE_(e->sig); // the signal of the event + QS_OBJ_PRE_(this); // this state machine object + QS_FUN_PRE_(s); // the current state + QS_END_PRE_() + QS_MEM_APP(); + QS_CRIT_EXIT(); + + r = QHSM_RESERVED_EVT_(s, Q_EMPTY_SIG); // superstate of s + } +} while (r == Q_RET_SUPER); + +if (r >= Q_RET_TRAN) { // regular tran. taken? + QStateHandler path[MAX_NEST_DEPTH_]; + + path[0] = m_temp.fun; // tran. target + path[1] = t; // current state + path[2] = s; // tran. source + + // exit current state to tran. source s... + for (; t != s; t = m_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 + static_cast<void>(QHSM_RESERVED_EVT_(t, Q_EMPTY_SIG)); + } + } + std::int_fast8_t ip = hsm_tran(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_(this); // this state machine object + QS_FUN_PRE_(t); // the source of the transition + QS_FUN_PRE_(path[0]); // the target of the tran. to history + QS_END_PRE_() + QS_MEM_APP(); + QS_CRIT_EXIT(); + } +#endif // Q_SPY + + // execute state entry actions in the desired order... + for (; ip >= 0; --ip) { + // enter path[ip] + if (QHSM_RESERVED_EVT_(path[ip], Q_ENTRY_SIG) + == Q_RET_HANDLED) + { + QS_STATE_ENTRY_(path[ip], qs_id); + } + } + t = path[0]; // stick the target into register + m_temp.fun = t; // update the next state + + // drill into the target hierarchy... + while (QHSM_RESERVED_EVT_(t, Q_INIT_SIG) == Q_RET_TRAN) { + + QS_CRIT_ENTRY(); + QS_MEM_SYS(); + QS_BEGIN_PRE_(QS_QEP_STATE_INIT, qs_id) + QS_OBJ_PRE_(this); // this state machine object + QS_FUN_PRE_(t); // the source (pseudo)state + QS_FUN_PRE_(m_temp.fun); // the target of the tran. + QS_END_PRE_() + QS_MEM_APP(); + QS_CRIT_EXIT(); + + ip = 0; + path[0] = m_temp.fun; + + // find superstate + static_cast<void>(QHSM_RESERVED_EVT_(m_temp.fun, Q_EMPTY_SIG)); + + while (m_temp.fun != t) { + ++ip; + path[ip] = m_temp.fun; + // find superstate + static_cast<void>( + QHSM_RESERVED_EVT_(m_temp.fun, Q_EMPTY_SIG)); + } + m_temp.fun = path[0]; + + // entry path must not overflow + QF_CRIT_ENTRY(); + Q_ASSERT_INCRIT(410, ip < 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_(this); // this state machine object + QS_FUN_PRE_(s); // the source of the tran. + QS_FUN_PRE_(t); // the new active state + QS_END_PRE_() + QS_MEM_APP(); + QS_CRIT_EXIT(); +} + +#ifdef Q_SPY +else if (r == Q_RET_HANDLED) { + QS_CRIT_ENTRY(); + QS_MEM_SYS(); + QS_BEGIN_PRE_(QS_QEP_INTERN_TRAN, qs_id) + QS_TIME_PRE_(); // time stamp + QS_SIG_PRE_(e->sig); // the signal of the event + QS_OBJ_PRE_(this); // this state machine object + QS_FUN_PRE_(s); // the source state + QS_END_PRE_() + QS_MEM_APP(); + QS_CRIT_EXIT(); +} +else { + QS_CRIT_ENTRY(); + QS_MEM_SYS(); + QS_BEGIN_PRE_(QS_QEP_IGNORED, qs_id) + QS_TIME_PRE_(); // time stamp + QS_SIG_PRE_(e->sig); // the signal of the event + QS_OBJ_PRE_(this); // this state machine object + QS_FUN_PRE_(m_state.fun); // the current state + QS_END_PRE_() + QS_MEM_APP(); + QS_CRIT_EXIT(); +} +#endif // Q_SPY + +m_state.fun = t; // change the current active state +#ifndef Q_UNSAFE +m_temp.uint = ~m_state.uint; +#endif + + + + noexcept + + + QF_CRIT_STAT +QF_CRIT_ENTRY(); +Q_REQUIRE_INCRIT(602, m_state.uint + == static_cast<std::uintptr_t>(~m_temp.uint)); +QF_CRIT_EXIT(); + +bool inState = false; // assume that this HSM is not in 'state' + +// scan the state hierarchy bottom-up +QState r; +do { + // do the states match? + if (m_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_(m_temp.fun, Q_EMPTY_SIG); + } +} while (r != Q_RET_IGNORED); // QHsm::top() state not reached + +#ifndef Q_UNSAFE +m_temp.uint = ~m_state.uint; +#endif + +return inState; // return the status + + + + const noexcept + return m_state.fun; + + + + noexcept + + + QStateHandler child = m_state.fun; // start with the current state +bool isFound = false; // start with the child not found + +// establish stable state configuration +m_temp.fun = child; +QState r; +do { + // is this the parent of the current child? + if (m_temp.fun == parent) { + isFound = true; // child is found + r = Q_RET_IGNORED; // break out of the loop + } + else { + child = m_temp.fun; + r = QHSM_RESERVED_EVT_(m_temp.fun, Q_EMPTY_SIG); + } +} while (r != Q_RET_IGNORED); // QHsm::top() state not reached + +#ifndef Q_UNSAFE +m_temp.uint = ~m_state.uint; +#endif + +QF_CRIT_STAT +QF_CRIT_ENTRY(); +Q_ASSERT_INCRIT(890, isFound); +QF_CRIT_EXIT(); + +return child; // return the child + + + + noexcept override + return m_state.fun; + + + + + + + + #ifndef Q_SPY +Q_UNUSED_PAR(qs_id); +#endif + +std::int_fast8_t ip = -1; // tran. entry path index +QStateHandler t = path[0]; +QStateHandler const s = path[2]; +QF_CRIT_STAT + +// (a) check source==target (tran. to self)... +if (s == t) { + // exit source s + if (QHSM_RESERVED_EVT_(s, Q_EXIT_SIG) == Q_RET_HANDLED) { + QS_STATE_EXIT_(s, qs_id); + } + ip = 0; // enter the target +} +else { + // find superstate of target + static_cast<void>(QHSM_RESERVED_EVT_(t, Q_EMPTY_SIG)); + + t = m_temp.fun; + + // (b) check source==target->super... + if (s == t) { + ip = 0; // enter the target + } + else { + // find superstate of src + static_cast<void>(QHSM_RESERVED_EVT_(s, Q_EMPTY_SIG)); + + // (c) check source->super==target->super... + if (m_temp.fun == t) { + // exit source s + if (QHSM_RESERVED_EVT_(s, Q_EXIT_SIG) == Q_RET_HANDLED) { + QS_STATE_EXIT_(s, qs_id); + } + ip = 0; // enter the target + } + else { + // (d) check source->super==target... + if (m_temp.fun == path[0]) { + // exit source s + if (QHSM_RESERVED_EVT_(s, Q_EXIT_SIG) == Q_RET_HANDLED) { + QS_STATE_EXIT_(s, qs_id); + } + } + else { + // (e) check rest of source==target->super->super.. + // and store the entry path along the way + std::int_fast8_t iq = 0; // indicate that LCA was found + ip = 1; // enter target and its superstate + path[1] = t; // save the superstate of target + t = m_temp.fun; // save source->super + + // find target->super->super... + QState r = QHSM_RESERVED_EVT_(path[1], Q_EMPTY_SIG); + while (r == Q_RET_SUPER) { + ++ip; + path[ip] = m_temp.fun; // store the entry path + if (m_temp.fun == s) { // is it the source? + iq = 1; // indicate that the LCA found + + // entry path must not overflow + QF_CRIT_ENTRY(); + Q_ASSERT_INCRIT(510, ip < 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_(m_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 < 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 + static_cast<void>( + QHSM_RESERVED_EVT_(t, Q_EMPTY_SIG)); + } + t = m_temp.fun; // set to super of t + iq = ip; + do { + // is this the LCA? + if (t == path[iq]) { + ip = iq - 1; // do not enter the LCA + iq = -1; // break out of inner loop + r = Q_RET_HANDLED; // break outer loop + } + else { + --iq; + } + } while (iq >= 0); + } while (r != Q_RET_HANDLED); + } + } + } + } + } +} +return ip; + + + + + Machine-generated State Machine + + + noexcept + + + : QAsm() + +m_state.obj = &l_msm_top_s; // the current state (top) +m_temp.fun = initial; // the initial tran. handler + + + + override + + + + + #ifndef Q_SPY +Q_UNUSED_PAR(qs_id); +#endif + +QF_CRIT_STAT +QF_CRIT_ENTRY(); +Q_REQUIRE_INCRIT(200, (m_temp.fun != nullptr) + && (m_state.obj == &l_msm_top_s)); +QF_CRIT_EXIT(); + +// execute the top-most initial tran. +QState r = (*m_temp.fun)(this, Q_EVT_CAST(QEvt)); + +QF_CRIT_ENTRY(); +// the top-most initial tran. must be taken +Q_ASSERT_INCRIT(210, r == Q_RET_TRAN_INIT); + +QS_MEM_SYS(); +QS_BEGIN_PRE_(QS_QEP_STATE_INIT, qs_id) + QS_OBJ_PRE_(this); // this state machine object + QS_FUN_PRE_(m_state.obj->stateHandler); // source state + QS_FUN_PRE_(m_temp.tatbl->target->stateHandler); // target state +QS_END_PRE_() +QS_MEM_APP(); + +QF_CRIT_EXIT(); + +// set state to the last tran. target +m_state.obj = m_temp.tatbl->target; + +// drill down into the state hierarchy with initial transitions... +do { + // execute the tran. table + r = execTatbl_(m_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_(this); // this state machine object + QS_FUN_PRE_(m_state.obj->stateHandler); // the new current state +QS_END_PRE_() +QS_MEM_APP(); +QS_CRIT_EXIT(); + +#ifndef Q_UNSAFE +m_temp.uint = ~m_state.uint; +#endif + + + + override + + + this->init(nullptr, qs_id); + + + + override + + + + + #ifndef Q_SPY +Q_UNUSED_PAR(qs_id); +#endif + +QMState const *s = m_state.obj; // store the current state +QMState const *t = s; + +QF_CRIT_STAT +QF_CRIT_ENTRY(); +Q_REQUIRE_INCRIT(300, (s != nullptr) + && (m_state.uint == static_cast<std::uintptr_t>(~m_temp.uint))); +Q_REQUIRE_INCRIT(302, QEvt::verify_(e)); + +QS_MEM_SYS(); +QS_BEGIN_PRE_(QS_QEP_DISPATCH, qs_id) + QS_TIME_PRE_(); // time stamp + QS_SIG_PRE_(e->sig); // the signal of the event + QS_OBJ_PRE_(this); // this state machine object + QS_FUN_PRE_(s->stateHandler); // the current state handler +QS_END_PRE_() +QS_MEM_APP(); + +QF_CRIT_EXIT(); + +// scan the state hierarchy up to the top state... +QState r; +do { + r = (*t->stateHandler)(this, e); // call state handler function + + // event handled? (the most frequent case) + if (r >= Q_RET_HANDLED) { + break; // done scanning the state hierarchy + } + // event unhandled and passed to the superstate? + else if (r == Q_RET_SUPER) { + t = t->superstate; // advance to the superstate + } + // event unhandled and passed to a submachine superstate? + else if (r == Q_RET_SUPER_SUB) { + t = m_temp.obj; // current host state of the submachie + } + else { // event unhandled due to a guard? + QF_CRIT_ENTRY(); + // event must be unhandled due to a guard evaluating to 'false' + Q_ASSERT_INCRIT(310, r == Q_RET_UNHANDLED); + + QS_MEM_SYS(); + QS_BEGIN_PRE_(QS_QEP_UNHANDLED, qs_id) + QS_SIG_PRE_(e->sig); // the signal of the event + QS_OBJ_PRE_(this); // this state machine object + QS_FUN_PRE_(t->stateHandler); // the current state + QS_END_PRE_() + QS_MEM_APP(); + + QF_CRIT_EXIT(); + + t = t->superstate; // advance to the superstate + } +} while (t != nullptr); + +if (r >= Q_RET_TRAN) { // any kind of tran. taken? +#ifdef Q_SPY + QMState const * const ts = t; // transition source for QS tracing + + QF_CRIT_ENTRY(); + // the tran. source state must not be nullptr + Q_ASSERT_INCRIT(320, ts != nullptr); + QF_CRIT_EXIT(); +#endif // Q_SPY + + do { + // save the tran-action table before it gets clobbered + QMTranActTable const * const tatbl = m_temp.tatbl; + QAsmAttr tmp; // temporary to save intermediate values + + // was TRAN, TRAN_INIT, or TRAN_EP taken? + if (r <= Q_RET_TRAN_EP) { + m_temp.obj = nullptr; // clear + exitToTranSource_(s, t, qs_id); + r = execTatbl_(tatbl, qs_id); + s = m_state.obj; + } + // was a tran. segment to history taken? + else if (r == Q_RET_TRAN_HIST) { + tmp.obj = m_state.obj; // save history + m_state.obj = s; // restore the original state + exitToTranSource_(s, t, qs_id); + static_cast<void>(execTatbl_(tatbl, qs_id)); + r = enterHistory_(tmp.obj, qs_id); + s = m_state.obj; + } + else { + QF_CRIT_ENTRY(); + // must be tran. to exit point + Q_ASSERT_INCRIT(340, r == Q_RET_TRAN_XP); + QF_CRIT_EXIT(); + + tmp.act = m_state.act; // save XP action + m_state.obj = s; // restore the original state + r = (*tmp.act)(this); // execute the XP action + if (r == Q_RET_TRAN) { // XP -> TRAN ? +#ifdef Q_SPY + tmp.tatbl = m_temp.tatbl; // save m_temp +#endif // Q_SPY + exitToTranSource_(s, t, qs_id); + // take the tran-to-XP segment inside submachine + static_cast<void>(execTatbl_(tatbl, qs_id)); + s = m_state.obj; +#ifdef Q_SPY + m_temp.tatbl = tmp.tatbl; // restore m_temp +#endif // Q_SPY + } + else if (r == Q_RET_TRAN_HIST) { // XP -> HIST ? + tmp.obj = m_state.obj; // save the history + m_state.obj = s; // restore the original state +#ifdef Q_SPY + s = m_temp.obj; // save m_temp +#endif // Q_SPY + exitToTranSource_(m_state.obj, t, qs_id); + // take the tran-to-XP segment inside submachine + static_cast<void>(execTatbl_(tatbl, qs_id)); +#ifdef Q_SPY + m_temp.obj = s; // restore me->temp +#endif // Q_SPY + s = m_state.obj; + m_state.obj = tmp.obj; // restore the history + } + else { + QF_CRIT_ENTRY(); + // TRAN_XP must NOT be followed by any other tran type + Q_ASSERT_INCRIT(330, r < Q_RET_TRAN); + QF_CRIT_EXIT(); + } + } + + t = s; // set target to the current state + + } 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_(this); // this state machine object + QS_FUN_PRE_(ts->stateHandler); // the tran. source + QS_FUN_PRE_(s->stateHandler); // the new active state + QS_END_PRE_() + QS_MEM_APP(); + 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 nullptr + Q_ASSERT_INCRIT(340, t != nullptr); + + 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_(this); // this state machine object + QS_FUN_PRE_(t->stateHandler); // the source state + QS_END_PRE_() + QS_MEM_APP(); + + QF_CRIT_EXIT(); +} +// event bubbled to the 'top' state? +else if (t == nullptr) { + QS_CRIT_ENTRY(); + QS_MEM_SYS(); + QS_BEGIN_PRE_(QS_QEP_IGNORED, qs_id) + QS_TIME_PRE_(); // time stamp + QS_SIG_PRE_(e->sig); // the signal of the event + QS_OBJ_PRE_(this); // this state machine object + QS_FUN_PRE_(s->stateHandler); // the current state + QS_END_PRE_() + QS_MEM_APP(); + QS_CRIT_EXIT(); +} +#endif // Q_SPY +else { + // empty +} + +#ifndef Q_UNSAFE +m_temp.uint = ~m_state.uint; +#endif + + + + noexcept override + return m_state.obj->stateHandler; + + + + const noexcept + + + bool inState = false; // assume that this MSM is not in 'state' + +for (QMState const *s = m_state.obj; + s != nullptr; + s = s->superstate) +{ + if (s == stateObj) { + inState = true; // match found, return 'true' + break; + } +} +return inState; + + + + const noexcept + return m_state.obj; + + + + const noexcept + + + QMState const *child = m_state.obj; +bool isFound = false; // start with the child not found +QMState const *s; + +for (s = m_state.obj; s != nullptr; s = s->superstate) { + if (s == parent) { + isFound = true; // child is found + break; + } + else { + child = s; + } +} +if (!isFound) { // still not found? + for (s = m_temp.obj; s != nullptr; 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 + + + + + + + + #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 != nullptr); +QF_CRIT_EXIT(); + +QState r = Q_RET_NULL; +for (QActionHandler const *a = &tatbl->act[0]; *a != nullptr; ++a) { + r = (*(*a))(this); // call the action through the 'a' pointer +#ifdef Q_SPY + QS_CRIT_ENTRY(); + QS_MEM_SYS(); + if (r == Q_RET_ENTRY) { + QS_BEGIN_PRE_(QS_QEP_STATE_ENTRY, qs_id) + QS_OBJ_PRE_(this); // this state machine object + QS_FUN_PRE_(m_temp.obj->stateHandler); // entered state + QS_END_PRE_() + } + else if (r == Q_RET_EXIT) { + QS_BEGIN_PRE_(QS_QEP_STATE_EXIT, qs_id) + QS_OBJ_PRE_(this); // this state machine object + QS_FUN_PRE_(m_temp.obj->stateHandler); // exited state + QS_END_PRE_() + } + else if (r == Q_RET_TRAN_INIT) { + QS_BEGIN_PRE_(QS_QEP_STATE_INIT, qs_id) + QS_OBJ_PRE_(this); // this state machine object + QS_FUN_PRE_(tatbl->target->stateHandler); // source + QS_FUN_PRE_(m_temp.tatbl->target->stateHandler); // target + QS_END_PRE_() + } + else if (r == Q_RET_TRAN_EP) { + QS_BEGIN_PRE_(QS_QEP_TRAN_EP, qs_id) + QS_OBJ_PRE_(this); // this state machine object + QS_FUN_PRE_(tatbl->target->stateHandler); // source + QS_FUN_PRE_(m_temp.tatbl->target->stateHandler); // target + QS_END_PRE_() + } + else if (r == Q_RET_TRAN_XP) { + QS_BEGIN_PRE_(QS_QEP_TRAN_XP, qs_id) + QS_OBJ_PRE_(this); // this state machine object + QS_FUN_PRE_(tatbl->target->stateHandler); // source + QS_FUN_PRE_(m_temp.tatbl->target->stateHandler); // target + QS_END_PRE_() + } + else { + // empty + } + QS_MEM_APP(); + QS_CRIT_EXIT(); +#endif // Q_SPY +} + +m_state.obj = (r >= Q_RET_TRAN) + ? m_temp.tatbl->target + : tatbl->target; +return r; + + + + + + + + + + #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 != nullptr) { + // execute the exit action + static_cast<void>((*s->exitAction)(this)); + + QS_CRIT_ENTRY(); + QS_MEM_SYS(); + QS_BEGIN_PRE_(QS_QEP_STATE_EXIT, qs_id) + QS_OBJ_PRE_(this); // this state machine object + QS_FUN_PRE_(s->stateHandler); // the exited state handler + QS_END_PRE_() + QS_MEM_APP(); + QS_CRIT_EXIT(); + } + + s = s->superstate; // advance to the superstate + + if (s == nullptr) { // reached the top of a submachine? + s = m_temp.obj; // the superstate from QM_SM_EXIT() + QF_CRIT_ENTRY(); + Q_ASSERT_INCRIT(510, s != nullptr); + QF_CRIT_EXIT(); + } +} + + + + + + + + #ifndef Q_SPY +Q_UNUSED_PAR(qs_id); +#endif + +QMState const *s = hist; +QMState const *ts = m_state.obj; // tran. source +QMState const *epath[MAX_ENTRY_DEPTH_]; + +QF_CRIT_STAT + +QS_CRIT_ENTRY(); +QS_MEM_SYS(); +QS_BEGIN_PRE_(QS_QEP_TRAN_HIST, qs_id) + QS_OBJ_PRE_(this); // this state machine object + QS_FUN_PRE_(ts->stateHandler); // source state handler + QS_FUN_PRE_(hist->stateHandler); // target state handler +QS_END_PRE_() +QS_MEM_APP(); +QS_CRIT_EXIT(); + +std::int_fast8_t i = 0; // tran. entry path index +while (s != ts) { + if (s->entryAction != nullptr) { + QF_CRIT_ENTRY(); + Q_ASSERT_INCRIT(620, i < MAX_ENTRY_DEPTH_); + QF_CRIT_EXIT(); + epath[i] = s; + ++i; + } + s = s->superstate; + if (s == nullptr) { + ts = s; // force exit from the for-loop + } +} + +// retrace the entry path in reverse (desired) order... +while (i > 0) { + --i; + (*epath[i]->entryAction)(this); // run entry action in epath[i] + + QS_CRIT_ENTRY(); + QS_MEM_SYS(); + QS_BEGIN_PRE_(QS_QEP_STATE_ENTRY, qs_id) + QS_OBJ_PRE_(this); + QS_FUN_PRE_(epath[i]->stateHandler); // entered state handler + QS_END_PRE_() + QS_MEM_APP(); + QS_CRIT_EXIT(); +} + +m_state.obj = hist; // set current state to the tran. target + +// initial tran. present? +QState r; +if (hist->initAction != nullptr) { + r = (*hist->initAction)(this); // execute the tran. action +} +else { + r = Q_RET_NULL; +} + +return r; + + + + const noexcept + return &l_msm_top_s; + + + + + + + + + + \ + QP::QState state_ ## _h(QP::QEvt const * const e); \ + static QP::QState state_(void * const me, QP::QEvt const * const e) + + + + + + + + \ + QP::QState subclass_::state_(void * const me, QP::QEvt const * const e) { \ + return static_cast<subclass_ *>(me)->state_ ## _h(e); } \ + QP::QState subclass_::state_ ## _h(QP::QEvt const * const e) + + + + (Q_RET_HANDLED) + + + + (Q_RET_UNHANDLED) + + + + + + (static_cast<subclass_ const *>(e)) + + + + + + \ + (reinterpret_cast<QP::QStateHandler>(handler_)) + + + + + + \ + QP::QState state_ ## _h(QP::QEvt const * const e); \ + static QP::QState state_(void * const me, QP::QEvt const * const e); \ + static QP::QMState const state_ ## _s + + + + + + + + \ + QP::QState state_ ## _h(QP::QEvt const * const e);\ + static QP::QState state_(void * const me, QP::QEvt const * const e); \ + static SM_ ## subm_ const state_ ## _s + + + + + + \ + QP::QState action_ ## _h(); \ + static QP::QState action_(void * const me) + + + + + + + + \ + QP::QState subclass_::state_(void * const me, QP::QEvt const * const e) {\ + return static_cast<subclass_ *>(me)->state_ ## _h(e); } \ + QP::QState subclass_::state_ ## _h(QP::QEvt const * const e) + + + + + + + + \ + QP::QState subclass_::action_(void * const me) { \ + return static_cast<subclass_ *>(me)->action_ ## _h(); } \ + QP::QState subclass_::action_ ## _h() + + + + (Q_RET_HANDLED) + + + + (Q_RET_HANDLED) + + + + (Q_RET_SUPER) + + + + (nullptr) + + + + (nullptr) + + + + + + (static_cast<void>(par_)) + + + + + + (sizeof(array_) / sizeof((array_)[0U])) + + + + + + + + (reinterpret_cast<type_ *>(uint_)) + + + + + + init((qs_id_)) + + + + + + init(0U) + + + + + + + + dispatch((e_), (qs_id_)) + + + + + + + + dispatch((e_), 0U) + + + + + + + + + = std::uint16_t; + + + + = std::uint8_t; + + + + = std::uint16_t; + + + + = std::uint32_t; + + + + = std::uint8_t; + + + + = std::uint16_t; + + + + = std::uint32_t; + + + + noexcept + + + static std::uint8_t const log2LUT[16] = { + 0U, 1U, 2U, 2U, 3U, 3U, 3U, 3U, + 4U, 4U, 4U, 4U, 4U, 4U, 4U, 4U +}; +std::uint_fast8_t n = 0U; +QP::QPSetBits t; + +#if (QF_MAX_ACTIVE > 16U) +t = static_cast<QP::QPSetBits>(x >> 16U); +if (t != 0U) { + n += 16U; + x = t; +} +#endif +#if (QF_MAX_ACTIVE > 8U) +t = (x >> 8U); +if (t != 0U) { + n += 8U; + x = t; +} +#endif +t = (x >> 4U); +if (t != 0U) { + n += 4U; + x = t; +} +return n + log2LUT[x]; + + + + + + + + noexcept + m_bits[0] = 0U; +#if (QF_MAX_ACTIVE > 32) +m_bits[1] = 0U; +#endif + + + + const noexcept + #if (QF_MAX_ACTIVE <= 32U) +return (m_bits[0] == 0U); +#else +return (m_bits[0] == 0U) ? (m_bits[1] == 0U) : false; +#endif + + + + const noexcept + #if (QF_MAX_ACTIVE <= 32U) +return (m_bits[0] != 0U); +#else +return (m_bits[0] != 0U) ? true : (m_bits[1] != 0U); +#endif + + + + const noexcept + + + #if (QF_MAX_ACTIVE <= 32U) +return (m_bits[0] & (static_cast<QPSetBits>(1U) << (n - 1U))) != 0U; +#else +return (n <= 32U) + ? ((m_bits[0] & (static_cast<QPSetBits>(1U) << (n - 1U))) != 0U) + : ((m_bits[1] & (static_cast<QPSetBits>(1U) << (n - 33U))) != 0U); +#endif + + + + noexcept + + + #if (QF_MAX_ACTIVE <= 32U) +m_bits[0] = (m_bits[0] | (static_cast<QPSetBits>(1U) << (n - 1U))); +#else +if (n <= 32U) { + m_bits[0] = (m_bits[0] | (static_cast<QPSetBits>(1U) << (n - 1U))); +} +else { + m_bits[1] = (m_bits[1] | (static_cast<QPSetBits>(1U) << (n - 33U))); +} +#endif + + + + noexcept + + + #if (QF_MAX_ACTIVE <= 32U) +m_bits[0] = (m_bits[0] & static_cast<QPSetBits>(~(1U << (n - 1U)))); +#else +if (n <= 32U) { + (m_bits[0] = (m_bits[0] & ~(static_cast<QPSetBits>(1U) << (n - 1U)))); +} +else { + (m_bits[1] = (m_bits[1] & ~(static_cast<QPSetBits>(1U) << (n - 33U)))); +} +#endif + + + + const noexcept + #if (QF_MAX_ACTIVE <= 32U) +return QF_LOG2(m_bits[0]); +#else +return (m_bits[1] != 0U) + ? (QF_LOG2(m_bits[1]) + 32U) + : (QF_LOG2(m_bits[0])); +#endif + + + + const noexcept + + + dis->m_bits[0] = ~m_bits[0]; +#if (QF_MAX_ACTIVE > 32U) +dis->m_bits[1] = ~m_bits[1]; +#endif + + + + const noexcept + + + #if (QF_MAX_ACTIVE <= 32U) +return m_bits[0] == static_cast<QPSetBits>(~dis->m_bits[0]); +#else +return (m_bits[0] == static_cast<QPSetBits>(~dis->m_bits[0])) + && (m_bits[1] == static_cast<QPSetBits>(~dis->m_bits[1])); +#endif + + + + + + + + + + + // friends... + + + + + + + + + + + + + + + + + + + + + + + + + + // friends... + + + + + + + + + + + + + + + + + + + + noexcept + + + : QAsm(), + m_prio(0U), + m_pthre(0U) + +m_state.fun = Q_STATE_CAST(&top); +m_temp.fun = initial; + + + + override + + + + + reinterpret_cast<QHsm *>(this)->QHsm::init(e, qs_id); + + + + override + + + this->init(nullptr, qs_id); + + + + override + + + + + reinterpret_cast<QHsm *>(this)->QHsm::dispatch(e, qs_id); + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + this->start(prioSpec, qSto, qLen, stkSto, stkSize, nullptr); + + + + + + noexcept + QF_CRIT_STAT +QF_CRIT_ENTRY(); +QF_MEM_SYS(); + +if (m_pthre == 0U) { // preemption-threshold not defined? + m_pthre = m_prio; // apply the default +} + +#ifndef Q_UNSAFE + +Q_REQUIRE_INCRIT(100, (0U < m_prio) && (m_prio <= QF_MAX_ACTIVE) + && (registry_[m_prio] == nullptr) + && (m_prio <= m_pthre)); + +std::uint8_t prev_thre = m_pthre; +std::uint8_t next_thre = m_pthre; + +std::uint_fast8_t p; +for (p = static_cast<std::uint_fast8_t>(m_prio) - 1U; p > 0U; --p) { + if (registry_[p] != nullptr) { + prev_thre = registry_[p]->m_pthre; + break; + } +} +for (p = static_cast<std::uint_fast8_t>(m_prio) + 1U; + p <= QF_MAX_ACTIVE; ++p) +{ + if (registry_[p] != nullptr) { + next_thre = registry_[p]->m_pthre; + break; + } +} + +Q_ASSERT_INCRIT(190, (prev_thre <= m_pthre) + && (m_pthre <= next_thre)); +#endif // Q_UNSAFE + +// register the AO at the QF-prio. +registry_[m_prio] = this; + +QF_MEM_APP(); +QF_CRIT_EXIT(); + + + + noexcept + std::uint_fast8_t const p = static_cast<std::uint_fast8_t>(m_prio); + +QF_CRIT_STAT +QF_CRIT_ENTRY(); +QF_MEM_SYS(); + +Q_REQUIRE_INCRIT(200, (0U < p) && (p <= QF_MAX_ACTIVE) + && (registry_[p] == this)); +registry_[p] = nullptr; // free-up the priority level +m_state.fun = nullptr; // invalidate the state + +QF_MEM_APP(); +QF_CRIT_EXIT(); + + + + noexcept + + + + + + + #ifndef Q_SPY +Q_UNUSED_PAR(sender); +#endif + +#ifdef Q_UTEST // test? +#if Q_UTEST != 0 // testing QP-stub? +if (m_temp.fun == Q_STATE_CAST(0)) { // QActiveDummy? + return static_cast<QActiveDummy *>(this)->fakePost(e, margin, sender); +} +#endif +#endif + +QF_CRIT_STAT +QF_CRIT_ENTRY(); +QF_MEM_SYS(); + +Q_REQUIRE_INCRIT(102, QEvt::verify_(e)); + +QEQueueCtr nFree = m_eQueue.m_nFree; // get volatile into temporary + +// test-probe#1 for faking queue overflow +QS_TEST_PROBE_DEF(&QActive::post_) +QS_TEST_PROBE_ID(1, + nFree = 0U; +) + +bool status; +if (margin == QF::NO_MARGIN) { + if (nFree > 0U) { + status = true; // can post + } + else { + status = false; // cannot post + Q_ERROR_INCRIT(190); // must be able to post the event + } +} +else if (nFree > static_cast<QEQueueCtr>(margin)) { + status = true; // can post +} +else { + status = false; // cannot post, but don't assert +} + +// is it a mutable event? +if (e->getPoolId_() != 0U) { + QEvt_refCtr_inc_(e); // increment the reference counter +} + +if (status) { // can post the event? + + --nFree; // one free entry just used up + m_eQueue.m_nFree = nFree; // update the original + if (m_eQueue.m_nMin > nFree) { + m_eQueue.m_nMin = nFree; // update minimum so far + } + + QS_BEGIN_PRE_(QS_QF_ACTIVE_POST, m_prio) + QS_TIME_PRE_(); // timestamp + QS_OBJ_PRE_(sender); // the sender object + QS_SIG_PRE_(e->sig); // the signal of the event + QS_OBJ_PRE_(this); // this active object + QS_2U8_PRE_(e->getPoolId_(), e->refCtr_); // poolId & refCtr + QS_EQC_PRE_(nFree); // # free entries + QS_EQC_PRE_(m_eQueue.m_nMin); // min # free entries + QS_END_PRE_() + +#ifdef Q_UTEST + // callback to examine the posted event under the same conditions + // as producing the #QS_QF_ACTIVE_POST trace record, which are: + // the local filter for this AO ('m_prio') is set + if (QS_LOC_CHECK_(m_prio)) { + QS::onTestPost(sender, this, e, status); + } +#endif + + if (m_eQueue.m_frontEvt == nullptr) { // empty queue? + m_eQueue.m_frontEvt = e; // deliver event directly +#ifdef QXK_HPP_ + if (m_state.act == Q_ACTION_CAST(0)) { // eXtended thread? + QXTHREAD_EQUEUE_SIGNAL_(this); // signal the event queue + } + else { + QACTIVE_EQUEUE_SIGNAL_(this); // signal the event queue + } +#else + QACTIVE_EQUEUE_SIGNAL_(this); // signal the event queue +#endif + } + // queue is not empty, insert event into the ring-buffer + else { + // insert event into the ring buffer (FIFO) + m_eQueue.m_ring[m_eQueue.m_head] = e; + + if (m_eQueue.m_head == 0U) { // need to wrap head? + m_eQueue.m_head = m_eQueue.m_end; // wrap around + } + // advance the head (counter clockwise) + m_eQueue.m_head = (m_eQueue.m_head - 1U); + } + + QF_MEM_APP(); + QF_CRIT_EXIT(); +} +else { // cannot post the event + + QS_BEGIN_PRE_(QS_QF_ACTIVE_POST_ATTEMPT, m_prio) + QS_TIME_PRE_(); // timestamp + QS_OBJ_PRE_(sender); // the sender object + QS_SIG_PRE_(e->sig); // the signal of the event + QS_OBJ_PRE_(this); // this active object + QS_2U8_PRE_(e->getPoolId_(), 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_(m_prio)) { + QS::onTestPost(sender, this, e, status); + } +#endif + + QF_MEM_APP(); + QF_CRIT_EXIT(); + +#if (QF_MAX_EPOOL > 0U) + QF::gc(e); // recycle the event to avoid a leak +#endif +} + +return status; + + + + noexcept + + + #ifdef Q_UTEST // test? +#if Q_UTEST != 0 // testing QP-stub? +if (m_temp.fun == Q_STATE_CAST(0)) { // QActiveDummy? + static_cast<QActiveDummy *>(this)->QActiveDummy::fakePostLIFO(e); + return; +} +#endif +#endif + +QF_CRIT_STAT +QF_CRIT_ENTRY(); +QF_MEM_SYS(); + +Q_REQUIRE_INCRIT(202, QEvt::verify_(e)); + +#ifdef QXK_HPP_ +Q_REQUIRE_INCRIT(200, m_state.act != Q_ACTION_CAST(0)); +#endif + +QEQueueCtr nFree = m_eQueue.m_nFree; // get volatile into temporary + +// test-probe#1 for faking queue overflow +QS_TEST_PROBE_DEF(&QActive::postLIFO) +QS_TEST_PROBE_ID(1, + nFree = 0U; +) + +Q_REQUIRE_INCRIT(201, nFree != 0U); + +if (e->getPoolId_() != 0U) { // is it a mutable event? + QEvt_refCtr_inc_(e); // increment the reference counter +} + +--nFree; // one free entry just used up +m_eQueue.m_nFree = nFree; // update the original +if (m_eQueue.m_nMin > nFree) { + m_eQueue.m_nMin = nFree; // update minimum so far +} + +QS_BEGIN_PRE_(QS_QF_ACTIVE_POST_LIFO, m_prio) + QS_TIME_PRE_(); // timestamp + QS_SIG_PRE_(e->sig); // the signal of this event + QS_OBJ_PRE_(this); // this active object + QS_2U8_PRE_(e->getPoolId_(), e->refCtr_); // poolId & refCtr + QS_EQC_PRE_(nFree); // # free entries + QS_EQC_PRE_(m_eQueue.m_nMin); // min # free entries +QS_END_PRE_() + +#ifdef Q_UTEST +// callback to examine the posted event under the same conditions +// as producing the #QS_QF_ACTIVE_POST trace record, which are: +// the local filter for this AO ('m_prio') is set +if (QS_LOC_CHECK_(m_prio)) { + QS::onTestPost(nullptr, this, e, true); +} +#endif + +QEvt const * const frontEvt = m_eQueue.m_frontEvt; +m_eQueue.m_frontEvt = e; // deliver the event directly to the front + +if (frontEvt == nullptr) { // was the queue empty? + QACTIVE_EQUEUE_SIGNAL_(this); // signal the event queue +} +else { // queue was not empty, leave the event in the ring-buffer + m_eQueue.m_tail = (m_eQueue.m_tail + 1U); + if (m_eQueue.m_tail == m_eQueue.m_end) { // need to wrap the tail? + m_eQueue.m_tail = 0U; // wrap around + } + + m_eQueue.m_ring[m_eQueue.m_tail] = frontEvt; +} + +QF_MEM_APP(); +QF_CRIT_EXIT(); + + + + noexcept + QF_CRIT_STAT +QF_CRIT_ENTRY(); +QF_MEM_SYS(); + +QACTIVE_EQUEUE_WAIT_(this); // wait for event to arrive directly + +// always remove evt from the front +QEvt const * const e = m_eQueue.m_frontEvt; +QEQueueCtr const nFree = m_eQueue.m_nFree + 1U; // get volatile into tmp +m_eQueue.m_nFree = nFree; // update the # free + +if (nFree <= m_eQueue.m_end) { // any events in the ring buffer? + // remove event from the tail + m_eQueue.m_frontEvt = m_eQueue.m_ring[m_eQueue.m_tail]; + if (m_eQueue.m_tail == 0U) { // need to wrap the tail? + m_eQueue.m_tail = m_eQueue.m_end; // wrap around + } + m_eQueue.m_tail = (m_eQueue.m_tail - 1U); + + QS_BEGIN_PRE_(QS_QF_ACTIVE_GET, m_prio) + QS_TIME_PRE_(); // timestamp + QS_SIG_PRE_(e->sig); // the signal of this event + QS_OBJ_PRE_(this); // this active object + QS_2U8_PRE_(e->getPoolId_(), e->refCtr_); // poolId & refCtr + QS_EQC_PRE_(nFree); // # free entries + QS_END_PRE_() +} +else { + m_eQueue.m_frontEvt = nullptr; // the queue becomes empty + + // all entries in the queue must be free (+1 for fronEvt) + Q_ASSERT_INCRIT(310, nFree == (m_eQueue.m_end + 1U)); + + QS_BEGIN_PRE_(QS_QF_ACTIVE_GET_LAST, m_prio) + QS_TIME_PRE_(); // timestamp + QS_SIG_PRE_(e->sig); // the signal of this event + QS_OBJ_PRE_(this); // this active object + QS_2U8_PRE_(e->getPoolId_(), e->refCtr_); // poolId & refCtr + QS_END_PRE_() +} + +QF_MEM_APP(); +QF_CRIT_EXIT(); + +return e; + + + + noexcept + + + QF_CRIT_STAT +QF_CRIT_ENTRY(); +Q_REQUIRE_INCRIT(400, (prio <= QF_MAX_ACTIVE) + && (QActive::registry_[prio] != nullptr)); +std::uint_fast16_t const min = static_cast<std::uint_fast16_t>( + QActive::registry_[prio]->m_eQueue.getNMin()); +QF_CRIT_EXIT(); + +return min; + + + + noexcept + + + + + subscrList_ = subscrSto; +maxPubSignal_ = maxSignal; + +// initialize the subscriber list +for (enum_t sig = 0; sig < maxSignal; ++sig) { + subscrSto[sig].m_set.setEmpty(); +#ifndef Q_UNSAFE + subscrSto[sig].m_set.update_(&subscrSto[sig].m_set_dis); +#endif +} + + + + noexcept + + + + + + + #ifndef Q_SPY +Q_UNUSED_PAR(sender); +Q_UNUSED_PAR(qs_id); +#endif + +QSignal const sig = e->sig; + +QF_CRIT_STAT +QF_CRIT_ENTRY(); +QF_MEM_SYS(); + +Q_REQUIRE_INCRIT(200, sig < static_cast<QSignal>(maxPubSignal_)); +Q_REQUIRE_INCRIT(202, + subscrList_[sig].m_set.verify_(&subscrList_[sig].m_set_dis)); + +QS_BEGIN_PRE_(QS_QF_PUBLISH, qs_id) + QS_TIME_PRE_(); // the timestamp + QS_OBJ_PRE_(sender); // the sender object + QS_SIG_PRE_(e->sig); // the signal of the event + QS_2U8_PRE_(e->getPoolId_(), e->refCtr_); // poolId & refCtr +QS_END_PRE_() + +// is it a mutable event? +if (e->getPoolId_() != 0U) { + // NOTE: The reference counter of a mutable event is incremented to + // prevent premature recycling of the event while the multicasting + // is still in progress. At the end of the function, the garbage + // collector step (QF::gc()) decrements the reference counter and + // recycles the event if the counter drops to zero. This covers the + // case when the event was published without any subscribers. + QEvt_refCtr_inc_(e); +} + +// make a local, modifiable copy of the subscriber set +QPSet subscrSet = subscrList_[sig].m_set; + +QF_MEM_APP(); +QF_CRIT_EXIT(); + +if (subscrSet.notEmpty()) { // any subscribers? + // highest-prio subscriber + std::uint_fast8_t p = subscrSet.findMax(); + + QF_CRIT_ENTRY(); + QF_MEM_SYS(); + + QActive *a = registry_[p]; + // the AO must be registered with the framework + Q_ASSERT_INCRIT(210, a != nullptr); + + QF_MEM_APP(); + QF_CRIT_EXIT(); + + QF_SCHED_STAT_ + QF_SCHED_LOCK_(p); // lock the scheduler up to AO's prio + do { // loop over all subscribers + + // POST() asserts internally if the queue overflows + a->POST(e, sender); + + subscrSet.remove(p); // remove the handled subscriber + if (subscrSet.notEmpty()) { // still more subscribers? + p = subscrSet.findMax(); // highest-prio subscriber + + QF_CRIT_ENTRY(); + QF_MEM_SYS(); + + a = registry_[p]; + // the AO must be registered with the framework + Q_ASSERT_INCRIT(220, a != nullptr); + + QF_MEM_APP(); + QF_CRIT_EXIT(); + } + else { + p = 0U; // no more subscribers + } + } while (p != 0U); + QF_SCHED_UNLOCK_(); // unlock the scheduler +} + +// The following garbage collection step decrements the reference counter +// and recycles the event if the counter drops to zero. This covers both +// cases when the event was published with or without any subscribers. +#if (QF_MAX_EPOOL > 0U) +QF::gc(e); +#endif + + + + const noexcept + + + std::uint_fast8_t const p = static_cast<std::uint_fast8_t>(m_prio); + +QF_CRIT_STAT +QF_CRIT_ENTRY(); +QF_MEM_SYS(); + +Q_REQUIRE_INCRIT(300, (Q_USER_SIG <= sig) + && (sig < maxPubSignal_) + && (0U < p) && (p <= QF_MAX_ACTIVE) + && (registry_[p] == this)); +Q_REQUIRE_INCRIT(302, + subscrList_[sig].m_set.verify_(&subscrList_[sig].m_set_dis)); + +QS_BEGIN_PRE_(QS_QF_ACTIVE_SUBSCRIBE, m_prio) + QS_TIME_PRE_(); // timestamp + QS_SIG_PRE_(sig); // the signal of this event + QS_OBJ_PRE_(this); // this active object +QS_END_PRE_() + +// insert the prio. into the subscriber set +subscrList_[sig].m_set.insert(p); +#ifndef Q_UNSAFE +subscrList_[sig].m_set.update_(&subscrList_[sig].m_set_dis); +#endif + +QF_MEM_APP(); +QF_CRIT_EXIT(); + + + + const noexcept + + + std::uint_fast8_t const p = static_cast<std::uint_fast8_t>(m_prio); + +QF_CRIT_STAT +QF_CRIT_ENTRY(); +QF_MEM_SYS(); + +Q_REQUIRE_INCRIT(400, (Q_USER_SIG <= sig) + && (sig < maxPubSignal_) + && (0U < p) && (p <= QF_MAX_ACTIVE) + && (registry_[p] == this)); +Q_REQUIRE_INCRIT(402, + subscrList_[sig].m_set.verify_(&subscrList_[sig].m_set_dis)); + +QS_BEGIN_PRE_(QS_QF_ACTIVE_UNSUBSCRIBE, m_prio) + QS_TIME_PRE_(); // timestamp + QS_SIG_PRE_(sig); // the signal of this event + QS_OBJ_PRE_(this); // this active object +QS_END_PRE_() + +// remove the prio. from the subscriber set +subscrList_[sig].m_set.remove(p); +#ifndef Q_UNSAFE +subscrList_[sig].m_set.update_(&subscrList_[sig].m_set_dis); +#endif + +QF_MEM_APP(); +QF_CRIT_EXIT(); + + + + const noexcept + QF_CRIT_STAT +QF_CRIT_ENTRY(); +QF_MEM_SYS(); + +std::uint_fast8_t const p = static_cast<std::uint_fast8_t>(m_prio); + +Q_REQUIRE_INCRIT(500, (0U < p) && (p <= QF_MAX_ACTIVE) + && (registry_[p] == this)); +enum_t const maxPubSig = maxPubSignal_; + +QF_MEM_APP(); +QF_CRIT_EXIT(); + +for (enum_t sig = Q_USER_SIG; sig < maxPubSig; ++sig) { + QF_CRIT_ENTRY(); + QF_MEM_SYS(); + + if (subscrList_[sig].m_set.hasElement(p)) { + subscrList_[sig].m_set.remove(p); +#ifndef Q_UNSAFE + subscrList_[sig].m_set.update_(&subscrList_[sig].m_set_dis); +#endif + QS_BEGIN_PRE_(QS_QF_ACTIVE_UNSUBSCRIBE, m_prio) + QS_TIME_PRE_(); // timestamp + QS_SIG_PRE_(sig); // the signal of this event + QS_OBJ_PRE_(this); // this active object + QS_END_PRE_() + } + QF_MEM_APP(); + QF_CRIT_EXIT(); + + QF_CRIT_EXIT_NOP(); // prevent merging critical sections +} + + + + const noexcept + + + + + bool const status = eq->post(e, 0U, m_prio); + +QS_CRIT_STAT +QS_CRIT_ENTRY(); +QS_MEM_SYS(); +QS_BEGIN_PRE_(QS_QF_ACTIVE_DEFER, m_prio) + QS_TIME_PRE_(); // time stamp + QS_OBJ_PRE_(this); // this active object + QS_OBJ_PRE_(eq); // the deferred queue + QS_SIG_PRE_(e->sig); // the signal of the event + QS_2U8_PRE_(e->getPoolId_(), e->refCtr_); // poolId & refCtr +QS_END_PRE_() +QS_MEM_APP(); +QS_CRIT_EXIT(); + +return status; + + + + noexcept + + + QEvt const * const e = eq->get(m_prio); // get evt from deferred queue +QF_CRIT_STAT + +bool recalled; +if (e != nullptr) { // event available? + postLIFO(e); // post it to the _front_ of the AO's queue + + QF_CRIT_ENTRY(); + QF_MEM_SYS(); + + if (e->getPoolId_() != 0U) { // is it a mutable event? + + // after posting to the AO's queue the event must be referenced + // at least twice: once in the deferred event queue (eq->get() + // did NOT decrement the reference counter) and once in the + // AO's event queue. + Q_ASSERT_INCRIT(210, e->refCtr_ >= 2U); + + // we need to decrement the reference counter once, to account + // for removing the event from the deferred event queue. + QEvt_refCtr_dec_(e); // decrement the reference counter + } + + QS_BEGIN_PRE_(QS_QF_ACTIVE_RECALL, m_prio) + QS_TIME_PRE_(); // time stamp + QS_OBJ_PRE_(this); // this active object + QS_OBJ_PRE_(eq); // the deferred queue + QS_SIG_PRE_(e->sig); // the signal of the event + QS_2U8_PRE_(e->getPoolId_(), 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, m_prio) + QS_TIME_PRE_(); // time stamp + QS_OBJ_PRE_(this); // this active object + QS_OBJ_PRE_(eq); // the deferred queue + QS_END_PRE_() + + QS_MEM_APP(); + QS_CRIT_EXIT(); + + recalled = false; +} +return recalled; + + + + const noexcept + + + std::uint_fast16_t n = 0U; +for (QEvt const *e = eq->get(m_prio); + e != nullptr; + e = eq->get(m_prio)) +{ + ++n; // count the flushed event +#if (QF_MAX_EPOOL > 0U) + QF::gc(e); // garbage collect +#endif +} +return n; + + + + const noexcept + return static_cast<std::uint_fast8_t>(m_prio); + + + + noexcept + + + m_prio = static_cast<std::uint8_t>(prio & 0xFFU); +m_pthre = static_cast<std::uint8_t>(prio >> 8U); + + + + const noexcept + return static_cast<std::uint_fast8_t>(m_pthre); + + + + const noexcept + return m_eQueue; + + + + const noexcept + return m_osObject; + + + + const noexcept + return m_thread; + + + + + + m_thread = thr; + + + + + + + + + noexcept + + + + + + + + + + + + noexcept + + + + + + + + + + + + + noexcept + + + : QActive(initial) + +m_state.obj = reinterpret_cast<QMsm *>(this)->topQMState(); +m_temp.fun = initial; + + + + override + + + + + reinterpret_cast<QMsm *>(this)->QMsm::init(e, qs_id); + + + + override + + + this->init(nullptr, qs_id); + + + + override + + + + + reinterpret_cast<QMsm *>(this)->QMsm::dispatch(e, qs_id); + + + + noexcept override + return reinterpret_cast<QMsm *>(this)->QMsm::getStateHandler(); + + + + const noexcept + + + return reinterpret_cast<QMsm const *>(this)->QMsm::isInState(st); + + + + const noexcept + return m_state.obj; + + + + const noexcept + + + return reinterpret_cast<QMsm const *>(this) + ->QMsm::childStateObj(parent); + + + + + + + + + + + + + + + + + + + noexcept + + + + + + + : + QEvt(sig), + m_next(nullptr), + m_act(act), + m_ctr(0U), + m_interval(0U) + +QF_CRIT_STAT +QF_CRIT_ENTRY(); +Q_REQUIRE_INCRIT(300, (sig != 0U) + && (tickRate < QF_MAX_TICK_RATE)); +QF_CRIT_EXIT(); + +// The refCtr_ attribute is not used in time events, so it is +// reused to hold the tickRate as well as other information +refCtr_ = static_cast<std::uint8_t>(tickRate); + + + + noexcept + + + + + std::uint8_t const tickRate = refCtr_ & TE_TICK_RATE; +QTimeEvtCtr const ctr = m_ctr; +#ifdef Q_SPY +std::uint_fast8_t const qs_id = + static_cast<QActive const *>(m_act)->m_prio; +#endif + +QF_CRIT_STAT +QF_CRIT_ENTRY(); +QF_MEM_SYS(); + +Q_REQUIRE_INCRIT(400, (m_act != nullptr) + && (ctr == 0U) + && (nTicks != 0U) + && (tickRate < static_cast<std::uint8_t>(QF_MAX_TICK_RATE)) + && (sig >= static_cast<QSignal>(Q_USER_SIG))); +#ifdef Q_UNSAFE +Q_UNUSED_PAR(ctr); +#endif + +m_ctr = nTicks; +m_interval = interval; + +// is the time event unlinked? +// NOTE: For the duration of a single clock tick of the specified tick +// rate a time event can be disarmed and yet still linked into the list +// because un-linking is performed exclusively in the QF_tickX() function. +if (static_cast<std::uint_fast8_t>( + static_cast<std::uint_fast8_t>(refCtr_) & TE_IS_LINKED) == 0U) +{ + // mark as linked + refCtr_ = static_cast<std::uint8_t>(refCtr_ | TE_IS_LINKED); + + // The time event is initially inserted into the separate + // "freshly armed" list based on timeEvtHead_[tickRate].act. + // Only later, inside QTimeEvt::tick(), the "freshly armed" + // list is appended to the main list of armed time events based on + // timeEvtHead_[tickRate].next. Again, this is to keep any + // changes to the main list exclusively inside QTimeEvt::tick(). + m_next = timeEvtHead_[tickRate].toTimeEvt(); + timeEvtHead_[tickRate].m_act = this; +} + +QS_BEGIN_PRE_(QS_QF_TIMEEVT_ARM, qs_id) + QS_TIME_PRE_(); // timestamp + QS_OBJ_PRE_(this); // this time event object + QS_OBJ_PRE_(m_act); // the active object + QS_TEC_PRE_(nTicks); // the # ticks + QS_TEC_PRE_(interval); // the interval + QS_U8_PRE_(tickRate); // tick rate +QS_END_PRE_() + +QF_MEM_APP(); +QF_CRIT_EXIT(); + + + + noexcept + #ifdef Q_SPY +std::uint_fast8_t const qs_id = static_cast<QActive *>(m_act)->m_prio; +#endif + +QF_CRIT_STAT +QF_CRIT_ENTRY(); +QF_MEM_SYS(); + +// is the time event actually armed? +bool wasArmed; +if (m_ctr != 0U) { + wasArmed = true; + refCtr_ = static_cast<std::uint8_t>(refCtr_ | TE_WAS_DISARMED); + + QS_BEGIN_PRE_(QS_QF_TIMEEVT_DISARM, qs_id) + QS_TIME_PRE_(); // timestamp + QS_OBJ_PRE_(this); // this time event object + QS_OBJ_PRE_(m_act); // the target AO + QS_TEC_PRE_(m_ctr); // the # ticks + QS_TEC_PRE_(m_interval); // the interval + QS_U8_PRE_(refCtr_& TE_TICK_RATE); // tick rate + QS_END_PRE_() + + m_ctr = 0U; // schedule removal from the list +} +else { // the time event was already disarmed automatically + wasArmed = false; + refCtr_ = static_cast<std::uint8_t>(refCtr_ + & static_cast<std::uint8_t>(~TE_WAS_DISARMED)); + + QS_BEGIN_PRE_(QS_QF_TIMEEVT_DISARM_ATTEMPT, qs_id) + QS_TIME_PRE_(); // timestamp + QS_OBJ_PRE_(this); // this time event object + QS_OBJ_PRE_(m_act); // the target AO + QS_U8_PRE_(refCtr_& TE_TICK_RATE); // tick rate + QS_END_PRE_() +} + +QF_MEM_APP(); +QF_CRIT_EXIT(); + +return wasArmed; + + + + noexcept + + + std::uint8_t const tickRate = refCtr_ & TE_TICK_RATE; + +#ifdef Q_SPY +std::uint_fast8_t const qs_id = static_cast<QActive *>(m_act)->m_prio; +#endif + +QF_CRIT_STAT +QF_CRIT_ENTRY(); +QF_MEM_SYS(); + +Q_REQUIRE_INCRIT(600, (m_act != nullptr) + && (tickRate < static_cast<std::uint8_t>(QF_MAX_TICK_RATE)) + && (nTicks != 0U) + && (sig >= static_cast<QSignal>(Q_USER_SIG))); + +// is the time evt not running? +bool wasArmed; +if (m_ctr == 0U) { + wasArmed = false; + + // NOTE: For a duration of a single clock tick of the specified + // tick rate a time event can be disarmed and yet still linked into + // the list, because unlinking is performed exclusively in the + // QTimeEvt::tickX() function. + + // is the time event unlinked? + if (static_cast<std::uint8_t>(refCtr_ & TE_IS_LINKED) == 0U) { + // mark as linked + refCtr_ = static_cast<std::uint8_t>(refCtr_ | TE_IS_LINKED); + + // The time event is initially inserted into the separate + // "freshly armed" list based on timeEvtHead_[tickRate].act. + // Only later, inside QTimeEvt::tick(), the "freshly armed" + // list is appended to the main list of armed time events based on + // timeEvtHead_[tickRate].next. Again, this is to keep any + // changes to the main list exclusively inside QTimeEvt::tick(). + m_next = timeEvtHead_[tickRate].toTimeEvt(); + timeEvtHead_[tickRate].m_act = this; + } +} +else { // the time event was armed + wasArmed = true; +} +m_ctr = nTicks; // re-load the tick counter (shift the phasing) + +QS_BEGIN_PRE_(QS_QF_TIMEEVT_REARM, qs_id) + QS_TIME_PRE_(); // timestamp + QS_OBJ_PRE_(this); // this time event object + QS_OBJ_PRE_(m_act); // the target AO + QS_TEC_PRE_(m_ctr); // the # ticks + QS_TEC_PRE_(m_interval); // the interval + QS_2U8_PRE_(tickRate, (wasArmed ? 1U : 0U)); +QS_END_PRE_() + +QF_MEM_APP(); +QF_CRIT_EXIT(); + +return wasArmed; + + + + noexcept + QF_CRIT_STAT +QF_CRIT_ENTRY(); +QF_MEM_SYS(); + +std::uint8_t const isDisarmed = refCtr_ & TE_WAS_DISARMED; +refCtr_ = static_cast<std::uint8_t>(refCtr_ | TE_WAS_DISARMED); + +QF_MEM_APP(); +QF_CRIT_EXIT(); + +return isDisarmed != 0U; + + + + const noexcept + return m_act; + + + + const noexcept + return m_ctr; + + + + const noexcept + return m_interval; + + + + noexcept + + + + + #ifndef Q_SPY +Q_UNUSED_PAR(sender); +#endif + +QTimeEvt *prev = &timeEvtHead_[tickRate]; + +QF_CRIT_STAT +QF_CRIT_ENTRY(); +QF_MEM_SYS(); + +QS_BEGIN_PRE_(QS_QF_TICK, 0U) + prev->m_ctr = (prev->m_ctr + 1U); + QS_TEC_PRE_(prev->m_ctr); // tick ctr + QS_U8_PRE_(tickRate); // tick rate +QS_END_PRE_() + +// scan the linked-list of time events at this rate... +for (;;) { + QTimeEvt *t = prev->m_next; // advance down the time evt. list + + if (t == nullptr) { // end of the list? + + // any new time events armed since the last run of tick()? + if (timeEvtHead_[tickRate].m_act != nullptr) { + + // sanity check + Q_ASSERT_INCRIT(110, prev != nullptr); + prev->m_next = timeEvtHead_[tickRate].toTimeEvt(); + timeEvtHead_[tickRate].m_act = nullptr; + t = prev->m_next; // switch to the new list + } + else { + break; // all currently armed time evts. processed + } + } + + if (t->m_ctr == 0U) { // time event scheduled for removal? + prev->m_next = t->m_next; + // mark time event 't' as NOT linked + t->refCtr_ = static_cast<std::uint8_t>(t->refCtr_ + & static_cast<std::uint8_t>(~TE_IS_LINKED)); + // do NOT advance the prev pointer + 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->m_ctr = (t->m_ctr - 1U); + + if (t->m_ctr == 0U) { // is time evt about to expire? + QActive * const act = t->toActive(); + + if (t->m_interval != 0U) { // periodic time evt? + t->m_ctr = t->m_interval; // rearm the time event + prev = t; // advance to this time event + } + else { // one-shot time event: automatically disarm + prev->m_next = t->m_next; + + // mark time event 't' as NOT linked + t->refCtr_ = static_cast<std::uint8_t>(t->refCtr_ + & static_cast<std::uint8_t>(~TE_IS_LINKED)); + // do NOT advance the prev pointer + + QS_BEGIN_PRE_(QS_QF_TIMEEVT_AUTO_DISARM, act->m_prio) + QS_OBJ_PRE_(t); // this time event object + QS_OBJ_PRE_(act); // the target AO + QS_U8_PRE_(tickRate); // tick rate + QS_END_PRE_() + } + + QS_BEGIN_PRE_(QS_QF_TIMEEVT_POST, act->m_prio) + QS_TIME_PRE_(); // timestamp + QS_OBJ_PRE_(t); // the time event object + QS_SIG_PRE_(t->sig); // signal of this time event + QS_OBJ_PRE_(act); // the target AO + QS_U8_PRE_(tickRate); // tick rate + QS_END_PRE_() + +#ifdef QXK_HPP_ + if (t->sig < Q_USER_SIG) { + QXThread::timeout_(act); + } + else { + QF_MEM_APP(); + QF_CRIT_EXIT(); // exit crit. section before posting + + // act->POST() asserts if the queue overflows + act->POST(t, sender); + } +#else + QF_MEM_APP(); + QF_CRIT_EXIT(); // exit crit. section before posting + + // act->POST() asserts if the queue overflows + act->POST(t, 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(); + + + + + + + + + + + noexcept + + + + + + + + + + noexcept + + + QF_CRIT_STAT +QF_CRIT_ENTRY(); +Q_REQUIRE_INCRIT(800, tickRate < QF_MAX_TICK_RATE); +QF_CRIT_EXIT(); + +bool inactive; +if (timeEvtHead_[tickRate].m_next != nullptr) { + inactive = false; +} +else if (timeEvtHead_[tickRate].m_act != nullptr) { + inactive = false; +} +else { + inactive = true; +} +return inactive; + + + + noexcept + return static_cast<QActive *>(m_act); + + + + noexcept + return static_cast<QTimeEvt *>(m_act); + + + + noexcept + : + QEvt(0U), + m_next(nullptr), + m_act(nullptr), + m_ctr(0U), + m_interval(0U) + +// The refCtr_ attribute is not used in time events, so it is +// reused to hold the tickRate as well as other information +refCtr_ = 0U; // default rate 0 + + + + = delete + + + + + + = delete + + + + + + + + + noexcept + + + : QActive(nullptr) + +// reuse m_head for tick-rate +m_eQueue.m_head = static_cast<QEQueueCtr>(tickRate); + + + + override + + + + + Q_UNUSED_PAR(e); +Q_UNUSED_PAR(qs_id); + +QF_CRIT_STAT +QF_CRIT_ENTRY(); +QF_MEM_SYS(); + +m_eQueue.m_tail = 0U; + +QF_MEM_APP(); +QF_CRIT_EXIT(); + + + + override + + + this->init(nullptr, qs_id); + + + + override + + + + + Q_UNUSED_PAR(e); +Q_UNUSED_PAR(qs_id); + +QF_CRIT_STAT +QF_CRIT_ENTRY(); +QF_MEM_SYS(); + +QEQueueCtr nTicks = m_eQueue.m_tail; // save # of ticks +m_eQueue.m_tail = 0U; // clear the # ticks + +QF_MEM_APP(); +QF_CRIT_EXIT(); + +for (; nTicks > 0U; --nTicks) { + QTimeEvt::tick(static_cast<std::uint_fast8_t>(m_eQueue.m_head), + this); +} + + + + noexcept + + + #ifndef Q_SPY +Q_UNUSED_PAR(sender); +#endif + +QF_CRIT_STAT +QF_CRIT_ENTRY(); +QF_MEM_SYS(); + +if (m_eQueue.m_frontEvt == nullptr) { + + static QEvt const tickEvt(0U); // immutable event + + m_eQueue.m_frontEvt = &tickEvt; // deliver event directly + m_eQueue.m_nFree = (m_eQueue.m_nFree - 1U); // one less free event + + QACTIVE_EQUEUE_SIGNAL_(this); // signal the event queue +} + +// account for one more tick event +m_eQueue.m_tail = (m_eQueue.m_tail + 1U); + +QS_BEGIN_PRE_(QS_QF_ACTIVE_POST, m_prio) + QS_TIME_PRE_(); // timestamp + QS_OBJ_PRE_(sender); // the sender object + QS_SIG_PRE_(0U); // the signal of the event + QS_OBJ_PRE_(this); // this active object + QS_2U8_PRE_(0U, 0U); // poolId & refCtr + QS_EQC_PRE_(0U); // # free entries + QS_EQC_PRE_(0U); // min # free entries +QS_END_PRE_() + +QF_MEM_APP(); +QF_CRIT_EXIT(); + + + + + + + + + + + + + + + + + + + + + // friends... + + + + + + + + + + noexcept + : m_frontEvt(nullptr), + m_ring(nullptr), + m_end(0U), + m_head(0U), + m_tail(0U), + m_nFree(0U), + m_nMin(0U) + + + + noexcept + + + + + m_frontEvt = nullptr; // no events in the queue +m_ring = &qSto[0]; +m_end = static_cast<QEQueueCtr>(qLen); +if (qLen > 0U) { + m_head = 0U; + m_tail = 0U; +} +m_nFree = static_cast<QEQueueCtr>(qLen + 1U); //+1 for frontEvt +m_nMin = m_nFree; + + + + noexcept + + + + + + + #ifndef Q_SPY +Q_UNUSED_PAR(qs_id); +#endif + +QF_CRIT_STAT +QF_CRIT_ENTRY(); +QF_MEM_SYS(); + +Q_REQUIRE_INCRIT(200, e != nullptr); + +QEQueueCtr nFree = m_nFree; // get volatile into temporary + +// required margin available? +bool status; +if (((margin == QF::NO_MARGIN) && (nFree > 0U)) + || (nFree > static_cast<QEQueueCtr>(margin))) +{ + // is it a mutable event? + if (e->getPoolId_() != 0U) { + QEvt_refCtr_inc_(e); // increment the reference counter + } + + --nFree; // one free entry just used up + m_nFree = nFree; // update the original + if (m_nMin > nFree) { + m_nMin = nFree; // update minimum so far + } + + QS_BEGIN_PRE_(QS_QF_EQUEUE_POST, qs_id) + QS_TIME_PRE_(); // timestamp + QS_SIG_PRE_(e->sig); // the signal of this event + QS_OBJ_PRE_(this); // this queue object + QS_2U8_PRE_(e->getPoolId_(), e->refCtr_); // poolId & refCtr + QS_EQC_PRE_(nFree); // # free entries + QS_EQC_PRE_(m_nMin); // min # free entries + QS_END_PRE_() + + if (m_frontEvt == nullptr) { // was the queue empty? + m_frontEvt = e; // deliver event directly + } + else { // queue was not empty, insert event into the ring-buffer + // insert event into the ring buffer (FIFO) + m_ring[m_head] = e; // insert e into buffer + + // need to wrap? + if (m_head == 0U) { + m_head = m_end; // wrap around + } + m_head = (m_head - 1U); + } + status = true; // event posted successfully +} +else { // event cannot be posted + // dropping events must be acceptable + Q_ASSERT_INCRIT(210, margin != QF::NO_MARGIN); + + QS_BEGIN_PRE_(QS_QF_EQUEUE_POST_ATTEMPT, qs_id) + QS_TIME_PRE_(); // timestamp + QS_SIG_PRE_(e->sig); // the signal of this event + QS_OBJ_PRE_(this); // this queue object + QS_2U8_PRE_(e->getPoolId_(), 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; + + + + noexcept + + + + + #ifndef Q_SPY +Q_UNUSED_PAR(qs_id); +#endif + +QF_CRIT_STAT +QF_CRIT_ENTRY(); +QF_MEM_SYS(); + +QEQueueCtr nFree = m_nFree; // get volatile into temporary + +Q_REQUIRE_INCRIT(300, nFree != 0U); + +if (e->getPoolId_() != 0U) { // is it a mutable event? + QEvt_refCtr_inc_(e); // increment the reference counter +} + +--nFree; // one free entry just used up +m_nFree = nFree; // update the original +if (m_nMin > nFree) { + m_nMin = nFree; // update minimum so far +} + +QS_BEGIN_PRE_(QS_QF_EQUEUE_POST_LIFO, qs_id) + QS_TIME_PRE_(); // timestamp + QS_SIG_PRE_(e->sig); // the signal of this event + QS_OBJ_PRE_(this); // this queue object + QS_2U8_PRE_(e->getPoolId_(), e->refCtr_); // poolId & refCtr + QS_EQC_PRE_(nFree); // # free entries + QS_EQC_PRE_(m_nMin); // min # free entries +QS_END_PRE_() + +QEvt const * const frontEvt = m_frontEvt; // read into temporary +m_frontEvt = e; // deliver event directly to the front of the queue + +if (frontEvt != nullptr) { // was the queue not empty? + m_tail = (m_tail + 1U); + if (m_tail == m_end) { // need to wrap the tail? + m_tail = 0U; // wrap around + } + m_ring[m_tail] = frontEvt; // buffer the old front evt +} + +QF_MEM_APP(); +QF_CRIT_EXIT(); + + + + noexcept + + + #ifndef Q_SPY +Q_UNUSED_PAR(qs_id); +#endif + +QF_CRIT_STAT +QF_CRIT_ENTRY(); +QF_MEM_SYS(); + +QEvt const * const e = m_frontEvt; // always remove evt from the front + +if (e != nullptr) { // was the queue not empty? + QEQueueCtr const nFree = m_nFree + 1U; + m_nFree = nFree; // upate the # free + + // any events in the the ring buffer? + if (nFree <= m_end) { + m_frontEvt = m_ring[m_tail]; // remove from the tail + if (m_tail == 0U) { // need to wrap? + m_tail = m_end; // wrap around + } + m_tail = (m_tail - 1U); + + QS_BEGIN_PRE_(QS_QF_EQUEUE_GET, qs_id) + QS_TIME_PRE_(); // timestamp + QS_SIG_PRE_(e->sig); // the signal of this event + QS_OBJ_PRE_(this); // this queue object + QS_2U8_PRE_(e->getPoolId_(), e->refCtr_); // poolId & refCtr + QS_EQC_PRE_(nFree); // # free entries + QS_END_PRE_() + } + else { + m_frontEvt = nullptr; // queue becomes empty + + // all entries in the queue must be free (+1 for fronEvt) + Q_ASSERT_INCRIT(410, nFree == (m_end + 1U)); + + QS_BEGIN_PRE_(QS_QF_EQUEUE_GET_LAST, qs_id) + QS_TIME_PRE_(); // timestamp + QS_SIG_PRE_(e->sig); // the signal of this event + QS_OBJ_PRE_(this); // this queue object + QS_2U8_PRE_(e->getPoolId_(), e->refCtr_); // poolId & refCtr + QS_END_PRE_() + } +} + +QF_MEM_APP(); +QF_CRIT_EXIT(); + +return e; + + + + const noexcept + return m_nFree; + + + + const noexcept + return m_nMin; + + + + const noexcept + return m_frontEvt == nullptr; + + + + = delete + + + + + + = delete + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + : m_start(nullptr), + m_end(nullptr), + m_free_head(nullptr), + m_blockSize(0U), + m_nTot(0U), + m_nFree(0U), + m_nMin(0U) + + + + noexcept + + + + + + + QF_CRIT_STAT +QF_CRIT_ENTRY(); +QF_MEM_SYS(); + +Q_REQUIRE_INCRIT(100, (poolSto != nullptr) + && (poolSize >= static_cast<std::uint_fast32_t>(sizeof(QFreeBlock))) + && (static_cast<std::uint_fast16_t>(blockSize + sizeof(QFreeBlock)) + > blockSize)); + +m_free_head = static_cast<QFreeBlock *>(poolSto); + +// find # free blocks in a memory block, NO DIVISION +m_blockSize = static_cast<QMPoolSize>(sizeof(QFreeBlock)); +std::uint_fast16_t nblocks = 1U; +while (m_blockSize < static_cast<QMPoolSize>(blockSize)) { + m_blockSize += static_cast<QMPoolSize>(sizeof(QFreeBlock)); + ++nblocks; +} + +// the pool buffer must fit at least one rounded-up block +Q_ASSERT_INCRIT(110, poolSize >= m_blockSize); + +// start at the head of the free list +QFreeBlock *fb = m_free_head; +m_nTot = 1U; // the last block already in the list + +// chain all blocks together in a free-list... +for (std::uint_fast32_t size = poolSize - m_blockSize; + size >= static_cast<std::uint_fast32_t>(m_blockSize); + size -= static_cast<std::uint_fast32_t>(m_blockSize)) +{ + fb->m_next = &fb[nblocks]; // point next link to next block +#ifndef Q_UNSAFE + fb->m_next_dis = ~Q_UINTPTR_CAST_(fb->m_next); +#endif + fb = fb->m_next; // advance to the next block + ++m_nTot; // one more free block in the pool +} + +fb->m_next = nullptr; // the last link points to NULL +#ifndef Q_UNSAFE +fb->m_next_dis = ~Q_UINTPTR_CAST_(fb->m_next); +#endif + +fb->m_next = nullptr; // the last link points to NULL +m_nFree = m_nTot; // all blocks are free +m_nMin = m_nTot; // the minimum # free blocks +m_start = static_cast<QFreeBlock *>(poolSto); // original start +m_end = fb; // the last block in this pool + +QF_MEM_APP(); +QF_CRIT_EXIT(); + + + + noexcept + + + + + #ifndef Q_SPY +Q_UNUSED_PAR(qs_id); +#endif + +QF_CRIT_STAT +QF_CRIT_ENTRY(); +QF_MEM_SYS(); + +// have more free blocks than the requested margin? +QFreeBlock *fb; +if (m_nFree > static_cast<QMPoolCtr>(margin)) { + fb = m_free_head; // get a free block + + // a free block must be valid + Q_ASSERT_INCRIT(300, fb != nullptr); + + QFreeBlock * const fb_next = fb->m_next; + + // the free block must have integrity (duplicate inverse storage) + Q_ASSERT_INCRIT(302, Q_UINTPTR_CAST_(fb_next) + == static_cast<std::uintptr_t>(~fb->m_next_dis)); + + m_nFree = (m_nFree - 1U); // one free block less + if (m_nFree == 0U) { // is the pool becoming empty? + // pool is becoming empty, so the next free block must be NULL + Q_ASSERT_INCRIT(320, fb_next == nullptr); + + m_nMin = 0U; // remember that the pool got empty + } + else { + // invariant: + // The pool is not empty, so the next free-block pointer, + // so the next free block must be in range. + + // NOTE: The next free block pointer can fall out of range + // when the client code writes past the memory block, thus + // corrupting the next block. + Q_ASSERT_INCRIT(330, + QF_PTR_RANGE_(fb_next, m_start, m_end)); + + // is the # free blocks the new minimum so far? + if (m_nMin > m_nFree) { + m_nMin = m_nFree; // remember the minimum so far + } + } + + m_free_head = fb_next; // set the head to the next free block + + QS_BEGIN_PRE_(QS_QF_MPOOL_GET, qs_id) + QS_TIME_PRE_(); // timestamp + QS_OBJ_PRE_(this); // this memory pool + QS_MPC_PRE_(m_nFree); // # of free blocks in the pool + QS_MPC_PRE_(m_nMin); // min # free blocks ever in the pool + QS_END_PRE_() +} +else { // don't have enough free blocks at this point + fb = nullptr; + + QS_BEGIN_PRE_(QS_QF_MPOOL_GET_ATTEMPT, qs_id) + QS_TIME_PRE_(); // timestamp + QS_OBJ_PRE_(this); // this memory pool + QS_MPC_PRE_(m_nFree); // # of free blocks in the pool + QS_MPC_PRE_(margin); // the requested margin + QS_END_PRE_() +} + +QF_MEM_APP(); +QF_CRIT_EXIT(); + +return fb; // return the block or nullptr to the caller + + + + noexcept + + + + + #ifndef Q_SPY +Q_UNUSED_PAR(qs_id); +#endif + +QFreeBlock * const fb = static_cast<QFreeBlock *>(block); + +QF_CRIT_STAT +QF_CRIT_ENTRY(); +QF_MEM_SYS(); + +Q_REQUIRE_INCRIT(200, (m_nFree < m_nTot) + && QF_PTR_RANGE_(fb, m_start, m_end)); + +fb->m_next = m_free_head; // link into list +#ifndef Q_UNSAFE +fb->m_next_dis = static_cast<std::uintptr_t>( + ~Q_UINTPTR_CAST_(fb->m_next)); +#endif + +// set as new head of the free list +m_free_head = static_cast<QFreeBlock *>(block); + +m_nFree = m_nFree + 1U; // one more free block in this pool + +QS_BEGIN_PRE_(QS_QF_MPOOL_PUT, qs_id) + QS_TIME_PRE_(); // timestamp + QS_OBJ_PRE_(this); // this memory pool + QS_MPC_PRE_(m_nFree); // the # free blocks in the pool +QS_END_PRE_() + +QF_MEM_APP(); +QF_CRIT_EXIT(); + + + + const noexcept + return m_blockSize; + + + + const noexcept + return m_nMin; + + + + const noexcept + return m_nFree; + + + + = delete + + + + + + = delete + + + + + + noexcept + + + + + + + + noexcept + + + + + + + + + + + + + + + + + + + + + + noexcept + + + + + std::uint8_t *ptr = static_cast<std::uint8_t *>(start); +for (std::uint_fast16_t n = len; n > 0U; --n) { + *ptr = 0U; + ++ptr; +} + + + + + + + + + + + + + + + + + noexcept + //! @deprecated + + + + + QActive::psInit(subscrSto, maxSignal); + + + + noexcept + //! @deprecated + + + + + + + QActive::publish_(e, sender, qs_id); + + + + noexcept + //! @deprecated + + + + + QTimeEvt::tick(tickRate, sender); + + + + noexcept + //! @deprecated + + + return QActive::getQueueMin(prio); + + + + {0xFFFFU}; + + + + + + + noexcept + + + + + + + std::uint_fast8_t const poolId = 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_(priv_.ePool_[poolId - 1U]) < evtSize); +} +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_(priv_.ePool_[poolId], poolSto, poolSize, evtSize); + +#ifdef Q_SPY +// generate the object-dictionary entry for the initialized pool +{ + std::uint8_t obj_name[9] = "EvtPool?"; + obj_name[7] = static_cast<std::uint8_t>( + static_cast<std::uint8_t>('0') + + static_cast<std::uint8_t>(poolId + 1U)); + QS_CRIT_ENTRY(); + QS_MEM_SYS(); + QS::obj_dict_pre_(&priv_.ePool_[poolId], + reinterpret_cast<char *>(&obj_name[0])); + QS_MEM_APP(); + QS_CRIT_EXIT(); +} +#endif // Q_SPY + + + + noexcept + return QF_EPOOL_EVENT_SIZE_(priv_.ePool_[priv_.maxPool_ - 1U]); + + + + noexcept + + + QF_CRIT_STAT +QF_CRIT_ENTRY(); +QF_MEM_SYS(); + +Q_REQUIRE_INCRIT(400, (poolId <= QF_MAX_EPOOL) + && (0U < poolId) && (poolId <= priv_.maxPool_)); + +std::uint_fast16_t const min = static_cast<std::uint_fast16_t>( + priv_.ePool_[poolId - 1U].getNMin()); + +QF_MEM_APP(); +QF_CRIT_EXIT(); + +return min; + + + + noexcept + + + + + + + QF_CRIT_STAT +QF_CRIT_ENTRY(); +QF_MEM_SYS(); + +// find the pool id that fits the requested event size... +std::uint_fast8_t poolId = 0U; // zero-based poolId initially +for (; poolId < priv_.maxPool_; ++poolId) { + if (evtSize <= QF_EPOOL_EVENT_SIZE_(priv_.ePool_[poolId])) { + break; + } +} + +// precondition: +// - cannot run out of registered pools +Q_REQUIRE_INCRIT(300, poolId < 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_(priv_.ePool_[poolId - 1U], e, + ((margin != NO_MARGIN) ? margin : 0U), + static_cast<std::uint_fast8_t>(QS_EP_ID) + poolId); +#else +QF_EPOOL_GET_(priv_.ePool_[poolId - 1U], e, + ((margin != NO_MARGIN) ? margin : 0U), 0U); +#endif + +if (e != nullptr) { // was e allocated correctly? + e->sig = static_cast<QSignal>(sig); // set the signal + e->refCtr_ = 0U; // initialize the reference counter to 0 + e->evtTag_ = static_cast<std::uint8_t>(QEvt::MARKER | poolId); + + QS_CRIT_ENTRY(); + QS_MEM_SYS(); + QS_BEGIN_PRE_(QS_QF_NEW, + static_cast<std::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 != NO_MARGIN); + + QS_MEM_SYS(); + QS_BEGIN_PRE_(QS_QF_NEW_ATTEMPT, + static_cast<std::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; + + + + noexcept + + + QF_CRIT_STAT +QF_CRIT_ENTRY(); +Q_REQUIRE_INCRIT(402, QEvt::verify_(e)); + +std::uint_fast8_t const poolId = e->getPoolId_(); + +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, + static_cast<std::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, + static_cast<std::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 <= 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_(priv_.ePool_[poolId - 1U], + QF_CONST_CAST_(QEvt*, e), + static_cast<std::uint_fast8_t>(QS_EP_ID) + poolId); +#else + QF_EPOOL_PUT_(priv_.ePool_[poolId - 1U], + QF_CONST_CAST_(QEvt*, e), 0U); +#endif + } +} +else { + QF_CRIT_EXIT(); +} + + + + noexcept + + + + + #ifdef Q_UNSAFE +Q_UNUSED_PAR(evtRef); +#endif + +QF_CRIT_STAT +QF_CRIT_ENTRY(); + +Q_REQUIRE_INCRIT(502, QEvt::verify_(e)); + +std::uint_fast8_t const poolId = e->getPoolId_(); + +Q_REQUIRE_INCRIT(500, (poolId != 0U) + && (evtRef == nullptr)); + +QEvt_refCtr_inc_(e); // increments the ref counter + +QS_MEM_SYS(); +QS_BEGIN_PRE_(QS_QF_NEW_REF, + static_cast<std::uint_fast8_t>(QS_EP_ID) + 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; + + + + noexcept + + + QEvt const * const e = evtRef; + +QF_CRIT_STAT +QF_CRIT_ENTRY(); +Q_REQUIRE_INCRIT(602, QEvt::verify_(e)); + +#ifdef Q_SPY +std::uint_fast8_t const poolId = e->getPoolId_(); +#endif + +QS_MEM_SYS(); +QS_BEGIN_PRE_(QS_QF_DELETE_REF, + static_cast<std::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) +gc(evtRef); // recycle the referenced event +#endif + + + + noexcept + + + + + + + + + + noexcept + + + + + + + + + + + + + + + + + + + + + + + + \ + (static_cast<QP::QPrioSpec>((prio_) | (pthre_) << 8U)) + + + + + + + + (static_cast<evtT_ *>( \ + QP::QF::newX_(sizeof(evtT_), QP::QF::NO_MARGIN, (sig_)))) + + + + + + + + + + (static_cast<evtT_ *>( \ + new(QP::QF::newX_(sizeof(evtT_), QP::QF::NO_MARGIN, (sig_))) \ + evtT_(__VA_ARGS__))) + + + + + + + + + + (static_cast<evtT_ *>( \ + QP::QF::newX_(sizeof(evtT_), (margin_), (sig_)))) + + + + + + + + + + + + (static_cast<evtT_ *>( \ + new(QP::QF::newX_(sizeof(evtT_), (margin_), (sig_))) \ + evtT_(__VA_ARGS__))) + + + + + + + + \ + ((evtRef_) = static_cast<evtT_ const *>(QP::QF::newRef_(e, (evtRef_)))) + + + + + + do { \ + QP::QF::deleteRef_((evtRef_)); \ + (evtRef_) = 0U; \ +} while (false) + + + + + + + + \ + publish_((e_), (sender_), (sender_)->getPrio()) + + + + + + + + publish_((e_), nullptr, 0U) + + + + + + + + post_((e_), QP::QF::NO_MARGIN, (sender_)) + + + + + + + + post_((e_), QP::QF::NO_MARGIN, nullptr) + + + + + + + + + + \ + post_((e_), (margin_), (sender_)) + + + + + + + + + + post_((e_), (margin_), nullptr) + + + + + + + + tick((tickRate_), (sender_)) + + + + + + + + tick((tickRate_), nullptr) + + + + + + TICK_X(0U, (sender_)) + + + + + + trig_((sender_)) + + + + + + trig_(nullptr) + + + + (static_cast<void>(0)) + + + + (static_cast<void>(0)) + + + + (static_cast<void>(0)) + + + + + + + Native QF event pool + QMPool + + + + Native QF event pool initialization + + + + + + + + + \ + (p_).init((poolSto_), (poolSize_), (evtSize_)) + + + + Native QF event pool event-size getter + + + ((p_).getBlockSize()) + + + + Native QF event pool get-event + + + + + + + + + \ + ((e_) = static_cast<QEvt *>((p_).get((m_), (qs_id_)))) + + + + Native QF event pool put-event + + + + + + + ((p_).put((e_), (qs_id_))) + + + + + + + + + + + + + + + + + + + + + + + bzero_(&QF::priv_, sizeof(QF::priv_)); +bzero_(&QV::priv_, sizeof(QV::priv_)); +bzero_(&QTimeEvt::timeEvtHead_[0], sizeof(QTimeEvt::timeEvtHead_)); +bzero_(&QActive::registry_[0], sizeof(QActive::registry_)); + +#ifndef Q_UNSAFE +QV::priv_.readySet.update_(&QV::priv_.readySet_dis); +#endif + +#ifdef QV_INIT +QV_INIT(); // port-specific initialization of the QV kernel +#endif + + + + onCleanup(); // cleanup callback +// nothing else to do for the QV kernel + + + + #ifdef Q_SPY +// produce the QS_QF_RUN trace record +QF_INT_DISABLE(); +QF_MEM_SYS(); +QS::beginRec_(QS_REC_NUM_(QS_QF_RUN)); +QS::endRec_(); +QF_MEM_APP(); +QF_INT_ENABLE(); +#endif + +onStartup(); // application-specific startup callback + +QF_INT_DISABLE(); +QF_MEM_SYS(); + +#ifdef QV_START +QV_START(); // port-specific startup of the QV kernel +#endif + +#if (defined QF_ON_CONTEXT_SW) || (defined Q_SPY) +std::uint_fast8_t pprev = 0U; // previous prio. +#endif + +for (;;) { // QV event loop... + + // check internal integrity (duplicate inverse storage) + Q_ASSERT_INCRIT(202, + QV::priv_.readySet.verify_(&QV::priv_.readySet_dis)); + + // find the maximum prio. AO ready to run + if (QV::priv_.readySet.notEmpty()) { + std::uint_fast8_t const p = QV::priv_.readySet.findMax(); + 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] + : nullptr), a); +#endif // QF_ON_CONTEXT_SW + + pprev = p; // update previous prio. +#endif // (defined QF_ON_CONTEXT_SW) || (defined Q_SPY) + + QF_MEM_APP(); + QF_INT_ENABLE(); + + QEvt const * const e = a->get_(); + // NOTE QActive::get_() performs QF_MEM_APP() before return + + // dispatch event (virtual call) + a->dispatch(e, a->getPrio()); +#if (QF_MAX_EPOOL > 0U) + gc(e); +#endif + QF_INT_DISABLE(); + QF_MEM_SYS(); + + if (a->getEQueue().isEmpty()) { // empty queue? + QV::priv_.readySet.remove(p); +#ifndef Q_UNSAFE + QV::priv_.readySet.update_(&QV::priv_.readySet_dis); +#endif + } + } + else { // no AO ready to run --> idle +#if (defined QF_ON_CONTEXT_SW) || (defined Q_SPY) + if (pprev != 0U) { + QS_BEGIN_PRE_(QS_SCHED_IDLE, pprev) + QS_TIME_PRE_(); // timestamp + QS_U8_PRE_(pprev); // previous prio + QS_END_PRE_() + +#ifdef QF_ON_CONTEXT_SW + QF_onContextSw(QActive::registry_[pprev], nullptr); +#endif // QF_ON_CONTEXT_SW + + pprev = 0U; // update previous prio + } +#endif // (defined QF_ON_CONTEXT_SW) || (defined Q_SPY) + + QF_MEM_APP(); + + // QV::onIdle() must be called with interrupts DISABLED + // because the determination of the idle condition (all event + // queues empty) can change at any time by an interrupt posting + // events to a queue. + // + // NOTE: QV::onIdle() MUST enable interrupts internally, + // ideally at the same time as putting the CPU into a power- + // saving mode. + QV::onIdle(); + + QF_INT_DISABLE(); + QF_MEM_SYS(); + } +} +#ifdef __GNUC__ // GNU compiler? +return 0; +#endif + + + + + + + + + + + + + + + + + + + Q_UNUSED_PAR(stkSto); // not needed in QV +Q_UNUSED_PAR(stkSize); // not needed in QV + +QF_CRIT_STAT +QF_CRIT_ENTRY(); +Q_REQUIRE_INCRIT(300, stkSto == nullptr); +QF_CRIT_EXIT(); + +m_prio = static_cast<std::uint8_t>(prioSpec & 0xFFU); // QF-prio. +m_pthre = static_cast<std::uint8_t>(prioSpec >> 8U); // preemption-thre. +register_(); // make QF aware of this AO + +m_eQueue.init(qSto, qLen); // initialize QEQueue of this AO + +this->init(par, m_prio); // take the top-most initial tran. (virtual) +QS_FLUSH(); // flush the trace buffer to the host + + + + + + + + + + + + (static_cast<void>(0)) + + + + (static_cast<void>(0)) + + + + + + \ + Q_ASSERT_INCRIT(310, (me_)->m_eQueue.m_frontEvt != nullptr) + + + + + + \ + QV::priv_.readySet.insert((me_)->m_prio); \ + QV::priv_.readySet.update_(&QV::priv_.readySet_dis) + + + + + + \ + (QV::priv_.readySet.insert((me_)->m_prio)) + + + + + + + = std::uint_fast16_t; + + + + + + noexcept + + + 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_(static_cast<std::uint8_t>(QK_priv_.lockCeil), + static_cast<std::uint8_t>(ceiling)); + QS_END_PRE_() + + // previous status of the lock + stat = static_cast<QSchedStatus>(QK_priv_.lockHolder); + stat = stat | (static_cast<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 + + + + noexcept + + + // has the scheduler been actually locked by the last QK_schedLock()? +if (stat != 0xFFU) { + QF_CRIT_STAT + QF_CRIT_ENTRY(); + QF_MEM_SYS(); + + std::uint_fast8_t const lockCeil = QK_priv_.lockCeil; + std::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_(static_cast<std::uint8_t>(lockCeil), + static_cast<std::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(); +} + + + + + + + + + bzero_(&QF::priv_, sizeof(QF::priv_)); +bzero_(&QK_priv_, sizeof(QK_priv_)); +bzero_(&QTimeEvt::timeEvtHead_[0], sizeof(QTimeEvt::timeEvtHead_)); +bzero_(&QActive::registry_[0], sizeof(QActive::registry_)); + +#ifndef Q_UNSAFE +QK_priv_.readySet.update_(&QK_priv_.readySet_dis); +#endif + +// setup the QK scheduler as initially locked and not running +QK_priv_.lockCeil = (QF_MAX_ACTIVE + 1U); // scheduler locked + +// storage capable for holding a blank QActive object (const in ROM) +static void* const + idle_ao[((sizeof(QActive) + sizeof(void*)) - 1U) / sizeof(void*)] + = { nullptr }; + +// register the blank QActive object as the idle-AO (cast 'const' away) +QActive::registry_[0] = QF_CONST_CAST_(QActive*, + reinterpret_cast<QActive const*>(idle_ao)); + +#ifdef QK_INIT +QK_INIT(); // port-specific initialization of the QK kernel +#endif + + + + onCleanup(); // cleanup callback +// nothing else to do for the QK preemptive kernel + + + + #ifdef Q_SPY +// produce the QS_QF_RUN trace record +QF_INT_DISABLE(); +QF_MEM_SYS(); +QS::beginRec_(QS_REC_NUM_(QS_QF_RUN)); +QS::endRec_(); +QF_MEM_APP(); +QF_INT_ENABLE(); +#endif // Q_SPY + +onStartup(); // application-specific startup callback + +QF_INT_DISABLE(); +QF_MEM_SYS(); + +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__ // GNU compiler? +return 0; +#endif + + + + + + + + + + + + + + + + + + + Q_UNUSED_PAR(stkSto); // not needed in QK +Q_UNUSED_PAR(stkSize); // not needed in QK + +QF_CRIT_STAT +QF_CRIT_ENTRY(); +QF_MEM_SYS(); + +Q_REQUIRE_INCRIT(300, (!QK_ISR_CONTEXT_()) + && (stkSto == nullptr)); +QF_MEM_APP(); +QF_CRIT_EXIT(); + +m_prio = static_cast<std::uint8_t>(prioSpec & 0xFFU); // QF-prio. +m_pthre = static_cast<std::uint8_t>(prioSpec >> 8U); // preemption-thre. +register_(); // make QF aware of this AO + +m_eQueue.init(qSto, qLen); // initialize the built-in queue + +this->init(par, m_prio); // top-most initial tran. (virtual call) +QS_FLUSH(); // flush the trace buffer to the host + +// See if this AO needs to be scheduled in case QK is already running +QF_CRIT_ENTRY(); +QF_MEM_SYS(); +if (QK_sched_() != 0U) { // synchronous preemption needed? + QK_activate_(); // synchronously activate AOs +} +QF_MEM_APP(); +QF_CRIT_EXIT(); + + + + + + + + + + + + + + + + + + + + + + + + + + + + + noexcept + Q_REQUIRE_INCRIT(400, QK_priv_.readySet.verify_( + &QK_priv_.readySet_dis)); + +std::uint_fast8_t p; +if (QK_priv_.readySet.isEmpty()) { + p = 0U; // no activation needed +} +else { + // find the highest-prio AO with non-empty event queue + p = QK_priv_.readySet.findMax(); + + // 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; + } +} + +return p; + + + + noexcept + std::uint_fast8_t const prio_in = QK_priv_.actPrio; // save initial prio. +std::uint_fast8_t p = QK_priv_.nextPrio; // next prio to run +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) +std::uint_fast8_t pprev = prio_in; +#endif // QF_ON_CONTEXT_SW || Q_SPY + +// loop until no more ready-to-run AOs of higher prio than the initial +QP::QActive *a; +do { + a = QP::QActive::registry_[p]; // obtain the pointer to the AO + Q_ASSERT_INCRIT(505, a != nullptr); // the AO must be registered + + // set new active prio. and preemption-ceiling + QK_priv_.actPrio = p; + QK_priv_.actThre = a->getPThre(); + +#if (defined QF_ON_CONTEXT_SW) || (defined Q_SPY) + if (p != pprev) { // changing threads? + + QS_BEGIN_PRE_(QP::QS_SCHED_NEXT, p) + QS_TIME_PRE_(); // timestamp + QS_2U8_PRE_(p, // prio. of the scheduled AO + pprev); // previous prio. + QS_END_PRE_() + +#ifdef QF_ON_CONTEXT_SW + QF_onContextSw(((pprev != 0U) + ? QP::QActive::registry_[pprev] + : nullptr), a); +#endif // QF_ON_CONTEXT_SW + + pprev = p; // update previous prio. + } +#endif // QF_ON_CONTEXT_SW || Q_SPY + + QF_INT_ENABLE(); // unconditionally enable interrupts + + QP::QEvt const * const e = a->get_(); + // NOTE QActive_get_() performs QF_MEM_APP() before return + + // dispatch event (virtual call) + a->dispatch(e, a->getPrio()); +#if (QF_MAX_EPOOL > 0U) + QP::QF::gc(e); +#endif + + // determine the next highest-prio. AO ready to run... + QF_INT_DISABLE(); // unconditionally disable interrupts + QF_MEM_SYS(); + + // internal integrity check (duplicate inverse storage) + Q_ASSERT_INCRIT(502, QK_priv_.readySet.verify_( + &QK_priv_.readySet_dis)); + + if (a->getEQueue().isEmpty()) { // empty queue? + QK_priv_.readySet.remove(p); +#ifndef Q_UNSAFE + QK_priv_.readySet.update_(&QK_priv_.readySet_dis); +#endif + } + + if (QK_priv_.readySet.isEmpty()) { + p = 0U; // no activation needed + } + else { + // find new highest-prio AO ready to run... + p = static_cast<std::uint8_t>(QK_priv_.readySet.findMax()); + + // is the new prio. below the initial preemption-threshold? + if (p <= QP::QActive::registry_[prio_in]->getPThre()) { + 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 = QP::QActive::registry_[prio_in]->getPThre(); + +#if (defined QF_ON_CONTEXT_SW) || (defined Q_SPY) +if (prio_in != 0U) { // resuming an active object? + a = QP::QActive::registry_[prio_in]; // pointer to preempted AO + + QS_BEGIN_PRE_(QP::QS_SCHED_NEXT, prio_in) + QS_TIME_PRE_(); // timestamp + // prio. of the resumed AO, previous prio. + QS_2U8_PRE_(prio_in, pprev); + QS_END_PRE_() +} +else { // resuming prio.==0 --> idle + a = nullptr; // QK idle loop + + QS_BEGIN_PRE_(QP::QS_SCHED_IDLE, pprev) + QS_TIME_PRE_(); // timestamp + QS_U8_PRE_(pprev); // previous prio. + QS_END_PRE_() +} + +#ifdef QF_ON_CONTEXT_SW +QF_onContextSw(QP::QActive::registry_[pprev], a); +#endif // QF_ON_CONTEXT_SW + +#endif // QF_ON_CONTEXT_SW || Q_SPY + + + + + + + QSchedStatus lockStat_; + + + + + + do { \ + if (QK_ISR_CONTEXT_()) { \ + lockStat_ = 0xFFU; \ + } else { \ + lockStat_ = QK::schedLock((ceil_)); \ + } \ +} while (false) + + + + do { \ + if (lockStat_ != 0xFFU) { \ + QK::schedUnlock(lockStat_); \ + } \ +} while (false) + + + + + + \ + Q_ASSERT_INCRIT(320, (me_)->m_eQueue.m_frontEvt != nullptr) + + + + + + do { \ + QK_priv_.readySet.insert( \ + static_cast<std::uint_fast8_t>((me_)->m_prio)); \ + QK_priv_.readySet.update_(&QK_priv_.readySet_dis); \ + if (!QK_ISR_CONTEXT_()) { \ + if (QK_sched_() != 0U) { \ + QK_activate_(); \ + } \ + } \ +} while (false) + + + + + + do { \ + QK_priv_.readySet.insert( \ + static_cast<std::uint_fast8_t>((me_)->m_prio)); \ + if (!QK_ISR_CONTEXT_()) { \ + if (QK_sched_() != 0U) { \ + QK_activate_(); \ + } \ + } \ +} while (false) + + + + + + + = std::uint_fast16_t; + + + + {0U}; + + + + + + + + noexcept + + + QF_CRIT_STAT +QF_CRIT_ENTRY(); +QF_MEM_SYS(); + +Q_REQUIRE_INCRIT(100, !QXK_ISR_CONTEXT_()); + +QSchedStatus stat; // saved lock status to be returned + +// is the lock ceiling being raised? +if (ceiling > QXK_priv_.lockCeil) { + QS_BEGIN_PRE_(QS_SCHED_LOCK, 0U) + QS_TIME_PRE_(); // timestamp + // the previous lock ceiling & new lock ceiling + QS_2U8_PRE_(static_cast<std::uint8_t>(QXK_priv_.lockCeil), + static_cast<std::uint8_t>(ceiling)); + QS_END_PRE_() + + // previous status of the lock + stat = static_cast<QSchedStatus>(QXK_priv_.lockHolder); + stat |= static_cast<QSchedStatus>(QXK_priv_.lockCeil) << 8U; + + // new status of the lock + QXK_priv_.lockHolder = (QXK_priv_.curr != nullptr) + ? QXK_priv_.curr->getPrio() + : 0U; + QXK_priv_.lockCeil = ceiling; +} +else { + stat = 0xFFU; // scheduler not locked +} +QF_MEM_APP(); +QF_CRIT_EXIT(); + +return stat; // return the status to be saved in a stack variable + + + + noexcept + + + // has the scheduler been actually locked by the last QXK::schedLock()? +if (stat != 0xFFU) { + std::uint8_t const prevCeil = static_cast<std::uint8_t>(stat >> 8U); + QF_CRIT_STAT + QF_CRIT_ENTRY(); + QF_MEM_SYS(); + + Q_REQUIRE_INCRIT(200, !QXK_ISR_CONTEXT_()); + Q_REQUIRE_INCRIT(201, QXK_priv_.lockCeil > prevCeil); + + QS_BEGIN_PRE_(QS_SCHED_UNLOCK, 0U) + QS_TIME_PRE_(); // timestamp + // ceiling before unlocking & prio after unlocking + QS_2U8_PRE_(QXK_priv_.lockCeil, prevCeil); + QS_END_PRE_() + + // restore the previous lock ceiling and lock holder + QXK_priv_.lockCeil = prevCeil; + QXK_priv_.lockHolder = (stat & 0xFFU); + + // find if any threads should be run after unlocking the scheduler + if (QXK_sched_() != 0U) { // activation needed? + QXK_activate_(); // synchronously activate basic-thred(s) + } + + QF_MEM_APP(); + QF_CRIT_EXIT(); +} + + + + noexcept + QF_CRIT_STAT +QF_CRIT_ENTRY(); +QF_MEM_SYS(); + +Q_REQUIRE_INCRIT(600, QXK_priv_.lockCeil <= QF_MAX_ACTIVE); + +QP::QActive *curr = QXK_priv_.curr; +if (curr == nullptr) { // basic thread? + curr = QP::QActive::registry_[QXK_priv_.actPrio]; +} + +Q_ASSERT_INCRIT(690, curr != nullptr); + +QF_MEM_APP(); +QF_CRIT_EXIT(); + +return curr; + + + + + + + + + + + + + + + + + {0U}; + + + + noexcept + + + + + : QActive(Q_STATE_CAST(handler)), + m_timeEvt(this, static_cast<QSignal>(QXK::DELAY_SIG), tickRate) + +m_state.act = nullptr; // mark as extended thread + + + + override + + + + + Q_UNUSED_PAR(e); +Q_UNUSED_PAR(qs_id); +Q_ERROR_INCRIT(110); + + + + override + + + this->init(nullptr, qs_id); + + + + override + + + + + Q_UNUSED_PAR(e); +Q_UNUSED_PAR(qs_id); +Q_ERROR_INCRIT(120); + + + + const noexcept + return &m_timeEvt; + + + + noexcept + + + QF_CRIT_STAT +QF_CRIT_ENTRY(); +QF_MEM_SYS(); + +QXThread * const thr = QXK_PTR_CAST_(QXThread*, QXK_priv_.curr); + +// precondition, this function: +// - must NOT be called from an ISR; +// - number of ticks cannot be zero +// - be called from an extended thread; +// - the thread must NOT be already blocked on any object. +Q_REQUIRE_INCRIT(800, (!QXK_ISR_CONTEXT_()) + && (nTicks != 0U) + && (thr != nullptr) + && (thr->m_temp.obj == nullptr)); +// - the thread must NOT be holding a scheduler lock. +Q_REQUIRE_INCRIT(801, QXK_priv_.lockHolder != thr->m_prio); + +// remember the blocking object +thr->m_temp.obj = QXK_PTR_CAST_(QMState const*, &thr->m_timeEvt); +thr->teArm_(static_cast<enum_t>(QXK::DELAY_SIG), nTicks); +thr->block_(); + +QF_MEM_APP(); +QF_CRIT_EXIT(); +QF_CRIT_EXIT_NOP(); // BLOCK here + +// after unblocking... +QF_CRIT_ENTRY(); +QF_MEM_SYS(); + +// the blocking object must be the time event +Q_ASSERT_INCRIT(890, thr->m_temp.obj + == QXK_PTR_CAST_(QMState*, &thr->m_timeEvt)); +thr->m_temp.obj = nullptr; // clear + +QF_MEM_APP(); +QF_CRIT_EXIT(); + +// signal of zero means that the time event was posted without +// being canceled. +return (thr->m_timeEvt.sig == 0U); + + + + noexcept + QF_CRIT_STAT +QF_CRIT_ENTRY(); +QF_MEM_SYS(); + +bool wasArmed; +if (m_temp.obj == QXK_PTR_CAST_(QMState*, &m_timeEvt)) { + wasArmed = teDisarm_(); + unblock_(); +} +else { + wasArmed = false; +} +QF_MEM_APP(); +QF_CRIT_EXIT(); + +return wasArmed; + + + + noexcept + + + QF_CRIT_STAT +QF_CRIT_ENTRY(); +QF_MEM_SYS(); + +QXThread * const thr = QXTHREAD_CAST_(QXK_priv_.curr); + +// precondition, this function: +// - must NOT be called from an ISR; +// - be called from an extended thread; +// - the thread must NOT be already blocked on any object. +Q_REQUIRE_INCRIT(500, (!QXK_ISR_CONTEXT_()) + && (thr != nullptr) + && (thr->m_temp.obj == nullptr)); +// - the thread must NOT be holding a scheduler lock. +Q_REQUIRE_INCRIT(501, QXK_priv_.lockHolder != thr->m_prio); + +// is the queue empty? +if (thr->m_eQueue.m_frontEvt == nullptr) { + + // remember the blocking object (the thread's queue) + thr->m_temp.obj = QXK_PTR_CAST_(QMState*, &thr->m_eQueue); + + thr->teArm_(static_cast<enum_t>(QXK::TIMEOUT_SIG), nTicks); + QXK_priv_.readySet.remove( + static_cast<std::uint_fast8_t>(thr->m_prio)); +#ifndef Q_UNSAFE + QXK_priv_.readySet.update_(&QXK_priv_.readySet_dis); +#endif + + static_cast<void>(QXK_sched_()); // synchronous scheduling + + QF_MEM_APP(); + QF_CRIT_EXIT(); + QF_CRIT_EXIT_NOP(); // BLOCK here + + // after unblocking... + QF_CRIT_ENTRY(); + QF_MEM_SYS(); + + // the blocking object must be this queue + Q_ASSERT_INCRIT(510, thr->m_temp.obj == + QXK_PTR_CAST_(QMState *, &thr->m_eQueue)); + thr->m_temp.obj = nullptr; // clear +} + +// is the queue not empty? +QEvt const *e; +if (thr->m_eQueue.m_frontEvt != nullptr) { + e = thr->m_eQueue.m_frontEvt; // remove from the front + QEQueueCtr const nFree = thr->m_eQueue.m_nFree + 1U; + thr->m_eQueue.m_nFree = nFree; // update the # free + + // any events in the ring buffer? + if (nFree <= thr->m_eQueue.m_end) { + + // remove event from the tail + thr->m_eQueue.m_frontEvt = + thr->m_eQueue.m_ring[thr->m_eQueue.m_tail]; + if (thr->m_eQueue.m_tail == 0U) { + thr->m_eQueue.m_tail = thr->m_eQueue.m_end; // wrap + } + // advance the tail (counter clockwise) + thr->m_eQueue.m_tail = (thr->m_eQueue.m_tail - 1U); + + QS_BEGIN_PRE_(QS_QF_ACTIVE_GET, thr->m_prio) + QS_TIME_PRE_(); // timestamp + QS_SIG_PRE_(e->sig); // the signal of this event + QS_OBJ_PRE_(&thr); // this active object + QS_2U8_PRE_(e->getPoolId_(), e->refCtr_); // poolId & refCtr + QS_EQC_PRE_(nFree); // # free entries + QS_END_PRE_() + } + else { + thr->m_eQueue.m_frontEvt = nullptr; // empty queue + + // all entries in the queue must be free (+1 for fronEvt) + Q_ASSERT_INCRIT(520, nFree == (thr->m_eQueue.m_end + 1U)); + + QS_BEGIN_PRE_(QS_QF_ACTIVE_GET_LAST, thr->m_prio) + QS_TIME_PRE_(); // timestamp + QS_SIG_PRE_(e->sig); // the signal of this event + QS_OBJ_PRE_(&thr); // this active object + QS_2U8_PRE_(e->getPoolId_(), e->refCtr_); // poolId & refCtr + QS_END_PRE_() + } +} +else { // the queue is still empty -- the timeout must have fired + e = nullptr; +} +QF_MEM_APP(); +QF_CRIT_EXIT(); + +return e; + + + + const noexcept + // NOTE: must be called IN a critical section + +Q_REQUIRE_INCRIT(600, (QXK_priv_.lockHolder != m_prio)); + +QXK_priv_.readySet.remove(static_cast<std::uint_fast8_t>(m_prio)); +#ifndef Q_UNSAFE +QXK_priv_.readySet.update_(&QXK_priv_.readySet_dis); +#endif + +static_cast<void>(QXK_sched_()); // schedule other threads + + + + const noexcept + // NOTE: must be called IN a critical section + +QXK_priv_.readySet.insert(static_cast<std::uint_fast8_t>(m_prio)); +#ifndef Q_UNSAFE +QXK_priv_.readySet.update_(&QXK_priv_.readySet_dis); +#endif + +if ((!QXK_ISR_CONTEXT_()) // not inside ISR? + && (QActive::registry_[0] != nullptr)) // kernel started? +{ + static_cast<void>(QXK_sched_()); // schedule other threads +} + + + + + + // NOTE: must be called IN a critical section + +// the private time event is now disarmed and not in any queue, +// so it is safe to change its signal. The signal of 0 means +// that the time event has expired. +QXTHREAD_CAST_(act)->m_timeEvt.sig = 0U; + +QXTHREAD_CAST_(act)->unblock_(); + + + + noexcept + + + + + // NOTE: must be called IN a critical section + +// precondition: +// - the time event must be unused +Q_REQUIRE_INCRIT(700, m_timeEvt.m_ctr == 0U); + +m_timeEvt.sig = static_cast<QSignal>(sig); + +if (nTicks != QXTHREAD_NO_TIMEOUT) { + m_timeEvt.m_ctr = static_cast<QTimeEvtCtr>(nTicks); + m_timeEvt.m_interval = 0U; + + // is the time event unlinked? + // NOTE: For the duration of a single clock tick of the specified tick + // rate a time event can be disarmed and yet still linked in the list, + // because un-linking is performed exclusively in QTimeEvt::tickX(). + if (static_cast<std::uint8_t>(m_timeEvt.refCtr_ & TE_IS_LINKED) == 0U) + { + std::uint_fast8_t const tickRate = + static_cast<std::uint_fast8_t>(m_timeEvt.refCtr_); + Q_ASSERT_INCRIT(710, tickRate < QF_MAX_TICK_RATE); + + // mark as linked + m_timeEvt.refCtr_ = static_cast<std::uint8_t>( + m_timeEvt.refCtr_ | TE_IS_LINKED); + + // The time event is initially inserted into the separate + // "freshly armed" list based on timeEvtHead_[tickRate].act. + // Only later, inside QTimeEvt::tick(), the "freshly armed" + // list is appended to the main list of armed time events based on + // timeEvtHead_[tickRate].next. Again, this is to keep any + // changes to the main list exclusively inside QTimeEvt::tick(). + m_timeEvt.m_next + = QXK_PTR_CAST_(QTimeEvt*, + QTimeEvt::timeEvtHead_[tickRate].m_act); + QTimeEvt::timeEvtHead_[tickRate].m_act = &m_timeEvt; + } +} + + + + noexcept + // NOTE: must be called IN a critical section + +bool wasArmed; +// is the time evt running? +if (m_timeEvt.m_ctr != 0U) { + wasArmed = true; + // schedule removal from list + m_timeEvt.m_ctr = 0U; +} +// the time event was already automatically disarmed +else { + wasArmed = false; +} +return wasArmed; + + + + noexcept + + + + + + + + + + + + + + + + + + + noexcept + + + + + QF_CRIT_STAT +QF_CRIT_ENTRY(); +QF_MEM_SYS(); + +Q_REQUIRE_INCRIT(100, (count <= max_count) + && (0U < max_count) && (max_count <= 0xFFU)); + +m_count = static_cast<std::uint8_t>(count); +m_max_count = static_cast<std::uint8_t>(max_count); +m_waitSet.setEmpty(); + +QF_MEM_APP(); +QF_CRIT_EXIT(); + + + + noexcept + + + QF_CRIT_STAT +QF_CRIT_ENTRY(); +QF_MEM_SYS(); + +QXThread * const curr = QXK_PTR_CAST_(QXThread*, QXK_priv_.curr); + +// precondition, this function: +// - must NOT be called from an ISR; +// - the semaphore must be initialized +// - be called from an extended thread; +// - the thread must NOT be already blocked on any object. +Q_REQUIRE_INCRIT(200, (!QXK_ISR_CONTEXT_()) + && (m_max_count > 0U) + && (curr != nullptr) + && (curr->m_temp.obj == nullptr)); +// - the thread must NOT be holding a scheduler lock. +Q_REQUIRE_INCRIT(201, QXK_priv_.lockHolder != curr->m_prio); + +bool taken = true; // assume that the semaphore will be signaled +if (m_count > 0U) { + m_count = m_count - 1U; // semaphore taken: decrement the count + + QS_BEGIN_PRE_(QS_SEM_TAKE, curr->m_prio) + QS_TIME_PRE_(); // timestamp + QS_OBJ_PRE_(this); // this semaphore + QS_2U8_PRE_(curr->m_prio, m_count); + QS_END_PRE_() +} +else { // semaphore not available -- BLOCK the thread + std::uint_fast8_t const p = + static_cast<std::uint_fast8_t>(curr->m_prio); + // remove the curr prio from the ready set (will block) + // and insert to the waiting set on this semaphore + QXK_priv_.readySet.remove(p); +#ifndef Q_UNSAFE + QXK_priv_.readySet.update_(&QXK_priv_.readySet_dis); +#endif + m_waitSet.insert(p); + + // remember the blocking object (this semaphore) + curr->m_temp.obj = QXK_PTR_CAST_(QMState*, this); + curr->teArm_(static_cast<enum_t>(QXK::TIMEOUT_SIG), nTicks); + + QS_BEGIN_PRE_(QS_SEM_BLOCK, curr->m_prio) + QS_TIME_PRE_(); // timestamp + QS_OBJ_PRE_(this); // this semaphore + QS_2U8_PRE_(curr->m_prio, m_count); + QS_END_PRE_() + + // schedule the next thread if multitasking started + static_cast<void>(QXK_sched_()); // schedule other threads + + QF_MEM_APP(); + QF_CRIT_EXIT(); + QF_CRIT_EXIT_NOP(); // BLOCK here !!! + + QF_CRIT_ENTRY(); // AFTER unblocking... + QF_MEM_SYS(); + + // the blocking object must be this semaphore + Q_ASSERT_INCRIT(240, curr->m_temp.obj + == QXK_PTR_CAST_(QMState*, this)); + + // did the blocking time-out? (signal of zero means that it did) + if (curr->m_timeEvt.sig == 0U) { + if (m_waitSet.hasElement(p)) { // still waiting? + m_waitSet.remove(p); // remove unblocked thread + taken = false; // the semaphore was NOT taken + } + } + else { // blocking did NOT time out + // the thread must NOT be waiting on this semaphore + Q_ASSERT_INCRIT(250, !m_waitSet.hasElement(p)); + } + curr->m_temp.obj = nullptr; // clear blocking obj. +} +QF_MEM_APP(); +QF_CRIT_EXIT(); + +return taken; + + + + noexcept + QF_CRIT_STAT +QF_CRIT_ENTRY(); +QF_MEM_SYS(); + +// precondition: +// - the semaphore must be initialized +Q_REQUIRE_INCRIT(300, m_max_count > 0U); + +#ifdef Q_SPY +QActive const * const curr = QXK_PTR_CAST_(QActive*, QXK_priv_.curr); +#endif // Q_SPY + +bool taken; +// is the semaphore available? +if (m_count > 0U) { + m_count = m_count - 1U; // semaphore signaled: decrement + taken = true; + + QS_BEGIN_PRE_(QS_SEM_TAKE, curr->m_prio) + QS_TIME_PRE_(); // timestamp + QS_OBJ_PRE_(this); // this semaphore + QS_2U8_PRE_(curr->m_prio, m_count); + QS_END_PRE_() +} +else { // the semaphore is NOT available (would block) + taken = false; + + QS_BEGIN_PRE_(QS_SEM_BLOCK_ATTEMPT, curr->m_prio) + QS_TIME_PRE_(); // timestamp + QS_OBJ_PRE_(this); // this semaphore + QS_2U8_PRE_(curr->m_prio, m_count); + QS_END_PRE_() +} +QF_MEM_APP(); +QF_CRIT_EXIT(); + +return taken; + + + + noexcept + bool signaled = true; // assume that the semaphore will be signaled + +QF_CRIT_STAT +QF_CRIT_ENTRY(); +QF_MEM_SYS(); + +// precondition: +// - the semaphore must be initialized +Q_REQUIRE_INCRIT(400, m_max_count > 0U); + +// any threads blocked on this semaphore? +if (m_waitSet.notEmpty()) { + // find the highest-prio. thread waiting on this semaphore + std::uint_fast8_t const p = m_waitSet.findMax(); + QXThread * const thr = + QXK_PTR_CAST_(QXThread*, QActive::registry_[p]); + + // assert that the tread: + // - must be registered in QF; + // - must be extended; and + // - must be blocked on this semaphore; + Q_ASSERT_INCRIT(410, (thr != nullptr) + && (thr->m_osObject != nullptr) + && (thr->m_temp.obj + == QXK_PTR_CAST_(QMState*, this))); + + // disarm the internal time event + static_cast<void>(thr->teDisarm_()); + + // make the thread ready to run and remove from the wait-list + QXK_priv_.readySet.insert(p); +#ifndef Q_UNSAFE + QXK_priv_.readySet.update_(&QXK_priv_.readySet_dis); +#endif + m_waitSet.remove(p); + + QS_BEGIN_PRE_(QS_SEM_TAKE, thr->m_prio) + QS_TIME_PRE_(); // timestamp + QS_OBJ_PRE_(this); // this semaphore + QS_2U8_PRE_(thr->m_prio, m_count); + QS_END_PRE_() + + if (!QXK_ISR_CONTEXT_()) { // not inside ISR? + static_cast<void>(QXK_sched_()); // schedule other threads + } +} +else if (m_count < m_max_count) { + m_count = m_count + 1U; // semaphore signaled: increment + + QS_BEGIN_PRE_(QS_SEM_SIGNAL, 0U) + QS_TIME_PRE_(); // timestamp + QS_OBJ_PRE_(this); // this semaphore + QS_2U8_PRE_(0U, m_count); + QS_END_PRE_() +} +else { + signaled = false; // semaphore NOT signaled +} +QF_MEM_APP(); +QF_CRIT_EXIT(); + +return signaled; + + + + + + + + + + + : m_ao(Q_STATE_CAST(0)) + + + + noexcept + + + QF_CRIT_STAT +QF_CRIT_ENTRY(); +QF_MEM_SYS(); + +Q_REQUIRE_INCRIT(100, (prioSpec & 0xFF00U) == 0U); + +m_ao.m_prio = static_cast<std::uint8_t>(prioSpec & 0xFFU); // QF-prio. +m_ao.m_pthre = 0U; // preemption-threshold (not used) +QActive &ao = m_ao; + +QF_MEM_APP(); +QF_CRIT_EXIT(); + +ao.register_(); // register this mutex as AO + + + + noexcept + + + QF_CRIT_STAT +QF_CRIT_ENTRY(); +QF_MEM_SYS(); + +QXThread * const curr = QXK_PTR_CAST_(QXThread*, QXK_priv_.curr); + +// precondition, this mutex operation must: +// - NOT be called from an ISR; +// - be called from an eXtended thread; +// - the mutex-prio. must be in range; +// - the thread must NOT be already blocked on any object. +Q_REQUIRE_INCRIT(200, (!QXK_ISR_CONTEXT_()) + && (curr != nullptr) + && (m_ao.m_prio <= QF_MAX_ACTIVE) + && (curr->m_temp.obj == nullptr)); +// also: the thread must NOT be holding a scheduler lock. +Q_REQUIRE_INCRIT(201, QXK_priv_.lockHolder != curr->m_prio); + +// is the mutex available? +bool locked = true; // assume that the mutex will be locked +if (m_ao.m_eQueue.m_nFree == 0U) { + m_ao.m_eQueue.m_nFree = 1U; // mutex lock nesting + + // also: the newly locked mutex must have no holder yet + Q_REQUIRE_INCRIT(203, m_ao.m_osObject == nullptr); + + // set the new mutex holder to the curr thread and + // save the thread's prio in the mutex + // NOTE: reuse the otherwise unused eQueue data member. + m_ao.m_osObject = curr; + m_ao.m_eQueue.m_head = static_cast<QEQueueCtr>(curr->m_prio); + + QS_BEGIN_PRE_(QS_MTX_LOCK, curr->m_prio) + QS_TIME_PRE_(); // timestamp + QS_OBJ_PRE_(this); // this mutex + QS_U8_PRE_(static_cast<std::uint8_t>(m_ao.m_eQueue.m_head)); + QS_U8_PRE_(static_cast<std::uint8_t>(m_ao.m_eQueue.m_nFree)); + QS_END_PRE_() + + if (m_ao.m_prio != 0U) { // prio.-ceiling protocol used? + // the holder prio. must be lower than that of the mutex + // and the prio. slot must be occupied by this mutex + Q_ASSERT_INCRIT(210, (curr->m_prio < m_ao.m_prio) + && (QActive::registry_[m_ao.m_prio] == &m_ao)); + + // remove the thread's original prio from the ready set + // and insert the mutex's prio into the ready set + QXK_priv_.readySet.remove( + static_cast<std::uint_fast8_t>(m_ao.m_eQueue.m_head)); + QXK_priv_.readySet.insert( + static_cast<std::uint_fast8_t>(m_ao.m_prio)); +#ifndef Q_UNSAFE + QXK_priv_.readySet.update_(&QXK_priv_.readySet_dis); +#endif + // put the thread into the AO registry in place of the mutex + QActive::registry_[m_ao.m_prio] = curr; + + // set thread's prio to that of the mutex + curr->m_prio = m_ao.m_prio; + } +} +// is the mutex locked by this thread already (nested locking)? +else if (m_ao.m_osObject == curr) { + + // the nesting level beyond the arbitrary but high limit + // most likely means cyclic or recursive locking of a mutex. + Q_ASSERT_INCRIT(220, m_ao.m_eQueue.m_nFree < 0xFFU); + + // lock one more level + m_ao.m_eQueue.m_nFree = m_ao.m_eQueue.m_nFree + 1U; + + QS_BEGIN_PRE_(QS_MTX_LOCK, curr->m_prio) + QS_TIME_PRE_(); // timestamp + QS_OBJ_PRE_(this); // this mutex + QS_U8_PRE_(static_cast<std::uint8_t>(m_ao.m_eQueue.m_head)); + QS_U8_PRE_(static_cast<std::uint8_t>(m_ao.m_eQueue.m_nFree)); + QS_END_PRE_() +} +else { // the mutex is already locked by a different thread + // the mutex holder must be valid + Q_ASSERT_INCRIT(230, m_ao.m_osObject != nullptr); + + if (m_ao.m_prio != 0U) { // prio.-ceiling protocol used? + // the prio slot must be occupied by the thr. holding the mutex + Q_ASSERT_INCRIT(240, QActive::registry_[m_ao.m_prio] + == QXK_PTR_CAST_(QActive const *, m_ao.m_osObject)); + } + + // remove the curr thread's prio from the ready set (will block) + // and insert it to the waiting set on this mutex + std::uint_fast8_t const p = + static_cast<std::uint_fast8_t>(curr->m_prio); + QXK_priv_.readySet.remove(p); +#ifndef Q_UNSAFE + QXK_priv_.readySet.update_(&QXK_priv_.readySet_dis); +#endif + m_waitSet.insert(p); + + // set the blocking object (this mutex) + curr->m_temp.obj = QXK_PTR_CAST_(QMState*, this); + curr->teArm_(static_cast<enum_t>(QXK::TIMEOUT_SIG), nTicks); + + QS_BEGIN_PRE_(QS_MTX_BLOCK, curr->m_prio) + QS_TIME_PRE_(); // timestamp + QS_OBJ_PRE_(this); // this mutex + QS_2U8_PRE_(static_cast<std::uint8_t>(m_ao.m_eQueue.m_head), + curr->m_prio); + QS_END_PRE_() + + // schedule the next thread if multitasking started + static_cast<void>(QXK_sched_()); // schedule other threads + + QF_MEM_APP(); + QF_CRIT_EXIT(); + QF_CRIT_EXIT_NOP(); // BLOCK here !!! + + // AFTER unblocking... + QF_CRIT_ENTRY(); + QF_MEM_SYS(); + // the blocking object must be this mutex + Q_ASSERT_INCRIT(250, curr->m_temp.obj + == QXK_PTR_CAST_(QMState*, this)); + + // did the blocking time-out? (signal of zero means that it did) + if (curr->m_timeEvt.sig == 0U) { + if (m_waitSet.hasElement(p)) { // still waiting? + m_waitSet.remove(p); // remove unblocked thread + locked = false; // the mutex was NOT locked + } + } + else { // blocking did NOT time out + // the thread must NOT be waiting on this mutex + Q_ASSERT_INCRIT(260, !m_waitSet.hasElement(p)); + } + curr->m_temp.obj = nullptr; // clear blocking obj. +} +QF_MEM_APP(); +QF_CRIT_EXIT(); + +return locked; + + + + noexcept + QF_CRIT_STAT +QF_CRIT_ENTRY(); +QF_MEM_SYS(); + +QActive *curr = QXK_priv_.curr; +if (curr == nullptr) { // called from a basic thread? + curr = QActive::registry_[QXK_priv_.actPrio]; +} + +// precondition, this mutex must: +// - NOT be called from an ISR; +// - the calling thread must be valid; +// - the mutex-prio. must be in range +Q_REQUIRE_INCRIT(300, (!QXK_ISR_CONTEXT_()) + && (curr != nullptr) + && (m_ao.m_prio <= QF_MAX_ACTIVE)); +// also: the thread must NOT be holding a scheduler lock. +Q_REQUIRE_INCRIT(301, QXK_priv_.lockHolder != curr->m_prio); + +// is the mutex available? +if (m_ao.m_eQueue.m_nFree == 0U) { + m_ao.m_eQueue.m_nFree = 1U; // mutex lock nesting + + // also the newly locked mutex must have no holder yet + Q_REQUIRE_INCRIT(303, m_ao.m_osObject == nullptr); + + // set the new mutex holder to the curr thread and + // save the thread's prio in the mutex + // NOTE: reuse the otherwise unused eQueue data member. + m_ao.m_osObject = curr; + m_ao.m_eQueue.m_head = static_cast<QEQueueCtr>(curr->m_prio); + + QS_BEGIN_PRE_(QS_MTX_LOCK, curr->m_prio) + QS_TIME_PRE_(); // timestamp + QS_OBJ_PRE_(this); // this mutex + QS_U8_PRE_(static_cast<std::uint8_t>(m_ao.m_eQueue.m_head)); + QS_U8_PRE_(static_cast<std::uint8_t>(m_ao.m_eQueue.m_nFree)); + QS_END_PRE_() + + if (m_ao.m_prio != 0U) { // prio.-ceiling protocol used? + // the holder prio. must be lower than that of the mutex + // and the prio. slot must be occupied by this mutex + Q_ASSERT_INCRIT(310, (curr->m_prio < m_ao.m_prio) + && (QActive::registry_[m_ao.m_prio] == &m_ao)); + + // remove the thread's original prio from the ready set + // and insert the mutex's prio into the ready set + QXK_priv_.readySet.remove( + static_cast<std::uint_fast8_t>(m_ao.m_eQueue.m_head)); + QXK_priv_.readySet.insert( + static_cast<std::uint_fast8_t>(m_ao.m_prio)); +#ifndef Q_UNSAFE + QXK_priv_.readySet.update_(&QXK_priv_.readySet_dis); +#endif + // put the thread into the AO registry in place of the mutex + QActive::registry_[m_ao.m_prio] = curr; + + // set thread's prio to that of the mutex + curr->m_prio = m_ao.m_prio; + } +} +// is the mutex locked by this thread already (nested locking)? +else if (m_ao.m_osObject == curr) { + // the nesting level must not exceed the specified limit + Q_ASSERT_INCRIT(320, m_ao.m_eQueue.m_nFree < 0xFFU); + + // lock one more level + m_ao.m_eQueue.m_nFree = m_ao.m_eQueue.m_nFree + 1U; + + QS_BEGIN_PRE_(QS_MTX_LOCK, curr->m_prio) + QS_TIME_PRE_(); // timestamp + QS_OBJ_PRE_(this); // this mutex + QS_U8_PRE_(static_cast<std::uint8_t>(m_ao.m_eQueue.m_head)); + QS_U8_PRE_(static_cast<std::uint8_t>(m_ao.m_eQueue.m_nFree)); + QS_END_PRE_() +} +else { // the mutex is already locked by a different thread + if (m_ao.m_prio != 0U) { // prio.-ceiling protocol used? + // the prio slot must be occupied by the thr. holding the mutex + Q_ASSERT_INCRIT(330, QActive::registry_[m_ao.m_prio] + == QXK_PTR_CAST_(QActive const *, m_ao.m_osObject)); + } + + QS_BEGIN_PRE_(QS_MTX_BLOCK_ATTEMPT, curr->m_prio) + QS_TIME_PRE_(); // timestamp + QS_OBJ_PRE_(this); // this mutex + QS_2U8_PRE_(static_cast<std::uint8_t>(m_ao.m_eQueue.m_head), + curr->m_prio); // trying thread prio + QS_END_PRE_() + + curr = nullptr; // means that mutex is NOT available +} +QF_MEM_APP(); +QF_CRIT_EXIT(); + +return curr != nullptr; + + + + noexcept + QF_CRIT_STAT +QF_CRIT_ENTRY(); +QF_MEM_SYS(); + +QActive *curr = QXK_priv_.curr; +if (curr == nullptr) { // called from a basic thread? + curr = QActive::registry_[QXK_priv_.actPrio]; +} + +Q_REQUIRE_INCRIT(400, (!QXK_ISR_CONTEXT_()) + && (curr != nullptr)); +Q_REQUIRE_INCRIT(401, m_ao.m_eQueue.m_nFree > 0U); +Q_REQUIRE_INCRIT(403, m_ao.m_osObject == curr); + +// is this the last nesting level? +if (m_ao.m_eQueue.m_nFree == 1U) { + + if (m_ao.m_prio != 0U) { // prio.-ceiling protocol used? + + Q_ASSERT_INCRIT(410, m_ao.m_prio < QF_MAX_ACTIVE); + + // restore the holding thread's prio from the mutex + curr->m_prio = + static_cast<std::uint8_t>(m_ao.m_eQueue.m_head); + + // put the mutex back into the AO registry + QActive::registry_[m_ao.m_prio] = &m_ao; + + // remove the mutex' prio from the ready set + // and insert the original thread's prio. + QXK_priv_.readySet.remove( + static_cast<std::uint_fast8_t>(m_ao.m_prio)); + QXK_priv_.readySet.insert( + static_cast<std::uint_fast8_t>(m_ao.m_eQueue.m_head)); +#ifndef Q_UNSAFE + QXK_priv_.readySet.update_(&QXK_priv_.readySet_dis); +#endif + } + + QS_BEGIN_PRE_(QS_MTX_UNLOCK, curr->m_prio) + QS_TIME_PRE_(); // timestamp + QS_OBJ_PRE_(this); // this mutex + QS_2U8_PRE_(static_cast<std::uint8_t>(m_ao.m_eQueue.m_head), + 0U); + QS_END_PRE_() + + // are any other threads waiting on this mutex? + if (m_waitSet.notEmpty()) { + // find the highest-prio. thread waiting on this mutex + std::uint_fast8_t const p = m_waitSet.findMax(); + + // remove this thread from waiting on the mutex + // and insert it into the ready set. + m_waitSet.remove(p); + QXK_priv_.readySet.insert(p); +#ifndef Q_UNSAFE + QXK_priv_.readySet.update_(&QXK_priv_.readySet_dis); +#endif + + QXThread * const thr = + QXK_PTR_CAST_(QXThread*, QActive::registry_[p]); + + // the waiting thread must: + // - be registered in QF + // - have the prio. corresponding to the registration + // - be an extended thread + // - be blocked on this mutex + Q_ASSERT_INCRIT(420, (thr != nullptr) + && (thr->m_prio == static_cast<std::uint8_t>(p)) + && (thr->m_state.act == Q_ACTION_CAST(0)) + && (thr->m_temp.obj == QXK_PTR_CAST_(QMState*, this))); + + // disarm the internal time event + static_cast<void>(thr->teDisarm_()); + + // set the new mutex holder to the curr thread and + // save the thread's prio in the mutex + // NOTE: reuse the otherwise unused eQueue data member. + m_ao.m_osObject = thr; + m_ao.m_eQueue.m_head = static_cast<QEQueueCtr>(thr->m_prio); + + QS_BEGIN_PRE_(QS_MTX_LOCK, thr->m_prio) + QS_TIME_PRE_(); // timestamp + QS_OBJ_PRE_(this); // this mutex + QS_U8_PRE_(static_cast<std::uint8_t>(m_ao.m_eQueue.m_head)); + QS_U8_PRE_(static_cast<std::uint8_t>(m_ao.m_eQueue.m_nFree)); + QS_END_PRE_() + + if (m_ao.m_prio != 0U) { // prio.-ceiling protocol used? + // the holder prio. must be lower than that of the mutex + Q_ASSERT_INCRIT(430, (m_ao.m_prio < QF_MAX_ACTIVE) + && (thr->m_prio < m_ao.m_prio)); + + // put the thread into AO registry in place of the mutex + QActive::registry_[m_ao.m_prio] = thr; + } + } + else { // no threads are waiting for this mutex + m_ao.m_eQueue.m_nFree = 0U; // free up the nesting count + + // the mutex no longer held by any thread + m_ao.m_osObject = nullptr; + m_ao.m_eQueue.m_head = 0U; + m_ao.m_eQueue.m_tail = 0U; + + if (m_ao.m_prio != 0U) { // prio.-ceiling protocol used? + // the AO priority must be in range + Q_ASSERT_INCRIT(440, m_ao.m_prio < QF_MAX_ACTIVE); + + // put the mutex back at the original mutex slot + QActive::registry_[m_ao.m_prio] = &m_ao; + } + } + + // schedule the next thread if multitasking started + if (QXK_sched_() != 0U) { // activation needed? + QXK_activate_(); // synchronously activate basic-thred(s) + } +} +else { // releasing one level of nested mutex lock + // unlock one level + m_ao.m_eQueue.m_nFree = m_ao.m_eQueue.m_nFree - 1U; + + QS_BEGIN_PRE_(QS_MTX_UNLOCK_ATTEMPT, curr->m_prio) + QS_TIME_PRE_(); // timestamp + QS_OBJ_PRE_(this); // this mutex + QS_U8_PRE_(static_cast<std::uint8_t>(m_ao.m_eQueue.m_head)); + QS_U8_PRE_(static_cast<std::uint8_t>(m_ao.m_eQueue.m_nFree)); + QS_END_PRE_() +} +QF_MEM_APP(); +QF_CRIT_EXIT(); + + + + + + + bzero_(&QF::priv_, sizeof(QF::priv_)); +bzero_(&QXK_priv_, sizeof(QXK_priv_)); +bzero_(&QTimeEvt::timeEvtHead_[0], sizeof(QTimeEvt::timeEvtHead_)); +bzero_(&QActive::registry_[0], sizeof(QActive::registry_)); + +#ifndef Q_UNSAFE +QXK_priv_.readySet.update_(&QXK_priv_.readySet_dis); +#endif + +// setup the QXK scheduler as initially locked and not running +QXK_priv_.lockCeil = (QF_MAX_ACTIVE + 1U); // scheduler locked + +// storage capable for holding a blank QActive object (const in ROM) +static void* const + idle_ao[((sizeof(QActive) + sizeof(void*)) - 1U) / sizeof(void*)] + = { nullptr }; + +// register the idle AO object (cast 'const' away) +QActive::registry_[0] = QF_CONST_CAST_(QActive*, + reinterpret_cast<QActive const*>(idle_ao)); + +#ifdef QXK_INIT +QXK_INIT(); // port-specific initialization of the QXK kernel +#endif + + + + onCleanup(); // cleanup callback +// nothing else to do for the QXK preemptive kernel + + + + #ifdef Q_SPY +QS_SIG_DICTIONARY(QXK::DELAY_SIG, nullptr); +QS_SIG_DICTIONARY(QXK::TIMEOUT_SIG, nullptr); + +// produce the QS_QF_RUN trace record +QF_INT_DISABLE(); +QF_MEM_SYS(); +QS::beginRec_(QS_REC_NUM_(QS_QF_RUN)); +QS::endRec_(); +QF_MEM_APP(); +QF_INT_ENABLE(); +#endif // Q_SPY + +onStartup(); // application-specific startup callback + +QF_INT_DISABLE(); +QF_MEM_SYS(); + +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 + + + + + + + + + + + + + + + + + + + QF_CRIT_STAT +QF_CRIT_ENTRY(); +Q_REQUIRE_INCRIT(300, (!QXK_ISR_CONTEXT_()) + && ((prioSpec & 0xFF00U) == 0U)); +QF_CRIT_EXIT(); + +m_prio = static_cast<std::uint8_t>(prioSpec & 0xFFU); // QF-prio. +m_pthre = 0U; // preemption-threshold NOT used +register_(); // make QF aware of this QActive/QXThread + +if (stkSto == nullptr) { // starting basic thread (AO)? + m_eQueue.init(qSto, qLen); // init the built-in queue + m_osObject = nullptr; // no private stack for AO + + this->init(par, m_prio); // top-most initial tran. (virtual call) + QS_FLUSH(); // flush the trace buffer to the host + + // see if this AO needs to be scheduled if QXK is already running + QF_CRIT_ENTRY(); + QF_MEM_SYS(); + if (QXK_priv_.lockCeil <= QF_MAX_ACTIVE) { // scheduler running? + if (QXK_sched_() != 0U) { // activation needed? + QXK_activate_(); // synchronously activate basic-thred(s) + } + } + QF_MEM_APP(); + QF_CRIT_EXIT(); +} +else { // starting QXThread + + // is storage for the queue buffer provided? + if (qSto != nullptr) { + m_eQueue.init(qSto, qLen); + } + + // extended threads provide their thread function in place of + // the top-most initial tran. 'm_temp.act' + QXK_PTR_CAST_(QXThread*, this)->stackInit_(m_temp.thr, + stkSto, stkSize); + + // the new thread is not blocked on any object + m_temp.obj = nullptr; + + QF_CRIT_ENTRY(); + QF_MEM_SYS(); + + // extended-thread becomes ready immediately + QXK_priv_.readySet.insert(static_cast<std::uint_fast8_t>(m_prio)); + #ifndef Q_UNSAFE + QXK_priv_.readySet.update_(&QXK_priv_.readySet_dis); + #endif + + // see if this thread needs to be scheduled in case QXK is running + if (QXK_priv_.lockCeil <= QF_MAX_ACTIVE) { + static_cast<void>(QXK_sched_()); // schedule other threads + } + QF_MEM_APP(); + QF_CRIT_EXIT(); +} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + noexcept + Q_REQUIRE_INCRIT(402, + QXK_priv_.readySet.verify_(&QXK_priv_.readySet_dis)); + +std::uint_fast8_t p; +if (QXK_priv_.readySet.isEmpty()) { + p = 0U; // no activation needed +} +else { + // find the highest-prio thread ready to run + p = QXK_priv_.readySet.findMax(); + if (p <= QXK_priv_.lockCeil) { + // prio. of the thread holding the lock + p = static_cast<std::uint_fast8_t>( + QP::QActive::registry_[QXK_priv_.lockHolder]->getPrio()); + if (p != 0U) { + Q_ASSERT_INCRIT(410, QXK_priv_.readySet.hasElement(p)); + } + } +} +QP::QActive const * const curr = QXK_priv_.curr; +QP::QActive * const next = QP::QActive::registry_[p]; + +// the next thread found must be registered in QF +Q_ASSERT_INCRIT(420, next != nullptr); + +// is the current thread a basic-thread? +if (curr == nullptr) { + + // is the new prio. above the active prio.? + if (p > QXK_priv_.actPrio) { + QXK_priv_.next = next; // set the next AO to activate + + if (next->getOsObject() != nullptr) { // is next extended? + QXK_CONTEXT_SWITCH_(); + p = 0U; // no activation needed + } + } + else { // below the pre-thre + QXK_priv_.next = nullptr; + p = 0U; // no activation needed + } +} +else { // currently executing an extended-thread + // is the current thread different from the next? + if (curr != next) { + QXK_priv_.next = next; + QXK_CONTEXT_SWITCH_(); + } + else { // current is the same as next + QXK_priv_.next = nullptr; // no need to context-switch + } + p = 0U; // no activation needed +} + +return p; + + + + noexcept + std::uint_fast8_t const prio_in = QXK_priv_.actPrio; +QP::QActive *next = QXK_priv_.next; // the next AO (basic-thread) to run + +Q_REQUIRE_INCRIT(500, (next != nullptr) && (prio_in <= QF_MAX_ACTIVE)); + +// QXK Context switch callback defined or QS tracing enabled? +#if (defined QF_ON_CONTEXT_SW) || (defined Q_SPY) +QXK_contextSw_(next); +#endif // QF_ON_CONTEXT_SW || Q_SPY + +QXK_priv_.next = nullptr; // clear the next AO +QXK_priv_.curr = nullptr; // current is basic-thread + +// prio. of the next thread +std::uint_fast8_t p = next->getPrio(); + +// loop until no more ready-to-run AOs of higher prio than the initial +do { + QXK_priv_.actPrio = p; // next active prio + + QF_INT_ENABLE(); // unconditionally enable interrupts + + QP::QEvt const * const e = next->get_(); + // NOTE QActive::get_() performs QS_MEM_APP() before return + + // dispatch event (virtual call) + next->dispatch(e, next->getPrio()); +#if (QF_MAX_EPOOL > 0U) + QP::QF::gc(e); +#endif + + QF_INT_DISABLE(); // unconditionally disable interrupts + QF_MEM_SYS(); + + // check internal integrity (duplicate inverse storage) + Q_ASSERT_INCRIT(502, + QXK_priv_.readySet.verify_(&QXK_priv_.readySet_dis)); + + if (next->getEQueue().isEmpty()) { // empty queue? + QXK_priv_.readySet.remove(p); +#ifndef Q_UNSAFE + QXK_priv_.readySet.update_(&QXK_priv_.readySet_dis); +#endif + } + + if (QXK_priv_.readySet.isEmpty()) { + QXK_priv_.next = nullptr; + next = QP::QActive::registry_[0]; + p = 0U; // no activation needed + } + else { + // find next highest-prio below the lock ceiling + p = QXK_priv_.readySet.findMax(); + if (p <= QXK_priv_.lockCeil) { + p = QXK_priv_.lockHolder; + if (p != 0U) { + Q_ASSERT_INCRIT(510, QXK_priv_.readySet.hasElement(p)); + } + } + + // set the next thread and ensure that it is registered + next = QP::QActive::registry_[p]; + Q_ASSERT_INCRIT(520, next != nullptr); + + // is next a basic thread? + if (next->getOsObject() == nullptr) { + // is the next prio. above the initial prio.? + if (p > QP::QActive::registry_[prio_in]->getPrio()) { +#if (defined QF_ON_CONTEXT_SW) || (defined Q_SPY) + if (p != QXK_priv_.actPrio) { // changing threads? + QXK_contextSw_(next); + } +#endif // QF_ON_CONTEXT_SW || Q_SPY + QXK_priv_.next = next; + } + else { + QXK_priv_.next = nullptr; + p = 0U; // no activation needed + } + } + else { // next is the extended-thread + QXK_priv_.next = next; + QXK_CONTEXT_SWITCH_(); + p = 0U; // no activation needed + } + } +} while (p != 0U); // while activation needed + +// restore the active prio. +QXK_priv_.actPrio = prio_in; + +#if (defined QF_ON_CONTEXT_SW) || (defined Q_SPY) +if (next->getOsObject() == nullptr) { + QXK_contextSw_((prio_in == 0U) + ? nullptr + : QP::QActive::registry_[prio_in]); +} +#endif // QF_ON_CONTEXT_SW || Q_SPY + + + + + + #ifdef Q_SPY +std::uint_fast8_t const prev_prio = (QXK_priv_.prev != nullptr) + ? QXK_priv_.prev->getPrio() + : 0U; +if (next != nullptr) { // next is NOT idle? + std::uint_fast8_t const next_prio = next->getPrio(); + QS_BEGIN_PRE_(QP::QS_SCHED_NEXT, next_prio) + QS_TIME_PRE_(); // timestamp + QS_2U8_PRE_(next_prio, prev_prio); + QS_END_PRE_() +} +else { // going to idle + QS_BEGIN_PRE_(QP::QS_SCHED_IDLE, prev_prio) + QS_TIME_PRE_(); // timestamp + QS_U8_PRE_(prev_prio); + QS_END_PRE_() +} +#endif // Q_SPY + +#ifdef QF_ON_CONTEXT_SW +QF_onContextSw(QXK_priv_.prev, next); +#endif // QF_ON_CONTEXT_SW + +QXK_priv_.prev = next; // update the previous thread + + + + QF_CRIT_STAT +QF_CRIT_ENTRY(); + +QP::QXThread const * const thr = QXTHREAD_CAST_(QXK_priv_.curr); + +Q_REQUIRE_INCRIT(900, (!QXK_ISR_CONTEXT_()) + && (thr != nullptr)); // current thread must be extended +Q_REQUIRE_INCRIT(901, QXK_priv_.lockHolder != thr->getPrio()); + +std::uint_fast8_t const p = + static_cast<std::uint_fast8_t>(thr->getPrio()); + +QF_MEM_SYS(); +QP::QActive::registry_[p] = nullptr; +QXK_priv_.readySet.remove(p); +#ifndef Q_UNSAFE +QXK_priv_.readySet.update_(&QXK_priv_.readySet_dis); +#endif + +static_cast<void>(QXK_sched_()); // schedule other threads + +QF_MEM_APP(); +QF_CRIT_EXIT(); + + + + + + + QSchedStatus lockStat_; + + + + + + do { \ + if (QXK_ISR_CONTEXT_()) { \ + lockStat_ = 0xFFU; \ + } else { \ + lockStat_ = QXK::schedLock((ceil_)); \ + } \ +} while (false) + + + + do { \ + if (lockStat_ != 0xFFU) { \ + QXK::schedUnlock(lockStat_); \ + } \ +} while (false) + + + + // QXK native event queue waiting + + + \ + Q_ASSERT_INCRIT(310, (me_)->m_eQueue.m_frontEvt != nullptr) + + + + // QXK native event queue signalling + + + do { \ + QXK_priv_.readySet.insert( \ + static_cast<std::uint_fast8_t>((me_)->m_prio)); \ + QXK_priv_.readySet.update_(&QXK_priv_.readySet_dis); \ + if (!QXK_ISR_CONTEXT_()) { \ + if (QXK_sched_() != 0U) { \ + QXK_activate_(); \ + } \ + } \ +} while (false) + + + + // QXK native event queue signalling + + + do { \ + QXK_priv_.readySet.insert( \ + static_cast<std::uint_fast8_t>((me_)->m_prio)); \ + if (!QXK_ISR_CONTEXT_()) { \ + if (QXK_sched_() != 0U) { \ + QXK_activate_(); \ + } \ + } \ +} while (false) + + + + + + do { \ + if ((me_)->m_temp.obj == QXK_PTR_CAST_(QMState*, &(me_)->m_eQueue)) { \ + static_cast<void>(QXTHREAD_CAST_(me_)->teDisarm_()); \ + QXK_priv_.readySet.insert( \ + static_cast<std::uint_fast8_t>((me_)->m_prio)); \ + QXK_priv_.readySet.update_(&QXK_priv_.readySet_dis); \ + if (!QXK_ISR_CONTEXT_()) { \ + static_cast<void>(QXK_sched_()); \ + } \ + } \ +} while (false) + + + + + + do { \ + if ((me_)->m_temp.obj == QXK_PTR_CAST_(QMState*, &(me_)->m_eQueue)) { \ + static_cast<void>(QXTHREAD_CAST_(me_)->teDisarm_()); \ + QXK_priv_.readySet.insert( \ + static_cast<std::uint_fast8_t>((me_)->m_prio)); \ + if (!QXK_ISR_CONTEXT_()) { \ + static_cast<void>(QXK_sched_()); \ + } \ + } \ +} while (false) + + + + + + (static_cast<QP::QXThread *>(ptr_)) + + + + <type_> + + + + + (reinterpret_cast<type_>(ptr_)) + + + + + + + + + //! pre-defined QS record IDs + : std::int8_t { + // [0] QS session (not maskable) + QS_EMPTY, //!< QS record for cleanly starting a session + + // [1] SM records + QS_QEP_STATE_ENTRY, //!< a state was entered + QS_QEP_STATE_EXIT, //!< a state was exited + QS_QEP_STATE_INIT, //!< an initial transition was taken in a state + QS_QEP_INIT_TRAN, //!< the top-most initial transition was taken + QS_QEP_INTERN_TRAN, //!< an internal transition was taken + QS_QEP_TRAN, //!< a regular transition was taken + QS_QEP_IGNORED, //!< an event was ignored (silently discarded) + QS_QEP_DISPATCH, //!< an event was dispatched (begin of RTC step) + QS_QEP_UNHANDLED, //!< an event was un-handled due to a guard + + // [10] Active Object (AO) records + QS_QF_ACTIVE_DEFER, //!< AO deferred an event + QS_QF_ACTIVE_RECALL, //!< AO recalled an event + QS_QF_ACTIVE_SUBSCRIBE, //!< an AO subscribed to an event + QS_QF_ACTIVE_UNSUBSCRIBE, //!< an AO unsubscribed to an event + QS_QF_ACTIVE_POST, //!< an event was posted (FIFO) directly to AO + QS_QF_ACTIVE_POST_LIFO, //!< an event was posted (LIFO) directly to AO + QS_QF_ACTIVE_GET, //!< AO got an event and its queue is not empty + QS_QF_ACTIVE_GET_LAST,//!< AO got an event and its queue is empty + QS_QF_ACTIVE_RECALL_ATTEMPT, //!< AO attempted to recall an event + + // [19] Event Queue (EQ) records + QS_QF_EQUEUE_POST, //!< an event was posted (FIFO) to a raw queue + QS_QF_EQUEUE_POST_LIFO, //!< an event was posted (LIFO) to a raw queue + QS_QF_EQUEUE_GET, //!< get an event and queue still not empty + QS_QF_EQUEUE_GET_LAST,//!< get the last event from the queue + + // [23] Framework (QF) records + QS_QF_NEW_ATTEMPT, //!< an attempt to allocate an event failed + + // [24] Memory Pool (MP) records + QS_QF_MPOOL_GET, //!< a memory block was removed from memory pool + QS_QF_MPOOL_PUT, //!< a memory block was returned to memory pool + + // [26] Additional Framework (QF) records + QS_QF_PUBLISH, //!< an event was published to active objects + QS_QF_NEW_REF, //!< new event reference was created + QS_QF_NEW, //!< new event was created + QS_QF_GC_ATTEMPT, //!< garbage collection attempt + QS_QF_GC, //!< garbage collection + QS_QF_TICK, //!< QTimeEvt tick was called + + // [32] Time Event (TE) records + QS_QF_TIMEEVT_ARM, //!< a time event was armed + QS_QF_TIMEEVT_AUTO_DISARM, //!< a time event expired and was disarmed + QS_QF_TIMEEVT_DISARM_ATTEMPT,//!< attempt to disarm a disarmed QTimeEvt + QS_QF_TIMEEVT_DISARM, //!< true disarming of an armed time event + QS_QF_TIMEEVT_REARM, //!< rearming of a time event + QS_QF_TIMEEVT_POST, //!< a time event posted itself directly to an AO + + // [38] Additional Framework (QF) records + QS_QF_DELETE_REF, //!< an event reference is about to be deleted + QS_QF_CRIT_ENTRY, //!< critical section was entered + QS_QF_CRIT_EXIT, //!< critical section was exited + QS_QF_ISR_ENTRY, //!< an ISR was entered + QS_QF_ISR_EXIT, //!< an ISR was exited + QS_QF_INT_DISABLE, //!< interrupts were disabled + QS_QF_INT_ENABLE, //!< interrupts were enabled + + // [45] Additional Active Object (AO) records + QS_QF_ACTIVE_POST_ATTEMPT,//!< attempt to post an evt to AO failed + + // [46] Additional Event Queue (EQ) records + QS_QF_EQUEUE_POST_ATTEMPT,//!< attempt to post evt to QEQueue failed + + // [47] Additional Memory Pool (MP) records + QS_QF_MPOOL_GET_ATTEMPT, //!< attempt to get a memory block failed + + // [48] Scheduler (SC) records + QS_SCHED_PREEMPT, //!< scheduler asynchronously preempted a task + QS_SCHED_RESTORE, //!< scheduler restored preempted task + QS_SCHED_LOCK, //!< scheduler was locked + QS_SCHED_UNLOCK, //!< scheduler was unlocked + QS_SCHED_NEXT, //!< scheduler started next task + QS_SCHED_IDLE, //!< scheduler restored the idle task + + // [54] Miscellaneous QS records (not maskable) + QS_ENUM_DICT, //!< enumeration dictionary entry + + // [55] Additional QEP records + QS_QEP_TRAN_HIST, //!< a tran to history was taken + QS_QEP_TRAN_EP, //!< a tran to entry point into a submachine + QS_QEP_TRAN_XP, //!< a tran to exit point out of a submachine + + // [58] Miscellaneous QS records (not maskable) + QS_TEST_PAUSED, //!< test has been paused + QS_TEST_PROBE_GET, //!< reports that Test-Probe has been used + QS_SIG_DICT, //!< signal dictionary entry + QS_OBJ_DICT, //!< object dictionary entry + QS_FUN_DICT, //!< function dictionary entry + QS_USR_DICT, //!< user QS record dictionary entry + QS_TARGET_INFO, //!< reports the Target information + QS_TARGET_DONE, //!< reports completion of a user callback + QS_RX_STATUS, //!< reports QS data receive status + QS_QUERY_DATA, //!< reports the data from "current object" query + QS_PEEK_DATA, //!< reports the data from the PEEK query + QS_ASSERT_FAIL, //!< assertion failed in the code + QS_QF_RUN, //!< QF_run() was entered + + // [71] Semaphore (SEM) records + QS_SEM_TAKE, //!< a semaphore was taken by a thread + QS_SEM_BLOCK, //!< a semaphore blocked a thread + QS_SEM_SIGNAL, //!< a semaphore was signaled + QS_SEM_BLOCK_ATTEMPT, //!< a semaphore blocked was attempted + + // [75] Mutex (MTX) records + QS_MTX_LOCK, //!< a mutex was locked + QS_MTX_BLOCK, //!< a mutex blocked a thread + QS_MTX_UNLOCK, //!< a mutex was unlocked + QS_MTX_LOCK_ATTEMPT, //!< a mutex lock was attempted + QS_MTX_BLOCK_ATTEMPT, //!< a mutex blocking was attempted + QS_MTX_UNLOCK_ATTEMPT,//!< a mutex unlock was attempted + + // [81] + QS_PRE_MAX //!< the # predefined signals +}; + + + + //! QS-TX record groups for QS_GLB_FILTER() + : std::int16_t { + QS_ALL_RECORDS = 0xF0,//!< all maskable QS records + QS_SM_RECORDS, //!< State Machine QS records + QS_AO_RECORDS, //!< Active Object QS records + QS_EQ_RECORDS, //!< Event Queues QS records + QS_MP_RECORDS, //!< Memory Pools QS records + QS_TE_RECORDS, //!< Time Events QS records + QS_QF_RECORDS, //!< QF QS records + QS_SC_RECORDS, //!< Scheduler QS records + QS_SEM_RECORDS, //!< Semaphore QS records + QS_MTX_RECORDS, //!< Mutex QS records + QS_U0_RECORDS, //!< User Group 100-104 records + QS_U1_RECORDS, //!< User Group 105-109 records + QS_U2_RECORDS, //!< User Group 110-114 records + QS_U3_RECORDS, //!< User Group 115-119 records + QS_U4_RECORDS, //!< User Group 120-124 records + QS_UA_RECORDS //!< All User records +}; + + + + //! QS user record group offsets for QS_GLB_FILTER() + : std::int16_t { + QS_USER = 100, //!< the first record available to QS users + QS_USER0 = QS_USER, //!< offset for User Group 0 + QS_USER1 = QS_USER0 + 5, //!< offset for User Group 1 + QS_USER2 = QS_USER1 + 5, //!< offset for User Group 2 + QS_USER3 = QS_USER2 + 5, //!< offset for User Group 3 + QS_USER4 = QS_USER3 + 5 //!< offset for User Group 4 +}; + + + + //! QS ID offsets for QS_LOC_FILTER() + : std::int16_t { + QS_AO_ID = 0, //!< offset for AO priorities + QS_EP_ID = 64, //!< offset for event-pool IDs + QS_EQ_ID = 80, //!< offset for event-queue IDs + QS_AP_ID = 96 //!< offset for Application-specific IDs +}; + + + + //! QS ID groups for QS_LOC_FILTER() + : std::int16_t { + QS_ALL_IDS = 0xF0, //!< all QS IDs + QS_AO_IDS = 0x80 + QS_AO_ID, //!< AO IDs (priorities) + QS_EP_IDS = 0x80 + QS_EP_ID, //!< event-pool IDs + QS_EQ_IDS = 0x80 + QS_EQ_ID, //!< event-queue IDs + QS_AP_IDS = 0x80 + QS_AP_ID //!< Application-specific IDs +}; + + + + { + std::uint8_t m_prio; //!< prio. (qs_id) for the QS "local filter" + + // get the prio. (qs_id) from the QSpyId opbject + std::uint_fast8_t getPrio() const noexcept { + return static_cast<std::uint_fast8_t>(m_prio); + } +}; + + + + = void (*)(); + + + + = std::uint16_t; + + + + = std::uint32_t; + + + + = std::uint16_t; + + + + = std::uint32_t; + + + + = std::uint16_t; + + + + = std::uint32_t; + + + + = std::uint64_t; + + + + + + + + + + + + + + + + + + + //! Enumerates data elements for app-specific trace records + : std::uint8_t { + I8_ENUM_T, //!< signed 8-bit integer or enum format + U8_T, //!< unsigned 8-bit integer format + I16_T, //!< signed 16-bit integer format + U16_T, //!< unsigned 16-bit integer format + I32_T, //!< signed 32-bit integer format + U32_T, //!< unsigned 32-bit integer format + F32_T, //!< 32-bit floating point format + F64_T, //!< 64-bit floating point format + STR_T, //!< zero-terminated ASCII string format + MEM_T, //!< up to 255-bytes memory block format + SIG_T, //!< event signal format + OBJ_T, //!< object pointer format + FUN_T, //!< function pointer format + I64_T, //!< signed 64-bit integer format + U64_T //!< unsigned 64-bit integer format +}; + + + + + + union TCast { + T_IN in; + T_OUT out; +} u = { in }; +return u.out; + + + + noexcept + + + + + priv_.buf = sto; +priv_.end = static_cast<QSCtr>(stoSize); +priv_.head = 0U; +priv_.tail = 0U; +priv_.used = 0U; +priv_.seq = 0U; +priv_.chksum = 0U; +priv_.critNest = 0U; + +glbFilter_(-static_cast<enum_t>(QS_ALL_RECORDS));// all global filters OFF +locFilter_(static_cast<enum_t>(QS_ALL_IDS)); // all local filters ON +priv_.locFilter_AP = nullptr; // deprecated "AP-filter" + +// produce an empty record to "flush" the QS trace buffer +beginRec_(QS_REC_NUM_(QS_EMPTY)); +endRec_(); + +// produce the reset record to inform QSPY of a new session +target_info_pre_(0xFFU); + +// hold off flushing after successfull initialization (see QS_INIT()) + + + + noexcept + // NOTE: Must be called IN critical section. +// Also requires system-level memory access (QF_MEM_SYS()). + +std::uint16_t ret; +if (priv_.used == 0U) { + ret = QS_EOD; // set End-Of-Data +} +else { + std::uint8_t const * const buf = priv_.buf; // put in a temporary + QSCtr tail = priv_.tail; // put in a temporary (register) + ret = static_cast<std::uint16_t>(buf[tail]); // set the byte to return + ++tail; // advance the tail + if (tail == priv_.end) { // tail wrap around? + tail = 0U; + } + priv_.tail = tail; // update the tail + priv_.used = (priv_.used - 1U); // one less byte used +} +return ret; // return the byte or EOD + + + + noexcept + + + // NOTE: Must be called IN critical section. +// Also requires system-level memory access (QF_MEM_SYS()). + +QSCtr const used = priv_.used; // put in a temporary (register) +std::uint8_t *buf; + +// any bytes used in the ring buffer? +if (used != 0U) { + QSCtr tail = priv_.tail; // put in a temporary (register) + QSCtr const end = priv_.end; // put in a temporary (register) + QSCtr n = static_cast<QSCtr>(end - tail); + if (n > used) { + n = used; + } + if (n > static_cast<QSCtr>(*pNbytes)) { + n = static_cast<QSCtr>(*pNbytes); + } + *pNbytes = static_cast<std::uint16_t>(n); // n-bytes available + buf = priv_.buf; + buf = &buf[tail]; // the bytes are at the tail + + priv_.used = static_cast<QSCtr>(used - n); + tail += n; + if (tail == end) { + tail = 0U; + } + priv_.tail = tail; +} +else { // no bytes available + *pNbytes = 0U; // no bytes available right now + buf = nullptr; // no bytes available right now +} +return buf; + + + + + + + + + + + + + + + + + + + + //! Kinds of objects used QS-RX + : std::uint8_t { + SM_OBJ, //!< state machine object + AO_OBJ, //!< active object + MP_OBJ, //!< event pool object + EQ_OBJ, //!< raw queue object + TE_OBJ, //!< time event object + AP_OBJ, //!< generic Application-specific object + MAX_OBJ +}; + + + + //! Object combinations for QS-RX + : std::uint8_t { + SM_AO_OBJ = MAX_OBJ //!< combination of SM and AO +}; + + + + noexcept + + + + + rxPriv_.buf = &sto[0]; +rxPriv_.end = static_cast<QSCtr>(stoSize); +rxPriv_.head = 0U; +rxPriv_.tail = 0U; + +rxPriv_.currObj[QS::SM_OBJ] = nullptr; +rxPriv_.currObj[QS::AO_OBJ] = nullptr; +rxPriv_.currObj[QS::MP_OBJ] = nullptr; +rxPriv_.currObj[QS::EQ_OBJ] = nullptr; +rxPriv_.currObj[QS::TE_OBJ] = nullptr; +rxPriv_.currObj[QS::AP_OBJ] = nullptr; + +tran_(WAIT4_SEQ); +l_rx.esc = 0U; +l_rx.seq = 0U; +l_rx.chksum = 0U; + +beginRec_(static_cast<std::uint_fast8_t>(QS_OBJ_DICT)); + QS_OBJ_PRE_(&rxPriv_); + QS_STR_PRE_("QS_RX"); +endRec_(); +// no QS_REC_DONE(), because QS is not running yet + +#ifdef Q_UTEST +tstPriv_.tpNum = 0U; +tstPriv_.testTime = 0U; +#endif // Q_UTEST + + + + noexcept + + + // NOTE: does not need critical section +// But requires system-level memory access (QF_MEM_SYS()). + +QSCtr head = rxPriv_.head + 1U; +if (head == rxPriv_.end) { + head = 0U; +} +if (head != rxPriv_.tail) { // buffer NOT full? + rxPriv_.buf[rxPriv_.head] = b; + rxPriv_.head = head; + return true; // byte placed in the buffer +} +else { + return false; // byte NOT placed in the buffer +} + + + + // NOTE: Must be called IN critical section. +// Also requires system-level memory access (QF_MEM_SYS()). + +QSCtr tail = rxPriv_.tail; +while (rxPriv_.head != tail) { // QS-RX buffer NOT empty? + std::uint8_t b = rxPriv_.buf[tail]; + + ++tail; + if (tail == rxPriv_.end) { + tail = 0U; + } + rxPriv_.tail = tail; // update the tail to a *valid* index + + if (l_rx.esc != 0U) { // escaped byte arrived? + l_rx.esc = 0U; + b ^= QS_ESC_XOR; + + l_rx.chksum += b; + 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; + tran_(WAIT4_SEQ); + + if (l_rx.chksum == QS_GOOD_CHKSUM) { + l_rx.chksum = 0U; + rxHandleGoodFrame_(b); + } + else { // bad checksum + l_rx.chksum = 0U; + rxReportError_(0x41U); + rxHandleBadFrame_(b); + } + } + else { + l_rx.chksum += b; + rxParseData_(b); + } +} + + + + noexcept + // NOTE: Must be called IN critical section. +// Also requires system-level memory access (QF_MEM_SYS()). + +QSCtr const head = rxPriv_.head; +std::uint16_t nFree; +if (head == rxPriv_.tail) { // buffer empty? + nFree = static_cast<std::uint16_t>(rxPriv_.end - 1U); +} +else if (head < rxPriv_.tail) { + nFree = static_cast<std::uint16_t>(rxPriv_.tail - head - 1U); +} +else { + nFree = static_cast<std::uint16_t>(rxPriv_.end + rxPriv_.tail + - head - 1U); +} +return nFree; + + + + + + + + + + + + + + + + + + + + + + { + QSFun addr; + std::uint32_t data; + std::uint8_t idx; +}; + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + // Clear the internal QF variables, so that the framework can start +// correctly even if the startup code fails to clear the uninitialized +// data (as is required by the C++ Standard). +bzero_(&QF::priv_, sizeof(QF::priv_)); +bzero_(&QS::tstPriv_, sizeof(QS::tstPriv_)); +bzero_(&QActive::registry_[0], sizeof(QActive::registry_)); + +#ifndef Q_UNSAFE +QS::tstPriv_.readySet.update_(&QS::tstPriv_.readySet_dis); +#endif + + + + QS::onReset(); + + + + QS_CRIT_STAT +QS_CRIT_ENTRY(); +QS_MEM_SYS(); + +// function dictionaries for the standard API +QS_FUN_DICTIONARY(&QActive::post_); +QS_FUN_DICTIONARY(&QActive::postLIFO); +QS_FUN_DICTIONARY(&QS::processTestEvts_); + +// produce the QS_QF_RUN trace record +QS_BEGIN_PRE_(QS_QF_RUN, 0U) +QS_END_PRE_() + +QS_MEM_APP(); +QS_CRIT_EXIT(); + +QS::processTestEvts_(); // process all events posted so far +QS::onTestLoop(); // run the unit test +QS::onCleanup(); // application cleanup +return 0; // return no error + + + + + + + + + + + + + + + + + + + Q_UNUSED_PAR(stkSto); +Q_UNUSED_PAR(stkSize); + +m_prio = static_cast<std::uint8_t>(prioSpec & 0xFFU); // QF-priol +m_pthre = static_cast<std::uint8_t>(prioSpec >> 8U); // preemption-thre. +register_(); // make QF aware of this AO + +m_eQueue.init(qSto, qLen); // initialize QEQueue of this AO + +this->init(par, m_prio); // take the top-most initial tran. (virtual) + + + + unsubscribeAll(); // unsubscribe from all events +unregister_(); // remove this object from QF + + + + + + + + + + + QS_CRIT_STAT +QS_CRIT_ENTRY(); +QS_MEM_SYS(); + +QTimeEvt *prev = &QTimeEvt::timeEvtHead_[tickRate]; + +QS_BEGIN_PRE_(QS_QF_TICK, 0U) + prev->m_ctr = (prev->m_ctr + 1U); + QS_TEC_PRE_(prev->m_ctr); // tick ctr + QS_U8_PRE_(tickRate); // tick rate +QS_END_PRE_() + +// is current Time Event object provided? +QTimeEvt *t = static_cast<QTimeEvt *>(QS::rxPriv_.currObj[QS::TE_OBJ]); +if (t != nullptr) { + + // the time event must be armed + Q_ASSERT_INCRIT(810, t->m_ctr != 0U); + + QActive * const act = static_cast<QActive *>(t->m_act); + + // the recipient AO must be provided + Q_ASSERT_INCRIT(820, act != nullptr); + + // periodic time evt? + if (t->m_interval != 0U) { + t->m_ctr = t->m_interval; // rearm the time event + } + else { // one-shot time event: automatically disarm + t->m_ctr = 0U; // auto-disarm + // mark time event 't' as NOT linked + t->refCtr_ = static_cast<std::uint8_t>(t->refCtr_ + & static_cast<std::uint8_t>(~TE_IS_LINKED)); + + QS_BEGIN_PRE_(QS_QF_TIMEEVT_AUTO_DISARM, act->m_prio) + QS_OBJ_PRE_(t); // this time event object + QS_OBJ_PRE_(act); // the target AO + QS_U8_PRE_(tickRate); // tick rate + QS_END_PRE_() + } + + QS_BEGIN_PRE_(QS_QF_TIMEEVT_POST, act->m_prio) + QS_TIME_PRE_(); // timestamp + QS_OBJ_PRE_(t); // the time event object + QS_SIG_PRE_(t->sig); // signal of this time event + QS_OBJ_PRE_(act); // the target AO + QS_U8_PRE_(tickRate); // tick rate + QS_END_PRE_() + QS_MEM_APP(); + QS_CRIT_EXIT(); // exit critical section before posting + + // asserts if queue overflows + static_cast<void>(act->POST(t, sender)); + + QS_CRIT_ENTRY(); + QS_MEM_SYS(); +} + +// update the linked list of time events +for (;;) { + t = prev->m_next; // advance down the time evt. list + + // end of the list? + if (t == nullptr) { + + // any new time events armed since the last run of tick()? + if (QTimeEvt::timeEvtHead_[tickRate].m_act != nullptr) { + + // sanity check + Q_ASSERT_INCRIT(830, prev != nullptr); + prev->m_next = QTimeEvt::timeEvtHead_[tickRate].toTimeEvt(); + QTimeEvt::timeEvtHead_[tickRate].m_act = nullptr; + t = prev->m_next; // switch to the new list + } + else { + break; // all currently armed time evts. processed + } + } + + // time event scheduled for removal? + if (t->m_ctr == 0U) { + prev->m_next = t->m_next; + // mark time event 't' as NOT linked + t->refCtr_ = static_cast<std::uint8_t>(t->refCtr_ + & static_cast<std::uint8_t>(~TE_IS_LINKED)); + // do NOT advance the prev pointer + QS_MEM_APP(); + QS_CRIT_EXIT(); // exit crit. section to reduce latency + } + else { + prev = t; // advance to this time event + QS_MEM_APP(); + QS_CRIT_EXIT(); // exit crit. section to reduce latency + } + QS_CRIT_ENTRY(); // re-enter crit. section to continue + QS_MEM_SYS(); +} + +QS_MEM_APP(); +QS_CRIT_EXIT(); + + + + + + + : QAsm() + + + + override + + + + + Q_UNUSED_PAR(e); + +#ifdef Q_SPY +if ((QS::priv_.flags & 0x01U) == 0U) { + QS::priv_.flags |= 0x01U; + QS_FUN_DICTIONARY(&QP::QHsm::top); +} +#else +Q_UNUSED_PAR(qs_id); +#endif + +QS_CRIT_STAT +QS_CRIT_ENTRY(); +QS_MEM_SYS(); +QS_BEGIN_PRE_(QS_QEP_STATE_INIT, qs_id) + QS_OBJ_PRE_(this); // this state machine object + QS_FUN_PRE_(m_state.fun); // the source state + QS_FUN_PRE_(m_temp.fun); // the target of the initial tran. +QS_END_PRE_() +QS_MEM_APP(); +QS_CRIT_EXIT(); + + + + override + + + + + QS_CRIT_STAT +QS_CRIT_ENTRY(); +QS_MEM_SYS(); +QS_BEGIN_PRE_(QS_QEP_DISPATCH, qs_id) + QS_TIME_PRE_(); // time stamp + QS_SIG_PRE_(e->sig); // the signal of the event + QS_OBJ_PRE_(this); // this state machine object + QS_FUN_PRE_(m_state.fun); // the current state +QS_END_PRE_() +QS_MEM_APP(); +QS_CRIT_EXIT(); + + + + + + + + + : QActive(nullptr) + + + + override + + + + + Q_UNUSED_PAR(e); +Q_UNUSED_PAR(qs_id); + +#ifdef Q_SPY +if ((QS::priv_.flags & 0x01U) == 0U) { + QS::priv_.flags |= 0x01U; + QS_FUN_DICTIONARY(&QP::QHsm::top); +} +#endif + +QS_CRIT_STAT +QS_CRIT_ENTRY(); +QS_MEM_SYS(); +QS_BEGIN_PRE_(QS_QEP_STATE_INIT, m_prio) + QS_OBJ_PRE_(this); // this state machine object + QS_FUN_PRE_(m_state.fun); // the source state + QS_FUN_PRE_(m_temp.fun); // the target of the initial tran. +QS_END_PRE_() +QS_MEM_APP(); +QS_CRIT_EXIT(); + + + + override + + + + + QS_CRIT_STAT +QS_CRIT_ENTRY(); +QS_MEM_SYS(); +QS_BEGIN_PRE_(QS_QEP_DISPATCH, qs_id) + QS_TIME_PRE_(); // time stamp + QS_SIG_PRE_(e->sig); // the signal of the event + QS_OBJ_PRE_(this); // this state machine object + QS_FUN_PRE_(m_state.fun); // the current state +QS_END_PRE_() +QS_MEM_APP(); +QS_CRIT_EXIT(); + + + + noexcept + + + + + + + QS_TEST_PROBE_DEF(&QActive::post_) + +QF_CRIT_STAT +QF_CRIT_ENTRY(); +QF_MEM_SYS(); + +// test-probe#1 for faking queue overflow +bool status = true; +QS_TEST_PROBE_ID(1, + status = false; + if (margin == QF::NO_MARGIN) { + // fake assertion Mod=qf_actq,Loc=110 + Q_onError("qf_actq", 110); + } +) + +// is it a mutable event? +if (e->getPoolId_() != 0U) { + QEvt_refCtr_inc_(e); +} + +std::uint_fast8_t const rec = + (status ? static_cast<std::uint8_t>(QS_QF_ACTIVE_POST) + : static_cast<std::uint8_t>(QS_QF_ACTIVE_POST_ATTEMPT)); +QS_BEGIN_PRE_(rec, m_prio) + QS_TIME_PRE_(); // timestamp + QS_OBJ_PRE_(sender); // the sender object + QS_SIG_PRE_(e->sig); // the signal of the event + QS_OBJ_PRE_(this); // this active object + QS_2U8_PRE_(e->getPoolId_(), 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_(m_prio)) { + QS::onTestPost(sender, this, e, status); +} +QF_MEM_APP(); +QF_CRIT_EXIT(); + +// recycle the event immediately, because it was not really posted +#if (QF_MAX_EPOOL > 0U) +QF::gc(e); +#endif + +return status; + + + + noexcept + + + QS_TEST_PROBE_DEF(&QActive::postLIFO) + +QF_CRIT_STAT +QF_CRIT_ENTRY(); +QF_MEM_SYS(); + +// test-probe#1 for faking queue overflow +QS_TEST_PROBE_ID(1, + // fake assertion Mod=qf_actq,Loc=210 + Q_onError("qf_actq", 210); +) + +// is it a mutable event? +if (e->getPoolId_() != 0U) { + QEvt_refCtr_inc_(e); +} + +QS_BEGIN_PRE_(QS_QF_ACTIVE_POST_LIFO, m_prio) + QS_TIME_PRE_(); // timestamp + QS_SIG_PRE_(e->sig); // the signal of this event + QS_OBJ_PRE_(this); // this active object + QS_2U8_PRE_(e->getPoolId_(), 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_(m_prio)) { + QS::onTestPost(nullptr, this, e, true); +} +QF_MEM_APP(); +QF_CRIT_EXIT(); + +// recycle the event immediately, because it was not really posted +#if (QF_MAX_EPOOL > 0U) +QF::gc(e); +#endif + + + + + + + + + + + (QP::QS::onStartup(arg_)) + + + + (QP::QS::onCleanup()) + + + + (QP::QS::doOutput()) + + + + (QP::QS::doInput()) + + + + + + \ + (QP::QS::glbFilter_(static_cast<std::int_fast16_t>(rec_))) + + + + + + \ + (QP::QS::locFilter_(static_cast<std::int_fast16_t>(qs_id_))) + + + + + + + + \ +if (QS_GLB_CHECK_(rec_) && QS_LOC_CHECK_(qs_id_)) { \ + QS_CRIT_STAT \ + QS_CRIT_ENTRY(); \ + QS_MEM_SYS(); \ + QP::QS::beginRec_(static_cast<std::uint_fast8_t>(rec_)); \ + QS_TIME_PRE_(); { + + + + } \ + QP::QS::endRec_(); \ + QS_MEM_APP(); \ + QS_CRIT_EXIT(); \ +} + + + + (QP::QS::onFlush()) + + + + + + + + \ +if (QS_GLB_CHECK_(rec_) && QS_LOC_CHECK_(qs_id_)) { \ + QP::QS::beginRec_(rec_); \ + QS_TIME_PRE_(); { + + + + } \ + QP::QS::endRec_(); \ +} + + + + + + \ +((static_cast<std::uint_fast8_t>(QP::QS::filt_.glb[ \ + static_cast<std::uint_fast8_t>(rec_) >> 3U]) \ + & (static_cast<std::uint_fast8_t>(1U) \ + << (static_cast<std::uint_fast8_t>(rec_) & 7U))) != 0U) + + + + + + \ +((static_cast<std::uint_fast8_t>(QP::QS::filt_.loc \ + [static_cast<std::uint_fast8_t>(qs_id_) >> 3U]) \ + & (static_cast<std::uint_fast8_t>(1U) \ + << (static_cast<std::uint_fast8_t>(qs_id_) & 7U))) != 0U) + + + + (static_cast<void>(0)) + + + + + + + + \ +(QP::QS::u8_fmt_(static_cast<std::uint8_t>( \ + (static_cast<std::uint8_t>(((width_) << 4U) & 0x7U)) \ + | static_cast<std::uint8_t>(QP::QS::I8_ENUM_T)), (data_))) + + + + + + + + \ +(QP::QS::u8_fmt_(static_cast<std::uint8_t>( \ + (static_cast<std::uint8_t>((width_) << 4U)) \ + | static_cast<std::uint8_t>(QP::QS::U8_T)), (data_))) + + + + + + + + \ +(QP::QS::u16_fmt_(static_cast<std::uint8_t>( \ + (static_cast<std::uint8_t>((width_) << 4U)) \ + | static_cast<std::uint8_t>(QP::QS::I16_T)), (data_))) + + + + + + + + \ +(QP::QS::u16_fmt_(static_cast<std::uint8_t>((((width_) << 4U)) \ + | static_cast<std::uint8_t>(QP::QS::U16_T)), (data_))) + + + + + + + + \ +(QP::QS::u32_fmt_( \ + static_cast<std::uint8_t>((static_cast<std::uint8_t>((width_) << 4U)) \ + | static_cast<std::uint8_t>(QP::QS::I32_T)), (data_))) + + + + + + + + \ +(QP::QS::u32_fmt_(static_cast<std::uint8_t>( \ + (static_cast<std::uint8_t>((width_) << 4U)) \ + | static_cast<std::uint8_t>(QP::QS::U32_T)), (data_))) + + + + + + + + \ +(QP::QS::u64_fmt_(static_cast<std::uint8_t>( \ + (static_cast<std::uint8_t>((width_) << 4U)) \ + | static_cast<std::uint8_t>(QP::QS::I64_T)), (data_))) + + + + + + + + \ +(QP::QS::u64_fmt_(static_cast<std::uint8_t>( \ + (static_cast<std::uint8_t>((width_) << 4U)) \ + | static_cast<std::uint8_t>(QP::QS::U64_T)), (data_))) + + + + + + + + \ +(QP::QS::f32_fmt_(static_cast<std::uint8_t>( \ + (static_cast<std::uint8_t>((width_) << 4U)) \ + | static_cast<std::uint8_t>(QP::QS::F32_T)), (data_))) + + + + + + + + \ +(QP::QS::f64_fmt_(static_cast<std::uint8_t>( \ + (static_cast<std::uint8_t>((width_) << 4U)) \ + | static_cast<std::uint8_t>(QP::QS::F64_T)), (data_))) + + + + + + (QP::QS::str_fmt_(str_)) + + + + + + + + (QP::QS::mem_fmt_((mem_), (size_))) + + + + + + + + \ + (QP::QS::u8_fmt_(static_cast<std::uint8_t>(0x80U | ((group_) << 4U)) \ + | static_cast<std::uint8_t>(QP::QS::I8_ENUM_T),\ + static_cast<std::uint8_t>(value_))) + + + + (QP::QS::u16_raw_(QP::QS::onGetTime())) + + + + (QP::QS::u32_raw_(QP::QS::onGetTime())) + + + + + + (QP::QS::u16_fmt_(QP::QS::OBJ_T, \ + reinterpret_cast<std::uint16_t>(obj_))) + + + + + + (QP::QS::u32_fmt_(QP::QS::OBJ_T, \ + reinterpret_cast<std::uint32_t>(obj_))) + + + + + + (QP::QS::u64_fmt_(QP::QS::OBJ_T, \ + reinterpret_cast<std::uint64_t>(obj_))) + + + + + + (QP::QS::u16_fmt_(QP::QS::FUN_T, \ + reinterpret_cast<std::uint16_t>(fun_))) + + + + + + (QP::QS::u32_fmt_(QP::QS::FUN_T, \ + reinterpret_cast<std::uint32_t>(fun_))) + + + + + + (QP::QS::u64_fmt_(QP::QS::FUN_T, \ + reinterpret_cast<std::uint64_t>(fun_))) + + + + + + + + \ + QP::QS::u8_fmt_(QP::QS::SIG_T, static_cast<std::uint8_t>(sig_)); \ + QP::QS::obj_raw_(obj_) + + + + + + + + \ + QP::QS::u16_fmt_(QP::QS::SIG_T, static_cast<std::uint16_t>(sig_)); \ + QP::QS::obj_raw_(obj_) + + + + + + + + \ + QP::QS::u32_fmt_(QP::QS::SIG_T, static_cast<std::uint32_t>(sig_)); \ + QP::QS::obj_raw_(obj_) + + + + + + + + \ + (QP::QS::sig_dict_pre_((sig_), (obj_), #sig_)) + + + + + + \ + (QP::QS::obj_dict_pre_((obj_), #obj_)) + + + + + + + + \ + (QP::QS::obj_arr_dict_pre_((obj_), (idx_), #obj_)) + + + + + + \ + (QP::QS::fun_dict_pre_( \ + QP::QS::force_cast<void (*)()>(fun_), #fun_)) + + + + + + \ + (QP::QS::usr_dict_pre_((rec_), #rec_)) + + + + + + + + \ + (QP::QS::enum_dict_pre_((value_), (group_), #value_)) + + + + (QP::QS::crit_entry_pre_()) + + + + (QP::QS::crit_exit_pre_()) + + + + + + + + \ + (QP::QS::isr_entry_pre_((isrnest_), (prio_))) + + + + + + + + \ + (QP::QS::isr_exit_pre_((isrnest_), (prio_))) + + + + + + (code_) + + + + + + + + + + \ + (QP::QS::assertion_pre_((module_), (id_), (delay_))) + + + + (static_cast<std::uint16_t>(0xFFFFU)) + + + + (static_cast<std::uint8_t>(7U)) + + + + (static_cast<std::uint8_t>(0x0FU)) + + + + QF_CRIT_STAT + + + + QF_CRIT_ENTRY() + + + + QF_CRIT_EXIT() + + + + QF_MEM_SYS() + + + + QF_MEM_APP() + + + + + + + #ifndef QP_HPP_ +#define QP_HPP_ + +//============================================================================ +#define QP_VERSION 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 // ndef Q_SIGNAL_SIZE + +#ifndef QF_MAX_ACTIVE +#define QF_MAX_ACTIVE 32U +#endif + +#if (QF_MAX_ACTIVE > 64U) +#error QF_MAX_ACTIVE exceeds the maximum of 64U; +#endif + +#ifndef QF_MAX_TICK_RATE +#define QF_MAX_TICK_RATE 1U +#endif + +#if (QF_MAX_TICK_RATE > 15U) +#error QF_MAX_TICK_RATE exceeds the maximum of 15U; +#endif + +#ifndef QF_MAX_EPOOL +#define QF_MAX_EPOOL 3U +#endif + +#if (QF_MAX_EPOOL > 15U) +#error QF_MAX_EPOOL exceeds the maximum of 15U; +#endif + +#ifndef QF_TIMEEVT_CTR_SIZE +#define QF_TIMEEVT_CTR_SIZE 4U +#endif + +#if (QF_TIMEEVT_CTR_SIZE > 4U) +#error QF_TIMEEVT_CTR_SIZE defined incorrectly, expected 1U, 2U, or 4U; +#endif + +#ifndef QF_EVENT_SIZ_SIZE +#define QF_EVENT_SIZ_SIZE 2U +#endif + +#if (QF_EVENT_SIZ_SIZE > 4U) +#error QF_EVENT_SIZ_SIZE defined incorrectly, expected 1U, 2U, or 4U; +#endif + +//! @endcond +//============================================================================ +#ifdef QEVT_DYN_CTOR +#include <new> // for placement new +#include <cstdarg> // for va_list +#endif // QEVT_DYN_CTOR + +$declare ${glob-types} + +$declare ${QEP} + +$declare ${QEP-macros} + +$declare ${QF::types} + +$declare ${QF::QActive} + +$declare ${QF::QMActive} + +$declare ${QF::QTimeEvt} + +$declare ${QF::QTicker} + +$declare ${QF::QF-base} + +$declare ${QF::QF-dyn} + +extern "C" { +$declare ${QF-extern-C} +} // extern "C" + +$declare ${QF-macros} + +#endif // QP_HPP_ + + + + #ifndef QP_PKG_HPP_ +#define QP_PKG_HPP_ + +$declare ${QF::QF-pkg} + +#define QF_CONST_CAST_(type_, ptr_) const_cast<type_>(ptr_) +#define QF_PTR_RANGE_(x_, min_, max_) (((min_) <= (x_)) && ((x_) <= (max_))) +#define Q_UINTPTR_CAST_(ptr_) (reinterpret_cast<std::uintptr_t>(ptr_)) +#define Q_ACTION_CAST(act_) (reinterpret_cast<QP::QActionHandler>(act_)) + +namespace QP { + +// Bitmasks are for the QTimeEvt::refCtr_ attribute (inherited from QEvt). +// In QTimeEvt this attribute is NOT used for reference counting. +constexpr std::uint8_t TE_IS_LINKED = 1U << 7U; // flag +constexpr std::uint8_t TE_WAS_DISARMED = 1U << 6U; // flag +constexpr std::uint8_t TE_TICK_RATE = 0x0FU; // bitmask + +inline void QEvt_refCtr_inc_(QEvt const * const e) noexcept { + (QF_CONST_CAST_(QEvt*, e))->refCtr_ = e->refCtr_ + 1U; +} + +inline void QEvt_refCtr_dec_(QEvt const * const e) noexcept { + (QF_CONST_CAST_(QEvt*, e))->refCtr_ = e->refCtr_ - 1U; +} + +} // namespace QP + +#endif // QP_PKG_HPP_ + + + + #ifndef QEQUEUE_HPP_ +#define QEQUEUE_HPP_ + +#ifndef QF_EQUEUE_CTR_SIZE + #define QF_EQUEUE_CTR_SIZE 1U +#endif + +namespace QP { + +#if (QF_EQUEUE_CTR_SIZE == 1U) + using QEQueueCtr = std::uint8_t; +#elif (QF_EQUEUE_CTR_SIZE == 2U) + using QEQueueCtr = std::uint16_t; +#elif (QF_EQUEUE_CTR_SIZE == 4U) + using QEQueueCtr = std::uint32_t; +#else + #error "QF_EQUEUE_CTR_SIZE defined incorrectly, expected 1U, 2U, or 4U" +#endif + +class QEvt; // forward declaration + +} // namespace QP + +$declare ${QF::QEQueue} + +#endif // QEQUEUE_HPP_ + + + + #ifndef QMPOOL_HPP_ +#define QMPOOL_HPP_ + +#ifndef QF_MPOOL_SIZ_SIZE + #define QF_MPOOL_SIZ_SIZE 2U +#endif +#ifndef QF_MPOOL_CTR_SIZE + #define QF_MPOOL_CTR_SIZE 2U +#endif + +namespace QP { + +#if (QF_MPOOL_SIZ_SIZE == 1U) + using QMPoolSize = std::uint8_t; +#elif (QF_MPOOL_SIZ_SIZE == 2U) + using QMPoolSize = std::uint16_t; +#elif (QF_MPOOL_SIZ_SIZE == 4U) + using QMPoolSize = std::uint32_t; +#else + #error "QF_MPOOL_SIZ_SIZE defined incorrectly, expected 1U, 2U, or 4U" +#endif + +#if (QF_MPOOL_CTR_SIZE == 1U) + using QMPoolCtr = std::uint8_t; +#elif (QF_MPOOL_CTR_SIZE == 2U) + using QMPoolCtr = std::uint16_t; +#elif (QF_MPOOL_CTR_SIZE == 4U) + using QMPoolCtr = std::uint32_t; +#else + #error "QF_MPOOL_CTR_SIZE defined incorrectly, expected 1U, 2U, or 4U" +#endif + +} // namespace QP + +#define QF_MPOOL_EL(evType_) struct { \ + QP::QFreeBlock sto_[((sizeof(evType_) - 1U) \ + / sizeof(QP::QFreeBlock)) + 1U]; } +$declare ${QF::QFreeBlock} + +$declare ${QF::QMPool} + +#endif // QMPOOL_HPP_ + + + + #ifndef QV_HPP_ +#define QV_HPP_ + +$declare ${QV::QV-base} + +//============================================================================ +// interface used only for internal implementation, but not in applications +#ifdef QP_IMPL + +$declare ${QV-impl} + +$declare ${QF_EPOOL-impl} + +#endif // QP_IMPL + +#endif // QV_HPP_ + + + + #ifndef QK_HPP_ +#define QK_HPP_ + +$declare ${QK::QSchedStatus} + +$declare ${QK::QK-base} + +extern "C" { +$declare ${QK-extern-C} +} // extern "C" + +//============================================================================ +// interface used only for internal implementation, but not in applications +#ifdef QP_IMPL + +$declare ${QK-impl} + +$declare ${QF_EPOOL-impl} + +#endif // QP_IMPL + +#endif // QK_HPP_ + + + + #ifndef QXK_HPP_ +#define QXK_HPP_ + +$declare ${QXK::QSchedStatus} + +$declare ${QXK::QXTHREAD_NO_TIMEOUT} + +$declare ${QXK::QXK-base} + +$declare ${QXK::QXThread} + +$declare ${QXK::QXSemaphore} + +$declare ${QXK::QXMutex} + +extern "C" { +$declare ${QXK-extern-C} +} // extern "C" + +//============================================================================ +// interface used only for internal implementation, but not in applications +#ifdef QP_IMPL + +$declare ${QXK-impl} + +$declare ${QF_EPOOL-impl} + +namespace QP { +namespace QXK { +enum TimeoutSigs : QSignal { + DELAY_SIG = 1U, + TIMEOUT_SIG +}; +} // namespace QXK +} // namespace QP + +#endif // QP_IMPL + +#endif // QXK_HPP_ + + + + #ifndef QS_HPP_ +#define QS_HPP_ + +#ifndef Q_SPY +#error "Q_SPY must be defined to include qs.hpp" +#endif + +//============================================================================ +//! @cond INTERNAL + +#ifndef QS_CTR_SIZE +#define QS_CTR_SIZE 2U +#endif + +#ifndef QS_TIME_SIZE +#define QS_TIME_SIZE 4U +#endif + +//! @endcond +//============================================================================ + +$declare ${QS::types} +$declare ${QS::filters} +$declare ${QS-macros} + +//============================================================================ +//! @cond INTERNAL + +namespace QP { +namespace QS { + +struct Attr { + void const * locFilter_AP; //!< @deprecated + std::uint8_t * buf; + QSCtr end; + QSCtr volatile head; + QSCtr volatile tail; + QSCtr volatile used; + std::uint8_t volatile seq; + std::uint8_t volatile chksum; + std::uint8_t volatile critNest; + std::uint8_t flags; +}; + +extern Attr priv_; + +void glbFilter_(std::int_fast16_t const filter) noexcept; +void locFilter_(std::int_fast16_t const filter) noexcept; + +void beginRec_(std::uint_fast8_t const rec) noexcept; +void endRec_() noexcept; + +void u8_raw_(std::uint8_t const d) noexcept; +void u8u8_raw_( + std::uint8_t const d1, + std::uint8_t const d2) noexcept; +void u16_raw_(std::uint16_t d) noexcept; +void u32_raw_(std::uint32_t d) noexcept; +void u64_raw_(std::uint64_t d) noexcept; +void obj_raw_(void const * const obj) noexcept; +void str_raw_(char const * s) noexcept; + +void u8_fmt_( + std::uint8_t const format, + std::uint8_t const d) noexcept; +void u16_fmt_( + std::uint8_t format, + std::uint16_t d) noexcept; +void u32_fmt_( + std::uint8_t format, + std::uint32_t d) noexcept; +void u64_fmt_( + std::uint8_t format, + std::uint64_t d) noexcept; +void f32_fmt_( + std::uint8_t format, + float32_t f) noexcept; +void f64_fmt_( + std::uint8_t format, + float64_t d) noexcept; +void str_fmt_(char const * s) noexcept; +void mem_fmt_( + std::uint8_t const * blk, + std::uint8_t size) noexcept; + +void sig_dict_pre_( + QSignal const sig, + void const * const obj, + char const * const name) noexcept; +void obj_dict_pre_( + void const * const obj, + char const * const name) noexcept; +void obj_arr_dict_pre_( + void const * const obj, + std::uint_fast16_t const idx, + char const * const name) noexcept; +void fun_dict_pre_( + QSpyFunPtr fun, + char const * const name) noexcept; +void usr_dict_pre_( + enum_t const rec, + char const * const name) noexcept; +void enum_dict_pre_( + enum_t const value, + std::uint8_t const group, + char const * const name) noexcept; + +void assertion_pre_( + char const * const module, + int_t const id, + std::uint32_t const delay) noexcept; +void crit_entry_pre_() noexcept; +void crit_exit_pre_() noexcept; +void isr_entry_pre_( + std::uint8_t const isrnest, + std::uint8_t const prio) noexcept; +void isr_exit_pre_( + std::uint8_t const isrnest, + std::uint8_t const prio) noexcept; + +void target_info_pre_(std::uint8_t const isReset); + +} // namespace QS +} // namespace QP + +//! @endcond +//============================================================================ + +$declare ${QS::QS-TX} + +//============================================================================ +//! @cond INTERNAL + +namespace QP { +namespace QS { + +struct RxAttr { + void * currObj[8]; + std::uint8_t * buf; + QSCtr end; + QSCtr volatile head; + QSCtr volatile tail; +#ifdef Q_UTEST + bool inTestLoop; +#endif +} ; + +extern RxAttr rxPriv_; + +} // namespace QS +} // namespace QP + +//! @endcond +//============================================================================ + +$declare ${QS::QS-RX} + +//============================================================================ +#ifdef Q_UTEST + +$declare ${QS::QUTest} + +#define QUTEST_ON_POST 124 + +//============================================================================ +//! @cond INTERNAL + +namespace QP { +namespace QS { + +struct TestAttr { + TProbe tpBuf[16]; + std::uint8_t tpNum; + QSTimeCtr testTime; + QPSet readySet; + QPSet readySet_dis; + std::uint_fast8_t intLock; +}; + +extern TestAttr tstPriv_; + +void test_pause_(); +std::uint32_t getTestProbe_(QSpyFunPtr const api) noexcept; + +} // namespace QS +} // namespace QP + +//! @endcond +//============================================================================ + +// QP-stub for QUTest +// NOTE: The QP-stub is needed for unit testing QP applications, +// but might NOT be needed for testing QP itself. +#if (Q_UTEST != 0) + +$declare ${QS::QUTest-stub::QHsmDummy} +$declare ${QS::QUTest-stub::QActiveDummy} + +#endif // Q_UTEST != 0 + +#define QS_TEST_PROBE_DEF(fun_) \ + std::uint32_t const qs_tp_ = \ + QP::QS::getTestProbe_(QP::QS::force_cast<void (*)()>(fun_)); + +#define QS_TEST_PROBE(code_) \ + if (qs_tp_ != 0U) { code_ } + +#define QS_TEST_PROBE_ID(id_, code_) \ + if (qs_tp_ == static_cast<std::uint32_t>(id_)) { code_ } + +#define QS_TEST_PAUSE() (QP::QS::test_pause_()) + +#else // Q_UTEST not defined + +// dummy definitions when not building for QUTEST +#define QS_TEST_PROBE_DEF(fun_) +#define QS_TEST_PROBE(code_) +#define QS_TEST_PROBE_ID(id_, code_) +#define QS_TEST_PAUSE() (static_cast<void>(0)) + +#endif // Q_UTEST + +#endif // QS_HPP_ + + + + #ifndef QS_DUMMY_HPP_ +#define QS_DUMMY_HPP_ + +#ifdef Q_SPY +#error "Q_SPY must NOT be defined to include qs_dummy.hpp" +#endif + +#define QS_INIT(arg_) (true) +#define QS_EXIT() static_cast<void>(0) +#define QS_DUMP() static_cast<void>(0) +#define QS_GLB_FILTER(rec_) static_cast<void>(0) +#define QS_LOC_FILTER(qs_id_) static_cast<void>(0) + +#define QS_GET_BYTE(pByte_) (0xFFFFU) +#define QS_GET_BLOCK(pSize_) (nullptr) + +#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_) static_cast<void>(0) +#define QS_U8(width_, data_) static_cast<void>(0) +#define QS_I16(width_, data_) static_cast<void>(0) +#define QS_U16(width_, data_) static_cast<void>(0) +#define QS_I32(width_, data_) static_cast<void>(0) +#define QS_U32(width_, data_) static_cast<void>(0) +#define QS_F32(width_, data_) static_cast<void>(0) +#define QS_F64(width_, data_) static_cast<void>(0) +#define QS_I64(width_, data_) static_cast<void>(0) +#define QS_U64(width_, data_) static_cast<void>(0) +#define QS_ENUM(group_, value_) static_cast<void>(0) +#define QS_STR(str_) static_cast<void>(0) +#define QS_MEM(mem_, size_) static_cast<void>(0) +#define QS_SIG(sig_, obj_) static_cast<void>(0) +#define QS_OBJ(obj_) static_cast<void>(0) +#define QS_FUN(fun_) static_cast<void>(0) + +#define QS_SIG_DICTIONARY(sig_, obj_) static_cast<void>(0) +#define QS_OBJ_DICTIONARY(obj_) static_cast<void>(0) +#define QS_OBJ_ARR_DICTIONARY(obj_, idx_) static_cast<void>(0) +#define QS_FUN_DICTIONARY(fun_) static_cast<void>(0) +#define QS_USR_DICTIONARY(rec_) static_cast<void>(0) +#define QS_ENUM_DICTIONARY(value_, group_) static_cast<void>(0) +#define QS_ASSERTION(module_, loc_, delay_) static_cast<void>(0) +#define QS_FLUSH() static_cast<void>(0) + +#define QS_TEST_PROBE_DEF(fun_) +#define QS_TEST_PROBE(code_) +#define QS_TEST_PROBE_ID(id_, code_) +#define QS_TEST_PAUSE() static_cast<void>(0) + +#define QS_OUTPUT() static_cast<void>(0) +#define QS_RX_INPUT() static_cast<void>(0) +#define QS_ONLY(code_) static_cast<void>(0) + +//============================================================================ +// interface used only for internal implementation, but not in applications +#ifdef QP_IMPL + // predefined QS trace records + #define QS_BEGIN_PRE_(rec_, qs_id_) if (false) { + #define QS_END_PRE_() } + #define QS_BEGIN_PRE_(rec_, qs_id_) if (false) { + #define QS_END_PRE_() } + #define QS_U8_PRE_(data_) static_cast<void>(0) + #define QS_2U8_PRE_(data1_, data2_) static_cast<void>(0) + #define QS_U16_PRE_(data_) static_cast<void>(0) + #define QS_U32_PRE_(data_) static_cast<void>(0) + #define QS_TIME_PRE_() static_cast<void>(0) + #define QS_SIG_PRE_(sig_) static_cast<void>(0) + #define QS_EVS_PRE_(size_) static_cast<void>(0) + #define QS_OBJ_PRE_(obj_) static_cast<void>(0) + #define QS_FUN_PRE_(fun_) static_cast<void>(0) + #define QS_EQC_PRE_(ctr_) static_cast<void>(0) + #define QS_MPC_PRE_(ctr_) static_cast<void>(0) + #define QS_MPS_PRE_(size_) static_cast<void>(0) + #define QS_TEC_PRE_(ctr_) static_cast<void>(0) + + #define QS_CRIT_STAT + #define QS_CRIT_ENTRY() static_cast<void>(0) + #define QS_CRIT_EXIT() static_cast<void>(0) + + #define QS_MEM_SYS() static_cast<void>(0) + #define QS_MEM_APP() static_cast<void>(0) + + #define QS_TR_CRIT_ENTRY() static_cast<void>(0) + #define QS_TR_CRIT_EXIT() static_cast<void>(0) + #define QS_TR_ISR_ENTRY(isrnest_, prio_) static_cast<void>(0) + #define QS_Tr_ISR_EXIT(isrnest_, prio_) static_cast<void>(0) +#endif // QP_IMPL + +#endif // QS_DUMMY_HPP_ + + + + #ifndef QS_PKG_HPP_ +#define QS_PKG_HPP_ + +//============================================================================ +//! @cond INTERNAL + +namespace QP { + +//! QS received record types (RX channel) +enum QSpyRxRecords : std::uint8_t { + QS_RX_INFO, //!< query Target info (ver, config, tstamp) + QS_RX_COMMAND, //!< execute a user-defined command in the Target + QS_RX_RESET, //!< reset the Target + QS_RX_TICK, //!< call system clock tick in the Target + QS_RX_PEEK, //!< peek Target memory + QS_RX_POKE, //!< poke Target memory + QS_RX_FILL, //!< fill Target memory + QS_RX_TEST_SETUP, //!< test setup + QS_RX_TEST_TEARDOWN, //!< test teardown + QS_RX_TEST_PROBE, //!< set a Test-Probe in the Target + QS_RX_GLB_FILTER, //!< set global filters in the Target + QS_RX_LOC_FILTER, //!< set local filters in the Target + QS_RX_AO_FILTER, //!< set local AO filter in the Target + QS_RX_CURR_OBJ, //!< set the "current-object" in the Target + QS_RX_TEST_CONTINUE, //!< continue a test after QS_TEST_PAUSE() + QS_RX_QUERY_CURR, //!< query the "current object" in the Target + QS_RX_EVENT //!< inject an event to the Target +}; + +constexpr std::uint8_t QS_FRAME {0x7EU}; +constexpr std::uint8_t QS_ESC {0x7DU}; +constexpr std::uint8_t QS_ESC_XOR {0x20U}; +constexpr std::uint8_t QS_GOOD_CHKSUM {0xFFU}; + +} // namespace QP + +//---------------------------------------------------------------------------- +#define QS_BEGIN_PRE_(rec_, qs_id_) \ + if (QS_GLB_CHECK_(rec_) && QS_LOC_CHECK_(qs_id_)) { \ + QP::QS::beginRec_(static_cast<std::uint_fast8_t>(rec_)); +#define QS_END_PRE_() QP::QS::endRec_(); } + +#define QS_U8_PRE_(data_) \ + (QP::QS::u8_raw_(static_cast<std::uint8_t>(data_))) +#define QS_2U8_PRE_(data1_, data2_) \ + (QP::QS::u8u8_raw_(static_cast<std::uint8_t>(data1_), \ + static_cast<std::uint8_t>(data2_))) +#define QS_U16_PRE_(data_) \ + (QP::QS::u16_raw_(static_cast<std::uint16_t>(data_))) +#define QS_U32_PRE_(data_) \ + (QP::QS::u32_raw_(static_cast<std::uint32_t>(data_))) +#define QS_STR_PRE_(msg_) (QP::QS::str_raw_(msg_)) +#define QS_OBJ_PRE_(obj_) (QP::QS::obj_raw_(obj_)) + +#if (!defined Q_SIGNAL_SIZE || (Q_SIGNAL_SIZE == 1U)) + #define QS_SIG_PRE_(sig_) \ + (QP::QS::u8_raw_(static_cast<std::uint8_t>(sig_))) +#elif (Q_SIGNAL_SIZE == 2U) + #define QS_SIG_PRE_(sig_) \ + (QP::QS::u16_raw_(static_cast<std::uint16_t>(sig_))) +#elif (Q_SIGNAL_SIZE == 4U) + #define QS_SIG_PRE_(sig_) \ + (QP::QS::u32_raw_(static_cast<std::uint32_t>(sig_))) +#endif + +#if (!defined QS_FUN_PTR_SIZE || (QS_FUN_PTR_SIZE == 2U)) + #define QS_FUN_PRE_(fun_) \ + (QP::QS::u16_raw_(reinterpret_cast<std::uint16_t>(fun_))) +#elif (QS_FUN_PTR_SIZE == 4U) + #define QS_FUN_PRE_(fun_) \ + (QP::QS::u32_raw_(reinterpret_cast<std::uint32_t>(fun_))) +#elif (QS_FUN_PTR_SIZE == 8U) + #define QS_FUN_PRE_(fun_) \ + (QP::QS::u64_raw_(reinterpret_cast<std::uint64_t>(fun_))) +#else + #define QS_FUN_PRE_(fun_) \ + (QP::QS::u32_raw_(reinterpret_cast<std::uint32_t>(fun_))) +#endif + +//---------------------------------------------------------------------------- +#if (!defined QF_EQUEUE_CTR_SIZE || (QF_EQUEUE_CTR_SIZE == 1U)) + #define QS_EQC_PRE_(ctr_) \ + QP::QS::u8_raw_(static_cast<std::uint8_t>(ctr_)) +#elif (QF_EQUEUE_CTR_SIZE == 2U) + #define QS_EQC_PRE_(ctr_) \ + QP::QS::u16_raw_(static_cast<std::uint16_t>(ctr_)) +#elif (QF_EQUEUE_CTR_SIZE == 4U) + #define QS_EQC_PRE_(ctr_) \ + QP::QS::u32_raw_(static_cast<std::uint32_t>(ctr_)) +#else + #error "QF_EQUEUE_CTR_SIZE not defined" +#endif + +#if (!defined QF_EVENT_SIZ_SIZE || (QF_EVENT_SIZ_SIZE == 1U)) + #define QS_EVS_PRE_(size_) \ + QP::QS::u8_raw_(static_cast<std::uint8_t>(size_)) +#elif (QF_EVENT_SIZ_SIZE == 2U) + #define QS_EVS_PRE_(size_) \ + QP::QS::u16_raw_(static_cast<std::uint16_t>(size_)) +#elif (QF_EVENT_SIZ_SIZE == 4U) + #define QS_EVS_PRE_(size_) \ + QP::QS::u32_raw_(static_cast<std::uint32_t>(size_)) +#endif + +#if (!defined QF_MPOOL_SIZ_SIZE || (QF_MPOOL_SIZ_SIZE == 1U)) + #define QS_MPS_PRE_(size_) \ + QP::QS::u8_raw_(static_cast<std::uint8_t>(size_)) +#elif (QF_MPOOL_SIZ_SIZE == 2U) + #define QS_MPS_PRE_(size_) \ + QP::QS::u16_raw_(static_cast<std::uint16_t>(size_)) +#elif (QF_MPOOL_SIZ_SIZE == 4U) + #define QS_MPS_PRE_(size_) \ + QP::QS::u32_raw_(static_cast<std::uint32_t>(size_)) +#endif + +#if (!defined QF_MPOOL_CTR_SIZE || (QF_MPOOL_CTR_SIZE == 1U)) + #define QS_MPC_PRE_(ctr_) \ + QP::QS::u8_raw_(static_cast<std::uint8_t>(ctr_)) +#elif (QF_MPOOL_CTR_SIZE == 2U) + #define QS_MPC_PRE_(ctr_) \ + QP::QS::u16_raw_(static_cast<std::uint16_t>(ctr_)) +#elif (QF_MPOOL_CTR_SIZE == 4U) + #define QS_MPC_PRE_(ctr_) \ + QP::QS::u32_raw_(static_cast<std::uint32_t>(ctr_)) +#endif + +#if (!defined QF_TIMEEVT_CTR_SIZE || (QF_TIMEEVT_CTR_SIZE == 1U)) + #define QS_TEC_PRE_(ctr_) \ + QP::QS::u8_raw_(static_cast<std::uint8_t>(ctr_)) +#elif (QF_TIMEEVT_CTR_SIZE == 2U) + #define QS_TEC_PRE_(ctr_) \ + QP::QS::u16_raw_(static_cast<std::uint16_t>(ctr_)) +#elif (QF_TIMEEVT_CTR_SIZE == 4U) + #define QS_TEC_PRE_(ctr_) \ + QP::QS::u32_raw_(static_cast<std::uint32_t>(ctr_)) +#endif + +#define QS_REC_NUM_(enum_) (static_cast<std::uint_fast8_t>(enum_)) + +//---------------------------------------------------------------------------- +#define QS_INSERT_BYTE_(b_) \ + buf[head] = (b_); \ + ++head; \ + if (head == end) { \ + head = 0U; \ + } + +#define QS_INSERT_ESC_BYTE_(b_) \ + chksum = static_cast<std::uint8_t>(chksum + (b_)); \ + if (((b_) != QS_FRAME) && ((b_) != QS_ESC)) { \ + QS_INSERT_BYTE_(b_) \ + } \ + else { \ + QS_INSERT_BYTE_(QS_ESC) \ + QS_INSERT_BYTE_(static_cast<std::uint8_t>((b_) ^ QS_ESC_XOR)) \ + priv_.used = priv_.used + 1U; \ + } + +//---------------------------------------------------------------------------- +#if (defined Q_UTEST) && (Q_UTEST != 0) +namespace QP { +namespace QS { + +void processTestEvts_(); + +} // namespace QS +} // namespace QP +#endif // Q_UTEST != 0 + +//! @endcond +//============================================================================ + +#endif // QS_PKG_HPP_ + + + + #ifndef QSTAMP_HPP_ +#define QSTAMP_HPP_ + +namespace QP { +extern char const BUILD_DATE[12]; +extern char const BUILD_TIME[9]; +} // namespace QP + +#endif // QSTAMP_HPP_ + + + + #ifndef QPCPP_HPP_ +#define QPCPP_HPP_ + +//============================================================================ +#include "qp_port.hpp" // QP port from the port directory +#include "qsafe.h" // QP Functional Safety (FuSa) Subsystem +#ifdef Q_SPY // software tracing enabled? + #include "qs_port.hpp" // QS/C++ port from the port directory +#else + #include "qs_dummy.hpp" // QS/C++ dummy (inactive) interface +#endif + +//============================================================================ +#ifndef QP_API_VERSION + +#define QP_API_VERSION 0 + +#endif // QP_API_VERSION + +//============================================================================ +// QP/C++ API compatibility layer... + +#if (QP_API_VERSION < 730) + +//! @deprecated plain 'char' is no longer forbidden in MISRA/AUTOSAR-C++ +using char_t = char; + +//! @deprecated assertion failure handler +//! Use Q_onError() instead. +#define Q_onAssert(module_, id_) Q_onError(module_, id_) + +//! @deprecated #Q_NASSERT preprocessor switch to disable QP assertions +#ifdef Q_NASSERT + + // #Q_UNSAFE now replaces the functionality of Q_NASSERT + #define Q_UNSAFE + + //! @deprecated general purpose assertion with user-specified ID + //! number that **always** evaluates the `expr_` expression. + #define Q_ALLEGE_ID(id_, expr_) ((void)(expr_)) + +#else // QP FuSa Subsystem enabled + + //! @deprecated general purpose assertion with user-specified ID + //! number that **always** evaluates the `expr_` expression. + //! @note + //! The use of this macro is no longer recommended. + #define Q_ALLEGE_ID(id_, expr_) if (!(expr_)) { \ + QF_CRIT_STAT \ + QF_CRIT_ENTRY(); \ + Q_onError(&Q_this_module_[0], (id_)); \ + QF_CRIT_EXIT(); \ + } else ((void)0) + +#endif + +//! @deprecated general purpose assertion without ID number +//! that **always** evaluates the `expr_` expression. +//! Instead of ID number, this macro is based on the standard +//! `__LINE__` macro. +//! +//! @note The use of this macro is no longer recommended. +#define Q_ALLEGE(expr_) Q_ALLEGE_ID(__LINE__, (expr_)) + +//! Static (compile-time) assertion. +//! +//! @deprecated +//! Use Q_ASSERT_STATIC() or better yet `static_assert()` instead. +//! +#define Q_ASSERT_COMPILE(expr_) Q_ASSERT_STATIC(expr_) + +//! @deprecated use QP::QF::NO_MARGIN instead +#define QF_NO_MARGIN QP::QF::NO_MARGIN + +//============================================================================ +#if (QP_API_VERSION < 691) + +//! @deprecated enable the QS global filter +#define QS_FILTER_ON(rec_) QS_GLB_FILTER((rec_)) + +//! @deprecated disable the QS global filter +#define QS_FILTER_OFF(rec_) QS_GLB_FILTER(-(rec_)) + +//! @deprecated enable the QS local filter for SM (state machine) object +#define QS_FILTER_SM_OBJ(obj_) (static_cast<void>(0)) + +//! @deprecated enable the QS local filter for AO (active objects) +#define QS_FILTER_AO_OBJ(obj_) (static_cast<void>(0)) + +//! @deprecated enable the QS local filter for MP (memory pool) object +#define QS_FILTER_MP_OBJ(obj_) (static_cast<void>(0)) + +//! @deprecated enable the QS local filter for EQ (event queue) object +#define QS_FILTER_EQ_OBJ(obj_) (static_cast<void>(0)) + +//! @deprecated enable the QS local filter for TE (time event) object +#define QS_FILTER_TE_OBJ(obj_) (static_cast<void>(0)) + +#ifdef Q_SPY + +//! @deprecated local Filter for a generic application object `obj_`. +#define QS_FILTER_AP_OBJ(obj_) \ + (QP::QS::filt_.loc_AP = (obj_)) + +//! @deprecated begin of a user QS record, instead use QS_BEGIN_ID() +#define QS_BEGIN(rec_, obj_) \ + if (QS_GLB_FILTER_(rec_) && \ + ((QP::QS::filt_.loc[QP::QS::AP_OBJ] == nullptr) \ + || (QP::QS::filt_.loc_AP == (obj_)))) \ + { \ + QS_CRIT_STAT \ + QS_CRIT_ENTRY(); \ + QP::QS::beginRec_(static_cast<std::uint_fast8_t>(rec_)); \ + QS_TIME_PRE_(); + +//! @deprecated output hex-formatted std::uint32_t to the QS record +#define QS_U32_HEX(width_, data_) \ + (QP::QS::u32_fmt_(static_cast<std::uint8_t>( \ + (static_cast<std::uint8_t>((width_) << 4)) | QS_HEX_FMT), (data_))) + +#else + +#define QS_FILTER_AP_OBJ(obj_) (static_cast<void>(0)) +#define QS_BEGIN(rec_, obj_) if (false) { +#define QS_U32_HEX(width_, data_) (Q_UNUSED_PAR(0)) + +#endif // def Q_SPY + +//============================================================================ +#if (QP_API_VERSION < 680) + +//! @deprecated +//! Macro to specify a tran. in the "me->" impl-strategy. +//! Instead use the new impl-strategy without the "me->" pointer, where +//! you call tran(Q_STATE_CAST(target_)). +#define Q_TRAN(target_) (me->tran(Q_STATE_CAST(target_))) + +//! @deprecated +//! Macro to specify a tran-to-history in the "me->" impl-strategy. +//! Instead use the new impl-strategy without the "me->" pointer, where +//! you call tran_hist(Q_STATE_CAST(hist_)). +#define Q_TRAN_HIST(hist_) (me->tran_hist((hist_))) + +//! @deprecated +//! Macro to specify the superstate in the "me->" impl-strategy. +//! Instead use the new impl-strategy without the "me->" pointer, where +//! you call super(state_)). +#define Q_SUPER(state_) (me->super(Q_STATE_CAST(state_))) + +//! @deprecated +//! Macro to call in a QM state entry-handler. Applicable only to QMSMs. +//! Instead use the new impl-strategy without the "me->" pointer, where +//! the QM-generated code calls qm_entry(Q_STATE_CAST(state_)). +#define QM_ENTRY(state_) (me->qm_entry((state_))) + +//! @deprecated +//! Macro to call in a QM state exit-handler. Applicable only to QMSMs. +//! Instead use the new impl-strategy without the "me->" pointer, where +//! the QM-generated code calls qm_exit(Q_STATE_CAST(state_)). +#define QM_EXIT(state_) (me->qm_exit((state_))) + +//! @deprecated +//! Macro to call in a QM submachine exit-handler. Applicable only to QMSMs. +//! Instead use the new impl-strategy without the "me->" pointer, where +//! the QM-generated code calls qm_sm_exit(Q_STATE_CAST(state_)). +#define QM_SM_EXIT(state_) (me->qm_sm_exit((state_))) + +//! @deprecated +//! Macro to call in a QM state-handler when it executes a tran. +//! Instead use the new impl-strategy without the "me->" pointer, where +//! the QM-generated code calls qm_tran((tatbl_)). +#define QM_TRAN(tatbl_) (me->qm_tran((tatbl_))) + +//! @deprecated +//! Macro to call in a QM state-handler when it executes an initial tran. +//! Instead use the new impl-strategy without the "me->" pointer, where +//! the QM-generated code calls qm_tran_init((tatbl_)). +#define QM_TRAN_INIT(tatbl_) (me->qm_tran_init((tatbl_))) + +//! @deprecated +//! Macro to call in a QM state-handler when it executes a tran-to-history. +//! Instead use the new impl-strategy without the "me->" pointer, where +//! the QM-generated code calls qm_tran_hist((history_), (tatbl_)). +#define QM_TRAN_HIST(history_, tatbl_) \ + (me->qm_tran_hist((history_), (tatbl_))) + +//! @deprecated +//! Macro to call in a QM state-handler when it executes an initial tran. +//! Instead use the new impl-strategy without the "me->" pointer, where +//! the QM-generated code calls qm_tran_ep((tatbl_)). +#define QM_TRAN_EP(tatbl_) (me->qm_tran_ep((tatbl_))) + +//! @deprecated +//! Macro to call in a QM state-handler when it executes a tran-to-exit-point. +//! Instead use the new impl-strategy without the "me->" pointer, where +//! the QM-generated code calls qm_tran_xp((xp_), (tatbl_)). +#define QM_TRAN_XP(xp_, tatbl_) (me->qm_tran_xp((xp_), (tatbl_))) + +//! @deprecated +//! Designates the superstate of a given state in a subclass of QP::QMsm. +//! Instead use the new impl-strategy without the "me->" pointer, where +//! the QM-generated code calls qm_super_sub((state_)). +#define QM_SUPER_SUB(state_) (me->qm_super_sub((state_))) + +#endif // QP_API_VERSION < 680 +#endif // QP_API_VERSION < 691 +#endif // QP_API_VERSION < 700 + +#endif // QPCPP_HPP_ + + + + + + + + + #define QP_IMPL // this is QP implementation +#include "qp_port.hpp" // QP port +#include "qp_pkg.hpp" // QP package-scope interface +#include "qsafe.h" // QP Functional Safety (FuSa) Subsystem +#ifdef Q_SPY // QS software tracing enabled? + #include "qs_port.hpp" // QS port + #include "qs_pkg.hpp" // QS facilities for pre-defined trace records +#else + #include "qs_dummy.hpp" // disable the QS software tracing +#endif // Q_SPY + +//============================================================================ +//! @cond INTERNAL + +// unnamed namespace for local definitions with internal linkage +namespace { +Q_DEFINE_THIS_MODULE("qep_hsm") + +// immutable events corresponding to the reserved signals. +static QP::QEvt const l_reservedEvt_[4] { + QP::QEvt(static_cast<QP::QSignal>(QP::QHsm::Q_EMPTY_SIG)), + QP::QEvt(static_cast<QP::QSignal>(QP::QHsm::Q_ENTRY_SIG)), + QP::QEvt(static_cast<QP::QSignal>(QP::QHsm::Q_EXIT_SIG)), + QP::QEvt(static_cast<QP::QSignal>(QP::QHsm::Q_INIT_SIG)) +}; + +} // unnamed namespace + +// helper macro to handle reserved event in an QHsm +#define QHSM_RESERVED_EVT_(state_, sig_) \ + ((*(state_))(this, &l_reservedEvt_[(sig_)])) + +// helper macro to trace state entry +#define QS_STATE_ENTRY_(state_, qs_id_) \ + QS_CRIT_ENTRY(); \ + QS_MEM_SYS(); \ + QS_BEGIN_PRE_(QS_QEP_STATE_ENTRY, (qs_id_)) \ + QS_OBJ_PRE_(this); \ + QS_FUN_PRE_(state_); \ + QS_END_PRE_() \ + QS_MEM_APP(); \ + QS_CRIT_EXIT() + +// helper macro to trace state exit +#define QS_STATE_EXIT_(state_, qs_id_) \ + QS_CRIT_ENTRY(); \ + QS_MEM_SYS(); \ + QS_BEGIN_PRE_(QS_QEP_STATE_EXIT, (qs_id_)) \ + QS_OBJ_PRE_(this); \ + QS_FUN_PRE_(state_); \ + QS_END_PRE_() \ + QS_MEM_APP(); \ + QS_CRIT_EXIT() + +//! @endcond +//============================================================================ + +$define ${QEP::versionStr[]} + +$define ${QEP::QHsm} + + + + #define QP_IMPL // this is QP implementation +#include "qp_port.hpp" // QP port +#include "qp_pkg.hpp" // QP package-scope interface +#include "qsafe.h" // QP Functional Safety (FuSa) Subsystem +#ifdef Q_SPY // QS software tracing enabled? + #include "qs_port.hpp" // QS port + #include "qs_pkg.hpp" // QS facilities for pre-defined trace records +#else + #include "qs_dummy.hpp" // disable the QS software tracing +#endif // Q_SPY + +//============================================================================ +//! @cond INTERNAL + +// unnamed namespace for local definitions with internal linkage +namespace { + +Q_DEFINE_THIS_MODULE("qep_msm") + +// top-state object for QMsm-style state machines +QP::QMState const l_msm_top_s = { + nullptr, + nullptr, + nullptr, + nullptr, + nullptr +}; + +static constexpr std::int_fast8_t MAX_ENTRY_DEPTH_ {4}; + +} // unnamed namespace + +//! @endcond +//============================================================================ + +$define ${QEP::QMsm} + + + + #define QP_IMPL // this is QP implementation +#include "qp_port.hpp" // QP port +#include "qp_pkg.hpp" // QP package-scope interface +#include "qsafe.h" // QP Functional Safety (FuSa) Subsystem +#ifdef Q_SPY // QS software tracing enabled? + #include "qs_port.hpp" // QS port + #include "qs_pkg.hpp" // QS facilities for pre-defined trace records +#else + #include "qs_dummy.hpp" // disable the QS software tracing +#endif // Q_SPY + +// unnamed namespace for local definitions with internal linkage +namespace { +//Q_DEFINE_THIS_MODULE("qf_act") +} // unnamed namespace + +$define ${QF::QActive::registry_[QF_MAX_ACTIVE + 1U]} + +$define ${QF::QF-pkg} + +$define ${QF::types::QF_LOG2} + + + + #define QP_IMPL // this is QP implementation +#include "qp_port.hpp" // QP port +#include "qp_pkg.hpp" // QP package-scope interface +#include "qsafe.h" // QP Functional Safety (FuSa) Subsystem +#ifdef Q_SPY // QS software tracing enabled? + #include "qs_port.hpp" // QS port + #include "qs_pkg.hpp" // QS facilities for pre-defined trace records +#else + #include "qs_dummy.hpp" // disable the QS software tracing +#endif // Q_SPY + +//============================================================================ +// unnamed namespace for local definitions with internal linkage +namespace { +Q_DEFINE_THIS_MODULE("qf_actq") +} // unnamed namespace + +$define ${QF::QActive::post_} + +$define ${QF::QActive::postLIFO} + +$define ${QF::QActive::get_} + +$define ${QF::QTicker} + + + + #define QP_IMPL // this is QP implementation +#include "qp_port.hpp" // QP port +#include "qp_pkg.hpp" // QP package-scope interface +#include "qsafe.h" // QP Functional Safety (FuSa) Subsystem +#ifdef Q_SPY // QS software tracing enabled? + #include "qs_port.hpp" // QS port + #include "qs_pkg.hpp" // QS facilities for pre-defined trace records +#else + #include "qs_dummy.hpp" // disable the QS software tracing +#endif // Q_SPY + +// unnamed namespace for local definitions with internal linkage +namespace { +Q_DEFINE_THIS_MODULE("qf_defer") +} // unnamed namespace + +$define ${QF::QActive::defer} + +$define ${QF::QActive::recall} + +$define ${QF::QActive::flushDeferred} + + + + #define QP_IMPL // this is QP implementation +#include "qp_port.hpp" // QP port +#include "qp_pkg.hpp" // QP package-scope interface +#include "qsafe.h" // QP Functional Safety (FuSa) Subsystem +#ifdef Q_SPY // QS software tracing enabled? + #include "qs_port.hpp" // QS port + #include "qs_pkg.hpp" // QS facilities for pre-defined trace records +#else + #include "qs_dummy.hpp" // disable the QS software tracing +#endif // Q_SPY + +#if (QF_MAX_EPOOL > 0U) // mutable events configured? + +// unnamed namespace for local definitions with internal linkage +namespace { +Q_DEFINE_THIS_MODULE("qf_dyn") +} // unnamed namespace + +$define ${QF::QF-dyn} + +#endif // (QF_MAX_EPOOL > 0U) mutable events configured + + + + #define QP_IMPL // this is QP implementation +#include "qp_port.hpp" // QP port +#include "qp_pkg.hpp" // QP package-scope interface +#include "qsafe.h" // QP Functional Safety (FuSa) Subsystem +#ifdef Q_SPY // QS software tracing enabled? + #include "qs_port.hpp" // QS port + #include "qs_pkg.hpp" // QS facilities for pre-defined trace records +#else + #include "qs_dummy.hpp" // disable the QS software tracing +#endif // Q_SPY + +// unnamed namespace for local definitions with internal linkage +namespace { +Q_DEFINE_THIS_MODULE("qf_mem") +} // unnamed namespace + +$define ${QF::QMPool} + + + + #define QP_IMPL // this is QP implementation +#include "qp_port.hpp" // QP port +#include "qp_pkg.hpp" // QP package-scope interface +#include "qsafe.h" // QP Functional Safety (FuSa) Subsystem +#ifdef Q_SPY // QS software tracing enabled? + #include "qs_port.hpp" // QS port + #include "qs_pkg.hpp" // QS facilities for pre-defined trace records +#else + #include "qs_dummy.hpp" // disable the QS software tracing +#endif // Q_SPY + +// unnamed namespace for local definitions with internal linkage +namespace { +Q_DEFINE_THIS_MODULE("qf_qact") +} // unnamed namespace + +$define ${QF::QActive::QActive} + +$define ${QF::QActive::register_} + +$define ${QF::QActive::unregister_} + + + + #define QP_IMPL // this is QP implementation +#include "qp_port.hpp" // QP port +#include "qp_pkg.hpp" // QP package-scope interface +#include "qsafe.h" // QP Functional Safety (FuSa) Subsystem +#ifdef Q_SPY // QS software tracing enabled? + #include "qs_port.hpp" // QS port + #include "qs_pkg.hpp" // QS facilities for pre-defined trace records +#else + #include "qs_dummy.hpp" // disable the QS software tracing +#endif // Q_SPY + +// unnamed namespace for local definitions with internal linkage +namespace { +//Q_DEFINE_THIS_MODULE("qf_qmact") +} // unnamed namespace + +$define ${QF::QMActive} + + + + #define QP_IMPL // this is QP implementation +#include "qp_port.hpp" // QP port +#include "qp_pkg.hpp" // QP package-scope interface +#include "qsafe.h" // QP Functional Safety (FuSa) Subsystem +#ifdef Q_SPY // QS software tracing enabled? + #include "qs_port.hpp" // QS port + #include "qs_pkg.hpp" // QS facilities for pre-defined trace records +#else + #include "qs_dummy.hpp" // disable the QS software tracing +#endif // Q_SPY + +// unnamed namespace for local definitions with internal linkage +namespace { +Q_DEFINE_THIS_MODULE("qf_qeq") +} // unnamed namespace + +$define ${QF::QEQueue} + + + + #define QP_IMPL // this is QP implementation +#include "qp_port.hpp" // QP port +#include "qp_pkg.hpp" // QP package-scope interface +#include "qsafe.h" // QP Functional Safety (FuSa) Subsystem +#ifdef Q_SPY // QS software tracing enabled? + #include "qs_port.hpp" // QS port + #include "qs_pkg.hpp" // QS facilities for pre-defined trace records +#else + #include "qs_dummy.hpp" // disable the QS software tracing +#endif // Q_SPY + +// unnamed namespace for local definitions with internal linkage +namespace { +Q_DEFINE_THIS_MODULE("qf_ps") +} // unnamed namespace + +$define ${QF::QActive::subscrList_} + +$define ${QF::QActive::maxPubSignal_} + +$define ${QF::QActive::psInit} + +$define ${QF::QActive::publish_} + +$define ${QF::QActive::subscribe} + +$define ${QF::QActive::unsubscribe} + +$define ${QF::QActive::unsubscribeAll} + + + + #define QP_IMPL // this is QP implementation +#include "qp_port.hpp" // QP port +#include "qp_pkg.hpp" // QP package-scope interface +#include "qsafe.h" // QP Functional Safety (FuSa) Subsystem +#ifdef Q_SPY // QS software tracing enabled? + #include "qs_port.hpp" // QS port + #include "qs_pkg.hpp" // QS facilities for pre-defined trace records +#else + #include "qs_dummy.hpp" // disable the QS software tracing +#endif // Q_SPY + +// unnamed namespace for local definitions with internal linkage +namespace { +Q_DEFINE_THIS_MODULE("qf_time") +} // unnamed namespace + +$define ${QF::QTimeEvt} + + + + + + + #define QP_IMPL // this is QP implementation +#include "qp_port.hpp" // QP port +#include "qp_pkg.hpp" // QP package-scope interface +#include "qsafe.h" // QP Functional Safety (FuSa) Subsystem +#ifdef Q_SPY // QS software tracing enabled? + #include "qs_port.hpp" // QS port + #include "qs_pkg.hpp" // QS facilities for pre-defined trace records +#else + #include "qs_dummy.hpp" // disable the QS software tracing +#endif // Q_SPY + +// protection against including this source file in a wrong project +#ifndef QV_HPP_ + #error "Source file included in a project NOT based on the QV kernel" +#endif // QV_HPP_ + +// unnamed namespace for local definitions with internal linkage +namespace { +Q_DEFINE_THIS_MODULE("qv") +} // unnamed namespace + +$define ${QV::QV-base} + +$define ${QV::QF-cust} + +$define ${QV::QActive} + + + + + + + #define QP_IMPL // this is QP implementation +#include "qp_port.hpp" // QP port +#include "qp_pkg.hpp" // QP package-scope interface +#include "qsafe.h" // QP Functional Safety (FuSa) Subsystem +#ifdef Q_SPY // QS software tracing enabled? + #include "qs_port.hpp" // QS port + #include "qs_pkg.hpp" // QS facilities for pre-defined trace records +#else + #include "qs_dummy.hpp" // disable the QS software tracing +#endif // Q_SPY + +// protection against including this source file in a wrong project +#ifndef QK_HPP_ + #error "Source file included in a project NOT based on the QK kernel" +#endif // QK_HPP_ + +// unnamed namespace for local definitions with internal linkage +namespace { +Q_DEFINE_THIS_MODULE("qk") +} // unnamed namespace + +$define ${QK::QK-base} + +extern "C" { +$define ${QK-extern-C} +} // extern "C" + +$define ${QK::QF-cust} + +$define ${QK::QActive} + + + + + + + #define QP_IMPL // this is QP implementation +#include "qp_port.hpp" // QP port +#include "qp_pkg.hpp" // QP package-scope interface +#include "qsafe.h" // QP Functional Safety (FuSa) Subsystem +#ifdef Q_SPY // QS software tracing enabled? + #include "qs_port.hpp" // QS port + #include "qs_pkg.hpp" // QS facilities for pre-defined trace records +#else + #include "qs_dummy.hpp" // disable the QS software tracing +#endif // Q_SPY + +// protection against including this source file in a wrong project +#ifndef QXK_HPP_ + #error "Source file included in a project NOT based on the QXK kernel" +#endif // QXK_HPP_ + +// unnamed namespace for local definitions with internal linkage +namespace { +Q_DEFINE_THIS_MODULE("qxk") +} // unnamed namespace + +$define ${QXK::QXK-base} + +extern "C" { +$define ${QXK-extern-C} +} // extern "C" + +$define ${QXK::QF-cust} + +$define ${QXK::QActive} + + + + #define QP_IMPL // this is QP implementation +#include "qp_port.hpp" // QP port +#include "qp_pkg.hpp" // QP package-scope interface +#include "qsafe.h" // QP Functional Safety (FuSa) Subsystem +#ifdef Q_SPY // QS software tracing enabled? + #include "qs_port.hpp" // QS port + #include "qs_pkg.hpp" // QS facilities for pre-defined trace records +#else + #include "qs_dummy.hpp" // disable the QS software tracing +#endif // Q_SPY + +// protection against including this source file in a wrong project +#ifndef QXK_HPP_ + #error "Source file included in a project NOT based on the QXK kernel" +#endif // QXK_HPP_ + +// unnamed namespace for local definitions with internal linkage +namespace { +Q_DEFINE_THIS_MODULE("qxk_mutex") +} // unnamed namespace + +$define ${QXK::QXMutex} + + + + #define QP_IMPL // this is QP implementation +#include "qp_port.hpp" // QP port +#include "qp_pkg.hpp" // QP package-scope interface +#include "qsafe.h" // QP Functional Safety (FuSa) Subsystem +#ifdef Q_SPY // QS software tracing enabled? + #include "qs_port.hpp" // QS port + #include "qs_pkg.hpp" // QS facilities for pre-defined trace records +#else + #include "qs_dummy.hpp" // disable the QS software tracing +#endif // Q_SPY + +// protection against including this source file in a wrong project +#ifndef QXK_HPP_ + #error "Source file included in a project NOT based on the QXK kernel" +#endif // QXK_HPP_ + +// unnamed namespace for local definitions with internal linkage +namespace { +Q_DEFINE_THIS_MODULE("qxk_sema") +} // unnamed namespace + +$define ${QXK::QXSemaphore} + + + + #define QP_IMPL // this is QP implementation +#include "qp_port.hpp" // QP port +#include "qp_pkg.hpp" // QP package-scope interface +#include "qsafe.h" // QP Functional Safety (FuSa) Subsystem +#ifdef Q_SPY // QS software tracing enabled? + #include "qs_port.hpp" // QS port + #include "qs_pkg.hpp" // QS facilities for pre-defined trace records +#else + #include "qs_dummy.hpp" // disable the QS software tracing +#endif // Q_SPY + +// protection against including this source file in a wrong project +#ifndef QXK_HPP_ + #error "Source file included in a project NOT based on the QXK kernel" +#endif // QXK_HPP_ + +// unnamed namespace for local definitions with internal linkage +namespace { +Q_DEFINE_THIS_MODULE("qxk_xthr") +} // unnamed namespace + +$define ${QXK::QXThread} + + + + + + + #define QP_IMPL // this is QP implementation +#include "qs_port.hpp" // QS port +#include "qs_pkg.hpp" // QS package-scope internal interface +#include "qstamp.hpp" // QP time-stamp +#include "qsafe.h" // QP Functional Safety (FuSa) Subsystem + +// unnamed namespace for local definitions with internal linkage +namespace { +Q_DEFINE_THIS_MODULE("qs") +} // unnamed namespace + +$define ${QS::QS-TX} + +#ifndef QF_MEM_ISOLATE +$define ${QS::filters} +#endif + +//============================================================================ +//! @cond INTERNAL + +namespace QP { +namespace QS { + +//............................................................................ +Attr priv_; + +//............................................................................ +void glbFilter_(std::int_fast16_t const filter) noexcept { + bool const isRemove = (filter < 0); + std::uint16_t const rec = isRemove + ? static_cast<std::uint16_t>(-filter) + : static_cast<std::uint16_t>(filter); + switch (rec) { + case QS_ALL_RECORDS: { + std::uint8_t const tmp = (isRemove ? 0x00U : 0xFFU); + std::uint_fast8_t i; + // set all global filters (partially unrolled loop) + for (i = 0U; i < Q_DIM(filt_.glb); i += 4U) { + filt_.glb[i ] = tmp; + filt_.glb[i + 1U] = tmp; + filt_.glb[i + 2U] = tmp; + filt_.glb[i + 3U] = tmp; + } + if (isRemove) { + // leave the "not maskable" filters enabled, + // see qs.h, Miscellaneous QS records (not maskable) + // + filt_.glb[0] = 0x01U; + filt_.glb[6] = 0x40U; + filt_.glb[7] = 0xFCU; + filt_.glb[8] = 0x7FU; + } + else { + // never turn the last 3 records on (0x7D, 0x7E, 0x7F) + filt_.glb[15] = 0x1FU; + } + break; + } + case QS_SM_RECORDS: + if (isRemove) { + filt_.glb[0] &= static_cast<std::uint8_t>(~0xFEU & 0xFFU); + filt_.glb[1] &= static_cast<std::uint8_t>(~0x03U & 0xFFU); + filt_.glb[6] &= static_cast<std::uint8_t>(~0x80U & 0xFFU); + filt_.glb[7] &= static_cast<std::uint8_t>(~0x03U & 0xFFU); + } + else { + filt_.glb[0] |= 0xFEU; + filt_.glb[1] |= 0x03U; + filt_.glb[6] |= 0x80U; + filt_.glb[7] |= 0x03U; + } + break; + case QS_AO_RECORDS: + if (isRemove) { + filt_.glb[1] &= static_cast<std::uint8_t>(~0xFCU & 0xFFU); + filt_.glb[2] &= static_cast<std::uint8_t>(~0x07U & 0xFFU); + filt_.glb[5] &= static_cast<std::uint8_t>(~0x20U & 0xFFU); + } + else { + filt_.glb[1] |= 0xFCU; + filt_.glb[2] |= 0x07U; + filt_.glb[5] |= 0x20U; + } + break; + case QS_EQ_RECORDS: + if (isRemove) { + filt_.glb[2] &= static_cast<std::uint8_t>(~0x78U & 0xFFU); + filt_.glb[5] &= static_cast<std::uint8_t>(~0x40U & 0xFFU); + } + else { + filt_.glb[2] |= 0x78U; + filt_.glb[5] |= 0x40U; + } + break; + case QS_MP_RECORDS: + if (isRemove) { + filt_.glb[3] &= static_cast<std::uint8_t>(~0x03U & 0xFFU); + filt_.glb[5] &= static_cast<std::uint8_t>(~0x80U & 0xFFU); + } + else { + filt_.glb[3] |= 0x03U; + filt_.glb[5] |= 0x80U; + } + break; + case QS_QF_RECORDS: + if (isRemove) { + filt_.glb[2] &= static_cast<std::uint8_t>(~0x80U & 0xFFU); + filt_.glb[3] &= static_cast<std::uint8_t>(~0xFCU & 0xFFU); + filt_.glb[4] &= static_cast<std::uint8_t>(~0xC0U & 0xFFU); + filt_.glb[5] &= static_cast<std::uint8_t>(~0x1FU & 0xFFU); + } + else { + filt_.glb[2] |= 0x80U; + filt_.glb[3] |= 0xFCU; + filt_.glb[4] |= 0xC0U; + filt_.glb[5] |= 0x1FU; + } + break; + case QS_TE_RECORDS: + if (isRemove) { + filt_.glb[4] &= static_cast<std::uint8_t>(~0x3FU & 0xFFU); + } + else { + filt_.glb[4] |= 0x3FU; + } + break; + case QS_SC_RECORDS: + if (isRemove) { + filt_.glb[6] &= static_cast<std::uint8_t>(~0x3FU & 0xFFU); + } + else { + filt_.glb[6] |= 0x3FU; + } + break; + case QS_SEM_RECORDS: + if (isRemove) { + filt_.glb[8] &= static_cast<std::uint8_t>(~0x80U & 0xFFU); + filt_.glb[9] &= static_cast<std::uint8_t>(~0x07U & 0xFFU); + } + else { + filt_.glb[8] |= 0x80U; + filt_.glb[9] |= 0x07U; + } + break; + case QS_MTX_RECORDS: + if (isRemove) { + filt_.glb[9] &= static_cast<std::uint8_t>(~0xF8U & 0xFFU); + filt_.glb[10] &= static_cast<std::uint8_t>(~0x01U & 0xFFU); + } + else { + filt_.glb[9] |= 0xF8U; + filt_.glb[10] |= 0x01U; + } + break; + case QS_U0_RECORDS: + if (isRemove) { + filt_.glb[12] &= static_cast<std::uint8_t>(~0xF0U & 0xFFU); + filt_.glb[13] &= static_cast<std::uint8_t>(~0x01U & 0xFFU); + } + else { + filt_.glb[12] |= 0xF0U; + filt_.glb[13] |= 0x01U; + } + break; + case QS_U1_RECORDS: + if (isRemove) { + filt_.glb[13] &= static_cast<std::uint8_t>(~0x3EU & 0xFFU); + } + else { + filt_.glb[13] |= 0x3EU; + } + break; + case QS_U2_RECORDS: + if (isRemove) { + filt_.glb[13] &= static_cast<std::uint8_t>(~0xC0U & 0xFFU); + filt_.glb[14] &= static_cast<std::uint8_t>(~0x07U & 0xFFU); + } + else { + filt_.glb[13] |= 0xC0U; + filt_.glb[14] |= 0x07U; + } + break; + case QS_U3_RECORDS: + if (isRemove) { + filt_.glb[14] &= static_cast<std::uint8_t>(~0xF8U & 0xFFU); + } + else { + filt_.glb[14] |= 0xF8U; + } + break; + case QS_U4_RECORDS: + if (isRemove) { + filt_.glb[15] &= static_cast<std::uint8_t>(~0x1FU & 0xFFU); + } + else { + filt_.glb[15] |= 0x1FU; + } + break; + case QS_UA_RECORDS: + if (isRemove) { + filt_.glb[12] &= static_cast<std::uint8_t>(~0xF0U & 0xFFU); + filt_.glb[13] = 0U; + filt_.glb[14] = 0U; + filt_.glb[15] &= static_cast<std::uint8_t>(~0x1FU & 0xFFU); + } + else { + filt_.glb[12] |= 0xF0U; + filt_.glb[13] |= 0xFFU; + filt_.glb[14] |= 0xFFU; + filt_.glb[15] |= 0x1FU; + } + break; + default: { + QS_CRIT_STAT + QS_CRIT_ENTRY(); + // QS rec number must be below 0x7D, so no need for escaping + Q_ASSERT_INCRIT(210, rec < 0x7DU); + QS_CRIT_EXIT(); + + if (isRemove) { + filt_.glb[rec >> 3U] + &= static_cast<std::uint8_t>(~(1U << (rec & 7U)) & 0xFFU); + } + else { + filt_.glb[rec >> 3U] + |= static_cast<std::uint8_t>(1U << (rec & 7U)); + // never turn the last 3 records on (0x7D, 0x7E, 0x7F) + filt_.glb[15] &= 0x1FU; + } + break; + } + } +} + +//............................................................................ +void locFilter_(std::int_fast16_t const filter) noexcept { + bool const isRemove = (filter < 0); + std::uint16_t const qs_id = isRemove + ? static_cast<std::uint16_t>(-filter) + : static_cast<std::uint16_t>(filter); + std::uint8_t const tmp = (isRemove ? 0x00U : 0xFFU); + std::uint_fast8_t i; + switch (qs_id) { + case QS_ALL_IDS: + // set all local filters (partially unrolled loop) + for (i = 0U; i < Q_DIM(filt_.loc); i += 4U) { + filt_.loc[i ] = tmp; + filt_.loc[i + 1U] = tmp; + filt_.loc[i + 2U] = tmp; + filt_.loc[i + 3U] = tmp; + } + break; + case QS_AO_IDS: + for (i = 0U; i < 8U; i += 4U) { + filt_.loc[i ] = tmp; + filt_.loc[i + 1U] = tmp; + filt_.loc[i + 2U] = tmp; + filt_.loc[i + 3U] = tmp; + } + break; + case QS_EP_IDS: + i = 8U; + filt_.loc[i ] = tmp; + filt_.loc[i + 1U] = tmp; + break; + case QS_AP_IDS: + i = 12U; + filt_.loc[i ] = tmp; + filt_.loc[i + 1U] = tmp; + filt_.loc[i + 2U] = tmp; + filt_.loc[i + 3U] = tmp; + break; + default: { + QS_CRIT_STAT + QS_CRIT_ENTRY(); + // qs_id must be in range + Q_ASSERT_INCRIT(310, qs_id < 0x7FU); + QS_CRIT_EXIT(); + if (isRemove) { + filt_.loc[qs_id >> 3U] &= + static_cast<std::uint8_t>( + ~(1U << (qs_id & 7U)) & 0xFFU); + } + else { + filt_.loc[qs_id >> 3U] + |= (1U << (qs_id & 7U)); + } + break; + } + } + filt_.loc[0] |= 0x01U; // leave QS_ID == 0 always on +} + +//............................................................................ +void beginRec_(std::uint_fast8_t const rec) noexcept { + std::uint8_t const b = priv_.seq + 1U; + std::uint8_t chksum = 0U; // reset the checksum + std::uint8_t * const buf = priv_.buf; // put in a temporary (register) + QSCtr head = priv_.head; // put in a temporary (register) + QSCtr const end = priv_.end; // put in a temporary (register) + + priv_.seq = b; // store the incremented sequence num + priv_.used = (priv_.used + 2U); // 2 bytes about to be added + + QS_INSERT_ESC_BYTE_(b) + + chksum += static_cast<std::uint8_t>(rec); + QS_INSERT_BYTE_(static_cast<std::uint8_t>(rec)) // no need for escaping + + priv_.head = head; // save the head + priv_.chksum = chksum; // save the checksum +} + +//............................................................................ +void endRec_() noexcept { + std::uint8_t * const buf = priv_.buf; // put in a temporary (register) + QSCtr head = priv_.head; + QSCtr const end = priv_.end; + std::uint8_t b = priv_.chksum; + b ^= 0xFFU; // invert the bits in the checksum + + priv_.used = (priv_.used + 2U); // 2 bytes about to be added + + if ((b != QS_FRAME) && (b != QS_ESC)) { + QS_INSERT_BYTE_(b) + } + else { + QS_INSERT_BYTE_(QS_ESC) + QS_INSERT_BYTE_(b ^ QS_ESC_XOR) + priv_.used = (priv_.used + 1U); // account for the ESC byte + } + + QS_INSERT_BYTE_(QS_FRAME) // do not escape this QS_FRAME + + priv_.head = head; // save the head + if (priv_.used > end) { // overrun over the old data? + priv_.used = end; // the whole buffer is used + priv_.tail = head; // shift the tail to the old data + } +} + +//............................................................................ +void u8_raw_(std::uint8_t const d) noexcept { + std::uint8_t chksum = priv_.chksum; // put in a temporary (register) + std::uint8_t * const buf = priv_.buf; // put in a temporary (register) + QSCtr head = priv_.head; // put in a temporary (register) + QSCtr const end = priv_.end; // put in a temporary (register) + + priv_.used = (priv_.used + 1U); // 1 byte about to be added + QS_INSERT_ESC_BYTE_(d) + + priv_.head = head; // save the head + priv_.chksum = chksum; // save the checksum +} + +//............................................................................ +void u8u8_raw_( + std::uint8_t const d1, + std::uint8_t const d2) noexcept +{ + std::uint8_t chksum = priv_.chksum; // put in a temporary (register) + std::uint8_t * const buf = priv_.buf; // put in a temporary (register) + QSCtr head = priv_.head; // put in a temporary (register) + QSCtr const end = priv_.end; // put in a temporary (register) + + priv_.used = (priv_.used + 2U); // 2 bytes about to be added + QS_INSERT_ESC_BYTE_(d1) + QS_INSERT_ESC_BYTE_(d2) + + priv_.head = head; // save the head + priv_.chksum = chksum; // save the checksum +} + +//............................................................................ +void u16_raw_(std::uint16_t d) noexcept { + std::uint8_t b = static_cast<std::uint8_t>(d); + std::uint8_t chksum = priv_.chksum; // put in a temporary (register) + std::uint8_t * const buf = priv_.buf; // put in a temporary (register) + QSCtr head = priv_.head; // put in a temporary (register) + QSCtr const end = priv_.end; // put in a temporary (register) + + priv_.used = (priv_.used + 2U); // 2 bytes about to be added + + QS_INSERT_ESC_BYTE_(b) + + d >>= 8U; + b = static_cast<std::uint8_t>(d); + QS_INSERT_ESC_BYTE_(b) + + priv_.head = head; // save the head + priv_.chksum = chksum; // save the checksum +} + +//............................................................................ +void u32_raw_(std::uint32_t d) noexcept { + std::uint8_t chksum = priv_.chksum; // put in a temporary (register) + std::uint8_t * const buf = priv_.buf; // put in a temporary (register) + QSCtr head = priv_.head; // put in a temporary (register) + QSCtr const end = priv_.end; // put in a temporary (register) + + priv_.used = (priv_.used + 4U); // 4 bytes about to be added + for (std::uint_fast8_t i = 4U; i != 0U; --i) { + std::uint8_t const b = static_cast<std::uint8_t>(d); + QS_INSERT_ESC_BYTE_(b) + d >>= 8U; + } + + priv_.head = head; // save the head + priv_.chksum = chksum; // save the checksum +} + +//............................................................................ +void obj_raw_(void const * const obj) noexcept { + #if (QS_OBJ_PTR_SIZE == 1U) + u8_raw_(reinterpret_cast<std::uint8_t>(obj)); + #elif (QS_OBJ_PTR_SIZE == 2U) + u16_raw_(reinterpret_cast<std::uint16_t>(obj)); + #elif (QS_OBJ_PTR_SIZE == 4U) + u32_raw_(reinterpret_cast<std::uint32_t>(obj)); + #elif (QS_OBJ_PTR_SIZE == 8U) + u64_raw_(reinterpret_cast<std::uint64_t>(obj)); + #else + u32_raw_(reinterpret_cast<std::uint32_t>(obj)); + #endif +} + +//............................................................................ +void str_raw_(char const * s) noexcept { + std::uint8_t b = static_cast<std::uint8_t>(*s); + std::uint8_t chksum = priv_.chksum; // put in a temporary (register) + std::uint8_t * const buf = priv_.buf; // put in a temporary (register) + QSCtr head = priv_.head; // put in a temporary (register) + QSCtr const end = priv_.end; // put in a temporary (register) + QSCtr used = priv_.used; // put in a temporary (register) + + while (b != 0U) { + chksum += b; // update checksum + QS_INSERT_BYTE_(b) // ASCII characters don't need escaping + ++s; + b = static_cast<std::uint8_t>(*s); + ++used; + } + QS_INSERT_BYTE_(0U) // zero-terminate the string + ++used; + + priv_.head = head; // save the head + priv_.chksum = chksum; // save the checksum + priv_.used = used; // save # of used buffer space +} + +//............................................................................ +void u8_fmt_( + std::uint8_t const format, + std::uint8_t const d) noexcept +{ + std::uint8_t chksum = priv_.chksum; // put in a temporary (register) + std::uint8_t *const buf = priv_.buf; // put in a temporary (register) + QSCtr head = priv_.head; // put in a temporary (register) + QSCtr const end = priv_.end; // put in a temporary (register) + + priv_.used = (priv_.used + 2U); // 2 bytes about to be added + + QS_INSERT_ESC_BYTE_(format) + QS_INSERT_ESC_BYTE_(d) + + priv_.head = head; // save the head + priv_.chksum = chksum; // save the checksum +} + +//............................................................................ +void u16_fmt_( + std::uint8_t format, + std::uint16_t d) noexcept +{ + std::uint8_t chksum = priv_.chksum; // put in a temporary (register) + std::uint8_t * const buf = priv_.buf; // put in a temporary (register) + QSCtr head = priv_.head; // put in a temporary (register) + QSCtr const end = priv_.end; // put in a temporary (register) + + priv_.used = (priv_.used + 3U); // 3 bytes about to be added + + QS_INSERT_ESC_BYTE_(format) + + format = static_cast<std::uint8_t>(d); + QS_INSERT_ESC_BYTE_(format) + + d >>= 8U; + format = static_cast<std::uint8_t>(d); + QS_INSERT_ESC_BYTE_(format) + + priv_.head = head; // save the head + priv_.chksum = chksum; // save the checksum +} + +//............................................................................ +void u32_fmt_( + std::uint8_t format, + std::uint32_t d) noexcept +{ + std::uint8_t chksum = priv_.chksum; // put in a temporary (register) + std::uint8_t * const buf = priv_.buf; // put in a temporary (register) + QSCtr head = priv_.head; // put in a temporary (register) + QSCtr const end = priv_.end; // put in a temporary (register) + + priv_.used = (priv_.used + 5U); // 5 bytes about to be added + QS_INSERT_ESC_BYTE_(format) // insert the format byte + + for (std::uint_fast8_t i = 4U; i != 0U; --i) { + format = static_cast<std::uint8_t>(d); + QS_INSERT_ESC_BYTE_(format) + d >>= 8U; + } + + priv_.head = head; // save the head + priv_.chksum = chksum; // save the checksum +} + +//............................................................................ +void str_fmt_(char const * s) noexcept { + std::uint8_t b = static_cast<std::uint8_t>(*s); + std::uint8_t chksum = static_cast<std::uint8_t>( + priv_.chksum + static_cast<std::uint8_t>(STR_T)); + std::uint8_t * const buf = priv_.buf; // put in a temporary (register) + QSCtr head = priv_.head; // put in a temporary (register) + QSCtr const end = priv_.end; // put in a temporary (register) + QSCtr used = priv_.used; // put in a temporary (register) + + used += 2U; // the format byte and the terminating-0 + + QS_INSERT_BYTE_(static_cast<std::uint8_t>(STR_T)) + while (b != 0U) { + // ASCII characters don't need escaping + chksum += b; // update checksum + QS_INSERT_BYTE_(b) + ++s; + b = static_cast<std::uint8_t>(*s); + ++used; + } + QS_INSERT_BYTE_(0U) // zero-terminate the string + + priv_.head = head; // save the head + priv_.chksum = chksum; // save the checksum + priv_.used = used; // save # of used buffer space +} + +//............................................................................ +void mem_fmt_( + std::uint8_t const * blk, + std::uint8_t size) noexcept +{ + std::uint8_t b = static_cast<std::uint8_t>(MEM_T); + std::uint8_t chksum = priv_.chksum + b; + std::uint8_t * const buf = priv_.buf; // put in a temporary (register) + QSCtr head = priv_.head; // put in a temporary (register) + QSCtr const end = priv_.end; // put in a temporary (register) + + priv_.used = (priv_.used + size + 2U); // size+2 bytes to be added + + QS_INSERT_BYTE_(b) + QS_INSERT_ESC_BYTE_(size) + + // output the 'size' number of bytes + for (; size != 0U; --size) { + b = *blk; + QS_INSERT_ESC_BYTE_(b) + ++blk; + } + + priv_.head = head; // save the head + priv_.chksum = chksum; // save the checksum +} + +//............................................................................ +void sig_dict_pre_( + QSignal const sig, + void const * const obj, + char const * const name) noexcept +{ + QS_CRIT_STAT + QS_CRIT_ENTRY(); + QS_MEM_SYS(); + + beginRec_(static_cast<std::uint_fast8_t>(QS_SIG_DICT)); + QS_SIG_PRE_(sig); + QS_OBJ_PRE_(obj); + QS_STR_PRE_((*name == '&') ? &name[1] : name); + endRec_(); + + QS_MEM_APP(); + QS_CRIT_EXIT(); + onFlush(); +} + +//............................................................................ +void obj_dict_pre_( + void const * const obj, + char const * const name) noexcept +{ + QS_CRIT_STAT + QS_CRIT_ENTRY(); + QS_MEM_SYS(); + + beginRec_(static_cast<std::uint_fast8_t>(QS_OBJ_DICT)); + QS_OBJ_PRE_(obj); + QS_STR_PRE_((*name == '&') ? &name[1] : name); + endRec_(); + + QS_MEM_APP(); + QS_CRIT_EXIT(); + onFlush(); +} + +//............................................................................ +void obj_arr_dict_pre_( + void const * const obj, + std::uint_fast16_t const idx, + char const * const name) noexcept +{ + QS_CRIT_STAT + QS_CRIT_ENTRY(); + Q_REQUIRE_INCRIT(400, idx < 1000U); + QS_CRIT_EXIT(); + + // format idx into a char buffer as "xxx\0" + std::uint8_t idx_str[4]; + std::uint_fast16_t tmp = idx; + std::uint8_t i; + idx_str[3] = 0U; // zero-terminate + idx_str[2] = static_cast<std::uint8_t>( + static_cast<std::uint8_t>('0') + (tmp % 10U)); + tmp /= 10U; + idx_str[1] = static_cast<std::uint8_t>( + static_cast<std::uint8_t>('0') + (tmp % 10U)); + if (idx_str[1] == static_cast<std::uint8_t>('0')) { + i = 2U; + } + else { + tmp /= 10U; + idx_str[0] = static_cast<std::uint8_t>( + static_cast<std::uint8_t>('0') + (tmp % 10U)); + if (idx_str[0] == static_cast<std::uint8_t>('0')) { + i = 1U; + } + else { + i = 0U; + } + } + + std::uint8_t j = ((*name == '&') ? 1U : 0U); + + QS_CRIT_ENTRY(); + QS_MEM_SYS(); + + beginRec_(static_cast<std::uint_fast8_t>(QS_OBJ_DICT)); + QS_OBJ_PRE_(obj); + for (; name[j] != '\0'; ++j) { + QS_U8_PRE_(name[j]); + if (name[j] == '[') { + ++j; + break; + } + } + for (; idx_str[i] != 0U; ++i) { + QS_U8_PRE_(idx_str[i]); + } + // skip chars until ']' + for (; name[j] != '\0'; ++j) { + if (name[j] == ']') { + break; + } + } + for (; name[j] != '\0'; ++j) { + QS_U8_PRE_(name[j]); + } + QS_U8_PRE_(0U); // zero-terminate + endRec_(); + + QS_MEM_APP(); + QS_CRIT_EXIT(); + onFlush(); +} + +//............................................................................ +void fun_dict_pre_( + QSpyFunPtr fun, + char const * const name) noexcept +{ + QS_CRIT_STAT + QS_CRIT_ENTRY(); + QS_MEM_SYS(); + + beginRec_(static_cast<std::uint_fast8_t>(QS_FUN_DICT)); + QS_FUN_PRE_(fun); + QS_STR_PRE_((*name == '&') ? &name[1] : name); + endRec_(); + + QS_MEM_APP(); + QS_CRIT_EXIT(); + onFlush(); +} + +//............................................................................ +void usr_dict_pre_( + enum_t const rec, + char const * const name) noexcept +{ + QS_CRIT_STAT + QS_CRIT_ENTRY(); + QS_MEM_SYS(); + + beginRec_(static_cast<std::uint_fast8_t>(QS_USR_DICT)); + QS_U8_PRE_(rec); + QS_STR_PRE_(name); + endRec_(); + + QS_MEM_APP(); + QS_CRIT_EXIT(); + onFlush(); +} + +//............................................................................ +void enum_dict_pre_( + enum_t const value, + std::uint8_t const group, + char const * const name) noexcept +{ + QS_CRIT_STAT + QS_CRIT_ENTRY(); + QS_MEM_SYS(); + + beginRec_(static_cast<std::uint_fast8_t>(QS_ENUM_DICT)); + QS_2U8_PRE_(static_cast<std::uint8_t>(value), group); + QS_STR_PRE_(name); + endRec_(); + + QS_MEM_APP(); + QS_CRIT_EXIT(); + onFlush(); +} + +//............................................................................ +void assertion_pre_( + char const * const module, + int_t const id, + std::uint32_t const delay) noexcept +{ + // NOTE: called in a critical section + + beginRec_(static_cast<std::uint_fast8_t>(QS_ASSERT_FAIL)); + QS_TIME_PRE_(); + QS_U16_PRE_(id); + QS_STR_PRE_((module != nullptr) ? module : "?"); + endRec_(); + onFlush(); + for (std::uint32_t volatile ctr = delay; ctr > 0U; ) { + ctr = (ctr - 1U); + } + QS::onCleanup(); +} + +//............................................................................ +void crit_entry_pre_() noexcept { + beginRec_(static_cast<std::uint_fast8_t>(QS_QF_CRIT_ENTRY)); + QS_TIME_PRE_(); + priv_.critNest = (priv_.critNest + 1U); + QS_U8_PRE_(priv_.critNest); + endRec_(); +} + +//............................................................................ +void crit_exit_pre_() noexcept { + beginRec_(static_cast<std::uint_fast8_t>(QS_QF_CRIT_EXIT)); + QS_TIME_PRE_(); + QS_U8_PRE_(QS::priv_.critNest); + priv_.critNest = (priv_.critNest - 1U); + endRec_(); +} + +//............................................................................ +void isr_entry_pre_( + std::uint8_t const isrnest, + std::uint8_t const prio) noexcept +{ + beginRec_(static_cast<std::uint_fast8_t>(QS_QF_ISR_ENTRY)); + QS_TIME_PRE_(); + QS_U8_PRE_(isrnest); + QS_U8_PRE_(prio); + endRec_(); +} + +//............................................................................ +void isr_exit_pre_( + std::uint8_t const isrnest, + std::uint8_t const prio) noexcept +{ + beginRec_(static_cast<std::uint_fast8_t>(QS_QF_ISR_EXIT)); + QS_TIME_PRE_(); + QS_U8_PRE_(isrnest); + QS_U8_PRE_(prio); + endRec_(); +} + +//............................................................................ +void target_info_pre_(std::uint8_t const isReset) { + // NOTE: called in a critical section + + static constexpr std::uint8_t ZERO = static_cast<std::uint8_t>('0'); + static std::uint8_t const * const TIME = + reinterpret_cast<std::uint8_t const *>(&BUILD_TIME[0]); + static std::uint8_t const * const DATE = + reinterpret_cast<std::uint8_t const *>(&BUILD_DATE[0]); + + beginRec_(static_cast<std::uint_fast8_t>(QS_TARGET_INFO)); + u8_raw_(isReset); + + static union { + std::uint16_t u16; + std::uint8_t u8[2]; + } endian_test; + endian_test.u16 = 0x0102U; + // big endian ? add the 0x8000U flag + QS_U16_PRE_(((endian_test.u8[0] == 0x01U) + ? (0x8000U | QP_VERSION) + : QP_VERSION)); // target endianness + version number + + // send the object sizes... + u8_raw_(Q_SIGNAL_SIZE + | static_cast<std::uint8_t>(QF_EVENT_SIZ_SIZE << 4U)); + +#ifdef QF_EQUEUE_CTR_SIZE + u8_raw_(QF_EQUEUE_CTR_SIZE + | static_cast<std::uint8_t>(QF_TIMEEVT_CTR_SIZE << 4U)); +#else + QS::u8_raw_(static_cast<std::uint8_t>(QF_TIMEEVT_CTR_SIZE << 4U)); +#endif // ifdef QF_EQUEUE_CTR_SIZE + +#ifdef QF_MPOOL_CTR_SIZE + QS::u8_raw_(QF_MPOOL_SIZ_SIZE + | static_cast<std::uint8_t>(QF_MPOOL_CTR_SIZE << 4U)); +#else + QS::u8_raw_(0U); +#endif // ifdef QF_MPOOL_CTR_SIZE + + QS::u8_raw_(QS_OBJ_PTR_SIZE | (QS_FUN_PTR_SIZE << 4U)); + QS::u8_raw_(QS_TIME_SIZE); + + // send the limits... + QS::u8_raw_(QF_MAX_ACTIVE); + QS::u8_raw_(QF_MAX_EPOOL | (QF_MAX_TICK_RATE << 4U)); + + // send the build time in three bytes (sec, min, hour)... + QS::u8_raw_((10U * (TIME[6] - ZERO)) + (TIME[7] - ZERO)); + QS::u8_raw_((10U * (TIME[3] - ZERO)) + (TIME[4] - ZERO)); + if (BUILD_TIME[0] == static_cast<std::uint8_t>(' ')) { + QS::u8_raw_(TIME[1] - ZERO); + } + else { + QS::u8_raw_((10U * (TIME[0] - ZERO)) + (TIME[1] - ZERO)); + } + + // send the build date in three bytes (day, month, year) ... + if (BUILD_DATE[4] == static_cast<std::uint8_t>(' ')) { + QS::u8_raw_(DATE[5] - ZERO); + } + else { + QS::u8_raw_((10U * (DATE[4] - ZERO)) + (DATE[5] - ZERO)); + } + // convert the 3-letter month to a number 1-12 ... + std::uint8_t b; + switch (DATE[0] + DATE[1] + DATE[2]) { + case 'J' + 'a' +'n': + b = 1U; + break; + case 'F' + 'e' + 'b': + b = 2U; + break; + case 'M' + 'a' +'r': + b = 3U; + break; + case 'A' + 'p' + 'r': + b = 4U; + break; + case 'M' + 'a' + 'y': + b = 5U; + break; + case 'J' + 'u' + 'n': + b = 6U; + break; + case 'J' + 'u' + 'l': + b = 7U; + break; + case 'A' + 'u' + 'g': + b = 8U; + break; + case 'S' + 'e' + 'p': + b = 9U; + break; + case 'O' + 'c' + 't': + b = 10U; + break; + case 'N' + 'o' + 'v': + b = 11U; + break; + case 'D' + 'e' + 'c': + b = 12U; + break; + default: + b = 0U; + break; + } + QS::u8_raw_(b); // store the month + QS::u8_raw_((10U * (DATE[9] - ZERO)) + (DATE[10] - ZERO)); + QS::endRec_(); +} + +} // namespace QS +} // namespace QP + +//! @endcond + + + + #define QP_IMPL // this is QF/QK implementation +#include "qs_port.hpp" // QS port +#include "qs_pkg.hpp" // QS package-scope internal interface + +//============================================================================ +//! @cond INTERNAL + +namespace QP { +namespace QS { + +//${QS::QS-tx-64bit::u64_raw_} ............................................... +void u64_raw_(std::uint64_t d) noexcept { + std::uint8_t chksum = priv_.chksum; + std::uint8_t * const buf = priv_.buf; + QSCtr head = priv_.head; + QSCtr const end = priv_.end; + + priv_.used = (priv_.used + 8U); // 8 bytes are about to be added + for (std::int_fast8_t i = 8U; i != 0U; --i) { + std::uint8_t const b = static_cast<std::uint8_t>(d); + QS_INSERT_ESC_BYTE_(b) + d >>= 8U; + } + + priv_.head = head; // save the head + priv_.chksum = chksum; // save the checksum +} + +//${QS::QS-tx-64bit::u64_fmt_} ............................................... +void u64_fmt_( + std::uint8_t format, + std::uint64_t d) noexcept +{ + std::uint8_t chksum = priv_.chksum; + std::uint8_t * const buf = priv_.buf; + QSCtr head = priv_.head; + QSCtr const end = priv_.end; + + priv_.used = (priv_.used + 9U); // 9 bytes are about to be added + QS_INSERT_ESC_BYTE_(format) // insert the format byte + + for (std::int_fast8_t i = 8U; i != 0U; --i) { + format = static_cast<std::uint8_t>(d); + QS_INSERT_ESC_BYTE_(format) + d >>= 8U; + } + + priv_.head = head; // save the head + priv_.chksum = chksum; // save the checksum +} + +} // namespace QS +} // namespace QP + +//! @endcond + + + + #define QP_IMPL // this is QF/QK implementation +#include "qs_port.hpp" // QS port +#include "qs_pkg.hpp" // QS package-scope internal interface + +//============================================================================ +//! @cond INTERNAL + +namespace QP { +namespace QS { + +//${QS::QS-tx-fp::f32_fmt_} .................................................. +void f32_fmt_( + std::uint8_t format, + float32_t f) noexcept +{ + union F32Rep { + float32_t f; + std::uint32_t u; + } fu32; // the internal binary representation + std::uint8_t chksum = priv_.chksum; // put in a temporary (register) + std::uint8_t * const buf = priv_.buf; // put in a temporary (register) + QSCtr head = priv_.head; // put in a temporary (register) + QSCtr const end = priv_.end; // put in a temporary (register) + + fu32.f = f; // assign the binary representation + + priv_.used = (priv_.used + 5U); // 5 bytes about to be added + QS_INSERT_ESC_BYTE_(format) // insert the format byte + + for (std::uint_fast8_t i = 4U; i != 0U; --i) { + format = static_cast<std::uint8_t>(fu32.u); + QS_INSERT_ESC_BYTE_(format) + fu32.u >>= 8U; + } + + priv_.head = head; // save the head + priv_.chksum = chksum; // save the checksum +} + +//${QS::QS-tx-fp::f64_fmt_} .................................................. +void f64_fmt_( + std::uint8_t format, + float64_t d) noexcept +{ + union F64Rep { + float64_t d; + std::uint32_t u[2]; + } fu64; // the internal binary representation + std::uint8_t chksum = priv_.chksum; + std::uint8_t * const buf = priv_.buf; + QSCtr head = priv_.head; + QSCtr const end = priv_.end; + std::uint32_t i; + // static constant untion to detect endianness of the machine + static union U32Rep { + std::uint32_t u32; + std::uint8_t u8; + } const endian = { 1U }; + + fu64.d = d; // assign the binary representation + + // is this a big-endian machine? + if (endian.u8 == 0U) { + // swap fu64.u[0] <-> fu64.u[1]... + i = fu64.u[0]; + fu64.u[0] = fu64.u[1]; + fu64.u[1] = i; + } + + priv_.used = (priv_.used + 9U); // 9 bytes about to be added + QS_INSERT_ESC_BYTE_(format) // insert the format byte + + // output 4 bytes from fu64.u[0]... + for (i = 4U; i != 0U; --i) { + QS_INSERT_ESC_BYTE_(static_cast<std::uint8_t>(fu64.u[0])) + fu64.u[0] >>= 8U; + } + + // output 4 bytes from fu64.u[1]... + for (i = 4U; i != 0U; --i) { + QS_INSERT_ESC_BYTE_(static_cast<std::uint8_t>(fu64.u[1])) + fu64.u[1] >>= 8U; + } + + priv_.head = head; // update the head + priv_.chksum = chksum; // update the checksum +} + +} // namespace QS +} // namespace QP + +//! @endcond + + + + #define QP_IMPL // this is QP implementation +#include "qs_port.hpp" // QS port +#include "qs_pkg.hpp" // QS package-scope internal interface +#include "qp_pkg.hpp" // QP package-scope interface +#include "qsafe.h" // QP Functional Safety (FuSa) Subsystem + +//============================================================================ +//! @cond INTERNAL + +namespace { // unnamed local namespace + +Q_DEFINE_THIS_MODULE("qs_rx") + +//............................................................................ +#if (QS_OBJ_PTR_SIZE == 2U) + using QSObj = std::uint16_t; +#elif (QS_OBJ_PTR_SIZE == 4U) + using QSObj = std::uint32_t; +#elif (QS_OBJ_PTR_SIZE == 8U) + using QSObj = std::uint64_t; +#endif + +struct CmdVar { + std::uint32_t param1; + std::uint32_t param2; + std::uint32_t param3; + std::uint8_t idx; + std::uint8_t cmdId; +}; + +struct TickVar { + std::uint_fast8_t rate; +}; + +struct PeekVar { + std::uint16_t offs; + std::uint8_t size; + std::uint8_t num; + std::uint8_t idx; +}; + +struct PokeVar { + std::uint32_t data; + std::uint16_t offs; + std::uint8_t size; + std::uint8_t num; + std::uint8_t idx; + std::uint8_t fill; +}; + +struct FltVar { + std::uint8_t data[16]; + std::uint8_t idx; + std::uint8_t recId; // global/local +}; + +struct ObjVar { + QSObj addr; + std::uint8_t idx; + std::uint8_t kind; // see qs.hpp, enum QSpyObjKind + std::uint8_t recId; +}; + +struct EvtVar { + QP::QEvt *e; + std::uint8_t *p; + QP::QSignal sig; + std::uint16_t len; + std::uint8_t prio; + std::uint8_t idx; +}; + +// extended-state variables for the current QS-RX state +static struct ExtState { + union Variant { + CmdVar cmd; + TickVar tick; + PeekVar peek; + PokeVar poke; + FltVar flt; + ObjVar obj; + EvtVar evt; +#ifdef Q_UTEST + QP::QS::TProbe tp; +#endif // Q_UTEST + } var; + std::uint8_t state; + std::uint8_t esc; + std::uint8_t seq; + std::uint8_t chksum; +} l_rx; + +enum RxStateEnum : std::uint8_t { + ERROR_STATE, + WAIT4_SEQ, + WAIT4_REC, + WAIT4_INFO_FRAME, + WAIT4_CMD_ID, + WAIT4_CMD_PARAM1, + WAIT4_CMD_PARAM2, + WAIT4_CMD_PARAM3, + WAIT4_CMD_FRAME, + WAIT4_RESET_FRAME, + WAIT4_TICK_RATE, + WAIT4_TICK_FRAME, + WAIT4_PEEK_OFFS, + WAIT4_PEEK_SIZE, + WAIT4_PEEK_NUM, + WAIT4_PEEK_FRAME, + WAIT4_POKE_OFFS, + WAIT4_POKE_SIZE, + WAIT4_POKE_NUM, + WAIT4_POKE_DATA, + WAIT4_POKE_FRAME, + WAIT4_FILL_DATA, + WAIT4_FILL_FRAME, + WAIT4_FILTER_LEN, + WAIT4_FILTER_DATA, + WAIT4_FILTER_FRAME, + WAIT4_OBJ_KIND, + WAIT4_OBJ_ADDR, + WAIT4_OBJ_FRAME, + WAIT4_QUERY_KIND, + WAIT4_QUERY_FRAME, + WAIT4_EVT_PRIO, + WAIT4_EVT_SIG, + WAIT4_EVT_LEN, + WAIT4_EVT_PAR, + WAIT4_EVT_FRAME + +#ifdef Q_UTEST + , + WAIT4_TEST_SETUP_FRAME, + WAIT4_TEST_TEARDOWN_FRAME, + WAIT4_TEST_PROBE_DATA, + WAIT4_TEST_PROBE_ADDR, + WAIT4_TEST_PROBE_FRAME, + WAIT4_TEST_CONTINUE_FRAME +#endif // Q_UTEST +}; + +// internal helper functions... +static void rxParseData_(std::uint8_t const b) noexcept; +static void rxHandleGoodFrame_(std::uint8_t const state); +static void rxHandleBadFrame_(std::uint8_t const state) noexcept; +static void rxReportAck_(enum QP::QSpyRxRecords const recId) noexcept; +static void rxReportError_(std::uint8_t const code) noexcept; +static void rxReportDone_(enum QP::QSpyRxRecords const recId) noexcept; +static void queryCurrObj_(std::uint8_t obj_kind) noexcept; +static void rxPoke_() noexcept; + +// Internal QS-RX function to take a tran. in the QS-RX FSM +static inline void tran_(RxStateEnum const target) noexcept { + l_rx.state = static_cast<std::uint8_t>(target); +} + +} // unnamed namespace + +namespace QP { +namespace QS { +RxAttr rxPriv_; +} // namespace QS +} // namespace QP + +//! @endcond +//============================================================================ + +$define ${QS::QS-RX} + +//============================================================================ +//! @cond INTERNAL +namespace { // unnamed local namespace + +//............................................................................ +static void rxParseData_(std::uint8_t const b) noexcept { + switch (l_rx.state) { + case WAIT4_SEQ: { + ++l_rx.seq; + if (l_rx.seq != b) { // not the expected sequence? + rxReportError_(0x42U); + l_rx.seq = b; // update the sequence + } + tran_(WAIT4_REC); + break; + } + case WAIT4_REC: { + switch (b) { + case QP::QS_RX_INFO: + tran_(WAIT4_INFO_FRAME); + break; + case QP::QS_RX_COMMAND: + tran_(WAIT4_CMD_ID); + break; + case QP::QS_RX_RESET: + tran_(WAIT4_RESET_FRAME); + break; + case QP::QS_RX_TICK: + tran_(WAIT4_TICK_RATE); + break; + case QP::QS_RX_PEEK: + if (QP::QS::rxPriv_.currObj[QP::QS::AP_OBJ] != nullptr) { + l_rx.var.peek.offs = 0U; + l_rx.var.peek.idx = 0U; + tran_(WAIT4_PEEK_OFFS); + } + else { + rxReportError_( + static_cast<std::uint8_t>(QP::QS_RX_PEEK)); + tran_(ERROR_STATE); + } + break; + case QP::QS_RX_POKE: + case QP::QS_RX_FILL: + l_rx.var.poke.fill = + (b == static_cast<std::uint8_t>(QP::QS_RX_FILL)) + ? 1U + : 0U; + if (QP::QS::rxPriv_.currObj[QP::QS::AP_OBJ] != nullptr) { + l_rx.var.poke.offs = 0U; + l_rx.var.poke.idx = 0U; + tran_(WAIT4_POKE_OFFS); + } + else { + rxReportError_( + (l_rx.var.poke.fill != 0U) + ? static_cast<std::uint8_t>(QP::QS_RX_FILL) + : static_cast<std::uint8_t>(QP::QS_RX_POKE)); + tran_(ERROR_STATE); + } + break; + case QP::QS_RX_GLB_FILTER: // intentionally fall-through + case QP::QS_RX_LOC_FILTER: + l_rx.var.flt.recId = b; + tran_(WAIT4_FILTER_LEN); + break; + case QP::QS_RX_AO_FILTER: // intentionally fall-through + case QP::QS_RX_CURR_OBJ: + l_rx.var.obj.recId = b; + tran_(WAIT4_OBJ_KIND); + break; + case QP::QS_RX_QUERY_CURR: + l_rx.var.obj.recId = + static_cast<std::uint8_t>(QP::QS_RX_QUERY_CURR); + tran_(WAIT4_QUERY_KIND); + break; + case QP::QS_RX_EVENT: + tran_(WAIT4_EVT_PRIO); + break; + +#ifdef Q_UTEST + case QP::QS_RX_TEST_SETUP: + tran_(WAIT4_TEST_SETUP_FRAME); + break; + case QP::QS_RX_TEST_TEARDOWN: + tran_(WAIT4_TEST_TEARDOWN_FRAME); + break; + case QP::QS_RX_TEST_CONTINUE: + tran_(WAIT4_TEST_CONTINUE_FRAME); + break; + case QP::QS_RX_TEST_PROBE: + if (QP::QS::tstPriv_.tpNum + < static_cast<std::uint8_t>( + (sizeof(QP::QS::tstPriv_.tpBuf) + / sizeof(QP::QS::tstPriv_.tpBuf[0])))) + { + l_rx.var.tp.data = 0U; + l_rx.var.tp.idx = 0U; + tran_(WAIT4_TEST_PROBE_DATA); + } + else { // the # Test-Probes exceeded + rxReportError_( + static_cast<std::uint8_t>(QP::QS_RX_TEST_PROBE)); + tran_(ERROR_STATE); + } + break; +#endif // Q_UTEST + + default: + rxReportError_(0x43U); + tran_(ERROR_STATE); + break; + } + break; + } + case WAIT4_INFO_FRAME: { + // keep ignoring the data until a frame is collected + break; + } + case WAIT4_CMD_ID: { + 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; + tran_(WAIT4_CMD_PARAM1); + break; + } + case WAIT4_CMD_PARAM1: { + l_rx.var.cmd.param1 |= + (static_cast<std::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; + tran_(WAIT4_CMD_PARAM2); + } + break; + } + case WAIT4_CMD_PARAM2: { + l_rx.var.cmd.param2 |= + static_cast<std::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; + tran_(WAIT4_CMD_PARAM3); + } + break; + } + case WAIT4_CMD_PARAM3: { + l_rx.var.cmd.param3 |= + static_cast<std::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; + tran_(WAIT4_CMD_FRAME); + } + break; + } + case WAIT4_CMD_FRAME: { + // keep ignoring the data until a frame is collected + break; + } + case WAIT4_RESET_FRAME: { + // keep ignoring the data until a frame is collected + break; + } + case WAIT4_TICK_RATE: { + l_rx.var.tick.rate = static_cast<std::uint_fast8_t>(b); + tran_(WAIT4_TICK_FRAME); + break; + } + case WAIT4_TICK_FRAME: { + // keep ignoring the data until a frame is collected + break; + } + case WAIT4_PEEK_OFFS: { + if (l_rx.var.peek.idx == 0U) { + l_rx.var.peek.offs = static_cast<std::uint16_t>(b); + l_rx.var.peek.idx += 8U; + } + else { + l_rx.var.peek.offs |= static_cast<std::uint16_t>( + static_cast<std::uint16_t>(b) << 8U); + tran_(WAIT4_PEEK_SIZE); + } + break; + } + case WAIT4_PEEK_SIZE: { + if ((b == 1U) || (b == 2U) || (b == 4U)) { + l_rx.var.peek.size = b; + tran_(WAIT4_PEEK_NUM); + } + else { + rxReportError_(static_cast<std::uint8_t>(QP::QS_RX_PEEK)); + tran_(ERROR_STATE); + } + break; + } + case WAIT4_PEEK_NUM: { + l_rx.var.peek.num = b; + tran_(WAIT4_PEEK_FRAME); + break; + } + case WAIT4_PEEK_FRAME: { + // keep ignoring the data until a frame is collected + break; + } + case WAIT4_POKE_OFFS: { + if (l_rx.var.poke.idx == 0U) { + l_rx.var.poke.offs = static_cast<std::uint16_t>(b); + l_rx.var.poke.idx = 1U; + } + else { + l_rx.var.poke.offs |= static_cast<std::uint16_t>( + static_cast<std::uint16_t>(b) << 8U); + tran_(WAIT4_POKE_SIZE); + } + break; + } + case WAIT4_POKE_SIZE: { + if ((b == 1U) || (b == 2U) || (b == 4U)) { + l_rx.var.poke.size = b; + tran_(WAIT4_POKE_NUM); + } + else { + rxReportError_((l_rx.var.poke.fill != 0U) + ? static_cast<std::uint8_t>(QP::QS_RX_FILL) + : static_cast<std::uint8_t>(QP::QS_RX_POKE)); + tran_(ERROR_STATE); + } + break; + } + case WAIT4_POKE_NUM: { + if (b > 0U) { + l_rx.var.poke.num = b; + l_rx.var.poke.data = 0U; + l_rx.var.poke.idx = 0U; + tran_((l_rx.var.poke.fill != 0U) + ? WAIT4_FILL_DATA + : WAIT4_POKE_DATA); + } + else { + rxReportError_((l_rx.var.poke.fill != 0U) + ? static_cast<std::uint8_t>(QP::QS_RX_FILL) + : static_cast<std::uint8_t>(QP::QS_RX_POKE)); + tran_(ERROR_STATE); + } + break; + } + case WAIT4_FILL_DATA: { + l_rx.var.poke.data |= + static_cast<std::uint32_t>(b) << l_rx.var.poke.idx; + l_rx.var.poke.idx += 8U; + if ((l_rx.var.poke.idx >> 3U) == l_rx.var.poke.size) { + tran_(WAIT4_FILL_FRAME); + } + break; + } + case WAIT4_POKE_DATA: { + l_rx.var.poke.data |= + static_cast<std::uint32_t>(b) << l_rx.var.poke.idx; + l_rx.var.poke.idx += 8U; + if ((l_rx.var.poke.idx >> 3U) == l_rx.var.poke.size) { + rxPoke_(); + --l_rx.var.poke.num; + if (l_rx.var.poke.num == 0U) { + tran_(WAIT4_POKE_FRAME); + } + } + break; + } + case WAIT4_FILL_FRAME: { + // keep ignoring the data until a frame is collected + break; + } + case WAIT4_POKE_FRAME: { + // keep ignoring the data until a frame is collected + break; + } + case WAIT4_FILTER_LEN: { + if (b == static_cast<std::uint8_t>(sizeof(l_rx.var.flt.data))) { + l_rx.var.flt.idx = 0U; + tran_(WAIT4_FILTER_DATA); + } + else { + rxReportError_(l_rx.var.flt.recId); + tran_(ERROR_STATE); + } + break; + } + case 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)) { + tran_(WAIT4_FILTER_FRAME); + } + break; + } + case WAIT4_FILTER_FRAME: { + // keep ignoring the data until a frame is collected + break; + } + case WAIT4_OBJ_KIND: { + if (b <= static_cast<std::uint8_t>(QP::QS::SM_AO_OBJ)) { + l_rx.var.obj.kind = b; + l_rx.var.obj.addr = 0U; + l_rx.var.obj.idx = 0U; + tran_(WAIT4_OBJ_ADDR); + } + else { + rxReportError_(l_rx.var.obj.recId); + tran_(ERROR_STATE); + } + break; + } + case WAIT4_OBJ_ADDR: { + l_rx.var.obj.addr |= + static_cast<QSObj>(b) << l_rx.var.obj.idx; + l_rx.var.obj.idx += 8U; + if (l_rx.var.obj.idx + == (8U * static_cast<unsigned>(QS_OBJ_PTR_SIZE))) + { + tran_(WAIT4_OBJ_FRAME); + } + break; + } + case WAIT4_OBJ_FRAME: { + // keep ignoring the data until a frame is collected + break; + } + case WAIT4_QUERY_KIND: { + if (b < static_cast<std::uint8_t>(QP::QS::MAX_OBJ)) { + l_rx.var.obj.kind = b; + tran_(WAIT4_QUERY_FRAME); + } + else { + rxReportError_(l_rx.var.obj.recId); + tran_(ERROR_STATE); + } + break; + } + case WAIT4_QUERY_FRAME: { + // keep ignoring the data until a frame is collected + break; + } + case WAIT4_EVT_PRIO: { + l_rx.var.evt.prio = b; + l_rx.var.evt.sig = 0U; + l_rx.var.evt.idx = 0U; + tran_(WAIT4_EVT_SIG); + break; + } + case WAIT4_EVT_SIG: { + l_rx.var.evt.sig |= static_cast<QP::QSignal>( + static_cast<std::uint32_t>(b) << l_rx.var.evt.idx); + l_rx.var.evt.idx += 8U; + if (l_rx.var.evt.idx + == (8U *static_cast<unsigned>(Q_SIGNAL_SIZE))) + { + l_rx.var.evt.len = 0U; + l_rx.var.evt.idx = 0U; + tran_(WAIT4_EVT_LEN); + } + break; + } + case WAIT4_EVT_LEN: { + l_rx.var.evt.len |= static_cast<std::uint16_t>( + static_cast<unsigned>(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(QP::QEvt)) + <= static_cast<std::uint16_t>( + QP::QF::poolGetMaxBlockSize())) + { + // report Ack before generating any other QS records + rxReportAck_(QP::QS_RX_EVENT); + + l_rx.var.evt.e = QP::QF::newX_( + (static_cast<std::uint_fast16_t>(l_rx.var.evt.len) + + sizeof(QP::QEvt)), + 0U, // margin + static_cast<enum_t>(l_rx.var.evt.sig)); + // event allocated? + if (l_rx.var.evt.e != nullptr) { + l_rx.var.evt.p = + reinterpret_cast<std::uint8_t *>(l_rx.var.evt.e); + l_rx.var.evt.p = &l_rx.var.evt.p[sizeof(QP::QEvt)]; + if (l_rx.var.evt.len > 0U) { + tran_(WAIT4_EVT_PAR); + } + else { + tran_(WAIT4_EVT_FRAME); + } + } + else { + rxReportError_( + static_cast<std::uint8_t>(QP::QS_RX_EVENT)); + tran_(ERROR_STATE); + } + } + else { + rxReportError_( + static_cast<std::uint8_t>(QP::QS_RX_EVENT)); + tran_(ERROR_STATE); + } + } + break; + } + case WAIT4_EVT_PAR: { // event parameters + *l_rx.var.evt.p = b; + ++l_rx.var.evt.p; + --l_rx.var.evt.len; + if (l_rx.var.evt.len == 0U) { + tran_(WAIT4_EVT_FRAME); + } + break; + } + case WAIT4_EVT_FRAME: { + // keep ignoring the data until a frame is collected + break; + } + +#ifdef Q_UTEST + case WAIT4_TEST_SETUP_FRAME: { + // keep ignoring the data until a frame is collected + break; + } + case WAIT4_TEST_TEARDOWN_FRAME: { + // keep ignoring the data until a frame is collected + break; + } + case WAIT4_TEST_CONTINUE_FRAME: { + // keep ignoring the data until a frame is collected + break; + } + case WAIT4_TEST_PROBE_DATA: { + l_rx.var.tp.data |= + (static_cast<QP::QSFun>(b) << l_rx.var.tp.idx); + l_rx.var.tp.idx += 8U; + if (l_rx.var.tp.idx == (8U * sizeof(std::uint32_t))) { + l_rx.var.tp.addr = 0U; + l_rx.var.tp.idx = 0U; + tran_(WAIT4_TEST_PROBE_ADDR); + } + break; + } + case WAIT4_TEST_PROBE_ADDR: { + l_rx.var.tp.addr |= + (static_cast<std::uint32_t>(b) << l_rx.var.tp.idx); + l_rx.var.tp.idx += 8U; + if (l_rx.var.tp.idx + == (8U * static_cast<unsigned>(QS_FUN_PTR_SIZE))) + { + tran_(WAIT4_TEST_PROBE_FRAME); + } + break; + } + case WAIT4_TEST_PROBE_FRAME: { + // keep ignoring the data until a frame is collected + break; + } +#endif // Q_UTEST + + case ERROR_STATE: { + // keep ignoring the data until a good frame is collected + break; + } + default: { // unexpected or unimplemented state + rxReportError_(0x45U); + tran_(ERROR_STATE); + break; + } + } +} + +//............................................................................ +void rxHandleGoodFrame_(std::uint8_t const state) { + std::uint8_t i; + std::uint8_t *ptr; + + switch (state) { + case WAIT4_INFO_FRAME: { + // no need to report Ack or Done + QP::QS::target_info_pre_(0U); // send only Target info + break; + } + case WAIT4_RESET_FRAME: { + // no need to report Ack or Done, because Target resets + QP::QS::onReset(); // reset the Target + break; + } + case WAIT4_CMD_PARAM1: // intentionally fall-through + case WAIT4_CMD_PARAM2: // intentionally fall-through + case WAIT4_CMD_PARAM3: // intentionally fall-through + case WAIT4_CMD_FRAME: { + rxReportAck_(QP::QS_RX_COMMAND); + QP::QS::onCommand(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 + QP::QS::processTestEvts_(); // process all events produced + #endif // Q_UTEST != 0 +#endif // Q_UTEST + rxReportDone_(QP::QS_RX_COMMAND); + break; + } + case WAIT4_TICK_FRAME: { + rxReportAck_(QP::QS_RX_TICK); +#ifdef Q_UTEST + QP::QTimeEvt::tick1_( + static_cast<std::uint_fast8_t>(l_rx.var.tick.rate), + &QP::QS::rxPriv_); + #if Q_UTEST != 0 + QP::QS::processTestEvts_(); // process all events produced + #endif // Q_UTEST != 0 +#else + QP::QTimeEvt::tick( + static_cast<std::uint_fast8_t>(l_rx.var.tick.rate), + &QP::QS::rxPriv_); +#endif // Q_UTEST + rxReportDone_(QP::QS_RX_TICK); + break; + } + case WAIT4_PEEK_FRAME: { + // no need to report Ack or Done + QP::QS::beginRec_(static_cast<std::uint_fast8_t>( + QP::QS_PEEK_DATA)); + ptr = static_cast<std::uint8_t*>( + QP::QS::rxPriv_.currObj[QP::QS::AP_OBJ]); + ptr = &ptr[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); // number of 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_( + reinterpret_cast<std::uint16_t*>(ptr)[i]); + break; + case 4: + QS_U32_PRE_( + reinterpret_cast<std::uint32_t*>(ptr)[i]); + break; + default: + // intentionally empty + break; + } + } + QP::QS::endRec_(); + + QS_REC_DONE(); // user callback (if defined) + break; + } + case WAIT4_POKE_DATA: { + // received less than expected poke data items + rxReportError_(static_cast<std::uint8_t>(QP::QS_RX_POKE)); + break; + } + case WAIT4_POKE_FRAME: { + rxReportAck_(QP::QS_RX_POKE); + // no need to report done + break; + } + case WAIT4_FILL_FRAME: { + rxReportAck_(QP::QS_RX_FILL); + ptr = static_cast<std::uint8_t *>( + QP::QS::rxPriv_.currObj[QP::QS::AP_OBJ]); + ptr = &ptr[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] = + static_cast<std::uint8_t>(l_rx.var.poke.data); + break; + case 2: + reinterpret_cast<std::uint16_t *>(ptr)[i] = + static_cast<std::uint16_t>(l_rx.var.poke.data); + break; + case 4: + reinterpret_cast<std::uint32_t *>(ptr)[i] = + l_rx.var.poke.data; + break; + default: + // intentionally empty + break; + } + } + break; + } + case WAIT4_FILTER_FRAME: { + rxReportAck_(static_cast<enum QP::QSpyRxRecords>( + l_rx.var.flt.recId)); + + // apply the received filters + if (l_rx.var.flt.recId + == static_cast<std::uint8_t>(QP::QS_RX_GLB_FILTER)) + { + for (i = 0U; + i < static_cast<std::uint8_t>(sizeof(QP::QS::filt_.glb)); + ++i) + { + QP::QS::filt_.glb[i] = l_rx.var.flt.data[i]; + } + // leave the "not maskable" filters enabled, + // see qs.hpp, Miscellaneous QS records (not maskable) + QP::QS::filt_.glb[0] |= 0x01U; + QP::QS::filt_.glb[7] |= 0xFCU; + QP::QS::filt_.glb[8] |= 0x7FU; + + // never enable the last 3 records (0x7D, 0x7E, 0x7F) + QP::QS::filt_.glb[15] &= 0x1FU; + } + else if (l_rx.var.flt.recId + == static_cast<std::uint8_t>(QP::QS_RX_LOC_FILTER)) + { + for (i = 0U; i < Q_DIM(QP::QS::filt_.loc); ++i) { + QP::QS::filt_.loc[i] = l_rx.var.flt.data[i]; + } + // leave QS_ID == 0 always on + QP::QS::filt_.loc[0] |= 0x01U; + } + else { + rxReportError_(l_rx.var.flt.recId); + } + // no need to report Done + break; + } + case WAIT4_OBJ_FRAME: { + i = l_rx.var.obj.kind; + if (i < static_cast<std::uint8_t>(QP::QS::MAX_OBJ)) { + if (l_rx.var.obj.recId + == static_cast<std::uint8_t>(QP::QS_RX_CURR_OBJ)) + { + QP::QS::rxPriv_.currObj[i] = + reinterpret_cast<void *>(l_rx.var.obj.addr); + rxReportAck_(QP::QS_RX_CURR_OBJ); + } + else if (l_rx.var.obj.recId + == static_cast<std::uint8_t>(QP::QS_RX_AO_FILTER)) + { + if (l_rx.var.obj.addr != 0U) { + std::int_fast16_t const filter = + static_cast<std::int_fast16_t>( + reinterpret_cast<QP::QActive *>( + l_rx.var.obj.addr)->getPrio()); + QP::QS::locFilter_((i == 0) + ? filter + :-filter); + rxReportAck_(QP::QS_RX_AO_FILTER); + } + else { + rxReportError_(static_cast<enum_t>( + QP::QS_RX_AO_FILTER)); + } + } + else { + rxReportError_(l_rx.var.obj.recId); + } + } + // both SM and AO + else if (i == static_cast<std::uint8_t>(QP::QS::SM_AO_OBJ)) { + if (l_rx.var.obj.recId + == static_cast<std::uint8_t>(QP::QS_RX_CURR_OBJ)) + { + QP::QS::rxPriv_.currObj[QP::QS::SM_OBJ] + = reinterpret_cast<void *>(l_rx.var.obj.addr); + QP::QS::rxPriv_.currObj[QP::QS::AO_OBJ] + = reinterpret_cast<void *>(l_rx.var.obj.addr); + } + rxReportAck_( + static_cast<enum QP::QSpyRxRecords>(l_rx.var.obj.recId)); + } + else { + rxReportError_(l_rx.var.obj.recId); + } + break; + } + case WAIT4_QUERY_FRAME: { + 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 + QP::QS::onTestEvt(l_rx.var.evt.e); // "massage" the event +#endif // Q_UTEST + // use 'i' as status, 0 == success,no-recycle + i = 0U; + + if (l_rx.var.evt.prio == 0U) { // publish + QP::QActive::publish_(l_rx.var.evt.e, &QP::QS::rxPriv_, 0U); + } + else if (l_rx.var.evt.prio < QF_MAX_ACTIVE) { + if (!QP::QActive::registry_[l_rx.var.evt.prio]->POST_X( + l_rx.var.evt.e, + 0U, // margin + &QP::QS::rxPriv_)) + { + // failed QACTIVE_POST() recycles the event + i = 0x80U; // failure, no recycle + } + } + else if (l_rx.var.evt.prio == 255U) { + // dispatch to the current SM object + if (QP::QS::rxPriv_.currObj[QP::QS::SM_OBJ] != nullptr) { + // increment the ref-ctr to simulate the situation + // when the event is just retreived from a queue. + // This is expected for the following QF::gc() call. + QP::QEvt_refCtr_inc_(l_rx.var.evt.e); + + static_cast<QP::QAsm *>( + QP::QS::rxPriv_.currObj[QP::QS::SM_OBJ]) + ->dispatch(l_rx.var.evt.e, 0U); + i = 0x01U; // success, recycle + } + else { + i = 0x81U; // failure, recycle + } + } + else if (l_rx.var.evt.prio == 254U) { + // init the current SM object" + if (QP::QS::rxPriv_.currObj[QP::QS::SM_OBJ] != nullptr) { + // increment the ref-ctr to simulate the situation + // when the event is just retreived from a queue. + // This is expected for the following QF::gc() call. + QP::QEvt_refCtr_inc_(l_rx.var.evt.e); + + static_cast<QP::QAsm *>( + QP::QS::rxPriv_.currObj[QP::QS::SM_OBJ]) + ->init(l_rx.var.evt.e, 0U); + i = 0x01U; // success, recycle + } + else { + i = 0x81U; // failure, recycle + } + } + else if (l_rx.var.evt.prio == 253U) { + // post to the current AO + if (QP::QS::rxPriv_.currObj[QP::QS::AO_OBJ] != nullptr) { + if (!static_cast<QP::QActive *>( + QP::QS::rxPriv_.currObj[QP::QS::AO_OBJ])->POST_X( + l_rx.var.evt.e, + 0U, // margin + &QP::QS::rxPriv_)) + { + // failed QACTIVE_POST() recycles the event + i = 0x80U; // failure, no recycle + } + } + else { + i = 0x81U; // failure, recycle + } + } + else { + i = 0x81U; // failure, recycle + } + +#if (QF_MAX_EPOOL > 0U) + // recycle needed? + if ((i & 1U) != 0U) { + QP::QF::gc(l_rx.var.evt.e); + } +#endif + // failure? + if ((i & 0x80U) != 0U) { + rxReportError_(static_cast<std::uint8_t>(QP::QS_RX_EVENT)); + } + else { +#ifdef Q_UTEST + #if Q_UTEST != 0 + QP::QS::processTestEvts_(); // process all events produced + #endif // Q_UTEST != 0 +#endif // Q_UTEST + rxReportDone_(QP::QS_RX_EVENT); + } + break; + } + +#ifdef Q_UTEST + case WAIT4_TEST_SETUP_FRAME: { + rxReportAck_(QP::QS_RX_TEST_SETUP); + QP::QS::tstPriv_.tpNum = 0U; // clear Test-Probes + QP::QS::tstPriv_.testTime = 0U; //clear time tick + // don't clear current objects + QP::QS::onTestSetup(); // application-specific test setup + // no need to report Done + break; + } + case WAIT4_TEST_TEARDOWN_FRAME: { + rxReportAck_(QP::QS_RX_TEST_TEARDOWN); + QP::QS::onTestTeardown(); // application-specific test teardown + // no need to report Done + break; + } + case WAIT4_TEST_CONTINUE_FRAME: { + rxReportAck_(QP::QS_RX_TEST_CONTINUE); + QP::QS::rxPriv_.inTestLoop = false; // exit the QUTest loop + // no need to report Done + break; + } + case WAIT4_TEST_PROBE_FRAME: { + rxReportAck_(QP::QS_RX_TEST_PROBE); + Q_ASSERT_INCRIT(815, + QP::QS::tstPriv_.tpNum + < (sizeof(QP::QS::tstPriv_.tpBuf) + / sizeof(QP::QS::tstPriv_.tpBuf[0]))); + QP::QS::tstPriv_.tpBuf[QP::QS::tstPriv_.tpNum] = l_rx.var.tp; + ++QP::QS::tstPriv_.tpNum; + // no need to report Done + break; + } +#endif // Q_UTEST + + case ERROR_STATE: { + // keep ignoring all bytes until new frame + break; + } + default: { + rxReportError_(0x47U); + break; + } + } +} + +//............................................................................ +static void rxHandleBadFrame_(std::uint8_t const state) noexcept { + rxReportError_(0x50U); // error for all bad frames + switch (state) { + case WAIT4_EVT_FRAME: { + Q_ASSERT_INCRIT(910, l_rx.var.evt.e != nullptr); +#if (QF_MAX_EPOOL > 0U) + QP::QF::gc(l_rx.var.evt.e); // don't leak an allocated event +#endif + break; + } + default: { + // intentionally empty + break; + } + } +} + +//............................................................................ +static void rxReportAck_(enum QP::QSpyRxRecords const recId) noexcept { + QP::QS::beginRec_(static_cast<std::uint_fast8_t>(QP::QS_RX_STATUS)); + QS_U8_PRE_(recId); // record ID + QP::QS::endRec_(); + QS_REC_DONE(); // user callback (if defined) +} + +//............................................................................ +static void rxReportError_(std::uint8_t const code) noexcept { + QP::QS::beginRec_(static_cast<std::uint_fast8_t>(QP::QS_RX_STATUS)); + QS_U8_PRE_(0x80U | code); // error code + QP::QS::endRec_(); + QS_REC_DONE(); // user callback (if defined) +} + +//............................................................................ +static void rxReportDone_(enum QP::QSpyRxRecords const recId) noexcept { + QP::QS::beginRec_(static_cast<std::uint_fast8_t>(QP::QS_TARGET_DONE)); + QS_TIME_PRE_(); // timestamp + QS_U8_PRE_(recId); // record ID + QP::QS::endRec_(); + QS_REC_DONE(); // user callback (if defined) +} + +//............................................................................ +static void queryCurrObj_(std::uint8_t obj_kind) noexcept { + Q_REQUIRE_INCRIT(200, obj_kind < Q_DIM(QP::QS::rxPriv_.currObj)); + + if (QP::QS::rxPriv_.currObj[obj_kind] != nullptr) { + QP::QS::beginRec_(static_cast<std::uint_fast8_t>(QP::QS_QUERY_DATA)); + QS_TIME_PRE_(); // timestamp + QS_U8_PRE_(obj_kind); // object kind + QS_OBJ_PRE_(QP::QS::rxPriv_.currObj[obj_kind]); // object pointer + switch (obj_kind) { + case QP::QS::SM_OBJ: // intentionally fall through + case QP::QS::AO_OBJ: + QS_FUN_PRE_(reinterpret_cast<QP::QHsm *>( + QP::QS::rxPriv_.currObj[obj_kind])->getStateHandler()); + break; + case QP::QS::MP_OBJ: + QS_MPC_PRE_(reinterpret_cast<QP::QMPool *>( + QP::QS::rxPriv_.currObj[obj_kind])->getNFree()); + QS_MPC_PRE_(reinterpret_cast<QP::QMPool *>( + QP::QS::rxPriv_.currObj[obj_kind])->getNMin()); + break; + case QP::QS::EQ_OBJ: + QS_EQC_PRE_(reinterpret_cast<QP::QEQueue *>( + QP::QS::rxPriv_.currObj[obj_kind])->getNFree()); + QS_EQC_PRE_(reinterpret_cast<QP::QEQueue *>( + QP::QS::rxPriv_.currObj[obj_kind])->getNMin()); + break; + case QP::QS::TE_OBJ: + QS_OBJ_PRE_(reinterpret_cast<QP::QTimeEvt *>( + QP::QS::rxPriv_.currObj[obj_kind])->getAct()); + QS_TEC_PRE_(reinterpret_cast<QP::QTimeEvt *>( + QP::QS::rxPriv_.currObj[obj_kind])->getCtr()); + QS_TEC_PRE_(reinterpret_cast<QP::QTimeEvt *>( + QP::QS::rxPriv_.currObj[obj_kind])->getInterval()); + QS_SIG_PRE_(reinterpret_cast<QP::QTimeEvt *>( + QP::QS::rxPriv_.currObj[obj_kind])->sig); + QS_U8_PRE_ (reinterpret_cast<QP::QTimeEvt *>( + QP::QS::rxPriv_.currObj[obj_kind])->refCtr_); + break; + default: + // intentionally empty + break; + } + QP::QS::endRec_(); + QS_REC_DONE(); // user callback (if defined) + } + else { + rxReportError_(static_cast<std::uint8_t>(QP::QS_RX_AO_FILTER)); + } +} + +//............................................................................ +static void rxPoke_() noexcept { + std::uint8_t * ptr = + static_cast<std::uint8_t *>(QP::QS::rxPriv_.currObj[QP::QS::AP_OBJ]); + ptr = &ptr[l_rx.var.poke.offs]; + switch (l_rx.var.poke.size) { + case 1: + *ptr = static_cast<std::uint8_t>(l_rx.var.poke.data); + break; + case 2: + *reinterpret_cast<std::uint16_t *>(ptr) + = static_cast<std::uint16_t>(l_rx.var.poke.data); + break; + case 4: + *reinterpret_cast<std::uint32_t *>(ptr) = l_rx.var.poke.data; + break; + default: + Q_ERROR_ID(900); + break; + } + + l_rx.var.poke.data = 0U; + l_rx.var.poke.idx = 0U; + l_rx.var.poke.offs += static_cast<std::uint16_t>(l_rx.var.poke.size); +} + +} // unnamed namespace + +//! @endcond + + + + // only build when Q_UTEST is defined +#ifdef Q_UTEST + +#define QP_IMPL // this is QP implementation +#include "qp_port.hpp" // QP port +#include "qp_pkg.hpp" // QP package-scope interface +#include "qsafe.h" // QP Functional Safety (FuSa) Subsystem +#include "qs_port.hpp" // QS port +#include "qs_pkg.hpp" // QS package-scope internal interface + +$define ${QS::QUTest} + +//============================================================================ +//! @cond INTERNAL + +namespace QP { +namespace QS { + +TestAttr tstPriv_; + +//............................................................................ +void test_pause_() { + beginRec_(static_cast<std::uint_fast8_t>(QS_TEST_PAUSED)); + endRec_(); + onTestLoop(); +} + +//............................................................................ +std::uint32_t getTestProbe_(QSpyFunPtr const api) noexcept { + std::uint32_t data = 0U; + for (std::uint8_t i = 0U; i < tstPriv_.tpNum; ++i) { + if (tstPriv_.tpBuf[i].addr == reinterpret_cast<QSFun>(api)) { + data = tstPriv_.tpBuf[i].data; + + QS_CRIT_STAT + QS_CRIT_ENTRY(); + QS_MEM_SYS(); + QS::beginRec_(static_cast<std::uint_fast8_t>(QS_TEST_PROBE_GET)); + QS_TIME_PRE_(); // timestamp + QS_FUN_PRE_(api); // the calling API + QS_U32_PRE_(data); // the Test-Probe data + QS::endRec_(); + + QS_REC_DONE(); // user callback (if defined) + + --tstPriv_.tpNum; // one less Test-Probe + // move all remaining entries in the buffer up by one + for (std::uint8_t j = i; j < tstPriv_.tpNum; ++j) { + tstPriv_.tpBuf[j] = tstPriv_.tpBuf[j + 1U]; + } + QS_MEM_APP(); + QS_CRIT_EXIT(); + break; // we are done (Test-Probe retreived) + } + } + return data; +} + +//............................................................................ +QSTimeCtr onGetTime() { + return (++tstPriv_.testTime); +} + +} // namespace QS +} // namespace QP + +//............................................................................ +extern "C" { + +Q_NORETURN Q_onError(char const * const module, int_t const location) { + QP::QS::beginRec_(static_cast<std::uint_fast8_t>(QP::QS_ASSERT_FAIL)); + QS_TIME_PRE_(); + QS_U16_PRE_(location); + QS_STR_PRE_((module != nullptr) ? module : "?"); + QP::QS::endRec_(); + + QP::QS::onFlush(); // flush the assertion record to the host + QP::QS::onCleanup(); // cleanup after the failure + QP::QS::onReset(); // reset the target to prevent it from continuing + for (;;) { // onReset() should not return, but to ensure no-return... + } +} + +} // extern "C" + +//! @endcond +//============================================================================ + +// QP-stub for QUTest +// NOTE: The QP-stub is needed for unit testing QP applications, but might +// NOT be needed for testing QP itself. In that case, the build process +// can define Q_UTEST=0 to exclude the QP-stub from the build. +#if Q_UTEST != 0 + +// unnamed namespace for local definitions with internal linkage +namespace { +Q_DEFINE_THIS_MODULE("qutest") +} // unnamed namespace + +namespace QP { +namespace QS { + +void processTestEvts_() { + QS_TEST_PROBE_DEF(&QS::processTestEvts_) + + // return immediately (do nothing) for Test Probe != 0 + QS_TEST_PROBE(return;) + + while (tstPriv_.readySet.notEmpty()) { + std::uint_fast8_t const p = tstPriv_.readySet.findMax(); + QActive * const a = QActive::registry_[p]; + + QEvt const * const e = a->get_(); + a->dispatch(e, a->getPrio()); + #if (QF_MAX_EPOOL > 0U) + QF::gc(e); + #endif + if (a->getEQueue().isEmpty()) { // empty queue? + tstPriv_.readySet.remove(p); +#ifndef Q_UNSAFE + tstPriv_.readySet.verify_(&tstPriv_.readySet_dis); +#endif + } + } +} + +} // namespace QS +} // namespace QP + +$define ${QS::QUTest-stub} + +#endif // Q_UTEST != 0 + +#endif // def Q_UTEST + + + + #include "qstamp.hpp" + +namespace QP { +char const BUILD_DATE[12] = __DATE__; +char const BUILD_TIME[9] = __TIME__; +} // namespace QP + + + + diff --git a/qpcpp.qms b/qpcpp.qms new file mode 100644 index 0000000..225a6ec --- /dev/null +++ b/qpcpp.qms @@ -0,0 +1,58 @@ + + + + + 1 + 0 + 3 + 0 + + + + 2032128 + 0 + + + + + 0 + + + + + 0 + + + 0 + + + + + 0 + + + 0 + + + + + 0 + + + 0 + + + + + 0 + + + 0 + + + + + 0 + + + diff --git a/src/qassert.h b/src/qassert.h deleted file mode 100644 index 43aafec..0000000 --- a/src/qassert.h +++ /dev/null @@ -1,395 +0,0 @@ -/*$file${include::qassert.h} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv*/ -/* -* Model: qpc.qm -* File: ${include::qassert.h} -* -* This code has been generated by QM 5.2.5 . -* DO NOT EDIT THIS FILE MANUALLY. All your changes will be lost. -* -* This code is covered by the following QP license: -* License # : LicenseRef-QL-dual -* Issued to : Any user of the QP/C real-time embedded framework -* Framework(s) : qpc -* Support ends : 2023-12-31 -* License scope: -* -* Copyright (C) 2005 Quantum Leaps, LLC . -* -* 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: -* -* The terms of the closed source Quantum Leaps commercial licenses -* can be found at: -* -* Redistributions in source code must retain this top-level comment block. -* Plagiarizing this software to sidestep the license obligations is illegal. -* -* Contact information: -* -* -*/ -/*$endhead${include::qassert.h} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ -/*! @file -* @brief Customizable and memory-efficient Design by Contract (DbC) -* for embedded systems -* -* @note -* This header file can be used in C, C++, and mixed C/C++ programs. -* -* @note -* The preprocessor switch #Q_NASSERT disables checking assertions. -* However, it is generally **not** advisable to disable assertions, -* **especially** in the production code. Instead, the assertion -* handler Q_onAssert() should be very carefully designed and tested. -*/ -#ifndef QASSERT_H_ -#define QASSERT_H_ - -#ifdef __cplusplus - extern "C" { -#endif - -#ifndef Q_NASSERT -/*$declare${DbC::active} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv*/ - -/*${DbC::active::Q_DEFINE_THIS_MODULE} .....................................*/ -/*! Define the user-specified module name for assertions in this file. -* -* @details -* Macro to be placed at the top of each C/C++ module to define the -* single instance of the module name string to be used in reporting -* assertions in this module. This macro takes the user-supplied parameter -* `name_` instead of `__FILE__` to precisely control the name of the -* module. -* -* @param[in] name_ string constant representing the module name -* -* @note -* This macro should **not** be terminated by a semicolon. -*/ -#define Q_DEFINE_THIS_MODULE(name_) \ - static char const Q_this_module_[] = name_; - -/*${DbC::active::Q_ASSERT_ID} ..............................................*/ -/*! General-purpose assertion with user-specified ID number. -* -* @details -* Evaluates the Boolean expression `expr_` and does nothing else when -* it evaluates to 'true'. However, when `expr_` evaluates to 'false', -* the Q_ASSERT_ID() macro calls the no-return function Q_onAssert(). -* -* @param[in] id_ ID number (unique within the module) of the assertion -* @param[in] expr_ Boolean expression to check -* -* @attention -* When assertions are disabled (by defining the ::Q_NASSERT macro), the -* Q_ASSERT_ID() macro expands to nothing, and consequently the Boolean -* expression `expr_` is **not** evaluated and the callback function -* Q_onAssert() is **not** called. -*/ -#define Q_ASSERT_ID(id_, expr_) ((expr_) \ - ? ((void)0) : Q_onAssert(&Q_this_module_[0], (id_))) - -/*${DbC::active::Q_ERROR_ID} ...............................................*/ -/*! Assertion with user-specified ID for a wrong path through the code -* -* @details -* Calls the Q_onAssert() callback if ever executed. This assertion -* takes the user-supplied parameter `id_` to identify the location of -* this assertion within the file. This avoids the volatility of using -* line numbers, which change whenever a line of code is added or removed -* upstream from the assertion. -* -* @param[in] id_ ID number (unique within the module) of the assertion -* -* @note -* Does noting if assertions are disabled with the ::Q_NASSERT switch. -*/ -#define Q_ERROR_ID(id_) Q_onAssert(&Q_this_module_[0], (id_)) - -/*${DbC::active::Q_ALLEGE_ID} ..............................................*/ -/*! General purpose assertion with user-specified ID number that -* **always** evaluates the `expr_` expression. -* -* @details -* Like the Q_ASSERT_ID() macro, except it **always** evaluates the -* `expr_` expression even when assertions are disabled with the -* ::Q_NASSERT macro. However, when the ::Q_NASSERT macro is defined, the -* Q_onAssert() callback is **not** called, even if `expr_` evaluates -* to FALSE. -* -* @param[in] id_ ID number (unique within the module) of the assertion -* @param[in] expr_ Boolean expression to check -*/ -#define Q_ALLEGE_ID(id_, expr_) Q_ASSERT_ID((id_), (expr_)) -/*$enddecl${DbC::active} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ -#else -/*$declare${DbC::inactive} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv*/ - -/*${DbC::inactive::Q_DEFINE_THIS_MODULE} ...................................*/ -/*! inactive version of Q_DEFINE_THIS_MODULE() */ -#define Q_DEFINE_THIS_MODULE(name_) - -/*${DbC::inactive::Q_ASSERT_ID} ............................................*/ -/*! inactive version of Q_ASSERT_ID() */ -#define Q_ASSERT_ID(id_, expr_) ((void)0) - -/*${DbC::inactive::Q_ERROR_ID} .............................................*/ -/*! inactive version of Q_ERROR_ID() */ -#define Q_ERROR_ID(id_) ((void)0) - -/*${DbC::inactive::Q_ALLEGE_ID} ............................................*/ -/*! inactive version of Q_ALLEGE_ID() -* -* @attention -* The expression `expr_` **is** evaluated, even though assertion -* callback Q_onAssert() is NOT called when `expr_` evaluates to -* false. -*/ -#define Q_ALLEGE_ID(id_, expr_) ((void)(expr_)) -/*$enddecl${DbC::inactive} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ -#endif - -/*$declare1${DbC} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv*/ - -/*${DbC::Q_DEFINE_THIS_FILE} ...............................................*/ -/*! Define the file name (with `__FILE__`) for assertions in this file -* -* @details -* Macro to be placed at the top of each C/C++ module to define the -* single instance of the file name string to be used in reporting -* assertions in this module. -* -* @note -* The file name string literal is defined by means of the standard -* preprocessor macro `__FILE__`. However, please note that, depending -* on the compiler, the `__FILE__` macro might contain the whole path name -* to the file, which might be inconvenient to log assertions. -* -* @attention -* This macro should **not** be terminated by a semicolon. -* -* @sa Q_DEFINE_THIS_MODULE() -*/ -#define Q_DEFINE_THIS_FILE Q_DEFINE_THIS_MODULE(__FILE__) - -/*${DbC::Q_ASSERT} .........................................................*/ -/*! General-purpose assertion (with __LINE__ used as location in the file) -* -* @details -* Equivalent to Q_ASSERT_ID(), except it uses __LINE__ to identify the -* assertion within a file. -* -* @param[in] expr_ Boolean expression to check -* -* @sa Q_ASSERT_ID() -*/ -#define Q_ASSERT(expr_) Q_ASSERT_ID(__LINE__, (expr_)) - -/*${DbC::Q_ERROR} ..........................................................*/ -/*! Assertion for a wrong path through the code -* -* @details -* Calls the Q_onAssert() callback if ever executed. -* -* @note -* This macro identifies the problem location with the line number, -* which might change as the code is modified. -* -* @sa Q_ERROR_ID() -*/ -#define Q_ERROR() Q_ERROR_ID(__LINE__) - -/*${DbC::Q_REQUIRE_ID} .....................................................*/ -/*! Assertion for checking **preconditions**. -* -* @details -* Equivalent to Q_ASSERT_ID(), except the name provides a better -* documentation of the intention of this assertion. -* -* @param[in] id_ ID number (unique within the module) of the assertion -* @param[in] expr_ Boolean expression -*/ -#define Q_REQUIRE_ID(id_, expr_) Q_ASSERT_ID((id_), (expr_)) - -/*${DbC::Q_REQUIRE} ........................................................*/ -/*! Assertion for checking preconditions (based on __LINE__). -* -* @details -* Equivalent to Q_ASSERT(), except the name provides a better documentation -* of the intention of this assertion. -* -* @param[in] expr_ Boolean expression -*/ -#define Q_REQUIRE(expr_) Q_ASSERT(expr_) - -/*${DbC::Q_ENSURE_ID} ......................................................*/ -/*! Assertion for checking postconditions. -* -* @details -* Equivalent to Q_ASSERT_ID(), except the name provides a better documentation -* of the intention of this assertion. -* -* @param[in] id_ ID number (unique within the module) of the assertion -* @param[in] expr_ Boolean expression -*/ -#define Q_ENSURE_ID(id_, expr_) Q_ASSERT_ID((id_), (expr_)) - -/*${DbC::Q_ENSURE} .........................................................*/ -/*! Assertion for checking postconditions. -* -* @details -* Equivalent to Q_ASSERT(), except the name provides a better documentation -* of the intention of this assertion. -* -* @param[in] expr_ Boolean expression -*/ -#define Q_ENSURE(expr_) Q_ASSERT(expr_) - -/*${DbC::Q_INVARIANT_ID} ...................................................*/ -/*! Assertion for checking invariants. -* -* @details -* Equivalent to Q_ASSERT_ID(), except the name provides a better -* documentation of the intention of this assertion. -* -* @param[in] id_ ID number (unique within the module) of the assertion -* @param[in] expr_ Boolean expression -*/ -#define Q_INVARIANT_ID(id_, expr_) Q_ASSERT_ID((id_), (expr_)) - -/*${DbC::Q_INVARIANT} ......................................................*/ -/*! Assertion for checking invariants. -* -* @details -* Equivalent to Q_ASSERT(), except the name provides a better documentation -* of the intention of this assertion. -* -* @param[in] expr_ Boolean expression -*/ -#define Q_INVARIANT(expr_) Q_ASSERT(expr_) - -/*${DbC::Q_ALLEGE} .........................................................*/ -/*! General purpose assertion with user-specified ID number that -* **always** evaluates the `expr_` expression. -* -* @details -* Equivalent to Q_ALLEGE_ID(), except it identifies the problem location -* with the line number `__LINE__`, which might change as the code is modified. -* -* @param[in] expr_ Boolean expression to check -* -* @sa Q_ALLEGE_ID() -*/ -#define Q_ALLEGE(expr_) Q_ALLEGE_ID(__LINE__, (expr_)) - -/*${DbC::Q_ASSERT_STATIC} ..................................................*/ -/*! Static (compile-time) assertion. -* -* @details -* This type of assertion deliberately causes a compile-time error when -* the `expr_` Boolean expression evaluates to FALSE. The macro exploits -* the fact that in C/C++ a dimension of an array cannot be negative. -* The compile-time assertion has no runtime side effects. -* -* @param[in] expr_ Compile-time Boolean expression -* -* @note -* The static assertion macro is provided for backwards compatibility with -* older C standards. Newer C11 supports `_Static_assert()`, which should -* be used instead of Q_ASSERT_STATIC(). -*/ -#define Q_ASSERT_STATIC(expr_) extern char Q_static_assert_[(expr_) ? 1 : -1] - -/*${DbC::Q_NORETURN} .......................................................*/ -#ifndef Q_NORETURN -/*! No-return function specifier for the Q_onAssert() callback function. -* -* @details -* If the `Q_NORETURN` macro is undefined, the default definition uses -* the C99 specifier `_Noreturn`. -* -* @note -* The `Q_NORETURN` macro can be defined in the QP port (typically in -* `qep_port.h` or `qep_port.hpp`). If such definition is porvided -* the default won't be used. -* -* @trace -* @tr{PQA01_4} -*/ -#define Q_NORETURN _Noreturn void -#endif /* ndef Q_NORETURN */ - -/*${DbC::int_t} ............................................................*/ -#ifndef QP_VERSION -/*! typedef for assertions-ids and line numbers in assertions. -* -* @details -* This typedef specifies integer type for exclusive use in assertions. -* Use of this type, rather than plain 'int', is in compliance -* with the MISRA-C 2012 Dir 4.6 (adv). -*/ -typedef int int_t; -#endif /* ndef QP_VERSION */ - -/*${DbC::Q_onAssert} .......................................................*/ -/*! Callback function invoked in case of an assertion failure. -* -* @details -* This callback function needs to be defined in the application to perform -* any corrective action after a non-recoverable error has been detected. -* The Q_onAssert() function is the last line of defense after the -* system failure and its implementation shouild be very **carefully** -* designed and **tested** under various fault conditions, including but -* not limited to: stack overflow, stack corruption, or calling Q_onAssert() -* from an interrupt. -* -* @param[in] module name of the file/module in which the assertion failed -* (constant, zero-terminated C string) -* @param[in] location location of the assertion within the module. This could -* be a line number or a user-specified ID-number. -* -* @returns -* This callback function should **not return** (see ::Q_NORETURN), -* as continuation after an assertion failure does not make sense. -* -* @note -* During debugging, Q_onAssert() is an ideal place to put a breakpoint. -* For deployment, tt is typically a **bad idea** to implement Q_onAssert() -* as an endless loop that ties up the CPU (denial of service). -* -* Called by the following: Q_ASSERT_ID(), Q_ERROR_ID(), Q_REQUIRE_ID(), -* Q_ENSURE_ID(), Q_INVARIANT_ID() and Q_ALLEGE_ID() as well as: -* Q_ASSERT(), Q_ERROR(), Q_REQUIRE(), Q_ENSURE(), Q_INVARIANT(), -* and Q_ALLEGE(). -* -* @trace -* @tr{PQA01_4} -*/ -Q_NORETURN Q_onAssert( - char const * module, - int_t location); - -/*${DbC::Q_DIM} ............................................................*/ -#ifndef QP_VERSION -/*! Helper macro to calculate static dimension of a 1-dim `array_` -* -* @param array_ 1-dimensional array -* @returns the length of the array (number of elements it can hold) -*/ -#define Q_DIM(array_) (sizeof(array_) / sizeof((array_)[0U])) -#endif /* ndef QP_VERSION */ -/*$enddecl${DbC} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ - -#ifdef __cplusplus - } -#endif - -#endif /* QASSERT_H_ */ diff --git a/src/qep.hpp b/src/qep.hpp deleted file mode 100644 index e578321..0000000 --- a/src/qep.hpp +++ /dev/null @@ -1,982 +0,0 @@ -//$file${include::qep.hpp} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv -// -// Model: qpcpp.qm -// File: ${include::qep.hpp} -// -// This code has been generated by QM 5.2.5 . -// DO NOT EDIT THIS FILE MANUALLY. All your changes will be lost. -// -// This code is covered by the following QP license: -// License # : LicenseRef-QL-dual -// Issued to : Any user of the QP/C++ real-time embedded framework -// Framework(s) : qpcpp -// Support ends : 2023-12-31 -// License scope: -// -// Copyright (C) 2005 Quantum Leaps, LLC . -// -// 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: -// -// The terms of the closed source Quantum Leaps commercial licenses -// can be found at: -// -// Redistributions in source code must retain this top-level comment block. -// Plagiarizing this software to sidestep the license obligations is illegal. -// -// Contact information: -// -// -// -//$endhead${include::qep.hpp} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -//! @file -//! @brief QEP/C++ platform-independent public interface. -//! -//! @tr{RQP001} @tr{RQP101} - -#ifndef QEP_HPP_ -#define QEP_HPP_ - -//============================================================================ -//! The current QP version as an unsigned number -// -// @details -// ::QP_VERSION is a decimal constant, where XX is a 1-digit or 2-digit -// major version number, Y is a 1-digit minor version number, and Z is -// a 1-digit release number. -// -#define QP_VERSION 722U - -//! The current QP version as a zero terminated string literal. -// -// @details -// ::QP_VERSION_STR is of the form "XX.Y.Z", where XX is a 1-or 2-digit -// major version number, Y is a 1-digit minor version number, and Z is -// a 1-digit release number. -// -#define QP_VERSION_STR "7.2.2" - -//! Encrypted current QP release (7.2.2) and date (2023-03-01) -#define QP_RELEASE 0x76BAD85DU - -//============================================================================ -// Global namespace... -//$declare${glob-types} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv - -//${glob-types::int_t} ....................................................... -//! alias for line numbers in assertions and return from QF::run() -using int_t = int; - -//${glob-types::enum_t} ...................................................... -//! alias for enumerations used for event signals -using enum_t = int; - -//${glob-types::float32_t} ................................................... -//! alias for 32-bit IEEE 754 floating point numbers -//! -//! @note -//! QP does not use floating-point types anywhere in the internal -//! implementation, except in QS software tracing, where utilities for -//! output of floating-point numbers are provided for application-specific -//! trace records. -using float32_t = float; - -//${glob-types::float64_t} ................................................... -//! alias for 64-bit IEEE 754 floating point numbers -//! -//! @note -//! QP does not use floating-point types anywhere in the internal -//! implementation, except in QS software tracing, where utilities for -//! output of floating-point numbers are provided for application-specific -//! trace records. -using float64_t = double; -//$enddecl${glob-types} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -//$declare${QEP-config} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv - -//${QEP-config::Q_SIGNAL_SIZE} ............................................... -#ifndef Q_SIGNAL_SIZE -//! The size (in bytes) of the signal of an event. Valid values: -//! 1U, 2U, or 4U; default 2U -//! -//! @details -//! This macro can be defined in the QEP port file (qep_port.hpp) to -//! configure the QP::QSignal type. When the macro is not defined, the -//! default of 2 bytes is applied. -#define Q_SIGNAL_SIZE 2U -#endif // ndef Q_SIGNAL_SIZE -//$enddecl${QEP-config} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -//============================================================================ -//$declare${QEP} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv -namespace QP { - -//${QEP::versionStr[]} ....................................................... -//! the current QP version number string based on QP_VERSION_STR -constexpr char const versionStr[] {QP_VERSION_STR}; - -//${QEP::QSignal} ............................................................ -#if (Q_SIGNAL_SIZE == 2U) -//! QSignal represents the signal of an event -//! -//! @details -//! The relationship between an event and a signal is as follows. A signal -//! in UML is the specification of an asynchronous stimulus that triggers -//! reactions, and as such is an essential part of an event. (The signal -//! conveys the type of the occurrence--what happened?) However, an event -//! can also contain additional quantitative information about the -//! occurrence in form of event parameters. -using QSignal = std::uint16_t; -#endif // (Q_SIGNAL_SIZE == 2U) - -//${QEP::QSignal} ............................................................ -#if (Q_SIGNAL_SIZE == 1U) -using QSignal = std::uint8_t; -#endif // (Q_SIGNAL_SIZE == 1U) - -//${QEP::QSignal} ............................................................ -#if (Q_SIGNAL_SIZE == 4U) -using QSignal = std::uint32_t; -#endif // (Q_SIGNAL_SIZE == 4U) - -//${QEP::QEvt} ............................................................... -//! Event class -//! -//! @details -//! QP::QEvt represents events without parameters and serves as the -//! base class for derivation of events with parameters. -//! -//! @note -//! When #Q_EVT_CTOR and #Q_EVT_VIRTUAL are NOT defined, the QP::QEvt is -//! a POD (Plain Old Data). Otherwise, it is a class with constructors -//! and virtual destructor. -//! -//! @usage -//! The following example illustrates how to add an event parameter by -//! inheriting from the QP::QEvt class. -//! @include qep_qevt.cpp -class QEvt { -public: - - //! signal of the event instance - //! @tr{RQP002} - QSignal sig; - - //! pool ID (0 for static, immutable event) - //! @tr{RQP003} - std::uint8_t poolId_; - - //! reference counter (only used for dynamic, mutable events) - //! @tr{RQP003} - std::uint8_t volatile refCtr_; - -public: - -#ifdef Q_EVT_CTOR - //! QP::QEvt constructor when the macro #Q_EVT_CTOR is defined - explicit QEvt(QSignal s) noexcept - : sig(s) - // poolId_/refCtr_ intentionally uninitialized - {} -#endif // def Q_EVT_CTOR - -#ifdef Q_EVT_CTOR - //! QP::QEvt constructor (overload for static, immutable events) - constexpr QEvt( - QSignal s, - std::uint8_t /* dummy */) noexcept - : sig(s), - poolId_(0U), - refCtr_(0U) - {} -#endif // def Q_EVT_CTOR - -#ifdef Q_EVT_VIRTUAL - //! QP::QEvt virtual destructor when the macro #Q_EVT_VIRTUAL is defined - virtual ~QEvt() noexcept { - // empty - } -#endif // def Q_EVT_VIRTUAL -}; // class QEvt - -//${QEP::QState} ............................................................. -//! Type returned from state-handler functions -using QState = std::uint_fast8_t; - -//${QEP::QStateHandler} ...................................................... -//! Pointer to state-handler function -using QStateHandler = QState (*)(void * const me, QEvt const * const e); - -//${QEP::QActionHandler} ..................................................... -//! Pointer to an action-handler function -using QActionHandler = QState (*)(void * const me); - -//${QEP::QXThread} ........................................................... -//! forward declaration -class QXThread; - -//${QEP::QXThreadHandler} .................................................... -//! Pointer to an extended thread-handler function -using QXThreadHandler = void (*)(QXThread * const me); - -//${QEP::QMState} ............................................................ -//! State object for the QP::QMsm class (QM State Machine). -//! -//! @details -//! This class groups together the attributes of a QP::QMsm state, such as -//! the parent state (state nesting), the associated state handler function -//! and the exit action handler function. These attributes are used inside -//! the QP::QMsm::dispatch() and QP::QMsm::init() functions. -//! -//! @attention -//! The QP::QMState class is only intended for the QM code generator and -//! should not be used in hand-crafted code. -struct QMState { - QMState const * superstate; //!< superstate of this state - QStateHandler const stateHandler; //!< state handler function - QActionHandler const entryAction; //!< entry action handler function - QActionHandler const exitAction; //!< exit action handler function - QActionHandler const initAction; //!< init action handler function -}; - -//${QEP::QMTranActTable} ..................................................... -//! Transition-Action Table for the QP::QMsm State Machine. -struct QMTranActTable { - QMState const * target; //!< target of the transition - QActionHandler const act[1]; //!< array of actions -}; - -//${QEP::QHsmAttr} ........................................................... -//! Attribute of for the QP::QHsm class (Hierarchical State Machine) -//! -//! @details -//! This union represents possible values stored in the 'state' and 'temp' -//! attributes of the QP::QHsm class. -union QHsmAttr { - QStateHandler fun; //!< pointer to a state handler function - QActionHandler act; //!< pointer to an action-handler function - QXThreadHandler thr; //!< pointer to an thread-handler function - QMState const *obj; //!< pointer to QMState object - QMTranActTable const *tatbl; //!< transition-action table -}; - -//${QEP::Q_USER_SIG} ......................................................... -//! Type returned from state-handler functions -constexpr enum_t Q_USER_SIG {4}; - -//${QEP::QHsm} ............................................................... -//! Hierarchical State Machine abstract base class (ABC) -//! -//! @details -//! QP::QHsm represents a Hierarchical State Machine (HSM) with full support -//! for hierarchical nesting of states, entry/exit actions, and initial -//! transitions in any composite state. QHsm inherits QP::QMsm without adding -//! new attributes, so it takes the same amount of RAM as QP::QMsm.
-//! -//! QP::QHsm is also the base class for the QP::QMsm state machine, which -//! provides better efficiency, but requires the use of the QM modeling tool -//! to generate code. -//! -//! @note -//! QP::QHsm is not intended to be instantiated directly, but rather serves as -//! the base class for derivation of state machines in the application code. -//! -//! @usage -//! The following example illustrates how to derive a state machine class -//! from QP::QHsm. -//! @include qep_qhsm.cpp -class QHsm { -private: - - //! current active state (the state-variable) - QHsmAttr m_state; - - //! temporary: transition chain, target state, etc. - QHsmAttr m_temp; - -public: - - //! Maximum nesting depth of states in HSM - static constexpr std::int_fast8_t MAX_NEST_DEPTH_{6}; - -private: - - // friends... - friend class QMsm; - friend class QActive; - friend class QMActive; - friend class QXThread; - friend class QXMutex; - friend class QXSemaphore; - -#ifdef Q_UTEST - friend class QHsmDummy; -#endif // def Q_UTEST - -#ifdef Q_UTEST - friend class QActiveDummy; -#endif // def Q_UTEST - -public: - - //! Reserved signals by the HSM-style state machine - //! implementation strategy. - enum ReservedSig : QSignal { - Q_EMPTY_SIG, //!< signal to execute the default case - Q_ENTRY_SIG, //!< signal for entry actions - Q_EXIT_SIG, //!< signal for exit actions - Q_INIT_SIG //!< signal for nested initial transitions - }; - - //! All possible return values from state-handlers - enum QStateRet : std::uint_fast8_t { - // unhandled and need to "bubble up" - Q_RET_SUPER, //!< event passed to superstate to handle - Q_RET_SUPER_SUB, //!< event passed to submachine superstate - Q_RET_UNHANDLED, //!< event unhandled due to a guard - - // handled and do not need to "bubble up" - Q_RET_HANDLED, //!< event handled (internal transition) - Q_RET_IGNORED, //!< event silently ignored (bubbled up to top) - - // entry/exit - Q_RET_ENTRY, //!< state entry action executed - Q_RET_EXIT, //!< state exit action executed - - // no side effects - Q_RET_NULL, //!< return value without any effect - - // transitions need to execute transition-action table in QP::QMsm - Q_RET_TRAN, //!< regular transition - Q_RET_TRAN_INIT, //!< initial transition in a state or submachine - Q_RET_TRAN_EP, //!< entry-point transition into a submachine - - // transitions that additionally clobber QHsm.m_state - Q_RET_TRAN_HIST, //!< transition to history of a given state - Q_RET_TRAN_XP //!< exit-point transition out of a submachine - }; - -protected: - - //! protected constructor of QHsm - explicit QHsm(QStateHandler const initial) noexcept; - -public: - -#ifdef Q_HSM_XTOR - //! virtual destructor - virtual ~QHsm() noexcept { - // empty - } -#endif // def Q_HSM_XTOR - - //! Executes the top-most initial transition in QP::QHsm - //! - //! @details - //! Executes the top-most initial transition in a HSM. - //! - //! @param[in] e pointer to an extra parameter (might be NULL) - //! @param[in] qs_id QS-id of this state machine (for QS local filter) - //! - //! @note - //! Must be called exactly __once__ before the QP::QHsm::dispatch(). - //! - //! @tr{RQP103} @tr{RQP120I} @tr{RQP120D} - virtual void init( - void const * const e, - std::uint_fast8_t const qs_id); - - //! overloaded init(qs_id) - //! - //! @details - //! Executes the top-most initial transition in a HSM (overloaded). - //! - //! @param[in] qs_id QS-id of this state machine (for QS local filter) - //! - //! @attention - //! QHsm::init() must be called exactly **once** before - //! QHsm::dispatch() - virtual void init(std::uint_fast8_t const qs_id) { - init(nullptr, qs_id); - } - - //! Dispatches an event to QP::QHsm - //! - //! @details - //! Dispatches an event for processing to a hierarchical state machine. - //! The event dispatching represents one run-to-completion (RTC) step. - //! - //! @param[in] e pointer to the event to be dispatched to the HSM - //! @param[in] qs_id QS-id of this state machine (for QS local filter) - //! - //! @attention - //! This state machine must be initialized by calling QP::QHsm::init() - //! exactly **once** before calling QP::QHsm::dispatch(). - //! - //! @tr{RQP103} - //! @tr{RQP120A} @tr{RQP120B} @tr{RQP120C} @tr{RQP120D} @tr{RQP120E} - virtual void dispatch( - QEvt const * const e, - std::uint_fast8_t const qs_id); - - //! The top-state handler - //! - //! @details - //! The QHsm::top() state handler is the ultimate root of state - //! hierarchy in all HSMs derived from QP::QHsm. - //! - //! @param[in] me pointer to the HSM instance - //! @param[in] e pointer to the event to be dispatched to the HSM - //! - //! @returns - //! Always returns #Q_RET_IGNORED, which means that the top state ignores - //! all events. - //! - //! @note - //! The parameters to this state handler are not used. They are provided - //! for conformance with the state-handler function signature - //! QP::QStateHandler. - //! - //! @tr{RQP103} @tr{RQP120T} - static QState top( - void * const me, - QEvt const * const e) noexcept; - - //! Obtain the current state (state handler function) - //! - //! @note used in the QM code generation - QStateHandler state() const noexcept { - return m_state.fun; - } - -#ifdef Q_SPY - //! Get the current state handler of the QP::QHsm - virtual QStateHandler getStateHandler() noexcept { - return m_state.fun; - } -#endif // def Q_SPY - - //! Tests if a given state is part of the current active state - //! configuration - //! - //! @details - //! Tests if a state machine derived from QHsm is-in a given state. - //! - //! @note - //! For a HSM, to "be in a state" means also to be in a superstate of - //! of the state. - //! - //! @param[in] s pointer to the state-handler function to be tested - //! - //! @returns - //! 'true' if the HSM is in the `state` and 'false' otherwise - //! - //! @tr{RQP103} - //! @tr{RQP120S} - bool isIn(QStateHandler const s) noexcept; - - //! Obtain the current active child state of a given parent - //! - //! @note used in the QM code generation - QStateHandler childState(QStateHandler const parent) noexcept; - -protected: - - //! Helper function to specify a state transition - QState tran(QStateHandler const target) noexcept { - m_temp.fun = target; - return Q_RET_TRAN; - } - - //! Helper function to specify a transition to history - QState tran_hist(QStateHandler const hist) noexcept { - m_temp.fun = hist; - return Q_RET_TRAN_HIST; - } - - //! Helper function to specify the superstate of a given state - QState super(QStateHandler const superstate) noexcept { - m_temp.fun = superstate; - return Q_RET_SUPER; - } - - //! Helper function to specify a regular state transition - //! in a QM state-handler - QState qm_tran(void const * const tatbl) noexcept { - m_temp.tatbl = static_cast(tatbl); - return Q_RET_TRAN; - } - - //! Helper function to specify an initial state transition - //! in a QM state-handler - QState qm_tran_init(void const * const tatbl) noexcept { - m_temp.tatbl = static_cast(tatbl); - return Q_RET_TRAN_INIT; - } - - //! Helper function to specifiy a transition to history - //! in a QM state-handler - QState qm_tran_hist( - QMState const * const hist, - void const * const tatbl) noexcept - { - m_state.obj = hist; - m_temp.tatbl = static_cast(tatbl); - return Q_RET_TRAN_HIST; - } - - //! Helper function to specify a transition to an entry point - //! to a submachine state in a QM state-handler - QState qm_tran_ep(void const * const tatbl) noexcept { - m_temp.tatbl = static_cast(tatbl); - return Q_RET_TRAN_EP; - } - - //! Helper function to specify a transition to an exit point - //! from a submachine state in a QM state-handler - QState qm_tran_xp( - QActionHandler const xp, - void const * const tatbl) noexcept - { - m_state.act = xp; - m_temp.tatbl = static_cast(tatbl); - return Q_RET_TRAN_XP; - } - -#ifdef Q_SPY - //! Helper function to specify a state entry in a QM state-handler - QState qm_entry(QMState const * const s) noexcept { - m_temp.obj = s; - return Q_RET_ENTRY; - } -#endif // def Q_SPY - -#ifndef Q_SPY - //! Helper function to specify a state entry in a QM state-handler - QState qm_entry(QMState const * const s) noexcept { - static_cast(s); // unused parameter - return Q_RET_ENTRY; - } -#endif // ndef Q_SPY - -#ifdef Q_SPY - //! Helper function to specify a state exit in a QM state-handler - QState qm_exit(QMState const * const s) noexcept { - m_temp.obj = s; - return Q_RET_EXIT; - } -#endif // def Q_SPY - -#ifndef Q_SPY - //! Helper function to specify a state exit in a QM state-handler - QState qm_exit(QMState const * const s) noexcept { - static_cast(s); // unused parameter - return Q_RET_EXIT; - } -#endif // ndef Q_SPY - - //! Helper function to specify a submachine exit in a QM state-handler - QState qm_sm_exit(QMState const * const s) noexcept { - m_temp.obj = s; - return Q_RET_EXIT; - } - - //! Helper function to call in a QM state-handler when it passes - //! the event to the host submachine state to handle an event. - QState qm_super_sub(QMState const * const s) noexcept { - m_temp.obj = s; - return Q_RET_SUPER_SUB; - } - -private: - - //! @details - //! helper function to execute transition sequence in a hierarchical - //! state machine (HSM). - //! - //! @param[in,out] path array of pointers to state-handler functions - //! to execute the entry actions - //! @param[in] qs_id QS-id of this state machine (for QS local filter) - //! - //! @returns - //! the depth of the entry path stored in the `path` parameter. - //! - //! @tr{RQP103} - //! @tr{RQP120E} @tr{RQP120F} - std::int_fast8_t hsm_tran( - QStateHandler (&path)[MAX_NEST_DEPTH_], - std::uint_fast8_t const qs_id); -}; // class QHsm - -//${QEP::QMsm} ............................................................... -//! QM State Machine implementation strategy -//! -//! @details -//! QP::QMsm (QM State Machine) provides a more efficient state machine -//! implementation strategy than QHsm, but requires the use of the QM -//! modeling tool, but are the fastest and need the least run-time -//! support (the smallest event-processor taking up the least code space). -//! -//! @note -//! QP::QMsm is not intended to be instantiated directly, but rather serves as -//! the base class for derivation of state machines in the application code. -//! -//! @usage -//! The following example illustrates how to derive a state machine class -//! from QP::QMsm: -//! @include qep_qmsm.cpp -class QMsm : public QP::QHsm { -private: - - //! the top state object for the QP::QMsm - static QMState const msm_top_s; - - // friends... - friend class QMActive; - -public: - - //! maximum depth of implemented entry levels for transitions to history - static constexpr std::int_fast8_t MAX_ENTRY_DEPTH_{4}; - -public: - - //! Performs the second step of SM initialization by triggering - //! the top-most initial transition. - //! - //! @details - //! Executes the top-most initial transition in a MSM. - //! - //! @param[in] e pointer to an extra parameter (might be nullptr) - //! @param[in] qs_id QS-id of this state machine (for QS local filter) - //! - //! @attention - //! QMsm::init() must be called exactly **once** before QMsm::dispatch() - void init( - void const * const e, - std::uint_fast8_t const qs_id) override; - -protected: - - //! Protected constructor - //! @details - //! Performs the first step of initialization by assigning the initial - //! pseudostate to the currently active state of the state machine. - //! - //! @param[in] initial the top-most initial transition for the MSM. - //! - //! @note - //! The constructor is protected to prevent direct instantiating of the - //! QP::QMsm objects. This class is intended for subclassing only. - //! - //! @sa - //! The QP::QMsm example illustrates how to use the QMsm constructor - //! in the constructor initializer list of the derived state machines. - explicit QMsm(QStateHandler const initial) noexcept - : QHsm(initial) - { - m_state.obj = &msm_top_s; - m_temp.fun = initial; - } - -public: - - //! overloaded init(qs_id) - //! - //! @details - //! Executes the top-most initial transition in a MSM (overloaded). - //! - //! @param[in] qs_id QS-id of this state machine (for QS local filter) - //! - //! @attention - //! QMsm::init() must be called exactly **once** before QMsm::dispatch() - void init(std::uint_fast8_t const qs_id) override { - QMsm::init(nullptr, qs_id); - } - - //! Dispatches an event to a MSM - //! - //! @details - //! Dispatches an event for processing to a meta state machine (MSM). - //! The processing of an event represents one run-to-completion (RTC) step. - //! - //! @param[in] e pointer to the event to be dispatched to the MSM - //! @param[in] qs_id QS-id of this state machine (for QS local filter) - //! - //! @note - //! Must be called after QMsm::init(). - void dispatch( - QEvt const * const e, - std::uint_fast8_t const qs_id) override; - - //! Tests if a given state is part of the active state configuration - //! - //! @details - //! Tests if a state machine derived from QMsm is-in a given state. - //! - //! @note - //! For a MSM, to "be-in" a state means also to "be-in" a superstate of - //! of the state. - //! - //! @param[in] st pointer to the QMState object that corresponds to the - //! tested state. - //! @returns - //! 'true' if the MSM is in the \c st and 'false' otherwise - bool isInState(QMState const * const st) const noexcept; - -private: - - //! disallow inherited top() function in QP::QMsm and subclasses - //! @sa QMsm::msm_top_s - static QStateHandler top( - void * const me, - QEvt const * const e) noexcept = delete; - -public: - - //! Return the current active state object (read only) - QMState const * stateObj() const noexcept { - return m_state.obj; - } - - //! Obtain the current active child state of a given parent (read only) - //! - //! @details - //! Finds the child state of the given `parent`, such that this child - //! state is an ancestor of the currently active state. The main purpose - //! of this function is to support **shallow history** transitions in - //! state machines derived from QHsm. - //! - //! @param[in] parent pointer to the state-handler function - //! - //! @returns - //! the child of a given `parent` state, which is an ancestor of the - //! currently active state - //! - //! @note - //! this function is designed to be called during state transitions, so it - //! does not necessarily start in a stable state configuration. - //! However, the function establishes stable state configuration upon exit. - //! - //! @tr{RQP103} - //! @tr{RQP120H} - QMState const * childStateObj(QMState const * const parent) const noexcept; - -protected: - -#ifdef Q_SPY - //! Get the current state handler of the QMsm - QStateHandler getStateHandler() noexcept override { - return m_state.obj->stateHandler; - } -#endif // def Q_SPY - -private: - - //! Internal helper function to execute a transition-action table - //! - //! @details - //! Helper function to execute transition sequence in a tran-action table. - //! - //! @param[in] tatbl pointer to the transition-action table - //! @param[in] qs_id QS-id of this state machine (for QS local filter) - //! - //! @returns - //! the status of the last action from the transition-action table. - //! - //! @note - //! This function is for internal use inside the QEP event processor and - //! should **not** be called directly from the applications. - QState execTatbl_( - QMTranActTable const * const tatbl, - std::uint_fast8_t const qs_id); - - //! Internal helper function to exit current state to transition source - //! - //! @details - //! Helper function to exit the current state configuration to the - //! transition source, which is a hierarchical state machine might be a - //! superstate of the current state. - //! - //! @param[in] s pointer to the current state - //! @param[in] ts pointer to the transition source state - //! @param[in] qs_id QS-id of this state machine (for QS local filter) - void exitToTranSource_( - QMState const * s, - QMState const * const ts, - std::uint_fast8_t const qs_id); - - //! Internal helper function to enter state history - //! - //! @details - //! Static helper function to execute the segment of transition to history - //! after entering the composite state and - //! - //! @param[in] hist pointer to the history substate - //! @param[in] qs_id QS-id of this state machine (for QS local filter) - //! - //! @returns - //! ::Q_RET_INIT, if an initial transition has been executed in the last - //! entered state or ::Q_RET_NULL if no such transition was taken. - QState enterHistory_( - QMState const * const hist, - std::uint_fast8_t const qs_id); -}; // class QMsm - -} // namespace QP -//$enddecl${QEP} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -//============================================================================ -//$declare${QEP-macros} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv - -//${QEP-macros::Q_STATE_DECL} ................................................ -//! Macro to generate a declaration of a state-handler, state-caller and -//! a state-object for a given state in a subclass of QP::QHsm. -#define Q_STATE_DECL(state_) \ - QP::QState state_ ## _h(QP::QEvt const * const e); \ - static QP::QState state_(void * const me, QP::QEvt const * const e) - -//${QEP-macros::Q_STATE_DEF} ................................................. -//! Macro to generate a declaration of a state-handler, state-caller and -//! a state-object for a given state in a subclass of QP::QHsm. -#define Q_STATE_DEF(subclass_, state_) \ - QP::QState subclass_::state_(void * const me, QP::QEvt const * const e) { \ - return static_cast(me)->state_ ## _h(e); } \ - QP::QState subclass_::state_ ## _h(QP::QEvt const * const e) - -//${QEP-macros::Q_HANDLED} ................................................... -//! Macro to specify that the event was handled -#define Q_HANDLED() (Q_RET_HANDLED) - -//${QEP-macros::Q_UNHANDLED} ................................................. -//! Macro to specify that the event was NOT handled -//! due to a guard condition evaluating to 'false' -#define Q_UNHANDLED() (Q_RET_UNHANDLED) - -//${QEP-macros::Q_EVT_CAST} .................................................. -//! Perform downcast of an event onto a subclass of QEvt `class_` -//! -//! @details -//! This macro encapsulates the downcast of QEvt pointers, which violates -//! MISRA-C 2004 rule 11.4(advisory). This macro helps to localize this -//! deviation. -#define Q_EVT_CAST(subclass_) (static_cast(e)) - -//${QEP-macros::Q_STATE_CAST} ................................................ -//! Macro to perform casting to QStateHandler. -//! -//! @details -//! This macro encapsulates the cast of a specific state handler function -//! pointer to QStateHandler, which violates MISRA-C 2004 rule 11.4(advisory). -//! This macro helps to localize this deviation. -#define Q_STATE_CAST(handler_) \ - (reinterpret_cast(handler_)) - -//${QEP-macros::Q_ACTION_CAST} ............................................... -//! Macro to perform casting to QActionHandler. -//! -//! @details -//! This macro encapsulates the cast of a specific action handler function -//! pointer to QActionHandler, which violates MISRA-C2004 rule 11.4(advisory). -//! This macro helps to localize this deviation. -#define Q_ACTION_CAST(act_) \ - (reinterpret_cast(act_)) - -//${QEP-macros::QM_STATE_DECL} ............................................... -//! Macro to generate a declaration of a state-handler, state-caller and -//! a state-object for a given state in a subclass of QP::QMsm. -#define QM_STATE_DECL(state_) \ - QP::QState state_ ## _h(QP::QEvt const * const e); \ - static QP::QState state_(void * const me, QP::QEvt const * const e); \ - static QP::QMState const state_ ## _s - -//${QEP-macros::QM_SM_STATE_DECL} ............................................ -//! Macro to generate a declaration of a state-handler, state-caller and -//! a state-object for a given *submachine* state in a subclass of QP::QMsm. -#define QM_SM_STATE_DECL(subm_, state_) \ - QP::QState state_ ## _h(QP::QEvt const * const e);\ - static QP::QState state_(void * const me, QP::QEvt const * const e); \ - static SM_ ## subm_ const state_ ## _s - -//${QEP-macros::QM_ACTION_DECL} .............................................. -//! Macro to generate a declaration of an action-handler and action-caller -//! in a subclass of QP::QMsm. -#define QM_ACTION_DECL(action_) \ - QP::QState action_ ## _h(); \ - static QP::QState action_(void * const me) - -//${QEP-macros::QM_STATE_DEF} ................................................ -//! Macro to generate a definition of a state-caller and state-handler -//! for a given state in a subclass of QP::QMsm. -#define QM_STATE_DEF(subclass_, state_) \ - QP::QState subclass_::state_(void * const me, QP::QEvt const * const e) { \ - return static_cast(me)->state_ ## _h(e); } \ - QP::QState subclass_::state_ ## _h(QP::QEvt const * const e) - -//${QEP-macros::QM_ACTION_DEF} ............................................... -//! Macro to generate a definition of an action-caller and action-handler -//! in a subclass of QP::QMsm. -#define QM_ACTION_DEF(subclass_, action_) \ - QP::QState subclass_::action_(void * const me) { \ - return static_cast(me)->action_ ## _h(); } \ - QP::QState subclass_::action_ ## _h() - -//${QEP-macros::QM_HANDLED} .................................................. -//! Macro for a QM action-handler when it handles the event. -#define QM_HANDLED() (Q_RET_HANDLED) - -//${QEP-macros::QM_UNHANDLED} ................................................ -//! Macro for a QM action-handler when it does not handle the event -//! due to a guard condition evaluating to false. -#define QM_UNHANDLED() (Q_RET_HANDLED) - -//${QEP-macros::QM_SUPER} .................................................... -//! Macro for a QM action-handler when it passes the event to the superstate -#define QM_SUPER() (Q_RET_SUPER) - -//${QEP-macros::QM_STATE_NULL} ............................................... -//! Macro to provide strictly-typed zero-state to use for submachines. -//! Applicable to suclasses of QP::QMsm. -#define QM_STATE_NULL (nullptr) - -//${QEP-macros::Q_ACTION_NULL} ............................................... -//! Macro to provide strictly-typed zero-action to terminate action lists -//! in the transition-action-tables in QP::QMsm. -#define Q_ACTION_NULL (nullptr) - -//${QEP-macros::Q_UNUSED_PAR} ................................................ -//! Helper macro to clearly mark unused parameters of functions. -#define Q_UNUSED_PAR(par_) (static_cast(par_)) - -//${QEP-macros::Q_DIM} ....................................................... -//! Helper macro to calculate static dimension of a 1-dim `array_` -//! -//! @param[in] array_ 1-dimensional array -//! -//! @returns -//! the length of the array (number of elements it can hold) -#define Q_DIM(array_) (sizeof(array_) / sizeof((array_)[0U])) - -//${QEP-macros::Q_UINT2PTR_CAST} ............................................. -//! Perform cast from unsigned integer `uint_` to pointer of type `type_` -//! -//! @details -//! This macro encapsulates the cast to (type_ *), which QP ports or -//! application might use to access embedded hardware registers. -//! Such uses can trigger PC-Lint "Note 923: cast from int to pointer" -//! and this macro helps to encapsulate this deviation. -#define Q_UINT2PTR_CAST(type_, uint_) (reinterpret_cast(uint_)) - -//${QEP-macros::QEVT_INITIALIZER} ............................................ -//! Initializer of static constant QEvt instances -//! -//! @details -//! This macro encapsulates the ugly casting of enumerated signals -//! to QSignal and constants for QEvt.poolID and QEvt.refCtr_. -#define QEVT_INITIALIZER(sig_) { static_cast(sig_), 0U, 0U } -//$enddecl${QEP-macros} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -#endif // QEP_HPP_ diff --git a/src/qep_hsm.cpp b/src/qep_hsm.cpp deleted file mode 100644 index 4afcf5e..0000000 --- a/src/qep_hsm.cpp +++ /dev/null @@ -1,628 +0,0 @@ -//$file${src::qf::qep_hsm.cpp} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv -// -// Model: qpcpp.qm -// File: ${src::qf::qep_hsm.cpp} -// -// This code has been generated by QM 5.2.5 . -// DO NOT EDIT THIS FILE MANUALLY. All your changes will be lost. -// -// This code is covered by the following QP license: -// License # : LicenseRef-QL-dual -// Issued to : Any user of the QP/C++ real-time embedded framework -// Framework(s) : qpcpp -// Support ends : 2023-12-31 -// License scope: -// -// Copyright (C) 2005 Quantum Leaps, LLC . -// -// 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: -// -// The terms of the closed source Quantum Leaps commercial licenses -// can be found at: -// -// Redistributions in source code must retain this top-level comment block. -// Plagiarizing this software to sidestep the license obligations is illegal. -// -// Contact information: -// -// -// -//$endhead${src::qf::qep_hsm.cpp} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -//! @file -//! @brief QP::QHsm implementation -//! -//! @tr{RQP103} @tr{RQP104} @tr{RQP120} @tr{RQP130} - -#define QP_IMPL // this is QP implementation -#include "qep_port.hpp" // QEP port -#ifdef Q_SPY // QS software tracing enabled? - #include "qs_port.hpp" // QS port - #include "qs_pkg.hpp" // QS facilities for pre-defined trace records -#else - #include "qs_dummy.hpp" // disable the QS software tracing -#endif // Q_SPY -#include "qassert.h" // QP embedded systems-friendly assertions - -//============================================================================ -//$skip${QP_VERSION} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv -// Check for the minimum required QP version -#if (QP_VERSION < 700U) || (QP_VERSION != ((QP_RELEASE^4294967295U) % 0x3E8U)) -#error qpcpp version 7.0.0 or higher required -#endif -//$endskip${QP_VERSION} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -//$define${QEP::versionStr[]} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv -namespace QP { - -} // namespace QP -//$enddef${QEP::versionStr[]} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -//============================================================================ -// unnamed namespace for local definitions with internal linkage -namespace { -Q_DEFINE_THIS_MODULE("qep_hsm") - -//---------------------------------------------------------------------------- -//! Immutable events corresponding to the reserved signals. -//! -//! @details -//! Static, immutable reserved events that the QEP event processor sends -//! to state handler functions of QHsm-style state machine to execute entry -//! actions, exit actions, and initial transitions. -//! -static QP::QEvt const l_reservedEvt_[4] { -#ifdef Q_EVT_CTOR // Is the QEvt constructor provided? - QP::QEvt(static_cast(QP::QHsm::Q_EMPTY_SIG), 0U), - QP::QEvt(static_cast(QP::QHsm::Q_ENTRY_SIG), 0U), - QP::QEvt(static_cast(QP::QHsm::Q_EXIT_SIG), 0U), - QP::QEvt(static_cast(QP::QHsm::Q_INIT_SIG), 0U) -#else // QEvt is a POD (Plain Old Datatype) - { static_cast(QP::QHsm::Q_EMPTY_SIG), 0U, 0U }, - { static_cast(QP::QHsm::Q_ENTRY_SIG), 0U, 0U }, - { static_cast(QP::QHsm::Q_EXIT_SIG), 0U, 0U }, - { static_cast(QP::QHsm::Q_INIT_SIG), 0U, 0U } -#endif -}; - -//---------------------------------------------------------------------------- -// inline helper functions - -//............................................................................ -//! helper function to trigger reserved event in an QHsm -//! -//! @param[in] state state handler function -//! @param[in] sig reserved signal to trigger -static inline QP::QState hsm_reservedEvt_( - QP::QHsm * const me, - QP::QStateHandler const state, - enum QP::QHsm::ReservedSig const sig) -{ - return (*state)(me, &l_reservedEvt_[sig]); -} - -//............................................................................ -//! Helper function to execute entry into a given state in a -//! hierarchical state machine (HSM). -//! -//! @param[in] state state handler function -//! @param[in] qs_id QS-id of this state machine (for QS local filter) -static inline void hsm_state_entry_( - QP::QHsm * const me, - QP::QStateHandler const state, - std::uint_fast8_t const qs_id) -{ -#ifdef Q_SPY - if ((*state)(me, &l_reservedEvt_[QP::QHsm::Q_ENTRY_SIG]) - == QP::QHsm::Q_RET_HANDLED) - { - QS_CRIT_STAT_ - QS_BEGIN_PRE_(QP::QS_QEP_STATE_ENTRY, qs_id) - QS_OBJ_PRE_(me); - QS_FUN_PRE_(state); - QS_END_PRE_() - } -#else - Q_UNUSED_PAR(qs_id); - static_cast((*state)(me, &l_reservedEvt_[QP::QHsm::Q_ENTRY_SIG])); -#endif // Q_SPY -} - -//............................................................................ -//! Helper function to execute exit from a given state in a -//! hierarchical state machine (HSM). -//! -//! @param[in] state state handler function -//! @param[in] qs_id QS-id of this state machine (for QS local filter) -//! -//! @returns -//! 'true' if the exit action has been found in the state and -//! 'flase' otherwise. -static inline bool hsm_state_exit_( - QP::QHsm * const me, - QP::QStateHandler const state, - std::uint_fast8_t const qs_id) -{ -#ifdef Q_SPY - bool isHandled; - if ((*state)(me, &l_reservedEvt_[QP::QHsm::Q_EXIT_SIG]) - == QP::QHsm::Q_RET_HANDLED) - { - QS_CRIT_STAT_ - QS_BEGIN_PRE_(QP::QS_QEP_STATE_EXIT, qs_id) - QS_OBJ_PRE_(me); - QS_FUN_PRE_(state); - QS_END_PRE_() - isHandled = true; - } - else { - isHandled = false; - } - return isHandled; -#else - Q_UNUSED_PAR(qs_id); - return (*state)(me, &l_reservedEvt_[QP::QHsm::Q_EXIT_SIG]); -#endif // Q_SPY -} - -} // unnamed namespace - -//$define${QEP::QHsm} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv -namespace QP { - -//${QEP::QHsm} ............................................................... - -//${QEP::QHsm::QHsm} ......................................................... -QHsm::QHsm(QStateHandler const initial) noexcept{ - m_state.fun = Q_STATE_CAST(&top); - m_temp.fun = initial; -} - -//${QEP::QHsm::init} ......................................................... -void QHsm::init( - void const * const e, - std::uint_fast8_t const qs_id) -{ - #ifdef Q_SPY - if ((QS::priv_.flags & 0x01U) == 0U) { - QS::priv_.flags |= 0x01U; - QS_FUN_DICTIONARY(&QP::QHsm::top); - } - #else - Q_UNUSED_PAR(qs_id); - #endif - - QStateHandler t = m_state.fun; - - //! @pre ctor must have been executed and initial tran NOT taken - Q_REQUIRE_ID(200, (m_temp.fun != nullptr) - && (t == Q_STATE_CAST(&top))); - - // execute the top-most initial transition - QState r = (*m_temp.fun)(this, Q_EVT_CAST(QEvt)); - - // the top-most initial transition must be taken - Q_ASSERT_ID(210, r == Q_RET_TRAN); - - QS_CRIT_STAT_ - QS_BEGIN_PRE_(QS_QEP_STATE_INIT, qs_id) - QS_OBJ_PRE_(this); // this state machine object - QS_FUN_PRE_(t); // the source state - QS_FUN_PRE_(m_temp.fun); // the target of the initial transition - QS_END_PRE_() - - // drill down into the state hierarchy with initial transitions... - do { - QStateHandler path[MAX_NEST_DEPTH_]; // tran entry path array - std::int_fast8_t ip = 0; // entry path index - - path[0] = m_temp.fun; - static_cast(hsm_reservedEvt_(this, m_temp.fun, Q_EMPTY_SIG)); - while (m_temp.fun != t) { - ++ip; - Q_ASSERT_ID(220, ip < MAX_NEST_DEPTH_); - path[ip] = m_temp.fun; - static_cast(hsm_reservedEvt_(this, m_temp.fun, Q_EMPTY_SIG)); - } - m_temp.fun = path[0]; - - // retrace the entry path in reverse (desired) order... - do { - hsm_state_entry_(this, path[ip], qs_id); // enter path[ip] - --ip; - } while (ip >= 0); - - t = path[0]; // current state becomes the new source - - r = hsm_reservedEvt_(this, t, Q_INIT_SIG); // execute initial tran. - - #ifdef Q_SPY - if (r == Q_RET_TRAN) { - QS_BEGIN_PRE_(QS_QEP_STATE_INIT, qs_id) - QS_OBJ_PRE_(this); // this state machine object - QS_FUN_PRE_(t); // the source state - QS_FUN_PRE_(m_temp.fun); // the target of the initial tran. - QS_END_PRE_() - } - #endif // Q_SPY - - } while (r == Q_RET_TRAN); - - QS_BEGIN_PRE_(QS_QEP_INIT_TRAN, qs_id) - QS_TIME_PRE_(); // time stamp - QS_OBJ_PRE_(this); // this state machine object - QS_FUN_PRE_(t); // the new active state - QS_END_PRE_() - - m_state.fun = t; // change the current active state - m_temp.fun = t; // mark the configuration as stable -} - -//${QEP::QHsm::dispatch} ..................................................... -void QHsm::dispatch( - QEvt const * const e, - std::uint_fast8_t const qs_id) -{ - #ifndef Q_SPY - Q_UNUSED_PAR(qs_id); - #endif - - QStateHandler t = m_state.fun; - QS_CRIT_STAT_ - - //! @pre the current state must be initialized and - //! the state configuration must be stable - Q_REQUIRE_ID(400, (t != nullptr) - && (t == m_temp.fun)); - - 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_(this); // this state machine object - QS_FUN_PRE_(t); // the current state - QS_END_PRE_() - - QStateHandler s; - QState r; - // process the event hierarchically... - //! @tr{RQP120A} - do { - s = m_temp.fun; - r = (*s)(this, e); // invoke state handler s - - if (r == Q_RET_UNHANDLED) { // unhandled due to a guard? - - QS_BEGIN_PRE_(QS_QEP_UNHANDLED, qs_id) - QS_SIG_PRE_(e->sig); // the signal of the event - QS_OBJ_PRE_(this); // this state machine object - QS_FUN_PRE_(s); // the current state - QS_END_PRE_() - - r = hsm_reservedEvt_(this, s, Q_EMPTY_SIG); // find superstate of s - } - } while (r == Q_RET_SUPER); - - // regular transition taken? - //! @tr{RQP120E} - if (r >= Q_RET_TRAN) { - QStateHandler path[MAX_NEST_DEPTH_]; - - path[0] = m_temp.fun; // save the target of the transition - path[1] = t; - path[2] = s; - - // exit current state to transition source s... - //! @tr{RQP120C} - for (; t != s; t = m_temp.fun) { - // exit handled? - if (hsm_state_exit_(this, t, qs_id)) { - // find superstate of t - static_cast(hsm_reservedEvt_(this, t, Q_EMPTY_SIG)); - } - } - - std::int_fast8_t ip = hsm_tran(path, qs_id); // the HSM transition - - #ifdef Q_SPY - if (r == Q_RET_TRAN_HIST) { - - QS_BEGIN_PRE_(QS_QEP_TRAN_HIST, qs_id) - QS_OBJ_PRE_(this); // this state machine object - QS_FUN_PRE_(t); // the source of the transition - QS_FUN_PRE_(path[0]); // the target of the tran. to history - QS_END_PRE_() - - } - #endif // Q_SPY - - // execute state entry actions in the desired order... - //! @tr{RQP120B} - for (; ip >= 0; --ip) { - hsm_state_entry_(this, path[ip], qs_id); // enter path[ip] - } - t = path[0]; // stick the target into register - m_temp.fun = t; // update the next state - - // drill into the target hierarchy... - //! @tr{RQP120I} - while (hsm_reservedEvt_(this, t, Q_INIT_SIG) == Q_RET_TRAN) { - - QS_BEGIN_PRE_(QS_QEP_STATE_INIT, qs_id) - QS_OBJ_PRE_(this); // this state machine object - QS_FUN_PRE_(t); // the source (pseudo)state - QS_FUN_PRE_(m_temp.fun); // the target of the transition - QS_END_PRE_() - - ip = 0; - path[0] = m_temp.fun; - - // find superstate - static_cast(hsm_reservedEvt_(this, m_temp.fun, Q_EMPTY_SIG)); - - while (m_temp.fun != t) { - ++ip; - path[ip] = m_temp.fun; - // find superstate - static_cast( - hsm_reservedEvt_(this, m_temp.fun, Q_EMPTY_SIG)); - } - m_temp.fun = path[0]; - - // entry path must not overflow - Q_ASSERT_ID(410, ip < MAX_NEST_DEPTH_); - - // retrace the entry path in reverse (correct) order... - do { - hsm_state_entry_(this, path[ip], qs_id); // enter path[ip] - --ip; - } while (ip >= 0); - - t = path[0]; - } - - 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_(this); // this state machine object - QS_FUN_PRE_(s); // the source of the transition - QS_FUN_PRE_(t); // the new active state - QS_END_PRE_() - } - - #ifdef Q_SPY - else if (r == Q_RET_HANDLED) { - - 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_(this); // this state machine object - QS_FUN_PRE_(s); // the source state - QS_END_PRE_() - - } - else { - - 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_(this); // this state machine object - QS_FUN_PRE_(m_state.fun);// the current state - QS_END_PRE_() - - } - #else - Q_UNUSED_PAR(qs_id); // when Q_SPY not defined - #endif // Q_SPY - - m_state.fun = t; // change the current active state - m_temp.fun = t; // mark the configuration as stable -} - -//${QEP::QHsm::top} .......................................................... -QState QHsm::top( - void * const me, - QEvt const * const e) noexcept -{ - Q_UNUSED_PAR(me); - Q_UNUSED_PAR(e); - return Q_RET_IGNORED; // the top state ignores all events -} - -//${QEP::QHsm::isIn} ......................................................... -bool QHsm::isIn(QStateHandler const s) noexcept { - //! @pre state configuration must be stable - Q_REQUIRE_ID(600, m_temp.fun == m_state.fun); - - 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 (m_temp.fun == s) { - inState = true; // 'true' means that match found - r = Q_RET_IGNORED; // cause breaking out of the loop - } - else { - r = hsm_reservedEvt_(this, m_temp.fun, Q_EMPTY_SIG); - } - } while (r != Q_RET_IGNORED); // QHsm::top() state not reached - m_temp.fun = m_state.fun; // restore the stable state configuration - - return inState; // return the status -} - -//${QEP::QHsm::childState} ................................................... -QStateHandler QHsm::childState(QStateHandler const parent) noexcept { - QStateHandler child = m_state.fun; // start with the current state - bool isFound = false; // start with the child not found - - // establish stable state configuration - m_temp.fun = m_state.fun; - QState r; - do { - // is this the parent of the current child? - if (m_temp.fun == parent) { - isFound = true; // child is found - r = Q_RET_IGNORED; // cause breaking out of the loop - } - else { - child = m_temp.fun; - r = hsm_reservedEvt_(this, m_temp.fun, Q_EMPTY_SIG); - } - } while (r != Q_RET_IGNORED); // QHsm::top() state not reached - m_temp.fun = m_state.fun; // establish stable state configuration - - //! @post the child must be confirmed - Q_ENSURE_ID(810, isFound); - - #ifdef Q_NASSERT - Q_UNUSED_PAR(isFound); - #endif - - return child; // return the child -} - -//${QEP::QHsm::hsm_tran} ..................................................... -std::int_fast8_t QHsm::hsm_tran( - QStateHandler (&path)[MAX_NEST_DEPTH_], - std::uint_fast8_t const qs_id) -{ - #ifndef Q_SPY - Q_UNUSED_PAR(qs_id); - #endif - - std::int_fast8_t ip = -1; // transition entry path index - QStateHandler t = path[0]; - QStateHandler const s = path[2]; - - // (a) check source==target (transition to self) - if (s == t) { - // exit the source - static_cast(hsm_state_exit_(this, s, qs_id)); - ip = 0; // enter the target - } - else { - // superstate of target - static_cast(hsm_reservedEvt_(this, t, Q_EMPTY_SIG)); - t = m_temp.fun; - - // (b) check source==target->super - if (s == t) { - ip = 0; // enter the target - } - else { - // superstate of src - static_cast(hsm_reservedEvt_(this, s, Q_EMPTY_SIG)); - - // (c) check source->super==target->super - if (m_temp.fun == t) { - // exit the source - static_cast(hsm_state_exit_(this, s, qs_id)); - ip = 0; // enter the target - } - else { - // (d) check source->super==target - if (m_temp.fun == path[0]) { - // exit the source - static_cast(hsm_state_exit_(this, s, qs_id)); - } - else { - // (e) check rest of source==target->super->super.. - // and store the entry path along the way - std::int_fast8_t iq = 0; // indicate that LCA was found - ip = 1; // enter target and its superstate - path[1] = t; // save the superstate of target - t = m_temp.fun; // save source->super - - // find target->super->super - QState r = hsm_reservedEvt_(this, path[1], Q_EMPTY_SIG); - while (r == Q_RET_SUPER) { - ++ip; - path[ip] = m_temp.fun; // store the entry path - if (m_temp.fun == s) { // is it the source? - // indicate that the LCA was found - iq = 1; - - // entry path must not overflow - Q_ASSERT_ID(510, ip < MAX_NEST_DEPTH_); - --ip; // do not enter the source - r = Q_RET_HANDLED; // terminate the loop - } - // it is not the source, keep going up - else { - r = hsm_reservedEvt_(this, m_temp.fun, Q_EMPTY_SIG); - } - } - - // the LCA not found yet? - if (iq == 0) { - // entry path must not overflow - Q_ASSERT_ID(520, ip < MAX_NEST_DEPTH_); - - // exit the source - static_cast(hsm_state_exit_(this, s, qs_id)); - - // (f) check the rest of source->super - // == target->super->super... - // - iq = ip; - r = Q_RET_IGNORED; // indicate LCA NOT found - do { - // is this the LCA? - if (t == path[iq]) { - r = Q_RET_HANDLED; // indicate LCA found - ip = iq - 1; // do not enter LCA - iq = -1; // cause termination of the loop - } - else { - --iq; // try lower superstate of target - } - } while (iq >= 0); - - // 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 handled? - if (hsm_state_exit_(this, t, qs_id)) { - // find superstate of t - static_cast( - hsm_reservedEvt_(this, t, Q_EMPTY_SIG)); - } - t = m_temp.fun; // set to super of t - iq = ip; - do { - // is this LCA? - if (t == path[iq]) { - ip = iq - 1; // do not enter 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; - -} - -} // namespace QP -//$enddef${QEP::QHsm} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/src/qep_port.hpp b/src/qep_port.hpp deleted file mode 100644 index 60f5c1e..0000000 --- a/src/qep_port.hpp +++ /dev/null @@ -1,52 +0,0 @@ -/// @file -/// @brief QEP/C++ port, generic C++11 compiler -/// @cond -///*************************************************************************** -/// Last updated for version 6.8.0 -/// Last updated on 2022-03-20 -/// -/// Q u a n t u m L e a P s -/// ------------------------ -/// Modern Embedded Software -/// -/// Copyright (C) 2022 Victor Chavez. -/// Copyright (C) 2005-2020 Quantum Leaps. All rights reserved. -/// -/// This program is open source software: you can redistribute it and/or -/// modify it under the terms of the GNU General Public License as published -/// by the Free Software Foundation, either version 3 of the License, or -/// (at your option) any later version. -/// -/// Alternatively, this program may be distributed and modified under the -/// terms of Quantum Leaps commercial licenses, which expressly supersede -/// the GNU General Public License and are specifically designed for -/// licensees interested in retaining the proprietary status of their code. -/// -/// This program is distributed in the hope that it will be useful, -/// but WITHOUT ANY WARRANTY; without even the implied warranty of -/// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -/// GNU General Public License for more details. -/// -/// You should have received a copy of the GNU General Public License -/// along with this program. If not, see . -/// -/// Contact information: -/// -/// -///*************************************************************************** -/// @endcond - -#ifndef QEP_PORT_HPP -#define QEP_PORT_HPP - -#include // Exact-width types. C++11 Standard - -// enable QP/Spy software tracing instrumentation -#define Q_SPY 1U - -#include "qep.hpp" // QEP platform-independent public interface - -//! no-return function specifier (GCC) -#define Q_NORETURN __attribute__ ((noreturn)) void - -#endif // QEP_PORT_HPP diff --git a/src/qequeue.hpp b/src/qequeue.hpp deleted file mode 100644 index 03fd257..0000000 --- a/src/qequeue.hpp +++ /dev/null @@ -1,334 +0,0 @@ -//$file${include::qequeue.hpp} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv -// -// Model: qpcpp.qm -// File: ${include::qequeue.hpp} -// -// This code has been generated by QM 5.2.5 . -// DO NOT EDIT THIS FILE MANUALLY. All your changes will be lost. -// -// This code is covered by the following QP license: -// License # : LicenseRef-QL-dual -// Issued to : Any user of the QP/C++ real-time embedded framework -// Framework(s) : qpcpp -// Support ends : 2023-12-31 -// License scope: -// -// Copyright (C) 2005 Quantum Leaps, LLC . -// -// 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: -// -// The terms of the closed source Quantum Leaps commercial licenses -// can be found at: -// -// Redistributions in source code must retain this top-level comment block. -// Plagiarizing this software to sidestep the license obligations is illegal. -// -// Contact information: -// -// -// -//$endhead${include::qequeue.hpp} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -//! @file -//! @brief platform-independent fast "raw" thread-safe event queue interface -//! -//! @details -//! This header file must be included in all QF ports that use native QF -//! event queue for active objects. Also, this file needs to be included -//! in the QP/C++ library when the application uses QActive::defer() / -//! QActive::recall(). Finally, this file is also needed when the "raw" -//! thread-safe queues are used for communication between active objects -//! and non-framework entities, such as ISRs, device drivers, or legacy -//! code. - -#ifndef QEQUEUE_HPP_ -#define QEQUEUE_HPP_ - -#ifndef QF_EQUEUE_CTR_SIZE - - //! The size (in bytes) of the ring-buffer counters used in the - //! native QF event queue implementation. Valid values: 1U, 2U, or 4U; - //! default 1U. - //! @details - //! This macro can be defined in the QF port file (qf_port.hpp) to - //! configure the QP::QEQueueCtr type. Here the macro is not defined - //! so the default of 1 byte is chosen. - #define QF_EQUEUE_CTR_SIZE 1U -#endif - -namespace QP { - -#if (QF_EQUEUE_CTR_SIZE == 1U) - - //! The data type to store the ring-buffer counters based on - //! the macro #QF_EQUEUE_CTR_SIZE. - //! @details - //! The dynamic range of this data type determines the maximum length - //! of the ring buffer managed by the native QF event queue. - using QEQueueCtr = std::uint8_t; -#elif (QF_EQUEUE_CTR_SIZE == 2U) - using QEQueueCtr = std::uint16_t; -#elif (QF_EQUEUE_CTR_SIZE == 4U) - using QEQueueCtr = std::uint32_t; -#else - #error "QF_EQUEUE_CTR_SIZE defined incorrectly, expected 1U, 2U, or 4U" -#endif - -} // namespace QP - -//============================================================================ -//$declare${QF::QEQueue} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv -namespace QP { - -//${QF::QEQueue} ............................................................. -//! Native QF Event Queue class -//! -//! @details -//! This structure describes the native QF event queue, which can be used as -//! the event queue for active objects, or as a simple "raw" event queue for -//! thread-safe event passing among non-framework entities, such as ISRs, -//! device drivers, or other third-party components.
-//! -//! The native QF event queue is configured by defining the macro -//! #QF_EQUEUE_TYPE as QP::QEQueue in the specific QF port header file.
-//! -//! The QP::QEQueue class contains only data members for managing an event -//! queue, but does not contain the storage for the queue buffer, which must -//! be provided externally during the queue initialization.
-//! -//! The event queue can store only event pointers, not the whole events. The -//! internal implementation uses the standard ring-buffer plus one external -//! location that optimizes the queue operation for the most frequent case -//! of empty queue.
-//! -//! The QP::QEQueue class is used with two sets of functions. One set is for -//! the active object event queue, which needs to block the active object -//! task when the event queue is empty and unblock it when events are posted -//! to the queue. The interface for the native active object event queue -//! consists of the following functions: QActive::post(), QActive::postLIFO(), -//! and QActive::get_(). Additionally the function QEQueue::init() is used -//! to initialize the queue.
-//! -//! The other set of functions, uses this class as a simple "raw" event -//! queue to pass events between entities other than active objects, such as -//! ISRs. The "raw" event queue is not capable of blocking on the get() -//! operation, but is still thread-safe because it uses QF critical section -//! to protect its integrity. The interface for the "raw" thread-safe queue -//! consists of the following functions: QP::QEQueue::post(), -//! QP::QEQueue::postLIFO(), and QP::QEQueue::get(). Additionally the -//! function QP::QEQueue::init() is used to initialize the queue. -//! -//! @note -//! Most event queue operations (both the active object queues and the "raw" -//! queues) internally use the QF critical section. You should be careful -//! not to invoke those operations from other critical sections when nesting -//! of critical sections is not supported. -class QEQueue { -private: - - //! pointer to event at the front of the queue - //! - //! @details - //! All incoming and outgoing events pass through the m_frontEvt location. - //! When the queue is empty (which is most of the time), the extra - //! m_frontEvt location allows to bypass the ring buffer altogether, - //! greatly optimizing the performance of the queue. Only bursts of events - //! engage the ring buffer.
- //! - //! The additional role of this attribute is to indicate the empty status - //! of the queue. The queue is empty if the m_frontEvt location is nullptr. - QEvt const * volatile m_frontEvt; - - //! pointer to the start of the ring buffer - QEvt const ** m_ring; - - //! offset of the end of the ring buffer from the start of the buffer - QEQueueCtr m_end; - - //! offset to where next event will be inserted into the buffer - QEQueueCtr volatile m_head; - - //! offset of where next event will be extracted from the buffer - QEQueueCtr volatile m_tail; - - //! number of free events in the ring buffer - QEQueueCtr volatile m_nFree; - - //! minimum number of free events ever in the ring buffer. - //! @note this attribute remembers the low-watermark of the ring buffer, - //! which provides a valuable information for sizing event queues. - //! @sa QP::QF::getQueueMin(). - QEQueueCtr m_nMin; - friend class QActive; - friend class QTicker; - friend class QXMutex; - friend class QXThread; - -public: - - //! public default constructor - QEQueue() noexcept; - - //! Initializes the native QF event queue - //! - //! @details - //! Initialize the event queue by giving it the storage for the - //! ring buffer. - //! - //! @param[in] qSto an array of pointers to QP::QEvt to serve as the - //! ring buffer for the event queue - //! @param[in] qLen the length of the qSto[] buffer (in QP::QEvt pointers) - //! - //! @note - //! The actual capacity of the queue is qLen + 1, because of the extra - //! location forntEvt. - //! - //! @note - //! This function is also used to initialize the event queues of active - //! objects in the built-int QV, QK and QXK kernels, as well as other - //! QP ports to OSes/RTOSes that do provide a suitable message queue. - void init( - QEvt const * qSto[], - std::uint_fast16_t const qLen) noexcept; - - //! Posts (FIFO) an event to the "raw" thread-safe QF event queue - //! - //! @details - //! Post an event to the "raw" thread-safe event queue using the - //! First-In-First-Out (FIFO) order. - //! - //! @param[in] e pointer to the event to be posted to the queue - //! @param[in] margin number of required free slots in the queue after - //! posting the event. The special value - //! QF::NO_MARGIN means that this function will - //! assert if posting - //! @param[in] qs_id QS-id of this state machine (for QS local filter) - //! - //! @note - //! The QF::NO_MARGIN value of the `margin` argument is special and - //! denotes situation when the post() operation is assumed to succeed - //! (event delivery guarantee). An assertion fires, when the event cannot - //! be delivered in this case. - //! - //! @returns 'true' (success) when the posting succeeded with the provided - //! margin and 'false' (failure) when the posting fails. - //! - //! @note - //! This function can be called from any task context or ISR context. - //! - //! @sa QP::QEQueue::postLIFO(), QP::QEQueue::get() - bool post( - QEvt const * const e, - std::uint_fast16_t const margin, - std::uint_fast8_t const qs_id) noexcept; - - //! Posts (LIFO) an event to the "raw" thread-safe QF event queue - //! - //! @details - //! Post an event to the "raw" thread-safe event queue using the - //! Last-In-First-Out (LIFO) order. - //! - //! @param[in] e pointer to the event to be posted to the queue - //! @param[in] qs_id QS-id of this state machine (for QS local filter) - //! - //! @attention - //! The LIFO policy should be used only with great __caution__, - //! because it alters the order of events in the queue. - //! - //! @note - //! This function can be called from any task context or ISR context. - //! - //! @note - //! This function is used for the "raw" thread-safe queues and __not__ - //! for the queues of active objects. - //! - //! @sa - //! QEQueue::post(), QEQueue::get(), QActive::defer() - void postLIFO( - QEvt const * const e, - std::uint_fast8_t const qs_id) noexcept; - - //! Gets an event from the "raw" thread-safe QF event queue - //! - //! @details - //! Retrieves an event from the front of the "raw" thread-safe queue and - //! returns a pointer to this event to the caller. - //! - //! @param[in] qs_id QS-id of this state machine (for QS local filter) - //! - //! @returns - //! pointer to event at the front of the queue, if the queue is - //! not empty and NULL if the queue is empty. - //! - //! @note - //! this function is used for the "raw" thread-safe queues and **not** - //! for the queues of active objects. - //! - //! @sa - //! QEQueue::post(), QEQueue::postLIFO(), QActive::recall() - QEvt const * get(std::uint_fast8_t const qs_id) noexcept; - - //! Gets the number of free slots currently in "raw" thread-safe - //! QF event queue - //! - //! @note - //! This operation needs to be used with caution because the - //! number of free entries can change unexpectedly. The main intent for - //! using this operation is in conjunction with event deferral. In this - //! case the queue is accessed only from a single thread (by a single AO), - //! so the number of free entries cannot change unexpectedly. - //! - //! @sa QP::QMActive::defer(), QP::QMActive::recall() - QEQueueCtr getNFree() const noexcept { - return m_nFree; - } - - //! "raw" thread-safe QF event queue operation for obtaining the minimum - //! number of free entries ever in the queue (a.k.a. "low-watermark"). - //! - //! @details - //! This operation needs to be used with caution because the - //! "low-watermark" can change unexpectedly. The main intent for using - //! this operation is to get an idea of queue usage to size the queue - //! adequately. - //! - //! @returns the minimum number of free entries ever in the queue - //! since init. - QEQueueCtr getNMin() const noexcept { - return m_nMin; - } - - //! "raw" thread-safe QF event queue operation to find out if the queue - //! is empty - //! @note - //! This operation needs to be used with caution because the - //! queue status can change unexpectedly. The main intent for using - //! this operation is in conjunction with event deferral. In this case - //! the queue is accessed only from a single thread (by a single AO), - //! so no other entity can post events to the queue. - //! - //! @sa QP::QMActive::defer(), QP::QMActive::recall() - bool isEmpty() const noexcept { - return m_frontEvt == nullptr; - } - -private: - - //! disallow copying of QP::QEQueue - QEQueue(QEQueue const & other) = delete; - - //! disallow copying of QP::QEQueue - QEQueue & operator=(QEQueue const & other) = delete; -}; // class QEQueue - -} // namespace QP -//$enddecl${QF::QEQueue} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -#endif // QEQUEUE_HPP_ diff --git a/src/qf.hpp b/src/qf.hpp deleted file mode 100644 index aabb9f4..0000000 --- a/src/qf.hpp +++ /dev/null @@ -1,1880 +0,0 @@ -//$file${include::qf.hpp} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv -// -// Model: qpcpp.qm -// File: ${include::qf.hpp} -// -// This code has been generated by QM 5.2.5 . -// DO NOT EDIT THIS FILE MANUALLY. All your changes will be lost. -// -// This code is covered by the following QP license: -// License # : LicenseRef-QL-dual -// Issued to : Any user of the QP/C++ real-time embedded framework -// Framework(s) : qpcpp -// Support ends : 2023-12-31 -// License scope: -// -// Copyright (C) 2005 Quantum Leaps, LLC . -// -// 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: -// -// The terms of the closed source Quantum Leaps commercial licenses -// can be found at: -// -// Redistributions in source code must retain this top-level comment block. -// Plagiarizing this software to sidestep the license obligations is illegal. -// -// Contact information: -// -// -// -//$endhead${include::qf.hpp} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -//! @file -//! @brief QF/C++ platform-independent public interface. - -#ifndef QF_HPP_ -#define QF_HPP_ - -#ifdef Q_EVT_CTOR -#include // for placement new -#include // for va_list -#endif // Q_EVT_CTOR - -//============================================================================ -// Global namespace... -//$declare${QF-config} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv - -//${QF-config::QF_MAX_ACTIVE} ................................................ -#ifndef QF_MAX_ACTIVE -//! Maximum number of active objects (configurable value in qf_port.hpp) -//! Valid values: [1U..64U]; default 32U -#define QF_MAX_ACTIVE 32U -#endif // ndef QF_MAX_ACTIVE - -//${QF-config::QF_MAX_ACTIVE exceeds the maximu~} ............................ -#if (QF_MAX_ACTIVE > 64U) -#error QF_MAX_ACTIVE exceeds the maximum of 64U; -#endif // (QF_MAX_ACTIVE > 64U) - -//${QF-config::QF_MAX_TICK_RATE} ............................................. -#ifndef QF_MAX_TICK_RATE -//! Maximum number of clock rates (configurable value in qf_port.hpp) -//! Valid values: [0U..15U]; default 1U -#define QF_MAX_TICK_RATE 1U -#endif // ndef QF_MAX_TICK_RATE - -//${QF-config::QF_MAX_TICK_RATE exceeds the max~} ............................ -#if (QF_MAX_TICK_RATE > 15U) -#error QF_MAX_TICK_RATE exceeds the maximum of 15U; -#endif // (QF_MAX_TICK_RATE > 15U) - -//${QF-config::QF_MAX_EPOOL} ................................................. -#ifndef QF_MAX_EPOOL -//! Maximum number of event pools (configurable value in qf_port.hpp) -//! Valid values: [0U..15U]; default 3U -//! -//! @note -//! #QF_MAX_EPOOL set to zero means that dynamic events are NOT configured -//! and should not be used in the application. -#define QF_MAX_EPOOL 3U -#endif // ndef QF_MAX_EPOOL - -//${QF-config::QF_MAX_EPOOL exceeds the maximum~} ............................ -#if (QF_MAX_EPOOL > 15U) -#error QF_MAX_EPOOL exceeds the maximum of 15U; -#endif // (QF_MAX_EPOOL > 15U) - -//${QF-config::QF_TIMEEVT_CTR_SIZE} .......................................... -#ifndef QF_TIMEEVT_CTR_SIZE -//! Size of the QTimeEvt counter (configurable value in qf_port.hpp) -//! Valid values: 1U, 2U, or 4U; default 4U -#define QF_TIMEEVT_CTR_SIZE 4U -#endif // ndef QF_TIMEEVT_CTR_SIZE - -//${QF-config::QF_TIMEEVT_CTR_SIZE defined inco~} ............................ -#if (QF_TIMEEVT_CTR_SIZE != 1U) && (QF_TIMEEVT_CTR_SIZE != 2U) && (QF_TIMEEVT_CTR_SIZE != 4U) -#error QF_TIMEEVT_CTR_SIZE defined incorrectly, expected 1U, 2U, or 4U; -#endif // (QF_TIMEEVT_CTR_SIZE != 1U) && (QF_TIMEEVT_CTR_SIZE != 2U) && (QF_TIMEEVT_CTR_SIZE != 4U) - -//${QF-config::QF_EVENT_SIZ_SIZE} ............................................ -#ifndef QF_EVENT_SIZ_SIZE -//! Size of the event-size (configurable value in qf_port.hpp) -//! Valid values: 1U, 2U, or 4U; default 2U -#define QF_EVENT_SIZ_SIZE 2U -#endif // ndef QF_EVENT_SIZ_SIZE - -//${QF-config::QF_EVENT_SIZ_SIZE defined incorr~} ............................ -#if (QF_EVENT_SIZ_SIZE != 1U) && (QF_EVENT_SIZ_SIZE != 2U) && (QF_EVENT_SIZ_SIZE != 4U) -#error QF_EVENT_SIZ_SIZE defined incorrectly, expected 1U, 2U, or 4U; -#endif // (QF_EVENT_SIZ_SIZE != 1U) && (QF_EVENT_SIZ_SIZE != 2U) && (QF_EVENT_SIZ_SIZE != 4U) -//$enddecl${QF-config} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -//============================================================================ -//$declare${QF-types} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv -namespace QP { - -//${QF-types::QPSetBits} ..................................................... -#if (8U < QF_MAX_ACTIVE) && (QF_MAX_ACTIVE <= 16U) -//! bitmask for the internal representation of QPSet elements -using QPSetBits = std::uint16_t; -#endif // (8U < QF_MAX_ACTIVE) && (QF_MAX_ACTIVE <= 16U) - -//${QF-types::QPSetBits} ..................................................... -#if (16 < QF_MAX_ACTIVE) -using QPSetBits = std::uint32_t; -#endif // (16 < QF_MAX_ACTIVE) - -//${QF-types::QPSetBits} ..................................................... -#if (QF_MAX_ACTIVE <= 8U) -using QPSetBits = std::uint8_t; -#endif // (QF_MAX_ACTIVE <= 8U) - -//${QF-types::QTimeEvtCtr} ................................................... -#if (QF_TIMEEVT_CTR_SIZE== 2U) -//! Data type to store the block-size defined based on the macro -//! #QF_TIMEEVT_CTR_SIZE. -//! -//! @details -//! The dynamic range of this data type determines the maximum block -//! size that can be managed by the pool. -using QTimeEvtCtr = std::uint16_t; -#endif // (QF_TIMEEVT_CTR_SIZE== 2U) - -//${QF-types::QTimeEvtCtr} ................................................... -#if (QF_TIMEEVT_CTR_SIZE== 1U) -using QTimeEvtCtr = std::uint8_t; -#endif // (QF_TIMEEVT_CTR_SIZE== 1U) - -//${QF-types::QTimeEvtCtr} ................................................... -#if (QF_TIMEEVT_CTR_SIZE== 4U) -using QTimeEvtCtr = std::uint32_t; -#endif // (QF_TIMEEVT_CTR_SIZE== 4U) - -//${QF-types::QPrioSpec} ..................................................... -//! Priority specification for Active Objects in QP -//! -//! @details -//! Active Object priorities in QP are integer numbers in the range -//! [1..#QF_MAX_ACTIVE], whereas the special priority number 0 is reserved -//! for the lowest-priority idle thread. The QP framework uses the *direct* -//! priority numbering, in which higher numerical values denote higher -//! urgency. For example, an AO with priority 32 has higher urgency than -//! an AO with priority 23. -//! -//! QP::QPrioSpec allows an application developer to assign **two** -//! priorities to a given AO (see also Q_PRIO()): -//! -//! 1. The "QF-priority", which resides in the least-significant byte -//! of the QP::QPrioSpec data type. The "QF-priority" must be **unique** -//! for each thread in the system and higher numerical values represent -//! higher urgency (direct pirority numbering). -//! -//! 2. The "preemption-threshold" priority, which resides in the most- -//! significant byte of the ::QPrioSpec data type. The second priority -//! cannot be lower than the "QF-priority", but does NOT need to be -//! unuque. -//! -//! In the QP native preemptive kernels, like QK and QXK, the "preemption- -//! threshold" priority is used as to implement the "preemption-threshold -//! scheduling" (PTS). It determines the conditions under which a given -//! thread can be *preempted* by other threads. Specifically, a given -//! thread can be preempted only by another thread with a *higher* -//! priority than the "preemption-threshold" of the original thread. -//! -//! ![QF-priority and preemption-threshold relations](qp-prio.png) -//! -//! @note -//! For backwards-compatibility, QP::QPrioSpec data type might contain only -//! the "QF-priority" component (and the "preemption-threshold" component -//! left at zero). In that case, the "preemption-threshold" will be assumed -//! to be the same as the "QF-priority". This corresponds exactly to the -//! previous semantics of AO priority. -//! -//! @remark -//! When QP runs on top of 3rd-party kernels/RTOSes or general-purpose -//! operating systems, sthe second priority can have different meaning, -//! depending on the specific RTOS/GPOS used. -using QPrioSpec = std::uint16_t; - -//${QF-types::QSchedStatus} .................................................. -//! The scheduler lock status used in some real-time kernels -using QSchedStatus = std::uint_fast16_t; - -//${QF-types::QPSet} ......................................................... -//! Priority Set of up to #QF_MAX_ACTIVE elements -//! -//! @details -//! The priority set represents the set of active objects that are ready to -//! run and need to be considered by the scheduling algorithm. The set is -//! capable of storing up to #QF_MAX_ACTIVE priority levels, which can be -//! configured in the rage 1..64, inclusive. -class QPSet { -public: - -#if (QF_MAX_ACTIVE <= 32) - //! bitmask with a bit for each element - QPSetBits volatile m_bits; -#endif // (QF_MAX_ACTIVE <= 32) - -#if (32 < QF_MAX_ACTIVE) - //! bitmasks with a bit for each element - QPSetBits volatile m_bits[2]; -#endif // (32 < QF_MAX_ACTIVE) - -public: - - //! Make the priority set empty - void setEmpty() noexcept { - #if (QF_MAX_ACTIVE <= 32U) - m_bits = 0U; - #else - m_bits[0] = 0U; - m_bits[1] = 0U; - #endif - } - - //! Return 'true' if the priority set is empty - bool isEmpty() const noexcept { - #if (QF_MAX_ACTIVE <= 32U) - return (m_bits == 0U); - #else - return (m_bits[0] == 0U) ? (m_bits[1] == 0U) : false; - #endif - } - - //! Return 'true' if the priority set is NOT empty - bool notEmpty() const noexcept { - #if (QF_MAX_ACTIVE <= 32U) - return (m_bits != 0U); - #else - return (m_bits[0] != 0U) ? true : (m_bits[1] != 0U); - #endif - } - - //! Return 'true' if the priority set has the element n. - bool hasElement(std::uint_fast8_t const n) const noexcept { - #if (QF_MAX_ACTIVE <= 32U) - return (m_bits & (1U << (n - 1U))) != 0U; - #else - return (n <= 32U) - ? ((m_bits[0] & (static_cast(1) << (n - 1U))) != 0U) - : ((m_bits[1] & (static_cast(1) << (n - 33U))) != 0U); - #endif - } - - //! insert element `n` into the set (n = 1..QF_MAX_ACTIVE) - void insert(std::uint_fast8_t const n) noexcept { - #if (QF_MAX_ACTIVE <= 32U) - m_bits = (m_bits | (1U << (n - 1U))); - #else - if (n <= 32U) { - m_bits[0] = (m_bits[0] - | (static_cast(1) << (n - 1U))); - } - else { - m_bits[1] = (m_bits[1] - | (static_cast(1) << (n - 33U))); - } - #endif - } - - //! Remove element `n` from the set (n = 1U..64U) - void remove(std::uint_fast8_t const n) noexcept { - #if (QF_MAX_ACTIVE <= 32U) - m_bits = (m_bits & static_cast( - ~(static_cast(1) << (n - 1U)))); - #else - if (n <= 32U) { - (m_bits[0] = (m_bits[0] - & ~(static_cast(1) << (n - 1U)))); - } - else { - (m_bits[1] = (m_bits[1] - & ~(static_cast(1) << (n - 33U)))); - } - #endif - } - - //! Find the maximum element in the set, returns zero if the set is empty - std::uint_fast8_t findMax() const noexcept { - #if (QF_MAX_ACTIVE <= 32U) - return QF_LOG2(m_bits); - #else - return (m_bits[1] != 0U) - ? (QF_LOG2(m_bits[1]) + 32U) - : (QF_LOG2(m_bits[0])); - #endif - } - -#ifndef QF_LOG2 - //! Log-base-2 calculation when hardware acceleration - //! is NOT provided (#QF_LOG2 not defined). - std::uint_fast8_t QF_LOG2(QP::QPSetBits x) const noexcept; -#endif // ndef QF_LOG2 -}; // class QPSet - -//${QF-types::QSubscrList} ................................................... -//! Subscriber List (for publish-subscribe) -//! -//! @details -//! This data type represents a set of Active Objects that subscribe to -//! a given signal. The set is represented as priority-set, where each bit -//! corresponds to the unique QF-priority of an AO (see QP::QPrioSpec). -using QSubscrList = QPSet; - -} // namespace QP -//$enddecl${QF-types} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -//$declare${QF::QActive} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv -namespace QP { - -//${QF::QActive} ............................................................. -//! QP::QActive active object class (based on the QP::QHsm-style -//! implementation strategy) -//! -//! @details -//! Active objects are encapsulated tasks (each containing an event queue and -//! a state machine) that communicate with one another asynchronously by -//! sending and receiving events. Within an active object, events are -//! processed in a run-to-completion (RTC) fashion, while QF encapsulates -//! all the details of thread-safe event exchange and queuing.
-//! -//! QP::QActive represents an active object that uses the QP::QHsm-style -//! implementation strategy for state machines. This strategy is tailored -//! to manual coding, but it is also supported by the QM modeling tool. -//! The resulting code is slower than in the QP::QMsm-style implementation -//! strategy. -//! -//! @note -//! QP::QActive is not intended to be instantiated directly, but rather serves -//! as the abstract base class for derivation of active objects in the -//! applications. -//! -//! @sa QP::QMActive -//! -//! @usage -//! The following example illustrates how to derive an active object from -//! QP::QActive. -//! @include qf_qactive.cpp -class QActive : public QP::QHsm { -public: - -#ifdef QF_EQUEUE_TYPE - //! OS-dependent event-queue type - //! - //! @details - //! The type of the queue depends on the underlying operating system or - //! a kernel. Many kernels support "message queues" that can be adapted - //! to deliver QF events to the active object. Alternatively, QF provides - //! a native event queue implementation that can be used as well. - //! - //! @note - //! The native QF event queue is configured by defining the macro - //! #QF_EQUEUE_TYPE as QP::QEQueue. - QF_EQUEUE_TYPE m_eQueue; -#endif // def QF_EQUEUE_TYPE - -#ifdef QF_OS_OBJECT_TYPE - //! OS-dependent per-thread object - //! - //! @details - //! This data might be used in various ways, depending on the QF port. - //! In some ports m_osObject is used to block the calling thread when - //! the native QF queue is empty. In other QF ports the OS-dependent - //! object might be used differently. - QF_OS_OBJECT_TYPE m_osObject; -#endif // def QF_OS_OBJECT_TYPE - -#ifdef QF_THREAD_TYPE - //! OS-dependent representation of the thread of the active object - //! - //! @details - //! This data might be used in various ways, depending on the QF port. - //! In some ports m_thread is used store the thread handle. In other ports - //! m_thread can be a pointer to the Thread-Local-Storage (TLS). - QF_THREAD_TYPE m_thread; -#endif // def QF_THREAD_TYPE - - //! QF-priority [1..#QF_MAX_ACTIVE] of this AO. - //! @sa QP::QPrioSpec - std::uint8_t m_prio; - - //! preemption-threshold [1..#QF_MAX_ACTIVE] of this AO. - //! @sa QP::QPrioSpec - std::uint8_t m_pthre; - -private: - friend class QTimeEvt; - friend class QTicker; - -#ifdef QXK_HPP_ - friend class QXThread; -#endif // def QXK_HPP_ - -#ifdef QXK_HPP_ - friend class QXMutex; -#endif // def QXK_HPP_ - -#ifdef QXK_HPP_ - friend class QXSemaphore; -#endif // def QXK_HPP_ - -#ifdef Q_UTEST - friend class QActiveDummy; -#endif // def Q_UTEST - friend class GuiQActive; - friend class GuiQMActive; - -public: - - //! Internal array of registered active objects - static QActive * registry_[QF_MAX_ACTIVE + 1U]; - - //! pointer to the array of all subscriber AOs for a given event signal - static QSubscrList * subscrList_; - - //! The maximum published signal (the size of the subscrList_ array) - static enum_t maxPubSignal_; - -protected: - - //! protected constructor (abstract class) - QActive(QStateHandler const initial) noexcept; - -public: - - //! Starts execution of an active object and registers the object - //! with the framework - //! - //! @details - //! Starts execution of the AO and registers the AO with the framework. - //! - //! @param[in] prioSpec priority specification of the AO containing the - //! QF-priority and (optionally) preemption-threshold of this AO - //! (for preemptive kernels that support it). See also QP::QPrioSpec. - //! @param[in] qSto pointer to the storage for the ring buffer of the - //! event queue - //! @param[in] qLen length of the event queue [# QP::QEvt* pointers] - //! @param[in] stkSto pointer to the stack storage (might be nullptr) - //! @param[in] stkSize stack size [bytes] - //! @param[in] par pointer to an extra parameter (might be nullptr) - //! - //! @usage - //! The following example shows starting an AO when a per-task stack - //! is needed: - //! @include qf_start.cpp - virtual void start( - QPrioSpec const prioSpec, - QEvt const * * const qSto, - std::uint_fast16_t const qLen, - void * const stkSto, - std::uint_fast16_t const stkSize, - void const * const par); - - //! Overloaded start function (no initialization parameter) - virtual void start( - QPrioSpec const prioSpec, - QEvt const * * const qSto, - std::uint_fast16_t const qLen, - void * const stkSto, - std::uint_fast16_t const stkSize) - { - this->start(prioSpec, qSto, qLen, stkSto, stkSize, nullptr); - } - -#ifdef QF_ACTIVE_STOP - //! Stops execution of an active object and removes it from the - //! framework's supervision - //! - //! @attention - //! QActive::stop() must be called only from the AO that is about - //! to stop its execution. By that time, any pointers or references - //! to the AO are considered invalid (dangling) and it becomes - //! illegal for the rest of the application to post events to the AO. - void stop(); -#endif // def QF_ACTIVE_STOP - - //! Posts an event `e` directly to the event queue of the active object - //! using the First-In-First-Out (FIFO) policy. - //! - //! @details - //! Direct event posting is the simplest asynchronous communication - //! method available in QF. - //! - //! @param[in] e pointer to the event to be posted - //! @param[in] margin number of required free slots in the queue - //! after posting the event or QF::NO_MARGIN. - //! @param[in] sender pointer to a sender object (used in QS only) - //! - //! @returns - //! 'true' (success) if the posting succeeded (with the provided margin) - //! and 'false' (failure) when the posting fails. - //! - //! @attention - //! For `margin` == QF::NO_MARGIN, this function will assert internally - //! if the event posting fails. In that case, it is unnecessary to check - //! the retrun value from this function. - //! - //! @note - //! This function might be implemented differentyl in various QP/C++ - //! ports. The provided implementation assumes that the QP::QEQueue - //! class is used for the QP::QActive event queue. - //! - //! @usage - //! @include qf_post.cpp - //! - //! @sa - //! QActive::postLIFO() - virtual bool post_( - QEvt const * const e, - std::uint_fast16_t const margin, - void const * const sender) noexcept; - - //! Posts an event `e` directly to the event queue of the active object - //! using the Last-In-First-Out (LIFO) policy. - //! - //! @details - //! The LIFO policy should be used only for self-posting and with caution, - //! because it alters order of events in the queue. - //! - //! @param[in] e pointer to the event to be posted - //! - //! @attention - //! This function asserts internally if the posting fails. - //! - //! @note - //! This function might be implemented differentyl in various QP/C++ - //! ports. The provided implementation assumes that the QP::QEQueue - //! class is used for the QActive event queue. - //! - //! @sa - //! QActive::post() - virtual void postLIFO(QEvt const * const e) noexcept; - - //! Get an event from the event queue of an active object - //! - //! @details - //! The behavior of this function depends on the kernel used in the - //! QF port. For built-in kernels (Vanilla or QK) the function can be - //! called only when the queue is not empty, so it doesn't block. For - //! a blocking kernel/OS the function can block and wait for delivery - //! of an event. - //! - //! @returns - //! A pointer to the received event. The returned pointer is guaranteed - //! to be valid (can't be nullptr). - //! - //! @note - //! This function might be implemented differentyl in various QP/C++ - //! ports. The provided implementation assumes that the QP::QEQueue - //! class is used for the QActive event queue. - QEvt const * get_() noexcept; - - //! Subscribes for delivery of signal `sig` to the active object - //! - //! @details - //! This function is part of the Publish-Subscribe event delivery - //! mechanism available in QF. Subscribing to an event means that the - //! framework will start posting all published events with a given signal - //! `sig` to the event queue of the active object. - //! - //! @param[in] sig event signal to subscribe - //! - //! The following example shows how the Table active object subscribes - //! to three signals in the initial transition: - //! @include qf_subscribe.cpp - //! - //! @sa - //! QActive::publish_(), QActive::unsubscribe(), and - //! QActive::unsubscribeAll() - void subscribe(enum_t const sig) const noexcept; - - //! Unsubscribes from the delivery of signal `sig` to the active object - //! - //! @details - //! This function is part of the Publish-Subscribe event delivery - //! mechanism available in QF. Un-subscribing from an event means that - //! the framework will stop posting published events with a given signal - //! `sig` to the event queue of the active object. - //! - //! @param[in] sig event signal to unsubscribe - //! - //! @note - //! Due to the latency of event queues, an active object should NOT - //! assume that a given signal `sig` will never be dispatched to the - //! state machine of the active object after un-subscribing from that - //! signal. The event might be already in the queue, or just about to - //! be posted and the un-subscribe operation will not flush such events. - //! - //! @note - //! Un-subscribing from a signal that has never been subscribed in the - //! first place is considered an error and QF will raise an assertion. - //! - //! @sa - //! QActive::publish_(), QActive::subscribe(), and - //! QActive::unsubscribeAll() - void unsubscribe(enum_t const sig) const noexcept; - - //! Unsubscribes from the delivery of all signals to the active object - //! - //! @details - //! This function is part of the Publish-Subscribe event delivery - //! mechanism available in QF. Un-subscribing from all events means that - //! the framework will stop posting any published events to the event - //! queue of the active object. - //! - //! @note - //! Due to the latency of event queues, an active object should NOT - //! assume that no events will ever be dispatched to the state machine of - //! the active object after un-subscribing from all events. - //! The events might be already in the queue, or just about to be posted - //! and the un-subscribe operation will not flush such events. Also, the - //! alternative event-delivery mechanisms, such as direct event posting or - //! time events, can be still delivered to the event queue of the active - //! object. - //! - //! @sa - //! QActive::publish_(), QActive::subscribe(), and QActive::unsubscribe() - void unsubscribeAll() const noexcept; - - //! Defer an event to a given separate event queue - //! - //! @details - //! This function is part of the event deferral support. An active object - //! uses this function to defer an event `e` to the QF-supported native - //! event queue `eq`. QF correctly accounts for another outstanding - //! reference to the event and will not recycle the event at the end of - //! the RTC step. Later, the active object might recall one event at a - //! time from the event queue. - //! - //! @param[in] eq pointer to a "raw" thread-safe queue to recall - //! an event from. - //! @param[in] e pointer to the event to be deferred - //! - //! @returns - //! 'true' (success) when the event could be deferred and 'false' - //! (failure) if event deferral failed due to overflowing the queue. - //! - //! An active object can use multiple event queues to defer events of - //! different kinds. - //! - //! @sa - //! QActive::recall(), QP::QEQueue, QActive::flushDeferred() - bool defer( - QEQueue * const eq, - QEvt const * const e) const noexcept; - - //! Recall a deferred event from a given event queue - //! - //! @details - //! This function is part of the event deferral support. An active object - //! uses this function to recall a deferred event from a given QF - //! event queue. Recalling an event means that it is removed from the - //! deferred event queue `eq` and posted (LIFO) to the event queue of - //! the active object. - //! - //! @param[in] eq pointer to a "raw" thread-safe queue to recall - //! an event from. - //! - //! @returns - //! 'true' if an event has been recalled and 'false' if not. - //! - //! @note - //! An active object can use multiple event queues to defer events of - //! different kinds. - //! - //! @sa - //! QActive::recall(), QActive::postLIFO_(), QP::QEQueue - bool recall(QEQueue * const eq) noexcept; - - //! Flush the specified deferred queue 'eq' - //! - //! @details - //! This function is part of the event deferral support. An active object - //! can use this function to flush a given QF event queue. The function - //! makes sure that the events are not leaked. - //! - //! @param[in] eq pointer to a "raw" thread-safe queue to flush. - //! - //! @returns - //! the number of events actually flushed from the queue. - //! - //! @sa - //! QActive::defer(), QActive::recall(), QP::QEQueue - std::uint_fast16_t flushDeferred(QEQueue * const eq) const noexcept; - - //! Get the priority of the active object. - std::uint_fast8_t getPrio() const noexcept { - return static_cast(m_prio); - } - -protected: - - //! Set the priority of the active object. - void setPrio(QPrioSpec const prio) noexcept { - m_prio = static_cast(prio & 0xFFU); - m_pthre = static_cast(prio >> 8U); - } - -public: - - //! Generic setting of additional attributes (useful in QP ports) - void setAttr( - std::uint32_t attr1, - void const * attr2 = nullptr); - -#ifdef QF_OS_OBJECT_TYPE - //! accessor to the OS-object for extern "C" functions, such as - //! the QK or QXK schedulers - QF_OS_OBJECT_TYPE & getOsObject() noexcept { - return m_osObject; - } -#endif // def QF_OS_OBJECT_TYPE - -#ifdef QF_THREAD_TYPE - //! accessor to the Thread for extern "C" functions, such as - //! the QK or QXK schedulers - QF_THREAD_TYPE & getThread() noexcept { - return m_thread; - } -#endif // def QF_THREAD_TYPE - - //! Publish-subscribe initialization - //! - //! @details - //! This function initializes the publish-subscribe facilities of QF and must - //! be called exactly once before any subscriptions/publications occur in - //! the application. - //! - //! @param[in] subscrSto pointer to the array of subscriber lists - //! @param[in] maxSignal the dimension of the subscriber array and at - //! the same time the maximum signal that can be - //! published or subscribed. - //! - //! The array of subscriber-lists is indexed by signals and provides a mapping - //! between the signals and subscriber-lists. The subscriber-lists are - //! bitmasks of type QP::QSubscrList, each bit in the bit mask corresponding - //! to the unique priority of an active object. The size of the - //! QP::QSubscrList bitmask depends on the value of the #QF_MAX_ACTIVE macro. - //! - //! @note - //! The publish-subscribe facilities are optional, meaning that you might - //! choose not to use publish-subscribe. In that case calling QF::psInit() - //! and using up memory for the subscriber-lists is unnecessary. - //! - //! @sa - //! QP::QSubscrList - //! - //! @usage - //! The following example shows the typical initialization sequence of QF: - //! @include qf_main.cpp - static void psInit( - QSubscrList * const subscrSto, - enum_t const maxSignal) noexcept; - - //! Publish event to all subscribers of a given signal `e->sig` - //! - //! @details - //! This function posts (using the FIFO policy) the event @a e to **all** - //! active objects that have subscribed to the signal @a e->sig, which is - //! called _multicasting_. The multicasting performed in this function is - //! very efficient based on reference-counting inside the published event - //! ("zero-copy" event multicasting). This function is designed to be - //! callable from any part of the system, including ISRs, device drivers, - //! and active objects. - //! - //! @note - //! To avoid any unexpected re-ordering of events posted into AO queues, - //! the event multicasting is performed with scheduler **locked**. - //! However, the scheduler is locked only up to the priority level of - //! the highest-priority subscriber, so any AOs of even higher priority, - //! which did not subscribe to this event are *not* affected. - static void publish_( - QEvt const * const e, - void const * const sender, - std::uint_fast8_t const qs_id) noexcept; - - //! Thread routine for executing an active object `act` - static void thread_(QActive * act); - - //! Register this active object to be managed by the framework - //! - //! @details - //! This function adds a given active object to the active objects - //! managed by the QF framework. It should not be called by the - //! application directly, only through the function QActive::start(). - //! - //! @note - //! The priority of the active object a should be set before calling - //! this function. - //! - //! @sa QActive::unregister_() - void register_() noexcept; - - //! Un-register the active object from the framework. - //! - //! @details - //! This function un-registers a given active object from the active objects - //! managed by the QF framework. It should not be called by the QP ports. - //! - //! @param[in] a pointer to the active object to remove from the - //! framework. - //! - //! @note - //! The active object that is removed from the framework can no longer - //! participate in any event exchange. - //! - //! @sa QActive::register_() - void unregister_() noexcept; - -#ifdef QF_ISR_API - //! the "FromISR" variant used in the QP port to "FreeRTOS" - virtual bool postFromISR_( - QEvt const * const e, - std::uint_fast16_t const margin, - void * par, - void const * const sender) noexcept; -#endif // def QF_ISR_API - -#ifdef QF_ISR_API - //! the "FromISR" variant used in the QP port to "FreeRTOS" - static void publishFromISR_( - QEvt const * e, - void * par, - void const * sender) noexcept; -#endif // def QF_ISR_API -}; // class QActive - -} // namespace QP -//$enddecl${QF::QActive} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -//$declare${QF::QMActive} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv -namespace QP { - -//${QF::QMActive} ............................................................ -//! QMActive active object (based on QP::QMsm implementation) -//! -//! @details -//! QP::QMActive represents an active object that uses the QP::QMsm-style -//! state machine implementation strategy. This strategy requires the use of -//! the QM modeling tool to generate state machine code automatically, but -//! the code is faster than in the QP::QHsm-style implementation strategy -//! and needs less run-time support (smaller event-processor). -//! -//! @note -//! QP::QMActive is not intended to be instantiated directly, but rather -//! serves as the base class for derivation of active objects in the -//! applications. -//! -//! @sa QP::QActive -//! -//! @usage -//! The following example illustrates how to derive an active object from -//! QP::QMActive. -//! @include qf_qmactive.cpp -class QMActive : public QP::QActive { -public: - - //! inherited from QP::QHsm, but disallowed in QP::QMActive - using QHsm::isIn; - - //! inherited from QP::QHsm, but disallowed in QP::QMActive - using QHsm::state; - - //! inherited from QP::QHsm, but disallowed in QP::QMActive - using QHsm::childState; - -protected: - - //! protected constructor (abstract class) - QMActive(QStateHandler const initial) noexcept - : QActive(initial) - { - m_temp.fun = initial; - } - -public: - - //! delegate to QP::QMsm::init() - void init( - void const * const e, - std::uint_fast8_t const qs_id) override; - - //! delegate to QP::QMsm::init() - void init(std::uint_fast8_t const qs_id) override; - - //! delegate to QMsm::dispatch() - void dispatch( - QEvt const * const e, - std::uint_fast8_t const qs_id) override; - - //! Tests if a given state is part of the active state configuration - bool isInState(QMState const * const st) const noexcept; - - //! Return the current active state object (read only) - QMState const * stateObj() const noexcept { - return m_state.obj; - } - - //! Return the current active state object (read only) - QMState const * childStateObj(QMState const * const parent) const noexcept; - -#ifdef Q_SPY - //! Get the current state handler of the QP::QMsm - QStateHandler getStateHandler() noexcept override; -#endif // def Q_SPY -}; // class QMActive - -} // namespace QP -//$enddecl${QF::QMActive} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -//$declare${QF::QTimeEvt} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv -namespace QP { - -//${QF::QTimeEvt} ............................................................ -//! Time Event class (inherits QP:QEvt) -//! -//! @details -//! Time events are special QF events equipped with the notion of time -//! passage. The basic usage model of the time events is as follows. An -//! active object allocates one or more QTimeEvt objects (provides the -//! storage for them). When the active object needs to arrange for a timeout, -//! it arms one of its time events to fire either just once (one-shot) or -//! periodically. Each time event times out independently from the others, -//! so a QF application can make multiple parallel timeout requests (from the -//! same or different active objects). When QF detects that the appropriate -//! moment has arrived, it inserts the time event directly into the -//! recipient's event queue. The recipient then processes the time event just -//! like any other event. -//!
-//! Time events, as any other QF events derive from the QP::QEvt base -//! class. Typically, you will use a time event as-is, but you can also -//! further derive more specialized time events from it by adding some more -//! data members and/or specialized functions that operate on the specialized -//! time events. -//!
-//! Internally, the armed time events are organized into a bi-directional -//! linked list. This linked list is scanned in every invocation of the -//! QTimeEvt::tick_() function. Only armed (timing out) time events are in the -//! list, so only armed time events consume CPU cycles. -//! -//! @note -//! QF manages the time events in the macro TICK_X(), which must be called -//! periodically, from the clock tick ISR or from the special QP::QTicker -//! active object. -//! -//! @note -//! Even though QP::QTimeEvt is a subclass of QP::QEvt, QP::QTimeEvt instances -//! can NOT be allocated dynamically from event pools. In other words, it is -//! illegal to allocate QP::QTimeEvt instances with the Q_NEW() or Q_NEW_X() -//! macros. -class QTimeEvt : public QP::QEvt { -private: - - //! link to the next time event in the list - QTimeEvt * volatile m_next; - - //! the active object that receives the time events - //! - //! @details - //! The m_act pointer is reused inside the QP implementation to hold - //! the head of the list of newly armed time events. - void * m_act; - - //! the internal down-counter of the time event - //! - //! @details - //! The down-counter is decremented by 1 in every TICK_X() - //! invocation. The time event fires (gets posted or published) when - //! the down-counter reaches zero. - QTimeEvtCtr volatile m_ctr; - - //! the interval for the periodic time event (zero for the one-shot - //! time event) - //! - //! @details - //! The value of the interval is re-loaded to the internal - //! down-counter when the time event expires, so that the time event - //! keeps timing out periodically. - QTimeEvtCtr m_interval; - -public: - - //! heads of linked lists of time events, one for every clock tick rate - static QTimeEvt timeEvtHead_[QF_MAX_TICK_RATE]; - -private: - friend class QXThread; - -public: - - //! The Time Event constructor - QTimeEvt( - QActive * const act, - enum_t const sgnl, - std::uint_fast8_t const tickRate = 0U); - - //! Arm a time event (one shot or periodic) for event posting - //! - //! @details - //! Arms a time event to fire in a specified number of clock ticks and - //! with a specified interval. If the interval is zero, the time event - //! is armed for one shot ('one-shot' time event). The time event gets - //! directly posted (using the FIFO policy) into the event queue of the - //! host active object. After posting, a one-shot time event gets - //! automatically disarmed while a periodic time event (interval != 0) - //! is automatically re-armed. - //! - //! A time event can be disarmed at any time by calling - //! QP::QTimeEvt::disarm(). Also, a time event can be re-armed to fire - //! in a different number of clock ticks by calling QP::QTimeEvt::rearm(). - //! - //! @param[in] nTicks number of clock ticks (at the associated rate) - //! to rearm the time event with. - //! @param[in] interval interval (in clock ticks) for periodic time event. - //! - //! @attention - //! Arming an already armed time event is __not__ allowed and is - //! considered a programming error. The QP/C++ framework will assert - //! if it detects an attempt to arm an already armed time event. - //! - //! @usage - //! The following example shows how to arm a one-shot time event from a - //! state machine of an active object: - //! @include qf_state.cpp - void armX( - QTimeEvtCtr const nTicks, - QTimeEvtCtr const interval = 0U) noexcept; - - //! Disarm a time event - //! - //! @details - //! Disarm the time event so it can be safely reused. - //! - //! @returns - //! 'true' if the time event was truly disarmed, that is, it was running. - //! The return of 'false' means that the time event was not truly - //! disarmed because it was not running. The 'false' return is only - //! possible for one-shot time events that have been automatically - //! disarmed upon expiration. In that case the 'false' return means that - //! the time event has already been posted or published and should be - //! expected in the active object's state machine. - //! - //! @note - //! there is no harm in disarming an already disarmed time event - bool disarm() noexcept; - - //! Rearm a time event - //! - //! @details - //! Rearms a time event with a new number of clock ticks. This function - //! can be used to adjust the current period of a periodic time event - //! or to prevent a one-shot time event from expiring (e.g., a watchdog - //! time event). Rearming a periodic timer leaves the interval unchanged - //! and is a convenient method to adjust the phasing of a periodic - //! time event. - //! - //! @param[in] nTicks number of clock ticks (at the associated rate) - //! to rearm the time event with. - //! - //! @returns - //! 'true' if the time event was running as it was re-armed. The 'false' - //! return means that the time event was not truly rearmed because it was - //! not running. The 'false' return is only possible for one-shot time - //! events that have been automatically disarmed upon expiration. In that - //! case the 'false' return means that the time event has already been - //! posted and should be expected in the active object's state machine. - bool rearm(QTimeEvtCtr const nTicks) noexcept; - - //! Check the "was disarmed" status of a time event - //! - //! @details - //! Useful for checking whether a one-shot time event was disarmed in the - //! QTimeEvt_disarm() operation. - //! - //! @returns - //! 'true' if the time event was truly disarmed in the last - //! QTimeEvt::disarm() operation. The 'false' return means that the time - //! event was not truly disarmed, because it was not running at that time. - //! The 'false' return is only possible for one-shot time events that - //! have been automatically disarmed upon expiration. In this case the - //! 'false' return means that the time event has already been posted or - //! published and should be expected in the active object's event queue. - //! - //! @note - //! This function has a **side effect** of setting the "was disarmed" - //! status, which means that the second and subsequent times this - //! function is called the function will return 'true'. - bool wasDisarmed() noexcept; - - //! Gets the active object associated with the time event - void const * getAct() const noexcept { - return m_act; - } - - //! Gets the current count of the time event - QTimeEvtCtr getCtr() const noexcept { - return m_ctr; - } - - //! Gets the interval of the time event - QTimeEvtCtr getInterval() const noexcept { - return m_interval; - } - - //! Processes all armed time events at every clock tick - //! - //! @details - //! This function must be called periodically from a time-tick ISR or from - //! a task so that QF can manage the timeout events assigned to the given - //! system clock tick rate. - //! - //! @param[in] tickRate system clock tick rate serviced [1..15]. - //! @param[in] sender pointer to a sender object (used in QS only). - //! - //! @attention - //! this function should be called only via the macros TICK_X() or TICK() - //! - //! @note - //! the calls to QTimeEvt::tick_() with different `tickRate` parameter can - //! preempt each other. For example, higher clock tick rates might be - //! serviced from interrupts while others from tasks (active objects). - static void tick_( - std::uint_fast8_t const tickRate, - void const * const sender) noexcept; - -#ifdef Q_UTEST - //! Processes one clock tick for QUTest - static void tick1_( - std::uint_fast8_t const tickRate, - void const * const sender); -#endif // def Q_UTEST - -#ifdef QF_ISR_API - //! the "FromISR" variant used in the QP port to "FreeRTOS" - static void tickFromISR_( - std::uint_fast8_t const tickRate, - void * par, - void const * sender) noexcept; -#endif // def QF_ISR_API - - //! Returns true if all time events are inactive and false - //! any time event is active - //! - //! @details - //! Find out if any time events are armed at the given clock tick rate. - //! - //! @param[in] tickRate system clock tick rate to find out about. - //! - //! @returns - //! 'true' if no time events are armed at the given tick rate and - //! 'false' otherwise. - //! - //! @note - //! This function should be called in critical section. - static bool noActive(std::uint_fast8_t const tickRate) noexcept; - - //! encapsulate the cast the m_act attribute to QActive* - QActive * toActive() noexcept { - return static_cast(m_act); - } - - //! encapsulate the cast the `QTimeEvt.m_act` attribute - QTimeEvt * toTimeEvt() noexcept { - return static_cast(m_act); - } - -private: - - //! private default constructor only for friends - //! - //! @note - //! private default ctor for internal use only - QTimeEvt(); - - //! private copy constructor to disallow copying of QTimeEvts - QTimeEvt(QTimeEvt const & other) = delete; - - //! disallow copying of QP::QTimeEvt - QTimeEvt & operator=(QTimeEvt const & other) = delete; -}; // class QTimeEvt - -} // namespace QP -//$enddecl${QF::QTimeEvt} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -//$declare${QF::QTicker} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv -namespace QP { - -//${QF::QTicker} ............................................................. -//! "Ticker" Active Object class (inherits QP::QActive) -//! -//! @details -//! QP::QTicker is an efficient active object specialized to process -//! QF system clock tick at a specified tick frequency [0..#QF_MAX_TICK_RATE]. -//! Placing system clock tick processing in an active object allows you -//! to remove the non-deterministic TICK_X() processing from the interrupt -//! level and move it into the thread-level, where you can prioritize it -//! as low as you wish. -//! -//! @usage -//! The following example illustrates use of QP::QTicker active objects: -//! @include qf_ticker.cpp -class QTicker : public QP::QActive { -public: - - //! constructor - explicit QTicker(std::uint_fast8_t const tickRate) noexcept; - void init( - void const * const e, - std::uint_fast8_t const qs_id) override; - void init(std::uint_fast8_t const qs_id) override; - void dispatch( - QEvt const * const e, - std::uint_fast8_t const qs_id) override; - bool post_( - QEvt const * const e, - std::uint_fast16_t const margin, - void const * const sender) noexcept override; -}; // class QTicker - -} // namespace QP -//$enddecl${QF::QTicker} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -//$declare${QF::QF-base} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv -namespace QP { -namespace QF { - -//${QF::QF-base::intLock_} ................................................... -//! Interrupt lock up-down counter (used in some QF ports) -extern std::uint_fast8_t volatile intLock_; - -//${QF::QF-base::intNest_} ................................................... -//! Interrupt nesting up-down counter (used in some QF ports) -extern std::uint_fast8_t volatile intNest_; - -//${QF::QF-base::init} ....................................................... -//! QF initialization -//! -//! @details -//! Initializes QF and must be called exactly once before any other QF -//! function. Typcially, QP::QF::init() is called from main() even before -//! initializing the Board Support Package (BSP). -//! -//! @note -//! QP::QF::init() clears 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). -void init(); - -//${QF::QF-base::stop} ....................................................... -//! Function invoked by the application layer to stop the QF -//! application and return control to the OS/Kernel -//! -//! @details -//! This function stops the QF application. After calling this function, -//! QF attempts to gracefully stop the application. This graceful shutdown -//! might take some time to complete. The typical use of this function is -//! for terminating the QF application to return back to the operating -//! system or for handling fatal errors that require shutting down -//! (and possibly re-setting) the system. -//! -//! @attention -//! After calling QF::stop() the application must terminate and cannot -//! continue. In particular, QF::stop() is **not** intended to be followed -//! by a call to QF::init() to "resurrect" the application. -//! -//! @sa QP::QF::onCleanup() -void stop(); - -//${QF::QF-base::run} ........................................................ -//! Transfers control to QF to run the application -//! -//! @details -//! QF::run() is typically called from your startup code after you -//! initialize the QF and start at least one active object with -//! QActive::start(). -//! -//! @returns -//! In QK, the QP::QF::run() function does not return. -int_t run(); - -//${QF::QF-base::onStartup} .................................................. -//! Startup QF callback (defined in applications/ports) -//! -//! @details -//! The purpose of the QF::onStartup() callback is to configure and enable -//! hardware interrupts. The callback is invoked from QF::run(), right before -//! starting the underlying real-time kernel. By that time, the application -//! is considered ready to receive and service interrupts. -//! -//! This function is application-specific and is not implemented in QF, but -//! rather in the Board Support Package (BSP) for the given application. -void onStartup(); - -//${QF::QF-base::onCleanup} .................................................. -//! Cleanup QF callback (defined in applications/ports) -void onCleanup(); - -//${QF::QF-base::getQueueMin} ................................................ -//! This function returns the minimum of free entries of the given -//! event queue of an active object (indicated by priority `prio`) -//! -//! @details -//! Queries the minimum of free ever present in the given event queue of -//! an active object with priority `prio`, since the active object -//! was started. -//! -//! @note -//! QF::getQueueMin() is available only when the native QF event queue -//! implementation is used. Requesting the queue minimum of an unused -//! priority level raises an assertion in the QF. (A priority level -//! becomes used in QF after the call to QActive::register_().) -//! -//! @param[in] prio Priority of the active object, whose queue is queried -//! -//! @returns -//! the minimum of free ever present in the given event queue of an active -//! object with priority `prio`, since the active object was started. -std::uint_fast16_t getQueueMin(std::uint_fast8_t const prio) noexcept; - -//${QF::QF-base::psInit} ..................................................... -//! Publish-subscribe initialization -//! -//! @deprecated -//! superseded by QActive::psInit() -inline void psInit( - QSubscrList * const subscrSto, - enum_t const maxSignal) noexcept -{ - QActive::psInit(subscrSto, maxSignal); -} - -//${QF::QF-base::publish_} ................................................... -//! Publish event to all subscribers of a given signal `e->sig` -//! -//! @deprecated -//! superseded by QActive::publish_() -inline void publish_( - QEvt const * const e, - void const * const sender, - std::uint_fast8_t const qs_id) noexcept -{ - QActive::publish_(e, sender, qs_id); -} - -//${QF::QF-base::tick_} ...................................................... -//! Processes all armed time events at every clock tick -//! -//! @deprecated -//! superseded by QTimeEvt::tick_() -inline void tick_( - std::uint_fast8_t const tickRate, - void const * const sender) noexcept -{ - QTimeEvt::tick_(tickRate, sender); -} - -//${QF::QF-base::NO_MARGIN} .................................................. -//! Special value of margin that causes asserting failure in case -//! event allocation or event posting fails -constexpr std::uint_fast16_t NO_MARGIN {0xFFFFU}; - -} // namespace QF -} // namespace QP -//$enddecl${QF::QF-base} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -//$declare${QF::QF-dyn} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv -namespace QP { -namespace QF { - -//${QF::QF-dyn::poolInit} .................................................... -//! Event pool initialization for dynamic allocation of events. -//! -//! @details -//! This function initializes one event pool at a time and must be called -//! exactly once for each event pool before the pool can be used. -//! -//! @param[in] poolSto pointer to the storage for the event pool -//! @param[in] poolSize size of the storage for the pool in bytes -//! @param[in] evtSize the block-size of the pool in bytes, which -//! determines the maximum size of events that -//! can be allocated from the pool -//! @note -//! You might initialize many event pools by making many consecutive calls -//! to the QF::poolInit() function. However, for the simplicity of the -//! internal implementation, you must initialize event pools in the -//! ascending order of the event size. -//! -//! @note -//! The actual number of events available in the pool might be actually -//! less than (`poolSize / evtSize`) due to the internal alignment of -//! the blocks that the pool might perform. You can always check the -//! capacity of the pool by calling QF::getPoolMin(). -//! -//! @note -//! The dynamic allocation of events is optional, meaning that you might -//! choose not to use dynamic events. In that case calling -//! QF::poolInit() and using up memory for the memory blocks is -//! unnecessary. -//! -//! @sa QF initialization example for QF::init() -void poolInit( - void * const poolSto, - std::uint_fast32_t const poolSize, - std::uint_fast16_t const evtSize) noexcept; - -//${QF::QF-dyn::newX_} ....................................................... -//! Internal QF implementation of creating new dynamic mutable event -//! -//! @details -//! Allocates an event dynamically from one of the QF event pools. -//! -//! @param[in] evtSize the size (in bytes) of the event to allocate -//! @param[in] margin the number of un-allocated events still available -//! in a given event pool after the allocation -//! completes. The special value QF::NO_MARGIN -//! means that this function will assert if allocation -//! fails. -//! @param[in] sig the signal to be assigned to the allocated event -//! -//! @returns -//! pointer to the newly allocated event. This pointer can be nullptr -//! only if margin!=0 and the event cannot be allocated with the -//! specified margin still available in the given pool. -//! -//! @note -//! The internal QF function QF::newX_() raises an assertion when -//! the margin argument is QF::NO_MARGIN and allocation of the event -//! turns out to be impossible due to event pool depletion, or incorrect -//! (too big) size of the requested event. -//! -//! @note -//! The application code should not call this function directly. -//! The only allowed use is thorough the macros Q_NEW() or Q_NEW_X(). -QEvt * newX_( - std::uint_fast16_t const evtSize, - std::uint_fast16_t const margin, - enum_t const sig) noexcept; - -//${QF::QF-dyn::gc} .......................................................... -//! Recycle a dynamic event -//! -//! @details -//! This function implements a garbage collector for dynamic events. -//! Only dynamic events are candidates for recycling. (A dynamic event -//! is one that is allocated from an event pool, which is determined as -//! non-zero `e->poolId_` attribute.) Next, the function decrements the -//! reference counter of the event (`e->refCtr_`), and recycles the event -//! only if the counter drops to zero (meaning that no more references -//! are outstanding for this event). The dynamic event is recycled by -//! returning it to the pool from which it was originally allocated. -//! -//! @param[in] e pointer to the event to recycle -//! -//! @note -//! QF invokes the garbage collector at all appropriate contexts, when -//! an event can become garbage (automatic garbage collection), so the -//! application code should have no need to call QF::gc() directly. -//! The QF::gc() function is exposed only for special cases when your -//! application sends dynamic events to the "raw" thread-safe queues -//! (see QP::QEQueue). Such queues are processed outside of QF and the -//! automatic garbage collection is **NOT** performed for these events. -//! In this case you need to call QF::gc() explicitly. -void gc(QEvt const * const e) noexcept; - -//${QF::QF-dyn::poolGetMaxBlockSize} ......................................... -//! Obtain the block size of any registered event pools -std::uint_fast16_t poolGetMaxBlockSize() noexcept; - -//${QF::QF-dyn::newRef_} ..................................................... -//! Internal QF implementation of creating new event reference -//! -//! @details -//! Creates and returns a new reference to the current event e -//! -//! @param[in] e pointer to the current event -//! @param[in] evtRef the event reference -//! -//! @returns -//! the newly created reference to the event `e` -//! -//! @note -//! The application code should not call this function directly. -//! The only allowed use is thorough the macro Q_NEW_REF(). -QEvt const * newRef_( - QEvt const * const e, - QEvt const * const evtRef) noexcept; - -//${QF::QF-dyn::deleteRef_} .................................................. -//! Internal QF implementation of deleting event reference -//! -//! @details -//! Deletes an existing reference to the event e -//! -//! @param[in] evtRef the event reference -//! -//! @note -//! The application code should not call this function directly. -//! The only allowed use is thorough the macro Q_DELETE_REF(). -void deleteRef_(QEvt const * const evtRef) noexcept; - -//${QF::QF-dyn::getPoolMin} .................................................. -//! This function returns the minimum of free entries of the given -//! event pool -//! -//! @details -//! This function obtains the minimum number of free blocks in the given -//! event pool since this pool has been initialized by a call to -//! QP::QF::poolInit(). -//! -//! @param[in] poolId event pool ID in the range 1..QF::maxPool_, where -//! QF::maxPool_ is the number of event pools -//! initialized with the function QF::poolInit(). -//! @returns -//! the minimum number of unused blocks in the given event pool. -std::uint_fast16_t getPoolMin(std::uint_fast8_t const poolId) noexcept; - -//${QF::QF-dyn::newXfromISR_} ................................................ -#ifdef QF_ISR_API -//! the "FromISR" variant used in the QP port to "FreeRTOS" -QEvt * newXfromISR_( - std::uint_fast16_t const evtSize, - std::uint_fast16_t const margin, - enum_t const sig) noexcept; -#endif // def QF_ISR_API - -//${QF::QF-dyn::gcFromISR} ................................................... -#ifdef QF_ISR_API -//! the "FromISR" variant used in the QP port to "FreeRTOS" -void gcFromISR(QEvt const * e) noexcept; -#endif // def QF_ISR_API - -} // namespace QF -} // namespace QP -//$enddecl${QF::QF-dyn} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -//============================================================================ -extern "C" { -//$declare${QF-extern-C} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv - -//${QF-extern-C::QF_onContextSw} ............................................. -#ifdef QF_ON_CONTEXT_SW -//! QF context switch callback used in built-in kernels -//! -//! @details -//! This callback function provides a mechanism to perform additional -//! custom operations when one of the built-in kernels switches context -//! from one thread to another. -//! -//! @param[in] prev pointer to the previous thread (active object) -//! (prev==0 means that `prev` was the QK idle loop) -//! @param[in] next pointer to the next thread (active object) -//! (next==0) means that `next` is the QK idle loop) -//! @attention -//! QF_onContextSw() is invoked with interrupts **disabled** and must also -//! return with interrupts **disabled**. -//! -//! @note -//! This callback is enabled by defining the macro #QF_ON_CONTEXT_SW. -//! -//! @include qf_oncontextsw.cpp -void QF_onContextSw( - QP::QActive * prev, - QP::QActive * next); -#endif // def QF_ON_CONTEXT_SW -//$enddecl${QF-extern-C} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -} // extern "C" - -//============================================================================ -// Global namespace... -//$declare${QF-macros} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv - -//${QF-macros::Q_PRIO} ....................................................... -//! Create a QP::QPrioSpec object to specify priority of an AO or a thread -#define Q_PRIO(prio_, pthre_) (static_cast((prio_) | (pthre_) << 8U)) - -//${QF-macros::Q_NEW} ........................................................ -#ifndef Q_EVT_CTOR -//! Allocate a dynamic event (case when QP::QEvt is a POD) -//! -//! @details -//! The macro calls the internal QF function QF::newX_() with -//! margin == QF::NO_MARGIN, which causes an assertion when the event -//! cannot be successfully allocated. -//! -//! @param[in] evtT_ event type (class name) of the event to allocate -//! @param[in] sig_ signal to assign to the newly allocated event -//! -//! @returns a valid event pointer cast to the type `evtT_`. -//! -//! @note -//! If #Q_EVT_CTOR is defined, the Q_NEW() macro becomes variadic and -//! takes all the arguments needed by the constructor of the event -//! class being allocated. The constructor is then called by means -//! of the placement-new operator. -//! -//! @usage -//! The following example illustrates dynamic allocation of an event: -//! @include qf_post.cpp -#define Q_NEW(evtT_, sig_) (static_cast( \ - QP::QF::newX_(sizeof(evtT_), QP::QF::NO_MARGIN, (sig_)))) -#endif // ndef Q_EVT_CTOR - -//${QF-macros::Q_NEW} ........................................................ -#ifdef Q_EVT_CTOR -//! Allocate a dynamic event (case when QP::QEvt is not a POD) -#define Q_NEW(evtT_, sig_, ...) \ - (new(QP::QF::newX_(sizeof(evtT_), QP::QF::NO_MARGIN, (sig_))) \ - evtT_((sig_), ##__VA_ARGS__)) -#endif // def Q_EVT_CTOR - -//${QF-macros::Q_NEW_X} ...................................................... -#ifndef Q_EVT_CTOR -//! Non-asserting allocate a dynamic event (case when QP::QEvt is a POD). -//! -//! @details -//! This macro allocates a new event and sets the pointer `e_`, while -//! leaving at least `margin_` of events still available in the pool -//! -//! @param[out] e_ pointer to the newly allocated event -//! @param[in] evtT_ event type (class name) of the event to allocate -//! @param[in] margin_ number of events that must remain available -//! in the given pool after this allocation. The -//! special value QF::NO_MARGIN causes asserting -//! failure in case event allocation fails. -//! @param[in] sig_ signal to assign to the newly allocated event -//! -//! @returns an event pointer cast to the type `evtT_` or NULL if the -//! event cannot be allocated with the specified `margin`. -//! -//! @note -//! If #Q_EVT_CTOR is defined, the Q_NEW_X() macro becomes variadic and -//! takes all the arguments needed by the constructor of the event -//! class being allocated. The constructor is then called by means -//! of the placement-new operator. -//! -//! @usage -//! The following example illustrates dynamic allocation of an event: -//! @include qf_postx.cpp -#define Q_NEW_X(e_, evtT_, margin_, sig_) \ - ((e_) = static_cast(QP::QF::newX_( \ - sizeof(evtT_), (margin_), (sig_)))) -#endif // ndef Q_EVT_CTOR - -//${QF-macros::Q_NEW_X} ...................................................... -#ifdef Q_EVT_CTOR -//! Non-asserting allocate a dynamic event -//! (case when QP::QEvt is not a POD) -#define Q_NEW_X(e_, evtT_, margin_, sig_, ...) do { \ - (e_) = static_cast( \ - QP::QF::newX_(sizeof(evtT_), (margin_), (sig_))); \ - if ((e_) != nullptr) { \ - new((e_)) evtT_((sig_), ##__VA_ARGS__); \ - } \ -} while (false) -#endif // def Q_EVT_CTOR - -//${QF-macros::Q_NEW_REF} .................................................... -//! Create a new reference of the current event `e` -//! -//! @details -//! The current event processed by an active object is available only for -//! the duration of the run-to-completion (RTC) step. After that step, the -//! current event is no longer available and the framework might recycle -//! (garbage-collect) the event. The macro Q_NEW_REF() explicitly creates -//! a new reference to the current event that can be stored and used beyond -//! the current RTC step, until the reference is explicitly recycled by -//! means of the macro Q_DELETE_REF(). -//! -//! @param[in,out] evtRef_ event reference to create -//! @param[in] evtT_ event type (class name) of the event reference -//! -//! @usage -//! The example **defer** in the directory `examples/win32/defer` illustrates -//! the use of Q_NEW_REF() -//! -//! @sa Q_DELETE_REF() -#define Q_NEW_REF(evtRef_, evtT_) \ - ((evtRef_) = static_cast(QP::QF::newRef_(e, (evtRef_)))) - -//${QF-macros::Q_DELETE_REF} ................................................. -//! Delete the event reference -//! -//! @details -//! Every event reference created with the macro Q_NEW_REF() needs to be -//! eventually deleted by means of the macro Q_DELETE_REF() to avoid leaking -//! the event. -//! -//! @param[in,out] evtRef_ event reference to delete -//! -//! @usage -//! The example **defer** in the directory `examples/win32/defer` illustrates -//! the use of Q_DELETE_REF() -//! -//! @sa Q_NEW_REF() -#define Q_DELETE_REF(evtRef_) do { \ - QP::QF::deleteRef_((evtRef_)); \ - (evtRef_) = 0U; \ -} while (false) - -//${QF-macros::PUBLISH} ...................................................... -#ifdef Q_SPY -//! Invoke the event publishing facility QActive::publish_(). -//! -//! @details -//! This macro is the recommended way of publishing events, because it -//! provides the vital information for software tracing and avoids any -//! overhead when the tracing is disabled. -//! -//! @param[in] e_ pointer to the posted event -//! @param[in] sender_ pointer to the sender object (actually used -//! only when #Q_SPY is defined) -//! -//! @note -//! The pointer to the `sender_` object is not necessarily a pointer -//! to an active object. In fact, if QActive::PUBLISH() is called from an -//! interrupt or other context, you can create a unique object just to -//! unambiguously identify the sender of the event. -//! -//! @sa QActive::publish_() -#define PUBLISH(e_, sender_) \ - publish_((e_), (sender_), (sender_)->getPrio()) -#endif // def Q_SPY - -//${QF-macros::PUBLISH} ...................................................... -#ifndef Q_SPY -#define PUBLISH(e_, dummy) publish_((e_), nullptr, 0U) -#endif // ndef Q_SPY - -//${QF-macros::POST} ......................................................... -#ifdef Q_SPY -//! Invoke the direct event posting facility QActive::post_() -//! -//! @details -//! This macro asserts if the queue overflows and cannot accept the event. -//! -//! @param[in] e_ pointer to the event to post -//! @param[in] sender_ pointer to the sender object. -//! -//! @note -//! The `sendedr_` parameter is actually only used when QS tracing -//! is enabled (macro #Q_SPY is defined). When QS software tracing is -//! disenabled, the POST() macro does not pass the `sender_` parameter, -//1 so the overhead of passing this extra parameter is entirely avoided. -//! -//! @note -//! the pointer to the sender object is not necessarily a pointer to an -//! active object. In fact, if POST() is called from an interrupt or -//! other context, you can create a unique object just to unambiguously -//! identify the sender of the event. -//! -//! @sa QActive::post_() -#define POST(e_, sender_) post_((e_), QP::QF::NO_MARGIN, (sender_)) -#endif // def Q_SPY - -//${QF-macros::POST} ......................................................... -#ifndef Q_SPY -#define POST(e_, dummy) post_((e_), QP::QF::NO_MARGIN, nullptr) -#endif // ndef Q_SPY - -//${QF-macros::POST_X} ....................................................... -#ifdef Q_SPY -//! Invoke the direct event posting facility QActive::post_() -//! without delivery guarantee -//! -//! @details -//! This macro does not assert if the queue overflows and cannot accept -//! the event with the specified margin of free slots remaining. -//! -//! @param[in] e_ pointer to the event to post -//! @param[in] margin_ the minimum free slots in the queue, which -//! must still be available after posting the event. -//! The special value QF::NO_MARGIN causes -//! asserting failure in case event posting fails. -//! @param[in] sender_ pointer to the sender object. -//! -//! @returns -//! 'true' if the posting succeeded, and 'false' if the posting -//! failed due to insufficient margin of free entries available in -//! the queue. -//! -//! @note -//! The `sender_` parameter is actually only used when QS tracing -//! is enabled (macro #Q_SPY is defined). When QS software tracing is -//! disabled, the POST_X() macro does not pass the `sender_` parameter, -//! so the overhead of passing this extra parameter is entirely avoided. -//! -//! @note -//! The pointer to the sender object is not necessarily a pointer -//! to an active object. In fact, if POST_X() is called from an -//! interrupt or other context, you can create a unique object just to -//! unambiguously identify the sender of the event. -//! -//! @usage -//! @include qf_postx.cpp -#define POST_X(e_, margin_, sender_) \ - post_((e_), (margin_), (sender_)) -#endif // def Q_SPY - -//${QF-macros::POST_X} ....................................................... -#ifndef Q_SPY -#define POST_X(e_, margin_, dummy) post_((e_), (margin_), nullptr) -#endif // ndef Q_SPY - -//${QF-macros::TICK_X} ....................................................... -#ifdef Q_SPY -//! Invoke the system clock tick processing QTimeEvt::tick_() -//! -//! @details -//! This macro is the recommended way of invoking clock tick processing, -//! because it provides the vital information for software tracing and -//! avoids any overhead when the tracing is disabled. -//! -//! @param[in] tickRate_ clock tick rate to be serviced through this call -//! @param[in] sender_ pointer to the sender object. This parameter -//! is actually only used when QS software tracing is enabled -//! (macro #Q_SPY is defined) -//! @note -//! When QS software tracing is disabled, the macro calls -//! QTimeEvt::tick_() without the `sender` parameter, so the overhead -//! of passing this extra parameter is entirely avoided. -//! -//! @note -//! The pointer to the sender object is not necessarily a pointer -//! to an active object. In fact, when TICK_X() is called from -//! an interrupt, you would create a unique object just to unambiguously -//! identify the ISR as the sender of the time events. -//! -//! @sa QTimeEvt::tick_() -#define TICK_X(tickRate_, sender_) tick_((tickRate_), (sender_)) -#endif // def Q_SPY - -//${QF-macros::TICK_X} ....................................................... -#ifndef Q_SPY -#define TICK_X(tickRate_, dummy) tick_((tickRate_), nullptr) -#endif // ndef Q_SPY - -//${QF-macros::TICK} ......................................................... -//! Invoke the system clock tick processing for rate 0 -//! @sa TICK_X() -#define TICK(sender_) TICK_X(0U, (sender_)) - -//${QF-macros::QF_CRIT_EXIT_NOP} ............................................. -#ifndef QF_CRIT_EXIT_NOP -//! No-operation for exiting a critical section -//! -//! @details -//! In some QF ports the critical section exit takes effect only on the -//! next machine instruction. If this next instruction is another entry -//! to a critical section, the critical section won't be really exited, -//! but rather the two adjecent critical sections would be merged. -//! The QF_CRIT_EXIT_NOP() macro contains minimal code required to -//! prevent such merging of critical sections in such merging of -//! critical sections in QF ports, in which it can occur. -#define QF_CRIT_EXIT_NOP() (static_cast(0)) -#endif // ndef QF_CRIT_EXIT_NOP -//$enddecl${QF-macros} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -#endif // QF_HPP_ diff --git a/src/qf/qep_hsm.cpp b/src/qf/qep_hsm.cpp new file mode 100644 index 0000000..59600f7 --- /dev/null +++ b/src/qf/qep_hsm.cpp @@ -0,0 +1,626 @@ +//$file${src::qf::qep_hsm.cpp} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv +// +// Model: qpcpp.qm +// File: ${src::qf::qep_hsm.cpp} +// +// This code has been generated by QM 6.2.3 . +// DO NOT EDIT THIS FILE MANUALLY. All your changes will be lost. +// +// ____________________________________ +// / / +// / GGGGGGG PPPPPPPP LL / +// / GG GG PP PP LL / +// / GG PP PP LL / +// / GG GGGGG PPPPPPPP LL / +// / GG GG PP LL / +// / GGGGGGG PP LLLLLLL / +// /___________________________________/ +// +// Copyright (c) 2005 Quantum Leaps, LLC +// SPDX-License-Identifier: GPL-3.0-or-later +// +// This generated code is open-source software licensed under the GNU +// General Public License (GPL) as published by the Free Software Foundation +// (see ). +// +// NOTE: +// The GPL does NOT permit the incorporation of this code into proprietary +// programs. Please contact Quantum Leaps for commercial licensing options, +// which expressly supersede the GPL and are designed explicitly for licensees +// interested in retaining the proprietary status of the generated code. +// +// Quantum Leaps contact information: +// +// +// +//$endhead${src::qf::qep_hsm.cpp} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +#define QP_IMPL // this is QP implementation +#include "qp_port.hpp" // QP port +#include "qp_pkg.hpp" // QP package-scope interface +#include "qsafe.h" // QP Functional Safety (FuSa) Subsystem +#ifdef Q_SPY // QS software tracing enabled? + #include "qs_port.hpp" // QS port + #include "qs_pkg.hpp" // QS facilities for pre-defined trace records +#else + #include "qs_dummy.hpp" // disable the QS software tracing +#endif // Q_SPY + +//============================================================================ +//! @cond INTERNAL + +// unnamed namespace for local definitions with internal linkage +namespace { +Q_DEFINE_THIS_MODULE("qep_hsm") + +// immutable events corresponding to the reserved signals. +static QP::QEvt const l_reservedEvt_[4] { + QP::QEvt(static_cast(QP::QHsm::Q_EMPTY_SIG)), + QP::QEvt(static_cast(QP::QHsm::Q_ENTRY_SIG)), + QP::QEvt(static_cast(QP::QHsm::Q_EXIT_SIG)), + QP::QEvt(static_cast(QP::QHsm::Q_INIT_SIG)) +}; + +} // unnamed namespace + +// helper macro to handle reserved event in an QHsm +#define QHSM_RESERVED_EVT_(state_, sig_) \ + ((*(state_))(this, &l_reservedEvt_[(sig_)])) + +// helper macro to trace state entry +#define QS_STATE_ENTRY_(state_, qs_id_) \ + QS_CRIT_ENTRY(); \ + QS_MEM_SYS(); \ + QS_BEGIN_PRE_(QS_QEP_STATE_ENTRY, (qs_id_)) \ + QS_OBJ_PRE_(this); \ + QS_FUN_PRE_(state_); \ + QS_END_PRE_() \ + QS_MEM_APP(); \ + QS_CRIT_EXIT() + +// helper macro to trace state exit +#define QS_STATE_EXIT_(state_, qs_id_) \ + QS_CRIT_ENTRY(); \ + QS_MEM_SYS(); \ + QS_BEGIN_PRE_(QS_QEP_STATE_EXIT, (qs_id_)) \ + QS_OBJ_PRE_(this); \ + QS_FUN_PRE_(state_); \ + QS_END_PRE_() \ + QS_MEM_APP(); \ + QS_CRIT_EXIT() + +//! @endcond +//============================================================================ + +//$skip${QP_VERSION} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv +// Check for the minimum required QP version +#if (QP_VERSION < 730U) || (QP_VERSION != ((QP_RELEASE^4294967295U)%0x2710U)) +#error qpcpp version 7.3.0 or higher required +#endif +//$endskip${QP_VERSION} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +//$define${QEP::versionStr[]} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv +namespace QP { + +} // namespace QP +//$enddef${QEP::versionStr[]} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +//$define${QEP::QHsm} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv +namespace QP { + +//${QEP::QHsm} ............................................................... + +//${QEP::QHsm::QHsm} ......................................................... +QHsm::QHsm(QStateHandler const initial) noexcept +: QAsm() +{ + m_state.fun = Q_STATE_CAST(&top); + m_temp.fun = initial; +} + +//${QEP::QHsm::init} ......................................................... +void QHsm::init( + void const * const e, + std::uint_fast8_t const qs_id) +{ + 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(&QP::QHsm::top); + } + QS_MEM_APP(); + QS_CRIT_EXIT(); + #else + Q_UNUSED_PAR(qs_id); + #endif + + QStateHandler t = m_state.fun; + + QF_CRIT_ENTRY(); + Q_REQUIRE_INCRIT(200, (m_temp.fun != nullptr) + && (t == Q_STATE_CAST(&top))); + QF_CRIT_EXIT(); + + // execute the top-most initial tran. + QState r = (*m_temp.fun)(this, Q_EVT_CAST(QEvt)); + + QF_CRIT_ENTRY(); + // the top-most initial tran. must be taken + Q_ASSERT_INCRIT(210, r == Q_RET_TRAN); + + QS_MEM_SYS(); + QS_BEGIN_PRE_(QS_QEP_STATE_INIT, qs_id) + QS_OBJ_PRE_(this); // this state machine object + QS_FUN_PRE_(t); // the source state + QS_FUN_PRE_(m_temp.fun); // the target of the initial tran. + QS_END_PRE_() + QS_MEM_APP(); + + QF_CRIT_EXIT(); + + // drill down into the state hierarchy with initial transitions... + do { + QStateHandler path[MAX_NEST_DEPTH_]; // tran entry path array + std::int_fast8_t ip = 0; // entry path index + + path[0] = m_temp.fun; + static_cast(QHSM_RESERVED_EVT_(m_temp.fun, Q_EMPTY_SIG)); + while (m_temp.fun != t) { + ++ip; + QF_CRIT_ENTRY(); + Q_ASSERT_INCRIT(220, ip < MAX_NEST_DEPTH_); + QF_CRIT_EXIT(); + path[ip] = m_temp.fun; + static_cast(QHSM_RESERVED_EVT_(m_temp.fun, Q_EMPTY_SIG)); + } + m_temp.fun = path[0]; + + // retrace the entry path in reverse (desired) order... + do { + // enter path[ip] + if (QHSM_RESERVED_EVT_(path[ip], Q_ENTRY_SIG) + == Q_RET_HANDLED) + { + QS_STATE_ENTRY_(path[ip], 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_(this); // this state machine object + QS_FUN_PRE_(t); // the source state + QS_FUN_PRE_(m_temp.fun); // the target of the initial tran. + QS_END_PRE_() + QS_MEM_APP(); + QS_CRIT_EXIT(); + } + #endif // Q_SPY + + } 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_(this); // this state machine object + QS_FUN_PRE_(t); // the new active state + QS_END_PRE_() + QS_MEM_APP(); + QS_CRIT_EXIT(); + + m_state.fun = t; // change the current active state + #ifndef Q_UNSAFE + m_temp.uint = ~m_state.uint; + #endif +} + +//${QEP::QHsm::dispatch} ..................................................... +void QHsm::dispatch( + QEvt const * const e, + std::uint_fast8_t const qs_id) +{ + #ifndef Q_SPY + Q_UNUSED_PAR(qs_id); + #endif + + QStateHandler s = m_state.fun; + QStateHandler t = s; + QF_CRIT_STAT + + QF_CRIT_ENTRY(); + Q_REQUIRE_INCRIT(300, (s != Q_STATE_CAST(0)) + && (m_state.uint == static_cast(~m_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_(this); // this state machine object + QS_FUN_PRE_(s); // the current state + QS_END_PRE_() + QS_MEM_APP(); + + QF_CRIT_EXIT(); + + // process the event hierarchically... + QState r; + m_temp.fun = s; + do { + s = m_temp.fun; + r = (*s)(this, e); // invoke state handler s + + if (r == Q_RET_UNHANDLED) { // unhandled due to a guard? + + QS_CRIT_ENTRY(); + QS_MEM_SYS(); + QS_BEGIN_PRE_(QS_QEP_UNHANDLED, qs_id) + QS_SIG_PRE_(e->sig); // the signal of the event + QS_OBJ_PRE_(this); // this state machine object + QS_FUN_PRE_(s); // the current state + QS_END_PRE_() + QS_MEM_APP(); + QS_CRIT_EXIT(); + + r = QHSM_RESERVED_EVT_(s, Q_EMPTY_SIG); // superstate of s + } + } while (r == Q_RET_SUPER); + + if (r >= Q_RET_TRAN) { // regular tran. taken? + QStateHandler path[MAX_NEST_DEPTH_]; + + path[0] = m_temp.fun; // tran. target + path[1] = t; // current state + path[2] = s; // tran. source + + // exit current state to tran. source s... + for (; t != s; t = m_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 + static_cast(QHSM_RESERVED_EVT_(t, Q_EMPTY_SIG)); + } + } + std::int_fast8_t ip = hsm_tran(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_(this); // this state machine object + QS_FUN_PRE_(t); // the source of the transition + QS_FUN_PRE_(path[0]); // the target of the tran. to history + QS_END_PRE_() + QS_MEM_APP(); + QS_CRIT_EXIT(); + } + #endif // Q_SPY + + // execute state entry actions in the desired order... + for (; ip >= 0; --ip) { + // enter path[ip] + if (QHSM_RESERVED_EVT_(path[ip], Q_ENTRY_SIG) + == Q_RET_HANDLED) + { + QS_STATE_ENTRY_(path[ip], qs_id); + } + } + t = path[0]; // stick the target into register + m_temp.fun = t; // update the next state + + // drill into the target hierarchy... + while (QHSM_RESERVED_EVT_(t, Q_INIT_SIG) == Q_RET_TRAN) { + + QS_CRIT_ENTRY(); + QS_MEM_SYS(); + QS_BEGIN_PRE_(QS_QEP_STATE_INIT, qs_id) + QS_OBJ_PRE_(this); // this state machine object + QS_FUN_PRE_(t); // the source (pseudo)state + QS_FUN_PRE_(m_temp.fun); // the target of the tran. + QS_END_PRE_() + QS_MEM_APP(); + QS_CRIT_EXIT(); + + ip = 0; + path[0] = m_temp.fun; + + // find superstate + static_cast(QHSM_RESERVED_EVT_(m_temp.fun, Q_EMPTY_SIG)); + + while (m_temp.fun != t) { + ++ip; + path[ip] = m_temp.fun; + // find superstate + static_cast( + QHSM_RESERVED_EVT_(m_temp.fun, Q_EMPTY_SIG)); + } + m_temp.fun = path[0]; + + // entry path must not overflow + QF_CRIT_ENTRY(); + Q_ASSERT_INCRIT(410, ip < 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_(this); // this state machine object + QS_FUN_PRE_(s); // the source of the tran. + QS_FUN_PRE_(t); // the new active state + QS_END_PRE_() + QS_MEM_APP(); + QS_CRIT_EXIT(); + } + + #ifdef Q_SPY + else if (r == Q_RET_HANDLED) { + QS_CRIT_ENTRY(); + QS_MEM_SYS(); + QS_BEGIN_PRE_(QS_QEP_INTERN_TRAN, qs_id) + QS_TIME_PRE_(); // time stamp + QS_SIG_PRE_(e->sig); // the signal of the event + QS_OBJ_PRE_(this); // this state machine object + QS_FUN_PRE_(s); // the source state + QS_END_PRE_() + QS_MEM_APP(); + QS_CRIT_EXIT(); + } + else { + QS_CRIT_ENTRY(); + QS_MEM_SYS(); + QS_BEGIN_PRE_(QS_QEP_IGNORED, qs_id) + QS_TIME_PRE_(); // time stamp + QS_SIG_PRE_(e->sig); // the signal of the event + QS_OBJ_PRE_(this); // this state machine object + QS_FUN_PRE_(m_state.fun); // the current state + QS_END_PRE_() + QS_MEM_APP(); + QS_CRIT_EXIT(); + } + #endif // Q_SPY + + m_state.fun = t; // change the current active state + #ifndef Q_UNSAFE + m_temp.uint = ~m_state.uint; + #endif +} + +//${QEP::QHsm::isIn} ......................................................... +bool QHsm::isIn(QStateHandler const state) noexcept { + QF_CRIT_STAT + QF_CRIT_ENTRY(); + Q_REQUIRE_INCRIT(602, m_state.uint + == static_cast(~m_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 (m_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_(m_temp.fun, Q_EMPTY_SIG); + } + } while (r != Q_RET_IGNORED); // QHsm::top() state not reached + + #ifndef Q_UNSAFE + m_temp.uint = ~m_state.uint; + #endif + + return inState; // return the status +} + +//${QEP::QHsm::childState} ................................................... +QStateHandler QHsm::childState(QStateHandler const parent) noexcept { + QStateHandler child = m_state.fun; // start with the current state + bool isFound = false; // start with the child not found + + // establish stable state configuration + m_temp.fun = child; + QState r; + do { + // is this the parent of the current child? + if (m_temp.fun == parent) { + isFound = true; // child is found + r = Q_RET_IGNORED; // break out of the loop + } + else { + child = m_temp.fun; + r = QHSM_RESERVED_EVT_(m_temp.fun, Q_EMPTY_SIG); + } + } while (r != Q_RET_IGNORED); // QHsm::top() state not reached + + #ifndef Q_UNSAFE + m_temp.uint = ~m_state.uint; + #endif + + QF_CRIT_STAT + QF_CRIT_ENTRY(); + Q_ASSERT_INCRIT(890, isFound); + QF_CRIT_EXIT(); + + return child; // return the child +} + +//${QEP::QHsm::hsm_tran} ..................................................... +std::int_fast8_t QHsm::hsm_tran( + QStateHandler (&path)[MAX_NEST_DEPTH_], + std::uint_fast8_t const qs_id) +{ + #ifndef Q_SPY + Q_UNUSED_PAR(qs_id); + #endif + + std::int_fast8_t ip = -1; // tran. entry path index + QStateHandler t = path[0]; + QStateHandler const s = path[2]; + QF_CRIT_STAT + + // (a) check source==target (tran. to self)... + if (s == t) { + // exit source s + if (QHSM_RESERVED_EVT_(s, Q_EXIT_SIG) == Q_RET_HANDLED) { + QS_STATE_EXIT_(s, qs_id); + } + ip = 0; // enter the target + } + else { + // find superstate of target + static_cast(QHSM_RESERVED_EVT_(t, Q_EMPTY_SIG)); + + t = m_temp.fun; + + // (b) check source==target->super... + if (s == t) { + ip = 0; // enter the target + } + else { + // find superstate of src + static_cast(QHSM_RESERVED_EVT_(s, Q_EMPTY_SIG)); + + // (c) check source->super==target->super... + if (m_temp.fun == t) { + // exit source s + if (QHSM_RESERVED_EVT_(s, Q_EXIT_SIG) == Q_RET_HANDLED) { + QS_STATE_EXIT_(s, qs_id); + } + ip = 0; // enter the target + } + else { + // (d) check source->super==target... + if (m_temp.fun == path[0]) { + // exit source s + if (QHSM_RESERVED_EVT_(s, Q_EXIT_SIG) == Q_RET_HANDLED) { + QS_STATE_EXIT_(s, qs_id); + } + } + else { + // (e) check rest of source==target->super->super.. + // and store the entry path along the way + std::int_fast8_t iq = 0; // indicate that LCA was found + ip = 1; // enter target and its superstate + path[1] = t; // save the superstate of target + t = m_temp.fun; // save source->super + + // find target->super->super... + QState r = QHSM_RESERVED_EVT_(path[1], Q_EMPTY_SIG); + while (r == Q_RET_SUPER) { + ++ip; + path[ip] = m_temp.fun; // store the entry path + if (m_temp.fun == s) { // is it the source? + iq = 1; // indicate that the LCA found + + // entry path must not overflow + QF_CRIT_ENTRY(); + Q_ASSERT_INCRIT(510, ip < 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_(m_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 < 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 + static_cast( + QHSM_RESERVED_EVT_(t, Q_EMPTY_SIG)); + } + t = m_temp.fun; // set to super of t + iq = ip; + do { + // is this the LCA? + if (t == path[iq]) { + ip = iq - 1; // do not enter the LCA + iq = -1; // break out of inner loop + r = Q_RET_HANDLED; // break outer loop + } + else { + --iq; + } + } while (iq >= 0); + } while (r != Q_RET_HANDLED); + } + } + } + } + } + } + return ip; +} + +} // namespace QP +//$enddef${QEP::QHsm} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/src/qep_msm.cpp b/src/qf/qep_msm.cpp similarity index 62% rename from src/qep_msm.cpp rename to src/qf/qep_msm.cpp index 7799bf2..9af05e6 100644 --- a/src/qep_msm.cpp +++ b/src/qf/qep_msm.cpp @@ -3,116 +3,145 @@ // Model: qpcpp.qm // File: ${src::qf::qep_msm.cpp} // -// This code has been generated by QM 5.2.5 . +// This code has been generated by QM 6.2.3 . // DO NOT EDIT THIS FILE MANUALLY. All your changes will be lost. // -// This code is covered by the following QP license: -// License # : LicenseRef-QL-dual -// Issued to : Any user of the QP/C++ real-time embedded framework -// Framework(s) : qpcpp -// Support ends : 2023-12-31 -// License scope: +// ____________________________________ +// / / +// / GGGGGGG PPPPPPPP LL / +// / GG GG PP PP LL / +// / GG PP PP LL / +// / GG GGGGG PPPPPPPP LL / +// / GG GG PP LL / +// / GGGGGGG PP LLLLLLL / +// /___________________________________/ // -// Copyright (C) 2005 Quantum Leaps, LLC . +// Copyright (c) 2005 Quantum Leaps, LLC +// SPDX-License-Identifier: GPL-3.0-or-later // -// SPDX-License-Identifier: GPL-3.0-or-later OR LicenseRef-QL-commercial +// This generated code is open-source software licensed under the GNU +// General Public License (GPL) as published by the Free Software Foundation +// (see ). // -// 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. +// NOTE: +// The GPL does NOT permit the incorporation of this code into proprietary +// programs. Please contact Quantum Leaps for commercial licensing options, +// which expressly supersede the GPL and are designed explicitly for licensees +// interested in retaining the proprietary status of the generated code. // -// The terms of the open source GNU General Public License version 3 -// can be found at: -// -// The terms of the closed source Quantum Leaps commercial licenses -// can be found at: -// -// Redistributions in source code must retain this top-level comment block. -// Plagiarizing this software to sidestep the license obligations is illegal. -// -// Contact information: +// Quantum Leaps contact information: // // // //$endhead${src::qf::qep_msm.cpp} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -//! @file -//! @brief QP::QMsm implementation - #define QP_IMPL // this is QP implementation -#include "qep_port.hpp" // QEP port +#include "qp_port.hpp" // QP port +#include "qp_pkg.hpp" // QP package-scope interface +#include "qsafe.h" // QP Functional Safety (FuSa) Subsystem #ifdef Q_SPY // QS software tracing enabled? #include "qs_port.hpp" // QS port #include "qs_pkg.hpp" // QS facilities for pre-defined trace records #else #include "qs_dummy.hpp" // disable the QS software tracing #endif // Q_SPY -#include "qassert.h" // QP embedded systems-friendly assertions + +//============================================================================ +//! @cond INTERNAL // unnamed namespace for local definitions with internal linkage namespace { + Q_DEFINE_THIS_MODULE("qep_msm") + +// top-state object for QMsm-style state machines +QP::QMState const l_msm_top_s = { + nullptr, + nullptr, + nullptr, + nullptr, + nullptr +}; + +static constexpr std::int_fast8_t MAX_ENTRY_DEPTH_ {4}; + } // unnamed namespace +//! @endcond //============================================================================ + //$skip${QP_VERSION} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv // Check for the minimum required QP version -#if (QP_VERSION < 700U) || (QP_VERSION != ((QP_RELEASE^4294967295U) % 0x3E8U)) -#error qpcpp version 7.0.0 or higher required +#if (QP_VERSION < 730U) || (QP_VERSION != ((QP_RELEASE^4294967295U)%0x2710U)) +#error qpcpp version 7.3.0 or higher required #endif //$endskip${QP_VERSION} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - //$define${QEP::QMsm} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv namespace QP { //${QEP::QMsm} ............................................................... -QMState const QMsm::msm_top_s = { - nullptr, - nullptr, - nullptr, - nullptr, - nullptr -}; +//${QEP::QMsm::QMsm} ......................................................... +QMsm::QMsm(QStateHandler const initial) noexcept + : QAsm() +{ + m_state.obj = &l_msm_top_s; // the current state (top) + m_temp.fun = initial; // the initial tran. handler +} //${QEP::QMsm::init} ......................................................... void QMsm::init( void const * const e, std::uint_fast8_t const qs_id) { - //! @pre the top-most initial transition must be initialized, and the - //! initial transition must not be taken yet. - Q_REQUIRE_ID(200, (m_temp.fun != nullptr) - && (m_state.obj == &msm_top_s)); + #ifndef Q_SPY + Q_UNUSED_PAR(qs_id); + #endif + + QF_CRIT_STAT + QF_CRIT_ENTRY(); + Q_REQUIRE_INCRIT(200, (m_temp.fun != nullptr) + && (m_state.obj == &l_msm_top_s)); + QF_CRIT_EXIT(); // execute the top-most initial tran. QState r = (*m_temp.fun)(this, Q_EVT_CAST(QEvt)); - // initial tran. must be taken - Q_ASSERT_ID(210, r == Q_RET_TRAN_INIT); + QF_CRIT_ENTRY(); + // the top-most initial tran. must be taken + Q_ASSERT_INCRIT(210, r == Q_RET_TRAN_INIT); - QS_CRIT_STAT_ + QS_MEM_SYS(); QS_BEGIN_PRE_(QS_QEP_STATE_INIT, qs_id) QS_OBJ_PRE_(this); // this state machine object - QS_FUN_PRE_(m_state.obj->stateHandler); // source handler - QS_FUN_PRE_(m_temp.tatbl->target->stateHandler); // target handler + QS_FUN_PRE_(m_state.obj->stateHandler); // source state + QS_FUN_PRE_(m_temp.tatbl->target->stateHandler); // target state QS_END_PRE_() + QS_MEM_APP(); + + QF_CRIT_EXIT(); // set state to the last tran. target m_state.obj = m_temp.tatbl->target; // drill down into the state hierarchy with initial transitions... do { - r = execTatbl_(m_temp.tatbl, qs_id); // execute the tran-action table + // execute the tran. table + r = execTatbl_(m_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_(this); // this state machine object + QS_TIME_PRE_(); // time stamp + QS_OBJ_PRE_(this); // this state machine object QS_FUN_PRE_(m_state.obj->stateHandler); // the new current state QS_END_PRE_() + QS_MEM_APP(); + QS_CRIT_EXIT(); - Q_UNUSED_PAR(qs_id); // when Q_SPY not defined + #ifndef Q_UNSAFE + m_temp.uint = ~m_state.uint; + #endif } //${QEP::QMsm::dispatch} ..................................................... @@ -120,20 +149,31 @@ void QMsm::dispatch( QEvt const * const e, std::uint_fast8_t const qs_id) { - QMState const *s = m_state.obj; // store the current state - //! @pre current state must be initialized - Q_REQUIRE_ID(300, s != nullptr); + #ifndef Q_SPY + Q_UNUSED_PAR(qs_id); + #endif + + QMState const *s = m_state.obj; // store the current state + QMState const *t = s; + + QF_CRIT_STAT + QF_CRIT_ENTRY(); + Q_REQUIRE_INCRIT(300, (s != nullptr) + && (m_state.uint == static_cast(~m_temp.uint))); + Q_REQUIRE_INCRIT(302, QEvt::verify_(e)); - QS_CRIT_STAT_ + 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_(this); // this state machine object QS_FUN_PRE_(s->stateHandler); // the current state handler QS_END_PRE_() + QS_MEM_APP(); + + QF_CRIT_EXIT(); // scan the state hierarchy up to the top state... - QMState const *t = s; QState r; do { r = (*t->stateHandler)(this, e); // call state handler function @@ -150,36 +190,39 @@ void QMsm::dispatch( else if (r == Q_RET_SUPER_SUB) { t = m_temp.obj; // current host state of the submachie } - // event unhandled due to a guard? - else if (r == Q_RET_UNHANDLED) { + 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_(this); // this state machine object + QS_SIG_PRE_(e->sig); // the signal of the event + QS_OBJ_PRE_(this); // this state machine object QS_FUN_PRE_(t->stateHandler); // the current state QS_END_PRE_() + QS_MEM_APP(); + + QF_CRIT_EXIT(); t = t->superstate; // advance to the superstate } - else { - // no other return value should be produced - Q_ERROR_ID(310); - } } while (t != nullptr); - // any kind of transition taken? - if (r >= Q_RET_TRAN) { + if (r >= Q_RET_TRAN) { // any kind of tran. taken? #ifdef Q_SPY QMState const * const ts = t; // transition source for QS tracing - // the transition source state must not be nullptr - Q_ASSERT_ID(320, ts != nullptr); + QF_CRIT_ENTRY(); + // the tran. source state must not be nullptr + Q_ASSERT_INCRIT(320, ts != nullptr); + QF_CRIT_EXIT(); #endif // Q_SPY do { - // save the transition-action table before it gets clobbered + // save the tran-action table before it gets clobbered QMTranActTable const * const tatbl = m_temp.tatbl; - QHsmAttr tmp; // temporary to save intermediate values + QAsmAttr tmp; // temporary to save intermediate values // was TRAN, TRAN_INIT, or TRAN_EP taken? if (r <= Q_RET_TRAN_EP) { @@ -188,7 +231,7 @@ void QMsm::dispatch( r = execTatbl_(tatbl, qs_id); s = m_state.obj; } - // was a transition segment to history taken? + // was a tran. segment to history taken? else if (r == Q_RET_TRAN_HIST) { tmp.obj = m_state.obj; // save history m_state.obj = s; // restore the original state @@ -197,8 +240,12 @@ void QMsm::dispatch( r = enterHistory_(tmp.obj, qs_id); s = m_state.obj; } - // was a transition segment to an exit point taken? - else if (r == Q_RET_TRAN_XP) { + else { + QF_CRIT_ENTRY(); + // must be tran. to exit point + Q_ASSERT_INCRIT(340, r == Q_RET_TRAN_XP); + QF_CRIT_EXIT(); + tmp.act = m_state.act; // save XP action m_state.obj = s; // restore the original state r = (*tmp.act)(this); // execute the XP action @@ -230,71 +277,80 @@ void QMsm::dispatch( m_state.obj = tmp.obj; // restore the history } else { + QF_CRIT_ENTRY(); // TRAN_XP must NOT be followed by any other tran type - Q_ASSERT_ID(330, r < Q_RET_TRAN); + Q_ASSERT_INCRIT(330, r < Q_RET_TRAN); + QF_CRIT_EXIT(); } } - else { - // no other return value should be produced - Q_ERROR_ID(340); - } 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_(this); // this state machine object - QS_FUN_PRE_(ts->stateHandler); // the transition source - QS_FUN_PRE_(s->stateHandler); // the new active state + QS_TIME_PRE_(); // time stamp + QS_SIG_PRE_(e->sig); // the signal of the event + QS_OBJ_PRE_(this); // this state machine object + QS_FUN_PRE_(ts->stateHandler); // the tran. source + QS_FUN_PRE_(s->stateHandler); // the new active state QS_END_PRE_() + QS_MEM_APP(); + 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 nullptr - Q_ASSERT_ID(340, t != nullptr); + Q_ASSERT_INCRIT(340, t != nullptr); + 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_(this); // this state machine object - QS_FUN_PRE_(t->stateHandler); // the source state + QS_TIME_PRE_(); // time stamp + QS_SIG_PRE_(e->sig); // the signal of the event + QS_OBJ_PRE_(this); // this state machine object + QS_FUN_PRE_(t->stateHandler); // the source state QS_END_PRE_() + QS_MEM_APP(); + QF_CRIT_EXIT(); } // event bubbled to the 'top' state? else if (t == nullptr) { - + QS_CRIT_ENTRY(); + QS_MEM_SYS(); QS_BEGIN_PRE_(QS_QEP_IGNORED, qs_id) - QS_TIME_PRE_(); // time stamp - QS_SIG_PRE_(e->sig); // the signal of the event - QS_OBJ_PRE_(this); // this state machine object - QS_FUN_PRE_(s->stateHandler); // the current state + QS_TIME_PRE_(); // time stamp + QS_SIG_PRE_(e->sig); // the signal of the event + QS_OBJ_PRE_(this); // this state machine object + QS_FUN_PRE_(s->stateHandler); // the current state QS_END_PRE_() - + QS_MEM_APP(); + QS_CRIT_EXIT(); } #endif // Q_SPY - else { // empty } - Q_UNUSED_PAR(qs_id); // when Q_SPY not defined + #ifndef Q_UNSAFE + m_temp.uint = ~m_state.uint; + #endif } //${QEP::QMsm::isInState} .................................................... -bool QMsm::isInState(QMState const * const st) const noexcept { +bool QMsm::isInState(QMState const * const stateObj) const noexcept { bool inState = false; // assume that this MSM is not in 'state' for (QMState const *s = m_state.obj; s != nullptr; s = s->superstate) { - if (s == st) { + if (s == stateObj) { inState = true; // match found, return 'true' break; } @@ -329,12 +385,10 @@ QMState const * QMsm::childStateObj(QMState const * const parent) const noexcept } } - //! @post the child must be found - Q_ENSURE_ID(810, isFound); - - #ifdef Q_NASSERT - Q_UNUSED_PAR(isFound); - #endif + QF_CRIT_STAT + QF_CRIT_ENTRY(); + Q_ASSERT_INCRIT(890, isFound); + QF_CRIT_EXIT(); return child; // return the child } @@ -344,30 +398,36 @@ QState QMsm::execTatbl_( QMTranActTable const * const tatbl, std::uint_fast8_t const qs_id) { - //! @pre the transition-action table pointer must not be nullptr - Q_REQUIRE_ID(400, tatbl != nullptr); + #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 != nullptr); + QF_CRIT_EXIT(); QState r = Q_RET_NULL; - QS_CRIT_STAT_ for (QActionHandler const *a = &tatbl->act[0]; *a != nullptr; ++a) { r = (*(*a))(this); // call the action through the 'a' pointer #ifdef Q_SPY + QS_CRIT_ENTRY(); + QS_MEM_SYS(); if (r == Q_RET_ENTRY) { - QS_BEGIN_PRE_(QS_QEP_STATE_ENTRY, qs_id) QS_OBJ_PRE_(this); // this state machine object - QS_FUN_PRE_(m_temp.obj->stateHandler); // entered state handler + QS_FUN_PRE_(m_temp.obj->stateHandler); // entered state QS_END_PRE_() } else if (r == Q_RET_EXIT) { - QS_BEGIN_PRE_(QS_QEP_STATE_EXIT, qs_id) QS_OBJ_PRE_(this); // this state machine object - QS_FUN_PRE_(m_temp.obj->stateHandler); // exited state handler + QS_FUN_PRE_(m_temp.obj->stateHandler); // exited state QS_END_PRE_() } else if (r == Q_RET_TRAN_INIT) { - QS_BEGIN_PRE_(QS_QEP_STATE_INIT, qs_id) QS_OBJ_PRE_(this); // this state machine object QS_FUN_PRE_(tatbl->target->stateHandler); // source @@ -375,7 +435,6 @@ QState QMsm::execTatbl_( QS_END_PRE_() } else if (r == Q_RET_TRAN_EP) { - QS_BEGIN_PRE_(QS_QEP_TRAN_EP, qs_id) QS_OBJ_PRE_(this); // this state machine object QS_FUN_PRE_(tatbl->target->stateHandler); // source @@ -383,7 +442,6 @@ QState QMsm::execTatbl_( QS_END_PRE_() } else if (r == Q_RET_TRAN_XP) { - QS_BEGIN_PRE_(QS_QEP_TRAN_XP, qs_id) QS_OBJ_PRE_(this); // this state machine object QS_FUN_PRE_(tatbl->target->stateHandler); // source @@ -393,10 +451,11 @@ QState QMsm::execTatbl_( else { // empty } + QS_MEM_APP(); + QS_CRIT_EXIT(); #endif // Q_SPY } - Q_UNUSED_PAR(qs_id); // when Q_SPY not defined m_state.obj = (r >= Q_RET_TRAN) ? m_temp.tatbl->target : tatbl->target; @@ -405,33 +464,43 @@ QState QMsm::execTatbl_( //${QEP::QMsm::exitToTranSource_} ............................................ void QMsm::exitToTranSource_( - QMState const * s, + QMState const * const cs, QMState const * const ts, std::uint_fast8_t const qs_id) { + #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 != nullptr) { // execute the exit action - (*s->exitAction)(this); + static_cast((*s->exitAction)(this)); - QS_CRIT_STAT_ + QS_CRIT_ENTRY(); + QS_MEM_SYS(); QS_BEGIN_PRE_(QS_QEP_STATE_EXIT, qs_id) QS_OBJ_PRE_(this); // this state machine object QS_FUN_PRE_(s->stateHandler); // the exited state handler QS_END_PRE_() + QS_MEM_APP(); + QS_CRIT_EXIT(); } s = s->superstate; // advance to the superstate - // reached the top of a submachine? - if (s == nullptr) { + if (s == nullptr) { // reached the top of a submachine? s = m_temp.obj; // the superstate from QM_SM_EXIT() - Q_ASSERT_ID(510, s != nullptr); + QF_CRIT_ENTRY(); + Q_ASSERT_INCRIT(510, s != nullptr); + QF_CRIT_EXIT(); } } - Q_UNUSED_PAR(qs_id); // when Q_SPY not defined } //${QEP::QMsm::enterHistory_} ................................................ @@ -439,22 +508,32 @@ QState QMsm::enterHistory_( QMState const * const hist, std::uint_fast8_t const qs_id) { + #ifndef Q_SPY + Q_UNUSED_PAR(qs_id); + #endif + QMState const *s = hist; - QMState const *ts = m_state.obj; // transition source + QMState const *ts = m_state.obj; // tran. source QMState const *epath[MAX_ENTRY_DEPTH_]; - QS_CRIT_STAT_ + QF_CRIT_STAT + QS_CRIT_ENTRY(); + QS_MEM_SYS(); QS_BEGIN_PRE_(QS_QEP_TRAN_HIST, qs_id) QS_OBJ_PRE_(this); // this state machine object QS_FUN_PRE_(ts->stateHandler); // source state handler QS_FUN_PRE_(hist->stateHandler); // target state handler QS_END_PRE_() + QS_MEM_APP(); + QS_CRIT_EXIT(); - std::int_fast8_t i = 0; // entry path index + std::int_fast8_t i = 0; // tran. entry path index while (s != ts) { if (s->entryAction != nullptr) { - Q_ASSERT_ID(620, i < MAX_ENTRY_DEPTH_); + QF_CRIT_ENTRY(); + Q_ASSERT_INCRIT(620, i < MAX_ENTRY_DEPTH_); + QF_CRIT_EXIT(); epath[i] = s; ++i; } @@ -467,29 +546,36 @@ QState QMsm::enterHistory_( // retrace the entry path in reverse (desired) order... while (i > 0) { --i; - // run entry action in epath[i] - (*epath[i]->entryAction)(this); + (*epath[i]->entryAction)(this); // run entry action in epath[i] + QS_CRIT_ENTRY(); + QS_MEM_SYS(); QS_BEGIN_PRE_(QS_QEP_STATE_ENTRY, qs_id) QS_OBJ_PRE_(this); QS_FUN_PRE_(epath[i]->stateHandler); // entered state handler QS_END_PRE_() + QS_MEM_APP(); + QS_CRIT_EXIT(); } - m_state.obj = hist; // set current state to the transition target + m_state.obj = hist; // set current state to the tran. target // initial tran. present? QState r; if (hist->initAction != nullptr) { - r = (*hist->initAction)(this); // execute the transition action + r = (*hist->initAction)(this); // execute the tran. action } else { r = Q_RET_NULL; } - Q_UNUSED_PAR(qs_id); // when Q_SPY not defined return r; } +//${QEP::QMsm::topQMState} ................................................... +QMState const * QMsm::topQMState() const noexcept { + return &l_msm_top_s; +} + } // namespace QP //$enddef${QEP::QMsm} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/src/qf/qf_act.cpp b/src/qf/qf_act.cpp new file mode 100644 index 0000000..d3cf7ec --- /dev/null +++ b/src/qf/qf_act.cpp @@ -0,0 +1,126 @@ +//$file${src::qf::qf_act.cpp} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv +// +// Model: qpcpp.qm +// File: ${src::qf::qf_act.cpp} +// +// This code has been generated by QM 6.2.3 . +// DO NOT EDIT THIS FILE MANUALLY. All your changes will be lost. +// +// ____________________________________ +// / / +// / GGGGGGG PPPPPPPP LL / +// / GG GG PP PP LL / +// / GG PP PP LL / +// / GG GGGGG PPPPPPPP LL / +// / GG GG PP LL / +// / GGGGGGG PP LLLLLLL / +// /___________________________________/ +// +// Copyright (c) 2005 Quantum Leaps, LLC +// SPDX-License-Identifier: GPL-3.0-or-later +// +// This generated code is open-source software licensed under the GNU +// General Public License (GPL) as published by the Free Software Foundation +// (see ). +// +// NOTE: +// The GPL does NOT permit the incorporation of this code into proprietary +// programs. Please contact Quantum Leaps for commercial licensing options, +// which expressly supersede the GPL and are designed explicitly for licensees +// interested in retaining the proprietary status of the generated code. +// +// Quantum Leaps contact information: +// +// +// +//$endhead${src::qf::qf_act.cpp} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +#define QP_IMPL // this is QP implementation +#include "qp_port.hpp" // QP port +#include "qp_pkg.hpp" // QP package-scope interface +#include "qsafe.h" // QP Functional Safety (FuSa) Subsystem +#ifdef Q_SPY // QS software tracing enabled? + #include "qs_port.hpp" // QS port + #include "qs_pkg.hpp" // QS facilities for pre-defined trace records +#else + #include "qs_dummy.hpp" // disable the QS software tracing +#endif // Q_SPY + +// unnamed namespace for local definitions with internal linkage +namespace { +//Q_DEFINE_THIS_MODULE("qf_act") +} // unnamed namespace + +//$skip${QP_VERSION} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv +// Check for the minimum required QP version +#if (QP_VERSION < 730U) || (QP_VERSION != ((QP_RELEASE^4294967295U)%0x2710U)) +#error qpcpp version 7.3.0 or higher required +#endif +//$endskip${QP_VERSION} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +//$define${QF::QActive::registry_[QF_MAX_ACTIVE + 1U]} vvvvvvvvvvvvvvvvvvvvvvv +namespace QP { +QActive * QActive::registry_[QF_MAX_ACTIVE + 1U]; + +} // namespace QP +//$enddef${QF::QActive::registry_[QF_MAX_ACTIVE + 1U]} ^^^^^^^^^^^^^^^^^^^^^^^ + +//$define${QF::QF-pkg} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv +namespace QP { +namespace QF { + +//${QF::QF-pkg::priv_} ....................................................... +QF::Attr priv_; + +//${QF::QF-pkg::bzero_} ...................................................... +void bzero_( + void * const start, + std::uint_fast16_t const len) noexcept +{ + std::uint8_t *ptr = static_cast(start); + for (std::uint_fast16_t n = len; n > 0U; --n) { + *ptr = 0U; + ++ptr; + } +} + +} // namespace QF +} // namespace QP +//$enddef${QF::QF-pkg} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +//$define${QF::types::QF_LOG2} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv +namespace QP { + +//${QF::types::QF_LOG2} ...................................................... +#ifndef QF_LOG2 +std::uint_fast8_t QF_LOG2(QP::QPSetBits x) noexcept { + static std::uint8_t const log2LUT[16] = { + 0U, 1U, 2U, 2U, 3U, 3U, 3U, 3U, + 4U, 4U, 4U, 4U, 4U, 4U, 4U, 4U + }; + std::uint_fast8_t n = 0U; + QP::QPSetBits t; + + #if (QF_MAX_ACTIVE > 16U) + t = static_cast(x >> 16U); + if (t != 0U) { + n += 16U; + x = t; + } + #endif + #if (QF_MAX_ACTIVE > 8U) + t = (x >> 8U); + if (t != 0U) { + n += 8U; + x = t; + } + #endif + t = (x >> 4U); + if (t != 0U) { + n += 4U; + x = t; + } + return n + log2LUT[x]; +} +#endif // ndef QF_LOG2 + +} // namespace QP +//$enddef${QF::types::QF_LOG2} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/src/qf/qf_actq.cpp b/src/qf/qf_actq.cpp new file mode 100644 index 0000000..af6d6b4 --- /dev/null +++ b/src/qf/qf_actq.cpp @@ -0,0 +1,439 @@ +//$file${src::qf::qf_actq.cpp} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv +// +// Model: qpcpp.qm +// File: ${src::qf::qf_actq.cpp} +// +// This code has been generated by QM 6.2.3 . +// DO NOT EDIT THIS FILE MANUALLY. All your changes will be lost. +// +// ____________________________________ +// / / +// / GGGGGGG PPPPPPPP LL / +// / GG GG PP PP LL / +// / GG PP PP LL / +// / GG GGGGG PPPPPPPP LL / +// / GG GG PP LL / +// / GGGGGGG PP LLLLLLL / +// /___________________________________/ +// +// Copyright (c) 2005 Quantum Leaps, LLC +// SPDX-License-Identifier: GPL-3.0-or-later +// +// This generated code is open-source software licensed under the GNU +// General Public License (GPL) as published by the Free Software Foundation +// (see ). +// +// NOTE: +// The GPL does NOT permit the incorporation of this code into proprietary +// programs. Please contact Quantum Leaps for commercial licensing options, +// which expressly supersede the GPL and are designed explicitly for licensees +// interested in retaining the proprietary status of the generated code. +// +// Quantum Leaps contact information: +// +// +// +//$endhead${src::qf::qf_actq.cpp} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +#define QP_IMPL // this is QP implementation +#include "qp_port.hpp" // QP port +#include "qp_pkg.hpp" // QP package-scope interface +#include "qsafe.h" // QP Functional Safety (FuSa) Subsystem +#ifdef Q_SPY // QS software tracing enabled? + #include "qs_port.hpp" // QS port + #include "qs_pkg.hpp" // QS facilities for pre-defined trace records +#else + #include "qs_dummy.hpp" // disable the QS software tracing +#endif // Q_SPY + +//============================================================================ +// unnamed namespace for local definitions with internal linkage +namespace { +Q_DEFINE_THIS_MODULE("qf_actq") +} // unnamed namespace + +//$skip${QP_VERSION} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv +// Check for the minimum required QP version +#if (QP_VERSION < 730U) || (QP_VERSION != ((QP_RELEASE^4294967295U)%0x2710U)) +#error qpcpp version 7.3.0 or higher required +#endif +//$endskip${QP_VERSION} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +//$define${QF::QActive::post_} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv +namespace QP { + +//${QF::QActive::post_} ...................................................... +bool QActive::post_( + QEvt const * const e, + std::uint_fast16_t const margin, + void const * const sender) noexcept +{ + #ifndef Q_SPY + Q_UNUSED_PAR(sender); + #endif + + #ifdef Q_UTEST // test? + #if Q_UTEST != 0 // testing QP-stub? + if (m_temp.fun == Q_STATE_CAST(0)) { // QActiveDummy? + return static_cast(this)->fakePost(e, margin, sender); + } + #endif + #endif + + QF_CRIT_STAT + QF_CRIT_ENTRY(); + QF_MEM_SYS(); + + Q_REQUIRE_INCRIT(102, QEvt::verify_(e)); + + QEQueueCtr nFree = m_eQueue.m_nFree; // get volatile into temporary + + // test-probe#1 for faking queue overflow + QS_TEST_PROBE_DEF(&QActive::post_) + QS_TEST_PROBE_ID(1, + nFree = 0U; + ) + + bool status; + if (margin == QF::NO_MARGIN) { + if (nFree > 0U) { + status = true; // can post + } + else { + status = false; // cannot post + Q_ERROR_INCRIT(190); // must be able to post the event + } + } + else if (nFree > static_cast(margin)) { + status = true; // can post + } + else { + status = false; // cannot post, but don't assert + } + + // is it a mutable event? + if (e->getPoolId_() != 0U) { + QEvt_refCtr_inc_(e); // increment the reference counter + } + + if (status) { // can post the event? + + --nFree; // one free entry just used up + m_eQueue.m_nFree = nFree; // update the original + if (m_eQueue.m_nMin > nFree) { + m_eQueue.m_nMin = nFree; // update minimum so far + } + + QS_BEGIN_PRE_(QS_QF_ACTIVE_POST, m_prio) + QS_TIME_PRE_(); // timestamp + QS_OBJ_PRE_(sender); // the sender object + QS_SIG_PRE_(e->sig); // the signal of the event + QS_OBJ_PRE_(this); // this active object + QS_2U8_PRE_(e->getPoolId_(), e->refCtr_); // poolId & refCtr + QS_EQC_PRE_(nFree); // # free entries + QS_EQC_PRE_(m_eQueue.m_nMin); // min # free entries + QS_END_PRE_() + + #ifdef Q_UTEST + // callback to examine the posted event under the same conditions + // as producing the #QS_QF_ACTIVE_POST trace record, which are: + // the local filter for this AO ('m_prio') is set + if (QS_LOC_CHECK_(m_prio)) { + QS::onTestPost(sender, this, e, status); + } + #endif + + if (m_eQueue.m_frontEvt == nullptr) { // empty queue? + m_eQueue.m_frontEvt = e; // deliver event directly + #ifdef QXK_HPP_ + if (m_state.act == Q_ACTION_CAST(0)) { // eXtended thread? + QXTHREAD_EQUEUE_SIGNAL_(this); // signal the event queue + } + else { + QACTIVE_EQUEUE_SIGNAL_(this); // signal the event queue + } + #else + QACTIVE_EQUEUE_SIGNAL_(this); // signal the event queue + #endif + } + // queue is not empty, insert event into the ring-buffer + else { + // insert event into the ring buffer (FIFO) + m_eQueue.m_ring[m_eQueue.m_head] = e; + + if (m_eQueue.m_head == 0U) { // need to wrap head? + m_eQueue.m_head = m_eQueue.m_end; // wrap around + } + // advance the head (counter clockwise) + m_eQueue.m_head = (m_eQueue.m_head - 1U); + } + + QF_MEM_APP(); + QF_CRIT_EXIT(); + } + else { // cannot post the event + + QS_BEGIN_PRE_(QS_QF_ACTIVE_POST_ATTEMPT, m_prio) + QS_TIME_PRE_(); // timestamp + QS_OBJ_PRE_(sender); // the sender object + QS_SIG_PRE_(e->sig); // the signal of the event + QS_OBJ_PRE_(this); // this active object + QS_2U8_PRE_(e->getPoolId_(), 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_(m_prio)) { + QS::onTestPost(sender, this, e, status); + } + #endif + + QF_MEM_APP(); + QF_CRIT_EXIT(); + + #if (QF_MAX_EPOOL > 0U) + QF::gc(e); // recycle the event to avoid a leak + #endif + } + + return status; +} + +} // namespace QP +//$enddef${QF::QActive::post_} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +//$define${QF::QActive::postLIFO} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv +namespace QP { + +//${QF::QActive::postLIFO} ................................................... +void QActive::postLIFO(QEvt const * const e) noexcept { + #ifdef Q_UTEST // test? + #if Q_UTEST != 0 // testing QP-stub? + if (m_temp.fun == Q_STATE_CAST(0)) { // QActiveDummy? + static_cast(this)->QActiveDummy::fakePostLIFO(e); + return; + } + #endif + #endif + + QF_CRIT_STAT + QF_CRIT_ENTRY(); + QF_MEM_SYS(); + + Q_REQUIRE_INCRIT(202, QEvt::verify_(e)); + + #ifdef QXK_HPP_ + Q_REQUIRE_INCRIT(200, m_state.act != Q_ACTION_CAST(0)); + #endif + + QEQueueCtr nFree = m_eQueue.m_nFree; // get volatile into temporary + + // test-probe#1 for faking queue overflow + QS_TEST_PROBE_DEF(&QActive::postLIFO) + QS_TEST_PROBE_ID(1, + nFree = 0U; + ) + + Q_REQUIRE_INCRIT(201, nFree != 0U); + + if (e->getPoolId_() != 0U) { // is it a mutable event? + QEvt_refCtr_inc_(e); // increment the reference counter + } + + --nFree; // one free entry just used up + m_eQueue.m_nFree = nFree; // update the original + if (m_eQueue.m_nMin > nFree) { + m_eQueue.m_nMin = nFree; // update minimum so far + } + + QS_BEGIN_PRE_(QS_QF_ACTIVE_POST_LIFO, m_prio) + QS_TIME_PRE_(); // timestamp + QS_SIG_PRE_(e->sig); // the signal of this event + QS_OBJ_PRE_(this); // this active object + QS_2U8_PRE_(e->getPoolId_(), e->refCtr_); // poolId & refCtr + QS_EQC_PRE_(nFree); // # free entries + QS_EQC_PRE_(m_eQueue.m_nMin); // min # free entries + QS_END_PRE_() + + #ifdef Q_UTEST + // callback to examine the posted event under the same conditions + // as producing the #QS_QF_ACTIVE_POST trace record, which are: + // the local filter for this AO ('m_prio') is set + if (QS_LOC_CHECK_(m_prio)) { + QS::onTestPost(nullptr, this, e, true); + } + #endif + + QEvt const * const frontEvt = m_eQueue.m_frontEvt; + m_eQueue.m_frontEvt = e; // deliver the event directly to the front + + if (frontEvt == nullptr) { // was the queue empty? + QACTIVE_EQUEUE_SIGNAL_(this); // signal the event queue + } + else { // queue was not empty, leave the event in the ring-buffer + m_eQueue.m_tail = (m_eQueue.m_tail + 1U); + if (m_eQueue.m_tail == m_eQueue.m_end) { // need to wrap the tail? + m_eQueue.m_tail = 0U; // wrap around + } + + m_eQueue.m_ring[m_eQueue.m_tail] = frontEvt; + } + + QF_MEM_APP(); + QF_CRIT_EXIT(); +} + +} // namespace QP +//$enddef${QF::QActive::postLIFO} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +//$define${QF::QActive::get_} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv +namespace QP { + +//${QF::QActive::get_} ....................................................... +QEvt const * QActive::get_() noexcept { + QF_CRIT_STAT + QF_CRIT_ENTRY(); + QF_MEM_SYS(); + + QACTIVE_EQUEUE_WAIT_(this); // wait for event to arrive directly + + // always remove evt from the front + QEvt const * const e = m_eQueue.m_frontEvt; + QEQueueCtr const nFree = m_eQueue.m_nFree + 1U; // get volatile into tmp + m_eQueue.m_nFree = nFree; // update the # free + + if (nFree <= m_eQueue.m_end) { // any events in the ring buffer? + // remove event from the tail + m_eQueue.m_frontEvt = m_eQueue.m_ring[m_eQueue.m_tail]; + if (m_eQueue.m_tail == 0U) { // need to wrap the tail? + m_eQueue.m_tail = m_eQueue.m_end; // wrap around + } + m_eQueue.m_tail = (m_eQueue.m_tail - 1U); + + QS_BEGIN_PRE_(QS_QF_ACTIVE_GET, m_prio) + QS_TIME_PRE_(); // timestamp + QS_SIG_PRE_(e->sig); // the signal of this event + QS_OBJ_PRE_(this); // this active object + QS_2U8_PRE_(e->getPoolId_(), e->refCtr_); // poolId & refCtr + QS_EQC_PRE_(nFree); // # free entries + QS_END_PRE_() + } + else { + m_eQueue.m_frontEvt = nullptr; // the queue becomes empty + + // all entries in the queue must be free (+1 for fronEvt) + Q_ASSERT_INCRIT(310, nFree == (m_eQueue.m_end + 1U)); + + QS_BEGIN_PRE_(QS_QF_ACTIVE_GET_LAST, m_prio) + QS_TIME_PRE_(); // timestamp + QS_SIG_PRE_(e->sig); // the signal of this event + QS_OBJ_PRE_(this); // this active object + QS_2U8_PRE_(e->getPoolId_(), e->refCtr_); // poolId & refCtr + QS_END_PRE_() + } + + QF_MEM_APP(); + QF_CRIT_EXIT(); + + return e; +} + +} // namespace QP +//$enddef${QF::QActive::get_} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +//$define${QF::QTicker} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv +namespace QP { + +//${QF::QTicker} ............................................................. + +//${QF::QTicker::QTicker} .................................................... +QTicker::QTicker(std::uint_fast8_t const tickRate) noexcept +: QActive(nullptr) +{ + // reuse m_head for tick-rate + m_eQueue.m_head = static_cast(tickRate); +} + +//${QF::QTicker::init} ....................................................... +void QTicker::init( + void const * const e, + std::uint_fast8_t const qs_id) +{ + Q_UNUSED_PAR(e); + Q_UNUSED_PAR(qs_id); + + QF_CRIT_STAT + QF_CRIT_ENTRY(); + QF_MEM_SYS(); + + m_eQueue.m_tail = 0U; + + QF_MEM_APP(); + QF_CRIT_EXIT(); +} + +//${QF::QTicker::dispatch} ................................................... +void QTicker::dispatch( + QEvt const * const e, + std::uint_fast8_t const qs_id) +{ + Q_UNUSED_PAR(e); + Q_UNUSED_PAR(qs_id); + + QF_CRIT_STAT + QF_CRIT_ENTRY(); + QF_MEM_SYS(); + + QEQueueCtr nTicks = m_eQueue.m_tail; // save # of ticks + m_eQueue.m_tail = 0U; // clear the # ticks + + QF_MEM_APP(); + QF_CRIT_EXIT(); + + for (; nTicks > 0U; --nTicks) { + QTimeEvt::tick(static_cast(m_eQueue.m_head), + this); + } +} + +//${QF::QTicker::trig_} ...................................................... +void QTicker::trig_(void const * const sender) noexcept { + #ifndef Q_SPY + Q_UNUSED_PAR(sender); + #endif + + QF_CRIT_STAT + QF_CRIT_ENTRY(); + QF_MEM_SYS(); + + if (m_eQueue.m_frontEvt == nullptr) { + + static QEvt const tickEvt(0U); // immutable event + + m_eQueue.m_frontEvt = &tickEvt; // deliver event directly + m_eQueue.m_nFree = (m_eQueue.m_nFree - 1U); // one less free event + + QACTIVE_EQUEUE_SIGNAL_(this); // signal the event queue + } + + // account for one more tick event + m_eQueue.m_tail = (m_eQueue.m_tail + 1U); + + QS_BEGIN_PRE_(QS_QF_ACTIVE_POST, m_prio) + QS_TIME_PRE_(); // timestamp + QS_OBJ_PRE_(sender); // the sender object + QS_SIG_PRE_(0U); // the signal of the event + QS_OBJ_PRE_(this); // this active object + QS_2U8_PRE_(0U, 0U); // poolId & refCtr + QS_EQC_PRE_(0U); // # free entries + QS_EQC_PRE_(0U); // min # free entries + QS_END_PRE_() + + QF_MEM_APP(); + QF_CRIT_EXIT(); +} + +} // namespace QP +//$enddef${QF::QTicker} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/src/qf_defer.cpp b/src/qf/qf_defer.cpp similarity index 63% rename from src/qf_defer.cpp rename to src/qf/qf_defer.cpp index 189731e..bc96f9e 100644 --- a/src/qf_defer.cpp +++ b/src/qf/qf_defer.cpp @@ -3,47 +3,41 @@ // Model: qpcpp.qm // File: ${src::qf::qf_defer.cpp} // -// This code has been generated by QM 5.2.5 . +// This code has been generated by QM 6.2.3 . // DO NOT EDIT THIS FILE MANUALLY. All your changes will be lost. // -// This code is covered by the following QP license: -// License # : LicenseRef-QL-dual -// Issued to : Any user of the QP/C++ real-time embedded framework -// Framework(s) : qpcpp -// Support ends : 2023-12-31 -// License scope: +// ____________________________________ +// / / +// / GGGGGGG PPPPPPPP LL / +// / GG GG PP PP LL / +// / GG PP PP LL / +// / GG GGGGG PPPPPPPP LL / +// / GG GG PP LL / +// / GGGGGGG PP LLLLLLL / +// /___________________________________/ // -// Copyright (C) 2005 Quantum Leaps, LLC . +// Copyright (c) 2005 Quantum Leaps, LLC +// SPDX-License-Identifier: GPL-3.0-or-later // -// SPDX-License-Identifier: GPL-3.0-or-later OR LicenseRef-QL-commercial +// This generated code is open-source software licensed under the GNU +// General Public License (GPL) as published by the Free Software Foundation +// (see ). // -// 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. +// NOTE: +// The GPL does NOT permit the incorporation of this code into proprietary +// programs. Please contact Quantum Leaps for commercial licensing options, +// which expressly supersede the GPL and are designed explicitly for licensees +// interested in retaining the proprietary status of the generated code. // -// The terms of the open source GNU General Public License version 3 -// can be found at: -// -// The terms of the closed source Quantum Leaps commercial licenses -// can be found at: -// -// Redistributions in source code must retain this top-level comment block. -// Plagiarizing this software to sidestep the license obligations is illegal. -// -// Contact information: +// Quantum Leaps contact information: // // // //$endhead${src::qf::qf_defer.cpp} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -//! @file -//! @brief QActive::defer(), QActive::recall(), and -//! QActive::flushDeferred() definitions. - #define QP_IMPL // this is QP implementation -#include "qf_port.hpp" // QF port -#include "qf_pkg.hpp" // QF package-scope interface -#include "qassert.h" // QP embedded systems-friendly assertions +#include "qp_port.hpp" // QP port +#include "qp_pkg.hpp" // QP package-scope interface +#include "qsafe.h" // QP Functional Safety (FuSa) Subsystem #ifdef Q_SPY // QS software tracing enabled? #include "qs_port.hpp" // QS port #include "qs_pkg.hpp" // QS facilities for pre-defined trace records @@ -58,11 +52,10 @@ Q_DEFINE_THIS_MODULE("qf_defer") //$skip${QP_VERSION} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv // Check for the minimum required QP version -#if (QP_VERSION < 700U) || (QP_VERSION != ((QP_RELEASE^4294967295U) % 0x3E8U)) -#error qpcpp version 7.0.0 or higher required +#if (QP_VERSION < 730U) || (QP_VERSION != ((QP_RELEASE^4294967295U)%0x2710U)) +#error qpcpp version 7.3.0 or higher required #endif //$endskip${QP_VERSION} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - //$define${QF::QActive::defer} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv namespace QP { @@ -72,63 +65,70 @@ bool QActive::defer( QEvt const * const e) const noexcept { bool const status = eq->post(e, 0U, m_prio); - QS_CRIT_STAT_ + QS_CRIT_STAT + QS_CRIT_ENTRY(); + QS_MEM_SYS(); QS_BEGIN_PRE_(QS_QF_ACTIVE_DEFER, m_prio) QS_TIME_PRE_(); // time stamp QS_OBJ_PRE_(this); // this active object QS_OBJ_PRE_(eq); // the deferred queue QS_SIG_PRE_(e->sig); // the signal of the event - QS_2U8_PRE_(e->poolId_, e->refCtr_); // pool Id & ref Count + QS_2U8_PRE_(e->getPoolId_(), e->refCtr_); // poolId & refCtr QS_END_PRE_() + QS_MEM_APP(); + QS_CRIT_EXIT(); return status; } } // namespace QP //$enddef${QF::QActive::defer} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + //$define${QF::QActive::recall} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv namespace QP { //${QF::QActive::recall} ..................................................... bool QActive::recall(QEQueue * const eq) noexcept { QEvt const * const e = eq->get(m_prio); // get evt from deferred queue - bool recalled; + QF_CRIT_STAT - // event available? - if (e != nullptr) { - QActive::postLIFO(e); // post it to the _front_ of the AO's queue + bool recalled; + if (e != nullptr) { // event available? + postLIFO(e); // post it to the _front_ of the AO's queue - QF_CRIT_STAT_ - QF_CRIT_E_(); + QF_CRIT_ENTRY(); + QF_MEM_SYS(); - // is it a dynamic event? - if (e->poolId_ != 0U) { + if (e->getPoolId_() != 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_CRIT_(210, e->refCtr_ >= 2U); + 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_NOCRIT_PRE_(QS_QF_ACTIVE_RECALL, m_prio) + QS_BEGIN_PRE_(QS_QF_ACTIVE_RECALL, m_prio) QS_TIME_PRE_(); // time stamp QS_OBJ_PRE_(this); // this active object QS_OBJ_PRE_(eq); // the deferred queue QS_SIG_PRE_(e->sig); // the signal of the event - QS_2U8_PRE_(e->poolId_, e->refCtr_); // pool Id & ref Count - QS_END_NOCRIT_PRE_() + QS_2U8_PRE_(e->getPoolId_(), e->refCtr_); // poolId & refCtr + QS_END_PRE_() + + QF_MEM_APP(); + QF_CRIT_EXIT(); - QF_CRIT_X_(); recalled = true; } else { - QS_CRIT_STAT_ + QS_CRIT_ENTRY(); + QS_MEM_SYS(); QS_BEGIN_PRE_(QS_QF_ACTIVE_RECALL_ATTEMPT, m_prio) QS_TIME_PRE_(); // time stamp @@ -136,14 +136,17 @@ bool QActive::recall(QEQueue * const eq) noexcept { QS_OBJ_PRE_(eq); // the deferred queue QS_END_PRE_() + QS_MEM_APP(); + QS_CRIT_EXIT(); + recalled = false; } return recalled; - } } // namespace QP //$enddef${QF::QActive::recall} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + //$define${QF::QActive::flushDeferred} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv namespace QP { diff --git a/src/qf/qf_dyn.cpp b/src/qf/qf_dyn.cpp new file mode 100644 index 0000000..f5ff176 --- /dev/null +++ b/src/qf/qf_dyn.cpp @@ -0,0 +1,333 @@ +//$file${src::qf::qf_dyn.cpp} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv +// +// Model: qpcpp.qm +// File: ${src::qf::qf_dyn.cpp} +// +// This code has been generated by QM 6.2.3 . +// DO NOT EDIT THIS FILE MANUALLY. All your changes will be lost. +// +// ____________________________________ +// / / +// / GGGGGGG PPPPPPPP LL / +// / GG GG PP PP LL / +// / GG PP PP LL / +// / GG GGGGG PPPPPPPP LL / +// / GG GG PP LL / +// / GGGGGGG PP LLLLLLL / +// /___________________________________/ +// +// Copyright (c) 2005 Quantum Leaps, LLC +// SPDX-License-Identifier: GPL-3.0-or-later +// +// This generated code is open-source software licensed under the GNU +// General Public License (GPL) as published by the Free Software Foundation +// (see ). +// +// NOTE: +// The GPL does NOT permit the incorporation of this code into proprietary +// programs. Please contact Quantum Leaps for commercial licensing options, +// which expressly supersede the GPL and are designed explicitly for licensees +// interested in retaining the proprietary status of the generated code. +// +// Quantum Leaps contact information: +// +// +// +//$endhead${src::qf::qf_dyn.cpp} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +#define QP_IMPL // this is QP implementation +#include "qp_port.hpp" // QP port +#include "qp_pkg.hpp" // QP package-scope interface +#include "qsafe.h" // QP Functional Safety (FuSa) Subsystem +#ifdef Q_SPY // QS software tracing enabled? + #include "qs_port.hpp" // QS port + #include "qs_pkg.hpp" // QS facilities for pre-defined trace records +#else + #include "qs_dummy.hpp" // disable the QS software tracing +#endif // Q_SPY + +#if (QF_MAX_EPOOL > 0U) // mutable events configured? + +// unnamed namespace for local definitions with internal linkage +namespace { +Q_DEFINE_THIS_MODULE("qf_dyn") +} // unnamed namespace + +//$skip${QP_VERSION} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv +// Check for the minimum required QP version +#if (QP_VERSION < 730U) || (QP_VERSION != ((QP_RELEASE^4294967295U)%0x2710U)) +#error qpcpp version 7.3.0 or higher required +#endif +//$endskip${QP_VERSION} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +//$define${QF::QF-dyn} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv +namespace QP { +namespace QF { + +//${QF::QF-dyn::poolInit} .................................................... +void poolInit( + void * const poolSto, + std::uint_fast32_t const poolSize, + std::uint_fast16_t const evtSize) noexcept +{ + std::uint_fast8_t const poolId = 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_(priv_.ePool_[poolId - 1U]) < evtSize); + } + 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_(priv_.ePool_[poolId], poolSto, poolSize, evtSize); + + #ifdef Q_SPY + // generate the object-dictionary entry for the initialized pool + { + std::uint8_t obj_name[9] = "EvtPool?"; + obj_name[7] = static_cast( + static_cast('0') + + static_cast(poolId + 1U)); + QS_CRIT_ENTRY(); + QS_MEM_SYS(); + QS::obj_dict_pre_(&priv_.ePool_[poolId], + reinterpret_cast(&obj_name[0])); + QS_MEM_APP(); + QS_CRIT_EXIT(); + } + #endif // Q_SPY +} + +//${QF::QF-dyn::poolGetMaxBlockSize} ......................................... +std::uint_fast16_t poolGetMaxBlockSize() noexcept { + return QF_EPOOL_EVENT_SIZE_(priv_.ePool_[priv_.maxPool_ - 1U]); +} + +//${QF::QF-dyn::getPoolMin} .................................................. +std::uint_fast16_t getPoolMin(std::uint_fast8_t const poolId) noexcept { + QF_CRIT_STAT + QF_CRIT_ENTRY(); + QF_MEM_SYS(); + + Q_REQUIRE_INCRIT(400, (poolId <= QF_MAX_EPOOL) + && (0U < poolId) && (poolId <= priv_.maxPool_)); + + std::uint_fast16_t const min = static_cast( + priv_.ePool_[poolId - 1U].getNMin()); + + QF_MEM_APP(); + QF_CRIT_EXIT(); + + return min; +} + +//${QF::QF-dyn::newX_} ....................................................... +QEvt * newX_( + std::uint_fast16_t const evtSize, + std::uint_fast16_t const margin, + enum_t const sig) noexcept +{ + QF_CRIT_STAT + QF_CRIT_ENTRY(); + QF_MEM_SYS(); + + // find the pool id that fits the requested event size... + std::uint_fast8_t poolId = 0U; // zero-based poolId initially + for (; poolId < priv_.maxPool_; ++poolId) { + if (evtSize <= QF_EPOOL_EVENT_SIZE_(priv_.ePool_[poolId])) { + break; + } + } + + // precondition: + // - cannot run out of registered pools + Q_REQUIRE_INCRIT(300, poolId < 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_(priv_.ePool_[poolId - 1U], e, + ((margin != NO_MARGIN) ? margin : 0U), + static_cast(QS_EP_ID) + poolId); + #else + QF_EPOOL_GET_(priv_.ePool_[poolId - 1U], e, + ((margin != NO_MARGIN) ? margin : 0U), 0U); + #endif + + if (e != nullptr) { // was e allocated correctly? + e->sig = static_cast(sig); // set the signal + e->refCtr_ = 0U; // initialize the reference counter to 0 + e->evtTag_ = static_cast(QEvt::MARKER | poolId); + + QS_CRIT_ENTRY(); + QS_MEM_SYS(); + QS_BEGIN_PRE_(QS_QF_NEW, + static_cast(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 != NO_MARGIN); + + QS_MEM_SYS(); + QS_BEGIN_PRE_(QS_QF_NEW_ATTEMPT, + static_cast(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; +} + +//${QF::QF-dyn::gc} .......................................................... +void gc(QEvt const * const e) noexcept { + QF_CRIT_STAT + QF_CRIT_ENTRY(); + Q_REQUIRE_INCRIT(402, QEvt::verify_(e)); + + std::uint_fast8_t const poolId = e->getPoolId_(); + + 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, + static_cast(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, + static_cast(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 <= 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_(priv_.ePool_[poolId - 1U], + QF_CONST_CAST_(QEvt*, e), + static_cast(QS_EP_ID) + poolId); + #else + QF_EPOOL_PUT_(priv_.ePool_[poolId - 1U], + QF_CONST_CAST_(QEvt*, e), 0U); + #endif + } + } + else { + QF_CRIT_EXIT(); + } +} + +//${QF::QF-dyn::newRef_} ..................................................... +QEvt const * newRef_( + QEvt const * const e, + QEvt const * const evtRef) noexcept +{ + #ifdef Q_UNSAFE + Q_UNUSED_PAR(evtRef); + #endif + + QF_CRIT_STAT + QF_CRIT_ENTRY(); + + Q_REQUIRE_INCRIT(502, QEvt::verify_(e)); + + std::uint_fast8_t const poolId = e->getPoolId_(); + + Q_REQUIRE_INCRIT(500, (poolId != 0U) + && (evtRef == nullptr)); + + QEvt_refCtr_inc_(e); // increments the ref counter + + QS_MEM_SYS(); + QS_BEGIN_PRE_(QS_QF_NEW_REF, + static_cast(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; +} + +//${QF::QF-dyn::deleteRef_} .................................................. +void deleteRef_(QEvt const * const evtRef) noexcept { + QEvt const * const e = evtRef; + + QF_CRIT_STAT + QF_CRIT_ENTRY(); + Q_REQUIRE_INCRIT(602, QEvt::verify_(e)); + + #ifdef Q_SPY + std::uint_fast8_t const poolId = e->getPoolId_(); + #endif + + QS_MEM_SYS(); + QS_BEGIN_PRE_(QS_QF_DELETE_REF, + static_cast(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) + gc(evtRef); // recycle the referenced event + #endif +} + +} // namespace QF +} // namespace QP +//$enddef${QF::QF-dyn} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +#endif // (QF_MAX_EPOOL > 0U) mutable events configured diff --git a/src/qf/qf_mem.cpp b/src/qf/qf_mem.cpp new file mode 100644 index 0000000..0ce463a --- /dev/null +++ b/src/qf/qf_mem.cpp @@ -0,0 +1,246 @@ +//$file${src::qf::qf_mem.cpp} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv +// +// Model: qpcpp.qm +// File: ${src::qf::qf_mem.cpp} +// +// This code has been generated by QM 6.2.3 . +// DO NOT EDIT THIS FILE MANUALLY. All your changes will be lost. +// +// ____________________________________ +// / / +// / GGGGGGG PPPPPPPP LL / +// / GG GG PP PP LL / +// / GG PP PP LL / +// / GG GGGGG PPPPPPPP LL / +// / GG GG PP LL / +// / GGGGGGG PP LLLLLLL / +// /___________________________________/ +// +// Copyright (c) 2005 Quantum Leaps, LLC +// SPDX-License-Identifier: GPL-3.0-or-later +// +// This generated code is open-source software licensed under the GNU +// General Public License (GPL) as published by the Free Software Foundation +// (see ). +// +// NOTE: +// The GPL does NOT permit the incorporation of this code into proprietary +// programs. Please contact Quantum Leaps for commercial licensing options, +// which expressly supersede the GPL and are designed explicitly for licensees +// interested in retaining the proprietary status of the generated code. +// +// Quantum Leaps contact information: +// +// +// +//$endhead${src::qf::qf_mem.cpp} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +#define QP_IMPL // this is QP implementation +#include "qp_port.hpp" // QP port +#include "qp_pkg.hpp" // QP package-scope interface +#include "qsafe.h" // QP Functional Safety (FuSa) Subsystem +#ifdef Q_SPY // QS software tracing enabled? + #include "qs_port.hpp" // QS port + #include "qs_pkg.hpp" // QS facilities for pre-defined trace records +#else + #include "qs_dummy.hpp" // disable the QS software tracing +#endif // Q_SPY + +// unnamed namespace for local definitions with internal linkage +namespace { +Q_DEFINE_THIS_MODULE("qf_mem") +} // unnamed namespace + +//$skip${QP_VERSION} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv +// Check for the minimum required QP version +#if (QP_VERSION < 730U) || (QP_VERSION != ((QP_RELEASE^4294967295U)%0x2710U)) +#error qpcpp version 7.3.0 or higher required +#endif +//$endskip${QP_VERSION} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +//$define${QF::QMPool} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv +namespace QP { + +//${QF::QMPool} .............................................................. + +//${QF::QMPool::init} ........................................................ +void QMPool::init( + void * const poolSto, + std::uint_fast32_t const poolSize, + std::uint_fast16_t const blockSize) noexcept +{ + QF_CRIT_STAT + QF_CRIT_ENTRY(); + QF_MEM_SYS(); + + Q_REQUIRE_INCRIT(100, (poolSto != nullptr) + && (poolSize >= static_cast(sizeof(QFreeBlock))) + && (static_cast(blockSize + sizeof(QFreeBlock)) + > blockSize)); + + m_free_head = static_cast(poolSto); + + // find # free blocks in a memory block, NO DIVISION + m_blockSize = static_cast(sizeof(QFreeBlock)); + std::uint_fast16_t nblocks = 1U; + while (m_blockSize < static_cast(blockSize)) { + m_blockSize += static_cast(sizeof(QFreeBlock)); + ++nblocks; + } + + // the pool buffer must fit at least one rounded-up block + Q_ASSERT_INCRIT(110, poolSize >= m_blockSize); + + // start at the head of the free list + QFreeBlock *fb = m_free_head; + m_nTot = 1U; // the last block already in the list + + // chain all blocks together in a free-list... + for (std::uint_fast32_t size = poolSize - m_blockSize; + size >= static_cast(m_blockSize); + size -= static_cast(m_blockSize)) + { + fb->m_next = &fb[nblocks]; // point next link to next block + #ifndef Q_UNSAFE + fb->m_next_dis = ~Q_UINTPTR_CAST_(fb->m_next); + #endif + fb = fb->m_next; // advance to the next block + ++m_nTot; // one more free block in the pool + } + + fb->m_next = nullptr; // the last link points to NULL + #ifndef Q_UNSAFE + fb->m_next_dis = ~Q_UINTPTR_CAST_(fb->m_next); + #endif + + fb->m_next = nullptr; // the last link points to NULL + m_nFree = m_nTot; // all blocks are free + m_nMin = m_nTot; // the minimum # free blocks + m_start = static_cast(poolSto); // original start + m_end = fb; // the last block in this pool + + QF_MEM_APP(); + QF_CRIT_EXIT(); +} + +//${QF::QMPool::get} ......................................................... +void * QMPool::get( + std::uint_fast16_t const margin, + std::uint_fast8_t const qs_id) noexcept +{ + #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 (m_nFree > static_cast(margin)) { + fb = m_free_head; // get a free block + + // a free block must be valid + Q_ASSERT_INCRIT(300, fb != nullptr); + + QFreeBlock * const fb_next = fb->m_next; + + // the free block must have integrity (duplicate inverse storage) + Q_ASSERT_INCRIT(302, Q_UINTPTR_CAST_(fb_next) + == static_cast(~fb->m_next_dis)); + + m_nFree = (m_nFree - 1U); // one free block less + if (m_nFree == 0U) { // is the pool becoming empty? + // pool is becoming empty, so the next free block must be NULL + Q_ASSERT_INCRIT(320, fb_next == nullptr); + + m_nMin = 0U; // remember that the pool got empty + } + else { + // invariant: + // The pool is not empty, so the next free-block pointer, + // so the next free block must be in range. + + // NOTE: The next free block pointer can fall out of range + // when the client code writes past the memory block, thus + // corrupting the next block. + Q_ASSERT_INCRIT(330, + QF_PTR_RANGE_(fb_next, m_start, m_end)); + + // is the # free blocks the new minimum so far? + if (m_nMin > m_nFree) { + m_nMin = m_nFree; // remember the minimum so far + } + } + + m_free_head = fb_next; // set the head to the next free block + + QS_BEGIN_PRE_(QS_QF_MPOOL_GET, qs_id) + QS_TIME_PRE_(); // timestamp + QS_OBJ_PRE_(this); // this memory pool + QS_MPC_PRE_(m_nFree); // # of free blocks in the pool + QS_MPC_PRE_(m_nMin); // min # free blocks ever in the pool + QS_END_PRE_() + } + else { // don't have enough free blocks at this point + fb = nullptr; + + QS_BEGIN_PRE_(QS_QF_MPOOL_GET_ATTEMPT, qs_id) + QS_TIME_PRE_(); // timestamp + QS_OBJ_PRE_(this); // this memory pool + QS_MPC_PRE_(m_nFree); // # of free blocks in the pool + QS_MPC_PRE_(margin); // the requested margin + QS_END_PRE_() + } + + QF_MEM_APP(); + QF_CRIT_EXIT(); + + return fb; // return the block or nullptr to the caller +} + +//${QF::QMPool::put} ......................................................... +void QMPool::put( + void * const block, + std::uint_fast8_t const qs_id) noexcept +{ + #ifndef Q_SPY + Q_UNUSED_PAR(qs_id); + #endif + + QFreeBlock * const fb = static_cast(block); + + QF_CRIT_STAT + QF_CRIT_ENTRY(); + QF_MEM_SYS(); + + Q_REQUIRE_INCRIT(200, (m_nFree < m_nTot) + && QF_PTR_RANGE_(fb, m_start, m_end)); + + fb->m_next = m_free_head; // link into list + #ifndef Q_UNSAFE + fb->m_next_dis = static_cast( + ~Q_UINTPTR_CAST_(fb->m_next)); + #endif + + // set as new head of the free list + m_free_head = static_cast(block); + + m_nFree = m_nFree + 1U; // one more free block in this pool + + QS_BEGIN_PRE_(QS_QF_MPOOL_PUT, qs_id) + QS_TIME_PRE_(); // timestamp + QS_OBJ_PRE_(this); // this memory pool + QS_MPC_PRE_(m_nFree); // the # free blocks in the pool + QS_END_PRE_() + + QF_MEM_APP(); + QF_CRIT_EXIT(); +} + +//${QF::QMPool::getBlockSize} ................................................ +QMPoolSize QMPool::getBlockSize() const noexcept { + return m_blockSize; +} + +} // namespace QP +//$enddef${QF::QMPool} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/src/qf/qf_ps.cpp b/src/qf/qf_ps.cpp new file mode 100644 index 0000000..637a46f --- /dev/null +++ b/src/qf/qf_ps.cpp @@ -0,0 +1,312 @@ +//$file${src::qf::qf_ps.cpp} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv +// +// Model: qpcpp.qm +// File: ${src::qf::qf_ps.cpp} +// +// This code has been generated by QM 6.2.3 . +// DO NOT EDIT THIS FILE MANUALLY. All your changes will be lost. +// +// ____________________________________ +// / / +// / GGGGGGG PPPPPPPP LL / +// / GG GG PP PP LL / +// / GG PP PP LL / +// / GG GGGGG PPPPPPPP LL / +// / GG GG PP LL / +// / GGGGGGG PP LLLLLLL / +// /___________________________________/ +// +// Copyright (c) 2005 Quantum Leaps, LLC +// SPDX-License-Identifier: GPL-3.0-or-later +// +// This generated code is open-source software licensed under the GNU +// General Public License (GPL) as published by the Free Software Foundation +// (see ). +// +// NOTE: +// The GPL does NOT permit the incorporation of this code into proprietary +// programs. Please contact Quantum Leaps for commercial licensing options, +// which expressly supersede the GPL and are designed explicitly for licensees +// interested in retaining the proprietary status of the generated code. +// +// Quantum Leaps contact information: +// +// +// +//$endhead${src::qf::qf_ps.cpp} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +#define QP_IMPL // this is QP implementation +#include "qp_port.hpp" // QP port +#include "qp_pkg.hpp" // QP package-scope interface +#include "qsafe.h" // QP Functional Safety (FuSa) Subsystem +#ifdef Q_SPY // QS software tracing enabled? + #include "qs_port.hpp" // QS port + #include "qs_pkg.hpp" // QS facilities for pre-defined trace records +#else + #include "qs_dummy.hpp" // disable the QS software tracing +#endif // Q_SPY + +// unnamed namespace for local definitions with internal linkage +namespace { +Q_DEFINE_THIS_MODULE("qf_ps") +} // unnamed namespace + +//$skip${QP_VERSION} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv +// Check for the minimum required QP version +#if (QP_VERSION < 730U) || (QP_VERSION != ((QP_RELEASE^4294967295U)%0x2710U)) +#error qpcpp version 7.3.0 or higher required +#endif +//$endskip${QP_VERSION} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +//$define${QF::QActive::subscrList_} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv +namespace QP { +QSubscrList * QActive::subscrList_; + +} // namespace QP +//$enddef${QF::QActive::subscrList_} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +//$define${QF::QActive::maxPubSignal_} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv +namespace QP { +enum_t QActive::maxPubSignal_; + +} // namespace QP +//$enddef${QF::QActive::maxPubSignal_} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +//$define${QF::QActive::psInit} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv +namespace QP { + +//${QF::QActive::psInit} ..................................................... +void QActive::psInit( + QSubscrList * const subscrSto, + enum_t const maxSignal) noexcept +{ + subscrList_ = subscrSto; + maxPubSignal_ = maxSignal; + + // initialize the subscriber list + for (enum_t sig = 0; sig < maxSignal; ++sig) { + subscrSto[sig].m_set.setEmpty(); + #ifndef Q_UNSAFE + subscrSto[sig].m_set.update_(&subscrSto[sig].m_set_dis); + #endif + } +} + +} // namespace QP +//$enddef${QF::QActive::psInit} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +//$define${QF::QActive::publish_} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv +namespace QP { + +//${QF::QActive::publish_} ................................................... +void QActive::publish_( + QEvt const * const e, + void const * const sender, + std::uint_fast8_t const qs_id) noexcept +{ + #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 < static_cast(maxPubSignal_)); + Q_REQUIRE_INCRIT(202, + subscrList_[sig].m_set.verify_(&subscrList_[sig].m_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_(e->sig); // the signal of the event + QS_2U8_PRE_(e->getPoolId_(), e->refCtr_); // poolId & refCtr + QS_END_PRE_() + + // is it a mutable event? + if (e->getPoolId_() != 0U) { + // NOTE: The reference counter of a mutable event is incremented to + // prevent premature recycling of the event while the multicasting + // is still in progress. At the end of the function, the garbage + // collector step (QF::gc()) decrements the reference counter and + // recycles the event if the counter drops to zero. This covers the + // case when the event was published without any subscribers. + QEvt_refCtr_inc_(e); + } + + // make a local, modifiable copy of the subscriber set + QPSet subscrSet = subscrList_[sig].m_set; + + QF_MEM_APP(); + QF_CRIT_EXIT(); + + if (subscrSet.notEmpty()) { // any subscribers? + // highest-prio subscriber + std::uint_fast8_t p = subscrSet.findMax(); + + QF_CRIT_ENTRY(); + QF_MEM_SYS(); + + QActive *a = registry_[p]; + // the AO must be registered with the framework + Q_ASSERT_INCRIT(210, a != nullptr); + + QF_MEM_APP(); + QF_CRIT_EXIT(); + + QF_SCHED_STAT_ + QF_SCHED_LOCK_(p); // lock the scheduler up to AO's prio + do { // loop over all subscribers + + // POST() asserts internally if the queue overflows + a->POST(e, sender); + + subscrSet.remove(p); // remove the handled subscriber + if (subscrSet.notEmpty()) { // still more subscribers? + p = subscrSet.findMax(); // highest-prio subscriber + + QF_CRIT_ENTRY(); + QF_MEM_SYS(); + + a = registry_[p]; + // the AO must be registered with the framework + Q_ASSERT_INCRIT(220, a != nullptr); + + QF_MEM_APP(); + QF_CRIT_EXIT(); + } + else { + p = 0U; // no more subscribers + } + } while (p != 0U); + QF_SCHED_UNLOCK_(); // unlock the scheduler + } + + // The following garbage collection step decrements the reference counter + // and recycles the event if the counter drops to zero. This covers both + // cases when the event was published with or without any subscribers. + #if (QF_MAX_EPOOL > 0U) + QF::gc(e); + #endif +} + +} // namespace QP +//$enddef${QF::QActive::publish_} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +//$define${QF::QActive::subscribe} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv +namespace QP { + +//${QF::QActive::subscribe} .................................................. +void QActive::subscribe(enum_t const sig) const noexcept { + std::uint_fast8_t const p = static_cast(m_prio); + + QF_CRIT_STAT + QF_CRIT_ENTRY(); + QF_MEM_SYS(); + + Q_REQUIRE_INCRIT(300, (Q_USER_SIG <= sig) + && (sig < maxPubSignal_) + && (0U < p) && (p <= QF_MAX_ACTIVE) + && (registry_[p] == this)); + Q_REQUIRE_INCRIT(302, + subscrList_[sig].m_set.verify_(&subscrList_[sig].m_set_dis)); + + QS_BEGIN_PRE_(QS_QF_ACTIVE_SUBSCRIBE, m_prio) + QS_TIME_PRE_(); // timestamp + QS_SIG_PRE_(sig); // the signal of this event + QS_OBJ_PRE_(this); // this active object + QS_END_PRE_() + + // insert the prio. into the subscriber set + subscrList_[sig].m_set.insert(p); + #ifndef Q_UNSAFE + subscrList_[sig].m_set.update_(&subscrList_[sig].m_set_dis); + #endif + + QF_MEM_APP(); + QF_CRIT_EXIT(); +} + +} // namespace QP +//$enddef${QF::QActive::subscribe} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +//$define${QF::QActive::unsubscribe} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv +namespace QP { + +//${QF::QActive::unsubscribe} ................................................ +void QActive::unsubscribe(enum_t const sig) const noexcept { + std::uint_fast8_t const p = static_cast(m_prio); + + QF_CRIT_STAT + QF_CRIT_ENTRY(); + QF_MEM_SYS(); + + Q_REQUIRE_INCRIT(400, (Q_USER_SIG <= sig) + && (sig < maxPubSignal_) + && (0U < p) && (p <= QF_MAX_ACTIVE) + && (registry_[p] == this)); + Q_REQUIRE_INCRIT(402, + subscrList_[sig].m_set.verify_(&subscrList_[sig].m_set_dis)); + + QS_BEGIN_PRE_(QS_QF_ACTIVE_UNSUBSCRIBE, m_prio) + QS_TIME_PRE_(); // timestamp + QS_SIG_PRE_(sig); // the signal of this event + QS_OBJ_PRE_(this); // this active object + QS_END_PRE_() + + // remove the prio. from the subscriber set + subscrList_[sig].m_set.remove(p); + #ifndef Q_UNSAFE + subscrList_[sig].m_set.update_(&subscrList_[sig].m_set_dis); + #endif + + QF_MEM_APP(); + QF_CRIT_EXIT(); +} + +} // namespace QP +//$enddef${QF::QActive::unsubscribe} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +//$define${QF::QActive::unsubscribeAll} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv +namespace QP { + +//${QF::QActive::unsubscribeAll} ............................................. +void QActive::unsubscribeAll() const noexcept { + QF_CRIT_STAT + QF_CRIT_ENTRY(); + QF_MEM_SYS(); + + std::uint_fast8_t const p = static_cast(m_prio); + + Q_REQUIRE_INCRIT(500, (0U < p) && (p <= QF_MAX_ACTIVE) + && (registry_[p] == this)); + enum_t const maxPubSig = maxPubSignal_; + + QF_MEM_APP(); + QF_CRIT_EXIT(); + + for (enum_t sig = Q_USER_SIG; sig < maxPubSig; ++sig) { + QF_CRIT_ENTRY(); + QF_MEM_SYS(); + + if (subscrList_[sig].m_set.hasElement(p)) { + subscrList_[sig].m_set.remove(p); + #ifndef Q_UNSAFE + subscrList_[sig].m_set.update_(&subscrList_[sig].m_set_dis); + #endif + QS_BEGIN_PRE_(QS_QF_ACTIVE_UNSUBSCRIBE, m_prio) + QS_TIME_PRE_(); // timestamp + QS_SIG_PRE_(sig); // the signal of this event + QS_OBJ_PRE_(this); // this active object + QS_END_PRE_() + } + QF_MEM_APP(); + QF_CRIT_EXIT(); + + QF_CRIT_EXIT_NOP(); // prevent merging critical sections + } +} + +} // namespace QP +//$enddef${QF::QActive::unsubscribeAll} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/src/qf/qf_qact.cpp b/src/qf/qf_qact.cpp new file mode 100644 index 0000000..5e530f5 --- /dev/null +++ b/src/qf/qf_qact.cpp @@ -0,0 +1,148 @@ +//$file${src::qf::qf_qact.cpp} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv +// +// Model: qpcpp.qm +// File: ${src::qf::qf_qact.cpp} +// +// This code has been generated by QM 6.2.3 . +// DO NOT EDIT THIS FILE MANUALLY. All your changes will be lost. +// +// ____________________________________ +// / / +// / GGGGGGG PPPPPPPP LL / +// / GG GG PP PP LL / +// / GG PP PP LL / +// / GG GGGGG PPPPPPPP LL / +// / GG GG PP LL / +// / GGGGGGG PP LLLLLLL / +// /___________________________________/ +// +// Copyright (c) 2005 Quantum Leaps, LLC +// SPDX-License-Identifier: GPL-3.0-or-later +// +// This generated code is open-source software licensed under the GNU +// General Public License (GPL) as published by the Free Software Foundation +// (see ). +// +// NOTE: +// The GPL does NOT permit the incorporation of this code into proprietary +// programs. Please contact Quantum Leaps for commercial licensing options, +// which expressly supersede the GPL and are designed explicitly for licensees +// interested in retaining the proprietary status of the generated code. +// +// Quantum Leaps contact information: +// +// +// +//$endhead${src::qf::qf_qact.cpp} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +#define QP_IMPL // this is QP implementation +#include "qp_port.hpp" // QP port +#include "qp_pkg.hpp" // QP package-scope interface +#include "qsafe.h" // QP Functional Safety (FuSa) Subsystem +#ifdef Q_SPY // QS software tracing enabled? + #include "qs_port.hpp" // QS port + #include "qs_pkg.hpp" // QS facilities for pre-defined trace records +#else + #include "qs_dummy.hpp" // disable the QS software tracing +#endif // Q_SPY + +// unnamed namespace for local definitions with internal linkage +namespace { +Q_DEFINE_THIS_MODULE("qf_qact") +} // unnamed namespace + +//$skip${QP_VERSION} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv +// Check for the minimum required QP version +#if (QP_VERSION < 730U) || (QP_VERSION != ((QP_RELEASE^4294967295U)%0x2710U)) +#error qpcpp version 7.3.0 or higher required +#endif +//$endskip${QP_VERSION} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +//$define${QF::QActive::QActive} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv +namespace QP { + +//${QF::QActive::QActive} .................................................... +QActive::QActive(QStateHandler const initial) noexcept + : QAsm(), + m_prio(0U), + m_pthre(0U) +{ + m_state.fun = Q_STATE_CAST(&top); + m_temp.fun = initial; +} + +} // namespace QP +//$enddef${QF::QActive::QActive} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +//$define${QF::QActive::register_} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv +namespace QP { + +//${QF::QActive::register_} .................................................. +void QActive::register_() noexcept { + QF_CRIT_STAT + QF_CRIT_ENTRY(); + QF_MEM_SYS(); + + if (m_pthre == 0U) { // preemption-threshold not defined? + m_pthre = m_prio; // apply the default + } + + #ifndef Q_UNSAFE + + Q_REQUIRE_INCRIT(100, (0U < m_prio) && (m_prio <= QF_MAX_ACTIVE) + && (registry_[m_prio] == nullptr) + && (m_prio <= m_pthre)); + + std::uint8_t prev_thre = m_pthre; + std::uint8_t next_thre = m_pthre; + + std::uint_fast8_t p; + for (p = static_cast(m_prio) - 1U; p > 0U; --p) { + if (registry_[p] != nullptr) { + prev_thre = registry_[p]->m_pthre; + break; + } + } + for (p = static_cast(m_prio) + 1U; + p <= QF_MAX_ACTIVE; ++p) + { + if (registry_[p] != nullptr) { + next_thre = registry_[p]->m_pthre; + break; + } + } + + Q_ASSERT_INCRIT(190, (prev_thre <= m_pthre) + && (m_pthre <= next_thre)); + #endif // Q_UNSAFE + + // register the AO at the QF-prio. + registry_[m_prio] = this; + + QF_MEM_APP(); + QF_CRIT_EXIT(); +} + +} // namespace QP +//$enddef${QF::QActive::register_} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +//$define${QF::QActive::unregister_} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv +namespace QP { + +//${QF::QActive::unregister_} ................................................ +void QActive::unregister_() noexcept { + std::uint_fast8_t const p = static_cast(m_prio); + + QF_CRIT_STAT + QF_CRIT_ENTRY(); + QF_MEM_SYS(); + + Q_REQUIRE_INCRIT(200, (0U < p) && (p <= QF_MAX_ACTIVE) + && (registry_[p] == this)); + registry_[p] = nullptr; // free-up the priority level + m_state.fun = nullptr; // invalidate the state + + QF_MEM_APP(); + QF_CRIT_EXIT(); +} + +} // namespace QP +//$enddef${QF::QActive::unregister_} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/src/qf/qf_qeq.cpp b/src/qf/qf_qeq.cpp new file mode 100644 index 0000000..c872755 --- /dev/null +++ b/src/qf/qf_qeq.cpp @@ -0,0 +1,265 @@ +//$file${src::qf::qf_qeq.cpp} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv +// +// Model: qpcpp.qm +// File: ${src::qf::qf_qeq.cpp} +// +// This code has been generated by QM 6.2.3 . +// DO NOT EDIT THIS FILE MANUALLY. All your changes will be lost. +// +// ____________________________________ +// / / +// / GGGGGGG PPPPPPPP LL / +// / GG GG PP PP LL / +// / GG PP PP LL / +// / GG GGGGG PPPPPPPP LL / +// / GG GG PP LL / +// / GGGGGGG PP LLLLLLL / +// /___________________________________/ +// +// Copyright (c) 2005 Quantum Leaps, LLC +// SPDX-License-Identifier: GPL-3.0-or-later +// +// This generated code is open-source software licensed under the GNU +// General Public License (GPL) as published by the Free Software Foundation +// (see ). +// +// NOTE: +// The GPL does NOT permit the incorporation of this code into proprietary +// programs. Please contact Quantum Leaps for commercial licensing options, +// which expressly supersede the GPL and are designed explicitly for licensees +// interested in retaining the proprietary status of the generated code. +// +// Quantum Leaps contact information: +// +// +// +//$endhead${src::qf::qf_qeq.cpp} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +#define QP_IMPL // this is QP implementation +#include "qp_port.hpp" // QP port +#include "qp_pkg.hpp" // QP package-scope interface +#include "qsafe.h" // QP Functional Safety (FuSa) Subsystem +#ifdef Q_SPY // QS software tracing enabled? + #include "qs_port.hpp" // QS port + #include "qs_pkg.hpp" // QS facilities for pre-defined trace records +#else + #include "qs_dummy.hpp" // disable the QS software tracing +#endif // Q_SPY + +// unnamed namespace for local definitions with internal linkage +namespace { +Q_DEFINE_THIS_MODULE("qf_qeq") +} // unnamed namespace + +//$skip${QP_VERSION} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv +// Check for the minimum required QP version +#if (QP_VERSION < 730U) || (QP_VERSION != ((QP_RELEASE^4294967295U)%0x2710U)) +#error qpcpp version 7.3.0 or higher required +#endif +//$endskip${QP_VERSION} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +//$define${QF::QEQueue} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv +namespace QP { + +//${QF::QEQueue} ............................................................. + +//${QF::QEQueue::init} ....................................................... +void QEQueue::init( + QEvt const * qSto[], + std::uint_fast16_t const qLen) noexcept +{ + m_frontEvt = nullptr; // no events in the queue + m_ring = &qSto[0]; + m_end = static_cast(qLen); + if (qLen > 0U) { + m_head = 0U; + m_tail = 0U; + } + m_nFree = static_cast(qLen + 1U); //+1 for frontEvt + m_nMin = m_nFree; +} + +//${QF::QEQueue::post} ....................................................... +bool QEQueue::post( + QEvt const * const e, + std::uint_fast16_t const margin, + std::uint_fast8_t const qs_id) noexcept +{ + #ifndef Q_SPY + Q_UNUSED_PAR(qs_id); + #endif + + QF_CRIT_STAT + QF_CRIT_ENTRY(); + QF_MEM_SYS(); + + Q_REQUIRE_INCRIT(200, e != nullptr); + + QEQueueCtr nFree = m_nFree; // get volatile into temporary + + // required margin available? + bool status; + if (((margin == QF::NO_MARGIN) && (nFree > 0U)) + || (nFree > static_cast(margin))) + { + // is it a mutable event? + if (e->getPoolId_() != 0U) { + QEvt_refCtr_inc_(e); // increment the reference counter + } + + --nFree; // one free entry just used up + m_nFree = nFree; // update the original + if (m_nMin > nFree) { + m_nMin = nFree; // update minimum so far + } + + QS_BEGIN_PRE_(QS_QF_EQUEUE_POST, qs_id) + QS_TIME_PRE_(); // timestamp + QS_SIG_PRE_(e->sig); // the signal of this event + QS_OBJ_PRE_(this); // this queue object + QS_2U8_PRE_(e->getPoolId_(), e->refCtr_); // poolId & refCtr + QS_EQC_PRE_(nFree); // # free entries + QS_EQC_PRE_(m_nMin); // min # free entries + QS_END_PRE_() + + if (m_frontEvt == nullptr) { // was the queue empty? + m_frontEvt = e; // deliver event directly + } + else { // queue was not empty, insert event into the ring-buffer + // insert event into the ring buffer (FIFO) + m_ring[m_head] = e; // insert e into buffer + + // need to wrap? + if (m_head == 0U) { + m_head = m_end; // wrap around + } + m_head = (m_head - 1U); + } + status = true; // event posted successfully + } + else { // event cannot be posted + // dropping events must be acceptable + Q_ASSERT_INCRIT(210, margin != QF::NO_MARGIN); + + QS_BEGIN_PRE_(QS_QF_EQUEUE_POST_ATTEMPT, qs_id) + QS_TIME_PRE_(); // timestamp + QS_SIG_PRE_(e->sig); // the signal of this event + QS_OBJ_PRE_(this); // this queue object + QS_2U8_PRE_(e->getPoolId_(), 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; +} + +//${QF::QEQueue::postLIFO} ................................................... +void QEQueue::postLIFO( + QEvt const * const e, + std::uint_fast8_t const qs_id) noexcept +{ + #ifndef Q_SPY + Q_UNUSED_PAR(qs_id); + #endif + + QF_CRIT_STAT + QF_CRIT_ENTRY(); + QF_MEM_SYS(); + + QEQueueCtr nFree = m_nFree; // get volatile into temporary + + Q_REQUIRE_INCRIT(300, nFree != 0U); + + if (e->getPoolId_() != 0U) { // is it a mutable event? + QEvt_refCtr_inc_(e); // increment the reference counter + } + + --nFree; // one free entry just used up + m_nFree = nFree; // update the original + if (m_nMin > nFree) { + m_nMin = nFree; // update minimum so far + } + + QS_BEGIN_PRE_(QS_QF_EQUEUE_POST_LIFO, qs_id) + QS_TIME_PRE_(); // timestamp + QS_SIG_PRE_(e->sig); // the signal of this event + QS_OBJ_PRE_(this); // this queue object + QS_2U8_PRE_(e->getPoolId_(), e->refCtr_); // poolId & refCtr + QS_EQC_PRE_(nFree); // # free entries + QS_EQC_PRE_(m_nMin); // min # free entries + QS_END_PRE_() + + QEvt const * const frontEvt = m_frontEvt; // read into temporary + m_frontEvt = e; // deliver event directly to the front of the queue + + if (frontEvt != nullptr) { // was the queue not empty? + m_tail = (m_tail + 1U); + if (m_tail == m_end) { // need to wrap the tail? + m_tail = 0U; // wrap around + } + m_ring[m_tail] = frontEvt; // buffer the old front evt + } + + QF_MEM_APP(); + QF_CRIT_EXIT(); +} + +//${QF::QEQueue::get} ........................................................ +QEvt const * QEQueue::get(std::uint_fast8_t const qs_id) noexcept { + #ifndef Q_SPY + Q_UNUSED_PAR(qs_id); + #endif + + QF_CRIT_STAT + QF_CRIT_ENTRY(); + QF_MEM_SYS(); + + QEvt const * const e = m_frontEvt; // always remove evt from the front + + if (e != nullptr) { // was the queue not empty? + QEQueueCtr const nFree = m_nFree + 1U; + m_nFree = nFree; // upate the # free + + // any events in the the ring buffer? + if (nFree <= m_end) { + m_frontEvt = m_ring[m_tail]; // remove from the tail + if (m_tail == 0U) { // need to wrap? + m_tail = m_end; // wrap around + } + m_tail = (m_tail - 1U); + + QS_BEGIN_PRE_(QS_QF_EQUEUE_GET, qs_id) + QS_TIME_PRE_(); // timestamp + QS_SIG_PRE_(e->sig); // the signal of this event + QS_OBJ_PRE_(this); // this queue object + QS_2U8_PRE_(e->getPoolId_(), e->refCtr_); // poolId & refCtr + QS_EQC_PRE_(nFree); // # free entries + QS_END_PRE_() + } + else { + m_frontEvt = nullptr; // queue becomes empty + + // all entries in the queue must be free (+1 for fronEvt) + Q_ASSERT_INCRIT(410, nFree == (m_end + 1U)); + + QS_BEGIN_PRE_(QS_QF_EQUEUE_GET_LAST, qs_id) + QS_TIME_PRE_(); // timestamp + QS_SIG_PRE_(e->sig); // the signal of this event + QS_OBJ_PRE_(this); // this queue object + QS_2U8_PRE_(e->getPoolId_(), e->refCtr_); // poolId & refCtr + QS_END_PRE_() + } + } + + QF_MEM_APP(); + QF_CRIT_EXIT(); + + return e; +} + +} // namespace QP +//$enddef${QF::QEQueue} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/src/qf/qf_qmact.cpp b/src/qf/qf_qmact.cpp new file mode 100644 index 0000000..d0b0937 --- /dev/null +++ b/src/qf/qf_qmact.cpp @@ -0,0 +1,73 @@ +//$file${src::qf::qf_qmact.cpp} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv +// +// Model: qpcpp.qm +// File: ${src::qf::qf_qmact.cpp} +// +// This code has been generated by QM 6.2.3 . +// DO NOT EDIT THIS FILE MANUALLY. All your changes will be lost. +// +// ____________________________________ +// / / +// / GGGGGGG PPPPPPPP LL / +// / GG GG PP PP LL / +// / GG PP PP LL / +// / GG GGGGG PPPPPPPP LL / +// / GG GG PP LL / +// / GGGGGGG PP LLLLLLL / +// /___________________________________/ +// +// Copyright (c) 2005 Quantum Leaps, LLC +// SPDX-License-Identifier: GPL-3.0-or-later +// +// This generated code is open-source software licensed under the GNU +// General Public License (GPL) as published by the Free Software Foundation +// (see ). +// +// NOTE: +// The GPL does NOT permit the incorporation of this code into proprietary +// programs. Please contact Quantum Leaps for commercial licensing options, +// which expressly supersede the GPL and are designed explicitly for licensees +// interested in retaining the proprietary status of the generated code. +// +// Quantum Leaps contact information: +// +// +// +//$endhead${src::qf::qf_qmact.cpp} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +#define QP_IMPL // this is QP implementation +#include "qp_port.hpp" // QP port +#include "qp_pkg.hpp" // QP package-scope interface +#include "qsafe.h" // QP Functional Safety (FuSa) Subsystem +#ifdef Q_SPY // QS software tracing enabled? + #include "qs_port.hpp" // QS port + #include "qs_pkg.hpp" // QS facilities for pre-defined trace records +#else + #include "qs_dummy.hpp" // disable the QS software tracing +#endif // Q_SPY + +// unnamed namespace for local definitions with internal linkage +namespace { +//Q_DEFINE_THIS_MODULE("qf_qmact") +} // unnamed namespace + +//$skip${QP_VERSION} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv +// Check for the minimum required QP version +#if (QP_VERSION < 730U) || (QP_VERSION != ((QP_RELEASE^4294967295U)%0x2710U)) +#error qpcpp version 7.3.0 or higher required +#endif +//$endskip${QP_VERSION} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +//$define${QF::QMActive} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv +namespace QP { + +//${QF::QMActive} ............................................................ + +//${QF::QMActive::QMActive} .................................................. +QMActive::QMActive(QStateHandler const initial) noexcept + : QActive(initial) +{ + m_state.obj = reinterpret_cast(this)->topQMState(); + m_temp.fun = initial; +} + +} // namespace QP +//$enddef${QF::QMActive} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/src/qf_time.cpp b/src/qf/qf_time.cpp similarity index 56% rename from src/qf_time.cpp rename to src/qf/qf_time.cpp index 2e1fcc8..bcb744a 100644 --- a/src/qf_time.cpp +++ b/src/qf/qf_time.cpp @@ -3,46 +3,41 @@ // Model: qpcpp.qm // File: ${src::qf::qf_time.cpp} // -// This code has been generated by QM 5.2.5 . +// This code has been generated by QM 6.2.3 . // DO NOT EDIT THIS FILE MANUALLY. All your changes will be lost. // -// This code is covered by the following QP license: -// License # : LicenseRef-QL-dual -// Issued to : Any user of the QP/C++ real-time embedded framework -// Framework(s) : qpcpp -// Support ends : 2023-12-31 -// License scope: +// ____________________________________ +// / / +// / GGGGGGG PPPPPPPP LL / +// / GG GG PP PP LL / +// / GG PP PP LL / +// / GG GGGGG PPPPPPPP LL / +// / GG GG PP LL / +// / GGGGGGG PP LLLLLLL / +// /___________________________________/ // -// Copyright (C) 2005 Quantum Leaps, LLC . +// Copyright (c) 2005 Quantum Leaps, LLC +// SPDX-License-Identifier: GPL-3.0-or-later // -// SPDX-License-Identifier: GPL-3.0-or-later OR LicenseRef-QL-commercial +// This generated code is open-source software licensed under the GNU +// General Public License (GPL) as published by the Free Software Foundation +// (see ). // -// 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. +// NOTE: +// The GPL does NOT permit the incorporation of this code into proprietary +// programs. Please contact Quantum Leaps for commercial licensing options, +// which expressly supersede the GPL and are designed explicitly for licensees +// interested in retaining the proprietary status of the generated code. // -// The terms of the open source GNU General Public License version 3 -// can be found at: -// -// The terms of the closed source Quantum Leaps commercial licenses -// can be found at: -// -// Redistributions in source code must retain this top-level comment block. -// Plagiarizing this software to sidestep the license obligations is illegal. -// -// Contact information: +// Quantum Leaps contact information: // // // //$endhead${src::qf::qf_time.cpp} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -//! @file -//! @brief QF/C++ time events and time management services - #define QP_IMPL // this is QP implementation -#include "qf_port.hpp" // QF port -#include "qf_pkg.hpp" // QF package-scope interface -#include "qassert.h" // QP embedded systems-friendly assertions +#include "qp_port.hpp" // QP port +#include "qp_pkg.hpp" // QP package-scope interface +#include "qsafe.h" // QP Functional Safety (FuSa) Subsystem #ifdef Q_SPY // QS software tracing enabled? #include "qs_port.hpp" // QS port #include "qs_pkg.hpp" // QS facilities for pre-defined trace records @@ -57,11 +52,10 @@ Q_DEFINE_THIS_MODULE("qf_time") //$skip${QP_VERSION} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv // Check for the minimum required QP version -#if (QP_VERSION < 700U) || (QP_VERSION != ((QP_RELEASE^4294967295U) % 0x3E8U)) -#error qpcpp version 7.0.0 or higher required +#if (QP_VERSION < 730U) || (QP_VERSION != ((QP_RELEASE^4294967295U)%0x2710U)) +#error qpcpp version 7.3.0 or higher required #endif //$endskip${QP_VERSION} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - //$define${QF::QTimeEvt} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv namespace QP { @@ -71,36 +65,23 @@ QTimeEvt QTimeEvt::timeEvtHead_[QF_MAX_TICK_RATE]; //${QF::QTimeEvt::QTimeEvt} .................................................. QTimeEvt::QTimeEvt( QActive * const act, - enum_t const sgnl, - std::uint_fast8_t const tickRate) -: -#ifndef Q_EVT_CTOR - QEvt(), -#else - QEvt(static_cast(sgnl), 0U), -#endif + QSignal const sig, + std::uint_fast8_t const tickRate) noexcept + : + QEvt(sig), m_next(nullptr), m_act(act), m_ctr(0U), m_interval(0U) { - //! @pre The signal must be valid and the tick rate in range - Q_REQUIRE_ID(300, (sgnl != 0) - && (tickRate < QF_MAX_TICK_RATE)); - - #ifndef Q_EVT_CTOR - sig = static_cast(sgnl); // set QEvt::sig of this time event - #endif - - // Setting the POOL_ID event attribute to zero is correct only for - // events not allocated from event pools, which must be the case - // for Time Events. - // - poolId_ = 0U; + QF_CRIT_STAT + QF_CRIT_ENTRY(); + Q_REQUIRE_INCRIT(300, (sig != 0U) + && (tickRate < QF_MAX_TICK_RATE)); + QF_CRIT_EXIT(); // The refCtr_ attribute is not used in time events, so it is // reused to hold the tickRate as well as other information - // refCtr_ = static_cast(tickRate); } @@ -111,28 +92,31 @@ void QTimeEvt::armX( { std::uint8_t const tickRate = refCtr_ & TE_TICK_RATE; QTimeEvtCtr const ctr = m_ctr; + #ifdef Q_SPY + std::uint_fast8_t const qs_id = + static_cast(m_act)->m_prio; + #endif - //! @pre the host AO must be valid, time evnet must be disarmed, - //! number of clock ticks cannot be zero, and the signal must be valid. - Q_REQUIRE_ID(400, (m_act != nullptr) - && (ctr == 0U) - && (nTicks != 0U) - && (tickRate < static_cast(QF_MAX_TICK_RATE)) - && (static_cast(sig) >= Q_USER_SIG)); - #ifdef Q_NASSERT - (void)ctr; // avoid compiler warning about unused variable + QF_CRIT_STAT + QF_CRIT_ENTRY(); + QF_MEM_SYS(); + + Q_REQUIRE_INCRIT(400, (m_act != nullptr) + && (ctr == 0U) + && (nTicks != 0U) + && (tickRate < static_cast(QF_MAX_TICK_RATE)) + && (sig >= static_cast(Q_USER_SIG))); + #ifdef Q_UNSAFE + Q_UNUSED_PAR(ctr); #endif - QF_CRIT_STAT_ - QF_CRIT_E_(); m_ctr = nTicks; m_interval = interval; // is the time event unlinked? // NOTE: For the duration of a single clock tick of the specified tick - // rate a time event can be disarmed and yet still linked into the list, + // rate a time event can be disarmed and yet still linked into the list // because un-linking is performed exclusively in the QF_tickX() function. - // if (static_cast( static_cast(refCtr_) & TE_IS_LINKED) == 0U) { @@ -141,51 +125,51 @@ void QTimeEvt::armX( // The time event is initially inserted into the separate // "freshly armed" list based on timeEvtHead_[tickRate].act. - // Only later, inside QTimeEvt::tick_(), the "freshly armed" + // Only later, inside QTimeEvt::tick(), the "freshly armed" // list is appended to the main list of armed time events based on // timeEvtHead_[tickRate].next. Again, this is to keep any - // changes to the main list exclusively inside QTimeEvt::tick_(). + // changes to the main list exclusively inside QTimeEvt::tick(). m_next = timeEvtHead_[tickRate].toTimeEvt(); timeEvtHead_[tickRate].m_act = this; } - #ifdef Q_SPY - std::uint_fast8_t const qs_id = static_cast(m_act)->m_prio; - #endif - QS_BEGIN_NOCRIT_PRE_(QS_QF_TIMEEVT_ARM, qs_id) + QS_BEGIN_PRE_(QS_QF_TIMEEVT_ARM, qs_id) QS_TIME_PRE_(); // timestamp QS_OBJ_PRE_(this); // this time event object QS_OBJ_PRE_(m_act); // the active object - QS_TEC_PRE_(nTicks); // the number of ticks + QS_TEC_PRE_(nTicks); // the # ticks QS_TEC_PRE_(interval); // the interval QS_U8_PRE_(tickRate); // tick rate - QS_END_NOCRIT_PRE_() + QS_END_PRE_() - QF_CRIT_X_(); + QF_MEM_APP(); + QF_CRIT_EXIT(); } //${QF::QTimeEvt::disarm} .................................................... bool QTimeEvt::disarm() noexcept { - QF_CRIT_STAT_ - QF_CRIT_E_(); #ifdef Q_SPY std::uint_fast8_t const qs_id = static_cast(m_act)->m_prio; #endif + QF_CRIT_STAT + QF_CRIT_ENTRY(); + QF_MEM_SYS(); + // is the time event actually armed? bool wasArmed; if (m_ctr != 0U) { wasArmed = true; refCtr_ = static_cast(refCtr_ | TE_WAS_DISARMED); - QS_BEGIN_NOCRIT_PRE_(QS_QF_TIMEEVT_DISARM, qs_id) + QS_BEGIN_PRE_(QS_QF_TIMEEVT_DISARM, qs_id) QS_TIME_PRE_(); // timestamp QS_OBJ_PRE_(this); // this time event object QS_OBJ_PRE_(m_act); // the target AO - QS_TEC_PRE_(m_ctr); // the number of ticks + QS_TEC_PRE_(m_ctr); // the # ticks QS_TEC_PRE_(m_interval); // the interval - QS_U8_PRE_(refCtr_& TE_TICK_RATE); - QS_END_NOCRIT_PRE_() + QS_U8_PRE_(refCtr_& TE_TICK_RATE); // tick rate + QS_END_PRE_() m_ctr = 0U; // schedule removal from the list } @@ -194,15 +178,16 @@ bool QTimeEvt::disarm() noexcept { refCtr_ = static_cast(refCtr_ & static_cast(~TE_WAS_DISARMED)); - QS_BEGIN_NOCRIT_PRE_(QS_QF_TIMEEVT_DISARM_ATTEMPT, qs_id) + QS_BEGIN_PRE_(QS_QF_TIMEEVT_DISARM_ATTEMPT, qs_id) QS_TIME_PRE_(); // timestamp QS_OBJ_PRE_(this); // this time event object QS_OBJ_PRE_(m_act); // the target AO QS_U8_PRE_(refCtr_& TE_TICK_RATE); // tick rate - QS_END_NOCRIT_PRE_() - + QS_END_PRE_() } - QF_CRIT_X_(); + + QF_MEM_APP(); + QF_CRIT_EXIT(); return wasArmed; } @@ -211,100 +196,111 @@ bool QTimeEvt::disarm() noexcept { bool QTimeEvt::rearm(QTimeEvtCtr const nTicks) noexcept { std::uint8_t const tickRate = refCtr_ & TE_TICK_RATE; - //! @pre AO must be valid, tick rate must be in range, nTicks must not - //! be zero, and the signal of this time event must be valid - Q_REQUIRE_ID(600, (m_act != nullptr) + #ifdef Q_SPY + std::uint_fast8_t const qs_id = static_cast(m_act)->m_prio; + #endif + + QF_CRIT_STAT + QF_CRIT_ENTRY(); + QF_MEM_SYS(); + + Q_REQUIRE_INCRIT(600, (m_act != nullptr) && (tickRate < static_cast(QF_MAX_TICK_RATE)) && (nTicks != 0U) - && (static_cast(sig) >= Q_USER_SIG)); - - QF_CRIT_STAT_ - QF_CRIT_E_(); + && (sig >= static_cast(Q_USER_SIG))); // is the time evt not running? bool wasArmed; if (m_ctr == 0U) { wasArmed = false; - // is the time event unlinked? // NOTE: For a duration of a single clock tick of the specified // tick rate a time event can be disarmed and yet still linked into // the list, because unlinking is performed exclusively in the // QTimeEvt::tickX() function. + + // is the time event unlinked? if (static_cast(refCtr_ & TE_IS_LINKED) == 0U) { // mark as linked refCtr_ = static_cast(refCtr_ | TE_IS_LINKED); // The time event is initially inserted into the separate // "freshly armed" list based on timeEvtHead_[tickRate].act. - // Only later, inside QTimeEvt::tick_(), the "freshly armed" + // Only later, inside QTimeEvt::tick(), the "freshly armed" // list is appended to the main list of armed time events based on // timeEvtHead_[tickRate].next. Again, this is to keep any - // changes to the main list exclusively inside QTimeEvt::tick_(). + // changes to the main list exclusively inside QTimeEvt::tick(). m_next = timeEvtHead_[tickRate].toTimeEvt(); timeEvtHead_[tickRate].m_act = this; } } - else { // the time event is being disarmed + else { // the time event was armed wasArmed = true; } m_ctr = nTicks; // re-load the tick counter (shift the phasing) - #ifdef Q_SPY - std::uint_fast8_t const qs_id = static_cast(m_act)->m_prio; - #endif - QS_BEGIN_NOCRIT_PRE_(QS_QF_TIMEEVT_REARM, qs_id) - QS_TIME_PRE_(); // timestamp - QS_OBJ_PRE_(this); // this time event object - QS_OBJ_PRE_(m_act); // the target AO - QS_TEC_PRE_(m_ctr); // the number of ticks - QS_TEC_PRE_(m_interval); // the interval + QS_BEGIN_PRE_(QS_QF_TIMEEVT_REARM, qs_id) + QS_TIME_PRE_(); // timestamp + QS_OBJ_PRE_(this); // this time event object + QS_OBJ_PRE_(m_act); // the target AO + QS_TEC_PRE_(m_ctr); // the # ticks + QS_TEC_PRE_(m_interval); // the interval QS_2U8_PRE_(tickRate, (wasArmed ? 1U : 0U)); - QS_END_NOCRIT_PRE_() + QS_END_PRE_() - QF_CRIT_X_(); + QF_MEM_APP(); + QF_CRIT_EXIT(); return wasArmed; } //${QF::QTimeEvt::wasDisarmed} ............................................... bool QTimeEvt::wasDisarmed() noexcept { + QF_CRIT_STAT + QF_CRIT_ENTRY(); + QF_MEM_SYS(); + std::uint8_t const isDisarmed = refCtr_ & TE_WAS_DISARMED; - // mark as disarmed refCtr_ = static_cast(refCtr_ | TE_WAS_DISARMED); + + QF_MEM_APP(); + QF_CRIT_EXIT(); + return isDisarmed != 0U; } -//${QF::QTimeEvt::tick_} ..................................................... -void QTimeEvt::tick_( +//${QF::QTimeEvt::tick} ...................................................... +void QTimeEvt::tick( std::uint_fast8_t const tickRate, void const * const sender) noexcept { - Q_UNUSED_PAR(sender); // when Q_SPY not defined + #ifndef Q_SPY + Q_UNUSED_PAR(sender); + #endif QTimeEvt *prev = &timeEvtHead_[tickRate]; - QF_CRIT_STAT_ - QF_CRIT_E_(); + QF_CRIT_STAT + QF_CRIT_ENTRY(); + QF_MEM_SYS(); - QS_BEGIN_NOCRIT_PRE_(QS_QF_TICK, 0U) + QS_BEGIN_PRE_(QS_QF_TICK, 0U) prev->m_ctr = (prev->m_ctr + 1U); QS_TEC_PRE_(prev->m_ctr); // tick ctr QS_U8_PRE_(tickRate); // tick rate - QS_END_NOCRIT_PRE_() + QS_END_PRE_() // scan the linked-list of time events at this rate... for (;;) { QTimeEvt *t = prev->m_next; // advance down the time evt. list - // end of the list? - if (t == nullptr) { + if (t == nullptr) { // end of the list? - // any new time events armed since the last run of tick_()? + // any new time events armed since the last run of tick()? if (timeEvtHead_[tickRate].m_act != nullptr) { // sanity check - Q_ASSERT_CRIT_(110, prev != nullptr); + Q_ASSERT_INCRIT(110, prev != nullptr); prev->m_next = timeEvtHead_[tickRate].toTimeEvt(); timeEvtHead_[tickRate].m_act = nullptr; t = prev->m_next; // switch to the new list @@ -314,32 +310,36 @@ void QTimeEvt::tick_( } } - // time event scheduled for removal? - if (t->m_ctr == 0U) { + if (t->m_ctr == 0U) { // time event scheduled for removal? prev->m_next = t->m_next; // mark time event 't' as NOT linked t->refCtr_ = static_cast(t->refCtr_ & static_cast(~TE_IS_LINKED)); // do NOT advance the prev pointer - QF_CRIT_X_(); // exit crit. section to reduce latency - - // prevent merging critical sections, see NOTE1 below + 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->m_ctr = (t->m_ctr - 1U); - // is time evt about to expire? - if (t->m_ctr == 0U) { + if (t->m_ctr == 0U) { // is time evt about to expire? QActive * const act = t->toActive(); - // periodic time evt? - if (t->m_interval != 0U) { + if (t->m_interval != 0U) { // periodic time evt? t->m_ctr = t->m_interval; // rearm the time event prev = t; // advance to this time event } - // one-shot time event: automatically disarm - else { + else { // one-shot time event: automatically disarm prev->m_next = t->m_next; // mark time event 't' as NOT linked @@ -347,52 +347,64 @@ void QTimeEvt::tick_( & static_cast(~TE_IS_LINKED)); // do NOT advance the prev pointer - QS_BEGIN_NOCRIT_PRE_(QS_QF_TIMEEVT_AUTO_DISARM, - act->m_prio) - QS_OBJ_PRE_(t); // this time event object - QS_OBJ_PRE_(act); // the target AO - QS_U8_PRE_(tickRate); // tick rate - QS_END_NOCRIT_PRE_() + QS_BEGIN_PRE_(QS_QF_TIMEEVT_AUTO_DISARM, act->m_prio) + QS_OBJ_PRE_(t); // this time event object + QS_OBJ_PRE_(act); // the target AO + QS_U8_PRE_(tickRate); // tick rate + QS_END_PRE_() } - QS_BEGIN_NOCRIT_PRE_(QS_QF_TIMEEVT_POST, act->m_prio) - QS_TIME_PRE_(); // timestamp - QS_OBJ_PRE_(t); // the time event object - QS_SIG_PRE_(t->sig); // signal of this time event - QS_OBJ_PRE_(act); // the target AO - QS_U8_PRE_(tickRate); // tick rate - QS_END_NOCRIT_PRE_() + QS_BEGIN_PRE_(QS_QF_TIMEEVT_POST, act->m_prio) + QS_TIME_PRE_(); // timestamp + QS_OBJ_PRE_(t); // the time event object + QS_SIG_PRE_(t->sig); // signal of this time event + QS_OBJ_PRE_(act); // the target AO + QS_U8_PRE_(tickRate); // tick rate + QS_END_PRE_() + + #ifdef QXK_HPP_ + if (t->sig < Q_USER_SIG) { + QXThread::timeout_(act); + } + else { + QF_MEM_APP(); + QF_CRIT_EXIT(); // exit crit. section before posting - QF_CRIT_X_(); // exit crit. section before posting + // act->POST() asserts if the queue overflows + act->POST(t, sender); + } + #else + QF_MEM_APP(); + QF_CRIT_EXIT(); // exit crit. section before posting - // asserts if queue overflows + // act->POST() asserts if the queue overflows act->POST(t, sender); + #endif } else { prev = t; // advance to this time event - QF_CRIT_X_(); // exit crit. section to reduce latency - - // prevent merging critical sections - // In some QF ports the critical section exit takes effect only - // on the next machine instruction. If this case, the next - // instruction is another entry to a critical section, the - // critical section won't 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_MEM_APP(); + QF_CRIT_EXIT(); // exit crit. section to reduce latency + + // prevent merging critical sections, see NOTE above QF_CRIT_EXIT_NOP(); } } - QF_CRIT_E_(); // re-enter crit. section to continue + QF_CRIT_ENTRY(); // re-enter crit. section to continue the loop + QF_MEM_SYS(); } - QF_CRIT_X_(); + + QF_MEM_APP(); + QF_CRIT_EXIT(); } //${QF::QTimeEvt::noActive} .................................................. bool QTimeEvt::noActive(std::uint_fast8_t const tickRate) noexcept { - //! @pre the tick rate must be in range - Q_REQUIRE_ID(200, tickRate < QF_MAX_TICK_RATE); + QF_CRIT_STAT + QF_CRIT_ENTRY(); + Q_REQUIRE_INCRIT(800, tickRate < QF_MAX_TICK_RATE); + QF_CRIT_EXIT(); bool inactive; if (timeEvtHead_[tickRate].m_next != nullptr) { @@ -408,32 +420,17 @@ bool QTimeEvt::noActive(std::uint_fast8_t const tickRate) noexcept { } //${QF::QTimeEvt::QTimeEvt} .................................................. -QTimeEvt::QTimeEvt() +QTimeEvt::QTimeEvt() noexcept : -#ifdef Q_EVT_CTOR - QEvt(0U, 0U), -#else - QEvt(), -#endif // Q_EVT_CTOR + QEvt(0U), m_next(nullptr), m_act(nullptr), m_ctr(0U), m_interval(0U) { - #ifndef Q_EVT_CTOR - sig = 0U; - - // Setting the POOL_ID event attribute to zero is correct only for - // events not allocated from event pools, which must be the case - // for Time Events. - // - poolId_ = 0U; // not from any event pool - // The refCtr_ attribute is not used in time events, so it is // reused to hold the tickRate as well as other information - // refCtr_ = 0U; // default rate 0 - #endif // Q_EVT_CTOR } } // namespace QP diff --git a/src/qf_act.cpp b/src/qf_act.cpp deleted file mode 100644 index bf98f6f..0000000 --- a/src/qf_act.cpp +++ /dev/null @@ -1,45 +0,0 @@ -//$file${src::qf::qf_act.cpp} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv -// -// Model: qpcpp.qm -// File: ${src::qf::qf_act.cpp} -// -// This code has been generated by QM 5.2.5 . -// DO NOT EDIT THIS FILE MANUALLY. All your changes will be lost. -// -// This code is covered by the following QP license: -// License # : LicenseRef-QL-dual -// Issued to : Any user of the QP/C++ real-time embedded framework -// Framework(s) : qpcpp -// Support ends : 2023-12-31 -// License scope: -// -// Copyright (C) 2005 Quantum Leaps, LLC . -// -// 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: -// -// The terms of the closed source Quantum Leaps commercial licenses -// can be found at: -// -// Redistributions in source code must retain this top-level comment block. -// Plagiarizing this software to sidestep the license obligations is illegal. -// -// Contact information: -// -// -// -//$endhead${src::qf::qf_act.cpp} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -//! @file -//! @deprecated -//! Empty file kept only for backwards compatibility. -//! @sa qf_qact.cpp - -extern char const dummy; // declaration -char const dummy = '\0'; // definition diff --git a/src/qf_actq.cpp b/src/qf_actq.cpp deleted file mode 100644 index daa48a8..0000000 --- a/src/qf_actq.cpp +++ /dev/null @@ -1,437 +0,0 @@ -//$file${src::qf::qf_actq.cpp} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv -// -// Model: qpcpp.qm -// File: ${src::qf::qf_actq.cpp} -// -// This code has been generated by QM 5.2.5 . -// DO NOT EDIT THIS FILE MANUALLY. All your changes will be lost. -// -// This code is covered by the following QP license: -// License # : LicenseRef-QL-dual -// Issued to : Any user of the QP/C++ real-time embedded framework -// Framework(s) : qpcpp -// Support ends : 2023-12-31 -// License scope: -// -// Copyright (C) 2005 Quantum Leaps, LLC . -// -// 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: -// -// The terms of the closed source Quantum Leaps commercial licenses -// can be found at: -// -// Redistributions in source code must retain this top-level comment block. -// Plagiarizing this software to sidestep the license obligations is illegal. -// -// Contact information: -// -// -// -//$endhead${src::qf::qf_actq.cpp} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -//! @file -//! @brief QP::QActive native queue operations (based on QP::QEQueue) -//! -//! @attention -//! This qf_actq.cpp source file is only included in the build when the -//! macro #QF_EQUEUE_TYPE is defined as QEQueue. This means that the QP -//! port uses the QP::QEQueue for active objects and so this implementation -//! applies to the QP port. - -#define QP_IMPL // this is QP implementation -#include "qf_port.hpp" // QF port -#include "qf_pkg.hpp" // QF package-scope interface -#include "qassert.h" // QP embedded systems-friendly assertions -#ifdef Q_SPY // QS software tracing enabled? - #include "qs_port.hpp" // QS port - #include "qs_pkg.hpp" // QS facilities for pre-defined trace records -#else - #include "qs_dummy.hpp" // disable the QS software tracing -#endif // Q_SPY - -//============================================================================ -// unnamed namespace for local definitions with internal linkage -namespace { -Q_DEFINE_THIS_MODULE("qf_actq") -} // unnamed namespace - -//============================================================================ -//$skip${QP_VERSION} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv -// Check for the minimum required QP version -#if (QP_VERSION < 700U) || (QP_VERSION != ((QP_RELEASE^4294967295U) % 0x3E8U)) -#error qpcpp version 7.0.0 or higher required -#endif -//$endskip${QP_VERSION} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -//$define${QF::QActive::post_} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv -namespace QP { - -//${QF::QActive::post_} ...................................................... -bool QActive::post_( - QEvt const * const e, - std::uint_fast16_t const margin, - void const * const sender) noexcept -{ - Q_UNUSED_PAR(sender); // when Q_SPY not defined - - //! @pre event pointer must be valid - Q_REQUIRE_ID(100, e != nullptr); - - QF_CRIT_STAT_ - QF_CRIT_E_(); - QEQueueCtr nFree = m_eQueue.m_nFree; // get into the 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_CRIT_(110); // must be able to post the event - } - } - else if (nFree > static_cast(margin)) { - status = true; // can post - } - else { - status = false; // cannot post, but don't assert - } - - // is it a dynamic event? - if (e->poolId_ != 0U) { - QEvt_refCtr_inc_(e); // increment the reference counter - } - - if (status) { // can post the event? - - --nFree; // one free entry just used up - m_eQueue.m_nFree = nFree; // update the original - if (m_eQueue.m_nMin > nFree) { - m_eQueue.m_nMin = nFree; // update minimum so far - } - - QS_BEGIN_NOCRIT_PRE_(QS_QF_ACTIVE_POST, m_prio) - QS_TIME_PRE_(); // timestamp - QS_OBJ_PRE_(sender); // the sender object - QS_SIG_PRE_(e->sig); // the signal of the event - QS_OBJ_PRE_(this); // this active object - QS_2U8_PRE_(e->poolId_, e->refCtr_); // pool-Id & ref-ctr - QS_EQC_PRE_(nFree); // number of free entries - QS_EQC_PRE_(m_eQueue.m_nMin); // min number of free entries - QS_END_NOCRIT_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_(m_prio)) { - QS::onTestPost(sender, this, e, status); - } - #endif - // empty queue? - if (m_eQueue.m_frontEvt == nullptr) { - m_eQueue.m_frontEvt = e; // deliver event directly - QACTIVE_EQUEUE_SIGNAL_(this); // signal the event queue - } - // queue is not empty, insert event into the ring-buffer - else { - // insert event pointer e into the buffer (FIFO) - m_eQueue.m_ring[m_eQueue.m_head] = e; - - // need to wrap head? - if (m_eQueue.m_head == 0U) { - m_eQueue.m_head = m_eQueue.m_end; // wrap around - } - // advance the head (counter clockwise) - m_eQueue.m_head = (m_eQueue.m_head - 1U); - } - - QF_CRIT_X_(); - } - else { // cannot post the event - - QS_BEGIN_NOCRIT_PRE_(QS_QF_ACTIVE_POST_ATTEMPT, m_prio) - QS_TIME_PRE_(); // timestamp - QS_OBJ_PRE_(sender); // the sender object - QS_SIG_PRE_(e->sig); // the signal of the event - QS_OBJ_PRE_(this); // this active object - QS_2U8_PRE_(e->poolId_, e->refCtr_); // pool-Id & ref-ctr - QS_EQC_PRE_(nFree); // number of free entries - QS_EQC_PRE_(margin); // margin requested - QS_END_NOCRIT_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_(m_prio)) { - QS::onTestPost(sender, this, e, status); - } - #endif - - QF_CRIT_X_(); - - #if (QF_MAX_EPOOL > 0U) - QF::gc(e); // recycle the event to avoid a leak - #endif - } - - return status; -} - -} // namespace QP -//$enddef${QF::QActive::post_} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -//$define${QF::QActive::postLIFO} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv -namespace QP { - -//${QF::QActive::postLIFO} ................................................... -void QActive::postLIFO(QEvt const * const e) noexcept { - QF_CRIT_STAT_ - QF_CRIT_E_(); - QEQueueCtr nFree = m_eQueue.m_nFree; - - QS_TEST_PROBE_DEF(&QActive::postLIFO) - QS_TEST_PROBE_ID(1, - nFree = 0U; - ) - - // the queue must be able to accept the event (cannot overflow) - Q_ASSERT_CRIT_(210, nFree != 0U); - - // is it a dynamic event? - if (e->poolId_ != 0U) { - QEvt_refCtr_inc_(e); // increment the reference counter - } - - --nFree; // one free entry just used up - m_eQueue.m_nFree = nFree; // update the original - if (m_eQueue.m_nMin > nFree) { - m_eQueue.m_nMin = nFree; // update minimum so far - } - - QS_BEGIN_NOCRIT_PRE_(QS_QF_ACTIVE_POST_LIFO, m_prio) - QS_TIME_PRE_(); // timestamp - QS_SIG_PRE_(e->sig); // the signal of this event - QS_OBJ_PRE_(this); // this active object - QS_2U8_PRE_(e->poolId_, e->refCtr_); // pool-Id & ref-ctr - QS_EQC_PRE_(nFree); // number of free entries - QS_EQC_PRE_(m_eQueue.m_nMin); // min number of free entries - QS_END_NOCRIT_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_(m_prio)) { - QS::onTestPost(nullptr, this, e, true); - } - #endif - - QEvt const * const frontEvt = m_eQueue.m_frontEvt; - m_eQueue.m_frontEvt = e; // deliver the event directly to the front - - // was the queue empty? - if (frontEvt == nullptr) { - QACTIVE_EQUEUE_SIGNAL_(this); // signal the event queue - } - // queue was not empty, leave the event in the ring-buffer - else { - m_eQueue.m_tail = (m_eQueue.m_tail + 1U); - if (m_eQueue.m_tail == m_eQueue.m_end) { // need to wrap the tail? - m_eQueue.m_tail = 0U; // wrap around - } - - m_eQueue.m_ring[m_eQueue.m_tail] = frontEvt; - } - QF_CRIT_X_(); - -} - -} // namespace QP -//$enddef${QF::QActive::postLIFO} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -//$define${QF::QActive::get_} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv -namespace QP { - -//${QF::QActive::get_} ....................................................... -QEvt const * QActive::get_() noexcept { - QF_CRIT_STAT_ - QF_CRIT_E_(); - QACTIVE_EQUEUE_WAIT_(this); // wait for event to arrive directly - - // always remove evt from the front - QEvt const * const e = m_eQueue.m_frontEvt; - QEQueueCtr const nFree = m_eQueue.m_nFree + 1U; - m_eQueue.m_nFree = nFree; // upate the number of free - - // any events in the ring buffer? - if (nFree <= m_eQueue.m_end) { - - // remove event from the tail - m_eQueue.m_frontEvt = m_eQueue.m_ring[m_eQueue.m_tail]; - if (m_eQueue.m_tail == 0U) { // need to wrap? - m_eQueue.m_tail = m_eQueue.m_end; // wrap around - } - m_eQueue.m_tail = (m_eQueue.m_tail - 1U); - - QS_BEGIN_NOCRIT_PRE_(QS_QF_ACTIVE_GET, m_prio) - QS_TIME_PRE_(); // timestamp - QS_SIG_PRE_(e->sig); // the signal of this event - QS_OBJ_PRE_(this); // this active object - QS_2U8_PRE_(e->poolId_, e->refCtr_); // pool-Id & ref-ctr - QS_EQC_PRE_(nFree); // number of free entries - QS_END_NOCRIT_PRE_() - } - else { - // the queue becomes empty - m_eQueue.m_frontEvt = nullptr; - - // all entries in the queue must be free (+1 for fronEvt) - Q_ASSERT_CRIT_(310, nFree == (m_eQueue.m_end + 1U)); - - QS_BEGIN_NOCRIT_PRE_(QS_QF_ACTIVE_GET_LAST, m_prio) - QS_TIME_PRE_(); // timestamp - QS_SIG_PRE_(e->sig); // the signal of this event - QS_OBJ_PRE_(this); // this active object - QS_2U8_PRE_(e->poolId_, e->refCtr_); // pool-Id & ref-ctr - QS_END_NOCRIT_PRE_() - } - QF_CRIT_X_(); - return e; -} - -} // namespace QP -//$enddef${QF::QActive::get_} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -//$define${QF::QF-base::getQueueMin} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv -namespace QP { -namespace QF { - -//${QF::QF-base::getQueueMin} ................................................ -std::uint_fast16_t getQueueMin(std::uint_fast8_t const prio) noexcept { - Q_REQUIRE_ID(400, (prio <= QF_MAX_ACTIVE) - && (QActive::registry_[prio] != nullptr)); - QF_CRIT_STAT_ - QF_CRIT_E_(); - std::uint_fast16_t const min = static_cast( - QActive::registry_[prio]->m_eQueue.getNMin()); - QF_CRIT_X_(); - - return min; -} - -} // namespace QF -} // namespace QP -//$enddef${QF::QF-base::getQueueMin} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -//============================================================================ -//$define${QF::QTicker} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv -namespace QP { - -//${QF::QTicker} ............................................................. - -//${QF::QTicker::QTicker} .................................................... -QTicker::QTicker(std::uint_fast8_t const tickRate) noexcept -: QActive(nullptr) -{ - // reuse m_head for tick-rate - m_eQueue.m_head = static_cast(tickRate); -} - -//${QF::QTicker::init} ....................................................... -void QTicker::init( - void const * const e, - std::uint_fast8_t const qs_id) -{ - Q_UNUSED_PAR(e); - Q_UNUSED_PAR(qs_id); - m_eQueue.m_tail = 0U; -} - -//${QF::QTicker::init} ....................................................... -void QTicker::init(std::uint_fast8_t const qs_id) { - QTicker::init(nullptr, qs_id); -} - -//${QF::QTicker::dispatch} ................................................... -void QTicker::dispatch( - QEvt const * const e, - std::uint_fast8_t const qs_id) -{ - Q_UNUSED_PAR(e); - Q_UNUSED_PAR(qs_id); - - QF_CRIT_STAT_ - QF_CRIT_E_(); - QEQueueCtr nTicks = m_eQueue.m_tail; // # ticks since the last call - m_eQueue.m_tail = 0U; // clear the # ticks - QF_CRIT_X_(); - - for (; nTicks > 0U; --nTicks) { - QTimeEvt::TICK_X(static_cast(m_eQueue.m_head), - this); - } -} - -//${QF::QTicker::post_} ...................................................... -bool QTicker::post_( - QEvt const * const e, - std::uint_fast16_t const margin, - void const * const sender) noexcept -{ - Q_UNUSED_PAR(e); - Q_UNUSED_PAR(margin); - Q_UNUSED_PAR(sender); // when Q_SPY not defined - - QF_CRIT_STAT_ - QF_CRIT_E_(); - if (m_eQueue.m_frontEvt == nullptr) { - - #ifdef Q_EVT_CTOR - static QEvt const tickEvt(0U, 0U); - #else - static QEvt const tickEvt = { 0U, 0U, 0U }; - #endif // Q_EVT_CTOR - - m_eQueue.m_frontEvt = &tickEvt; // deliver event directly - m_eQueue.m_nFree = (m_eQueue.m_nFree - 1U); // one less free event - - QACTIVE_EQUEUE_SIGNAL_(this); // signal the event queue - } - - // account for one more tick event - m_eQueue.m_tail = (m_eQueue.m_tail + 1U); - - QS_BEGIN_NOCRIT_PRE_(QS_QF_ACTIVE_POST, m_prio) - QS_TIME_PRE_(); // timestamp - QS_OBJ_PRE_(sender); // the sender object - QS_SIG_PRE_(0U); // the signal of the event - QS_OBJ_PRE_(this); // this active object - QS_2U8_PRE_(0U, 0U); // pool-Id & ref-ctr - QS_EQC_PRE_(0U); // number of free entries - QS_EQC_PRE_(0U); // min number of free entries - QS_END_NOCRIT_PRE_() - - QF_CRIT_X_(); - - return true; // the event is always posted correctly -} - -} // namespace QP -//$enddef${QF::QTicker} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/src/qf_dyn.cpp b/src/qf_dyn.cpp deleted file mode 100644 index f7701ae..0000000 --- a/src/qf_dyn.cpp +++ /dev/null @@ -1,309 +0,0 @@ -//$file${src::qf::qf_dyn.cpp} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv -// -// Model: qpcpp.qm -// File: ${src::qf::qf_dyn.cpp} -// -// This code has been generated by QM 5.2.5 . -// DO NOT EDIT THIS FILE MANUALLY. All your changes will be lost. -// -// This code is covered by the following QP license: -// License # : LicenseRef-QL-dual -// Issued to : Any user of the QP/C++ real-time embedded framework -// Framework(s) : qpcpp -// Support ends : 2023-12-31 -// License scope: -// -// Copyright (C) 2005 Quantum Leaps, LLC . -// -// 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: -// -// The terms of the closed source Quantum Leaps commercial licenses -// can be found at: -// -// Redistributions in source code must retain this top-level comment block. -// Plagiarizing this software to sidestep the license obligations is illegal. -// -// Contact information: -// -// -// -//$endhead${src::qf::qf_dyn.cpp} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -//! @file -//! @brief QF/C++ dynamic event management - -#define QP_IMPL // this is QP implementation -#include "qf_port.hpp" // QF port -#include "qf_pkg.hpp" // QF package-scope interface -#include "qassert.h" // QP embedded systems-friendly assertions -#ifdef Q_SPY // QS software tracing enabled? - #include "qs_port.hpp" // QS port - #include "qs_pkg.hpp" // QS facilities for pre-defined trace records -#else - #include "qs_dummy.hpp" // disable the QS software tracing -#endif // Q_SPY - -#if (QF_MAX_EPOOL > 0U) // dynamic events configured? - -// unnamed namespace for local definitions with internal linkage -namespace { -Q_DEFINE_THIS_MODULE("qf_dyn") -} // unnamed namespace - -//============================================================================ -//$skip${QP_VERSION} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv -// Check for the minimum required QP version -#if (QP_VERSION < 700U) || (QP_VERSION != ((QP_RELEASE^4294967295U) % 0x3E8U)) -#error qpcpp version 7.0.0 or higher required -#endif -//$endskip${QP_VERSION} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -//$define${QF::QF-pkg::maxPool_} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv -namespace QP { -namespace QF { - -//${QF::QF-pkg::maxPool_} .................................................... -std::uint_fast8_t maxPool_; - -} // namespace QF -} // namespace QP -//$enddef${QF::QF-pkg::maxPool_} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -//$define${QF::QF-pkg::ePool_[QF_MAX_EPOOL]} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv -namespace QP { -namespace QF { - -//${QF::QF-pkg::ePool_[QF_MAX_EPOOL]} ........................................ -#if (QF_MAX_EPOOL > 0U) -QF_EPOOL_TYPE_ ePool_[QF_MAX_EPOOL]; -#endif // (QF_MAX_EPOOL > 0U) - -} // namespace QF -} // namespace QP -//$enddef${QF::QF-pkg::ePool_[QF_MAX_EPOOL]} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -//============================================================================ -//$define${QF::QF-dyn} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv -namespace QP { -namespace QF { - -//${QF::QF-dyn::poolInit} .................................................... -void poolInit( - void * const poolSto, - std::uint_fast32_t const poolSize, - std::uint_fast16_t const evtSize) noexcept -{ - //! @pre cannot exceed the number of available memory pools - Q_REQUIRE_ID(200, QF::maxPool_ < QF_MAX_EPOOL); - - //! @pre QF event pools must be initialized in ascending order of evtSize - if (QF::maxPool_ > 0U) { - Q_REQUIRE_ID(201, - QF_EPOOL_EVENT_SIZE_(QF::ePool_[QF::maxPool_ - 1U]) < evtSize); - } - - QF_EPOOL_INIT_(QF::ePool_[QF::maxPool_], poolSto, poolSize, evtSize); - ++QF::maxPool_; // one more pool - - #ifdef Q_SPY - // generate the object-dictionary entry for the initialized pool - char obj_name[9] = "EvtPool?"; - obj_name[7] = static_cast( - static_cast('0') - + static_cast(QF::maxPool_)); - QS::obj_dict_pre_(&QF::ePool_[QF::maxPool_ - 1U], &obj_name[0]); - #endif // Q_SPY -} - -//${QF::QF-dyn::newX_} ....................................................... -QEvt * newX_( - std::uint_fast16_t const evtSize, - std::uint_fast16_t const margin, - enum_t const sig) noexcept -{ - std::uint_fast8_t idx; - - // find the pool id that fits the requested event size ... - for (idx = 0U; idx < QF::maxPool_; ++idx) { - if (evtSize <= QF_EPOOL_EVENT_SIZE_(QF::ePool_[idx])) { - break; - } - } - // cannot run out of registered pools - Q_ASSERT_ID(310, idx < QF::maxPool_); - - // get e -- platform-dependent - QEvt *e; - - #ifdef Q_SPY - QF_EPOOL_GET_(QF::ePool_[idx], e, ((margin != QF::NO_MARGIN) ? margin : 0U), - static_cast(QS_EP_ID) + idx + 1U); - #else - QF_EPOOL_GET_(QF::ePool_[idx], e, ((margin != QF::NO_MARGIN) ? margin : 0U), - 0U); - #endif - - // was e allocated correctly? - QS_CRIT_STAT_ - if (e != nullptr) { - e->sig = static_cast(sig); // set the signal - e->poolId_ = static_cast(idx + 1U); // store pool ID - e->refCtr_ = 0U; // initialize the reference counter to 0 - - QS_BEGIN_PRE_(QS_QF_NEW, - static_cast(QS_EP_ID) - + static_cast(e->poolId_)) - QS_TIME_PRE_(); // timestamp - QS_EVS_PRE_(evtSize); // the size of the evt - QS_SIG_PRE_(sig); // the signal of the evt - QS_END_PRE_() - } - else { - // 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_ID(320, margin != QF::NO_MARGIN); - - QS_BEGIN_PRE_(QS_QF_NEW_ATTEMPT, - static_cast(QS_EP_ID) + idx + 1U) - QS_TIME_PRE_(); // timestamp - QS_EVS_PRE_(evtSize); // the size of the evt - QS_SIG_PRE_(sig); // the signal of the evt - QS_END_PRE_() - } - return e; // can't be NULL if we can't tolerate bad allocation -} - -//${QF::QF-dyn::gc} .......................................................... -void gc(QEvt const * const e) noexcept { - // is it a dynamic event? - if (e->poolId_ != 0U) { - QF_CRIT_STAT_ - QF_CRIT_E_(); - - // isn't this the last reference? - if (e->refCtr_ > 1U) { - - QS_BEGIN_NOCRIT_PRE_(QS_QF_GC_ATTEMPT, - static_cast(QS_EP_ID) - + static_cast(e->poolId_)) - QS_TIME_PRE_(); // timestamp - QS_SIG_PRE_(e->sig); // the signal of the event - QS_2U8_PRE_(e->poolId_, e->refCtr_); // pool Id & refCtr - QS_END_NOCRIT_PRE_() - - QEvt_refCtr_dec_(e); // decrement the ref counter - - QF_CRIT_X_(); - } - // this is the last reference to this event, recycle it - else { - std::uint_fast8_t const idx = - static_cast(e->poolId_) - 1U; - - QS_BEGIN_NOCRIT_PRE_(QS_QF_GC, - static_cast(QS_EP_ID) - + static_cast(e->poolId_)) - QS_TIME_PRE_(); // timestamp - QS_SIG_PRE_(e->sig); // the signal of the event - QS_2U8_PRE_(e->poolId_, e->refCtr_); - QS_END_NOCRIT_PRE_() - - QF_CRIT_X_(); - - // pool ID must be in range - Q_ASSERT_ID(410, idx < QF::maxPool_); - - #ifdef Q_EVT_XTOR - // explicitly exectute the destructor' - // NOTE: casting 'const' away is legitimate, - // because it's a pool event - QF_CONST_CAST_(QEvt*, e)->~QEvt(); // xtor, - #endif - - #ifdef Q_SPY - // cast 'const' away, which is OK, because it's a pool event - QF_EPOOL_PUT_(QF::ePool_[idx], QF_CONST_CAST_(QEvt*, e), - static_cast(QS_EP_ID) - + static_cast(e->poolId_)); - #else - QF_EPOOL_PUT_(QF::ePool_[idx], QF_CONST_CAST_(QEvt*, e), 0U); - #endif - } - } -} - -//${QF::QF-dyn::poolGetMaxBlockSize} ......................................... -std::uint_fast16_t poolGetMaxBlockSize() noexcept { - return QF_EPOOL_EVENT_SIZE_(QF::ePool_[QF::maxPool_ - 1U]); -} - -//${QF::QF-dyn::newRef_} ..................................................... -QEvt const * newRef_( - QEvt const * const e, - QEvt const * const evtRef) noexcept -{ - //! @pre the event must be dynamic and the provided event reference - //! must not be already in use - Q_REQUIRE_ID(500, (e->poolId_ != 0U) - && (evtRef == nullptr)); - - QF_CRIT_STAT_ - QF_CRIT_E_(); - - QEvt_refCtr_inc_(e); // increments the ref counter - - QS_BEGIN_NOCRIT_PRE_(QS_QF_NEW_REF, - static_cast(QS_EP_ID) - + static_cast(e->poolId_)) - QS_TIME_PRE_(); // timestamp - QS_SIG_PRE_(e->sig); // the signal of the event - QS_2U8_PRE_(e->poolId_, e->refCtr_); // pool Id & ref Count - QS_END_NOCRIT_PRE_() - - QF_CRIT_X_(); - - return e; -} - -//${QF::QF-dyn::deleteRef_} .................................................. -void deleteRef_(QEvt const * const evtRef) noexcept { - QS_CRIT_STAT_ - QS_BEGIN_PRE_(QS_QF_DELETE_REF, - static_cast(QS_EP_ID) - + static_cast(evtRef->poolId_)) - QS_TIME_PRE_(); // timestamp - QS_SIG_PRE_(evtRef->sig); // the signal of the event - QS_2U8_PRE_(evtRef->poolId_, evtRef->refCtr_); // pool Id & ref Count - QS_END_PRE_() - - #if (QF_MAX_EPOOL > 0U) - gc(evtRef); // recycle the referenced event - #endif -} - -//${QF::QF-dyn::getPoolMin} .................................................. -std::uint_fast16_t getPoolMin(std::uint_fast8_t const poolId) noexcept { - //! @pre the poolId must be in range - Q_REQUIRE_ID(400, (QF::maxPool_ <= QF_MAX_EPOOL) - && (0U < poolId) && (poolId <= QF::maxPool_)); - QF_CRIT_STAT_ - QF_CRIT_E_(); - std::uint_fast16_t const min = static_cast( - QF::ePool_[poolId - 1U].getNMin()); - QF_CRIT_X_(); - - return min; -} - -} // namespace QF -} // namespace QP -//$enddef${QF::QF-dyn} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -#endif // (QF_MAX_EPOOL > 0U) dynamic events configured diff --git a/src/qf_mem.cpp b/src/qf_mem.cpp deleted file mode 100644 index e37f5f4..0000000 --- a/src/qf_mem.cpp +++ /dev/null @@ -1,244 +0,0 @@ -//$file${src::qf::qf_mem.cpp} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv -// -// Model: qpcpp.qm -// File: ${src::qf::qf_mem.cpp} -// -// This code has been generated by QM 5.2.5 . -// DO NOT EDIT THIS FILE MANUALLY. All your changes will be lost. -// -// This code is covered by the following QP license: -// License # : LicenseRef-QL-dual -// Issued to : Any user of the QP/C++ real-time embedded framework -// Framework(s) : qpcpp -// Support ends : 2023-12-31 -// License scope: -// -// Copyright (C) 2005 Quantum Leaps, LLC . -// -// 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: -// -// The terms of the closed source Quantum Leaps commercial licenses -// can be found at: -// -// Redistributions in source code must retain this top-level comment block. -// Plagiarizing this software to sidestep the license obligations is illegal. -// -// Contact information: -// -// -// -//$endhead${src::qf::qf_mem.cpp} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -//! @file -//! @brief QF/C++ memory management services - -#define QP_IMPL // this is QP implementation -#include "qf_port.hpp" // QF port -#include "qf_pkg.hpp" // QF package-scope interface -#include "qassert.h" // QP embedded systems-friendly assertions -#ifdef Q_SPY // QS software tracing enabled? - #include "qs_port.hpp" // QS port - #include "qs_pkg.hpp" // QS facilities for pre-defined trace records -#else - #include "qs_dummy.hpp" // disable the QS software tracing -#endif // Q_SPY - -// unnamed namespace for local definitions with internal linkage -namespace { -Q_DEFINE_THIS_MODULE("qf_mem") -} // unnamed namespace - -//$skip${QP_VERSION} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv -// Check for the minimum required QP version -#if (QP_VERSION < 700U) || (QP_VERSION != ((QP_RELEASE^4294967295U) % 0x3E8U)) -#error qpcpp version 7.0.0 or higher required -#endif -//$endskip${QP_VERSION} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -//$define${QF::QMPool} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv -namespace QP { - -//${QF::QMPool} .............................................................. - -//${QF::QMPool::QMPool} ...................................................... -QMPool::QMPool() - : m_start(nullptr), - m_end(nullptr), - m_free_head(nullptr), - m_blockSize(0U), - m_nTot(0U), - m_nFree(0U), - m_nMin(0U) -{} - -//${QF::QMPool::init} ........................................................ -void QMPool::init( - void * const poolSto, - std::uint_fast32_t poolSize, - std::uint_fast16_t blockSize) noexcept -{ - //! @pre The memory block must be valid and - //! the poolSize must fit at least one free block and - //! the blockSize must not be too close to the top of the dynamic range - Q_REQUIRE_ID(100, (poolSto != nullptr) - && (poolSize >= static_cast(sizeof(QFreeBlock))) - && (static_cast(blockSize + sizeof(QFreeBlock)) - > blockSize)); - - m_free_head = poolSto; - - // round up the blockSize to fit an integer number of pointers... - //start with one - m_blockSize = static_cast(sizeof(QFreeBlock)); - - //# free blocks in a memory block - std::uint_fast16_t nblocks = 1U; - while (m_blockSize < static_cast(blockSize)) { - m_blockSize += static_cast(sizeof(QFreeBlock)); - ++nblocks; - } - // use rounded-up value - blockSize = static_cast(m_blockSize); - - // the whole pool buffer must fit at least one rounded-up block - Q_ASSERT_ID(110, poolSize >= blockSize); - - // chain all blocks together in a free-list... - - // don't count the last block - poolSize -= static_cast(blockSize); - m_nTot = 1U; // one (the last) block in the pool - - // start at the head of the free list - QFreeBlock *fb = static_cast(m_free_head); - - // chain all blocks together in a free-list... - while (poolSize >= blockSize) { - fb->m_next = &fb[nblocks]; // setup the next link - fb = fb->m_next; // advance to next block - // reduce the available pool size - poolSize -= static_cast(blockSize); - ++m_nTot; // increment the number of blocks so far - } - - fb->m_next = nullptr; // the last link points to NULL - m_nFree = m_nTot; // all blocks are free - m_nMin = m_nTot; // the minimum number of free blocks - m_start = poolSto; // the original start this pool buffer - m_end = fb; // the last block in this pool -} - -//${QF::QMPool::get} ......................................................... -void * QMPool::get( - std::uint_fast16_t const margin, - std::uint_fast8_t const qs_id) noexcept -{ - Q_UNUSED_PAR(qs_id); // when Q_SPY not defined - - QF_CRIT_STAT_ - QF_CRIT_E_(); - - // have the than margin? - QFreeBlock *fb; - if (m_nFree > static_cast(margin)) { - fb = static_cast(m_free_head); // get a free block - - // the pool has some free blocks, so a free block must be available - Q_ASSERT_CRIT_(310, fb != nullptr); - - void * const fb_next = fb->m_next; - - // is the pool becoming empty? - m_nFree = (m_nFree - 1U); // one free block less - if (m_nFree == 0U) { - // pool is becoming empty, so the next free block must be NULL - Q_ASSERT_CRIT_(320, fb_next == nullptr); - - m_nMin = 0U;// remember that 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. - //! - //! @tr{PQP18_3} - - // 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_CRIT_(330, QF_PTR_RANGE_(fb_next, m_start, m_end)); - - // is the number of free blocks the new minimum so far? - if (m_nMin > m_nFree) { - m_nMin = m_nFree; // remember the minimum so far - } - } - - m_free_head = fb_next; // set the head to the next free block - - QS_BEGIN_NOCRIT_PRE_(QS_QF_MPOOL_GET, qs_id) - QS_TIME_PRE_(); // timestamp - QS_OBJ_PRE_(this); // this memory pool - QS_MPC_PRE_(m_nFree); // # of free blocks in the pool - QS_MPC_PRE_(m_nMin); // min # free blocks ever in the pool - QS_END_NOCRIT_PRE_() - } - // don't have enough free blocks at this point - else { - fb = nullptr; - - QS_BEGIN_NOCRIT_PRE_(QS_QF_MPOOL_GET_ATTEMPT, qs_id) - QS_TIME_PRE_(); // timestamp - QS_OBJ_PRE_(m_start); // the memory managed by this pool - QS_MPC_PRE_(m_nFree); // the # free blocks in the pool - QS_MPC_PRE_(margin); // the requested margin - QS_END_NOCRIT_PRE_() - } - QF_CRIT_X_(); - - return fb; // return the block or NULL pointer to the caller - -} - -//${QF::QMPool::put} ......................................................... -void QMPool::put( - void * const b, - std::uint_fast8_t const qs_id) noexcept -{ - Q_UNUSED_PAR(qs_id); // when Q_SPY not defined - - //! @pre # free blocks cannot exceed the total # blocks and - //! the block pointer must be in range to come from this pool. - //! - Q_REQUIRE_ID(200, (m_nFree < m_nTot) - && QF_PTR_RANGE_(b, m_start, m_end)); - QF_CRIT_STAT_ - QF_CRIT_E_(); - static_cast(b)->m_next = - static_cast(m_free_head); // link into the free list - m_free_head = b; // set as new head of the free list - m_nFree = (m_nFree + 1U); // one more free block in this pool - - QS_BEGIN_NOCRIT_PRE_(QS_QF_MPOOL_PUT, qs_id) - QS_TIME_PRE_(); // timestamp - QS_OBJ_PRE_(this); // this memory pool - QS_MPC_PRE_(m_nFree); // the number of free blocks in the pool - QS_END_NOCRIT_PRE_() - - QF_CRIT_X_(); -} - -//${QF::QMPool::getBlockSize} ................................................ -QMPoolSize QMPool::getBlockSize() const noexcept { - return m_blockSize; -} - -} // namespace QP -//$enddef${QF::QMPool} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/src/qf_pkg.hpp b/src/qf_pkg.hpp deleted file mode 100644 index b2aada2..0000000 --- a/src/qf_pkg.hpp +++ /dev/null @@ -1,203 +0,0 @@ -//$file${include::qf_pkg.hpp} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv -// -// Model: qpcpp.qm -// File: ${include::qf_pkg.hpp} -// -// This code has been generated by QM 5.2.5 . -// DO NOT EDIT THIS FILE MANUALLY. All your changes will be lost. -// -// This code is covered by the following QP license: -// License # : LicenseRef-QL-dual -// Issued to : Any user of the QP/C++ real-time embedded framework -// Framework(s) : qpcpp -// Support ends : 2023-12-31 -// License scope: -// -// Copyright (C) 2005 Quantum Leaps, LLC . -// -// 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: -// -// The terms of the closed source Quantum Leaps commercial licenses -// can be found at: -// -// Redistributions in source code must retain this top-level comment block. -// Plagiarizing this software to sidestep the license obligations is illegal. -// -// Contact information: -// -// -// -//$endhead${include::qf_pkg.hpp} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -//! @file -//! @brief Internal (package scope) QF/C++ interface. - -#ifndef QF_PKG_HPP_ -#define QF_PKG_HPP_ - -//============================================================================ -//! helper macro to cast const away from an event pointer -#define QF_CONST_CAST_(type_, ptr_) const_cast(ptr_) - -//$declare${QF::QF-pkg} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv -namespace QP { -namespace QF { - -//${QF::QF-pkg::readySet_} ................................................... -//! "Ready-set" of all threads used in the built-in kernels -extern QPSet readySet_; - -//${QF::QF-pkg::ePool_[QF_MAX_EPOOL]} ........................................ -#if (QF_MAX_EPOOL > 0U) -//! event pools managed by QF -extern QF_EPOOL_TYPE_ ePool_[QF_MAX_EPOOL]; -#endif // (QF_MAX_EPOOL > 0U) - -//${QF::QF-pkg::maxPool_} .................................................... -//! number of initialized event pools -extern std::uint_fast8_t maxPool_; - -//${QF::QF-pkg::bzero} ....................................................... -//! Clear a specified region of memory to zero -//! -//! @details -//! Clears a memory buffer by writing zeros byte-by-byte. -//! -//! @param[in] start pointer to the beginning of a memory buffer. -//! @param[in] len length of the memory buffer to clear (in bytes) -//! -//! @note The main application of this function is clearing the internal -//! QF variables upon startup. This is done to avoid problems with -//! non-standard startup code provided with some compilers and toolchains -//! (e.g., TI DSPs or Microchip MPLAB), which does not zero the -//! uninitialized variables, as required by the C++ standard. -void bzero( - void * const start, - std::uint_fast16_t const len) noexcept; - -} // namespace QF -} // namespace QP -//$enddecl${QF::QF-pkg} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -//============================================================================ -namespace QP { - -//............................................................................ -//! Structure representing a free block in the Native QF Memory Pool -//! @sa QP::QMPool -struct QFreeBlock { - QFreeBlock * volatile m_next; //!< link to the next free block -}; - -//............................................................................ -// The following flags and flags and bitmasks are for the fields of the -// `QEvt.refCtr_` attribute of the QP::QTimeEvt class (subclass of QP::QEvt). -// This attribute is NOT used for reference counting in time events -// because the `QEvt.poolId_` attribute is zero ("static event"). -// -constexpr std::uint8_t TE_IS_LINKED = 1U << 7U; // flag -constexpr std::uint8_t TE_WAS_DISARMED = 1U << 6U; // flag -constexpr std::uint8_t TE_TICK_RATE = 0x0FU; // bitmask - -// internal helper inline functions - -//! increment the refCtr_ of an event `e` -inline void QEvt_refCtr_inc_(QEvt const * const e) noexcept { - (QF_CONST_CAST_(QEvt*, e))->refCtr_ = e->refCtr_ + 1U; -} - -//! decrement the refCtr_ of an event `e` -inline void QEvt_refCtr_dec_(QEvt const * const e) noexcept { - (QF_CONST_CAST_(QEvt*, e))->refCtr_ = e->refCtr_ - 1U; -} - -} // namespace QP - -//============================================================================ -// QF-specific critical section... -#ifndef QF_CRIT_STAT_TYPE - //! This is an internal macro for defining the critical section - //! status type. - //! - //! @details - //! The purpose of this macro is to enable writing the same code for the - //! case when critical section status type is defined and when it is not. - //! If the macro #QF_CRIT_STAT_TYPE is defined, this internal macro - //! provides the definition of the critical section status variable. - //! Otherwise this macro is empty. - //! @sa #QF_CRIT_STAT_TYPE - #define QF_CRIT_STAT_ - - //! This is an internal macro for entering a critical section. - //! - //! @details - //! The purpose of this macro is to enable writing the same code for the - //! case when critical section status type is defined and when it is not. - //! If the macro #QF_CRIT_STAT_TYPE is defined, this internal macro - //! invokes QF_CRIT_ENTRY() passing the key variable as the parameter. - //! Otherwise QF_CRIT_ENTRY() is invoked with a dummy parameter. - //! @sa QF_CRIT_ENTRY() - #define QF_CRIT_E_() QF_CRIT_ENTRY(dummy) - - //! This is an internal macro for exiting a critical section. - //! - //! @details - //! The purpose of this macro is to enable writing the same code for the - //! case when critical section status type is defined and when it is not. - //! If the macro #QF_CRIT_STAT_TYPE is defined, this internal macro - //! invokes QF_CRIT_EXIT() passing the key variable as the parameter. - //! Otherwise QF_CRIT_EXIT() is invoked with a dummy parameter. - //! @sa QF_CRIT_EXIT() - //! - #define QF_CRIT_X_() QF_CRIT_EXIT(dummy) - -#elif (!defined QF_CRIT_STAT_) - #define QF_CRIT_STAT_ QF_CRIT_STAT_TYPE critStat_; - #define QF_CRIT_E_() QF_CRIT_ENTRY(critStat_) - #define QF_CRIT_X_() QF_CRIT_EXIT(critStat_) -#endif // QF_CRIT_STAT_TYPE - -// Assertions inside the critical section ------------------------------------ -#ifdef Q_NASSERT // Q_NASSERT defined--assertion checking disabled - - #define Q_ASSERT_CRIT_(id_, test_) static_cast(0) - #define Q_REQUIRE_CRIT_(id_, test_) static_cast(0) - #define Q_ERROR_CRIT_(id_) static_cast(0) - -#else // Q_NASSERT not defined--assertion checking enabled - - #define Q_ASSERT_CRIT_(id_, test_) do { \ - if ((test_)) {} else { \ - QF_CRIT_X_(); \ - Q_onAssert(&Q_this_module_[0], static_cast(id_)); \ - } \ - } while (false) - - #define Q_REQUIRE_CRIT_(id_, test_) Q_ASSERT_CRIT_((id_), (test_)) - - #define Q_ERROR_CRIT_(id_) do { \ - QF_CRIT_X_(); \ - Q_onAssert(&Q_this_module_[0], static_cast(id_)); \ - } while (false) - -#endif // Q_NASSERT - -//! helper macro to test that a pointer `x_` is in range between -//! `min_` and `max_` -//! -//! @details -//! This macro is specifically and exclusively used for checking the range -//! of a block pointer returned to the pool. Such a check must rely on the -//! pointer arithmetic not compliant with the [AUTOSAR Rule M5-0-18]. -//! Defining a specific macro for this purpose allows to selectively -//! disable the warnings for this particular case. -#define QF_PTR_RANGE_(x_, min_, max_) (((min_) <= (x_)) && ((x_) <= (max_))) - -#endif // QF_PKG_HPP_ diff --git a/src/qf_port.cpp b/src/qf_port.cpp deleted file mode 100644 index 78e2ef6..0000000 --- a/src/qf_port.cpp +++ /dev/null @@ -1,623 +0,0 @@ -/** -* @file -* @brief "Experimental" QF/CPP port to Espressif ESP-IDF (version 4.x) -* @ingroup ports -* @cond -****************************************************************************** -* Last updated for version 6.9.4 -* Last updated on 2022-03-20 -* Copyright (C) 2022 Victor Chavez -* -* Q u a n t u m L e a P s -* ------------------------ -* Modern Embedded Software -* -* Copyright (C) 2005-2022 Quantum Leaps, LLC. All rights reserved. -* This program is open source software: you can redistribute it and/or -* modify it under the terms of the GNU General Public License as published -* by the Free Software Foundation, either version 3 of the License, or -* (at your option) any later version. -* -* Alternatively, this program may be distributed and modified under the -* terms of Quantum Leaps commercial licenses, which expressly supersede -* the GNU General Public License and are specifically designed for -* licensees interested in retaining the proprietary status of their code. -* -* This program is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU General Public License for more details. -* -* You should have received a copy of the GNU General Public License -* along with this program. If not, see . -* -* Contact information: -* -* -****************************************************************************** -* @endcond -*/ -#define QP_IMPL /* this is QP implementation */ -#include "qf_port.hpp" /* QF port */ -#include "qf_pkg.hpp" -#include "qassert.h" -#ifdef Q_SPY /* QS software tracing enabled? */ - #include "qs_port.hpp" /* QS port */ - #include "qs_pkg.hpp" /* QS package-scope internal interface */ -#else - #include "qs_dummy.hpp" /* disable the QS software tracing */ -#endif /* Q_SPY */ - -#include "esp_log.h" -#include "esp_freertos_hooks.h" -#include "freertos/task.h" - -#if ( configSUPPORT_STATIC_ALLOCATION == 0 ) -#error "This QP/C++ port to FreeRTOS requires configSUPPORT_STATIC_ALLOCATION" -#endif - -#if ( configMAX_PRIORITIES < QF_MAX_ACTIVE ) - #error "FreeRTOS configMAX_PRIORITIES must not be less than QF_MAX_ACTIVE" -#endif - - -/* Global objects ----------------------------------------------------------*/ -PRIVILEGED_DATA portMUX_TYPE QF_esp32mux = portMUX_INITIALIZER_UNLOCKED; - -// namespace QP ============================================================== -namespace QP { - -Q_DEFINE_THIS_MODULE("qf_port") - -/* Local objects -----------------------------------------------------------*/ -static void task_function(void *pvParameters); /* FreeRTOS task signature */ -/*==========================================================================*/ - - -void QF::init(void) { - -} -/*..........................................................................*/ - -int_t QF::run(void) { - onStartup(); - // produce the QS_QF_RUN trace record - QS_CRIT_STAT_ - QS_BEGIN_PRE_(QS_QF_RUN, 0U) - QS_END_PRE_() - return 0; /* dummy return to make the compiler happy */ -} -/*..........................................................................*/ -void QF::stop(void) { - onCleanup(); /* cleanup callback */ -} -/*..........................................................................*/ -void QActive::start(QPrioSpec const prioSpec, - QEvt const * * const qSto, std::uint_fast16_t const qLen, - void * const stkSto, std::uint_fast16_t const stkSize, - void const * const par) -{ - Q_REQUIRE_ID(200, - (qSto != nullptr) /* queue storage */ - && (qLen > 0U) /* queue size */ - //&& (stkSto != nullptr) /* stack storage */ - && (stkSize > 0U)); // stack size - - /* create the event queue for the AO */ - m_eQueue.init(qSto, qLen); - - m_prio = static_cast(prioSpec & 0xFFU); // QF-priority - register_(); // make QF aware of this AO - init(par, m_prio); /* the top-most initial tran. */ - QS_FLUSH(); /* flush the QS trace buffer to the host */ - - /* task name provided by the user in QF_setTaskName() or default name */ - char const *taskName = (m_thread != nullptr) - ? static_cast(m_thread) - : static_cast("AO"); - - /* statically create the FreeRTOS task for the AO */ - BaseType_t task_res = xTaskCreatePinnedToCore( - &task_function, /* the task function */ - taskName , /* the name of the task */ - stkSize/sizeof(portSTACK_TYPE), /* stack size */ - this, /* the 'pvParameters' parameter */ - m_prio + tskIDLE_PRIORITY, /* FreeRTOS priority */ - //static_cast(stkSto), /* stack storage */ - &m_thread, /* task buffer */ - static_cast(QP_CPU_NUM)); /* CPU number */ - - Q_ENSURE_ID(210, task_res == pdPASS ); /* must be created */ -} -/*..........................................................................*/ -void QActive::setAttr(std::uint32_t attr1, void const *attr2) { - /* this function must be called before Qregistry_START(), - * which implies that me->thread.pxDummy1 must not be used yet; - */ - Q_REQUIRE_ID(300, m_thread == nullptr); - switch (attr1) { - case TASK_NAME_ATTR: - /* temporarily store the name */ - m_thread = const_cast(attr2); /* cast 'const' away */ - break; - /* ... */ - } -} - -// thread for active objects ------------------------------------------------- -void QActive::thread_(QActive *act) { - /* event-loop */ - for (;;) { /* for-ever */ - QEvt const *e = act->get_(); // wait for event - act->dispatch(e, act->m_prio); // dispatch to the AO's state machine - QF::gc(e); // check if the event is garbage, and collect it if so - } -} -/*..........................................................................*/ -static void task_function(void *pvParameters) { /* FreeRTOS task signature */ - QP::QActive::thread_(reinterpret_cast(pvParameters)); -} - -/*==========================================================================*/ -/* The "FromISR" QP APIs for the FreeRTOS port... */ -bool QActive::postFromISR_(QEvt const * const e, - std::uint_fast16_t const margin, void *par, - void const * const sender) noexcept -{ - QEQueueCtr nFree; /* temporary to avoid UB for volatile access */ - bool status; - - /** @pre event pointer must be valid */ - Q_REQUIRE_ID(400, e != nullptr); - - portENTER_CRITICAL_ISR(&QF_esp32mux); - nFree = m_eQueue.m_nFree; /* get volatile into the temporary */ - if (margin == QF::NO_MARGIN) { - if (nFree > 0U) { - status = true; /* can post */ - } - else { - status = false; /* cannot post */ - Q_ERROR_ID(810); /* must be able to post the event */ - } - } - else if (nFree > static_cast(margin)) { - status = true; /* can post */ - } - else { - status = false; /* cannot post */ - } - - if (status) { /* can post the event? */ - QS_BEGIN_NOCRIT_PRE_(QS_QF_ACTIVE_POST, m_prio) - QS_TIME_PRE_(); /* timestamp */ - QS_OBJ_PRE_(sender); /* the sender object */ - QS_SIG_PRE_(e->sig); /* the signal of the event */ - QS_OBJ_PRE_(this); /* this active object (recipient) */ - QS_2U8_PRE_(e->poolId_, e->refCtr_); /* pool Id & ref Count */ - QS_EQC_PRE_(nFree); /* number of free entries */ - QS_EQC_PRE_(m_eQueue.m_nMin); /* min number of free entries */ - QS_END_NOCRIT_PRE_() - - /* is it a pool event? */ - if (e->poolId_ != 0U) { - QEvt_refCtr_inc_(e); /* increment the reference counter */ - } - --nFree; /* one free entry just used up */ - m_eQueue.m_nFree = nFree; /* update the volatile */ - if (m_eQueue.m_nMin > nFree) { - m_eQueue.m_nMin = nFree; /* update minimum so far */ - } - - /* empty queue? */ - if (m_eQueue.m_frontEvt == nullptr) { - m_eQueue.m_frontEvt = e; /* deliver event directly */ - portEXIT_CRITICAL_ISR(&QF_esp32mux); - /* signal the event queue */ - vTaskNotifyGiveFromISR(static_cast(m_thread), - static_cast(par)); - } - /* queue is not empty, insert event into the ring-buffer */ - else { - /* insert event into the ring buffer (FIFO) */ - m_eQueue.m_ring[m_eQueue.m_head] = e; - if (m_eQueue.m_head == 0U) { /* need to wrap head? */ - m_eQueue.m_head = m_eQueue.m_end; /* wrap around */ - } - --m_eQueue.m_head; /* advance the head (counter clockwise) */ - portEXIT_CRITICAL_ISR(&QF_esp32mux); - } - } - else { - - QS_BEGIN_NOCRIT_PRE_(QS_QF_ACTIVE_POST_ATTEMPT, m_prio) - QS_TIME_PRE_(); /* timestamp */ - QS_OBJ_PRE_(sender); /* the sender object */ - QS_SIG_PRE_(e->sig); /* the signal of the event */ - QS_OBJ_PRE_(this); /* this active object (recipient) */ - QS_2U8_PRE_(e->poolId_, e->refCtr_); /* pool Id & ref Count */ - QS_EQC_PRE_(nFree); /* number of free entries */ - QS_EQC_PRE_(margin); /* margin requested */ - QS_END_NOCRIT_PRE_() - - portEXIT_CRITICAL_ISR(&QF_esp32mux); - - QF::gcFromISR(e); // recycle the event to avoid a leak - } - - return status; -} - -/*..........................................................................*/ -void QActive::publishFromISR_(QEvt const *e, void *par, - void const * const sender) noexcept -{ - QPSet subscrList; /* local, modifiable copy of the subscriber list */ - - /** @pre the published signal must be within the configured range */ - Q_REQUIRE_ID(500, static_cast(e->sig) < QActive::maxPubSignal_); - - portENTER_CRITICAL_ISR(&QF_esp32mux); - - QS_BEGIN_NOCRIT_PRE_(QS_QF_PUBLISH, 0U) - QS_TIME_PRE_(); /* the timestamp */ - QS_OBJ_PRE_(sender); /* the sender object */ - QS_SIG_PRE_(e->sig); /* the signal of the event */ - QS_2U8_PRE_(e->poolId_, e->refCtr_);/* pool-Id & ref-Count */ - QS_END_NOCRIT_PRE_() - - /* is it a dynamic event? */ - if (e->poolId_ != 0U) { - /* NOTE: The reference counter of a dynamic 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_gcFromISR()) 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 list */ - subscrList = QActive::subscrList_[e->sig]; - portEXIT_CRITICAL_ISR(&QF_esp32mux); - - if (subscrList.notEmpty()) { /* any subscribers? */ - // the highest-prio subscriber - std::uint_fast8_t p = subscrList.findMax(); - - /* no need to lock the scheduler in the ISR context */ - do { /* loop over all subscribers */ - /* the prio of the AO must be registered with the framework */ - Q_ASSERT_ID(510, registry_[p] != nullptr); - - // POST_FROM_ISR() asserts internally if the queue overflows - registry_[p]->POST_FROM_ISR(e, par, sender); - - subscrList.remove(p); // remove the handled subscriber - if (subscrList.notEmpty()) { // still more subscribers? - p = subscrList.findMax(); // the highest-prio subscriber - } - else { - p = 0U; /* no more subscribers */ - } - } while (p != 0U); - /* no need to unlock the scheduler in the ISR context */ - } - - /* 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. - */ - QF::gcFromISR(e); -} - -/*..........................................................................*/ -void QTimeEvt::tickFromISR_(std::uint_fast8_t const tickRate, void *par, - void const * const sender) noexcept -{ - QTimeEvt *prev = &timeEvtHead_[tickRate]; - - portENTER_CRITICAL_ISR(&QF_esp32mux); - - QS_BEGIN_NOCRIT_PRE_(QS_QF_TICK, 0U) - ++prev->m_ctr; - QS_TEC_PRE_(prev->m_ctr); /* tick ctr */ - QS_U8_PRE_(tickRate); /* tick rate */ - QS_END_NOCRIT_PRE_() - - /* scan the linked-list of time events at this rate... */ - for (;;) { - QTimeEvt *t = prev->m_next; /* advance down the time evt. list */ - - /* end of the list? */ - if (t == nullptr) { - - /* any new time events armed since the last run of QF_tickX_()? */ - if (timeEvtHead_[tickRate].m_act != nullptr) { - - /* sanity check */ - Q_ASSERT_ID(610, prev != nullptr); - prev->m_next = timeEvtHead_[tickRate].toTimeEvt(); - timeEvtHead_[tickRate].m_act = nullptr; - t = prev->m_next; /* switch to the new list */ - } - else { - break; /* all currently armed time evts. processed */ - } - } - - /* time event scheduled for removal? */ - if (t->m_ctr == 0U) { - prev->m_next = t->m_next; - /* mark time event 't' as NOT linked */ - t->refCtr_ &= static_cast(~TE_IS_LINKED); - /* do NOT advance the prev pointer */ - /* exit crit. section to reduce latency */ - portEXIT_CRITICAL_ISR(&QF_esp32mux); - } - else { - --t->m_ctr; - - /* is time event about to expire? */ - if (t->m_ctr == 0U) { - QActive *act = t->toActive(); // temp. for volatile - - /* periodic time evt? */ - if (t->m_interval != 0U) { - t->m_ctr = t->m_interval; /* rearm the time event */ - prev = t; /* advance to this time event */ - } - /* one-shot time event: automatically disarm */ - else { - prev->m_next = t->m_next; - /* mark time event 't' as NOT linked */ - t->refCtr_ &= static_cast(~TE_IS_LINKED); - /* do NOT advance the prev pointer */ - - QS_BEGIN_NOCRIT_PRE_(QS_QF_TIMEEVT_AUTO_DISARM, - act->m_prio) - QS_OBJ_PRE_(t); /* this time event object */ - QS_OBJ_PRE_(act); /* the target AO */ - QS_U8_PRE_(tickRate); /* tick rate */ - QS_END_NOCRIT_PRE_() - } - - QS_BEGIN_NOCRIT_PRE_(QS_QF_TIMEEVT_POST, act->m_prio) - QS_TIME_PRE_(); /* timestamp */ - QS_OBJ_PRE_(t); /* the time event object */ - QS_SIG_PRE_(t->sig); /* signal of this time event */ - QS_OBJ_PRE_(act); /* the target AO */ - QS_U8_PRE_(tickRate); /* tick rate */ - QS_END_NOCRIT_PRE_() - - /* exit critical section before posting */ - portEXIT_CRITICAL_ISR(&QF_esp32mux); - - // asserts if queue overflows - (void)act->POST_FROM_ISR(t, par, sender); - } - else { - prev = t; /* advance to this time event */ - /* exit crit. section to reduce latency */ - portEXIT_CRITICAL_ISR(&QF_esp32mux); - } - } - /* re-enter crit. section to continue */ - portENTER_CRITICAL_ISR(&QF_esp32mux); - } - portEXIT_CRITICAL_ISR(&QF_esp32mux); -} - - -/*..........................................................................*/ -QEvt *QF::newXfromISR_(std::uint_fast16_t const evtSize, - std::uint_fast16_t const margin, - enum_t const sig) noexcept -{ - QEvt *e; - std::uint_fast8_t idx; - - /* find the pool index that fits the requested event size ... */ - for (idx = 0U; idx < QF::maxPool_; ++idx) { - if (evtSize <= QF_EPOOL_EVENT_SIZE_(QF::ePool_[idx])) { - break; - } - } - /* cannot run out of registered pools */ - Q_ASSERT_ID(710, idx < QF::maxPool_); - - - /* get e -- platform-dependent */ -#ifdef Q_SPY - e = static_cast( - QF::ePool_[idx].getFromISR(((margin != QF::NO_MARGIN) - ? margin : 0U), - static_cast(QS_EP_ID) + idx + 1U)); -#else - e = static_cast( - QF::ePool_[idx].getFromISR(((margin != QF::NO_MARGIN) - ? margin : 0U), 0U)); -#endif - - /* was e allocated correctly? */ - if (e != nullptr) { - e->sig = static_cast(sig); // set the signal - e->poolId_ = static_cast(idx + 1U); /* store the pool ID */ - e->refCtr_ = 0U; /* set the reference counter to 0 */ - -#ifdef Q_SPY - portENTER_CRITICAL_ISR(&QF_esp32mux); - QS_BEGIN_PRE_(QS_QF_NEW, static_cast(QS_EP_ID) + idx + 1U) - QS_TIME_PRE_(); /* timestamp */ - QS_EVS_PRE_(evtSize); /* the size of the event */ - QS_SIG_PRE_(sig); /* the signal of the event */ - QS_END_NOCRIT_PRE_() - portEXIT_CRITICAL_ISR(&QF_esp32mux); -#endif /* Q_SPY */ - } - /* event cannot be allocated */ - else { - /* must tolerate bad alloc. */ - Q_ASSERT_ID(720, margin != QF::NO_MARGIN); - -#ifdef Q_SPY - portENTER_CRITICAL_ISR(&QF_esp32mux); - QS_BEGIN_PRE_(QS_QF_NEW_ATTEMPT, static_cast(QS_EP_ID) + idx + 1U) - QS_TIME_PRE_(); /* timestamp */ - QS_EVS_PRE_(evtSize); /* the size of the event */ - QS_SIG_PRE_(sig); /* the signal of the event */ - QS_END_NOCRIT_PRE_() - portEXIT_CRITICAL_ISR(&QF_esp32mux); -#endif /* Q_SPY */ - } - return e; /* can't be NULL if we can't tolerate bad allocation */ -} -/*..........................................................................*/ -void QF::gcFromISR(QEvt const * const e) noexcept { - - /* is it a dynamic event? */ - if (e->poolId_ != 0U) { - portENTER_CRITICAL_ISR(&QF_esp32mux); - - /* isn't this the last ref? */ - if (e->refCtr_ > 1U) { - QEvt_refCtr_dec_(e); /* decrements the ref counter */ - - QS_BEGIN_NOCRIT_PRE_(QS_QF_GC_ATTEMPT, - static_cast(e->poolId_)) - QS_TIME_PRE_(); /* timestamp */ - QS_SIG_PRE_(e->sig); /* the signal of the event */ - QS_2U8_PRE_(e->poolId_, e->refCtr_); /* pool Id & ref Count */ - QS_END_NOCRIT_PRE_() - - portEXIT_CRITICAL_ISR(&QF_esp32mux); - } - /* this is the last reference to this event, recycle it */ - else { - std::uint_fast8_t idx = - static_cast(e->poolId_) - 1U; - - QS_BEGIN_NOCRIT_PRE_(QS_QF_GC, - static_cast(e->poolId_)) - QS_TIME_PRE_(); /* timestamp */ - QS_SIG_PRE_(e->sig); /* the signal of the event */ - QS_2U8_PRE_(e->poolId_, e->refCtr_); /* pool Id & ref Count */ - QS_END_NOCRIT_PRE_() - - portEXIT_CRITICAL_ISR(&QF_esp32mux); - - /* pool ID must be in range */ - Q_ASSERT_ID(810, idx < QF::maxPool_); -#ifdef Q_EVT_VIRTUAL - // explicitly exectute the destructor' - // NOTE: casting 'const' away is legitimate, - // because it's a pool event - QF_EVT_CONST_CAST_(e)->~QEvt(); // xtor, -#endif -#ifdef Q_SPY - // cast 'const' away, which is OK, because it's a pool event - QF::ePool_[idx].putFromISR(QF_CONST_CAST_(QEvt*, e), - static_cast(QS_EP_ID) + e->poolId_); -#else - QF::ePool_[idx].putFromISR(QF_CONST_CAST_(QEvt*, e), 0U); -#endif - } - } -} - - -/*..........................................................................*/ -void QMPool::putFromISR(void *b, std::uint_fast8_t const qs_id) noexcept { - - /** @pre # free blocks cannot exceed the total # blocks and - * the block pointer must be from this pool. - */ - Q_REQUIRE_ID(900, (m_nFree < m_nTot) - && QF_PTR_RANGE_(b, m_start, m_end)); - - (void)qs_id; /* unused parameter (outside Q_SPY build configuration) */ - portENTER_CRITICAL_ISR(&QF_esp32mux); - - static_cast(b)->m_next = - static_cast(m_free_head); // link into the free list - m_free_head = b; /* set as new head of the free list */ - ++m_nFree; /* one more free block in this pool */ - - QS_BEGIN_NOCRIT_PRE_(QS_QF_MPOOL_PUT, qs_id) - QS_TIME_PRE_(); /* timestamp */ - QS_OBJ_PRE_(m_start); /* the memory managed by this pool */ - QS_MPC_PRE_(m_nFree); /* # free blocks in the pool */ - QS_END_NOCRIT_PRE_() - - portEXIT_CRITICAL_ISR(&QF_esp32mux); -} -/*..........................................................................*/ -void *QMPool::getFromISR(std::uint_fast16_t const margin, - std::uint_fast8_t const qs_id) noexcept { - QFreeBlock *fb; - - (void)qs_id; /* unused parameter (outside Q_SPY build configuration) */ - portENTER_CRITICAL_ISR(&QF_esp32mux); - - /* have more free blocks than the requested margin? */ - if (m_nFree > static_cast(margin)) { - - fb = static_cast(m_free_head); // get a free block - - /* the pool has some free blocks, so a free block must be available */ - Q_ASSERT_ID(910, fb != nullptr); - - void *fb_next = fb->m_next; /* put volatile to a temporary to avoid UB */ - - /* is the pool becoming empty? */ - --m_nFree; /* one less free block */ - if (m_nFree == 0) { - /* pool is becoming empty, so the next free block must be NULL */ - Q_ASSERT_ID(920, fb_next == nullptr); - - m_nMin = 0U; /* remember that the pool got empty */ - } - else { - /* pool is not empty, 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_ID(930, QF_PTR_RANGE_(fb_next, m_start, m_end)); - - /* is the number of free blocks the new minimum so far? */ - if (m_nMin > m_nFree) { - m_nMin = m_nFree; /* remember the new minimum */ - } - } - - m_free_head = fb_next; /* set the head to the next free block */ - - QS_BEGIN_NOCRIT_PRE_(QS_QF_MPOOL_GET, qs_id) - QS_TIME_PRE_(); /* timestamp */ - QS_OBJ_PRE_(m_start); /* the memory managed by this pool */ - QS_MPC_PRE_(m_nFree); /* # free blocks in the pool */ - QS_MPC_PRE_(m_nMin); /* min # free blocks ever in the pool */ - QS_END_NOCRIT_PRE_() - - } - /* don't have enough free blocks at this point */ - else { - fb = nullptr; - - QS_BEGIN_NOCRIT_PRE_(QS_QF_MPOOL_GET_ATTEMPT, qs_id) - QS_TIME_PRE_(); /* timestamp */ - QS_OBJ_PRE_(m_start); /* the memory managed by this pool */ - QS_MPC_PRE_(m_nFree); /* # free blocks in the pool */ - QS_MPC_PRE_(margin); /* the requested margin */ - QS_END_NOCRIT_PRE_() - } - portEXIT_CRITICAL_ISR(&QF_esp32mux); - - return fb; /* return the pointer to memory block or NULL to the caller */ -} - - -} // namespace QP diff --git a/src/qf_port.hpp b/src/qf_port.hpp deleted file mode 100644 index ff1de65..0000000 --- a/src/qf_port.hpp +++ /dev/null @@ -1,240 +0,0 @@ -/** -* @file -* @brief "Experimental" QF/CPP port to Espressif ESP-IDF (version 4.x) -* @ingroup ports -* @cond -****************************************************************************** -* Last updated for version 6.9.4 -* Last updated on 2022-03-20 -* Copyright (C) 2022 Victor Chavez -* -* Q u a n t u m L e a P s -* ------------------------ -* Modern Embedded Software -* -* Copyright (C) 2005-2021 Quantum Leaps, LLC. All rights reserved. -* This program is open source software: you can redistribute it and/or -* modify it under the terms of the GNU General Public License as published -* by the Free Software Foundation, either version 3 of the License, or -* (at your option) any later version. -* -* Alternatively, this program may be distributed and modified under the -* terms of Quantum Leaps commercial licenses, which expressly supersede -* the GNU General Public License and are specifically designed for -* licensees interested in retaining the proprietary status of their code. -* -* This program is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU General Public License for more details. -* -* You should have received a copy of the GNU General Public License -* along with this program. If not, see . -* -* Contact information: -* -* -****************************************************************************** -* @endcond -*/ -#ifndef QF_PORT_HPP -#define QF_PORT_HPP - -// Select the CPU at which the QP Framework will be attached -#define CONFIG_QP_PINNED_TO_CORE_0 -//#define CONFIG_QP_PINNED_TO_CORE_1 - -// Activate the QF ISR API required for FreeRTOS -#define QF_ISR_API 1 - -/* FreeRTOS-ESP32 event queue and thread types, see NOTE0 */ -#define QF_EQUEUE_TYPE QEQueue -#define QF_THREAD_TYPE TaskHandle_t - -/* The maximum number of active objects in the application, see NOTE1 */ -#define QF_MAX_ACTIVE 8U - -// QF interrupt disabling/enabling (task level) -#define QF_INT_DISABLE() taskDISABLE_INTERRUPTS() -#define QF_INT_ENABLE() taskENABLE_INTERRUPTS() - -/* QF critical section for FreeRTOS-ESP32 (task level), see NOTE2 */ -/* #define QF_CRIT_STAT_TYPE not defined */ -#define QF_CRIT_ENTRY(dummy) portENTER_CRITICAL(&QF_esp32mux) -#define QF_CRIT_EXIT(dummy) portEXIT_CRITICAL(&QF_esp32mux) - -#include "freertos/FreeRTOS.h" /* FreeRTOS master include file, see NOTE4 */ -#include "freertos/task.h" /* FreeRTOS task management */ - -#include "qep_port.hpp" /* QEP port */ -#include "qequeue.hpp" /* this QP port uses the native QF event queue */ -#include "qmpool.hpp" /* this QP port uses the native QF memory pool */ -#include "qf.hpp" /* QF platform-independent public interface */ - -/* global spinlock "mutex" for all critical sections in QF (see NOTE3) */ -extern PRIVILEGED_DATA portMUX_TYPE QF_esp32mux; - -#if defined( CONFIG_QP_PINNED_TO_CORE_0 ) - #define QP_CPU_NUM PRO_CPU_NUM -#elif defined( CONFIG_QP_PINNED_TO_CORE_1 ) - #define QP_CPU_NUM APP_CPU_NUM -#else - /* Defaults to APP_CPU */ - #define QP_CPU_NUM APP_CPU_NUM -#endif - -/* the "FromISR" versions of the QF APIs, see NOTE4 */ -#ifdef Q_SPY - #define PUBLISH_FROM_ISR(e_, pxHigherPrioTaskWoken_, sender_) \ - publishFromISR_((e_), (pxHigherPrioTaskWoken_),(sender_)) - - #define POST_FROM_ISR(e_, pxHigherPrioTaskWoken_, sender_) \ - postFromISR_((e_), QP::QF::NO_MARGIN, \ - (pxHigherPrioTaskWoken_), (sender_)) - - #define POST_X_FROM_ISR(e_, margin_, pxHigherPrioTaskWoken_, sender_) \ - postFromISR_((e_), (margin_), (pxHigherPrioTaskWoken_), (sender_)) - - #define TICK_X_FROM_ISR(tickRate_, pxHigherPrioTaskWoken_, sender_) \ - tickFromISR_((tickRate_), (pxHigherPrioTaskWoken_), (sender_)) -#else - #define PUBLISH_FROM_ISR(e_, pxHigherPrioTaskWoken_, dummy) \ - publishFromISR_((e_), (pxHigherPrioTaskWoken_)) - - #define POST_FROM_ISR(e_, pxHigherPrioTaskWoken_, dummy) \ - postFromISR_((e_), QP::QF::NO_MARGIN, (pxHigherPrioTaskWoken_)) - - #define POST_X_FROM_ISR(me_, e_, margin_, \ - pxHigherPrioTaskWoken_, dummy) \ - postFromISR_((e_), (margin_), (pxHigherPrioTaskWoken_)) - - #define TICK_X_FROM_ISR(tickRate_, pxHigherPrioTaskWoken_, dummy) \ - tickFromISR_((tickRate_), (pxHigherPrioTaskWoken_)) -#endif - -#define TICK_FROM_ISR(pxHigherPrioTaskWoken_, sender_) \ - TICK_X_FROM_ISR(0U, pxHigherPrioTaskWoken_, sender_) - -#ifdef Q_EVT_CTOR /* Shall the ctor for the ::QEvt class be provided? */ - - #define Q_NEW_FROM_ISR(evtT_, sig_, ...) \ - (new(QP::QF::newXfromISR_(sizeof(evtT_), QP::QF::NO_MARGIN, 0)) \ - evtT_((sig_), ##__VA_ARGS__)) - - #define Q_NEW_X_FROM_ISR(e_, evtT_, margin_, sig_, ...) do { \ - (e_) = (evtT_ *)QP::QF::newXfromISR_(sizeof(evtT_), \ - (margin_), 0); \ - if ((e_) != (evtT_ *)0) { \ - evtT_##_ctor((e_), (sig_), ##__VA_ARGS__); \ - } \ - } while (false) - -#else - - #define Q_NEW_FROM_ISR(evtT_, sig_) \ - (static_cast(QP::QF::newXfromISR_( \ - static_cast(sizeof(evtT_)), \ - QP::QF::NO_MARGIN, (sig_)))) - - #define Q_NEW_X_FROM_ISR(e_, evtT_, margin_, sig_) ((e_) = \ - (evtT_ *)QP::QF::newXfromISR_((uint_fast16_t)sizeof(evtT_), \ - (margin_), (sig_))) - -#endif /* Q_EVT_CTOR */ - - -namespace QP { - -enum FreeRTOS_TaskAttrs { - TASK_NAME_ATTR -}; -} // namespace QP - -/***************************************************************************** -* interface used only inside QF, but not in applications -*/ -#ifdef QP_IMPL - /* FreeRTOS blocking for event queue implementation (task level) */ - #define QACTIVE_EQUEUE_WAIT_(me_) \ - while ((me_)->m_eQueue.m_frontEvt == nullptr) { \ - QF_CRIT_X_(); \ - ulTaskNotifyTake(pdTRUE, portMAX_DELAY); \ - QF_CRIT_E_(); \ - } - - /* FreeRTOS signaling (unblocking) for event queue (task level) */ - #define QACTIVE_EQUEUE_SIGNAL_(me_) do { \ - QF_CRIT_X_(); \ - xTaskNotifyGive((TaskHandle_t)(me_)->m_thread); \ - QF_CRIT_E_(); \ - } while (false) - - #define QF_SCHED_STAT_ - #define QF_SCHED_LOCK_(dummy) vTaskSuspendAll() - #define QF_SCHED_UNLOCK_() xTaskResumeAll() - - /* native QF event pool operations */ - #define QF_EPOOL_TYPE_ QMPool - #define QF_EPOOL_INIT_(p_, poolSto_, poolSize_, evtSize_) \ - (p_).init((poolSto_), (poolSize_), (evtSize_)) - #define QF_EPOOL_EVENT_SIZE_(p_) ((p_).getBlockSize()) - #define QF_EPOOL_GET_(p_, e_, m_, qs_id_) \ - ((e_) = static_cast((p_).get((m_), (qs_id_)))) - #define QF_EPOOL_PUT_(p_, e_, qs_id_) \ - ((p_).put((e_), (qs_id_))) - -#endif /* ifdef QP_IMPL */ - -/***************************************************************************** -* NOTE0: -* This is the "experimental" port to the [Espressif ESP-IDF][1] -* IoT Framework, which is loosely based on the [FreeRTOS kernel][2]. -* -* "Experimental" means that the port has not been thouroughly tested at -* Quantum Leaps and no working examples are provided. -* -* The [Espressif ESP-IDF][1] is based on a significantly changed version -* of the FreeRTOS kernel developed by Espressif to support the ESP32 multi-core -* CPUs (see [ESP-IDF][1]). -* -* The Espressif version of FreeRTOS is __NOT__ compatible with the baseline -* FreeRTOS and it needs to be treated as a separate RTOS kernel. -* According to the comments in the Espressif source code, FreeRTOS-ESP-IDF -* is based on FreeRTOS V8.2.0, but apparently FreeRTOS-ESP-IDF has been -* updated with the newer features introduced to the original FreeRTOS in the -* later versions. For example, FreeRTOS-ESP32 supports the "static allocation", -* first introduced in baseline FreeRTOS V9.x. -* -* [1]: https://www.espressif.com/en/products/sdks/esp-idf -* [2]: https://freertos.org -* -* NOTE1: -* The maximum number of active objects QF_MAX_ACTIVE can be increased to 64, -* inclusive, but it can be reduced to save some memory. Also, the number of -* active objects cannot exceed the number of FreeRTOS task priorities, -* because each QP active object requires a unique priority level. -* -* NOTE2: -* The critical section definition applies only to the FreeRTOS "task level" -* APIs. The "FromISR" APIs are defined separately. -* -* NOTE3: -* This QF port to FreeRTOS-ESP32 uses the FreeRTOS-ESP32 spin lock "mutex", -* similar to the internal implementation of FreeRTOS-ESP32 (see tasks.c). -* However, the QF port uses its own "mutex" object QF_esp32mux. -* -* NOTE4: -* This QP/C++ port used as reference the esp-idf QP/C port counterpart. The main -* difference implementation-wise is that instead of using xTaskCreateStaticPinnedToCore, -* it uses xTaskCreatePinnedToCore. The reason is that this port was designed to work -* with the Arduino SDK, which does not include xTaskCreateStaticPinnedToCore. -* -* NOTE5: -* The design of FreeRTOS requires using different APIs inside the ISRs -* (the "FromISR" variant) than at the task level. Accordingly, this port -* provides the "FromISR" variants for QP functions and "FROM_ISR" variants -* for QP macros to be used inside ISRs. ONLY THESE "FROM_ISR" VARIANTS -* ARE ALLOWED INSIDE ISRs AND CALLING THE TASK-LEVEL APIs IS AN ERROR. -*/ - -#endif /* QF_PORT_HPP */ diff --git a/src/qf_ps.cpp b/src/qf_ps.cpp deleted file mode 100644 index 79caa83..0000000 --- a/src/qf_ps.cpp +++ /dev/null @@ -1,261 +0,0 @@ -//$file${src::qf::qf_ps.cpp} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv -// -// Model: qpcpp.qm -// File: ${src::qf::qf_ps.cpp} -// -// This code has been generated by QM 5.2.5 . -// DO NOT EDIT THIS FILE MANUALLY. All your changes will be lost. -// -// This code is covered by the following QP license: -// License # : LicenseRef-QL-dual -// Issued to : Any user of the QP/C++ real-time embedded framework -// Framework(s) : qpcpp -// Support ends : 2023-12-31 -// License scope: -// -// Copyright (C) 2005 Quantum Leaps, LLC . -// -// 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: -// -// The terms of the closed source Quantum Leaps commercial licenses -// can be found at: -// -// Redistributions in source code must retain this top-level comment block. -// Plagiarizing this software to sidestep the license obligations is illegal. -// -// Contact information: -// -// -// -//$endhead${src::qf::qf_ps.cpp} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -//! @file -//! @brief QF/C++ Publish-Subscribe services -//! definitions. - -#define QP_IMPL // this is QP implementation -#include "qf_port.hpp" // QF port -#include "qf_pkg.hpp" // QF package-scope interface -#include "qassert.h" // QP embedded systems-friendly assertions -#ifdef Q_SPY // QS software tracing enabled? - #include "qs_port.hpp" // QS port - #include "qs_pkg.hpp" // QS facilities for pre-defined trace records -#else - #include "qs_dummy.hpp" // disable the QS software tracing -#endif // Q_SPY - -// unnamed namespace for local definitions with internal linkage -namespace { -Q_DEFINE_THIS_MODULE("qf_ps") -} // unnamed namespace - -//$skip${QP_VERSION} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv -// Check for the minimum required QP version -#if (QP_VERSION < 700U) || (QP_VERSION != ((QP_RELEASE^4294967295U) % 0x3E8U)) -#error qpcpp version 7.0.0 or higher required -#endif -//$endskip${QP_VERSION} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -//$define${QF::QActive::subscrList_} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv -namespace QP { -QSubscrList * QActive::subscrList_; - -} // namespace QP -//$enddef${QF::QActive::subscrList_} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -//$define${QF::QActive::maxPubSignal_} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv -namespace QP { -enum_t QActive::maxPubSignal_; - -} // namespace QP -//$enddef${QF::QActive::maxPubSignal_} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -//$define${QF::QActive::psInit} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv -namespace QP { - -//${QF::QActive::psInit} ..................................................... -void QActive::psInit( - QSubscrList * const subscrSto, - enum_t const maxSignal) noexcept -{ - subscrList_ = subscrSto; - maxPubSignal_ = maxSignal; - QF::bzero(subscrSto, static_cast(maxSignal) - * sizeof(QSubscrList)); -} - -} // namespace QP -//$enddef${QF::QActive::psInit} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -//$define${QF::QActive::publish_} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv -namespace QP { - -//${QF::QActive::publish_} ................................................... -void QActive::publish_( - QEvt const * const e, - void const * const sender, - std::uint_fast8_t const qs_id) noexcept -{ - Q_UNUSED_PAR(sender); // when Q_SPY not defined - Q_UNUSED_PAR(qs_id); // when Q_SPY not defined - - //! @pre the published signal must be within the configured range - Q_REQUIRE_ID(100, static_cast(e->sig) < maxPubSignal_); - - QF_CRIT_STAT_ - QF_CRIT_E_(); - - QS_BEGIN_NOCRIT_PRE_(QS_QF_PUBLISH, qs_id) - QS_TIME_PRE_(); // the timestamp - QS_OBJ_PRE_(sender); // the sender object - QS_SIG_PRE_(e->sig); // the signal of the event - QS_2U8_PRE_(e->poolId_, e->refCtr_); // pool Id & refCtr of the evt - QS_END_NOCRIT_PRE_() - - // is it a dynamic event? - if (e->poolId_ != 0U) { - // NOTE: The reference counter of a dynamic 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 list - QPSet subscrList = subscrList_[e->sig]; - QF_CRIT_X_(); - - if (subscrList.notEmpty()) { // any subscribers? - // the highest-prio subscriber - std::uint_fast8_t p = subscrList.findMax(); - QActive *a = registry_[p]; - QF_SCHED_STAT_ - - QF_SCHED_LOCK_(a->m_prio); // lock the scheduler up to AO's prio - do { // loop over all subscribers - // the prio of the AO must be registered with the framework - Q_ASSERT_ID(210, a != nullptr); - - // POST() asserts internally if the queue overflows - a->POST(e, sender); - - subscrList.remove(p); // remove the handled subscriber - if (subscrList.notEmpty()) { // still more subscribers? - p = subscrList.findMax(); // the highest-prio subscriber - a = registry_[p]; - } - 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); - #endif -} - -} // namespace QP -//$enddef${QF::QActive::publish_} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -//$define${QF::QActive::subscribe} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv -namespace QP { - -//${QF::QActive::subscribe} .................................................. -void QActive::subscribe(enum_t const sig) const noexcept { - std::uint_fast8_t const p = static_cast(m_prio); - - Q_REQUIRE_ID(300, (Q_USER_SIG <= sig) - && (sig < maxPubSignal_) - && (0U < p) && (p <= QF_MAX_ACTIVE) - && (registry_[p] == this)); - - QF_CRIT_STAT_ - QF_CRIT_E_(); - - QS_BEGIN_NOCRIT_PRE_(QS_QF_ACTIVE_SUBSCRIBE, m_prio) - QS_TIME_PRE_(); // timestamp - QS_SIG_PRE_(sig); // the signal of this event - QS_OBJ_PRE_(this); // this active object - QS_END_NOCRIT_PRE_() - - subscrList_[sig].insert(p); // insert into subscriber-list - QF_CRIT_X_(); -} - -} // namespace QP -//$enddef${QF::QActive::subscribe} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -//$define${QF::QActive::unsubscribe} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv -namespace QP { - -//${QF::QActive::unsubscribe} ................................................ -void QActive::unsubscribe(enum_t const sig) const noexcept { - std::uint_fast8_t const p = static_cast(m_prio); - - //! @pre the signal and the priority must be in range, the AO must also - // be registered with the framework - Q_REQUIRE_ID(400, (Q_USER_SIG <= sig) - && (sig < maxPubSignal_) - && (0U < p) && (p <= QF_MAX_ACTIVE) - && (registry_[p] == this)); - - QF_CRIT_STAT_ - QF_CRIT_E_(); - - QS_BEGIN_NOCRIT_PRE_(QS_QF_ACTIVE_UNSUBSCRIBE, m_prio) - QS_TIME_PRE_(); // timestamp - QS_SIG_PRE_(sig); // the signal of this event - QS_OBJ_PRE_(this); // this active object - QS_END_NOCRIT_PRE_() - - subscrList_[sig].remove(p); // remove from subscriber-list - - QF_CRIT_X_(); -} - -} // namespace QP -//$enddef${QF::QActive::unsubscribe} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -//$define${QF::QActive::unsubscribeAll} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv -namespace QP { - -//${QF::QActive::unsubscribeAll} ............................................. -void QActive::unsubscribeAll() const noexcept { - std::uint_fast8_t const p = static_cast(m_prio); - - Q_REQUIRE_ID(500, (0U < p) && (p <= QF_MAX_ACTIVE) - && (registry_[p] == this)); - - for (enum_t sig = Q_USER_SIG; sig < maxPubSignal_; ++sig) { - QF_CRIT_STAT_ - QF_CRIT_E_(); - if (subscrList_[sig].hasElement(p)) { - subscrList_[sig].remove(p); - - QS_BEGIN_NOCRIT_PRE_(QS_QF_ACTIVE_UNSUBSCRIBE, m_prio) - QS_TIME_PRE_(); // timestamp - QS_SIG_PRE_(sig); // the signal of this event - QS_OBJ_PRE_(this); // this active object - QS_END_NOCRIT_PRE_() - - } - QF_CRIT_X_(); - - // prevent merging critical sections - QF_CRIT_EXIT_NOP(); - } -} - -} // namespace QP -//$enddef${QF::QActive::unsubscribeAll} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/src/qf_qact.cpp b/src/qf_qact.cpp deleted file mode 100644 index 47c7d40..0000000 --- a/src/qf_qact.cpp +++ /dev/null @@ -1,258 +0,0 @@ -//$file${src::qf::qf_qact.cpp} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv -// -// Model: qpcpp.qm -// File: ${src::qf::qf_qact.cpp} -// -// This code has been generated by QM 5.2.5 . -// DO NOT EDIT THIS FILE MANUALLY. All your changes will be lost. -// -// This code is covered by the following QP license: -// License # : LicenseRef-QL-dual -// Issued to : Any user of the QP/C++ real-time embedded framework -// Framework(s) : qpcpp -// Support ends : 2023-12-31 -// License scope: -// -// Copyright (C) 2005 Quantum Leaps, LLC . -// -// 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: -// -// The terms of the closed source Quantum Leaps commercial licenses -// can be found at: -// -// Redistributions in source code must retain this top-level comment block. -// Plagiarizing this software to sidestep the license obligations is illegal. -// -// Contact information: -// -// -// -//$endhead${src::qf::qf_qact.cpp} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -//! @file -//! @brief QP::QActive services and QF support code - -#define QP_IMPL // this is QP implementation -#include "qf_port.hpp" // QF port -#include "qf_pkg.hpp" // QF package-scope interface -#include "qassert.h" // QP embedded systems-friendly assertions -#ifdef Q_SPY // QS software tracing enabled? - #include "qs_port.hpp" // QS port - #include "qs_pkg.hpp" // QS facilities for pre-defined trace records -#else - #include "qs_dummy.hpp" // disable the QS software tracing -#endif // Q_SPY - -//============================================================================ -namespace { // unnamed local namespace -Q_DEFINE_THIS_MODULE("qf_qact") -} // unnamed namespace - -//============================================================================ -//$skip${QP_VERSION} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv -// Check for the minimum required QP version -#if (QP_VERSION < 700U) || (QP_VERSION != ((QP_RELEASE^4294967295U) % 0x3E8U)) -#error qpcpp version 7.0.0 or higher required -#endif -//$endskip${QP_VERSION} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -//$define${QF::QActive::registry_[QF_MAX_ACTIVE + 1U]} vvvvvvvvvvvvvvvvvvvvvvv -namespace QP { -QActive * QActive::registry_[QF_MAX_ACTIVE + 1U]; - -} // namespace QP -//$enddef${QF::QActive::registry_[QF_MAX_ACTIVE + 1U]} ^^^^^^^^^^^^^^^^^^^^^^^ -//$define${QF::QF-base::intLock_} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv -namespace QP { -namespace QF { - -//${QF::QF-base::intLock_} ................................................... -std::uint_fast8_t volatile intLock_; - -} // namespace QF -} // namespace QP -//$enddef${QF::QF-base::intLock_} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -//$define${QF::QF-base::intNest_} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv -namespace QP { -namespace QF { - -//${QF::QF-base::intNest_} ................................................... -std::uint_fast8_t volatile intNest_; - -} // namespace QF -} // namespace QP -//$enddef${QF::QF-base::intNest_} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -//$define${QF::QF-pkg::readySet_} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv -namespace QP { -namespace QF { - -//${QF::QF-pkg::readySet_} ................................................... -QPSet readySet_; - -} // namespace QF -} // namespace QP -//$enddef${QF::QF-pkg::readySet_} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -//$define${QF::QF-pkg::bzero} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv -namespace QP { -namespace QF { - -//${QF::QF-pkg::bzero} ....................................................... -void bzero( - void * const start, - std::uint_fast16_t const len) noexcept -{ - std::uint8_t *ptr = static_cast(start); - for (std::uint_fast16_t n = len; n > 0U; --n) { - *ptr = 0U; - ++ptr; - } -} - -} // namespace QF -} // namespace QP -//$enddef${QF::QF-pkg::bzero} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -//============================================================================ -//$define${QF::QActive::QActive} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv -namespace QP { - -//${QF::QActive::QActive} .................................................... -QActive::QActive(QStateHandler const initial) noexcept - : QHsm(initial), - m_prio(0U), - m_pthre(0U) -{ - #ifdef QF_EQUEUE_TYPE - QF::bzero(&m_eQueue, sizeof(m_eQueue)); - #endif - - #ifdef QF_OS_OBJECT_TYPE - QF::bzero(&m_osObject, sizeof(m_osObject)); - #endif - - #ifdef QF_THREAD_TYPE - QF::bzero(&m_thread, sizeof(m_thread)); - #endif -} - -} // namespace QP -//$enddef${QF::QActive::QActive} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -//$define${QF::QActive::register_} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv -namespace QP { - -//${QF::QActive::register_} .................................................. -void QActive::register_() noexcept { - if (m_pthre == 0U) { // preemption-threshold not defined? - m_pthre = m_prio; // apply the default - } - - #ifndef Q_NASSERT - - //! @pre - //! 1. the "QF-priority" of the AO must be in range - //! 2. the "QF-priority" must not be already in use (unique priority) - //! 3. the "QF-priority" must not exceed the "preemption-threshold" - Q_REQUIRE_ID(100, (0U < m_prio) && (m_prio <= QF_MAX_ACTIVE) - && (registry_[m_prio] == nullptr) - && (m_prio <= m_pthre)); - - std::uint8_t prev_thre = m_pthre; - std::uint8_t next_thre = m_pthre; - std::uint_fast8_t p; - - for (p = static_cast(m_prio) - 1U; p > 0U; --p) { - if (registry_[p] != nullptr) { - prev_thre = registry_[p]->m_pthre; - break; - } - } - for (p = static_cast(m_prio) + 1U; - p <= QF_MAX_ACTIVE; ++p) - { - if (registry_[p] != nullptr) { - next_thre = registry_[p]->m_pthre; - break; - } - } - - //! @post - //! 1. the preceding pre-thre must not exceed the preemption-threshold - //! 2. the preemption-threshold must not exceed the next pre-thre - Q_ENSURE_ID(110, (prev_thre <= m_pthre) && (m_pthre <= next_thre)); - #endif // Q_NASSERT - - QF_CRIT_STAT_ - QF_CRIT_E_(); - // register the AO at the "QF-priority" - registry_[m_prio] = this; - QF_CRIT_X_(); -} - -} // namespace QP -//$enddef${QF::QActive::register_} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -//$define${QF::QActive::unregister_} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv -namespace QP { - -//${QF::QActive::unregister_} ................................................ -void QActive::unregister_() noexcept { - std::uint_fast8_t const p = static_cast(m_prio); - - Q_REQUIRE_ID(200, (0U < p) && (p <= QF_MAX_ACTIVE) - && (registry_[p] == this)); - QF_CRIT_STAT_ - QF_CRIT_E_(); - registry_[p] = nullptr; // free-up the priority level - m_state.fun = nullptr; // invalidate the state - QF_CRIT_X_(); -} - -} // namespace QP -//$enddef${QF::QActive::unregister_} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -//============================================================================ -//$define${QF-types::QPSet::QF_LOG2} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv -namespace QP { - -//${QF-types::QPSet::QF_LOG2} ................................................ -#ifndef QF_LOG2 -std::uint_fast8_t QPSet::QF_LOG2(QP::QPSetBits x) const noexcept { - static std::uint8_t const log2LUT[16] = { - 0U, 1U, 2U, 2U, 3U, 3U, 3U, 3U, - 4U, 4U, 4U, 4U, 4U, 4U, 4U, 4U - }; - std::uint_fast8_t n = 0U; - QP::QPSetBits t; - - #if (QF_MAX_ACTIVE > 16U) - t = static_cast(x >> 16U); - if (t != 0U) { - n += 16U; - x = t; - } - #endif - #if (QF_MAX_ACTIVE > 8U) - t = (x >> 8U); - if (t != 0U) { - n += 8U; - x = t; - } - #endif - t = (x >> 4U); - if (t != 0U) { - n += 4U; - x = t; - } - return n + log2LUT[x]; -} - -#endif // ndef QF_LOG2 - -} // namespace QP -//$enddef${QF-types::QPSet::QF_LOG2} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/src/qf_qeq.cpp b/src/qf_qeq.cpp deleted file mode 100644 index d9ccc99..0000000 --- a/src/qf_qeq.cpp +++ /dev/null @@ -1,272 +0,0 @@ -//$file${src::qf::qf_qeq.cpp} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv -// -// Model: qpcpp.qm -// File: ${src::qf::qf_qeq.cpp} -// -// This code has been generated by QM 5.2.5 . -// DO NOT EDIT THIS FILE MANUALLY. All your changes will be lost. -// -// This code is covered by the following QP license: -// License # : LicenseRef-QL-dual -// Issued to : Any user of the QP/C++ real-time embedded framework -// Framework(s) : qpcpp -// Support ends : 2023-12-31 -// License scope: -// -// Copyright (C) 2005 Quantum Leaps, LLC . -// -// 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: -// -// The terms of the closed source Quantum Leaps commercial licenses -// can be found at: -// -// Redistributions in source code must retain this top-level comment block. -// Plagiarizing this software to sidestep the license obligations is illegal. -// -// Contact information: -// -// -// -//$endhead${src::qf::qf_qeq.cpp} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -//! @file -//! @brief QP::QEQueue implementation - -#define QP_IMPL // this is QP implementation -#include "qf_port.hpp" // QF port -#include "qf_pkg.hpp" // QF package-scope interface -#include "qassert.h" // QP embedded systems-friendly assertions -#ifdef Q_SPY // QS software tracing enabled? - #include "qs_port.hpp" // QS port - #include "qs_pkg.hpp" // QS facilities for pre-defined trace records -#else - #include "qs_dummy.hpp" // disable the QS software tracing -#endif // Q_SPY - -// unnamed namespace for local definitions with internal linkage -namespace { -Q_DEFINE_THIS_MODULE("qf_qeq") -} // unnamed namespace - -//$skip${QP_VERSION} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv -// Check for the minimum required QP version -#if (QP_VERSION < 700U) || (QP_VERSION != ((QP_RELEASE^4294967295U) % 0x3E8U)) -#error qpcpp version 7.0.0 or higher required -#endif -//$endskip${QP_VERSION} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -//$define${QF::QEQueue} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv -namespace QP { - -//${QF::QEQueue} ............................................................. - -//${QF::QEQueue::QEQueue} .................................................... -QEQueue::QEQueue() noexcept - : m_frontEvt(nullptr), - m_ring(nullptr), - m_end(0U), - m_head(0U), - m_tail(0U), - m_nFree(0U), - m_nMin(0U) -{} - -//${QF::QEQueue::init} ....................................................... -void QEQueue::init( - QEvt const * qSto[], - std::uint_fast16_t const qLen) noexcept -{ - m_frontEvt = nullptr; // no events in the queue - m_ring = &qSto[0]; - m_end = static_cast(qLen); - if (qLen > 0U) { - m_head = 0U; - m_tail = 0U; - } - m_nFree = static_cast(qLen + 1U); //+1 for frontEvt - m_nMin = m_nFree; -} - -//${QF::QEQueue::post} ....................................................... -bool QEQueue::post( - QEvt const * const e, - std::uint_fast16_t const margin, - std::uint_fast8_t const qs_id) noexcept -{ - //! @pre event must be valid - Q_REQUIRE_ID(200, e != nullptr); - - QF_CRIT_STAT_ - QF_CRIT_E_(); - QEQueueCtr nFree = m_nFree; - - // margin available? - bool status; - if (((margin == QF::NO_MARGIN) && (nFree > 0U)) - || (nFree > static_cast(margin))) - { - // is it a dynamic event? - if (e->poolId_ != 0U) { - QEvt_refCtr_inc_(e); // increment the reference counter - } - - --nFree; // one free entry just used up - m_nFree = nFree; // update the original - if (m_nMin > nFree) { - m_nMin = nFree; // update minimum so far - } - - QS_BEGIN_NOCRIT_PRE_(QS_QF_EQUEUE_POST, qs_id) - QS_TIME_PRE_(); // timestamp - QS_SIG_PRE_(e->sig); // the signal of this event - QS_OBJ_PRE_(this); // this queue object - QS_2U8_PRE_(e->poolId_, e->refCtr_);// pool Id & refCtr of the evt - QS_EQC_PRE_(nFree); // number of free entries - QS_EQC_PRE_(m_nMin); // min number of free entries - QS_END_NOCRIT_PRE_() - - // is the queue empty? - if (m_frontEvt == nullptr) { - m_frontEvt = e; // deliver event directly - } - // queue is not empty, leave event in the ring-buffer - else { - // insert event into the ring buffer (FIFO) - m_ring[m_head] = e; // insert e into buffer - - // need to wrap? - if (m_head == 0U) { - m_head = m_end; // wrap around - } - m_head = (m_head - 1U); - } - status = true; // event posted successfully - } - else { - //! @note assert if event cannot be posted and dropping events is - //! not acceptable - Q_ASSERT_CRIT_(210, margin != QF::NO_MARGIN); - - QS_BEGIN_NOCRIT_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_(this); // this queue object - QS_2U8_PRE_(e->poolId_, e->refCtr_);// pool Id & refCtr of the evt - QS_EQC_PRE_(nFree); // number of free entries - QS_EQC_PRE_(margin); // margin requested - QS_END_NOCRIT_PRE_() - - status = false; // event not posted - } - QF_CRIT_X_(); - - Q_UNUSED_PAR(qs_id); // when Q_SPY not defined - - return status; -} - -//${QF::QEQueue::postLIFO} ................................................... -void QEQueue::postLIFO( - QEvt const * const e, - std::uint_fast8_t const qs_id) noexcept -{ - Q_UNUSED_PAR(qs_id); // when Q_SPY not defined - - QF_CRIT_STAT_ - QF_CRIT_E_(); - QEQueueCtr nFree = m_nFree; - - //! @pre the queue must be able to accept the event (cannot overflow) - Q_REQUIRE_CRIT_(300, nFree != 0U); - - // is it a dynamic event? - if (e->poolId_ != 0U) { - QEvt_refCtr_inc_(e); // increment the reference counter - } - - --nFree; // one free entry just used up - m_nFree = nFree; // update the original - if (m_nMin > nFree) { - m_nMin = nFree; // update minimum so far - } - - QS_BEGIN_NOCRIT_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_(this); // this queue object - QS_2U8_PRE_(e->poolId_, e->refCtr_); // pool Id & refCtr of the evt - QS_EQC_PRE_(nFree); // number of free entries - QS_EQC_PRE_(m_nMin); // min number of free entries - QS_END_NOCRIT_PRE_() - - QEvt const * const frontEvt = m_frontEvt; // read into temporary - m_frontEvt = e; // deliver event directly to the front of the queue - - // was the queue not empty? - if (frontEvt != nullptr) { - m_tail = (m_tail + 1U); - if (m_tail == m_end) { // need to wrap the tail? - m_tail = 0U; // wrap around - } - m_ring[m_tail] = frontEvt; // buffer the old front evt - } - QF_CRIT_X_(); -} - -//${QF::QEQueue::get} ........................................................ -QEvt const * QEQueue::get(std::uint_fast8_t const qs_id) noexcept { - Q_UNUSED_PAR(qs_id); // when Q_SPY not defined - - QF_CRIT_STAT_ - QF_CRIT_E_(); - QEvt const * const e = m_frontEvt; // always remove evt from the front - - // is the queue not empty? - if (e != nullptr) { - QEQueueCtr const nFree = m_nFree + 1U; - m_nFree = nFree; // upate the number of free - - // any events in the the ring buffer? - if (nFree <= m_end) { - m_frontEvt = m_ring[m_tail]; // remove from the tail - if (m_tail == 0U) { // need to wrap? - m_tail = m_end; // wrap around - } - m_tail = (m_tail - 1U); - - QS_BEGIN_NOCRIT_PRE_(QS_QF_EQUEUE_GET, qs_id) - QS_TIME_PRE_(); // timestamp - QS_SIG_PRE_(e->sig); // the signal of this event - QS_OBJ_PRE_(this); // this queue object - QS_2U8_PRE_(e->poolId_, e->refCtr_); - QS_EQC_PRE_(nFree); // # free entries - QS_END_NOCRIT_PRE_() - } - else { - m_frontEvt = nullptr; // queue becomes empty - - // all entries in the queue must be free (+1 for fronEvt) - Q_ASSERT_CRIT_(410, nFree == (m_end + 1U)); - - QS_BEGIN_NOCRIT_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_(this); // this queue object - QS_2U8_PRE_(e->poolId_, e->refCtr_); - QS_END_NOCRIT_PRE_() - } - } - QF_CRIT_X_(); - - return e; -} - -} // namespace QP -//$enddef${QF::QEQueue} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/src/qf_qmact.cpp b/src/qf_qmact.cpp deleted file mode 100644 index 3d297e5..0000000 --- a/src/qf_qmact.cpp +++ /dev/null @@ -1,120 +0,0 @@ -//$file${src::qf::qf_qmact.cpp} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv -// -// Model: qpcpp.qm -// File: ${src::qf::qf_qmact.cpp} -// -// This code has been generated by QM 5.2.5 . -// DO NOT EDIT THIS FILE MANUALLY. All your changes will be lost. -// -// This code is covered by the following QP license: -// License # : LicenseRef-QL-dual -// Issued to : Any user of the QP/C++ real-time embedded framework -// Framework(s) : qpcpp -// Support ends : 2023-12-31 -// License scope: -// -// Copyright (C) 2005 Quantum Leaps, LLC . -// -// 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: -// -// The terms of the closed source Quantum Leaps commercial licenses -// can be found at: -// -// Redistributions in source code must retain this top-level comment block. -// Plagiarizing this software to sidestep the license obligations is illegal. -// -// Contact information: -// -// -// -//$endhead${src::qf::qf_qmact.cpp} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -//! @file -//! @brief QMActive::QMActive() and virtual functions - -#define QP_IMPL // this is QP implementation -#include "qf_port.hpp" // QF port -#include "qassert.h" // QP embedded systems-friendly assertions - -//! Internal macro to cast a QP::QMActive pointer `qact_` to QP::QMsm* -//! @note -//! Casting pointer to pointer pointer violates the MISRA-C++ 2008 Rule 5-2-7, -//! cast from pointer to pointer. Additionally this cast violates the MISRA- -//! C++ 2008 Rule 5-2-8 Unusual pointer cast (incompatible indirect types). -//! Encapsulating these violations in a macro allows to selectively suppress -//! this specific deviation. -#define QF_QMACTIVE_TO_QMSM_CAST_(qact_) \ - reinterpret_cast((qact_)) - -//! Internal macro to cast a QP::QMActive pointer `qact_` to QP::QMsm const * -#define QF_QMACTIVE_TO_QMSM_CONST_CAST_(qact_) \ - reinterpret_cast((qact_)) - -// unnamed namespace for local definitions with internal linkage -namespace { -//Q_DEFINE_THIS_MODULE("qf_qmact") -} // unnamed namespace - -//$skip${QP_VERSION} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv -// Check for the minimum required QP version -#if (QP_VERSION < 700U) || (QP_VERSION != ((QP_RELEASE^4294967295U) % 0x3E8U)) -#error qpcpp version 7.0.0 or higher required -#endif -//$endskip${QP_VERSION} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -//$define${QF::QMActive} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv -namespace QP { - -//${QF::QMActive} ............................................................ - -//${QF::QMActive::init} ...................................................... -void QMActive::init( - void const * const e, - std::uint_fast8_t const qs_id) -{ - m_state.obj = &QMsm::msm_top_s; - QF_QMACTIVE_TO_QMSM_CAST_(this)->QMsm::init(e, qs_id); -} - -//${QF::QMActive::init} ...................................................... -void QMActive::init(std::uint_fast8_t const qs_id) { - m_state.obj = &QMsm::msm_top_s; - QF_QMACTIVE_TO_QMSM_CAST_(this)->QMsm::init(qs_id); -} - -//${QF::QMActive::dispatch} .................................................. -void QMActive::dispatch( - QEvt const * const e, - std::uint_fast8_t const qs_id) -{ - QF_QMACTIVE_TO_QMSM_CAST_(this)->QMsm::dispatch(e, qs_id); -} - -//${QF::QMActive::isInState} ................................................. -bool QMActive::isInState(QMState const * const st) const noexcept { - return QF_QMACTIVE_TO_QMSM_CONST_CAST_(this)->QMsm::isInState(st); -} - -//${QF::QMActive::childStateObj} ............................................. -QMState const * QMActive::childStateObj(QMState const * const parent) const noexcept { - return QF_QMACTIVE_TO_QMSM_CONST_CAST_(this) - ->QMsm::childStateObj(parent); -} - -//${QF::QMActive::getStateHandler} ........................................... -#ifdef Q_SPY -QStateHandler QMActive::getStateHandler() noexcept { - return QF_QMACTIVE_TO_QMSM_CAST_(this)->QMsm::getStateHandler(); -} - -#endif // def Q_SPY - -} // namespace QP -//$enddef${QF::QMActive} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/src/qk/qk.cpp b/src/qk/qk.cpp new file mode 100644 index 0000000..a2b0757 --- /dev/null +++ b/src/qk/qk.cpp @@ -0,0 +1,426 @@ +//$file${src::qk::qk.cpp} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv +// +// Model: qpcpp.qm +// File: ${src::qk::qk.cpp} +// +// This code has been generated by QM 6.2.3 . +// DO NOT EDIT THIS FILE MANUALLY. All your changes will be lost. +// +// ____________________________________ +// / / +// / GGGGGGG PPPPPPPP LL / +// / GG GG PP PP LL / +// / GG PP PP LL / +// / GG GGGGG PPPPPPPP LL / +// / GG GG PP LL / +// / GGGGGGG PP LLLLLLL / +// /___________________________________/ +// +// Copyright (c) 2005 Quantum Leaps, LLC +// SPDX-License-Identifier: GPL-3.0-or-later +// +// This generated code is open-source software licensed under the GNU +// General Public License (GPL) as published by the Free Software Foundation +// (see ). +// +// NOTE: +// The GPL does NOT permit the incorporation of this code into proprietary +// programs. Please contact Quantum Leaps for commercial licensing options, +// which expressly supersede the GPL and are designed explicitly for licensees +// interested in retaining the proprietary status of the generated code. +// +// Quantum Leaps contact information: +// +// +// +//$endhead${src::qk::qk.cpp} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +#define QP_IMPL // this is QP implementation +#include "qp_port.hpp" // QP port +#include "qp_pkg.hpp" // QP package-scope interface +#include "qsafe.h" // QP Functional Safety (FuSa) Subsystem +#ifdef Q_SPY // QS software tracing enabled? + #include "qs_port.hpp" // QS port + #include "qs_pkg.hpp" // QS facilities for pre-defined trace records +#else + #include "qs_dummy.hpp" // disable the QS software tracing +#endif // Q_SPY + +// protection against including this source file in a wrong project +#ifndef QK_HPP_ + #error "Source file included in a project NOT based on the QK kernel" +#endif // QK_HPP_ + +// unnamed namespace for local definitions with internal linkage +namespace { +Q_DEFINE_THIS_MODULE("qk") +} // unnamed namespace + +//$skip${QP_VERSION} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv +// Check for the minimum required QP version +#if (QP_VERSION < 730U) || (QP_VERSION != ((QP_RELEASE^4294967295U)%0x2710U)) +#error qpcpp version 7.3.0 or higher required +#endif +//$endskip${QP_VERSION} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +//$define${QK::QK-base} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv +namespace QP { +namespace QK { + +//${QK::QK-base::schedLock} .................................................. +QSchedStatus schedLock(std::uint_fast8_t const ceiling) noexcept { + 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_(static_cast(QK_priv_.lockCeil), + static_cast(ceiling)); + QS_END_PRE_() + + // previous status of the lock + stat = static_cast(QK_priv_.lockHolder); + stat = stat | (static_cast(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 +} + +//${QK::QK-base::schedUnlock} ................................................ +void schedUnlock(QSchedStatus const stat) noexcept { + // has the scheduler been actually locked by the last QK_schedLock()? + if (stat != 0xFFU) { + QF_CRIT_STAT + QF_CRIT_ENTRY(); + QF_MEM_SYS(); + + std::uint_fast8_t const lockCeil = QK_priv_.lockCeil; + std::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_(static_cast(lockCeil), + static_cast(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(); + } +} + +} // namespace QK +} // namespace QP +//$enddef${QK::QK-base} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +extern "C" { +//$define${QK-extern-C} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv + +//${QK-extern-C::QK_priv_} ................................................... +QK_Attr QK_priv_; + +//${QK-extern-C::QK_sched_} .................................................. +std::uint_fast8_t QK_sched_() noexcept { + Q_REQUIRE_INCRIT(400, QK_priv_.readySet.verify_( + &QK_priv_.readySet_dis)); + + std::uint_fast8_t p; + if (QK_priv_.readySet.isEmpty()) { + p = 0U; // no activation needed + } + else { + // find the highest-prio AO with non-empty event queue + p = QK_priv_.readySet.findMax(); + + // 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; + } + } + + return p; +} + +//${QK-extern-C::QK_activate_} ............................................... +void QK_activate_() noexcept { + std::uint_fast8_t const prio_in = QK_priv_.actPrio; // save initial prio. + std::uint_fast8_t p = QK_priv_.nextPrio; // next prio to run + 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) + std::uint_fast8_t pprev = prio_in; + #endif // QF_ON_CONTEXT_SW || Q_SPY + + // loop until no more ready-to-run AOs of higher prio than the initial + QP::QActive *a; + do { + a = QP::QActive::registry_[p]; // obtain the pointer to the AO + Q_ASSERT_INCRIT(505, a != nullptr); // the AO must be registered + + // set new active prio. and preemption-ceiling + QK_priv_.actPrio = p; + QK_priv_.actThre = a->getPThre(); + + #if (defined QF_ON_CONTEXT_SW) || (defined Q_SPY) + if (p != pprev) { // changing threads? + + QS_BEGIN_PRE_(QP::QS_SCHED_NEXT, p) + QS_TIME_PRE_(); // timestamp + QS_2U8_PRE_(p, // prio. of the scheduled AO + pprev); // previous prio. + QS_END_PRE_() + + #ifdef QF_ON_CONTEXT_SW + QF_onContextSw(((pprev != 0U) + ? QP::QActive::registry_[pprev] + : nullptr), a); + #endif // QF_ON_CONTEXT_SW + + pprev = p; // update previous prio. + } + #endif // QF_ON_CONTEXT_SW || Q_SPY + + QF_INT_ENABLE(); // unconditionally enable interrupts + + QP::QEvt const * const e = a->get_(); + // NOTE QActive_get_() performs QF_MEM_APP() before return + + // dispatch event (virtual call) + a->dispatch(e, a->getPrio()); + #if (QF_MAX_EPOOL > 0U) + QP::QF::gc(e); + #endif + + // determine the next highest-prio. AO ready to run... + QF_INT_DISABLE(); // unconditionally disable interrupts + QF_MEM_SYS(); + + // internal integrity check (duplicate inverse storage) + Q_ASSERT_INCRIT(502, QK_priv_.readySet.verify_( + &QK_priv_.readySet_dis)); + + if (a->getEQueue().isEmpty()) { // empty queue? + QK_priv_.readySet.remove(p); + #ifndef Q_UNSAFE + QK_priv_.readySet.update_(&QK_priv_.readySet_dis); + #endif + } + + if (QK_priv_.readySet.isEmpty()) { + p = 0U; // no activation needed + } + else { + // find new highest-prio AO ready to run... + p = static_cast(QK_priv_.readySet.findMax()); + + // is the new prio. below the initial preemption-threshold? + if (p <= QP::QActive::registry_[prio_in]->getPThre()) { + 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 = QP::QActive::registry_[prio_in]->getPThre(); + + #if (defined QF_ON_CONTEXT_SW) || (defined Q_SPY) + if (prio_in != 0U) { // resuming an active object? + a = QP::QActive::registry_[prio_in]; // pointer to preempted AO + + QS_BEGIN_PRE_(QP::QS_SCHED_NEXT, prio_in) + QS_TIME_PRE_(); // timestamp + // prio. of the resumed AO, previous prio. + QS_2U8_PRE_(prio_in, pprev); + QS_END_PRE_() + } + else { // resuming prio.==0 --> idle + a = nullptr; // QK idle loop + + QS_BEGIN_PRE_(QP::QS_SCHED_IDLE, pprev) + QS_TIME_PRE_(); // timestamp + QS_U8_PRE_(pprev); // previous prio. + QS_END_PRE_() + } + + #ifdef QF_ON_CONTEXT_SW + QF_onContextSw(QP::QActive::registry_[pprev], a); + #endif // QF_ON_CONTEXT_SW + + #endif // QF_ON_CONTEXT_SW || Q_SPY +} +//$enddef${QK-extern-C} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +} // extern "C" + +//$define${QK::QF-cust} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv +namespace QP { +namespace QF { + +//${QK::QF-cust::init} ....................................................... +void init() { + bzero_(&QF::priv_, sizeof(QF::priv_)); + bzero_(&QK_priv_, sizeof(QK_priv_)); + bzero_(&QTimeEvt::timeEvtHead_[0], sizeof(QTimeEvt::timeEvtHead_)); + bzero_(&QActive::registry_[0], sizeof(QActive::registry_)); + + #ifndef Q_UNSAFE + QK_priv_.readySet.update_(&QK_priv_.readySet_dis); + #endif + + // setup the QK scheduler as initially locked and not running + QK_priv_.lockCeil = (QF_MAX_ACTIVE + 1U); // scheduler locked + + // storage capable for holding a blank QActive object (const in ROM) + static void* const + idle_ao[((sizeof(QActive) + sizeof(void*)) - 1U) / sizeof(void*)] + = { nullptr }; + + // register the blank QActive object as the idle-AO (cast 'const' away) + QActive::registry_[0] = QF_CONST_CAST_(QActive*, + reinterpret_cast(idle_ao)); + + #ifdef QK_INIT + QK_INIT(); // port-specific initialization of the QK kernel + #endif +} + +//${QK::QF-cust::stop} ....................................................... +void stop() { + onCleanup(); // cleanup callback + // nothing else to do for the QK preemptive kernel +} + +//${QK::QF-cust::run} ........................................................ +int_t run() { + #ifdef Q_SPY + // produce the QS_QF_RUN trace record + QF_INT_DISABLE(); + QF_MEM_SYS(); + QS::beginRec_(QS_REC_NUM_(QS_QF_RUN)); + QS::endRec_(); + QF_MEM_APP(); + QF_INT_ENABLE(); + #endif // Q_SPY + + onStartup(); // application-specific startup callback + + QF_INT_DISABLE(); + QF_MEM_SYS(); + + 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__ // GNU compiler? + return 0; + #endif +} + +} // namespace QF +} // namespace QP +//$enddef${QK::QF-cust} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +//$define${QK::QActive} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv +namespace QP { + +//${QK::QActive} ............................................................. + +//${QK::QActive::start} ...................................................... +void QActive::start( + QPrioSpec const prioSpec, + QEvt const * * const qSto, + std::uint_fast16_t const qLen, + void * const stkSto, + std::uint_fast16_t const stkSize, + void const * const par) +{ + Q_UNUSED_PAR(stkSto); // not needed in QK + Q_UNUSED_PAR(stkSize); // not needed in QK + + QF_CRIT_STAT + QF_CRIT_ENTRY(); + QF_MEM_SYS(); + + Q_REQUIRE_INCRIT(300, (!QK_ISR_CONTEXT_()) + && (stkSto == nullptr)); + QF_MEM_APP(); + QF_CRIT_EXIT(); + + m_prio = static_cast(prioSpec & 0xFFU); // QF-prio. + m_pthre = static_cast(prioSpec >> 8U); // preemption-thre. + register_(); // make QF aware of this AO + + m_eQueue.init(qSto, qLen); // initialize the built-in queue + + this->init(par, m_prio); // top-most initial tran. (virtual call) + QS_FLUSH(); // flush the trace buffer to the host + + // See if this AO needs to be scheduled in case QK is already running + QF_CRIT_ENTRY(); + QF_MEM_SYS(); + if (QK_sched_() != 0U) { // synchronous preemption needed? + QK_activate_(); // synchronously activate AOs + } + QF_MEM_APP(); + QF_CRIT_EXIT(); +} + +} // namespace QP +//$enddef${QK::QActive} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/src/qmpool.hpp b/src/qmpool.hpp deleted file mode 100644 index 91faa01..0000000 --- a/src/qmpool.hpp +++ /dev/null @@ -1,307 +0,0 @@ -//$file${include::qmpool.hpp} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv -// -// Model: qpcpp.qm -// File: ${include::qmpool.hpp} -// -// This code has been generated by QM 5.2.5 . -// DO NOT EDIT THIS FILE MANUALLY. All your changes will be lost. -// -// This code is covered by the following QP license: -// License # : LicenseRef-QL-dual -// Issued to : Any user of the QP/C++ real-time embedded framework -// Framework(s) : qpcpp -// Support ends : 2023-12-31 -// License scope: -// -// Copyright (C) 2005 Quantum Leaps, LLC . -// -// 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: -// -// The terms of the closed source Quantum Leaps commercial licenses -// can be found at: -// -// Redistributions in source code must retain this top-level comment block. -// Plagiarizing this software to sidestep the license obligations is illegal. -// -// Contact information: -// -// -// -//$endhead${include::qmpool.hpp} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -//! @file -//! @brief platform-independent memory pool QP::QMPool interface. - -#ifndef QMPOOL_HPP_ -#define QMPOOL_HPP_ - -#ifndef QF_MPOOL_SIZ_SIZE - //! macro to override the default QP::QMPoolSize size. - //! Valid values 1U, 2U, or 4U; default 2U - #define QF_MPOOL_SIZ_SIZE 2U -#endif - -#ifndef QF_MPOOL_CTR_SIZE - //! macro to override the default QMPoolCtr size. - //! Valid values 1U, 2U, or 4U; default 2U - #define QF_MPOOL_CTR_SIZE 2 -#endif - -//! Memory pool element to allocate correctly aligned storage for QP::QMPool -#define QF_MPOOL_EL(type_) \ - struct { void *sto_[((sizeof(type_) - 1U)/sizeof(void*)) + 1U]; } - -//============================================================================ -namespace QP { - -#if (QF_MPOOL_SIZ_SIZE == 1U) - using QMPoolSize = std::uint8_t; -#elif (QF_MPOOL_SIZ_SIZE == 2U) - //! The data type to store the block-size based on the macro - //! #QF_MPOOL_SIZ_SIZE - //! - //! @details - //! The dynamic range of this data type determines the maximum size - //! of blocks that can be managed by the native QF event pool. - using QMPoolSize = std::uint16_t; -#elif (QF_MPOOL_SIZ_SIZE == 4U) - using QMPoolSize = std::uint32_t; -#else - #error "QF_MPOOL_SIZ_SIZE defined incorrectly, expected 1U, 2U, or 4U" -#endif - -#if (QF_MPOOL_CTR_SIZE == 1U) - using QMPoolCtr = std::uint8_t; -#elif (QF_MPOOL_CTR_SIZE == 2U) - //! The data type to store the block-counter based on the macro - //! #QF_MPOOL_CTR_SIZE - //! - //! @details - //! The dynamic range of this data type determines the maximum number - //! of blocks that can be stored in the pool. - using QMPoolCtr = std::uint16_t; -#elif (QF_MPOOL_CTR_SIZE == 4U) - using QMPoolCtr = std::uint32_t; -#else - #error "QF_MPOOL_CTR_SIZE defined incorrectly, expected 1U, 2U, or 4U" -#endif - -} // namespace QP - -//============================================================================ -//$declare${QF::QMPool} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv -namespace QP { - -//${QF::QMPool} .............................................................. -//! Native QF memory pool class -//! -//! @details -//! A fixed block-size memory pool is a very fast and efficient data -//! structure for dynamic allocation of fixed block-size chunks of memory. -//! A memory pool offers fast and deterministic allocation and recycling of -//! memory blocks and is not subject to fragmenation.@n -//! @n -//! The QP::QMPool class describes the native QF memory pool, which can be -//! used as the event pool for dynamic event allocation, or as a fast, -//! deterministic fixed block-size heap for any other objects in your -//! application. -//! -//! @note -//! The QP::QMPool class contains only data members for managing a memory -//! pool, but does not contain the pool storage, which must be provided -//! externally during the pool initialization. -//! -//! @note -//! The native QF event pool is configured by defining the macro -//! #QF_EPOOL_TYPE_ as QP::QMPool in the specific QF port header file. -class QMPool { -private: - - //! start of the memory managed by this memory pool - void * m_start; - - //! end of the memory managed by this memory pool - void * m_end; - - //! head of linked list of free blocks - void * volatile m_free_head; - - //! maximum block size (in bytes) - QMPoolSize m_blockSize; - - //! total number of blocks - QMPoolCtr m_nTot; - - //! number of free blocks remaining - QMPoolCtr volatile m_nFree; - - //! minimum number of free blocks ever present in this pool - //! - //! @note - //! This attribute remembers the low watermark of the pool, - //! which provides a valuable information for sizing event pools. - //! - //! @sa QF::getPoolMin(). - QMPoolCtr m_nMin; - -public: - - //! public default constructor - QMPool(); - - //! Initializes the native QF event pool - //! - //! @details - //! Initialize a fixed block-size memory pool by providing it with the - //! pool memory to manage, size of this memory, and the block size. - //! - //! @param[in] poolSto pointer to the memory buffer for pool storage - //! @param[in] poolSize size of the storage buffer in bytes - //! @param[in] blockSize fixed-size of the memory blocks in bytes - //! - //! @attention - //! The caller of QMPool::init() must make sure that the `poolSto` - //! pointer is properly **aligned**. In particular, it must be possible to - //! efficiently store a pointer at the location pointed to by `poolSto`. - //! Internally, the QMPool::init() function rounds up the block size - //! `blockSize` so that it can fit an integer number of pointers. This - //! is done to achieve proper alignment of the blocks within the pool. - //! - //! @note - //! Due to the rounding of block size the actual capacity of the pool - //! might be less than (`poolSize` / `blockSize`). You can check the - //! capacity of the pool by calling the QF::getPoolMin() function. - //! - //! @note - //! This function is **not** protected by a critical section, because - //! it is intended to be called only during the initialization of the - //! system, when interrupts are not allowed yet. - //! - //! @note - //! Many QF ports use memory pools to implement the event pools. - void init( - void * const poolSto, - std::uint_fast32_t poolSize, - std::uint_fast16_t blockSize) noexcept; - - //! Obtains a memory block from a memory pool - //! - //! @details - //! The function allocates a memory block from the pool and returns a - //! pointer to the block back to the caller. - //! - //! @param[in] margin the minimum number of unused blocks still - //! available in the pool after the allocation. - //! @param[in] qs_id QS-id of this state machine (for QS local filter) - //! - //! @returns - //! A pointer to a memory block or NULL if no more blocks are available - //! in the memory pool. - //! - //! @note - //! This function can be called from any task level or ISR level. - //! - //! @note - //! The memory pool must be initialized before any events can - //! be requested from it. Also, the QP::QMPool::get() function uses - //! internally a QF critical section, so you should be careful not to - //! call it from within a critical section when nesting of critical - //! section is not supported. - //! - //! @attention - //! An allocated block must be later returned back to the **same** pool - //! from which it has been allocated. - //! - //! @sa - //! QP::QMPool::put() - void * get( - std::uint_fast16_t const margin, - std::uint_fast8_t const qs_id) noexcept; - - //! Returns a memory block back to a memory pool - //! - //! @details - //! Recycle a memory block to the fixed block-size memory pool. - //! - //! @param[in] b pointer to the memory block that is being recycled - //! @param[in] qs_id QS-id of this state machine (for QS local filter) - //! - //! @attention - //! The recycled block must be allocated from the **same** memory pool - //! to which it is returned. - //! - //! @note - //! This function can be called from any task level or ISR level. - //! - //! @sa - //! QP::QMPool::get() - void put( - void * const b, - std::uint_fast8_t const qs_id) noexcept; - - //! return the fixed block-size of the blocks managed by this pool - QMPoolSize getBlockSize() const noexcept; - - //! Memory pool operation for obtaining the minimum number of free - //! blocks ever in the pool (a.k.a. "low-watermark"). - //! - //! @details - //! This operation needs to be used with caution because the - //! "low-watermark" can change unexpectedly. The main intent for using - //! this operation is to get an idea of pool usage to size the pool - //! adequately. - //! - //! @returns the minimum number of free entries ever in the memory pool - //! since init. - QMPoolCtr getNMin() const noexcept { - return m_nMin; - } - - //! Memory pool operation for obtaining the current number of free - //! blocks in the pool. - //! - //! @details - //! This operation needs to be used with caution because the number - //! of free blocks can change unexpectedly. - //! - //! @returns the current number of free blocks in the memory pool. - QMPoolCtr getNFree() const noexcept { - return m_nFree; - } - -private: - - //! disallow copying of QP::QMPool - QMPool(QEQueue const & other) = delete; - - //! disallow copying of QP::QMPool - QMPool & operator=(QMPool const & other) = delete; - -public: - -#ifdef QF_ISR_API - //! the "FromISR" variant used in the QP port to "FreeRTOS" - void * getFromISR( - std::uint_fast16_t const margin, - std::uint_fast8_t const qs_id) noexcept; -#endif // def QF_ISR_API - -#ifdef QF_ISR_API - //! the "FromISR" variant used in the QP port to "FreeRTOS" - void putFromISR( - void * const b, - std::uint_fast8_t const qs_id) noexcept; -#endif // def QF_ISR_API -}; // class QMPool - -} // namespace QP -//$enddecl${QF::QMPool} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -#endif // QMPOOL_HPP_ diff --git a/src/qpset.hpp b/src/qpset.hpp deleted file mode 100644 index 67e0963..0000000 --- a/src/qpset.hpp +++ /dev/null @@ -1,197 +0,0 @@ -/// @file -/// @brief platform-independent priority sets of 8 or 64 elements. -/// @ingroup qf -/// @cond -///*************************************************************************** -/// Last updated for version 6.8.2 -/// Last updated on 2020-07-18 -/// -/// Q u a n t u m L e a P s -/// ------------------------ -/// Modern Embedded Software -/// -/// Copyright (C) 2005-2019 Quantum Leaps. All rights reserved. -/// -/// This program is open source software: you can redistribute it and/or -/// modify it under the terms of the GNU General Public License as published -/// by the Free Software Foundation, either version 3 of the License, or -/// (at your option) any later version. -/// -/// Alternatively, this program may be distributed and modified under the -/// terms of Quantum Leaps commercial licenses, which expressly supersede -/// the GNU General Public License and are specifically designed for -/// licensees interested in retaining the proprietary status of their code. -/// -/// This program is distributed in the hope that it will be useful, -/// but WITHOUT ANY WARRANTY; without even the implied warranty of -/// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -/// GNU General Public License for more details. -/// -/// You should have received a copy of the GNU General Public License -/// along with this program. If not, see . -/// -/// Contact information: -/// -/// -///*************************************************************************** -/// @endcond - -#ifndef QPSET_HPP -#define QPSET_HPP - -#ifndef QF_MAX_ACTIVE - // default value when NOT defined - #define QF_MAX_ACTIVE 32U -#endif - -namespace QP { - -#if (QF_MAX_ACTIVE < 1U) || (64U < QF_MAX_ACTIVE) - #error "QF_MAX_ACTIVE out of range. Valid range is 1U..64U" -#elif (QF_MAX_ACTIVE <= 8U) - using QPSetBits = std::uint8_t; -#elif (QF_MAX_ACTIVE <= 16U) - using QPSetBits = std::uint16_t; -#else - //! bitmask for the internal representation of QPSet elements - using QPSetBits = std::uint32_t; -#endif - -//**************************************************************************** -// Log-base-2 calculations ... -#ifndef QF_LOG2 - extern "C" std::uint_fast8_t QF_LOG2(QPSetBits x) noexcept; -#endif // QF_LOG2 - -//**************************************************************************** -#if (QF_MAX_ACTIVE <= 32) -//! Priority Set of up to 32 elements */ -/// -/// The priority set represents the set of active objects that are ready to -/// run and need to be considered by the scheduling algorithm. The set is -/// capable of storing up to 32 priority levels. QP::QPSet is specifically -/// declared as a POD (Plain Old Data) for ease of initialization and -/// interfacing with plain "C" code. -/// -struct QPSet { - - QPSetBits volatile m_bits; //!< bitmask with a bit for each element - - //! Makes the priority set @p me_ empty. - void setEmpty(void) noexcept { - m_bits = 0U; - } - - //! Evaluates to true if the priority set is empty - bool isEmpty(void) const noexcept { - return (m_bits == 0U); - } - - //! Evaluates to true if the priority set is not empty - bool notEmpty(void) const noexcept { - return (m_bits != 0U); - } - - //! the function evaluates to TRUE if the priority set has the element n. - bool hasElement(std::uint_fast8_t const n) const noexcept { - return (m_bits & (1U << (n - 1U))) != 0U; - } - - //! insert element @p n into the set, n = 1..QF_MAX_ACTIVE - void insert(std::uint_fast8_t const n) noexcept { - m_bits |= (1U << (n - 1U)); - } - - //! remove element @p n from the set, n = 1..QF_MAX_ACTIVE - /// @note - /// intentionally misspelled ("rmove") to avoid collision with - /// the C++ standard library facility "remove" - void rmove(std::uint_fast8_t const n) noexcept { - m_bits &= - static_cast(~(static_cast(1) << (n - 1U))); - } - - std::uint_fast8_t findMax(void) const noexcept { - return QF_LOG2(m_bits); - } -}; - -#else // QF_MAX_ACTIVE > 32U - -//! Priority Set of up to 64 elements -/// -/// The priority set represents the set of active objects that are ready to -/// run and need to be considered by the scheduling algorithm. The set is -/// capable of storing up to 64 priority levels. QP::QPSet is specifically -/// declared as a POD (Plain Old Data) for ease of initialization and -/// interfacing with plain "C" code. -/// -struct QPSet { - - //! Two 32-bit bitmasks with a bit for each element - std::uint32_t volatile m_bits[2]; - - //! Makes the priority set @p me_ empty. - void setEmpty(void) noexcept { - m_bits[0] = 0U; - m_bits[1] = 0U; - } - - //! Evaluates to true if the priority set is empty - // the following logic avoids UB in volatile access for MISRA compliantce - bool isEmpty(void) const noexcept { - return (m_bits[0] == 0U) ? (m_bits[1] == 0U) : false; - } - - //! Evaluates to true if the priority set is not empty - // the following logic avoids UB in volatile access for MISRA compliantce - bool notEmpty(void) const noexcept { - return (m_bits[0] != 0U) ? true : (m_bits[1] != 0U); - } - - //! the function evaluates to TRUE if the priority set has the element n. - bool hasElement(std::uint_fast8_t const n) const noexcept { - return (n <= 32U) - ? ((m_bits[0] & (static_cast(1) << (n - 1U))) - != 0U) - : ((m_bits[1] & (static_cast(1) << (n - 33U))) - != 0U); - } - - //! insert element @p n into the set, n = 1..64 - void insert(std::uint_fast8_t const n) noexcept { - if (n <= 32U) { - m_bits[0] |= (static_cast(1) << (n - 1U)); - } - else { - m_bits[1] |= (static_cast(1) << (n - 33U)); - } - } - - //! remove element @p n from the set, n = 1..64 - /// @note - /// intentionally misspelled ("rmove") to avoid collision with - /// the C++ standard library facility "remove" - void rmove(std::uint_fast8_t const n) noexcept { - if (n <= 32U) { - (m_bits[0] &= ~(static_cast(1) << (n - 1U))); - } - else { - (m_bits[1] &= ~(static_cast(1) << (n - 33U))); - } - } - - //! find the maximum element in the set, returns zero if the set is empty - std::uint_fast8_t findMax(void) const noexcept { - return (m_bits[1] != 0U) - ? (QF_LOG2(m_bits[1]) + 32U) - : (QF_LOG2(m_bits[0])); - } -}; - -#endif // QF_MAX_ACTIVE - -} // namespace QP - -#endif // QPSET_HPP - diff --git a/src/qs.cpp b/src/qs.cpp deleted file mode 100644 index d980f2f..0000000 --- a/src/qs.cpp +++ /dev/null @@ -1,1051 +0,0 @@ -//$file${src::qs::qs.cpp} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv -// -// Model: qpcpp.qm -// File: ${src::qs::qs.cpp} -// -// This code has been generated by QM 5.2.5 . -// DO NOT EDIT THIS FILE MANUALLY. All your changes will be lost. -// -// This code is covered by the following QP license: -// License # : LicenseRef-QL-dual -// Issued to : Any user of the QP/C++ real-time embedded framework -// Framework(s) : qpcpp -// Support ends : 2023-12-31 -// License scope: -// -// Copyright (C) 2005 Quantum Leaps, LLC . -// -// 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: -// -// The terms of the closed source Quantum Leaps commercial licenses -// can be found at: -// -// Redistributions in source code must retain this top-level comment block. -// Plagiarizing this software to sidestep the license obligations is illegal. -// -// Contact information: -// -// -// -//$endhead${src::qs::qs.cpp} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -//! @file -//! @brief QS software tracing services - -#define QP_IMPL // this is QP implementation -#include "qs_port.hpp" // QS port -#include "qs_pkg.hpp" // QS package-scope internal interface -#include "qstamp.hpp" // QP time-stamp -#include "qassert.h" // QP assertions - -// unnamed namespace for local definitions with internal linkage -namespace { -Q_DEFINE_THIS_MODULE("qs") -} // unnamed namespace - -//$skip${QP_VERSION} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv -// Check for the minimum required QP version -#if (QP_VERSION < 700U) || (QP_VERSION != ((QP_RELEASE^4294967295U) % 0x3E8U)) -#error qpcpp version 7.0.0 or higher required -#endif -//$endskip${QP_VERSION} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -//$define${QS::QS-tx} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv -namespace QP { -namespace QS { - -//${QS::QS-tx::priv_} ........................................................ -QStx priv_; - -//${QS::QS-tx::initBuf} ...................................................... -void initBuf( - std::uint8_t * const sto, - std::uint_fast32_t const stoSize) noexcept -{ - // the provided buffer must be at least 8 bytes long - Q_REQUIRE_ID(100, stoSize > 8U); - - // This function initializes all the internal QS variables, so that the - // tracing can start correctly even if the startup code fails to clear - // any uninitialized data (as is required by the C Standard). - // - glbFilter_(-static_cast(QS_ALL_RECORDS));// all global filters OFF - locFilter_(static_cast(QS_ALL_IDS)); // all local filters ON - priv_.locFilter_AP = nullptr; // deprecated "AP-filter" - - priv_.buf = sto; - priv_.end = static_cast(stoSize); - priv_.head = 0U; - priv_.tail = 0U; - priv_.used = 0U; - priv_.seq = 0U; - priv_.chksum = 0U; - priv_.critNest = 0U; - - // produce an empty record to "flush" the QS trace buffer - beginRec_(QS_REC_NUM_(QS_EMPTY)); - endRec_(); - - // produce the Target info QS record - target_info_pre_(0xFFU); - - // wait with flushing after successful initialization (see QS_INIT()) -} - -//${QS::QS-tx::getByte} ...................................................... -std::uint16_t getByte() noexcept { - std::uint16_t ret; - if (priv_.used == 0U) { - ret = QS_EOD; // set End-Of-Data - } - else { - std::uint8_t const * const buf_ = priv_.buf; // put in a temporary - QSCtr tail_ = priv_.tail; // put in a temporary (register) - - // the byte to return - ret = static_cast(buf_[tail_]); - - ++tail_; // advance the tail - if (tail_ == priv_.end) { // tail wrap around? - tail_ = 0U; - } - priv_.tail = tail_; // update the tail - priv_.used = (priv_.used - 1U); // one less byte used - } - return ret; // return the byte or EOD -} - -//${QS::QS-tx::getBlock} ..................................................... -std::uint8_t const * getBlock(std::uint16_t * const pNbytes) noexcept { - QSCtr const used_ = priv_.used; // put in a temporary (register) - std::uint8_t *buf_; - - // any bytes used in the ring buffer? - if (used_ == 0U) { - *pNbytes = 0U; // no bytes available right now - buf_ = nullptr; // no bytes available right now - } - else { - QSCtr tail_ = priv_.tail; // put in a temporary (register) - QSCtr const end_ = priv_.end; // put in a temporary (register) - QSCtr n = static_cast(end_ - tail_); - if (n > used_) { - n = used_; - } - if (n > static_cast(*pNbytes)) { - n = static_cast(*pNbytes); - } - *pNbytes = static_cast(n); // n-bytes available - buf_ = priv_.buf; - buf_ = &buf_[tail_]; // the bytes are at the tail - - priv_.used = (priv_.used - n); - tail_ += n; - if (tail_ == end_) { - tail_ = 0U; - } - priv_.tail = tail_; - } - return buf_; -} - -//${QS::QS-tx::glbFilter_} ................................................... -void glbFilter_(std::int_fast16_t const filter) noexcept { - bool const isRemove = (filter < 0); - std::uint16_t const rec = isRemove - ? static_cast(-filter) - : static_cast(filter); - switch (rec) { - case QS_ALL_RECORDS: { - std::uint8_t const tmp = (isRemove ? 0x00U : 0xFFU); - std::uint_fast8_t i; - // set all global filters (partially unrolled loop) - for (i = 0U; i < Q_DIM(priv_.glbFilter); i += 4U) { - priv_.glbFilter[i ] = tmp; - priv_.glbFilter[i + 1U] = tmp; - priv_.glbFilter[i + 2U] = tmp; - priv_.glbFilter[i + 3U] = tmp; - } - if (isRemove) { - // leave the "not maskable" filters enabled, - // see qs.h, Miscellaneous QS records (not maskable) - // - priv_.glbFilter[0] = 0x01U; - priv_.glbFilter[6] = 0x40U; - priv_.glbFilter[7] = 0xFCU; - priv_.glbFilter[8] = 0x7FU; - } - else { - // never turn the last 3 records on (0x7D, 0x7E, 0x7F) - priv_.glbFilter[15] = 0x1FU; - } - break; - } - case QS_SM_RECORDS: - if (isRemove) { - priv_.glbFilter[0] &= - static_cast(~0xFEU & 0xFFU); - priv_.glbFilter[1] &= - static_cast(~0x03U & 0xFFU); - priv_.glbFilter[6] &= - static_cast(~0x80U & 0xFFU); - priv_.glbFilter[7] &= - static_cast(~0x03U & 0xFFU); - } - else { - priv_.glbFilter[0] |= 0xFEU; - priv_.glbFilter[1] |= 0x03U; - priv_.glbFilter[6] |= 0x80U; - priv_.glbFilter[7] |= 0x03U; - } - break; - case QS_AO_RECORDS: - if (isRemove) { - priv_.glbFilter[1] &= - static_cast(~0xFCU & 0xFFU); - priv_.glbFilter[2] &= - static_cast(~0x07U & 0xFFU); - priv_.glbFilter[5] &= - static_cast(~0x20U & 0xFFU); - } - else { - priv_.glbFilter[1] |= 0xFCU; - priv_.glbFilter[2] |= 0x07U; - priv_.glbFilter[5] |= 0x20U; - } - break; - case QS_EQ_RECORDS: - if (isRemove) { - priv_.glbFilter[2] &= - static_cast(~0x78U & 0xFFU); - priv_.glbFilter[5] &= - static_cast(~0x40U & 0xFFU); - } - else { - priv_.glbFilter[2] |= 0x78U; - priv_.glbFilter[5] |= 0x40U; - } - break; - case QS_MP_RECORDS: - if (isRemove) { - priv_.glbFilter[3] &= - static_cast(~0x03U & 0xFFU); - priv_.glbFilter[5] &= - static_cast(~0x80U & 0xFFU); - } - else { - priv_.glbFilter[3] |= 0x03U; - priv_.glbFilter[5] |= 0x80U; - } - break; - case QS_QF_RECORDS: - if (isRemove) { - priv_.glbFilter[2] &= - static_cast(~0x80U & 0xFFU); - priv_.glbFilter[3] &= - static_cast(~0xFCU & 0xFFU); - priv_.glbFilter[4] &= - static_cast(~0xC0U & 0xFFU); - priv_.glbFilter[5] &= - static_cast(~0x1FU & 0xFFU); - } - else { - priv_.glbFilter[2] |= 0x80U; - priv_.glbFilter[3] |= 0xFCU; - priv_.glbFilter[4] |= 0xC0U; - priv_.glbFilter[5] |= 0x1FU; - } - break; - case QS_TE_RECORDS: - if (isRemove) { - priv_.glbFilter[4] &= - static_cast(~0x3FU & 0xFFU); - } - else { - priv_.glbFilter[4] |= 0x3FU; - } - break; - case QS_SC_RECORDS: - if (isRemove) { - priv_.glbFilter[6] &= - static_cast(~0x3FU & 0xFFU); - } - else { - priv_.glbFilter[6] |= 0x3FU; - } - break; - case QS_SEM_RECORDS: - if (isRemove) { - priv_.glbFilter[8] &= - static_cast(~0x80U & 0xFFU); - priv_.glbFilter[9] &= - static_cast(~0x07U & 0xFFU); - } - else { - priv_.glbFilter[8] |= 0x80U; - priv_.glbFilter[9] |= 0x07U; - } - break; - case QS_MTX_RECORDS: - if (isRemove) { - priv_.glbFilter[9] &= - static_cast(~0xF8U & 0xFFU); - priv_.glbFilter[10] &= - static_cast(~0x01U & 0xFFU); - } - else { - priv_.glbFilter[9] |= 0xF8U; - priv_.glbFilter[10] |= 0x01U; - } - break; - case QS_U0_RECORDS: - if (isRemove) { - priv_.glbFilter[12] &= - static_cast(~0xF0U & 0xFFU); - priv_.glbFilter[13] &= - static_cast(~0x01U & 0xFFU); - } - else { - priv_.glbFilter[12] |= 0xF0U; - priv_.glbFilter[13] |= 0x01U; - } - break; - case QS_U1_RECORDS: - if (isRemove) { - priv_.glbFilter[13] &= - static_cast(~0x3EU & 0xFFU); - } - else { - priv_.glbFilter[13] |= 0x3EU; - } - break; - case QS_U2_RECORDS: - if (isRemove) { - priv_.glbFilter[13] &= - static_cast(~0xC0U & 0xFFU); - priv_.glbFilter[14] &= - static_cast(~0x07U & 0xFFU); - } - else { - priv_.glbFilter[13] |= 0xC0U; - priv_.glbFilter[14] |= 0x07U; - } - break; - case QS_U3_RECORDS: - if (isRemove) { - priv_.glbFilter[14] &= - static_cast(~0xF8U & 0xFFU); - } - else { - priv_.glbFilter[14] |= 0xF8U; - } - break; - case QS_U4_RECORDS: - if (isRemove) { - priv_.glbFilter[15] &= 0x1FU; - } - else { - priv_.glbFilter[15] |= 0x1FU; - } - break; - case QS_UA_RECORDS: - if (isRemove) { - priv_.glbFilter[12] &= - static_cast(~0xF0U & 0xFFU); - priv_.glbFilter[13] = 0U; - priv_.glbFilter[14] = 0U; - priv_.glbFilter[15] &= - static_cast(~0x1FU & 0xFFU); - } - else { - priv_.glbFilter[12] |= 0xF0U; - priv_.glbFilter[13] |= 0xFFU; - priv_.glbFilter[14] |= 0xFFU; - priv_.glbFilter[15] |= 0x1FU; - } - break; - default: - // QS rec number can't exceed 0x7D, so no need for escaping - Q_ASSERT_ID(210, rec < 0x7DU); - - if (isRemove) { - priv_.glbFilter[rec >> 3U] - &= static_cast(~(1U << (rec & 7U)) & 0xFFU); - } - else { - priv_.glbFilter[rec >> 3U] - |= static_cast(1U << (rec & 7U)); - // never turn the last 3 records on (0x7D, 0x7E, 0x7F) - priv_.glbFilter[15] &= 0x1FU; - } - break; - } -} - -//${QS::QS-tx::locFilter_} ................................................... -void locFilter_(std::int_fast16_t const filter) noexcept { - bool const isRemove = (filter < 0); - std::uint16_t const qs_id = isRemove - ? static_cast(-filter) - : static_cast(filter); - std::uint8_t const tmp = (isRemove ? 0x00U : 0xFFU); - std::uint_fast8_t i; - switch (qs_id) { - case QS_ALL_IDS: - // set all global filters (partially unrolled loop) - for (i = 0U; i < Q_DIM(priv_.locFilter); i += 4U) { - priv_.locFilter[i ] = tmp; - priv_.locFilter[i + 1U] = tmp; - priv_.locFilter[i + 2U] = tmp; - priv_.locFilter[i + 3U] = tmp; - } - break; - case QS_AO_IDS: - for (i = 0U; i < 8U; i += 4U) { - priv_.locFilter[i ] = tmp; - priv_.locFilter[i + 1U] = tmp; - priv_.locFilter[i + 2U] = tmp; - priv_.locFilter[i + 3U] = tmp; - } - break; - case QS_EP_IDS: - i = 8U; - priv_.locFilter[i ] = tmp; - priv_.locFilter[i + 1U] = tmp; - break; - case QS_AP_IDS: - i = 12U; - priv_.locFilter[i ] = tmp; - priv_.locFilter[i + 1U] = tmp; - priv_.locFilter[i + 2U] = tmp; - priv_.locFilter[i + 3U] = tmp; - break; - default: - if (qs_id < 0x7FU) { - if (isRemove) { - priv_.locFilter[qs_id >> 3U] &= - static_cast( - ~(1U << (qs_id & 7U)) & 0xFFU); - } - else { - priv_.locFilter[qs_id >> 3U] - |= (1U << (qs_id & 7U)); - } - } - else { - Q_ERROR_ID(310); // incorrect qs_id - } - break; - } - priv_.locFilter[0] |= 0x01U; // leave QS_ID == 0 always on - -} - -//${QS::QS-tx::beginRec_} .................................................... -void beginRec_(std::uint_fast8_t const rec) noexcept { - std::uint8_t const b = priv_.seq + 1U; - std::uint8_t chksum_ = 0U; // reset the checksum - std::uint8_t * const buf_ = priv_.buf; // put in a temporary (register) - QSCtr head_ = priv_.head; // put in a temporary (register) - QSCtr const end_ = priv_.end; // put in a temporary (register) - - priv_.seq = b; // store the incremented sequence num - priv_.used = (priv_.used + 2U); // 2 bytes about to be added - - QS_INSERT_ESC_BYTE_(b) - - chksum_ += static_cast(rec); - QS_INSERT_BYTE_(static_cast(rec)) // no need for escaping - - priv_.head = head_; // save the head - priv_.chksum = chksum_; // save the checksum -} - -//${QS::QS-tx::endRec_} ...................................................... -void endRec_() noexcept { - std::uint8_t * const buf_ = priv_.buf; // put in a temporary (register) - QSCtr head_ = priv_.head; - QSCtr const end_ = priv_.end; - std::uint8_t b = priv_.chksum; - b ^= 0xFFU; // invert the bits in the checksum - - priv_.used = (priv_.used + 2U); // 2 bytes about to be added - - if ((b != QS_FRAME) && (b != QS_ESC)) { - QS_INSERT_BYTE_(b) - } - else { - QS_INSERT_BYTE_(QS_ESC) - QS_INSERT_BYTE_(b ^ QS_ESC_XOR) - priv_.used = (priv_.used + 1U); // account for the ESC byte - } - - QS_INSERT_BYTE_(QS_FRAME) // do not escape this QS_FRAME - - priv_.head = head_; // save the head - if (priv_.used > end_) { // overrun over the old data? - priv_.used = end_; // the whole buffer is used - priv_.tail = head_; // shift the tail to the old data - } -} - -//${QS::QS-tx::u8_raw_} ...................................................... -void u8_raw_(std::uint8_t const d) noexcept { - std::uint8_t chksum_ = priv_.chksum; // put in a temporary (register) - std::uint8_t * const buf_ = priv_.buf; // put in a temporary (register) - QSCtr head_ = priv_.head; // put in a temporary (register) - QSCtr const end_= priv_.end; // put in a temporary (register) - - priv_.used = (priv_.used + 1U); // 1 byte about to be added - QS_INSERT_ESC_BYTE_(d) - - priv_.head = head_; // save the head - priv_.chksum = chksum_; // save the checksum -} - -//${QS::QS-tx::u8u8_raw_} .................................................... -void u8u8_raw_( - std::uint8_t const d1, - std::uint8_t const d2) noexcept -{ - std::uint8_t chksum_ = priv_.chksum; // put in a temporary (register) - std::uint8_t * const buf_ = priv_.buf; // put in a temporary (register) - QSCtr head_ = priv_.head; // put in a temporary (register) - QSCtr const end_= priv_.end; // put in a temporary (register) - - priv_.used = (priv_.used + 2U); // 2 bytes about to be added - QS_INSERT_ESC_BYTE_(d1) - QS_INSERT_ESC_BYTE_(d2) - - priv_.head = head_; // save the head - priv_.chksum = chksum_; // save the checksum -} - -//${QS::QS-tx::u16_raw_} ..................................................... -void u16_raw_(std::uint16_t d) noexcept { - std::uint8_t b = static_cast(d); - std::uint8_t chksum_ = priv_.chksum; // put in a temporary (register) - std::uint8_t * const buf_ = priv_.buf; // put in a temporary (register) - QSCtr head_ = priv_.head; // put in a temporary (register) - QSCtr const end_= priv_.end; // put in a temporary (register) - - priv_.used = (priv_.used + 2U); // 2 bytes about to be added - - QS_INSERT_ESC_BYTE_(b) - - d >>= 8U; - b = static_cast(d); - QS_INSERT_ESC_BYTE_(b) - - priv_.head = head_; // save the head - priv_.chksum = chksum_; // save the checksum -} - -//${QS::QS-tx::u32_raw_} ..................................................... -void u32_raw_(std::uint32_t d) noexcept { - std::uint8_t chksum_ = priv_.chksum; // put in a temporary (register) - std::uint8_t * const buf_ = priv_.buf; // put in a temporary (register) - QSCtr head_ = priv_.head; // put in a temporary (register) - QSCtr const end_= priv_.end; // put in a temporary (register) - - priv_.used = (priv_.used + 4U); // 4 bytes about to be added - for (std::uint_fast8_t i = 4U; i != 0U; --i) { - std::uint8_t const b = static_cast(d); - QS_INSERT_ESC_BYTE_(b) - d >>= 8U; - } - - priv_.head = head_; // save the head - priv_.chksum = chksum_; // save the checksum -} - -//${QS::QS-tx::obj_raw_} ..................................................... -void obj_raw_(void const * const obj) noexcept { - #if (QS_OBJ_PTR_SIZE == 1U) - u8_raw_(reinterpret_cast(obj)); - #elif (QS_OBJ_PTR_SIZE == 2U) - u16_raw_(reinterpret_cast(obj)); - #elif (QS_OBJ_PTR_SIZE == 4U) - u32_raw_(reinterpret_cast(obj)); - #elif (QS_OBJ_PTR_SIZE == 8U) - u64_raw_(reinterpret_cast(obj)); - #else - u32_raw_(reinterpret_cast(obj)); - #endif -} - -//${QS::QS-tx::str_raw_} ..................................................... -void str_raw_(char const * s) noexcept { - std::uint8_t b = static_cast(*s); - std::uint8_t chksum_ = priv_.chksum; // put in a temporary (register) - std::uint8_t * const buf_ = priv_.buf; // put in a temporary (register) - QSCtr head_ = priv_.head; // put in a temporary (register) - QSCtr const end_= priv_.end; // put in a temporary (register) - QSCtr used_ = priv_.used; // put in a temporary (register) - - while (b != 0U) { - chksum_ += b; // update checksum - QS_INSERT_BYTE_(b) // ASCII characters don't need escaping - ++s; - b = static_cast(*s); - ++used_; - } - QS_INSERT_BYTE_(0U) // zero-terminate the string - ++used_; - - priv_.head = head_; // save the head - priv_.chksum = chksum_; // save the checksum - priv_.used = used_; // save # of used buffer space -} - -//${QS::QS-tx::u8_fmt_} ...................................................... -void u8_fmt_( - std::uint8_t const format, - std::uint8_t const d) noexcept -{ - std::uint8_t chksum_ = priv_.chksum; // put in a temporary (register) - std::uint8_t *const buf_ = priv_.buf; // put in a temporary (register) - QSCtr head_ = priv_.head; // put in a temporary (register) - QSCtr const end_= priv_.end; // put in a temporary (register) - - priv_.used = (priv_.used + 2U); // 2 bytes about to be added - - QS_INSERT_ESC_BYTE_(format) - QS_INSERT_ESC_BYTE_(d) - - priv_.head = head_; // save the head - priv_.chksum = chksum_; // save the checksum -} - -//${QS::QS-tx::u16_fmt_} ..................................................... -void u16_fmt_( - std::uint8_t format, - std::uint16_t d) noexcept -{ - std::uint8_t chksum_ = priv_.chksum; // put in a temporary (register) - std::uint8_t * const buf_ = priv_.buf; // put in a temporary (register) - QSCtr head_ = priv_.head; // put in a temporary (register) - QSCtr const end_= priv_.end; // put in a temporary (register) - - priv_.used = (priv_.used + 3U); // 3 bytes about to be added - - QS_INSERT_ESC_BYTE_(format) - - format = static_cast(d); - QS_INSERT_ESC_BYTE_(format) - - d >>= 8U; - format = static_cast(d); - QS_INSERT_ESC_BYTE_(format) - - priv_.head = head_; // save the head - priv_.chksum = chksum_; // save the checksum -} - -//${QS::QS-tx::u32_fmt_} ..................................................... -void u32_fmt_( - std::uint8_t format, - std::uint32_t d) noexcept -{ - std::uint8_t chksum_ = priv_.chksum; // put in a temporary (register) - std::uint8_t * const buf_= priv_.buf; // put in a temporary (register) - QSCtr head_ = priv_.head; // put in a temporary (register) - QSCtr const end_= priv_.end; // put in a temporary (register) - - priv_.used = (priv_.used + 5U); // 5 bytes about to be added - QS_INSERT_ESC_BYTE_(format) // insert the format byte - - for (std::uint_fast8_t i = 4U; i != 0U; --i) { - format = static_cast(d); - QS_INSERT_ESC_BYTE_(format) - d >>= 8U; - } - - priv_.head = head_; // save the head - priv_.chksum = chksum_; // save the checksum -} - -//${QS::QS-tx::str_fmt_} ..................................................... -void str_fmt_(char const * s) noexcept { - std::uint8_t b = static_cast(*s); - std::uint8_t chksum_ = static_cast( - priv_.chksum + static_cast(STR_T)); - std::uint8_t * const buf_= priv_.buf; // put in a temporary (register) - QSCtr head_ = priv_.head; // put in a temporary (register) - QSCtr const end_= priv_.end; // put in a temporary (register) - QSCtr used_ = priv_.used; // put in a temporary (register) - - used_ += 2U; // the format byte and the terminating-0 - - QS_INSERT_BYTE_(static_cast(STR_T)) - while (b != 0U) { - // ASCII characters don't need escaping - chksum_ += b; // update checksum - QS_INSERT_BYTE_(b) - ++s; - b = static_cast(*s); - ++used_; - } - QS_INSERT_BYTE_(0U) // zero-terminate the string - - priv_.head = head_; // save the head - priv_.chksum = chksum_; // save the checksum - priv_.used = used_; // save # of used buffer space -} - -//${QS::QS-tx::mem_fmt_} ..................................................... -void mem_fmt_( - std::uint8_t const * blk, - std::uint8_t size) noexcept -{ - std::uint8_t b = static_cast(MEM_T); - std::uint8_t chksum_ = priv_.chksum + b; - std::uint8_t * const buf_= priv_.buf; // put in a temporary (register) - QSCtr head_ = priv_.head; // put in a temporary (register) - QSCtr const end_= priv_.end; // put in a temporary (register) - - priv_.used = (priv_.used + size + 2U); // size+2 bytes to be added - - QS_INSERT_BYTE_(b) - QS_INSERT_ESC_BYTE_(size) - - // output the 'size' number of bytes - for (; size != 0U; --size) { - b = *blk; - QS_INSERT_ESC_BYTE_(b) - ++blk; - } - - priv_.head = head_; // save the head - priv_.chksum = chksum_; // save the checksum -} - -//${QS::QS-tx::sig_dict_pre_} ................................................ -void sig_dict_pre_( - enum_t const sig, - void const * const obj, - char const * const name) noexcept -{ - QS_CRIT_STAT_ - - QS_CRIT_E_(); - beginRec_(static_cast(QS_SIG_DICT)); - QS_SIG_PRE_(sig); - QS_OBJ_PRE_(obj); - QS_STR_PRE_((*name == '&') ? &name[1] : name); - endRec_(); - QS_CRIT_X_(); - onFlush(); -} - -//${QS::QS-tx::obj_dict_pre_} ................................................ -void obj_dict_pre_( - void const * const obj, - char const * const name) noexcept -{ - QS_CRIT_STAT_ - - QS_CRIT_E_(); - beginRec_(static_cast(QS_OBJ_DICT)); - QS_OBJ_PRE_(obj); - QS_STR_PRE_((*name == '&') ? &name[1] : name); - endRec_(); - QS_CRIT_X_(); - onFlush(); -} - -//${QS::QS-tx::obj_arr_dict_pre_} ............................................ -void obj_arr_dict_pre_( - void const * const obj, - std::uint_fast16_t const idx, - char const * const name) noexcept -{ - Q_REQUIRE_ID(400, idx < 1000U); - - // format idx into a char buffer as "xxx\0" - std::uint8_t idx_str[4]; - std::uint_fast16_t tmp = idx; - std::uint8_t i; - idx_str[3] = 0U; // zero-terminate - idx_str[2] = static_cast( - static_cast('0') + (tmp % 10U)); - tmp /= 10U; - idx_str[1] = static_cast( - static_cast('0') + (tmp % 10U)); - if (idx_str[1] == static_cast('0')) { - i = 2U; - } - else { - tmp /= 10U; - idx_str[0] = static_cast( - static_cast('0') + (tmp % 10U)); - if (idx_str[0] == static_cast('0')) { - i = 1U; - } - else { - i = 0U; - } - } - - QS_CRIT_STAT_ - std::uint8_t j = ((*name == '&') ? 1U : 0U); - - QS_CRIT_E_(); - beginRec_(static_cast(QS_OBJ_DICT)); - QS_OBJ_PRE_(obj); - for (; name[j] != '\0'; ++j) { - QS_U8_PRE_(name[j]); - if (name[j] == '[') { - ++j; - break; - } - } - for (; idx_str[i] != 0U; ++i) { - QS_U8_PRE_(idx_str[i]); - } - // skip chars until ']' - for (; name[j] != '\0'; ++j) { - if (name[j] == ']') { - break; - } - } - for (; name[j] != '\0'; ++j) { - QS_U8_PRE_(name[j]); - } - QS_U8_PRE_(0U); // zero-terminate - endRec_(); - QS_CRIT_X_(); - onFlush(); -} - -//${QS::QS-tx::fun_dict_pre_} ................................................ -void fun_dict_pre_( - QSpyFunPtr fun, - char const * const name) noexcept -{ - QS_CRIT_STAT_ - - QS_CRIT_E_(); - beginRec_(static_cast(QS_FUN_DICT)); - QS_FUN_PRE_(fun); - QS_STR_PRE_((*name == '&') ? &name[1] : name); - endRec_(); - QS_CRIT_X_(); - onFlush(); -} - -//${QS::QS-tx::usr_dict_pre_} ................................................ -void usr_dict_pre_( - enum_t const rec, - char const * const name) noexcept -{ - QS_CRIT_STAT_ - - QS_CRIT_E_(); - beginRec_(static_cast(QS_USR_DICT)); - QS_U8_PRE_(rec); - QS_STR_PRE_(name); - endRec_(); - QS_CRIT_X_(); - onFlush(); -} - -//${QS::QS-tx::enum_dict_pre_} ............................................... -void enum_dict_pre_( - enum_t const value, - std::uint8_t const group, - char const * const name) noexcept -{ - QS_CRIT_STAT_ - - QS_CRIT_E_(); - beginRec_(static_cast(QS_ENUM_DICT)); - QS_2U8_PRE_(static_cast(value), group); - QS_STR_PRE_(name); - endRec_(); - QS_CRIT_X_(); - onFlush(); -} - -//${QS::QS-tx::assertion_pre_} ............................................... -void assertion_pre_( - char const * const module, - int_t const loc, - std::uint32_t const delay) noexcept -{ - QS_BEGIN_NOCRIT_PRE_(QP::QS_ASSERT_FAIL, 0U) - QS_TIME_PRE_(); - QS_U16_PRE_(loc); - QS_STR_PRE_((module != nullptr) ? module : "?"); - QS_END_NOCRIT_PRE_() - QP::QS::onFlush(); - for (std::uint32_t volatile ctr = delay; ctr > 0U; ) { - ctr = (ctr - 1U); - } - QP::QS::onCleanup(); -} - -//${QS::QS-tx::crit_entry_pre_} .............................................. -void crit_entry_pre_() noexcept { - QS_BEGIN_NOCRIT_PRE_(QP::QS_QF_CRIT_ENTRY, 0U) - QS_TIME_PRE_(); - QS::priv_.critNest = (QS::priv_.critNest + 1U); - QS_U8_PRE_(QS::priv_.critNest); - QS_END_NOCRIT_PRE_() -} - -//${QS::QS-tx::crit_exit_pre_} ............................................... -void crit_exit_pre_() noexcept { - QS_BEGIN_NOCRIT_PRE_(QP::QS_QF_CRIT_EXIT, 0U) - QS_TIME_PRE_(); - QS_U8_PRE_(QS::priv_.critNest); - QS::priv_.critNest = (QS::priv_.critNest - 1U); - QS_END_NOCRIT_PRE_() -} - -//${QS::QS-tx::isr_entry_pre_} ............................................... -void isr_entry_pre_( - std::uint8_t const isrnest, - std::uint8_t const prio) noexcept -{ - QS_BEGIN_NOCRIT_PRE_(QP::QS_QF_ISR_ENTRY, 0U) - QS_TIME_PRE_(); - QS_U8_PRE_(isrnest); - QS_U8_PRE_(prio); - QS_END_NOCRIT_PRE_() -} - -//${QS::QS-tx::isr_exit_pre_} ................................................ -void isr_exit_pre_( - std::uint8_t const isrnest, - std::uint8_t const prio) noexcept -{ - QS_BEGIN_NOCRIT_PRE_(QP::QS_QF_ISR_EXIT, 0U) - QS_TIME_PRE_(); - QS_U8_PRE_(isrnest); - QS_U8_PRE_(prio); - QS_END_NOCRIT_PRE_() -} - -//${QS::QS-tx::target_info_pre_} ............................................. -void target_info_pre_(std::uint8_t const isReset) { - static constexpr std::uint8_t ZERO = static_cast('0'); - static std::uint8_t const * const TIME = - reinterpret_cast(&BUILD_TIME[0]); - static std::uint8_t const * const DATE = - reinterpret_cast(&BUILD_DATE[0]); - - QS::beginRec_(static_cast(QS_TARGET_INFO)); - QS::u8_raw_(isReset); - - static union { - std::uint16_t u16; - std::uint8_t u8[2]; - } endian_test; - endian_test.u16 = 0x0102U; - // big endian ? add the 0x8000U flag - QS_U16_PRE_(((endian_test.u8[0] == 0x01U) - ? (0x8000U | QP_VERSION) - : QP_VERSION)); // target endianness + version number - - // send the object sizes... - QS::u8_raw_(Q_SIGNAL_SIZE - | static_cast(QF_EVENT_SIZ_SIZE << 4U)); - - #ifdef QF_EQUEUE_CTR_SIZE - QS::u8_raw_(QF_EQUEUE_CTR_SIZE - | static_cast(QF_TIMEEVT_CTR_SIZE << 4U)); - #else - QS::u8_raw_(static_cast(QF_TIMEEVT_CTR_SIZE << 4U)); - #endif // ifdef QF_EQUEUE_CTR_SIZE - - #ifdef QF_MPOOL_CTR_SIZE - QS::u8_raw_(QF_MPOOL_SIZ_SIZE - | static_cast(QF_MPOOL_CTR_SIZE << 4U)); - #else - QS::u8_raw_(0U); - #endif // ifdef QF_MPOOL_CTR_SIZE - - QS::u8_raw_(QS_OBJ_PTR_SIZE | (QS_FUN_PTR_SIZE << 4U)); - QS::u8_raw_(QS_TIME_SIZE); - - // send the limits... - QS::u8_raw_(QF_MAX_ACTIVE); - QS::u8_raw_(QF_MAX_EPOOL | (QF_MAX_TICK_RATE << 4U)); - - // send the build time in three bytes (sec, min, hour)... - QS::u8_raw_((10U * (TIME[6] - ZERO)) + (TIME[7] - ZERO)); - QS::u8_raw_((10U * (TIME[3] - ZERO)) + (TIME[4] - ZERO)); - if (BUILD_TIME[0] == static_cast(' ')) { - QS::u8_raw_(TIME[1] - ZERO); - } - else { - QS::u8_raw_((10U * (TIME[0] - ZERO)) + (TIME[1] - ZERO)); - } - - // send the build date in three bytes (day, month, year) ... - if (BUILD_DATE[4] == static_cast(' ')) { - QS::u8_raw_(DATE[5] - ZERO); - } - else { - QS::u8_raw_((10U * (DATE[4] - ZERO)) + (DATE[5] - ZERO)); - } - // convert the 3-letter month to a number 1-12 ... - std::uint8_t b; - switch (DATE[0] + DATE[1] + DATE[2]) { - case 'J' + 'a' +'n': - b = 1U; - break; - case 'F' + 'e' + 'b': - b = 2U; - break; - case 'M' + 'a' +'r': - b = 3U; - break; - case 'A' + 'p' + 'r': - b = 4U; - break; - case 'M' + 'a' + 'y': - b = 5U; - break; - case 'J' + 'u' + 'n': - b = 6U; - break; - case 'J' + 'u' + 'l': - b = 7U; - break; - case 'A' + 'u' + 'g': - b = 8U; - break; - case 'S' + 'e' + 'p': - b = 9U; - break; - case 'O' + 'c' + 't': - b = 10U; - break; - case 'N' + 'o' + 'v': - b = 11U; - break; - case 'D' + 'e' + 'c': - b = 12U; - break; - default: - b = 0U; - break; - } - QS::u8_raw_(b); // store the month - QS::u8_raw_((10U * (DATE[9] - ZERO)) + (DATE[10] - ZERO)); - QS::endRec_(); - -} - -} // namespace QS -} // namespace QP -//$enddef${QS::QS-tx} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/src/qs.hpp b/src/qs.hpp deleted file mode 100644 index ef7ee1b..0000000 --- a/src/qs.hpp +++ /dev/null @@ -1,1613 +0,0 @@ -//$file${include::qs.hpp} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv -// -// Model: qpcpp.qm -// File: ${include::qs.hpp} -// -// This code has been generated by QM 5.2.5 . -// DO NOT EDIT THIS FILE MANUALLY. All your changes will be lost. -// -// This code is covered by the following QP license: -// License # : LicenseRef-QL-dual -// Issued to : Any user of the QP/C++ real-time embedded framework -// Framework(s) : qpcpp -// Support ends : 2023-12-31 -// License scope: -// -// Copyright (C) 2005 Quantum Leaps, LLC . -// -// 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: -// -// The terms of the closed source Quantum Leaps commercial licenses -// can be found at: -// -// Redistributions in source code must retain this top-level comment block. -// Plagiarizing this software to sidestep the license obligations is illegal. -// -// Contact information: -// -// -// -//$endhead${include::qs.hpp} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -//! @file -//! @brief QS/C++ platform-independent public interface. - -#ifndef QS_HPP_ -#define QS_HPP_ - -#ifndef Q_SPY - #error "Q_SPY must be defined to include qs.hpp" -#endif - -//============================================================================ -// Global namespace... -//$declare${QS-config} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv - -//${QS-config::QS_CTR_SIZE} .................................................. -#ifndef QS_CTR_SIZE -//! The size [bytes] of the internal QS buffer-counters. Valid values: 2U or 4U; -//! default 2U. -//! -//! @details -//! This macro can be defined in the QS port file (qs_port.hpp) to -//! configure the QS::QSCtr type. Here the macro is not defined so the -//! default of 2 byte is chosen. -#define QS_CTR_SIZE 2U -#endif // ndef QS_CTR_SIZE - -//${QS-config::QS_CTR_SIZE defined incorrectly,~} ............................ -#if (QS_CTR_SIZE != 2U) && (QS_CTR_SIZE != 4U) -#error QS_CTR_SIZE defined incorrectly, expected 2U or 4U; -#endif // (QS_CTR_SIZE != 2U) && (QS_CTR_SIZE != 4U) - -//${QS-config::QS_TIME_SIZE} ................................................. -#ifndef QS_TIME_SIZE -//! Size (in bytes) of the QS time stamp -//! -//! @details -//! This macro can be defined in the QS port file (qs_port.hpp) to configure -//! the QP::QSTimeCtr type. Valid values 1U, 2U, 4U. Default 4U. -#define QS_TIME_SIZE 4U -#endif // ndef QS_TIME_SIZE - -//${QS-config::QS_TIME_SIZE defined incorrectly~} ............................ -#if (QS_TIME_SIZE != 1U) && (QS_TIME_SIZE != 2U) && (QS_TIME_SIZE != 4U) -#error QS_TIME_SIZE defined incorrectly, expected 1U, 2U, or 4U; -#endif // (QS_TIME_SIZE != 1U) && (QS_TIME_SIZE != 2U) && (QS_TIME_SIZE != 4U) -//$enddecl${QS-config} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -//============================================================================ -//$declare${QS} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv -namespace QP { - -//${QS::QSCtr} ............................................................... -#if (QS_CTR_SIZE == 2U) -//! QS ring buffer counter and offset type -using QSCtr = std::uint16_t; -#endif // (QS_CTR_SIZE == 2U) - -//${QS::QSCtr} ............................................................... -#if (QS_CTR_SIZE == 4U) -using QSCtr = std::uint32_t; -#endif // (QS_CTR_SIZE == 4U) - -//${QS::QSTimeCtr} ........................................................... -#if (QS_TIME_SIZE == 4U) -//! QS time stamp type, which determines the dynamic range of QS time stamps -using QSTimeCtr = std::uint32_t; -#endif // (QS_TIME_SIZE == 4U) - -//${QS::QSTimeCtr} ........................................................... -#if (QS_TIME_SIZE == 2U) -using QSTimeCtr = std::uint16_t; -#endif // (QS_TIME_SIZE == 2U) - -//${QS::QSTimeCtr} ........................................................... -#if (QS_TIME_SIZE == 1U) -using QSTimeCtr = std::uint8_t; -#endif // (QS_TIME_SIZE == 1U) - -//${QS::QSFun} ............................................................... -#if (QS_FUN_PTR_SIZE == 4U) -//! QS function pointer type (for serializing function pointers) -using QSFun = std::uint32_t; -#endif // (QS_FUN_PTR_SIZE == 4U) - -//${QS::QSFun} ............................................................... -#if (QS_FUN_PTR_SIZE == 8U) -using QSFun = std::uint64_t; -#endif // (QS_FUN_PTR_SIZE == 8U) - -//${QS::QSFun} ............................................................... -#if (QS_FUN_PTR_SIZE == 2U) -using QSFun = std::uint16_t; -#endif // (QS_FUN_PTR_SIZE == 2U) - -//${QS::QSFun} ............................................................... -#if (QS_FUN_PTR_SIZE == 1U) -using QSFun = std::uint8_t; -#endif // (QS_FUN_PTR_SIZE == 1U) - -//${QS::QSpyPre} ............................................................. -//! QS pre-defined record types (TX channel) -//! -//! @details -//! This enumeration specifies the record types used in the QP components. -//! You can specify your own record types starting from QP::QS_USER offset. -//! Currently, the maximum of all records cannot exceed 125. -//! -//! @note -//! The QS records labeled as "not maskable" are always enabled and cannot -//! be turend off with the QS_GLB_FILTER() macro. Other QS trace records -//! can be disabled by means of the "global filters" -//! -//! @sa QS_GLB_FILTER() macro -enum QSpyPre : std::int8_t { - // [0] QS session (not maskable) - QS_EMPTY, //!< QS record for cleanly starting a session - - // [1] SM records - QS_QEP_STATE_ENTRY, //!< a state was entered - QS_QEP_STATE_EXIT, //!< a state was exited - QS_QEP_STATE_INIT, //!< an initial transition was taken in a state - QS_QEP_INIT_TRAN, //!< the top-most initial transition was taken - QS_QEP_INTERN_TRAN, //!< an internal transition was taken - QS_QEP_TRAN, //!< a regular transition was taken - QS_QEP_IGNORED, //!< an event was ignored (silently discarded) - QS_QEP_DISPATCH, //!< an event was dispatched (begin of RTC step) - QS_QEP_UNHANDLED, //!< an event was unhandled 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 found next task to execute - QS_SCHED_IDLE, //!< scheduler became idle - - // [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 number of predefined signals -}; - -//${QS::QSpyGroups} .......................................................... -//! QS record groups for QS_GLB_FILTER() -enum QSpyGroups : std::int16_t { - QS_ALL_RECORDS = static_cast(0xF0U), //!< all QS records - QS_SM_RECORDS, //!< State Machine QS records - QS_AO_RECORDS, //!< Active Object QS records - QS_EQ_RECORDS, //!< Event Queues QS records - QS_MP_RECORDS, //!< Memory Pools QS records - QS_TE_RECORDS, //!< Time Events QS records - QS_QF_RECORDS, //!< QF QS records - QS_SC_RECORDS, //!< Scheduler QS records - QS_SEM_RECORDS, //!< Semaphore QS records - QS_MTX_RECORDS, //!< Mutex QS records - QS_U0_RECORDS, //!< User Group 100-104 records - QS_U1_RECORDS, //!< User Group 105-109 records - QS_U2_RECORDS, //!< User Group 110-114 records - QS_U3_RECORDS, //!< User Group 115-119 records - QS_U4_RECORDS, //!< User Group 120-124 records - QS_UA_RECORDS //!< All User records -}; - -//${QS::QSpyUserOffsets} ..................................................... -//! QS user record group offsets for QS_GLB_FILTER() -enum QSpyUserOffsets : std::int16_t { - QS_USER = 100, //!< the first record available to QS users - QS_USER0 = QS_USER, //!< offset for User Group 0 - QS_USER1 = QS_USER0 + 5, //!< offset of Group 1 - QS_USER2 = QS_USER1 + 5, //!< offset of Group 2 - QS_USER3 = QS_USER2 + 5, //!< offset of Group 3 - QS_USER4 = QS_USER3 + 5 //!< offset of Group 4 -}; - -//${QS::QSpyIdOffsets} ....................................................... -//! QS ID offsets for QS_LOC_FILTER() -enum QSpyIdOffsets : std::int16_t { - QS_AO_ID = 0, //!< offset for AO priorities - QS_EP_ID = 64, //!< offset for event-pool IDs - QS_EQ_ID = 80, //!< offset for event-queue IDs - QS_AP_ID = 96 //!< offset for Appl-spec IDs -}; - -//${QS::QSpyIdGroups} ........................................................ -//! QS ID groups for QS_LOC_FILTER() -enum QSpyIdGroups : std::int16_t { - QS_ALL_IDS = 0xF0, //!< all QS IDs - QS_AO_IDS = 0x80 + QS_AO_ID, //!< AO IDs (priorities) - QS_EP_IDS = 0x80 + QS_EP_ID, //!< event-pool IDs - QS_EQ_IDS = 0x80 + QS_EQ_ID, //!< event-queue IDs - QS_AP_IDS = 0x80 + QS_AP_ID //!< Application-specific IDs -}; - -//${QS::QSpyFunPtr} .......................................................... -//! function pointer type for fun_dict_pre_() -using QSpyFunPtr = void (*)(); - -//${QS::QSpyId} .............................................................. -//! QS ID type for applying local filtering -struct QSpyId { - std::uint8_t m_prio; //!< "priority" (qs_id) for the QS "local filter" - - //! get the "priority" (qs_id) from the QSpyId opbject - std::uint_fast8_t getPrio() const noexcept { - return static_cast(m_prio); - } -}; -namespace QS { - -//${QS::QS-tx::QStx} ......................................................... -//! QS software tracing, output QS-TX -class QStx { -public: - - //! global on/off QS filter - std::uint8_t glbFilter[16]; - - //! local on/off QS filter - std::uint8_t locFilter[16]; - - //! old local QS filter - // @deprecated - void const * locFilter_AP; - - //! pointer to the start of the QS-TX ring buffer - std::uint8_t * buf; - - //! offset of the end of the ring buffer - QSCtr end; - - //! offset to where next byte will be inserted - QSCtr volatile head; - - //! offset of where next record will be extracted - QSCtr volatile tail; - - //! number of bytes currently in the ring buffer - QSCtr volatile used; - - //! sequence number of the last inserted QS record - std::uint8_t volatile seq; - - //! checksum of the currently inserted record - std::uint8_t volatile chksum; - - //! critical section nesting level - std::uint8_t volatile critNest; - - //! flags for internal use - std::uint8_t flags; -}; // class QStx - -//${QS::QS-tx::QSType} ....................................................... -//! Enumerates data elements for app-specific trace records -enum QSType : std::uint8_t { - I8_ENUM_T, //!< signed 8-bit integer or enum format - U8_T, //!< unsigned 8-bit integer format - I16_T, //!< signed 16-bit integer format - U16_T, //!< unsigned 16-bit integer format - I32_T, //!< signed 32-bit integer format - U32_T, //!< unsigned 32-bit integer format - F32_T, //!< 32-bit floating point format - F64_T, //!< 64-bit floating point format - STR_T, //!< zero-terminated ASCII string format - MEM_T, //!< up to 255-bytes memory block format - SIG_T, //!< event signal format - OBJ_T, //!< object pointer format - FUN_T, //!< function pointer format - I64_T, //!< signed 64-bit integer format - U64_T //!< unsigned 64-bit integer format -}; - -//${QS::QS-tx::priv_} ........................................................ -//! the only instance of the QS-TX object (Singleton) -extern QStx priv_; - -//${QS::QS-tx::force_cast} ................................................... -//! template for forcing cast of member functions for function -//! dictionaries and test probes. -//! -//! @tparam T_OUT type of the returned representation -//! @tparam T_IN type of the provided representation -//! -//! @returns the binary representation of `T_IN` as `T_OUT` -template -inline T_OUT force_cast(T_IN in) { - union TCast { - T_IN in; - T_OUT out; - } u = { in }; - return u.out; -} - -//${QS::QS-tx::initBuf} ...................................................... -//! Initialize the QS data buffer -//! -//! @details -//! This function should be called from QP::QS::onStartup() to provide -//! QS with the data buffer. The first argument `sto[]` is the address -//! of the memory block, and the second argument `stoSize` is the size -//! of this block [in bytes]. Currently the size of the QS buffer cannot -//! exceed 64KB. -//! -//! @note -//! QS can work with quite small data buffers, but you will start losing -//! data if the buffer is too small for the bursts of tracing activity. -//! The right size of the buffer depends on the data production rate and -//! the data output rate. QS offers flexible filtering to reduce the data -//! production rate. -//! -//! @note -//! If the data output rate cannot keep up with the production rate, -//! QS will start overwriting the older data with newer data. This is -//! consistent with the "last-is-best" QS policy. The record sequence -//! counters and check sums on each record allow the QSPY host utility -//! to easily detect any data loss. -void initBuf( - std::uint8_t * const sto, - std::uint_fast32_t const stoSize) noexcept; - -//${QS::QS-tx::getByte} ...................................................... -//! Byte-oriented interface to the QS data buffer -//! -//! @details -//! This function delivers one byte at a time from the QS data buffer. -//! -//! @returns -//! the byte in the least-significant 8-bits of the 16-bit return -//! value if the byte is available. If no more data is available at the -//! time, the function returns ::QS_EOD (End-Of-Data). -//! -//! @note -//! QS::getByte() is NOT protected with a critical section. -std::uint16_t getByte() noexcept; - -//${QS::QS-tx::getBlock} ..................................................... -//! Block-oriented interface to the QS data buffer -//! -//! @details -//! This function delivers a contiguous block of data from the QS data -//! buffer. The function returns the pointer to the beginning of the -//! block, and writes the number of bytes in the block to the location -//! pointed to by `pNbytes`. The argument `pNbytes` is also used as -//! input to provide the maximum size of the data block that the caller -//! can accept. -//! -//! @returns -//! if data is available, the function returns pointer to the -//! contiguous block of data and sets the value pointed to by `pNbytes` -//! to the # available bytes. If data is available at the time the -//! function is called, the function returns NULL pointer and sets the -//! value pointed to by `pNbytes` to zero. -//! -//! @note -//! Only the NULL return from QP::QS::getBlock() indicates that the QS -//! buffer is empty at the time of the call. The non-NULL return often -//! means that the block is at the end of the buffer and you need to call -//! QP::QS::getBlock() again to obtain the rest of the data that -//! "wrapped around" to the beginning of the QS data buffer. -//! -//! @note QP::QS::getBlock() is __not__ protected with a critical section. -std::uint8_t const * getBlock(std::uint16_t * const pNbytes) noexcept; - -//${QS::QS-tx::glbFilter_} ................................................... -//! Set/clear the global Filter for a given QS record -//! or a group of records -//! -//! @details -//! This function sets up the QS filter to enable record types specified -//! in the `filter` parameter. The value #QS_ALL_RECORDS specifies to -//! filter-in all records. This function should be called indirectly -//! through the macro QS_GLB_FILTER() -//! -//! @param[in] filter the QS record-d or group to enable in the filter, -//! if positive or disable, if negative. The record-id -//! numbers must be in the range -127..127. -//! @note -//! Filtering based on the record-type is only the first layer of -//! filtering. The second layer is based on the object-type. Both filter -//! layers must be enabled for the QS record to be inserted in the -//! QS buffer. -//! -//! @sa QP::QS::locFilter_() -void glbFilter_(std::int_fast16_t const filter) noexcept; - -//${QS::QS-tx::locFilter_} ................................................... -//! Set/clear the local Filter for a given object-id -//! or a group of object-ids -//! -//! @details -//! This function sets up the local QS filter to enable or disable the -//! given QS object-id or a group of object-ids @a filter. -//! This function should be called indirectly through the macro -//! QS_LOC_FILTER() -//! -//! @param[in] filter the QS object-id or group to enable in the filter, -//! if positive or disable, if negative. The qs_id numbers -//! must be in the range 1..127. -//! @note -//! Filtering based on the object-id (local filter) is the second layer -//! of filtering. The first layer is based on the QS record-type (global -//! filter). Both filter layers must be enabled for the QS record to be -//! inserted into the QS buffer. -//! -//! @sa QP::QS::glbFilter_() -void locFilter_(std::int_fast16_t const filter) noexcept; - -//${QS::QS-tx::doOutput} ..................................................... -//! Perform the QS-TX output (implemented in some QS ports) -void doOutput(); - -//${QS::QS-tx::beginRec_} .................................................... -//! Mark the begin of a QS record `rec` -//! -//! @details -//! This function must be called at the beginning of each QS record. -//! This function should be called indirectly through the macro -//! QS_BEGIN_ID(), or QS_BEGIN_NOCRIT(), depending if it's called in -//! a normal code or from a critical section. -void beginRec_(std::uint_fast8_t const rec) noexcept; - -//${QS::QS-tx::endRec_} ...................................................... -//! Mark the end of a QS record `rec` -//! -//! @details -//! This function must be called at the end of each QS record. -//! This function should be called indirectly through the macro QS_END(), -//! or QS_END_NOCRIT(), depending if it's called in a normal code or from -//! a critical section. -void endRec_() noexcept; - -//${QS::QS-tx::u8_raw_} ...................................................... -//! output std::uint8_t data element without format information -void u8_raw_(std::uint8_t const d) noexcept; - -//${QS::QS-tx::u8u8_raw_} .................................................... -//! output two std::uint8_t data elements without format information -void u8u8_raw_( - std::uint8_t const d1, - std::uint8_t const d2) noexcept; - -//${QS::QS-tx::u16_raw_} ..................................................... -//! Output std::uint16_t data element without format information -void u16_raw_(std::uint16_t d) noexcept; - -//${QS::QS-tx::u32_raw_} ..................................................... -//! Output std::uint32_t data element without format information -void u32_raw_(std::uint32_t d) noexcept; - -//${QS::QS-tx::obj_raw_} ..................................................... -//! Output object pointer data element without format information -void obj_raw_(void const * const obj) noexcept; - -//${QS::QS-tx::str_raw_} ..................................................... -//! Output zero-terminated ASCII string element without format information -void str_raw_(char const * s) noexcept; - -//${QS::QS-tx::u8_fmt_} ...................................................... -//! Output std::uint8_t data element with format information -//! @sa QS_U8(), QS_I8() -void u8_fmt_( - std::uint8_t const format, - std::uint8_t const d) noexcept; - -//${QS::QS-tx::u16_fmt_} ..................................................... -//! Output std::uint16_t data element with format information -//! @sa QS_U16(), QS_I16() -void u16_fmt_( - std::uint8_t format, - std::uint16_t d) noexcept; - -//${QS::QS-tx::u32_fmt_} ..................................................... -//! Output std::uint32_t data element with format information -//! @sa QS_U32(), QS_I32() -void u32_fmt_( - std::uint8_t format, - std::uint32_t d) noexcept; - -//${QS::QS-tx::str_fmt_} ..................................................... -//! Output zero-terminated ASCII string element with format information -//! @sa QS_STR() -void str_fmt_(char const * s) noexcept; - -//${QS::QS-tx::mem_fmt_} ..................................................... -//! Output memory block of up to 255-bytes with format information -//! @sa QS_MEM() -void mem_fmt_( - std::uint8_t const * blk, - std::uint8_t size) noexcept; - -//${QS::QS-tx::sig_dict_pre_} ................................................ -//! Output signal dictionary record -//! @sa QS_SIG_DICTIONARY() -void sig_dict_pre_( - enum_t const sig, - void const * const obj, - char const * const name) noexcept; - -//${QS::QS-tx::obj_dict_pre_} ................................................ -//! Output object dictionary record -//! @sa QS_OBJ_DICTIONARY() -void obj_dict_pre_( - void const * const obj, - char const * const name) noexcept; - -//${QS::QS-tx::obj_arr_dict_pre_} ............................................ -//! Output predefined object-array dictionary record -//! @sa QS_OBJ_ARR_DICTIONARY() -void obj_arr_dict_pre_( - void const * const obj, - std::uint_fast16_t const idx, - char const * const name) noexcept; - -//${QS::QS-tx::fun_dict_pre_} ................................................ -//! Output function dictionary record -//! @sa QS_FUN_DICTIONARY() -void fun_dict_pre_( - QSpyFunPtr fun, - char const * const name) noexcept; - -//${QS::QS-tx::usr_dict_pre_} ................................................ -//! Output user dictionary record -//! @sa QS_USR_DICTIONARY() -void usr_dict_pre_( - enum_t const rec, - char const * const name) noexcept; - -//${QS::QS-tx::enum_dict_pre_} ............................................... -//! Output enumeration dictionary record -//! @sa QS_ENUM_DICTIONARY() -void enum_dict_pre_( - enum_t const value, - std::uint8_t const group, - char const * const name) noexcept; - -//${QS::QS-tx::assertion_pre_} ............................................... -//! internal function to produce the assertion failure trace record -//! @sa QS_ASSERTION() -void assertion_pre_( - char const * const module, - int_t const loc, - std::uint32_t const delay) noexcept; - -//${QS::QS-tx::crit_entry_pre_} .............................................. -//! internal function to produce the critical section entry record -//! @sa QF_QS_CRIT_ENTRY() -void crit_entry_pre_() noexcept; - -//${QS::QS-tx::crit_exit_pre_} ............................................... -//! internal function to produce the critical section exit record -//! @sa QF_QS_CRIT_EXIT() -void crit_exit_pre_() noexcept; - -//${QS::QS-tx::isr_entry_pre_} ............................................... -//! internal function to produce the ISR entry record -//! @sa QF_QS_ISR_ENTRY() -void isr_entry_pre_( - std::uint8_t const isrnest, - std::uint8_t const prio) noexcept; - -//${QS::QS-tx::isr_exit_pre_} ................................................ -//! internal function to produce the ISR exit record -//! @sa QF_QS_ISR_EXIT() -void isr_exit_pre_( - std::uint8_t const isrnest, - std::uint8_t const prio) noexcept; - -//${QS::QS-tx::target_info_pre_} ............................................. -//! Helper function to output the predefined Target-info trace record. -void target_info_pre_(std::uint8_t const isReset); - -//${QS::QS-tx::onStartup} .................................................... -//! Callback to startup the QS facility -bool onStartup(void const * arg); - -//${QS::QS-tx::onCleanup} .................................................... -//! Callback to cleanup the QS facility -void onCleanup(); - -//${QS::QS-tx::onFlush} ...................................................... -//! Callback to flush the QS trace data to the host -void onFlush(); - -//${QS::QS-tx::onGetTime} .................................................... -//! Callback to obtain a timestamp for a QS record -QSTimeCtr onGetTime(); - -} // namespace QS -namespace QS { - -//${QS::QS-tx-64bit::u64_raw_} ............................................... -//! Output std::uint64_t data element without format information -void u64_raw_(std::uint64_t d) noexcept; - -//${QS::QS-tx-64bit::u64_fmt_} ............................................... -//! Output std::uint64_t data element with format information -//! @sa QS_U64(), QS_I64() -void u64_fmt_( - std::uint8_t format, - std::uint64_t d) noexcept; - -} // namespace QS -namespace QS { - -//${QS::QS-tx-fp::f32_fmt_} .................................................. -//! Output 32-bit floating point data element with format information -//! @sa QS_F32() -void f32_fmt_( - std::uint8_t format, - float32_t d) noexcept; - -//${QS::QS-tx-fp::f64_fmt_} .................................................. -//! Output 64-bit floating point data element with format information -//! @sa QS_F64() -void f64_fmt_( - std::uint8_t format, - float32_t d) noexcept; - -} // namespace QS -namespace QS { - -//${QS::QS-rx::QSrx} ......................................................... -//! QS software tracing parameters for QS input (QS-RX) -class QSrx { -public: - - //! current objects - void * currObj[8]; - - //! pointer to the start of the ring buffer - std::uint8_t * buf; - - //! offset of the end of the ring buffer - QSCtr end; - - //! offset to where next byte will be inserted - QSCtr volatile head; - - //! offset of where next byte will be extracted - QSCtr volatile tail; - -#ifdef Q_UTEST - //! QUTest event loop is running - bool inTestLoop; -#endif // def Q_UTEST -}; // class QSrx - -//${QS::QS-rx::rxPriv_} ...................................................... -//! the only instance of the QS-RX object (Singleton) -extern QSrx rxPriv_; - -//${QS::QS-rx::QSpyObjKind} .................................................. -//! Kinds of objects used in QS::setCurrObj() and QS::queryCurrObj() -enum QSpyObjKind : std::uint8_t { - SM_OBJ, //!< state machine object for QEP - 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 -}; - -//${QS::QS-rx::OSpyObjCombnation} ............................................ -//! Object combinations for QS::setCurrObj() and QS::queryCurrObj() -enum OSpyObjCombnation : std::uint8_t { - SM_AO_OBJ = MAX_OBJ //!< combination of SM and AO -}; - -//${QS::QS-rx::rxInitBuf} .................................................... -//! Initialize the QS RX data buffer -//! -//! @details -//! This function should be called from QS::onStartup() to provide QS-RX -//! with the receive data buffer. -//! -//! @param[in] sto[] the address of the memory block -//! @param[in] stoSize the size of this block [bytes]. The size of the -//! QS RX buffer cannot exceed 64KB. -//! -//! @note -//! QS-RX can work with quite small data buffers, but you will start -//! losing data if the buffer is not drained fast enough (e.g., in the -//! idle task). -//! -//! @note -//! If the data input rate exceeds the QS-RX processing rate, the data -//! will be lost, but the QS protocol will notice that: -//! (1) that the checksum in the incomplete QS records will fail; and -//! (2) the sequence counter in QS records will show discontinuities. -//! -//! The QS-RX channel will report any data errors by sending the -//! QS_RX_DATA_ERROR trace record. -void rxInitBuf( - std::uint8_t * const sto, - std::uint16_t const stoSize) noexcept; - -//${QS::QS-rx::rxPut} ........................................................ -//! Put one byte into the QS RX lock-free buffer -inline bool rxPut(std::uint8_t const b) noexcept { - QSCtr head = rxPriv_.head + 1U; - if (head == rxPriv_.end) { - head = 0U; - } - if (head != rxPriv_.tail) { // buffer NOT full? - rxPriv_.buf[rxPriv_.head] = b; - rxPriv_.head = head; - return true; // byte placed in the buffer - } - else { - return false; // byte NOT placed in the buffer - } -} - -//${QS::QS-rx::rxGetNfree} ................................................... -//! Obtain the number of free bytes in the QS RX data buffer -//! -//! @details -//! This function is intended to be called from the ISR that reads the -//! QS-RX bytes from the QSPY application. The function returns the -//! conservative number of free bytes currently available in the buffer, -//! assuming that the head pointer is not being moved concurrently. -//! The tail pointer might be moving, meaning that bytes can be -//! concurrently removed from the buffer. -std::uint16_t rxGetNfree() noexcept; - -//${QS::QS-rx::doInput} ...................................................... -//! Perform the QS-RX input (implemented in some QS ports) -void doInput(); - -//${QS::QS-rx::setCurrObj} ................................................... -//! Set the "current object" in the Target -//! -//! @details -//! This function sets the "current object" in the Target. -void setCurrObj( - std::uint8_t obj_kind, - void * obj_ptr) noexcept; - -//${QS::QS-rx::queryCurrObj} ................................................. -//! Query the "current object" in the Target -//! -//! @details -//! This function programmatically generates the response to the query for -//! a "current object". -void queryCurrObj(std::uint8_t obj_kind) noexcept; - -//${QS::QS-rx::rxParse} ...................................................... -//! Parse all bytes present in the QS RX data buffer -void rxParse(); - -//${QS::QS-rx::rxHandleGoodFrame_} ........................................... -//! internal function to handle incoming (QS-RX) packet -void rxHandleGoodFrame_(std::uint8_t const state); - -//${QS::QS-rx::onReset} ...................................................... -//! callback function to reset the Target (to be implemented in the BSP) -void onReset(); - -//${QS::QS-rx::onCommand} .................................................... -//! Callback function to execute user commands (to be implemented in BSP) -void onCommand( - std::uint8_t cmdId, - std::uint32_t param1, - std::uint32_t param2, - std::uint32_t param3); - -} // namespace QS - -} // namespace QP -//$enddecl${QS} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -//============================================================================ -// Global namespace... -//$declare${QS-macros} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv - -//${QS-macros::QS_INIT} ...................................................... -//! Initialize the QS facility -//! -//! @details -//! This macro provides an indirection layer to invoke the QS initialization -//! routine if #Q_SPY is defined, or do nothing if #Q_SPY is not defined. -//! @sa QP::QS::onStartup(), example of setting up a QS filter in -//! QS_GLB_FILTER() -#define QS_INIT(arg_) (QP::QS::onStartup(arg_)) - -//${QS-macros::QS_EXIT} ...................................................... -//! Cleanup the QS facility -//! -//! @details -//! This macro provides an indirection layer to invoke the QS cleanup -//! routine if #Q_SPY is defined, or do nothing if #Q_SPY is not defined. -//! @sa QP::QS::onCleanup() -#define QS_EXIT() (QP::QS::onCleanup()) - -//${QS-macros::QS_OUTPUT} .................................................... -//! macro to handle the QS output from the application -//! -//! @note -//! If this macro is used, the application must define QS::doOutput(). -#define QS_OUTPUT() (QP::QS::doOutput()) - -//${QS-macros::QS_RX_INPUT} .................................................. -//! macro to handle the QS-RX input to the application -//! -//! @note -//! If this macro is used, the application must define QS::doInput(). -#define QS_RX_INPUT() (QP::QS::doInput()) - -//${QS-macros::QS_GLB_FILTER} ................................................ -//! Global Filter ON for a given record type `rec_` -//! -//! @details -//! This macro provides an indirection layer to call QP::QS::filterOn() -//! if #Q_SPY is defined, or do nothing if #Q_SPY is not defined. -//! -//! @sa -//! - QP::QSpyRecordGroups - QS record groups that can be used as `rec_` -//! - QP::QSpyRecords - individual QS records that can be used as `rec_` -//! -//! @usage -//! The following example shows how to use QS filters: -//! @include qs_filter.cpp -#define QS_GLB_FILTER(rec_) \ - (QP::QS::glbFilter_(static_cast(rec_))) - -//${QS-macros::QS_LOC_FILTER} ................................................ -//! Local Filter for a given state machine object `qs_id` -//! @details -//! This macro provides an indirection layer to call QS_locFilter_() -//! if #Q_SPY is defined, or do nothing if #Q_SPY is not defined. -//! -//! @sa -//! - QP::QSpyIdGroups - QS ID groups that can be used as `qs_id_` -//! - QP::QSpyIdOffsets - QS ID offsets for `qs_id_` (e.g., QS_AP_IDS + 5) -//! -//! The following example shows how to use QS filters: -//! @include qs_filter.cpp -#define QS_LOC_FILTER(qs_id_) \ - (QP::QS::locFilter_(static_cast(qs_id_))) - -//${QS-macros::QS_BEGIN_ID} .................................................. -//! Begin an application-specific QS record with entering critical section -//! -//! @details -//! The following example shows how to build a user QS record using the -//! macros QS_BEGIN_ID(), QS_END(), and the formatted output macros: -//! QS_U8(), QS_STR(), etc. -//! -//! @note -//! Must always be used in pair with QS_END() -//! -//! @include qs_user.cpp -#define QS_BEGIN_ID(rec_, qs_id_) \ -if (QS_GLB_CHECK_(rec_) && QS_LOC_CHECK_(qs_id_)) { \ - QS_CRIT_STAT_ \ - QS_CRIT_E_(); \ - QP::QS::beginRec_(static_cast(rec_)); \ - QS_TIME_PRE_(); - -//${QS-macros::QS_END} ....................................................... -//! End an applicationi-specific QS record with exiting critical section. -//! @sa example for QS_BEGIN_ID() -//! @note Must always be used in pair with QS_BEGIN_ID() -#define QS_END() \ - QP::QS::endRec_(); \ - QS_CRIT_X_(); \ -} - -//${QS-macros::QS_FLUSH} ..................................................... -//! Flush the QS trace data to the host -//! -//! @details -//! This macro invokes the QP::QS::flush() platform-dependent callback -//! function to flush the QS trace buffer to the host. The function -//! typically busy-waits until all the data in the buffer is sent to -//! the host. This is acceptable only in the initial transient. -#define QS_FLUSH() (QP::QS::onFlush()) - -//${QS-macros::QS_BEGIN_NOCRIT} .............................................. -//! Begin an application-specific QS record WITHOUT entering critical section -#define QS_BEGIN_NOCRIT(rec_, qs_id_) \ -if (QS_GLB_CHECK_(rec_) && QS_LOC_CHECK_(qs_id_)) { \ - QP::QS::beginRec_(rec_); \ - QS_TIME_PRE_(); - -//${QS-macros::QS_END_NOCRIT} ................................................ -//! End an application-specific QS record WITHOUT exiting critical section. -#define QS_END_NOCRIT() \ - QP::QS::endRec_(); \ -} - -//${QS-macros::QS_GLB_CHECK_} ................................................ -//! Helper macro for checking the global QS filter -#define QS_GLB_CHECK_(rec_) \ -((static_cast(QP::QS::priv_.glbFilter[ \ - static_cast(rec_) >> 3U]) \ - & (static_cast(1U) \ - << (static_cast(rec_) & 7U))) != 0U) - -//${QS-macros::QS_LOC_CHECK_} ................................................ -//! Helper macro for checking the local QS filter -#define QS_LOC_CHECK_(qs_id_) \ -((static_cast(QP::QS::priv_.locFilter \ - [static_cast(qs_id_) >> 3U]) \ - & (static_cast(1U) \ - << (static_cast(qs_id_) & 7U))) != 0U) - -//${QS-macros::QS_REC_DONE} .................................................. -#ifndef QS_REC_DONE -//! Macro to execute user code when a QS record is produced -//! -//! @note -//! This is a dummy definition in case this macro is undefined. -#define QS_REC_DONE() (static_cast(0)) -#endif // ndef QS_REC_DONE - -//${QS-macros::QS_I8} ........................................................ -//! Output formatted std::int8_t to the QS record -#define QS_I8(width_, data_) \ -(QP::QS::u8_fmt_(static_cast( \ - (static_cast(((width_) << 4U) & 0x7U)) \ - | static_cast(QP::QS::I8_ENUM_T)), (data_))) - -//${QS-macros::QS_U8} ........................................................ -//! Output formatted std::uint8_t to the QS record -#define QS_U8(width_, data_) \ -(QP::QS::u8_fmt_(static_cast( \ - (static_cast((width_) << 4U)) \ - | static_cast(QP::QS::U8_T)), (data_))) - -//${QS-macros::QS_I16} ....................................................... -//! Output formatted std::int16_t to the QS record -#define QS_I16(width_, data_) \ -(QP::QS::u16_fmt_(static_cast( \ - (static_cast((width_) << 4U)) \ - | static_cast(QP::QS::I16_T)), (data_))) - -//${QS-macros::QS_U16} ....................................................... -//! Output formatted std::uint16_t to the QS record -#define QS_U16(width_, data_) \ -(QP::QS::u16_fmt_(static_cast((((width_) << 4U)) \ - | static_cast(QP::QS::U16_T)), (data_))) - -//${QS-macros::QS_I32} ....................................................... -//! Output formatted std::int32_t to the QS record -#define QS_I32(width_, data_) \ -(QP::QS::u32_fmt_( \ - static_cast((static_cast((width_) << 4U)) \ - | static_cast(QP::QS::I32_T)), (data_))) - -//${QS-macros::QS_U32} ....................................................... -//! Output formatted std::uint32_t to the QS record -#define QS_U32(width_, data_) \ -(QP::QS::u32_fmt_(static_cast( \ - (static_cast((width_) << 4U)) \ - | static_cast(QP::QS::U32_T)), (data_))) - -//${QS-macros::QS_I64} ....................................................... -//! Output formatted std::int64_t to the QS record -#define QS_I64(width_, data_) \ -(QP::QS::u64_fmt_(static_cast( \ - (static_cast((width_) << 4U)) \ - | static_cast(QP::QS::I64_T)), (data_))) - -//${QS-macros::QS_U64} ....................................................... -//! Output formatted std::uint64_t to the QS record -#define QS_U64(width_, data_) \ -(QP::QS::u64_fmt_(static_cast( \ - (static_cast((width_) << 4U)) \ - | static_cast(QP::QS::U64_T)), (data_))) - -//${QS-macros::QS_F32} ....................................................... -//! Output formatted 32-bit floating point number to the QS record -#define QS_F32(width_, data_) \ -(QP::QS::f32_fmt_(static_cast( \ - (static_cast((width_) << 4U)) \ - | static_cast(QP::QS::F32_T)), (data_))) - -//${QS-macros::QS_F64} ....................................................... -//! Output formatted 64-bit floating point number to the QS record -#define QS_F64(width_, data_) \ -(QP::QS::f64_fmt_(static_cast( \ - (static_cast((width_) << 4U)) \ - | static_cast(QP::QS::F64_T)), (data_))) - -//${QS-macros::QS_STR} ....................................................... -//! Output formatted zero-terminated ASCII string to the QS record -#define QS_STR(str_) (QP::QS::str_fmt_(str_)) - -//${QS-macros::QS_MEM} ....................................................... -//! Output formatted memory block of up to 255 bytes to the QS record -#define QS_MEM(mem_, size_) (QP::QS::mem_fmt_((mem_), (size_))) - -//${QS-macros::QS_ENUM} ...................................................... -//! Output formatted enumeration to the QS record -#define QS_ENUM(group_, value_) \ - (QP::QS::u8_fmt_(static_cast(0x80U | ((group_) << 4U)) \ - | static_cast(QP::QS::I8_ENUM_T),\ - static_cast(value_))) - -//${QS-macros::QS_TIME_PRE_} ................................................. -#if (QS_TIME_SIZE == 4U) -//! Output time stamp to a QS record (used in predefined -//! and application-specific trace records) -#define QS_TIME_PRE_() (QP::QS::u32_raw_(QP::QS::onGetTime())) -#endif // (QS_TIME_SIZE == 4U) - -//${QS-macros::QS_TIME_PRE_} ................................................. -#if (QS_TIME_SIZE == 2U) -#define QS_TIME_PRE_() (QP::QS::u16_raw_(QP::QS::onGetTime())) -#endif // (QS_TIME_SIZE == 2U) - -//${QS-macros::QS_TIME_PRE_} ................................................. -#if (QS_TIME_SIZE == 1U) -#define QS_TIME_PRE_() (QP::QS::u8_raw_(QP::QS::onGetTime())) -#endif // (QS_TIME_SIZE == 1U) - -//${QS-macros::QS_OBJ} ....................................................... -#if (QS_OBJ_PTR_SIZE == 4U) -//! Output formatted object pointer to the QS record -#define QS_OBJ(obj_) (QP::QS::u32_fmt_(QP::QS::OBJ_T, \ - reinterpret_cast(obj_))) -#endif // (QS_OBJ_PTR_SIZE == 4U) - -//${QS-macros::QS_OBJ} ....................................................... -#if (QS_OBJ_PTR_SIZE == 2U) -#define QS_OBJ(obj_) (QP::QS::u16_fmt_(QP::QS::OBJ_T, \ - reinterpret_cast(obj_))) -#endif // (QS_OBJ_PTR_SIZE == 2U) - -//${QS-macros::QS_OBJ} ....................................................... -#if (QS_OBJ_PTR_SIZE == 1U) -#define QS_OBJ(obj_) (QP::QS::u8_fmt_(QP::QS::OBJ_T, \ - reinterpret_cast(obj_))) -#endif // (QS_OBJ_PTR_SIZE == 1U) - -//${QS-macros::QS_OBJ} ....................................................... -#if (QS_OBJ_PTR_SIZE == 8U) -#define QS_OBJ(obj_) (QP::QS::u64_fmt_(QP::QS::OBJ_T, \ - reinterpret_cast(obj_))) -#endif // (QS_OBJ_PTR_SIZE == 8U) - -//${QS-macros::QS_FUN} ....................................................... -#if (QS_FUN_PTR_SIZE == 4U) -//! Output formatted function pointer to the QS record -#define QS_FUN(fun_) (QP::QS::u32_fmt_(QP::QS::FUN_T, \ - reinterpret_cast(fun_))) -#endif // (QS_FUN_PTR_SIZE == 4U) - -//${QS-macros::QS_FUN} ....................................................... -#if (QS_FUN_PTR_SIZE == 2U) -#define QS_FUN(fun_) (QP::QS::u16_fmt_(QP::QS::FUN_T, \ - reinterpret_cast(fun_))) -#endif // (QS_FUN_PTR_SIZE == 2U) - -//${QS-macros::QS_FUN} ....................................................... -#if (QS_FUN_PTR_SIZE == 1U) -#define QS_FUN(fun_) (QP::QS::u8_fmt_(QP::QS::FUN_T, \ - reinterpret_cast(fun_))) -#endif // (QS_FUN_PTR_SIZE == 1U) - -//${QS-macros::QS_FUN} ....................................................... -#if (QS_FUN_PTR_SIZE == 8U) -#define QS_FUN(fun_) (QP::QS::u64_fmt_(QP::QS::FUN_T, \ - reinterpret_cast(fun_))) -#endif // (QS_FUN_PTR_SIZE == 8U) - -//${QS-macros::QS_SIG} ....................................................... -#if (Q_SIGNAL_SIZE == 4U) -//! Output formatted event signal (of type QP::QSignal) and -//! the state machine object to the user QS record -#define QS_SIG(sig_, obj_) \ - QP::QS::u32_fmt_(QP::QS::SIG_T, static_cast(sig_)); \ - QP::QS::obj_raw_(obj_) -#endif // (Q_SIGNAL_SIZE == 4U) - -//${QS-macros::QS_SIG} ....................................................... -#if (Q_SIGNAL_SIZE == 2U) -#define QS_SIG(sig_, obj_) \ - QP::QS::u16_fmt_(QP::QS::SIG_T, static_cast(sig_)); \ - QP::QS::obj_raw_(obj_) -#endif // (Q_SIGNAL_SIZE == 2U) - -//${QS-macros::QS_SIG} ....................................................... -#if (Q_SIGNAL_SIZE == 1U) -#define QS_SIG(sig_, obj_) \ - QP::QS::u8_fmt_(QP::QS::SIG_T, static_cast(sig_)); \ - QP::QS::obj_raw_(obj_) -#endif // (Q_SIGNAL_SIZE == 1U) - -//${QS-macros::QS_SIG_DICTIONARY} ............................................ -//! Output signal dictionary record -//! -//! @details -//! A signal dictionary record associates the numerical value of the signal -//! and the binary address of the state machine that consumes that signal -//! with the human-readable name of the signal. -//! -//! @param[in] sig_ event signal (typically enumerated, e.g. `TIMEOUT_SIG`) -//! @param[in] obj_ pointer to the associated state machine object -//! (might be `nullptr` for globally recognized signals) -//! -//! A signal dictionary entry is associated with both the signal value `sig_` -//! and the state machine `obj_`, because signals are required to be unique -//! only within a given state machine and therefore the same numerical values -//! can represent different signals in different state machines. -//! -//! For the "global" signals that have the same meaning in all state machines -//! (such as globally published signals), you can specify a signal dictionary -//! entry with the `obj_` parameter set to `nullptr`. -//! -//! The following example shows the definition of signal dictionary entries -//! in the initial transition of the Table active object. Please note that -//! signals HUNGRY_SIG and DONE_SIG are associated with the Table state -//! machine only ("me" `obj_` pointer). The EAT_SIG signal, on the other -//! hand, is global (`obj_ == nullptr`): -//! @include qs_sigDic.cpp -//! -//! The following QSpy log example shows the signal dictionary records -//! generated from the Table initial transition and subsequent records that -//! show human-readable names of the signals: -//! @include qs_sigLog.txt -#define QS_SIG_DICTIONARY(sig_, obj_) \ - (QP::QS::sig_dict_pre_((sig_), (obj_), #sig_)) - -//${QS-macros::QS_OBJ_DICTIONARY} ............................................ -//! Output QS object dictionary record -//! -//! @details -//! An object dictionary record associates the binary address of an object -//! in the target's memory with the human-readable name of the object. -//! -//! @param[in] obj_ pointer to the object (any object) -//! -//! The following example shows the definition of object dictionary entry -//! for the Table active object: -//! @include qs_objDic.cpp -#define QS_OBJ_DICTIONARY(obj_) \ - (QP::QS::obj_dict_pre_((obj_), #obj_)) - -//${QS-macros::QS_OBJ_ARR_DICTIONARY} ........................................ -//! Output QS object-array dictionary record -//! -//! @details -//! An object array dictionary record associates the binary address of the -//! object element in the target's memory with the human-readable name -//! of the object. -//! -//! @param[in] obj_ pointer to the object (any object) -//! @param[in] idx_ array index -//! -//! The following example shows the definition of object array dictionary -//! for `Philo::inst[n]` and `Philo::inst[n].m_timeEvt`: -//! @include qs_objArrDic.cpp -#define QS_OBJ_ARR_DICTIONARY(obj_, idx_) \ - (QP::QS::obj_arr_dict_pre_((obj_), (idx_), #obj_)) - -//${QS-macros::QS_FUN_DICTIONARY} ............................................ -//! Output function dictionary record -//! -//! @details -//! A function dictionary record associates the binary address of a function -//! in the target's memory with the human-readable name of the function. -//! -//! Providing a function dictionary QS record can vastly improve readability -//! of the QS log, because instead of dealing with cryptic machine addresses -//! the QSpy host utility can display human-readable function names. -//! -//! The example from #QS_SIG_DICTIONARY shows the definition of a function -//! dictionary. -#define QS_FUN_DICTIONARY(fun_) \ - (QP::QS::fun_dict_pre_( \ - QP::QS::force_cast(fun_), #fun_)) - -//${QS-macros::QS_USR_DICTIONARY} ............................................ -//! Output user QS record dictionary record -//! -//! @details -//! A user QS record dictionary record associates the numerical value of a -//! user record with the human-readable identifier. -#define QS_USR_DICTIONARY(rec_) \ - (QP::QS::usr_dict_pre_((rec_), #rec_)) - -//${QS-macros::QS_ENUM_DICTIONARY} ........................................... -//! Output enumeration dictionary record -//! -//! @details -//! An enum QS record dictionary record associates the numerical value of -//! an enumeration with the human-readable identifier. -#define QS_ENUM_DICTIONARY(value_, group_) \ - (QP::QS::enum_dict_pre_((value_), (group_), #value_)) - -//${QS-macros::QF_QS_CRIT_ENTRY} ............................................. -//! Output the critical section entry record -#define QF_QS_CRIT_ENTRY() (QP::QS::crit_entry_pre_()) - -//${QS-macros::QF_QS_CRIT_EXIT} .............................................. -//! Output the critical section exit record -#define QF_QS_CRIT_EXIT() (QP::QS::crit_exit_pre_()) - -//${QS-macros::QF_QS_ISR_ENTRY} .............................................. -//! Output the interrupt entry record -#define QF_QS_ISR_ENTRY(isrnest_, prio_) \ - (QP::QS::isr_entry_pre_((isrnest_), (prio_))) - -//${QS-macros::QF_QS_ISR_EXIT} ............................................... -//! Output the interrupt exit record -#define QF_QS_ISR_EXIT(isrnest_, prio_) \ - (QP::QS::isr_exit_pre_((isrnest_), (prio_))) - -//${QS-macros::QF_QS_ACTION} ................................................. -//! Execute an action that is only necessary for QS output -#define QF_QS_ACTION(act_) (act_) - -//${QS-macros::QS_ASSERTION} ................................................. -//! Produce the assertion failure trace record -#define QS_ASSERTION(module_, loc_, delay_) \ - (QP::QS::assertion_pre_((module_), (loc_), (delay_))) - -//${QS-macros::QS_EOD} ....................................................... -//! Constant representing End-Of-Data condition returned from the -//! QS::getByte() function. -#define QS_EOD (static_cast(0xFFFFU)) - -//${QS-macros::QS_CMD} ....................................................... -//! Constant representing command enumeration group -//! in QS_ENUM_DICTIONARY() and QS_ENUM() -//! @sa QS::onCommand() -#define QS_CMD (static_cast(7U)) - -//${QS-macros::QS_HEX_FMT} ................................................... -//! Constant representing HEX format for the "width" filed -//! in QS_U8(), QS_U16(), QS_U32(), and QS_U64(). -#define QS_HEX_FMT (static_cast(0x0FU)) -//$enddecl${QS-macros} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -//============================================================================ -// Facilities for QS critical section - -// QS-specific critical section -#ifdef QS_CRIT_ENTRY // separate QS critical section defined? - -#ifndef QS_CRIT_STAT_TYPE - #define QS_CRIT_STAT_ - #define QS_CRIT_E_() QS_CRIT_ENTRY(dummy) - #define QS_CRIT_X_() QS_CRIT_EXIT(dummy); QS_REC_DONE() -#else - #define QS_CRIT_STAT_ QS_CRIT_STAT_TYPE critStat_; - #define QS_CRIT_E_() QS_CRIT_ENTRY(critStat_) - #define QS_CRIT_X_() QS_CRIT_EXIT(critStat_); QS_REC_DONE() -#endif // QS_CRIT_STAT_TYPE - -#else // separate QS critical section not defined--use the QF definition - -#ifndef QF_CRIT_STAT_TYPE - //! This is an internal macro for defining the critical section - //! status type - //! - //! @details - //! The purpose of this macro is to enable writing the same code for the - //! case when critical section status type is defined and when it is not. - //! If the macro #QF_CRIT_STAT_TYPE is defined, this internal macro - //! provides the definition of the critical section status variable. - //! Otherwise this macro is empty. - //! @sa #QF_CRIT_STAT_TYPE - #define QS_CRIT_STAT_ - - //! This is an internal macro for entering a critical section - //! - //! @details - //! The purpose of this macro is to enable writing the same code for the - //! case when critical section status type is defined and when it is not. - //! If the macro #QF_CRIT_STAT_TYPE is defined, this internal macro - //! invokes #QF_CRIT_ENTRY passing the key variable as the parameter. - //! Otherwise #QF_CRIT_ENTRY is invoked with a dummy parameter. - //! @sa #QF_CRIT_ENTRY - #define QS_CRIT_E_() QF_CRIT_ENTRY(dummy) - - //! This is an internal macro for exiting a critical section - //! - //! @details - //! The purpose of this macro is to enable writing the same code for the - //! case when critical section status type is defined and when it is not. - //! If the macro #QF_CRIT_STAT_TYPE is defined, this internal macro - //! invokes #QF_CRIT_EXIT passing the key variable as the parameter. - //! Otherwise #QF_CRIT_EXIT is invoked with a dummy parameter. - //! @sa #QF_CRIT_EXIT - #define QS_CRIT_X_() QF_CRIT_EXIT(dummy); QS_REC_DONE() - -#elif (!defined QS_CRIT_STAT_) - #define QS_CRIT_STAT_ QF_CRIT_STAT_TYPE critStat_; - #define QS_CRIT_E_() QF_CRIT_ENTRY(critStat_) - #define QS_CRIT_X_() QF_CRIT_EXIT(critStat_); QS_REC_DONE() -#endif // simple unconditional interrupt disabling used - -#endif // separate QS critical section not defined - -//============================================================================ -// Macros for use in QUTest only -#ifdef Q_UTEST - -//$declare${QUTest} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv -namespace QP { -namespace QS { - -//${QUTest::QS::QUTEST_ON_POST} .............................................. -//! record ID for posting events -constexpr std::uint8_t QUTEST_ON_POST {124U}; - -//${QUTest::QS::TProbe} ...................................................... -//! Test Probe attributes -struct TProbe { - QSFun addr; //!< pointer to function hosting the Test Probe - std::uint32_t data; //!< data associated with the Test Probe - std::uint8_t idx; //!< index of the Test Probe -}; - -//${QUTest::QS::TestData} .................................................... -//! QUTest data -struct TestData { - TProbe tpBuf[16]; //!< up to 16 Test Probes - std::uint8_t tpNum; //!< # of registered Test Probes - QSTimeCtr testTime; //!< test time stamp -}; - -//${QUTest::QS::testData} .................................................... -//! QUTest data -extern TestData testData; - -//${QUTest::QS::test_pause_} ................................................. -//! internal function to pause test and enter the test event loop -void test_pause_(); - -//${QUTest::QS::getTestProbe_} ............................................... -//! get the test probe data for the given API -std::uint32_t getTestProbe_(QP::QSpyFunPtr const api) noexcept; - -//${QUTest::QS::onTestSetup} ................................................. -//! callback to setup a unit test inside the Target -void onTestSetup(); - -//${QUTest::QS::onTestTeardown} .............................................. -//! callback to teardown after a unit test inside the Target -void onTestTeardown(); - -//${QUTest::QS::onTestEvt} ................................................... -//! callback to "massage" the test event before dispatching/posting it -void onTestEvt(QEvt * e); - -//${QUTest::QS::onTestPost} .................................................. -//! callback to examine an event that is about to be posted -void onTestPost( - void const * sender, - QActive * recipient, - QEvt const * e, - bool status); - -//${QUTest::QS::onTestLoop} .................................................. -//! callback to run the test loop -void onTestLoop(); - -} // namespace QS - -} // namespace QP -//$enddecl${QUTest} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -//---------------------------------------------------------------------------- -// 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${QUTest-stub::QS} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv -namespace QP { -namespace QS { - -//${QUTest-stub::QS::processTestEvts_} ....................................... -//! internal function to process posted events during test -void processTestEvts_(); - -} // namespace QS -} // namespace QP -//$enddecl${QUTest-stub::QS} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -//$declare${QUTest-stub::QHsmDummy} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv -namespace QP { - -//${QUTest-stub::QHsmDummy} .................................................. -//! Dummy HSM class for testing (inherits QP::QHsm) -//! -//! @details -//! QHsmDummy is a test double for the role of "Orthogonal Components" -//! HSM objects in QUTest unit testing. -class QHsmDummy : public QP::QHsm { -public: - - //! ctor - QHsmDummy(); - void init( - void const * const e, - std::uint_fast8_t const qs_id) override; - void init(std::uint_fast8_t const qs_id) override; - void dispatch( - QEvt const * const e, - std::uint_fast8_t const qs_id) override; -}; // class QHsmDummy - -} // namespace QP -//$enddecl${QUTest-stub::QHsmDummy} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -//$declare${QUTest-stub::QActiveDummy} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv -namespace QP { - -//${QUTest-stub::QActiveDummy} ............................................... -//! Dummy Active Object class for testing (inherits QP::QActive) -//! -//! @details -//! QActiveDummy is a test double for the role of collaborating active -//! objects in QUTest unit testing. -class QActiveDummy : public QP::QActive { -public: - - //! ctor - QActiveDummy(); - void start( - QPrioSpec const prioSpec, - QEvt const * * const qSto, - std::uint_fast16_t const qLen, - void * const stkSto, - std::uint_fast16_t const stkSize, - void const * const par) override; - void start( - QPrioSpec const prioSpec, - QEvt const * * const qSto, - std::uint_fast16_t const qLen, - void * const stkSto, - std::uint_fast16_t const stkSize) override - { - this->start(prioSpec, qSto, qLen, stkSto, stkSize, nullptr); - } - void init( - void const * const e, - std::uint_fast8_t const qs_id) override; - void init(std::uint_fast8_t const qs_id) override; - void dispatch( - QEvt const * const e, - std::uint_fast8_t const qs_id) override; - bool post_( - QEvt const * const e, - std::uint_fast16_t const margin, - void const * const sender) noexcept override; - void postLIFO(QEvt const * const e) noexcept override; -}; // class QActiveDummy - -} // namespace QP -//$enddecl${QUTest-stub::QActiveDummy} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -#endif // Q_UTEST != 0 - -//! QS macro to define the Test-Probe for a given `fun_` -#define QS_TEST_PROBE_DEF(fun_) \ - std::uint32_t const qs_tp_ = \ - QP::QS::getTestProbe_(QP::QS::force_cast(fun_)); - -//! QS macro to apply a Test-Probe -#define QS_TEST_PROBE(code_) \ - if (qs_tp_ != 0U) { code_ } - -//! QS macro to apply a Test-Probe -#define QS_TEST_PROBE_ID(id_, code_) \ - if (qs_tp_ == static_cast(id_)) { code_ } - -//! QS macro to pause test execution and enter the test event loop -#define QS_TEST_PAUSE() (QP::QS::test_pause_()) - -#else - // 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_HPP_ diff --git a/src/qs/qs.cpp b/src/qs/qs.cpp new file mode 100644 index 0000000..a141acc --- /dev/null +++ b/src/qs/qs.cpp @@ -0,0 +1,1064 @@ +//$file${src::qs::qs.cpp} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv +// +// Model: qpcpp.qm +// File: ${src::qs::qs.cpp} +// +// This code has been generated by QM 6.2.3 . +// DO NOT EDIT THIS FILE MANUALLY. All your changes will be lost. +// +// ____________________________________ +// / / +// / GGGGGGG PPPPPPPP LL / +// / GG GG PP PP LL / +// / GG PP PP LL / +// / GG GGGGG PPPPPPPP LL / +// / GG GG PP LL / +// / GGGGGGG PP LLLLLLL / +// /___________________________________/ +// +// Copyright (c) 2005 Quantum Leaps, LLC +// SPDX-License-Identifier: GPL-3.0-or-later +// +// This generated code is open-source software licensed under the GNU +// General Public License (GPL) as published by the Free Software Foundation +// (see ). +// +// NOTE: +// The GPL does NOT permit the incorporation of this code into proprietary +// programs. Please contact Quantum Leaps for commercial licensing options, +// which expressly supersede the GPL and are designed explicitly for licensees +// interested in retaining the proprietary status of the generated code. +// +// Quantum Leaps contact information: +// +// +// +//$endhead${src::qs::qs.cpp} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +#define QP_IMPL // this is QP implementation +#include "qs_port.hpp" // QS port +#include "qs_pkg.hpp" // QS package-scope internal interface +#include "qstamp.hpp" // QP time-stamp +#include "qsafe.h" // QP Functional Safety (FuSa) Subsystem + +// unnamed namespace for local definitions with internal linkage +namespace { +Q_DEFINE_THIS_MODULE("qs") +} // unnamed namespace + +//$skip${QP_VERSION} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv +// Check for the minimum required QP version +#if (QP_VERSION < 730U) || (QP_VERSION != ((QP_RELEASE^4294967295U)%0x2710U)) +#error qpcpp version 7.3.0 or higher required +#endif +//$endskip${QP_VERSION} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +//$define${QS::QS-TX} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv +namespace QP { +namespace QS { + +//${QS::QS-TX::initBuf} ...................................................... +void initBuf( + std::uint8_t * const sto, + std::uint_fast32_t const stoSize) noexcept +{ + priv_.buf = sto; + priv_.end = static_cast(stoSize); + priv_.head = 0U; + priv_.tail = 0U; + priv_.used = 0U; + priv_.seq = 0U; + priv_.chksum = 0U; + priv_.critNest = 0U; + + glbFilter_(-static_cast(QS_ALL_RECORDS));// all global filters OFF + locFilter_(static_cast(QS_ALL_IDS)); // all local filters ON + priv_.locFilter_AP = nullptr; // deprecated "AP-filter" + + // produce an empty record to "flush" the QS trace buffer + beginRec_(QS_REC_NUM_(QS_EMPTY)); + endRec_(); + + // produce the reset record to inform QSPY of a new session + target_info_pre_(0xFFU); + + // hold off flushing after successfull initialization (see QS_INIT()) +} + +//${QS::QS-TX::getByte} ...................................................... +std::uint16_t getByte() noexcept { + // NOTE: Must be called IN critical section. + // Also requires system-level memory access (QF_MEM_SYS()). + + std::uint16_t ret; + if (priv_.used == 0U) { + ret = QS_EOD; // set End-Of-Data + } + else { + std::uint8_t const * const buf = priv_.buf; // put in a temporary + QSCtr tail = priv_.tail; // put in a temporary (register) + ret = static_cast(buf[tail]); // set the byte to return + ++tail; // advance the tail + if (tail == priv_.end) { // tail wrap around? + tail = 0U; + } + priv_.tail = tail; // update the tail + priv_.used = (priv_.used - 1U); // one less byte used + } + return ret; // return the byte or EOD +} + +//${QS::QS-TX::getBlock} ..................................................... +std::uint8_t const * getBlock(std::uint16_t * const pNbytes) noexcept { + // NOTE: Must be called IN critical section. + // Also requires system-level memory access (QF_MEM_SYS()). + + QSCtr const used = priv_.used; // put in a temporary (register) + std::uint8_t *buf; + + // any bytes used in the ring buffer? + if (used != 0U) { + QSCtr tail = priv_.tail; // put in a temporary (register) + QSCtr const end = priv_.end; // put in a temporary (register) + QSCtr n = static_cast(end - tail); + if (n > used) { + n = used; + } + if (n > static_cast(*pNbytes)) { + n = static_cast(*pNbytes); + } + *pNbytes = static_cast(n); // n-bytes available + buf = priv_.buf; + buf = &buf[tail]; // the bytes are at the tail + + priv_.used = static_cast(used - n); + tail += n; + if (tail == end) { + tail = 0U; + } + priv_.tail = tail; + } + else { // no bytes available + *pNbytes = 0U; // no bytes available right now + buf = nullptr; // no bytes available right now + } + return buf; +} + +} // namespace QS +} // namespace QP +//$enddef${QS::QS-TX} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +#ifndef QF_MEM_ISOLATE +//$define${QS::filters} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv +namespace QP { +namespace QS { + +//${QS::filters::filt_} ...................................................... +Filter filt_; + +} // namespace QS +} // namespace QP +//$enddef${QS::filters} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +#endif + +//============================================================================ +//! @cond INTERNAL + +namespace QP { +namespace QS { + +//............................................................................ +Attr priv_; + +//............................................................................ +void glbFilter_(std::int_fast16_t const filter) noexcept { + bool const isRemove = (filter < 0); + std::uint16_t const rec = isRemove + ? static_cast(-filter) + : static_cast(filter); + switch (rec) { + case QS_ALL_RECORDS: { + std::uint8_t const tmp = (isRemove ? 0x00U : 0xFFU); + std::uint_fast8_t i; + // set all global filters (partially unrolled loop) + for (i = 0U; i < Q_DIM(filt_.glb); i += 4U) { + filt_.glb[i ] = tmp; + filt_.glb[i + 1U] = tmp; + filt_.glb[i + 2U] = tmp; + filt_.glb[i + 3U] = tmp; + } + if (isRemove) { + // leave the "not maskable" filters enabled, + // see qs.h, Miscellaneous QS records (not maskable) + // + filt_.glb[0] = 0x01U; + filt_.glb[6] = 0x40U; + filt_.glb[7] = 0xFCU; + filt_.glb[8] = 0x7FU; + } + else { + // never turn the last 3 records on (0x7D, 0x7E, 0x7F) + filt_.glb[15] = 0x1FU; + } + break; + } + case QS_SM_RECORDS: + if (isRemove) { + filt_.glb[0] &= static_cast(~0xFEU & 0xFFU); + filt_.glb[1] &= static_cast(~0x03U & 0xFFU); + filt_.glb[6] &= static_cast(~0x80U & 0xFFU); + filt_.glb[7] &= static_cast(~0x03U & 0xFFU); + } + else { + filt_.glb[0] |= 0xFEU; + filt_.glb[1] |= 0x03U; + filt_.glb[6] |= 0x80U; + filt_.glb[7] |= 0x03U; + } + break; + case QS_AO_RECORDS: + if (isRemove) { + filt_.glb[1] &= static_cast(~0xFCU & 0xFFU); + filt_.glb[2] &= static_cast(~0x07U & 0xFFU); + filt_.glb[5] &= static_cast(~0x20U & 0xFFU); + } + else { + filt_.glb[1] |= 0xFCU; + filt_.glb[2] |= 0x07U; + filt_.glb[5] |= 0x20U; + } + break; + case QS_EQ_RECORDS: + if (isRemove) { + filt_.glb[2] &= static_cast(~0x78U & 0xFFU); + filt_.glb[5] &= static_cast(~0x40U & 0xFFU); + } + else { + filt_.glb[2] |= 0x78U; + filt_.glb[5] |= 0x40U; + } + break; + case QS_MP_RECORDS: + if (isRemove) { + filt_.glb[3] &= static_cast(~0x03U & 0xFFU); + filt_.glb[5] &= static_cast(~0x80U & 0xFFU); + } + else { + filt_.glb[3] |= 0x03U; + filt_.glb[5] |= 0x80U; + } + break; + case QS_QF_RECORDS: + if (isRemove) { + filt_.glb[2] &= static_cast(~0x80U & 0xFFU); + filt_.glb[3] &= static_cast(~0xFCU & 0xFFU); + filt_.glb[4] &= static_cast(~0xC0U & 0xFFU); + filt_.glb[5] &= static_cast(~0x1FU & 0xFFU); + } + else { + filt_.glb[2] |= 0x80U; + filt_.glb[3] |= 0xFCU; + filt_.glb[4] |= 0xC0U; + filt_.glb[5] |= 0x1FU; + } + break; + case QS_TE_RECORDS: + if (isRemove) { + filt_.glb[4] &= static_cast(~0x3FU & 0xFFU); + } + else { + filt_.glb[4] |= 0x3FU; + } + break; + case QS_SC_RECORDS: + if (isRemove) { + filt_.glb[6] &= static_cast(~0x3FU & 0xFFU); + } + else { + filt_.glb[6] |= 0x3FU; + } + break; + case QS_SEM_RECORDS: + if (isRemove) { + filt_.glb[8] &= static_cast(~0x80U & 0xFFU); + filt_.glb[9] &= static_cast(~0x07U & 0xFFU); + } + else { + filt_.glb[8] |= 0x80U; + filt_.glb[9] |= 0x07U; + } + break; + case QS_MTX_RECORDS: + if (isRemove) { + filt_.glb[9] &= static_cast(~0xF8U & 0xFFU); + filt_.glb[10] &= static_cast(~0x01U & 0xFFU); + } + else { + filt_.glb[9] |= 0xF8U; + filt_.glb[10] |= 0x01U; + } + break; + case QS_U0_RECORDS: + if (isRemove) { + filt_.glb[12] &= static_cast(~0xF0U & 0xFFU); + filt_.glb[13] &= static_cast(~0x01U & 0xFFU); + } + else { + filt_.glb[12] |= 0xF0U; + filt_.glb[13] |= 0x01U; + } + break; + case QS_U1_RECORDS: + if (isRemove) { + filt_.glb[13] &= static_cast(~0x3EU & 0xFFU); + } + else { + filt_.glb[13] |= 0x3EU; + } + break; + case QS_U2_RECORDS: + if (isRemove) { + filt_.glb[13] &= static_cast(~0xC0U & 0xFFU); + filt_.glb[14] &= static_cast(~0x07U & 0xFFU); + } + else { + filt_.glb[13] |= 0xC0U; + filt_.glb[14] |= 0x07U; + } + break; + case QS_U3_RECORDS: + if (isRemove) { + filt_.glb[14] &= static_cast(~0xF8U & 0xFFU); + } + else { + filt_.glb[14] |= 0xF8U; + } + break; + case QS_U4_RECORDS: + if (isRemove) { + filt_.glb[15] &= static_cast(~0x1FU & 0xFFU); + } + else { + filt_.glb[15] |= 0x1FU; + } + break; + case QS_UA_RECORDS: + if (isRemove) { + filt_.glb[12] &= static_cast(~0xF0U & 0xFFU); + filt_.glb[13] = 0U; + filt_.glb[14] = 0U; + filt_.glb[15] &= static_cast(~0x1FU & 0xFFU); + } + else { + filt_.glb[12] |= 0xF0U; + filt_.glb[13] |= 0xFFU; + filt_.glb[14] |= 0xFFU; + filt_.glb[15] |= 0x1FU; + } + break; + default: { + QS_CRIT_STAT + QS_CRIT_ENTRY(); + // QS rec number must be below 0x7D, so no need for escaping + Q_ASSERT_INCRIT(210, rec < 0x7DU); + QS_CRIT_EXIT(); + + if (isRemove) { + filt_.glb[rec >> 3U] + &= static_cast(~(1U << (rec & 7U)) & 0xFFU); + } + else { + filt_.glb[rec >> 3U] + |= static_cast(1U << (rec & 7U)); + // never turn the last 3 records on (0x7D, 0x7E, 0x7F) + filt_.glb[15] &= 0x1FU; + } + break; + } + } +} + +//............................................................................ +void locFilter_(std::int_fast16_t const filter) noexcept { + bool const isRemove = (filter < 0); + std::uint16_t const qs_id = isRemove + ? static_cast(-filter) + : static_cast(filter); + std::uint8_t const tmp = (isRemove ? 0x00U : 0xFFU); + std::uint_fast8_t i; + switch (qs_id) { + case QS_ALL_IDS: + // set all local filters (partially unrolled loop) + for (i = 0U; i < Q_DIM(filt_.loc); i += 4U) { + filt_.loc[i ] = tmp; + filt_.loc[i + 1U] = tmp; + filt_.loc[i + 2U] = tmp; + filt_.loc[i + 3U] = tmp; + } + break; + case QS_AO_IDS: + for (i = 0U; i < 8U; i += 4U) { + filt_.loc[i ] = tmp; + filt_.loc[i + 1U] = tmp; + filt_.loc[i + 2U] = tmp; + filt_.loc[i + 3U] = tmp; + } + break; + case QS_EP_IDS: + i = 8U; + filt_.loc[i ] = tmp; + filt_.loc[i + 1U] = tmp; + break; + case QS_AP_IDS: + i = 12U; + filt_.loc[i ] = tmp; + filt_.loc[i + 1U] = tmp; + filt_.loc[i + 2U] = tmp; + filt_.loc[i + 3U] = tmp; + break; + default: { + QS_CRIT_STAT + QS_CRIT_ENTRY(); + // qs_id must be in range + Q_ASSERT_INCRIT(310, qs_id < 0x7FU); + QS_CRIT_EXIT(); + if (isRemove) { + filt_.loc[qs_id >> 3U] &= + static_cast( + ~(1U << (qs_id & 7U)) & 0xFFU); + } + else { + filt_.loc[qs_id >> 3U] + |= (1U << (qs_id & 7U)); + } + break; + } + } + filt_.loc[0] |= 0x01U; // leave QS_ID == 0 always on +} + +//............................................................................ +void beginRec_(std::uint_fast8_t const rec) noexcept { + std::uint8_t const b = priv_.seq + 1U; + std::uint8_t chksum = 0U; // reset the checksum + std::uint8_t * const buf = priv_.buf; // put in a temporary (register) + QSCtr head = priv_.head; // put in a temporary (register) + QSCtr const end = priv_.end; // put in a temporary (register) + + priv_.seq = b; // store the incremented sequence num + priv_.used = (priv_.used + 2U); // 2 bytes about to be added + + QS_INSERT_ESC_BYTE_(b) + + chksum += static_cast(rec); + QS_INSERT_BYTE_(static_cast(rec)) // no need for escaping + + priv_.head = head; // save the head + priv_.chksum = chksum; // save the checksum +} + +//............................................................................ +void endRec_() noexcept { + std::uint8_t * const buf = priv_.buf; // put in a temporary (register) + QSCtr head = priv_.head; + QSCtr const end = priv_.end; + std::uint8_t b = priv_.chksum; + b ^= 0xFFU; // invert the bits in the checksum + + priv_.used = (priv_.used + 2U); // 2 bytes about to be added + + if ((b != QS_FRAME) && (b != QS_ESC)) { + QS_INSERT_BYTE_(b) + } + else { + QS_INSERT_BYTE_(QS_ESC) + QS_INSERT_BYTE_(b ^ QS_ESC_XOR) + priv_.used = (priv_.used + 1U); // account for the ESC byte + } + + QS_INSERT_BYTE_(QS_FRAME) // do not escape this QS_FRAME + + priv_.head = head; // save the head + if (priv_.used > end) { // overrun over the old data? + priv_.used = end; // the whole buffer is used + priv_.tail = head; // shift the tail to the old data + } +} + +//............................................................................ +void u8_raw_(std::uint8_t const d) noexcept { + std::uint8_t chksum = priv_.chksum; // put in a temporary (register) + std::uint8_t * const buf = priv_.buf; // put in a temporary (register) + QSCtr head = priv_.head; // put in a temporary (register) + QSCtr const end = priv_.end; // put in a temporary (register) + + priv_.used = (priv_.used + 1U); // 1 byte about to be added + QS_INSERT_ESC_BYTE_(d) + + priv_.head = head; // save the head + priv_.chksum = chksum; // save the checksum +} + +//............................................................................ +void u8u8_raw_( + std::uint8_t const d1, + std::uint8_t const d2) noexcept +{ + std::uint8_t chksum = priv_.chksum; // put in a temporary (register) + std::uint8_t * const buf = priv_.buf; // put in a temporary (register) + QSCtr head = priv_.head; // put in a temporary (register) + QSCtr const end = priv_.end; // put in a temporary (register) + + priv_.used = (priv_.used + 2U); // 2 bytes about to be added + QS_INSERT_ESC_BYTE_(d1) + QS_INSERT_ESC_BYTE_(d2) + + priv_.head = head; // save the head + priv_.chksum = chksum; // save the checksum +} + +//............................................................................ +void u16_raw_(std::uint16_t d) noexcept { + std::uint8_t b = static_cast(d); + std::uint8_t chksum = priv_.chksum; // put in a temporary (register) + std::uint8_t * const buf = priv_.buf; // put in a temporary (register) + QSCtr head = priv_.head; // put in a temporary (register) + QSCtr const end = priv_.end; // put in a temporary (register) + + priv_.used = (priv_.used + 2U); // 2 bytes about to be added + + QS_INSERT_ESC_BYTE_(b) + + d >>= 8U; + b = static_cast(d); + QS_INSERT_ESC_BYTE_(b) + + priv_.head = head; // save the head + priv_.chksum = chksum; // save the checksum +} + +//............................................................................ +void u32_raw_(std::uint32_t d) noexcept { + std::uint8_t chksum = priv_.chksum; // put in a temporary (register) + std::uint8_t * const buf = priv_.buf; // put in a temporary (register) + QSCtr head = priv_.head; // put in a temporary (register) + QSCtr const end = priv_.end; // put in a temporary (register) + + priv_.used = (priv_.used + 4U); // 4 bytes about to be added + for (std::uint_fast8_t i = 4U; i != 0U; --i) { + std::uint8_t const b = static_cast(d); + QS_INSERT_ESC_BYTE_(b) + d >>= 8U; + } + + priv_.head = head; // save the head + priv_.chksum = chksum; // save the checksum +} + +//............................................................................ +void obj_raw_(void const * const obj) noexcept { + #if (QS_OBJ_PTR_SIZE == 1U) + u8_raw_(reinterpret_cast(obj)); + #elif (QS_OBJ_PTR_SIZE == 2U) + u16_raw_(reinterpret_cast(obj)); + #elif (QS_OBJ_PTR_SIZE == 4U) + u32_raw_(reinterpret_cast(obj)); + #elif (QS_OBJ_PTR_SIZE == 8U) + u64_raw_(reinterpret_cast(obj)); + #else + u32_raw_(reinterpret_cast(obj)); + #endif +} + +//............................................................................ +void str_raw_(char const * s) noexcept { + std::uint8_t b = static_cast(*s); + std::uint8_t chksum = priv_.chksum; // put in a temporary (register) + std::uint8_t * const buf = priv_.buf; // put in a temporary (register) + QSCtr head = priv_.head; // put in a temporary (register) + QSCtr const end = priv_.end; // put in a temporary (register) + QSCtr used = priv_.used; // put in a temporary (register) + + while (b != 0U) { + chksum += b; // update checksum + QS_INSERT_BYTE_(b) // ASCII characters don't need escaping + ++s; + b = static_cast(*s); + ++used; + } + QS_INSERT_BYTE_(0U) // zero-terminate the string + ++used; + + priv_.head = head; // save the head + priv_.chksum = chksum; // save the checksum + priv_.used = used; // save # of used buffer space +} + +//............................................................................ +void u8_fmt_( + std::uint8_t const format, + std::uint8_t const d) noexcept +{ + std::uint8_t chksum = priv_.chksum; // put in a temporary (register) + std::uint8_t *const buf = priv_.buf; // put in a temporary (register) + QSCtr head = priv_.head; // put in a temporary (register) + QSCtr const end = priv_.end; // put in a temporary (register) + + priv_.used = (priv_.used + 2U); // 2 bytes about to be added + + QS_INSERT_ESC_BYTE_(format) + QS_INSERT_ESC_BYTE_(d) + + priv_.head = head; // save the head + priv_.chksum = chksum; // save the checksum +} + +//............................................................................ +void u16_fmt_( + std::uint8_t format, + std::uint16_t d) noexcept +{ + std::uint8_t chksum = priv_.chksum; // put in a temporary (register) + std::uint8_t * const buf = priv_.buf; // put in a temporary (register) + QSCtr head = priv_.head; // put in a temporary (register) + QSCtr const end = priv_.end; // put in a temporary (register) + + priv_.used = (priv_.used + 3U); // 3 bytes about to be added + + QS_INSERT_ESC_BYTE_(format) + + format = static_cast(d); + QS_INSERT_ESC_BYTE_(format) + + d >>= 8U; + format = static_cast(d); + QS_INSERT_ESC_BYTE_(format) + + priv_.head = head; // save the head + priv_.chksum = chksum; // save the checksum +} + +//............................................................................ +void u32_fmt_( + std::uint8_t format, + std::uint32_t d) noexcept +{ + std::uint8_t chksum = priv_.chksum; // put in a temporary (register) + std::uint8_t * const buf = priv_.buf; // put in a temporary (register) + QSCtr head = priv_.head; // put in a temporary (register) + QSCtr const end = priv_.end; // put in a temporary (register) + + priv_.used = (priv_.used + 5U); // 5 bytes about to be added + QS_INSERT_ESC_BYTE_(format) // insert the format byte + + for (std::uint_fast8_t i = 4U; i != 0U; --i) { + format = static_cast(d); + QS_INSERT_ESC_BYTE_(format) + d >>= 8U; + } + + priv_.head = head; // save the head + priv_.chksum = chksum; // save the checksum +} + +//............................................................................ +void str_fmt_(char const * s) noexcept { + std::uint8_t b = static_cast(*s); + std::uint8_t chksum = static_cast( + priv_.chksum + static_cast(STR_T)); + std::uint8_t * const buf = priv_.buf; // put in a temporary (register) + QSCtr head = priv_.head; // put in a temporary (register) + QSCtr const end = priv_.end; // put in a temporary (register) + QSCtr used = priv_.used; // put in a temporary (register) + + used += 2U; // the format byte and the terminating-0 + + QS_INSERT_BYTE_(static_cast(STR_T)) + while (b != 0U) { + // ASCII characters don't need escaping + chksum += b; // update checksum + QS_INSERT_BYTE_(b) + ++s; + b = static_cast(*s); + ++used; + } + QS_INSERT_BYTE_(0U) // zero-terminate the string + + priv_.head = head; // save the head + priv_.chksum = chksum; // save the checksum + priv_.used = used; // save # of used buffer space +} + +//............................................................................ +void mem_fmt_( + std::uint8_t const * blk, + std::uint8_t size) noexcept +{ + std::uint8_t b = static_cast(MEM_T); + std::uint8_t chksum = priv_.chksum + b; + std::uint8_t * const buf = priv_.buf; // put in a temporary (register) + QSCtr head = priv_.head; // put in a temporary (register) + QSCtr const end = priv_.end; // put in a temporary (register) + + priv_.used = (priv_.used + size + 2U); // size+2 bytes to be added + + QS_INSERT_BYTE_(b) + QS_INSERT_ESC_BYTE_(size) + + // output the 'size' number of bytes + for (; size != 0U; --size) { + b = *blk; + QS_INSERT_ESC_BYTE_(b) + ++blk; + } + + priv_.head = head; // save the head + priv_.chksum = chksum; // save the checksum +} + +//............................................................................ +void sig_dict_pre_( + QSignal const sig, + void const * const obj, + char const * const name) noexcept +{ + QS_CRIT_STAT + QS_CRIT_ENTRY(); + QS_MEM_SYS(); + + beginRec_(static_cast(QS_SIG_DICT)); + QS_SIG_PRE_(sig); + QS_OBJ_PRE_(obj); + QS_STR_PRE_((*name == '&') ? &name[1] : name); + endRec_(); + + QS_MEM_APP(); + QS_CRIT_EXIT(); + onFlush(); +} + +//............................................................................ +void obj_dict_pre_( + void const * const obj, + char const * const name) noexcept +{ + QS_CRIT_STAT + QS_CRIT_ENTRY(); + QS_MEM_SYS(); + + beginRec_(static_cast(QS_OBJ_DICT)); + QS_OBJ_PRE_(obj); + QS_STR_PRE_((*name == '&') ? &name[1] : name); + endRec_(); + + QS_MEM_APP(); + QS_CRIT_EXIT(); + onFlush(); +} + +//............................................................................ +void obj_arr_dict_pre_( + void const * const obj, + std::uint_fast16_t const idx, + char const * const name) noexcept +{ + QS_CRIT_STAT + QS_CRIT_ENTRY(); + Q_REQUIRE_INCRIT(400, idx < 1000U); + QS_CRIT_EXIT(); + + // format idx into a char buffer as "xxx\0" + std::uint8_t idx_str[4]; + std::uint_fast16_t tmp = idx; + std::uint8_t i; + idx_str[3] = 0U; // zero-terminate + idx_str[2] = static_cast( + static_cast('0') + (tmp % 10U)); + tmp /= 10U; + idx_str[1] = static_cast( + static_cast('0') + (tmp % 10U)); + if (idx_str[1] == static_cast('0')) { + i = 2U; + } + else { + tmp /= 10U; + idx_str[0] = static_cast( + static_cast('0') + (tmp % 10U)); + if (idx_str[0] == static_cast('0')) { + i = 1U; + } + else { + i = 0U; + } + } + + std::uint8_t j = ((*name == '&') ? 1U : 0U); + + QS_CRIT_ENTRY(); + QS_MEM_SYS(); + + beginRec_(static_cast(QS_OBJ_DICT)); + QS_OBJ_PRE_(obj); + for (; name[j] != '\0'; ++j) { + QS_U8_PRE_(name[j]); + if (name[j] == '[') { + ++j; + break; + } + } + for (; idx_str[i] != 0U; ++i) { + QS_U8_PRE_(idx_str[i]); + } + // skip chars until ']' + for (; name[j] != '\0'; ++j) { + if (name[j] == ']') { + break; + } + } + for (; name[j] != '\0'; ++j) { + QS_U8_PRE_(name[j]); + } + QS_U8_PRE_(0U); // zero-terminate + endRec_(); + + QS_MEM_APP(); + QS_CRIT_EXIT(); + onFlush(); +} + +//............................................................................ +void fun_dict_pre_( + QSpyFunPtr fun, + char const * const name) noexcept +{ + QS_CRIT_STAT + QS_CRIT_ENTRY(); + QS_MEM_SYS(); + + beginRec_(static_cast(QS_FUN_DICT)); + QS_FUN_PRE_(fun); + QS_STR_PRE_((*name == '&') ? &name[1] : name); + endRec_(); + + QS_MEM_APP(); + QS_CRIT_EXIT(); + onFlush(); +} + +//............................................................................ +void usr_dict_pre_( + enum_t const rec, + char const * const name) noexcept +{ + QS_CRIT_STAT + QS_CRIT_ENTRY(); + QS_MEM_SYS(); + + beginRec_(static_cast(QS_USR_DICT)); + QS_U8_PRE_(rec); + QS_STR_PRE_(name); + endRec_(); + + QS_MEM_APP(); + QS_CRIT_EXIT(); + onFlush(); +} + +//............................................................................ +void enum_dict_pre_( + enum_t const value, + std::uint8_t const group, + char const * const name) noexcept +{ + QS_CRIT_STAT + QS_CRIT_ENTRY(); + QS_MEM_SYS(); + + beginRec_(static_cast(QS_ENUM_DICT)); + QS_2U8_PRE_(static_cast(value), group); + QS_STR_PRE_(name); + endRec_(); + + QS_MEM_APP(); + QS_CRIT_EXIT(); + onFlush(); +} + +//............................................................................ +void assertion_pre_( + char const * const module, + int_t const id, + std::uint32_t const delay) noexcept +{ + // NOTE: called in a critical section + + beginRec_(static_cast(QS_ASSERT_FAIL)); + QS_TIME_PRE_(); + QS_U16_PRE_(id); + QS_STR_PRE_((module != nullptr) ? module : "?"); + endRec_(); + onFlush(); + for (std::uint32_t volatile ctr = delay; ctr > 0U; ) { + ctr = (ctr - 1U); + } + QS::onCleanup(); +} + +//............................................................................ +void crit_entry_pre_() noexcept { + beginRec_(static_cast(QS_QF_CRIT_ENTRY)); + QS_TIME_PRE_(); + priv_.critNest = (priv_.critNest + 1U); + QS_U8_PRE_(priv_.critNest); + endRec_(); +} + +//............................................................................ +void crit_exit_pre_() noexcept { + beginRec_(static_cast(QS_QF_CRIT_EXIT)); + QS_TIME_PRE_(); + QS_U8_PRE_(QS::priv_.critNest); + priv_.critNest = (priv_.critNest - 1U); + endRec_(); +} + +//............................................................................ +void isr_entry_pre_( + std::uint8_t const isrnest, + std::uint8_t const prio) noexcept +{ + beginRec_(static_cast(QS_QF_ISR_ENTRY)); + QS_TIME_PRE_(); + QS_U8_PRE_(isrnest); + QS_U8_PRE_(prio); + endRec_(); +} + +//............................................................................ +void isr_exit_pre_( + std::uint8_t const isrnest, + std::uint8_t const prio) noexcept +{ + beginRec_(static_cast(QS_QF_ISR_EXIT)); + QS_TIME_PRE_(); + QS_U8_PRE_(isrnest); + QS_U8_PRE_(prio); + endRec_(); +} + +//............................................................................ +void target_info_pre_(std::uint8_t const isReset) { + // NOTE: called in a critical section + + static constexpr std::uint8_t ZERO = static_cast('0'); + static std::uint8_t const * const TIME = + reinterpret_cast(&BUILD_TIME[0]); + static std::uint8_t const * const DATE = + reinterpret_cast(&BUILD_DATE[0]); + + beginRec_(static_cast(QS_TARGET_INFO)); + u8_raw_(isReset); + + static union { + std::uint16_t u16; + std::uint8_t u8[2]; + } endian_test; + endian_test.u16 = 0x0102U; + // big endian ? add the 0x8000U flag + QS_U16_PRE_(((endian_test.u8[0] == 0x01U) + ? (0x8000U | QP_VERSION) + : QP_VERSION)); // target endianness + version number + + // send the object sizes... + u8_raw_(Q_SIGNAL_SIZE + | static_cast(QF_EVENT_SIZ_SIZE << 4U)); + +#ifdef QF_EQUEUE_CTR_SIZE + u8_raw_(QF_EQUEUE_CTR_SIZE + | static_cast(QF_TIMEEVT_CTR_SIZE << 4U)); +#else + QS::u8_raw_(static_cast(QF_TIMEEVT_CTR_SIZE << 4U)); +#endif // ifdef QF_EQUEUE_CTR_SIZE + +#ifdef QF_MPOOL_CTR_SIZE + QS::u8_raw_(QF_MPOOL_SIZ_SIZE + | static_cast(QF_MPOOL_CTR_SIZE << 4U)); +#else + QS::u8_raw_(0U); +#endif // ifdef QF_MPOOL_CTR_SIZE + + QS::u8_raw_(QS_OBJ_PTR_SIZE | (QS_FUN_PTR_SIZE << 4U)); + QS::u8_raw_(QS_TIME_SIZE); + + // send the limits... + QS::u8_raw_(QF_MAX_ACTIVE); + QS::u8_raw_(QF_MAX_EPOOL | (QF_MAX_TICK_RATE << 4U)); + + // send the build time in three bytes (sec, min, hour)... + QS::u8_raw_((10U * (TIME[6] - ZERO)) + (TIME[7] - ZERO)); + QS::u8_raw_((10U * (TIME[3] - ZERO)) + (TIME[4] - ZERO)); + if (BUILD_TIME[0] == static_cast(' ')) { + QS::u8_raw_(TIME[1] - ZERO); + } + else { + QS::u8_raw_((10U * (TIME[0] - ZERO)) + (TIME[1] - ZERO)); + } + + // send the build date in three bytes (day, month, year) ... + if (BUILD_DATE[4] == static_cast(' ')) { + QS::u8_raw_(DATE[5] - ZERO); + } + else { + QS::u8_raw_((10U * (DATE[4] - ZERO)) + (DATE[5] - ZERO)); + } + // convert the 3-letter month to a number 1-12 ... + std::uint8_t b; + switch (DATE[0] + DATE[1] + DATE[2]) { + case 'J' + 'a' +'n': + b = 1U; + break; + case 'F' + 'e' + 'b': + b = 2U; + break; + case 'M' + 'a' +'r': + b = 3U; + break; + case 'A' + 'p' + 'r': + b = 4U; + break; + case 'M' + 'a' + 'y': + b = 5U; + break; + case 'J' + 'u' + 'n': + b = 6U; + break; + case 'J' + 'u' + 'l': + b = 7U; + break; + case 'A' + 'u' + 'g': + b = 8U; + break; + case 'S' + 'e' + 'p': + b = 9U; + break; + case 'O' + 'c' + 't': + b = 10U; + break; + case 'N' + 'o' + 'v': + b = 11U; + break; + case 'D' + 'e' + 'c': + b = 12U; + break; + default: + b = 0U; + break; + } + QS::u8_raw_(b); // store the month + QS::u8_raw_((10U * (DATE[9] - ZERO)) + (DATE[10] - ZERO)); + QS::endRec_(); +} + +} // namespace QS +} // namespace QP + +//! @endcond diff --git a/src/qs/qs_64bit.cpp b/src/qs/qs_64bit.cpp new file mode 100644 index 0000000..3eafba6 --- /dev/null +++ b/src/qs/qs_64bit.cpp @@ -0,0 +1,91 @@ +//$file${src::qs::qs_64bit.cpp} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv +// +// Model: qpcpp.qm +// File: ${src::qs::qs_64bit.cpp} +// +// This code has been generated by QM 6.2.3 . +// DO NOT EDIT THIS FILE MANUALLY. All your changes will be lost. +// +// ____________________________________ +// / / +// / GGGGGGG PPPPPPPP LL / +// / GG GG PP PP LL / +// / GG PP PP LL / +// / GG GGGGG PPPPPPPP LL / +// / GG GG PP LL / +// / GGGGGGG PP LLLLLLL / +// /___________________________________/ +// +// Copyright (c) 2005 Quantum Leaps, LLC +// SPDX-License-Identifier: GPL-3.0-or-later +// +// This generated code is open-source software licensed under the GNU +// General Public License (GPL) as published by the Free Software Foundation +// (see ). +// +// NOTE: +// The GPL does NOT permit the incorporation of this code into proprietary +// programs. Please contact Quantum Leaps for commercial licensing options, +// which expressly supersede the GPL and are designed explicitly for licensees +// interested in retaining the proprietary status of the generated code. +// +// Quantum Leaps contact information: +// +// +// +//$endhead${src::qs::qs_64bit.cpp} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +#define QP_IMPL // this is QF/QK implementation +#include "qs_port.hpp" // QS port +#include "qs_pkg.hpp" // QS package-scope internal interface + +//============================================================================ +//! @cond INTERNAL + +namespace QP { +namespace QS { + +//${QS::QS-tx-64bit::u64_raw_} ............................................... +void u64_raw_(std::uint64_t d) noexcept { + std::uint8_t chksum = priv_.chksum; + std::uint8_t * const buf = priv_.buf; + QSCtr head = priv_.head; + QSCtr const end = priv_.end; + + priv_.used = (priv_.used + 8U); // 8 bytes are about to be added + for (std::int_fast8_t i = 8U; i != 0U; --i) { + std::uint8_t const b = static_cast(d); + QS_INSERT_ESC_BYTE_(b) + d >>= 8U; + } + + priv_.head = head; // save the head + priv_.chksum = chksum; // save the checksum +} + +//${QS::QS-tx-64bit::u64_fmt_} ............................................... +void u64_fmt_( + std::uint8_t format, + std::uint64_t d) noexcept +{ + std::uint8_t chksum = priv_.chksum; + std::uint8_t * const buf = priv_.buf; + QSCtr head = priv_.head; + QSCtr const end = priv_.end; + + priv_.used = (priv_.used + 9U); // 9 bytes are about to be added + QS_INSERT_ESC_BYTE_(format) // insert the format byte + + for (std::int_fast8_t i = 8U; i != 0U; --i) { + format = static_cast(d); + QS_INSERT_ESC_BYTE_(format) + d >>= 8U; + } + + priv_.head = head; // save the head + priv_.chksum = chksum; // save the checksum +} + +} // namespace QS +} // namespace QP + +//! @endcond diff --git a/src/qs/qs_fp.cpp b/src/qs/qs_fp.cpp new file mode 100644 index 0000000..606c94c --- /dev/null +++ b/src/qs/qs_fp.cpp @@ -0,0 +1,128 @@ +//$file${src::qs::qs_fp.cpp} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv +// +// Model: qpcpp.qm +// File: ${src::qs::qs_fp.cpp} +// +// This code has been generated by QM 6.2.3 . +// DO NOT EDIT THIS FILE MANUALLY. All your changes will be lost. +// +// ____________________________________ +// / / +// / GGGGGGG PPPPPPPP LL / +// / GG GG PP PP LL / +// / GG PP PP LL / +// / GG GGGGG PPPPPPPP LL / +// / GG GG PP LL / +// / GGGGGGG PP LLLLLLL / +// /___________________________________/ +// +// Copyright (c) 2005 Quantum Leaps, LLC +// SPDX-License-Identifier: GPL-3.0-or-later +// +// This generated code is open-source software licensed under the GNU +// General Public License (GPL) as published by the Free Software Foundation +// (see ). +// +// NOTE: +// The GPL does NOT permit the incorporation of this code into proprietary +// programs. Please contact Quantum Leaps for commercial licensing options, +// which expressly supersede the GPL and are designed explicitly for licensees +// interested in retaining the proprietary status of the generated code. +// +// Quantum Leaps contact information: +// +// +// +//$endhead${src::qs::qs_fp.cpp} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +#define QP_IMPL // this is QF/QK implementation +#include "qs_port.hpp" // QS port +#include "qs_pkg.hpp" // QS package-scope internal interface + +//============================================================================ +//! @cond INTERNAL + +namespace QP { +namespace QS { + +//${QS::QS-tx-fp::f32_fmt_} .................................................. +void f32_fmt_( + std::uint8_t format, + float32_t f) noexcept +{ + union F32Rep { + float32_t f; + std::uint32_t u; + } fu32; // the internal binary representation + std::uint8_t chksum = priv_.chksum; // put in a temporary (register) + std::uint8_t * const buf = priv_.buf; // put in a temporary (register) + QSCtr head = priv_.head; // put in a temporary (register) + QSCtr const end = priv_.end; // put in a temporary (register) + + fu32.f = f; // assign the binary representation + + priv_.used = (priv_.used + 5U); // 5 bytes about to be added + QS_INSERT_ESC_BYTE_(format) // insert the format byte + + for (std::uint_fast8_t i = 4U; i != 0U; --i) { + format = static_cast(fu32.u); + QS_INSERT_ESC_BYTE_(format) + fu32.u >>= 8U; + } + + priv_.head = head; // save the head + priv_.chksum = chksum; // save the checksum +} + +//${QS::QS-tx-fp::f64_fmt_} .................................................. +void f64_fmt_( + std::uint8_t format, + float64_t d) noexcept +{ + union F64Rep { + float64_t d; + std::uint32_t u[2]; + } fu64; // the internal binary representation + std::uint8_t chksum = priv_.chksum; + std::uint8_t * const buf = priv_.buf; + QSCtr head = priv_.head; + QSCtr const end = priv_.end; + std::uint32_t i; + // static constant untion to detect endianness of the machine + static union U32Rep { + std::uint32_t u32; + std::uint8_t u8; + } const endian = { 1U }; + + fu64.d = d; // assign the binary representation + + // is this a big-endian machine? + if (endian.u8 == 0U) { + // swap fu64.u[0] <-> fu64.u[1]... + i = fu64.u[0]; + fu64.u[0] = fu64.u[1]; + fu64.u[1] = i; + } + + priv_.used = (priv_.used + 9U); // 9 bytes about to be added + QS_INSERT_ESC_BYTE_(format) // insert the format byte + + // output 4 bytes from fu64.u[0]... + for (i = 4U; i != 0U; --i) { + QS_INSERT_ESC_BYTE_(static_cast(fu64.u[0])) + fu64.u[0] >>= 8U; + } + + // output 4 bytes from fu64.u[1]... + for (i = 4U; i != 0U; --i) { + QS_INSERT_ESC_BYTE_(static_cast(fu64.u[1])) + fu64.u[1] >>= 8U; + } + + priv_.head = head; // update the head + priv_.chksum = chksum; // update the checksum +} + +} // namespace QS +} // namespace QP + +//! @endcond diff --git a/src/qs_rx.cpp b/src/qs/qs_rx.cpp similarity index 75% rename from src/qs_rx.cpp rename to src/qs/qs_rx.cpp index 36bc5d4..5528cc5 100644 --- a/src/qs_rx.cpp +++ b/src/qs/qs_rx.cpp @@ -3,59 +3,52 @@ // Model: qpcpp.qm // File: ${src::qs::qs_rx.cpp} // -// This code has been generated by QM 5.2.5 . +// This code has been generated by QM 6.2.3 . // DO NOT EDIT THIS FILE MANUALLY. All your changes will be lost. // -// This code is covered by the following QP license: -// License # : LicenseRef-QL-dual -// Issued to : Any user of the QP/C++ real-time embedded framework -// Framework(s) : qpcpp -// Support ends : 2023-12-31 -// License scope: +// ____________________________________ +// / / +// / GGGGGGG PPPPPPPP LL / +// / GG GG PP PP LL / +// / GG PP PP LL / +// / GG GGGGG PPPPPPPP LL / +// / GG GG PP LL / +// / GGGGGGG PP LLLLLLL / +// /___________________________________/ // -// Copyright (C) 2005 Quantum Leaps, LLC . +// Copyright (c) 2005 Quantum Leaps, LLC +// SPDX-License-Identifier: GPL-3.0-or-later // -// SPDX-License-Identifier: GPL-3.0-or-later OR LicenseRef-QL-commercial +// This generated code is open-source software licensed under the GNU +// General Public License (GPL) as published by the Free Software Foundation +// (see ). // -// 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. +// NOTE: +// The GPL does NOT permit the incorporation of this code into proprietary +// programs. Please contact Quantum Leaps for commercial licensing options, +// which expressly supersede the GPL and are designed explicitly for licensees +// interested in retaining the proprietary status of the generated code. // -// The terms of the open source GNU General Public License version 3 -// can be found at: -// -// The terms of the closed source Quantum Leaps commercial licenses -// can be found at: -// -// Redistributions in source code must retain this top-level comment block. -// Plagiarizing this software to sidestep the license obligations is illegal. -// -// Contact information: +// Quantum Leaps contact information: // // // //$endhead${src::qs::qs_rx.cpp} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -//! @file -//! @brief QS receive channel services - -#define QP_IMPL // this is QP implementation -#include "qs_port.hpp" // QS port -#include "qs_pkg.hpp" // QS package-scope internal interface -#include "qf_pkg.hpp" // QF package-scope internal interface -#include "qassert.h" // QP assertions - -static_assert(QP::QS::MAX_OBJ <= 8U, "QS::MAX_OBJECT below the limit"); +#define QP_IMPL // this is QP implementation +#include "qs_port.hpp" // QS port +#include "qs_pkg.hpp" // QS package-scope internal interface +#include "qp_pkg.hpp" // QP package-scope interface +#include "qsafe.h" // QP Functional Safety (FuSa) Subsystem //============================================================================ +//! @cond INTERNAL + namespace { // unnamed local namespace Q_DEFINE_THIS_MODULE("qs_rx") //............................................................................ -#if (QS_OBJ_PTR_SIZE == 1U) - using QSObj = std::uint8_t; -#elif (QS_OBJ_PTR_SIZE == 2U) +#if (QS_OBJ_PTR_SIZE == 2U) using QSObj = std::uint16_t; #elif (QS_OBJ_PTR_SIZE == 4U) using QSObj = std::uint32_t; @@ -63,9 +56,6 @@ Q_DEFINE_THIS_MODULE("qs_rx") using QSObj = std::uint64_t; #endif -//! @cond -//! Exclude the following internals from the Doxygen documentation -//! Extended-state variables used for parsing various QS-RX Records struct CmdVar { std::uint32_t param1; std::uint32_t param2; @@ -187,36 +177,41 @@ enum RxStateEnum : std::uint8_t { // internal helper functions... static void rxParseData_(std::uint8_t const b) noexcept; +static void rxHandleGoodFrame_(std::uint8_t const state); static void rxHandleBadFrame_(std::uint8_t const state) noexcept; static void rxReportAck_(enum QP::QSpyRxRecords const recId) noexcept; static void rxReportError_(std::uint8_t const code) noexcept; static void rxReportDone_(enum QP::QSpyRxRecords const recId) noexcept; -static void rxPoke_(void) noexcept; +static void queryCurrObj_(std::uint8_t obj_kind) noexcept; +static void rxPoke_() noexcept; -//! Internal QS-RX function to take a transition in the QS-RX FSM +// Internal QS-RX function to take a tran. in the QS-RX FSM static inline void tran_(RxStateEnum const target) noexcept { l_rx.state = static_cast(target); } -//! @endcond } // unnamed namespace +namespace QP { +namespace QS { +RxAttr rxPriv_; +} // namespace QS +} // namespace QP + +//! @endcond //============================================================================ + //$skip${QP_VERSION} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv // Check for the minimum required QP version -#if (QP_VERSION < 700U) || (QP_VERSION != ((QP_RELEASE^4294967295U) % 0x3E8U)) -#error qpcpp version 7.0.0 or higher required +#if (QP_VERSION < 730U) || (QP_VERSION != ((QP_RELEASE^4294967295U)%0x2710U)) +#error qpcpp version 7.3.0 or higher required #endif //$endskip${QP_VERSION} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -//$define${QS::QS-rx} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv +//$define${QS::QS-RX} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv namespace QP { namespace QS { -//${QS::QS-rx::rxPriv_} ...................................................... -QSrx rxPriv_; - -//${QS::QS-rx::rxInitBuf} .................................................... +//${QS::QS-RX::rxInitBuf} .................................................... void rxInitBuf( std::uint8_t * const sto, std::uint16_t const stoSize) noexcept @@ -245,92 +240,16 @@ void rxInitBuf( // no QS_REC_DONE(), because QS is not running yet #ifdef Q_UTEST - QP::QS::testData.tpNum = 0U; - QP::QS::testData.testTime = 0U; + tstPriv_.tpNum = 0U; + tstPriv_.testTime = 0U; #endif // Q_UTEST } -//${QS::QS-rx::rxGetNfree} ................................................... -std::uint16_t rxGetNfree() noexcept { - QSCtr const head = rxPriv_.head; - if (head == rxPriv_.tail) { // buffer empty? - return static_cast(rxPriv_.end - 1U); - } - else if (head < rxPriv_.tail) { - return static_cast(rxPriv_.tail - head - 1U); - } - else { - return static_cast(rxPriv_.end + rxPriv_.tail - - head - 1U); - } -} - -//${QS::QS-rx::setCurrObj} ................................................... -void setCurrObj( - std::uint8_t obj_kind, - void * obj_ptr) noexcept -{ - Q_REQUIRE_ID(100, obj_kind < Q_DIM(rxPriv_.currObj)); - rxPriv_.currObj[obj_kind] = obj_ptr; -} - -//${QS::QS-rx::queryCurrObj} ................................................. -void queryCurrObj(std::uint8_t obj_kind) noexcept { - Q_REQUIRE_ID(200, obj_kind < Q_DIM(rxPriv_.currObj)); - - if (QS::rxPriv_.currObj[obj_kind] != nullptr) { - QS_CRIT_STAT_ - QS_CRIT_E_(); - QS::beginRec_(static_cast(QS_QUERY_DATA)); - QS_TIME_PRE_(); // timestamp - QS_U8_PRE_(obj_kind); // object kind - QS_OBJ_PRE_(QS::rxPriv_.currObj[obj_kind]); // object pointer - switch (obj_kind) { - case SM_OBJ: // intentionally fall through - case AO_OBJ: - QS_FUN_PRE_( - reinterpret_cast( - QS::rxPriv_.currObj[obj_kind])->getStateHandler()); - break; - case QS::MP_OBJ: - QS_MPC_PRE_(reinterpret_cast( - QS::rxPriv_.currObj[obj_kind])->getNFree()); - QS_MPC_PRE_(reinterpret_cast( - QS::rxPriv_.currObj[obj_kind])->getNMin()); - break; - case QS::EQ_OBJ: - QS_EQC_PRE_(reinterpret_cast( - QS::rxPriv_.currObj[obj_kind])->getNFree()); - QS_EQC_PRE_(reinterpret_cast( - QS::rxPriv_.currObj[obj_kind])->getNMin()); - break; - case QS::TE_OBJ: - QS_OBJ_PRE_(reinterpret_cast( - QS::rxPriv_.currObj[obj_kind])->getAct()); - QS_TEC_PRE_(reinterpret_cast( - QS::rxPriv_.currObj[obj_kind])->getCtr()); - QS_TEC_PRE_(reinterpret_cast( - QS::rxPriv_.currObj[obj_kind])->getInterval()); - QS_SIG_PRE_(reinterpret_cast( - QS::rxPriv_.currObj[obj_kind])->sig); - QS_U8_PRE_ (reinterpret_cast( - QS::rxPriv_.currObj[obj_kind])->refCtr_); - break; - default: - break; - } - QS::endRec_(); - QS_CRIT_X_(); - - QS_REC_DONE(); // user callback (if defined) - } - else { - rxReportError_(static_cast(QS_RX_AO_FILTER)); - } -} - -//${QS::QS-rx::rxParse} ...................................................... +//${QS::QS-RX::rxParse} ...................................................... void rxParse() { + // NOTE: Must be called IN critical section. + // Also requires system-level memory access (QF_MEM_SYS()). + QSCtr tail = rxPriv_.tail; while (rxPriv_.head != tail) { // QS-RX buffer NOT empty? std::uint8_t b = rxPriv_.buf[tail]; @@ -374,520 +293,186 @@ void rxParse() { } } -//${QS::QS-rx::rxHandleGoodFrame_} ........................................... -void rxHandleGoodFrame_(std::uint8_t const state) { - std::uint8_t i; - std::uint8_t *ptr; - QS_CRIT_STAT_ +//${QS::QS-RX::rxGetNfree} ................................................... +std::uint16_t rxGetNfree() noexcept { + // NOTE: Must be called IN critical section. + // Also requires system-level memory access (QF_MEM_SYS()). - switch (state) { - case WAIT4_INFO_FRAME: { - // no need to report Ack or Done - QS_CRIT_E_(); - target_info_pre_(0U); // send only Target info - QS_CRIT_X_(); + QSCtr const head = rxPriv_.head; + std::uint16_t nFree; + if (head == rxPriv_.tail) { // buffer empty? + nFree = static_cast(rxPriv_.end - 1U); + } + else if (head < rxPriv_.tail) { + nFree = static_cast(rxPriv_.tail - head - 1U); + } + else { + nFree = static_cast(rxPriv_.end + rxPriv_.tail + - head - 1U); + } + return nFree; +} + +} // namespace QS +} // namespace QP +//$enddef${QS::QS-RX} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +//============================================================================ +//! @cond INTERNAL +namespace { // unnamed local namespace + +//............................................................................ +static void rxParseData_(std::uint8_t const b) noexcept { + switch (l_rx.state) { + case WAIT4_SEQ: { + ++l_rx.seq; + if (l_rx.seq != b) { // not the expected sequence? + rxReportError_(0x42U); + l_rx.seq = b; // update the sequence + } + tran_(WAIT4_REC); break; } - case WAIT4_RESET_FRAME: { - // no need to report Ack or Done, because Target resets - QS::onReset(); // reset the Target + case WAIT4_REC: { + switch (b) { + case QP::QS_RX_INFO: + tran_(WAIT4_INFO_FRAME); + break; + case QP::QS_RX_COMMAND: + tran_(WAIT4_CMD_ID); + break; + case QP::QS_RX_RESET: + tran_(WAIT4_RESET_FRAME); + break; + case QP::QS_RX_TICK: + tran_(WAIT4_TICK_RATE); + break; + case QP::QS_RX_PEEK: + if (QP::QS::rxPriv_.currObj[QP::QS::AP_OBJ] != nullptr) { + l_rx.var.peek.offs = 0U; + l_rx.var.peek.idx = 0U; + tran_(WAIT4_PEEK_OFFS); + } + else { + rxReportError_( + static_cast(QP::QS_RX_PEEK)); + tran_(ERROR_STATE); + } + break; + case QP::QS_RX_POKE: + case QP::QS_RX_FILL: + l_rx.var.poke.fill = + (b == static_cast(QP::QS_RX_FILL)) + ? 1U + : 0U; + if (QP::QS::rxPriv_.currObj[QP::QS::AP_OBJ] != nullptr) { + l_rx.var.poke.offs = 0U; + l_rx.var.poke.idx = 0U; + tran_(WAIT4_POKE_OFFS); + } + else { + rxReportError_( + (l_rx.var.poke.fill != 0U) + ? static_cast(QP::QS_RX_FILL) + : static_cast(QP::QS_RX_POKE)); + tran_(ERROR_STATE); + } + break; + case QP::QS_RX_GLB_FILTER: // intentionally fall-through + case QP::QS_RX_LOC_FILTER: + l_rx.var.flt.recId = b; + tran_(WAIT4_FILTER_LEN); + break; + case QP::QS_RX_AO_FILTER: // intentionally fall-through + case QP::QS_RX_CURR_OBJ: + l_rx.var.obj.recId = b; + tran_(WAIT4_OBJ_KIND); + break; + case QP::QS_RX_QUERY_CURR: + l_rx.var.obj.recId = + static_cast(QP::QS_RX_QUERY_CURR); + tran_(WAIT4_QUERY_KIND); + break; + case QP::QS_RX_EVENT: + tran_(WAIT4_EVT_PRIO); + break; + +#ifdef Q_UTEST + case QP::QS_RX_TEST_SETUP: + tran_(WAIT4_TEST_SETUP_FRAME); + break; + case QP::QS_RX_TEST_TEARDOWN: + tran_(WAIT4_TEST_TEARDOWN_FRAME); + break; + case QP::QS_RX_TEST_CONTINUE: + tran_(WAIT4_TEST_CONTINUE_FRAME); + break; + case QP::QS_RX_TEST_PROBE: + if (QP::QS::tstPriv_.tpNum + < static_cast( + (sizeof(QP::QS::tstPriv_.tpBuf) + / sizeof(QP::QS::tstPriv_.tpBuf[0])))) + { + l_rx.var.tp.data = 0U; + l_rx.var.tp.idx = 0U; + tran_(WAIT4_TEST_PROBE_DATA); + } + else { // the # Test-Probes exceeded + rxReportError_( + static_cast(QP::QS_RX_TEST_PROBE)); + tran_(ERROR_STATE); + } + break; +#endif // Q_UTEST + + default: + rxReportError_(0x43U); + tran_(ERROR_STATE); + break; + } break; } - case WAIT4_CMD_PARAM1: // intentionally fall-through - case WAIT4_CMD_PARAM2: // intentionally fall-through - case WAIT4_CMD_PARAM3: // intentionally fall-through - case WAIT4_CMD_FRAME: { - rxReportAck_(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 - rxReportDone_(QS_RX_COMMAND); + case WAIT4_INFO_FRAME: { + // keep ignoring the data until a frame is collected break; } - case WAIT4_TICK_FRAME: { - rxReportAck_(QS_RX_TICK); - #ifdef Q_UTEST - QTimeEvt::tick1_( - static_cast(l_rx.var.tick.rate), - &QS::rxPriv_); - #if Q_UTEST != 0 - QS::processTestEvts_(); // process all events produced - #endif // Q_UTEST != 0 - #else - QTimeEvt::tick_( - static_cast(l_rx.var.tick.rate), - &QS::rxPriv_); - #endif // Q_UTEST - rxReportDone_(QS_RX_TICK); + case 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; + tran_(WAIT4_CMD_PARAM1); break; } - case WAIT4_PEEK_FRAME: { - // no need to report Ack or Done - QS_CRIT_E_(); - QS::beginRec_(static_cast(QS_PEEK_DATA)); - ptr = static_cast( - QS::rxPriv_.currObj[QS::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); // number of 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_( - reinterpret_cast(ptr)[i]); - break; - case 4: - QS_U32_PRE_( - reinterpret_cast(ptr)[i]); - break; - default: - break; - } - } - QS::endRec_(); - QS_CRIT_X_(); - - QS_REC_DONE(); // user callback (if defined) + case WAIT4_CMD_PARAM1: { + l_rx.var.cmd.param1 |= + (static_cast(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; + tran_(WAIT4_CMD_PARAM2); + } break; } - case WAIT4_POKE_DATA: { - // received less than expected poke data items - rxReportError_(static_cast(QS_RX_POKE)); + case WAIT4_CMD_PARAM2: { + l_rx.var.cmd.param2 |= + static_cast(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; + tran_(WAIT4_CMD_PARAM3); + } break; } - case WAIT4_POKE_FRAME: { - rxReportAck_(QS_RX_POKE); - // no need to report done - break; - } - case WAIT4_FILL_FRAME: { - rxReportAck_(QS_RX_FILL); - ptr = static_cast( - QS::rxPriv_.currObj[QS::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] = - static_cast(l_rx.var.poke.data); - break; - case 2: - reinterpret_cast(ptr)[i] = - static_cast(l_rx.var.poke.data); - break; - case 4: - reinterpret_cast(ptr)[i] = - l_rx.var.poke.data; - break; - default: - break; - } - } - break; - } - case WAIT4_FILTER_FRAME: { - rxReportAck_(static_cast(l_rx.var.flt.recId)); - - // apply the received filters - if (l_rx.var.flt.recId - == static_cast(QS_RX_GLB_FILTER)) - { - for (i = 0U; - i < static_cast(sizeof(priv_.glbFilter)); - ++i) - { - priv_.glbFilter[i] = l_rx.var.flt.data[i]; - } - // leave the "not maskable" filters enabled, - // see qs.h, Miscellaneous QS records (not maskable) - // - priv_.glbFilter[0] |= 0x01U; - priv_.glbFilter[7] |= 0xFCU; - priv_.glbFilter[8] |= 0x7FU; - - // never enable the last 3 records (0x7D, 0x7E, 0x7F) - priv_.glbFilter[15] &= 0x1FU; - } - else if (l_rx.var.flt.recId - == static_cast(QS_RX_LOC_FILTER)) - { - for (i = 0U; i < Q_DIM(priv_.locFilter); ++i) { - priv_.locFilter[i] = l_rx.var.flt.data[i]; - } - // leave QS_ID == 0 always on - priv_.locFilter[0] |= 0x01U; - } - else { - rxReportError_(l_rx.var.flt.recId); - } - - // no need to report Done - break; - } - case WAIT4_OBJ_FRAME: { - i = l_rx.var.obj.kind; - if (i < static_cast(QS::MAX_OBJ)) { - if (l_rx.var.obj.recId - == static_cast(QS_RX_CURR_OBJ)) - { - rxPriv_.currObj[i] = - reinterpret_cast(l_rx.var.obj.addr); - rxReportAck_(QS_RX_CURR_OBJ); - } - else if (l_rx.var.obj.recId - == static_cast(QS_RX_AO_FILTER)) - { - if (l_rx.var.obj.addr != 0U) { - std::int_fast16_t const filter = - static_cast( - reinterpret_cast( - l_rx.var.obj.addr)->m_prio); - locFilter_((i == 0) - ? filter - :-filter); - rxReportAck_(QS_RX_AO_FILTER); - } - else { - rxReportError_(static_cast(QS_RX_AO_FILTER)); - } - } - else { - rxReportError_(l_rx.var.obj.recId); - } - } - // both SM and AO - else if (i == static_cast(QS::SM_AO_OBJ)) { - if (l_rx.var.obj.recId - == static_cast(QS_RX_CURR_OBJ)) - { - rxPriv_.currObj[SM_OBJ] - = reinterpret_cast(l_rx.var.obj.addr); - rxPriv_.currObj[AO_OBJ] - = reinterpret_cast(l_rx.var.obj.addr); - } - rxReportAck_( - static_cast(l_rx.var.obj.recId)); - } - else { - rxReportError_(l_rx.var.obj.recId); - } - break; - } - case WAIT4_QUERY_FRAME: { - 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); // "massage" the event, if needed - #endif // Q_UTEST - // use 'i' as status, 0 == success,no-recycle - i = 0U; - - 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::registry_[l_rx.var.evt.prio]->POST_X( - l_rx.var.evt.e, - 0U, // margin - &QS::rxPriv_)) - { - // failed QACTIVE_POST() recycles the event - i = 0x80U; // failure, no recycle - } - } - else if (l_rx.var.evt.prio == 255U) { - // dispatch to the current SM object - if (QS::rxPriv_.currObj[QS::SM_OBJ] != nullptr) { - // 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. - // - QEvt_refCtr_inc_(l_rx.var.evt.e); - - static_cast(QS::rxPriv_.currObj[QS::SM_OBJ]) - ->dispatch(l_rx.var.evt.e, 0U); - i = 0x01U; // success, recycle - } - else { - i = 0x81U; // failure, recycle - } - } - else if (l_rx.var.evt.prio == 254U) { - // init the current SM object" - if (QS::rxPriv_.currObj[QS::SM_OBJ] != nullptr) { - // 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. - // - QEvt_refCtr_inc_(l_rx.var.evt.e); - - static_cast(QS::rxPriv_.currObj[QS::SM_OBJ]) - ->init(l_rx.var.evt.e, 0U); - i = 0x01U; // success, recycle - } - else { - i = 0x81U; // failure, recycle - } - } - else if (l_rx.var.evt.prio == 253U) { - // post to the current AO - if (QS::rxPriv_.currObj[QS::AO_OBJ] != nullptr) { - if (!static_cast( - QS::rxPriv_.currObj[QS::AO_OBJ])->POST_X( - l_rx.var.evt.e, - 0U, // margin - &QS::rxPriv_)) - { - // failed QACTIVE_POST() recycles the event - i = 0x80U; // failure, no recycle - } - } - else { - i = 0x81U; // failure, recycle - } - } - else { - i = 0x81U; // failure, recycle - } - - #if (QF_MAX_EPOOL > 0U) - // recycle needed? - if ((i & 1U) != 0U) { - QF::gc(l_rx.var.evt.e); - } - #endif - // failure? - if ((i & 0x80U) != 0U) { - rxReportError_(static_cast(QS_RX_EVENT)); - } - else { - #ifdef Q_UTEST - #if Q_UTEST != 0 - QS::processTestEvts_(); // process all events produced - #endif // Q_UTEST != 0 - #endif // Q_UTEST - rxReportDone_(QS_RX_EVENT); - } - break; - } - - #ifdef Q_UTEST - case WAIT4_TEST_SETUP_FRAME: { - rxReportAck_(QS_RX_TEST_SETUP); - QP::QS::testData.tpNum = 0U; // clear Test-Probes - QP::QS::testData.testTime = 0U; //clear time tick - // don't clear current objects - QS::onTestSetup(); // application-specific test setup - // no need to report Done - break; - } - case WAIT4_TEST_TEARDOWN_FRAME: { - rxReportAck_(QS_RX_TEST_TEARDOWN); - QS::onTestTeardown(); // application-specific test teardown - // no need to report Done - break; - } - case WAIT4_TEST_CONTINUE_FRAME: { - rxReportAck_(QS_RX_TEST_CONTINUE); - QS::rxPriv_.inTestLoop = false; // exit the QUTest loop - // no need to report Done - break; - } - case WAIT4_TEST_PROBE_FRAME: { - rxReportAck_(QS_RX_TEST_PROBE); - Q_ASSERT_ID(815, - QP::QS::testData.tpNum - < (sizeof(QP::QS::testData.tpBuf) - / sizeof(QP::QS::testData.tpBuf[0]))); - QP::QS::testData.tpBuf[QP::QS::testData.tpNum] = l_rx.var.tp; - ++QP::QS::testData.tpNum; - // no need to report Done - break; - } - #endif // Q_UTEST - - case ERROR_STATE: { - // keep ignoring all bytes until new frame - break; - } - default: { - rxReportError_(0x47U); - break; - } - } -} - -} // namespace QS -} // namespace QP -//$enddef${QS::QS-rx} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -//============================================================================ -namespace { // unnamed local namespace - -//............................................................................ -static void rxParseData_(std::uint8_t const b) noexcept { - switch (l_rx.state) { - case WAIT4_SEQ: { - ++l_rx.seq; - if (l_rx.seq != b) { // not the expected sequence? - rxReportError_(0x42U); - l_rx.seq = b; // update the sequence - } - tran_(WAIT4_REC); - break; - } - case WAIT4_REC: { - switch (b) { - case QP::QS_RX_INFO: - tran_(WAIT4_INFO_FRAME); - break; - case QP::QS_RX_COMMAND: - tran_(WAIT4_CMD_ID); - break; - case QP::QS_RX_RESET: - tran_(WAIT4_RESET_FRAME); - break; - case QP::QS_RX_TICK: - tran_(WAIT4_TICK_RATE); - break; - case QP::QS_RX_PEEK: - if (QP::QS::rxPriv_.currObj[QP::QS::AP_OBJ] != nullptr) { - l_rx.var.peek.offs = 0U; - l_rx.var.peek.idx = 0U; - tran_(WAIT4_PEEK_OFFS); - } - else { - rxReportError_( - static_cast(QP::QS_RX_PEEK)); - tran_(ERROR_STATE); - } - break; - case QP::QS_RX_POKE: - case QP::QS_RX_FILL: - l_rx.var.poke.fill = - (b == static_cast(QP::QS_RX_FILL)) - ? 1U - : 0U; - if (QP::QS::rxPriv_.currObj[QP::QS::AP_OBJ] != nullptr) { - l_rx.var.poke.offs = 0U; - l_rx.var.poke.idx = 0U; - tran_(WAIT4_POKE_OFFS); - } - else { - rxReportError_( - (l_rx.var.poke.fill != 0U) - ? static_cast(QP::QS_RX_FILL) - : static_cast(QP::QS_RX_POKE)); - tran_(ERROR_STATE); - } - break; - case QP::QS_RX_GLB_FILTER: // intentionally fall-through - case QP::QS_RX_LOC_FILTER: - l_rx.var.flt.recId = b; - tran_(WAIT4_FILTER_LEN); - break; - case QP::QS_RX_AO_FILTER: // intentionally fall-through - case QP::QS_RX_CURR_OBJ: - l_rx.var.obj.recId = b; - tran_(WAIT4_OBJ_KIND); - break; - case QP::QS_RX_QUERY_CURR: - l_rx.var.obj.recId = - static_cast(QP::QS_RX_QUERY_CURR); - tran_(WAIT4_QUERY_KIND); - break; - case QP::QS_RX_EVENT: - tran_(WAIT4_EVT_PRIO); - break; - -#ifdef Q_UTEST - case QP::QS_RX_TEST_SETUP: - tran_(WAIT4_TEST_SETUP_FRAME); - break; - case QP::QS_RX_TEST_TEARDOWN: - tran_(WAIT4_TEST_TEARDOWN_FRAME); - break; - case QP::QS_RX_TEST_CONTINUE: - tran_(WAIT4_TEST_CONTINUE_FRAME); - break; - case QP::QS_RX_TEST_PROBE: - if (QP::QS::testData.tpNum - < static_cast( - (sizeof(QP::QS::testData.tpBuf) - / sizeof(QP::QS::testData.tpBuf[0])))) - { - l_rx.var.tp.data = 0U; - l_rx.var.tp.idx = 0U; - tran_(WAIT4_TEST_PROBE_DATA); - } - else { // the number of Test-Probes exceeded - rxReportError_( - static_cast(QP::QS_RX_TEST_PROBE)); - tran_(ERROR_STATE); - } - break; -#endif // Q_UTEST - - default: - rxReportError_(0x43U); - tran_(ERROR_STATE); - break; - } - break; - } - case WAIT4_INFO_FRAME: { - // keep ignoring the data until a frame is collected - break; - } - case WAIT4_CMD_ID: { - 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; - tran_(WAIT4_CMD_PARAM1); - break; - } - case WAIT4_CMD_PARAM1: { - l_rx.var.cmd.param1 |= - (static_cast(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; - tran_(WAIT4_CMD_PARAM2); - } - break; - } - case WAIT4_CMD_PARAM2: { - l_rx.var.cmd.param2 |= - static_cast(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; - tran_(WAIT4_CMD_PARAM3); - } - break; - } - case WAIT4_CMD_PARAM3: { - l_rx.var.cmd.param3 |= - static_cast(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; - tran_(WAIT4_CMD_FRAME); - } + case WAIT4_CMD_PARAM3: { + l_rx.var.cmd.param3 |= + static_cast(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; + tran_(WAIT4_CMD_FRAME); + } break; } case WAIT4_CMD_FRAME: { @@ -952,10 +537,7 @@ static void rxParseData_(std::uint8_t const b) noexcept { break; } case WAIT4_POKE_SIZE: { - if ((b == 1U) - || (b == 2U) - || (b == 4U)) - { + if ((b == 1U) || (b == 2U) || (b == 4U)) { l_rx.var.poke.size = b; tran_(WAIT4_POKE_NUM); } @@ -1050,161 +632,514 @@ static void rxParseData_(std::uint8_t const b) noexcept { } break; } - case WAIT4_OBJ_ADDR: { - l_rx.var.obj.addr |= - static_cast(b) << l_rx.var.obj.idx; - l_rx.var.obj.idx += 8U; - if (l_rx.var.obj.idx - == (8U * static_cast(QS_OBJ_PTR_SIZE))) + case WAIT4_OBJ_ADDR: { + l_rx.var.obj.addr |= + static_cast(b) << l_rx.var.obj.idx; + l_rx.var.obj.idx += 8U; + if (l_rx.var.obj.idx + == (8U * static_cast(QS_OBJ_PTR_SIZE))) + { + tran_(WAIT4_OBJ_FRAME); + } + break; + } + case WAIT4_OBJ_FRAME: { + // keep ignoring the data until a frame is collected + break; + } + case WAIT4_QUERY_KIND: { + if (b < static_cast(QP::QS::MAX_OBJ)) { + l_rx.var.obj.kind = b; + tran_(WAIT4_QUERY_FRAME); + } + else { + rxReportError_(l_rx.var.obj.recId); + tran_(ERROR_STATE); + } + break; + } + case WAIT4_QUERY_FRAME: { + // keep ignoring the data until a frame is collected + break; + } + case WAIT4_EVT_PRIO: { + l_rx.var.evt.prio = b; + l_rx.var.evt.sig = 0U; + l_rx.var.evt.idx = 0U; + tran_(WAIT4_EVT_SIG); + break; + } + case WAIT4_EVT_SIG: { + l_rx.var.evt.sig |= static_cast( + static_cast(b) << l_rx.var.evt.idx); + l_rx.var.evt.idx += 8U; + if (l_rx.var.evt.idx + == (8U *static_cast(Q_SIGNAL_SIZE))) + { + l_rx.var.evt.len = 0U; + l_rx.var.evt.idx = 0U; + tran_(WAIT4_EVT_LEN); + } + break; + } + case WAIT4_EVT_LEN: { + l_rx.var.evt.len |= static_cast( + static_cast(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(QP::QEvt)) + <= static_cast( + QP::QF::poolGetMaxBlockSize())) + { + // report Ack before generating any other QS records + rxReportAck_(QP::QS_RX_EVENT); + + l_rx.var.evt.e = QP::QF::newX_( + (static_cast(l_rx.var.evt.len) + + sizeof(QP::QEvt)), + 0U, // margin + static_cast(l_rx.var.evt.sig)); + // event allocated? + if (l_rx.var.evt.e != nullptr) { + l_rx.var.evt.p = + reinterpret_cast(l_rx.var.evt.e); + l_rx.var.evt.p = &l_rx.var.evt.p[sizeof(QP::QEvt)]; + if (l_rx.var.evt.len > 0U) { + tran_(WAIT4_EVT_PAR); + } + else { + tran_(WAIT4_EVT_FRAME); + } + } + else { + rxReportError_( + static_cast(QP::QS_RX_EVENT)); + tran_(ERROR_STATE); + } + } + else { + rxReportError_( + static_cast(QP::QS_RX_EVENT)); + tran_(ERROR_STATE); + } + } + break; + } + case 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) { + tran_(WAIT4_EVT_FRAME); + } + break; + } + case WAIT4_EVT_FRAME: { + // keep ignoring the data until a frame is collected + break; + } + +#ifdef Q_UTEST + case WAIT4_TEST_SETUP_FRAME: { + // keep ignoring the data until a frame is collected + break; + } + case WAIT4_TEST_TEARDOWN_FRAME: { + // keep ignoring the data until a frame is collected + break; + } + case WAIT4_TEST_CONTINUE_FRAME: { + // keep ignoring the data until a frame is collected + break; + } + case WAIT4_TEST_PROBE_DATA: { + l_rx.var.tp.data |= + (static_cast(b) << l_rx.var.tp.idx); + l_rx.var.tp.idx += 8U; + if (l_rx.var.tp.idx == (8U * sizeof(std::uint32_t))) { + l_rx.var.tp.addr = 0U; + l_rx.var.tp.idx = 0U; + tran_(WAIT4_TEST_PROBE_ADDR); + } + break; + } + case WAIT4_TEST_PROBE_ADDR: { + l_rx.var.tp.addr |= + (static_cast(b) << l_rx.var.tp.idx); + l_rx.var.tp.idx += 8U; + if (l_rx.var.tp.idx + == (8U * static_cast(QS_FUN_PTR_SIZE))) + { + tran_(WAIT4_TEST_PROBE_FRAME); + } + break; + } + case WAIT4_TEST_PROBE_FRAME: { + // keep ignoring the data until a frame is collected + break; + } +#endif // Q_UTEST + + case ERROR_STATE: { + // keep ignoring the data until a good frame is collected + break; + } + default: { // unexpected or unimplemented state + rxReportError_(0x45U); + tran_(ERROR_STATE); + break; + } + } +} + +//............................................................................ +void rxHandleGoodFrame_(std::uint8_t const state) { + std::uint8_t i; + std::uint8_t *ptr; + + switch (state) { + case WAIT4_INFO_FRAME: { + // no need to report Ack or Done + QP::QS::target_info_pre_(0U); // send only Target info + break; + } + case WAIT4_RESET_FRAME: { + // no need to report Ack or Done, because Target resets + QP::QS::onReset(); // reset the Target + break; + } + case WAIT4_CMD_PARAM1: // intentionally fall-through + case WAIT4_CMD_PARAM2: // intentionally fall-through + case WAIT4_CMD_PARAM3: // intentionally fall-through + case WAIT4_CMD_FRAME: { + rxReportAck_(QP::QS_RX_COMMAND); + QP::QS::onCommand(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 + QP::QS::processTestEvts_(); // process all events produced + #endif // Q_UTEST != 0 +#endif // Q_UTEST + rxReportDone_(QP::QS_RX_COMMAND); + break; + } + case WAIT4_TICK_FRAME: { + rxReportAck_(QP::QS_RX_TICK); +#ifdef Q_UTEST + QP::QTimeEvt::tick1_( + static_cast(l_rx.var.tick.rate), + &QP::QS::rxPriv_); + #if Q_UTEST != 0 + QP::QS::processTestEvts_(); // process all events produced + #endif // Q_UTEST != 0 +#else + QP::QTimeEvt::tick( + static_cast(l_rx.var.tick.rate), + &QP::QS::rxPriv_); +#endif // Q_UTEST + rxReportDone_(QP::QS_RX_TICK); + break; + } + case WAIT4_PEEK_FRAME: { + // no need to report Ack or Done + QP::QS::beginRec_(static_cast( + QP::QS_PEEK_DATA)); + ptr = static_cast( + QP::QS::rxPriv_.currObj[QP::QS::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); // number of 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_( + reinterpret_cast(ptr)[i]); + break; + case 4: + QS_U32_PRE_( + reinterpret_cast(ptr)[i]); + break; + default: + // intentionally empty + break; + } + } + QP::QS::endRec_(); + + QS_REC_DONE(); // user callback (if defined) + break; + } + case WAIT4_POKE_DATA: { + // received less than expected poke data items + rxReportError_(static_cast(QP::QS_RX_POKE)); + break; + } + case WAIT4_POKE_FRAME: { + rxReportAck_(QP::QS_RX_POKE); + // no need to report done + break; + } + case WAIT4_FILL_FRAME: { + rxReportAck_(QP::QS_RX_FILL); + ptr = static_cast( + QP::QS::rxPriv_.currObj[QP::QS::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] = + static_cast(l_rx.var.poke.data); + break; + case 2: + reinterpret_cast(ptr)[i] = + static_cast(l_rx.var.poke.data); + break; + case 4: + reinterpret_cast(ptr)[i] = + l_rx.var.poke.data; + break; + default: + // intentionally empty + break; + } + } + break; + } + case WAIT4_FILTER_FRAME: { + rxReportAck_(static_cast( + l_rx.var.flt.recId)); + + // apply the received filters + if (l_rx.var.flt.recId + == static_cast(QP::QS_RX_GLB_FILTER)) { - tran_(WAIT4_OBJ_FRAME); + for (i = 0U; + i < static_cast(sizeof(QP::QS::filt_.glb)); + ++i) + { + QP::QS::filt_.glb[i] = l_rx.var.flt.data[i]; + } + // leave the "not maskable" filters enabled, + // see qs.hpp, Miscellaneous QS records (not maskable) + QP::QS::filt_.glb[0] |= 0x01U; + QP::QS::filt_.glb[7] |= 0xFCU; + QP::QS::filt_.glb[8] |= 0x7FU; + + // never enable the last 3 records (0x7D, 0x7E, 0x7F) + QP::QS::filt_.glb[15] &= 0x1FU; + } + else if (l_rx.var.flt.recId + == static_cast(QP::QS_RX_LOC_FILTER)) + { + for (i = 0U; i < Q_DIM(QP::QS::filt_.loc); ++i) { + QP::QS::filt_.loc[i] = l_rx.var.flt.data[i]; + } + // leave QS_ID == 0 always on + QP::QS::filt_.loc[0] |= 0x01U; + } + else { + rxReportError_(l_rx.var.flt.recId); } + // no need to report Done break; } case WAIT4_OBJ_FRAME: { - // keep ignoring the data until a frame is collected - break; - } - case WAIT4_QUERY_KIND: { - if (b < static_cast(QP::QS::MAX_OBJ)) { - l_rx.var.obj.kind = b; - tran_(WAIT4_QUERY_FRAME); + i = l_rx.var.obj.kind; + if (i < static_cast(QP::QS::MAX_OBJ)) { + if (l_rx.var.obj.recId + == static_cast(QP::QS_RX_CURR_OBJ)) + { + QP::QS::rxPriv_.currObj[i] = + reinterpret_cast(l_rx.var.obj.addr); + rxReportAck_(QP::QS_RX_CURR_OBJ); + } + else if (l_rx.var.obj.recId + == static_cast(QP::QS_RX_AO_FILTER)) + { + if (l_rx.var.obj.addr != 0U) { + std::int_fast16_t const filter = + static_cast( + reinterpret_cast( + l_rx.var.obj.addr)->getPrio()); + QP::QS::locFilter_((i == 0) + ? filter + :-filter); + rxReportAck_(QP::QS_RX_AO_FILTER); + } + else { + rxReportError_(static_cast( + QP::QS_RX_AO_FILTER)); + } + } + else { + rxReportError_(l_rx.var.obj.recId); + } + } + // both SM and AO + else if (i == static_cast(QP::QS::SM_AO_OBJ)) { + if (l_rx.var.obj.recId + == static_cast(QP::QS_RX_CURR_OBJ)) + { + QP::QS::rxPriv_.currObj[QP::QS::SM_OBJ] + = reinterpret_cast(l_rx.var.obj.addr); + QP::QS::rxPriv_.currObj[QP::QS::AO_OBJ] + = reinterpret_cast(l_rx.var.obj.addr); + } + rxReportAck_( + static_cast(l_rx.var.obj.recId)); } else { rxReportError_(l_rx.var.obj.recId); - tran_(ERROR_STATE); } break; } case WAIT4_QUERY_FRAME: { - // keep ignoring the data until a frame is collected - break; - } - case WAIT4_EVT_PRIO: { - l_rx.var.evt.prio = b; - l_rx.var.evt.sig = 0U; - l_rx.var.evt.idx = 0U; - tran_(WAIT4_EVT_SIG); + queryCurrObj_(l_rx.var.obj.kind); break; } - case WAIT4_EVT_SIG: { - l_rx.var.evt.sig |= static_cast( - static_cast(b) << l_rx.var.evt.idx); - l_rx.var.evt.idx += 8U; - if (l_rx.var.evt.idx - == (8U *static_cast(Q_SIGNAL_SIZE))) - { - l_rx.var.evt.len = 0U; - l_rx.var.evt.idx = 0U; - tran_(WAIT4_EVT_LEN); + case WAIT4_EVT_FRAME: { + // NOTE: Ack was already reported in the WAIT4_EVT_LEN state +#ifdef Q_UTEST + QP::QS::onTestEvt(l_rx.var.evt.e); // "massage" the event +#endif // Q_UTEST + // use 'i' as status, 0 == success,no-recycle + i = 0U; + + if (l_rx.var.evt.prio == 0U) { // publish + QP::QActive::publish_(l_rx.var.evt.e, &QP::QS::rxPriv_, 0U); } - break; - } - case WAIT4_EVT_LEN: { - l_rx.var.evt.len |= static_cast( - static_cast(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(QP::QEvt)) - <= static_cast( - QP::QF::poolGetMaxBlockSize())) + else if (l_rx.var.evt.prio < QF_MAX_ACTIVE) { + if (!QP::QActive::registry_[l_rx.var.evt.prio]->POST_X( + l_rx.var.evt.e, + 0U, // margin + &QP::QS::rxPriv_)) { - // report Ack before generating any other QS records - rxReportAck_(QP::QS_RX_EVENT); + // failed QACTIVE_POST() recycles the event + i = 0x80U; // failure, no recycle + } + } + else if (l_rx.var.evt.prio == 255U) { + // dispatch to the current SM object + if (QP::QS::rxPriv_.currObj[QP::QS::SM_OBJ] != nullptr) { + // increment the ref-ctr to simulate the situation + // when the event is just retreived from a queue. + // This is expected for the following QF::gc() call. + QP::QEvt_refCtr_inc_(l_rx.var.evt.e); - l_rx.var.evt.e = QP::QF::newX_( - (static_cast(l_rx.var.evt.len) - + sizeof(QP::QEvt)), - 0U, // margin - static_cast(l_rx.var.evt.sig)); - // event allocated? - if (l_rx.var.evt.e != nullptr) { - l_rx.var.evt.p = - reinterpret_cast(l_rx.var.evt.e); - l_rx.var.evt.p = &l_rx.var.evt.p[sizeof(QP::QEvt)]; - if (l_rx.var.evt.len > 0U) { - tran_(WAIT4_EVT_PAR); - } - else { - tran_(WAIT4_EVT_FRAME); - } - } - else { - rxReportError_( - static_cast(QP::QS_RX_EVENT)); - tran_(ERROR_STATE); + static_cast( + QP::QS::rxPriv_.currObj[QP::QS::SM_OBJ]) + ->dispatch(l_rx.var.evt.e, 0U); + i = 0x01U; // success, recycle + } + else { + i = 0x81U; // failure, recycle + } + } + else if (l_rx.var.evt.prio == 254U) { + // init the current SM object" + if (QP::QS::rxPriv_.currObj[QP::QS::SM_OBJ] != nullptr) { + // increment the ref-ctr to simulate the situation + // when the event is just retreived from a queue. + // This is expected for the following QF::gc() call. + QP::QEvt_refCtr_inc_(l_rx.var.evt.e); + + static_cast( + QP::QS::rxPriv_.currObj[QP::QS::SM_OBJ]) + ->init(l_rx.var.evt.e, 0U); + i = 0x01U; // success, recycle + } + else { + i = 0x81U; // failure, recycle + } + } + else if (l_rx.var.evt.prio == 253U) { + // post to the current AO + if (QP::QS::rxPriv_.currObj[QP::QS::AO_OBJ] != nullptr) { + if (!static_cast( + QP::QS::rxPriv_.currObj[QP::QS::AO_OBJ])->POST_X( + l_rx.var.evt.e, + 0U, // margin + &QP::QS::rxPriv_)) + { + // failed QACTIVE_POST() recycles the event + i = 0x80U; // failure, no recycle } } else { - rxReportError_( - static_cast(QP::QS_RX_EVENT)); - tran_(ERROR_STATE); + i = 0x81U; // failure, recycle } } - break; - } - case 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) { - tran_(WAIT4_EVT_FRAME); + else { + i = 0x81U; // failure, recycle + } + +#if (QF_MAX_EPOOL > 0U) + // recycle needed? + if ((i & 1U) != 0U) { + QP::QF::gc(l_rx.var.evt.e); + } +#endif + // failure? + if ((i & 0x80U) != 0U) { + rxReportError_(static_cast(QP::QS_RX_EVENT)); + } + else { +#ifdef Q_UTEST + #if Q_UTEST != 0 + QP::QS::processTestEvts_(); // process all events produced + #endif // Q_UTEST != 0 +#endif // Q_UTEST + rxReportDone_(QP::QS_RX_EVENT); } - break; - } - case WAIT4_EVT_FRAME: { - // keep ignoring the data until a frame is collected break; } #ifdef Q_UTEST case WAIT4_TEST_SETUP_FRAME: { - // keep ignoring the data until a frame is collected + rxReportAck_(QP::QS_RX_TEST_SETUP); + QP::QS::tstPriv_.tpNum = 0U; // clear Test-Probes + QP::QS::tstPriv_.testTime = 0U; //clear time tick + // don't clear current objects + QP::QS::onTestSetup(); // application-specific test setup + // no need to report Done break; } case WAIT4_TEST_TEARDOWN_FRAME: { - // keep ignoring the data until a frame is collected + rxReportAck_(QP::QS_RX_TEST_TEARDOWN); + QP::QS::onTestTeardown(); // application-specific test teardown + // no need to report Done break; } case WAIT4_TEST_CONTINUE_FRAME: { - // keep ignoring the data until a frame is collected - break; - } - case WAIT4_TEST_PROBE_DATA: { - l_rx.var.tp.data |= - (static_cast(b) << l_rx.var.tp.idx); - l_rx.var.tp.idx += 8U; - if (l_rx.var.tp.idx == (8U * sizeof(std::uint32_t))) { - l_rx.var.tp.addr = 0U; - l_rx.var.tp.idx = 0U; - tran_(WAIT4_TEST_PROBE_ADDR); - } - break; - } - case WAIT4_TEST_PROBE_ADDR: { - l_rx.var.tp.addr |= - (static_cast(b) << l_rx.var.tp.idx); - l_rx.var.tp.idx += 8U; - if (l_rx.var.tp.idx - == (8U * static_cast(QS_FUN_PTR_SIZE))) - { - tran_(WAIT4_TEST_PROBE_FRAME); - } + rxReportAck_(QP::QS_RX_TEST_CONTINUE); + QP::QS::rxPriv_.inTestLoop = false; // exit the QUTest loop + // no need to report Done break; } case WAIT4_TEST_PROBE_FRAME: { - // keep ignoring the data until a frame is collected + rxReportAck_(QP::QS_RX_TEST_PROBE); + Q_ASSERT_INCRIT(815, + QP::QS::tstPriv_.tpNum + < (sizeof(QP::QS::tstPriv_.tpBuf) + / sizeof(QP::QS::tstPriv_.tpBuf[0]))); + QP::QS::tstPriv_.tpBuf[QP::QS::tstPriv_.tpNum] = l_rx.var.tp; + ++QP::QS::tstPriv_.tpNum; + // no need to report Done break; } #endif // Q_UTEST case ERROR_STATE: { - // keep ignoring the data until a good frame is collected + // keep ignoring all bytes until new frame break; } - default: { // unexpected or unimplemented state - rxReportError_(0x45U); - tran_(ERROR_STATE); + default: { + rxReportError_(0x47U); break; } } @@ -1215,13 +1150,14 @@ static void rxHandleBadFrame_(std::uint8_t const state) noexcept { rxReportError_(0x50U); // error for all bad frames switch (state) { case WAIT4_EVT_FRAME: { - Q_ASSERT_ID(910, l_rx.var.evt.e != nullptr); + Q_ASSERT_INCRIT(910, l_rx.var.evt.e != nullptr); #if (QF_MAX_EPOOL > 0U) QP::QF::gc(l_rx.var.evt.e); // don't leak an allocated event #endif break; } default: { + // intentionally empty break; } } @@ -1229,43 +1165,82 @@ static void rxHandleBadFrame_(std::uint8_t const state) noexcept { //............................................................................ static void rxReportAck_(enum QP::QSpyRxRecords const recId) noexcept { - QS_CRIT_STAT_ - QS_CRIT_E_(); QP::QS::beginRec_(static_cast(QP::QS_RX_STATUS)); QS_U8_PRE_(recId); // record ID QP::QS::endRec_(); - QS_CRIT_X_(); - QS_REC_DONE(); // user callback (if defined) } //............................................................................ static void rxReportError_(std::uint8_t const code) noexcept { - QS_CRIT_STAT_ - QS_CRIT_E_(); QP::QS::beginRec_(static_cast(QP::QS_RX_STATUS)); QS_U8_PRE_(0x80U | code); // error code QP::QS::endRec_(); - QS_CRIT_X_(); - QS_REC_DONE(); // user callback (if defined) } //............................................................................ static void rxReportDone_(enum QP::QSpyRxRecords const recId) noexcept { - QS_CRIT_STAT_ - QS_CRIT_E_(); QP::QS::beginRec_(static_cast(QP::QS_TARGET_DONE)); QS_TIME_PRE_(); // timestamp QS_U8_PRE_(recId); // record ID QP::QS::endRec_(); - QS_CRIT_X_(); - QS_REC_DONE(); // user callback (if defined) } //............................................................................ -static void rxPoke_(void) noexcept { +static void queryCurrObj_(std::uint8_t obj_kind) noexcept { + Q_REQUIRE_INCRIT(200, obj_kind < Q_DIM(QP::QS::rxPriv_.currObj)); + + if (QP::QS::rxPriv_.currObj[obj_kind] != nullptr) { + QP::QS::beginRec_(static_cast(QP::QS_QUERY_DATA)); + QS_TIME_PRE_(); // timestamp + QS_U8_PRE_(obj_kind); // object kind + QS_OBJ_PRE_(QP::QS::rxPriv_.currObj[obj_kind]); // object pointer + switch (obj_kind) { + case QP::QS::SM_OBJ: // intentionally fall through + case QP::QS::AO_OBJ: + QS_FUN_PRE_(reinterpret_cast( + QP::QS::rxPriv_.currObj[obj_kind])->getStateHandler()); + break; + case QP::QS::MP_OBJ: + QS_MPC_PRE_(reinterpret_cast( + QP::QS::rxPriv_.currObj[obj_kind])->getNFree()); + QS_MPC_PRE_(reinterpret_cast( + QP::QS::rxPriv_.currObj[obj_kind])->getNMin()); + break; + case QP::QS::EQ_OBJ: + QS_EQC_PRE_(reinterpret_cast( + QP::QS::rxPriv_.currObj[obj_kind])->getNFree()); + QS_EQC_PRE_(reinterpret_cast( + QP::QS::rxPriv_.currObj[obj_kind])->getNMin()); + break; + case QP::QS::TE_OBJ: + QS_OBJ_PRE_(reinterpret_cast( + QP::QS::rxPriv_.currObj[obj_kind])->getAct()); + QS_TEC_PRE_(reinterpret_cast( + QP::QS::rxPriv_.currObj[obj_kind])->getCtr()); + QS_TEC_PRE_(reinterpret_cast( + QP::QS::rxPriv_.currObj[obj_kind])->getInterval()); + QS_SIG_PRE_(reinterpret_cast( + QP::QS::rxPriv_.currObj[obj_kind])->sig); + QS_U8_PRE_ (reinterpret_cast( + QP::QS::rxPriv_.currObj[obj_kind])->refCtr_); + break; + default: + // intentionally empty + break; + } + QP::QS::endRec_(); + QS_REC_DONE(); // user callback (if defined) + } + else { + rxReportError_(static_cast(QP::QS_RX_AO_FILTER)); + } +} + +//............................................................................ +static void rxPoke_() noexcept { std::uint8_t * ptr = static_cast(QP::QS::rxPriv_.currObj[QP::QS::AP_OBJ]); ptr = &ptr[l_rx.var.poke.offs]; @@ -1291,3 +1266,5 @@ static void rxPoke_(void) noexcept { } } // unnamed namespace + +//! @endcond diff --git a/src/qs/qstamp.cpp b/src/qs/qstamp.cpp new file mode 100644 index 0000000..1b70f73 --- /dev/null +++ b/src/qs/qstamp.cpp @@ -0,0 +1,42 @@ +//$file${src::qs::qstamp.cpp} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv +// +// Model: qpcpp.qm +// File: ${src::qs::qstamp.cpp} +// +// This code has been generated by QM 6.2.3 . +// DO NOT EDIT THIS FILE MANUALLY. All your changes will be lost. +// +// ____________________________________ +// / / +// / GGGGGGG PPPPPPPP LL / +// / GG GG PP PP LL / +// / GG PP PP LL / +// / GG GGGGG PPPPPPPP LL / +// / GG GG PP LL / +// / GGGGGGG PP LLLLLLL / +// /___________________________________/ +// +// Copyright (c) 2005 Quantum Leaps, LLC +// SPDX-License-Identifier: GPL-3.0-or-later +// +// This generated code is open-source software licensed under the GNU +// General Public License (GPL) as published by the Free Software Foundation +// (see ). +// +// NOTE: +// The GPL does NOT permit the incorporation of this code into proprietary +// programs. Please contact Quantum Leaps for commercial licensing options, +// which expressly supersede the GPL and are designed explicitly for licensees +// interested in retaining the proprietary status of the generated code. +// +// Quantum Leaps contact information: +// +// +// +//$endhead${src::qs::qstamp.cpp} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +#include "qstamp.hpp" + +namespace QP { +char const BUILD_DATE[12] = __DATE__; +char const BUILD_TIME[9] = __TIME__; +} // namespace QP diff --git a/src/qs/qutest.cpp b/src/qs/qutest.cpp new file mode 100644 index 0000000..42b0112 --- /dev/null +++ b/src/qs/qutest.cpp @@ -0,0 +1,578 @@ +//$file${src::qs::qutest.cpp} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv +// +// Model: qpcpp.qm +// File: ${src::qs::qutest.cpp} +// +// This code has been generated by QM 6.2.3 . +// DO NOT EDIT THIS FILE MANUALLY. All your changes will be lost. +// +// ____________________________________ +// / / +// / GGGGGGG PPPPPPPP LL / +// / GG GG PP PP LL / +// / GG PP PP LL / +// / GG GGGGG PPPPPPPP LL / +// / GG GG PP LL / +// / GGGGGGG PP LLLLLLL / +// /___________________________________/ +// +// Copyright (c) 2005 Quantum Leaps, LLC +// SPDX-License-Identifier: GPL-3.0-or-later +// +// This generated code is open-source software licensed under the GNU +// General Public License (GPL) as published by the Free Software Foundation +// (see ). +// +// NOTE: +// The GPL does NOT permit the incorporation of this code into proprietary +// programs. Please contact Quantum Leaps for commercial licensing options, +// which expressly supersede the GPL and are designed explicitly for licensees +// interested in retaining the proprietary status of the generated code. +// +// Quantum Leaps contact information: +// +// +// +//$endhead${src::qs::qutest.cpp} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +// only build when Q_UTEST is defined +#ifdef Q_UTEST + +#define QP_IMPL // this is QP implementation +#include "qp_port.hpp" // QP port +#include "qp_pkg.hpp" // QP package-scope interface +#include "qsafe.h" // QP Functional Safety (FuSa) Subsystem +#include "qs_port.hpp" // QS port +#include "qs_pkg.hpp" // QS package-scope internal interface + +//$skip${QP_VERSION} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv +// Check for the minimum required QP version +#if (QP_VERSION < 730U) || (QP_VERSION != ((QP_RELEASE^4294967295U)%0x2710U)) +#error qpcpp version 7.3.0 or higher required +#endif +//$endskip${QP_VERSION} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +//$define${QS::QUTest} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv +namespace QP { +namespace QS { + +} // namespace QS +} // namespace QP +//$enddef${QS::QUTest} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +//============================================================================ +//! @cond INTERNAL + +namespace QP { +namespace QS { + +TestAttr tstPriv_; + +//............................................................................ +void test_pause_() { + beginRec_(static_cast(QS_TEST_PAUSED)); + endRec_(); + onTestLoop(); +} + +//............................................................................ +std::uint32_t getTestProbe_(QSpyFunPtr const api) noexcept { + std::uint32_t data = 0U; + for (std::uint8_t i = 0U; i < tstPriv_.tpNum; ++i) { + if (tstPriv_.tpBuf[i].addr == reinterpret_cast(api)) { + data = tstPriv_.tpBuf[i].data; + + QS_CRIT_STAT + QS_CRIT_ENTRY(); + QS_MEM_SYS(); + QS::beginRec_(static_cast(QS_TEST_PROBE_GET)); + QS_TIME_PRE_(); // timestamp + QS_FUN_PRE_(api); // the calling API + QS_U32_PRE_(data); // the Test-Probe data + QS::endRec_(); + + QS_REC_DONE(); // user callback (if defined) + + --tstPriv_.tpNum; // one less Test-Probe + // move all remaining entries in the buffer up by one + for (std::uint8_t j = i; j < tstPriv_.tpNum; ++j) { + tstPriv_.tpBuf[j] = tstPriv_.tpBuf[j + 1U]; + } + QS_MEM_APP(); + QS_CRIT_EXIT(); + break; // we are done (Test-Probe retreived) + } + } + return data; +} + +//............................................................................ +QSTimeCtr onGetTime() { + return (++tstPriv_.testTime); +} + +} // namespace QS +} // namespace QP + +//............................................................................ +extern "C" { + +Q_NORETURN Q_onError(char const * const module, int_t const location) { + QP::QS::beginRec_(static_cast(QP::QS_ASSERT_FAIL)); + QS_TIME_PRE_(); + QS_U16_PRE_(location); + QS_STR_PRE_((module != nullptr) ? module : "?"); + QP::QS::endRec_(); + + QP::QS::onFlush(); // flush the assertion record to the host + QP::QS::onCleanup(); // cleanup after the failure + QP::QS::onReset(); // reset the target to prevent it from continuing + for (;;) { // onReset() should not return, but to ensure no-return... + } +} + +} // extern "C" + +//! @endcond +//============================================================================ + +// QP-stub for QUTest +// NOTE: The QP-stub is needed for unit testing QP applications, but might +// NOT be needed for testing QP itself. In that case, the build process +// can define Q_UTEST=0 to exclude the QP-stub from the build. +#if Q_UTEST != 0 + +// unnamed namespace for local definitions with internal linkage +namespace { +Q_DEFINE_THIS_MODULE("qutest") +} // unnamed namespace + +namespace QP { +namespace QS { + +void processTestEvts_() { + QS_TEST_PROBE_DEF(&QS::processTestEvts_) + + // return immediately (do nothing) for Test Probe != 0 + QS_TEST_PROBE(return;) + + while (tstPriv_.readySet.notEmpty()) { + std::uint_fast8_t const p = tstPriv_.readySet.findMax(); + QActive * const a = QActive::registry_[p]; + + QEvt const * const e = a->get_(); + a->dispatch(e, a->getPrio()); + #if (QF_MAX_EPOOL > 0U) + QF::gc(e); + #endif + if (a->getEQueue().isEmpty()) { // empty queue? + tstPriv_.readySet.remove(p); +#ifndef Q_UNSAFE + tstPriv_.readySet.verify_(&tstPriv_.readySet_dis); +#endif + } + } +} + +} // namespace QS +} // namespace QP + +//$define${QS::QUTest-stub} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv +namespace QP { +namespace QF { + +//${QS::QUTest-stub::QF::init} ............................................... +void init() { + // Clear the internal QF variables, so that the framework can start + // correctly even if the startup code fails to clear the uninitialized + // data (as is required by the C++ Standard). + bzero_(&QF::priv_, sizeof(QF::priv_)); + bzero_(&QS::tstPriv_, sizeof(QS::tstPriv_)); + bzero_(&QActive::registry_[0], sizeof(QActive::registry_)); + + #ifndef Q_UNSAFE + QS::tstPriv_.readySet.update_(&QS::tstPriv_.readySet_dis); + #endif +} + +//${QS::QUTest-stub::QF::stop} ............................................... +void stop() { + QS::onReset(); +} + +//${QS::QUTest-stub::QF::run} ................................................ +int_t run() { + QS_CRIT_STAT + QS_CRIT_ENTRY(); + QS_MEM_SYS(); + + // function dictionaries for the standard API + QS_FUN_DICTIONARY(&QActive::post_); + QS_FUN_DICTIONARY(&QActive::postLIFO); + QS_FUN_DICTIONARY(&QS::processTestEvts_); + + // produce the QS_QF_RUN trace record + QS_BEGIN_PRE_(QS_QF_RUN, 0U) + QS_END_PRE_() + + QS_MEM_APP(); + QS_CRIT_EXIT(); + + QS::processTestEvts_(); // process all events posted so far + QS::onTestLoop(); // run the unit test + QS::onCleanup(); // application cleanup + return 0; // return no error +} + +} // namespace QF + +//${QS::QUTest-stub::QActive} ................................................ + +//${QS::QUTest-stub::QActive::start} ......................................... +void QActive::start( + QPrioSpec const prioSpec, + QEvt const * * const qSto, + std::uint_fast16_t const qLen, + void * const stkSto, + std::uint_fast16_t const stkSize, + void const * const par) +{ + Q_UNUSED_PAR(stkSto); + Q_UNUSED_PAR(stkSize); + + m_prio = static_cast(prioSpec & 0xFFU); // QF-priol + m_pthre = static_cast(prioSpec >> 8U); // preemption-thre. + register_(); // make QF aware of this AO + + m_eQueue.init(qSto, qLen); // initialize QEQueue of this AO + + this->init(par, m_prio); // take the top-most initial tran. (virtual) +} + +//${QS::QUTest-stub::QActive::stop} .......................................... +#ifdef QACTIVE_CAN_STOP +void QActive::stop() { + unsubscribeAll(); // unsubscribe from all events + unregister_(); // remove this object from QF +} + +#endif // def QACTIVE_CAN_STOP + +//${QS::QUTest-stub::QTimeEvt} ............................................... + +//${QS::QUTest-stub::QTimeEvt::tick1_} ....................................... +void QTimeEvt::tick1_( + std::uint_fast8_t const tickRate, + void const * const sender) +{ + QS_CRIT_STAT + QS_CRIT_ENTRY(); + QS_MEM_SYS(); + + QTimeEvt *prev = &QTimeEvt::timeEvtHead_[tickRate]; + + QS_BEGIN_PRE_(QS_QF_TICK, 0U) + prev->m_ctr = (prev->m_ctr + 1U); + QS_TEC_PRE_(prev->m_ctr); // tick ctr + QS_U8_PRE_(tickRate); // tick rate + QS_END_PRE_() + + // is current Time Event object provided? + QTimeEvt *t = static_cast(QS::rxPriv_.currObj[QS::TE_OBJ]); + if (t != nullptr) { + + // the time event must be armed + Q_ASSERT_INCRIT(810, t->m_ctr != 0U); + + QActive * const act = static_cast(t->m_act); + + // the recipient AO must be provided + Q_ASSERT_INCRIT(820, act != nullptr); + + // periodic time evt? + if (t->m_interval != 0U) { + t->m_ctr = t->m_interval; // rearm the time event + } + else { // one-shot time event: automatically disarm + t->m_ctr = 0U; // auto-disarm + // mark time event 't' as NOT linked + t->refCtr_ = static_cast(t->refCtr_ + & static_cast(~TE_IS_LINKED)); + + QS_BEGIN_PRE_(QS_QF_TIMEEVT_AUTO_DISARM, act->m_prio) + QS_OBJ_PRE_(t); // this time event object + QS_OBJ_PRE_(act); // the target AO + QS_U8_PRE_(tickRate); // tick rate + QS_END_PRE_() + } + + QS_BEGIN_PRE_(QS_QF_TIMEEVT_POST, act->m_prio) + QS_TIME_PRE_(); // timestamp + QS_OBJ_PRE_(t); // the time event object + QS_SIG_PRE_(t->sig); // signal of this time event + QS_OBJ_PRE_(act); // the target AO + QS_U8_PRE_(tickRate); // tick rate + QS_END_PRE_() + QS_MEM_APP(); + QS_CRIT_EXIT(); // exit critical section before posting + + // asserts if queue overflows + static_cast(act->POST(t, sender)); + + QS_CRIT_ENTRY(); + QS_MEM_SYS(); + } + + // update the linked list of time events + for (;;) { + t = prev->m_next; // advance down the time evt. list + + // end of the list? + if (t == nullptr) { + + // any new time events armed since the last run of tick()? + if (QTimeEvt::timeEvtHead_[tickRate].m_act != nullptr) { + + // sanity check + Q_ASSERT_INCRIT(830, prev != nullptr); + prev->m_next = QTimeEvt::timeEvtHead_[tickRate].toTimeEvt(); + QTimeEvt::timeEvtHead_[tickRate].m_act = nullptr; + t = prev->m_next; // switch to the new list + } + else { + break; // all currently armed time evts. processed + } + } + + // time event scheduled for removal? + if (t->m_ctr == 0U) { + prev->m_next = t->m_next; + // mark time event 't' as NOT linked + t->refCtr_ = static_cast(t->refCtr_ + & static_cast(~TE_IS_LINKED)); + // do NOT advance the prev pointer + QS_MEM_APP(); + QS_CRIT_EXIT(); // exit crit. section to reduce latency + } + else { + prev = t; // advance to this time event + QS_MEM_APP(); + QS_CRIT_EXIT(); // exit crit. section to reduce latency + } + QS_CRIT_ENTRY(); // re-enter crit. section to continue + QS_MEM_SYS(); + } + + QS_MEM_APP(); + QS_CRIT_EXIT(); +} + +//${QS::QUTest-stub::QHsmDummy} .............................................. + +//${QS::QUTest-stub::QHsmDummy::QHsmDummy} ................................... +QHsmDummy::QHsmDummy() +: QAsm() +{} + +//${QS::QUTest-stub::QHsmDummy::init} ........................................ +void QHsmDummy::init( + void const * const e, + std::uint_fast8_t const qs_id) +{ + Q_UNUSED_PAR(e); + + #ifdef Q_SPY + if ((QS::priv_.flags & 0x01U) == 0U) { + QS::priv_.flags |= 0x01U; + QS_FUN_DICTIONARY(&QP::QHsm::top); + } + #else + Q_UNUSED_PAR(qs_id); + #endif + + QS_CRIT_STAT + QS_CRIT_ENTRY(); + QS_MEM_SYS(); + QS_BEGIN_PRE_(QS_QEP_STATE_INIT, qs_id) + QS_OBJ_PRE_(this); // this state machine object + QS_FUN_PRE_(m_state.fun); // the source state + QS_FUN_PRE_(m_temp.fun); // the target of the initial tran. + QS_END_PRE_() + QS_MEM_APP(); + QS_CRIT_EXIT(); +} + +//${QS::QUTest-stub::QHsmDummy::dispatch} .................................... +void QHsmDummy::dispatch( + QEvt const * const e, + std::uint_fast8_t const qs_id) +{ + 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_(this); // this state machine object + QS_FUN_PRE_(m_state.fun); // the current state + QS_END_PRE_() + QS_MEM_APP(); + QS_CRIT_EXIT(); +} + +//${QS::QUTest-stub::QActiveDummy} ........................................... + +//${QS::QUTest-stub::QActiveDummy::QActiveDummy} ............................. +QActiveDummy::QActiveDummy() +: QActive(nullptr) +{} + +//${QS::QUTest-stub::QActiveDummy::init} ..................................... +void QActiveDummy::init( + void const * const e, + std::uint_fast8_t const qs_id) +{ + Q_UNUSED_PAR(e); + Q_UNUSED_PAR(qs_id); + + #ifdef Q_SPY + if ((QS::priv_.flags & 0x01U) == 0U) { + QS::priv_.flags |= 0x01U; + QS_FUN_DICTIONARY(&QP::QHsm::top); + } + #endif + + QS_CRIT_STAT + QS_CRIT_ENTRY(); + QS_MEM_SYS(); + QS_BEGIN_PRE_(QS_QEP_STATE_INIT, m_prio) + QS_OBJ_PRE_(this); // this state machine object + QS_FUN_PRE_(m_state.fun); // the source state + QS_FUN_PRE_(m_temp.fun); // the target of the initial tran. + QS_END_PRE_() + QS_MEM_APP(); + QS_CRIT_EXIT(); +} + +//${QS::QUTest-stub::QActiveDummy::dispatch} ................................. +void QActiveDummy::dispatch( + QEvt const * const e, + std::uint_fast8_t const qs_id) +{ + 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_(this); // this state machine object + QS_FUN_PRE_(m_state.fun); // the current state + QS_END_PRE_() + QS_MEM_APP(); + QS_CRIT_EXIT(); +} + +//${QS::QUTest-stub::QActiveDummy::fakePost} ................................. +bool QActiveDummy::fakePost( + QEvt const * const e, + std::uint_fast16_t const margin, + void const * const sender) noexcept +{ + QS_TEST_PROBE_DEF(&QActive::post_) + + QF_CRIT_STAT + QF_CRIT_ENTRY(); + QF_MEM_SYS(); + + // test-probe#1 for faking queue overflow + bool status = true; + QS_TEST_PROBE_ID(1, + status = false; + if (margin == QF::NO_MARGIN) { + // fake assertion Mod=qf_actq,Loc=110 + Q_onError("qf_actq", 110); + } + ) + + // is it a mutable event? + if (e->getPoolId_() != 0U) { + QEvt_refCtr_inc_(e); + } + + std::uint_fast8_t const rec = + (status ? static_cast(QS_QF_ACTIVE_POST) + : static_cast(QS_QF_ACTIVE_POST_ATTEMPT)); + QS_BEGIN_PRE_(rec, m_prio) + QS_TIME_PRE_(); // timestamp + QS_OBJ_PRE_(sender); // the sender object + QS_SIG_PRE_(e->sig); // the signal of the event + QS_OBJ_PRE_(this); // this active object + QS_2U8_PRE_(e->getPoolId_(), 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_(m_prio)) { + QS::onTestPost(sender, this, e, status); + } + QF_MEM_APP(); + QF_CRIT_EXIT(); + + // recycle the event immediately, because it was not really posted + #if (QF_MAX_EPOOL > 0U) + QF::gc(e); + #endif + + return status; +} + +//${QS::QUTest-stub::QActiveDummy::fakePostLIFO} ............................. +void QActiveDummy::fakePostLIFO(QEvt const * const e) noexcept { + QS_TEST_PROBE_DEF(&QActive::postLIFO) + + QF_CRIT_STAT + QF_CRIT_ENTRY(); + QF_MEM_SYS(); + + // test-probe#1 for faking queue overflow + QS_TEST_PROBE_ID(1, + // fake assertion Mod=qf_actq,Loc=210 + Q_onError("qf_actq", 210); + ) + + // is it a mutable event? + if (e->getPoolId_() != 0U) { + QEvt_refCtr_inc_(e); + } + + QS_BEGIN_PRE_(QS_QF_ACTIVE_POST_LIFO, m_prio) + QS_TIME_PRE_(); // timestamp + QS_SIG_PRE_(e->sig); // the signal of this event + QS_OBJ_PRE_(this); // this active object + QS_2U8_PRE_(e->getPoolId_(), 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_(m_prio)) { + QS::onTestPost(nullptr, this, e, true); + } + QF_MEM_APP(); + QF_CRIT_EXIT(); + + // recycle the event immediately, because it was not really posted + #if (QF_MAX_EPOOL > 0U) + QF::gc(e); + #endif +} + +} // namespace QP +//$enddef${QS::QUTest-stub} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +#endif // Q_UTEST != 0 + +#endif // def Q_UTEST diff --git a/src/qs_fp.cpp b/src/qs_fp.cpp deleted file mode 100644 index 70a1584..0000000 --- a/src/qs_fp.cpp +++ /dev/null @@ -1,137 +0,0 @@ -//$file${src::qs::qs_fp.cpp} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv -// -// Model: qpcpp.qm -// File: ${src::qs::qs_fp.cpp} -// -// This code has been generated by QM 5.2.5 . -// DO NOT EDIT THIS FILE MANUALLY. All your changes will be lost. -// -// This code is covered by the following QP license: -// License # : LicenseRef-QL-dual -// Issued to : Any user of the QP/C++ real-time embedded framework -// Framework(s) : qpcpp -// Support ends : 2023-12-31 -// License scope: -// -// Copyright (C) 2005 Quantum Leaps, LLC . -// -// 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: -// -// The terms of the closed source Quantum Leaps commercial licenses -// can be found at: -// -// Redistributions in source code must retain this top-level comment block. -// Plagiarizing this software to sidestep the license obligations is illegal. -// -// Contact information: -// -// -// -//$endhead${src::qs::qs_fp.cpp} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -//! @file -//! @brief QS floating point output implementation - -#define QP_IMPL // this is QF/QK implementation -#include "qs_port.hpp" // QS port -#include "qs_pkg.hpp" // QS package-scope internal interface - -//$skip${QP_VERSION} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv -// Check for the minimum required QP version -#if (QP_VERSION < 700U) || (QP_VERSION != ((QP_RELEASE^4294967295U) % 0x3E8U)) -#error qpcpp version 7.0.0 or higher required -#endif -//$endskip${QP_VERSION} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -//$define${QS::QS-tx-fp} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv -namespace QP { -namespace QS { - -//${QS::QS-tx-fp::f32_fmt_} .................................................. -void f32_fmt_( - std::uint8_t format, - float32_t d) noexcept -{ - union F32Rep { - float32_t f; - std::uint32_t u; - } fu32; // the internal binary representation - std::uint8_t chksum_ = priv_.chksum; // put in a temporary (register) - std::uint8_t * const buf_ = priv_.buf; // put in a temporary (register) - QSCtr head_ = priv_.head; // put in a temporary (register) - QSCtr const end_ = priv_.end; // put in a temporary (register) - - fu32.f = d; // assign the binary representation - - priv_.used = (priv_.used + 5U); // 5 bytes about to be added - QS_INSERT_ESC_BYTE_(format) // insert the format byte - - for (std::uint_fast8_t i = 4U; i != 0U; --i) { - format = static_cast(fu32.u); - QS_INSERT_ESC_BYTE_(format) - fu32.u >>= 8U; - } - - priv_.head = head_; // save the head - priv_.chksum = chksum_; // save the checksum -} - -//${QS::QS-tx-fp::f64_fmt_} .................................................. -void f64_fmt_( - std::uint8_t format, - float32_t d) noexcept -{ - union F64Rep { - float64_t d; - std::uint32_t u[2]; - } fu64; // the internal binary representation - std::uint8_t chksum_ = priv_.chksum; - std::uint8_t * const buf_ = priv_.buf; - QSCtr head_ = priv_.head; - QSCtr const end_ = priv_.end; - std::uint32_t i; - // static constant untion to detect endianness of the machine - static union U32Rep { - std::uint32_t u32; - std::uint8_t u8; - } const endian = { 1U }; - - fu64.d = d; // assign the binary representation - - // is this a big-endian machine? - if (endian.u8 == 0U) { - // swap fu64.u[0] <-> fu64.u[1]... - i = fu64.u[0]; - fu64.u[0] = fu64.u[1]; - fu64.u[1] = i; - } - - priv_.used = (priv_.used + 9U); // 9 bytes about to be added - QS_INSERT_ESC_BYTE_(format) // insert the format byte - - // output 4 bytes from fu64.u[0]... - for (i = 4U; i != 0U; --i) { - QS_INSERT_ESC_BYTE_(static_cast(fu64.u[0])) - fu64.u[0] >>= 8U; - } - - // output 4 bytes from fu64.u[1]... - for (i = 4U; i != 0U; --i) { - QS_INSERT_ESC_BYTE_(static_cast(fu64.u[1])) - fu64.u[1] >>= 8U; - } - - priv_.head = head_; // update the head - priv_.chksum = chksum_; // update the checksum -} - -} // namespace QS -} // namespace QP -//$enddef${QS::QS-tx-fp} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/src/qs_pkg.hpp b/src/qs_pkg.hpp deleted file mode 100644 index 7dbe63a..0000000 --- a/src/qs_pkg.hpp +++ /dev/null @@ -1,304 +0,0 @@ -//$file${include::qs_pkg.hpp} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv -// -// Model: qpcpp.qm -// File: ${include::qs_pkg.hpp} -// -// This code has been generated by QM 5.2.5 . -// DO NOT EDIT THIS FILE MANUALLY. All your changes will be lost. -// -// This code is covered by the following QP license: -// License # : LicenseRef-QL-dual -// Issued to : Any user of the QP/C++ real-time embedded framework -// Framework(s) : qpcpp -// Support ends : 2023-12-31 -// License scope: -// -// Copyright (C) 2005 Quantum Leaps, LLC . -// -// 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: -// -// The terms of the closed source Quantum Leaps commercial licenses -// can be found at: -// -// Redistributions in source code must retain this top-level comment block. -// Plagiarizing this software to sidestep the license obligations is illegal. -// -// Contact information: -// -// -// -//$endhead${include::qs_pkg.hpp} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -//! @file -//! @brief Internal (package scope) QS/C++ interface. - -#ifndef QS_PKG_HPP_ -#define QS_PKG_HPP_ - -//============================================================================ -namespace QP { - -//! QS received record types (RX channel) -//! -//! @details -//! This enumeration specifies the record types for the QS receive channel -enum QSpyRxRecords : std::uint8_t { - QS_RX_INFO, //!< query Target info (ver, config, tstamp) - QS_RX_COMMAND, //!< execute a user-defined command in the Target - QS_RX_RESET, //!< reset the Target - QS_RX_TICK, //!< call QF_tick() - 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_RX_TEST_WAIT() - QS_RX_QUERY_CURR, //!< query the "current object" in the Target - QS_RX_EVENT //!< inject an event to the Target (post/publish) -}; - -//! @brief Frame character of the QS output protocol -constexpr std::uint8_t QS_FRAME = 0x7EU; - -//! @brief Escape character of the QS output protocol -constexpr std::uint8_t QS_ESC = 0x7DU; - -//! @brief Escape modifier of the QS output protocol -//! -//! @details -//! The escaped byte is XOR-ed with the escape modifier before it is inserted -//! into the QS buffer. -constexpr std::uint8_t QS_ESC_XOR = 0x20U; - -//! @brief Escape character of the QS output protocol -constexpr std::uint8_t QS_GOOD_CHKSUM = 0xFFU; - -} // namespace QP - -//============================================================================ -// Macros for use inside other macros or internally in the QP code - -//! Internal QS macro to insert an un-escaped byte into the QS buffer -#define QS_INSERT_BYTE_(b_) \ - buf_[head_] = (b_); \ - ++head_; \ - if (head_ == end_) { \ - head_ = 0U; \ - } - -//! Internal QS macro to insert an escaped byte into the QS buffer -#define QS_INSERT_ESC_BYTE_(b_) \ - chksum_ += (b_); \ - if (((b_) != QS_FRAME) && ((b_) != QS_ESC)) { \ - QS_INSERT_BYTE_(b_) \ - } \ - else { \ - QS_INSERT_BYTE_(QS_ESC) \ - QS_INSERT_BYTE_(static_cast((b_) ^ QS_ESC_XOR)) \ - priv_.used = (priv_.used + 1U); \ - } - -//! Internal QS macro to begin a predefined QS record with critical section. -//! @note -//! This macro is intended to use only inside QP components and NOT -//! at the application level. -//! @sa QS_BEGIN_ID() -//! -#define QS_BEGIN_PRE_(rec_, qs_id_) \ - if (QS_GLB_CHECK_(rec_) && QS_LOC_CHECK_(qs_id_)) { \ - QS_CRIT_E_(); \ - QP::QS::beginRec_(static_cast(rec_)); - -//! Internal QS macro to end a predefined QS record with critical section. -//! @note -//! This macro is intended to use only inside QP components and NOT -//! at the application level. -//! @sa QS_END() -//! -#define QS_END_PRE_() \ - QP::QS::endRec_(); \ - QS_CRIT_X_(); \ - } - -//! Internal QS macro to begin a predefined QS record without critical section -//! -//! @note -//! This macro is intended to use only inside QP components and NOT -//! at the application level. -//! @sa QS_BEGIN_NOCRIT_PRE_() -#define QS_BEGIN_NOCRIT_PRE_(rec_, qs_id_) \ - if (QS_GLB_CHECK_(rec_) && QS_LOC_CHECK_(qs_id_)) { \ - QP::QS::beginRec_(static_cast(rec_)); - -//! Internal QS macro to end a predefiend QS record without critical section -//! -//! @note -//! This macro is intended to use only inside QP components and NOT -//! at the application level. @sa #QS_END_NOCRIT -#define QS_END_NOCRIT_PRE_() \ - QP::QS::endRec_(); \ - } - -#if (Q_SIGNAL_SIZE == 1U) - //! Internal QS macro to output an unformatted event signal data element - //! @note - //! The size of the pointer depends on the macro #Q_SIGNAL_SIZE. - #define QS_SIG_PRE_(sig_) \ - (QP::QS::u8_raw_(static_cast(sig_))) -#elif (Q_SIGNAL_SIZE == 2U) - #define QS_SIG_PRE_(sig_) \ - (QP::QS::u16_raw_(static_cast(sig_))) -#elif (Q_SIGNAL_SIZE == 4U) - #define QS_SIG_PRE_(sig_) \ - (QP::QS::u32_raw_(static_cast(sig_))) -#endif - -//! Internal QS macro to output an unformatted uint8_t data element -#define QS_U8_PRE_(data_) \ - (QP::QS::u8_raw_(static_cast(data_))) - -//! Internal QS macro to output 2 unformatted uint8_t data elements -#define QS_2U8_PRE_(data1_, data2_) \ - (QP::QS::u8u8_raw_(static_cast(data1_), \ - static_cast(data2_))) - -//! Internal QS macro to output an unformatted uint16_t data element -#define QS_U16_PRE_(data_) \ - (QP::QS::u16_raw_(static_cast(data_))) - -//! Internal QS macro to output an unformatted uint32_t data element -#define QS_U32_PRE_(data_) \ - (QP::QS::u32_raw_(static_cast(data_))) - -//! Internal QS macro to output a zero-terminated ASCII string -//! data element -#define QS_STR_PRE_(msg_) (QP::QS::str_raw_(msg_)) - -//! Internal QS macro to output object pointer data element -#define QS_OBJ_PRE_(obj_) (QP::QS::obj_raw_(obj_)) - -#if (QS_FUN_PTR_SIZE == 1U) - #define QS_FUN_PRE_(fun_) \ - (QP::QS::u8_raw_(reinterpret_cast(fun_))) -#elif (QS_FUN_PTR_SIZE == 2U) - #define QS_FUN_PRE_(fun_) \ - (QP::QS::u16_raw_(reinterpret_cast(fun_))) -#elif (QS_FUN_PTR_SIZE == 4U) - #define QS_FUN_PRE_(fun_) \ - (QP::QS::u32_raw_(reinterpret_cast(fun_))) -#elif (QS_FUN_PTR_SIZE == 8U) - #define QS_FUN_PRE_(fun_) \ - (QP::QS::u64_raw_(reinterpret_cast(fun_))) -#else - - //! Internal QS macro to output an unformatted function pointer - //! data element - //! - //! @note - //! The size of the pointer depends on the macro #QS_FUN_PTR_SIZE. - //! If the size is not defined the size of pointer is assumed 4-bytes. - #define QS_FUN_PRE_(fun_) \ - (QP::QS::u32_raw_(reinterpret_cast(fun_))) -#endif - -#if (QF_EQUEUE_CTR_SIZE == 1U) - - //! Internal QS macro to output an unformatted event queue - //! counter data element - //! @note the counter size depends on the macro #QF_EQUEUE_CTR_SIZE. - #define QS_EQC_PRE_(ctr_) \ - QS::u8_raw_(static_cast(ctr_)) -#elif (QF_EQUEUE_CTR_SIZE == 2U) - #define QS_EQC_PRE_(ctr_) \ - QS::u16_raw_(static_cast(ctr_)) -#elif (QF_EQUEUE_CTR_SIZE == 4U) - #define QS_EQC_PRE_(ctr_) \ - QS::u32_raw_(static_cast(ctr_)) -#else - #error "QF_EQUEUE_CTR_SIZE not defined" -#endif - - -#if (QF_EVENT_SIZ_SIZE == 1U) - - //! Internal QS macro to output an unformatted event size - //! data element - //! - //! @note the event size depends on the macro #QF_EVENT_SIZ_SIZE. - #define QS_EVS_PRE_(size_) \ - QS::u8_raw_(static_cast(size_)) -#elif (QF_EVENT_SIZ_SIZE == 2U) - #define QS_EVS_PRE_(size_) \ - QS::u16_raw_(static_cast(size_)) -#elif (QF_EVENT_SIZ_SIZE == 4U) - #define QS_EVS_PRE_(size_) \ - QS::u32_raw_(static_cast(size_)) -#endif - - -#if (QF_MPOOL_SIZ_SIZE == 1U) - - //! Internal QS macro to output an unformatted memory pool - //! block-size data element - //! @note the block-size depends on the macro #QF_MPOOL_SIZ_SIZE. - #define QS_MPS_PRE_(size_) \ - QS::u8_raw_(static_cast(size_)) -#elif (QF_MPOOL_SIZ_SIZE == 2U) - #define QS_MPS_PRE_(size_) \ - QS::u16_raw_(static_cast(size_)) -#elif (QF_MPOOL_SIZ_SIZE == 4U) - #define QS_MPS_PRE_(size_) \ - QS::u32_raw_(static_cast(size_)) -#endif - -#if (QF_MPOOL_CTR_SIZE == 1U) - - //! Internal QS macro to output an unformatted memory pool - //! block-counter data element - //! @note the counter size depends on the macro #QF_MPOOL_CTR_SIZE. - #define QS_MPC_PRE_(ctr_) \ - QS::u8_raw_(static_cast(ctr_)) -#elif (QF_MPOOL_CTR_SIZE == 2U) - #define QS_MPC_PRE_(ctr_) \ - QS::u16_raw_(static_cast(ctr_)) -#elif (QF_MPOOL_CTR_SIZE == 4U) - #define QS_MPC_PRE_(ctr_) \ - QS::u32_raw_(static_cast(ctr_)) -#endif - - -#if (QF_TIMEEVT_CTR_SIZE == 1U) - - //! Internal QS macro to output an unformatted time event - //! tick-counter data element - //! @note the counter size depends on the macro #QF_TIMEEVT_CTR_SIZE. - #define QS_TEC_PRE_(ctr_) \ - QS::u8_raw_(static_cast(ctr_)) -#elif (QF_TIMEEVT_CTR_SIZE == 2U) - #define QS_TEC_PRE_(ctr_) \ - QS::u16_raw_(static_cast(ctr_)) -#elif (QF_TIMEEVT_CTR_SIZE == 4U) - #define QS_TEC_PRE_(ctr_) \ - QS::u32_raw_(static_cast(ctr_)) -#endif - -//! Internal QS macro to cast enumerated QS record number to uint8_t -//! -//! @note Casting from enum to unsigned char violates the MISRA-C++ 2008 rules -//! 5-2-7, 5-2-8 and 5-2-9. Encapsulating this violation in a macro allows to -//! selectively suppress this specific deviation. -#define QS_REC_NUM_(enum_) (static_cast(enum_)) - -#endif // QS_PKG_HPP_ diff --git a/src/qs_port.hpp b/src/qs_port.hpp deleted file mode 100644 index 84581cd..0000000 --- a/src/qs_port.hpp +++ /dev/null @@ -1,59 +0,0 @@ -/// @file -/// @brief QS/C++ port to ARM Cortex-M, generic compiler -/// @cond -///*************************************************************************** -/// Last updated for version 6.6.0 -/// Last updated on 2019-07-30 -/// -/// Q u a n t u m L e a P s -/// ------------------------ -/// Modern Embedded Software -/// -/// Copyright (C) 2005-2019 Quantum Leaps. All rights reserved. -/// -/// This program is open source software: you can redistribute it and/or -/// modify it under the terms of the GNU General Public License as published -/// by the Free Software Foundation, either version 3 of the License, or -/// (at your option) any later version. -/// -/// Alternatively, this program may be distributed and modified under the -/// terms of Quantum Leaps commercial licenses, which expressly supersede -/// the GNU General Public License and are specifically designed for -/// licensees interested in retaining the proprietary status of their code. -/// -/// This program is distributed in the hope that it will be useful, -/// but WITHOUT ANY WARRANTY; without even the implied warranty of -/// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -/// GNU General Public License for more details. -/// -/// You should have received a copy of the GNU General Public License -/// along with this program. If not, see . -/// -/// Contact information: -/// -/// -///*************************************************************************** -/// @endcond - -#ifndef QS_PORT_HPP -#define QS_PORT_HPP - -// QS time-stamp size in bytes -#define QS_TIME_SIZE 4U - -// object pointer size in bytes -#define QS_OBJ_PTR_SIZE 4U - -// function pointer size in bytes -#define QS_FUN_PTR_SIZE 4U - -//**************************************************************************** -// NOTE: QS might be used with or without other QP components, in which case -// the separate definitions of the macros QF_CRIT_STAT_TYPE, QF_CRIT_ENTRY, -// and QF_CRIT_EXIT are needed. In this port QS is configured to be used with -// the other QP component, by simply including "qf_port.hpp" *before* "qs.hpp". -// -#include "qf_port.hpp" // use QS with QF -#include "qs.hpp" // QS platform-independent public interface - -#endif // QS_PORT_HPP diff --git a/src/qstamp.cpp b/src/qstamp.cpp deleted file mode 100644 index c7c1c23..0000000 --- a/src/qstamp.cpp +++ /dev/null @@ -1,56 +0,0 @@ -//$file${src::qs::qstamp.cpp} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv -// -// Model: qpcpp.qm -// File: ${src::qs::qstamp.cpp} -// -// This code has been generated by QM 5.2.5 . -// DO NOT EDIT THIS FILE MANUALLY. All your changes will be lost. -// -// This code is covered by the following QP license: -// License # : LicenseRef-QL-dual -// Issued to : Any user of the QP/C++ real-time embedded framework -// Framework(s) : qpcpp -// Support ends : 2023-12-31 -// License scope: -// -// Copyright (C) 2005 Quantum Leaps, LLC . -// -// 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: -// -// The terms of the closed source Quantum Leaps commercial licenses -// can be found at: -// -// Redistributions in source code must retain this top-level comment block. -// Plagiarizing this software to sidestep the license obligations is illegal. -// -// Contact information: -// -// -// -//$endhead${src::qs::qstamp.cpp} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -//! @file -//! @brief Application build time-stamp -//! @note -//! This module needs to be re-compiled in every new software build. To achive -//! this, it is recommended to delete the object file (qstamp.o or qstamp.obj) -//! in the build directory before each build. (Most development tools allow -//! you to specify a pre-build action, which is the ideal place to delete -//! the qstamp object file.) - -#include "qstamp.hpp" - -namespace QP { -//! the calendar date of the last translation of the form: "Mmm dd yyyy" -char const BUILD_DATE[12] = __DATE__; - -//! the time of the last translation of the form: "hh:mm:ss" -char const BUILD_TIME[9] = __TIME__; -} // namespace QP diff --git a/src/qstamp.hpp b/src/qstamp.hpp deleted file mode 100644 index 0611c06..0000000 --- a/src/qstamp.hpp +++ /dev/null @@ -1,50 +0,0 @@ -//$file${include::qstamp.hpp} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv -// -// Model: qpcpp.qm -// File: ${include::qstamp.hpp} -// -// This code has been generated by QM 5.2.5 . -// DO NOT EDIT THIS FILE MANUALLY. All your changes will be lost. -// -// This code is covered by the following QP license: -// License # : LicenseRef-QL-dual -// Issued to : Any user of the QP/C++ real-time embedded framework -// Framework(s) : qpcpp -// Support ends : 2023-12-31 -// License scope: -// -// Copyright (C) 2005 Quantum Leaps, LLC . -// -// 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: -// -// The terms of the closed source Quantum Leaps commercial licenses -// can be found at: -// -// Redistributions in source code must retain this top-level comment block. -// Plagiarizing this software to sidestep the license obligations is illegal. -// -// Contact information: -// -// -// -//$endhead${include::qstamp.hpp} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -//! @file -//! @brief Application build time-stamp interface - -#ifndef QSTAMP_HPP_ -#define QSTAMP_HPP_ - -namespace QP { -extern char const BUILD_DATE[12]; -extern char const BUILD_TIME[9]; -} // namespace QP - -#endif // QSTAMP_HPP_ diff --git a/src/qv/qv.cpp b/src/qv/qv.cpp new file mode 100644 index 0000000..ca4f967 --- /dev/null +++ b/src/qv/qv.cpp @@ -0,0 +1,247 @@ +//$file${src::qv::qv.cpp} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv +// +// Model: qpcpp.qm +// File: ${src::qv::qv.cpp} +// +// This code has been generated by QM 6.2.3 . +// DO NOT EDIT THIS FILE MANUALLY. All your changes will be lost. +// +// ____________________________________ +// / / +// / GGGGGGG PPPPPPPP LL / +// / GG GG PP PP LL / +// / GG PP PP LL / +// / GG GGGGG PPPPPPPP LL / +// / GG GG PP LL / +// / GGGGGGG PP LLLLLLL / +// /___________________________________/ +// +// Copyright (c) 2005 Quantum Leaps, LLC +// SPDX-License-Identifier: GPL-3.0-or-later +// +// This generated code is open-source software licensed under the GNU +// General Public License (GPL) as published by the Free Software Foundation +// (see ). +// +// NOTE: +// The GPL does NOT permit the incorporation of this code into proprietary +// programs. Please contact Quantum Leaps for commercial licensing options, +// which expressly supersede the GPL and are designed explicitly for licensees +// interested in retaining the proprietary status of the generated code. +// +// Quantum Leaps contact information: +// +// +// +//$endhead${src::qv::qv.cpp} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +#define QP_IMPL // this is QP implementation +#include "qp_port.hpp" // QP port +#include "qp_pkg.hpp" // QP package-scope interface +#include "qsafe.h" // QP Functional Safety (FuSa) Subsystem +#ifdef Q_SPY // QS software tracing enabled? + #include "qs_port.hpp" // QS port + #include "qs_pkg.hpp" // QS facilities for pre-defined trace records +#else + #include "qs_dummy.hpp" // disable the QS software tracing +#endif // Q_SPY + +// protection against including this source file in a wrong project +#ifndef QV_HPP_ + #error "Source file included in a project NOT based on the QV kernel" +#endif // QV_HPP_ + +// unnamed namespace for local definitions with internal linkage +namespace { +Q_DEFINE_THIS_MODULE("qv") +} // unnamed namespace + +//$skip${QP_VERSION} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv +// Check for the minimum required QP version +#if (QP_VERSION < 730U) || (QP_VERSION != ((QP_RELEASE^4294967295U)%0x2710U)) +#error qpcpp version 7.3.0 or higher required +#endif +//$endskip${QP_VERSION} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +//$define${QV::QV-base} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv +namespace QP { +namespace QV { + +//${QV::QV-base::priv_} ...................................................... +QV::Attr priv_; + +} // namespace QV +} // namespace QP +//$enddef${QV::QV-base} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +//$define${QV::QF-cust} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv +namespace QP { +namespace QF { + +//${QV::QF-cust::init} ....................................................... +void init() { + bzero_(&QF::priv_, sizeof(QF::priv_)); + bzero_(&QV::priv_, sizeof(QV::priv_)); + bzero_(&QTimeEvt::timeEvtHead_[0], sizeof(QTimeEvt::timeEvtHead_)); + bzero_(&QActive::registry_[0], sizeof(QActive::registry_)); + + #ifndef Q_UNSAFE + QV::priv_.readySet.update_(&QV::priv_.readySet_dis); + #endif + + #ifdef QV_INIT + QV_INIT(); // port-specific initialization of the QV kernel + #endif +} + +//${QV::QF-cust::stop} ....................................................... +void stop() { + onCleanup(); // cleanup callback + // nothing else to do for the QV kernel +} + +//${QV::QF-cust::run} ........................................................ +int_t run() { + #ifdef Q_SPY + // produce the QS_QF_RUN trace record + QF_INT_DISABLE(); + QF_MEM_SYS(); + QS::beginRec_(QS_REC_NUM_(QS_QF_RUN)); + QS::endRec_(); + QF_MEM_APP(); + QF_INT_ENABLE(); + #endif + + onStartup(); // application-specific startup callback + + QF_INT_DISABLE(); + QF_MEM_SYS(); + + #ifdef QV_START + QV_START(); // port-specific startup of the QV kernel + #endif + + #if (defined QF_ON_CONTEXT_SW) || (defined Q_SPY) + std::uint_fast8_t pprev = 0U; // previous prio. + #endif + + for (;;) { // QV event loop... + + // check internal integrity (duplicate inverse storage) + Q_ASSERT_INCRIT(202, + QV::priv_.readySet.verify_(&QV::priv_.readySet_dis)); + + // find the maximum prio. AO ready to run + if (QV::priv_.readySet.notEmpty()) { + std::uint_fast8_t const p = QV::priv_.readySet.findMax(); + 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] + : nullptr), a); + #endif // QF_ON_CONTEXT_SW + + pprev = p; // update previous prio. + #endif // (defined QF_ON_CONTEXT_SW) || (defined Q_SPY) + + QF_MEM_APP(); + QF_INT_ENABLE(); + + QEvt const * const e = a->get_(); + // NOTE QActive::get_() performs QF_MEM_APP() before return + + // dispatch event (virtual call) + a->dispatch(e, a->getPrio()); + #if (QF_MAX_EPOOL > 0U) + gc(e); + #endif + QF_INT_DISABLE(); + QF_MEM_SYS(); + + if (a->getEQueue().isEmpty()) { // empty queue? + QV::priv_.readySet.remove(p); + #ifndef Q_UNSAFE + QV::priv_.readySet.update_(&QV::priv_.readySet_dis); + #endif + } + } + else { // no AO ready to run --> idle + #if (defined QF_ON_CONTEXT_SW) || (defined Q_SPY) + if (pprev != 0U) { + QS_BEGIN_PRE_(QS_SCHED_IDLE, pprev) + QS_TIME_PRE_(); // timestamp + QS_U8_PRE_(pprev); // previous prio + QS_END_PRE_() + + #ifdef QF_ON_CONTEXT_SW + QF_onContextSw(QActive::registry_[pprev], nullptr); + #endif // QF_ON_CONTEXT_SW + + pprev = 0U; // update previous prio + } + #endif // (defined QF_ON_CONTEXT_SW) || (defined Q_SPY) + + QF_MEM_APP(); + + // QV::onIdle() must be called with interrupts DISABLED + // because the determination of the idle condition (all event + // queues empty) can change at any time by an interrupt posting + // events to a queue. + // + // NOTE: QV::onIdle() MUST enable interrupts internally, + // ideally at the same time as putting the CPU into a power- + // saving mode. + QV::onIdle(); + + QF_INT_DISABLE(); + QF_MEM_SYS(); + } + } + #ifdef __GNUC__ // GNU compiler? + return 0; + #endif +} + +} // namespace QF +} // namespace QP +//$enddef${QV::QF-cust} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +//$define${QV::QActive} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv +namespace QP { + +//${QV::QActive} ............................................................. + +//${QV::QActive::start} ...................................................... +void QActive::start( + QPrioSpec const prioSpec, + QEvt const * * const qSto, + std::uint_fast16_t const qLen, + void * const stkSto, + std::uint_fast16_t const stkSize, + void const * const par) +{ + Q_UNUSED_PAR(stkSto); // not needed in QV + Q_UNUSED_PAR(stkSize); // not needed in QV + + QF_CRIT_STAT + QF_CRIT_ENTRY(); + Q_REQUIRE_INCRIT(300, stkSto == nullptr); + QF_CRIT_EXIT(); + + m_prio = static_cast(prioSpec & 0xFFU); // QF-prio. + m_pthre = static_cast(prioSpec >> 8U); // preemption-thre. + register_(); // make QF aware of this AO + + m_eQueue.init(qSto, qLen); // initialize QEQueue of this AO + + this->init(par, m_prio); // take the top-most initial tran. (virtual) + QS_FLUSH(); // flush the trace buffer to the host +} + +} // namespace QP +//$enddef${QV::QActive} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/src/qxk/qxk.cpp b/src/qxk/qxk.cpp new file mode 100644 index 0000000..ec14326 --- /dev/null +++ b/src/qxk/qxk.cpp @@ -0,0 +1,548 @@ +//$file${src::qxk::qxk.cpp} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv +// +// Model: qpcpp.qm +// File: ${src::qxk::qxk.cpp} +// +// This code has been generated by QM 6.2.3 . +// DO NOT EDIT THIS FILE MANUALLY. All your changes will be lost. +// +// ____________________________________ +// / / +// / GGGGGGG PPPPPPPP LL / +// / GG GG PP PP LL / +// / GG PP PP LL / +// / GG GGGGG PPPPPPPP LL / +// / GG GG PP LL / +// / GGGGGGG PP LLLLLLL / +// /___________________________________/ +// +// Copyright (c) 2005 Quantum Leaps, LLC +// SPDX-License-Identifier: GPL-3.0-or-later +// +// This generated code is open-source software licensed under the GNU +// General Public License (GPL) as published by the Free Software Foundation +// (see ). +// +// NOTE: +// The GPL does NOT permit the incorporation of this code into proprietary +// programs. Please contact Quantum Leaps for commercial licensing options, +// which expressly supersede the GPL and are designed explicitly for licensees +// interested in retaining the proprietary status of the generated code. +// +// Quantum Leaps contact information: +// +// +// +//$endhead${src::qxk::qxk.cpp} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +#define QP_IMPL // this is QP implementation +#include "qp_port.hpp" // QP port +#include "qp_pkg.hpp" // QP package-scope interface +#include "qsafe.h" // QP Functional Safety (FuSa) Subsystem +#ifdef Q_SPY // QS software tracing enabled? + #include "qs_port.hpp" // QS port + #include "qs_pkg.hpp" // QS facilities for pre-defined trace records +#else + #include "qs_dummy.hpp" // disable the QS software tracing +#endif // Q_SPY + +// protection against including this source file in a wrong project +#ifndef QXK_HPP_ + #error "Source file included in a project NOT based on the QXK kernel" +#endif // QXK_HPP_ + +// unnamed namespace for local definitions with internal linkage +namespace { +Q_DEFINE_THIS_MODULE("qxk") +} // unnamed namespace + +//$skip${QP_VERSION} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv +// Check for the minimum required QP version +#if (QP_VERSION < 730U) || (QP_VERSION != ((QP_RELEASE^4294967295U)%0x2710U)) +#error qpcpp version 7.3.0 or higher required +#endif +//$endskip${QP_VERSION} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +//$define${QXK::QXK-base} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv +namespace QP { +namespace QXK { + +//${QXK::QXK-base::schedLock} ................................................ +QSchedStatus schedLock(std::uint_fast8_t const ceiling) noexcept { + QF_CRIT_STAT + QF_CRIT_ENTRY(); + QF_MEM_SYS(); + + Q_REQUIRE_INCRIT(100, !QXK_ISR_CONTEXT_()); + + QSchedStatus stat; // saved lock status to be returned + + // is the lock ceiling being raised? + if (ceiling > QXK_priv_.lockCeil) { + QS_BEGIN_PRE_(QS_SCHED_LOCK, 0U) + QS_TIME_PRE_(); // timestamp + // the previous lock ceiling & new lock ceiling + QS_2U8_PRE_(static_cast(QXK_priv_.lockCeil), + static_cast(ceiling)); + QS_END_PRE_() + + // previous status of the lock + stat = static_cast(QXK_priv_.lockHolder); + stat |= static_cast(QXK_priv_.lockCeil) << 8U; + + // new status of the lock + QXK_priv_.lockHolder = (QXK_priv_.curr != nullptr) + ? QXK_priv_.curr->getPrio() + : 0U; + QXK_priv_.lockCeil = ceiling; + } + else { + stat = 0xFFU; // scheduler not locked + } + QF_MEM_APP(); + QF_CRIT_EXIT(); + + return stat; // return the status to be saved in a stack variable +} + +//${QXK::QXK-base::schedUnlock} .............................................. +void schedUnlock(QSchedStatus const stat) noexcept { + // has the scheduler been actually locked by the last QXK::schedLock()? + if (stat != 0xFFU) { + std::uint8_t const prevCeil = static_cast(stat >> 8U); + QF_CRIT_STAT + QF_CRIT_ENTRY(); + QF_MEM_SYS(); + + Q_REQUIRE_INCRIT(200, !QXK_ISR_CONTEXT_()); + Q_REQUIRE_INCRIT(201, QXK_priv_.lockCeil > prevCeil); + + QS_BEGIN_PRE_(QS_SCHED_UNLOCK, 0U) + QS_TIME_PRE_(); // timestamp + // ceiling before unlocking & prio after unlocking + QS_2U8_PRE_(QXK_priv_.lockCeil, prevCeil); + QS_END_PRE_() + + // restore the previous lock ceiling and lock holder + QXK_priv_.lockCeil = prevCeil; + QXK_priv_.lockHolder = (stat & 0xFFU); + + // find if any threads should be run after unlocking the scheduler + if (QXK_sched_() != 0U) { // activation needed? + QXK_activate_(); // synchronously activate basic-thred(s) + } + + QF_MEM_APP(); + QF_CRIT_EXIT(); + } +} + +//${QXK::QXK-base::current} .................................................. +QP::QActive * current() noexcept { + QF_CRIT_STAT + QF_CRIT_ENTRY(); + QF_MEM_SYS(); + + Q_REQUIRE_INCRIT(600, QXK_priv_.lockCeil <= QF_MAX_ACTIVE); + + QP::QActive *curr = QXK_priv_.curr; + if (curr == nullptr) { // basic thread? + curr = QP::QActive::registry_[QXK_priv_.actPrio]; + } + + Q_ASSERT_INCRIT(690, curr != nullptr); + + QF_MEM_APP(); + QF_CRIT_EXIT(); + + return curr; +} + +} // namespace QXK +} // namespace QP +//$enddef${QXK::QXK-base} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +extern "C" { +//$define${QXK-extern-C} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv + +//${QXK-extern-C::QXK_priv_} ................................................. +QXK_Attr QXK_priv_; + +//${QXK-extern-C::QXK_sched_} ................................................ +std::uint_fast8_t QXK_sched_() noexcept { + Q_REQUIRE_INCRIT(402, + QXK_priv_.readySet.verify_(&QXK_priv_.readySet_dis)); + + std::uint_fast8_t p; + if (QXK_priv_.readySet.isEmpty()) { + p = 0U; // no activation needed + } + else { + // find the highest-prio thread ready to run + p = QXK_priv_.readySet.findMax(); + if (p <= QXK_priv_.lockCeil) { + // prio. of the thread holding the lock + p = static_cast( + QP::QActive::registry_[QXK_priv_.lockHolder]->getPrio()); + if (p != 0U) { + Q_ASSERT_INCRIT(410, QXK_priv_.readySet.hasElement(p)); + } + } + } + QP::QActive const * const curr = QXK_priv_.curr; + QP::QActive * const next = QP::QActive::registry_[p]; + + // the next thread found must be registered in QF + Q_ASSERT_INCRIT(420, next != nullptr); + + // is the current thread a basic-thread? + if (curr == nullptr) { + + // is the new prio. above the active prio.? + if (p > QXK_priv_.actPrio) { + QXK_priv_.next = next; // set the next AO to activate + + if (next->getOsObject() != nullptr) { // is next extended? + QXK_CONTEXT_SWITCH_(); + p = 0U; // no activation needed + } + } + else { // below the pre-thre + QXK_priv_.next = nullptr; + p = 0U; // no activation needed + } + } + else { // currently executing an extended-thread + // is the current thread different from the next? + if (curr != next) { + QXK_priv_.next = next; + QXK_CONTEXT_SWITCH_(); + } + else { // current is the same as next + QXK_priv_.next = nullptr; // no need to context-switch + } + p = 0U; // no activation needed + } + + return p; +} + +//${QXK-extern-C::QXK_activate_} ............................................. +void QXK_activate_() noexcept { + std::uint_fast8_t const prio_in = QXK_priv_.actPrio; + QP::QActive *next = QXK_priv_.next; // the next AO (basic-thread) to run + + Q_REQUIRE_INCRIT(500, (next != nullptr) && (prio_in <= QF_MAX_ACTIVE)); + + // QXK Context switch callback defined or QS tracing enabled? + #if (defined QF_ON_CONTEXT_SW) || (defined Q_SPY) + QXK_contextSw_(next); + #endif // QF_ON_CONTEXT_SW || Q_SPY + + QXK_priv_.next = nullptr; // clear the next AO + QXK_priv_.curr = nullptr; // current is basic-thread + + // prio. of the next thread + std::uint_fast8_t p = next->getPrio(); + + // loop until no more ready-to-run AOs of higher prio than the initial + do { + QXK_priv_.actPrio = p; // next active prio + + QF_INT_ENABLE(); // unconditionally enable interrupts + + QP::QEvt const * const e = next->get_(); + // NOTE QActive::get_() performs QS_MEM_APP() before return + + // dispatch event (virtual call) + next->dispatch(e, next->getPrio()); + #if (QF_MAX_EPOOL > 0U) + QP::QF::gc(e); + #endif + + QF_INT_DISABLE(); // unconditionally disable interrupts + QF_MEM_SYS(); + + // check internal integrity (duplicate inverse storage) + Q_ASSERT_INCRIT(502, + QXK_priv_.readySet.verify_(&QXK_priv_.readySet_dis)); + + if (next->getEQueue().isEmpty()) { // empty queue? + QXK_priv_.readySet.remove(p); + #ifndef Q_UNSAFE + QXK_priv_.readySet.update_(&QXK_priv_.readySet_dis); + #endif + } + + if (QXK_priv_.readySet.isEmpty()) { + QXK_priv_.next = nullptr; + next = QP::QActive::registry_[0]; + p = 0U; // no activation needed + } + else { + // find next highest-prio below the lock ceiling + p = QXK_priv_.readySet.findMax(); + if (p <= QXK_priv_.lockCeil) { + p = QXK_priv_.lockHolder; + if (p != 0U) { + Q_ASSERT_INCRIT(510, QXK_priv_.readySet.hasElement(p)); + } + } + + // set the next thread and ensure that it is registered + next = QP::QActive::registry_[p]; + Q_ASSERT_INCRIT(520, next != nullptr); + + // is next a basic thread? + if (next->getOsObject() == nullptr) { + // is the next prio. above the initial prio.? + if (p > QP::QActive::registry_[prio_in]->getPrio()) { + #if (defined QF_ON_CONTEXT_SW) || (defined Q_SPY) + if (p != QXK_priv_.actPrio) { // changing threads? + QXK_contextSw_(next); + } + #endif // QF_ON_CONTEXT_SW || Q_SPY + QXK_priv_.next = next; + } + else { + QXK_priv_.next = nullptr; + p = 0U; // no activation needed + } + } + else { // next is the extended-thread + QXK_priv_.next = next; + QXK_CONTEXT_SWITCH_(); + p = 0U; // no activation needed + } + } + } while (p != 0U); // while activation needed + + // restore the active prio. + QXK_priv_.actPrio = prio_in; + + #if (defined QF_ON_CONTEXT_SW) || (defined Q_SPY) + if (next->getOsObject() == nullptr) { + QXK_contextSw_((prio_in == 0U) + ? nullptr + : QP::QActive::registry_[prio_in]); + } + #endif // QF_ON_CONTEXT_SW || Q_SPY +} + +//${QXK-extern-C::QXK_contextSw_} ............................................ +void QXK_contextSw_(QP::QActive * const next) { + #ifdef Q_SPY + std::uint_fast8_t const prev_prio = (QXK_priv_.prev != nullptr) + ? QXK_priv_.prev->getPrio() + : 0U; + if (next != nullptr) { // next is NOT idle? + std::uint_fast8_t const next_prio = next->getPrio(); + QS_BEGIN_PRE_(QP::QS_SCHED_NEXT, next_prio) + QS_TIME_PRE_(); // timestamp + QS_2U8_PRE_(next_prio, prev_prio); + QS_END_PRE_() + } + else { // going to idle + QS_BEGIN_PRE_(QP::QS_SCHED_IDLE, prev_prio) + QS_TIME_PRE_(); // timestamp + QS_U8_PRE_(prev_prio); + QS_END_PRE_() + } + #endif // Q_SPY + + #ifdef QF_ON_CONTEXT_SW + QF_onContextSw(QXK_priv_.prev, next); + #endif // QF_ON_CONTEXT_SW + + QXK_priv_.prev = next; // update the previous thread +} + +//${QXK-extern-C::QXK_threadExit_} ........................................... +void QXK_threadExit_() { + QF_CRIT_STAT + QF_CRIT_ENTRY(); + + QP::QXThread const * const thr = QXTHREAD_CAST_(QXK_priv_.curr); + + Q_REQUIRE_INCRIT(900, (!QXK_ISR_CONTEXT_()) + && (thr != nullptr)); // current thread must be extended + Q_REQUIRE_INCRIT(901, QXK_priv_.lockHolder != thr->getPrio()); + + std::uint_fast8_t const p = + static_cast(thr->getPrio()); + + QF_MEM_SYS(); + QP::QActive::registry_[p] = nullptr; + QXK_priv_.readySet.remove(p); + #ifndef Q_UNSAFE + QXK_priv_.readySet.update_(&QXK_priv_.readySet_dis); + #endif + + static_cast(QXK_sched_()); // schedule other threads + + QF_MEM_APP(); + QF_CRIT_EXIT(); +} +//$enddef${QXK-extern-C} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +} // extern "C" + +//$define${QXK::QF-cust} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv +namespace QP { +namespace QF { + +//${QXK::QF-cust::init} ...................................................... +void init() { + bzero_(&QF::priv_, sizeof(QF::priv_)); + bzero_(&QXK_priv_, sizeof(QXK_priv_)); + bzero_(&QTimeEvt::timeEvtHead_[0], sizeof(QTimeEvt::timeEvtHead_)); + bzero_(&QActive::registry_[0], sizeof(QActive::registry_)); + + #ifndef Q_UNSAFE + QXK_priv_.readySet.update_(&QXK_priv_.readySet_dis); + #endif + + // setup the QXK scheduler as initially locked and not running + QXK_priv_.lockCeil = (QF_MAX_ACTIVE + 1U); // scheduler locked + + // storage capable for holding a blank QActive object (const in ROM) + static void* const + idle_ao[((sizeof(QActive) + sizeof(void*)) - 1U) / sizeof(void*)] + = { nullptr }; + + // register the idle AO object (cast 'const' away) + QActive::registry_[0] = QF_CONST_CAST_(QActive*, + reinterpret_cast(idle_ao)); + + #ifdef QXK_INIT + QXK_INIT(); // port-specific initialization of the QXK kernel + #endif +} + +//${QXK::QF-cust::stop} ...................................................... +void stop() { + onCleanup(); // cleanup callback + // nothing else to do for the QXK preemptive kernel +} + +//${QXK::QF-cust::run} ....................................................... +int_t run() { + #ifdef Q_SPY + QS_SIG_DICTIONARY(QXK::DELAY_SIG, nullptr); + QS_SIG_DICTIONARY(QXK::TIMEOUT_SIG, nullptr); + + // produce the QS_QF_RUN trace record + QF_INT_DISABLE(); + QF_MEM_SYS(); + QS::beginRec_(QS_REC_NUM_(QS_QF_RUN)); + QS::endRec_(); + QF_MEM_APP(); + QF_INT_ENABLE(); + #endif // Q_SPY + + onStartup(); // application-specific startup callback + + QF_INT_DISABLE(); + QF_MEM_SYS(); + + 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 +} + +} // namespace QF +} // namespace QP +//$enddef${QXK::QF-cust} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +//$define${QXK::QActive} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv +namespace QP { + +//${QXK::QActive} ............................................................ + +//${QXK::QActive::start} ..................................................... +void QActive::start( + QPrioSpec const prioSpec, + QEvt const * * const qSto, + std::uint_fast16_t const qLen, + void * const stkSto, + std::uint_fast16_t const stkSize, + void const * const par) +{ + QF_CRIT_STAT + QF_CRIT_ENTRY(); + Q_REQUIRE_INCRIT(300, (!QXK_ISR_CONTEXT_()) + && ((prioSpec & 0xFF00U) == 0U)); + QF_CRIT_EXIT(); + + m_prio = static_cast(prioSpec & 0xFFU); // QF-prio. + m_pthre = 0U; // preemption-threshold NOT used + register_(); // make QF aware of this QActive/QXThread + + if (stkSto == nullptr) { // starting basic thread (AO)? + m_eQueue.init(qSto, qLen); // init the built-in queue + m_osObject = nullptr; // no private stack for AO + + this->init(par, m_prio); // top-most initial tran. (virtual call) + QS_FLUSH(); // flush the trace buffer to the host + + // see if this AO needs to be scheduled if QXK is already running + QF_CRIT_ENTRY(); + QF_MEM_SYS(); + if (QXK_priv_.lockCeil <= QF_MAX_ACTIVE) { // scheduler running? + if (QXK_sched_() != 0U) { // activation needed? + QXK_activate_(); // synchronously activate basic-thred(s) + } + } + QF_MEM_APP(); + QF_CRIT_EXIT(); + } + else { // starting QXThread + + // is storage for the queue buffer provided? + if (qSto != nullptr) { + m_eQueue.init(qSto, qLen); + } + + // extended threads provide their thread function in place of + // the top-most initial tran. 'm_temp.act' + QXK_PTR_CAST_(QXThread*, this)->stackInit_(m_temp.thr, + stkSto, stkSize); + + // the new thread is not blocked on any object + m_temp.obj = nullptr; + + QF_CRIT_ENTRY(); + QF_MEM_SYS(); + + // extended-thread becomes ready immediately + QXK_priv_.readySet.insert(static_cast(m_prio)); + #ifndef Q_UNSAFE + QXK_priv_.readySet.update_(&QXK_priv_.readySet_dis); + #endif + + // see if this thread needs to be scheduled in case QXK is running + if (QXK_priv_.lockCeil <= QF_MAX_ACTIVE) { + static_cast(QXK_sched_()); // schedule other threads + } + QF_MEM_APP(); + QF_CRIT_EXIT(); + } +} + +} // namespace QP +//$enddef${QXK::QActive} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/src/qxk/qxk_mutex.cpp b/src/qxk/qxk_mutex.cpp new file mode 100644 index 0000000..9b204d0 --- /dev/null +++ b/src/qxk/qxk_mutex.cpp @@ -0,0 +1,473 @@ +//$file${src::qxk::qxk_mutex.cpp} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv +// +// Model: qpcpp.qm +// File: ${src::qxk::qxk_mutex.cpp} +// +// This code has been generated by QM 6.2.3 . +// DO NOT EDIT THIS FILE MANUALLY. All your changes will be lost. +// +// ____________________________________ +// / / +// / GGGGGGG PPPPPPPP LL / +// / GG GG PP PP LL / +// / GG PP PP LL / +// / GG GGGGG PPPPPPPP LL / +// / GG GG PP LL / +// / GGGGGGG PP LLLLLLL / +// /___________________________________/ +// +// Copyright (c) 2005 Quantum Leaps, LLC +// SPDX-License-Identifier: GPL-3.0-or-later +// +// This generated code is open-source software licensed under the GNU +// General Public License (GPL) as published by the Free Software Foundation +// (see ). +// +// NOTE: +// The GPL does NOT permit the incorporation of this code into proprietary +// programs. Please contact Quantum Leaps for commercial licensing options, +// which expressly supersede the GPL and are designed explicitly for licensees +// interested in retaining the proprietary status of the generated code. +// +// Quantum Leaps contact information: +// +// +// +//$endhead${src::qxk::qxk_mutex.cpp} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +#define QP_IMPL // this is QP implementation +#include "qp_port.hpp" // QP port +#include "qp_pkg.hpp" // QP package-scope interface +#include "qsafe.h" // QP Functional Safety (FuSa) Subsystem +#ifdef Q_SPY // QS software tracing enabled? + #include "qs_port.hpp" // QS port + #include "qs_pkg.hpp" // QS facilities for pre-defined trace records +#else + #include "qs_dummy.hpp" // disable the QS software tracing +#endif // Q_SPY + +// protection against including this source file in a wrong project +#ifndef QXK_HPP_ + #error "Source file included in a project NOT based on the QXK kernel" +#endif // QXK_HPP_ + +// unnamed namespace for local definitions with internal linkage +namespace { +Q_DEFINE_THIS_MODULE("qxk_mutex") +} // unnamed namespace + +//$skip${QP_VERSION} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv +// Check for the minimum required QP version +#if (QP_VERSION < 730U) || (QP_VERSION != ((QP_RELEASE^4294967295U)%0x2710U)) +#error qpcpp version 7.3.0 or higher required +#endif +//$endskip${QP_VERSION} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +//$define${QXK::QXMutex} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv +namespace QP { + +//${QXK::QXMutex} ............................................................ + +//${QXK::QXMutex::QXMutex} ................................................... +QXMutex::QXMutex() + : m_ao(Q_STATE_CAST(0)) +{} + +//${QXK::QXMutex::init} ...................................................... +void QXMutex::init(QPrioSpec const prioSpec) noexcept { + QF_CRIT_STAT + QF_CRIT_ENTRY(); + QF_MEM_SYS(); + + Q_REQUIRE_INCRIT(100, (prioSpec & 0xFF00U) == 0U); + + m_ao.m_prio = static_cast(prioSpec & 0xFFU); // QF-prio. + m_ao.m_pthre = 0U; // preemption-threshold (not used) + QActive &ao = m_ao; + + QF_MEM_APP(); + QF_CRIT_EXIT(); + + ao.register_(); // register this mutex as AO +} + +//${QXK::QXMutex::lock} ...................................................... +bool QXMutex::lock(QTimeEvtCtr const nTicks) noexcept { + QF_CRIT_STAT + QF_CRIT_ENTRY(); + QF_MEM_SYS(); + + QXThread * const curr = QXK_PTR_CAST_(QXThread*, QXK_priv_.curr); + + // precondition, this mutex operation must: + // - NOT be called from an ISR; + // - be called from an eXtended thread; + // - the mutex-prio. must be in range; + // - the thread must NOT be already blocked on any object. + Q_REQUIRE_INCRIT(200, (!QXK_ISR_CONTEXT_()) + && (curr != nullptr) + && (m_ao.m_prio <= QF_MAX_ACTIVE) + && (curr->m_temp.obj == nullptr)); + // also: the thread must NOT be holding a scheduler lock. + Q_REQUIRE_INCRIT(201, QXK_priv_.lockHolder != curr->m_prio); + + // is the mutex available? + bool locked = true; // assume that the mutex will be locked + if (m_ao.m_eQueue.m_nFree == 0U) { + m_ao.m_eQueue.m_nFree = 1U; // mutex lock nesting + + // also: the newly locked mutex must have no holder yet + Q_REQUIRE_INCRIT(203, m_ao.m_osObject == nullptr); + + // set the new mutex holder to the curr thread and + // save the thread's prio in the mutex + // NOTE: reuse the otherwise unused eQueue data member. + m_ao.m_osObject = curr; + m_ao.m_eQueue.m_head = static_cast(curr->m_prio); + + QS_BEGIN_PRE_(QS_MTX_LOCK, curr->m_prio) + QS_TIME_PRE_(); // timestamp + QS_OBJ_PRE_(this); // this mutex + QS_U8_PRE_(static_cast(m_ao.m_eQueue.m_head)); + QS_U8_PRE_(static_cast(m_ao.m_eQueue.m_nFree)); + QS_END_PRE_() + + if (m_ao.m_prio != 0U) { // prio.-ceiling protocol used? + // the holder prio. must be lower than that of the mutex + // and the prio. slot must be occupied by this mutex + Q_ASSERT_INCRIT(210, (curr->m_prio < m_ao.m_prio) + && (QActive::registry_[m_ao.m_prio] == &m_ao)); + + // remove the thread's original prio from the ready set + // and insert the mutex's prio into the ready set + QXK_priv_.readySet.remove( + static_cast(m_ao.m_eQueue.m_head)); + QXK_priv_.readySet.insert( + static_cast(m_ao.m_prio)); + #ifndef Q_UNSAFE + QXK_priv_.readySet.update_(&QXK_priv_.readySet_dis); + #endif + // put the thread into the AO registry in place of the mutex + QActive::registry_[m_ao.m_prio] = curr; + + // set thread's prio to that of the mutex + curr->m_prio = m_ao.m_prio; + } + } + // is the mutex locked by this thread already (nested locking)? + else if (m_ao.m_osObject == curr) { + + // the nesting level beyond the arbitrary but high limit + // most likely means cyclic or recursive locking of a mutex. + Q_ASSERT_INCRIT(220, m_ao.m_eQueue.m_nFree < 0xFFU); + + // lock one more level + m_ao.m_eQueue.m_nFree = m_ao.m_eQueue.m_nFree + 1U; + + QS_BEGIN_PRE_(QS_MTX_LOCK, curr->m_prio) + QS_TIME_PRE_(); // timestamp + QS_OBJ_PRE_(this); // this mutex + QS_U8_PRE_(static_cast(m_ao.m_eQueue.m_head)); + QS_U8_PRE_(static_cast(m_ao.m_eQueue.m_nFree)); + QS_END_PRE_() + } + else { // the mutex is already locked by a different thread + // the mutex holder must be valid + Q_ASSERT_INCRIT(230, m_ao.m_osObject != nullptr); + + if (m_ao.m_prio != 0U) { // prio.-ceiling protocol used? + // the prio slot must be occupied by the thr. holding the mutex + Q_ASSERT_INCRIT(240, QActive::registry_[m_ao.m_prio] + == QXK_PTR_CAST_(QActive const *, m_ao.m_osObject)); + } + + // remove the curr thread's prio from the ready set (will block) + // and insert it to the waiting set on this mutex + std::uint_fast8_t const p = + static_cast(curr->m_prio); + QXK_priv_.readySet.remove(p); + #ifndef Q_UNSAFE + QXK_priv_.readySet.update_(&QXK_priv_.readySet_dis); + #endif + m_waitSet.insert(p); + + // set the blocking object (this mutex) + curr->m_temp.obj = QXK_PTR_CAST_(QMState*, this); + curr->teArm_(static_cast(QXK::TIMEOUT_SIG), nTicks); + + QS_BEGIN_PRE_(QS_MTX_BLOCK, curr->m_prio) + QS_TIME_PRE_(); // timestamp + QS_OBJ_PRE_(this); // this mutex + QS_2U8_PRE_(static_cast(m_ao.m_eQueue.m_head), + curr->m_prio); + QS_END_PRE_() + + // schedule the next thread if multitasking started + static_cast(QXK_sched_()); // schedule other threads + + QF_MEM_APP(); + QF_CRIT_EXIT(); + QF_CRIT_EXIT_NOP(); // BLOCK here !!! + + // AFTER unblocking... + QF_CRIT_ENTRY(); + QF_MEM_SYS(); + // the blocking object must be this mutex + Q_ASSERT_INCRIT(250, curr->m_temp.obj + == QXK_PTR_CAST_(QMState*, this)); + + // did the blocking time-out? (signal of zero means that it did) + if (curr->m_timeEvt.sig == 0U) { + if (m_waitSet.hasElement(p)) { // still waiting? + m_waitSet.remove(p); // remove unblocked thread + locked = false; // the mutex was NOT locked + } + } + else { // blocking did NOT time out + // the thread must NOT be waiting on this mutex + Q_ASSERT_INCRIT(260, !m_waitSet.hasElement(p)); + } + curr->m_temp.obj = nullptr; // clear blocking obj. + } + QF_MEM_APP(); + QF_CRIT_EXIT(); + + return locked; +} + +//${QXK::QXMutex::tryLock} ................................................... +bool QXMutex::tryLock() noexcept { + QF_CRIT_STAT + QF_CRIT_ENTRY(); + QF_MEM_SYS(); + + QActive *curr = QXK_priv_.curr; + if (curr == nullptr) { // called from a basic thread? + curr = QActive::registry_[QXK_priv_.actPrio]; + } + + // precondition, this mutex must: + // - NOT be called from an ISR; + // - the calling thread must be valid; + // - the mutex-prio. must be in range + Q_REQUIRE_INCRIT(300, (!QXK_ISR_CONTEXT_()) + && (curr != nullptr) + && (m_ao.m_prio <= QF_MAX_ACTIVE)); + // also: the thread must NOT be holding a scheduler lock. + Q_REQUIRE_INCRIT(301, QXK_priv_.lockHolder != curr->m_prio); + + // is the mutex available? + if (m_ao.m_eQueue.m_nFree == 0U) { + m_ao.m_eQueue.m_nFree = 1U; // mutex lock nesting + + // also the newly locked mutex must have no holder yet + Q_REQUIRE_INCRIT(303, m_ao.m_osObject == nullptr); + + // set the new mutex holder to the curr thread and + // save the thread's prio in the mutex + // NOTE: reuse the otherwise unused eQueue data member. + m_ao.m_osObject = curr; + m_ao.m_eQueue.m_head = static_cast(curr->m_prio); + + QS_BEGIN_PRE_(QS_MTX_LOCK, curr->m_prio) + QS_TIME_PRE_(); // timestamp + QS_OBJ_PRE_(this); // this mutex + QS_U8_PRE_(static_cast(m_ao.m_eQueue.m_head)); + QS_U8_PRE_(static_cast(m_ao.m_eQueue.m_nFree)); + QS_END_PRE_() + + if (m_ao.m_prio != 0U) { // prio.-ceiling protocol used? + // the holder prio. must be lower than that of the mutex + // and the prio. slot must be occupied by this mutex + Q_ASSERT_INCRIT(310, (curr->m_prio < m_ao.m_prio) + && (QActive::registry_[m_ao.m_prio] == &m_ao)); + + // remove the thread's original prio from the ready set + // and insert the mutex's prio into the ready set + QXK_priv_.readySet.remove( + static_cast(m_ao.m_eQueue.m_head)); + QXK_priv_.readySet.insert( + static_cast(m_ao.m_prio)); + #ifndef Q_UNSAFE + QXK_priv_.readySet.update_(&QXK_priv_.readySet_dis); + #endif + // put the thread into the AO registry in place of the mutex + QActive::registry_[m_ao.m_prio] = curr; + + // set thread's prio to that of the mutex + curr->m_prio = m_ao.m_prio; + } + } + // is the mutex locked by this thread already (nested locking)? + else if (m_ao.m_osObject == curr) { + // the nesting level must not exceed the specified limit + Q_ASSERT_INCRIT(320, m_ao.m_eQueue.m_nFree < 0xFFU); + + // lock one more level + m_ao.m_eQueue.m_nFree = m_ao.m_eQueue.m_nFree + 1U; + + QS_BEGIN_PRE_(QS_MTX_LOCK, curr->m_prio) + QS_TIME_PRE_(); // timestamp + QS_OBJ_PRE_(this); // this mutex + QS_U8_PRE_(static_cast(m_ao.m_eQueue.m_head)); + QS_U8_PRE_(static_cast(m_ao.m_eQueue.m_nFree)); + QS_END_PRE_() + } + else { // the mutex is already locked by a different thread + if (m_ao.m_prio != 0U) { // prio.-ceiling protocol used? + // the prio slot must be occupied by the thr. holding the mutex + Q_ASSERT_INCRIT(330, QActive::registry_[m_ao.m_prio] + == QXK_PTR_CAST_(QActive const *, m_ao.m_osObject)); + } + + QS_BEGIN_PRE_(QS_MTX_BLOCK_ATTEMPT, curr->m_prio) + QS_TIME_PRE_(); // timestamp + QS_OBJ_PRE_(this); // this mutex + QS_2U8_PRE_(static_cast(m_ao.m_eQueue.m_head), + curr->m_prio); // trying thread prio + QS_END_PRE_() + + curr = nullptr; // means that mutex is NOT available + } + QF_MEM_APP(); + QF_CRIT_EXIT(); + + return curr != nullptr; +} + +//${QXK::QXMutex::unlock} .................................................... +void QXMutex::unlock() noexcept { + QF_CRIT_STAT + QF_CRIT_ENTRY(); + QF_MEM_SYS(); + + QActive *curr = QXK_priv_.curr; + if (curr == nullptr) { // called from a basic thread? + curr = QActive::registry_[QXK_priv_.actPrio]; + } + + Q_REQUIRE_INCRIT(400, (!QXK_ISR_CONTEXT_()) + && (curr != nullptr)); + Q_REQUIRE_INCRIT(401, m_ao.m_eQueue.m_nFree > 0U); + Q_REQUIRE_INCRIT(403, m_ao.m_osObject == curr); + + // is this the last nesting level? + if (m_ao.m_eQueue.m_nFree == 1U) { + + if (m_ao.m_prio != 0U) { // prio.-ceiling protocol used? + + Q_ASSERT_INCRIT(410, m_ao.m_prio < QF_MAX_ACTIVE); + + // restore the holding thread's prio from the mutex + curr->m_prio = + static_cast(m_ao.m_eQueue.m_head); + + // put the mutex back into the AO registry + QActive::registry_[m_ao.m_prio] = &m_ao; + + // remove the mutex' prio from the ready set + // and insert the original thread's prio. + QXK_priv_.readySet.remove( + static_cast(m_ao.m_prio)); + QXK_priv_.readySet.insert( + static_cast(m_ao.m_eQueue.m_head)); + #ifndef Q_UNSAFE + QXK_priv_.readySet.update_(&QXK_priv_.readySet_dis); + #endif + } + + QS_BEGIN_PRE_(QS_MTX_UNLOCK, curr->m_prio) + QS_TIME_PRE_(); // timestamp + QS_OBJ_PRE_(this); // this mutex + QS_2U8_PRE_(static_cast(m_ao.m_eQueue.m_head), + 0U); + QS_END_PRE_() + + // are any other threads waiting on this mutex? + if (m_waitSet.notEmpty()) { + // find the highest-prio. thread waiting on this mutex + std::uint_fast8_t const p = m_waitSet.findMax(); + + // remove this thread from waiting on the mutex + // and insert it into the ready set. + m_waitSet.remove(p); + QXK_priv_.readySet.insert(p); + #ifndef Q_UNSAFE + QXK_priv_.readySet.update_(&QXK_priv_.readySet_dis); + #endif + + QXThread * const thr = + QXK_PTR_CAST_(QXThread*, QActive::registry_[p]); + + // the waiting thread must: + // - be registered in QF + // - have the prio. corresponding to the registration + // - be an extended thread + // - be blocked on this mutex + Q_ASSERT_INCRIT(420, (thr != nullptr) + && (thr->m_prio == static_cast(p)) + && (thr->m_state.act == Q_ACTION_CAST(0)) + && (thr->m_temp.obj == QXK_PTR_CAST_(QMState*, this))); + + // disarm the internal time event + static_cast(thr->teDisarm_()); + + // set the new mutex holder to the curr thread and + // save the thread's prio in the mutex + // NOTE: reuse the otherwise unused eQueue data member. + m_ao.m_osObject = thr; + m_ao.m_eQueue.m_head = static_cast(thr->m_prio); + + QS_BEGIN_PRE_(QS_MTX_LOCK, thr->m_prio) + QS_TIME_PRE_(); // timestamp + QS_OBJ_PRE_(this); // this mutex + QS_U8_PRE_(static_cast(m_ao.m_eQueue.m_head)); + QS_U8_PRE_(static_cast(m_ao.m_eQueue.m_nFree)); + QS_END_PRE_() + + if (m_ao.m_prio != 0U) { // prio.-ceiling protocol used? + // the holder prio. must be lower than that of the mutex + Q_ASSERT_INCRIT(430, (m_ao.m_prio < QF_MAX_ACTIVE) + && (thr->m_prio < m_ao.m_prio)); + + // put the thread into AO registry in place of the mutex + QActive::registry_[m_ao.m_prio] = thr; + } + } + else { // no threads are waiting for this mutex + m_ao.m_eQueue.m_nFree = 0U; // free up the nesting count + + // the mutex no longer held by any thread + m_ao.m_osObject = nullptr; + m_ao.m_eQueue.m_head = 0U; + m_ao.m_eQueue.m_tail = 0U; + + if (m_ao.m_prio != 0U) { // prio.-ceiling protocol used? + // the AO priority must be in range + Q_ASSERT_INCRIT(440, m_ao.m_prio < QF_MAX_ACTIVE); + + // put the mutex back at the original mutex slot + QActive::registry_[m_ao.m_prio] = &m_ao; + } + } + + // schedule the next thread if multitasking started + if (QXK_sched_() != 0U) { // activation needed? + QXK_activate_(); // synchronously activate basic-thred(s) + } + } + else { // releasing one level of nested mutex lock + // unlock one level + m_ao.m_eQueue.m_nFree = m_ao.m_eQueue.m_nFree - 1U; + + QS_BEGIN_PRE_(QS_MTX_UNLOCK_ATTEMPT, curr->m_prio) + QS_TIME_PRE_(); // timestamp + QS_OBJ_PRE_(this); // this mutex + QS_U8_PRE_(static_cast(m_ao.m_eQueue.m_head)); + QS_U8_PRE_(static_cast(m_ao.m_eQueue.m_nFree)); + QS_END_PRE_() + } + QF_MEM_APP(); + QF_CRIT_EXIT(); +} + +} // namespace QP +//$enddef${QXK::QXMutex} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/src/qxk/qxk_sema.cpp b/src/qxk/qxk_sema.cpp new file mode 100644 index 0000000..88533df --- /dev/null +++ b/src/qxk/qxk_sema.cpp @@ -0,0 +1,281 @@ +//$file${src::qxk::qxk_sema.cpp} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv +// +// Model: qpcpp.qm +// File: ${src::qxk::qxk_sema.cpp} +// +// This code has been generated by QM 6.2.3 . +// DO NOT EDIT THIS FILE MANUALLY. All your changes will be lost. +// +// ____________________________________ +// / / +// / GGGGGGG PPPPPPPP LL / +// / GG GG PP PP LL / +// / GG PP PP LL / +// / GG GGGGG PPPPPPPP LL / +// / GG GG PP LL / +// / GGGGGGG PP LLLLLLL / +// /___________________________________/ +// +// Copyright (c) 2005 Quantum Leaps, LLC +// SPDX-License-Identifier: GPL-3.0-or-later +// +// This generated code is open-source software licensed under the GNU +// General Public License (GPL) as published by the Free Software Foundation +// (see ). +// +// NOTE: +// The GPL does NOT permit the incorporation of this code into proprietary +// programs. Please contact Quantum Leaps for commercial licensing options, +// which expressly supersede the GPL and are designed explicitly for licensees +// interested in retaining the proprietary status of the generated code. +// +// Quantum Leaps contact information: +// +// +// +//$endhead${src::qxk::qxk_sema.cpp} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +#define QP_IMPL // this is QP implementation +#include "qp_port.hpp" // QP port +#include "qp_pkg.hpp" // QP package-scope interface +#include "qsafe.h" // QP Functional Safety (FuSa) Subsystem +#ifdef Q_SPY // QS software tracing enabled? + #include "qs_port.hpp" // QS port + #include "qs_pkg.hpp" // QS facilities for pre-defined trace records +#else + #include "qs_dummy.hpp" // disable the QS software tracing +#endif // Q_SPY + +// protection against including this source file in a wrong project +#ifndef QXK_HPP_ + #error "Source file included in a project NOT based on the QXK kernel" +#endif // QXK_HPP_ + +// unnamed namespace for local definitions with internal linkage +namespace { +Q_DEFINE_THIS_MODULE("qxk_sema") +} // unnamed namespace + +//$skip${QP_VERSION} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv +// Check for the minimum required QP version +#if (QP_VERSION < 730U) || (QP_VERSION != ((QP_RELEASE^4294967295U)%0x2710U)) +#error qpcpp version 7.3.0 or higher required +#endif +//$endskip${QP_VERSION} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +//$define${QXK::QXSemaphore} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv +namespace QP { + +//${QXK::QXSemaphore} ........................................................ + +//${QXK::QXSemaphore::init} .................................................. +void QXSemaphore::init( + std::uint_fast8_t const count, + std::uint_fast8_t const max_count) noexcept +{ + QF_CRIT_STAT + QF_CRIT_ENTRY(); + QF_MEM_SYS(); + + Q_REQUIRE_INCRIT(100, (count <= max_count) + && (0U < max_count) && (max_count <= 0xFFU)); + + m_count = static_cast(count); + m_max_count = static_cast(max_count); + m_waitSet.setEmpty(); + + QF_MEM_APP(); + QF_CRIT_EXIT(); +} + +//${QXK::QXSemaphore::wait} .................................................. +bool QXSemaphore::wait(QTimeEvtCtr const nTicks) noexcept { + QF_CRIT_STAT + QF_CRIT_ENTRY(); + QF_MEM_SYS(); + + QXThread * const curr = QXK_PTR_CAST_(QXThread*, QXK_priv_.curr); + + // precondition, this function: + // - must NOT be called from an ISR; + // - the semaphore must be initialized + // - be called from an extended thread; + // - the thread must NOT be already blocked on any object. + Q_REQUIRE_INCRIT(200, (!QXK_ISR_CONTEXT_()) + && (m_max_count > 0U) + && (curr != nullptr) + && (curr->m_temp.obj == nullptr)); + // - the thread must NOT be holding a scheduler lock. + Q_REQUIRE_INCRIT(201, QXK_priv_.lockHolder != curr->m_prio); + + bool taken = true; // assume that the semaphore will be signaled + if (m_count > 0U) { + m_count = m_count - 1U; // semaphore taken: decrement the count + + QS_BEGIN_PRE_(QS_SEM_TAKE, curr->m_prio) + QS_TIME_PRE_(); // timestamp + QS_OBJ_PRE_(this); // this semaphore + QS_2U8_PRE_(curr->m_prio, m_count); + QS_END_PRE_() + } + else { // semaphore not available -- BLOCK the thread + std::uint_fast8_t const p = + static_cast(curr->m_prio); + // remove the curr prio from the ready set (will block) + // and insert to the waiting set on this semaphore + QXK_priv_.readySet.remove(p); + #ifndef Q_UNSAFE + QXK_priv_.readySet.update_(&QXK_priv_.readySet_dis); + #endif + m_waitSet.insert(p); + + // remember the blocking object (this semaphore) + curr->m_temp.obj = QXK_PTR_CAST_(QMState*, this); + curr->teArm_(static_cast(QXK::TIMEOUT_SIG), nTicks); + + QS_BEGIN_PRE_(QS_SEM_BLOCK, curr->m_prio) + QS_TIME_PRE_(); // timestamp + QS_OBJ_PRE_(this); // this semaphore + QS_2U8_PRE_(curr->m_prio, m_count); + QS_END_PRE_() + + // schedule the next thread if multitasking started + static_cast(QXK_sched_()); // schedule other threads + + QF_MEM_APP(); + QF_CRIT_EXIT(); + QF_CRIT_EXIT_NOP(); // BLOCK here !!! + + QF_CRIT_ENTRY(); // AFTER unblocking... + QF_MEM_SYS(); + + // the blocking object must be this semaphore + Q_ASSERT_INCRIT(240, curr->m_temp.obj + == QXK_PTR_CAST_(QMState*, this)); + + // did the blocking time-out? (signal of zero means that it did) + if (curr->m_timeEvt.sig == 0U) { + if (m_waitSet.hasElement(p)) { // still waiting? + m_waitSet.remove(p); // remove unblocked thread + taken = false; // the semaphore was NOT taken + } + } + else { // blocking did NOT time out + // the thread must NOT be waiting on this semaphore + Q_ASSERT_INCRIT(250, !m_waitSet.hasElement(p)); + } + curr->m_temp.obj = nullptr; // clear blocking obj. + } + QF_MEM_APP(); + QF_CRIT_EXIT(); + + return taken; +} + +//${QXK::QXSemaphore::tryWait} ............................................... +bool QXSemaphore::tryWait() noexcept { + QF_CRIT_STAT + QF_CRIT_ENTRY(); + QF_MEM_SYS(); + + // precondition: + // - the semaphore must be initialized + Q_REQUIRE_INCRIT(300, m_max_count > 0U); + + #ifdef Q_SPY + QActive const * const curr = QXK_PTR_CAST_(QActive*, QXK_priv_.curr); + #endif // Q_SPY + + bool taken; + // is the semaphore available? + if (m_count > 0U) { + m_count = m_count - 1U; // semaphore signaled: decrement + taken = true; + + QS_BEGIN_PRE_(QS_SEM_TAKE, curr->m_prio) + QS_TIME_PRE_(); // timestamp + QS_OBJ_PRE_(this); // this semaphore + QS_2U8_PRE_(curr->m_prio, m_count); + QS_END_PRE_() + } + else { // the semaphore is NOT available (would block) + taken = false; + + QS_BEGIN_PRE_(QS_SEM_BLOCK_ATTEMPT, curr->m_prio) + QS_TIME_PRE_(); // timestamp + QS_OBJ_PRE_(this); // this semaphore + QS_2U8_PRE_(curr->m_prio, m_count); + QS_END_PRE_() + } + QF_MEM_APP(); + QF_CRIT_EXIT(); + + return taken; +} + +//${QXK::QXSemaphore::signal} ................................................ +bool QXSemaphore::signal() noexcept { + bool signaled = true; // assume that the semaphore will be signaled + + QF_CRIT_STAT + QF_CRIT_ENTRY(); + QF_MEM_SYS(); + + // precondition: + // - the semaphore must be initialized + Q_REQUIRE_INCRIT(400, m_max_count > 0U); + + // any threads blocked on this semaphore? + if (m_waitSet.notEmpty()) { + // find the highest-prio. thread waiting on this semaphore + std::uint_fast8_t const p = m_waitSet.findMax(); + QXThread * const thr = + QXK_PTR_CAST_(QXThread*, QActive::registry_[p]); + + // assert that the tread: + // - must be registered in QF; + // - must be extended; and + // - must be blocked on this semaphore; + Q_ASSERT_INCRIT(410, (thr != nullptr) + && (thr->m_osObject != nullptr) + && (thr->m_temp.obj + == QXK_PTR_CAST_(QMState*, this))); + + // disarm the internal time event + static_cast(thr->teDisarm_()); + + // make the thread ready to run and remove from the wait-list + QXK_priv_.readySet.insert(p); + #ifndef Q_UNSAFE + QXK_priv_.readySet.update_(&QXK_priv_.readySet_dis); + #endif + m_waitSet.remove(p); + + QS_BEGIN_PRE_(QS_SEM_TAKE, thr->m_prio) + QS_TIME_PRE_(); // timestamp + QS_OBJ_PRE_(this); // this semaphore + QS_2U8_PRE_(thr->m_prio, m_count); + QS_END_PRE_() + + if (!QXK_ISR_CONTEXT_()) { // not inside ISR? + static_cast(QXK_sched_()); // schedule other threads + } + } + else if (m_count < m_max_count) { + m_count = m_count + 1U; // semaphore signaled: increment + + QS_BEGIN_PRE_(QS_SEM_SIGNAL, 0U) + QS_TIME_PRE_(); // timestamp + QS_OBJ_PRE_(this); // this semaphore + QS_2U8_PRE_(0U, m_count); + QS_END_PRE_() + } + else { + signaled = false; // semaphore NOT signaled + } + QF_MEM_APP(); + QF_CRIT_EXIT(); + + return signaled; +} + +} // namespace QP +//$enddef${QXK::QXSemaphore} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/src/qxk/qxk_xthr.cpp b/src/qxk/qxk_xthr.cpp new file mode 100644 index 0000000..1839fbc --- /dev/null +++ b/src/qxk/qxk_xthr.cpp @@ -0,0 +1,368 @@ +//$file${src::qxk::qxk_xthr.cpp} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv +// +// Model: qpcpp.qm +// File: ${src::qxk::qxk_xthr.cpp} +// +// This code has been generated by QM 6.2.3 . +// DO NOT EDIT THIS FILE MANUALLY. All your changes will be lost. +// +// ____________________________________ +// / / +// / GGGGGGG PPPPPPPP LL / +// / GG GG PP PP LL / +// / GG PP PP LL / +// / GG GGGGG PPPPPPPP LL / +// / GG GG PP LL / +// / GGGGGGG PP LLLLLLL / +// /___________________________________/ +// +// Copyright (c) 2005 Quantum Leaps, LLC +// SPDX-License-Identifier: GPL-3.0-or-later +// +// This generated code is open-source software licensed under the GNU +// General Public License (GPL) as published by the Free Software Foundation +// (see ). +// +// NOTE: +// The GPL does NOT permit the incorporation of this code into proprietary +// programs. Please contact Quantum Leaps for commercial licensing options, +// which expressly supersede the GPL and are designed explicitly for licensees +// interested in retaining the proprietary status of the generated code. +// +// Quantum Leaps contact information: +// +// +// +//$endhead${src::qxk::qxk_xthr.cpp} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +#define QP_IMPL // this is QP implementation +#include "qp_port.hpp" // QP port +#include "qp_pkg.hpp" // QP package-scope interface +#include "qsafe.h" // QP Functional Safety (FuSa) Subsystem +#ifdef Q_SPY // QS software tracing enabled? + #include "qs_port.hpp" // QS port + #include "qs_pkg.hpp" // QS facilities for pre-defined trace records +#else + #include "qs_dummy.hpp" // disable the QS software tracing +#endif // Q_SPY + +// protection against including this source file in a wrong project +#ifndef QXK_HPP_ + #error "Source file included in a project NOT based on the QXK kernel" +#endif // QXK_HPP_ + +// unnamed namespace for local definitions with internal linkage +namespace { +Q_DEFINE_THIS_MODULE("qxk_xthr") +} // unnamed namespace + +//$skip${QP_VERSION} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv +// Check for the minimum required QP version +#if (QP_VERSION < 730U) || (QP_VERSION != ((QP_RELEASE^4294967295U)%0x2710U)) +#error qpcpp version 7.3.0 or higher required +#endif +//$endskip${QP_VERSION} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +//$define${QXK::QXThread} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv +namespace QP { + +//${QXK::QXThread} ........................................................... + +//${QXK::QXThread::QXThread} ................................................. +QXThread::QXThread( + QXThreadHandler const handler, + std::uint_fast8_t const tickRate) noexcept + : QActive(Q_STATE_CAST(handler)), + m_timeEvt(this, static_cast(QXK::DELAY_SIG), tickRate) +{ + m_state.act = nullptr; // mark as extended thread +} + +//${QXK::QXThread::init} ..................................................... +void QXThread::init( + void const * const e, + std::uint_fast8_t const qs_id) +{ + Q_UNUSED_PAR(e); + Q_UNUSED_PAR(qs_id); + Q_ERROR_INCRIT(110); +} + +//${QXK::QXThread::dispatch} ................................................. +void QXThread::dispatch( + QEvt const * const e, + std::uint_fast8_t const qs_id) +{ + Q_UNUSED_PAR(e); + Q_UNUSED_PAR(qs_id); + Q_ERROR_INCRIT(120); +} + +//${QXK::QXThread::delay} .................................................... +bool QXThread::delay(QTimeEvtCtr const nTicks) noexcept { + QF_CRIT_STAT + QF_CRIT_ENTRY(); + QF_MEM_SYS(); + + QXThread * const thr = QXK_PTR_CAST_(QXThread*, QXK_priv_.curr); + + // precondition, this function: + // - must NOT be called from an ISR; + // - number of ticks cannot be zero + // - be called from an extended thread; + // - the thread must NOT be already blocked on any object. + Q_REQUIRE_INCRIT(800, (!QXK_ISR_CONTEXT_()) + && (nTicks != 0U) + && (thr != nullptr) + && (thr->m_temp.obj == nullptr)); + // - the thread must NOT be holding a scheduler lock. + Q_REQUIRE_INCRIT(801, QXK_priv_.lockHolder != thr->m_prio); + + // remember the blocking object + thr->m_temp.obj = QXK_PTR_CAST_(QMState const*, &thr->m_timeEvt); + thr->teArm_(static_cast(QXK::DELAY_SIG), nTicks); + thr->block_(); + + QF_MEM_APP(); + QF_CRIT_EXIT(); + QF_CRIT_EXIT_NOP(); // BLOCK here + + // after unblocking... + QF_CRIT_ENTRY(); + QF_MEM_SYS(); + + // the blocking object must be the time event + Q_ASSERT_INCRIT(890, thr->m_temp.obj + == QXK_PTR_CAST_(QMState*, &thr->m_timeEvt)); + thr->m_temp.obj = nullptr; // clear + + QF_MEM_APP(); + QF_CRIT_EXIT(); + + // signal of zero means that the time event was posted without + // being canceled. + return (thr->m_timeEvt.sig == 0U); +} + +//${QXK::QXThread::delayCancel} .............................................. +bool QXThread::delayCancel() noexcept { + QF_CRIT_STAT + QF_CRIT_ENTRY(); + QF_MEM_SYS(); + + bool wasArmed; + if (m_temp.obj == QXK_PTR_CAST_(QMState*, &m_timeEvt)) { + wasArmed = teDisarm_(); + unblock_(); + } + else { + wasArmed = false; + } + QF_MEM_APP(); + QF_CRIT_EXIT(); + + return wasArmed; +} + +//${QXK::QXThread::queueGet} ................................................. +QEvt const * QXThread::queueGet(QTimeEvtCtr const nTicks) noexcept { + QF_CRIT_STAT + QF_CRIT_ENTRY(); + QF_MEM_SYS(); + + QXThread * const thr = QXTHREAD_CAST_(QXK_priv_.curr); + + // precondition, this function: + // - must NOT be called from an ISR; + // - be called from an extended thread; + // - the thread must NOT be already blocked on any object. + Q_REQUIRE_INCRIT(500, (!QXK_ISR_CONTEXT_()) + && (thr != nullptr) + && (thr->m_temp.obj == nullptr)); + // - the thread must NOT be holding a scheduler lock. + Q_REQUIRE_INCRIT(501, QXK_priv_.lockHolder != thr->m_prio); + + // is the queue empty? + if (thr->m_eQueue.m_frontEvt == nullptr) { + + // remember the blocking object (the thread's queue) + thr->m_temp.obj = QXK_PTR_CAST_(QMState*, &thr->m_eQueue); + + thr->teArm_(static_cast(QXK::TIMEOUT_SIG), nTicks); + QXK_priv_.readySet.remove( + static_cast(thr->m_prio)); + #ifndef Q_UNSAFE + QXK_priv_.readySet.update_(&QXK_priv_.readySet_dis); + #endif + + static_cast(QXK_sched_()); // synchronous scheduling + + QF_MEM_APP(); + QF_CRIT_EXIT(); + QF_CRIT_EXIT_NOP(); // BLOCK here + + // after unblocking... + QF_CRIT_ENTRY(); + QF_MEM_SYS(); + + // the blocking object must be this queue + Q_ASSERT_INCRIT(510, thr->m_temp.obj == + QXK_PTR_CAST_(QMState *, &thr->m_eQueue)); + thr->m_temp.obj = nullptr; // clear + } + + // is the queue not empty? + QEvt const *e; + if (thr->m_eQueue.m_frontEvt != nullptr) { + e = thr->m_eQueue.m_frontEvt; // remove from the front + QEQueueCtr const nFree = thr->m_eQueue.m_nFree + 1U; + thr->m_eQueue.m_nFree = nFree; // update the # free + + // any events in the ring buffer? + if (nFree <= thr->m_eQueue.m_end) { + + // remove event from the tail + thr->m_eQueue.m_frontEvt = + thr->m_eQueue.m_ring[thr->m_eQueue.m_tail]; + if (thr->m_eQueue.m_tail == 0U) { + thr->m_eQueue.m_tail = thr->m_eQueue.m_end; // wrap + } + // advance the tail (counter clockwise) + thr->m_eQueue.m_tail = (thr->m_eQueue.m_tail - 1U); + + QS_BEGIN_PRE_(QS_QF_ACTIVE_GET, thr->m_prio) + QS_TIME_PRE_(); // timestamp + QS_SIG_PRE_(e->sig); // the signal of this event + QS_OBJ_PRE_(&thr); // this active object + QS_2U8_PRE_(e->getPoolId_(), e->refCtr_); // poolId & refCtr + QS_EQC_PRE_(nFree); // # free entries + QS_END_PRE_() + } + else { + thr->m_eQueue.m_frontEvt = nullptr; // empty queue + + // all entries in the queue must be free (+1 for fronEvt) + Q_ASSERT_INCRIT(520, nFree == (thr->m_eQueue.m_end + 1U)); + + QS_BEGIN_PRE_(QS_QF_ACTIVE_GET_LAST, thr->m_prio) + QS_TIME_PRE_(); // timestamp + QS_SIG_PRE_(e->sig); // the signal of this event + QS_OBJ_PRE_(&thr); // this active object + QS_2U8_PRE_(e->getPoolId_(), e->refCtr_); // poolId & refCtr + QS_END_PRE_() + } + } + else { // the queue is still empty -- the timeout must have fired + e = nullptr; + } + QF_MEM_APP(); + QF_CRIT_EXIT(); + + return e; +} + +//${QXK::QXThread::block_} ................................................... +void QXThread::block_() const noexcept { + // NOTE: must be called IN a critical section + + Q_REQUIRE_INCRIT(600, (QXK_priv_.lockHolder != m_prio)); + + QXK_priv_.readySet.remove(static_cast(m_prio)); + #ifndef Q_UNSAFE + QXK_priv_.readySet.update_(&QXK_priv_.readySet_dis); + #endif + + static_cast(QXK_sched_()); // schedule other threads +} + +//${QXK::QXThread::unblock_} ................................................. +void QXThread::unblock_() const noexcept { + // NOTE: must be called IN a critical section + + QXK_priv_.readySet.insert(static_cast(m_prio)); + #ifndef Q_UNSAFE + QXK_priv_.readySet.update_(&QXK_priv_.readySet_dis); + #endif + + if ((!QXK_ISR_CONTEXT_()) // not inside ISR? + && (QActive::registry_[0] != nullptr)) // kernel started? + { + static_cast(QXK_sched_()); // schedule other threads + } +} + +//${QXK::QXThread::timeout_} ................................................. +void QXThread::timeout_(QActive * const act) { + // NOTE: must be called IN a critical section + + // the private time event is now disarmed and not in any queue, + // so it is safe to change its signal. The signal of 0 means + // that the time event has expired. + QXTHREAD_CAST_(act)->m_timeEvt.sig = 0U; + + QXTHREAD_CAST_(act)->unblock_(); +} + +//${QXK::QXThread::teArm_} ................................................... +void QXThread::teArm_( + enum_t const sig, + QTimeEvtCtr const nTicks) noexcept +{ + // NOTE: must be called IN a critical section + + // precondition: + // - the time event must be unused + Q_REQUIRE_INCRIT(700, m_timeEvt.m_ctr == 0U); + + m_timeEvt.sig = static_cast(sig); + + if (nTicks != QXTHREAD_NO_TIMEOUT) { + m_timeEvt.m_ctr = static_cast(nTicks); + m_timeEvt.m_interval = 0U; + + // is the time event unlinked? + // NOTE: For the duration of a single clock tick of the specified tick + // rate a time event can be disarmed and yet still linked in the list, + // because un-linking is performed exclusively in QTimeEvt::tickX(). + if (static_cast(m_timeEvt.refCtr_ & TE_IS_LINKED) == 0U) + { + std::uint_fast8_t const tickRate = + static_cast(m_timeEvt.refCtr_); + Q_ASSERT_INCRIT(710, tickRate < QF_MAX_TICK_RATE); + + // mark as linked + m_timeEvt.refCtr_ = static_cast( + m_timeEvt.refCtr_ | TE_IS_LINKED); + + // The time event is initially inserted into the separate + // "freshly armed" list based on timeEvtHead_[tickRate].act. + // Only later, inside QTimeEvt::tick(), the "freshly armed" + // list is appended to the main list of armed time events based on + // timeEvtHead_[tickRate].next. Again, this is to keep any + // changes to the main list exclusively inside QTimeEvt::tick(). + m_timeEvt.m_next + = QXK_PTR_CAST_(QTimeEvt*, + QTimeEvt::timeEvtHead_[tickRate].m_act); + QTimeEvt::timeEvtHead_[tickRate].m_act = &m_timeEvt; + } + } +} + +//${QXK::QXThread::teDisarm_} ................................................ +bool QXThread::teDisarm_() noexcept { + // NOTE: must be called IN a critical section + + bool wasArmed; + // is the time evt running? + if (m_timeEvt.m_ctr != 0U) { + wasArmed = true; + // schedule removal from list + m_timeEvt.m_ctr = 0U; + } + // the time event was already automatically disarmed + else { + wasArmed = false; + } + return wasArmed; +} + +} // namespace QP +//$enddef${QXK::QXThread} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/version-7.3.0 b/version-7.3.0 new file mode 100644 index 0000000..5899b83 --- /dev/null +++ b/version-7.3.0 @@ -0,0 +1,2 @@ +QP/C++ 7.3.0 +2023-09-12