QP is an event-driven, RTOS-like, active object framework for microcontrollers, such as mbed. The QP framework provides thread-safe execution of active objects (concurrent state machines) and support both manual and automatic coding of UML statecharts in readable, production-quality C or C++. Automatic code generation of QP code is supported by the free QM modeling tool.
Dependents: qp_hangman qp_dpp qp_blinky
QP/C++ (Quantum Platform in C++) is a lightweight, open source active object (actor) framework for building responsive and modular real-time embedded applications as systems of asynchronous event-driven active objects (actors). The QP/C++ framework is a member of a larger family consisting of QP/C++, QP/C, and QP-nano frameworks, which are all strictly quality controlled, thoroughly documented, and available under GPLv3 with a special Exception for mbed (see http://www.state-machine.com/licensing/QP-mbed_GPL_Exception.txt).
The behavior of active objects is specified in QP/C++ by means of hierarchical state machines (UML statecharts). The framework supports manual coding of UML state machines in C++ as well as automatic code generation by means of the free QM modeling tool (http://www.state-machine.com/qm).
Please see the "QP/C++ Reference Manual" (http://www.state-machine.com/qpcpp) for more information.
Diff: qp.cpp
- Revision:
- 9:ca2e6010d9e2
- Parent:
- 7:bf92d3a6625e
--- a/qp.cpp Mon Sep 26 03:27:09 2011 +0000 +++ b/qp.cpp Tue Sep 04 22:20:52 2012 +0000 @@ -1,2196 +1,2778 @@ -////////////////////////////////////////////////////////////////////////////// -// Product: QP/C++, selectabel Vanilla/QK kernels -// Last Updated for QP ver: 4.2.04 (modified to fit in one file) -// Date of the Last Update: Sep 25, 2011 -// -// Q u a n t u m L e a P s -// --------------------------- -// innovating embedded systems -// -// Copyright (C) 2002-2011 Quantum Leaps, LLC. All rights reserved. -// -// This software may be distributed and modified under the terms of the GNU -// General Public License version 2 (GPL) as published by the Free Software -// Foundation and appearing in the file GPL.TXT included in the packaging of -// this file. Please note that GPL Section 2[b] requires that all works based -// on this software must also be made publicly available under the terms of -// the GPL ("Copyleft"). -// -// Alternatively, this software may be distributed and modified under the -// terms of Quantum Leaps commercial licenses, which expressly supersede -// the GPL and are specifically designed for licensees interested in -// retaining the proprietary status of their code. -// -// Contact information: -// Quantum Leaps Web site: http://www.quantum-leaps.com -// e-mail: info@quantum-leaps.com -////////////////////////////////////////////////////////////////////////////// -#include "qp_port.h" // QP port - -#ifdef Q_USE_NAMESPACE -namespace QP { -#endif - -Q_DEFINE_THIS_MODULE(qp) - -// "qep_pkg.h" =============================================================== -/// internal QEP constants -enum QEPConst { - QEP_EMPTY_SIG_ = 0, ///< empty signal for internal use only - - /// maximum depth of state nesting (including the top level), must be >= 3 - QEP_MAX_NEST_DEPTH_ = 6 -}; - -/// helper macro to trigger internal event in an HSM -#define QEP_TRIG_(state_, sig_) \ - ((*(state_))(this, &QEP_reservedEvt_[sig_])) - -/// helper macro to trigger exit action in an HSM -#define QEP_EXIT_(state_) \ - if (QEP_TRIG_(state_, Q_EXIT_SIG) == Q_RET_HANDLED) { \ - QS_BEGIN_(QS_QEP_STATE_EXIT, QS::smObj_, this) \ - QS_OBJ_(this); \ - QS_FUN_(state_); \ - QS_END_() \ - } - -/// helper macro to trigger entry action in an HSM -#define QEP_ENTER_(state_) \ - if (QEP_TRIG_(state_, Q_ENTRY_SIG) == Q_RET_HANDLED) { \ - QS_BEGIN_(QS_QEP_STATE_ENTRY, QS::smObj_, this) \ - QS_OBJ_(this); \ - QS_FUN_(state_); \ - QS_END_() \ - } - -// "qep.cpp" ================================================================= -// Package-scope objects ----------------------------------------------------- -QEvent const QEP_reservedEvt_[] = { -#ifdef Q_EVT_CTOR - (QSignal)QEP_EMPTY_SIG_, - (QSignal)Q_ENTRY_SIG, - (QSignal)Q_EXIT_SIG, - (QSignal)Q_INIT_SIG -#else - {(QSignal)QEP_EMPTY_SIG_, (uint8_t)0, (uint8_t)0}, - {(QSignal)Q_ENTRY_SIG, (uint8_t)0, (uint8_t)0}, - {(QSignal)Q_EXIT_SIG, (uint8_t)0, (uint8_t)0}, - {(QSignal)Q_INIT_SIG, (uint8_t)0, (uint8_t)0} -#endif -}; -//............................................................................ -//lint -e970 -e971 -e778 ignore MISRA rules 13 and 14 in this function -char const Q_ROM * Q_ROM_VAR QEP::getVersion(void) { - static char const Q_ROM Q_ROM_VAR version[] = { - (char)(((QP_VERSION >> 12U) & 0xFU) + (uint8_t)'0'), - '.', - (char)(((QP_VERSION >> 8U) & 0xFU) + (uint8_t)'0'), - '.', - (char)(((QP_VERSION >> 4U) & 0xFU) + (uint8_t)'0'), - (char)((QP_VERSION & 0xFU) + (uint8_t)'0'), - '\0' - }; - return version; -} - -// "qhsm_top.cpp" ============================================================ -QState QHsm::top(QHsm *, QEvent const *) { - return Q_IGNORED(); // the top state ignores all events -} - -// "qhsm_ini.cpp" ============================================================ -QHsm::~QHsm() { -} -//............................................................................ -void QHsm::init(QEvent const *e) { - QStateHandler t; - QS_INT_LOCK_KEY_ - - // the top-most initial transition must be taken - Q_ALLEGE((*m_state)(this, e) == Q_RET_TRAN); - - t = (QStateHandler)&QHsm::top; // HSM starts in the top state - do { // drill into the target... - QStateHandler path[QEP_MAX_NEST_DEPTH_]; - int8_t ip = (int8_t)0; // transition entry path index - - - QS_BEGIN_(QS_QEP_STATE_INIT, QS::smObj_, this) - QS_OBJ_(this); // this state machine object - QS_FUN_(t); // the source state - QS_FUN_(m_state); // the target of the initial transition - QS_END_() - - path[0] = m_state; - (void)QEP_TRIG_(m_state, QEP_EMPTY_SIG_); - while (m_state != t) { - ++ip; - path[ip] = m_state; - (void)QEP_TRIG_(m_state, QEP_EMPTY_SIG_); - } - m_state = path[0]; - // entry path must not overflow - Q_ASSERT(ip < (int8_t)QEP_MAX_NEST_DEPTH_); - - do { // retrace the entry path in reverse (desired) order... - QEP_ENTER_(path[ip]); // enter path[ip] - --ip; - } while (ip >= (int8_t)0); - - t = path[0]; // current state becomes the new source - } while (QEP_TRIG_(t, Q_INIT_SIG) == Q_RET_TRAN); - m_state = t; - - QS_BEGIN_(QS_QEP_INIT_TRAN, QS::smObj_, this) - QS_TIME_(); // time stamp - QS_OBJ_(this); // this state machine object - QS_FUN_(m_state); // the new active state - QS_END_() -} - -// "qhsm_dis.cpp" ============================================================ -void QHsm::dispatch(QEvent const *e) { - QStateHandler path[QEP_MAX_NEST_DEPTH_]; - QStateHandler s; - QStateHandler t; - QState r; - QS_INT_LOCK_KEY_ - - t = m_state; // save the current state - - QS_BEGIN_(QS_QEP_DISPATCH, QS::smObj_, this) - QS_TIME_(); // time stamp - QS_SIG_(e->sig); // the signal of the event - QS_OBJ_(this); // this state machine object - QS_FUN_(t); // the current state - QS_END_() - - do { // process the event hierarchically... - s = m_state; - r = (*s)(this, e); // invoke state handler s - } while (r == Q_RET_SUPER); - - if (r == Q_RET_TRAN) { // transition taken? -#ifdef Q_SPY - QStateHandler src = s; // save the transition source for tracing -#endif - int8_t ip = (int8_t)(-1); // transition entry path index - int8_t iq; // helper transition entry path index - - path[0] = m_state; // save the target of the transition - path[1] = t; - - while (t != s) { // exit current state to transition source s... - if (QEP_TRIG_(t, Q_EXIT_SIG) == Q_RET_HANDLED) { //exit handled? - QS_BEGIN_(QS_QEP_STATE_EXIT, QS::smObj_, this) - QS_OBJ_(this); // this state machine object - QS_FUN_(t); // the exited state - QS_END_() - - (void)QEP_TRIG_(t, QEP_EMPTY_SIG_); // find superstate of t - } - t = m_state; // m_state holds the superstate - } - - t = path[0]; // target of the transition - - if (s == t) { // (a) check source==target (transition to self) - QEP_EXIT_(s) // exit the source - ip = (int8_t)0; // enter the target - } - else { - (void)QEP_TRIG_(t, QEP_EMPTY_SIG_); // superstate of target - t = m_state; - if (s == t) { // (b) check source==target->super - ip = (int8_t)0; // enter the target - } - else { - (void)QEP_TRIG_(s, QEP_EMPTY_SIG_); // superstate of src - // (c) check source->super==target->super - if (m_state == t) { - QEP_EXIT_(s) // exit the source - ip = (int8_t)0; // enter the target - } - else { - // (d) check source->super==target - if (m_state == path[0]) { - QEP_EXIT_(s) // exit the source - } - else { // (e) check rest of source==target->super->super.. - // and store the entry path along the way - // - iq = (int8_t)0; // indicate that LCA not found - ip = (int8_t)1; // enter target and its superstate - path[1] = t; // save the superstate of target - t = m_state; // save source->super - // find target->super->super - r = QEP_TRIG_(path[1], QEP_EMPTY_SIG_); - while (r == Q_RET_SUPER) { - ++ip; - path[ip] = m_state; // store the entry path - if (m_state == s) { // is it the source? - iq = (int8_t)1; // indicate that LCA found - // entry path must not overflow - Q_ASSERT(ip < (int8_t)QEP_MAX_NEST_DEPTH_); - --ip; // do not enter the source - r = Q_RET_HANDLED; // terminate the loop - } - else { // it is not the source, keep going up - r = QEP_TRIG_(m_state, QEP_EMPTY_SIG_); - } - } - if (iq == (int8_t)0) { // the LCA not found yet? - - // entry path must not overflow - Q_ASSERT(ip < (int8_t)QEP_MAX_NEST_DEPTH_); - - QEP_EXIT_(s) // exit the source - - // (f) check the rest of source->super - // == target->super->super... - // - iq = ip; - r = Q_RET_IGNORED; // indicate LCA NOT found - do { - if (t == path[iq]) { // is this the LCA? - r = Q_RET_HANDLED; // indicate LCA found - ip = (int8_t)(iq - 1); // do not enter LCA - iq = (int8_t)(-1); // terminate the loop - } - else { - --iq; // try lower superstate of target - } - } while (iq >= (int8_t)0); - - if (r != Q_RET_HANDLED) { // LCA not found yet? - // (g) check each source->super->... - // for each target->super... - // - r = Q_RET_IGNORED; // keep looping - do { - // exit t unhandled? - if (QEP_TRIG_(t, Q_EXIT_SIG) - == Q_RET_HANDLED) - { - QS_BEGIN_(QS_QEP_STATE_EXIT, - QS::smObj_, this) - QS_OBJ_(this); - QS_FUN_(t); - QS_END_() - - (void)QEP_TRIG_(t, QEP_EMPTY_SIG_); - } - t = m_state; // set to super of t - iq = ip; - do { - if (t == path[iq]) { // is this LCA? - // do not enter LCA - ip = (int8_t)(iq - 1); - iq = (int8_t)(-1); //break inner - r = Q_RET_HANDLED; //break outer - } - else { - --iq; - } - } while (iq >= (int8_t)0); - } while (r != Q_RET_HANDLED); - } - } - } - } - } - } - // retrace the entry path in reverse (desired) order... - for (; ip >= (int8_t)0; --ip) { - QEP_ENTER_(path[ip]) // enter path[ip] - } - t = path[0]; // stick the target into register - m_state = t; // update the current state - - // drill into the target hierarchy... - while (QEP_TRIG_(t, Q_INIT_SIG) == Q_RET_TRAN) { - - QS_BEGIN_(QS_QEP_STATE_INIT, QS::smObj_, this) - QS_OBJ_(this); // this state machine object - QS_FUN_(t); // the source (pseudo)state - QS_FUN_(m_state); // the target of the transition - QS_END_() - - ip = (int8_t)0; - path[0] = m_state; - (void)QEP_TRIG_(m_state, QEP_EMPTY_SIG_); // find superstate - while (m_state != t) { - ++ip; - path[ip] = m_state; - (void)QEP_TRIG_(m_state, QEP_EMPTY_SIG_); // find superstate - } - m_state = path[0]; - // entry path must not overflow - Q_ASSERT(ip < (int8_t)QEP_MAX_NEST_DEPTH_); - - do { // retrace the entry path in reverse (correct) order... - QEP_ENTER_(path[ip]) // enter path[ip] - --ip; - } while (ip >= (int8_t)0); - - t = path[0]; - } - - QS_BEGIN_(QS_QEP_TRAN, QS::smObj_, this) - QS_TIME_(); // time stamp - QS_SIG_(e->sig); // the signal of the event - QS_OBJ_(this); // this state machine object - QS_FUN_(src); // the source of the transition - QS_FUN_(t); // the new active state - QS_END_() - - } - else { // transition not taken -#ifdef Q_SPY - if (r == Q_RET_IGNORED) { // event ignored? - - QS_BEGIN_(QS_QEP_IGNORED, QS::smObj_, this) - QS_TIME_(); // time stamp - QS_SIG_(e->sig); // the signal of the event - QS_OBJ_(this); // this state machine object - QS_FUN_(t); // the current state - QS_END_() - - } - else { // event handled - - QS_BEGIN_(QS_QEP_INTERN_TRAN, QS::smObj_, this) - QS_TIME_(); // time stamp - QS_SIG_(e->sig); // the signal of the event - QS_OBJ_(this); // this state machine object - QS_FUN_(s); // the state that handled the event - QS_END_() - - } -#endif - } - m_state = t; // set new state or restore the current state -} - -// "qf_pkg.h" ================================================================ - // QF-specific interrupt locking/unlocking -#ifndef QF_INT_KEY_TYPE - - /// \brief This is an internal macro for defining the interrupt lock key. - /// - /// The purpose of this macro is to enable writing the same code for the - /// case when interrupt key is defined and when it is not. If the macro - /// #QF_INT_KEY_TYPE is defined, this internal macro provides the - /// definition of the lock key variable. Otherwise this macro is empty. - /// \sa #QF_INT_KEY_TYPE - #define QF_INT_LOCK_KEY_ - - /// \brief This is an internal macro for locking interrupts. - /// - /// The purpose of this macro is to enable writing the same code for the - /// case when interrupt key is defined and when it is not. If the macro - /// #QF_INT_KEY_TYPE is defined, this internal macro invokes #QF_INT_LOCK - /// passing the key variable as the parameter. Otherwise #QF_INT_LOCK - /// is invoked with a dummy parameter. - /// \sa #QF_INT_LOCK, #QK_INT_LOCK - #define QF_INT_LOCK_() QF_INT_LOCK(dummy) - - /// \brief This is an internal macro for unlocking interrupts. - /// - /// The purpose of this macro is to enable writing the same code for the - /// case when interrupt key is defined and when it is not. If the macro - /// #QF_INT_KEY_TYPE is defined, this internal macro invokes - /// #QF_INT_UNLOCK passing the key variable as the parameter. - /// Otherwise #QF_INT_UNLOCK is invoked with a dummy parameter. - /// \sa #QF_INT_UNLOCK, #QK_INT_UNLOCK - #define QF_INT_UNLOCK_() QF_INT_UNLOCK(dummy) -#else - #define QF_INT_LOCK_KEY_ QF_INT_KEY_TYPE intLockKey_; - #define QF_INT_LOCK_() QF_INT_LOCK(intLockKey_) - #define QF_INT_UNLOCK_() QF_INT_UNLOCK(intLockKey_) -#endif - -// package-scope objects ----------------------------------------------------- -extern QTimeEvt *QF_timeEvtListHead_; ///< head of linked list of time events -extern QF_EPOOL_TYPE_ QF_pool_[QF_MAX_EPOOL]; ///< allocate event pools -extern uint8_t QF_maxPool_; ///< # of initialized event pools -extern QSubscrList *QF_subscrList_; ///< the subscriber list array -extern QSignal QF_maxSignal_; ///< the maximum published signal - -//............................................................................ -/// \brief Structure representing a free block in the Native QF Memory Pool -/// \sa ::QMPool -struct QFreeBlock { - QFreeBlock *m_next; -}; - -/// \brief access to the poolId of an event \a e_ -#define EVT_POOL_ID(e_) ((e_)->poolId_) - -/// \brief access to the refCtr of an event \a e_ -#define EVT_REF_CTR(e_) ((e_)->refCtr_) - -/// \brief increment the refCtr of an event \a e_ -#define EVT_INC_REF_CTR(e_) (++((QEvent *)(e_))->refCtr_) - -/// \brief decrement the refCtr of an event \a e_ -#define EVT_DEC_REF_CTR(e_) (--((QEvent *)(e_))->refCtr_) - -// "qa_defer.cpp" ============================================================ -//............................................................................ -void QActive::defer(QEQueue *eq, QEvent const *e) { - eq->postFIFO(e); -} -//............................................................................ -uint8_t QActive::recall(QEQueue *eq) { - QEvent const *e = eq->get(); // try to get an event from deferred queue - if (e != (QEvent *)0) { // event available? - - postLIFO(e); // post it to the front of the Active Object's queue - - QF_INT_LOCK_KEY_ - QF_INT_LOCK_(); - - if (EVT_POOL_ID(e) != (uint8_t)0) { // is it a dynamic 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(EVT_REF_CTR(e) > (uint8_t)1); - - // we need to decrement the reference counter once, to account - // for removing the event from the deferred event queue. - // - EVT_DEC_REF_CTR(e); // decrement the reference counter - } - - QF_INT_UNLOCK_(); - - return (uint8_t)1; // event recalled - } - else { - return (uint8_t)0; // event not recalled - } -} - -// "qa_fifo.cpp" ============================================================= -//............................................................................ -#ifndef Q_SPY -void QActive::postFIFO(QEvent const *e) { -#else -void QActive::postFIFO(QEvent const *e, void const *sender) { -#endif - - QF_INT_LOCK_KEY_ - QF_INT_LOCK_(); - - QS_BEGIN_NOLOCK_(QS_QF_ACTIVE_POST_FIFO, QS::aoObj_, this) - QS_TIME_(); // timestamp - QS_OBJ_(sender); // the sender object - QS_SIG_(e->sig); // the signal of the event - QS_OBJ_(this); // this active object - QS_U8_(EVT_POOL_ID(e)); // the pool Id of the event - QS_U8_(EVT_REF_CTR(e)); // the ref count of the event - QS_EQC_(m_eQueue.m_nFree); // number of free entries - QS_EQC_(m_eQueue.m_nMin); // min number of free entries - QS_END_NOLOCK_() - - if (EVT_POOL_ID(e) != (uint8_t)0) { // is it a dynamic event? - EVT_INC_REF_CTR(e); // increment the reference counter - } - - if (m_eQueue.m_frontEvt == (QEvent *)0) { // is the queue empty? - m_eQueue.m_frontEvt = e; // deliver event directly - QACTIVE_EQUEUE_SIGNAL_(this); // signal the event queue - } - else { // queue is not empty, leave event in the ring-buffer - // queue must accept all posted events - Q_ASSERT(m_eQueue.m_nFree != (QEQueueCtr)0); - m_eQueue.m_ring[m_eQueue.m_head] = e;//insert e into the buffer (FIFO) - if (m_eQueue.m_head == (QEQueueCtr)0) { // need to wrap the head? - m_eQueue.m_head = m_eQueue.m_end; // wrap around - } - --m_eQueue.m_head; - - --m_eQueue.m_nFree; // update number of free events - if (m_eQueue.m_nMin > m_eQueue.m_nFree) { - m_eQueue.m_nMin = m_eQueue.m_nFree; // update minimum so far - } - } - QF_INT_UNLOCK_(); -} - -// "qa_get_.cpp" ============================================================= -QEvent const *QActive::get_(void) { - QF_INT_LOCK_KEY_ - QF_INT_LOCK_(); - - QACTIVE_EQUEUE_WAIT_(this); // wait for event to arrive directly - - QEvent const *e = m_eQueue.m_frontEvt; - - if (m_eQueue.m_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 == (QEQueueCtr)0) { // need to wrap the tail? - m_eQueue.m_tail = m_eQueue.m_end; // wrap around - } - --m_eQueue.m_tail; - - ++m_eQueue.m_nFree; // one more free event in the ring buffer - - QS_BEGIN_NOLOCK_(QS_QF_ACTIVE_GET, QS::aoObj_, this) - QS_TIME_(); // timestamp - QS_SIG_(e->sig); // the signal of this event - QS_OBJ_(this); // this active object - QS_U8_(EVT_POOL_ID(e)); // the pool Id of the event - QS_U8_(EVT_REF_CTR(e)); // the ref count of the event - QS_EQC_(m_eQueue.m_nFree); // number of free entries - QS_END_NOLOCK_() - } - else { - m_eQueue.m_frontEvt = (QEvent *)0; // the queue becomes empty - QACTIVE_EQUEUE_ONEMPTY_(this); - - QS_BEGIN_NOLOCK_(QS_QF_ACTIVE_GET_LAST, QS::aoObj_, this) - QS_TIME_(); // timestamp - QS_SIG_(e->sig); // the signal of this event - QS_OBJ_(this); // this active object - QS_U8_(EVT_POOL_ID(e)); // the pool Id of the event - QS_U8_(EVT_REF_CTR(e)); // the ref count of the event - QS_END_NOLOCK_() - } - QF_INT_UNLOCK_(); - return e; -} -//............................................................................ -uint32_t QF::getQueueMargin(uint8_t prio) { - Q_REQUIRE((prio <= (uint8_t)QF_MAX_ACTIVE) - && (active_[prio] != (QActive *)0)); - - QF_INT_LOCK_KEY_ - QF_INT_LOCK_(); - uint32_t margin = (uint32_t)(active_[prio]->m_eQueue.m_nMin); - QF_INT_UNLOCK_(); - - return margin; -} - -// "qa_lifo.cpp" ============================================================= -void QActive::postLIFO(QEvent const *e) { - QF_INT_LOCK_KEY_ - QF_INT_LOCK_(); - - QS_BEGIN_NOLOCK_(QS_QF_ACTIVE_POST_LIFO, QS::aoObj_, this) - QS_TIME_(); // timestamp - QS_SIG_(e->sig); // the signal of this event - QS_OBJ_(this); // this active object - QS_U8_(EVT_POOL_ID(e)); // the pool Id of the event - QS_U8_(EVT_REF_CTR(e)); // the ref count of the event - QS_EQC_(m_eQueue.m_nFree); // number of free entries - QS_EQC_(m_eQueue.m_nMin); // min number of free entries - QS_END_NOLOCK_() - - if (EVT_POOL_ID(e) != (uint8_t)0) { // is it a dynamic event? - EVT_INC_REF_CTR(e); // increment the reference counter - } - - if (m_eQueue.m_frontEvt == (QEvent *)0) { // is the queue empty? - m_eQueue.m_frontEvt = e; // deliver event directly - QACTIVE_EQUEUE_SIGNAL_(this); // signal the event queue - } - else { // queue is not empty, leave event in the ring-buffer - // queue must accept all posted events - Q_ASSERT(m_eQueue.m_nFree != (QEQueueCtr)0); - - ++m_eQueue.m_tail; - if (m_eQueue.m_tail == m_eQueue.m_end) { // need to wrap the tail? - m_eQueue.m_tail = (QEQueueCtr)0; // wrap around - } - - m_eQueue.m_ring[m_eQueue.m_tail] = m_eQueue.m_frontEvt; - m_eQueue.m_frontEvt = e; // put event to front - - --m_eQueue.m_nFree; // update number of free events - if (m_eQueue.m_nMin > m_eQueue.m_nFree) { - m_eQueue.m_nMin = m_eQueue.m_nFree; // update minimum so far - } - } - QF_INT_UNLOCK_(); -} - -// "qa_sub.cpp" ============================================================== -void QActive::subscribe(QSignal sig) const { - uint8_t p = m_prio; - Q_REQUIRE(((QSignal)Q_USER_SIG <= sig) - && (sig < QF_maxSignal_) - && ((uint8_t)0 < p) && (p <= (uint8_t)QF_MAX_ACTIVE) - && (QF::active_[p] == this)); - - uint8_t i = Q_ROM_BYTE(QF_div8Lkup[p]); - - QF_INT_LOCK_KEY_ - QF_INT_LOCK_(); - - QS_BEGIN_NOLOCK_(QS_QF_ACTIVE_SUBSCRIBE, QS::aoObj_, this) - QS_TIME_(); // timestamp - QS_SIG_(sig); // the signal of this event - QS_OBJ_(this); // this active object - QS_END_NOLOCK_() - // set the priority bit - QF_subscrList_[sig].m_bits[i] |= Q_ROM_BYTE(QF_pwr2Lkup[p]); - QF_INT_UNLOCK_(); -} - -// "qa_usub.cpp" ============================================================= -void QActive::unsubscribe(QSignal sig) const { - uint8_t p = m_prio; - Q_REQUIRE(((QSignal)Q_USER_SIG <= sig) - && (sig < QF_maxSignal_) - && ((uint8_t)0 < p) && (p <= (uint8_t)QF_MAX_ACTIVE) - && (QF::active_[p] == this)); - - uint8_t i = Q_ROM_BYTE(QF_div8Lkup[p]); - - QF_INT_LOCK_KEY_ - QF_INT_LOCK_(); - - QS_BEGIN_NOLOCK_(QS_QF_ACTIVE_UNSUBSCRIBE, QS::aoObj_, this) - QS_TIME_(); // timestamp - QS_SIG_(sig); // the signal of this event - QS_OBJ_(this); // this active object - QS_END_NOLOCK_() - // clear the priority bit - QF_subscrList_[sig].m_bits[i] &= Q_ROM_BYTE(QF_invPwr2Lkup[p]); - QF_INT_UNLOCK_(); -} - -// "qa_usuba.cpp" ============================================================ -void QActive::unsubscribeAll(void) const { - uint8_t p = m_prio; - Q_REQUIRE(((uint8_t)0 < p) && (p <= (uint8_t)QF_MAX_ACTIVE) - && (QF::active_[p] == this)); - - uint8_t i = Q_ROM_BYTE(QF_div8Lkup[p]); - - QSignal sig; - for (sig = (QSignal)Q_USER_SIG; sig < QF_maxSignal_; ++sig) { - QF_INT_LOCK_KEY_ - QF_INT_LOCK_(); - if ((QF_subscrList_[sig].m_bits[i] & Q_ROM_BYTE(QF_pwr2Lkup[p])) - != 0) - { - - QS_BEGIN_NOLOCK_(QS_QF_ACTIVE_UNSUBSCRIBE, QS::aoObj_, this) - QS_TIME_(); // timestamp - QS_SIG_(sig); // the signal of this event - QS_OBJ_(this); // this active object - QS_END_NOLOCK_() - // clear the priority bit - QF_subscrList_[sig].m_bits[i] &= Q_ROM_BYTE(QF_invPwr2Lkup[p]); - } - QF_INT_UNLOCK_(); - } -} - -// "qeq_fifo.cpp" ============================================================ -void QEQueue::postFIFO(QEvent const *e) { - QF_INT_LOCK_KEY_ - QF_INT_LOCK_(); - - QS_BEGIN_NOLOCK_(QS_QF_EQUEUE_POST_FIFO, QS::eqObj_, this) - QS_TIME_(); // timestamp - QS_SIG_(e->sig); // the signal of this event - QS_OBJ_(this); // this queue object - QS_U8_(EVT_POOL_ID(e)); // the pool Id of the event - QS_U8_(EVT_REF_CTR(e)); // the ref count of the event - QS_EQC_(m_nFree); // number of free entries - QS_EQC_(m_nMin); // min number of free entries - QS_END_NOLOCK_() - - if (EVT_POOL_ID(e) != (uint8_t)0) { // is it a dynamic event? - EVT_INC_REF_CTR(e); // increment the reference counter - } - - if (m_frontEvt == (QEvent *)0) { // is the queue empty? - m_frontEvt = e; // deliver event directly - } - else { // queue is not empty, leave event in the ring-buffer - // the queue must be able to accept the event (cannot overflow) - Q_ASSERT(m_nFree != (QEQueueCtr)0); - - m_ring[m_head] = e; // insert event into the buffer (FIFO) - if (m_head == (QEQueueCtr)0) { // need to wrap the head? - m_head = m_end; // wrap around - } - --m_head; - - --m_nFree; // update number of free events - if (m_nMin > m_nFree) { - m_nMin = m_nFree; // update minimum so far - } - } - QF_INT_UNLOCK_(); -} - -// "qeq_get.cpp" ============================================================= -QEvent const *QEQueue::get(void) { - QEvent const *e; - QF_INT_LOCK_KEY_ - - QF_INT_LOCK_(); - if (m_frontEvt == (QEvent *)0) { // is the queue empty? - e = (QEvent *)0; // no event available at this time - } - else { - e = m_frontEvt; - - if (m_nFree != m_end) { // any events in the the ring buffer? - m_frontEvt = m_ring[m_tail]; // remove event from the tail - if (m_tail == (QEQueueCtr)0) { // need to wrap the tail? - m_tail = m_end; // wrap around - } - --m_tail; - - ++m_nFree; // one more free event in the ring buffer - - QS_BEGIN_NOLOCK_(QS_QF_EQUEUE_GET, QS::eqObj_, this) - QS_TIME_(); // timestamp - QS_SIG_(e->sig); // the signal of this event - QS_OBJ_(this); // this queue object - QS_U8_(EVT_POOL_ID(e)); // the pool Id of the event - QS_U8_(EVT_REF_CTR(e)); // the ref count of the event - QS_EQC_(m_nFree); // number of free entries - QS_END_NOLOCK_() - } - else { - m_frontEvt = (QEvent *)0; // the queue becomes empty - - QS_BEGIN_NOLOCK_(QS_QF_EQUEUE_GET_LAST, QS::eqObj_, this) - QS_TIME_(); // timestamp - QS_SIG_(e->sig); // the signal of this event - QS_OBJ_(this); // this queue object - QS_U8_(EVT_POOL_ID(e)); // the pool Id of the event - QS_U8_(EVT_REF_CTR(e)); // the ref count of the event - QS_END_NOLOCK_() - } - } - QF_INT_UNLOCK_(); - return e; -} - -// "qeq_init.cpp" ============================================================ -void QEQueue::init(QEvent const *qSto[], QEQueueCtr qLen) { - m_frontEvt = (QEvent *)0; // no events in the queue - m_ring = &qSto[0]; - m_end = qLen; - m_head = (QEQueueCtr)0; - m_tail = (QEQueueCtr)0; - m_nFree = qLen; - m_nMin = qLen; - - QS_INT_LOCK_KEY_ - QS_BEGIN_(QS_QF_EQUEUE_INIT, QS::eqObj_, this) - QS_OBJ_(qSto); // this QEQueue object - QS_EQC_(qLen); // the length of the queue - QS_END_() -} - -// "qeq_lifo.cpp" ============================================================ -void QEQueue::postLIFO(QEvent const *e) { - QF_INT_LOCK_KEY_ - QF_INT_LOCK_(); - - QS_BEGIN_NOLOCK_(QS_QF_EQUEUE_POST_LIFO, QS::eqObj_, this) - QS_TIME_(); // timestamp - QS_SIG_(e->sig); // the signal of this event - QS_OBJ_(this); // this queue object - QS_U8_(EVT_POOL_ID(e)); // the pool Id of the event - QS_U8_(EVT_REF_CTR(e)); // the ref count of the event - QS_EQC_(m_nFree); // number of free entries - QS_EQC_(m_nMin); // min number of free entries - QS_END_NOLOCK_() - - if (EVT_POOL_ID(e) != (uint8_t)0) { // is it a dynamic event? - EVT_INC_REF_CTR(e); // increment the reference counter - } - - if (m_frontEvt != (QEvent *)0) { // is the queue not empty? - // the queue must be able to accept the event (cannot overflow) - Q_ASSERT(m_nFree != (QEQueueCtr)0); - - ++m_tail; - if (m_tail == m_end) { // need to wrap the tail? - m_tail = (QEQueueCtr)0; // wrap around - } - - m_ring[m_tail] = m_frontEvt; // buffer the old front evt - - --m_nFree; // update number of free events - if (m_nMin > m_nFree) { - m_nMin = m_nFree; // update minimum so far - } - } - - m_frontEvt = e; // stick the new event to the front - - QF_INT_UNLOCK_(); -} - -// "qf_act.cpp" ============================================================== -// public objects ------------------------------------------------------------ -QActive *QF::active_[QF_MAX_ACTIVE + 1]; // to be used by QF ports only -uint8_t QF_intLockNest_; // interrupt-lock nesting level - -//............................................................................ -//lint -e970 -e971 ignore MISRA rules 13 and 14 in this function -const char Q_ROM * Q_ROM_VAR QF::getVersion(void) { - static char const Q_ROM Q_ROM_VAR version[] = { - (char)(((QP_VERSION >> 12U) & 0xFU) + (uint8_t)'0'), - '.', - (char)(((QP_VERSION >> 8U) & 0xFU) + (uint8_t)'0'), - '.', - (char)(((QP_VERSION >> 4U) & 0xFU) + (uint8_t)'0'), - (char)((QP_VERSION & 0xFU) + (uint8_t)'0'), - '\0' - }; - return version; -} -//............................................................................ -void QF::add_(QActive *a) { - uint8_t p = a->m_prio; - - Q_REQUIRE(((uint8_t)0 < p) && (p <= (uint8_t)QF_MAX_ACTIVE) - && (active_[p] == (QActive *)0)); - - QF_INT_LOCK_KEY_ - QF_INT_LOCK_(); - - active_[p] = a; // registger the active object at this priority - - QS_BEGIN_NOLOCK_(QS_QF_ACTIVE_ADD, QS::aoObj_, a) - QS_TIME_(); // timestamp - QS_OBJ_(a); // the active object - QS_U8_(p); // the priority of the active object - QS_END_NOLOCK_() - - QF_INT_UNLOCK_(); -} -//............................................................................ -void QF::remove_(QActive const *a) { - uint8_t p = a->m_prio; - - Q_REQUIRE(((uint8_t)0 < p) && (p <= (uint8_t)QF_MAX_ACTIVE) - && (active_[p] == a)); - - QF_INT_LOCK_KEY_ - QF_INT_LOCK_(); - - active_[p] = (QActive *)0; // free-up the priority level - - QS_BEGIN_NOLOCK_(QS_QF_ACTIVE_REMOVE, QS::aoObj_, a) - QS_TIME_(); // timestamp - QS_OBJ_(a); // the active object - QS_U8_(p); // the priority of the active object - QS_END_NOLOCK_() - - QF_INT_UNLOCK_(); -} - -// "qf_gc.cpp" =============================================================== -void QF::gc(QEvent const *e) { - if (EVT_POOL_ID(e) != (uint8_t)0) { // is it a dynamic event? - QF_INT_LOCK_KEY_ - QF_INT_LOCK_(); - - if (EVT_REF_CTR(e) > (uint8_t)1) { // isn't this the last reference? - EVT_DEC_REF_CTR(e); // decrement the ref counter - - QS_BEGIN_NOLOCK_(QS_QF_GC_ATTEMPT, (void *)0, (void *)0) - QS_TIME_(); // timestamp - QS_SIG_(e->sig); // the signal of the event - QS_U8_(EVT_POOL_ID(e)); // the pool Id of the event - QS_U8_(EVT_REF_CTR(e)); // the ref count of the event - QS_END_NOLOCK_() - - QF_INT_UNLOCK_(); - } - else { // this is the last reference to this event, recycle it - uint8_t idx = (uint8_t)(EVT_POOL_ID(e) - 1); - - QS_BEGIN_NOLOCK_(QS_QF_GC, (void *)0, (void *)0) - QS_TIME_(); // timestamp - QS_SIG_(e->sig); // the signal of the event - QS_U8_(EVT_POOL_ID(e)); // the pool Id of the event - QS_U8_(EVT_REF_CTR(e)); // the ref count of the event - QS_END_NOLOCK_() - - QF_INT_UNLOCK_(); - - Q_ASSERT(idx < QF_maxPool_); - -#ifdef Q_EVT_CTOR - //lint -e1773 Attempt to cast away const - ((QEvent *)e)->~QEvent(); // call the xtor, cast 'const' away, - // which is legitimate, because it's a pool event -#endif - //lint -e1773 Attempt to cast away const - QF_EPOOL_PUT_(QF_pool_[idx], (QEvent *)e); // cast 'const' away, - // which is legitimate, because it's a pool event - } - } -} - -// "qf_log2.cpp" ============================================================= -uint8_t const Q_ROM Q_ROM_VAR QF_log2Lkup[256] = { - 0U, 1U, 2U, 2U, 3U, 3U, 3U, 3U, 4U, 4U, 4U, 4U, 4U, 4U, 4U, 4U, - 5U, 5U, 5U, 5U, 5U, 5U, 5U, 5U, 5U, 5U, 5U, 5U, 5U, 5U, 5U, 5U, - 6U, 6U, 6U, 6U, 6U, 6U, 6U, 6U, 6U, 6U, 6U, 6U, 6U, 6U, 6U, 6U, - 6U, 6U, 6U, 6U, 6U, 6U, 6U, 6U, 6U, 6U, 6U, 6U, 6U, 6U, 6U, 6U, - 7U, 7U, 7U, 7U, 7U, 7U, 7U, 7U, 7U, 7U, 7U, 7U, 7U, 7U, 7U, 7U, - 7U, 7U, 7U, 7U, 7U, 7U, 7U, 7U, 7U, 7U, 7U, 7U, 7U, 7U, 7U, 7U, - 7U, 7U, 7U, 7U, 7U, 7U, 7U, 7U, 7U, 7U, 7U, 7U, 7U, 7U, 7U, 7U, - 7U, 7U, 7U, 7U, 7U, 7U, 7U, 7U, 7U, 7U, 7U, 7U, 7U, 7U, 7U, 7U, - 8U, 8U, 8U, 8U, 8U, 8U, 8U, 8U, 8U, 8U, 8U, 8U, 8U, 8U, 8U, 8U, - 8U, 8U, 8U, 8U, 8U, 8U, 8U, 8U, 8U, 8U, 8U, 8U, 8U, 8U, 8U, 8U, - 8U, 8U, 8U, 8U, 8U, 8U, 8U, 8U, 8U, 8U, 8U, 8U, 8U, 8U, 8U, 8U, - 8U, 8U, 8U, 8U, 8U, 8U, 8U, 8U, 8U, 8U, 8U, 8U, 8U, 8U, 8U, 8U, - 8U, 8U, 8U, 8U, 8U, 8U, 8U, 8U, 8U, 8U, 8U, 8U, 8U, 8U, 8U, 8U, - 8U, 8U, 8U, 8U, 8U, 8U, 8U, 8U, 8U, 8U, 8U, 8U, 8U, 8U, 8U, 8U, - 8U, 8U, 8U, 8U, 8U, 8U, 8U, 8U, 8U, 8U, 8U, 8U, 8U, 8U, 8U, 8U, - 8U, 8U, 8U, 8U, 8U, 8U, 8U, 8U, 8U, 8U, 8U, 8U, 8U, 8U, 8U, 8U -}; - -// "qf_new.cpp" ============================================================== -QEvent *QF::new_(uint16_t evtSize, QSignal sig) { - // find the pool id that fits the requested event size ... - uint8_t idx = (uint8_t)0; - while (evtSize > QF_EPOOL_EVENT_SIZE_(QF_pool_[idx])) { - ++idx; - Q_ASSERT(idx < QF_maxPool_); // cannot run out of registered pools - } - - QS_INT_LOCK_KEY_ - QS_BEGIN_(QS_QF_NEW, (void *)0, (void *)0) - QS_TIME_(); // timestamp - QS_EVS_(evtSize); // the size of the event - QS_SIG_(sig); // the signal of the event - QS_END_() - - QEvent *e; - QF_EPOOL_GET_(QF_pool_[idx], e); - Q_ASSERT(e != (QEvent *)0); // pool must not run out of events - - e->sig = sig; // set signal for this event - EVT_POOL_ID(e) = (uint8_t)(idx + 1); // store the pool ID in the event - EVT_REF_CTR(e) = (uint8_t)0; // set the reference counter to 0 - - return e; -} - -// "qf_pool.cpp" ============================================================= -// Package-scope objects ----------------------------------------------------- -QF_EPOOL_TYPE_ QF_pool_[QF_MAX_EPOOL]; // allocate the event pools -uint8_t QF_maxPool_; // number of initialized event pools - -//............................................................................ -void QF::poolInit(void *poolSto, uint32_t poolSize, QEventSize evtSize) { - // cannot exceed the number of available memory pools - Q_REQUIRE(QF_maxPool_ < (uint8_t)Q_DIM(QF_pool_)); - // please initialize event pools in ascending order of evtSize: - Q_REQUIRE((QF_maxPool_ == (uint8_t)0) - || (QF_EPOOL_EVENT_SIZE_(QF_pool_[QF_maxPool_ - 1]) < evtSize)); - QF_EPOOL_INIT_(QF_pool_[QF_maxPool_], poolSto, poolSize, evtSize); - ++QF_maxPool_; // one more pool -} - -// "qf_psini.cpp" ============================================================ -// Package-scope objects ----------------------------------------------------- -QSubscrList *QF_subscrList_; -QSignal QF_maxSignal_; - -//............................................................................ -void QF::psInit(QSubscrList *subscrSto, QSignal maxSignal) { - QF_subscrList_ = subscrSto; - QF_maxSignal_ = maxSignal; -} - -// "qf_pspub.cpp" ============================================================ -#ifndef Q_SPY -void QF::publish(QEvent const *e) { -#else -void QF::publish(QEvent const *e, void const *sender) { -#endif - // make sure that the published signal is within the configured range - Q_REQUIRE(e->sig < QF_maxSignal_); - - QF_INT_LOCK_KEY_ - QF_INT_LOCK_(); - - QS_BEGIN_NOLOCK_(QS_QF_PUBLISH, (void *)0, (void *)0) - QS_TIME_(); // the timestamp - QS_OBJ_(sender); // the sender object - QS_SIG_(e->sig); // the signal of the event - QS_U8_(EVT_POOL_ID(e)); // the pool Id of the event - QS_U8_(EVT_REF_CTR(e)); // the ref count of the event - QS_END_NOLOCK_() - - if (EVT_POOL_ID(e) != (uint8_t)0) { // is it a dynamic event? - EVT_INC_REF_CTR(e); // increment the reference counter, NOTE01 - } - QF_INT_UNLOCK_(); - -#if (QF_MAX_ACTIVE <= 8) - uint8_t tmp = QF_subscrList_[e->sig].m_bits[0]; - while (tmp != (uint8_t)0) { - uint8_t p = Q_ROM_BYTE(QF_log2Lkup[tmp]); - tmp &= Q_ROM_BYTE(QF_invPwr2Lkup[p]); // clear the subscriber bit - Q_ASSERT(active_[p] != (QActive *)0); // must be registered - - // POST() asserts internally if the queue overflows - active_[p]->POST(e, sender); - } -#else - uint8_t i = Q_DIM(QF_subscrList_[0].m_bits);// number of bytes in the list - do { // go through all bytes in the subsciption list - --i; - uint8_t tmp = QF_subscrList_[e->sig].m_bits[i]; - while (tmp != (uint8_t)0) { - uint8_t p = Q_ROM_BYTE(QF_log2Lkup[tmp]); - tmp &= Q_ROM_BYTE(QF_invPwr2Lkup[p]); // clear the subscriber bit - p = (uint8_t)(p + (i << 3)); // adjust the priority - Q_ASSERT(active_[p] != (QActive *)0); // must be registered - - // POST() asserts internally if the queue overflows - active_[p]->POST(e, sender); - } - } while (i != (uint8_t)0); -#endif - - gc(e); // run the garbage collector, see NOTE01 -} - -// "qf_pwr2.cpp" ============================================================= -// Global objects ------------------------------------------------------------ -uint8_t const Q_ROM Q_ROM_VAR QF_pwr2Lkup[65] = { - 0x00U, // unused location - 0x01U, 0x02U, 0x04U, 0x08U, 0x10U, 0x20U, 0x40U, 0x80U, - 0x01U, 0x02U, 0x04U, 0x08U, 0x10U, 0x20U, 0x40U, 0x80U, - 0x01U, 0x02U, 0x04U, 0x08U, 0x10U, 0x20U, 0x40U, 0x80U, - 0x01U, 0x02U, 0x04U, 0x08U, 0x10U, 0x20U, 0x40U, 0x80U, - 0x01U, 0x02U, 0x04U, 0x08U, 0x10U, 0x20U, 0x40U, 0x80U, - 0x01U, 0x02U, 0x04U, 0x08U, 0x10U, 0x20U, 0x40U, 0x80U, - 0x01U, 0x02U, 0x04U, 0x08U, 0x10U, 0x20U, 0x40U, 0x80U, - 0x01U, 0x02U, 0x04U, 0x08U, 0x10U, 0x20U, 0x40U, 0x80U -}; - -uint8_t const Q_ROM Q_ROM_VAR QF_invPwr2Lkup[65] = { - 0xFFU, // unused location - 0xFEU, 0xFDU, 0xFBU, 0xF7U, 0xEFU, 0xDFU, 0xBFU, 0x7FU, - 0xFEU, 0xFDU, 0xFBU, 0xF7U, 0xEFU, 0xDFU, 0xBFU, 0x7FU, - 0xFEU, 0xFDU, 0xFBU, 0xF7U, 0xEFU, 0xDFU, 0xBFU, 0x7FU, - 0xFEU, 0xFDU, 0xFBU, 0xF7U, 0xEFU, 0xDFU, 0xBFU, 0x7FU, - 0xFEU, 0xFDU, 0xFBU, 0xF7U, 0xEFU, 0xDFU, 0xBFU, 0x7FU, - 0xFEU, 0xFDU, 0xFBU, 0xF7U, 0xEFU, 0xDFU, 0xBFU, 0x7FU, - 0xFEU, 0xFDU, 0xFBU, 0xF7U, 0xEFU, 0xDFU, 0xBFU, 0x7FU, - 0xFEU, 0xFDU, 0xFBU, 0xF7U, 0xEFU, 0xDFU, 0xBFU, 0x7FU -}; - -uint8_t const Q_ROM Q_ROM_VAR QF_div8Lkup[65] = { - 0U, // unused location - 0U, 0U, 0U, 0U, 0U, 0U, 0U, 0U, - 1U, 1U, 1U, 1U, 1U, 1U, 1U, 1U, - 2U, 2U, 2U, 2U, 2U, 2U, 2U, 2U, - 3U, 3U, 3U, 3U, 3U, 3U, 3U, 3U, - 4U, 4U, 4U, 4U, 4U, 4U, 4U, 4U, - 5U, 5U, 5U, 5U, 5U, 5U, 5U, 5U, - 6U, 6U, 6U, 6U, 6U, 6U, 6U, 6U, - 7U, 7U, 7U, 7U, 7U, 7U, 7U, 7U -}; - -// "qf_tick.cpp" ============================================================= -#ifndef Q_SPY -void QF::tick(void) { // see NOTE01 -#else -void QF::tick(void const *sender) { -#endif - - QF_INT_LOCK_KEY_ - QF_INT_LOCK_(); - - QS_BEGIN_NOLOCK_(QS_QF_TICK, (void *)0, (void *)0) - QS_TEC_(++QS::tickCtr_); // the tick counter - QS_END_NOLOCK_() - - QTimeEvt *t = QF_timeEvtListHead_; - while (t != (QTimeEvt *)0) { - --t->m_ctr; - if (t->m_ctr == (QTimeEvtCtr)0) { // is the time evt about to expire? - if (t->m_interval != (QTimeEvtCtr)0) {//is it a periodic time evt? - t->m_ctr = t->m_interval; // rearm the time evt - } - else { // one-shot time evt, disarm by removing it from the list - if (t == QF_timeEvtListHead_) { - QF_timeEvtListHead_ = t->m_next; - } - else { - if (t->m_next != (QTimeEvt *)0) {// not the last time evt? - t->m_next->m_prev = t->m_prev; - } - t->m_prev->m_next = t->m_next; - } - t->m_prev = (QTimeEvt *)0; // mark the time event disarmed - - QS_BEGIN_NOLOCK_(QS_QF_TIMEEVT_AUTO_DISARM, QS::teObj_, t) - QS_OBJ_(t); // this time event object - QS_OBJ_(t->m_act); // the active object - QS_END_NOLOCK_() - } - - QS_BEGIN_NOLOCK_(QS_QF_TIMEEVT_POST, QS::teObj_, t) - QS_TIME_(); // timestamp - QS_OBJ_(t); // the time event object - QS_SIG_(t->sig); // the signal of this time event - QS_OBJ_(t->m_act); // the active object - QS_END_NOLOCK_() - - QF_INT_UNLOCK_(); // unlock interrupts before calling QF service - - // POST() asserts internally if the queue overflows - t->m_act->POST(t, sender); - } - else { - QF_INT_UNLOCK_(); - static uint8_t volatile dummy; - dummy = (uint8_t)0; // execute a few instructions, see NOTE02 - } - - QF_INT_LOCK_(); // lock interrupts again to advance the link - t = t->m_next; - } - QF_INT_UNLOCK_(); -} - -// "qmp_get.cpp" ============================================================= -void *QMPool::get(void) { - QF_INT_LOCK_KEY_ - QF_INT_LOCK_(); - - QFreeBlock *fb = (QFreeBlock *)m_free; // get a free block or NULL - if (fb != (QFreeBlock *)0) { // free block available? - m_free = fb->m_next; // adjust list head to the next free block - --m_nFree; // one free block less - if (m_nMin > m_nFree) { - m_nMin = m_nFree; // remember the minimum so far - } - } - - QS_BEGIN_NOLOCK_(QS_QF_MPOOL_GET, QS::mpObj_, m_start) - QS_TIME_(); // timestamp - QS_OBJ_(m_start); // the memory managed by this pool - QS_MPC_(m_nFree); // the number of free blocks in the pool - QS_MPC_(m_nMin); // the mninimum number of free blocks in the pool - QS_END_NOLOCK_() - - QF_INT_UNLOCK_(); - return fb; // return the block or NULL pointer to the caller -} -//............................................................................ -uint32_t QF::getPoolMargin(uint8_t poolId) { - Q_REQUIRE(((uint8_t)1 <= poolId) && (poolId <= QF_maxPool_)); - - QF_INT_LOCK_KEY_ - QF_INT_LOCK_(); - uint32_t margin = (uint32_t)QF_pool_[poolId - (uint8_t)1].m_nMin; - QF_INT_UNLOCK_(); - - return margin; -} - -// "qmp_init.cpp" ============================================================ -void QMPool::init(void *poolSto, uint32_t poolSize, QMPoolSize blockSize) { - // 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((poolSto != (void *)0) - && (poolSize >= (uint32_t)sizeof(QFreeBlock)) - && ((QMPoolSize)(blockSize + (QMPoolSize)sizeof(QFreeBlock)) - > blockSize)); - - m_free = poolSto; - - // round up the blockSize to fit an integer number of pointers - m_blockSize = (QMPoolSize)sizeof(QFreeBlock); // start with just one - uint32_t nblocks = (uint32_t)1;// # free blocks that fit in a memory block - while (m_blockSize < blockSize) { - m_blockSize += (QMPoolSize)sizeof(QFreeBlock); - ++nblocks; - } - blockSize = m_blockSize; // use the rounded-up value from here on - - // the whole pool buffer must fit at least one rounded-up block - Q_ASSERT(poolSize >= (uint32_t)blockSize); - - // chain all blocks together in a free-list... - poolSize -= (uint32_t)blockSize; // don't chain the last block - m_nTot = (QMPoolCtr)1; // one (the last) block in the pool - QFreeBlock *fb = (QFreeBlock *)m_free;//start at the head of the free list - while (poolSize >= (uint32_t)blockSize) { - fb->m_next = &fb[nblocks]; // setup the next link - fb = fb->m_next; // advance to next block - poolSize -= (uint32_t)blockSize; // reduce the available pool size - ++m_nTot; // increment the number of blocks so far - } - - fb->m_next = (QFreeBlock *)0; // 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 - - QS_INT_LOCK_KEY_ - QS_BEGIN_(QS_QF_MPOOL_INIT, QS::mpObj_, m_start) - QS_OBJ_(m_start); // the memory managed by this pool - QS_MPC_(m_nTot); // the total number of blocks - QS_END_() -} - -// "qmp_put.cpp" ============================================================= -void QMPool::put(void *b) { - //lint -e946 -e1904 ignore MISRA Rule 103 in this precondition - Q_REQUIRE((m_start <= b) && (b <= m_end) /* must be in range */ - && (m_nFree <= m_nTot)); // # free blocks must be < total - - QF_INT_LOCK_KEY_ - QF_INT_LOCK_(); - - ((QFreeBlock *)b)->m_next = (QFreeBlock *)m_free;//link into the free list - m_free = b; // set as new head of the free list - ++m_nFree; // one more free block in this pool - - QS_BEGIN_NOLOCK_(QS_QF_MPOOL_PUT, QS::mpObj_, m_start) - QS_TIME_(); // timestamp - QS_OBJ_(m_start); // the memory managed by this pool - QS_MPC_(m_nFree); // the number of free blocks in the pool - QS_END_NOLOCK_() - - QF_INT_UNLOCK_(); -} - -// "qte_arm.cpp" ============================================================= -// Package-scope objects ----------------------------------------------------- -QTimeEvt *QF_timeEvtListHead_; // head of linked list of time events - -//............................................................................ -void QTimeEvt::arm_(QActive *act, QTimeEvtCtr nTicks) { - Q_REQUIRE((nTicks > (QTimeEvtCtr)0) /* cannot arm with 0 ticks */ - && (sig >= (QSignal)Q_USER_SIG) /* valid signal */ - && (m_prev == (QTimeEvt *)0) /* time event must NOT be used */ - && (act != (QActive *)0)); /* active object must be provided */ - m_ctr = nTicks; - m_prev = this; // mark the timer in use - m_act = act; - QF_INT_LOCK_KEY_ - QF_INT_LOCK_(); - - QS_BEGIN_NOLOCK_(QS_QF_TIMEEVT_ARM, QS::teObj_, this) - QS_TIME_(); // timestamp - QS_OBJ_(this); // this time event object - QS_OBJ_(act); // the active object - QS_TEC_(nTicks); // the number of ticks - QS_TEC_(m_interval); // the interval - QS_END_NOLOCK_() - - m_next = QF_timeEvtListHead_; - if (QF_timeEvtListHead_ != (QTimeEvt *)0) { - QF_timeEvtListHead_->m_prev = this; - } - QF_timeEvtListHead_ = this; - QF_INT_UNLOCK_(); -} - -// "qte_ctor.cpp" ============================================================ -QTimeEvt::QTimeEvt(QSignal s) - : -#ifdef Q_EVT_CTOR - QEvent(s), -#endif - m_prev((QTimeEvt *)0), - m_next((QTimeEvt *)0), - m_act((QActive *)0), - m_ctr((QTimeEvtCtr)0), - m_interval((QTimeEvtCtr)0) -{ - Q_REQUIRE(s >= (QSignal)Q_USER_SIG); // valid signal - sig = s; - EVT_POOL_ID(this) = (uint8_t)0; // time event must be static, see NOTE01 -} - -// "qte_ctr.cpp" ============================================================= -QTimeEvtCtr QTimeEvt::ctr(void) { - QTimeEvtCtr ctr; - QF_INT_LOCK_KEY_ - QF_INT_LOCK_(); - if (m_prev != (QTimeEvt *)0) { // is the time event actually armed? - ctr = m_ctr; - } - else { // the time event was not armed - ctr = (QTimeEvtCtr)0; - } - - QS_BEGIN_NOLOCK_(QS_QF_TIMEEVT_CTR, QS::teObj_, this) - QS_TIME_(); // timestamp - QS_OBJ_(this); // this time event object - QS_OBJ_(m_act); // the active object - QS_TEC_(ctr); // the current counter - QS_TEC_(m_interval); // the interval - QS_END_NOLOCK_() - - QF_INT_UNLOCK_(); - return ctr; -} - -// "qte_darm.cpp" ============================================================ -// NOTE: disarm a time evt (no harm in disarming an already disarmed time evt) -uint8_t QTimeEvt::disarm(void) { - uint8_t wasArmed; - QF_INT_LOCK_KEY_ - QF_INT_LOCK_(); - if (m_prev != (QTimeEvt *)0) { // is the time event actually armed? - wasArmed = (uint8_t)1; - if (this == QF_timeEvtListHead_) { - QF_timeEvtListHead_ = m_next; - } - else { - if (m_next != (QTimeEvt *)0) { // not the last in the list? - m_next->m_prev = m_prev; - } - m_prev->m_next = m_next; - } - m_prev = (QTimeEvt *)0; // mark the time event as disarmed - - QS_BEGIN_NOLOCK_(QS_QF_TIMEEVT_DISARM, QS::teObj_, this) - QS_TIME_(); // timestamp - QS_OBJ_(this); // this time event object - QS_OBJ_(m_act); // the active object - QS_TEC_(m_ctr); // the number of ticks - QS_TEC_(m_interval); // the interval - QS_END_NOLOCK_() - } - else { // the time event was not armed - wasArmed = (uint8_t)0; - - QS_BEGIN_NOLOCK_(QS_QF_TIMEEVT_DISARM_ATTEMPT, QS::teObj_, this) - QS_TIME_(); // timestamp - QS_OBJ_(this); // this time event object - QS_OBJ_(m_act); // the active object - QS_END_NOLOCK_() - } - QF_INT_UNLOCK_(); - return wasArmed; -} - -// "qte_rarm.cpp" ============================================================ -uint8_t QTimeEvt::rearm(QTimeEvtCtr nTicks) { - Q_REQUIRE((nTicks > (QTimeEvtCtr)0) /* cannot rearm with 0 ticks */ - && (sig >= (QSignal)Q_USER_SIG) /* valid signal */ - && (m_act != (QActive *)0)); // valid active object - uint8_t isArmed; - QF_INT_LOCK_KEY_ - QF_INT_LOCK_(); - m_ctr = nTicks; - if (m_prev == (QTimeEvt *)0) { // is this time event disarmed? - isArmed = (uint8_t)0; - m_next = QF_timeEvtListHead_; - if (QF_timeEvtListHead_ != (QTimeEvt *)0) { - QF_timeEvtListHead_->m_prev = this; - } - QF_timeEvtListHead_ = this; - m_prev = this; // mark the time evt in use - } - else { // the time event is armed - isArmed = (uint8_t)1; - } - - QS_BEGIN_NOLOCK_(QS_QF_TIMEEVT_REARM, QS::teObj_, this) - QS_TIME_(); // timestamp - QS_OBJ_(this); // this time event object - QS_OBJ_(m_act); // the active object - QS_TEC_(m_ctr); // the number of ticks - QS_TEC_(m_interval); // the interval - QS_U8_(isArmed); // was the timer armed? - QS_END_NOLOCK_() - - QF_INT_UNLOCK_(); - return isArmed; -} - -////////////////////////////////////////////////////////////////////////////// -// Kernel selection based on QK_PREEMPTIVE -// -#ifdef QK_PREEMPTIVE - -// "qk_pkg.h" ================================================================ - // QK internal interrupt locking/unlocking -#ifndef QF_INT_KEY_TYPE - #define QK_INT_LOCK_KEY_ - #define QK_INT_LOCK_() QF_INT_LOCK(dummy) - #define QK_INT_UNLOCK_() QF_INT_UNLOCK(dummy) -#else - - /// \brief This is an internal macro for defining the interrupt lock key. - /// - /// The purpose of this macro is to enable writing the same code for the - /// case when interrupt key is defined and when it is not. If the macro - /// #QF_INT_KEY_TYPE is defined, this internal macro provides the - /// definition of the lock key variable. Otherwise this macro is empty. - /// \sa #QF_INT_KEY_TYPE, #QF_INT_LOCK_, #QF_INT_UNLOCK_ - #define QK_INT_LOCK_KEY_ QF_INT_KEY_TYPE intLockKey_; - - /// \brief This is an internal macro for locking interrupts. - /// - /// The purpose of this macro is to enable writing the same code for the - /// case when interrupt key is defined and when it is not. If the macro - /// #QF_INT_KEY_TYPE is defined, this internal macro invokes #QF_INT_LOCK - /// passing the key variable as the parameter. Otherwise #QF_INT_LOCK - /// is invoked with a dummy parameter. - /// \sa #QK_INT_LOCK_KEY_, #QK_INT_UNLOCK_ - #define QK_INT_LOCK_() QF_INT_LOCK(intLockKey_) - - /// \brief This is an internal macro for unlocking interrupts. - /// - /// The purpose of this macro is to enable writing the same code for the - /// case when interrupt key is defined and when it is not. If the macro - /// #QF_INT_KEY_TYPE is defined, this internal macro invokes - /// #QF_INT_UNLOCK passing the key variable as the parameter. Otherwise - /// #QF_INT_UNLOCK is invoked with a dummy parameter. - /// \sa #QK_INT_LOCK_KEY_, #QK_INT_LOCK_ - #define QK_INT_UNLOCK_() QF_INT_UNLOCK(intLockKey_) -#endif - - // package-scope objects... -#ifndef QK_NO_MUTEX - extern uint8_t volatile QK_ceilingPrio_; ///< QK mutex priority ceiling -#endif - -// "qk.cpp" ================================================================== - -// Public-scope objects ------------------------------------------------------ -#if (QF_MAX_ACTIVE <= 8U) -#ifdef Q_USE_NAMESPACE - QP::QPSet8 volatile QK_readySet_; // ready set of QK -#else - QPSet8 volatile QK_readySet_; // ready set of QK -#endif -#else -#ifdef Q_USE_NAMESPACE - QP::QPSet64 volatile QK_readySet_; // ready set of QK -#else - QPSet64 volatile QK_readySet_; // ready set of QK -#endif -#endif - -#ifdef Q_USE_NAMESPACE -} // namespace QP -#endif - // start with the QK scheduler locked -extern "C" { - -uint8_t volatile QK_currPrio_ = (uint8_t)(QF_MAX_ACTIVE + 1); -uint8_t volatile QK_intNest_; // start with nesting level of 0 - -} // extern "C" - -#ifdef Q_USE_NAMESPACE -namespace QP { -#endif - -//............................................................................ -//lint -e970 -e971 ignore MISRA rules 13 and 14 in this function -char const Q_ROM * Q_ROM_VAR QK::getVersion(void) { - static char const Q_ROM Q_ROM_VAR version[] = { - (char)(((QP_VERSION >> 12U) & 0xFU) + (uint8_t)'0'), - '.', - (char)(((QP_VERSION >> 8U) & 0xFU) + (uint8_t)'0'), - '.', - (char)(((QP_VERSION >> 4U) & 0xFU) + (uint8_t)'0'), - (char)((QP_VERSION & 0xFU) + (uint8_t)'0'), - '\0' - }; - return version; -} -//............................................................................ -void QF::init(void) { - QK_init(); // QK initialization ("C" linkage, might be assembly) -} -//............................................................................ -void QF::stop(void) { - QF::onCleanup(); // cleanup callback - // nothing else to do for the QK preemptive kernel -} -//............................................................................ -void QF::run(void) { - QK_INT_LOCK_KEY_ - - QK_INT_LOCK_(); - QK_currPrio_ = (uint8_t)0; // set the priority for the QK idle loop - QK_SCHEDULE_(); // process all events produced so far - QK_INT_UNLOCK_(); - - QF::onStartup(); // startup callback - - for (;;) { // the QK idle loop - QK::onIdle(); // invoke the QK on-idle callback - } -} -//............................................................................ -void QActive::start(uint8_t prio, - QEvent const *qSto[], uint32_t qLen, - void *tls, uint32_t flags, - QEvent const *ie) -{ - Q_REQUIRE(((uint8_t)0 < prio) && (prio <= (uint8_t)QF_MAX_ACTIVE)); - - m_eQueue.init(qSto, (QEQueueCtr)qLen); // initialize the event queue - m_prio = prio; - QF::add_(this); // make QF aware of this active object - -#if defined(QK_TLS) || defined(QK_EXT_SAVE) - m_osObject = (uint8_t)flags; // m_osObject contains the thread flags - m_thread = tls; // contains the pointer to the thread-local-storage -#else - Q_ASSERT((tls == (void *)0) && (flags == (uint32_t)0)); -#endif - - init(ie); // execute initial transition - - QS_FLUSH(); // flush the trace buffer to the host -} -//............................................................................ -void QActive::stop(void) { - QF::remove_(this); // remove this active object from the QF -} - -// "qk_sched" ================================================================ - -#ifdef Q_USE_NAMESPACE -} // namespace QP -#endif - -//............................................................................ -// NOTE: the QK scheduler is entered and exited with interrupts LOCKED. -// QK_schedule_() is extern "C", so it does not belong to the QP namespace. -// -extern "C" { - -#ifndef QF_INT_KEY_TYPE -void QK_schedule_(void) { -#else -void QK_schedule_(QF_INT_KEY_TYPE intLockKey_) { -#endif - -#ifdef Q_USE_NAMESPACE - using namespace QP; -#endif - // the QK scheduler must be called at task level only - Q_REQUIRE(QK_intNest_ == (uint8_t)0); - - // determine the priority of the highest-priority task ready to run - uint8_t p = QK_readySet_.findMax(); - -#ifdef QK_NO_MUTEX - if (p > QK_currPrio_) { // do we have a preemption? -#else // QK priority-ceiling mutexes allowed - if ((p > QK_currPrio_) && (p > QK_ceilingPrio_)) { -#endif - uint8_t pin = QK_currPrio_; // save the initial priority - QActive *a; -#ifdef QK_TLS // thread-local storage used? - uint8_t pprev = pin; -#endif - do { - QEvent const *e; - a = QF::active_[p]; // obtain the pointer to the AO - QK_currPrio_ = p; // this becomes the current task priority - -#ifdef QK_TLS // thread-local storage used? - if (p != pprev) { // are we changing threads? - QK_TLS(a); // switch new thread-local storage - pprev = p; - } -#endif - QS_BEGIN_NOLOCK_(QS_QK_SCHEDULE, QS::aoObj_, a) - QS_TIME_(); // timestamp - QS_U8_(p); // the priority of the active object - QS_U8_(pin); // the preempted priority - QS_END_NOLOCK_() - - QK_INT_UNLOCK_(); // unlock the interrupts - - e = a->get_(); // get the next event for this active object - a->dispatch(e); // dispatch e to the active object - QF::gc(e); // garbage collect the event, if necessary - - QK_INT_LOCK_(); - // determine the highest-priority AO ready to run - if (QK_readySet_.notEmpty()) { - p = QK_readySet_.findMax(); - } - else { - p = (uint8_t)0; - } -#ifdef QK_NO_MUTEX - } while (p > pin); // is the new priority higher than initial? -#else // QK priority-ceiling mutexes allowed - } while ((p > pin) && (p > QK_ceilingPrio_)); -#endif - QK_currPrio_ = pin; // restore the initial priority - -#ifdef QK_TLS // thread-local storage used? - if (pin != (uint8_t)0) { // no extended context for the idle loop - a = QF::active_[pin]; // the pointer to the preempted AO - QK_TLS(a); // restore the original TLS - } -#endif - } -} - -} // extern "C" - -#ifdef Q_USE_NAMESPACE -namespace QP { -#endif - -// "qk_mutex.cpp" ============================================================ -#ifndef QK_NO_MUTEX - -// package-scope objects ----------------------------------------------------- -uint8_t volatile QK_ceilingPrio_; // ceiling priority of a mutex - -//............................................................................ -QMutex QK::mutexLock(uint8_t prioCeiling) { - QK_INT_LOCK_KEY_ - QK_INT_LOCK_(); - uint8_t mutex = QK_ceilingPrio_; // original QK priority ceiling to return - if (QK_ceilingPrio_ < prioCeiling) { - QK_ceilingPrio_ = prioCeiling; // raise the QK priority ceiling - } - - QS_BEGIN_NOLOCK_(QS_QK_MUTEX_LOCK, (void *)0, (void *)0) - QS_TIME_(); // timestamp - QS_U8_(mutex); // the original priority - QS_U8_(QK_ceilingPrio_); // the current priority ceiling - QS_END_NOLOCK_() - - QK_INT_UNLOCK_(); - return mutex; -} -//............................................................................ -void QK::mutexUnlock(QMutex mutex) { - QK_INT_LOCK_KEY_ - QK_INT_LOCK_(); - - QS_BEGIN_NOLOCK_(QS_QK_MUTEX_UNLOCK, (void *)0, (void *)0) - QS_TIME_(); // timestamp - QS_U8_(mutex); // the original priority - QS_U8_(QK_ceilingPrio_); // the current priority ceiling - QS_END_NOLOCK_() - - if (QK_ceilingPrio_ > mutex) { - QK_ceilingPrio_ = mutex; // restore the saved priority ceiling - QK_SCHEDULE_(); - } - QK_INT_UNLOCK_(); -} -#endif // QK_NO_MUTEX - -#else // QK_PREEMPTIVE - -// "qvanilla.cpp" ============================================================ -// Package-scope objects ----------------------------------------------------- -#if (QF_MAX_ACTIVE <= 8) - QPSet8 volatile QF_readySet_; // QF-ready set of active objects -#else - QPSet64 volatile QF_readySet_; // QF-ready set of active objects -#endif - -//............................................................................ -char const Q_ROM * Q_ROM_VAR QF::getPortVersion(void) { - static const char Q_ROM version[] = "4.0.00"; - return version; -} -//............................................................................ -void QF::init(void) { - // nothing to do for the "vanilla" kernel -} -//............................................................................ -void QActive::start(uint8_t prio, - QEvent const *qSto[], uint32_t qLen, - void *stkSto, uint32_t /*lint -e1904 stkSize */, - QEvent const *ie) -{ - Q_REQUIRE(((uint8_t)0 < prio) && (prio <= (uint8_t)QF_MAX_ACTIVE) - && (stkSto == (void *)0)); // does not need per-actor stack - - m_eQueue.init(qSto, (QEQueueCtr)qLen); // initialize QEQueue - m_prio = prio; // set the QF priority of this active object - QF::add_(this); // make QF aware of this active object - init(ie); // execute initial transition - - QS_FLUSH(); // flush the trace buffer to the host -} -//............................................................................ -void QActive::stop(void) { - QF::remove_(this); -} - -//............................................................................ -void QF::stop(void) { - QF::onCleanup(); // cleanup callback - // nothing else to do for the "vanilla" kernel -} -//............................................................................ -void QF::run(void) { - QF::onStartup(); // startup callback - - for (;;) { // the bacground loop - QF_INT_LOCK_KEY_ - QF_INT_LOCK_(); - if (QF_readySet_.notEmpty()) { - uint8_t p = QF_readySet_.findMax(); - QActive *a = active_[p]; - QF_INT_UNLOCK_(); - - QEvent const *e = a->get_(); // get the next event for this AO - a->dispatch(e); // dispatch evt to the HSM - gc(e); // determine if event is garbage and collect it if so - } - else { -#ifndef QF_INT_KEY_TYPE - onIdle(); // see NOTE01 -#else - onIdle(intLockKey_); // see NOTE01 -#endif // QF_INT_KEY_TYPE - } - } -} - -////////////////////////////////////////////////////////////////////////////// -// NOTE01: -// QF::onIdle() must be called with interrupts LOCKED because the -// determination of the idle condition (no events in the queues) can change -// at any time by an interrupt posting events to a queue. The QF::onIdle() -// MUST enable interrups internally, perhaps at the same time as putting the -// CPU into a power-saving mode. -// - -#endif // QK_PREEMPTIVE - -////////////////////////////////////////////////////////////////////////////// -#ifdef Q_SPY - -// "qs_pkg.h" ================================================================ -/// \brief QS ring buffer counter and offset type -typedef uint16_t QSCtr; - -/// \brief Internal QS macro to insert an un-escaped byte into -/// the QS buffer -//// -#define QS_INSERT_BYTE(b_) \ - QS_ring_[QS_head_] = (b_); \ - ++QS_head_; \ - if (QS_head_ == QS_end_) { \ - QS_head_ = (QSCtr)0; \ - } \ - ++QS_used_; - -/// \brief Internal QS macro to insert an escaped byte into the QS buffer -#define QS_INSERT_ESC_BYTE(b_) \ - QS_chksum_ = (uint8_t)(QS_chksum_ + (b_)); \ - if (((b_) == QS_FRAME) || ((b_) == QS_ESC)) { \ - QS_INSERT_BYTE(QS_ESC) \ - QS_INSERT_BYTE((uint8_t)((b_) ^ QS_ESC_XOR)) \ - } \ - else { \ - QS_INSERT_BYTE(b_) \ - } - -/// \brief Internal QS macro to insert a escaped checksum byte into -/// the QS buffer -#define QS_INSERT_CHKSUM_BYTE() \ - QS_chksum_ = (uint8_t)~QS_chksum_; \ - if ((QS_chksum_ == QS_FRAME) || (QS_chksum_ == QS_ESC)) { \ - QS_INSERT_BYTE(QS_ESC) \ - QS_INSERT_BYTE((uint8_t)(QS_chksum_ ^ QS_ESC_XOR)) \ - } \ - else { \ - QS_INSERT_BYTE(QS_chksum_) \ - } - - -/// \brief Frame character of the QS output protocol -#define QS_FRAME ((uint8_t)0x7E) - -/// \brief Escape character of the QS output protocol -#define QS_ESC ((uint8_t)0x7D) - -/// \brief Escape modifier of the QS output protocol -/// -/// The escaped byte is XOR-ed with the escape modifier before it is inserted -/// into the QS buffer. -#define QS_ESC_XOR 0x20 - -#ifndef Q_ROM_BYTE - /// \brief Macro to access a byte allocated in ROM - /// - /// Some compilers for Harvard-architecture MCUs, such as gcc for AVR, do - /// not generate correct code for accessing data allocated in the program - /// space (ROM). The workaround for such compilers is to explictly add - /// assembly code to access each data element allocated in the program - /// space. The macro Q_ROM_BYTE() retrieves a byte from the given ROM - /// address. - /// - /// The Q_ROM_BYTE() macro should be defined for the compilers that - /// cannot handle correctly data allocated in ROM (such as the gcc). - /// If the macro is left undefined, the default definition simply returns - /// the argument and lets the compiler generate the correct code. - #define Q_ROM_BYTE(rom_var_) (rom_var_) -#endif - -//............................................................................ -extern uint8_t *QS_ring_; ///< pointer to the start of the ring buffer -extern QSCtr QS_end_; ///< offset of the end of the ring buffer -extern QSCtr QS_head_; ///< offset to where next byte will be inserted -extern QSCtr QS_tail_; ///< offset of where next event will be extracted -extern QSCtr QS_used_; ///< number of bytes currently in the ring buffer -extern uint8_t QS_seq_; ///< the record sequence number -extern uint8_t QS_chksum_; ///< the checksum of the current record -extern uint8_t QS_full_; ///< the ring buffer is temporarily full - -// "qs.cpp" ================================================================== -//............................................................................ -uint8_t QS::glbFilter_[32]; // global QS filter - -//............................................................................ -uint8_t *QS_ring_; // pointer to the start of the ring buffer -QSCtr QS_end_; // offset of the end of the ring buffer -QSCtr QS_head_; // offset to where next byte will be inserted -QSCtr QS_tail_; // offset of where next byte will be extracted -QSCtr QS_used_; // number of bytes currently in the ring buffer -uint8_t QS_seq_; // the record sequence number -uint8_t QS_chksum_; // the checksum of the current record -uint8_t QS_full_; // the ring buffer is temporarily full - -//............................................................................ -//lint -e970 -e971 ignore MISRA rules 13 and 14 in this function -char const Q_ROM * Q_ROM_VAR QS::getVersion(void) { - static char const Q_ROM Q_ROM_VAR version[] = { - (char)(((QP_VERSION >> 12U) & 0xFU) + (uint8_t)'0'), - '.', - (char)(((QP_VERSION >> 8U) & 0xFU) + (uint8_t)'0'), - '.', - (char)(((QP_VERSION >> 4U) & 0xFU) + (uint8_t)'0'), - (char)((QP_VERSION & 0xFU) + (uint8_t)'0'), - '\0' - }; - return version; -} -//............................................................................ -void QS::initBuf(uint8_t sto[], uint32_t stoSize) { - QS_ring_ = &sto[0]; - QS_end_ = (QSCtr)stoSize; -} -//............................................................................ -void QS::filterOn(uint8_t rec) { - if (rec == QS_ALL_RECORDS) { - uint8_t i; - for (i = (uint8_t)0; i < (uint8_t)sizeof(glbFilter_); ++i) { - glbFilter_[i] = (uint8_t)0xFF; - } - } - else { - glbFilter_[rec >> 3] |= (uint8_t)(1U << (rec & 0x07)); - } -} -//............................................................................ -void QS::filterOff(uint8_t rec) { - if (rec == QS_ALL_RECORDS) { - uint8_t i; - for (i = (uint8_t)0; i < (uint8_t)sizeof(glbFilter_); ++i) { - glbFilter_[i] = (uint8_t)0; - } - } - else { - glbFilter_[rec >> 3] &= (uint8_t)(~(1U << (rec & 0x07))); - } -} -//............................................................................ -void QS::begin(uint8_t rec) { - QS_chksum_ = (uint8_t)0; // clear the checksum - ++QS_seq_; // always increment the sequence number - QS_INSERT_ESC_BYTE(QS_seq_) // store the sequence number - QS_INSERT_ESC_BYTE(rec) // store the record ID -} -//............................................................................ -void QS::end(void) { - QS_INSERT_CHKSUM_BYTE() - QS_INSERT_BYTE(QS_FRAME) - if (QS_used_ > QS_end_) { // overrun over the old data? - QS_tail_ = QS_head_; // shift the tail to the old data - QS_used_ = QS_end_; // the whole buffer is used - } -} -//............................................................................ -void QS::u8(uint8_t format, uint8_t d) { - QS_INSERT_ESC_BYTE(format) - QS_INSERT_ESC_BYTE(d) -} -//............................................................................ -void QS::u16(uint8_t format, uint16_t d) { - QS_INSERT_ESC_BYTE(format) - QS_INSERT_ESC_BYTE((uint8_t)d) - d >>= 8; - QS_INSERT_ESC_BYTE((uint8_t)d) -} -//............................................................................ -void QS::u32(uint8_t format, uint32_t d) { - QS_INSERT_ESC_BYTE(format) - QS_INSERT_ESC_BYTE((uint8_t)d) - d >>= 8; - QS_INSERT_ESC_BYTE((uint8_t)d) - d >>= 8; - QS_INSERT_ESC_BYTE((uint8_t)d) - d >>= 8; - QS_INSERT_ESC_BYTE((uint8_t)d) -} - -// "qs_.cpp" ================================================================= -//............................................................................ -void const *QS::smObj_; // local state machine for QEP filter -void const *QS::aoObj_; // local active object for QF filter -void const *QS::mpObj_; // local event pool for QF filter -void const *QS::eqObj_; // local raw queue for QF filter -void const *QS::teObj_; // local time event for QF filter -void const *QS::apObj_; // local object Application filter - -QSTimeCtr volatile QS::tickCtr_; // tick counter for the QS_QF_TICK record - -//............................................................................ -void QS::u8_(uint8_t d) { - QS_INSERT_ESC_BYTE(d) -} -//............................................................................ -void QS::u16_(uint16_t d) { - QS_INSERT_ESC_BYTE((uint8_t)d) - d >>= 8; - QS_INSERT_ESC_BYTE((uint8_t)d) -} -//............................................................................ -void QS::u32_(uint32_t d) { - QS_INSERT_ESC_BYTE((uint8_t)d) - d >>= 8; - QS_INSERT_ESC_BYTE((uint8_t)d) - d >>= 8; - QS_INSERT_ESC_BYTE((uint8_t)d) - d >>= 8; - QS_INSERT_ESC_BYTE((uint8_t)d) -} -//............................................................................ -//lint -e970 -e971 ignore MISRA rules 13 and 14 in this function -void QS::str_(char const *s) { - while (*s != '\0') { - // ASCII characters don't need escaping - QS_chksum_ = (uint8_t)(QS_chksum_ + (uint8_t)(*s)); - QS_INSERT_BYTE((uint8_t)(*s)) - ++s; - } - QS_INSERT_BYTE((uint8_t)0) -} -//............................................................................ -//lint -e970 -e971 ignore MISRA rules 13 and 14 in this function -void QS::str_ROM_(char const Q_ROM * Q_ROM_VAR s) { - uint8_t b; - while ((b = (uint8_t)Q_ROM_BYTE(*s)) != (uint8_t)0) { - // ASCII characters don't need escaping - QS_chksum_ = (uint8_t)(QS_chksum_ + b); - QS_INSERT_BYTE(b) - ++s; - } - QS_INSERT_BYTE((uint8_t)0) -} - -// "qs_blk.cpp" ============================================================== -//............................................................................ -// get up to *pn bytes of contiguous memory -uint8_t const *QS::getBlock(uint16_t *pNbytes) { - uint8_t *block; - if (QS_used_ == (QSCtr)0) { - *pNbytes = (uint16_t)0; - block = (uint8_t *)0; // no bytes to return right now - } - else { - QSCtr n = (QSCtr)(QS_end_ - QS_tail_); - if (n > QS_used_) { - n = QS_used_; - } - if (n > (QSCtr)(*pNbytes)) { - n = (QSCtr)(*pNbytes); - } - *pNbytes = (uint16_t)n; - QS_used_ = (QSCtr)(QS_used_ - n); - QSCtr t = QS_tail_; - QS_tail_ = (QSCtr)(QS_tail_ + n); - if (QS_tail_ == QS_end_) { - QS_tail_ = (QSCtr)0; - } - block = &QS_ring_[t]; - } - return block; -} - -// "qs_byte.cpp" ============================================================= -//............................................................................ -uint16_t QS::getByte(void) { - uint16_t ret; - if (QS_used_ == (QSCtr)0) { - ret = QS_EOD; // set End-Of-Data - } - else { - ret = QS_ring_[QS_tail_]; // set the byte to return - ++QS_tail_; // advance the tail - if (QS_tail_ == QS_end_) { // tail wrap around? - QS_tail_ = (QSCtr)0; - } - --QS_used_; // one less byte used - } - return ret; // return the byte or EOD -} - -// "qs_f32.cpp" ============================================================== -//............................................................................ -void QS::f32(uint8_t format, float f) { - union F32Rep { - float f; - uint32_t u; - } fu32; - fu32.f = f; - - QS_INSERT_ESC_BYTE(format) - QS_INSERT_ESC_BYTE((uint8_t)fu32.u) - fu32.u >>= 8; - QS_INSERT_ESC_BYTE((uint8_t)fu32.u) - fu32.u >>= 8; - QS_INSERT_ESC_BYTE((uint8_t)fu32.u) - fu32.u >>= 8; - QS_INSERT_ESC_BYTE((uint8_t)fu32.u) -} - -// "qs_f64.cpp" ============================================================== -//............................................................................ -void QS::f64(uint8_t format, double d) { - union F64Rep { - double d; - struct UInt2 { - uint32_t u1, u2; - } i; - } fu64; - fu64.d = d; - - QS_INSERT_ESC_BYTE(format) - - QS_INSERT_ESC_BYTE((uint8_t)fu64.i.u1) - fu64.i.u1 >>= 8; - QS_INSERT_ESC_BYTE((uint8_t)fu64.i.u1) - fu64.i.u1 >>= 8; - QS_INSERT_ESC_BYTE((uint8_t)fu64.i.u1) - fu64.i.u1 >>= 8; - QS_INSERT_ESC_BYTE((uint8_t)fu64.i.u1) - - QS_INSERT_ESC_BYTE((uint8_t)fu64.i.u2) - fu64.i.u2 >>= 8; - QS_INSERT_ESC_BYTE((uint8_t)fu64.i.u2) - fu64.i.u2 >>= 8; - QS_INSERT_ESC_BYTE((uint8_t)fu64.i.u2) - fu64.i.u2 >>= 8; - QS_INSERT_ESC_BYTE((uint8_t)fu64.i.u2) -} - -// "qs_mem.cpp" ============================================================== -//............................................................................ -void QS::mem(uint8_t const *blk, uint8_t size) { - QS_INSERT_BYTE((uint8_t)QS_MEM_T) - QS_chksum_ = (uint8_t)(QS_chksum_ + (uint8_t)QS_MEM_T); - QS_INSERT_ESC_BYTE(size) - while (size != (uint8_t)0) { - QS_INSERT_ESC_BYTE(*blk) - ++blk; - --size; - } -} - -// "qs_str.cpp" ============================================================== -//............................................................................ -//lint -e970 -e971 ignore MISRA rules 13 and 14 in this function -void QS::str(char const *s) { - QS_INSERT_BYTE((uint8_t)QS_STR_T) - QS_chksum_ = (uint8_t)(QS_chksum_ + (uint8_t)QS_STR_T); - while ((*s) != '\0') { - // ASCII characters don't need escaping - QS_INSERT_BYTE((uint8_t)(*s)) - QS_chksum_ = (uint8_t)(QS_chksum_ + (uint8_t)(*s)); - ++s; - } - QS_INSERT_BYTE((uint8_t)0) -} -//............................................................................ -//lint -e970 -e971 ignore MISRA rules 13 and 14 in this function -void QS::str_ROM(char const Q_ROM * Q_ROM_VAR s) { - QS_INSERT_BYTE((uint8_t)QS_STR_T) - QS_chksum_ = (uint8_t)(QS_chksum_ + (uint8_t)QS_STR_T); - uint8_t b; - while ((b = (uint8_t)Q_ROM_BYTE(*s)) != (uint8_t)0) { - // ASCII characters don't need escaping - QS_INSERT_BYTE(b) - QS_chksum_ = (uint8_t)(QS_chksum_ + b); - ++s; - } - QS_INSERT_BYTE((uint8_t)0) -} - -// "qs_u64.cpp" ============================================================== -#if (QS_OBJ_PTR_SIZE == 8) || (QS_FUN_PTR_SIZE == 8) - -//............................................................................ -void QS::u64_(uint64_t d) { - QS_INSERT_ESC_BYTE((uint8_t)d) - d >>= 8; - QS_INSERT_ESC_BYTE((uint8_t)d) - d >>= 8; - QS_INSERT_ESC_BYTE((uint8_t)d) - d >>= 8; - QS_INSERT_ESC_BYTE((uint8_t)d) - d >>= 8; - QS_INSERT_ESC_BYTE((uint8_t)d) - d >>= 8; - QS_INSERT_ESC_BYTE((uint8_t)d) - d >>= 8; - QS_INSERT_ESC_BYTE((uint8_t)d) - d >>= 8; - QS_INSERT_ESC_BYTE((uint8_t)d) -} -//............................................................................ -void QS::u64(uint8_t format, uint64_t d) { - QS_INSERT_ESC_BYTE(format) - u64_(d); -} - -#endif - -#endif // Q_SPY - -#ifdef Q_USE_NAMESPACE -} // namespace QP -#endif - +////////////////////////////////////////////////////////////////////////////// +// Product: QP/C++ +// Last Updated for QP ver: 4.5.02 (modified to fit in one file) +// Date of the Last Update: Aug 24, 2012 +// +// Q u a n t u m L e a P s +// --------------------------- +// innovating embedded systems +// +// Copyright (C) 2002-2012 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 2 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 <http://www.gnu.org/licenses/>. +// +// Contact information: +// Quantum Leaps Web sites: http://www.quantum-leaps.com +// http://www.state-machine.com +// e-mail: info@quantum-leaps.com +////////////////////////////////////////////////////////////////////////////// +#include "qp_port.h" // QP port + +// "qep_pkg.h" =============================================================== +QP_BEGIN_ + +Q_DEFINE_THIS_MODULE("qp") + +////////////////////////////////////////////////////////////////////////////// +/// preallocated reserved events +extern QEvt const QEP_reservedEvt_[]; + +/// empty signal for internal use only +QSignal const QEP_EMPTY_SIG_ = static_cast<QSignal>(0); + +/// maximum depth of state nesting (including the top level), must be >= 3 +int8_t const QEP_MAX_NEST_DEPTH_ = static_cast<int8_t>(6); + +uint8_t const u8_0 = static_cast<uint8_t>(0); ///< \brief constant (uint8_t)0 +uint8_t const u8_1 = static_cast<uint8_t>(1); ///< \brief constant (uint8_t)1 +int8_t const s8_0 = static_cast<int8_t>(0); ///< \brief constant (int8_t)0 +int8_t const s8_1 = static_cast<int8_t>(1); ///< \brief constant (int8_t)1 +int8_t const s8_n1 = static_cast<int8_t>(-1); ///< \brief constant (int8_t)-1 + +QP_END_ + +/// helper macro to trigger internal event in an HSM +#define QEP_TRIG_(state_, sig_) \ + ((*(state_))(this, &QEP_reservedEvt_[sig_])) + +/// helper macro to trigger exit action in an HSM +#define QEP_EXIT_(state_) do { \ + if (QEP_TRIG_(state_, Q_EXIT_SIG) == Q_RET_HANDLED) { \ + QS_BEGIN_(QS_QEP_STATE_EXIT, QS::smObj_, this) \ + QS_OBJ_(this); \ + QS_FUN_(state_); \ + QS_END_() \ + } \ +} while (false) + +/// helper macro to trigger entry action in an HSM +#define QEP_ENTER_(state_) do { \ + if (QEP_TRIG_(state_, Q_ENTRY_SIG) == Q_RET_HANDLED) { \ + QS_BEGIN_(QS_QEP_STATE_ENTRY, QS::smObj_, this) \ + QS_OBJ_(this); \ + QS_FUN_(state_); \ + QS_END_() \ + } \ +} while (false) + +// "qep.cpp" ================================================================= +/// \brief ::QEP_reservedEvt_ definition and QEP::getVersion() implementation. + +QP_BEGIN_ + +// Package-scope objects ----------------------------------------------------- +QEvt const QEP_reservedEvt_[] = { +#ifdef Q_EVT_CTOR // Is the QEvt constructor provided? + static_cast<QSignal>(0), + static_cast<QSignal>(1), + static_cast<QSignal>(2), + static_cast<QSignal>(3) +#else // QEvt is a POD (Plain Old Datatype) + { static_cast<QSignal>(0), u8_0, u8_0 }, + { static_cast<QSignal>(1), u8_0, u8_0 }, + { static_cast<QSignal>(2), u8_0, u8_0 }, + { static_cast<QSignal>(3), u8_0, u8_0 } +#endif +}; +//............................................................................ +char_t const Q_ROM * Q_ROM_VAR QEP::getVersion(void) { + uint8_t const u8_zero = static_cast<uint8_t>('0'); + static char_t const Q_ROM Q_ROM_VAR version[] = { + static_cast<char_t>(((QP_VERSION >> 12) & 0xFU) + u8_zero), + static_cast<char_t>('.'), + static_cast<char_t>(((QP_VERSION >> 8) & 0xFU) + u8_zero), + static_cast<char_t>('.'), + static_cast<char_t>(((QP_VERSION >> 4) & 0xFU) + u8_zero), + static_cast<char_t>((QP_VERSION & 0xFU) + u8_zero), + static_cast<char_t>('\0') + }; + return version; +} + +QP_END_ + +// "qhsm_top.cpp" ============================================================ +/// \brief QHsm::top() implementation. + +QP_BEGIN_ + +//............................................................................ +QState QHsm::top(void * const, QEvt const * const) { + return Q_RET_IGNORED; // the top state ignores all events +} + +QP_END_ + +// "qhsm_ini.cpp" ============================================================ +/// \brief QHsm::init() implementation. + +QP_BEGIN_ + +//............................................................................ +QHsm::~QHsm() { +} +//............................................................................ +void QHsm::init(QEvt const * const e) { + QStateHandler t = m_state; + + Q_REQUIRE((m_temp != Q_STATE_CAST(0)) // ctor must be executed + && (t == Q_STATE_CAST(&QHsm::top))); // initial tran. NOT taken + + // the top-most initial transition must be taken + Q_ALLEGE((*m_temp)(this, e) == Q_RET_TRAN); + + QS_CRIT_STAT_ + do { // drill into the target... + QStateHandler path[QEP_MAX_NEST_DEPTH_]; + int8_t ip = s8_0; // transition entry path index + + QS_BEGIN_(QS_QEP_STATE_INIT, QS::smObj_, this) + QS_OBJ_(this); // this state machine object + QS_FUN_(t); // the source state + QS_FUN_(m_temp); // the target of the initial transition + QS_END_() + + path[0] = m_temp; + (void)QEP_TRIG_(m_temp, QEP_EMPTY_SIG_); + while (m_temp != t) { + ++ip; + path[ip] = m_temp; + (void)QEP_TRIG_(m_temp, QEP_EMPTY_SIG_); + } + m_temp = path[0]; + // entry path must not overflow + Q_ASSERT(ip < QEP_MAX_NEST_DEPTH_); + + do { // retrace the entry path in reverse (desired) order... + QEP_ENTER_(path[ip]); // enter path[ip] + --ip; + } while (ip >= s8_0); + + t = path[0]; // current state becomes the new source + } while (QEP_TRIG_(t, Q_INIT_SIG) == Q_RET_TRAN); + + QS_BEGIN_(QS_QEP_INIT_TRAN, QS::smObj_, this) + QS_TIME_(); // time stamp + QS_OBJ_(this); // this state machine object + QS_FUN_(t); // the new active state + QS_END_() + + m_state = t; // change the current active state + m_temp = t; // mark the configuration as stable +} + +QP_END_ + +// "qhsm_dis.cpp" ============================================================ +/// \brief QHsm::dispatch() implementation. + +QP_BEGIN_ + +//............................................................................ +void QHsm::dispatch(QEvt const * const e) { + QStateHandler t = m_state; + + Q_REQUIRE(t == m_temp); // the state configuration must be stable + + QStateHandler s; + QState r; + QS_CRIT_STAT_ + + QS_BEGIN_(QS_QEP_DISPATCH, QS::smObj_, this) + QS_TIME_(); // time stamp + QS_SIG_(e->sig); // the signal of the event + QS_OBJ_(this); // this state machine object + QS_FUN_(t); // the current state + QS_END_() + + do { // process the event hierarchically... + s = m_temp; + r = (*s)(this, e); // invoke state handler s + + if (r == Q_RET_UNHANDLED) { // unhandled due to a guard? + + QS_BEGIN_(QS_QEP_UNHANDLED, QS::smObj_, this) + QS_SIG_(e->sig); // the signal of the event + QS_OBJ_(this); // this state machine object + QS_FUN_(s); // the current state + QS_END_() + + r = QEP_TRIG_(s, QEP_EMPTY_SIG_); // find superstate of s + } + } while (r == Q_RET_SUPER); + + if (r == Q_RET_TRAN) { // transition taken? + QStateHandler path[QEP_MAX_NEST_DEPTH_]; + int8_t ip = s8_n1; // transition entry path index + int8_t iq; // helper transition entry path index +#ifdef Q_SPY + QStateHandler src = s; // save the transition source for tracing +#endif + + path[0] = m_temp; // save the target of the transition + path[1] = t; + + while (t != s) { // exit current state to transition source s... + if (QEP_TRIG_(t, Q_EXIT_SIG) == Q_RET_HANDLED) { //exit handled? + QS_BEGIN_(QS_QEP_STATE_EXIT, QS::smObj_, this) + QS_OBJ_(this); // this state machine object + QS_FUN_(t); // the exited state + QS_END_() + + (void)QEP_TRIG_(t, QEP_EMPTY_SIG_); // find superstate of t + } + t = m_temp; // m_temp holds the superstate + } + + t = path[0]; // target of the transition + + if (s == t) { // (a) check source==target (transition to self) + QEP_EXIT_(s); // exit the source + ip = s8_0; // enter the target + } + else { + (void)QEP_TRIG_(t, QEP_EMPTY_SIG_); // superstate of target + t = m_temp; + if (s == t) { // (b) check source==target->super + ip = s8_0; // enter the target + } + else { + (void)QEP_TRIG_(s, QEP_EMPTY_SIG_); // superstate of src + // (c) check source->super==target->super + if (m_temp == t) { + QEP_EXIT_(s); // exit the source + ip = s8_0; // enter the target + } + else { + // (d) check source->super==target + if (m_temp == path[0]) { + QEP_EXIT_(s); // exit the source + } + else { // (e) check rest of source==target->super->super.. + // and store the entry path along the way + // + iq = s8_0; // indicate LCA not found + ip = s8_1; // enter target's superst + path[1] = t; // save the superstate of target + t = m_temp; // save source->super + // find target->super->super + r = QEP_TRIG_(path[1], QEP_EMPTY_SIG_); + while (r == Q_RET_SUPER) { + ++ip; + path[ip] = m_temp; // store the entry path + if (m_temp == s) { // is it the source? + // indicate that LCA found + iq = s8_1; + // entry path must not overflow + Q_ASSERT(ip < QEP_MAX_NEST_DEPTH_); + --ip; // do not enter the source + r = Q_RET_HANDLED; // terminate the loop + } + else { // it is not the source, keep going up + r = QEP_TRIG_(m_temp, QEP_EMPTY_SIG_); + } + } + if (iq == s8_0) { // LCA found yet? + + // entry path must not overflow + Q_ASSERT(ip < QEP_MAX_NEST_DEPTH_); + + QEP_EXIT_(s); // exit the source + + // (f) check the rest of source->super + // == target->super->super... + // + iq = ip; + r = Q_RET_IGNORED; // indicate LCA NOT found + do { + if (t == path[iq]) { // is this the LCA? + r = Q_RET_HANDLED; // indicate LCA found + // do not enter LCA + ip = static_cast<int8_t>(iq - s8_1); + // terminate the loop + iq = s8_n1; + } + else { + --iq; // try lower superstate of target + } + } while (iq >= s8_0); + + if (r != Q_RET_HANDLED) { // LCA not found yet? + // (g) check each source->super->... + // for each target->super... + // + r = Q_RET_IGNORED; // keep looping + do { + // exit t unhandled? + if (QEP_TRIG_(t, Q_EXIT_SIG) + == Q_RET_HANDLED) + { + QS_BEGIN_(QS_QEP_STATE_EXIT, + QS::smObj_, this) + QS_OBJ_(this); + QS_FUN_(t); + QS_END_() + + (void)QEP_TRIG_(t, QEP_EMPTY_SIG_); + } + t = m_temp; // set to super of t + iq = ip; + do { + if (t == path[iq]) { // is this LCA? + // do not enter LCA + ip = static_cast<int8_t>(iq-s8_1); + // break out of the inner loop + iq = s8_n1; + r = Q_RET_HANDLED; // break outer + } + else { + --iq; + } + } while (iq >= s8_0); + } while (r != Q_RET_HANDLED); + } + } + } + } + } + } + // retrace the entry path in reverse (desired) order... + for (; ip >= s8_0; --ip) { + QEP_ENTER_(path[ip]); // enter path[ip] + } + t = path[0]; // stick the target into register + m_temp = t; // update the next state + + // drill into the target hierarchy... + while (QEP_TRIG_(t, Q_INIT_SIG) == Q_RET_TRAN) { + + QS_BEGIN_(QS_QEP_STATE_INIT, QS::smObj_, this) + QS_OBJ_(this); // this state machine object + QS_FUN_(t); // the source (pseudo)state + QS_FUN_(m_temp); // the target of the transition + QS_END_() + + ip = s8_0; + path[0] = m_temp; + (void)QEP_TRIG_(m_temp, QEP_EMPTY_SIG_); // find superstate + while (m_temp != t) { + ++ip; + path[ip] = m_temp; + (void)QEP_TRIG_(m_temp, QEP_EMPTY_SIG_); // find superstate + } + m_temp = path[0]; + // entry path must not overflow + Q_ASSERT(ip < QEP_MAX_NEST_DEPTH_); + + do { // retrace the entry path in reverse (correct) order... + QEP_ENTER_(path[ip]); // enter path[ip] + --ip; + } while (ip >= s8_0); + + t = path[0]; + } + + QS_BEGIN_(QS_QEP_TRAN, QS::smObj_, this) + QS_TIME_(); // time stamp + QS_SIG_(e->sig); // the signal of the event + QS_OBJ_(this); // this state machine object + QS_FUN_(src); // the source of the transition + QS_FUN_(t); // the new active state + QS_END_() + + } + else { // transition not taken +#ifdef Q_SPY + if (r == Q_RET_HANDLED) { + + QS_BEGIN_(QS_QEP_INTERN_TRAN, QS::smObj_, this) + QS_TIME_(); // time stamp + QS_SIG_(e->sig); // the signal of the event + QS_OBJ_(this); // this state machine object + QS_FUN_(m_state); // the state that handled the event + QS_END_() + + } + else { + + QS_BEGIN_(QS_QEP_IGNORED, QS::smObj_, this) + QS_TIME_(); // time stamp + QS_SIG_(e->sig); // the signal of the event + QS_OBJ_(this); // this state machine object + QS_FUN_(m_state); // the current state + QS_END_() + + } +#endif + } + + m_state = t; // change the current active state + m_temp = t; // mark the configuration as stable +} + +QP_END_ + +// "qf_pkg.h" ================================================================ +/// \brief Internal (package scope) QF/C++ interface. + +/// \brief helper macro to cast const away from an event pointer \a e_ +#define QF_EVT_CONST_CAST_(e_) const_cast<QEvt *>(e_) + +QP_BEGIN_ + // QF-specific critical section +#ifndef QF_CRIT_STAT_TYPE + /// \brief This is an internal macro for defining the critical section + /// status type. + /// + /// The purpose of this macro is to enable writing the same code for the + /// case when critical sectgion 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 varaible. + /// Otherwise this macro is empty. + /// \sa #QF_CRIT_STAT_TYPE + /// + #define QF_CRIT_STAT_ + + /// \brief This is an internal macro for entering a critical section. + /// + /// The purpose of this macro is to enable writing the same code for the + /// case when critical sectgion 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_ENTRY_() QF_CRIT_ENTRY(dummy) + + /// \brief This is an internal macro for exiting a cricial section. + /// + /// The purpose of this macro is to enable writing the same code for the + /// case when critical sectgion 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_EXIT_() QF_CRIT_EXIT(dummy) + +#else + #define QF_CRIT_STAT_ QF_CRIT_STAT_TYPE critStat_; + #define QF_CRIT_ENTRY_() QF_CRIT_ENTRY(critStat_) + #define QF_CRIT_EXIT_() QF_CRIT_EXIT(critStat_) +#endif + +// package-scope objects ----------------------------------------------------- +extern QTimeEvt *QF_timeEvtListHead_; ///< head of linked list of time events +extern QF_EPOOL_TYPE_ QF_pool_[QF_MAX_EPOOL]; ///< allocate event pools +extern uint8_t QF_maxPool_; ///< # of initialized event pools +extern QSubscrList *QF_subscrList_; ///< the subscriber list array +extern enum_t QF_maxSignal_; ///< the maximum published signal + +//............................................................................ +/// \brief Structure representing a free block in the Native QF Memory Pool +/// \sa ::QMPool +struct QFreeBlock { + QFreeBlock *m_next; +}; + +////////////////////////////////////////////////////////////////////////////// +// internal helper inline functions + +/// \brief access to the poolId_ of an event \a e +inline uint8_t QF_EVT_POOL_ID_(QEvt const * const e) { return e->poolId_; } + +/// \brief access to the refCtr_ of an event \a e +inline uint8_t QF_EVT_REF_CTR_(QEvt const * const e) { return e->refCtr_; } + +/// \brief increment the refCtr_ of an event \a e +inline void QF_EVT_REF_CTR_INC_(QEvt const * const e) { + ++(QF_EVT_CONST_CAST_(e))->refCtr_; +} + +/// \brief decrement the refCtr_ of an event \a e +inline void QF_EVT_REF_CTR_DEC_(QEvt const * const e) { + --(QF_EVT_CONST_CAST_(e))->refCtr_; +} + +////////////////////////////////////////////////////////////////////////////// +// internal frequently used srongly-typed constants + +QTimeEvtCtr const tc_0 = static_cast<QTimeEvtCtr>(0); + +void * const null_void = static_cast<void *>(0); +QEvt const * const null_evt = static_cast<QEvt const *>(0); +QTimeEvt * const null_tevt = static_cast<QTimeEvt *>(0); +QActive * const null_act = static_cast<QActive *>(0); + +QP_END_ + +/// \brief access element at index \a i_ from the base pointer \a base_ +#define QF_PTR_AT_(base_, i_) (base_[i_]) + +////////////////////////////////////////////////////////////////////////////// +#ifdef Q_SPY // QS software tracing enabled? + + #if (QF_EQUEUE_CTR_SIZE == 1) + + /// \brief 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_(ctr_) QS::u8_(ctr_) + #elif (QF_EQUEUE_CTR_SIZE == 2) + #define QS_EQC_(ctr_) QS::u16_(ctr_) + #elif (QF_EQUEUE_CTR_SIZE == 4) + #define QS_EQC_(ctr_) QS::u32_(ctr_) + #else + #error "QF_EQUEUE_CTR_SIZE not defined" + #endif + + + #if (QF_EVENT_SIZ_SIZE == 1) + + /// \brief 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_(size_) QS::u8_(size_) + #elif (QF_EVENT_SIZ_SIZE == 2) + #define QS_EVS_(size_) QS::u16_(size_) + #elif (QF_EVENT_SIZ_SIZE == 4) + #define QS_EVS_(size_) QS::u32_(size_) + #endif + + + #if (QF_MPOOL_SIZ_SIZE == 1) + + /// \brief 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_(size_) QS::u8_(size_) + #elif (QF_MPOOL_SIZ_SIZE == 2) + #define QS_MPS_(size_) QS::u16_(size_) + #elif (QF_MPOOL_SIZ_SIZE == 4) + #define QS_MPS_(size_) QS::u32_(size_) + #endif + + #if (QF_MPOOL_CTR_SIZE == 1) + + /// \brief 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_(ctr_) QS::u8_(ctr_) + #elif (QF_MPOOL_CTR_SIZE == 2) + #define QS_MPC_(ctr_) QS::u16_(ctr_) + #elif (QF_MPOOL_CTR_SIZE == 4) + #define QS_MPC_(ctr_) QS::u32_(ctr_) + #endif + + + #if (QF_TIMEEVT_CTR_SIZE == 1) + + /// \brief 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_(ctr_) QS::u8_(ctr_) + #elif (QF_TIMEEVT_CTR_SIZE == 2) + #define QS_TEC_(ctr_) QS::u16_(ctr_) + #elif (QF_TIMEEVT_CTR_SIZE == 4) + #define QS_TEC_(ctr_) QS::u32_(ctr_) + #endif + +#endif // Q_SPY + +// "qa_defer.cpp" ============================================================ +/// \brief QActive::defer() and QActive::recall() implementation. +/// + +QP_BEGIN_ + +//............................................................................ +void QActive::defer(QEQueue * const eq, QEvt const * const e) const { + eq->postFIFO(e); +} +//............................................................................ +bool QActive::recall(QEQueue * const eq) { + QEvt const * const e = eq->get(); // try to get evt from deferred queue + bool const recalled = (e != null_evt); // event available? + if (recalled) { + postLIFO(e); // post it to the front of the Active Object's queue + + QF_CRIT_STAT_ + QF_CRIT_ENTRY_(); + + if (QF_EVT_POOL_ID_(e) != u8_0) { // is it a dynamic 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(QF_EVT_REF_CTR_(e) > u8_1); + + // we need to decrement the reference counter once, to account + // for removing the event from the deferred event queue. + // + QF_EVT_REF_CTR_DEC_(e); // decrement the reference counter + } + + QF_CRIT_EXIT_(); + } + return recalled; // event not recalled +} + +QP_END_ + + +// "qa_fifo.cpp" ============================================================= +/// \brief QActive::postFIFO() implementation. +/// +/// \note this source file is only included in the QF library when the native +/// QF active object queue is used (instead of a message queue of an RTOS). + +QP_BEGIN_ + +//............................................................................ +#ifndef Q_SPY +void QActive::postFIFO(QEvt const * const e) { +#else +void QActive::postFIFO(QEvt const * const e, void const * const sender) { +#endif + + QF_CRIT_STAT_ + QF_CRIT_ENTRY_(); + + QS_BEGIN_NOCRIT_(QS_QF_ACTIVE_POST_FIFO, QS::aoObj_, this) + QS_TIME_(); // timestamp + QS_OBJ_(sender); // the sender object + QS_SIG_(e->sig); // the signal of the event + QS_OBJ_(this); // this active object + QS_U8_(QF_EVT_POOL_ID_(e)); // the pool Id of the event + QS_U8_(QF_EVT_REF_CTR_(e)); // the ref count of the event + QS_EQC_(m_eQueue.m_nFree); // number of free entries + QS_EQC_(m_eQueue.m_nMin); // min number of free entries + QS_END_NOCRIT_() + + if (QF_EVT_POOL_ID_(e) != u8_0) { // is it a dynamic event? + QF_EVT_REF_CTR_INC_(e); // increment the reference counter + } + + if (m_eQueue.m_frontEvt == null_evt) { // is the queue empty? + m_eQueue.m_frontEvt = e; // deliver event directly + QACTIVE_EQUEUE_SIGNAL_(this); // signal the event queue + } + else { // queue is not empty, leave event in the ring-buffer + // queue must accept all posted events + Q_ASSERT(m_eQueue.m_nFree != static_cast<QEQueueCtr>(0)); + // insert event pointer e into the buffer (FIFO) + QF_PTR_AT_(m_eQueue.m_ring, m_eQueue.m_head) = e; + if (m_eQueue.m_head == static_cast<QEQueueCtr>(0)) { // need to wrap? + m_eQueue.m_head = m_eQueue.m_end; // wrap around + } + --m_eQueue.m_head; + + --m_eQueue.m_nFree; // update number of free events + if (m_eQueue.m_nMin > m_eQueue.m_nFree) { + m_eQueue.m_nMin = m_eQueue.m_nFree; // update minimum so far + } + } + QF_CRIT_EXIT_(); +} + +QP_END_ + +// "qa_get_.cpp" ============================================================= +/// \brief QActive::get_() and QF::getQueueMargin() definitions. +/// +/// \note this source file is only included in the QF library when the native +/// QF active object queue is used (instead of a message queue of an RTOS). + +QP_BEGIN_ + +//............................................................................ +QEvt const *QActive::get_(void) { + QF_CRIT_STAT_ + QF_CRIT_ENTRY_(); + + QACTIVE_EQUEUE_WAIT_(this); // wait for event to arrive directly + + QEvt const *e = m_eQueue.m_frontEvt; + + if (m_eQueue.m_nFree != m_eQueue.m_end) { //any events in the ring buffer? + // remove event from the tail + m_eQueue.m_frontEvt = QF_PTR_AT_(m_eQueue.m_ring, m_eQueue.m_tail); + if (m_eQueue.m_tail == static_cast<QEQueueCtr>(0)) { // need to wrap? + m_eQueue.m_tail = m_eQueue.m_end; // wrap around + } + --m_eQueue.m_tail; + + ++m_eQueue.m_nFree; // one more free event in the ring buffer + + QS_BEGIN_NOCRIT_(QS_QF_ACTIVE_GET, QS::aoObj_, this) + QS_TIME_(); // timestamp + QS_SIG_(e->sig); // the signal of this event + QS_OBJ_(this); // this active object + QS_U8_(QF_EVT_POOL_ID_(e)); // the pool Id of the event + QS_U8_(QF_EVT_REF_CTR_(e)); // the ref count of the event + QS_EQC_(m_eQueue.m_nFree); // number of free entries + QS_END_NOCRIT_() + } + else { + m_eQueue.m_frontEvt = null_evt; // the queue becomes empty + QACTIVE_EQUEUE_ONEMPTY_(this); + + QS_BEGIN_NOCRIT_(QS_QF_ACTIVE_GET_LAST, QS::aoObj_, this) + QS_TIME_(); // timestamp + QS_SIG_(e->sig); // the signal of this event + QS_OBJ_(this); // this active object + QS_U8_(QF_EVT_POOL_ID_(e)); // the pool Id of the event + QS_U8_(QF_EVT_REF_CTR_(e)); // the ref count of the event + QS_END_NOCRIT_() + } + QF_CRIT_EXIT_(); + return e; +} +//............................................................................ +uint32_t QF::getQueueMargin(uint8_t const prio) { + Q_REQUIRE((prio <= static_cast<uint8_t>(QF_MAX_ACTIVE)) + && (active_[prio] != static_cast<QActive *>(0))); + + QF_CRIT_STAT_ + QF_CRIT_ENTRY_(); + uint32_t margin = static_cast<uint32_t>(active_[prio]->m_eQueue.m_nMin); + QF_CRIT_EXIT_(); + + return margin; +} + +QP_END_ + +// "qa_lifo.cpp" ============================================================= +/// \brief QActive::postLIFO() implementation. +/// +/// \note this source file is only included in the QF library when the native +/// QF active object queue is used (instead of a message queue of an RTOS). + +QP_BEGIN_ + +//............................................................................ +void QActive::postLIFO(QEvt const * const e) { + QF_CRIT_STAT_ + QF_CRIT_ENTRY_(); + + QS_BEGIN_NOCRIT_(QS_QF_ACTIVE_POST_LIFO, QS::aoObj_, this) + QS_TIME_(); // timestamp + QS_SIG_(e->sig); // the signal of this event + QS_OBJ_(this); // this active object + QS_U8_(QF_EVT_POOL_ID_(e)); // the pool Id of the event + QS_U8_(QF_EVT_REF_CTR_(e)); // the ref count of the event + QS_EQC_(m_eQueue.m_nFree); // number of free entries + QS_EQC_(m_eQueue.m_nMin); // min number of free entries + QS_END_NOCRIT_() + + if (QF_EVT_POOL_ID_(e) != u8_0) { // is it a dynamic event? + QF_EVT_REF_CTR_INC_(e); // increment the reference counter + } + + if (m_eQueue.m_frontEvt == null_evt) { // is the queue empty? + m_eQueue.m_frontEvt = e; // deliver event directly + QACTIVE_EQUEUE_SIGNAL_(this); // signal the event queue + } + else { // queue is not empty, leave event in the ring-buffer + // queue must accept all posted events + Q_ASSERT(m_eQueue.m_nFree != static_cast<QEQueueCtr>(0)); + + ++m_eQueue.m_tail; + if (m_eQueue.m_tail == m_eQueue.m_end) { // need to wrap the tail? + m_eQueue.m_tail = static_cast<QEQueueCtr>(0); // wrap around + } + + QF_PTR_AT_(m_eQueue.m_ring, m_eQueue.m_tail) = m_eQueue.m_frontEvt; + m_eQueue.m_frontEvt = e; // put event to front + + --m_eQueue.m_nFree; // update number of free events + if (m_eQueue.m_nMin > m_eQueue.m_nFree) { + m_eQueue.m_nMin = m_eQueue.m_nFree; // update minimum so far + } + } + QF_CRIT_EXIT_(); +} + +QP_END_ + +// "qa_sub.cpp" ============================================================== +/// \brief QActive::subscribe() implementation. + +QP_BEGIN_ + +//............................................................................ +void QActive::subscribe(enum_t const sig) const { + uint8_t p = m_prio; + Q_REQUIRE((Q_USER_SIG <= sig) + && (sig < QF_maxSignal_) + && (u8_0 < p) && (p <= static_cast<uint8_t>(QF_MAX_ACTIVE)) + && (QF::active_[p] == this)); + + uint8_t const i = Q_ROM_BYTE(QF_div8Lkup[p]); + + QF_CRIT_STAT_ + QF_CRIT_ENTRY_(); + + QS_BEGIN_NOCRIT_(QS_QF_ACTIVE_SUBSCRIBE, QS::aoObj_, this) + QS_TIME_(); // timestamp + QS_SIG_(sig); // the signal of this event + QS_OBJ_(this); // this active object + QS_END_NOCRIT_() + // set the priority bit + QF_PTR_AT_(QF_subscrList_, sig).m_bits[i] |= Q_ROM_BYTE(QF_pwr2Lkup[p]); + QF_CRIT_EXIT_(); +} + +QP_END_ + +// "qa_usub.cpp" ============================================================= +/// \brief QActive::unsubscribe() implementation. + +QP_BEGIN_ + +//............................................................................ +void QActive::unsubscribe(enum_t const sig) const { + uint8_t p = m_prio; + Q_REQUIRE((Q_USER_SIG <= sig) + && (sig < QF_maxSignal_) + && (u8_0 < p) && (p <= static_cast<uint8_t>(QF_MAX_ACTIVE)) + && (QF::active_[p] == this)); + + uint8_t const i = Q_ROM_BYTE(QF_div8Lkup[p]); + + QF_CRIT_STAT_ + QF_CRIT_ENTRY_(); + + QS_BEGIN_NOCRIT_(QS_QF_ACTIVE_UNSUBSCRIBE, QS::aoObj_, this) + QS_TIME_(); // timestamp + QS_SIG_(sig); // the signal of this event + QS_OBJ_(this); // this active object + QS_END_NOCRIT_() + // clear the priority bit + QF_PTR_AT_(QF_subscrList_,sig).m_bits[i] &= Q_ROM_BYTE(QF_invPwr2Lkup[p]); + QF_CRIT_EXIT_(); +} + +QP_END_ + +// "qa_usuba.cpp" ============================================================ +/// \brief QActive::unsubscribeAll() implementation. + +QP_BEGIN_ + +//............................................................................ +void QActive::unsubscribeAll(void) const { + uint8_t const p = m_prio; + Q_REQUIRE((u8_0 < p) && (p <= static_cast<uint8_t>(QF_MAX_ACTIVE)) + && (QF::active_[p] == this)); + + uint8_t const i = Q_ROM_BYTE(QF_div8Lkup[p]); + + enum_t sig; + for (sig = Q_USER_SIG; sig < QF_maxSignal_; ++sig) { + QF_CRIT_STAT_ + QF_CRIT_ENTRY_(); + if ((QF_PTR_AT_(QF_subscrList_, sig).m_bits[i] + & Q_ROM_BYTE(QF_pwr2Lkup[p])) != u8_0) + { + + QS_BEGIN_NOCRIT_(QS_QF_ACTIVE_UNSUBSCRIBE, QS::aoObj_, this) + QS_TIME_(); // timestamp + QS_SIG_(sig); // the signal of this event + QS_OBJ_(this); // this active object + QS_END_NOCRIT_() + // clear the priority bit + QF_PTR_AT_(QF_subscrList_, sig).m_bits[i] &= + Q_ROM_BYTE(QF_invPwr2Lkup[p]); + } + QF_CRIT_EXIT_(); + } +} + +QP_END_ + +// "qeq_fifo.cpp" ============================================================ +/// \brief QEQueue::postFIFO() implementation. + +QP_BEGIN_ + +//............................................................................ +void QEQueue::postFIFO(QEvt const * const e) { + QF_CRIT_STAT_ + QF_CRIT_ENTRY_(); + + QS_BEGIN_NOCRIT_(QS_QF_EQUEUE_POST_FIFO, QS::eqObj_, this) + QS_TIME_(); // timestamp + QS_SIG_(e->sig); // the signal of this event + QS_OBJ_(this); // this queue object + QS_U8_(QF_EVT_POOL_ID_(e)); // the pool Id of the event + QS_U8_(QF_EVT_REF_CTR_(e)); // the ref count of the event + QS_EQC_(m_nFree); // number of free entries + QS_EQC_(m_nMin); // min number of free entries + QS_END_NOCRIT_() + + if (QF_EVT_POOL_ID_(e) != u8_0) { // is it a dynamic event? + QF_EVT_REF_CTR_INC_(e); // increment the reference counter + } + + if (m_frontEvt == null_evt) { // is the queue empty? + m_frontEvt = e; // deliver event directly + } + else { // queue is not empty, leave event in the ring-buffer + // the queue must be able to accept the event (cannot overflow) + Q_ASSERT(m_nFree != static_cast<QEQueueCtr>(0)); + + QF_PTR_AT_(m_ring, m_head) = e; // insert event into the buffer (FIFO) + if (m_head == static_cast<QEQueueCtr>(0)) { // need to wrap? + m_head = m_end; // wrap around + } + --m_head; + + --m_nFree; // update number of free events + if (m_nMin > m_nFree) { + m_nMin = m_nFree; // update minimum so far + } + } + QF_CRIT_EXIT_(); +} + +QP_END_ + +// "qeq_get.cpp" ============================================================= +/// \brief QEQueue::get() implementation. + +QP_BEGIN_ + +//............................................................................ +QEvt const *QEQueue::get(void) { + QEvt const *e; + QF_CRIT_STAT_ + + QF_CRIT_ENTRY_(); + if (m_frontEvt == null_evt) { // is the queue empty? + e = null_evt; // no event available at this time + } + else { + e = m_frontEvt; + + if (m_nFree != m_end) { // any events in the the ring buffer? + m_frontEvt = QF_PTR_AT_(m_ring, m_tail); // remove from the tail + if (m_tail == static_cast<QEQueueCtr>(0)) { // need to wrap? + m_tail = m_end; // wrap around + } + --m_tail; + + ++m_nFree; // one more free event in the ring buffer + + QS_BEGIN_NOCRIT_(QS_QF_EQUEUE_GET, QS::eqObj_, this) + QS_TIME_(); // timestamp + QS_SIG_(e->sig); // the signal of this event + QS_OBJ_(this); // this queue object + QS_U8_(QF_EVT_POOL_ID_(e)); // the pool Id of the event + QS_U8_(QF_EVT_REF_CTR_(e)); // the ref count of the event + QS_EQC_(m_nFree); // number of free entries + QS_END_NOCRIT_() + } + else { + m_frontEvt = null_evt; // the queue becomes empty + + QS_BEGIN_NOCRIT_(QS_QF_EQUEUE_GET_LAST, QS::eqObj_, this) + QS_TIME_(); // timestamp + QS_SIG_(e->sig); // the signal of this event + QS_OBJ_(this); // this queue object + QS_U8_(QF_EVT_POOL_ID_(e)); // the pool Id of the event + QS_U8_(QF_EVT_REF_CTR_(e)); // the ref count of the event + QS_END_NOCRIT_() + } + } + QF_CRIT_EXIT_(); + return e; +} + +QP_END_ + +// "qeq_init.cpp" ============================================================ +/// \brief QEQueue::init() implementation. + +QP_BEGIN_ + +//............................................................................ +void QEQueue::init(QEvt const *qSto[], QEQueueCtr const qLen) { + m_frontEvt = null_evt; // no events in the queue + m_ring = &qSto[0]; + m_end = qLen; + m_head = static_cast<QEQueueCtr>(0); + m_tail = static_cast<QEQueueCtr>(0); + m_nFree = qLen; + m_nMin = qLen; + + QS_CRIT_STAT_ + QS_BEGIN_(QS_QF_EQUEUE_INIT, QS::eqObj_, this) + QS_OBJ_(qSto); // this QEQueue object + QS_EQC_(qLen); // the length of the queue + QS_END_() +} + +QP_END_ + +// "qeq_lifo.cpp" ============================================================ +/// \brief QEQueue::postLIFO() implementation. + +QP_BEGIN_ + +//............................................................................ +void QEQueue::postLIFO(QEvt const * const e) { + QF_CRIT_STAT_ + QF_CRIT_ENTRY_(); + + QS_BEGIN_NOCRIT_(QS_QF_EQUEUE_POST_LIFO, QS::eqObj_, this) + QS_TIME_(); // timestamp + QS_SIG_(e->sig); // the signal of this event + QS_OBJ_(this); // this queue object + QS_U8_(QF_EVT_POOL_ID_(e)); // the pool Id of the event + QS_U8_(QF_EVT_REF_CTR_(e)); // the ref count of the event + QS_EQC_(m_nFree); // number of free entries + QS_EQC_(m_nMin); // min number of free entries + QS_END_NOCRIT_() + + if (QF_EVT_POOL_ID_(e) != u8_0) { // is it a dynamic event? + QF_EVT_REF_CTR_INC_(e); // increment the reference counter + } + + if (m_frontEvt != null_evt) { // is the queue not empty? + // the queue must be able to accept the event (cannot overflow) + Q_ASSERT(m_nFree != static_cast<QEQueueCtr>(0)); + + ++m_tail; + if (m_tail == m_end) { // need to wrap the tail? + m_tail = static_cast<QEQueueCtr>(0); // wrap around + } + + QF_PTR_AT_(m_ring, m_tail) = m_frontEvt; // buffer the old front evt + + --m_nFree; // update number of free events + if (m_nMin > m_nFree) { + m_nMin = m_nFree; // update minimum so far + } + } + + m_frontEvt = e; // stick the new event to the front + + QF_CRIT_EXIT_(); +} + +QP_END_ + +// "qf_act.cpp" ============================================================== +/// \brief QF::active_[], QF::getVersion(), and QF::add_()/QF::remove_() +/// implementation. + +QP_BEGIN_ + +// public objects ------------------------------------------------------------ +QActive *QF::active_[QF_MAX_ACTIVE + 1]; // to be used by QF ports only +uint8_t QF_intLockNest_; // interrupt-lock nesting level + +//............................................................................ +char_t const Q_ROM * Q_ROM_VAR QF::getVersion(void) { + uint8_t const u8_zero = static_cast<uint8_t>('0'); + static char_t const Q_ROM Q_ROM_VAR version[] = { + static_cast<char_t>(((QP_VERSION >> 12) & 0xFU) + u8_zero), + static_cast<char_t>('.'), + static_cast<char_t>(((QP_VERSION >> 8) & 0xFU) + u8_zero), + static_cast<char_t>('.'), + static_cast<char_t>(((QP_VERSION >> 4) & 0xFU) + u8_zero), + static_cast<char_t>((QP_VERSION & 0xFU) + u8_zero), + static_cast<char_t>('\0') + }; + return version; +} +//............................................................................ +void QF::add_(QActive * const a) { + uint8_t p = a->m_prio; + + Q_REQUIRE((u8_0 < p) && (p <= static_cast<uint8_t>(QF_MAX_ACTIVE)) + && (active_[p] == static_cast<QActive *>(0))); + + QF_CRIT_STAT_ + QF_CRIT_ENTRY_(); + + active_[p] = a; // registger the active object at this priority + + QS_BEGIN_NOCRIT_(QS_QF_ACTIVE_ADD, QS::aoObj_, a) + QS_TIME_(); // timestamp + QS_OBJ_(a); // the active object + QS_U8_(p); // the priority of the active object + QS_END_NOCRIT_() + + QF_CRIT_EXIT_(); +} +//............................................................................ +void QF::remove_(QActive const * const a) { + uint8_t p = a->m_prio; + + Q_REQUIRE((u8_0 < p) && (p <= static_cast<uint8_t>(QF_MAX_ACTIVE)) + && (active_[p] == a)); + + QF_CRIT_STAT_ + QF_CRIT_ENTRY_(); + + active_[p] = static_cast<QActive *>(0); // free-up the priority level + + QS_BEGIN_NOCRIT_(QS_QF_ACTIVE_REMOVE, QS::aoObj_, a) + QS_TIME_(); // timestamp + QS_OBJ_(a); // the active object + QS_U8_(p); // the priority of the active object + QS_END_NOCRIT_() + + QF_CRIT_EXIT_(); +} + +QP_END_ + + +// "qf_gc.cpp" =============================================================== +/// \brief QF::gc() implementation. + +QP_BEGIN_ + +//............................................................................ +void QF::gc(QEvt const * const e) { + if (QF_EVT_POOL_ID_(e) != u8_0) { // is it a dynamic event? + QF_CRIT_STAT_ + QF_CRIT_ENTRY_(); + + if (QF_EVT_REF_CTR_(e) > u8_1) { // isn't this the last reference? + QF_EVT_REF_CTR_DEC_(e); // decrement the ref counter + + QS_BEGIN_NOCRIT_(QS_QF_GC_ATTEMPT, null_void, null_void) + QS_TIME_(); // timestamp + QS_SIG_(e->sig); // the signal of the event + QS_U8_(QF_EVT_POOL_ID_(e)); // the pool Id of the event + QS_U8_(QF_EVT_REF_CTR_(e)); // the ref count of the event + QS_END_NOCRIT_() + + QF_CRIT_EXIT_(); + } + else { // this is the last reference to this event, recycle it + uint8_t idx = static_cast<uint8_t>(QF_EVT_POOL_ID_(e) - u8_1); + + QS_BEGIN_NOCRIT_(QS_QF_GC, null_void, null_void) + QS_TIME_(); // timestamp + QS_SIG_(e->sig); // the signal of the event + QS_U8_(QF_EVT_POOL_ID_(e)); // the pool Id of the event + QS_U8_(QF_EVT_REF_CTR_(e)); // the ref count of the event + QS_END_NOCRIT_() + + QF_CRIT_EXIT_(); + + Q_ASSERT(idx < QF_maxPool_); + +#ifdef Q_EVT_VIRTUAL + QF_EVT_CONST_CAST_(e)->~QEvt(); // xtor, cast 'const' away, + // which is legitimate, because it's a pool event +#endif + // cast 'const' away, which is OK, because it's a pool event + QF_EPOOL_PUT_(QF_pool_[idx], QF_EVT_CONST_CAST_(e)); + } + } +} + +QP_END_ + +// "qf_log2.cpp" ============================================================= +/// \brief QF_log2Lkup[] implementation. + +QP_BEGIN_ + +// Global objects ------------------------------------------------------------ +uint8_t const Q_ROM Q_ROM_VAR QF_log2Lkup[256] = { + static_cast<uint8_t>(0), + static_cast<uint8_t>(1), + static_cast<uint8_t>(2), static_cast<uint8_t>(2), + static_cast<uint8_t>(3), static_cast<uint8_t>(3), static_cast<uint8_t>(3), + static_cast<uint8_t>(3), + static_cast<uint8_t>(4), static_cast<uint8_t>(4), static_cast<uint8_t>(4), + static_cast<uint8_t>(4), static_cast<uint8_t>(4), static_cast<uint8_t>(4), + static_cast<uint8_t>(4), static_cast<uint8_t>(4), + static_cast<uint8_t>(5), static_cast<uint8_t>(5), static_cast<uint8_t>(5), + static_cast<uint8_t>(5), static_cast<uint8_t>(5), static_cast<uint8_t>(5), + static_cast<uint8_t>(5), static_cast<uint8_t>(5), static_cast<uint8_t>(5), + static_cast<uint8_t>(5), static_cast<uint8_t>(5), static_cast<uint8_t>(5), + static_cast<uint8_t>(5), static_cast<uint8_t>(5), static_cast<uint8_t>(5), + static_cast<uint8_t>(5), + static_cast<uint8_t>(6), static_cast<uint8_t>(6), static_cast<uint8_t>(6), + static_cast<uint8_t>(6), static_cast<uint8_t>(6), static_cast<uint8_t>(6), + static_cast<uint8_t>(6), static_cast<uint8_t>(6), static_cast<uint8_t>(6), + static_cast<uint8_t>(6), static_cast<uint8_t>(6), static_cast<uint8_t>(6), + static_cast<uint8_t>(6), static_cast<uint8_t>(6), static_cast<uint8_t>(6), + static_cast<uint8_t>(6), static_cast<uint8_t>(6), static_cast<uint8_t>(6), + static_cast<uint8_t>(6), static_cast<uint8_t>(6), static_cast<uint8_t>(6), + static_cast<uint8_t>(6), static_cast<uint8_t>(6), static_cast<uint8_t>(6), + static_cast<uint8_t>(6), static_cast<uint8_t>(6), static_cast<uint8_t>(6), + static_cast<uint8_t>(6), static_cast<uint8_t>(6), static_cast<uint8_t>(6), + static_cast<uint8_t>(6), static_cast<uint8_t>(6), + static_cast<uint8_t>(7), static_cast<uint8_t>(7), static_cast<uint8_t>(7), + static_cast<uint8_t>(7), static_cast<uint8_t>(7), static_cast<uint8_t>(7), + static_cast<uint8_t>(7), static_cast<uint8_t>(7), static_cast<uint8_t>(7), + static_cast<uint8_t>(7), static_cast<uint8_t>(7), static_cast<uint8_t>(7), + static_cast<uint8_t>(7), static_cast<uint8_t>(7), static_cast<uint8_t>(7), + static_cast<uint8_t>(7), static_cast<uint8_t>(7), static_cast<uint8_t>(7), + static_cast<uint8_t>(7), static_cast<uint8_t>(7), static_cast<uint8_t>(7), + static_cast<uint8_t>(7), static_cast<uint8_t>(7), static_cast<uint8_t>(7), + static_cast<uint8_t>(7), static_cast<uint8_t>(7), static_cast<uint8_t>(7), + static_cast<uint8_t>(7), static_cast<uint8_t>(7), static_cast<uint8_t>(7), + static_cast<uint8_t>(7), static_cast<uint8_t>(7), static_cast<uint8_t>(7), + static_cast<uint8_t>(7), static_cast<uint8_t>(7), static_cast<uint8_t>(7), + static_cast<uint8_t>(7), static_cast<uint8_t>(7), static_cast<uint8_t>(7), + static_cast<uint8_t>(7), static_cast<uint8_t>(7), static_cast<uint8_t>(7), + static_cast<uint8_t>(7), static_cast<uint8_t>(7), static_cast<uint8_t>(7), + static_cast<uint8_t>(7), static_cast<uint8_t>(7), static_cast<uint8_t>(7), + static_cast<uint8_t>(7), static_cast<uint8_t>(7), static_cast<uint8_t>(7), + static_cast<uint8_t>(7), static_cast<uint8_t>(7), static_cast<uint8_t>(7), + static_cast<uint8_t>(7), static_cast<uint8_t>(7), static_cast<uint8_t>(7), + static_cast<uint8_t>(7), static_cast<uint8_t>(7), static_cast<uint8_t>(7), + static_cast<uint8_t>(7), static_cast<uint8_t>(7), static_cast<uint8_t>(7), + static_cast<uint8_t>(7), + static_cast<uint8_t>(8), static_cast<uint8_t>(8), static_cast<uint8_t>(8), + static_cast<uint8_t>(8), static_cast<uint8_t>(8), static_cast<uint8_t>(8), + static_cast<uint8_t>(8), static_cast<uint8_t>(8), static_cast<uint8_t>(8), + static_cast<uint8_t>(8), static_cast<uint8_t>(8), static_cast<uint8_t>(8), + static_cast<uint8_t>(8), static_cast<uint8_t>(8), static_cast<uint8_t>(8), + static_cast<uint8_t>(8), static_cast<uint8_t>(8), static_cast<uint8_t>(8), + static_cast<uint8_t>(8), static_cast<uint8_t>(8), static_cast<uint8_t>(8), + static_cast<uint8_t>(8), static_cast<uint8_t>(8), static_cast<uint8_t>(8), + static_cast<uint8_t>(8), static_cast<uint8_t>(8), static_cast<uint8_t>(8), + static_cast<uint8_t>(8), static_cast<uint8_t>(8), static_cast<uint8_t>(8), + static_cast<uint8_t>(8), static_cast<uint8_t>(8), static_cast<uint8_t>(8), + static_cast<uint8_t>(8), static_cast<uint8_t>(8), static_cast<uint8_t>(8), + static_cast<uint8_t>(8), static_cast<uint8_t>(8), static_cast<uint8_t>(8), + static_cast<uint8_t>(8), static_cast<uint8_t>(8), static_cast<uint8_t>(8), + static_cast<uint8_t>(8), static_cast<uint8_t>(8), static_cast<uint8_t>(8), + static_cast<uint8_t>(8), static_cast<uint8_t>(8), static_cast<uint8_t>(8), + static_cast<uint8_t>(8), static_cast<uint8_t>(8), static_cast<uint8_t>(8), + static_cast<uint8_t>(8), static_cast<uint8_t>(8), static_cast<uint8_t>(8), + static_cast<uint8_t>(8), static_cast<uint8_t>(8), static_cast<uint8_t>(8), + static_cast<uint8_t>(8), static_cast<uint8_t>(8), static_cast<uint8_t>(8), + static_cast<uint8_t>(8), static_cast<uint8_t>(8), static_cast<uint8_t>(8), + static_cast<uint8_t>(8), static_cast<uint8_t>(8), static_cast<uint8_t>(8), + static_cast<uint8_t>(8), static_cast<uint8_t>(8), static_cast<uint8_t>(8), + static_cast<uint8_t>(8), static_cast<uint8_t>(8), static_cast<uint8_t>(8), + static_cast<uint8_t>(8), static_cast<uint8_t>(8), static_cast<uint8_t>(8), + static_cast<uint8_t>(8), static_cast<uint8_t>(8), static_cast<uint8_t>(8), + static_cast<uint8_t>(8), static_cast<uint8_t>(8), static_cast<uint8_t>(8), + static_cast<uint8_t>(8), static_cast<uint8_t>(8), static_cast<uint8_t>(8), + static_cast<uint8_t>(8), static_cast<uint8_t>(8), static_cast<uint8_t>(8), + static_cast<uint8_t>(8), static_cast<uint8_t>(8), static_cast<uint8_t>(8), + static_cast<uint8_t>(8), static_cast<uint8_t>(8), static_cast<uint8_t>(8), + static_cast<uint8_t>(8), static_cast<uint8_t>(8), static_cast<uint8_t>(8), + static_cast<uint8_t>(8), static_cast<uint8_t>(8), static_cast<uint8_t>(8), + static_cast<uint8_t>(8), static_cast<uint8_t>(8), static_cast<uint8_t>(8), + static_cast<uint8_t>(8), static_cast<uint8_t>(8), static_cast<uint8_t>(8), + static_cast<uint8_t>(8), static_cast<uint8_t>(8), static_cast<uint8_t>(8), + static_cast<uint8_t>(8), static_cast<uint8_t>(8), static_cast<uint8_t>(8), + static_cast<uint8_t>(8), static_cast<uint8_t>(8), static_cast<uint8_t>(8), + static_cast<uint8_t>(8), static_cast<uint8_t>(8), static_cast<uint8_t>(8), + static_cast<uint8_t>(8), static_cast<uint8_t>(8), static_cast<uint8_t>(8), + static_cast<uint8_t>(8), static_cast<uint8_t>(8), static_cast<uint8_t>(8), + static_cast<uint8_t>(8), static_cast<uint8_t>(8), static_cast<uint8_t>(8), + static_cast<uint8_t>(8), static_cast<uint8_t>(8) +}; + +QP_END_ + +// "qf_new.cpp" ============================================================== +/// \brief QF::new_() implementation. + +QP_BEGIN_ + +//............................................................................ +QEvt *QF::new_(QEvtSize const evtSize, enum_t const sig) { + // find the pool id that fits the requested event size ... + uint8_t idx = u8_0; + while (evtSize + > static_cast<QEvtSize>(QF_EPOOL_EVENT_SIZE_(QF_pool_[idx]))) + { + ++idx; + Q_ASSERT(idx < QF_maxPool_); // cannot run out of registered pools + } + + QS_CRIT_STAT_ + QS_BEGIN_(QS_QF_NEW, null_void, null_void) + QS_TIME_(); // timestamp + QS_EVS_(evtSize); // the size of the event + QS_SIG_(static_cast<QSignal>(sig)); // the signal of the event + QS_END_() + + QEvt *e; + QF_EPOOL_GET_(QF_pool_[idx], e); + Q_ASSERT(e != static_cast<QEvt *>(0));// pool must not run out of events + + e->sig = static_cast<QSignal>(sig); // set signal for this event + e->poolId_ = static_cast<uint8_t>(idx + u8_1); // store pool ID in the evt + e->refCtr_ = u8_0; // set the reference counter to 0 + + return e; +} + +QP_END_ + +// "qf_pool.cpp" ============================================================= +/// \brief QF_pool_[] and QF_maxPool_ definition, QF::poolInit() +/// implementation. + +QP_BEGIN_ + +// Package-scope objects ----------------------------------------------------- +QF_EPOOL_TYPE_ QF_pool_[QF_MAX_EPOOL]; // allocate the event pools +uint8_t QF_maxPool_; // number of initialized event pools + +//............................................................................ +void QF::poolInit(void * const poolSto, + uint32_t const poolSize, uint32_t const evtSize) +{ + // cannot exceed the number of available memory pools + Q_REQUIRE(QF_maxPool_ < static_cast<uint8_t>(Q_DIM(QF_pool_))); + // please initialize event pools in ascending order of evtSize: + Q_REQUIRE((QF_maxPool_ == u8_0) + || (QF_EPOOL_EVENT_SIZE_(QF_pool_[QF_maxPool_ - u8_1]) + < evtSize)); + QF_EPOOL_INIT_(QF_pool_[QF_maxPool_], poolSto, poolSize, evtSize); + ++QF_maxPool_; // one more pool +} + +QP_END_ + + +// "qf_psini.cpp" ============================================================ +/// \brief QF_subscrList_ and QF_maxSignal_ definition, QF::psInit() +/// implementation. + +QP_BEGIN_ + +// Package-scope objects ----------------------------------------------------- +QSubscrList *QF_subscrList_; +enum_t QF_maxSignal_; + +//............................................................................ +void QF::psInit(QSubscrList * const subscrSto, uint32_t const maxSignal) { + QF_subscrList_ = subscrSto; + QF_maxSignal_ = static_cast<enum_t>(maxSignal); +} + +QP_END_ + +// "qf_pspub.cpp" ============================================================ +/// \brief QF::publish() implementation. + +QP_BEGIN_ + +//............................................................................ +#ifndef Q_SPY +void QF::publish(QEvt const * const e) { +#else +void QF::publish(QEvt const * const e, void const * const sender) { +#endif + // make sure that the published signal is within the configured range + Q_REQUIRE(static_cast<enum_t>(e->sig) < QF_maxSignal_); + + QF_CRIT_STAT_ + QF_CRIT_ENTRY_(); + + QS_BEGIN_NOCRIT_(QS_QF_PUBLISH, null_void, null_void) + QS_TIME_(); // the timestamp + QS_OBJ_(sender); // the sender object + QS_SIG_(e->sig); // the signal of the event + QS_U8_(QF_EVT_POOL_ID_(e)); // the pool Id of the event + QS_U8_(QF_EVT_REF_CTR_(e)); // the ref count of the event + QS_END_NOCRIT_() + + if (QF_EVT_POOL_ID_(e) != u8_0) { // is it a dynamic event? + QF_EVT_REF_CTR_INC_(e); // increment the reference counter, NOTE01 + } + QF_CRIT_EXIT_(); + +#if (QF_MAX_ACTIVE <= 8) + uint8_t tmp = QF_PTR_AT_(QF_subscrList_, e->sig).m_bits[0]; + while (tmp != u8_0) { + uint8_t p = Q_ROM_BYTE(QF_log2Lkup[tmp]); + tmp &= Q_ROM_BYTE(QF_invPwr2Lkup[p]); // clear the subscriber bit + Q_ASSERT(active_[p] != static_cast<QActive *>(0));//must be registered + + // POST() asserts internally if the queue overflows + active_[p]->POST(e, sender); + } +#else + // number of bytes in the list + uint8_t i = QF_SUBSCR_LIST_SIZE; + do { // go through all bytes in the subsciption list + --i; + uint8_t tmp = QF_PTR_AT_(QF_subscrList_, e->sig).m_bits[i]; + while (tmp != u8_0) { + uint8_t p = Q_ROM_BYTE(QF_log2Lkup[tmp]); + tmp &= Q_ROM_BYTE(QF_invPwr2Lkup[p]); // clear the subscriber bit + // adjust the priority + p = static_cast<uint8_t>(p + static_cast<uint8_t>(i << 3)); + Q_ASSERT(active_[p] != static_cast<QActive *>(0)); // registered + + // POST() asserts internally if the queue overflows + active_[p]->POST(e, sender); + } + } while (i != u8_0); +#endif + + gc(e); // run the garbage collector, see NOTE01 +} + +QP_END_ + + +// "qf_pwr2.cpp" ============================================================= +/// \brief QF_pwr2Lkup[], QF_invPwr2Lkup[], and QF_div8Lkup[] definitions. + +QP_BEGIN_ + +// Global objects ------------------------------------------------------------ +uint8_t const Q_ROM Q_ROM_VAR QF_pwr2Lkup[65] = { + static_cast<uint8_t>(0x00), // unused location + static_cast<uint8_t>(0x01), static_cast<uint8_t>(0x02), + static_cast<uint8_t>(0x04), static_cast<uint8_t>(0x08), + static_cast<uint8_t>(0x10), static_cast<uint8_t>(0x20), + static_cast<uint8_t>(0x40), static_cast<uint8_t>(0x80), + static_cast<uint8_t>(0x01), static_cast<uint8_t>(0x02), + static_cast<uint8_t>(0x04), static_cast<uint8_t>(0x08), + static_cast<uint8_t>(0x10), static_cast<uint8_t>(0x20), + static_cast<uint8_t>(0x40), static_cast<uint8_t>(0x80), + static_cast<uint8_t>(0x01), static_cast<uint8_t>(0x02), + static_cast<uint8_t>(0x04), static_cast<uint8_t>(0x08), + static_cast<uint8_t>(0x10), static_cast<uint8_t>(0x20), + static_cast<uint8_t>(0x40), static_cast<uint8_t>(0x80), + static_cast<uint8_t>(0x01), static_cast<uint8_t>(0x02), + static_cast<uint8_t>(0x04), static_cast<uint8_t>(0x08), + static_cast<uint8_t>(0x10), static_cast<uint8_t>(0x20), + static_cast<uint8_t>(0x40), static_cast<uint8_t>(0x80), + static_cast<uint8_t>(0x01), static_cast<uint8_t>(0x02), + static_cast<uint8_t>(0x04), static_cast<uint8_t>(0x08), + static_cast<uint8_t>(0x10), static_cast<uint8_t>(0x20), + static_cast<uint8_t>(0x40), static_cast<uint8_t>(0x80), + static_cast<uint8_t>(0x01), static_cast<uint8_t>(0x02), + static_cast<uint8_t>(0x04), static_cast<uint8_t>(0x08), + static_cast<uint8_t>(0x10), static_cast<uint8_t>(0x20), + static_cast<uint8_t>(0x40), static_cast<uint8_t>(0x80), + static_cast<uint8_t>(0x01), static_cast<uint8_t>(0x02), + static_cast<uint8_t>(0x04), static_cast<uint8_t>(0x08), + static_cast<uint8_t>(0x10), static_cast<uint8_t>(0x20), + static_cast<uint8_t>(0x40), static_cast<uint8_t>(0x80), + static_cast<uint8_t>(0x01), static_cast<uint8_t>(0x02), + static_cast<uint8_t>(0x04), static_cast<uint8_t>(0x08), + static_cast<uint8_t>(0x10), static_cast<uint8_t>(0x20), + static_cast<uint8_t>(0x40), static_cast<uint8_t>(0x80) +}; + +uint8_t const Q_ROM Q_ROM_VAR QF_invPwr2Lkup[65] = { + static_cast<uint8_t>(0xFF), // unused location + static_cast<uint8_t>(0xFE), static_cast<uint8_t>(0xFD), + static_cast<uint8_t>(0xFB), static_cast<uint8_t>(0xF7), + static_cast<uint8_t>(0xEF), static_cast<uint8_t>(0xDF), + static_cast<uint8_t>(0xBF), static_cast<uint8_t>(0x7F), + static_cast<uint8_t>(0xFE), static_cast<uint8_t>(0xFD), + static_cast<uint8_t>(0xFB), static_cast<uint8_t>(0xF7), + static_cast<uint8_t>(0xEF), static_cast<uint8_t>(0xDF), + static_cast<uint8_t>(0xBF), static_cast<uint8_t>(0x7F), + static_cast<uint8_t>(0xFE), static_cast<uint8_t>(0xFD), + static_cast<uint8_t>(0xFB), static_cast<uint8_t>(0xF7), + static_cast<uint8_t>(0xEF), static_cast<uint8_t>(0xDF), + static_cast<uint8_t>(0xBF), static_cast<uint8_t>(0x7F), + static_cast<uint8_t>(0xFE), static_cast<uint8_t>(0xFD), + static_cast<uint8_t>(0xFB), static_cast<uint8_t>(0xF7), + static_cast<uint8_t>(0xEF), static_cast<uint8_t>(0xDF), + static_cast<uint8_t>(0xBF), static_cast<uint8_t>(0x7F), + static_cast<uint8_t>(0xFE), static_cast<uint8_t>(0xFD), + static_cast<uint8_t>(0xFB), static_cast<uint8_t>(0xF7), + static_cast<uint8_t>(0xEF), static_cast<uint8_t>(0xDF), + static_cast<uint8_t>(0xBF), static_cast<uint8_t>(0x7F), + static_cast<uint8_t>(0xFE), static_cast<uint8_t>(0xFD), + static_cast<uint8_t>(0xFB), static_cast<uint8_t>(0xF7), + static_cast<uint8_t>(0xEF), static_cast<uint8_t>(0xDF), + static_cast<uint8_t>(0xBF), static_cast<uint8_t>(0x7F), + static_cast<uint8_t>(0xFE), static_cast<uint8_t>(0xFD), + static_cast<uint8_t>(0xFB), static_cast<uint8_t>(0xF7), + static_cast<uint8_t>(0xEF), static_cast<uint8_t>(0xDF), + static_cast<uint8_t>(0xBF), static_cast<uint8_t>(0x7F), + static_cast<uint8_t>(0xFE), static_cast<uint8_t>(0xFD), + static_cast<uint8_t>(0xFB), static_cast<uint8_t>(0xF7), + static_cast<uint8_t>(0xEF), static_cast<uint8_t>(0xDF), + static_cast<uint8_t>(0xBF), static_cast<uint8_t>(0x7F) +}; + +uint8_t const Q_ROM Q_ROM_VAR QF_div8Lkup[65] = { + static_cast<uint8_t>(0), // unused location + static_cast<uint8_t>(0), static_cast<uint8_t>(0), static_cast<uint8_t>(0), + static_cast<uint8_t>(0), static_cast<uint8_t>(0), static_cast<uint8_t>(0), + static_cast<uint8_t>(0), static_cast<uint8_t>(0), + static_cast<uint8_t>(1), static_cast<uint8_t>(1), static_cast<uint8_t>(1), + static_cast<uint8_t>(1), static_cast<uint8_t>(1), static_cast<uint8_t>(1), + static_cast<uint8_t>(1), static_cast<uint8_t>(1), + static_cast<uint8_t>(2), static_cast<uint8_t>(2), static_cast<uint8_t>(2), + static_cast<uint8_t>(2), static_cast<uint8_t>(2), static_cast<uint8_t>(2), + static_cast<uint8_t>(2), static_cast<uint8_t>(2), + static_cast<uint8_t>(3), static_cast<uint8_t>(3), static_cast<uint8_t>(3), + static_cast<uint8_t>(3), static_cast<uint8_t>(3), static_cast<uint8_t>(3), + static_cast<uint8_t>(3), static_cast<uint8_t>(3), + static_cast<uint8_t>(4), static_cast<uint8_t>(4), static_cast<uint8_t>(4), + static_cast<uint8_t>(4), static_cast<uint8_t>(4), static_cast<uint8_t>(4), + static_cast<uint8_t>(4), static_cast<uint8_t>(4), + static_cast<uint8_t>(5), static_cast<uint8_t>(5), static_cast<uint8_t>(5), + static_cast<uint8_t>(5), static_cast<uint8_t>(5), static_cast<uint8_t>(5), + static_cast<uint8_t>(5), static_cast<uint8_t>(5), + static_cast<uint8_t>(6), static_cast<uint8_t>(6), static_cast<uint8_t>(6), + static_cast<uint8_t>(6), static_cast<uint8_t>(6), static_cast<uint8_t>(6), + static_cast<uint8_t>(6), static_cast<uint8_t>(6), + static_cast<uint8_t>(7), static_cast<uint8_t>(7), static_cast<uint8_t>(7), + static_cast<uint8_t>(7), static_cast<uint8_t>(7), static_cast<uint8_t>(7), + static_cast<uint8_t>(7), static_cast<uint8_t>(7) +}; + +QP_END_ + +// "qf_tick.cpp" ============================================================= +/// \brief QF::tick() implementation. + +QP_BEGIN_ + +//............................................................................ +#ifndef Q_SPY +void QF::tick(void) { // see NOTE01 +#else +void QF::tick(void const * const sender) { +#endif + + QF_CRIT_STAT_ + QF_CRIT_ENTRY_(); + + QS_BEGIN_NOCRIT_(QS_QF_TICK, null_void, null_void) + QS_TEC_(static_cast<QTimeEvtCtr>(++QS::tickCtr_)); // the tick counter + QS_END_NOCRIT_() + + QTimeEvt *prev = null_tevt; + for (QTimeEvt *t = QF_timeEvtListHead_; t != null_tevt; t = t->m_next) { + if (t->m_ctr == tc_0) { // time evt. scheduled for removal? + if (t == QF_timeEvtListHead_) { + QF_timeEvtListHead_ = t->m_next; + } + else { + Q_ASSERT(prev != null_tevt); + prev->m_next = t->m_next; + } + QF_EVT_REF_CTR_DEC_(t); // mark as not linked + } + else { + --t->m_ctr; + if (t->m_ctr == tc_0) { // about to expire? + if (t->m_interval != tc_0) { // periodic time event? + t->m_ctr = t->m_interval; // rearm the time evt + } + else { + QS_BEGIN_NOCRIT_(QS_QF_TIMEEVT_AUTO_DISARM, QS::teObj_, t) + QS_OBJ_(t); // this time event object + QS_OBJ_(t->m_act); // the active object + QS_END_NOCRIT_() + } + + QS_BEGIN_NOCRIT_(QS_QF_TIMEEVT_POST, QS::teObj_, t) + QS_TIME_(); // timestamp + QS_OBJ_(t); // the time event object + QS_SIG_(t->sig); // the signal of this time event + QS_OBJ_(t->m_act); // the active object + QS_END_NOCRIT_() + + QF_CRIT_EXIT_(); // leave crit. section before posting + // POST() asserts internally if the queue overflows + t->m_act->POST(t, sender); + QF_CRIT_ENTRY_(); // re-enter crit. section to continue + + if (t->m_ctr == tc_0) { // still marked to expire? + if (t == QF_timeEvtListHead_) { + QF_timeEvtListHead_ = t->m_next; + } + else { + Q_ASSERT(prev != null_tevt); + prev->m_next = t->m_next; + } + QF_EVT_REF_CTR_DEC_(t); // mark as removed + } + else { + prev = t; + } + } + else { + prev = t; + } + } + } + QF_CRIT_EXIT_(); +} + +QP_END_ + +// "qmp_get.cpp" ============================================================= +/// \brief QMPool::get() and QF::getPoolMargin() implementation. + +QP_BEGIN_ + +//............................................................................ +void *QMPool::get(void) { + QF_CRIT_STAT_ + QF_CRIT_ENTRY_(); + + QFreeBlock *fb = static_cast<QFreeBlock *>(m_free); // free block or NULL + if (fb != static_cast<QFreeBlock *>(0)) { // free block available? + m_free = fb->m_next; // adjust list head to the next free block + + Q_ASSERT(m_nFree > static_cast<QMPoolCtr>(0)); // at least one free + --m_nFree; // one free block less + if (m_nMin > m_nFree) { + m_nMin = m_nFree; // remember the minimum so far + } + } + + QS_BEGIN_NOCRIT_(QS_QF_MPOOL_GET, QS::mpObj_, m_start) + QS_TIME_(); // timestamp + QS_OBJ_(m_start); // the memory managed by this pool + QS_MPC_(m_nFree); // the number of free blocks in the pool + QS_MPC_(m_nMin); // the mninimum number of free blocks in the pool + QS_END_NOCRIT_() + + QF_CRIT_EXIT_(); + return fb; // return the block or NULL pointer to the caller +} +//............................................................................ +uint32_t QF::getPoolMargin(uint8_t const poolId) { + Q_REQUIRE((u8_1 <= poolId) && (poolId <= QF_maxPool_)); + + QF_CRIT_STAT_ + QF_CRIT_ENTRY_(); + uint32_t margin = static_cast<uint32_t>(QF_pool_[poolId - u8_1].m_nMin); + QF_CRIT_EXIT_(); + + return margin; +} + +QP_END_ + +// "qmp_init.cpp" ============================================================ +/// \brief QMPool::init() implementation. + +QP_BEGIN_ + +//............................................................................ +void QMPool::init(void * const poolSto, uint32_t const poolSize, + QMPoolSize const blockSize) +{ + // 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((poolSto != null_void) + && (poolSize >= static_cast<uint32_t>(sizeof(QFreeBlock))) + && ((blockSize + static_cast<QMPoolSize>(sizeof(QFreeBlock))) + > blockSize)); + + m_free = poolSto; + + // round up the blockSize to fit an integer number of pointers + m_blockSize = static_cast<QMPoolSize>(sizeof(QFreeBlock));//start with one + uint32_t nblocks = static_cast<uint32_t>(1);//# free blocks in a mem block + while (m_blockSize < blockSize) { + m_blockSize += static_cast<QMPoolSize>(sizeof(QFreeBlock)); + ++nblocks; + } + + // the whole pool buffer must fit at least one rounded-up block + Q_ASSERT(poolSize >= static_cast<uint32_t>(m_blockSize)); + + // chain all blocks together in a free-list... + // don't chain last block + uint32_t availSize = poolSize - static_cast<uint32_t>(m_blockSize); + m_nTot = static_cast<QMPoolCtr>(1); // one (the last) block in the pool + //start at the head of the free list + QFreeBlock *fb = static_cast<QFreeBlock *>(m_free); + while (availSize >= static_cast<uint32_t>(m_blockSize)) { + fb->m_next = &QF_PTR_AT_(fb, nblocks); // setup the next link + fb = fb->m_next; // advance to next block + availSize -= static_cast<uint32_t>(m_blockSize);// less available size + ++m_nTot; // increment the number of blocks so far + } + + fb->m_next = static_cast<QFreeBlock *>(0); // 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 + + QS_CRIT_STAT_ + QS_BEGIN_(QS_QF_MPOOL_INIT, QS::mpObj_, m_start) + QS_OBJ_(m_start); // the memory managed by this pool + QS_MPC_(m_nTot); // the total number of blocks + QS_END_() +} + +QP_END_ + +// "qmp_put.cpp" ============================================================= +/// \brief QMPool::put() implementation. + +QP_BEGIN_ + +// 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 MISRA-C++:2008 rules ??? and +// ???. 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_))) + +//............................................................................ +void QMPool::put(void * const b) { + + Q_REQUIRE(m_nFree < m_nTot); // adding one free so, # free < total + Q_REQUIRE(QF_PTR_RANGE_(b, m_start, m_end)); // b must be in range + + QF_CRIT_STAT_ + QF_CRIT_ENTRY_(); + + // link into the free list + (static_cast<QFreeBlock*>(b))->m_next = static_cast<QFreeBlock *>(m_free); + m_free = b; // set as new head of the free list + ++m_nFree; // one more free block in this pool + + QS_BEGIN_NOCRIT_(QS_QF_MPOOL_PUT, QS::mpObj_, m_start) + QS_TIME_(); // timestamp + QS_OBJ_(m_start); // the memory managed by this pool + QS_MPC_(m_nFree); // the number of free blocks in the pool + QS_END_NOCRIT_() + + QF_CRIT_EXIT_(); +} + +QP_END_ + + +// "qte_arm.cpp" ============================================================= +/// \brief QF_timeEvtListHead_ definition and QTimeEvt::arm_() implementation. + +QP_BEGIN_ + +// Package-scope objects ----------------------------------------------------- +QTimeEvt *QF_timeEvtListHead_; // head of linked list of time events + +//............................................................................ +bool QF::noTimeEvtsActive(void) { + return QF_timeEvtListHead_ == null_tevt; +} +//............................................................................ +void QTimeEvt::arm_(QActive * const act, QTimeEvtCtr const nTicks) { + Q_REQUIRE((nTicks > tc_0) /* cannot arm with 0 ticks */ + && (act != null_act) /* Active object must be provided */ + && (m_ctr == tc_0) /* must be disarmed */ + && (static_cast<enum_t>(sig) >= Q_USER_SIG)); // valid signal + QF_CRIT_STAT_ + QF_CRIT_ENTRY_(); + m_ctr = nTicks; + m_act = act; + if (refCtr_ == u8_0) { // not linked? + m_next = QF_timeEvtListHead_; + QF_timeEvtListHead_ = this; + QF_EVT_REF_CTR_INC_(this); // mark as linked + } + + QS_BEGIN_NOCRIT_(QS_QF_TIMEEVT_ARM, QS::teObj_, this) + QS_TIME_(); // timestamp + QS_OBJ_(this); // this time event object + QS_OBJ_(act); // the active object + QS_TEC_(nTicks); // the number of ticks + QS_TEC_(m_interval); // the interval + QS_END_NOCRIT_() + + QF_CRIT_EXIT_(); +} + +QP_END_ + + +// "qte_ctor.cpp" ============================================================ +/// \brief QTimeEvt::QTimeEvt() implementation. +QP_BEGIN_ + +//............................................................................ +QTimeEvt::QTimeEvt(enum_t const s) + : +#ifdef Q_EVT_CTOR + QEvt(static_cast<QSignal>(s)), +#endif + m_next(null_tevt), + m_act(null_act), + m_ctr(tc_0), + m_interval(tc_0) +{ + Q_REQUIRE(s >= Q_USER_SIG); // signal must be valid +#ifndef Q_EVT_CTOR + sig = static_cast<QSignal>(s); +#endif + // time event must be static, see NOTE01 + poolId_ = u8_0; // not from any event pool + refCtr_ = u8_0; // not linked +} + +QP_END_ + + +// "qte_ctr.cpp" ============================================================= +/// \brief QTimeEvt::ctr() implementation. + +QP_BEGIN_ + +//............................................................................ +QTimeEvtCtr QTimeEvt::ctr(void) const { + QF_CRIT_STAT_ + + QF_CRIT_ENTRY_(); + QTimeEvtCtr ret = m_ctr; + + QS_BEGIN_NOCRIT_(QS_QF_TIMEEVT_CTR, QS::teObj_, this) + QS_TIME_(); // timestamp + QS_OBJ_(this); // this time event object + QS_OBJ_(m_act); // the active object + QS_TEC_(ret); // the current counter + QS_TEC_(m_interval); // the interval + QS_END_NOCRIT_() + + QF_CRIT_EXIT_(); + return ret; +} + +QP_END_ + +// "qte_darm.cpp" ============================================================ +/// \brief QTimeEvt::disarm() implementation. + +QP_BEGIN_ + +//............................................................................ +// NOTE: disarm a time evt (no harm in disarming an already disarmed time evt) +bool QTimeEvt::disarm(void) { + QF_CRIT_STAT_ + QF_CRIT_ENTRY_(); + bool wasArmed; + if (m_ctr != tc_0) { // is the time event actually armed? + wasArmed = true; + + QS_BEGIN_NOCRIT_(QS_QF_TIMEEVT_DISARM, QS::teObj_, this) + QS_TIME_(); // timestamp + QS_OBJ_(this); // this time event object + QS_OBJ_(m_act); // the active object + QS_TEC_(m_ctr); // the number of ticks + QS_TEC_(m_interval); // the interval + QS_END_NOCRIT_() + + m_ctr = tc_0; // schedule removal from the list + } + else { // the time event was not armed + wasArmed = false; + + QS_BEGIN_NOCRIT_(QS_QF_TIMEEVT_DISARM_ATTEMPT, QS::teObj_, this) + QS_TIME_(); // timestamp + QS_OBJ_(this); // this time event object + QS_OBJ_(m_act); // the active object + QS_END_NOCRIT_() + } + QF_CRIT_EXIT_(); + return wasArmed; +} + +QP_END_ + + +// "qte_rarm.cpp" ============================================================ +/// \brief QTimeEvt::rearm() implementation. + +QP_BEGIN_ + +//............................................................................ +bool QTimeEvt::rearm(QTimeEvtCtr const nTicks) { + Q_REQUIRE((nTicks > tc_0) /* cannot rearm with 0 ticks */ + && (m_act != null_act) /* active object must be valid */ + && (static_cast<enum_t>(sig) >= Q_USER_SIG)); // valid signal + + QF_CRIT_STAT_ + QF_CRIT_ENTRY_(); + + bool isArmed; + if (m_ctr == tc_0) { // is this time event disarmed? + isArmed = false; + m_next = QF_timeEvtListHead_; + if (QF_timeEvtListHead_ != null_tevt) { + m_next = QF_timeEvtListHead_; + QF_timeEvtListHead_ = this; + QF_EVT_REF_CTR_INC_(this); // mark as linked + } + } + else { + isArmed = true; + } + m_ctr = nTicks; // re-load the tick counter (shift the phasing) + + QS_BEGIN_NOCRIT_(QS_QF_TIMEEVT_REARM, QS::teObj_, this) + QS_TIME_(); // timestamp + QS_OBJ_(this); // this time event object + QS_OBJ_(m_act); // the active object + QS_TEC_(m_ctr); // the number of ticks + QS_TEC_(m_interval); // the interval + QS_U8_(isArmed ? u8_1 : u8_0); // was the timer armed? + QS_END_NOCRIT_() + + QF_CRIT_EXIT_(); + return isArmed; +} + +QP_END_ + + +////////////////////////////////////////////////////////////////////////////// +// Kernel selection based on QK_PREEMPTIVE +// +#ifdef QK_PREEMPTIVE + +// "qk_pkg.h" ================================================================ +/// \brief Internal (package scope) QK/C interface. + +#ifndef QF_CRIT_STAT_TYPE + /// \brief This is an internal macro for defining the critical section + /// status type. + /// + /// The purpose of this macro is to enable writing the same code for the + /// case when critical sectgion 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 varaible. + /// Otherwise this macro is empty. + /// \sa #QF_CRIT_STAT_TYPE + #define QF_CRIT_STAT_ + + /// \brief This is an internal macro for entering a critical section. + /// + /// The purpose of this macro is to enable writing the same code for the + /// case when critical sectgion 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_ENTRY_() QF_CRIT_ENTRY(dummy) + + /// \brief This is an internal macro for exiting a cricial section. + /// + /// The purpose of this macro is to enable writing the same code for the + /// case when critical sectgion 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_EXIT_() QF_CRIT_EXIT(dummy) + +#else + #define QF_CRIT_STAT_ QF_CRIT_STAT_TYPE critStat_; + #define QF_CRIT_ENTRY_() QF_CRIT_ENTRY(critStat_) + #define QF_CRIT_EXIT_() QF_CRIT_EXIT(critStat_) +#endif + // define for backwards compatibility, see NOTE01 +#ifndef QF_CRIT_STAT_TYPE +#if !defined(QF_INT_DISABLE) && defined(QF_CRIT_ENTRY) + #define QF_INT_DISABLE() QF_CRIT_ENTRY(dummy) +#endif +#if !defined(QF_INT_ENABLE) && defined(QF_CRIT_EXIT) + #define QF_INT_ENABLE() QF_CRIT_EXIT(dummy) +#endif +#endif // QF_CRIT_STAT_TYPE + + // package-scope objects... +#ifndef QK_NO_MUTEX + extern "C" uint8_t QK_ceilingPrio_; ///< QK mutex priority ceiling +#endif + +// "qk.cpp" ================================================================== +/// \brief QK_readySet_, QK_currPrio_, and QK_intNest_ definitions and +/// QK::getVersion(), QF::init(), QF::run(), QF::stop(), +/// QActive::start(), QActive::stop() implementations. + +// Public-scope objects ------------------------------------------------------ +extern "C" { +#if (QF_MAX_ACTIVE <= 8) + QP_ QPSet8 QK_readySet_; // ready set of QK +#else + QP_ QPSet64 QK_readySet_; // ready set of QK +#endif + // start with the QK scheduler locked +uint8_t QK_currPrio_ = static_cast<uint8_t>(QF_MAX_ACTIVE + 1); +uint8_t QK_intNest_; // start with nesting level of 0 + +} // extern "C" + +QP_BEGIN_ + +//............................................................................ +char_t const Q_ROM * Q_ROM_VAR QK::getVersion(void) { + uint8_t const u8_zero = static_cast<uint8_t>('0'); + static char_t const Q_ROM Q_ROM_VAR version[] = { + static_cast<char_t>(((QP_VERSION >> 12) & 0xFU) + u8_zero), + static_cast<char_t>('.'), + static_cast<char_t>(((QP_VERSION >> 8) & 0xFU) + u8_zero), + static_cast<char_t>('.'), + static_cast<char_t>(((QP_VERSION >> 4) & 0xFU) + u8_zero), + static_cast<char_t>((QP_VERSION & 0xFU) + u8_zero), + static_cast<char_t>('\0') + }; + return version; +} +//............................................................................ +void QF::init(void) { + QK_init(); // QK initialization ("C" linkage, might be assembly) +} +//............................................................................ +void QF::stop(void) { + QF::onCleanup(); // cleanup callback + // nothing else to do for the QK preemptive kernel +} +//............................................................................ +static void initialize(void) { + QK_currPrio_ = static_cast<uint8_t>(0); // priority for the QK idle loop + uint8_t p = QK_schedPrio_(); + if (p != static_cast<uint8_t>(0)) { + QK_sched_(p); // process all events produced so far + } +} +//............................................................................ +int16_t QF::run(void) { + QF_INT_DISABLE(); + initialize(); + onStartup(); // startup callback + QF_INT_ENABLE(); + + for (;;) { // the QK idle loop + QK::onIdle(); // invoke the QK on-idle callback + } + // this unreachable return is to make the compiler happy + return static_cast<int16_t>(0); +} +//............................................................................ +void QActive::start(uint8_t const prio, + QEvt const *qSto[], uint32_t const qLen, + void * const stkSto, uint32_t const stkSize, + QEvt const * const ie) +{ + Q_REQUIRE((static_cast<uint8_t>(0) < prio) + && (prio <= static_cast<uint8_t>(QF_MAX_ACTIVE))); + + m_eQueue.init(qSto, static_cast<QEQueueCtr>(qLen)); // initialize queue + m_prio = prio; + QF::add_(this); // make QF aware of this active object + +#if defined(QK_TLS) || defined(QK_EXT_SAVE) + // in the QK port the parameter stkSize is used as the thread flags + m_osObject = static_cast<uint8_t>(stkSize); // m_osObject contains flags + + // in the QK port the parameter stkSto is used as the thread-local-storage + m_thread = stkSto; // contains the pointer to the thread-local-storage +#else + Q_ASSERT((stkSto == static_cast<void *>(0)) + && (stkSize == static_cast<uint32_t>(0))); +#endif + + init(ie); // execute initial transition + + QS_FLUSH(); // flush the trace buffer to the host +} +//............................................................................ +void QActive::stop(void) { + QF::remove_(this); // remove this active object from the QF +} + +QP_END_ + +// "qk_sched" ================================================================ +/// \brief QK_schedPrio_() and QK_sched_() implementation. + +//............................................................................ +// NOTE: the QK scheduler is entered and exited with interrupts DISABLED. +// QK_sched_() is extern "C", so it does not belong to the QP namespace. +// +extern "C" { + +//............................................................................ +// NOTE: QK schedPrio_() is entered and exited with interrupts DISABLED +uint8_t QK_schedPrio_(void) { + + uint8_t p = QK_readySet_.findMax(); + +#ifdef QK_NO_MUTEX + if (p > QK_currPrio_) { // do we have a preemption? +#else // QK priority-ceiling mutexes allowed + if ((p > QK_currPrio_) && (p > QK_ceilingPrio_)) { +#endif + } + else { + p = static_cast<uint8_t>(0); + } + return p; +} +//............................................................................ +void QK_sched_(uint8_t p) { + + uint8_t pin = QK_currPrio_; // save the initial priority + QP_ QActive *a; +#ifdef QK_TLS // thread-local storage used? + uint8_t pprev = pin; +#endif + do { + a = QP_ QF::active_[p]; // obtain the pointer to the AO + QK_currPrio_ = p; // this becomes the current task priority + +#ifdef QK_TLS // thread-local storage used? + if (p != pprev) { // are we changing threads? + QK_TLS(a); // switch new thread-local storage + pprev = p; + } +#endif + QS_BEGIN_NOCRIT_(QP_ QS_QK_SCHEDULE, QP_ QS::aoObj_, a) + QS_TIME_(); // timestamp + QS_U8_(p); // the priority of the active object + QS_U8_(pin); // the preempted priority + QS_END_NOCRIT_() + + QF_INT_ENABLE(); // unconditionally enable interrupts + + QP_ QEvt const *e = a->get_(); // get the next event for this AO + a->dispatch(e); // dispatch e to this AO + QP_ QF::gc(e); // garbage collect the event, if necessary + + // determine the next highest-priority AO ready to run + QF_INT_DISABLE(); // disable interrupts + p = QK_readySet_.findMax(); + +#ifdef QK_NO_MUTEX + } while (p > pin); // is the new priority higher than initial? +#else // QK priority-ceiling mutexes allowed + } while ((p > pin) && (p > QK_ceilingPrio_)); +#endif + + QK_currPrio_ = pin; // restore the initial priority + +#ifdef QK_TLS // thread-local storage used? + if (pin != static_cast<uint8_t>(0)) { // no extended context for idle loop + a = QP_ QF::active_[pin]; // the pointer to the preempted AO + QK_TLS(a); // restore the original TLS + } +#endif +} + +} // extern "C" + +#ifndef QK_NO_MUTEX + +// "qk_mutex.cpp" ============================================================ +/// \brief QK::mutexLock()/QK::mutexUnlock() implementation. + +// package-scope objects ----------------------------------------------------- +extern "C" { + uint8_t QK_ceilingPrio_; // ceiling priority of a mutex +} + +QP_BEGIN_ + +//............................................................................ +QMutex QK::mutexLock(uint8_t const prioCeiling) { + QF_CRIT_STAT_ + QF_CRIT_ENTRY_(); + uint8_t mutex = QK_ceilingPrio_; // original QK priority ceiling to return + if (QK_ceilingPrio_ < prioCeiling) { + QK_ceilingPrio_ = prioCeiling; // raise the QK priority ceiling + } + + QS_BEGIN_NOCRIT_(QS_QK_MUTEX_LOCK, + static_cast<void *>(0), static_cast<void *>(0)) + QS_TIME_(); // timestamp + QS_U8_(mutex); // the original priority + QS_U8_(QK_ceilingPrio_); // the current priority ceiling + QS_END_NOCRIT_() + + QF_CRIT_EXIT_(); + return mutex; +} +//............................................................................ +void QK::mutexUnlock(QMutex mutex) { + QF_CRIT_STAT_ + QF_CRIT_ENTRY_(); + + QS_BEGIN_NOCRIT_(QS_QK_MUTEX_UNLOCK, + static_cast<void *>(0), static_cast<void *>(0)) + QS_TIME_(); // timestamp + QS_U8_(mutex); // the original priority + QS_U8_(QK_ceilingPrio_); // the current priority ceiling + QS_END_NOCRIT_() + + if (QK_ceilingPrio_ > mutex) { + QK_ceilingPrio_ = mutex; // restore the saved priority ceiling + mutex = QK_schedPrio_(); // reuse 'mutex' to hold priority + if (mutex != static_cast<uint8_t>(0)) { + QK_sched_(mutex); + } + } + QF_CRIT_EXIT_(); +} + +QP_END_ + +#endif // QK_NO_MUTEX + +#else // QK_PREEMPTIVE + +// "qvanilla.cpp" ============================================================ +/// \brief "vanilla" cooperative kernel, +/// QActive::start(), QActive::stop(), and QF::run() implementation. + +QP_BEGIN_ + +// Package-scope objects ----------------------------------------------------- +extern "C" { +#if (QF_MAX_ACTIVE <= 8) + QPSet8 QF_readySet_; // ready set of active objects +#else + QPSet64 QF_readySet_; // ready set of active objects +#endif + +uint8_t QF_currPrio_; ///< current task/interrupt priority +uint8_t QF_intNest_; ///< interrupt nesting level + +} // extern "C" + +//............................................................................ +void QF::init(void) { + // nothing to do for the "vanilla" kernel +} +//............................................................................ +void QF::stop(void) { + onCleanup(); // cleanup callback + // nothing else to do for the "vanilla" kernel +} +//............................................................................ +int16_t QF::run(void) { + onStartup(); // startup callback + + for (;;) { // the bacground loop + QF_INT_DISABLE(); + if (QF_readySet_.notEmpty()) { + uint8_t p = QF_readySet_.findMax(); + QActive *a = active_[p]; + QF_currPrio_ = p; // save the current priority + QF_INT_ENABLE(); + + QEvt const *e = a->get_(); // get the next event for this AO + a->dispatch(e); // dispatch evt to the HSM + gc(e); // determine if event is garbage and collect it if so + } + else { + onIdle(); // see NOTE01 + } + } + // this unreachable return is to make the compiler happy + return static_cast<int16_t>(0); +} +//............................................................................ +void QActive::start(uint8_t const prio, + QEvt const *qSto[], uint32_t const qLen, + void * const stkSto, uint32_t const, + QEvt const * const ie) +{ + Q_REQUIRE((u8_0 < prio) && (prio <= static_cast<uint8_t>(QF_MAX_ACTIVE)) + && (stkSto == null_void)); // does not need per-actor stack + + m_eQueue.init(qSto, static_cast<QEQueueCtr>(qLen)); // initialize QEQueue + m_prio = prio; // set the QF priority of this active object + QF::add_(this); // make QF aware of this active object + init(ie); // execute initial transition + + QS_FLUSH(); // flush the trace buffer to the host +} +//............................................................................ +void QActive::stop(void) { + QF::remove_(this); +} + +QP_END_ + +////////////////////////////////////////////////////////////////////////////// +// NOTE01: +// QF::onIdle() must be called with interrupts DISABLED because the +// determination of the idle condition (no events in the queues) can change +// at any time by an interrupt posting events to a queue. The QF::onIdle() +// MUST enable interrups internally, perhaps at the same time as putting the +// CPU into a power-saving mode. +// + +extern "C" { + +void QK_sched_(uint8_t p) { // dummy empty definition for qk_port.s + (void)p; +} + +uint8_t QK_schedPrio_(void) { // dummy empty definition for qk_port.s + return static_cast<uint8_t>(0U); +} + +} // extern "C" + +#endif // QK_PREEMPTIVE + +////////////////////////////////////////////////////////////////////////////// +#ifdef Q_SPY + +// "qs_pkg.h" ================================================================ +/// \brief Internal (package scope) QS/C++ interface. + +QP_BEGIN_ + +/// \brief QS ring buffer counter and offset type +typedef uint16_t QSCtr; + +/// \brief Internal QS macro to insert an un-escaped byte into +/// the QS buffer +//// +#define QS_INSERT_BYTE(b_) \ + QS_PTR_AT_(QS_head_) = (b_); \ + ++QS_head_; \ + if (QS_head_ == QS_end_) { \ + QS_head_ = static_cast<QSCtr>(0); \ + } \ + ++QS_used_; + +/// \brief Internal QS macro to insert an escaped byte into the QS buffer +#define QS_INSERT_ESC_BYTE(b_) \ + QS_chksum_ = static_cast<uint8_t>(QS_chksum_ + (b_)); \ + if (((b_) == QS_FRAME) || ((b_) == QS_ESC)) { \ + QS_INSERT_BYTE(QS_ESC) \ + QS_INSERT_BYTE(static_cast<uint8_t>((b_) ^ QS_ESC_XOR)) \ + } \ + else { \ + QS_INSERT_BYTE(b_) \ + } + +/// \brief Internal QS macro to insert a escaped checksum byte into +/// the QS buffer +#define QS_INSERT_CHKSUM_BYTE() \ + QS_chksum_ = static_cast<uint8_t>(~QS_chksum_); \ + if ((QS_chksum_ == QS_FRAME) || (QS_chksum_ == QS_ESC)) { \ + QS_INSERT_BYTE(QS_ESC) \ + QS_INSERT_BYTE(static_cast<uint8_t>(QS_chksum_ ^ QS_ESC_XOR)) \ + } \ + else { \ + QS_INSERT_BYTE(QS_chksum_) \ + } + + +/// \brief Internal QS macro to access the QS ring buffer +/// +/// \note The QS buffer is allocated by the user and is accessed through the +/// pointer QS_ring_, which violates the MISRA-C 2004 Rule 17.4(req), pointer +/// arithmetic other than array indexing. Encapsulating this violation in a +/// macro allows to selectively suppress this specific deviation. +#define QS_PTR_AT_(i_) (QS_ring_[i_]) + + +/// \brief Frame character of the QS output protocol +uint8_t const QS_FRAME = static_cast<uint8_t>(0x7E); + +/// \brief Escape character of the QS output protocol +uint8_t const QS_ESC = static_cast<uint8_t>(0x7D); + +/// \brief Escape modifier of the QS output protocol +/// +/// The escaped byte is XOR-ed with the escape modifier before it is inserted +/// into the QS buffer. +uint8_t const QS_ESC_XOR = static_cast<uint8_t>(0x20); + +#ifndef Q_ROM_BYTE + /// \brief Macro to access a byte allocated in ROM + /// + /// Some compilers for Harvard-architecture MCUs, such as gcc for AVR, do + /// not generate correct code for accessing data allocated in the program + /// space (ROM). The workaround for such compilers is to explictly add + /// assembly code to access each data element allocated in the program + /// space. The macro Q_ROM_BYTE() retrieves a byte from the given ROM + /// address. + /// + /// The Q_ROM_BYTE() macro should be defined for the compilers that + /// cannot handle correctly data allocated in ROM (such as the gcc). + /// If the macro is left undefined, the default definition simply returns + /// the argument and lets the compiler generate the correct code. + #define Q_ROM_BYTE(rom_var_) (rom_var_) +#endif + +//............................................................................ +extern uint8_t *QS_ring_; ///< pointer to the start of the ring buffer +extern QSCtr QS_end_; ///< offset of the end of the ring buffer +extern QSCtr QS_head_; ///< offset to where next byte will be inserted +extern QSCtr QS_tail_; ///< offset of where next event will be extracted +extern QSCtr QS_used_; ///< number of bytes currently in the ring buffer +extern uint8_t QS_seq_; ///< the record sequence number +extern uint8_t QS_chksum_; ///< the checksum of the current record +extern uint8_t QS_full_; ///< the ring buffer is temporarily full + +QP_END_ + + +// "qs.cpp" ================================================================== +/// \brief QS internal variables definitions and core QS functions +/// implementations. + +QP_BEGIN_ + +//............................................................................ +uint8_t QS::glbFilter_[32]; // global QS filter + +//............................................................................ +uint8_t *QS_ring_; // pointer to the start of the ring buffer +QSCtr QS_end_; // offset of the end of the ring buffer +QSCtr QS_head_; // offset to where next byte will be inserted +QSCtr QS_tail_; // offset of where next byte will be extracted +QSCtr QS_used_; // number of bytes currently in the ring buffer +uint8_t QS_seq_; // the record sequence number +uint8_t QS_chksum_; // the checksum of the current record +uint8_t QS_full_; // the ring buffer is temporarily full + +//............................................................................ +char_t const Q_ROM * Q_ROM_VAR QS::getVersion(void) { + uint8_t const u8_zero = static_cast<uint8_t>('0'); + static char_t const Q_ROM Q_ROM_VAR version[] = { + static_cast<char_t>(((QP_VERSION >> 12) & 0xFU) + u8_zero), + static_cast<char_t>('.'), + static_cast<char_t>(((QP_VERSION >> 8) & 0xFU) + u8_zero), + static_cast<char_t>('.'), + static_cast<char_t>(((QP_VERSION >> 4) & 0xFU) + u8_zero), + static_cast<char_t>((QP_VERSION & 0xFU) + u8_zero), + static_cast<char_t>('\0') + }; + return version; +} +//............................................................................ +void QS::initBuf(uint8_t sto[], uint32_t const stoSize) { + QS_ring_ = &sto[0]; + QS_end_ = static_cast<QSCtr>(stoSize); +} +//............................................................................ +void QS::filterOn(uint8_t const rec) { + if (rec == QS_ALL_RECORDS) { + uint8_t i; + for (i = static_cast<uint8_t>(0); + i < static_cast<uint8_t>(sizeof(glbFilter_)); + ++i) + { + glbFilter_[i] = static_cast<uint8_t>(0xFF); + } + } + else { + glbFilter_[rec >> 3] |= + static_cast<uint8_t>(1U << (rec & static_cast<uint8_t>(0x07))); + } +} +//............................................................................ +void QS::filterOff(uint8_t const rec) { + if (rec == QS_ALL_RECORDS) { + uint8_t i; + for (i = static_cast<uint8_t>(0); + i < static_cast<uint8_t>(sizeof(glbFilter_)); + ++i) + { + glbFilter_[i] = static_cast<uint8_t>(0); + } + } + else { + glbFilter_[rec >> 3] &= static_cast<uint8_t>( + ~static_cast<uint8_t>((1U << (rec & static_cast<uint8_t>(0x07))))); + } +} +//............................................................................ +void QS::begin(uint8_t const rec) { + QS_chksum_ = static_cast<uint8_t>(0); // clear the checksum + ++QS_seq_; // always increment the sequence number + QS_INSERT_ESC_BYTE(QS_seq_) // store the sequence number + QS_INSERT_ESC_BYTE(rec) // store the record ID +} +//............................................................................ +void QS::end(void) { + QS_INSERT_CHKSUM_BYTE() + QS_INSERT_BYTE(QS_FRAME) + if (QS_used_ > QS_end_) { // overrun over the old data? + QS_tail_ = QS_head_; // shift the tail to the old data + QS_used_ = QS_end_; // the whole buffer is used + } +} +//............................................................................ +void QS::u8(uint8_t const format, uint8_t const d) { + QS_INSERT_ESC_BYTE(format) + QS_INSERT_ESC_BYTE(d) +} +//............................................................................ +void QS::u16(uint8_t const format, uint16_t d) { + QS_INSERT_ESC_BYTE(format) + QS_INSERT_ESC_BYTE(static_cast<uint8_t>(d)) + d >>= 8; + QS_INSERT_ESC_BYTE(static_cast<uint8_t>(d)) +} +//............................................................................ +void QS::u32(uint8_t const format, uint32_t d) { + QS_INSERT_ESC_BYTE(format) + QS_INSERT_ESC_BYTE(static_cast<uint8_t>(d)) + d >>= 8; + QS_INSERT_ESC_BYTE(static_cast<uint8_t>(d)) + d >>= 8; + QS_INSERT_ESC_BYTE(static_cast<uint8_t>(d)) + d >>= 8; + QS_INSERT_ESC_BYTE(static_cast<uint8_t>(d)) +} + +QP_END_ + + +// "qs_.cpp" ================================================================= +/// \brief QS functions for internal use inside QP components + +QP_BEGIN_ + +//............................................................................ +void const *QS::smObj_; // local state machine for QEP filter +void const *QS::aoObj_; // local active object for QF filter +void const *QS::mpObj_; // local event pool for QF filter +void const *QS::eqObj_; // local raw queue for QF filter +void const *QS::teObj_; // local time event for QF filter +void const *QS::apObj_; // local object Application filter + +QSTimeCtr QS::tickCtr_; // tick counter for the QS_QF_TICK record + +//............................................................................ +void QS::u8_(uint8_t const d) { + QS_INSERT_ESC_BYTE(d) +} +//............................................................................ +void QS::u16_(uint16_t d) { + QS_INSERT_ESC_BYTE(static_cast<uint8_t>(d)) + d >>= 8; + QS_INSERT_ESC_BYTE(static_cast<uint8_t>(d)) +} +//............................................................................ +void QS::u32_(uint32_t d) { + QS_INSERT_ESC_BYTE(static_cast<uint8_t>(d)) + d >>= 8; + QS_INSERT_ESC_BYTE(static_cast<uint8_t>(d)) + d >>= 8; + QS_INSERT_ESC_BYTE(static_cast<uint8_t>(d)) + d >>= 8; + QS_INSERT_ESC_BYTE(static_cast<uint8_t>(d)) +} +//............................................................................ +void QS::str_(char_t const *s) { + uint8_t b = static_cast<uint8_t>(*s); + while (b != static_cast<uint8_t>(0)) { + // ASCII characters don't need escaping + QS_chksum_ = static_cast<uint8_t>(QS_chksum_ + b); + QS_INSERT_BYTE(b) + ++s; + b = static_cast<uint8_t>(*s); + } + QS_INSERT_BYTE(static_cast<uint8_t>(0)) +} +//............................................................................ +void QS::str_ROM_(char_t const Q_ROM * Q_ROM_VAR s) { + uint8_t b = static_cast<uint8_t>(Q_ROM_BYTE(*s)); + while (b != static_cast<uint8_t>(0)) { + // ASCII characters don't need escaping + QS_chksum_ = static_cast<uint8_t>(QS_chksum_ + b); + QS_INSERT_BYTE(b) + ++s; + b = static_cast<uint8_t>(Q_ROM_BYTE(*s)); + } + QS_INSERT_BYTE(static_cast<uint8_t>(0)) +} + +QP_END_ + +// "qs_blk.cpp" ============================================================== +/// \brief QS::getBlock() implementation + +QP_BEGIN_ + +//............................................................................ +// get up to *pn bytes of contiguous memory +uint8_t const *QS::getBlock(uint16_t * const pNbytes) { + uint8_t *block; + if (QS_used_ == static_cast<QSCtr>(0)) { + *pNbytes = static_cast<uint16_t>(0); + block = static_cast<uint8_t *>(0); // no bytes to return right now + } + else { + QSCtr n = static_cast<QSCtr>(QS_end_ - QS_tail_); + if (n > QS_used_) { + n = QS_used_; + } + if (n > static_cast<QSCtr>(*pNbytes)) { + n = static_cast<QSCtr>(*pNbytes); + } + *pNbytes = static_cast<uint16_t>(n); + QS_used_ = static_cast<QSCtr>(QS_used_ - n); + QSCtr t = QS_tail_; + QS_tail_ = static_cast<QSCtr>(QS_tail_ + n); + if (QS_tail_ == QS_end_) { + QS_tail_ = static_cast<QSCtr>(0); + } + block = &QS_PTR_AT_(t); + } + return block; +} + +QP_END_ + +// "qs_byte.cpp" ============================================================= +/// \brief QS::getByte() implementation + +QP_BEGIN_ + +//............................................................................ +uint16_t QS::getByte(void) { + uint16_t ret; + if (QS_used_ == static_cast<QSCtr>(0)) { + ret = QS_EOD; // set End-Of-Data + } + else { + ret = static_cast<uint16_t>(QS_PTR_AT_(QS_tail_)); // byte to return + ++QS_tail_; // advance the tail + if (QS_tail_ == QS_end_) { // tail wrap around? + QS_tail_ = static_cast<QSCtr>(0); + } + --QS_used_; // one less byte used + } + return ret; // return the byte or EOD +} + +QP_END_ + + +// "qs_f32.cpp" ============================================================== +/// \brief QS::f32() implementation + +QP_BEGIN_ + +//............................................................................ +void QS::f32(uint8_t const format, float32_t const d) { + union F32Rep { + float32_t f; + uint32_t u; + } fu32; + fu32.f = d; + + QS_INSERT_ESC_BYTE(format) + QS_INSERT_ESC_BYTE(static_cast<uint8_t>(fu32.u)) + fu32.u >>= 8; + QS_INSERT_ESC_BYTE(static_cast<uint8_t>(fu32.u)) + fu32.u >>= 8; + QS_INSERT_ESC_BYTE(static_cast<uint8_t>(fu32.u)) + fu32.u >>= 8; + QS_INSERT_ESC_BYTE(static_cast<uint8_t>(fu32.u)) +} + +QP_END_ + + +// "qs_mem.cpp" ============================================================== +/// \brief QS::mem() implementation + +QP_BEGIN_ + +//............................................................................ +void QS::mem(uint8_t const *blk, uint8_t size) { + QS_INSERT_BYTE(static_cast<uint8_t>(QS_MEM_T)) + QS_chksum_ = + static_cast<uint8_t>(QS_chksum_ + static_cast<uint8_t>(QS_MEM_T)); + QS_INSERT_ESC_BYTE(size) + while (size != static_cast<uint8_t>(0)) { + QS_INSERT_ESC_BYTE(*blk) + ++blk; + --size; + } +} + +QP_END_ + + +// "qs_str.cpp" ============================================================== +/// \brief QS::str() and QS::str_ROM() implementation + +QP_BEGIN_ + +//............................................................................ +void QS::str(char_t const *s) { + uint8_t b = static_cast<uint8_t>(*s); + QS_INSERT_BYTE(static_cast<uint8_t>(QS_STR_T)) + QS_chksum_ = + static_cast<uint8_t>(QS_chksum_ + static_cast<uint8_t>(QS_STR_T)); + while (b != static_cast<uint8_t>(0)) { + // ASCII characters don't need escaping + QS_INSERT_BYTE(b) + QS_chksum_ = static_cast<uint8_t>(QS_chksum_ + b); + ++s; + b = static_cast<uint8_t>(*s); + } + QS_INSERT_BYTE(static_cast<uint8_t>(0)) +} +//............................................................................ +void QS::str_ROM(char_t const Q_ROM * Q_ROM_VAR s) { + uint8_t b = static_cast<uint8_t>(Q_ROM_BYTE(*s)); + QS_INSERT_BYTE(static_cast<uint8_t>(QS_STR_T)) + QS_chksum_ = + static_cast<uint8_t>(QS_chksum_ + static_cast<uint8_t>(QS_STR_T)); + while (b != static_cast<uint8_t>(0)) { + // ASCII characters don't need escaping + QS_INSERT_BYTE(b) + QS_chksum_ = static_cast<uint8_t>(QS_chksum_ + b); + ++s; + b = static_cast<uint8_t>(Q_ROM_BYTE(*s)); + } + QS_INSERT_BYTE(static_cast<uint8_t>(0)) +} + +QP_END_ + +#endif // Q_SPY