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.
Revision 9:ca2e6010d9e2, committed 2012-09-04
- Comitter:
- QL
- Date:
- Tue Sep 04 22:20:52 2012 +0000
- Parent:
- 8:934bb9eea80b
- Commit message:
- QP/C++ 4.5.02 compatible with QM 2.2.xx
Changed in this revision
diff -r 934bb9eea80b -r ca2e6010d9e2 qk_port.s --- a/qk_port.s Mon Sep 26 03:27:09 2011 +0000 +++ b/qk_port.s Tue Sep 04 22:20:52 2012 +0000 @@ -1,130 +1,135 @@ -;***************************************************************************** -; Product: QK port to ARM Cortex-M0/M3, mbed ARM assembler, CMSIS-compliant -; Last Updated for Version: 4.2.04 -; Date of the Last Update: Sep 23, 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 -;***************************************************************************** - AREA QK, CODE, READONLY - - EXPORT QK_init - EXPORT PendSV_Handler ; CMSIS-compliant PendSV exception name - EXPORT SVC_Handler ; CMSIS-compliant SVC exception name - - EXTERN QK_schedule_ ; external references - EXTERN QK_readySet_ ; external references - - -;***************************************************************************** -; -; The QK_init function sets the priorities of PendSV and SVCall exceptions -; to 0xFF and 0x00, respectively. The function internally disables -; interrupts, but restores the original interrupt lock before exit. -; -;***************************************************************************** -QK_init - MRS r0,PRIMASK ; store the state of the PRIMASK in r0 - CPSID i ; disable interrupts (set PRIMASK) - - LDR r1,=0xE000ED18 ; System Handler Priority Register - LDR r2,[r1,#8] ; load the System 12-15 Priority Register - MOVS r3,#0xFF - LSLS r3,r3,#16 - ORRS r2,r3 ; set PRI_14 (PendSV) to 0xFF - STR r2,[r1,#8] ; write the System 12-15 Priority Register - LDR r2,[r1,#4] ; load the System 8-11 Priority Register - LSLS r3,r3,#8 - BICS r2,r3 ; set PRI_11 (SVCall) to 0x00 - STR r2,[r1,#4] ; write the System 8-11 Priority Register - - MSR PRIMASK,r0 ; restore the original PRIMASK - BX lr ; return to the caller - - -;***************************************************************************** -; -; The PendSV_Handler exception hanlder is used for handling asynchronous -; preemptions in QK. The use of the PendSV exception is the recommended -; and most efficient method for performing context switches with ARM Cortex. -; -; The PendSV exception should have the lowest priority in the whole system -; (0xFF, see QK_init). All other exeptions and interrupts should have higher -; priority. For example, for NVIC with 2 priority bits all interrupts and -; exceptions must have numerical value of priority lower than 0xC0. In this -; case the interrupt priority levels available to your applications are (in -; the order from the lowest urgency to the highest urgency): 0x80, 0x40, 0x00. -; -; Also, *all* ISRs in the QK application must trigger the PendSV exception -; by calling the QK_ISR_EXIT() macro. -; -; Due to tail-chaining and its lowest priority, the PendSV exception will be -; entered immediately after the exit from the *last* nested interrupt (or -; exception). In QK, this is exactly the time when the QK scheduler needs to -; check for the asynchronous preemptions. -; -;***************************************************************************** -PendSV_Handler - CPSID i ; disable interrupts at processor level - LDR r0,=0xE000ED04 ; load the NVIC-ICSR register address - MOVS r1,#0x01 - LSLS r1,r1,#27 ; make up a mask with only the PENDSVCLR bit set - STR r1,[r0] ; remove the pending status of PendSV - LDR r0,=QK_readySet_ ; load the address of QK_readySet_ - LDRB r0,[r0] ; load the first byte of QK_readySet_ - CMP r0,#0 ; is QK_readySet_ == 0 ? - BEQ.N iret ; if QK_readySet_ == 0, branch to iret - - ; at this point r1 contains (1 << 27) - LSRS r1,r1,#3 ; make up a task xPSR with only the T bit set - LDR r0,=schedule ; load the address of sched wrapper (new PC) - PUSH {r0-r1} ; push xPSR,PC - SUB sp,sp,#(6*4) ; don't care for lr,r12,r3,r2,r1,r0 - BX lr ; interrupt return to the scheduler - -iret - CPSIE i ; enable interrupts at processor level - BX lr ; interrupt return to the task - -schedule - BL QK_schedule_ ; call the QK scheduler - CPSIE i ; enable interrupts to allow SVCall exception - SVC 0 ; SV exception returns to the preempted task - - -;***************************************************************************** -; -; The SVC_Handler exception handler is used for returning back to the -; interrupted context (task or interrupt). The SVC exception should have -; the lowest priority in the whole system (see QK_init). The SVCall -; exception simply removes its own interrupt stack frame from the stack and -; returns to the preempted task using the interrupt stack frame that must be -; at the top of the stack. -; -;***************************************************************************** -SVC_Handler - ADD sp,sp,#(8*4) ; remove one interrupt frame from the stack - BX lr ; return to the preempted task - - ALIGN ; make sure proper alignment - END - \ No newline at end of file +;***************************************************************************** +; Product: QK port to ARM Cortex-M0/M3, mbed ARM assembler, CMSIS-compliant +; Last Updated for Version: 4.5.02 +; Date of the Last Update: Sep 04, 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 +;***************************************************************************** + AREA QK, CODE, READONLY + PRESERVE8 + + EXPORT QK_init + EXPORT PendSV_Handler ; CMSIS-compliant PendSV exception name + EXPORT SVC_Handler ; CMSIS-compliant SVC exception name + + EXTERN QK_schedPrio_ ; external reference + EXTERN QK_sched_ ; external reference + + +;***************************************************************************** +; +; The QK_init function sets the priorities of PendSV and SVCall exceptions +; to 0xFF and 0x00, respectively. The function internally disables +; interrupts, but restores the original interrupt lock before exit. +; +;***************************************************************************** +QK_init + MRS r0,PRIMASK ; store the state of the PRIMASK in r0 + CPSID i ; disable interrupts (set PRIMASK) + + LDR r1,=0xE000ED18 ; System Handler Priority Register + LDR r2,[r1,#8] ; load the System 12-15 Priority Register + MOVS r3,#0xFF + LSLS r3,r3,#16 + ORRS r2,r3 ; set PRI_14 (PendSV) to 0xFF + STR r2,[r1,#8] ; write the System 12-15 Priority Register + LDR r2,[r1,#4] ; load the System 8-11 Priority Register + LSLS r3,r3,#8 + BICS r2,r3 ; set PRI_11 (SVCall) to 0x00 + STR r2,[r1,#4] ; write the System 8-11 Priority Register + + MSR PRIMASK,r0 ; restore the original PRIMASK + BX lr ; return to the caller + + +;***************************************************************************** +; +; The PendSV_Handler exception hanlder is used for handling asynchronous +; preemptions in QK. The use of the PendSV exception is the recommended +; and most efficient method for performing context switches with ARM Cortex. +; +; The PendSV exception should have the lowest priority in the whole system +; (0xFF, see QK_init). All other exeptions and interrupts should have higher +; priority. For example, for NVIC with 2 priority bits all interrupts and +; exceptions must have numerical value of priority lower than 0xC0. In this +; case the interrupt priority levels available to your applications are (in +; the order from the lowest urgency to the highest urgency): 0x80, 0x40, 0x00. +; +; Also, *all* ISRs in the QK application must trigger the PendSV exception +; by calling the QK_ISR_EXIT() macro. +; +; Due to tail-chaining and its lowest priority, the PendSV exception will be +; entered immediately after the exit from the *last* nested interrupt (or +; exception). In QK, this is exactly the time when the QK scheduler needs to +; check for the asynchronous preemptions. +; +;***************************************************************************** +PendSV_Handler + CPSID i ; disable interrupts at processor level + BL QK_schedPrio_ ; check if we have preemption + CMP r0,#0 ; is prio == 0 ? + BNE.N scheduler ; if prio != 0, branch to scheduler + + CPSIE i ; enable interrupts at processor level + MOVS r0,#0x6 + MVNS r0,r0 ; r0:=~0x6=0xFFFFFFF9 + BX r0 ; exception-return to the task + +scheduler + MOVS r3,#1 + LSLS r3,r3,#24 ; r3:=(1 << 24), set the T bit (new xpsr) + LDR r2,=QK_sched_ ; address of the QK scheduler (new pc) + LDR r1,=svc_ret ; return address after the call (new lr) + PUSH {r1-r3} ; push xpsr,pc,lr + SUB sp,sp,#(4*4) ; don't care for r12,r3,r2,r1 + PUSH {r0} ; push the prio argument (new r0) + MOVS r0,#0x6 + MVNS r0,r0 ; r0:=~0x6=0xFFFFFFF9 + BX r0 ; exception-return to the scheduler + +svc_ret + CPSIE i ; enable interrupts to allow SVCall exception + SVC 0 ; SV exception returns to the preempted task + + +;***************************************************************************** +; +; The SVC_Handler exception handler is used for returning back to the +; interrupted task. The SVCall exception simply removes its own interrupt +; stack frame from the stack and returns to the preempted task using the +; interrupt stack frame that must be at the top of the stack. +; +;***************************************************************************** +SVC_Handler + ADD sp,sp,#(8*4) ; remove one interrupt frame from the stack + BX lr ; return to the preempted task + + ALIGN ; make sure the END is properly aligned + END
diff -r 934bb9eea80b -r ca2e6010d9e2 qp.cpp --- 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
diff -r 934bb9eea80b -r ca2e6010d9e2 qp.h --- a/qp.h Mon Sep 26 03:27:09 2011 +0000 +++ b/qp.h Tue Sep 04 22:20:52 2012 +0000 @@ -1,3618 +1,3731 @@ -////////////////////////////////////////////////////////////////////////////// -// Product: QP/C++ -// 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 -////////////////////////////////////////////////////////////////////////////// -#ifndef qp_h -#define qp_h - -#ifdef Q_USE_NAMESPACE -namespace QP { -#endif - -// "qevent.h" ================================================================ -/// \brief QEvent class and basic macros used by all QP components. -/// -/// This header file must be included, perhaps indirectly, in all modules -/// (*.cpp files) that use any component of QP/C++ (such as QEP, QF, or QK). - -////////////////////////////////////////////////////////////////////////////// -/// \brief The current QP version number -/// -/// \return version of the QP as a hex constant constant 0xXYZZ, where X is -/// a 1-digit major version number, Y is a 1-digit minor version number, and -/// ZZ is a 2-digit release number. -#define QP_VERSION 0x4204U - -#ifndef Q_ROM - /// \brief Macro to specify compiler-specific directive for placing a - /// constant object in ROM. - /// - /// Many compilers for Harvard-architecture MCUs provide non-stanard - /// extensions to support placement of objects in different memories. - /// In order to conserve the precious RAM, QP uses the Q_ROM macro for - /// all constant objects that can be allocated in ROM. - /// - /// To override the following empty definition, you need to define the - /// Q_ROM macro in the qep_port.h header file. Some examples of valid - /// Q_ROM macro definitions are: __code (IAR 8051 compiler), code (Keil - /// Cx51 compiler), PROGMEM (gcc for AVR), __flash (IAR for AVR). - #define Q_ROM -#endif -#ifndef Q_ROM_VAR // if NOT defined, provide the default definition - /// \brief Macro to specify compiler-specific directive for accessing a - /// constant object in ROM. - /// - /// Many compilers for MCUs provide different size pointers for - /// accessing objects in various memories. Constant objects allocated - /// in ROM (see #Q_ROM macro) often mandate the use of specific-size - /// pointers (e.g., far pointers) to get access to ROM objects. The - /// macro Q_ROM_VAR specifies the kind of the pointer to be used to access - /// the ROM objects. - /// - /// To override the following empty definition, you need to define the - /// Q_ROM_VAR macro in the qep_port.h header file. An example of valid - /// Q_ROM_VAR macro definition is: __far (Freescale HC(S)08 compiler). - #define Q_ROM_VAR -#endif -#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 - -#ifndef Q_SIGNAL_SIZE - /// \brief The size (in bytes) of the signal of an event. Valid values: - /// 1, 2, or 4; default 1 - /// - /// This macro can be defined in the QEP port file (qep_port.h) to - /// configure the ::QSignal type. When the macro is not defined, the - /// default of 1 byte is chosen. - #define Q_SIGNAL_SIZE 2 -#endif -#if (Q_SIGNAL_SIZE == 1) - typedef uint8_t QSignal; -#elif (Q_SIGNAL_SIZE == 2) - /// \brief QSignal represents the signal of an event. - /// - /// The relationship between an event and a signal is as follows. A signal - /// in UML is the specification of an asynchronous stimulus that triggers - /// reactions [<A HREF="http://www.omg.org/docs/ptc/03-08-02.pdf">UML - /// document ptc/03-08-02</A>], and as such is an essential part of an - /// event. (The signal conveys the type of the occurrence-what happened?) - /// However, an event can also contain additional quantitative information - /// about the occurrence in form of event parameters. Please refer to the - typedef uint16_t QSignal; -#elif (Q_SIGNAL_SIZE == 4) - typedef uint32_t QSignal; -#else - #error "Q_SIGNAL_SIZE defined incorrectly, expected 1, 2, or 4" -#endif - -////////////////////////////////////////////////////////////////////////////// -/// \brief QEvent base class. -/// -/// QEvent represents events without parameters and serves as the base class -/// for derivation of events with parameters. -/// -/// \note All data members of the QEvent class must remain public to keep it -/// an AGGREGATE. Therefore, the attribute QEvent::dynamic_ cannot be -/// declared private. -/// -/// The following example illustrates how to add an event parameter by -/// inheriting from the QEvent class. -/// \include qep_qevent.cpp -struct QEvent { - QSignal sig; ///< signal of the event instance - uint8_t poolId_; ///< pool ID (0 for static event) - uint8_t refCtr_; ///< reference counter - -#ifdef Q_EVT_CTOR - QEvent(QSignal s) : sig(s) {} - virtual ~QEvent() {} // virtual destructor -#endif -}; - -////////////////////////////////////////////////////////////////////////////// -/// helper macro to calculate static dimension of a 1-dim array \a array_ -#define Q_DIM(array_) (sizeof(array_) / sizeof(array_[0])) - -// "qep.h" =================================================================== -/// \brief QEP/C++ platform-independent public interface. -/// -/// This header file must be included directly or indirectly -/// in all modules (*.cpp files) that use QEP/C++. - -////////////////////////////////////////////////////////////////////////////// -/// \brief Provides miscellaneous QEP services. -class QEP { -public: - /// \brief get the current QEP version number string - /// - /// \return version of the QEP as a constant 6-character string of the - /// form x.y.zz, where x is a 1-digit major version number, y is a - /// 1-digit minor version number, and zz is a 2-digit release number. - static char const Q_ROM * Q_ROM_VAR getVersion(void); -}; - -////////////////////////////////////////////////////////////////////////////// - - /// \brief Type returned from a state-handler function -typedef uint8_t QState; - - /// \brief pointer to state-handler function -typedef QState (*QStateHandler)(void *me, QEvent const *e); - - -////////////////////////////////////////////////////////////////////////////// -/// \brief Finite State Machine base class -/// -/// QFsm represents a traditional non-hierarchical Finite State Machine (FSM) -/// without state hierarchy, but with entry/exit actions. -/// -/// QFsm is also a base structure for the ::QHsm class. -/// -/// \note QFsm is not intended to be instantiated directly, but rather serves -/// as the base class for derivation of state machines in the application -/// code. -/// -/// The following example illustrates how to derive a state machine class -/// from QFsm. -/// \include qep_qfsm.cpp -class QFsm { -protected: - QStateHandler m_state; ///< current active state (state-variable) - -public: - /// \brief virtual destructor - virtual ~QFsm(); - - /// \brief Performs the second step of FSM initialization by triggering - /// the top-most initial transition. - /// - /// The argument \a e is constant pointer to ::QEvent or a class - /// derived from ::QEvent. - /// - /// \note Must be called only ONCE before QFsm::dispatch() - /// - /// The following example illustrates how to initialize a FSM, and - /// dispatch events to it: - /// \include qep_qfsm_use.cpp - void init(QEvent const *e = (QEvent *)0); - - /// \brief Dispatches an event to a FSM - /// - /// Processes one event at a time in Run-to-Completion (RTC) fashion. - /// The argument \a e is a constant pointer the ::QEvent or a - /// class derived from ::QEvent. - /// - /// \note Must be called after QFsm::init(). - /// - /// \sa example for QFsm::init() - void dispatch(QEvent const *e); - -protected: - - /// \brief Protected constructor of a FSM. - /// - /// Performs the first step of FSM initialization by assigning the - /// initial pseudostate to the currently active state of the state - /// machine. - /// - /// \note The constructor is protected to prevent direct instantiating - /// of QFsm objects. This class is intended for subclassing only. - /// - /// \sa The ::QFsm example illustrates how to use the QHsm constructor - /// in the constructor initializer list of the derived state machines. - QFsm(QStateHandler initial) : m_state(initial) {} -}; - -////////////////////////////////////////////////////////////////////////////// -/// \brief Hierarchical State Machine base class -/// -/// QHsm represents a Hierarchical Finite State Machine (HSM). QHsm derives -/// from the ::QFsm class and extends the capabilities of a basic FSM -/// with state hierarchy. -/// -/// \note QHsm is not intended to be instantiated directly, but rather serves -/// as the base structure for derivation of state machines in the application -/// code. -/// -/// The following example illustrates how to derive a state machine class -/// from QHsm. -/// \include qep_qhsm.cpp -class QHsm { -protected: - QStateHandler m_state; ///< current active state (state-variable) - -public: - /// \brief virtual destructor - virtual ~QHsm(); - - /// \brief Performs the second step of HSM initialization by triggering - /// the top-most initial transition. - /// - /// \param e constant pointer ::QEvent or a class derived from ::QEvent - /// \note Must be called only ONCE before QHsm::dispatch() - /// - /// The following example illustrates how to initialize a HSM, and - /// dispatch events to it: - /// \include qep_qhsm_use.cpp - void init(QEvent const *e = (QEvent *)0); - - /// \brief Dispatches an event to a HSM - /// - /// Processes one event at a time in Run-to-Completion (RTC) fashion. - /// The argument \a e is a constant pointer the ::QEvent or a - /// class derived from ::QEvent. - /// - /// \note Must be called after QHsm::init(). - /// - /// \sa example for QHsm::init() - void dispatch(QEvent const *e); - - /// \brief Tests if a given state is part of the current active state - /// configuratioin - /// - /// \param state is a pointer to the state handler function, e.g., - /// &QCalc::on. - uint8_t isIn(QStateHandler state); - -protected: - - /// \brief Protected constructor of a HSM. - /// - /// Performs the first step of HSM initialization by assigning the - /// initial pseudostate to the currently active state of the state - /// machine. - /// - /// \note The constructor is protected to prevent direct instantiating - /// of QHsm objects. This class is intended for subclassing only. - /// - /// \sa The ::QHsm example illustrates how to use the QHsm constructor - /// in the constructor initializer list of the derived state machines. - /// \sa QFsm::QFsm() - QHsm(QStateHandler initial) : m_state(initial) {} - - /// \brief the top-state. - /// - /// QHsm::top() is the ultimate root of state hierarchy in all HSMs - /// derived from ::QHsm. This state handler always returns (QSTATE)0, - /// which means that it "handles" all events. - /// - /// \sa Example of the QCalc::on() state handler. - static QState top(QHsm *me, QEvent const *e); -}; - -/// \brief Value returned by a non-hierarchical state-handler function when -/// it ignores (does not handle) the event. -#define Q_RET_IGNORED ((QState)1) - -/// \brief The macro returned from a non-hierarchical state-handler function -/// when it ignores (does not handle) the event. -/// -/// You call that macro after the return statement (return Q_IGNORED();) -/// -/// \include qepn_qfsm.cpp -#define Q_IGNORED() (Q_RET_IGNORED) - -/// \brief Value returned by a state-handler function when it handles -/// the event. -#define Q_RET_HANDLED ((QState)0) - -/// \brief Value returned by a state-handler function when it handles -/// the event. -/// -/// You call that macro after the return statement (return Q_HANDLED();) -/// Q_HANDLED() can be used both in the FSMs and HSMs. -/// -/// \include qepn_qfsm.cpp -#define Q_HANDLED() (Q_RET_HANDLED) - -/// \brief Value returned by a state-handler function when it takes a -/// regular state transition. -#define Q_RET_TRAN ((QState)2) - -/// \brief Designates a target for an initial or regular transition. -/// Q_TRAN() can be used both in the FSMs and HSMs. -/// -/// \include qepn_qtran.cpp -//lint -e960 -e1924 ignore MISRA Rule 42 (comma operator) and C-style cast -#define Q_TRAN(target_) \ - (me->m_state = (QStateHandler)(target_), Q_RET_TRAN) - -/// \brief Value returned by a state-handler function when it cannot -/// handle the event. -#define Q_RET_SUPER ((QState)3) - -/// \brief Designates the superstate of a given state in an HSM. -/// -/// \include qep_qhsm.cpp -//lint -e960 -e1924 ignore MISRA Rule 42 (comma operator) and C-style cast -#define Q_SUPER(super_) \ - (me->m_state = (QStateHandler)(super_), Q_RET_SUPER) - -////////////////////////////////////////////////////////////////////////////// -/// \brief QEP reserved signals. -enum QReservedSignals { - Q_ENTRY_SIG = 1, ///< signal for entry actions - Q_EXIT_SIG, ///< signal for exit actions - Q_INIT_SIG, ///< signal for nested initial transitions - Q_USER_SIG ///< signal to offset user signals -}; - -// "qequeue.h" =============================================================== -/// \brief platform-independent event queue interface. -/// -/// This header file must be included in all QF ports that use native QF -/// event queue implementation. Also, this file is needed when the "raw" -/// thread-safe queues are used for communication between active objects -/// and non-framework entities, such as ISRs, device drivers, or legacy -/// code. - -#ifndef QF_EQUEUE_CTR_SIZE - - /// \brief The size (in bytes) of the ring-buffer counters used in the - /// native QF event queue implementation. Valid values: 1, 2, or 4; - /// default 1. - /// - /// This macro can be defined in the QF port file (qf_port.h) to - /// configure the ::QEQueueCtr type. Here the macro is not defined so the - /// default of 1 byte is chosen. - #define QF_EQUEUE_CTR_SIZE 1 -#endif -#if (QF_EQUEUE_CTR_SIZE == 1) - - /// \brief The data type to store the ring-buffer counters based on - /// the macro #QF_EQUEUE_CTR_SIZE. - /// - /// The dynamic range of this data type determines the maximum length - /// of the ring buffer managed by the native QF event queue. - typedef uint8_t QEQueueCtr; -#elif (QF_EQUEUE_CTR_SIZE == 2) - typedef uint16_t QEQueueCtr; -#elif (QF_EQUEUE_CTR_SIZE == 4) - typedef uint32_t QEQueueCtr; -#else - #error "QF_EQUEUE_CTR_SIZE defined incorrectly, expected 1, 2, or 4" -#endif - - -////////////////////////////////////////////////////////////////////////////// -/// \brief Native QF Event Queue class -/// -/// This structure describes the native QF event queue, which can be used as -/// the event queue for active objects, or as a simple "raw" event queue for -/// thread-safe event passing among non-framework entities, such as ISRs, -/// device drivers, or other third-party components. -/// -/// The native QF event queue is configured by defining the macro -/// #QF_EQUEUE_TYPE as ::QEQueue in the specific QF port header file. -/// -/// The ::QEQueue structure contains only data members for managing an event -/// queue, but does not contain the storage for the queue buffer, which must -/// be provided externally during the queue initialization. -/// -/// The event queue can store only event pointers, not the whole events. The -/// internal implementation uses the standard ring-buffer plus one external -/// location that optimizes the queue operation for the most frequent case -/// of empty queue. -/// -/// The ::QEQueue structure is used with two sets of functions. One set is for -/// the active object event queue, which needs to block the active object -/// task when the event queue is empty and unblock it when events are posted -/// to the queue. The interface for the native active object event queue -/// consists of the following functions: QActive::postFIFO_(), -/// QActive::postLIFO_(), and QActive::get_(). Additionally the function -/// QEQueue_init() is used to initialize the queue. -/// -/// The other set of functions, uses this structure as a simple "raw" event -/// queue to pass events between entities other than active objects, such as -/// ISRs. The "raw" event queue is not capable of blocking on the get() -/// operation, but is still thread-safe because it uses QF critical section -/// to protect its integrity. The interface for the "raw" thread-safe queue -/// consists of the following functions: QEQueue::postFIFO(), -/// QEQueue::postLIFO(), and QEQueue::get(). Additionally the function -/// QEQueue::init() is used to initialize the queue. -/// -/// \note Most event queue operations (both the active object queues and -/// the "raw" queues) internally use the QF critical section. You should be -/// careful not to invoke those operations from other critical sections when -/// nesting of critical sections is not supported. -class QEQueue { -private: - - /// \brief pointer to event at the front of the queue - /// - /// All incoming and outgoing events pass through the m_frontEvt location. - /// When the queue is empty (which is most of the time), the extra - /// m_frontEvt location allows to bypass the ring buffer altogether, - /// greatly optimizing the performance of the queue. Only bursts of events - /// engage the ring buffer. - /// - /// The additional role of this attribute is to indicate the empty status - /// of the queue. The queue is empty if the m_frontEvt location is NULL. - QEvent const *m_frontEvt; - - /// \brief pointer to the start of the ring buffer - QEvent const **m_ring; - - /// \brief offset of the end of the ring buffer from the start of the - /// buffer m_ring - QEQueueCtr m_end; - - /// \brief offset to where next event will be inserted into the buffer - QEQueueCtr m_head; - - /// \brief offset of where next event will be extracted from the buffer - QEQueueCtr m_tail; - - /// \brief number of free events in the ring buffer - QEQueueCtr m_nFree; - - /// \brief minimum number of free events ever in the ring buffer. - /// - /// \note this attribute remembers the low-watermark of the ring buffer, - /// which provides a valuable information for sizing event queues. - /// \sa QF::getQueueMargin(). - QEQueueCtr m_nMin; - -public: - - /// \brief Initializes the native QF event queue - /// - /// The parameters are as follows: \a qSto[] is the ring buffer storage, - /// \a qLen is the length of the ring buffer in the units of event- - /// pointers. - /// - /// \note The actual capacity of the queue is qLen + 1, because of the - /// extra location fornEvt_. - void init(QEvent const *qSto[], QEQueueCtr qLen); - - /// \brief "raw" thread-safe QF event queue implementation for the - /// First-In-First-Out (FIFO) event posting. You can call this function - /// from any task context or ISR context. Please note that this function - /// uses internally a critical section. - /// - /// \note The function raises an assertion if the native QF queue becomes - /// full and cannot accept the event. - /// - /// \sa QEQueue::postLIFO(), QEQueue::get() - void postFIFO(QEvent const *e); - - /// \brief "raw" thread-safe QF event queue implementation for the - /// First-In-First-Out (FIFO) event posting. You can call this function - /// from any task context or ISR context. Please note that this function - /// uses internally a critical section. - /// - /// \note The function raises an assertion if the native QF queue becomes - /// full and cannot accept the event. - /// - /// \sa QEQueue::postLIFO(), QEQueue::get() - void postLIFO(QEvent const *e); - - /// \brief "raw" thread-safe QF event queue implementation for the - /// Last-In-First-Out (LIFO) event posting. - /// - /// \note The LIFO policy should be used only with great caution because - /// it alters order of events in the queue. - /// \note The function raises an assertion if the native QF queue becomes - /// full and cannot accept the event. You can call this function from - /// any task context or ISR context. Please note that this function uses - /// internally a critical section. - /// - /// \sa QEQueue::postFIFO(), QEQueue::get() - QEvent const *get(void); - - /// \brief "raw" thread-safe QF event queue operation for - /// obtaining the number of free entries still available in the queue. - /// - /// \note This operation needs to be used with caution because the - /// number of free entries can change unexpectedly. The main intent for - /// using this operation is in conjunction with event deferral. In this - /// case the queue is accessed only from a single thread (by a single AO), - /// so the number of free entries cannot change unexpectedly. - /// - /// \sa QActive::defer(), QActive::recall() - QEQueueCtr getNFree(void) const { - return m_nFree; - } - -private: - friend class QF; - friend class QActive; -}; - -// "qmpool.h" ================================================================ -/// \brief platform-independent memory pool interface. -/// -/// This header file must be included in all QF ports that use native QF -/// memory pool implementation. - - -////////////////////////////////////////////////////////////////////////////// -#ifndef QF_MPOOL_SIZ_SIZE - /// \brief macro to override the default ::QMPoolSize size. - /// Valid values 1, 2, or 4; default 2 - #define QF_MPOOL_SIZ_SIZE 2 -#endif -#if (QF_MPOOL_SIZ_SIZE == 1) - - /// \brief The data type to store the block-size based on the macro - /// #QF_MPOOL_SIZ_SIZE. - /// - /// The dynamic range of this data type determines the maximum size - /// of blocks that can be managed by the native QF event pool. - typedef uint8_t QMPoolSize; -#elif (QF_MPOOL_SIZ_SIZE == 2) - - typedef uint16_t QMPoolSize; -#elif (QF_MPOOL_SIZ_SIZE == 4) - typedef uint32_t QMPoolSize; -#else - #error "QF_MPOOL_SIZ_SIZE defined incorrectly, expected 1, 2, or 4" -#endif - -////////////////////////////////////////////////////////////////////////////// -#ifndef QF_MPOOL_CTR_SIZE - - /// \brief macro to override the default QMPoolCtr size. - /// Valid values 1, 2, or 4; default 2 - #define QF_MPOOL_CTR_SIZE 2 -#endif -#if (QF_MPOOL_CTR_SIZE == 1) - - /// \brief The data type to store the block-counter based on the macro - /// #QF_MPOOL_CTR_SIZE. - /// - /// The dynamic range of this data type determines the maximum number - /// of blocks that can be stored in the pool. - typedef uint8_t QMPoolCtr; -#elif (QF_MPOOL_CTR_SIZE == 2) - typedef uint16_t QMPoolCtr; -#elif (QF_MPOOL_CTR_SIZE == 4) - typedef uint32_t QMPoolCtr; -#else - #error "QF_MPOOL_CTR_SIZE defined incorrectly, expected 1, 2, or 4" -#endif - -////////////////////////////////////////////////////////////////////////////// -/// \brief Native QF memory pool class -/// -/// This class describes the native QF memory pool, which can be used as -/// the event pool for dynamic event allocation, or as a fast, deterministic -/// fixed block-size heap for any other objects in your application. -/// -/// The ::QMPool structure contains only data members for managing a memory -/// pool, but does not contain the pool storage, which must be provided -/// externally during the pool initialization. -/// -/// The native QF event pool is configured by defining the macro -/// #QF_EPOOL_TYPE_ as QEQueue in the specific QF port header file. -class QMPool { -private: - - /// start of the memory managed by this memory pool - void *m_start; - - /// end of the memory managed by this memory pool - void *m_end; - - /// linked list of free blocks - void *m_free; - - /// maximum block size (in bytes) - QMPoolSize m_blockSize; - - /// total number of blocks - QMPoolCtr m_nTot; - - /// number of free blocks remaining - QMPoolCtr m_nFree; - - /// minimum number of free blocks ever present in this pool - /// - /// \note this attribute remembers the low watermark of the pool, - /// which provides a valuable information for sizing event pools. - /// \sa QF::getPoolMargin(). - QMPoolCtr m_nMin; - -public: - - /// \brief Initializes the native QF event pool - /// - /// The parameters are as follows: \a poolSto is the pool storage, - /// \a poolSize is the size of the pool storage in bytes, and - /// \a blockSize is the block size of this pool. - /// - /// The caller of this method must make sure that the \a poolSto pointer - /// is properly aligned. In particular, it must be possible to efficiently - /// store a pointer at the location pointed to by \a poolSto. - /// Internally, the QMPool::init() function rounds up the block size - /// \a blockSize so that it can fit an integer number of pointers. - /// This is done to achieve proper alignment of the blocks within the - /// pool. - /// - /// \note Due to the rounding of block size the actual capacity of the - /// pool might be less than (\a poolSize / \a blockSize). You can check - /// the capacity of the pool by calling the QF::getPoolMargin() function. - void init(void *poolSto, uint32_t poolSize, QMPoolSize blockSize); - - /// \brief Obtains a memory block from a memory pool. - /// - /// The only parameter \a me is a pointer to the ::QMPool from which the - /// block is requested. The function returns a pointer to the allocated - /// memory block or NULL if no free blocks are available. - /// - /// A allocated block must be returned to the same pool from which it has - /// been allocated. - /// - /// This function can be called from any task level or ISR level. - /// - /// \note The memory pool \a me must be initialized before any events can - /// be requested from it. Also, the QMPool::get() function uses internally - /// a QF critical section, so you should be careful not to call it from - /// within a critical section when nesting of critical section is not - /// supported. - /// - /// \sa QMPool::put() - void *get(void); - - /// \brief Returns a memory block back to a memory pool. - /// - /// - /// This function can be called from any task level or ISR level. - /// - /// \note The block must be allocated from the same memory pool to which - /// it is returned. The QMPool::put() function raises an assertion if the - /// returned pointer to the block points outside of the original memory - /// buffer managed by the memory pool. Also, the QMPool::put() function - /// uses internally a QF critical section, so you should be careful not - /// to call it from within a critical section when nesting of critical - /// section is not supported. - /// - /// \sa QMPool::get() - void put(void *b); - - /// \brief return the fixed block-size of the blocks managed by this pool - QMPoolSize getBlockSize(void) const { - return m_blockSize; - } - -private: - friend class QF; -}; - -// "qpset.h" ================================================================= -/// \brief platform-independent priority sets of 8 or 64 elements. -/// -/// This header file must be included in those QF ports that use the -/// cooperative multitasking QF scheduler or the QK. - - // external declarations of QF lookup tables used inline -extern uint8_t const Q_ROM Q_ROM_VAR QF_log2Lkup[256]; -extern uint8_t const Q_ROM Q_ROM_VAR QF_pwr2Lkup[65]; -extern uint8_t const Q_ROM Q_ROM_VAR QF_invPwr2Lkup[65]; -extern uint8_t const Q_ROM Q_ROM_VAR QF_div8Lkup[65]; - -////////////////////////////////////////////////////////////////////////////// -/// \brief Priority Set of up to 8 elements for building various schedulers, -/// but also useful as a general set of up to 8 elements of any kind. -/// -/// The priority set represents the set of active objects that are ready to -/// run and need to be considered by scheduling processing. The set is capable -/// of storing up to 8 priority levels. -class QPSet8 { -protected: - ////////////////////////////////////////////////////////////////////////// - /// \brief bimask representing elements of the set - uint8_t m_bits; - -public: - - /// \brief the function evaluates to TRUE if the priority set is empty, - /// which means that no active objects are ready to run. - uint8_t isEmpty(void) volatile { - return (uint8_t)(m_bits == (uint8_t)0); - } - - /// \brief the function evaluates to TRUE if the priority set has elements, - /// which means that some active objects are ready to run. - uint8_t notEmpty(void) volatile { - return (uint8_t)(m_bits != (uint8_t)0); - } - - /// \brief the function evaluates to TRUE if the priority set has the - /// element \a n. - uint8_t hasElement(uint8_t n) volatile { - return (uint8_t)((m_bits & Q_ROM_BYTE(QF_pwr2Lkup[n])) != 0); - } - - /// \brief insert element \a n into the set, n = 1..8 - void insert(uint8_t n) volatile { - m_bits |= Q_ROM_BYTE(QF_pwr2Lkup[n]); - } - - /// \brief remove element \a n from the set, n = 1..8 - void remove(uint8_t n) volatile { - m_bits &= Q_ROM_BYTE(QF_invPwr2Lkup[n]); - } - - /// \brief find the maximum element in the set, - /// \note returns zero if the set is empty - uint8_t findMax(void) volatile { - return Q_ROM_BYTE(QF_log2Lkup[m_bits]); - } - - friend class QPSet64; -}; - -////////////////////////////////////////////////////////////////////////////// -/// \brief Priority Set of up to 64 elements for building various schedulers, -/// but also useful as a general set of up to 64 elements of any kind. -/// -/// The priority set represents the set of active objects that are ready to -/// run and need to be considered by scheduling processing. The set is capable -/// of storing up to 64 priority levels. -/// -/// The priority set allows to build cooperative multitasking schedulers -/// to manage up to 64 tasks. It is also used in the Quantum Kernel (QK) -/// preemptive scheduler. -/// -/// The inherited 8-bit set is used as the 8-elemtn set of of 8-bit subsets -/// Each bit in the super.bits set represents a subset (8-elements) -/// as follows: \n -/// bit 0 in this->m_bits is 1 when subset[0] is not empty \n -/// bit 1 in this->m_bits is 1 when subset[1] is not empty \n -/// bit 2 in this->m_bits is 1 when subset[2] is not empty \n -/// bit 3 in this->m_bits is 1 when subset[3] is not empty \n -/// bit 4 in this->m_bits is 1 when subset[4] is not empty \n -/// bit 5 in this->m_bits is 1 when subset[5] is not empty \n -/// bit 6 in this->m_bits is 1 when subset[6] is not empty \n -/// bit 7 in this->m_bits is 1 when subset[7] is not empty \n -class QPSet64 : public QPSet8 { - - /// \brief subsets representing elements in the set as follows: \n - /// m_subset[0] represent elements 1..8 \n - /// m_subset[1] represent elements 9..16 \n - /// m_subset[2] represent elements 17..24 \n - /// m_subset[3] represent elements 25..32 \n - /// m_subset[4] represent elements 33..40 \n - /// m_subset[5] represent elements 41..48 \n - /// m_subset[6] represent elements 49..56 \n - /// m_subset[7] represent elements 57..64 \n - QPSet8 m_subset[8]; - -public: - - /// \brief the function evaluates to TRUE if the priority set has the - /// element \a n. - uint8_t hasElement(uint8_t n) volatile { - return m_subset[Q_ROM_BYTE(QF_div8Lkup[n])].QPSet8::hasElement(n); - } - - /// \brief insert element \a n into the set, n = 1..64 - void insert(uint8_t n) volatile { - QPSet8::insert(Q_ROM_BYTE(QF_div8Lkup[n]) + 1); - m_subset[Q_ROM_BYTE(QF_div8Lkup[n])].QPSet8::insert(n); - } - - /// \brief remove element \a n from the set, n = 1..64 - void remove(uint8_t n) volatile { - if ((m_subset[Q_ROM_BYTE(QF_div8Lkup[n])].m_bits - &= Q_ROM_BYTE(QF_invPwr2Lkup[n])) == (uint8_t)0) - { - QPSet8::remove(Q_ROM_BYTE(QF_div8Lkup[n]) + 1); - } - } - - /// \brief find the maximum element in the set, - /// \note returns zero if the set is empty - uint8_t findMax(void) volatile { - if (m_bits != (uint8_t)0) { - uint8_t n = (uint8_t)(Q_ROM_BYTE(QF_log2Lkup[m_bits]) - 1); - return (uint8_t)(Q_ROM_BYTE(QF_log2Lkup[m_subset[n].m_bits]) - + (n << 3)); - } - else { - return (uint8_t)0; - } - } -}; - - -////////////////////////////////////////////////////////////////////////////// -// Kernel selection based on QK_PREEMPTIVE -// -#ifdef QK_PREEMPTIVE - -/// \brief This macro defines the type of the event queue used for the -/// active objects. -/// -/// \note This is just an example of the macro definition. Typically, you need -/// to define it in the specific QF port file (qf_port.h). In case of QK, -/// which always depends on the native QF queue, this macro is defined at the -/// level of the platform-independent interface qk.h. -#define QF_EQUEUE_TYPE QEQueue - -#if defined(QK_TLS) || defined(QK_EXT_SAVE) - /// \brief This macro defines the type of the OS-Object used for blocking - /// the native QF event queue when the queue is empty - /// - /// In QK, the OS object is used to hold the per-thread flags, which might - /// be used, for example, to rembember the thread attributes (e.g., - /// if the thread uses a floating point co-processor). The OS object value - /// is set on per-thread basis in QActive::start(). Later, the extended - /// context switch macros (QK_EXT_SAVE() and QK_EXT_RESTORE()) might use - /// the per-thread flags to determine what kind of extended context switch - /// this particular thread needs (e.g., the thread might not be using the - /// coprocessor or might be using a different one). - #define QF_OS_OBJECT_TYPE uint8_t - - /// \brief This macro defines the type of the thread handle used for the - /// active objects. - /// - /// The thread type in QK is the pointer to the thread-local storage (TLS) - /// This thread-local storage can be set on per-thread basis in - /// QActive::start(). Later, the QK scheduler, passes the pointer to the - /// thread-local storage to the macro #QK_TLS. - #define QF_THREAD_TYPE void * -#endif /* QK_TLS || QK_EXT_SAVE */ - -#if (QF_MAX_ACTIVE <= 8) - extern QPSet8 volatile QK_readySet_; ///< ready set of QK -#else - extern QPSet64 volatile QK_readySet_; ///< ready set of QK -#endif - -#ifdef Q_USE_NAMESPACE -} // namespace QP -#endif - -extern "C" { - -extern uint8_t volatile QK_currPrio_; ///< current task/interrupt priority -extern uint8_t volatile QK_intNest_; ///< interrupt nesting level - -} // extern "C" - -#ifdef Q_USE_NAMESPACE -namespace QP { -#endif - -// QK active object queue implementation ..................................... - -/// \brief Platform-dependent macro defining how QF should block the calling -/// task when the QF native queue is empty -/// -/// \note This is just an example of #QACTIVE_EQUEUE_WAIT_ for the QK-port -/// of QF. QK never activates a task that has no events to process, so in this -/// case the macro asserts that the queue is not empty. In other QF ports you -// need to define the macro appropriately for the underlying kernel/OS you're -/// using. -#define QACTIVE_EQUEUE_WAIT_(me_) \ - Q_ASSERT((me_)->m_eQueue.m_frontEvt != (QEvent *)0) - -/// \brief Platform-dependent macro defining how QF should signal the -/// active object task that an event has just arrived. -/// -/// The macro is necessary only when the native QF event queue is used. -/// The signaling of task involves unblocking the task if it is blocked. -/// -/// \note #QACTIVE_EQUEUE_SIGNAL_ is called from a critical section. -/// It might leave the critical section internally, but must restore -/// the critical section before exiting to the caller. -/// -/// \note This is just an example of #QACTIVE_EQUEUE_SIGNAL_ for the QK-port -/// of QF. In other QF ports you need to define the macro appropriately for -/// the underlying kernel/OS you're using. -#define QACTIVE_EQUEUE_SIGNAL_(me_) \ - QK_readySet_.insert((me_)->m_prio); \ - if (QK_intNest_ == (uint8_t)0) { \ - QK_SCHEDULE_(); \ - } else ((void)0) - -/// \brief Platform-dependent macro defining the action QF should take -/// when the native QF event queue becomes empty. -/// -/// \note #QACTIVE_EQUEUE_ONEMPTY_ is called from a critical section. -/// It should not leave the critical section. -/// -/// \note This is just an example of #QACTIVE_EQUEUE_ONEMPTY_ for the QK-port -/// of QF. In other QF ports you need to define the macro appropriately for -/// the underlying kernel/OS you're using. -#define QACTIVE_EQUEUE_ONEMPTY_(me_) \ - QK_readySet_.remove((me_)->m_prio) - -// QK event pool operations .................................................. - -/// \brief This macro defines the type of the event pool used in this QF port. -/// -/// \note This is just an example of the macro definition. Typically, you need -/// to define it in the specific QF port file (qf_port.h). In case of QK, -/// which always depends on the native QF memory pool, this macro is defined -/// at the level of the platform-independent interface qk.h. -#define QF_EPOOL_TYPE_ QMPool - -/// \brief Platform-dependent macro defining the event pool initialization -/// -/// \note This is just an example of #QF_EPOOL_INIT_ for the QK-port of QF. -/// In other QF ports you need to define the macro appropriately for the -/// underlying kernel/OS you're using. -#define QF_EPOOL_INIT_(p_, poolSto_, poolSize_, evtSize_) \ - (p_).init(poolSto_, poolSize_, evtSize_) - -/// \brief Platform-dependent macro defining how QF should obtain the -/// event pool block-size -/// -/// \note This is just an example of #QF_EPOOL_EVENT_SIZE_ for the QK-port -/// of QF. In other QF ports you need to define the macro appropriately for -/// the underlying kernel/OS you're using. -#define QF_EPOOL_EVENT_SIZE_(p_) ((p_).getBlockSize()) - -/// \brief Platform-dependent macro defining how QF should obtain an event -/// \a e_ from the event pool \a p_ -/// -/// \note This is just an example of #QF_EPOOL_GET_ for the QK-port of QF. -/// In other QF ports you need to define the macro appropriately for the -/// underlying kernel/OS you're using. -#define QF_EPOOL_GET_(p_, e_) ((e_) = (QEvent *)(p_).get()) - -/// \brief Platform-dependent macro defining how QF should return an event -/// \a e_ to the event pool \a p_ -/// -/// \note This is just an example of #QF_EPOOL_PUT_ for the QK-port of QF. -/// In other QF ports you need to define the macro appropriately for the -/// underlying kernel/OS you're using. -#define QF_EPOOL_PUT_(p_, e_) ((p_).put(e_)) - -#ifndef QK_NO_MUTEX - ////////////////////////////////////////////////////////////////////////// - /// \brief QK Mutex type. - /// - /// QMutex represents the priority-ceiling mutex available in QK. - /// \sa QK::mutexLock() - /// \sa QK::mutexUnlock() - typedef uint8_t QMutex; -#endif // QK_NO_MUTEX - -////////////////////////////////////////////////////////////////////////////// -/// \brief QK services. -/// -/// This class groups together QK services. It has only static members and -/// should not be instantiated. -/// -// \note The QK scheduler, QK priority, QK ready set, etc. belong conceptually -/// to the QK class (as static class members). However, to avoid C++ potential -/// name-mangling problems in assembly language, these elements are defined -/// outside of the QK class and use the extern "C" linkage specification. -/// -class QK { -public: - - /// \brief get the current QK version number string - /// - /// \return version of the QK as a constant 6-character string of the - /// form x.y.zz, where x is a 1-digit major version number, y is a - /// 1-digit minor version number, and zz is a 2-digit release number. - /// - /// \sa QK::getPortVersion() - static char const Q_ROM * Q_ROM_VAR getVersion(void); - - /// \brief Returns the QK-port version. - /// - /// This function returns constant version string in the format x.y.zz, - /// where x (one digit) is the major version, y (one digit) is the minor - /// version, and zz (two digits) is the maintenance release version. - /// An example of the QK-port version string is "1.1.03". - /// - /// \sa QK::getVersion() - static char const Q_ROM * Q_ROM_VAR getPortVersion(void); - - /// \brief QK idle callback (customized in BSPs for QK) - /// - /// QK::onIdle() is called continously by the QK idle loop. This callback - /// gives the application an opportunity to enter a power-saving CPU mode, - /// or perform some other idle processing. - /// - /// \note QK::onIdle() is invoked with interrupts unlocked and must also - /// return with interrupts unlocked. - /// - /// \sa QF::onIdle() - static void onIdle(void); - -#ifndef QK_NO_MUTEX - - /// \brief QK priority-ceiling mutex lock - /// - /// Lock the QK scheduler up to the priority level \a prioCeiling. - /// - // \note This function should be always paired with QK::mutexUnlock(). - /// The code between QK::mutexLock() and QK::mutexUnlock() should be - /// kept to the minimum. - /// - /// \include qk_mux.cpp - static QMutex mutexLock(uint8_t prioCeiling); - - /// \brief QK priority-ceiling mutex unlock - /// - /// \note This function should be always paired with QK::mutexLock(). - /// The code between QK::mutexLock() and QK::mutexUnlock() should be - /// kept to the minimum. - /// - /// \include qk_mux.cpp - static void mutexUnlock(QMutex mutex); - -#endif // QK_NO_MUTEX - -}; - -#ifdef Q_USE_NAMESPACE -} // namespace QP -#endif - -extern "C" { - -/// \brief QK initialization -/// -/// QK_init() is called from QF_init() in qk.cpp. This function is -/// defined in the QK ports. -void QK_init(void); - -// QK scheduler and extended scheduler -#ifndef QF_INT_KEY_TYPE - void QK_schedule_(void); // QK scheduler - void QK_scheduleExt_(void); // QK extended scheduler - - #define QK_SCHEDULE_() QK_schedule_() -#else - - /// \brief The QK scheduler - /// - /// \note The QK scheduler must be always called with the interrupts - /// locked and unlocks interrupts internally. - /// - /// The signature of QK_schedule_() depends on the policy of locking and - /// unlocking interrupts. When the interrupt lock key is not used - /// (#QF_INT_KEY_TYPE undefined), the signature is as follows: \n - /// void QK_schedule_(void); \n - /// - /// However, when the interrupt key lock is used (#QF_INT_KEY_TYPE - /// defined), the signature is different: \n - /// void QK_schedule_(QF_INT_KEY_TYPE intLockKey); \n - /// - /// For the internal use, these differences are hidden by the macro - /// #QK_SCHEDULE_. - void QK_schedule_(QF_INT_KEY_TYPE intLockKey); - - /// \brief The QK extended scheduler for interrupt context - /// - /// \note The QK extended exscheduler must be always called with the - /// interrupts locked and unlocks interrupts internally. - /// - /// The signature of QK_scheduleExt_() depends on the policy of locking - /// and unlocking interrupts. When the interrupt lock key is not used - /// (#QF_INT_KEY_TYPE undefined), the signature is as follows: \n - /// void QK_scheduleExt_(void); \n - /// - /// However, when the interrupt key lock is used (#QF_INT_KEY_TYPE - /// defined), the signature is different: \n - /// void QK_scheduleExt_(QF_INT_KEY_TYPE intLockKey); \n - void QK_scheduleExt_(QF_INT_KEY_TYPE intLockKey); // QK extended scheduler - - /// #QF_INT_KEY_TYPE is defined, this internal macro invokes - /// QK_schedule_() passing the key variable as the parameter. Otherwise - /// QK_schedule_() is invoked without parameters. - /// \sa #QK_INT_LOCK, #QK_INT_UNLOCK - #define QK_SCHEDULE_() QK_schedule_(intLockKey_) - -#endif -} // extern "C" - -#ifdef Q_USE_NAMESPACE -namespace QP { -#endif - -#else // QK_PREEMPTIVE - -// "qvanilla.h" ============================================================== -#define QF_EQUEUE_TYPE QEQueue - // native event queue operations -#define QACTIVE_EQUEUE_WAIT_(me_) \ - Q_ASSERT((me_)->m_eQueue.m_frontEvt != (QEvent *)0) - -#define QACTIVE_EQUEUE_SIGNAL_(me_) \ - QF_readySet_.insert((me_)->m_prio) - -#define QACTIVE_EQUEUE_ONEMPTY_(me_) \ - QF_readySet_.remove((me_)->m_prio) - - // native QF event pool operations -#define QF_EPOOL_TYPE_ QMPool -#define QF_EPOOL_INIT_(p_, poolSto_, poolSize_, evtSize_) \ - (p_).init(poolSto_, poolSize_, evtSize_) -#define QF_EPOOL_EVENT_SIZE_(p_) ((p_).getBlockSize()) -#define QF_EPOOL_GET_(p_, e_) ((e_) = (QEvent *)(p_).get()) -#define QF_EPOOL_PUT_(p_, e_) ((p_).put(e_)) - -#if (QF_MAX_ACTIVE <= 8) - extern QPSet8 volatile QF_readySet_; ///< QF-ready set of active objects -#else - extern QPSet64 volatile QF_readySet_; ///< QF-ready set of active objects -#endif - -#endif // QK_PREEMPTIVE - - -// qf.h (QF platform-independent public interface) =========================== -////////////////////////////////////////////////////////////////////////////// -#if (QF_MAX_ACTIVE < 1) || (63 < QF_MAX_ACTIVE) - #error "QF_MAX_ACTIVE not defined or out of range. Valid range is 1..63" -#endif - -////////////////////////////////////////////////////////////////////////////// -#ifndef QF_EVENT_SIZ_SIZE - - /// \brief Default value of the macro configurable value in qf_port.h - #define QF_EVENT_SIZ_SIZE 2 -#endif -#if (QF_EVENT_SIZ_SIZE == 1) - - /// \brief The data type to store the block-size defined based on - /// the macro #QF_EVENT_SIZ_SIZE. - /// - /// The dynamic range of this data type determines the maximum block - /// size that can be managed by the pool. - typedef uint8_t QEventSize; -#elif (QF_EVENT_SIZ_SIZE == 2) - typedef uint16_t QEventSize; -#elif (QF_EVENT_SIZ_SIZE == 4) - typedef uint32_t QEventSize; -#else - #error "QF_EVENT_SIZ_SIZE defined incorrectly, expected 1, 2, or 4" -#endif - -////////////////////////////////////////////////////////////////////////////// -#ifndef QF_MAX_EPOOL - /// \brief Default value of the macro configurable value in qf_port.h - #define QF_MAX_EPOOL 3 -#endif - -////////////////////////////////////////////////////////////////////////////// -#ifndef QF_ACTIVE_SUPER_ - - ////////////////////////////////////////////////////////////////////////// - /// \brief The macro defining the base class for QActive. - /// - /// By default, the ::QActive class is derived from ::QHsm. However, - /// if the macro QF_ACTIVE_SUPER_ is defined, QActive is derived from - /// QF_ACTIVE_SUPER_. - /// - /// Clients might choose, for example, to define QF_ACTIVE_SUPER_ as QFsm - /// to avoid the 1-2KB overhead of the hierarchical event processor. - /// - /// Clients might also choose to define QF_ACTIVE_SUPER_ as their own - /// completely customized class that has nothing to do with QHsm or QFsm. - /// The QF_ACTIVE_SUPER_ class must provide member functions init() and - /// dispatch(), consistent with the signatures of QHsm and QFsm. But - /// the implementatin of these functions is completely open. - #define QF_ACTIVE_SUPER_ QHsm - - /// \brief The argument of the base class' constructor. - #define QF_ACTIVE_STATE_ QStateHandler - -#endif - -class QEQueue; // forward declaration - - -/// \brief Base class for derivation of application-level active object -/// classes. -/// -/// QActive is the base class for derivation of active objects. Active objects -/// in QF are encapsulated tasks (each embedding a state machine and an event -/// queue) that communicate with one another asynchronously by sending and -/// receiving events. Within an active object, events are processed -/// sequentially in a run-to-completion (RTC) fashion, while QF encapsulates -/// all the details of thread-safe event exchange and queuing. -/// -/// \note QActive is not intended to be instantiated directly, but rather -/// serves as the base class for derivation of active objects in the -/// application code. -/// -/// The following example illustrates how to derive an active object from -/// QActive. -/// \include qf_qactive.cpp -/// -/// \sa #QF_ACTIVE_SUPER_ defines the base class for QActive -class QActive : public QF_ACTIVE_SUPER_ { -private: - - /// \brief OS-dependent event-queue type. - /// - /// The type of the queue depends on the underlying operating system or - /// a kernel. Many kernels support "message queues" that can be adapted - /// to deliver QF events to the active object. Alternatively, QF provides - /// a native event queue implementation that can be used as well. - /// - /// The native QF event queue is configured by defining the macro - /// #QF_EQUEUE_TYPE as ::QEQueue. - QF_EQUEUE_TYPE m_eQueue; - -public: -#ifdef QF_OS_OBJECT_TYPE - /// \brief OS-dependent per-thread object. - /// - /// This data might be used in various ways, depending on the QF port. - /// In some ports m_osObject is used to block the calling thread when - /// the native QF queue is empty. In other QF ports the OS-dependent - /// object might be used differently. - QF_OS_OBJECT_TYPE m_osObject; -#endif - -#ifdef QF_THREAD_TYPE - /// \brief OS-dependent representation of the thread of the active - /// object. - /// - /// This data might be used in various ways, depending on the QF port. - /// In some ports m_thread is used store the thread handle. In other ports - /// m_thread can be the pointer to the Thread-Local-Storage (TLS). - QF_THREAD_TYPE m_thread; -#endif - - /// \brief QF priority associated with the active object. - /// \sa QActive::start() - uint8_t m_prio; - - /// \brief The Boolean loop variable determining if the thread routine - /// of the active object is running. - /// - /// This flag is only used with the traditional loop-structured thread - /// routines. Clearing this flag breaks out of the thread loop, which is - /// often the cleanest way to terminate the thread. The following example - /// illustrates the thread routine for Win32: - /// \include qf_run.cpp - uint8_t m_running; - -public: - - /// \brief Starts execution of an active object and registers the object - /// with the framework. - /// - /// The function takes six arguments. - /// \a prio is the priority of the active object. QF allows you to start - /// up to 63 active objects, each one having a unique priority number - /// between 1 and 63 inclusive, where higher numerical values correspond - /// to higher priority (urgency) of the active object relative to the - /// others. - /// \a qSto[] and \a qLen arguments are the storage and size of the event - /// queue used by this active object. - /// \a stkSto and \a stkSize are the stack storage and size in bytes. - /// Please note that a per-active object stack is used only when the - /// underlying OS requies it. If the stack is not required, or the - /// underlying OS allocates the stack internally, the \a stkSto should be - /// NULL and/or \a stkSize should be 0. - /// \a ie is an optional initialization event that can be used to pass - /// additional startup data to the active object. (Pass NULL if your - /// active object does not expect the initialization event). - /// - /// \note This function is strongly OS-dependent and must be defined in - /// the QF port to a particular platform. - /// - /// The following example shows starting of the Philosopher object when a - /// per-task stack is required: - /// \include qf_start.cpp - void start(uint8_t prio, - QEvent const *qSto[], uint32_t qLen, - void *stkSto, uint32_t stkSize, - QEvent const *ie = (QEvent *)0); - - /// \brief Posts an event \a e directly to the event queue of the acitve - /// object \a me using the First-In-First-Out (FIFO) policy. - /// - /// Direct event posting is the simplest asynchronous communication method - /// available in QF. The following example illustrates how the Philosopher - /// active obejct posts directly the HUNGRY event to the Table active - /// object. \include qf_post.cpp - /// - /// \note The producer of the event (Philosopher in this case) must only - /// "know" the recipient (Table) by a generic (QActive *QDPP_table) - /// pointer, but the specific definition of the Table class is not - /// required. - /// - /// \note Direct event posting should not be confused with direct event - /// dispatching. In contrast to asynchronous event posting through event - /// queues, direct event dispatching is synchronous. Direct event - /// dispatching occurs when you call QHsm::dispatch(), or QFsm::dispatch() - /// function. -#ifndef Q_SPY - void postFIFO(QEvent const *e); -#else - void postFIFO(QEvent const *e, void const *sender); -#endif - - /// \brief Posts an event directly to the event queue of the active object - /// \a me using the Last-In-First-Out (LIFO) policy. - /// - /// \note The LIFO policy should be used only for self-posting and with - /// great caution because it alters order of events in the queue. - /// - /// \sa QActive::postFIFO() - void postLIFO(QEvent const *e); - - /// \brief Traditional loop-structured thread routine for an active object - /// - /// This function is only used when QF is ported to a traditional - /// RTOS/Kernel. QActive::run() is structured as a typical endless loop, - /// which blocks on the event queue get() operation of an active object. - /// When an event becomes available, it's dispatched to the active - /// object's state machine and after this recycled with QF::gc(). - /// The loop might optionally use the QActive::m_running flag to terminate - /// and cause QActive::run() to return which is often the cleanest way to - /// terminate the thread. - void run(void); - - /// \brief Get an event from the event queue of an active object. - /// - /// This function is used internally by a QF port to extract events from - /// the event queue of an active object. This function depends on the - /// event queue implementation and is sometimes implemented in the QF port - /// (qf_port.cpp file). Depending on the underlying OS or kernel, the - /// function might block the calling thread when no events are available. - /// - /// \note QActive::get_() is public because it often needs to be called - /// from thread-run routines with difficult to foresee signature (so - /// declaring friendship with such function(s) is not possible.) - /// - /// \sa QActive::postFIFO(), QActive::postLIFO() - QEvent const *get_(void); - -protected: - - /// \brief protected constructor - /// - /// Performs the first step of active object initialization by assigning - /// the initial pseudostate to the currently active state of the state - /// machine. - /// - /// \note The constructor is protected to prevent direct instantiation - /// of QActive objects. This class is intended only for derivation - /// (abstract class). - QActive(QF_ACTIVE_STATE_ initial) : QF_ACTIVE_SUPER_(initial) { - } - - /// \brief Stops execution of an active object and removes it from the - /// framework's supervision. - /// - /// The preferred way of calling this function is from within the active - /// object that needs to stop (that's why this function is protected). - /// In other words, an active object should stop itself rather than being - /// stopped by some other entity. This policy works best, because only - /// the active object itself "knows" when it has reached the appropriate - /// state for the shutdown. - /// - /// \note This function is strongly OS-dependent and should be defined in - /// the QF port to a particular platform. This function is optional in - /// embedded systems where active objects never need to be stopped. - void stop(void); - - /// \brief Subscribes for delivery of signal \a sig to the active object - /// - /// This function is part of the Publish-Subscribe event delivery - /// mechanism available in QF. Subscribing to an event means that the - /// framework will start posting all published events with a given signal - /// \a sig to the event queue of the active object. - /// - /// The following example shows how the Table active object subscribes - /// to three signals in the initial transition: - /// \include qf_subscribe.cpp - /// - /// \sa QF::publish(), QActive::unsubscribe(), and - /// QActive::unsubscribeAll() - void subscribe(QSignal sig) const; - - /// \brief Un-subscribes from the delivery of signal \a sig to the - /// active object. - /// - /// This function is part of the Publish-Subscribe event delivery - /// mechanism available in QF. Un-subscribing from an event means that - /// the framework will stop posting published events with a given signal - /// \a sig to the event queue of the active object. - /// - /// \note Due to the latency of event queues, an active object should NOT - /// assume that a given signal \a sig will never be dispatched to the - /// state machine of the active object after un-subscribing from that - /// signal. The event might be already in the queue, or just about to be - /// posted and the un-subscribe operation will not flush such events. - /// - /// \note Un-subscribing from a signal that has never been subscribed in - /// the first place is considered an error and QF will rise an assertion. - /// - /// \sa QF::publish(), QActive::subscribe(), and QActive::unsubscribeAll() - void unsubscribe(QSignal sig) const; - - /// \brief Defer an event to a given separate event queue. - /// - /// This function is part of the event deferral support. An active object - /// uses this function to defer an event \a e to the QF-supported native - /// event queue \a eq. QF correctly accounts for another outstanding - /// reference to the event and will not recycle the event at the end of - /// the RTC step. Later, the active object might recall one event at a - /// time from the event queue. - /// - /// An active object can use multiple event queues to defer events of - /// different kinds. - /// - /// \sa QActive::recall(), QEQueue - void defer(QEQueue *eq, QEvent const *e); - - /// \brief Recall a deferred event from a given event queue. - /// - /// This function is part of the event deferral support. An active object - /// uses this function to recall a deferred event from a given QF - /// event queue. Recalling an event means that it is removed from the - /// deferred event queue \a eq and posted (LIFO) to the event queue of - /// the active object. - /// - /// QActive::recall() returns 1 (TRUE) if an event has been recalled. - /// Otherwise the function returns 0. - /// - /// An active object can use multiple event queues to defer events of - /// different kinds. - /// - /// \sa QActive::defer(), QEQueue, QActive::postLIFO() - uint8_t recall(QEQueue *eq); - -public: - /// \brief Un-subscribes from the delivery of all signals to the active - /// object. - /// - /// This function is part of the Publish-Subscribe event delivery - /// mechanism available in QF. Un-subscribing from all events means that - /// the framework will stop posting any published events to the event - /// queue of the active object. - /// - /// \note Due to the latency of event queues, an active object should NOT - /// assume that no events will ever be dispatched to the state machine of - /// the active object after un-subscribing from all events. - /// The events might be already in the queue, or just about to be posted - /// and the un-subscribe operation will not flush such events. Also, the - /// alternative event-delivery mechanisms, such as direct event posting or - /// time events, can be still delivered to the event queue of the active - /// object. - /// - /// \sa QF::publish(), QActive::subscribe(), and QActive::unsubscribe() - void unsubscribeAll(void) const; - -private: - - friend class QF; - friend class QTimeEvt; - #ifndef QF_INT_KEY_TYPE - friend void QK_schedule_(void); - friend void QK_scheduleExt_(void); - #else - friend void QK_schedule_(QF_INT_KEY_TYPE intLockKey); - friend void QK_scheduleExt_(QF_INT_KEY_TYPE intLockKey); - #endif -}; - - -////////////////////////////////////////////////////////////////////////////// -#ifndef QF_TIMEEVT_CTR_SIZE - /// \brief macro to override the default QTimeEvtCtr size. - /// Valid values 1, 2, or 4; default 2 - #define QF_TIMEEVT_CTR_SIZE 2 -#endif -#if (QF_TIMEEVT_CTR_SIZE == 1) - - /// \brief type of the Time Event counter, which determines the dynamic - /// range of the time delays measured in clock ticks. - /// - /// This typedef is configurable via the preprocessor switch - /// #QF_TIMEEVT_CTR_SIZE. The other possible values of this type are - /// as follows: \n - /// uint8_t when (QF_TIMEEVT_CTR_SIZE == 1), and \n - /// uint32_t when (QF_TIMEEVT_CTR_SIZE == 4). - typedef uint8_t QTimeEvtCtr; -#elif (QF_TIMEEVT_CTR_SIZE == 2) - typedef uint16_t QTimeEvtCtr; -#elif (QF_TIMEEVT_CTR_SIZE == 4) - typedef uint32_t QTimeEvtCtr; -#else - #error "QF_TIMEEVT_CTR_SIZE defined incorrectly, expected 1, 2, or 4" -#endif - -////////////////////////////////////////////////////////////////////////////// -/// \brief Time Event class -/// -/// Time events are special QF events equipped with the notion of time -/// passage. The basic usage model of the time events is as follows. An -/// active object allocates one or more QTimeEvt objects (provides the -/// storage for them). When the active object needs to arrange for a timeout, -/// it arms one of its time events to fire either just once (one-shot) or -/// periodically. Each time event times out independently from the others, -/// so a QF application can make multiple parallel timeout requests (from the -/// same or different active objects). When QF detects that the appropriate -/// moment has arrived, it inserts the time event directly into the -/// recipient's event queue. The recipient then processes the time event just -/// like any other event. -/// -/// Time events, as any other QF events derive from the ::QEvent base -/// class. Typically, you will use a time event as-is, but you can also -/// further derive more specialized time events from it by adding some more -/// data members and/or specialized functions that operate on the specialized -/// time events. -/// -/// Internally, the armed time events are organized into a bi-directional -/// linked list. This linked list is scanned in every invocation of the -/// QF::tick() function. Only armed (timing out) time events are in the list, -/// so only armed time events consume CPU cycles. -/// -/// \note QF manages the time events in the function QF::tick(), which -/// must be called periodically, preferably from the clock tick ISR. -/// \note In this version of QF QTimeEvt objects should be allocated -/// statically rather than dynamically from event pools. Currently, QF will -/// not correctly recycle the dynamically allocated Time Events. -class QTimeEvt : public QEvent { -private: - - //// link to the previous time event in the list - QTimeEvt *m_prev; - - /// link to the next time event in the list - QTimeEvt *m_next; - - /// the active object that receives the time events. - QActive *m_act; - - /// the internal down-counter of the time event. The down-counter - /// is decremented by 1 in every QF_tick() invocation. The time event - /// fires (gets posted or published) when the down-counter reaches zero. - QTimeEvtCtr m_ctr; - - /// the interval for the periodic time event (zero for the one-shot - /// time event). The value of the interval is re-loaded to the internal - /// down-counter when the time event expires, so that the time event - /// keeps timing out periodically. - QTimeEvtCtr m_interval; - -public: - - /// \brief The Time Event constructor. - /// - /// The most important initialization performed in the constructor is - /// assigning a signal to the Time Event. You can reuse the Time Event - /// any number of times, but you cannot change the signal. - /// This is because pointers to Time Events might still be held in event - /// queues and changing signal could to hard-to-detect errors. - /// - /// The following example shows the use of QTimeEvt::QTimeEvt() - /// constructor in the constructor initializer list of the Philosopher - /// active object constructor that owns the time event - /// \include qf_ctor.cpp - QTimeEvt(QSignal s); - - /// \brief Arm a one-shot time event for direct event posting. - /// - /// Arms a time event to fire in \a nTicks clock ticks (one-shot time - /// event). The time event gets directly posted (using the FIFO policy) - /// into the event queue of the active object \a act. - /// - /// After posting, the time event gets automatically disarmed and can be - /// reused for a one-shot or periodic timeout requests. - /// - /// A one-shot time event can be disarmed at any time by calling the - /// QTimeEvt::disarm() function. Also, a one-shot time event can be - /// re-armed to fire in a different number of clock ticks by calling the - /// QTimeEvt::rearm() function. - /// - /// The following example shows how to arm a one-shot time event from a - /// state machine of an active object: - /// \include qf_state.cpp - void postIn(QActive *act, QTimeEvtCtr nTicks) { - m_interval = (uint16_t)0; - arm_(act, nTicks); - } - - /// \brief Arm a periodic time event for direct event posting. - /// - /// Arms a time event to fire every \a nTicks clock ticks (periodic time - /// event). The time event gets directly posted (using the FIFO policy) - /// into the event queue of the active object \a act. - /// - /// After posting, the time event gets automatically re-armed to fire - /// again in the specified \a nTicks clock ticks. - /// - /// A periodic time event can be disarmed only by calling the - /// QTimeEvt::disarm() function. After disarming, the time event can be - /// reused for a one-shot or periodic timeout requests. - /// - /// \note An attempt to reuse (arm again) a running periodic time event - /// raises an assertion. - /// - /// Also, a periodic time event can be re-armed to shorten or extend the - /// current period by calling the QTimeEvt_rearm() function. After - /// adjusting the current period, the periodic time event goes back - /// timing out at the original rate. - void postEvery(QActive *act, QTimeEvtCtr nTicks) { - m_interval = nTicks; - arm_(act, nTicks); - } - - /// \brief Disarm a time event. - /// - /// The time event gets disarmed and can be reused. The function - /// returns 1 (TRUE) if the time event was truly disarmed, that is, it - /// was running. The return of 0 (FALSE) means that the time event was - /// not truly disarmed because it was not running. The FALSE return is - /// only possible for one-shot time events that have been automatically - /// disarmed upon expiration. In this case the FALSE return means that - /// the time event has already been posted or published and should be - /// expected in the active object's state machine. - uint8_t disarm(void); - - /// \brief Rearm a time event. - /// - /// The time event gets rearmed with a new number of clock ticks - /// \a nTicks. This facility can be used to prevent a one-shot time event - /// from expiring (e.g., a watchdog time event), or to adjusts the - /// current period of a periodic time event. Rearming a periodic timer - /// leaves the interval unchanged and is a convenient method to adjust the - /// phasing of the periodic time event. - /// - /// The function returns 1 (TRUE) if the time event was running as it - /// was re-armed. The return of 0 (FALSE) means that the time event was - /// not truly rearmed because it was not running. The FALSE return is only - /// possible for one-shot time events that have been automatically - /// disarmed upon expiration. In this case the FALSE return means that - /// the time event has already been posted or published and should be - /// expected in the active object's state machine. - uint8_t rearm(QTimeEvtCtr nTicks); - - /// \brief Get the current value of the down-counter of a time event. - /// - /// If the time event is armed, the function returns the current value of - /// the down-counter of the given time event. If the time event is not - /// armed, the function returns 0. - /// - /// /note The function is thread-safe. - QTimeEvtCtr ctr(void); - -private: - - /// \brief Arm a time event (internal function to be used through macros - /// only). - /// - /// \sa QTimeEvt::postIn(), QTimeEvt::postEvery(), - /// \sa QTimeEvt::publishIn(), QTimeEvt::publishEvery() - void arm_(QActive *act, QTimeEvtCtr nTicks); - - friend class QF; -}; - - -#if (QF_MAX_ACTIVE > 63) - #error "QF_MAX_ACTIVE exceeds 63" -#endif - -////////////////////////////////////////////////////////////////////////////// -/// \brief Subscriber List class -/// -/// This data type represents a set of active objects that subscribe to -/// a given signal. The set is represented as an array of bits, where each -/// bit corresponds to the unique priority of an active object. -class QSubscrList { -private: - - /// An array of bits representing subscriber active objects. Each bit - /// in the array corresponds to the unique priority of the active object. - /// The size of the array is determined of the maximum number of active - /// objects in the application configured by the #QF_MAX_ACTIVE macro. - /// For example, an active object of priority p is a subscriber if the - /// following is true: ((m_bits[QF_div8Lkup[p]] & QF_pwr2Lkup[p]) != 0) - /// - /// \sa QF::psInit(), QF_div8Lkup, QF_pwr2Lkup, #QF_MAX_ACTIVE - uint8_t m_bits[((QF_MAX_ACTIVE - 1) / 8) + 1]; - - friend class QF; - friend class QActive; -}; - -////////////////////////////////////////////////////////////////////////////// -/// \brief QF services. -/// -/// This class groups together QF services. It has only static members and -/// should not be instantiated. -class QF { -public: - - /// \brief QF initialization. - /// - /// This function initializes QF and must be called exactly once before - /// any other QF function. - static void init(void); - - /// \brief Publish-subscribe initialization. - /// - /// This function initializes the publish-subscribe facilities of QF and - /// must be called exactly once before any subscriptions/publications - /// occur in the application. The arguments are as follows: \a subscrSto - /// is a pointer to the array of subscriber-lists. \a maxSignal is the - /// dimension of this array and at the same time the maximum signal that - /// can be published or subscribed. - /// - /// The array of subscriber-lists is indexed by signals and provides - /// mapping between the signals and subscirber-lists. The subscriber- - /// lists are bitmasks of type ::QSubscrList, each bit in the bitmask - /// corresponding to the unique priority of an active object. The size - /// of the ::QSubscrList bitmask depends on the value of the - /// #QF_MAX_ACTIVE macro. - /// - /// \note The publish-subscribe facilities are optional, meaning that - /// you might choose not to use publish-subscribe. In that case calling - /// QF::psInit() and using up memory for the subscriber-lists is - /// unnecessary. - /// - /// \sa ::QSubscrList - /// - /// The following example shows the typical initialization sequence of - /// QF: \include qf_main.cpp - static void psInit(QSubscrList *subscrSto, QSignal maxSignal); - - /// \brief Event pool initialization for dynamic allocation of events. - /// - /// This function initializes one event pool at a time and must be called - /// exactly once for each event pool before the pool can be used. - /// The arguments are as follows: \a poolSto is a pointer to the memory - /// block for the events. \a poolSize is the size of the memory block in - /// bytes. \a evtSize is the block-size of the pool in bytes, which - /// determines the maximum size of events that can be allocated from the - /// pool. - /// - /// You might initialize one, two, and up to three event pools by making - /// one, two, or three calls to the QF_poolInit() function. However, - /// for the simplicity of the internal implementation, you must initialize - /// event pools in the ascending order of the event size. - /// - /// Many RTOSes provide fixed block-size heaps, a.k.a. memory pools that - /// can be used for QF event pools. In case such support is missing, QF - /// provides a native QF event pool implementation. The macro - /// #QF_EPOOL_TYPE_ determines the type of event pool used by a - /// particular QF port. See class ::QMPool for more information. - /// - /// \note The actual number of events available in the pool might be - /// actually less than (\a poolSize / \a evtSize) due to the internal - /// alignment of the blocks that the pool might perform. You can always - /// check the capacity of the pool by calling QF::getPoolMargin(). - /// - /// \note The dynamic allocation of events is optional, meaning that you - /// might choose not to use dynamic events. In that case calling - /// QF::poolInit() and using up memory for the memory blocks is - /// unnecessary. - /// - /// \sa QF initialization example for QF::init() - static void poolInit(void *poolSto, uint32_t poolSize, - QEventSize evtSize); - - /// \brief Transfers control to QF to run the application. - /// - /// QF::run() is typically called from your startup code after you - /// initialize the QF and start at least one active object with - /// QActive::start(). Also, QF::start() call must precede the transfer - /// of control to QF::run(), but some QF ports might call QF::start() - /// from QF::run(). QF::run() typically never returns to the caller. - /// - /// \note This function is strongly platform-dependent and is not - /// implemented in the QF, but either in the QF port or in the - /// Board Support Package (BSP) for the given application. All QF ports - /// must implement QF::run(). - /// - /// \note When the Quantum Kernel (QK) is used as the underlying real-time - /// kernel for the QF, all platfrom dependencies are handled in the QK, so - /// no porting of QF is necessary. In other words, you only need to - /// recompile the QF platform-independent code with the compiler for your - /// platform, but you don't need to provide any platform-specific - /// implementation (so, no qf_port.cpp file is necessary). Moreover, QK - /// implements the function QF::run() in a platform-independent way, - /// in the modile qk.cpp. - static void run(void); - - /// \brief Startup QF callback. - /// - /// The timeline for calling QF::onStartup() depends on the particular - /// QF port. In most cases, QF::onStartup() is called from QF::run(), - /// right before starting any multitasking kernel or the background loop. - static void onStartup(void); - - /// \brief Cleanup QF callback. - /// - /// QF::onCleanup() is called in some QF ports before QF returns to the - /// underlying operating system or RTOS. - /// - /// This function is strongly platform-specific and is not implemented in - /// the QF but either in the QF port or in the Board Support Package (BSP) - /// for the given application. Some QF ports might not require - /// implementing QF::onCleanup() at all, because many embedded - /// applications don't have anything to exit to. - /// - /// \sa QF::init() and QF::stop() - static void onCleanup(void); - -#ifndef QF_INT_KEY_TYPE - static void onIdle(void); // interrupt lock key NOT defined - -#else - - /// \brief QF idle callback (customized in BSPs for QF) - /// - /// QF::onIdle() is called by the non-preemptive scheduler built into QF - /// when the framework detects that no events are available for active - /// objects (the idle condition). This callback gives the application an - /// opportunity to enter a power-saving CPU mode, or perform some other - /// idle processing (such as Q-Spy output). - /// - /// \note QF::onIdle() is invoked with interrupts LOCKED because the idle - /// condition can be asynchronously changed at any time by an interrupt. - /// QF::onIdle() MUST unlock the interrupts internally, but not before - /// putting the CPU into the low-power mode. (Ideally, unlocking - /// interrupts and low-power mode should happen atomically). At the very - /// least, the function MUST unlock interrupts, otherwise interrups will - /// be locked permanently. - /// - /// \note QF::onIdle() is only used by the non-preemptive scheduler built - /// into QF in the "bare metal" port, and is NOT used in any other ports. - /// When QF is combined with QK, the QK idle loop calls a different - /// function QK::onIdle(), with different semantics than QF::onIdle(). - /// When QF is combined with a 3rd-party RTOS or kernel, the idle - /// processing mechanism of the RTOS or kernal is used instead of - /// QF::onIdle(). - static void onIdle(QF_INT_KEY_TYPE intLockKey); // int. lock key defined - -#endif // QF_INT_KEY_TYPE - - /// \brief Function invoked by the application layer to stop the QF - /// application and return control to the OS/Kernel. - /// - /// This function stops the QF application. After calling this function, - /// QF attempts to gracefully stop the application. This graceful - /// shutdown might take some time to complete. The typical use of this - /// funcition is for terminating the QF application to return back to the - /// operating system or for handling fatal errors that require shutting - /// down (and possibly re-setting) the system. - /// - /// This function is strongly platform-specific and is not implemented in - /// the QF but either in the QF port or in the Board Support Package (BSP) - /// for the given application. Some QF ports might not require - /// implementing QF::stop() at all, because many embedded application - /// don't have anything to exit to. - /// - /// \sa QF::stop() and QF::onCleanup() - static void stop(void); - - /// \brief Publish event to the framework. - /// - /// This function posts (using the FIFO policy) the event \a e it to ALL - /// active object that have subscribed to the signal \a e->sig. - /// This function is designed to be callable from any part of the system, - /// including ISRs, device drivers, and active objects. - /// - /// In the general case, event publishing requires multi-casting the - /// event to multiple subscribers. This happens in the caller's thread - /// with the scheduler locked to prevent preemptions during the multi- - /// casting process. (Please note that the interrupts are not locked.) -#ifndef Q_SPY - static void publish(QEvent const *e); -#else - static void publish(QEvent const *e, void const *sender); -#endif - - /// \brief Processes all armed time events at every clock tick. - /// - /// This function must be called periodically from a time-tick ISR or from - /// the highest-priority task so that QF can manage the timeout events. - /// - /// \note The QF::tick() function is not reentrant meaning that it must - /// run to completion before it is called again. Also, QF::tick() assumes - /// that it never will get preempted by a task, which is always the case - /// when it is called from an ISR or the highest-priority task. - /// - /// \sa ::QTimeEvt. - /// - /// The following example illustrates the call to QF::tick(): - /// \include qf_tick.cpp -#ifndef Q_SPY - static void tick(void); -#else - static void tick(void const *sender); -#endif - - /// \brief Returns the QF version. - /// - /// This function returns constant version string in the format x.y.zz, - /// where x (one digit) is the major version, y (one digit) is the minor - /// version, and zz (two digits) is the maintenance release version. - /// An example of the version string is "3.1.03". - /// - /// The following example illustrates the usage of this function: - /// \include qf_version.cpp - static char const Q_ROM * Q_ROM_VAR getVersion(void); - - /// \brief Returns the QF-port version. - /// - /// This function returns constant version string in the format x.y.zz, - /// where x (one digit) is the major version, y (one digit) is the minor - /// version, and zz (two digits) is the maintenance release version. - /// An example of the QF-port version string is "1.1.03". - /// - /// \sa QF::getVersion() - static char const Q_ROM * Q_ROM_VAR getPortVersion(void); - - /// \brief This function returns the margin of the given event pool. - /// - /// This function returns the margin of the given event pool \a poolId, - /// where poolId is the ID of the pool initialized by the call to - /// QF::poolInit(). The poolId of the first initialized pool is 1, the - /// second 2, and so on. - /// - /// The returned pool margin is the minimal number of free blocks - /// encountered in the given pool since system startup. - /// - /// \note Requesting the margin of an un-initialized pool raises an - /// assertion in the QF. - static uint32_t getPoolMargin(uint8_t poolId); - - /// \brief This function returns the margin of the given event queue. - /// - /// This function returns the margin of the given event queue of an active - /// object with priority \a prio. (QF priorities start with 1 and go up to - /// #QF_MAX_ACTIVE.) The margin is the minimal number of free events - /// encountered in the given queue since system startup. - /// - /// \note QF::getQueueMargin() is available only when the native QF event - /// queue implementation is used. Requesting the queue margin of an unused - /// priority level raises an assertion in the QF. (A priority level - /// becomes used in QF after the call to the QF::add_() function.) - static uint32_t getQueueMargin(uint8_t prio); - - /// \brief Internal QF implementation of the dynamic event allocator. - /// - /// \note The application code should not call this function directly. - /// Please use the macro #Q_NEW. - static QEvent *new_(uint16_t evtSize, QSignal sig); - -#ifdef Q_EVT_CTOR - #define Q_NEW(evtT_, sig_, ...) \ - (new(QF::new_(sizeof(evtT_), sig_)) evtT_((sig_), ##__VA_ARGS__)) -#else - /// \brief Allocate a dynamic event. - /// - /// This macro returns an event pointer cast to the type \a evtT_. The - /// event is initialized with the signal \a sig. Internally, the macro - /// calls the internal QF function QF::new_(), which always returns a - /// valid event pointer. - /// - /// \note The internal QF function QF::new_() raises an assertion when - /// the allocation of the event turns out to be impossible due to event - /// pool depletion, or incorrect (too big) size of the requested event. - /// - /// The following example illustrates dynamic allocation of an event: - /// \include qf_post.cpp - #define Q_NEW(evtT_, sig_) ((evtT_ *)QF::new_(sizeof(evtT_), (sig_))) -#endif - - /// \brief Recycle a dynamic event. - /// - /// This function implements a simple garbage collector for the dynamic - /// events. Only dynamic events are candidates for recycling. (A dynamic - /// event is one that is allocated from an event pool, which is - /// determined as non-zero e->attrQF__ attribute.) Next, the function - /// decrements the reference counter of the event, and recycles the event - /// only if the counter drops to zero (meaning that no more references - /// are outstanding for this event). The dynamic event is recycled by - /// returning it to the pool from which it was originally allocated. - /// The pool-of-origin information is stored in the upper 2-MSBs of the - /// e->attrQF__ attribute.) - /// - /// \note QF invokes the garbage collector at all appropriate contexts, - /// when an event can become garbage (automatic garbage collection), - /// so the application code should have NO need to call QF::gc() directly. - /// The QF::gc() function is exposed only for special cases when your - /// application sends dynamic events to the "raw" thread-safe queues - /// (see ::QEQueue). Such queues are processed outside of QF and the - /// automatic garbage collection CANNOT be performed for these events. - /// In this case you need to call QF::gc() explicitly. - static void gc(QEvent const *e); - - /// \brief array of registered active objects - /// - /// \note Not to be used by Clients directly, only in ports of QF - static QActive *active_[]; - -private: // functions to be used in QF ports only - - /// \brief Register an active object to be managed by the framework - /// - /// This function should not be called by the application directly, only - /// through the function QActive::start(). The priority of the active - /// object \a a should be set before calling this function. - /// - /// \note This function raises an assertion if the priority of the active - /// object exceeds the maximum value #QF_MAX_ACTIVE. Also, this function - /// raises an assertion if the priority of the active object is already in - /// use. (QF requires each active object to have a UNIQUE priority.) - static void add_(QActive *a); - -public: - /// \brief Remove the active object from the framework. - /// - /// This function should not be called by the application directly, only - /// inside the QF port. The priority level occupied by the active object - /// is freed-up and can be reused for another active object. - /// - /// The active object that is removed from the framework can no longer - /// participate in the publish-subscribe event exchange. - /// - /// \note This function raises an assertion if the priority of the active - /// object exceeds the maximum value #QF_MAX_ACTIVE or is not used. - static void remove_(QActive const *a); - - friend class QActive; -}; - -////////////////////////////////////////////////////////////////////////////// -// useful lookup tables - -/// \brief Lookup table for (log2(n) + 1), where n is the index -/// into the table. -/// -/// This lookup delivers the 1-based number of the most significant 1-bit -/// of a byte. -/// -/// \note Index range n = 0..255. The first index (n == 0) should never -/// be used. -/// -extern uint8_t const Q_ROM Q_ROM_VAR QF_log2Lkup[256]; - -/// \brief Lookup table for (1 << ((n-1) % 8)), where n is the index -/// into the table. -/// -/// \note Index range n = 0..64. The first index (n == 0) should never -/// be used. -extern uint8_t const Q_ROM Q_ROM_VAR QF_pwr2Lkup[65]; - -/// \brief Lookup table for ~(1 << ((n-1) % 8)), where n is the index -/// into the table. -/// -/// \note Index range n = 0..64. The first index (n == 0) should never -/// be used. -extern uint8_t const Q_ROM Q_ROM_VAR QF_invPwr2Lkup[65]; - -/// \brief Lookup table for (n-1)/8 -/// -/// \note Index range n = 0..64. The first index (n == 0) should never -/// be used. -extern uint8_t const Q_ROM Q_ROM_VAR QF_div8Lkup[65]; - -////////////////////////////////////////////////////////////////////////////// -#ifdef Q_EVT_CTOR -#include <new> // for placement new -#endif - -// from qf.h ----------------------------------------------------------------- -////////////////////////////////////////////////////////////////////////////// -// QS software tracing integration, only if enabled -#ifdef Q_SPY // QS software tracing enabled? - #define QS_TIME_SIZE 4 - #define QS_OBJ_PTR_SIZE 4 - #define QS_FUN_PTR_SIZE 4 - - /// \brief Invoke the system clock tick processing QF::tick(). This macro - /// is the recommended way of invoking clock tick processing, because it - /// provides the vital information for software tracing and avoids any - /// overhead when the tracing is disabled. - /// - /// This macro takes the argument \a sender_, which is a pointer to the - /// sender object. This argument is actually only used when QS software - /// tracing is enabled (macro #Q_SPY is defined). When QS software - /// tracing is disabled, the macro calls QF::tick() without any - /// arguments, so the overhead of passing this extra argument is - /// entirely avoided. - /// - /// \note the pointer to the sender object is not necessarily a poiner - /// to an active object. In fact, typically QF::TICK() will be called from - /// an interrupt, in which case you would create a unique object just to - /// unambiguously identify the ISR as the sender of the time events. - /// - /// \sa QF::tick() - #define TICK(sender_) tick(sender_) - - /// \brief Invoke the event publishing facility QF::publish(). This macro - /// is the recommended way of publishing events, because it provides the - /// vital information for software tracing and avoids any overhead when the - /// tracing is disabled. - /// - /// - /// This macro takes the last argument \a sender_, which is a pointer to - /// the sender object. This argument is actually only used when QS software - /// tracing is enabled (macro #Q_SPY is defined). When QS software - /// tracing is disabled, the macro calls QF::publish() without the - /// \a sender_ argument, so the overhead of passing this extra argument - /// is entirely avoided. - /// - /// \note the pointer to the sender object is not necessarily a poiner - /// to an active object. In fact, if QF::PUBLISH() is called from an - /// interrupt or other context, you can create a unique object just to - /// unambiguously identify the publisher of the event. - /// - /// \sa QF::publish() - #define PUBLISH(e_, sender_) publish((e_), (sender_)) - - /// \brief Invoke the direct event posting facility QActive::postFIFO(). - /// This macro is the recommended way of posting events, because it provides - /// the vital information for software tracing and avoids any overhead when - /// the tracing is disabled. - /// - /// - /// This macro takes the last argument \a sender_, which is a pointer to - /// the sender object. This argument is actually only used when QS software - /// tracing is disabled (macro #Q_SPY is defined). When QS software - /// tracing is not enabled, the macro calls QF_publish() without the - /// \a sender_ argument, so the overhead of passing this extra argument - /// is entirely avoided. - /// - /// \note the pointer to the sender object is not necessarily a poiner - /// to an active object. In fact, if ao->POST() is called from an - /// interrupt or other context, you can create a unique object just to - /// unambiguously identify the publisher of the event. - /// - /// \sa QActive::postFIFO() - #define POST(e_, sender_) postFIFO((e_), (sender_)) - - #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 - -#else - - #define TICK(dummy_) tick() - #define PUBLISH(e_, dummy_) publish((e_)) - #define POST(e_, dummy_) postFIFO((e_)) - -#endif // Q_SPY - -////////////////////////////////////////////////////////////////////////////// -// QS software tracing -#ifdef Q_SPY - -// qs.h ====================================================================== -////////////////////////////////////////////////////////////////////////////// - -/// \brief Quantum Spy record types. -/// -/// This enumeration specifies the record types used in the QP components. -/// You can specify your own record types starting from ::QS_USER offset. -/// Currently, the maximum of all records cannot exceed 256. -/// \sa QS::filterOn()/#QS_FILTER_ON and QS::filterOff()/#QS_FILTER_OFF -enum QSpyRecords { - // QEP records - QS_QEP_STATE_EMPTY, - QS_QEP_STATE_ENTRY, ///< a state was entered - QS_QEP_STATE_EXIT, ///< a state was exited - QS_QEP_STATE_INIT, ///< an intial transition was taken in a state - QS_QEP_INIT_TRAN, ///< the top-most initial transition was taken - QS_QEP_INTERN_TRAN, ///< an internal transition was taken - QS_QEP_TRAN, ///< a regular transition was taken - QS_QEP_IGNORED, ///< an event was ignored (silently discarded) - QS_QEP_DISPATCH, ///< an event was dispatched (begin of RTC step) - QS_QEP_RESERVED0, - - // QF records - QS_QF_ACTIVE_ADD, ///< an AO has been added to QF (started) - QS_QF_ACTIVE_REMOVE, ///< an AO has been removed from QF (stopped) - QS_QF_ACTIVE_SUBSCRIBE, ///< an AO subscribed to an event - QS_QF_ACTIVE_UNSUBSCRIBE, ///< an AO unsubscribed to an event - QS_QF_ACTIVE_POST_FIFO, ///< an event was posted (FIFO) directly to an AO - QS_QF_ACTIVE_POST_LIFO, ///< an event was posted (LIFO) directly to an AO - QS_QF_ACTIVE_GET, ///< an AO got an event and its queue is still not empty - QS_QF_ACTIVE_GET_LAST, ///< an AO got an event and its queue is empty - QS_QF_EQUEUE_INIT, ///< an event queue was initialized - QS_QF_EQUEUE_POST_FIFO, ///< an event was posted (FIFO) to a raw queue - QS_QF_EQUEUE_POST_LIFO, ///< an event was posted (LIFO) to a raw queue - QS_QF_EQUEUE_GET, ///< get an event and queue still not empty - QS_QF_EQUEUE_GET_LAST, ///< get the last event from the queue - QS_QF_MPOOL_INIT, ///< a memory pool was initialized - QS_QF_MPOOL_GET, ///< a memory block was removed from a memory pool - QS_QF_MPOOL_PUT, ///< a memory block was returned to a memory pool - QS_QF_PUBLISH, ///< an event was truly published to some subscribers - QS_QF_RESERVED8, - QS_QF_NEW, ///< new event creation - QS_QF_GC_ATTEMPT, ///< garbage collection attempt - QS_QF_GC, ///< garbage collection - QS_QF_TICK, ///< QF::tick() was called - QS_QF_TIMEEVT_ARM, ///< a time event was armed - QS_QF_TIMEEVT_AUTO_DISARM, ///< a time event expired and was disarmed - QS_QF_TIMEEVT_DISARM_ATTEMPT,///< an attempt to disarmed a disarmed tevent - QS_QF_TIMEEVT_DISARM, ///< true disarming of an armed time event - QS_QF_TIMEEVT_REARM, ///< rearming of a time event - QS_QF_TIMEEVT_POST, ///< a time event posted itself directly to an AO - QS_QF_TIMEEVT_CTR, ///< a time event counter was requested - QS_QF_INT_LOCK, ///< interrupts were locked - QS_QF_INT_UNLOCK, ///< interrupts were unlocked - QS_QF_ISR_ENTRY, ///< an ISR was entered - QS_QF_ISR_EXIT, ///< an ISR was exited - QS_QF_RESERVED6, - QS_QF_RESERVED5, - QS_QF_RESERVED4, - QS_QF_RESERVED3, - QS_QF_RESERVED2, - QS_QF_RESERVED1, - QS_QF_RESERVED0, - - // QK records - QS_QK_MUTEX_LOCK, ///< the QK mutex was locked - QS_QK_MUTEX_UNLOCK, ///< the QK mutex was unlocked - QS_QK_SCHEDULE, ///< the QK scheduler scheduled a new task to execute - QS_QK_RESERVED6, - QS_QK_RESERVED5, - QS_QK_RESERVED4, - QS_QK_RESERVED3, - QS_QK_RESERVED2, - QS_QK_RESERVED1, - QS_QK_RESERVED0, - - // Miscellaneous QS records - QS_SIG_DICTIONARY, ///< signal dictionary entry - QS_OBJ_DICTIONARY, ///< object dictionary entry - QS_FUN_DICTIONARY, ///< function dictionary entry - QS_ASSERT, ///< assertion fired in the code - QS_RESERVED5, - QS_RESERVED4, - QS_RESERVED3, - QS_RESERVED2, - QS_RESERVED1, - QS_RESERVED0, - - // User records - QS_USER ///< the first record available for user QS records -}; - -/// \brief Specification of all QS records for the QS::filterOn() and -/// QS::filterOff() -#define QS_ALL_RECORDS ((uint8_t)0xFF) - -/// \brief Constant representing End-Of-Data condition returned from the -/// QS::getByte() function. -#define QS_EOD ((uint16_t)0xFFFF) - - -#ifndef QS_TIME_SIZE - - /// \brief The size (in bytes) of the QS time stamp. Valid values: 1, 2, - /// or 4; default 4. - /// - /// This macro can be defined in the QS port file (qs_port.h) to - /// configure the ::QSTimeCtr type. Here the macro is not defined so the - /// default of 4 byte is chosen. - #define QS_TIME_SIZE 4 -#endif -#if (QS_TIME_SIZE == 1) - typedef uint8_t QSTimeCtr; - #define QS_TIME_() QS::u8_(QS::onGetTime()) -#elif (QS_TIME_SIZE == 2) - typedef uint16_t QSTimeCtr; - #define QS_TIME_() QS::u16_(QS::onGetTime()) -#elif (QS_TIME_SIZE == 4) - - /// \brief The type of the QS time stamp - /// - /// This type determines the dynamic range of QS time stamps - typedef uint32_t QSTimeCtr; - - /// \brief Internal macro to output time stamp to the QS record - #define QS_TIME_() QS::u32_(QS::onGetTime()) -#else - #error "QS_TIME_SIZE defined incorrectly, expected 1, 2, or 4" -#endif - -#ifndef Q_ROM // provide the default if Q_ROM NOT defined - #define Q_ROM -#endif -#ifndef Q_ROM_VAR // provide the default if Q_ROM_VAR NOT defined - #define Q_ROM_VAR -#endif -#ifndef Q_ROM_BYTE // provide the default if Q_ROM_BYTE NOT defined - #define Q_ROM_BYTE(rom_var_) (rom_var_) -#endif - - -/// \brief Quantum Spy logging facilities -/// -/// This class groups together QS services. It has only static members and -/// should not be instantiated. -class QS { -public: - - /// \brief Get the current version of QS - /// - /// \return version of the QS as a constant 6-character string of the form - /// x.y.zz, where x is a 1-digit major version number, y is a 1-digit - /// minor version number, and zz is a 2-digit release number. - static char const Q_ROM * Q_ROM_VAR getVersion(void); - - /// \brief Initialize the QS data buffer. - /// - /// This function should be called from QS_init() to provide QS with the - /// data buffer. The first argument \a sto[] is the address of the memory - /// block, and the second argument \a stoSize is the size of this block - /// in bytes. Currently the size of the QS buffer cannot exceed 64KB. - /// - /// QS can work with quite small data buffers, but you will start losing - /// data if the buffer is too small for the bursts of logging activity. - /// The right size of the buffer depends on the data production rate and - /// the data output rate. QS offers flexible filtering to reduce the data - /// production rate. - /// - /// \note If the data output rate cannot keep up with the production rate, - /// QS will start overwriting the older data with newer data. This is - /// consistent with the "last-is-best" QS policy. The record sequence - /// counters and checksums on each record allow to easily detect data - /// loss. - static void initBuf(uint8_t sto[], uint32_t stoSize); - - /// \brief Turn the global Filter on for a given record type \a rec. - /// - /// This function sets up the QS filter to enable the record type \a rec. - /// The argument #QS_ALL_RECORDS specifies to filter-on all records. - /// This function should be called indirectly through the macro - /// #QS_FILTER_ON. - /// - /// \note Filtering based on the record-type is only the first layer of - /// filtering. The second layer is based on the object-type. Both filter - /// layers must be enabled for the QS record to be inserted into the QS - /// buffer. - /// \sa QS_filterOff(), #QS_FILTER_SM_OBJ, #QS_FILTER_AO_OBJ, - /// #QS_FILTER_MP_OBJ, #QS_FILTER_EQ_OBJ, and #QS_FILTER_TE_OBJ. - static void filterOn(uint8_t rec); - - /// \brief Turn the global Filter off for a given record type \a rec. - /// - /// This function sets up the QS filter to disable the record type \a rec. - /// The argument #QS_ALL_RECORDS specifies to suppress all records. - /// This function should be called indirectly through the macro - /// #QS_FILTER_OFF. - /// - /// \note Filtering records based on the record-type is only the first - /// layer of filtering. The second layer is based on the object-type. - /// Both filter layers must be enabled for the QS record to be inserted - /// into the QS buffer. - /// \sa - static void filterOff(uint8_t rec); - - /// \brief Mark the begin of a QS record \a rec - /// - /// This function must be called at the beginning of each QS record. - /// This function should be called indirectly through the macro #QS_BEGIN, - /// or #QS_BEGIN_NOLOCK, depending if it's called in a normal code or from - /// a critical section. - static void begin(uint8_t rec); - - /// \brief Mark the end of a QS record \a rec - /// - /// This function must be called at the end of each QS record. - /// This function should be called indirectly through the macro #QS_END, - /// or #QS_END_NOLOCK, depending if it's called in a normal code or from - /// a critical section. - static void end(void); - - // unformatted data elements output ...................................... - - /// \brief output uint8_t data element without format information - /// \note This function is only to be used through macros, never in the - /// client code directly. - static void u8_(uint8_t d); - - /// \brief Output uint16_t data element without format information - /// \note This function is only to be used through macros, never in the - /// client code directly. - static void u16_(uint16_t d); - - /// \brief Output uint32_t data element without format information - /// \note This function is only to be used through macros, never in the - /// client code directly. - static void u32_(uint32_t d); - - /// \brief Output zero-terminated ASCII string element without format - /// information - /// \note This function is only to be used through macros, never in the - /// client code directly. - static void str_(char const *s); - - /// \brief Output zero-terminated ASCII string element allocated in ROM - /// without format information - /// \note This function is only to be used through macros, never in the - /// client code directly. - static void str_ROM_(char const Q_ROM * Q_ROM_VAR s); - - // formatted data elements output ........................................ - - /// \brief Output uint8_t data element with format information - /// \note This function is only to be used through macros, never in the - /// client code directly. - static void u8(uint8_t format, uint8_t d); - - /// \brief output uint16_t data element with format information - /// \note This function is only to be used through macros, never in the - /// client code directly. - static void u16(uint8_t format, uint16_t d); - - /// \brief Output uint32_t data element with format information - /// \note This function is only to be used through macros, never in the - /// client code directly. - static void u32(uint8_t format, uint32_t d); - - /// \brief Output 32-bit floating point data element with format - /// information - /// \note This function is only to be used through macros, never in the - /// client code directly. - static void f32(uint8_t format, float d); - - /// \brief Output 64-bit floating point data element with format - /// information - /// \note This function is only to be used through macros, never in the - /// client code directly. - static void f64(uint8_t format, double d); - - /// \brief Output zero-terminated ASCII string element with format - /// information - /// \note This function is only to be used through macros, never in the - /// client code directly. - static void str(char const *s); - - /// \brief Output zero-terminated ASCII string element allocated in ROM - /// with format information - /// \note This function is only to be used through macros, never in the - /// client code directly. - static void str_ROM(char const Q_ROM * Q_ROM_VAR s); - - /// \brief Output memory block of up to 255-bytes with format information - /// \note This function is only to be used through macros, never in the - /// client code directly. - static void mem(uint8_t const *blk, uint8_t size); - -#if (QS_OBJ_PTR_SIZE == 8) || (QS_FUN_PTR_SIZE == 8) - /// \brief Output uint64_t data element without format information - /// \note This function is only to be used through macros, never in the - /// client code directly. - static void u64_(uint64_t d); - - /// \brief Output uint64_t data element with format information - /// \note This function is only to be used through macros, never in the - /// client code directly. - static void u64(uint8_t format, uint64_t d); -#endif - - // QS buffer access ...................................................... - - /// \brief Byte-oriented interface to the QS data buffer. - /// - /// This function delivers one byte at a time from the QS data buffer. - /// The function returns the byte in the least-significant 8-bits of the - /// 16-bit return value if the byte is available. If no more data is - /// available at the time, the function returns QS_EOD (End-Of-Data). - /// - /// \note QS::getByte() is NOT protected with a critical section. - static uint16_t getByte(void); - - /// \brief Block-oriented interface to the QS data buffer. - /// - /// This function delivers a contiguous block of data from the QS data - /// buffer. The function returns the pointer to the beginning of the - /// block, and writes the number of bytes in the block to the location - /// pointed to by \a pNbytes. The argument \a pNbytes is also used as - /// input to provide the maximum size of the data block that the caller - /// can accept. - /// - /// If no bytes are available in the QS buffer when the function is - /// called, the function returns a NULL pointer and sets the value - /// pointed to by \a pNbytes to zero. - /// - /// \note Only the NULL return from QS::getBlock() indicates that the QS - /// buffer is empty at the time of the call. The non-NULL return often - /// means that the block is at the end of the buffer and you need to call - /// QS::getBlock() again to obtain the rest of the data that "wrapped - /// around" to the beginning of the QS data buffer. - /// - /// \note QS::getBlock() is NOT protected with a critical section. - static uint8_t const *getBlock(uint16_t *pNbytes); - -// platform-dependent callback functions, need to be implemented by clients -public: - - // platform-specific callback functions, need to be implemented by clients - /// \brief Callback to startup the QS facility - /// - /// This is a platform-dependent "callback" function invoked through the - /// macro #QS_INIT. You need to implement this function in your - /// application. At a minimum, the function must configure the QS buffer - /// by calling QS::initBuf(). Typically, you will also want to open/ - /// configure the QS output channel, such as a serial port, or a file. - /// The void* argument \a arg can be used to pass parameter(s) needed to - /// configure the output channel. - /// - /// The function returns TRUE (1) if the QS initialization was successful, - /// or FALSE (0) if it failed. - /// - /// The following example illustrates an implementation of QS_onStartup(): - /// \include qs_startup.cpp - static uint8_t onStartup(void const *arg); - - /// \brief Callback to cleanup the QS facility - /// - /// This is a platform-dependent "callback" function invoked through the - /// macro #QS_EXIT. You need to implement this function in your - /// application. The main purpose of this function is to close the QS - /// output channel, if necessary. - static void onCleanup(void); - - /// \brief Callback to flush the QS trace data to the host - /// - /// This is a platform-dependent "callback" function to flush the QS - /// trace buffer to the host. The function typically busy-waits until all - /// the data in the buffer is sent to the host. This is acceptable only - /// in the initial transient. - static void onFlush(void); - - /// \brief Callback to obtain a timestamp for a QS record. - /// - /// This is a platform-dependent "callback" function invoked from the - /// macro #QS_TIME_ to add the time stamp to the QS record. - /// - /// \note Some of the pre-defined QS records from QP do not output the - /// time stamp. However, ALL user records do output the time stamp. - /// \note QS::onGetTime() is called in a critical section and should not - /// unlock interrupts. - /// - /// The following example shows using a system call to implement QS - /// time stamping: - /// \include qs_onGetTime.cpp - static QSTimeCtr onGetTime(void); - -// Global and Local QS filters ............................................... -public: - static uint8_t glbFilter_[32]; ///< global on/off QS filter - static void const *smObj_; ///< state machine for QEP local filter - static void const *aoObj_; ///< active object for QF/QK local filter - static void const *mpObj_; ///< event pool for QF local filter - static void const *eqObj_; ///< raw queue for QF local filter - static void const *teObj_; ///< time event for QF local filter - static void const *apObj_;///< generic object Application QF local filter - -// Miscallaneous ............................................................. -public: - /// tick counter for the QS_QF_TICK record - static QSTimeCtr volatile tickCtr_; -}; - - -////////////////////////////////////////////////////////////////////////////// -// Macros for adding QS instrumentation to the client code - -/// \brief Initialize the QS facility. -/// -/// This macro provides an indirection layer to invoke the QS initialization -/// routine if #Q_SPY is defined, or do nothing if #Q_SPY is not defined. -/// \sa QS::onStartup(), example of setting up a QS filter in #QS_FILTER_IN -#define QS_INIT(arg_) QS::onStartup(arg_) - -/// \brief Cleanup the QS facility. -/// -/// This macro provides an indirection layer to invoke the QS cleanup -/// routine if #Q_SPY is defined, or do nothing if #Q_SPY is not defined. -/// \sa QS::onCleanup() -#define QS_EXIT() QS::onCleanup() - -/// \brief Global Filter ON for a given record type \a rec. -/// -/// This macro provides an indirection layer to call QS::filterOn() if #Q_SPY -/// is defined, or do nothing if #Q_SPY is not defined. -/// -/// The following example shows how to use QS filters: -/// \include qs_filter.cpp -#define QS_FILTER_ON(rec_) QS::filterOn(rec_) - -/// \brief Global filter OFF for a given record type \a rec. -/// -/// This macro provides an indirection layer to call QS::filterOff() if #Q_SPY -/// is defined, or do nothing if #Q_SPY is not defined. -/// -/// \sa Example of using QS filters in #QS_FILTER_ON documentation -#define QS_FILTER_OFF(rec_) QS::filterOff(rec_) - -/// \brief Local Filter for a given state machine object \a obj_. -/// -/// This macro sets up the state machine object local filter if #Q_SPY is -/// defined, or does nothing if #Q_SPY is not defined. The argument \a obj_ -/// is the pointer to the state machine object that you want to monitor. -/// -/// The state machine object filter allows you to filter QS records pertaining -/// only to a given state machine object. With this filter disabled, QS will -/// output records from all state machines in your application. The object -/// filter is disabled by setting the state machine pointer to NULL. -/// -/// The state machine filter affects the following QS records: -/// ::QS_QEP_STATE_ENTRY, ::QS_QEP_STATE_EXIT, ::QS_QEP_STATE_INIT, -/// ::QS_QEP_INIT_TRAN, ::QS_QEP_INTERN_TRAN, ::QS_QEP_TRAN, -/// and ::QS_QEP_IGNORED. -/// -/// \note Because active objects are state machines at the same time, -/// the state machine filter (#QS_FILTER_SM_OBJ) pertains to active -/// objects as well. However, the state machine filter is more general, -/// because it can be used only for state machines that are not active -/// objects, such as "Orthogonal Components". -/// -/// \sa Example of using QS filters in #QS_FILTER_ON documentation -#define QS_FILTER_SM_OBJ(obj_) (QS::smObj_ = (obj_)) - -/// \brief Local Filter for a given active object \a obj_. -/// -/// This macro sets up the active object local filter if #Q_SPY is defined, -/// or does nothing if #Q_SPY is not defined. The argument \a obj_ is the -/// pointer to the active object that you want to monitor. -/// -/// The active object filter allows you to filter QS records pertaining -/// only to a given active object. With this filter disabled, QS will -/// output records from all active objects in your application. The object -/// filter is disabled by setting the active object pointer \a obj_ to NULL. -/// -/// The active object filter affects the following QS records: -/// ::QS_QF_ACTIVE_ADD, ::QS_QF_ACTIVE_REMOVE, ::QS_QF_ACTIVE_SUBSCRIBE, -/// ::QS_QF_ACTIVE_UNSUBSCRIBE, ::QS_QF_ACTIVE_POST_FIFO, -/// ::QS_QF_ACTIVE_POST_LIFO, ::QS_QF_ACTIVE_GET, and ::QS_QF_ACTIVE_GET_LAST. -/// -/// \sa Example of using QS filters in #QS_FILTER_ON documentation -#define QS_FILTER_AO_OBJ(obj_) (QS::aoObj_ = (obj_)) - -/// \brief Local Filter for a given memory pool object \a obj_. -/// -/// This macro sets up the memory pool object local filter if #Q_SPY is -/// defined, or does nothing if #Q_SPY is not defined. The argument \a obj_ -/// is the pointer to the memory buffer used during the initialization of the -/// event pool with QF::poolInit(). -/// -/// The memory pool filter allows you to filter QS records pertaining -/// only to a given memory pool. With this filter disabled, QS will -/// output records from all memory pools in your application. The object -/// filter is disabled by setting the memory pool pointer \a obj_ to NULL. -/// -/// The memory pool filter affects the following QS records: -/// ::QS_QF_MPOOL_INIT, ::QS_QF_MPOOL_GET, and ::QS_QF_MPOOL_PUT. -/// -/// \sa Example of using QS filters in #QS_FILTER_ON documentation -#define QS_FILTER_MP_OBJ(obj_) (QS::mpObj_ = (obj_)) - -/// \brief Filter for a given event queue object \a obj_. -/// -/// This macro sets up the event queue object filter if #Q_SPY is defined, -/// or does nothing if #Q_SPY is not defined. The argument \a obj_ is the -/// pointer to the "raw" thread-safe queue object you want to monitor. -/// -/// The event queue filter allows you to filter QS records pertaining -/// only to a given event queue. With this filter disabled, QS will -/// output records from all event queues in your application. The object -/// filter is disabled by setting the event queue pointer \a obj_ to NULL. -/// -/// The event queue filter affects the following QS records: -/// ::QS_QF_EQUEUE_INIT, ::QS_QF_EQUEUE_POST_FIFO, ::QS_QF_EQUEUE_POST_LIFO, -/// ::QS_QF_EQUEUE_GET, and ::QS_QF_EQUEUE_GET_LAST. -/// -/// \sa Example of using QS filters in #QS_FILTER_IN documentation -#define QS_FILTER_EQ_OBJ(obj_) (QS::eqObj_ = (obj_)) - -/// \brief Local Filter for a given time event object \a obj_. -/// -/// This macro sets up the time event object local filter if #Q_SPY is -/// defined, or does nothing if #Q_SPY is not defined. The argument \a obj_ -/// is the pointer to the time event object you want to monitor. -/// -/// The time event filter allows you to filter QS records pertaining -/// only to a given time event. With this filter disabled, QS will -/// output records from all time events in your application. The object -/// filter is disabled by setting the time event pointer \a obj_ to NULL. -/// -/// The time event filter affects the following QS records: -/// ::QS_QF_TIMEEVT_ARM, ::QS_QF_TIMEEVT_AUTO_DISARM, -/// ::QS_QF_TIMEEVT_DISARM_ATTEMPT, ::QS_QF_TIMEEVT_DISARM, -/// ::QS_QF_TIMEEVT_REARM, ::QS_QF_TIMEEVT_POST, and ::QS_QF_TIMEEVT_PUBLISH. -/// -/// \sa Example of using QS filters in #QS_FILTER_ON documentation -#define QS_FILTER_TE_OBJ(obj_) (QS::teObj_ = (obj_)) - -/// \brief Local Filter for a generic application object \a obj_. -/// -/// This macro sets up the local application object filter if #Q_SPY is -/// defined, or does nothing if #Q_SPY is not defined. The argument \a obj_ -/// is the pointer to the application object you want to monitor. -/// -/// The application object filter allows you to filter QS records pertaining -/// only to a given application object. With this filter disabled, QS will -/// output records from all application-records enabled by the global filter. -/// The local filter is disabled by setting the time event pointer \a obj_ -/// to NULL. -/// -/// \sa Example of using QS filters in #QS_FILTER_ON documentation -#define QS_FILTER_AP_OBJ(obj_) (QS::apObj_ = (obj_)) - - -////////////////////////////////////////////////////////////////////////////// -// Macros to generate user QS records - -/// \brief Begin a QS user record without locking interrupts. -#define QS_BEGIN_NOLOCK(rec_, obj_) \ - if (((QS::glbFilter_[(uint8_t)(rec_) >> 3U] \ - & (1U << ((uint8_t)(rec_) & 7U))) != 0) \ - && ((QS::apObj_ == (void *)0) || (QS::apObj_ == (obj_)))) \ - { \ - QS::begin((uint8_t)(rec_)); \ - QS_TIME_(); - -/// \brief End a QS user record without locking interrupts. -#define QS_END_NOLOCK() \ - QS_END_NOLOCK_() - - // QS-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 - /// #QS_INT_KEY_TYPE is defined, this internal macro provides the - /// definition of the lock key variable. Otherwise this macro is empty. - /// \sa #QS_INT_KEY_TYPE, #QF_INT_KEY_TYPE - #define QS_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 - /// #QS_INT_KEY_TYPE is defined, this internal macro invokes #QS_INT_LOCK - /// passing the key variable as the parameter. Otherwise #QS_INT_LOCK - /// is invoked with a dummy parameter. - /// \sa #QS_INT_LOCK, #QF_INT_LOCK, #QK_INT_LOCK - #define QS_INT_LOCK_() QF_INT_LOCK(ignore_) - - /// \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 - /// #QS_INT_KEY_TYPE is defined, this internal macro invokes - /// #QS_INT_UNLOCK passing the key variable as the parameter. Otherwise - /// #QS_INT_UNLOCK is invoked with a dummy parameter. - /// \sa #QS_INT_UNLOCK, #QF_INT_UNLOCK, #QK_INT_UNLOCK - #define QS_INT_UNLOCK_() QF_INT_UNLOCK(ignore_) -#else - #define QS_INT_LOCK_KEY_ QF_INT_KEY_TYPE intLockKey_; - #define QS_INT_LOCK_() QF_INT_LOCK(intLockKey_) - #define QS_INT_UNLOCK_() QF_INT_UNLOCK(intLockKey_) -#endif - -/// \brief Begin a user QS record with locking interrupts. -/// -/// The following example shows how to build a user QS record using the -/// macros #QS_BEGIN, #QS_END, and the formatted output macros: #QS_U8 and -/// #QS_STR. -/// \include qs_user.cpp -/// \note Must always be used in pair with #QS_END -#define QS_BEGIN(rec_, obj_) \ - if (((QS::glbFilter_[(uint8_t)(rec_) >> 3U] \ - & (1U << ((uint8_t)(rec_) & 7U))) != 0U) \ - && ((QS::apObj_ == (void *)0) || (QS::apObj_ == (obj_)))) \ - { \ - QS_INT_LOCK_KEY_ \ - QS_INT_LOCK_(); \ - QS::begin((uint8_t)(rec_)); \ - QS_TIME_(); - -/// \brief End a QS record with locking interrupts. -/// \sa example for #QS_BEGIN -/// \note Must always be used in pair with #QS_BEGIN -#define QS_END() \ - QS_END_() - - -////////////////////////////////////////////////////////////////////////////// -// Macros for use inside other macros or internally in the QP code - -/// \brief Internal QS macro to begin a QS record with locking the interrupts. -/// \note This macro is intended to use only inside QP components and NOT -/// at the application level. \sa #QS_BEGIN -#define QS_BEGIN_(rec_, objFilter_, obj_) \ - if (((QS::glbFilter_[(uint8_t)(rec_) >> 3U] \ - & (1U << ((uint8_t)(rec_) & 7U))) != 0U) \ - && (((objFilter_) == (void *)0) || ((objFilter_) == (obj_)))) \ - { \ - QS_INT_LOCK_(); \ - QS::begin((uint8_t)(rec_)); - -/// \brief Internal QS macro to end a QS record with locking the interrupts. -/// \note This macro is intended to use only inside QP components and NOT -/// at the application level. \sa #QS_END -#define QS_END_() \ - QS::end(); \ - QS_INT_UNLOCK_(); \ - } - -/// \brief Internal QS macro to begin a QS record without locking the -/// interrupts. -/// \note This macro is intended to use only inside QP components and NOT -/// at the application level. \sa #QS_BEGIN_NOLOCK -#define QS_BEGIN_NOLOCK_(rec_, objFilter_, obj_) \ - if (((QS::glbFilter_[(uint8_t)(rec_) >> 3U] \ - & (1U << ((uint8_t)(rec_) & 7U))) != 0U) \ - && (((objFilter_) == (void *)0) || ((objFilter_) == (obj_)))) \ - { \ - QS::begin((uint8_t)(rec_)); - -/// \brief Internal QS macro to end a QS record without locking -/// the interrupts. -/// \note This macro is intended to use only inside QP components and NOT -/// at the application level. \sa #QS_END_NOLOCK -#define QS_END_NOLOCK_() \ - QS::end(); \ - } - -/// \brief Internal QS macro to output an unformatted uint8_t data element -#define QS_U8_(data_) QS::u8_(data_) - -/// \brief Internal QS macro to output an unformatted uint16_t data element -#define QS_U16_(data_) QS::u16_(data_) - -/// \brief Internal QS macro to output an unformatted uint32_t data element -#define QS_U32_(data_) QS::u32_(data_) - - -#if (QS_OBJ_PTR_SIZE == 1) - #define QS_OBJ_(obj_) QS::u8_((uint8_t)(obj_)) -#elif (QS_OBJ_PTR_SIZE == 2) - #define QS_OBJ_(obj_) QS::u16_((uint16_t)(obj_)) -#elif (QS_OBJ_PTR_SIZE == 4) - #define QS_OBJ_(obj_) QS::u32_((uint32_t)(obj_)) -#elif (QS_OBJ_PTR_SIZE == 8) - #define QS_OBJ_(obj_) QS::u64_((uint64_t)(obj_)) -#else - - /// \brief Internal QS macro to output an unformatted object pointer - /// data element - /// \note the size of the pointer depends on the macro #QS_OBJ_PTR_SIZE. - /// If the size is not defined the size of pointer is assumed 4-bytes. - #define QS_OBJ_(obj_) QS::u32_((uint32_t)(obj_)) -#endif - - -#if (QS_FUN_PTR_SIZE == 1) - #define QS_FUN_(fun_) QS::u8_((uint8_t)(fun_)) -#elif (QS_FUN_PTR_SIZE == 2) - #define QS_FUN_(fun_) QS::u16_((uint16_t)(fun_)) -#elif (QS_FUN_PTR_SIZE == 4) - #define QS_FUN_(fun_) QS::u32_((uint32_t)(fun_)) -#elif (QS_FUN_PTR_SIZE == 8) - #define QS_FUN_(fun_) QS::u64_((uint64_t)(fun_)) -#else - - /// \brief Internal QS macro to output an unformatted function pointer - /// data element - /// \note the size of the pointer depends on the macro #QS_FUN_PTR_SIZE. - /// If the size is not defined the size of pointer is assumed 4-bytes. - #define QS_FUN_(fun_) QS::u32_((uint32_t)(fun_)) -#endif - -/// \brief Internal QS macro to output a zero-terminated ASCII string -/// data element -#define QS_STR_(msg_) QS::str_(msg_) - -/// \brief Internal QS macro to output a zero-terminated ASCII string -/// allocated in ROM data element -#define QS_STR_ROM_(msg_) QS::str_ROM_(msg_) - -////////////////////////////////////////////////////////////////////////////// -// Macros for use in the client code - -/// \brief Enumerates data formats recognized by QS -/// -/// QS uses this enumeration is used only internally for the formatted user -/// data elements. -enum QSType { - QS_I8_T, ///< signed 8-bit integer format - QS_U8_T, ///< unsigned 8-bit integer format - QS_I16_T, ///< signed 16-bit integer format - QS_U16_T, ///< unsigned 16-bit integer format - QS_I32_T, ///< signed 32-bit integer format - QS_U32_T, ///< unsigned 32-bit integer format - QS_F32_T, ///< 32-bit floating point format - QS_F64_T, ///< 64-bit floating point format - QS_STR_T, ///< zero-terminated ASCII string format - QS_MEM_T, ///< up to 255-bytes memory block format - QS_SIG_T, ///< event signal format - QS_OBJ_T, ///< object pointer format - QS_FUN_T, ///< function pointer format - QS_I64_T, ///< signed 64-bit integer format - QS_U64_T, ///< unsigned 64-bit integer format - QS_U32_HEX_T ///< unsigned 32-bit integer in hex format -}; - -/// \brief Output formatted int8_t to the QS record -#define QS_I8(width_, data_) \ - QS::u8((uint8_t)(((width_) << 4)) | QS_I8_T, (data_)) - -/// \brief Output formatted uint8_t to the QS record -#define QS_U8(width_, data_) \ - QS::u8((uint8_t)(((width_) << 4)) | QS_U8_T, (data_)) - -/// \brief Output formatted int16_t to the QS record -#define QS_I16(width_, data_) \ - QS::u16((uint8_t)(((width_) << 4)) | QS_I16_T, (data_)) - -/// \brief Output formatted uint16_t to the QS record -#define QS_U16(width_, data_) \ - QS::u16((uint8_t)(((width_) << 4)) | QS_U16_T, (data_)) - -/// \brief Output formatted int32_t to the QS record -#define QS_I32(width_, data_) \ - QS::u32((uint8_t)(((width_) << 4)) | QS_I32_T, (data_)) - -/// \brief Output formatted uint32_t to the QS record -#define QS_U32(width_, data_) \ - QS::u32((uint8_t)(((width_) << 4)) | QS_U32_T, (data_)) - -/// \brief Output formatted 32-bit floating point number to the QS record -#define QS_F32(width_, data_) \ - QS::f32((uint8_t)(((width_) << 4)) | QS_F32_T, (data_)) - -/// \brief Output formatted 64-bit floating point number to the QS record -#define QS_F64(width_, data_) \ - QS::f64((uint8_t)(((width_) << 4)) | QS_F64_T, (data_)) - -/// \brief Output formatted int64_t to the QS record -#define QS_I64(width_, data_) \ - QS::u64((uint8_t)(((width_) << 4)) | QS_I64_T, (data_)) - -/// \brief Output formatted uint64_t to the QS record -#define QS_U64(width_, data_) \ - QS::u64((uint8_t)(((width_) << 4)) | QS_U64_T, (data_)) - -/// \brief Output formatted uint32_t to the QS record -#define QS_U32_HEX(width_, data_) \ - QS::u32((uint8_t)(((width_) << 4)) | QS_U32_HEX_T, (data_)) - -/// \brief Output formatted zero-terminated ASCII string to the QS record -#define QS_STR(str_) QS::str(str_) - -/// \brief Output formatted zero-terminated ASCII string from ROM -/// to the QS record -#define QS_STR_ROM(str_) QS::str_ROM(str_) - -/// \brief Output formatted memory block of up to 255 bytes to the QS -/// record -#define QS_MEM(mem_, size_) QS::mem((mem_), (size_)) - - -#if (QS_OBJ_PTR_SIZE == 1) - #define QS_OBJ(obj_) QS::u8(QS_OBJ_T, (uint8_t)(obj_)) -#elif (QS_OBJ_PTR_SIZE == 2) - #define QS_OBJ(obj_) QS::u16(QS_OBJ_T, (uint16_t)(obj_)) -#elif (QS_OBJ_PTR_SIZE == 4) - #define QS_OBJ(obj_) QS::u32(QS_OBJ_T, (uint32_t)(obj_)) -#elif (QS_OBJ_PTR_SIZE == 8) - #define QS_OBJ(obj_) QS::u64(QS_OBJ_T, (uint64_t)(obj_)) -#else - /// \brief Output formatted object pointer to the QS record - #define QS_OBJ(obj_) QS::u32(QS_OBJ_T, (uint32_t)(obj_)) -#endif - - -#if (QS_FUN_PTR_SIZE == 1) - #define QS_FUN(fun_) QS::u8(QS_FUN_T, (uint8_t)(fun_)) -#elif (QS_FUN_PTR_SIZE == 2) - #define QS_FUN(fun_) QS::u16(QS_FUN_T, (uint16_t)(fun_)) -#elif (QS_FUN_PTR_SIZE == 4) - #define QS_FUN(fun_) QS::u32(QS_FUN_T, (uint32_t)(fun_)) -#elif (QS_FUN_PTR_SIZE == 8) - #define QS_FUN(fun_) QS::u64(QS_FUN_T, (uint64_t)(fun_)) -#else - /// \brief Output formatted function pointer to the QS record - #define QS_FUN(fun_) QS::u32(QS_FUN_T, (uint32_t)(fun_)) -#endif - - -/// \brief Output signal dictionary record -/// -/// A signal dictionary record associates the numerical value of the signal -/// and the binary address of the state machine that consumes that signal -/// with the human-readable name of the signal. -/// -/// Providing a signal dictionary QS record can vastly improve readability of -/// the QS log, because instead of dealing with cryptic machine addresses the -/// QSpy host utility can display human-readable names. -/// -/// A signal dictionary entry is associated with both the signal value \a sig_ -/// and the state machine \a obj_, because signals are required to be unique -/// only within a given state machine and therefore the same numerical values -/// can represent different signals in different state machines. -/// -/// For the "global" signals that have the same meaning in all state machines -/// (such as globally published signals), you can specify a signal dictionary -/// entry with the \a obj_ parameter set to NULL. -/// -/// The following example shows the definition of signal dictionary entries -/// in the initial transition of the Table active object. Please note that -/// signals HUNGRY_SIG and DONE_SIG are associated with the Table state -/// machine only ("me" \a obj_ pointer). The EAT_SIG signal, on the other -/// hand, is global (0 \a obj_ pointer): -/// \include qs_sigDic.cpp -/// -/// \note The QSpy log utility must capture the signal dictionary record -/// in order to use the human-readable information. You need to connect to -/// the target before the dictionary entries have been transmitted. -/// -/// The following QSpy log example shows the signal dictionary records -/// generated from the Table initial transition and subsequent records that -/// show human-readable names of the signals: -/// \include qs_sigLog.txt -/// -/// The following QSpy log example shows the same sequence of records, but -/// with dictionary records removed. The human-readable signal names are not -/// available. -/// \include qs_sigLog0.txt -#define QS_SIG_DICTIONARY(sig_, obj_) \ - if (((QS::glbFilter_[(uint8_t)QS_SIG_DICTIONARY >> 3U] \ - & (1U << ((uint8_t)QS_SIG_DICTIONARY & 7U))) != 0U)) \ - { \ - static char const Q_ROM Q_ROM_VAR sig_name__[] = #sig_; \ - QS_INT_LOCK_KEY_ \ - QS_INT_LOCK_(); \ - QS::begin((uint8_t)QS_SIG_DICTIONARY); \ - QS_SIG_(sig_); \ - QS_OBJ_(obj_); \ - QS_STR_ROM_(sig_name__); \ - QS::end(); \ - QS_INT_UNLOCK_(); \ - QS::onFlush(); \ - } else ((void)0) - -/// \brief Output object dictionary record -/// -/// An object dictionary record associates the binary address of an object -/// in the target's memory with the human-readable name of the object. -/// -/// Providing an object dictionary QS record can vastly improve readability of -/// the QS log, because instead of dealing with cryptic machine addresses the -/// QSpy host utility can display human-readable object names. -/// -/// The following example shows the definition of object dictionary entry -/// for the Table active object: -/// \include qs_objDic.cpp -#define QS_OBJ_DICTIONARY(obj_) \ - if (((QS::glbFilter_[(uint8_t)QS_OBJ_DICTIONARY >> 3U] \ - & (1U << ((uint8_t)QS_OBJ_DICTIONARY & 7U))) != 0U)) \ - { \ - static char const Q_ROM Q_ROM_VAR obj_name__[] = #obj_; \ - QS_INT_LOCK_KEY_ \ - QS_INT_LOCK_(); \ - QS::begin((uint8_t)QS_OBJ_DICTIONARY); \ - QS_OBJ_(obj_); \ - QS_STR_ROM_(obj_name__); \ - QS::end(); \ - QS_INT_UNLOCK_(); \ - QS::onFlush(); \ - } else ((void)0) - -/// \brief Output function dictionary record -/// -/// A function dictionary record associates the binary address of a function -/// in the target's memory with the human-readable name of the function. -/// -/// Providing a function dictionary QS record can vastly improve readability -/// of the QS log, because instead of dealing with cryptic machine addresses -/// the QSpy host utility can display human-readable function names. -/// -/// The example from #QS_SIG_DICTIONARY shows the definition of a function -/// dictionary. -#define QS_FUN_DICTIONARY(fun_) \ - if (((QS::glbFilter_[(uint8_t)QS_FUN_DICTIONARY >> 3U] \ - & (1U << ((uint8_t)QS_FUN_DICTIONARY & 7U))) != 0U)) \ - { \ - static char const Q_ROM Q_ROM_VAR fun_name__[] = #fun_; \ - QS_INT_LOCK_KEY_ \ - QS_INT_LOCK_(); \ - QS::begin((uint8_t)QS_FUN_DICTIONARY); \ - QS_FUN_(fun_); \ - QS_STR_ROM_(fun_name__); \ - QS::end(); \ - QS_INT_UNLOCK_(); \ - QS::onFlush(); \ - } else ((void)0) - -/// \brief Flush the QS trace data to the host -/// -/// This macro invokes the QS::flush() platform-dependent callback function -/// to flush the QS trace buffer to the host. The function typically -/// busy-waits until all the data in the buffer is sent to the host. -/// This is acceptable only in the initial transient. -#define QS_FLUSH() QS::onFlush() - - -/// \brief Output the interrupt lock record -#define QF_QS_INT_LOCK() \ - QS_BEGIN_NOLOCK_(QS_QF_INT_LOCK, (void *)0, (void *)0); \ - QS_TIME_(); \ - QS_U8_((uint8_t)(++QF_intLockNest_)); \ - QS_END_NOLOCK_() - -/// \brief Output the interrupt unlock record -#define QF_QS_INT_UNLOCK() \ - QS_BEGIN_NOLOCK_(QS_QF_INT_UNLOCK, (void *)0, (void *)0); \ - QS_TIME_(); \ - QS_U8_((uint8_t)(QF_intLockNest_--)); \ - QS_END_NOLOCK_() - -/// \brief Output the interrupt entry record -#define QF_QS_ISR_ENTRY(isrnest_, prio_) \ - QS_BEGIN_NOLOCK_(QS_QF_ISR_ENTRY, (void *)0, (void *)0); \ - QS_TIME_(); \ - QS_U8_(isrnest_); \ - QS_U8_(prio_); \ - QS_END_NOLOCK_() - -/// \brief Output the interrupt exit record -#define QF_QS_ISR_EXIT(isrnest_, prio_) \ - QS_BEGIN_NOLOCK_(QS_QF_ISR_EXIT, (void *)0, (void *)0); \ - QS_TIME_(); \ - QS_U8_(isrnest_); \ - QS_U8_(prio_); \ - QS_END_NOLOCK_() - -/// \brief Execute an action that is only necessary for QS output -#define QF_QS_ACTION(act_) (act_) - -/// \brief interrupt-lock nesting level -/// -/// \note Not to be used by Clients directly, only in ports of QF -extern uint8_t QF_intLockNest_; - -// from "qep.h" -------------------------------------------------------------- - #if (Q_SIGNAL_SIZE == 1) - - /// \brief Internal QS macro to output an unformatted event signal - /// data element - /// \note the size of the pointer depends on the macro #Q_SIGNAL_SIZE. - #define QS_SIG_(sig_) QS::u8_(sig_) - #elif (Q_SIGNAL_SIZE == 2) - #define QS_SIG_(sig_) QS::u16_(sig_) - #elif (Q_SIGNAL_SIZE == 4) - #define QS_SIG_(sig_) QS::u32_(sig_) - #endif - -// from "qf.h" --------------------------------------------------------------- - #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 - -#else // Q_SPY - -// qs_dummy.h ================================================================ - -#define QS_INIT(arg_) ((uint8_t)1) -#define QS_EXIT() ((void)0) -#define QS_DUMP() ((void)0) -#define QS_FILTER_ON(rec_) ((void)0) -#define QS_FILTER_OFF(rec_) ((void)0) -#define QS_FILTER_SM_OBJ(obj_) ((void)0) -#define QS_FILTER_AO_OBJ(obj_) ((void)0) -#define QS_FILTER_MP_OBJ(obj_) ((void)0) -#define QS_FILTER_EQ_OBJ(obj_) ((void)0) -#define QS_FILTER_TE_OBJ(obj_) ((void)0) -#define QS_FILTER_AP_OBJ(obj_) ((void)0) - -#define QS_GET_BYTE(pByte_) ((uint16_t)0xFFFF) -#define QS_GET_BLOCK(pSize_) ((uint8_t *)0) - -#define QS_BEGIN(rec_, obj_) if (0) { -#define QS_END() } -#define QS_BEGIN_NOLOCK(rec_, obj_) if (0) { -#define QS_END_NOLOCK() } - -#define QS_I8(width_, data_) ((void)0) -#define QS_U8(width_, data_) ((void)0) -#define QS_I16(width_, data_) ((void)0) -#define QS_U16(width_, data_) ((void)0) -#define QS_I32(width_, data_) ((void)0) -#define QS_U32(width_, data_) ((void)0) -#define QS_F32(width_, data_) ((void)0) -#define QS_F64(width_, data_) ((void)0) -#define QS_U64(width_, data_) ((void)0) -#define QS_STR(str_) ((void)0) -#define QS_U32_HEX(width_, data_) ((void)0) -#define QS_STR_ROM(str_) ((void)0) -#define QS_MEM(mem_, size_) ((void)0) -#define QS_SIG(sig_, obj_) ((void)0) -#define QS_OBJ(obj_) ((void)0) -#define QS_FUN(fun_) ((void)0) - -#define QS_SIG_DICTIONARY(sig_, obj_) ((void)0) -#define QS_OBJ_DICTIONARY(obj_) ((void)0) -#define QS_FUN_DICTIONARY(fun_) ((void)0) -#define QS_FLUSH() ((void)0) - -// internal QS macros used only in the QP components ......................... -#define QS_INT_LOCK_KEY_ -#define QS_BEGIN_(rec_, refObj_, obj_) if (0) { -#define QS_END_() } -#define QS_BEGIN_NOLOCK_(rec_, refObj_, obj_) if (0) { -#define QS_END_NOLOCK_() } -#define QS_U8_(data_) ((void)0) -#define QS_U16_(data_) ((void)0) -#define QS_U32_(data_) ((void)0) -#define QS_U64_(data_) ((void)0) -#define QS_STR_(str_) ((void)0) -#define QS_TIME_() ((void)0) -#define QS_SIG_(sig_) ((void)0) -#define QS_EVS_(size_) ((void)0) -#define QS_OBJ_(obj_) ((void)0) -#define QS_FUN_(fun_) ((void)0) -#define QS_EQC_(ctr_) ((void)0) -#define QS_MPC_(ctr_) ((void)0) -#define QS_MPS_(size_) ((void)0) -#define QS_TEC_(ctr_) ((void)0) - -#define QF_QS_INT_LOCK() ((void)0) -#define QF_QS_INT_UNLOCK() ((void)0) -#define QF_QS_ISR_ENTRY(isrnest_, prio_) ((void)0) -#define QF_QS_ISR_EXIT(isrnest_, prio_) ((void)0) -#define QF_QS_ACTION(act_) ((void)0) - -#endif // Q_SPY - -#ifdef Q_USE_NAMESPACE -} // namespace QP -#endif - -////////////////////////////////////////////////////////////////////////////// -/** -* \brief Customizable QP assertions. -* -* Defines customizable and memory-efficient assertions applicable to -* embedded systems. This header file can be used in C, C++, and mixed C/C++ -* programs. -* -* \note The preprocessor switch Q_NASSERT disables checking assertions. -* In particular macros #Q_ASSERT, #Q_REQUIRE, #Q_ENSURE, #Q_INVARIANT, -* #Q_ERROR as well as #Q_ASSERT_ID, #Q_REQUIRE_ID, #Q_ENSURE_ID, -* #Q_INVARIANT_ID, and #Q_ERROR_ID do NOT evaluate the test condition -* passed as the argument to these macros. One notable exception is the -* macro #Q_ALLEGE, that still evaluates the test condition, but does -* not report assertion failures when the switch Q_NASSERT is defined. -*/ -#ifdef Q_NASSERT /* Q_NASSERT defined--assertion checking disabled */ - - #define Q_DEFINE_THIS_FILE - #define Q_DEFINE_THIS_MODULE(name_) - #define Q_ASSERT(test_) ((void)0) - #define Q_ASSERT_ID(id_, test_) ((void)0) - #define Q_ALLEGE(test_) ((void)(test_)) - #define Q_ALLEGE_ID(id_, test_) ((void)(test_)) - #define Q_ERROR() ((void)0) - #define Q_ERROR_ID(id_) ((void)0) - -#else /* Q_NASSERT not defined--assertion checking enabled */ - - #ifdef __cplusplus - extern "C" { - #endif - - /** callback invoked in case the condition passed to #Q_ASSERT, - * #Q_REQUIRE, #Q_ENSURE, #Q_ERROR, #Q_ALLEGE as well as #Q_ASSERT_ID, - * #Q_REQUIRE_ID, #Q_ENSURE_ID, #Q_ERROR_ID, and #Q_ALLEGE_ID evaluates - * to FALSE. - * - * \param file file name where the assertion failed - * \param line line number at which the assertion failed - */ - /*lint -sem(Q_onAssert, r_no) Q_onAssert() never returns */ - void Q_onAssert(char const Q_ROM * const Q_ROM_VAR file, int line); - - #ifdef __cplusplus - } - #endif - - /** Place this macro at the top of each C/C++ module to define the file - * name string using __FILE__ (NOTE: __FILE__ might contain lengthy path - * name). This file name will be used in reporting assertions in this file. - */ - #define Q_DEFINE_THIS_FILE \ - static char const Q_ROM Q_ROM_VAR l_this_file[] = __FILE__; - - /** Place this macro at the top of each C/C++ module to define the module - * name as the argument \a name_. This file name will be used in reporting - * assertions in this file. - */ - #define Q_DEFINE_THIS_MODULE(name_) \ - static char const Q_ROM Q_ROM_VAR l_this_file[] = #name_; - - /** General purpose assertion that makes sure the \a test_ argument is - * TRUE. Calls the Q_onAssert() callback if the \a test_ evaluates - * to FALSE. - * \note the \a test_ is NOT evaluated if assertions are disabled with - * the Q_NASSERT switch. - * \sa #Q_ASSERT_ID - */ - #define Q_ASSERT(test_) \ - if (test_) { \ - } \ - else (Q_onAssert(&l_this_file[0], __LINE__)) - - /** General purpose assertion that makes sure the \a test_ argument is - * TRUE. Calls the Q_onAssert() callback if the \a test_ evaluates - * to FALSE. The argument \a id_ is the ID number (unique within - * the file) of the assertion. This assertion style is better suited - * for unit testig, because it avoids the volatility of line numbers - * for indentifying assertions. - * \note the \a test_ is NOT evaluated if assertions are disabled with - * the Q_NASSERT switch. - * \sa #Q_ASSERT - */ - #define Q_ASSERT_ID(id_, test_) \ - if (test_) { \ - } \ - else (Q_onAssert(&l_this_file[0], (id_))) - - /** General purpose assertion that ALWAYS evaluates the \a test_ - * argument and calls the Q_onAssert() callback if the \a test_ - * evaluates to FALSE. - * \note the \a test_ argument IS always evaluated even when assertions - * are disabled with the Q_NASSERT macro. When the Q_NASSERT macro is - * defined, the Q_onAssert() callback is NOT called, even if the - * \a test_ evaluates to FALSE. - * \sa #Q_ALLEGE_ID - */ - #define Q_ALLEGE(test_) Q_ASSERT(test_) - - /** General purpose assertion that ALWAYS evaluates the \a test_ - * argument and calls the Q_onAssert() callback if the \a test_ - * evaluates to FALSE. This assertion style is better suited - * for unit testig, because it avoids the volatility of line numbers - * for indentifying assertions. - * \note the \a test_ argument IS always evaluated even when assertions - * are disabled with the Q_NASSERT macro. When the Q_NASSERT macro is - * defined, the Q_onAssert() callback is NOT called, even if the - * \a test_ evaluates to FALSE. - * \sa #Q_ALLEGE - */ - #define Q_ALLEGE_ID(id_, test_) Q_ASSERT_ID(id_, test_) - - /** Assertion that always calls the Q_onAssert() callback if - * ever executed. - * \note can be disabled with the Q_NASSERT switch. - * \sa #Q_ERROR_ID - */ - #define Q_ERROR() \ - (Q_onAssert(l_this_file, __LINE__)) - - /** Assertion that always calls the Q_onAssert() callback if - * ever executed. This assertion style is better suited for unit - * testig, because it avoids the volatility of line numbers for - * indentifying assertions. - * \note can be disabled with the Q_NASSERT switch. - * \sa #Q_ERROR - */ - #define Q_ERROR_ID(id_) \ - (Q_onAssert(l_this_file, (id_))) - - -#endif /* Q_NASSERT */ - -/** Assertion that checks for a precondition. This macro is equivalent to -* #Q_ASSERT, except the name provides a better documentation of the -* intention of this assertion. -* \sa #Q_REQUIRE_ID -*/ -#define Q_REQUIRE(test_) Q_ASSERT(test_) - -/** Assertion that checks for a precondition. This macro is equivalent to -* #Q_ASSERT_ID, except the name provides a better documentation of the -* intention of this assertion. -* \sa #Q_REQUIRE -*/ -#define Q_REQUIRE_ID(id_, test_) Q_ASSERT_ID(id_, test_) - -/** Assertion that checks for a postcondition. This macro is equivalent to -* #Q_ASSERT, except the name provides a better documentation of the -* intention of this assertion. -* \sa #Q_ENSURE_ID -*/ -#define Q_ENSURE(test_) Q_ASSERT(test_) - -/** Assertion that checks for a postcondition. This macro is equivalent to -* #Q_ASSERT_ID, except the name provides a better documentation of the -* intention of this assertion. -* \sa #Q_ENSURE -*/ -#define Q_ENSURE_ID(id_, test_) Q_ASSERT_ID(id_, test_) - -/** Assertion that checks for an invariant. This macro is equivalent to -* #Q_ASSERT, except the name provides a better documentation of the -* intention of this assertion. -* \sa #Q_INVARIANT_ID -*/ -#define Q_INVARIANT(test_) Q_ASSERT(test_) - -/** Assertion that checks for an invariant. This macro is equivalent to -* #Q_ASSERT_ID, except the name provides a better documentation of the -* intention of this assertion. -* \sa #Q_INVARIANT -*/ -#define Q_INVARIANT_ID(id_, test_) Q_ASSERT_ID(id_, test_) - -/** Compile-time assertion exploits the fact that in C/C++ a dimension of -* an array cannot be negative. The following declaration causes a compilation -* error if the compile-time expression (\a test_) is not TRUE. The assertion -* has no runtime side effects. -*/ -#define Q_ASSERT_COMPILE(test_) \ - extern char Q_assert_compile[(test_) ? 1 : -1] - -#endif // qp_h +////////////////////////////////////////////////////////////////////////////// +// Product: QP/C++ platform-independent interface +// 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 +////////////////////////////////////////////////////////////////////////////// +#ifndef qp_h +#define qp_h + +// "qevt.h" ================================================================== +/// \brief QEvt class and basic macros used by all QP components. +/// +/// This header file must be included, perhaps indirectly, in all modules +/// (*.cpp files) that use any component of QP/C++ (such as QEP, QF, or QK). + +////////////////////////////////////////////////////////////////////////////// +/// \brief The current QP version number +/// +/// \return version of the QP as a hex constant constant 0xXYZZ, where X is +/// a 1-digit major version number, Y is a 1-digit minor version number, and +/// ZZ is a 2-digit release number. +#define QP_VERSION 0x4502U + +#ifndef Q_ROM + /// \brief Macro to specify compiler-specific directive for placing a + /// constant object in ROM. + /// + /// Many compilers for Harvard-architecture MCUs provide non-stanard + /// extensions to support placement of objects in different memories. + /// In order to conserve the precious RAM, QP uses the Q_ROM macro for + /// all constant objects that can be allocated in ROM. + /// + /// To override the following empty definition, you need to define the + /// Q_ROM macro in the qep_port.h header file. Some examples of valid + /// Q_ROM macro definitions are: __code (IAR 8051 compiler), code (Keil + /// Cx51 compiler), PROGMEM (gcc for AVR), __flash (IAR for AVR). + #define Q_ROM +#endif +#ifndef Q_ROM_VAR // if NOT defined, provide the default definition + /// \brief Macro to specify compiler-specific directive for accessing a + /// constant object in ROM. + /// + /// Many compilers for MCUs provide different size pointers for + /// accessing objects in various memories. Constant objects allocated + /// in ROM (see #Q_ROM macro) often mandate the use of specific-size + /// pointers (e.g., far pointers) to get access to ROM objects. The + /// macro Q_ROM_VAR specifies the kind of the pointer to be used to access + /// the ROM objects. + /// + /// To override the following empty definition, you need to define the + /// Q_ROM_VAR macro in the qep_port.h header file. An example of valid + /// Q_ROM_VAR macro definition is: __far (Freescale HC(S)08 compiler). + #define Q_ROM_VAR +#endif +#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 + +#ifndef Q_SIGNAL_SIZE + /// \brief The size (in bytes) of the signal of an event. Valid values: + /// 1, 2, or 4; default 1 + /// + /// This macro can be defined in the QEP port file (qep_port.h) to + /// configure the ::QSignal type. When the macro is not defined, the + /// default of 1 byte is chosen. + #define Q_SIGNAL_SIZE 2 +#endif + +#ifndef Q_NNAMESPACE + + /// \brief begin of the namespace QP + /// + /// \note defines to nothing if #Q_USE_NAMESPACE is undefined + #define QP_BEGIN_ namespace QP { + + /// \brief end of the namespace QP + /// + /// \note defines to nothing if #Q_USE_NAMESPACE is undefined + #define QP_END_ } + + /// \brief namespace QP prefix + /// + /// \note defines to nothing if #Q_USE_NAMESPACE is undefined + #define QP_ QP:: + +#else + + #define QP_BEGIN_ + #define QP_END_ + #define QP_ + +#endif + +QP_BEGIN_ + +#if (Q_SIGNAL_SIZE == 1) + typedef uint8_t QSignal; +#elif (Q_SIGNAL_SIZE == 2) + /// \brief QSignal represents the signal of an event. + /// + /// The relationship between an event and a signal is as follows. A signal + /// in UML is the specification of an asynchronous stimulus that triggers + /// reactions [<A HREF="http://www.omg.org/docs/ptc/03-08-02.pdf">UML + /// document ptc/03-08-02</A>], and as such is an essential part of an + /// event. (The signal conveys the type of the occurrence-what happened?) + /// However, an event can also contain additional quantitative information + /// about the occurrence in form of event parameters. Please refer to the + typedef uint16_t QSignal; +#elif (Q_SIGNAL_SIZE == 4) + typedef uint32_t QSignal; +#else + #error "Q_SIGNAL_SIZE defined incorrectly, expected 1, 2, or 4" +#endif + +#ifdef Q_EVT_CTOR // Provide the constructor for the QEvt class? + + ////////////////////////////////////////////////////////////////////////// + /// \brief QEvt base class. + /// + /// QEvt represents events without parameters and serves as the base class + /// for derivation of events with parameters. + /// + /// The following example illustrates how to add an event parameter by + /// inheriting from the QEvt class. + /// \include qep_qevent.cpp + class QEvt { + public: + QSignal sig; ///< signal of the event instance + + QEvt(QSignal const s) // poolId_ intentionally uninitialized + : sig(s) {} + +#ifdef Q_EVT_VIRTUAL + virtual ~QEvt() {} // virtual destructor +#endif + + private: + uint8_t poolId_; ///< pool ID (0 for static event) + uint8_t refCtr_; ///< reference counter + + friend class QF; + friend class QTimeEvt; + friend uint8_t QF_EVT_POOL_ID_ (QEvt const * const e); + friend uint8_t QF_EVT_REF_CTR_ (QEvt const * const e); + friend void QF_EVT_REF_CTR_INC_(QEvt const * const e); + friend void QF_EVT_REF_CTR_DEC_(QEvt const * const e); + }; + +#else // QEvt is a POD (Plain Old Datatype) + + struct QEvt { + QSignal sig; ///< signal of the event instance + uint8_t poolId_; ///< pool ID (0 for static event) + uint8_t refCtr_; ///< reference counter + }; + +#endif // Q_EVT_CTOR + +QP_END_ + +////////////////////////////////////////////////////////////////////////////// +/// helper macro to calculate static dimension of a 1-dim array \a array_ +#define Q_DIM(array_) (sizeof(array_) / sizeof(array_[0])) + +////////////////////////////////////////////////////////////////////////////// +// typedefs for basic numerical types; MISRA-C++ 2008 rule 3-9-2(req). + +/// \brief typedef for character strings. +/// +/// This typedef specifies character type for exclusive use in character +/// strings. Use of this type, rather than plain 'char', is in compliance +/// with the MISRA-C 2004 Rules 6.1(req), 6.3(adv). +/// +typedef char char_t; + +/// typedef for 32-bit IEEE 754 floating point numbers +typedef float float32_t; + +/// typedef for 64-bit IEEE 754 floating point numbers +typedef double float64_t; + +/// typedef for enumerations used for event signals +typedef int enum_t; + +/// \brief Perform cast from unsigned integer \a uint_ to pointer +/// of type \a type_. +/// +/// This macro encapsulates the cast to (type_ *), which QP ports or +/// application might use to access embedded hardware registers. +/// Such uses can trigger PC-Lint "Note 923: cast from int to pointer" +/// and this macro helps to encapsulate this deviation. +/// +#define Q_UINT2PTR_CAST(type_, uint_) (reinterpret_cast<type_ *>(uint_)) + +/// \brief Initializer of static constant QEvt instances +/// +/// This macro encapsulates the ugly casting of enumerated signals +/// to QSignal and constants for QEvt.poolID and QEvt.refCtr_. +/// +#define QEVT_INITIALIZER(sig_) { static_cast<QP_ QSignal>(sig_), \ + static_cast<uint8_t>(0), static_cast<uint8_t>(0) } + +////////////////////////////////////////////////////////////////////////////// +// facilities for backwards compatibility +// +#ifndef Q_NQEVENT +// typedef struct QEvt QEvent; + #define QEvent QEvt +#endif + + +// "qep.h" =================================================================== +/// \brief QEP/C++ platform-independent public interface. +/// +/// This header file must be included directly or indirectly +/// in all modules (*.cpp files) that use QEP/C++. + +/// \brief Designates a target for an initial or regular transition. +/// Q_TRAN() can be used both in the FSMs and HSMs. +/// +/// \include qep_qtran.c +/// +#define Q_TRAN(target_) (me->tran(Q_STATE_CAST(target_))) + +/// \brief Designates the superstate of a given state in an HSM. +/// +/// \include qep_qhsm.c +/// +#define Q_SUPER(super_) (me->super(Q_STATE_CAST(super_))) + +/// \brief Perform cast to QStateHandler. +/// +/// This macro encapsulates the cast of a specific state handler function +/// pointer to QStateHandler, which violates MISRA-C 2004 rule 11.4(advisory). +/// This macro helps to localize this deviation. +/// +#define Q_STATE_CAST(handler_) \ + (reinterpret_cast<QP_ QStateHandler>(handler_)) + +/// \brief Perform downcast of an event onto a subclass of QEvt \a class_ +/// +/// This macro encapsulates the downcast of QEvt pointers, which violates +/// MISRA-C 2004 rule 11.4(advisory). This macro helps to localize this +/// deviation. +/// +#define Q_EVT_CAST(class_) (static_cast<class_ const *>(e)) + +////////////////////////////////////////////////////////////////////////////// +QP_BEGIN_ + +/// \brief Provides miscellaneous QEP services. +class QEP { +public: + /// \brief get the current QEP version number string + /// + /// \return version of the QEP as a constant 6-character string of the + /// form x.y.zz, where x is a 1-digit major version number, y is a + /// 1-digit minor version number, and zz is a 2-digit release number. + static char_t const Q_ROM * Q_ROM_VAR getVersion(void); +}; + +////////////////////////////////////////////////////////////////////////////// + + /// \brief Type returned from a state-handler function +typedef uint8_t QState; + /// \brief pointer to state-handler function +typedef QState (*QStateHandler)(void * const me, QEvt const * const e); + +////////////////////////////////////////////////////////////////////////////// +/// \brief Value returned by a state-handler function when it handles +/// the event. +QState const Q_RET_HANDLED = static_cast<QState>(0); + +/// \brief Value returned by a non-hierarchical state-handler function when +/// it ignores (does not handle) the event. +QState const Q_RET_IGNORED = static_cast<QState>(1); + +/// \brief Value returned by a state-handler function when it takes a +/// regular state transition. +QState const Q_RET_TRAN = static_cast<QState>(2); + +/// \brief Value returned by a state-handler function when it forwards +/// the event to the superstate to handle. +QState const Q_RET_SUPER = static_cast<QState>(3); + +/// \brief Value returned by a state-handler function when a guard +/// condition prevents it from handling the event. +/// +QState const Q_RET_UNHANDLED = static_cast<QState>(4); + + +/// Offset or the user signals +enum_t const Q_USER_SIG = static_cast<enum_t>(4); + + +////////////////////////////////////////////////////////////////////////////// +/// \brief Finite State Machine base class +/// +/// QFsm represents a traditional non-hierarchical Finite State Machine (FSM) +/// without state hierarchy, but with entry/exit actions. +/// +/// QFsm is also a base structure for the ::QHsm class. +/// +/// \note QFsm is not intended to be instantiated directly, but rather serves +/// as the base class for derivation of state machines in the application +/// code. +/// +/// The following example illustrates how to derive a state machine class +/// from QFsm. +/// \include qep_qfsm.cpp +class QFsm { +private: + QStateHandler m_state; ///< current active state (state-variable) + QStateHandler m_temp; ///< temporary state: target of tran. or superstate + +public: + /// \brief virtual destructor + virtual ~QFsm(); + + /// \brief Performs the second step of FSM initialization by triggering + /// the top-most initial transition. + /// + /// The argument \a e is constant pointer to ::QEvt or a class + /// derived from ::QEvt. + /// + /// \note Must be called only ONCE before QFsm::dispatch() + /// + /// The following example illustrates how to initialize a FSM, and + /// dispatch events to it: + /// \include qep_qfsm_use.cpp + void init(QEvt const * const e = static_cast<QEvt const *>(0)); + + /// \brief Dispatches an event to a FSM + /// + /// Processes one event at a time in Run-to-Completion (RTC) fashion. + /// The argument \a e is a constant pointer the ::QEvt or a + /// class derived from ::QEvt. + /// + /// \note Must be called after QFsm::init(). + /// + /// \sa example for QFsm::init() + void dispatch(QEvt const * const e); + +protected: + /// \brief Protected constructor of a FSM. + /// + /// Performs the first step of FSM initialization by assigning the + /// initial pseudostate to the currently active state of the state + /// machine. + /// + /// \note The constructor is protected to prevent direct instantiating + /// of QFsm objects. This class is intended for subclassing only. + /// + /// \sa The ::QFsm example illustrates how to use the QHsm constructor + /// in the constructor initializer list of the derived state machines. + QFsm(QStateHandler const initial) + : m_state(Q_STATE_CAST(0)), + m_temp(initial) + {} + + /// \brief Return the current active state. + QStateHandler state(void) const { + return m_state; + } + + /// \brief Inline function to specify the return of a state-handler + /// when it igonres (does not handle in any way) the event. + static QState Q_IGNORED(void) { return Q_RET_IGNORED; } + + /// \brief Inline function to specify the return of a state-handler + /// when it handles the event. + /// + /// \include qep_qhsm.cpp + static QState Q_HANDLED(void) { return Q_RET_HANDLED; } + + /// \brief Macro to specify the return of a state-handler function when + /// it attempts to handle the event but a guard condition evaluates to + /// false and there is no other explicit way of handling the event. + static QState Q_UNHANDLED(void) { return Q_RET_UNHANDLED; } + + /// \brief internal helper function to record a state transition + QState tran(QStateHandler const target) { + m_temp = target; + return Q_RET_TRAN; + } + + enum ReservedFsmSignals { + Q_ENTRY_SIG = 1, ///< signal for entry actions + Q_EXIT_SIG ///< signal for exit actions + }; +}; + +////////////////////////////////////////////////////////////////////////////// +/// \brief Hierarchical State Machine base class +/// +/// QHsm represents a Hierarchical Finite State Machine (HSM). QHsm derives +/// from the ::QFsm class and extends the capabilities of a basic FSM +/// with state hierarchy. +/// +/// \note QHsm is not intended to be instantiated directly, but rather serves +/// as the base structure for derivation of state machines in the application +/// code. +/// +/// The following example illustrates how to derive a state machine class +/// from QHsm. +/// \include qep_qhsm.cpp +class QHsm { +private: + QStateHandler m_state; ///< current active state (state-variable) + QStateHandler m_temp; ///< temporary state: target of tran. or superstate + +public: + /// \brief virtual destructor + virtual ~QHsm(); + + /// \brief Performs the second step of HSM initialization by triggering + /// the top-most initial transition. + /// + /// \param e constant pointer ::QEvt or a class derived from ::QEvt + /// \note Must be called only ONCE before QHsm::dispatch() + /// + /// The following example illustrates how to initialize a HSM, and + /// dispatch events to it: + /// \include qep_qhsm_use.cpp + void init(QEvt const * const e = static_cast<QEvt const *>(0)); + + /// \brief Dispatches an event to a HSM + /// + /// Processes one event at a time in Run-to-Completion (RTC) fashion. + /// The argument \a e is a constant pointer the ::QEvt or a + /// class derived from ::QEvt. + /// + /// \note Must be called after QHsm::init(). + /// + /// \sa example for QHsm::init() + void dispatch(QEvt const * const e); + + /// \brief Tests if a given state is part of the current active state + /// configuratioin + /// + /// \param state is a pointer to the state handler function, e.g., + /// &QCalc::on. + bool isIn(QStateHandler const s); + +protected: + /// \brief Protected constructor of a HSM. + /// + /// Performs the first step of HSM initialization by assigning the + /// initial pseudostate to the currently active state of the state + /// machine. + /// + /// \note The constructor is protected to prevent direct instantiating + /// of QHsm objects. This class is intended for subclassing only. + /// + /// \sa The ::QHsm example illustrates how to use the QHsm constructor + /// in the constructor initializer list of the derived state machines. + /// \sa QFsm::QFsm() + QHsm(QStateHandler const initial) + : m_state(Q_STATE_CAST(&QHsm::top)), + m_temp(initial) + {} + + /// \brief Return the current active state. + QStateHandler state(void) const { + return m_state; + } + + /// \brief the top-state. + /// + /// QHsm::top() is the ultimate root of state hierarchy in all HSMs + /// derived from ::QHsm. This state handler always returns (QSTATE)0, + /// which means that it "handles" all events. + /// + /// \sa Example of the QCalc::on() state handler. + static QState top(void * const me, QEvt const * const e); + + /// \brief Inline function to specify the return of a state-handler + /// when it handles the event. + /// + /// \include qep_qhsm.cpp + static QState Q_HANDLED(void) { return Q_RET_HANDLED; } + + /// \brief Macro to specify the return of a state-handler function when + /// it attempts to handle the event but a guard condition evaluates to + /// false and there is no other explicit way of handling the event. + static QState Q_UNHANDLED(void) { return Q_RET_UNHANDLED; } + + /// \brief internal helper function to record a state transition + QState tran(QStateHandler const target) { + m_temp = target; + return Q_RET_TRAN; + } + + /// \brief internal helper function to record the superstate + QState super(QStateHandler const superstate) { + m_temp = superstate; + return Q_RET_SUPER; + } + + enum ReservedHsmSignals { + Q_ENTRY_SIG = 1, ///< signal for entry actions + Q_EXIT_SIG, ///< signal for exit actions + Q_INIT_SIG ///< signal for nested initial transitions + }; +}; + +////////////////////////////////////////////////////////////////////////////// + +QP_END_ + +// "qequeue.h" =============================================================== +/// \brief platform-independent event queue interface. +/// +/// This header file must be included in all QF ports that use native QF +/// event queue implementation. Also, this file is needed when the "raw" +/// thread-safe queues are used for communication between active objects +/// and non-framework entities, such as ISRs, device drivers, or legacy +/// code. + +#ifndef QF_EQUEUE_CTR_SIZE + + /// \brief The size (in bytes) of the ring-buffer counters used in the + /// native QF event queue implementation. Valid values: 1, 2, or 4; + /// default 1. + /// + /// This macro can be defined in the QF port file (qf_port.h) to + /// configure the ::QEQueueCtr type. Here the macro is not defined so the + /// default of 1 byte is chosen. + #define QF_EQUEUE_CTR_SIZE 1 +#endif + +QP_BEGIN_ + +#if (QF_EQUEUE_CTR_SIZE == 1) + /// \brief The data type to store the ring-buffer counters based on + /// the macro #QF_EQUEUE_CTR_SIZE. + /// + /// The dynamic range of this data type determines the maximum length + /// of the ring buffer managed by the native QF event queue. + typedef uint8_t QEQueueCtr; +#elif (QF_EQUEUE_CTR_SIZE == 2) + typedef uint16_t QEQueueCtr; +#elif (QF_EQUEUE_CTR_SIZE == 4) + typedef uint32_t QEQueueCtr; +#else + #error "QF_EQUEUE_CTR_SIZE defined incorrectly, expected 1, 2, or 4" +#endif + + +////////////////////////////////////////////////////////////////////////////// +/// \brief Native QF Event Queue class +/// +/// This structure describes the native QF event queue, which can be used as +/// the event queue for active objects, or as a simple "raw" event queue for +/// thread-safe event passing among non-framework entities, such as ISRs, +/// device drivers, or other third-party components. +/// +/// The native QF event queue is configured by defining the macro +/// #QF_EQUEUE_TYPE as ::QEQueue in the specific QF port header file. +/// +/// The ::QEQueue structure contains only data members for managing an event +/// queue, but does not contain the storage for the queue buffer, which must +/// be provided externally during the queue initialization. +/// +/// The event queue can store only event pointers, not the whole events. The +/// internal implementation uses the standard ring-buffer plus one external +/// location that optimizes the queue operation for the most frequent case +/// of empty queue. +/// +/// The ::QEQueue structure is used with two sets of functions. One set is for +/// the active object event queue, which needs to block the active object +/// task when the event queue is empty and unblock it when events are posted +/// to the queue. The interface for the native active object event queue +/// consists of the following functions: QActive::postFIFO_(), +/// QActive::postLIFO_(), and QActive::get_(). Additionally the function +/// QEQueue_init() is used to initialize the queue. +/// +/// The other set of functions, uses this structure as a simple "raw" event +/// queue to pass events between entities other than active objects, such as +/// ISRs. The "raw" event queue is not capable of blocking on the get() +/// operation, but is still thread-safe because it uses QF critical section +/// to protect its integrity. The interface for the "raw" thread-safe queue +/// consists of the following functions: QEQueue::postFIFO(), +/// QEQueue::postLIFO(), and QEQueue::get(). Additionally the function +/// QEQueue::init() is used to initialize the queue. +/// +/// \note Most event queue operations (both the active object queues and +/// the "raw" queues) internally use the QF critical section. You should be +/// careful not to invoke those operations from other critical sections when +/// nesting of critical sections is not supported. +class QEQueue { +private: + + /// \brief pointer to event at the front of the queue + /// + /// All incoming and outgoing events pass through the m_frontEvt location. + /// When the queue is empty (which is most of the time), the extra + /// m_frontEvt location allows to bypass the ring buffer altogether, + /// greatly optimizing the performance of the queue. Only bursts of events + /// engage the ring buffer. + /// + /// The additional role of this attribute is to indicate the empty status + /// of the queue. The queue is empty if the m_frontEvt location is NULL. + QEvt const *m_frontEvt; + + /// \brief pointer to the start of the ring buffer + QEvt const **m_ring; + + /// \brief offset of the end of the ring buffer from the start of the + /// buffer m_ring + QEQueueCtr m_end; + + /// \brief offset to where next event will be inserted into the buffer + QEQueueCtr m_head; + + /// \brief offset of where next event will be extracted from the buffer + QEQueueCtr m_tail; + + /// \brief number of free events in the ring buffer + QEQueueCtr m_nFree; + + /// \brief minimum number of free events ever in the ring buffer. + /// + /// \note this attribute remembers the low-watermark of the ring buffer, + /// which provides a valuable information for sizing event queues. + /// \sa QF::getQueueMargin(). + QEQueueCtr m_nMin; + +public: + + /// \brief Initializes the native QF event queue + /// + /// The parameters are as follows: \a qSto[] is the ring buffer storage, + /// \a qLen is the length of the ring buffer in the units of event- + /// pointers. + /// + /// \note The actual capacity of the queue is qLen + 1, because of the + /// extra location fornEvt_. + void init(QEvt const *qSto[], QEQueueCtr const qLen); + + /// \brief "raw" thread-safe QF event queue implementation for the + /// First-In-First-Out (FIFO) event posting. You can call this function + /// from any task context or ISR context. Please note that this function + /// uses internally a critical section. + /// + /// \note The function raises an assertion if the native QF queue becomes + /// full and cannot accept the event. + /// + /// \sa QEQueue::postLIFO(), QEQueue::get() + void postFIFO(QEvt const * const e); + + /// \brief "raw" thread-safe QF event queue implementation for the + /// First-In-First-Out (FIFO) event posting. You can call this function + /// from any task context or ISR context. Please note that this function + /// uses internally a critical section. + /// + /// \note The function raises an assertion if the native QF queue becomes + /// full and cannot accept the event. + /// + /// \sa QEQueue::postLIFO(), QEQueue::get() + void postLIFO(QEvt const * const e); + + /// \brief "raw" thread-safe QF event queue implementation for the + /// Last-In-First-Out (LIFO) event posting. + /// + /// \note The LIFO policy should be used only with great caution because + /// it alters order of events in the queue. + /// \note The function raises an assertion if the native QF queue becomes + /// full and cannot accept the event. You can call this function from + /// any task context or ISR context. Please note that this function uses + /// internally a critical section. + /// + /// \sa QEQueue::postFIFO(), QEQueue::get() + QEvt const *get(void); + + /// \brief "raw" thread-safe QF event queue operation for + /// obtaining the number of free entries still available in the queue. + /// + /// \note This operation needs to be used with caution because the + /// number of free entries can change unexpectedly. The main intent for + /// using this operation is in conjunction with event deferral. In this + /// case the queue is accessed only from a single thread (by a single AO), + /// so the number of free entries cannot change unexpectedly. + /// + /// \sa QActive::defer(), QActive::recall() + QEQueueCtr getNFree(void) const { + return m_nFree; + } + +private: + friend class QF; + friend class QActive; +}; + +QP_END_ + +// "qmpool.h" ================================================================ +/// \brief platform-independent memory pool interface. +/// +/// This header file must be included in all QF ports that use native QF +/// memory pool implementation. + +#ifndef QF_MPOOL_SIZ_SIZE + /// \brief macro to override the default ::QMPoolSize size. + /// Valid values 1, 2, or 4; default 2 + #define QF_MPOOL_SIZ_SIZE 2 +#endif + +#ifndef QF_MPOOL_CTR_SIZE + + /// \brief macro to override the default QMPoolCtr size. + /// Valid values 1, 2, or 4; default 2 + #define QF_MPOOL_CTR_SIZE 2 +#endif + +////////////////////////////////////////////////////////////////////////////// +QP_BEGIN_ + +#if (QF_MPOOL_SIZ_SIZE == 1) + /// \brief The data type to store the block-size based on the macro + /// #QF_MPOOL_SIZ_SIZE. + /// + /// The dynamic range of this data type determines the maximum size + /// of blocks that can be managed by the native QF event pool. + typedef uint8_t QMPoolSize; +#elif (QF_MPOOL_SIZ_SIZE == 2) + + typedef uint16_t QMPoolSize; +#elif (QF_MPOOL_SIZ_SIZE == 4) + typedef uint32_t QMPoolSize; +#else + #error "QF_MPOOL_SIZ_SIZE defined incorrectly, expected 1, 2, or 4" +#endif + +#if (QF_MPOOL_CTR_SIZE == 1) + /// \brief The data type to store the block-counter based on the macro + /// #QF_MPOOL_CTR_SIZE. + /// + /// The dynamic range of this data type determines the maximum number + /// of blocks that can be stored in the pool. + typedef uint8_t QMPoolCtr; +#elif (QF_MPOOL_CTR_SIZE == 2) + typedef uint16_t QMPoolCtr; +#elif (QF_MPOOL_CTR_SIZE == 4) + typedef uint32_t QMPoolCtr; +#else + #error "QF_MPOOL_CTR_SIZE defined incorrectly, expected 1, 2, or 4" +#endif + +////////////////////////////////////////////////////////////////////////////// +/// \brief Native QF memory pool class +/// +/// This class describes the native QF memory pool, which can be used as +/// the event pool for dynamic event allocation, or as a fast, deterministic +/// fixed block-size heap for any other objects in your application. +/// +/// The ::QMPool structure contains only data members for managing a memory +/// pool, but does not contain the pool storage, which must be provided +/// externally during the pool initialization. +/// +/// The native QF event pool is configured by defining the macro +/// #QF_EPOOL_TYPE_ as QEQueue in the specific QF port header file. +class QMPool { +private: + + /// start of the memory managed by this memory pool + void *m_start; + + /// end of the memory managed by this memory pool + void *m_end; + + /// linked list of free blocks + void *m_free; + + /// maximum block size (in bytes) + QMPoolSize m_blockSize; + + /// total number of blocks + QMPoolCtr m_nTot; + + /// number of free blocks remaining + QMPoolCtr m_nFree; + + /// minimum number of free blocks ever present in this pool + /// + /// \note this attribute remembers the low watermark of the pool, + /// which provides a valuable information for sizing event pools. + /// \sa QF::getPoolMargin(). + QMPoolCtr m_nMin; + +public: + + /// \brief Initializes the native QF event pool + /// + /// The parameters are as follows: \a poolSto is the pool storage, + /// \a poolSize is the size of the pool storage in bytes, and + /// \a blockSize is the block size of this pool. + /// + /// The caller of this method must make sure that the \a poolSto pointer + /// is properly aligned. In particular, it must be possible to efficiently + /// store a pointer at the location pointed to by \a poolSto. + /// Internally, the QMPool::init() function rounds up the block size + /// \a blockSize so that it can fit an integer number of pointers. + /// This is done to achieve proper alignment of the blocks within the + /// pool. + /// + /// \note Due to the rounding of block size the actual capacity of the + /// pool might be less than (\a poolSize / \a blockSize). You can check + /// the capacity of the pool by calling the QF::getPoolMargin() function. + void init(void * const poolSto, uint32_t const poolSize, + QMPoolSize const blockSize); + + /// \brief Obtains a memory block from a memory pool. + /// + /// The only parameter \a me is a pointer to the ::QMPool from which the + /// block is requested. The function returns a pointer to the allocated + /// memory block or NULL if no free blocks are available. + /// + /// A allocated block must be returned to the same pool from which it has + /// been allocated. + /// + /// This function can be called from any task level or ISR level. + /// + /// \note The memory pool \a me must be initialized before any events can + /// be requested from it. Also, the QMPool::get() function uses internally + /// a QF critical section, so you should be careful not to call it from + /// within a critical section when nesting of critical section is not + /// supported. + /// + /// \sa QMPool::put() + void *get(void); + + /// \brief Returns a memory block back to a memory pool. + /// + /// + /// This function can be called from any task level or ISR level. + /// + /// \note The block must be allocated from the same memory pool to which + /// it is returned. The QMPool::put() function raises an assertion if the + /// returned pointer to the block points outside of the original memory + /// buffer managed by the memory pool. Also, the QMPool::put() function + /// uses internally a QF critical section, so you should be careful not + /// to call it from within a critical section when nesting of critical + /// section is not supported. + /// + /// \sa QMPool::get() + void put(void * const b); + + /// \brief return the fixed block-size of the blocks managed by this pool + QMPoolSize getBlockSize(void) const { + return m_blockSize; + } + +private: + friend class QF; +}; + +QP_END_ + +/// \brief Memory pool element to allocate correctly aligned storage +/// for QMPool class. +/// +#define QF_MPOOL_EL(type_) \ + struct { void *sto_[((sizeof(type_) - 1U)/sizeof(void*)) + 1U]; } + +// "qpset.h" ================================================================= +/// \brief platform-independent priority sets of 8 or 64 elements. +/// +/// This header file must be included in those QF ports that use the +/// cooperative multitasking QF scheduler or the QK. + +QP_BEGIN_ + +////////////////////////////////////////////////////////////////////////////// +// useful lookup tables + +/// \brief Lookup table for (log2(n) + 1), where n is the index +/// into the table. +/// +/// This lookup delivers the 1-based number of the most significant 1-bit +/// of a byte. +/// +/// \note Index range n = 0..255. The first index (n == 0) should never +/// be used. +/// +extern uint8_t const Q_ROM Q_ROM_VAR QF_log2Lkup[256]; + +/// \brief Lookup table for (1 << ((n-1) % 8)), where n is the index +/// into the table. +/// +/// \note Index range n = 0..64. The first index (n == 0) should never +/// be used. +extern uint8_t const Q_ROM Q_ROM_VAR QF_pwr2Lkup[65]; + +/// \brief Lookup table for ~(1 << ((n-1) % 8)), where n is the index +/// into the table. +/// +/// \note Index range n = 0..64. The first index (n == 0) should never +/// be used. +extern uint8_t const Q_ROM Q_ROM_VAR QF_invPwr2Lkup[65]; + +/// \brief Lookup table for (n-1)/8 +/// +/// \note Index range n = 0..64. The first index (n == 0) should never +/// be used. +extern uint8_t const Q_ROM Q_ROM_VAR QF_div8Lkup[65]; + + +////////////////////////////////////////////////////////////////////////////// +/// \brief Priority Set of up to 8 elements for building various schedulers, +/// but also useful as a general set of up to 8 elements of any kind. +/// +/// The priority set represents the set of active objects that are ready to +/// run and need to be considered by scheduling processing. The set is capable +/// of storing up to 8 priority levels. +class QPSet8 { + + ////////////////////////////////////////////////////////////////////////// + /// \brief bimask representing elements of the set + uint8_t m_bits; + +public: + + /// \brief the function evaluates to TRUE if the set is empty, + /// which means that no active objects are ready to run. + bool isEmpty(void) const { + return (m_bits == static_cast<uint8_t>(0)); + } + + /// \brief the function evaluates to TRUE if the set has elements, + /// which means that some active objects are ready to run. + bool notEmpty(void) const { + return (m_bits != static_cast<uint8_t>(0)); + } + + /// \brief the function evaluates to TRUE if the priority set has the + /// element \a n. + bool hasElement(uint8_t const n) const { + return ((m_bits & Q_ROM_BYTE(QF_pwr2Lkup[n])) + != static_cast<uint8_t>(0)); + } + + /// \brief insert element \a n into the set, n = 1..8 + void insert(uint8_t const n) { + m_bits |= Q_ROM_BYTE(QF_pwr2Lkup[n]); + } + + /// \brief remove element \a n from the set, n = 1..8 + void remove(uint8_t const n) { + m_bits &= Q_ROM_BYTE(QF_invPwr2Lkup[n]); + } + + /// \brief find the maximum element in the set, + /// \note returns zero if the set is empty + uint8_t findMax(void) const { + return Q_ROM_BYTE(QF_log2Lkup[m_bits]); + } + + friend class QPSet64; +}; + +////////////////////////////////////////////////////////////////////////////// +/// \brief Priority Set of up to 64 elements for building various schedulers, +/// but also useful as a general set of up to 64 elements of any kind. +/// +/// The priority set represents the set of active objects that are ready to +/// run and need to be considered by scheduling processing. The set is capable +/// of storing up to 64 priority levels. +/// +/// The priority set allows to build cooperative multitasking schedulers +/// to manage up to 64 tasks. It is also used in the Quantum Kernel (QK) +/// preemptive scheduler. +/// +/// The inherited 8-bit set is used as the 8-elemtn set of of 8-bit subsets +/// Each bit in the super.bits set represents a subset (8-elements) +/// as follows: \n +/// bit 0 in this->m_bits is 1 when subset[0] is not empty \n +/// bit 1 in this->m_bits is 1 when subset[1] is not empty \n +/// bit 2 in this->m_bits is 1 when subset[2] is not empty \n +/// bit 3 in this->m_bits is 1 when subset[3] is not empty \n +/// bit 4 in this->m_bits is 1 when subset[4] is not empty \n +/// bit 5 in this->m_bits is 1 when subset[5] is not empty \n +/// bit 6 in this->m_bits is 1 when subset[6] is not empty \n +/// bit 7 in this->m_bits is 1 when subset[7] is not empty \n +class QPSet64 { + + ////////////////////////////////////////////////////////////////////////// + /// \brief bimask representing 8-element subsets of the set + uint8_t m_bytes; + + /// \brief bits representing elements in the set as follows: \n + /// m_bits[0] represent elements 1..8 \n + /// m_bits[1] represent elements 9..16 \n + /// m_bits[2] represent elements 17..24 \n + /// m_bits[3] represent elements 25..32 \n + /// m_bits[4] represent elements 33..40 \n + /// m_bits[5] represent elements 41..48 \n + /// m_bits[6] represent elements 49..56 \n + /// m_bits[7] represent elements 57..64 \n + uint8_t m_bits[8]; + +public: + + /// \brief the function evaluates to TRUE if the set is empty, + /// which means that no active objects are ready to run. + bool isEmpty(void) const { + return (m_bytes == static_cast<uint8_t>(0)); + } + + /// \brief the function evaluates to TRUE if the set has elements, + /// which means that some active objects are ready to run. + bool notEmpty(void) const { + return (m_bytes != static_cast<uint8_t>(0)); + } + + /// \brief the function evaluates to TRUE if the priority set has the + /// element \a n. + bool hasElement(uint8_t const n) const { + uint8_t const m = Q_ROM_BYTE(QF_div8Lkup[n]); + return ((m_bits[m] & Q_ROM_BYTE(QF_pwr2Lkup[n])) + != static_cast<uint8_t>(0)); + } + + /// \brief insert element \a n into the set, n = 1..64 + void insert(uint8_t const n) { + uint8_t m = Q_ROM_BYTE(QF_div8Lkup[n]); + m_bits[m] |= Q_ROM_BYTE(QF_pwr2Lkup[n]); + m_bytes |= Q_ROM_BYTE(QF_pwr2Lkup[m + static_cast<uint8_t>(1)]); + } + + /// \brief remove element \a n from the set, n = 1..64 + void remove(uint8_t const n) { + uint8_t m = Q_ROM_BYTE(QF_div8Lkup[n]); + if ((m_bits[m] &= Q_ROM_BYTE(QF_invPwr2Lkup[n])) + == static_cast<uint8_t>(0)) + { + m_bytes &=Q_ROM_BYTE(QF_invPwr2Lkup[m + static_cast<uint8_t>(1)]); + } + } + + /// \brief find the maximum element in the set, + /// \note returns zero if the set is empty + uint8_t findMax(void) const { + uint8_t n; + if (m_bytes != static_cast<uint8_t>(0)) { + n = static_cast<uint8_t>(Q_ROM_BYTE(QF_log2Lkup[m_bytes]) + - static_cast<uint8_t>(1)); + n = static_cast<uint8_t>( + Q_ROM_BYTE(QF_log2Lkup[m_bits[n]]) + + static_cast<uint8_t>(n << 3)); + } + else { + n = static_cast<uint8_t>(0); + } + return n; + } +}; + +QP_END_ + + +////////////////////////////////////////////////////////////////////////////// +// Kernel selection based on QK_PREEMPTIVE +// +#ifdef QK_PREEMPTIVE + +// "qk.h" ==================================================================== +/// \brief This macro defines the type of the event queue used for the +/// active objects. +/// +/// \note This is just an example of the macro definition. Typically, you need +/// to define it in the specific QF port file (qf_port.h). In case of QK, +/// which always depends on the native QF queue, this macro is defined at the +/// level of the platform-independent interface qk.h. +#define QF_EQUEUE_TYPE QEQueue + +#if defined(QK_TLS) || defined(QK_EXT_SAVE) + /// \brief This macro defines the type of the OS-Object used for blocking + /// the native QF event queue when the queue is empty + /// + /// In QK, the OS object is used to hold the per-thread flags, which might + /// be used, for example, to rembember the thread attributes (e.g., + /// if the thread uses a floating point co-processor). The OS object value + /// is set on per-thread basis in QActive::start(). Later, the extended + /// context switch macros (QK_EXT_SAVE() and QK_EXT_RESTORE()) might use + /// the per-thread flags to determine what kind of extended context switch + /// this particular thread needs (e.g., the thread might not be using the + /// coprocessor or might be using a different one). + #define QF_OS_OBJECT_TYPE uint8_t + + /// \brief This macro defines the type of the thread handle used for the + /// active objects. + /// + /// The thread type in QK is the pointer to the thread-local storage (TLS) + /// This thread-local storage can be set on per-thread basis in + /// QActive::start(). Later, the QK scheduler, passes the pointer to the + /// thread-local storage to the macro #QK_TLS. + #define QF_THREAD_TYPE void * +#endif // QK_TLS || QK_EXT_SAVE + +#if (QF_MAX_ACTIVE <= 8) + extern "C" QP_ QPSet8 QK_readySet_; ///< ready set of QK +#else + extern "C" QP_ QPSet64 QK_readySet_; ///< ready set of QK +#endif + +extern "C" uint8_t QK_currPrio_; ///< current task/interrupt priority +extern "C" uint8_t QK_intNest_; ///< interrupt nesting level + +// QK active object queue implementation ..................................... + +/// \brief Platform-dependent macro defining how QF should block the calling +/// task when the QF native queue is empty +/// +/// \note This is just an example of #QACTIVE_EQUEUE_WAIT_ for the QK-port +/// of QF. QK never activates a task that has no events to process, so in this +/// case the macro asserts that the queue is not empty. In other QF ports you +// need to define the macro appropriately for the underlying kernel/OS you're +/// using. +#define QACTIVE_EQUEUE_WAIT_(me_) \ + Q_ASSERT((me_)->m_eQueue.m_frontEvt != static_cast<QEvt const *>(0)) + +/// \brief Platform-dependent macro defining how QF should signal the +/// active object task that an event has just arrived. +/// +/// The macro is necessary only when the native QF event queue is used. +/// The signaling of task involves unblocking the task if it is blocked. +/// +/// \note #QACTIVE_EQUEUE_SIGNAL_ is called from a critical section. +/// It might leave the critical section internally, but must restore +/// the critical section before exiting to the caller. +/// +/// \note This is just an example of #QACTIVE_EQUEUE_SIGNAL_ for the QK-port +/// of QF. In other QF ports you need to define the macro appropriately for +/// the underlying kernel/OS you're using. +#define QACTIVE_EQUEUE_SIGNAL_(me_) do { \ + QK_readySet_.insert((me_)->m_prio); \ + if (QK_intNest_ == static_cast<uint8_t>(0)) { \ + uint8_t p = QK_schedPrio_(); \ + if (p != static_cast<uint8_t>(0)) { \ + QK_sched_(p); \ + } \ + } \ +} while (false) + +/// \brief Platform-dependent macro defining the action QF should take +/// when the native QF event queue becomes empty. +/// +/// \note #QACTIVE_EQUEUE_ONEMPTY_ is called from a critical section. +/// It should not leave the critical section. +/// +/// \note This is just an example of #QACTIVE_EQUEUE_ONEMPTY_ for the QK-port +/// of QF. In other QF ports you need to define the macro appropriately for +/// the underlying kernel/OS you're using. +#define QACTIVE_EQUEUE_ONEMPTY_(me_) \ + QK_readySet_.remove((me_)->m_prio) + +// QK event pool operations .................................................. + +/// \brief This macro defines the type of the event pool used in this QF port. +/// +/// \note This is a specific implementation for the QK-port of QF. +/// In other QF ports you need to define the macro appropriately for +/// the underlying kernel/OS you're using. +#define QF_EPOOL_TYPE_ QMPool + +/// \brief Platform-dependent macro defining the event pool initialization +/// +/// \note This is a specific implementation for the QK-port of QF. +/// In other QF ports you need to define the macro appropriately for +/// the underlying kernel/OS you're using. +#define QF_EPOOL_INIT_(p_, poolSto_, poolSize_, evtSize_) \ + (p_).init((poolSto_), (poolSize_), \ + static_cast<QMPoolSize>(evtSize_)) + +/// \brief Platform-dependent macro defining how QF should obtain the +/// event pool block-size +/// +/// \note This is a specific implementation for the QK-port of QF. +/// In other QF ports you need to define the macro appropriately for +/// the underlying kernel/OS you're using. +#define QF_EPOOL_EVENT_SIZE_(p_) \ + static_cast<uint32_t>((p_).getBlockSize()) + +/// \brief Platform-dependent macro defining how QF should obtain an event +/// \a e_ from the event pool \a p_ +/// +/// \note This is a specific implementation for the QK-port of QF. +/// In other QF ports you need to define the macro appropriately for +/// the underlying kernel/OS you're using. +#define QF_EPOOL_GET_(p_, e_) ((e_) = static_cast<QEvt *>((p_).get())) + +/// \brief Platform-dependent macro defining how QF should return an event +/// \a e_ to the event pool \a p_ +/// +/// \note This is a specific implementation for the QK-port of QF. +/// In other QF ports you need to define the macro appropriately for +/// the underlying kernel/OS you're using. +#define QF_EPOOL_PUT_(p_, e_) ((p_).put(e_)) + +////////////////////////////////////////////////////////////////////////////// +QP_BEGIN_ + +#ifndef QK_NO_MUTEX + ////////////////////////////////////////////////////////////////////////// + /// \brief QK Mutex type. + /// + /// QMutex represents the priority-ceiling mutex available in QK. + /// \sa QK::mutexLock() + /// \sa QK::mutexUnlock() + typedef uint8_t QMutex; +#endif // QK_NO_MUTEX + +////////////////////////////////////////////////////////////////////////////// +/// \brief QK services. +/// +/// This class groups together QK services. It has only static members and +/// should not be instantiated. +/// +// \note The QK scheduler, QK priority, QK ready set, etc. belong conceptually +/// to the QK class (as static class members). However, to avoid C++ potential +/// name-mangling problems in assembly language, these elements are defined +/// outside of the QK class and use the extern "C" linkage specification. +/// +class QK { +public: + + /// \brief get the current QK version number string + /// + /// \return version of the QK as a constant 6-character string of the + /// form x.y.zz, where x is a 1-digit major version number, y is a + /// 1-digit minor version number, and zz is a 2-digit release number. + static char_t const Q_ROM * Q_ROM_VAR getVersion(void); + + /// \brief QK idle callback (customized in BSPs for QK) + /// + /// QK::onIdle() is called continously by the QK idle loop. This callback + /// gives the application an opportunity to enter a power-saving CPU mode, + /// or perform some other idle processing. + /// + /// \note QK::onIdle() is invoked with interrupts unlocked and must also + /// return with interrupts unlocked. + /// + /// \sa QF::onIdle() + static void onIdle(void); + +#ifndef QK_NO_MUTEX + + /// \brief QK priority-ceiling mutex lock + /// + /// Lock the QK scheduler up to the priority level \a prioCeiling. + /// + // \note This function should be always paired with QK::mutexUnlock(). + /// The code between QK::mutexLock() and QK::mutexUnlock() should be + /// kept to the minimum. + /// + /// \include qk_mux.cpp + static QMutex mutexLock(uint8_t const prioCeiling); + + /// \brief QK priority-ceiling mutex unlock + /// + /// \note This function should be always paired with QK::mutexLock(). + /// The code between QK::mutexLock() and QK::mutexUnlock() should be + /// kept to the minimum. + /// + /// \include qk_mux.cpp + static void mutexUnlock(QMutex const mutex); + +#endif // QK_NO_MUTEX + +}; + +QP_END_ + +////////////////////////////////////////////////////////////////////////////// +extern "C" { + +/// \brief QK initialization +/// +/// QK_init() is called from QF_init() in qk.cpp. This function is +/// defined in the QK ports. +void QK_init(void); + +/// \brief The QK scheduler +/// +/// \note The QK scheduler must be always called with the interrupts +/// disabled and enables interrupts internally. +/// +/// \sa QK_schedPrio_() +void QK_sched_(uint8_t p); + +/// \brief The QK extended scheduler for interrupt context +/// +/// \note The QK extended exscheduler must be always called with the +/// interrupts disabled and enables interrupts internally. +/// +/// \sa QK_schedPrio_() +void QK_schedExt_(uint8_t p); + +/// \brief Find the highest-priority task ready to run +/// +/// \note QK_schedPrio_() must be always called with interrupts disabled +/// and returns with interrupts disabled. +uint8_t QK_schedPrio_(void); + +} // extern "C" + +#else // QK_PREEMPTIVE + +// "qvanilla.h" ============================================================== +/// \brief platform-independent interface to the cooperative "vanilla" kernel. + +QP_BEGIN_ + // the event queue type for the "Vanilla" kernel +#define QF_EQUEUE_TYPE QEQueue + // native event queue operations +#define QACTIVE_EQUEUE_WAIT_(me_) \ + Q_ASSERT((me_)->m_eQueue.m_frontEvt != static_cast<QEvt const *>(0)) + +#define QACTIVE_EQUEUE_SIGNAL_(me_) \ + (QF_readySet_.insert((me_)->m_prio)) + +#define QACTIVE_EQUEUE_ONEMPTY_(me_) \ + (QF_readySet_.remove((me_)->m_prio)) + + // native QF event pool operations +#define QF_EPOOL_TYPE_ QMPool +#define QF_EPOOL_INIT_(p_, poolSto_, poolSize_, evtSize_) \ + (p_).init((poolSto_), (poolSize_), static_cast<QMPoolSize>(evtSize_)) +#define QF_EPOOL_EVENT_SIZE_(p_) \ + static_cast<uint32_t>((p_).getBlockSize()) +#define QF_EPOOL_GET_(p_, e_) ((e_) = static_cast<QEvt *>((p_).get())) +#define QF_EPOOL_PUT_(p_, e_) ((p_).put(e_)) + +QP_END_ + +#if (QF_MAX_ACTIVE <= 8) + extern "C" QP_ QPSet8 QF_readySet_; ///< ready set of active objects +#else + extern "C" QP_ QPSet64 QF_readySet_; ///< ready set of active objects +#endif + +extern "C" uint8_t QF_currPrio_; ///< current task/interrupt priority +extern "C" uint8_t QF_intNest_; ///< interrupt nesting level + +#endif // QK_PREEMPTIVE + + +// qf.h (QF platform-independent public interface) =========================== + +/// \brief QF/C++ platform-independent public interface. +/// +/// This header file must be included directly or indirectly +/// in all modules (*.cpp files) that use QF/C++. + +////////////////////////////////////////////////////////////////////////////// +#ifdef Q_EVT_CTOR +#include <new> // for placement new +#endif + +////////////////////////////////////////////////////////////////////////////// +// apply defaults for all undefined configuration parameters +// +#ifndef QF_EVENT_SIZ_SIZE + /// \brief Default value of the macro configurable value in qf_port.h + #define QF_EVENT_SIZ_SIZE 2 +#endif + +#ifndef QF_MAX_EPOOL + /// \brief Default value of the macro configurable value in qf_port.h + #define QF_MAX_EPOOL 3 +#endif + +#ifndef QF_TIMEEVT_CTR_SIZE + /// \brief macro to override the default QTimeEvtCtr size. + /// Valid values 1, 2, or 4; default 2 + #define QF_TIMEEVT_CTR_SIZE 2 +#endif + +#if (QF_MAX_ACTIVE < 1) || (63 < QF_MAX_ACTIVE) + #error "QF_MAX_ACTIVE not defined or out of range. Valid range is 1..63" +#endif + +#ifndef QF_ACTIVE_SUPER_ + + ////////////////////////////////////////////////////////////////////////// + /// \brief The macro defining the base class for QActive. + /// + /// By default, the ::QActive class is derived from ::QHsm. However, + /// if the macro QF_ACTIVE_SUPER_ is defined, QActive is derived from + /// QF_ACTIVE_SUPER_. + /// + /// Clients might choose, for example, to define QF_ACTIVE_SUPER_ as QFsm + /// to avoid the 1-2KB overhead of the hierarchical event processor. + /// + /// Clients might also choose to define QF_ACTIVE_SUPER_ as their own + /// completely customized class that has nothing to do with QHsm or QFsm. + /// The QF_ACTIVE_SUPER_ class must provide member functions init() and + /// dispatch(), consistent with the signatures of QHsm and QFsm. But + /// the implementatin of these functions is completely open. + #define QF_ACTIVE_SUPER_ QHsm + + /// \brief The argument of the base class' constructor. + #define QF_ACTIVE_STATE_ QStateHandler + +#endif + +////////////////////////////////////////////////////////////////////////////// +QP_BEGIN_ + +////////////////////////////////////////////////////////////////////////////// +#if (QF_EVENT_SIZ_SIZE == 1) + + /// \brief The data type to store the block-size defined based on + /// the macro #QF_EVENT_SIZ_SIZE. + /// + /// The dynamic range of this data type determines the maximum block + /// size that can be managed by the pool. + typedef uint8_t QEvtSize; +#elif (QF_EVENT_SIZ_SIZE == 2) + typedef uint16_t QEvtSize; +#elif (QF_EVENT_SIZ_SIZE == 4) + typedef uint32_t QEvtSize; +#else + #error "QF_EVENT_SIZ_SIZE defined incorrectly, expected 1, 2, or 4" +#endif + +////////////////////////////////////////////////////////////////////////////// +#if (QF_TIMEEVT_CTR_SIZE == 1) + /// \brief type of the Time Event counter, which determines the dynamic + /// range of the time delays measured in clock ticks. + /// + /// This typedef is configurable via the preprocessor switch + /// #QF_TIMEEVT_CTR_SIZE. The other possible values of this type are + /// as follows: \n + /// uint8_t when (QF_TIMEEVT_CTR_SIZE == 1), and \n + /// uint32_t when (QF_TIMEEVT_CTR_SIZE == 4). + typedef uint8_t QTimeEvtCtr; +#elif (QF_TIMEEVT_CTR_SIZE == 2) + typedef uint16_t QTimeEvtCtr; +#elif (QF_TIMEEVT_CTR_SIZE == 4) + typedef uint32_t QTimeEvtCtr; +#else + #error "QF_TIMEEVT_CTR_SIZE defined incorrectly, expected 1, 2, or 4" +#endif + +class QEQueue; // forward declaration + +////////////////////////////////////////////////////////////////////////////// +/// \brief Base class for derivation of application-level active object +/// classes. +/// +/// QActive is the base class for derivation of active objects. Active objects +/// in QF are encapsulated tasks (each embedding a state machine and an event +/// queue) that communicate with one another asynchronously by sending and +/// receiving events. Within an active object, events are processed +/// sequentially in a run-to-completion (RTC) fashion, while QF encapsulates +/// all the details of thread-safe event exchange and queuing. +/// +/// \note QActive is not intended to be instantiated directly, but rather +/// serves as the base class for derivation of active objects in the +/// application code. +/// +/// The following example illustrates how to derive an active object from +/// QActive. +/// \include qf_qactive.cpp +/// +/// \sa #QF_ACTIVE_SUPER_ defines the base class for QActive +class QActive : public QF_ACTIVE_SUPER_ { + +#ifdef QF_EQUEUE_TYPE + /// \brief OS-dependent event-queue type. + /// + /// The type of the queue depends on the underlying operating system or + /// a kernel. Many kernels support "message queues" that can be adapted + /// to deliver QF events to the active object. Alternatively, QF provides + /// a native event queue implementation that can be used as well. + /// + /// The native QF event queue is configured by defining the macro + /// #QF_EQUEUE_TYPE as ::QEQueue. + QF_EQUEUE_TYPE m_eQueue; +#endif + +#ifdef QF_OS_OBJECT_TYPE + /// \brief OS-dependent per-thread object. + /// + /// This data might be used in various ways, depending on the QF port. + /// In some ports m_osObject is used to block the calling thread when + /// the native QF queue is empty. In other QF ports the OS-dependent + /// object might be used differently. + QF_OS_OBJECT_TYPE m_osObject; +#endif + +#ifdef QF_THREAD_TYPE + /// \brief OS-dependent representation of the thread of the active + /// object. + /// + /// This data might be used in various ways, depending on the QF port. + /// In some ports m_thread is used store the thread handle. In other ports + /// m_thread can be a pointer to the Thread-Local-Storage (TLS). + QF_THREAD_TYPE m_thread; +#endif + + /// \brief QF priority associated with the active object. + /// \sa QActive::start() + uint8_t m_prio; + +public: + /// \brief Starts execution of an active object and registers the object + /// with the framework. + /// + /// The function takes six arguments. + /// \a prio is the priority of the active object. QF allows you to start + /// up to 63 active objects, each one having a unique priority number + /// between 1 and 63 inclusive, where higher numerical values correspond + /// to higher priority (urgency) of the active object relative to the + /// others. + /// \a qSto[] and \a qLen arguments are the storage and size of the event + /// queue used by this active object. + /// \a stkSto and \a stkSize are the stack storage and size in bytes. + /// Please note that a per-active object stack is used only when the + /// underlying OS requies it. If the stack is not required, or the + /// underlying OS allocates the stack internally, the \a stkSto should be + /// NULL and/or \a stkSize should be 0. + /// \a ie is an optional initialization event that can be used to pass + /// additional startup data to the active object. (Pass NULL if your + /// active object does not expect the initialization event). + /// + /// \note This function is strongly OS-dependent and must be defined in + /// the QF port to a particular platform. + /// + /// The following example shows starting of the Philosopher object when a + /// per-task stack is required: + /// \include qf_start.cpp + void start(uint8_t const prio, + QEvt const *qSto[], uint32_t const qLen, + void * const stkSto, uint32_t const stkSize, + QEvt const * const ie = static_cast<QEvt *>(0)); + + /// \brief Posts an event \a e directly to the event queue of the acitve + /// object \a me using the First-In-First-Out (FIFO) policy. + /// + /// Direct event posting is the simplest asynchronous communication method + /// available in QF. The following example illustrates how the Philosopher + /// active obejct posts directly the HUNGRY event to the Table active + /// object. \include qf_post.cpp + /// + /// \note The producer of the event (Philosopher in this case) must only + /// "know" the recipient (Table) by a generic (QActive *QDPP_table) + /// pointer, but the specific definition of the Table class is not + /// required. + /// + /// \note Direct event posting should not be confused with direct event + /// dispatching. In contrast to asynchronous event posting through event + /// queues, direct event dispatching is synchronous. Direct event + /// dispatching occurs when you call QHsm::dispatch(), or QFsm::dispatch() + /// function. +#ifndef Q_SPY + void postFIFO(QEvt const * const e); +#else + void postFIFO(QEvt const * const e, void const * const sender); +#endif + + /// \brief Un-subscribes from the delivery of all signals to the active + /// object. + /// + /// This function is part of the Publish-Subscribe event delivery + /// mechanism available in QF. Un-subscribing from all events means that + /// the framework will stop posting any published events to the event + /// queue of the active object. + /// + /// \note Due to the latency of event queues, an active object should NOT + /// assume that no events will ever be dispatched to the state machine of + /// the active object after un-subscribing from all events. + /// The events might be already in the queue, or just about to be posted + /// and the un-subscribe operation will not flush such events. Also, the + /// alternative event-delivery mechanisms, such as direct event posting or + /// time events, can be still delivered to the event queue of the active + /// object. + /// + /// \sa QF::publish(), QActive::subscribe(), and QActive::unsubscribe() + void unsubscribeAll(void) const; + +protected: + /// \brief protected constructor + /// + /// Performs the first step of active object initialization by assigning + /// the initial pseudostate to the currently active state of the state + /// machine. + /// + /// \note The constructor is protected to prevent direct instantiation + /// of QActive objects. This class is intended only for derivation + /// (abstract class). + QActive(QF_ACTIVE_STATE_ const initial) : QF_ACTIVE_SUPER_(initial) { + } + + /// \brief Posts an event directly to the event queue of the active object + /// \a me using the Last-In-First-Out (LIFO) policy. + /// + /// \note The LIFO policy should be used only for self-posting and with + /// great caution because it alters order of events in the queue. + /// + /// \sa QActive::postFIFO() + void postLIFO(QEvt const * const e); + + /// \brief Stops execution of an active object and removes it from the + /// framework's supervision. + /// + /// The preferred way of calling this function is from within the active + /// object that needs to stop (that's why this function is protected). + /// In other words, an active object should stop itself rather than being + /// stopped by some other entity. This policy works best, because only + /// the active object itself "knows" when it has reached the appropriate + /// state for the shutdown. + /// + /// \note This function is strongly OS-dependent and should be defined in + /// the QF port to a particular platform. This function is optional in + /// embedded systems where active objects never need to be stopped. + void stop(void); + + /// \brief Subscribes for delivery of signal \a sig to the active object + /// + /// This function is part of the Publish-Subscribe event delivery + /// mechanism available in QF. Subscribing to an event means that the + /// framework will start posting all published events with a given signal + /// \a sig to the event queue of the active object. + /// + /// The following example shows how the Table active object subscribes + /// to three signals in the initial transition: + /// \include qf_subscribe.cpp + /// + /// \sa QF::publish(), QActive::unsubscribe(), and + /// QActive::unsubscribeAll() + void subscribe(enum_t const sig) const; + + /// \brief Un-subscribes from the delivery of signal \a sig to the + /// active object. + /// + /// This function is part of the Publish-Subscribe event delivery + /// mechanism available in QF. Un-subscribing from an event means that + /// the framework will stop posting published events with a given signal + /// \a sig to the event queue of the active object. + /// + /// \note Due to the latency of event queues, an active object should NOT + /// assume that a given signal \a sig will never be dispatched to the + /// state machine of the active object after un-subscribing from that + /// signal. The event might be already in the queue, or just about to be + /// posted and the un-subscribe operation will not flush such events. + /// + /// \note Un-subscribing from a signal that has never been subscribed in + /// the first place is considered an error and QF will rise an assertion. + /// + /// \sa QF::publish(), QActive::subscribe(), and QActive::unsubscribeAll() + void unsubscribe(enum_t const sig) const; + + /// \brief Defer an event to a given separate event queue. + /// + /// This function is part of the event deferral support. An active object + /// uses this function to defer an event \a e to the QF-supported native + /// event queue \a eq. QF correctly accounts for another outstanding + /// reference to the event and will not recycle the event at the end of + /// the RTC step. Later, the active object might recall one event at a + /// time from the event queue. + /// + /// An active object can use multiple event queues to defer events of + /// different kinds. + /// + /// \sa QActive::recall(), QEQueue + void defer(QEQueue * const eq, QEvt const * const e) const; + + /// \brief Recall a deferred event from a given event queue. + /// + /// This function is part of the event deferral support. An active object + /// uses this function to recall a deferred event from a given QF + /// event queue. Recalling an event means that it is removed from the + /// deferred event queue \a eq and posted (LIFO) to the event queue of + /// the active object. + /// + /// QActive::recall() returns true if an event has been recalled. + /// Otherwise the function returns false. + /// + /// An active object can use multiple event queues to defer events of + /// different kinds. + /// + /// \sa QActive::defer(), QEQueue, QActive::postLIFO() + bool recall(QEQueue * const eq); + +private: + + /// \brief Get an event from the event queue of an active object. + /// + /// This function is used internally by a QF port to extract events from + /// the event queue of an active object. This function depends on the + /// event queue implementation and is sometimes implemented in the QF port + /// (qf_port.cpp file). Depending on the underlying OS or kernel, the + /// function might block the calling thread when no events are available. + /// + /// \note QActive::get_() is public because it often needs to be called + /// from thread-run routines with difficult to foresee signature (so + /// declaring friendship with such function(s) is not possible.) + /// + /// \sa QActive::postFIFO(), QActive::postLIFO() + QEvt const *get_(void); + + friend class QF; + friend class QTimeEvt; + +#ifdef QK_PREEMPTIVE // is this QK port? + friend void ::QK_schedExt_(uint8_t p); + friend void ::QK_sched_(uint8_t p); +#endif + +}; + +////////////////////////////////////////////////////////////////////////////// +/// \brief Time Event class +/// +/// Time events are special QF events equipped with the notion of time +/// passage. The basic usage model of the time events is as follows. An +/// active object allocates one or more QTimeEvt objects (provides the +/// storage for them). When the active object needs to arrange for a timeout, +/// it arms one of its time events to fire either just once (one-shot) or +/// periodically. Each time event times out independently from the others, +/// so a QF application can make multiple parallel timeout requests (from the +/// same or different active objects). When QF detects that the appropriate +/// moment has arrived, it inserts the time event directly into the +/// recipient's event queue. The recipient then processes the time event just +/// like any other event. +/// +/// Time events, as any other QF events derive from the ::QEvt base +/// class. Typically, you will use a time event as-is, but you can also +/// further derive more specialized time events from it by adding some more +/// data members and/or specialized functions that operate on the specialized +/// time events. +/// +/// Internally, the armed time events are organized into a bi-directional +/// linked list. This linked list is scanned in every invocation of the +/// QF::tick() function. Only armed (timing out) time events are in the list, +/// so only armed time events consume CPU cycles. +/// +/// \note QF manages the time events in the function QF::tick(), which +/// must be called periodically, preferably from the clock tick ISR. +/// \note In this version of QF QTimeEvt objects should be allocated +/// statically rather than dynamically from event pools. Currently, QF will +/// not correctly recycle the dynamically allocated Time Events. +class QTimeEvt : public QEvt { +private: + + /// link to the next time event in the list + QTimeEvt *m_next; + + /// the active object that receives the time events. + QActive *m_act; + + /// the internal down-counter of the time event. The down-counter + /// is decremented by 1 in every QF_tick() invocation. The time event + /// fires (gets posted or published) when the down-counter reaches zero. + QTimeEvtCtr m_ctr; + + /// the interval for the periodic time event (zero for the one-shot + /// time event). The value of the interval is re-loaded to the internal + /// down-counter when the time event expires, so that the time event + /// keeps timing out periodically. + QTimeEvtCtr m_interval; + +public: + + /// \brief The Time Event constructor. + /// + /// The most important initialization performed in the constructor is + /// assigning a signal to the Time Event. You can reuse the Time Event + /// any number of times, but you cannot change the signal. + /// This is because pointers to Time Events might still be held in event + /// queues and changing signal could to hard-to-detect errors. + /// + /// The following example shows the use of QTimeEvt::QTimeEvt() + /// constructor in the constructor initializer list of the Philosopher + /// active object constructor that owns the time event + /// \include qf_ctor.cpp + QTimeEvt(enum_t const s); + + /// \brief Arm a one-shot time event for direct event posting. + /// + /// Arms a time event to fire in \a nTicks clock ticks (one-shot time + /// event). The time event gets directly posted (using the FIFO policy) + /// into the event queue of the active object \a act. + /// + /// After posting, the time event gets automatically disarmed and can be + /// reused for a one-shot or periodic timeout requests. + /// + /// A one-shot time event can be disarmed at any time by calling the + /// QTimeEvt::disarm() function. Also, a one-shot time event can be + /// re-armed to fire in a different number of clock ticks by calling the + /// QTimeEvt::rearm() function. + /// + /// The following example shows how to arm a one-shot time event from a + /// state machine of an active object: + /// \include qf_state.cpp + void postIn(QActive * const act, QTimeEvtCtr const nTicks) { + m_interval = static_cast<QTimeEvtCtr>(0); + arm_(act, nTicks); + } + + /// \brief Arm a periodic time event for direct event posting. + /// + /// Arms a time event to fire every \a nTicks clock ticks (periodic time + /// event). The time event gets directly posted (using the FIFO policy) + /// into the event queue of the active object \a act. + /// + /// After posting, the time event gets automatically re-armed to fire + /// again in the specified \a nTicks clock ticks. + /// + /// A periodic time event can be disarmed only by calling the + /// QTimeEvt::disarm() function. After disarming, the time event can be + /// reused for a one-shot or periodic timeout requests. + /// + /// \note An attempt to reuse (arm again) a running periodic time event + /// raises an assertion. + /// + /// Also, a periodic time event can be re-armed to shorten or extend the + /// current period by calling the QTimeEvt_rearm() function. After + /// adjusting the current period, the periodic time event goes back + /// timing out at the original rate. + void postEvery(QActive * const act, QTimeEvtCtr const nTicks) { + m_interval = nTicks; + arm_(act, nTicks); + } + + /// \brief Disarm a time event. + /// + /// The time event gets disarmed and can be reused. The function + /// returns true if the time event was truly disarmed, that is, it + /// was running. The return of false means that the time event was + /// not truly disarmed because it was not running. The 'false' return is + /// only possible for one-shot time events that have been automatically + /// disarmed upon expiration. In this case the 'false' return means that + /// the time event has already been posted or published and should be + /// expected in the active object's state machine. + bool disarm(void); + + /// \brief Rearm a time event. + /// + /// The time event gets rearmed with a new number of clock ticks + /// \a nTicks. This facility can be used to prevent a one-shot time event + /// from expiring (e.g., a watchdog time event), or to adjusts the + /// current period of a periodic time event. Rearming a periodic timer + /// leaves the interval unchanged and is a convenient method to adjust + /// the phasing of the periodic time event. + /// + /// The function returns true if the time event was running as it + /// was re-armed. The return of false means that the time event was + /// not truly rearmed because it was not running. The 'false' return is + /// only possible for one-shot time events that have been automatically + /// disarmed upon expiration. In this case the 'false' return means that + /// the time event has already been posted or published and should be + /// expected in the active object's state machine. + bool rearm(QTimeEvtCtr const nTicks); + + /// \brief Get the current value of the down-counter of a time event. + /// + /// If the time event is armed, the function returns the current value of + /// the down-counter of the given time event. If the time event is not + /// armed, the function returns 0. + /// + /// /note The function is thread-safe. + QTimeEvtCtr ctr(void) const; + +private: + + /// \brief Arm a time event (internal function to be used through macros + /// only). + /// + /// \sa QTimeEvt::postIn(), QTimeEvt::postEvery(), + /// \sa QTimeEvt::publishIn(), QTimeEvt::publishEvery() + void arm_(QActive * const act, QTimeEvtCtr const nTicks); + + friend class QF; +}; + + +////////////////////////////////////////////////////////////////////////////// +/// \brief The size of the Subscriber list bit array +/// +/// The size is determined of the maximum number of active objects in the +/// application configured by the #QF_MAX_ACTIVE macro. +uint8_t const QF_SUBSCR_LIST_SIZE = + static_cast<uint8_t>(((QF_MAX_ACTIVE - 1) / 8) + 1); + +/// \brief Subscriber List class +/// +/// This data type represents a set of active objects that subscribe to +/// a given signal. The set is represented as an array of bits, where each +/// bit corresponds to the unique priority of an active object. +class QSubscrList { +private: + + /// An array of bits representing subscriber active objects. Each bit + /// in the array corresponds to the unique priority of the active object. + /// The size of the array is determined of the maximum number of active + /// objects in the application configured by the #QF_MAX_ACTIVE macro. + /// For example, an active object of priority p is a subscriber if the + /// following is true: ((m_bits[QF_div8Lkup[p]] & QF_pwr2Lkup[p]) != 0) + /// + /// \sa QF::psInit(), QF_div8Lkup, QF_pwr2Lkup, #QF_MAX_ACTIVE + uint8_t m_bits[QF_SUBSCR_LIST_SIZE]; + + friend class QF; + friend class QActive; +}; + +////////////////////////////////////////////////////////////////////////////// +/// \brief QF services. +/// +/// This class groups together QF services. It has only static members and +/// should not be instantiated. +class QF { +public: + + /// \brief QF initialization. + /// + /// This function initializes QF and must be called exactly once before + /// any other QF function. + static void init(void); + + /// \brief Publish-subscribe initialization. + /// + /// This function initializes the publish-subscribe facilities of QF and + /// must be called exactly once before any subscriptions/publications + /// occur in the application. The arguments are as follows: \a subscrSto + /// is a pointer to the array of subscriber-lists. \a maxSignal is the + /// dimension of this array and at the same time the maximum signal that + /// can be published or subscribed. + /// + /// The array of subscriber-lists is indexed by signals and provides + /// mapping between the signals and subscirber-lists. The subscriber- + /// lists are bitmasks of type ::QSubscrList, each bit in the bitmask + /// corresponding to the unique priority of an active object. The size + /// of the ::QSubscrList bitmask depends on the value of the + /// #QF_MAX_ACTIVE macro. + /// + /// \note The publish-subscribe facilities are optional, meaning that + /// you might choose not to use publish-subscribe. In that case calling + /// QF::psInit() and using up memory for the subscriber-lists is + /// unnecessary. + /// + /// \sa ::QSubscrList + /// + /// The following example shows the typical initialization sequence of + /// QF: \include qf_main.cpp + static void psInit(QSubscrList * const subscrSto, + uint32_t const maxSignal); + + /// \brief Event pool initialization for dynamic allocation of events. + /// + /// This function initializes one event pool at a time and must be called + /// exactly once for each event pool before the pool can be used. + /// The arguments are as follows: \a poolSto is a pointer to the memory + /// block for the events. \a poolSize is the size of the memory block in + /// bytes. \a evtSize is the block-size of the pool in bytes, which + /// determines the maximum size of events that can be allocated from the + /// pool. + /// + /// You might initialize one, two, and up to three event pools by making + /// one, two, or three calls to the QF_poolInit() function. However, + /// for the simplicity of the internal implementation, you must initialize + /// event pools in the ascending order of the event size. + /// + /// Many RTOSes provide fixed block-size heaps, a.k.a. memory pools that + /// can be used for QF event pools. In case such support is missing, QF + /// provides a native QF event pool implementation. The macro + /// #QF_EPOOL_TYPE_ determines the type of event pool used by a + /// particular QF port. See class ::QMPool for more information. + /// + /// \note The actual number of events available in the pool might be + /// actually less than (\a poolSize / \a evtSize) due to the internal + /// alignment of the blocks that the pool might perform. You can always + /// check the capacity of the pool by calling QF::getPoolMargin(). + /// + /// \note The dynamic allocation of events is optional, meaning that you + /// might choose not to use dynamic events. In that case calling + /// QF::poolInit() and using up memory for the memory blocks is + /// unnecessary. + /// + /// \sa QF initialization example for QF::init() + static void poolInit(void * const poolSto, uint32_t const poolSize, + uint32_t const evtSize); + + /// \brief Transfers control to QF to run the application. + /// + /// QF::run() is typically called from your startup code after you + /// initialize the QF and start at least one active object with + /// QActive::start(). Also, QF::start() call must precede the transfer + /// of control to QF::run(), but some QF ports might call QF::start() + /// from QF::run(). QF::run() typically never returns to the caller, + /// but when it does, it returns the error code (0 for success) + /// + /// \note This function is strongly platform-dependent and is not + /// implemented in the QF, but either in the QF port or in the + /// Board Support Package (BSP) for the given application. All QF ports + /// must implement QF::run(). + /// + /// \note When the Quantum Kernel (QK) is used as the underlying real-time + /// kernel for the QF, all platfrom dependencies are handled in the QK, so + /// no porting of QF is necessary. In other words, you only need to + /// recompile the QF platform-independent code with the compiler for your + /// platform, but you don't need to provide any platform-specific + /// implementation (so, no qf_port.cpp file is necessary). Moreover, QK + /// implements the function QF::run() in a platform-independent way, + /// in the modile qk.cpp. + static int16_t run(void); + + /// \brief Startup QF callback. + /// + /// The timeline for calling QF::onStartup() depends on the particular + /// QF port. In most cases, QF::onStartup() is called from QF::run(), + /// right before starting any multitasking kernel or the background loop. + static void onStartup(void); + + /// \brief Cleanup QF callback. + /// + /// QF::onCleanup() is called in some QF ports before QF returns to the + /// underlying operating system or RTOS. + /// + /// This function is strongly platform-specific and is not implemented in + /// the QF but either in the QF port or in the Board Support Package (BSP) + /// for the given application. Some QF ports might not require + /// implementing QF::onCleanup() at all, because many embedded + /// applications don't have anything to exit to. + /// + /// \sa QF::init() and QF::stop() + static void onCleanup(void); + + /// \brief QF idle callback (customized in BSPs for QF) + /// + /// QF::onIdle() is called by the non-preemptive "Vanilla" scheduler built + /// into QF when the framework detects that no events are available for + /// active objects (the idle condition). This callback gives the + /// application an opportunity to enter a power-saving CPU mode, or + /// perform some other idle processing (such as Q-Spy output). + /// + /// \note QF_onIdle() is invoked with interrupts DISABLED because the idle + /// condition can be asynchronously changed at any time by an interrupt. + /// QF_onIdle() MUST enable the interrupts internally, but not before + /// putting the CPU into the low-power mode. (Ideally, enabling interrupts + /// and low-power mode should happen atomically). At the very least, the + /// function MUST enable interrupts, otherwise interrups will remain + /// disabled permanently. + /// + /// \note QF::onIdle() is only used by the non-preemptive "Vanilla" + /// scheduler in the "bare metal" QF port, and is NOT used in any other + /// QF ports. When QF is combined with QK, the QK idle loop calls a + /// different function QK::onIdle(), with different semantics than + /// QF::onIdle(). When QF is combined with a 3rd-party RTOS or kernel, the + /// idle processing mechanism of the RTOS or kernal is used instead of + /// QF::onIdle(). + /// + static void onIdle(void); + + /// \brief Function invoked by the application layer to stop the QF + /// application and return control to the OS/Kernel. + /// + /// This function stops the QF application. After calling this function, + /// QF attempts to gracefully stop the application. This graceful + /// shutdown might take some time to complete. The typical use of this + /// funcition is for terminating the QF application to return back to the + /// operating system or for handling fatal errors that require shutting + /// down (and possibly re-setting) the system. + /// + /// This function is strongly platform-specific and is not implemented in + /// the QF but either in the QF port or in the Board Support Package (BSP) + /// for the given application. Some QF ports might not require + /// implementing QF::stop() at all, because many embedded application + /// don't have anything to exit to. + /// + /// \sa QF::stop() and QF::onCleanup() + static void stop(void); + + /// \brief Publish event to the framework. + /// + /// This function posts (using the FIFO policy) the event \a e it to ALL + /// active object that have subscribed to the signal \a e->sig. + /// This function is designed to be callable from any part of the system, + /// including ISRs, device drivers, and active objects. + /// + /// In the general case, event publishing requires multi-casting the + /// event to multiple subscribers. This happens in the caller's thread + /// with the scheduler locked to prevent preemptions during the multi- + /// casting process. (Please note that the interrupts are not locked.) +#ifndef Q_SPY + static void publish(QEvt const *e); +#else + static void publish(QEvt const *e, void const *sender); +#endif + + /// \brief Processes all armed time events at every clock tick. + /// + /// This function must be called periodically from a time-tick ISR or from + /// the highest-priority task so that QF can manage the timeout events. + /// + /// \note The QF::tick() function is not reentrant meaning that it must + /// run to completion before it is called again. Also, QF::tick() assumes + /// that it never will get preempted by a task, which is always the case + /// when it is called from an ISR or the highest-priority task. + /// + /// \sa ::QTimeEvt. + /// + /// The following example illustrates the call to QF::tick(): + /// \include qf_tick.cpp +#ifndef Q_SPY + static void tick(void); +#else + static void tick(void const * const sender); +#endif + + /// \brief Returns true if all time events are inactive and false + /// any time event is active. + /// + /// \note This function should be called in critical section. + static bool noTimeEvtsActive(void); + + /// \brief Returns the QF version. + /// + /// This function returns constant version string in the format x.y.zz, + /// where x (one digit) is the major version, y (one digit) is the minor + /// version, and zz (two digits) is the maintenance release version. + /// An example of the version string is "3.1.03". + /// + /// The following example illustrates the usage of this function: + /// \include qf_version.cpp + static char_t const Q_ROM * Q_ROM_VAR getVersion(void); + + /// \brief This function returns the margin of the given event pool. + /// + /// This function returns the margin of the given event pool \a poolId, + /// where poolId is the ID of the pool initialized by the call to + /// QF::poolInit(). The poolId of the first initialized pool is 1, the + /// second 2, and so on. + /// + /// The returned pool margin is the minimal number of free blocks + /// encountered in the given pool since system startup. + /// + /// \note Requesting the margin of an un-initialized pool raises an + /// assertion in the QF. + static uint32_t getPoolMargin(uint8_t const poolId); + + /// \brief This function returns the margin of the given event queue. + /// + /// This function returns the margin of the given event queue of an active + /// object with priority \a prio. (QF priorities start with 1 and go up to + /// #QF_MAX_ACTIVE.) The margin is the minimal number of free events + /// encountered in the given queue since system startup. + /// + /// \note QF::getQueueMargin() is available only when the native QF event + /// queue implementation is used. Requesting the queue margin of an unused + /// priority level raises an assertion in the QF. (A priority level + /// becomes used in QF after the call to the QF::add_() function.) + static uint32_t getQueueMargin(uint8_t const prio); + + /// \brief Internal QF implementation of the dynamic event allocator. + /// + /// \note The application code should not call this function directly. + /// Please use the macro #Q_NEW. + static QEvt *new_(QEvtSize const evtSize, enum_t const sig); + + /// \brief Recycle a dynamic event. + /// + /// This function implements a simple garbage collector for the dynamic + /// events. Only dynamic events are candidates for recycling. (A dynamic + /// event is one that is allocated from an event pool, which is + /// determined as non-zero e->attrQF__ attribute.) Next, the function + /// decrements the reference counter of the event, and recycles the event + /// only if the counter drops to zero (meaning that no more references + /// are outstanding for this event). The dynamic event is recycled by + /// returning it to the pool from which it was originally allocated. + /// The pool-of-origin information is stored in the upper 2-MSBs of the + /// e->attrQF__ attribute.) + /// + /// \note QF invokes the garbage collector at all appropriate contexts, + /// when an event can become garbage (automatic garbage collection), + /// so the application code should have NO need to call QF::gc() directly. + /// The QF::gc() function is exposed only for special cases when your + /// application sends dynamic events to the "raw" thread-safe queues + /// (see ::QEQueue). Such queues are processed outside of QF and the + /// automatic garbage collection CANNOT be performed for these events. + /// In this case you need to call QF::gc() explicitly. + static void gc(QEvt const *e); + +private: // functions to be used in QF ports only + + /// \brief Register an active object to be managed by the framework + /// + /// This function should not be called by the application directly, only + /// through the function QActive::start(). The priority of the active + /// object \a a should be set before calling this function. + /// + /// \note This function raises an assertion if the priority of the active + /// object exceeds the maximum value #QF_MAX_ACTIVE. Also, this function + /// raises an assertion if the priority of the active object is already in + /// use. (QF requires each active object to have a UNIQUE priority.) + static void add_(QActive * const a); + +public: + /// \brief Remove the active object from the framework. + /// + /// This function should not be called by the application directly, only + /// inside the QF port. The priority level occupied by the active object + /// is freed-up and can be reused for another active object. + /// + /// The active object that is removed from the framework can no longer + /// participate in the publish-subscribe event exchange. + /// + /// \note This function raises an assertion if the priority of the active + /// object exceeds the maximum value #QF_MAX_ACTIVE or is not used. + static void remove_(QActive const * const a); + + /// \brief array of registered active objects + /// + /// \note Not to be used by Clients directly, only in ports of QF + static QActive *active_[]; + + /// \brief Thread routine for executing an active object \a act. + /// + /// This function is actually implemented internally by certain QF ports + /// to be called by the active object thread routine. + static void thread_(QActive *act); + + friend class QActive; +}; + +QP_END_ + +////////////////////////////////////////////////////////////////////////////// +#ifdef Q_EVT_CTOR // Provide the constructor for the QEvt class? + + /// \brief Allocate a dynamic event. + /// + /// This macro returns an event pointer cast to the type \a evtT_. The + /// event is initialized with the signal \a sig. Internally, the macro + /// calls the internal QF function QF::new_(), which always returns a + /// valid event pointer. + /// + /// \note The internal QF function QF::new_() raises an assertion when + /// the allocation of the event turns out to be impossible due to + /// event pool depletion, or incorrect (too big) size of the requested + /// event. + /// + /// The following example illustrates dynamic allocation of an event: + /// \include qf_post.cpp + #define Q_NEW(evtT_, sig_, ...) \ + (new(QP_ QF::new_( \ + static_cast<QP_ QEvtSize>(sizeof(evtT_)), (sig_))) \ + evtT_((sig_), ##__VA_ARGS__)) +#else // QEvt is a POD (Plain Old Datatype) + + #define Q_NEW(evtT_, sig_) \ + (static_cast<evtT_ *>( \ + QP_ QF::new_(static_cast<QP_ QEvtSize>(sizeof(evtT_)), (sig_)))) +#endif + +////////////////////////////////////////////////////////////////////////////// +// QS software tracing integration, only if enabled +#ifdef Q_SPY // QS software tracing enabled? + + /// \brief Invoke the system clock tick processing QF::tick(). This macro + /// is the recommended way of invoking clock tick processing, because it + /// provides the vital information for software tracing and avoids any + /// overhead when the tracing is disabled. + /// + /// This macro takes the argument \a sender_, which is a pointer to the + /// sender object. This argument is actually only used when QS software + /// tracing is enabled (macro #Q_SPY is defined). When QS software + /// tracing is disabled, the macro calls QF::tick() without any + /// arguments, so the overhead of passing this extra argument is + /// entirely avoided. + /// + /// \note the pointer to the sender object is not necessarily a poiner + /// to an active object. In fact, typically QF::TICK() will be called from + /// an interrupt, in which case you would create a unique object just to + /// unambiguously identify the ISR as the sender of the time events. + /// + /// \sa QF::tick() + #define TICK(sender_) tick(sender_) + + /// \brief Invoke the event publishing facility QF::publish(). This macro + /// is the recommended way of publishing events, because it provides the + /// vital information for software tracing and avoids any overhead when + /// the tracing is disabled. + /// + /// + /// This macro takes the last argument \a sender_, which is a pointer to + /// the sender object. This argument is actually only used when QS + /// tracing is enabled (macro #Q_SPY is defined). When QS software + /// tracing is disabled, the macro calls QF::publish() without the + /// \a sender_ argument, so the overhead of passing this extra argument + /// is entirely avoided. + /// + /// \note the pointer to the sender object is not necessarily a poiner + /// to an active object. In fact, if QF::PUBLISH() is called from an + /// interrupt or other context, you can create a unique object just to + /// unambiguously identify the publisher of the event. + /// + /// \sa QF::publish() + #define PUBLISH(e_, sender_) publish((e_), (sender_)) + + /// \brief Invoke the direct event posting facility QActive::postFIFO(). + /// This macro is the recommended way of posting events, because it + /// provides the vital information for software tracing and avoids any + /// overhead when the tracing is disabled. + /// + /// + /// This macro takes the last argument \a sender_, which is a pointer to + /// the sender object. This argument is actually only used when QS + /// tracing is disabled (macro #Q_SPY is defined). When QS software + /// tracing is not enabled, the macro calls QF_publish() without the + /// \a sender_ argument, so the overhead of passing this extra argument + /// is entirely avoided. + /// + /// \note the pointer to the sender object is not necessarily a poiner + /// to an active object. In fact, if ao->POST() is called from an + /// interrupt or other context, you can create a unique object just to + /// unambiguously identify the publisher of the event. + /// + /// \sa QActive::postFIFO() + #define POST(e_, sender_) postFIFO((e_), (sender_)) + +#else + + #define TICK(dummy_) tick() + #define PUBLISH(e_, dummy_) publish(e_) + #define POST(e_, dummy_) postFIFO(e_) + +#endif // Q_SPY + +////////////////////////////////////////////////////////////////////////////// +// QS software tracing +#ifdef Q_SPY + +// qs.h ====================================================================== +/// \brief QS/C++ platform-independent public interface. +/// This header file must be included directly or indirectly +/// in all modules (*.cpp files) that use QS/C++. + +#ifndef Q_SPY + #error "Q_SPY must be defined to include qs.h" +#endif + +#ifndef Q_ROM // provide the default if Q_ROM NOT defined + #define Q_ROM +#endif +#ifndef Q_ROM_VAR // provide the default if Q_ROM_VAR NOT defined + #define Q_ROM_VAR +#endif +#ifndef Q_ROM_BYTE // provide the default if Q_ROM_BYTE NOT defined + #define Q_ROM_BYTE(rom_var_) (rom_var_) +#endif + +#ifndef QS_TIME_SIZE + + /// \brief The size (in bytes) of the QS time stamp. Valid values: 1, 2, + /// or 4; default 4. + /// + /// This macro can be defined in the QS port file (qs_port.h) to + /// configure the ::QSTimeCtr type. Here the macro is not defined so the + /// default of 4 byte is chosen. + #define QS_TIME_SIZE 4 +#endif + +////////////////////////////////////////////////////////////////////////////// +QP_BEGIN_ + +/// \brief Quantum Spy record types. +/// +/// The following constants specify the QS record types used in QP components. +/// You can specify your own record types starting from the ::QS_USER offset. +/// Currently, the maximum of all records cannot exceed 256. +/// \sa QS::filterOn()/#QS_FILTER_ON and QS::filterOff()/#QS_FILTER_OFF + +enum QSpyRecords { + QS_QP_RESET, ///< reset the QP (start of a new QS session) + + // QEP records + QS_QEP_STATE_ENTRY, ///< a state was entered + QS_QEP_STATE_EXIT, ///< a state was exited + QS_QEP_STATE_INIT, ///< an intial transition was taken in a state + QS_QEP_INIT_TRAN, ///< the top-most initial transition was taken + QS_QEP_INTERN_TRAN, ///< an internal transition was taken + QS_QEP_TRAN, ///< a regular transition was taken + QS_QEP_IGNORED, ///< an event was ignored (silently discarded) + QS_QEP_DISPATCH, ///< an event was dispatched (begin of RTC step) + QS_QEP_UNHANDLED, ///< an event was unhandled due to a guard + + // QF records + QS_QF_ACTIVE_ADD, ///< an AO has been added to QF (started) + QS_QF_ACTIVE_REMOVE, ///< an AO has been removed from QF (stopped) + QS_QF_ACTIVE_SUBSCRIBE, ///< an AO subscribed to an event + QS_QF_ACTIVE_UNSUBSCRIBE, ///< an AO unsubscribed to an event + QS_QF_ACTIVE_POST_FIFO, ///< an event was posted (FIFO) directly to an AO + QS_QF_ACTIVE_POST_LIFO, ///< an event was posted (LIFO) directly to an AO + QS_QF_ACTIVE_GET, ///< an AO got an event and its queue is still not empty + QS_QF_ACTIVE_GET_LAST, ///< an AO got an event and its queue is empty + QS_QF_EQUEUE_INIT, ///< an event queue was initialized + QS_QF_EQUEUE_POST_FIFO, ///< an event was posted (FIFO) to a raw queue + QS_QF_EQUEUE_POST_LIFO, ///< an event was posted (LIFO) to a raw queue + QS_QF_EQUEUE_GET, ///< get an event and queue still not empty + QS_QF_EQUEUE_GET_LAST, ///< get the last event from the queue + QS_QF_MPOOL_INIT, ///< a memory pool was initialized + QS_QF_MPOOL_GET, ///< a memory block was removed from a memory pool + QS_QF_MPOOL_PUT, ///< a memory block was returned to a memory pool + QS_QF_PUBLISH, ///< an event was truly published to some subscribers + QS_QF_RESERVED8, + QS_QF_NEW, ///< new event creation + QS_QF_GC_ATTEMPT, ///< garbage collection attempt + QS_QF_GC, ///< garbage collection + QS_QF_TICK, ///< QF::tick() was called + QS_QF_TIMEEVT_ARM, ///< a time event was armed + QS_QF_TIMEEVT_AUTO_DISARM, ///< a time event expired and was disarmed + QS_QF_TIMEEVT_DISARM_ATTEMPT,///< an attempt to disarmed a disarmed tevent + QS_QF_TIMEEVT_DISARM, ///< true disarming of an armed time event + QS_QF_TIMEEVT_REARM, ///< rearming of a time event + QS_QF_TIMEEVT_POST, ///< a time event posted itself directly to an AO + QS_QF_TIMEEVT_CTR, ///< a time event counter was requested + QS_QF_CRIT_ENTRY, ///< critical section was entered + QS_QF_CRIT_EXIT, ///< critical section was exited + QS_QF_ISR_ENTRY, ///< an ISR was entered + QS_QF_ISR_EXIT, ///< an ISR was exited + QS_QF_INT_DISABLE, ///< interrupts were disabled + QS_QF_INT_ENABLE, ///< interrupts were enabled + QS_QF_RESERVED4, + QS_QF_RESERVED3, + QS_QF_RESERVED2, + QS_QF_RESERVED1, + QS_QF_RESERVED0, + + // QK records + QS_QK_MUTEX_LOCK, ///< the QK mutex was locked + QS_QK_MUTEX_UNLOCK, ///< the QK mutex was unlocked + QS_QK_SCHEDULE, ///< the QK scheduler scheduled a new task to execute + QS_QK_RESERVED6, + QS_QK_RESERVED5, + QS_QK_RESERVED4, + QS_QK_RESERVED3, + QS_QK_RESERVED2, + QS_QK_RESERVED1, + QS_QK_RESERVED0, + + // Miscellaneous QS records + QS_SIG_DIC, ///< signal dictionary entry + QS_OBJ_DIC, ///< object dictionary entry + QS_FUN_DIC, ///< function dictionary entry + QS_USR_DIC, ///< user QS record dictionary entry + QS_RESERVED4, + QS_RESERVED3, + QS_RESERVED2, + QS_RESERVED1, + QS_RESERVED0, + QS_ASSERT, ///< assertion fired in the code + + // User records + QS_USER ///< the first record available for user QS records +}; + +/// \brief Specification of all QS records for the QS::filterOn() and +/// QS::filterOff() +uint8_t const QS_ALL_RECORDS = static_cast<uint8_t>(0xFF); + +/// \brief Constant representing End-Of-Data condition returned from the +/// QS::getByte() function. +uint16_t const QS_EOD = static_cast<uint16_t>(0xFFFF); + + +#if (QS_TIME_SIZE == 1) + typedef uint8_t QSTimeCtr; + #define QS_TIME_() (QP_ QS::u8_(QP_ QS::onGetTime())) +#elif (QS_TIME_SIZE == 2) + typedef uint16_t QSTimeCtr; + #define QS_TIME_() (QP_ QS::u16_(QP_ QS::onGetTime())) +#elif (QS_TIME_SIZE == 4) + + /// \brief The type of the QS time stamp + /// + /// This type determines the dynamic range of QS time stamps + typedef uint32_t QSTimeCtr; + + /// \brief Internal macro to output time stamp to the QS record + #define QS_TIME_() (QP_ QS::u32_(QP_ QS::onGetTime())) +#else + #error "QS_TIME_SIZE defined incorrectly, expected 1, 2, or 4" +#endif + +/// \brief Quantum Spy logging facilities +/// +/// This class groups together QS services. It has only static members and +/// should not be instantiated. +class QS { +public: + + /// \brief Get the current version of QS + /// + /// \return version of the QS as a constant 6-character string of the form + /// x.y.zz, where x is a 1-digit major version number, y is a 1-digit + /// minor version number, and zz is a 2-digit release number. + static char_t const Q_ROM * Q_ROM_VAR getVersion(void); + + /// \brief Initialize the QS data buffer. + /// + /// This function should be called from QS_init() to provide QS with the + /// data buffer. The first argument \a sto[] is the address of the memory + /// block, and the second argument \a stoSize is the size of this block + /// in bytes. Currently the size of the QS buffer cannot exceed 64KB. + /// + /// QS can work with quite small data buffers, but you will start losing + /// data if the buffer is too small for the bursts of logging activity. + /// The right size of the buffer depends on the data production rate and + /// the data output rate. QS offers flexible filtering to reduce the data + /// production rate. + /// + /// \note If the data output rate cannot keep up with the production rate, + /// QS will start overwriting the older data with newer data. This is + /// consistent with the "last-is-best" QS policy. The record sequence + /// counters and checksums on each record allow to easily detect data + /// loss. + static void initBuf(uint8_t sto[], uint32_t const stoSize); + + /// \brief Turn the global Filter on for a given record type \a rec. + /// + /// This function sets up the QS filter to enable the record type \a rec. + /// The argument #QS_ALL_RECORDS specifies to filter-on all records. + /// This function should be called indirectly through the macro + /// #QS_FILTER_ON. + /// + /// \note Filtering based on the record-type is only the first layer of + /// filtering. The second layer is based on the object-type. Both filter + /// layers must be enabled for the QS record to be inserted into the QS + /// buffer. + /// \sa QS_filterOff(), #QS_FILTER_SM_OBJ, #QS_FILTER_AO_OBJ, + /// #QS_FILTER_MP_OBJ, #QS_FILTER_EQ_OBJ, and #QS_FILTER_TE_OBJ. + static void filterOn(uint8_t const rec); + + /// \brief Turn the global Filter off for a given record type \a rec. + /// + /// This function sets up the QS filter to disable the record type \a rec. + /// The argument #QS_ALL_RECORDS specifies to suppress all records. + /// This function should be called indirectly through the macro + /// #QS_FILTER_OFF. + /// + /// \note Filtering records based on the record-type is only the first + /// layer of filtering. The second layer is based on the object-type. + /// Both filter layers must be enabled for the QS record to be inserted + /// into the QS buffer. + /// \sa + static void filterOff(uint8_t const rec); + + /// \brief Mark the begin of a QS record \a rec + /// + /// This function must be called at the beginning of each QS record. + /// This function should be called indirectly through the macro #QS_BEGIN, + /// or #QS_BEGIN_NOCRIT, depending if it's called in a normal code or from + /// a critical section. + static void begin(uint8_t const rec); + + /// \brief Mark the end of a QS record \a rec + /// + /// This function must be called at the end of each QS record. + /// This function should be called indirectly through the macro #QS_END, + /// or #QS_END_NOCRIT, depending if it's called in a normal code or from + /// a critical section. + static void end(void); + + // unformatted data elements output ...................................... + + /// \brief output uint8_t data element without format information + /// \note This function is only to be used through macros, never in the + /// client code directly. + static void u8_(uint8_t const d); + + /// \brief Output uint16_t data element without format information + /// \note This function is only to be used through macros, never in the + /// client code directly. + static void u16_(uint16_t d); + + /// \brief Output uint32_t data element without format information + /// \note This function is only to be used through macros, never in the + /// client code directly. + static void u32_(uint32_t d); + + /// \brief Output zero-terminated ASCII string element without format + /// information + /// \note This function is only to be used through macros, never in the + /// client code directly. + static void str_(char_t const *s); + + /// \brief Output zero-terminated ASCII string element allocated in ROM + /// without format information + /// \note This function is only to be used through macros, never in the + /// client code directly. + static void str_ROM_(char_t const Q_ROM * Q_ROM_VAR s); + + // formatted data elements output ........................................ + + /// \brief Output uint8_t data element with format information + /// \note This function is only to be used through macros, never in the + /// client code directly. + static void u8(uint8_t const format, uint8_t const d); + + /// \brief output uint16_t data element with format information + /// \note This function is only to be used through macros, never in the + /// client code directly. + static void u16(uint8_t const format, uint16_t d); + + /// \brief Output uint32_t data element with format information + /// \note This function is only to be used through macros, never in the + /// client code directly. + static void u32(uint8_t const format, uint32_t d); + + /// \brief Output 32-bit floating point data element with format + /// information + /// \note This function is only to be used through macros, never in the + /// client code directly. + static void f32(uint8_t const format, float32_t const d); + + /// \brief Output 64-bit floating point data element with format + /// information + /// \note This function is only to be used through macros, never in the + /// client code directly. + static void f64(uint8_t const format, float64_t const d); + + /// \brief Output zero-terminated ASCII string element with format + /// information + /// \note This function is only to be used through macros, never in the + /// client code directly. + static void str(char_t const *s); + + /// \brief Output zero-terminated ASCII string element allocated in ROM + /// with format information + /// \note This function is only to be used through macros, never in the + /// client code directly. + static void str_ROM(char_t const Q_ROM * Q_ROM_VAR s); + + /// \brief Output memory block of up to 255-bytes with format information + /// \note This function is only to be used through macros, never in the + /// client code directly. + static void mem(uint8_t const *blk, uint8_t size); + +#if (QS_OBJ_PTR_SIZE == 8) || (QS_FUN_PTR_SIZE == 8) + /// \brief Output uint64_t data element without format information + /// \note This function is only to be used through macros, never in the + /// client code directly. + static void u64_(uint64_t d); + + /// \brief Output uint64_t data element with format information + /// \note This function is only to be used through macros, never in the + /// client code directly. + static void u64(uint8_t format, uint64_t d); +#endif + + // QS buffer access ...................................................... + + /// \brief Byte-oriented interface to the QS data buffer. + /// + /// This function delivers one byte at a time from the QS data buffer. + /// The function returns the byte in the least-significant 8-bits of the + /// 16-bit return value if the byte is available. If no more data is + /// available at the time, the function returns QS_EOD (End-Of-Data). + /// + /// \note QS::getByte() is NOT protected with a critical section. + static uint16_t getByte(void); + + /// \brief Block-oriented interface to the QS data buffer. + /// + /// This function delivers a contiguous block of data from the QS data + /// buffer. The function returns the pointer to the beginning of the + /// block, and writes the number of bytes in the block to the location + /// pointed to by \a pNbytes. The argument \a pNbytes is also used as + /// input to provide the maximum size of the data block that the caller + /// can accept. + /// + /// If no bytes are available in the QS buffer when the function is + /// called, the function returns a NULL pointer and sets the value + /// pointed to by \a pNbytes to zero. + /// + /// \note Only the NULL return from QS::getBlock() indicates that the QS + /// buffer is empty at the time of the call. The non-NULL return often + /// means that the block is at the end of the buffer and you need to call + /// QS::getBlock() again to obtain the rest of the data that "wrapped + /// around" to the beginning of the QS data buffer. + /// + /// \note QS::getBlock() is NOT protected with a critical section. + static uint8_t const *getBlock(uint16_t * const pNbytes); + + ////////////////////////////////////////////////////////////////////////// + // platform-dependent callback functions to be implemented by clients + + // platform-specific callback functions, need to be implemented by clients + /// \brief Callback to startup the QS facility + /// + /// This is a platform-dependent "callback" function invoked through the + /// macro #QS_INIT. You need to implement this function in your + /// application. At a minimum, the function must configure the QS buffer + /// by calling QS::initBuf(). Typically, you will also want to open/ + /// configure the QS output channel, such as a serial port, or a file. + /// The void* argument \a arg can be used to pass parameter(s) needed to + /// configure the output channel. + /// + /// The function returns true if the QS initialization was successful, + /// or false if it failed. + /// + /// The following example illustrates an implementation of QS_onStartup(): + /// \include qs_startup.cpp + static bool onStartup(void const *arg); + + /// \brief Callback to cleanup the QS facility + /// + /// This is a platform-dependent "callback" function invoked through the + /// macro #QS_EXIT. You need to implement this function in your + /// application. The main purpose of this function is to close the QS + /// output channel, if necessary. + static void onCleanup(void); + + /// \brief Callback to flush the QS trace data to the host + /// + /// This is a platform-dependent "callback" function to flush the QS + /// trace buffer to the host. The function typically busy-waits until all + /// the data in the buffer is sent to the host. This is acceptable only + /// in the initial transient. + static void onFlush(void); + + /// \brief Callback to obtain a timestamp for a QS record. + /// + /// This is a platform-dependent "callback" function invoked from the + /// macro #QS_TIME_ to add the time stamp to the QS record. + /// + /// \note Some of the pre-defined QS records from QP do not output the + /// time stamp. However, ALL user records do output the time stamp. + /// \note QS::onGetTime() is called in a critical section and should not + /// exit critical section. + /// + /// The following example shows using a system call to implement QS + /// time stamping: + /// \include qs_onGetTime.cpp + static QSTimeCtr onGetTime(void); + + ////////////////////////////////////////////////////////////////////////// + // Global and Local QS filters + static uint8_t glbFilter_[32]; ///< global on/off QS filter + static void const *smObj_; ///< state machine for QEP local filter + static void const *aoObj_; ///< active object for QF/QK local filter + static void const *mpObj_; ///< event pool for QF local filter + static void const *eqObj_; ///< raw queue for QF local filter + static void const *teObj_; ///< time event for QF local filter + static void const *apObj_;///< generic object Application QF local filter + + ////////////////////////////////////////////////////////////////////////// + // Miscallaneous + /// tick counter for the QS_QF_TICK record + static QSTimeCtr tickCtr_; +}; + +/// \brief Enumerates data formats recognized by QS +/// +/// QS uses this enumeration is used only internally for the formatted user +/// data elements. +enum QSType { + QS_I8_T, ///< signed 8-bit integer format + QS_U8_T, ///< unsigned 8-bit integer format + QS_I16_T, ///< signed 16-bit integer format + QS_U16_T, ///< unsigned 16-bit integer format + QS_I32_T, ///< signed 32-bit integer format + QS_U32_T, ///< unsigned 32-bit integer format + QS_F32_T, ///< 32-bit floating point format + QS_F64_T, ///< 64-bit floating point format + QS_STR_T, ///< zero-terminated ASCII string format + QS_MEM_T, ///< up to 255-bytes memory block format + QS_SIG_T, ///< event signal format + QS_OBJ_T, ///< object pointer format + QS_FUN_T, ///< function pointer format + QS_I64_T, ///< signed 64-bit integer format + QS_U64_T, ///< unsigned 64-bit integer format + QS_U32_HEX_T ///< unsigned 32-bit integer in hex format +}; + +/// \brief critical section nesting level +/// +/// \note Not to be used by Clients directly, only in ports of QF +extern uint8_t QF_critNest_; + +QP_END_ + + +////////////////////////////////////////////////////////////////////////////// +// Macros for adding QS instrumentation to the client code + +/// \brief Initialize the QS facility. +/// +/// This macro provides an indirection layer to invoke the QS initialization +/// routine if #Q_SPY is defined, or do nothing if #Q_SPY is not defined. +/// \sa QS::onStartup(), example of setting up a QS filter in #QS_FILTER_IN +#define QS_INIT(arg_) (QP_ QS::onStartup(arg_)) + +/// \brief Cleanup the QS facility. +/// +/// This macro provides an indirection layer to invoke the QS cleanup +/// routine if #Q_SPY is defined, or do nothing if #Q_SPY is not defined. +/// \sa QS::onCleanup() +#define QS_EXIT() (QP_ QS::onCleanup()) + +/// \brief Global Filter ON for a given record type \a rec. +/// +/// This macro provides an indirection layer to call QS::filterOn() if #Q_SPY +/// is defined, or do nothing if #Q_SPY is not defined. +/// +/// The following example shows how to use QS filters: +/// \include qs_filter.cpp +#define QS_FILTER_ON(rec_) (QP_ QS::filterOn(static_cast<uint8_t>(rec_))) + +/// \brief Global filter OFF for a given record type \a rec. +/// +/// This macro provides an indirection layer to call QS::filterOff() if #Q_SPY +/// is defined, or do nothing if #Q_SPY is not defined. +/// +/// \sa Example of using QS filters in #QS_FILTER_ON documentation +#define QS_FILTER_OFF(rec_) (QP_ QS::filterOff(static_cast<uint8_t>(rec_))) + +/// \brief Local Filter for a given state machine object \a obj_. +/// +/// This macro sets up the state machine object local filter if #Q_SPY is +/// defined, or does nothing if #Q_SPY is not defined. The argument \a obj_ +/// is the pointer to the state machine object that you want to monitor. +/// +/// The state machine object filter allows you to filter QS records pertaining +/// only to a given state machine object. With this filter disabled, QS will +/// output records from all state machines in your application. The object +/// filter is disabled by setting the state machine pointer to NULL. +/// +/// The state machine filter affects the following QS records: +/// ::QS_QEP_STATE_ENTRY, ::QS_QEP_STATE_EXIT, ::QS_QEP_STATE_INIT, +/// ::QS_QEP_INIT_TRAN, ::QS_QEP_INTERN_TRAN, ::QS_QEP_TRAN, +/// and ::QS_QEP_IGNORED. +/// +/// \note Because active objects are state machines at the same time, +/// the state machine filter (#QS_FILTER_SM_OBJ) pertains to active +/// objects as well. However, the state machine filter is more general, +/// because it can be used only for state machines that are not active +/// objects, such as "Orthogonal Components". +/// +/// \sa Example of using QS filters in #QS_FILTER_ON documentation +#define QS_FILTER_SM_OBJ(obj_) (QP_ QS::smObj_ = (obj_)) + +/// \brief Local Filter for a given active object \a obj_. +/// +/// This macro sets up the active object local filter if #Q_SPY is defined, +/// or does nothing if #Q_SPY is not defined. The argument \a obj_ is the +/// pointer to the active object that you want to monitor. +/// +/// The active object filter allows you to filter QS records pertaining +/// only to a given active object. With this filter disabled, QS will +/// output records from all active objects in your application. The object +/// filter is disabled by setting the active object pointer \a obj_ to NULL. +/// +/// The active object filter affects the following QS records: +/// ::QS_QF_ACTIVE_ADD, ::QS_QF_ACTIVE_REMOVE, ::QS_QF_ACTIVE_SUBSCRIBE, +/// ::QS_QF_ACTIVE_UNSUBSCRIBE, ::QS_QF_ACTIVE_POST_FIFO, +/// ::QS_QF_ACTIVE_POST_LIFO, ::QS_QF_ACTIVE_GET, and ::QS_QF_ACTIVE_GET_LAST. +/// +/// \sa Example of using QS filters in #QS_FILTER_ON documentation +#define QS_FILTER_AO_OBJ(obj_) (QP_ QS::aoObj_ = (obj_)) + +/// \brief Local Filter for a given memory pool object \a obj_. +/// +/// This macro sets up the memory pool object local filter if #Q_SPY is +/// defined, or does nothing if #Q_SPY is not defined. The argument \a obj_ +/// is the pointer to the memory buffer used during the initialization of the +/// event pool with QF::poolInit(). +/// +/// The memory pool filter allows you to filter QS records pertaining +/// only to a given memory pool. With this filter disabled, QS will +/// output records from all memory pools in your application. The object +/// filter is disabled by setting the memory pool pointer \a obj_ to NULL. +/// +/// The memory pool filter affects the following QS records: +/// ::QS_QF_MPOOL_INIT, ::QS_QF_MPOOL_GET, and ::QS_QF_MPOOL_PUT. +/// +/// \sa Example of using QS filters in #QS_FILTER_ON documentation +#define QS_FILTER_MP_OBJ(obj_) (QP_ QS::mpObj_ = (obj_)) + +/// \brief Filter for a given event queue object \a obj_. +/// +/// This macro sets up the event queue object filter if #Q_SPY is defined, +/// or does nothing if #Q_SPY is not defined. The argument \a obj_ is the +/// pointer to the "raw" thread-safe queue object you want to monitor. +/// +/// The event queue filter allows you to filter QS records pertaining +/// only to a given event queue. With this filter disabled, QS will +/// output records from all event queues in your application. The object +/// filter is disabled by setting the event queue pointer \a obj_ to NULL. +/// +/// The event queue filter affects the following QS records: +/// ::QS_QF_EQUEUE_INIT, ::QS_QF_EQUEUE_POST_FIFO, ::QS_QF_EQUEUE_POST_LIFO, +/// ::QS_QF_EQUEUE_GET, and ::QS_QF_EQUEUE_GET_LAST. +/// +/// \sa Example of using QS filters in #QS_FILTER_IN documentation +#define QS_FILTER_EQ_OBJ(obj_) (QP_ QS::eqObj_ = (obj_)) + +/// \brief Local Filter for a given time event object \a obj_. +/// +/// This macro sets up the time event object local filter if #Q_SPY is +/// defined, or does nothing if #Q_SPY is not defined. The argument \a obj_ +/// is the pointer to the time event object you want to monitor. +/// +/// The time event filter allows you to filter QS records pertaining +/// only to a given time event. With this filter disabled, QS will +/// output records from all time events in your application. The object +/// filter is disabled by setting the time event pointer \a obj_ to NULL. +/// +/// The time event filter affects the following QS records: +/// ::QS_QF_TIMEEVT_ARM, ::QS_QF_TIMEEVT_AUTO_DISARM, +/// ::QS_QF_TIMEEVT_DISARM_ATTEMPT, ::QS_QF_TIMEEVT_DISARM, +/// ::QS_QF_TIMEEVT_REARM, ::QS_QF_TIMEEVT_POST, and ::QS_QF_TIMEEVT_PUBLISH. +/// +/// \sa Example of using QS filters in #QS_FILTER_ON documentation +#define QS_FILTER_TE_OBJ(obj_) (QP_ QS::teObj_ = (obj_)) + +/// \brief Local Filter for a generic application object \a obj_. +/// +/// This macro sets up the local application object filter if #Q_SPY is +/// defined, or does nothing if #Q_SPY is not defined. The argument \a obj_ +/// is the pointer to the application object you want to monitor. +/// +/// The application object filter allows you to filter QS records pertaining +/// only to a given application object. With this filter disabled, QS will +/// output records from all application-records enabled by the global filter. +/// The local filter is disabled by setting the time event pointer \a obj_ +/// to NULL. +/// +/// \sa Example of using QS filters in #QS_FILTER_ON documentation +#define QS_FILTER_AP_OBJ(obj_) (QP_ QS::apObj_ = (obj_)) + + +////////////////////////////////////////////////////////////////////////////// +// Macros to generate user QS records + +#define QS_GLB_FILTER_(rec_) \ + ((QP_ QS::glbFilter_[static_cast<uint8_t>(rec_) >> 3] \ + & (static_cast<uint8_t>(1U << (static_cast<uint8_t>(rec_) \ + & static_cast<uint8_t>(7))))) \ + != static_cast<uint8_t>(0)) + +/// \brief Begin a QS user record without entering critical section. +#define QS_BEGIN_NOCRIT(rec_, obj_) \ + if (QS_GLB_FILTER_(rec_) \ + && ((QP_ QS::apObj_ == static_cast<void *>(0)) \ + || (QP_ QS::apObj_ == (obj_)))) \ + { \ + QP_ QS::begin(static_cast<uint8_t>(rec_)); \ + QS_TIME_(); + +/// \brief End a QS user record without exiting critical section. +#define QS_END_NOCRIT() \ + QS_END_NOCRIT_() + + // QS-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 variable. + /// Otherwise this macro is empty. + /// \sa #QF_CRIT_STAT_TYPE + /// + #define QS_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 QS_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 QS_CRIT_EXIT_() QF_CRIT_EXIT(dummy) + +#else + #define QS_CRIT_STAT_ QF_CRIT_STAT_TYPE critStat_; + #define QS_CRIT_ENTRY_() QF_CRIT_ENTRY(critStat_) + #define QS_CRIT_EXIT_() QF_CRIT_EXIT(critStat_) +#endif + +/// \brief Begin a user QS record with entering critical section. +/// +/// The following example shows how to build a user QS record using the +/// macros #QS_BEGIN, #QS_END, and the formatted output macros: #QS_U8 and +/// #QS_STR. +/// \include qs_user.cpp +/// \note Must always be used in pair with #QS_END +#define QS_BEGIN(rec_, obj_) \ + if (QS_GLB_FILTER_(rec_) \ + && ((QP_ QS::apObj_ == static_cast<void *>(0)) \ + || (QP_ QS::apObj_ == (obj_)))) \ + { \ + QS_CRIT_STAT_ \ + QS_CRIT_ENTRY_(); \ + QP_ QS::begin(static_cast<uint8_t>(rec_)); \ + QS_TIME_(); + +/// \brief End a QS record with exiting critical section. +/// \sa example for #QS_BEGIN +/// \note Must always be used in pair with #QS_BEGIN +#define QS_END() \ + QS_END_() + + +////////////////////////////////////////////////////////////////////////////// +// Macros for use inside other macros or internally in the QP code + +/// \brief Internal QS macro to begin a QS record with entering critical +/// section. +/// \note This macro is intended to use only inside QP components and NOT +/// at the application level. \sa #QS_BEGIN +#define QS_BEGIN_(rec_, objFilter_, obj_) \ + if (QS_GLB_FILTER_(rec_) \ + && (((objFilter_) == static_cast<void *>(0)) \ + || ((objFilter_) == (obj_)))) \ + { \ + QS_CRIT_ENTRY_(); \ + QP_ QS::begin(static_cast<uint8_t>(rec_)); + +/// \brief Internal QS macro to end a QS record with exiting critical +/// section. +/// \note This macro is intended to use only inside QP components and NOT +/// at the application level. \sa #QS_END +#define QS_END_() \ + QP_ QS::end(); \ + QS_CRIT_EXIT_(); \ + } + +/// \brief Internal QS macro to begin a QS record without entering critical +/// section. +/// \note This macro is intended to use only inside QP components and NOT +/// at the application level. \sa #QS_BEGIN_NOCRIT +#define QS_BEGIN_NOCRIT_(rec_, objFilter_, obj_) \ + if (QS_GLB_FILTER_(rec_) \ + && (((objFilter_) == static_cast<void *>(0)) \ + || ((objFilter_) == (obj_)))) \ + { \ + QP_ QS::begin(static_cast<uint8_t>(rec_)); + +/// \brief Internal QS macro to end a QS record without exiting critical +/// section. +/// \note This macro is intended to use only inside QP components and NOT +/// at the application level. \sa #QS_END_NOCRIT +#define QS_END_NOCRIT_() \ + QP_ QS::end(); \ + } + +#if (Q_SIGNAL_SIZE == 1) + /// \brief Internal QS macro to output an unformatted event signal + /// data element + /// \note the size of the pointer depends on the macro #Q_SIGNAL_SIZE. + #define QS_SIG_(sig_) (QP_ QS::u8_(static_cast<uint8_t>(sig_)))) +#elif (Q_SIGNAL_SIZE == 2) + #define QS_SIG_(sig_) (QP_ QS::u16_(static_cast<uint16_t>(sig_))) +#elif (Q_SIGNAL_SIZE == 4) + #define QS_SIG_(sig_) (QP_ QS::u32_(static_cast<uint32_t>(sig_))) +#endif + +/// \brief Internal QS macro to output an unformatted uint8_t data element +#define QS_U8_(data_) (QP_ QS::u8_(data_)) + +/// \brief Internal QS macro to output an unformatted uint16_t data element +#define QS_U16_(data_) (QP_ QS::u16_(data_)) + +/// \brief Internal QS macro to output an unformatted uint32_t data element +#define QS_U32_(data_) (QP_ QS::u32_(data_)) + + +#if (QS_OBJ_PTR_SIZE == 1) + #define QS_OBJ_(obj_) (QP_ QS::u8_(reinterpret_cast<uint8_t>(obj_))) +#elif (QS_OBJ_PTR_SIZE == 2) + #define QS_OBJ_(obj_) (QP_ QS::u16_(reinterpret_cast<uint16_t>(obj_))) +#elif (QS_OBJ_PTR_SIZE == 4) + #define QS_OBJ_(obj_) (QP_ QS::u32_(reinterpret_cast<uint32_t>(obj_))) +#elif (QS_OBJ_PTR_SIZE == 8) + #define QS_OBJ_(obj_) (QP_ QS::u64_(reinterpret_cast<uint64_t>(obj_))) +#else + + /// \brief Internal QS macro to output an unformatted object pointer + /// data element + /// \note the size of the pointer depends on the macro #QS_OBJ_PTR_SIZE. + /// If the size is not defined the size of pointer is assumed 4-bytes. + #define QS_OBJ_(obj_) (QP_ QS::u32_(reinterpret_cast<uint32_t>(obj_))) +#endif + + +#if (QS_FUN_PTR_SIZE == 1) + #define QS_FUN_(fun_) (QP_ QS::u8_(reinterpret_cast<uint8_t>(fun_))) +#elif (QS_FUN_PTR_SIZE == 2) + #define QS_FUN_(fun_) (QP_ QS::u16_(reinterpret_cast<uint16_t>(fun_))) +#elif (QS_FUN_PTR_SIZE == 4) + #define QS_FUN_(fun_) (QP_ QS::u32_(reinterpret_cast<uint32_t>(fun_))) +#elif (QS_FUN_PTR_SIZE == 8) + #define QS_FUN_(fun_) (QP_ QS::u64_(reinterpret_cast<uint64_t>(fun_))) +#else + + /// \brief Internal QS macro to output an unformatted function pointer + /// data element + /// \note the size of the pointer depends on the macro #QS_FUN_PTR_SIZE. + /// If the size is not defined the size of pointer is assumed 4-bytes. + #define QS_FUN_(fun_) (QP_ QS::u32_(reinterpret_cast<uint32_t>(fun_))) +#endif + +/// \brief Internal QS macro to output a zero-terminated ASCII string +/// data element +#define QS_STR_(msg_) (QP_ QS::str_(msg_)) + +/// \brief Internal QS macro to output a zero-terminated ASCII string +/// allocated in ROM data element +#define QS_STR_ROM_(msg_) (QP_ QS::str_ROM_(msg_)) + +////////////////////////////////////////////////////////////////////////////// +// Macros for use in the client code + +/// \brief Output formatted int8_t to the QS record +#define QS_I8(width_, data_) \ + (QP_ QS::u8(static_cast<uint8_t>((static_cast<uint8_t>((width_) << 4)) \ + | static_cast<uint8_t>(QP_ QS_I8_T)), (data_))) + +/// \brief Output formatted uint8_t to the QS record +#define QS_U8(width_, data_) \ + (QP_ QS::u8(static_cast<uint8_t>((static_cast<uint8_t>((width_) << 4)) \ + | static_cast<uint8_t>(QP_ QS_U8_T)), (data_))) + +/// \brief Output formatted int16_t to the QS record +#define QS_I16(width_, data_) \ + (QP_ QS::u16(static_cast<uint8_t>((static_cast<uint8_t>((width_) << 4)) \ + | static_cast<uint8_t>(QP_ QS_I16_T)), (data_))) + +/// \brief Output formatted uint16_t to the QS record +#define QS_U16(width_, data_) \ + (QP_ QS::u16(static_cast<uint8_t>((((width_) << 4)) \ + | static_cast<uint8_t>(QP_ QS_U16_T)), (data_))) + +/// \brief Output formatted int32_t to the QS record +#define QS_I32(width_, data_) \ + (QP_ QS::u32(static_cast<uint8_t>((static_cast<uint8_t>((width_) << 4)) \ + | static_cast<uint8_t>(QP_ QS_I32_T)), (data_))) + +/// \brief Output formatted uint32_t to the QS record +#define QS_U32(width_, data_) \ + (QP_ QS::u32(static_cast<uint8_t>((static_cast<uint8_t>((width_) << 4)) \ + | static_cast<uint8_t>(QP_ QS_U32_T)), (data_))) + +/// \brief Output formatted 32-bit floating point number to the QS record +#define QS_F32(width_, data_) \ + QP_ QS::f32(static_cast<uint8_t>((static_cast<uint8_t>((width_) << 4)) \ + | static_cast<uint8_t>(QP_ QS_F32_T)), (data_))) + +/// \brief Output formatted 64-bit floating point number to the QS record +#define QS_F64(width_, data_) \ + (QP_ QS::f64(static_cast<uint8_t>((static_cast<uint8_t>((width_) << 4)) \ + | static_cast<uint8_t>(QP_ QS_F64_T)), (data_))) + +/// \brief Output formatted int64_t to the QS record +#define QS_I64(width_, data_) \ + (QP_ QS::u64(static_cast<uint8_t>((static_cast<uint8_t>((width_) << 4)) \ + | static_cast<uint8_t>(QP_ QS_I64_T)), (data_))) + +/// \brief Output formatted uint64_t to the QS record +#define QS_U64(width_, data_) \ + (QP_ QS::u64(static_cast<uint8_t>((static_cast<uint8_t>((width_) << 4)) \ + | static_cast<uint8_t>(QP_ QS_U64_T)), (data_))) + +/// \brief Output formatted uint32_t to the QS record +#define QS_U32_HEX(width_, data_) \ + (QP_ QS::u32(static_cast<uint8_t>((static_cast<uint8_t>((width_) << 4)) \ + | static_cast<uint8_t>(QP_ QS_U32_HEX_T)), (data_))) + +/// \brief Output formatted zero-terminated ASCII string to the QS record +#define QS_STR(str_) (QP_ QS::str(str_)) + +/// \brief Output formatted zero-terminated ASCII string from ROM +/// to the QS record +#define QS_STR_ROM(str_) (QP_ QS::str_ROM(str_)) + +/// \brief Output formatted memory block of up to 255 bytes to the QS +/// record +#define QS_MEM(mem_, size_) (QP_ QS::mem((mem_), (size_))) + + +#if (QS_OBJ_PTR_SIZE == 1) + #define QS_OBJ(obj_) (QP_ QS::u8(QS_OBJ_T, (uint8_t)(obj_))) +#elif (QS_OBJ_PTR_SIZE == 2) + #define QS_OBJ(obj_) (QP_ QS::u16(QS_OBJ_T, (uint16_t)(obj_))) +#elif (QS_OBJ_PTR_SIZE == 4) + #define QS_OBJ(obj_) (QP_ QS::u32(QS_OBJ_T, (uint32_t)(obj_))) +#elif (QS_OBJ_PTR_SIZE == 8) + #define QS_OBJ(obj_) (QP_ QS::u64(QS_OBJ_T, (uint64_t)(obj_))) +#else + /// \brief Output formatted object pointer to the QS record + #define QS_OBJ(obj_) (QP_ QS::u32(QS_OBJ_T, (uint32_t)(obj_))) +#endif + + +#if (QS_FUN_PTR_SIZE == 1) + #define QS_FUN(fun_) (QP_ QS::u8(QS_FUN_T, (uint8_t)(fun_))) +#elif (QS_FUN_PTR_SIZE == 2) + #define QS_FUN(fun_) (QP_ QS::u16(QS_FUN_T, (uint16_t)(fun_))) +#elif (QS_FUN_PTR_SIZE == 4) + #define QS_FUN(fun_) (QP_ QS::u32(QS_FUN_T, (uint32_t)(fun_))) +#elif (QS_FUN_PTR_SIZE == 8) + #define QS_FUN(fun_) (QP_ QS::u64(QS_FUN_T, (uint64_t)(fun_))) +#else + /// \brief Output formatted function pointer to the QS record + #define QS_FUN(fun_) (QP_ QS::u32(QS_FUN_T, (uint32_t)(fun_))) +#endif + + +/// \brief Reset the QS session. +/// +/// This trace record should be generated at the beginning of the QS session. +/// It informs the QSPY host application that the new session has been started. +#define QS_RESET() do { \ + if (QS_GLB_FILTER_(QP_ QS_QP_RESET)) { \ + QS_CRIT_STAT_ \ + QS_CRIT_ENTRY_(); \ + QP_ QS::begin(static_cast<uint8_t>(QP_ QS_QP_RESET)); \ + QP_ QS::end(); \ + QS_CRIT_EXIT_(); \ + QP_ QS::onFlush(); \ + } \ +} while (false) + +/// \brief Output signal dictionary record +/// +/// A signal dictionary record associates the numerical value of the signal +/// and the binary address of the state machine that consumes that signal +/// with the human-readable name of the signal. +/// +/// Providing a signal dictionary QS record can vastly improve readability of +/// the QS log, because instead of dealing with cryptic machine addresses the +/// QSpy host utility can display human-readable names. +/// +/// A signal dictionary entry is associated with both the signal value \a sig_ +/// and the state machine \a obj_, because signals are required to be unique +/// only within a given state machine and therefore the same numerical values +/// can represent different signals in different state machines. +/// +/// For the "global" signals that have the same meaning in all state machines +/// (such as globally published signals), you can specify a signal dictionary +/// entry with the \a obj_ parameter set to NULL. +/// +/// The following example shows the definition of signal dictionary entries +/// in the initial transition of the Table active object. Please note that +/// signals HUNGRY_SIG and DONE_SIG are associated with the Table state +/// machine only ("me" \a obj_ pointer). The EAT_SIG signal, on the other +/// hand, is global (0 \a obj_ pointer): +/// \include qs_sigDic.cpp +/// +/// \note The QSpy log utility must capture the signal dictionary record +/// in order to use the human-readable information. You need to connect to +/// the target before the dictionary entries have been transmitted. +/// +/// The following QSpy log example shows the signal dictionary records +/// generated from the Table initial transition and subsequent records that +/// show human-readable names of the signals: +/// \include qs_sigLog.txt +/// +/// The following QSpy log example shows the same sequence of records, but +/// with dictionary records removed. The human-readable signal names are not +/// available. +/// \include qs_sigLog0.txt +#define QS_SIG_DICTIONARY(sig_, obj_) do { \ + if (QS_GLB_FILTER_(QP_ QS_SIG_DIC)) { \ + static char_t const Q_ROM Q_ROM_VAR sig_name_[] = #sig_; \ + QS_CRIT_STAT_ \ + QS_CRIT_ENTRY_(); \ + QP_ QS::begin(static_cast<uint8_t>(QP_ QS_SIG_DIC)); \ + QS_SIG_(sig_); \ + QS_OBJ_(obj_); \ + QS_STR_ROM_(&sig_name_[0]); \ + QP_ QS::end(); \ + QS_CRIT_EXIT_(); \ + QP_ QS::onFlush(); \ + } \ +} while (false) + +/// \brief Output object dictionary record +/// +/// An object dictionary record associates the binary address of an object +/// in the target's memory with the human-readable name of the object. +/// +/// Providing an object dictionary QS record can vastly improve readability of +/// the QS log, because instead of dealing with cryptic machine addresses the +/// QSpy host utility can display human-readable object names. +/// +/// The following example shows the definition of object dictionary entry +/// for the Table active object: +/// \include qs_objDic.cpp +#define QS_OBJ_DICTIONARY(obj_) do { \ + if (QS_GLB_FILTER_(QP_ QS_OBJ_DIC)) { \ + static char_t const Q_ROM Q_ROM_VAR obj_name_[] = #obj_; \ + QS_CRIT_STAT_ \ + QS_CRIT_ENTRY_(); \ + QP_ QS::begin(static_cast<uint8_t>(QP_ QS_OBJ_DIC)); \ + QS_OBJ_(obj_); \ + QS_STR_ROM_(&obj_name_[0]); \ + QP_ QS::end(); \ + QS_CRIT_EXIT_(); \ + QP_ QS::onFlush(); \ + } \ +} while (false) + +/// \brief Output function dictionary record +/// +/// A function dictionary record associates the binary address of a function +/// in the target's memory with the human-readable name of the function. +/// +/// Providing a function dictionary QS record can vastly improve readability +/// of the QS log, because instead of dealing with cryptic machine addresses +/// the QSpy host utility can display human-readable function names. +/// +/// The example from #QS_SIG_DICTIONARY shows the definition of a function +/// dictionary. +#define QS_FUN_DICTIONARY(fun_) do { \ + if (QS_GLB_FILTER_(QP_ QS_FUN_DIC)) { \ + static char_t const Q_ROM Q_ROM_VAR fun_name_[] = #fun_; \ + QS_CRIT_STAT_ \ + QS_CRIT_ENTRY_(); \ + QP_ QS::begin(static_cast<uint8_t>(QP_ QS_FUN_DIC)); \ + QS_FUN_(fun_); \ + QS_STR_ROM_(&fun_name_[0]); \ + QP_ QS::end(); \ + QS_CRIT_EXIT_(); \ + QP_ QS::onFlush(); \ + } \ +} while (false) + +/// \brief Output user QS rectord dictionary record +/// +/// A user QS record dictionary record associates the numerical value of a +/// user record with the human-readable identifier. +#define QS_USR_DICTIONARY(rec_) do { \ + if (QS_GLB_FILTER_(QP_ QS_USR_DIC)) { \ + static char_t const Q_ROM Q_ROM_VAR usr_name_[] = #rec_; \ + QS_CRIT_STAT_ \ + QS_CRIT_ENTRY_(); \ + QP_ QS::begin(static_cast<uint8_t>(QP_ QS_USR_DIC)); \ + QS_U8_(static_cast<uint8_t>(rec_)); \ + QS_STR_ROM_(&usr_name_[0]); \ + QP_ QS::end(); \ + QS_CRIT_EXIT_(); \ + QP_ QS::onFlush(); \ + } \ +} while (false) + +/// \brief Output the assertion violation +#define QS_ASSERTION(module_, loc_) do { \ + QS_BEGIN_NOCRIT_(QP_ QS_ASSERT, \ + static_cast<void *>(0), static_cast<void *>(0)) \ + QS_TIME_(); \ + QS_U16_(static_cast<uint16_t>(loc_)); \ + QS_STR_ROM_(module_); \ + QS_END_NOCRIT_() \ + QP_ QS::onFlush(); \ +} while (false) + +/// \brief Flush the QS trace data to the host +/// +/// This macro invokes the QS::flush() platform-dependent callback function +/// to flush the QS trace buffer to the host. The function typically +/// busy-waits until all the data in the buffer is sent to the host. +/// This is acceptable only in the initial transient. +#define QS_FLUSH() (QP_ QS::onFlush()) + +/// \brief Output the critical section entry record +#define QF_QS_CRIT_ENTRY() \ + QS_BEGIN_NOCRIT_(QP_ QS_QF_CRIT_ENTRY, \ + static_cast<void *>(0), static_cast<void *>(0)) \ + QS_TIME_(); \ + QS_U8_((uint8_t)(++QF_critNest_)); \ + QS_END_NOCRIT_() + +/// \brief Output the critical section exit record +#define QF_QS_CRIT_EXIT() \ + QS_BEGIN_NOCRIT_(QP_ QS_QF_CRIT_EXIT, \ + static_cast<void *>(0), static_cast<void *>(0)) \ + QS_TIME_(); \ + QS_U8_((uint8_t)(QF_critNest_--)); \ + QS_END_NOCRIT_() + +/// \brief Output the interrupt entry record +#define QF_QS_ISR_ENTRY(isrnest_, prio_) \ + QS_BEGIN_NOCRIT_(QP_ QS_QF_ISR_ENTRY, \ + static_cast<void *>(0), static_cast<void *>(0)) \ + QS_TIME_(); \ + QS_U8_(isrnest_); \ + QS_U8_(prio_); \ + QS_END_NOCRIT_() + +/// \brief Output the interrupt exit record +#define QF_QS_ISR_EXIT(isrnest_, prio_) \ + QS_BEGIN_NOCRIT_(QP_ QS_QF_ISR_EXIT, \ + static_cast<void *>(0), static_cast<void *>(0)) \ + QS_TIME_(); \ + QS_U8_(isrnest_); \ + QS_U8_(prio_); \ + QS_END_NOCRIT_() + +/// \brief Execute an action that is only necessary for QS output +#define QF_QS_ACTION(act_) (act_) + +#else // Q_SPY + +// qs_dummy.h ================================================================ +/// \brief Dummy definitions of the QS macros that avoid code generation from +/// the QS instrumentation. + +#define QS_INIT(arg_) (true) +#define QS_EXIT() ((void)0) +#define QS_DUMP() ((void)0) +#define QS_RESET() ((void)0) +#define QS_FILTER_ON(rec_) ((void)0) +#define QS_FILTER_OFF(rec_) ((void)0) +#define QS_FILTER_SM_OBJ(obj_) ((void)0) +#define QS_FILTER_AO_OBJ(obj_) ((void)0) +#define QS_FILTER_MP_OBJ(obj_) ((void)0) +#define QS_FILTER_EQ_OBJ(obj_) ((void)0) +#define QS_FILTER_TE_OBJ(obj_) ((void)0) +#define QS_FILTER_AP_OBJ(obj_) ((void)0) + +#define QS_GET_BYTE(pByte_) (static_cast<uint16_t>(0xFFFFU)) +#define QS_GET_BLOCK(pSize_) (static_cast<uint8_t *>(0)) + +#define QS_BEGIN(rec_, obj_) if (false) { +#define QS_END() } +#define QS_BEGIN_NOCRIT(rec_, obj_) if (false) { +#define QS_END_NOCRIT() } + +#define QS_I8(width_, data_) ((void)0) +#define QS_U8(width_, data_) ((void)0) +#define QS_I16(width_, data_) ((void)0) +#define QS_U16(width_, data_) ((void)0) +#define QS_I32(width_, data_) ((void)0) +#define QS_U32(width_, data_) ((void)0) +#define QS_F32(width_, data_) ((void)0) +#define QS_F64(width_, data_) ((void)0) +#define QS_U64(width_, data_) ((void)0) +#define QS_STR(str_) ((void)0) +#define QS_U32_HEX(width_, data_) ((void)0) +#define QS_STR_ROM(str_) ((void)0) +#define QS_MEM(mem_, size_) ((void)0) +#define QS_SIG(sig_, obj_) ((void)0) +#define QS_OBJ(obj_) ((void)0) +#define QS_FUN(fun_) ((void)0) + +#define QS_SIG_DICTIONARY(sig_, obj_) ((void)0) +#define QS_OBJ_DICTIONARY(obj_) ((void)0) +#define QS_FUN_DICTIONARY(fun_) ((void)0) +#define QS_USR_DICTIONARY(rec_) ((void)0) +#define QS_ASSERTION(module_, loc_) ((void)0) +#define QS_FLUSH() ((void)0) + +// internal QS macros used only in the QP components ......................... +#define QS_CRIT_STAT_ +#define QS_BEGIN_(rec_, refObj_, obj_) if (false) { +#define QS_END_() } +#define QS_BEGIN_NOCRIT_(rec_, refObj_, obj_) if (false) { +#define QS_END_NOCRIT_() } +#define QS_U8_(data_) ((void)0) +#define QS_U16_(data_) ((void)0) +#define QS_U32_(data_) ((void)0) +#define QS_U64_(data_) ((void)0) +#define QS_TIME_() ((void)0) +#define QS_SIG_(sig_) ((void)0) +#define QS_EVS_(size_) ((void)0) +#define QS_OBJ_(obj_) ((void)0) +#define QS_FUN_(fun_) ((void)0) +#define QS_EQC_(ctr_) ((void)0) +#define QS_MPC_(ctr_) ((void)0) +#define QS_MPS_(size_) ((void)0) +#define QS_TEC_(ctr_) ((void)0) + +#define QF_QS_CRIT_ENTRY() ((void)0) +#define QF_QS_CRIT_EXIT() ((void)0) +#define QF_QS_ISR_ENTRY(isrnest_, prio_) ((void)0) +#define QF_QS_ISR_EXIT(isrnest_, prio_) ((void)0) +#define QF_QS_ACTION(act_) ((void)0) + +#endif // Q_SPY + +// qassert.h ================================================================= +/* \brief Customizable QP assertions. +* +* Defines customizable and memory-efficient assertions applicable to +* embedded systems. This header file can be used in C, C++, and mixed C/C++ +* programs. +* +* \note The preprocessor switch Q_NASSERT disables checking assertions. +* In particular macros #Q_ASSERT, #Q_REQUIRE, #Q_ENSURE, #Q_INVARIANT, +* #Q_ERROR as well as #Q_ASSERT_ID, #Q_REQUIRE_ID, #Q_ENSURE_ID, +* #Q_INVARIANT_ID, and #Q_ERROR_ID do NOT evaluate the test condition +* passed as the argument to these macros. One notable exception is the +* macro #Q_ALLEGE, that still evaluates the test condition, but does +* not report assertion failures when the switch Q_NASSERT is defined. +*/ +#ifdef Q_NASSERT /* Q_NASSERT defined--assertion checking disabled */ + + #define Q_DEFINE_THIS_FILE + #define Q_DEFINE_THIS_MODULE(name_) + #define Q_ASSERT(test_) ((void)0) + #define Q_ASSERT_ID(id_, test_) ((void)0) + #define Q_ALLEGE(test_) ((void)(test_)) + #define Q_ALLEGE_ID(id_, test_) ((void)(test_)) + #define Q_ERROR() ((void)0) + #define Q_ERROR_ID(id_) ((void)0) + +#else /* Q_NASSERT not defined--assertion checking enabled */ + + #ifdef __cplusplus + extern "C" { + #endif + + /** \brief Type for line numbers. + * + * This typedef specifies strong type for line numbers. The use of this + * type, rather than plain 'int', is in compliance with the MISRA-C 2004 + * Rule 6.3(adv). + */ + typedef int int_t; + + /** callback invoked in case the condition passed to #Q_ASSERT, + * #Q_REQUIRE, #Q_ENSURE, #Q_ERROR, #Q_ALLEGE as well as #Q_ASSERT_ID, + * #Q_REQUIRE_ID, #Q_ENSURE_ID, #Q_ERROR_ID, and #Q_ALLEGE_ID evaluates + * to FALSE. + * + * \param file name where the assertion failed + * \param line number at which the assertion failed + */ + void Q_onAssert(char_t const Q_ROM * const Q_ROM_VAR file, + int_t const line); + + #ifdef __cplusplus + } + #endif + + /** Place this macro at the top of each C/C++ module to define the file + * name string using __FILE__ (NOTE: __FILE__ might contain lengthy path + * name). This file name will be used in reporting assertions in this file. + */ + #define Q_DEFINE_THIS_FILE \ + static char_t const Q_ROM Q_ROM_VAR l_this_file[] = __FILE__; + + /** Place this macro at the top of each C/C++ module to define the module + * name as the argument \a name_. This file name will be used in reporting + * assertions in this file. + */ + #define Q_DEFINE_THIS_MODULE(name_) \ + static char_t const Q_ROM Q_ROM_VAR l_this_file[] = name_; + + /** General purpose assertion that makes sure the \a test_ argument is + * TRUE. Calls the Q_onAssert() callback if the \a test_ evaluates + * to FALSE. + * \note the \a test_ is NOT evaluated if assertions are disabled with + * the Q_NASSERT switch. + * \sa #Q_ASSERT_ID + */ + #define Q_ASSERT(test_) \ + ((test_) ? (void)0 : Q_onAssert(&l_this_file[0], (int_t)__LINE__)) + + /** General purpose assertion that makes sure the \a test_ argument is + * TRUE. Calls the Q_onAssert() callback if the \a test_ evaluates + * to FALSE. The argument \a id_ is the ID number (unique within + * the file) of the assertion. This assertion style is better suited + * for unit testig, because it avoids the volatility of line numbers + * for indentifying assertions. + * \note the \a test_ is NOT evaluated if assertions are disabled with + * the Q_NASSERT switch. + * \sa #Q_ASSERT + */ + #define Q_ASSERT_ID(id_, test_) \ + ((test_) ? (void)0 : Q_onAssert(&l_this_file[0], (int_t)(id_)) + + /** General purpose assertion that ALWAYS evaluates the \a test_ + * argument and calls the Q_onAssert() callback if the \a test_ + * evaluates to FALSE. + * \note the \a test_ argument IS always evaluated even when assertions + * are disabled with the Q_NASSERT macro. When the Q_NASSERT macro is + * defined, the Q_onAssert() callback is NOT called, even if the + * \a test_ evaluates to FALSE. + * \sa #Q_ALLEGE_ID + */ + #define Q_ALLEGE(test_) Q_ASSERT(test_) + + /** General purpose assertion that ALWAYS evaluates the \a test_ + * argument and calls the Q_onAssert() callback if the \a test_ + * evaluates to FALSE. This assertion style is better suited + * for unit testig, because it avoids the volatility of line numbers + * for indentifying assertions. + * \note the \a test_ argument IS always evaluated even when assertions + * are disabled with the Q_NASSERT macro. When the Q_NASSERT macro is + * defined, the Q_onAssert() callback is NOT called, even if the + * \a test_ evaluates to FALSE. + * \sa #Q_ALLEGE + */ + #define Q_ALLEGE_ID(id_, test_) Q_ASSERT_ID(id_, test_) + + /** Assertion that always calls the Q_onAssert() callback if + * ever executed. + * \note can be disabled with the Q_NASSERT switch. + * \sa #Q_ERROR_ID + */ + #define Q_ERROR() \ + Q_onAssert(&l_this_file[0], (int_t)__LINE__) + + /** Assertion that always calls the Q_onAssert() callback if + * ever executed. This assertion style is better suited for unit + * testig, because it avoids the volatility of line numbers for + * indentifying assertions. + * \note can be disabled with the Q_NASSERT switch. + * \sa #Q_ERROR + */ + #define Q_ERROR_ID(id_) \ + Q_onAssert(l_this_file, (int_t)(id_)) + +#endif /* Q_NASSERT */ + +/** Assertion that checks for a precondition. This macro is equivalent to +* #Q_ASSERT, except the name provides a better documentation of the +* intention of this assertion. +* \sa #Q_REQUIRE_ID +*/ +#define Q_REQUIRE(test_) Q_ASSERT(test_) + +/** Assertion that checks for a precondition. This macro is equivalent to +* #Q_ASSERT_ID, except the name provides a better documentation of the +* intention of this assertion. +* \sa #Q_REQUIRE +*/ +#define Q_REQUIRE_ID(id_, test_) Q_ASSERT_ID(id_, test_) + +/** Assertion that checks for a postcondition. This macro is equivalent to +* #Q_ASSERT, except the name provides a better documentation of the +* intention of this assertion. +* \sa #Q_ENSURE_ID +*/ +#define Q_ENSURE(test_) Q_ASSERT(test_) + +/** Assertion that checks for a postcondition. This macro is equivalent to +* #Q_ASSERT_ID, except the name provides a better documentation of the +* intention of this assertion. +* \sa #Q_ENSURE +*/ +#define Q_ENSURE_ID(id_, test_) Q_ASSERT_ID(id_, test_) + +/** Assertion that checks for an invariant. This macro is equivalent to +* #Q_ASSERT, except the name provides a better documentation of the +* intention of this assertion. +* \sa #Q_INVARIANT_ID +*/ +#define Q_INVARIANT(test_) Q_ASSERT(test_) + +/** Assertion that checks for an invariant. This macro is equivalent to +* #Q_ASSERT_ID, except the name provides a better documentation of the +* intention of this assertion. +* \sa #Q_INVARIANT +*/ +#define Q_INVARIANT_ID(id_, test_) Q_ASSERT_ID(id_, test_) + +/** Compile-time assertion exploits the fact that in C/C++ a dimension of +* an array cannot be negative. The following declaration causes a compilation +* error if the compile-time expression (\a test_) is not TRUE. The assertion +* has no runtime side effects. +*/ +#define Q_ASSERT_COMPILE(test_) \ + extern int_t Q_assert_compile[(test_) ? 1 : -1] + +#endif // qp_h
diff -r 934bb9eea80b -r ca2e6010d9e2 qp_port.cpp --- a/qp_port.cpp Mon Sep 26 03:27:09 2011 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,47 +0,0 @@ -////////////////////////////////////////////////////////////////////////////// -// Product: QP/C++ port to Arduino, cooperative "vanilla" kernel, no Q-SPY -// Last Updated for QP ver: 4.1.06 (modified to fit in one file) -// Date of the Last Update: Jan 04, 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 - -Q_DEFINE_THIS_MODULE(qp_port) - -//............................................................................ -extern "C" void loop() { - QF::run(); // run the application, NOTE: QF::run() does not return -} - -//............................................................................ -// This QP framework does NOT use new or delete, but the WinAVR/avr-g++ -// compiler generates somehow a reference to the operator delete for every -// class with a virtual destructor. QP declares virtual destructors, so to -// satisfy the linker the following dummy definition of the operator -// delete is provided. This operator should never be actually called. -// -void operator delete(void *) { - Q_ERROR(); // this operator should never be actually called -} -
diff -r 934bb9eea80b -r ca2e6010d9e2 qp_port.h --- a/qp_port.h Mon Sep 26 03:27:09 2011 +0000 +++ b/qp_port.h Tue Sep 04 22:20:52 2012 +0000 @@ -1,28 +1,35 @@ ////////////////////////////////////////////////////////////////////////////// // Product: QP/C++ port to ARM Cortex, selectable Vanilla/QK, mbed compiler -// Last Updated for QP ver: 4.1.06 (modified to fit in one file) -// Date of the Last Update: Feb 08, 2011 +// Last Updated for Version: 4.5.02 +// Date of the Last Update: Sep 04, 2012 // // 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. +// 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. // -// 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 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. // -// 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. +// 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 site: http://www.quantum-leaps.com +// Quantum Leaps Web sites: http://www.quantum-leaps.com +// http://www.state-machine.com // e-mail: info@quantum-leaps.com ////////////////////////////////////////////////////////////////////////////// #ifndef qp_port_h @@ -31,10 +38,15 @@ #include "qp_config.h" // QP configuration defined at the application level //............................................................................ + + // QF interrupt disable/enable +#define QF_INT_DISABLE() __disable_irq() +#define QF_INT_ENABLE() __enable_irq() + // QF critical section entry/exit -// QF_INT_KEY_TYPE not defined -#define QF_INT_LOCK(dummy) __disable_irq() -#define QF_INT_UNLOCK(dummy) __enable_irq() +// QF_CRIT_STAT_TYPE not defined: "unconditional interrupt unlocking" policy +#define QF_CRIT_ENTRY(dummy) __disable_irq() +#define QF_CRIT_EXIT(dummy) __enable_irq() #ifdef QK_PREEMPTIVE // QK interrupt entry and exit @@ -43,15 +55,17 @@ ++QK_intNest_; \ QF_QS_ISR_ENTRY(QK_intNest_, QK_currPrio_); \ __enable_irq(); \ - } while (0) + } while (false) #define QK_ISR_EXIT() do { \ __disable_irq(); \ QF_QS_ISR_EXIT(QK_intNest_, QK_currPrio_); \ --QK_intNest_; \ - *((uint32_t volatile *)0xE000ED04) = 0x10000000; \ - __enable_irq(); \ - } while (0) + *Q_UINT2PTR_CAST(uint32_t, 0xE000ED04U) = \ + static_cast<uint32_t>(0x10000000U); \ + __enable_irq(); \ + } while (false) + #else #define QK_ISR_ENTRY() ((void)0) #define QK_ISR_EXIT() ((void)0)