Important changes to repositories hosted on mbed.com
Mbed hosted mercurial repositories are deprecated and are due to be permanently deleted in July 2026.
To keep a copy of this software download the repository Zip archive or clone locally using Mercurial.
It is also possible to export all your personal repositories from the account settings page.
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)