From 455999682a31f1e936fbe4a5f19d9545b6812696 Mon Sep 17 00:00:00 2001 From: Peter Polidoro Date: Tue, 30 Jul 2024 14:12:39 -0400 Subject: [PATCH 1/3] Add v6.9.4 file list and copy script --- copy-qpcpp-files.sh | 4 ++++ qpcpp-file-list.txt | 35 +++++++++++++++++++++++++++++++++++ 2 files changed, 39 insertions(+) create mode 100755 copy-qpcpp-files.sh create mode 100644 qpcpp-file-list.txt diff --git a/copy-qpcpp-files.sh b/copy-qpcpp-files.sh new file mode 100755 index 0000000..d3a8270 --- /dev/null +++ b/copy-qpcpp-files.sh @@ -0,0 +1,4 @@ +for FILE in $(cat ./qpcpp-file-list.txt) +do + cp ${FILE} ./libraries/qpcpp_arm-cm/src +done diff --git a/qpcpp-file-list.txt b/qpcpp-file-list.txt new file mode 100644 index 0000000..4362ebc --- /dev/null +++ b/qpcpp-file-list.txt @@ -0,0 +1,35 @@ +../qpcpp/include/qassert.h +../qpcpp/include/qep.hpp +../qpcpp/src/qf/qep_hsm.cpp +../qpcpp/src/qf/qep_msm.cpp +../qpcpp/ports/arm-cm/qv/gnu/qep_port.hpp +../qpcpp/include/qequeue.hpp +../qpcpp/include/qf.hpp +../qpcpp/src/qf/qf_act.cpp +../qpcpp/src/qf/qf_actq.cpp +../qpcpp/src/qf/qf_defer.cpp +../qpcpp/src/qf/qf_dyn.cpp +../qpcpp/src/qf/qf_mem.cpp +../qpcpp/src/qf_pkg.hpp +../qpcpp/ports/arm-cm/qv/gnu/qf_port.hpp +../qpcpp/src/qf/qf_ps.cpp +../qpcpp/src/qf/qf_qact.cpp +../qpcpp/src/qf/qf_qeq.cpp +../qpcpp/src/qf/qf_qmact.cpp +../qpcpp/src/qf/qf_time.cpp +../qpcpp/include/qmpool.hpp +../qpcpp/include/qpcpp.hpp +../qpcpp/include/qpset.hpp +../qpcpp/src/qs/qs.cpp +../qpcpp/include/qs.hpp +../qpcpp/include/qs_dummy.hpp +../qpcpp/src/qs/qs_fp.cpp +../qpcpp/src/qs_pkg.hpp +../qpcpp/ports/arm-cm/qv/gnu/qs_port.hpp +../qpcpp/src/qs/qs_rx.cpp +../qpcpp/include/qstamp.cpp +../qpcpp/include/qstamp.hpp +../qpcpp/src/qv/qv.cpp +../qpcpp/include/qv.hpp +../qpcpp/ports/arm-cm/qv/gnu/qv_port.cpp +../qpcpp/ports/arm-cm/qv/gnu/qv_port.hpp From 383f1324386a0616e646363d4930a6eeb988923e Mon Sep 17 00:00:00 2001 From: Peter Polidoro Date: Tue, 30 Jul 2024 14:20:06 -0400 Subject: [PATCH 2/3] Remove qpn_avr --- libraries/qpn_avr/examples/blinky/blinky.ino | 175 ------ libraries/qpn_avr/examples/blinky/blinky.qm | 136 ----- .../qpn_avr/examples/blinky_bsp/blinky.hpp | 31 - .../examples/blinky_bsp/blinky_bsp.ino | 115 ---- .../qpn_avr/examples/blinky_bsp/blinky_bsp.qm | 278 --------- libraries/qpn_avr/examples/blinky_bsp/bsp.hpp | 31 - .../qpn_avr/examples/blinky_bsp/bsp_uno.cpp | 182 ------ libraries/qpn_avr/examples/dpp/dpp.ino | 530 ----------------- libraries/qpn_avr/examples/dpp/dpp.qm | 431 -------------- .../qpn_avr/examples/pelican/pelican.ino | 553 ------------------ libraries/qpn_avr/examples/pelican/pelican.qm | 401 ------------- libraries/qpn_avr/library.properties | 11 - libraries/qpn_avr/src/qassert.h | 345 ----------- libraries/qpn_avr/src/qepn.c | 480 --------------- libraries/qpn_avr/src/qepn.h | 487 --------------- libraries/qpn_avr/src/qfn.c | 508 ---------------- libraries/qpn_avr/src/qfn.h | 459 --------------- libraries/qpn_avr/src/qfn_port.h | 87 --- libraries/qpn_avr/src/qpn.h | 105 ---- libraries/qpn_avr/src/qpn_conf.h | 42 -- libraries/qpn_avr/src/qstamp.c | 19 - libraries/qpn_avr/src/qvn.c | 161 ----- libraries/qpn_avr/src/qvn.h | 59 -- 23 files changed, 5626 deletions(-) delete mode 100644 libraries/qpn_avr/examples/blinky/blinky.ino delete mode 100644 libraries/qpn_avr/examples/blinky/blinky.qm delete mode 100644 libraries/qpn_avr/examples/blinky_bsp/blinky.hpp delete mode 100644 libraries/qpn_avr/examples/blinky_bsp/blinky_bsp.ino delete mode 100644 libraries/qpn_avr/examples/blinky_bsp/blinky_bsp.qm delete mode 100644 libraries/qpn_avr/examples/blinky_bsp/bsp.hpp delete mode 100644 libraries/qpn_avr/examples/blinky_bsp/bsp_uno.cpp delete mode 100644 libraries/qpn_avr/examples/dpp/dpp.ino delete mode 100644 libraries/qpn_avr/examples/dpp/dpp.qm delete mode 100644 libraries/qpn_avr/examples/pelican/pelican.ino delete mode 100644 libraries/qpn_avr/examples/pelican/pelican.qm delete mode 100644 libraries/qpn_avr/library.properties delete mode 100644 libraries/qpn_avr/src/qassert.h delete mode 100644 libraries/qpn_avr/src/qepn.c delete mode 100644 libraries/qpn_avr/src/qepn.h delete mode 100644 libraries/qpn_avr/src/qfn.c delete mode 100644 libraries/qpn_avr/src/qfn.h delete mode 100644 libraries/qpn_avr/src/qfn_port.h delete mode 100644 libraries/qpn_avr/src/qpn.h delete mode 100644 libraries/qpn_avr/src/qpn_conf.h delete mode 100644 libraries/qpn_avr/src/qstamp.c delete mode 100644 libraries/qpn_avr/src/qvn.c delete mode 100644 libraries/qpn_avr/src/qvn.h diff --git a/libraries/qpn_avr/examples/blinky/blinky.ino b/libraries/qpn_avr/examples/blinky/blinky.ino deleted file mode 100644 index cbb6a8d..0000000 --- a/libraries/qpn_avr/examples/blinky/blinky.ino +++ /dev/null @@ -1,175 +0,0 @@ -/*.$file${.::blinky.ino} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv*/ -/* -* Model: blinky.qm -* File: ${.::blinky.ino} -* -* This code has been generated by QM 5.1.3 . -* DO NOT EDIT THIS FILE MANUALLY. All your changes will be lost. -* -* 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. -* -* 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. -*/ -/*.$endhead${.::blinky.ino} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ -#include "qpn.h" // QP-nano framework for Arduino - -#include "Arduino.h" // Main include file for the Arduino SDK - -//============================================================================ -// declare all AO classes... -/*.$declare${AOs::Blinky} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv*/ -/*.${AOs::Blinky} ..........................................................*/ -typedef struct Blinky { -/* protected: */ - QActive super; -} Blinky; - -/* protected: */ -static QState Blinky_initial(Blinky * const me); -static QState Blinky_off(Blinky * const me); -static QState Blinky_on(Blinky * const me); -/*.$enddecl${AOs::Blinky} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ -//... - -// AO instances and event queue buffers for them... -Blinky AO_Blinky; -static QEvt l_blinkyQSto[10]; // Event queue storage for Blinky -//... - -//============================================================================ -// QF_active[] array defines all active object control blocks ---------------- -QActiveCB const Q_ROM QF_active[] = { - { (QActive *)0, (QEvt *)0, 0U }, - { (QActive *)&AO_Blinky, l_blinkyQSto, Q_DIM(l_blinkyQSto) } -}; - -//============================================================================ -// various constants for the application... -enum { - BSP_TICKS_PER_SEC = 100, // number of system clock ticks in one second - LED_L = 13 // the pin number of the on-board LED (L) -}; - -//............................................................................ -void setup() { - // initialize the QF-nano framework - QF_init(Q_DIM(QF_active)); - - // initialize all AOs... - QActive_ctor(&AO_Blinky.super, Q_STATE_CAST(&Blinky_initial)); - - // initialize the hardware used in this sketch... - pinMode(LED_L, OUTPUT); // set the LED-L pin to output -} - -//............................................................................ -void loop() { - QF_run(); // run the QF-nano framework -} - -//============================================================================ -// interrupts... -ISR(TIMER2_COMPA_vect) { - QF_tickXISR(0); // process time events for tick rate 0 -} - -//============================================================================ -// QF callbacks... -void QF_onStartup(void) { - // set Timer2 in CTC mode, 1/1024 prescaler, start the timer ticking... - TCCR2A = (1U << WGM21) | (0U << WGM20); - TCCR2B = (1U << CS22 ) | (1U << CS21) | (1U << CS20); // 1/2^10 - ASSR &= ~(1U << AS2); - TIMSK2 = (1U << OCIE2A); // enable TIMER2 compare Interrupt - TCNT2 = 0U; - - // set the output-compare register based on the desired tick frequency - OCR2A = (F_CPU / BSP_TICKS_PER_SEC / 1024U) - 1U; -} -//............................................................................ -void QV_onIdle(void) { // called with interrupts DISABLED - // Put the CPU and peripherals to the low-power mode. You might - // need to customize the clock management for your application, - // see the datasheet for your particular AVR MCU. - SMCR = (0 << SM0) | (1 << SE); // idle mode, adjust to your project - QV_CPU_SLEEP(); // atomically go to sleep and enable interrupts -} -//............................................................................ -Q_NORETURN Q_onAssert(char const Q_ROM * const module, int location) { - // implement the error-handling policy for your application!!! - (void)module; - (void)location; - QF_INT_DISABLE(); // disable all interrupts - QF_RESET(); // reset the CPU - for (;;) { - } -} - -//============================================================================ -// define all AO classes (state machine)... -/*.$skip${QP_VERSION} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv*/ -/*. Check for the minimum required QP version */ -#if (QP_VERSION < 690U) || (QP_VERSION != ((QP_RELEASE^4294967295U) % 0x3E8U)) -#error qpn version 6.9.0 or higher required -#endif -/*.$endskip${QP_VERSION} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ -/*.$define${AOs::Blinky} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv*/ -/*.${AOs::Blinky} ..........................................................*/ -/*.${AOs::Blinky::SM} ......................................................*/ -static QState Blinky_initial(Blinky * const me) { - /*.${AOs::Blinky::SM::initial} */ - QActive_armX((QActive *)me, 0U, - BSP_TICKS_PER_SEC/2U, BSP_TICKS_PER_SEC/2U); - return Q_TRAN(&Blinky_off); -} -/*.${AOs::Blinky::SM::off} .................................................*/ -static QState Blinky_off(Blinky * const me) { - QState status_; - switch (Q_SIG(me)) { - /*.${AOs::Blinky::SM::off} */ - case Q_ENTRY_SIG: { - digitalWrite(LED_L, LOW); - status_ = Q_HANDLED(); - break; - } - /*.${AOs::Blinky::SM::off::Q_TIMEOUT} */ - case Q_TIMEOUT_SIG: { - status_ = Q_TRAN(&Blinky_on); - break; - } - default: { - status_ = Q_SUPER(&QHsm_top); - break; - } - } - return status_; -} -/*.${AOs::Blinky::SM::on} ..................................................*/ -static QState Blinky_on(Blinky * const me) { - QState status_; - switch (Q_SIG(me)) { - /*.${AOs::Blinky::SM::on} */ - case Q_ENTRY_SIG: { - digitalWrite(LED_L, HIGH); - status_ = Q_HANDLED(); - break; - } - /*.${AOs::Blinky::SM::on::Q_TIMEOUT} */ - case Q_TIMEOUT_SIG: { - status_ = Q_TRAN(&Blinky_off); - break; - } - default: { - status_ = Q_SUPER(&QHsm_top); - break; - } - } - return status_; -} -/*.$enddef${AOs::Blinky} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ -//... diff --git a/libraries/qpn_avr/examples/blinky/blinky.qm b/libraries/qpn_avr/examples/blinky/blinky.qm deleted file mode 100644 index 841f061..0000000 --- a/libraries/qpn_avr/examples/blinky/blinky.qm +++ /dev/null @@ -1,136 +0,0 @@ - - - This is the Blinky example for the Arduino-UNO board, which blinks the on-board LED (L) once per second. The example demonstrates: - -1. One active object class "Blinky" (inside the package "AOs") -2. A simple state machine - - - - - - QActive_armX((QActive *)me, 0U, - BSP_TICKS_PER_SEC/2U, BSP_TICKS_PER_SEC/2U); - - - - - - digitalWrite(LED_L, LOW); - - - - - - - - - - - digitalWrite(LED_L, HIGH); - - - - - - - - - - - - - - - - #include "qpn.h" // QP-nano framework for Arduino - -#include "Arduino.h" // Main include file for the Arduino SDK - -//============================================================================ -// declare all AO classes... -$declare${AOs::Blinky} -//... - -// AO instances and event queue buffers for them... -Blinky AO_Blinky; -static QEvt l_blinkyQSto[10]; // Event queue storage for Blinky -//... - -//============================================================================ -// QF_active[] array defines all active object control blocks ---------------- -QActiveCB const Q_ROM QF_active[] = { - { (QActive *)0, (QEvt *)0, 0U }, - { (QActive *)&AO_Blinky, l_blinkyQSto, Q_DIM(l_blinkyQSto) } -}; - -//============================================================================ -// various constants for the application... -enum { - BSP_TICKS_PER_SEC = 100, // number of system clock ticks in one second - LED_L = 13 // the pin number of the on-board LED (L) -}; - -//............................................................................ -void setup() { - // initialize the QF-nano framework - QF_init(Q_DIM(QF_active)); - - // initialize all AOs... - QActive_ctor(&AO_Blinky.super, Q_STATE_CAST(&Blinky_initial)); - - // initialize the hardware used in this sketch... - pinMode(LED_L, OUTPUT); // set the LED-L pin to output -} - -//............................................................................ -void loop() { - QF_run(); // run the QF-nano framework -} - -//============================================================================ -// interrupts... -ISR(TIMER2_COMPA_vect) { - QF_tickXISR(0); // process time events for tick rate 0 -} - -//============================================================================ -// QF callbacks... -void QF_onStartup(void) { - // set Timer2 in CTC mode, 1/1024 prescaler, start the timer ticking... - TCCR2A = (1U << WGM21) | (0U << WGM20); - TCCR2B = (1U << CS22 ) | (1U << CS21) | (1U << CS20); // 1/2^10 - ASSR &= ~(1U << AS2); - TIMSK2 = (1U << OCIE2A); // enable TIMER2 compare Interrupt - TCNT2 = 0U; - - // set the output-compare register based on the desired tick frequency - OCR2A = (F_CPU / BSP_TICKS_PER_SEC / 1024U) - 1U; -} -//............................................................................ -void QV_onIdle(void) { // called with interrupts DISABLED - // Put the CPU and peripherals to the low-power mode. You might - // need to customize the clock management for your application, - // see the datasheet for your particular AVR MCU. - SMCR = (0 << SM0) | (1 << SE); // idle mode, adjust to your project - QV_CPU_SLEEP(); // atomically go to sleep and enable interrupts -} -//............................................................................ -Q_NORETURN Q_onAssert(char const Q_ROM * const module, int location) { - // implement the error-handling policy for your application!!! - (void)module; - (void)location; - QF_INT_DISABLE(); // disable all interrupts - QF_RESET(); // reset the CPU - for (;;) { - } -} - -//============================================================================ -// define all AO classes (state machine)... -$define${AOs::Blinky} -//... - - - - diff --git a/libraries/qpn_avr/examples/blinky_bsp/blinky.hpp b/libraries/qpn_avr/examples/blinky_bsp/blinky.hpp deleted file mode 100644 index aa3b718..0000000 --- a/libraries/qpn_avr/examples/blinky_bsp/blinky.hpp +++ /dev/null @@ -1,31 +0,0 @@ -/*.$file${.::blinky.hpp} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv*/ -/* -* Model: blinky_bsp.qm -* File: ${.::blinky.hpp} -* -* This code has been generated by QM 5.1.3 . -* DO NOT EDIT THIS FILE MANUALLY. All your changes will be lost. -* -* 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. -* -* 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. -*/ -/*.$endhead${.::blinky.hpp} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ -#ifndef BLINKY_HPP -#define BLINKY_HPP - -enum BlinkySignals { - Q_BUTTON_PRESSED_SIG = Q_USER_SIG, - /* add more signals here... */ - MAX_SIG /* the last signal */ -}; - -void Blinky_ctor(void); -extern struct Blinky AO_Blinky; - -#endif // BLINKY_HPP diff --git a/libraries/qpn_avr/examples/blinky_bsp/blinky_bsp.ino b/libraries/qpn_avr/examples/blinky_bsp/blinky_bsp.ino deleted file mode 100644 index e386758..0000000 --- a/libraries/qpn_avr/examples/blinky_bsp/blinky_bsp.ino +++ /dev/null @@ -1,115 +0,0 @@ -/*.$file${.::blinky_bsp.ino} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv*/ -/* -* Model: blinky_bsp.qm -* File: ${.::blinky_bsp.ino} -* -* This code has been generated by QM 5.1.3 . -* DO NOT EDIT THIS FILE MANUALLY. All your changes will be lost. -* -* 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. -* -* 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. -*/ -/*.$endhead${.::blinky_bsp.ino} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ -#include "qpn.h" // QP-nano framework -#include "blinky.hpp" // Blinky interface -#include "bsp.hpp" // BSP for this application - -//============================================================================ -// declare all AO classes... -/*.$declare${AOs::Blinky} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv*/ -/*.${AOs::Blinky} ..........................................................*/ -typedef struct Blinky { -/* protected: */ - QActive super; -} Blinky; - -/* protected: */ -static QState Blinky_initial(Blinky * const me); -static QState Blinky_off(Blinky * const me); -static QState Blinky_on(Blinky * const me); -/*.$enddecl${AOs::Blinky} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ -//... - -// AO instances and event queue buffers for them... -struct Blinky AO_Blinky; - -//============================================================================ -void Blinky_ctor(void) { - Blinky * const me = &AO_Blinky; - QActive_ctor(&me->super, Q_STATE_CAST(&Blinky_initial)); -} - -//============================================================================ -// define all AO classes (state machine)... -/*.$skip${QP_VERSION} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv*/ -/*. Check for the minimum required QP version */ -#if (QP_VERSION < 690U) || (QP_VERSION != ((QP_RELEASE^4294967295U) % 0x3E8U)) -#error qpn version 6.9.0 or higher required -#endif -/*.$endskip${QP_VERSION} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ -/*.$define${AOs::Blinky} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv*/ -/*.${AOs::Blinky} ..........................................................*/ -/*.${AOs::Blinky::SM} ......................................................*/ -static QState Blinky_initial(Blinky * const me) { - /*.${AOs::Blinky::SM::initial} */ - QActive_armX((QActive *)me, 0U, - BSP_TICKS_PER_SEC/2U, BSP_TICKS_PER_SEC/2U); - return Q_TRAN(&Blinky_off); -} -/*.${AOs::Blinky::SM::off} .................................................*/ -static QState Blinky_off(Blinky * const me) { - QState status_; - switch (Q_SIG(me)) { - /*.${AOs::Blinky::SM::off} */ - case Q_ENTRY_SIG: { - BSP_onboardLedOff (); - status_ = Q_HANDLED(); - break; - } - /*.${AOs::Blinky::SM::off::Q_TIMEOUT} */ - case Q_TIMEOUT_SIG: { - status_ = Q_TRAN(&Blinky_on); - break; - } - default: { - status_ = Q_SUPER(&QHsm_top); - break; - } - } - return status_; -} -/*.${AOs::Blinky::SM::on} ..................................................*/ -static QState Blinky_on(Blinky * const me) { - QState status_; - switch (Q_SIG(me)) { - /*.${AOs::Blinky::SM::on} */ - case Q_ENTRY_SIG: { - BSP_onboardLedOn (); - status_ = Q_HANDLED(); - break; - } - /*.${AOs::Blinky::SM::on::Q_BUTTON_PRESSED} */ - case Q_BUTTON_PRESSED_SIG: { - status_ = Q_TRAN(&Blinky_off); - break; - } - /*.${AOs::Blinky::SM::on::Q_TIMEOUT} */ - case Q_TIMEOUT_SIG: { - status_ = Q_TRAN(&Blinky_off); - break; - } - default: { - status_ = Q_SUPER(&QHsm_top); - break; - } - } - return status_; -} -/*.$enddef${AOs::Blinky} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ -//... diff --git a/libraries/qpn_avr/examples/blinky_bsp/blinky_bsp.qm b/libraries/qpn_avr/examples/blinky_bsp/blinky_bsp.qm deleted file mode 100644 index ceb6346..0000000 --- a/libraries/qpn_avr/examples/blinky_bsp/blinky_bsp.qm +++ /dev/null @@ -1,278 +0,0 @@ - - - This is a redesign of the original Blinky example for the Arduino-UNO board, which blinks the on-board LED (L) once per second. - -This example demonstrates: -1. A simple state machine. -2. One active object class "Blinky" (inside the package "AOs"). -3. Extended with a BSP (Board Support Packet, bsp_ino.ino). -4. in the BSP conditional compilation for debug information. - - - - - - QActive_armX((QActive *)me, 0U, - BSP_TICKS_PER_SEC/2U, BSP_TICKS_PER_SEC/2U); - - - - - - BSP_onboardLedOff (); - - - - - - - - - - - BSP_onboardLedOn (); - - - - - - - - - - - - - - - - - - - - - #include "qpn.h" // QP-nano framework -#include "blinky.hpp" // Blinky interface -#include "bsp.hpp" // BSP for this application - -//============================================================================ -// declare all AO classes... -$declare${AOs::Blinky} -//... - -// AO instances and event queue buffers for them... -struct Blinky AO_Blinky; - -//============================================================================ -void Blinky_ctor(void) { - Blinky * const me = &AO_Blinky; - QActive_ctor(&me->super, Q_STATE_CAST(&Blinky_initial)); -} - -//============================================================================ -// define all AO classes (state machine)... -$define${AOs::Blinky} -//... - - - - #ifndef BLINKY_HPP -#define BLINKY_HPP - -enum BlinkySignals { - Q_BUTTON_PRESSED_SIG = Q_USER_SIG, - /* add more signals here... */ - MAX_SIG /* the last signal */ -}; - -void Blinky_ctor(void); -extern struct Blinky AO_Blinky; - -#endif // BLINKY_HPP - - - - #ifndef BSP_HPP -#define BSP_HPP - -enum { - BSP_TICKS_PER_SEC = 100 -}; - -void BSP_initSetup(void); -void BSP_initTick(void); -void BSP_onboardLedOff(void); -void BSP_onboardLedOn(void); - -#endif // BSP_HPP - - - - /* This file has been prepared for Doxygen **************************/ - -/*! \file bsp_uno.ino ************************************************* -* -* \brief Arduino Board Support Package for the Arduino-UNO -* -* Copyright (C) 2021 W.Nijs (ALF4all) -* -* This program is free software: you can redistribute it and/or modify -* it under the terms of the GNU General Public License as published by -* the Free Software Foundation, either version 3 of the License, or -* (at your option) any later version. -* -* 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/>. -* -* \author W.Nijs. -* \date 04/01/2021 -* \version 1.0 04/01/2021, initial revision, W.Nijs -* -*********************************************************************/ -/*****************************************************************//** -* Include section -* Add all #includes here. -*********************************************************************/ -#include "qpn.h" // QP-nano framework -#include "blinky.hpp" // Blinky interface -#include "bsp.hpp" // BSP for this application -#include "Arduino.h" // Main include file for the Arduino SDK - -//#include <avr/interrupt.h> - -/*****************************************************************//** -* Define section -* Add all #defines here. -*********************************************************************/ -#define DEBUG -#define BUTTON 2 // Can only be pin 2 or 3 on the Uno -#define ONBOARDLED 13 - -//unsigned long previousMillis = 0; - - -/*****************************************************************//** -* ISR BSP_buttonPressed() -*********************************************************************/ -void ISR_buttonPressed() { - QACTIVE_POST_ISR((QActive *)&AO_Blinky, Q_BUTTON_PRESSED_SIG, 0U); -}; - -//============================================================================ -static QEvt l_blinkyQSto[10]; // Event queue storage for Blinky -//... - -//============================================================================ -// QF_active[] array defines all active object control blocks ---------------- -QActiveCB const Q_ROM QF_active[] = { - { (QActive *)0, (QEvt *)0, 0U }, - { (QActive *)&AO_Blinky, l_blinkyQSto, Q_DIM(l_blinkyQSto) } -}; - -//............................................................................ -void setup() { - // initialize the QF-nano framework - QF_init(Q_DIM(QF_active)); - BSP_initSetup(); - - // initialize all AOs... - Blinky_ctor(); -} - -//............................................................................ -void loop() { - QF_run(); // run the QF-nano framework -} - -/*****************************************************************//** -* BSP_initSetup() -*********************************************************************/ -void BSP_initSetup(void) { - // initialize the hardware used in this sketch... - pinMode(BUTTON, INPUT_PULLUP); // set the BUTTON pin to input - attachInterrupt(digitalPinToInterrupt(2),ISR_buttonPressed,FALLING); - pinMode(ONBOARDLED, OUTPUT); // set the ONBOARDLED pin to output -#if defined(DEBUG) - Serial.begin(115200); - Serial.println("--- Blinky by ALF4all ---"); - Serial.println("-------------------------"); -#endif -}; - -/*****************************************************************//** -* BSP_initTick() -*********************************************************************/ -void BSP_initTick(void) { - // set Timer2 in CTC mode, 1/1024 prescaler, start the timer ticking... - TCCR2A = (1U << WGM21) | (0U << WGM20); - TCCR2B = (1U << CS22 ) | (1U << CS21) | (1U << CS20); // 1/2^10 - ASSR &= ~(1U << AS2); - TIMSK2 = (1U << OCIE2A); // enable TIMER2 compare Interrupt - TCNT2 = 0U; - - // set the output-compare register based on the desired tick frequency - OCR2A = (F_CPU / BSP_TICKS_PER_SEC / 1024U) - 1U; -}; - -/*****************************************************************//** -* BSP_onboardLedOff() -*********************************************************************/ -void BSP_onboardLedOff(void) { - digitalWrite(ONBOARDLED, LOW); -#if defined(DEBUG) - Serial.println("LED off"); -#endif -}; - -/*****************************************************************//** -* BSP_onboardLedOn() -*********************************************************************/ -void BSP_onboardLedOn(void) { - digitalWrite(ONBOARDLED, HIGH); -#if defined(DEBUG) - Serial.println("LED on"); -#endif -}; - -/*****************************************************************//** -* ISR TIMER2 COMPA -*********************************************************************/ -ISR(TIMER2_COMPA_vect) { - QF_tickXISR(0); // process time events for tick rate 0 -}; - - -//============================================================================ -// QF callbacks... -void QF_onStartup(void) { - BSP_initTick(); -} -//............................................................................ -void QV_onIdle(void) { // called with interrupts DISABLED - // Put the CPU and peripherals to the low-power mode. You might - // need to customize the clock management for your application, - // see the datasheet for your particular AVR MCU. - SMCR = (0 << SM0) | (1 << SE); // idle mode, adjust to your project - QV_CPU_SLEEP(); // atomically go to sleep and enable interrupts -} -//............................................................................ -Q_NORETURN Q_onAssert(char const Q_ROM * const module, int location) { - // implement the error-handling policy for your application!!! - (void)module; - (void)location; - QF_INT_DISABLE(); // disable all interrupts - QF_RESET(); // reset the CPU - for (;;) { - } -} - - - - - - diff --git a/libraries/qpn_avr/examples/blinky_bsp/bsp.hpp b/libraries/qpn_avr/examples/blinky_bsp/bsp.hpp deleted file mode 100644 index b87cc5e..0000000 --- a/libraries/qpn_avr/examples/blinky_bsp/bsp.hpp +++ /dev/null @@ -1,31 +0,0 @@ -/*.$file${.::bsp.hpp} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv*/ -/* -* Model: blinky_bsp.qm -* File: ${.::bsp.hpp} -* -* This code has been generated by QM 5.1.3 . -* DO NOT EDIT THIS FILE MANUALLY. All your changes will be lost. -* -* 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. -* -* 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. -*/ -/*.$endhead${.::bsp.hpp} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ -#ifndef BSP_HPP -#define BSP_HPP - -enum { - BSP_TICKS_PER_SEC = 100 -}; - -void BSP_initSetup(void); -void BSP_initTick(void); -void BSP_onboardLedOff(void); -void BSP_onboardLedOn(void); - -#endif // BSP_HPP diff --git a/libraries/qpn_avr/examples/blinky_bsp/bsp_uno.cpp b/libraries/qpn_avr/examples/blinky_bsp/bsp_uno.cpp deleted file mode 100644 index 375da54..0000000 --- a/libraries/qpn_avr/examples/blinky_bsp/bsp_uno.cpp +++ /dev/null @@ -1,182 +0,0 @@ -/*.$file${.::bsp_uno.cpp} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv*/ -/* -* Model: blinky_bsp.qm -* File: ${.::bsp_uno.cpp} -* -* This code has been generated by QM 5.1.3 . -* DO NOT EDIT THIS FILE MANUALLY. All your changes will be lost. -* -* 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. -* -* 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. -*/ -/*.$endhead${.::bsp_uno.cpp} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ -/* This file has been prepared for Doxygen **************************/ - -/*! \file bsp_uno.ino ************************************************* -* -* \brief Arduino Board Support Package for the Arduino-UNO -* -* Copyright (C) 2021 W.Nijs (ALF4all) -* -* This program is free software: you can redistribute it and/or modify -* it under the terms of the GNU General Public License as published by -* the Free Software Foundation, either version 3 of the License, or -* (at your option) any later version. -* -* 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 . -* -* \author W.Nijs. -* \date 04/01/2021 -* \version 1.0 04/01/2021, initial revision, W.Nijs -* -*********************************************************************/ -/*****************************************************************//** -* Include section -* Add all #includes here. -*********************************************************************/ -#include "qpn.h" // QP-nano framework -#include "blinky.hpp" // Blinky interface -#include "bsp.hpp" // BSP for this application -#include "Arduino.h" // Main include file for the Arduino SDK - -//#include - -/*****************************************************************//** -* Define section -* Add all #defines here. -*********************************************************************/ -#define DEBUG -#define BUTTON 2 // Can only be pin 2 or 3 on the Uno -#define ONBOARDLED 13 - -//unsigned long previousMillis = 0; - - -/*****************************************************************//** -* ISR BSP_buttonPressed() -*********************************************************************/ -void ISR_buttonPressed() { - QACTIVE_POST_ISR((QActive *)&AO_Blinky, Q_BUTTON_PRESSED_SIG, 0U); -}; - -//============================================================================ -static QEvt l_blinkyQSto[10]; // Event queue storage for Blinky -//... - -//============================================================================ -// QF_active[] array defines all active object control blocks ---------------- -QActiveCB const Q_ROM QF_active[] = { - { (QActive *)0, (QEvt *)0, 0U }, - { (QActive *)&AO_Blinky, l_blinkyQSto, Q_DIM(l_blinkyQSto) } -}; - -//............................................................................ -void setup() { - // initialize the QF-nano framework - QF_init(Q_DIM(QF_active)); - BSP_initSetup(); - - // initialize all AOs... - Blinky_ctor(); -} - -//............................................................................ -void loop() { - QF_run(); // run the QF-nano framework -} - -/*****************************************************************//** -* BSP_initSetup() -*********************************************************************/ -void BSP_initSetup(void) { - // initialize the hardware used in this sketch... - pinMode(BUTTON, INPUT_PULLUP); // set the BUTTON pin to input - attachInterrupt(digitalPinToInterrupt(2),ISR_buttonPressed,FALLING); - pinMode(ONBOARDLED, OUTPUT); // set the ONBOARDLED pin to output -#if defined(DEBUG) - Serial.begin(115200); - Serial.println("--- Blinky by ALF4all ---"); - Serial.println("-------------------------"); -#endif -}; - -/*****************************************************************//** -* BSP_initTick() -*********************************************************************/ -void BSP_initTick(void) { - // set Timer2 in CTC mode, 1/1024 prescaler, start the timer ticking... - TCCR2A = (1U << WGM21) | (0U << WGM20); - TCCR2B = (1U << CS22 ) | (1U << CS21) | (1U << CS20); // 1/2^10 - ASSR &= ~(1U << AS2); - TIMSK2 = (1U << OCIE2A); // enable TIMER2 compare Interrupt - TCNT2 = 0U; - - // set the output-compare register based on the desired tick frequency - OCR2A = (F_CPU / BSP_TICKS_PER_SEC / 1024U) - 1U; -}; - -/*****************************************************************//** -* BSP_onboardLedOff() -*********************************************************************/ -void BSP_onboardLedOff(void) { - digitalWrite(ONBOARDLED, LOW); -#if defined(DEBUG) - Serial.println("LED off"); -#endif -}; - -/*****************************************************************//** -* BSP_onboardLedOn() -*********************************************************************/ -void BSP_onboardLedOn(void) { - digitalWrite(ONBOARDLED, HIGH); -#if defined(DEBUG) - Serial.println("LED on"); -#endif -}; - -/*****************************************************************//** -* ISR TIMER2 COMPA -*********************************************************************/ -ISR(TIMER2_COMPA_vect) { - QF_tickXISR(0); // process time events for tick rate 0 -}; - - -//============================================================================ -// QF callbacks... -void QF_onStartup(void) { - BSP_initTick(); -} -//............................................................................ -void QV_onIdle(void) { // called with interrupts DISABLED - // Put the CPU and peripherals to the low-power mode. You might - // need to customize the clock management for your application, - // see the datasheet for your particular AVR MCU. - SMCR = (0 << SM0) | (1 << SE); // idle mode, adjust to your project - QV_CPU_SLEEP(); // atomically go to sleep and enable interrupts -} -//............................................................................ -Q_NORETURN Q_onAssert(char const Q_ROM * const module, int location) { - // implement the error-handling policy for your application!!! - (void)module; - (void)location; - QF_INT_DISABLE(); // disable all interrupts - QF_RESET(); // reset the CPU - for (;;) { - } -} - - diff --git a/libraries/qpn_avr/examples/dpp/dpp.ino b/libraries/qpn_avr/examples/dpp/dpp.ino deleted file mode 100644 index 84aaedd..0000000 --- a/libraries/qpn_avr/examples/dpp/dpp.ino +++ /dev/null @@ -1,530 +0,0 @@ -/*.$file${.::dpp.ino} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv*/ -/* -* Model: dpp.qm -* File: ${.::dpp.ino} -* -* This code has been generated by QM 5.1.3 . -* DO NOT EDIT THIS FILE MANUALLY. All your changes will be lost. -* -* 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. -* -* 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. -*/ -/*.$endhead${.::dpp.ino} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ -#include "qpn.h" // QP-nano framework -#include "Arduino.h" // Main include file for the Arduino SDK - -Q_DEFINE_THIS_MODULE("dpp") - -//============================================================================ -enum DPPSignals { - EAT_SIG = Q_USER_SIG, // posted by Table to let a philosopher eat - DONE_SIG, // posted by Philosopher when done eating - PAUSE_SIG, // posted by BSP to pause the application - SERVE_SIG, // posted by BSP to pause the application - HUNGRY_SIG, // posted to Table from hungry Philo - MAX_SIG // the last signal -}; - -enum { - N_PHILO = 5 // number of Philosophers -}; - -//============================================================================ -// declare all AO classes... -/*.$declare${AOs::Philo} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv*/ -/*.${AOs::Philo} ...........................................................*/ -typedef struct Philo { -/* protected: */ - QActive super; -} Philo; - -/* protected: */ -static QState Philo_initial(Philo * const me); -static QState Philo_thinking(Philo * const me); -static QState Philo_hungry(Philo * const me); -static QState Philo_eating(Philo * const me); -/*.$enddecl${AOs::Philo} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ -/*.$declare${AOs::Table} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv*/ -/*.${AOs::Table} ...........................................................*/ -typedef struct Table { -/* protected: */ - QActive super; - -/* private: */ - uint8_t fork[N_PHILO]; - uint8_t isHungry[N_PHILO]; -} Table; - -/* protected: */ -static QState Table_initial(Table * const me); -static QState Table_active(Table * const me); -static QState Table_serving(Table * const me); -static QState Table_paused(Table * const me); -/*.$enddecl${AOs::Table} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ -//... - -// define all AO instances and event queue buffers for them... -Philo AO_Philo[N_PHILO]; -static QEvt l_philoQueue[N_PHILO][N_PHILO]; - -Table AO_Table; -static QEvt l_tableQueue[2]; -//... - -//============================================================================ -// QF_active[] array defines all active object control blocks ---------------- -QActiveCB const Q_ROM QF_active[] = { - { (QActive *)0, (QEvt *)0, 0U }, - { (QActive *)&AO_Philo[0], l_philoQueue[0], Q_DIM(l_philoQueue[0]) }, - { (QActive *)&AO_Philo[1], l_philoQueue[1], Q_DIM(l_philoQueue[1]) }, - { (QActive *)&AO_Philo[2], l_philoQueue[2], Q_DIM(l_philoQueue[2]) }, - { (QActive *)&AO_Philo[3], l_philoQueue[3], Q_DIM(l_philoQueue[3]) }, - { (QActive *)&AO_Philo[4], l_philoQueue[4], Q_DIM(l_philoQueue[4]) }, - { (QActive *)&AO_Table, l_tableQueue, Q_DIM(l_tableQueue) } -}; - -//============================================================================ -// Board Support Package - -// various other constants for the application... -enum { - BSP_TICKS_PER_SEC = 100, // number of system clock ticks in one second - LED_L = 13, // the pin number of the on-board LED (L) - PHILO_0_PRIO = 1, // priority of the first Philo AO - THINK_TIME = 3*BSP_TICKS_PER_SEC, // time for thinking - EAT_TIME = 2*BSP_TICKS_PER_SEC // time for eating -}; - -//............................................................................ -void BSP_displayPhilStat(uint8_t n, char_t const *stat) { - if (stat[0] == 'e') { - digitalWrite(LED_L, HIGH); - } - else { - digitalWrite(LED_L, LOW); - } - - Serial.print(F("Philosopher ")); - Serial.print(n, DEC); - Serial.print(F(" ")); - Serial.println(stat); -} -//............................................................................ -void BSP_displayPaused(uint8_t paused) { - if (paused) { - Serial.println(F("Paused ON")); - } - else { - Serial.println(F("Paused OFF")); - } -} - -//............................................................................ -void setup() { - // initialize the QF-nano framework - QF_init(Q_DIM(QF_active)); - - // initialize all AOs... - QActive_ctor(&AO_Philo[0].super, Q_STATE_CAST(&Philo_initial)); - QActive_ctor(&AO_Philo[1].super, Q_STATE_CAST(&Philo_initial)); - QActive_ctor(&AO_Philo[2].super, Q_STATE_CAST(&Philo_initial)); - QActive_ctor(&AO_Philo[3].super, Q_STATE_CAST(&Philo_initial)); - QActive_ctor(&AO_Philo[4].super, Q_STATE_CAST(&Philo_initial)); - QActive_ctor(&AO_Table.super, Q_STATE_CAST(&Table_initial)); - - // initialize the hardware used in this sketch... - pinMode(LED_L, OUTPUT); // set the LED-L pin to output - - Serial.begin(115200); // set the highest stanard baud rate of 115200 bps - Serial.print(F("QP-nano: ")); - Serial.print(F(QP_VERSION_STR)); - Serial.println(F("")); -} - -//............................................................................ -void loop() { - QF_run(); // run the QP-nano application -} - -//============================================================================ -// interrupts -ISR(TIMER2_COMPA_vect) { - QF_tickXISR(0); // process time events for tick rate 0 - - if (Serial.available() > 0) { - switch (Serial.read()) { // read the incoming byte - case 'p': - case 'P': - QACTIVE_POST_ISR(&AO_Table, PAUSE_SIG, 0U); - break; - case 's': - case 'S': - QACTIVE_POST_ISR(&AO_Table, SERVE_SIG, 0U); - break; - } - } -} - -//============================================================================ -// QF callbacks... -void QF_onStartup(void) { - // set Timer2 in CTC mode, 1/1024 prescaler, start the timer ticking... - TCCR2A = (1U << WGM21) | (0U << WGM20); - TCCR2B = (1U << CS22 ) | (1U << CS21) | (1U << CS20); // 1/2^10 - ASSR &= ~(1U << AS2); - TIMSK2 = (1U << OCIE2A); // enable TIMER2 compare Interrupt - TCNT2 = 0U; - - // set the output-compare register based on the desired tick frequency - OCR2A = (F_CPU / BSP_TICKS_PER_SEC / 1024U) - 1U; -} -//............................................................................ -void QV_onIdle(void) { // called with interrupts DISABLED - // Put the CPU and peripherals to the low-power mode. You might - // need to customize the clock management for your application, - // see the datasheet for your particular AVR MCU. - SMCR = (0 << SM0) | (1 << SE); // idle mode, adjust to your project - QV_CPU_SLEEP(); // atomically go to sleep and enable interrupts -} -//............................................................................ -Q_NORETURN Q_onAssert(char const Q_ROM * const module, int location) { - // implement the error-handling policy for your application!!! - Serial.print(F("ASSERTION:")); - Serial.print(module); - Serial.print(location, DEC); - for (uint32_t volatile i = 100000U; i > 0U; --i) { - } - QF_INT_DISABLE(); // disable all interrupts - QF_RESET(); // reset the CPU - for (;;) { - } -} - -//============================================================================ -// define all AO classes... -/*.$skip${QP_VERSION} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv*/ -/*. Check for the minimum required QP version */ -#if (QP_VERSION < 690U) || (QP_VERSION != ((QP_RELEASE^4294967295U) % 0x3E8U)) -#error qpn version 6.9.0 or higher required -#endif -/*.$endskip${QP_VERSION} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ -/*.$define${AOs::Philo} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv*/ -/*.${AOs::Philo} ...........................................................*/ -/*.${AOs::Philo::SM} .......................................................*/ -static QState Philo_initial(Philo * const me) { - /*.${AOs::Philo::SM::initial} */ - return Q_TRAN(&Philo_thinking); -} -/*.${AOs::Philo::SM::thinking} .............................................*/ -static QState Philo_thinking(Philo * const me) { - QState status_; - switch (Q_SIG(me)) { - /*.${AOs::Philo::SM::thinking} */ - case Q_ENTRY_SIG: { - QActive_armX(&me->super, 0U, THINK_TIME, 0U); - status_ = Q_HANDLED(); - break; - } - /*.${AOs::Philo::SM::thinking} */ - case Q_EXIT_SIG: { - QActive_disarmX(&me->super, 0U); - status_ = Q_HANDLED(); - break; - } - /*.${AOs::Philo::SM::thinking::Q_TIMEOUT} */ - case Q_TIMEOUT_SIG: { - status_ = Q_TRAN(&Philo_hungry); - break; - } - /*.${AOs::Philo::SM::thinking::EAT, DONE} */ - case EAT_SIG: /* intentionally fall through */ - case DONE_SIG: { - Q_ERROR(); /* these events should never arrive in this state */ - status_ = Q_HANDLED(); - break; - } - default: { - status_ = Q_SUPER(&QHsm_top); - break; - } - } - return status_; -} -/*.${AOs::Philo::SM::hungry} ...............................................*/ -static QState Philo_hungry(Philo * const me) { - QState status_; - switch (Q_SIG(me)) { - /*.${AOs::Philo::SM::hungry} */ - case Q_ENTRY_SIG: { - QACTIVE_POST(&AO_Table, HUNGRY_SIG, me->super.prio); - status_ = Q_HANDLED(); - break; - } - /*.${AOs::Philo::SM::hungry::EAT} */ - case EAT_SIG: { - status_ = Q_TRAN(&Philo_eating); - break; - } - /*.${AOs::Philo::SM::hungry::DONE} */ - case DONE_SIG: { - Q_ERROR(); /* this event should never arrive in this state */ - status_ = Q_HANDLED(); - break; - } - default: { - status_ = Q_SUPER(&QHsm_top); - break; - } - } - return status_; -} -/*.${AOs::Philo::SM::eating} ...............................................*/ -static QState Philo_eating(Philo * const me) { - QState status_; - switch (Q_SIG(me)) { - /*.${AOs::Philo::SM::eating} */ - case Q_ENTRY_SIG: { - QActive_armX(&me->super, 0U, EAT_TIME, 0U); - status_ = Q_HANDLED(); - break; - } - /*.${AOs::Philo::SM::eating} */ - case Q_EXIT_SIG: { - QActive_disarmX(&me->super, 0U); - QACTIVE_POST(QF_ACTIVE_CAST(&AO_Table), DONE_SIG, me->super.prio); - status_ = Q_HANDLED(); - break; - } - /*.${AOs::Philo::SM::eating::Q_TIMEOUT} */ - case Q_TIMEOUT_SIG: { - status_ = Q_TRAN(&Philo_thinking); - break; - } - /*.${AOs::Philo::SM::eating::EAT, DONE} */ - case EAT_SIG: /* intentionally fall through */ - case DONE_SIG: { - Q_ERROR(); /* these events should never arrive in this state */ - status_ = Q_HANDLED(); - break; - } - default: { - status_ = Q_SUPER(&QHsm_top); - break; - } - } - return status_; -} -/*.$enddef${AOs::Philo} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ - -static inline uint8_t RIGHT(uint8_t n) { - return (n + (N_PHILO - 1)) % N_PHILO; -} -static inline uint8_t LEFT(uint8_t n) { - return (n + 1) % N_PHILO; -} -enum { - FREE = 0, - USED = 1 -}; - -/*.$define${AOs::Table} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv*/ -/*.${AOs::Table} ...........................................................*/ -/*.${AOs::Table::SM} .......................................................*/ -static QState Table_initial(Table * const me) { - /*.${AOs::Table::SM::initial} */ - uint8_t n; - for (n = 0U; n < N_PHILO; ++n) { - me->fork[n] = FREE; - me->isHungry[n] = 0U; - BSP_displayPhilStat(n, "thinking"); - } - return Q_TRAN(&Table_serving); -} -/*.${AOs::Table::SM::active} ...............................................*/ -static QState Table_active(Table * const me) { - QState status_; - switch (Q_SIG(me)) { - /*.${AOs::Table::SM::active::EAT} */ - case EAT_SIG: { - Q_ERROR(); - status_ = Q_HANDLED(); - break; - } - default: { - status_ = Q_SUPER(&QHsm_top); - break; - } - } - return status_; -} -/*.${AOs::Table::SM::active::serving} ......................................*/ -static QState Table_serving(Table * const me) { - QState status_; - switch (Q_SIG(me)) { - /*.${AOs::Table::SM::active::serving} */ - case Q_ENTRY_SIG: { - uint8_t n; - for (n = 0U; n < N_PHILO; ++n) { /* give permissions to eat... */ - if ((me->isHungry[n] != 0U) - && (me->fork[LEFT(n)] == FREE) - && (me->fork[n] == FREE)) - { - QMActive *philo; - - me->fork[LEFT(n)] = USED; - me->fork[n] = USED; - philo = QF_ACTIVE_CAST(Q_ROM_PTR(QF_active[PHILO_0_PRIO + n].act)); - QACTIVE_POST(philo, EAT_SIG, n); - me->isHungry[n] = 0U; - BSP_displayPhilStat(n, "eating "); - } - } - status_ = Q_HANDLED(); - break; - } - /*.${AOs::Table::SM::active::serving::HUNGRY} */ - case HUNGRY_SIG: { - uint8_t n, m; - - n = (uint8_t)(Q_PAR(me) - PHILO_0_PRIO); - /* phil ID must be in range and he must be not hungry */ - Q_ASSERT((n < N_PHILO) && (me->isHungry[n] == 0U)); - - BSP_displayPhilStat(n, "hungry "); - m = LEFT(n); - /*.${AOs::Table::SM::active::serving::HUNGRY::[bothfree]} */ - if ((me->fork[m] == FREE) && (me->fork[n] == FREE)) { - me->fork[m] = USED; - me->fork[n] = USED; - QACTIVE_POST(&AO_Philo[n], EAT_SIG, n); - BSP_displayPhilStat(n, "eating "); - status_ = Q_HANDLED(); - } - /*.${AOs::Table::SM::active::serving::HUNGRY::[else]} */ - else { - me->isHungry[n] = 1U; - status_ = Q_HANDLED(); - } - break; - } - /*.${AOs::Table::SM::active::serving::DONE} */ - case DONE_SIG: { - uint8_t n, m; - QMActive *philo; - - n = (uint8_t)(Q_PAR(me) - PHILO_0_PRIO); - /* phil ID must be in range and he must be not hungry */ - Q_ASSERT((n < N_PHILO) && (me->isHungry[n] == 0U)); - - BSP_displayPhilStat(n, "thinking"); - m = LEFT(n); - /* both forks of Phil[n] must be used */ - Q_ASSERT((me->fork[n] == USED) && (me->fork[m] == USED)); - - me->fork[m] = FREE; - me->fork[n] = FREE; - m = RIGHT(n); /* check the right neighbor */ - - if ((me->isHungry[m] != 0U) && (me->fork[m] == FREE)) { - me->fork[n] = USED; - me->fork[m] = USED; - me->isHungry[m] = 0U; - philo = QF_ACTIVE_CAST(Q_ROM_PTR(QF_active[PHILO_0_PRIO + m].act)); - QACTIVE_POST(philo, EAT_SIG, m); - BSP_displayPhilStat(m, "eating "); - } - m = LEFT(n); /* check the left neighbor */ - n = LEFT(m); /* left fork of the left neighbor */ - if ((me->isHungry[m] != 0U) && (me->fork[n] == FREE)) { - me->fork[m] = USED; - me->fork[n] = USED; - me->isHungry[m] = 0U; - philo = QF_ACTIVE_CAST(Q_ROM_PTR(QF_active[PHILO_0_PRIO + m].act)); - QACTIVE_POST(philo, EAT_SIG, m); - BSP_displayPhilStat(m, "eating "); - } - status_ = Q_HANDLED(); - break; - } - /*.${AOs::Table::SM::active::serving::EAT} */ - case EAT_SIG: { - Q_ERROR(); - status_ = Q_HANDLED(); - break; - } - /*.${AOs::Table::SM::active::serving::PAUSE} */ - case PAUSE_SIG: { - status_ = Q_TRAN(&Table_paused); - break; - } - default: { - status_ = Q_SUPER(&Table_active); - break; - } - } - return status_; -} -/*.${AOs::Table::SM::active::paused} .......................................*/ -static QState Table_paused(Table * const me) { - QState status_; - switch (Q_SIG(me)) { - /*.${AOs::Table::SM::active::paused} */ - case Q_ENTRY_SIG: { - BSP_displayPaused(1U); - status_ = Q_HANDLED(); - break; - } - /*.${AOs::Table::SM::active::paused} */ - case Q_EXIT_SIG: { - BSP_displayPaused(0U); - status_ = Q_HANDLED(); - break; - } - /*.${AOs::Table::SM::active::paused::SERVE} */ - case SERVE_SIG: { - status_ = Q_TRAN(&Table_serving); - break; - } - /*.${AOs::Table::SM::active::paused::HUNGRY} */ - case HUNGRY_SIG: { - uint8_t n = (uint8_t)(Q_PAR(me) - PHILO_0_PRIO); - /* philo ID must be in range and he must be not hungry */ - Q_ASSERT((n < N_PHILO) && (me->isHungry[n] == 0U)); - me->isHungry[n] = 1U; - BSP_displayPhilStat(n, "hungry "); - status_ = Q_HANDLED(); - break; - } - /*.${AOs::Table::SM::active::paused::DONE} */ - case DONE_SIG: { - uint8_t n, m; - - n = (uint8_t)(Q_PAR(me) - PHILO_0_PRIO); - /* phil ID must be in range and he must be not hungry */ - Q_ASSERT((n < N_PHILO) && (me->isHungry[n] == 0U)); - - BSP_displayPhilStat(n, "thinking"); - m = LEFT(n); - /* both forks of Phil[n] must be used */ - Q_ASSERT((me->fork[n] == USED) && (me->fork[m] == USED)); - - me->fork[m] = FREE; - me->fork[n] = FREE; - status_ = Q_HANDLED(); - break; - } - default: { - status_ = Q_SUPER(&Table_active); - break; - } - } - return status_; -} -/*.$enddef${AOs::Table} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ -//... diff --git a/libraries/qpn_avr/examples/dpp/dpp.qm b/libraries/qpn_avr/examples/dpp/dpp.qm deleted file mode 100644 index 644c41b..0000000 --- a/libraries/qpn_avr/examples/dpp/dpp.qm +++ /dev/null @@ -1,431 +0,0 @@ - - - This is the Dining Philosopher Problem (DPP) example for the Arduino-UNO board. The example demonstrates: - -1. Multiple active objects (5 Philosophers and 1 Table AO) -2. Two active object classes (Philo and Table inside the package "AOs") -2. State machines associated with Philo and Table -3. The concept of a Board Support Pakage (BSP) - - - - - - - - - - - QActive_armX(&me->super, 0U, THINK_TIME, 0U); - QActive_disarmX(&me->super, 0U); - - - - - - - Q_ERROR(); /* these events should never arrive in this state */ - - - - - - - - - - - QACTIVE_POST(&AO_Table, HUNGRY_SIG, me->super.prio); - - - - - - - Q_ERROR(); /* this event should never arrive in this state */ - - - - - - - - - - QActive_armX(&me->super, 0U, EAT_TIME, 0U); - QActive_disarmX(&me->super, 0U); -QACTIVE_POST(QF_ACTIVE_CAST(&AO_Table), DONE_SIG, me->super.prio); - - - - - - - Q_ERROR(); /* these events should never arrive in this state */ - - - - - - - - - - - - - - - - - - uint8_t n; -for (n = 0U; n < N_PHILO; ++n) { - me->fork[n] = FREE; - me->isHungry[n] = 0U; - BSP_displayPhilStat(n, "thinking"); -} - - - - - - - Q_ERROR(); - - - - - - uint8_t n; -for (n = 0U; n < N_PHILO; ++n) { /* give permissions to eat... */ - if ((me->isHungry[n] != 0U) - && (me->fork[LEFT(n)] == FREE) - && (me->fork[n] == FREE)) - { - QMActive *philo; - - me->fork[LEFT(n)] = USED; - me->fork[n] = USED; - philo = QF_ACTIVE_CAST(Q_ROM_PTR(QF_active[PHILO_0_PRIO + n].act)); - QACTIVE_POST(philo, EAT_SIG, n); - me->isHungry[n] = 0U; - BSP_displayPhilStat(n, "eating "); - } -} - - uint8_t n, m; - -n = (uint8_t)(Q_PAR(me) - PHILO_0_PRIO); -/* phil ID must be in range and he must be not hungry */ -Q_ASSERT((n < N_PHILO) && (me->isHungry[n] == 0U)); - -BSP_displayPhilStat(n, "hungry "); -m = LEFT(n); - - (me->fork[m] == FREE) && (me->fork[n] == FREE) - me->fork[m] = USED; -me->fork[n] = USED; -QACTIVE_POST(&AO_Philo[n], EAT_SIG, n); -BSP_displayPhilStat(n, "eating "); - - - - - - else - me->isHungry[n] = 1U; - - - - - - - - - - uint8_t n, m; -QMActive *philo; - -n = (uint8_t)(Q_PAR(me) - PHILO_0_PRIO); -/* phil ID must be in range and he must be not hungry */ -Q_ASSERT((n < N_PHILO) && (me->isHungry[n] == 0U)); - -BSP_displayPhilStat(n, "thinking"); -m = LEFT(n); -/* both forks of Phil[n] must be used */ -Q_ASSERT((me->fork[n] == USED) && (me->fork[m] == USED)); - -me->fork[m] = FREE; -me->fork[n] = FREE; -m = RIGHT(n); /* check the right neighbor */ - -if ((me->isHungry[m] != 0U) && (me->fork[m] == FREE)) { - me->fork[n] = USED; - me->fork[m] = USED; - me->isHungry[m] = 0U; - philo = QF_ACTIVE_CAST(Q_ROM_PTR(QF_active[PHILO_0_PRIO + m].act)); - QACTIVE_POST(philo, EAT_SIG, m); - BSP_displayPhilStat(m, "eating "); -} -m = LEFT(n); /* check the left neighbor */ -n = LEFT(m); /* left fork of the left neighbor */ -if ((me->isHungry[m] != 0U) && (me->fork[n] == FREE)) { - me->fork[m] = USED; - me->fork[n] = USED; - me->isHungry[m] = 0U; - philo = QF_ACTIVE_CAST(Q_ROM_PTR(QF_active[PHILO_0_PRIO + m].act)); - QACTIVE_POST(philo, EAT_SIG, m); - BSP_displayPhilStat(m, "eating "); -} - - - - - - Q_ERROR(); - - - - - - - - - - - - - - - BSP_displayPaused(1U); - BSP_displayPaused(0U); - - - - - - - uint8_t n = (uint8_t)(Q_PAR(me) - PHILO_0_PRIO); -/* philo ID must be in range and he must be not hungry */ -Q_ASSERT((n < N_PHILO) && (me->isHungry[n] == 0U)); -me->isHungry[n] = 1U; -BSP_displayPhilStat(n, "hungry "); - - - - - - uint8_t n, m; - -n = (uint8_t)(Q_PAR(me) - PHILO_0_PRIO); -/* phil ID must be in range and he must be not hungry */ -Q_ASSERT((n < N_PHILO) && (me->isHungry[n] == 0U)); - -BSP_displayPhilStat(n, "thinking"); -m = LEFT(n); -/* both forks of Phil[n] must be used */ -Q_ASSERT((me->fork[n] == USED) && (me->fork[m] == USED)); - -me->fork[m] = FREE; -me->fork[n] = FREE; - - - - - - - - - - - - - - - - - - #include "qpn.h" // QP-nano framework -#include "Arduino.h" // Main include file for the Arduino SDK - -Q_DEFINE_THIS_MODULE("dpp") - -//============================================================================ -enum DPPSignals { - EAT_SIG = Q_USER_SIG, // posted by Table to let a philosopher eat - DONE_SIG, // posted by Philosopher when done eating - PAUSE_SIG, // posted by BSP to pause the application - SERVE_SIG, // posted by BSP to pause the application - HUNGRY_SIG, // posted to Table from hungry Philo - MAX_SIG // the last signal -}; - -enum { - N_PHILO = 5 // number of Philosophers -}; - -//============================================================================ -// declare all AO classes... -$declare${AOs::Philo} -$declare${AOs::Table} -//... - -// define all AO instances and event queue buffers for them... -Philo AO_Philo[N_PHILO]; -static QEvt l_philoQueue[N_PHILO][N_PHILO]; - -Table AO_Table; -static QEvt l_tableQueue[2]; -//... - -//============================================================================ -// QF_active[] array defines all active object control blocks ---------------- -QActiveCB const Q_ROM QF_active[] = { - { (QActive *)0, (QEvt *)0, 0U }, - { (QActive *)&AO_Philo[0], l_philoQueue[0], Q_DIM(l_philoQueue[0]) }, - { (QActive *)&AO_Philo[1], l_philoQueue[1], Q_DIM(l_philoQueue[1]) }, - { (QActive *)&AO_Philo[2], l_philoQueue[2], Q_DIM(l_philoQueue[2]) }, - { (QActive *)&AO_Philo[3], l_philoQueue[3], Q_DIM(l_philoQueue[3]) }, - { (QActive *)&AO_Philo[4], l_philoQueue[4], Q_DIM(l_philoQueue[4]) }, - { (QActive *)&AO_Table, l_tableQueue, Q_DIM(l_tableQueue) } -}; - -//============================================================================ -// Board Support Package - -// various other constants for the application... -enum { - BSP_TICKS_PER_SEC = 100, // number of system clock ticks in one second - LED_L = 13, // the pin number of the on-board LED (L) - PHILO_0_PRIO = 1, // priority of the first Philo AO - THINK_TIME = 3*BSP_TICKS_PER_SEC, // time for thinking - EAT_TIME = 2*BSP_TICKS_PER_SEC // time for eating -}; - -//............................................................................ -void BSP_displayPhilStat(uint8_t n, char_t const *stat) { - if (stat[0] == 'e') { - digitalWrite(LED_L, HIGH); - } - else { - digitalWrite(LED_L, LOW); - } - - Serial.print(F("Philosopher ")); - Serial.print(n, DEC); - Serial.print(F(" ")); - Serial.println(stat); -} -//............................................................................ -void BSP_displayPaused(uint8_t paused) { - if (paused) { - Serial.println(F("Paused ON")); - } - else { - Serial.println(F("Paused OFF")); - } -} - -//............................................................................ -void setup() { - // initialize the QF-nano framework - QF_init(Q_DIM(QF_active)); - - // initialize all AOs... - QActive_ctor(&AO_Philo[0].super, Q_STATE_CAST(&Philo_initial)); - QActive_ctor(&AO_Philo[1].super, Q_STATE_CAST(&Philo_initial)); - QActive_ctor(&AO_Philo[2].super, Q_STATE_CAST(&Philo_initial)); - QActive_ctor(&AO_Philo[3].super, Q_STATE_CAST(&Philo_initial)); - QActive_ctor(&AO_Philo[4].super, Q_STATE_CAST(&Philo_initial)); - QActive_ctor(&AO_Table.super, Q_STATE_CAST(&Table_initial)); - - // initialize the hardware used in this sketch... - pinMode(LED_L, OUTPUT); // set the LED-L pin to output - - Serial.begin(115200); // set the highest stanard baud rate of 115200 bps - Serial.print(F("QP-nano: ")); - Serial.print(F(QP_VERSION_STR)); - Serial.println(F("")); -} - -//............................................................................ -void loop() { - QF_run(); // run the QP-nano application -} - -//============================================================================ -// interrupts -ISR(TIMER2_COMPA_vect) { - QF_tickXISR(0); // process time events for tick rate 0 - - if (Serial.available() > 0) { - switch (Serial.read()) { // read the incoming byte - case 'p': - case 'P': - QACTIVE_POST_ISR(&AO_Table, PAUSE_SIG, 0U); - break; - case 's': - case 'S': - QACTIVE_POST_ISR(&AO_Table, SERVE_SIG, 0U); - break; - } - } -} - -//============================================================================ -// QF callbacks... -void QF_onStartup(void) { - // set Timer2 in CTC mode, 1/1024 prescaler, start the timer ticking... - TCCR2A = (1U << WGM21) | (0U << WGM20); - TCCR2B = (1U << CS22 ) | (1U << CS21) | (1U << CS20); // 1/2^10 - ASSR &= ~(1U << AS2); - TIMSK2 = (1U << OCIE2A); // enable TIMER2 compare Interrupt - TCNT2 = 0U; - - // set the output-compare register based on the desired tick frequency - OCR2A = (F_CPU / BSP_TICKS_PER_SEC / 1024U) - 1U; -} -//............................................................................ -void QV_onIdle(void) { // called with interrupts DISABLED - // Put the CPU and peripherals to the low-power mode. You might - // need to customize the clock management for your application, - // see the datasheet for your particular AVR MCU. - SMCR = (0 << SM0) | (1 << SE); // idle mode, adjust to your project - QV_CPU_SLEEP(); // atomically go to sleep and enable interrupts -} -//............................................................................ -Q_NORETURN Q_onAssert(char const Q_ROM * const module, int location) { - // implement the error-handling policy for your application!!! - Serial.print(F("ASSERTION:")); - Serial.print(module); - Serial.print(location, DEC); - for (uint32_t volatile i = 100000U; i > 0U; --i) { - } - QF_INT_DISABLE(); // disable all interrupts - QF_RESET(); // reset the CPU - for (;;) { - } -} - -//============================================================================ -// define all AO classes... -$define${AOs::Philo} - -static inline uint8_t RIGHT(uint8_t n) { - return (n + (N_PHILO - 1)) % N_PHILO; -} -static inline uint8_t LEFT(uint8_t n) { - return (n + 1) % N_PHILO; -} -enum { - FREE = 0, - USED = 1 -}; - -$define${AOs::Table} -//... - - - - diff --git a/libraries/qpn_avr/examples/pelican/pelican.ino b/libraries/qpn_avr/examples/pelican/pelican.ino deleted file mode 100644 index 9928e69..0000000 --- a/libraries/qpn_avr/examples/pelican/pelican.ino +++ /dev/null @@ -1,553 +0,0 @@ -/*.$file${.::pelican.ino} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv*/ -/* -* Model: pelican.qm -* File: ${.::pelican.ino} -* -* This code has been generated by QM 5.1.3 . -* DO NOT EDIT THIS FILE MANUALLY. All your changes will be lost. -* -* 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. -* -* 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. -*/ -/*.$endhead${.::pelican.ino} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ -#include "qpn.h" // QP-nano framework -#include "Arduino.h" // Main include file for the Arduino SDK - -//============================================================================ -// events used in this application... -enum PelicanSignals { - PEDS_WAITING_SIG = Q_USER_SIG, // PEDestrians-waiting button press event - OFF_SIG, // OFF-button press event - ON_SIG // ON-button press event -}; - -//============================================================================ -// declare all AO classes... -/*.$declare${AOs::Pelican} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv*/ -/*.${AOs::Pelican} .........................................................*/ -typedef struct Pelican { -/* protected: */ - QActive super; - -/* private: */ - uint8_t flashCtr; -} Pelican; - -/* protected: */ -static QState Pelican_initial(Pelican * const me); -static QState Pelican_operational(Pelican * const me); -static QState Pelican_carsEnabled(Pelican * const me); -static QState Pelican_carsGreen(Pelican * const me); -static QState Pelican_carsGreenNoPed(Pelican * const me); -static QState Pelican_carsGreenInt(Pelican * const me); -static QState Pelican_carsGreenPedWait(Pelican * const me); -static QState Pelican_carsYellow(Pelican * const me); -static QState Pelican_pedsEnabled(Pelican * const me); -static QState Pelican_pedsWalk(Pelican * const me); -static QState Pelican_pedsFlash(Pelican * const me); -static QState Pelican_offline(Pelican * const me); -/*.$enddecl${AOs::Pelican} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ -//... - -// define all AO instances and event queue buffers for them... -Pelican AO_Pelican; -static QEvt l_pelicanQSto[10]; // Event queue storage for Pelican -//... - -//============================================================================ -// QF_active[] array defines all active object control blocks ---------------- -QActiveCB const Q_ROM QF_active[] = { - { (QActive *)0, (QEvt *)0, 0U }, - { (QActive *)&AO_Pelican, l_pelicanQSto, Q_DIM(l_pelicanQSto) } -}; - -//============================================================================ -// Board Support Package (BSP) -enum BSP_CarsSignal { // street signals ... - CARS_BLANK, CARS_RED, CARS_YELLOW, CARS_GREEN -}; - -enum BSP_PedsSignal { - PEDS_BLANK, PEDS_DONT_WALK, PEDS_WALK -}; - -// various other constants for the application... -enum { - BSP_TICKS_PER_SEC = 100, // number of system clock ticks in one second - LED_L = 13, // the pin number of the on-board LED (L) - CARS_GREEN_MIN_TOUT = (BSP_TICKS_PER_SEC * 8U), - CARS_YELLOW_TOUT = (BSP_TICKS_PER_SEC * 3U), - PEDS_WALK_TOUT = (BSP_TICKS_PER_SEC * 3U), - PEDS_FLASH_TOUT = (BSP_TICKS_PER_SEC / 5U), - PEDS_FLASH_NUM = (5U * 2U), - OFF_FLASH_TOUT = (BSP_TICKS_PER_SEC / 2U) -}; - -//............................................................................ -void BSP_signalCars(enum BSP_CarsSignal sig) { - switch (sig) { - case CARS_BLANK: - Serial.println(F("Cars: BLANK")); - break; - case CARS_RED: - Serial.println(F("Cars: RED")); - break; - case CARS_YELLOW: - Serial.println(F("Cars: YELLOW")); - break; - case CARS_GREEN: - Serial.println(F("Cars: GREEN")); - break; - } -} -//............................................................................ -void BSP_signalPeds(enum BSP_PedsSignal sig) { - switch (sig) { - case PEDS_BLANK: - Serial.println(F("Peds: BLANK")); - break; - case PEDS_DONT_WALK: - Serial.println(F("Peds: DON'T WALK")); - break; - case PEDS_WALK: - Serial.println(F("Peds: WALK")); - break; - } -} -//............................................................................ -void BSP_showState(char const *state) { - Serial.print(F("State->")); - Serial.println(state); -} - -//............................................................................ -void setup() { - // initialize the QF-nano framework - QF_init(Q_DIM(QF_active)); - - // initialize all AOs... - QActive_ctor(&AO_Pelican.super, Q_STATE_CAST(&Pelican_initial)); - - // initialize the hardware used in this sketch... - pinMode(LED_L, OUTPUT); // set the LED-L pin to output - - Serial.begin(115200); // set the highest stanard baud rate of 115200 bps - Serial.print(F("Start, QP-nano: ")); - Serial.print(F(QP_VERSION_STR)); - Serial.println(""); -} -//............................................................................ -void loop() { - QF_run(); // run the QP-nano application -} - -//============================================================================ -// interrupts... -ISR(TIMER2_COMPA_vect) { - QF_tickXISR(0); // process time events for tick rate 0 - - if (Serial.available() > 0) { - switch (Serial.read()) { // read the incoming byte - case 'p': - case 'P': - QACTIVE_POST_ISR((QMActive *)&AO_Pelican, PEDS_WAITING_SIG, 0U); - break; - case 'o': - case 'O': - QACTIVE_POST_ISR((QMActive *)&AO_Pelican, ON_SIG, 0U); - break; - case 'f': - case 'F': - QACTIVE_POST_ISR((QMActive *)&AO_Pelican, OFF_SIG, 0U); - break; - } - } -} - -//============================================================================ -// QP-nano callbacks... -void QF_onStartup(void) { - // set Timer2 in CTC mode, 1/1024 prescaler, start the timer ticking... - TCCR2A = (1U << WGM21) | (0U << WGM20); - TCCR2B = (1U << CS22 ) | (1U << CS21) | (1U << CS20); // 1/2^10 - ASSR &= ~(1U << AS2); - TIMSK2 = (1U << OCIE2A); // enable TIMER2 compare Interrupt - TCNT2 = 0U; - - // set the output-compare register based on the desired tick frequency - OCR2A = (F_CPU / BSP_TICKS_PER_SEC / 1024U) - 1U; -} -//............................................................................ -void QV_onIdle(void) { // called with interrupts DISABLED - // Put the CPU and peripherals to the low-power mode. You might - // need to customize the clock management for your application, - // see the datasheet for your particular AVR MCU. - SMCR = (0 << SM0) | (1 << SE); // idle mode, adjust to your project - QV_CPU_SLEEP(); // atomically go to sleep and enable interrupts -} -//............................................................................ -Q_NORETURN Q_onAssert(char const Q_ROM * const module, int location) { - // implement the error-handling policy for your application!!! - (void)module; - (void)location; - QF_INT_DISABLE(); // disable all interrupts - QF_RESET(); // reset the CPU - for (;;) { - } -} - -//============================================================================ -// define all AO classes (state machines)... -/*.$skip${QP_VERSION} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv*/ -/*. Check for the minimum required QP version */ -#if (QP_VERSION < 690U) || (QP_VERSION != ((QP_RELEASE^4294967295U) % 0x3E8U)) -#error qpn version 6.9.0 or higher required -#endif -/*.$endskip${QP_VERSION} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ -/*.$define${AOs::Pelican} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv*/ -/*.${AOs::Pelican} .........................................................*/ -/*.${AOs::Pelican::SM} .....................................................*/ -static QState Pelican_initial(Pelican * const me) { - /*.${AOs::Pelican::SM::initial} */ - return Q_TRAN(&Pelican_operational); -} -/*.${AOs::Pelican::SM::operational} ........................................*/ -static QState Pelican_operational(Pelican * const me) { - QState status_; - switch (Q_SIG(me)) { - /*.${AOs::Pelican::SM::operational} */ - case Q_ENTRY_SIG: { - BSP_signalCars(CARS_RED); - BSP_signalPeds(PEDS_DONT_WALK); - status_ = Q_HANDLED(); - break; - } - /*.${AOs::Pelican::SM::operational::initial} */ - case Q_INIT_SIG: { - status_ = Q_TRAN(&Pelican_carsEnabled); - break; - } - /*.${AOs::Pelican::SM::operational::OFF} */ - case OFF_SIG: { - status_ = Q_TRAN(&Pelican_offline); - break; - } - default: { - status_ = Q_SUPER(&QHsm_top); - break; - } - } - return status_; -} -/*.${AOs::Pelican::SM::operational::carsEnabled} ...........................*/ -static QState Pelican_carsEnabled(Pelican * const me) { - QState status_; - switch (Q_SIG(me)) { - /*.${AOs::Pelican::SM::operational::carsEnabled} */ - case Q_EXIT_SIG: { - BSP_signalCars(CARS_RED); - status_ = Q_HANDLED(); - break; - } - /*.${AOs::Pelican::SM::operational::carsEnabled::initial} */ - case Q_INIT_SIG: { - status_ = Q_TRAN(&Pelican_carsGreen); - break; - } - default: { - status_ = Q_SUPER(&Pelican_operational); - break; - } - } - return status_; -} -/*.${AOs::Pelican::SM::operational::carsEnabled::carsGreen} ................*/ -static QState Pelican_carsGreen(Pelican * const me) { - QState status_; - switch (Q_SIG(me)) { - /*.${AOs::Pelican::SM::operational::carsEnabled::carsGreen} */ - case Q_ENTRY_SIG: { - BSP_signalCars(CARS_GREEN); - /* one-shot timeout in CARS_GREEN_MIN_TOUT ticks */ - QActive_armX(&me->super, 0U, CARS_GREEN_MIN_TOUT, 0U); - status_ = Q_HANDLED(); - break; - } - /*.${AOs::Pelican::SM::operational::carsEnabled::carsGreen} */ - case Q_EXIT_SIG: { - QActive_disarmX(&me->super, 0U); - status_ = Q_HANDLED(); - break; - } - /*.${AOs::Pelican::SM::operational::carsEnabled::carsGreen::initial} */ - case Q_INIT_SIG: { - status_ = Q_TRAN(&Pelican_carsGreenNoPed); - break; - } - default: { - status_ = Q_SUPER(&Pelican_carsEnabled); - break; - } - } - return status_; -} -/*.${AOs::Pelican::SM::operational::carsEnabled::carsGreen::carsGreenNoPed} */ -static QState Pelican_carsGreenNoPed(Pelican * const me) { - QState status_; - switch (Q_SIG(me)) { - /*.${AOs::Pelican::SM::operational::carsEnabled::carsGreen::carsGreenNoPed} */ - case Q_ENTRY_SIG: { - BSP_showState("carsGreenNoPed"); - status_ = Q_HANDLED(); - break; - } - /*.${AOs::Pelican::SM::operational::carsEnabled::carsGreen::carsGreenNoPed::PEDS_WAITING} */ - case PEDS_WAITING_SIG: { - status_ = Q_TRAN(&Pelican_carsGreenPedWait); - break; - } - /*.${AOs::Pelican::SM::operational::carsEnabled::carsGreen::carsGreenNoPed::Q_TIMEOUT} */ - case Q_TIMEOUT_SIG: { - status_ = Q_TRAN(&Pelican_carsGreenInt); - break; - } - default: { - status_ = Q_SUPER(&Pelican_carsGreen); - break; - } - } - return status_; -} -/*.${AOs::Pelican::SM::operational::carsEnabled::carsGreen::carsGreenInt} ..*/ -static QState Pelican_carsGreenInt(Pelican * const me) { - QState status_; - switch (Q_SIG(me)) { - /*.${AOs::Pelican::SM::operational::carsEnabled::carsGreen::carsGreenInt} */ - case Q_ENTRY_SIG: { - BSP_showState("carsGreenInt"); - status_ = Q_HANDLED(); - break; - } - /*.${AOs::Pelican::SM::operational::carsEnabled::carsGreen::carsGreenInt::PEDS_WAITING} */ - case PEDS_WAITING_SIG: { - status_ = Q_TRAN(&Pelican_carsYellow); - break; - } - default: { - status_ = Q_SUPER(&Pelican_carsGreen); - break; - } - } - return status_; -} -/*.${AOs::Pelican::SM::operational::carsEnabled::carsGreen::carsGreenPedWait} */ -static QState Pelican_carsGreenPedWait(Pelican * const me) { - QState status_; - switch (Q_SIG(me)) { - /*.${AOs::Pelican::SM::operational::carsEnabled::carsGreen::carsGreenPedWait} */ - case Q_ENTRY_SIG: { - BSP_showState("carsGreenPedWait"); - status_ = Q_HANDLED(); - break; - } - /*.${AOs::Pelican::SM::operational::carsEnabled::carsGreen::carsGreenPedWait::Q_TIMEOUT} */ - case Q_TIMEOUT_SIG: { - status_ = Q_TRAN(&Pelican_carsYellow); - break; - } - default: { - status_ = Q_SUPER(&Pelican_carsGreen); - break; - } - } - return status_; -} -/*.${AOs::Pelican::SM::operational::carsEnabled::carsYellow} ...............*/ -static QState Pelican_carsYellow(Pelican * const me) { - QState status_; - switch (Q_SIG(me)) { - /*.${AOs::Pelican::SM::operational::carsEnabled::carsYellow} */ - case Q_ENTRY_SIG: { - BSP_showState("carsYellow"); - BSP_signalCars(CARS_YELLOW); - - /* one-shot timeout in CARS_YELLOW_TOUT ticks */ - QActive_armX(&me->super, 0U, CARS_YELLOW_TOUT, 0U); - status_ = Q_HANDLED(); - break; - } - /*.${AOs::Pelican::SM::operational::carsEnabled::carsYellow} */ - case Q_EXIT_SIG: { - QActive_disarmX(&me->super, 0U); - status_ = Q_HANDLED(); - break; - } - /*.${AOs::Pelican::SM::operational::carsEnabled::carsYellow::Q_TIMEOUT} */ - case Q_TIMEOUT_SIG: { - status_ = Q_TRAN(&Pelican_pedsEnabled); - break; - } - default: { - status_ = Q_SUPER(&Pelican_carsEnabled); - break; - } - } - return status_; -} -/*.${AOs::Pelican::SM::operational::pedsEnabled} ...........................*/ -static QState Pelican_pedsEnabled(Pelican * const me) { - QState status_; - switch (Q_SIG(me)) { - /*.${AOs::Pelican::SM::operational::pedsEnabled} */ - case Q_EXIT_SIG: { - BSP_signalPeds(PEDS_DONT_WALK); - status_ = Q_HANDLED(); - break; - } - /*.${AOs::Pelican::SM::operational::pedsEnabled::initial} */ - case Q_INIT_SIG: { - status_ = Q_TRAN(&Pelican_pedsWalk); - break; - } - default: { - status_ = Q_SUPER(&Pelican_operational); - break; - } - } - return status_; -} -/*.${AOs::Pelican::SM::operational::pedsEnabled::pedsWalk} .................*/ -static QState Pelican_pedsWalk(Pelican * const me) { - QState status_; - switch (Q_SIG(me)) { - /*.${AOs::Pelican::SM::operational::pedsEnabled::pedsWalk} */ - case Q_ENTRY_SIG: { - BSP_showState("pedsWalk"); - BSP_signalPeds(PEDS_WALK); - /* one-shot timeout in PEDS_WALK_TOUT ticks */ - QActive_armX(&me->super, 0U, PEDS_WALK_TOUT, 0U); - status_ = Q_HANDLED(); - break; - } - /*.${AOs::Pelican::SM::operational::pedsEnabled::pedsWalk} */ - case Q_EXIT_SIG: { - QActive_disarmX(&me->super, 0U); - status_ = Q_HANDLED(); - break; - } - /*.${AOs::Pelican::SM::operational::pedsEnabled::pedsWalk::Q_TIMEOUT} */ - case Q_TIMEOUT_SIG: { - status_ = Q_TRAN(&Pelican_pedsFlash); - break; - } - default: { - status_ = Q_SUPER(&Pelican_pedsEnabled); - break; - } - } - return status_; -} -/*.${AOs::Pelican::SM::operational::pedsEnabled::pedsFlash} ................*/ -static QState Pelican_pedsFlash(Pelican * const me) { - QState status_; - switch (Q_SIG(me)) { - /*.${AOs::Pelican::SM::operational::pedsEnabled::pedsFlash} */ - case Q_ENTRY_SIG: { - BSP_showState("pedsFlash"); - /* periodic timeout in PEDS_FLASH_TOUT and every PEDS_FLASH_TOUT ticks */ - QActive_armX(&me->super, 0U, PEDS_FLASH_TOUT, PEDS_FLASH_TOUT); - me->flashCtr = (PEDS_FLASH_NUM * 2U) + 1U; - status_ = Q_HANDLED(); - break; - } - /*.${AOs::Pelican::SM::operational::pedsEnabled::pedsFlash} */ - case Q_EXIT_SIG: { - QActive_disarmX(&me->super, 0U); - status_ = Q_HANDLED(); - break; - } - /*.${AOs::Pelican::SM::operational::pedsEnabled::pedsFlash::Q_TIMEOUT} */ - case Q_TIMEOUT_SIG: { - /*.${AOs::Pelican::SM::operational::pedsEnabled::pedsFlash::Q_TIMEOUT::[me->flashCtr!=0U]} */ - if (me->flashCtr != 0U) { - --me->flashCtr; - /*.${AOs::Pelican::SM::operational::pedsEnabled::pedsFlash::Q_TIMEOUT::[me->flashCtr!=0~::[(me->flashCtr&1U)==0U]} */ - if ((me->flashCtr & 1U) == 0U) { - BSP_signalPeds(PEDS_DONT_WALK); - status_ = Q_HANDLED(); - } - /*.${AOs::Pelican::SM::operational::pedsEnabled::pedsFlash::Q_TIMEOUT::[me->flashCtr!=0~::[else]} */ - else { - BSP_signalPeds(PEDS_BLANK); - status_ = Q_HANDLED(); - } - } - /*.${AOs::Pelican::SM::operational::pedsEnabled::pedsFlash::Q_TIMEOUT::[else]} */ - else { - status_ = Q_TRAN(&Pelican_carsEnabled); - } - break; - } - default: { - status_ = Q_SUPER(&Pelican_pedsEnabled); - break; - } - } - return status_; -} -/*.${AOs::Pelican::SM::offline} ............................................*/ -static QState Pelican_offline(Pelican * const me) { - QState status_; - switch (Q_SIG(me)) { - /*.${AOs::Pelican::SM::offline} */ - case Q_ENTRY_SIG: { - BSP_showState("offline"); - /* periodic timeout in OFF_FLASH_TOUT and every OFF_FLASH_TOUT ticks */ - QActive_armX(&me->super, 0U, OFF_FLASH_TOUT, OFF_FLASH_TOUT); - me->flashCtr = 0U; - status_ = Q_HANDLED(); - break; - } - /*.${AOs::Pelican::SM::offline} */ - case Q_EXIT_SIG: { - QActive_disarmX(&me->super, 0U); - status_ = Q_HANDLED(); - break; - } - /*.${AOs::Pelican::SM::offline::Q_TIMEOUT} */ - case Q_TIMEOUT_SIG: { - me->flashCtr ^= 1U; - /*.${AOs::Pelican::SM::offline::Q_TIMEOUT::[(me->flashCtr&1U)==0U]} */ - if ((me->flashCtr & 1U) == 0U) { - BSP_signalCars(CARS_RED); - BSP_signalPeds(PEDS_DONT_WALK); - status_ = Q_HANDLED(); - } - /*.${AOs::Pelican::SM::offline::Q_TIMEOUT::[else]} */ - else { - BSP_signalCars(CARS_BLANK); - BSP_signalPeds(PEDS_BLANK); - status_ = Q_HANDLED(); - } - break; - } - /*.${AOs::Pelican::SM::offline::ON} */ - case ON_SIG: { - status_ = Q_TRAN(&Pelican_operational); - break; - } - default: { - status_ = Q_SUPER(&QHsm_top); - break; - } - } - return status_; -} -/*.$enddef${AOs::Pelican} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ -//... diff --git a/libraries/qpn_avr/examples/pelican/pelican.qm b/libraries/qpn_avr/examples/pelican/pelican.qm deleted file mode 100644 index 17f06c8..0000000 --- a/libraries/qpn_avr/examples/pelican/pelican.qm +++ /dev/null @@ -1,401 +0,0 @@ - - - This is the PEdestrian LIght CONtrolled (PELICAN) crossing example for the Arduino-UNO board. The example demonstrates: - -1. One active object class "Pelican" (inside the package "AOs") -2. A non-trivial Hierarchical State Machine (HSM) -3. The concept of a Board Support Pakage (BSP) - - - - PEdestrian LIght CONtrolled (PELICAN) crossing - - - - - - - - - BSP_signalCars(CARS_RED); -BSP_signalPeds(PEDS_DONT_WALK); - - - - - - - - - - - - BSP_signalCars(CARS_RED); - - - - - - - BSP_signalCars(CARS_GREEN); -/* one-shot timeout in CARS_GREEN_MIN_TOUT ticks */ -QActive_armX(&me->super, 0U, CARS_GREEN_MIN_TOUT, 0U); - QActive_disarmX(&me->super, 0U); - - - - - - - BSP_showState("carsGreenNoPed"); - - - - - - - - - - - - - - - - BSP_showState("carsGreenInt"); - - - - - - - - - - - BSP_showState("carsGreenPedWait"); - - - - - - - - - - - - - - - - BSP_showState("carsYellow"); -BSP_signalCars(CARS_YELLOW); - -/* one-shot timeout in CARS_YELLOW_TOUT ticks */ -QActive_armX(&me->super, 0U, CARS_YELLOW_TOUT, 0U); - QActive_disarmX(&me->super, 0U); - - - - - - - - - - - - - - - - BSP_signalPeds(PEDS_DONT_WALK); - - - - - - - BSP_showState("pedsWalk"); -BSP_signalPeds(PEDS_WALK); -/* one-shot timeout in PEDS_WALK_TOUT ticks */ -QActive_armX(&me->super, 0U, PEDS_WALK_TOUT, 0U); - QActive_disarmX(&me->super, 0U); - - - - - - - - - - - - BSP_showState("pedsFlash"); -/* periodic timeout in PEDS_FLASH_TOUT and every PEDS_FLASH_TOUT ticks */ -QActive_armX(&me->super, 0U, PEDS_FLASH_TOUT, PEDS_FLASH_TOUT); -me->flashCtr = (PEDS_FLASH_NUM * 2U) + 1U; - QActive_disarmX(&me->super, 0U); - - - me->flashCtr != 0U - --me->flashCtr; - - (me->flashCtr & 1U) == 0U - BSP_signalPeds(PEDS_DONT_WALK); - - - - - - else - BSP_signalPeds(PEDS_BLANK); - - - - - - - - - - else - - - - - - - - - - - - - - - - - - - - - - - BSP_showState("offline"); -/* periodic timeout in OFF_FLASH_TOUT and every OFF_FLASH_TOUT ticks */ -QActive_armX(&me->super, 0U, OFF_FLASH_TOUT, OFF_FLASH_TOUT); -me->flashCtr = 0U; - QActive_disarmX(&me->super, 0U); - - me->flashCtr ^= 1U; - - (me->flashCtr & 1U) == 0U - BSP_signalCars(CARS_RED); -BSP_signalPeds(PEDS_DONT_WALK); - - - - - - else - BSP_signalCars(CARS_BLANK); -BSP_signalPeds(PEDS_BLANK); - - - - - - - - - - - - - - - - - - - - - - - - - #include "qpn.h" // QP-nano framework -#include "Arduino.h" // Main include file for the Arduino SDK - -//============================================================================ -// events used in this application... -enum PelicanSignals { - PEDS_WAITING_SIG = Q_USER_SIG, // PEDestrians-waiting button press event - OFF_SIG, // OFF-button press event - ON_SIG // ON-button press event -}; - -//============================================================================ -// declare all AO classes... -$declare${AOs::Pelican} -//... - -// define all AO instances and event queue buffers for them... -Pelican AO_Pelican; -static QEvt l_pelicanQSto[10]; // Event queue storage for Pelican -//... - -//============================================================================ -// QF_active[] array defines all active object control blocks ---------------- -QActiveCB const Q_ROM QF_active[] = { - { (QActive *)0, (QEvt *)0, 0U }, - { (QActive *)&AO_Pelican, l_pelicanQSto, Q_DIM(l_pelicanQSto) } -}; - -//============================================================================ -// Board Support Package (BSP) -enum BSP_CarsSignal { // street signals ... - CARS_BLANK, CARS_RED, CARS_YELLOW, CARS_GREEN -}; - -enum BSP_PedsSignal { - PEDS_BLANK, PEDS_DONT_WALK, PEDS_WALK -}; - -// various other constants for the application... -enum { - BSP_TICKS_PER_SEC = 100, // number of system clock ticks in one second - LED_L = 13, // the pin number of the on-board LED (L) - CARS_GREEN_MIN_TOUT = (BSP_TICKS_PER_SEC * 8U), - CARS_YELLOW_TOUT = (BSP_TICKS_PER_SEC * 3U), - PEDS_WALK_TOUT = (BSP_TICKS_PER_SEC * 3U), - PEDS_FLASH_TOUT = (BSP_TICKS_PER_SEC / 5U), - PEDS_FLASH_NUM = (5U * 2U), - OFF_FLASH_TOUT = (BSP_TICKS_PER_SEC / 2U) -}; - -//............................................................................ -void BSP_signalCars(enum BSP_CarsSignal sig) { - switch (sig) { - case CARS_BLANK: - Serial.println(F("Cars: BLANK")); - break; - case CARS_RED: - Serial.println(F("Cars: RED")); - break; - case CARS_YELLOW: - Serial.println(F("Cars: YELLOW")); - break; - case CARS_GREEN: - Serial.println(F("Cars: GREEN")); - break; - } -} -//............................................................................ -void BSP_signalPeds(enum BSP_PedsSignal sig) { - switch (sig) { - case PEDS_BLANK: - Serial.println(F("Peds: BLANK")); - break; - case PEDS_DONT_WALK: - Serial.println(F("Peds: DON'T WALK")); - break; - case PEDS_WALK: - Serial.println(F("Peds: WALK")); - break; - } -} -//............................................................................ -void BSP_showState(char const *state) { - Serial.print(F("State->")); - Serial.println(state); -} - -//............................................................................ -void setup() { - // initialize the QF-nano framework - QF_init(Q_DIM(QF_active)); - - // initialize all AOs... - QActive_ctor(&AO_Pelican.super, Q_STATE_CAST(&Pelican_initial)); - - // initialize the hardware used in this sketch... - pinMode(LED_L, OUTPUT); // set the LED-L pin to output - - Serial.begin(115200); // set the highest stanard baud rate of 115200 bps - Serial.print(F("Start, QP-nano: ")); - Serial.print(F(QP_VERSION_STR)); - Serial.println(""); -} -//............................................................................ -void loop() { - QF_run(); // run the QP-nano application -} - -//============================================================================ -// interrupts... -ISR(TIMER2_COMPA_vect) { - QF_tickXISR(0); // process time events for tick rate 0 - - if (Serial.available() > 0) { - switch (Serial.read()) { // read the incoming byte - case 'p': - case 'P': - QACTIVE_POST_ISR((QMActive *)&AO_Pelican, PEDS_WAITING_SIG, 0U); - break; - case 'o': - case 'O': - QACTIVE_POST_ISR((QMActive *)&AO_Pelican, ON_SIG, 0U); - break; - case 'f': - case 'F': - QACTIVE_POST_ISR((QMActive *)&AO_Pelican, OFF_SIG, 0U); - break; - } - } -} - -//============================================================================ -// QP-nano callbacks... -void QF_onStartup(void) { - // set Timer2 in CTC mode, 1/1024 prescaler, start the timer ticking... - TCCR2A = (1U << WGM21) | (0U << WGM20); - TCCR2B = (1U << CS22 ) | (1U << CS21) | (1U << CS20); // 1/2^10 - ASSR &= ~(1U << AS2); - TIMSK2 = (1U << OCIE2A); // enable TIMER2 compare Interrupt - TCNT2 = 0U; - - // set the output-compare register based on the desired tick frequency - OCR2A = (F_CPU / BSP_TICKS_PER_SEC / 1024U) - 1U; -} -//............................................................................ -void QV_onIdle(void) { // called with interrupts DISABLED - // Put the CPU and peripherals to the low-power mode. You might - // need to customize the clock management for your application, - // see the datasheet for your particular AVR MCU. - SMCR = (0 << SM0) | (1 << SE); // idle mode, adjust to your project - QV_CPU_SLEEP(); // atomically go to sleep and enable interrupts -} -//............................................................................ -Q_NORETURN Q_onAssert(char const Q_ROM * const module, int location) { - // implement the error-handling policy for your application!!! - (void)module; - (void)location; - QF_INT_DISABLE(); // disable all interrupts - QF_RESET(); // reset the CPU - for (;;) { - } -} - -//============================================================================ -// define all AO classes (state machines)... -$define${AOs::Pelican} -//... - - - - diff --git a/libraries/qpn_avr/library.properties b/libraries/qpn_avr/library.properties deleted file mode 100644 index 7df6996..0000000 --- a/libraries/qpn_avr/library.properties +++ /dev/null @@ -1,11 +0,0 @@ -name=QP-nano -version=6.9.0 -author=Quantum Leaps -maintainer=Quantum Leaps -sentence=QP-nano Real-Time Embedded Framework for Arduino. -paragraph=The QP-nano framework is like a modern real-time operating system (RTOS) specifically designed for executing event-driven, encapsulated state machines (Active Objects). It enables you to build responsive, robust, and truly concurrent Arduino programs. -category=Device Control -url=https://www.state-machine.com/arduino/ -architectures=avr -includes=qpn.h - diff --git a/libraries/qpn_avr/src/qassert.h b/libraries/qpn_avr/src/qassert.h deleted file mode 100644 index 88fd73f..0000000 --- a/libraries/qpn_avr/src/qassert.h +++ /dev/null @@ -1,345 +0,0 @@ -/** -* @file -* @brief Customizable and memory-efficient assertions for embedded systems -* @cond -****************************************************************************** -* Last updated for version 6.8.0 -* Last updated on 2020-01-21 -* -* Q u a n t u m L e a P s -* ------------------------ -* Modern Embedded Software -* -* Copyright (C) 2005-2020 Quantum Leaps, LLC. All rights reserved. -* -* This program is open source software: you can redistribute it and/or -* modify it under the terms of the GNU General Public License as published -* by the Free Software Foundation, either version 3 of the License, or -* (at your option) any later version. -* -* Alternatively, this program may be distributed and modified under the -* terms of Quantum Leaps commercial licenses, which expressly supersede -* the GNU General Public License and are specifically designed for -* licensees interested in retaining the proprietary status of their code. -* -* This program is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU General Public License for more details. -* -* You should have received a copy of the GNU General Public License -* along with this program. If not, see . -* -* Contact information: -* -* -****************************************************************************** -* @endcond -*/ -#ifndef QASSERT_H -#define QASSERT_H - -/** -* @note -* This header file can be used in C, C++, and mixed C/C++ programs. -* -* @note The preprocessor switch #Q_NASSERT disables checking assertions. -* However, it is generally __not__ advisable to disable assertions, -* __especially__ in the production code. Instead, the assertion handler -* Q_onAssert() should be very carefully designed and tested. -*/ - -#ifdef Q_NASSERT /* Q_NASSERT defined--assertion checking disabled */ - - /* provide dummy (empty) definitions that don't generate any code... */ - #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 */ - -#ifndef QP_VERSION /* is quassert.h used outside QP? */ - - /* provide typedefs so that qassert.h could be used "standalone"... */ - - /*! typedef for character strings. */ - /** - * @description - * 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 assertions-ids and line numbers in assertions. */ - /** - * @description - * This typedef specifies integer type for exclusive use in assertions. - * Use of this type, rather than plain 'int', is in compliance - * with the MISRA-C 2004 Rules 6.1(req), 6.3(adv). - */ - typedef int int_t; - - #ifndef Q_ROM /* if NOT defined, provide the default definition */ - /*! macro for accessing data in ROM */ - #define Q_ROM - #endif - -#endif - - /*! Define the file name (with `__FILE__`) for assertions in this file. */ - /** - * @description - * Macro to be placed at the top of each C/C++ module to define the - * single instance of the file name string to be used in reporting - * assertions in this module. - * - * @note The file name string literal is defined by means of the standard - * preprocessor macro `__FILE__`. However, please note that, depending - * on the compiler, the `__FILE__` macro might contain the whole path name - * to the file, which might be inconvenient to log assertions. - * @note This macro should __not__ be terminated by a semicolon. - * @sa Q_DEFINE_THIS_MODULE() - */ - #define Q_DEFINE_THIS_FILE \ - static char_t const Q_ROM Q_this_module_[] = __FILE__; - - /*! Define the user-specified module name for assertions in this file. */ - /** - * @description - * Macro to be placed at the top of each C/C++ module to define the - * single instance of the module name string to be used in reporting - * assertions in this module. This macro takes the user-supplied parameter - * @p name_ instead of `__FILE__` to precisely control the name of the - * module. - * - * @param[in] name_ string constant representing the module name - * - * @note This macro should __not__ be terminated by a semicolon. - */ - #define Q_DEFINE_THIS_MODULE(name_) \ - static char_t const Q_ROM Q_this_module_[] = name_; - - /*! General purpose assertion. */ - /** - * @description - * Makes sure the @p test_ parameter is TRUE. Calls the Q_onAssert() - * callback if the @p test_ expression evaluates to FALSE. This - * macro identifies the assertion location within the file by means - * of the standard `__LINE__` macro. - * - * @param[in] test_ Boolean expression - * - * @note the @p test_ is __not__ evaluated if assertions are disabled - * with the #Q_NASSERT switch. - */ - #define Q_ASSERT(test_) ((test_) \ - ? (void)0 : Q_onAssert(&Q_this_module_[0], (int_t)__LINE__)) - - /*! General purpose assertion with user-specified assertion-id. */ - /** - * @description - * Makes sure the @p test_ parameter is TRUE. Calls the Q_onAssert() - * callback if the @p test_ evaluates to FALSE. This assertion takes the - * user-supplied parameter @p id_ to identify the location of this - * assertion within the file. This avoids the volatility of using line - * numbers, which change whenever a line of code is added or removed - * upstream from the assertion. - * - * @param[in] id_ ID number (unique within the module) of the assertion - * @param[in] test_ Boolean expression - * - * @note the @p test_ expression is __not__ evaluated if assertions are - * disabled with the #Q_NASSERT switch. - */ - #define Q_ASSERT_ID(id_, test_) ((test_) \ - ? (void)0 : Q_onAssert(&Q_this_module_[0], (int_t)(id_))) - - /*! General purpose assertion that __always__ evaluates the @p test_ - * expression. */ - /** - * @description - * Like the Q_ASSERT() macro, except it __always__ evaluates the @p test_ - * expression even when assertions are disabled with the #Q_NASSERT macro. - * However, when the #Q_NASSERT macro is defined, the Q_onAssert() - * callback is __not__ called, even if @p test_ evaluates to FALSE. - * - * @param[in] test_ Boolean expression (__always__ evaluated) - * - * @sa #Q_ALLEGE_ID - */ - #define Q_ALLEGE(test_) Q_ASSERT(test_) - - /*! General purpose assertion with user-specified assertion-id that - * __always__ evaluates the @p test_ expression. */ - /** - * @description - * Like the Q_ASSERT_ID() macro, except it __always__ evaluates the - * @p test_ expression even when assertions are disabled with the - * #Q_NASSERT macro. However, when the #Q_NASSERT macro is defined, the - * Q_onAssert() callback is __not__ called, even if @p test_ evaluates - * to FALSE. - * - * @param[in] id_ ID number (unique within the module) of the assertion - * @param[in] test_ Boolean expression - */ - #define Q_ALLEGE_ID(id_, test_) Q_ASSERT_ID((id_), (test_)) - - /*! Assertion for a wrong path through the code. */ - /** - * @description - * Calls the Q_onAssert() callback if ever executed. - * - * @note Does noting if assertions are disabled with the #Q_NASSERT switch. - */ - #define Q_ERROR() \ - Q_onAssert(&Q_this_module_[0], (int_t)__LINE__) - - /*! Assertion with user-specified assertion-id for a wrong path. */ - /** - * @description - * Calls the Q_onAssert() callback if ever executed. This assertion - * takes the user-supplied parameter @p id_ to identify the location of - * this assertion within the file. This avoids the volatility of using - * line numbers, which change whenever a line of code is added or removed - * upstream from the assertion. - * - * @param[in] id_ ID number (unique within the module) of the assertion - * - * @note Does noting if assertions are disabled with the #Q_NASSERT switch. - */ - #define Q_ERROR_ID(id_) \ - Q_onAssert(&Q_this_module_[0], (int_t)(id_)) - -#endif /* Q_NASSERT */ - -/****************************************************************************/ -#ifdef __cplusplus - extern "C" { -#endif - -#ifndef Q_NORETURN - /*! no-return function specifier */ - #define Q_NORETURN void -#endif /* Q_NORETURN */ - -/*! Callback function invoked in case of any assertion failure. */ -/** -* @description -* This is an application-specific callback function needs to be defined in -* the application to perform the clean system shutdown and perhaps a reset. -* -* @param[in] module name of the file/module in which the assertion failed -* (constant, zero-terminated C string) -* @param[in] location location of the assertion within the module. This could -* be a line number or a user-specified ID-number. -* -* @note This callback function should _not_ return, as continuation after -* an assertion failure does not make sense. -* -* @note The Q_onAssert() function is the last line of defense after the -* system failure and its implementation shouild be very __carefully__ -* designed and __tested__ under various fault conditions, including but -* not limited to: stack overflow, stack corruption, or calling Q_onAssert() -* from an interrupt. -* -* @note It is typically a __bad idea__ to implement Q_onAssert() as an -* endless loop that ties up the CPU. During debuggin, Q_onAssert() is an -* ideal place to put a breakpoint. -* -* Called by the following macros: #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. -*/ -Q_NORETURN Q_onAssert(char_t const Q_ROM * const module, int_t const location); - -#ifdef __cplusplus - } -#endif - -/*! Assertion for checking preconditions. */ -/** -* @description -* This macro is equivalent to #Q_ASSERT, except the name provides a better -* documentation of the intention of this assertion. -* -* @param[in] test_ Boolean expression -*/ -#define Q_REQUIRE(test_) Q_ASSERT(test_) - -/*! Assertion for checking preconditions with user-specified assertion-id. */ -/** -* @description -* Equivalent to #Q_ASSERT_ID, except the macro name provides a better -* documentation of the intention of this assertion. -* -* @param[in] id_ ID number (unique within the module) of the assertion -* @param[in] test_ Boolean expression -*/ -#define Q_REQUIRE_ID(id_, test_) Q_ASSERT_ID((id_), (test_)) - -/*! Assertion for checking postconditions. */ -/** Equivalent to #Q_ASSERT, except the macro name provides a better -* documentation of the intention of this assertion. -* -* @param[in] test_ Boolean expression -*/ -#define Q_ENSURE(test_) Q_ASSERT(test_) - -/*! Assertion for checking postconditions with user-specified assertion-id. */ -/** -* @description -* Equivalent to #Q_ASSERT_ID, except the name provides a better documentation -* of the intention of this assertion. -* -* @param[in] id_ ID number (unique within the module) of the assertion -* @param[in] test_ Boolean expression -*/ -#define Q_ENSURE_ID(id_, test_) Q_ASSERT_ID((id_), (test_)) - -/*! Assertion for checking invariants. */ -/** -* @description -* Equivalent to #Q_ASSERT, except the macro name provides a better -* documentation of the intention of this assertion. -* -* @param[in] test_ Boolean expression -*/ -#define Q_INVARIANT(test_) Q_ASSERT(test_) - -/*! Assertion for checking invariants with user-specified assertion-id. */ -/** -* @description -* Equivalent to #Q_ASSERT_ID, except the macro name provides a better -* documentation of the intention of this assertion. -* -* @param[in] id_ ID number (unique within the module) of the assertion -* @param[in] test_ Boolean expression -*/ -#define Q_INVARIANT_ID(id_, test_) Q_ASSERT_ID((id_), (test_)) - -/*! Static (compile-time) assertion. */ -/** -* @description -* This type of assertion deliberately causes a compile-time error when -* the @p test_ evaluates to FALSE. The macro exploits the fact that in C/C++ -* a dimension of an array cannot be negative. The compile-time assertion has -* no runtime side effects. -* -* @param[in] test_ Compile-time Boolean expression -*/ -#define Q_ASSERT_STATIC(test_) \ - extern int_t Q_assert_static[(test_) ? 1 : -1] - -#define Q_ASSERT_COMPILE(test_) Q_ASSERT_STATIC(test_) - -/*! Helper macro to calculate static dimension of a 1-dim @p array_ */ -#define Q_DIM(array_) (sizeof(array_) / sizeof((array_)[0])) - -#endif /* QASSERT_H */ - diff --git a/libraries/qpn_avr/src/qepn.c b/libraries/qpn_avr/src/qepn.c deleted file mode 100644 index 0d8706e..0000000 --- a/libraries/qpn_avr/src/qepn.c +++ /dev/null @@ -1,480 +0,0 @@ -/** -* @file -* @brief QEP-nano implementation. -* @ingroup qep -* @cond -****************************************************************************** -* Last updated for version 6.8.2 -* Last updated on 2020-03-08 -* -* Q u a n t u m L e a P s -* ------------------------ -* Modern Embedded Software -* -* Copyright (C) 2005-2020 Quantum Leaps, LLC. All rights reserved. -* -* This program is open source software: you can redistribute it and/or -* modify it under the terms of the GNU General Public License as published -* by the Free Software Foundation, either version 3 of the License, or -* (at your option) any later version. -* -* Alternatively, this program may be distributed and modified under the -* terms of Quantum Leaps commercial licenses, which expressly supersede -* the GNU General Public License and are specifically designed for -* licensees interested in retaining the proprietary status of their code. -* -* This program is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU General Public License for more details. -* -* You should have received a copy of the GNU General Public License -* along with this program. If not, see . -* -* Contact information: -* -* -****************************************************************************** -* @endcond -*/ -#include "qpn_conf.h" /* QP-nano configuration file (from the application) */ -#include "qfn_port.h" /* QF-nano port from the port directory */ -#include "qassert.h" /* embedded systems-friendly assertions */ - -Q_DEFINE_THIS_MODULE("qepn") - -/****************************************************************************/ -char_t const Q_ROM QP_versionStr[7] = QP_VERSION_STR; - -/****************************************************************************/ -/****************************************************************************/ -/*! empty signal for internal use only */ -#define QEP_EMPTY_SIG_ ((QSignal)0) - -/*! maximum depth of state nesting in HSMs (including the top level), -* must be >= 2 -*/ -#define QHSM_MAX_NEST_DEPTH_ ((int_fast8_t)5) - -/*! helper function to execute a transition chain in HSM */ -static int_fast8_t QHsm_tran_(QHsm * const me, - QStateHandler path[QHSM_MAX_NEST_DEPTH_]); - -/****************************************************************************/ -/** -* @protected @memberof QHsm -* @description -* Performs the first step of HSM initialization by assigning the initial -* pseudostate to the currently active state of the state machine. -* -* @param[in,out] me pointer (see @ref oop) -* @param[in] initial pointer to the top-most initial state-handler -* function in the derived state machine -* @note -* Must be called only by the constructors of the derived state machines. -* -* @note -* Must be called only ONCE before QHSM_INIT(). -* -* @usage -* The following example illustrates how to invoke QHsm_ctor() in the -* "constructor" of a derived state machine: -* @include qepn_qhsm_ctor.c -*/ -void QHsm_ctor(QHsm * const me, QStateHandler initial) { - static QHsmVtable const vtable = { /* QHsm virtual table */ - &QHsm_init_, - &QHsm_dispatch_ - }; - me->vptr = &vtable; - me->state = Q_STATE_CAST(&QHsm_top); - me->temp = initial; -} - -/****************************************************************************/ -/** -* @private @memberof QHsm -* @description -* Executes the top-most initial transition in a HSM. -* -* @param[in,out] me pointer (see @ref oop) -* -* @note -* Must be called only ONCE after the QHsm_ctor(). -*/ -void QHsm_init_(QHsm * const me) { - QStateHandler t = me->state; - QState r; - - /** @pre the virtual pointer must be initialized, the top-most initial - * transition must be initialized, and the initial transition must not - * be taken yet. - */ - Q_REQUIRE_ID(200, (me->vptr != (QHsmVtable const *)0) - && (me->temp != Q_STATE_CAST(0)) - && (t == Q_STATE_CAST(&QHsm_top))); - - r = (*me->temp)(me); /* execute the top-most initial transition */ - - /* the top-most initial transition must be taken */ - Q_ASSERT_ID(210, r == Q_RET_TRAN); - - /* drill down into the state hierarchy with initial transitions... */ - do { - QStateHandler path[QHSM_MAX_NEST_DEPTH_]; - int_fast8_t ip = 0; /* transition entry path index */ - - path[0] = me->temp; - Q_SIG(me) = QEP_EMPTY_SIG_; - (void)(*me->temp)(me); - while (me->temp != t) { - ++ip; - Q_ASSERT_ID(220, ip < (int_fast8_t)Q_DIM(path)); - path[ip] = me->temp; - (void)(*me->temp)(me); - } - me->temp = path[0]; - - /* retrace the entry path in reverse (desired) order... */ - Q_SIG(me) = Q_ENTRY_SIG; - do { - (void)(*path[ip])(me); /* enter path[ip] */ - --ip; - } while (ip >= 0); - - t = path[0]; /* current state becomes the new source */ - - Q_SIG(me) = Q_INIT_SIG; - r = (*t)(me); - } while (r == Q_RET_TRAN); - - me->state = t; /* change the current active state */ - me->temp = t; /* mark the configuration as stable */ -} - -/****************************************************************************/ -/** -* @protected @memberof QHsm -* @description -* QHsm_top() is the ultimate root of state hierarchy in all HSMs derived -* from ::QHsm. -* -* @param[in] me pointer (see @ref oop) -* -* @returns -* Always returns #Q_RET_IGNORED, which means that the top state ignores -* all events. -* -* @note -* The parameter @p me to this state handler is not used. It is provided for -* conformance with the state-handler function signature ::QStateHandler. -*/ -QState QHsm_top(void const * const me) { - (void)me; /* suppress the "unused parameter" compiler warning */ - return Q_RET_IGNORED; /* the top state ignores all events */ -} - -/****************************************************************************/ -/** -* @private @memberof QHsm -* @description -* Dispatches an event for processing to a hierarchical state machine (HSM). -* The processing of an event represents one run-to-completion (RTC) step. -* -* @param[in,out] me pointer (see @ref oop) -* -* @note -* This function should be called only via the virtual table (see -* QHSM_DISPATCH()) and should NOT be called directly in the applications. -*/ -void QHsm_dispatch_(QHsm * const me) { - QStateHandler t = me->state; - QStateHandler s; - QState r; - int_fast8_t iq; /* helper transition entry path index */ - - /** @pre the current state must be initialized and - * the state configuration must be stable - */ - Q_REQUIRE_ID(400, (t != Q_STATE_CAST(0)) - && (t == me->temp)); - - /* process the event hierarchically... */ - do { - s = me->temp; - r = (*s)(me); /* invoke state handler s */ - - if (r == Q_RET_UNHANDLED) { /* unhandled due to a guard? */ - iq = (int_fast8_t)Q_SIG(me); /* save the original signal */ - Q_SIG(me) = QEP_EMPTY_SIG_; /* find the superstate */ - r = (*s)(me); /* invoke state handler s */ - Q_SIG(me) = (QSignal)iq; /* restore the original signal */ - } - } while (r == Q_RET_SUPER); - - /* transition taken? */ - if (r >= Q_RET_TRAN) { - QStateHandler path[QHSM_MAX_NEST_DEPTH_]; /* transition entry path */ - int_fast8_t ip; /* transition entry path index */ - - path[0] = me->temp; /* save the target of the transition */ - path[1] = t; - path[2] = s; - - /* exit current state to transition source s... */ - for (; t != s; t = me->temp) { - Q_SIG(me) = Q_EXIT_SIG; /* find superstate of t */ - - /* take the exit action and check if it was handled? */ - if ((*t)(me) == Q_RET_HANDLED) { - Q_SIG(me) = QEP_EMPTY_SIG_; - (void)(*t)(me); /* find superstate of t */ - } - } - - ip = QHsm_tran_(me, path); /* take the state transition */ - - /* retrace the entry path in reverse (desired) order... */ - Q_SIG(me) = Q_ENTRY_SIG; - for (; ip >= 0; --ip) { - (void)(*path[ip])(me); /* enter path[ip] */ - } - t = path[0]; /* stick the target into register */ - me->temp = t; /* update the current state */ - - /* drill into the target hierarchy... */ - Q_SIG(me) = Q_INIT_SIG; - while ((*t)(me) == Q_RET_TRAN) { - ip = 0; - - path[0] = me->temp; - Q_SIG(me) = QEP_EMPTY_SIG_; - (void)(*me->temp)(me); /* find the superstate */ - while (me->temp != t) { - ++ip; - path[ip] = me->temp; - (void)(*me->temp)(me); /* find the superstate */ - } - me->temp = path[0]; - - /* entry path must not overflow */ - Q_ASSERT_ID(410, ip < QHSM_MAX_NEST_DEPTH_); - - /* retrace the entry path in reverse (correct) order... */ - Q_SIG(me) = Q_ENTRY_SIG; - do { - (void)(*path[ip])(me); /* enter path[ip] */ - --ip; - } while (ip >= 0); - - t = path[0]; - Q_SIG(me) = Q_INIT_SIG; - } - } - - me->state = t; /* change the current active state */ - me->temp = t; /* mark the configuration as stable */ -} - -/****************************************************************************/ -/** -* @private @memberof QHsm -* @description -* Static helper function to execute transition sequence in a hierarchical -* state machine (HSM). -* -* @param[in,out] me pointer (see @ref oop) -* @param[in,out] path array of pointers to state-handler functions -* to execute the entry actions -* @returns -* the depth of the entry path stored in the @p path parameter. -*/ -static int_fast8_t QHsm_tran_(QHsm * const me, - QStateHandler path[QHSM_MAX_NEST_DEPTH_]) -{ - int_fast8_t ip = (int_fast8_t)(-1); /* transition entry path index */ - int_fast8_t iq; /* helper transition entry path index */ - QStateHandler t = path[0]; - QStateHandler s = path[2]; - QState r; - - /* (a) check source==target (transition to self) */ - if (s == t) { - Q_SIG(me) = Q_EXIT_SIG; - (void)(*s)(me); /* exit the source */ - ip = 0; /* enter the target */ - } - else { - Q_SIG(me) = QEP_EMPTY_SIG_; - (void)(*t)(me); /* find superstate of target */ - t = me->temp; - - /* (b) check source==target->super */ - if (s == t) { - ip = 0; /* enter the target */ - } - else { - Q_SIG(me) = QEP_EMPTY_SIG_; - (void)(*s)(me); /* find superstate of source */ - - /* (c) check source->super==target->super */ - if (me->temp == t) { - Q_SIG(me) = Q_EXIT_SIG; - (void)(*s)(me); /* exit the source */ - ip = 0; /* enter the target */ - } - else { - /* (d) check source->super==target */ - if (me->temp == path[0]) { - Q_SIG(me) = Q_EXIT_SIG; - (void)(*s)(me); /* exit the source */ - } - else { - /* (e) check rest of source==target->super->super.. - * and store the entry path along the way - */ - iq = 0; /* indicate that LCA not found */ - ip = 1; /* enter target and its superstate */ - path[1] = t; /* save the superstate of target */ - t = me->temp; /* save source->super */ - - /* find target->super->super... */ - Q_SIG(me) = QEP_EMPTY_SIG_; - r = (*path[1])(me); - while (r == Q_RET_SUPER) { - ++ip; - path[ip] = me->temp; /* store the entry path */ - if (me->temp == s) { /* is it the source? */ - iq = 1; /* indicate that LCA found */ - - /* entry path must not overflow */ - Q_ASSERT_ID(510, ip < QHSM_MAX_NEST_DEPTH_); - --ip; /* do not enter the source */ - r = Q_RET_HANDLED; /* terminate loop */ - } - /* it is not the source, keep going up */ - else { - r = (*me->temp)(me); /* superstate of t */ - } - } - - /* the LCA not found yet? */ - if (iq == 0) { - - /* entry path must not overflow */ - Q_ASSERT_ID(520, ip < QHSM_MAX_NEST_DEPTH_); - - Q_SIG(me) = Q_EXIT_SIG; - (void)(*s)(me); /* exit the source */ - - /* (f) check the rest of source->super - * == target->super->super... - */ - iq = ip; - r = Q_RET_IGNORED; /* LCA NOT found */ - do { - if (t == path[iq]) { /* is this the LCA? */ - r = Q_RET_HANDLED; /* LCA found */ - - /* do not enter LCA */ - ip = (int_fast8_t)(iq - 1); - iq = -1; /* force loop termination */ - } - else { - --iq; /* try lower superstate of target */ - } - } while (iq >= 0); - - /* LCA not found? */ - if (r != Q_RET_HANDLED) { - /* (g) check each source->super->... - * for each target->super... - */ - r = Q_RET_IGNORED; /* keep looping */ - do { - /* exit t unhandled? */ - Q_SIG(me) = Q_EXIT_SIG; - if ((*t)(me) == Q_RET_HANDLED) { - Q_SIG(me) = QEP_EMPTY_SIG_; - (void)(*t)(me); /* find super of t */ - } - t = me->temp; /* set to super of t */ - iq = ip; - do { - /* is this LCA? */ - if (t == path[iq]) { - /* do not enter LCA */ - ip = (int_fast8_t)(iq - 1); - iq = -1; /* break out of inner loop */ - r = Q_RET_HANDLED; /* break */ - } - else { - --iq; - } - } while (iq >= 0); - } while (r != Q_RET_HANDLED); - } - } - } - } - } - } - return ip; -} - -/****************************************************************************/ -/** -* @private @memberof QHsm -* @description -* Finds the child state of the given @c parent, such that this child state -* is an ancestor of the currently active state. The main purpose of this -* function is to support **shallow history** transitions in state machines -* derived from QHsm. -* -* @param[in] me pointer (see @ref oop) -* @param[in] parent pointer to the state-handler function -* -* @returns -* the child of a given @c parent state, which is an ancestor of the current -* active state. For the corner case when the currently active state is the -* given @c parent state, function returns the @c parent state. -* -* @note -* This function is designed to be called during state transitions, so it -* does not necessarily start in a stable state configuration. However, the -* function establishes stable state configuration upon exit. -* -* @sa -* QHsm_childState() -*/ -QStateHandler QHsm_childState_(QHsm * const me, - QStateHandler const parent) -{ - QStateHandler child = me->state; /* start with the current state */ - bool isFound = false; /* start with the child not found */ - QState r; - - /* establish stable state configuration */ - me->temp = me->state; - do { - /* is this the parent of the current child? */ - if (me->temp == parent) { - isFound = true; /* child is found */ - r = Q_RET_IGNORED; /* break out of the loop */ - } - else { - child = me->temp; - Q_SIG(me) = QEP_EMPTY_SIG_; - r = (*me->temp)(me); /* find the superstate */ - } - } while (r != Q_RET_IGNORED); /* QHsm_top() state not reached */ - me->temp = me->state; /* establish stable state configuration */ - - /** @post the child must be found */ - Q_ENSURE_ID(810, isFound != false); -#ifdef Q_NASSERT - (void)isFound; /* avoid compiler warning about unused variable */ -#endif - - return child; /* return the child */ -} diff --git a/libraries/qpn_avr/src/qepn.h b/libraries/qpn_avr/src/qepn.h deleted file mode 100644 index 695d42f..0000000 --- a/libraries/qpn_avr/src/qepn.h +++ /dev/null @@ -1,487 +0,0 @@ -/** -* @file -* @brief Public QEP-nano interface. -* @ingroup qepn -* @cond -****************************************************************************** -* Last updated for version 6.9.0 -* Last updated on 2020-08-21 -* -* Q u a n t u m L e a P s -* ------------------------ -* Modern Embedded Software -* -* Copyright (C) 2005-2020 Quantum Leaps, LLC. All rights reserved. -* -* This program is open source software: you can redistribute it and/or -* modify it under the terms of the GNU General Public License as published -* by the Free Software Foundation, either version 3 of the License, or -* (at your option) any later version. -* -* Alternatively, this program may be distributed and modified under the -* terms of Quantum Leaps commercial licenses, which expressly supersede -* the GNU General Public License and are specifically designed for -* licensees interested in retaining the proprietary status of their code. -* -* This program is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU General Public License for more details. -* -* You should have received a copy of the GNU General Public License -* along with this program. If not, see . -* -* Contact information: -* -* -****************************************************************************** -* @endcond -*/ -#ifndef QEPN_H -#define QEPN_H - -/****************************************************************************/ -/*! The current QP version as a decimal constant XXYZ, where XX is a 2-digit -* major version number, Y is a 1-digit minor version number, and Z is -* a 1-digit release number. -*/ -#define QP_VERSION 690U - -/*! The current QP version number string of the form XX.Y.Z, where XX is -* a 2-digit major version number, Y is a 1-digit minor version number, -* and Z is a 1-digit release number. -*/ -#define QP_VERSION_STR "6.9.0" - -/*! Encrypted current QP release (6.9.0) and date (2020-08-21) */ -#define QP_RELEASE 0x884D22FDU - - -/****************************************************************************/ -/* typedefs for basic numerical types; MISRA-C 2004 rule 6.3(req). */ - -/*! typedef for character strings. */ -/** -* @description -* 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 line numbers in assertions and return from QF_run() */ -typedef int int_t; - -/*! typedef for enumerations used for event signals */ -typedef int enum_t; - -/*! IEEE 754 32-bit floating point number, MISRA-C 2004 rule 6.3(req) */ -/** -* @note QP-nano does not use floating-point types anywhere in the internal -* implementation. -*/ -typedef float float32_t; - -/*! IEEE 754 64-bit floating point number, MISRA-C 2004 rule 6.3(req) */ -/** -* @note QP-nano does not use floating-point types anywhere in the internal -* implementation. -*/ -typedef double float64_t; - -/*! Scalar type describing the signal of an event. */ -typedef uint8_t QSignal; - -#ifndef Q_PARAM_SIZE - /*! The size of event parameter Valid values 0, 1, 2, 4 or 8; default 0 */ - #define Q_PARAM_SIZE 0U -#endif -#if (Q_PARAM_SIZE == 0U) -#elif (Q_PARAM_SIZE == 1U) - typedef uint8_t QParam; -#elif (Q_PARAM_SIZE == 2U) - typedef uint16_t QParam; -#elif (Q_PARAM_SIZE == 4U) - /*! type of the event parameter. */ - /** - * @description - * This typedef is configurable via the preprocessor switch #Q_PARAM_SIZE. - * The other possible values of this type are as follows: @n - * none when (Q_PARAM_SIZE == 0U);@n - * uint8_t when (Q_PARAM_SIZE == 1U);@n - * uint16_t when (Q_PARAM_SIZE == 2U);@n - * uint32_t when (Q_PARAM_SIZE == 4U); and @n - * uint64_t when (Q_PARAM_SIZE == 8U). - */ - typedef uint32_t QParam; -#elif (Q_PARAM_SIZE == 8U) - typedef uint64_t QParam; -#else - #error "Q_PARAM_SIZE defined incorrectly, expected 0U, 1U, 2U, 4U or 8U" -#endif - -/****************************************************************************/ -/*! Event structure. */ -/** -* @description -* ::QEvt represents events, optionally with a single scalar parameter. -* @sa Q_PARAM_SIZE -* @sa ::QParam -*/ -typedef struct { - QSignal sig; /*!< signal of the event */ -#if (Q_PARAM_SIZE != 0U) - QParam par; /*!< scalar parameter of the event */ -#endif -} QEvt; - -/****************************************************************************/ -/*! Macro to access the signal of the current event of a state machine */ -/** -* @param[in,out] me_ pointer to a subclass of ::QHsm (see @ref oop) -*/ -#define Q_SIG(me_) (((QHsm *)(me_))->evt.sig) - -#if (Q_PARAM_SIZE != 0U) -/*! Macro to access the parameter of the current event of a state machine */ -/** -* @param[in,out] me_ pointer to a subclass of ::QHsm (see @ref oop) -*/ -#define Q_PAR(me_) (((QHsm *)(me_))->evt.par) -#endif /* (Q_PARAM_SIZE != 0U) */ - -/****************************************************************************/ -/*! Type returned from a state-handler function. */ -typedef uint_fast8_t QState; - -/*! the signature of a state handler function */ -typedef QState (*QStateHandler)(void * const me); - -/****************************************************************************/ -/*! virtual table for the ::QHsm class. */ -typedef struct QHsmVtable QHsmVtable; - -/*! Hierarchical State Machine */ -/** -* @description -* QHsm represents a Hierarchical Finite State Machine (HSM) with full -* support for hierarchical nesting of states, entry/exit actions, -* and initial transitions in any composite state. -* -* @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. -* -* @usage -* The following example illustrates how to derive a state machine structure -* from QHsm. Please note that the QHsm member 'super' is defined as the -* _first_ member of the derived struct. -* @include qepn_qhsm.c -* -* @sa @ref oop -*/ -typedef struct { - QHsmVtable const *vptr; /*!< virtual pointer */ - QStateHandler state; /*!< current active state (state-variable) */ - QStateHandler temp; /*!< temporary: tran. chain, target state, etc. */ - QEvt evt; /*!< currently processed event in the HSM (protected) */ -} QHsm; - -/*! Virtual table for the QHsm class */ -struct QHsmVtable { - /*! Triggers the top-most initial transition in a HSM. */ - void (*init)(QHsm * const me); - - /*! Dispatches an event to a HSM. */ - void (*dispatch)(QHsm * const me); -}; - -/*! Polymorphically executes the top-most initial transition in a SM. */ -/** -* @param[in,out] me_ pointer (see @ref oop) -* -* @note Must be called only ONCE after the SM "constructor". -* -* @usage -* The following example illustrates how to initialize a SM, and dispatch -* events to it: -* @include qepn_qhsm_use.c -*/ -#define QHSM_INIT(me_) do { \ - Q_ASSERT((me_)->vptr); \ - (*(me_)->vptr->init)((me_)); \ -} while (false) - -/*! Polymorphically dispatches an event to a HSM. */ -/** -* @description -* Processes one event at a time in Run-to-Completion fashion. -* -* @param[in,out] me_ pointer (see @ref oop) -* -* @note Must be called after the "constructor" and after QHSM_INIT(). -*/ -#define QHSM_DISPATCH(me_) ((*(me_)->vptr->dispatch)((me_))) - -/* public methods */ -/*! "constructor" of a HSM. -* @protected @memberof QHsm -*/ -void QHsm_ctor(QHsm * const me, QStateHandler initial); - -/*! Obtain the current active state from a HSM (read only). */ -/** -* @param[in] me_ pointer (see @ref oop) -* -* @returns the current active state of a HSM -*/ -#define QHsm_state(me_) (Q_STATE_CAST(Q_HSM_UPCAST(me_)->state)) - -/*! Obtain the current active child state of a given parent in QHsm -* @public @memberof QHsm -*/ -/** -* @param[in] me_ pointer (see @ref oop) -* @param[in] parent_ pointer to the parent state-handler -* @returns the current active child state-handler of a given parent -* @note this macro is used in QM for auto-generating code for state history -*/ -#define QHsm_childState(me_, parent_) \ - QHsm_childState_(Q_HSM_UPCAST(me_), Q_STATE_CAST(parent_)) - -/*! Helper function to obtain the current active child state of a parent */ -QStateHandler QHsm_childState_(QHsm * const me, - QStateHandler const parent); - -/*! Implementation of the top-most initial transition in QHsm. -* @private @memberof QHsm -*/ -void QHsm_init_(QHsm * const me); - -/*! Implementation of dispatching events to QHsm. -* @private @memberof QHsm -*/ -void QHsm_dispatch_(QHsm * const me); - -/*! the top-state. -* @protected @memberof QHsm -*/ -QState QHsm_top(void const * const me); - - -/****************************************************************************/ -/* All possible values returned from state/action handlers */ - -/* unhandled and need to "bubble up"... */ - -/*! event passed to superstate to handle */ -#define Q_RET_SUPER ((QState)0) - -/*! event passed to submachine superstate */ -#define Q_RET_SUPER_SUB ((QState)1) - -/*! event unhandled due to a guard */ -#define Q_RET_UNHANDLED ((QState)2) - -/* handled and do not need to "bubble up"... */ -/*! event handled (internal transition) */ -#define Q_RET_HANDLED ((QState)3) - -/*! event silently ignored (bubbled up to top) */ -#define Q_RET_IGNORED ((QState)4) - -/* entry/exit... */ -/*! state entry action executed */ -#define Q_RET_ENTRY ((QState)5) - -/*! state exit action executed */ -#define Q_RET_EXIT ((QState)6) - -/* no side effects */ -/*! return value without any effect */ -#define Q_RET_NULL ((QState)7) - -/* transitions need to execute transition-action table in QHsm... */ -/*! event handled (regular transition) */ -#define Q_RET_TRAN ((QState)8) - -/*! initial transition in a state or submachine */ -#define Q_RET_TRAN_INIT ((QState)9) - -/*! event handled (transition to history) */ -#define Q_RET_TRAN_HIST ((QState)10) - - -/*! Perform upcast from a subclass of ::QHsm to the base class ::QHsm */ -/** -* @description -* Upcasting from a subclass to superclass is a very frequent and __safe__ -* operation in object-oriented programming and object-oriented languages -* (such as C++) perform such upcasting automatically. However, OOP is -* implemented in C just as a set of coding conventions (see @ref oop), -* and the C compiler does not "know" that certain types are related by -* inheritance. Therefore for C, the upcast must be performed explicitly. -* Unfortunately, pointer casting violates the advisory MISRA-C 2004 rule 11.4 -* "cast pointer to pointer". This macro encapsulates this deviation and -* provides a descriptive name for the reason of this cast. -*/ -#define Q_HSM_UPCAST(ptr_) ((QHsm *)(ptr_)) - -/*! Perform cast to ::QStateHandler. */ -/** -* @description -* 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. -* -* @usage -* @include qepn_qhsm_ctor.c -*/ -#define Q_STATE_CAST(handler_) ((QStateHandler)(handler_)) - -/*! Macro to call in a state-handler when it executes a regular -* or and initial transition. Applicable to both HSMs and FSMs. -* @include qepn_qtran.c -*/ -#define Q_TRAN(target_) \ - ((Q_HSM_UPCAST(me))->temp = Q_STATE_CAST(target_), (QState)Q_RET_TRAN) - -/*! Macro to call in a state-handler when it executes a transition -* to history. Applicable only to HSMs. -* -* @usage -* @include qepn_qhist.c -*/ -#define Q_TRAN_HIST(hist_) \ - ((Q_HSM_UPCAST(me))->temp = (hist_), (QState)Q_RET_TRAN_HIST) - -/*! Macro to call in a state-handler when it designates the -* superstate of a given state. Applicable only to HSMs. -* -* @usage -* @include qepn_qtran.c -*/ -#define Q_SUPER(super_) \ - ((Q_HSM_UPCAST(me))->temp = Q_STATE_CAST(super_), Q_RET_SUPER) - -/*! Macro to call in a state-handler when it handles an event. -* Applicable to both HSMs and FSMs. -*/ -#define Q_HANDLED() Q_RET_HANDLED - -/*! Macro to call in a state-handler when it attempts to handle -* an event but a guard condition evaluates to 'false' and there is no other -* explicit way of handling the event. Applicable only to HSMs. -*/ -#define Q_UNHANDLED() Q_RET_UNHANDLED - - -/*! QP reserved signals */ - -/*! signal for coding entry actions */ -#define Q_ENTRY_SIG ((QSignal)1) - -/*! signal for coding exit actions */ -#define Q_EXIT_SIG ((QSignal)2) - -/*! signal for coding nested initial transitions */ -#define Q_INIT_SIG ((QSignal)3) - -/*! timeout signal at the default tick rate 0 */ -#define Q_TIMEOUT_SIG ((QSignal)4) - -/*! timeout signal at tick rate 1 */ -#define Q_TIMEOUT1_SIG ((QSignal)5) - -/*! timeout signal at tick rate 2 */ -#define Q_TIMEOUT2_SIG ((QSignal)6) - -/*! timeout signal at tick rate 3 */ -#define Q_TIMEOUT3_SIG ((QSignal)7) - -/*!< first signal for the user applications */ -#define Q_USER_SIG ((QSignal)8) - -/*! Perform cast from unsigned integer to a pointer of type @a type_ */ -/** -* @description -* 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_, uintptr_) ((type_ *)(uintptr_)) - -/****************************************************************************/ -/* macros for accessing data in ROM */ -#ifndef Q_ROM /* if NOT defined, provide the default definition */ - - /*! Macro to specify compiler-specific directive for placing a - * constant object in ROM. */ - /** - * @description - * Many compilers for 8-bit Harvard-architecture MCUs provide non-standard - * extensions to support placement of objects in different memories. - * In order to conserve the precious RAM, QP-nano uses the Q_ROM macro for - * all constant objects that can be allocated in ROM. - * - * @note - * To override the following empty definition, you need to define the - * Q_ROM macro in the qpn_port.h header file. Some examples of valid - * Q_ROM macro definitions are: __code (IAR 8051 compiler), code (Keil - * 8051 compiler), PROGMEM (gcc for AVR), __flash (IAR for AVR). - */ - #define Q_ROM -#endif - -#ifndef Q_ROM_BYTE - /*! 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 explicitly 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. - * - * @note - * The Q_ROM_BYTE() macro should be defined in the qpn_port.h header file - * for each compiler that cannot handle correctly data allocated in ROM - * (such as the gcc). If the macro is left undefined, the default - * definition simply returns the parameter and lets the compiler - * synthesize the correct code. - */ - #define Q_ROM_BYTE(rom_var_) (rom_var_) -#endif - -#ifndef Q_ROM_PTR - /*! Macro to access a pointer 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 explicitly add - * assembly code to access each data element allocated in the program - * space. The macro Q_ROM_PTR() retrieves an object-pointer from the given - * ROM address. Please note that the pointer can be pointing to the object - * in RAM or ROM. - * - * @note - * The Q_ROM_PTR() macro should be defined in the qpn_port.h header file - * for each compiler that cannot handle correctly data allocated in ROM - * (such as the gcc). If the macro is left undefined, the default - * definition simply returns the parameter and lets the compiler - * synthesize the correct code. - */ - #define Q_ROM_PTR(rom_var_) (rom_var_) -#endif - - -/****************************************************************************/ -/*! the current QP version number string in ROM, based on QP_VERSION_STR */ -extern char_t const Q_ROM QP_versionStr[7]; - -/*! get the current QP-nano version number string of the form "X.Y.Z" */ -#define QP_getVersion() (QP_versionStr) - -#endif /* QEPN_H */ - diff --git a/libraries/qpn_avr/src/qfn.c b/libraries/qpn_avr/src/qfn.c deleted file mode 100644 index 564a03a..0000000 --- a/libraries/qpn_avr/src/qfn.c +++ /dev/null @@ -1,508 +0,0 @@ -/** -* @file -* @brief QF-nano implementation. -* @ingroup qfn -* @cond -****************************************************************************** -* Last updated for version 6.8.2 -* Last updated on 2020-03-08 -* -* Q u a n t u m L e a P s -* ------------------------ -* Modern Embedded Software -* -* Copyright (C) 2005-2020 Quantum Leaps, LLC. All rights reserved. -* -* This program is open source software: you can redistribute it and/or -* modify it under the terms of the GNU General Public License as published -* by the Free Software Foundation, either version 3 of the License, or -* (at your option) any later version. -* -* Alternatively, this program may be distributed and modified under the -* terms of Quantum Leaps commercial licenses, which expressly supersede -* the GNU General Public License and are specifically designed for -* licensees interested in retaining the proprietary status of their code. -* -* This program is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU General Public License for more details. -* -* You should have received a copy of the GNU General Public License -* along with this program. If not, see . -* -* Contact information: -* -* -****************************************************************************** -* @endcond -*/ -#include "qpn_conf.h" /* QP-nano configuration file (from the application) */ -#include "qfn_port.h" /* QF-nano port from the port directory */ -#include "qassert.h" /* embedded systems-friendly assertions */ - -Q_DEFINE_THIS_MODULE("qfn") - -/* Global-scope objects *****************************************************/ - -/** -* @description -* This variable stores the number of active objects in the application. -* This is the number of elements (dimension of) the QF_active[] array. -*/ -uint_fast8_t QF_maxActive_; - -/** -* @description -* The QF-nano ready set keeps track of active objects that are ready to run. -* The ready set represents each active object as a bit, with the bits -* assigned according to priorities of the active objects. The bit is set -* if the corresponding active object is ready to run (i.e., has one or -* more events in its event queue) and zero if the event queue is empty. -* The QF-nano ready set is one byte-wide, which corresponds to 8 active -* objects maximum. -*/ -uint_fast8_t volatile QF_readySet_; - -#ifdef QF_TIMEEVT_USAGE -/** -* @description -* The QF-nano timer set keeps track of the armed time events. The timer set -* represents the timeout down-counter of each active object as a bit, with -* the bits assigned according to priorities of the active objects. The bit -* is set if the corresponding timeout down-counter is not zero (i.e., is -* counting down) and zero if the down-counter is zero. The QF-nano time event -* set is one byte-wide, which corresponds to 8 active objects maximum.@n -* @n -* The main use of the QF_timerSetX_ is to quickly determine that all time -* events are disarmed by testing (QF_timerSetX_[tickRate] == 0). -* If so, the CPU can go to longer sleep mode, in which the system clock -* tick ISR is turned off. -* -* @note The test (QF_timerSet_[tickRate] == 0) must be always performed -* inside a CRITICAL SECTION. -*/ -uint_fast8_t volatile QF_timerSetX_[QF_MAX_TICK_RATE]; -#endif - -#ifndef QF_LOG2 -uint8_t const Q_ROM QF_log2Lkup[16] = { - 0U, 1U, 2U, 2U, 3U, 3U, 3U, 3U, - 4U, 4U, 4U, 4U, 4U, 4U, 4U, 4U -}; -#endif /* QF_LOG2 */ - -/****************************************************************************/ -/** -* @protected @memberof QActive -*/ -void QActive_ctor(QActive * const me, QStateHandler initial) { - static QActiveVtable const vtable = { /* QActive virtual table */ - { &QHsm_init_, - &QHsm_dispatch_ }, - &QActive_postX_, - &QActive_postXISR_ - }; - - /** - * @note QActive inherits QActive, so by the @ref oop convention - * it should call the constructor of the superclass, i.e., QActive_ctor(). - * However, this would pull in the QActiveVtable, which in turn will pull - * in the code for QHsm_init_() and QHsm_dispatch_() implemetations, - * which is expensive. To avoid this code size penalty, in case ::QHsm is - * not used in a given project, the call to QHsm_ctor() avoids pulling - * in the code for QHsm. - */ - QHsm_ctor(&me->super, initial); - me->super.vptr = &vtable.super; /* hook the vptr to QActive vtable */ -} - -/****************************************************************************/ -/** -* @private @memberof QActive -* @description -* Direct event posting is the simplest asynchronous communication method -* available in QF-nano. -* -* @attention -* This function should be called only via the macro QACTIVE_POST() -* or QACTIVE_POST_X(). This function should be only used in the -* __task__ context. -* -* @param[in,out] me pointer (see @ref oop) -* @param[in] margin number of required free slots in the queue after -* posting the event. The special value #QF_NO_MARGIN -* means that this function will assert if posting fails. -* @param[in] sig signal of the event to be posted -* @param[in] par parameter of the event to be posted -* -* @usage -* @include qfn_postx.c -*/ -#if (Q_PARAM_SIZE != 0U) -bool QActive_postX_(QActive * const me, uint_fast8_t margin, - enum_t const sig, QParam const par) -#else -bool QActive_postX_(QActive * const me, uint_fast8_t margin, - enum_t const sig) -#endif -{ - QActiveCB const Q_ROM *acb = &QF_active[me->prio]; - uint_fast8_t qlen = Q_ROM_BYTE(acb->qlen); - bool can_post; - - QF_INT_DISABLE(); - - if (margin == QF_NO_MARGIN) { - if (qlen > (uint_fast8_t)me->nUsed) { - can_post = true; /* can post */ - } - else { - can_post = false; /* cannot post */ -#ifndef Q_NASSERT - QF_INT_ENABLE(); - /* must be able to post event : Q_ERROR_ID(310) */ - Q_onAssert(Q_this_module_, 310); -#endif - } - } - else if ((qlen - (uint_fast8_t)me->nUsed) > margin) { - can_post = true; /* can post */ - } - else { - can_post = false; /* cannot post */ - } - - if (can_post) { /* can post the event? */ - /* insert event into the ring buffer (FIFO) */ - QF_ROM_QUEUE_AT_(acb, me->head).sig = (QSignal)sig; -#if (Q_PARAM_SIZE != 0U) - QF_ROM_QUEUE_AT_(acb, me->head).par = par; -#endif - if (me->head == 0U) { - me->head = (uint8_t)qlen; /* wrap the head */ - } - --me->head; - ++me->nUsed; - - /* is this the first event? */ - if (me->nUsed == 1U) { - - /* set the corresponding bit in the ready set */ - QF_readySet_ |= (uint_fast8_t)1 << (me->prio - 1U); - -#ifdef qkn_h - if (QK_sched_() != 0U) { - QK_activate_(); /* activate the next active object */ - } -#endif - } - } - QF_INT_ENABLE(); - - return can_post; -} - -/****************************************************************************/ -/** -* @private @memberof QActive -* @description -* Direct event posting is the simplest asynchronous communication method -* available in QF-nano. -* -* @attention -* This function should be called only via the macro QACTIVE_POST_ISR() -* or QACTIVE_POST_X_ISR(). This function should be only used in the -* __ISR__ context. -* -* @param[in,out] me pointer (see @ref oop) -* @param[in] margin number of required free slots in the queue after -* posting the event. The special value #QF_NO_MARGIN -* means that this function will assert if posting fails. -* @param[in] sig signal of the event to be posted -* @param[in] par parameter of the event to be posted -* -* @usage -* @include qfn_postx.c -*/ -#if (Q_PARAM_SIZE != 0U) -bool QActive_postXISR_(QActive * const me, uint_fast8_t margin, - enum_t const sig, QParam const par) -#else -bool QActive_postXISR_(QActive * const me, uint_fast8_t margin, - enum_t const sig) -#endif -{ -#ifdef QF_ISR_NEST -#ifdef QF_ISR_STAT_TYPE - QF_ISR_STAT_TYPE stat; -#endif -#endif - QActiveCB const Q_ROM *acb = &QF_active[me->prio]; - uint_fast8_t qlen = Q_ROM_BYTE(acb->qlen); - bool can_post; - -#ifdef QF_ISR_NEST -#ifdef QF_ISR_STAT_TYPE - QF_ISR_DISABLE(stat); -#else - QF_INT_DISABLE(); -#endif -#endif - - if (margin == QF_NO_MARGIN) { - if (qlen > (uint_fast8_t)me->nUsed) { - can_post = true; /* can post */ - } - else { - can_post = false; /* cannot post */ -#ifndef Q_NASSERT - QF_INT_ENABLE(); - /* must be able to post event : Q_ERROR_ID(410) */ - Q_onAssert(Q_this_module_, 410); -#endif - } - } - else if ((qlen - (uint_fast8_t)me->nUsed) > margin) { - can_post = true; /* can post */ - } - else { - can_post = false; /* cannot post */ - } - - if (can_post) { /* can post the event? */ - /* insert event into the ring buffer (FIFO) */ - QF_ROM_QUEUE_AT_(acb, me->head).sig = (QSignal)sig; -#if (Q_PARAM_SIZE != 0U) - QF_ROM_QUEUE_AT_(acb, me->head).par = par; -#endif - if (me->head == 0U) { - me->head = (uint8_t)qlen; /* wrap the head */ - } - --me->head; - ++me->nUsed; - /* is this the first event? */ - if (me->nUsed == 1U) { - /* set the bit */ - QF_readySet_ |= (uint_fast8_t)1 << (me->prio - 1U); - } - } - -#ifdef QF_ISR_NEST -#ifdef QF_ISR_STAT_TYPE - QF_ISR_RESTORE(stat); -#else - QF_INT_ENABLE(); -#endif -#endif - - return can_post; -} - -/****************************************************************************/ -/** -* @description -* The function QF_init() initializes the number of active objects to be -* managed by the framework and clears the internal QF-nano variables as well -* as all registered active objects to zero, which is needed in case when -* the startup code does not clear the uninitialized data (in violation of -* the C Standard). -* -* @note -* The intended use of the function is to call as follows: -* QF_init(Q_DIM(QF_active)); -*/ -void QF_init(uint_fast8_t maxActive) { - QActive *a; - uint_fast8_t p; - uint_fast8_t n; - - /** @pre the number of active objects must be in range */ - Q_REQUIRE_ID(100, (1U < maxActive) - && (maxActive <= 9U)); - QF_maxActive_ = (uint_fast8_t)maxActive - 1U; - -#ifdef QF_TIMEEVT_USAGE - for (n = 0U; n < (uint_fast8_t)QF_MAX_TICK_RATE; ++n) { - QF_timerSetX_[n] = 0U; - } -#endif /* QF_TIMEEVT_USAGE */ - - QF_readySet_ = 0U; - -#ifdef qkn_h - QK_attr_.actPrio = 8U; /* QK-nano scheduler locked */ - -#ifdef QF_ISR_NEST - QK_attr_.intNest = 0U; -#endif - -#ifdef QK_SCHED_LOCK - QK_attr_.lockPrio = 0U; - QK_attr_.lockHolder = 0U; -#endif - -#endif /* #ifdef qkn_h */ - - /* clear all registered active objects... */ - for (p = 1U; p <= QF_maxActive_; ++p) { - a = QF_ROM_ACTIVE_GET_(p); - - /* QF_active[p] must be initialized */ - Q_ASSERT_ID(110, a != (QActive *)0); - - a->head = 0U; - a->tail = 0U; - a->nUsed = 0U; -#if (QF_TIMEEVT_CTR_SIZE != 0U) - for (n = 0U; n < (uint_fast8_t)QF_MAX_TICK_RATE; ++n) { - a->tickCtr[n].nTicks = 0U; -#ifdef QF_TIMEEVT_PERIODIC - a->tickCtr[n].interval = 0U; -#endif /* def QF_TIMEEVT_PERIODIC */ - } -#endif /* (QF_TIMEEVT_CTR_SIZE != 0U) */ - } - -#ifdef QV_INIT /* initialization of the QV-nano kernel defined? */ - QV_INIT(); /* port-specific initialization of the QV-nano kernel */ -#elif defined QK_INIT /* initialization of the QK-nano kernel defined? */ - QK_INIT(); /* port-specific initialization of the QK-nano kernel */ -#endif -} - -/****************************************************************************/ -/****************************************************************************/ -#if (QF_TIMEEVT_CTR_SIZE != 0U) - -/****************************************************************************/ -/** -* @description -* This function must be called periodically from a time-tick ISR or from -* an ISR so that QF-nano can manage the timeout events assigned to the given -* system clock tick rate. -* -* @param[in] tickRate system clock tick rate serviced in this call. -* -* @note Each system tick rate posts timeout events with a different signal -* as follows:@n -* tickRate==0 Q_TIMEOUT_SIG@n -* tickRate==1 Q_TIMEOUT1_SIG@n -* tickRate==2 Q_TIMEOUT2_SIG@n -* tickRate==3 Q_TIMEOUT3_SIG -* -* @note The calls to QF_tickXISR() with different tick rate parameter can -* preempt each other. For example, higher clock tick rates might be serviced -* from interrupts that can preempt lower-priority interrupts. -*/ -void QF_tickXISR(uint_fast8_t const tickRate) { - uint_fast8_t p = QF_maxActive_; - do { - QActive *a = QF_ROM_ACTIVE_GET_(p); - QTimer *t = &a->tickCtr[tickRate]; - - if (t->nTicks != 0U) { - --t->nTicks; - if (t->nTicks == 0U) { - -#ifdef QF_TIMEEVT_PERIODIC - if (t->interval != 0U) { - t->nTicks = t->interval; /* re-arm the periodic timer */ - } -#endif /* QF_TIMEEVT_PERIODIC */ - -#ifdef QF_TIMEEVT_USAGE - QF_timerSetX_[tickRate] &= (uint_fast8_t)(~(1U << (p - 1U))); -#endif /* QF_TIMEEVT_USAGE */ - -#if (Q_PARAM_SIZE != 0) - QACTIVE_POST_ISR(a, (enum_t)Q_TIMEOUT_SIG + (enum_t)tickRate, - 0U); -#else - QACTIVE_POST_ISR(a, (enum_t)Q_TIMEOUT_SIG + (enum_t)tickRate); -#endif /* (Q_PARAM_SIZE != 0U) */ - } - } - --p; - } while (p != 0U); -} - -/****************************************************************************/ -/** -* @public @memberof QActive -* @description -* Arms a time event to fire in a specified number of clock ticks at the -* specified tick rate. The timeout signal gets directly posted (using the -* FIFO policy) into the event queue of the active object calling this -* function. -* -* @param[in,out] me pointer (see @ref oop) -* @param[in] tickRate tick rate . -* @param[in] nTicks number of clock ticks (at the associated rate) -* to rearm the time event with. -* -* @note Each system tick rate posts timeout events with a different signal -* as follows:@n -* tickRate==0 Q_TIMEOUT_SIG@n -* tickRate==1 Q_TIMEOUT1_SIG@n -* tickRate==2 Q_TIMEOUT2_SIG@n -* tickRate==3 Q_TIMEOUT3_SIG -* -* @note After posting, a one-shot time event gets automatically disarmed. -* -* @note A time event can be disarmed at any time by calling the -* QActive_disarmX() function. -* -* @usage -* The following example shows how to arm a time event from a state -* machine of an active object: -* @include qfn_armx.c -*/ -#ifdef QF_TIMEEVT_PERIODIC -void QActive_armX(QActive * const me, uint_fast8_t const tickRate, - QTimeEvtCtr const nTicks, QTimeEvtCtr const interval) -#else -void QActive_armX(QActive * const me, uint_fast8_t const tickRate, - QTimeEvtCtr const nTicks) -#endif -{ - QF_INT_DISABLE(); - me->tickCtr[tickRate].nTicks = nTicks; -#ifdef QF_TIMEEVT_PERIODIC - me->tickCtr[tickRate].interval = interval; -#endif /* QF_TIMEEVT_PERIODIC */ - -#ifdef QF_TIMEEVT_USAGE - /* set a bit in QF_timerSetX_[] to rememer that the timer is running */ - QF_timerSetX_[tickRate] |= (uint_fast8_t)1 << (me->prio - 1U); -#endif - QF_INT_ENABLE(); -} - -/****************************************************************************/ -/** -* @public @memberof QActive -* @description -* The time event of the active object gets disarmed (stopped). -* -* @param[in,out] me pointer (see @ref oop) -* @param[in] tickRate tick rate -* -* @note You should __not__ assume that the timeout event will not -* arrive after you disarm the time event. The timeout event could be -* already in the event queue. -*/ -void QActive_disarmX(QActive * const me, uint_fast8_t const tickRate) { - QF_INT_DISABLE(); - me->tickCtr[tickRate].nTicks = 0U; -#ifdef QF_TIMEEVT_PERIODIC - me->tickCtr[tickRate].interval = 0U; -#endif /* QF_TIMEEVT_PERIODIC */ - -#ifdef QF_TIMEEVT_USAGE - /* clear a bit in QF_timerSetX_[] to rememer that timer is not running */ - QF_timerSetX_[tickRate] &= (uint_fast8_t)(~(1U << (me->prio - 1U))); -#endif - QF_INT_ENABLE(); -} -#endif /* #if (QF_TIMEEVT_CTR_SIZE != 0U) */ diff --git a/libraries/qpn_avr/src/qfn.h b/libraries/qpn_avr/src/qfn.h deleted file mode 100644 index d1d3ab9..0000000 --- a/libraries/qpn_avr/src/qfn.h +++ /dev/null @@ -1,459 +0,0 @@ -/** -* @file -* @brief Public QF-nano interface. -* @ingroup qfn -* @cond -****************************************************************************** -* Last updated for version 6.8.2 -* Last updated on 2020-03-08 -* -* Q u a n t u m L e a P s -* ------------------------ -* Modern Embedded Software -* -* Copyright (C) 2005-2020 Quantum Leaps, LLC. All rights reserved. -* -* This program is open source software: you can redistribute it and/or -* modify it under the terms of the GNU General Public License as published -* by the Free Software Foundation, either version 3 of the License, or -* (at your option) any later version. -* -* Alternatively, this program may be distributed and modified under the -* terms of Quantum Leaps commercial licenses, which expressly supersede -* the GNU General Public License and are specifically designed for -* licensees interested in retaining the proprietary status of their code. -* -* This program is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU General Public License for more details. -* -* You should have received a copy of the GNU General Public License -* along with this program. If not, see . -* -* Contact information: -* -* -****************************************************************************** -* @endcond -*/ -#ifndef QFN_H -#define QFN_H - -/** -* @description -* This header file must be included in all modules that use QP-nano. -* Typically, this header file is included indirectly through the -* header file qpn_port.h. -*/ - -/****************************************************************************/ -#ifndef QF_TIMEEVT_CTR_SIZE - /*! macro to override the default QTimeEvtCtr size. - * Valid values 0U, 1U, 2U, or 4U; default 0U - */ - #define QF_TIMEEVT_CTR_SIZE 0U -#endif -#if (QF_TIMEEVT_CTR_SIZE == 0U) - /* no time events */ -#elif (QF_TIMEEVT_CTR_SIZE == 1U) - typedef uint8_t QTimeEvtCtr; -#elif (QF_TIMEEVT_CTR_SIZE == 2U) - /*! type of the Time Event counter, which determines the dynamic - * range of the time delays measured in clock ticks. - */ - /** - * @description - * This typedef is configurable via the preprocessor switch - * #QF_TIMEEVT_CTR_SIZE. The other possible values of this type are - * as follows: @n - * none when (QF_TIMEEVT_CTR_SIZE not defined or == 0U), @n - * uint8_t when (QF_TIMEEVT_CTR_SIZE == 1U); @n - * uint16_t when (QF_TIMEEVT_CTR_SIZE == 2U); and @n - * uint32_t when (QF_TIMEEVT_CTR_SIZE == 4U). - */ - typedef uint16_t QTimeEvtCtr; -#elif (QF_TIMEEVT_CTR_SIZE == 4U) - typedef uint32_t QTimeEvtCtr; -#else - #error "QF_TIMER_SIZE defined incorrectly, expected 1U, 2U, or 4U" -#endif - -#if (QF_TIMEEVT_CTR_SIZE != 0U) - /*! Timer structure the active objects */ - typedef struct { - QTimeEvtCtr nTicks; /*!< timer tick counter */ -#ifdef QF_TIMEEVT_PERIODIC - QTimeEvtCtr interval; /*!< timer interval */ -#endif /* QF_TIMEEVT_PERIODIC */ - } QTimer; -#endif /* (QF_TIMEEVT_CTR_SIZE != 0U) */ - -#ifndef QF_MAX_TICK_RATE - /*! Default value of the macro configurable value in qpn_port.h */ - #define QF_MAX_TICK_RATE 1U -#elif (QF_MAX_TICK_RATE > 4U) - #error "QF_MAX_TICK_RATE exceeds the 4U limit" -#endif - -/****************************************************************************/ -/*! QActive active object (based on QHsm-implementation) -* @extends QHsm -*/ -/** -* @description -* QActive is the base structure for derivation of active objects. Active -* objects in QF-nano 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 structure for derivation of active objects in the -* application code. -* -* @usage -* The following example illustrates how to derive an active object from -* ::QActive. Please note that the ::QActive member super_ is defined as -* the __first__ member of the derived struct. -* @include qfn_qactive.c -*/ -typedef struct QActive { - QHsm super; /**< derives from the ::QHsm base class */ - -#if (QF_TIMEEVT_CTR_SIZE != 0U) - /*! Timer for the active object */ - QTimer tickCtr[QF_MAX_TICK_RATE]; -#endif /* (QF_TIMEEVT_CTR_SIZE != 0U) */ - - /*! priority of the active object (1..8) */ - uint8_t prio; - - /*! offset to where next event will be inserted into the buffer */ - uint8_t volatile head; - - /*! offset of where next event will be extracted from the buffer */ - uint8_t volatile tail; - - /*! number of events currently present in the queue - * (events in the ring buffer + 1 event in the state machine) - */ - uint8_t volatile nUsed; - -} QActive; - -/*! Virtual table for the QActive class -* @extends QHsmVtable -*/ -typedef struct { - QHsmVtable super; /*!< inherits QHsmVtable */ - -#if (Q_PARAM_SIZE != 0U) - /*! virtual function to asynchronously post (FIFO) an event to an AO - * (task context). - */ - /** @sa QACTIVE_POST() and QACTIVE_POST_X() */ - bool (*post)(QActive * const me, uint_fast8_t const margin, - enum_t const sig, QParam const par); - - /*! virtual function to asynchronously post (FIFO) an event to an AO - * (ISR context). - */ - /** @sa QACTIVE_POST_ISR() and QACTIVE_POST_X_ISR() */ - bool (*postISR)(QActive * const me, uint_fast8_t const margin, - enum_t const sig, QParam const par); -#else - bool (*post) (QActive * const me, uint_fast8_t const margin, - enum_t const sig); - bool (*postISR)(QActive * const me, uint_fast8_t const margin, - enum_t const sig); -#endif -} QActiveVtable; - -/*! protected "constructor" of an QActive active object. */ -void QActive_ctor(QActive * const me, QStateHandler initial); - - -/*! special value of margin that causes asserting failure in case -* event posting fails. -*/ -#define QF_NO_MARGIN ((uint_fast8_t)0xFF) - -#if (Q_PARAM_SIZE != 0U) - /*! Polymorphically posts an event to an active object (FIFO) - * with delivery guarantee (task context). - */ - /** - * @description - * This macro asserts if the queue overflows and cannot accept the event. - * - * @param[in,out] me_ pointer (see @ref oop) - * @param[in] sig_ signal of the event to post - * @param[in] par_ parameter of the event to post. - * - * @sa QACTIVE_POST_X(), QActive_postX_(), - * QACTIVE_POST_ISR(), QActive_postXISR_(). - * - * @usage - * @include qfn_post.c - */ - #define QACTIVE_POST(me_, sig_, par_) do { \ - QActive * const ao_ = QF_ACTIVE_CAST((me_)); \ - ((void)(*((QActiveVtable const *)(ao_->super.vptr))->post)( \ - ao_, QF_NO_MARGIN, (enum_t)(sig_), (QParam)(par_)));\ - } while (false) - - /*! Polymorphically posts an event to an active object (FIFO) - * without delivery guarantee (task context). - */ - /** - * @description - * This macro does not assert if the queue overflows and cannot accept - * the event with the specified margin of free slots remaining. - * - * @param[in,out] me_ pointer (see @ref oop) - * @param[in] margin_ the minimum free slots in the queue, which - * must still be available after posting the event. - * The special value #QF_NO_MARGIN causes asserting failure - * in case event allocation fails. - * @param[in] sig_ signal of the event to post - * @param[in] par_ parameter of the event to post. - * - * @returns - * 'true' if the posting succeeded, and 'false' if the posting failed - * due to insufficient margin of free slots available in the queue. - * - * @usage - * @include qfn_postx.c - */ - #define QACTIVE_POST_X(me_, margin_, sig_, par_) \ - ((*((QActiveVtable const *)((me_)->super.vptr))->post)( \ - (me_), (margin_), (enum_t)(sig_), (QParam)(par_))) - - /*! Polymorphically posts an event to an active object (FIFO) - * with delivery guarantee (ISR context). - */ - /** - * @description - * This macro asserts if the queue overflows and cannot accept the event. - * - * @param[in,out] me_ pointer (see @ref oop) - * @param[in] sig_ signal of the event to post - * @param[in] par_ parameter of the event to post. - * - * @sa QACTIVE_POST_X(), QActive_postX_(). - * - * @usage - * @include qfn_post.c - */ - #define QACTIVE_POST_ISR(me_, sig_, par_) do { \ - QActive * const ao_ = QF_ACTIVE_CAST((me_)); \ - ((void)(*((QActiveVtable const *)(ao_->super.vptr))->postISR)( \ - ao_, QF_NO_MARGIN, (enum_t)(sig_), (QParam)(par_))); \ - } while (false) - - /*! Polymorphically posts an event to an active object (FIFO) - * without delivery guarantee (ISR context). - */ - /** - * @description - * This macro does not assert if the queue overflows and cannot accept - * the event with the specified margin of free slots remaining. - * - * @param[in,out] me_ pointer (see @ref oop) - * @param[in] margin_ the minimum free slots in the queue, which - * must still be available after posting the event. - * The special value #QF_NO_MARGIN causes asserting failure - * in case event allocation fails. - * @param[in] sig_ signal of the event to post - * @param[in] par_ parameter of the event to post. - * - * @returns - * 'true' if the posting succeeded, and 'false' if the posting failed - * due to insufficient margin of free slots available in the queue. - * - * @usage - * @include qfn_postx.c - */ - #define QACTIVE_POST_X_ISR(me_, margin_, sig_, par_) \ - ((*((QActiveVtable const *)( \ - QF_ACTIVE_CAST((me_))->super.vptr))->postISR)( \ - QF_ACTIVE_CAST((me_)), (margin_), \ - (enum_t)(sig_), (QParam)(par_))) - - /*! Implementation of the task-level event posting - * @private @memberof QActive - */ - bool QActive_postX_(QActive * const me, uint_fast8_t margin, - enum_t const sig, QParam const par); - - /*! Implementation of the ISR-level event posting - * @private @memberof QActive - */ - bool QActive_postXISR_(QActive * const me, uint_fast8_t margin, - enum_t const sig, QParam const par); - -#else /* no event parameter */ - - #define QACTIVE_POST(me_, sig_) do { \ - QActive * const ao_ = QF_ACTIVE_CAST((me_)); \ - ((void)(*((QActiveVtable const *)(ao_->super.vptr))->post)( \ - ao_, QF_NO_MARGIN, (enum_t)(sig_))); \ - } while (false) - - #define QACTIVE_POST_X(me_, margin_, sig_) \ - ((*((QActiveVtable const *)((me_)->super.vptr))->post)((me_), \ - (margin_), (sig_))) - - bool QActive_postX_(QActive * const me, uint_fast8_t margin, - enum_t const sig); - - #define QACTIVE_POST_ISR(me_, sig_) do { \ - QActive * const ao_ = QF_ACTIVE_CAST((me_)); \ - ((void)(*((QActiveVtable const *)(ao_->super.vptr))->postISR)( \ - ao_, QF_NO_MARGIN, (enum_t)(sig_))); \ - } while (false) - - #define QACTIVE_POST_X_ISR(me_, margin_, sig_) \ - ((*((QActiveVtable const *)( \ - QF_ACTIVE_CAST((me_))->super.vptr))->postISR)( \ - QF_ACTIVE_CAST((me_)), (margin_), (enum_t)(sig_))) - - bool QActive_postXISR_(QActive * const me, uint_fast8_t margin, - enum_t const sig); -#endif - -#if (QF_TIMEEVT_CTR_SIZE != 0U) - - /*! Processes all armed time events at every clock tick. */ - void QF_tickXISR(uint_fast8_t const tickRate); - -#ifdef QF_TIMEEVT_PERIODIC - /*! Arm the QP-nano one-shot time event. - * @public @memberof QActive - */ - void QActive_armX(QActive * const me, uint_fast8_t const tickRate, - QTimeEvtCtr const nTicks, QTimeEvtCtr const interval); -#else - /*! Arm the QP-nano one-shot time event. - * @public @memberof QActive - */ - void QActive_armX(QActive * const me, uint_fast8_t const tickRate, - QTimeEvtCtr const nTicks); -#endif - - /*! Disarm a time event. Since the tick counter - * @public @memberof QActive - */ - void QActive_disarmX(QActive * const me, uint_fast8_t const tickRate); - -#endif /* (QF_TIMEEVT_CTR_SIZE != 0U) */ - - -/****************************************************************************/ -/* QF-nano protected methods ...*/ - -/*! QF-nano initialization. */ -void QF_init(uint_fast8_t maxActive); - -/*! QF-nano termination. */ -/** -* @description -* This function terminates QF and performs any necessary cleanup. -* In QF-nano this function is defined in the BSP. Many QF ports might not -* require implementing QF_stop() at all, because many embedded applications -* don't have anything to exit to. -*/ -void QF_stop(void); - -/*! Startup QF-nano callback. */ -/** -* @description -* The time line 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. -* -* @sa QF initialization example for ::QActiveCB. -*/ -void QF_onStartup(void); - -/*! Transfers control to QF-nano to run the application. */ -int_t QF_run(void); - - -/****************************************************************************/ -/*! QActive Control Block -* -* QActiveCB represents the read-only information that the QF-nano needs to -* manage the active object. QActiveCB objects are grouped in the array -* QF_active[], which typically can be placed in ROM. -* -* @usage -* The following example illustrates how to allocate and initialize the -* ::QActive control blocks in the array QF_active[]. -* @include qfn_main.c -*/ -typedef struct { - QActive *act; /*!< pointer to the active object structure */ - QEvt *queue; /*!< pointer to the event queue buffer */ - uint8_t qlen; /*!< the length of the queue ring buffer */ -} QActiveCB; - -/** active object control blocks */ -/*lint -save -e9067 MISRA-C:2012 Rule 8.11, extern array declared without size */ -extern QActiveCB const Q_ROM QF_active[]; -/*lint -restore */ - -/*! number of active objects in the application (# elements in QF_active[]) */ -extern uint_fast8_t QF_maxActive_; - -/*! Ready set of QF-nano. */ -extern uint_fast8_t volatile QF_readySet_; - -#ifndef QF_LOG2 - - /*! 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 nibble. - */ - extern uint8_t const Q_ROM QF_log2Lkup[16]; - -#endif - - -#ifdef QF_TIMEEVT_USAGE - - /*! Timer set of QF-nano. */ - extern uint_fast8_t volatile QF_timerSetX_[QF_MAX_TICK_RATE]; - -#endif /* QF_TIMEEVT_USAGE */ - - -/*! Lookup table for ~(1 << (n - 1)), where n is the index into the table. */ -extern uint8_t const Q_ROM QF_invPow2Lkup[9]; - - -/****************************************************************************/ -/*! This macro encapsulates accessing the active object queue at a -* given index, which violates MISRA-C 2004 rules 17.4(req) and 11.4(adv). -* This macro helps to localize this deviation. -*/ -#define QF_ROM_QUEUE_AT_(ao_, i_) (((QEvt *)Q_ROM_PTR((ao_)->queue))[(i_)]) - -/*! This macro encapsulates accessing the active object control block, -* which violates MISRA-C 2004 rule 11.4(adv). This macro helps to localize -* this deviation. -*/ -#define QF_ROM_ACTIVE_GET_(p_) ((QActive *)Q_ROM_PTR(QF_active[(p_)].act)) - -/*! This macro encapsulates the upcast to QActive* -* -* This macro encapsulates up-casting a pointer to a subclass of ::QActive -* to the base class ::QActive, which violates MISRA-C 2004 rule 11.4(adv). -* This macro helps to localize this deviation. -*/ -#define QF_ACTIVE_CAST(a_) ((QActive *)(a_)) - -#endif /* QFN_H */ - diff --git a/libraries/qpn_avr/src/qfn_port.h b/libraries/qpn_avr/src/qfn_port.h deleted file mode 100644 index bd5a9ed..0000000 --- a/libraries/qpn_avr/src/qfn_port.h +++ /dev/null @@ -1,87 +0,0 @@ -/** -* @file -* @brief QF-nano port AVR ATmega, QV-nano kernel, GNU-AVR toolset, Arduino -* @cond -****************************************************************************** -* Last Updated for Version: 6.8.2 -* Date of the Last Update: 2021-07-07 -* -* Q u a n t u m L e a P s -* ------------------------ -* Modern Embedded Software -* -* Copyright (C) 2005-2021 Quantum Leaps, LLC. All rights reserved. -* -* This program is open source software: you can redistribute it and/or -* modify it under the terms of the GNU General Public License as published -* by the Free Software Foundation, either version 3 of the License, or -* (at your option) any later version. -* -* Alternatively, this program may be distributed and modified under the -* terms of Quantum Leaps commercial licenses, which expressly supersede -* the GNU General Public License and are specifically designed for -* licensees interested in retaining the proprietary status of their code. -* -* This program is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU General Public License for more details. -* -* You should have received a copy of the GNU General Public License -* along with this program. If not, see . -* -* Contact information: -* -* -****************************************************************************** -* @endcond -*/ -#ifndef QFN_PORT_H -#define QFN_PORT_H - -/* GNU-AVR function attribute for "no-return" function */ -#define Q_NORETURN __attribute__ ((noreturn)) void - -/* GNU-AVR PROGMEM utilities are not compatible with GNU-AVR-C++! */ -//#define Q_ROM PROGMEM -//#define Q_ROM_BYTE(rom_var_) pgm_read_byte_near(&(rom_var_)) -//#define Q_ROM_PTR(rom_var_) pgm_read_word_near(&(rom_var_)) - -/* QF-nano interrupt disable/enable... */ -#define QF_INT_DISABLE() __asm__ __volatile__ ("cli" ::) -#define QF_INT_ENABLE() __asm__ __volatile__ ("sei" ::) - -/* QF-nano interrupt disabling policy for interrupt level */ -/*#define QF_ISR_NEST*/ /* nesting of ISRs not allowed */ - -/* QV sleep mode, see NOTE1... */ -#define QV_CPU_SLEEP() do { \ - __asm__ __volatile__ ("sei" ::); \ - __asm__ __volatile__ ("sleep" ::); \ - SMCR = 0U; \ -} while (false) - -/* QF CPU reset for AVR */ -#define QF_RESET() __asm__ __volatile__ ("jmp 0x0000" ::) - -#include /* Exact-width types. WG14/N843 C99 Standard */ -#include /* Boolean type. WG14/N843 C99 Standard */ -//#include /* Don't include, C++ not compatible with C */ - -#include "qepn.h" /* QEP-nano platform-independent public interface */ -#include "qfn.h" /* QF-nano platform-independent public interface */ -#include "qvn.h" /* QV-nano platform-independent public interface */ - -/***************************************************************************** -* NOTE1: -* As described in the "AVR Datasheet" in Section "Reset and Interrupt -* Handling", when using the SEI instruction to enable interrupts, the -* instruction following SEI will be executed before any pending interrupts. -* As the Datasheet shows in the assembly example, the pair of instructions -* SEI ; enable interrupts -* SLEEP ; go to the sleep mode -* executes ATOMICALLY, and so no interrupt can be serviced between these -* instructins. You should NEVER separate these two lines. -*/ - -#endif /* QFN_PORT_H */ diff --git a/libraries/qpn_avr/src/qpn.h b/libraries/qpn_avr/src/qpn.h deleted file mode 100644 index aedc9ce..0000000 --- a/libraries/qpn_avr/src/qpn.h +++ /dev/null @@ -1,105 +0,0 @@ -/** -* @file -* @brief QP-nano public interface including backwards-compatibility layer -* @cond -****************************************************************************** -* Last updated for version 6.8.0 -* Last updated on 2020-03-08 -* -* Q u a n t u m L e a P s -* ------------------------ -* Modern Embedded Software -* -* Copyright (C) 2005-2020 Quantum Leaps, LLC. All rights reserved. -* -* This program is open source software: you can redistribute it and/or -* modify it under the terms of the GNU General Public License as published -* by the Free Software Foundation, either version 3 of the License, or -* (at your option) any later version. -* -* Alternatively, this program may be distributed and modified under the -* terms of Quantum Leaps commercial licenses, which expressly supersede -* the GNU General Public License and are specifically designed for -* licensees interested in retaining the proprietary status of their code. -* -* This program is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU General Public License for more details. -* -* You should have received a copy of the GNU General Public License -* along with this program. If not, see . -* -* Contact information: -* -* -****************************************************************************** -* @endcond -*/ -#ifndef QPN_H -#define QPN_H - -/** -* @description -* This header file must be included directly or indirectly -* in all application modules (*.c files) that use QP-nano. -*/ - -#ifdef __cplusplus -extern "C" { -#endif - -#include "qpn_conf.h" /* QP-nano configuration file (from the application) */ -#include "qfn_port.h" /* QF-nano port from the port directory */ -#include "qassert.h" /* embedded systems-friendly assertions */ - - -/****************************************************************************/ -/* QP API compatibility layer */ -#ifndef QP_API_VERSION - -/*! Macro that specifies the backwards compatibility with the -* QP-nano API version. -*/ -/** -* @description -* For example, QP_API_VERSION=450 will cause generating the compatibility -* layer with QP-nano version 4.5.0 and newer, but not older than 4.5.0. -* QP_API_VERSION=0 causes generation of the compatibility layer "from the -* begining of time", which is the maximum backwards compatibilty. This is -* the default.@n -* @n -* Conversely, QP_API_VERSION=9999 means that no compatibility layer should -* be generated. This setting is useful for checking if an application -* complies with the latest QP-nano API. -*/ -#define QP_API_VERSION 0 - -#endif /* QP_API_VERSION */ - -/****************************************************************************/ -#if (QP_API_VERSION < 580U) - -/*! @deprecated QMActive Control Block; instead use: ::QActiveCB. */ -typedef QActiveCB QMActiveCB; - -/*! @deprecated QMActive; instead use: ::QActive. */ -typedef QActive QMActive; - -/*! @deprecated QMsm state machine; instead use: ::QHsm. */ -typedef QHsm QMsm; - -/*! @deprecated QMActive constructor; instead use: QActive_ctor() */ -#define QMActive_ctor QActive_ctor - -/*! @deprecated QMsm state machine constructor; instead use: QHsm_ctor() */ -#define QMsm_ctor QHsm_ctor - -#endif /* QP_API_VERSION < 580U */ -/****************************************************************************/ - -#ifdef __cplusplus -} -#endif - -#endif /* QPN_H */ diff --git a/libraries/qpn_avr/src/qpn_conf.h b/libraries/qpn_avr/src/qpn_conf.h deleted file mode 100644 index 7ef2b55..0000000 --- a/libraries/qpn_avr/src/qpn_conf.h +++ /dev/null @@ -1,42 +0,0 @@ -/***************************************************************************** -* Product: QP-nano configuration for Arduino-AVR -* Last updated for version 6.8.0 -* Last updated on 2020-03-08 -* -* Q u a n t u m L e a P s -* ------------------------ -* Modern Embedded Software -* -* Copyright (C) 2005-2020 Quantum Leaps, LLC. All rights reserved. -* -* This program is open source software: you can redistribute it and/or -* modify it under the terms of the GNU General Public License as published -* by the Free Software Foundation, either version 3 of the License, or -* (at your option) any later version. -* -* Alternatively, this program may be distributed and modified under the -* terms of Quantum Leaps commercial licenses, which expressly supersede -* the GNU General Public License and are specifically designed for -* licensees interested in retaining the proprietary status of their code. -* -* This program is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU General Public License for more details. -* -* You should have received a copy of the GNU General Public License -* along with this program. If not, see . -* -* Contact information: -* -* -*****************************************************************************/ -#ifndef QPN_CONF_H -#define QPN_CONF_H - -#define Q_PARAM_SIZE 4U -#define QF_MAX_TICK_RATE 1U -#define QF_TIMEEVT_CTR_SIZE 2U -#define QF_TIMEEVT_PERIODIC - -#endif /* QPN_CONF_H */ diff --git a/libraries/qpn_avr/src/qstamp.c b/libraries/qpn_avr/src/qstamp.c deleted file mode 100644 index d480168..0000000 --- a/libraries/qpn_avr/src/qstamp.c +++ /dev/null @@ -1,19 +0,0 @@ -/** -* @file -* @brief Application build time-stamp -* @note -* This module needs to be re-compiled in every new software build. To achive -* this, it is recommended to delete the object file (qstamp.o, or qstamp.obj) -* in the build directory before each build. (Most development tools allow -* you to specify a pre-build command, which is the ideal place to delete -* the qstamp object file.) -*/ - -extern char const Q_BUILD_DATE[12]; -extern char const Q_BUILD_TIME[9]; - -/*! the calendar date of the last translation of the form: "Mmm dd yyyy" */ -char const Q_BUILD_DATE[12] = __DATE__; - -/*! the time of the last translation of the form: "hh:mm:ss" */ -char const Q_BUILD_TIME[9] = __TIME__; diff --git a/libraries/qpn_avr/src/qvn.c b/libraries/qpn_avr/src/qvn.c deleted file mode 100644 index bb82349..0000000 --- a/libraries/qpn_avr/src/qvn.c +++ /dev/null @@ -1,161 +0,0 @@ -/** -* @file -* @brief QV-nano implementation. -* @ingroup qvn -* @cond -****************************************************************************** -* Last updated for version 6.8.0 -* Last updated on 2020-03-08 -* -* Q u a n t u m L e a P s -* ------------------------ -* Modern Embedded Software -* -* Copyright (C) 2005-2020 Quantum Leaps, LLC. All rights reserved. -* -* This program is open source software: you can redistribute it and/or -* modify it under the terms of the GNU General Public License as published -* by the Free Software Foundation, either version 3 of the License, or -* (at your option) any later version. -* -* Alternatively, this program may be distributed and modified under the -* terms of Quantum Leaps commercial licenses, which expressly supersede -* the GNU General Public License and are specifically designed for -* licensees interested in retaining the proprietary status of their code. -* -* This program is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU General Public License for more details. -* -* You should have received a copy of the GNU General Public License -* along with this program. If not, see . -* -* Contact information: -* -* -****************************************************************************** -* @endcond -*/ -#include "qpn_conf.h" /* QP-nano configuration file (from the application) */ -#include "qfn_port.h" /* QF-nano port from the port directory */ -#include "qassert.h" /* embedded systems-friendly assertions */ - -Q_DEFINE_THIS_MODULE("qvn") - -/* protection against including this source file in a wrong project */ -#ifndef QVN_H - #error "Source file included in a project NOT based on the QV-nano kernel" -#endif /* QVN_H */ - -/****************************************************************************/ -/** -* @description -* QF_run() is typically called from your startup code after you initialize -* the QF and start at least one active object with QActive_start(). -* This implementation of QF_run() is for the cooperative Vanilla kernel. -* -* @returns QF_run() typically does not return in embedded applications. -* However, when QP runs on top of an operating system, QF_run() might -* return and in this case the return represents the error code (0 for -* success). Typically the value returned from QF_run() is subsequently -* passed on as return from main(). -*/ -int_t QF_run(void) { - uint_fast8_t p; - QActive *a; - -#ifdef QF_MAX_ACTIVE /* deprecated constant provided? */ -#if (QF_MAX_ACTIVE < 1) || (8 < QF_MAX_ACTIVE) - #error "QF_MAX_ACTIVE not defined or out of range. Valid range is 1..8" -#endif - QF_maxActive_ = (uint_fast8_t)QF_MAX_ACTIVE; -#else - /** @pre the number of active objects must be initialized by calling: - * QF_init(Q_DIM(QF_active)); - */ - Q_REQUIRE_ID(100, (1U <= QF_maxActive_) - && (QF_maxActive_ <= 8U)); -#endif - - /* set priorities all registered active objects... */ - for (p = 1U; p <= QF_maxActive_; ++p) { - a = QF_ROM_ACTIVE_GET_(p); - - /* QF_active[p] must be initialized */ - Q_ASSERT_ID(810, a != (QActive *)0); - - a->prio = (uint8_t)p; /* set the priority of the active object */ - } - - /* trigger initial transitions in all registered active objects... */ - for (p = 1U; p <= QF_maxActive_; ++p) { - a = QF_ROM_ACTIVE_GET_(p); - QHSM_INIT(&a->super); /* take the initial transition in the SM */ - } - - QF_onStartup(); /* invoke startup callback */ - - /* the event loop of the cooperative QV-nano kernel... */ - QF_INT_DISABLE(); - for (;;) { - if (QF_readySet_ != 0U) { - QActiveCB const Q_ROM *acb; - -#ifdef QF_LOG2 - p = QF_LOG2(QF_readySet_); -#else - /* hi nibble non-zero? */ - if ((QF_readySet_ & 0xF0U) != 0U) { - p = (uint_fast8_t)Q_ROM_BYTE(QF_log2Lkup[QF_readySet_ >> 4]) - + 4U; - } - else { /* hi nibble of QF_readySet_ is zero */ - p = (uint_fast8_t)Q_ROM_BYTE(QF_log2Lkup[QF_readySet_]); - } -#endif /* QF_LOG2 */ - - acb = &QF_active[p]; - a = QF_ROM_ACTIVE_GET_(p); - - /* some unsuded events must be available */ - Q_ASSERT_ID(820, a->nUsed > 0U); - - --a->nUsed; - Q_SIG(a) = QF_ROM_QUEUE_AT_(acb, a->tail).sig; -#if (Q_PARAM_SIZE != 0U) - Q_PAR(a) = QF_ROM_QUEUE_AT_(acb, a->tail).par; -#endif - if (a->tail == 0U) { /* wrap around? */ - a->tail = Q_ROM_BYTE(acb->qlen); - } - --a->tail; - QF_INT_ENABLE(); - - QHSM_DISPATCH(&a->super); /* dispatch to the HSM (RTC step) */ - - QF_INT_DISABLE(); - /* empty queue? */ - if (a->nUsed == 0U) { - /* clear the bit corresponding to 'p' */ - QF_readySet_ &= (uint_fast8_t)~(1U << (p - 1U)); - } - } - else { - /* QV_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. QV_onIdle() MUST enable interrupts internally, - * perhaps at the same time as putting the CPU into a power-saving - * mode. - */ - QV_onIdle(); - - QF_INT_DISABLE(); - } - } -#ifdef __GNUC__ /* GNU compiler? */ - return 0; -#endif -} - diff --git a/libraries/qpn_avr/src/qvn.h b/libraries/qpn_avr/src/qvn.h deleted file mode 100644 index 34cc8ec..0000000 --- a/libraries/qpn_avr/src/qvn.h +++ /dev/null @@ -1,59 +0,0 @@ -/** -* @file -* @brief Public QV-nano interface. -* @ingroup qvn -* @cond -****************************************************************************** -* Last updated for version 6.6.0 -* Last updated on 2019-07-30 -* -* Q u a n t u m L e a P s -* ------------------------ -* Modern Embedded Software -* -* Copyright (C) 2005-2019 Quantum Leaps, LLC. All rights reserved. -* -* This program is open source software: you can redistribute it and/or -* modify it under the terms of the GNU General Public License as published -* by the Free Software Foundation, either version 3 of the License, or -* (at your option) any later version. -* -* Alternatively, this program may be distributed and modified under the -* terms of Quantum Leaps commercial licenses, which expressly supersede -* the GNU General Public License and are specifically designed for -* licensees interested in retaining the proprietary status of their code. -* -* This program is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU General Public License for more details. -* -* You should have received a copy of the GNU General Public License -* along with this program. If not, see . -* -* Contact information: -* -* -****************************************************************************** -* @endcond -*/ -#ifndef QVN_H -#define QVN_H - -/****************************************************************************/ -/*! Ready set of QV-nano. */ -extern uint_fast8_t volatile QV_readySet_; - -/*! QV idle callback (customized in BSPs for QK) -* -* QV_onIdle() is called continuously by the QV-nano scheduler. This callback -* gives the application an opportunity to enter a power-saving CPU mode, -* or perform some other idle processing. -* -* @note QV_onIdle() is invoked with interrupts disabled, but must return -* with interrupts enabled. This is in contrast to the callback QK_onIdle(), -* which is used by the preemptive QK-nano scheduler. -*/ -void QV_onIdle(void); - -#endif /* QVN_H */ From afa9abe33d12ed149a19480991f0fd25ed6fbf50 Mon Sep 17 00:00:00 2001 From: Peter Polidoro Date: Tue, 30 Jul 2024 14:31:09 -0400 Subject: [PATCH 3/3] Updated library files and deleted those not in v7.3.4 --- libraries/qpcpp_arm-cm/src/qassert.h | 340 ----- libraries/qpcpp_arm-cm/src/qep.hpp | 709 ---------- libraries/qpcpp_arm-cm/src/qep_hsm.cpp | 898 ++++++------ libraries/qpcpp_arm-cm/src/qep_msm.cpp | 793 ++++++----- libraries/qpcpp_arm-cm/src/qep_port.hpp | 51 - libraries/qpcpp_arm-cm/src/qequeue.hpp | 326 ++--- libraries/qpcpp_arm-cm/src/qf.hpp | 913 ------------ libraries/qpcpp_arm-cm/src/qf_act.cpp | 267 ++-- libraries/qpcpp_arm-cm/src/qf_actq.cpp | 628 ++++----- libraries/qpcpp_arm-cm/src/qf_defer.cpp | 248 ++-- libraries/qpcpp_arm-cm/src/qf_dyn.cpp | 580 ++++---- libraries/qpcpp_arm-cm/src/qf_mem.cpp | 442 +++--- libraries/qpcpp_arm-cm/src/qf_pkg.hpp | 173 --- libraries/qpcpp_arm-cm/src/qf_port.hpp | 155 --- libraries/qpcpp_arm-cm/src/qf_ps.cpp | 506 +++---- libraries/qpcpp_arm-cm/src/qf_qact.cpp | 191 ++- libraries/qpcpp_arm-cm/src/qf_qeq.cpp | 407 +++--- libraries/qpcpp_arm-cm/src/qf_qmact.cpp | 154 +-- libraries/qpcpp_arm-cm/src/qf_time.cpp | 810 +++++------ libraries/qpcpp_arm-cm/src/qmpool.hpp | 251 ++-- libraries/qpcpp_arm-cm/src/qpcpp.hpp | 256 ++-- libraries/qpcpp_arm-cm/src/qpset.hpp | 197 --- libraries/qpcpp_arm-cm/src/qs.cpp | 1500 ++++++++++---------- libraries/qpcpp_arm-cm/src/qs.hpp | 1693 +++++++++++------------ libraries/qpcpp_arm-cm/src/qs_dummy.hpp | 131 +- libraries/qpcpp_arm-cm/src/qs_fp.cpp | 136 +- libraries/qpcpp_arm-cm/src/qs_pkg.hpp | 274 ---- libraries/qpcpp_arm-cm/src/qs_port.hpp | 102 +- libraries/qpcpp_arm-cm/src/qs_rx.cpp | 1284 ++++++++--------- libraries/qpcpp_arm-cm/src/qstamp.cpp | 21 - libraries/qpcpp_arm-cm/src/qstamp.hpp | 54 +- libraries/qpcpp_arm-cm/src/qv.cpp | 444 +++--- libraries/qpcpp_arm-cm/src/qv.hpp | 226 +-- libraries/qpcpp_arm-cm/src/qv_port.cpp | 335 +++-- libraries/qpcpp_arm-cm/src/qv_port.hpp | 78 -- qpcpp-file-list.txt | 20 +- version-6.9.4 | 7 - version-7.3.4 | 5 + 38 files changed, 6304 insertions(+), 9301 deletions(-) delete mode 100644 libraries/qpcpp_arm-cm/src/qassert.h delete mode 100644 libraries/qpcpp_arm-cm/src/qep.hpp delete mode 100644 libraries/qpcpp_arm-cm/src/qep_port.hpp delete mode 100644 libraries/qpcpp_arm-cm/src/qf.hpp delete mode 100644 libraries/qpcpp_arm-cm/src/qf_pkg.hpp delete mode 100644 libraries/qpcpp_arm-cm/src/qf_port.hpp delete mode 100644 libraries/qpcpp_arm-cm/src/qpset.hpp delete mode 100644 libraries/qpcpp_arm-cm/src/qs_pkg.hpp delete mode 100644 libraries/qpcpp_arm-cm/src/qstamp.cpp delete mode 100644 libraries/qpcpp_arm-cm/src/qv_port.hpp delete mode 100644 version-6.9.4 create mode 100644 version-7.3.4 diff --git a/libraries/qpcpp_arm-cm/src/qassert.h b/libraries/qpcpp_arm-cm/src/qassert.h deleted file mode 100644 index a88bbfa..0000000 --- a/libraries/qpcpp_arm-cm/src/qassert.h +++ /dev/null @@ -1,340 +0,0 @@ -/** -* @file -* @brief Customizable and memory-efficient assertions for embedded systems -* @cond -****************************************************************************** -* Last updated for version 6.8.2 -* Last updated on 2020-07-07 -* -* Q u a n t u m L e a P s -* ------------------------ -* Modern Embedded Software -* -* Copyright (C) 2005-2020 Quantum Leaps, LLC. All rights reserved. -* -* This program is open source software: you can redistribute it and/or -* modify it under the terms of the GNU General Public License as published -* by the Free Software Foundation, either version 3 of the License, or -* (at your option) any later version. -* -* Alternatively, this program may be distributed and modified under the -* terms of Quantum Leaps commercial licenses, which expressly supersede -* the GNU General Public License and are specifically designed for -* licensees interested in retaining the proprietary status of their code. -* -* This program is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU General Public License for more details. -* -* You should have received a copy of the GNU General Public License -* along with this program. If not, see . -* -* Contact information: -* -* -****************************************************************************** -* @endcond -*/ -#ifndef QASSERT_H -#define QASSERT_H - -/** -* @note -* This header file can be used in C, C++, and mixed C/C++ programs. -* -* @note The preprocessor switch #Q_NASSERT disables checking assertions. -* However, it is generally __not__ advisable to disable assertions, -* __especially__ in the production code. Instead, the assertion handler -* Q_onAssert() should be very carefully designed and tested. -*/ - -#ifdef Q_NASSERT /* Q_NASSERT defined--assertion checking disabled */ - - /* provide dummy (empty) definitions that don't generate any code... */ - #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 */ - -#ifndef QP_VERSION /* is quassert.h used outside QP? */ - - /* provide typedefs so that qassert.h could be used "standalone"... */ - - /*! typedef for character strings. */ - /** - * @description - * 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 assertions-ids and line numbers in assertions. */ - /** - * @description - * This typedef specifies integer type for exclusive use in assertions. - * Use of this type, rather than plain 'int', is in compliance - * with the MISRA-C 2004 Rules 6.1(req), 6.3(adv). - */ - typedef int int_t; - -#endif - - /*! Define the file name (with `__FILE__`) for assertions in this file. */ - /** - * @description - * Macro to be placed at the top of each C/C++ module to define the - * single instance of the file name string to be used in reporting - * assertions in this module. - * - * @note The file name string literal is defined by means of the standard - * preprocessor macro `__FILE__`. However, please note that, depending - * on the compiler, the `__FILE__` macro might contain the whole path name - * to the file, which might be inconvenient to log assertions. - * @note This macro should __not__ be terminated by a semicolon. - * @sa Q_DEFINE_THIS_MODULE() - */ - #define Q_DEFINE_THIS_FILE \ - static char_t const Q_this_module_[] = __FILE__; - - /*! Define the user-specified module name for assertions in this file. */ - /** - * @description - * Macro to be placed at the top of each C/C++ module to define the - * single instance of the module name string to be used in reporting - * assertions in this module. This macro takes the user-supplied parameter - * @p name_ instead of `__FILE__` to precisely control the name of the - * module. - * - * @param[in] name_ string constant representing the module name - * - * @note This macro should __not__ be terminated by a semicolon. - */ - #define Q_DEFINE_THIS_MODULE(name_) \ - static char_t const Q_this_module_[] = name_; - - /*! General purpose assertion. */ - /** - * @description - * Makes sure the @p test_ parameter is TRUE. Calls the Q_onAssert() - * callback if the @p test_ expression evaluates to FALSE. This - * macro identifies the assertion location within the file by means - * of the standard `__LINE__` macro. - * - * @param[in] test_ Boolean expression - * - * @note the @p test_ is __not__ evaluated if assertions are disabled - * with the #Q_NASSERT switch. - */ - #define Q_ASSERT(test_) ((test_) \ - ? (void)0 : Q_onAssert(&Q_this_module_[0], __LINE__)) - - /*! General purpose assertion with user-specified assertion-id. */ - /** - * @description - * Makes sure the @p test_ parameter is TRUE. Calls the Q_onAssert() - * callback if the @p test_ evaluates to FALSE. This assertion takes the - * user-supplied parameter @p id_ to identify the location of this - * assertion within the file. This avoids the volatility of using line - * numbers, which change whenever a line of code is added or removed - * upstream from the assertion. - * - * @param[in] id_ ID number (unique within the module) of the assertion - * @param[in] test_ Boolean expression - * - * @note the @p test_ expression is __not__ evaluated if assertions are - * disabled with the #Q_NASSERT switch. - */ - #define Q_ASSERT_ID(id_, test_) ((test_) \ - ? (void)0 : Q_onAssert(&Q_this_module_[0], (id_))) - - /*! General purpose assertion that __always__ evaluates the @p test_ - * expression. */ - /** - * @description - * Like the Q_ASSERT() macro, except it __always__ evaluates the @p test_ - * expression even when assertions are disabled with the #Q_NASSERT macro. - * However, when the #Q_NASSERT macro is defined, the Q_onAssert() - * callback is __not__ called, even if @p test_ evaluates to FALSE. - * - * @param[in] test_ Boolean expression (__always__ evaluated) - * - * @sa #Q_ALLEGE_ID - */ - #define Q_ALLEGE(test_) Q_ASSERT(test_) - - /*! General purpose assertion with user-specified assertion-id that - * __always__ evaluates the @p test_ expression. */ - /** - * @description - * Like the Q_ASSERT_ID() macro, except it __always__ evaluates the - * @p test_ expression even when assertions are disabled with the - * #Q_NASSERT macro. However, when the #Q_NASSERT macro is defined, the - * Q_onAssert() callback is __not__ called, even if @p test_ evaluates - * to FALSE. - * - * @param[in] id_ ID number (unique within the module) of the assertion - * @param[in] test_ Boolean expression - */ - #define Q_ALLEGE_ID(id_, test_) Q_ASSERT_ID((id_), (test_)) - - /*! Assertion for a wrong path through the code. */ - /** - * @description - * Calls the Q_onAssert() callback if ever executed. - * - * @note Does noting if assertions are disabled with the #Q_NASSERT switch. - */ - #define Q_ERROR() \ - Q_onAssert(&Q_this_module_[0], __LINE__) - - /*! Assertion with user-specified assertion-id for a wrong path. */ - /** - * @description - * Calls the Q_onAssert() callback if ever executed. This assertion - * takes the user-supplied parameter @p id_ to identify the location of - * this assertion within the file. This avoids the volatility of using - * line numbers, which change whenever a line of code is added or removed - * upstream from the assertion. - * - * @param[in] id_ ID number (unique within the module) of the assertion - * - * @note Does noting if assertions are disabled with the #Q_NASSERT switch. - */ - #define Q_ERROR_ID(id_) \ - Q_onAssert(&Q_this_module_[0], (id_)) - -#endif /* Q_NASSERT */ - -/****************************************************************************/ -#ifdef __cplusplus - extern "C" { -#endif - -#ifndef Q_NORETURN - /*! no-return function specifier */ - #define Q_NORETURN void -#endif /* Q_NORETURN */ - -/*! Callback function invoked in case of any assertion failure. */ -/** -* @description -* This is an application-specific callback function needs to be defined in -* the application to perform the clean system shutdown and perhaps a reset. -* -* @param[in] module name of the file/module in which the assertion failed -* (constant, zero-terminated C string) -* @param[in] location location of the assertion within the module. This could -* be a line number or a user-specified ID-number. -* -* @note This callback function should _not_ return, as continuation after -* an assertion failure does not make sense. -* -* @note The Q_onAssert() function is the last line of defense after the -* system failure and its implementation shouild be very __carefully__ -* designed and __tested__ under various fault conditions, including but -* not limited to: stack overflow, stack corruption, or calling Q_onAssert() -* from an interrupt. -* -* @note It is typically a __bad idea__ to implement Q_onAssert() as an -* endless loop that ties up the CPU. During debuggin, Q_onAssert() is an -* ideal place to put a breakpoint. -* -* Called by the following macros: #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. -*/ -Q_NORETURN Q_onAssert(char_t const * const module, int_t const location); - -#ifdef __cplusplus - } -#endif - -/*! Assertion for checking preconditions. */ -/** -* @description -* This macro is equivalent to #Q_ASSERT, except the name provides a better -* documentation of the intention of this assertion. -* -* @param[in] test_ Boolean expression -*/ -#define Q_REQUIRE(test_) Q_ASSERT(test_) - -/*! Assertion for checking preconditions with user-specified assertion-id. */ -/** -* @description -* Equivalent to #Q_ASSERT_ID, except the macro name provides a better -* documentation of the intention of this assertion. -* -* @param[in] id_ ID number (unique within the module) of the assertion -* @param[in] test_ Boolean expression -*/ -#define Q_REQUIRE_ID(id_, test_) Q_ASSERT_ID((id_), (test_)) - -/*! Assertion for checking postconditions. */ -/** Equivalent to #Q_ASSERT, except the macro name provides a better -* documentation of the intention of this assertion. -* -* @param[in] test_ Boolean expression -*/ -#define Q_ENSURE(test_) Q_ASSERT(test_) - -/*! Assertion for checking postconditions with user-specified assertion-id. */ -/** -* @description -* Equivalent to #Q_ASSERT_ID, except the name provides a better documentation -* of the intention of this assertion. -* -* @param[in] id_ ID number (unique within the module) of the assertion -* @param[in] test_ Boolean expression -*/ -#define Q_ENSURE_ID(id_, test_) Q_ASSERT_ID((id_), (test_)) - -/*! Assertion for checking invariants. */ -/** -* @description -* Equivalent to #Q_ASSERT, except the macro name provides a better -* documentation of the intention of this assertion. -* -* @param[in] test_ Boolean expression -*/ -#define Q_INVARIANT(test_) Q_ASSERT(test_) - -/*! Assertion for checking invariants with user-specified assertion-id. */ -/** -* @description -* Equivalent to #Q_ASSERT_ID, except the macro name provides a better -* documentation of the intention of this assertion. -* -* @param[in] id_ ID number (unique within the module) of the assertion -* @param[in] test_ Boolean expression -*/ -#define Q_INVARIANT_ID(id_, test_) Q_ASSERT_ID((id_), (test_)) - -/*! Static (compile-time) assertion. */ -/** -* @description -* This type of assertion deliberately causes a compile-time error when -* the @p test_ evaluates to FALSE. The macro exploits the fact that in C/C++ -* a dimension of an array cannot be negative. The compile-time assertion has -* no runtime side effects. -* -* @param[in] test_ Compile-time Boolean expression -*/ -#define Q_ASSERT_STATIC(test_) \ - extern int_t Q_assert_static[(test_) ? 1 : -1] - -#define Q_ASSERT_COMPILE(test_) Q_ASSERT_STATIC(test_) - -/*! Helper macro to calculate static dimension of a 1-dim @p array_ */ -#define Q_DIM(array_) (sizeof(array_) / sizeof((array_)[0U])) - -#endif /* QASSERT_H */ - diff --git a/libraries/qpcpp_arm-cm/src/qep.hpp b/libraries/qpcpp_arm-cm/src/qep.hpp deleted file mode 100644 index ac5bac6..0000000 --- a/libraries/qpcpp_arm-cm/src/qep.hpp +++ /dev/null @@ -1,709 +0,0 @@ -/// @file -/// @brief QEP/C++ platform-independent public interface. -/// @ingroup qep -/// @cond -///*************************************************************************** -/// Last updated for version 6.9.4 -/// Last updated on 2022-01-14 -/// -/// Q u a n t u m L e a P s -/// ------------------------ -/// Modern Embedded Software -/// -/// Copyright (C) 2005-2022 Quantum Leaps. All rights reserved. -/// -/// This program is open source software: you can redistribute it and/or -/// modify it under the terms of the GNU General Public License as published -/// by the Free Software Foundation, either version 3 of the License, or -/// (at your option) any later version. -/// -/// Alternatively, this program may be distributed and modified under the -/// terms of Quantum Leaps commercial licenses, which expressly supersede -/// the GNU General Public License and are specifically designed for -/// licensees interested in retaining the proprietary status of their code. -/// -/// This program is distributed in the hope that it will be useful, -/// but WITHOUT ANY WARRANTY; without even the implied warranty of -/// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -/// GNU General Public License for more details. -/// -/// You should have received a copy of the GNU General Public License -/// along with this program. If not, see . -/// -/// Contact information: -/// -/// -///*************************************************************************** -/// @endcond - -#ifndef QEP_HPP -#define QEP_HPP - -//**************************************************************************** -//! The current QP version as a decimal constant XXYZ, where XX is a 2-digit -// major version number, Y is a 1-digit minor version number, and Z is -// a 1-digit release number. -#define QP_VERSION 694U - -//! The current QP version number string of the form XX.Y.Z, where XX is -// a 2-digit major version number, Y is a 1-digit minor version number, -// and Z is a 1-digit release number. -#define QP_VERSION_STR "6.9.4" - -//! Encrypted current QP release (6.9.4) and date (2022-01-14) -#define QP_RELEASE 0x7CCD4229U - - -//**************************************************************************** -#ifndef Q_SIGNAL_SIZE - //! The size (in bytes) of the signal of an event. Valid values: - //! 1U, 2U, or 4U; default 2U - /// @description - /// This macro can be defined in the QEP port file (qep_port.hpp) to - /// configure the QP::QSignal type. When the macro is not defined, the - /// default of 2 bytes is applied. - #define Q_SIGNAL_SIZE 2U -#endif - -//**************************************************************************** -// Aliases for basic numerical types; MISRA-C++ 2008 rule 3-9-2(req). - -//! alias for characters and character strings. -/// @description -/// This alias 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). -using char_t = char; - -//! alias for line numbers in assertions and return from QF::run() -using int_t = int; - -//! alias for enumerations used for event signals -using enum_t = int; - -//! alias for 32-bit IEEE 754 floating point numbers -/// @note -/// QP does not use floating-point types anywhere in the internal -/// implementation, except in QS software tracing, where utilities for -/// output of floating-point numbers are provided for application-level -/// trace records. -/// -using float32_t = float; - -//! alias for 64-bit IEEE 754 floating point numbers -/// @note -/// QP does not use floating-point types anywhere in the internal -/// implementation, except in QS software tracing, where utilities for -/// output of floating-point numbers are provided for application-level -/// trace records. -/// -using float64_t = double; - -//! Perform downcast of an event onto a subclass of QEvt @p class_ -/// @description -/// 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(e)) - -//! Perform cast from unsigned integer @p uint_ to pointer of type @p type_. -/// @description -/// This macro encapsulates the cast to (type_ *), which QP ports or -/// application might use to access embedded hardware registers. -/// Such uses can trigger PC-Lint "Note 923: cast from int to pointer" -/// and this macro helps to encapsulate this deviation. -/// -#define Q_UINT2PTR_CAST(type_, uint_) (reinterpret_cast(uint_)) - -//! Initializer of static constant QEvt instances -/// @description -/// This macro encapsulates the ugly casting of enumerated signals -/// to QSignal and constants for QEvt.poolID and QEvt.refCtr_. -/// -#define QEVT_INITIALIZER(sig_) { static_cast(sig_), 0U, 0U } - - -//**************************************************************************** -//! namespace associated with the QP/C++ framework -/// @ingroup qep qf qs qv qk qxk -namespace QP { - - //! the current QP version number string based on QP_VERSION_STR - constexpr char_t const versionStr[]{QP_VERSION_STR}; - -#if (Q_SIGNAL_SIZE == 1U) - using QSignal = std::uint8_t; -#elif (Q_SIGNAL_SIZE == 2U) - //! QSignal represents the signal of an event. - /// @description - /// The relationship between an event and a signal is as follows. A signal - /// in UML is the specification of an asynchronous stimulus that triggers - /// reactions, and as such is an essential part of an event. (The signal - /// conveys the type of the occurrence--what happened?) However, an event - /// can also contain additional quantitative information about the - /// occurrence in form of event parameters. - using QSignal = std::uint16_t; -#elif (Q_SIGNAL_SIZE == 4U) - using QSignal = std::uint32_t; -#else - #error "Q_SIGNAL_SIZE defined incorrectly, expected 1U, 2U, or 4U" -#endif - -#ifdef Q_EVT_CTOR // Provide the constructor for the QEvt class? - - //************************************************************************ - class QEvt { - public: - //! public constructor (overload for dynamic events) - QEvt(QSignal const s) noexcept - : sig(s) - // poolId_/refCtr_ intentionally uninitialized - {} - - //! public constructor (overload for static events) - enum StaticEvt : std::uint8_t { STATIC_EVT }; - constexpr QEvt(QSignal const s, StaticEvt /*dummy*/) noexcept - : sig(s), - poolId_(0U), - refCtr_(0U) - {} - -#ifdef Q_EVT_VIRTUAL - //! virtual destructor - virtual ~QEvt() noexcept {} -#endif // Q_EVT_VIRTUAL - - public: - QSignal sig; //!< signal of the event instance - - private: - std::uint8_t poolId_; //!< pool ID (0 for static event) - std::uint8_t volatile refCtr_; //!< reference counter - - friend class QF; - friend class QActive; - friend class QMActive; - friend class QTimeEvt; - friend class QEQueue; - friend class QTicker; - friend class QXThread; - friend std::uint8_t QF_EVT_POOL_ID_ (QEvt const * const e) noexcept; - friend std::uint8_t QF_EVT_REF_CTR_ (QEvt const * const e) noexcept; - friend void QF_EVT_REF_CTR_INC_(QEvt const * const e) noexcept; - friend void QF_EVT_REF_CTR_DEC_(QEvt const * const e) noexcept; - }; - -#else // QEvt is a POD (Plain Old Datatype) - - //************************************************************************ - //! QEvt base class. - /// @description - /// QEvt represents events without parameters and serves as the - /// base class for derivation of events with parameters. - /// - /// @usage - /// The following example illustrates how to add an event parameter by - /// inheriting from the QEvt class. - /// @include qep_qevt.cpp - struct QEvt { - QSignal sig; //!< signal of the event instance - std::uint8_t poolId_; //!< pool ID (0 for static event) - std::uint8_t volatile refCtr_; //!< reference counter - }; - -#endif // Q_EVT_CTOR - -// forward declarations... -struct QMState; -struct QMTranActTable; -class QXThread; - -//! Type returned from state-handler functions -using QState = std::uint_fast8_t; - -//! Pointer to state-handler function -using QStateHandler = QState (*)(void * const me, QEvt const * const e); - -//! Pointer to an action-handler function -using QActionHandler = QState (*)(void * const me); - -//! Pointer to a thread-handler function -using QXThreadHandler = void (*)(QXThread * const me); - -//! Attribute of for the QHsm class (Hierarchical State Machine). -/// @description -/// This union represents possible values stored in the 'state' and 'temp' -/// attributes of the QHsm and QMsm classes. -union QHsmAttr { - QStateHandler fun; //!< pointer to a state handler function - QActionHandler act; //!< pointer to an action-handler function - QXThreadHandler thr; //!< pointer to an thread-handler function - QMState const *obj; //!< pointer to QMState object - QMTranActTable const *tatbl; //!< transition-action table -}; - - -//**************************************************************************** -//! Hierarchical State Machine base class -/// -/// @description -/// QHsm represents a Hierarchical State Machine (HSM) with full support for -/// hierarchical nesting of states, entry/exit actions, and initial -/// transitions in any composite state. QHsm inherits QMsm without adding -/// new attributes, so it takes the same amount of RAM as QMsm. -/// @n -/// QHsm is also the base class for the QMsm state machine, which provides -/// a superior efficiency, but requries the use of the QM modeling tool to -/// generate code. -/// -/// @note -/// QHsm is not intended to be instantiated directly, but rather serves as -/// the base class for derivation of state machines in the application code. -/// -/// @usage -/// The following example illustrates how to derive a state machine class -/// from QHsm. -/// @include qep_qhsm.cpp -/// -class QHsm { - QHsmAttr m_state; //!< current active state (state-variable) - QHsmAttr m_temp; //!< temporary: transition chain, target state, etc. - -public: - //! virtual destructor - virtual ~QHsm(); - - //! executes the top-most initial transition in QP::QHsm - virtual void init(void const * const e, - std::uint_fast8_t const qs_id); - - //! overloaded init(qs_id) - virtual void init(std::uint_fast8_t const qs_id) { - this->init(nullptr, qs_id); - } - - //! Dispatches an event to QHsm - virtual void dispatch(QEvt const * const e, - std::uint_fast8_t const qs_id); - - //! Tests if a given state is part of the current active state - //! configuration - bool isIn(QStateHandler const s) noexcept; - - //! Obtain the current state (state handler function) - //! @note used in the QM code generation - QStateHandler state(void) const noexcept { - return m_state.fun; - } - - //! Obtain the current active child state of a given parent - //! @note used in the QM code generation - QStateHandler childState(QStateHandler const parent) noexcept; - - //! the top-state. - static QState top(void * const me, QEvt const * const e) noexcept; - -protected: - //! Protected constructor of QHsm. - explicit QHsm(QStateHandler const initial) noexcept; - -public: -// facilities for the QHsm implementation strategy... - //! event passed to the superstate to handle - static constexpr QState Q_RET_SUPER {static_cast(0)}; - - //! event passed to submachine superstate - static constexpr QState Q_RET_SUPER_SUB {static_cast(1)}; - - //! event unhandled due to a guard evaluating to 'false' - static constexpr QState Q_RET_UNHANDLED {static_cast(2)}; - - //! event handled (internal transition) - static constexpr QState Q_RET_HANDLED {static_cast(3)}; - - //! event silently ignored (bubbled up to top) - static constexpr QState Q_RET_IGNORED {static_cast(4)}; - - //! state entry action executed - static constexpr QState Q_RET_ENTRY {static_cast(5)}; - - //! state exit action executed - static constexpr QState Q_RET_EXIT {static_cast(6)}; - - //! return value without any effect - static constexpr QState Q_RET_NULL {static_cast(7)}; - - //! regular transition taken - static constexpr QState Q_RET_TRAN {static_cast(8)}; - - //! initial transition taken - static constexpr QState Q_RET_TRAN_INIT {static_cast(9)}; - - //! entry-point transition into a submachine - static constexpr QState Q_RET_TRAN_EP {static_cast(10)}; - - //! transition to history of a given state - static constexpr QState Q_RET_TRAN_HIST {static_cast(11)}; - - //! exit-point transition out of a submachine - static constexpr QState Q_RET_TRAN_XP {static_cast(12)}; - -protected: - //! Helper function to specify a state transition - QState tran(QStateHandler const target) noexcept { - m_temp.fun = target; - return Q_RET_TRAN; - } - - //! Helper function to specify a transition to history - QState tran_hist(QStateHandler const hist) noexcept { - m_temp.fun = hist; - return Q_RET_TRAN_HIST; - } - - //! Helper function to specify the superstate of a given state - QState super(QStateHandler const superstate) noexcept { - m_temp.fun = superstate; - return Q_RET_SUPER; - } - - enum ReservedHsmSignals : QSignal { - Q_ENTRY_SIG = 1, //!< signal for entry actions - Q_EXIT_SIG, //!< signal for exit actions - Q_INIT_SIG //!< signal for nested initial transitions - }; - -// protected facilities for the QMsm implementation strategy... - //! Helper function to specify a regular state transition - //! in a QM state-handler - QState qm_tran(void const * const tatbl) noexcept { - m_temp.tatbl = static_cast(tatbl); - return Q_RET_TRAN; - } - - //! Helper function to specifiy a transition to history - //! in a QM state-handler - QState qm_tran_hist(QMState const * const hist, - void const * const tatbl) noexcept - { - m_state.obj = hist; - m_temp.tatbl = static_cast(tatbl); - return Q_RET_TRAN_HIST; - } - - //! Helper function to specify an initial state transition - //! in a QM state-handler - QState qm_tran_init(void const * const tatbl) noexcept { - m_temp.tatbl = static_cast(tatbl); - return Q_RET_TRAN_INIT; - } - - //! Helper function to specify a transition to an entry point - //! to a submachine state in a QM state-handler - QState qm_tran_ep(void const * const tatbl) noexcept { - m_temp.tatbl = static_cast(tatbl); - return Q_RET_TRAN_EP; - } - - //! Helper function to specify a transition to an exit point - //! from a submachine state in a QM state-handler - QState qm_tran_xp(QActionHandler const xp, - void const *const tatbl) noexcept - { - m_state.act = xp; - m_temp.tatbl = static_cast(tatbl); - return Q_RET_TRAN_XP; - } - -#ifdef Q_SPY - //! Helper function to specify a state entry in a QM state-handler - QState qm_entry(QMState const * const s) noexcept { - m_temp.obj = s; - return Q_RET_ENTRY; - } - - //! Helper function to specify a state exit in a QM state-handler - QState qm_exit(QMState const * const s) noexcept { - m_temp.obj = s; - return Q_RET_EXIT; - } - - //! Get the current state handler of the HSM - virtual QStateHandler getStateHandler() noexcept; -#else - //! Helper function to specify a state entry in a QM state-handler - QState qm_entry(QMState const * const s) noexcept { - (void)s; - return Q_RET_ENTRY; - } - - //! Helper function to specify a state exit in a QM state-handler - QState qm_exit(QMState const * const s) noexcept { - (void)s; - return Q_RET_EXIT; - } -#endif - - //! Helper function to specify a submachine exit in a QM state-handler - QState qm_sm_exit(QMState const * const s) noexcept { - m_temp.obj = s; - return Q_RET_EXIT; - } - - //! Helper function to call in a QM state-handler when it passes - //! the event to the host submachine state to handle an event. - QState qm_super_sub(QMState const * const s) noexcept { - m_temp.obj = s; - return Q_RET_SUPER_SUB; - } - -private: - //!< maximum nesting depth of states in HSM - static constexpr std::int_fast8_t MAX_NEST_DEPTH_{6}; - - //! internal helper function to take a transition in QP::QHsm - std::int_fast8_t hsm_tran(QStateHandler (&path)[MAX_NEST_DEPTH_], - std::uint_fast8_t const qs_id); - - friend class QMsm; - friend class QActive; - friend class QMActive; - friend class QF; - friend class QS; - friend class QXK; - friend class QXThread; - friend class QXMutex; - friend class QXSemaphore; -#ifdef Q_UTEST - friend class QActiveDummy; -#endif // Q_UTEST -}; - -//**************************************************************************** -//! QM State Machine implementation strategy -/// @description -/// QMsm (QM State Machine) provides a more efficient state machine -/// implementation strategy than QHsm, but requires the use of the QM -/// modeling tool, but are the fastest and need the least run-time -/// support (the smallest event-processor taking up the least code space). -/// -/// @note -/// QMsm is not intended to be instantiated directly, but rather serves as -/// the base class for derivation of state machines in the application code. -/// -/// @usage -/// The following example illustrates how to derive a state machine class -/// from QMsm. Please note that the QMsm member 'super' is defined as the -/// _first_ member of the derived struct. -/// @include qep_qmsm.cpp -/// -class QMsm : public QHsm { -public: - //! Performs the second step of SM initialization by triggering - /// the top-most initial transition. - void init(void const * const e, - std::uint_fast8_t const qs_id) override; - void init(std::uint_fast8_t const qs_id) override { - this->init(nullptr, qs_id); - } - - //! Dispatches an event to a HSM - void dispatch(QEvt const * const e, - std::uint_fast8_t const qs_id) override; - - //! Tests if a given state is part of the active state configuration - bool isInState(QMState const * const st) const noexcept; - - //! Return the current active state object (read only) - QMState const *stateObj(void) const noexcept { - return m_state.obj; - } - - //! Obtain the current active child state of a given parent (read only) - QMState const *childStateObj(QMState const * const parent) const noexcept; - -protected: - //! Protected constructor - explicit QMsm(QStateHandler const initial) noexcept; - -#ifdef Q_SPY - //! Get the current state handler of the QMsm - QStateHandler getStateHandler() noexcept override; -#endif - -private: - //! disallow inhertited isIn() function in QP::QMsm and subclasses - //! @sa QP::QMsm::isInState() - bool isIn(QStateHandler const s) noexcept = delete; - - //! disallow inhertited state() function in QP::QMsm and subclasses - //! @sa QP::QMsm::stateObj() - QStateHandler state(void) const noexcept = delete; - - //! disallow inhertited childState() function in QP::QMsm and subclasses - //! @sa QP::QMsm::childStateObj() - QStateHandler childState(QStateHandler const parent) noexcept = delete; - - //! disallow inhertited top() function in QP::QMsm and subclasses - //! @sa QP::QMsm::msm_top_s - static QState top(void * const me, QEvt const * const e) noexcept = delete; - - //! Internal helper function to execute a transition-action table - QState execTatbl_(QMTranActTable const * const tatbl, - std::uint_fast8_t const qs_id); - - //! Internal helper function to exit current state to transition source - void exitToTranSource_(QMState const *s, - QMState const * const ts, - std::uint_fast8_t const qs_id); - - //! Internal helper function to enter state history - QState enterHistory_(QMState const * const hist, - std::uint_fast8_t const qs_id); - - //! maximum depth of implemented entry levels for transitions to history - static constexpr std::int_fast8_t MAX_ENTRY_DEPTH_ {4}; - - //! the top state object for the QMsm - static QMState const msm_top_s; - - friend class QMActive; -}; - -//! State object for the QP::QMsm class (QM State Machine). -/// @description -/// This class groups together the attributes of a QP::QMsm state, such as -/// the parent state (state nesting), the associated state handler function -/// and the exit action handler function. These attributes are used inside -/// the QP::QMsm::dispatch() and QP::QMsm::init() functions. -/// -/// @attention -/// The QP::QMState class is only intended for the QM code generator and -/// should not be used in hand-crafted code. -struct QMState { - QMState const *superstate; //!< superstate of this state - QStateHandler const stateHandler; //!< state handler function - QActionHandler const entryAction; //!< entry action handler function - QActionHandler const exitAction; //!< exit action handler function - QActionHandler const initAction; //!< init action handler function -}; - -//! Transition-Action Table for the QP::QMsm State Machine. -struct QMTranActTable { - QMState const *target; - QActionHandler const act[1]; -}; - - -//**************************************************************************** -//! Provides miscellaneous QEP services. -class QEP { -public: - //! get the current QEP version number string of the form "X.Y.Z" - static char_t const *getVersion(void) noexcept { - return versionStr; - } -}; - -//! Offset or the user signals -constexpr enum_t Q_USER_SIG {4}; - -} // namespace QP - -//**************************************************************************** -// Macros for coding QHsm-style state machines... - -//! Macro to generate a declaration of a state-handler, state-caller and -//! a state-object for a given state in a subclass of QP::QHsm. -#define Q_STATE_DECL(state_) \ - QP::QState state_ ## _h(QP::QEvt const * const e); \ - static QP::QState state_(void * const me, QP::QEvt const * const e) - -//! Macro to generate a definition of a state-handler for a given state -//! in a subclass of QP::QHsm. -#define Q_STATE_DEF(subclass_, state_) \ - QP::QState subclass_::state_(void * const me, QP::QEvt const * const e) {\ - return static_cast(me)->state_ ## _h(e); } \ - QP::QState subclass_::state_ ## _h(QP::QEvt const * const e) - -//! Macro to specify that the event was handled -#define Q_HANDLED() (Q_RET_HANDLED) - -//! Macro to specify that the event was NOT handled -//! due to a guard condition evaluating to 'false' -#define Q_UNHANDLED() (Q_RET_UNHANDLED) - -//! Macro to perform casting to QStateHandler. -/// @description -/// This macro encapsulates the cast of a specific state handler function -/// pointer to QStateHandler, which violates MISRA-C 2004 rule 11.4(advisory). -/// This macro helps to localize this deviation. -#define Q_STATE_CAST(handler_) \ - (reinterpret_cast(handler_)) - -//! Macro to perform casting to QActionHandler. -/// @description -/// This macro encapsulates the cast of a specific action handler function -/// pointer to QActionHandler, which violates MISRA-C2004 rule 11.4(advisory). -/// This macro helps to localize this deviation. -#define Q_ACTION_CAST(act_) (reinterpret_cast(act_)) - -//! Macro to provide strictly-typed zero-action to terminate action lists -//! in the transition-action-tables -#define Q_ACTION_NULL (nullptr) - - -//**************************************************************************** -// Macros for coding QMsm-style state machines... - -//! Macro to generate a declaration of a state-handler, state-caller and -//! a state-object for a given state in a subclass of QP::QMsm. -#define QM_STATE_DECL(state_) \ - QP::QState state_ ## _h(QP::QEvt const * const e); \ - static QP::QState state_(void * const me, QP::QEvt const * const e); \ - static QP::QMState const state_ ## _s - -//! Macro to generate a declaration of a state-handler, state-caller and -//! a state-object for a given *submachine* state in a subclass of QP::QMsm. -#define QM_SM_STATE_DECL(subm_, state_) \ - QP::QState state_ ## _h(QP::QEvt const * const e); \ - static QP::QState state_(void * const me, QP::QEvt const * const e); \ - static SM_ ## subm_ const state_ ## _s - -//! Macro to generate a declaration of an action-handler and action-caller -//! in a subclass of QP::QMsm. -#define QM_ACTION_DECL(action_) \ - QP::QState action_ ## _h(void); \ - static QP::QState action_(void * const me) - -//! Macro to generate a definition of a state-caller and state-handler -//! for a given state in a subclass of QP::QMsm. -#define QM_STATE_DEF(subclass_, state_) \ - QP::QState subclass_::state_(void * const me, QP::QEvt const * const e) {\ - return static_cast(me)->state_ ## _h(e); } \ - QP::QState subclass_::state_ ## _h(QP::QEvt const * const e) - -//! Macro to generate a definition of an action-caller and action-handler -//! in a subclass of QP::QMsm. -#define QM_ACTION_DEF(subclass_, action_) \ - QP::QState subclass_::action_(void * const me) { \ - return static_cast(me)->action_ ## _h(); } \ - QP::QState subclass_::action_ ## _h(void) - -//! Macro for a QM action-handler when it handles the event. -#define QM_HANDLED() (Q_RET_HANDLED) - -//! Macro for a QM action-handler when it does not handle the event -//! due to a guard condition evaluating to false. -#define QM_UNHANDLED() (Q_RET_UNHANDLED) - -//! Macro for a QM action-handler when it passes the event to the superstate -#define QM_SUPER() (Q_RET_SUPER) - -//! Macro to provide strictly-typed zero-state to use for submachines. -//! Applicable to suclasses of QP::QMsm. -#define QM_STATE_NULL (nullptr) - -#endif // QEP_HPP - diff --git a/libraries/qpcpp_arm-cm/src/qep_hsm.cpp b/libraries/qpcpp_arm-cm/src/qep_hsm.cpp index b72f71c..615ef8a 100644 --- a/libraries/qpcpp_arm-cm/src/qep_hsm.cpp +++ b/libraries/qpcpp_arm-cm/src/qep_hsm.cpp @@ -1,490 +1,620 @@ -/// @file -/// @brief QP::QHsm implementation -/// @ingroup qep -/// @cond -///*************************************************************************** -/// Last updated for version 6.9.2 -/// Last updated on 2020-12-17 -/// -/// Q u a n t u m L e a P s -/// ------------------------ -/// Modern Embedded Software -/// -/// Copyright (C) 2005-2020 Quantum Leaps. All rights reserved. -/// -/// This program is open source software: you can redistribute it and/or -/// modify it under the terms of the GNU General Public License as published -/// by the Free Software Foundation, either version 3 of the License, or -/// (at your option) any later version. -/// -/// Alternatively, this program may be distributed and modified under the -/// terms of Quantum Leaps commercial licenses, which expressly supersede -/// the GNU General Public License and are specifically designed for -/// licensees interested in retaining the proprietary status of their code. -/// -/// This program is distributed in the hope that it will be useful, -/// but WITHOUT ANY WARRANTY; without even the implied warranty of -/// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -/// GNU General Public License for more details. -/// -/// You should have received a copy of the GNU General Public License -/// along with this program. If not, see . -/// -/// Contact information: -/// -/// -///*************************************************************************** -/// @endcond - +//$file${src::qf::qep_hsm.cpp} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv +// +// Model: qpcpp.qm +// File: ${src::qf::qep_hsm.cpp} +// +// This code has been generated by QM 6.1.1 . +// DO NOT EDIT THIS FILE MANUALLY. All your changes will be lost. +// +// This code is covered by the following QP license: +// License # : LicenseRef-QL-dual +// Issued to : Any user of the QP/C++ real-time embedded framework +// Framework(s) : qpcpp +// Support ends : 2024-12-31 +// License scope: +// +// Copyright (C) 2005 Quantum Leaps, LLC . +// +// Q u a n t u m L e a P s +// ------------------------ +// Modern Embedded Software +// +// SPDX-License-Identifier: GPL-3.0-or-later OR LicenseRef-QL-commercial +// +// This software is dual-licensed under the terms of the open source GNU +// General Public License version 3 (or any later version), or alternatively, +// under the terms of one of the closed source Quantum Leaps commercial +// licenses. +// +// The terms of the open source GNU General Public License version 3 +// can be found at: +// +// The terms of the closed source Quantum Leaps commercial licenses +// can be found at: +// +// Redistributions in source code must retain this top-level comment block. +// Plagiarizing this software to sidestep the license obligations is illegal. +// +// Contact information: +// +// +// +//$endhead${src::qf::qep_hsm.cpp} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ #define QP_IMPL // this is QP implementation -#include "qep_port.hpp" // QEP port +#include "qp_port.hpp" // QP port +#include "qp_pkg.hpp" // QP package-scope interface +#include "qsafe.h" // QP Functional Safety (FuSa) Subsystem #ifdef Q_SPY // QS software tracing enabled? #include "qs_port.hpp" // QS port #include "qs_pkg.hpp" // QS facilities for pre-defined trace records #else #include "qs_dummy.hpp" // disable the QS software tracing #endif // Q_SPY -#include "qassert.h" // QP embedded systems-friendly assertions - - -//! 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_PRE_(QS_QEP_STATE_EXIT, qs_id) \ - QS_OBJ_PRE_(this); \ - QS_FUN_PRE_(state_); \ - QS_END_PRE_() \ - } \ -} 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_PRE_(QS_QEP_STATE_ENTRY, qs_id) \ - QS_OBJ_PRE_(this); \ - QS_FUN_PRE_(state_); \ - QS_END_PRE_() \ - } \ -} while (false) - -namespace QP { +//============================================================================ +//! @cond INTERNAL +// unnamed namespace for local definitions with internal linkage +namespace { Q_DEFINE_THIS_MODULE("qep_hsm") -//**************************************************************************** -//char_t const versionStr[7] = QP_VERSION_STR; - -//**************************************************************************** -enum : QSignal { - //! empty signal for internal use only - QEP_EMPTY_SIG_ = 0U +// immutable events corresponding to the reserved signals. +static QP::QEvt const l_reservedEvt_[4] { + QP::QEvt(static_cast(QP::QHsm::Q_EMPTY_SIG)), + QP::QEvt(static_cast(QP::QHsm::Q_ENTRY_SIG)), + QP::QEvt(static_cast(QP::QHsm::Q_EXIT_SIG)), + QP::QEvt(static_cast(QP::QHsm::Q_INIT_SIG)) }; -//**************************************************************************** -/// @description -/// Static, preallocated standard events that the QEP event processor sends -/// to state handler functions of QP::QHsm and QP::QFsm subclasses to execute -/// entry actions, exit actions, and initial transitions. -/// -static QEvt const QEP_reservedEvt_[4] { -#ifdef Q_EVT_CTOR // Is the QEvt constructor provided? - QEvt(0U, QEvt::STATIC_EVT), - QEvt(1U, QEvt::STATIC_EVT), - QEvt(2U, QEvt::STATIC_EVT), - QEvt(3U, QEvt::STATIC_EVT) -#else // QEvt is a POD (Plain Old Datatype) - { 0U, 0U, 0U }, - { 1U, 0U, 0U }, - { 2U, 0U, 0U }, - { 3U, 0U, 0U } +} // unnamed namespace + +// helper macro to handle reserved event in an QHsm +#define QHSM_RESERVED_EVT_(state_, sig_) \ + ((*(state_))(this, &l_reservedEvt_[(sig_)])) + +// helper macro to trace state entry +#define QS_STATE_ENTRY_(state_, qsId_) \ + QS_CRIT_ENTRY(); \ + QS_MEM_SYS(); \ + QS_BEGIN_PRE_(QS_QEP_STATE_ENTRY, (qsId_)) \ + QS_OBJ_PRE_(this); \ + QS_FUN_PRE_(state_); \ + QS_END_PRE_() \ + QS_MEM_APP(); \ + QS_CRIT_EXIT() + +// helper macro to trace state exit +#define QS_STATE_EXIT_(state_, qsId_) \ + QS_CRIT_ENTRY(); \ + QS_MEM_SYS(); \ + QS_BEGIN_PRE_(QS_QEP_STATE_EXIT, (qsId_)) \ + QS_OBJ_PRE_(this); \ + QS_FUN_PRE_(state_); \ + QS_END_PRE_() \ + QS_MEM_APP(); \ + QS_CRIT_EXIT() + +//! @endcond +//============================================================================ + +//$skip${QP_VERSION} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv +// Check for the minimum required QP version +#if (QP_VERSION < 730U) || (QP_VERSION != ((QP_RELEASE^4294967295U) % 0x3E8U)) +#error qpcpp version 7.3.0 or higher required #endif -}; +//$endskip${QP_VERSION} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +//$define${QEP::versionStr[]} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv +namespace QP { + +} // namespace QP +//$enddef${QEP::versionStr[]} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -//**************************************************************************** -/// @description -/// Performs the first step of HSM initialization by assigning the initial -/// pseudostate to the currently active state of the state machine. -/// -/// @param[in] initial pointer to the top-most initial state-handler -/// function in the derived state machine -/// -QHsm::QHsm(QStateHandler const initial) noexcept { +//$define${QEP::QHsm} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv +namespace QP { + +//${QEP::QHsm} ............................................................... + +//${QEP::QHsm::QHsm} ......................................................... +QHsm::QHsm(QStateHandler const initial) noexcept +: QAsm() +{ m_state.fun = Q_STATE_CAST(&top); m_temp.fun = initial; } -//**************************************************************************** -/// @description -/// Virtual destructor of the QHsm state machine and any of its subclasses. -/// -QHsm::~QHsm() { -} +//${QEP::QHsm::init} ......................................................... +void QHsm::init( + void const * const e, + std::uint_fast8_t const qsId) +{ + QF_CRIT_STAT + + #ifdef Q_SPY + QS_CRIT_ENTRY(); + QS_MEM_SYS(); + if ((QS::priv_.flags & 0x01U) == 0U) { + QS::priv_.flags |= 0x01U; + QS_MEM_APP(); + QS_CRIT_EXIT(); + QS_FUN_DICTIONARY(&QP::QHsm::top); + } + else { + QS_MEM_APP(); + QS_CRIT_EXIT(); + } + #else + Q_UNUSED_PAR(qsId); + #endif -//**************************************************************************** -/// @description -/// Executes the top-most initial transition in a HSM. -/// -/// @param[in] e pointer to an extra parameter (might be NULL) -/// @param[in] qs_id QS-id of this state machine (for QS local filter) -/// -/// @note -/// Must be called exactly __once__ before the QP::QHsm::dispatch(). -/// -void QHsm::init(void const * const e, std::uint_fast8_t const qs_id) { QStateHandler t = m_state.fun; - /// @pre ctor must have been executed and initial tran NOT taken - Q_REQUIRE_ID(200, (m_temp.fun != nullptr) + QF_CRIT_ENTRY(); + Q_REQUIRE_INCRIT(200, (m_temp.fun != nullptr) && (t == Q_STATE_CAST(&top))); + QF_CRIT_EXIT(); - // execute the top-most initial transition + // execute the top-most initial tran. QState r = (*m_temp.fun)(this, Q_EVT_CAST(QEvt)); - // the top-most initial transition must be taken - Q_ASSERT_ID(210, r == Q_RET_TRAN); + QF_CRIT_ENTRY(); + // the top-most initial tran. must be taken + Q_ASSERT_INCRIT(210, r == Q_RET_TRAN); - QS_CRIT_STAT_ - QS_BEGIN_PRE_(QS_QEP_STATE_INIT, qs_id) - QS_OBJ_PRE_(this); // this state machine object - QS_FUN_PRE_(t); // the source state - QS_FUN_PRE_(m_temp.fun); // the target of the initial transition + QS_MEM_SYS(); + QS_BEGIN_PRE_(QS_QEP_STATE_INIT, qsId) + QS_OBJ_PRE_(this); // this state machine object + QS_FUN_PRE_(t); // the source state + QS_FUN_PRE_(m_temp.fun); // the target of the initial tran. QS_END_PRE_() + QS_MEM_APP(); + + QF_CRIT_EXIT(); // drill down into the state hierarchy with initial transitions... + std::int_fast8_t limit = MAX_NEST_DEPTH_; // loop hard limit do { QStateHandler path[MAX_NEST_DEPTH_]; // tran entry path array - std::int_fast8_t ip = 0; // entry path index + std::int_fast8_t ip = 0; // tran entry path index path[0] = m_temp.fun; - static_cast(QEP_TRIG_(m_temp.fun, QEP_EMPTY_SIG_)); + static_cast(QHSM_RESERVED_EVT_(m_temp.fun, Q_EMPTY_SIG)); while (m_temp.fun != t) { ++ip; - Q_ASSERT_ID(220, - ip < static_cast(Q_DIM(path))); path[ip] = m_temp.fun; - static_cast(QEP_TRIG_(m_temp.fun, QEP_EMPTY_SIG_)); + static_cast(QHSM_RESERVED_EVT_(m_temp.fun, Q_EMPTY_SIG)); } + QF_CRIT_ENTRY(); + // The initial transition source state must be reached + // Too many state nesting levels or "malformed" HSM. + Q_ASSERT_INCRIT(220, m_temp.fun == t); + QF_CRIT_EXIT(); + m_temp.fun = path[0]; // retrace the entry path in reverse (desired) order... do { - QEP_ENTER_(path[ip]); // enter path[ip] + // enter path[ip] + if (QHSM_RESERVED_EVT_(path[ip], Q_ENTRY_SIG) + == Q_RET_HANDLED) + { + QS_STATE_ENTRY_(path[ip], qsId); + } --ip; } while (ip >= 0); t = path[0]; // current state becomes the new source - r = QEP_TRIG_(t, Q_INIT_SIG); // execute initial transition + r = QHSM_RESERVED_EVT_(t, Q_INIT_SIG); // execute initial tran. -#ifdef Q_SPY + #ifdef Q_SPY if (r == Q_RET_TRAN) { - QS_BEGIN_PRE_(QS_QEP_STATE_INIT, qs_id) - QS_OBJ_PRE_(this); // this state machine object - QS_FUN_PRE_(t); // the source state - QS_FUN_PRE_(m_temp.fun); // the target of the initial tran. + QS_CRIT_ENTRY(); + QS_MEM_SYS(); + QS_BEGIN_PRE_(QS_QEP_STATE_INIT, qsId) + QS_OBJ_PRE_(this); // this state machine object + QS_FUN_PRE_(t); // the source state + QS_FUN_PRE_(m_temp.fun); // the target of the initial tran. QS_END_PRE_() + QS_MEM_APP(); + QS_CRIT_EXIT(); } -#endif // Q_SPY + #endif // Q_SPY - } while (r == Q_RET_TRAN); + --limit; + } while ((r == Q_RET_TRAN) && (limit > 0)); - QS_BEGIN_PRE_(QS_QEP_INIT_TRAN, qs_id) + QF_CRIT_ENTRY(); + // Loop limit must not be reached. + // Too many state nesting levels or likely "malformed" HSM + Q_ENSURE_INCRIT(290, limit > 0); + + QS_MEM_SYS(); + QS_BEGIN_PRE_(QS_QEP_INIT_TRAN, qsId) QS_TIME_PRE_(); // time stamp QS_OBJ_PRE_(this); // this state machine object QS_FUN_PRE_(t); // the new active state QS_END_PRE_() + QS_MEM_APP(); - m_state.fun = t; // change the current active state - m_temp.fun = t; // mark the configuration as stable - static_cast(qs_id); // unused parameter (if Q_SPY not defined) -} + QF_CRIT_EXIT(); -//***************************************************************************/ -/// @description -/// The QP::QHsm::top() state handler is the ultimate root of state hierarchy -/// in all HSMs derived from QP::QHsm. -/// -/// @param[in] me pointer to the HSM instance -/// @param[in] e pointer to the event to be dispatched to the HSM -/// -/// @returns -/// Always returns #Q_RET_IGNORED, which means that the top state ignores -/// all events. -/// -/// @note -/// The arguments to this state handler are not used. They are provided for -/// conformance with the state-handler function signature QP::QStateHandler. -/// -QState QHsm::top(void * const me, QEvt const * const e) noexcept { - static_cast(me); // unused parameter - static_cast(e); // unused parameter - return Q_RET_IGNORED; // the top state ignores all events + m_state.fun = t; // change the current active state + #ifndef Q_UNSAFE + m_temp.uint = ~m_state.uint; + #endif } -//**************************************************************************** -/// @description -/// Dispatches an event for processing to a hierarchical state machine (HSM). -/// The processing of an event represents one run-to-completion (RTC) step. -/// -/// @param[in] e pointer to the event to be dispatched to the HSM -/// @param[in] qs_id QS-id of this state machine (for QS local filter) -/// -/// @note -/// This state machine must be initialized by calling QP::QHsm::init() exactly -/// __once__ before calling QP::QHsm::dispatch(). -/// -void QHsm::dispatch(QEvt const * const e, std::uint_fast8_t const qs_id) { - QStateHandler t = m_state.fun; - QS_CRIT_STAT_ - - /// @pre the current state must be initialized and - /// the state configuration must be stable - Q_REQUIRE_ID(400, (t != nullptr) - && (t == m_temp.fun)); - - QS_BEGIN_PRE_(QS_QEP_DISPATCH, qs_id) - QS_TIME_PRE_(); // time stamp - QS_SIG_PRE_(e->sig); // the signal of the event - QS_OBJ_PRE_(this); // this state machine object - QS_FUN_PRE_(t); // the current state +//${QEP::QHsm::dispatch} ..................................................... +void QHsm::dispatch( + QEvt const * const e, + std::uint_fast8_t const qsId) +{ + #ifndef Q_SPY + Q_UNUSED_PAR(qsId); + #endif + + QStateHandler s = m_state.fun; + QStateHandler t = s; + QF_CRIT_STAT + + QF_CRIT_ENTRY(); + Q_REQUIRE_INCRIT(300, (s != Q_STATE_CAST(0)) + && (m_state.uint == static_cast(~m_temp.uint))); + Q_REQUIRE_INCRIT(302, QEvt::verify_(e)); + + QS_MEM_SYS(); + QS_BEGIN_PRE_(QS_QEP_DISPATCH, qsId) + QS_TIME_PRE_(); // time stamp + QS_SIG_PRE_(e->sig); // the signal of the event + QS_OBJ_PRE_(this); // this state machine object + QS_FUN_PRE_(s); // the current state QS_END_PRE_() + QS_MEM_APP(); + + QF_CRIT_EXIT(); - QStateHandler s; - QState r; // process the event hierarchically... + QState r; + m_temp.fun = s; + std::int_fast8_t limit = MAX_NEST_DEPTH_; // loop hard limit do { s = m_temp.fun; r = (*s)(this, e); // invoke state handler s if (r == Q_RET_UNHANDLED) { // unhandled due to a guard? - QS_BEGIN_PRE_(QS_QEP_UNHANDLED, qs_id) + QS_CRIT_ENTRY(); + QS_MEM_SYS(); + QS_BEGIN_PRE_(QS_QEP_UNHANDLED, qsId) QS_SIG_PRE_(e->sig); // the signal of the event QS_OBJ_PRE_(this); // this state machine object QS_FUN_PRE_(s); // the current state QS_END_PRE_() + QS_MEM_APP(); + QS_CRIT_EXIT(); - r = QEP_TRIG_(s, QEP_EMPTY_SIG_); // find superstate of s + r = QHSM_RESERVED_EVT_(s, Q_EMPTY_SIG); // superstate of s } - } while (r == Q_RET_SUPER); - // transition taken? - if (r >= Q_RET_TRAN) { - QStateHandler path[MAX_NEST_DEPTH_]; + --limit; + } while ((r == Q_RET_SUPER) && (limit > 0)); + + QF_CRIT_ENTRY(); + Q_ASSERT_INCRIT(310, limit > 0); + QF_CRIT_EXIT(); - path[0] = m_temp.fun; // save the target of the transition - path[1] = t; - path[2] = s; + if (r >= Q_RET_TRAN) { // regular tran. taken? + QStateHandler path[MAX_NEST_DEPTH_]; - // exit current state to transition source s... - for (; t != s; t = m_temp.fun) { - // exit handled? - if (QEP_TRIG_(t, Q_EXIT_SIG) == Q_RET_HANDLED) { - QS_BEGIN_PRE_(QS_QEP_STATE_EXIT, qs_id) - QS_OBJ_PRE_(this); // this state machine object - QS_FUN_PRE_(t); // the exited state - QS_END_PRE_() + path[0] = m_temp.fun; // tran. target + path[1] = t; // current state + path[2] = s; // tran. source + // exit current state to tran. source s... + limit = MAX_NEST_DEPTH_; // loop hard limit + for (; (t != s) && (limit > 0); t = m_temp.fun) { + // exit from t + if (QHSM_RESERVED_EVT_(t, Q_EXIT_SIG) == Q_RET_HANDLED) { + QS_STATE_EXIT_(t, qsId); // find superstate of t - static_cast(QEP_TRIG_(t, QEP_EMPTY_SIG_)); + static_cast(QHSM_RESERVED_EVT_(t, Q_EMPTY_SIG)); } + --limit; } + QF_CRIT_ENTRY(); + Q_ASSERT_INCRIT(320, limit > 0); + QF_CRIT_EXIT(); - std::int_fast8_t ip = hsm_tran(path, qs_id); // the HSM transition + std::int_fast8_t ip = hsm_tran(path, qsId); // take the tran. -#ifdef Q_SPY + #ifdef Q_SPY if (r == Q_RET_TRAN_HIST) { - - QS_BEGIN_PRE_(QS_QEP_TRAN_HIST, qs_id) - QS_OBJ_PRE_(this); // this state machine object - QS_FUN_PRE_(t); // the source of the transition - QS_FUN_PRE_(path[0]); // the target of the tran. to history + QS_CRIT_ENTRY(); + QS_MEM_SYS(); + QS_BEGIN_PRE_(QS_QEP_TRAN_HIST, qsId) + QS_OBJ_PRE_(this); // this state machine object + QS_FUN_PRE_(t); // the source of the transition + QS_FUN_PRE_(path[0]); // the target of the tran. to history QS_END_PRE_() - + QS_MEM_APP(); + QS_CRIT_EXIT(); } -#endif // Q_SPY + #endif // Q_SPY - // retrace the entry path in reverse (desired) order... + // execute state entry actions in the desired order... for (; ip >= 0; --ip) { - QEP_ENTER_(path[ip]); // enter path[ip] + // enter path[ip] + if (QHSM_RESERVED_EVT_(path[ip], Q_ENTRY_SIG) + == Q_RET_HANDLED) + { + QS_STATE_ENTRY_(path[ip], qsId); + } } t = path[0]; // stick the target into register m_temp.fun = t; // update the next state // drill into the target hierarchy... - while (QEP_TRIG_(t, Q_INIT_SIG) == Q_RET_TRAN) { - - QS_BEGIN_PRE_(QS_QEP_STATE_INIT, qs_id) - QS_OBJ_PRE_(this); // this state machine object - QS_FUN_PRE_(t); // the source (pseudo)state - QS_FUN_PRE_(m_temp.fun); // the target of the transition + while (QHSM_RESERVED_EVT_(t, Q_INIT_SIG) == Q_RET_TRAN) { + + QS_CRIT_ENTRY(); + QS_MEM_SYS(); + QS_BEGIN_PRE_(QS_QEP_STATE_INIT, qsId) + QS_OBJ_PRE_(this); // this state machine object + QS_FUN_PRE_(t); // the source (pseudo)state + QS_FUN_PRE_(m_temp.fun); // the target of the tran. QS_END_PRE_() + QS_MEM_APP(); + QS_CRIT_EXIT(); ip = 0; path[0] = m_temp.fun; // find superstate - static_cast(QEP_TRIG_(m_temp.fun, QEP_EMPTY_SIG_)); + static_cast(QHSM_RESERVED_EVT_(m_temp.fun, Q_EMPTY_SIG)); - while (m_temp.fun != t) { + while ((m_temp.fun != t) && (ip < (MAX_NEST_DEPTH_ - 1))) { ++ip; path[ip] = m_temp.fun; // find superstate - static_cast(QEP_TRIG_(m_temp.fun, QEP_EMPTY_SIG_)); + static_cast( + QHSM_RESERVED_EVT_(m_temp.fun, Q_EMPTY_SIG)); } - m_temp.fun = path[0]; + QF_CRIT_ENTRY(); + // The initial transition source state must be reached. + // Too many state nesting levels or "malformed" HSM. + Q_ASSERT_INCRIT(330, m_temp.fun == t); + QF_CRIT_EXIT(); - // entry path must not overflow - Q_ASSERT_ID(410, ip < MAX_NEST_DEPTH_); + m_temp.fun = path[0]; // retrace the entry path in reverse (correct) order... do { - QEP_ENTER_(path[ip]); // enter path[ip] + // enter path[ip] + if (QHSM_RESERVED_EVT_(path[ip], Q_ENTRY_SIG) + == Q_RET_HANDLED) + { + QS_STATE_ENTRY_(path[ip], qsId); + } --ip; } while (ip >= 0); - t = path[0]; + t = path[0]; // current state becomes the new source } - QS_BEGIN_PRE_(QS_QEP_TRAN, qs_id) - QS_TIME_PRE_(); // time stamp - QS_SIG_PRE_(e->sig); // the signal of the event - QS_OBJ_PRE_(this); // this state machine object - QS_FUN_PRE_(s); // the source of the transition - QS_FUN_PRE_(t); // the new active state + QS_CRIT_ENTRY(); + QS_MEM_SYS(); + QS_BEGIN_PRE_(QS_QEP_TRAN, qsId) + QS_TIME_PRE_(); // time stamp + QS_SIG_PRE_(e->sig); // the signal of the event + QS_OBJ_PRE_(this); // this state machine object + QS_FUN_PRE_(s); // the source of the tran. + QS_FUN_PRE_(t); // the new active state QS_END_PRE_() + QS_MEM_APP(); + QS_CRIT_EXIT(); } -#ifdef Q_SPY + #ifdef Q_SPY else if (r == Q_RET_HANDLED) { - - QS_BEGIN_PRE_(QS_QEP_INTERN_TRAN, qs_id) - QS_TIME_PRE_(); // time stamp - QS_SIG_PRE_(e->sig); // the signal of the event - QS_OBJ_PRE_(this); // this state machine object - QS_FUN_PRE_(s); // the source state + QS_CRIT_ENTRY(); + QS_MEM_SYS(); + QS_BEGIN_PRE_(QS_QEP_INTERN_TRAN, qsId) + QS_TIME_PRE_(); // time stamp + QS_SIG_PRE_(e->sig); // the signal of the event + QS_OBJ_PRE_(this); // this state machine object + QS_FUN_PRE_(s); // the source state QS_END_PRE_() - + QS_MEM_APP(); + QS_CRIT_EXIT(); } else { - - QS_BEGIN_PRE_(QS_QEP_IGNORED, qs_id) - QS_TIME_PRE_(); // time stamp - QS_SIG_PRE_(e->sig); // the signal of the event - QS_OBJ_PRE_(this); // this state machine object - QS_FUN_PRE_(m_state.fun);// the current state + QS_CRIT_ENTRY(); + QS_MEM_SYS(); + QS_BEGIN_PRE_(QS_QEP_IGNORED, qsId) + QS_TIME_PRE_(); // time stamp + QS_SIG_PRE_(e->sig); // the signal of the event + QS_OBJ_PRE_(this); // this state machine object + QS_FUN_PRE_(m_state.fun); // the current state QS_END_PRE_() - + QS_MEM_APP(); + QS_CRIT_EXIT(); } -#endif // Q_SPY + #endif // Q_SPY m_state.fun = t; // change the current active state - m_temp.fun = t; // mark the configuration as stable - static_cast(qs_id); // unused parameter (if Q_SPY not defined) + #ifndef Q_UNSAFE + m_temp.uint = ~m_state.uint; + #endif +} + +//${QEP::QHsm::isIn} ......................................................... +bool QHsm::isIn(QStateHandler const state) noexcept { + QF_CRIT_STAT + QF_CRIT_ENTRY(); + Q_REQUIRE_INCRIT(602, m_state.uint + == static_cast(~m_temp.uint)); + QF_CRIT_EXIT(); + + bool inState = false; // assume that this HSM is not in 'state' + + // scan the state hierarchy bottom-up + QStateHandler s = m_state.fun; + std::int_fast8_t limit = MAX_NEST_DEPTH_ + 1; // loop hard limit + QState r = Q_RET_SUPER; + for (; (r != Q_RET_IGNORED) && (limit > 0); --limit) { + if (s == state) { // do the states match? + inState = true; // 'true' means that match found + break; // break out of the for-loop + } + else { + r = QHSM_RESERVED_EVT_(s, Q_EMPTY_SIG); + s = m_temp.fun; + } + } + + QF_CRIT_ENTRY(); + Q_ENSURE_INCRIT(690, limit > 0); + QF_CRIT_EXIT(); + + #ifndef Q_UNSAFE + m_temp.uint = ~m_state.uint; + #endif + + return inState; // return the status +} + +//${QEP::QHsm::childState} ................................................... +QStateHandler QHsm::childState(QStateHandler const parent) noexcept { + QStateHandler child = m_state.fun; // start with the current state + bool isFound = false; // start with the child not found + + // establish stable state configuration + m_temp.fun = child; + QState r; + do { + // is this the parent of the current child? + if (m_temp.fun == parent) { + isFound = true; // child is found + r = Q_RET_IGNORED; // break out of the loop + } + else { + child = m_temp.fun; + r = QHSM_RESERVED_EVT_(m_temp.fun, Q_EMPTY_SIG); + } + } while (r != Q_RET_IGNORED); // QHsm::top() state not reached + + #ifndef Q_UNSAFE + m_temp.uint = ~m_state.uint; + #endif + + QF_CRIT_STAT + QF_CRIT_ENTRY(); + Q_ASSERT_INCRIT(890, isFound); + QF_CRIT_EXIT(); + + return child; // return the child } -//**************************************************************************** -/// @description -/// helper function to execute transition sequence in a hierarchical state -/// machine (HSM). -/// -/// @param[in,out] path array of pointers to state-handler functions -/// to execute the entry actions -/// @param[in] qs_id QS-id of this state machine (for QS local filter) -/// -/// @returns -/// the depth of the entry path stored in the @p path parameter. -//// -std::int_fast8_t QHsm::hsm_tran(QStateHandler (&path)[MAX_NEST_DEPTH_], - std::uint_fast8_t const qs_id) +//${QEP::QHsm::hsm_tran} ..................................................... +std::int_fast8_t QHsm::hsm_tran( + QStateHandler (&path)[MAX_NEST_DEPTH_], + std::uint_fast8_t const qsId) { - std::int_fast8_t ip = -1; // transition entry path index - std::int_fast8_t iq; // helper transition entry path index + #ifndef Q_SPY + Q_UNUSED_PAR(qsId); + #endif + + std::int_fast8_t ip = -1; // tran. entry path index QStateHandler t = path[0]; QStateHandler const s = path[2]; - QState r; - QS_CRIT_STAT_ + QF_CRIT_STAT - // (a) check source==target (transition to self) + // (a) check source==target (tran. to self)... if (s == t) { - QEP_EXIT_(s); // exit the source + // exit source s + if (QHSM_RESERVED_EVT_(s, Q_EXIT_SIG) == Q_RET_HANDLED) { + QS_STATE_EXIT_(s, qsId); + } ip = 0; // enter the target } else { - // superstate of target - static_cast(QEP_TRIG_(t, QEP_EMPTY_SIG_)); + // find superstate of target + static_cast(QHSM_RESERVED_EVT_(t, Q_EMPTY_SIG)); + t = m_temp.fun; - // (b) check source==target->super + // (b) check source==target->super... if (s == t) { ip = 0; // enter the target } else { - // superstate of src - static_cast(QEP_TRIG_(s, QEP_EMPTY_SIG_)); + // find superstate of src + static_cast(QHSM_RESERVED_EVT_(s, Q_EMPTY_SIG)); - // (c) check source->super==target->super + // (c) check source->super==target->super... if (m_temp.fun == t) { - QEP_EXIT_(s); // exit the source + // exit source s + if (QHSM_RESERVED_EVT_(s, Q_EXIT_SIG) == Q_RET_HANDLED) { + QS_STATE_EXIT_(s, qsId); + } ip = 0; // enter the target } else { - // (d) check source->super==target + // (d) check source->super==target... if (m_temp.fun == path[0]) { - QEP_EXIT_(s); // exit the source + // exit source s + if (QHSM_RESERVED_EVT_(s, Q_EXIT_SIG) == Q_RET_HANDLED) { + QS_STATE_EXIT_(s, qsId); + } } else { // (e) check rest of source==target->super->super.. // and store the entry path along the way - - iq = 0; // indicate that the LCA was not found + std::int_fast8_t iq = 0; // indicate that LCA was found ip = 1; // enter target and its superstate - path[1] = t; // save the superstate of target - t = m_temp.fun; // save source->super - - // find target->super->super - r = QEP_TRIG_(path[1], QEP_EMPTY_SIG_); - while (r == Q_RET_SUPER) { + path[1] = t; // save the superstate of target + t = m_temp.fun; // save source->super + + // find target->super->super... + QState r = QHSM_RESERVED_EVT_(path[1], Q_EMPTY_SIG); + while ((r == Q_RET_SUPER) + && (ip < (MAX_NEST_DEPTH_ - 1))) + { ++ip; path[ip] = m_temp.fun; // store the entry path if (m_temp.fun == s) { // is it the source? - // indicate that the LCA was found - iq = 1; - - // entry path must not overflow - Q_ASSERT_ID(510, ip < MAX_NEST_DEPTH_); - --ip; // do not enter the source + iq = 1; // indicate that the LCA found + --ip; // do not enter the source r = Q_RET_HANDLED; // terminate the loop } - // it is not the source, keep going up - else { - r = QEP_TRIG_(m_temp.fun, QEP_EMPTY_SIG_); + else { // it is not the source, keep going up + r = QHSM_RESERVED_EVT_(m_temp.fun, Q_EMPTY_SIG); } } + QF_CRIT_ENTRY(); + // Tran. source must be found within the nesting depth + // Too many state nesting levels or "malformed" HSM. + Q_ASSERT_INCRIT(510, r != Q_RET_SUPER); + QF_CRIT_EXIT(); // the LCA not found yet? if (iq == 0) { - // entry path must not overflow - Q_ASSERT_ID(520, ip < MAX_NEST_DEPTH_); - - QEP_EXIT_(s); // exit the source + // exit source s + if (QHSM_RESERVED_EVT_(s, Q_EXIT_SIG) + == Q_RET_HANDLED) + { + QS_STATE_EXIT_(s, qsId); + } // (f) check the rest of source->super // == target->super->super... - // iq = ip; - r = Q_RET_IGNORED; // indicate LCA NOT found + r = Q_RET_IGNORED; // indicate that the LCA NOT found do { - // is this the LCA? - if (t == path[iq]) { - r = Q_RET_HANDLED; // indicate LCA found - ip = iq - 1; // do not enter LCA + if (t == path[iq]) { // is this the LCA? + r = Q_RET_HANDLED; // indicate the LCA found + ip = iq - 1; // do not enter the LCA iq = -1; // cause termination of the loop } else { @@ -492,140 +622,52 @@ std::int_fast8_t QHsm::hsm_tran(QStateHandler (&path)[MAX_NEST_DEPTH_], } } while (iq >= 0); - // LCA not found yet? + // the LCA not found yet? if (r != Q_RET_HANDLED) { // (g) check each source->super->... // for each target->super... - // r = Q_RET_IGNORED; // keep looping + std::int_fast8_t limit = MAX_NEST_DEPTH_; do { - // exit t unhandled? - if (QEP_TRIG_(t, Q_EXIT_SIG) == Q_RET_HANDLED) + // exit from t + if (QHSM_RESERVED_EVT_(t, Q_EXIT_SIG) + == Q_RET_HANDLED) { - QS_BEGIN_PRE_(QS_QEP_STATE_EXIT, qs_id) - QS_OBJ_PRE_(this); - QS_FUN_PRE_(t); - QS_END_PRE_() - + QS_STATE_EXIT_(t, qsId); + // find superstate of t static_cast( - QEP_TRIG_(t, QEP_EMPTY_SIG_)); + QHSM_RESERVED_EVT_(t, Q_EMPTY_SIG)); } - t = m_temp.fun; // set to super of t + t = m_temp.fun; // set to super of t iq = ip; do { - // is this LCA? + // is this the LCA? if (t == path[iq]) { - ip = iq - 1; // do not enter LCA - iq = -1; // break out of inner loop + ip = iq - 1; // do not enter the LCA + iq = -1; // break out of inner loop r = Q_RET_HANDLED; // break outer loop } else { --iq; } } while (iq >= 0); - } while (r != Q_RET_HANDLED); + + --limit; + } while ((r != Q_RET_HANDLED) && (limit > 0)); + QF_CRIT_ENTRY(); + Q_ASSERT_INCRIT(530, limit > 0); + QF_CRIT_EXIT(); } } } } } } - static_cast(qs_id); // unused parameter (if Q_SPY not defined) + QF_CRIT_ENTRY(); + Q_ENSURE_INCRIT(590, ip < MAX_NEST_DEPTH_); + QF_CRIT_EXIT(); return ip; } -//**************************************************************************** -#ifdef Q_SPY - QStateHandler QHsm::getStateHandler() noexcept { - return m_state.fun; - } -#endif - -//**************************************************************************** -/// @description -/// Tests if a state machine derived from QHsm is-in a given state. -/// -/// @note -/// For a HSM, to "be in a state" means also to be in a superstate of -/// of the state. -/// -/// @param[in] s pointer to the state-handler function to be tested -/// -/// @returns -/// 'true' if the HSM is in the @p state and 'false' otherwise -/// -bool QHsm::isIn(QStateHandler const s) noexcept { - - /// @pre state configuration must be stable - Q_REQUIRE_ID(600, m_temp.fun == m_state.fun); - - bool inState = false; // assume that this HSM is not in 'state' - QState r; - - // scan the state hierarchy bottom-up - do { - // do the states match? - if (m_temp.fun == s) { - inState = true; // 'true' means that match found - r = Q_RET_IGNORED; // cause breaking out of the loop - } - else { - r = QEP_TRIG_(m_temp.fun, QEP_EMPTY_SIG_); - } - } while (r != Q_RET_IGNORED); // QHsm::top() state not reached - m_temp.fun = m_state.fun; // restore the stable state configuration - - return inState; // return the status -} - -//**************************************************************************** -/// -/// @description -/// Finds the child state of the given @c parent, such that this child state -/// is an ancestor of the currently active state. The main purpose of this -/// function is to support **shallow history** transitions in state machines -/// derived from QHsm. -/// -/// @param[in] parent pointer to the state-handler function -/// -/// @returns -/// the child of a given @c parent state, which is an ancestor of the -/// currently active state -/// -/// @note -/// this function is designed to be called during state transitions, so it -/// does not necessarily start in a stable state configuration. -/// However, the function establishes stable state configuration upon exit. -/// -QStateHandler QHsm::childState(QStateHandler const parent) noexcept { - QStateHandler child = m_state.fun; // start with the current state - bool isFound = false; // start with the child not found - QState r; - - // establish stable state configuration - m_temp.fun = m_state.fun; - do { - // is this the parent of the current child? - if (m_temp.fun == parent) { - isFound = true; // child is found - r = Q_RET_IGNORED; // cause breaking out of the loop - } - else { - child = m_temp.fun; - r = QEP_TRIG_(m_temp.fun, QEP_EMPTY_SIG_); - } - } while (r != Q_RET_IGNORED); // QHsm::top() state not reached - m_temp.fun = m_state.fun; // establish stable state configuration - - /// @post the child must be confirmed - Q_ENSURE_ID(810, isFound); -#ifdef Q_NASSERT - // avoid compiler warning about unused variable - static_cast(isFound); -#endif - - return child; // return the child -} - } // namespace QP - +//$enddef${QEP::QHsm} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/libraries/qpcpp_arm-cm/src/qep_msm.cpp b/libraries/qpcpp_arm-cm/src/qep_msm.cpp index 909e33e..ac9e4bf 100644 --- a/libraries/qpcpp_arm-cm/src/qep_msm.cpp +++ b/libraries/qpcpp_arm-cm/src/qep_msm.cpp @@ -1,63 +1,75 @@ -/// @file -/// @brief QP::QMsm implementation -/// @ingroup qep -/// @cond -///*************************************************************************** -/// Last updated for version 6.9.2 -/// Last updated on 2020-12-17 -/// -/// Q u a n t u m L e a P s -/// ------------------------ -/// Modern Embedded Software -/// -/// Copyright (C) 2005-2020 Quantum Leaps. All rights reserved. -/// -/// This program is open source software: you can redistribute it and/or -/// modify it under the terms of the GNU General Public License as published -/// by the Free Software Foundation, either version 3 of the License, or -/// (at your option) any later version. -/// -/// Alternatively, this program may be distributed and modified under the -/// terms of Quantum Leaps commercial licenses, which expressly supersede -/// the GNU General Public License and are specifically designed for -/// licensees interested in retaining the proprietary status of their code. -/// -/// This program is distributed in the hope that it will be useful, -/// but WITHOUT ANY WARRANTY; without even the implied warranty of -/// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -/// GNU General Public License for more details. -/// -/// You should have received a copy of the GNU General Public License -/// along with this program. If not, see . -/// -/// Contact information: -/// -/// -///*************************************************************************** -/// @endcond - +//$file${src::qf::qep_msm.cpp} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv +// +// Model: qpcpp.qm +// File: ${src::qf::qep_msm.cpp} +// +// This code has been generated by QM 6.1.1 . +// DO NOT EDIT THIS FILE MANUALLY. All your changes will be lost. +// +// This code is covered by the following QP license: +// License # : LicenseRef-QL-dual +// Issued to : Any user of the QP/C++ real-time embedded framework +// Framework(s) : qpcpp +// Support ends : 2024-12-31 +// License scope: +// +// Copyright (C) 2005 Quantum Leaps, LLC . +// +// Q u a n t u m L e a P s +// ------------------------ +// Modern Embedded Software +// +// SPDX-License-Identifier: GPL-3.0-or-later OR LicenseRef-QL-commercial +// +// This software is dual-licensed under the terms of the open source GNU +// General Public License version 3 (or any later version), or alternatively, +// under the terms of one of the closed source Quantum Leaps commercial +// licenses. +// +// The terms of the open source GNU General Public License version 3 +// can be found at: +// +// The terms of the closed source Quantum Leaps commercial licenses +// can be found at: +// +// Redistributions in source code must retain this top-level comment block. +// Plagiarizing this software to sidestep the license obligations is illegal. +// +// Contact information: +// +// +// +//$endhead${src::qf::qep_msm.cpp} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ #define QP_IMPL // this is QP implementation -#include "qep_port.hpp" // QEP port +#include "qp_port.hpp" // QP port +#include "qp_pkg.hpp" // QP package-scope interface +#include "qsafe.h" // QP Functional Safety (FuSa) Subsystem #ifdef Q_SPY // QS software tracing enabled? #include "qs_port.hpp" // QS port #include "qs_pkg.hpp" // QS facilities for pre-defined trace records #else #include "qs_dummy.hpp" // disable the QS software tracing #endif // Q_SPY -#include "qassert.h" // QP embedded systems-friendly assertions -//! Internal macro to increment the given action table @p act_ -/// @note Incrementing a pointer 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 QEP_ACT_PTR_INC_(act_) (++(act_)) +//============================================================================ +//! @cond INTERNAL -namespace QP { +// unnamed namespace for local definitions with internal linkage +namespace { Q_DEFINE_THIS_MODULE("qep_msm") -//**************************************************************************** -QMState const QMsm::msm_top_s = { +// maximum depth of state nesting in a QMsm (including the top level) +static constexpr std::int_fast8_t MAX_NEST_DEPTH_ {6}; + +// maximum length of transition-action array +static constexpr std::int_fast8_t MAX_TRAN_LENGTH_ {3*MAX_NEST_DEPTH_}; + +// maximum depth of entry levels in a MSM for tran. to history. +static constexpr std::int_fast8_t MAX_ENTRY_DEPTH_ {4}; + +// top-state object for QMsm-style state machines +QP::QMState const l_msm_top_s = { nullptr, nullptr, nullptr, @@ -65,104 +77,124 @@ QMState const QMsm::msm_top_s = { nullptr }; -//**************************************************************************** -/// @description -/// Performs the first step of initialization by assigning the initial -/// pseudostate to the currently active state of the state machine. -/// -/// @param[in] initial the top-most initial transition for the MSM. -/// -/// @note -/// The constructor is protected to prevent direct instantiating of the -/// QP::QMsm objects. This class is intended for subclassing only. -/// -/// @sa -/// The QP::QMsm example illustrates how to use the QMsm constructor -/// in the constructor initializer list of the derived state machines. -/// +} // unnamed namespace + +//! @endcond +//============================================================================ + +//$skip${QP_VERSION} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv +// Check for the minimum required QP version +#if (QP_VERSION < 730U) || (QP_VERSION != ((QP_RELEASE^4294967295U) % 0x3E8U)) +#error qpcpp version 7.3.0 or higher required +#endif +//$endskip${QP_VERSION} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +//$define${QEP::QMsm} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv +namespace QP { + +//${QEP::QMsm} ............................................................... + +//${QEP::QMsm::QMsm} ......................................................... QMsm::QMsm(QStateHandler const initial) noexcept - : QHsm(initial) + : QAsm() { - m_state.obj = &msm_top_s; - m_temp.fun = initial; + m_state.obj = &l_msm_top_s; // the current state (top) + m_temp.fun = initial; // the initial tran. handler } -//**************************************************************************** -/// @description -/// Executes the top-most initial transition in a MSM. -/// -/// @param[in] e pointer to an extra parameter (might be nullptr) -/// @param[in] qs_id QS-id of this state machine (for QS local filter) -/// -/// @attention -/// QP::QMsm::init() must be called exactly __once__ before -/// QP::QMsm::dispatch() -/// -void QMsm::init(void const * const e, std::uint_fast8_t const qs_id) { - QS_CRIT_STAT_ - - /// @pre the top-most initial transition must be initialized, and the - /// initial transition must not be taken yet. - Q_REQUIRE_ID(200, (m_temp.fun != nullptr) - && (m_state.obj == &msm_top_s)); +//${QEP::QMsm::init} ......................................................... +void QMsm::init( + void const * const e, + std::uint_fast8_t const qsId) +{ + #ifndef Q_SPY + Q_UNUSED_PAR(qsId); + #endif + + QF_CRIT_STAT + QF_CRIT_ENTRY(); + Q_REQUIRE_INCRIT(200, (m_temp.fun != nullptr) + && (m_state.obj == &l_msm_top_s)); + QF_CRIT_EXIT(); // execute the top-most initial tran. QState r = (*m_temp.fun)(this, Q_EVT_CAST(QEvt)); - // initial tran. must be taken - Q_ASSERT_ID(210, r == Q_RET_TRAN_INIT); + QF_CRIT_ENTRY(); + // the top-most initial tran. must be taken + Q_ASSERT_INCRIT(210, r == Q_RET_TRAN_INIT); - QS_BEGIN_PRE_(QS_QEP_STATE_INIT, qs_id) + QS_MEM_SYS(); + QS_BEGIN_PRE_(QS_QEP_STATE_INIT, qsId) QS_OBJ_PRE_(this); // this state machine object - QS_FUN_PRE_(m_state.obj->stateHandler); // source handler - QS_FUN_PRE_(m_temp.tatbl->target->stateHandler); // target handler + QS_FUN_PRE_(m_state.obj->stateHandler); // source state + QS_FUN_PRE_(m_temp.tatbl->target->stateHandler); // target state QS_END_PRE_() + QS_MEM_APP(); + + QF_CRIT_EXIT(); // set state to the last tran. target m_state.obj = m_temp.tatbl->target; // drill down into the state hierarchy with initial transitions... + std::int_fast8_t limit = MAX_NEST_DEPTH_; // loop hard limit do { - r = execTatbl_(m_temp.tatbl, qs_id); // execute the tran-action table - } while (r >= Q_RET_TRAN_INIT); - - QS_BEGIN_PRE_(QS_QEP_INIT_TRAN, qs_id) - QS_TIME_PRE_(); // time stamp - QS_OBJ_PRE_(this); // this state machine object + // execute the tran. table + r = execTatbl_(m_temp.tatbl, qsId); + --limit; + } while ((r >= Q_RET_TRAN_INIT) && (limit > 0)); + + QF_CRIT_ENTRY(); + Q_ENSURE_INCRIT(290, limit > 0); + + QS_MEM_SYS(); + QS_BEGIN_PRE_(QS_QEP_INIT_TRAN, qsId) + QS_TIME_PRE_(); // time stamp + QS_OBJ_PRE_(this); // this state machine object QS_FUN_PRE_(m_state.obj->stateHandler); // the new current state QS_END_PRE_() + QS_MEM_APP(); + + QF_CRIT_EXIT(); - static_cast(qs_id); // unused parameter (if Q_SPY not defined) + #ifndef Q_UNSAFE + m_temp.uint = ~m_state.uint; + #endif } -//**************************************************************************** -/// @description -/// Dispatches an event for processing to a meta state machine (MSM). -/// The processing of an event represents one run-to-completion (RTC) step. -/// -/// @param[in] e pointer to the event to be dispatched to the MSM -/// @param[in] qs_id QS-id of this state machine (for QS local filter) -/// -/// @note -/// Must be called after QP::QMsm::init(). -/// -void QMsm::dispatch(QEvt const * const e, std::uint_fast8_t const qs_id) { - QMState const *s = m_state.obj; // store the current state +//${QEP::QMsm::dispatch} ..................................................... +void QMsm::dispatch( + QEvt const * const e, + std::uint_fast8_t const qsId) +{ + #ifndef Q_SPY + Q_UNUSED_PAR(qsId); + #endif + + QMState const *s = m_state.obj; // store the current state QMState const *t = s; - QState r; - QS_CRIT_STAT_ - /// @pre current state must be initialized - Q_REQUIRE_ID(300, s != nullptr); + QF_CRIT_STAT + QF_CRIT_ENTRY(); + Q_REQUIRE_INCRIT(300, (s != nullptr) + && (m_state.uint == static_cast(~m_temp.uint))); + Q_REQUIRE_INCRIT(302, QEvt::verify_(e)); - QS_BEGIN_PRE_(QS_QEP_DISPATCH, qs_id) + QS_MEM_SYS(); + QS_BEGIN_PRE_(QS_QEP_DISPATCH, qsId) QS_TIME_PRE_(); // time stamp QS_SIG_PRE_(e->sig); // the signal of the event QS_OBJ_PRE_(this); // this state machine object QS_FUN_PRE_(s->stateHandler); // the current state handler QS_END_PRE_() + QS_MEM_APP(); + + QF_CRIT_EXIT(); // scan the state hierarchy up to the top state... + QState r; + std::int_fast8_t limit = MAX_NEST_DEPTH_; // loop hard limit do { r = (*t->stateHandler)(this, e); // call state handler function @@ -178,211 +210,315 @@ void QMsm::dispatch(QEvt const * const e, std::uint_fast8_t const qs_id) { else if (r == Q_RET_SUPER_SUB) { t = m_temp.obj; // current host state of the submachie } - // event unhandled due to a guard? - else if (r == Q_RET_UNHANDLED) { - - QS_BEGIN_PRE_(QS_QEP_UNHANDLED, qs_id) - QS_SIG_PRE_(e->sig); // the signal of the event - QS_OBJ_PRE_(this); // this state machine object + else { // event unhandled due to a guard? + QF_CRIT_ENTRY(); + // event must be unhandled due to a guard evaluating to 'false' + Q_ASSERT_INCRIT(310, r == Q_RET_UNHANDLED); + + QS_MEM_SYS(); + QS_BEGIN_PRE_(QS_QEP_UNHANDLED, qsId) + QS_SIG_PRE_(e->sig); // the signal of the event + QS_OBJ_PRE_(this); // this state machine object QS_FUN_PRE_(t->stateHandler); // the current state QS_END_PRE_() + QS_MEM_APP(); + + QF_CRIT_EXIT(); t = t->superstate; // advance to the superstate } - else { - // no other return value should be produced - Q_ERROR_ID(310); - } - } while (t != nullptr); - - // any kind of transition taken? - if (r >= Q_RET_TRAN) { -#ifdef Q_SPY - QMState const * const ts = t; // transition source for QS tracing - - // the transition source state must not be nullptr - Q_ASSERT_ID(320, ts != nullptr); -#endif // Q_SPY - + --limit; + } while ((t != nullptr) && (limit > 0)); + QF_CRIT_ENTRY(); + Q_ASSERT_INCRIT(310, limit > 0); + QF_CRIT_EXIT(); + + if (r >= Q_RET_TRAN) { // any kind of tran. taken? + #ifdef Q_SPY + QMState const * const ts = t; // tran. source for QS tracing + + QF_CRIT_ENTRY(); + // the tran. source state must not be nullptr + Q_ASSERT_INCRIT(320, ts != nullptr); + QF_CRIT_EXIT(); + #endif // Q_SPY + + limit = MAX_NEST_DEPTH_; // loop hard limit do { - // save the transition-action table before it gets clobbered + // save the tran-action table before it gets clobbered QMTranActTable const * const tatbl = m_temp.tatbl; - QHsmAttr tmp; // temporary to save intermediate values + QAsmAttr tmp; // temporary to save intermediate values // was TRAN, TRAN_INIT, or TRAN_EP taken? if (r <= Q_RET_TRAN_EP) { - exitToTranSource_(s, t, qs_id); - r = execTatbl_(tatbl, qs_id); + m_temp.obj = nullptr; // clear + exitToTranSource_(s, t, qsId); + r = execTatbl_(tatbl, qsId); s = m_state.obj; } - // was a transition segment to history taken? + // was a tran. segment to history taken? else if (r == Q_RET_TRAN_HIST) { tmp.obj = m_state.obj; // save history m_state.obj = s; // restore the original state - exitToTranSource_(s, t, qs_id); - static_cast(execTatbl_(tatbl, qs_id)); - r = enterHistory_(tmp.obj, qs_id); + exitToTranSource_(s, t, qsId); + static_cast(execTatbl_(tatbl, qsId)); + r = enterHistory_(tmp.obj, qsId); s = m_state.obj; } - // was a transition segment to an exit point taken? - else if (r == Q_RET_TRAN_XP) { + else { + QF_CRIT_ENTRY(); + // must be tran. to exit point + Q_ASSERT_INCRIT(340, r == Q_RET_TRAN_XP); + QF_CRIT_EXIT(); + tmp.act = m_state.act; // save XP action m_state.obj = s; // restore the original state r = (*tmp.act)(this); // execute the XP action if (r == Q_RET_TRAN) { // XP -> TRAN ? -#ifdef Q_SPY + #ifdef Q_SPY tmp.tatbl = m_temp.tatbl; // save m_temp -#endif // Q_SPY - exitToTranSource_(s, t, qs_id); + #endif // Q_SPY + exitToTranSource_(s, t, qsId); // take the tran-to-XP segment inside submachine - static_cast(execTatbl_(tatbl, qs_id)); + static_cast(execTatbl_(tatbl, qsId)); s = m_state.obj; -#ifdef Q_SPY + #ifdef Q_SPY m_temp.tatbl = tmp.tatbl; // restore m_temp -#endif // Q_SPY + #endif // Q_SPY } else if (r == Q_RET_TRAN_HIST) { // XP -> HIST ? tmp.obj = m_state.obj; // save the history m_state.obj = s; // restore the original state -#ifdef Q_SPY + #ifdef Q_SPY s = m_temp.obj; // save m_temp -#endif // Q_SPY - exitToTranSource_(m_state.obj, t, qs_id); + #endif // Q_SPY + exitToTranSource_(m_state.obj, t, qsId); // take the tran-to-XP segment inside submachine - static_cast(execTatbl_(tatbl, qs_id)); -#ifdef Q_SPY - m_temp.obj = s; // restore me->temp -#endif // Q_SPY + static_cast(execTatbl_(tatbl, qsId)); + #ifdef Q_SPY + m_temp.obj = s; // restore m_temp + #endif // Q_SPY s = m_state.obj; m_state.obj = tmp.obj; // restore the history } else { + QF_CRIT_ENTRY(); // TRAN_XP must NOT be followed by any other tran type - Q_ASSERT_ID(330, r < Q_RET_TRAN); + Q_ASSERT_INCRIT(330, r < Q_RET_TRAN); + QF_CRIT_EXIT(); } } - else { - // no other return value should be produced - Q_ERROR_ID(340); - } t = s; // set target to the current state - - } while (r >= Q_RET_TRAN); - - QS_BEGIN_PRE_(QS_QEP_TRAN, qs_id) - QS_TIME_PRE_(); // time stamp - QS_SIG_PRE_(e->sig); // the signal of the event - QS_OBJ_PRE_(this); // this state machine object - QS_FUN_PRE_(ts->stateHandler); // the transition source - QS_FUN_PRE_(s->stateHandler); // the new active state + --limit; + } while ((r >= Q_RET_TRAN) && (limit > 0)); + + QF_CRIT_ENTRY(); + Q_ASSERT_INCRIT(320, limit > 0); + + QS_MEM_SYS(); + QS_BEGIN_PRE_(QS_QEP_TRAN, qsId) + QS_TIME_PRE_(); // time stamp + QS_SIG_PRE_(e->sig); // the signal of the event + QS_OBJ_PRE_(this); // this state machine object + QS_FUN_PRE_(ts->stateHandler); // the tran. source + QS_FUN_PRE_(s->stateHandler); // the new active state QS_END_PRE_() + QS_MEM_APP(); + + QF_CRIT_EXIT(); } -#ifdef Q_SPY + #ifdef Q_SPY // was the event handled? else if (r == Q_RET_HANDLED) { + QF_CRIT_ENTRY(); // internal tran. source can't be nullptr - Q_ASSERT_ID(340, t != nullptr); - - QS_BEGIN_PRE_(QS_QEP_INTERN_TRAN, qs_id) - QS_TIME_PRE_(); // time stamp - QS_SIG_PRE_(e->sig); // the signal of the event - QS_OBJ_PRE_(this); // this state machine object - QS_FUN_PRE_(t->stateHandler); // the source state + Q_ASSERT_INCRIT(340, t != nullptr); + + QS_MEM_SYS(); + QS_BEGIN_PRE_(QS_QEP_INTERN_TRAN, qsId) + QS_TIME_PRE_(); // time stamp + QS_SIG_PRE_(e->sig); // the signal of the event + QS_OBJ_PRE_(this); // this state machine object + QS_FUN_PRE_(t->stateHandler); // the source state QS_END_PRE_() + QS_MEM_APP(); + QF_CRIT_EXIT(); } // event bubbled to the 'top' state? else if (t == nullptr) { - - QS_BEGIN_PRE_(QS_QEP_IGNORED, qs_id) - QS_TIME_PRE_(); // time stamp - QS_SIG_PRE_(e->sig); // the signal of the event - QS_OBJ_PRE_(this); // this state machine object - QS_FUN_PRE_(s->stateHandler); // the current state + QS_CRIT_ENTRY(); + QS_MEM_SYS(); + QS_BEGIN_PRE_(QS_QEP_IGNORED, qsId) + QS_TIME_PRE_(); // time stamp + QS_SIG_PRE_(e->sig); // the signal of the event + QS_OBJ_PRE_(this); // this state machine object + QS_FUN_PRE_(s->stateHandler); // the current state QS_END_PRE_() - + QS_MEM_APP(); + QS_CRIT_EXIT(); } -#endif // Q_SPY - + #endif // Q_SPY else { // empty } - static_cast(qs_id); // unused parameter (if Q_SPY not defined) + #ifndef Q_UNSAFE + m_temp.uint = ~m_state.uint; + #endif } -//**************************************************************************** -#ifdef Q_SPY - /// @description - /// Helper function to get the current state handler of QMsm. - /// - QStateHandler QMsm::getStateHandler() noexcept { - return m_state.obj->stateHandler; +//${QEP::QMsm::isIn} ......................................................... +bool QMsm::isIn(QStateHandler const state) noexcept { + bool inState = false; // assume that this SM is not in 'state' + + QMState const *s = m_state.obj; + std::int_fast8_t limit = MAX_NEST_DEPTH_; // loop hard limit + for (; (s != nullptr) && (limit > 0); --limit) { + if (s->stateHandler == state) { // match found? + inState = true; + break; + } + else { + s = s->superstate; // advance to the superstate + } } -#endif -//**************************************************************************** -/// @description -/// Helper function to execute transition sequence in a tran-action table. -/// -/// @param[in] tatbl pointer to the transition-action table -/// @param[in] qs_id QS-id of this state machine (for QS local filter) -/// -/// @returns -/// the status of the last action from the transition-action table. -/// -/// @note -/// This function is for internal use inside the QEP event processor and -/// should __not__ be called directly from the applications. -/// -QState QMsm::execTatbl_(QMTranActTable const * const tatbl, - std::uint_fast8_t const qs_id) + QF_CRIT_STAT + QF_CRIT_ENTRY(); + Q_ENSURE_INCRIT(490, limit > 0); + QF_CRIT_EXIT(); + + return inState; +} + +//${QEP::QMsm::isInState} .................................................... +bool QMsm::isInState(QMState const * const stateObj) const noexcept { + bool inState = false; // assume that this SM is not in 'state' + + QMState const *s = m_state.obj; + std::int_fast8_t limit = MAX_NEST_DEPTH_; // loop hard limit + for (; (s != nullptr) && (limit > 0); --limit) { + if (s == stateObj) { // match found? + inState = true; + break; + } + else { + s = s->superstate; // advance to the superstate + } + } + + QF_CRIT_STAT + QF_CRIT_ENTRY(); + Q_ENSURE_INCRIT(590, limit > 0); + QF_CRIT_EXIT(); + + return inState; +} + +//${QEP::QMsm::childStateObj} ................................................ +QMState const * QMsm::childStateObj(QMState const * const parent) const noexcept { + QMState const *child = m_state.obj; + bool isFound = false; // start with the child not found + QMState const *s; + + std::int_fast8_t limit = MAX_NEST_DEPTH_; // loop hard limit + for (s = m_state.obj; + (s != nullptr) && (limit > 0); + s = s->superstate) + { + if (s == parent) { + isFound = true; // child is found + break; + } + else { + child = s; + } + --limit; + } + QF_CRIT_STAT + QF_CRIT_ENTRY(); + Q_ASSERT_INCRIT(610, limit > 0); + QF_CRIT_EXIT(); + + if (!isFound) { // still not found? + limit = MAX_NEST_DEPTH_; // loop hard limit + for (s = m_temp.obj; + (s != nullptr) && (limit > 0); + s = s->superstate) + { + if (s == parent) { + isFound = true; // child is found + break; + } + else { + child = s; + } + --limit; + } + } + + QF_CRIT_ENTRY(); + Q_ENSURE_INCRIT(690, isFound && (limit > 0)); + QF_CRIT_EXIT(); + + return child; // return the child +} + +//${QEP::QMsm::execTatbl_} ................................................... +QState QMsm::execTatbl_( + QMTranActTable const * const tatbl, + std::uint_fast8_t const qsId) { - QActionHandler const *a; - QState r = Q_RET_NULL; - QS_CRIT_STAT_ + #ifndef Q_SPY + Q_UNUSED_PAR(qsId); + #endif - /// @pre the transition-action table pointer must not be nullptr - Q_REQUIRE_ID(400, tatbl != nullptr); + QF_CRIT_STAT + QF_CRIT_ENTRY(); + // precondition: + // - the tran-action table pointer must not be NULL + Q_REQUIRE_INCRIT(700, tatbl != nullptr); + QF_CRIT_EXIT(); - for (a = &tatbl->act[0]; *a != nullptr; QEP_ACT_PTR_INC_(a)) { + QState r = Q_RET_NULL; + std::int_fast8_t limit = MAX_TRAN_LENGTH_; // loop hard limit + QActionHandler const *a = &tatbl->act[0]; + for (; (*a != nullptr) && (limit > 0); ++a) { r = (*(*a))(this); // call the action through the 'a' pointer -#ifdef Q_SPY + #ifdef Q_SPY + QS_CRIT_ENTRY(); + QS_MEM_SYS(); if (r == Q_RET_ENTRY) { - - QS_BEGIN_PRE_(QS_QEP_STATE_ENTRY, qs_id) + QS_BEGIN_PRE_(QS_QEP_STATE_ENTRY, qsId) QS_OBJ_PRE_(this); // this state machine object - QS_FUN_PRE_(m_temp.obj->stateHandler); // entered state handler + QS_FUN_PRE_(m_temp.obj->stateHandler); // entered state QS_END_PRE_() } else if (r == Q_RET_EXIT) { - - QS_BEGIN_PRE_(QS_QEP_STATE_EXIT, qs_id) + QS_BEGIN_PRE_(QS_QEP_STATE_EXIT, qsId) QS_OBJ_PRE_(this); // this state machine object - QS_FUN_PRE_(m_temp.obj->stateHandler); // exited state handler + QS_FUN_PRE_(m_temp.obj->stateHandler); // exited state QS_END_PRE_() } else if (r == Q_RET_TRAN_INIT) { - - QS_BEGIN_PRE_(QS_QEP_STATE_INIT, qs_id) + QS_BEGIN_PRE_(QS_QEP_STATE_INIT, qsId) QS_OBJ_PRE_(this); // this state machine object QS_FUN_PRE_(tatbl->target->stateHandler); // source QS_FUN_PRE_(m_temp.tatbl->target->stateHandler); // target QS_END_PRE_() } else if (r == Q_RET_TRAN_EP) { - - QS_BEGIN_PRE_(QS_QEP_TRAN_EP, qs_id) + QS_BEGIN_PRE_(QS_QEP_TRAN_EP, qsId) QS_OBJ_PRE_(this); // this state machine object QS_FUN_PRE_(tatbl->target->stateHandler); // source QS_FUN_PRE_(m_temp.tatbl->target->stateHandler); // target QS_END_PRE_() } else if (r == Q_RET_TRAN_XP) { - - QS_BEGIN_PRE_(QS_QEP_TRAN_XP, qs_id) + QS_BEGIN_PRE_(QS_QEP_TRAN_XP, qsId) QS_OBJ_PRE_(this); // this state machine object QS_FUN_PRE_(tatbl->target->stateHandler); // source QS_FUN_PRE_(m_temp.tatbl->target->stateHandler); // target @@ -391,10 +527,14 @@ QState QMsm::execTatbl_(QMTranActTable const * const tatbl, else { // empty } -#endif // Q_SPY + QS_MEM_APP(); + QS_CRIT_EXIT(); + #endif // Q_SPY + --limit; } - - static_cast(qs_id); // unused parameter (if Q_SPY not defined) + QF_CRIT_ENTRY(); + Q_ENSURE_INCRIT(790, *a == nullptr); + QF_CRIT_EXIT(); m_state.obj = (r >= Q_RET_TRAN) ? m_temp.tatbl->target @@ -402,183 +542,124 @@ QState QMsm::execTatbl_(QMTranActTable const * const tatbl, return r; } -//**************************************************************************** -/// @description -/// Helper function to exit the current state configuration to the -/// transition source, which is a hierarchical state machine might be a -/// superstate of the current state. -/// -/// @param[in] s pointer to the current state -/// @param[in] ts pointer to the transition source state -/// @param[in] qs_id QS-id of this state machine (for QS local filter) -/// -void QMsm::exitToTranSource_(QMState const *s, - QMState const * const ts, - std::uint_fast8_t const qs_id) +//${QEP::QMsm::exitToTranSource_} ............................................ +void QMsm::exitToTranSource_( + QMState const * const cs, + QMState const * const ts, + std::uint_fast8_t const qsId) { + #ifndef Q_SPY + Q_UNUSED_PAR(qsId); + #endif + + QF_CRIT_STAT + // exit states from the current state to the tran. source state - while (s != ts) { + QMState const *s = cs; + std::int_fast8_t limit = MAX_NEST_DEPTH_; // loop hard limit + for (; (s != ts) && (limit > 0); --limit) { // exit action provided in state 's'? if (s->exitAction != nullptr) { // execute the exit action static_cast((*s->exitAction)(this)); - QS_CRIT_STAT_ - QS_BEGIN_PRE_(QS_QEP_STATE_EXIT, qs_id) + QS_CRIT_ENTRY(); + QS_MEM_SYS(); + QS_BEGIN_PRE_(QS_QEP_STATE_EXIT, qsId) QS_OBJ_PRE_(this); // this state machine object QS_FUN_PRE_(s->stateHandler); // the exited state handler QS_END_PRE_() + QS_MEM_APP(); + QS_CRIT_EXIT(); } s = s->superstate; // advance to the superstate - // reached the top of a submachine? - if (s == nullptr) { + if (s == nullptr) { // reached the top of a submachine? s = m_temp.obj; // the superstate from QM_SM_EXIT() - Q_ASSERT_ID(510, s != nullptr); + QF_CRIT_ENTRY(); + Q_ASSERT_INCRIT(810, s != nullptr); // must be valid + QF_CRIT_EXIT(); } } - static_cast(qs_id); // unused parameter (if Q_SPY not defined) + QF_CRIT_ENTRY(); + Q_ENSURE_INCRIT(890, limit > 0); + QF_CRIT_EXIT(); } -//**************************************************************************** -/// @description -/// Static helper function to execute the segment of transition to history -/// after entering the composite state and -/// -/// @param[in] hist pointer to the history substate -/// @param[in] qs_id QS-id of this state machine (for QS local filter) -/// -/// @returns -/// QP::Q_RET_INIT, if an initial transition has been executed in the last -/// entered state or QP::Q_RET_NULL if no such transition was taken. -/// -QState QMsm::enterHistory_(QMState const * const hist, - std::uint_fast8_t const qs_id) +//${QEP::QMsm::enterHistory_} ................................................ +QState QMsm::enterHistory_( + QMState const * const hist, + std::uint_fast8_t const qsId) { + #ifndef Q_SPY + Q_UNUSED_PAR(qsId); + #endif + QMState const *s = hist; - QMState const *ts = m_state.obj; // transition source + QMState const *ts = m_state.obj; // tran. source QMState const *epath[MAX_ENTRY_DEPTH_]; - QState r; - std::uint_fast8_t i = 0U; // entry path index - QS_CRIT_STAT_ - QS_BEGIN_PRE_(QS_QEP_TRAN_HIST, qs_id) + QF_CRIT_STAT + + QS_CRIT_ENTRY(); + QS_MEM_SYS(); + QS_BEGIN_PRE_(QS_QEP_TRAN_HIST, qsId) QS_OBJ_PRE_(this); // this state machine object QS_FUN_PRE_(ts->stateHandler); // source state handler QS_FUN_PRE_(hist->stateHandler); // target state handler QS_END_PRE_() + QS_MEM_APP(); + QS_CRIT_EXIT(); - while (s != ts) { + std::int_fast8_t i = 0; // tran. entry path index + while ((s != ts) && (i < MAX_ENTRY_DEPTH_)) { if (s->entryAction != nullptr) { epath[i] = s; ++i; - Q_ASSERT_ID(620, - i <= static_cast(Q_DIM(epath))); } s = s->superstate; if (s == nullptr) { ts = s; // force exit from the for-loop } } + QF_CRIT_ENTRY(); + Q_ASSERT_INCRIT(910, s == ts); + QF_CRIT_EXIT(); // retrace the entry path in reverse (desired) order... - while (i > 0U) { + while (i > 0) { --i; - // run entry action in epath[i] - static_cast((*epath[i]->entryAction)(this)); + (*epath[i]->entryAction)(this); // run entry action in epath[i] - QS_BEGIN_PRE_(QS_QEP_STATE_ENTRY, qs_id) + QS_CRIT_ENTRY(); + QS_MEM_SYS(); + QS_BEGIN_PRE_(QS_QEP_STATE_ENTRY, qsId) QS_OBJ_PRE_(this); QS_FUN_PRE_(epath[i]->stateHandler); // entered state handler QS_END_PRE_() + QS_MEM_APP(); + QS_CRIT_EXIT(); } - m_state.obj = hist; // set current state to the transition target + m_state.obj = hist; // set current state to the tran. target // initial tran. present? + QState r; if (hist->initAction != nullptr) { - r = (*hist->initAction)(this); // execute the transition action + r = (*hist->initAction)(this); // execute the tran. action } else { r = Q_RET_NULL; } - static_cast(qs_id); // unused parameter (if Q_SPY not defined) return r; } -//**************************************************************************** -/// @description -/// Tests if a state machine derived from QMsm is-in a given state. -/// -/// @note -/// For a MSM, to "be-in" a state means also to "be-in" a superstate of -/// of the state. -/// -/// @param[in] st pointer to the QMState object that corresponds to the -/// tested state. -/// @returns -/// 'true' if the MSM is in the \c st and 'false' otherwise -/// -bool QMsm::isInState(QMState const * const st) const noexcept { - bool inState = false; // assume that this MSM is not in 'state' - - for (QMState const *s = m_state.obj; - s != nullptr; - s = s->superstate) - { - if (s == st) { - inState = true; // match found, return 'true' - break; - } - } - return inState; -} - -//**************************************************************************** -/// -/// @description -/// Finds the child state of the given @c parent, such that this child state -/// is an ancestor of the currently active state. The main purpose of this -/// function is to support **shallow history** transitions in state machines -/// derived from QMsm. -/// -/// @param[in] parent pointer to the state-handler object -/// -/// @returns -/// the child of a given @c parent state, which is an ancestor of the -/// currently active state. For the corner case when the currently active -/// state is the given @c parent state, function returns the @c parent state. -/// -QMState const *QMsm::childStateObj(QMState const * const parent) - const noexcept -{ - QMState const *child = m_state.obj; - bool isFound = false; // start with the child not found - - for (QMState const *s = m_state.obj; - s != nullptr; - s = s->superstate) - { - if (s == parent) { - isFound = true; // child is found - break; - } - else { - child = s; - } - } - - /// @post the child must be found - Q_ENSURE_ID(810, isFound); -#ifdef Q_NASSERT - // avoid compiler warning about unused variable - static_cast(isFound); -#endif - - return child; // return the child +//${QEP::QMsm::topQMState} ................................................... +QMState const * QMsm::topQMState() const noexcept { + return &l_msm_top_s; } } // namespace QP - +//$enddef${QEP::QMsm} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/libraries/qpcpp_arm-cm/src/qep_port.hpp b/libraries/qpcpp_arm-cm/src/qep_port.hpp deleted file mode 100644 index 403a943..0000000 --- a/libraries/qpcpp_arm-cm/src/qep_port.hpp +++ /dev/null @@ -1,51 +0,0 @@ -/// @file -/// @brief QEP/C++ port, GCC-ARM compiler -/// @cond -///*************************************************************************** -/// Last updated for version 6.9.4 -/// Last updated on 2022-01-14 -/// -/// Q u a n t u m L e a P s -/// ------------------------ -/// Modern Embedded Software -/// -/// Copyright (C) 2005-2022 Quantum Leaps. All rights reserved. -/// -/// This program is open source software: you can redistribute it and/or -/// modify it under the terms of the GNU General Public License as published -/// by the Free Software Foundation, either version 3 of the License, or -/// (at your option) any later version. -/// -/// Alternatively, this program may be distributed and modified under the -/// terms of Quantum Leaps commercial licenses, which expressly supersede -/// the GNU General Public License and are specifically designed for -/// licensees interested in retaining the proprietary status of their code. -/// -/// This program is distributed in the hope that it will be useful, -/// but WITHOUT ANY WARRANTY; without even the implied warranty of -/// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -/// GNU General Public License for more details. -/// -/// You should have received a copy of the GNU General Public License -/// along with this program. If not, see . -/// -/// Contact information: -/// -/// -///*************************************************************************** -/// @endcond - -#ifndef QEP_PORT_HPP -#define QEP_PORT_HPP - -// enable QP/Spy software tracing instrumentation -#define Q_SPY 1U - -//! no-return function specifier (GCC-ARM compiler) -#define Q_NORETURN __attribute__ ((noreturn)) void - -#include // Exact-width types. C++11 Standard - -#include "qep.hpp" // QEP platform-independent public interface - -#endif // QEP_PORT_HPP diff --git a/libraries/qpcpp_arm-cm/src/qequeue.hpp b/libraries/qpcpp_arm-cm/src/qequeue.hpp index 547bf6f..d648bbb 100644 --- a/libraries/qpcpp_arm-cm/src/qequeue.hpp +++ b/libraries/qpcpp_arm-cm/src/qequeue.hpp @@ -1,74 +1,55 @@ -/// @file -/// @brief platform-independent fast "raw" thread-safe event queue interface -/// @ingroup qf -/// @cond -///*************************************************************************** -/// Last updated for version 6.9.1 -/// Last updated on 2020-09-15 -/// -/// Q u a n t u m L e a P s -/// ------------------------ -/// Modern Embedded Software -/// -/// Copyright (C) 2005-2020 Quantum Leaps. All rights reserved. -/// -/// This program is open source software: you can redistribute it and/or -/// modify it under the terms of the GNU General Public License as published -/// by the Free Software Foundation, either version 3 of the License, or -/// (at your option) any later version. -/// -/// Alternatively, this program may be distributed and modified under the -/// terms of Quantum Leaps commercial licenses, which expressly supersede -/// the GNU General Public License and are specifically designed for -/// licensees interested in retaining the proprietary status of their code. -/// -/// This program is distributed in the hope that it will be useful, -/// but WITHOUT ANY WARRANTY; without even the implied warranty of -/// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -/// GNU General Public License for more details. -/// -/// You should have received a copy of the GNU General Public License -/// along with this program. If not, see . -/// -/// Contact information: -/// -/// -///*************************************************************************** -/// @endcond - -#ifndef QEQUEUE_HPP -#define QEQUEUE_HPP - -/// @description -/// This header file must be included in all QF ports that use native QF -/// event queue for active objects. Also, this file needs to be included -/// in the QP/C++ library when the application uses QP::QMActive::defer() / -/// QP::QMActive::recall(). Finally, this file is also needed when the "raw" -/// thread-safe queues are used for communication between active objects -/// and non-framework entities, such as ISRs, device drivers, or legacy -/// code. +//$file${include::qequeue.hpp} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv +// +// Model: qpcpp.qm +// File: ${include::qequeue.hpp} +// +// This code has been generated by QM 6.1.1 . +// DO NOT EDIT THIS FILE MANUALLY. All your changes will be lost. +// +// This code is covered by the following QP license: +// License # : LicenseRef-QL-dual +// Issued to : Any user of the QP/C++ real-time embedded framework +// Framework(s) : qpcpp +// Support ends : 2024-12-31 +// License scope: +// +// Copyright (C) 2005 Quantum Leaps, LLC . +// +// Q u a n t u m L e a P s +// ------------------------ +// Modern Embedded Software +// +// SPDX-License-Identifier: GPL-3.0-or-later OR LicenseRef-QL-commercial +// +// This software is dual-licensed under the terms of the open source GNU +// General Public License version 3 (or any later version), or alternatively, +// under the terms of one of the closed source Quantum Leaps commercial +// licenses. +// +// The terms of the open source GNU General Public License version 3 +// can be found at: +// +// The terms of the closed source Quantum Leaps commercial licenses +// can be found at: +// +// Redistributions in source code must retain this top-level comment block. +// Plagiarizing this software to sidestep the license obligations is illegal. +// +// Contact information: +// +// +// +//$endhead${include::qequeue.hpp} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +#ifndef QEQUEUE_HPP_ +#define QEQUEUE_HPP_ #ifndef QF_EQUEUE_CTR_SIZE - - //! The size (in bytes) of the ring-buffer counters used in the - //! native QF event queue implementation. Valid values: 1U, 2U, or 4U; - //! default 1U. - /// @description - /// This macro can be defined in the QF port file (qf_port.hpp) to - /// configure the QP::QEQueueCtr type. Here the macro is not defined - /// so the default of 1 byte is chosen. #define QF_EQUEUE_CTR_SIZE 1U #endif - namespace QP { #if (QF_EQUEUE_CTR_SIZE == 1U) - //! The data type to store the ring-buffer counters based on - //! the macro #QF_EQUEUE_CTR_SIZE. - /// @description - /// The dynamic range of this data type determines the maximum length - /// of the ring buffer managed by the native QF event queue. using QEQueueCtr = std::uint8_t; #elif (QF_EQUEUE_CTR_SIZE == 2U) using QEQueueCtr = std::uint16_t; @@ -78,198 +59,71 @@ namespace QP { #error "QF_EQUEUE_CTR_SIZE defined incorrectly, expected 1U, 2U, or 4U" #endif +class QEvt; // forward declaration + +} // namespace QP + +//$declare${QF::QEQueue} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv +namespace QP { -//**************************************************************************** -//! Native QF Event Queue class -/// @description -/// 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.@n -/// @n -/// The native QF event queue is configured by defining the macro -/// #QF_EQUEUE_TYPE as QP::QEQueue in the specific QF port header file.@n -/// @n -/// The QP::QEQueue class contains only data members for managing an event -/// queue, but does not contain the storage for the queue buffer, which must -/// be provided externally during the queue initialization.@n -/// @n -/// 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.@n -/// @n -/// The QP::QEQueue class is used with two sets of functions. One set is for -/// the active object event queue, which needs to block the active object -/// task when the event queue is empty and unblock it when events are posted -/// to the queue. The interface for the native active object event queue -/// consists of the following functions: QP::QMActive::post(), -/// QP::QMActive::postLIFO(), and QP::QMActive::get_(). Additionally the -/// function QP::QEQueue::init() is used to initialize the queue.@n -/// @n -/// The other set of functions, uses this class as a simple "raw" event -/// queue to pass events between entities other than active objects, such as -/// ISRs. The "raw" event queue is not capable of blocking on the get() -/// operation, but is still thread-safe because it uses QF critical section -/// to protect its integrity. The interface for the "raw" thread-safe queue -/// consists of the following functions: QP::QEQueue::post(), -/// QP::QEQueue::postLIFO(), and QP::QEQueue::get(). Additionally the -/// function QP::QEQueue::init() is used to initialize the queue.@n -/// @n -/// @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. +//${QF::QEQueue} ............................................................. class QEQueue { private: - - //! pointer to event at the front of the queue - /// @description - /// 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.@n - /// @n - /// 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 * volatile m_frontEvt; - - //! pointer to the start of the ring buffer - QEvt const **m_ring; - - //! offset of the end of the ring buffer from the start of the buffer + QEvt const ** m_ring; QEQueueCtr m_end; - - //! offset to where next event will be inserted into the buffer QEQueueCtr volatile m_head; - - //! offset of where next event will be extracted from the buffer QEQueueCtr volatile m_tail; - - //! number of free events in the ring buffer QEQueueCtr volatile m_nFree; - - //! minimum number of free events ever in the ring buffer. - /// @note this attribute remembers the low-watermark of the ring buffer, - /// which provides a valuable information for sizing event queues. - /// @sa QP::QF::getQueueMin(). QEQueueCtr m_nMin; -public: - //! public default constructor - QEQueue(void) noexcept; - - //! Initializes the native QF event queue - /// @description - /// The parameters are as follows: @p qSto[] is the ring buffer storage, - /// @p 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[], std::uint_fast16_t const qLen) noexcept; - - //! "raw" thread-safe QF event queue implementation for the event - //! posting (FIFO). You can call this function from any task context or - //! ISR context. This function uses internally a critical section. - /// @description - /// The argument @p margin specifies the minimum number of free entries - /// in the queue that must be available for posting to succeed. The - /// function returns true (success) if the posting succeeded (with the - /// provided margin) and false (failure) when the posting fails. - /// - /// @note - /// The function raises an assertion if the @p margin is zero and the - /// queue becomes full and cannot accept the event. - /// - /// @sa QP::QEQueue::postLIFO(), QP::QEQueue::get() - bool post(QEvt const * const e, std::uint_fast16_t const margin, - std::uint_fast8_t const qs_id) noexcept; - - //! "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 QP::QEQueue::postLIFO(), QP::QEQueue::get() - void postLIFO(QEvt const * const e, - std::uint_fast8_t const qs_id) noexcept; - - //! "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 QP::QEQueue::post(), QP::QEQueue::postLIFO(), QP::QEQueue::get() - QEvt const *get(std::uint_fast8_t const qs_id) noexcept; + // friends... + friend class QActive; + friend class QTicker; + friend class QXMutex; + friend class QXThread; - //! "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 QP::QMActive::defer(), QP::QMActive::recall() - QEQueueCtr getNFree(void) const noexcept { +public: + QEQueue() noexcept + : m_frontEvt(nullptr), + m_ring(nullptr), + m_end(0U), + m_head(0U), + m_tail(0U), + m_nFree(0U), + m_nMin(0U) + {} + +#ifdef Q_XTOR + ~QEQueue(); +#endif // def Q_XTOR + void init( + QEvt const * qSto[], + std::uint_fast16_t const qLen) noexcept; + bool post( + QEvt const * const e, + std::uint_fast16_t const margin, + std::uint_fast8_t const qsId) noexcept; + void postLIFO( + QEvt const * const e, + std::uint_fast8_t const qsId) noexcept; + QEvt const * get(std::uint_fast8_t const qsId) noexcept; + QEQueueCtr getNFree() const noexcept { return m_nFree; } - - //! "raw" thread-safe QF event queue operation for obtaining the minimum - //! number of free entries ever in the queue (a.k.a. "low-watermark"). - /// - /// @description - /// This operation needs to be used with caution because the - /// "low-watermark" can change unexpectedly. The main intent for using - /// this operation is to get an idea of queue usage to size the queue - /// adequately. - /// - /// @returns the minimum number of free entries ever in the queue - /// since init. - QEQueueCtr getNMin(void) const noexcept { + QEQueueCtr getNMin() const noexcept { return m_nMin; } - - //! "raw" thread-safe QF event queue operation to find out if the queue - //! is empty - /// @note - /// This operation needs to be used with caution because the - /// queue status can change unexpectedly. The main intent for using - /// this operation is in conjunction with event deferral. In this case - /// the queue is accessed only from a single thread (by a single AO), - /// so no other entity can post events to the queue. - /// - /// @sa QP::QMActive::defer(), QP::QMActive::recall() - bool isEmpty(void) const noexcept { + bool isEmpty() const noexcept { return m_frontEvt == nullptr; } private: - //! disallow copying of QEQueue - QEQueue(QEQueue const &) = delete; - - //! disallow assignment of QEQueue - QEQueue & operator=(QEQueue const &) = delete; - - friend class QF; - friend class QActive; - friend class QXThread; - friend class QTicker; - friend class QS; -}; + QEQueue(QEQueue const & other) = delete; + QEQueue & operator=(QEQueue const & other) = delete; +}; // class QEQueue } // namespace QP +//$enddecl${QF::QEQueue} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -#endif // QEQUEUE_HPP - +#endif // QEQUEUE_HPP_ diff --git a/libraries/qpcpp_arm-cm/src/qf.hpp b/libraries/qpcpp_arm-cm/src/qf.hpp deleted file mode 100644 index 3665941..0000000 --- a/libraries/qpcpp_arm-cm/src/qf.hpp +++ /dev/null @@ -1,913 +0,0 @@ -/// @file -/// @brief QF/C++ platform-independent public interface. -/// @ingroup qf -/// @cond -///*************************************************************************** -/// Last updated for version 6.9.3 -/// Last updated on 2021-02-26 -/// -/// Q u a n t u m L e a P s -/// ------------------------ -/// Modern Embedded Software -/// -/// Copyright (C) 2005-2021 Quantum Leaps. All rights reserved. -/// -/// This program is open source software: you can redistribute it and/or -/// modify it under the terms of the GNU General Public License as published -/// by the Free Software Foundation, either version 3 of the License, or -/// (at your option) any later version. -/// -/// Alternatively, this program may be distributed and modified under the -/// terms of Quantum Leaps commercial licenses, which expressly supersede -/// the GNU General Public License and are specifically designed for -/// licensees interested in retaining the proprietary status of their code. -/// -/// This program is distributed in the hope that it will be useful, -/// but WITHOUT ANY WARRANTY; without even the implied warranty of -/// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -/// GNU General Public License for more details. -/// -/// You should have received a copy of the GNU General Public License -/// along with this program. If not, see . -/// -/// Contact information: -/// -/// -///*************************************************************************** -/// @endcond - -#ifndef QF_HPP -#define QF_HPP - -//**************************************************************************** -#ifndef QPSET_HPP -#include "qpset.hpp" -#endif - -#ifdef Q_EVT_CTOR -#include // for placement new -#endif // Q_EVT_CTOR - -//**************************************************************************** -// apply defaults for all undefined configuration parameters -// -#ifndef QF_EVENT_SIZ_SIZE - //! Default value of the macro configurable value in qf_port.hpp - #define QF_EVENT_SIZ_SIZE 2U -#endif - -#ifndef QF_MAX_EPOOL - //! Default value of the macro configurable value in qf_port.hpp - #define QF_MAX_EPOOL 3U -#endif - -#ifndef QF_MAX_TICK_RATE - //! Default value of the macro configurable value in qf_port.hpp - //! Valid values: [0U..15U]; default 1U - #define QF_MAX_TICK_RATE 1U -#elif (QF_MAX_TICK_RATE > 15U) - #error "QF_MAX_TICK_RATE exceeds the maximum of 15U" -#endif - -#ifndef QF_TIMEEVT_CTR_SIZE - //! macro to override the default QTimeEvtCtr size. - //! Valid values 1U, 2U, or 4U; default 2U - #define QF_TIMEEVT_CTR_SIZE 2U -#endif - - -//**************************************************************************** -namespace QP { - -#if (QF_EVENT_SIZ_SIZE == 1U) - using QEvtSize = std::uint8_t; -#elif (QF_EVENT_SIZ_SIZE == 2U) - //! The data type to store the block-size defined based on - //! the macro #QF_EVENT_SIZ_SIZE. - /// @description - /// The dynamic range of this data type determines the maximum block - /// size that can be managed by the pool. - using QEvtSize = std::uint16_t; -#elif (QF_EVENT_SIZ_SIZE == 4U) - using QEvtSize = std::uint32_t; -#else - #error "QF_EVENT_SIZ_SIZE defined incorrectly, expected 1U, 2U, or 4U" -#endif - -//**************************************************************************** -#if (QF_TIMEEVT_CTR_SIZE == 1U) - using QTimeEvtCtr = std::uint8_t; -#elif (QF_TIMEEVT_CTR_SIZE == 2U) - //! type of the Time Event counter, which determines the dynamic - //! range of the time delays measured in clock ticks. - /// @description - /// This alias is configurable via the preprocessor switch - /// #QF_TIMEEVT_CTR_SIZE. The other possible values of this type are - /// as follows: @n - /// std::uint8_t when (QF_TIMEEVT_CTR_SIZE == 1U), and @n - /// std::uint32_t when (QF_TIMEEVT_CTR_SIZE == 4U). - using QTimeEvtCtr = std::uint16_t; -#elif (QF_TIMEEVT_CTR_SIZE == 4U) - using QTimeEvtCtr = std::uint32_t; -#else - #error "QF_TIMEEVT_CTR_SIZE defined incorrectly, expected 1U, 2U, or 4U" -#endif - -class QEQueue; // forward declaration - -//**************************************************************************** -//! QActive active object (based on QP::QHsm implementation) -/// @description -/// Active objects in QP 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 in a run-to-completion (RTC) fashion, while QF -/// encapsulates all the details of thread-safe event exchange and queuing. -/// @n@n -/// QP::QActive represents an active object that uses the QP::QHsm-style -/// implementation strategy for state machines. This strategy is tailored -/// to manual coding, but it is also supported by the QM modeling tool. -/// The resulting code is slower than in the QP::QMsm-style implementation -/// strategy. -/// -/// @note -/// QP::QActive is not intended to be instantiated directly, but rather serves -/// as the base class for derivation of active objects in the applications. -/// -/// @sa QP::QMActive -/// -/// @usage -/// The following example illustrates how to derive an active object from -/// QP::QActive. -/// @include qf_qactive.cpp -/// -class QActive : public QHsm { -public: // for access from extern "C" functions -#ifdef QF_EQUEUE_TYPE - //! OS-dependent event-queue type. - /// @description - /// The type of the queue depends on the underlying operating system or - /// a kernel. Many kernels support "message queues" that can be adapted - /// to deliver QF events to the active object. Alternatively, QF provides - /// a native event queue implementation that can be used as well. - /// - /// @note - /// The native QF event queue is configured by defining the macro - /// #QF_EQUEUE_TYPE as QP::QEQueue. - QF_EQUEUE_TYPE m_eQueue; -#endif - -#ifdef QF_OS_OBJECT_TYPE - //! OS-dependent per-thread object. - /// @description - /// 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 - //! OS-dependent representation of the thread of the active object. - /// @description - /// 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 - -#ifdef QXK_HPP // QXK kernel used? - //! QXK dynamic priority (1..#QF_MAX_ACTIVE) of AO/thread. - std::uint8_t m_dynPrio; -#endif - - //! QF priority (1..#QF_MAX_ACTIVE) of this active object. - std::uint8_t m_prio; - -protected: - //! protected constructor (abstract class) - QActive(QStateHandler const initial) noexcept; - -public: - //! Starts execution of an active object and registers the object - //! with the framework. - virtual void start(std::uint_fast8_t const prio, - QEvt const * * const qSto, std::uint_fast16_t const qLen, - void * const stkSto, std::uint_fast16_t const stkSize, - void const * const par); - - //! Overloaded start function (no initialization event) - virtual void start(std::uint_fast8_t const prio, - QEvt const * * const qSto, std::uint_fast16_t const qLen, - void * const stkSto, std::uint_fast16_t const stkSize) - { - this->start(prio, qSto, qLen, stkSto, stkSize, nullptr); - } - -#ifdef QF_ACTIVE_STOP - //! Stops execution of an active object and removes it from the - //! framework's supervision. - /// @attention - /// QActive::stop() must be called only from the AO that is about - /// to stop its execution. By that time, any pointers or references - /// to the AO are considered invalid (dangling) and it becomes - /// illegal for the rest of the application to post events to the AO. - void stop(void); -#endif - -#ifndef Q_SPY - //! Posts an event @p e directly to the event queue of the active - //! object @p me using the First-In-First-Out (FIFO) policy. - virtual bool post_(QEvt const * const e, - std::uint_fast16_t const margin) noexcept; -#else - virtual bool post_(QEvt const * const e, std::uint_fast16_t const margin, - void const * const sender) noexcept; -#endif - - //! Posts an event directly to the event queue of the active object - //! using the Last-In-First-Out (LIFO) policy. - virtual void postLIFO(QEvt const * const e) noexcept; - - //! Un-subscribes from the delivery of all signals to the active object. - void unsubscribeAll(void) const noexcept; - - //! Subscribes for delivery of signal @p sig to the active object - void subscribe(enum_t const sig) const noexcept; - - //! Un-subscribes from the delivery of signal @p sig to the active object. - void unsubscribe(enum_t const sig) const noexcept; - - //! Defer an event to a given separate event queue. - bool defer(QEQueue * const eq, QEvt const * const e) const noexcept; - - //! Recall a deferred event from a given event queue. - bool recall(QEQueue * const eq) noexcept; - - //! Flush the specified deferred queue 'eq'. - std::uint_fast16_t flushDeferred(QEQueue * const eq) const noexcept; - - //! Get the priority of the active object. - std::uint_fast8_t getPrio(void) const noexcept { - return static_cast(m_prio); - } - - //! Set the priority of the active object. - void setPrio(std::uint_fast8_t const prio) { - m_prio = static_cast(prio); - } - - //! Generic setting of additional attributes (useful in QP ports) - void setAttr(std::uint32_t attr1, void const *attr2 = nullptr); - -#ifdef QF_OS_OBJECT_TYPE - //! accessor to the OS-object for extern "C" functions, such as - //! the QK or QXK schedulers - QF_OS_OBJECT_TYPE &getOsObject(void) noexcept { return m_osObject; } -#endif - -#ifdef QF_THREAD_TYPE - //! accessor to the Thread for extern "C" functions, such as - //! the QK or QXK schedulers - QF_THREAD_TYPE &getThread(void) noexcept { return m_thread; } -#endif - - //! Get an event from the event queue of an active object. - QEvt const *get_(void) noexcept; - -// duplicated API to be used exclusively inside ISRs (useful in some QP ports) -#ifdef QF_ISR_API -#ifdef Q_SPY - virtual bool postFromISR_(QEvt const * const e, - std::uint_fast16_t const margin, void *par, - void const * const sender) noexcept; -#else - virtual bool postFromISR_(QEvt const * const e, - std::uint_fast16_t const margin, void *par) noexcept; -#endif // Q_SPY -#endif // QF_ISR_API - -// friendships... -private: - friend class QF; - friend class QTimeEvt; - friend class QTicker; -#ifdef QK_HPP - friend class QMutex; -#endif // QK_HPP -#ifdef QXK_HPP - friend class QXK; - friend class QXThread; - friend class QXMutex; - friend class QXSemaphore; -#endif // QXK_HPP -#ifdef Q_UTEST - friend class QActiveDummy; -#endif // Q_UTEST -}; - -//**************************************************************************** -//! QMActive active object (based on QP::QMsm implementation) -/// @description -/// QP::QMActive represents an active object that uses the QP::QMsm-style -/// state machine implementation strategy. This strategy requires the use of -/// the QM modeling tool to generate state machine code automatically, but -/// the code is faster than in the QP::QHsm-style implementation strategy -/// and needs less run-time support (smaller event-processor). -/// -/// @note -/// QP::QMActive is not intended to be instantiated directly, but rather -/// serves as the base class for derivation of active objects in the -/// applications. -/// -/// @sa QP::QActive -/// -/// @usage -/// The following example illustrates how to derive an active object from -/// QP::QMActive. -/// @include qf_qmactive.cpp -/// -class QMActive : public QActive { -public: - // all the following operations delegate to the QHsm class... - void init(void const * const e, - std::uint_fast8_t const qs_id) override; - void init(std::uint_fast8_t const qs_id) override; - void dispatch(QEvt const * const e, - std::uint_fast8_t const qs_id) override; - -#ifdef Q_SPY - //! Get the current state handler of the QMsm - QStateHandler getStateHandler() noexcept override; -#endif - - //! Tests if a given state is part of the active state configuration - bool isInState(QMState const * const st) const noexcept; - - //! Return the current active state object (read only) - QMState const *stateObj(void) const noexcept { - return m_state.obj; - } - - //! Obtain the current active child state of a given parent (read only) - QMState const *childStateObj(QMState const * const parent) const noexcept; - -protected: - //! protected constructor (abstract class) - QMActive(QStateHandler const initial) noexcept; - -private: - //! operations inherited from QP::QHsm, but disallowed in QP::QMActive - using QHsm::isIn; - using QHsm::state; - using QHsm::childState; -}; - - -//**************************************************************************** -//! Time Event class -/// @description -/// 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.@n -/// @n -/// Time events, as any other QF events derive from the QP::QEvt base -/// class. Typically, you will use a time event as-is, but you can also -/// further derive more specialized time events from it by adding some more -/// data members and/or specialized functions that operate on the specialized -/// time events.@n -/// @n -/// Internally, the armed time events are organized into a bi-directional -/// linked list. This linked list is scanned in every invocation of the -/// QP::QF::tickX_() function. Only armed (timing out) time events are in the -/// list, so only armed time events consume CPU cycles. -/// -/// @note -/// QF manages the time events in the macro TICK_X(), which must be called -/// periodically, from the clock tick ISR or from the special QP::QTicker -/// active object. -/// -/// @note -/// Even though QP::QTimeEvt is a subclass of QP::QEvt, QP::QTimeEvt instances -/// can NOT be allocated dynamically from event pools. In other words, it is -/// illegal to allocate QP::QTimeEvt instances with the Q_NEW() or Q_NEW_X() -/// macros. -/// -class QTimeEvt : public QEvt { -private: - - //! link to the next time event in the list - QTimeEvt * volatile m_next; - - //! the active object that receives the time events - /// @description - /// The m_act pointer is reused inside the QP implementation to hold - /// the head of the list of newly armed time events. - void * volatile m_act; - - //! the internal down-counter of the time event. - /// @description - /// The down-counter is decremented by 1 in every TICK_X() - /// invocation. The time event fires (gets posted or published) when - /// the down-counter reaches zero. - QTimeEvtCtr volatile m_ctr; - - //! the interval for the periodic time event (zero for the one-shot - //! time event). - /// @description - /// 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: - - //! The Time Event constructor. - QTimeEvt(QActive * const act, enum_t const sgnl, - std::uint_fast8_t const tickRate = 0U) noexcept; - - //! Arm a time event (one shot or periodic) for event posting. - void armX(QTimeEvtCtr const nTicks, - QTimeEvtCtr const interval = 0U) noexcept; - - //! Disarm a time event. - bool disarm(void) noexcept; - - //! Rearm a time event. - bool rearm(QTimeEvtCtr const nTicks) noexcept; - - //! Check the "was disarmed" status of a time event. - bool wasDisarmed(void) noexcept; - - //! Get the current value of the down-counter of a time event. - QTimeEvtCtr currCtr(void) const noexcept; - -private: - //! private default constructor only for friends - QTimeEvt(void) noexcept; - - //! private copy constructor to disallow copying of QTimeEvts - QTimeEvt(QTimeEvt const &) = delete; - - //! private assignment operator to disallow assigning of QTimeEvts - QTimeEvt & operator=(QTimeEvt const &) = delete; - - //! encapsulate the cast the m_act attribute to QActive* - QActive *toActive(void) noexcept { - return static_cast(m_act); - } - - //! encapsulate the cast the m_act attribute to QTimeEvt* - QTimeEvt *toTimeEvt(void) noexcept { - return static_cast(m_act); - } - - friend class QF; - friend class QS; -#ifdef QXK_HPP - friend class QXThread; - friend void QXK_activate_(void); -#endif // QXK_HPP -}; - - -//**************************************************************************** -//! Subscriber List -/// @description -/// This data type represents a set of active objects that subscribe to -/// a given signal. The set is represented as priority-set, where each -/// bit corresponds to the unique priority of an active object. -using QSubscrList = QPSet; - - -//**************************************************************************** -//! QF services. -/// @description -/// This class groups together QF services. It has only static members and -/// should not be instantiated. -class QF { -public: - - //! get the current QF version number string of the form X.Y.Z - static char_t const *getVersion(void) noexcept { - return versionStr; - } - - //! QF initialization. - static void init(void); - - //! Publish-subscribe initialization. - static void psInit(QSubscrList * const subscrSto, - enum_t const maxSignal) noexcept; - - //! Event pool initialization for dynamic allocation of events. - static void poolInit(void * const poolSto, - std::uint_fast32_t const poolSize, - std::uint_fast16_t const evtSize) noexcept; - - //! Obtain the block size of any registered event pools - static std::uint_fast16_t poolGetMaxBlockSize(void) noexcept; - - - //! Transfers control to QF to run the application. - static int_t run(void); - - //! Startup QF callback. - static void onStartup(void); - - //! Cleanup QF callback. - static void onCleanup(void); - - //! Function invoked by the application layer to stop the QF - //! application and return control to the OS/Kernel. - static void stop(void); - -#ifndef Q_SPY - static void publish_(QEvt const * const e) noexcept; - static void tickX_(std::uint_fast8_t const tickRate) noexcept; -#else - //! Publish event to the framework. - static void publish_(QEvt const * const e, - void const * const sender, - std::uint_fast8_t const qs_id) noexcept; - - //! Processes all armed time events at every clock tick. - static void tickX_(std::uint_fast8_t const tickRate, - void const * const sender) noexcept; -#endif // Q_SPY - - //! Returns true if all time events are inactive and false - //! any time event is active. - static bool noTimeEvtsActiveX(std::uint_fast8_t const tickRate) noexcept; - - //! This function returns the minimum of free entries of the given - //! event pool. - static std::uint_fast16_t getPoolMin(std::uint_fast8_t const poolId) - noexcept; - - //! This function returns the minimum of free entries of the given - //! event queue. - static std::uint_fast16_t getQueueMin(std::uint_fast8_t const prio) - noexcept; - - //! Internal QF implementation of creating new dynamic event. - static QEvt *newX_(std::uint_fast16_t const evtSize, - std::uint_fast16_t const margin, - enum_t const sig) noexcept; - - //! Recycle a dynamic event. - static void gc(QEvt const * const e) noexcept; - - //! Internal QF implementation of creating new event reference. - static QEvt const *newRef_(QEvt const * const e, - QEvt const * const evtRef) noexcept; - - //! Internal QF implementation of deleting event reference. - static void deleteRef_(QEvt const * const evtRef) noexcept; - - //! Remove the active object from the framework. - static void remove_(QActive * const a) noexcept; - - //! array of registered active objects - static QActive *active_[QF_MAX_ACTIVE + 1U]; - - //! Thread routine for executing an active object @p act. - static void thread_(QActive *act); - - //! Register an active object to be managed by the framework - static void add_(QActive * const a) noexcept; - - //! Clear a specified region of memory to zero. - static void bzero(void * const start, - std::uint_fast16_t const len) noexcept; - -// API to be used exclusively inside ISRs (useful in some QP ports) -#ifdef QF_ISR_API -#ifdef Q_SPY - static void publishFromISR_(QEvt const *e, void *par, - void const *sender) noexcept; - static void tickXfromISR_(std::uint_fast8_t const tickRate, void *par, - void const * const sender) noexcept; -#else - static void publishFromISR_(QEvt const *e, void *par) noexcept; - static void tickXfromISR_(std::uint_fast8_t const tickRate, - void *par) noexcept; -#endif // Q_SPY - - static QEvt *newXfromISR_(std::uint_fast16_t const evtSize, - std::uint_fast16_t const margin, - enum_t const sig) noexcept; - static void gcFromISR(QEvt const *e) noexcept; - -#endif // QF_ISR_API - -// to be used in QF ports only... -private: - //! heads of linked lists of time events, one for every clock tick rate - static QTimeEvt timeEvtHead_[QF_MAX_TICK_RATE]; - - friend class QActive; - friend class QTimeEvt; - friend class QS; -#ifdef QXK_HPP - friend class QXThread; -#endif // QXK_HPP -}; - -//! special value of margin that causes asserting failure in case -//! event allocation or event posting fails -std::uint_fast16_t const QF_NO_MARGIN = 0xFFFFU; - - -//**************************************************************************** -//! Ticker Active Object class -/// @description -/// QP::QTicker is an efficient active object specialized to process -/// QF system clock tick at a specified tick frequency [0..#QF_MAX_TICK_RATE]. -/// Placing system clock tick processing in an active object allows you -/// to remove the non-deterministic TICK_X() processing from the interrupt -/// level and move it into the thread-level, where you can prioritize it -/// as low as you wish. -/// -class QTicker : public QActive { -public: - explicit QTicker(std::uint_fast8_t const tickRate) noexcept; // ctor - - void init(void const * const e, - std::uint_fast8_t const qs_id) noexcept override; - void init(std::uint_fast8_t const qs_id) noexcept override { - this->init(qs_id); - } - void dispatch(QEvt const * const e, - std::uint_fast8_t const qs_id) noexcept override; -#ifndef Q_SPY - bool post_(QEvt const * const e, - std::uint_fast16_t const margin) noexcept override; -#else - bool post_(QEvt const * const e, std::uint_fast16_t const margin, - void const * const sender) noexcept override; -#endif - void postLIFO(QEvt const * const e) noexcept override; -}; - -} // namespace QP - -//**************************************************************************** -#ifndef QF_CRIT_EXIT_NOP - //! No-operation for exiting a critical section - /// @description - /// In some QF ports the critical section exit takes effect only on the - /// next machine instruction. If this next instruction is another entry - /// to a critical section, the critical section won't be really exited, - /// but rather the two adjecent critical sections would be merged. - /// The #QF_CRIT_EXIT_NOP() macro contains minimal code required to - /// prevent such merging of critical sections in such merging of - /// critical sections in QF ports, in which it can occur. - #define QF_CRIT_EXIT_NOP() (static_cast(0)) -#endif - -//**************************************************************************** -// Provide the constructor for the QEvt class? -#ifdef Q_EVT_CTOR - - #define Q_NEW(evtT_, sig_, ...) \ - (new(QP::QF::newX_(sizeof(evtT_), QP::QF_NO_MARGIN, 0)) \ - evtT_((sig_), ##__VA_ARGS__)) - - #define Q_NEW_X(e_, evtT_, margin_, sig_, ...) do { \ - (e_) = static_cast( \ - QP::QF::newX_(sizeof(evtT_), (margin_), 0)); \ - if ((e_) != nullptr) { \ - new((e_)) evtT_((sig_), ##__VA_ARGS__); \ - } \ - } while (false) - -#else // QEvt is a POD (Plain Old Datatype) - - //! Allocate a dynamic event. - /// @description - /// The macro calls the internal QF function QP::QF::newX_() with - /// margin == QP::QF_NO_MARGIN, which causes an assertion when the event - /// cannot be successfully allocated. - /// - /// @param[in] evtT_ event type (class name) of the event to allocate - /// @param[in] sig_ signal to assign to the newly allocated event - /// - /// @returns a valid event pointer cast to the type @p evtT_. - /// - /// @note - /// If #Q_EVT_CTOR is defined, the Q_NEW() macro becomes variadic and - /// takes all the arguments needed by the constructor of the event - /// class being allocated. The constructor is then called by means - /// of the placement-new operator. - /// - /// @usage - /// The following example illustrates dynamic allocation of an event: - /// @include qf_post.cpp - #define Q_NEW(evtT_, sig_) (static_cast( \ - QP::QF::newX_(sizeof(evtT_), QP::QF_NO_MARGIN, (sig_)))) - - //! Allocate a dynamic event (non-asserting version). - /// @description - /// This macro allocates a new event and sets the pointer @p e_, while - /// leaving at least @p margin_ of events still available in the pool - /// - /// @param[out] e_ pointer to the newly allocated event - /// @param[in] evtT_ event type (class name) of the event to allocate - /// @param[in] margin_ number of events that must remain available - /// in the given pool after this allocation. The - /// special value QP::QF_NO_MARGIN causes asserting - /// failure in case event allocation fails. - /// @param[in] sig_ signal to assign to the newly allocated event - /// - /// @returns an event pointer cast to the type @p evtT_ or NULL if the - /// event cannot be allocated with the specified @p margin. - /// - /// @note - /// If #Q_EVT_CTOR is defined, the Q_NEW_X() macro becomes variadic and - /// takes all the arguments needed by the constructor of the event - /// class being allocated. The constructor is then called by means - /// of the placement-new operator. - /// - /// @usage - /// The following example illustrates dynamic allocation of an event: - /// @include qf_postx.cpp - #define Q_NEW_X(e_, evtT_, margin_, sig_) \ - ((e_) = static_cast(QP::QF::newX_( \ - sizeof(evtT_), (margin_), (sig_)))) -#endif - -//! Create a new reference of the current event `e` */ -/// @description -/// The current event processed by an active object is available only for -/// the duration of the run-to-completion (RTC) step. After that step, the -/// current event is no longer available and the framework might recycle -/// (garbage-collect) the event. The macro Q_NEW_REF() explicitly creates -/// a new reference to the current event that can be stored and used beyond -/// the current RTC step, until the reference is explicitly recycled by -/// means of the macro Q_DELETE_REF(). -/// -/// @param[in,out] evtRef_ event reference to create -/// @param[in] evtT_ event type (class name) of the event refrence -/// -/// @usage -/// The example **defer** in the directory `examples/win32/defer` illustrates -/// the use of Q_NEW_REF() -/// -/// @sa Q_DELETE_REF() -/// -#define Q_NEW_REF(evtRef_, evtT_) \ - ((evtRef_) = static_cast(QP::QF::newRef_(e, (evtRef_)))) - -//! Delete the event reference */ -/// @description -/// Every event reference created with the macro Q_NEW_REF() needs to be -/// eventually deleted by means of the macro Q_DELETE_REF() to avoid leaking -/// the event. -/// -/// @param[in,out] evtRef_ event reference to delete -/// -/// @usage -/// The example **defer** in the directory `examples/win32/defer` illustrates -/// the use of Q_DELETE_REF() -/// -/// @sa Q_NEW_REF() -/// -#define Q_DELETE_REF(evtRef_) do { \ - QP::QF::deleteRef_((evtRef_)); \ - (evtRef_) = 0U; \ -} while (false) - - -//**************************************************************************** -// QS software tracing integration, only if enabled -#ifdef Q_SPY - - //! Invoke the system clock tick processing QP::QF::tickX_(). - /// @description - /// This macro is the recommended way of invoking clock tick processing, - /// because it provides the vital information for software tracing and - /// avoids any overhead when the tracing is disabled. - /// - /// @param[in] tickRate_ clock tick rate to be serviced through this call - /// @param[in] sender_ pointer to the sender object. This parameter - /// is actually only used when QS software tracing is enabled - /// (macro #Q_SPY is defined) - /// @note - /// When QS software tracing is disabled, the macro calls QF_tickX_() - /// without the @p sender parameter, so the overhead of passing this - /// extra parameter is entirely avoided. - /// - /// @note - /// The pointer to the sender object is not necessarily a pointer - /// to an active object. In fact, when TICK_X() is called from - /// an interrupt, you would create a unique object just to unambiguously - /// identify the ISR as the sender of the time events. - /// - /// @sa QP::QF::tickX_() - #define TICK_X(tickRate_, sender_) tickX_((tickRate_), (sender_)) - - //! Invoke the event publishing facility QP::QF::publish_(). This macro - /// @description - /// This macro is the recommended way of publishing events, because it - /// provides the vital information for software tracing and avoids any - /// overhead when the tracing is disabled. - /// - /// @param[in] e_ pointer to the posted event - /// @param[in] sender_ pointer to the sender object. This parameter 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 - /// @p sender_ parameter, so the overhead of passing this - /// extra parameter is entirely avoided. - /// - /// @note - /// The pointer to the sender object is not necessarily a pointer - /// to an active object. In fact, if 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 QP::QF::publish_() - #define PUBLISH(e_, sender_) \ - publish_((e_), (sender_), (sender_)->getPrio()) - - //! Invoke the direct event posting facility QP::QActive::post_(). - /// @description - /// This macro asserts if the queue overflows and cannot accept the event. - /// - /// @param[in] e_ pointer to the event to post - /// @param[in] sender_ pointer to the sender object. - /// - /// @note - /// The @p sendedr_ parameter is actually only used when QS tracing - /// is enabled (macro #Q_SPY is defined). When QS software tracing is - /// disenabled, the POST() macro does not pass the @p sender_ - /// parameter, so the overhead of passing this extra parameter is entirely - /// avoided. - /// - /// @note the pointer to the sender object is not necessarily a pointer - /// to an active object. In fact, if POST() is called from an interrupt - /// or other context, you can create a unique object just to - /// unambiguously identify the sender of the event. - /// - /// @sa QP::QActive::post_() - #define POST(e_, sender_) post_((e_), QP::QF_NO_MARGIN, (sender_)) - - //! Invoke the direct event posting facility QP::QActive::post_() - //! without delivery guarantee. - /// @description - /// This macro does not assert if the queue overflows and cannot accept - /// the event with the specified margin of free slots remaining. - /// - /// @param[in] e_ pointer to the event to post - /// @param[in] margin_ the minimum free slots in the queue, which - /// must still be available after posting the event. - /// The special value QP::QF_NO_MARGIN causes - /// asserting failure in case event posting fails. - /// @param[in] sender_ pointer to the sender object. - /// - /// @returns - /// 'true' if the posting succeeded, and 'false' if the posting - /// failed due to insufficient margin of free entries available in - /// the queue. - /// - /// @note - /// The @p sender_ parameter is actually only used when QS tracing - /// is enabled (macro #Q_SPY is defined). When QS software tracing is - /// disabled, the POST_X() macro does not pass the @p sender_ parameter, - /// so the overhead of passing this extra parameter is entirely avoided. - /// - /// @note - /// The pointer to the sender object is not necessarily a pointer - /// to an active object. In fact, if POST_X() is called from an - /// interrupt or other context, you can create a unique object just to - /// unambiguously identify the sender of the event. - /// - /// @usage - /// @include qf_postx.cpp - #define POST_X(e_, margin_, sender_) \ - post_((e_), (margin_), (sender_)) - -#else - - #define PUBLISH(e_, dummy_) publish_((e_)) - #define POST(e_, dummy_) post_((e_), QP::QF_NO_MARGIN) - #define POST_X(e_, margin_, dummy_) post_((e_), (margin_)) - #define TICK_X(tickRate_, dummy_) tickX_((tickRate_)) - -#endif // Q_SPY - -//! Invoke the system clock tick processing for rate 0 -/// @sa TICK_X() -#define TICK(sender_) TICK_X(0U, (sender_)) - -#endif // QF_HPP - diff --git a/libraries/qpcpp_arm-cm/src/qf_act.cpp b/libraries/qpcpp_arm-cm/src/qf_act.cpp index 55a8855..970ebea 100644 --- a/libraries/qpcpp_arm-cm/src/qf_act.cpp +++ b/libraries/qpcpp_arm-cm/src/qf_act.cpp @@ -1,45 +1,49 @@ -/// @file -/// @brief QP::QActive services and QF support code -/// @ingroup qf -/// @cond -///*************************************************************************** -/// Last updated for version 6.9.1 -/// Last updated on 2020-09-18 -/// -/// Q u a n t u m L e a P s -/// ------------------------ -/// Modern Embedded Software -/// -/// Copyright (C) 2005-2020 Quantum Leaps. All rights reserved. -/// -/// This program is open source software: you can redistribute it and/or -/// modify it under the terms of the GNU General Public License as published -/// by the Free Software Foundation, either version 3 of the License, or -/// (at your option) any later version. -/// -/// Alternatively, this program may be distributed and modified under the -/// terms of Quantum Leaps commercial licenses, which expressly supersede -/// the GNU General Public License and are specifically designed for -/// licensees interested in retaining the proprietary status of their code. -/// -/// This program is distributed in the hope that it will be useful, -/// but WITHOUT ANY WARRANTY; without even the implied warranty of -/// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -/// GNU General Public License for more details. -/// -/// You should have received a copy of the GNU General Public License -/// along with this program. If not, see . -/// -/// Contact information: -/// -/// -///*************************************************************************** -/// @endcond - +//$file${src::qf::qf_act.cpp} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv +// +// Model: qpcpp.qm +// File: ${src::qf::qf_act.cpp} +// +// This code has been generated by QM 6.1.1 . +// DO NOT EDIT THIS FILE MANUALLY. All your changes will be lost. +// +// This code is covered by the following QP license: +// License # : LicenseRef-QL-dual +// Issued to : Any user of the QP/C++ real-time embedded framework +// Framework(s) : qpcpp +// Support ends : 2024-12-31 +// License scope: +// +// Copyright (C) 2005 Quantum Leaps, LLC . +// +// Q u a n t u m L e a P s +// ------------------------ +// Modern Embedded Software +// +// SPDX-License-Identifier: GPL-3.0-or-later OR LicenseRef-QL-commercial +// +// This software is dual-licensed under the terms of the open source GNU +// General Public License version 3 (or any later version), or alternatively, +// under the terms of one of the closed source Quantum Leaps commercial +// licenses. +// +// The terms of the open source GNU General Public License version 3 +// can be found at: +// +// The terms of the closed source Quantum Leaps commercial licenses +// can be found at: +// +// Redistributions in source code must retain this top-level comment block. +// Plagiarizing this software to sidestep the license obligations is illegal. +// +// Contact information: +// +// +// +//$endhead${src::qf::qf_act.cpp} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ #define QP_IMPL // this is QP implementation -#include "qf_port.hpp" // QF port -#include "qf_pkg.hpp" // QF package-scope interface -#include "qassert.h" // QP embedded systems-friendly assertions +#include "qp_port.hpp" // QP port +#include "qp_pkg.hpp" // QP package-scope interface +#include "qsafe.h" // QP Functional Safety (FuSa) Subsystem #ifdef Q_SPY // QS software tracing enabled? #include "qs_port.hpp" // QS port #include "qs_pkg.hpp" // QS facilities for pre-defined trace records @@ -47,138 +51,83 @@ #include "qs_dummy.hpp" // disable the QS software tracing #endif // Q_SPY -//**************************************************************************** -/// @description -/// macro to encapsulate pointer increment, which violates MISRA-C:2004 -/// required rule 17.4 (pointer arithmetic used). -/// -/// @param[in] p_ pointer to be incremented. -/// -#define QF_PTR_INC_(p_) (++(p_)) - -namespace QP { - -Q_DEFINE_THIS_MODULE("qf_act") - -// public objects ************************************************************ -QActive *QF::active_[QF_MAX_ACTIVE + 1U]; // to be used by QF ports only +// unnamed namespace for local definitions with internal linkage +namespace { +//Q_DEFINE_THIS_MODULE("qf_act") +} // unnamed namespace -//**************************************************************************** -/// @description -/// This function adds a given active object to the active objects managed -/// by the QF framework. It should not be called by the application directly, -/// only through the function QP::QActive::start(). -/// -/// @param[in] a pointer to the active object to add to the framework. -/// -/// @note The priority of the active object @p a should be set before calling -/// this function. -/// -/// @sa QP::QF::remove_() -/// -void QF::add_(QActive * const a) noexcept { - std::uint_fast8_t const p = static_cast(a->m_prio); +//$skip${QP_VERSION} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv +// Check for the minimum required QP version +#if (QP_VERSION < 730U) || (QP_VERSION != ((QP_RELEASE^4294967295U) % 0x3E8U)) +#error qpcpp version 7.3.0 or higher required +#endif +//$endskip${QP_VERSION} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - Q_REQUIRE_ID(100, (0U < p) && (p <= QF_MAX_ACTIVE) - && (active_[p] == nullptr)); - QF_CRIT_STAT_ - QF_CRIT_E_(); - active_[p] = a; // registger the active object at this priority - QF_CRIT_X_(); -} +//$define${QF::QActive::registry_[QF_MAX_ACTIVE + 1U]} vvvvvvvvvvvvvvvvvvvvvvv +namespace QP { +QActive * QActive::registry_[QF_MAX_ACTIVE + 1U]; -//**************************************************************************** -/// @description -/// This function removes a given active object from the active objects -/// managed by the QF framework. It should not be called by the QP ports. -/// -/// @param[in] a pointer to the active object to remove from the framework. -/// -/// @note -/// The active object that is removed from the framework can no longer -/// participate in the publish-subscribe event exchange. -/// -/// @sa QP::QF::add_() -/// -void QF::remove_(QActive * const a) noexcept { - std::uint_fast8_t const p = static_cast(a->m_prio); +} // namespace QP +//$enddef${QF::QActive::registry_[QF_MAX_ACTIVE + 1U]} ^^^^^^^^^^^^^^^^^^^^^^^ - Q_REQUIRE_ID(200, (0U < p) && (p <= QF_MAX_ACTIVE) - && (active_[p] == a)); +//$define${QF::QF-pkg} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv +namespace QP { +namespace QF { - QF_CRIT_STAT_ - QF_CRIT_E_(); - active_[p] = nullptr; // free-up the priority level - a->m_state.fun = nullptr; // invalidate the state - QF_CRIT_X_(); -} +//${QF::QF-pkg::priv_} ....................................................... +QF::Attr priv_; -//**************************************************************************** -/// @description -/// Clears a memory buffer by writing zeros byte-by-byte. -/// -/// @param[in] start pointer to the beginning of a memory buffer. -/// @param[in] len length of the memory buffer to clear (in bytes) -/// -/// @note The main application of this function is clearing the internal QF -/// variables upon startup. This is done to avoid problems with non-standard -/// startup code provided with some compilers and toolsets (e.g., TI DSPs or -/// Microchip MPLAB), which does not zero the uninitialized variables, as -/// required by the ANSI C standard. -/// -void QF::bzero(void * const start, std::uint_fast16_t const len) noexcept { +//${QF::QF-pkg::bzero_} ...................................................... +void bzero_( + void * const start, + std::uint_fast16_t const len) noexcept +{ std::uint8_t *ptr = static_cast(start); for (std::uint_fast16_t n = len; n > 0U; --n) { *ptr = 0U; - QF_PTR_INC_(ptr); + ++ptr; } } +} // namespace QF } // namespace QP +//$enddef${QF::QF-pkg} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -// Log-base-2 calculations ... -#ifndef QF_LOG2 - -//! function that returns (log2(x) + 1), where @p x is a 32-bit bitmask -/// -/// @description -/// This function returns the 1-based number of the most significant 1-bit -/// of a 32-bit bitmask. This function can be replaced in the QP ports, if -/// the CPU has special instructions, such as CLZ (count leading zeros). -/// -extern "C" { - - std::uint_fast8_t QF_LOG2(QP::QPSetBits x) noexcept { - static std::uint8_t const log2LUT[16] = { - 0U, 1U, 2U, 2U, 3U, 3U, 3U, 3U, - 4U, 4U, 4U, 4U, 4U, 4U, 4U, 4U - }; - std::uint_fast8_t n = 0U; - QP::QPSetBits t; +//$define${QF::types::QF_LOG2} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv +namespace QP { -#if (QF_MAX_ACTIVE > 16U) - t = static_cast(x >> 16U); - if (t != 0U) { - n += 16U; - x = t; - } -#endif -#if (QF_MAX_ACTIVE > 8U) - t = (x >> 8U); - if (t != 0U) { - n += 8U; - x = t; - } -#endif - t = (x >> 4U); - if (t != 0U) { - n += 4U; - x = t; - } - return n + log2LUT[x]; +//${QF::types::QF_LOG2} ...................................................... +#ifndef QF_LOG2 +std::uint_fast8_t QF_LOG2(QP::QPSetBits x) noexcept { + static std::uint8_t const log2LUT[16] = { + 0U, 1U, 2U, 2U, 3U, 3U, 3U, 3U, + 4U, 4U, 4U, 4U, 4U, 4U, 4U, 4U + }; + std::uint_fast8_t n = 0U; + QP::QPSetBits t; + + #if (QF_MAX_ACTIVE > 16U) + t = static_cast(x >> 16U); + if (t != 0U) { + n += 16U; + x = t; } + #endif + #if (QF_MAX_ACTIVE > 8U) + t = (x >> 8U); + if (t != 0U) { + n += 8U; + x = t; + } + #endif + t = (x >> 4U); + if (t != 0U) { + n += 4U; + x = t; + } + return n + log2LUT[x]; +} +#endif // ndef QF_LOG2 -} // extern "C" - -#endif // QF_LOG2 - +} // namespace QP +//$enddef${QF::types::QF_LOG2} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/libraries/qpcpp_arm-cm/src/qf_actq.cpp b/libraries/qpcpp_arm-cm/src/qf_actq.cpp index 59b4854..8a63504 100644 --- a/libraries/qpcpp_arm-cm/src/qf_actq.cpp +++ b/libraries/qpcpp_arm-cm/src/qf_actq.cpp @@ -1,50 +1,49 @@ -/// @file -/// @brief QP::QActive native queue operations (based on QP::QEQueue) -/// -/// @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). -/// -/// @ingroup qf -/// @cond -///*************************************************************************** -/// Last updated for version 6.9.1 -/// Last updated on 2020-09-17 -/// -/// Q u a n t u m L e a P s -/// ------------------------ -/// Modern Embedded Software -/// -/// Copyright (C) 2005-2020 Quantum Leaps. All rights reserved. -/// -/// This program is open source software: you can redistribute it and/or -/// modify it under the terms of the GNU General Public License as published -/// by the Free Software Foundation, either version 3 of the License, or -/// (at your option) any later version. -/// -/// Alternatively, this program may be distributed and modified under the -/// terms of Quantum Leaps commercial licenses, which expressly supersede -/// the GNU General Public License and are specifically designed for -/// licensees interested in retaining the proprietary status of their code. -/// -/// This program is distributed in the hope that it will be useful, -/// but WITHOUT ANY WARRANTY; without even the implied warranty of -/// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -/// GNU General Public License for more details. -/// -/// You should have received a copy of the GNU General Public License -/// along with this program. If not, see . -/// -/// Contact information: -/// -/// -///*************************************************************************** -/// @endcond - +//$file${src::qf::qf_actq.cpp} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv +// +// Model: qpcpp.qm +// File: ${src::qf::qf_actq.cpp} +// +// This code has been generated by QM 6.1.1 . +// DO NOT EDIT THIS FILE MANUALLY. All your changes will be lost. +// +// This code is covered by the following QP license: +// License # : LicenseRef-QL-dual +// Issued to : Any user of the QP/C++ real-time embedded framework +// Framework(s) : qpcpp +// Support ends : 2024-12-31 +// License scope: +// +// Copyright (C) 2005 Quantum Leaps, LLC . +// +// Q u a n t u m L e a P s +// ------------------------ +// Modern Embedded Software +// +// SPDX-License-Identifier: GPL-3.0-or-later OR LicenseRef-QL-commercial +// +// This software is dual-licensed under the terms of the open source GNU +// General Public License version 3 (or any later version), or alternatively, +// under the terms of one of the closed source Quantum Leaps commercial +// licenses. +// +// The terms of the open source GNU General Public License version 3 +// can be found at: +// +// The terms of the closed source Quantum Leaps commercial licenses +// can be found at: +// +// Redistributions in source code must retain this top-level comment block. +// Plagiarizing this software to sidestep the license obligations is illegal. +// +// Contact information: +// +// +// +//$endhead${src::qf::qf_actq.cpp} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ #define QP_IMPL // this is QP implementation -#include "qf_port.hpp" // QF port -#include "qf_pkg.hpp" // QF package-scope interface -#include "qassert.h" // QP embedded systems-friendly assertions +#include "qp_port.hpp" // QP port +#include "qp_pkg.hpp" // QP package-scope interface +#include "qsafe.h" // QP Functional Safety (FuSa) Subsystem #ifdef Q_SPY // QS software tracing enabled? #include "qs_port.hpp" // QS port #include "qs_pkg.hpp" // QS facilities for pre-defined trace records @@ -52,71 +51,65 @@ #include "qs_dummy.hpp" // disable the QS software tracing #endif // Q_SPY -namespace QP { - +//============================================================================ +// unnamed namespace for local definitions with internal linkage +namespace { Q_DEFINE_THIS_MODULE("qf_actq") +} // unnamed namespace -#ifdef Q_SPY -//**************************************************************************** -/// @description -/// Direct event posting is the simplest asynchronous communication method -/// available in QF. -/// -/// @param[in,out] e pointer to the event to be posted -/// @param[in] margin number of required free slots in the queue -/// after posting the event. The special value QP::QF_NO_MARGIN -/// means that this function will assert if posting fails. -/// @param[in] sender pointer to a sender object (used in QS only) -/// -/// @returns -/// 'true' (success) if the posting succeeded (with the provided margin) and -/// 'false' (failure) when the posting fails. -/// -/// @attention -/// Should be called only via the macro POST() or POST_X(). -/// -/// @note -/// The QP::QF_NO_MARGIN value of the @p margin argument is special and -/// denotes situation when the post() operation is assumed to succeed (event -/// delivery guarantee). An assertion fires, when the event cannot be -/// delivered in this case. -/// -/// @usage -/// @include qf_post.cpp -/// -/// @sa -/// QActive::postLIFO() -/// -bool QActive::post_(QEvt const * const e, - std::uint_fast16_t const margin, - void const * const sender) noexcept -#else -bool QActive::post_(QEvt const * const e, - std::uint_fast16_t const margin) noexcept +//$skip${QP_VERSION} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv +// Check for the minimum required QP version +#if (QP_VERSION < 730U) || (QP_VERSION != ((QP_RELEASE^4294967295U) % 0x3E8U)) +#error qpcpp version 7.3.0 or higher required #endif +//$endskip${QP_VERSION} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +//$define${QF::QActive::post_} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv +namespace QP { + +//${QF::QActive::post_} ...................................................... +bool QActive::post_( + QEvt const * const e, + std::uint_fast16_t const margin, + void const * const sender) noexcept { - bool status; - QF_CRIT_STAT_ - QS_TEST_PROBE_DEF(&QActive::post_) + #ifndef Q_SPY + Q_UNUSED_PAR(sender); + #endif + + #ifdef Q_UTEST // test? + #if Q_UTEST != 0 // testing QP-stub? + if (m_temp.fun == Q_STATE_CAST(0)) { // QActiveDummy? + return static_cast(this)->fakePost(e, margin, sender); + } + #endif + #endif - /// @pre event pointer must be valid - Q_REQUIRE_ID(100, e != nullptr); + QF_CRIT_STAT + QF_CRIT_ENTRY(); + QF_MEM_SYS(); - QF_CRIT_E_(); - QEQueueCtr nFree = m_eQueue.m_nFree; // get volatile into the temporary + #ifndef Q_UNSAFE + std::uint8_t const pcopy = static_cast(~m_prio_dis); + Q_REQUIRE_INCRIT(102, (QEvt::verify_(e)) && (m_prio == pcopy)); + #endif + + QEQueueCtr nFree = m_eQueue.m_nFree; // get volatile into temporary // test-probe#1 for faking queue overflow + QS_TEST_PROBE_DEF(&QActive::post_) QS_TEST_PROBE_ID(1, nFree = 0U; ) - if (margin == QF_NO_MARGIN) { + bool status; + if (margin == QF::NO_MARGIN) { if (nFree > 0U) { status = true; // can post } else { status = false; // cannot post - Q_ERROR_CRIT_(110); // must be able to post the event + Q_ERROR_INCRIT(190); // must be able to post the event } } else if (nFree > static_cast(margin)) { @@ -126,355 +119,334 @@ bool QActive::post_(QEvt const * const e, status = false; // cannot post, but don't assert } - // is it a dynamic event? - if (e->poolId_ != 0U) { - QF_EVT_REF_CTR_INC_(e); // increment the reference counter + // is it a mutable event? + if (e->getPoolNum_() != 0U) { + QEvt_refCtr_inc_(e); // increment the reference counter } if (status) { // can post the event? - --nFree; // one free entry just used up - m_eQueue.m_nFree = nFree; // update the volatile + --nFree; // one free entry just used up + m_eQueue.m_nFree = nFree; // update the original if (m_eQueue.m_nMin > nFree) { m_eQueue.m_nMin = nFree; // update minimum so far } - QS_BEGIN_NOCRIT_PRE_(QS_QF_ACTIVE_POST, m_prio) - QS_TIME_PRE_(); // timestamp - QS_OBJ_PRE_(sender); // the sender object - QS_SIG_PRE_(e->sig); // the signal of the event - QS_OBJ_PRE_(this); // this active object - QS_2U8_PRE_(e->poolId_, e->refCtr_); // pool-Id & ref-ctr - QS_EQC_PRE_(nFree); // number of free entries - QS_EQC_PRE_(m_eQueue.m_nMin); // min number of free entries - QS_END_NOCRIT_PRE_() - -#ifdef Q_UTEST + QS_BEGIN_PRE_(QS_QF_ACTIVE_POST, m_prio) + QS_TIME_PRE_(); // timestamp + QS_OBJ_PRE_(sender); // the sender object + QS_SIG_PRE_(e->sig); // the signal of the event + QS_OBJ_PRE_(this); // this active object + QS_2U8_PRE_(e->getPoolNum_(), e->refCtr_); // poolNum & refCtr + QS_EQC_PRE_(nFree); // # free entries + QS_EQC_PRE_(m_eQueue.m_nMin); // min # free entries + QS_END_PRE_() + + #ifdef Q_UTEST // callback to examine the posted event under the same conditions // as producing the #QS_QF_ACTIVE_POST trace record, which are: - // the local filter for this AO ('me->prio') is set - // - if ((QS::priv_.locFilter[m_prio >> 3U] - & static_cast(1U << (m_prio & 7U))) != 0U) - { + // the local filter for this AO ('m_prio') is set + if (QS_LOC_CHECK_(m_prio)) { QS::onTestPost(sender, this, e, status); } -#endif - // empty queue? - if (m_eQueue.m_frontEvt == nullptr) { - m_eQueue.m_frontEvt = e; // deliver event directly + #endif + + if (m_eQueue.m_frontEvt == nullptr) { // empty queue? + m_eQueue.m_frontEvt = e; // deliver event directly + #ifdef QXK_HPP_ + if (m_state.act == Q_ACTION_CAST(0)) { // eXtended thread? + QXTHREAD_EQUEUE_SIGNAL_(this); // signal the event queue + } + else { + QACTIVE_EQUEUE_SIGNAL_(this); // signal the event queue + } + #else QACTIVE_EQUEUE_SIGNAL_(this); // signal the event queue + #endif } // queue is not empty, insert event into the ring-buffer else { - // insert event pointer e into the buffer (FIFO) - QF_PTR_AT_(m_eQueue.m_ring, m_eQueue.m_head) = e; + // insert event into the ring buffer (FIFO) + m_eQueue.m_ring[m_eQueue.m_head] = e; - // need to wrap head? - if (m_eQueue.m_head == 0U) { + if (m_eQueue.m_head == 0U) { // need to wrap head? m_eQueue.m_head = m_eQueue.m_end; // wrap around } - --m_eQueue.m_head; // advance the head (counter clockwise) + // advance the head (counter clockwise) + m_eQueue.m_head = (m_eQueue.m_head - 1U); } - QF_CRIT_X_(); + QF_MEM_APP(); + QF_CRIT_EXIT(); } else { // cannot post the event - QS_BEGIN_NOCRIT_PRE_(QS_QF_ACTIVE_POST_ATTEMPT, m_prio) - QS_TIME_PRE_(); // timestamp - QS_OBJ_PRE_(sender); // the sender object - QS_SIG_PRE_(e->sig); // the signal of the event - QS_OBJ_PRE_(this); // this active object - QS_2U8_PRE_(e->poolId_, e->refCtr_); // pool-Id & ref-ctr - QS_EQC_PRE_(nFree); // number of free entries - QS_EQC_PRE_(margin); // margin requested - QS_END_NOCRIT_PRE_() - -#ifdef Q_UTEST + QS_BEGIN_PRE_(QS_QF_ACTIVE_POST_ATTEMPT, m_prio) + QS_TIME_PRE_(); // timestamp + QS_OBJ_PRE_(sender); // the sender object + QS_SIG_PRE_(e->sig); // the signal of the event + QS_OBJ_PRE_(this); // this active object + QS_2U8_PRE_(e->getPoolNum_(), e->refCtr_); // poolNum & refCtr + QS_EQC_PRE_(nFree); // # free entries + QS_EQC_PRE_(margin); // margin requested + QS_END_PRE_() + + #ifdef Q_UTEST // callback to examine the posted event under the same conditions // as producing the #QS_QF_ACTIVE_POST trace record, which are: - // the local filter for this AO ('me->prio') is set - // - if ((QS::priv_.locFilter[m_prio >> 3U] - & static_cast(1U << (m_prio & 7U))) != 0U) - { + // the local filter for this AO ('m_prio') is set + if (QS_LOC_CHECK_(m_prio)) { QS::onTestPost(sender, this, e, status); } -#endif + #endif - QF_CRIT_X_(); + QF_MEM_APP(); + QF_CRIT_EXIT(); + #if (QF_MAX_EPOOL > 0U) QF::gc(e); // recycle the event to avoid a leak + #endif } return status; } -//**************************************************************************** -/// @description -/// posts an event to the event queue of the active object using the -/// Last-In-First-Out (LIFO) policy. -/// -/// @note -/// The LIFO policy should be used only for self-posting and with caution, -/// because it alters order of events in the queue. -/// -/// @param[in] e pointer to the event to post to the queue -/// -/// @sa -/// QActive::post_() -/// +} // namespace QP +//$enddef${QF::QActive::post_} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +//$define${QF::QActive::postLIFO} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv +namespace QP { + +//${QF::QActive::postLIFO} ................................................... void QActive::postLIFO(QEvt const * const e) noexcept { - QF_CRIT_STAT_ - QS_TEST_PROBE_DEF(&QActive::postLIFO) + #ifdef Q_UTEST // test? + #if Q_UTEST != 0 // testing QP-stub? + if (m_temp.fun == Q_STATE_CAST(0)) { // QActiveDummy? + static_cast(this)->QActiveDummy::fakePostLIFO(e); + return; + } + #endif + #endif - QF_CRIT_E_(); - QEQueueCtr nFree = m_eQueue.m_nFree;// tmp to avoid UB for volatile access + QF_CRIT_STAT + QF_CRIT_ENTRY(); + QF_MEM_SYS(); + #ifndef Q_UNSAFE + std::uint8_t const pcopy = static_cast(~m_prio_dis); + Q_REQUIRE_INCRIT(202, (QEvt::verify_(e)) && (m_prio == pcopy)); + #endif + + #ifdef QXK_HPP_ + Q_REQUIRE_INCRIT(200, m_state.act != Q_ACTION_CAST(0)); + #endif + + QEQueueCtr nFree = m_eQueue.m_nFree; // get volatile into temporary + + // test-probe#1 for faking queue overflow + QS_TEST_PROBE_DEF(&QActive::postLIFO) QS_TEST_PROBE_ID(1, nFree = 0U; ) - // the queue must be able to accept the event (cannot overflow) - Q_ASSERT_CRIT_(210, nFree != 0U); + Q_REQUIRE_INCRIT(201, nFree != 0U); - // is it a dynamic event? - if (e->poolId_ != 0U) { - QF_EVT_REF_CTR_INC_(e); // increment the reference counter + if (e->getPoolNum_() != 0U) { // is it a mutable event? + QEvt_refCtr_inc_(e); // increment the reference counter } - --nFree; // one free entry just used up - m_eQueue.m_nFree = nFree; // update the volatile + --nFree; // one free entry just used up + m_eQueue.m_nFree = nFree; // update the original if (m_eQueue.m_nMin > nFree) { m_eQueue.m_nMin = nFree; // update minimum so far } - QS_BEGIN_NOCRIT_PRE_(QS_QF_ACTIVE_POST_LIFO, m_prio) - QS_TIME_PRE_(); // timestamp - QS_SIG_PRE_(e->sig); // the signal of this event - QS_OBJ_PRE_(this); // this active object - QS_2U8_PRE_(e->poolId_, e->refCtr_); // pool-Id & ref-ctr - QS_EQC_PRE_(nFree); // number of free entries - QS_EQC_PRE_(m_eQueue.m_nMin); // min number of free entries - QS_END_NOCRIT_PRE_() + QS_BEGIN_PRE_(QS_QF_ACTIVE_POST_LIFO, m_prio) + QS_TIME_PRE_(); // timestamp + QS_SIG_PRE_(e->sig); // the signal of this event + QS_OBJ_PRE_(this); // this active object + QS_2U8_PRE_(e->getPoolNum_(), e->refCtr_); // poolNum & refCtr + QS_EQC_PRE_(nFree); // # free entries + QS_EQC_PRE_(m_eQueue.m_nMin); // min # free entries + QS_END_PRE_() -#ifdef Q_UTEST + #ifdef Q_UTEST // callback to examine the posted event under the same conditions // as producing the #QS_QF_ACTIVE_POST trace record, which are: - // the local filter for this AO ('me->prio') is set - // - if ((QS::priv_.locFilter[m_prio >> 3U] - & static_cast(1U << (m_prio & 7U))) != 0U) - { + // the local filter for this AO ('m_prio') is set + if (QS_LOC_CHECK_(m_prio)) { QS::onTestPost(nullptr, this, e, true); } -#endif + #endif - // read volatile into temporary QEvt const * const frontEvt = m_eQueue.m_frontEvt; m_eQueue.m_frontEvt = e; // deliver the event directly to the front - // was the queue empty? - if (frontEvt == nullptr) { + if (frontEvt == nullptr) { // was the queue empty? QACTIVE_EQUEUE_SIGNAL_(this); // signal the event queue } - // queue was not empty, leave the event in the ring-buffer - else { - ++m_eQueue.m_tail; + else { // queue was not empty, leave the event in the ring-buffer + m_eQueue.m_tail = (m_eQueue.m_tail + 1U); if (m_eQueue.m_tail == m_eQueue.m_end) { // need to wrap the tail? m_eQueue.m_tail = 0U; // wrap around } - QF_PTR_AT_(m_eQueue.m_ring, m_eQueue.m_tail) = frontEvt; + m_eQueue.m_ring[m_eQueue.m_tail] = frontEvt; } - QF_CRIT_X_(); + + QF_MEM_APP(); + QF_CRIT_EXIT(); } -//**************************************************************************** -/// @description -/// The behavior of this function depends on the kernel used in the QF port. -/// For built-in kernels (Vanilla or QK) the function can be called only when -/// the queue is not empty, so it doesn't block. For a blocking kernel/OS -/// the function can block and wait for delivery of an event. -/// -/// @returns -/// A pointer to the received event. The returned pointer is guaranteed to be -/// valid (can't be NULL). -/// -/// @note -/// 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 customized in the QF port -/// (file qf_port.hpp). Depending on the definition of the macro -/// QACTIVE_EQUEUE_WAIT_(), the function might block the calling thread when -/// no events are available. -/// -QEvt const *QActive::get_(void) noexcept { - QF_CRIT_STAT_ - - QF_CRIT_E_(); +} // namespace QP +//$enddef${QF::QActive::postLIFO} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +//$define${QF::QActive::get_} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv +namespace QP { + +//${QF::QActive::get_} ....................................................... +QEvt const * QActive::get_() noexcept { + QF_CRIT_STAT + QF_CRIT_ENTRY(); + QF_MEM_SYS(); + QACTIVE_EQUEUE_WAIT_(this); // wait for event to arrive directly // always remove evt from the front QEvt const * const e = m_eQueue.m_frontEvt; - QEQueueCtr const nFree = m_eQueue.m_nFree + 1U; - m_eQueue.m_nFree = nFree; // upate the number of free - - // any events in the ring buffer? - if (nFree <= m_eQueue.m_end) { + QEQueueCtr const nFree = m_eQueue.m_nFree + 1U; // get volatile into tmp + m_eQueue.m_nFree = nFree; // update the # free + if (nFree <= m_eQueue.m_end) { // any events in the ring buffer? // remove event from the tail - m_eQueue.m_frontEvt = QF_PTR_AT_(m_eQueue.m_ring, m_eQueue.m_tail); - if (m_eQueue.m_tail == 0U) { // need to wrap? + m_eQueue.m_frontEvt = m_eQueue.m_ring[m_eQueue.m_tail]; + if (m_eQueue.m_tail == 0U) { // need to wrap the tail? m_eQueue.m_tail = m_eQueue.m_end; // wrap around } - --m_eQueue.m_tail; - - QS_BEGIN_NOCRIT_PRE_(QS_QF_ACTIVE_GET, m_prio) - QS_TIME_PRE_(); // timestamp - QS_SIG_PRE_(e->sig); // the signal of this event - QS_OBJ_PRE_(this); // this active object - QS_2U8_PRE_(e->poolId_, e->refCtr_); // pool-Id & ref-ctr - QS_EQC_PRE_(nFree); // number of free entries - QS_END_NOCRIT_PRE_() + m_eQueue.m_tail = (m_eQueue.m_tail - 1U); + + QS_BEGIN_PRE_(QS_QF_ACTIVE_GET, m_prio) + QS_TIME_PRE_(); // timestamp + QS_SIG_PRE_(e->sig); // the signal of this event + QS_OBJ_PRE_(this); // this active object + QS_2U8_PRE_(e->getPoolNum_(), e->refCtr_); // poolNum & refCtr + QS_EQC_PRE_(nFree); // # free entries + QS_END_PRE_() } else { - // the queue becomes empty - m_eQueue.m_frontEvt = nullptr; + m_eQueue.m_frontEvt = nullptr; // the queue becomes empty // all entries in the queue must be free (+1 for fronEvt) - Q_ASSERT_CRIT_(310, nFree == (m_eQueue.m_end + 1U)); - - QS_BEGIN_NOCRIT_PRE_(QS_QF_ACTIVE_GET_LAST, m_prio) - QS_TIME_PRE_(); // timestamp - QS_SIG_PRE_(e->sig); // the signal of this event - QS_OBJ_PRE_(this); // this active object - QS_2U8_PRE_(e->poolId_, e->refCtr_); // pool-Id & ref-ctr - QS_END_NOCRIT_PRE_() + Q_ASSERT_INCRIT(310, nFree == (m_eQueue.m_end + 1U)); + + QS_BEGIN_PRE_(QS_QF_ACTIVE_GET_LAST, m_prio) + QS_TIME_PRE_(); // timestamp + QS_SIG_PRE_(e->sig); // the signal of this event + QS_OBJ_PRE_(this); // this active object + QS_2U8_PRE_(e->getPoolNum_(), e->refCtr_); // poolNum & refCtr + QS_END_PRE_() } - QF_CRIT_X_(); + + QF_MEM_APP(); + QF_CRIT_EXIT(); + return e; } -//**************************************************************************** -/// @description -/// Queries the minimum of free ever present in the given event queue of -/// an active object with priority @p prio, since the active object -/// was started. -/// -/// @note -/// QP::QF::getQueueMin() is available only when the native QF event -/// queue implementation is used. Requesting the queue minimum of an unused -/// priority level raises an assertion in the QF. (A priority level becomes -/// used in QF after the call to the QP::QF::add_() function.) -/// -/// @param[in] prio Priority of the active object, whose queue is queried -/// -/// @returns -/// the minimum of free ever present in the given event queue of an active -/// object with priority @p prio, since the active object was started. -/// -std::uint_fast16_t QF::getQueueMin(std::uint_fast8_t const prio) noexcept { - - Q_REQUIRE_ID(400, (prio <= QF_MAX_ACTIVE) - && (active_[prio] != nullptr)); - - QF_CRIT_STAT_ - QF_CRIT_E_(); - std::uint_fast16_t const min = - static_cast(active_[prio]->m_eQueue.m_nMin); - QF_CRIT_X_(); - - return min; -} +} // namespace QP +//$enddef${QF::QActive::get_} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +//$define${QF::QTicker} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv +namespace QP { + +//${QF::QTicker} ............................................................. -//**************************************************************************** +//${QF::QTicker::QTicker} .................................................... QTicker::QTicker(std::uint_fast8_t const tickRate) noexcept - : QActive(nullptr) +: QActive(nullptr) { // reuse m_head for tick-rate m_eQueue.m_head = static_cast(tickRate); } -//............................................................................ -void QTicker::init(void const * const e, - std::uint_fast8_t const qs_id) noexcept + +//${QF::QTicker::init} ....................................................... +void QTicker::init( + void const * const e, + std::uint_fast8_t const qsId) { - static_cast(e); // unused parameter - static_cast(qs_id); // unused parameter + Q_UNUSED_PAR(e); + Q_UNUSED_PAR(qsId); + + QF_CRIT_STAT + QF_CRIT_ENTRY(); + QF_MEM_SYS(); + m_eQueue.m_tail = 0U; + + QF_MEM_APP(); + QF_CRIT_EXIT(); } -//............................................................................ -void QTicker::dispatch(QEvt const * const e, - std::uint_fast8_t const qs_id) noexcept + +//${QF::QTicker::dispatch} ................................................... +void QTicker::dispatch( + QEvt const * const e, + std::uint_fast8_t const qsId) { - static_cast(e); // unused parameter - static_cast(qs_id); // unused parameter + Q_UNUSED_PAR(e); + Q_UNUSED_PAR(qsId); + + QF_CRIT_STAT + QF_CRIT_ENTRY(); + QF_MEM_SYS(); - QF_CRIT_STAT_ - QF_CRIT_E_(); - QEQueueCtr nTicks = m_eQueue.m_tail; // # ticks since the last call + QEQueueCtr nTicks = m_eQueue.m_tail; // save # of ticks m_eQueue.m_tail = 0U; // clear the # ticks - QF_CRIT_X_(); + + QF_MEM_APP(); + QF_CRIT_EXIT(); for (; nTicks > 0U; --nTicks) { - QF::TICK_X(static_cast(m_eQueue.m_head), this); + QTimeEvt::tick(static_cast(m_eQueue.m_head), + this); } } -//............................................................................ -#ifdef Q_SPY -//**************************************************************************** -/// @sa -/// QActive::post_() -/// -bool QTicker::post_(QEvt const * const e, std::uint_fast16_t const margin, - void const * const sender) noexcept -#else -bool QTicker::post_(QEvt const * const e, std::uint_fast16_t const margin) - noexcept -#endif -{ - static_cast(e); // unused parameter - static_cast(margin); // unused parameter - QF_CRIT_STAT_ - QF_CRIT_E_(); +//${QF::QTicker::trig_} ...................................................... +void QTicker::trig_(void const * const sender) noexcept { + #ifndef Q_SPY + Q_UNUSED_PAR(sender); + #endif + + QF_CRIT_STAT + QF_CRIT_ENTRY(); + QF_MEM_SYS(); + if (m_eQueue.m_frontEvt == nullptr) { -#ifdef Q_EVT_CTOR - static QEvt const tickEvt(0U, QEvt::STATIC_EVT); -#else - static QEvt const tickEvt = { 0U, 0U, 0U }; -#endif // Q_EVT_CTOR + static QEvt const tickEvt(0U); // immutable event m_eQueue.m_frontEvt = &tickEvt; // deliver event directly - --m_eQueue.m_nFree; // one less free event + m_eQueue.m_nFree = (m_eQueue.m_nFree - 1U); // one less free event QACTIVE_EQUEUE_SIGNAL_(this); // signal the event queue } - ++m_eQueue.m_tail; // account for one more tick event + // account for one more tick event + m_eQueue.m_tail = (m_eQueue.m_tail + 1U); - QS_BEGIN_NOCRIT_PRE_(QS_QF_ACTIVE_POST, m_prio) + QS_BEGIN_PRE_(QS_QF_ACTIVE_POST, m_prio) QS_TIME_PRE_(); // timestamp QS_OBJ_PRE_(sender); // the sender object QS_SIG_PRE_(0U); // the signal of the event QS_OBJ_PRE_(this); // this active object - QS_2U8_PRE_(0U, 0U); // pool-Id & ref-ctr - QS_EQC_PRE_(0U); // number of free entries - QS_EQC_PRE_(0U); // min number of free entries - QS_END_NOCRIT_PRE_() - - QF_CRIT_X_(); + QS_2U8_PRE_(0U, 0U); // poolNum & refCtr + QS_EQC_PRE_(0U); // # free entries + QS_EQC_PRE_(0U); // min # free entries + QS_END_PRE_() - return true; // the event is always posted correctly -} - -//**************************************************************************** -void QTicker::postLIFO(QEvt const * const e) noexcept { - static_cast(e); // unused parameter - Q_ERROR_ID(900); // operation not allowed + QF_MEM_APP(); + QF_CRIT_EXIT(); } } // namespace QP - +//$enddef${QF::QTicker} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/libraries/qpcpp_arm-cm/src/qf_defer.cpp b/libraries/qpcpp_arm-cm/src/qf_defer.cpp index f572de0..9d97b2c 100644 --- a/libraries/qpcpp_arm-cm/src/qf_defer.cpp +++ b/libraries/qpcpp_arm-cm/src/qf_defer.cpp @@ -1,45 +1,49 @@ -/// @file -/// @brief QP::QActive::defer() and QP::QActive::recall() definitions. -/// @ingroup qf -/// @cond -///*************************************************************************** -/// Last updated for version 6.9.1 -/// Last updated on 2020-09-17 -/// -/// Q u a n t u m L e a P s -/// ------------------------ -/// Modern Embedded Software -/// -/// Copyright (C) 2005-2020 Quantum Leaps. All rights reserved. -/// -/// This program is open source software: you can redistribute it and/or -/// modify it under the terms of the GNU General Public License as published -/// by the Free Software Foundation, either version 3 of the License, or -/// (at your option) any later version. -/// -/// Alternatively, this program may be distributed and modified under the -/// terms of Quantum Leaps commercial licenses, which expressly supersede -/// the GNU General Public License and are specifically designed for -/// licensees interested in retaining the proprietary status of their code. -/// -/// This program is distributed in the hope that it will be useful, -/// but WITHOUT ANY WARRANTY; without even the implied warranty of -/// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -/// GNU General Public License for more details. -/// -/// You should have received a copy of the GNU General Public License -/// along with this program. If not, see . -/// -/// Contact information: -/// -/// -///*************************************************************************** -/// @endcond - +//$file${src::qf::qf_defer.cpp} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv +// +// Model: qpcpp.qm +// File: ${src::qf::qf_defer.cpp} +// +// This code has been generated by QM 6.1.1 . +// DO NOT EDIT THIS FILE MANUALLY. All your changes will be lost. +// +// This code is covered by the following QP license: +// License # : LicenseRef-QL-dual +// Issued to : Any user of the QP/C++ real-time embedded framework +// Framework(s) : qpcpp +// Support ends : 2024-12-31 +// License scope: +// +// Copyright (C) 2005 Quantum Leaps, LLC . +// +// Q u a n t u m L e a P s +// ------------------------ +// Modern Embedded Software +// +// SPDX-License-Identifier: GPL-3.0-or-later OR LicenseRef-QL-commercial +// +// This software is dual-licensed under the terms of the open source GNU +// General Public License version 3 (or any later version), or alternatively, +// under the terms of one of the closed source Quantum Leaps commercial +// licenses. +// +// The terms of the open source GNU General Public License version 3 +// can be found at: +// +// The terms of the closed source Quantum Leaps commercial licenses +// can be found at: +// +// Redistributions in source code must retain this top-level comment block. +// Plagiarizing this software to sidestep the license obligations is illegal. +// +// Contact information: +// +// +// +//$endhead${src::qf::qf_defer.cpp} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ #define QP_IMPL // this is QP implementation -#include "qf_port.hpp" // QF port -#include "qf_pkg.hpp" // QF package-scope interface -#include "qassert.h" // QP embedded systems-friendly assertions +#include "qp_port.hpp" // QP port +#include "qp_pkg.hpp" // QP package-scope interface +#include "qsafe.h" // QP Functional Safety (FuSa) Subsystem #ifdef Q_SPY // QS software tracing enabled? #include "qs_port.hpp" // QS port #include "qs_pkg.hpp" // QS facilities for pre-defined trace records @@ -47,107 +51,91 @@ #include "qs_dummy.hpp" // disable the QS software tracing #endif // Q_SPY -namespace QP { - +// unnamed namespace for local definitions with internal linkage +namespace { Q_DEFINE_THIS_MODULE("qf_defer") +} // unnamed namespace + +//$skip${QP_VERSION} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv +// Check for the minimum required QP version +#if (QP_VERSION < 730U) || (QP_VERSION != ((QP_RELEASE^4294967295U) % 0x3E8U)) +#error qpcpp version 7.3.0 or higher required +#endif +//$endskip${QP_VERSION} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +//$define${QF::QActive::defer} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv +namespace QP { -//**************************************************************************** -/// @description -/// This function is part of the event deferral support. An active object -/// uses this function to defer an event @p e to the QF-supported native -/// event queue @p eq. QF correctly accounts for another outstanding -/// reference to the event and will not recycle the event at the end of -/// the RTC step. Later, the active object might recall one event at a -/// time from the event queue. -/// -/// @param[in] eq pointer to a "raw" thread-safe queue to recall -/// an event from. -/// @param[in] e pointer to the event to be deferred -/// -/// @returns -/// 'true' (success) when the event could be deferred and 'false' -/// (failure) if event deferral failed due to overflowing the queue. -/// -/// An active object can use multiple event queues to defer events of -/// different kinds. -/// -/// @sa -/// QP::QActive::recall(), QP::QEQueue, QP::QActive::flushDeferred() -/// -bool QActive::defer(QEQueue * const eq, QEvt const * const e) const noexcept { +//${QF::QActive::defer} ...................................................... +bool QActive::defer( + QEQueue * const eq, + QEvt const * const e) const noexcept +{ bool const status = eq->post(e, 0U, m_prio); - QS_CRIT_STAT_ + QS_CRIT_STAT + QS_CRIT_ENTRY(); + QS_MEM_SYS(); QS_BEGIN_PRE_(QS_QF_ACTIVE_DEFER, m_prio) QS_TIME_PRE_(); // time stamp QS_OBJ_PRE_(this); // this active object QS_OBJ_PRE_(eq); // the deferred queue QS_SIG_PRE_(e->sig); // the signal of the event - QS_2U8_PRE_(e->poolId_, e->refCtr_); // pool Id & ref Count + QS_2U8_PRE_(e->getPoolNum_(), e->refCtr_); // poolNum & refCtr QS_END_PRE_() + QS_MEM_APP(); + QS_CRIT_EXIT(); return status; } -//**************************************************************************** -/// @description -/// 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 @p eq and posted (LIFO) to the event queue of -/// the active object. -/// -/// @param[in] eq pointer to a "raw" thread-safe queue to recall -/// an event from. -/// -/// @returns -/// 'true' if an event has been recalled and 'false' if not. -/// -/// @note -/// An active object can use multiple event queues to defer events of -/// different kinds. -/// -/// @sa -/// QP::QActive::recall(), QP::QEQueue, QP::QActive::postLIFO_() -/// +} // namespace QP +//$enddef${QF::QActive::defer} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +//$define${QF::QActive::recall} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv +namespace QP { + +//${QF::QActive::recall} ..................................................... bool QActive::recall(QEQueue * const eq) noexcept { QEvt const * const e = eq->get(m_prio); // get evt from deferred queue - bool recalled; + QF_CRIT_STAT - // event available? - if (e != nullptr) { - QActive::postLIFO(e); // post it to the _front_ of the AO's queue + bool recalled; + if (e != nullptr) { // event available? + postLIFO(e); // post it to the _front_ of the AO's queue - QF_CRIT_STAT_ - QF_CRIT_E_(); + QF_CRIT_ENTRY(); + QF_MEM_SYS(); - // is it a dynamic event? - if (e->poolId_ != 0U) { + if (e->getPoolNum_() != 0U) { // is it a mutable event? // after posting to the AO's queue the event must be referenced // at least twice: once in the deferred event queue (eq->get() // did NOT decrement the reference counter) and once in the // AO's event queue. - Q_ASSERT_CRIT_(210, e->refCtr_ >= 2U); + Q_ASSERT_INCRIT(210, e->refCtr_ >= 2U); // we need to decrement the reference counter once, to account // for removing the event from the deferred event queue. - QF_EVT_REF_CTR_DEC_(e); // decrement the reference counter + QEvt_refCtr_dec_(e); // decrement the reference counter } - QS_BEGIN_NOCRIT_PRE_(QS_QF_ACTIVE_RECALL, m_prio) + QS_BEGIN_PRE_(QS_QF_ACTIVE_RECALL, m_prio) QS_TIME_PRE_(); // time stamp QS_OBJ_PRE_(this); // this active object QS_OBJ_PRE_(eq); // the deferred queue QS_SIG_PRE_(e->sig); // the signal of the event - QS_2U8_PRE_(e->poolId_, e->refCtr_); // pool Id & ref Count - QS_END_NOCRIT_PRE_() + QS_2U8_PRE_(e->getPoolNum_(), e->refCtr_); // poolNum & refCtr + QS_END_PRE_() + + QF_MEM_APP(); + QF_CRIT_EXIT(); - QF_CRIT_X_(); recalled = true; } else { - QS_CRIT_STAT_ + QS_CRIT_ENTRY(); + QS_MEM_SYS(); QS_BEGIN_PRE_(QS_QF_ACTIVE_RECALL_ATTEMPT, m_prio) QS_TIME_PRE_(); // time stamp @@ -155,37 +143,41 @@ bool QActive::recall(QEQueue * const eq) noexcept { QS_OBJ_PRE_(eq); // the deferred queue QS_END_PRE_() + QS_MEM_APP(); + QS_CRIT_EXIT(); + recalled = false; } return recalled; } -//**************************************************************************** -/// @description -/// This function is part of the event deferral support. An active object -/// can use this function to flush a given QF event queue. The function makes -/// sure that the events are not leaked. -/// -/// @param[in] eq pointer to a "raw" thread-safe queue to flush. -/// -/// @returns -/// the number of events actually flushed from the queue. -/// -/// -/// @sa -/// QP::QActive::defer(), QP::QActive::recall(), QP::QEQueue -/// -std::uint_fast16_t QActive::flushDeferred(QEQueue * const eq) const noexcept { +} // namespace QP +//$enddef${QF::QActive::recall} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +//$define${QF::QActive::flushDeferred} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv +namespace QP { + +//${QF::QActive::flushDeferred} .............................................. +std::uint_fast16_t QActive::flushDeferred( + QEQueue * const eq, + std::uint_fast16_t const num) const noexcept +{ std::uint_fast16_t n = 0U; - for (QEvt const *e = eq->get(m_prio); - e != nullptr; - e = eq->get(m_prio)) - { - QF::gc(e); // garbage collect - ++n; // count the flushed event + while (n < num) { + QEvt const * const e = eq->get(m_prio); + if (e != nullptr) { + ++n; // count one more flushed event + #if (QF_MAX_EPOOL > 0U) + QF::gc(e); // garbage collect + #endif + } + else { + break; + } } + return n; } } // namespace QP - +//$enddef${QF::QActive::flushDeferred} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/libraries/qpcpp_arm-cm/src/qf_dyn.cpp b/libraries/qpcpp_arm-cm/src/qf_dyn.cpp index 66bb5e4..99a37dc 100644 --- a/libraries/qpcpp_arm-cm/src/qf_dyn.cpp +++ b/libraries/qpcpp_arm-cm/src/qf_dyn.cpp @@ -1,44 +1,49 @@ -/// @file -/// @brief QF/C++ dynamic event management -/// @cond -///*************************************************************************** -/// Last updated for version 6.9.3 -/// Last updated on 2021-04-09 -/// -/// Q u a n t u m L e a P s -/// ------------------------ -/// Modern Embedded Software -/// -/// Copyright (C) 2005-2021 Quantum Leaps. All rights reserved. -/// -/// This program is open source software: you can redistribute it and/or -/// modify it under the terms of the GNU General Public License as published -/// by the Free Software Foundation, either version 3 of the License, or -/// (at your option) any later version. -/// -/// Alternatively, this program may be distributed and modified under the -/// terms of Quantum Leaps commercial licenses, which expressly supersede -/// the GNU General Public License and are specifically designed for -/// licensees interested in retaining the proprietary status of their code. -/// -/// This program is distributed in the hope that it will be useful, -/// but WITHOUT ANY WARRANTY; without even the implied warranty of -/// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -/// GNU General Public License for more details. -/// -/// You should have received a copy of the GNU General Public License -/// along with this program. If not, see . -/// -/// Contact information: -/// -/// -///*************************************************************************** -/// @endcond - +//$file${src::qf::qf_dyn.cpp} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv +// +// Model: qpcpp.qm +// File: ${src::qf::qf_dyn.cpp} +// +// This code has been generated by QM 6.1.1 . +// DO NOT EDIT THIS FILE MANUALLY. All your changes will be lost. +// +// This code is covered by the following QP license: +// License # : LicenseRef-QL-dual +// Issued to : Any user of the QP/C++ real-time embedded framework +// Framework(s) : qpcpp +// Support ends : 2024-12-31 +// License scope: +// +// Copyright (C) 2005 Quantum Leaps, LLC . +// +// Q u a n t u m L e a P s +// ------------------------ +// Modern Embedded Software +// +// SPDX-License-Identifier: GPL-3.0-or-later OR LicenseRef-QL-commercial +// +// This software is dual-licensed under the terms of the open source GNU +// General Public License version 3 (or any later version), or alternatively, +// under the terms of one of the closed source Quantum Leaps commercial +// licenses. +// +// The terms of the open source GNU General Public License version 3 +// can be found at: +// +// The terms of the closed source Quantum Leaps commercial licenses +// can be found at: +// +// Redistributions in source code must retain this top-level comment block. +// Plagiarizing this software to sidestep the license obligations is illegal. +// +// Contact information: +// +// +// +//$endhead${src::qf::qf_dyn.cpp} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ #define QP_IMPL // this is QP implementation -#include "qf_port.hpp" // QF port -#include "qf_pkg.hpp" // QF package-scope interface -#include "qassert.h" // QP embedded systems-friendly assertions +#include "qp_port.hpp" // QP port +#include "qp_pkg.hpp" // QP package-scope interface +#include "qsafe.h" // QP Functional Safety (FuSa) Subsystem #ifdef Q_SPY // QS software tracing enabled? #include "qs_port.hpp" // QS port #include "qs_pkg.hpp" // QS facilities for pre-defined trace records @@ -46,305 +51,308 @@ #include "qs_dummy.hpp" // disable the QS software tracing #endif // Q_SPY -namespace QP { +#if (QF_MAX_EPOOL > 0U) // mutable events configured? +// unnamed namespace for local definitions with internal linkage +namespace { Q_DEFINE_THIS_MODULE("qf_dyn") +} // unnamed namespace + +//$skip${QP_VERSION} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv +// Check for the minimum required QP version +#if (QP_VERSION < 730U) || (QP_VERSION != ((QP_RELEASE^4294967295U) % 0x3E8U)) +#error qpcpp version 7.3.0 or higher required +#endif +//$endskip${QP_VERSION} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +//$define${QEP::QEvt::ctor} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv +namespace QP { + +//${QEP::QEvt::ctor} ......................................................... +#ifdef QEVT_DYN_CTOR +QEvt * QEvt::ctor(DynEvt const dummy) noexcept { + Q_UNUSED_PAR(dummy); + return this; +} + +#endif // def QEVT_DYN_CTOR -// Package-scope objects ***************************************************** -QF_EPOOL_TYPE_ QF_pool_[QF_MAX_EPOOL]; // allocate the event pools -std::uint_fast8_t QF_maxPool_; // number of initialized event pools - -//**************************************************************************** -/// @description -/// This function initializes one event pool at a time and must be called -/// exactly once for each event pool before the pool can be used. -/// -/// @param[in] poolSto pointer to the storage for the event pool -/// @param[in] poolSize size of the storage for the pool in bytes -/// @param[in] evtSize the block-size of the pool in bytes, which determines -/// the maximum size of events that can be allocated -/// from the pool -/// @note -/// You might initialize many event pools by making many consecutive calls -/// to the QF_poolInit() function. However, for the simplicity of the internal -/// implementation, you must initialize event pools in the ascending order of -/// the event size. -/// -/// @note The actual number of events available in the pool might be actually -/// less than (@p poolSize / @p evtSize) due to the internal alignment -/// of the blocks that the pool might perform. You can always check the -/// capacity of the pool by calling QF_getPoolMin(). -/// -/// @note The dynamic allocation of events is optional, meaning that you might -/// choose not to use dynamic events. In that case calling QP::QF::poolInit() -/// and using up memory for the memory blocks is unnecessary. -/// -/// @sa QF initialization example for QP::QF::init() -/// -void QF::poolInit(void * const poolSto, - std::uint_fast32_t const poolSize, - std::uint_fast16_t const evtSize) noexcept +} // namespace QP +//$enddef${QEP::QEvt::ctor} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +//$define${QF::QF-dyn} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv +namespace QP { +namespace QF { + +//${QF::QF-dyn::poolInit} .................................................... +void poolInit( + void * const poolSto, + std::uint_fast32_t const poolSize, + std::uint_fast16_t const evtSize) noexcept { - /// @pre cannot exceed the number of available memory pools - Q_REQUIRE_ID(200, QF_maxPool_ - < static_cast(Q_DIM(QF_pool_))); + std::uint_fast8_t const poolNum = priv_.maxPool_; + + // see precondition{qf_dyn,200} and precondition{qf_dyn,201} + QF_CRIT_STAT + QF_CRIT_ENTRY(); + QF_MEM_SYS(); - // last initialized event size - std::uint_fast16_t const lastEvtSize = ((QF_maxPool_ == 0U) - ? 0U - : QF_EPOOL_EVENT_SIZE_(QF_pool_[QF_maxPool_ - 1U])); + Q_REQUIRE_INCRIT(200, poolNum < QF_MAX_EPOOL); + if (poolNum > 0U) { + Q_REQUIRE_INCRIT(201, + QF_EPOOL_EVENT_SIZE_(priv_.ePool_[poolNum - 1U]) < evtSize); + } + priv_.maxPool_ = poolNum + 1U; // one more pool - /// @pre please initialize event pools in ascending order of evtSize - Q_REQUIRE_ID(201, (QF_maxPool_ == 0U) - || (lastEvtSize < evtSize)); + QF_MEM_APP(); + QF_CRIT_EXIT(); - QF_EPOOL_INIT_(QF_pool_[QF_maxPool_], poolSto, poolSize, evtSize); - ++QF_maxPool_; // one more pool + // perform the port-dependent initialization of the event-pool + QF_EPOOL_INIT_(priv_.ePool_[poolNum], poolSto, poolSize, evtSize); -#ifdef Q_SPY + #ifdef Q_SPY // generate the object-dictionary entry for the initialized pool - char_t obj_name[9] = "EvtPool?"; - obj_name[7] = static_cast( - static_cast('0') - + static_cast(QF_maxPool_)); - QS::obj_dict_pre_(&QF_pool_[QF_maxPool_ - 1U], &obj_name[0]); -#endif // Q_SPY + { + std::uint8_t obj_name[9] = "EvtPool?"; + obj_name[7] = static_cast( + static_cast('0') + + static_cast(poolNum + 1U)); + QS::obj_dict_pre_(&priv_.ePool_[poolNum], + reinterpret_cast(&obj_name[0])); + } + #endif // Q_SPY } -//**************************************************************************** -/// @description -/// Allocates an event dynamically from one of the QF event pools. -/// -/// @param[in] evtSize the size (in bytes) of the event to allocate -/// @param[in] margin the number of un-allocated events still available -/// in a given event pool after the allocation completes -/// The special value QP::QF_NO_MARGIN means that this -/// function will assert if allocation fails. -/// @param[in] sig the signal to be assigned to the allocated event -/// -/// @returns -/// pointer to the newly allocated event. This pointer can be NULL -/// only if margin!=0 and the event cannot be allocated with the specified -/// margin still available in the given pool. -/// -/// @note -/// The internal QF function QP::QF::newX_() raises an assertion when -/// the margin argument is QP::QF_NO_MARGIN and allocation of the event turns -/// out to be impossible due to event pool depletion, or incorrect (too big) -/// size of the requested event. -/// -/// @note -/// The application code should not call this function directly. -/// The only allowed use is thorough the macros Q_NEW() or Q_NEW_X(). -/// -QEvt *QF::newX_(std::uint_fast16_t const evtSize, - std::uint_fast16_t const margin, enum_t const sig) noexcept -{ - std::uint_fast8_t idx; +//${QF::QF-dyn::poolGetMaxBlockSize} ......................................... +std::uint_fast16_t poolGetMaxBlockSize() noexcept { + QF_CRIT_STAT + QF_CRIT_ENTRY(); + QF_MEM_SYS(); + std::uint_fast16_t const max_size = + QF_EPOOL_EVENT_SIZE_(priv_.ePool_[priv_.maxPool_ - 1U]); + QF_MEM_APP(); + QF_CRIT_EXIT(); + + return max_size; +} + +//${QF::QF-dyn::getPoolMin} .................................................. +std::uint_fast16_t getPoolMin(std::uint_fast8_t const poolNum) noexcept { + QF_CRIT_STAT + QF_CRIT_ENTRY(); + QF_MEM_SYS(); + + Q_REQUIRE_INCRIT(400, (poolNum <= QF_MAX_EPOOL) + && (0U < poolNum) && (poolNum <= priv_.maxPool_)); + + std::uint_fast16_t const min = static_cast( + priv_.ePool_[poolNum - 1U].getNMin()); - // find the pool id that fits the requested event size ... - for (idx = 0U; idx < QF_maxPool_; ++idx) { - if (evtSize <= QF_EPOOL_EVENT_SIZE_(QF_pool_[idx])) { + QF_MEM_APP(); + QF_CRIT_EXIT(); + + return min; +} + +//${QF::QF-dyn::newX_} ....................................................... +QEvt * newX_( + std::uint_fast16_t const evtSize, + std::uint_fast16_t const margin, + enum_t const sig) noexcept +{ + QF_CRIT_STAT + QF_CRIT_ENTRY(); + QF_MEM_SYS(); + + // find the pool id that fits the requested event size... + std::uint_fast8_t poolNum = 0U; // zero-based poolNum initially + for (; poolNum < priv_.maxPool_; ++poolNum) { + if (evtSize <= QF_EPOOL_EVENT_SIZE_(priv_.ePool_[poolNum])) { break; } } - // cannot run out of registered pools - Q_ASSERT_ID(310, idx < QF_maxPool_); - QS_CRIT_STAT_ - // get e -- platform-dependent - QEvt *e; + // precondition: + // - cannot run out of registered pools + Q_REQUIRE_INCRIT(300, poolNum < priv_.maxPool_); -#ifdef Q_SPY - QF_EPOOL_GET_(QF_pool_[idx], e, ((margin != QF_NO_MARGIN) ? margin : 0U), - static_cast(QS_EP_ID) + idx + 1U); -#else - QF_EPOOL_GET_(QF_pool_[idx], e, ((margin != QF_NO_MARGIN) ? margin : 0U), - 0U); -#endif + ++poolNum; // convert to 1-based poolNum - // was e allocated correctly? - if (e != nullptr) { + QF_MEM_APP(); + QF_CRIT_EXIT(); + + // get event e (port-dependent)... + QEvt *e; + #ifdef Q_SPY + QF_EPOOL_GET_(priv_.ePool_[poolNum - 1U], e, + ((margin != NO_MARGIN) ? margin : 0U), + static_cast(QS_EP_ID) + poolNum); + #else + QF_EPOOL_GET_(priv_.ePool_[poolNum - 1U], e, + ((margin != NO_MARGIN) ? margin : 0U), 0U); + #endif + + if (e != nullptr) { // was e allocated correctly? e->sig = static_cast(sig); // set the signal - e->poolId_ = static_cast(idx + 1U); // store pool ID e->refCtr_ = 0U; // initialize the reference counter to 0 + e->evtTag_ = static_cast(QEvt::MARKER | poolNum); + QS_CRIT_ENTRY(); + QS_MEM_SYS(); QS_BEGIN_PRE_(QS_QF_NEW, - static_cast(QS_EP_ID) - + static_cast(e->poolId_)) - QS_TIME_PRE_(); // timestamp - QS_EVS_PRE_(evtSize); // the size of the evt - QS_SIG_PRE_(sig); // the signal of the evt + static_cast(QS_EP_ID) + poolNum) + QS_TIME_PRE_(); // timestamp + QS_EVS_PRE_(evtSize); // the size of the event + QS_SIG_PRE_(sig); // the signal of the event QS_END_PRE_() + QS_MEM_APP(); + QS_CRIT_EXIT(); } - else { + else { // event was not allocated + + QF_CRIT_ENTRY(); // This assertion means that the event allocation failed, // and this failure cannot be tolerated. The most frequent // reason is an event leak in the application. - Q_ASSERT_ID(320, margin != QF_NO_MARGIN); + Q_ASSERT_INCRIT(320, margin != NO_MARGIN); + QS_MEM_SYS(); QS_BEGIN_PRE_(QS_QF_NEW_ATTEMPT, - static_cast(QS_EP_ID) + idx + 1U) - QS_TIME_PRE_(); // timestamp - QS_EVS_PRE_(evtSize); // the size of the evt - QS_SIG_PRE_(sig); // the signal of the evt + static_cast(QS_EP_ID) + poolNum) + QS_TIME_PRE_(); // timestamp + QS_EVS_PRE_(evtSize); // the size of the event + QS_SIG_PRE_(sig); // the signal of the event QS_END_PRE_() + QS_MEM_APP(); + + QF_CRIT_EXIT(); } - return e; // can't be NULL if we can't tolerate bad allocation + + // the returned event e is guaranteed to be valid (not NULL) + // if we can't tolerate failed allocation + return e; } -//**************************************************************************** -/// @description -/// This function implements a simple garbage collector for dynamic events. -/// Only dynamic events are candidates for recycling. (A dynamic event is one -/// that is allocated from an event pool, which is determined as non-zero -/// e->poolId_ attribute.) Next, the function decrements the reference counter -/// of the event (e->refCtr_), and recycles the event only if the counter -/// drops to zero (meaning that no more references are outstanding for this -/// event). The dynamic event is recycled by returning it to the pool from -/// which it was originally allocated. -/// -/// @param[in] e pointer to the event to recycle -/// -/// @note -/// QF invokes the garbage collector at all appropriate contexts, when -/// an event can become garbage (automatic garbage collection), so the -/// application code should have no need to call QP::QF::gc() directly. -/// The QP::QF::gc() function is exposed only for special cases when your -/// application sends dynamic events to the "raw" thread-safe queues -/// (see QP::QEQueue). Such queues are processed outside of QF and the -/// automatic garbage collection is **NOT** performed for these events. -/// In this case you need to call QP::QF::gc() explicitly. -/// -void QF::gc(QEvt const * const e) noexcept { - // is it a dynamic event? - if (e->poolId_ != 0U) { - QF_CRIT_STAT_ - QF_CRIT_E_(); - - // isn't this the last reference? - if (e->refCtr_ > 1U) { - - QS_BEGIN_NOCRIT_PRE_(QS_QF_GC_ATTEMPT, - static_cast(QS_EP_ID) - + static_cast(e->poolId_)) - QS_TIME_PRE_(); // timestamp - QS_SIG_PRE_(e->sig); // the signal of the event - QS_2U8_PRE_(e->poolId_, e->refCtr_); // pool Id & refCtr - QS_END_NOCRIT_PRE_() - - QF_EVT_REF_CTR_DEC_(e); // decrement the ref counter - - QF_CRIT_X_(); - } - // this is the last reference to this event, recycle it - else { - std::uint_fast8_t const idx = - static_cast(e->poolId_) - 1U; - - QS_BEGIN_NOCRIT_PRE_(QS_QF_GC, - static_cast(QS_EP_ID) - + static_cast(e->poolId_)) - QS_TIME_PRE_(); // timestamp - QS_SIG_PRE_(e->sig); // the signal of the event - QS_2U8_PRE_(e->poolId_, e->refCtr_); - QS_END_NOCRIT_PRE_() - - QF_CRIT_X_(); - - // pool ID must be in range - Q_ASSERT_ID(410, idx < QF_maxPool_); - -#ifdef Q_EVT_VIRTUAL - // explicitly exectute the destructor' - // NOTE: casting 'const' away is legitimate, - // because it's a pool event - QF_EVT_CONST_CAST_(e)->~QEvt(); // xtor, -#endif +//${QF::QF-dyn::gc} .......................................................... +void gc(QEvt const * const e) noexcept { + QF_CRIT_STAT + QF_CRIT_ENTRY(); + Q_REQUIRE_INCRIT(402, QEvt::verify_(e)); -#ifdef Q_SPY - // cast 'const' away, which is OK, because it's a pool event - QF_EPOOL_PUT_(QF_pool_[idx], QF_EVT_CONST_CAST_(e), - static_cast(QS_EP_ID) - + static_cast(e->poolId_)); -#else - QF_EPOOL_PUT_(QF_pool_[idx], QF_EVT_CONST_CAST_(e), 0U); -#endif + std::uint_fast8_t const poolNum = e->getPoolNum_(); + + if (poolNum != 0U) { // is it a pool event (mutable)? + QF_MEM_SYS(); + + if (e->refCtr_ > 1U) { // isn't this the last reference? + + QS_BEGIN_PRE_(QS_QF_GC_ATTEMPT, + static_cast(QS_EP_ID) + poolNum) + QS_TIME_PRE_(); // timestamp + QS_SIG_PRE_(e->sig); // the signal of the event + QS_2U8_PRE_(poolNum, e->refCtr_); // poolNum & refCtr + QS_END_PRE_() + + QEvt_refCtr_dec_(e); // decrement the ref counter + + QF_MEM_APP(); + QF_CRIT_EXIT(); + } + else { // this is the last reference to this event, recycle it + + QS_BEGIN_PRE_(QS_QF_GC, + static_cast(QS_EP_ID) + poolNum) + QS_TIME_PRE_(); // timestamp + QS_SIG_PRE_(e->sig); // the signal of the event + QS_2U8_PRE_(poolNum, e->refCtr_); // poolNum & refCtr + QS_END_PRE_() + + // pool number must be in range + Q_ASSERT_INCRIT(410, (poolNum <= priv_.maxPool_) + && (poolNum <= QF_MAX_EPOOL)); + QF_MEM_APP(); + QF_CRIT_EXIT(); + + // NOTE: casting 'const' away is legit because it's a pool event + #ifdef Q_SPY + QF_EPOOL_PUT_(priv_.ePool_[poolNum - 1U], + QF_CONST_CAST_(QEvt*, e), + static_cast(QS_EP_ID) + poolNum); + #else + QF_EPOOL_PUT_(priv_.ePool_[poolNum - 1U], + QF_CONST_CAST_(QEvt*, e), 0U); + #endif } } + else { + QF_CRIT_EXIT(); + } } -//**************************************************************************** -/// @description -/// Creates and returns a new reference to the current event e -/// -/// @param[in] e pointer to the current event -/// @param[in] evtRef the event reference -/// -/// @returns -/// the newly created reference to the event `e` -/// -/// @note -/// The application code should not call this function directly. -/// The only allowed use is thorough the macro Q_NEW_REF(). -/// -QEvt const *QF::newRef_(QEvt const * const e, - QEvt const * const evtRef) noexcept +//${QF::QF-dyn::newRef_} ..................................................... +QEvt const * newRef_( + QEvt const * const e, + QEvt const * const evtRef) noexcept { - //! @pre the event must be dynamic and the provided event reference - //! must not be already in use - Q_REQUIRE_ID(500, (e->poolId_ != 0U) - && (evtRef == nullptr)); + #ifdef Q_UNSAFE + Q_UNUSED_PAR(evtRef); + #endif + + QF_CRIT_STAT + QF_CRIT_ENTRY(); + + Q_REQUIRE_INCRIT(502, QEvt::verify_(e)); - QF_CRIT_STAT_ - QF_CRIT_E_(); + std::uint_fast8_t const poolNum = e->getPoolNum_(); - QF_EVT_REF_CTR_INC_(e); // increments the ref counter + Q_REQUIRE_INCRIT(500, (poolNum != 0U) + && (evtRef == nullptr)); - QS_BEGIN_NOCRIT_PRE_(QS_QF_NEW_REF, - static_cast(QS_EP_ID) - + static_cast(e->poolId_)) - QS_TIME_PRE_(); // timestamp - QS_SIG_PRE_(e->sig); // the signal of the event - QS_2U8_PRE_(e->poolId_, e->refCtr_); // pool Id & ref Count - QS_END_NOCRIT_PRE_() + QEvt_refCtr_inc_(e); // increments the ref counter - QF_CRIT_X_(); + QS_MEM_SYS(); + QS_BEGIN_PRE_(QS_QF_NEW_REF, + static_cast(QS_EP_ID) + poolNum) + QS_TIME_PRE_(); // timestamp + QS_SIG_PRE_(e->sig); // the signal of the event + QS_2U8_PRE_(poolNum, e->refCtr_); // poolNum & refCtr + QS_END_PRE_() + QS_MEM_APP(); + + QF_CRIT_EXIT(); return e; } -//**************************************************************************** -/// @description -/// Deletes an existing reference to the event e -/// -/// @param[in] evtRef the event reference -/// -/// @note -/// The application code should not call this function directly. -/// The only allowed use is thorough the macro Q_DELETE_REF(). -/// -void QF::deleteRef_(QEvt const * const evtRef) noexcept { - QS_CRIT_STAT_ +//${QF::QF-dyn::deleteRef_} .................................................. +void deleteRef_(QEvt const * const evtRef) noexcept { + QEvt const * const e = evtRef; + + QF_CRIT_STAT + QF_CRIT_ENTRY(); + Q_REQUIRE_INCRIT(602, QEvt::verify_(e)); + + #ifdef Q_SPY + std::uint_fast8_t const poolNum = e->getPoolNum_(); + #endif + QS_MEM_SYS(); QS_BEGIN_PRE_(QS_QF_DELETE_REF, - static_cast(QS_EP_ID) - + static_cast(evtRef->poolId_)) - QS_TIME_PRE_(); // timestamp - QS_SIG_PRE_(evtRef->sig); // the signal of the event - QS_2U8_PRE_(evtRef->poolId_, evtRef->refCtr_); // pool Id & ref Count + static_cast(QS_EP_ID) + poolNum) + QS_TIME_PRE_(); // timestamp + QS_SIG_PRE_(e->sig); // the signal of the event + QS_2U8_PRE_(poolNum, e->refCtr_); // poolNum & refCtr QS_END_PRE_() + QS_MEM_APP(); - gc(evtRef); // recycle the referenced event -} + QF_CRIT_EXIT(); -//**************************************************************************** -/// @description -/// Obtain the block size of any registered event pools -/// -std::uint_fast16_t QF::poolGetMaxBlockSize(void) noexcept { - return QF_EPOOL_EVENT_SIZE_(QF_pool_[QF_maxPool_ - 1U]); + #if (QF_MAX_EPOOL > 0U) + gc(evtRef); // recycle the referenced event + #endif } +} // namespace QF } // namespace QP +//$enddef${QF::QF-dyn} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +#endif // (QF_MAX_EPOOL > 0U) mutable events configured diff --git a/libraries/qpcpp_arm-cm/src/qf_mem.cpp b/libraries/qpcpp_arm-cm/src/qf_mem.cpp index ef1689e..8714b86 100644 --- a/libraries/qpcpp_arm-cm/src/qf_mem.cpp +++ b/libraries/qpcpp_arm-cm/src/qf_mem.cpp @@ -1,44 +1,49 @@ -/// @file -/// @brief QF/C++ memory management services -/// @cond -///*************************************************************************** -/// Last updated for version 6.9.1 -/// Last updated on 2020-09-17 -/// -/// Q u a n t u m L e a P s -/// ------------------------ -/// Modern Embedded Software -/// -/// Copyright (C) 2005-2020 Quantum Leaps. All rights reserved. -/// -/// This program is open source software: you can redistribute it and/or -/// modify it under the terms of the GNU General Public License as published -/// by the Free Software Foundation, either version 3 of the License, or -/// (at your option) any later version. -/// -/// Alternatively, this program may be distributed and modified under the -/// terms of Quantum Leaps commercial licenses, which expressly supersede -/// the GNU General Public License and are specifically designed for -/// licensees interested in retaining the proprietary status of their code. -/// -/// This program is distributed in the hope that it will be useful, -/// but WITHOUT ANY WARRANTY; without even the implied warranty of -/// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -/// GNU General Public License for more details. -/// -/// You should have received a copy of the GNU General Public License -/// along with this program. If not, see . -/// -/// Contact information: -/// -/// -///*************************************************************************** -/// @endcond - +//$file${src::qf::qf_mem.cpp} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv +// +// Model: qpcpp.qm +// File: ${src::qf::qf_mem.cpp} +// +// This code has been generated by QM 6.1.1 . +// DO NOT EDIT THIS FILE MANUALLY. All your changes will be lost. +// +// This code is covered by the following QP license: +// License # : LicenseRef-QL-dual +// Issued to : Any user of the QP/C++ real-time embedded framework +// Framework(s) : qpcpp +// Support ends : 2024-12-31 +// License scope: +// +// Copyright (C) 2005 Quantum Leaps, LLC . +// +// Q u a n t u m L e a P s +// ------------------------ +// Modern Embedded Software +// +// SPDX-License-Identifier: GPL-3.0-or-later OR LicenseRef-QL-commercial +// +// This software is dual-licensed under the terms of the open source GNU +// General Public License version 3 (or any later version), or alternatively, +// under the terms of one of the closed source Quantum Leaps commercial +// licenses. +// +// The terms of the open source GNU General Public License version 3 +// can be found at: +// +// The terms of the closed source Quantum Leaps commercial licenses +// can be found at: +// +// Redistributions in source code must retain this top-level comment block. +// Plagiarizing this software to sidestep the license obligations is illegal. +// +// Contact information: +// +// +// +//$endhead${src::qf::qf_mem.cpp} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ #define QP_IMPL // this is QP implementation -#include "qf_port.hpp" // QF port -#include "qf_pkg.hpp" // QF package-scope interface -#include "qassert.h" // QP embedded systems-friendly assertions +#include "qp_port.hpp" // QP port +#include "qp_pkg.hpp" // QP package-scope interface +#include "qsafe.h" // QP Functional Safety (FuSa) Subsystem #ifdef Q_SPY // QS software tracing enabled? #include "qs_port.hpp" // QS port #include "qs_pkg.hpp" // QS facilities for pre-defined trace records @@ -46,215 +51,129 @@ #include "qs_dummy.hpp" // disable the QS software tracing #endif // Q_SPY +// unnamed namespace for local definitions with internal linkage +namespace { +Q_DEFINE_THIS_MODULE("qf_mem") +} // unnamed namespace + +//$skip${QP_VERSION} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv +// Check for the minimum required QP version +#if (QP_VERSION < 730U) || (QP_VERSION != ((QP_RELEASE^4294967295U) % 0x3E8U)) +#error qpcpp version 7.3.0 or higher required +#endif +//$endskip${QP_VERSION} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +//$define${QF::QMPool} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv namespace QP { -Q_DEFINE_THIS_MODULE("qf_mem") +//${QF::QMPool} .............................................................. -//**************************************************************************** -/// @description -/// Default constructor of a fixed block-size memory pool. -/// -/// @note -/// The memory pool is __not__ ready to use directly after instantiation. -/// To become ready, the QP::QMPool::init() must be called to give the pool -/// memory, size of this memory, and the block size to manage. -/// -QMPool::QMPool(void) - : m_start(nullptr), - m_end(nullptr), - m_free_head(nullptr), - m_blockSize(0U), - m_nTot(0U), - m_nFree(0U), - m_nMin(0U) -{} - -//**************************************************************************** -/// @description -/// Initialize a fixed block-size memory pool by providing it with the pool -/// memory to manage, size of this memory, and the block size. -/// -/// @param[in] poolSto pointer to the memory buffer for pool storage -/// @param[in] poolSize size of the storage buffer in bytes -/// @param[in] blockSize fixed-size of the memory blocks in bytes -/// -/// @attention -/// The caller of QP::QMPool::init() must make sure that the @p poolSto -/// pointer is properly __aligned__. In particular, it must be possible to -/// efficiently store a pointer at the location pointed to by @p poolSto. -/// Internally, the QP::QMPool::init() function rounds up the block size -/// @p 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 (@p poolSize / @p blockSize). You can check the -/// capacity of the pool by calling the QP::QF::getPoolMin() function. -/// -/// @note -/// This function is __not__ protected by a critical section, because -/// it is intended to be called only during the initialization of the system, -/// when interrupts are not allowed yet. -/// -/// @note -/// Many QF ports use memory pools to implement the event pools. -/// -void QMPool::init(void * const poolSto, std::uint_fast32_t poolSize, - std::uint_fast16_t blockSize) noexcept +//${QF::QMPool::init} ........................................................ +void QMPool::init( + void * const poolSto, + std::uint_fast32_t const poolSize, + std::uint_fast16_t const blockSize) noexcept { - /// @pre The memory block must be valid and - /// the poolSize must fit at least one free block and - /// the blockSize must not be too close to the top of the dynamic range - Q_REQUIRE_ID(100, (poolSto != nullptr) - && (poolSize >= sizeof(QFreeBlock)) + QF_CRIT_STAT + QF_CRIT_ENTRY(); + QF_MEM_SYS(); + + Q_REQUIRE_INCRIT(100, (poolSto != nullptr) + && (poolSize >= static_cast(sizeof(QFreeBlock))) && (static_cast(blockSize + sizeof(QFreeBlock)) > blockSize)); - m_free_head = poolSto; + m_free_head = static_cast(poolSto); - // round up the blockSize to fit an integer number of pointers... - //start with one + // find # free blocks in a memory block, NO DIVISION m_blockSize = static_cast(sizeof(QFreeBlock)); - - //# free blocks in a memory block std::uint_fast16_t nblocks = 1U; while (m_blockSize < static_cast(blockSize)) { m_blockSize += static_cast(sizeof(QFreeBlock)); ++nblocks; } - // use rounded-up value - blockSize = static_cast(m_blockSize); - - // the whole pool buffer must fit at least one rounded-up block - Q_ASSERT_ID(110, poolSize >= blockSize); - // chain all blocks together in a free-list... - - // don't count the last block - poolSize -= static_cast(blockSize); - m_nTot = 1U; // one (the last) block in the pool + // the pool buffer must fit at least one rounded-up block + Q_ASSERT_INCRIT(110, poolSize >= m_blockSize); // start at the head of the free list - QFreeBlock *fb = static_cast(m_free_head); + QFreeBlock *fb = m_free_head; + m_nTot = 1U; // the last block already in the list // chain all blocks together in a free-list... - while (poolSize >= blockSize) { - fb->m_next = &QF_PTR_AT_(fb, nblocks); // setup the next link - fb = fb->m_next; // advance to next block - // reduce the available pool size - poolSize -= static_cast(blockSize); - ++m_nTot; // increment the number of blocks so far + for (std::uint_fast32_t size = poolSize - m_blockSize; + size >= static_cast(m_blockSize); + size -= static_cast(m_blockSize)) + { + fb->m_next = &fb[nblocks]; // point next link to next block + #ifndef Q_UNSAFE + fb->m_next_dis = ~Q_UINTPTR_CAST_(fb->m_next); + #endif + fb = fb->m_next; // advance to the next block + ++m_nTot; // one more free block in the pool } + fb->m_next = nullptr; // the last link points to NULL + #ifndef Q_UNSAFE + fb->m_next_dis = ~Q_UINTPTR_CAST_(fb->m_next); + #endif + fb->m_next = nullptr; // the last link points to NULL - m_nFree = m_nTot; // all blocks are free - m_nMin = m_nTot; // the minimum number of free blocks - m_start = poolSto; // the original start this pool buffer - m_end = fb; // the last block in this pool -} + m_nFree = m_nTot; // all blocks are free + m_nMin = m_nTot; // the minimum # free blocks + m_start = static_cast(poolSto); // original start + m_end = fb; // the last block in this pool -//**************************************************************************** -/// @description -/// Recycle a memory block to the fixed block-size memory pool. -/// -/// @param[in] b pointer to the memory block that is being recycled -/// @param[in] qs_id QS-id of this state machine (for QS local filter) -/// -/// @attention -/// The recycled block must be allocated from the __same__ memory pool -/// to which it is returned. -/// -/// @note -/// This function can be called from any task level or ISR level. -/// -/// @sa -/// QP::QMPool::get() -/// -void QMPool::put(void * const b, std::uint_fast8_t const qs_id) noexcept { - - /// @pre # free blocks cannot exceed the total # blocks and - /// the block pointer must be in range to come from this pool. - /// - Q_REQUIRE_ID(200, (m_nFree < m_nTot) - && QF_PTR_RANGE_(b, m_start, m_end)); - QF_CRIT_STAT_ - - QF_CRIT_E_(); - static_cast(b)->m_next = - static_cast(m_free_head); // link into the free list - m_free_head = b; // set as new head of the free list - ++m_nFree; // one more free block in this pool - - QS_BEGIN_NOCRIT_PRE_(QS_QF_MPOOL_PUT, qs_id) - QS_TIME_PRE_(); // timestamp - QS_OBJ_PRE_(this); // this memory pool - QS_MPC_PRE_(m_nFree); // the number of free blocks in the pool - QS_END_NOCRIT_PRE_() - - QF_CRIT_X_(); - static_cast(qs_id); // unused parameter, if Q_SPY not defined + QF_MEM_APP(); + QF_CRIT_EXIT(); } -//**************************************************************************** -/// @description -/// The function allocates a memory block from the pool and returns a pointer -/// to the block back to the caller. -/// -/// @param[in] margin the minimum number of unused blocks still available -/// in the pool after the allocation. -/// @param[in] qs_id QS-id of this state machine (for QS local filter) -/// -/// @note -/// This function can be called from any task level or ISR level. -/// -/// @note -/// The memory pool must be initialized before any events can -/// be requested from it. Also, the QP::QMPool::get() function uses internally -/// a QF critical section, so you should be careful not to call it from within -/// a critical section when nesting of critical section is not supported. -/// -/// @attention -/// An allocated block must be later returned back to the same pool -/// from which it has been allocated. -/// -/// @sa -/// QP::QMPool::put() -/// -void *QMPool::get(std::uint_fast16_t const margin, - std::uint_fast8_t const qs_id) noexcept +//${QF::QMPool::get} ......................................................... +void * QMPool::get( + std::uint_fast16_t const margin, + std::uint_fast8_t const qsId) noexcept { - QFreeBlock *fb; - QF_CRIT_STAT_ + #ifndef Q_SPY + Q_UNUSED_PAR(qsId); + #endif + + QF_CRIT_STAT + QF_CRIT_ENTRY(); + QF_MEM_SYS(); - QF_CRIT_E_(); - // have the than margin? + // have more free blocks than the requested margin? + QFreeBlock *fb; if (m_nFree > static_cast(margin)) { - fb = static_cast(m_free_head); // get a free block + fb = m_free_head; // get a free block - // the pool has some free blocks, so a free block must be available - Q_ASSERT_CRIT_(310, fb != nullptr); + // a free block must be valid + Q_ASSERT_INCRIT(300, fb != nullptr); - // put volatile to a temporary to avoid UB - void * const fb_next = fb->m_next; + QFreeBlock * const fb_next = fb->m_next; - // is the pool becoming empty? - --m_nFree; // one free block less - if (m_nFree == 0U) { + // the free block must have integrity (duplicate inverse storage) + Q_ASSERT_INCRIT(302, Q_UINTPTR_CAST_(fb_next) + == static_cast(~fb->m_next_dis)); + + m_nFree = (m_nFree - 1U); // one free block less + if (m_nFree == 0U) { // is the pool becoming empty? // pool is becoming empty, so the next free block must be NULL - Q_ASSERT_CRIT_(320, fb_next == nullptr); + Q_ASSERT_INCRIT(320, fb_next == nullptr); - m_nMin = 0U;// remember that pool got empty + m_nMin = 0U; // remember that the pool got empty } else { - // pool is not empty, so the next free block must be in range - // - // NOTE: the next free block pointer can fall out of range + // invariant: + // The pool is not empty, so the next free-block pointer, + // so the next free block must be in range. + + // NOTE: The next free block pointer can fall out of range // when the client code writes past the memory block, thus // corrupting the next block. - Q_ASSERT_CRIT_(330, QF_PTR_RANGE_(fb_next, m_start, m_end)); + Q_ASSERT_INCRIT(330, + QF_PTR_RANGE_(fb_next, m_start, m_end)); - // is the number of free blocks the new minimum so far? + // is the # free blocks the new minimum so far? if (m_nMin > m_nFree) { m_nMin = m_nFree; // remember the minimum so far } @@ -262,58 +181,73 @@ void *QMPool::get(std::uint_fast16_t const margin, m_free_head = fb_next; // set the head to the next free block - QS_BEGIN_NOCRIT_PRE_(QS_QF_MPOOL_GET, qs_id) - QS_TIME_PRE_(); // timestamp - QS_OBJ_PRE_(this); // this memory pool - QS_MPC_PRE_(m_nFree); // the number of free blocks in the pool - QS_MPC_PRE_(m_nMin); // the mninimum # free blocks in the pool - QS_END_NOCRIT_PRE_() + QS_BEGIN_PRE_(QS_QF_MPOOL_GET, qsId) + QS_TIME_PRE_(); // timestamp + QS_OBJ_PRE_(this); // this memory pool + QS_MPC_PRE_(m_nFree); // # of free blocks in the pool + QS_MPC_PRE_(m_nMin); // min # free blocks ever in the pool + QS_END_PRE_() } - // don't have enough free blocks at this point - else { + else { // don't have enough free blocks at this point fb = nullptr; - QS_BEGIN_NOCRIT_PRE_(QS_QF_MPOOL_GET_ATTEMPT, qs_id) - QS_TIME_PRE_(); // timestamp - QS_OBJ_PRE_(m_start); // the memory managed by this pool - QS_MPC_PRE_(m_nFree); // the # free blocks in the pool - QS_MPC_PRE_(margin); // the requested margin - QS_END_NOCRIT_PRE_() + QS_BEGIN_PRE_(QS_QF_MPOOL_GET_ATTEMPT, qsId) + QS_TIME_PRE_(); // timestamp + QS_OBJ_PRE_(this); // this memory pool + QS_MPC_PRE_(m_nFree); // # of free blocks in the pool + QS_MPC_PRE_(margin); // the requested margin + QS_END_PRE_() } - QF_CRIT_X_(); - static_cast(qs_id); // unused parameter, if Q_SPY not defined + QF_MEM_APP(); + QF_CRIT_EXIT(); + + return fb; // return the block or nullptr to the caller +} + +//${QF::QMPool::put} ......................................................... +void QMPool::put( + void * const block, + std::uint_fast8_t const qsId) noexcept +{ + #ifndef Q_SPY + Q_UNUSED_PAR(qsId); + #endif + + QFreeBlock * const fb = static_cast(block); - return fb; // return the block or NULL pointer to the caller + QF_CRIT_STAT + QF_CRIT_ENTRY(); + QF_MEM_SYS(); + + Q_REQUIRE_INCRIT(200, (m_nFree < m_nTot) + && QF_PTR_RANGE_(fb, m_start, m_end)); + + fb->m_next = m_free_head; // link into list + #ifndef Q_UNSAFE + fb->m_next_dis = static_cast( + ~Q_UINTPTR_CAST_(fb->m_next)); + #endif + + // set as new head of the free list + m_free_head = static_cast(block); + + m_nFree = m_nFree + 1U; // one more free block in this pool + + QS_BEGIN_PRE_(QS_QF_MPOOL_PUT, qsId) + QS_TIME_PRE_(); // timestamp + QS_OBJ_PRE_(this); // this memory pool + QS_MPC_PRE_(m_nFree); // the # free blocks in the pool + QS_END_PRE_() + + QF_MEM_APP(); + QF_CRIT_EXIT(); } -//**************************************************************************** -/// @description -/// This function obtains the minimum number of free blocks in the given -/// event pool since this pool has been initialized by a call to -/// QP::QF::poolInit(). -/// -/// @param[in] poolId event pool ID in the range 1..QF_maxPool_, where -/// QF_maxPool_ is the number of event pools initialized -/// with the function QP::QF::poolInit(). -/// -/// @returns -/// the minimum number of unused blocks in the given event pool. -/// -std::uint_fast16_t QF::getPoolMin(std::uint_fast8_t const poolId) noexcept { - - /// @pre the poolId must be in range - Q_REQUIRE_ID(400, (QF_maxPool_ <= Q_DIM(QF_pool_)) - && (1U <= poolId) - && (poolId <= QF_maxPool_)); - QF_CRIT_STAT_ - QF_CRIT_E_(); - std::uint_fast16_t const min = static_cast( - QF_pool_[poolId - 1U].m_nMin); - QF_CRIT_X_(); - - return min; +//${QF::QMPool::getBlockSize} ................................................ +QMPoolSize QMPool::getBlockSize() const noexcept { + return m_blockSize; } } // namespace QP - +//$enddef${QF::QMPool} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/libraries/qpcpp_arm-cm/src/qf_pkg.hpp b/libraries/qpcpp_arm-cm/src/qf_pkg.hpp deleted file mode 100644 index 0404c1f..0000000 --- a/libraries/qpcpp_arm-cm/src/qf_pkg.hpp +++ /dev/null @@ -1,173 +0,0 @@ -/// @file -/// @ingroup qf -/// @brief Internal (package scope) QF/C++ interface. -/// @cond -///*************************************************************************** -/// Last updated for version 6.9.1 -/// Last updated on 2020-09-17 -/// -/// Q u a n t u m L e a P s -/// ------------------------ -/// Modern Embedded Software -/// -/// Copyright (C) 2005-2020 Quantum Leaps. All rights reserved. -/// -/// This program is open source software: you can redistribute it and/or -/// modify it under the terms of the GNU General Public License as published -/// by the Free Software Foundation, either version 3 of the License, or -/// (at your option) any later version. -/// -/// Alternatively, this program may be distributed and modified under the -/// terms of Quantum Leaps commercial licenses, which expressly supersede -/// the GNU General Public License and are specifically designed for -/// licensees interested in retaining the proprietary status of their code. -/// -/// This program is distributed in the hope that it will be useful, -/// but WITHOUT ANY WARRANTY; without even the implied warranty of -/// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -/// GNU General Public License for more details. -/// -/// You should have received a copy of the GNU General Public License -/// along with this program. If not, see . -/// -/// Contact information: -/// -/// -///*************************************************************************** -/// @endcond - -#ifndef QF_PKG_HPP -#define QF_PKG_HPP - -//! helper macro to cast const away from an event pointer @p e_ -#define QF_EVT_CONST_CAST_(e_) const_cast(e_) - -// QF-specific critical section... -#ifndef QF_CRIT_STAT_TYPE - //! This is an internal macro for defining the critical section - //! status type. - /// @description - /// The purpose of this macro is to enable writing the same code for the - /// case when critical section status type is defined and when it is not. - /// If the macro #QF_CRIT_STAT_TYPE is defined, this internal macro - /// provides the definition of the critical section status variable. - /// Otherwise this macro is empty. - /// @sa #QF_CRIT_STAT_TYPE - #define QF_CRIT_STAT_ - - //! This is an internal macro for entering a critical section. - /// @description - /// The purpose of this macro is to enable writing the same code for the - /// case when critical section status type is defined and when it is not. - /// If the macro #QF_CRIT_STAT_TYPE is defined, this internal macro - /// invokes QF_CRIT_ENTRY() passing the key variable as the parameter. - /// Otherwise QF_CRIT_ENTRY() is invoked with a dummy parameter. - /// @sa QF_CRIT_ENTRY() - #define QF_CRIT_E_() QF_CRIT_ENTRY(dummy) - - //! This is an internal macro for exiting a critical section. - /// @description - /// The purpose of this macro is to enable writing the same code for the - /// case when critical section status type is defined and when it is not. - /// If the macro #QF_CRIT_STAT_TYPE is defined, this internal macro - /// invokes QF_CRIT_EXIT() passing the key variable as the parameter. - /// Otherwise QF_CRIT_EXIT() is invoked with a dummy parameter. - /// @sa QF_CRIT_EXIT() - /// - #define QF_CRIT_X_() QF_CRIT_EXIT(dummy) - -#elif (!defined QF_CRIT_STAT_) - #define QF_CRIT_STAT_ QF_CRIT_STAT_TYPE critStat_; - #define QF_CRIT_E_() QF_CRIT_ENTRY(critStat_) - #define QF_CRIT_X_() QF_CRIT_EXIT(critStat_) -#endif // QF_CRIT_STAT_TYPE - -// Assertions inside the crticial section ------------------------------------ -#ifdef Q_NASSERT // Q_NASSERT defined--assertion checking disabled - - #define Q_ASSERT_CRIT_(id_, test_) ((void)0) - #define Q_REQUIRE_CRIT_(id_, test_) ((void)0) - #define Q_ERROR_CRIT_(id_) ((void)0) - -#else // Q_NASSERT not defined--assertion checking enabled - - #define Q_ASSERT_CRIT_(id_, test_) do {\ - if ((test_)) {} else { \ - QF_CRIT_X_(); \ - Q_onAssert(&Q_this_module_[0], static_cast(id_)); \ - } \ - } while (false) - - #define Q_REQUIRE_CRIT_(id_, test_) Q_ASSERT_CRIT_((id_), (test_)) - - #define Q_ERROR_CRIT_(id_) do { \ - QF_CRIT_X_(); \ - Q_onAssert(&Q_this_module_[0], static_cast(id_)); \ - } while (false) - -#endif // Q_NASSERT - - -namespace QP { - -// package-scope objects ----------------------------------------------------- -extern QF_EPOOL_TYPE_ QF_pool_[QF_MAX_EPOOL]; //!< allocate event pools -extern std::uint_fast8_t QF_maxPool_; //!< # of initialized event pools -extern QSubscrList *QF_subscrList_; //!< the subscriber list array -extern enum_t QF_maxPubSignal_; //!< the maximum published signal - -//............................................................................ -//! Structure representing a free block in the Native QF Memory Pool -/// @sa QP::QMPool -struct QFreeBlock { - QFreeBlock * volatile m_next; //!< link to the next free block -}; - -//............................................................................ -// The following flags and bitmasks are for the fields of the @c refCtr_ -// attribute of the QP::QTimeEvt class (inherited from QEvt). This attribute -// is NOT used for reference counting in time events, because the @c poolId_ -// attribute is zero ("static events"). -// -constexpr std::uint8_t TE_IS_LINKED = 1U << 7U; // flag -constexpr std::uint8_t TE_WAS_DISARMED = 1U << 6U; // flag -constexpr std::uint8_t TE_TICK_RATE = 0x0FU; // bitmask - -//**************************************************************************** -// internal helper inline functions - -//! return the Pool-ID of an event @p e -inline std::uint8_t QF_EVT_POOL_ID_ (QEvt const * const e) noexcept { - return e->poolId_; -} - -//! return the Reference Conter of an event @p e -inline std::uint8_t QF_EVT_REF_CTR_ (QEvt const * const e) noexcept { - return e->refCtr_; -} - -//! increment the refCtr_ of an event @p e -inline void QF_EVT_REF_CTR_INC_(QEvt const * const e) noexcept { - ++(QF_EVT_CONST_CAST_(e))->refCtr_; -} - -//! decrement the refCtr_ of an event @p e -inline void QF_EVT_REF_CTR_DEC_(QEvt const * const e) noexcept { - --(QF_EVT_CONST_CAST_(e))->refCtr_; -} - -} // namespace QP - -//! macro to test that a pointer @p x_ is in range between @p min_ and @p max_ -/// @description -/// 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_))) - -//! access element at index @p i_ from the base pointer @p base_ -#define QF_PTR_AT_(base_, i_) (base_[i_]) - -#endif // QF_PKG_HPP diff --git a/libraries/qpcpp_arm-cm/src/qf_port.hpp b/libraries/qpcpp_arm-cm/src/qf_port.hpp deleted file mode 100644 index 2c6f671..0000000 --- a/libraries/qpcpp_arm-cm/src/qf_port.hpp +++ /dev/null @@ -1,155 +0,0 @@ -/// @file -/// @brief QF/C++ port to ARM Cortex-M, cooperative QV kernel, GNU-ARM toolset -/// @cond -///*************************************************************************** -/// Last updated for version 6.9.2 -/// Last updated on 2021-01-28 -/// -/// Q u a n t u m L e a P s -/// ------------------------ -/// Modern Embedded Software -/// -/// Copyright (C) 2005-2021 Quantum Leaps. All rights reserved. -/// -/// This program is open source software: you can redistribute it and/or -/// modify it under the terms of the GNU General Public License as published -/// by the Free Software Foundation, either version 3 of the License, or -/// (at your option) any later version. -/// -/// Alternatively, this program may be distributed and modified under the -/// terms of Quantum Leaps commercial licenses, which expressly supersede -/// the GNU General Public License and are specifically designed for -/// licensees interested in retaining the proprietary status of their code. -/// -/// This program is distributed in the hope that it will be useful, -/// but WITHOUT ANY WARRANTY; without even the implied warranty of -/// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -/// GNU General Public License for more details. -/// -/// You should have received a copy of the GNU General Public License -/// along with this program. If not, see . -/// -/// Contact information: -/// -/// -///*************************************************************************** -/// @endcond - -#ifndef QF_PORT_HPP -#define QF_PORT_HPP - -// The maximum number of system clock tick rates -#define QF_MAX_TICK_RATE 2U - -// QF interrupt disable/enable and log2()... -#if (__ARM_ARCH == 6) // Cortex-M0/M0+/M1(v6-M, v6S-M)? - - // The maximum number of active objects in the application, see NOTE1 - #define QF_MAX_ACTIVE 8U - - // Cortex-M0/M0+/M1(v6-M, v6S-M) interrupt disabling policy, see NOTE2 - #define QF_INT_DISABLE() __asm volatile ("cpsid i") - #define QF_INT_ENABLE() __asm volatile ("cpsie i") - - // QF critical section entry/exit (unconditional interrupt disabling) - //#define QF_CRIT_STAT_TYPE not defined - #define QF_CRIT_ENTRY(dummy) QF_INT_DISABLE() - #define QF_CRIT_EXIT(dummy) QF_INT_ENABLE() - - // CMSIS threshold for "QF-aware" interrupts, see NOTE2 and NOTE4 - #define QF_AWARE_ISR_CMSIS_PRI 0 - - // hand-optimized LOG2 in assembly for Cortex-M0/M0+/M1(v6-M, v6S-M) - #define QF_LOG2(n_) QF_qlog2((n_)) - -#else // Cortex-M3/M4/M7 - - // The maximum number of active objects in the application, see NOTE1 - #define QF_MAX_ACTIVE 16U - - // Cortex-M3/M4/M7 alternative interrupt disabling with PRIMASK - #define QF_PRIMASK_DISABLE() __asm volatile ("cpsid i") - #define QF_PRIMASK_ENABLE() __asm volatile ("cpsie i") - - // Cortex-M3/M4/M7 interrupt disabling policy, see NOTE3 and NOTE4 - #define QF_INT_DISABLE() __asm volatile (\ - "cpsid i\n" "msr BASEPRI,%0\n" "cpsie i" :: "r" (QF_BASEPRI) : ) - #define QF_INT_ENABLE() __asm volatile (\ - "msr BASEPRI,%0" :: "r" (0) : ) - - // QF critical section entry/exit (unconditional interrupt disabling) - //#define QF_CRIT_STAT_TYPE not defined - #define QF_CRIT_ENTRY(dummy) QF_INT_DISABLE() - #define QF_CRIT_EXIT(dummy) QF_INT_ENABLE() - - // BASEPRI threshold for "QF-aware" interrupts, see NOTE3 - #define QF_BASEPRI 0x3F - - // CMSIS threshold for "QF-aware" interrupts, see NOTE5 - #define QF_AWARE_ISR_CMSIS_PRI (QF_BASEPRI >> (8 - __NVIC_PRIO_BITS)) - - // Cortex-M3/M4/M7 provide the CLZ instruction for fast LOG2 - #define QF_LOG2(n_) (static_cast( \ - 32U - __builtin_clz(static_cast(n_)))) - -#endif - -#define QF_CRIT_EXIT_NOP() __asm volatile ("isb") - -#include "qep_port.hpp" // QEP port - -#if (__ARM_ARCH == 6) // Cortex-M0/M0+/M1(v6-M, v6S-M)? - // hand-optimized quick LOG2 in assembly - extern "C" uint_fast8_t QF_qlog2(uint32_t x); -#endif // Cortex-M0/M0+/M1(v6-M, v6S-M) - -#include "qv_port.hpp" // QV port -#include "qf.hpp" // QF platform-independent public interface -#include "Arduino.h" // Main include file for the Arduino SDK - -//**************************************************************************** -// NOTE1: -// The maximum number of active objects QF_MAX_ACTIVE can be increased -// up to 64, if necessary. Here it is set to a lower level to save some RAM. -// -// NOTE2: -// On Cortex-M0/M0+/M1 (architecture v6-M, v6S-M), the interrupt disabling -// policy uses the PRIMASK register to disable interrupts globally. The -// QF_AWARE_ISR_CMSIS_PRI level is zero, meaning that all interrupts are -// "QF-aware". -// -// NOTE3: -// On Cortex-M3/M4/M7, the interrupt disable/enable policy uses the BASEPRI -// register (which is not implemented in Cortex-M0/M0+/M1) to disable -// interrupts only with priority lower than the threshold specified by the -// QF_BASEPRI macro. The interrupts with priorities above QF_BASEPRI (i.e., -// with numerical priority values lower than QF_BASEPRI) are NOT disabled in -// this method. These free-running interrupts have very low ("zero") latency, -// but they are not allowed to call any QF services, because QF is unaware -// of them ("QF-unaware" interrutps). Consequently, only interrupts with -// numerical values of priorities eqal to or higher than QF_BASEPRI -// ("QF-aware" interrupts ), can call QF services. -// -// NOTE4: -// The QF_AWARE_ISR_CMSIS_PRI macro is useful as an offset for enumerating -// the "QF-aware" interrupt priorities in the applications, whereas the -// numerical values of the "QF-aware" interrupts must be greater or equal to -// QF_AWARE_ISR_CMSIS_PRI. The values based on QF_AWARE_ISR_CMSIS_PRI can be -// passed directly to the CMSIS function NVIC_SetPriority(), which shifts -// them by (8 - __NVIC_PRIO_BITS) into the correct bit position, while -// __NVIC_PRIO_BITS is the CMSIS macro defining the number of implemented -// priority bits in the NVIC. Please note that the macro QF_AWARE_ISR_CMSIS_PRI -// is intended only for applications and is not used inside the QF port, which -// remains generic and not dependent on the number of implemented priority bits -// implemented in the NVIC. -// -// NOTE5: -// The selective disabling of "QF-aware" interrupts with the BASEPRI register -// has a problem on ARM Cortex-M7 core r0p1 (see SDEN-1068427, errata -// 837070). The workaround recommended by ARM is to surround MSR BASEPRI with -// the CPSID i/CPSIE i pair, which is implemented in the QF_INT_DISABLE() -// macro. This workaround works also for Cortex-M3/M4 cores. -// - -#endif // QF_PORT_HPP - diff --git a/libraries/qpcpp_arm-cm/src/qf_ps.cpp b/libraries/qpcpp_arm-cm/src/qf_ps.cpp index b1ad29f..94c883f 100644 --- a/libraries/qpcpp_arm-cm/src/qf_ps.cpp +++ b/libraries/qpcpp_arm-cm/src/qf_ps.cpp @@ -1,46 +1,49 @@ -/// @file -/// @brief QF/C++ Publish-Subscribe services -/// definitions. -/// @ingroup qf -/// @cond -///*************************************************************************** -/// Last updated for version 6.9.3 -/// Last updated on 2021-02-26 -/// -/// Q u a n t u m L e a P s -/// ------------------------ -/// Modern Embedded Software -/// -/// Copyright (C) 2005-2021 Quantum Leaps. All rights reserved. -/// -/// This program is open source software: you can redistribute it and/or -/// modify it under the terms of the GNU General Public License as published -/// by the Free Software Foundation, either version 3 of the License, or -/// (at your option) any later version. -/// -/// Alternatively, this program may be distributed and modified under the -/// terms of Quantum Leaps commercial licenses, which expressly supersede -/// the GNU General Public License and are specifically designed for -/// licensees interested in retaining the proprietary status of their code. -/// -/// This program is distributed in the hope that it will be useful, -/// but WITHOUT ANY WARRANTY; without even the implied warranty of -/// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -/// GNU General Public License for more details. -/// -/// You should have received a copy of the GNU General Public License -/// along with this program. If not, see . -/// -/// Contact information: -/// -/// -///*************************************************************************** -/// @endcond - +//$file${src::qf::qf_ps.cpp} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv +// +// Model: qpcpp.qm +// File: ${src::qf::qf_ps.cpp} +// +// This code has been generated by QM 6.1.1 . +// DO NOT EDIT THIS FILE MANUALLY. All your changes will be lost. +// +// This code is covered by the following QP license: +// License # : LicenseRef-QL-dual +// Issued to : Any user of the QP/C++ real-time embedded framework +// Framework(s) : qpcpp +// Support ends : 2024-12-31 +// License scope: +// +// Copyright (C) 2005 Quantum Leaps, LLC . +// +// Q u a n t u m L e a P s +// ------------------------ +// Modern Embedded Software +// +// SPDX-License-Identifier: GPL-3.0-or-later OR LicenseRef-QL-commercial +// +// This software is dual-licensed under the terms of the open source GNU +// General Public License version 3 (or any later version), or alternatively, +// under the terms of one of the closed source Quantum Leaps commercial +// licenses. +// +// The terms of the open source GNU General Public License version 3 +// can be found at: +// +// The terms of the closed source Quantum Leaps commercial licenses +// can be found at: +// +// Redistributions in source code must retain this top-level comment block. +// Plagiarizing this software to sidestep the license obligations is illegal. +// +// Contact information: +// +// +// +//$endhead${src::qf::qf_ps.cpp} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ #define QP_IMPL // this is QP implementation -#include "qf_port.hpp" // QF port -#include "qf_pkg.hpp" // QF package-scope interface -#include "qassert.h" // QP embedded systems-friendly assertions +#include "qp_port.hpp" // QP port +#include "qp_pkg.hpp" // QP package-scope interface +#include "qsafe.h" // QP Functional Safety (FuSa) Subsystem #ifdef Q_SPY // QS software tracing enabled? #include "qs_port.hpp" // QS port #include "qs_pkg.hpp" // QS facilities for pre-defined trace records @@ -48,273 +51,276 @@ #include "qs_dummy.hpp" // disable the QS software tracing #endif // Q_SPY +// unnamed namespace for local definitions with internal linkage +namespace { +Q_DEFINE_THIS_MODULE("qf_ps") +} // unnamed namespace + +//$skip${QP_VERSION} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv +// Check for the minimum required QP version +#if (QP_VERSION < 730U) || (QP_VERSION != ((QP_RELEASE^4294967295U) % 0x3E8U)) +#error qpcpp version 7.3.0 or higher required +#endif +//$endskip${QP_VERSION} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +//$define${QF::QActive::subscrList_} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv namespace QP { +QSubscrList * QActive::subscrList_; -Q_DEFINE_THIS_MODULE("qf_ps") +} // namespace QP +//$enddef${QF::QActive::subscrList_} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -// Package-scope objects ***************************************************** -QSubscrList *QF_subscrList_; -enum_t QF_maxPubSignal_; - -//**************************************************************************** -/// @description -/// This function initializes the publish-subscribe facilities of QF and must -/// be called exactly once before any subscriptions/publications occur in -/// the application. -/// -/// @param[in] subscrSto pointer to the array of subscriber lists -/// @param[in] maxSignal the dimension of the subscriber array and at -/// the same time the maximum signal that can be -/// published or subscribed. -/// -/// The array of subscriber-lists is indexed by signals and provides a mapping -/// between the signals and subscriber-lists. The subscriber-lists are -/// bitmasks of type QP::QSubscrList, each bit in the bit mask corresponding -/// to the unique priority of an active object. The size of the -/// QP::QSubscrList bitmask depends on the value of the #QF_MAX_ACTIVE macro. -/// -/// @note -/// The publish-subscribe facilities are optional, meaning that you might -/// choose not to use publish-subscribe. In that case calling QF::psInit() -/// and using up memory for the subscriber-lists is unnecessary. -/// -/// @sa -/// QP::QSubscrList -/// -/// @usage -/// The following example shows the typical initialization sequence of QF: -/// @include qf_main.cpp -/// -void QF::psInit(QSubscrList * const subscrSto, - enum_t const maxSignal) noexcept -{ - QF_subscrList_ = subscrSto; - QF_maxPubSignal_ = maxSignal; +//$define${QF::QActive::maxPubSignal_} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv +namespace QP { +enum_t QActive::maxPubSignal_; + +} // namespace QP +//$enddef${QF::QActive::maxPubSignal_} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +//$define${QF::QActive::psInit} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv +namespace QP { - // zero the subscriber list, so that the framework can start correctly - // even if the startup code fails to clear the uninitialized data - // (as is required by the C++ Standard) - bzero(subscrSto, static_cast(maxSignal) * sizeof(QSubscrList)); +//${QF::QActive::psInit} ..................................................... +void QActive::psInit( + QSubscrList * const subscrSto, + enum_t const maxSignal) noexcept +{ + subscrList_ = subscrSto; + maxPubSignal_ = maxSignal; + + // initialize the subscriber list + for (enum_t sig = 0; sig < maxSignal; ++sig) { + subscrSto[sig].m_set.setEmpty(); + #ifndef Q_UNSAFE + subscrSto[sig].m_set.update_(&subscrSto[sig].m_set_dis); + #endif + } } -//**************************************************************************** -/// @description -/// This function posts (using the FIFO policy) the event @a e to **all** -/// active objects that have subscribed to the signal @a e->sig, which is -/// called _multicasting_. The multicasting performed in this function is -/// very efficient based on reference-counting inside the published event -/// ("zero-copy" event multicasting). This function is designed to be -/// callable from any part of the system, including ISRs, device drivers, -/// and active objects. -/// -/// @note -/// To avoid any unexpected re-ordering of events posted into AO queues, -/// the event multicasting is performed with scheduler __locked__. However, -/// the scheduler is locked only up to the priority level of the highest- -/// priority subscriber, so any AOs of even higher priority, which did not -/// subscribe to this event are _not_ affected. -/// -#ifndef Q_SPY -void QF::publish_(QEvt const * const e) noexcept { -#else -void QF::publish_(QEvt const * const e, - void const * const sender, - std::uint_fast8_t const qs_id) noexcept +} // namespace QP +//$enddef${QF::QActive::psInit} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +//$define${QF::QActive::publish_} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv +namespace QP { + +//${QF::QActive::publish_} ................................................... +void QActive::publish_( + QEvt const * const e, + void const * const sender, + std::uint_fast8_t const qsId) noexcept { -#endif - /// @pre the published signal must be within the configured range - Q_REQUIRE_ID(100, static_cast(e->sig) < QF_maxPubSignal_); - - QF_CRIT_STAT_ - QF_CRIT_E_(); - - QS_BEGIN_NOCRIT_PRE_(QS_QF_PUBLISH, qs_id) - QS_TIME_PRE_(); // the timestamp - QS_OBJ_PRE_(sender); // the sender object - QS_SIG_PRE_(e->sig); // the signal of the event - QS_2U8_PRE_(e->poolId_, e->refCtr_); // pool Id & refCtr of the evt - QS_END_NOCRIT_PRE_() - - // is it a dynamic event? - if (e->poolId_ != 0U) { - // NOTE: The reference counter of a dynamic event is incremented to + #ifndef Q_SPY + Q_UNUSED_PAR(sender); + Q_UNUSED_PAR(qsId); + #endif + + QSignal const sig = e->sig; + + QF_CRIT_STAT + QF_CRIT_ENTRY(); + QF_MEM_SYS(); + + Q_REQUIRE_INCRIT(200, sig < static_cast(maxPubSignal_)); + Q_REQUIRE_INCRIT(202, + subscrList_[sig].m_set.verify_(&subscrList_[sig].m_set_dis)); + + QS_BEGIN_PRE_(QS_QF_PUBLISH, qsId) + QS_TIME_PRE_(); // the timestamp + QS_OBJ_PRE_(sender); // the sender object + QS_SIG_PRE_(e->sig); // the signal of the event + QS_2U8_PRE_(e->getPoolNum_(), e->refCtr_); // poolNum & refCtr + QS_END_PRE_() + + // is it a mutable event? + if (e->getPoolNum_() != 0U) { + // NOTE: The reference counter of a mutable event is incremented to // prevent premature recycling of the event while the multicasting // is still in progress. At the end of the function, the garbage // collector step (QF::gc()) decrements the reference counter and // recycles the event if the counter drops to zero. This covers the // case when the event was published without any subscribers. - // - QF_EVT_REF_CTR_INC_(e); + QEvt_refCtr_inc_(e); } - // make a local, modifiable copy of the subscriber list - QPSet subscrList = QF_PTR_AT_(QF_subscrList_, e->sig); - QF_CRIT_X_(); + // make a local, modifiable copy of the subscriber set + QPSet subscrSet = subscrList_[sig].m_set; - if (subscrList.notEmpty()) { // any subscribers? - // the highest-prio subscriber - std::uint_fast8_t p = subscrList.findMax(); - QF_SCHED_STAT_ + QF_MEM_APP(); + QF_CRIT_EXIT(); + + if (subscrSet.notEmpty()) { // any subscribers? + // highest-prio subscriber + std::uint_fast8_t p = subscrSet.findMax(); + + QF_CRIT_ENTRY(); + QF_MEM_SYS(); + + QActive *a = registry_[p]; + // the AO must be registered with the framework + Q_ASSERT_INCRIT(210, a != nullptr); - QF_SCHED_LOCK_(p); // lock the scheduler up to prio 'p' - do { // loop over all subscribers */ - // the prio of the AO must be registered with the framework - Q_ASSERT_ID(210, active_[p] != nullptr); + QF_MEM_APP(); + QF_CRIT_EXIT(); + + QF_SCHED_STAT_ + QF_SCHED_LOCK_(p); // lock the scheduler up to AO's prio + std::uint_fast8_t limit = QF_MAX_ACTIVE + 1U; + do { // loop over all subscribers + --limit; // POST() asserts internally if the queue overflows - static_cast(active_[p]->POST(e, sender)); + a->POST(e, sender); + + subscrSet.remove(p); // remove the handled subscriber + if (subscrSet.notEmpty()) { // still more subscribers? + p = subscrSet.findMax(); // highest-prio subscriber - subscrList.rmove(p); // remove the handled subscriber - if (subscrList.notEmpty()) { // still more subscribers? - p = subscrList.findMax(); // the highest-prio subscriber + QF_CRIT_ENTRY(); + QF_MEM_SYS(); + + a = registry_[p]; + // the AO must be registered with the framework + Q_ASSERT_INCRIT(220, a != nullptr); + + QF_MEM_APP(); + QF_CRIT_EXIT(); } else { p = 0U; // no more subscribers } - } while (p != 0U); + } while ((p != 0U) && (limit > 0U)); + + QF_CRIT_ENTRY(); + Q_ENSURE_INCRIT(290, p == 0U); + QF_CRIT_EXIT(); + QF_SCHED_UNLOCK_(); // unlock the scheduler } // The following garbage collection step decrements the reference counter // and recycles the event if the counter drops to zero. This covers both // cases when the event was published with or without any subscribers. - // - gc(e); + #if (QF_MAX_EPOOL > 0U) + QF::gc(e); + #endif } +} // namespace QP +//$enddef${QF::QActive::publish_} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +//$define${QF::QActive::subscribe} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv +namespace QP { -//**************************************************************************** -/// @description -/// 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 @p sig to the -/// event queue of the active object. -/// -/// @param[in] sig event signal to subscribe -/// -/// The following example shows how the Table active object subscribes -/// to three signals in the initial transition: -/// @include qf_subscribe.cpp -/// -/// @sa -/// QP::QF::publish_(), QP::QActive::unsubscribe(), and -/// QP::QActive::unsubscribeAll() -/// +//${QF::QActive::subscribe} .................................................. void QActive::subscribe(enum_t const sig) const noexcept { std::uint_fast8_t const p = static_cast(m_prio); - Q_REQUIRE_ID(300, (Q_USER_SIG <= sig) - && (sig < QF_maxPubSignal_) - && (0U < p) && (p <= QF_MAX_ACTIVE) - && (QF::active_[p] == this)); - QF_CRIT_STAT_ - QF_CRIT_E_(); + QF_CRIT_STAT + QF_CRIT_ENTRY(); + QF_MEM_SYS(); + + Q_REQUIRE_INCRIT(300, (Q_USER_SIG <= sig) + && (sig < maxPubSignal_) + && (0U < p) && (p <= QF_MAX_ACTIVE) + && (registry_[p] == this)); + Q_REQUIRE_INCRIT(302, + subscrList_[sig].m_set.verify_(&subscrList_[sig].m_set_dis)); - QS_BEGIN_NOCRIT_PRE_(QS_QF_ACTIVE_SUBSCRIBE, m_prio) + QS_BEGIN_PRE_(QS_QF_ACTIVE_SUBSCRIBE, m_prio) QS_TIME_PRE_(); // timestamp QS_SIG_PRE_(sig); // the signal of this event QS_OBJ_PRE_(this); // this active object - QS_END_NOCRIT_PRE_() + QS_END_PRE_() + + // insert the prio. into the subscriber set + subscrList_[sig].m_set.insert(p); + #ifndef Q_UNSAFE + subscrList_[sig].m_set.update_(&subscrList_[sig].m_set_dis); + #endif - QF_PTR_AT_(QF_subscrList_, sig).insert(p); // insert into subscriber-list - QF_CRIT_X_(); + QF_MEM_APP(); + QF_CRIT_EXIT(); } -//**************************************************************************** -/// @description -/// 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 @p sig to the -/// event queue of the active object. -/// -/// @param[in] sig event signal to unsubscribe -/// -/// @note -/// Due to the latency of event queues, an active object should NOT -/// assume that a given signal @p sig will never be dispatched to the -/// state machine of the active object after un-subscribing from that signal. -/// The event might be already in the queue, or just about to be posted -/// and the un-subscribe operation will not flush such events. -/// -/// @note -/// Un-subscribing from a signal that has never been subscribed in the -/// first place is considered an error and QF will raise an assertion. -/// -/// @sa -/// QP::QF::publish_(), QP::QActive::subscribe(), and -/// QP::QActive::unsubscribeAll() -/// +} // namespace QP +//$enddef${QF::QActive::subscribe} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +//$define${QF::QActive::unsubscribe} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv +namespace QP { + +//${QF::QActive::unsubscribe} ................................................ void QActive::unsubscribe(enum_t const sig) const noexcept { std::uint_fast8_t const p = static_cast(m_prio); - //! @pre the singal and the prioriy must be in ragne, the AO must also - // be registered with the framework - Q_REQUIRE_ID(400, (Q_USER_SIG <= sig) - && (sig < QF_maxPubSignal_) - && (0U < p) && (p <= QF_MAX_ACTIVE) - && (QF::active_[p] == this)); + QF_CRIT_STAT + QF_CRIT_ENTRY(); + QF_MEM_SYS(); - QF_CRIT_STAT_ - QF_CRIT_E_(); + Q_REQUIRE_INCRIT(400, (Q_USER_SIG <= sig) + && (sig < maxPubSignal_) + && (0U < p) && (p <= QF_MAX_ACTIVE) + && (registry_[p] == this)); + Q_REQUIRE_INCRIT(402, + subscrList_[sig].m_set.verify_(&subscrList_[sig].m_set_dis)); - QS_BEGIN_NOCRIT_PRE_(QS_QF_ACTIVE_UNSUBSCRIBE, m_prio) - QS_TIME_PRE_(); // timestamp - QS_SIG_PRE_(sig); // the signal of this event - QS_OBJ_PRE_(this); // this active object - QS_END_NOCRIT_PRE_() + QS_BEGIN_PRE_(QS_QF_ACTIVE_UNSUBSCRIBE, m_prio) + QS_TIME_PRE_(); // timestamp + QS_SIG_PRE_(sig); // the signal of this event + QS_OBJ_PRE_(this); // this active object + QS_END_PRE_() - QF_PTR_AT_(QF_subscrList_,sig).rmove(p); // remove from subscriber-list + // remove the prio. from the subscriber set + subscrList_[sig].m_set.remove(p); + #ifndef Q_UNSAFE + subscrList_[sig].m_set.update_(&subscrList_[sig].m_set_dis); + #endif - QF_CRIT_X_(); + QF_MEM_APP(); + QF_CRIT_EXIT(); } -//**************************************************************************** -/// @description -/// 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 -/// QP::QF::publish_(), QP::QActive::subscribe(), and -/// QP::QActive::unsubscribe() -/// -void QActive::unsubscribeAll(void) const noexcept { - std::uint_fast8_t const p = static_cast(m_prio); +} // namespace QP +//$enddef${QF::QActive::unsubscribe} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - Q_REQUIRE_ID(500, (0U < p) && (p <= QF_MAX_ACTIVE) - && (QF::active_[p] == this)); +//$define${QF::QActive::unsubscribeAll} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv +namespace QP { - for (enum_t sig = Q_USER_SIG; sig < QF_maxPubSignal_; ++sig) { - QF_CRIT_STAT_ - QF_CRIT_E_(); - if (QF_PTR_AT_(QF_subscrList_, sig).hasElement(p)) { - QF_PTR_AT_(QF_subscrList_, sig).rmove(p); +//${QF::QActive::unsubscribeAll} ............................................. +void QActive::unsubscribeAll() const noexcept { + QF_CRIT_STAT + QF_CRIT_ENTRY(); + QF_MEM_SYS(); - QS_BEGIN_NOCRIT_PRE_(QS_QF_ACTIVE_UNSUBSCRIBE, m_prio) - QS_TIME_PRE_(); // timestamp - QS_SIG_PRE_(sig); // the signal of this event - QS_OBJ_PRE_(this); // this active object - QS_END_NOCRIT_PRE_() + std::uint_fast8_t const p = static_cast(m_prio); + Q_REQUIRE_INCRIT(500, (0U < p) && (p <= QF_MAX_ACTIVE) + && (registry_[p] == this)); + enum_t const maxPubSig = maxPubSignal_; + + QF_MEM_APP(); + QF_CRIT_EXIT(); + + for (enum_t sig = Q_USER_SIG; sig < maxPubSig; ++sig) { + QF_CRIT_ENTRY(); + QF_MEM_SYS(); + + if (subscrList_[sig].m_set.hasElement(p)) { + subscrList_[sig].m_set.remove(p); + #ifndef Q_UNSAFE + subscrList_[sig].m_set.update_(&subscrList_[sig].m_set_dis); + #endif + QS_BEGIN_PRE_(QS_QF_ACTIVE_UNSUBSCRIBE, m_prio) + QS_TIME_PRE_(); // timestamp + QS_SIG_PRE_(sig); // the signal of this event + QS_OBJ_PRE_(this); // this active object + QS_END_PRE_() } - QF_CRIT_X_(); + QF_MEM_APP(); + QF_CRIT_EXIT(); - // prevent merging critical sections - QF_CRIT_EXIT_NOP(); + QF_CRIT_EXIT_NOP(); // prevent merging critical sections } } } // namespace QP - +//$enddef${QF::QActive::unsubscribeAll} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/libraries/qpcpp_arm-cm/src/qf_qact.cpp b/libraries/qpcpp_arm-cm/src/qf_qact.cpp index f6a8516..b9a103d 100644 --- a/libraries/qpcpp_arm-cm/src/qf_qact.cpp +++ b/libraries/qpcpp_arm-cm/src/qf_qact.cpp @@ -1,60 +1,149 @@ -/// @file -/// @brief QP::QActive::QActive() definition -/// @cond -///*************************************************************************** -/// Last updated for version 6.8.0 -/// Last updated on 2020-01-13 -/// -/// Q u a n t u m L e a P s -/// ------------------------ -/// Modern Embedded Software -/// -/// Copyright (C) 2005-2020 Quantum Leaps. All rights reserved. -/// -/// This program is open source software: you can redistribute it and/or -/// modify it under the terms of the GNU General Public License as published -/// by the Free Software Foundation, either version 3 of the License, or -/// (at your option) any later version. -/// -/// Alternatively, this program may be distributed and modified under the -/// terms of Quantum Leaps commercial licenses, which expressly supersede -/// the GNU General Public License and are specifically designed for -/// licensees interested in retaining the proprietary status of their code. -/// -/// This program is distributed in the hope that it will be useful, -/// but WITHOUT ANY WARRANTY; without even the implied warranty of -/// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -/// GNU General Public License for more details. -/// -/// You should have received a copy of the GNU General Public License -/// along with this program. If not, see . -/// -/// Contact information: -/// -/// -///*************************************************************************** -/// @endcond - -#define QP_IMPL // this is QP implementation -#include "qf_port.hpp" // QF port +//$file${src::qf::qf_qact.cpp} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv +// +// Model: qpcpp.qm +// File: ${src::qf::qf_qact.cpp} +// +// This code has been generated by QM 6.1.1 . +// DO NOT EDIT THIS FILE MANUALLY. All your changes will be lost. +// +// This code is covered by the following QP license: +// License # : LicenseRef-QL-dual +// Issued to : Any user of the QP/C++ real-time embedded framework +// Framework(s) : qpcpp +// Support ends : 2024-12-31 +// License scope: +// +// Copyright (C) 2005 Quantum Leaps, LLC . +// +// Q u a n t u m L e a P s +// ------------------------ +// Modern Embedded Software +// +// SPDX-License-Identifier: GPL-3.0-or-later OR LicenseRef-QL-commercial +// +// This software is dual-licensed under the terms of the open source GNU +// General Public License version 3 (or any later version), or alternatively, +// under the terms of one of the closed source Quantum Leaps commercial +// licenses. +// +// The terms of the open source GNU General Public License version 3 +// can be found at: +// +// The terms of the closed source Quantum Leaps commercial licenses +// can be found at: +// +// Redistributions in source code must retain this top-level comment block. +// Plagiarizing this software to sidestep the license obligations is illegal. +// +// Contact information: +// +// +// +//$endhead${src::qf::qf_qact.cpp} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +#define QP_IMPL // this is QP implementation +#include "qp_port.hpp" // QP port +#include "qp_pkg.hpp" // QP package-scope interface +#include "qsafe.h" // QP Functional Safety (FuSa) Subsystem +#ifdef Q_SPY // QS software tracing enabled? + #include "qs_port.hpp" // QS port + #include "qs_pkg.hpp" // QS facilities for pre-defined trace records +#else + #include "qs_dummy.hpp" // disable the QS software tracing +#endif // Q_SPY +// unnamed namespace for local definitions with internal linkage +namespace { +Q_DEFINE_THIS_MODULE("qf_qact") +} // unnamed namespace + +//$skip${QP_VERSION} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv +// Check for the minimum required QP version +#if (QP_VERSION < 730U) || (QP_VERSION != ((QP_RELEASE^4294967295U) % 0x3E8U)) +#error qpcpp version 7.3.0 or higher required +#endif +//$endskip${QP_VERSION} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +//$define${QF::QActive::QActive} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv namespace QP { -//**************************************************************************** -QActive::QActive(QStateHandler const initial) noexcept - : QHsm(initial), - m_prio(0U) -{ - m_state.fun = Q_STATE_CAST(&QHsm::top); +} // namespace QP +//$enddef${QF::QActive::QActive} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -#ifdef QF_OS_OBJECT_TYPE - QF::bzero(&m_osObject, sizeof(m_osObject)); -#endif +//$define${QF::QActive::register_} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv +namespace QP { -#ifdef QF_THREAD_TYPE - QF::bzero(&m_thread, sizeof(m_thread)); -#endif +//${QF::QActive::register_} .................................................. +void QActive::register_() noexcept { + QF_CRIT_STAT + QF_CRIT_ENTRY(); + QF_MEM_SYS(); + + if (m_pthre == 0U) { // preemption-threshold not defined? + m_pthre = m_prio; // apply the default + } + + #ifndef Q_UNSAFE + + Q_REQUIRE_INCRIT(100, (0U < m_prio) && (m_prio <= QF_MAX_ACTIVE) + && (registry_[m_prio] == nullptr) + && (m_prio <= m_pthre)); + + std::uint8_t prev_thre = m_pthre; + std::uint8_t next_thre = m_pthre; + + std::uint_fast8_t p; + for (p = static_cast(m_prio) - 1U; p > 0U; --p) { + if (registry_[p] != nullptr) { + prev_thre = registry_[p]->m_pthre; + break; + } + } + for (p = static_cast(m_prio) + 1U; + p <= QF_MAX_ACTIVE; ++p) + { + if (registry_[p] != nullptr) { + next_thre = registry_[p]->m_pthre; + break; + } + } + + Q_ASSERT_INCRIT(190, (prev_thre <= m_pthre) + && (m_pthre <= next_thre)); + + m_prio_dis = static_cast(~m_prio); + m_pthre_dis = static_cast(~m_pthre); + + #endif // Q_UNSAFE + + // register the AO at the QF-prio. + registry_[m_prio] = this; + + QF_MEM_APP(); + QF_CRIT_EXIT(); } } // namespace QP +//$enddef${QF::QActive::register_} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +//$define${QF::QActive::unregister_} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv +namespace QP { +//${QF::QActive::unregister_} ................................................ +void QActive::unregister_() noexcept { + std::uint_fast8_t const p = static_cast(m_prio); + + QF_CRIT_STAT + QF_CRIT_ENTRY(); + QF_MEM_SYS(); + + Q_REQUIRE_INCRIT(200, (0U < p) && (p <= QF_MAX_ACTIVE) + && (registry_[p] == this)); + registry_[p] = nullptr; // free-up the priority level + m_state.fun = nullptr; // invalidate the state + + QF_MEM_APP(); + QF_CRIT_EXIT(); +} + +} // namespace QP +//$enddef${QF::QActive::unregister_} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/libraries/qpcpp_arm-cm/src/qf_qeq.cpp b/libraries/qpcpp_arm-cm/src/qf_qeq.cpp index 80d9c5b..95b2345 100644 --- a/libraries/qpcpp_arm-cm/src/qf_qeq.cpp +++ b/libraries/qpcpp_arm-cm/src/qf_qeq.cpp @@ -1,44 +1,49 @@ -/// @file -/// @brief QP::QEQueue implementation -/// @cond -///*************************************************************************** -/// Last updated for version 6.9.1 -/// Last updated on 2020-09-17 -/// -/// Q u a n t u m L e a P s -/// ------------------------ -/// Modern Embedded Software -/// -/// Copyright (C) 2005-2020 Quantum Leaps. All rights reserved. -/// -/// This program is open source software: you can redistribute it and/or -/// modify it under the terms of the GNU General Public License as published -/// by the Free Software Foundation, either version 3 of the License, or -/// (at your option) any later version. -/// -/// Alternatively, this program may be distributed and modified under the -/// terms of Quantum Leaps commercial licenses, which expressly supersede -/// the GNU General Public License and are specifically designed for -/// licensees interested in retaining the proprietary status of their code. -/// -/// This program is distributed in the hope that it will be useful, -/// but WITHOUT ANY WARRANTY; without even the implied warranty of -/// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -/// GNU General Public License for more details. -/// -/// You should have received a copy of the GNU General Public License -/// along with this program. If not, see . -/// -/// Contact information: -/// -/// -///*************************************************************************** -/// @endcond - +//$file${src::qf::qf_qeq.cpp} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv +// +// Model: qpcpp.qm +// File: ${src::qf::qf_qeq.cpp} +// +// This code has been generated by QM 6.1.1 . +// DO NOT EDIT THIS FILE MANUALLY. All your changes will be lost. +// +// This code is covered by the following QP license: +// License # : LicenseRef-QL-dual +// Issued to : Any user of the QP/C++ real-time embedded framework +// Framework(s) : qpcpp +// Support ends : 2024-12-31 +// License scope: +// +// Copyright (C) 2005 Quantum Leaps, LLC . +// +// Q u a n t u m L e a P s +// ------------------------ +// Modern Embedded Software +// +// SPDX-License-Identifier: GPL-3.0-or-later OR LicenseRef-QL-commercial +// +// This software is dual-licensed under the terms of the open source GNU +// General Public License version 3 (or any later version), or alternatively, +// under the terms of one of the closed source Quantum Leaps commercial +// licenses. +// +// The terms of the open source GNU General Public License version 3 +// can be found at: +// +// The terms of the closed source Quantum Leaps commercial licenses +// can be found at: +// +// Redistributions in source code must retain this top-level comment block. +// Plagiarizing this software to sidestep the license obligations is illegal. +// +// Contact information: +// +// +// +//$endhead${src::qf::qf_qeq.cpp} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ #define QP_IMPL // this is QP implementation -#include "qf_port.hpp" // QF port -#include "qf_pkg.hpp" // QF package-scope interface -#include "qassert.h" // QP embedded systems-friendly assertions +#include "qp_port.hpp" // QP port +#include "qp_pkg.hpp" // QP package-scope interface +#include "qsafe.h" // QP Functional Safety (FuSa) Subsystem #ifdef Q_SPY // QS software tracing enabled? #include "qs_port.hpp" // QS port #include "qs_pkg.hpp" // QS facilities for pre-defined trace records @@ -46,43 +51,27 @@ #include "qs_dummy.hpp" // disable the QS software tracing #endif // Q_SPY -namespace QP { - +// unnamed namespace for local definitions with internal linkage +namespace { Q_DEFINE_THIS_MODULE("qf_qeq") +} // unnamed namespace + +//$skip${QP_VERSION} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv +// Check for the minimum required QP version +#if (QP_VERSION < 730U) || (QP_VERSION != ((QP_RELEASE^4294967295U) % 0x3E8U)) +#error qpcpp version 7.3.0 or higher required +#endif +//$endskip${QP_VERSION} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +//$define${QF::QEQueue} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv +namespace QP { +//${QF::QEQueue} ............................................................. -//**************************************************************************** -/// @description -/// Default constructor -/// -QEQueue::QEQueue(void) noexcept - : m_frontEvt(nullptr), - m_ring(nullptr), - m_end(0U), - m_head(0U), - m_tail(0U), - m_nFree(0U), - m_nMin(0U) -{} - -//**************************************************************************** -/// @description -/// Initialize the event queue by giving it the storage for the ring buffer. -/// -/// @param[in] qSto an array of pointers to QP::QEvt to sereve as the -/// ring buffer for the event queue -/// @param[in] qLen the length of the qSto[] buffer (in QP::QEvt pointers) -/// -/// @note -/// The actual capacity of the queue is qLen + 1, because of the extra -/// location forntEvt. -/// -/// @note This function is also used to initialize the event queues of active -/// objects in the built-int QV, QK and QXK kernels, as well as other -/// QP ports to OSes/RTOSes that do provide a suitable message queue. -/// -void QEQueue::init(QEvt const *qSto[], - std::uint_fast16_t const qLen) noexcept +//${QF::QEQueue::init} ....................................................... +void QEQueue::init( + QEvt const * qSto[], + std::uint_fast16_t const qLen) noexcept { m_frontEvt = nullptr; // no events in the queue m_ring = &qSto[0]; @@ -95,243 +84,189 @@ void QEQueue::init(QEvt const *qSto[], m_nMin = m_nFree; } -//**************************************************************************** -/// @description -/// Post an event to the "raw" thread-safe event queue using the -/// First-In-First-Out (FIFO) order. -/// -/// @param[in] e pointer to the event to be posted to the queue -/// @param[in] margin number of required free slots in the queue after -/// posting the event. The special value QP::QF_NO_MARGIN -/// means that this function will assert if posting -/// @param[in] qs_id QS-id of this state machine (for QS local filter) -/// -/// @note -/// The QP::QF_NO_MARGIN value of the @p margin argument is special and -/// denotes situation when the post() operation is assumed to succeed (event -/// delivery guarantee). An assertion fires, when the event cannot be -/// delivered in this case. -/// -/// @returns 'true' (success) when the posting succeeded with the provided -/// margin and 'false' (failure) when the posting fails. -/// -/// @note This function can be called from any task context or ISR context. -/// -/// @sa QP::QEQueue::postLIFO(), QP::QEQueue::get() -/// -bool QEQueue::post(QEvt const * const e, - std::uint_fast16_t const margin, - std::uint_fast8_t const qs_id) noexcept +//${QF::QEQueue::post} ....................................................... +bool QEQueue::post( + QEvt const * const e, + std::uint_fast16_t const margin, + std::uint_fast8_t const qsId) noexcept { - bool status; - QF_CRIT_STAT_ + #ifndef Q_SPY + Q_UNUSED_PAR(qsId); + #endif + + QF_CRIT_STAT + QF_CRIT_ENTRY(); + QF_MEM_SYS(); - /// @pre event must be valid - Q_REQUIRE_ID(200, e != nullptr); + Q_REQUIRE_INCRIT(200, e != nullptr); - QF_CRIT_E_(); - QEQueueCtr nFree = m_nFree; // temporary to avoid UB for volatile access + QEQueueCtr nFree = m_nFree; // get volatile into temporary - // margin available? - if (((margin == QF_NO_MARGIN) && (nFree > 0U)) + // required margin available? + bool status; + if (((margin == QF::NO_MARGIN) && (nFree > 0U)) || (nFree > static_cast(margin))) { - // is it a dynamic event? - if (e->poolId_ != 0U) { - QF_EVT_REF_CTR_INC_(e); // increment the reference counter + // is it a mutable event? + if (e->getPoolNum_() != 0U) { + QEvt_refCtr_inc_(e); // increment the reference counter } --nFree; // one free entry just used up - m_nFree = nFree; // update the volatile + m_nFree = nFree; // update the original if (m_nMin > nFree) { m_nMin = nFree; // update minimum so far } - QS_BEGIN_NOCRIT_PRE_(QS_QF_EQUEUE_POST, qs_id) - QS_TIME_PRE_(); // timestamp - QS_SIG_PRE_(e->sig); // the signal of this event - QS_OBJ_PRE_(this); // this queue object - QS_2U8_PRE_(e->poolId_, e->refCtr_);// pool Id & refCtr of the evt - QS_EQC_PRE_(nFree); // number of free entries - QS_EQC_PRE_(m_nMin); // min number of free entries - QS_END_NOCRIT_PRE_() - - // is the queue empty? - if (m_frontEvt == nullptr) { + QS_BEGIN_PRE_(QS_QF_EQUEUE_POST, qsId) + QS_TIME_PRE_(); // timestamp + QS_SIG_PRE_(e->sig); // the signal of this event + QS_OBJ_PRE_(this); // this queue object + QS_2U8_PRE_(e->getPoolNum_(), e->refCtr_); // poolNum & refCtr + QS_EQC_PRE_(nFree); // # free entries + QS_EQC_PRE_(m_nMin); // min # free entries + QS_END_PRE_() + + if (m_frontEvt == nullptr) { // was the queue empty? m_frontEvt = e; // deliver event directly } - // queue is not empty, leave event in the ring-buffer - else { + else { // queue was not empty, insert event into the ring-buffer // insert event into the ring buffer (FIFO) - QF_PTR_AT_(m_ring, m_head) = e; // insert e into buffer + m_ring[m_head] = e; // insert e into buffer // need to wrap? if (m_head == 0U) { m_head = m_end; // wrap around } - --m_head; + m_head = (m_head - 1U); } status = true; // event posted successfully } - else { - /// @note assert if event cannot be posted and dropping events is - /// not acceptable - Q_ASSERT_CRIT_(210, margin != QF_NO_MARGIN); + else { // event cannot be posted + // dropping events must be acceptable + Q_ASSERT_INCRIT(210, margin != QF::NO_MARGIN); - QS_BEGIN_NOCRIT_PRE_(QS_QF_EQUEUE_POST_ATTEMPT, qs_id) + QS_BEGIN_PRE_(QS_QF_EQUEUE_POST_ATTEMPT, qsId) QS_TIME_PRE_(); // timestamp QS_SIG_PRE_(e->sig); // the signal of this event QS_OBJ_PRE_(this); // this queue object - QS_2U8_PRE_(e->poolId_, e->refCtr_);// pool Id & refCtr of the evt - QS_EQC_PRE_(nFree); // number of free entries + QS_2U8_PRE_(e->getPoolNum_(), e->refCtr_); // poolNum & refCtr + QS_EQC_PRE_(nFree); // # free entries QS_EQC_PRE_(margin); // margin requested - QS_END_NOCRIT_PRE_() + QS_END_PRE_() status = false; // event not posted } - QF_CRIT_X_(); - static_cast(qs_id); // unused parameter, if Q_SPY not defined + QF_MEM_APP(); + QF_CRIT_EXIT(); return status; } -//**************************************************************************** -/// @description -/// Post an event to the "raw" thread-safe event queue using the -/// Last-In-First-Out (LIFO) order. -/// -/// @param[in] e pointer to the event to be posted to the queue -/// @param[in] qs_id QS-id of this state machine (for QS local filter) -/// -/// @attention -/// The LIFO policy should be used only with great __caution__, -/// because it alters the order of events in the queue. -/// -/// @note -/// This function can be called from any task context or ISR context. -/// -/// @note -/// This function is used for the "raw" thread-safe queues and __not__ -/// for the queues of active objects. -/// -/// @sa -/// QP::QEQueue::post(), QP::QEQueue::get(), QP::QActive::defer() -/// -void QEQueue::postLIFO(QEvt const * const e, - std::uint_fast8_t const qs_id) noexcept +//${QF::QEQueue::postLIFO} ................................................... +void QEQueue::postLIFO( + QEvt const * const e, + std::uint_fast8_t const qsId) noexcept { - QF_CRIT_STAT_ + #ifndef Q_SPY + Q_UNUSED_PAR(qsId); + #endif + + QF_CRIT_STAT + QF_CRIT_ENTRY(); + QF_MEM_SYS(); - QF_CRIT_E_(); - QEQueueCtr nFree = m_nFree; // temporary to avoid UB for volatile access + QEQueueCtr nFree = m_nFree; // get volatile into temporary - /// @pre the queue must be able to accept the event (cannot overflow) - Q_REQUIRE_CRIT_(300, nFree != 0U); + Q_REQUIRE_INCRIT(300, nFree != 0U); - // is it a dynamic event? - if (e->poolId_ != 0U) { - QF_EVT_REF_CTR_INC_(e); // increment the reference counter + if (e->getPoolNum_() != 0U) { // is it a mutable event? + QEvt_refCtr_inc_(e); // increment the reference counter } --nFree; // one free entry just used up - m_nFree = nFree; // update the volatile + m_nFree = nFree; // update the original if (m_nMin > nFree) { m_nMin = nFree; // update minimum so far } - QS_BEGIN_NOCRIT_PRE_(QS_QF_EQUEUE_POST_LIFO, qs_id) - QS_TIME_PRE_(); // timestamp - QS_SIG_PRE_(e->sig); // the signal of this event - QS_OBJ_PRE_(this); // this queue object - QS_2U8_PRE_(e->poolId_, e->refCtr_); // pool Id & refCtr of the evt - QS_EQC_PRE_(nFree); // number of free entries - QS_EQC_PRE_(m_nMin); // min number of free entries - QS_END_NOCRIT_PRE_() + QS_BEGIN_PRE_(QS_QF_EQUEUE_POST_LIFO, qsId) + QS_TIME_PRE_(); // timestamp + QS_SIG_PRE_(e->sig); // the signal of this event + QS_OBJ_PRE_(this); // this queue object + QS_2U8_PRE_(e->getPoolNum_(), e->refCtr_); // poolNum & refCtr + QS_EQC_PRE_(nFree); // # free entries + QS_EQC_PRE_(m_nMin); // min # free entries + QS_END_PRE_() - QEvt const * const frontEvt = m_frontEvt; // read volatile into temporary + QEvt const * const frontEvt = m_frontEvt; // read into temporary m_frontEvt = e; // deliver event directly to the front of the queue - // was the queue not empty? - if (frontEvt != nullptr) { - ++m_tail; + if (frontEvt != nullptr) { // was the queue not empty? + m_tail = (m_tail + 1U); if (m_tail == m_end) { // need to wrap the tail? m_tail = 0U; // wrap around } - QF_PTR_AT_(m_ring, m_tail) = frontEvt; // buffer the old front evt + m_ring[m_tail] = frontEvt; // buffer the old front evt } - static_cast(qs_id); // unused parameter, if Q_SPY not defined - - QF_CRIT_X_(); + QF_MEM_APP(); + QF_CRIT_EXIT(); } -//**************************************************************************** -/// @description -/// Retrieves an event from the front of the "raw" thread-safe queue and -/// returns a pointer to this event to the caller. -/// -/// @param[in] qs_id QS-id of this state machine (for QS local filter) -/// -/// @returns -/// pointer to event at the front of the queue, if the queue is -/// not empty and NULL if the queue is empty. -/// -/// @note -/// this function is used for the "raw" thread-safe queues and __not__ -/// for the queues of active objects. -/// -/// @sa -/// QP::QEQueue::post(), QP::QEQueue::postLIFO(), QP::QActive::recall() -/// -QEvt const *QEQueue::get(std::uint_fast8_t const qs_id) noexcept { - QEvt const *e; - QF_CRIT_STAT_ - - QF_CRIT_E_(); - e = m_frontEvt; // always remove the event from the front location - - // is the queue not empty? - if (e != nullptr) { +//${QF::QEQueue::get} ........................................................ +QEvt const * QEQueue::get(std::uint_fast8_t const qsId) noexcept { + #ifndef Q_SPY + Q_UNUSED_PAR(qsId); + #endif + + QF_CRIT_STAT + QF_CRIT_ENTRY(); + QF_MEM_SYS(); + + QEvt const * const e = m_frontEvt; // always remove evt from the front + + if (e != nullptr) { // was the queue not empty? QEQueueCtr const nFree = m_nFree + 1U; - m_nFree = nFree; // upate the number of free + m_nFree = nFree; // update the # free // any events in the the ring buffer? if (nFree <= m_end) { - m_frontEvt = QF_PTR_AT_(m_ring, m_tail); // remove from the tail + m_frontEvt = m_ring[m_tail]; // remove from the tail if (m_tail == 0U) { // need to wrap? m_tail = m_end; // wrap around } - --m_tail; - - QS_BEGIN_NOCRIT_PRE_(QS_QF_EQUEUE_GET, qs_id) - QS_TIME_PRE_(); // timestamp - QS_SIG_PRE_(e->sig); // the signal of this event - QS_OBJ_PRE_(this); // this queue object - QS_2U8_PRE_(e->poolId_, e->refCtr_); - QS_EQC_PRE_(nFree); // # free entries - QS_END_NOCRIT_PRE_() + m_tail = (m_tail - 1U); + + QS_BEGIN_PRE_(QS_QF_EQUEUE_GET, qsId) + QS_TIME_PRE_(); // timestamp + QS_SIG_PRE_(e->sig); // the signal of this event + QS_OBJ_PRE_(this); // this queue object + QS_2U8_PRE_(e->getPoolNum_(), e->refCtr_); // poolNum & refCtr + QS_EQC_PRE_(nFree); // # free entries + QS_END_PRE_() } else { m_frontEvt = nullptr; // queue becomes empty // all entries in the queue must be free (+1 for fronEvt) - Q_ASSERT_CRIT_(410, nFree == (m_end + 1U)); - - QS_BEGIN_NOCRIT_PRE_(QS_QF_EQUEUE_GET_LAST, qs_id) - QS_TIME_PRE_(); // timestamp - QS_SIG_PRE_(e->sig); // the signal of this event - QS_OBJ_PRE_(this); // this queue object - QS_2U8_PRE_(e->poolId_, e->refCtr_); - QS_END_NOCRIT_PRE_() + Q_ASSERT_INCRIT(410, nFree == (m_end + 1U)); + + QS_BEGIN_PRE_(QS_QF_EQUEUE_GET_LAST, qsId) + QS_TIME_PRE_(); // timestamp + QS_SIG_PRE_(e->sig); // the signal of this event + QS_OBJ_PRE_(this); // this queue object + QS_2U8_PRE_(e->getPoolNum_(), e->refCtr_); // poolNum & refCtr + QS_END_PRE_() } } - QF_CRIT_X_(); - static_cast(qs_id); // unused parameter, if Q_SPY not defined + QF_MEM_APP(); + QF_CRIT_EXIT(); return e; } } // namespace QP - +//$enddef${QF::QEQueue} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/libraries/qpcpp_arm-cm/src/qf_qmact.cpp b/libraries/qpcpp_arm-cm/src/qf_qmact.cpp index 54c11aa..1fb6bde 100644 --- a/libraries/qpcpp_arm-cm/src/qf_qmact.cpp +++ b/libraries/qpcpp_arm-cm/src/qf_qmact.cpp @@ -1,100 +1,80 @@ -/// @file -/// @brief QMActive::QMActive() and virtual functions -/// @cond -///*************************************************************************** -/// Last updated for version 6.9.2 -/// Last updated on 2020-12-17 -/// -/// Q u a n t u m L e a P s -/// ------------------------ -/// Modern Embedded Software -/// -/// Copyright (C) 2005-2020 Quantum Leaps. All rights reserved. -/// -/// This program is open source software: you can redistribute it and/or -/// modify it under the terms of the GNU General Public License as published -/// by the Free Software Foundation, either version 3 of the License, or -/// (at your option) any later version. -/// -/// Alternatively, this program may be distributed and modified under the -/// terms of Quantum Leaps commercial licenses, which expressly supersede -/// the GNU General Public License and are specifically designed for -/// licensees interested in retaining the proprietary status of their code. -/// -/// This program is distributed in the hope that it will be useful, -/// but WITHOUT ANY WARRANTY; without even the implied warranty of -/// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -/// GNU General Public License for more details. -/// -/// You should have received a copy of the GNU General Public License -/// along with this program. If not, see . -/// -/// Contact information: -/// -/// -///*************************************************************************** -/// @endcond +//$file${src::qf::qf_qmact.cpp} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv +// +// Model: qpcpp.qm +// File: ${src::qf::qf_qmact.cpp} +// +// This code has been generated by QM 6.1.1 . +// DO NOT EDIT THIS FILE MANUALLY. All your changes will be lost. +// +// This code is covered by the following QP license: +// License # : LicenseRef-QL-dual +// Issued to : Any user of the QP/C++ real-time embedded framework +// Framework(s) : qpcpp +// Support ends : 2024-12-31 +// License scope: +// +// Copyright (C) 2005 Quantum Leaps, LLC . +// +// Q u a n t u m L e a P s +// ------------------------ +// Modern Embedded Software +// +// SPDX-License-Identifier: GPL-3.0-or-later OR LicenseRef-QL-commercial +// +// This software is dual-licensed under the terms of the open source GNU +// General Public License version 3 (or any later version), or alternatively, +// under the terms of one of the closed source Quantum Leaps commercial +// licenses. +// +// The terms of the open source GNU General Public License version 3 +// can be found at: +// +// The terms of the closed source Quantum Leaps commercial licenses +// can be found at: +// +// Redistributions in source code must retain this top-level comment block. +// Plagiarizing this software to sidestep the license obligations is illegal. +// +// Contact information: +// +// +// +//$endhead${src::qf::qf_qmact.cpp} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +#define QP_IMPL // this is QP implementation +#include "qp_port.hpp" // QP port +#include "qp_pkg.hpp" // QP package-scope interface +#include "qsafe.h" // QP Functional Safety (FuSa) Subsystem +#ifdef Q_SPY // QS software tracing enabled? + #include "qs_port.hpp" // QS port + #include "qs_pkg.hpp" // QS facilities for pre-defined trace records +#else + #include "qs_dummy.hpp" // disable the QS software tracing +#endif // Q_SPY -#define QP_IMPL // this is QP implementation -#include "qf_port.hpp" // QF port -#include "qassert.h" // QP embedded systems-friendly assertions - -//! Internal macro to cast a QP::QMActive pointer @p qact_ to QP::QMsm* -/// @note -/// Casting pointer to pointer pointer violates the MISRA-C++ 2008 Rule 5-2-7, -/// cast from pointer to pointer. Additionally this cast violates the MISRA- -/// C++ 2008 Rule 5-2-8 Unusual pointer cast (incompatible indirect types). -/// Encapsulating these violations in a macro allows to selectively suppress -/// this specific deviation. -#define QF_QMACTIVE_TO_QMSM_CAST_(qact_) \ - reinterpret_cast((qact_)) +// unnamed namespace for local definitions with internal linkage +namespace { +//Q_DEFINE_THIS_MODULE("qf_qmact") +} // unnamed namespace -//! Internal macro to cast a QP::QMActive pointer @p qact_ to QP::QMsm const * -#define QF_QMACTIVE_TO_QMSM_CONST_CAST_(qact_) \ - reinterpret_cast((qact_)) +//$skip${QP_VERSION} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv +// Check for the minimum required QP version +#if (QP_VERSION < 730U) || (QP_VERSION != ((QP_RELEASE^4294967295U) % 0x3E8U)) +#error qpcpp version 7.3.0 or higher required +#endif +//$endskip${QP_VERSION} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +//$define${QF::QMActive} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv namespace QP { -//Q_DEFINE_THIS_MODULE("qf_qmact") +//${QF::QMActive} ............................................................ -//............................................................................ +//${QF::QMActive::QMActive} .................................................. QMActive::QMActive(QStateHandler const initial) noexcept : QActive(initial) { + m_state.obj = reinterpret_cast(this)->topQMState(); m_temp.fun = initial; } -//............................................................................ -void QMActive::init(void const * const e, std::uint_fast8_t const qs_id) { - m_state.obj = &QMsm::msm_top_s; - QF_QMACTIVE_TO_QMSM_CAST_(this)->QMsm::init(e, qs_id); -} -//............................................................................ -void QMActive::init(std::uint_fast8_t const qs_id) { - QF_QMACTIVE_TO_QMSM_CAST_(this)->QMsm::init(qs_id); -} -//............................................................................ -void QMActive::dispatch(QEvt const * const e, std::uint_fast8_t const qs_id) { - QF_QMACTIVE_TO_QMSM_CAST_(this)->QMsm::dispatch(e, qs_id); -} - -//............................................................................ -bool QMActive::isInState(QMState const * const st) const noexcept { - return QF_QMACTIVE_TO_QMSM_CONST_CAST_(this)->QMsm::isInState(st); -} -//............................................................................ -QMState const *QMActive::childStateObj(QMState const * const parent) - const noexcept -{ - return QF_QMACTIVE_TO_QMSM_CONST_CAST_(this)->QMsm::childStateObj(parent); -} - -//............................................................................ -#ifdef Q_SPY - QStateHandler QMActive::getStateHandler() noexcept { - return QF_QMACTIVE_TO_QMSM_CAST_(this)->QMsm::getStateHandler(); - } -#endif - } // namespace QP - +//$enddef${QF::QMActive} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/libraries/qpcpp_arm-cm/src/qf_time.cpp b/libraries/qpcpp_arm-cm/src/qf_time.cpp index 0836fa5..cd8187b 100644 --- a/libraries/qpcpp_arm-cm/src/qf_time.cpp +++ b/libraries/qpcpp_arm-cm/src/qf_time.cpp @@ -1,45 +1,49 @@ -/// @file -/// @brief QF/C++ time events and time management services -/// @ingroup qf -/// @cond -///*************************************************************************** -/// Last updated for version 6.9.1 -/// Last updated on 2020-09-17 -/// -/// Q u a n t u m L e a P s -/// ------------------------ -/// Modern Embedded Software -/// -/// Copyright (C) 2005-2020 Quantum Leaps. All rights reserved. -/// -/// This program is open source software: you can redistribute it and/or -/// modify it under the terms of the GNU General Public License as published -/// by the Free Software Foundation, either version 3 of the License, or -/// (at your option) any later version. -/// -/// Alternatively, this program may be distributed and modified under the -/// terms of Quantum Leaps commercial licenses, which expressly supersede -/// the GNU General Public License and are specifically designed for -/// licensees interested in retaining the proprietary status of their code. -/// -/// This program is distributed in the hope that it will be useful, -/// but WITHOUT ANY WARRANTY; without even the implied warranty of -/// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -/// GNU General Public License for more details. -/// -/// You should have received a copy of the GNU General Public License -/// along with this program. If not, see . -/// -/// Contact information: -/// -/// -///*************************************************************************** -/// @endcond - +//$file${src::qf::qf_time.cpp} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv +// +// Model: qpcpp.qm +// File: ${src::qf::qf_time.cpp} +// +// This code has been generated by QM 6.1.1 . +// DO NOT EDIT THIS FILE MANUALLY. All your changes will be lost. +// +// This code is covered by the following QP license: +// License # : LicenseRef-QL-dual +// Issued to : Any user of the QP/C++ real-time embedded framework +// Framework(s) : qpcpp +// Support ends : 2024-12-31 +// License scope: +// +// Copyright (C) 2005 Quantum Leaps, LLC . +// +// Q u a n t u m L e a P s +// ------------------------ +// Modern Embedded Software +// +// SPDX-License-Identifier: GPL-3.0-or-later OR LicenseRef-QL-commercial +// +// This software is dual-licensed under the terms of the open source GNU +// General Public License version 3 (or any later version), or alternatively, +// under the terms of one of the closed source Quantum Leaps commercial +// licenses. +// +// The terms of the open source GNU General Public License version 3 +// can be found at: +// +// The terms of the closed source Quantum Leaps commercial licenses +// can be found at: +// +// Redistributions in source code must retain this top-level comment block. +// Plagiarizing this software to sidestep the license obligations is illegal. +// +// Contact information: +// +// +// +//$endhead${src::qf::qf_time.cpp} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ #define QP_IMPL // this is QP implementation -#include "qf_port.hpp" // QF port -#include "qf_pkg.hpp" // QF package-scope interface -#include "qassert.h" // QP embedded systems-friendly assertions +#include "qp_port.hpp" // QP port +#include "qp_pkg.hpp" // QP package-scope interface +#include "qsafe.h" // QP Functional Safety (FuSa) Subsystem #ifdef Q_SPY // QS software tracing enabled? #include "qs_port.hpp" // QS port #include "qs_pkg.hpp" // QS facilities for pre-defined trace records @@ -47,525 +51,403 @@ #include "qs_dummy.hpp" // disable the QS software tracing #endif // Q_SPY -namespace QP { - +// unnamed namespace for local definitions with internal linkage +namespace { Q_DEFINE_THIS_MODULE("qf_time") +} // unnamed namespace -// Package-scope objects ***************************************************** -QTimeEvt QF::timeEvtHead_[QF_MAX_TICK_RATE]; // heads of time event lists - -#ifdef Q_SPY -//**************************************************************************** -/// @description -/// This function must be called periodically from a time-tick ISR or from -/// a task so that QF can manage the timeout events assigned to the given -/// system clock tick rate. -/// -/// @param[in] tickRate system clock tick rate serviced in this call [1..15]. -/// @param[in] sender pointer to a sender object (used in QS only). -/// -/// @note -/// this function should be called only via the macro TICK_X() -/// -/// @note -/// the calls to QP::QF::tickX_() with different @p tickRate parameter can -/// preempt each other. For example, higher clock tick rates might be -/// serviced from interrupts while others from tasks (active objects). -/// -/// @sa -/// QP::QTimeEvt. -/// -void QF::tickX_(std::uint_fast8_t const tickRate, - void const * const sender) noexcept -#else -void QF::tickX_(std::uint_fast8_t const tickRate) noexcept +//$skip${QP_VERSION} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv +// Check for the minimum required QP version +#if (QP_VERSION < 730U) || (QP_VERSION != ((QP_RELEASE^4294967295U) % 0x3E8U)) +#error qpcpp version 7.3.0 or higher required #endif -{ - QTimeEvt *prev = &timeEvtHead_[tickRate]; - QF_CRIT_STAT_ - - QF_CRIT_E_(); - - QS_BEGIN_NOCRIT_PRE_(QS_QF_TICK, 0U) - ++prev->m_ctr; - QS_TEC_PRE_(prev->m_ctr); // tick ctr - QS_U8_PRE_(tickRate); // tick rate - QS_END_NOCRIT_PRE_() - - // scan the linked-list of time events at this rate... - for (;;) { - QTimeEvt *t = prev->m_next; // advance down the time evt. list - - // end of the list? - if (t == nullptr) { - - // any new time events armed since the last run of QF::tickX_()? - if (timeEvtHead_[tickRate].m_act != nullptr) { - - // sanity check - Q_ASSERT_CRIT_(110, prev != nullptr); - prev->m_next = QF::timeEvtHead_[tickRate].toTimeEvt(); - timeEvtHead_[tickRate].m_act = nullptr; - t = prev->m_next; // switch to the new list - } - else { - break; // all currently armed time evts. processed - } - } - - // time event scheduled for removal? - if (t->m_ctr == 0U) { - prev->m_next = t->m_next; - // mark time event 't' as NOT linked - t->refCtr_ &= static_cast(~TE_IS_LINKED); - // do NOT advance the prev pointer - QF_CRIT_X_(); // exit crit. section to reduce latency - - // prevent merging critical sections, see NOTE1 below - QF_CRIT_EXIT_NOP(); - } - else { - --t->m_ctr; - - // is time evt about to expire? - if (t->m_ctr == 0U) { - QActive * const act = t->toActive(); // temporary for volatile - - // periodic time evt? - if (t->m_interval != 0U) { - t->m_ctr = t->m_interval; // rearm the time event - prev = t; // advance to this time event - } - // one-shot time event: automatically disarm - else { - prev->m_next = t->m_next; - - // mark time event 't' as NOT linked - t->refCtr_ &= static_cast(~TE_IS_LINKED); - // do NOT advance the prev pointer - - QS_BEGIN_NOCRIT_PRE_(QS_QF_TIMEEVT_AUTO_DISARM, - act->m_prio) - QS_OBJ_PRE_(t); // this time event object - QS_OBJ_PRE_(act); // the target AO - QS_U8_PRE_(tickRate); // tick rate - QS_END_NOCRIT_PRE_() - } +//$endskip${QP_VERSION} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - QS_BEGIN_NOCRIT_PRE_(QS_QF_TIMEEVT_POST, act->m_prio) - QS_TIME_PRE_(); // timestamp - QS_OBJ_PRE_(t); // the time event object - QS_SIG_PRE_(t->sig); // signal of this time event - QS_OBJ_PRE_(act); // the target AO - QS_U8_PRE_(tickRate); // tick rate - QS_END_NOCRIT_PRE_() - - QF_CRIT_X_(); // exit crit. section before posting - - // asserts if queue overflows - static_cast(act->POST(t, sender)); - } - else { - prev = t; // advance to this time event - QF_CRIT_X_(); // exit crit. section to reduce latency - - // prevent merging critical sections, see NOTE1 below - QF_CRIT_EXIT_NOP(); - } - } - QF_CRIT_E_(); // re-enter crit. section to continue - } - QF_CRIT_X_(); -} +//$define${QF::QTimeEvt} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv +namespace QP { -//**************************************************************************** -// NOTE1: -// In some QF ports the critical section exit takes effect only on the next -// machine instruction. If this case, the next instruction is another entry -// to a critical section, the critical section won't be really exited, but -// rather the two adjacent critical sections would be merged. -// -// The QF_CRIT_EXIT_NOP() macro contains minimal code required -// to prevent such merging of critical sections in QF ports, -// in which it can occur. - - -//**************************************************************************** -/// @description -/// Find out if any time events are armed at the given clock tick rate. -/// -/// @param[in] tickRate system clock tick rate to find out about. -/// -/// @returns -/// 'true' if no time events are armed at the given tick rate and -/// 'false' otherwise. -/// -/// @note -/// This function should be called in critical section. -/// -bool QF::noTimeEvtsActiveX(std::uint_fast8_t const tickRate) noexcept { - bool inactive; - if (timeEvtHead_[tickRate].m_next != nullptr) { - inactive = false; - } - else if (timeEvtHead_[tickRate].m_act != nullptr) { - inactive = false; - } - else { - inactive = true; - } - return inactive; -} +//${QF::QTimeEvt} ............................................................ +QTimeEvt QTimeEvt::timeEvtHead_[QF_MAX_TICK_RATE]; -//**************************************************************************** -/// @description -/// When creating a time event, you must commit it to a specific active object -/// @p act, tick rate @p tickRate and event signal @p sgnl. You cannot change -/// these attributes later. -/// -/// @param[in] act pointer to the active object associated with this -/// time event. The time event will post itself to this AO. -/// @param[in] sgnl signal to associate with this time event. -/// @param[in] tickRate system tick rate to associate with this time event. -/// -QTimeEvt::QTimeEvt(QActive * const act, - enum_t const sgnl, std::uint_fast8_t const tickRate) noexcept - : -#ifdef Q_EVT_CTOR - QEvt(static_cast(sgnl), QEvt::STATIC_EVT), -#else - QEvt(), -#endif +//${QF::QTimeEvt::QTimeEvt} .................................................. +QTimeEvt::QTimeEvt( + QActive * const act, + QSignal const sig, + std::uint_fast8_t const tickRate) noexcept + : + QEvt(sig), m_next(nullptr), m_act(act), m_ctr(0U), m_interval(0U) { - /// @pre The signal must be valid and the tick rate in range - Q_REQUIRE_ID(300, (sgnl >= Q_USER_SIG) - && (tickRate < QF_MAX_TICK_RATE)); - -#ifndef Q_EVT_CTOR - sig = static_cast(sgnl); // set QEvt::sig of this time event -#endif - - // Setting the POOL_ID event attribute to zero is correct only for - // events not allocated from event pools, which must be the case - // for Time Events. - // - poolId_ = 0U; + QF_CRIT_STAT + QF_CRIT_ENTRY(); + Q_REQUIRE_INCRIT(300, (sig != 0U) + && (tickRate < QF_MAX_TICK_RATE)); + QF_CRIT_EXIT(); // The refCtr_ attribute is not used in time events, so it is // reused to hold the tickRate as well as other information - // refCtr_ = static_cast(tickRate); } -//**************************************************************************** -/// @note -/// private default ctor for internal use only -/// -QTimeEvt::QTimeEvt() noexcept - : -#ifdef Q_EVT_CTOR - QEvt(0U, QEvt::STATIC_EVT), -#else - QEvt(), -#endif // Q_EVT_CTOR - m_next(nullptr), - m_act(nullptr), - m_ctr(0U), - m_interval(0U) -{ -#ifndef Q_EVT_CTOR - sig = 0U; - - // Setting the POOL_ID event attribute to zero is correct only for - // events not allocated from event pools, which must be the case - // for Time Events. - // - poolId_ = 0U; // not from any event pool - - // The refCtr_ attribute is not used in time events, so it is - // reused to hold the tickRate as well as other information - // - refCtr_ = 0U; // default rate 0 - -#endif // Q_EVT_CTOR - -} - -//**************************************************************************** -/// @description -/// Arms a time event to fire in a specified number of clock ticks and with -/// a specified interval. If the interval is zero, the time event is armed -/// for one shot ('one-shot' time event). The time event gets directly posted -/// (using the FIFO policy) into the event queue of the host active object. -/// -/// @param[in] nTicks number of clock ticks (at the associated rate) -/// to rearm the time event with. -/// @param[in] interval interval (in clock ticks) for periodic time event. -/// -/// @note -/// After posting, a one-shot time event gets automatically disarmed -/// while a periodic time event (interval != 0) is automatically re-armed. -/// -/// @note -/// A time event can be disarmed at any time by calling -/// QP::QTimeEvt::disarm(). Also, a time event can be re-armed to fire in a -/// different number of clock ticks by calling the QP::QTimeEvt::rearm() -/// function. -/// -/// @usage -/// The following example shows how to arm a one-shot time event from a state -/// machine of an active object: -/// @include qf_state.cpp -/// -void QTimeEvt::armX(QTimeEvtCtr const nTicks, - QTimeEvtCtr const interval) noexcept +//${QF::QTimeEvt::armX} ...................................................... +void QTimeEvt::armX( + QTimeEvtCtr const nTicks, + QTimeEvtCtr const interval) noexcept { std::uint8_t const tickRate = refCtr_ & TE_TICK_RATE; - QTimeEvtCtr const ctr = m_ctr; // temporary to hold volatile - QF_CRIT_STAT_ - - /// @pre the host AO must be valid, time evnet must be disarmed, - /// number of clock ticks cannot be zero, and the signal must be valid. - /// - Q_REQUIRE_ID(400, (m_act != nullptr) - && (ctr == 0U) - && (nTicks != 0U) - && (tickRate < static_cast(QF_MAX_TICK_RATE)) - && (static_cast(sig) >= Q_USER_SIG)); -#ifdef Q_NASSERT - (void)ctr; // avoid compiler warning about unused variable -#endif + QTimeEvtCtr const ctr = m_ctr; + #ifdef Q_SPY + std::uint_fast8_t const qsId = + static_cast(m_act)->m_prio; + #endif + + QF_CRIT_STAT + QF_CRIT_ENTRY(); + QF_MEM_SYS(); + + Q_REQUIRE_INCRIT(400, (m_act != nullptr) + && (ctr == 0U) + && (nTicks != 0U) + && (tickRate < static_cast(QF_MAX_TICK_RATE)) + && (sig >= static_cast(Q_USER_SIG))); + #ifdef Q_UNSAFE + Q_UNUSED_PAR(ctr); + #endif - QF_CRIT_E_(); m_ctr = nTicks; m_interval = interval; // is the time event unlinked? // NOTE: For the duration of a single clock tick of the specified tick - // rate a time event can be disarmed and yet still linked into the list, + // rate a time event can be disarmed and yet still linked into the list // because un-linking is performed exclusively in the QF_tickX() function. - // if (static_cast( static_cast(refCtr_) & TE_IS_LINKED) == 0U) { - refCtr_ |= TE_IS_LINKED; // mark as linked + // mark as linked + refCtr_ = static_cast(refCtr_ | TE_IS_LINKED); // The time event is initially inserted into the separate - // "freshly armed" link list based on QF::timeEvtHead_[tickRate].act. - // Only later, inside the QF::tickX() function, the "freshly armed" + // "freshly armed" list based on timeEvtHead_[tickRate].act. + // Only later, inside QTimeEvt::tick(), the "freshly armed" // list is appended to the main list of armed time events based on - // QF::timeEvtHead_[tickRate].next. Again, this is to keep any - // changes to the main list exclusively inside the QF::tickX() - // function. - // - m_next = QF::timeEvtHead_[tickRate].toTimeEvt(); - QF::timeEvtHead_[tickRate].m_act = this; + // timeEvtHead_[tickRate].next. Again, this is to keep any + // changes to the main list exclusively inside QTimeEvt::tick(). + m_next = timeEvtHead_[tickRate].toTimeEvt(); + timeEvtHead_[tickRate].m_act = this; } -#ifdef Q_SPY - std::uint_fast8_t const qs_id = static_cast(m_act)->m_prio; -#endif - QS_BEGIN_NOCRIT_PRE_(QS_QF_TIMEEVT_ARM, qs_id) + QS_BEGIN_PRE_(QS_QF_TIMEEVT_ARM, qsId) QS_TIME_PRE_(); // timestamp QS_OBJ_PRE_(this); // this time event object QS_OBJ_PRE_(m_act); // the active object - QS_TEC_PRE_(nTicks); // the number of ticks + QS_TEC_PRE_(nTicks); // the # ticks QS_TEC_PRE_(interval); // the interval QS_U8_PRE_(tickRate); // tick rate - QS_END_NOCRIT_PRE_() + QS_END_PRE_() - QF_CRIT_X_(); + QF_MEM_APP(); + QF_CRIT_EXIT(); } -//**************************************************************************** -/// @description -/// Disarm the time event so it can be safely reused. -/// -/// @returns -/// 'true' if the time event was truly disarmed, that is, it was running. -/// The return of 'false' means that the time event was not truly disarmed, -/// because it was not running. The 'false' return is only possible for one- -///shot time events that have been automatically disarmed upon expiration. -/// In 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. -/// -/// @note -/// there is no harm in disarming an already disarmed time event -/// -bool QTimeEvt::disarm(void) noexcept { - QF_CRIT_STAT_ - QF_CRIT_E_(); - bool wasArmed; -#ifdef Q_SPY - std::uint_fast8_t const qs_id = static_cast(m_act)->m_prio; -#endif +//${QF::QTimeEvt::disarm} .................................................... +bool QTimeEvt::disarm() noexcept { + #ifdef Q_SPY + std::uint_fast8_t const qsId = static_cast(m_act)->m_prio; + #endif + + QF_CRIT_STAT + QF_CRIT_ENTRY(); + QF_MEM_SYS(); // is the time event actually armed? + bool wasArmed; if (m_ctr != 0U) { wasArmed = true; - refCtr_ |= TE_WAS_DISARMED; + refCtr_ = static_cast(refCtr_ | TE_WAS_DISARMED); - QS_BEGIN_NOCRIT_PRE_(QS_QF_TIMEEVT_DISARM, qs_id) + QS_BEGIN_PRE_(QS_QF_TIMEEVT_DISARM, qsId) QS_TIME_PRE_(); // timestamp QS_OBJ_PRE_(this); // this time event object QS_OBJ_PRE_(m_act); // the target AO - QS_TEC_PRE_(m_ctr); // the number of ticks + QS_TEC_PRE_(m_ctr); // the # ticks QS_TEC_PRE_(m_interval); // the interval - QS_U8_PRE_(refCtr_& TE_TICK_RATE); - QS_END_NOCRIT_PRE_() + QS_U8_PRE_(refCtr_& TE_TICK_RATE); // tick rate + QS_END_PRE_() m_ctr = 0U; // schedule removal from the list } else { // the time event was already disarmed automatically wasArmed = false; - refCtr_ &= static_cast(~TE_WAS_DISARMED); + refCtr_ = static_cast(refCtr_ + & static_cast(~TE_WAS_DISARMED)); - QS_BEGIN_NOCRIT_PRE_(QS_QF_TIMEEVT_DISARM_ATTEMPT, qs_id) + QS_BEGIN_PRE_(QS_QF_TIMEEVT_DISARM_ATTEMPT, qsId) QS_TIME_PRE_(); // timestamp QS_OBJ_PRE_(this); // this time event object QS_OBJ_PRE_(m_act); // the target AO QS_U8_PRE_(refCtr_& TE_TICK_RATE); // tick rate - QS_END_NOCRIT_PRE_() - + QS_END_PRE_() } - QF_CRIT_X_(); + + QF_MEM_APP(); + QF_CRIT_EXIT(); + return wasArmed; } -//**************************************************************************** -/// -/// @description -/// Rearms a time event with a new number of clock ticks. This function can -/// be used to adjust the current period of a periodic time event or to -/// prevent a one-shot time event from expiring (e.g., a watchdog time event). -/// Rearming a periodic timer leaves the interval unchanged and is a -/// convenient method to adjust the phasing of a periodic time event. -/// -/// @param[in] nTicks number of clock ticks (at the associated rate) -/// to rearm the time event with. -/// -/// @returns -/// 'true' if the time event was running as it was re-armed. The 'false' -/// return means that the time event was not truly rearmed because it was -/// not running. The 'false' return is only possible for one-shot time events -/// that have been automatically disarmed upon expiration. In 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. -/// +//${QF::QTimeEvt::rearm} ..................................................... bool QTimeEvt::rearm(QTimeEvtCtr const nTicks) noexcept { std::uint8_t const tickRate = refCtr_ & TE_TICK_RATE; - QF_CRIT_STAT_ - /// @pre AO must be valid, tick rate must be in range, nTicks must not - /// be zero, and the signal of this time event must be valid - /// - Q_REQUIRE_ID(600, (m_act != nullptr) + #ifdef Q_SPY + std::uint_fast8_t const qsId = static_cast(m_act)->m_prio; + #endif + + QF_CRIT_STAT + QF_CRIT_ENTRY(); + QF_MEM_SYS(); + + Q_REQUIRE_INCRIT(600, (m_act != nullptr) && (tickRate < static_cast(QF_MAX_TICK_RATE)) && (nTicks != 0U) - && (static_cast(sig) >= Q_USER_SIG)); - - QF_CRIT_E_(); - bool wasArmed; + && (sig >= static_cast(Q_USER_SIG))); // is the time evt not running? + bool wasArmed; if (m_ctr == 0U) { wasArmed = false; - // is the time event unlinked? // NOTE: For a duration of a single clock tick of the specified // tick rate a time event can be disarmed and yet still linked into // the list, because unlinking is performed exclusively in the - // QF::tickX() function. - // - if ((refCtr_ & TE_IS_LINKED) == 0U) { - refCtr_ |= TE_IS_LINKED; // mark as linked + // QTimeEvt::tickX() function. + + // is the time event unlinked? + if (static_cast(refCtr_ & TE_IS_LINKED) == 0U) { + // mark as linked + refCtr_ = static_cast(refCtr_ | TE_IS_LINKED); // The time event is initially inserted into the separate - // "freshly armed" list based on QF_timeEvtHead_[tickRate].act. - // Only later, inside the QF_tickX() function, the "freshly armed" + // "freshly armed" list based on timeEvtHead_[tickRate].act. + // Only later, inside QTimeEvt::tick(), the "freshly armed" // list is appended to the main list of armed time events based on - // QF_timeEvtHead_[tickRate].next. Again, this is to keep any - // changes to the main list exclusively inside the QF::tickX() - // function. - // - m_next = QF::timeEvtHead_[tickRate].toTimeEvt(); - QF::timeEvtHead_[tickRate].m_act = this; + // timeEvtHead_[tickRate].next. Again, this is to keep any + // changes to the main list exclusively inside QTimeEvt::tick(). + m_next = timeEvtHead_[tickRate].toTimeEvt(); + timeEvtHead_[tickRate].m_act = this; } } - else { // the time event is being disarmed + else { // the time event was armed wasArmed = true; } m_ctr = nTicks; // re-load the tick counter (shift the phasing) -#ifdef Q_SPY - std::uint_fast8_t const qs_id = static_cast(m_act)->m_prio; -#endif - QS_BEGIN_NOCRIT_PRE_(QS_QF_TIMEEVT_REARM, qs_id) - QS_TIME_PRE_(); // timestamp - QS_OBJ_PRE_(this); // this time event object - QS_OBJ_PRE_(m_act); // the target AO - QS_TEC_PRE_(m_ctr); // the number of ticks - QS_TEC_PRE_(m_interval); // the interval + QS_BEGIN_PRE_(QS_QF_TIMEEVT_REARM, qsId) + QS_TIME_PRE_(); // timestamp + QS_OBJ_PRE_(this); // this time event object + QS_OBJ_PRE_(m_act); // the target AO + QS_TEC_PRE_(m_ctr); // the # ticks + QS_TEC_PRE_(m_interval); // the interval QS_2U8_PRE_(tickRate, (wasArmed ? 1U : 0U)); - QS_END_NOCRIT_PRE_() + QS_END_PRE_() + + QF_MEM_APP(); + QF_CRIT_EXIT(); - QF_CRIT_X_(); return wasArmed; } -//**************************************************************************** -/// -/// @description -/// Useful for checking whether a one-shot time event was disarmed in the -/// QTimeEvt_disarm() operation. -/// -/// @returns -/// 'true' if the time event was truly disarmed in the last QTimeEvt::disarm() -/// operation. The 'false' return means that the time event was not truly -/// disarmed, because it was not running at that time. The 'false' return is -/// only possible for one-shot time events that have been automatically -/// disarmed upon expiration. In this case the 'false' return means that the -/// time event has already been posted or published and should be expected -/// in the active object's event queue. -/// -/// @note -/// This function has a **side effect** of setting the "was disarmed" status, -/// which means that the second and subsequent times this function is called -/// the function will return 'true'. -/// -bool QTimeEvt::wasDisarmed(void) noexcept { +//${QF::QTimeEvt::wasDisarmed} ............................................... +bool QTimeEvt::wasDisarmed() noexcept { + QF_CRIT_STAT + QF_CRIT_ENTRY(); + QF_MEM_SYS(); + std::uint8_t const isDisarmed = refCtr_ & TE_WAS_DISARMED; - refCtr_ |= TE_WAS_DISARMED; // set the flag + refCtr_ = static_cast(refCtr_ | TE_WAS_DISARMED); + + QF_MEM_APP(); + QF_CRIT_EXIT(); + return isDisarmed != 0U; } -//**************************************************************************** -/// @description -/// Useful for checking how many clock ticks (at the tick rate associated -/// with the time event) remain until the time event expires. -/// -/// @returns -/// For an armed time event, 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 QTimeEvt::currCtr(void) const noexcept { - QF_CRIT_STAT_ - - QF_CRIT_E_(); - QTimeEvtCtr const ret = m_ctr; - QF_CRIT_X_(); - - return ret; +//${QF::QTimeEvt::tick} ...................................................... +void QTimeEvt::tick( + std::uint_fast8_t const tickRate, + void const * const sender) noexcept +{ + #ifndef Q_SPY + Q_UNUSED_PAR(sender); + #endif + + QF_CRIT_STAT + QF_CRIT_ENTRY(); + QF_MEM_SYS(); + + Q_REQUIRE_INCRIT(100, tickRate < Q_DIM(timeEvtHead_)); + + QTimeEvt *prev = &timeEvtHead_[tickRate]; + + QS_BEGIN_PRE_(QS_QF_TICK, 0U) + prev->m_ctr = (prev->m_ctr + 1U); + QS_TEC_PRE_(prev->m_ctr); // tick ctr + QS_U8_PRE_(tickRate); // tick rate + QS_END_PRE_() + + // scan the linked-list of time events at this rate... + std::uint_fast8_t limit = 2U*QF_MAX_ACTIVE; // loop hard limit + for (; limit > 0U; --limit) { + QTimeEvt *e = prev->m_next; // advance down the time evt. list + + if (e == nullptr) { // end of the list? + + // any new time events armed since the last run of tick()? + if (timeEvtHead_[tickRate].m_act != nullptr) { + + // sanity check + Q_ASSERT_INCRIT(110, prev != nullptr); + prev->m_next = timeEvtHead_[tickRate].toTimeEvt(); + timeEvtHead_[tickRate].m_act = nullptr; + e = prev->m_next; // switch to the new list + } + else { // all currently armed time events are processed + break; // terminate the for-loop + } + } + + // the time event 'e' must be valid + Q_ASSERT_INCRIT(112, QEvt::verify_(e)); + + if (e->m_ctr == 0U) { // time event scheduled for removal? + prev->m_next = e->m_next; + // mark time event 'e' as NOT linked + e->refCtr_ = static_cast(e->refCtr_ + & static_cast(~TE_IS_LINKED)); + // do NOT advance the prev pointer + QF_MEM_APP(); + QF_CRIT_EXIT(); // exit crit. section to reduce latency + + // NOTE: prevent merging critical sections + // In some QF ports the critical section exit takes effect only + // on the next machine instruction. If the next instruction is + // another entry to a critical section, the critical section + // might not be really exited, but rather the two adjacent + // critical sections would be MERGED. The QF_CRIT_EXIT_NOP() + // macro contains minimal code required to prevent such merging + // of critical sections in QF ports, in which it can occur. + QF_CRIT_EXIT_NOP(); + } + else { + e->m_ctr = (e->m_ctr - 1U); + + if (e->m_ctr == 0U) { // is time evt about to expire? + QActive * const act = e->toActive(); + + if (e->m_interval != 0U) { // periodic time evt? + e->m_ctr = e->m_interval; // rearm the time event + prev = e; // advance to this time event + } + else { // one-shot time event: automatically disarm + prev->m_next = e->m_next; + + // mark time event 'e' as NOT linked + e->refCtr_ = static_cast(e->refCtr_ + & static_cast(~TE_IS_LINKED)); + // do NOT advance the prev pointer + + QS_BEGIN_PRE_(QS_QF_TIMEEVT_AUTO_DISARM, act->m_prio) + QS_OBJ_PRE_(e); // this time event object + QS_OBJ_PRE_(act); // the target AO + QS_U8_PRE_(tickRate); // tick rate + QS_END_PRE_() + } + + QS_BEGIN_PRE_(QS_QF_TIMEEVT_POST, act->m_prio) + QS_TIME_PRE_(); // timestamp + QS_OBJ_PRE_(e); // the time event object + QS_SIG_PRE_(e->sig); // signal of this time event + QS_OBJ_PRE_(act); // the target AO + QS_U8_PRE_(tickRate); // tick rate + QS_END_PRE_() + + #ifdef QXK_HPP_ + if (e->sig < Q_USER_SIG) { + QXThread::timeout_(act); + QF_MEM_APP(); + QF_CRIT_EXIT(); + } + else { + QF_MEM_APP(); + QF_CRIT_EXIT(); // exit crit. section before posting + + // act->POST() asserts if the queue overflows + act->POST(e, sender); + } + #else + QF_MEM_APP(); + QF_CRIT_EXIT(); // exit crit. section before posting + + // act->POST() asserts if the queue overflows + act->POST(e, sender); + #endif + } + else { + prev = e; // advance to this time event + + QF_MEM_APP(); + QF_CRIT_EXIT(); // exit crit. section to reduce latency + + // prevent merging critical sections, see NOTE above + QF_CRIT_EXIT_NOP(); + } + } + QF_CRIT_ENTRY(); // re-enter crit. section to continue the loop + QF_MEM_SYS(); + } + + Q_ENSURE_INCRIT(190, limit > 0U); + QF_MEM_APP(); + QF_CRIT_EXIT(); } -} // namespace QP +//${QF::QTimeEvt::noActive} .................................................. +bool QTimeEvt::noActive(std::uint_fast8_t const tickRate) noexcept { + QF_CRIT_STAT + QF_CRIT_ENTRY(); + Q_REQUIRE_INCRIT(800, tickRate < QF_MAX_TICK_RATE); + QF_CRIT_EXIT(); + bool inactive; + if (timeEvtHead_[tickRate].m_next != nullptr) { + inactive = false; + } + else if (timeEvtHead_[tickRate].m_act != nullptr) { + inactive = false; + } + else { + inactive = true; + } + return inactive; +} + +//${QF::QTimeEvt::QTimeEvt} .................................................. +QTimeEvt::QTimeEvt() noexcept + : + QEvt(0U), + m_next(nullptr), + m_act(nullptr), + m_ctr(0U), + m_interval(0U) +{ + // The refCtr_ attribute is not used in time events, so it is + // reused to hold the tickRate as well as other information + refCtr_ = 0U; // default rate 0 +} + +} // namespace QP +//$enddef${QF::QTimeEvt} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/libraries/qpcpp_arm-cm/src/qmpool.hpp b/libraries/qpcpp_arm-cm/src/qmpool.hpp index f0088af..6e00517 100644 --- a/libraries/qpcpp_arm-cm/src/qmpool.hpp +++ b/libraries/qpcpp_arm-cm/src/qmpool.hpp @@ -1,65 +1,60 @@ -/// @file -/// @brief platform-independent memory pool QP::QMPool interface. -/// @ingroup qf -/// @cond -///*************************************************************************** -/// Last updated for version 6.9.1 -/// Last updated on 2020-09-14 -/// -/// Q u a n t u m L e a P s -/// ------------------------ -/// Modern Embedded Software -/// -/// Copyright (C) 2005-2020 Quantum Leaps. All rights reserved. -/// -/// This program is open source software: you can redistribute it and/or -/// modify it under the terms of the GNU General Public License as published -/// by the Free Software Foundation, either version 3 of the License, or -/// (at your option) any later version. -/// -/// Alternatively, this program may be distributed and modified under the -/// terms of Quantum Leaps commercial licenses, which expressly supersede -/// the GNU General Public License and are specifically designed for -/// licensees interested in retaining the proprietary status of their code. -/// -/// This program is distributed in the hope that it will be useful, -/// but WITHOUT ANY WARRANTY; without even the implied warranty of -/// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -/// GNU General Public License for more details. -/// -/// You should have received a copy of the GNU General Public License -/// along with this program. If not, see . -/// -/// Contact information: -/// -/// -///*************************************************************************** -/// @endcond - -#ifndef QMPOOL_HPP -#define QMPOOL_HPP +//$file${include::qmpool.hpp} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv +// +// Model: qpcpp.qm +// File: ${include::qmpool.hpp} +// +// This code has been generated by QM 6.1.1 . +// DO NOT EDIT THIS FILE MANUALLY. All your changes will be lost. +// +// This code is covered by the following QP license: +// License # : LicenseRef-QL-dual +// Issued to : Any user of the QP/C++ real-time embedded framework +// Framework(s) : qpcpp +// Support ends : 2024-12-31 +// License scope: +// +// Copyright (C) 2005 Quantum Leaps, LLC . +// +// Q u a n t u m L e a P s +// ------------------------ +// Modern Embedded Software +// +// SPDX-License-Identifier: GPL-3.0-or-later OR LicenseRef-QL-commercial +// +// This software is dual-licensed under the terms of the open source GNU +// General Public License version 3 (or any later version), or alternatively, +// under the terms of one of the closed source Quantum Leaps commercial +// licenses. +// +// The terms of the open source GNU General Public License version 3 +// can be found at: +// +// The terms of the closed source Quantum Leaps commercial licenses +// can be found at: +// +// Redistributions in source code must retain this top-level comment block. +// Plagiarizing this software to sidestep the license obligations is illegal. +// +// Contact information: +// +// +// +//$endhead${include::qmpool.hpp} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +#ifndef QMPOOL_HPP_ +#define QMPOOL_HPP_ #ifndef QF_MPOOL_SIZ_SIZE - //! macro to override the default QP::QMPoolSize size. - /// Valid values 1U, 2U, or 4U; default 2U #define QF_MPOOL_SIZ_SIZE 2U #endif - #ifndef QF_MPOOL_CTR_SIZE - //! macro to override the default QMPoolCtr size. - //! Valid values 1U, 2U, or 4U; default 2U - #define QF_MPOOL_CTR_SIZE 2 + #define QF_MPOOL_CTR_SIZE 2U #endif namespace QP { + #if (QF_MPOOL_SIZ_SIZE == 1U) using QMPoolSize = std::uint8_t; #elif (QF_MPOOL_SIZ_SIZE == 2U) - //! The data type to store the block-size based on the macro - //! #QF_MPOOL_SIZ_SIZE. - /// @description - /// The dynamic range of this data type determines the maximum size - /// of blocks that can be managed by the native QF event pool. using QMPoolSize = std::uint16_t; #elif (QF_MPOOL_SIZ_SIZE == 4U) using QMPoolSize = std::uint32_t; @@ -70,11 +65,6 @@ namespace QP { #if (QF_MPOOL_CTR_SIZE == 1U) using QMPoolCtr = std::uint8_t; #elif (QF_MPOOL_CTR_SIZE == 2U) - //! The data type to store the block-counter based on the macro - //! #QF_MPOOL_CTR_SIZE. - /// @description - /// The dynamic range of this data type determines the maximum number - /// of blocks that can be stored in the pool. using QMPoolCtr = std::uint16_t; #elif (QF_MPOOL_CTR_SIZE == 4U) using QMPoolCtr = std::uint32_t; @@ -82,99 +72,94 @@ namespace QP { #error "QF_MPOOL_CTR_SIZE defined incorrectly, expected 1U, 2U, or 4U" #endif -//**************************************************************************** -//! Native QF memory pool class -/// @description -/// A fixed block-size memory pool is a very fast and efficient data -/// structure for dynamic allocation of fixed block-size chunks of memory. -/// A memory pool offers fast and deterministic allocation and recycling of -/// memory blocks and is not subject to fragmenation.@n -/// @n -/// The QP::QMPool class describes the native QF memory pool, which can be -/// used as the event pool for dynamic event allocation, or as a fast, -/// deterministic fixed block-size heap for any other objects in your -/// application. -/// -/// @note -/// The QP::QMPool class contains only data members for managing a memory -/// pool, but does not contain the pool storage, which must be provided -/// externally during the pool initialization. -/// -/// @note -/// The native QF event pool is configured by defining the macro -/// #QF_EPOOL_TYPE_ as QP::QMPool in the specific QF port header file. -class QMPool { +} // namespace QP + +#define QF_MPOOL_EL(evType_) struct { \ + QP::QFreeBlock sto_[((sizeof(evType_) - 1U) \ + / sizeof(QP::QFreeBlock)) + 1U]; } +//$declare${QF::QFreeBlock} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv +namespace QP { + +//${QF::QFreeBlock} .......................................................... +class QFreeBlock { private: + QFreeBlock * m_next; - //! start of the memory managed by this memory pool - void *m_start; +#ifndef Q_UNSAFE + std::uintptr_t m_next_dis; +#endif // ndef Q_UNSAFE + friend class QMPool; +}; // class QFreeBlock - //! end of the memory managed by this memory pool - void *m_end; +} // namespace QP +//$enddecl${QF::QFreeBlock} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - //! head of linked list of free blocks - void * volatile m_free_head; +//$declare${QF::QMPool} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv +namespace QP { - //! maximum block size (in bytes) +//${QF::QMPool} .............................................................. +class QMPool { +private: + QFreeBlock * m_start; + QFreeBlock * m_end; + QFreeBlock * volatile m_free_head; QMPoolSize m_blockSize; - - //! total number of blocks QMPoolCtr m_nTot; - - //! number of free blocks remaining QMPoolCtr volatile m_nFree; - - //! minimum number of free blocks ever present in this pool - /// @note - /// This attribute remembers the low watermark of the pool, - /// which provides a valuable information for sizing event pools. - /// - /// @sa QP::QF::getPoolMin(). QMPoolCtr m_nMin; public: - QMPool(void); //!< public default constructor - - //! Initializes the native QF event pool - void init(void * const poolSto, std::uint_fast32_t poolSize, - std::uint_fast16_t blockSize) noexcept; - - //! Obtains a memory block from a memory pool. - void *get(std::uint_fast16_t const margin, - std::uint_fast8_t const qs_id) noexcept; - - //! Returns a memory block back to a memory pool. - void put(void * const b, std::uint_fast8_t const qs_id) noexcept; - - //! return the fixed block-size of the blocks managed by this pool - QMPoolSize getBlockSize(void) const noexcept { - return m_blockSize; + QMPool() + : m_start(nullptr), + m_end(nullptr), + m_free_head(nullptr), + m_blockSize(0U), + m_nTot(0U), + m_nFree(0U), + m_nMin(0U) + {} + +#ifdef Q_XTOR + ~QMPool(); +#endif // def Q_XTOR + void init( + void * const poolSto, + std::uint_fast32_t const poolSize, + std::uint_fast16_t const blockSize) noexcept; + void * get( + std::uint_fast16_t const margin, + std::uint_fast8_t const qsId) noexcept; + void put( + void * const block, + std::uint_fast8_t const qsId) noexcept; + QMPoolSize getBlockSize() const noexcept; + QMPoolCtr getNMin() const noexcept { + return m_nMin; + } + QMPoolCtr getNFree() const noexcept { + return m_nFree; } - -// duplicated API to be used exclusively inside ISRs (useful in some QP ports) -#ifdef QF_ISR_API - void *getFromISR(std::uint_fast16_t const margin, - std::uint_fast8_t const qs_id) noexcept; - void putFromISR(void * const b, - std::uint_fast8_t const qs_id) noexcept; -#endif // QF_ISR_API private: - //! disallow copying of QMPools - QMPool(QMPool const &) = delete; + QMPool(QEQueue const & other) = delete; + QMPool & operator=(QMPool const & other) = delete; - //!< disallow assigning of QMPools - QMPool &operator=(QMPool const &) = delete; - - friend class QF; - friend class QS; -}; +public: -} // namespace QP +#ifdef QF_ISR_API + void * getFromISR( + std::uint_fast16_t const margin, + std::uint_fast8_t const qsId) noexcept; +#endif // def QF_ISR_API -//! Memory pool element to allocate correctly aligned storage for QP::QMPool -#define QF_MPOOL_EL(type_) \ - struct { void *sto_[((sizeof(type_) - 1U)/sizeof(void*)) + 1U]; } +#ifdef QF_ISR_API + void putFromISR( + void * const b, + std::uint_fast8_t const qsId) noexcept; +#endif // def QF_ISR_API +}; // class QMPool -#endif // QMPOOL_HPP +} // namespace QP +//$enddecl${QF::QMPool} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +#endif // QMPOOL_HPP_ diff --git a/libraries/qpcpp_arm-cm/src/qpcpp.hpp b/libraries/qpcpp_arm-cm/src/qpcpp.hpp index 47671e6..2319bbf 100644 --- a/libraries/qpcpp_arm-cm/src/qpcpp.hpp +++ b/libraries/qpcpp_arm-cm/src/qpcpp.hpp @@ -1,79 +1,120 @@ -/// @file -/// @brief QP/C++ public interface including backwards-compatibility layer -/// @ingroup qep qf qv qk qxk qs -/// @cond -///*************************************************************************** -/// Last updated for version 6.9.1 -/// Last updated on 2020-09-30 -/// -/// Q u a n t u m L e a P s -/// ------------------------ -/// Modern Embedded Software -/// -/// Copyright (C) 2005-2020 Quantum Leaps. All rights reserved. -/// -/// This program is open source software: you can redistribute it and/or -/// modify it under the terms of the GNU General Public License as published -/// by the Free Software Foundation, either version 3 of the License, or -/// (at your option) any later version. -/// -/// Alternatively, this program may be distributed and modified under the -/// terms of Quantum Leaps commercial licenses, which expressly supersede -/// the GNU General Public License and are specifically designed for -/// licensees interested in retaining the proprietary status of their code. -/// -/// This program is distributed in the hope that it will be useful, -/// but WITHOUT ANY WARRANTY; without even the implied warranty of -/// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -/// GNU General Public License for more details. -/// -/// You should have received a copy of the GNU General Public License -/// along with this program. If not, see . -/// -/// Contact information: -/// -/// -///*************************************************************************** -/// @endcond - -#ifndef qpcpp_h -#define qpcpp_h - -/// @description -/// This header file must be included directly or indirectly -/// in all application modules (*.cpp files) that use QP/C++. - -//**************************************************************************** -#include "qf_port.hpp" // QF/C++ port from the port directory -#include "qassert.h" // QP assertions +//$file${include::qpcpp.hpp} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv +// +// Model: qpcpp.qm +// File: ${include::qpcpp.hpp} +// +// This code has been generated by QM 6.1.1 . +// DO NOT EDIT THIS FILE MANUALLY. All your changes will be lost. +// +// This code is covered by the following QP license: +// License # : LicenseRef-QL-dual +// Issued to : Any user of the QP/C++ real-time embedded framework +// Framework(s) : qpcpp +// Support ends : 2024-12-31 +// License scope: +// +// Copyright (C) 2005 Quantum Leaps, LLC . +// +// Q u a n t u m L e a P s +// ------------------------ +// Modern Embedded Software +// +// SPDX-License-Identifier: GPL-3.0-or-later OR LicenseRef-QL-commercial +// +// This software is dual-licensed under the terms of the open source GNU +// General Public License version 3 (or any later version), or alternatively, +// under the terms of one of the closed source Quantum Leaps commercial +// licenses. +// +// The terms of the open source GNU General Public License version 3 +// can be found at: +// +// The terms of the closed source Quantum Leaps commercial licenses +// can be found at: +// +// Redistributions in source code must retain this top-level comment block. +// Plagiarizing this software to sidestep the license obligations is illegal. +// +// Contact information: +// +// +// +//$endhead${include::qpcpp.hpp} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +#ifndef QPCPP_HPP_ +#define QPCPP_HPP_ + +//============================================================================ +#include "qp_port.hpp" // QP port from the port directory +#include "qsafe.h" // QP Functional Safety (FuSa) Subsystem #ifdef Q_SPY // software tracing enabled? #include "qs_port.hpp" // QS/C++ port from the port directory #else #include "qs_dummy.hpp" // QS/C++ dummy (inactive) interface #endif -//**************************************************************************** +//============================================================================ #ifndef QP_API_VERSION -//! Macro that specifies the backwards compatibility with the -//! QP/C++ API version. -/// @description -/// For example, QP_API_VERSION=540 will cause generating the compatibility -/// layer with QP/C++ version 5.4.0 and newer, but not older than 5.4.0. -/// QP_API_VERSION=0 causes generation of the compatibility layer "from the -/// begining of time", which is the maximum backwards compatibilty. This is -/// the default.@n -/// @n -/// Conversely, QP_API_VERSION=9999 means that no compatibility layer should -/// be generated. This setting is useful for checking if an application -/// complies with the latest QP/C++ API. #define QP_API_VERSION 0 #endif // QP_API_VERSION +//============================================================================ // QP/C++ API compatibility layer... -//**************************************************************************** +#if (QP_API_VERSION < 730) + +//! @deprecated plain 'char' is no longer forbidden in MISRA/AUTOSAR-C++ +using char_t = char; + +//! @deprecated assertion failure handler +//! Use Q_onError() instead. +#define Q_onAssert(module_, id_) Q_onError(module_, id_) + +//! @deprecated #Q_NASSERT preprocessor switch to disable QP assertions +#ifdef Q_NASSERT + + // #Q_UNSAFE now replaces the functionality of Q_NASSERT + #define Q_UNSAFE + + //! @deprecated general purpose assertion with user-specified ID + //! number that **always** evaluates the `expr_` expression. + #define Q_ALLEGE_ID(id_, expr_) ((void)(expr_)) + +#else // QP FuSa Subsystem enabled + + //! @deprecated general purpose assertion with user-specified ID + //! number that **always** evaluates the `expr_` expression. + //! @note + //! The use of this macro is no longer recommended. + #define Q_ALLEGE_ID(id_, expr_) if (!(expr_)) { \ + QF_CRIT_STAT \ + QF_CRIT_ENTRY(); \ + Q_onError(&Q_this_module_[0], (id_)); \ + QF_CRIT_EXIT(); \ + } else ((void)0) + +#endif + +//! @deprecated general purpose assertion without ID number +//! that **always** evaluates the `expr_` expression. +//! Instead of ID number, this macro is based on the standard +//! `__LINE__` macro. +//! +//! @note The use of this macro is no longer recommended. +#define Q_ALLEGE(expr_) Q_ALLEGE_ID(__LINE__, (expr_)) + +//! Static (compile-time) assertion. +//! +//! @deprecated +//! Use Q_ASSERT_STATIC() or better yet `static_assert()` instead. +//! +#define Q_ASSERT_COMPILE(expr_) Q_ASSERT_STATIC(expr_) + +//! @deprecated use QP::QF::NO_MARGIN instead +#define QF_NO_MARGIN QP::QF::NO_MARGIN + +//============================================================================ #if (QP_API_VERSION < 691) //! @deprecated enable the QS global filter @@ -99,115 +140,112 @@ #ifdef Q_SPY -//! @deprecated local Filter for a generic application object @p obj_. +//! @deprecated local Filter for a generic application object `obj_`. #define QS_FILTER_AP_OBJ(obj_) \ - (QP::QS::priv_.locFilter_AP = (obj_)) + (QP::QS::filt_.loc_AP = (obj_)) //! @deprecated begin of a user QS record, instead use QS_BEGIN_ID() -#define QS_BEGIN(rec_, obj_) \ - if (QS_GLB_FILTER_(rec_) && \ - ((QP::QS::priv_.locFilter[QP::QS::AP_OBJ] == nullptr) \ - || (QP::QS::priv_.locFilter_AP == (obj_)))) \ - { \ - QS_CRIT_STAT_ \ - QS_CRIT_E_(); \ +#define QS_BEGIN(rec_, obj_) \ + if (QS_GLB_FILTER_(rec_) && \ + ((QP::QS::filt_.loc[QP::QS::AP_OBJ] == nullptr) \ + || (QP::QS::filt_.loc_AP == (obj_)))) \ + { \ + QS_CRIT_STAT \ + QS_CRIT_ENTRY(); \ QP::QS::beginRec_(static_cast(rec_)); \ QS_TIME_PRE_(); //! @deprecated output hex-formatted std::uint32_t to the QS record -#define QS_U32_HEX(width_, data_) \ - (QP::QS::u32_fmt_(static_cast( \ - (static_cast((width_) << 4)) \ - | static_cast(0xFU)), (data_))) - - +#define QS_U32_HEX(width_, data_) \ + (QP::QS::u32_fmt_(static_cast( \ + (static_cast((width_) << 4)) | QS_HEX_FMT), (data_))) #else #define QS_FILTER_AP_OBJ(obj_) (static_cast(0)) #define QS_BEGIN(rec_, obj_) if (false) { -#define QS_U32_HEX(width_, data_) (static_cast(0)) +#define QS_U32_HEX(width_, data_) (Q_UNUSED_PAR(0)) -#endif +#endif // def Q_SPY -//**************************************************************************** +//============================================================================ #if (QP_API_VERSION < 680) //! @deprecated -//! Macro to specify a transition in the "me->" impl-strategy. -/// Instead use the new impl-strategy without the "me->" pointer, where -/// you call tran(Q_STATE_CAST(target_)). +//! Macro to specify a tran. in the "me->" impl-strategy. +//! Instead use the new impl-strategy without the "me->" pointer, where +//! you call tran(Q_STATE_CAST(target_)). #define Q_TRAN(target_) (me->tran(Q_STATE_CAST(target_))) //! @deprecated //! Macro to specify a tran-to-history in the "me->" impl-strategy. -/// Instead use the new impl-strategy without the "me->" pointer, where -/// you call tran_hist(Q_STATE_CAST(hist_)). +//! Instead use the new impl-strategy without the "me->" pointer, where +//! you call tran_hist(Q_STATE_CAST(hist_)). #define Q_TRAN_HIST(hist_) (me->tran_hist((hist_))) //! @deprecated //! Macro to specify the superstate in the "me->" impl-strategy. -/// Instead use the new impl-strategy without the "me->" pointer, where -/// you call super(state_)). -#define Q_SUPER(state_) (me->super((state_))) +//! Instead use the new impl-strategy without the "me->" pointer, where +//! you call super(state_)). +#define Q_SUPER(state_) (me->super(Q_STATE_CAST(state_))) //! @deprecated //! Macro to call in a QM state entry-handler. Applicable only to QMSMs. -/// Instead use the new impl-strategy without the "me->" pointer, where -/// the QM-generated code calls qm_entry(Q_STATE_CAST(state_)). +//! Instead use the new impl-strategy without the "me->" pointer, where +//! the QM-generated code calls qm_entry(Q_STATE_CAST(state_)). #define QM_ENTRY(state_) (me->qm_entry((state_))) //! @deprecated //! Macro to call in a QM state exit-handler. Applicable only to QMSMs. -/// Instead use the new impl-strategy without the "me->" pointer, where -/// the QM-generated code calls qm_exit(Q_STATE_CAST(state_)). +//! Instead use the new impl-strategy without the "me->" pointer, where +//! the QM-generated code calls qm_exit(Q_STATE_CAST(state_)). #define QM_EXIT(state_) (me->qm_exit((state_))) //! @deprecated //! Macro to call in a QM submachine exit-handler. Applicable only to QMSMs. -/// Instead use the new impl-strategy without the "me->" pointer, where -/// the QM-generated code calls qm_sm_exit(Q_STATE_CAST(state_)). +//! Instead use the new impl-strategy without the "me->" pointer, where +//! the QM-generated code calls qm_sm_exit(Q_STATE_CAST(state_)). #define QM_SM_EXIT(state_) (me->qm_sm_exit((state_))) //! @deprecated -//! Macro to call in a QM state-handler when it executes a transition. -/// Instead use the new impl-strategy without the "me->" pointer, where -/// the QM-generated code calls qm_tran((tatbl_)). +//! Macro to call in a QM state-handler when it executes a tran. +//! Instead use the new impl-strategy without the "me->" pointer, where +//! the QM-generated code calls qm_tran((tatbl_)). #define QM_TRAN(tatbl_) (me->qm_tran((tatbl_))) //! @deprecated //! Macro to call in a QM state-handler when it executes an initial tran. -/// Instead use the new impl-strategy without the "me->" pointer, where -/// the QM-generated code calls qm_tran_init((tatbl_)). +//! Instead use the new impl-strategy without the "me->" pointer, where +//! the QM-generated code calls qm_tran_init((tatbl_)). #define QM_TRAN_INIT(tatbl_) (me->qm_tran_init((tatbl_))) //! @deprecated //! Macro to call in a QM state-handler when it executes a tran-to-history. -/// Instead use the new impl-strategy without the "me->" pointer, where -/// the QM-generated code calls qm_tran_hist((history_), (tatbl_)). +//! Instead use the new impl-strategy without the "me->" pointer, where +//! the QM-generated code calls qm_tran_hist((history_), (tatbl_)). #define QM_TRAN_HIST(history_, tatbl_) \ (me->qm_tran_hist((history_), (tatbl_))) //! @deprecated //! Macro to call in a QM state-handler when it executes an initial tran. -/// Instead use the new impl-strategy without the "me->" pointer, where -/// the QM-generated code calls qm_tran_ep((tatbl_)). +//! Instead use the new impl-strategy without the "me->" pointer, where +//! the QM-generated code calls qm_tran_ep((tatbl_)). #define QM_TRAN_EP(tatbl_) (me->qm_tran_ep((tatbl_))) //! @deprecated //! Macro to call in a QM state-handler when it executes a tran-to-exit-point. -/// Instead use the new impl-strategy without the "me->" pointer, where -/// the QM-generated code calls qm_tran_xp((xp_), (tatbl_)). +//! Instead use the new impl-strategy without the "me->" pointer, where +//! the QM-generated code calls qm_tran_xp((xp_), (tatbl_)). #define QM_TRAN_XP(xp_, tatbl_) (me->qm_tran_xp((xp_), (tatbl_))) //! @deprecated //! Designates the superstate of a given state in a subclass of QP::QMsm. -/// Instead use the new impl-strategy without the "me->" pointer, where -/// the QM-generated code calls qm_super_sub((state_)). +//! Instead use the new impl-strategy without the "me->" pointer, where +//! the QM-generated code calls qm_super_sub((state_)). #define QM_SUPER_SUB(state_) (me->qm_super_sub((state_))) #endif // QP_API_VERSION < 680 #endif // QP_API_VERSION < 691 +#endif // QP_API_VERSION < 700 -#endif // qpcpp_h - +#endif // QPCPP_HPP_ diff --git a/libraries/qpcpp_arm-cm/src/qpset.hpp b/libraries/qpcpp_arm-cm/src/qpset.hpp deleted file mode 100644 index 67e0963..0000000 --- a/libraries/qpcpp_arm-cm/src/qpset.hpp +++ /dev/null @@ -1,197 +0,0 @@ -/// @file -/// @brief platform-independent priority sets of 8 or 64 elements. -/// @ingroup qf -/// @cond -///*************************************************************************** -/// Last updated for version 6.8.2 -/// Last updated on 2020-07-18 -/// -/// Q u a n t u m L e a P s -/// ------------------------ -/// Modern Embedded Software -/// -/// Copyright (C) 2005-2019 Quantum Leaps. All rights reserved. -/// -/// This program is open source software: you can redistribute it and/or -/// modify it under the terms of the GNU General Public License as published -/// by the Free Software Foundation, either version 3 of the License, or -/// (at your option) any later version. -/// -/// Alternatively, this program may be distributed and modified under the -/// terms of Quantum Leaps commercial licenses, which expressly supersede -/// the GNU General Public License and are specifically designed for -/// licensees interested in retaining the proprietary status of their code. -/// -/// This program is distributed in the hope that it will be useful, -/// but WITHOUT ANY WARRANTY; without even the implied warranty of -/// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -/// GNU General Public License for more details. -/// -/// You should have received a copy of the GNU General Public License -/// along with this program. If not, see . -/// -/// Contact information: -/// -/// -///*************************************************************************** -/// @endcond - -#ifndef QPSET_HPP -#define QPSET_HPP - -#ifndef QF_MAX_ACTIVE - // default value when NOT defined - #define QF_MAX_ACTIVE 32U -#endif - -namespace QP { - -#if (QF_MAX_ACTIVE < 1U) || (64U < QF_MAX_ACTIVE) - #error "QF_MAX_ACTIVE out of range. Valid range is 1U..64U" -#elif (QF_MAX_ACTIVE <= 8U) - using QPSetBits = std::uint8_t; -#elif (QF_MAX_ACTIVE <= 16U) - using QPSetBits = std::uint16_t; -#else - //! bitmask for the internal representation of QPSet elements - using QPSetBits = std::uint32_t; -#endif - -//**************************************************************************** -// Log-base-2 calculations ... -#ifndef QF_LOG2 - extern "C" std::uint_fast8_t QF_LOG2(QPSetBits x) noexcept; -#endif // QF_LOG2 - -//**************************************************************************** -#if (QF_MAX_ACTIVE <= 32) -//! Priority Set of up to 32 elements */ -/// -/// The priority set represents the set of active objects that are ready to -/// run and need to be considered by the scheduling algorithm. The set is -/// capable of storing up to 32 priority levels. QP::QPSet is specifically -/// declared as a POD (Plain Old Data) for ease of initialization and -/// interfacing with plain "C" code. -/// -struct QPSet { - - QPSetBits volatile m_bits; //!< bitmask with a bit for each element - - //! Makes the priority set @p me_ empty. - void setEmpty(void) noexcept { - m_bits = 0U; - } - - //! Evaluates to true if the priority set is empty - bool isEmpty(void) const noexcept { - return (m_bits == 0U); - } - - //! Evaluates to true if the priority set is not empty - bool notEmpty(void) const noexcept { - return (m_bits != 0U); - } - - //! the function evaluates to TRUE if the priority set has the element n. - bool hasElement(std::uint_fast8_t const n) const noexcept { - return (m_bits & (1U << (n - 1U))) != 0U; - } - - //! insert element @p n into the set, n = 1..QF_MAX_ACTIVE - void insert(std::uint_fast8_t const n) noexcept { - m_bits |= (1U << (n - 1U)); - } - - //! remove element @p n from the set, n = 1..QF_MAX_ACTIVE - /// @note - /// intentionally misspelled ("rmove") to avoid collision with - /// the C++ standard library facility "remove" - void rmove(std::uint_fast8_t const n) noexcept { - m_bits &= - static_cast(~(static_cast(1) << (n - 1U))); - } - - std::uint_fast8_t findMax(void) const noexcept { - return QF_LOG2(m_bits); - } -}; - -#else // QF_MAX_ACTIVE > 32U - -//! Priority Set of up to 64 elements -/// -/// The priority set represents the set of active objects that are ready to -/// run and need to be considered by the scheduling algorithm. The set is -/// capable of storing up to 64 priority levels. QP::QPSet is specifically -/// declared as a POD (Plain Old Data) for ease of initialization and -/// interfacing with plain "C" code. -/// -struct QPSet { - - //! Two 32-bit bitmasks with a bit for each element - std::uint32_t volatile m_bits[2]; - - //! Makes the priority set @p me_ empty. - void setEmpty(void) noexcept { - m_bits[0] = 0U; - m_bits[1] = 0U; - } - - //! Evaluates to true if the priority set is empty - // the following logic avoids UB in volatile access for MISRA compliantce - bool isEmpty(void) const noexcept { - return (m_bits[0] == 0U) ? (m_bits[1] == 0U) : false; - } - - //! Evaluates to true if the priority set is not empty - // the following logic avoids UB in volatile access for MISRA compliantce - bool notEmpty(void) const noexcept { - return (m_bits[0] != 0U) ? true : (m_bits[1] != 0U); - } - - //! the function evaluates to TRUE if the priority set has the element n. - bool hasElement(std::uint_fast8_t const n) const noexcept { - return (n <= 32U) - ? ((m_bits[0] & (static_cast(1) << (n - 1U))) - != 0U) - : ((m_bits[1] & (static_cast(1) << (n - 33U))) - != 0U); - } - - //! insert element @p n into the set, n = 1..64 - void insert(std::uint_fast8_t const n) noexcept { - if (n <= 32U) { - m_bits[0] |= (static_cast(1) << (n - 1U)); - } - else { - m_bits[1] |= (static_cast(1) << (n - 33U)); - } - } - - //! remove element @p n from the set, n = 1..64 - /// @note - /// intentionally misspelled ("rmove") to avoid collision with - /// the C++ standard library facility "remove" - void rmove(std::uint_fast8_t const n) noexcept { - if (n <= 32U) { - (m_bits[0] &= ~(static_cast(1) << (n - 1U))); - } - else { - (m_bits[1] &= ~(static_cast(1) << (n - 33U))); - } - } - - //! find the maximum element in the set, returns zero if the set is empty - std::uint_fast8_t findMax(void) const noexcept { - return (m_bits[1] != 0U) - ? (QF_LOG2(m_bits[1]) + 32U) - : (QF_LOG2(m_bits[0])); - } -}; - -#endif // QF_MAX_ACTIVE - -} // namespace QP - -#endif // QPSET_HPP - diff --git a/libraries/qpcpp_arm-cm/src/qs.cpp b/libraries/qpcpp_arm-cm/src/qs.cpp index 4dd54ef..e783cbf 100644 --- a/libraries/qpcpp_arm-cm/src/qs.cpp +++ b/libraries/qpcpp_arm-cm/src/qs.cpp @@ -1,87 +1,72 @@ -/// @file -/// @brief QS software tracing services -/// @ingroup qs -/// @cond -///*************************************************************************** -/// Last updated for version 6.9.1 -/// Last updated on 2020-09-19 -/// -/// Q u a n t u m L e a P s -/// ------------------------ -/// Modern Embedded Software -/// -/// Copyright (C) 2005-2020 Quantum Leaps. All rights reserved. -/// -/// This program is open source software: you can redistribute it and/or -/// modify it under the terms of the GNU General Public License as published -/// by the Free Software Foundation, either version 3 of the License, or -/// (at your option) any later version. -/// -/// Alternatively, this program may be distributed and modified under the -/// terms of Quantum Leaps commercial licenses, which expressly supersede -/// the GNU General Public License and are specifically designed for -/// licensees interested in retaining the proprietary status of their code. -/// -/// This program is distributed in the hope that it will be useful, -/// but WITHOUT ANY WARRANTY; without even the implied warranty of -/// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -/// GNU General Public License for more details. -/// -/// You should have received a copy of the GNU General Public License -/// along with this program. If not, see . -/// -/// Contact information: -/// -/// -///*************************************************************************** -/// @endcond - -#define QP_IMPL // this is QP implementation -#include "qs_port.hpp" // QS port -#include "qs_pkg.hpp" // QS package-scope internal interface -#include "qstamp.hpp" // QP time-stamp -#include "qassert.h" // QP assertions - -namespace QP { - +//$file${src::qs::qs.cpp} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv +// +// Model: qpcpp.qm +// File: ${src::qs::qs.cpp} +// +// This code has been generated by QM 6.1.1 . +// DO NOT EDIT THIS FILE MANUALLY. All your changes will be lost. +// +// This code is covered by the following QP license: +// License # : LicenseRef-QL-dual +// Issued to : Any user of the QP/C++ real-time embedded framework +// Framework(s) : qpcpp +// Support ends : 2024-12-31 +// License scope: +// +// Copyright (C) 2005 Quantum Leaps, LLC . +// +// Q u a n t u m L e a P s +// ------------------------ +// Modern Embedded Software +// +// SPDX-License-Identifier: GPL-3.0-or-later OR LicenseRef-QL-commercial +// +// This software is dual-licensed under the terms of the open source GNU +// General Public License version 3 (or any later version), or alternatively, +// under the terms of one of the closed source Quantum Leaps commercial +// licenses. +// +// The terms of the open source GNU General Public License version 3 +// can be found at: +// +// The terms of the closed source Quantum Leaps commercial licenses +// can be found at: +// +// Redistributions in source code must retain this top-level comment block. +// Plagiarizing this software to sidestep the license obligations is illegal. +// +// Contact information: +// +// +// +//$endhead${src::qs::qs.cpp} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +#define QP_IMPL // this is QP implementation +#include "qs_port.hpp" // QS port +#include "qs_pkg.hpp" // QS package-scope internal interface +#include "qstamp.hpp" // QP time-stamp +#include "qsafe.h" // QP Functional Safety (FuSa) Subsystem + +// unnamed namespace for local definitions with internal linkage +namespace { Q_DEFINE_THIS_MODULE("qs") +} // unnamed namespace -//**************************************************************************** -QS QS::priv_; // QS private data - -//**************************************************************************** -/// @description -/// This function should be called from QP::QS::onStartup() 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. -/// -/// @note QS can work with quite small data buffers, but you will start losing -/// data if the buffer is too small for the bursts of tracing activity. -/// The right size of the buffer depends on the data production rate and -/// the data output rate. QS offers flexible filtering to reduce the data -/// production rate. -/// -/// @note If the data output rate cannot keep up with the production rate, -/// QS will start overwriting the older data with newer data. This is -/// consistent with the "last-is-best" QS policy. The record sequence counters -/// and check sums on each record allow the QSPY host uitiliy to easily detect -/// any data loss. -/// -void QS::initBuf(std::uint8_t * const sto, - std::uint_fast16_t const stoSize) noexcept -{ - // the provided buffer must be at least 8 bytes long - Q_REQUIRE_ID(100, stoSize > 8U); +//$skip${QP_VERSION} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv +// Check for the minimum required QP version +#if (QP_VERSION < 730U) || (QP_VERSION != ((QP_RELEASE^4294967295U) % 0x3E8U)) +#error qpcpp version 7.3.0 or higher required +#endif +//$endskip${QP_VERSION} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - // This function initializes all the internal QS variables, so that the - // tracing can start correctly even if the startup code fails to clear - // any uninitialized data (as is required by the C Standard). - // - glbFilter_(-QS_ALL_RECORDS); // all global filters OFF - locFilter_(QS_ALL_IDS); // all local filters ON - priv_.locFilter_AP = nullptr; // deprecated "AP-filter" +//$define${QS::QS-TX} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv +namespace QP { +namespace QS { +//${QS::QS-TX::initBuf} ...................................................... +void initBuf( + std::uint8_t * const sto, + std::uint_fast32_t const stoSize) noexcept +{ priv_.buf = sto; priv_.end = static_cast(stoSize); priv_.head = 0U; @@ -91,34 +76,108 @@ void QS::initBuf(std::uint8_t * const sto, priv_.chksum = 0U; priv_.critNest = 0U; + glbFilter_(-static_cast(QS_ALL_RECORDS));// all global filters OFF + locFilter_(static_cast(QS_ALL_IDS)); // all local filters ON + priv_.locFilter_AP = nullptr; // deprecated "AP-filter" + // produce an empty record to "flush" the QS trace buffer beginRec_(QS_REC_NUM_(QS_EMPTY)); endRec_(); - // produce the Target info QS record - QS_target_info_(0xFFU); + // produce the reset record to inform QSPY of a new session + target_info_pre_(0xFFU); + + // hold off flushing after successful initialization (see QS_INIT()) +} + +//${QS::QS-TX::getByte} ...................................................... +std::uint16_t getByte() noexcept { + // NOTE: Must be called IN critical section. + // Also requires system-level memory access (QF_MEM_SYS()). + + std::uint16_t ret; + if (priv_.used == 0U) { + ret = QS_EOD; // set End-Of-Data + } + else { + std::uint8_t const * const buf = priv_.buf; // put in a temporary + QSCtr tail = priv_.tail; // put in a temporary (register) + ret = static_cast(buf[tail]); // set the byte to return + ++tail; // advance the tail + if (tail == priv_.end) { // tail wrap around? + tail = 0U; + } + priv_.tail = tail; // update the tail + priv_.used = (priv_.used - 1U); // one less byte used + } + return ret; // return the byte or EOD +} + +//${QS::QS-TX::getBlock} ..................................................... +std::uint8_t const * getBlock(std::uint16_t * const pNbytes) noexcept { + // NOTE: Must be called IN critical section. + // Also requires system-level memory access (QF_MEM_SYS()). + + QSCtr const used = priv_.used; // put in a temporary (register) + std::uint8_t *buf; + + // any bytes used in the ring buffer? + if (used != 0U) { + QSCtr tail = priv_.tail; // put in a temporary (register) + QSCtr const end = priv_.end; // put in a temporary (register) + QSCtr n = static_cast(end - tail); + if (n > used) { + n = used; + } + if (n > static_cast(*pNbytes)) { + n = static_cast(*pNbytes); + } + *pNbytes = static_cast(n); // n-bytes available + buf = priv_.buf; + buf = &buf[tail]; // the bytes are at the tail - // wait with flushing after successfull initialization (see QS_INIT()) + priv_.used = static_cast(used - n); + tail += n; + if (tail == end) { + tail = 0U; + } + priv_.tail = tail; + } + else { // no bytes available + *pNbytes = 0U; // no bytes available right now + buf = nullptr; // no bytes available right now + } + return buf; } -//**************************************************************************** -/// @description -/// This function sets up the QS filter to enable the record type @a filter. -/// The argument #QS_ALL_RECORDS specifies to filter-in all records. -/// This function should be called indirectly through the macro -/// QS_GLB_FILTER() -/// -/// @param[in] filter the QS record-d or group to enable in the filter, -/// if positive or disable, if negative. The record-id -/// numbers must be in the range -127..127. -/// @note -/// Filtering based on the record-type is only the first layer of filtering. -/// The second layer is based on the object-type. Both filter layers must -/// be enabled for the QS record to be inserted in the QS buffer. -/// -/// @sa QP::QS::locFilter_() -/// -void QS::glbFilter_(std::int_fast16_t const filter) noexcept { +} // namespace QS +} // namespace QP +//$enddef${QS::QS-TX} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +#ifndef QF_MEM_ISOLATE +//$define${QS::filters} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv +namespace QP { +namespace QS { + +//${QS::filters::filt_} ...................................................... +Filter filt_; + +} // namespace QS +} // namespace QP +//$enddef${QS::filters} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +#endif + +//============================================================================ +//! @cond INTERNAL + +namespace QP { +namespace QS { + +//............................................................................ +Attr priv_; + +//............................................................................ +void glbFilter_(std::int_fast16_t const filter) noexcept { bool const isRemove = (filter < 0); std::uint16_t const rec = isRemove ? static_cast(-filter) @@ -128,319 +187,291 @@ void QS::glbFilter_(std::int_fast16_t const filter) noexcept { std::uint8_t const tmp = (isRemove ? 0x00U : 0xFFU); std::uint_fast8_t i; // set all global filters (partially unrolled loop) - for (i = 0U; i < Q_DIM(priv_.glbFilter); i += 4U) { - priv_.glbFilter[i ] = tmp; - priv_.glbFilter[i + 1U] = tmp; - priv_.glbFilter[i + 2U] = tmp; - priv_.glbFilter[i + 3U] = tmp; + for (i = 0U; i < Q_DIM(filt_.glb); i += 4U) { + filt_.glb[i ] = tmp; + filt_.glb[i + 1U] = tmp; + filt_.glb[i + 2U] = tmp; + filt_.glb[i + 3U] = tmp; } if (isRemove) { // leave the "not maskable" filters enabled, // see qs.h, Miscellaneous QS records (not maskable) // - priv_.glbFilter[0] = 0x01U; - priv_.glbFilter[7] = 0xFCU; - priv_.glbFilter[8] = 0x7FU; + filt_.glb[0] = 0x01U; + filt_.glb[6] = 0x40U; + filt_.glb[7] = 0xFCU; + filt_.glb[8] = 0x7FU; } else { // never turn the last 3 records on (0x7D, 0x7E, 0x7F) - priv_.glbFilter[15] = 0x1FU; + filt_.glb[15] = 0x1FU; } break; } case QS_SM_RECORDS: if (isRemove) { - priv_.glbFilter[0] &= - static_cast(~0xFEU & 0xFFU); - priv_.glbFilter[1] &= - static_cast(~0x03U & 0xFFU); - priv_.glbFilter[6] &= - static_cast(~0x80U & 0xFFU); - priv_.glbFilter[7] &= - static_cast(~0x03U & 0xFFU); + filt_.glb[0] &= static_cast(~0xFEU & 0xFFU); + filt_.glb[1] &= static_cast(~0x03U & 0xFFU); + filt_.glb[6] &= static_cast(~0x80U & 0xFFU); + filt_.glb[7] &= static_cast(~0x03U & 0xFFU); } else { - priv_.glbFilter[0] |= 0xFEU; - priv_.glbFilter[1] |= 0x03U; - priv_.glbFilter[6] |= 0x80U; - priv_.glbFilter[7] |= 0x03U; + filt_.glb[0] |= 0xFEU; + filt_.glb[1] |= 0x03U; + filt_.glb[6] |= 0x80U; + filt_.glb[7] |= 0x03U; } break; case QS_AO_RECORDS: if (isRemove) { - priv_.glbFilter[1] &= - static_cast(~0xFCU & 0xFFU); - priv_.glbFilter[2] &= - static_cast(~0x07U & 0xFFU); - priv_.glbFilter[5] &= - static_cast(~0x20U & 0xFFU); + filt_.glb[1] &= static_cast(~0xFCU & 0xFFU); + filt_.glb[2] &= static_cast(~0x07U & 0xFFU); + filt_.glb[5] &= static_cast(~0x20U & 0xFFU); } else { - priv_.glbFilter[1] |= 0xFCU; - priv_.glbFilter[2] |= 0x07U; - priv_.glbFilter[5] |= 0x20U; + filt_.glb[1] |= 0xFCU; + filt_.glb[2] |= 0x07U; + filt_.glb[5] |= 0x20U; } break; case QS_EQ_RECORDS: if (isRemove) { - priv_.glbFilter[2] &= - static_cast(~0x78U & 0xFFU); - priv_.glbFilter[5] &= - static_cast(~0x40U & 0xFFU); + filt_.glb[2] &= static_cast(~0x78U & 0xFFU); + filt_.glb[5] &= static_cast(~0x40U & 0xFFU); } else { - priv_.glbFilter[2] |= 0x78U; - priv_.glbFilter[5] |= 0x40U; + filt_.glb[2] |= 0x78U; + filt_.glb[5] |= 0x40U; } break; case QS_MP_RECORDS: if (isRemove) { - priv_.glbFilter[3] &= - static_cast(~0x03U & 0xFFU); - priv_.glbFilter[5] &= - static_cast(~0x80U & 0xFFU); + filt_.glb[3] &= static_cast(~0x03U & 0xFFU); + filt_.glb[5] &= static_cast(~0x80U & 0xFFU); } else { - priv_.glbFilter[3] |= 0x03U; - priv_.glbFilter[5] |= 0x80U; + filt_.glb[3] |= 0x03U; + filt_.glb[5] |= 0x80U; } break; case QS_QF_RECORDS: if (isRemove) { - priv_.glbFilter[2] &= - static_cast(~0x80U & 0xFFU); - priv_.glbFilter[3] &= - static_cast(~0xFCU & 0xFFU); - priv_.glbFilter[4] &= - static_cast(~0xC0U & 0xFFU); - priv_.glbFilter[5] &= - static_cast(~0x1FU & 0xFFU); + filt_.glb[2] &= static_cast(~0x80U & 0xFFU); + filt_.glb[3] &= static_cast(~0xFCU & 0xFFU); + filt_.glb[4] &= static_cast(~0xC0U & 0xFFU); + filt_.glb[5] &= static_cast(~0x1FU & 0xFFU); } else { - priv_.glbFilter[2] |= 0x80U; - priv_.glbFilter[3] |= 0xFCU; - priv_.glbFilter[4] |= 0xC0U; - priv_.glbFilter[5] |= 0x1FU; + filt_.glb[2] |= 0x80U; + filt_.glb[3] |= 0xFCU; + filt_.glb[4] |= 0xC0U; + filt_.glb[5] |= 0x1FU; } break; case QS_TE_RECORDS: if (isRemove) { - priv_.glbFilter[4] &= - static_cast(~0x3FU & 0xFFU); + filt_.glb[4] &= static_cast(~0x3FU & 0xFFU); } else { - priv_.glbFilter[4] |= 0x3FU; + filt_.glb[4] |= 0x3FU; } break; case QS_SC_RECORDS: if (isRemove) { - priv_.glbFilter[6] &= - static_cast(~0x7FU & 0xFFU); + filt_.glb[6] &= static_cast(~0x3FU & 0xFFU); + } + else { + filt_.glb[6] |= 0x3FU; + } + break; + case QS_SEM_RECORDS: + if (isRemove) { + filt_.glb[8] &= static_cast(~0x80U & 0xFFU); + filt_.glb[9] &= static_cast(~0x07U & 0xFFU); + } + else { + filt_.glb[8] |= 0x80U; + filt_.glb[9] |= 0x07U; + } + break; + case QS_MTX_RECORDS: + if (isRemove) { + filt_.glb[9] &= static_cast(~0xF8U & 0xFFU); + filt_.glb[10] &= static_cast(~0x01U & 0xFFU); } else { - priv_.glbFilter[6] |= 0x7FU; + filt_.glb[9] |= 0xF8U; + filt_.glb[10] |= 0x01U; } break; case QS_U0_RECORDS: if (isRemove) { - priv_.glbFilter[12] &= - static_cast(~0xF0U & 0xFFU); - priv_.glbFilter[13] &= - static_cast(~0x01U & 0xFFU); + filt_.glb[12] &= static_cast(~0xF0U & 0xFFU); + filt_.glb[13] &= static_cast(~0x01U & 0xFFU); } else { - priv_.glbFilter[12] |= 0xF0U; - priv_.glbFilter[13] |= 0x01U; + filt_.glb[12] |= 0xF0U; + filt_.glb[13] |= 0x01U; } break; case QS_U1_RECORDS: if (isRemove) { - priv_.glbFilter[13] &= - static_cast(~0x3EU & 0xFFU); + filt_.glb[13] &= static_cast(~0x3EU & 0xFFU); } else { - priv_.glbFilter[13] |= 0x3EU; + filt_.glb[13] |= 0x3EU; } break; case QS_U2_RECORDS: if (isRemove) { - priv_.glbFilter[13] &= - static_cast(~0xC0U & 0xFFU); - priv_.glbFilter[14] &= - static_cast(~0x07U & 0xFFU); + filt_.glb[13] &= static_cast(~0xC0U & 0xFFU); + filt_.glb[14] &= static_cast(~0x07U & 0xFFU); } else { - priv_.glbFilter[13] |= 0xC0U; - priv_.glbFilter[14] |= 0x07U; + filt_.glb[13] |= 0xC0U; + filt_.glb[14] |= 0x07U; } break; case QS_U3_RECORDS: if (isRemove) { - priv_.glbFilter[14] &= - static_cast(~0xF8U & 0xFFU); + filt_.glb[14] &= static_cast(~0xF8U & 0xFFU); } else { - priv_.glbFilter[14] |= 0xF8U; + filt_.glb[14] |= 0xF8U; } break; case QS_U4_RECORDS: if (isRemove) { - priv_.glbFilter[15] &= 0x1FU; + filt_.glb[15] &= static_cast(~0x1FU & 0xFFU); } else { - priv_.glbFilter[15] |= 0x1FU; + filt_.glb[15] |= 0x1FU; } break; case QS_UA_RECORDS: if (isRemove) { - priv_.glbFilter[12] &= - static_cast(~0xF0U & 0xFFU); - priv_.glbFilter[13] = 0U; - priv_.glbFilter[14] = 0U; - priv_.glbFilter[15] &= - static_cast(~0x1FU & 0xFFU); + filt_.glb[12] &= static_cast(~0xF0U & 0xFFU); + filt_.glb[13] = 0U; + filt_.glb[14] = 0U; + filt_.glb[15] &= static_cast(~0x1FU & 0xFFU); } else { - priv_.glbFilter[12] |= 0xF0U; - priv_.glbFilter[13] |= 0xFFU; - priv_.glbFilter[14] |= 0xFFU; - priv_.glbFilter[15] |= 0x1FU; + filt_.glb[12] |= 0xF0U; + filt_.glb[13] |= 0xFFU; + filt_.glb[14] |= 0xFFU; + filt_.glb[15] |= 0x1FU; } break; - default: - // QS rec number can't exceed 0x7D, so no need for escaping - Q_ASSERT_ID(210, rec < 0x7DU); + default: { + QS_CRIT_STAT + QS_CRIT_ENTRY(); + // QS rec number must be below 0x7D, so no need for escaping + Q_ASSERT_INCRIT(210, rec < 0x7DU); + QS_CRIT_EXIT(); if (isRemove) { - priv_.glbFilter[rec >> 3U] + filt_.glb[rec >> 3U] &= static_cast(~(1U << (rec & 7U)) & 0xFFU); } else { - priv_.glbFilter[rec >> 3U] + filt_.glb[rec >> 3U] |= static_cast(1U << (rec & 7U)); // never turn the last 3 records on (0x7D, 0x7E, 0x7F) - priv_.glbFilter[15] &= 0x1FU; + filt_.glb[15] &= 0x1FU; } break; + } } } -//**************************************************************************** -/// @description -/// This function sets up the local QS filter to enable or disable the -/// given QS object-id or a group of object-ids @a filter. -/// This function should be called indirectly through the macro -/// QS_LOC_FILTER() -/// -/// @param[in] filter the QS object-id or group to enable in the filter, -/// if positive or disable, if negative. The qs_id numbers -/// must be in the range 1..127. -/// @note -/// Filtering based on the object-id (local filter) is the second layer of -/// filtering. The first layer is based on the QS record-type (gloabl filter). -/// Both filter layers must be enabled for the QS record to be inserted into -/// the QS buffer. -/// -/// @sa QP::QS::glbFilter_() -/// -void QS::locFilter_(std::int_fast16_t const filter) noexcept { +//............................................................................ +void locFilter_(std::int_fast16_t const filter) noexcept { bool const isRemove = (filter < 0); - std::uint16_t const qs_id = isRemove + std::uint16_t const qsId = isRemove ? static_cast(-filter) : static_cast(filter); std::uint8_t const tmp = (isRemove ? 0x00U : 0xFFU); std::uint_fast8_t i; - switch (qs_id) { + switch (qsId) { case QS_ALL_IDS: - // set all global filters (partially unrolled loop) - for (i = 0U; i < Q_DIM(priv_.locFilter); i += 4U) { - priv_.locFilter[i ] = tmp; - priv_.locFilter[i + 1U] = tmp; - priv_.locFilter[i + 2U] = tmp; - priv_.locFilter[i + 3U] = tmp; + // set all local filters (partially unrolled loop) + for (i = 0U; i < Q_DIM(filt_.loc); i += 4U) { + filt_.loc[i ] = tmp; + filt_.loc[i + 1U] = tmp; + filt_.loc[i + 2U] = tmp; + filt_.loc[i + 3U] = tmp; } break; case QS_AO_IDS: for (i = 0U; i < 8U; i += 4U) { - priv_.locFilter[i ] = tmp; - priv_.locFilter[i + 1U] = tmp; - priv_.locFilter[i + 2U] = tmp; - priv_.locFilter[i + 3U] = tmp; + filt_.loc[i ] = tmp; + filt_.loc[i + 1U] = tmp; + filt_.loc[i + 2U] = tmp; + filt_.loc[i + 3U] = tmp; } break; case QS_EP_IDS: i = 8U; - priv_.locFilter[i ] = tmp; - priv_.locFilter[i + 1U] = tmp; + filt_.loc[i ] = tmp; + filt_.loc[i + 1U] = tmp; break; case QS_AP_IDS: i = 12U; - priv_.locFilter[i ] = tmp; - priv_.locFilter[i + 1U] = tmp; - priv_.locFilter[i + 2U] = tmp; - priv_.locFilter[i + 3U] = tmp; + filt_.loc[i ] = tmp; + filt_.loc[i + 1U] = tmp; + filt_.loc[i + 2U] = tmp; + filt_.loc[i + 3U] = tmp; break; - default: - if (qs_id < 0x7FU) { - if (isRemove) { - priv_.locFilter[qs_id >> 3U] &= - static_cast( - ~(1U << (qs_id & 7U)) & 0xFFU); - } - else { - priv_.locFilter[qs_id >> 3U] - |= (1U << (qs_id & 7U)); - } + default: { + QS_CRIT_STAT + QS_CRIT_ENTRY(); + // qsId must be in range + Q_ASSERT_INCRIT(310, qsId < 0x7FU); + QS_CRIT_EXIT(); + if (isRemove) { + filt_.loc[qsId >> 3U] &= + static_cast( + ~(1U << (qsId & 7U)) & 0xFFU); } else { - Q_ERROR_ID(310); // incorrect qs_id + filt_.loc[qsId >> 3U] + |= (1U << (qsId & 7U)); } break; + } } - priv_.locFilter[0] |= 0x01U; // leave QS_ID == 0 always on + filt_.loc[0] |= 0x01U; // leave QS_ID == 0 always on } -//**************************************************************************** -/// @description -/// This function must be called at the beginning of each QS record. -/// This function should be called indirectly through the macro QS_BEGIN_ID(), -/// or QS_BEGIN_NOCRIT(), depending if it's called in a normal code or from -/// a critical section. -/// -void QS::beginRec_(std::uint_fast8_t const rec) noexcept { +//............................................................................ +void beginRec_(std::uint_fast8_t const rec) noexcept { std::uint8_t const b = priv_.seq + 1U; - std::uint8_t chksum_ = 0U; // reset the checksum - std::uint8_t * const buf_ = priv_.buf; // put in a temporary (register) - QSCtr head_ = priv_.head; // put in a temporary (register) - QSCtr const end_ = priv_.end; // put in a temporary (register) + std::uint8_t chksum = 0U; // reset the checksum + std::uint8_t * const buf = priv_.buf; // put in a temporary (register) + QSCtr head = priv_.head; // put in a temporary (register) + QSCtr const end = priv_.end; // put in a temporary (register) priv_.seq = b; // store the incremented sequence num - priv_.used += 2U; // 2 bytes about to be added + priv_.used = (priv_.used + 2U); // 2 bytes about to be added QS_INSERT_ESC_BYTE_(b) - chksum_ += static_cast(rec); + chksum += static_cast(rec); QS_INSERT_BYTE_(static_cast(rec)) // no need for escaping - priv_.head = head_; // save the head - priv_.chksum = chksum_; // save the checksum + priv_.head = head; // save the head + priv_.chksum = chksum; // save the checksum } -//**************************************************************************** -/// @description -/// This function must be called at the end of each QS record. -/// This function should be called indirectly through the macro QS_END(), -/// or QS_END_NOCRIT(), depending if it's called in a normal code or from -/// a critical section. -/// -void QS::endRec_(void) noexcept { - std::uint8_t * const buf_ = priv_.buf; // put in a temporary (register) - QSCtr head_ = priv_.head; - QSCtr const end_ = priv_.end; - std::uint8_t b = priv_.chksum; +//............................................................................ +void endRec_() noexcept { + std::uint8_t * const buf = priv_.buf; // put in a temporary (register) + QSCtr head = priv_.head; + QSCtr const end = priv_.end; + std::uint8_t b = priv_.chksum; b ^= 0xFFU; // invert the bits in the checksum - priv_.used += 2U; // 2 bytes about to be added + priv_.used = (priv_.used + 2U); // 2 bytes about to be added if ((b != QS_FRAME) && (b != QS_ESC)) { QS_INSERT_BYTE_(b) @@ -448,161 +479,157 @@ void QS::endRec_(void) noexcept { else { QS_INSERT_BYTE_(QS_ESC) QS_INSERT_BYTE_(b ^ QS_ESC_XOR) - ++priv_.used; // account for the ESC byte + priv_.used = (priv_.used + 1U); // account for the ESC byte } QS_INSERT_BYTE_(QS_FRAME) // do not escape this QS_FRAME - priv_.head = head_; // save the head - if (priv_.used > end_) { // overrun over the old data? - priv_.used = end_; // the whole buffer is used - priv_.tail = head_; // shift the tail to the old data + priv_.head = head; // save the head + if (priv_.used > end) { // overrun over the old data? + priv_.used = end; // the whole buffer is used + priv_.tail = head; // shift the tail to the old data } } -//**************************************************************************** -void QS_target_info_(std::uint8_t const isReset) noexcept { - static constexpr std::uint8_t ZERO = static_cast('0'); - static std::uint8_t const * const TIME = - reinterpret_cast(&BUILD_TIME[0]); - static std::uint8_t const * const DATE = - reinterpret_cast(&BUILD_DATE[0]); +//............................................................................ +void u8_raw_(std::uint8_t const d) noexcept { + std::uint8_t chksum = priv_.chksum; // put in a temporary (register) + std::uint8_t * const buf = priv_.buf; // put in a temporary (register) + QSCtr head = priv_.head; // put in a temporary (register) + QSCtr const end = priv_.end; // put in a temporary (register) - QS::beginRec_(static_cast(QS_TARGET_INFO)); - QS::u8_raw_(isReset); + priv_.used = (priv_.used + 1U); // 1 byte about to be added + QS_INSERT_ESC_BYTE_(d) - static union { - std::uint16_t u16; - std::uint8_t u8[2]; - } endian_test; - endian_test.u16 = 0x0102U; - // big endian ? add the 0x8000U flag - QS_U16_PRE_(((endian_test.u8[0] == 0x01U) - ? (0x8000U | QP_VERSION) - : QP_VERSION)); // target endianness + version number + priv_.head = head; // save the head + priv_.chksum = chksum; // save the checksum +} - // send the object sizes... - QS::u8_raw_(Q_SIGNAL_SIZE - | static_cast(QF_EVENT_SIZ_SIZE << 4U)); +//............................................................................ +void u8u8_raw_( + std::uint8_t const d1, + std::uint8_t const d2) noexcept +{ + std::uint8_t chksum = priv_.chksum; // put in a temporary (register) + std::uint8_t * const buf = priv_.buf; // put in a temporary (register) + QSCtr head = priv_.head; // put in a temporary (register) + QSCtr const end = priv_.end; // put in a temporary (register) -#ifdef QF_EQUEUE_CTR_SIZE - QS::u8_raw_(QF_EQUEUE_CTR_SIZE - | static_cast(QF_TIMEEVT_CTR_SIZE << 4U)); -#else - QS::u8_raw_(static_cast(QF_TIMEEVT_CTR_SIZE << 4U)); -#endif // ifdef QF_EQUEUE_CTR_SIZE + priv_.used = (priv_.used + 2U); // 2 bytes about to be added + QS_INSERT_ESC_BYTE_(d1) + QS_INSERT_ESC_BYTE_(d2) -#ifdef QF_MPOOL_CTR_SIZE - QS::u8_raw_(QF_MPOOL_SIZ_SIZE - | static_cast(QF_MPOOL_CTR_SIZE << 4U)); -#else - QS::u8_raw_(0U); -#endif // ifdef QF_MPOOL_CTR_SIZE + priv_.head = head; // save the head + priv_.chksum = chksum; // save the checksum +} - QS::u8_raw_(QS_OBJ_PTR_SIZE | (QS_FUN_PTR_SIZE << 4U)); - QS::u8_raw_(QS_TIME_SIZE); +//............................................................................ +void u16_raw_(std::uint16_t d) noexcept { + std::uint8_t b = static_cast(d); + std::uint8_t chksum = priv_.chksum; // put in a temporary (register) + std::uint8_t * const buf = priv_.buf; // put in a temporary (register) + QSCtr head = priv_.head; // put in a temporary (register) + QSCtr const end = priv_.end; // put in a temporary (register) - // send the limits... - QS::u8_raw_(QF_MAX_ACTIVE); - QS::u8_raw_(QF_MAX_EPOOL | (QF_MAX_TICK_RATE << 4U)); + priv_.used = (priv_.used + 2U); // 2 bytes about to be added - // send the build time in three bytes (sec, min, hour)... - QS::u8_raw_((10U * (TIME[6] - ZERO)) + (TIME[7] - ZERO)); - QS::u8_raw_((10U * (TIME[3] - ZERO)) + (TIME[4] - ZERO)); - if (BUILD_TIME[0] == ' ') { - QS::u8_raw_(TIME[1] - ZERO); - } - else { - QS::u8_raw_((10U * (TIME[0] - ZERO)) + (TIME[1] - ZERO)); - } + QS_INSERT_ESC_BYTE_(b) - // send the build date in three bytes (day, month, year) ... - if (BUILD_DATE[4] == ' ') { - QS::u8_raw_(DATE[5] - ZERO); - } - else { - QS::u8_raw_((10U * (DATE[4] - ZERO)) + (DATE[5] - ZERO)); - } - // convert the 3-letter month to a number 1-12 ... - std::uint8_t b; - switch (DATE[0] + DATE[1] + DATE[2]) { - case 'J' + 'a' +'n': - b = 1U; - break; - case 'F' + 'e' + 'b': - b = 2U; - break; - case 'M' + 'a' +'r': - b = 3U; - break; - case 'A' + 'p' + 'r': - b = 4U; - break; - case 'M' + 'a' + 'y': - b = 5U; - break; - case 'J' + 'u' + 'n': - b = 6U; - break; - case 'J' + 'u' + 'l': - b = 7U; - break; - case 'A' + 'u' + 'g': - b = 8U; - break; - case 'S' + 'e' + 'p': - b = 9U; - break; - case 'O' + 'c' + 't': - b = 10U; - break; - case 'N' + 'o' + 'v': - b = 11U; - break; - case 'D' + 'e' + 'c': - b = 12U; - break; - default: - b = 0U; - break; - } - QS::u8_raw_(b); // store the month - QS::u8_raw_((10U * (DATE[9] - ZERO)) + (DATE[10] - ZERO)); - QS::endRec_(); + d >>= 8U; + b = static_cast(d); + QS_INSERT_ESC_BYTE_(b) + + priv_.head = head; // save the head + priv_.chksum = chksum; // save the checksum +} + +//............................................................................ +void u32_raw_(std::uint32_t d) noexcept { + std::uint8_t chksum = priv_.chksum; // put in a temporary (register) + std::uint8_t * const buf = priv_.buf; // put in a temporary (register) + QSCtr head = priv_.head; // put in a temporary (register) + QSCtr const end = priv_.end; // put in a temporary (register) + + priv_.used = (priv_.used + 4U); // 4 bytes about to be added + for (std::uint_fast8_t i = 4U; i != 0U; --i) { + std::uint8_t const b = static_cast(d); + QS_INSERT_ESC_BYTE_(b) + d >>= 8U; + } + + priv_.head = head; // save the head + priv_.chksum = chksum; // save the checksum +} + +//............................................................................ +void obj_raw_(void const * const obj) noexcept { + #if (QS_OBJ_PTR_SIZE == 1U) + u8_raw_(reinterpret_cast(obj)); + #elif (QS_OBJ_PTR_SIZE == 2U) + u16_raw_(reinterpret_cast(obj)); + #elif (QS_OBJ_PTR_SIZE == 4U) + u32_raw_(reinterpret_cast(obj)); + #elif (QS_OBJ_PTR_SIZE == 8U) + u64_raw_(reinterpret_cast(obj)); + #else + u32_raw_(reinterpret_cast(obj)); + #endif +} + +//............................................................................ +void str_raw_(char const * s) noexcept { + std::uint8_t b = static_cast(*s); + std::uint8_t chksum = priv_.chksum; // put in a temporary (register) + std::uint8_t * const buf = priv_.buf; // put in a temporary (register) + QSCtr head = priv_.head; // put in a temporary (register) + QSCtr const end = priv_.end; // put in a temporary (register) + QSCtr used = priv_.used; // put in a temporary (register) + + while (b != 0U) { + chksum += b; // update checksum + QS_INSERT_BYTE_(b) // ASCII characters don't need escaping + ++s; + b = static_cast(*s); + ++used; + } + QS_INSERT_BYTE_(0U) // zero-terminate the string + ++used; + + priv_.head = head; // save the head + priv_.chksum = chksum; // save the checksum + priv_.used = used; // save # of used buffer space } -//**************************************************************************** -/// @description -/// @note This function is only to be used through macros, never in the -/// client code directly. -/// -void QS::u8_fmt_(std::uint8_t const format, std::uint8_t const d) noexcept { - std::uint8_t chksum_ = priv_.chksum; // put in a temporary (register) - std::uint8_t *const buf_ = priv_.buf; // put in a temporary (register) - QSCtr head_ = priv_.head; // put in a temporary (register) - QSCtr const end_= priv_.end; // put in a temporary (register) +//............................................................................ +void u8_fmt_( + std::uint8_t const format, + std::uint8_t const d) noexcept +{ + std::uint8_t chksum = priv_.chksum; // put in a temporary (register) + std::uint8_t *const buf = priv_.buf; // put in a temporary (register) + QSCtr head = priv_.head; // put in a temporary (register) + QSCtr const end = priv_.end; // put in a temporary (register) - priv_.used += 2U; // 2 bytes about to be added + priv_.used = (priv_.used + 2U); // 2 bytes about to be added QS_INSERT_ESC_BYTE_(format) QS_INSERT_ESC_BYTE_(d) - priv_.head = head_; // save the head - priv_.chksum = chksum_; // save the checksum + priv_.head = head; // save the head + priv_.chksum = chksum; // save the checksum } -//**************************************************************************** -/// @description -/// This function is only to be used through macros, never in the -/// client code directly. -/// -void QS::u16_fmt_(std::uint8_t format, std::uint16_t d) noexcept { - std::uint8_t chksum_ = priv_.chksum; // put in a temporary (register) - std::uint8_t * const buf_ = priv_.buf; // put in a temporary (register) - QSCtr head_ = priv_.head; // put in a temporary (register) - QSCtr const end_= priv_.end; // put in a temporary (register) +//............................................................................ +void u16_fmt_( + std::uint8_t format, + std::uint16_t d) noexcept +{ + std::uint8_t chksum = priv_.chksum; // put in a temporary (register) + std::uint8_t * const buf = priv_.buf; // put in a temporary (register) + QSCtr head = priv_.head; // put in a temporary (register) + QSCtr const end = priv_.end; // put in a temporary (register) - priv_.used += 3U; // 3 bytes about to be added + priv_.used = (priv_.used + 3U); // 3 bytes about to be added QS_INSERT_ESC_BYTE_(format) @@ -613,21 +640,21 @@ void QS::u16_fmt_(std::uint8_t format, std::uint16_t d) noexcept { format = static_cast(d); QS_INSERT_ESC_BYTE_(format) - priv_.head = head_; // save the head - priv_.chksum = chksum_; // save the checksum + priv_.head = head; // save the head + priv_.chksum = chksum; // save the checksum } -//**************************************************************************** -/// @note This function is only to be used through macros, never in the -/// client code directly. -/// -void QS::u32_fmt_(std::uint8_t format, std::uint32_t d) noexcept { - std::uint8_t chksum_ = priv_.chksum; // put in a temporary (register) - std::uint8_t * const buf_= priv_.buf; // put in a temporary (register) - QSCtr head_ = priv_.head; // put in a temporary (register) - QSCtr const end_= priv_.end; // put in a temporary (register) - - priv_.used += static_cast(5); // 5 bytes about to be added +//............................................................................ +void u32_fmt_( + std::uint8_t format, + std::uint32_t d) noexcept +{ + std::uint8_t chksum = priv_.chksum; // put in a temporary (register) + std::uint8_t * const buf = priv_.buf; // put in a temporary (register) + QSCtr head = priv_.head; // put in a temporary (register) + QSCtr const end = priv_.end; // put in a temporary (register) + + priv_.used = (priv_.used + 5U); // 5 bytes about to be added QS_INSERT_ESC_BYTE_(format) // insert the format byte for (std::uint_fast8_t i = 4U; i != 0U; --i) { @@ -636,414 +663,411 @@ void QS::u32_fmt_(std::uint8_t format, std::uint32_t d) noexcept { d >>= 8U; } - priv_.head = head_; // save the head - priv_.chksum = chksum_; // save the checksum + priv_.head = head; // save the head + priv_.chksum = chksum; // save the checksum } -//**************************************************************************** -/// @note This function is only to be used through macro QS_USR_DICTIONARY() -/// -void QS::usr_dict_pre_(enum_t const rec, - char_t const * const name) noexcept -{ - QS_CRIT_STAT_ - QS_CRIT_E_(); - beginRec_(static_cast(QS_USR_DICT)); - QS_U8_PRE_(rec); - QS_STR_PRE_(name); - endRec_(); - QS_CRIT_X_(); - onFlush(); -} - -//**************************************************************************** -/// @note This function is only to be used through macros, never in the -/// client code directly. -/// -void QS::mem_fmt_(std::uint8_t const *blk, std::uint8_t size) noexcept { - std::uint8_t b = static_cast(MEM_T); - std::uint8_t chksum_ = priv_.chksum + b; - std::uint8_t * const buf_= priv_.buf; // put in a temporary (register) - QSCtr head_ = priv_.head; // put in a temporary (register) - QSCtr const end_= priv_.end; // put in a temporary (register) - - // size+2 bytes to be added - priv_.used += static_cast(size + 2U); - - QS_INSERT_BYTE_(b) - QS_INSERT_ESC_BYTE_(size) - - // output the 'size' number of bytes - for (; size != 0U; --size) { - b = *blk; - QS_INSERT_ESC_BYTE_(b) - ++blk; - } - - priv_.head = head_; // save the head - priv_.chksum = chksum_; // save the checksum -} +//............................................................................ +void str_fmt_(char const * s) noexcept { + std::uint8_t b = static_cast(*s); + std::uint8_t chksum = static_cast( + priv_.chksum + static_cast(STR_T)); + std::uint8_t * const buf = priv_.buf; // put in a temporary (register) + QSCtr head = priv_.head; // put in a temporary (register) + QSCtr const end = priv_.end; // put in a temporary (register) + QSCtr used = priv_.used; // put in a temporary (register) -//**************************************************************************** -/// @note This function is only to be used through macros, never in the -/// client code directly. -/// -void QS::str_fmt_(char_t const *s) noexcept { - std::uint8_t b = static_cast(*s); - std::uint8_t chksum_ = static_cast( - priv_.chksum + static_cast(STR_T)); - std::uint8_t * const buf_= priv_.buf; // put in a temporary (register) - QSCtr head_ = priv_.head; // put in a temporary (register) - QSCtr const end_= priv_.end; // put in a temporary (register) - QSCtr used_ = priv_.used; // put in a temporary (register) - - used_ += 2U; // the format byte and the terminating-0 + used += 2U; // the format byte and the terminating-0 QS_INSERT_BYTE_(static_cast(STR_T)) while (b != 0U) { // ASCII characters don't need escaping - chksum_ += b; // update checksum + chksum += b; // update checksum QS_INSERT_BYTE_(b) ++s; b = static_cast(*s); - ++used_; + ++used; } QS_INSERT_BYTE_(0U) // zero-terminate the string - priv_.head = head_; // save the head - priv_.chksum = chksum_; // save the checksum - priv_.used = used_; // save # of used buffer space + priv_.head = head; // save the head + priv_.chksum = chksum; // save the checksum + priv_.used = used; // save # of used buffer space } -//**************************************************************************** -/// @note This function is only to be used through macros, never in the -/// client code directly. -/// -void QS::u8_raw_(std::uint8_t const d) noexcept { - std::uint8_t chksum_ = priv_.chksum; // put in a temporary (register) - std::uint8_t * const buf_ = priv_.buf; // put in a temporary (register) - QSCtr head_ = priv_.head; // put in a temporary (register) - QSCtr const end_= priv_.end; // put in a temporary (register) - - priv_.used += 1U; // 1 byte about to be added - QS_INSERT_ESC_BYTE_(d) - - priv_.head = head_; // save the head - priv_.chksum = chksum_; // save the checksum -} - -//**************************************************************************** -/// @note This function is only to be used through macros, never in the -/// client code directly. -/// -void QS::u8u8_raw_(std::uint8_t const d1, std::uint8_t const d2) noexcept { - std::uint8_t chksum_ = priv_.chksum; // put in a temporary (register) - std::uint8_t * const buf_ = priv_.buf; // put in a temporary (register) - QSCtr head_ = priv_.head; // put in a temporary (register) - QSCtr const end_= priv_.end; // put in a temporary (register) - - priv_.used += 2U; // 2 bytes about to be added - QS_INSERT_ESC_BYTE_(d1) - QS_INSERT_ESC_BYTE_(d2) - - priv_.head = head_; // save the head - priv_.chksum = chksum_; // save the checksum -} - -//**************************************************************************** -/// @note This function is only to be used through macros, never in the -/// client code directly. -/// -void QS::u16_raw_(std::uint16_t d) noexcept { - std::uint8_t b = static_cast(d); - std::uint8_t chksum_ = priv_.chksum; // put in a temporary (register) - std::uint8_t * const buf_ = priv_.buf; // put in a temporary (register) - QSCtr head_ = priv_.head; // put in a temporary (register) - QSCtr const end_= priv_.end; // put in a temporary (register) - - priv_.used += 2U; // 2 bytes about to be added - - QS_INSERT_ESC_BYTE_(b) +//............................................................................ +void mem_fmt_( + std::uint8_t const * blk, + std::uint8_t size) noexcept +{ + std::uint8_t b = static_cast(MEM_T); + std::uint8_t chksum = priv_.chksum + b; + std::uint8_t * const buf = priv_.buf; // put in a temporary (register) + QSCtr head = priv_.head; // put in a temporary (register) + QSCtr const end = priv_.end; // put in a temporary (register) - d >>= 8U; - b = static_cast(d); - QS_INSERT_ESC_BYTE_(b) + priv_.used = (priv_.used + size + 2U); // size+2 bytes to be added - priv_.head = head_; // save the head - priv_.chksum = chksum_; // save the checksum -} + QS_INSERT_BYTE_(b) + QS_INSERT_ESC_BYTE_(size) -//**************************************************************************** -/// @note This function is only to be used through macros, never in the -/// client code directly. -/// -void QS::u32_raw_(std::uint32_t d) noexcept { - std::uint8_t chksum_ = priv_.chksum; // put in a temporary (register) - std::uint8_t * const buf_ = priv_.buf; // put in a temporary (register) - QSCtr head_ = priv_.head; // put in a temporary (register) - QSCtr const end_= priv_.end; // put in a temporary (register) - - priv_.used += 4U; // 4 bytes about to be added - for (std::uint_fast8_t i = 4U; i != 0U; --i) { - std::uint8_t const b = static_cast(d); + // output the 'size' number of bytes + for (; size != 0U; --size) { + b = *blk; QS_INSERT_ESC_BYTE_(b) - d >>= 8U; + ++blk; } - priv_.head = head_; // save the head - priv_.chksum = chksum_; // save the checksum -} - -//**************************************************************************** -/// @note This function is only to be used through macros, never in the -/// client code directly. -/// -void QS::obj_raw_(void const * const obj) noexcept { -#if (QS_OBJ_PTR_SIZE == 1U) - u8_raw_(reinterpret_cast(obj)); -#elif (QS_OBJ_PTR_SIZE == 2U) - u16_raw_(reinterpret_cast(obj)); -#elif (QS_OBJ_PTR_SIZE == 4U) - u32_raw_(reinterpret_cast(obj)); -#elif (QS_OBJ_PTR_SIZE == 8U) - u64_raw_(reinterpret_cast(obj)); -#else - u32_raw_(reinterpret_cast(obj)); -#endif + priv_.head = head; // save the head + priv_.chksum = chksum; // save the checksum } -//**************************************************************************** -/// @note This function is only to be used through macros, never in the -/// client code directly. -/// -void QS::str_raw_(char_t const *s) noexcept { - std::uint8_t b = static_cast(*s); - std::uint8_t chksum_ = priv_.chksum; // put in a temporary (register) - std::uint8_t * const buf_ = priv_.buf; // put in a temporary (register) - QSCtr head_ = priv_.head; // put in a temporary (register) - QSCtr const end_= priv_.end; // put in a temporary (register) - QSCtr used_ = priv_.used; // put in a temporary (register) +//............................................................................ +void sig_dict_pre_( + QSignal const sig, + void const * const obj, + char const * const name) noexcept +{ + QS_CRIT_STAT + QS_CRIT_ENTRY(); + QS_MEM_SYS(); - while (b != 0U) { - chksum_ += b; // update checksum - QS_INSERT_BYTE_(b) // ASCII characters don't need escaping - ++s; - b = static_cast(*s); - ++used_; - } - QS_INSERT_BYTE_(0U) // zero-terminate the string - ++used_; + beginRec_(static_cast(QS_SIG_DICT)); + QS_SIG_PRE_(sig); + QS_OBJ_PRE_(obj); + QS_STR_PRE_((*name == '&') ? &name[1] : name); + endRec_(); - priv_.head = head_; // save the head - priv_.chksum = chksum_; // save the checksum - priv_.used = used_; // save # of used buffer space + QS_MEM_APP(); + QS_CRIT_EXIT(); + onFlush(); } -//**************************************************************************** -/// @description -/// This function delivers one byte at a time from the QS data buffer. -/// -/// @returns the byte in the least-significant 8-bits of the 16-bit return -/// value if the byte is available. If no more data is available at the time, -/// the function returns QP::QS_EOD (End-Of-Data). -/// -/// @note QP::QS::getByte() is __not__ protected with a critical section. -/// -std::uint16_t QS::getByte(void) noexcept { - std::uint16_t ret; - if (priv_.used == 0U) { - ret = QS_EOD; // set End-Of-Data - } - else { - std::uint8_t * const buf_ = priv_.buf; //put in a temporary (register) - QSCtr tail_ = priv_.tail; // put in a temporary (register) +//............................................................................ +void obj_dict_pre_( + void const * const obj, + char const * const name) noexcept +{ + QS_CRIT_STAT + QS_CRIT_ENTRY(); + QS_MEM_SYS(); - // the byte to return - ret = static_cast(buf_[tail_]); + beginRec_(static_cast(QS_OBJ_DICT)); + QS_OBJ_PRE_(obj); + QS_STR_PRE_((*name == '&') ? &name[1] : name); + endRec_(); - ++tail_; // advance the tail - if (tail_ == priv_.end) { // tail wrap around? - tail_ = 0U; - } - priv_.tail = tail_; // update the tail - --priv_.used; // one less byte used - } - return ret; // return the byte or EOD + QS_MEM_APP(); + QS_CRIT_EXIT(); + onFlush(); } -//**************************************************************************** -/// @description -/// 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. -/// -/// @returns if data is available, the function returns pointer to the -/// contiguous block of data and sets the value pointed to by @p pNbytes -/// to the # available bytes. If data is available at the time the function is -/// called, the function returns NULL pointer and sets the value pointed to by -/// @p pNbytes to zero. -/// -/// @note -/// Only the NULL return from QP::QS::getBlock() indicates that the QS buffer -/// is empty at the time of the call. The non-NULL return often means that -/// the block is at the end of the buffer and you need to call -/// QP::QS::getBlock() again to obtain the rest of the data that -/// "wrapped around" to the beginning of the QS data buffer. -/// -/// @note QP::QS::getBlock() is __not__ protected with a critical section. -/// -std::uint8_t const *QS::getBlock(std::uint16_t * const pNbytes) noexcept { - QSCtr const used_ = priv_.used; // put in a temporary (register) - std::uint8_t *buf_; - - // any bytes used in the ring buffer? - if (used_ == 0U) { - *pNbytes = 0U; // no bytes available right now - buf_ = nullptr; // no bytes available right now +//............................................................................ +void obj_arr_dict_pre_( + void const * const obj, + std::uint_fast16_t const idx, + char const * const name) noexcept +{ + QS_CRIT_STAT + QS_CRIT_ENTRY(); + Q_REQUIRE_INCRIT(400, idx < 1000U); + QS_CRIT_EXIT(); + + // format idx into a char buffer as "xxx\0" + std::uint8_t idx_str[4]; + std::uint_fast16_t tmp = idx; + std::uint8_t i; + idx_str[3] = 0U; // zero-terminate + idx_str[2] = static_cast( + static_cast('0') + (tmp % 10U)); + tmp /= 10U; + idx_str[1] = static_cast( + static_cast('0') + (tmp % 10U)); + if (idx_str[1] == static_cast('0')) { + i = 2U; } else { - QSCtr tail_ = priv_.tail; // put in a temporary (register) - QSCtr const end_ = priv_.end; // put in a temporary (register) - QSCtr n = static_cast(end_ - tail_); - if (n > used_) { - n = used_; + tmp /= 10U; + idx_str[0] = static_cast( + static_cast('0') + (tmp % 10U)); + if (idx_str[0] == static_cast('0')) { + i = 1U; } - if (n > static_cast(*pNbytes)) { - n = static_cast(*pNbytes); + else { + i = 0U; } - *pNbytes = static_cast(n); // n-bytes available - buf_ = priv_.buf; - buf_ = &buf_[tail_]; // the bytes are at the tail + } + + std::uint8_t j = ((*name == '&') ? 1U : 0U); - priv_.used -= n; - tail_ += n; - if (tail_ == end_) { - tail_ = 0U; + QS_CRIT_ENTRY(); + QS_MEM_SYS(); + + beginRec_(static_cast(QS_OBJ_DICT)); + QS_OBJ_PRE_(obj); + for (; name[j] != '\0'; ++j) { + QS_U8_PRE_(name[j]); + if (name[j] == '[') { + ++j; + break; } - priv_.tail = tail_; } - return buf_; + for (; idx_str[i] != 0U; ++i) { + QS_U8_PRE_(idx_str[i]); + } + // skip chars until ']' + for (; name[j] != '\0'; ++j) { + if (name[j] == ']') { + break; + } + } + for (; name[j] != '\0'; ++j) { + QS_U8_PRE_(name[j]); + } + QS_U8_PRE_(0U); // zero-terminate + endRec_(); + + QS_MEM_APP(); + QS_CRIT_EXIT(); + onFlush(); } -//**************************************************************************** -/// @note This function is only to be used through macro QS_SIG_DICTIONARY() -/// -void QS::sig_dict_pre_(enum_t const sig, void const * const obj, - char_t const *name) noexcept +//............................................................................ +void fun_dict_pre_( + QSpyFunPtr fun, + char const * const name) noexcept { - QS_CRIT_STAT_ + QS_CRIT_STAT + QS_CRIT_ENTRY(); + QS_MEM_SYS(); - if (*name == '&') { - ++name; - } - QS_CRIT_E_(); - beginRec_(static_cast(QS_SIG_DICT)); - QS_SIG_PRE_(sig); - QS_OBJ_PRE_(obj); - QS_STR_PRE_(name); + beginRec_(static_cast(QS_FUN_DICT)); + QS_FUN_PRE_(fun); + QS_STR_PRE_((*name == '&') ? &name[1] : name); endRec_(); - QS_CRIT_X_(); + + QS_MEM_APP(); + QS_CRIT_EXIT(); onFlush(); } -//**************************************************************************** -/// @note This function is only to be used through macro QS_OBJ_DICTIONARY() -/// -void QS::obj_dict_pre_(void const * const obj, - char_t const *name) noexcept +//............................................................................ +void usr_dict_pre_( + enum_t const rec, + char const * const name) noexcept { - QS_CRIT_STAT_ + QS_CRIT_STAT + QS_CRIT_ENTRY(); + QS_MEM_SYS(); - if (*name == '&') { - ++name; - } - QS_CRIT_E_(); - beginRec_(static_cast(QS_OBJ_DICT)); - QS_OBJ_PRE_(obj); + beginRec_(static_cast(QS_USR_DICT)); + QS_U8_PRE_(rec); QS_STR_PRE_(name); endRec_(); - QS_CRIT_X_(); + + QS_MEM_APP(); + QS_CRIT_EXIT(); onFlush(); } -//**************************************************************************** -/// @note This function is only to be used through macro QS_FUN_DICTIONARY() -/// -void QS::fun_dict_pre_(void (* const fun)(void), - char_t const *name) noexcept +//............................................................................ +void enum_dict_pre_( + enum_t const value, + std::uint8_t const group, + char const * const name) noexcept { - QS_CRIT_STAT_ + QS_CRIT_STAT + QS_CRIT_ENTRY(); + QS_MEM_SYS(); - if (*name == '&') { - ++name; - } - QS_CRIT_E_(); - beginRec_(static_cast(QS_FUN_DICT)); - QS_FUN_PRE_(fun); + beginRec_(static_cast(QS_ENUM_DICT)); + QS_2U8_PRE_(static_cast(value), group); QS_STR_PRE_(name); endRec_(); - QS_CRIT_X_(); + + QS_MEM_APP(); + QS_CRIT_EXIT(); onFlush(); } -//**************************************************************************** -/// @note This function is only to be used through macro QS_ASSERTION() -/// -void QS::assertion_pre_(char_t const * const module, int_t const loc, - std::uint32_t delay) +//............................................................................ +void assertion_pre_( + char const * const module, + int_t const id, + std::uint32_t const delay) noexcept { - QS_BEGIN_NOCRIT_PRE_(QP::QS_ASSERT_FAIL, 0U) + // NOTE: called in a critical section + + beginRec_(static_cast(QS_ASSERT_FAIL)); QS_TIME_PRE_(); - QS_U16_PRE_(loc); + QS_U16_PRE_(id); QS_STR_PRE_((module != nullptr) ? module : "?"); - QS_END_NOCRIT_PRE_() - QP::QS::onFlush(); - for (std::uint32_t volatile ctr = delay; ctr > 0U; --ctr) { + endRec_(); + onFlush(); + + // busy-wait until all QS data makes it over to the host + for (std::uint32_t volatile ctr = delay; ctr > 0U; ) { + ctr = (ctr - 1U); } - QP::QS::onCleanup(); + QS::onCleanup(); } //............................................................................ -void QS::crit_entry_pre_(void) { - QS_BEGIN_NOCRIT_PRE_(QP::QS_QF_CRIT_ENTRY, 0U) +void crit_entry_pre_() noexcept { + beginRec_(static_cast(QS_QF_CRIT_ENTRY)); QS_TIME_PRE_(); - ++QS::priv_.critNest; - QS_U8_PRE_(QS::priv_.critNest); - QS_END_NOCRIT_PRE_() + priv_.critNest = (priv_.critNest + 1U); + QS_U8_PRE_(priv_.critNest); + endRec_(); } //............................................................................ -void QS::crit_exit_pre_(void) { - QS_BEGIN_NOCRIT_PRE_(QP::QS_QF_CRIT_EXIT, 0U) +void crit_exit_pre_() noexcept { + beginRec_(static_cast(QS_QF_CRIT_EXIT)); QS_TIME_PRE_(); QS_U8_PRE_(QS::priv_.critNest); - --QS::priv_.critNest; - QS_END_NOCRIT_PRE_() + priv_.critNest = (priv_.critNest - 1U); + endRec_(); } //............................................................................ -void QS::isr_entry_pre_(std::uint8_t const isrnest, - std::uint8_t const prio) +void isr_entry_pre_( + std::uint8_t const isrnest, + std::uint8_t const prio) noexcept { - QS_BEGIN_NOCRIT_PRE_(QP::QS_QF_ISR_ENTRY, 0U) + beginRec_(static_cast(QS_QF_ISR_ENTRY)); QS_TIME_PRE_(); QS_U8_PRE_(isrnest); QS_U8_PRE_(prio); - QS_END_NOCRIT_PRE_() + endRec_(); } //............................................................................ -void QS::isr_exit_pre_(std::uint8_t const isrnest, - std::uint8_t const prio) +void isr_exit_pre_( + std::uint8_t const isrnest, + std::uint8_t const prio) noexcept { - QS_BEGIN_NOCRIT_PRE_(QP::QS_QF_ISR_EXIT, 0U) + beginRec_(static_cast(QS_QF_ISR_EXIT)); QS_TIME_PRE_(); QS_U8_PRE_(isrnest); QS_U8_PRE_(prio); - QS_END_NOCRIT_PRE_() + endRec_(); +} + +//............................................................................ +void target_info_pre_(std::uint8_t const isReset) { + // NOTE: called in a critical section + + static constexpr std::uint8_t ZERO = static_cast('0'); + static std::uint8_t const * const TIME = + reinterpret_cast(&BUILD_TIME[0]); + static std::uint8_t const * const DATE = + reinterpret_cast(&BUILD_DATE[0]); + + beginRec_(static_cast(QS_TARGET_INFO)); + u8_raw_(isReset); + + static union { + std::uint16_t u16; + std::uint8_t u8[2]; + } endian_test; + endian_test.u16 = 0x0102U; + // big endian ? add the 0x8000U flag + QS_U16_PRE_(((endian_test.u8[0] == 0x01U) + ? (0x8000U | QP_VERSION) + : QP_VERSION)); // target endianness + version number + + // send the object sizes... + u8_raw_(Q_SIGNAL_SIZE + | static_cast(QF_EVENT_SIZ_SIZE << 4U)); + +#ifdef QF_EQUEUE_CTR_SIZE + u8_raw_(QF_EQUEUE_CTR_SIZE + | static_cast(QF_TIMEEVT_CTR_SIZE << 4U)); +#else + QS::u8_raw_(static_cast(QF_TIMEEVT_CTR_SIZE << 4U)); +#endif // ifdef QF_EQUEUE_CTR_SIZE + +#ifdef QF_MPOOL_CTR_SIZE + QS::u8_raw_(QF_MPOOL_SIZ_SIZE + | static_cast(QF_MPOOL_CTR_SIZE << 4U)); +#else + QS::u8_raw_(0U); +#endif // ifdef QF_MPOOL_CTR_SIZE + + QS::u8_raw_(QS_OBJ_PTR_SIZE | (QS_FUN_PTR_SIZE << 4U)); + QS::u8_raw_(QS_TIME_SIZE); + + // send the limits... + QS::u8_raw_(QF_MAX_ACTIVE); + QS::u8_raw_(QF_MAX_EPOOL | (QF_MAX_TICK_RATE << 4U)); + + // send the build time in three bytes (sec, min, hour)... + QS::u8_raw_((10U * (TIME[6] - ZERO)) + (TIME[7] - ZERO)); + QS::u8_raw_((10U * (TIME[3] - ZERO)) + (TIME[4] - ZERO)); + if (BUILD_TIME[0] == static_cast(' ')) { + QS::u8_raw_(TIME[1] - ZERO); + } + else { + QS::u8_raw_((10U * (TIME[0] - ZERO)) + (TIME[1] - ZERO)); + } + + // send the build date in three bytes (day, month, year) ... + if (BUILD_DATE[4] == static_cast(' ')) { + QS::u8_raw_(DATE[5] - ZERO); + } + else { + QS::u8_raw_((10U * (DATE[4] - ZERO)) + (DATE[5] - ZERO)); + } + // convert the 3-letter month to a number 1-12 ... + std::uint8_t b; + switch (DATE[0] + DATE[1] + DATE[2]) { + case 'J' + 'a' +'n': + b = 1U; + break; + case 'F' + 'e' + 'b': + b = 2U; + break; + case 'M' + 'a' +'r': + b = 3U; + break; + case 'A' + 'p' + 'r': + b = 4U; + break; + case 'M' + 'a' + 'y': + b = 5U; + break; + case 'J' + 'u' + 'n': + b = 6U; + break; + case 'J' + 'u' + 'l': + b = 7U; + break; + case 'A' + 'u' + 'g': + b = 8U; + break; + case 'S' + 'e' + 'p': + b = 9U; + break; + case 'O' + 'c' + 't': + b = 10U; + break; + case 'N' + 'o' + 'v': + b = 11U; + break; + case 'D' + 'e' + 'c': + b = 12U; + break; + default: + b = 0U; + break; + } + QS::u8_raw_(b); // store the month + QS::u8_raw_((10U * (DATE[9] - ZERO)) + (DATE[10] - ZERO)); + QS::endRec_(); } +} // namespace QS } // namespace QP +//! @endcond diff --git a/libraries/qpcpp_arm-cm/src/qs.hpp b/libraries/qpcpp_arm-cm/src/qs.hpp index bd066e2..f646605 100644 --- a/libraries/qpcpp_arm-cm/src/qs.hpp +++ b/libraries/qpcpp_arm-cm/src/qs.hpp @@ -1,65 +1,72 @@ -/// @file -/// @brief QS/C++ platform-independent public interface. -/// @ingroup qs -/// @cond -///*************************************************************************** -/// Last updated for version 6.9.3 -/// Last updated on 2021-02-26 -/// -/// Q u a n t u m L e a P s -/// ------------------------ -/// Modern Embedded Software -/// -/// Copyright (C) 2005-2021 Quantum Leaps. All rights reserved. -/// -/// This program is open source software: you can redistribute it and/or -/// modify it under the terms of the GNU General Public License as published -/// by the Free Software Foundation, either version 3 of the License, or -/// (at your option) any later version. -/// -/// Alternatively, this program may be distributed and modified under the -/// terms of Quantum Leaps commercial licenses, which expressly supersede -/// the GNU General Public License and are specifically designed for -/// licensees interested in retaining the proprietary status of their code. -/// -/// This program is distributed in the hope that it will be useful, -/// but WITHOUT ANY WARRANTY; without even the implied warranty of -/// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -/// GNU General Public License for more details. -/// -/// You should have received a copy of the GNU General Public License -/// along with this program. If not, see . -/// -/// Contact information: -/// -/// -///*************************************************************************** -/// @endcond - -#ifndef QS_HPP -#define QS_HPP +//$file${include::qs.hpp} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv +// +// Model: qpcpp.qm +// File: ${include::qs.hpp} +// +// This code has been generated by QM 6.1.1 . +// DO NOT EDIT THIS FILE MANUALLY. All your changes will be lost. +// +// This code is covered by the following QP license: +// License # : LicenseRef-QL-dual +// Issued to : Any user of the QP/C++ real-time embedded framework +// Framework(s) : qpcpp +// Support ends : 2024-12-31 +// License scope: +// +// Copyright (C) 2005 Quantum Leaps, LLC . +// +// Q u a n t u m L e a P s +// ------------------------ +// Modern Embedded Software +// +// SPDX-License-Identifier: GPL-3.0-or-later OR LicenseRef-QL-commercial +// +// This software is dual-licensed under the terms of the open source GNU +// General Public License version 3 (or any later version), or alternatively, +// under the terms of one of the closed source Quantum Leaps commercial +// licenses. +// +// The terms of the open source GNU General Public License version 3 +// can be found at: +// +// The terms of the closed source Quantum Leaps commercial licenses +// can be found at: +// +// Redistributions in source code must retain this top-level comment block. +// Plagiarizing this software to sidestep the license obligations is illegal. +// +// Contact information: +// +// +// +//$endhead${include::qs.hpp} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +#ifndef QS_HPP_ +#define QS_HPP_ #ifndef Q_SPY - #error "Q_SPY must be defined to include qs.hpp" +#error "Q_SPY must be defined to include qs.hpp" #endif -//**************************************************************************** +//============================================================================ +//! @cond INTERNAL + +#ifndef QS_CTR_SIZE +#define QS_CTR_SIZE 2U +#endif + +#ifndef QS_TIME_SIZE +#define QS_TIME_SIZE 4U +#endif + +//! @endcond +//============================================================================ + +//$declare${QS::types} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv namespace QP { -//! Quantum Spy record types. -/// @description -/// This enumeration specifies the record types used in the QP components. -/// You can specify your own record types starting from QP::QS_USER offset. -/// Currently, the maximum of all records cannot exceed 125. -/// -/// @note -/// The QS records labeled as "not maskable" are always enabled and cannot -/// be turend off with the QS_GLB_FILTER() macro. Other QS trace records -/// can be disabled by means of the "global filters" -/// -/// @sa QS_GLB_FILTER() macro - -enum QSpyRecords : std::int8_t { +//${QS::types::QSpyPre} ...................................................... +//! pre-defined QS record IDs +enum QSpyPre : std::int8_t { // [0] QS session (not maskable) QS_EMPTY, //!< QS record for cleanly starting a session @@ -72,13 +79,13 @@ enum QSpyRecords : std::int8_t { 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 + QS_QEP_UNHANDLED, //!< an event was un-handled due to a guard // [10] Active Object (AO) records QS_QF_ACTIVE_DEFER, //!< AO deferred an event QS_QF_ACTIVE_RECALL, //!< AO recalled an event - QS_QF_ACTIVE_SUBSCRIBE, //!< an AO subscribed to an event - QS_QF_ACTIVE_UNSUBSCRIBE,//!< an AO unsubscribed to an event + QS_QF_ACTIVE_SUBSCRIBE, //!< an AO subscribed to an event + QS_QF_ACTIVE_UNSUBSCRIBE, //!< an AO unsubscribed to an event QS_QF_ACTIVE_POST, //!< an event was posted (FIFO) directly to AO QS_QF_ACTIVE_POST_LIFO, //!< an event was posted (LIFO) directly to AO QS_QF_ACTIVE_GET, //!< AO got an event and its queue is not empty @@ -91,7 +98,7 @@ enum QSpyRecords : std::int8_t { QS_QF_EQUEUE_GET, //!< get an event and queue still not empty QS_QF_EQUEUE_GET_LAST,//!< get the last event from the queue - // [23] Framework (QF) records */ + // [23] Framework (QF) records QS_QF_NEW_ATTEMPT, //!< an attempt to allocate an event failed // [24] Memory Pool (MP) records @@ -99,12 +106,12 @@ enum QSpyRecords : std::int8_t { QS_QF_MPOOL_PUT, //!< a memory block was returned to memory pool // [26] Additional Framework (QF) records - QS_QF_PUBLISH, //!< an event was published + QS_QF_PUBLISH, //!< an event was published to active objects QS_QF_NEW_REF, //!< new event reference was created QS_QF_NEW, //!< new event was created QS_QF_GC_ATTEMPT, //!< garbage collection attempt QS_QF_GC, //!< garbage collection - QS_QF_TICK, //!< QP::QF::tickX() was called + QS_QF_TICK, //!< QTimeEvt tick was called // [32] Time Event (TE) records QS_QF_TIMEEVT_ARM, //!< a time event was armed @@ -124,22 +131,24 @@ enum QSpyRecords : std::int8_t { QS_QF_INT_ENABLE, //!< interrupts were enabled // [45] Additional Active Object (AO) records - QS_QF_ACTIVE_POST_ATTEMPT, //!< attempt to post an evt to AO failed + QS_QF_ACTIVE_POST_ATTEMPT,//!< attempt to post an evt to AO failed // [46] Additional Event Queue (EQ) records - QS_QF_EQUEUE_POST_ATTEMPT, //!< attempt to post an evt to QEQueue failed + QS_QF_EQUEUE_POST_ATTEMPT,//!< attempt to post evt to QEQueue failed // [47] Additional Memory Pool (MP) records - QS_QF_MPOOL_GET_ATTEMPT, //!< attempt to get a memory block failed + QS_QF_MPOOL_GET_ATTEMPT, //!< attempt to get a memory block failed // [48] Scheduler (SC) records - QS_MUTEX_LOCK, //!< a mutex was locked - QS_MUTEX_UNLOCK, //!< a mutex was unlocked + QS_SCHED_PREEMPT, //!< scheduler asynchronously preempted a task + QS_SCHED_RESTORE, //!< scheduler restored preempted task QS_SCHED_LOCK, //!< scheduler was locked QS_SCHED_UNLOCK, //!< scheduler was unlocked - QS_SCHED_NEXT, //!< scheduler found next task to execute - QS_SCHED_IDLE, //!< scheduler became idle - QS_SCHED_RESUME, //!< scheduler resumed previous task (not idle) + QS_SCHED_NEXT, //!< scheduler started next task + QS_SCHED_IDLE, //!< scheduler restored the idle task + + // [54] Miscellaneous QS records (not maskable) + QS_ENUM_DICT, //!< enumeration dictionary entry // [55] Additional QEP records QS_QEP_TRAN_HIST, //!< a tran to history was taken @@ -161,937 +170,889 @@ enum QSpyRecords : std::int8_t { QS_ASSERT_FAIL, //!< assertion failed in the code QS_QF_RUN, //!< QF_run() was entered - // [71] Reserved QS records - QS_RESERVED_71, - QS_RESERVED_72, - QS_RESERVED_73, - QS_RESERVED_74, - QS_RESERVED_75, - QS_RESERVED_76, - QS_RESERVED_77, - QS_RESERVED_78, - QS_RESERVED_79, - QS_RESERVED_80, - QS_RESERVED_81, - QS_RESERVED_82, - QS_RESERVED_83, - QS_RESERVED_84, - QS_RESERVED_85, - QS_RESERVED_86, - QS_RESERVED_87, - QS_RESERVED_88, - QS_RESERVED_89, - QS_RESERVED_90, - QS_RESERVED_91, - QS_RESERVED_92, - QS_RESERVED_93, - QS_RESERVED_94, - QS_RESERVED_95, - QS_RESERVED_96, - QS_RESERVED_97, - QS_RESERVED_98, - QS_RESERVED_99, - - // [100] Application-specific (User) QS records - QS_USER //!< the first record available to QS users + // [71] Semaphore (SEM) records + QS_SEM_TAKE, //!< a semaphore was taken by a thread + QS_SEM_BLOCK, //!< a semaphore blocked a thread + QS_SEM_SIGNAL, //!< a semaphore was signaled + QS_SEM_BLOCK_ATTEMPT, //!< a semaphore blocked was attempted + + // [75] Mutex (MTX) records + QS_MTX_LOCK, //!< a mutex was locked + QS_MTX_BLOCK, //!< a mutex blocked a thread + QS_MTX_UNLOCK, //!< a mutex was unlocked + QS_MTX_LOCK_ATTEMPT, //!< a mutex lock was attempted + QS_MTX_BLOCK_ATTEMPT, //!< a mutex blocking was attempted + QS_MTX_UNLOCK_ATTEMPT,//!< a mutex unlock was attempted + + // [81] + QS_PRE_MAX //!< the # predefined signals }; -//! QS record groups for QS_GLB_FILTER() -enum QSpyRecordGroups : std::int16_t { - QS_ALL_RECORDS = static_cast(0xF0U), //!< all QS records - QS_SM_RECORDS, //!< State Machine QS records - QS_AO_RECORDS, //!< Active Object QS records - QS_EQ_RECORDS, //!< Event Queues QS records - QS_MP_RECORDS, //!< Memory Pools QS records - QS_TE_RECORDS, //!< Time Events QS records - QS_QF_RECORDS, //!< QF QS records - QS_SC_RECORDS, //!< Scheduler QS records - QS_U0_RECORDS, //!< User Group 100-104 records - QS_U1_RECORDS, //!< User Group 105-109 records - QS_U2_RECORDS, //!< User Group 110-114 records - QS_U3_RECORDS, //!< User Group 115-119 records - QS_U4_RECORDS, //!< User Group 120-124 records - QS_UA_RECORDS //!< All User records +//${QS::types::QSpyGroups} ................................................... +//! QS-TX record groups for QS_GLB_FILTER() +enum QSpyGroups : std::int16_t { + QS_ALL_RECORDS = 0xF0,//!< all maskable QS records + QS_SM_RECORDS, //!< State Machine QS records + QS_AO_RECORDS, //!< Active Object QS records + QS_EQ_RECORDS, //!< Event Queues QS records + QS_MP_RECORDS, //!< Memory Pools QS records + QS_TE_RECORDS, //!< Time Events QS records + QS_QF_RECORDS, //!< QF QS records + QS_SC_RECORDS, //!< Scheduler QS records + QS_SEM_RECORDS, //!< Semaphore QS records + QS_MTX_RECORDS, //!< Mutex QS records + QS_U0_RECORDS, //!< User Group 100-104 records + QS_U1_RECORDS, //!< User Group 105-109 records + QS_U2_RECORDS, //!< User Group 110-114 records + QS_U3_RECORDS, //!< User Group 115-119 records + QS_U4_RECORDS, //!< User Group 120-124 records + QS_UA_RECORDS //!< All User records }; +//${QS::types::QSpyUserOffsets} .............................................. //! QS user record group offsets for QS_GLB_FILTER() enum QSpyUserOffsets : std::int16_t { + QS_USER = 100, //!< the first record available to QS users QS_USER0 = QS_USER, //!< offset for User Group 0 - QS_USER1 = QS_USER0 + 5, //!< offset of Group 1 - QS_USER2 = QS_USER1 + 5, //!< offset of Group 2 - QS_USER3 = QS_USER2 + 5, //!< offset of Group 3 - QS_USER4 = QS_USER3 + 5, //!< offset of Group 4 + QS_USER1 = QS_USER0 + 5, //!< offset for User Group 1 + QS_USER2 = QS_USER1 + 5, //!< offset for User Group 2 + QS_USER3 = QS_USER2 + 5, //!< offset for User Group 3 + QS_USER4 = QS_USER3 + 5 //!< offset for User Group 4 }; +//${QS::types::QSpyIdOffsets} ................................................ //! QS ID offsets for QS_LOC_FILTER() enum QSpyIdOffsets : std::int16_t { QS_AO_ID = 0, //!< offset for AO priorities QS_EP_ID = 64, //!< offset for event-pool IDs QS_EQ_ID = 80, //!< offset for event-queue IDs - QS_AP_ID = 96, //!< offset for Appl-spec IDs + QS_AP_ID = 96 //!< offset for Application-specific IDs }; +//${QS::types::QSpyIdGroups} ................................................. //! QS ID groups for QS_LOC_FILTER() enum QSpyIdGroups : std::int16_t { QS_ALL_IDS = 0xF0, //!< all QS IDs QS_AO_IDS = 0x80 + QS_AO_ID, //!< AO IDs (priorities) QS_EP_IDS = 0x80 + QS_EP_ID, //!< event-pool IDs QS_EQ_IDS = 0x80 + QS_EQ_ID, //!< event-queue IDs - QS_AP_IDS = 0x80 + QS_AP_ID, //!< Application-specific IDs + QS_AP_IDS = 0x80 + QS_AP_ID //!< Application-specific IDs }; -//! QS ID type for applying local filtering +//${QS::types::QSpyId} ....................................................... struct QSpyId { - std::uint8_t m_prio; - std::uint_fast8_t getPrio(void) const noexcept { + std::uint8_t m_prio; //!< prio. (qsId) for the QS "local filter" + + // get the prio. (qsId) from the QSpyId object + std::uint_fast8_t getPrio() const noexcept { return static_cast(m_prio); } }; -} // namespace QP ************************************************************ +//${QS::types::QSObj} ........................................................ +#if (QS_OBJ_PTR_SIZE == 2U) +using QSObj = std::uint16_t; +#endif // (QS_OBJ_PTR_SIZE == 2U) + +//${QS::types::QSObj} ........................................................ +#if (QS_OBJ_PTR_SIZE == 4U) +using QSObj = std::uint32_t; +#endif // (QS_OBJ_PTR_SIZE == 4U) + +//${QS::types::QSObj} ........................................................ +#if (QS_OBJ_PTR_SIZE == 8U) +using QSObj = std::uint64_t; +#endif // (QS_OBJ_PTR_SIZE == 8U) + +//${QS::types::QSFun} ........................................................ +#if (QS_FUN_PTR_SIZE == 2U) +using QSFun = std::uint16_t; +#endif // (QS_FUN_PTR_SIZE == 2U) + +//${QS::types::QSFun} ........................................................ +#if (QS_FUN_PTR_SIZE == 4U) +using QSFun = std::uint32_t; +#endif // (QS_FUN_PTR_SIZE == 4U) + +//${QS::types::QSFun} ........................................................ +#if (QS_FUN_PTR_SIZE == 8U) +using QSFun = std::uint64_t; +#endif // (QS_FUN_PTR_SIZE == 8U) + +//${QS::types::QSpyFunPtr} ................................................... +using QSpyFunPtr = void (*)(); + +//${QS::types::QSCtr} ........................................................ +#if (QS_CTR_SIZE == 2U) +using QSCtr = std::uint16_t; +#endif // (QS_CTR_SIZE == 2U) + +//${QS::types::QSCtr} ........................................................ +#if (QS_CTR_SIZE == 4U) +using QSCtr = std::uint32_t; +#endif // (QS_CTR_SIZE == 4U) + +//${QS::types::QSTimeCtr} .................................................... +#if (QS_TIME_SIZE == 2U) +using QSTimeCtr = std::uint16_t; +#endif // (QS_TIME_SIZE == 2U) + +//${QS::types::QSTimeCtr} .................................................... +#if (QS_TIME_SIZE == 4U) +using QSTimeCtr = std::uint32_t; +#endif // (QS_TIME_SIZE == 4U) -#ifndef QS_TIME_SIZE +} // namespace QP +//$enddecl${QS::types} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +//$declare${QS::filters} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv +namespace QP { +namespace QS { - //! The size (in bytes) of the QS time stamp. Valid values: 1U, 2U, - //! or 4U; default 4U. - /// @description - /// This macro can be defined in the QS port file (qs_port.hpp) to - /// configure the QP::QSTimeCtr type. Here the macro is not defined so - /// the default of 4 byte is chosen. +//${QS::filters::Filter} ..................................................... +class Filter { +public: + std::uint8_t glb[16]; + std::uint8_t loc[16]; +}; // class Filter - #define QS_TIME_SIZE 4U -#endif - -#if (QS_TIME_SIZE == 1U) - #define QS_TIME_PRE_() (QP::QS::u8_raw_(QP::QS::onGetTime())) -#elif (QS_TIME_SIZE == 2U) - #define QS_TIME_PRE_() (QP::QS::u16_raw_(QP::QS::onGetTime())) -#elif (QS_TIME_SIZE == 4U) - //! Internal macro to output time stamp to a QS record - #define QS_TIME_PRE_() (QP::QS::u32_raw_(QP::QS::onGetTime())) -#else - #error "QS_TIME_SIZE defined incorrectly, expected 1U, 2U, or 4U" -#endif +//${QS::filters::filt_} ...................................................... +extern Filter filt_; +} // namespace QS +} // namespace QP +//$enddecl${QS::filters} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +//$declare${QS-macros} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv -//**************************************************************************** -namespace QP { +//${QS-macros::QS_INIT} ...................................................... +#define QS_INIT(arg_) (QP::QS::onStartup(arg_)) -#if (QS_TIME_SIZE == 1U) - using QSTimeCtr = std::uint8_t; -#elif (QS_TIME_SIZE == 2U) - using QSTimeCtr = std::uint16_t; -#elif (QS_TIME_SIZE == 4U) - //! The type of the QS time stamp. This type determines the dynamic - // range of QS time stamps - // - using QSTimeCtr = std::uint32_t; -#endif +//${QS-macros::QS_EXIT} ...................................................... +#define QS_EXIT() (QP::QS::onCleanup()) -//! QS ring buffer counter and offset type -using QSCtr = std::uint_fast16_t; +//${QS-macros::QS_OUTPUT} .................................................... +#define QS_OUTPUT() (QP::QS::doOutput()) -//! Constant representing End-Of-Data condition returned from the -//! QP::QS::getByte() function. -constexpr std::uint16_t QS_EOD = 0xFFFFU; +//${QS-macros::QS_RX_INPUT} .................................................. +#define QS_RX_INPUT() (QP::QS::doInput()) -//! QS logging facilities -/// @description -/// This class groups together QS services. It has only static members and -/// should not be instantiated. -class QS { -public: - //! Initialize the QS data buffer. - static void initBuf(std::uint8_t * const sto, - std::uint_fast16_t const stoSize) noexcept; - - //! Set/clear the global Filter for a given QS record - // or a group of records. - static void glbFilter_(std::int_fast16_t const filter) noexcept; +//${QS-macros::QS_GLB_FILTER} ................................................ +#define QS_GLB_FILTER(rec_) \ + (QP::QS::glbFilter_(static_cast(rec_))) - //! Set/clear the local Filter for a given object-id - // or a group of object-ids. - static void locFilter_(std::int_fast16_t const filter) noexcept; +//${QS-macros::QS_LOC_FILTER} ................................................ +#define QS_LOC_FILTER(qsId_) \ + (QP::QS::locFilter_(static_cast(qsId_))) + +//${QS-macros::QS_BEGIN_ID} .................................................. +#define QS_BEGIN_ID(rec_, qsId_) \ +if (QS_GLB_CHECK_(rec_) && QS_LOC_CHECK_(qsId_)) { \ + QS_CRIT_STAT \ + QS_CRIT_ENTRY(); \ + QS_MEM_SYS(); \ + QP::QS::beginRec_(static_cast(rec_)); \ + QS_TIME_PRE_(); { + +//${QS-macros::QS_END} ....................................................... +#define QS_END() } \ + QP::QS::endRec_(); \ + QS_MEM_APP(); \ + QS_CRIT_EXIT(); \ +} - //! Mark the begin of a QS record @p rec - static void beginRec_(std::uint_fast8_t const rec) noexcept; +//${QS-macros::QS_FLUSH} ..................................................... +#define QS_FLUSH() (QP::QS::onFlush()) - //! Mark the end of a QS record @p rec - static void endRec_(void) noexcept; +//${QS-macros::QS_BEGIN_INCRIT} .............................................. +#define QS_BEGIN_INCRIT(rec_, qsId_) \ +if (QS_GLB_CHECK_(rec_) && QS_LOC_CHECK_(qsId_)) { \ + QP::QS::beginRec_(rec_); \ + QS_TIME_PRE_(); { - // raw (unformatted) output of data elements ............................. +//${QS-macros::QS_END_INCRIT} ................................................ +#define QS_END_INCRIT() } \ + QP::QS::endRec_(); \ +} - //! output std::uint8_t data element without format information - static void u8_raw_(std::uint8_t const d) noexcept; +//${QS-macros::QS_GLB_CHECK_} ................................................ +#define QS_GLB_CHECK_(rec_) \ +((static_cast(QP::QS::filt_.glb[ \ + static_cast(rec_) >> 3U]) \ + & (static_cast(1U) \ + << (static_cast(rec_) & 7U))) != 0U) + +//${QS-macros::QS_LOC_CHECK_} ................................................ +#define QS_LOC_CHECK_(qsId_) \ +((static_cast(QP::QS::filt_.loc \ + [static_cast(qsId_) >> 3U]) \ + & (static_cast(1U) \ + << (static_cast(qsId_) & 7U))) != 0U) + +//${QS-macros::QS_REC_DONE} .................................................. +#ifndef QS_REC_DONE +#define QS_REC_DONE() (static_cast(0)) +#endif // ndef QS_REC_DONE + +//${QS-macros::QS_I8} ........................................................ +#define QS_I8(width_, data_) \ +(QP::QS::u8_fmt_(static_cast( \ + (static_cast(((width_) << 4U) & 0x7U)) \ + | static_cast(QP::QS::I8_ENUM_T)), (data_))) + +//${QS-macros::QS_U8} ........................................................ +#define QS_U8(width_, data_) \ +(QP::QS::u8_fmt_(static_cast( \ + (static_cast((width_) << 4U)) \ + | static_cast(QP::QS::U8_T)), (data_))) + +//${QS-macros::QS_I16} ....................................................... +#define QS_I16(width_, data_) \ +(QP::QS::u16_fmt_(static_cast( \ + (static_cast((width_) << 4U)) \ + | static_cast(QP::QS::I16_T)), (data_))) + +//${QS-macros::QS_U16} ....................................................... +#define QS_U16(width_, data_) \ +(QP::QS::u16_fmt_(static_cast((((width_) << 4U)) \ + | static_cast(QP::QS::U16_T)), (data_))) + +//${QS-macros::QS_I32} ....................................................... +#define QS_I32(width_, data_) \ +(QP::QS::u32_fmt_( \ + static_cast((static_cast((width_) << 4U)) \ + | static_cast(QP::QS::I32_T)), (data_))) + +//${QS-macros::QS_U32} ....................................................... +#define QS_U32(width_, data_) \ +(QP::QS::u32_fmt_(static_cast( \ + (static_cast((width_) << 4U)) \ + | static_cast(QP::QS::U32_T)), (data_))) + +//${QS-macros::QS_I64} ....................................................... +#define QS_I64(width_, data_) \ +(QP::QS::u64_fmt_(static_cast( \ + (static_cast((width_) << 4U)) \ + | static_cast(QP::QS::I64_T)), (data_))) + +//${QS-macros::QS_U64} ....................................................... +#define QS_U64(width_, data_) \ +(QP::QS::u64_fmt_(static_cast( \ + (static_cast((width_) << 4U)) \ + | static_cast(QP::QS::U64_T)), (data_))) + +//${QS-macros::QS_F32} ....................................................... +#define QS_F32(width_, data_) \ +(QP::QS::f32_fmt_(static_cast( \ + (static_cast((width_) << 4U)) \ + | static_cast(QP::QS::F32_T)), (data_))) + +//${QS-macros::QS_F64} ....................................................... +#define QS_F64(width_, data_) \ +(QP::QS::f64_fmt_(static_cast( \ + (static_cast((width_) << 4U)) \ + | static_cast(QP::QS::F64_T)), (data_))) + +//${QS-macros::QS_STR} ....................................................... +#define QS_STR(str_) (QP::QS::str_fmt_(str_)) + +//${QS-macros::QS_MEM} ....................................................... +#define QS_MEM(mem_, size_) (QP::QS::mem_fmt_((mem_), (size_))) - //! output two std::uint8_t data elements without format information - static void u8u8_raw_(std::uint8_t const d1, - std::uint8_t const d2) noexcept; +//${QS-macros::QS_ENUM} ...................................................... +#define QS_ENUM(group_, value_) \ + (QP::QS::u8_fmt_(static_cast(0x80U | ((group_) << 4U)) \ + | static_cast(QP::QS::I8_ENUM_T),\ + static_cast(value_))) + +//${QS-macros::QS_TIME_PRE_} ................................................. +#if (QS_TIME_SIZE == 2U) +#define QS_TIME_PRE_() (QP::QS::u16_raw_(QP::QS::onGetTime())) +#endif // (QS_TIME_SIZE == 2U) + +//${QS-macros::QS_TIME_PRE_} ................................................. +#if (QS_TIME_SIZE == 4U) +#define QS_TIME_PRE_() (QP::QS::u32_raw_(QP::QS::onGetTime())) +#endif // (QS_TIME_SIZE == 4U) + +//${QS-macros::QS_OBJ} ....................................................... +#if (QS_OBJ_PTR_SIZE == 2U) +#define QS_OBJ(obj_) (QP::QS::u16_fmt_(QP::QS::OBJ_T, \ + reinterpret_cast(obj_))) +#endif // (QS_OBJ_PTR_SIZE == 2U) - //! Output std::uint16_t data element without format information - static void u16_raw_(std::uint16_t d) noexcept; +//${QS-macros::QS_OBJ} ....................................................... +#if (QS_OBJ_PTR_SIZE == 4U) +#define QS_OBJ(obj_) (QP::QS::u32_fmt_(QP::QS::OBJ_T, \ + reinterpret_cast(obj_))) +#endif // (QS_OBJ_PTR_SIZE == 4U) - //! Output std::uint32_t data element without format information - static void u32_raw_(std::uint32_t d) noexcept; +//${QS-macros::QS_OBJ} ....................................................... +#if (QS_OBJ_PTR_SIZE == 8U) +#define QS_OBJ(obj_) (QP::QS::u64_fmt_(QP::QS::OBJ_T, \ + reinterpret_cast(obj_))) +#endif // (QS_OBJ_PTR_SIZE == 8U) - //! Output obj pointer data element without format information - static void obj_raw_(void const * const obj) noexcept; +//${QS-macros::QS_FUN} ....................................................... +#if (QS_FUN_PTR_SIZE == 2U) +#define QS_FUN(fun_) (QP::QS::u16_fmt_(QP::QS::FUN_T, \ + reinterpret_cast(fun_))) +#endif // (QS_FUN_PTR_SIZE == 2U) - //! Output zero-terminated ASCII string element without format information - static void str_raw_(char_t const *s) noexcept; +//${QS-macros::QS_FUN} ....................................................... +#if (QS_FUN_PTR_SIZE == 4U) +#define QS_FUN(fun_) (QP::QS::u32_fmt_(QP::QS::FUN_T, \ + reinterpret_cast(fun_))) +#endif // (QS_FUN_PTR_SIZE == 4U) +//${QS-macros::QS_FUN} ....................................................... +#if (QS_FUN_PTR_SIZE == 8U) +#define QS_FUN(fun_) (QP::QS::u64_fmt_(QP::QS::FUN_T, \ + reinterpret_cast(fun_))) +#endif // (QS_FUN_PTR_SIZE == 8U) - // formatted data elements output ........................................ +//${QS-macros::QS_SIG} ....................................................... +#if (Q_SIGNAL_SIZE == 1U) +#define QS_SIG(sig_, obj_) \ + QP::QS::u8_fmt_(QP::QS::SIG_T, static_cast(sig_)); \ + QP::QS::obj_raw_(obj_) +#endif // (Q_SIGNAL_SIZE == 1U) + +//${QS-macros::QS_SIG} ....................................................... +#if (Q_SIGNAL_SIZE == 2U) +#define QS_SIG(sig_, obj_) \ + QP::QS::u16_fmt_(QP::QS::SIG_T, static_cast(sig_)); \ + QP::QS::obj_raw_(obj_) +#endif // (Q_SIGNAL_SIZE == 2U) + +//${QS-macros::QS_SIG} ....................................................... +#if (Q_SIGNAL_SIZE == 4U) +#define QS_SIG(sig_, obj_) \ + QP::QS::u32_fmt_(QP::QS::SIG_T, static_cast(sig_)); \ + QP::QS::obj_raw_(obj_) +#endif // (Q_SIGNAL_SIZE == 4U) + +//${QS-macros::QS_SIG_DICTIONARY} ............................................ +#define QS_SIG_DICTIONARY(sig_, obj_) \ + (QP::QS::sig_dict_pre_((sig_), (obj_), #sig_)) - //! Output std::uint8_t data element with format information - static void u8_fmt_(std::uint8_t const format, - std::uint8_t const d) noexcept; +//${QS-macros::QS_OBJ_DICTIONARY} ............................................ +#define QS_OBJ_DICTIONARY(obj_) \ + (QP::QS::obj_dict_pre_((obj_), #obj_)) - //! output std::uint16_t data element with format information - static void u16_fmt_(std::uint8_t format, std::uint16_t d) noexcept; +//${QS-macros::QS_OBJ_ARR_DICTIONARY} ........................................ +#define QS_OBJ_ARR_DICTIONARY(obj_, idx_) \ + (QP::QS::obj_arr_dict_pre_((obj_), (idx_), #obj_)) - //! Output std::uint32_t data element with format information - static void u32_fmt_(std::uint8_t format, std::uint32_t d) noexcept; +//${QS-macros::QS_FUN_DICTIONARY} ............................................ +#define QS_FUN_DICTIONARY(fun_) \ + (QP::QS::fun_dict_pre_( \ + QP::QS::force_cast(fun_), #fun_)) - //! Output 32-bit floating point data element with format information - static void f32_fmt_(std::uint8_t format, float32_t const d) noexcept; +//${QS-macros::QS_USR_DICTIONARY} ............................................ +#define QS_USR_DICTIONARY(rec_) \ + (QP::QS::usr_dict_pre_((rec_), #rec_)) - //! Output 64-bit floating point data element with format information - static void f64_fmt_(std::uint8_t format, float64_t const d) noexcept; +//${QS-macros::QS_ENUM_DICTIONARY} ........................................... +#define QS_ENUM_DICTIONARY(value_, group_) \ + (QP::QS::enum_dict_pre_((value_), (group_), #value_)) - //! Output zero-terminated ASCII string element with format information - static void str_fmt_(char_t const *s) noexcept; +//${QS-macros::QS_QF_CRIT_ENTRY} ............................................. +#define QS_QF_CRIT_ENTRY() (QP::QS::crit_entry_pre_()) - //! Output memory block of up to 255-bytes with format information - static void mem_fmt_(std::uint8_t const *blk, std::uint8_t size) noexcept; +//${QS-macros::QS_QF_CRIT_EXIT} .............................................. +#define QS_QF_CRIT_EXIT() (QP::QS::crit_exit_pre_()) - //! Output uint64_t data element without format information - static void u64_raw_(std::uint64_t d) noexcept; +//${QS-macros::QS_QF_ISR_ENTRY} .............................................. +#define QS_QF_ISR_ENTRY(isrnest_, prio_) \ + (QP::QS::isr_entry_pre_((isrnest_), (prio_))) - //! Output uint64_t data element with format information - static void u64_fmt_(std::uint8_t format, std::uint64_t d) noexcept; +//${QS-macros::QS_QF_ISR_EXIT} ............................................... +#define QS_QF_ISR_EXIT(isrnest_, prio_) \ + (QP::QS::isr_exit_pre_((isrnest_), (prio_))) - //! Output signal dictionary record - static void sig_dict_pre_(enum_t const sig, void const * const obj, - char_t const *name) noexcept; +//${QS-macros::QS_ONLY} ...................................................... +#define QS_ONLY(code_) (code_) - //! Output object dictionary record - static void obj_dict_pre_(void const * const obj, - char_t const *name) noexcept; +//${QS-macros::QS_ASSERTION} ................................................. +#define QS_ASSERTION(module_, id_, delay_) \ + (QP::QS::assertion_pre_((module_), (id_), (delay_))) - //! Output function dictionary record - static void fun_dict_pre_(void (* const fun)(void), - char_t const *name) noexcept; +//${QS-macros::QS_EOD} ....................................................... +#define QS_EOD (static_cast(0xFFFFU)) - //! Output user dictionary record - static void usr_dict_pre_(enum_t const rec, - char_t const * const name) noexcept; +//${QS-macros::QS_CMD} ....................................................... +#define QS_CMD (static_cast(7U)) - //! Initialize the QS RX data buffer - static void rxInitBuf(std::uint8_t * const sto, - std::uint16_t const stoSize) noexcept; +//${QS-macros::QS_HEX_FMT} ................................................... +#define QS_HEX_FMT (static_cast(0x0FU)) - //! Parse all bytes present in the QS RX data buffer - static void rxParse(void); +//${QS-macros::QS_CRIT_STAT} ................................................. +#ifndef QS_CRIT_STAT +#define QS_CRIT_STAT QF_CRIT_STAT +#endif // ndef QS_CRIT_STAT - //! Obtain the number of free bytes in the QS RX data buffer - static std::uint16_t rxGetNfree(void) noexcept; +//${QS-macros::QS_CRIT_ENTRY} ................................................ +#ifndef QS_CRIT_ENTRY +#define QS_CRIT_ENTRY() QF_CRIT_ENTRY() +#endif // ndef QS_CRIT_ENTRY - //! Put one byte into the QS RX lock-free buffer - static bool rxPut(std::uint8_t const b) noexcept; +//${QS-macros::QS_CRIT_EXIT} ................................................. +#ifndef QS_CRIT_EXIT +#define QS_CRIT_EXIT() QF_CRIT_EXIT() +#endif // ndef QS_CRIT_EXIT - //! Set the "current object" in the Target - static void setCurrObj(std::uint8_t obj_kind, void *obj_ptr) noexcept; +//${QS-macros::QS_MEM_SYS} ................................................... +#ifndef QS_MEM_SYS +#define QS_MEM_SYS() QF_MEM_SYS() +#endif // ndef QS_MEM_SYS - //! Query the "current object" in the Target - static void queryCurrObj(std::uint8_t obj_kind) noexcept; +//${QS-macros::QS_MEM_APP} ................................................... +#ifndef QS_MEM_APP +#define QS_MEM_APP() QF_MEM_APP() +#endif // ndef QS_MEM_APP +//$enddecl${QS-macros} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - // QS buffer access ...................................................... - //! Byte-oriented interface to the QS data buffer. - static std::uint16_t getByte(void) noexcept; +//============================================================================ +//! @cond INTERNAL - //! Block-oriented interface to the QS data buffer. - static std::uint8_t const *getBlock( - std::uint16_t * const pNbytes) noexcept; +namespace QP { +namespace QS { + +struct Attr { + void const * locFilter_AP; //!< @deprecated + std::uint8_t * buf; + QSCtr end; + QSCtr volatile head; + QSCtr volatile tail; + QSCtr volatile used; + std::uint8_t volatile seq; + std::uint8_t volatile chksum; + std::uint8_t volatile critNest; + std::uint8_t flags; +}; - // platform-dependent callback functions to be implemented by clients .... +extern Attr priv_; + +void glbFilter_(std::int_fast16_t const filter) noexcept; +void locFilter_(std::int_fast16_t const filter) noexcept; + +void beginRec_(std::uint_fast8_t const rec) noexcept; +void endRec_() noexcept; + +void u8_raw_(std::uint8_t const d) noexcept; +void u8u8_raw_(std::uint8_t const d1, std::uint8_t const d2) noexcept; +void u16_raw_(std::uint16_t d) noexcept; +void u32_raw_(std::uint32_t d) noexcept; +void u64_raw_(std::uint64_t d) noexcept; +void obj_raw_(void const * const obj) noexcept; +void str_raw_(char const * s) noexcept; + +void u8_fmt_(std::uint8_t const format, std::uint8_t const d) noexcept; +void u16_fmt_(std::uint8_t format, std::uint16_t d) noexcept; +void u32_fmt_(std::uint8_t format, + std::uint32_t d) noexcept; +void u64_fmt_(std::uint8_t format, std::uint64_t d) noexcept; +void f32_fmt_(std::uint8_t format, float32_t f) noexcept; +void f64_fmt_(std::uint8_t format, float64_t d) noexcept; +void str_fmt_(char const * s) noexcept; +void mem_fmt_(std::uint8_t const * blk, std::uint8_t size) noexcept; + +void sig_dict_pre_(QSignal const sig, void const * const obj, + char const * const name) noexcept; +void obj_dict_pre_(void const * const obj, + char const * const name) noexcept; +void obj_arr_dict_pre_(void const * const obj, + std::uint_fast16_t const idx, + char const * const name) noexcept; +void fun_dict_pre_(QSpyFunPtr fun, + char const * const name) noexcept; +void usr_dict_pre_(enum_t const rec, char const * const name) noexcept; +void enum_dict_pre_(enum_t const value, std::uint8_t const group, + char const * const name) noexcept; + +void assertion_pre_(char const * const module, int_t const id, + std::uint32_t const delay) noexcept; +void crit_entry_pre_() noexcept; +void crit_exit_pre_() noexcept; +void isr_entry_pre_(std::uint8_t const isrnest, + std::uint8_t const prio) noexcept; +void isr_exit_pre_(std::uint8_t const isrnest, + std::uint8_t const prio) noexcept; + +void target_info_pre_(std::uint8_t const isReset); + +} // namespace QS +} // namespace QP - //! Callback to startup the QS facility - static bool onStartup(void const *arg); +//! @endcond +//============================================================================ - //! Callback to cleanup the QS facility - static void onCleanup(void); +//$declare${QS::QS-TX} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv +namespace QP { +namespace QS { + +//${QS::QS-TX::preType} ...................................................... +//! Enumerates data elements for app-specific trace records +enum preType : std::uint8_t { + I8_ENUM_T, //!< signed 8-bit integer or enum format + U8_T, //!< unsigned 8-bit integer format + I16_T, //!< signed 16-bit integer format + U16_T, //!< unsigned 16-bit integer format + I32_T, //!< signed 32-bit integer format + U32_T, //!< unsigned 32-bit integer format + F32_T, //!< 32-bit floating point format + F64_T, //!< 64-bit floating point format + STR_T, //!< zero-terminated ASCII string format + MEM_T, //!< up to 255-bytes memory block format + SIG_T, //!< event signal format + OBJ_T, //!< object pointer format + FUN_T, //!< function pointer format + I64_T, //!< signed 64-bit integer format + U64_T //!< unsigned 64-bit integer format +}; - //! Callback to flush the QS trace data to the host - static void onFlush(void); +//${QS::QS-TX::force_cast} ................................................... +template +inline T_OUT force_cast(T_IN in) { + union TCast { + T_IN in; + T_OUT out; + } u = { in }; + return u.out; +} - //! Callback to obtain a timestamp for a QS record. - static QSTimeCtr onGetTime(void); +//${QS::QS-TX::initBuf} ...................................................... +void initBuf( + std::uint8_t * const sto, + std::uint_fast32_t const stoSize) noexcept; - //! callback function to reset the Target (to be implemented in the BSP) - static void onReset(void); +//${QS::QS-TX::getByte} ...................................................... +std::uint16_t getByte() noexcept; - //! Callback function to execute user commands (to be implemented in BSP) - static void onCommand(std::uint8_t cmdId, - std::uint32_t param1, - std::uint32_t param2, - std::uint32_t param3); +//${QS::QS-TX::getBlock} ..................................................... +std::uint8_t const * getBlock(std::uint16_t * const pNbytes) noexcept; - //! internal function to handle incoming (QS-RX) packet - static void rxHandleGoodFrame_(std::uint8_t const state); +//${QS::QS-TX::doOutput} ..................................................... +void doOutput(); - //! internal function to produce the assertion failure trace record - static void assertion_pre_(char_t const * const module, int_t const loc, - std::uint32_t delay); +//${QS::QS-TX::onStartup} .................................................... +bool onStartup(void const * arg); - //! internal function to produce the critical section entry record - static void crit_entry_pre_(void); +//${QS::QS-TX::onCleanup} .................................................... +void onCleanup(); - //! internal function to produce the critical section exit record - static void crit_exit_pre_(void); +//${QS::QS-TX::onFlush} ...................................................... +void onFlush(); - //! internal function to produce the ISR entry record - static void isr_entry_pre_(std::uint8_t const isrnest, - std::uint8_t const prio); +//${QS::QS-TX::onGetTime} .................................................... +QSTimeCtr onGetTime(); - //! internal function to produce the ISR exit record - static void isr_exit_pre_(std::uint8_t const isrnest, - std::uint8_t const prio); +} // namespace QS +} // namespace QP +//$enddecl${QS::QS-TX} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +//============================================================================ #ifdef Q_UTEST - //! callback to setup a unit test inside the Target - static void onTestSetup(void); - //! callback to teardown after a unit test inside the Target - static void onTestTeardown(void); +//$declare${QS::QUTest} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv +namespace QP { +namespace QS { - //! callback to "massage" the test event before dispatching/posting it - static void onTestEvt(QEvt *e); +//${QS::QUTest::TProbe} ...................................................... +struct TProbe { + QSFun addr; + std::uint32_t data; + std::uint8_t idx; +}; - // callback to examine an event that is about to be posted - static void onTestPost(void const *sender, QActive *recipient, - QEvt const *e, bool status); +//${QS::QUTest::onTestSetup} ................................................. +void onTestSetup(); - //! callback to run the test loop - static void onTestLoop(void); +//${QS::QUTest::onTestTeardown} .............................................. +void onTestTeardown(); - //! internal function to process posted events during test - static void processTestEvts_(void); +//${QS::QUTest::onTestEvt} ................................................... +void onTestEvt(QEvt * e); - //! internal function to process armed time events during test - static void tickX_(std::uint_fast8_t const tickRate, - void const * const sender) noexcept; +//${QS::QUTest::onTestPost} .................................................. +void onTestPost( + void const * sender, + QActive * recipient, + QEvt const * e, + bool status); - //! internal function to get the Test-Probe for a given API - static std::uint32_t getTestProbe_(void (* const api)(void)) noexcept; +//${QS::QUTest::onTestLoop} .................................................. +void onTestLoop(); -#endif // Q_UTEST +} // namespace QS +} // namespace QP +//$enddecl${QS::QUTest} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - //! Enumerates data formats recognized by QS - /// @description - /// QS uses this enumeration is used only internally for the formatted - /// user data elements. - enum QSType : std::uint8_t { - I8_T, //!< signed 8-bit integer format - U8_T, //!< unsigned 8-bit integer format - I16_T, //!< signed 16-bit integer format - U16_T, //!< unsigned 16-bit integer format - I32_T, //!< signed 32-bit integer format - U32_T, //!< unsigned 32-bit integer format - F32_T, //!< 32-bit floating point format - F64_T, //!< 64-bit floating point format - STR_T, //!< zero-terminated ASCII string format - MEM_T, //!< up to 255-bytes memory block format - SIG_T, //!< event signal format - OBJ_T, //!< object pointer format - FUN_T, //!< function pointer format - I64_T, //!< signed 64-bit integer format - U64_T, //!< unsigned 64-bit integer format - HEX_FMT //!< HEX format for the "width" filed - }; - - //! Kinds of objects used in QS - enum QSpyObjKind : std::uint8_t { - SM_OBJ, //!< state machine object for QEP - AO_OBJ, //!< active object - MP_OBJ, //!< event pool object - EQ_OBJ, //!< raw queue object - TE_OBJ, //!< time event object - AP_OBJ, //!< generic Application-specific object - MAX_OBJ - }; - - enum OSpyObjCombnation : std::uint8_t { - SM_AO_OBJ = MAX_OBJ //!< combination of SM and AO - }; - - //! template for forcing cast of member functions for function - //! dictionaries and test probes. - template - static T_OUT force_cast(T_IN in) { - union TCast { - T_IN in; - T_OUT out; - } u = { in }; - return u.out; - } +#define QUTEST_ON_POST 124 - // private QS attributes ................................................. - std::uint8_t glbFilter[16]; //!< global on/off QS filter - std::uint8_t locFilter[16]; //!< lobal on/off QS filter - void const *locFilter_AP; //!< deprecated local QS filter - std::uint8_t *buf; //!< pointer to the start of the ring buffer - QSCtr end; //!< offset of the end of the ring buffer - QSCtr head; //!< offset to where next byte will be inserted - QSCtr tail; //!< offset of where next record will be extracted - QSCtr used; //!< number of bytes currently in the ring buffer - std::uint8_t seq; //!< the record sequence number - std::uint8_t chksum; //!< the checksum of the current record - std::uint8_t full; //!< the ring buffer is temporarily full - - std::uint_fast8_t critNest; //!< critical section nesting level - - static QS priv_; - - static struct QSrxPriv { - void *currObj[MAX_OBJ]; //!< current objects - std::uint8_t *buf; //!< pointer to the start of the ring buffer - QSCtr end; //!< offset of the end of the ring buffer - QSCtr head; //!< offset to where next byte will be inserted - QSCtr tail; //!< offset of where next byte will be extracted -#ifdef Q_UTEST - QP::QPSet readySet; //!< QUTEST ready-set of active objects - bool inTestLoop; //!< QUTest event loop is running -#endif - } rxPriv_; -}; +//============================================================================ +//! @cond INTERNAL -//**************************************************************************** -// QS receive channel - -//! Quantum Spy Receive (RX) record types -/// @description -/// This enumeration specifies the record types for the QS receive channel -enum QSpyRxRecords : std::uint8_t { - QS_RX_INFO, //!< query Target info (ver, config, tstamp) - QS_RX_COMMAND, //!< execute a user-defined command in the Target - QS_RX_RESET, //!< reset the Target - QS_RX_TICK, //!< call QF_tick() - QS_RX_PEEK, //!< peek Target memory - QS_RX_POKE, //!< poke Target memory - QS_RX_FILL, //!< fill Target memory - QS_RX_TEST_SETUP, //!< test setup - QS_RX_TEST_TEARDOWN, //!< test teardown - QS_RX_TEST_PROBE, //!< set a Test-Probe in the Target - QS_RX_GLB_FILTER, //!< set global filters in the Target - QS_RX_LOC_FILTER, //!< set local filters in the Target - QS_RX_AO_FILTER, //!< set local AO filter in the Target - QS_RX_CURR_OBJ, //!< set the "current-object" in the Target - QS_RX_TEST_CONTINUE, //!< continue a test after QS_RX_TEST_WAIT() - QS_RX_QUERY_CURR, //!< query the "current object" in the Target - QS_RX_EVENT //!< inject an event to the Target (post/publish) +namespace QP { +namespace QS { + +struct TestAttr { + TProbe tpBuf[16]; + std::uint8_t tpNum; + QSTimeCtr testTime; + QPSet readySet; + QPSet readySet_dis; + std::uint_fast8_t intLock; }; -//! put one byte into the QS RX lock-free buffer -inline bool QS::rxPut(std::uint8_t const b) noexcept { - QSCtr head = rxPriv_.head + 1U; - if (head == rxPriv_.end) { - head = 0U; - } - if (head != rxPriv_.tail) { // buffer NOT full? - rxPriv_.buf[rxPriv_.head] = b; - rxPriv_.head = head; - return true; // byte placed in the buffer - } - else { - return false; // byte NOT placed in the buffer - } -} +extern TestAttr tstPriv_; +void test_pause_(); +std::uint32_t getTestProbe_(QSpyFunPtr const api) noexcept; -//**************************************************************************** -#ifdef Q_UTEST +} // namespace QS +} // namespace QP -//! Dummy Active Object class -/// @description -/// QActiveDummy is a test double for the role of collaborating active -/// objects in QUTest unit testing. -/// -class QActiveDummy : public QActive { -public: - QActiveDummy(void); // ctor - - void start(std::uint_fast8_t const prio, - QEvt const * * const qSto, std::uint_fast16_t const qLen, - void * const stkSto, std::uint_fast16_t const stkSize, - void const * const par) override; - - //! Overloaded start function (no initialization event) - void start(std::uint_fast8_t const prio, - QEvt const * * const qSto, std::uint_fast16_t const qLen, - void * const stkSto, std::uint_fast16_t const stkSize) override - { - this->start(prio, qSto, qLen, stkSto, stkSize, nullptr); - } +//! @endcond +//============================================================================ - void init(void const * const e, - std::uint_fast8_t const qs_id) noexcept override; - void init(std::uint_fast8_t const qs_id) noexcept override { - this->init(qs_id); - } - void dispatch(QEvt const * const e, - std::uint_fast8_t const qs_id) noexcept override; - bool post_(QEvt const * const e, - std::uint_fast16_t const margin, - void const * const sender) noexcept override; - void postLIFO(QEvt const * const e) noexcept override; -}; +// QP-stub for QUTest +// NOTE: The QP-stub is needed for unit testing QP applications, +// but might NOT be needed for testing QP itself. +#if (Q_UTEST != 0) + +//$declare${QS::QUTest-stub::QHsmDummy} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv +namespace QP { -constexpr std::uint8_t QUTEST_ON_POST {124U}; +//${QS::QUTest-stub::QHsmDummy} .............................................. +class QHsmDummy : public QP::QAsm { +public: + QHsmDummy(); + void init( + void const * const e, + std::uint_fast8_t const qsId) override; + void dispatch( + QEvt const * const e, + std::uint_fast8_t const qsId) override; +}; // class QHsmDummy -// interrupt nesting up-down counter -extern std::uint8_t volatile QF_intNest; +} // namespace QP +//$enddecl${QS::QUTest-stub::QHsmDummy} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +//$declare${QS::QUTest-stub::QActiveDummy} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv +namespace QP { -#endif // Q_UTEST +//${QS::QUTest-stub::QActiveDummy} ........................................... +class QActiveDummy : public QP::QActive { +private: + friend class QActive; + +public: + QActiveDummy(); + void init( + void const * const e, + std::uint_fast8_t const qsId) override; + void dispatch( + QEvt const * const e, + std::uint_fast8_t const qsId) override; + +private: + bool fakePost( + QEvt const * const e, + std::uint_fast16_t const margin, + void const * const sender) noexcept; + void fakePostLIFO(QEvt const * const e) noexcept; +}; // class QActiveDummy } // namespace QP +//$enddecl${QS::QUTest-stub::QActiveDummy} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -//**************************************************************************** -// Macros for adding QS instrumentation to the client code - -//! Initialize the QS facility. -/// @description -/// This macro provides an indirection layer to invoke the QS initialization -/// routine if #Q_SPY is defined, or do nothing if #Q_SPY is not defined. -/// @sa QP::QS::onStartup(), example of setting up a QS filter in -/// QS_GLB_FILTER() -#define QS_INIT(arg_) (QP::QS::onStartup(arg_)) - -//! Cleanup the QS facility. -/// @description -/// This macro provides an indirection layer to invoke the QS cleanup -/// routine if #Q_SPY is defined, or do nothing if #Q_SPY is not defined. -/// @sa QP::QS::onCleanup() -#define QS_EXIT() (QP::QS::onCleanup()) - -//! Global Filter ON for a given record type @p rec. -/// @description -/// This macro provides an indirection layer to call QP::QS::filterOn() -/// if #Q_SPY is defined, or do nothing if #Q_SPY is not defined. -/// -/// @usage -/// The following example shows how to use QS filters: -/// @include qs_filter.cpp -#define QS_GLB_FILTER(rec_) \ - (QP::QS::glbFilter_(static_cast(rec_))) +#endif // Q_UTEST != 0 -//! Local Filter for a given state machine object @p qs_id. -/// @description -/// This macro provides an indirection layer to call QS_locFilter_() -/// 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_LOC_FILTER(qs_id_) \ - (QP::QS::locFilter_(static_cast(qs_id_))) - - -//**************************************************************************** -// Macros to generate application-specific (user) QS records - -//! Begin a user QS record with entering critical section. -/// @description -/// The following example shows how to build a user QS record using the -/// macros QS_BEGIN_ID(), QS_END(), and the formatted output macros: -/// QS_U8(), QS_STR(), etc. -/// -/// @note -/// Must always be used in pair with QS_END() -/// -/// @include qs_user.cpp -#define QS_BEGIN_ID(rec_, qs_id_) \ - if (QS_GLB_CHECK_(rec_) && QS_LOC_CHECK_(qs_id_)) { \ - QS_CRIT_STAT_ \ - QS_CRIT_E_(); \ - QP::QS::beginRec_(static_cast(rec_)); \ - QS_TIME_PRE_(); - -//! End a QS record with exiting critical section. -/// @sa example for QS_BEGIN_ID() -/// @note Must always be used in pair with QS_BEGIN_ID() -#define QS_END() \ - QP::QS::endRec_(); \ - QS_CRIT_X_(); \ - } +#define QS_TEST_PROBE_DEF(fun_) \ + std::uint32_t const qs_tp_ = \ + QP::QS::getTestProbe_(QP::QS::force_cast(fun_)); -//! Begin a QS user record without entering critical section. -#define QS_BEGIN_NOCRIT(rec_, qs_id_) \ - if (QS_GLB_CHECK_(rec_) && QS_LOC_CHECK_(qs_id_)) { \ - QP::QS::beginRec_(rec_); \ - QS_TIME_PRE_(); +#define QS_TEST_PROBE(code_) \ + if (qs_tp_ != 0U) { code_ } -//! End a QS user record without exiting critical section. -#define QS_END_NOCRIT() \ - QP::QS::endRec_(); \ - } +#define QS_TEST_PROBE_ID(id_, code_) \ + if (qs_tp_ == static_cast(id_)) { code_ } -#ifndef QS_REC_DONE - //! macro to hook up user code when a QS record is produced - #define QS_REC_DONE() (static_cast(0)) -#endif // QS_REC_DONE - -//! helper macro for checking the global QS filter -#define QS_GLB_CHECK_(rec_) \ - ((QP::QS::priv_.glbFilter[static_cast(rec_) >> 3] \ - & static_cast(1U \ - << (static_cast(rec_) & 7U))) != 0U) - -//! helper macro for checking the local QS filter -#define QS_LOC_CHECK_(qs_id_) \ - ((QP::QS::priv_.locFilter[static_cast(qs_id_) >> 3] \ - & static_cast(1U \ - << (static_cast(qs_id_) & 7U))) != 0U) - - -//**************************************************************************** -// Facilities for QS ciritical section - -// QS-specific critical section -#ifdef QS_CRIT_ENTRY // separate QS critical section defined? - -#ifndef QS_CRIT_STAT_TYPE - #define QS_CRIT_STAT_ - #define QS_CRIT_E_() QS_CRIT_ENTRY(dummy) - #define QS_CRIT_X_() QS_CRIT_EXIT(dummy); QS_REC_DONE() -#else - #define QS_CRIT_STAT_ QS_CRIT_STAT_TYPE critStat_; - #define QS_CRIT_E_() QS_CRIT_ENTRY(critStat_) - #define QS_CRIT_X_() QS_CRIT_EXIT(critStat_); QS_REC_DONE() -#endif // QS_CRIT_STAT_TYPE - -#else // separate QS critical section not defined--use the QF definition -#ifndef QF_CRIT_STAT_TYPE - //! This is an internal macro for defining the critical section - //! status type. - /// @description - /// The purpose of this macro is to enable writing the same code for the - /// case when critical section status type is defined and when it is not. - /// If the macro #QF_CRIT_STAT_TYPE is defined, this internal macro - /// provides the definition of the critical section status variable. - /// Otherwise this macro is empty. - /// @sa #QF_CRIT_STAT_TYPE - #define QS_CRIT_STAT_ - - //! This is an internal macro for entering a critical section. - /// @description - /// The purpose of this macro is to enable writing the same code for the - /// case when critical section status type is defined and when it is not. - /// If the macro #QF_CRIT_STAT_TYPE is defined, this internal macro - /// invokes #QF_CRIT_ENTRY passing the key variable as the parameter. - /// Otherwise #QF_CRIT_ENTRY is invoked with a dummy parameter. - /// @sa #QF_CRIT_ENTRY - #define QS_CRIT_E_() QF_CRIT_ENTRY(dummy) - - //! This is an internal macro for exiting a critical section. - /// @description - /// The purpose of this macro is to enable writing the same code for the - /// case when critical section status type is defined and when it is not. - /// If the macro #QF_CRIT_STAT_TYPE is defined, this internal macro - /// invokes #QF_CRIT_EXIT passing the key variable as the parameter. - /// Otherwise #QF_CRIT_EXIT is invoked with a dummy parameter. - /// @sa #QF_CRIT_EXIT - #define QS_CRIT_X_() QF_CRIT_EXIT(dummy); QS_REC_DONE() - -#elif (!defined QS_CRIT_STAT_) - #define QS_CRIT_STAT_ QF_CRIT_STAT_TYPE critStat_; - #define QS_CRIT_E_() QF_CRIT_ENTRY(critStat_) - #define QS_CRIT_X_() QF_CRIT_EXIT(critStat_); QS_REC_DONE() -#endif // simple unconditional interrupt disabling used - -#endif // separate QS critical section not defined - - -//**************************************************************************** -// Macros for use in the client code - -//! Output formatted std::int8_t to the QS record -#define QS_I8(width_, data_) \ - (QP::QS::u8_fmt_(static_cast( \ - (static_cast(width_) << 4)) \ - | static_cast(QP::QS::I8_T)), (data_))) - -//! Output formatted std::uint8_t to the QS record -#define QS_U8(width_, data_) \ - (QP::QS::u8_fmt_(static_cast( \ - (static_cast((width_) << 4)) \ - | static_cast(QP::QS::U8_T)), (data_))) - -//! Output formatted std::int16_t to the QS record -#define QS_I16(width_, data_) \ - (QP::QS::u16_fmt_(static_cast( \ - (static_cast((width_) << 4)) \ - | static_cast(QP::QS::I16_T)), (data_))) - -//! Output formatted std::uint16_t to the QS record -#define QS_U16(width_, data_) \ - (QP::QS::u16_fmt_(static_cast((((width_) << 4)) \ - | static_cast(QP::QS::U16_T)), (data_))) - -//! Output formatted std::int32_t to the QS record -#define QS_I32(width_, data_) \ - (QP::QS::u32_fmt_( \ - static_cast((static_cast((width_) << 4)) \ - | static_cast(QP::QS::I32_T)), (data_))) - -//! Output formatted std::uint32_t to the QS record -#define QS_U32(width_, data_) \ - (QP::QS::u32_fmt_(static_cast( \ - (static_cast((width_) << 4)) \ - | static_cast(QP::QS::U32_T)), (data_))) - -//! Output formatted std::int64_t to the QS record -#define QS_I64(width_, data_) \ - (QP::QS::u64_fmt_(static_cast( \ - (static_cast((width_) << 4)) \ - | static_cast(QP::QS::I64_T)), (data_))) - -//! Output formatted std::uint64_t to the QS record -#define QS_U64(width_, data_) \ - (QP::QS::u64_fmt_(static_cast( \ - (static_cast((width_) << 4)) \ - | static_cast(QP::QS::U64_T)), (data_))) - -//! Output formatted 32-bit floating point number to the QS record -#define QS_F32(width_, data_) \ - (QP::QS::f32_fmt_(static_cast( \ - (static_cast((width_) << 4)) \ - | static_cast(QP::QS::F32_T)), (data_))) - -//! Output formatted 64-bit floating point number to the QS record -#define QS_F64(width_, data_) \ - (QP::QS::f64_fmt_(static_cast( \ - (static_cast((width_) << 4)) \ - | static_cast(QP::QS::F64_T)), (data_))) - -//! Output formatted zero-terminated ASCII string to the QS record -#define QS_STR(str_) (QP::QS::str_fmt_(str_)) - -//! Output formatted memory block of up to 255 bytes to the QS record -#define QS_MEM(mem_, size_) (QP::QS::mem_fmt_((mem_), (size_))) +#define QS_TEST_PAUSE() (QP::QS::test_pause_()) +#else // Q_UTEST not defined -#if (QS_OBJ_PTR_SIZE == 1U) - #define QS_OBJ(obj_) (QP::QS::u8_fmt_(QP::QS::OBJ_T, \ - reinterpret_cast(obj_))) -#elif (QS_OBJ_PTR_SIZE == 2U) - #define QS_OBJ(obj_) (QP::QS::u16_fmt_(QP::QS::OBJ_T, \ - reinterpret_cast(obj_))) -#elif (QS_OBJ_PTR_SIZE == 4U) - #define QS_OBJ(obj_) (QP::QS::u32_fmt_(QP::QS::OBJ_T, \ - reinterpret_cast(obj_))) -#elif (QS_OBJ_PTR_SIZE == 8U) - #define QS_OBJ(obj_) (QP::QS::u64_fmt_(QP::QS::OBJ_T, \ - reinterpret_cast(obj_))) -#else - //! Output formatted object pointer to the QS record - #define QS_OBJ(obj_) (QP::QS::u32_fmt_(QP::QS::OBJ_T, \ - reinterpret_cast(obj_))) -#endif +// dummy definitions when not building for QUTEST +#define QS_TEST_PROBE_DEF(fun_) +#define QS_TEST_PROBE(code_) +#define QS_TEST_PROBE_ID(id_, code_) +#define QS_TEST_PAUSE() (static_cast(0)) +#endif // Q_UTEST -#if (QS_FUN_PTR_SIZE == 1U) - #define QS_FUN(fun_) (QP::QS::u8_fmt_(QP::QS::FUN_T, \ - reinterpret_cast(fun_))) -#elif (QS_FUN_PTR_SIZE == 2U) - #define QS_FUN(fun_) (QP::QS::u16_fmt_(QP::QS::FUN_T, \ - reinterpret_cast(fun_))) -#elif (QS_FUN_PTR_SIZE == 4U) - #define QS_FUN(fun_) (QP::QS::u32_fmt_(QP::QS::FUN_T, \ - reinterpret_cast(fun_))) -#elif (QS_FUN_PTR_SIZE == 8U) - #define QS_FUN(fun_) (QP::QS::u64_fmt_(QP::QS::FUN_T, \ - reinterpret_cast(fun_))) -#else - //! Output formatted function pointer to the QS record - #define QS_FUN(fun_) (QP::QS::u32_fmt_(QP::QS::FUN_T, \ - reinterpret_cast(fun_))) -#endif +//============================================================================ +//! @cond INTERNAL +namespace QP { +namespace QS { + +//............................................................................ +struct CmdVar { + std::uint32_t param1; + std::uint32_t param2; + std::uint32_t param3; + std::uint8_t idx; + std::uint8_t cmdId; +}; -#if (Q_SIGNAL_SIZE == 1U) - #define QS_SIG(sig_, obj_) \ - QP::QS::u8_fmt_(QP::QS::SIG_T, static_cast(sig_)); \ - QP::QS::obj_raw_(obj_) -#elif (Q_SIGNAL_SIZE == 2U) - #define QS_SIG(sig_, obj_) \ - QP::QS::u16_fmt_(QP::QS::SIG_T, static_cast(sig_)); \ - QP::QS::obj_raw_(obj_) -#elif (Q_SIGNAL_SIZE == 4U) - #define QS_SIG(sig_, obj_) \ - QP::QS::u32_fmt_(QP::QS::SIG_T, static_cast(sig_)); \ - QP::QS::obj_raw_(obj_) -#else - //! Output formatted event signal (of type QP::QSignal) and - //! the state machine object to the user QS record - #define QS_SIG(sig_, obj_) \ - QP::QS::u16_fmt_(QP::QS::SIG_T, static_cast(sig_)); \ - QP::QS::obj_raw_(obj_) -#endif +struct TickVar { + std::uint_fast8_t rate; +}; +struct PeekVar { + std::uint16_t offs; + std::uint8_t size; + std::uint8_t num; + std::uint8_t idx; +}; -////////////////////////////////////////////////////////////////////////////// - -//! 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 @p sig_ -/// and the state machine @p 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 @p 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" @p obj_ pointer). The EAT_SIG signal, on the other -/// hand, is global (0 @p 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_) \ - (QP::QS::sig_dict_pre_((sig_), (obj_), #sig_)) +struct PokeVar { + std::uint32_t data; + std::uint16_t offs; + std::uint8_t size; + std::uint8_t num; + std::uint8_t idx; + std::uint8_t fill; +}; -//! 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_) \ - (QP::QS::obj_dict_pre_((obj_), #obj_)) +struct FltVar { + std::uint8_t data[16]; + std::uint8_t idx; + std::uint8_t recId; // global/local +}; -//! 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_) \ - (QP::QS::fun_dict_pre_( \ - QP::QS::force_cast(fun_), #fun_)) +struct ObjVar { + QSObj addr; + std::uint8_t idx; + std::uint8_t kind; // see qs.hpp, enum QSpyObjKind + std::uint8_t recId; +}; + +struct EvtVar { + QP::QEvt *e; + std::uint8_t *p; + QP::QSignal sig; + std::uint16_t len; + std::uint8_t prio; + std::uint8_t idx; +}; -//! Output user QS record 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 { \ - static char_t const usr_name_[] = #rec_; \ - QP::QS::usr_dict_pre_((rec_), &usr_name_[0]); \ -} while (false) +struct RxAttr { + void * currObj[8]; + std::uint8_t * buf; + QSCtr end; + QSCtr volatile head; + QSCtr volatile tail; + std::uint8_t state; + std::uint8_t esc; + std::uint8_t seq; + std::uint8_t chksum; +#ifdef Q_UTEST + bool inTestLoop; +#endif + union Variant { + CmdVar cmd; + TickVar tick; + PeekVar peek; + PokeVar poke; + FltVar flt; + ObjVar obj; + EvtVar evt; +#ifdef Q_UTEST + QP::QS::TProbe tp; +#endif // Q_UTEST + } var; +} ; -//! Produce the assertion failure trace record -#define QS_ASSERTION(module_, loc_, delay_) \ - (QP::QS::assertion_pre_((module_), (loc_), (delay_))) +extern RxAttr rxPriv_; -//! Output the critical section entry record -#define QF_QS_CRIT_ENTRY() (QP::QS::crit_entry_pre_()) +} // namespace QS +} // namespace QP -//! Output the critical section exit record -#define QF_QS_CRIT_EXIT() (QP::QS::crit_exit_pre_()) +//! @endcond +//============================================================================ -//! Output the interrupt entry record -#define QF_QS_ISR_ENTRY(isrnest_, prio_) \ - (QP::QS::isr_entry_pre_((isrnest_), (prio_))) +//$declare${QS::QS-RX} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv +namespace QP { +namespace QS { + +//${QS::QS-RX::QSpyObjKind} .................................................. +//! Kinds of objects used QS-RX +enum QSpyObjKind : std::uint8_t { + SM_OBJ, //!< state machine object + AO_OBJ, //!< active object + MP_OBJ, //!< event pool object + EQ_OBJ, //!< raw queue object + TE_OBJ, //!< time event object + AP_OBJ, //!< generic Application-specific object + MAX_OBJ +}; -//! Output the interrupt exit record -#define QF_QS_ISR_EXIT(isrnest_, prio_) \ - (QP::QS::isr_exit_pre_((isrnest_), (prio_))) +//${QS::QS-RX::OSpyObjCombnation} ............................................ +//! Object combinations for QS-RX +enum OSpyObjCombnation : std::uint8_t { + SM_AO_OBJ = MAX_OBJ //!< combination of SM and AO +}; -//! Flush the QS trace data to the host -/// -/// This macro invokes the QP::QS::flush() platform-dependent callback -/// function to flush the QS trace buffer to the host. The function -/// typically busy-waits until all the data in the buffer is sent to -/// the host. This is acceptable only in the initial transient. -#define QS_FLUSH() (QP::QS::onFlush()) +//${QS::QS-RX::rxInitBuf} .................................................... +void rxInitBuf( + std::uint8_t * const sto, + std::uint16_t const stoSize) noexcept; -//! Execute an action that is only necessary for QS output -#define QF_QS_ACTION(act_) (act_) +//${QS::QS-RX::rxPut} ........................................................ +inline bool rxPut(std::uint8_t const b) noexcept { + // NOTE: does not need critical section + // But requires system-level memory access (QF_MEM_SYS()). -//! macro to handle the QS output from the application -//! NOTE: if this macro is used, the application must define QS_output(). -#define QS_OUTPUT() (QS_output()) + QSCtr head = rxPriv_.head + 1U; + if (head == rxPriv_.end) { + head = 0U; + } + if (head != rxPriv_.tail) { // buffer NOT full? + rxPriv_.buf[rxPriv_.head] = b; + rxPriv_.head = head; + return true; // byte placed in the buffer + } + else { + return false; // byte NOT placed in the buffer + } +} -//! macro to handle the QS-RX input to the application -//! NOTE: if this macro is used, the application must define QS_rx_input(). -#define QS_RX_INPUT() (QS_rx_input()) +//${QS::QS-RX::rxParse} ...................................................... +void rxParse(); +//${QS::QS-RX::setCurrObj} ................................................... +void setCurrObj( + std::uint8_t const obj_kind, + void * const obj_ptr); -//**************************************************************************** -// Macros for use in QUTest only +//${QS::QS-RX::rxGetNfree} ................................................... +std::uint16_t rxGetNfree() noexcept; -#ifdef Q_UTEST - //! QS macro to define the Test-Probe for a given @p fun_ - #define QS_TEST_PROBE_DEF(fun_) \ - std::uint32_t const qs_tp_ = \ - QP::QS::getTestProbe_(QP::QS::force_cast(fun_)); - - //! QS macro to apply a Test-Probe - #define QS_TEST_PROBE(code_) \ - if (qs_tp_ != 0U) { code_ } - - //! QS macro to apply a Test-Probe - #define QS_TEST_PROBE_ID(id_, code_) \ - if (qs_tp_ == static_cast(id_)) { code_ } - - //! QS macro to pause test execution and enter the test event loop - #define QS_TEST_PAUSE() do { \ - QP::QS::beginRec_( \ - static_cast(QP::QS_TEST_PAUSED)); \ - QP::QS::endRec_(); \ - QP::QS::onTestLoop(); \ - } while (false) - -#else - // dummy definitions when not building for QUTEST - #define QS_TEST_PROBE_DEF(fun_) - #define QS_TEST_PROBE(code_) - #define QS_TEST_PROBE_ID(id_, code_) - #define QS_TEST_PAUSE() ((void)0) -#endif // Q_UTEST +//${QS::QS-RX::doInput} ...................................................... +void doInput(); + +//${QS::QS-RX::onReset} ...................................................... +void onReset(); -#endif // QS_HPP +//${QS::QS-RX::onCommand} .................................................... +void onCommand( + std::uint8_t cmdId, + std::uint32_t param1, + std::uint32_t param2, + std::uint32_t param3); + +} // namespace QS +} // namespace QP +//$enddecl${QS::QS-RX} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +#endif // QS_HPP_ diff --git a/libraries/qpcpp_arm-cm/src/qs_dummy.hpp b/libraries/qpcpp_arm-cm/src/qs_dummy.hpp index 6e8b730..27ba09b 100644 --- a/libraries/qpcpp_arm-cm/src/qs_dummy.hpp +++ b/libraries/qpcpp_arm-cm/src/qs_dummy.hpp @@ -1,62 +1,65 @@ -/// @file -/// @brief Dummy definitions of the QS macros that avoid code generation from -/// the QS instrumentation. -/// @ingroup qs qpspy -/// @cond -///*************************************************************************** -/// Last updated for version 6.9.1 -/// Last updated on 2020-09-30 -/// -/// Q u a n t u m L e a P s -/// ------------------------ -/// Modern Embedded Software -/// -/// Copyright (C) 2005-2020 Quantum Leaps. All rights reserved. -/// -/// This program is open source software: you can redistribute it and/or -/// modify it under the terms of the GNU General Public License as published -/// by the Free Software Foundation, either version 3 of the License, or -/// (at your option) any later version. -/// -/// Alternatively, this program may be distributed and modified under the -/// terms of Quantum Leaps commercial licenses, which expressly supersede -/// the GNU General Public License and are specifically designed for -/// licensees interested in retaining the proprietary status of their code. -/// -/// This program is distributed in the hope that it will be useful, -/// but WITHOUT ANY WARRANTY; without even the implied warranty of -/// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -/// GNU General Public License for more details. -/// -/// You should have received a copy of the GNU General Public License -/// along with this program. If not, see . -/// -/// Contact information: -/// -/// -///*************************************************************************** -/// @endcond - -#ifndef QS_DUMMY_HPP -#define QS_DUMMY_HPP +//$file${include::qs_dummy.hpp} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv +// +// Model: qpcpp.qm +// File: ${include::qs_dummy.hpp} +// +// This code has been generated by QM 6.1.1 . +// DO NOT EDIT THIS FILE MANUALLY. All your changes will be lost. +// +// This code is covered by the following QP license: +// License # : LicenseRef-QL-dual +// Issued to : Any user of the QP/C++ real-time embedded framework +// Framework(s) : qpcpp +// Support ends : 2024-12-31 +// License scope: +// +// Copyright (C) 2005 Quantum Leaps, LLC . +// +// Q u a n t u m L e a P s +// ------------------------ +// Modern Embedded Software +// +// SPDX-License-Identifier: GPL-3.0-or-later OR LicenseRef-QL-commercial +// +// This software is dual-licensed under the terms of the open source GNU +// General Public License version 3 (or any later version), or alternatively, +// under the terms of one of the closed source Quantum Leaps commercial +// licenses. +// +// The terms of the open source GNU General Public License version 3 +// can be found at: +// +// The terms of the closed source Quantum Leaps commercial licenses +// can be found at: +// +// Redistributions in source code must retain this top-level comment block. +// Plagiarizing this software to sidestep the license obligations is illegal. +// +// Contact information: +// +// +// +//$endhead${include::qs_dummy.hpp} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +#ifndef QS_DUMMY_HPP_ +#define QS_DUMMY_HPP_ #ifdef Q_SPY - #error "Q_SPY must NOT be defined to include qs_dummy.hpp" +#error "Q_SPY must NOT be defined to include qs_dummy.hpp" #endif #define QS_INIT(arg_) (true) #define QS_EXIT() static_cast(0) #define QS_DUMP() static_cast(0) #define QS_GLB_FILTER(rec_) static_cast(0) -#define QS_LOC_FILTER(qs_id_) static_cast(0) +#define QS_LOC_FILTER(qsId_) static_cast(0) #define QS_GET_BYTE(pByte_) (0xFFFFU) #define QS_GET_BLOCK(pSize_) (nullptr) -#define QS_BEGIN_ID(rec_, qs_id_) if (false) { +#define QS_BEGIN_ID(rec_, qsId_) if (false) { #define QS_END() } -#define QS_BEGIN_NOCRIT(rec_, qs_id_) if (false) { -#define QS_END_NOCRIT() } +#define QS_BEGIN_INCRIT(rec_, qsId_) if (false) { +#define QS_END_INCRIT() } #define QS_I8(width_, data_) static_cast(0) #define QS_U8(width_, data_) static_cast(0) @@ -66,7 +69,9 @@ #define QS_U32(width_, data_) static_cast(0) #define QS_F32(width_, data_) static_cast(0) #define QS_F64(width_, data_) static_cast(0) +#define QS_I64(width_, data_) static_cast(0) #define QS_U64(width_, data_) static_cast(0) +#define QS_ENUM(group_, value_) static_cast(0) #define QS_STR(str_) static_cast(0) #define QS_MEM(mem_, size_) static_cast(0) #define QS_SIG(sig_, obj_) static_cast(0) @@ -75,8 +80,10 @@ #define QS_SIG_DICTIONARY(sig_, obj_) static_cast(0) #define QS_OBJ_DICTIONARY(obj_) static_cast(0) +#define QS_OBJ_ARR_DICTIONARY(obj_, idx_) static_cast(0) #define QS_FUN_DICTIONARY(fun_) static_cast(0) #define QS_USR_DICTIONARY(rec_) static_cast(0) +#define QS_ENUM_DICTIONARY(value_, group_) static_cast(0) #define QS_ASSERTION(module_, loc_, delay_) static_cast(0) #define QS_FLUSH() static_cast(0) @@ -87,16 +94,16 @@ #define QS_OUTPUT() static_cast(0) #define QS_RX_INPUT() static_cast(0) +#define QS_ONLY(code_) static_cast(0) -//**************************************************************************** -// internal QS macros used only in the QP components - +//============================================================================ +// interface used only for internal implementation, but not in applications #ifdef QP_IMPL // predefined QS trace records - #define QS_BEGIN_PRE_(rec_, qs_id_) if (false) { + #define QS_BEGIN_PRE_(rec_, qsId_) if (false) { #define QS_END_PRE_() } - #define QS_BEGIN_NOCRIT_PRE_(rec_, qs_id_) if (false) { - #define QS_END_NOCRIT_PRE_() } + #define QS_BEGIN_PRE_(rec_, qsId_) if (false) { + #define QS_END_PRE_() } #define QS_U8_PRE_(data_) static_cast(0) #define QS_2U8_PRE_(data1_, data2_) static_cast(0) #define QS_U16_PRE_(data_) static_cast(0) @@ -111,13 +118,17 @@ #define QS_MPS_PRE_(size_) static_cast(0) #define QS_TEC_PRE_(ctr_) static_cast(0) - #define QS_CRIT_STAT_ - #define QF_QS_CRIT_ENTRY() static_cast(0) - #define QF_QS_CRIT_EXIT() static_cast(0) - #define QF_QS_ISR_ENTRY(isrnest_, prio_) static_cast(0) - #define QF_QS_ISR_EXIT(isrnest_, prio_) static_cast(0) - #define QF_QS_ACTION(act_) static_cast(0) -#endif // QP_IMPL + #define QS_CRIT_STAT + #define QS_CRIT_ENTRY() static_cast(0) + #define QS_CRIT_EXIT() static_cast(0) -#endif // QS_DUMMY_HPP + #define QS_MEM_SYS() static_cast(0) + #define QS_MEM_APP() static_cast(0) + + #define QS_TR_CRIT_ENTRY() static_cast(0) + #define QS_TR_CRIT_EXIT() static_cast(0) + #define QS_TR_ISR_ENTRY(isrnest_, prio_) static_cast(0) + #define QS_Tr_ISR_EXIT(isrnest_, prio_) static_cast(0) +#endif // QP_IMPL +#endif // QS_DUMMY_HPP_ diff --git a/libraries/qpcpp_arm-cm/src/qs_fp.cpp b/libraries/qpcpp_arm-cm/src/qs_fp.cpp index 4368fa4..db4bb64 100644 --- a/libraries/qpcpp_arm-cm/src/qs_fp.cpp +++ b/libraries/qpcpp_arm-cm/src/qs_fp.cpp @@ -1,64 +1,72 @@ -/// @file -/// @brief QS floating point output implementation -/// @ingroup qs -/// @cond -///*************************************************************************** -/// Last updated for version 6.9.2 -/// Last updated on 2021-01-13 -/// -/// Q u a n t u m L e a P s -/// ------------------------ -/// Modern Embedded Software -/// -/// Copyright (C) 2005-2021 Quantum Leaps. All rights reserved. -/// -/// This program is open source software: you can redistribute it and/or -/// modify it under the terms of the GNU General Public License as published -/// by the Free Software Foundation, either version 3 of the License, or -/// (at your option) any later version. -/// -/// Alternatively, this program may be distributed and modified under the -/// terms of Quantum Leaps commercial licenses, which expressly supersede -/// the GNU General Public License and are specifically designed for -/// licensees interested in retaining the proprietary status of their code. -/// -/// This program is distributed in the hope that it will be useful, -/// but WITHOUT ANY WARRANTY; without even the implied warranty of -/// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -/// GNU General Public License for more details. -/// -/// You should have received a copy of the GNU General Public License -/// along with this program. If not, see . -/// -/// Contact information: -/// -/// -///*************************************************************************** -/// @endcond - +//$file${src::qs::qs_fp.cpp} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv +// +// Model: qpcpp.qm +// File: ${src::qs::qs_fp.cpp} +// +// This code has been generated by QM 6.1.1 . +// DO NOT EDIT THIS FILE MANUALLY. All your changes will be lost. +// +// This code is covered by the following QP license: +// License # : LicenseRef-QL-dual +// Issued to : Any user of the QP/C++ real-time embedded framework +// Framework(s) : qpcpp +// Support ends : 2024-12-31 +// License scope: +// +// Copyright (C) 2005 Quantum Leaps, LLC . +// +// Q u a n t u m L e a P s +// ------------------------ +// Modern Embedded Software +// +// SPDX-License-Identifier: GPL-3.0-or-later OR LicenseRef-QL-commercial +// +// This software is dual-licensed under the terms of the open source GNU +// General Public License version 3 (or any later version), or alternatively, +// under the terms of one of the closed source Quantum Leaps commercial +// licenses. +// +// The terms of the open source GNU General Public License version 3 +// can be found at: +// +// The terms of the closed source Quantum Leaps commercial licenses +// can be found at: +// +// Redistributions in source code must retain this top-level comment block. +// Plagiarizing this software to sidestep the license obligations is illegal. +// +// Contact information: +// +// +// +//$endhead${src::qs::qs_fp.cpp} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ #define QP_IMPL // this is QF/QK implementation #include "qs_port.hpp" // QS port #include "qs_pkg.hpp" // QS package-scope internal interface +//============================================================================ +//! @cond INTERNAL + namespace QP { +namespace QS { -//**************************************************************************** -/// @note This function is only to be used through macros, never in the -/// client code directly. -/// -void QS::f32_fmt_(std::uint8_t format, float32_t const d) noexcept { +//${QS::QS-tx-fp::f32_fmt_} .................................................. +void f32_fmt_( + std::uint8_t format, + float32_t f) noexcept +{ union F32Rep { float32_t f; std::uint32_t u; } fu32; // the internal binary representation - std::uint8_t chksum_ = priv_.chksum; // put in a temporary (register) - std::uint8_t * const buf_ = priv_.buf; // put in a temporary (register) - QSCtr head_ = priv_.head; // put in a temporary (register) - QSCtr const end_ = priv_.end; // put in a temporary (register) + std::uint8_t chksum = priv_.chksum; // put in a temporary (register) + std::uint8_t * const buf = priv_.buf; // put in a temporary (register) + QSCtr head = priv_.head; // put in a temporary (register) + QSCtr const end = priv_.end; // put in a temporary (register) - fu32.f = d; // assign the binary representation + fu32.f = f; // assign the binary representation - priv_.used += 5U; // 5 bytes about to be added + priv_.used = (priv_.used + 5U); // 5 bytes about to be added QS_INSERT_ESC_BYTE_(format) // insert the format byte for (std::uint_fast8_t i = 4U; i != 0U; --i) { @@ -67,23 +75,23 @@ void QS::f32_fmt_(std::uint8_t format, float32_t const d) noexcept { fu32.u >>= 8U; } - priv_.head = head_; // save the head - priv_.chksum = chksum_; // save the checksum + priv_.head = head; // save the head + priv_.chksum = chksum; // save the checksum } -//**************************************************************************** -/// @note This function is only to be used through macros, never in the -/// client code directly. -/// -void QS::f64_fmt_(std::uint8_t format, float64_t const d) noexcept { +//${QS::QS-tx-fp::f64_fmt_} .................................................. +void f64_fmt_( + std::uint8_t format, + float64_t d) noexcept +{ union F64Rep { float64_t d; std::uint32_t u[2]; } fu64; // the internal binary representation - std::uint8_t chksum_ = priv_.chksum; - std::uint8_t * const buf_ = priv_.buf; - QSCtr head_ = priv_.head; - QSCtr const end_ = priv_.end; + std::uint8_t chksum = priv_.chksum; + std::uint8_t * const buf = priv_.buf; + QSCtr head = priv_.head; + QSCtr const end = priv_.end; std::uint32_t i; // static constant untion to detect endianness of the machine static union U32Rep { @@ -101,7 +109,7 @@ void QS::f64_fmt_(std::uint8_t format, float64_t const d) noexcept { fu64.u[1] = i; } - priv_.used += 9U; // 9 bytes about to be added + priv_.used = (priv_.used + 9U); // 9 bytes about to be added QS_INSERT_ESC_BYTE_(format) // insert the format byte // output 4 bytes from fu64.u[0]... @@ -116,9 +124,11 @@ void QS::f64_fmt_(std::uint8_t format, float64_t const d) noexcept { fu64.u[1] >>= 8U; } - priv_.head = head_; // update the head - priv_.chksum = chksum_; // update the checksum + priv_.head = head; // update the head + priv_.chksum = chksum; // update the checksum } +} // namespace QS } // namespace QP +//! @endcond diff --git a/libraries/qpcpp_arm-cm/src/qs_pkg.hpp b/libraries/qpcpp_arm-cm/src/qs_pkg.hpp deleted file mode 100644 index c2a2404..0000000 --- a/libraries/qpcpp_arm-cm/src/qs_pkg.hpp +++ /dev/null @@ -1,274 +0,0 @@ -/// @file -/// @ingroup qs -/// @brief Internal (package scope) QS/C++ interface. -/// @cond -///*************************************************************************** -/// Last updated for version 6.9.1 -/// Last updated on 2020-09-18 -/// -/// Q u a n t u m L e a P s -/// ------------------------ -/// Modern Embedded Software -/// -/// Copyright (C) 2005-2020 Quantum Leaps. All rights reserved. -/// -/// This program is open source software: you can redistribute it and/or -/// modify it under the terms of the GNU General Public License as published -/// by the Free Software Foundation, either version 3 of the License, or -/// (at your option) any later version. -/// -/// Alternatively, this program may be distributed and modified under the -/// terms of Quantum Leaps commercial licenses, which expressly supersede -/// the GNU General Public License and are specifically designed for -/// licensees interested in retaining the proprietary status of their code. -/// -/// This program is distributed in the hope that it will be useful, -/// but WITHOUT ANY WARRANTY; without even the implied warranty of -/// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -/// GNU General Public License for more details. -/// -/// You should have received a copy of the GNU General Public License -/// along with this program. If not, see . -/// -/// Contact information: -/// -/// -///*************************************************************************** -/// @endcond - -#ifndef QS_PKG_HPP -#define QS_PKG_HPP - -//! Internal QS macro to insert an un-escaped byte into the QS buffer -#define QS_INSERT_BYTE_(b_) \ - buf_[head_] = (b_); \ - ++head_; \ - if (head_ == end_) { \ - head_ = 0U; \ - } - -//! Internal QS macro to insert an escaped byte into the QS buffer -#define QS_INSERT_ESC_BYTE_(b_) \ - chksum_ += (b_); \ - if (((b_) != QS_FRAME) && ((b_) != QS_ESC)) { \ - QS_INSERT_BYTE_(b_) \ - } \ - else { \ - QS_INSERT_BYTE_(QS_ESC) \ - QS_INSERT_BYTE_(static_cast((b_) ^ QS_ESC_XOR)) \ - ++priv_.used; \ - } - -//**************************************************************************** -// Macros for use inside other macros or internally in the QP code - -//! Internal QS macro to begin a predefined QS record with critical section. -/// @note -/// This macro is intended to use only inside QP components and NOT -/// at the application level. -/// @sa QS_BEGIN_ID() -/// -#define QS_BEGIN_PRE_(rec_, qs_id_) \ - if (QS_GLB_CHECK_(rec_) && QS_LOC_CHECK_(qs_id_)) { \ - QS_CRIT_E_(); \ - QP::QS::beginRec_(static_cast(rec_)); - -//! Internal QS macro to end a predefined QS record with critical section. -/// @note -/// This macro is intended to use only inside QP components and NOT -/// at the application level. -/// @sa QS_END() -/// -#define QS_END_PRE_() \ - QP::QS::endRec_(); \ - QS_CRIT_X_(); \ - } - -//! Internal QS macro to begin a predefined QS record without critical section. -/// @note -/// This macro is intended to use only inside QP components and NOT -/// at the application level. -/// @sa QS_BEGIN_NOCRIT_PRE_() -#define QS_BEGIN_NOCRIT_PRE_(rec_, qs_id_) \ - if (QS_GLB_CHECK_(rec_) && QS_LOC_CHECK_(qs_id_)) { \ - QP::QS::beginRec_(static_cast(rec_)); - -//! Internal QS macro to end a predefiend QS record without critical section. -/// @note -/// This macro is intended to use only inside QP components and NOT -/// at the application level. @sa #QS_END_NOCRIT -#define QS_END_NOCRIT_PRE_() \ - QP::QS::endRec_(); \ - } - -#if (Q_SIGNAL_SIZE == 1U) - //! Internal QS macro to output an unformatted event signal data element - /// @note - /// The size of the pointer depends on the macro #Q_SIGNAL_SIZE. - #define QS_SIG_PRE_(sig_) \ - (QP::QS::u8_raw_(static_cast(sig_))) -#elif (Q_SIGNAL_SIZE == 2U) - #define QS_SIG_PRE_(sig_) \ - (QP::QS::u16_raw_(static_cast(sig_))) -#elif (Q_SIGNAL_SIZE == 4U) - #define QS_SIG_PRE_(sig_) \ - (QP::QS::u32_raw_(static_cast(sig_))) -#endif - -//! Internal QS macro to output an unformatted uint8_t data element -#define QS_U8_PRE_(data_) \ - (QP::QS::u8_raw_(static_cast(data_))) - -//! Internal QS macro to output 2 unformatted uint8_t data elements -#define QS_2U8_PRE_(data1_, data2_) \ - (QP::QS::u8u8_raw_(static_cast(data1_), \ - static_cast(data2_))) - -//! Internal QS macro to output an unformatted uint16_t data element -#define QS_U16_PRE_(data_) \ - (QP::QS::u16_raw_(static_cast(data_))) - -//! Internal QS macro to output an unformatted uint32_t data element -#define QS_U32_PRE_(data_) \ - (QP::QS::u32_raw_(static_cast(data_))) - -//! Internal QS macro to output a zero-terminated ASCII string -/// data element -#define QS_STR_PRE_(msg_) (QP::QS::str_raw_(msg_)) - -//! Internal QS macro to output object pointer data element -#define QS_OBJ_PRE_(obj_) (QP::QS::obj_raw_(obj_)) - -#if (QS_FUN_PTR_SIZE == 1U) - #define QS_FUN_PRE_(fun_) \ - (QP::QS::u8_raw_(reinterpret_cast(fun_))) -#elif (QS_FUN_PTR_SIZE == 2U) - #define QS_FUN_PRE_(fun_) \ - (QP::QS::u16_raw_(reinterpret_cast(fun_))) -#elif (QS_FUN_PTR_SIZE == 4U) - #define QS_FUN_PRE_(fun_) \ - (QP::QS::u32_raw_(reinterpret_cast(fun_))) -#elif (QS_FUN_PTR_SIZE == 8U) - #define QS_FUN_PRE_(fun_) \ - (QP::QS::u64_raw_(reinterpret_cast(fun_))) -#else - - //! Internal QS macro to output an unformatted function pointer - //! data element - /// @note - /// The size of the pointer depends on the macro #QS_FUN_PTR_SIZE. - /// If the size is not defined the size of pointer is assumed 4-bytes. - #define QS_FUN_PRE_(fun_) \ - (QP::QS::u32_raw_(reinterpret_cast(fun_))) -#endif - -#if (QF_EQUEUE_CTR_SIZE == 1U) - - //! Internal QS macro to output an unformatted event queue - //! counter data element - /// @note the counter size depends on the macro #QF_EQUEUE_CTR_SIZE. - #define QS_EQC_PRE_(ctr_) \ - QS::u8_raw_(static_cast(ctr_)) -#elif (QF_EQUEUE_CTR_SIZE == 2U) - #define QS_EQC_PRE_(ctr_) \ - QS::u16_raw_(static_cast(ctr_)) -#elif (QF_EQUEUE_CTR_SIZE == 4U) - #define QS_EQC_PRE_(ctr_) \ - QS::u32_raw_(static_cast(ctr_)) -#else - #error "QF_EQUEUE_CTR_SIZE not defined" -#endif - - -#if (QF_EVENT_SIZ_SIZE == 1U) - - //! Internal QS macro to output an unformatted event size - //! data element - /// @note the event size depends on the macro #QF_EVENT_SIZ_SIZE. - #define QS_EVS_PRE_(size_) \ - QS::u8_raw_(static_cast(size_)) -#elif (QF_EVENT_SIZ_SIZE == 2U) - #define QS_EVS_PRE_(size_) \ - QS::u16_raw_(static_cast(size_)) -#elif (QF_EVENT_SIZ_SIZE == 4U) - #define QS_EVS_PRE_(size_) \ - QS::u32_raw_(static_cast(size_)) -#endif - - -#if (QF_MPOOL_SIZ_SIZE == 1U) - - //! Internal QS macro to output an unformatted memory pool - /// block-size data element - /// @note the block-size depends on the macro #QF_MPOOL_SIZ_SIZE. - #define QS_MPS_PRE_(size_) \ - QS::u8_raw_(static_cast(size_)) -#elif (QF_MPOOL_SIZ_SIZE == 2U) - #define QS_MPS_PRE_(size_) \ - QS::u16_raw_(static_cast(size_)) -#elif (QF_MPOOL_SIZ_SIZE == 4U) - #define QS_MPS_PRE_(size_) \ - QS::u32_raw_(static_cast(size_)) -#endif - -#if (QF_MPOOL_CTR_SIZE == 1U) - - //! Internal QS macro to output an unformatted memory pool - //! block-counter data element - /// @note the counter size depends on the macro #QF_MPOOL_CTR_SIZE. - #define QS_MPC_PRE_(ctr_) \ - QS::u8_raw_(static_cast(ctr_)) -#elif (QF_MPOOL_CTR_SIZE == 2U) - #define QS_MPC_PRE_(ctr_) \ - QS::u16_raw_(static_cast(ctr_)) -#elif (QF_MPOOL_CTR_SIZE == 4U) - #define QS_MPC_PRE_(ctr_) \ - QS::u32_raw_(static_cast(ctr_)) -#endif - - -#if (QF_TIMEEVT_CTR_SIZE == 1U) - - //! Internal QS macro to output an unformatted time event - //! tick-counter data element - /// @note the counter size depends on the macro #QF_TIMEEVT_CTR_SIZE. - #define QS_TEC_PRE_(ctr_) \ - QS::u8_raw_(static_cast(ctr_)) -#elif (QF_TIMEEVT_CTR_SIZE == 2U) - #define QS_TEC_PRE_(ctr_) \ - QS::u16_raw_(static_cast(ctr_)) -#elif (QF_TIMEEVT_CTR_SIZE == 4U) - #define QS_TEC_PRE_(ctr_) \ - QS::u32_raw_(static_cast(ctr_)) -#endif - -//! Internal QS macro to cast enumerated QS record number to uint8_t -/// -/// @note Casting from enum to unsigned char violates the MISRA-C++ 2008 rules -/// 5-2-7, 5-2-8 and 5-2-9. Encapsulating this violation in a macro allows to -/// selectively suppress this specific deviation. -#define QS_REC_NUM_(enum_) (static_cast(enum_)) - -namespace QP { - -/// @brief Frame character of the QS output protocol -constexpr std::uint8_t QS_FRAME = 0x7EU; - -/// @brief Escape character of the QS output protocol -constexpr std::uint8_t QS_ESC = 0x7DU; - -/// @brief Escape modifier of the QS output protocol -/// -/// The escaped byte is XOR-ed with the escape modifier before it is inserted -/// into the QS buffer. -constexpr std::uint8_t QS_ESC_XOR = 0x20U; - -/// @brief Escape character of the QS output protocol -constexpr std::uint8_t QS_GOOD_CHKSUM = 0xFFU; - -//! send the Target info (object sizes, build time-stamp, QP version) -void QS_target_info_(std::uint8_t const isReset) noexcept; - -} // namespace QP - -#endif // QS_PKG_HPP diff --git a/libraries/qpcpp_arm-cm/src/qs_port.hpp b/libraries/qpcpp_arm-cm/src/qs_port.hpp index 84581cd..8ce26fa 100644 --- a/libraries/qpcpp_arm-cm/src/qs_port.hpp +++ b/libraries/qpcpp_arm-cm/src/qs_port.hpp @@ -1,59 +1,61 @@ -/// @file -/// @brief QS/C++ port to ARM Cortex-M, generic compiler -/// @cond -///*************************************************************************** -/// Last updated for version 6.6.0 -/// Last updated on 2019-07-30 -/// -/// Q u a n t u m L e a P s -/// ------------------------ -/// Modern Embedded Software -/// -/// Copyright (C) 2005-2019 Quantum Leaps. All rights reserved. -/// -/// This program is open source software: you can redistribute it and/or -/// modify it under the terms of the GNU General Public License as published -/// by the Free Software Foundation, either version 3 of the License, or -/// (at your option) any later version. -/// -/// Alternatively, this program may be distributed and modified under the -/// terms of Quantum Leaps commercial licenses, which expressly supersede -/// the GNU General Public License and are specifically designed for -/// licensees interested in retaining the proprietary status of their code. -/// -/// This program is distributed in the hope that it will be useful, -/// but WITHOUT ANY WARRANTY; without even the implied warranty of -/// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -/// GNU General Public License for more details. -/// -/// You should have received a copy of the GNU General Public License -/// along with this program. If not, see . -/// -/// Contact information: -/// -/// -///*************************************************************************** -/// @endcond - -#ifndef QS_PORT_HPP -#define QS_PORT_HPP +//============================================================================ +// QP/C++ Real-Time Embedded Framework (RTEF) +// +// Q u a n t u m L e a P s +// ------------------------ +// Modern Embedded Software +// +// Copyright (C) 2005 Quantum Leaps, LLC. All rights reserved. +// +// SPDX-License-Identifier: GPL-3.0-or-later OR LicenseRef-QL-commercial +// +// This software is dual-licensed under the terms of the open source GNU +// General Public License version 3 (or any later version), or alternatively, +// under the terms of one of the closed source Quantum Leaps commercial +// licenses. +// +// The terms of the open source GNU General Public License version 3 +// can be found at: +// +// The terms of the closed source Quantum Leaps commercial licenses +// can be found at: +// +// Redistributions in source code must retain this top-level comment block. +// Plagiarizing this software to sidestep the license obligations is illegal. +// +// Contact information: +// +// +//============================================================================ +//! @date Last updated on: 2023-08-16 +//! @version Last updated for: @ref qpcpp_7_3_0 +//! +//! @file +//! @brief QS/C++ port to a 32-bit CPU, generic C++ compiler + +#ifndef QS_PORT_HPP_ +#define QS_PORT_HPP_ // QS time-stamp size in bytes -#define QS_TIME_SIZE 4U +#define QS_TIME_SIZE 4U // object pointer size in bytes -#define QS_OBJ_PTR_SIZE 4U +#define QS_OBJ_PTR_SIZE 4U // function pointer size in bytes -#define QS_FUN_PTR_SIZE 4U +#define QS_FUN_PTR_SIZE 4U + +//============================================================================ +// NOTE: QS might be used with or without other QP components, in which +// case the separate definitions of the macros QF_CRIT_STAT, QF_CRIT_ENTRY(), +// and QF_CRIT_EXIT() are needed. In this port QS is configured to be used +// with the other QP component, by simply including "qp_port.hpp" +//*before* "qs.hpp". +#ifndef QP_PORT_HPP_ +#include "qp_port.hpp" // use QS with QP +#endif -//**************************************************************************** -// NOTE: QS might be used with or without other QP components, in which case -// the separate definitions of the macros QF_CRIT_STAT_TYPE, QF_CRIT_ENTRY, -// and QF_CRIT_EXIT are needed. In this port QS is configured to be used with -// the other QP component, by simply including "qf_port.hpp" *before* "qs.hpp". -// -#include "qf_port.hpp" // use QS with QF #include "qs.hpp" // QS platform-independent public interface -#endif // QS_PORT_HPP +#endif // QS_PORT_HPP_ + diff --git a/libraries/qpcpp_arm-cm/src/qs_rx.cpp b/libraries/qpcpp_arm-cm/src/qs_rx.cpp index b9ccc6d..b01a6c3 100644 --- a/libraries/qpcpp_arm-cm/src/qs_rx.cpp +++ b/libraries/qpcpp_arm-cm/src/qs_rx.cpp @@ -1,158 +1,60 @@ -/// @file -/// @brief QS receive channel services -/// @ingroup qs -/// @cond -///*************************************************************************** -/// Last updated for version 6.9.2a -/// Last updated on 2021-01-28 -/// -/// Q u a n t u m L e a P s -/// ------------------------ -/// Modern Embedded Software -/// -/// Copyright (C) 2005-2021 Quantum Leaps. All rights reserved. -/// -/// This program is open source software: you can redistribute it and/or -/// modify it under the terms of the GNU General Public License as published -/// by the Free Software Foundation, either version 3 of the License, or -/// (at your option) any later version. -/// -/// Alternatively, this program may be distributed and modified under the -/// terms of Quantum Leaps commercial licenses, which expressly supersede -/// the GNU General Public License and are specifically designed for -/// licensees interested in retaining the proprietary status of their code. -/// -/// This program is distributed in the hope that it will be useful, -/// but WITHOUT ANY WARRANTY; without even the implied warranty of -/// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -/// GNU General Public License for more details. -/// -/// You should have received a copy of the GNU General Public License -/// along with this program. If not, see . -/// -/// Contact information: -/// -/// -///*************************************************************************** -/// @endcond +//$file${src::qs::qs_rx.cpp} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv +// +// Model: qpcpp.qm +// File: ${src::qs::qs_rx.cpp} +// +// This code has been generated by QM 6.1.1 . +// DO NOT EDIT THIS FILE MANUALLY. All your changes will be lost. +// +// This code is covered by the following QP license: +// License # : LicenseRef-QL-dual +// Issued to : Any user of the QP/C++ real-time embedded framework +// Framework(s) : qpcpp +// Support ends : 2024-12-31 +// License scope: +// +// Copyright (C) 2005 Quantum Leaps, LLC . +// +// Q u a n t u m L e a P s +// ------------------------ +// Modern Embedded Software +// +// SPDX-License-Identifier: GPL-3.0-or-later OR LicenseRef-QL-commercial +// +// This software is dual-licensed under the terms of the open source GNU +// General Public License version 3 (or any later version), or alternatively, +// under the terms of one of the closed source Quantum Leaps commercial +// licenses. +// +// The terms of the open source GNU General Public License version 3 +// can be found at: +// +// The terms of the closed source Quantum Leaps commercial licenses +// can be found at: +// +// Redistributions in source code must retain this top-level comment block. +// Plagiarizing this software to sidestep the license obligations is illegal. +// +// Contact information: +// +// +// +//$endhead${src::qs::qs_rx.cpp} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +#define QP_IMPL // this is QP implementation +#include "qs_port.hpp" // QS port +#include "qs_pkg.hpp" // QS package-scope internal interface +#include "qp_pkg.hpp" // QP package-scope interface +#include "qsafe.h" // QP Functional Safety (FuSa) Subsystem -#define QP_IMPL // this is QP implementation -#include "qs_port.hpp" // QS port -#include "qs_pkg.hpp" // QS package-scope internal interface -#include "qf_pkg.hpp" // QF package-scope internal interface -#include "qassert.h" // QP assertions +//============================================================================ +//! @cond INTERNAL -namespace QP { +namespace { // unnamed local namespace Q_DEFINE_THIS_MODULE("qs_rx") -//**************************************************************************** -struct QS::QSrxPriv QS::rxPriv_; // QS-RX private data - -//**************************************************************************** -#if (QS_OBJ_PTR_SIZE == 1U) - using QSObj = std::uint8_t; -#elif (QS_OBJ_PTR_SIZE == 2U) - using QSObj = std::uint16_t; -#elif (QS_OBJ_PTR_SIZE == 4U) - using QSObj = std::uint32_t; -#elif (QS_OBJ_PTR_SIZE == 8U) - using QSObj = std::uint64_t; -#endif - -#if (QS_FUN_PTR_SIZE == 1U) - using QSFun = std::uint8_t; -#elif (QS_FUN_PTR_SIZE == 2U) - using QSFun = std::uint16_t; -#elif (QS_FUN_PTR_SIZE == 4U) - using QSFun = std::uint32_t; -#elif (QS_FUN_PTR_SIZE == 8U) - using QSFun = std::uint64_t; -#endif - -/// @cond -/// Exlcude the following internals from the Doxygen documentation -/// Extended-state variables used for parsing various QS-RX Records -struct CmdVar { - std::uint32_t param1; - std::uint32_t param2; - std::uint32_t param3; - std::uint8_t idx; - std::uint8_t cmdId; -}; - -struct TickVar { - std::uint_fast8_t rate; -}; - -struct PeekVar { - std::uint16_t offs; - std::uint8_t size; - std::uint8_t num; - std::uint8_t idx; -}; - -struct PokeVar { - std::uint32_t data; - std::uint16_t offs; - std::uint8_t size; - std::uint8_t num; - std::uint8_t idx; - std::uint8_t fill; -}; - -struct FltVar { - std::uint8_t data[16]; - std::uint8_t idx; - std::uint8_t recId; // global/local -}; - -struct ObjVar { - QSObj addr; - std::uint8_t idx; - std::uint8_t kind; // see qs.hpp, enum QSpyObjKind - std::uint8_t recId; -}; - -struct TPVar { // Test-Probe - QSFun addr; - std::uint32_t data; - std::uint8_t idx; -}; - -struct AFltVar { - std::uint8_t prio; -}; - -struct EvtVar { - QEvt *e; - std::uint8_t *p; - QSignal sig; - std::uint16_t len; - std::uint8_t prio; - std::uint8_t idx; -}; - -// extended-state variables for the current QS-RX state -static struct ExtState { - union Variant { - CmdVar cmd; - TickVar tick; - PeekVar peek; - PokeVar poke; - FltVar flt; - AFltVar aFlt; - ObjVar obj; - EvtVar evt; - TPVar tp; - } var; - std::uint8_t state; - std::uint8_t esc; - std::uint8_t seq; - std::uint8_t chksum; -} l_rx; - enum RxStateEnum : std::uint8_t { + ERROR_STATE, WAIT4_SEQ, WAIT4_REC, WAIT4_INFO_FRAME, @@ -187,61 +89,62 @@ enum RxStateEnum : std::uint8_t { WAIT4_EVT_SIG, WAIT4_EVT_LEN, WAIT4_EVT_PAR, - WAIT4_EVT_FRAME, + WAIT4_EVT_FRAME + +#ifdef Q_UTEST + , WAIT4_TEST_SETUP_FRAME, WAIT4_TEST_TEARDOWN_FRAME, WAIT4_TEST_PROBE_DATA, WAIT4_TEST_PROBE_ADDR, WAIT4_TEST_PROBE_FRAME, - WAIT4_TEST_CONTINUE_FRAME, - ERROR_STATE -}; - -#ifdef Q_UTEST - static struct TestData { - TPVar tpBuf[16]; // buffer of Test-Probes received so far - std::uint8_t tpNum; // current number of Test-Probes - QSTimeCtr testTime; // test time (tick counter) - } l_testData; + WAIT4_TEST_CONTINUE_FRAME #endif // Q_UTEST +}; // internal helper functions... static void rxParseData_(std::uint8_t const b) noexcept; +static void rxHandleGoodFrame_(std::uint8_t const state); static void rxHandleBadFrame_(std::uint8_t const state) noexcept; -static void rxReportAck_(enum QSpyRxRecords const recId) noexcept; +static void rxReportAck_(enum QP::QSpyRxRecords const recId) noexcept; static void rxReportError_(std::uint8_t const code) noexcept; -static void rxReportDone_(enum QSpyRxRecords const recId) noexcept; -static void rxPoke_(void) noexcept; +static void rxReportDone_(enum QP::QSpyRxRecords const recId) noexcept; +static void queryCurrObj_(std::uint8_t obj_kind) noexcept; +static void rxPoke_() noexcept; -//! Internal QS-RX function to take a transition in the QS-RX FSM +// Internal QS-RX function to take a tran. in the QS-RX FSM static inline void tran_(RxStateEnum const target) noexcept { - l_rx.state = static_cast(target); + QP::QS::rxPriv_.state = static_cast(target); } -/// @endcond +} // unnamed namespace -//**************************************************************************** -/// @description -/// This function should be called from QS::onStartup() to provide QS-RX with -/// the receive data buffer. -/// -/// @param[in] sto[] the address of the memory block -/// @param[in] stoSize the size of this block [bytes]. The size of the -/// QS RX buffer cannot exceed 64KB. -/// -/// @note QS-RX can work with quite small data buffers, but you will start -/// losing data if the buffer is not drained fast enough in the idle task. -/// -/// @note If the data input rate exceeds the QS-RX processing rate, the data -/// will be lost, but the QS protocol will notice that: -/// (1) that the checksum in the incomplete QS records will fail; and -/// (2) the sequence counter in QS records will show discontinuities. -/// -/// The QS-RX channel will report any data errors by sending the -/// QS_RX_DATA_ERROR trace record. -/// -void QS::rxInitBuf(std::uint8_t * const sto, - std::uint16_t const stoSize) noexcept +#ifndef QF_MEM_ISOLATE +namespace QP { +namespace QS { +RxAttr rxPriv_; +} // namespace QS +} // namespace QP +#endif // QF_MEM_ISOLATE + +//! @endcond +//============================================================================ + +//$skip${QP_VERSION} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv +// Check for the minimum required QP version +#if (QP_VERSION < 730U) || (QP_VERSION != ((QP_RELEASE^4294967295U) % 0x3E8U)) +#error qpcpp version 7.3.0 or higher required +#endif +//$endskip${QP_VERSION} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +//$define${QS::QS-RX} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv +namespace QP { +namespace QS { + +//${QS::QS-RX::rxInitBuf} .................................................... +void rxInitBuf( + std::uint8_t * const sto, + std::uint16_t const stoSize) noexcept { rxPriv_.buf = &sto[0]; rxPriv_.end = static_cast(stoSize); @@ -256,9 +159,9 @@ void QS::rxInitBuf(std::uint8_t * const sto, rxPriv_.currObj[QS::AP_OBJ] = nullptr; tran_(WAIT4_SEQ); - l_rx.esc = 0U; - l_rx.seq = 0U; - l_rx.chksum = 0U; + QP::QS::rxPriv_.esc = 0U; + QP::QS::rxPriv_.seq = 0U; + QP::QS::rxPriv_.chksum = 0U; beginRec_(static_cast(QS_OBJ_DICT)); QS_OBJ_PRE_(&rxPriv_); @@ -266,108 +169,14 @@ void QS::rxInitBuf(std::uint8_t * const sto, endRec_(); // no QS_REC_DONE(), because QS is not running yet -#ifdef Q_UTEST - l_testData.tpNum = 0U; - l_testData.testTime = 0U; -#endif // Q_UTEST -} - -//**************************************************************************** -/// @description -/// This function is intended to be called from the ISR that reads the QS-RX -/// bytes from the QSPY application. The function returns the conservative -/// number of free bytes currently available in the buffer, assuming that -/// the head pointer is not being moved concurrently. The tail pointer might -/// be moving, meaning that bytes can be concurrently removed from the buffer. -/// -std::uint16_t QS::rxGetNfree(void) noexcept { - QSCtr head = rxPriv_.head; - if (head == rxPriv_.tail) { // buffer empty? - return static_cast(rxPriv_.end - 1U); - } - else if (head < rxPriv_.tail) { - return static_cast(rxPriv_.tail - head - 1U); - } - else { - return static_cast(rxPriv_.end + rxPriv_.tail - - head - 1U); - } -} - -//**************************************************************************** -/// -/// @description -/// This function programmatically sets the "current object" in the Target. -/// -void QS::setCurrObj(std::uint8_t obj_kind, void *obj_ptr) noexcept { - - Q_REQUIRE_ID(100, obj_kind < Q_DIM(rxPriv_.currObj)); - rxPriv_.currObj[obj_kind] = obj_ptr; -} - -//**************************************************************************** -/// -/// @description -/// This function programmatically generates the response to the query for -/// a "current object". -/// -void QS::queryCurrObj(std::uint8_t obj_kind) noexcept { - Q_REQUIRE_ID(200, obj_kind < Q_DIM(rxPriv_.currObj)); - - if (QS::rxPriv_.currObj[obj_kind] != nullptr) { - QS_CRIT_STAT_ - QS_CRIT_E_(); - QS::beginRec_(static_cast(QS_QUERY_DATA)); - QS_TIME_PRE_(); // timestamp - QS_U8_PRE_(obj_kind); // object kind - QS_OBJ_PRE_(QS::rxPriv_.currObj[obj_kind]); // object pointer - switch (obj_kind) { - case SM_OBJ: // intentionally fall through - case AO_OBJ: - QS_FUN_PRE_( - reinterpret_cast( - QS::rxPriv_.currObj[obj_kind])->getStateHandler()); - break; - case QS::MP_OBJ: - QS_MPC_PRE_(reinterpret_cast( - QS::rxPriv_.currObj[obj_kind])->m_nFree); - QS_MPC_PRE_(reinterpret_cast( - QS::rxPriv_.currObj[obj_kind])->m_nMin); - break; - case QS::EQ_OBJ: - QS_EQC_PRE_(reinterpret_cast( - QS::rxPriv_.currObj[obj_kind])->m_nFree); - QS_EQC_PRE_(reinterpret_cast( - QS::rxPriv_.currObj[obj_kind])->m_nMin); - break; - case QS::TE_OBJ: - QS_OBJ_PRE_(reinterpret_cast( - QS::rxPriv_.currObj[obj_kind])->m_act); - QS_TEC_PRE_(reinterpret_cast( - QS::rxPriv_.currObj[obj_kind])->m_ctr); - QS_TEC_PRE_(reinterpret_cast( - QS::rxPriv_.currObj[obj_kind])->m_interval); - QS_SIG_PRE_(reinterpret_cast( - QS::rxPriv_.currObj[obj_kind])->sig); - QS_U8_PRE_ (reinterpret_cast( - QS::rxPriv_.currObj[obj_kind])->refCtr_); - break; - default: - break; - } - QS::endRec_(); - QS_CRIT_X_(); - - QS_REC_DONE(); // user callback (if defined) - } - else { - rxReportError_(static_cast(QS_RX_AO_FILTER)); - } - // no need to report Done + #ifdef Q_UTEST + tstPriv_.tpNum = 0U; + tstPriv_.testTime = 0U; + #endif // Q_UTEST } -//**************************************************************************** -void QS::rxParse(void) { +//${QS::QS-RX::rxParse} ...................................................... +void rxParse() { QSCtr tail = rxPriv_.tail; while (rxPriv_.head != tail) { // QS-RX buffer NOT empty? std::uint8_t b = rxPriv_.buf[tail]; @@ -378,137 +187,182 @@ void QS::rxParse(void) { } rxPriv_.tail = tail; // update the tail to a *valid* index - if (l_rx.esc != 0U) { // escaped byte arrived? - l_rx.esc = 0U; + if (QP::QS::rxPriv_.esc != 0U) { // escaped byte arrived? + QP::QS::rxPriv_.esc = 0U; b ^= QS_ESC_XOR; - l_rx.chksum += b; + QP::QS::rxPriv_.chksum += b; rxParseData_(b); } else if (b == QS_ESC) { - l_rx.esc = 1U; + QP::QS::rxPriv_.esc = 1U; } else if (b == QS_FRAME) { // get ready for the next frame - b = l_rx.state; // save the current state in b - l_rx.esc = 0U; + b = QP::QS::rxPriv_.state; // save the current state in b + QP::QS::rxPriv_.esc = 0U; tran_(WAIT4_SEQ); - if (l_rx.chksum == QS_GOOD_CHKSUM) { - l_rx.chksum = 0U; + if (QP::QS::rxPriv_.chksum == QS_GOOD_CHKSUM) { + QP::QS::rxPriv_.chksum = 0U; rxHandleGoodFrame_(b); } else { // bad checksum - l_rx.chksum = 0U; - rxReportError_(0x00U); + QP::QS::rxPriv_.chksum = 0U; + rxReportError_(0x41U); rxHandleBadFrame_(b); } } else { - l_rx.chksum += b; + QP::QS::rxPriv_.chksum += b; rxParseData_(b); } } } -//**************************************************************************** +//${QS::QS-RX::setCurrObj} ................................................... +void setCurrObj( + std::uint8_t const obj_kind, + void * const obj_ptr) +{ + QS_CRIT_STAT + QS_CRIT_ENTRY(); + Q_REQUIRE_INCRIT(300, obj_kind < Q_DIM(rxPriv_.currObj)); + + QS_MEM_SYS(); + + rxPriv_.currObj[obj_kind] = obj_ptr; + + QS_MEM_APP(); + QS_CRIT_EXIT(); +} + +//${QS::QS-RX::rxGetNfree} ................................................... +std::uint16_t rxGetNfree() noexcept { + // NOTE: Must be called IN critical section. + // Also requires system-level memory access (QF_MEM_SYS()). + + QSCtr const head = rxPriv_.head; + std::uint16_t nFree; + if (head == rxPriv_.tail) { // buffer empty? + nFree = static_cast(rxPriv_.end - 1U); + } + else if (head < rxPriv_.tail) { + nFree = static_cast(rxPriv_.tail - head - 1U); + } + else { + nFree = static_cast(rxPriv_.end + rxPriv_.tail + - head - 1U); + } + return nFree; +} + +} // namespace QS +} // namespace QP +//$enddef${QS::QS-RX} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +//============================================================================ +//! @cond INTERNAL +namespace { // unnamed local namespace + +//............................................................................ static void rxParseData_(std::uint8_t const b) noexcept { - switch (l_rx.state) { + switch (QP::QS::rxPriv_.state) { case WAIT4_SEQ: { - ++l_rx.seq; - if (l_rx.seq != b) { // not the expected sequence? + ++QP::QS::rxPriv_.seq; + if (QP::QS::rxPriv_.seq != b) { // not the expected sequence? rxReportError_(0x42U); - l_rx.seq = b; // update the sequence + QP::QS::rxPriv_.seq = b; // update the sequence } tran_(WAIT4_REC); break; } case WAIT4_REC: { switch (b) { - case QS_RX_INFO: + case QP::QS_RX_INFO: tran_(WAIT4_INFO_FRAME); break; - case QS_RX_COMMAND: + case QP::QS_RX_COMMAND: tran_(WAIT4_CMD_ID); break; - case QS_RX_RESET: + case QP::QS_RX_RESET: tran_(WAIT4_RESET_FRAME); break; - case QS_RX_TICK: + case QP::QS_RX_TICK: tran_(WAIT4_TICK_RATE); break; - case QS_RX_PEEK: - if (QS::rxPriv_.currObj[QS::AP_OBJ] != nullptr) { - l_rx.var.peek.offs = 0U; - l_rx.var.peek.idx = 0U; + case QP::QS_RX_PEEK: + if (QP::QS::rxPriv_.currObj[QP::QS::AP_OBJ] != nullptr) { + QP::QS::rxPriv_.var.peek.offs = 0U; + QP::QS::rxPriv_.var.peek.idx = 0U; tran_(WAIT4_PEEK_OFFS); } else { - rxReportError_(static_cast(QS_RX_PEEK)); + rxReportError_( + static_cast(QP::QS_RX_PEEK)); tran_(ERROR_STATE); } break; - case QS_RX_POKE: - case QS_RX_FILL: - l_rx.var.poke.fill = - (b == static_cast(QS_RX_FILL)) - ? 1U - : 0U; - if (QS::rxPriv_.currObj[QS::AP_OBJ] != nullptr) { - l_rx.var.poke.offs = 0U; - l_rx.var.poke.idx = 0U; + case QP::QS_RX_POKE: + case QP::QS_RX_FILL: + QP::QS::rxPriv_.var.poke.fill = + (b == static_cast(QP::QS_RX_FILL)) + ? 1U : 0U; + if (QP::QS::rxPriv_.currObj[QP::QS::AP_OBJ] != nullptr) { + QP::QS::rxPriv_.var.poke.offs = 0U; + QP::QS::rxPriv_.var.poke.idx = 0U; tran_(WAIT4_POKE_OFFS); } else { rxReportError_( - (l_rx.var.poke.fill != 0U) - ? static_cast(QS_RX_FILL) - : static_cast(QS_RX_POKE)); + (QP::QS::rxPriv_.var.poke.fill != 0U) + ? static_cast(QP::QS_RX_FILL) + : static_cast(QP::QS_RX_POKE)); tran_(ERROR_STATE); } break; - case QS_RX_GLB_FILTER: // intentionally fall-through - case QS_RX_LOC_FILTER: - l_rx.var.flt.recId = b; + case QP::QS_RX_GLB_FILTER: // intentionally fall-through + case QP::QS_RX_LOC_FILTER: + QP::QS::rxPriv_.var.flt.recId = b; tran_(WAIT4_FILTER_LEN); break; - case QS_RX_AO_FILTER: // intentionally fall-through - case QS_RX_CURR_OBJ: - l_rx.var.obj.recId = b; + case QP::QS_RX_AO_FILTER: // intentionally fall-through + case QP::QS_RX_CURR_OBJ: + QP::QS::rxPriv_.var.obj.recId = b; tran_(WAIT4_OBJ_KIND); break; - case QS_RX_QUERY_CURR: - l_rx.var.obj.recId = - static_cast(QS_RX_QUERY_CURR); + case QP::QS_RX_QUERY_CURR: + QP::QS::rxPriv_.var.obj.recId = + static_cast(QP::QS_RX_QUERY_CURR); tran_(WAIT4_QUERY_KIND); break; - case QS_RX_EVENT: + case QP::QS_RX_EVENT: tran_(WAIT4_EVT_PRIO); break; #ifdef Q_UTEST - case QS_RX_TEST_SETUP: + case QP::QS_RX_TEST_SETUP: tran_(WAIT4_TEST_SETUP_FRAME); break; - case QS_RX_TEST_TEARDOWN: + case QP::QS_RX_TEST_TEARDOWN: tran_(WAIT4_TEST_TEARDOWN_FRAME); break; - case QS_RX_TEST_CONTINUE: + case QP::QS_RX_TEST_CONTINUE: tran_(WAIT4_TEST_CONTINUE_FRAME); break; - case QS_RX_TEST_PROBE: - if (l_testData.tpNum + case QP::QS_RX_TEST_PROBE: + if (QP::QS::tstPriv_.tpNum < static_cast( - (sizeof(l_testData.tpBuf) - / sizeof(l_testData.tpBuf[0])))) + (sizeof(QP::QS::tstPriv_.tpBuf) + / sizeof(QP::QS::tstPriv_.tpBuf[0])))) { - l_rx.var.tp.data = 0U; - l_rx.var.tp.idx = 0U; + QP::QS::rxPriv_.var.tp.data = 0U; + QP::QS::rxPriv_.var.tp.idx = 0U; tran_(WAIT4_TEST_PROBE_DATA); } - else { // the number of Test-Probes exceeded + else { // the # Test-Probes exceeded rxReportError_( - static_cast(QS_RX_TEST_PROBE)); + static_cast(QP::QS_RX_TEST_PROBE)); tran_(ERROR_STATE); } break; @@ -526,40 +380,40 @@ static void rxParseData_(std::uint8_t const b) noexcept { break; } case WAIT4_CMD_ID: { - l_rx.var.cmd.cmdId = b; - l_rx.var.cmd.idx = 0U; - l_rx.var.cmd.param1 = 0U; - l_rx.var.cmd.param2 = 0U; - l_rx.var.cmd.param3 = 0U; + QP::QS::rxPriv_.var.cmd.cmdId = b; + QP::QS::rxPriv_.var.cmd.idx = 0U; + QP::QS::rxPriv_.var.cmd.param1 = 0U; + QP::QS::rxPriv_.var.cmd.param2 = 0U; + QP::QS::rxPriv_.var.cmd.param3 = 0U; tran_(WAIT4_CMD_PARAM1); break; } case WAIT4_CMD_PARAM1: { - l_rx.var.cmd.param1 |= - (static_cast(b) << l_rx.var.cmd.idx); - l_rx.var.cmd.idx += 8U; - if (l_rx.var.cmd.idx == (8U*4U)) { - l_rx.var.cmd.idx = 0U; + QP::QS::rxPriv_.var.cmd.param1 |= + (static_cast(b) << QP::QS::rxPriv_.var.cmd.idx); + QP::QS::rxPriv_.var.cmd.idx += 8U; + if (QP::QS::rxPriv_.var.cmd.idx == (8U * 4U)) { + QP::QS::rxPriv_.var.cmd.idx = 0U; tran_(WAIT4_CMD_PARAM2); } break; } case WAIT4_CMD_PARAM2: { - l_rx.var.cmd.param2 |= - static_cast(b) << l_rx.var.cmd.idx; - l_rx.var.cmd.idx += 8U; - if (l_rx.var.cmd.idx == (8U*4U)) { - l_rx.var.cmd.idx = 0U; + QP::QS::rxPriv_.var.cmd.param2 |= + static_cast(b) << QP::QS::rxPriv_.var.cmd.idx; + QP::QS::rxPriv_.var.cmd.idx += 8U; + if (QP::QS::rxPriv_.var.cmd.idx == (8U * 4U)) { + QP::QS::rxPriv_.var.cmd.idx = 0U; tran_(WAIT4_CMD_PARAM3); } break; } case WAIT4_CMD_PARAM3: { - l_rx.var.cmd.param3 |= - static_cast(b) << l_rx.var.cmd.idx; - l_rx.var.cmd.idx += 8U; - if (l_rx.var.cmd.idx == (8U*4U)) { - l_rx.var.cmd.idx = 0U; + QP::QS::rxPriv_.var.cmd.param3 |= + static_cast(b) << QP::QS::rxPriv_.var.cmd.idx; + QP::QS::rxPriv_.var.cmd.idx += 8U; + if (QP::QS::rxPriv_.var.cmd.idx == (8U * 4U)) { + QP::QS::rxPriv_.var.cmd.idx = 0U; tran_(WAIT4_CMD_FRAME); } break; @@ -573,7 +427,7 @@ static void rxParseData_(std::uint8_t const b) noexcept { break; } case WAIT4_TICK_RATE: { - l_rx.var.tick.rate = static_cast(b); + QP::QS::rxPriv_.var.tick.rate = static_cast(b); tran_(WAIT4_TICK_FRAME); break; } @@ -582,12 +436,12 @@ static void rxParseData_(std::uint8_t const b) noexcept { break; } case WAIT4_PEEK_OFFS: { - if (l_rx.var.peek.idx == 0U) { - l_rx.var.peek.offs = static_cast(b); - l_rx.var.peek.idx += 8U; + if (QP::QS::rxPriv_.var.peek.idx == 0U) { + QP::QS::rxPriv_.var.peek.offs = static_cast(b); + QP::QS::rxPriv_.var.peek.idx += 8U; } else { - l_rx.var.peek.offs |= static_cast( + QP::QS::rxPriv_.var.peek.offs |= static_cast( static_cast(b) << 8U); tran_(WAIT4_PEEK_SIZE); } @@ -595,17 +449,17 @@ static void rxParseData_(std::uint8_t const b) noexcept { } case WAIT4_PEEK_SIZE: { if ((b == 1U) || (b == 2U) || (b == 4U)) { - l_rx.var.peek.size = b; + QP::QS::rxPriv_.var.peek.size = b; tran_(WAIT4_PEEK_NUM); } else { - rxReportError_(static_cast(QS_RX_PEEK)); + rxReportError_(static_cast(QP::QS_RX_PEEK)); tran_(ERROR_STATE); } break; } case WAIT4_PEEK_NUM: { - l_rx.var.peek.num = b; + QP::QS::rxPriv_.var.peek.num = b; tran_(WAIT4_PEEK_FRAME); break; } @@ -614,67 +468,64 @@ static void rxParseData_(std::uint8_t const b) noexcept { break; } case WAIT4_POKE_OFFS: { - if (l_rx.var.poke.idx == 0U) { - l_rx.var.poke.offs = static_cast(b); - l_rx.var.poke.idx = 1U; + if (QP::QS::rxPriv_.var.poke.idx == 0U) { + QP::QS::rxPriv_.var.poke.offs = static_cast(b); + QP::QS::rxPriv_.var.poke.idx = 1U; } else { - l_rx.var.poke.offs |= static_cast( + QP::QS::rxPriv_.var.poke.offs |= static_cast( static_cast(b) << 8U); tran_(WAIT4_POKE_SIZE); } break; } case WAIT4_POKE_SIZE: { - if ((b == 1U) - || (b == 2U) - || (b == 4U)) - { - l_rx.var.poke.size = b; + if ((b == 1U) || (b == 2U) || (b == 4U)) { + QP::QS::rxPriv_.var.poke.size = b; tran_(WAIT4_POKE_NUM); } else { - rxReportError_((l_rx.var.poke.fill != 0U) - ? static_cast(QS_RX_FILL) - : static_cast(QS_RX_POKE)); + rxReportError_((QP::QS::rxPriv_.var.poke.fill != 0U) + ? static_cast(QP::QS_RX_FILL) + : static_cast(QP::QS_RX_POKE)); tran_(ERROR_STATE); } break; } case WAIT4_POKE_NUM: { if (b > 0U) { - l_rx.var.poke.num = b; - l_rx.var.poke.data = 0U; - l_rx.var.poke.idx = 0U; - tran_((l_rx.var.poke.fill != 0U) + QP::QS::rxPriv_.var.poke.num = b; + QP::QS::rxPriv_.var.poke.data = 0U; + QP::QS::rxPriv_.var.poke.idx = 0U; + tran_((QP::QS::rxPriv_.var.poke.fill != 0U) ? WAIT4_FILL_DATA : WAIT4_POKE_DATA); } else { - rxReportError_((l_rx.var.poke.fill != 0U) - ? static_cast(QS_RX_FILL) - : static_cast(QS_RX_POKE)); + rxReportError_((QP::QS::rxPriv_.var.poke.fill != 0U) + ? static_cast(QP::QS_RX_FILL) + : static_cast(QP::QS_RX_POKE)); tran_(ERROR_STATE); } break; } case WAIT4_FILL_DATA: { - l_rx.var.poke.data |= - static_cast(b) << l_rx.var.poke.idx; - l_rx.var.poke.idx += 8U; - if ((l_rx.var.poke.idx >> 3U) == l_rx.var.poke.size) { + QP::QS::rxPriv_.var.poke.data |= + static_cast(b) << QP::QS::rxPriv_.var.poke.idx; + QP::QS::rxPriv_.var.poke.idx += 8U; + if ((QP::QS::rxPriv_.var.poke.idx >> 3U) == QP::QS::rxPriv_.var.poke.size) { tran_(WAIT4_FILL_FRAME); } break; } case WAIT4_POKE_DATA: { - l_rx.var.poke.data |= - static_cast(b) << l_rx.var.poke.idx; - l_rx.var.poke.idx += 8U; - if ((l_rx.var.poke.idx >> 3U) == l_rx.var.poke.size) { + QP::QS::rxPriv_.var.poke.data |= + static_cast(b) << QP::QS::rxPriv_.var.poke.idx; + QP::QS::rxPriv_.var.poke.idx += 8U; + if ((QP::QS::rxPriv_.var.poke.idx >> 3U) == QP::QS::rxPriv_.var.poke.size) { rxPoke_(); - --l_rx.var.poke.num; - if (l_rx.var.poke.num == 0U) { + --QP::QS::rxPriv_.var.poke.num; + if (QP::QS::rxPriv_.var.poke.num == 0U) { tran_(WAIT4_POKE_FRAME); } } @@ -689,20 +540,20 @@ static void rxParseData_(std::uint8_t const b) noexcept { break; } case WAIT4_FILTER_LEN: { - if (b == static_cast(sizeof(l_rx.var.flt.data))) { - l_rx.var.flt.idx = 0U; + if (b == static_cast(sizeof(QP::QS::rxPriv_.var.flt.data))) { + QP::QS::rxPriv_.var.flt.idx = 0U; tran_(WAIT4_FILTER_DATA); } else { - rxReportError_(l_rx.var.flt.recId); + rxReportError_(QP::QS::rxPriv_.var.flt.recId); tran_(ERROR_STATE); } break; } case WAIT4_FILTER_DATA: { - l_rx.var.flt.data[l_rx.var.flt.idx] = b; - ++l_rx.var.flt.idx; - if (l_rx.var.flt.idx == sizeof(l_rx.var.flt.data)) { + QP::QS::rxPriv_.var.flt.data[QP::QS::rxPriv_.var.flt.idx] = b; + ++QP::QS::rxPriv_.var.flt.idx; + if (QP::QS::rxPriv_.var.flt.idx == sizeof(QP::QS::rxPriv_.var.flt.data)) { tran_(WAIT4_FILTER_FRAME); } break; @@ -712,23 +563,23 @@ static void rxParseData_(std::uint8_t const b) noexcept { break; } case WAIT4_OBJ_KIND: { - if (b <= static_cast(QS::SM_AO_OBJ)) { - l_rx.var.obj.kind = b; - l_rx.var.obj.addr = 0U; - l_rx.var.obj.idx = 0U; + if (b <= static_cast(QP::QS::SM_AO_OBJ)) { + QP::QS::rxPriv_.var.obj.kind = b; + QP::QS::rxPriv_.var.obj.addr = 0U; + QP::QS::rxPriv_.var.obj.idx = 0U; tran_(WAIT4_OBJ_ADDR); } else { - rxReportError_(l_rx.var.obj.recId); + rxReportError_(QP::QS::rxPriv_.var.obj.recId); tran_(ERROR_STATE); } break; } case WAIT4_OBJ_ADDR: { - l_rx.var.obj.addr |= - static_cast(b) << l_rx.var.obj.idx; - l_rx.var.obj.idx += 8U; - if (l_rx.var.obj.idx + QP::QS::rxPriv_.var.obj.addr |= + static_cast(b) << QP::QS::rxPriv_.var.obj.idx; + QP::QS::rxPriv_.var.obj.idx += 8U; + if (QP::QS::rxPriv_.var.obj.idx == (8U * static_cast(QS_OBJ_PTR_SIZE))) { tran_(WAIT4_OBJ_FRAME); @@ -740,12 +591,12 @@ static void rxParseData_(std::uint8_t const b) noexcept { break; } case WAIT4_QUERY_KIND: { - if (b < static_cast(QS::MAX_OBJ)) { - l_rx.var.obj.kind = b; + if (b < static_cast(QP::QS::MAX_OBJ)) { + QP::QS::rxPriv_.var.obj.kind = b; tran_(WAIT4_QUERY_FRAME); } else { - rxReportError_(l_rx.var.obj.recId); + rxReportError_(QP::QS::rxPriv_.var.obj.recId); tran_(ERROR_STATE); } break; @@ -755,47 +606,47 @@ static void rxParseData_(std::uint8_t const b) noexcept { break; } case WAIT4_EVT_PRIO: { - l_rx.var.evt.prio = b; - l_rx.var.evt.sig = 0U; - l_rx.var.evt.idx = 0U; + QP::QS::rxPriv_.var.evt.prio = b; + QP::QS::rxPriv_.var.evt.sig = 0U; + QP::QS::rxPriv_.var.evt.idx = 0U; tran_(WAIT4_EVT_SIG); break; } case WAIT4_EVT_SIG: { - l_rx.var.evt.sig |= static_cast( - static_cast(b) << l_rx.var.evt.idx); - l_rx.var.evt.idx += 8U; - if (l_rx.var.evt.idx + QP::QS::rxPriv_.var.evt.sig |= static_cast( + static_cast(b) << QP::QS::rxPriv_.var.evt.idx); + QP::QS::rxPriv_.var.evt.idx += 8U; + if (QP::QS::rxPriv_.var.evt.idx == (8U *static_cast(Q_SIGNAL_SIZE))) { - l_rx.var.evt.len = 0U; - l_rx.var.evt.idx = 0U; + QP::QS::rxPriv_.var.evt.len = 0U; + QP::QS::rxPriv_.var.evt.idx = 0U; tran_(WAIT4_EVT_LEN); } break; } case WAIT4_EVT_LEN: { - l_rx.var.evt.len |= static_cast( - static_cast(b) << l_rx.var.evt.idx); - l_rx.var.evt.idx += 8U; - if (l_rx.var.evt.idx == (8U * 2U)) { - if ((l_rx.var.evt.len + sizeof(QEvt)) - <= static_cast(QF::poolGetMaxBlockSize())) + QP::QS::rxPriv_.var.evt.len |= static_cast( + static_cast(b) << QP::QS::rxPriv_.var.evt.idx); + QP::QS::rxPriv_.var.evt.idx += 8U; + if (QP::QS::rxPriv_.var.evt.idx == (8U * 2U)) { + if ((QP::QS::rxPriv_.var.evt.len + sizeof(QP::QEvt)) + <= QP::QF::poolGetMaxBlockSize()) { // report Ack before generating any other QS records - rxReportAck_(QS_RX_EVENT); + rxReportAck_(QP::QS_RX_EVENT); - l_rx.var.evt.e = QF::newX_( - (static_cast(l_rx.var.evt.len) - + sizeof(QEvt)), + QP::QS::rxPriv_.var.evt.e = QP::QF::newX_( + (static_cast(QP::QS::rxPriv_.var.evt.len) + + sizeof(QP::QEvt)), 0U, // margin - static_cast(l_rx.var.evt.sig)); + static_cast(QP::QS::rxPriv_.var.evt.sig)); // event allocated? - if (l_rx.var.evt.e != nullptr) { - l_rx.var.evt.p = - reinterpret_cast(l_rx.var.evt.e); - l_rx.var.evt.p += sizeof(QEvt); - if (l_rx.var.evt.len > 0U) { + if (QP::QS::rxPriv_.var.evt.e != nullptr) { + QP::QS::rxPriv_.var.evt.p = + reinterpret_cast(QP::QS::rxPriv_.var.evt.e); + QP::QS::rxPriv_.var.evt.p = &QP::QS::rxPriv_.var.evt.p[sizeof(QP::QEvt)]; + if (QP::QS::rxPriv_.var.evt.len > 0U) { tran_(WAIT4_EVT_PAR); } else { @@ -803,22 +654,24 @@ static void rxParseData_(std::uint8_t const b) noexcept { } } else { - rxReportError_(static_cast(QS_RX_EVENT)); + rxReportError_( + static_cast(QP::QS_RX_EVENT)); tran_(ERROR_STATE); } } else { - rxReportError_(static_cast(QS_RX_EVENT)); + rxReportError_( + static_cast(QP::QS_RX_EVENT)); tran_(ERROR_STATE); } } break; } case WAIT4_EVT_PAR: { // event parameters - *l_rx.var.evt.p = b; - ++l_rx.var.evt.p; - --l_rx.var.evt.len; - if (l_rx.var.evt.len == 0U) { + *QP::QS::rxPriv_.var.evt.p = b; + ++QP::QS::rxPriv_.var.evt.p; + --QP::QS::rxPriv_.var.evt.len; + if (QP::QS::rxPriv_.var.evt.len == 0U) { tran_(WAIT4_EVT_FRAME); } break; @@ -842,21 +695,21 @@ static void rxParseData_(std::uint8_t const b) noexcept { break; } case WAIT4_TEST_PROBE_DATA: { - l_rx.var.tp.data |= - (static_cast(b) << l_rx.var.tp.idx); - l_rx.var.tp.idx += 8U; - if (l_rx.var.tp.idx == (8U * sizeof(std::uint32_t))) { - l_rx.var.tp.addr = 0U; - l_rx.var.tp.idx = 0U; + QP::QS::rxPriv_.var.tp.data |= + (static_cast(b) << QP::QS::rxPriv_.var.tp.idx); + QP::QS::rxPriv_.var.tp.idx += 8U; + if (QP::QS::rxPriv_.var.tp.idx == (8U * sizeof(std::uint32_t))) { + QP::QS::rxPriv_.var.tp.addr = 0U; + QP::QS::rxPriv_.var.tp.idx = 0U; tran_(WAIT4_TEST_PROBE_ADDR); } break; } case WAIT4_TEST_PROBE_ADDR: { - l_rx.var.tp.addr |= - (static_cast(b) << l_rx.var.tp.idx); - l_rx.var.tp.idx += 8U; - if (l_rx.var.tp.idx + QP::QS::rxPriv_.var.tp.addr |= + (static_cast(b) << QP::QS::rxPriv_.var.tp.idx); + QP::QS::rxPriv_.var.tp.idx += 8U; + if (QP::QS::rxPriv_.var.tp.idx == (8U * static_cast(QS_FUN_PTR_SIZE))) { tran_(WAIT4_TEST_PROBE_FRAME); @@ -881,272 +734,288 @@ static void rxParseData_(std::uint8_t const b) noexcept { } } -//**************************************************************************** -void QS::rxHandleGoodFrame_(std::uint8_t const state) { +//............................................................................ +void rxHandleGoodFrame_(std::uint8_t const state) { std::uint8_t i; std::uint8_t *ptr; - QS_CRIT_STAT_ + QS_CRIT_STAT switch (state) { case WAIT4_INFO_FRAME: { // no need to report Ack or Done - QS_CRIT_E_(); - QS_target_info_(0U); // send only Target info - QS_CRIT_X_(); + QS_CRIT_ENTRY(); + QS_MEM_SYS(); + QP::QS::target_info_pre_(0U); // send only Target info + QS_MEM_APP(); + QS_CRIT_EXIT(); break; } case WAIT4_RESET_FRAME: { // no need to report Ack or Done, because Target resets - QS::onReset(); // reset the Target + QP::QS::onReset(); // reset the Target break; } case WAIT4_CMD_PARAM1: // intentionally fall-through case WAIT4_CMD_PARAM2: // intentionally fall-through case WAIT4_CMD_PARAM3: // intentionally fall-through case WAIT4_CMD_FRAME: { - rxReportAck_(QS_RX_COMMAND); - QS::onCommand(l_rx.var.cmd.cmdId, l_rx.var.cmd.param1, - l_rx.var.cmd.param2, l_rx.var.cmd.param3); + rxReportAck_(QP::QS_RX_COMMAND); + QP::QS::onCommand(QP::QS::rxPriv_.var.cmd.cmdId, QP::QS::rxPriv_.var.cmd.param1, + QP::QS::rxPriv_.var.cmd.param2, QP::QS::rxPriv_.var.cmd.param3); #ifdef Q_UTEST - QS::processTestEvts_(); // process all events produced -#endif - rxReportDone_(QS_RX_COMMAND); + #if Q_UTEST != 0 + QP::QS::processTestEvts_(); // process all events produced + #endif // Q_UTEST != 0 +#endif // Q_UTEST + rxReportDone_(QP::QS_RX_COMMAND); break; } case WAIT4_TICK_FRAME: { - rxReportAck_(QS_RX_TICK); + rxReportAck_(QP::QS_RX_TICK); #ifdef Q_UTEST - QS::tickX_(l_rx.var.tick.rate, &QS::rxPriv_); // process tick - QS::processTestEvts_(); // process all events produced + QP::QTimeEvt::tick1_( + static_cast(QP::QS::rxPriv_.var.tick.rate), + &QP::QS::rxPriv_); + #if Q_UTEST != 0 + QP::QS::processTestEvts_(); // process all events produced + #endif // Q_UTEST != 0 #else - QF::tickX_(static_cast(l_rx.var.tick.rate), - &QS::rxPriv_); -#endif - rxReportDone_(QS_RX_TICK); + QP::QTimeEvt::tick( + static_cast(QP::QS::rxPriv_.var.tick.rate), + &QP::QS::rxPriv_); +#endif // Q_UTEST + rxReportDone_(QP::QS_RX_TICK); break; } case WAIT4_PEEK_FRAME: { // no need to report Ack or Done - QS_CRIT_E_(); - QS::beginRec_(static_cast(QS_PEEK_DATA)); - ptr = (static_cast( - QS::rxPriv_.currObj[QS::AP_OBJ]) - + l_rx.var.peek.offs); + QS_CRIT_ENTRY(); + QS_MEM_SYS(); + QP::QS::beginRec_(static_cast( + QP::QS_PEEK_DATA)); + ptr = static_cast( + QP::QS::rxPriv_.currObj[QP::QS::AP_OBJ]); + ptr = &ptr[QP::QS::rxPriv_.var.peek.offs]; QS_TIME_PRE_(); // timestamp - QS_U16_PRE_(l_rx.var.peek.offs); // data offset - QS_U8_PRE_(l_rx.var.peek.size); // data size - QS_U8_PRE_(l_rx.var.peek.num); // number of data items - for (i = 0U; i < l_rx.var.peek.num; ++i) { - switch (l_rx.var.peek.size) { + QS_U16_PRE_(QP::QS::rxPriv_.var.peek.offs); // data offset + QS_U8_PRE_(QP::QS::rxPriv_.var.peek.size); // data size + QS_U8_PRE_(QP::QS::rxPriv_.var.peek.num); // number of data items + for (i = 0U; i < QP::QS::rxPriv_.var.peek.num; ++i) { + switch (QP::QS::rxPriv_.var.peek.size) { case 1: - QS_U8_PRE_(*(ptr + i)); + QS_U8_PRE_(ptr[i]); break; case 2: QS_U16_PRE_( - *(reinterpret_cast(ptr) + i)); + reinterpret_cast(ptr)[i]); break; case 4: QS_U32_PRE_( - *(reinterpret_cast(ptr) + i)); + reinterpret_cast(ptr)[i]); break; default: + // intentionally empty break; } } - QS::endRec_(); - QS_CRIT_X_(); + QP::QS::endRec_(); + QS_MEM_APP(); + QS_CRIT_EXIT(); QS_REC_DONE(); // user callback (if defined) break; } case WAIT4_POKE_DATA: { // received less than expected poke data items - rxReportError_(static_cast(QS_RX_POKE)); + rxReportError_(static_cast(QP::QS_RX_POKE)); break; } case WAIT4_POKE_FRAME: { - rxReportAck_(QS_RX_POKE); + rxReportAck_(QP::QS_RX_POKE); // no need to report done break; } case WAIT4_FILL_FRAME: { - rxReportAck_(QS_RX_FILL); - ptr = (static_cast( - QS::rxPriv_.currObj[QS::AP_OBJ]) - + l_rx.var.poke.offs); - for (i = 0U; i < l_rx.var.poke.num; ++i) { - switch (l_rx.var.poke.size) { + rxReportAck_(QP::QS_RX_FILL); + ptr = static_cast( + QP::QS::rxPriv_.currObj[QP::QS::AP_OBJ]); + ptr = &ptr[QP::QS::rxPriv_.var.poke.offs]; + for (i = 0U; i < QP::QS::rxPriv_.var.poke.num; ++i) { + switch (QP::QS::rxPriv_.var.poke.size) { case 1: - *(ptr + i) = - static_cast(l_rx.var.poke.data); + ptr[i] = + static_cast(QP::QS::rxPriv_.var.poke.data); break; case 2: - *(reinterpret_cast(ptr) + i) = - static_cast(l_rx.var.poke.data); + reinterpret_cast(ptr)[i] = + static_cast(QP::QS::rxPriv_.var.poke.data); break; case 4: - *(reinterpret_cast(ptr) + i) = - l_rx.var.poke.data; + reinterpret_cast(ptr)[i] = + QP::QS::rxPriv_.var.poke.data; break; default: + // intentionally empty break; } } break; } case WAIT4_FILTER_FRAME: { - rxReportAck_(static_cast(l_rx.var.flt.recId)); + rxReportAck_(static_cast( + QP::QS::rxPriv_.var.flt.recId)); // apply the received filters - if (l_rx.var.flt.recId - == static_cast(QS_RX_GLB_FILTER)) + if (QP::QS::rxPriv_.var.flt.recId + == static_cast(QP::QS_RX_GLB_FILTER)) { for (i = 0U; - i < static_cast(sizeof(priv_.glbFilter)); + i < static_cast(sizeof(QP::QS::filt_.glb)); ++i) { - priv_.glbFilter[i] = l_rx.var.flt.data[i]; + QP::QS::filt_.glb[i] = QP::QS::rxPriv_.var.flt.data[i]; } // leave the "not maskable" filters enabled, - // see qs.h, Miscellaneous QS records (not maskable) - // - priv_.glbFilter[0] |= 0x01U; - priv_.glbFilter[7] |= 0xFCU; - priv_.glbFilter[8] |= 0x7FU; + // see qs.hpp, Miscellaneous QS records (not maskable) + QP::QS::filt_.glb[0] |= 0x01U; + QP::QS::filt_.glb[7] |= 0xFCU; + QP::QS::filt_.glb[8] |= 0x7FU; // never enable the last 3 records (0x7D, 0x7E, 0x7F) - priv_.glbFilter[15] &= 0x1FU; + QP::QS::filt_.glb[15] &= 0x1FU; } - else if (l_rx.var.flt.recId - == static_cast(QS_RX_LOC_FILTER)) + else if (QP::QS::rxPriv_.var.flt.recId + == static_cast(QP::QS_RX_LOC_FILTER)) { - for (i = 0U; i < Q_DIM(priv_.locFilter); ++i) { - priv_.locFilter[i] = l_rx.var.flt.data[i]; + for (i = 0U; i < Q_DIM(QP::QS::filt_.loc); ++i) { + QP::QS::filt_.loc[i] = QP::QS::rxPriv_.var.flt.data[i]; } // leave QS_ID == 0 always on - priv_.locFilter[0] |= 0x01U; + QP::QS::filt_.loc[0] |= 0x01U; } else { - rxReportError_(l_rx.var.flt.recId); + rxReportError_(QP::QS::rxPriv_.var.flt.recId); } - // no need to report Done break; } case WAIT4_OBJ_FRAME: { - i = l_rx.var.obj.kind; - if (i < static_cast(QS::MAX_OBJ)) { - if (l_rx.var.obj.recId - == static_cast(QS_RX_CURR_OBJ)) + i = QP::QS::rxPriv_.var.obj.kind; + if (i < static_cast(QP::QS::MAX_OBJ)) { + if (QP::QS::rxPriv_.var.obj.recId + == static_cast(QP::QS_RX_CURR_OBJ)) { - rxPriv_.currObj[i] = - reinterpret_cast(l_rx.var.obj.addr); - rxReportAck_(QS_RX_CURR_OBJ); + QP::QS::rxPriv_.currObj[i] = + reinterpret_cast(QP::QS::rxPriv_.var.obj.addr); + rxReportAck_(QP::QS_RX_CURR_OBJ); } - else if (l_rx.var.obj.recId - == static_cast(QS_RX_AO_FILTER)) + else if (QP::QS::rxPriv_.var.obj.recId + == static_cast(QP::QS_RX_AO_FILTER)) { - if (l_rx.var.obj.addr != 0U) { - std::int_fast16_t filter = + if (QP::QS::rxPriv_.var.obj.addr != 0U) { + std::int_fast16_t const filter = static_cast( - reinterpret_cast( - l_rx.var.obj.addr)->m_prio); - locFilter_((i == 0) - ? filter - :-filter); - rxReportAck_(QS_RX_AO_FILTER); + reinterpret_cast( + QP::QS::rxPriv_.var.obj.addr)->getPrio()); + QP::QS::locFilter_((i == 0) + ? filter + :-filter); + rxReportAck_(QP::QS_RX_AO_FILTER); } else { - rxReportError_(QS_RX_AO_FILTER); + rxReportError_(static_cast( + QP::QS_RX_AO_FILTER)); } } else { - rxReportError_(l_rx.var.obj.recId); + rxReportError_(QP::QS::rxPriv_.var.obj.recId); } } // both SM and AO - else if (i == static_cast(QS::SM_AO_OBJ)) { - if (l_rx.var.obj.recId - == static_cast(QS_RX_CURR_OBJ)) + else if (i == static_cast(QP::QS::SM_AO_OBJ)) { + if (QP::QS::rxPriv_.var.obj.recId + == static_cast(QP::QS_RX_CURR_OBJ)) { - rxPriv_.currObj[SM_OBJ] = (void *)l_rx.var.obj.addr; - rxPriv_.currObj[AO_OBJ] = (void *)l_rx.var.obj.addr; + QP::QS::rxPriv_.currObj[QP::QS::SM_OBJ] + = reinterpret_cast(QP::QS::rxPriv_.var.obj.addr); + QP::QS::rxPriv_.currObj[QP::QS::AO_OBJ] + = reinterpret_cast(QP::QS::rxPriv_.var.obj.addr); } rxReportAck_( - static_cast(l_rx.var.obj.recId)); + static_cast(QP::QS::rxPriv_.var.obj.recId)); } else { - rxReportError_(l_rx.var.obj.recId); + rxReportError_(QP::QS::rxPriv_.var.obj.recId); } break; } case WAIT4_QUERY_FRAME: { - queryCurrObj(l_rx.var.obj.kind); + queryCurrObj_(QP::QS::rxPriv_.var.obj.kind); break; } case WAIT4_EVT_FRAME: { // NOTE: Ack was already reported in the WAIT4_EVT_LEN state #ifdef Q_UTEST - QS::onTestEvt(l_rx.var.evt.e); // "massage" the event, if needed -#endif // Q_UTEST + QP::QS::onTestEvt(QP::QS::rxPriv_.var.evt.e); // "massage" the event +#endif // Q_UTEST // use 'i' as status, 0 == success,no-recycle i = 0U; - if (l_rx.var.evt.prio == 0U) { // publish - QF::publish_(l_rx.var.evt.e, &QS::rxPriv_, 0U); + if (QP::QS::rxPriv_.var.evt.prio == 0U) { // publish + QP::QActive::publish_(QP::QS::rxPriv_.var.evt.e, &QP::QS::rxPriv_, 0U); } - else if (l_rx.var.evt.prio < QF_MAX_ACTIVE) { - if (!QF::active_[l_rx.var.evt.prio]->POST_X( - l_rx.var.evt.e, + else if (QP::QS::rxPriv_.var.evt.prio < QF_MAX_ACTIVE) { + if (!QP::QActive::registry_[QP::QS::rxPriv_.var.evt.prio]->POST_X( + QP::QS::rxPriv_.var.evt.e, 0U, // margin - &QS::rxPriv_)) + &QP::QS::rxPriv_)) { // failed QACTIVE_POST() recycles the event - i = 0x80U; // failure, no recycle + i = 0x80U; // failure status, no recycle } } - else if (l_rx.var.evt.prio == 255U) { + else if (QP::QS::rxPriv_.var.evt.prio == 255U) { // dispatch to the current SM object - if (QS::rxPriv_.currObj[QS::SM_OBJ] != nullptr) { + if (QP::QS::rxPriv_.currObj[QP::QS::SM_OBJ] != nullptr) { // increment the ref-ctr to simulate the situation - // when the event is just retreived from a queue. + // when the event is just retrieved from a queue. // This is expected for the following QF::gc() call. - // - QF_EVT_REF_CTR_INC_(l_rx.var.evt.e); + QP::QEvt_refCtr_inc_(QP::QS::rxPriv_.var.evt.e); - static_cast(QS::rxPriv_.currObj[QS::SM_OBJ]) - ->dispatch(l_rx.var.evt.e, 0U); + static_cast( + QP::QS::rxPriv_.currObj[QP::QS::SM_OBJ]) + ->dispatch(QP::QS::rxPriv_.var.evt.e, 0U); i = 0x01U; // success, recycle } else { i = 0x81U; // failure, recycle } } - else if (l_rx.var.evt.prio == 254U) { + else if (QP::QS::rxPriv_.var.evt.prio == 254U) { // init the current SM object" - if (QS::rxPriv_.currObj[QS::SM_OBJ] != nullptr) { + if (QP::QS::rxPriv_.currObj[QP::QS::SM_OBJ] != nullptr) { // increment the ref-ctr to simulate the situation - // when the event is just retreived from a queue. + // when the event is just retrieved from a queue. // This is expected for the following QF::gc() call. - // - QF_EVT_REF_CTR_INC_(l_rx.var.evt.e); + QP::QEvt_refCtr_inc_(QP::QS::rxPriv_.var.evt.e); - static_cast(QS::rxPriv_.currObj[QS::SM_OBJ]) - ->init(l_rx.var.evt.e, 0U); + static_cast( + QP::QS::rxPriv_.currObj[QP::QS::SM_OBJ]) + ->init(QP::QS::rxPriv_.var.evt.e, 0U); i = 0x01U; // success, recycle } else { i = 0x81U; // failure, recycle } } - else if (l_rx.var.evt.prio == 253U) { + else if (QP::QS::rxPriv_.var.evt.prio == 253U) { // post to the current AO - if (QS::rxPriv_.currObj[QS::AO_OBJ] != nullptr) { - if (!static_cast( - QS::rxPriv_.currObj[QS::AO_OBJ])->POST_X( - l_rx.var.evt.e, + if (QP::QS::rxPriv_.currObj[QP::QS::AO_OBJ] != nullptr) { + if (!static_cast( + QP::QS::rxPriv_.currObj[QP::QS::AO_OBJ])->POST_X( + QP::QS::rxPriv_.var.evt.e, 0U, // margin - &QS::rxPriv_)) + &QP::QS::rxPriv_)) { // failed QACTIVE_POST() recycles the event i = 0x80U; // failure, no recycle @@ -1160,52 +1029,57 @@ void QS::rxHandleGoodFrame_(std::uint8_t const state) { i = 0x81U; // failure, recycle } +#if (QF_MAX_EPOOL > 0U) // recycle needed? if ((i & 1U) != 0U) { - QF::gc(l_rx.var.evt.e); + QP::QF::gc(QP::QS::rxPriv_.var.evt.e); } +#endif // failure? if ((i & 0x80U) != 0U) { - rxReportError_(static_cast(QS_RX_EVENT)); + rxReportError_(static_cast(QP::QS_RX_EVENT)); } else { #ifdef Q_UTEST - QS::processTestEvts_(); // process all events produced -#endif - rxReportDone_(QS_RX_EVENT); + #if Q_UTEST != 0 + QP::QS::processTestEvts_(); // process all events produced + #endif // Q_UTEST != 0 +#endif // Q_UTEST + rxReportDone_(QP::QS_RX_EVENT); } break; } #ifdef Q_UTEST case WAIT4_TEST_SETUP_FRAME: { - rxReportAck_(QS_RX_TEST_SETUP); - l_testData.tpNum = 0U; // clear Test-Probes - l_testData.testTime = 0U; //clear time tick + rxReportAck_(QP::QS_RX_TEST_SETUP); + QP::QS::tstPriv_.tpNum = 0U; // clear Test-Probes + QP::QS::tstPriv_.testTime = 0U; //clear time tick // don't clear current objects - QS::onTestSetup(); // application-specific test setup + QP::QS::onTestSetup(); // application-specific test setup // no need to report Done break; } case WAIT4_TEST_TEARDOWN_FRAME: { - rxReportAck_(QS_RX_TEST_TEARDOWN); - QS::onTestTeardown(); // application-specific test teardown + rxReportAck_(QP::QS_RX_TEST_TEARDOWN); + QP::QS::onTestTeardown(); // application-specific test teardown // no need to report Done break; } case WAIT4_TEST_CONTINUE_FRAME: { - rxReportAck_(QS_RX_TEST_CONTINUE); - QS::rxPriv_.inTestLoop = false; // exit the QUTest loop + rxReportAck_(QP::QS_RX_TEST_CONTINUE); + QP::QS::rxPriv_.inTestLoop = false; // exit the QUTest loop // no need to report Done break; } case WAIT4_TEST_PROBE_FRAME: { - rxReportAck_(QS_RX_TEST_PROBE); - Q_ASSERT_ID(815, - l_testData.tpNum - < (sizeof(l_testData.tpBuf) / sizeof(l_testData.tpBuf[0]))); - l_testData.tpBuf[l_testData.tpNum] = l_rx.var.tp; - ++l_testData.tpNum; + rxReportAck_(QP::QS_RX_TEST_PROBE); + Q_ASSERT_INCRIT(815, + QP::QS::tstPriv_.tpNum + < (sizeof(QP::QS::tstPriv_.tpBuf) + / sizeof(QP::QS::tstPriv_.tpBuf[0]))); + QP::QS::tstPriv_.tpBuf[QP::QS::tstPriv_.tpNum] = QP::QS::rxPriv_.var.tp; + ++QP::QS::tstPriv_.tpNum; // no need to report Done break; } @@ -1222,133 +1096,151 @@ void QS::rxHandleGoodFrame_(std::uint8_t const state) { } } -//**************************************************************************** +//............................................................................ static void rxHandleBadFrame_(std::uint8_t const state) noexcept { rxReportError_(0x50U); // error for all bad frames switch (state) { case WAIT4_EVT_FRAME: { - Q_ASSERT_ID(910, l_rx.var.evt.e != nullptr); - QF::gc(l_rx.var.evt.e); // don't leak an allocated event + QS_CRIT_STAT + QS_CRIT_ENTRY(); + Q_ASSERT_INCRIT(910, QP::QS::rxPriv_.var.evt.e != nullptr); + QS_CRIT_EXIT(); +#if (QF_MAX_EPOOL > 0U) + QP::QF::gc(QP::QS::rxPriv_.var.evt.e); // don't leak allocated evt +#endif break; } default: { + // intentionally empty break; } } } -//**************************************************************************** -static void rxReportAck_(enum QSpyRxRecords const recId) noexcept { - QS_CRIT_STAT_ - QS_CRIT_E_(); - QS::beginRec_(static_cast(QS_RX_STATUS)); +//............................................................................ +static void rxReportAck_(enum QP::QSpyRxRecords const recId) noexcept { + QS_CRIT_STAT + QS_CRIT_ENTRY(); + QS_MEM_SYS(); + QP::QS::beginRec_(static_cast(QP::QS_RX_STATUS)); QS_U8_PRE_(recId); // record ID - QS::endRec_(); - QS_CRIT_X_(); - + QP::QS::endRec_(); + QS_MEM_APP(); + QS_CRIT_EXIT(); QS_REC_DONE(); // user callback (if defined) } -//**************************************************************************** +//............................................................................ static void rxReportError_(std::uint8_t const code) noexcept { - QS_CRIT_STAT_ - QS_CRIT_E_(); - QS::beginRec_(static_cast(QS_RX_STATUS)); + QS_CRIT_STAT + QS_CRIT_ENTRY(); + QS_MEM_SYS(); + QP::QS::beginRec_(static_cast(QP::QS_RX_STATUS)); QS_U8_PRE_(0x80U | code); // error code - QS::endRec_(); - QS_CRIT_X_(); - + QP::QS::endRec_(); + QS_MEM_APP(); + QS_CRIT_EXIT(); QS_REC_DONE(); // user callback (if defined) } -//**************************************************************************** -static void rxReportDone_(enum QSpyRxRecords const recId) noexcept { - QS_CRIT_STAT_ - QS_CRIT_E_(); - QS::beginRec_(static_cast(QS_TARGET_DONE)); +//............................................................................ +static void rxReportDone_(enum QP::QSpyRxRecords const recId) noexcept { + QS_CRIT_STAT + QS_CRIT_ENTRY(); + QS_MEM_SYS(); + QP::QS::beginRec_(static_cast(QP::QS_TARGET_DONE)); QS_TIME_PRE_(); // timestamp QS_U8_PRE_(recId); // record ID - QS::endRec_(); - QS_CRIT_X_(); - + QP::QS::endRec_(); + QS_MEM_APP(); + QS_CRIT_EXIT(); QS_REC_DONE(); // user callback (if defined) } -//**************************************************************************** -static void rxPoke_(void) noexcept { - std::uint8_t * const ptr = - (static_cast(QS::rxPriv_.currObj[QS::AP_OBJ]) - + l_rx.var.poke.offs); - switch (l_rx.var.poke.size) { +//............................................................................ +static void queryCurrObj_(std::uint8_t obj_kind) noexcept { + QS_CRIT_STAT + QS_CRIT_ENTRY(); + Q_REQUIRE_INCRIT(800, obj_kind < Q_DIM(QP::QS::rxPriv_.currObj)); + QS_CRIT_EXIT(); + + if (QP::QS::rxPriv_.currObj[obj_kind] != nullptr) { + QS_CRIT_ENTRY(); + QS_MEM_SYS(); + QP::QS::beginRec_(static_cast(QP::QS_QUERY_DATA)); + QS_TIME_PRE_(); // timestamp + QS_U8_PRE_(obj_kind); // object kind + QS_OBJ_PRE_(QP::QS::rxPriv_.currObj[obj_kind]); // object pointer + switch (obj_kind) { + case QP::QS::SM_OBJ: // intentionally fall through + case QP::QS::AO_OBJ: + QS_FUN_PRE_(reinterpret_cast( + QP::QS::rxPriv_.currObj[obj_kind])->getStateHandler()); + break; + case QP::QS::MP_OBJ: + QS_MPC_PRE_(reinterpret_cast( + QP::QS::rxPriv_.currObj[obj_kind])->getNFree()); + QS_MPC_PRE_(reinterpret_cast( + QP::QS::rxPriv_.currObj[obj_kind])->getNMin()); + break; + case QP::QS::EQ_OBJ: + QS_EQC_PRE_(reinterpret_cast( + QP::QS::rxPriv_.currObj[obj_kind])->getNFree()); + QS_EQC_PRE_(reinterpret_cast( + QP::QS::rxPriv_.currObj[obj_kind])->getNMin()); + break; + case QP::QS::TE_OBJ: + QS_OBJ_PRE_(reinterpret_cast( + QP::QS::rxPriv_.currObj[obj_kind])->getAct()); + QS_TEC_PRE_(reinterpret_cast( + QP::QS::rxPriv_.currObj[obj_kind])->getCtr()); + QS_TEC_PRE_(reinterpret_cast( + QP::QS::rxPriv_.currObj[obj_kind])->getInterval()); + QS_SIG_PRE_(reinterpret_cast( + QP::QS::rxPriv_.currObj[obj_kind])->sig); + QS_U8_PRE_ (reinterpret_cast( + QP::QS::rxPriv_.currObj[obj_kind])->refCtr_); + break; + default: + // intentionally empty + break; + } + QP::QS::endRec_(); + QS_MEM_APP(); + QS_CRIT_EXIT(); + QS_REC_DONE(); // user callback (if defined) + } + else { + rxReportError_(static_cast(QP::QS_RX_AO_FILTER)); + } +} + +//............................................................................ +static void rxPoke_() noexcept { + std::uint8_t * ptr = + static_cast(QP::QS::rxPriv_.currObj[QP::QS::AP_OBJ]); + ptr = &ptr[QP::QS::rxPriv_.var.poke.offs]; + switch (QP::QS::rxPriv_.var.poke.size) { case 1: - *ptr = static_cast(l_rx.var.poke.data); + *ptr = static_cast(QP::QS::rxPriv_.var.poke.data); break; case 2: *reinterpret_cast(ptr) - = static_cast(l_rx.var.poke.data); + = static_cast(QP::QS::rxPriv_.var.poke.data); break; case 4: - *reinterpret_cast(ptr) = l_rx.var.poke.data; + *reinterpret_cast(ptr) = QP::QS::rxPriv_.var.poke.data; break; default: Q_ERROR_ID(900); break; } - l_rx.var.poke.data = 0U; - l_rx.var.poke.idx = 0U; - l_rx.var.poke.offs += static_cast(l_rx.var.poke.size); + QP::QS::rxPriv_.var.poke.data = 0U; + QP::QS::rxPriv_.var.poke.idx = 0U; + QP::QS::rxPriv_.var.poke.offs += static_cast(QP::QS::rxPriv_.var.poke.size); } -//============================================================================ -#ifdef Q_UTEST - -//**************************************************************************** -/// @description -/// This function obtains the Test-Probe for a given API. -/// -/// @param[in] api pointer to the API function that requests its Test-Probe -/// -/// @returns Test-Probe data that has been received for the given API -/// from the Host (running qutest). For any ginve API, the function returns -/// the Test-Probe data in the same order as it was received from the Host. -/// If there is no Test-Probe for a ginve API, or no more Test-Probes for -/// a given API, the function returns zero. -/// -std::uint32_t QS::getTestProbe_(void (* const api)(void)) noexcept { - std::uint32_t data = 0U; - for (std::uint8_t i = 0U; i < l_testData.tpNum; ++i) { - if (l_testData.tpBuf[i].addr == reinterpret_cast(api)) { - data = l_testData.tpBuf[i].data; - - QS_CRIT_STAT_ - QS_CRIT_E_(); - QS::beginRec_(static_cast(QS_TEST_PROBE_GET)); - QS_TIME_PRE_(); // timestamp - QS_FUN_PRE_(api); // the calling API - QS_U32_PRE_(data); // the Test-Probe data - QS::endRec_(); - QS_CRIT_X_(); - - QS_REC_DONE(); // user callback (if defined) - - --l_testData.tpNum; // one less Test-Probe - // move all remaining entries in the buffer up by one - for (std::uint8_t j = i; j < l_testData.tpNum; ++j) { - l_testData.tpBuf[j] = l_testData.tpBuf[j + 1U]; - } - break; // we are done (Test-Probe retreived) - } - } - return data; -} - -//**************************************************************************** -QSTimeCtr QS::onGetTime(void) { - return (++l_testData.testTime); -} - -#endif // Q_UTEST - -} // namespace QP +} // unnamed namespace +//! @endcond diff --git a/libraries/qpcpp_arm-cm/src/qstamp.cpp b/libraries/qpcpp_arm-cm/src/qstamp.cpp deleted file mode 100644 index 05d86d1..0000000 --- a/libraries/qpcpp_arm-cm/src/qstamp.cpp +++ /dev/null @@ -1,21 +0,0 @@ -/// @file -/// @brief Application build time-stamp -/// @note -/// This module needs to be re-compiled in every new software build. To achive -/// this, it is recommended to delete the object file (qstamp.o or qstamp.obj) -/// in the build directory before each build. (Most development tools allow -/// you to specify a pre-build action, which is the ideal place to delete -/// the qstamp object file.) - -#include "qstamp.hpp" - -namespace QP { - -//! the calendar date of the last translation of the form: "Mmm dd yyyy" -char const BUILD_DATE[12] = __DATE__; - -//! the time of the last translation of the form: "hh:mm:ss" -char const BUILD_TIME[9] = __TIME__; - -} // namespace QP - diff --git a/libraries/qpcpp_arm-cm/src/qstamp.hpp b/libraries/qpcpp_arm-cm/src/qstamp.hpp index bb2b599..82dadd7 100644 --- a/libraries/qpcpp_arm-cm/src/qstamp.hpp +++ b/libraries/qpcpp_arm-cm/src/qstamp.hpp @@ -1,15 +1,51 @@ -/// @file -/// @brief Application build time-stamp interface - -#ifndef QSTAMP_HPP -#define QSTAMP_HPP +//$file${include::qstamp.hpp} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv +// +// Model: qpcpp.qm +// File: ${include::qstamp.hpp} +// +// This code has been generated by QM 6.1.1 . +// DO NOT EDIT THIS FILE MANUALLY. All your changes will be lost. +// +// This code is covered by the following QP license: +// License # : LicenseRef-QL-dual +// Issued to : Any user of the QP/C++ real-time embedded framework +// Framework(s) : qpcpp +// Support ends : 2024-12-31 +// License scope: +// +// Copyright (C) 2005 Quantum Leaps, LLC . +// +// Q u a n t u m L e a P s +// ------------------------ +// Modern Embedded Software +// +// SPDX-License-Identifier: GPL-3.0-or-later OR LicenseRef-QL-commercial +// +// This software is dual-licensed under the terms of the open source GNU +// General Public License version 3 (or any later version), or alternatively, +// under the terms of one of the closed source Quantum Leaps commercial +// licenses. +// +// The terms of the open source GNU General Public License version 3 +// can be found at: +// +// The terms of the closed source Quantum Leaps commercial licenses +// can be found at: +// +// Redistributions in source code must retain this top-level comment block. +// Plagiarizing this software to sidestep the license obligations is illegal. +// +// Contact information: +// +// +// +//$endhead${include::qstamp.hpp} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +#ifndef QSTAMP_HPP_ +#define QSTAMP_HPP_ namespace QP { - extern char const BUILD_DATE[12]; extern char const BUILD_TIME[9]; - } // namespace QP -#endif // QSTAMP - +#endif // QSTAMP_HPP_ diff --git a/libraries/qpcpp_arm-cm/src/qv.cpp b/libraries/qpcpp_arm-cm/src/qv.cpp index e539b24..5d2d4ff 100644 --- a/libraries/qpcpp_arm-cm/src/qv.cpp +++ b/libraries/qpcpp_arm-cm/src/qv.cpp @@ -1,46 +1,49 @@ -/// @file -/// @brief Cooperative QV kernel, definition of QP::QV_readySet_ and -/// implementation of kernel-specific functions. -/// @ingroup qv -/// @cond -///*************************************************************************** -/// Last updated for version 6.9.1 -/// Last updated on 2020-09-18 -/// -/// Q u a n t u m L e a P s -/// ------------------------ -/// Modern Embedded Software -/// -/// Copyright (C) 2005-2020 Quantum Leaps. All rights reserved. -/// -/// This program is open source software: you can redistribute it and/or -/// modify it under the terms of the GNU General Public License as published -/// by the Free Software Foundation, either version 3 of the License, or -/// (at your option) any later version. -/// -/// Alternatively, this program may be distributed and modified under the -/// terms of Quantum Leaps commercial licenses, which expressly supersede -/// the GNU General Public License and are specifically designed for -/// licensees interested in retaining the proprietary status of their code. -/// -/// This program is distributed in the hope that it will be useful, -/// but WITHOUT ANY WARRANTY; without even the implied warranty of -/// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -/// GNU General Public License for more details. -/// -/// You should have received a copy of the GNU General Public License -/// along with this program. If not, see . -/// -/// Contact information: -/// -/// -///*************************************************************************** -/// @endcond - +//$file${src::qv::qv.cpp} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv +// +// Model: qpcpp.qm +// File: ${src::qv::qv.cpp} +// +// This code has been generated by QM 6.1.1 . +// DO NOT EDIT THIS FILE MANUALLY. All your changes will be lost. +// +// This code is covered by the following QP license: +// License # : LicenseRef-QL-dual +// Issued to : Any user of the QP/C++ real-time embedded framework +// Framework(s) : qpcpp +// Support ends : 2024-12-31 +// License scope: +// +// Copyright (C) 2005 Quantum Leaps, LLC . +// +// Q u a n t u m L e a P s +// ------------------------ +// Modern Embedded Software +// +// SPDX-License-Identifier: GPL-3.0-or-later OR LicenseRef-QL-commercial +// +// This software is dual-licensed under the terms of the open source GNU +// General Public License version 3 (or any later version), or alternatively, +// under the terms of one of the closed source Quantum Leaps commercial +// licenses. +// +// The terms of the open source GNU General Public License version 3 +// can be found at: +// +// The terms of the closed source Quantum Leaps commercial licenses +// can be found at: +// +// Redistributions in source code must retain this top-level comment block. +// Plagiarizing this software to sidestep the license obligations is illegal. +// +// Contact information: +// +// +// +//$endhead${src::qv::qv.cpp} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ #define QP_IMPL // this is QP implementation -#include "qf_port.hpp" // QF port -#include "qf_pkg.hpp" // QF package-scope internal interface -#include "qassert.h" // QP embedded systems-friendly assertions +#include "qp_port.hpp" // QP port +#include "qp_pkg.hpp" // QP package-scope interface +#include "qsafe.h" // QP Functional Safety (FuSa) Subsystem #ifdef Q_SPY // QS software tracing enabled? #include "qs_port.hpp" // QS port #include "qs_pkg.hpp" // QS facilities for pre-defined trace records @@ -49,198 +52,263 @@ #endif // Q_SPY // protection against including this source file in a wrong project -#ifndef QV_HPP +#ifndef QV_HPP_ #error "Source file included in a project NOT based on the QV kernel" -#endif // QV_HPP - -namespace QP { +#endif // QV_HPP_ +// unnamed namespace for local definitions with internal linkage +namespace { Q_DEFINE_THIS_MODULE("qv") +} // unnamed namespace -/// @note The functions implemented in this module can have a different -/// implementation in other QF ports. The implementations included here -/// are appropriate for the cooperative QV kernel only. - -// Package-scope objects ***************************************************** -extern "C" { - QPSet QV_readySet_; // ready set of AOs -} // extern "C" - -//**************************************************************************** -/// @description -/// Initializes QF and must be called exactly once before any other QF -/// function. Typcially, QP::QF::init() is called from main() even before -/// initializing the Board Support Package (BSP). -/// -/// @note QP::QF::init() clears the internal QF variables, so that the -/// framework can start correctly even if the startup code fails to clear -/// the uninitialized data (as is required by the C++ Standard). -/// -void QF::init(void) { - QF_maxPool_ = 0U; - QF_subscrList_ = nullptr; - QF_maxPubSignal_ = 0; - - bzero(&QF::timeEvtHead_[0], sizeof(QF::timeEvtHead_)); - bzero(&active_[0], sizeof(active_)); - bzero(&QV_readySet_, sizeof(QV_readySet_)); - -#ifdef QV_INIT - QV_INIT(); // port-specific initialization of the QV kernel +//$skip${QP_VERSION} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv +// Check for the minimum required QP version +#if (QP_VERSION < 730U) || (QP_VERSION != ((QP_RELEASE^4294967295U) % 0x3E8U)) +#error qpcpp version 7.3.0 or higher required #endif +//$endskip${QP_VERSION} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +//$define${QV::QV-base} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv +namespace QP { +namespace QV { + +//${QV::QV-base::priv_} ...................................................... +QV::Attr priv_; + +//${QV::QV-base::schedDisable} ............................................... +void schedDisable(std::uint_fast8_t const ceiling) { + QF_CRIT_STAT + QF_CRIT_ENTRY(); + QF_MEM_SYS(); + + Q_ASSERT_INCRIT(102, priv_.schedCeil + == static_cast(~priv_.schedCeil_dis)); + + if (ceiling > priv_.schedCeil) { // raising the scheduler ceiling? + + QS_BEGIN_PRE_(QS_SCHED_LOCK, 0U) + QS_TIME_PRE_(); // timestamp + // the previous sched ceiling & new sched ceiling + QS_2U8_PRE_(static_cast(priv_.schedCeil), + static_cast(ceiling)); + QS_END_PRE_() + + priv_.schedCeil = ceiling; + #ifndef Q_UNSAFE + priv_.schedCeil_dis = static_cast(~ceiling); + #endif + } + QF_MEM_APP(); + QF_CRIT_EXIT(); } -//**************************************************************************** -/// @description -/// This function stops the QF application. After calling this function, -/// QF attempts to gracefully stop the application. This graceful shutdown -/// might take some time to complete. The typical use of this function is -/// for terminating the QF application to return back to the operating -/// system or for handling fatal errors that require shutting down -/// (and possibly re-setting) the system. -/// -/// @attention -/// After calling QF::stop() the application must terminate and cannot -/// continue. In particular, QF::stop() is **not** intended to be followed -/// by a call to QF::init() to "resurrect" the application. -/// -/// @sa QP::QF::onCleanup() -/// -void QF::stop(void) { - onCleanup(); // cleanup callback - // nothing else to do for the "vanilla" kernel +//${QV::QV-base::schedEnable} ................................................ +void schedEnable() { + QF_CRIT_STAT + QF_CRIT_ENTRY(); + QF_MEM_SYS(); + + Q_ASSERT_INCRIT(202, priv_.schedCeil + == static_cast(~priv_.schedCeil_dis)); + + if (priv_.schedCeil != 0U) { // actually enabling the scheduler? + + QS_BEGIN_PRE_(QS_SCHED_UNLOCK, 0U) + QS_TIME_PRE_(); // timestamp + // current sched ceiling (old), previous sched ceiling (new) + QS_2U8_PRE_(static_cast(priv_.schedCeil), 0U); + QS_END_PRE_() + + priv_.schedCeil = 0U; + #ifndef Q_UNSAFE + priv_.schedCeil_dis = ~static_cast(0U); + #endif + } + QF_MEM_APP(); + QF_CRIT_EXIT(); } -//**************************************************************************** -/// @description -/// QP::QF::run() is typically called from your startup code after you -/// initialize the QF and start at least one active object with -/// QP::QActive::start(). -/// -/// @returns QP::QF::run() typically does not return in embedded applications. -/// However, when QP runs on top of an operating system, QP::QF::run() might -/// return and in this case the return represents the error code (0 for -/// success). Typically the value returned from QP::QF::run() is subsequently -/// passed on as return from main(). -/// -/// @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 QP::QF::run(). -/// -int_t QF::run(void) { -#ifdef Q_SPY - std::uint_fast8_t pprev = 0U; // previous priority -#endif +} // namespace QV +} // namespace QP +//$enddef${QV::QV-base} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - onStartup(); // startup callback +//$define${QV::QF-cust} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv +namespace QP { +namespace QF { - // the combined event-loop and background-loop of the QV kernel... - QF_INT_DISABLE(); +//${QV::QF-cust::init} ....................................................... +void init() { + bzero_(&QF::priv_, sizeof(QF::priv_)); + bzero_(&QV::priv_, sizeof(QV::priv_)); + bzero_(&QActive::registry_[0], sizeof(QActive::registry_)); - // produce the QS_QF_RUN trace record - QS_BEGIN_NOCRIT_PRE_(QS_QF_RUN, 0U) - QS_END_NOCRIT_PRE_() + #ifndef Q_UNSAFE + QV::priv_.readySet.update_(&QV::priv_.readySet_dis); + QV::priv_.schedCeil_dis = ~static_cast(0U); + #endif - for (;;) { + #ifdef QV_INIT + QV_INIT(); // port-specific initialization of the QV kernel + #endif +} - // find the maximum priority AO ready to run - if (QV_readySet_.notEmpty()) { - std::uint_fast8_t const p = QV_readySet_.findMax(); - QActive * const a = active_[p]; +//${QV::QF-cust::stop} ....................................................... +void stop() { + onCleanup(); // cleanup callback + // nothing else to do for the QV kernel +} -#ifdef Q_SPY - QS_BEGIN_NOCRIT_PRE_(QS_SCHED_NEXT, a->m_prio) - QS_TIME_PRE_(); // timestamp - QS_2U8_PRE_(p, pprev);// scheduled prio & previous prio - QS_END_NOCRIT_PRE_() +//${QV::QF-cust::run} ........................................................ +int_t run() { + #ifdef Q_SPY + // produce the QS_QF_RUN trace record + QF_INT_DISABLE(); + QF_MEM_SYS(); + QS::beginRec_(QS_REC_NUM_(QS_QF_RUN)); + QS::endRec_(); + QF_MEM_APP(); + QF_INT_ENABLE(); + #endif // Q_SPY - pprev = p; // update previous priority -#endif // Q_SPY + onStartup(); // application-specific startup callback + QF_INT_DISABLE(); + QF_MEM_SYS(); + + #ifdef QV_START + QV_START(); // port-specific startup of the QV kernel + #endif + + #if (defined QF_ON_CONTEXT_SW) || (defined Q_SPY) + std::uint_fast8_t pprev = 0U; // previous prio. + #endif + + for (;;) { // QV event loop... + + // check internal integrity (duplicate inverse storage) + Q_ASSERT_INCRIT(302, + QV::priv_.readySet.verify_(&QV::priv_.readySet_dis)); + // check internal integrity (duplicate inverse storage) + Q_ASSERT_INCRIT(303, QV::priv_.schedCeil + == static_cast(~QV::priv_.schedCeil_dis)); + + // find the maximum prio. AO ready to run + std::uint_fast8_t const p = (QV::priv_.readySet.notEmpty() + ? QV::priv_.readySet.findMax() + : 0U); + + if (p > QV::priv_.schedCeil) { // is it above the sched ceiling? + QActive * const a = QActive::registry_[p]; + + #if (defined QF_ON_CONTEXT_SW) || (defined Q_SPY) + QS_BEGIN_PRE_(QS_SCHED_NEXT, p) + QS_TIME_PRE_(); // timestamp + QS_2U8_PRE_(static_cast(p), + static_cast(pprev)); + QS_END_PRE_() + + #ifdef QF_ON_CONTEXT_SW + QF_onContextSw(((pprev != 0U) + ? QActive::registry_[pprev] + : nullptr), a); + #endif // QF_ON_CONTEXT_SW + + pprev = p; // update previous prio. + #endif // (defined QF_ON_CONTEXT_SW) || (defined Q_SPY) + + QF_MEM_APP(); QF_INT_ENABLE(); - // perform the run-to-completion (RTC) step... - // 1. retrieve the event from the AO's event queue, which by this - // time must be non-empty and The "Vanialla" kernel asserts it. - // 2. dispatch the event to the AO's state machine. - // 3. determine if event is garbage and collect it if so - // QEvt const * const e = a->get_(); - a->dispatch(e, a->m_prio); - gc(e); + // NOTE QActive::get_() performs QF_MEM_APP() before return + // dispatch event (virtual call) + a->dispatch(e, a->getPrio()); + #if (QF_MAX_EPOOL > 0U) + gc(e); + #endif QF_INT_DISABLE(); + QF_MEM_SYS(); - if (a->m_eQueue.isEmpty()) { // empty queue? - QV_readySet_.rmove(p); + if (a->getEQueue().isEmpty()) { // empty queue? + QV::priv_.readySet.remove(p); + #ifndef Q_UNSAFE + QV::priv_.readySet.update_(&QV::priv_.readySet_dis); + #endif } } else { // no AO ready to run --> idle -#ifdef Q_SPY + #if (defined QF_ON_CONTEXT_SW) || (defined Q_SPY) if (pprev != 0U) { - QS_BEGIN_NOCRIT_PRE_(QS_SCHED_IDLE, 0U) + QS_BEGIN_PRE_(QS_SCHED_IDLE, pprev) QS_TIME_PRE_(); // timestamp - QS_U8_PRE_(pprev); // previous prio - QS_END_NOCRIT_PRE_() + QS_U8_PRE_(static_cast(pprev)); + QS_END_PRE_() + + #ifdef QF_ON_CONTEXT_SW + QF_onContextSw(QActive::registry_[pprev], nullptr); + #endif // QF_ON_CONTEXT_SW pprev = 0U; // update previous prio } -#endif // Q_SPY + #endif // (defined QF_ON_CONTEXT_SW) || (defined Q_SPY) + + QF_MEM_APP(); - // QV::onIdle() must be called with interrupts DISABLED because - // the determination of the idle condition (no events in the - // queues) can change at any time by an interrupt posting events - // to a queue. QV::onIdle() MUST enable interrupts internally, - // perhaps at the same time as putting the CPU into a power-saving - // mode. - QP::QV::onIdle(); + // QV::onIdle() must be called with interrupts DISABLED + // because the determination of the idle condition (all event + // queues empty) can change at any time by an interrupt posting + // events to a queue. + // + // NOTE: QV::onIdle() MUST enable interrupts internally, + // ideally at the same time as putting the CPU into a power- + // saving mode. + QV::onIdle(); QF_INT_DISABLE(); + QF_MEM_SYS(); } } -#ifdef __GNUC__ // GNU compiler? + #ifdef __GNUC__ // GNU compiler? return 0; -#endif + #endif } -//**************************************************************************** -/// @description -/// Starts execution of the AO and registers the AO with the framework. -/// -/// @param[in] prio priority at which to start the active object -/// @param[in] qSto pointer to the storage for the ring buffer of the -/// event queue (used only with the built-in QP::QEQueue) -/// @param[in] qLen length of the event queue (in events) -/// @param[in] stkSto pointer to the stack storage (must be nullptr in QV) -/// @param[in] stkSize stack size [bytes] -/// @param[in] par pointer to an extra parameter (might be nullptr) -/// -/// @usage -/// The following example shows starting an AO when a per-task stack is needed -/// @include qf_start.cpp -/// -void QActive::start(std::uint_fast8_t const prio, - QEvt const * * const qSto, std::uint_fast16_t const qLen, - void * const stkSto, std::uint_fast16_t const stkSize, - void const * const par) +} // namespace QF +} // namespace QP +//$enddef${QV::QF-cust} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +//$define${QV::QActive} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv +namespace QP { + +//${QV::QActive} ............................................................. + +//${QV::QActive::start} ...................................................... +void QActive::start( + QPrioSpec const prioSpec, + QEvt const * * const qSto, + std::uint_fast16_t const qLen, + void * const stkSto, + std::uint_fast16_t const stkSize, + void const * const par) { - static_cast(stkSize); // unused paramteter in the QV port + Q_UNUSED_PAR(stkSto); // not needed in QV + Q_UNUSED_PAR(stkSize); // not needed in QV - /// @pre the priority must be in range and the stack storage must not - /// be provided, because the QV kernel does not need per-AO stacks. - /// - Q_REQUIRE_ID(500, - (0U < prio) && (prio <= QF_MAX_ACTIVE) - && (stkSto == nullptr)); + QF_CRIT_STAT + QF_CRIT_ENTRY(); + Q_REQUIRE_INCRIT(300, stkSto == nullptr); + QF_CRIT_EXIT(); - m_eQueue.init(qSto, qLen); // initialize QEQueue of this AO - m_prio = static_cast(prio); // set the QF prio of this AO + m_prio = static_cast(prioSpec & 0xFFU); // QF-prio. + m_pthre = 0U; // not used + register_(); // make QF aware of this AO - QF::add_(this); // make QF aware of this AO + m_eQueue.init(qSto, qLen); // initialize QEQueue of this AO this->init(par, m_prio); // take the top-most initial tran. (virtual) QS_FLUSH(); // flush the trace buffer to the host } } // namespace QP - +//$enddef${QV::QActive} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/libraries/qpcpp_arm-cm/src/qv.hpp b/libraries/qpcpp_arm-cm/src/qv.hpp index c5a3482..ba6ac10 100644 --- a/libraries/qpcpp_arm-cm/src/qv.hpp +++ b/libraries/qpcpp_arm-cm/src/qv.hpp @@ -1,116 +1,136 @@ -/// @file -/// @brief QV/C++ platform-independent public interface. -/// @ingroup qv -/// @cond -///*************************************************************************** -/// Last updated for version 6.9.1 -/// Last updated on 2020-09-15 -/// -/// Q u a n t u m L e a P s -/// ------------------------ -/// Modern Embedded Software -/// -/// Copyright (C) 2005-2020 Quantum Leaps. All rights reserved. -/// -/// This program is open source software: you can redistribute it and/or -/// modify it under the terms of the GNU General Public License as published -/// by the Free Software Foundation, either version 3 of the License, or -/// (at your option) any later version. -/// -/// Alternatively, this program may be distributed and modified under the -/// terms of Quantum Leaps commercial licenses, which expressly supersede -/// the GNU General Public License and are specifically designed for -/// licensees interested in retaining the proprietary status of their code. -/// -/// This program is distributed in the hope that it will be useful, -/// but WITHOUT ANY WARRANTY; without even the implied warranty of -/// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -/// GNU General Public License for more details. -/// -/// You should have received a copy of the GNU General Public License -/// along with this program. If not, see . -/// -/// Contact information: -/// -/// -///*************************************************************************** -/// @endcond - -#ifndef QV_HPP -#define QV_HPP - -#include "qequeue.hpp" // QV kernel uses the native QF event queue -#include "qmpool.hpp" // QV kernel uses the native QF memory pool -#include "qpset.hpp" // QV kernel uses the native QF priority set - -//**************************************************************************** -// QF configuration for QK - -// QV event-queue used for AOs -#define QF_EQUEUE_TYPE QEQueue - - -//**************************************************************************** +//$file${include::qv.hpp} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv +// +// Model: qpcpp.qm +// File: ${include::qv.hpp} +// +// This code has been generated by QM 6.1.1 . +// DO NOT EDIT THIS FILE MANUALLY. All your changes will be lost. +// +// This code is covered by the following QP license: +// License # : LicenseRef-QL-dual +// Issued to : Any user of the QP/C++ real-time embedded framework +// Framework(s) : qpcpp +// Support ends : 2024-12-31 +// License scope: +// +// Copyright (C) 2005 Quantum Leaps, LLC . +// +// Q u a n t u m L e a P s +// ------------------------ +// Modern Embedded Software +// +// SPDX-License-Identifier: GPL-3.0-or-later OR LicenseRef-QL-commercial +// +// This software is dual-licensed under the terms of the open source GNU +// General Public License version 3 (or any later version), or alternatively, +// under the terms of one of the closed source Quantum Leaps commercial +// licenses. +// +// The terms of the open source GNU General Public License version 3 +// can be found at: +// +// The terms of the closed source Quantum Leaps commercial licenses +// can be found at: +// +// Redistributions in source code must retain this top-level comment block. +// Plagiarizing this software to sidestep the license obligations is illegal. +// +// Contact information: +// +// +// +//$endhead${include::qv.hpp} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +#ifndef QV_HPP_ +#define QV_HPP_ + +//$declare${QV::QV-base} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv namespace QP { +namespace QV { -//! QV services. -/// @description -/// This class groups together QV services. It has only static members and -/// should not be instantiated. -/// -// @note The QV ready set, etc. belong conceptually to the QV 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 QV { +//${QV::QV-base::Attr} ....................................................... +class Attr { public: + QPSet readySet; + std::uint_fast8_t schedCeil; - //! QV idle callback (customized in BSPs for QK) - /// @description - /// QV::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. QV::onIdle() MUST enable interrupts internally, - /// perhaps at the same time as putting the CPU into a power-saving - /// mode. - /// - /// @sa QP::QK::onIdle() - static void onIdle(void); -}; +#ifndef Q_UNSAFE + QPSet readySet_dis; +#endif // ndef Q_UNSAFE -} // namespace QP +#ifndef Q_UNSAFE + std::uint_fast8_t schedCeil_dis; +#endif // ndef Q_UNSAFE +}; // class Attr + +//${QV::QV-base::priv_} ...................................................... +extern QV::Attr priv_; + +//${QV::QV-base::schedDisable} ............................................... +void schedDisable(std::uint_fast8_t const ceiling); -//**************************************************************************** -extern "C" { - extern QP::QPSet QV_readySet_; //!< ready set of AOs -} // extern "C" +//${QV::QV-base::schedEnable} ................................................ +void schedEnable(); -//**************************************************************************** -// interface used only inside QF, but not in applications +//${QV::QV-base::onIdle} ..................................................... +void onIdle(); + +} // namespace QV +} // namespace QP +//$enddecl${QV::QV-base} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +//============================================================================ +// interface used only for internal implementation, but not in applications #ifdef QP_IMPL - // QV-specific scheduler locking (not needed in QV) - #define QF_SCHED_STAT_ - #define QF_SCHED_LOCK_(dummy) (static_cast(0)) - #define QF_SCHED_UNLOCK_() (static_cast(0)) - - // QV-specific native event queue operations... - #define QACTIVE_EQUEUE_WAIT_(me_) \ - Q_ASSERT_ID(110, (me_)->m_eQueue.m_frontEvt != nullptr) - #define QACTIVE_EQUEUE_SIGNAL_(me_) \ - (QV_readySet_.insert(static_cast((me_)->m_prio))) - - // QV-specific native QF event pool operations... - #define QF_EPOOL_TYPE_ QMPool - #define QF_EPOOL_INIT_(p_, poolSto_, poolSize_, evtSize_) \ - (p_).init((poolSto_), (poolSize_), (evtSize_)) - #define QF_EPOOL_EVENT_SIZE_(p_) ((p_).getBlockSize()) - #define QF_EPOOL_GET_(p_, e_, m_, qs_id_) \ - ((e_) = static_cast((p_).get((m_), (qs_id_)))) - #define QF_EPOOL_PUT_(p_, e_, qs_id_) ((p_).put((e_), (qs_id_))) +//$declare${QV-impl} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv -#endif // QP_IMPL +//${QV-impl::QF_SCHED_STAT_} ................................................. +#define QF_SCHED_STAT_ + +//${QV-impl::QF_SCHED_LOCK_} ................................................. +#define QF_SCHED_LOCK_(dummy) (static_cast(0)) + +//${QV-impl::QF_SCHED_UNLOCK_} ............................................... +#define QF_SCHED_UNLOCK_() (static_cast(0)) + +//${QV-impl::QACTIVE_EQUEUE_WAIT_} ........................................... +#define QACTIVE_EQUEUE_WAIT_(me_) \ + Q_ASSERT_INCRIT(310, (me_)->m_eQueue.m_frontEvt != nullptr) -#endif // QV_HPP +//${QV-impl::QACTIVE_EQUEUE_SIGNAL_} ......................................... +#ifndef Q_UNSAFE +#define QACTIVE_EQUEUE_SIGNAL_(me_) \ + QV::priv_.readySet.insert((me_)->m_prio); \ + QV::priv_.readySet.update_(&QV::priv_.readySet_dis) +#endif // ndef Q_UNSAFE + +//${QV-impl::QACTIVE_EQUEUE_SIGNAL_} ......................................... +#ifdef Q_UNSAFE +#define QACTIVE_EQUEUE_SIGNAL_(me_) \ + (QV::priv_.readySet.insert((me_)->m_prio)) +#endif // def Q_UNSAFE +//$enddecl${QV-impl} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +//$declare${QF_EPOOL-impl} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv + +//${QF_EPOOL-impl::QF_EPOOL_TYPE_} ........................................... +#define QF_EPOOL_TYPE_ QMPool + +//${QF_EPOOL-impl::QF_EPOOL_INIT_} ........................................... +#define QF_EPOOL_INIT_(p_, poolSto_, poolSize_, evtSize_) \ + (p_).init((poolSto_), (poolSize_), (evtSize_)) + +//${QF_EPOOL-impl::QF_EPOOL_EVENT_SIZE_} ..................................... +#define QF_EPOOL_EVENT_SIZE_(p_) ((p_).getBlockSize()) + +//${QF_EPOOL-impl::QF_EPOOL_GET_} ............................................ +#define QF_EPOOL_GET_(p_, e_, m_, qsId_) \ + ((e_) = static_cast((p_).get((m_), (qsId_)))) + +//${QF_EPOOL-impl::QF_EPOOL_PUT_} ............................................ +#define QF_EPOOL_PUT_(p_, e_, qsId_) ((p_).put((e_), (qsId_))) +//$enddecl${QF_EPOOL-impl} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +#endif // QP_IMPL +#endif // QV_HPP_ diff --git a/libraries/qpcpp_arm-cm/src/qv_port.cpp b/libraries/qpcpp_arm-cm/src/qv_port.cpp index 9c2ffee..9c02be0 100644 --- a/libraries/qpcpp_arm-cm/src/qv_port.cpp +++ b/libraries/qpcpp_arm-cm/src/qv_port.cpp @@ -1,59 +1,249 @@ -/** -* @file -* @brief QV/C++ port to ARM Cortex-M, GNU-ARM toolset -* @cond -****************************************************************************** -* Last updated for version 6.9.1 -* Last updated on 2020-09-23 -* -* Q u a n t u m L e a P s -* ------------------------ -* Modern Embedded Software -* -* Copyright (C) 2005-2020 Quantum Leaps, LLC. All rights reserved. -* -* This program is open source software: you can redistribute it and/or -* modify it under the terms of the GNU General Public License as published -* by the Free Software Foundation, either version 3 of the License, or -* (at your option) any later version. -* -* Alternatively, this program may be distributed and modified under the -* terms of Quantum Leaps commercial licenses, which expressly supersede -* the GNU General Public License and are specifically designed for -* licensees interested in retaining the proprietary status of their code. -* -* This program is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU General Public License for more details. -* -* You should have received a copy of the GNU General Public License -* along with this program. If not, see . -* -* Contact information: -* -* -****************************************************************************** -* @endcond -*/ -/* This QV port is part of the interanl QP implementation */ +//============================================================================ +// QP/C Real-Time Embedded Framework (RTEF) +// +// Q u a n t u m L e a P s +// ------------------------ +// Modern Embedded Software +// +// Copyright (C) 2005 Quantum Leaps, LLC. All rights reserved. +// +// SPDX-License-Identifier: GPL-3.0-or-later OR LicenseRef-QL-commercial +// +// This software is dual-licensed under the terms of the open source GNU +// General Public License version 3 (or any later version), or alternatively, +// under the terms of one of the closed source Quantum Leaps commercial +// licenses. +// +// The terms of the open source GNU General Public License version 3 +// can be found at: +// +// The terms of the closed source Quantum Leaps commercial licenses +// can be found at: +// +// Redistributions in source code must retain this top-level comment block. +// Plagiarizing this software to sidestep the license obligations is illegal. +// +// Contact information: +// +// +//============================================================================ +//! @date Last updated on: 2023-12-03 +//! @version Last updated for: @ref qpcpp_7_3_1 +//! +//! @file +//! @brief QV/C++ port to ARM Cortex-M, GNU-ARM + #define QP_IMPL 1U -#include "qf_port.hpp" +#include "qp_port.hpp" +#include "qsafe.h" // QP Functional Safety (FuSa) Subsystem extern "C" { -#if (__ARM_ARCH == 6) /* Cortex-M0/M0+/M1 (v6-M, v6S-M)? */ +#define SCB_SYSPRI ((uint32_t volatile *)0xE000ED18U) +#define NVIC_IP ((uint32_t volatile *)0xE000E400U) +#define SCB_CPACR *((uint32_t volatile *)0xE000ED88U) +#define FPU_FPCCR *((uint32_t volatile *)0xE000EF34U) + +// helper macros to "stringify" values +#define VAL(x) #x +#define STRINGIFY(x) VAL(x) + +//============================================================================ +// interrupts and critical section... +// +// NOTE: +// The following interrupt disabling/enabling as well as critical section +// entry/exit functions are defined as "weak" so that they can be +// re-implemented differently at the application level. +// +// NOTE: +// For best performance, these functions are implemented in assembly, +// but they can be implemented in C as well. + +//std::int32_t volatile QF_int_lock_nest_; // not used +extern char const QF_port_module_[]; +char const QF_port_module_[] = "qv_port"; + +//............................................................................ +// Unconditionally disable interrupts. +// +// description: +// On ARMv6-M, interrupts are disabled with the PRIMASK register. +// On ARMv7-M and higher, interrupts are disabled *selectively* with the +// BASEPRI register. +// Additionally, the function also asserts that the interrupts are +// NOT disabled upon the entry to the function. +__attribute__ ((naked, weak)) void QF_int_disable_(void) { +__asm volatile ( +#if (__ARM_ARCH == 6) //--------- ARMv6-M architecture? + " MRS r0,PRIMASK \n" // r0 <- previous PRIMASK + " CPSID i \n" // set PRIMASK +#else //--------- ARMv7-M or higher + " MRS r0,BASEPRI \n" // r0 <- previous BASEPRI + " MOVS r1,#" STRINGIFY(QF_BASEPRI) "\n" + " MSR BASEPRI,r1 \n" // NOTE: Cortes-M7 erratum 837070 is OK +#endif //--------- ARMv7-M or higher + " CMP r0,#0 \n" // assert(PRIMASK/BASEPRI == 0) + " BNE QF_int_disable_error\n" + " BX lr \n" + "QF_int_disable_error: \n" + " LDR r0,=QF_port_module_ \n" + " MOVS r1,#100 \n" + " LDR r2,=Q_onError \n" + " BX r2 \n" + ); +} +//............................................................................ +// Unconditionally enable interrupts. +// +// description: +// On ARMv6-M, interrupts are enabled with the PRIMASK register. +// On ARMv7-M and higher, interrupts are enabled with the BASEPRI register. +// Additionally, the function also asserts that the interrupts ARE +// disabled upon the entry to the function. +__attribute__ ((naked, weak)) void QF_int_enable_(void) { +__asm volatile ( +#if (__ARM_ARCH == 6) //--------- ARMv6-M architecture? + " MRS r0,PRIMASK \n" // r0 <- previous PRIMASK +#else //--------- ARMv7-M or higher + " MRS r0,BASEPRI \n" // r0 <- previous BASEPRI +#endif //--------- ARMv7-M or higher + " CMP r0,#0 \n" // assert(PRIMASK/BASEPRI != 0) + " BEQ QF_int_enable_error\n" +#if (__ARM_ARCH == 6) //--------- ARMv6-M architecture? + " CPSIE i \n" // clear PRIMASK +#else //--------- ARMv7-M or higher + " MOVS r1,#0 \n" + " MSR BASEPRI,r1 \n" // NOTE: Cortes-M7 erratum 837070 is OK +#endif //--------- ARMv7-M or higher + " BX lr \n" + "QF_int_enable_error: \n" + " LDR r0,=QF_port_module_ \n" + " MOVS r1,#101 \n" + " LDR r2,=Q_onError \n" + " BX r2 \n" + ); +} +//............................................................................ +// Enter QF critical section. +// +// description: +// On ARMv6-M, critical section is entered by disabling interrupts +// with the PRIMASK register. +// On ARMv7-M and higher, critical section is entered by disabling +// interrupts *selectively* with the BASEPRI register. +// Additionally, the function also asserts that the interrupts are +// NOT disabled upon the entry to the function. +// +// NOTE: +// The assertion means that this critical section CANNOT nest. +__attribute__ ((naked, weak)) void QF_crit_entry_(void) { +__asm volatile ( +#if (__ARM_ARCH == 6) //--------- ARMv6-M architecture? + " MRS r0,PRIMASK \n" // r0 <- previous PRIMASK + " CPSID i \n" // set PRIMASK +#else //--------- ARMv7-M or higher + " MRS r0,BASEPRI \n" // r0 <- previous BASEPRI + " MOVS r1,#" STRINGIFY(QF_BASEPRI) "\n" + " MSR BASEPRI,r1 \n" // NOTE: Cortes-M7 erratum 837070 is OK +#endif //--------- ARMv7-M or higher + " CMP r0,#0 \n" // assert(PRIMASK/BASEPRI == 0) + " BNE QF_crit_entry_error\n" + " BX lr \n" + "QF_crit_entry_error: \n" + " LDR r0,=QF_port_module_ \n" + " MOVS r1,#110 \n" + " LDR r2,=Q_onError \n" + " BX r2 \n" + ); +} +//............................................................................ +// Exit QF critical section. +// +// description: +// On ARMv6-M, critical section is exited by enabling interrupts +// with the PRIMASK register. +// On ARMv7-M and higher, critical section is exited by enabling +// interrupts with the BASEPRI register. +// Additionally, the function also asserts that the interrupts ARE +// disabled upon the entry to the function. +// +// NOTE: +// The assertion means that this critical section CANNOT nest. +__attribute__ ((naked, weak)) void QF_crit_exit_(void) { +__asm volatile ( +#if (__ARM_ARCH == 6) //--------- ARMv6-M architecture? + " MRS r0,PRIMASK \n" // r0 <- previous PRIMASK +#else //--------- ARMv7-M or higher + " MRS r0,BASEPRI \n" // r0 <- previous BASEPRI +#endif //--------- ARMv7-M or higher + " CMP r0,#0 \n" // assert(PRIMASK/BASEPRI != 0) + " BEQ QF_crit_exit_error\n" +#if (__ARM_ARCH == 6) //--------- ARMv6-M architecture? + " CPSIE i \n" // clear PRIMASK +#else //--------- ARMv7-M or higher + " MOVS r1,#0 \n" + " MSR BASEPRI,r1 \n" // NOTE: Cortes-M7 erratum 837070 is OK +#endif //--------- ARMv7-M or higher + " BX lr \n" + "QF_crit_exit_error: \n" + " LDR r0,=QF_port_module_ \n" + " MOVS r1,#111 \n" + " LDR r2,=Q_onError \n" + " BX r2 \n" + ); +} + +//============================================================================ +// Initialize the exception priorities and IRQ priorities to safe values. +// +// description: +// On ARMv7-M or higher, this QF port disables interrupts by means of the +// BASEPRI register. However, this method cannot disable interrupt +// priority zero, which is the default for all interrupts out of reset. +// The following code changes the SysTick priority and all IRQ priorities +// to the safe value QF_BASEPRI, which the QF critical section can disable. +// This avoids breaching of the QF critical sections in case the +// application programmer forgets to explicitly set priorities of all +// "kernel aware" interrupts. +// +// The interrupt priorities established in QV_init() can be later +// changed by the application-level code. +void QV_init(void) { + +#if (__ARM_ARCH != 6) //--------- if ARMv7-M and higher... + + // SCB_SYSPRI[2]: SysTick + SCB_SYSPRI[2] = (SCB_SYSPRI[2] | (QF_BASEPRI << 24U)); + + // set all 240 possible IRQ priories to QF_BASEPRI... + for (uint_fast8_t n = 0U; n < (240U/sizeof(uint32_t)); ++n) { + NVIC_IP[n] = (QF_BASEPRI << 24U) | (QF_BASEPRI << 16U) + | (QF_BASEPRI << 8U) | QF_BASEPRI; + } -/* -* Hand-optimized quick LOG2 in assembly (M0/M0+ have no CLZ instruction) -* -* NOTE: -* The inline GNU assembler does not accept mnemonics MOVS, LSRS and ADDS, -* but for Cortex-M0/M0+/M1 the mnemonics MOV, LSR and ADD always set the -* condition flags in the PSR. -*/ +#endif //--------- ARMv7-M or higher + +#ifdef __ARM_FP //--------- if VFP available... + // make sure that the FPU is enabled by setting CP10 & CP11 Full Access + SCB_CPACR = (SCB_CPACR | ((3UL << 20U) | (3UL << 22U))); + + // FPU automatic state preservation (ASPEN) lazy stacking (LSPEN) + FPU_FPCCR = (FPU_FPCCR | (1U << 30U) | (1U << 31U)); +#endif //--------- VFP available +} + +//============================================================================ +#if (__ARM_ARCH == 6) // if ARMv6-M... + +// hand-optimized quick LOG2 in assembly (no CLZ instruction in ARMv6-M) +// NOTE: +// The inline GNU assembler does not accept mnemonics MOVS, LSRS and ADDS, +// but for ARMv6-M the mnemonics MOV, LSR and ADD always set the condition +// flags in the PSR. __attribute__ ((naked, optimize("-fno-stack-protector"))) uint_fast8_t QF_qlog2(uint32_t x) { + Q_UNUSED_PAR(x); __asm volatile ( " MOV r1,#0 \n" #if (QF_MAX_ACTIVE > 16U) @@ -85,52 +275,7 @@ __asm volatile ( ); } -#else /* NOT Cortex-M0/M0+/M1(v6-M, v6S-M)? */ - -#define SCnSCB_ICTR ((uint32_t volatile *)0xE000E004) -#define SCB_SYSPRI ((uint32_t volatile *)0xE000ED14) -#define NVIC_IP ((uint32_t volatile *)0xE000E400) - -/* -* Initialize the exception priorities and IRQ priorities to safe values. -* -* Description: -* On Cortex-M3/M4/M7, this QV port disables interrupts by means of the -* BASEPRI register. However, this method cannot disable interrupt -* priority zero, which is the default for all interrupts out of reset. -* The following code changes the SysTick priority and all IRQ priorities -* to the safe value QF_BASEPRI, wich the QF critical section can disable. -* This avoids breaching of the QF critical sections in case the -* application programmer forgets to explicitly set priorities of all -* "kernel aware" interrupts. -* -* The interrupt priorities established in QV_init() can be later -* changed by the application-level code. -*/ -void QV_init(void) { - uint32_t n; - - /* set exception priorities to QF_BASEPRI... - * SCB_SYSPRI1: Usage-fault, Bus-fault, Memory-fault - */ - SCB_SYSPRI[1] |= (QF_BASEPRI << 16) | (QF_BASEPRI << 8) | QF_BASEPRI; - - /* SCB_SYSPRI2: SVCall */ - SCB_SYSPRI[2] |= (QF_BASEPRI << 24); - - /* SCB_SYSPRI3: SysTick, PendSV, Debug */ - SCB_SYSPRI[3] |= (QF_BASEPRI << 24) | (QF_BASEPRI << 16) | QF_BASEPRI; - - /* set all implemented IRQ priories to QF_BASEPRI... */ - n = 8U + ((*SCnSCB_ICTR & 0x7U) << 3); /* (# NVIC_PRIO registers)/4 */ - do { - --n; - NVIC_IP[n] = (QF_BASEPRI << 24) | (QF_BASEPRI << 16) - | (QF_BASEPRI << 8) | QF_BASEPRI; - } while (n != 0); -} - -#endif /* NOT Cortex-M0/M0+/M1(v6-M, v6S-M)? */ +#endif // ARMv6-M } // extern "C" diff --git a/libraries/qpcpp_arm-cm/src/qv_port.hpp b/libraries/qpcpp_arm-cm/src/qv_port.hpp deleted file mode 100644 index baa8a83..0000000 --- a/libraries/qpcpp_arm-cm/src/qv_port.hpp +++ /dev/null @@ -1,78 +0,0 @@ -/// @file -/// @brief QV/C++ port to ARM Cortex-M, GNU-ARM toolset -/// @cond -///*************************************************************************** -/// Last updated for version 6.9.1 -/// Last updated on 2020-09-23 -/// -/// Q u a n t u m L e a P s -/// ------------------------ -/// Modern Embedded Software -/// -/// Copyright (C) 2005-2020 Quantum Leaps. All rights reserved. -/// -/// This program is open source software: you can redistribute it and/or -/// modify it under the terms of the GNU General Public License as published -/// by the Free Software Foundation, either version 3 of the License, or -/// (at your option) any later version. -/// -/// Alternatively, this program may be distributed and modified under the -/// terms of Quantum Leaps commercial licenses, which expressly supersede -/// the GNU General Public License and are specifically designed for -/// licensees interested in retaining the proprietary status of their code. -/// -/// This program is distributed in the hope that it will be useful, -/// but WITHOUT ANY WARRANTY; without even the implied warranty of -/// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -/// GNU General Public License for more details. -/// -/// You should have received a copy of the GNU General Public License -/// along with this program. If not, see . -/// -/// Contact information: -/// -/// -///*************************************************************************** -/// @endcond - -#ifndef QV_PORT_HPP -#define QV_PORT_HPP - -#if (__ARM_ARCH == 6) // Cortex-M0/M0+/M1 (v6-M, v6S-M)? - - // macro to put the CPU to sleep inside QV::onIdle() - #define QV_CPU_SLEEP() do { \ - __asm volatile ("wfi"); \ - QF_INT_ENABLE(); \ - } while (false) - - #define QV_ARM_ERRATUM_838869() ((void)0) - -#else // Cortex-M3/M4/M7(v7-M) - - // macro to put the CPU to sleep inside QV::onIdle() - #define QV_CPU_SLEEP() do { \ - QF_PRIMASK_DISABLE(); \ - QF_INT_ENABLE(); \ - __asm volatile ("wfi"); \ - QF_PRIMASK_ENABLE(); \ - } while (false) - - // initialization of the QV kernel for Cortex-M3/M4/M4F - #define QV_INIT() QV_init() - extern "C" void QV_init(void); - - // The following macro implements the recommended workaround for the - // ARM Erratum 838869. Specifically, for Cortex-M3/M4/M7 the DSB - // (memory barrier) instruction needs to be added before exiting an ISR. - // This macro should be inserted at the end of ISRs. - // - #define QV_ARM_ERRATUM_838869() \ - __asm volatile ("dsb 0xf" ::: "memory") - -#endif - -#include "qv.hpp" // QV platform-independent public interface - -#endif // QV_PORT_HPP - diff --git a/qpcpp-file-list.txt b/qpcpp-file-list.txt index 4362ebc..14f4a54 100644 --- a/qpcpp-file-list.txt +++ b/qpcpp-file-list.txt @@ -1,17 +1,17 @@ -../qpcpp/include/qassert.h -../qpcpp/include/qep.hpp +#../qpcpp/include/qassert.h +#../qpcpp/include/qep.hpp ../qpcpp/src/qf/qep_hsm.cpp ../qpcpp/src/qf/qep_msm.cpp -../qpcpp/ports/arm-cm/qv/gnu/qep_port.hpp +#../qpcpp/ports/arm-cm/qv/gnu/qep_port.hpp ../qpcpp/include/qequeue.hpp -../qpcpp/include/qf.hpp +#../qpcpp/include/qf.hpp ../qpcpp/src/qf/qf_act.cpp ../qpcpp/src/qf/qf_actq.cpp ../qpcpp/src/qf/qf_defer.cpp ../qpcpp/src/qf/qf_dyn.cpp ../qpcpp/src/qf/qf_mem.cpp -../qpcpp/src/qf_pkg.hpp -../qpcpp/ports/arm-cm/qv/gnu/qf_port.hpp +#../qpcpp/src/qf_pkg.hpp +#../qpcpp/ports/arm-cm/qv/gnu/qf_port.hpp ../qpcpp/src/qf/qf_ps.cpp ../qpcpp/src/qf/qf_qact.cpp ../qpcpp/src/qf/qf_qeq.cpp @@ -19,17 +19,17 @@ ../qpcpp/src/qf/qf_time.cpp ../qpcpp/include/qmpool.hpp ../qpcpp/include/qpcpp.hpp -../qpcpp/include/qpset.hpp +#../qpcpp/include/qpset.hpp ../qpcpp/src/qs/qs.cpp ../qpcpp/include/qs.hpp ../qpcpp/include/qs_dummy.hpp ../qpcpp/src/qs/qs_fp.cpp -../qpcpp/src/qs_pkg.hpp +#../qpcpp/src/qs_pkg.hpp ../qpcpp/ports/arm-cm/qv/gnu/qs_port.hpp ../qpcpp/src/qs/qs_rx.cpp -../qpcpp/include/qstamp.cpp +#../qpcpp/include/qstamp.cpp ../qpcpp/include/qstamp.hpp ../qpcpp/src/qv/qv.cpp ../qpcpp/include/qv.hpp ../qpcpp/ports/arm-cm/qv/gnu/qv_port.cpp -../qpcpp/ports/arm-cm/qv/gnu/qv_port.hpp +#../qpcpp/ports/arm-cm/qv/gnu/qv_port.hpp diff --git a/version-6.9.4 b/version-6.9.4 deleted file mode 100644 index 97afd86..0000000 --- a/version-6.9.4 +++ /dev/null @@ -1,7 +0,0 @@ -QP-Arduino 6.9.4 - -Release date: 2022-01-14 -QP/C++ 6.9.4 -QP-nano 6.9.0 -QM 5.1.3 - diff --git a/version-7.3.4 b/version-7.3.4 new file mode 100644 index 0000000..1f4da76 --- /dev/null +++ b/version-7.3.4 @@ -0,0 +1,5 @@ +QP-Arduino 7.3.4 + +Release date: 2024-07-30 +QP/C++ 7.3.4 +