diff --git a/README.MD b/README.MD index 8c9e25f..d258790 100644 --- a/README.MD +++ b/README.MD @@ -15,23 +15,4 @@ Open any of the examples and upload to an ESP32. - Typically `QF::run()` should be executed as a last instruction before QP takes charge of executing the framework. As such, it should not return and instead give control to the scheduler of the host. However, the ESP32 framework (esp-idf) already runs the scheduler [before](https://github.com/espressif/arduino-esp32/blob/8ee5f0a11e5423a018e0f89146e05074466274db/cores/esp32/main.cpp#L55) `void setup()` and `void loop()`. This means that instead of running `QF::run()` in a loop, it suffices to execute it once as the last instruction in `void setup()`. -## QPSY - -[QSPY](https://www.state-machine.com/qtools/qspy.html) can be activated by defining `QS_ON`in `bsp.cpp` of both examples - -Example of how to communicate with esp32 target with qspy enabled. - -```bash -qspy -c YOUR_SERIAL_PORT -b 115200 -``` - -## QView - -DPP example can be tested with [QView™](https://www.state-machine.com/qtools/qview.html) example located in `examples/dpp_bsp-esp32/qview`. There are two bat scripts (Windows) which can run the QView scripts. - - -Note: QSpy must be running before using QView. - - - - +- No more QSpy on this port \ No newline at end of file diff --git a/examples/blinky_bsp-esp32/blinky.hpp b/examples/blinky_bsp-esp32/blinky.hpp index c81c1ed..aefffd1 100644 --- a/examples/blinky_bsp-esp32/blinky.hpp +++ b/examples/blinky_bsp-esp32/blinky.hpp @@ -1,21 +1,40 @@ -//.$file${.::blinky.hpp} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv +//$file${.::blinky.hpp} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv // // Model: blinky_bsp-esp32.qm // File: ${.::blinky.hpp} // -// This code has been generated by QM 5.1.1 . +// This code has been generated by QM 7.0.1 . // 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. +// Copyright (c) 2005 Quantum Leaps, LLC. All rights reserved. // -// 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. +// ____________________________________ +// / / +// / GGGGGGG PPPPPPPP LL / +// / GG GG PP PP LL / +// / GG PP PP LL / +// / GG GGGGG PPPPPPPP LL / +// / GG GG PP LL / +// / GGGGGGG PP LLLLLLL / +// /___________________________________/ // -//.$endhead${.::blinky.hpp} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +// SPDX-License-Identifier: GPL-3.0-or-later +// +// This generated code is open-source software licensed under the GNU +// General Public License (GPL) as published by the Free Software Foundation +// (see ). +// +// NOTE: +// The GPL does NOT permit the incorporation of this code into proprietary +// programs. Please contact Quantum Leaps for commercial licensing options, +// which expressly supersede the GPL and are designed explicitly for +// closed-source distribution. +// +// Quantum Leaps contact information: +// +// +// +//$endhead${.::blinky.hpp} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ #ifndef BLINKY_HPP #define BLINKY_HPP @@ -25,9 +44,12 @@ enum BlinkySignals { }; // genearate declarations of all opaque AO pointers -//.$declare${AOs::AO_Blinky} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv +//$declare${AOs::AO_Blinky} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv + +//${AOs::AO_Blinky} .......................................................... extern QP::QActive * const AO_Blinky; -//.$enddecl${AOs::AO_Blinky} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +//$enddecl${AOs::AO_Blinky} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //... #endif // BLINKY_HPP + diff --git a/examples/blinky_bsp-esp32/blinky_bsp-esp32.ino b/examples/blinky_bsp-esp32/blinky_bsp-esp32.ino index a2079a5..f1f9de2 100644 --- a/examples/blinky_bsp-esp32/blinky_bsp-esp32.ino +++ b/examples/blinky_bsp-esp32/blinky_bsp-esp32.ino @@ -1,41 +1,69 @@ -//.$file${.::blinky_bsp-esp32.ino} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv +//$file${.::blinky_bsp-esp32.ino} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv // // Model: blinky_bsp-esp32.qm // File: ${.::blinky_bsp-esp32.ino} // -// This code has been generated by QM 5.1.1 . +// This code has been generated by QM 7.0.1 . // 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. +// Copyright (c) 2005 Quantum Leaps, LLC. All rights reserved. // -// 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. +// ____________________________________ +// / / +// / GGGGGGG PPPPPPPP LL / +// / GG GG PP PP LL / +// / GG PP PP LL / +// / GG GGGGG PPPPPPPP LL / +// / GG GG PP LL / +// / GGGGGGG PP LLLLLLL / +// /___________________________________/ // -//.$endhead${.::blinky_bsp-esp32.ino} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +// SPDX-License-Identifier: GPL-3.0-or-later +// +// This generated code is open-source software licensed under the GNU +// General Public License (GPL) as published by the Free Software Foundation +// (see ). +// +// NOTE: +// The GPL does NOT permit the incorporation of this code into proprietary +// programs. Please contact Quantum Leaps for commercial licensing options, +// which expressly supersede the GPL and are designed explicitly for +// closed-source distribution. +// +// Quantum Leaps contact information: +// +// +// +//$endhead${.::blinky_bsp-esp32.ino} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ #include "qpcpp.hpp" // QP-C++ framework #include "blinky.hpp" // Blinky application interface #include "bsp.hpp" // Board Support Package (BSP) using namespace QP; -static constexpr unsigned stack_size = 1000; - +static constexpr unsigned stack_size = 1700; //............................................................................ void setup() { QF::init(); // initialize the framework BSP::init(); // initialize the BSP + // initialize event pools + static QF_MPOOL_EL(QP::QEvt) smlPoolSto[20]; + QP::QF::poolInit(smlPoolSto, sizeof(smlPoolSto), sizeof(smlPoolSto[0])); + + // initialize publish-subscribe + static QP::QSubscrList subscrSto[10]; + QP::QActive::psInit(subscrSto, Q_DIM(subscrSto)); + // statically allocate event queues for the AOs and start them... - static QEvt const *blinky_queueSto[30]; + static QP::QEvtPtr blinky_queueSto[30]; + static StackType_t stack[stack_size]; AO_Blinky->start(1U, // priority - blinky_queueSto, // queu storage for evets + blinky_queueSto, // queue storage for events Q_DIM(blinky_queueSto), //len of queue - nullptr, // No static stack - stack_size); - QF::run(); // Normally QF Run is located in a loop + stack, + sizeof(stack)); + + QF::run(); // Normally QF Run is located in a loop //but since using Arduino SDK not necessary // Called once to call QF::OnStartup and produce the QS trace } @@ -46,8 +74,9 @@ void loop() { //============================================================================ // generate declarations and definitions of all AO classes (state machines)... -//.$declare${AOs::Blinky} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv -//.${AOs::Blinky} ............................................................ +//$declare${AOs::Blinky} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv + +//${AOs::Blinky} ............................................................. class Blinky : public QP::QActive { private: QP::QTimeEvt m_timeEvt; @@ -62,26 +91,28 @@ protected: Q_STATE_DECL(initial); Q_STATE_DECL(off); Q_STATE_DECL(on); -}; -//.$enddecl${AOs::Blinky} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -//.$skip${QP_VERSION} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv -//. Check for the minimum required QP version -#if (QP_VERSION < 690U) || (QP_VERSION != ((QP_RELEASE^4294967295U) % 0x3E8U)) -#error qpcpp version 6.9.0 or higher required +}; // class Blinky +//$enddecl${AOs::Blinky} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +//$skip${QP_VERSION} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv +// Check for the minimum required QP version +#if (QP_VERSION < 730U) || (QP_VERSION != ((QP_RELEASE^4294967295U)%0x2710U)) +#error qpcpp version 7.3.0 or higher required #endif -//.$endskip${QP_VERSION} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -//.$define${AOs::Blinky} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv -//.${AOs::Blinky} ............................................................ +//$endskip${QP_VERSION} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +//$define${AOs::Blinky} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv + +//${AOs::Blinky} ............................................................. Blinky Blinky::instance; -//.${AOs::Blinky::Blinky} .................................................... + +//${AOs::Blinky::Blinky} ..................................................... Blinky::Blinky() : QActive(Q_STATE_CAST(&Blinky::initial)), m_timeEvt(this, TIMEOUT_SIG, 0U) {} -//.${AOs::Blinky::SM} ........................................................ +//${AOs::Blinky::SM} ......................................................... Q_STATE_DEF(Blinky, initial) { - //.${AOs::Blinky::SM::initial} + //${AOs::Blinky::SM::initial} (void)e; // unused parameter m_timeEvt.armX(BSP::TICKS_PER_SEC/2, BSP::TICKS_PER_SEC/2); @@ -93,17 +124,18 @@ Q_STATE_DEF(Blinky, initial) { return tran(&off); } -//.${AOs::Blinky::SM::off} ................................................... + +//${AOs::Blinky::SM::off} .................................................... Q_STATE_DEF(Blinky, off) { QP::QState status_; switch (e->sig) { - //.${AOs::Blinky::SM::off} + //${AOs::Blinky::SM::off} case Q_ENTRY_SIG: { BSP::ledOff(); status_ = Q_RET_HANDLED; break; } - //.${AOs::Blinky::SM::off::TIMEOUT} + //${AOs::Blinky::SM::off::TIMEOUT} case TIMEOUT_SIG: { status_ = tran(&on); break; @@ -115,17 +147,18 @@ Q_STATE_DEF(Blinky, off) { } return status_; } -//.${AOs::Blinky::SM::on} .................................................... + +//${AOs::Blinky::SM::on} ..................................................... Q_STATE_DEF(Blinky, on) { QP::QState status_; switch (e->sig) { - //.${AOs::Blinky::SM::on} + //${AOs::Blinky::SM::on} case Q_ENTRY_SIG: { BSP::ledOn(); status_ = Q_RET_HANDLED; break; } - //.${AOs::Blinky::SM::on::TIMEOUT} + //${AOs::Blinky::SM::on::TIMEOUT} case TIMEOUT_SIG: { status_ = tran(&off); break; @@ -137,13 +170,15 @@ Q_STATE_DEF(Blinky, on) { } return status_; } -//.$enddef${AOs::Blinky} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +//$enddef${AOs::Blinky} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //... //============================================================================ // generate definitions of all AO opaque pointers... -//.$define${AOs::AO_Blinky} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv -//.${AOs::AO_Blinky} ......................................................... +//$define${AOs::AO_Blinky} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv + +//${AOs::AO_Blinky} .......................................................... QP::QActive * const AO_Blinky = &Blinky::instance; -//.$enddef${AOs::AO_Blinky} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +//$enddef${AOs::AO_Blinky} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //... + diff --git a/examples/blinky_bsp-esp32/blinky_bsp-esp32.qm b/examples/blinky_bsp-esp32/blinky_bsp-esp32.qm index 79733bc..1138bf9 100644 --- a/examples/blinky_bsp-esp32/blinky_bsp-esp32.qm +++ b/examples/blinky_bsp-esp32/blinky_bsp-esp32.qm @@ -1,5 +1,5 @@ - + This is a little more advanced Blinky example for the Arduino ESP32 board. The example demonstrates: 1. One active object class "Blinky" (inside the package "AOs") @@ -47,7 +47,7 @@ QS_SIG_DICTIONARY(TIMEOUT_SIG, nullptr); - + @@ -58,7 +58,7 @@ QS_SIG_DICTIONARY(TIMEOUT_SIG, nullptr); - + @@ -74,13 +74,12 @@ QS_SIG_DICTIONARY(TIMEOUT_SIG, nullptr); #ifndef BSP_HPP #define BSP_HPP -class BSP { -public: +namespace BSP { enum { TICKS_PER_SEC = CONFIG_FREERTOS_HZ} ; - static void init(void); - static void ledOff(void); - static void ledOn(void); -}; + void init(void); + void ledOff(void); + void ledOn(void); +} #endif // BSP_HPP @@ -92,13 +91,11 @@ public: #include "Arduino.h" #include "esp_freertos_hooks.h" -using namespace QP; -static constexpr unsigned LED_BUILTIN = 13; -// QS facilities +// If current ESP32 board does not define LED_BUILTIN +#ifndef LED_BUILTIN +#define LED_BUILTIN 2U +#endif -// un-comment if QS instrumentation needed -//#define QS_ON -// BSP functions static void tickHook_ESP32(void); /*Tick hook for QP */ static uint8_t const l_TickHook = static_cast<uint8_t>(0); @@ -106,76 +103,16 @@ static void tickHook_ESP32(void) { BaseType_t xHigherPriorityTaskWoken = pdFALSE; /* process time events for rate 0 */ - QF::TICK_FROM_ISR(&xHigherPriorityTaskWoken, &l_TickHook); + QP::QTimeEvt::TICK_FROM_ISR(&xHigherPriorityTaskWoken, &l_TickHook); /* notify FreeRTOS to perform context switch from ISR, if needed */ if(xHigherPriorityTaskWoken) { portYIELD_FROM_ISR(); } } - -//............................................................................ -void BSP::init(void) { - // initialize the hardware used in this sketch... - // NOTE: interrupts are configured and started later in QF::onStartup() - pinMode(LED_BUILTIN, OUTPUT); - Serial.begin(115200); // run serial port at 115200 baud rate - QS_INIT(nullptr); -#ifdef QS_ON - // setup the QS filters... - QS_GLB_FILTER(QP::QS_SM_RECORDS); // state machine records - QS_GLB_FILTER(QP::QS_AO_RECORDS); // active object records - QS_GLB_FILTER(QP::QS_UA_RECORDS); // all user records -#endif -} //............................................................................ -void BSP::ledOff(void) { - digitalWrite(LED_BUILTIN, LOW); - Serial.println("led off"); -} -//............................................................................ -void BSP::ledOn(void) { - digitalWrite(LED_BUILTIN, HIGH); - Serial.println("led on"); -} - -//............................................................................ -void QSpy_Task(void *) { - while(1) - { - // transmit QS outgoing data (QS-TX) - uint16_t len = Serial.availableForWrite(); - if (len > 0U) { // any space available in the output buffer? - uint8_t const *buf = QS::getBlock(&len); - if (buf) { - Serial.write(buf, len); // asynchronous and non-blocking - } - } - - // receive QS incoming data (QS-RX) - len = Serial.available(); - if (len > 0U) { - do { - QP::QS::rxPut(Serial.read()); - } while (--len > 0U); - QS::rxParse(); - } - delay(100); - }; -} - -void QF::onStartup(void) { +void QP::QF::onStartup(void) { esp_register_freertos_tick_hook_for_cpu(tickHook_ESP32, QP_CPU_NUM); QS_OBJ_DICTIONARY(&l_TickHook); -#ifdef QS_ON - xTaskCreatePinnedToCore( - QSpy_Task, /* Function to implement the task */ - "QSPY", /* Name of the task */ - 10000, /* Stack size in words */ - NULL, /* Task input parameter */ - configMAX_PRIORITIES-1, /* Priority of the task */ - NULL, /* Task handle. */ - QP_CPU_NUM); /* Core where the task should run */ -#endif } //............................................................................ extern "C" Q_NORETURN Q_onAssert(char const * const module, int location) { @@ -184,59 +121,34 @@ extern "C" Q_NORETURN Q_onAssert(char const * const module, int locati // (void)module; (void)location; - Serial.print("QP Assert module:"); + Serial.print("QP Assert module: "); Serial.print(module); - Serial.print(","); + Serial.print("@ "); Serial.println(location); QF_INT_DISABLE(); // disable all interrupts for (;;) { // sit in an endless loop for now } } - -//---------------------------------------------------------------------------- -// QS callbacks... //............................................................................ -bool QP::QS::onStartup(void const * arg) { - static uint8_t qsTxBuf[1024]; // buffer for QS transmit channel (QS-TX) - static uint8_t qsRxBuf[128]; // buffer for QS receive channel (QS-RX) - initBuf (qsTxBuf, sizeof(qsTxBuf)); - rxInitBuf(qsRxBuf, sizeof(qsRxBuf)); - return true; // return success -} -//............................................................................ -void QP::QS::onCommand(uint8_t cmdId, uint32_t param1, - uint32_t param2, uint32_t param3) -{ - (void)cmdId; - (void)param1; - (void)param2; - (void)param3; -} -//............................................................................ -void QP::QS::onCleanup(void) { -} -//............................................................................ -QP::QSTimeCtr QP::QS::onGetTime(void) { - return millis(); +namespace BSP { +void init(void) { + // initialize the hardware used in this sketch... + // NOTE: interrupts are configured and started later in QF::onStartup() + pinMode(LED_BUILTIN, OUTPUT); + Serial.begin(115200); // run serial port at 115200 baud rate + QS_INIT(nullptr); } //............................................................................ -void QP::QS::onFlush(void) { -#ifdef QS_ON - uint16_t len = 0xFFFFU; // big number to get as many bytes as available - uint8_t const *buf = QS::getBlock(&len); // get continguous block of data - while (buf != nullptr) { // data available? - Serial.write(buf, len); // might poll until all bytes fit - len = 0xFFFFU; // big number to get as many bytes as available - buf = QS::getBlock(&len); // try to get more data - } - Serial.flush(); // wait for the transmission of outgoing data to complete -#endif // QS_ON +void ledOff(void) { + digitalWrite(LED_BUILTIN, LOW); + Serial.println("led off"); } //............................................................................ -void QP::QS::onReset(void) { - esp_restart(); +void ledOn(void) { + digitalWrite(LED_BUILTIN, HIGH); + Serial.println("led on"); } - +} // namespace BSP #ifndef BLINKY_HPP @@ -260,21 +172,30 @@ $declare${AOs::AO_Blinky} #include "bsp.hpp" // Board Support Package (BSP) using namespace QP; -static constexpr unsigned stack_size = 1000; - +static constexpr unsigned stack_size = 1700; //............................................................................ void setup() { QF::init(); // initialize the framework BSP::init(); // initialize the BSP + // initialize event pools + static QF_MPOOL_EL(QP::QEvt) smlPoolSto[20]; + QP::QF::poolInit(smlPoolSto, sizeof(smlPoolSto), sizeof(smlPoolSto[0])); + + // initialize publish-subscribe + static QP::QSubscrList subscrSto[10]; + QP::QActive::psInit(subscrSto, Q_DIM(subscrSto)); + // statically allocate event queues for the AOs and start them... - static QEvt const *blinky_queueSto[30]; + static QP::QEvtPtr blinky_queueSto[30]; + static StackType_t stack[stack_size]; AO_Blinky->start(1U, // priority - blinky_queueSto, // queu storage for evets + blinky_queueSto, // queue storage for events Q_DIM(blinky_queueSto), //len of queue - nullptr, // No static stack - stack_size); - QF::run(); // Normally QF Run is located in a loop + stack, + sizeof(stack)); + + QF::run(); // Normally QF Run is located in a loop //but since using Arduino SDK not necessary // Called once to call QF::OnStartup and produce the QS trace } diff --git a/examples/blinky_bsp-esp32/bsp.cpp b/examples/blinky_bsp-esp32/bsp.cpp index 67aa8de..667e12d 100644 --- a/examples/blinky_bsp-esp32/bsp.cpp +++ b/examples/blinky_bsp-esp32/bsp.cpp @@ -1,34 +1,51 @@ -//.$file${.::bsp.cpp} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv +//$file${.::bsp.cpp} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv // // Model: blinky_bsp-esp32.qm // File: ${.::bsp.cpp} // -// This code has been generated by QM 5.1.1 . +// This code has been generated by QM 7.0.1 . // 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. +// Copyright (c) 2005 Quantum Leaps, LLC. All rights reserved. // -// 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. +// ____________________________________ +// / / +// / GGGGGGG PPPPPPPP LL / +// / GG GG PP PP LL / +// / GG PP PP LL / +// / GG GGGGG PPPPPPPP LL / +// / GG GG PP LL / +// / GGGGGGG PP LLLLLLL / +// /___________________________________/ // -//.$endhead${.::bsp.cpp} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +// SPDX-License-Identifier: GPL-3.0-or-later +// +// This generated code is open-source software licensed under the GNU +// General Public License (GPL) as published by the Free Software Foundation +// (see ). +// +// NOTE: +// The GPL does NOT permit the incorporation of this code into proprietary +// programs. Please contact Quantum Leaps for commercial licensing options, +// which expressly supersede the GPL and are designed explicitly for +// closed-source distribution. +// +// Quantum Leaps contact information: +// +// +// +//$endhead${.::bsp.cpp} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ #include "qpcpp.hpp" // QP-C++ framework #include "blinky.hpp" // Blinky application interface #include "bsp.hpp" // Board Support Package (BSP) #include "Arduino.h" #include "esp_freertos_hooks.h" -using namespace QP; -static constexpr unsigned LED_BUILTIN = 13; -// QS facilities +// If current ESP32 board does not define LED_BUILTIN +#ifndef LED_BUILTIN +#define LED_BUILTIN 2U +#endif -// un-comment if QS instrumentation needed -//#define QS_ON -// BSP functions static void tickHook_ESP32(void); /*Tick hook for QP */ static uint8_t const l_TickHook = static_cast(0); @@ -36,76 +53,16 @@ static void tickHook_ESP32(void) { BaseType_t xHigherPriorityTaskWoken = pdFALSE; /* process time events for rate 0 */ - QTimeEvt::TICK_X_FROM_ISR(0U, &xHigherPriorityTaskWoken, &l_TickHook); + QP::QTimeEvt::TICK_FROM_ISR(&xHigherPriorityTaskWoken, &l_TickHook); /* notify FreeRTOS to perform context switch from ISR, if needed */ if(xHigherPriorityTaskWoken) { portYIELD_FROM_ISR(); } } - -//............................................................................ -void BSP::init(void) { - // initialize the hardware used in this sketch... - // NOTE: interrupts are configured and started later in QF::onStartup() - pinMode(LED_BUILTIN, OUTPUT); - Serial.begin(115200); // run serial port at 115200 baud rate - QS_INIT(nullptr); -#ifdef QS_ON - // setup the QS filters... - QS_GLB_FILTER(QP::QS_SM_RECORDS); // state machine records - QS_GLB_FILTER(QP::QS_AO_RECORDS); // active object records - QS_GLB_FILTER(QP::QS_UA_RECORDS); // all user records -#endif -} -//............................................................................ -void BSP::ledOff(void) { - digitalWrite(LED_BUILTIN, LOW); - Serial.println("led off"); -} -//............................................................................ -void BSP::ledOn(void) { - digitalWrite(LED_BUILTIN, HIGH); - Serial.println("led on"); -} - //............................................................................ -void QSpy_Task(void *) { - while(1) - { - // transmit QS outgoing data (QS-TX) - uint16_t len = Serial.availableForWrite(); - if (len > 0U) { // any space available in the output buffer? - uint8_t const *buf = QS::getBlock(&len); - if (buf) { - Serial.write(buf, len); // asynchronous and non-blocking - } - } - - // receive QS incoming data (QS-RX) - len = Serial.available(); - if (len > 0U) { - do { - QP::QS::rxPut(Serial.read()); - } while (--len > 0U); - QS::rxParse(); - } - delay(100); - }; -} - -void QF::onStartup(void) { +void QP::QF::onStartup(void) { esp_register_freertos_tick_hook_for_cpu(tickHook_ESP32, QP_CPU_NUM); QS_OBJ_DICTIONARY(&l_TickHook); -#ifdef QS_ON - xTaskCreatePinnedToCore( - QSpy_Task, /* Function to implement the task */ - "QSPY", /* Name of the task */ - 10000, /* Stack size in words */ - NULL, /* Task input parameter */ - configMAX_PRIORITIES-1, /* Priority of the task */ - NULL, /* Task handle. */ - QP_CPU_NUM); /* Core where the task should run */ -#endif } //............................................................................ extern "C" Q_NORETURN Q_onAssert(char const * const module, int location) { @@ -114,55 +71,31 @@ extern "C" Q_NORETURN Q_onAssert(char const * const module, int location) { // (void)module; (void)location; - Serial.print("QP Assert module:"); + Serial.print("QP Assert module: "); Serial.print(module); - Serial.print(","); + Serial.print("@ "); Serial.println(location); QF_INT_DISABLE(); // disable all interrupts for (;;) { // sit in an endless loop for now } } - -//---------------------------------------------------------------------------- -// QS callbacks... -//............................................................................ -bool QP::QS::onStartup(void const * arg) { - static uint8_t qsTxBuf[1024]; // buffer for QS transmit channel (QS-TX) - static uint8_t qsRxBuf[128]; // buffer for QS receive channel (QS-RX) - initBuf (qsTxBuf, sizeof(qsTxBuf)); - rxInitBuf(qsRxBuf, sizeof(qsRxBuf)); - return true; // return success -} -//............................................................................ -void QP::QS::onCommand(uint8_t cmdId, uint32_t param1, - uint32_t param2, uint32_t param3) -{ - (void)cmdId; - (void)param1; - (void)param2; - (void)param3; -} //............................................................................ -void QP::QS::onCleanup(void) { +namespace BSP { +void init(void) { + // initialize the hardware used in this sketch... + // NOTE: interrupts are configured and started later in QF::onStartup() + pinMode(LED_BUILTIN, OUTPUT); + Serial.begin(115200); // run serial port at 115200 baud rate + QS_INIT(nullptr); } //............................................................................ -QP::QSTimeCtr QP::QS::onGetTime(void) { - return millis(); +void ledOff(void) { + digitalWrite(LED_BUILTIN, LOW); + Serial.println("led off"); } //............................................................................ -void QP::QS::onFlush(void) { -#ifdef QS_ON - uint16_t len = 0xFFFFU; // big number to get as many bytes as available - uint8_t const *buf = QS::getBlock(&len); // get continguous block of data - while (buf != nullptr) { // data available? - Serial.write(buf, len); // might poll until all bytes fit - len = 0xFFFFU; // big number to get as many bytes as available - buf = QS::getBlock(&len); // try to get more data - } - Serial.flush(); // wait for the transmission of outgoing data to complete -#endif // QS_ON +void ledOn(void) { + digitalWrite(LED_BUILTIN, HIGH); + Serial.println("led on"); } -//............................................................................ -void QP::QS::onReset(void) { - esp_restart(); -} \ No newline at end of file +} // namespace BSP diff --git a/examples/blinky_bsp-esp32/bsp.hpp b/examples/blinky_bsp-esp32/bsp.hpp index c1e667a..140b77d 100644 --- a/examples/blinky_bsp-esp32/bsp.hpp +++ b/examples/blinky_bsp-esp32/bsp.hpp @@ -1,30 +1,49 @@ -//.$file${.::bsp.hpp} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv +//$file${.::bsp.hpp} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv // // Model: blinky_bsp-esp32.qm // File: ${.::bsp.hpp} // -// This code has been generated by QM 5.1.1 . +// This code has been generated by QM 7.0.1 . // 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. +// Copyright (c) 2005 Quantum Leaps, LLC. All rights reserved. // -// 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. +// ____________________________________ +// / / +// / GGGGGGG PPPPPPPP LL / +// / GG GG PP PP LL / +// / GG PP PP LL / +// / GG GGGGG PPPPPPPP LL / +// / GG GG PP LL / +// / GGGGGGG PP LLLLLLL / +// /___________________________________/ // -//.$endhead${.::bsp.hpp} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +// SPDX-License-Identifier: GPL-3.0-or-later +// +// This generated code is open-source software licensed under the GNU +// General Public License (GPL) as published by the Free Software Foundation +// (see ). +// +// NOTE: +// The GPL does NOT permit the incorporation of this code into proprietary +// programs. Please contact Quantum Leaps for commercial licensing options, +// which expressly supersede the GPL and are designed explicitly for +// closed-source distribution. +// +// Quantum Leaps contact information: +// +// +// +//$endhead${.::bsp.hpp} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ #ifndef BSP_HPP #define BSP_HPP -class BSP { -public: +namespace BSP { enum { TICKS_PER_SEC = CONFIG_FREERTOS_HZ} ; - static void init(void); - static void ledOff(void); - static void ledOn(void); -}; + void init(void); + void ledOff(void); + void ledOn(void); +} #endif // BSP_HPP + diff --git a/examples/blinky_bsp-esp32/qp_config.hpp b/examples/blinky_bsp-esp32/qp_config.hpp new file mode 100644 index 0000000..8891edf --- /dev/null +++ b/examples/blinky_bsp-esp32/qp_config.hpp @@ -0,0 +1,199 @@ +//============================================================================ +// QP configuration file (generic) +// Last updated for version: 7.3.0 +// Last updated on: 2023-10-30 +// +// 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: +// +// +//============================================================================ +#ifndef QP_CONFIG_HPP_ +#define QP_CONFIG_HPP_ + +//-------- <<< Use Configuration Wizard in Context Menu >>> ----------------- + +// NOTE: Requires command-line macro: QP_CONFIG +// This qp_config.h header file is activated only when the macro +// QP_CONFIG is defined on the command-line to the compiler +// ------------------------------------------- + +// QP API compatibility version (QP_API_VERSION) +// <0=> 0 (Maximum compatibility) +// <580=>580 (QP 5.8.0 or newer) +// <660=>660 (QP 6.6.0 or newer) +// <691=>691 (QP 6.9.1 or newer) +// <700=>700 (QP 7.0.0 or newer) +// <9999=>9999 (Latest only) +// QP API backwards compatibility with the QP/C API version. +// Lower QP_API_VERSION values enable backwards compatibility +// with lower (older) QP API versions. +// For example, QP_API_VERSION==691 will enable the compatibility +// layer with QP version 6.9.1 and newer, but not older than 6.9.1. +// QP_API_VERSION==0 enables the maximum currently supported +// backwards compatibility. Conversely, QP_API_VERSION==9999 means +// that no backwards compatibility layer should be enabled. +// Default: 0 (All supported) +#define QP_API_VERSION 0 + +//.......................................................................... +// QP Functional Safety (FuSa) Subsystem (Q_UNSAFE) +// The QP FuSa Subsystem consists of the following facilities: +// - Software assertions as a recommended technique +// (called Failure Assertion Programming (FAP) in IEC 61508) +// - Software Self-Monitoring (SSM), which encompasses such techniques: +// * Duplicate Inverse Storage for critical variables +// * Memory Markers for critical objects (e.g., events) +// * Hard-limits for all loops +// * Memory Isolation by means of Memory Protection Unit (MPU) + +// Disable QP FuSa in development +// Disable assertions and other self monitoring features +// in development build configurations (NDEBUG undefined). +// VIOLATES functional safety standards. NOT recommended !!! +//#ifndef NDEBUG +//#define Q_UNSAFE +//#endif +// + +// Disable QP FuSa in production release +// Disable assertions and other self monitoring features +// in the release build configurations (NDEBUG defined). +// VIOLATES functional safety standards. NOT recommended !!! +//#ifdef NDEBUG +//#define Q_UNSAFE +//#endif +// + +// + +//.......................................................................... +// QEP Event Processor +// Events and state machines. + +// Event signal size (Q_SIGNAL_SIZE) +// <1U=>1 +// <2U=>2 (default) +// <4U=>4 +// Size of the QEvt signal for QEP/QF [bytes] +// Default: 2 +#define Q_SIGNAL_SIZE 2U + +// + +//.......................................................................... +// QF Framework +// Active Object framework + +// Maximum # Active Objects (QF_MAX_ACTIVE) <1-64> +// Maximum # Active Objects in the system <1..64> +// Default: 32 +#define QF_MAX_ACTIVE 8U + +// Maximum # event pools (QF_MAX_EPOOL) +// <0=>0 no event pools +// <1=>1 <2=>2 <3=>3 (default) <4=>4 <5=>5 +// <6=>6 <7=>7 <8=>8 <9=>9 <10=>10 <11=>11 +// <12=>12 <13=>13 <14=>14 <15=>15 +// Maximum # Event Pools <1..15> +// Default: 3 +#define QF_MAX_EPOOL 3U + +// Maximum # clock tick rates (QF_MAX_TICK_RATE) +// <0=>0 no time events +// <1=>1 (default) <2=>2 <3=>3 <4=>4 <5=>5 +// <6=>6 <7=>7 <8=>8 <9=>9 <10=>10 <11=>11 +// <12=>12 <13=>13 <14=>14 <15=>15 +// Maximum # clock tick rates for time events <1..15> +// Default: 1 +#define QF_MAX_TICK_RATE 1U + +// Dynamic Event Constructor (QEVT_DYN_CTOR) +// Dynamic Event Constructor (RAII) +//#define QEVT_DYN_CTOR +// + +// Provide destructors for QP classes +// Destructors for classes +//#define Q_XTOR +// + +// Active Object stop API (QACTIVE_CAN_STOP) +// Enable Active Object stop API (Not recommended) +//#define QACTIVE_CAN_STOP +// + +// Event size (QF_EVENT_SIZ_SIZE) +// <1U=>1 +// <2U=>2 (default) +// <4U=>4 +// Size of the dynamic events for QF [bytes] +// Default: 2 (64K bytes maximum event size) +#define QF_EVENT_SIZ_SIZE 2U + +// Time event counter size (QF_TIMEEVT_CTR_SIZE) +// <1U=>1 +// <2U=>2 +// <4U=>4 (default) +// Size of the QTimeEvt counter [bytes] +// Default: 4 (2^32 dynamic range) +#define QF_TIMEEVT_CTR_SIZE 4U + +// Event queue counter size (QF_EQUEUE_CTR_SIZE) +// <1U=>1 (default) +// <2U=>2 +// <4U=>4 +// Size of event queue counter [bytes] +// Default: 1 (255 events maximum in a queue) +#define QF_EQUEUE_CTR_SIZE 1U + +// Memory pool counter size (QF_MPOOL_CTR_SIZE) +// <1U=>1 +// <2U=>2 (default) +// <4U=>4 +// Size of memory pool counter [bytes] +// Default: 2 (64K blocks maximum in a pool) +#define QF_MPOOL_CTR_SIZE 2U + +// Memory block size (QF_MPOOL_SIZ_SIZE) +// <1U=>1 +// <2U=>2 (default) +// <4U=>4 +// Size of memory pool block [bytes] +// Default: 2 (64K bytes maximum block size) +#define QF_MPOOL_SIZ_SIZE 2U + +// + +// Select the CPU at which the QP Framework will be attached +#define QP_PINNED_TO_CORE_0 +//#define QP_PINNED_TO_CORE_1 + +// Select QACTIVE_THREAD_TYPE +// <0U=> Dynamic thread +// <1U=> Static thread +#define QP_STATIC_THREAD 0U +//------------- <<< end of configuration section >>> ----------------------- + +#endif // QP_CONFIG_HPP_ diff --git a/examples/dpp_bsp-esp32/bsp.cpp b/examples/dpp_bsp-esp32/bsp.cpp index 08c1de4..a82a47c 100644 --- a/examples/dpp_bsp-esp32/bsp.cpp +++ b/examples/dpp_bsp-esp32/bsp.cpp @@ -1,138 +1,122 @@ -//.$file${.::bsp.cpp} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv +//$file${.::bsp.cpp} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv // // Model: dpp_bsp-esp32.qm // File: ${.::bsp.cpp} // -// This code has been generated by QM 5.1.1 . +// This code has been generated by QM 7.0.1 . // 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. +// Copyright (c) 2005 Quantum Leaps, LLC. All rights reserved. // -// 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. +// ____________________________________ +// / / +// / GGGGGGG PPPPPPPP LL / +// / GG GG PP PP LL / +// / GG PP PP LL / +// / GG GGGGG PPPPPPPP LL / +// / GG GG PP LL / +// / GGGGGGG PP LLLLLLL / +// /___________________________________/ // -//.$endhead${.::bsp.cpp} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +// SPDX-License-Identifier: GPL-3.0-or-later +// +// This generated code is open-source software licensed under the GNU +// General Public License (GPL) as published by the Free Software Foundation +// (see ). +// +// NOTE: +// The GPL does NOT permit the incorporation of this code into proprietary +// programs. Please contact Quantum Leaps for commercial licensing options, +// which expressly supersede the GPL and are designed explicitly for +// closed-source distribution. +// +// Quantum Leaps contact information: +// +// +// +//$endhead${.::bsp.cpp} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ #include "qpcpp.hpp" // QP-C++ framework #include "dpp.hpp" // DPP application #include "bsp.hpp" // Board Support Package #include #include "esp_freertos_hooks.h" -#ifndef LED_BUILTIN //If current ESP32 board does not define LED_BUILTIN -static constexpr unsigned LED_BUILTIN=13U; +//If current ESP32 board does not define LED_BUILTIN +#ifndef LED_BUILTIN +#define LED_BUILTIN 2U #endif -using namespace QP; -static uint8_t const l_TickHook = static_cast(0); - -//............................................................................ -// QS facilities - -// un-comment if QS instrumentation needed -//#define QS_ON - -enum AppRecords { // application-specific QS trace records - PHILO_STAT = QP::QS_USER, -}; - -static QP::QSpyId const l_TIMER_ID = { 0U }; // QSpy source ID - //---------------------------------------------------------------------------- // BSP functions - -static void tickHook_ESP32(void); /*Tick hook for QP */ - static void tickHook_ESP32(void) { BaseType_t xHigherPriorityTaskWoken = pdFALSE; /* process time events for rate 0 */ - QTimeEvt::tickFromISR_(0U,&xHigherPriorityTaskWoken, &l_TickHook); + QP::QTimeEvt::TICK_FROM_ISR(&xHigherPriorityTaskWoken, &l_TIMER_ID); /* notify FreeRTOS to perform context switch from ISR, if needed */ if(xHigherPriorityTaskWoken) { portYIELD_FROM_ISR(); } -#ifndef QS_ON - if (Serial.available() > 0) { - switch (Serial.read()) { // read the incoming byte - case 'p': - case 'P': - static QEvt const pauseEvt = { PAUSE_SIG, 0U, 0U}; - QF::PUBLISH(&pauseEvt, &l_TIMER_ID); - break; - case 's': - case 'S': - static QEvt const serveEvt = { SERVE_SIG, 0U, 0U}; - QF::PUBLISH(&serveEvt, &l_TIMER_ID); - break; - } +} +//............................................................................ +void QP::QF::onStartup(void) { + esp_register_freertos_tick_hook_for_cpu(tickHook_ESP32, QP_CPU_NUM); +} +//............................................................................ +extern "C" Q_NORETURN Q_onAssert(char const * const module, int location) { + // + // NOTE: add here your application-specific error handling + // + (void)module; + (void)location; + Serial.print("QP Assert module: "); + Serial.print(module); + Serial.print("@ "); + Serial.println(location); + QF_INT_DISABLE(); // disable all interrupts + for (;;) { // sit in an endless loop for now } -#endif } - -void BSP::init(void) { +//............................................................................ +namespace BSP { +void init(void) { // initialize the hardware used in this sketch... // NOTE: interrupts are configured and started later in QF::onStartup() pinMode(LED_BUILTIN, OUTPUT); randomSeed(1234); // seed the Random Number Generator Serial.begin(115200); // set the highest stanard baud rate of 115200 bps - QS_INIT(nullptr); -#ifdef QS_ON - // output QS dictionaries - QS_OBJ_DICTIONARY(&l_TIMER_ID); - QS_USR_DICTIONARY(PHILO_STAT); - - // setup the QS filters... - QS_GLB_FILTER(QP::QS_SM_RECORDS); // state machine records - QS_GLB_FILTER(QP::QS_AO_RECORDS); // active object records - QS_GLB_FILTER(QP::QS_UA_RECORDS); // all user records -#else Serial.print("QP-C++: "); Serial.print(QP_VERSION_STR); Serial.println(""); -#endif } //............................................................................ -void BSP::displayPhilStat(uint8_t n, char_t const *stat) { -#ifdef QS_ON - QS_BEGIN_ID(PHILO_STAT, AO_Philo[n]->m_prio) // app-specific record begin - QS_U8(1, n); // Philo number - QS_STR(stat); // Philo status - QS_END() -#else +void displayPhilStat(uint8_t n, char_t const *stat) { + Serial.print("Philosopher "); Serial.write(48+n); Serial.print(" "); Serial.println(stat); -#endif } //............................................................................ -void BSP::displayPaused(uint8_t paused) { +void displayPaused(uint8_t paused) { char const *msg = paused ? "Paused ON" : "Paused OFF"; -#ifndef QS_ON - Serial.println(msg); -#endif } //............................................................................ -void BSP::ledOff(void) { +void ledOff(void) { digitalWrite(LED_BUILTIN, LOW); } //............................................................................ -void BSP::ledOn(void) { +void ledOn(void) { digitalWrite(LED_BUILTIN, HIGH); } - //............................................................................ static uint32_t l_rnd; // random seed -void BSP::randomSeed(uint32_t seed) { +void randomSeed(uint32_t seed) { l_rnd = seed; } //............................................................................ -uint32_t BSP::random(void) { // a very cheap pseudo-random-number generator +uint32_t random(void) { // a very cheap pseudo-random-number generator // "Super-Duper" Linear Congruential Generator (LCG) // LCG(2^32, 3*7*11*13*23, 0, seed) // @@ -140,105 +124,4 @@ uint32_t BSP::random(void) { // a very cheap pseudo-random-number generator l_rnd = rnd; // set for the next time return (rnd >> 8); } - -//............................................................................ -void QSpy_Task(void *) { - while(1) - { - // transmit QS outgoing data (QS-TX) - uint16_t len = Serial.availableForWrite(); - if (len > 0U) { // any space available in the output buffer? - uint8_t const *buf = QS::getBlock(&len); - if (buf) { - Serial.write(buf, len); // asynchronous and non-blocking - } - } - - // receive QS incoming data (QS-RX) - len = Serial.available(); - if (len > 0U) { - do { - QP::QS::rxPut(Serial.read()); - } while (--len > 0U); - QS::rxParse(); - } - delay(100); - }; -} - -void QF::onStartup(void) { - esp_register_freertos_tick_hook_for_cpu(tickHook_ESP32, QP_CPU_NUM); - QS_OBJ_DICTIONARY(&l_TickHook); -#ifdef QS_ON - xTaskCreatePinnedToCore( - QSpy_Task, /* Function to implement the task */ - "QSPY", /* Name of the task */ - 10000, /* Stack size in words */ - NULL, /* Task input parameter */ - configMAX_PRIORITIES-1, /* Priority of the task */ - NULL, /* Task handle. */ - QP_CPU_NUM); /* Core where the task should run */ -#endif -} -//............................................................................ - -//............................................................................ -extern "C" Q_NORETURN Q_onAssert(char const * const module, int location) { - // - // NOTE: add here your application-specific error handling - // - (void)module; - (void)location; - Serial.print("QP Assert module:"); - Serial.print(module); - Serial.print(","); - Serial.println(location); - QF_INT_DISABLE(); // disable all interrupts - for (;;) { // sit in an endless loop for now - } -} - -//---------------------------------------------------------------------------- -// QS callbacks... -//............................................................................ -bool QP::QS::onStartup(void const * arg) { - static uint8_t qsTxBuf[1024]; // buffer for QS transmit channel (QS-TX) - static uint8_t qsRxBuf[128]; // buffer for QS receive channel (QS-RX) - initBuf (qsTxBuf, sizeof(qsTxBuf)); - rxInitBuf(qsRxBuf, sizeof(qsRxBuf)); - return true; // return success -} -//............................................................................ -void QP::QS::onCommand(uint8_t cmdId, uint32_t param1, - uint32_t param2, uint32_t param3) -{ -} -//............................................................................ -void QP::QS::onCleanup(void) { -} -//............................................................................ -QP::QSTimeCtr QP::QS::onGetTime(void) { -#ifdef QS_ON - return millis(); -#else - return 0; -#endif - -} -//............................................................................ -void QP::QS::onFlush(void) { -#ifdef QS_ON - uint16_t len = 0xFFFFU; // big number to get as many bytes as available - uint8_t const *buf = QS::getBlock(&len); // get continguous block of data - while (buf != nullptr) { // data available? - Serial.write(buf, len); // might poll until all bytes fit - len = 0xFFFFU; // big number to get as many bytes as available - buf = QS::getBlock(&len); // try to get more data - } - Serial.flush(); // wait for the transmission of outgoing data to complete -#endif // QS_ON -} -//............................................................................ -void QP::QS::onReset(void) { - esp_restart(); -} +} // namespace BSP diff --git a/examples/dpp_bsp-esp32/bsp.hpp b/examples/dpp_bsp-esp32/bsp.hpp index 59d6c50..903d921 100644 --- a/examples/dpp_bsp-esp32/bsp.hpp +++ b/examples/dpp_bsp-esp32/bsp.hpp @@ -1,39 +1,55 @@ -//.$file${.::bsp.hpp} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv +//$file${.::bsp.hpp} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv // // Model: dpp_bsp-esp32.qm // File: ${.::bsp.hpp} // -// This code has been generated by QM 5.1.1 . +// This code has been generated by QM 7.0.1 . // 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. +// Copyright (c) 2005 Quantum Leaps, LLC. All rights reserved. // -// 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. +// ____________________________________ +// / / +// / GGGGGGG PPPPPPPP LL / +// / GG GG PP PP LL / +// / GG PP PP LL / +// / GG GGGGG PPPPPPPP LL / +// / GG GG PP LL / +// / GGGGGGG PP LLLLLLL / +// /___________________________________/ // -//.$endhead${.::bsp.hpp} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +// SPDX-License-Identifier: GPL-3.0-or-later +// +// This generated code is open-source software licensed under the GNU +// General Public License (GPL) as published by the Free Software Foundation +// (see ). +// +// NOTE: +// The GPL does NOT permit the incorporation of this code into proprietary +// programs. Please contact Quantum Leaps for commercial licensing options, +// which expressly supersede the GPL and are designed explicitly for +// closed-source distribution. +// +// Quantum Leaps contact information: +// +// +// +//$endhead${.::bsp.hpp} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ #ifndef BSP_HPP #define BSP_HPP -class BSP { -public: +namespace BSP { enum { TICKS_PER_SEC = CONFIG_FREERTOS_HZ} ; - static void init(void); - static void displayPaused(uint8_t const paused); - static void displayPhilStat(uint8_t const n, char_t const *stat); - static void terminate(int16_t const result); + void init(void); + void displayPaused(uint8_t const paused); + void displayPhilStat(uint8_t const n, char_t const *stat); - static void randomSeed(uint32_t const seed); // random seed - static uint32_t random(void); // pseudo-random generator - static QP::QTimeEvtCtr think_rnd_time(); - static QP::QTimeEvtCtr eat_rnd_time(); + void randomSeed(uint32_t const seed); // random seed + uint32_t random(void); // pseudo-random generator - static void ledOff(void); - static void ledOn(void); + void ledOff(void); + void ledOn(void); }; #endif // BSP_HPP + diff --git a/examples/dpp_bsp-esp32/dpp.hpp b/examples/dpp_bsp-esp32/dpp.hpp index e045ab4..3c21b07 100644 --- a/examples/dpp_bsp-esp32/dpp.hpp +++ b/examples/dpp_bsp-esp32/dpp.hpp @@ -1,21 +1,40 @@ -//.$file${.::dpp.hpp} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv +//$file${.::dpp.hpp} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv // // Model: dpp_bsp-esp32.qm // File: ${.::dpp.hpp} // -// This code has been generated by QM 5.1.1 . +// This code has been generated by QM 7.0.1 . // 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. +// Copyright (c) 2005 Quantum Leaps, LLC. All rights reserved. // -// 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. +// ____________________________________ +// / / +// / GGGGGGG PPPPPPPP LL / +// / GG GG PP PP LL / +// / GG PP PP LL / +// / GG GGGGG PPPPPPPP LL / +// / GG GG PP LL / +// / GGGGGGG PP LLLLLLL / +// /___________________________________/ // -//.$endhead${.::dpp.hpp} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +// SPDX-License-Identifier: GPL-3.0-or-later +// +// This generated code is open-source software licensed under the GNU +// General Public License (GPL) as published by the Free Software Foundation +// (see ). +// +// NOTE: +// The GPL does NOT permit the incorporation of this code into proprietary +// programs. Please contact Quantum Leaps for commercial licensing options, +// which expressly supersede the GPL and are designed explicitly for +// closed-source distribution. +// +// Quantum Leaps contact information: +// +// +// +//$endhead${.::dpp.hpp} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ #ifndef DPP_HPP #define DPP_HPP @@ -33,23 +52,29 @@ enum Signals { }; // generate declarations all event classes -//.$declare${Events} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv -//.${Events::TableEvt} ....................................................... +//$declare${Events} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv + +//${Events::TableEvt} ........................................................ class TableEvt : public QP::QEvt { public: uint8_t philoNum; -}; -//.$enddecl${Events} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +}; // class TableEvt +//$enddecl${Events} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ // number of philosophers enum { N_PHILO = 5 }; // generate declarations of all opaque pointers... -//.$declare${AOs::AO_Philo[N_PHILO]} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv +//$declare${AOs::AO_Philo[N_PHILO]} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv + +//${AOs::AO_Philo[N_PHILO]} .................................................. extern QP::QActive * const AO_Philo[N_PHILO]; -//.$enddecl${AOs::AO_Philo[N_PHILO]} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -//.$declare${AOs::AO_Table} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv +//$enddecl${AOs::AO_Philo[N_PHILO]} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +//$declare${AOs::AO_Table} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv + +//${AOs::AO_Table} ........................................................... extern QP::QActive * const AO_Table; -//.$enddecl${AOs::AO_Table} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +//$enddecl${AOs::AO_Table} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ #endif // DPP_HPP + diff --git a/examples/dpp_bsp-esp32/dpp_bsp-esp32.ino b/examples/dpp_bsp-esp32/dpp_bsp-esp32.ino index b6160db..d16db1d 100644 --- a/examples/dpp_bsp-esp32/dpp_bsp-esp32.ino +++ b/examples/dpp_bsp-esp32/dpp_bsp-esp32.ino @@ -1,27 +1,46 @@ -//.$file${.::dpp_bsp-esp32.ino} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv +//$file${.::dpp_bsp-esp32.ino} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv // // Model: dpp_bsp-esp32.qm // File: ${.::dpp_bsp-esp32.ino} // -// This code has been generated by QM 5.1.1 . +// This code has been generated by QM 7.0.1 . // 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. +// Copyright (c) 2005 Quantum Leaps, LLC. All rights reserved. // -// 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. +// ____________________________________ +// / / +// / GGGGGGG PPPPPPPP LL / +// / GG GG PP PP LL / +// / GG PP PP LL / +// / GG GGGGG PPPPPPPP LL / +// / GG GG PP LL / +// / GGGGGGG PP LLLLLLL / +// /___________________________________/ // -//.$endhead${.::dpp_bsp-esp32.ino} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +// SPDX-License-Identifier: GPL-3.0-or-later +// +// This generated code is open-source software licensed under the GNU +// General Public License (GPL) as published by the Free Software Foundation +// (see ). +// +// NOTE: +// The GPL does NOT permit the incorporation of this code into proprietary +// programs. Please contact Quantum Leaps for commercial licensing options, +// which expressly supersede the GPL and are designed explicitly for +// closed-source distribution. +// +// Quantum Leaps contact information: +// +// +// +//$endhead${.::dpp_bsp-esp32.ino} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ #include "qpcpp.hpp" // QP-C++ framework #include "dpp.hpp" // DPP application #include "bsp.hpp" // Board Support Package using namespace QP; -static constexpr unsigned stack_size = 1000; +static constexpr unsigned stack_size = 2048; Q_DEFINE_THIS_FILE @@ -43,16 +62,23 @@ void setup() { // start Philos static QP::QEvt const *philoQueueSto[10][N_PHILO]; + static StackType_t philoStack[10][stack_size]; for (uint8_t n = 0U; n < N_PHILO; ++n) { AO_Philo[n]->start((uint_fast8_t)(n + 1U), // priority - philoQueueSto[n], Q_DIM(philoQueueSto[n]), - (void *)0, stack_size); + philoQueueSto[n], + Q_DIM(philoQueueSto[n]), + philoStack[n], + stack_size); } // start Table static QP::QEvt const *tableQueueSto[N_PHILO]; - AO_Table->start((uint_fast8_t)(N_PHILO + 1U), // priority - tableQueueSto, Q_DIM(tableQueueSto), - (void *)0, stack_size); + static StackType_t tableStack[stack_size]; + AO_Table->start( + (uint_fast8_t)(N_PHILO + 1U), // priority + tableQueueSto, + Q_DIM(tableQueueSto), + tableStack, + stack_size); QF::run(); // run the QF/C++ framework } diff --git a/examples/dpp_bsp-esp32/dpp_bsp-esp32.qm b/examples/dpp_bsp-esp32/dpp_bsp-esp32.qm index 4f99518..6635b41 100644 --- a/examples/dpp_bsp-esp32/dpp_bsp-esp32.qm +++ b/examples/dpp_bsp-esp32/dpp_bsp-esp32.qm @@ -1,5 +1,5 @@ - + This is the Dining Philosopher Problem (DPP) example for the Arduino-ESP32 board. The example demonstrates: 1. Multiple active objects (5 Philosophers and 1 Table AO) @@ -109,8 +109,8 @@ Q_ASSERT(Q_EVT_CAST(TableEvt)->philoNum != PHILO_ID(this)); - - + + @@ -140,7 +140,7 @@ Q_ASSERT(Q_EVT_CAST(TableEvt)->philoNum != PHILO_ID(this)); - + @@ -165,8 +165,8 @@ Q_ASSERT(Q_EVT_CAST(TableEvt)->philoNum != PHILO_ID(this)); - - + + @@ -338,7 +338,7 @@ if (m_isHungry[m] && (m_fork[n] == FREE)) { - + @@ -380,8 +380,8 @@ m_fork[n] = FREE; - - + + @@ -406,73 +406,22 @@ m_fork[n] = FREE; - - - #include "qpcpp.hpp" // QP-C++ framework -#include "dpp.hpp" // DPP application -#include "bsp.hpp" // Board Support Package - -using namespace QP; -static constexpr unsigned stack_size = 1000; - -Q_DEFINE_THIS_FILE - -//............................................................................ -void setup() { - QF::init(); // initialize the framework - BSP::init(); // initialize the Board Support Package - - // init publish-subscribe - static QSubscrList subscrSto[MAX_PUB_SIG]; - QF::psInit(subscrSto, Q_DIM(subscrSto)); - - // initialize event pools... - static QF_MPOOL_EL(TableEvt) smlPoolSto[2*N_PHILO]; - QF::poolInit(smlPoolSto, - sizeof(smlPoolSto), sizeof(smlPoolSto[0])); - - // start all active objects... - - // start Philos - static QP::QEvt const *philoQueueSto[10][N_PHILO]; - for (uint8_t n = 0U; n < N_PHILO; ++n) { - AO_Philo[n]->start((uint_fast8_t)(n + 1U), // priority - philoQueueSto[n], Q_DIM(philoQueueSto[n]), - (void *)0, stack_size); - } - // start Table - static QP::QEvt const *tableQueueSto[N_PHILO]; - AO_Table->start((uint_fast8_t)(N_PHILO + 1U), // priority - tableQueueSto, Q_DIM(tableQueueSto), - (void *)0, stack_size); - QF::run(); // run the QF/C++ framework -} - -//............................................................................ -void loop() { -} - - #ifndef BSP_HPP #define BSP_HPP -class BSP { -public: +namespace BSP { enum { TICKS_PER_SEC = CONFIG_FREERTOS_HZ} ; - static void init(void); - static void displayPaused(uint8_t const paused); - static void displayPhilStat(uint8_t const n, char_t const *stat); - static void terminate(int16_t const result); - - static void randomSeed(uint32_t const seed); // random seed - static uint32_t random(void); // pseudo-random generator - static QP::QTimeEvtCtr think_rnd_time(); - static QP::QTimeEvtCtr eat_rnd_time(); - - static void ledOff(void); - static void ledOn(void); + void init(void); + void displayPaused(uint8_t const paused); + void displayPhilStat(uint8_t const n, char_t const *stat); + + void randomSeed(uint32_t const seed); // random seed + uint32_t random(void); // pseudo-random generator + + void ledOff(void); + void ledOn(void); }; #endif // BSP_HPP @@ -486,116 +435,82 @@ public: #include <Arduino.h> #include "esp_freertos_hooks.h" -#ifndef LED_BUILTIN //If current ESP32 board does not define LED_BUILTIN -static constexpr unsigned LED_BUILTIN=13U; +//If current ESP32 board does not define LED_BUILTIN +#ifndef LED_BUILTIN +#define LED_BUILTIN 2U #endif -using namespace QP; - -//............................................................................ -// QS facilities - -// un-comment if QS instrumentation needed -//#define QS_ON - -enum AppRecords { // application-specific QS trace records - PHILO_STAT = QP::QS_USER, -}; - -static QP::QSpyId const l_TIMER_ID = { 0U }; // QSpy source ID - //---------------------------------------------------------------------------- // BSP functions - -static void tickHook_ESP32(void); /*Tick hook for QP */ - static void tickHook_ESP32(void) { BaseType_t xHigherPriorityTaskWoken = pdFALSE; /* process time events for rate 0 */ - QF::TICK_FROM_ISR(&xHigherPriorityTaskWoken, &l_TIMER_ID); + QP::QTimeEvt::TICK_FROM_ISR(&xHigherPriorityTaskWoken, &l_TIMER_ID); /* notify FreeRTOS to perform context switch from ISR, if needed */ if(xHigherPriorityTaskWoken) { portYIELD_FROM_ISR(); } -#ifndef QS_ON - if (Serial.available() > 0) { - switch (Serial.read()) { // read the incoming byte - case 'p': - case 'P': - static QEvt const pauseEvt = { PAUSE_SIG, 0U, 0U}; - QF::PUBLISH(&pauseEvt, &l_TIMER_ID); - break; - case 's': - case 'S': - static QEvt const serveEvt = { SERVE_SIG, 0U, 0U}; - QF::PUBLISH(&serveEvt, &l_TIMER_ID); - break; - } +} +//............................................................................ +void QP::QF::onStartup(void) { + esp_register_freertos_tick_hook_for_cpu(tickHook_ESP32, QP_CPU_NUM); +} +//............................................................................ +extern "C" Q_NORETURN Q_onAssert(char const * const module, int location) { + // + // NOTE: add here your application-specific error handling + // + (void)module; + (void)location; + Serial.print("QP Assert module: "); + Serial.print(module); + Serial.print("@ "); + Serial.println(location); + QF_INT_DISABLE(); // disable all interrupts + for (;;) { // sit in an endless loop for now } -#endif } - -void BSP::init(void) { +//............................................................................ +namespace BSP { +void init(void) { // initialize the hardware used in this sketch... // NOTE: interrupts are configured and started later in QF::onStartup() pinMode(LED_BUILTIN, OUTPUT); randomSeed(1234); // seed the Random Number Generator Serial.begin(115200); // set the highest stanard baud rate of 115200 bps - QS_INIT(nullptr); -#ifdef QS_ON - // output QS dictionaries - QS_OBJ_DICTIONARY(&l_TIMER_ID); - QS_USR_DICTIONARY(PHILO_STAT); - - // setup the QS filters... - QS_GLB_FILTER(QP::QS_SM_RECORDS); // state machine records - QS_GLB_FILTER(QP::QS_AO_RECORDS); // active object records - QS_GLB_FILTER(QP::QS_UA_RECORDS); // all user records -#else Serial.print("QP-C++: "); Serial.print(QP_VERSION_STR); Serial.println(""); -#endif } //............................................................................ -void BSP::displayPhilStat(uint8_t n, char_t const *stat) { -#ifdef QS_ON - QS_BEGIN_ID(PHILO_STAT, AO_Philo[n]->m_prio) // app-specific record begin - QS_U8(1, n); // Philo number - QS_STR(stat); // Philo status - QS_END() -#else +void displayPhilStat(uint8_t n, char_t const *stat) { + Serial.print("Philosopher "); Serial.write(48+n); Serial.print(" "); Serial.println(stat); -#endif } //............................................................................ -void BSP::displayPaused(uint8_t paused) { +void displayPaused(uint8_t paused) { char const *msg = paused ? "Paused ON" : "Paused OFF"; -#ifndef QS_ON - Serial.println(msg); -#endif } //............................................................................ -void BSP::ledOff(void) { +void ledOff(void) { digitalWrite(LED_BUILTIN, LOW); } //............................................................................ -void BSP::ledOn(void) { +void ledOn(void) { digitalWrite(LED_BUILTIN, HIGH); } - //............................................................................ static uint32_t l_rnd; // random seed -void BSP::randomSeed(uint32_t seed) { +void randomSeed(uint32_t seed) { l_rnd = seed; } //............................................................................ -uint32_t BSP::random(void) { // a very cheap pseudo-random-number generator +uint32_t random(void) { // a very cheap pseudo-random-number generator // "Super-Duper" Linear Congruential Generator (LCG) // LCG(2^32, 3*7*11*13*23, 0, seed) // @@ -603,108 +518,7 @@ uint32_t BSP::random(void) { // a very cheap pseudo-random-number generator l_rnd = rnd; // set for the next time return (rnd >> 8); } - -//............................................................................ -void QSpy_Task(void *) { - while(1) - { - // transmit QS outgoing data (QS-TX) - uint16_t len = Serial.availableForWrite(); - if (len > 0U) { // any space available in the output buffer? - uint8_t const *buf = QS::getBlock(&len); - if (buf) { - Serial.write(buf, len); // asynchronous and non-blocking - } - } - - // receive QS incoming data (QS-RX) - len = Serial.available(); - if (len > 0U) { - do { - QP::QS::rxPut(Serial.read()); - } while (--len > 0U); - QS::rxParse(); - } - delay(100); - }; -} - -void QF::onStartup(void) { - esp_register_freertos_tick_hook_for_cpu(tickHook_ESP32, QP_CPU_NUM); -#ifdef QS_ON - xTaskCreatePinnedToCore( - QSpy_Task, /* Function to implement the task */ - "QSPY", /* Name of the task */ - 10000, /* Stack size in words */ - NULL, /* Task input parameter */ - configMAX_PRIORITIES-1, /* Priority of the task */ - NULL, /* Task handle. */ - QP_CPU_NUM); /* Core where the task should run */ -#endif -} -//............................................................................ - -//............................................................................ -extern "C" Q_NORETURN Q_onAssert(char const * const module, int location) { - // - // NOTE: add here your application-specific error handling - // - (void)module; - (void)location; - Serial.print("QP Assert module:"); - Serial.print(module); - Serial.print(","); - Serial.println(location); - QF_INT_DISABLE(); // disable all interrupts - for (;;) { // sit in an endless loop for now - } -} - -//---------------------------------------------------------------------------- -// QS callbacks... -//............................................................................ -bool QP::QS::onStartup(void const * arg) { - static uint8_t qsTxBuf[1024]; // buffer for QS transmit channel (QS-TX) - static uint8_t qsRxBuf[128]; // buffer for QS receive channel (QS-RX) - initBuf (qsTxBuf, sizeof(qsTxBuf)); - rxInitBuf(qsRxBuf, sizeof(qsRxBuf)); - return true; // return success -} -//............................................................................ -void QP::QS::onCommand(uint8_t cmdId, uint32_t param1, - uint32_t param2, uint32_t param3) -{ -} -//............................................................................ -void QP::QS::onCleanup(void) { -} -//............................................................................ -QP::QSTimeCtr QP::QS::onGetTime(void) { -#ifdef QS_ON - return millis(); -#else - return 0; -#endif - -} -//............................................................................ -void QP::QS::onFlush(void) { -#ifdef QS_ON - uint16_t len = 0xFFFFU; // big number to get as many bytes as available - uint8_t const *buf = QS::getBlock(&len); // get continguous block of data - while (buf != nullptr) { // data available? - Serial.write(buf, len); // might poll until all bytes fit - len = 0xFFFFU; // big number to get as many bytes as available - buf = QS::getBlock(&len); // try to get more data - } - Serial.flush(); // wait for the transmission of outgoing data to complete -#endif // QS_ON -} -//............................................................................ -void QP::QS::onReset(void) { - esp_restart(); -} - +} // namespace BSP @@ -807,5 +621,58 @@ static char const * const EATING = "eating "; $define${AOs::Table} + + + #include "qpcpp.hpp" // QP-C++ framework +#include "dpp.hpp" // DPP application +#include "bsp.hpp" // Board Support Package + +using namespace QP; +static constexpr unsigned stack_size = 2048; + +Q_DEFINE_THIS_FILE + +//............................................................................ +void setup() { + QF::init(); // initialize the framework + BSP::init(); // initialize the Board Support Package + + // init publish-subscribe + static QSubscrList subscrSto[MAX_PUB_SIG]; + QF::psInit(subscrSto, Q_DIM(subscrSto)); + + // initialize event pools... + static QF_MPOOL_EL(TableEvt) smlPoolSto[2*N_PHILO]; + QF::poolInit(smlPoolSto, + sizeof(smlPoolSto), sizeof(smlPoolSto[0])); + + // start all active objects... + + // start Philos + static QP::QEvt const *philoQueueSto[10][N_PHILO]; + static StackType_t philoStack[10][stack_size]; + for (uint8_t n = 0U; n < N_PHILO; ++n) { + AO_Philo[n]->start((uint_fast8_t)(n + 1U), // priority + philoQueueSto[n], + Q_DIM(philoQueueSto[n]), + philoStack[n], + stack_size); + } + // start Table + static QP::QEvt const *tableQueueSto[N_PHILO]; + static StackType_t tableStack[stack_size]; + AO_Table->start( + (uint_fast8_t)(N_PHILO + 1U), // priority + tableQueueSto, + Q_DIM(tableQueueSto), + tableStack, + stack_size); + QF::run(); // run the QF/C++ framework +} + +//............................................................................ +void loop() { +} + diff --git a/examples/dpp_bsp-esp32/philo.cpp b/examples/dpp_bsp-esp32/philo.cpp index 3e8dd81..b5745bc 100644 --- a/examples/dpp_bsp-esp32/philo.cpp +++ b/examples/dpp_bsp-esp32/philo.cpp @@ -1,21 +1,40 @@ -//.$file${.::philo.cpp} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv +//$file${.::philo.cpp} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv // // Model: dpp_bsp-esp32.qm // File: ${.::philo.cpp} // -// This code has been generated by QM 5.1.1 . +// This code has been generated by QM 7.0.1 . // 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. +// Copyright (c) 2005 Quantum Leaps, LLC. All rights reserved. // -// 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. +// ____________________________________ +// / / +// / GGGGGGG PPPPPPPP LL / +// / GG GG PP PP LL / +// / GG PP PP LL / +// / GG GGGGG PPPPPPPP LL / +// / GG GG PP LL / +// / GGGGGGG PP LLLLLLL / +// /___________________________________/ // -//.$endhead${.::philo.cpp} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +// SPDX-License-Identifier: GPL-3.0-or-later +// +// This generated code is open-source software licensed under the GNU +// General Public License (GPL) as published by the Free Software Foundation +// (see ). +// +// NOTE: +// The GPL does NOT permit the incorporation of this code into proprietary +// programs. Please contact Quantum Leaps for commercial licensing options, +// which expressly supersede the GPL and are designed explicitly for +// closed-source distribution. +// +// Quantum Leaps contact information: +// +// +// +//$endhead${.::philo.cpp} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ #include "qpcpp.hpp" // QP-C++ framework #include "dpp.hpp" // DPP application #include "bsp.hpp" // Board Support Package @@ -23,8 +42,9 @@ Q_DEFINE_THIS_FILE // generate declaration of the active object --------------------------------- -//.$declare${AOs::Philo} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv -//.${AOs::Philo} ............................................................. +//$declare${AOs::Philo} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv + +//${AOs::Philo} .............................................................. class Philo : public QP::QActive { private: QP::QTimeEvt m_timeEvt; @@ -40,18 +60,19 @@ class Philo : public QP::QActive { Q_STATE_DECL(thinking); Q_STATE_DECL(hungry); Q_STATE_DECL(eating); -}; -//.$enddecl${AOs::Philo} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +}; // class Philo +//$enddecl${AOs::Philo} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ // generate definition of the opaque pointer to the AO ----------------------- -//.$skip${QP_VERSION} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv -//. Check for the minimum required QP version -#if (QP_VERSION < 690U) || (QP_VERSION != ((QP_RELEASE^4294967295U) % 0x3E8U)) -#error qpcpp version 6.9.0 or higher required +//$skip${QP_VERSION} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv +// Check for the minimum required QP version +#if (QP_VERSION < 730U) || (QP_VERSION != ((QP_RELEASE^4294967295U)%0x2710U)) +#error qpcpp version 7.3.0 or higher required #endif -//.$endskip${QP_VERSION} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -//.$define${AOs::AO_Philo[N_PHILO]} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv -//.${AOs::AO_Philo[N_PHILO]} ................................................. +//$endskip${QP_VERSION} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +//$define${AOs::AO_Philo[N_PHILO]} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv + +//${AOs::AO_Philo[N_PHILO]} .................................................. QP::QActive * const AO_Philo[N_PHILO] = { // "opaque" pointers to Philo AO &Philo::inst[0], &Philo::inst[1], @@ -59,7 +80,7 @@ QP::QActive * const AO_Philo[N_PHILO] = { // "opaque" pointers to Philo AO &Philo::inst[3], &Philo::inst[4] }; -//.$enddef${AOs::AO_Philo[N_PHILO]} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +//$enddef${AOs::AO_Philo[N_PHILO]} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ // helper function to provide a randomized think time for Philos inline QP::QTimeEvtCtr think_time() { @@ -79,18 +100,20 @@ inline uint8_t PHILO_ID(Philo const * const philo) { } // generate definition of the AO -------------------------------------------- -//.$define${AOs::Philo} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv -//.${AOs::Philo} ............................................................. +//$define${AOs::Philo} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv + +//${AOs::Philo} .............................................................. Philo Philo::inst[N_PHILO]; -//.${AOs::Philo::Philo} ...................................................... + +//${AOs::Philo::Philo} ....................................................... Philo::Philo() : QActive(Q_STATE_CAST(&Philo::initial)), m_timeEvt(this, TIMEOUT_SIG, 0U) {} -//.${AOs::Philo::SM} ......................................................... +//${AOs::Philo::SM} .......................................................... Q_STATE_DEF(Philo, initial) { - //.${AOs::Philo::SM::initial} + //${AOs::Philo::SM::initial} (void)e; // unused parameter subscribe(EAT_SIG); @@ -121,28 +144,29 @@ Q_STATE_DEF(Philo, initial) { } return tran(&thinking); } -//.${AOs::Philo::SM::thinking} ............................................... + +//${AOs::Philo::SM::thinking} ................................................ Q_STATE_DEF(Philo, thinking) { QP::QState status_; switch (e->sig) { - //.${AOs::Philo::SM::thinking} + //${AOs::Philo::SM::thinking} case Q_ENTRY_SIG: { m_timeEvt.armX(think_time(), 0U); status_ = Q_RET_HANDLED; break; } - //.${AOs::Philo::SM::thinking} + //${AOs::Philo::SM::thinking} case Q_EXIT_SIG: { (void)m_timeEvt.disarm(); status_ = Q_RET_HANDLED; break; } - //.${AOs::Philo::SM::thinking::TIMEOUT} + //${AOs::Philo::SM::thinking::TIMEOUT} case TIMEOUT_SIG: { status_ = tran(&hungry); break; } - //.${AOs::Philo::SM::thinking::EAT, DONE} + //${AOs::Philo::SM::thinking::EAT, DONE} case EAT_SIG: // intentionally fall through case DONE_SIG: { // EAT or DONE must be for other Philos than this one @@ -150,7 +174,7 @@ Q_STATE_DEF(Philo, thinking) { status_ = Q_RET_HANDLED; break; } - //.${AOs::Philo::SM::thinking::TEST} + //${AOs::Philo::SM::thinking::TEST} case TEST_SIG: { status_ = Q_RET_HANDLED; break; @@ -162,11 +186,12 @@ Q_STATE_DEF(Philo, thinking) { } return status_; } -//.${AOs::Philo::SM::hungry} ................................................. + +//${AOs::Philo::SM::hungry} .................................................. Q_STATE_DEF(Philo, hungry) { QP::QState status_; switch (e->sig) { - //.${AOs::Philo::SM::hungry} + //${AOs::Philo::SM::hungry} case Q_ENTRY_SIG: { TableEvt *pe = Q_NEW(TableEvt, HUNGRY_SIG); pe->philoNum = PHILO_ID(this); @@ -174,9 +199,9 @@ Q_STATE_DEF(Philo, hungry) { status_ = Q_RET_HANDLED; break; } - //.${AOs::Philo::SM::hungry::EAT} + //${AOs::Philo::SM::hungry::EAT} case EAT_SIG: { - //.${AOs::Philo::SM::hungry::EAT::[Q_EVT_CAST(TableEvt)->philoNum=~} + //${AOs::Philo::SM::hungry::EAT::[Q_EVT_CAST(TableEvt)->philoNum=~} if (Q_EVT_CAST(TableEvt)->philoNum == PHILO_ID(this)) { status_ = tran(&eating); } @@ -185,7 +210,7 @@ Q_STATE_DEF(Philo, hungry) { } break; } - //.${AOs::Philo::SM::hungry::DONE} + //${AOs::Philo::SM::hungry::DONE} case DONE_SIG: { /* DONE must be for other Philos than this one */ Q_ASSERT(Q_EVT_CAST(TableEvt)->philoNum != PHILO_ID(this)); @@ -199,17 +224,18 @@ Q_STATE_DEF(Philo, hungry) { } return status_; } -//.${AOs::Philo::SM::eating} ................................................. + +//${AOs::Philo::SM::eating} .................................................. Q_STATE_DEF(Philo, eating) { QP::QState status_; switch (e->sig) { - //.${AOs::Philo::SM::eating} + //${AOs::Philo::SM::eating} case Q_ENTRY_SIG: { m_timeEvt.armX(eat_time(), 0U); status_ = Q_RET_HANDLED; break; } - //.${AOs::Philo::SM::eating} + //${AOs::Philo::SM::eating} case Q_EXIT_SIG: { TableEvt *pe = Q_NEW(TableEvt, DONE_SIG); pe->philoNum = PHILO_ID(this); @@ -218,12 +244,12 @@ Q_STATE_DEF(Philo, eating) { status_ = Q_RET_HANDLED; break; } - //.${AOs::Philo::SM::eating::TIMEOUT} + //${AOs::Philo::SM::eating::TIMEOUT} case TIMEOUT_SIG: { status_ = tran(&thinking); break; } - //.${AOs::Philo::SM::eating::EAT, DONE} + //${AOs::Philo::SM::eating::EAT, DONE} case EAT_SIG: // intentionally fall through case DONE_SIG: { // EAT or DONE must be for other Philos than this one @@ -238,4 +264,4 @@ Q_STATE_DEF(Philo, eating) { } return status_; } -//.$enddef${AOs::Philo} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +//$enddef${AOs::Philo} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/examples/dpp_bsp-esp32/qp_config.hpp b/examples/dpp_bsp-esp32/qp_config.hpp new file mode 100644 index 0000000..8891edf --- /dev/null +++ b/examples/dpp_bsp-esp32/qp_config.hpp @@ -0,0 +1,199 @@ +//============================================================================ +// QP configuration file (generic) +// Last updated for version: 7.3.0 +// Last updated on: 2023-10-30 +// +// 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: +// +// +//============================================================================ +#ifndef QP_CONFIG_HPP_ +#define QP_CONFIG_HPP_ + +//-------- <<< Use Configuration Wizard in Context Menu >>> ----------------- + +// NOTE: Requires command-line macro: QP_CONFIG +// This qp_config.h header file is activated only when the macro +// QP_CONFIG is defined on the command-line to the compiler +// ------------------------------------------- + +// QP API compatibility version (QP_API_VERSION) +// <0=> 0 (Maximum compatibility) +// <580=>580 (QP 5.8.0 or newer) +// <660=>660 (QP 6.6.0 or newer) +// <691=>691 (QP 6.9.1 or newer) +// <700=>700 (QP 7.0.0 or newer) +// <9999=>9999 (Latest only) +// QP API backwards compatibility with the QP/C API version. +// Lower QP_API_VERSION values enable backwards compatibility +// with lower (older) QP API versions. +// For example, QP_API_VERSION==691 will enable the compatibility +// layer with QP version 6.9.1 and newer, but not older than 6.9.1. +// QP_API_VERSION==0 enables the maximum currently supported +// backwards compatibility. Conversely, QP_API_VERSION==9999 means +// that no backwards compatibility layer should be enabled. +// Default: 0 (All supported) +#define QP_API_VERSION 0 + +//.......................................................................... +// QP Functional Safety (FuSa) Subsystem (Q_UNSAFE) +// The QP FuSa Subsystem consists of the following facilities: +// - Software assertions as a recommended technique +// (called Failure Assertion Programming (FAP) in IEC 61508) +// - Software Self-Monitoring (SSM), which encompasses such techniques: +// * Duplicate Inverse Storage for critical variables +// * Memory Markers for critical objects (e.g., events) +// * Hard-limits for all loops +// * Memory Isolation by means of Memory Protection Unit (MPU) + +// Disable QP FuSa in development +// Disable assertions and other self monitoring features +// in development build configurations (NDEBUG undefined). +// VIOLATES functional safety standards. NOT recommended !!! +//#ifndef NDEBUG +//#define Q_UNSAFE +//#endif +// + +// Disable QP FuSa in production release +// Disable assertions and other self monitoring features +// in the release build configurations (NDEBUG defined). +// VIOLATES functional safety standards. NOT recommended !!! +//#ifdef NDEBUG +//#define Q_UNSAFE +//#endif +// + +// + +//.......................................................................... +// QEP Event Processor +// Events and state machines. + +// Event signal size (Q_SIGNAL_SIZE) +// <1U=>1 +// <2U=>2 (default) +// <4U=>4 +// Size of the QEvt signal for QEP/QF [bytes] +// Default: 2 +#define Q_SIGNAL_SIZE 2U + +// + +//.......................................................................... +// QF Framework +// Active Object framework + +// Maximum # Active Objects (QF_MAX_ACTIVE) <1-64> +// Maximum # Active Objects in the system <1..64> +// Default: 32 +#define QF_MAX_ACTIVE 8U + +// Maximum # event pools (QF_MAX_EPOOL) +// <0=>0 no event pools +// <1=>1 <2=>2 <3=>3 (default) <4=>4 <5=>5 +// <6=>6 <7=>7 <8=>8 <9=>9 <10=>10 <11=>11 +// <12=>12 <13=>13 <14=>14 <15=>15 +// Maximum # Event Pools <1..15> +// Default: 3 +#define QF_MAX_EPOOL 3U + +// Maximum # clock tick rates (QF_MAX_TICK_RATE) +// <0=>0 no time events +// <1=>1 (default) <2=>2 <3=>3 <4=>4 <5=>5 +// <6=>6 <7=>7 <8=>8 <9=>9 <10=>10 <11=>11 +// <12=>12 <13=>13 <14=>14 <15=>15 +// Maximum # clock tick rates for time events <1..15> +// Default: 1 +#define QF_MAX_TICK_RATE 1U + +// Dynamic Event Constructor (QEVT_DYN_CTOR) +// Dynamic Event Constructor (RAII) +//#define QEVT_DYN_CTOR +// + +// Provide destructors for QP classes +// Destructors for classes +//#define Q_XTOR +// + +// Active Object stop API (QACTIVE_CAN_STOP) +// Enable Active Object stop API (Not recommended) +//#define QACTIVE_CAN_STOP +// + +// Event size (QF_EVENT_SIZ_SIZE) +// <1U=>1 +// <2U=>2 (default) +// <4U=>4 +// Size of the dynamic events for QF [bytes] +// Default: 2 (64K bytes maximum event size) +#define QF_EVENT_SIZ_SIZE 2U + +// Time event counter size (QF_TIMEEVT_CTR_SIZE) +// <1U=>1 +// <2U=>2 +// <4U=>4 (default) +// Size of the QTimeEvt counter [bytes] +// Default: 4 (2^32 dynamic range) +#define QF_TIMEEVT_CTR_SIZE 4U + +// Event queue counter size (QF_EQUEUE_CTR_SIZE) +// <1U=>1 (default) +// <2U=>2 +// <4U=>4 +// Size of event queue counter [bytes] +// Default: 1 (255 events maximum in a queue) +#define QF_EQUEUE_CTR_SIZE 1U + +// Memory pool counter size (QF_MPOOL_CTR_SIZE) +// <1U=>1 +// <2U=>2 (default) +// <4U=>4 +// Size of memory pool counter [bytes] +// Default: 2 (64K blocks maximum in a pool) +#define QF_MPOOL_CTR_SIZE 2U + +// Memory block size (QF_MPOOL_SIZ_SIZE) +// <1U=>1 +// <2U=>2 (default) +// <4U=>4 +// Size of memory pool block [bytes] +// Default: 2 (64K bytes maximum block size) +#define QF_MPOOL_SIZ_SIZE 2U + +// + +// Select the CPU at which the QP Framework will be attached +#define QP_PINNED_TO_CORE_0 +//#define QP_PINNED_TO_CORE_1 + +// Select QACTIVE_THREAD_TYPE +// <0U=> Dynamic thread +// <1U=> Static thread +#define QP_STATIC_THREAD 0U +//------------- <<< end of configuration section >>> ----------------------- + +#endif // QP_CONFIG_HPP_ diff --git a/examples/dpp_bsp-esp32/qview/dpp.py b/examples/dpp_bsp-esp32/qview/dpp.py deleted file mode 100644 index b0ea9f8..0000000 --- a/examples/dpp_bsp-esp32/qview/dpp.py +++ /dev/null @@ -1,103 +0,0 @@ -# This is an example of QView customization for a specific application -# (DPP in this case). This example animates the Phil images on the -# QView canvas. Additionally, there is a button in the middle of the screen, -# which, when clicked once pauses the DPP ("forks" are not being served). -# A second click on the button, "un-pauses" the DPP ("forks" are served -# to all hungry Philosophers). -# -# This version of the DPP customization uses the application-specific -# packet QS_USER_00 (PHILO_STAT) produced when the status of a Philo changes. -# - -class DPP: - def __init__(self): - - # add commands to the Custom menu... - QView.custom_menu.add_command(label="Custom command", - command=self.cust_command) - - # configure the custom QView.canvas... - QView.show_canvas() # make the canvas visible - QView.canvas.configure(width=400, height=260) - - # tuple of activity images (correspond to self._philo_state) - self._act_img = ( - PhotoImage(file=HOME_DIR + "/img/thinking.gif"), - PhotoImage(file=HOME_DIR + "/img/hungry.gif"), - PhotoImage(file=HOME_DIR + "/img/eating.gif"), - ) - # tuple of philo canvas images (correspond to self._philo_obj) - self._philo_img = (\ - QView.canvas.create_image(190, 57, image=self._act_img[0]), - QView.canvas.create_image(273, 100, image=self._act_img[0]), - QView.canvas.create_image(237, 185, image=self._act_img[0]), - QView.canvas.create_image(146, 185, image=self._act_img[0]), - QView.canvas.create_image(107, 100, image=self._act_img[0]) - ) - - # button images for UP and DOWN - self.img_UP = PhotoImage(file=HOME_DIR + "/img/BTN_UP.gif") - self.img_DWN = PhotoImage(file=HOME_DIR + "/img/BTN_DWN.gif") - - # images of a button for pause/serve - self.btn = QView.canvas.create_image(200, 120, image=self.img_UP) - QView.canvas.tag_bind(self.btn, "", self.cust_pause) - - # request target reset on startup... - # NOTE: Normally, for an embedded application you would like - # to start with resetting the Target, to start clean with - # Qs dictionaries, etc. - reset_target() - - # on_reset() callback - def on_reset(self): - # clear the lists - self._philo_obj = [0, 0, 0, 0, 0] - self._philo_state = [0, 0, 0] - - # on_run() callback - def on_run(self): - glb_filter("QS_USER_00") - - # NOTE: the name of object for current_obj() must match the - # QS Object Dictionaries produced by the application. - current_obj(OBJ_AO, "Table::inst") - - # turn lists into tuples for better performance - self._philo_obj = tuple(self._philo_obj) - self._philo_state = tuple(self._philo_state) - - - # example of a custom command - def cust_command(self): - command(1, 12345) - - # example of a custom interaction with a canvas object (pause/serve) - def cust_pause(self, event): - if QView.canvas.itemcget(self.btn, "image") != str(self.img_UP): - QView.canvas.itemconfig(self.btn, image=self.img_UP) - post("SERVE_SIG") - QView.print_text("Table SERVING") - else: - QView.canvas.itemconfig(self.btn, image=self.img_DWN) - post("PAUSE_SIG") - QView.print_text("Table PAUSED") - - # intercept the QS_USER_00 application-specific packet - # this packet has the following structure (see bsp.c:displayPhilStat()): - # record-ID, seq-num, Timestamp, format-byte, Philo-num, - # format-bye, Zero-terminated string (status) - def QS_USER_00(self, packet): - # unpack: Timestamp->data[0], Philo-num->data[1], status->data[3] - data = qunpack("xxTxBxZ", packet) - i = data[1] - j = ("t", "h", "e").index(data[2][0]) # the first letter - - # animate the given philo image according to its activity - QView.canvas.itemconfig(self._philo_img[i], image=self._act_img[j]) - - # print a message to the text view - QView.print_text("%010d Philo %1d is %s"%(data[0], i, data[2])) - -#============================================================================= -QView.customize(DPP()) # set the QView customization diff --git a/examples/dpp_bsp-esp32/qview/dpp1.py b/examples/dpp_bsp-esp32/qview/dpp1.py deleted file mode 100644 index 20d0412..0000000 --- a/examples/dpp_bsp-esp32/qview/dpp1.py +++ /dev/null @@ -1,147 +0,0 @@ -# This is an example of QView customization for a specific application -# (DPP in this case). This example animates the Phil images on the -# QView canvas. Additionally, there is a button in the middle of the screen, -# which, when clicked once pauses the DPP ("forks" are not being served). -# A second click on the button, "un-pauses" the DPP ("forks" are served -# to all hungry Philosophers). -# -# This version of the DPP customization uses the standard QS_QEP_STATE_ENTRY -# packet, which provides information about the current states of the dining -# Philosophers. The example also demonstrates how to intercept the QS -# "dictionary" records QS_OBJ_DICT and QS_FUN_DICT to extract the information -# about the addresses of the Philosopher objects and the states of their -# state machines. -# - -class DPP: - def __init__(self): - - # add commands to the Custom menu... - QView.custom_menu.add_command(label="Custom command", - command=self.cust_command) - - # configure the custom QView.canvas... - QView.show_canvas() # make the canvas visible - QView.canvas.configure(width=400, height=260) - - # tuple of activity images (correspond to self._philo_state) - self._act_img = ( - PhotoImage(file=HOME_DIR + "/img/thinking.gif"), - PhotoImage(file=HOME_DIR + "/img/hungry.gif"), - PhotoImage(file=HOME_DIR + "/img/eating.gif"), - ) - # tuple of philo canvas images (correspond to self._philo_obj) - self._philo_img = (\ - QView.canvas.create_image(190, 57, image=self._act_img[0]), - QView.canvas.create_image(273, 100, image=self._act_img[0]), - QView.canvas.create_image(237, 185, image=self._act_img[0]), - QView.canvas.create_image(146, 185, image=self._act_img[0]), - QView.canvas.create_image(107, 100, image=self._act_img[0]) - ) - - # button images for UP and DOWN - self.img_UP = PhotoImage(file=HOME_DIR + "/img/BTN_UP.gif") - self.img_DWN = PhotoImage(file=HOME_DIR + "/img/BTN_DWN.gif") - - # images of a button for pause/serve - self.btn = QView.canvas.create_image(200, 120, image=self.img_UP) - QView.canvas.tag_bind(self.btn, "", self.cust_pause) - - # request target reset on startup... - # NOTE: Normally, for an embedded application you would like - # to start with resetting the Target, to start clean with - # Qs dictionaries, etc. - reset_target() - - # on_reset() callback invoked when Target-reset packet is received - # NOTE: the QS dictionaries are not known at this time yet, so - # this callback shouild generally not set filters or current objects - def on_reset(self): - # (re)set the lists - self._philo_obj = [0, 0, 0, 0, 0] - self._philo_state = [0, 0, 0] - - # on_run() callback invoked when the QF_RUN packet is received - # NOTE: the QS dictionaries are typically known at this time yet, so - # this callback can set filters or current objects - def on_run(self): - glb_filter("QS_QEP_TRAN") - - # NOTE: the name of object for current_obj() must match the - # QS Object Dictionaries produced by the application. - current_obj(OBJ_AO, "Table::inst") - - # turn lists into tuples for better performance - self._philo_obj = tuple(self._philo_obj) - self._philo_state = tuple(self._philo_state) - - - # example of a custom command - def cust_command(self): - command(1, 12345) - - # example of a custom interaction with a canvas object (pause/serve) - def cust_pause(self, event): - if QView.canvas.itemcget(self.btn, "image") != str(self.img_UP): - QView.canvas.itemconfig(self.btn, image=self.img_UP) - post("SERVE_SIG") - QView.print_text("Table SERVING") - else: - QView.canvas.itemconfig(self.btn, image=self.img_DWN) - post("PAUSE_SIG") - QView.print_text("Table PAUSED") - - # intercept the QS_OBJ_DICT stadard packet - # this packet has the following structure: - # record-ID, seq-num, Object-ptr, Zero-terminated string - def QS_OBJ_DICT(self, packet): - data = qunpack("xxOZ", packet) - try: - # NOTE: the names of objects must match the QS Object Dictionaries - # produced by the application. - i = ("Philo::inst[0]", - "Philo::inst[1]", - "Philo::inst[2]", - "Philo::inst[3]", - "Philo::inst[4]").index(data[1]) - self._philo_obj[i] = data[0] - except: - pass # dictionary for a different object - - # intercept the QS_FUN_DICT stadard packet - # this packet has the following structure: - # record-ID, seq-num, Function-ptr, Zero-terminated string - def QS_FUN_DICT(self, packet): - data = qunpack("xxFZ", packet) - try: - # NOTE: the names of states must match the QS Object Dictionaries - # produced by the application. - j = ("Philo::thinking", - "Philo::hungry", - "Philo::eating").index(data[1]) - self._philo_state[j] = data[0] - except: - pass # dictionary for a different state - - # intercept the QS_QEP_TRAN stadard packet - # this packet has the following structure: - # record-ID, seq-num, Timestamp, Signal, Object-ptr, - # Function-ptr (source state), Function-ptr (new active state) - def QS_QEP_TRAN(self, packet): - data = qunpack("xxTSOFF", packet) - try: - i = self._philo_obj.index(data[2]) - j = self._philo_state.index(data[4]) - - # animate the given philo image according to its activity - QView.canvas.itemconfig(self._philo_img[i], - image=self._act_img[j]) - # print a message to the text view - QView.print_text("%010d Philo %d is %s"\ - %(data[0], i, ("thinking", "hungry", "eating")[j])) - except: - pass # state-entry in a different object - -#============================================================================= -# instantiate the DPP class and set it as the QView customization -QView.customize(DPP()) diff --git a/examples/dpp_bsp-esp32/qview/img/BTN_DWN.gif b/examples/dpp_bsp-esp32/qview/img/BTN_DWN.gif deleted file mode 100644 index 36d8c79..0000000 Binary files a/examples/dpp_bsp-esp32/qview/img/BTN_DWN.gif and /dev/null differ diff --git a/examples/dpp_bsp-esp32/qview/img/BTN_UP.gif b/examples/dpp_bsp-esp32/qview/img/BTN_UP.gif deleted file mode 100644 index 3246ff9..0000000 Binary files a/examples/dpp_bsp-esp32/qview/img/BTN_UP.gif and /dev/null differ diff --git a/examples/dpp_bsp-esp32/qview/img/eating.gif b/examples/dpp_bsp-esp32/qview/img/eating.gif deleted file mode 100644 index 632b9d2..0000000 Binary files a/examples/dpp_bsp-esp32/qview/img/eating.gif and /dev/null differ diff --git a/examples/dpp_bsp-esp32/qview/img/hungry.gif b/examples/dpp_bsp-esp32/qview/img/hungry.gif deleted file mode 100644 index dc144db..0000000 Binary files a/examples/dpp_bsp-esp32/qview/img/hungry.gif and /dev/null differ diff --git a/examples/dpp_bsp-esp32/qview/img/thinking.gif b/examples/dpp_bsp-esp32/qview/img/thinking.gif deleted file mode 100644 index ec684ee..0000000 Binary files a/examples/dpp_bsp-esp32/qview/img/thinking.gif and /dev/null differ diff --git a/examples/dpp_bsp-esp32/qview/qspy.bat b/examples/dpp_bsp-esp32/qview/qspy.bat deleted file mode 100644 index 624af68..0000000 --- a/examples/dpp_bsp-esp32/qview/qspy.bat +++ /dev/null @@ -1,5 +0,0 @@ -@setlocal - -qspy.exe -c COM3 -b 115200 - -@endlocal diff --git a/examples/dpp_bsp-esp32/qview/qview-dpp.bat b/examples/dpp_bsp-esp32/qview/qview-dpp.bat deleted file mode 100644 index 70e6717..0000000 --- a/examples/dpp_bsp-esp32/qview/qview-dpp.bat +++ /dev/null @@ -1,8 +0,0 @@ -@setlocal - -if "%QTOOLS%"=="" ( - set QTOOLS=C:\qp\qtools -) -python %QTOOLS%\qview\qview.py dpp.py - -@endlocal diff --git a/examples/dpp_bsp-esp32/qview/qview-dpp.lnk b/examples/dpp_bsp-esp32/qview/qview-dpp.lnk deleted file mode 100644 index 5cb99eb..0000000 Binary files a/examples/dpp_bsp-esp32/qview/qview-dpp.lnk and /dev/null differ diff --git a/examples/dpp_bsp-esp32/qview/qview-dpp1.bat b/examples/dpp_bsp-esp32/qview/qview-dpp1.bat deleted file mode 100644 index a15f585..0000000 --- a/examples/dpp_bsp-esp32/qview/qview-dpp1.bat +++ /dev/null @@ -1,8 +0,0 @@ -@setlocal - -if "%QTOOLS%"=="" ( - set QTOOLS=C:\qp\qtools -) -python %QTOOLS%\qview\qview.py dpp1.py - -@endlocal diff --git a/examples/dpp_bsp-esp32/qview/qview-dpp1.lnk b/examples/dpp_bsp-esp32/qview/qview-dpp1.lnk deleted file mode 100644 index 41e1e3d..0000000 Binary files a/examples/dpp_bsp-esp32/qview/qview-dpp1.lnk and /dev/null differ diff --git a/examples/dpp_bsp-esp32/table.cpp b/examples/dpp_bsp-esp32/table.cpp index 9fd72fe..d254443 100644 --- a/examples/dpp_bsp-esp32/table.cpp +++ b/examples/dpp_bsp-esp32/table.cpp @@ -1,21 +1,40 @@ -//.$file${.::table.cpp} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv +//$file${.::table.cpp} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv // // Model: dpp_bsp-esp32.qm // File: ${.::table.cpp} // -// This code has been generated by QM 5.1.1 . +// This code has been generated by QM 7.0.1 . // 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. +// Copyright (c) 2005 Quantum Leaps, LLC. All rights reserved. // -// 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. +// ____________________________________ +// / / +// / GGGGGGG PPPPPPPP LL / +// / GG GG PP PP LL / +// / GG PP PP LL / +// / GG GGGGG PPPPPPPP LL / +// / GG GG PP LL / +// / GGGGGGG PP LLLLLLL / +// /___________________________________/ // -//.$endhead${.::table.cpp} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +// SPDX-License-Identifier: GPL-3.0-or-later +// +// This generated code is open-source software licensed under the GNU +// General Public License (GPL) as published by the Free Software Foundation +// (see ). +// +// NOTE: +// The GPL does NOT permit the incorporation of this code into proprietary +// programs. Please contact Quantum Leaps for commercial licensing options, +// which expressly supersede the GPL and are designed explicitly for +// closed-source distribution. +// +// Quantum Leaps contact information: +// +// +// +//$endhead${.::table.cpp} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ #include "qpcpp.hpp" // QP-C++ framework #include "dpp.hpp" // DPP application #include "bsp.hpp" // Board Support Package @@ -23,8 +42,9 @@ Q_DEFINE_THIS_FILE // generate declaration of the active object --------------------------------- -//.$declare${AOs::Table} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv -//.${AOs::Table} ............................................................. +//$declare${AOs::Table} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv + +//${AOs::Table} .............................................................. class Table : public QP::QActive { private: uint8_t m_fork[N_PHILO]; @@ -41,20 +61,21 @@ class Table : public QP::QActive { Q_STATE_DECL(active); Q_STATE_DECL(serving); Q_STATE_DECL(paused); -}; -//.$enddecl${AOs::Table} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +}; // class Table +//$enddecl${AOs::Table} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ // generate definition of the opaque pointer to the AO ----------------------- -//.$skip${QP_VERSION} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv -//. Check for the minimum required QP version -#if (QP_VERSION < 690U) || (QP_VERSION != ((QP_RELEASE^4294967295U) % 0x3E8U)) -#error qpcpp version 6.9.0 or higher required +//$skip${QP_VERSION} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv +// Check for the minimum required QP version +#if (QP_VERSION < 730U) || (QP_VERSION != ((QP_RELEASE^4294967295U)%0x2710U)) +#error qpcpp version 7.3.0 or higher required #endif -//.$endskip${QP_VERSION} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -//.$define${AOs::AO_Table} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv -//.${AOs::AO_Table} .......................................................... +//$endskip${QP_VERSION} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +//$define${AOs::AO_Table} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv + +//${AOs::AO_Table} ........................................................... QP::QActive * const AO_Table = &Table::inst; // "opaque" AO pointer -//.$enddef${AOs::AO_Table} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +//$enddef${AOs::AO_Table} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ // helper function to provide the RIGHT neighbour of a Philo[n] inline uint8_t RIGHT(uint8_t const n) { @@ -75,10 +96,12 @@ static char const * const EATING = "eating "; // generate definition of the AO --------------------------------------------- -//.$define${AOs::Table} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv -//.${AOs::Table} ............................................................. +//$define${AOs::Table} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv + +//${AOs::Table} .............................................................. Table Table::inst; -//.${AOs::Table::Table} ...................................................... + +//${AOs::Table::Table} ....................................................... Table::Table() : QActive(Q_STATE_CAST(&Table::initial)) { @@ -88,9 +111,9 @@ Table::Table() } } -//.${AOs::Table::SM} ......................................................... +//${AOs::Table::SM} .......................................................... Q_STATE_DEF(Table, initial) { - //.${AOs::Table::SM::initial} + //${AOs::Table::SM::initial} (void)e; // unused parameter subscribe(DONE_SIG); @@ -115,16 +138,17 @@ Q_STATE_DEF(Table, initial) { QS_SIG_DICTIONARY(HUNGRY_SIG, nullptr); return tran(&serving); } -//.${AOs::Table::SM::active} ................................................. + +//${AOs::Table::SM::active} .................................................. Q_STATE_DEF(Table, active) { QP::QState status_; switch (e->sig) { - //.${AOs::Table::SM::active::TEST} + //${AOs::Table::SM::active::TEST} case TEST_SIG: { status_ = Q_RET_HANDLED; break; } - //.${AOs::Table::SM::active::EAT} + //${AOs::Table::SM::active::EAT} case EAT_SIG: { Q_ERROR(); status_ = Q_RET_HANDLED; @@ -137,11 +161,12 @@ Q_STATE_DEF(Table, active) { } return status_; } -//.${AOs::Table::SM::active::serving} ........................................ + +//${AOs::Table::SM::active::serving} ......................................... Q_STATE_DEF(Table, serving) { QP::QState status_; switch (e->sig) { - //.${AOs::Table::SM::active::serving} + //${AOs::Table::SM::active::serving} case Q_ENTRY_SIG: { for (uint8_t n = 0U; n < N_PHILO; ++n) { // give permissions to eat... if (m_isHungry[n] @@ -160,7 +185,7 @@ Q_STATE_DEF(Table, serving) { status_ = Q_RET_HANDLED; break; } - //.${AOs::Table::SM::active::serving::HUNGRY} + //${AOs::Table::SM::active::serving::HUNGRY} case HUNGRY_SIG: { uint8_t n = Q_EVT_CAST(TableEvt)->philoNum; // phil ID must be in range and he must be not hungry @@ -168,7 +193,7 @@ Q_STATE_DEF(Table, serving) { BSP::displayPhilStat(n, HUNGRY); uint8_t m = LEFT(n); - //.${AOs::Table::SM::active::serving::HUNGRY::[bothfree]} + //${AOs::Table::SM::active::serving::HUNGRY::[bothfree]} if ((m_fork[m] == FREE) && (m_fork[n] == FREE)) { m_fork[m] = USED; m_fork[n] = USED; @@ -178,14 +203,14 @@ Q_STATE_DEF(Table, serving) { BSP::displayPhilStat(n, EATING); status_ = Q_RET_HANDLED; } - //.${AOs::Table::SM::active::serving::HUNGRY::[else]} + //${AOs::Table::SM::active::serving::HUNGRY::[else]} else { m_isHungry[n] = true; status_ = Q_RET_HANDLED; } break; } - //.${AOs::Table::SM::active::serving::DONE} + //${AOs::Table::SM::active::serving::DONE} case DONE_SIG: { uint8_t n = Q_EVT_CAST(TableEvt)->philoNum; // phil ID must be in range and he must be not hungry @@ -223,13 +248,13 @@ Q_STATE_DEF(Table, serving) { status_ = Q_RET_HANDLED; break; } - //.${AOs::Table::SM::active::serving::EAT} + //${AOs::Table::SM::active::serving::EAT} case EAT_SIG: { Q_ERROR(); status_ = Q_RET_HANDLED; break; } - //.${AOs::Table::SM::active::serving::PAUSE} + //${AOs::Table::SM::active::serving::PAUSE} case PAUSE_SIG: { status_ = tran(&paused); break; @@ -241,28 +266,29 @@ Q_STATE_DEF(Table, serving) { } return status_; } -//.${AOs::Table::SM::active::paused} ......................................... + +//${AOs::Table::SM::active::paused} .......................................... Q_STATE_DEF(Table, paused) { QP::QState status_; switch (e->sig) { - //.${AOs::Table::SM::active::paused} + //${AOs::Table::SM::active::paused} case Q_ENTRY_SIG: { BSP::displayPaused(1U); status_ = Q_RET_HANDLED; break; } - //.${AOs::Table::SM::active::paused} + //${AOs::Table::SM::active::paused} case Q_EXIT_SIG: { BSP::displayPaused(0U); status_ = Q_RET_HANDLED; break; } - //.${AOs::Table::SM::active::paused::SERVE} + //${AOs::Table::SM::active::paused::SERVE} case SERVE_SIG: { status_ = tran(&serving); break; } - //.${AOs::Table::SM::active::paused::HUNGRY} + //${AOs::Table::SM::active::paused::HUNGRY} case HUNGRY_SIG: { uint8_t n = Q_EVT_CAST(TableEvt)->philoNum; // philo ID must be in range and he must be not hungry @@ -272,7 +298,7 @@ Q_STATE_DEF(Table, paused) { status_ = Q_RET_HANDLED; break; } - //.${AOs::Table::SM::active::paused::DONE} + //${AOs::Table::SM::active::paused::DONE} case DONE_SIG: { uint8_t n = Q_EVT_CAST(TableEvt)->philoNum; // phil ID must be in range and he must be not hungry @@ -295,4 +321,5 @@ Q_STATE_DEF(Table, paused) { } return status_; } -//.$enddef${AOs::Table} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +//$enddef${AOs::Table} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + diff --git a/library.properties b/library.properties index 6662f06..aea7446 100644 --- a/library.properties +++ b/library.properties @@ -1,9 +1,9 @@ -name=QPESP32 -version=0.2.1 -author=Victor Chavez -maintainer=Victor Chavez +name=QPCPP_ESP32 +version=8.0.2 +author=NguyenDB +maintainer=NguyenDB sentence=QP/C++ Port for ESP32. -paragraph=QP/C++ Port for the ESP32. Based on v7.2.2 from QP/C++ +paragraph=QP/C++ Port for the ESP32. Based on v8.0.2 from QP/C++ category=Device Control url=https://www.state-machine.com/arduino/ architectures=* diff --git a/src/qassert.h b/src/qassert.h deleted file mode 100644 index 43aafec..0000000 --- a/src/qassert.h +++ /dev/null @@ -1,395 +0,0 @@ -/*$file${include::qassert.h} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv*/ -/* -* Model: qpc.qm -* File: ${include::qassert.h} -* -* This code has been generated by QM 5.2.5 . -* DO NOT EDIT THIS FILE MANUALLY. All your changes will be lost. -* -* This code is covered by the following QP license: -* License # : LicenseRef-QL-dual -* Issued to : Any user of the QP/C real-time embedded framework -* Framework(s) : qpc -* Support ends : 2023-12-31 -* License scope: -* -* Copyright (C) 2005 Quantum Leaps, LLC . -* -* SPDX-License-Identifier: GPL-3.0-or-later OR LicenseRef-QL-commercial -* -* This software is dual-licensed under the terms of the open source GNU -* General Public License version 3 (or any later version), or alternatively, -* under the terms of one of the closed source Quantum Leaps commercial -* licenses. -* -* The terms of the open source GNU General Public License version 3 -* can be found at: -* -* The terms of the closed source Quantum Leaps commercial licenses -* can be found at: -* -* Redistributions in source code must retain this top-level comment block. -* Plagiarizing this software to sidestep the license obligations is illegal. -* -* Contact information: -* -* -*/ -/*$endhead${include::qassert.h} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ -/*! @file -* @brief Customizable and memory-efficient Design by Contract (DbC) -* for embedded systems -* -* @note -* This header file can be used in C, C++, and mixed C/C++ programs. -* -* @note -* The preprocessor switch #Q_NASSERT disables checking assertions. -* However, it is generally **not** advisable to disable assertions, -* **especially** in the production code. Instead, the assertion -* handler Q_onAssert() should be very carefully designed and tested. -*/ -#ifndef QASSERT_H_ -#define QASSERT_H_ - -#ifdef __cplusplus - extern "C" { -#endif - -#ifndef Q_NASSERT -/*$declare${DbC::active} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv*/ - -/*${DbC::active::Q_DEFINE_THIS_MODULE} .....................................*/ -/*! Define the user-specified module name for assertions in this file. -* -* @details -* Macro to be placed at the top of each C/C++ module to define the -* single instance of the module name string to be used in reporting -* assertions in this module. This macro takes the user-supplied parameter -* `name_` instead of `__FILE__` to precisely control the name of the -* module. -* -* @param[in] name_ string constant representing the module name -* -* @note -* This macro should **not** be terminated by a semicolon. -*/ -#define Q_DEFINE_THIS_MODULE(name_) \ - static char const Q_this_module_[] = name_; - -/*${DbC::active::Q_ASSERT_ID} ..............................................*/ -/*! General-purpose assertion with user-specified ID number. -* -* @details -* Evaluates the Boolean expression `expr_` and does nothing else when -* it evaluates to 'true'. However, when `expr_` evaluates to 'false', -* the Q_ASSERT_ID() macro calls the no-return function Q_onAssert(). -* -* @param[in] id_ ID number (unique within the module) of the assertion -* @param[in] expr_ Boolean expression to check -* -* @attention -* When assertions are disabled (by defining the ::Q_NASSERT macro), the -* Q_ASSERT_ID() macro expands to nothing, and consequently the Boolean -* expression `expr_` is **not** evaluated and the callback function -* Q_onAssert() is **not** called. -*/ -#define Q_ASSERT_ID(id_, expr_) ((expr_) \ - ? ((void)0) : Q_onAssert(&Q_this_module_[0], (id_))) - -/*${DbC::active::Q_ERROR_ID} ...............................................*/ -/*! Assertion with user-specified ID for a wrong path through the code -* -* @details -* Calls the Q_onAssert() callback if ever executed. This assertion -* takes the user-supplied parameter `id_` to identify the location of -* this assertion within the file. This avoids the volatility of using -* line numbers, which change whenever a line of code is added or removed -* upstream from the assertion. -* -* @param[in] id_ ID number (unique within the module) of the assertion -* -* @note -* Does noting if assertions are disabled with the ::Q_NASSERT switch. -*/ -#define Q_ERROR_ID(id_) Q_onAssert(&Q_this_module_[0], (id_)) - -/*${DbC::active::Q_ALLEGE_ID} ..............................................*/ -/*! General purpose assertion with user-specified ID number that -* **always** evaluates the `expr_` expression. -* -* @details -* Like the Q_ASSERT_ID() macro, except it **always** evaluates the -* `expr_` expression even when assertions are disabled with the -* ::Q_NASSERT macro. However, when the ::Q_NASSERT macro is defined, the -* Q_onAssert() callback is **not** called, even if `expr_` evaluates -* to FALSE. -* -* @param[in] id_ ID number (unique within the module) of the assertion -* @param[in] expr_ Boolean expression to check -*/ -#define Q_ALLEGE_ID(id_, expr_) Q_ASSERT_ID((id_), (expr_)) -/*$enddecl${DbC::active} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ -#else -/*$declare${DbC::inactive} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv*/ - -/*${DbC::inactive::Q_DEFINE_THIS_MODULE} ...................................*/ -/*! inactive version of Q_DEFINE_THIS_MODULE() */ -#define Q_DEFINE_THIS_MODULE(name_) - -/*${DbC::inactive::Q_ASSERT_ID} ............................................*/ -/*! inactive version of Q_ASSERT_ID() */ -#define Q_ASSERT_ID(id_, expr_) ((void)0) - -/*${DbC::inactive::Q_ERROR_ID} .............................................*/ -/*! inactive version of Q_ERROR_ID() */ -#define Q_ERROR_ID(id_) ((void)0) - -/*${DbC::inactive::Q_ALLEGE_ID} ............................................*/ -/*! inactive version of Q_ALLEGE_ID() -* -* @attention -* The expression `expr_` **is** evaluated, even though assertion -* callback Q_onAssert() is NOT called when `expr_` evaluates to -* false. -*/ -#define Q_ALLEGE_ID(id_, expr_) ((void)(expr_)) -/*$enddecl${DbC::inactive} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ -#endif - -/*$declare1${DbC} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv*/ - -/*${DbC::Q_DEFINE_THIS_FILE} ...............................................*/ -/*! Define the file name (with `__FILE__`) for assertions in this file -* -* @details -* Macro to be placed at the top of each C/C++ module to define the -* single instance of the file name string to be used in reporting -* assertions in this module. -* -* @note -* The file name string literal is defined by means of the standard -* preprocessor macro `__FILE__`. However, please note that, depending -* on the compiler, the `__FILE__` macro might contain the whole path name -* to the file, which might be inconvenient to log assertions. -* -* @attention -* This macro should **not** be terminated by a semicolon. -* -* @sa Q_DEFINE_THIS_MODULE() -*/ -#define Q_DEFINE_THIS_FILE Q_DEFINE_THIS_MODULE(__FILE__) - -/*${DbC::Q_ASSERT} .........................................................*/ -/*! General-purpose assertion (with __LINE__ used as location in the file) -* -* @details -* Equivalent to Q_ASSERT_ID(), except it uses __LINE__ to identify the -* assertion within a file. -* -* @param[in] expr_ Boolean expression to check -* -* @sa Q_ASSERT_ID() -*/ -#define Q_ASSERT(expr_) Q_ASSERT_ID(__LINE__, (expr_)) - -/*${DbC::Q_ERROR} ..........................................................*/ -/*! Assertion for a wrong path through the code -* -* @details -* Calls the Q_onAssert() callback if ever executed. -* -* @note -* This macro identifies the problem location with the line number, -* which might change as the code is modified. -* -* @sa Q_ERROR_ID() -*/ -#define Q_ERROR() Q_ERROR_ID(__LINE__) - -/*${DbC::Q_REQUIRE_ID} .....................................................*/ -/*! Assertion for checking **preconditions**. -* -* @details -* Equivalent to Q_ASSERT_ID(), except the name provides a better -* documentation of the intention of this assertion. -* -* @param[in] id_ ID number (unique within the module) of the assertion -* @param[in] expr_ Boolean expression -*/ -#define Q_REQUIRE_ID(id_, expr_) Q_ASSERT_ID((id_), (expr_)) - -/*${DbC::Q_REQUIRE} ........................................................*/ -/*! Assertion for checking preconditions (based on __LINE__). -* -* @details -* Equivalent to Q_ASSERT(), except the name provides a better documentation -* of the intention of this assertion. -* -* @param[in] expr_ Boolean expression -*/ -#define Q_REQUIRE(expr_) Q_ASSERT(expr_) - -/*${DbC::Q_ENSURE_ID} ......................................................*/ -/*! Assertion for checking postconditions. -* -* @details -* Equivalent to Q_ASSERT_ID(), except the name provides a better documentation -* of the intention of this assertion. -* -* @param[in] id_ ID number (unique within the module) of the assertion -* @param[in] expr_ Boolean expression -*/ -#define Q_ENSURE_ID(id_, expr_) Q_ASSERT_ID((id_), (expr_)) - -/*${DbC::Q_ENSURE} .........................................................*/ -/*! Assertion for checking postconditions. -* -* @details -* Equivalent to Q_ASSERT(), except the name provides a better documentation -* of the intention of this assertion. -* -* @param[in] expr_ Boolean expression -*/ -#define Q_ENSURE(expr_) Q_ASSERT(expr_) - -/*${DbC::Q_INVARIANT_ID} ...................................................*/ -/*! Assertion for checking invariants. -* -* @details -* Equivalent to Q_ASSERT_ID(), except the name provides a better -* documentation of the intention of this assertion. -* -* @param[in] id_ ID number (unique within the module) of the assertion -* @param[in] expr_ Boolean expression -*/ -#define Q_INVARIANT_ID(id_, expr_) Q_ASSERT_ID((id_), (expr_)) - -/*${DbC::Q_INVARIANT} ......................................................*/ -/*! Assertion for checking invariants. -* -* @details -* Equivalent to Q_ASSERT(), except the name provides a better documentation -* of the intention of this assertion. -* -* @param[in] expr_ Boolean expression -*/ -#define Q_INVARIANT(expr_) Q_ASSERT(expr_) - -/*${DbC::Q_ALLEGE} .........................................................*/ -/*! General purpose assertion with user-specified ID number that -* **always** evaluates the `expr_` expression. -* -* @details -* Equivalent to Q_ALLEGE_ID(), except it identifies the problem location -* with the line number `__LINE__`, which might change as the code is modified. -* -* @param[in] expr_ Boolean expression to check -* -* @sa Q_ALLEGE_ID() -*/ -#define Q_ALLEGE(expr_) Q_ALLEGE_ID(__LINE__, (expr_)) - -/*${DbC::Q_ASSERT_STATIC} ..................................................*/ -/*! Static (compile-time) assertion. -* -* @details -* This type of assertion deliberately causes a compile-time error when -* the `expr_` Boolean expression evaluates to FALSE. The macro exploits -* the fact that in C/C++ a dimension of an array cannot be negative. -* The compile-time assertion has no runtime side effects. -* -* @param[in] expr_ Compile-time Boolean expression -* -* @note -* The static assertion macro is provided for backwards compatibility with -* older C standards. Newer C11 supports `_Static_assert()`, which should -* be used instead of Q_ASSERT_STATIC(). -*/ -#define Q_ASSERT_STATIC(expr_) extern char Q_static_assert_[(expr_) ? 1 : -1] - -/*${DbC::Q_NORETURN} .......................................................*/ -#ifndef Q_NORETURN -/*! No-return function specifier for the Q_onAssert() callback function. -* -* @details -* If the `Q_NORETURN` macro is undefined, the default definition uses -* the C99 specifier `_Noreturn`. -* -* @note -* The `Q_NORETURN` macro can be defined in the QP port (typically in -* `qep_port.h` or `qep_port.hpp`). If such definition is porvided -* the default won't be used. -* -* @trace -* @tr{PQA01_4} -*/ -#define Q_NORETURN _Noreturn void -#endif /* ndef Q_NORETURN */ - -/*${DbC::int_t} ............................................................*/ -#ifndef QP_VERSION -/*! typedef for assertions-ids and line numbers in assertions. -* -* @details -* This typedef specifies integer type for exclusive use in assertions. -* Use of this type, rather than plain 'int', is in compliance -* with the MISRA-C 2012 Dir 4.6 (adv). -*/ -typedef int int_t; -#endif /* ndef QP_VERSION */ - -/*${DbC::Q_onAssert} .......................................................*/ -/*! Callback function invoked in case of an assertion failure. -* -* @details -* This callback function needs to be defined in the application to perform -* any corrective action after a non-recoverable error has been detected. -* The Q_onAssert() function is the last line of defense after the -* system failure and its implementation shouild be very **carefully** -* designed and **tested** under various fault conditions, including but -* not limited to: stack overflow, stack corruption, or calling Q_onAssert() -* from an interrupt. -* -* @param[in] module name of the file/module in which the assertion failed -* (constant, zero-terminated C string) -* @param[in] location location of the assertion within the module. This could -* be a line number or a user-specified ID-number. -* -* @returns -* This callback function should **not return** (see ::Q_NORETURN), -* as continuation after an assertion failure does not make sense. -* -* @note -* During debugging, Q_onAssert() is an ideal place to put a breakpoint. -* For deployment, tt is typically a **bad idea** to implement Q_onAssert() -* as an endless loop that ties up the CPU (denial of service). -* -* Called by the following: Q_ASSERT_ID(), Q_ERROR_ID(), Q_REQUIRE_ID(), -* Q_ENSURE_ID(), Q_INVARIANT_ID() and Q_ALLEGE_ID() as well as: -* Q_ASSERT(), Q_ERROR(), Q_REQUIRE(), Q_ENSURE(), Q_INVARIANT(), -* and Q_ALLEGE(). -* -* @trace -* @tr{PQA01_4} -*/ -Q_NORETURN Q_onAssert( - char const * module, - int_t location); - -/*${DbC::Q_DIM} ............................................................*/ -#ifndef QP_VERSION -/*! Helper macro to calculate static dimension of a 1-dim `array_` -* -* @param array_ 1-dimensional array -* @returns the length of the array (number of elements it can hold) -*/ -#define Q_DIM(array_) (sizeof(array_) / sizeof((array_)[0U])) -#endif /* ndef QP_VERSION */ -/*$enddecl${DbC} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ - -#ifdef __cplusplus - } -#endif - -#endif /* QASSERT_H_ */ diff --git a/src/qep.hpp b/src/qep.hpp deleted file mode 100644 index e578321..0000000 --- a/src/qep.hpp +++ /dev/null @@ -1,982 +0,0 @@ -//$file${include::qep.hpp} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv -// -// Model: qpcpp.qm -// File: ${include::qep.hpp} -// -// This code has been generated by QM 5.2.5 . -// DO NOT EDIT THIS FILE MANUALLY. All your changes will be lost. -// -// This code is covered by the following QP license: -// License # : LicenseRef-QL-dual -// Issued to : Any user of the QP/C++ real-time embedded framework -// Framework(s) : qpcpp -// Support ends : 2023-12-31 -// License scope: -// -// Copyright (C) 2005 Quantum Leaps, LLC . -// -// SPDX-License-Identifier: GPL-3.0-or-later OR LicenseRef-QL-commercial -// -// This software is dual-licensed under the terms of the open source GNU -// General Public License version 3 (or any later version), or alternatively, -// under the terms of one of the closed source Quantum Leaps commercial -// licenses. -// -// The terms of the open source GNU General Public License version 3 -// can be found at: -// -// The terms of the closed source Quantum Leaps commercial licenses -// can be found at: -// -// Redistributions in source code must retain this top-level comment block. -// Plagiarizing this software to sidestep the license obligations is illegal. -// -// Contact information: -// -// -// -//$endhead${include::qep.hpp} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -//! @file -//! @brief QEP/C++ platform-independent public interface. -//! -//! @tr{RQP001} @tr{RQP101} - -#ifndef QEP_HPP_ -#define QEP_HPP_ - -//============================================================================ -//! The current QP version as an unsigned number -// -// @details -// ::QP_VERSION is a decimal constant, where XX is a 1-digit or 2-digit -// major version number, Y is a 1-digit minor version number, and Z is -// a 1-digit release number. -// -#define QP_VERSION 722U - -//! The current QP version as a zero terminated string literal. -// -// @details -// ::QP_VERSION_STR is of the form "XX.Y.Z", where XX is a 1-or 2-digit -// major version number, Y is a 1-digit minor version number, and Z is -// a 1-digit release number. -// -#define QP_VERSION_STR "7.2.2" - -//! Encrypted current QP release (7.2.2) and date (2023-03-01) -#define QP_RELEASE 0x76BAD85DU - -//============================================================================ -// Global namespace... -//$declare${glob-types} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv - -//${glob-types::int_t} ....................................................... -//! alias for line numbers in assertions and return from QF::run() -using int_t = int; - -//${glob-types::enum_t} ...................................................... -//! alias for enumerations used for event signals -using enum_t = int; - -//${glob-types::float32_t} ................................................... -//! alias for 32-bit IEEE 754 floating point numbers -//! -//! @note -//! QP does not use floating-point types anywhere in the internal -//! implementation, except in QS software tracing, where utilities for -//! output of floating-point numbers are provided for application-specific -//! trace records. -using float32_t = float; - -//${glob-types::float64_t} ................................................... -//! alias for 64-bit IEEE 754 floating point numbers -//! -//! @note -//! QP does not use floating-point types anywhere in the internal -//! implementation, except in QS software tracing, where utilities for -//! output of floating-point numbers are provided for application-specific -//! trace records. -using float64_t = double; -//$enddecl${glob-types} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -//$declare${QEP-config} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv - -//${QEP-config::Q_SIGNAL_SIZE} ............................................... -#ifndef Q_SIGNAL_SIZE -//! The size (in bytes) of the signal of an event. Valid values: -//! 1U, 2U, or 4U; default 2U -//! -//! @details -//! This macro can be defined in the QEP port file (qep_port.hpp) to -//! configure the QP::QSignal type. When the macro is not defined, the -//! default of 2 bytes is applied. -#define Q_SIGNAL_SIZE 2U -#endif // ndef Q_SIGNAL_SIZE -//$enddecl${QEP-config} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -//============================================================================ -//$declare${QEP} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv -namespace QP { - -//${QEP::versionStr[]} ....................................................... -//! the current QP version number string based on QP_VERSION_STR -constexpr char const versionStr[] {QP_VERSION_STR}; - -//${QEP::QSignal} ............................................................ -#if (Q_SIGNAL_SIZE == 2U) -//! QSignal represents the signal of an event -//! -//! @details -//! The relationship between an event and a signal is as follows. A signal -//! in UML is the specification of an asynchronous stimulus that triggers -//! reactions, and as such is an essential part of an event. (The signal -//! conveys the type of the occurrence--what happened?) However, an event -//! can also contain additional quantitative information about the -//! occurrence in form of event parameters. -using QSignal = std::uint16_t; -#endif // (Q_SIGNAL_SIZE == 2U) - -//${QEP::QSignal} ............................................................ -#if (Q_SIGNAL_SIZE == 1U) -using QSignal = std::uint8_t; -#endif // (Q_SIGNAL_SIZE == 1U) - -//${QEP::QSignal} ............................................................ -#if (Q_SIGNAL_SIZE == 4U) -using QSignal = std::uint32_t; -#endif // (Q_SIGNAL_SIZE == 4U) - -//${QEP::QEvt} ............................................................... -//! Event class -//! -//! @details -//! QP::QEvt represents events without parameters and serves as the -//! base class for derivation of events with parameters. -//! -//! @note -//! When #Q_EVT_CTOR and #Q_EVT_VIRTUAL are NOT defined, the QP::QEvt is -//! a POD (Plain Old Data). Otherwise, it is a class with constructors -//! and virtual destructor. -//! -//! @usage -//! The following example illustrates how to add an event parameter by -//! inheriting from the QP::QEvt class. -//! @include qep_qevt.cpp -class QEvt { -public: - - //! signal of the event instance - //! @tr{RQP002} - QSignal sig; - - //! pool ID (0 for static, immutable event) - //! @tr{RQP003} - std::uint8_t poolId_; - - //! reference counter (only used for dynamic, mutable events) - //! @tr{RQP003} - std::uint8_t volatile refCtr_; - -public: - -#ifdef Q_EVT_CTOR - //! QP::QEvt constructor when the macro #Q_EVT_CTOR is defined - explicit QEvt(QSignal s) noexcept - : sig(s) - // poolId_/refCtr_ intentionally uninitialized - {} -#endif // def Q_EVT_CTOR - -#ifdef Q_EVT_CTOR - //! QP::QEvt constructor (overload for static, immutable events) - constexpr QEvt( - QSignal s, - std::uint8_t /* dummy */) noexcept - : sig(s), - poolId_(0U), - refCtr_(0U) - {} -#endif // def Q_EVT_CTOR - -#ifdef Q_EVT_VIRTUAL - //! QP::QEvt virtual destructor when the macro #Q_EVT_VIRTUAL is defined - virtual ~QEvt() noexcept { - // empty - } -#endif // def Q_EVT_VIRTUAL -}; // class QEvt - -//${QEP::QState} ............................................................. -//! Type returned from state-handler functions -using QState = std::uint_fast8_t; - -//${QEP::QStateHandler} ...................................................... -//! Pointer to state-handler function -using QStateHandler = QState (*)(void * const me, QEvt const * const e); - -//${QEP::QActionHandler} ..................................................... -//! Pointer to an action-handler function -using QActionHandler = QState (*)(void * const me); - -//${QEP::QXThread} ........................................................... -//! forward declaration -class QXThread; - -//${QEP::QXThreadHandler} .................................................... -//! Pointer to an extended thread-handler function -using QXThreadHandler = void (*)(QXThread * const me); - -//${QEP::QMState} ............................................................ -//! State object for the QP::QMsm class (QM State Machine). -//! -//! @details -//! This class groups together the attributes of a QP::QMsm state, such as -//! the parent state (state nesting), the associated state handler function -//! and the exit action handler function. These attributes are used inside -//! the QP::QMsm::dispatch() and QP::QMsm::init() functions. -//! -//! @attention -//! The QP::QMState class is only intended for the QM code generator and -//! should not be used in hand-crafted code. -struct QMState { - QMState const * superstate; //!< superstate of this state - QStateHandler const stateHandler; //!< state handler function - QActionHandler const entryAction; //!< entry action handler function - QActionHandler const exitAction; //!< exit action handler function - QActionHandler const initAction; //!< init action handler function -}; - -//${QEP::QMTranActTable} ..................................................... -//! Transition-Action Table for the QP::QMsm State Machine. -struct QMTranActTable { - QMState const * target; //!< target of the transition - QActionHandler const act[1]; //!< array of actions -}; - -//${QEP::QHsmAttr} ........................................................... -//! Attribute of for the QP::QHsm class (Hierarchical State Machine) -//! -//! @details -//! This union represents possible values stored in the 'state' and 'temp' -//! attributes of the QP::QHsm class. -union QHsmAttr { - QStateHandler fun; //!< pointer to a state handler function - QActionHandler act; //!< pointer to an action-handler function - QXThreadHandler thr; //!< pointer to an thread-handler function - QMState const *obj; //!< pointer to QMState object - QMTranActTable const *tatbl; //!< transition-action table -}; - -//${QEP::Q_USER_SIG} ......................................................... -//! Type returned from state-handler functions -constexpr enum_t Q_USER_SIG {4}; - -//${QEP::QHsm} ............................................................... -//! Hierarchical State Machine abstract base class (ABC) -//! -//! @details -//! QP::QHsm represents a Hierarchical State Machine (HSM) with full support -//! for hierarchical nesting of states, entry/exit actions, and initial -//! transitions in any composite state. QHsm inherits QP::QMsm without adding -//! new attributes, so it takes the same amount of RAM as QP::QMsm.
-//! -//! QP::QHsm is also the base class for the QP::QMsm state machine, which -//! provides better efficiency, but requires the use of the QM modeling tool -//! to generate code. -//! -//! @note -//! QP::QHsm is not intended to be instantiated directly, but rather serves as -//! the base class for derivation of state machines in the application code. -//! -//! @usage -//! The following example illustrates how to derive a state machine class -//! from QP::QHsm. -//! @include qep_qhsm.cpp -class QHsm { -private: - - //! current active state (the state-variable) - QHsmAttr m_state; - - //! temporary: transition chain, target state, etc. - QHsmAttr m_temp; - -public: - - //! Maximum nesting depth of states in HSM - static constexpr std::int_fast8_t MAX_NEST_DEPTH_{6}; - -private: - - // friends... - friend class QMsm; - friend class QActive; - friend class QMActive; - friend class QXThread; - friend class QXMutex; - friend class QXSemaphore; - -#ifdef Q_UTEST - friend class QHsmDummy; -#endif // def Q_UTEST - -#ifdef Q_UTEST - friend class QActiveDummy; -#endif // def Q_UTEST - -public: - - //! Reserved signals by the HSM-style state machine - //! implementation strategy. - enum ReservedSig : QSignal { - Q_EMPTY_SIG, //!< signal to execute the default case - Q_ENTRY_SIG, //!< signal for entry actions - Q_EXIT_SIG, //!< signal for exit actions - Q_INIT_SIG //!< signal for nested initial transitions - }; - - //! All possible return values from state-handlers - enum QStateRet : std::uint_fast8_t { - // unhandled and need to "bubble up" - Q_RET_SUPER, //!< event passed to superstate to handle - Q_RET_SUPER_SUB, //!< event passed to submachine superstate - Q_RET_UNHANDLED, //!< event unhandled due to a guard - - // handled and do not need to "bubble up" - Q_RET_HANDLED, //!< event handled (internal transition) - Q_RET_IGNORED, //!< event silently ignored (bubbled up to top) - - // entry/exit - Q_RET_ENTRY, //!< state entry action executed - Q_RET_EXIT, //!< state exit action executed - - // no side effects - Q_RET_NULL, //!< return value without any effect - - // transitions need to execute transition-action table in QP::QMsm - Q_RET_TRAN, //!< regular transition - Q_RET_TRAN_INIT, //!< initial transition in a state or submachine - Q_RET_TRAN_EP, //!< entry-point transition into a submachine - - // transitions that additionally clobber QHsm.m_state - Q_RET_TRAN_HIST, //!< transition to history of a given state - Q_RET_TRAN_XP //!< exit-point transition out of a submachine - }; - -protected: - - //! protected constructor of QHsm - explicit QHsm(QStateHandler const initial) noexcept; - -public: - -#ifdef Q_HSM_XTOR - //! virtual destructor - virtual ~QHsm() noexcept { - // empty - } -#endif // def Q_HSM_XTOR - - //! Executes the top-most initial transition in QP::QHsm - //! - //! @details - //! Executes the top-most initial transition in a HSM. - //! - //! @param[in] e pointer to an extra parameter (might be NULL) - //! @param[in] qs_id QS-id of this state machine (for QS local filter) - //! - //! @note - //! Must be called exactly __once__ before the QP::QHsm::dispatch(). - //! - //! @tr{RQP103} @tr{RQP120I} @tr{RQP120D} - virtual void init( - void const * const e, - std::uint_fast8_t const qs_id); - - //! overloaded init(qs_id) - //! - //! @details - //! Executes the top-most initial transition in a HSM (overloaded). - //! - //! @param[in] qs_id QS-id of this state machine (for QS local filter) - //! - //! @attention - //! QHsm::init() must be called exactly **once** before - //! QHsm::dispatch() - virtual void init(std::uint_fast8_t const qs_id) { - init(nullptr, qs_id); - } - - //! Dispatches an event to QP::QHsm - //! - //! @details - //! Dispatches an event for processing to a hierarchical state machine. - //! The event dispatching represents one run-to-completion (RTC) step. - //! - //! @param[in] e pointer to the event to be dispatched to the HSM - //! @param[in] qs_id QS-id of this state machine (for QS local filter) - //! - //! @attention - //! This state machine must be initialized by calling QP::QHsm::init() - //! exactly **once** before calling QP::QHsm::dispatch(). - //! - //! @tr{RQP103} - //! @tr{RQP120A} @tr{RQP120B} @tr{RQP120C} @tr{RQP120D} @tr{RQP120E} - virtual void dispatch( - QEvt const * const e, - std::uint_fast8_t const qs_id); - - //! The top-state handler - //! - //! @details - //! The QHsm::top() state handler is the ultimate root of state - //! hierarchy in all HSMs derived from QP::QHsm. - //! - //! @param[in] me pointer to the HSM instance - //! @param[in] e pointer to the event to be dispatched to the HSM - //! - //! @returns - //! Always returns #Q_RET_IGNORED, which means that the top state ignores - //! all events. - //! - //! @note - //! The parameters to this state handler are not used. They are provided - //! for conformance with the state-handler function signature - //! QP::QStateHandler. - //! - //! @tr{RQP103} @tr{RQP120T} - static QState top( - void * const me, - QEvt const * const e) noexcept; - - //! Obtain the current state (state handler function) - //! - //! @note used in the QM code generation - QStateHandler state() const noexcept { - return m_state.fun; - } - -#ifdef Q_SPY - //! Get the current state handler of the QP::QHsm - virtual QStateHandler getStateHandler() noexcept { - return m_state.fun; - } -#endif // def Q_SPY - - //! Tests if a given state is part of the current active state - //! configuration - //! - //! @details - //! Tests if a state machine derived from QHsm is-in a given state. - //! - //! @note - //! For a HSM, to "be in a state" means also to be in a superstate of - //! of the state. - //! - //! @param[in] s pointer to the state-handler function to be tested - //! - //! @returns - //! 'true' if the HSM is in the `state` and 'false' otherwise - //! - //! @tr{RQP103} - //! @tr{RQP120S} - bool isIn(QStateHandler const s) noexcept; - - //! Obtain the current active child state of a given parent - //! - //! @note used in the QM code generation - QStateHandler childState(QStateHandler const parent) noexcept; - -protected: - - //! Helper function to specify a state transition - QState tran(QStateHandler const target) noexcept { - m_temp.fun = target; - return Q_RET_TRAN; - } - - //! Helper function to specify a transition to history - QState tran_hist(QStateHandler const hist) noexcept { - m_temp.fun = hist; - return Q_RET_TRAN_HIST; - } - - //! Helper function to specify the superstate of a given state - QState super(QStateHandler const superstate) noexcept { - m_temp.fun = superstate; - return Q_RET_SUPER; - } - - //! Helper function to specify a regular state transition - //! in a QM state-handler - QState qm_tran(void const * const tatbl) noexcept { - m_temp.tatbl = static_cast(tatbl); - return Q_RET_TRAN; - } - - //! Helper function to specify an initial state transition - //! in a QM state-handler - QState qm_tran_init(void const * const tatbl) noexcept { - m_temp.tatbl = static_cast(tatbl); - return Q_RET_TRAN_INIT; - } - - //! Helper function to specifiy a transition to history - //! in a QM state-handler - QState qm_tran_hist( - QMState const * const hist, - void const * const tatbl) noexcept - { - m_state.obj = hist; - m_temp.tatbl = static_cast(tatbl); - return Q_RET_TRAN_HIST; - } - - //! Helper function to specify a transition to an entry point - //! to a submachine state in a QM state-handler - QState qm_tran_ep(void const * const tatbl) noexcept { - m_temp.tatbl = static_cast(tatbl); - return Q_RET_TRAN_EP; - } - - //! Helper function to specify a transition to an exit point - //! from a submachine state in a QM state-handler - QState qm_tran_xp( - QActionHandler const xp, - void const * const tatbl) noexcept - { - m_state.act = xp; - m_temp.tatbl = static_cast(tatbl); - return Q_RET_TRAN_XP; - } - -#ifdef Q_SPY - //! Helper function to specify a state entry in a QM state-handler - QState qm_entry(QMState const * const s) noexcept { - m_temp.obj = s; - return Q_RET_ENTRY; - } -#endif // def Q_SPY - -#ifndef Q_SPY - //! Helper function to specify a state entry in a QM state-handler - QState qm_entry(QMState const * const s) noexcept { - static_cast(s); // unused parameter - return Q_RET_ENTRY; - } -#endif // ndef Q_SPY - -#ifdef Q_SPY - //! Helper function to specify a state exit in a QM state-handler - QState qm_exit(QMState const * const s) noexcept { - m_temp.obj = s; - return Q_RET_EXIT; - } -#endif // def Q_SPY - -#ifndef Q_SPY - //! Helper function to specify a state exit in a QM state-handler - QState qm_exit(QMState const * const s) noexcept { - static_cast(s); // unused parameter - return Q_RET_EXIT; - } -#endif // ndef Q_SPY - - //! Helper function to specify a submachine exit in a QM state-handler - QState qm_sm_exit(QMState const * const s) noexcept { - m_temp.obj = s; - return Q_RET_EXIT; - } - - //! Helper function to call in a QM state-handler when it passes - //! the event to the host submachine state to handle an event. - QState qm_super_sub(QMState const * const s) noexcept { - m_temp.obj = s; - return Q_RET_SUPER_SUB; - } - -private: - - //! @details - //! helper function to execute transition sequence in a hierarchical - //! state machine (HSM). - //! - //! @param[in,out] path array of pointers to state-handler functions - //! to execute the entry actions - //! @param[in] qs_id QS-id of this state machine (for QS local filter) - //! - //! @returns - //! the depth of the entry path stored in the `path` parameter. - //! - //! @tr{RQP103} - //! @tr{RQP120E} @tr{RQP120F} - std::int_fast8_t hsm_tran( - QStateHandler (&path)[MAX_NEST_DEPTH_], - std::uint_fast8_t const qs_id); -}; // class QHsm - -//${QEP::QMsm} ............................................................... -//! QM State Machine implementation strategy -//! -//! @details -//! QP::QMsm (QM State Machine) provides a more efficient state machine -//! implementation strategy than QHsm, but requires the use of the QM -//! modeling tool, but are the fastest and need the least run-time -//! support (the smallest event-processor taking up the least code space). -//! -//! @note -//! QP::QMsm is not intended to be instantiated directly, but rather serves as -//! the base class for derivation of state machines in the application code. -//! -//! @usage -//! The following example illustrates how to derive a state machine class -//! from QP::QMsm: -//! @include qep_qmsm.cpp -class QMsm : public QP::QHsm { -private: - - //! the top state object for the QP::QMsm - static QMState const msm_top_s; - - // friends... - friend class QMActive; - -public: - - //! maximum depth of implemented entry levels for transitions to history - static constexpr std::int_fast8_t MAX_ENTRY_DEPTH_{4}; - -public: - - //! Performs the second step of SM initialization by triggering - //! the top-most initial transition. - //! - //! @details - //! Executes the top-most initial transition in a MSM. - //! - //! @param[in] e pointer to an extra parameter (might be nullptr) - //! @param[in] qs_id QS-id of this state machine (for QS local filter) - //! - //! @attention - //! QMsm::init() must be called exactly **once** before QMsm::dispatch() - void init( - void const * const e, - std::uint_fast8_t const qs_id) override; - -protected: - - //! Protected constructor - //! @details - //! Performs the first step of initialization by assigning the initial - //! pseudostate to the currently active state of the state machine. - //! - //! @param[in] initial the top-most initial transition for the MSM. - //! - //! @note - //! The constructor is protected to prevent direct instantiating of the - //! QP::QMsm objects. This class is intended for subclassing only. - //! - //! @sa - //! The QP::QMsm example illustrates how to use the QMsm constructor - //! in the constructor initializer list of the derived state machines. - explicit QMsm(QStateHandler const initial) noexcept - : QHsm(initial) - { - m_state.obj = &msm_top_s; - m_temp.fun = initial; - } - -public: - - //! overloaded init(qs_id) - //! - //! @details - //! Executes the top-most initial transition in a MSM (overloaded). - //! - //! @param[in] qs_id QS-id of this state machine (for QS local filter) - //! - //! @attention - //! QMsm::init() must be called exactly **once** before QMsm::dispatch() - void init(std::uint_fast8_t const qs_id) override { - QMsm::init(nullptr, qs_id); - } - - //! Dispatches an event to a MSM - //! - //! @details - //! Dispatches an event for processing to a meta state machine (MSM). - //! The processing of an event represents one run-to-completion (RTC) step. - //! - //! @param[in] e pointer to the event to be dispatched to the MSM - //! @param[in] qs_id QS-id of this state machine (for QS local filter) - //! - //! @note - //! Must be called after QMsm::init(). - void dispatch( - QEvt const * const e, - std::uint_fast8_t const qs_id) override; - - //! Tests if a given state is part of the active state configuration - //! - //! @details - //! Tests if a state machine derived from QMsm is-in a given state. - //! - //! @note - //! For a MSM, to "be-in" a state means also to "be-in" a superstate of - //! of the state. - //! - //! @param[in] st pointer to the QMState object that corresponds to the - //! tested state. - //! @returns - //! 'true' if the MSM is in the \c st and 'false' otherwise - bool isInState(QMState const * const st) const noexcept; - -private: - - //! disallow inherited top() function in QP::QMsm and subclasses - //! @sa QMsm::msm_top_s - static QStateHandler top( - void * const me, - QEvt const * const e) noexcept = delete; - -public: - - //! Return the current active state object (read only) - QMState const * stateObj() const noexcept { - return m_state.obj; - } - - //! Obtain the current active child state of a given parent (read only) - //! - //! @details - //! Finds the child state of the given `parent`, such that this child - //! state is an ancestor of the currently active state. The main purpose - //! of this function is to support **shallow history** transitions in - //! state machines derived from QHsm. - //! - //! @param[in] parent pointer to the state-handler function - //! - //! @returns - //! the child of a given `parent` state, which is an ancestor of the - //! currently active state - //! - //! @note - //! this function is designed to be called during state transitions, so it - //! does not necessarily start in a stable state configuration. - //! However, the function establishes stable state configuration upon exit. - //! - //! @tr{RQP103} - //! @tr{RQP120H} - QMState const * childStateObj(QMState const * const parent) const noexcept; - -protected: - -#ifdef Q_SPY - //! Get the current state handler of the QMsm - QStateHandler getStateHandler() noexcept override { - return m_state.obj->stateHandler; - } -#endif // def Q_SPY - -private: - - //! Internal helper function to execute a transition-action table - //! - //! @details - //! Helper function to execute transition sequence in a tran-action table. - //! - //! @param[in] tatbl pointer to the transition-action table - //! @param[in] qs_id QS-id of this state machine (for QS local filter) - //! - //! @returns - //! the status of the last action from the transition-action table. - //! - //! @note - //! This function is for internal use inside the QEP event processor and - //! should **not** be called directly from the applications. - QState execTatbl_( - QMTranActTable const * const tatbl, - std::uint_fast8_t const qs_id); - - //! Internal helper function to exit current state to transition source - //! - //! @details - //! Helper function to exit the current state configuration to the - //! transition source, which is a hierarchical state machine might be a - //! superstate of the current state. - //! - //! @param[in] s pointer to the current state - //! @param[in] ts pointer to the transition source state - //! @param[in] qs_id QS-id of this state machine (for QS local filter) - void exitToTranSource_( - QMState const * s, - QMState const * const ts, - std::uint_fast8_t const qs_id); - - //! Internal helper function to enter state history - //! - //! @details - //! Static helper function to execute the segment of transition to history - //! after entering the composite state and - //! - //! @param[in] hist pointer to the history substate - //! @param[in] qs_id QS-id of this state machine (for QS local filter) - //! - //! @returns - //! ::Q_RET_INIT, if an initial transition has been executed in the last - //! entered state or ::Q_RET_NULL if no such transition was taken. - QState enterHistory_( - QMState const * const hist, - std::uint_fast8_t const qs_id); -}; // class QMsm - -} // namespace QP -//$enddecl${QEP} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -//============================================================================ -//$declare${QEP-macros} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv - -//${QEP-macros::Q_STATE_DECL} ................................................ -//! Macro to generate a declaration of a state-handler, state-caller and -//! a state-object for a given state in a subclass of QP::QHsm. -#define Q_STATE_DECL(state_) \ - QP::QState state_ ## _h(QP::QEvt const * const e); \ - static QP::QState state_(void * const me, QP::QEvt const * const e) - -//${QEP-macros::Q_STATE_DEF} ................................................. -//! Macro to generate a declaration of a state-handler, state-caller and -//! a state-object for a given state in a subclass of QP::QHsm. -#define Q_STATE_DEF(subclass_, state_) \ - QP::QState subclass_::state_(void * const me, QP::QEvt const * const e) { \ - return static_cast(me)->state_ ## _h(e); } \ - QP::QState subclass_::state_ ## _h(QP::QEvt const * const e) - -//${QEP-macros::Q_HANDLED} ................................................... -//! Macro to specify that the event was handled -#define Q_HANDLED() (Q_RET_HANDLED) - -//${QEP-macros::Q_UNHANDLED} ................................................. -//! Macro to specify that the event was NOT handled -//! due to a guard condition evaluating to 'false' -#define Q_UNHANDLED() (Q_RET_UNHANDLED) - -//${QEP-macros::Q_EVT_CAST} .................................................. -//! Perform downcast of an event onto a subclass of QEvt `class_` -//! -//! @details -//! This macro encapsulates the downcast of QEvt pointers, which violates -//! MISRA-C 2004 rule 11.4(advisory). This macro helps to localize this -//! deviation. -#define Q_EVT_CAST(subclass_) (static_cast(e)) - -//${QEP-macros::Q_STATE_CAST} ................................................ -//! Macro to perform casting to QStateHandler. -//! -//! @details -//! This macro encapsulates the cast of a specific state handler function -//! pointer to QStateHandler, which violates MISRA-C 2004 rule 11.4(advisory). -//! This macro helps to localize this deviation. -#define Q_STATE_CAST(handler_) \ - (reinterpret_cast(handler_)) - -//${QEP-macros::Q_ACTION_CAST} ............................................... -//! Macro to perform casting to QActionHandler. -//! -//! @details -//! This macro encapsulates the cast of a specific action handler function -//! pointer to QActionHandler, which violates MISRA-C2004 rule 11.4(advisory). -//! This macro helps to localize this deviation. -#define Q_ACTION_CAST(act_) \ - (reinterpret_cast(act_)) - -//${QEP-macros::QM_STATE_DECL} ............................................... -//! Macro to generate a declaration of a state-handler, state-caller and -//! a state-object for a given state in a subclass of QP::QMsm. -#define QM_STATE_DECL(state_) \ - QP::QState state_ ## _h(QP::QEvt const * const e); \ - static QP::QState state_(void * const me, QP::QEvt const * const e); \ - static QP::QMState const state_ ## _s - -//${QEP-macros::QM_SM_STATE_DECL} ............................................ -//! Macro to generate a declaration of a state-handler, state-caller and -//! a state-object for a given *submachine* state in a subclass of QP::QMsm. -#define QM_SM_STATE_DECL(subm_, state_) \ - QP::QState state_ ## _h(QP::QEvt const * const e);\ - static QP::QState state_(void * const me, QP::QEvt const * const e); \ - static SM_ ## subm_ const state_ ## _s - -//${QEP-macros::QM_ACTION_DECL} .............................................. -//! Macro to generate a declaration of an action-handler and action-caller -//! in a subclass of QP::QMsm. -#define QM_ACTION_DECL(action_) \ - QP::QState action_ ## _h(); \ - static QP::QState action_(void * const me) - -//${QEP-macros::QM_STATE_DEF} ................................................ -//! Macro to generate a definition of a state-caller and state-handler -//! for a given state in a subclass of QP::QMsm. -#define QM_STATE_DEF(subclass_, state_) \ - QP::QState subclass_::state_(void * const me, QP::QEvt const * const e) { \ - return static_cast(me)->state_ ## _h(e); } \ - QP::QState subclass_::state_ ## _h(QP::QEvt const * const e) - -//${QEP-macros::QM_ACTION_DEF} ............................................... -//! Macro to generate a definition of an action-caller and action-handler -//! in a subclass of QP::QMsm. -#define QM_ACTION_DEF(subclass_, action_) \ - QP::QState subclass_::action_(void * const me) { \ - return static_cast(me)->action_ ## _h(); } \ - QP::QState subclass_::action_ ## _h() - -//${QEP-macros::QM_HANDLED} .................................................. -//! Macro for a QM action-handler when it handles the event. -#define QM_HANDLED() (Q_RET_HANDLED) - -//${QEP-macros::QM_UNHANDLED} ................................................ -//! Macro for a QM action-handler when it does not handle the event -//! due to a guard condition evaluating to false. -#define QM_UNHANDLED() (Q_RET_HANDLED) - -//${QEP-macros::QM_SUPER} .................................................... -//! Macro for a QM action-handler when it passes the event to the superstate -#define QM_SUPER() (Q_RET_SUPER) - -//${QEP-macros::QM_STATE_NULL} ............................................... -//! Macro to provide strictly-typed zero-state to use for submachines. -//! Applicable to suclasses of QP::QMsm. -#define QM_STATE_NULL (nullptr) - -//${QEP-macros::Q_ACTION_NULL} ............................................... -//! Macro to provide strictly-typed zero-action to terminate action lists -//! in the transition-action-tables in QP::QMsm. -#define Q_ACTION_NULL (nullptr) - -//${QEP-macros::Q_UNUSED_PAR} ................................................ -//! Helper macro to clearly mark unused parameters of functions. -#define Q_UNUSED_PAR(par_) (static_cast(par_)) - -//${QEP-macros::Q_DIM} ....................................................... -//! Helper macro to calculate static dimension of a 1-dim `array_` -//! -//! @param[in] array_ 1-dimensional array -//! -//! @returns -//! the length of the array (number of elements it can hold) -#define Q_DIM(array_) (sizeof(array_) / sizeof((array_)[0U])) - -//${QEP-macros::Q_UINT2PTR_CAST} ............................................. -//! Perform cast from unsigned integer `uint_` to pointer of type `type_` -//! -//! @details -//! This macro encapsulates the cast to (type_ *), which QP ports or -//! application might use to access embedded hardware registers. -//! Such uses can trigger PC-Lint "Note 923: cast from int to pointer" -//! and this macro helps to encapsulate this deviation. -#define Q_UINT2PTR_CAST(type_, uint_) (reinterpret_cast(uint_)) - -//${QEP-macros::QEVT_INITIALIZER} ............................................ -//! Initializer of static constant QEvt instances -//! -//! @details -//! This macro encapsulates the ugly casting of enumerated signals -//! to QSignal and constants for QEvt.poolID and QEvt.refCtr_. -#define QEVT_INITIALIZER(sig_) { static_cast(sig_), 0U, 0U } -//$enddecl${QEP-macros} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -#endif // QEP_HPP_ diff --git a/src/qep_hsm.cpp b/src/qep_hsm.cpp index 4afcf5e..3e693ea 100644 --- a/src/qep_hsm.cpp +++ b/src/qep_hsm.cpp @@ -1,628 +1,577 @@ -//$file${src::qf::qep_hsm.cpp} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv -// -// Model: qpcpp.qm -// File: ${src::qf::qep_hsm.cpp} -// -// This code has been generated by QM 5.2.5 . -// DO NOT EDIT THIS FILE MANUALLY. All your changes will be lost. +//============================================================================ +// QP/C++ Real-Time Embedded Framework (RTEF) +// Version 8.0.2 // -// This code is covered by the following QP license: -// License # : LicenseRef-QL-dual -// Issued to : Any user of the QP/C++ real-time embedded framework -// Framework(s) : qpcpp -// Support ends : 2023-12-31 -// License scope: +// Copyright (C) 2005 Quantum Leaps, LLC. All rights reserved. // -// 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: +// This software is dual-licensed under the terms of the open-source GNU +// General Public License (GPL) or under the terms of one of the closed- +// source Quantum Leaps commercial licenses. // // Redistributions in source code must retain this top-level comment block. // Plagiarizing this software to sidestep the license obligations is illegal. // -// Contact information: +// NOTE: +// The GPL does NOT permit the incorporation of this code into proprietary +// programs. Please contact Quantum Leaps for commercial licensing options, +// which expressly supersede the GPL and are designed explicitly for +// closed-source distribution. +// +// Quantum Leaps contact information: // // -// -//$endhead${src::qf::qep_hsm.cpp} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -//! @file -//! @brief QP::QHsm implementation -//! -//! @tr{RQP103} @tr{RQP104} @tr{RQP120} @tr{RQP130} - +//============================================================================ #define QP_IMPL // this is QP implementation -#include "qep_port.hpp" // QEP port +#include "qp_port.hpp" // QP port +#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 - -//============================================================================ -//$skip${QP_VERSION} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv -// Check for the minimum required QP version -#if (QP_VERSION < 700U) || (QP_VERSION != ((QP_RELEASE^4294967295U) % 0x3E8U)) -#error qpcpp version 7.0.0 or higher required -#endif -//$endskip${QP_VERSION} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -//$define${QEP::versionStr[]} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv -namespace QP { - -} // namespace QP -//$enddef${QEP::versionStr[]} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -//============================================================================ // unnamed namespace for local definitions with internal linkage namespace { + Q_DEFINE_THIS_MODULE("qep_hsm") -//---------------------------------------------------------------------------- -//! Immutable events corresponding to the reserved signals. -//! -//! @details -//! Static, immutable reserved events that the QEP event processor sends -//! to state handler functions of QHsm-style state machine to execute entry -//! actions, exit actions, and initial transitions. -//! +// maximum depth of state nesting in a QHsm (including the top level) +// must be >= 3 +static constexpr std::int_fast8_t QHSM_MAX_NEST_DEPTH_ {6}; + +//! @cond INTERNAL + +// immutable events corresponding to the reserved signals. static QP::QEvt const l_reservedEvt_[4] { -#ifdef Q_EVT_CTOR // Is the QEvt constructor provided? - QP::QEvt(static_cast(QP::QHsm::Q_EMPTY_SIG), 0U), - QP::QEvt(static_cast(QP::QHsm::Q_ENTRY_SIG), 0U), - QP::QEvt(static_cast(QP::QHsm::Q_EXIT_SIG), 0U), - QP::QEvt(static_cast(QP::QHsm::Q_INIT_SIG), 0U) -#else // QEvt is a POD (Plain Old Datatype) - { static_cast(QP::QHsm::Q_EMPTY_SIG), 0U, 0U }, - { static_cast(QP::QHsm::Q_ENTRY_SIG), 0U, 0U }, - { static_cast(QP::QHsm::Q_EXIT_SIG), 0U, 0U }, - { static_cast(QP::QHsm::Q_INIT_SIG), 0U, 0U } -#endif + 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)) }; -//---------------------------------------------------------------------------- -// inline helper functions +//! @endcond -//............................................................................ -//! helper function to trigger reserved event in an QHsm -//! -//! @param[in] state state handler function -//! @param[in] sig reserved signal to trigger -static inline QP::QState hsm_reservedEvt_( - QP::QHsm * const me, - QP::QStateHandler const state, - enum QP::QHsm::ReservedSig const sig) -{ - return (*state)(me, &l_reservedEvt_[sig]); -} +} // unnamed namespace -//............................................................................ -//! Helper function to execute entry into a given state in a -//! hierarchical state machine (HSM). -//! -//! @param[in] state state handler function -//! @param[in] qs_id QS-id of this state machine (for QS local filter) -static inline void hsm_state_entry_( - QP::QHsm * const me, - QP::QStateHandler const state, - std::uint_fast8_t const qs_id) -{ -#ifdef Q_SPY - if ((*state)(me, &l_reservedEvt_[QP::QHsm::Q_ENTRY_SIG]) - == QP::QHsm::Q_RET_HANDLED) - { - QS_CRIT_STAT_ - QS_BEGIN_PRE_(QP::QS_QEP_STATE_ENTRY, qs_id) - QS_OBJ_PRE_(me); - QS_FUN_PRE_(state); - QS_END_PRE_() - } -#else - Q_UNUSED_PAR(qs_id); - static_cast((*state)(me, &l_reservedEvt_[QP::QHsm::Q_ENTRY_SIG])); -#endif // Q_SPY -} +//============================================================================ +//! @cond INTERNAL + +// internal helper macro to pass a reserved event into the state handler +#define QHSM_RESERVED_EVT_(state_, sig_) \ + ((*(state_))(this, &l_reservedEvt_[(sig_)])) -//............................................................................ -//! Helper function to execute exit from a given state in a -//! hierarchical state machine (HSM). -//! -//! @param[in] state state handler function -//! @param[in] qs_id QS-id of this state machine (for QS local filter) -//! -//! @returns -//! 'true' if the exit action has been found in the state and -//! 'flase' otherwise. -static inline bool hsm_state_exit_( - QP::QHsm * const me, - QP::QStateHandler const state, - std::uint_fast8_t const qs_id) -{ #ifdef Q_SPY - bool isHandled; - if ((*state)(me, &l_reservedEvt_[QP::QHsm::Q_EXIT_SIG]) - == QP::QHsm::Q_RET_HANDLED) - { - QS_CRIT_STAT_ - QS_BEGIN_PRE_(QP::QS_QEP_STATE_EXIT, qs_id) - QS_OBJ_PRE_(me); - QS_FUN_PRE_(state); - QS_END_PRE_() - isHandled = true; - } - else { - isHandled = false; - } - return isHandled; +// helper macro to trace state action (entry/exit) +#define QS_STATE_ACT_(rec_, state_) \ + QS_CRIT_ENTRY(); \ + QS_BEGIN_PRE((rec_), qsId) \ + QS_OBJ_PRE(this); \ + QS_FUN_PRE(state_); \ + QS_END_PRE() \ + QS_CRIT_EXIT() + +// internal helper macro to top-most init +#define QS_TOP_INIT_(rec_, trg_) \ + QS_CRIT_ENTRY(); \ + QS_BEGIN_PRE((rec_), qsId) \ + QS_TIME_PRE(); \ + QS_OBJ_PRE(this); \ + QS_FUN_PRE(trg_); \ + QS_END_PRE() \ + QS_CRIT_EXIT() + +// internal helper macro to trace transition segment +#define QS_TRAN_SEG_(rec_, src_, trg_) \ + QS_CRIT_ENTRY(); \ + QS_BEGIN_PRE((rec_), qsId) \ + QS_OBJ_PRE(this); \ + QS_FUN_PRE(src_); \ + QS_FUN_PRE(trg_); \ + QS_END_PRE() \ + QS_CRIT_EXIT() + +// internal helper macro to trace transition action +#define QS_TRAN_ACT_(rec_, state_) \ + QS_CRIT_ENTRY(); \ + QS_BEGIN_PRE((rec_), qsId) \ + QS_SIG_PRE(e->sig); \ + QS_OBJ_PRE(this); \ + QS_FUN_PRE(state_); \ + QS_END_PRE() \ + QS_CRIT_EXIT() + +// internal helper macro to trace transition begin/end +#define QS_TRAN0_(rec_, trg_) \ + QS_CRIT_ENTRY(); \ + QS_BEGIN_PRE((rec_), qsId) \ + QS_TIME_PRE(); \ + QS_SIG_PRE(e->sig); \ + QS_OBJ_PRE(this); \ + QS_FUN_PRE(trg_); \ + QS_END_PRE() \ + QS_CRIT_EXIT() + +// internal helper macro to trace regulsr transition +#define QS_TRAN_END_(rec_, src_, trg_) \ + QS_CRIT_ENTRY(); \ + QS_BEGIN_PRE((rec_), qsId) \ + QS_TIME_PRE(); \ + QS_SIG_PRE(e->sig); \ + QS_OBJ_PRE(this); \ + QS_FUN_PRE(src_); \ + QS_FUN_PRE(trg_); \ + QS_END_PRE() \ + QS_CRIT_EXIT() + #else - Q_UNUSED_PAR(qs_id); - return (*state)(me, &l_reservedEvt_[QP::QHsm::Q_EXIT_SIG]); -#endif // Q_SPY -} +#define QS_STATE_ACT_(rec_, state_) (static_cast(0)) +#define QS_TOP_INIT_(rec_, trg_) (static_cast(0)) +#define QS_TRAN_SEG_(rec_, src_, trg_) (static_cast(0)) +#define QS_TRAN_ACT_(rec_, state_) (static_cast(0)) +#define QS_TRAN0_(rec_, trg_) (static_cast(0)) +#define QS_TRAN_END_(rec_, src_, trg_) (static_cast(0)) +#endif -} // unnamed namespace +//! @endcond -//$define${QEP::QHsm} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv namespace QP { -//${QEP::QHsm} ............................................................... - -//${QEP::QHsm::QHsm} ......................................................... -QHsm::QHsm(QStateHandler const initial) noexcept{ - m_state.fun = Q_STATE_CAST(&top); +//............................................................................ +QHsm::QHsm(QStateHandler const initial) noexcept +: QAsm() +{ + m_state.fun = ⊤ m_temp.fun = initial; } -//${QEP::QHsm::init} ......................................................... +//............................................................................ void QHsm::init( void const * const e, - std::uint_fast8_t const qs_id) + std::uint_fast8_t const qsId) { - #ifdef Q_SPY + QF_CRIT_STAT + + // produce QS dictionary for QP::QHsm::top() +#ifdef Q_SPY + QS_CRIT_ENTRY(); + bool isDone = true; if ((QS::priv_.flags & 0x01U) == 0U) { QS::priv_.flags |= 0x01U; + isDone = false; + } + QS_CRIT_EXIT(); + if (!isDone) { QS_FUN_DICTIONARY(&QP::QHsm::top); } - #else - Q_UNUSED_PAR(qs_id); - #endif +#else + Q_UNUSED_PAR(qsId); +#endif // def Q_SPY QStateHandler t = m_state.fun; - //! @pre ctor must have been executed and initial tran NOT taken - Q_REQUIRE_ID(200, (m_temp.fun != nullptr) - && (t == Q_STATE_CAST(&top))); + 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); + QF_CRIT_EXIT(); - QS_CRIT_STAT_ - QS_BEGIN_PRE_(QS_QEP_STATE_INIT, qs_id) - QS_OBJ_PRE_(this); // this state machine object - QS_FUN_PRE_(t); // the source state - QS_FUN_PRE_(m_temp.fun); // the target of the initial transition - QS_END_PRE_() + QS_TRAN_SEG_(QS_QEP_STATE_INIT, t, m_temp.fun); // drill down into the state hierarchy with initial transitions... - do { - QStateHandler path[MAX_NEST_DEPTH_]; // tran entry path array - std::int_fast8_t ip = 0; // entry path index - - path[0] = m_temp.fun; - static_cast(hsm_reservedEvt_(this, m_temp.fun, Q_EMPTY_SIG)); - while (m_temp.fun != t) { - ++ip; - Q_ASSERT_ID(220, ip < MAX_NEST_DEPTH_); - path[ip] = m_temp.fun; - static_cast(hsm_reservedEvt_(this, m_temp.fun, Q_EMPTY_SIG)); - } - m_temp.fun = path[0]; - - // retrace the entry path in reverse (desired) order... - do { - hsm_state_entry_(this, path[ip], qs_id); // enter path[ip] - --ip; - } while (ip >= 0); - - t = path[0]; // current state becomes the new source - - r = hsm_reservedEvt_(this, t, Q_INIT_SIG); // execute initial tran. - - #ifdef Q_SPY - if (r == Q_RET_TRAN) { - QS_BEGIN_PRE_(QS_QEP_STATE_INIT, qs_id) - QS_OBJ_PRE_(this); // this state machine object - QS_FUN_PRE_(t); // the source state - QS_FUN_PRE_(m_temp.fun); // the target of the initial tran. - QS_END_PRE_() - } - #endif // Q_SPY + QStateHandler path[QHSM_MAX_NEST_DEPTH_]; // tran. entry path array + path[0] = m_temp.fun; + static_cast(QHSM_RESERVED_EVT_(m_temp.fun, Q_EMPTY_SIG)); + + std::int_fast8_t ip = 1; // tran. entry path index (also the loop bound) + for (; (m_temp.fun != t) && (ip < QHSM_MAX_NEST_DEPTH_); ++ip) { + path[ip] = m_temp.fun; + static_cast(QHSM_RESERVED_EVT_(m_temp.fun, Q_EMPTY_SIG)); + } + QF_CRIT_ENTRY(); + // must NOT be too many state nesting levels or "malformed" HSM + Q_ASSERT_INCRIT(220, ip <= QHSM_MAX_NEST_DEPTH_); + QF_CRIT_EXIT(); - } while (r == Q_RET_TRAN); + m_temp.fun = path[0]; + enter_target_(&path[0], ip - 1, qsId); + t = path[0]; - QS_BEGIN_PRE_(QS_QEP_INIT_TRAN, qs_id) - QS_TIME_PRE_(); // time stamp - QS_OBJ_PRE_(this); // this state machine object - QS_FUN_PRE_(t); // the new active state - QS_END_PRE_() + QS_TOP_INIT_(QS_QEP_INIT_TRAN, t); m_state.fun = t; // change the current active state - m_temp.fun = t; // mark the configuration as stable +#ifdef Q_UNSAFE + Q_UNUSED_PAR(r); +#endif } -//${QEP::QHsm::dispatch} ..................................................... +//............................................................................ void QHsm::dispatch( QEvt const * const e, - std::uint_fast8_t const qs_id) + std::uint_fast8_t const qsId) { - #ifndef Q_SPY - Q_UNUSED_PAR(qs_id); - #endif +#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, + (e != nullptr) + && (s != nullptr)); + QF_CRIT_EXIT(); + + QS_TRAN0_(QS_QEP_DISPATCH, s); + + QState r = Q_RET_SUPER; - QStateHandler t = m_state.fun; - QS_CRIT_STAT_ - - //! @pre the current state must be initialized and - //! the state configuration must be stable - Q_REQUIRE_ID(400, (t != nullptr) - && (t == m_temp.fun)); - - QS_BEGIN_PRE_(QS_QEP_DISPATCH, qs_id) - QS_TIME_PRE_(); // time stamp - QS_SIG_PRE_(e->sig); // the signal of the event - QS_OBJ_PRE_(this); // this state machine object - QS_FUN_PRE_(t); // the current state - QS_END_PRE_() - - QStateHandler s; - QState r; // process the event hierarchically... - //! @tr{RQP120A} - do { + m_temp.fun = s; + std::int_fast8_t ip = QHSM_MAX_NEST_DEPTH_; + // NOTE: ip is the fixed loop upper bound + for (; ip > 0; --ip) { s = m_temp.fun; r = (*s)(this, e); // invoke state handler s if (r == Q_RET_UNHANDLED) { // unhandled due to a guard? - - QS_BEGIN_PRE_(QS_QEP_UNHANDLED, qs_id) - QS_SIG_PRE_(e->sig); // the signal of the event - QS_OBJ_PRE_(this); // this state machine object - QS_FUN_PRE_(s); // the current state - QS_END_PRE_() - - r = hsm_reservedEvt_(this, s, Q_EMPTY_SIG); // find superstate of s + QS_TRAN_ACT_(QS_QEP_UNHANDLED, s); + r = QHSM_RESERVED_EVT_(s, Q_EMPTY_SIG); // superstate of s } - } while (r == Q_RET_SUPER); - - // regular transition taken? - //! @tr{RQP120E} - if (r >= Q_RET_TRAN) { - QStateHandler path[MAX_NEST_DEPTH_]; - - path[0] = m_temp.fun; // save the target of the transition - path[1] = t; - path[2] = s; - - // exit current state to transition source s... - //! @tr{RQP120C} - for (; t != s; t = m_temp.fun) { - // exit handled? - if (hsm_state_exit_(this, t, qs_id)) { - // find superstate of t - static_cast(hsm_reservedEvt_(this, t, Q_EMPTY_SIG)); - } + if (r != Q_RET_SUPER) { // event NOT "bubbled up" + break; } + } + QF_CRIT_ENTRY(); + Q_ASSERT_INCRIT(310, ip > 0); + QF_CRIT_EXIT(); - std::int_fast8_t ip = hsm_tran(path, qs_id); // the HSM transition - - #ifdef Q_SPY - if (r == Q_RET_TRAN_HIST) { - - QS_BEGIN_PRE_(QS_QEP_TRAN_HIST, qs_id) - QS_OBJ_PRE_(this); // this state machine object - QS_FUN_PRE_(t); // the source of the transition - QS_FUN_PRE_(path[0]); // the target of the tran. to history - QS_END_PRE_() - - } - #endif // Q_SPY - - // execute state entry actions in the desired order... - //! @tr{RQP120B} - for (; ip >= 0; --ip) { - hsm_state_entry_(this, path[ip], qs_id); // enter path[ip] + if (r >= Q_RET_TRAN) { // tran. (regular or history) taken? +#ifdef Q_SPY + if (r == Q_RET_TRAN_HIST) { // tran. to history? + QS_TRAN_SEG_(QS_QEP_TRAN_HIST, s, m_temp.fun); } - t = path[0]; // stick the target into register - m_temp.fun = t; // update the next state - - // drill into the target hierarchy... - //! @tr{RQP120I} - while (hsm_reservedEvt_(this, t, Q_INIT_SIG) == Q_RET_TRAN) { - - QS_BEGIN_PRE_(QS_QEP_STATE_INIT, qs_id) - QS_OBJ_PRE_(this); // this state machine object - QS_FUN_PRE_(t); // the source (pseudo)state - QS_FUN_PRE_(m_temp.fun); // the target of the transition - QS_END_PRE_() +#endif // Q_SPY - ip = 0; - path[0] = m_temp.fun; + QStateHandler path[QHSM_MAX_NEST_DEPTH_]; + path[0] = m_temp.fun; // tran. target + path[1] = t; // current state + path[2] = s; // tran. source - // find superstate - static_cast(hsm_reservedEvt_(this, m_temp.fun, Q_EMPTY_SIG)); - - while (m_temp.fun != t) { - ++ip; - path[ip] = m_temp.fun; - // find superstate - static_cast( - hsm_reservedEvt_(this, m_temp.fun, Q_EMPTY_SIG)); + // exit current state to tran. source s... + while (t != s) { + // exit from t + if (QHSM_RESERVED_EVT_(t, Q_EXIT_SIG) == Q_RET_HANDLED) { + QS_STATE_ACT_(QS_QEP_STATE_EXIT, t); + // find superstate of t + static_cast(QHSM_RESERVED_EVT_(t, Q_EMPTY_SIG)); } - m_temp.fun = path[0]; - - // entry path must not overflow - Q_ASSERT_ID(410, ip < MAX_NEST_DEPTH_); - - // retrace the entry path in reverse (correct) order... - do { - hsm_state_entry_(this, path[ip], qs_id); // enter path[ip] - --ip; - } while (ip >= 0); + t = m_temp.fun; + } - t = path[0]; + // take the tran... + ip = tran_simple_(&path[0], qsId); + if (ip < -1) { // not a simple tran.? + ip = tran_complex_(&path[0], qsId); } - QS_BEGIN_PRE_(QS_QEP_TRAN, qs_id) - QS_TIME_PRE_(); // time stamp - QS_SIG_PRE_(e->sig); // the signal of the event - QS_OBJ_PRE_(this); // this state machine object - QS_FUN_PRE_(s); // the source of the transition - QS_FUN_PRE_(t); // the new active state - QS_END_PRE_() + enter_target_(&path[0], ip, qsId); + t = path[0]; + QS_TRAN_END_(QS_QEP_TRAN, s, t); } - - #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_END_PRE_() - + QS_TRAN0_(QS_QEP_INTERN_TRAN, s); } else { - - QS_BEGIN_PRE_(QS_QEP_IGNORED, qs_id) - QS_TIME_PRE_(); // time stamp - QS_SIG_PRE_(e->sig); // the signal of the event - QS_OBJ_PRE_(this); // this state machine object - QS_FUN_PRE_(m_state.fun);// the current state - QS_END_PRE_() - + QS_TRAN0_(QS_QEP_IGNORED, m_state.fun); } - #else - Q_UNUSED_PAR(qs_id); // when Q_SPY not defined - #endif // Q_SPY +#endif // Q_SPY m_state.fun = t; // change the current active state - m_temp.fun = t; // mark the configuration as stable -} - -//${QEP::QHsm::top} .......................................................... -QState QHsm::top( - void * const me, - QEvt const * const e) noexcept -{ - Q_UNUSED_PAR(me); - Q_UNUSED_PAR(e); - return Q_RET_IGNORED; // the top state ignores all events } -//${QEP::QHsm::isIn} ......................................................... -bool QHsm::isIn(QStateHandler const s) noexcept { - //! @pre state configuration must be stable - Q_REQUIRE_ID(600, m_temp.fun == m_state.fun); - - bool inState = false; // assume that this HSM is not in 'state' +//............................................................................ +bool QHsm::isIn(QStateHandler const state) noexcept { + bool inState = false; // assume that this HSM is not in 'state' // scan the state hierarchy bottom-up - QState r; - do { - // do the states match? - if (m_temp.fun == s) { - inState = true; // 'true' means that match found - r = Q_RET_IGNORED; // cause breaking out of the loop + QStateHandler s = m_state.fun; + QState r = Q_RET_SUPER; + while (r != Q_RET_IGNORED) { + if (s == state) { // do the states match? + inState = true; // 'true' means that match found + break; // break out of the for-loop } - else { - r = hsm_reservedEvt_(this, m_temp.fun, Q_EMPTY_SIG); - } - } while (r != Q_RET_IGNORED); // QHsm::top() state not reached - m_temp.fun = m_state.fun; // restore the stable state configuration - + r = QHSM_RESERVED_EVT_(s, Q_EMPTY_SIG); + s = m_temp.fun; + } return inState; // return the status } -//${QEP::QHsm::childState} ................................................... +//............................................................................ QStateHandler QHsm::childState(QStateHandler const parent) noexcept { - QStateHandler child = m_state.fun; // start with the current state - bool isFound = false; // start with the child not found - - // establish stable state configuration - m_temp.fun = m_state.fun; - QState r; - do { - // is this the parent of the current child? +#ifndef Q_UNSAFE + bool isFound = false; // assume the child state NOT found +#endif + + QStateHandler child = m_state.fun; // start with current state + m_temp.fun = child; // establish stable state configuration + QState r = Q_RET_SUPER; + while (r != Q_RET_IGNORED) { + // have the parent of the current child? if (m_temp.fun == parent) { - isFound = true; // child is found - r = Q_RET_IGNORED; // cause breaking out of the loop - } - else { - child = m_temp.fun; - r = hsm_reservedEvt_(this, m_temp.fun, Q_EMPTY_SIG); +#ifndef Q_UNSAFE + isFound = true; // indicate that child state was found +#endif + break; } - } while (r != Q_RET_IGNORED); // QHsm::top() state not reached - m_temp.fun = m_state.fun; // establish stable state configuration - - //! @post the child must be confirmed - Q_ENSURE_ID(810, isFound); - - #ifdef Q_NASSERT - Q_UNUSED_PAR(isFound); - #endif + child = m_temp.fun; + r = QHSM_RESERVED_EVT_(child, Q_EMPTY_SIG); + } + QF_CRIT_STAT + QF_CRIT_ENTRY(); + Q_ASSERT_INCRIT(590, isFound); + QF_CRIT_EXIT(); - return child; // return the child + return child; } -//${QEP::QHsm::hsm_tran} ..................................................... -std::int_fast8_t QHsm::hsm_tran( - QStateHandler (&path)[MAX_NEST_DEPTH_], - std::uint_fast8_t const qs_id) +//............................................................................ +//! @private @memberof QHsm +std::int_fast8_t QHsm::tran_simple_( + QStateHandler * const path, + std::uint_fast8_t const qsId) { - #ifndef Q_SPY - Q_UNUSED_PAR(qs_id); - #endif +#ifndef Q_SPY + Q_UNUSED_PAR(qsId); +#endif - std::int_fast8_t ip = -1; // transition entry path index QStateHandler t = path[0]; QStateHandler const s = path[2]; + std::int_fast8_t ip = 0; // tran. entry path index + QS_CRIT_STAT - // (a) check source==target (transition to self) + // (a) check source==target (tran. to self)... if (s == t) { - // exit the source - static_cast(hsm_state_exit_(this, s, qs_id)); + // exit source s + if (QHSM_RESERVED_EVT_(s, Q_EXIT_SIG) == Q_RET_HANDLED) { + QS_STATE_ACT_(QS_QEP_STATE_EXIT, s); + } ip = 0; // enter the target } else { - // superstate of target - static_cast(hsm_reservedEvt_(this, t, Q_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(hsm_reservedEvt_(this, s, Q_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) { - // exit the source - static_cast(hsm_state_exit_(this, s, qs_id)); + // exit source s + if (QHSM_RESERVED_EVT_(s, Q_EXIT_SIG) == Q_RET_HANDLED) { + QS_STATE_ACT_(QS_QEP_STATE_EXIT, s); + } ip = 0; // enter the target } - else { - // (d) check source->super==target - if (m_temp.fun == path[0]) { - // exit the source - static_cast(hsm_state_exit_(this, s, qs_id)); + // (d) check source->super==target... + else if (m_temp.fun == path[0]) { + // exit source s + if (QHSM_RESERVED_EVT_(s, Q_EXIT_SIG) == Q_RET_HANDLED) { + QS_STATE_ACT_(QS_QEP_STATE_EXIT, s); } - else { - // (e) check rest of source==target->super->super.. - // and store the entry path along the way - std::int_fast8_t iq = 0; // indicate that LCA was found - ip = 1; // enter target and its superstate - path[1] = t; // save the superstate of target - t = m_temp.fun; // save source->super - - // find target->super->super - QState r = hsm_reservedEvt_(this, path[1], Q_EMPTY_SIG); - while (r == Q_RET_SUPER) { - ++ip; - path[ip] = m_temp.fun; // store the entry path - if (m_temp.fun == s) { // is it the source? - // indicate that the LCA was found - iq = 1; - - // entry path must not overflow - Q_ASSERT_ID(510, ip < MAX_NEST_DEPTH_); - --ip; // do not enter the source - r = Q_RET_HANDLED; // terminate the loop - } - // it is not the source, keep going up - else { - r = hsm_reservedEvt_(this, m_temp.fun, Q_EMPTY_SIG); - } - } + ip = -1; // do not enter the target + } + else { + path[1] = t; // save the superstate of target + ip = -2; // cause execution of complex tran. + } + } + } + return ip; +} - // the LCA not found yet? - if (iq == 0) { - // entry path must not overflow - Q_ASSERT_ID(520, ip < MAX_NEST_DEPTH_); - - // exit the source - static_cast(hsm_state_exit_(this, s, qs_id)); - - // (f) check the rest of source->super - // == target->super->super... - // - iq = ip; - r = Q_RET_IGNORED; // indicate LCA NOT found - do { - // is this the LCA? - if (t == path[iq]) { - r = Q_RET_HANDLED; // indicate LCA found - ip = iq - 1; // do not enter LCA - iq = -1; // cause termination of the loop - } - else { - --iq; // try lower superstate of target - } - } while (iq >= 0); - - // LCA not found yet? - if (r != Q_RET_HANDLED) { - // (g) check each source->super->... - // for each target->super... - // - r = Q_RET_IGNORED; // keep looping - do { - // exit from t handled? - if (hsm_state_exit_(this, t, qs_id)) { - // find superstate of t - static_cast( - hsm_reservedEvt_(this, t, Q_EMPTY_SIG)); - } - t = m_temp.fun; // set to super of t - iq = ip; - do { - // is this LCA? - if (t == path[iq]) { - ip = iq - 1; // do not enter LCA - iq = -1; // break out of inner loop - r = Q_RET_HANDLED; // break outer loop - } - else { - --iq; - } - } while (iq >= 0); - } while (r != Q_RET_HANDLED); - } - } +//............................................................................ +//! @private @memberof QHsm +std::int_fast8_t QHsm::tran_complex_( + QStateHandler * const path, + std::uint_fast8_t const qsId) +{ +#ifndef Q_SPY + Q_UNUSED_PAR(qsId); +#endif + + // (e) check rest of source==target->super->super.. + // and store the entry path along the way + std::int_fast8_t iq = 0; // indicate that LCA was found + std::int_fast8_t ip = 1; // enter target and its superstate + QStateHandler const s = path[2]; // source state + QStateHandler t = m_temp.fun; // source->super + QF_CRIT_STAT + + // find target->super->super... + // note: ip is the fixed upper loop bound + QState r = QHSM_RESERVED_EVT_(path[1], Q_EMPTY_SIG); + while ((r == Q_RET_SUPER) && (ip < (QHSM_MAX_NEST_DEPTH_ - 1))) { + ++ip; + path[ip] = m_temp.fun; // store the entry path + if (m_temp.fun == s) { // is it the source? + iq = 1; // indicate that the LCA found + --ip; // do not enter the source + break; // terminate the loop + } + r = QHSM_RESERVED_EVT_(m_temp.fun, Q_EMPTY_SIG); + } + QF_CRIT_ENTRY(); + Q_INVARIANT_INCRIT(711, ip < (QHSM_MAX_NEST_DEPTH_ - 1)); + QF_CRIT_EXIT(); + + // the LCA not found yet? + if (iq == 0) { + + // exit source s +#ifndef Q_SPY + static_cast(QHSM_RESERVED_EVT_(s, Q_EXIT_SIG)); +#else + if (QHSM_RESERVED_EVT_(s, Q_EXIT_SIG) == Q_RET_HANDLED) { + QS_STATE_ACT_(QS_QEP_STATE_EXIT, s); + } +#endif // def Q_SPY + + // (f) check the rest of + // source->super == target->super->super... + iq = ip; + r = Q_RET_IGNORED; // indicate that the LCA NOT found + // note: iq is the fixed upper loop bound + do { + if (t == path[iq]) { // is this the LCA? + r = Q_RET_HANDLED; // indicate the LCA found + ip = iq - 1; // do not enter the LCA + break; // terminate the loop + } + --iq; // try lower superstate of target + } while (iq >= 0); + + if (r != Q_RET_HANDLED) { // the LCA still not found? + // (g) check each source->super->... + // for each target->super... + r = Q_RET_SUPER; // keep looping + while (r != Q_RET_HANDLED) { + // exit from t + if (QHSM_RESERVED_EVT_(t, Q_EXIT_SIG) == Q_RET_HANDLED) { + QS_STATE_ACT_(QS_QEP_STATE_EXIT, t); + // find superstate of t + static_cast(QHSM_RESERVED_EVT_(t, Q_EMPTY_SIG)); } + t = m_temp.fun; // set to super of t + iq = ip; + do { + if (t == path[iq]) { // is this the LCA? + ip = iq - 1; // do not enter the LCA + r = Q_RET_HANDLED; // break outer loop + break; // terminate the inner loop + } + --iq; // try lower superstate of target + } while (iq >= 0); } } } return ip; +} + +//............................................................................ +//! @private @memberof QHsm +void QHsm::enter_target_( + QStateHandler * const path, + std::int_fast8_t const depth, + std::uint_fast8_t const qsId) +{ +#ifndef Q_SPY + Q_UNUSED_PAR(qsId); +#endif + + QF_CRIT_STAT + + QF_CRIT_ENTRY(); + Q_REQUIRE_INCRIT(800, depth < QHSM_MAX_NEST_DEPTH_); + QF_CRIT_EXIT(); + + std::int_fast8_t ip = depth; + // execute state entry actions in the desired order... + // note: ip is the fixed upper loop bound + for (; ip >= 0; --ip) { + // enter path[ip] + if (QHSM_RESERVED_EVT_(path[ip], Q_ENTRY_SIG) + == Q_RET_HANDLED) + { + QS_STATE_ACT_(QS_QEP_STATE_ENTRY, path[ip]); + } + } + QStateHandler t = path[0]; + m_temp.fun = t; // update the next state + + // drill into the target hierarchy... + while (QHSM_RESERVED_EVT_(t, Q_INIT_SIG) == Q_RET_TRAN) { + + QS_TRAN_SEG_(QS_QEP_STATE_INIT, t, m_temp.fun); + + ip = 0; + path[0] = m_temp.fun; + + // find superstate + static_cast(QHSM_RESERVED_EVT_(m_temp.fun, Q_EMPTY_SIG)); + + // note: ip is the fixed upper loop bound + while ((m_temp.fun != t) && (ip < (QHSM_MAX_NEST_DEPTH_ - 1))) { + ++ip; + path[ip] = m_temp.fun; + // find superstate + static_cast(QHSM_RESERVED_EVT_( + m_temp.fun, Q_EMPTY_SIG)); + } + QF_CRIT_ENTRY(); + // too many state nesting levels or "malformed" HSM + Q_INVARIANT_INCRIT(891, ip < QHSM_MAX_NEST_DEPTH_); + QF_CRIT_EXIT(); + + m_temp.fun = path[0]; + // retrace the entry path in reverse (correct) order... + // note: ip is the fixed upper loop bound + do { + // enter path[ip] + if (QHSM_RESERVED_EVT_(path[ip], Q_ENTRY_SIG) == Q_RET_HANDLED) { + QS_STATE_ACT_(QS_QEP_STATE_ENTRY, path[ip]); + } + --ip; + } while (ip >= 0); + + t = path[0]; // current state becomes the new source + } } } // namespace QP -//$enddef${QEP::QHsm} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/src/qep_msm.cpp b/src/qep_msm.cpp index 7799bf2..7c2d259 100644 --- a/src/qep_msm.cpp +++ b/src/qep_msm.cpp @@ -1,72 +1,54 @@ -//$file${src::qf::qep_msm.cpp} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv -// -// Model: qpcpp.qm -// File: ${src::qf::qep_msm.cpp} -// -// This code has been generated by QM 5.2.5 . -// DO NOT EDIT THIS FILE MANUALLY. All your changes will be lost. +//============================================================================ +// QP/C++ Real-Time Embedded Framework (RTEF) +// Version 8.0.2 // -// This code is covered by the following QP license: -// License # : LicenseRef-QL-dual -// Issued to : Any user of the QP/C++ real-time embedded framework -// Framework(s) : qpcpp -// Support ends : 2023-12-31 -// License scope: +// Copyright (C) 2005 Quantum Leaps, LLC. All rights reserved. // -// 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: +// This software is dual-licensed under the terms of the open-source GNU +// General Public License (GPL) or under the terms of one of the closed- +// source Quantum Leaps commercial licenses. // // Redistributions in source code must retain this top-level comment block. // Plagiarizing this software to sidestep the license obligations is illegal. // -// Contact information: +// NOTE: +// The GPL does NOT permit the incorporation of this code into proprietary +// programs. Please contact Quantum Leaps for commercial licensing options, +// which expressly supersede the GPL and are designed explicitly for +// closed-source distribution. +// +// Quantum Leaps contact information: // // -// -//$endhead${src::qf::qep_msm.cpp} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -//! @file -//! @brief QP::QMsm implementation - +//============================================================================ #define QP_IMPL // this is QP implementation -#include "qep_port.hpp" // QEP port +#include "qp_port.hpp" // QP port +#include "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 // unnamed namespace for local definitions with internal linkage namespace { + Q_DEFINE_THIS_MODULE("qep_msm") -} // unnamed namespace -//============================================================================ -//$skip${QP_VERSION} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv -// Check for the minimum required QP version -#if (QP_VERSION < 700U) || (QP_VERSION != ((QP_RELEASE^4294967295U) % 0x3E8U)) -#error qpcpp version 7.0.0 or higher required -#endif -//$endskip${QP_VERSION} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +// maximum depth of entry levels in a MSM for tran. to history. +static constexpr std::int_fast8_t QMSM_MAX_ENTRY_DEPTH_ {4}; -//$define${QEP::QMsm} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv -namespace QP { +//! @cond INTERNAL -//${QEP::QMsm} ............................................................... -QMState const QMsm::msm_top_s = { +// top-state object for QMsm-style state machines +constexpr QP::QMState l_msm_top_s = { nullptr, nullptr, nullptr, @@ -74,422 +56,387 @@ QMState const QMsm::msm_top_s = { nullptr }; +} // unnamed namespace + +#ifdef Q_SPY +// helper macro to trace state action (entry/exit) +#define QS_STATE_ACT_(rec_, state_) \ + QS_CRIT_ENTRY(); \ + QS_BEGIN_PRE((rec_), qsId) \ + QS_OBJ_PRE(this); \ + QS_FUN_PRE(state_); \ + QS_END_PRE() \ + QS_CRIT_EXIT() + +// internal helper macro to top-most init +#define QS_TOP_INIT_(rec_, trg_) \ + QS_CRIT_ENTRY(); \ + QS_BEGIN_PRE((rec_), qsId) \ + QS_TIME_PRE(); \ + QS_OBJ_PRE(this); \ + QS_FUN_PRE(trg_); \ + QS_END_PRE() \ + QS_CRIT_EXIT() + +// internal helper macro to trace transition segment +#define QS_TRAN_SEG_(rec_, src_, trg_) \ + QS_CRIT_ENTRY(); \ + QS_BEGIN_PRE((rec_), qsId) \ + QS_OBJ_PRE(this); \ + QS_FUN_PRE(src_); \ + QS_FUN_PRE(trg_); \ + QS_END_PRE() \ + QS_CRIT_EXIT() + +// internal helper macro to trace transition begin/end +#define QS_TRAN0_(rec_, trg_) \ + QS_CRIT_ENTRY(); \ + QS_BEGIN_PRE((rec_), qsId) \ + QS_TIME_PRE(); \ + QS_SIG_PRE(e->sig); \ + QS_OBJ_PRE(this); \ + QS_FUN_PRE(trg_); \ + QS_END_PRE() \ + QS_CRIT_EXIT() + +// internal helper macro to trace regulsr transition +#define QS_TRAN_END_(rec_, src_, trg_) \ + QS_CRIT_ENTRY(); \ + QS_BEGIN_PRE((rec_), qsId) \ + QS_TIME_PRE(); \ + QS_SIG_PRE(e->sig); \ + QS_OBJ_PRE(this); \ + QS_FUN_PRE(src_); \ + QS_FUN_PRE(trg_); \ + QS_END_PRE() \ + QS_CRIT_EXIT() + +#else +#define QS_STATE_ACT_(rec_, state_) (static_cast(0)) +#define QS_TOP_INIT_(rec_, trg_) (static_cast(0)) +#define QS_TRAN_SEG_(rec_, src_, trg_) (static_cast(0)) +#define QS_TRAN0_(rec_, trg_) (static_cast(0)) +#define QS_TRAN_END_(rec_, src_, trg_) (static_cast(0)) +#endif + +//! @endcond + +//============================================================================ +namespace QP { + +//............................................................................ +QMsm::QMsm(QStateHandler const initial) noexcept + : QAsm() +{ + m_state.obj = &l_msm_top_s; // the current state (top) + m_temp.fun = initial; // the initial tran. handler +} -//${QEP::QMsm::init} ......................................................... +//............................................................................ void QMsm::init( void const * const e, - std::uint_fast8_t const qs_id) + std::uint_fast8_t const qsId) { - //! @pre the top-most initial transition must be initialized, and the - //! initial transition must not be taken yet. - Q_REQUIRE_ID(200, (m_temp.fun != nullptr) - && (m_state.obj == &msm_top_s)); +#ifndef Q_SPY + Q_UNUSED_PAR(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); + QF_CRIT_EXIT(); - QS_CRIT_STAT_ - QS_BEGIN_PRE_(QS_QEP_STATE_INIT, qs_id) - QS_OBJ_PRE_(this); // this state machine object - QS_FUN_PRE_(m_state.obj->stateHandler); // source handler - QS_FUN_PRE_(m_temp.tatbl->target->stateHandler); // target handler - QS_END_PRE_() + QS_TRAN_SEG_(QS_QEP_STATE_INIT, + m_state.obj->stateHandler, m_temp.tatbl->target->stateHandler); // set state to the last tran. target m_state.obj = m_temp.tatbl->target; // drill down into the state hierarchy with initial transitions... - do { - r = execTatbl_(m_temp.tatbl, qs_id); // execute the tran-action table - } 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 - QS_FUN_PRE_(m_state.obj->stateHandler); // the new current state - QS_END_PRE_() + while (r >= Q_RET_TRAN_INIT) { + // execute the tran. table + r = execTatbl_(m_temp.tatbl, qsId); + } - Q_UNUSED_PAR(qs_id); // when Q_SPY not defined + QS_TOP_INIT_(QS_QEP_INIT_TRAN, m_state.obj->stateHandler); } -//${QEP::QMsm::dispatch} ..................................................... +//............................................................................ void QMsm::dispatch( QEvt const * const e, - std::uint_fast8_t const qs_id) + std::uint_fast8_t const qsId) { - QMState const *s = m_state.obj; // store the current state - //! @pre current state must be initialized - Q_REQUIRE_ID(300, s != nullptr); - - QS_CRIT_STAT_ - QS_BEGIN_PRE_(QS_QEP_DISPATCH, qs_id) - QS_TIME_PRE_(); // time stamp - QS_SIG_PRE_(e->sig); // the signal of the event - QS_OBJ_PRE_(this); // this state machine object - QS_FUN_PRE_(s->stateHandler); // the current state handler - QS_END_PRE_() +#ifndef Q_SPY + Q_UNUSED_PAR(qsId); +#endif - // scan the state hierarchy up to the top state... + QMState const *s = m_state.obj; // store the current state QMState const *t = s; - QState r; - do { + + QF_CRIT_STAT + QF_CRIT_ENTRY(); + Q_REQUIRE_INCRIT(300, + (e != nullptr) + && (s != nullptr)); + QF_CRIT_EXIT(); + + QS_TRAN0_(QS_QEP_DISPATCH, s->stateHandler); + + // scan the state hierarchy up to the top state... + QState r = Q_RET_SUPER; + while (t != nullptr) { r = (*t->stateHandler)(this, e); // call state handler function - // event handled? (the most frequent case) - if (r >= Q_RET_HANDLED) { + if (r >= Q_RET_HANDLED) { // event handled? (the most frequent case) break; // done scanning the state hierarchy } - // event unhandled and passed to the superstate? - else if (r == Q_RET_SUPER) { - t = t->superstate; // advance to the superstate +#ifdef Q_SPY + if (r == Q_RET_UNHANDLED) { // event unhandled due to a guard? + QS_CRIT_ENTRY(); + QS_BEGIN_PRE(QS_QEP_UNHANDLED, qsId) + QS_SIG_PRE(e->sig); + QS_OBJ_PRE(this); + QS_FUN_PRE(t->stateHandler); + QS_END_PRE() + QS_CRIT_EXIT(); } - // event unhandled and passed to a submachine superstate? - else if (r == Q_RET_SUPER_SUB) { - t = m_temp.obj; // current host state of the submachie - } - // 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 - QS_FUN_PRE_(t->stateHandler); // the current state - QS_END_PRE_() +#endif + t = t->superstate; // advance to the superstate + } - 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 - - do { - // save the transition-action table before it gets clobbered - QMTranActTable const * const tatbl = m_temp.tatbl; - QHsmAttr tmp; // temporary to save intermediate values - - // was TRAN, TRAN_INIT, or TRAN_EP taken? - if (r <= Q_RET_TRAN_EP) { - m_temp.obj = nullptr; // clear - exitToTranSource_(s, t, qs_id); - r = execTatbl_(tatbl, qs_id); - s = m_state.obj; - } - // was a transition 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); - s = m_state.obj; - } - // was a transition segment to an exit point taken? - else if (r == Q_RET_TRAN_XP) { - tmp.act = m_state.act; // save XP action - m_state.obj = s; // restore the original state - r = (*tmp.act)(this); // execute the XP action - if (r == Q_RET_TRAN) { // XP -> TRAN ? - #ifdef Q_SPY - tmp.tatbl = m_temp.tatbl; // save m_temp - #endif // Q_SPY - exitToTranSource_(s, t, qs_id); - // take the tran-to-XP segment inside submachine - static_cast(execTatbl_(tatbl, qs_id)); - s = m_state.obj; - #ifdef Q_SPY - m_temp.tatbl = tmp.tatbl; // restore m_temp - #endif // Q_SPY - } - else if (r == Q_RET_TRAN_HIST) { // XP -> HIST ? - tmp.obj = m_state.obj; // save the history - m_state.obj = s; // restore the original state - #ifdef Q_SPY - s = m_temp.obj; // save m_temp - #endif // Q_SPY - exitToTranSource_(m_state.obj, t, qs_id); - // take the tran-to-XP segment inside submachine - static_cast(execTatbl_(tatbl, qs_id)); - #ifdef Q_SPY - m_temp.obj = s; // restore me->temp - #endif // Q_SPY - s = m_state.obj; - m_state.obj = tmp.obj; // restore the history - } - else { - // TRAN_XP must NOT be followed by any other tran type - Q_ASSERT_ID(330, r < Q_RET_TRAN); - } - } - else { - // no other return value should be produced - Q_ERROR_ID(340); - } + if (r >= Q_RET_TRAN) { // any kind of tran. taken? + QF_CRIT_ENTRY(); + // the tran. source state must not be NULL + Q_ASSERT_INCRIT(330, t != nullptr); + QF_CRIT_EXIT(); - t = s; // set target to the current state +#ifdef Q_SPY + QMState const * const ts = t; // tran. source for QS tracing +#endif // Q_SPY - } while (r >= Q_RET_TRAN); + if (r == Q_RET_TRAN_HIST) { // was it tran. to history? + QMState const * const hist = m_state.obj; // save history + m_state.obj = s; // restore the original state - QS_BEGIN_PRE_(QS_QEP_TRAN, qs_id) - QS_TIME_PRE_(); // time stamp - QS_SIG_PRE_(e->sig); // the signal of the event - QS_OBJ_PRE_(this); // this state machine object - QS_FUN_PRE_(ts->stateHandler); // the transition source - QS_FUN_PRE_(s->stateHandler); // the new active state - QS_END_PRE_() - } + QS_TRAN_SEG_(QS_QEP_TRAN_HIST, + t->stateHandler, hist->stateHandler); - #ifdef Q_SPY - // was the event handled? - else if (r == Q_RET_HANDLED) { - // internal tran. source can't be nullptr - Q_ASSERT_ID(340, t != nullptr); + // save the tran-action table before it gets clobbered + QMTranActTable const *tatbl = m_temp.tatbl; + exitToTranSource_(s, t, qsId); + static_cast(execTatbl_(tatbl, qsId)); + r = enterHistory_(hist, qsId); + s = m_state.obj; + t = s; // set target to the current state + } - QS_BEGIN_PRE_(QS_QEP_INTERN_TRAN, qs_id) - QS_TIME_PRE_(); // time stamp - QS_SIG_PRE_(e->sig); // the signal of the event - QS_OBJ_PRE_(this); // this state machine object - QS_FUN_PRE_(t->stateHandler); // the source state - QS_END_PRE_() + while (r >= Q_RET_TRAN) { + // save the tran-action table before it gets clobbered + QMTranActTable const *tatbl = m_temp.tatbl; + m_temp.obj = nullptr; // clear + exitToTranSource_(s, t, qsId); + r = execTatbl_(tatbl, qsId); + s = m_state.obj; + t = s; // set target to the current state + } + QS_TRAN_END_(QS_QEP_TRAN, ts->stateHandler, s->stateHandler); } - // 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_END_PRE_() - +#ifdef Q_SPY + else if (r == Q_RET_HANDLED) { // was the event handled? + QF_CRIT_ENTRY(); + // internal tran. source can't be NULL + Q_ASSERT_INCRIT(380, t != nullptr); + QF_CRIT_EXIT(); + + QS_TRAN0_(QS_QEP_INTERN_TRAN, t->stateHandler); } - #endif // Q_SPY - + else if (t == nullptr) { // event bubbled to the 'top' state? + QS_TRAN0_(QS_QEP_IGNORED, s->stateHandler); + } +#endif // Q_SPY else { // empty } - - Q_UNUSED_PAR(qs_id); // when Q_SPY not defined } -//${QEP::QMsm::isInState} .................................................... -bool QMsm::isInState(QMState const * const st) const noexcept { - bool inState = false; // assume that this MSM is not in 'state' +//............................................................................ +bool QMsm::isIn(QStateHandler const state) noexcept { + bool inState = false; // assume that this SM 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' + QMState const *s = m_state.obj; + while (s != nullptr) { + if (s->stateHandler == state) { // match found? + inState = true; break; } + s = s->superstate; // advance to the superstate } + 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; +//............................................................................ +QMState const * QMsm::childStateObj(QMState const * const parent) + const noexcept +{ + QMState const *s = m_state.obj; // start with current state + QMState const *child = s; + bool isFound = false; // assume the child NOT found - for (s = m_state.obj; s != nullptr; s = s->superstate) { + while (s != nullptr) { if (s == parent) { isFound = true; // child is found break; } - else { - child = s; - } - } - if (!isFound) { // still not found? - for (s = m_temp.obj; s != nullptr; s = s->superstate) { - if (s == parent) { - isFound = true; // child is found - break; - } - else { - child = s; - } - } + child = s; + s = s->superstate; } + QF_CRIT_STAT + QF_CRIT_ENTRY(); + Q_ASSERT_INCRIT(590, isFound); + QF_CRIT_EXIT(); - //! @post the child must be found - Q_ENSURE_ID(810, isFound); - - #ifdef Q_NASSERT +#ifdef Q_UNSAFE Q_UNUSED_PAR(isFound); - #endif +#endif return child; // return the child } -//${QEP::QMsm::execTatbl_} ................................................... +//............................................................................ QState QMsm::execTatbl_( QMTranActTable const * const tatbl, - std::uint_fast8_t const qs_id) + std::uint_fast8_t const qsId) { - //! @pre the transition-action table pointer must not be nullptr - Q_REQUIRE_ID(400, tatbl != nullptr); +#ifndef Q_SPY + Q_UNUSED_PAR(qsId); +#endif + + QF_CRIT_STAT + QF_CRIT_ENTRY(); + // precondition: + // - the tran-action table pointer must not be NULL + Q_REQUIRE_INCRIT(600, tatbl != nullptr); + QF_CRIT_EXIT(); QState r = Q_RET_NULL; - QS_CRIT_STAT_ - for (QActionHandler const *a = &tatbl->act[0]; *a != nullptr; ++a) { + QActionHandler const *a = &tatbl->act[0]; + while (*a != nullptr) { r = (*(*a))(this); // call the action through the 'a' pointer - #ifdef Q_SPY + ++a; +#ifdef Q_SPY if (r == Q_RET_ENTRY) { - - QS_BEGIN_PRE_(QS_QEP_STATE_ENTRY, qs_id) - QS_OBJ_PRE_(this); // this state machine object - QS_FUN_PRE_(m_temp.obj->stateHandler); // entered state handler - QS_END_PRE_() + QS_STATE_ACT_(QS_QEP_STATE_ENTRY, m_temp.obj->stateHandler); } else if (r == Q_RET_EXIT) { - - QS_BEGIN_PRE_(QS_QEP_STATE_EXIT, qs_id) - QS_OBJ_PRE_(this); // this state machine object - QS_FUN_PRE_(m_temp.obj->stateHandler); // exited state handler - QS_END_PRE_() + QS_STATE_ACT_(QS_QEP_STATE_EXIT, m_temp.obj->stateHandler); } else if (r == Q_RET_TRAN_INIT) { - - QS_BEGIN_PRE_(QS_QEP_STATE_INIT, qs_id) - QS_OBJ_PRE_(this); // this state machine object - QS_FUN_PRE_(tatbl->target->stateHandler); // source - QS_FUN_PRE_(m_temp.tatbl->target->stateHandler); // target - QS_END_PRE_() - } - else if (r == Q_RET_TRAN_EP) { - - QS_BEGIN_PRE_(QS_QEP_TRAN_EP, qs_id) - QS_OBJ_PRE_(this); // this state machine object - QS_FUN_PRE_(tatbl->target->stateHandler); // source - QS_FUN_PRE_(m_temp.tatbl->target->stateHandler); // target - QS_END_PRE_() - } - else if (r == Q_RET_TRAN_XP) { - - QS_BEGIN_PRE_(QS_QEP_TRAN_XP, qs_id) - QS_OBJ_PRE_(this); // this state machine object - QS_FUN_PRE_(tatbl->target->stateHandler); // source - QS_FUN_PRE_(m_temp.tatbl->target->stateHandler); // target - QS_END_PRE_() + QS_TRAN_SEG_(QS_QEP_STATE_INIT, + tatbl->target->stateHandler, + m_temp.tatbl->target->stateHandler); } else { // empty } - #endif // Q_SPY +#endif // Q_SPY } + QF_CRIT_ENTRY(); + Q_ASSERT_INCRIT(690, *a == nullptr); + QF_CRIT_EXIT(); - Q_UNUSED_PAR(qs_id); // when Q_SPY not defined m_state.obj = (r >= Q_RET_TRAN) ? m_temp.tatbl->target : tatbl->target; return r; } -//${QEP::QMsm::exitToTranSource_} ............................................ +//............................................................................ void QMsm::exitToTranSource_( - QMState const * s, + QMState const * const cs, QMState const * const ts, - std::uint_fast8_t const qs_id) + std::uint_fast8_t const qsId) { +#ifndef Q_SPY + Q_UNUSED_PAR(qsId); +#endif + QS_CRIT_STAT + // exit states from the current state to the tran. source state + QMState const *s = cs; while (s != ts) { // exit action provided in state 's'? if (s->exitAction != nullptr) { // execute the exit action - (*s->exitAction)(this); + static_cast((*s->exitAction)(this)); - QS_CRIT_STAT_ - QS_BEGIN_PRE_(QS_QEP_STATE_EXIT, qs_id) - QS_OBJ_PRE_(this); // this state machine object - QS_FUN_PRE_(s->stateHandler); // the exited state handler - QS_END_PRE_() + QS_STATE_ACT_(QS_QEP_STATE_EXIT, m_temp.obj->stateHandler); } - s = s->superstate; // advance to the superstate - - // reached the top of a submachine? - if (s == nullptr) { - s = m_temp.obj; // the superstate from QM_SM_EXIT() - Q_ASSERT_ID(510, s != nullptr); - } } - Q_UNUSED_PAR(qs_id); // when Q_SPY not defined } -//${QEP::QMsm::enterHistory_} ................................................ +//............................................................................ QState QMsm::enterHistory_( QMState const * const hist, - std::uint_fast8_t const qs_id) + std::uint_fast8_t const qsId) { - QMState const *s = hist; - QMState const *ts = m_state.obj; // transition source - QMState const *epath[MAX_ENTRY_DEPTH_]; - - QS_CRIT_STAT_ - - QS_BEGIN_PRE_(QS_QEP_TRAN_HIST, qs_id) - QS_OBJ_PRE_(this); // this state machine object - QS_FUN_PRE_(ts->stateHandler); // source state handler - QS_FUN_PRE_(hist->stateHandler); // target state handler - QS_END_PRE_() +#ifndef Q_SPY + Q_UNUSED_PAR(qsId); +#endif - std::int_fast8_t i = 0; // entry path index - while (s != ts) { + // record the entry path from current state to history + QMState const *epath[QMSM_MAX_ENTRY_DEPTH_]; + QMState const *s = hist; + std::int_fast8_t i = 0; // tran. entry path index & fixed upper loop bound + while ((s != m_state.obj) && (i < QMSM_MAX_ENTRY_DEPTH_)) { if (s->entryAction != nullptr) { - Q_ASSERT_ID(620, i < MAX_ENTRY_DEPTH_); epath[i] = s; ++i; } s = s->superstate; - if (s == nullptr) { - ts = s; // force exit from the for-loop - } } + QF_CRIT_STAT + QF_CRIT_ENTRY(); + Q_ASSERT_INCRIT(810, i <= QMSM_MAX_ENTRY_DEPTH_); + QF_CRIT_EXIT(); // retrace the entry path in reverse (desired) order... - while (i > 0) { - --i; + // NOTE: i the fixed upper loop bound + for (i = i - 1; i >= 0; --i) { // run entry action in epath[i] - (*epath[i]->entryAction)(this); + static_cast((*epath[i]->entryAction)(this)); - QS_BEGIN_PRE_(QS_QEP_STATE_ENTRY, qs_id) - QS_OBJ_PRE_(this); - QS_FUN_PRE_(epath[i]->stateHandler); // entered state handler - QS_END_PRE_() + QS_STATE_ACT_(QS_QEP_STATE_ENTRY, epath[i]->stateHandler); } - 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; + QState r = Q_RET_NULL; if (hist->initAction != nullptr) { - r = (*hist->initAction)(this); // execute the transition action - } - else { - r = Q_RET_NULL; - } + r = (*hist->initAction)(this); // execute the tran. action - Q_UNUSED_PAR(qs_id); // when Q_SPY not defined + QS_TRAN_SEG_(QS_QEP_STATE_INIT, + hist->stateHandler, m_temp.tatbl->target->stateHandler); + } return r; } +//............................................................................ +QMState const * QMsm::topQMState() const noexcept { + return &l_msm_top_s; +} + } // namespace QP -//$enddef${QEP::QMsm} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/src/qep_port.hpp b/src/qep_port.hpp deleted file mode 100644 index 60f5c1e..0000000 --- a/src/qep_port.hpp +++ /dev/null @@ -1,52 +0,0 @@ -/// @file -/// @brief QEP/C++ port, generic C++11 compiler -/// @cond -///*************************************************************************** -/// Last updated for version 6.8.0 -/// Last updated on 2022-03-20 -/// -/// Q u a n t u m L e a P s -/// ------------------------ -/// Modern Embedded Software -/// -/// Copyright (C) 2022 Victor Chavez. -/// Copyright (C) 2005-2020 Quantum Leaps. All rights reserved. -/// -/// This program is open source software: you can redistribute it and/or -/// modify it under the terms of the GNU General Public License as published -/// by the Free Software Foundation, either version 3 of the License, or -/// (at your option) any later version. -/// -/// Alternatively, this program may be distributed and modified under the -/// terms of Quantum Leaps commercial licenses, which expressly supersede -/// the GNU General Public License and are specifically designed for -/// licensees interested in retaining the proprietary status of their code. -/// -/// This program is distributed in the hope that it will be useful, -/// but WITHOUT ANY WARRANTY; without even the implied warranty of -/// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -/// GNU General Public License for more details. -/// -/// You should have received a copy of the GNU General Public License -/// along with this program. If not, see . -/// -/// Contact information: -/// -/// -///*************************************************************************** -/// @endcond - -#ifndef QEP_PORT_HPP -#define QEP_PORT_HPP - -#include // Exact-width types. C++11 Standard - -// enable QP/Spy software tracing instrumentation -#define Q_SPY 1U - -#include "qep.hpp" // QEP platform-independent public interface - -//! no-return function specifier (GCC) -#define Q_NORETURN __attribute__ ((noreturn)) void - -#endif // QEP_PORT_HPP diff --git a/src/qequeue.hpp b/src/qequeue.hpp index 03fd257..029526c 100644 --- a/src/qequeue.hpp +++ b/src/qequeue.hpp @@ -1,334 +1,111 @@ -//$file${include::qequeue.hpp} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv -// -// Model: qpcpp.qm -// File: ${include::qequeue.hpp} -// -// This code has been generated by QM 5.2.5 . -// DO NOT EDIT THIS FILE MANUALLY. All your changes will be lost. +//============================================================================ +// QP/C++ Real-Time Embedded Framework (RTEF) +// Version 8.0.2 // -// This code is covered by the following QP license: -// License # : LicenseRef-QL-dual -// Issued to : Any user of the QP/C++ real-time embedded framework -// Framework(s) : qpcpp -// Support ends : 2023-12-31 -// License scope: +// Copyright (C) 2005 Quantum Leaps, LLC. All rights reserved. // -// 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: +// This software is dual-licensed under the terms of the open-source GNU +// General Public License (GPL) or under the terms of one of the closed- +// source Quantum Leaps commercial licenses. // // Redistributions in source code must retain this top-level comment block. // Plagiarizing this software to sidestep the license obligations is illegal. // -// Contact information: +// NOTE: +// The GPL does NOT permit the incorporation of this code into proprietary +// programs. Please contact Quantum Leaps for commercial licensing options, +// which expressly supersede the GPL and are designed explicitly for +// closed-source distribution. +// +// Quantum Leaps contact information: // // -// -//$endhead${include::qequeue.hpp} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -//! @file -//! @brief platform-independent fast "raw" thread-safe event queue interface -//! -//! @details -//! This header file must be included in all QF ports that use native QF -//! event queue for active objects. Also, this file needs to be included -//! in the QP/C++ library when the application uses QActive::defer() / -//! QActive::recall(). Finally, this file is also needed when the "raw" -//! thread-safe queues are used for communication between active objects -//! and non-framework entities, such as ISRs, device drivers, or legacy -//! code. - +//============================================================================ #ifndef QEQUEUE_HPP_ #define QEQUEUE_HPP_ #ifndef QF_EQUEUE_CTR_SIZE - - //! The size (in bytes) of the ring-buffer counters used in the - //! native QF event queue implementation. Valid values: 1U, 2U, or 4U; - //! default 1U. - //! @details - //! This macro can be defined in the QF port file (qf_port.hpp) to - //! configure the QP::QEQueueCtr type. Here the macro is not defined - //! so the default of 1 byte is chosen. #define QF_EQUEUE_CTR_SIZE 1U #endif namespace QP { #if (QF_EQUEUE_CTR_SIZE == 1U) - - //! The data type to store the ring-buffer counters based on - //! the macro #QF_EQUEUE_CTR_SIZE. - //! @details - //! The dynamic range of this data type determines the maximum length - //! of the ring buffer managed by the native QF event queue. using QEQueueCtr = std::uint8_t; #elif (QF_EQUEUE_CTR_SIZE == 2U) using QEQueueCtr = std::uint16_t; -#elif (QF_EQUEUE_CTR_SIZE == 4U) - using QEQueueCtr = std::uint32_t; #else - #error "QF_EQUEUE_CTR_SIZE defined incorrectly, expected 1U, 2U, or 4U" + #error QF_EQUEUE_CTR_SIZE defined incorrectly, expected 1U or 2U #endif +class QEvt; // forward declaration + } // namespace QP //============================================================================ -//$declare${QF::QEQueue} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv namespace QP { -//${QF::QEQueue} ............................................................. -//! Native QF Event Queue class -//! -//! @details -//! This structure describes the native QF event queue, which can be used as -//! the event queue for active objects, or as a simple "raw" event queue for -//! thread-safe event passing among non-framework entities, such as ISRs, -//! device drivers, or other third-party components.
-//! -//! The native QF event queue is configured by defining the macro -//! #QF_EQUEUE_TYPE as QP::QEQueue in the specific QF port header file.
-//! -//! The QP::QEQueue class contains only data members for managing an event -//! queue, but does not contain the storage for the queue buffer, which must -//! be provided externally during the queue initialization.
-//! -//! The event queue can store only event pointers, not the whole events. The -//! internal implementation uses the standard ring-buffer plus one external -//! location that optimizes the queue operation for the most frequent case -//! of empty queue.
-//! -//! The QP::QEQueue class is used with two sets of functions. One set is for -//! the active object event queue, which needs to block the active object -//! task when the event queue is empty and unblock it when events are posted -//! to the queue. The interface for the native active object event queue -//! consists of the following functions: QActive::post(), QActive::postLIFO(), -//! and QActive::get_(). Additionally the function QEQueue::init() is used -//! to initialize the queue.
-//! -//! The other set of functions, uses this class as a simple "raw" event -//! queue to pass events between entities other than active objects, such as -//! ISRs. The "raw" event queue is not capable of blocking on the get() -//! operation, but is still thread-safe because it uses QF critical section -//! to protect its integrity. The interface for the "raw" thread-safe queue -//! consists of the following functions: QP::QEQueue::post(), -//! QP::QEQueue::postLIFO(), and QP::QEQueue::get(). Additionally the -//! function QP::QEQueue::init() is used to initialize the queue. -//! -//! @note -//! Most event queue operations (both the active object queues and the "raw" -//! queues) internally use the QF critical section. You should be careful -//! not to invoke those operations from other critical sections when nesting -//! of critical sections is not supported. class QEQueue { private: - - //! pointer to event at the front of the queue - //! - //! @details - //! All incoming and outgoing events pass through the m_frontEvt location. - //! When the queue is empty (which is most of the time), the extra - //! m_frontEvt location allows to bypass the ring buffer altogether, - //! greatly optimizing the performance of the queue. Only bursts of events - //! engage the ring buffer.
- //! - //! The additional role of this attribute is to indicate the empty status - //! of the queue. The queue is empty if the m_frontEvt location is nullptr. QEvt const * volatile m_frontEvt; - - //! pointer to the start of the ring buffer - QEvt const ** m_ring; - - //! offset of the end of the ring buffer from the start of the buffer + 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; + + // friends... friend class QActive; friend class QTicker; friend class QXMutex; friend class QXThread; public: - - //! public default constructor - QEQueue() noexcept; - - //! Initializes the native QF event queue - //! - //! @details - //! Initialize the event queue by giving it the storage for the - //! ring buffer. - //! - //! @param[in] qSto an array of pointers to QP::QEvt to serve as the - //! ring buffer for the event queue - //! @param[in] qLen the length of the qSto[] buffer (in QP::QEvt pointers) - //! - //! @note - //! The actual capacity of the queue is qLen + 1, because of the extra - //! location forntEvt. - //! - //! @note - //! This function is also used to initialize the event queues of active - //! objects in the built-int QV, QK and QXK kernels, as well as other - //! QP ports to OSes/RTOSes that do provide a suitable message queue. + QEQueue() noexcept + : m_frontEvt(nullptr), + m_ring(nullptr), + m_end(0U), + m_head(0U), + m_tail(0U), + m_nFree(0U), + m_nMin(0U) + {} void init( - QEvt const * qSto[], + QEvt const * * const qSto, std::uint_fast16_t const qLen) noexcept; - - //! Posts (FIFO) an event to the "raw" thread-safe QF event queue - //! - //! @details - //! Post an event to the "raw" thread-safe event queue using the - //! First-In-First-Out (FIFO) order. - //! - //! @param[in] e pointer to the event to be posted to the queue - //! @param[in] margin number of required free slots in the queue after - //! posting the event. The special value - //! QF::NO_MARGIN means that this function will - //! assert if posting - //! @param[in] qs_id QS-id of this state machine (for QS local filter) - //! - //! @note - //! The QF::NO_MARGIN value of the `margin` argument is special and - //! denotes situation when the post() operation is assumed to succeed - //! (event delivery guarantee). An assertion fires, when the event cannot - //! be delivered in this case. - //! - //! @returns 'true' (success) when the posting succeeded with the provided - //! margin and 'false' (failure) when the posting fails. - //! - //! @note - //! This function can be called from any task context or ISR context. - //! - //! @sa QP::QEQueue::postLIFO(), QP::QEQueue::get() bool post( QEvt const * const e, std::uint_fast16_t const margin, - std::uint_fast8_t const qs_id) noexcept; - - //! Posts (LIFO) an event to the "raw" thread-safe QF event queue - //! - //! @details - //! Post an event to the "raw" thread-safe event queue using the - //! Last-In-First-Out (LIFO) order. - //! - //! @param[in] e pointer to the event to be posted to the queue - //! @param[in] qs_id QS-id of this state machine (for QS local filter) - //! - //! @attention - //! The LIFO policy should be used only with great __caution__, - //! because it alters the order of events in the queue. - //! - //! @note - //! This function can be called from any task context or ISR context. - //! - //! @note - //! This function is used for the "raw" thread-safe queues and __not__ - //! for the queues of active objects. - //! - //! @sa - //! QEQueue::post(), QEQueue::get(), QActive::defer() + std::uint_fast8_t const qsId) noexcept; void postLIFO( QEvt const * const e, - std::uint_fast8_t const qs_id) noexcept; - - //! Gets an event from the "raw" thread-safe QF event queue - //! - //! @details - //! Retrieves an event from the front of the "raw" thread-safe queue and - //! returns a pointer to this event to the caller. - //! - //! @param[in] qs_id QS-id of this state machine (for QS local filter) - //! - //! @returns - //! pointer to event at the front of the queue, if the queue is - //! not empty and NULL if the queue is empty. - //! - //! @note - //! this function is used for the "raw" thread-safe queues and **not** - //! for the queues of active objects. - //! - //! @sa - //! QEQueue::post(), QEQueue::postLIFO(), QActive::recall() - QEvt const * get(std::uint_fast8_t const qs_id) noexcept; - - //! Gets the number of free slots currently in "raw" thread-safe - //! QF event queue - //! - //! @note - //! This operation needs to be used with caution because the - //! number of free entries can change unexpectedly. The main intent for - //! using this operation is in conjunction with event deferral. In this - //! case the queue is accessed only from a single thread (by a single AO), - //! so the number of free entries cannot change unexpectedly. - //! - //! @sa QP::QMActive::defer(), QP::QMActive::recall() + 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"). - //! - //! @details - //! This operation needs to be used with caution because the - //! "low-watermark" can change unexpectedly. The main intent for using - //! this operation is to get an idea of queue usage to size the queue - //! adequately. - //! - //! @returns the minimum number of free entries ever in the queue - //! since init. QEQueueCtr getNMin() const noexcept { return m_nMin; } - - //! "raw" thread-safe QF event queue operation to find out if the queue - //! is empty - //! @note - //! This operation needs to be used with caution because the - //! queue status can change unexpectedly. The main intent for using - //! this operation is in conjunction with event deferral. In this case - //! the queue is accessed only from a single thread (by a single AO), - //! so no other entity can post events to the queue. - //! - //! @sa QP::QMActive::defer(), QP::QMActive::recall() bool isEmpty() const noexcept { return m_frontEvt == nullptr; } private: - - //! disallow copying of QP::QEQueue QEQueue(QEQueue const & other) = delete; - - //! disallow copying of QP::QEQueue QEQueue & operator=(QEQueue const & other) = delete; + void postFIFO_( + QEvt const * const e, + void const * const sender); }; // class QEQueue } // namespace QP -//$enddecl${QF::QEQueue} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ #endif // QEQUEUE_HPP_ diff --git a/src/qf.hpp b/src/qf.hpp deleted file mode 100644 index aabb9f4..0000000 --- a/src/qf.hpp +++ /dev/null @@ -1,1880 +0,0 @@ -//$file${include::qf.hpp} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv -// -// Model: qpcpp.qm -// File: ${include::qf.hpp} -// -// This code has been generated by QM 5.2.5 . -// DO NOT EDIT THIS FILE MANUALLY. All your changes will be lost. -// -// This code is covered by the following QP license: -// License # : LicenseRef-QL-dual -// Issued to : Any user of the QP/C++ real-time embedded framework -// Framework(s) : qpcpp -// Support ends : 2023-12-31 -// License scope: -// -// Copyright (C) 2005 Quantum Leaps, LLC . -// -// SPDX-License-Identifier: GPL-3.0-or-later OR LicenseRef-QL-commercial -// -// This software is dual-licensed under the terms of the open source GNU -// General Public License version 3 (or any later version), or alternatively, -// under the terms of one of the closed source Quantum Leaps commercial -// licenses. -// -// The terms of the open source GNU General Public License version 3 -// can be found at: -// -// The terms of the closed source Quantum Leaps commercial licenses -// can be found at: -// -// Redistributions in source code must retain this top-level comment block. -// Plagiarizing this software to sidestep the license obligations is illegal. -// -// Contact information: -// -// -// -//$endhead${include::qf.hpp} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -//! @file -//! @brief QF/C++ platform-independent public interface. - -#ifndef QF_HPP_ -#define QF_HPP_ - -#ifdef Q_EVT_CTOR -#include // for placement new -#include // for va_list -#endif // Q_EVT_CTOR - -//============================================================================ -// Global namespace... -//$declare${QF-config} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv - -//${QF-config::QF_MAX_ACTIVE} ................................................ -#ifndef QF_MAX_ACTIVE -//! Maximum number of active objects (configurable value in qf_port.hpp) -//! Valid values: [1U..64U]; default 32U -#define QF_MAX_ACTIVE 32U -#endif // ndef QF_MAX_ACTIVE - -//${QF-config::QF_MAX_ACTIVE exceeds the maximu~} ............................ -#if (QF_MAX_ACTIVE > 64U) -#error QF_MAX_ACTIVE exceeds the maximum of 64U; -#endif // (QF_MAX_ACTIVE > 64U) - -//${QF-config::QF_MAX_TICK_RATE} ............................................. -#ifndef QF_MAX_TICK_RATE -//! Maximum number of clock rates (configurable value in qf_port.hpp) -//! Valid values: [0U..15U]; default 1U -#define QF_MAX_TICK_RATE 1U -#endif // ndef QF_MAX_TICK_RATE - -//${QF-config::QF_MAX_TICK_RATE exceeds the max~} ............................ -#if (QF_MAX_TICK_RATE > 15U) -#error QF_MAX_TICK_RATE exceeds the maximum of 15U; -#endif // (QF_MAX_TICK_RATE > 15U) - -//${QF-config::QF_MAX_EPOOL} ................................................. -#ifndef QF_MAX_EPOOL -//! Maximum number of event pools (configurable value in qf_port.hpp) -//! Valid values: [0U..15U]; default 3U -//! -//! @note -//! #QF_MAX_EPOOL set to zero means that dynamic events are NOT configured -//! and should not be used in the application. -#define QF_MAX_EPOOL 3U -#endif // ndef QF_MAX_EPOOL - -//${QF-config::QF_MAX_EPOOL exceeds the maximum~} ............................ -#if (QF_MAX_EPOOL > 15U) -#error QF_MAX_EPOOL exceeds the maximum of 15U; -#endif // (QF_MAX_EPOOL > 15U) - -//${QF-config::QF_TIMEEVT_CTR_SIZE} .......................................... -#ifndef QF_TIMEEVT_CTR_SIZE -//! Size of the QTimeEvt counter (configurable value in qf_port.hpp) -//! Valid values: 1U, 2U, or 4U; default 4U -#define QF_TIMEEVT_CTR_SIZE 4U -#endif // ndef QF_TIMEEVT_CTR_SIZE - -//${QF-config::QF_TIMEEVT_CTR_SIZE defined inco~} ............................ -#if (QF_TIMEEVT_CTR_SIZE != 1U) && (QF_TIMEEVT_CTR_SIZE != 2U) && (QF_TIMEEVT_CTR_SIZE != 4U) -#error QF_TIMEEVT_CTR_SIZE defined incorrectly, expected 1U, 2U, or 4U; -#endif // (QF_TIMEEVT_CTR_SIZE != 1U) && (QF_TIMEEVT_CTR_SIZE != 2U) && (QF_TIMEEVT_CTR_SIZE != 4U) - -//${QF-config::QF_EVENT_SIZ_SIZE} ............................................ -#ifndef QF_EVENT_SIZ_SIZE -//! Size of the event-size (configurable value in qf_port.hpp) -//! Valid values: 1U, 2U, or 4U; default 2U -#define QF_EVENT_SIZ_SIZE 2U -#endif // ndef QF_EVENT_SIZ_SIZE - -//${QF-config::QF_EVENT_SIZ_SIZE defined incorr~} ............................ -#if (QF_EVENT_SIZ_SIZE != 1U) && (QF_EVENT_SIZ_SIZE != 2U) && (QF_EVENT_SIZ_SIZE != 4U) -#error QF_EVENT_SIZ_SIZE defined incorrectly, expected 1U, 2U, or 4U; -#endif // (QF_EVENT_SIZ_SIZE != 1U) && (QF_EVENT_SIZ_SIZE != 2U) && (QF_EVENT_SIZ_SIZE != 4U) -//$enddecl${QF-config} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -//============================================================================ -//$declare${QF-types} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv -namespace QP { - -//${QF-types::QPSetBits} ..................................................... -#if (8U < QF_MAX_ACTIVE) && (QF_MAX_ACTIVE <= 16U) -//! bitmask for the internal representation of QPSet elements -using QPSetBits = std::uint16_t; -#endif // (8U < QF_MAX_ACTIVE) && (QF_MAX_ACTIVE <= 16U) - -//${QF-types::QPSetBits} ..................................................... -#if (16 < QF_MAX_ACTIVE) -using QPSetBits = std::uint32_t; -#endif // (16 < QF_MAX_ACTIVE) - -//${QF-types::QPSetBits} ..................................................... -#if (QF_MAX_ACTIVE <= 8U) -using QPSetBits = std::uint8_t; -#endif // (QF_MAX_ACTIVE <= 8U) - -//${QF-types::QTimeEvtCtr} ................................................... -#if (QF_TIMEEVT_CTR_SIZE== 2U) -//! Data type to store the block-size defined based on the macro -//! #QF_TIMEEVT_CTR_SIZE. -//! -//! @details -//! The dynamic range of this data type determines the maximum block -//! size that can be managed by the pool. -using QTimeEvtCtr = std::uint16_t; -#endif // (QF_TIMEEVT_CTR_SIZE== 2U) - -//${QF-types::QTimeEvtCtr} ................................................... -#if (QF_TIMEEVT_CTR_SIZE== 1U) -using QTimeEvtCtr = std::uint8_t; -#endif // (QF_TIMEEVT_CTR_SIZE== 1U) - -//${QF-types::QTimeEvtCtr} ................................................... -#if (QF_TIMEEVT_CTR_SIZE== 4U) -using QTimeEvtCtr = std::uint32_t; -#endif // (QF_TIMEEVT_CTR_SIZE== 4U) - -//${QF-types::QPrioSpec} ..................................................... -//! Priority specification for Active Objects in QP -//! -//! @details -//! Active Object priorities in QP are integer numbers in the range -//! [1..#QF_MAX_ACTIVE], whereas the special priority number 0 is reserved -//! for the lowest-priority idle thread. The QP framework uses the *direct* -//! priority numbering, in which higher numerical values denote higher -//! urgency. For example, an AO with priority 32 has higher urgency than -//! an AO with priority 23. -//! -//! QP::QPrioSpec allows an application developer to assign **two** -//! priorities to a given AO (see also Q_PRIO()): -//! -//! 1. The "QF-priority", which resides in the least-significant byte -//! of the QP::QPrioSpec data type. The "QF-priority" must be **unique** -//! for each thread in the system and higher numerical values represent -//! higher urgency (direct pirority numbering). -//! -//! 2. The "preemption-threshold" priority, which resides in the most- -//! significant byte of the ::QPrioSpec data type. The second priority -//! cannot be lower than the "QF-priority", but does NOT need to be -//! unuque. -//! -//! In the QP native preemptive kernels, like QK and QXK, the "preemption- -//! threshold" priority is used as to implement the "preemption-threshold -//! scheduling" (PTS). It determines the conditions under which a given -//! thread can be *preempted* by other threads. Specifically, a given -//! thread can be preempted only by another thread with a *higher* -//! priority than the "preemption-threshold" of the original thread. -//! -//! ![QF-priority and preemption-threshold relations](qp-prio.png) -//! -//! @note -//! For backwards-compatibility, QP::QPrioSpec data type might contain only -//! the "QF-priority" component (and the "preemption-threshold" component -//! left at zero). In that case, the "preemption-threshold" will be assumed -//! to be the same as the "QF-priority". This corresponds exactly to the -//! previous semantics of AO priority. -//! -//! @remark -//! When QP runs on top of 3rd-party kernels/RTOSes or general-purpose -//! operating systems, sthe second priority can have different meaning, -//! depending on the specific RTOS/GPOS used. -using QPrioSpec = std::uint16_t; - -//${QF-types::QSchedStatus} .................................................. -//! The scheduler lock status used in some real-time kernels -using QSchedStatus = std::uint_fast16_t; - -//${QF-types::QPSet} ......................................................... -//! Priority Set of up to #QF_MAX_ACTIVE elements -//! -//! @details -//! The priority set represents the set of active objects that are ready to -//! run and need to be considered by the scheduling algorithm. The set is -//! capable of storing up to #QF_MAX_ACTIVE priority levels, which can be -//! configured in the rage 1..64, inclusive. -class QPSet { -public: - -#if (QF_MAX_ACTIVE <= 32) - //! bitmask with a bit for each element - QPSetBits volatile m_bits; -#endif // (QF_MAX_ACTIVE <= 32) - -#if (32 < QF_MAX_ACTIVE) - //! bitmasks with a bit for each element - QPSetBits volatile m_bits[2]; -#endif // (32 < QF_MAX_ACTIVE) - -public: - - //! Make the priority set empty - void setEmpty() noexcept { - #if (QF_MAX_ACTIVE <= 32U) - m_bits = 0U; - #else - m_bits[0] = 0U; - m_bits[1] = 0U; - #endif - } - - //! Return 'true' if the priority set is empty - bool isEmpty() const noexcept { - #if (QF_MAX_ACTIVE <= 32U) - return (m_bits == 0U); - #else - return (m_bits[0] == 0U) ? (m_bits[1] == 0U) : false; - #endif - } - - //! Return 'true' if the priority set is NOT empty - bool notEmpty() const noexcept { - #if (QF_MAX_ACTIVE <= 32U) - return (m_bits != 0U); - #else - return (m_bits[0] != 0U) ? true : (m_bits[1] != 0U); - #endif - } - - //! Return 'true' if the priority set has the element n. - bool hasElement(std::uint_fast8_t const n) const noexcept { - #if (QF_MAX_ACTIVE <= 32U) - return (m_bits & (1U << (n - 1U))) != 0U; - #else - return (n <= 32U) - ? ((m_bits[0] & (static_cast(1) << (n - 1U))) != 0U) - : ((m_bits[1] & (static_cast(1) << (n - 33U))) != 0U); - #endif - } - - //! insert element `n` into the set (n = 1..QF_MAX_ACTIVE) - void insert(std::uint_fast8_t const n) noexcept { - #if (QF_MAX_ACTIVE <= 32U) - m_bits = (m_bits | (1U << (n - 1U))); - #else - if (n <= 32U) { - m_bits[0] = (m_bits[0] - | (static_cast(1) << (n - 1U))); - } - else { - m_bits[1] = (m_bits[1] - | (static_cast(1) << (n - 33U))); - } - #endif - } - - //! Remove element `n` from the set (n = 1U..64U) - void remove(std::uint_fast8_t const n) noexcept { - #if (QF_MAX_ACTIVE <= 32U) - m_bits = (m_bits & static_cast( - ~(static_cast(1) << (n - 1U)))); - #else - if (n <= 32U) { - (m_bits[0] = (m_bits[0] - & ~(static_cast(1) << (n - 1U)))); - } - else { - (m_bits[1] = (m_bits[1] - & ~(static_cast(1) << (n - 33U)))); - } - #endif - } - - //! Find the maximum element in the set, returns zero if the set is empty - std::uint_fast8_t findMax() const noexcept { - #if (QF_MAX_ACTIVE <= 32U) - return QF_LOG2(m_bits); - #else - return (m_bits[1] != 0U) - ? (QF_LOG2(m_bits[1]) + 32U) - : (QF_LOG2(m_bits[0])); - #endif - } - -#ifndef QF_LOG2 - //! Log-base-2 calculation when hardware acceleration - //! is NOT provided (#QF_LOG2 not defined). - std::uint_fast8_t QF_LOG2(QP::QPSetBits x) const noexcept; -#endif // ndef QF_LOG2 -}; // class QPSet - -//${QF-types::QSubscrList} ................................................... -//! Subscriber List (for publish-subscribe) -//! -//! @details -//! This data type represents a set of Active Objects that subscribe to -//! a given signal. The set is represented as priority-set, where each bit -//! corresponds to the unique QF-priority of an AO (see QP::QPrioSpec). -using QSubscrList = QPSet; - -} // namespace QP -//$enddecl${QF-types} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -//$declare${QF::QActive} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv -namespace QP { - -//${QF::QActive} ............................................................. -//! QP::QActive active object class (based on the QP::QHsm-style -//! implementation strategy) -//! -//! @details -//! Active objects are encapsulated tasks (each containing an event queue and -//! a state machine) that communicate with one another asynchronously by -//! sending and receiving events. Within an active object, events are -//! processed in a run-to-completion (RTC) fashion, while QF encapsulates -//! all the details of thread-safe event exchange and queuing.
-//! -//! QP::QActive represents an active object that uses the QP::QHsm-style -//! implementation strategy for state machines. This strategy is tailored -//! to manual coding, but it is also supported by the QM modeling tool. -//! The resulting code is slower than in the QP::QMsm-style implementation -//! strategy. -//! -//! @note -//! QP::QActive is not intended to be instantiated directly, but rather serves -//! as the abstract base class for derivation of active objects in the -//! applications. -//! -//! @sa QP::QMActive -//! -//! @usage -//! The following example illustrates how to derive an active object from -//! QP::QActive. -//! @include qf_qactive.cpp -class QActive : public QP::QHsm { -public: - -#ifdef QF_EQUEUE_TYPE - //! OS-dependent event-queue type - //! - //! @details - //! The type of the queue depends on the underlying operating system or - //! a kernel. Many kernels support "message queues" that can be adapted - //! to deliver QF events to the active object. Alternatively, QF provides - //! a native event queue implementation that can be used as well. - //! - //! @note - //! The native QF event queue is configured by defining the macro - //! #QF_EQUEUE_TYPE as QP::QEQueue. - QF_EQUEUE_TYPE m_eQueue; -#endif // def QF_EQUEUE_TYPE - -#ifdef QF_OS_OBJECT_TYPE - //! OS-dependent per-thread object - //! - //! @details - //! This data might be used in various ways, depending on the QF port. - //! In some ports m_osObject is used to block the calling thread when - //! the native QF queue is empty. In other QF ports the OS-dependent - //! object might be used differently. - QF_OS_OBJECT_TYPE m_osObject; -#endif // def QF_OS_OBJECT_TYPE - -#ifdef QF_THREAD_TYPE - //! OS-dependent representation of the thread of the active object - //! - //! @details - //! This data might be used in various ways, depending on the QF port. - //! In some ports m_thread is used store the thread handle. In other ports - //! m_thread can be a pointer to the Thread-Local-Storage (TLS). - QF_THREAD_TYPE m_thread; -#endif // def QF_THREAD_TYPE - - //! QF-priority [1..#QF_MAX_ACTIVE] of this AO. - //! @sa QP::QPrioSpec - std::uint8_t m_prio; - - //! preemption-threshold [1..#QF_MAX_ACTIVE] of this AO. - //! @sa QP::QPrioSpec - std::uint8_t m_pthre; - -private: - friend class QTimeEvt; - friend class QTicker; - -#ifdef QXK_HPP_ - friend class QXThread; -#endif // def QXK_HPP_ - -#ifdef QXK_HPP_ - friend class QXMutex; -#endif // def QXK_HPP_ - -#ifdef QXK_HPP_ - friend class QXSemaphore; -#endif // def QXK_HPP_ - -#ifdef Q_UTEST - friend class QActiveDummy; -#endif // def Q_UTEST - friend class GuiQActive; - friend class GuiQMActive; - -public: - - //! Internal array of registered active objects - static QActive * registry_[QF_MAX_ACTIVE + 1U]; - - //! pointer to the array of all subscriber AOs for a given event signal - static QSubscrList * subscrList_; - - //! The maximum published signal (the size of the subscrList_ array) - static enum_t maxPubSignal_; - -protected: - - //! protected constructor (abstract class) - QActive(QStateHandler const initial) noexcept; - -public: - - //! Starts execution of an active object and registers the object - //! with the framework - //! - //! @details - //! Starts execution of the AO and registers the AO with the framework. - //! - //! @param[in] prioSpec priority specification of the AO containing the - //! QF-priority and (optionally) preemption-threshold of this AO - //! (for preemptive kernels that support it). See also QP::QPrioSpec. - //! @param[in] qSto pointer to the storage for the ring buffer of the - //! event queue - //! @param[in] qLen length of the event queue [# QP::QEvt* pointers] - //! @param[in] stkSto pointer to the stack storage (might be nullptr) - //! @param[in] stkSize stack size [bytes] - //! @param[in] par pointer to an extra parameter (might be nullptr) - //! - //! @usage - //! The following example shows starting an AO when a per-task stack - //! is needed: - //! @include qf_start.cpp - virtual void start( - QPrioSpec const prioSpec, - QEvt const * * const qSto, - std::uint_fast16_t const qLen, - void * const stkSto, - std::uint_fast16_t const stkSize, - void const * const par); - - //! Overloaded start function (no initialization parameter) - virtual void start( - QPrioSpec const prioSpec, - QEvt const * * const qSto, - std::uint_fast16_t const qLen, - void * const stkSto, - std::uint_fast16_t const stkSize) - { - this->start(prioSpec, qSto, qLen, stkSto, stkSize, nullptr); - } - -#ifdef QF_ACTIVE_STOP - //! Stops execution of an active object and removes it from the - //! framework's supervision - //! - //! @attention - //! QActive::stop() must be called only from the AO that is about - //! to stop its execution. By that time, any pointers or references - //! to the AO are considered invalid (dangling) and it becomes - //! illegal for the rest of the application to post events to the AO. - void stop(); -#endif // def QF_ACTIVE_STOP - - //! Posts an event `e` directly to the event queue of the active object - //! using the First-In-First-Out (FIFO) policy. - //! - //! @details - //! Direct event posting is the simplest asynchronous communication - //! method available in QF. - //! - //! @param[in] e pointer to the event to be posted - //! @param[in] margin number of required free slots in the queue - //! after posting the event or QF::NO_MARGIN. - //! @param[in] sender pointer to a sender object (used in QS only) - //! - //! @returns - //! 'true' (success) if the posting succeeded (with the provided margin) - //! and 'false' (failure) when the posting fails. - //! - //! @attention - //! For `margin` == QF::NO_MARGIN, this function will assert internally - //! if the event posting fails. In that case, it is unnecessary to check - //! the retrun value from this function. - //! - //! @note - //! This function might be implemented differentyl in various QP/C++ - //! ports. The provided implementation assumes that the QP::QEQueue - //! class is used for the QP::QActive event queue. - //! - //! @usage - //! @include qf_post.cpp - //! - //! @sa - //! QActive::postLIFO() - virtual bool post_( - QEvt const * const e, - std::uint_fast16_t const margin, - void const * const sender) noexcept; - - //! Posts an event `e` directly to the event queue of the active object - //! using the Last-In-First-Out (LIFO) policy. - //! - //! @details - //! The LIFO policy should be used only for self-posting and with caution, - //! because it alters order of events in the queue. - //! - //! @param[in] e pointer to the event to be posted - //! - //! @attention - //! This function asserts internally if the posting fails. - //! - //! @note - //! This function might be implemented differentyl in various QP/C++ - //! ports. The provided implementation assumes that the QP::QEQueue - //! class is used for the QActive event queue. - //! - //! @sa - //! QActive::post() - virtual void postLIFO(QEvt const * const e) noexcept; - - //! Get an event from the event queue of an active object - //! - //! @details - //! The behavior of this function depends on the kernel used in the - //! QF port. For built-in kernels (Vanilla or QK) the function can be - //! called only when the queue is not empty, so it doesn't block. For - //! a blocking kernel/OS the function can block and wait for delivery - //! of an event. - //! - //! @returns - //! A pointer to the received event. The returned pointer is guaranteed - //! to be valid (can't be nullptr). - //! - //! @note - //! This function might be implemented differentyl in various QP/C++ - //! ports. The provided implementation assumes that the QP::QEQueue - //! class is used for the QActive event queue. - QEvt const * get_() noexcept; - - //! Subscribes for delivery of signal `sig` to the active object - //! - //! @details - //! This function is part of the Publish-Subscribe event delivery - //! mechanism available in QF. Subscribing to an event means that the - //! framework will start posting all published events with a given signal - //! `sig` to the event queue of the active object. - //! - //! @param[in] sig event signal to subscribe - //! - //! The following example shows how the Table active object subscribes - //! to three signals in the initial transition: - //! @include qf_subscribe.cpp - //! - //! @sa - //! QActive::publish_(), QActive::unsubscribe(), and - //! QActive::unsubscribeAll() - void subscribe(enum_t const sig) const noexcept; - - //! Unsubscribes from the delivery of signal `sig` to the active object - //! - //! @details - //! This function is part of the Publish-Subscribe event delivery - //! mechanism available in QF. Un-subscribing from an event means that - //! the framework will stop posting published events with a given signal - //! `sig` to the event queue of the active object. - //! - //! @param[in] sig event signal to unsubscribe - //! - //! @note - //! Due to the latency of event queues, an active object should NOT - //! assume that a given signal `sig` will never be dispatched to the - //! state machine of the active object after un-subscribing from that - //! signal. The event might be already in the queue, or just about to - //! be posted and the un-subscribe operation will not flush such events. - //! - //! @note - //! Un-subscribing from a signal that has never been subscribed in the - //! first place is considered an error and QF will raise an assertion. - //! - //! @sa - //! QActive::publish_(), QActive::subscribe(), and - //! QActive::unsubscribeAll() - void unsubscribe(enum_t const sig) const noexcept; - - //! Unsubscribes from the delivery of all signals to the active object - //! - //! @details - //! This function is part of the Publish-Subscribe event delivery - //! mechanism available in QF. Un-subscribing from all events means that - //! the framework will stop posting any published events to the event - //! queue of the active object. - //! - //! @note - //! Due to the latency of event queues, an active object should NOT - //! assume that no events will ever be dispatched to the state machine of - //! the active object after un-subscribing from all events. - //! The events might be already in the queue, or just about to be posted - //! and the un-subscribe operation will not flush such events. Also, the - //! alternative event-delivery mechanisms, such as direct event posting or - //! time events, can be still delivered to the event queue of the active - //! object. - //! - //! @sa - //! QActive::publish_(), QActive::subscribe(), and QActive::unsubscribe() - void unsubscribeAll() const noexcept; - - //! Defer an event to a given separate event queue - //! - //! @details - //! This function is part of the event deferral support. An active object - //! uses this function to defer an event `e` to the QF-supported native - //! event queue `eq`. QF correctly accounts for another outstanding - //! reference to the event and will not recycle the event at the end of - //! the RTC step. Later, the active object might recall one event at a - //! time from the event queue. - //! - //! @param[in] eq pointer to a "raw" thread-safe queue to recall - //! an event from. - //! @param[in] e pointer to the event to be deferred - //! - //! @returns - //! 'true' (success) when the event could be deferred and 'false' - //! (failure) if event deferral failed due to overflowing the queue. - //! - //! An active object can use multiple event queues to defer events of - //! different kinds. - //! - //! @sa - //! QActive::recall(), QP::QEQueue, QActive::flushDeferred() - bool defer( - QEQueue * const eq, - QEvt const * const e) const noexcept; - - //! Recall a deferred event from a given event queue - //! - //! @details - //! This function is part of the event deferral support. An active object - //! uses this function to recall a deferred event from a given QF - //! event queue. Recalling an event means that it is removed from the - //! deferred event queue `eq` and posted (LIFO) to the event queue of - //! the active object. - //! - //! @param[in] eq pointer to a "raw" thread-safe queue to recall - //! an event from. - //! - //! @returns - //! 'true' if an event has been recalled and 'false' if not. - //! - //! @note - //! An active object can use multiple event queues to defer events of - //! different kinds. - //! - //! @sa - //! QActive::recall(), QActive::postLIFO_(), QP::QEQueue - bool recall(QEQueue * const eq) noexcept; - - //! Flush the specified deferred queue 'eq' - //! - //! @details - //! This function is part of the event deferral support. An active object - //! can use this function to flush a given QF event queue. The function - //! makes sure that the events are not leaked. - //! - //! @param[in] eq pointer to a "raw" thread-safe queue to flush. - //! - //! @returns - //! the number of events actually flushed from the queue. - //! - //! @sa - //! QActive::defer(), QActive::recall(), QP::QEQueue - std::uint_fast16_t flushDeferred(QEQueue * const eq) const noexcept; - - //! Get the priority of the active object. - std::uint_fast8_t getPrio() const noexcept { - return static_cast(m_prio); - } - -protected: - - //! Set the priority of the active object. - void setPrio(QPrioSpec const prio) noexcept { - m_prio = static_cast(prio & 0xFFU); - m_pthre = static_cast(prio >> 8U); - } - -public: - - //! Generic setting of additional attributes (useful in QP ports) - void setAttr( - std::uint32_t attr1, - void const * attr2 = nullptr); - -#ifdef QF_OS_OBJECT_TYPE - //! accessor to the OS-object for extern "C" functions, such as - //! the QK or QXK schedulers - QF_OS_OBJECT_TYPE & getOsObject() noexcept { - return m_osObject; - } -#endif // def QF_OS_OBJECT_TYPE - -#ifdef QF_THREAD_TYPE - //! accessor to the Thread for extern "C" functions, such as - //! the QK or QXK schedulers - QF_THREAD_TYPE & getThread() noexcept { - return m_thread; - } -#endif // def QF_THREAD_TYPE - - //! Publish-subscribe initialization - //! - //! @details - //! This function initializes the publish-subscribe facilities of QF and must - //! be called exactly once before any subscriptions/publications occur in - //! the application. - //! - //! @param[in] subscrSto pointer to the array of subscriber lists - //! @param[in] maxSignal the dimension of the subscriber array and at - //! the same time the maximum signal that can be - //! published or subscribed. - //! - //! The array of subscriber-lists is indexed by signals and provides a mapping - //! between the signals and subscriber-lists. The subscriber-lists are - //! bitmasks of type QP::QSubscrList, each bit in the bit mask corresponding - //! to the unique priority of an active object. The size of the - //! QP::QSubscrList bitmask depends on the value of the #QF_MAX_ACTIVE macro. - //! - //! @note - //! The publish-subscribe facilities are optional, meaning that you might - //! choose not to use publish-subscribe. In that case calling QF::psInit() - //! and using up memory for the subscriber-lists is unnecessary. - //! - //! @sa - //! QP::QSubscrList - //! - //! @usage - //! The following example shows the typical initialization sequence of QF: - //! @include qf_main.cpp - static void psInit( - QSubscrList * const subscrSto, - enum_t const maxSignal) noexcept; - - //! Publish event to all subscribers of a given signal `e->sig` - //! - //! @details - //! This function posts (using the FIFO policy) the event @a e to **all** - //! active objects that have subscribed to the signal @a e->sig, which is - //! called _multicasting_. The multicasting performed in this function is - //! very efficient based on reference-counting inside the published event - //! ("zero-copy" event multicasting). This function is designed to be - //! callable from any part of the system, including ISRs, device drivers, - //! and active objects. - //! - //! @note - //! To avoid any unexpected re-ordering of events posted into AO queues, - //! the event multicasting is performed with scheduler **locked**. - //! However, the scheduler is locked only up to the priority level of - //! the highest-priority subscriber, so any AOs of even higher priority, - //! which did not subscribe to this event are *not* affected. - static void publish_( - QEvt const * const e, - void const * const sender, - std::uint_fast8_t const qs_id) noexcept; - - //! Thread routine for executing an active object `act` - static void thread_(QActive * act); - - //! Register this active object to be managed by the framework - //! - //! @details - //! This function adds a given active object to the active objects - //! managed by the QF framework. It should not be called by the - //! application directly, only through the function QActive::start(). - //! - //! @note - //! The priority of the active object a should be set before calling - //! this function. - //! - //! @sa QActive::unregister_() - void register_() noexcept; - - //! Un-register the active object from the framework. - //! - //! @details - //! This function un-registers a given active object from the active objects - //! managed by the QF framework. It should not be called by the QP ports. - //! - //! @param[in] a pointer to the active object to remove from the - //! framework. - //! - //! @note - //! The active object that is removed from the framework can no longer - //! participate in any event exchange. - //! - //! @sa QActive::register_() - void unregister_() noexcept; - -#ifdef QF_ISR_API - //! the "FromISR" variant used in the QP port to "FreeRTOS" - virtual bool postFromISR_( - QEvt const * const e, - std::uint_fast16_t const margin, - void * par, - void const * const sender) noexcept; -#endif // def QF_ISR_API - -#ifdef QF_ISR_API - //! the "FromISR" variant used in the QP port to "FreeRTOS" - static void publishFromISR_( - QEvt const * e, - void * par, - void const * sender) noexcept; -#endif // def QF_ISR_API -}; // class QActive - -} // namespace QP -//$enddecl${QF::QActive} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -//$declare${QF::QMActive} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv -namespace QP { - -//${QF::QMActive} ............................................................ -//! QMActive active object (based on QP::QMsm implementation) -//! -//! @details -//! QP::QMActive represents an active object that uses the QP::QMsm-style -//! state machine implementation strategy. This strategy requires the use of -//! the QM modeling tool to generate state machine code automatically, but -//! the code is faster than in the QP::QHsm-style implementation strategy -//! and needs less run-time support (smaller event-processor). -//! -//! @note -//! QP::QMActive is not intended to be instantiated directly, but rather -//! serves as the base class for derivation of active objects in the -//! applications. -//! -//! @sa QP::QActive -//! -//! @usage -//! The following example illustrates how to derive an active object from -//! QP::QMActive. -//! @include qf_qmactive.cpp -class QMActive : public QP::QActive { -public: - - //! inherited from QP::QHsm, but disallowed in QP::QMActive - using QHsm::isIn; - - //! inherited from QP::QHsm, but disallowed in QP::QMActive - using QHsm::state; - - //! inherited from QP::QHsm, but disallowed in QP::QMActive - using QHsm::childState; - -protected: - - //! protected constructor (abstract class) - QMActive(QStateHandler const initial) noexcept - : QActive(initial) - { - m_temp.fun = initial; - } - -public: - - //! delegate to QP::QMsm::init() - void init( - void const * const e, - std::uint_fast8_t const qs_id) override; - - //! delegate to QP::QMsm::init() - void init(std::uint_fast8_t const qs_id) override; - - //! delegate to QMsm::dispatch() - void dispatch( - QEvt const * const e, - std::uint_fast8_t const qs_id) override; - - //! Tests if a given state is part of the active state configuration - bool isInState(QMState const * const st) const noexcept; - - //! Return the current active state object (read only) - QMState const * stateObj() const noexcept { - return m_state.obj; - } - - //! Return the current active state object (read only) - QMState const * childStateObj(QMState const * const parent) const noexcept; - -#ifdef Q_SPY - //! Get the current state handler of the QP::QMsm - QStateHandler getStateHandler() noexcept override; -#endif // def Q_SPY -}; // class QMActive - -} // namespace QP -//$enddecl${QF::QMActive} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -//$declare${QF::QTimeEvt} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv -namespace QP { - -//${QF::QTimeEvt} ............................................................ -//! Time Event class (inherits QP:QEvt) -//! -//! @details -//! Time events are special QF events equipped with the notion of time -//! passage. The basic usage model of the time events is as follows. An -//! active object allocates one or more QTimeEvt objects (provides the -//! storage for them). When the active object needs to arrange for a timeout, -//! it arms one of its time events to fire either just once (one-shot) or -//! periodically. Each time event times out independently from the others, -//! so a QF application can make multiple parallel timeout requests (from the -//! same or different active objects). When QF detects that the appropriate -//! moment has arrived, it inserts the time event directly into the -//! recipient's event queue. The recipient then processes the time event just -//! like any other event. -//!
-//! Time events, as any other QF events derive from the QP::QEvt base -//! class. Typically, you will use a time event as-is, but you can also -//! further derive more specialized time events from it by adding some more -//! data members and/or specialized functions that operate on the specialized -//! time events. -//!
-//! Internally, the armed time events are organized into a bi-directional -//! linked list. This linked list is scanned in every invocation of the -//! QTimeEvt::tick_() function. Only armed (timing out) time events are in the -//! list, so only armed time events consume CPU cycles. -//! -//! @note -//! QF manages the time events in the macro TICK_X(), which must be called -//! periodically, from the clock tick ISR or from the special QP::QTicker -//! active object. -//! -//! @note -//! Even though QP::QTimeEvt is a subclass of QP::QEvt, QP::QTimeEvt instances -//! can NOT be allocated dynamically from event pools. In other words, it is -//! illegal to allocate QP::QTimeEvt instances with the Q_NEW() or Q_NEW_X() -//! macros. -class QTimeEvt : public QP::QEvt { -private: - - //! link to the next time event in the list - QTimeEvt * volatile m_next; - - //! the active object that receives the time events - //! - //! @details - //! The m_act pointer is reused inside the QP implementation to hold - //! the head of the list of newly armed time events. - void * m_act; - - //! the internal down-counter of the time event - //! - //! @details - //! The down-counter is decremented by 1 in every TICK_X() - //! invocation. The time event fires (gets posted or published) when - //! the down-counter reaches zero. - QTimeEvtCtr volatile m_ctr; - - //! the interval for the periodic time event (zero for the one-shot - //! time event) - //! - //! @details - //! The value of the interval is re-loaded to the internal - //! down-counter when the time event expires, so that the time event - //! keeps timing out periodically. - QTimeEvtCtr m_interval; - -public: - - //! heads of linked lists of time events, one for every clock tick rate - static QTimeEvt timeEvtHead_[QF_MAX_TICK_RATE]; - -private: - friend class QXThread; - -public: - - //! The Time Event constructor - QTimeEvt( - QActive * const act, - enum_t const sgnl, - std::uint_fast8_t const tickRate = 0U); - - //! Arm a time event (one shot or periodic) for event posting - //! - //! @details - //! Arms a time event to fire in a specified number of clock ticks and - //! with a specified interval. If the interval is zero, the time event - //! is armed for one shot ('one-shot' time event). The time event gets - //! directly posted (using the FIFO policy) into the event queue of the - //! host active object. After posting, a one-shot time event gets - //! automatically disarmed while a periodic time event (interval != 0) - //! is automatically re-armed. - //! - //! A time event can be disarmed at any time by calling - //! QP::QTimeEvt::disarm(). Also, a time event can be re-armed to fire - //! in a different number of clock ticks by calling QP::QTimeEvt::rearm(). - //! - //! @param[in] nTicks number of clock ticks (at the associated rate) - //! to rearm the time event with. - //! @param[in] interval interval (in clock ticks) for periodic time event. - //! - //! @attention - //! Arming an already armed time event is __not__ allowed and is - //! considered a programming error. The QP/C++ framework will assert - //! if it detects an attempt to arm an already armed time event. - //! - //! @usage - //! The following example shows how to arm a one-shot time event from a - //! state machine of an active object: - //! @include qf_state.cpp - void armX( - QTimeEvtCtr const nTicks, - QTimeEvtCtr const interval = 0U) noexcept; - - //! Disarm a time event - //! - //! @details - //! Disarm the time event so it can be safely reused. - //! - //! @returns - //! 'true' if the time event was truly disarmed, that is, it was running. - //! The return of 'false' means that the time event was not truly - //! disarmed because it was not running. The 'false' return is only - //! possible for one-shot time events that have been automatically - //! disarmed upon expiration. In that case the 'false' return means that - //! the time event has already been posted or published and should be - //! expected in the active object's state machine. - //! - //! @note - //! there is no harm in disarming an already disarmed time event - bool disarm() noexcept; - - //! Rearm a time event - //! - //! @details - //! Rearms a time event with a new number of clock ticks. This function - //! can be used to adjust the current period of a periodic time event - //! or to prevent a one-shot time event from expiring (e.g., a watchdog - //! time event). Rearming a periodic timer leaves the interval unchanged - //! and is a convenient method to adjust the phasing of a periodic - //! time event. - //! - //! @param[in] nTicks number of clock ticks (at the associated rate) - //! to rearm the time event with. - //! - //! @returns - //! 'true' if the time event was running as it was re-armed. The 'false' - //! return means that the time event was not truly rearmed because it was - //! not running. The 'false' return is only possible for one-shot time - //! events that have been automatically disarmed upon expiration. In that - //! case the 'false' return means that the time event has already been - //! posted and should be expected in the active object's state machine. - bool rearm(QTimeEvtCtr const nTicks) noexcept; - - //! Check the "was disarmed" status of a time event - //! - //! @details - //! Useful for checking whether a one-shot time event was disarmed in the - //! QTimeEvt_disarm() operation. - //! - //! @returns - //! 'true' if the time event was truly disarmed in the last - //! QTimeEvt::disarm() operation. The 'false' return means that the time - //! event was not truly disarmed, because it was not running at that time. - //! The 'false' return is only possible for one-shot time events that - //! have been automatically disarmed upon expiration. In this case the - //! 'false' return means that the time event has already been posted or - //! published and should be expected in the active object's event queue. - //! - //! @note - //! This function has a **side effect** of setting the "was disarmed" - //! status, which means that the second and subsequent times this - //! function is called the function will return 'true'. - bool wasDisarmed() noexcept; - - //! Gets the active object associated with the time event - void const * getAct() const noexcept { - return m_act; - } - - //! Gets the current count of the time event - QTimeEvtCtr getCtr() const noexcept { - return m_ctr; - } - - //! Gets the interval of the time event - QTimeEvtCtr getInterval() const noexcept { - return m_interval; - } - - //! Processes all armed time events at every clock tick - //! - //! @details - //! This function must be called periodically from a time-tick ISR or from - //! a task so that QF can manage the timeout events assigned to the given - //! system clock tick rate. - //! - //! @param[in] tickRate system clock tick rate serviced [1..15]. - //! @param[in] sender pointer to a sender object (used in QS only). - //! - //! @attention - //! this function should be called only via the macros TICK_X() or TICK() - //! - //! @note - //! the calls to QTimeEvt::tick_() with different `tickRate` parameter can - //! preempt each other. For example, higher clock tick rates might be - //! serviced from interrupts while others from tasks (active objects). - static void tick_( - std::uint_fast8_t const tickRate, - void const * const sender) noexcept; - -#ifdef Q_UTEST - //! Processes one clock tick for QUTest - static void tick1_( - std::uint_fast8_t const tickRate, - void const * const sender); -#endif // def Q_UTEST - -#ifdef QF_ISR_API - //! the "FromISR" variant used in the QP port to "FreeRTOS" - static void tickFromISR_( - std::uint_fast8_t const tickRate, - void * par, - void const * sender) noexcept; -#endif // def QF_ISR_API - - //! Returns true if all time events are inactive and false - //! any time event is active - //! - //! @details - //! Find out if any time events are armed at the given clock tick rate. - //! - //! @param[in] tickRate system clock tick rate to find out about. - //! - //! @returns - //! 'true' if no time events are armed at the given tick rate and - //! 'false' otherwise. - //! - //! @note - //! This function should be called in critical section. - static bool noActive(std::uint_fast8_t const tickRate) noexcept; - - //! encapsulate the cast the m_act attribute to QActive* - QActive * toActive() noexcept { - return static_cast(m_act); - } - - //! encapsulate the cast the `QTimeEvt.m_act` attribute - QTimeEvt * toTimeEvt() noexcept { - return static_cast(m_act); - } - -private: - - //! private default constructor only for friends - //! - //! @note - //! private default ctor for internal use only - QTimeEvt(); - - //! private copy constructor to disallow copying of QTimeEvts - QTimeEvt(QTimeEvt const & other) = delete; - - //! disallow copying of QP::QTimeEvt - QTimeEvt & operator=(QTimeEvt const & other) = delete; -}; // class QTimeEvt - -} // namespace QP -//$enddecl${QF::QTimeEvt} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -//$declare${QF::QTicker} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv -namespace QP { - -//${QF::QTicker} ............................................................. -//! "Ticker" Active Object class (inherits QP::QActive) -//! -//! @details -//! QP::QTicker is an efficient active object specialized to process -//! QF system clock tick at a specified tick frequency [0..#QF_MAX_TICK_RATE]. -//! Placing system clock tick processing in an active object allows you -//! to remove the non-deterministic TICK_X() processing from the interrupt -//! level and move it into the thread-level, where you can prioritize it -//! as low as you wish. -//! -//! @usage -//! The following example illustrates use of QP::QTicker active objects: -//! @include qf_ticker.cpp -class QTicker : public QP::QActive { -public: - - //! constructor - explicit QTicker(std::uint_fast8_t const tickRate) noexcept; - void init( - void const * const e, - std::uint_fast8_t const qs_id) override; - void init(std::uint_fast8_t const qs_id) override; - void dispatch( - QEvt const * const e, - std::uint_fast8_t const qs_id) override; - bool post_( - QEvt const * const e, - std::uint_fast16_t const margin, - void const * const sender) noexcept override; -}; // class QTicker - -} // namespace QP -//$enddecl${QF::QTicker} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -//$declare${QF::QF-base} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv -namespace QP { -namespace QF { - -//${QF::QF-base::intLock_} ................................................... -//! Interrupt lock up-down counter (used in some QF ports) -extern std::uint_fast8_t volatile intLock_; - -//${QF::QF-base::intNest_} ................................................... -//! Interrupt nesting up-down counter (used in some QF ports) -extern std::uint_fast8_t volatile intNest_; - -//${QF::QF-base::init} ....................................................... -//! QF initialization -//! -//! @details -//! Initializes QF and must be called exactly once before any other QF -//! function. Typcially, QP::QF::init() is called from main() even before -//! initializing the Board Support Package (BSP). -//! -//! @note -//! QP::QF::init() clears the internal QF variables, so that the framework -//! can start correctly even if the startup code fails to clear the -//! uninitialized data (as is required by the C Standard). -void init(); - -//${QF::QF-base::stop} ....................................................... -//! Function invoked by the application layer to stop the QF -//! application and return control to the OS/Kernel -//! -//! @details -//! This function stops the QF application. After calling this function, -//! QF attempts to gracefully stop the application. This graceful shutdown -//! might take some time to complete. The typical use of this function is -//! for terminating the QF application to return back to the operating -//! system or for handling fatal errors that require shutting down -//! (and possibly re-setting) the system. -//! -//! @attention -//! After calling QF::stop() the application must terminate and cannot -//! continue. In particular, QF::stop() is **not** intended to be followed -//! by a call to QF::init() to "resurrect" the application. -//! -//! @sa QP::QF::onCleanup() -void stop(); - -//${QF::QF-base::run} ........................................................ -//! Transfers control to QF to run the application -//! -//! @details -//! QF::run() is typically called from your startup code after you -//! initialize the QF and start at least one active object with -//! QActive::start(). -//! -//! @returns -//! In QK, the QP::QF::run() function does not return. -int_t run(); - -//${QF::QF-base::onStartup} .................................................. -//! Startup QF callback (defined in applications/ports) -//! -//! @details -//! The purpose of the QF::onStartup() callback is to configure and enable -//! hardware interrupts. The callback is invoked from QF::run(), right before -//! starting the underlying real-time kernel. By that time, the application -//! is considered ready to receive and service interrupts. -//! -//! This function is application-specific and is not implemented in QF, but -//! rather in the Board Support Package (BSP) for the given application. -void onStartup(); - -//${QF::QF-base::onCleanup} .................................................. -//! Cleanup QF callback (defined in applications/ports) -void onCleanup(); - -//${QF::QF-base::getQueueMin} ................................................ -//! This function returns the minimum of free entries of the given -//! event queue of an active object (indicated by priority `prio`) -//! -//! @details -//! Queries the minimum of free ever present in the given event queue of -//! an active object with priority `prio`, since the active object -//! was started. -//! -//! @note -//! QF::getQueueMin() is available only when the native QF event queue -//! implementation is used. Requesting the queue minimum of an unused -//! priority level raises an assertion in the QF. (A priority level -//! becomes used in QF after the call to QActive::register_().) -//! -//! @param[in] prio Priority of the active object, whose queue is queried -//! -//! @returns -//! the minimum of free ever present in the given event queue of an active -//! object with priority `prio`, since the active object was started. -std::uint_fast16_t getQueueMin(std::uint_fast8_t const prio) noexcept; - -//${QF::QF-base::psInit} ..................................................... -//! Publish-subscribe initialization -//! -//! @deprecated -//! superseded by QActive::psInit() -inline void psInit( - QSubscrList * const subscrSto, - enum_t const maxSignal) noexcept -{ - QActive::psInit(subscrSto, maxSignal); -} - -//${QF::QF-base::publish_} ................................................... -//! Publish event to all subscribers of a given signal `e->sig` -//! -//! @deprecated -//! superseded by QActive::publish_() -inline void publish_( - QEvt const * const e, - void const * const sender, - std::uint_fast8_t const qs_id) noexcept -{ - QActive::publish_(e, sender, qs_id); -} - -//${QF::QF-base::tick_} ...................................................... -//! Processes all armed time events at every clock tick -//! -//! @deprecated -//! superseded by QTimeEvt::tick_() -inline void tick_( - std::uint_fast8_t const tickRate, - void const * const sender) noexcept -{ - QTimeEvt::tick_(tickRate, sender); -} - -//${QF::QF-base::NO_MARGIN} .................................................. -//! Special value of margin that causes asserting failure in case -//! event allocation or event posting fails -constexpr std::uint_fast16_t NO_MARGIN {0xFFFFU}; - -} // namespace QF -} // namespace QP -//$enddecl${QF::QF-base} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -//$declare${QF::QF-dyn} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv -namespace QP { -namespace QF { - -//${QF::QF-dyn::poolInit} .................................................... -//! Event pool initialization for dynamic allocation of events. -//! -//! @details -//! This function initializes one event pool at a time and must be called -//! exactly once for each event pool before the pool can be used. -//! -//! @param[in] poolSto pointer to the storage for the event pool -//! @param[in] poolSize size of the storage for the pool in bytes -//! @param[in] evtSize the block-size of the pool in bytes, which -//! determines the maximum size of events that -//! can be allocated from the pool -//! @note -//! You might initialize many event pools by making many consecutive calls -//! to the QF::poolInit() function. However, for the simplicity of the -//! internal implementation, you must initialize event pools in the -//! ascending order of the event size. -//! -//! @note -//! The actual number of events available in the pool might be actually -//! less than (`poolSize / evtSize`) due to the internal alignment of -//! the blocks that the pool might perform. You can always check the -//! capacity of the pool by calling QF::getPoolMin(). -//! -//! @note -//! The dynamic allocation of events is optional, meaning that you might -//! choose not to use dynamic events. In that case calling -//! QF::poolInit() and using up memory for the memory blocks is -//! unnecessary. -//! -//! @sa QF initialization example for QF::init() -void poolInit( - void * const poolSto, - std::uint_fast32_t const poolSize, - std::uint_fast16_t const evtSize) noexcept; - -//${QF::QF-dyn::newX_} ....................................................... -//! Internal QF implementation of creating new dynamic mutable event -//! -//! @details -//! Allocates an event dynamically from one of the QF event pools. -//! -//! @param[in] evtSize the size (in bytes) of the event to allocate -//! @param[in] margin the number of un-allocated events still available -//! in a given event pool after the allocation -//! completes. The special value QF::NO_MARGIN -//! means that this function will assert if allocation -//! fails. -//! @param[in] sig the signal to be assigned to the allocated event -//! -//! @returns -//! pointer to the newly allocated event. This pointer can be nullptr -//! only if margin!=0 and the event cannot be allocated with the -//! specified margin still available in the given pool. -//! -//! @note -//! The internal QF function QF::newX_() raises an assertion when -//! the margin argument is QF::NO_MARGIN and allocation of the event -//! turns out to be impossible due to event pool depletion, or incorrect -//! (too big) size of the requested event. -//! -//! @note -//! The application code should not call this function directly. -//! The only allowed use is thorough the macros Q_NEW() or Q_NEW_X(). -QEvt * newX_( - std::uint_fast16_t const evtSize, - std::uint_fast16_t const margin, - enum_t const sig) noexcept; - -//${QF::QF-dyn::gc} .......................................................... -//! Recycle a dynamic event -//! -//! @details -//! This function implements a garbage collector for dynamic events. -//! Only dynamic events are candidates for recycling. (A dynamic event -//! is one that is allocated from an event pool, which is determined as -//! non-zero `e->poolId_` attribute.) Next, the function decrements the -//! reference counter of the event (`e->refCtr_`), and recycles the event -//! only if the counter drops to zero (meaning that no more references -//! are outstanding for this event). The dynamic event is recycled by -//! returning it to the pool from which it was originally allocated. -//! -//! @param[in] e pointer to the event to recycle -//! -//! @note -//! QF invokes the garbage collector at all appropriate contexts, when -//! an event can become garbage (automatic garbage collection), so the -//! application code should have no need to call QF::gc() directly. -//! The QF::gc() function is exposed only for special cases when your -//! application sends dynamic events to the "raw" thread-safe queues -//! (see QP::QEQueue). Such queues are processed outside of QF and the -//! automatic garbage collection is **NOT** performed for these events. -//! In this case you need to call QF::gc() explicitly. -void gc(QEvt const * const e) noexcept; - -//${QF::QF-dyn::poolGetMaxBlockSize} ......................................... -//! Obtain the block size of any registered event pools -std::uint_fast16_t poolGetMaxBlockSize() noexcept; - -//${QF::QF-dyn::newRef_} ..................................................... -//! Internal QF implementation of creating new event reference -//! -//! @details -//! Creates and returns a new reference to the current event e -//! -//! @param[in] e pointer to the current event -//! @param[in] evtRef the event reference -//! -//! @returns -//! the newly created reference to the event `e` -//! -//! @note -//! The application code should not call this function directly. -//! The only allowed use is thorough the macro Q_NEW_REF(). -QEvt const * newRef_( - QEvt const * const e, - QEvt const * const evtRef) noexcept; - -//${QF::QF-dyn::deleteRef_} .................................................. -//! Internal QF implementation of deleting event reference -//! -//! @details -//! Deletes an existing reference to the event e -//! -//! @param[in] evtRef the event reference -//! -//! @note -//! The application code should not call this function directly. -//! The only allowed use is thorough the macro Q_DELETE_REF(). -void deleteRef_(QEvt const * const evtRef) noexcept; - -//${QF::QF-dyn::getPoolMin} .................................................. -//! This function returns the minimum of free entries of the given -//! event pool -//! -//! @details -//! This function obtains the minimum number of free blocks in the given -//! event pool since this pool has been initialized by a call to -//! QP::QF::poolInit(). -//! -//! @param[in] poolId event pool ID in the range 1..QF::maxPool_, where -//! QF::maxPool_ is the number of event pools -//! initialized with the function QF::poolInit(). -//! @returns -//! the minimum number of unused blocks in the given event pool. -std::uint_fast16_t getPoolMin(std::uint_fast8_t const poolId) noexcept; - -//${QF::QF-dyn::newXfromISR_} ................................................ -#ifdef QF_ISR_API -//! the "FromISR" variant used in the QP port to "FreeRTOS" -QEvt * newXfromISR_( - std::uint_fast16_t const evtSize, - std::uint_fast16_t const margin, - enum_t const sig) noexcept; -#endif // def QF_ISR_API - -//${QF::QF-dyn::gcFromISR} ................................................... -#ifdef QF_ISR_API -//! the "FromISR" variant used in the QP port to "FreeRTOS" -void gcFromISR(QEvt const * e) noexcept; -#endif // def QF_ISR_API - -} // namespace QF -} // namespace QP -//$enddecl${QF::QF-dyn} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -//============================================================================ -extern "C" { -//$declare${QF-extern-C} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv - -//${QF-extern-C::QF_onContextSw} ............................................. -#ifdef QF_ON_CONTEXT_SW -//! QF context switch callback used in built-in kernels -//! -//! @details -//! This callback function provides a mechanism to perform additional -//! custom operations when one of the built-in kernels switches context -//! from one thread to another. -//! -//! @param[in] prev pointer to the previous thread (active object) -//! (prev==0 means that `prev` was the QK idle loop) -//! @param[in] next pointer to the next thread (active object) -//! (next==0) means that `next` is the QK idle loop) -//! @attention -//! QF_onContextSw() is invoked with interrupts **disabled** and must also -//! return with interrupts **disabled**. -//! -//! @note -//! This callback is enabled by defining the macro #QF_ON_CONTEXT_SW. -//! -//! @include qf_oncontextsw.cpp -void QF_onContextSw( - QP::QActive * prev, - QP::QActive * next); -#endif // def QF_ON_CONTEXT_SW -//$enddecl${QF-extern-C} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -} // extern "C" - -//============================================================================ -// Global namespace... -//$declare${QF-macros} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv - -//${QF-macros::Q_PRIO} ....................................................... -//! Create a QP::QPrioSpec object to specify priority of an AO or a thread -#define Q_PRIO(prio_, pthre_) (static_cast((prio_) | (pthre_) << 8U)) - -//${QF-macros::Q_NEW} ........................................................ -#ifndef Q_EVT_CTOR -//! Allocate a dynamic event (case when QP::QEvt is a POD) -//! -//! @details -//! The macro calls the internal QF function QF::newX_() with -//! margin == QF::NO_MARGIN, which causes an assertion when the event -//! cannot be successfully allocated. -//! -//! @param[in] evtT_ event type (class name) of the event to allocate -//! @param[in] sig_ signal to assign to the newly allocated event -//! -//! @returns a valid event pointer cast to the type `evtT_`. -//! -//! @note -//! If #Q_EVT_CTOR is defined, the Q_NEW() macro becomes variadic and -//! takes all the arguments needed by the constructor of the event -//! class being allocated. The constructor is then called by means -//! of the placement-new operator. -//! -//! @usage -//! The following example illustrates dynamic allocation of an event: -//! @include qf_post.cpp -#define Q_NEW(evtT_, sig_) (static_cast( \ - QP::QF::newX_(sizeof(evtT_), QP::QF::NO_MARGIN, (sig_)))) -#endif // ndef Q_EVT_CTOR - -//${QF-macros::Q_NEW} ........................................................ -#ifdef Q_EVT_CTOR -//! Allocate a dynamic event (case when QP::QEvt is not a POD) -#define Q_NEW(evtT_, sig_, ...) \ - (new(QP::QF::newX_(sizeof(evtT_), QP::QF::NO_MARGIN, (sig_))) \ - evtT_((sig_), ##__VA_ARGS__)) -#endif // def Q_EVT_CTOR - -//${QF-macros::Q_NEW_X} ...................................................... -#ifndef Q_EVT_CTOR -//! Non-asserting allocate a dynamic event (case when QP::QEvt is a POD). -//! -//! @details -//! This macro allocates a new event and sets the pointer `e_`, while -//! leaving at least `margin_` of events still available in the pool -//! -//! @param[out] e_ pointer to the newly allocated event -//! @param[in] evtT_ event type (class name) of the event to allocate -//! @param[in] margin_ number of events that must remain available -//! in the given pool after this allocation. The -//! special value QF::NO_MARGIN causes asserting -//! failure in case event allocation fails. -//! @param[in] sig_ signal to assign to the newly allocated event -//! -//! @returns an event pointer cast to the type `evtT_` or NULL if the -//! event cannot be allocated with the specified `margin`. -//! -//! @note -//! If #Q_EVT_CTOR is defined, the Q_NEW_X() macro becomes variadic and -//! takes all the arguments needed by the constructor of the event -//! class being allocated. The constructor is then called by means -//! of the placement-new operator. -//! -//! @usage -//! The following example illustrates dynamic allocation of an event: -//! @include qf_postx.cpp -#define Q_NEW_X(e_, evtT_, margin_, sig_) \ - ((e_) = static_cast(QP::QF::newX_( \ - sizeof(evtT_), (margin_), (sig_)))) -#endif // ndef Q_EVT_CTOR - -//${QF-macros::Q_NEW_X} ...................................................... -#ifdef Q_EVT_CTOR -//! Non-asserting allocate a dynamic event -//! (case when QP::QEvt is not a POD) -#define Q_NEW_X(e_, evtT_, margin_, sig_, ...) do { \ - (e_) = static_cast( \ - QP::QF::newX_(sizeof(evtT_), (margin_), (sig_))); \ - if ((e_) != nullptr) { \ - new((e_)) evtT_((sig_), ##__VA_ARGS__); \ - } \ -} while (false) -#endif // def Q_EVT_CTOR - -//${QF-macros::Q_NEW_REF} .................................................... -//! Create a new reference of the current event `e` -//! -//! @details -//! The current event processed by an active object is available only for -//! the duration of the run-to-completion (RTC) step. After that step, the -//! current event is no longer available and the framework might recycle -//! (garbage-collect) the event. The macro Q_NEW_REF() explicitly creates -//! a new reference to the current event that can be stored and used beyond -//! the current RTC step, until the reference is explicitly recycled by -//! means of the macro Q_DELETE_REF(). -//! -//! @param[in,out] evtRef_ event reference to create -//! @param[in] evtT_ event type (class name) of the event reference -//! -//! @usage -//! The example **defer** in the directory `examples/win32/defer` illustrates -//! the use of Q_NEW_REF() -//! -//! @sa Q_DELETE_REF() -#define Q_NEW_REF(evtRef_, evtT_) \ - ((evtRef_) = static_cast(QP::QF::newRef_(e, (evtRef_)))) - -//${QF-macros::Q_DELETE_REF} ................................................. -//! Delete the event reference -//! -//! @details -//! Every event reference created with the macro Q_NEW_REF() needs to be -//! eventually deleted by means of the macro Q_DELETE_REF() to avoid leaking -//! the event. -//! -//! @param[in,out] evtRef_ event reference to delete -//! -//! @usage -//! The example **defer** in the directory `examples/win32/defer` illustrates -//! the use of Q_DELETE_REF() -//! -//! @sa Q_NEW_REF() -#define Q_DELETE_REF(evtRef_) do { \ - QP::QF::deleteRef_((evtRef_)); \ - (evtRef_) = 0U; \ -} while (false) - -//${QF-macros::PUBLISH} ...................................................... -#ifdef Q_SPY -//! Invoke the event publishing facility QActive::publish_(). -//! -//! @details -//! This macro is the recommended way of publishing events, because it -//! provides the vital information for software tracing and avoids any -//! overhead when the tracing is disabled. -//! -//! @param[in] e_ pointer to the posted event -//! @param[in] sender_ pointer to the sender object (actually used -//! only when #Q_SPY is defined) -//! -//! @note -//! The pointer to the `sender_` object is not necessarily a pointer -//! to an active object. In fact, if QActive::PUBLISH() is called from an -//! interrupt or other context, you can create a unique object just to -//! unambiguously identify the sender of the event. -//! -//! @sa QActive::publish_() -#define PUBLISH(e_, sender_) \ - publish_((e_), (sender_), (sender_)->getPrio()) -#endif // def Q_SPY - -//${QF-macros::PUBLISH} ...................................................... -#ifndef Q_SPY -#define PUBLISH(e_, dummy) publish_((e_), nullptr, 0U) -#endif // ndef Q_SPY - -//${QF-macros::POST} ......................................................... -#ifdef Q_SPY -//! Invoke the direct event posting facility QActive::post_() -//! -//! @details -//! This macro asserts if the queue overflows and cannot accept the event. -//! -//! @param[in] e_ pointer to the event to post -//! @param[in] sender_ pointer to the sender object. -//! -//! @note -//! The `sendedr_` parameter is actually only used when QS tracing -//! is enabled (macro #Q_SPY is defined). When QS software tracing is -//! disenabled, the POST() macro does not pass the `sender_` parameter, -//1 so the overhead of passing this extra parameter is entirely avoided. -//! -//! @note -//! the pointer to the sender object is not necessarily a pointer to an -//! active object. In fact, if POST() is called from an interrupt or -//! other context, you can create a unique object just to unambiguously -//! identify the sender of the event. -//! -//! @sa QActive::post_() -#define POST(e_, sender_) post_((e_), QP::QF::NO_MARGIN, (sender_)) -#endif // def Q_SPY - -//${QF-macros::POST} ......................................................... -#ifndef Q_SPY -#define POST(e_, dummy) post_((e_), QP::QF::NO_MARGIN, nullptr) -#endif // ndef Q_SPY - -//${QF-macros::POST_X} ....................................................... -#ifdef Q_SPY -//! Invoke the direct event posting facility QActive::post_() -//! without delivery guarantee -//! -//! @details -//! This macro does not assert if the queue overflows and cannot accept -//! the event with the specified margin of free slots remaining. -//! -//! @param[in] e_ pointer to the event to post -//! @param[in] margin_ the minimum free slots in the queue, which -//! must still be available after posting the event. -//! The special value QF::NO_MARGIN causes -//! asserting failure in case event posting fails. -//! @param[in] sender_ pointer to the sender object. -//! -//! @returns -//! 'true' if the posting succeeded, and 'false' if the posting -//! failed due to insufficient margin of free entries available in -//! the queue. -//! -//! @note -//! The `sender_` parameter is actually only used when QS tracing -//! is enabled (macro #Q_SPY is defined). When QS software tracing is -//! disabled, the POST_X() macro does not pass the `sender_` parameter, -//! so the overhead of passing this extra parameter is entirely avoided. -//! -//! @note -//! The pointer to the sender object is not necessarily a pointer -//! to an active object. In fact, if POST_X() is called from an -//! interrupt or other context, you can create a unique object just to -//! unambiguously identify the sender of the event. -//! -//! @usage -//! @include qf_postx.cpp -#define POST_X(e_, margin_, sender_) \ - post_((e_), (margin_), (sender_)) -#endif // def Q_SPY - -//${QF-macros::POST_X} ....................................................... -#ifndef Q_SPY -#define POST_X(e_, margin_, dummy) post_((e_), (margin_), nullptr) -#endif // ndef Q_SPY - -//${QF-macros::TICK_X} ....................................................... -#ifdef Q_SPY -//! Invoke the system clock tick processing QTimeEvt::tick_() -//! -//! @details -//! This macro is the recommended way of invoking clock tick processing, -//! because it provides the vital information for software tracing and -//! avoids any overhead when the tracing is disabled. -//! -//! @param[in] tickRate_ clock tick rate to be serviced through this call -//! @param[in] sender_ pointer to the sender object. This parameter -//! is actually only used when QS software tracing is enabled -//! (macro #Q_SPY is defined) -//! @note -//! When QS software tracing is disabled, the macro calls -//! QTimeEvt::tick_() without the `sender` parameter, so the overhead -//! of passing this extra parameter is entirely avoided. -//! -//! @note -//! The pointer to the sender object is not necessarily a pointer -//! to an active object. In fact, when TICK_X() is called from -//! an interrupt, you would create a unique object just to unambiguously -//! identify the ISR as the sender of the time events. -//! -//! @sa QTimeEvt::tick_() -#define TICK_X(tickRate_, sender_) tick_((tickRate_), (sender_)) -#endif // def Q_SPY - -//${QF-macros::TICK_X} ....................................................... -#ifndef Q_SPY -#define TICK_X(tickRate_, dummy) tick_((tickRate_), nullptr) -#endif // ndef Q_SPY - -//${QF-macros::TICK} ......................................................... -//! Invoke the system clock tick processing for rate 0 -//! @sa TICK_X() -#define TICK(sender_) TICK_X(0U, (sender_)) - -//${QF-macros::QF_CRIT_EXIT_NOP} ............................................. -#ifndef QF_CRIT_EXIT_NOP -//! No-operation for exiting a critical section -//! -//! @details -//! In some QF ports the critical section exit takes effect only on the -//! next machine instruction. If this next instruction is another entry -//! to a critical section, the critical section won't be really exited, -//! but rather the two adjecent critical sections would be merged. -//! The QF_CRIT_EXIT_NOP() macro contains minimal code required to -//! prevent such merging of critical sections in such merging of -//! critical sections in QF ports, in which it can occur. -#define QF_CRIT_EXIT_NOP() (static_cast(0)) -#endif // ndef QF_CRIT_EXIT_NOP -//$enddecl${QF-macros} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -#endif // QF_HPP_ diff --git a/src/qf_act.cpp b/src/qf_act.cpp index bf98f6f..60e67a5 100644 --- a/src/qf_act.cpp +++ b/src/qf_act.cpp @@ -1,45 +1,112 @@ -//$file${src::qf::qf_act.cpp} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv +//============================================================================ +// QP/C++ Real-Time Embedded Framework (RTEF) +// Version 8.0.2 // -// Model: qpcpp.qm -// File: ${src::qf::qf_act.cpp} +// Copyright (C) 2005 Quantum Leaps, LLC. All rights reserved. // -// This code has been generated by QM 5.2.5 . -// DO NOT EDIT THIS FILE MANUALLY. All your changes will be lost. -// -// This code is covered by the following QP license: -// License # : LicenseRef-QL-dual -// Issued to : Any user of the QP/C++ real-time embedded framework -// Framework(s) : qpcpp -// Support ends : 2023-12-31 -// License scope: -// -// Copyright (C) 2005 Quantum Leaps, LLC . +// 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: +// This software is dual-licensed under the terms of the open-source GNU +// General Public License (GPL) or under the terms of one of the closed- +// source Quantum Leaps commercial licenses. // // Redistributions in source code must retain this top-level comment block. // Plagiarizing this software to sidestep the license obligations is illegal. // -// Contact information: +// NOTE: +// The GPL does NOT permit the incorporation of this code into proprietary +// programs. Please contact Quantum Leaps for commercial licensing options, +// which expressly supersede the GPL and are designed explicitly for +// closed-source distribution. +// +// Quantum Leaps contact information: // // -// -//$endhead${src::qf::qf_act.cpp} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -//! @file -//! @deprecated -//! Empty file kept only for backwards compatibility. -//! @sa qf_qact.cpp - -extern char const dummy; // declaration -char const dummy = '\0'; // definition +//============================================================================ +#define QP_IMPL // this is QP implementation +#include "qp_port.hpp" // QP port +#include "qp_pkg.hpp" // QP package-scope interface +#include "qsafe.h" // QP Functional Safety (FuSa) Subsystem +#ifdef Q_SPY // QS software tracing enabled? + #include "qs_port.hpp" // QS port + #include "qs_pkg.hpp" // QS facilities for pre-defined trace records +#else + #include "qs_dummy.hpp" // disable the QS software tracing +#endif // Q_SPY + +// unnamed namespace for local definitions with internal linkage +namespace { +//Q_DEFINE_THIS_MODULE("qf_act") +} // unnamed namespace + +namespace QP { + +// QP version string embedded in the binary image +char const versionStr[] = "QP/C++ " QP_VERSION_STR; + +QActive * QActive::registry_[QF_MAX_ACTIVE + 1U]; + +namespace QF { + +QF::Attr priv_; + +void bzero_( + void * const start, + std::uint_fast16_t const len) noexcept +{ + std::uint8_t *ptr = static_cast(start); + for (std::uint_fast16_t n = len; n > 0U; --n) { + *ptr = 0U; + ++ptr; + } +} + +} // namespace QF + +//............................................................................ +#ifndef QF_LOG2 +std::uint_fast8_t QF_LOG2(QP::QPSetBits const bitmask) noexcept { + static constexpr std::uint8_t 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 x = bitmask; + QP::QPSetBits tmp; + +#if (QF_MAX_ACTIVE > 16U) + tmp = static_cast(x >> 16U); + if (tmp != 0U) { + n += 16U; + x = tmp; + } +#endif +#if (QF_MAX_ACTIVE > 8U) + tmp = (x >> 8U); + if (tmp != 0U) { + n += 8U; + x = tmp; + } +#endif + tmp = (x >> 4U); + if (tmp != 0U) { + n += 4U; + x = tmp; + } + return n + log2LUT[x]; +} +#endif // ndef QF_LOG2 + +//............................................................................ +#ifndef Q_UNSAFE +QPtrDis::QPtrDis(void const * const ptr) noexcept + : m_ptr_dis(static_cast(~Q_PTR2UINT_CAST_(ptr))) +{} +#endif + +} // namespace QP + diff --git a/src/qf_actq.cpp b/src/qf_actq.cpp deleted file mode 100644 index daa48a8..0000000 --- a/src/qf_actq.cpp +++ /dev/null @@ -1,437 +0,0 @@ -//$file${src::qf::qf_actq.cpp} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv -// -// Model: qpcpp.qm -// File: ${src::qf::qf_actq.cpp} -// -// This code has been generated by QM 5.2.5 . -// DO NOT EDIT THIS FILE MANUALLY. All your changes will be lost. -// -// This code is covered by the following QP license: -// License # : LicenseRef-QL-dual -// Issued to : Any user of the QP/C++ real-time embedded framework -// Framework(s) : qpcpp -// Support ends : 2023-12-31 -// License scope: -// -// Copyright (C) 2005 Quantum Leaps, LLC . -// -// SPDX-License-Identifier: GPL-3.0-or-later OR LicenseRef-QL-commercial -// -// This software is dual-licensed under the terms of the open source GNU -// General Public License version 3 (or any later version), or alternatively, -// under the terms of one of the closed source Quantum Leaps commercial -// licenses. -// -// The terms of the open source GNU General Public License version 3 -// can be found at: -// -// The terms of the closed source Quantum Leaps commercial licenses -// can be found at: -// -// Redistributions in source code must retain this top-level comment block. -// Plagiarizing this software to sidestep the license obligations is illegal. -// -// Contact information: -// -// -// -//$endhead${src::qf::qf_actq.cpp} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -//! @file -//! @brief QP::QActive native queue operations (based on QP::QEQueue) -//! -//! @attention -//! This qf_actq.cpp source file is only included in the build when the -//! macro #QF_EQUEUE_TYPE is defined as QEQueue. This means that the QP -//! port uses the QP::QEQueue for active objects and so this implementation -//! applies to the QP port. - -#define QP_IMPL // this is QP implementation -#include "qf_port.hpp" // QF port -#include "qf_pkg.hpp" // QF package-scope interface -#include "qassert.h" // QP embedded systems-friendly assertions -#ifdef Q_SPY // QS software tracing enabled? - #include "qs_port.hpp" // QS port - #include "qs_pkg.hpp" // QS facilities for pre-defined trace records -#else - #include "qs_dummy.hpp" // disable the QS software tracing -#endif // Q_SPY - -//============================================================================ -// unnamed namespace for local definitions with internal linkage -namespace { -Q_DEFINE_THIS_MODULE("qf_actq") -} // unnamed namespace - -//============================================================================ -//$skip${QP_VERSION} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv -// Check for the minimum required QP version -#if (QP_VERSION < 700U) || (QP_VERSION != ((QP_RELEASE^4294967295U) % 0x3E8U)) -#error qpcpp version 7.0.0 or higher required -#endif -//$endskip${QP_VERSION} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -//$define${QF::QActive::post_} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv -namespace QP { - -//${QF::QActive::post_} ...................................................... -bool QActive::post_( - QEvt const * const e, - std::uint_fast16_t const margin, - void const * const sender) noexcept -{ - Q_UNUSED_PAR(sender); // when Q_SPY not defined - - //! @pre event pointer must be valid - Q_REQUIRE_ID(100, e != nullptr); - - QF_CRIT_STAT_ - QF_CRIT_E_(); - QEQueueCtr nFree = m_eQueue.m_nFree; // get into the temporary - - // test-probe#1 for faking queue overflow - QS_TEST_PROBE_DEF(&QActive::post_) - QS_TEST_PROBE_ID(1, - nFree = 0U; - ) - - bool status; - if (margin == QF::NO_MARGIN) { - if (nFree > 0U) { - status = true; // can post - } - else { - status = false; // cannot post - Q_ERROR_CRIT_(110); // must be able to post the event - } - } - else if (nFree > static_cast(margin)) { - status = true; // can post - } - else { - status = false; // cannot post, but don't assert - } - - // is it a dynamic event? - if (e->poolId_ != 0U) { - QEvt_refCtr_inc_(e); // increment the reference counter - } - - if (status) { // can post the event? - - --nFree; // one free entry just used up - m_eQueue.m_nFree = nFree; // update the original - if (m_eQueue.m_nMin > nFree) { - m_eQueue.m_nMin = nFree; // update minimum so far - } - - QS_BEGIN_NOCRIT_PRE_(QS_QF_ACTIVE_POST, m_prio) - QS_TIME_PRE_(); // timestamp - QS_OBJ_PRE_(sender); // the sender object - QS_SIG_PRE_(e->sig); // the signal of the event - QS_OBJ_PRE_(this); // this active object - QS_2U8_PRE_(e->poolId_, e->refCtr_); // pool-Id & ref-ctr - QS_EQC_PRE_(nFree); // number of free entries - QS_EQC_PRE_(m_eQueue.m_nMin); // min number of free entries - QS_END_NOCRIT_PRE_() - - #ifdef Q_UTEST - // callback to examine the posted event under the same conditions - // as producing the #QS_QF_ACTIVE_POST trace record, which are: - // the local filter for this AO ('me->prio') is set - // - if (QS_LOC_CHECK_(m_prio)) { - QS::onTestPost(sender, this, e, status); - } - #endif - // empty queue? - if (m_eQueue.m_frontEvt == nullptr) { - m_eQueue.m_frontEvt = e; // deliver event directly - QACTIVE_EQUEUE_SIGNAL_(this); // signal the event queue - } - // queue is not empty, insert event into the ring-buffer - else { - // insert event pointer e into the buffer (FIFO) - m_eQueue.m_ring[m_eQueue.m_head] = e; - - // need to wrap head? - if (m_eQueue.m_head == 0U) { - m_eQueue.m_head = m_eQueue.m_end; // wrap around - } - // advance the head (counter clockwise) - m_eQueue.m_head = (m_eQueue.m_head - 1U); - } - - QF_CRIT_X_(); - } - else { // cannot post the event - - QS_BEGIN_NOCRIT_PRE_(QS_QF_ACTIVE_POST_ATTEMPT, m_prio) - QS_TIME_PRE_(); // timestamp - QS_OBJ_PRE_(sender); // the sender object - QS_SIG_PRE_(e->sig); // the signal of the event - QS_OBJ_PRE_(this); // this active object - QS_2U8_PRE_(e->poolId_, e->refCtr_); // pool-Id & ref-ctr - QS_EQC_PRE_(nFree); // number of free entries - QS_EQC_PRE_(margin); // margin requested - QS_END_NOCRIT_PRE_() - - #ifdef Q_UTEST - // callback to examine the posted event under the same conditions - // as producing the #QS_QF_ACTIVE_POST trace record, which are: - // the local filter for this AO ('me->prio') is set - // - if (QS_LOC_CHECK_(m_prio)) { - QS::onTestPost(sender, this, e, status); - } - #endif - - QF_CRIT_X_(); - - #if (QF_MAX_EPOOL > 0U) - QF::gc(e); // recycle the event to avoid a leak - #endif - } - - return status; -} - -} // namespace QP -//$enddef${QF::QActive::post_} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -//$define${QF::QActive::postLIFO} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv -namespace QP { - -//${QF::QActive::postLIFO} ................................................... -void QActive::postLIFO(QEvt const * const e) noexcept { - QF_CRIT_STAT_ - QF_CRIT_E_(); - QEQueueCtr nFree = m_eQueue.m_nFree; - - QS_TEST_PROBE_DEF(&QActive::postLIFO) - QS_TEST_PROBE_ID(1, - nFree = 0U; - ) - - // the queue must be able to accept the event (cannot overflow) - Q_ASSERT_CRIT_(210, nFree != 0U); - - // is it a dynamic event? - if (e->poolId_ != 0U) { - QEvt_refCtr_inc_(e); // increment the reference counter - } - - --nFree; // one free entry just used up - m_eQueue.m_nFree = nFree; // update the original - if (m_eQueue.m_nMin > nFree) { - m_eQueue.m_nMin = nFree; // update minimum so far - } - - QS_BEGIN_NOCRIT_PRE_(QS_QF_ACTIVE_POST_LIFO, m_prio) - QS_TIME_PRE_(); // timestamp - QS_SIG_PRE_(e->sig); // the signal of this event - QS_OBJ_PRE_(this); // this active object - QS_2U8_PRE_(e->poolId_, e->refCtr_); // pool-Id & ref-ctr - QS_EQC_PRE_(nFree); // number of free entries - QS_EQC_PRE_(m_eQueue.m_nMin); // min number of free entries - QS_END_NOCRIT_PRE_() - - #ifdef Q_UTEST - // callback to examine the posted event under the same conditions - // as producing the #QS_QF_ACTIVE_POST trace record, which are: - // the local filter for this AO ('me->prio') is set - // - if (QS_LOC_CHECK_(m_prio)) { - QS::onTestPost(nullptr, this, e, true); - } - #endif - - QEvt const * const frontEvt = m_eQueue.m_frontEvt; - m_eQueue.m_frontEvt = e; // deliver the event directly to the front - - // was the queue empty? - if (frontEvt == nullptr) { - QACTIVE_EQUEUE_SIGNAL_(this); // signal the event queue - } - // queue was not empty, leave the event in the ring-buffer - else { - m_eQueue.m_tail = (m_eQueue.m_tail + 1U); - if (m_eQueue.m_tail == m_eQueue.m_end) { // need to wrap the tail? - m_eQueue.m_tail = 0U; // wrap around - } - - m_eQueue.m_ring[m_eQueue.m_tail] = frontEvt; - } - QF_CRIT_X_(); - -} - -} // namespace QP -//$enddef${QF::QActive::postLIFO} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -//$define${QF::QActive::get_} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv -namespace QP { - -//${QF::QActive::get_} ....................................................... -QEvt const * QActive::get_() noexcept { - QF_CRIT_STAT_ - QF_CRIT_E_(); - QACTIVE_EQUEUE_WAIT_(this); // wait for event to arrive directly - - // always remove evt from the front - QEvt const * const e = m_eQueue.m_frontEvt; - QEQueueCtr const nFree = m_eQueue.m_nFree + 1U; - m_eQueue.m_nFree = nFree; // upate the number of free - - // any events in the ring buffer? - if (nFree <= m_eQueue.m_end) { - - // remove event from the tail - m_eQueue.m_frontEvt = m_eQueue.m_ring[m_eQueue.m_tail]; - if (m_eQueue.m_tail == 0U) { // need to wrap? - m_eQueue.m_tail = m_eQueue.m_end; // wrap around - } - m_eQueue.m_tail = (m_eQueue.m_tail - 1U); - - QS_BEGIN_NOCRIT_PRE_(QS_QF_ACTIVE_GET, m_prio) - QS_TIME_PRE_(); // timestamp - QS_SIG_PRE_(e->sig); // the signal of this event - QS_OBJ_PRE_(this); // this active object - QS_2U8_PRE_(e->poolId_, e->refCtr_); // pool-Id & ref-ctr - QS_EQC_PRE_(nFree); // number of free entries - QS_END_NOCRIT_PRE_() - } - else { - // the queue becomes empty - m_eQueue.m_frontEvt = nullptr; - - // all entries in the queue must be free (+1 for fronEvt) - Q_ASSERT_CRIT_(310, nFree == (m_eQueue.m_end + 1U)); - - QS_BEGIN_NOCRIT_PRE_(QS_QF_ACTIVE_GET_LAST, m_prio) - QS_TIME_PRE_(); // timestamp - QS_SIG_PRE_(e->sig); // the signal of this event - QS_OBJ_PRE_(this); // this active object - QS_2U8_PRE_(e->poolId_, e->refCtr_); // pool-Id & ref-ctr - QS_END_NOCRIT_PRE_() - } - QF_CRIT_X_(); - return e; -} - -} // namespace QP -//$enddef${QF::QActive::get_} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -//$define${QF::QF-base::getQueueMin} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv -namespace QP { -namespace QF { - -//${QF::QF-base::getQueueMin} ................................................ -std::uint_fast16_t getQueueMin(std::uint_fast8_t const prio) noexcept { - Q_REQUIRE_ID(400, (prio <= QF_MAX_ACTIVE) - && (QActive::registry_[prio] != nullptr)); - QF_CRIT_STAT_ - QF_CRIT_E_(); - std::uint_fast16_t const min = static_cast( - QActive::registry_[prio]->m_eQueue.getNMin()); - QF_CRIT_X_(); - - return min; -} - -} // namespace QF -} // namespace QP -//$enddef${QF::QF-base::getQueueMin} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -//============================================================================ -//$define${QF::QTicker} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv -namespace QP { - -//${QF::QTicker} ............................................................. - -//${QF::QTicker::QTicker} .................................................... -QTicker::QTicker(std::uint_fast8_t const tickRate) noexcept -: QActive(nullptr) -{ - // reuse m_head for tick-rate - m_eQueue.m_head = static_cast(tickRate); -} - -//${QF::QTicker::init} ....................................................... -void QTicker::init( - void const * const e, - std::uint_fast8_t const qs_id) -{ - Q_UNUSED_PAR(e); - Q_UNUSED_PAR(qs_id); - m_eQueue.m_tail = 0U; -} - -//${QF::QTicker::init} ....................................................... -void QTicker::init(std::uint_fast8_t const qs_id) { - QTicker::init(nullptr, qs_id); -} - -//${QF::QTicker::dispatch} ................................................... -void QTicker::dispatch( - QEvt const * const e, - std::uint_fast8_t const qs_id) -{ - Q_UNUSED_PAR(e); - Q_UNUSED_PAR(qs_id); - - QF_CRIT_STAT_ - QF_CRIT_E_(); - QEQueueCtr nTicks = m_eQueue.m_tail; // # ticks since the last call - m_eQueue.m_tail = 0U; // clear the # ticks - QF_CRIT_X_(); - - for (; nTicks > 0U; --nTicks) { - QTimeEvt::TICK_X(static_cast(m_eQueue.m_head), - this); - } -} - -//${QF::QTicker::post_} ...................................................... -bool QTicker::post_( - QEvt const * const e, - std::uint_fast16_t const margin, - void const * const sender) noexcept -{ - Q_UNUSED_PAR(e); - Q_UNUSED_PAR(margin); - Q_UNUSED_PAR(sender); // when Q_SPY not defined - - QF_CRIT_STAT_ - QF_CRIT_E_(); - if (m_eQueue.m_frontEvt == nullptr) { - - #ifdef Q_EVT_CTOR - static QEvt const tickEvt(0U, 0U); - #else - static QEvt const tickEvt = { 0U, 0U, 0U }; - #endif // Q_EVT_CTOR - - m_eQueue.m_frontEvt = &tickEvt; // deliver event directly - m_eQueue.m_nFree = (m_eQueue.m_nFree - 1U); // one less free event - - QACTIVE_EQUEUE_SIGNAL_(this); // signal the event queue - } - - // account for one more tick event - m_eQueue.m_tail = (m_eQueue.m_tail + 1U); - - QS_BEGIN_NOCRIT_PRE_(QS_QF_ACTIVE_POST, m_prio) - QS_TIME_PRE_(); // timestamp - QS_OBJ_PRE_(sender); // the sender object - QS_SIG_PRE_(0U); // the signal of the event - QS_OBJ_PRE_(this); // this active object - QS_2U8_PRE_(0U, 0U); // pool-Id & ref-ctr - QS_EQC_PRE_(0U); // number of free entries - QS_EQC_PRE_(0U); // min number of free entries - QS_END_NOCRIT_PRE_() - - QF_CRIT_X_(); - - return true; // the event is always posted correctly -} - -} // namespace QP -//$enddef${QF::QTicker} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/src/qf_defer.cpp b/src/qf_defer.cpp index 189731e..75eae08 100644 --- a/src/qf_defer.cpp +++ b/src/qf_defer.cpp @@ -1,49 +1,36 @@ -//$file${src::qf::qf_defer.cpp} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv +//============================================================================ +// QP/C++ Real-Time Embedded Framework (RTEF) +// Version 8.0.2 // -// Model: qpcpp.qm -// File: ${src::qf::qf_defer.cpp} +// Copyright (C) 2005 Quantum Leaps, LLC. All rights reserved. // -// This code has been generated by QM 5.2.5 . -// DO NOT EDIT THIS FILE MANUALLY. All your changes will be lost. -// -// This code is covered by the following QP license: -// License # : LicenseRef-QL-dual -// Issued to : Any user of the QP/C++ real-time embedded framework -// Framework(s) : qpcpp -// Support ends : 2023-12-31 -// License scope: -// -// Copyright (C) 2005 Quantum Leaps, LLC . +// 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: +// This software is dual-licensed under the terms of the open-source GNU +// General Public License (GPL) or under the terms of one of the closed- +// source Quantum Leaps commercial licenses. // // Redistributions in source code must retain this top-level comment block. // Plagiarizing this software to sidestep the license obligations is illegal. // -// Contact information: +// NOTE: +// The GPL does NOT permit the incorporation of this code into proprietary +// programs. Please contact Quantum Leaps for commercial licensing options, +// which expressly supersede the GPL and are designed explicitly for +// closed-source distribution. +// +// Quantum Leaps contact information: // // -// -//$endhead${src::qf::qf_defer.cpp} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -//! @file -//! @brief QActive::defer(), QActive::recall(), and -//! QActive::flushDeferred() definitions. - +//============================================================================ #define QP_IMPL // this is QP implementation -#include "qf_port.hpp" // QF port -#include "qf_pkg.hpp" // QF package-scope interface -#include "qassert.h" // QP embedded systems-friendly assertions +#include "qp_port.hpp" // QP port +#include "qp_pkg.hpp" // QP package-scope interface +#include "qsafe.h" // QP Functional Safety (FuSa) Subsystem #ifdef Q_SPY // QS software tracing enabled? #include "qs_port.hpp" // QS port #include "qs_pkg.hpp" // QS facilities for pre-defined trace records @@ -56,111 +43,101 @@ namespace { Q_DEFINE_THIS_MODULE("qf_defer") } // unnamed namespace -//$skip${QP_VERSION} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv -// Check for the minimum required QP version -#if (QP_VERSION < 700U) || (QP_VERSION != ((QP_RELEASE^4294967295U) % 0x3E8U)) -#error qpcpp version 7.0.0 or higher required -#endif -//$endskip${QP_VERSION} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -//$define${QF::QActive::defer} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv namespace QP { -//${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_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_END_PRE_() + QS_CRIT_STAT + QS_CRIT_ENTRY(); + 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->poolNum_, e->refCtr_); + QS_END_PRE() + QS_CRIT_EXIT(); return status; } -} // namespace QP -//$enddef${QF::QActive::defer} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -//$define${QF::QActive::recall} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv -namespace QP { - -//${QF::QActive::recall} ..................................................... +//............................................................................ bool QActive::recall(QEQueue * const eq) noexcept { QEvt const * const e = eq->get(m_prio); // get evt from deferred queue - bool recalled; + QF_CRIT_STAT - // event available? - if (e != nullptr) { - QActive::postLIFO(e); // post it to the _front_ of the AO's queue + bool recalled; + if (e != nullptr) { // event available? + postLIFO(e); // post it to the _front_ of the AO's queue - QF_CRIT_STAT_ - QF_CRIT_E_(); + QF_CRIT_ENTRY(); - // is it a dynamic event? - if (e->poolId_ != 0U) { + if (e->poolNum_ != 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(205, e->refCtr_ >= 2U); // we need to decrement the reference counter once, to account // for removing the event from the deferred event queue. QEvt_refCtr_dec_(e); // decrement the reference counter } - QS_BEGIN_NOCRIT_PRE_(QS_QF_ACTIVE_RECALL, m_prio) - QS_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_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->poolNum_, e->refCtr_); + QS_END_PRE() + + QF_CRIT_EXIT(); - QF_CRIT_X_(); recalled = true; } else { - QS_CRIT_STAT_ + QS_CRIT_ENTRY(); - QS_BEGIN_PRE_(QS_QF_ACTIVE_RECALL_ATTEMPT, m_prio) - QS_TIME_PRE_(); // time stamp - QS_OBJ_PRE_(this); // this active object - QS_OBJ_PRE_(eq); // the deferred queue - QS_END_PRE_() + QS_BEGIN_PRE(QS_QF_ACTIVE_RECALL_ATTEMPT, m_prio) + QS_TIME_PRE(); // time stamp + QS_OBJ_PRE(this); // this active object + QS_OBJ_PRE(eq); // the deferred queue + QS_END_PRE() + + QS_CRIT_EXIT(); recalled = false; } return recalled; - } -} // 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) const noexcept { +//............................................................................ +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)) - { - ++n; // count the flushed event - #if (QF_MAX_EPOOL > 0U) - QF::gc(e); // garbage collect - #endif + 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/src/qf_dyn.cpp b/src/qf_dyn.cpp index f7701ae..b064ebb 100644 --- a/src/qf_dyn.cpp +++ b/src/qf_dyn.cpp @@ -1,48 +1,36 @@ -//$file${src::qf::qf_dyn.cpp} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv -// -// Model: qpcpp.qm -// File: ${src::qf::qf_dyn.cpp} -// -// This code has been generated by QM 5.2.5 . -// DO NOT EDIT THIS FILE MANUALLY. All your changes will be lost. +//============================================================================ +// QP/C++ Real-Time Embedded Framework (RTEF) +// Version 8.0.2 // -// This code is covered by the following QP license: -// License # : LicenseRef-QL-dual -// Issued to : Any user of the QP/C++ real-time embedded framework -// Framework(s) : qpcpp -// Support ends : 2023-12-31 -// License scope: +// Copyright (C) 2005 Quantum Leaps, LLC. All rights reserved. // -// 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: +// This software is dual-licensed under the terms of the open-source GNU +// General Public License (GPL) or under the terms of one of the closed- +// source Quantum Leaps commercial licenses. // // Redistributions in source code must retain this top-level comment block. // Plagiarizing this software to sidestep the license obligations is illegal. // -// Contact information: +// NOTE: +// The GPL does NOT permit the incorporation of this code into proprietary +// programs. Please contact Quantum Leaps for commercial licensing options, +// which expressly supersede the GPL and are designed explicitly for +// closed-source distribution. +// +// Quantum Leaps contact information: // // -// -//$endhead${src::qf::qf_dyn.cpp} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -//! @file -//! @brief QF/C++ dynamic event management - +//============================================================================ #define QP_IMPL // this is QP implementation -#include "qf_port.hpp" // QF port -#include "qf_pkg.hpp" // QF package-scope interface -#include "qassert.h" // QP embedded systems-friendly assertions +#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 @@ -50,260 +38,270 @@ #include "qs_dummy.hpp" // disable the QS software tracing #endif // Q_SPY -#if (QF_MAX_EPOOL > 0U) // dynamic events configured? +#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 < 700U) || (QP_VERSION != ((QP_RELEASE^4294967295U) % 0x3E8U)) -#error qpcpp version 7.0.0 or higher required -#endif -//$endskip${QP_VERSION} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -//$define${QF::QF-pkg::maxPool_} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv -namespace QP { -namespace QF { - -//${QF::QF-pkg::maxPool_} .................................................... -std::uint_fast8_t maxPool_; - -} // namespace QF -} // namespace QP -//$enddef${QF::QF-pkg::maxPool_} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -//$define${QF::QF-pkg::ePool_[QF_MAX_EPOOL]} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv -namespace QP { -namespace QF { - -//${QF::QF-pkg::ePool_[QF_MAX_EPOOL]} ........................................ -#if (QF_MAX_EPOOL > 0U) -QF_EPOOL_TYPE_ ePool_[QF_MAX_EPOOL]; -#endif // (QF_MAX_EPOOL > 0U) - -} // namespace QF -} // namespace QP -//$enddef${QF::QF-pkg::ePool_[QF_MAX_EPOOL]} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -//============================================================================ -//$define${QF::QF-dyn} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv namespace QP { namespace QF { -//${QF::QF-dyn::poolInit} .................................................... +//............................................................................ void poolInit( void * const poolSto, std::uint_fast32_t const poolSize, std::uint_fast16_t const evtSize) noexcept { - //! @pre cannot exceed the number of available memory pools - Q_REQUIRE_ID(200, QF::maxPool_ < QF_MAX_EPOOL); + std::uint_fast8_t const poolNum = priv_.maxPool_; + + // see precondition{qf_dyn,200} and precondition{qf_dyn,201} + QF_CRIT_STAT + QF_CRIT_ENTRY(); - //! @pre QF event pools must be initialized in ascending order of evtSize - if (QF::maxPool_ > 0U) { - Q_REQUIRE_ID(201, - QF_EPOOL_EVENT_SIZE_(QF::ePool_[QF::maxPool_ - 1U]) < evtSize); + Q_REQUIRE_INCRIT(100, poolNum < QF_MAX_EPOOL); + if (poolNum > 0U) { + Q_REQUIRE_INCRIT(110, + QF_EPOOL_EVENT_SIZE_(priv_.ePool_[poolNum - 1U]) < evtSize); } + priv_.maxPool_ = poolNum + 1U; // one more pool - QF_EPOOL_INIT_(QF::ePool_[QF::maxPool_], poolSto, poolSize, evtSize); - ++QF::maxPool_; // one more pool + QF_CRIT_EXIT(); - #ifdef Q_SPY + // perform the port-dependent initialization of the event-pool + QF_EPOOL_INIT_(priv_.ePool_[poolNum], poolSto, poolSize, evtSize); + +#ifdef Q_SPY // generate the object-dictionary entry for the initialized pool - char obj_name[9] = "EvtPool?"; - obj_name[7] = static_cast( - static_cast('0') - + static_cast(QF::maxPool_)); - QS::obj_dict_pre_(&QF::ePool_[QF::maxPool_ - 1U], &obj_name[0]); - #endif // Q_SPY + { + 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 +} + +//............................................................................ +std::uint_fast16_t poolGetMaxBlockSize() noexcept { + QF_CRIT_STAT + QF_CRIT_ENTRY(); + std::uint_fast16_t const max_size = + QF_EPOOL_EVENT_SIZE_(priv_.ePool_[priv_.maxPool_ - 1U]); + QF_CRIT_EXIT(); + + return max_size; +} + +//............................................................................ +std::uint_fast16_t getPoolMin(std::uint_fast8_t const poolNum) noexcept { + QF_CRIT_STAT + QF_CRIT_ENTRY(); + + Q_REQUIRE_INCRIT(300, (poolNum <= QF_MAX_EPOOL) + && (0U < poolNum) && (poolNum <= priv_.maxPool_)); + + std::uint_fast16_t const min = static_cast( + priv_.ePool_[poolNum - 1U].getNMin()); + + 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 { - std::uint_fast8_t idx; + QF_CRIT_STAT + QF_CRIT_ENTRY(); - // find the pool id that fits the requested event size ... - for (idx = 0U; idx < QF::maxPool_; ++idx) { - if (evtSize <= QF_EPOOL_EVENT_SIZE_(QF::ePool_[idx])) { + // find the pool id that fits the requested event size... + std::uint8_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_); - - // get e -- platform-dependent - QEvt *e; - - #ifdef Q_SPY - QF_EPOOL_GET_(QF::ePool_[idx], e, ((margin != QF::NO_MARGIN) ? margin : 0U), - static_cast(QS_EP_ID) + idx + 1U); - #else - QF_EPOOL_GET_(QF::ePool_[idx], e, ((margin != QF::NO_MARGIN) ? margin : 0U), - 0U); - #endif - - // was e allocated correctly? - QS_CRIT_STAT_ - if (e != nullptr) { - e->sig = static_cast(sig); // set the signal - e->poolId_ = static_cast(idx + 1U); // store pool ID - e->refCtr_ = 0U; // initialize the reference counter to 0 - - QS_BEGIN_PRE_(QS_QF_NEW, - static_cast(QS_EP_ID) - + static_cast(e->poolId_)) - QS_TIME_PRE_(); // timestamp - QS_EVS_PRE_(evtSize); // the size of the evt - QS_SIG_PRE_(sig); // the signal of the evt - QS_END_PRE_() + + // precondition: + // - cannot run out of registered pools + Q_REQUIRE_INCRIT(400, poolNum < priv_.maxPool_); + + ++poolNum; // convert to 1-based poolNum + + QF_CRIT_EXIT(); + + // get event `e` out of the event pool (port-dependent)... + QEvt *e = nullptr; +#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->poolNum_ = poolNum; + e->refCtr_ = 0U; // initialize the reference counter to 0 + + QS_CRIT_ENTRY(); + QS_BEGIN_PRE(QS_QF_NEW, + 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_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); - - QS_BEGIN_PRE_(QS_QF_NEW_ATTEMPT, - static_cast(QS_EP_ID) + idx + 1U) - QS_TIME_PRE_(); // timestamp - QS_EVS_PRE_(evtSize); // the size of the evt - QS_SIG_PRE_(sig); // the signal of the evt - QS_END_PRE_() + Q_ASSERT_INCRIT(420, margin != NO_MARGIN); + + QS_BEGIN_PRE(QS_QF_NEW_ATTEMPT, + 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() + + 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; } -//${QF::QF-dyn::gc} .......................................................... +//............................................................................ void gc(QEvt const * const e) noexcept { - // is it a dynamic event? - if (e->poolId_ != 0U) { - QF_CRIT_STAT_ - QF_CRIT_E_(); - - // isn't this the last reference? - if (e->refCtr_ > 1U) { - - QS_BEGIN_NOCRIT_PRE_(QS_QF_GC_ATTEMPT, - static_cast(QS_EP_ID) - + static_cast(e->poolId_)) - QS_TIME_PRE_(); // timestamp - QS_SIG_PRE_(e->sig); // the signal of the event - QS_2U8_PRE_(e->poolId_, e->refCtr_); // pool Id & refCtr - QS_END_NOCRIT_PRE_() + QF_CRIT_STAT + QF_CRIT_ENTRY(); + + Q_REQUIRE_INCRIT(500, e != nullptr); + + std::uint_fast8_t const poolNum = e->poolNum_; + + if (poolNum != 0U) { // is it a pool event (mutable)? + + 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_); + QS_END_PRE() + + Q_ASSERT_INCRIT(505, e->refCtr_ > 0U); QEvt_refCtr_dec_(e); // decrement the ref counter - QF_CRIT_X_(); + QF_CRIT_EXIT(); } - // this is the last reference to this event, recycle it - else { - std::uint_fast8_t const idx = - static_cast(e->poolId_) - 1U; - - QS_BEGIN_NOCRIT_PRE_(QS_QF_GC, - static_cast(QS_EP_ID) - + static_cast(e->poolId_)) - QS_TIME_PRE_(); // timestamp - QS_SIG_PRE_(e->sig); // the signal of the event - QS_2U8_PRE_(e->poolId_, e->refCtr_); - QS_END_NOCRIT_PRE_() - - QF_CRIT_X_(); - - // pool ID must be in range - Q_ASSERT_ID(410, idx < QF::maxPool_); - - #ifdef Q_EVT_XTOR - // explicitly exectute the destructor' - // NOTE: casting 'const' away is legitimate, - // because it's a pool event - QF_CONST_CAST_(QEvt*, e)->~QEvt(); // xtor, - #endif - - #ifdef Q_SPY - // cast 'const' away, which is OK, because it's a pool event - QF_EPOOL_PUT_(QF::ePool_[idx], QF_CONST_CAST_(QEvt*, e), - static_cast(QS_EP_ID) - + static_cast(e->poolId_)); - #else - QF_EPOOL_PUT_(QF::ePool_[idx], QF_CONST_CAST_(QEvt*, e), 0U); - #endif + 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_); + QS_END_PRE() + + // pool number must be in range + Q_ASSERT_INCRIT(510, (poolNum <= priv_.maxPool_) + && (poolNum <= QF_MAX_EPOOL)); + 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(); + } } -//${QF::QF-dyn::poolGetMaxBlockSize} ......................................... -std::uint_fast16_t poolGetMaxBlockSize() noexcept { - return QF_EPOOL_EVENT_SIZE_(QF::ePool_[QF::maxPool_ - 1U]); -} - -//${QF::QF-dyn::newRef_} ..................................................... +//............................................................................ QEvt const * newRef_( QEvt const * const e, QEvt const * const evtRef) noexcept { - //! @pre the event must be dynamic and the provided event reference - //! must not be already in use - Q_REQUIRE_ID(500, (e->poolId_ != 0U) - && (evtRef == nullptr)); +#ifdef Q_UNSAFE + Q_UNUSED_PAR(evtRef); +#endif + + QF_CRIT_STAT + QF_CRIT_ENTRY(); - QF_CRIT_STAT_ - QF_CRIT_E_(); + Q_REQUIRE_INCRIT(600, e != nullptr); + std::uint_fast8_t const poolNum = e->poolNum_; + Q_UNUSED_PAR(poolNum); // might be unused + + Q_REQUIRE_INCRIT(610, (poolNum != 0U) + && (evtRef == nullptr)); + + Q_ASSERT_INCRIT(605, e->refCtr_ < (2U * QF_MAX_ACTIVE)); QEvt_refCtr_inc_(e); // increments the ref counter - QS_BEGIN_NOCRIT_PRE_(QS_QF_NEW_REF, - static_cast(QS_EP_ID) - + static_cast(e->poolId_)) - QS_TIME_PRE_(); // timestamp - QS_SIG_PRE_(e->sig); // the signal of the event - QS_2U8_PRE_(e->poolId_, e->refCtr_); // pool Id & ref Count - QS_END_NOCRIT_PRE_() + 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_); + QS_END_PRE() - QF_CRIT_X_(); + QF_CRIT_EXIT(); return e; } -//${QF::QF-dyn::deleteRef_} .................................................. +//............................................................................ void deleteRef_(QEvt const * const evtRef) noexcept { - QS_CRIT_STAT_ - QS_BEGIN_PRE_(QS_QF_DELETE_REF, - static_cast(QS_EP_ID) - + static_cast(evtRef->poolId_)) - QS_TIME_PRE_(); // timestamp - QS_SIG_PRE_(evtRef->sig); // the signal of the event - QS_2U8_PRE_(evtRef->poolId_, evtRef->refCtr_); // pool Id & ref Count - QS_END_PRE_() - - #if (QF_MAX_EPOOL > 0U) - gc(evtRef); // recycle the referenced event - #endif -} + QF_CRIT_STAT + QF_CRIT_ENTRY(); -//${QF::QF-dyn::getPoolMin} .................................................. -std::uint_fast16_t getPoolMin(std::uint_fast8_t const poolId) noexcept { - //! @pre the poolId must be in range - Q_REQUIRE_ID(400, (QF::maxPool_ <= QF_MAX_EPOOL) - && (0U < poolId) && (poolId <= QF::maxPool_)); - QF_CRIT_STAT_ - QF_CRIT_E_(); - std::uint_fast16_t const min = static_cast( - QF::ePool_[poolId - 1U].getNMin()); - QF_CRIT_X_(); + QEvt const * const e = evtRef; + Q_REQUIRE_INCRIT(700, e != nullptr); - return min; +#ifdef Q_SPY + std::uint_fast8_t const poolNum = e->poolNum_; + + QS_BEGIN_PRE(QS_QF_DELETE_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_); + QS_END_PRE() +#endif // def Q_SPY + + QF_CRIT_EXIT(); + +#if (QF_MAX_EPOOL > 0U) + gc(e); // recycle the referenced event +#endif } } // namespace QF } // namespace QP -//$enddef${QF::QF-dyn} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -#endif // (QF_MAX_EPOOL > 0U) dynamic events configured +#endif // (QF_MAX_EPOOL > 0U) mutable events configured diff --git a/src/qf_mem.cpp b/src/qf_mem.cpp index e37f5f4..ef9f282 100644 --- a/src/qf_mem.cpp +++ b/src/qf_mem.cpp @@ -1,51 +1,39 @@ -//$file${src::qf::qf_mem.cpp} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv +//============================================================================ +// QP/C++ Real-Time Embedded Framework (RTEF) +// Version 8.0.2 // -// Model: qpcpp.qm -// File: ${src::qf::qf_mem.cpp} +// Copyright (C) 2005 Quantum Leaps, LLC. All rights reserved. // -// This code has been generated by QM 5.2.5 . -// DO NOT EDIT THIS FILE MANUALLY. All your changes will be lost. -// -// This code is covered by the following QP license: -// License # : LicenseRef-QL-dual -// Issued to : Any user of the QP/C++ real-time embedded framework -// Framework(s) : qpcpp -// Support ends : 2023-12-31 -// License scope: -// -// Copyright (C) 2005 Quantum Leaps, LLC . +// 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: +// This software is dual-licensed under the terms of the open-source GNU +// General Public License (GPL) or under the terms of one of the closed- +// source Quantum Leaps commercial licenses. // // Redistributions in source code must retain this top-level comment block. // Plagiarizing this software to sidestep the license obligations is illegal. // -// Contact information: +// NOTE: +// The GPL does NOT permit the incorporation of this code into proprietary +// programs. Please contact Quantum Leaps for commercial licensing options, +// which expressly supersede the GPL and are designed explicitly for +// closed-source distribution. +// +// Quantum Leaps contact information: // // -// -//$endhead${src::qf::qf_mem.cpp} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -//! @file -//! @brief QF/C++ memory management services - +//============================================================================ #define QP_IMPL // this is QP implementation -#include "qf_port.hpp" // QF port -#include "qf_pkg.hpp" // QF package-scope interface -#include "qassert.h" // QP embedded systems-friendly assertions +#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 + #include "qs_pkg.hpp" // QS package-scope internal interface #else #include "qs_dummy.hpp" // disable the QS software tracing #endif // Q_SPY @@ -55,190 +43,179 @@ namespace { Q_DEFINE_THIS_MODULE("qf_mem") } // unnamed namespace -//$skip${QP_VERSION} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv -// Check for the minimum required QP version -#if (QP_VERSION < 700U) || (QP_VERSION != ((QP_RELEASE^4294967295U) % 0x3E8U)) -#error qpcpp version 7.0.0 or higher required -#endif -//$endskip${QP_VERSION} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -//$define${QF::QMPool} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv namespace QP { -//${QF::QMPool} .............................................................. - -//${QF::QMPool::QMPool} ...................................................... -QMPool::QMPool() - : m_start(nullptr), - m_end(nullptr), - m_free_head(nullptr), - m_blockSize(0U), - m_nTot(0U), - m_nFree(0U), - m_nMin(0U) -{} - -//${QF::QMPool::init} ........................................................ +//............................................................................ void QMPool::init( void * const poolSto, - std::uint_fast32_t poolSize, - std::uint_fast16_t blockSize) noexcept + 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 >= static_cast(sizeof(QFreeBlock))) - && (static_cast(blockSize + sizeof(QFreeBlock)) - > blockSize)); - - m_free_head = poolSto; - - // round up the blockSize to fit an integer number of pointers... - //start with one - m_blockSize = static_cast(sizeof(QFreeBlock)); - - //# free blocks in a memory block - std::uint_fast16_t nblocks = 1U; - while (m_blockSize < static_cast(blockSize)) { - m_blockSize += static_cast(sizeof(QFreeBlock)); - ++nblocks; - } - // use rounded-up value - blockSize = static_cast(m_blockSize); + QF_CRIT_STAT + QF_CRIT_ENTRY(); - // the whole pool buffer must fit at least one rounded-up block - Q_ASSERT_ID(110, poolSize >= blockSize); + Q_REQUIRE_INCRIT(100, poolSto != nullptr); - // chain all blocks together in a free-list... + m_freeHead = static_cast(poolSto); + + // find # free links in a memory block, see NOTE1 + m_blockSize = static_cast(2U * sizeof(void *)); + std::uint_fast16_t inext = 2U; + while (m_blockSize < static_cast(blockSize)) { + m_blockSize += static_cast(sizeof(void *)); + ++inext; + } - // 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); + void * *pfb = m_freeHead; // pointer to free block + std::uint32_t nTot = 1U; // the last block already in the list // chain all blocks together in a free-list... - while (poolSize >= blockSize) { - fb->m_next = &fb[nblocks]; // setup the next link - fb = fb->m_next; // advance to next block - // reduce the available pool size - poolSize -= static_cast(blockSize); - ++m_nTot; // increment the number of blocks so far + for (std::uint_fast32_t size = poolSize - m_blockSize; + size >= static_cast(m_blockSize); + size -= static_cast(m_blockSize)) + { + pfb[0] = &pfb[inext]; // set the next link to next free block + pfb = static_cast(pfb[0]); // advance to the next block + ++nTot; // one more free block in the pool } + pfb[0] = nullptr; // the last link points to NULL - 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 + // dynamic range check +#if (QF_MPOOL_CTR_SIZE == 1U) + Q_ASSERT_INCRIT(190, nTot < 0xFFU); +#elif (QF_MPOOL_CTR_SIZE == 2U) + Q_ASSERT_INCRIT(190, nTot < 0xFFFFU); +#endif + + m_nTot = static_cast(nTot); + m_nFree = m_nTot; // all blocks are free + m_start = static_cast(poolSto); // original start + m_end = pfb; // the last block in this pool + m_nMin = m_nTot; // the minimum # free blocks + + QF_CRIT_EXIT(); } -//${QF::QMPool::get} ......................................................... +//............................................................................ void * QMPool::get( std::uint_fast16_t const margin, - std::uint_fast8_t const qs_id) noexcept + std::uint_fast8_t const qsId) noexcept { - Q_UNUSED_PAR(qs_id); // when Q_SPY not defined +#ifndef Q_SPY + Q_UNUSED_PAR(qsId); +#endif - QF_CRIT_STAT_ - QF_CRIT_E_(); + QF_CRIT_STAT + QF_CRIT_ENTRY(); - // have the than margin? - QFreeBlock *fb; - if (m_nFree > static_cast(margin)) { - fb = static_cast(m_free_head); // get a free block + // get volatile into temporaries + void * *pfb = m_freeHead; // pointer to free block + QMPoolCtr nFree = m_nFree; - // the pool has some free blocks, so a free block must be available - Q_ASSERT_CRIT_(310, fb != nullptr); + // have more free blocks than the requested margin? + if (nFree > static_cast(margin)) { + Q_ASSERT_INCRIT(310, pfb != nullptr); - void * const fb_next = fb->m_next; + // fast temporary + void * * const pfb_next = static_cast(pfb[0]); - // is the pool becoming empty? - m_nFree = (m_nFree - 1U); // one free block less - if (m_nFree == 0U) { + --nFree; // one less free block + if (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, pfb_next == nullptr); - m_nMin = 0U;// remember that pool got empty + m_nFree = 0U; // no more free blocks + m_nMin = 0U; // remember that the pool got empty } - else { - //! @invariant - //! The pool is not empty, so the next free-block pointer, - //! so the next free block must be in range. - //! - //! @tr{PQP18_3} - - // NOTE: The next free block pointer can fall out of range - // when the client code writes past the memory block, thus - // corrupting the next block. - Q_ASSERT_CRIT_(330, QF_PTR_RANGE_(fb_next, m_start, m_end)); - - // is the number of free blocks the new minimum so far? - if (m_nMin > m_nFree) { - m_nMin = m_nFree; // remember the minimum so far + else { // the pool is NOT empty + + // the next free-block pointer must be in range + Q_ASSERT_INCRIT(330, QF_PTR_RANGE_(pfb_next, m_start, m_end)); + + m_nFree = nFree; // update the original + if (m_nMin > nFree) { // is this the new minimum? + m_nMin = nFree; // remember the minimum so far } } - m_free_head = fb_next; // set the head to the next free block + m_freeHead = pfb_next; // set the head to the next free block + + // change the allocated block contents so that it is different + // than a free block inside the pool. + pfb[0] = &m_end[1]; // invalid location beyond the end - QS_BEGIN_NOCRIT_PRE_(QS_QF_MPOOL_GET, qs_id) - QS_TIME_PRE_(); // timestamp - QS_OBJ_PRE_(this); // this memory pool - QS_MPC_PRE_(m_nFree); // # of free blocks in the pool - QS_MPC_PRE_(m_nMin); // min # free blocks ever in the pool - QS_END_NOCRIT_PRE_() + QS_BEGIN_PRE(QS_QF_MPOOL_GET, qsId) + QS_TIME_PRE(); // timestamp + QS_OBJ_PRE(this); // this memory pool + QS_MPC_PRE(nFree); // # 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 { - 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_() + else { // don't have enough free blocks at this point + pfb = nullptr; + + QS_BEGIN_PRE(QS_QF_MPOOL_GET_ATTEMPT, qsId) + QS_TIME_PRE(); // timestamp + QS_OBJ_PRE(this); // this memory pool + QS_MPC_PRE(nFree); // # free blocks in the pool + QS_MPC_PRE(margin); // the requested margin + QS_END_PRE() } - QF_CRIT_X_(); - return fb; // return the block or NULL pointer to the caller + QF_CRIT_EXIT(); + return pfb; // return the block or nullptr to the caller } -//${QF::QMPool::put} ......................................................... +//............................................................................ void QMPool::put( - void * const b, - std::uint_fast8_t const qs_id) noexcept + void * const block, + std::uint_fast8_t const qsId) noexcept { - Q_UNUSED_PAR(qs_id); // when Q_SPY not defined - - //! @pre # free blocks cannot exceed the total # blocks and - //! the block pointer must be in range to come from this pool. - //! - Q_REQUIRE_ID(200, (m_nFree < m_nTot) - && QF_PTR_RANGE_(b, m_start, m_end)); - QF_CRIT_STAT_ - QF_CRIT_E_(); - static_cast(b)->m_next = - static_cast(m_free_head); // link into the free list - m_free_head = b; // set as new head of the free list - m_nFree = (m_nFree + 1U); // one more free block in this pool - - QS_BEGIN_NOCRIT_PRE_(QS_QF_MPOOL_PUT, qs_id) - QS_TIME_PRE_(); // timestamp - QS_OBJ_PRE_(this); // this memory pool - QS_MPC_PRE_(m_nFree); // the number of free blocks in the pool - QS_END_NOCRIT_PRE_() - - QF_CRIT_X_(); -} +#ifndef Q_SPY + Q_UNUSED_PAR(qsId); +#endif -//${QF::QMPool::getBlockSize} ................................................ -QMPoolSize QMPool::getBlockSize() const noexcept { - return m_blockSize; + void * * const pfb = static_cast(block); // pointer to free block + + QF_CRIT_STAT + QF_CRIT_ENTRY(); + + // get volatile into temporaries + void * * const freeHead = m_freeHead; + QMPoolCtr nFree = m_nFree; + + Q_REQUIRE_INCRIT(400, nFree < m_nTot); + Q_REQUIRE_INCRIT(410, QF_PTR_RANGE_(pfb, m_start, m_end)); + + ++nFree; // one more free block in this pool + + m_freeHead = pfb; // set as new head of the free list + m_nFree = nFree; + pfb[0] = freeHead; // link into the list + + QS_BEGIN_PRE(QS_QF_MPOOL_PUT, qsId) + QS_TIME_PRE(); // timestamp + QS_OBJ_PRE(this); // this memory pool + QS_MPC_PRE(nFree); // the # free blocks in the pool + QS_END_PRE() + + QF_CRIT_EXIT(); } } // namespace QP -//$enddef${QF::QMPool} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +//============================================================================ +// NOTE1: +// The memory buffer for the pool is organized as an array of void* pointers +// (see void * data type). These pointers are used to form a linked-list +// of free blocks in the pool. The first location pfb[0] is the actual link. +// The second location pfb[1] is used in SafeQP as the redundant "duplicate +// storage" for the link at pfb[0]. Even though the "duplicate storage" is NOT +// used in this QP edition, the minimum number of number of void* pointers +// (void * data type) inside a memory block is still kept at 2 to maintain +// the same policy for sizing the memory blocks. diff --git a/src/qf_pkg.hpp b/src/qf_pkg.hpp deleted file mode 100644 index b2aada2..0000000 --- a/src/qf_pkg.hpp +++ /dev/null @@ -1,203 +0,0 @@ -//$file${include::qf_pkg.hpp} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv -// -// Model: qpcpp.qm -// File: ${include::qf_pkg.hpp} -// -// This code has been generated by QM 5.2.5 . -// DO NOT EDIT THIS FILE MANUALLY. All your changes will be lost. -// -// This code is covered by the following QP license: -// License # : LicenseRef-QL-dual -// Issued to : Any user of the QP/C++ real-time embedded framework -// Framework(s) : qpcpp -// Support ends : 2023-12-31 -// License scope: -// -// Copyright (C) 2005 Quantum Leaps, LLC . -// -// SPDX-License-Identifier: GPL-3.0-or-later OR LicenseRef-QL-commercial -// -// This software is dual-licensed under the terms of the open source GNU -// General Public License version 3 (or any later version), or alternatively, -// under the terms of one of the closed source Quantum Leaps commercial -// licenses. -// -// The terms of the open source GNU General Public License version 3 -// can be found at: -// -// The terms of the closed source Quantum Leaps commercial licenses -// can be found at: -// -// Redistributions in source code must retain this top-level comment block. -// Plagiarizing this software to sidestep the license obligations is illegal. -// -// Contact information: -// -// -// -//$endhead${include::qf_pkg.hpp} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -//! @file -//! @brief Internal (package scope) QF/C++ interface. - -#ifndef QF_PKG_HPP_ -#define QF_PKG_HPP_ - -//============================================================================ -//! helper macro to cast const away from an event pointer -#define QF_CONST_CAST_(type_, ptr_) const_cast(ptr_) - -//$declare${QF::QF-pkg} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv -namespace QP { -namespace QF { - -//${QF::QF-pkg::readySet_} ................................................... -//! "Ready-set" of all threads used in the built-in kernels -extern QPSet readySet_; - -//${QF::QF-pkg::ePool_[QF_MAX_EPOOL]} ........................................ -#if (QF_MAX_EPOOL > 0U) -//! event pools managed by QF -extern QF_EPOOL_TYPE_ ePool_[QF_MAX_EPOOL]; -#endif // (QF_MAX_EPOOL > 0U) - -//${QF::QF-pkg::maxPool_} .................................................... -//! number of initialized event pools -extern std::uint_fast8_t maxPool_; - -//${QF::QF-pkg::bzero} ....................................................... -//! Clear a specified region of memory to zero -//! -//! @details -//! Clears a memory buffer by writing zeros byte-by-byte. -//! -//! @param[in] start pointer to the beginning of a memory buffer. -//! @param[in] len length of the memory buffer to clear (in bytes) -//! -//! @note The main application of this function is clearing the internal -//! QF variables upon startup. This is done to avoid problems with -//! non-standard startup code provided with some compilers and toolchains -//! (e.g., TI DSPs or Microchip MPLAB), which does not zero the -//! uninitialized variables, as required by the C++ standard. -void bzero( - void * const start, - std::uint_fast16_t const len) noexcept; - -} // namespace QF -} // namespace QP -//$enddecl${QF::QF-pkg} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -//============================================================================ -namespace QP { - -//............................................................................ -//! Structure representing a free block in the Native QF Memory Pool -//! @sa QP::QMPool -struct QFreeBlock { - QFreeBlock * volatile m_next; //!< link to the next free block -}; - -//............................................................................ -// The following flags and flags and bitmasks are for the fields of the -// `QEvt.refCtr_` attribute of the QP::QTimeEvt class (subclass of QP::QEvt). -// This attribute is NOT used for reference counting in time events -// because the `QEvt.poolId_` attribute is zero ("static event"). -// -constexpr std::uint8_t TE_IS_LINKED = 1U << 7U; // flag -constexpr std::uint8_t TE_WAS_DISARMED = 1U << 6U; // flag -constexpr std::uint8_t TE_TICK_RATE = 0x0FU; // bitmask - -// internal helper inline functions - -//! increment the refCtr_ of an event `e` -inline void QEvt_refCtr_inc_(QEvt const * const e) noexcept { - (QF_CONST_CAST_(QEvt*, e))->refCtr_ = e->refCtr_ + 1U; -} - -//! decrement the refCtr_ of an event `e` -inline void QEvt_refCtr_dec_(QEvt const * const e) noexcept { - (QF_CONST_CAST_(QEvt*, e))->refCtr_ = e->refCtr_ - 1U; -} - -} // namespace QP - -//============================================================================ -// QF-specific critical section... -#ifndef QF_CRIT_STAT_TYPE - //! This is an internal macro for defining the critical section - //! status type. - //! - //! @details - //! The purpose of this macro is to enable writing the same code for the - //! case when critical section status type is defined and when it is not. - //! If the macro #QF_CRIT_STAT_TYPE is defined, this internal macro - //! provides the definition of the critical section status variable. - //! Otherwise this macro is empty. - //! @sa #QF_CRIT_STAT_TYPE - #define QF_CRIT_STAT_ - - //! This is an internal macro for entering a critical section. - //! - //! @details - //! The purpose of this macro is to enable writing the same code for the - //! case when critical section status type is defined and when it is not. - //! If the macro #QF_CRIT_STAT_TYPE is defined, this internal macro - //! invokes QF_CRIT_ENTRY() passing the key variable as the parameter. - //! Otherwise QF_CRIT_ENTRY() is invoked with a dummy parameter. - //! @sa QF_CRIT_ENTRY() - #define QF_CRIT_E_() QF_CRIT_ENTRY(dummy) - - //! This is an internal macro for exiting a critical section. - //! - //! @details - //! The purpose of this macro is to enable writing the same code for the - //! case when critical section status type is defined and when it is not. - //! If the macro #QF_CRIT_STAT_TYPE is defined, this internal macro - //! invokes QF_CRIT_EXIT() passing the key variable as the parameter. - //! Otherwise QF_CRIT_EXIT() is invoked with a dummy parameter. - //! @sa QF_CRIT_EXIT() - //! - #define QF_CRIT_X_() QF_CRIT_EXIT(dummy) - -#elif (!defined QF_CRIT_STAT_) - #define QF_CRIT_STAT_ QF_CRIT_STAT_TYPE critStat_; - #define QF_CRIT_E_() QF_CRIT_ENTRY(critStat_) - #define QF_CRIT_X_() QF_CRIT_EXIT(critStat_) -#endif // QF_CRIT_STAT_TYPE - -// Assertions inside the critical section ------------------------------------ -#ifdef Q_NASSERT // Q_NASSERT defined--assertion checking disabled - - #define Q_ASSERT_CRIT_(id_, test_) static_cast(0) - #define Q_REQUIRE_CRIT_(id_, test_) static_cast(0) - #define Q_ERROR_CRIT_(id_) static_cast(0) - -#else // Q_NASSERT not defined--assertion checking enabled - - #define Q_ASSERT_CRIT_(id_, test_) do { \ - if ((test_)) {} else { \ - QF_CRIT_X_(); \ - Q_onAssert(&Q_this_module_[0], static_cast(id_)); \ - } \ - } while (false) - - #define Q_REQUIRE_CRIT_(id_, test_) Q_ASSERT_CRIT_((id_), (test_)) - - #define Q_ERROR_CRIT_(id_) do { \ - QF_CRIT_X_(); \ - Q_onAssert(&Q_this_module_[0], static_cast(id_)); \ - } while (false) - -#endif // Q_NASSERT - -//! helper macro to test that a pointer `x_` is in range between -//! `min_` and `max_` -//! -//! @details -//! This macro is specifically and exclusively used for checking the range -//! of a block pointer returned to the pool. Such a check must rely on the -//! pointer arithmetic not compliant with the [AUTOSAR Rule M5-0-18]. -//! Defining a specific macro for this purpose allows to selectively -//! disable the warnings for this particular case. -#define QF_PTR_RANGE_(x_, min_, max_) (((min_) <= (x_)) && ((x_) <= (max_))) - -#endif // QF_PKG_HPP_ diff --git a/src/qf_port.cpp b/src/qf_port.cpp index aea2526..c902a74 100644 --- a/src/qf_port.cpp +++ b/src/qf_port.cpp @@ -1,623 +1,925 @@ -/** -* @file -* @brief "Experimental" QF/CPP port to Espressif ESP-IDF (version 4.x) -* @ingroup ports -* @cond -****************************************************************************** -* Last updated for version 6.9.4 -* Last updated on 2022-03-20 -* Copyright (C) 2022 Victor Chavez -* -* Q u a n t u m L e a P s -* ------------------------ -* Modern Embedded Software -* -* Copyright (C) 2005-2022 Quantum Leaps, LLC. All rights reserved. -* This program is open source software: you can redistribute it and/or -* modify it under the terms of the GNU General Public License as published -* by the Free Software Foundation, either version 3 of the License, or -* (at your option) any later version. -* -* Alternatively, this program may be distributed and modified under the -* terms of Quantum Leaps commercial licenses, which expressly supersede -* the GNU General Public License and are specifically designed for -* licensees interested in retaining the proprietary status of their code. -* -* This program is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU General Public License for more details. -* -* You should have received a copy of the GNU General Public License -* along with this program. If not, see . -* -* Contact information: -* -* -****************************************************************************** -* @endcond -*/ -#define QP_IMPL /* this is QP implementation */ -#include "qf_port.hpp" /* QF port */ -#include "qf_pkg.hpp" -#include "qassert.h" -#ifdef Q_SPY /* QS software tracing enabled? */ - #include "qs_port.hpp" /* QS port */ - #include "qs_pkg.hpp" /* QS package-scope internal interface */ -#else - #include "qs_dummy.hpp" /* disable the QS software tracing */ -#endif /* Q_SPY */ - -#include "esp_log.h" -#include "esp_freertos_hooks.h" -#include "freertos/task.h" - -#if ( configSUPPORT_STATIC_ALLOCATION == 0 ) -#error "This QP/C++ port to FreeRTOS requires configSUPPORT_STATIC_ALLOCATION" -#endif - -#if ( configMAX_PRIORITIES < QF_MAX_ACTIVE ) - #error "FreeRTOS configMAX_PRIORITIES must not be less than QF_MAX_ACTIVE" -#endif - - -/* Global objects ----------------------------------------------------------*/ -PRIVILEGED_DATA portMUX_TYPE QF_esp32mux = portMUX_INITIALIZER_UNLOCKED; - -// namespace QP ============================================================== -namespace QP { - -Q_DEFINE_THIS_MODULE("qf_port") - -/* Local objects -----------------------------------------------------------*/ -static void task_function(void *pvParameters); /* FreeRTOS task signature */ -/*==========================================================================*/ - - -void QF::init(void) { - -} -/*..........................................................................*/ - -int_t QF::run(void) { - onStartup(); - // produce the QS_QF_RUN trace record - QS_CRIT_STAT_ - QS_BEGIN_PRE_(QS_QF_RUN, 0U) - QS_END_PRE_() - return 0; /* dummy return to make the compiler happy */ -} -/*..........................................................................*/ -void QF::stop(void) { - onCleanup(); /* cleanup callback */ -} -/*..........................................................................*/ -void QActive::start(QPrioSpec const prioSpec, - QEvt const * * const qSto, std::uint_fast16_t const qLen, - void * const stkSto, std::uint_fast16_t const stkSize, - void const * const par) -{ - Q_REQUIRE_ID(200, - (qSto != nullptr) /* queue storage */ - && (qLen > 0U) /* queue size */ - //&& (stkSto != nullptr) /* stack storage */ - && (stkSize > 0U)); // stack size - - /* create the event queue for the AO */ - m_eQueue.init(qSto, qLen); - - m_prio = static_cast(prioSpec & 0xFFU); // QF-priority - register_(); // make QF aware of this AO - init(par, m_prio); /* the top-most initial tran. */ - QS_FLUSH(); /* flush the QS trace buffer to the host */ - - /* task name provided by the user in QF_setTaskName() or default name */ - char const *taskName = (m_thread != nullptr) - ? reinterpret_cast(m_thread) - : static_cast("AO"); - - /* statically create the FreeRTOS task for the AO */ - BaseType_t task_res = xTaskCreatePinnedToCore( - &task_function, /* the task function */ - taskName , /* the name of the task */ - stkSize/sizeof(portSTACK_TYPE), /* stack size */ - this, /* the 'pvParameters' parameter */ - m_prio + tskIDLE_PRIORITY, /* FreeRTOS priority */ - //static_cast(stkSto), /* stack storage */ - &m_thread, /* task buffer */ - static_cast(QP_CPU_NUM)); /* CPU number */ - - Q_ENSURE_ID(210, task_res == pdPASS ); /* must be created */ -} -/*..........................................................................*/ -void QActive::setAttr(std::uint32_t attr1, void const *attr2) { - /* this function must be called before Qregistry_START(), - * which implies that me->thread.pxDummy1 must not be used yet; - */ - Q_REQUIRE_ID(300, m_thread == nullptr); - switch (attr1) { - case TASK_NAME_ATTR: - /* temporarily store the name */ - m_thread = reinterpret_cast(const_cast(attr2)); /* cast 'const' away */ - break; - /* ... */ - } -} - -// thread for active objects ------------------------------------------------- -void QActive::thread_(QActive *act) { - /* event-loop */ - for (;;) { /* for-ever */ - QEvt const *e = act->get_(); // wait for event - act->dispatch(e, act->m_prio); // dispatch to the AO's state machine - QF::gc(e); // check if the event is garbage, and collect it if so - } -} -/*..........................................................................*/ -static void task_function(void *pvParameters) { /* FreeRTOS task signature */ - QP::QActive::thread_(reinterpret_cast(pvParameters)); -} - -/*==========================================================================*/ -/* The "FromISR" QP APIs for the FreeRTOS port... */ -bool QActive::postFromISR_(QEvt const * const e, - std::uint_fast16_t const margin, void *par, - void const * const sender) noexcept -{ - QEQueueCtr nFree; /* temporary to avoid UB for volatile access */ - bool status; - - /** @pre event pointer must be valid */ - Q_REQUIRE_ID(400, e != nullptr); - - portENTER_CRITICAL_ISR(&QF_esp32mux); - nFree = m_eQueue.m_nFree; /* get volatile into the temporary */ - if (margin == QF::NO_MARGIN) { - if (nFree > 0U) { - status = true; /* can post */ - } - else { - status = false; /* cannot post */ - Q_ERROR_ID(810); /* must be able to post the event */ - } - } - else if (nFree > static_cast(margin)) { - status = true; /* can post */ - } - else { - status = false; /* cannot post */ - } - - if (status) { /* can post the event? */ - QS_BEGIN_NOCRIT_PRE_(QS_QF_ACTIVE_POST, m_prio) - QS_TIME_PRE_(); /* timestamp */ - QS_OBJ_PRE_(sender); /* the sender object */ - QS_SIG_PRE_(e->sig); /* the signal of the event */ - QS_OBJ_PRE_(this); /* this active object (recipient) */ - QS_2U8_PRE_(e->poolId_, e->refCtr_); /* pool Id & ref Count */ - QS_EQC_PRE_(nFree); /* number of free entries */ - QS_EQC_PRE_(m_eQueue.m_nMin); /* min number of free entries */ - QS_END_NOCRIT_PRE_() - - /* is it a pool event? */ - if (e->poolId_ != 0U) { - QEvt_refCtr_inc_(e); /* increment the reference counter */ - } - --nFree; /* one free entry just used up */ - m_eQueue.m_nFree = nFree; /* update the volatile */ - if (m_eQueue.m_nMin > nFree) { - m_eQueue.m_nMin = nFree; /* update minimum so far */ - } - - /* empty queue? */ - if (m_eQueue.m_frontEvt == nullptr) { - m_eQueue.m_frontEvt = e; /* deliver event directly */ - portEXIT_CRITICAL_ISR(&QF_esp32mux); - /* signal the event queue */ - vTaskNotifyGiveFromISR(static_cast(m_thread), - static_cast(par)); - } - /* queue is not empty, insert event into the ring-buffer */ - else { - /* insert event into the ring buffer (FIFO) */ - m_eQueue.m_ring[m_eQueue.m_head] = e; - if (m_eQueue.m_head == 0U) { /* need to wrap head? */ - m_eQueue.m_head = m_eQueue.m_end; /* wrap around */ - } - --m_eQueue.m_head; /* advance the head (counter clockwise) */ - portEXIT_CRITICAL_ISR(&QF_esp32mux); - } - } - else { - - QS_BEGIN_NOCRIT_PRE_(QS_QF_ACTIVE_POST_ATTEMPT, m_prio) - QS_TIME_PRE_(); /* timestamp */ - QS_OBJ_PRE_(sender); /* the sender object */ - QS_SIG_PRE_(e->sig); /* the signal of the event */ - QS_OBJ_PRE_(this); /* this active object (recipient) */ - QS_2U8_PRE_(e->poolId_, e->refCtr_); /* pool Id & ref Count */ - QS_EQC_PRE_(nFree); /* number of free entries */ - QS_EQC_PRE_(margin); /* margin requested */ - QS_END_NOCRIT_PRE_() - - portEXIT_CRITICAL_ISR(&QF_esp32mux); - - QF::gcFromISR(e); // recycle the event to avoid a leak - } - - return status; -} - -/*..........................................................................*/ -void QActive::publishFromISR_(QEvt const *e, void *par, - void const * const sender) noexcept -{ - QPSet subscrList; /* local, modifiable copy of the subscriber list */ - - /** @pre the published signal must be within the configured range */ - Q_REQUIRE_ID(500, static_cast(e->sig) < QActive::maxPubSignal_); - - portENTER_CRITICAL_ISR(&QF_esp32mux); - - QS_BEGIN_NOCRIT_PRE_(QS_QF_PUBLISH, 0U) - QS_TIME_PRE_(); /* the timestamp */ - QS_OBJ_PRE_(sender); /* the sender object */ - QS_SIG_PRE_(e->sig); /* the signal of the event */ - QS_2U8_PRE_(e->poolId_, e->refCtr_);/* pool-Id & ref-Count */ - QS_END_NOCRIT_PRE_() - - /* is it a dynamic event? */ - if (e->poolId_ != 0U) { - /* NOTE: The reference counter of a dynamic event is incremented to - * prevent premature recycling of the event while the multicasting - * is still in progress. At the end of the function, the garbage - * collector step (QF_gcFromISR()) decrements the reference counter and - * recycles the event if the counter drops to zero. This covers the - * case when the event was published without any subscribers. - */ - QEvt_refCtr_inc_(e); - } - - /* make a local, modifiable copy of the subscriber list */ - subscrList = QActive::subscrList_[e->sig]; - portEXIT_CRITICAL_ISR(&QF_esp32mux); - - if (subscrList.notEmpty()) { /* any subscribers? */ - // the highest-prio subscriber - std::uint_fast8_t p = subscrList.findMax(); - - /* no need to lock the scheduler in the ISR context */ - do { /* loop over all subscribers */ - /* the prio of the AO must be registered with the framework */ - Q_ASSERT_ID(510, registry_[p] != nullptr); - - // POST_FROM_ISR() asserts internally if the queue overflows - registry_[p]->POST_FROM_ISR(e, par, sender); - - subscrList.remove(p); // remove the handled subscriber - if (subscrList.notEmpty()) { // still more subscribers? - p = subscrList.findMax(); // the highest-prio subscriber - } - else { - p = 0U; /* no more subscribers */ - } - } while (p != 0U); - /* no need to unlock the scheduler in the ISR context */ - } - - /* The following garbage collection step decrements the reference counter - * and recycles the event if the counter drops to zero. This covers both - * cases when the event was published with or without any subscribers. - */ - QF::gcFromISR(e); -} - -/*..........................................................................*/ -void QTimeEvt::tickFromISR_(std::uint_fast8_t const tickRate, void *par, - void const * const sender) noexcept -{ - QTimeEvt *prev = &timeEvtHead_[tickRate]; - - portENTER_CRITICAL_ISR(&QF_esp32mux); - - QS_BEGIN_NOCRIT_PRE_(QS_QF_TICK, 0U) - ++prev->m_ctr; - QS_TEC_PRE_(prev->m_ctr); /* tick ctr */ - QS_U8_PRE_(tickRate); /* tick rate */ - QS_END_NOCRIT_PRE_() - - /* scan the linked-list of time events at this rate... */ - for (;;) { - QTimeEvt *t = prev->m_next; /* advance down the time evt. list */ - - /* end of the list? */ - if (t == nullptr) { - - /* any new time events armed since the last run of QF_tickX_()? */ - if (timeEvtHead_[tickRate].m_act != nullptr) { - - /* sanity check */ - Q_ASSERT_ID(610, prev != nullptr); - prev->m_next = timeEvtHead_[tickRate].toTimeEvt(); - timeEvtHead_[tickRate].m_act = nullptr; - t = prev->m_next; /* switch to the new list */ - } - else { - break; /* all currently armed time evts. processed */ - } - } - - /* time event scheduled for removal? */ - if (t->m_ctr == 0U) { - prev->m_next = t->m_next; - /* mark time event 't' as NOT linked */ - t->refCtr_ &= static_cast(~TE_IS_LINKED); - /* do NOT advance the prev pointer */ - /* exit crit. section to reduce latency */ - portEXIT_CRITICAL_ISR(&QF_esp32mux); - } - else { - --t->m_ctr; - - /* is time event about to expire? */ - if (t->m_ctr == 0U) { - QActive *act = t->toActive(); // temp. for volatile - - /* periodic time evt? */ - if (t->m_interval != 0U) { - t->m_ctr = t->m_interval; /* rearm the time event */ - prev = t; /* advance to this time event */ - } - /* one-shot time event: automatically disarm */ - else { - prev->m_next = t->m_next; - /* mark time event 't' as NOT linked */ - t->refCtr_ &= static_cast(~TE_IS_LINKED); - /* do NOT advance the prev pointer */ - - QS_BEGIN_NOCRIT_PRE_(QS_QF_TIMEEVT_AUTO_DISARM, - act->m_prio) - QS_OBJ_PRE_(t); /* this time event object */ - QS_OBJ_PRE_(act); /* the target AO */ - QS_U8_PRE_(tickRate); /* tick rate */ - QS_END_NOCRIT_PRE_() - } - - QS_BEGIN_NOCRIT_PRE_(QS_QF_TIMEEVT_POST, act->m_prio) - QS_TIME_PRE_(); /* timestamp */ - QS_OBJ_PRE_(t); /* the time event object */ - QS_SIG_PRE_(t->sig); /* signal of this time event */ - QS_OBJ_PRE_(act); /* the target AO */ - QS_U8_PRE_(tickRate); /* tick rate */ - QS_END_NOCRIT_PRE_() - - /* exit critical section before posting */ - portEXIT_CRITICAL_ISR(&QF_esp32mux); - - // asserts if queue overflows - (void)act->POST_FROM_ISR(t, par, sender); - } - else { - prev = t; /* advance to this time event */ - /* exit crit. section to reduce latency */ - portEXIT_CRITICAL_ISR(&QF_esp32mux); - } - } - /* re-enter crit. section to continue */ - portENTER_CRITICAL_ISR(&QF_esp32mux); - } - portEXIT_CRITICAL_ISR(&QF_esp32mux); -} - - -/*..........................................................................*/ -QEvt *QF::newXfromISR_(std::uint_fast16_t const evtSize, - std::uint_fast16_t const margin, - enum_t const sig) noexcept -{ - QEvt *e; - std::uint_fast8_t idx; - - /* find the pool index that fits the requested event size ... */ - for (idx = 0U; idx < QF::maxPool_; ++idx) { - if (evtSize <= QF_EPOOL_EVENT_SIZE_(QF::ePool_[idx])) { - break; - } - } - /* cannot run out of registered pools */ - Q_ASSERT_ID(710, idx < QF::maxPool_); - - - /* get e -- platform-dependent */ -#ifdef Q_SPY - e = static_cast( - QF::ePool_[idx].getFromISR(((margin != QF::NO_MARGIN) - ? margin : 0U), - static_cast(QS_EP_ID) + idx + 1U)); -#else - e = static_cast( - QF::ePool_[idx].getFromISR(((margin != QF::NO_MARGIN) - ? margin : 0U), 0U)); -#endif - - /* was e allocated correctly? */ - if (e != nullptr) { - e->sig = static_cast(sig); // set the signal - e->poolId_ = static_cast(idx + 1U); /* store the pool ID */ - e->refCtr_ = 0U; /* set the reference counter to 0 */ - -#ifdef Q_SPY - portENTER_CRITICAL_ISR(&QF_esp32mux); - QS_BEGIN_PRE_(QS_QF_NEW, static_cast(QS_EP_ID) + idx + 1U) - QS_TIME_PRE_(); /* timestamp */ - QS_EVS_PRE_(evtSize); /* the size of the event */ - QS_SIG_PRE_(sig); /* the signal of the event */ - QS_END_NOCRIT_PRE_() - portEXIT_CRITICAL_ISR(&QF_esp32mux); -#endif /* Q_SPY */ - } - /* event cannot be allocated */ - else { - /* must tolerate bad alloc. */ - Q_ASSERT_ID(720, margin != QF::NO_MARGIN); - -#ifdef Q_SPY - portENTER_CRITICAL_ISR(&QF_esp32mux); - QS_BEGIN_PRE_(QS_QF_NEW_ATTEMPT, static_cast(QS_EP_ID) + idx + 1U) - QS_TIME_PRE_(); /* timestamp */ - QS_EVS_PRE_(evtSize); /* the size of the event */ - QS_SIG_PRE_(sig); /* the signal of the event */ - QS_END_NOCRIT_PRE_() - portEXIT_CRITICAL_ISR(&QF_esp32mux); -#endif /* Q_SPY */ - } - return e; /* can't be NULL if we can't tolerate bad allocation */ -} -/*..........................................................................*/ -void QF::gcFromISR(QEvt const * const e) noexcept { - - /* is it a dynamic event? */ - if (e->poolId_ != 0U) { - portENTER_CRITICAL_ISR(&QF_esp32mux); - - /* isn't this the last ref? */ - if (e->refCtr_ > 1U) { - QEvt_refCtr_dec_(e); /* decrements the ref counter */ - - QS_BEGIN_NOCRIT_PRE_(QS_QF_GC_ATTEMPT, - static_cast(e->poolId_)) - QS_TIME_PRE_(); /* timestamp */ - QS_SIG_PRE_(e->sig); /* the signal of the event */ - QS_2U8_PRE_(e->poolId_, e->refCtr_); /* pool Id & ref Count */ - QS_END_NOCRIT_PRE_() - - portEXIT_CRITICAL_ISR(&QF_esp32mux); - } - /* this is the last reference to this event, recycle it */ - else { - std::uint_fast8_t idx = - static_cast(e->poolId_) - 1U; - - QS_BEGIN_NOCRIT_PRE_(QS_QF_GC, - static_cast(e->poolId_)) - QS_TIME_PRE_(); /* timestamp */ - QS_SIG_PRE_(e->sig); /* the signal of the event */ - QS_2U8_PRE_(e->poolId_, e->refCtr_); /* pool Id & ref Count */ - QS_END_NOCRIT_PRE_() - - portEXIT_CRITICAL_ISR(&QF_esp32mux); - - /* pool ID must be in range */ - Q_ASSERT_ID(810, idx < QF::maxPool_); -#ifdef Q_EVT_VIRTUAL - // explicitly exectute the destructor' - // NOTE: casting 'const' away is legitimate, - // because it's a pool event - QF_EVT_CONST_CAST_(e)->~QEvt(); // xtor, -#endif -#ifdef Q_SPY - // cast 'const' away, which is OK, because it's a pool event - QF::ePool_[idx].putFromISR(QF_CONST_CAST_(QEvt*, e), - static_cast(QS_EP_ID) + e->poolId_); -#else - QF::ePool_[idx].putFromISR(QF_CONST_CAST_(QEvt*, e), 0U); -#endif - } - } -} - - -/*..........................................................................*/ -void QMPool::putFromISR(void *b, std::uint_fast8_t const qs_id) noexcept { - - /** @pre # free blocks cannot exceed the total # blocks and - * the block pointer must be from this pool. - */ - Q_REQUIRE_ID(900, (m_nFree < m_nTot) - && QF_PTR_RANGE_(b, m_start, m_end)); - - (void)qs_id; /* unused parameter (outside Q_SPY build configuration) */ - portENTER_CRITICAL_ISR(&QF_esp32mux); - - static_cast(b)->m_next = - static_cast(m_free_head); // link into the free list - m_free_head = b; /* set as new head of the free list */ - ++m_nFree; /* one more free block in this pool */ - - QS_BEGIN_NOCRIT_PRE_(QS_QF_MPOOL_PUT, qs_id) - QS_TIME_PRE_(); /* timestamp */ - QS_OBJ_PRE_(m_start); /* the memory managed by this pool */ - QS_MPC_PRE_(m_nFree); /* # free blocks in the pool */ - QS_END_NOCRIT_PRE_() - - portEXIT_CRITICAL_ISR(&QF_esp32mux); -} -/*..........................................................................*/ -void *QMPool::getFromISR(std::uint_fast16_t const margin, - std::uint_fast8_t const qs_id) noexcept { - QFreeBlock *fb; - - (void)qs_id; /* unused parameter (outside Q_SPY build configuration) */ - portENTER_CRITICAL_ISR(&QF_esp32mux); - - /* have more free blocks than the requested margin? */ - if (m_nFree > static_cast(margin)) { - - fb = static_cast(m_free_head); // get a free block - - /* the pool has some free blocks, so a free block must be available */ - Q_ASSERT_ID(910, fb != nullptr); - - void *fb_next = fb->m_next; /* put volatile to a temporary to avoid UB */ - - /* is the pool becoming empty? */ - --m_nFree; /* one less free block */ - if (m_nFree == 0) { - /* pool is becoming empty, so the next free block must be NULL */ - Q_ASSERT_ID(920, fb_next == nullptr); - - m_nMin = 0U; /* remember that the pool got empty */ - } - else { - /* pool is not empty, so the next free block must be in range - * - * NOTE: the next free block pointer can fall out of range - * when the client code writes past the memory block, thus - * corrupting the next block. - */ - Q_ASSERT_ID(930, QF_PTR_RANGE_(fb_next, m_start, m_end)); - - /* is the number of free blocks the new minimum so far? */ - if (m_nMin > m_nFree) { - m_nMin = m_nFree; /* remember the new minimum */ - } - } - - m_free_head = fb_next; /* set the head to the next free block */ - - QS_BEGIN_NOCRIT_PRE_(QS_QF_MPOOL_GET, qs_id) - QS_TIME_PRE_(); /* timestamp */ - QS_OBJ_PRE_(m_start); /* the memory managed by this pool */ - QS_MPC_PRE_(m_nFree); /* # free blocks in the pool */ - QS_MPC_PRE_(m_nMin); /* min # free blocks ever in the pool */ - QS_END_NOCRIT_PRE_() - - } - /* don't have enough free blocks at this point */ - else { - fb = nullptr; - - QS_BEGIN_NOCRIT_PRE_(QS_QF_MPOOL_GET_ATTEMPT, qs_id) - QS_TIME_PRE_(); /* timestamp */ - QS_OBJ_PRE_(m_start); /* the memory managed by this pool */ - QS_MPC_PRE_(m_nFree); /* # free blocks in the pool */ - QS_MPC_PRE_(margin); /* the requested margin */ - QS_END_NOCRIT_PRE_() - } - portEXIT_CRITICAL_ISR(&QF_esp32mux); - - return fb; /* return the pointer to memory block or NULL to the caller */ -} - - -} // namespace QP +//============================================================================ +// QP/C++ Real-Time Embedded Framework (RTEF) +// Version 8.0.2 +// +// Copyright (C) 2005 Quantum Leaps, LLC. All rights reserved. +// +// 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 (GPL) or under the terms of one of the closed- +// source Quantum Leaps commercial licenses. +// +// Redistributions in source code must retain this top-level comment block. +// Plagiarizing this software to sidestep the license obligations is illegal. +// +// NOTE: +// The GPL does NOT permit the incorporation of this code into proprietary +// programs. Please contact Quantum Leaps for commercial licensing options, +// which expressly supersede the GPL and are designed explicitly for +// closed-source distribution. +// +// Quantum Leaps contact information: +// +// +//============================================================================ +#define QP_IMPL // this is QP implementation +#include "qp_port.hpp" // QP port +#include "qp_pkg.hpp" // QP package-level 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 package-scope internal interface +#else + #include "qs_dummy.hpp" // disable the QS software tracing +#endif // Q_SPY + +#if ( configSUPPORT_STATIC_ALLOCATION == 0 ) + #error This QP/C++ port to FreeRTOS requires configSUPPORT_STATIC_ALLOCATION +#endif + +#if ( configMAX_PRIORITIES < QF_MAX_ACTIVE ) + #error FreeRTOS configMAX_PRIORITIES must not be less than QF_MAX_ACTIVE +#endif + +namespace { // unnamed local namespace + +Q_DEFINE_THIS_MODULE("qf_port") + +// Local objects ------------------------------------------------------------- +static void task_function(void *pvParameters) { // FreeRTOS task signature + QP::QActive::evtLoop_(reinterpret_cast(pvParameters)); +} + +} // unnamed local namespace + +// The following macro provides the number of free slots in the FreeRTOS +// queue. +// +// NOTE1: +// The official FreeRTOS API uxQueueSpacesAvailable() is not used +// here, because that API uses task-level critical section internally. +// Instead, the free slots calculation happens here in already +// established critical section. Unfortunately, the bizarre "information +// obfuscating" policy of FreeRTOS (incorrectly called "information +// hiding") forces the use of the StaticQueue_t with "dummy" members. +// This could potentially break in the future releases of FreeRTOS. +// +// Currently, the correspondence between xQUEUE and StaticQueue_t +// is as follows (see queue.c and FreeRTOS.h, respectively): +// +// xQUEUE.uxMessagesWaiting == StaticQueue_t.uxDummy4[0]; +// xQUEUE.uxLength == StaticQueue_t.uxDummy4[1]; +// +#define FREERTOS_QUEUE_GET_FREE() \ + (m_osObject.uxDummy4[1] - m_osObject.uxDummy4[0]) + +/* Global objects ----------------------------------------------------------*/ +PRIVILEGED_DATA portMUX_TYPE QF_esp32mux = portMUX_INITIALIZER_UNLOCKED; + +// namespace QP ============================================================== +namespace QP { + +//............................................................................ +void QF::init() { + bzero_(&QF::priv_, sizeof(QF::priv_)); + bzero_(&QActive::registry_[0], sizeof(QActive::registry_)); + // nothing to do for FreeRTOS +} +//............................................................................ +int_t QF::run() { + onStartup(); // the startup callback (configure/enable interrupts) + + // produce the QS_QF_RUN trace record +#ifdef Q_SPY + QF_CRIT_STAT + QF_CRIT_ENTRY(); + QS_BEGIN_PRE(QS_QF_RUN, 0U) + QS_END_PRE() + QF_CRIT_EXIT(); +#endif + + // vTaskStartScheduler(); // start the FreeRTOS scheduler + + // QF_CRIT_ENTRY(); + // Q_ERROR_INCRIT(110); // the FreeRTOS scheduler should never return + // QF_CRIT_EXIT(); + + return 0; // dummy return to make the compiler happy +} +//............................................................................ +void QF::stop() { + onCleanup(); // cleanup callback +} + +// thread for active objects ------------------------------------------------- +void QActive::evtLoop_(QActive *act) { +#ifdef QACTIVE_CAN_STOP + while (act->m_eQueue != static_cast(0)) +#else + for (;;) // for-ever +#endif + { + QEvt const *e = act->get_(); // wait for event + act->dispatch(e, act->m_prio); // dispatch to the SM + QF::gc(e); // check if the event is garbage, and collect it if so + } +#ifdef QACTIVE_CAN_STOP + act->unregister_(); // remove this object from the framewrok + vTaskDelete(static_cast(0)); // delete this FreeRTOS task +#endif +} + +//............................................................................ +void QActive::start( + QPrioSpec const prioSpec, + QEvtPtr * const qSto, + std::uint_fast16_t const qLen, + void * const stkSto, + std::uint_fast16_t const stkSize, + void const * const par) +{ + QF_CRIT_STAT + QF_CRIT_ENTRY(); + // precondition: + // - queue storage must be provided + // - queue size must be provided + // - stack storage must be provided + // - stack size must be provided + Q_REQUIRE_INCRIT(100, + (qSto != nullptr) + && (qLen > 0U) +#if (QP_STATIC_THREAD == 1) + && (stkSto != nullptr) +#endif + && (stkSize > 0U)); + QF_CRIT_EXIT(); + + // create FreeRTOS message queue + m_eQueue = xQueueCreateStatic( + static_cast(qLen), // length of the queue + static_cast(sizeof(QEvt *)), // element size + reinterpret_cast(qSto), // queue buffer + &m_osObject); // static queue buffer + QF_CRIT_ENTRY(); + Q_ASSERT_INCRIT(110, m_eQueue != static_cast(0)); + QF_CRIT_EXIT(); + + m_prio = static_cast(prioSpec & 0xFFU); // QF-priority + m_pthre = 0U; // preemption-threshold (not used) + register_(); // register this AO + + // top-most initial tran. (virtual call) + init(par, m_prio); + QS_FLUSH(); // flush the trace buffer to the host + + // task name provided by the user in QActive::setAttr() or default name +#if (QP_STATIC_THREAD == 0) + char const *taskName = (m_thread != nullptr) + ? reinterpret_cast(m_thread) + : static_cast("AO"); +#else // QP_STATIC_THREAD == 1 + char const *taskName = (m_thread.pxDummy1 != nullptr) + ? static_cast(m_thread.pxDummy1) + : static_cast("AO"); +#endif // QP_STATIC_THREAD + + // The FreeRTOS priority of the AO thread can be specified in two ways: + // + // 1. Implicitly based on the AO's priority (by the formula specified + // in the macro FREERTOS_TASK_PRIO(), see qp_port.h). This option + // is chosen, when the higher-byte of the prioSpec parameter is set + // to zero. + // + // 2. Explicitly as the higher-byte of the prioSpec parameter. + // This option is chosen when the prioSpec parameter is not-zero. + // For example, Q_PRIO(10U, 5U) will explicitly specify AO priority + // as 10 and FreeRTOS priority as 5. + // + // NOTE: The explicit FreeRTOS priority is NOT sanity-checked, + // so it is the responsibility of the application to ensure that + // it is consistent with the AO's priority. An example of + // inconsistent setting would be assigning FreeRTOS priorities that + // would result in a different relative prioritization of AO's threads + // than indicated by the AO priorities assigned. + // + UBaseType_t freertos_prio = (prioSpec >> 8U); + if (freertos_prio == 0U) { + freertos_prio = FREERTOS_TASK_PRIO(m_prio); + } + +#if (QP_STATIC_THREAD == 0) + // Dynamically create the FreeRTOS task for the AO + BaseType_t task_res = xTaskCreatePinnedToCore( + &task_function, // the task function + taskName , // the name of the task + stkSize/sizeof(portSTACK_TYPE), // stack length + this, // the 'pvParameters' parameter + m_prio + tskIDLE_PRIORITY, // FreeRTOS priority + &m_thread, // task buffer + static_cast(QP_CPU_NUM)); // CPU number + + QF_CRIT_ENTRY(); + Q_ENSURE_ID(120, task_res == pdPASS ); /* must be created */ + QF_CRIT_EXIT(); +#else + // statically create the FreeRTOS task for the AO + TaskHandle_t task = xTaskCreateStaticPinnedToCore( + &task_function, // the task function + taskName , // the name of the task + stkSize/sizeof(portSTACK_TYPE), // stack length + this, // the 'pvParameters' parameter + m_prio + tskIDLE_PRIORITY, // FreeRTOS priority + static_cast(stkSto), // stack storage + &m_thread, // task buffer + static_cast(QP_CPU_NUM)); // CPU number + + QF_CRIT_ENTRY(); + Q_ASSERT_INCRIT(120, task != static_cast(0)); + QF_CRIT_EXIT(); +#endif + +#ifdef Q_UNSAFE + Q_UNUSED_PAR(task); +#endif +} +//............................................................................ +#ifdef QACTIVE_CAN_STOP +void QActive::stop() { + unsubscribeAll(); // unsubscribe from all events + m_eQueue = static_cast(0); // stop thread (see QF::thread_) +} +#endif +//............................................................................ +void QActive::setAttr(std::uint32_t attr1, void const *attr2) { + QF_CRIT_STAT + QF_CRIT_ENTRY(); + // this function must be called before QACTIVE_START(), + // which implies that m_thread.pxDummy1 must not be used yet; +#if (QP_STATIC_THREAD == 0) + Q_REQUIRE_ID(150, m_thread == nullptr); + switch (attr1) { + case TASK_NAME_ATTR: + // temporarily store the name, cast 'const' away + m_thread = reinterpret_cast(const_cast(attr2)); + break; + /* ... */ + } +#else + Q_REQUIRE_INCRIT(150, m_thread.pxDummy1 == nullptr); + switch (attr1) { + case TASK_NAME_ATTR: + // temporarily store the name, cast 'const' away + m_thread.pxDummy1 = const_cast(attr2); + break; + // ... + default: + break; + } +#endif + QF_CRIT_EXIT(); +} + +//============================================================================ +bool QActive::post_(QEvt const * const e, std::uint_fast16_t const margin, + void const * const sender) noexcept +{ + Q_UNUSED_PAR(sender); // unused when Q_SPY is undefined + + QF_CRIT_STAT + QF_CRIT_ENTRY(); + + Q_REQUIRE_INCRIT(200, e != nullptr); + + // the number of free slots available in the queue + std::uint_fast16_t nFree = + static_cast(FREERTOS_QUEUE_GET_FREE()); + + // required margin available? + bool status = false; // assume that event cannot be posted + if (margin == QF::NO_MARGIN) { + if (nFree > 0U) { // free entries available in the queue? + status = true; // can post + } + else { // no free entries available + // The queue overflows, but QF_NO_MARGIN indicates that + // the "event delivery guarantee" is required. + Q_ERROR_INCRIT(210); // must be able to post the event + } + } + else if (nFree > margin) { // enough free entries? + status = true; // can post + } + else { + // empty + } + +#if (QF_MAX_EPOOL > 0U) + if (e->poolNum_ != 0U) { // is it a mutable event? + Q_ASSERT_INCRIT(205, e->refCtr_ < (2U * QF_MAX_ACTIVE)); + QEvt_refCtr_inc_(e); // increment the reference counter + } +#endif // (QF_MAX_EPOOL > 0U) + + if (status) { // can post the event? + 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 (recipient) + QS_2U8_PRE(e->poolNum_, e->refCtr_); + QS_EQC_PRE(static_cast(nFree)); // # free entries + QS_EQC_PRE(0U); // min # free entries (unknown) + QS_END_PRE() + + QF_CRIT_EXIT(); + + BaseType_t err = xQueueSendToBack( + m_eQueue, static_cast(&e), 0U); + + QF_CRIT_ENTRY(); + // posting to the FreeRTOS message queue must succeed, see NOTE3 + Q_ASSERT_INCRIT(220, err == pdPASS); + QF_CRIT_EXIT(); + +#ifdef Q_UNSAFE + Q_UNUSED_PAR(err); +#endif + } + else { // event cannot be posted + 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 (recipient) + QS_2U8_PRE(e->poolNum_, e->refCtr_); + QS_EQC_PRE(static_cast(nFree)); // # free entries + QS_EQC_PRE(margin); // margin requested + QS_END_PRE() + + QF_CRIT_EXIT(); + +#if (QF_MAX_EPOOL > 0U) + QF::gc(e); // recycle the event to avoid a leak +#endif // (QF_MAX_EPOOL > 0U) + } + + return status; +} + +//............................................................................ +void QActive::postLIFO(QEvt const * const e) noexcept { + QF_CRIT_STAT + QF_CRIT_ENTRY(); + + Q_REQUIRE_INCRIT(300, e != nullptr); + +#if (QF_MAX_EPOOL > 0U) + if (e->poolNum_ != 0U) { // is it a mutable event? + Q_ASSERT_INCRIT(205, e->refCtr_ < (2U * QF_MAX_ACTIVE)); + QEvt_refCtr_inc_(e); // increment the reference counter + } +#endif // (QF_MAX_EPOOL > 0U) + + 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->poolNum_, e->refCtr_); + QS_EQC_PRE(static_cast(FREERTOS_QUEUE_GET_FREE())); + QS_EQC_PRE(0U); // min # free entries (unknown) + QS_END_PRE() + + QF_CRIT_EXIT(); + + BaseType_t err = xQueueSendToFront( + m_eQueue, static_cast(&e), 0U); + + QF_CRIT_ENTRY(); + // LIFO posting to the FreeRTOS queue must succeed, see NOTE3 + Q_ASSERT_INCRIT(320, err == pdPASS); + QF_CRIT_EXIT(); + +#ifdef Q_UNSAFE + Q_UNUSED_PAR(err); +#endif +} +//............................................................................ +QEvt const *QActive::get_(void) noexcept { + QEvt const *e; + xQueueReceive(m_eQueue, (void *)&e, portMAX_DELAY); + + QS_CRIT_STAT + QS_CRIT_ENTRY(); + 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->poolNum_, e->refCtr_); + QS_EQC_PRE(static_cast(FREERTOS_QUEUE_GET_FREE())); + QS_END_PRE() + QS_CRIT_EXIT(); + + return e; +} + +//============================================================================ +// The "FromISR" QP APIs for the FreeRTOS port... +bool QActive::postFromISR(QEvt const * const e, + std::uint_fast16_t const margin, + void *par, + void const * const sender) noexcept +{ + UBaseType_t uxSavedInterruptStatus = portSET_INTERRUPT_MASK_FROM_ISR(); + + Q_REQUIRE_INCRIT(400, e != nullptr); + + // the number of free slots available in the FreeRTOS queue + std::uint_fast16_t const nFree = + static_cast(FREERTOS_QUEUE_GET_FREE()); + + // required margin available? + bool status = false; // assume that event cannot be posted + if (margin == QF::NO_MARGIN) { + if (nFree > 0U) { // free entries available in the queue? + status = true; // can post + } + else { // no free entries available + // The queue overflows, but QF_NO_MARGIN indicates that + // the "event delivery guarantee" is required. + Q_ERROR_INCRIT(410); // must be able to post the event + } + } + else if (nFree > margin) { // enough free entries? + status = true; // can post + } + else { + // empty + } + +#if (QF_MAX_EPOOL > 0U) + if (e->poolNum_ != 0U) { // is it a mutable event? + Q_ASSERT_INCRIT(405, e->refCtr_ < (2U * QF_MAX_ACTIVE)); + QEvt_refCtr_inc_(e); // increment the reference counter + } +#endif // (QF_MAX_EPOOL > 0U) + + if (status) { // can post the event? + 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 (recipient) + QS_2U8_PRE(e->poolNum_, e->refCtr_); + QS_EQC_PRE(nFree); // # free entries available + QS_EQC_PRE(0U); // min # free entries (unknown) + QS_END_PRE() + + portCLEAR_INTERRUPT_MASK_FROM_ISR(uxSavedInterruptStatus); + + BaseType_t err = xQueueSendToBackFromISR(m_eQueue, + static_cast(&e), + static_cast(par)); + + uxSavedInterruptStatus = portSET_INTERRUPT_MASK_FROM_ISR(); + // posting to the FreeRTOS message queue must succeed, see NOTE3 + Q_ASSERT_INCRIT(420, err == pdPASS); + portCLEAR_INTERRUPT_MASK_FROM_ISR(uxSavedInterruptStatus); + +#ifdef Q_UNSAFE + Q_UNUSED_PAR(err); +#endif + } + else { // event cannot be posted + 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 (recipient) + QS_2U8_PRE(e->poolNum_, e->refCtr_); + QS_EQC_PRE(nFree); // # free entries available + QS_EQC_PRE(margin); // margin requested + QS_END_PRE() + + portCLEAR_INTERRUPT_MASK_FROM_ISR(uxSavedInterruptStatus); + +#if (QF_MAX_EPOOL > 0U) + QF::gcFromISR(e); // recycle the event to avoid a leak +#endif // (QF_MAX_EPOOL > 0U) + } + + return status; +} +//............................................................................ +void QActive::publishFromISR(QEvt const *e, + void *par, + void const * const sender) noexcept +{ + Q_REQUIRE_INCRIT(500, e != nullptr); + + QSignal const sig = e->sig; + + UBaseType_t uxSavedInterruptStatus = portSET_INTERRUPT_MASK_FROM_ISR(); + + // the published signal must be within the configured range + Q_REQUIRE_INCRIT(510, sig < QActive::maxPubSignal_); + + QS_BEGIN_PRE(QS_QF_PUBLISH, 0U) + QS_TIME_PRE(); // the timestamp + QS_OBJ_PRE(sender); // the sender object + QS_SIG_PRE(sig); // the signal of the event + QS_2U8_PRE(e->poolNum_, e->refCtr_); + QS_END_PRE() + + // is it a mutable event? + if (e->poolNum_ != 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::gcFromISR()) decrements the reference counter + // and recycles the event if the counter drops to zero. This covers + // the case when the event was published without any subscribers. + Q_ASSERT_INCRIT(505, e->refCtr_ < (2U * QF_MAX_ACTIVE)); + QEvt_refCtr_inc_(e); + } + + // make a local, modifiable copy of the subscriber set + QPSet subscrSet = QActive::subscrList_[sig].m_set; + + portCLEAR_INTERRUPT_MASK_FROM_ISR(uxSavedInterruptStatus); + + if (subscrSet.notEmpty()) { // any subscribers? + // the highest-prio subscriber + std::uint_fast8_t p = subscrSet.findMax(); + + uxSavedInterruptStatus = portSET_INTERRUPT_MASK_FROM_ISR(); + + // no need to lock the scheduler in the ISR context + QActive *a = registry_[p]; + Q_ASSERT_INCRIT(520, a != nullptr); + + portCLEAR_INTERRUPT_MASK_FROM_ISR(uxSavedInterruptStatus); + + //QF_SCHED_LOCK_(p); // no scheduler locking in FreeRTOS + do { // loop over all subscribers + // QACTIVE_POST() asserts internally if the queue overflows + a->POST_FROM_ISR(e, par, sender); + + subscrSet.remove(p); // remove the handled subscriber + if (subscrSet.notEmpty()) { // still more subscribers? + p = subscrSet.findMax(); // the highest-prio subscriber + + uxSavedInterruptStatus = portSET_INTERRUPT_MASK_FROM_ISR(); + + a = registry_[p]; + // the AO must be registered with the framework + Q_ASSERT_INCRIT(530, a != nullptr); + + portCLEAR_INTERRUPT_MASK_FROM_ISR(uxSavedInterruptStatus); + } + else { + p = 0U; // no more subscribers + } + } while (p != 0U); + + uxSavedInterruptStatus = portSET_INTERRUPT_MASK_FROM_ISR(); + Q_ASSERT_INCRIT(590, p == 0U); // all subscribers processed + portCLEAR_INTERRUPT_MASK_FROM_ISR(uxSavedInterruptStatus); + + //QF_SCHED_UNLOCK_(); // no scheduler locking in FreeRTOS + } + +#if (QF_MAX_EPOOL > 0U) + // The following garbage collection step decrements the reference counter + // and recycles the event if the counter drops to zero. This covers both + // cases when the event was published with or without any subscribers. + QF::gcFromISR(e); +#endif // (QF_MAX_EPOOL > 0U) +} + +//............................................................................ +void QTimeEvt::tickFromISR(std::uint_fast8_t const tickRate, + void *pxHigherPriorityTaskWoken, + void const * const sender) noexcept +{ +#ifndef Q_SPY + Q_UNUSED_PAR(sender); +#endif + + UBaseType_t uxSavedInterruptStatus = portSET_INTERRUPT_MASK_FROM_ISR(); + + Q_REQUIRE_INCRIT(600, tickRate < Q_DIM(timeEvtHead_)); + + QTimeEvt *prev = &timeEvtHead_[tickRate]; + +#ifdef Q_SPY + 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() +#endif // def Q_SPY + + // scan the linked-list of time events at this rate... + while (true) { + Q_ASSERT_INCRIT(610, prev != nullptr); // sanity check + + QTimeEvt *te = prev->m_next; // advance down the time evt. list + + if (te == nullptr) { // end of the list? + + // NO any new time events armed since the last QTimeEvt_tick_()? + if (timeEvtHead_[tickRate].m_act == nullptr) { + break; // terminate the for-loop + } + + prev->m_next = timeEvtHead_[tickRate].toTimeEvt(); + timeEvtHead_[tickRate].m_act = nullptr; + + te = prev->m_next; // switch to the new list + } + + // the time event 'te' must be valid + Q_ASSERT_INCRIT(640, te != nullptr); + + QTimeEvtCtr ctr = te->m_ctr; + + if (ctr == 0U) { // time event scheduled for removal? + prev->m_next = te->m_next; + + // mark time event 'te' as NOT linked + te->m_flags &= static_cast(~QTE_FLAG_IS_LINKED); + // do NOT advance the prev pointer + + // exit crit. section to reduce latency + portCLEAR_INTERRUPT_MASK_FROM_ISR(uxSavedInterruptStatus); + } + else if (ctr == 1U) { // is time event about to expire? + QActive * const act = te->toActive(); + prev = te->expire_(prev, act, tickRate); + + portCLEAR_INTERRUPT_MASK_FROM_ISR(uxSavedInterruptStatus); + + // POST_FROM_ISR() asserts if the queue overflows + act->POST_FROM_ISR(te, + pxHigherPriorityTaskWoken, + sender); + } + else { // time event keeps timing out + --ctr; // decrement the tick counter + te->m_ctr = ctr; // update the original + + prev = te; // advance to this time event + + portCLEAR_INTERRUPT_MASK_FROM_ISR(uxSavedInterruptStatus); + } + // re-enter crit. section to continue the loop + uxSavedInterruptStatus = portSET_INTERRUPT_MASK_FROM_ISR(); + } + + portCLEAR_INTERRUPT_MASK_FROM_ISR(uxSavedInterruptStatus); +} + +//............................................................................ +QEvt *QF::newXfromISR_(std::uint_fast16_t const evtSize, + std::uint_fast16_t const margin, + enum_t const sig) noexcept +{ + // find the pool number 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; + } + } + + UBaseType_t uxSavedInterruptStatus = portSET_INTERRUPT_MASK_FROM_ISR(); + + // precondition: + // - cannot run out of registered pools + Q_REQUIRE_INCRIT(700, poolNum < priv_.maxPool_); + + ++poolNum; // convert to 1-based poolNum + + portCLEAR_INTERRUPT_MASK_FROM_ISR(uxSavedInterruptStatus); + + // get event e (port-dependent)... + QEvt *e; +#ifdef Q_SPY + e = static_cast( + priv_.ePool_[poolNum - 1U].getFromISR(((margin != QF::NO_MARGIN) + ? margin : 0U), + static_cast(QS_EP_ID) + poolNum)); +#else + e = static_cast( + priv_.ePool_[poolNum - 1U].getFromISR(((margin != QF::NO_MARGIN) + ? margin : 0U), 0U)); +#endif + + if (e != nullptr) { // was e allocated correctly? + e->sig = static_cast(sig); // set the signal + e->poolNum_ = poolNum; + e->refCtr_ = 0U; + +#ifdef Q_SPY + uxSavedInterruptStatus = portSET_INTERRUPT_MASK_FROM_ISR(); + QS_BEGIN_PRE(QS_QF_NEW, + 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() + portCLEAR_INTERRUPT_MASK_FROM_ISR(uxSavedInterruptStatus); +#endif // def Q_SPY + } + else { // event was not allocated + uxSavedInterruptStatus = portSET_INTERRUPT_MASK_FROM_ISR(); + + // This assertion means that the event allocation failed, + // and this failure cannot be tolerated. The most frequent + // reason is an event leak in the application. + Q_ASSERT_INCRIT(720, margin != QF::NO_MARGIN); + + QS_BEGIN_PRE(QS_QF_NEW_ATTEMPT, + 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() + + portCLEAR_INTERRUPT_MASK_FROM_ISR(uxSavedInterruptStatus); + } + + // the returned event e is guaranteed to be valid (not NULL) + // if we can't tolerate failed allocation + return e; +} +//............................................................................ +void QF::gcFromISR(QEvt const * const e) noexcept { + UBaseType_t uxSavedInterruptStatus = portSET_INTERRUPT_MASK_FROM_ISR(); + + Q_REQUIRE_INCRIT(800, e != nullptr); + + std::uint_fast8_t const poolNum = e->poolNum_; + + if (poolNum != 0U) { // is it a pool event (mutable)? + + if (e->refCtr_ > 1U) { // isn't this the last ref? + + 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_); + QS_END_PRE() + + Q_ASSERT_INCRIT(805, e->refCtr_ > 0U); + QEvt_refCtr_dec_(e); // decrement the ref counter + + portCLEAR_INTERRUPT_MASK_FROM_ISR(uxSavedInterruptStatus); + } + 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_); + QS_END_PRE() + + // pool number must be in range + Q_ASSERT_INCRIT(810, (poolNum <= priv_.maxPool_) + && (poolNum <= QF_MAX_EPOOL)); + portCLEAR_INTERRUPT_MASK_FROM_ISR(uxSavedInterruptStatus); + + // NOTE: casting 'const' away is legit because 'e' is a pool event +#ifdef Q_SPY + // cast 'const' away in (QEvt *)e is OK because it's a pool event + priv_.ePool_[poolNum - 1U].putFromISR(QF_CONST_CAST_(QEvt*, e), + static_cast(QS_EP_ID) + e->poolNum_); +#else + priv_.ePool_[poolNum - 1U].putFromISR(QF_CONST_CAST_(QEvt*, e), 0U); +#endif + } + } + else { + portCLEAR_INTERRUPT_MASK_FROM_ISR(uxSavedInterruptStatus); + } +} +//............................................................................ +void *QMPool::getFromISR(std::uint_fast16_t const margin, + std::uint_fast8_t const qsId) noexcept +{ +#ifndef Q_SPY + Q_UNUSED_PAR(qsId); +#endif + + UBaseType_t uxSavedInterruptStatus = portSET_INTERRUPT_MASK_FROM_ISR(); + + // get volatile into temporaries + void * *pfb = static_cast(m_freeHead); // pointer to free block + QMPoolCtr nFree = m_nFree; + + // have more free blocks than the requested margin? + if (nFree > static_cast(margin)) { + Q_ASSERT_INCRIT(910, pfb != nullptr); + + void ** const pfb_next = static_cast(pfb[0]); // fast temporary + + --nFree; // one less free block + if (nFree == 0U) { // is the pool becoming empty? + // pool is becoming empty, so the next free block must be NULL + Q_ASSERT_INCRIT(920, pfb_next == nullptr); + + m_nFree = 0U; // no more free blocks + m_nMin = 0U; // remember that the pool got empty + } + else { // the pool is NOT empty + + // the next free-block pointer must be in range + Q_INVARIANT_INCRIT(930, + QF_PTR_RANGE_(pfb_next, m_start, m_end)); + + m_nFree = nFree; // update the original + if (m_nMin > nFree) { // is this the new minimum? + m_nMin = nFree; // remember the minimum so far + } + } + + m_freeHead = pfb_next; // set the head to the next free block + + // change the allocated block contents so that it is different + // than a free block inside the pool. + pfb[0] = &m_end[1]; // invalid location beyond the end + + QS_BEGIN_PRE(QS_QF_MPOOL_GET, qsId) + QS_TIME_PRE(); // timestamp + QS_OBJ_PRE(this); // this memory pool + QS_MPC_PRE(nFree); // # free blocks in the pool + QS_MPC_PRE(m_nMin); // min # free blocks ever in the pool + QS_END_PRE() + } + else { // don't have enough free blocks at this point + pfb = nullptr; + + QS_BEGIN_PRE(QS_QF_MPOOL_GET_ATTEMPT, qsId) + QS_TIME_PRE(); // timestamp + QS_OBJ_PRE(this); // this memory pool + QS_MPC_PRE(nFree); // # free blocks in the pool + QS_MPC_PRE(margin); // the requested margin + QS_END_PRE() + } + + portCLEAR_INTERRUPT_MASK_FROM_ISR(uxSavedInterruptStatus); + + return pfb; // return the block or nullptr to the caller +} +//............................................................................ +void QMPool::putFromISR(void *block, + std::uint_fast8_t const qsId) noexcept +{ +#ifndef Q_SPY + Q_UNUSED_PAR(qsId); +#endif + + void * * const pfb = static_cast(block); + + UBaseType_t uxSavedInterruptStatus = portSET_INTERRUPT_MASK_FROM_ISR(); + + // get volatile into temporaries + void ** const freeHead = static_cast(m_freeHead); + QMPoolCtr nFree = m_nFree; + + Q_REQUIRE_INCRIT(1000, nFree < m_nTot); + Q_REQUIRE_INCRIT(1010, QF_PTR_RANGE_(pfb, m_start, m_end)); + + ++nFree; // one more free block in this pool + + m_freeHead = pfb; // set as new head of the free list + m_nFree = nFree; + pfb[0] = freeHead; // link into the list + + QS_BEGIN_PRE(QS_QF_MPOOL_PUT, qsId) + QS_TIME_PRE(); // timestamp + QS_OBJ_PRE(this); // this memory pool + QS_MPC_PRE(nFree); // the # free blocks in the pool + QS_END_PRE() + + portCLEAR_INTERRUPT_MASK_FROM_ISR(uxSavedInterruptStatus); +} + +} // namespace QP + +//============================================================================ +// NOTE3: +// The event posting to FreeRTOS message queue occurs OUTSIDE critical section, +// which means that the remaining margin of available slots in the queue +// cannot be guaranteed. The problem is that interrupts and other tasks can +// preempt the event posting after checking the margin, but before actually +// posting the event to the queue. +// + diff --git a/src/qf_port.hpp b/src/qf_port.hpp deleted file mode 100644 index ff1de65..0000000 --- a/src/qf_port.hpp +++ /dev/null @@ -1,240 +0,0 @@ -/** -* @file -* @brief "Experimental" QF/CPP port to Espressif ESP-IDF (version 4.x) -* @ingroup ports -* @cond -****************************************************************************** -* Last updated for version 6.9.4 -* Last updated on 2022-03-20 -* Copyright (C) 2022 Victor Chavez -* -* Q u a n t u m L e a P s -* ------------------------ -* Modern Embedded Software -* -* Copyright (C) 2005-2021 Quantum Leaps, LLC. All rights reserved. -* This program is open source software: you can redistribute it and/or -* modify it under the terms of the GNU General Public License as published -* by the Free Software Foundation, either version 3 of the License, or -* (at your option) any later version. -* -* Alternatively, this program may be distributed and modified under the -* terms of Quantum Leaps commercial licenses, which expressly supersede -* the GNU General Public License and are specifically designed for -* licensees interested in retaining the proprietary status of their code. -* -* This program is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU General Public License for more details. -* -* You should have received a copy of the GNU General Public License -* along with this program. If not, see . -* -* Contact information: -* -* -****************************************************************************** -* @endcond -*/ -#ifndef QF_PORT_HPP -#define QF_PORT_HPP - -// Select the CPU at which the QP Framework will be attached -#define CONFIG_QP_PINNED_TO_CORE_0 -//#define CONFIG_QP_PINNED_TO_CORE_1 - -// Activate the QF ISR API required for FreeRTOS -#define QF_ISR_API 1 - -/* FreeRTOS-ESP32 event queue and thread types, see NOTE0 */ -#define QF_EQUEUE_TYPE QEQueue -#define QF_THREAD_TYPE TaskHandle_t - -/* The maximum number of active objects in the application, see NOTE1 */ -#define QF_MAX_ACTIVE 8U - -// QF interrupt disabling/enabling (task level) -#define QF_INT_DISABLE() taskDISABLE_INTERRUPTS() -#define QF_INT_ENABLE() taskENABLE_INTERRUPTS() - -/* QF critical section for FreeRTOS-ESP32 (task level), see NOTE2 */ -/* #define QF_CRIT_STAT_TYPE not defined */ -#define QF_CRIT_ENTRY(dummy) portENTER_CRITICAL(&QF_esp32mux) -#define QF_CRIT_EXIT(dummy) portEXIT_CRITICAL(&QF_esp32mux) - -#include "freertos/FreeRTOS.h" /* FreeRTOS master include file, see NOTE4 */ -#include "freertos/task.h" /* FreeRTOS task management */ - -#include "qep_port.hpp" /* QEP port */ -#include "qequeue.hpp" /* this QP port uses the native QF event queue */ -#include "qmpool.hpp" /* this QP port uses the native QF memory pool */ -#include "qf.hpp" /* QF platform-independent public interface */ - -/* global spinlock "mutex" for all critical sections in QF (see NOTE3) */ -extern PRIVILEGED_DATA portMUX_TYPE QF_esp32mux; - -#if defined( CONFIG_QP_PINNED_TO_CORE_0 ) - #define QP_CPU_NUM PRO_CPU_NUM -#elif defined( CONFIG_QP_PINNED_TO_CORE_1 ) - #define QP_CPU_NUM APP_CPU_NUM -#else - /* Defaults to APP_CPU */ - #define QP_CPU_NUM APP_CPU_NUM -#endif - -/* the "FromISR" versions of the QF APIs, see NOTE4 */ -#ifdef Q_SPY - #define PUBLISH_FROM_ISR(e_, pxHigherPrioTaskWoken_, sender_) \ - publishFromISR_((e_), (pxHigherPrioTaskWoken_),(sender_)) - - #define POST_FROM_ISR(e_, pxHigherPrioTaskWoken_, sender_) \ - postFromISR_((e_), QP::QF::NO_MARGIN, \ - (pxHigherPrioTaskWoken_), (sender_)) - - #define POST_X_FROM_ISR(e_, margin_, pxHigherPrioTaskWoken_, sender_) \ - postFromISR_((e_), (margin_), (pxHigherPrioTaskWoken_), (sender_)) - - #define TICK_X_FROM_ISR(tickRate_, pxHigherPrioTaskWoken_, sender_) \ - tickFromISR_((tickRate_), (pxHigherPrioTaskWoken_), (sender_)) -#else - #define PUBLISH_FROM_ISR(e_, pxHigherPrioTaskWoken_, dummy) \ - publishFromISR_((e_), (pxHigherPrioTaskWoken_)) - - #define POST_FROM_ISR(e_, pxHigherPrioTaskWoken_, dummy) \ - postFromISR_((e_), QP::QF::NO_MARGIN, (pxHigherPrioTaskWoken_)) - - #define POST_X_FROM_ISR(me_, e_, margin_, \ - pxHigherPrioTaskWoken_, dummy) \ - postFromISR_((e_), (margin_), (pxHigherPrioTaskWoken_)) - - #define TICK_X_FROM_ISR(tickRate_, pxHigherPrioTaskWoken_, dummy) \ - tickFromISR_((tickRate_), (pxHigherPrioTaskWoken_)) -#endif - -#define TICK_FROM_ISR(pxHigherPrioTaskWoken_, sender_) \ - TICK_X_FROM_ISR(0U, pxHigherPrioTaskWoken_, sender_) - -#ifdef Q_EVT_CTOR /* Shall the ctor for the ::QEvt class be provided? */ - - #define Q_NEW_FROM_ISR(evtT_, sig_, ...) \ - (new(QP::QF::newXfromISR_(sizeof(evtT_), QP::QF::NO_MARGIN, 0)) \ - evtT_((sig_), ##__VA_ARGS__)) - - #define Q_NEW_X_FROM_ISR(e_, evtT_, margin_, sig_, ...) do { \ - (e_) = (evtT_ *)QP::QF::newXfromISR_(sizeof(evtT_), \ - (margin_), 0); \ - if ((e_) != (evtT_ *)0) { \ - evtT_##_ctor((e_), (sig_), ##__VA_ARGS__); \ - } \ - } while (false) - -#else - - #define Q_NEW_FROM_ISR(evtT_, sig_) \ - (static_cast(QP::QF::newXfromISR_( \ - static_cast(sizeof(evtT_)), \ - QP::QF::NO_MARGIN, (sig_)))) - - #define Q_NEW_X_FROM_ISR(e_, evtT_, margin_, sig_) ((e_) = \ - (evtT_ *)QP::QF::newXfromISR_((uint_fast16_t)sizeof(evtT_), \ - (margin_), (sig_))) - -#endif /* Q_EVT_CTOR */ - - -namespace QP { - -enum FreeRTOS_TaskAttrs { - TASK_NAME_ATTR -}; -} // namespace QP - -/***************************************************************************** -* interface used only inside QF, but not in applications -*/ -#ifdef QP_IMPL - /* FreeRTOS blocking for event queue implementation (task level) */ - #define QACTIVE_EQUEUE_WAIT_(me_) \ - while ((me_)->m_eQueue.m_frontEvt == nullptr) { \ - QF_CRIT_X_(); \ - ulTaskNotifyTake(pdTRUE, portMAX_DELAY); \ - QF_CRIT_E_(); \ - } - - /* FreeRTOS signaling (unblocking) for event queue (task level) */ - #define QACTIVE_EQUEUE_SIGNAL_(me_) do { \ - QF_CRIT_X_(); \ - xTaskNotifyGive((TaskHandle_t)(me_)->m_thread); \ - QF_CRIT_E_(); \ - } while (false) - - #define QF_SCHED_STAT_ - #define QF_SCHED_LOCK_(dummy) vTaskSuspendAll() - #define QF_SCHED_UNLOCK_() xTaskResumeAll() - - /* native QF event pool operations */ - #define QF_EPOOL_TYPE_ QMPool - #define QF_EPOOL_INIT_(p_, poolSto_, poolSize_, evtSize_) \ - (p_).init((poolSto_), (poolSize_), (evtSize_)) - #define QF_EPOOL_EVENT_SIZE_(p_) ((p_).getBlockSize()) - #define QF_EPOOL_GET_(p_, e_, m_, qs_id_) \ - ((e_) = static_cast((p_).get((m_), (qs_id_)))) - #define QF_EPOOL_PUT_(p_, e_, qs_id_) \ - ((p_).put((e_), (qs_id_))) - -#endif /* ifdef QP_IMPL */ - -/***************************************************************************** -* NOTE0: -* This is the "experimental" port to the [Espressif ESP-IDF][1] -* IoT Framework, which is loosely based on the [FreeRTOS kernel][2]. -* -* "Experimental" means that the port has not been thouroughly tested at -* Quantum Leaps and no working examples are provided. -* -* The [Espressif ESP-IDF][1] is based on a significantly changed version -* of the FreeRTOS kernel developed by Espressif to support the ESP32 multi-core -* CPUs (see [ESP-IDF][1]). -* -* The Espressif version of FreeRTOS is __NOT__ compatible with the baseline -* FreeRTOS and it needs to be treated as a separate RTOS kernel. -* According to the comments in the Espressif source code, FreeRTOS-ESP-IDF -* is based on FreeRTOS V8.2.0, but apparently FreeRTOS-ESP-IDF has been -* updated with the newer features introduced to the original FreeRTOS in the -* later versions. For example, FreeRTOS-ESP32 supports the "static allocation", -* first introduced in baseline FreeRTOS V9.x. -* -* [1]: https://www.espressif.com/en/products/sdks/esp-idf -* [2]: https://freertos.org -* -* NOTE1: -* The maximum number of active objects QF_MAX_ACTIVE can be increased to 64, -* inclusive, but it can be reduced to save some memory. Also, the number of -* active objects cannot exceed the number of FreeRTOS task priorities, -* because each QP active object requires a unique priority level. -* -* NOTE2: -* The critical section definition applies only to the FreeRTOS "task level" -* APIs. The "FromISR" APIs are defined separately. -* -* NOTE3: -* This QF port to FreeRTOS-ESP32 uses the FreeRTOS-ESP32 spin lock "mutex", -* similar to the internal implementation of FreeRTOS-ESP32 (see tasks.c). -* However, the QF port uses its own "mutex" object QF_esp32mux. -* -* NOTE4: -* This QP/C++ port used as reference the esp-idf QP/C port counterpart. The main -* difference implementation-wise is that instead of using xTaskCreateStaticPinnedToCore, -* it uses xTaskCreatePinnedToCore. The reason is that this port was designed to work -* with the Arduino SDK, which does not include xTaskCreateStaticPinnedToCore. -* -* NOTE5: -* The design of FreeRTOS requires using different APIs inside the ISRs -* (the "FromISR" variant) than at the task level. Accordingly, this port -* provides the "FromISR" variants for QP functions and "FROM_ISR" variants -* for QP macros to be used inside ISRs. ONLY THESE "FROM_ISR" VARIANTS -* ARE ALLOWED INSIDE ISRs AND CALLING THE TASK-LEVEL APIs IS AN ERROR. -*/ - -#endif /* QF_PORT_HPP */ diff --git a/src/qf_ps.cpp b/src/qf_ps.cpp index 79caa83..d55d83f 100644 --- a/src/qf_ps.cpp +++ b/src/qf_ps.cpp @@ -1,49 +1,36 @@ -//$file${src::qf::qf_ps.cpp} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv +//============================================================================ +// QP/C++ Real-Time Embedded Framework (RTEF) +// Version 8.0.2 // -// Model: qpcpp.qm -// File: ${src::qf::qf_ps.cpp} +// Copyright (C) 2005 Quantum Leaps, LLC. All rights reserved. // -// This code has been generated by QM 5.2.5 . -// DO NOT EDIT THIS FILE MANUALLY. All your changes will be lost. -// -// This code is covered by the following QP license: -// License # : LicenseRef-QL-dual -// Issued to : Any user of the QP/C++ real-time embedded framework -// Framework(s) : qpcpp -// Support ends : 2023-12-31 -// License scope: -// -// Copyright (C) 2005 Quantum Leaps, LLC . +// 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: +// This software is dual-licensed under the terms of the open-source GNU +// General Public License (GPL) or under the terms of one of the closed- +// source Quantum Leaps commercial licenses. // // Redistributions in source code must retain this top-level comment block. // Plagiarizing this software to sidestep the license obligations is illegal. // -// Contact information: +// NOTE: +// The GPL does NOT permit the incorporation of this code into proprietary +// programs. Please contact Quantum Leaps for commercial licensing options, +// which expressly supersede the GPL and are designed explicitly for +// closed-source distribution. +// +// Quantum Leaps contact information: // // -// -//$endhead${src::qf::qf_ps.cpp} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -//! @file -//! @brief QF/C++ Publish-Subscribe services -//! definitions. - +//============================================================================ #define QP_IMPL // this is QP implementation -#include "qf_port.hpp" // QF port -#include "qf_pkg.hpp" // QF package-scope interface -#include "qassert.h" // QP embedded systems-friendly assertions +#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 @@ -56,206 +43,193 @@ namespace { Q_DEFINE_THIS_MODULE("qf_ps") } // unnamed namespace -//$skip${QP_VERSION} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv -// Check for the minimum required QP version -#if (QP_VERSION < 700U) || (QP_VERSION != ((QP_RELEASE^4294967295U) % 0x3E8U)) -#error qpcpp version 7.0.0 or higher required -#endif -//$endskip${QP_VERSION} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -//$define${QF::QActive::subscrList_} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv namespace QP { QSubscrList * QActive::subscrList_; -} // namespace QP -//$enddef${QF::QActive::subscrList_} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -//$define${QF::QActive::maxPubSignal_} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv -namespace QP { enum_t QActive::maxPubSignal_; -} // namespace QP -//$enddef${QF::QActive::maxPubSignal_} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -//$define${QF::QActive::psInit} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv -namespace QP { - -//${QF::QActive::psInit} ..................................................... +//............................................................................ void QActive::psInit( QSubscrList * const subscrSto, enum_t const maxSignal) noexcept { subscrList_ = subscrSto; maxPubSignal_ = maxSignal; - QF::bzero(subscrSto, static_cast(maxSignal) - * sizeof(QSubscrList)); -} -} // namespace QP -//$enddef${QF::QActive::psInit} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -//$define${QF::QActive::publish_} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv -namespace QP { + // initialize the subscriber list + for (enum_t sig = 0; sig < maxSignal; ++sig) { + subscrSto[sig].m_set.setEmpty(); + } +} -//${QF::QActive::publish_} ................................................... +//............................................................................ void QActive::publish_( QEvt const * const e, void const * const sender, - std::uint_fast8_t const qs_id) noexcept + std::uint_fast8_t const qsId) noexcept { - Q_UNUSED_PAR(sender); // when Q_SPY not defined - Q_UNUSED_PAR(qs_id); // when Q_SPY not defined +#ifndef Q_SPY + Q_UNUSED_PAR(sender); + Q_UNUSED_PAR(qsId); +#endif - //! @pre the published signal must be within the configured range - Q_REQUIRE_ID(100, static_cast(e->sig) < maxPubSignal_); + QSignal const sig = e->sig; - QF_CRIT_STAT_ - QF_CRIT_E_(); + QF_CRIT_STAT + QF_CRIT_ENTRY(); - 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_() + Q_REQUIRE_INCRIT(200, sig < static_cast(maxPubSignal_)); - // is it a dynamic event? - if (e->poolId_ != 0U) { - // NOTE: The reference counter of a dynamic event is incremented to + 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->poolNum_, e->refCtr_); + QS_END_PRE() + + // is it a mutable event? + if (e->poolNum_ != 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. - // + Q_ASSERT_INCRIT(205, e->refCtr_ < (2U * QF_MAX_ACTIVE)); QEvt_refCtr_inc_(e); } - // make a local, modifiable copy of the subscriber list - QPSet subscrList = subscrList_[e->sig]; - QF_CRIT_X_(); + // make a local, modifiable copy of the subscriber set + QPSet subscrSet = subscrList_[sig].m_set; + + QF_CRIT_EXIT(); + + if (subscrSet.notEmpty()) { // any subscribers? + // highest-prio subscriber + std::uint_fast8_t p = subscrSet.findMax(); + + QF_CRIT_ENTRY(); - if (subscrList.notEmpty()) { // any subscribers? - // the highest-prio subscriber - std::uint_fast8_t p = subscrList.findMax(); QActive *a = registry_[p]; - QF_SCHED_STAT_ + // the AO must be registered with the framework + Q_ASSERT_INCRIT(210, a != nullptr); - QF_SCHED_LOCK_(a->m_prio); // lock the scheduler up to AO's prio - do { // loop over all subscribers - // the prio of the AO must be registered with the framework - Q_ASSERT_ID(210, a != nullptr); + QF_CRIT_EXIT(); + QF_SCHED_STAT_ + QF_SCHED_LOCK_(p); // lock the scheduler up to AO's prio + do { // loop over all subscribers // POST() asserts internally if the queue overflows a->POST(e, sender); - subscrList.remove(p); // remove the handled subscriber - if (subscrList.notEmpty()) { // still more subscribers? - p = subscrList.findMax(); // the highest-prio subscriber + subscrSet.remove(p); // remove the handled subscriber + if (subscrSet.notEmpty()) { // still more subscribers? + p = subscrSet.findMax(); // highest-prio subscriber + + QF_CRIT_ENTRY(); + a = registry_[p]; + // the AO must be registered with the framework + Q_ASSERT_INCRIT(220, a != nullptr); + + QF_CRIT_EXIT(); } else { p = 0U; // no more subscribers } } while (p != 0U); + + QF_CRIT_ENTRY(); + Q_ASSERT_INCRIT(290, p == 0U); // all subscribers processed + 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. - // - #if (QF_MAX_EPOOL > 0U) - QF::gc(e); - #endif +#if (QF_MAX_EPOOL > 0U) + QF::gc(e); // recycle the event to avoid a leak +#endif } -} // namespace QP -//$enddef${QF::QActive::publish_} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -//$define${QF::QActive::subscribe} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv -namespace QP { - -//${QF::QActive::subscribe} .................................................. +//............................................................................ void QActive::subscribe(enum_t const sig) const noexcept { std::uint_fast8_t const p = static_cast(m_prio); - Q_REQUIRE_ID(300, (Q_USER_SIG <= sig) - && (sig < maxPubSignal_) - && (0U < p) && (p <= QF_MAX_ACTIVE) - && (registry_[p] == this)); + QF_CRIT_STAT + QF_CRIT_ENTRY(); - QF_CRIT_STAT_ - QF_CRIT_E_(); + Q_REQUIRE_INCRIT(300, (Q_USER_SIG <= sig) + && (sig < maxPubSignal_) + && (0U < p) && (p <= QF_MAX_ACTIVE) + && (registry_[p] == this)); - QS_BEGIN_NOCRIT_PRE_(QS_QF_ACTIVE_SUBSCRIBE, m_prio) - QS_TIME_PRE_(); // timestamp - QS_SIG_PRE_(sig); // the signal of this event - QS_OBJ_PRE_(this); // this active object - QS_END_NOCRIT_PRE_() + QS_BEGIN_PRE(QS_QF_ACTIVE_SUBSCRIBE, m_prio) + QS_TIME_PRE(); // timestamp + QS_SIG_PRE(sig); // the signal of this event + QS_OBJ_PRE(this); // this active object + QS_END_PRE() - subscrList_[sig].insert(p); // insert into subscriber-list - QF_CRIT_X_(); -} + // insert the prio. into the subscriber set + subscrList_[sig].m_set.insert(p); -} // namespace QP -//$enddef${QF::QActive::subscribe} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -//$define${QF::QActive::unsubscribe} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv -namespace QP { + QF_CRIT_EXIT(); +} -//${QF::QActive::unsubscribe} ................................................ +//............................................................................ void QActive::unsubscribe(enum_t const sig) const noexcept { std::uint_fast8_t const p = static_cast(m_prio); - //! @pre the signal and the priority must be in range, the AO must also - // be registered with the framework - Q_REQUIRE_ID(400, (Q_USER_SIG <= sig) - && (sig < maxPubSignal_) - && (0U < p) && (p <= QF_MAX_ACTIVE) - && (registry_[p] == this)); + QF_CRIT_STAT + QF_CRIT_ENTRY(); - 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)); - 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, p) + QS_TIME_PRE(); // timestamp + QS_SIG_PRE(sig); // the signal of this event + QS_OBJ_PRE(this); // this active object + QS_END_PRE() - subscrList_[sig].remove(p); // remove from subscriber-list + // remove the prio. from the subscriber set + subscrList_[sig].m_set.remove(p); - QF_CRIT_X_(); + QF_CRIT_EXIT(); } -} // namespace QP -//$enddef${QF::QActive::unsubscribe} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -//$define${QF::QActive::unsubscribeAll} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv -namespace QP { - -//${QF::QActive::unsubscribeAll} ............................................. +//............................................................................ void QActive::unsubscribeAll() const noexcept { + QF_CRIT_STAT + QF_CRIT_ENTRY(); + std::uint_fast8_t const p = static_cast(m_prio); - Q_REQUIRE_ID(500, (0U < p) && (p <= QF_MAX_ACTIVE) - && (registry_[p] == this)); + Q_REQUIRE_INCRIT(500, (0U < p) && (p <= QF_MAX_ACTIVE) + && (registry_[p] == this)); + enum_t const maxPubSig = maxPubSignal_; - for (enum_t sig = Q_USER_SIG; sig < maxPubSignal_; ++sig) { - QF_CRIT_STAT_ - QF_CRIT_E_(); - if (subscrList_[sig].hasElement(p)) { - subscrList_[sig].remove(p); + QF_CRIT_EXIT(); - 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_() + for (enum_t sig = Q_USER_SIG; sig < maxPubSig; ++sig) { + QF_CRIT_ENTRY(); + if (subscrList_[sig].m_set.hasElement(p)) { + subscrList_[sig].m_set.remove(p); + 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_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/src/qf_qact.cpp b/src/qf_qact.cpp index 47c7d40..0f99e08 100644 --- a/src/qf_qact.cpp +++ b/src/qf_qact.cpp @@ -1,48 +1,36 @@ -//$file${src::qf::qf_qact.cpp} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv -// -// Model: qpcpp.qm -// File: ${src::qf::qf_qact.cpp} -// -// This code has been generated by QM 5.2.5 . -// DO NOT EDIT THIS FILE MANUALLY. All your changes will be lost. +//============================================================================ +// QP/C++ Real-Time Embedded Framework (RTEF) +// Version 8.0.2 // -// This code is covered by the following QP license: -// License # : LicenseRef-QL-dual -// Issued to : Any user of the QP/C++ real-time embedded framework -// Framework(s) : qpcpp -// Support ends : 2023-12-31 -// License scope: +// Copyright (C) 2005 Quantum Leaps, LLC. All rights reserved. // -// 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: +// This software is dual-licensed under the terms of the open-source GNU +// General Public License (GPL) or under the terms of one of the closed- +// source Quantum Leaps commercial licenses. // // Redistributions in source code must retain this top-level comment block. // Plagiarizing this software to sidestep the license obligations is illegal. // -// Contact information: +// NOTE: +// The GPL does NOT permit the incorporation of this code into proprietary +// programs. Please contact Quantum Leaps for commercial licensing options, +// which expressly supersede the GPL and are designed explicitly for +// closed-source distribution. +// +// Quantum Leaps contact information: // // -// -//$endhead${src::qf::qf_qact.cpp} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -//! @file -//! @brief QP::QActive services and QF support code - +//============================================================================ #define QP_IMPL // this is QP implementation -#include "qf_port.hpp" // QF port -#include "qf_pkg.hpp" // QF package-scope interface -#include "qassert.h" // QP embedded systems-friendly assertions +#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 @@ -50,123 +38,32 @@ #include "qs_dummy.hpp" // disable the QS software tracing #endif // Q_SPY -//============================================================================ -namespace { // unnamed local namespace +// 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 < 700U) || (QP_VERSION != ((QP_RELEASE^4294967295U) % 0x3E8U)) -#error qpcpp version 7.0.0 or higher required -#endif -//$endskip${QP_VERSION} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -//$define${QF::QActive::registry_[QF_MAX_ACTIVE + 1U]} vvvvvvvvvvvvvvvvvvvvvvv -namespace QP { -QActive * QActive::registry_[QF_MAX_ACTIVE + 1U]; - -} // namespace QP -//$enddef${QF::QActive::registry_[QF_MAX_ACTIVE + 1U]} ^^^^^^^^^^^^^^^^^^^^^^^ -//$define${QF::QF-base::intLock_} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv -namespace QP { -namespace QF { - -//${QF::QF-base::intLock_} ................................................... -std::uint_fast8_t volatile intLock_; - -} // namespace QF -} // namespace QP -//$enddef${QF::QF-base::intLock_} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -//$define${QF::QF-base::intNest_} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv -namespace QP { -namespace QF { - -//${QF::QF-base::intNest_} ................................................... -std::uint_fast8_t volatile intNest_; - -} // namespace QF -} // namespace QP -//$enddef${QF::QF-base::intNest_} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -//$define${QF::QF-pkg::readySet_} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv -namespace QP { -namespace QF { - -//${QF::QF-pkg::readySet_} ................................................... -QPSet readySet_; - -} // namespace QF -} // namespace QP -//$enddef${QF::QF-pkg::readySet_} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -//$define${QF::QF-pkg::bzero} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv -namespace QP { -namespace QF { - -//${QF::QF-pkg::bzero} ....................................................... -void bzero( - void * const start, - std::uint_fast16_t const len) noexcept -{ - std::uint8_t *ptr = static_cast(start); - for (std::uint_fast16_t n = len; n > 0U; --n) { - *ptr = 0U; - ++ptr; - } -} - -} // namespace QF -} // namespace QP -//$enddef${QF::QF-pkg::bzero} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -//============================================================================ -//$define${QF::QActive::QActive} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv -namespace QP { - -//${QF::QActive::QActive} .................................................... -QActive::QActive(QStateHandler const initial) noexcept - : QHsm(initial), - m_prio(0U), - m_pthre(0U) -{ - #ifdef QF_EQUEUE_TYPE - QF::bzero(&m_eQueue, sizeof(m_eQueue)); - #endif - - #ifdef QF_OS_OBJECT_TYPE - QF::bzero(&m_osObject, sizeof(m_osObject)); - #endif - - #ifdef QF_THREAD_TYPE - QF::bzero(&m_thread, sizeof(m_thread)); - #endif -} - -} // namespace QP -//$enddef${QF::QActive::QActive} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -//$define${QF::QActive::register_} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv namespace QP { -//${QF::QActive::register_} .................................................. +//............................................................................ void QActive::register_() noexcept { + QF_CRIT_STAT + QF_CRIT_ENTRY(); + if (m_pthre == 0U) { // preemption-threshold not defined? m_pthre = m_prio; // apply the default } - #ifndef Q_NASSERT - - //! @pre - //! 1. the "QF-priority" of the AO must be in range - //! 2. the "QF-priority" must not be already in use (unique priority) - //! 3. the "QF-priority" must not exceed the "preemption-threshold" - Q_REQUIRE_ID(100, (0U < m_prio) && (m_prio <= QF_MAX_ACTIVE) - && (registry_[m_prio] == nullptr) - && (m_prio <= m_pthre)); +#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; + 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; @@ -182,77 +79,30 @@ void QActive::register_() noexcept { } } - //! @post - //! 1. the preceding pre-thre must not exceed the preemption-threshold - //! 2. the preemption-threshold must not exceed the next pre-thre - Q_ENSURE_ID(110, (prev_thre <= m_pthre) && (m_pthre <= next_thre)); - #endif // Q_NASSERT + Q_ASSERT_INCRIT(190, + (prev_thre <= m_pthre) + && (m_pthre <= next_thre)); +#endif // Q_UNSAFE - QF_CRIT_STAT_ - QF_CRIT_E_(); - // register the AO at the "QF-priority" + // register the AO at the QF-prio. registry_[m_prio] = this; - QF_CRIT_X_(); -} -} // namespace QP -//$enddef${QF::QActive::register_} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -//$define${QF::QActive::unregister_} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv -namespace QP { + QF_CRIT_EXIT(); +} -//${QF::QActive::unregister_} ................................................ +//............................................................................ void QActive::unregister_() noexcept { std::uint_fast8_t const p = static_cast(m_prio); - Q_REQUIRE_ID(200, (0U < p) && (p <= QF_MAX_ACTIVE) + QF_CRIT_STAT + QF_CRIT_ENTRY(); + + Q_REQUIRE_INCRIT(200, (0U < p) && (p <= QF_MAX_ACTIVE) && (registry_[p] == this)); - QF_CRIT_STAT_ - QF_CRIT_E_(); registry_[p] = nullptr; // free-up the priority level m_state.fun = nullptr; // invalidate the state - QF_CRIT_X_(); -} - -} // namespace QP -//$enddef${QF::QActive::unregister_} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -//============================================================================ -//$define${QF-types::QPSet::QF_LOG2} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv -namespace QP { - -//${QF-types::QPSet::QF_LOG2} ................................................ -#ifndef QF_LOG2 -std::uint_fast8_t QPSet::QF_LOG2(QP::QPSetBits x) const noexcept { - static std::uint8_t const log2LUT[16] = { - 0U, 1U, 2U, 2U, 3U, 3U, 3U, 3U, - 4U, 4U, 4U, 4U, 4U, 4U, 4U, 4U - }; - std::uint_fast8_t n = 0U; - QP::QPSetBits t; - - #if (QF_MAX_ACTIVE > 16U) - t = static_cast(x >> 16U); - if (t != 0U) { - n += 16U; - x = t; - } - #endif - #if (QF_MAX_ACTIVE > 8U) - t = (x >> 8U); - if (t != 0U) { - n += 8U; - x = t; - } - #endif - t = (x >> 4U); - if (t != 0U) { - n += 4U; - x = t; - } - return n + log2LUT[x]; + QF_CRIT_EXIT(); } -#endif // ndef QF_LOG2 - } // namespace QP -//$enddef${QF-types::QPSet::QF_LOG2} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/src/qf_qeq.cpp b/src/qf_qeq.cpp index d9ccc99..8160adc 100644 --- a/src/qf_qeq.cpp +++ b/src/qf_qeq.cpp @@ -1,48 +1,36 @@ -//$file${src::qf::qf_qeq.cpp} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv +//============================================================================ +// QP/C++ Real-Time Embedded Framework (RTEF) +// Version 8.0.2 // -// Model: qpcpp.qm -// File: ${src::qf::qf_qeq.cpp} +// Copyright (C) 2005 Quantum Leaps, LLC. All rights reserved. // -// This code has been generated by QM 5.2.5 . -// DO NOT EDIT THIS FILE MANUALLY. All your changes will be lost. -// -// This code is covered by the following QP license: -// License # : LicenseRef-QL-dual -// Issued to : Any user of the QP/C++ real-time embedded framework -// Framework(s) : qpcpp -// Support ends : 2023-12-31 -// License scope: -// -// Copyright (C) 2005 Quantum Leaps, LLC . +// 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: +// This software is dual-licensed under the terms of the open-source GNU +// General Public License (GPL) or under the terms of one of the closed- +// source Quantum Leaps commercial licenses. // // Redistributions in source code must retain this top-level comment block. // Plagiarizing this software to sidestep the license obligations is illegal. // -// Contact information: +// NOTE: +// The GPL does NOT permit the incorporation of this code into proprietary +// programs. Please contact Quantum Leaps for commercial licensing options, +// which expressly supersede the GPL and are designed explicitly for +// closed-source distribution. +// +// Quantum Leaps contact information: // // -// -//$endhead${src::qf::qf_qeq.cpp} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -//! @file -//! @brief QP::QEQueue implementation - +//============================================================================ #define QP_IMPL // this is QP implementation -#include "qf_port.hpp" // QF port -#include "qf_pkg.hpp" // QF package-scope interface -#include "qassert.h" // QP embedded systems-friendly assertions +#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 @@ -55,218 +43,230 @@ namespace { Q_DEFINE_THIS_MODULE("qf_qeq") } // unnamed namespace -//$skip${QP_VERSION} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv -// Check for the minimum required QP version -#if (QP_VERSION < 700U) || (QP_VERSION != ((QP_RELEASE^4294967295U) % 0x3E8U)) -#error qpcpp version 7.0.0 or higher required -#endif -//$endskip${QP_VERSION} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -//$define${QF::QEQueue} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv namespace QP { -//${QF::QEQueue} ............................................................. - -//${QF::QEQueue::QEQueue} .................................................... -QEQueue::QEQueue() noexcept - : m_frontEvt(nullptr), - m_ring(nullptr), - m_end(0U), - m_head(0U), - m_tail(0U), - m_nFree(0U), - m_nMin(0U) -{} - -//${QF::QEQueue::init} ....................................................... +//............................................................................ void QEQueue::init( - QEvt const * qSto[], + QEvt const * * const qSto, std::uint_fast16_t const qLen) noexcept { + QF_CRIT_STAT + QF_CRIT_ENTRY(); + +#if (QF_EQUEUE_CTR_SIZE == 1U) + Q_REQUIRE_INCRIT(100, qLen < 0xFFU); +#endif + m_frontEvt = nullptr; // no events in the queue - m_ring = &qSto[0]; + m_ring = qSto; m_end = static_cast(qLen); if (qLen > 0U) { m_head = 0U; m_tail = 0U; } - m_nFree = static_cast(qLen + 1U); //+1 for frontEvt - m_nMin = m_nFree; + m_nFree = static_cast(qLen + 1U); //+1 for frontEvt + m_nMin = m_nFree; + + QF_CRIT_EXIT(); } -//${QF::QEQueue::post} ....................................................... +//............................................................................ bool QEQueue::post( QEvt const * const e, std::uint_fast16_t const margin, - std::uint_fast8_t const qs_id) noexcept + std::uint_fast8_t const qsId) noexcept { - //! @pre event must be valid - Q_REQUIRE_ID(200, e != nullptr); - - QF_CRIT_STAT_ - QF_CRIT_E_(); - QEQueueCtr nFree = m_nFree; - - // margin available? - bool status; - if (((margin == QF::NO_MARGIN) && (nFree > 0U)) - || (nFree > static_cast(margin))) - { - // is it a dynamic event? - if (e->poolId_ != 0U) { +#ifndef Q_SPY + Q_UNUSED_PAR(qsId); +#endif + + QF_CRIT_STAT + QF_CRIT_ENTRY(); + + Q_REQUIRE_INCRIT(200, e != nullptr); + + QEQueueCtr tmp = m_nFree; // get volatile into temporary + + // can the queue accept the event? + bool status = ((margin == QF::NO_MARGIN) && (tmp > 0U)) + || (tmp > static_cast(margin)); + if (status) { + // is it a mutable event? + if (e->poolNum_ != 0U) { + Q_ASSERT_INCRIT(205, e->refCtr_ < (2U * QF_MAX_ACTIVE)); QEvt_refCtr_inc_(e); // increment the reference counter } - --nFree; // one free entry just used up - m_nFree = nFree; // update the original - if (m_nMin > nFree) { - m_nMin = nFree; // update minimum so far + --tmp; // one free entry just used up + + m_nFree = tmp; // update the original + if (m_nMin > tmp) { // is this the new minimum? + m_nMin = tmp; // 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) { +#ifdef Q_SPY + QS_BEGIN_PRE(QS_QF_EQUEUE_POST, qsId) + QS_TIME_PRE(); // timestamp + QS_SIG_PRE(e->sig); // the signal of the event + QS_OBJ_PRE(this); // this queue object + QS_2U8_PRE(e->poolNum_, e->refCtr_); + QS_EQC_PRE(tmp); // # free entries + QS_EQC_PRE(m_nMin); // min # free entries + QS_END_PRE() +#endif // def Q_SPY + + if (m_frontEvt == nullptr) { // is the queue empty? m_frontEvt = e; // deliver event directly } - // queue is not empty, leave event in the ring-buffer - else { - // insert event into the ring buffer (FIFO) - m_ring[m_head] = e; // insert e into buffer + else { // queue was not empty, insert event into the ring-buffer + tmp = m_head; // get volatile into temporary + m_ring[tmp] = e; // insert e into buffer - // need to wrap? - if (m_head == 0U) { - m_head = m_end; // wrap around + if (tmp == 0U) { // need to wrap the head? + tmp = m_end; } - m_head = (m_head - 1U); + --tmp; // advance head (counter-clockwise) + + m_head = tmp; // update the original } - status = true; // event posted successfully } - else { - //! @note assert if event cannot be posted and dropping events is - //! not acceptable - Q_ASSERT_CRIT_(210, margin != QF::NO_MARGIN); - - QS_BEGIN_NOCRIT_PRE_(QS_QF_EQUEUE_POST_ATTEMPT, qs_id) - QS_TIME_PRE_(); // timestamp - QS_SIG_PRE_(e->sig); // the signal of this event - QS_OBJ_PRE_(this); // this queue object - QS_2U8_PRE_(e->poolId_, e->refCtr_);// pool Id & refCtr of the evt - QS_EQC_PRE_(nFree); // number of free entries - QS_EQC_PRE_(margin); // margin requested - QS_END_NOCRIT_PRE_() - - status = false; // event not posted + else { // event cannot be posted + // dropping events must be acceptable + Q_ASSERT_INCRIT(230, margin != QF::NO_MARGIN); + +#ifdef Q_SPY + 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->poolNum_, e->refCtr_); + QS_EQC_PRE(tmp); // # free entries + QS_EQC_PRE(margin); // margin requested + QS_END_PRE() +#endif // def Q_SPY } - QF_CRIT_X_(); - Q_UNUSED_PAR(qs_id); // when Q_SPY not defined + QF_CRIT_EXIT(); return status; } -//${QF::QEQueue::postLIFO} ................................................... +//............................................................................ void QEQueue::postLIFO( QEvt const * const e, - std::uint_fast8_t const qs_id) noexcept + std::uint_fast8_t const qsId) noexcept { - Q_UNUSED_PAR(qs_id); // when Q_SPY not defined +#ifndef Q_SPY + Q_UNUSED_PAR(qsId); +#endif - QF_CRIT_STAT_ - QF_CRIT_E_(); - QEQueueCtr nFree = m_nFree; + QF_CRIT_STAT + QF_CRIT_ENTRY(); - //! @pre the queue must be able to accept the event (cannot overflow) - Q_REQUIRE_CRIT_(300, nFree != 0U); + Q_REQUIRE_INCRIT(300, e != nullptr); - // is it a dynamic event? - if (e->poolId_ != 0U) { + QEQueueCtr tmp = m_nFree; // get volatile into temporary + + // must be able to LIFO-post the event + Q_REQUIRE_INCRIT(310, tmp != 0U); + + if (e->poolNum_ != 0U) { // is it a mutable event? + Q_ASSERT_INCRIT(305, e->refCtr_ < (2U * QF_MAX_ACTIVE)); QEvt_refCtr_inc_(e); // increment the reference counter } - --nFree; // one free entry just used up - m_nFree = nFree; // update the original - if (m_nMin > nFree) { - m_nMin = nFree; // update minimum so far + --tmp; // one free entry just used up + + m_nFree = tmp; // update the original + if (m_nMin > tmp) { // is this the new minimum? + m_nMin = tmp; // 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->poolNum_, e->refCtr_); + QS_EQC_PRE(tmp); // # free entries + QS_EQC_PRE(m_nMin); // min # free entries + QS_END_PRE() QEvt const * const frontEvt = m_frontEvt; // read into temporary - m_frontEvt = e; // deliver event directly to the front of the queue + m_frontEvt = e; // deliver the event directly to the front - // was the queue not empty? - if (frontEvt != nullptr) { - m_tail = (m_tail + 1U); - if (m_tail == m_end) { // need to wrap the tail? - m_tail = 0U; // wrap around + if (frontEvt != nullptr) { // was the queue NOT empty? + tmp = m_tail; // get volatile into temporary; + ++tmp; + if (tmp == m_end) { // need to wrap the tail? + tmp = 0U; // wrap around } - m_ring[m_tail] = frontEvt; // buffer the old front evt + m_tail = tmp; + m_ring[tmp] = frontEvt; } - QF_CRIT_X_(); + + QF_CRIT_EXIT(); } -//${QF::QEQueue::get} ........................................................ -QEvt const * QEQueue::get(std::uint_fast8_t const qs_id) noexcept { - Q_UNUSED_PAR(qs_id); // when Q_SPY not defined +//............................................................................ +QEvt const * QEQueue::get(std::uint_fast8_t const qsId) noexcept { +#ifndef Q_SPY + Q_UNUSED_PAR(qsId); +#endif - QF_CRIT_STAT_ - QF_CRIT_E_(); - QEvt const * const e = m_frontEvt; // always remove evt from the front + QF_CRIT_STAT + QF_CRIT_ENTRY(); - // is the queue not empty? - if (e != nullptr) { - QEQueueCtr const nFree = m_nFree + 1U; - m_nFree = nFree; // upate the number of free + QEvt const * const e = m_frontEvt; // always remove evt from the front - // any events in the the ring buffer? - if (nFree <= m_end) { - m_frontEvt = m_ring[m_tail]; // remove from the tail - if (m_tail == 0U) { // need to wrap? - m_tail = m_end; // wrap around + if (e != nullptr) { // was the queue not empty? + QEQueueCtr tmp = m_nFree; // get volatile into temporary + + ++tmp; // one more free event in the queue + + m_nFree = tmp; // update the # free + + // any events in the ring buffer? + if (tmp <= m_end) { + + 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->poolNum_, e->refCtr_); + QS_EQC_PRE(tmp); // # free entries + QS_END_PRE() + + tmp = m_tail; // get volatile into temporary + QEvt const * const frontEvt = m_ring[tmp]; + + Q_ASSERT_INCRIT(420, frontEvt != nullptr); + + m_frontEvt = frontEvt; // update the original + + if (tmp == 0U) { // need to wrap the tail? + tmp = m_end; } - m_tail = (m_tail - 1U); - - QS_BEGIN_NOCRIT_PRE_(QS_QF_EQUEUE_GET, qs_id) - QS_TIME_PRE_(); // timestamp - QS_SIG_PRE_(e->sig); // the signal of this event - QS_OBJ_PRE_(this); // this queue object - QS_2U8_PRE_(e->poolId_, e->refCtr_); - QS_EQC_PRE_(nFree); // # free entries - QS_END_NOCRIT_PRE_() + --tmp; // advance the tail (counter-clockwise) + m_tail = tmp; // update the original } 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)); + // all entries in the queue must be free (+1 for frontEvt) + Q_INVARIANT_INCRIT(440, tmp == (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_() + 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->poolNum_, e->refCtr_); + QS_END_PRE() } } - QF_CRIT_X_(); + + QF_CRIT_EXIT(); return e; } } // namespace QP -//$enddef${QF::QEQueue} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/src/qf_qmact.cpp b/src/qf_qmact.cpp index 3d297e5..3029e3a 100644 --- a/src/qf_qmact.cpp +++ b/src/qf_qmact.cpp @@ -1,120 +1,56 @@ -//$file${src::qf::qf_qmact.cpp} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv +//============================================================================ +// QP/C++ Real-Time Embedded Framework (RTEF) +// Version 8.0.2 // -// Model: qpcpp.qm -// File: ${src::qf::qf_qmact.cpp} +// Copyright (C) 2005 Quantum Leaps, LLC. All rights reserved. // -// This code has been generated by QM 5.2.5 . -// DO NOT EDIT THIS FILE MANUALLY. All your changes will be lost. -// -// This code is covered by the following QP license: -// License # : LicenseRef-QL-dual -// Issued to : Any user of the QP/C++ real-time embedded framework -// Framework(s) : qpcpp -// Support ends : 2023-12-31 -// License scope: -// -// Copyright (C) 2005 Quantum Leaps, LLC . +// 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: +// This software is dual-licensed under the terms of the open-source GNU +// General Public License (GPL) or under the terms of one of the closed- +// source Quantum Leaps commercial licenses. // // Redistributions in source code must retain this top-level comment block. // Plagiarizing this software to sidestep the license obligations is illegal. // -// Contact information: +// NOTE: +// The GPL does NOT permit the incorporation of this code into proprietary +// programs. Please contact Quantum Leaps for commercial licensing options, +// which expressly supersede the GPL and are designed explicitly for +// closed-source distribution. +// +// Quantum Leaps contact information: // // -// -//$endhead${src::qf::qf_qmact.cpp} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -//! @file -//! @brief QMActive::QMActive() and virtual functions - -#define QP_IMPL // this is QP implementation -#include "qf_port.hpp" // QF port -#include "qassert.h" // QP embedded systems-friendly assertions - -//! Internal macro to cast a QP::QMActive pointer `qact_` to QP::QMsm* -//! @note -//! Casting pointer to pointer pointer violates the MISRA-C++ 2008 Rule 5-2-7, -//! cast from pointer to pointer. Additionally this cast violates the MISRA- -//! C++ 2008 Rule 5-2-8 Unusual pointer cast (incompatible indirect types). -//! Encapsulating these violations in a macro allows to selectively suppress -//! this specific deviation. -#define QF_QMACTIVE_TO_QMSM_CAST_(qact_) \ - reinterpret_cast((qact_)) - -//! Internal macro to cast a QP::QMActive pointer `qact_` to QP::QMsm const * -#define QF_QMACTIVE_TO_QMSM_CONST_CAST_(qact_) \ - reinterpret_cast((qact_)) +//============================================================================ +#define QP_IMPL // this is QP implementation +#include "qp_port.hpp" // QP port +#include "qp_pkg.hpp" // QP package-scope interface +#include "qsafe.h" // QP Functional Safety (FuSa) Subsystem +#ifdef Q_SPY // QS software tracing enabled? + #include "qs_port.hpp" // QS port + #include "qs_pkg.hpp" // QS facilities for pre-defined trace records +#else + #include "qs_dummy.hpp" // disable the QS software tracing +#endif // Q_SPY // unnamed namespace for local definitions with internal linkage namespace { //Q_DEFINE_THIS_MODULE("qf_qmact") } // unnamed namespace -//$skip${QP_VERSION} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv -// Check for the minimum required QP version -#if (QP_VERSION < 700U) || (QP_VERSION != ((QP_RELEASE^4294967295U) % 0x3E8U)) -#error qpcpp version 7.0.0 or higher required -#endif -//$endskip${QP_VERSION} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -//$define${QF::QMActive} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv namespace QP { -//${QF::QMActive} ............................................................ - -//${QF::QMActive::init} ...................................................... -void QMActive::init( - void const * const e, - std::uint_fast8_t const qs_id) +//............................................................................ +QMActive::QMActive(QStateHandler const initial) noexcept + : QActive(initial) { - m_state.obj = &QMsm::msm_top_s; - QF_QMACTIVE_TO_QMSM_CAST_(this)->QMsm::init(e, qs_id); + m_state.obj = reinterpret_cast(this)->topQMState(); + m_temp.fun = initial; } -//${QF::QMActive::init} ...................................................... -void QMActive::init(std::uint_fast8_t const qs_id) { - m_state.obj = &QMsm::msm_top_s; - QF_QMACTIVE_TO_QMSM_CAST_(this)->QMsm::init(qs_id); -} - -//${QF::QMActive::dispatch} .................................................. -void QMActive::dispatch( - QEvt const * const e, - std::uint_fast8_t const qs_id) -{ - QF_QMACTIVE_TO_QMSM_CAST_(this)->QMsm::dispatch(e, qs_id); -} - -//${QF::QMActive::isInState} ................................................. -bool QMActive::isInState(QMState const * const st) const noexcept { - return QF_QMACTIVE_TO_QMSM_CONST_CAST_(this)->QMsm::isInState(st); -} - -//${QF::QMActive::childStateObj} ............................................. -QMState const * QMActive::childStateObj(QMState const * const parent) const noexcept { - return QF_QMACTIVE_TO_QMSM_CONST_CAST_(this) - ->QMsm::childStateObj(parent); -} - -//${QF::QMActive::getStateHandler} ........................................... -#ifdef Q_SPY -QStateHandler QMActive::getStateHandler() noexcept { - return QF_QMACTIVE_TO_QMSM_CAST_(this)->QMsm::getStateHandler(); -} - -#endif // def Q_SPY - } // namespace QP -//$enddef${QF::QMActive} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/src/qf_time.cpp b/src/qf_time.cpp index 2e1fcc8..1c5fd60 100644 --- a/src/qf_time.cpp +++ b/src/qf_time.cpp @@ -1,48 +1,35 @@ -//$file${src::qf::qf_time.cpp} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv +//============================================================================ +// SafeQP/C++ Real-Time Embedded Framework (RTEF) +// Version 8.0.2 // -// Model: qpcpp.qm -// File: ${src::qf::qf_time.cpp} +// Copyright (C) 2005 Quantum Leaps, LLC. All rights reserved. // -// This code has been generated by QM 5.2.5 . -// DO NOT EDIT THIS FILE MANUALLY. All your changes will be lost. +// Q u a n t u m L e a P s +// ------------------------ +// Modern Embedded Software // -// This code is covered by the following QP license: -// License # : LicenseRef-QL-dual -// Issued to : Any user of the QP/C++ real-time embedded framework -// Framework(s) : qpcpp -// Support ends : 2023-12-31 -// License scope: +// SPDX-License-Identifier: LicenseRef-QL-commercial // -// Copyright (C) 2005 Quantum Leaps, LLC . +// This software is licensed under the terms of the Quantum Leaps commercial +// licenses. Please contact Quantum Leaps for more information about the +// available licensing options. // -// SPDX-License-Identifier: GPL-3.0-or-later OR LicenseRef-QL-commercial +// RESTRICTIONS +// You may NOT : +// (a) redistribute, encumber, sell, rent, lease, sublicense, or otherwise +// transfer rights in this software, +// (b) remove or alter any trademark, logo, copyright or other proprietary +// notices, legends, symbols or labels present in this software, +// (c) plagiarize this software to sidestep the licensing obligations. // -// 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: +// Quantum Leaps contact information: // // -// -//$endhead${src::qf::qf_time.cpp} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -//! @file -//! @brief QF/C++ time events and time management services - +//============================================================================ #define QP_IMPL // this is QP implementation -#include "qf_port.hpp" // QF port -#include "qf_pkg.hpp" // QF package-scope interface -#include "qassert.h" // QP embedded systems-friendly assertions +#include "qp_port.hpp" // QP port +#include "qp_pkg.hpp" // QP package-scope interface +#include "qsafe.h" // QP Functional Safety (FuSa) Subsystem #ifdef Q_SPY // QS software tracing enabled? #include "qs_port.hpp" // QS port #include "qs_pkg.hpp" // QS facilities for pre-defined trace records @@ -55,386 +42,381 @@ namespace { Q_DEFINE_THIS_MODULE("qf_time") } // unnamed namespace -//$skip${QP_VERSION} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv -// Check for the minimum required QP version -#if (QP_VERSION < 700U) || (QP_VERSION != ((QP_RELEASE^4294967295U) % 0x3E8U)) -#error qpcpp version 7.0.0 or higher required -#endif -//$endskip${QP_VERSION} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -//$define${QF::QTimeEvt} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv namespace QP { //${QF::QTimeEvt} ............................................................ QTimeEvt QTimeEvt::timeEvtHead_[QF_MAX_TICK_RATE]; -//${QF::QTimeEvt::QTimeEvt} .................................................. +//............................................................................ QTimeEvt::QTimeEvt( QActive * const act, - enum_t const sgnl, - std::uint_fast8_t const tickRate) -: -#ifndef Q_EVT_CTOR - QEvt(), -#else - QEvt(static_cast(sgnl), 0U), -#endif + QSignal const sig, + std::uint_fast8_t const tickRate) noexcept + : QEvt(sig), m_next(nullptr), m_act(act), m_ctr(0U), - m_interval(0U) + m_interval(0U), + m_tickRate(0U), + m_flags(0U) { - //! @pre The signal must be valid and the tick rate in range - Q_REQUIRE_ID(300, (sgnl != 0) - && (tickRate < QF_MAX_TICK_RATE)); - - #ifndef Q_EVT_CTOR - sig = static_cast(sgnl); // set QEvt::sig of this time event - #endif - - // Setting the POOL_ID event attribute to zero is correct only for - // events not allocated from event pools, which must be the case - // for Time Events. - // - poolId_ = 0U; - - // 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); + QF_CRIT_STAT + QF_CRIT_ENTRY(); + Q_REQUIRE_INCRIT(300, (sig != 0U) + && (tickRate < QF_MAX_TICK_RATE)); + QF_CRIT_EXIT(); + + refCtr_ = 0U; // adjust from the QEvt(sig) ctor } -//${QF::QTimeEvt::armX} ...................................................... +//............................................................................ void QTimeEvt::armX( - QTimeEvtCtr const nTicks, - QTimeEvtCtr const interval) noexcept + std::uint32_t const nTicks, + std::uint32_t const interval) noexcept { - std::uint8_t const tickRate = refCtr_ & TE_TICK_RATE; + QF_CRIT_STAT + QF_CRIT_ENTRY(); + + // dynamic range checks +#if (QF_TIMEEVT_CTR_SIZE == 1U) + Q_REQUIRE_INCRIT(400, (nTicks < 0xFFU) && (interval < 0xFFU)); +#elif (QF_TIMEEVT_CTR_SIZE == 2U) + Q_REQUIRE_INCRIT(400, (nTicks < 0xFFFFU) && (interval < 0xFFFFU)); +#endif + QTimeEvtCtr const ctr = m_ctr; + std::uint8_t const tickRate = m_tickRate; +#ifdef Q_SPY + std::uint_fast8_t const qsId = + static_cast(m_act)->m_prio; +#endif // def Q_SPY - //! @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 - - QF_CRIT_STAT_ - QF_CRIT_E_(); - m_ctr = nTicks; - m_interval = interval; + Q_REQUIRE_INCRIT(410, + (nTicks != 0U) + && (ctr == 0U) + && (m_act != nullptr) + && (tickRate < QF_MAX_TICK_RATE)); + +#ifdef Q_UNSAFE + Q_UNUSED_PAR(ctr); +#endif // ndef Q_UNSAFE + + m_ctr = static_cast(nTicks); + m_interval = static_cast(interval); // is the time event unlinked? // NOTE: For the duration of a single clock tick of the specified tick - // rate a time event can be disarmed and yet still linked into the list, - // because un-linking is performed exclusively in the QF_tickX() function. - // - if (static_cast( - static_cast(refCtr_) & TE_IS_LINKED) == 0U) - { - // mark as linked - refCtr_ = static_cast(refCtr_ | TE_IS_LINKED); + // rate a time event can be disarmed and yet still linked into the list + // because un-linking is performed exclusively in the QTimeEvt::tick(). + if ((m_flags & QTE_FLAG_IS_LINKED) == 0U) { + m_flags |= QTE_FLAG_IS_LINKED; // mark as linked // The time event is initially inserted into the separate // "freshly armed" list based on timeEvtHead_[tickRate].act. - // Only later, inside QTimeEvt::tick_(), the "freshly armed" + // Only later, inside QTimeEvt::tick(), the "freshly armed" // list is appended to the main list of armed time events based on // timeEvtHead_[tickRate].next. Again, this is to keep any - // changes to the main list exclusively inside QTimeEvt::tick_(). + // changes to the main list exclusively inside QTimeEvt::tick(). m_next = timeEvtHead_[tickRate].toTimeEvt(); timeEvtHead_[tickRate].m_act = this; } - #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_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_(interval); // the interval - QS_U8_PRE_(tickRate); // tick rate - QS_END_NOCRIT_PRE_() - - QF_CRIT_X_(); + 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 # ticks + QS_TEC_PRE(interval); // the interval + QS_U8_PRE(tickRate); // tick rate + QS_END_PRE() + + QF_CRIT_EXIT(); } -//${QF::QTimeEvt::disarm} .................................................... +//............................................................................ bool QTimeEvt::disarm() noexcept { - QF_CRIT_STAT_ - QF_CRIT_E_(); - #ifdef Q_SPY - std::uint_fast8_t const qs_id = static_cast(m_act)->m_prio; - #endif - - // is the time event actually armed? - bool wasArmed; - if (m_ctr != 0U) { - wasArmed = true; - refCtr_ = static_cast(refCtr_ | TE_WAS_DISARMED); + QF_CRIT_STAT + QF_CRIT_ENTRY(); - QS_BEGIN_NOCRIT_PRE_(QS_QF_TIMEEVT_DISARM, qs_id) - QS_TIME_PRE_(); // timestamp - QS_OBJ_PRE_(this); // this time event object - QS_OBJ_PRE_(m_act); // the target AO - QS_TEC_PRE_(m_ctr); // the number of ticks - QS_TEC_PRE_(m_interval); // the interval - QS_U8_PRE_(refCtr_& TE_TICK_RATE); - QS_END_NOCRIT_PRE_() + QTimeEvtCtr const ctr = m_ctr; + +#ifdef Q_SPY + std::uint_fast8_t const qsId = static_cast(m_act)->m_prio; +#endif + // was the time event actually armed? + bool wasArmed = false; + if (ctr != 0U) { + wasArmed = true; + m_flags |= QTE_FLAG_WAS_DISARMED; m_ctr = 0U; // schedule removal from the list + + 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(ctr); // the # ticks + QS_TEC_PRE(m_interval); // the interval + QS_U8_PRE(m_tickRate); // tick rate + QS_END_PRE() } else { // the time event was already disarmed automatically - wasArmed = false; - refCtr_ = static_cast(refCtr_ - & static_cast(~TE_WAS_DISARMED)); - - QS_BEGIN_NOCRIT_PRE_(QS_QF_TIMEEVT_DISARM_ATTEMPT, qs_id) - QS_TIME_PRE_(); // timestamp - QS_OBJ_PRE_(this); // this time event object - QS_OBJ_PRE_(m_act); // the target AO - QS_U8_PRE_(refCtr_& TE_TICK_RATE); // tick rate - QS_END_NOCRIT_PRE_() - + m_flags &= static_cast(~QTE_FLAG_WAS_DISARMED); + + 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(m_tickRate); // tick rate + QS_END_PRE() } - QF_CRIT_X_(); + + QF_CRIT_EXIT(); return wasArmed; } -//${QF::QTimeEvt::rearm} ..................................................... -bool QTimeEvt::rearm(QTimeEvtCtr const nTicks) noexcept { - std::uint8_t const tickRate = refCtr_ & TE_TICK_RATE; +//............................................................................ +bool QTimeEvt::rearm(std::uint32_t const nTicks) noexcept { + QF_CRIT_STAT + QF_CRIT_ENTRY(); - //! @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) - && (tickRate < static_cast(QF_MAX_TICK_RATE)) - && (nTicks != 0U) - && (static_cast(sig) >= Q_USER_SIG)); + // dynamic range checks +#if (QF_TIMEEVT_CTR_SIZE == 1U) + Q_REQUIRE_INCRIT(600, nTicks < 0xFFU); +#elif (QF_TIMEEVT_CTR_SIZE == 2U) + Q_REQUIRE_INCRIT(600, nTicks < 0xFFFFU); +#endif - QF_CRIT_STAT_ - QF_CRIT_E_(); + std::uint8_t const tickRate = m_tickRate; + QTimeEvtCtr const ctr = m_ctr; - // is the time evt not running? - bool wasArmed; - if (m_ctr == 0U) { - wasArmed = false; + Q_REQUIRE_INCRIT(610, + (nTicks != 0U) + && (m_act != nullptr) + && (tickRate < QF_MAX_TICK_RATE)); - // is the time event unlinked? - // NOTE: For a duration of a single clock tick of the specified +#ifdef Q_SPY + std::uint_fast8_t const qsId = static_cast(m_act)->m_prio; +#endif + + m_ctr = static_cast(nTicks); + + // was the time evt not running? + bool wasArmed = false; + if (ctr == 0U) { + // NOTE: For the duration of a single clock tick of the specified // tick rate a time event can be disarmed and yet still linked into // the list, because unlinking is performed exclusively in the - // QTimeEvt::tickX() function. - if (static_cast(refCtr_ & TE_IS_LINKED) == 0U) { - // mark as linked - refCtr_ = static_cast(refCtr_ | TE_IS_LINKED); + // QTimeEvt::tick() function. + + // is the time event unlinked? + if ((m_flags & QTE_FLAG_IS_LINKED) == 0U) { + m_flags |= QTE_FLAG_IS_LINKED; // mark as linked // The time event is initially inserted into the separate // "freshly armed" list based on timeEvtHead_[tickRate].act. - // Only later, inside QTimeEvt::tick_(), the "freshly armed" + // Only later, inside QTimeEvt::tick(), the "freshly armed" // list is appended to the main list of armed time events based on // timeEvtHead_[tickRate].next. Again, this is to keep any - // changes to the main list exclusively inside QTimeEvt::tick_(). + // changes to the main list exclusively inside QTimeEvt::tick(). m_next = timeEvtHead_[tickRate].toTimeEvt(); timeEvtHead_[tickRate].m_act = this; } } - else { // the time event is being disarmed + else { // the time event was armed wasArmed = true; } - m_ctr = nTicks; // re-load the tick counter (shift the phasing) - - #ifdef Q_SPY - std::uint_fast8_t const qs_id = static_cast(m_act)->m_prio; - #endif - QS_BEGIN_NOCRIT_PRE_(QS_QF_TIMEEVT_REARM, qs_id) - QS_TIME_PRE_(); // timestamp - QS_OBJ_PRE_(this); // this time event object - QS_OBJ_PRE_(m_act); // the target AO - QS_TEC_PRE_(m_ctr); // the number of ticks - QS_TEC_PRE_(m_interval); // the interval - QS_2U8_PRE_(tickRate, (wasArmed ? 1U : 0U)); - QS_END_NOCRIT_PRE_() - - QF_CRIT_X_(); + + 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(nTicks); // the # ticks + QS_TEC_PRE(m_interval); // the interval + QS_2U8_PRE(tickRate, (wasArmed ? 1U : 0U)); + QS_END_PRE() + + QF_CRIT_EXIT(); return wasArmed; } -//${QF::QTimeEvt::wasDisarmed} ............................................... +//............................................................................ bool QTimeEvt::wasDisarmed() noexcept { - std::uint8_t const isDisarmed = refCtr_ & TE_WAS_DISARMED; - // mark as disarmed - refCtr_ = static_cast(refCtr_ | TE_WAS_DISARMED); - return isDisarmed != 0U; + QF_CRIT_STAT + QF_CRIT_ENTRY(); + + bool const wasDisarmed = (m_flags & QTE_FLAG_WAS_DISARMED) != 0U; + m_flags |= QTE_FLAG_WAS_DISARMED; + + QF_CRIT_EXIT(); + + return wasDisarmed; } -//${QF::QTimeEvt::tick_} ..................................................... -void QTimeEvt::tick_( +//............................................................................ +void QTimeEvt::tick( std::uint_fast8_t const tickRate, void const * const sender) noexcept { - Q_UNUSED_PAR(sender); // when Q_SPY not defined +#ifndef Q_SPY + Q_UNUSED_PAR(sender); +#endif - QTimeEvt *prev = &timeEvtHead_[tickRate]; + QF_CRIT_STAT + QF_CRIT_ENTRY(); + + Q_REQUIRE_INCRIT(800, tickRate < Q_DIM(timeEvtHead_)); - QF_CRIT_STAT_ - QF_CRIT_E_(); + QTimeEvt *prev = &timeEvtHead_[tickRate]; - QS_BEGIN_NOCRIT_PRE_(QS_QF_TICK, 0U) +#ifdef Q_SPY + QS_BEGIN_PRE(QS_QF_TICK, 0U) prev->m_ctr = (prev->m_ctr + 1U); - QS_TEC_PRE_(prev->m_ctr); // tick ctr - QS_U8_PRE_(tickRate); // tick rate - QS_END_NOCRIT_PRE_() + QS_TEC_PRE(prev->m_ctr); // tick ctr + QS_U8_PRE(tickRate); // tick rate + QS_END_PRE() +#endif // def Q_SPY // 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) { + while (true) { + Q_ASSERT_INCRIT(810, prev != nullptr); // sanity check - // any new time events armed since the last run of tick_()? - if (timeEvtHead_[tickRate].m_act != nullptr) { + QTimeEvt *te = prev->m_next; // advance down the time evt. list - // sanity check - Q_ASSERT_CRIT_(110, prev != nullptr); - prev->m_next = timeEvtHead_[tickRate].toTimeEvt(); - timeEvtHead_[tickRate].m_act = nullptr; - t = prev->m_next; // switch to the new list - } - else { - break; // all currently armed time evts. processed + if (te == nullptr) { // end of the list? + // NO any new time events armed since the last QTimeEvt_tick_()? + if (timeEvtHead_[tickRate].m_act == nullptr) { + break; // terminate the while-loop } + + prev->m_next = timeEvtHead_[tickRate].toTimeEvt(); + timeEvtHead_[tickRate].m_act = nullptr; + + te = prev->m_next; // switch to the new list } - // time event scheduled for removal? - if (t->m_ctr == 0U) { - prev->m_next = t->m_next; - // mark time event 't' as NOT linked - t->refCtr_ = static_cast(t->refCtr_ - & static_cast(~TE_IS_LINKED)); - // do NOT advance the prev pointer - QF_CRIT_X_(); // exit crit. section to reduce latency + // the time event 'te' must be valid + Q_ASSERT_INCRIT(840, te != nullptr); + + QTimeEvtCtr ctr = te->m_ctr; - // prevent merging critical sections, see NOTE1 below - QF_CRIT_EXIT_NOP(); + if (ctr == 0U) { // time event scheduled for removal? + prev->m_next = te->m_next; + + // mark time event 'te' as NOT linked + te->m_flags &= static_cast(~QTE_FLAG_IS_LINKED); + // do NOT advance the prev pointer + QF_CRIT_EXIT(); // exit crit. section to reduce latency } - else { - t->m_ctr = (t->m_ctr - 1U); - - // is time evt about to expire? - if (t->m_ctr == 0U) { - QActive * const act = t->toActive(); - - // 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(t->refCtr_ - & static_cast(~TE_IS_LINKED)); - // do NOT advance the prev pointer - - QS_BEGIN_NOCRIT_PRE_(QS_QF_TIMEEVT_AUTO_DISARM, - act->m_prio) - QS_OBJ_PRE_(t); // this time event object - QS_OBJ_PRE_(act); // the target AO - QS_U8_PRE_(tickRate); // tick rate - QS_END_NOCRIT_PRE_() - } - - QS_BEGIN_NOCRIT_PRE_(QS_QF_TIMEEVT_POST, act->m_prio) - QS_TIME_PRE_(); // timestamp - QS_OBJ_PRE_(t); // the time event object - QS_SIG_PRE_(t->sig); // signal of this time event - QS_OBJ_PRE_(act); // the target AO - QS_U8_PRE_(tickRate); // tick rate - QS_END_NOCRIT_PRE_() - - QF_CRIT_X_(); // exit crit. section before posting - - // asserts if queue overflows - act->POST(t, sender); + else if (ctr == 1U) { // is time event about to expire? + QActive * const act = te->toActive(); + prev = te->expire_(prev, act, tickRate); + +#ifdef QXK_HPP_ + if (te->sig < Q_USER_SIG) { + QXThread::timeout_(act); + QF_CRIT_EXIT(); } else { - prev = t; // advance to this time event - QF_CRIT_X_(); // exit crit. section to reduce latency - - // prevent merging critical sections - // In some QF ports the critical section exit takes effect only - // on the next machine instruction. If this case, the next - // instruction is another entry to a critical section, the - // critical section won't be really exited, but rather the - // two adjacent critical sections would be merged. The - // QF_CRIT_EXIT_NOP() macro contains minimal code required - // to prevent such merging of critical sections in QF ports, - // in which it can occur. - QF_CRIT_EXIT_NOP(); + QF_CRIT_EXIT(); // exit crit. section before posting + + // act->POST() asserts if the queue overflows + act->POST(te, sender); } +#else // not QXK + QF_CRIT_EXIT(); // exit crit. section before posting + + // act->POST() asserts if the queue overflows + act->POST(te, sender); +#endif + } + else { // time event keeps timing out + --ctr; // decrement the tick counter + te->m_ctr = ctr; // update the original + + prev = te; // advance to this time event + QF_CRIT_EXIT(); // exit crit. section to reduce latency } - QF_CRIT_E_(); // re-enter crit. section to continue + QF_CRIT_ENTRY(); // re-enter crit. section to continue the loop } - QF_CRIT_X_(); + QF_CRIT_EXIT(); } -//${QF::QTimeEvt::noActive} .................................................. +//............................................................................ bool QTimeEvt::noActive(std::uint_fast8_t const tickRate) noexcept { - //! @pre the tick rate must be in range - Q_REQUIRE_ID(200, tickRate < QF_MAX_TICK_RATE); + // NOTE: this function must be called *inside* critical section + Q_REQUIRE_INCRIT(900, tickRate < QF_MAX_TICK_RATE); + + bool inactive = false; - bool inactive; if (timeEvtHead_[tickRate].m_next != nullptr) { - inactive = false; + // empty } else if (timeEvtHead_[tickRate].m_act != nullptr) { - inactive = false; + // empty } else { inactive = true; } + return inactive; } -//${QF::QTimeEvt::QTimeEvt} .................................................. -QTimeEvt::QTimeEvt() - : -#ifdef Q_EVT_CTOR - QEvt(0U, 0U), -#else - QEvt(), -#endif // Q_EVT_CTOR +//............................................................................ +QTimeEvt::QTimeEvt() noexcept + : QEvt(0U), m_next(nullptr), m_act(nullptr), m_ctr(0U), - m_interval(0U) + m_interval(0U), + m_tickRate(0U), + m_flags(0U) +{} + +//............................................................................ +QTimeEvt *QTimeEvt::expire_( + QTimeEvt * const prev_link, + QActive const * const act, + std::uint_fast8_t const tickRate) noexcept { - #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 + // NOTE: this helper function is called *inside* critical section +#ifndef Q_SPY + Q_UNUSED_PAR(act); + Q_UNUSED_PAR(tickRate); +#endif + + QTimeEvt *prev = prev_link; + if (m_interval != 0U) { // periodic time evt? + m_ctr = m_interval; // rearm the time event + prev = this; // advance to this time event + } + else { // one-shot time event: automatically disarm + m_ctr = 0U; + prev->m_next = m_next; + + // mark this time event as NOT linked + m_flags &= + static_cast(~QTE_FLAG_IS_LINKED); + // do NOT advance the prev pointer + + QS_BEGIN_PRE(QS_QF_TIMEEVT_AUTO_DISARM, act->m_prio) + QS_OBJ_PRE(this); // 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(this); // the time event object + QS_SIG_PRE(sig); // signal of this time event + QS_OBJ_PRE(act); // the target AO + QS_U8_PRE(tickRate); // tick rate + QS_END_PRE() + + return prev; } } // namespace QP -//$enddef${QF::QTimeEvt} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/src/qmpool.hpp b/src/qmpool.hpp index 91faa01..8524824 100644 --- a/src/qmpool.hpp +++ b/src/qmpool.hpp @@ -1,62 +1,46 @@ -//$file${include::qmpool.hpp} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv -// -// Model: qpcpp.qm -// File: ${include::qmpool.hpp} -// -// This code has been generated by QM 5.2.5 . -// DO NOT EDIT THIS FILE MANUALLY. All your changes will be lost. +//============================================================================ +// QP/C++ Real-Time Embedded Framework (RTEF) +// Version 8.0.2 // -// This code is covered by the following QP license: -// License # : LicenseRef-QL-dual -// Issued to : Any user of the QP/C++ real-time embedded framework -// Framework(s) : qpcpp -// Support ends : 2023-12-31 -// License scope: +// Copyright (C) 2005 Quantum Leaps, LLC. All rights reserved. // -// 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: +// This software is dual-licensed under the terms of the open-source GNU +// General Public License (GPL) or under the terms of one of the closed- +// source Quantum Leaps commercial licenses. // // Redistributions in source code must retain this top-level comment block. // Plagiarizing this software to sidestep the license obligations is illegal. // -// Contact information: +// NOTE: +// The GPL does NOT permit the incorporation of this code into proprietary +// programs. Please contact Quantum Leaps for commercial licensing options, +// which expressly supersede the GPL and are designed explicitly for +// closed-source distribution. +// +// Quantum Leaps contact information: // // -// -//$endhead${include::qmpool.hpp} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -//! @file -//! @brief platform-independent memory pool QP::QMPool interface. - +//============================================================================ #ifndef QMPOOL_HPP_ #define QMPOOL_HPP_ #ifndef QF_MPOOL_SIZ_SIZE - //! macro to override the default QP::QMPoolSize size. - //! Valid values 1U, 2U, or 4U; default 2U #define QF_MPOOL_SIZ_SIZE 2U #endif - #ifndef QF_MPOOL_CTR_SIZE - //! macro to override the default QMPoolCtr size. - //! Valid values 1U, 2U, or 4U; default 2U - #define QF_MPOOL_CTR_SIZE 2 + #define QF_MPOOL_CTR_SIZE 2U #endif -//! Memory pool element to allocate correctly aligned storage for QP::QMPool -#define QF_MPOOL_EL(type_) \ - struct { void *sto_[((sizeof(type_) - 1U)/sizeof(void*)) + 1U]; } +#define QF_MPOOL_EL(evType_) struct { \ + void * sto_[((sizeof(evType_) - 1U) / sizeof(void *)) + \ + (sizeof(evType_) < (2U * sizeof(void *)) ? 2U : 1U)]; \ +} //============================================================================ namespace QP { @@ -64,244 +48,96 @@ namespace QP { #if (QF_MPOOL_SIZ_SIZE == 1U) using QMPoolSize = std::uint8_t; #elif (QF_MPOOL_SIZ_SIZE == 2U) - //! The data type to store the block-size based on the macro - //! #QF_MPOOL_SIZ_SIZE - //! - //! @details - //! The dynamic range of this data type determines the maximum size - //! of blocks that can be managed by the native QF event pool. using QMPoolSize = std::uint16_t; #elif (QF_MPOOL_SIZ_SIZE == 4U) using QMPoolSize = std::uint32_t; #else - #error "QF_MPOOL_SIZ_SIZE defined incorrectly, expected 1U, 2U, or 4U" + #error QF_MPOOL_SIZ_SIZE defined incorrectly, expected 1U, 2U, or 4U #endif #if (QF_MPOOL_CTR_SIZE == 1U) using QMPoolCtr = std::uint8_t; #elif (QF_MPOOL_CTR_SIZE == 2U) - //! The data type to store the block-counter based on the macro - //! #QF_MPOOL_CTR_SIZE - //! - //! @details - //! The dynamic range of this data type determines the maximum number - //! of blocks that can be stored in the pool. using QMPoolCtr = std::uint16_t; #elif (QF_MPOOL_CTR_SIZE == 4U) using QMPoolCtr = std::uint32_t; #else - #error "QF_MPOOL_CTR_SIZE defined incorrectly, expected 1U, 2U, or 4U" + #error QF_MPOOL_CTR_SIZE defined incorrectly, expected 1U, 2U, or 4U #endif -} // namespace QP - //============================================================================ -//$declare${QF::QMPool} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv -namespace QP { - -//${QF::QMPool} .............................................................. -//! Native QF memory pool class -//! -//! @details -//! A fixed block-size memory pool is a very fast and efficient data -//! structure for dynamic allocation of fixed block-size chunks of memory. -//! A memory pool offers fast and deterministic allocation and recycling of -//! memory blocks and is not subject to fragmenation.@n -//! @n -//! The QP::QMPool class describes the native QF memory pool, which can be -//! used as the event pool for dynamic event allocation, or as a fast, -//! deterministic fixed block-size heap for any other objects in your -//! application. -//! -//! @note -//! The QP::QMPool class contains only data members for managing a memory -//! pool, but does not contain the pool storage, which must be provided -//! externally during the pool initialization. -//! -//! @note -//! The native QF event pool is configured by defining the macro -//! #QF_EPOOL_TYPE_ as QP::QMPool in the specific QF port header file. class QMPool { private: - - //! start of the memory managed by this memory pool - void * m_start; - - //! end of the memory managed by this memory pool - void * m_end; - - //! head of linked list of free blocks - void * volatile m_free_head; - - //! maximum block size (in bytes) + void * * m_start; + void * * m_end; + void * * volatile m_freeHead; QMPoolSize m_blockSize; - - //! total number of blocks QMPoolCtr m_nTot; - - //! number of free blocks remaining QMPoolCtr volatile m_nFree; - - //! minimum number of free blocks ever present in this pool - //! - //! @note - //! This attribute remembers the low watermark of the pool, - //! which provides a valuable information for sizing event pools. - //! - //! @sa QF::getPoolMin(). QMPoolCtr m_nMin; -public: - - //! public default constructor - QMPool(); +#ifndef Q_UNSAFE + std::uintptr_t m_freeHead_dis; + QMPoolCtr m_nFree_dis; +#endif // ndef Q_UNSAFE - //! Initializes the native QF event pool - //! - //! @details - //! Initialize a fixed block-size memory pool by providing it with the - //! pool memory to manage, size of this memory, and the block size. - //! - //! @param[in] poolSto pointer to the memory buffer for pool storage - //! @param[in] poolSize size of the storage buffer in bytes - //! @param[in] blockSize fixed-size of the memory blocks in bytes - //! - //! @attention - //! The caller of QMPool::init() must make sure that the `poolSto` - //! pointer is properly **aligned**. In particular, it must be possible to - //! efficiently store a pointer at the location pointed to by `poolSto`. - //! Internally, the QMPool::init() function rounds up the block size - //! `blockSize` so that it can fit an integer number of pointers. This - //! is done to achieve proper alignment of the blocks within the pool. - //! - //! @note - //! Due to the rounding of block size the actual capacity of the pool - //! might be less than (`poolSize` / `blockSize`). You can check the - //! capacity of the pool by calling the QF::getPoolMin() function. - //! - //! @note - //! This function is **not** protected by a critical section, because - //! it is intended to be called only during the initialization of the - //! system, when interrupts are not allowed yet. - //! - //! @note - //! Many QF ports use memory pools to implement the event pools. +public: + QMPool() + : m_start(nullptr), + m_end(nullptr), + m_freeHead(nullptr), + m_blockSize(0U), + m_nTot(0U), + m_nFree(0U), + m_nMin(0U) + #ifndef Q_UNSAFE + ,m_freeHead_dis(static_cast(~0U)), + m_nFree_dis(static_cast(~0U)) + #endif + {} void init( void * const poolSto, - std::uint_fast32_t poolSize, - std::uint_fast16_t blockSize) noexcept; - - //! Obtains a memory block from a memory pool - //! - //! @details - //! The function allocates a memory block from the pool and returns a - //! pointer to the block back to the caller. - //! - //! @param[in] margin the minimum number of unused blocks still - //! available in the pool after the allocation. - //! @param[in] qs_id QS-id of this state machine (for QS local filter) - //! - //! @returns - //! A pointer to a memory block or NULL if no more blocks are available - //! in the memory pool. - //! - //! @note - //! This function can be called from any task level or ISR level. - //! - //! @note - //! The memory pool must be initialized before any events can - //! be requested from it. Also, the QP::QMPool::get() function uses - //! internally a QF critical section, so you should be careful not to - //! call it from within a critical section when nesting of critical - //! section is not supported. - //! - //! @attention - //! An allocated block must be later returned back to the **same** pool - //! from which it has been allocated. - //! - //! @sa - //! QP::QMPool::put() + std::uint_fast32_t const poolSize, + std::uint_fast16_t const blockSize) noexcept; void * get( std::uint_fast16_t const margin, - std::uint_fast8_t const qs_id) noexcept; - - //! Returns a memory block back to a memory pool - //! - //! @details - //! Recycle a memory block to the fixed block-size memory pool. - //! - //! @param[in] b pointer to the memory block that is being recycled - //! @param[in] qs_id QS-id of this state machine (for QS local filter) - //! - //! @attention - //! The recycled block must be allocated from the **same** memory pool - //! to which it is returned. - //! - //! @note - //! This function can be called from any task level or ISR level. - //! - //! @sa - //! QP::QMPool::get() + std::uint_fast8_t const qsId) noexcept; void put( - void * const b, - std::uint_fast8_t const qs_id) noexcept; - - //! return the fixed block-size of the blocks managed by this pool - QMPoolSize getBlockSize() const noexcept; - - //! Memory pool operation for obtaining the minimum number of free - //! blocks ever in the pool (a.k.a. "low-watermark"). - //! - //! @details - //! This operation needs to be used with caution because the - //! "low-watermark" can change unexpectedly. The main intent for using - //! this operation is to get an idea of pool usage to size the pool - //! adequately. - //! - //! @returns the minimum number of free entries ever in the memory pool - //! since init. + void * const block, + std::uint_fast8_t const qsId) noexcept; + QMPoolSize getBlockSize() const noexcept { + return m_blockSize; + } QMPoolCtr getNMin() const noexcept { + #ifndef Q_UNSAFE return m_nMin; + #else + return 0U; + #endif } - - //! Memory pool operation for obtaining the current number of free - //! blocks in the pool. - //! - //! @details - //! This operation needs to be used with caution because the number - //! of free blocks can change unexpectedly. - //! - //! @returns the current number of free blocks in the memory pool. QMPoolCtr getNFree() const noexcept { return m_nFree; } private: - - //! disallow copying of QP::QMPool QMPool(QEQueue const & other) = delete; - - //! disallow copying of QP::QMPool QMPool & operator=(QMPool const & other) = delete; public: #ifdef QF_ISR_API - //! the "FromISR" variant used in the QP port to "FreeRTOS" void * getFromISR( std::uint_fast16_t const margin, - std::uint_fast8_t const qs_id) noexcept; + std::uint_fast8_t const qsId) noexcept; #endif // def QF_ISR_API #ifdef QF_ISR_API - //! the "FromISR" variant used in the QP port to "FreeRTOS" void putFromISR( void * const b, - std::uint_fast8_t const qs_id) noexcept; + std::uint_fast8_t const qsId) noexcept; #endif // def QF_ISR_API }; // class QMPool } // namespace QP -//$enddecl${QF::QMPool} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ #endif // QMPOOL_HPP_ diff --git a/src/qp.hpp b/src/qp.hpp new file mode 100644 index 0000000..1eca340 --- /dev/null +++ b/src/qp.hpp @@ -0,0 +1,1067 @@ +//============================================================================ +// QP/C++ Real-Time Embedded Framework (RTEF) +// Version 8.0.2 +// +// Copyright (C) 2005 Quantum Leaps, LLC. All rights reserved. +// +// 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 (GPL) or under the terms of one of the closed- +// source Quantum Leaps commercial licenses. +// +// Redistributions in source code must retain this top-level comment block. +// Plagiarizing this software to sidestep the license obligations is illegal. +// +// NOTE: +// The GPL does NOT permit the incorporation of this code into proprietary +// programs. Please contact Quantum Leaps for commercial licensing options, +// which expressly supersede the GPL and are designed explicitly for +// closed-source distribution. +// +// Quantum Leaps contact information: +// +// +//============================================================================ +#ifndef QP_HPP_ +#define QP_HPP_ + +//============================================================================ +#define QP_VERSION_STR "8.0.2" +#define QP_VERSION 802U +// =802 =250120 +#define QP_RELEASE 0x6AEAB45DU + +//============================================================================ +// default configuration settings +//! @cond INTERNAL + +#ifndef Q_SIGNAL_SIZE +#define Q_SIGNAL_SIZE 2U +#endif + +#ifndef QF_MAX_ACTIVE +#define QF_MAX_ACTIVE 32U +#endif + +#if (QF_MAX_ACTIVE > 64U) +#error QF_MAX_ACTIVE exceeds the maximum of 64U; +#endif + +#ifndef QF_MAX_TICK_RATE +#define QF_MAX_TICK_RATE 1U +#endif + +#if (QF_MAX_TICK_RATE > 15U) +#error QF_MAX_TICK_RATE exceeds the maximum of 15U; +#endif + +#ifndef QF_MAX_EPOOL +#define QF_MAX_EPOOL 3U +#endif + +#if (QF_MAX_EPOOL > 15U) +#error QF_MAX_EPOOL exceeds the maximum of 15U; +#endif + +#ifndef QF_TIMEEVT_CTR_SIZE +#define QF_TIMEEVT_CTR_SIZE 4U +#endif + +#if (QF_TIMEEVT_CTR_SIZE > 4U) +#error QF_TIMEEVT_CTR_SIZE defined incorrectly, expected 1U, 2U, or 4U; +#endif + +#ifndef QF_EVENT_SIZ_SIZE +#define QF_EVENT_SIZ_SIZE 2U +#endif + +#if (QF_EVENT_SIZ_SIZE > 4U) +#error QF_EVENT_SIZ_SIZE defined incorrectly, expected 1U, 2U, or 4U; +#endif + +//! @endcond + +//============================================================================ +// global types/utilities + +using int_t = int; + +using enum_t = int; +using float32_t = float; +using float64_t = double; + +#define Q_UNUSED_PAR(par_) (static_cast(par_)) +#define Q_DIM(array_) (sizeof(array_) / sizeof((array_)[0U])) +#define Q_UINT2PTR_CAST(type_, uint_) (reinterpret_cast(uint_)) + +//============================================================================ + +namespace QP { + +extern char const versionStr[24]; + +// QSignal type +#if (Q_SIGNAL_SIZE == 1U) + using QSignal = std::uint8_t; +#elif (Q_SIGNAL_SIZE == 2U) + using QSignal = std::uint16_t; +#elif (Q_SIGNAL_SIZE == 4U) + using QSignal = std::uint32_t; +#endif + +//============================================================================ + +class QEvt { +public: + QSignal sig; + std::uint8_t poolNum_; + std::uint8_t volatile refCtr_; + +public: + enum DynEvt: std::uint8_t { DYNAMIC }; + +public: + explicit constexpr QEvt(QSignal const s) noexcept + : sig(s), + poolNum_(0x00U), + refCtr_(0xE0U) + {} + + QEvt() = delete; + void init() noexcept { + // no event parameters to initialize + } + void init(DynEvt const dummy) noexcept { + Q_UNUSED_PAR(dummy); + // no event parameters to initialize + } +}; // class QEvt + +using QEvtPtr = QEvt const *; + +//============================================================================ +// QEP (hierarchical event processor) types + +using QState = std::uint_fast8_t; + +class QXThread; // forward declaration + +using QStateHandler = QState (*)(void * const me, QEvt const * const e); +using QActionHandler = QState (*)(void * const me); +using QXThreadHandler = void (*)(QXThread * const me); + +struct QMState { + QMState const *superstate; + QStateHandler const stateHandler; + QActionHandler const entryAction; + QActionHandler const exitAction; + QActionHandler const initAction; +}; + +struct QMTranActTable { + QMState const *target; + QActionHandler const act[1]; +}; + +union QAsmAttr { + QStateHandler fun; + QActionHandler act; + QXThreadHandler thr; + QMState const *obj; + QMTranActTable const *tatbl; +}; + +constexpr enum_t Q_USER_SIG {4}; + +//============================================================================ +class QAsm { +protected: + QAsmAttr m_state; + QAsmAttr m_temp; + +public: + + //! All possible return values from state-handlers + //! NOTE: The ordering is important for algorithmic correctness. + enum QStateRet : QState { + // unhandled and need to "bubble up" + Q_RET_SUPER, //!< event passed to superstate to handle + Q_RET_UNHANDLED, //!< event unhandled due to a guard + + // handled and do not need to "bubble up" + Q_RET_HANDLED, //!< event handled (internal transition) + Q_RET_IGNORED, //!< event silently ignored (bubbled up to top) + + // entry/exit + Q_RET_ENTRY, //!< state entry action executed + Q_RET_EXIT, //!< state exit action executed + + // no side effects + Q_RET_NULL, //!< return value without any effect + + // transitions need to execute transition-action table in QP::QMsm + Q_RET_TRAN, //!< regular transition + Q_RET_TRAN_INIT, //!< initial transition in a state + + // transitions that additionally clobber QHsm.m_state + Q_RET_TRAN_HIST, //!< transition to history of a given state + }; + + //! Reserved signals by the QP-framework. + enum ReservedSig : QSignal { + Q_EMPTY_SIG, //!< signal to execute the default case + Q_ENTRY_SIG, //!< signal for entry actions + Q_EXIT_SIG, //!< signal for exit actions + Q_INIT_SIG //!< signal for nested initial transitions + }; + +protected: + explicit QAsm() noexcept + : m_state(), + m_temp () + {} + +public: + +#ifdef Q_XTOR + virtual ~QAsm() noexcept { + // empty + } +#endif // def Q_XTOR + virtual void init( + void const * const e, + std::uint_fast8_t const qsId) = 0; + virtual void init(std::uint_fast8_t const qsId) { + this->init(nullptr, qsId); + } + virtual void dispatch( + QEvt const * const e, + std::uint_fast8_t const qsId) = 0; + virtual bool isIn(QStateHandler const state) noexcept { + static_cast(state); + return false; + } + QStateHandler state() const noexcept { + return m_state.fun; + } + QMState const * stateObj() const noexcept { + return m_state.obj; + } + +#ifdef Q_SPY + virtual QStateHandler getStateHandler() noexcept { + return m_state.fun; + } +#endif // def Q_SPY + + static QState top( + void * const me, + QEvt const * const e) noexcept + { + static_cast(me); + static_cast(e); + return Q_RET_IGNORED; // the top state ignores all events + } + +protected: + QState tran(QStateHandler const target) noexcept { + m_temp.fun = target; + return Q_RET_TRAN; + } + QState tran_hist(QStateHandler const hist) noexcept { + m_temp.fun = hist; + return Q_RET_TRAN_HIST; + } + QState super(QStateHandler const superstate) noexcept { + m_temp.fun = superstate; + return Q_RET_SUPER; + } + QState qm_tran(void const * const tatbl) noexcept { + m_temp.tatbl = static_cast(tatbl); + return Q_RET_TRAN; + } + QState qm_tran_init(void const * const tatbl) noexcept { + m_temp.tatbl = static_cast(tatbl); + return Q_RET_TRAN_INIT; + } + QState qm_tran_hist( + QMState const * const hist, + void const * const tatbl) noexcept + { + m_state.obj = hist; + m_temp.tatbl = static_cast(tatbl); + return Q_RET_TRAN_HIST; + } + +#ifdef Q_SPY + QState qm_entry(QMState const * const s) noexcept { + m_temp.obj = s; + return Q_RET_ENTRY; + } +#endif // def Q_SPY + +#ifndef Q_SPY + QState qm_entry(QMState const * const s) noexcept { + static_cast(s); // unused parameter + return Q_RET_ENTRY; + } +#endif // ndef Q_SPY + +#ifdef Q_SPY + QState qm_exit(QMState const * const s) noexcept { + m_temp.obj = s; + return Q_RET_EXIT; + } +#endif // def Q_SPY + +#ifndef Q_SPY + QState qm_exit(QMState const * const s) noexcept { + static_cast(s); // unused parameter + return Q_RET_EXIT; + } +#endif // ndef Q_SPY +}; // class QAsm + +//============================================================================ +class QHsm : public QP::QAsm { +protected: + explicit QHsm(QStateHandler const initial) noexcept; + +public: + void init( + void const * const e, + std::uint_fast8_t const qsId) override; + void init(std::uint_fast8_t const qsId) override { + this->init(nullptr, qsId); + } + void dispatch( + QEvt const * const e, + std::uint_fast8_t const qsId) override; + bool isIn(QStateHandler const state) noexcept override; + QStateHandler childState(QStateHandler const parent) noexcept; + +#ifdef Q_SPY + QStateHandler getStateHandler() noexcept override { + return m_state.fun; + } +#endif // def Q_SPY + +private: + std::int_fast8_t tran_simple_( + QStateHandler * const path, + std::uint_fast8_t const qsId); + + std::int_fast8_t tran_complex_( + QStateHandler * const path, + std::uint_fast8_t const qsId); + + void enter_target_( + QStateHandler * const path, + std::int_fast8_t const depth, + std::uint_fast8_t const qsId); +}; // class QHsm + +//============================================================================ +class QMsm : public QP::QAsm { +protected: + explicit QMsm(QStateHandler const initial) noexcept; + +public: + void init( + void const * const e, + std::uint_fast8_t const qsId) override; + void init(std::uint_fast8_t const qsId) override { + this->init(nullptr, qsId); + } + void dispatch( + QEvt const * const e, + std::uint_fast8_t const qsId) override; + +#ifdef Q_SPY + QStateHandler getStateHandler() noexcept override { + return m_state.obj->stateHandler; + } +#endif // def Q_SPY + bool isIn(QStateHandler const state) noexcept override; + QMState const * childStateObj(QMState const * const parent) const noexcept; + +private: + QState execTatbl_( + QMTranActTable const * const tatbl, + std::uint_fast8_t const qsId); + void exitToTranSource_( + QMState const * const cs, + QMState const * const ts, + std::uint_fast8_t const qsId); + QState enterHistory_( + QMState const * const hist, + std::uint_fast8_t const qsId); + +public: + QMState const * topQMState() const noexcept; +}; // class QMsm + +} // namespace QP + +//============================================================================ +// QEP-macros + +#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) + +#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) + +#define Q_HANDLED() (Q_RET_HANDLED) +#define Q_UNHANDLED() (Q_RET_UNHANDLED) + +#define Q_EVT_CAST(subclass_) (static_cast(e)) +#define Q_STATE_CAST(handler_) (reinterpret_cast(handler_)) + +#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 + +#define QM_ACTION_DECL(action_) \ + QP::QState action_ ## _h(); \ + static QP::QState action_(void * const me) + +#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) + +#define QM_ACTION_DEF(subclass_, action_) \ + QP::QState subclass_::action_(void * const me) { \ + return static_cast(me)->action_ ## _h(); } \ + QP::QState subclass_::action_ ## _h() + +#define QM_HANDLED() (Q_RET_HANDLED) +#define QM_UNHANDLED() (Q_RET_HANDLED) +#define QM_SUPER() (Q_RET_SUPER) +#define QM_STATE_NULL (nullptr) +#define Q_ACTION_NULL (nullptr) + +#ifdef Q_SPY + #define INIT(qsId_) init((qsId_)) + #define DISPATCH(e_, qsId_) dispatch((e_), (qsId_)) +#else + #define INIT(dummy) init(0U) + #define DISPATCH(e_, dummy) dispatch((e_), 0U) +#endif // ndef Q_SPY + +//============================================================================ +namespace QP { + +using QPrioSpec = std::uint16_t; + +#if (QF_TIMEEVT_CTR_SIZE == 1U) + using QTimeEvtCtr = std::uint8_t; +#elif (QF_TIMEEVT_CTR_SIZE == 2U) + using QTimeEvtCtr = std::uint16_t; +#elif (QF_TIMEEVT_CTR_SIZE == 4U) + using QTimeEvtCtr = std::uint32_t; +#endif // (QF_TIMEEVT_CTR_SIZE == 4U) + +#if (QF_MAX_ACTIVE <= 8U) + using QPSetBits = std::uint8_t; +#elif (8U < QF_MAX_ACTIVE) && (QF_MAX_ACTIVE <= 16U) + using QPSetBits = std::uint16_t; +#elif (16 < QF_MAX_ACTIVE) + using QPSetBits = std::uint32_t; +#endif // (16 < QF_MAX_ACTIVE) + +#ifndef QF_LOG2 + std::uint_fast8_t QF_LOG2(QP::QPSetBits const bitmask) noexcept; +#endif // ndef QF_LOG2 + +//============================================================================ +class QPSet { +private: + QPSetBits m_bits[((QF_MAX_ACTIVE + (8U*sizeof(QPSetBits))) - 1U)/(8U*sizeof(QPSetBits))]; + +public: + void setEmpty() noexcept { + m_bits[0] = 0U; +#if (QF_MAX_ACTIVE > 32) + m_bits[1] = 0U; +#endif + } + bool isEmpty() const noexcept { +#if (QF_MAX_ACTIVE <= 32U) + return (m_bits[0] == 0U); +#else + return (m_bits[0] == 0U) ? (m_bits[1] == 0U) : false; +#endif + } + bool notEmpty() const noexcept { +#if (QF_MAX_ACTIVE <= 32U) + return (m_bits[0] != 0U); +#else + return (m_bits[0] != 0U) ? true : (m_bits[1] != 0U); +#endif + } + bool hasElement(std::uint_fast8_t const n) const noexcept { +#if (QF_MAX_ACTIVE <= 32U) + return (m_bits[0] & (static_cast(1U) << (n - 1U))) != 0U; +#else + return (n <= 32U) + ? ((m_bits[0] & (static_cast(1U) << (n - 1U))) != 0U) + : ((m_bits[1] & (static_cast(1U) << (n - 33U))) != 0U); +#endif + } + void insert(std::uint_fast8_t const n) noexcept { +#if (QF_MAX_ACTIVE <= 32U) + m_bits[0] = (m_bits[0] | (static_cast(1U) << (n - 1U))); +#else + if (n <= 32U) { + m_bits[0] = (m_bits[0] | (static_cast(1U) << (n - 1U))); + } + else { + m_bits[1] = (m_bits[1] | (static_cast(1U) << (n - 33U))); + } +#endif + } + void remove(std::uint_fast8_t const n) noexcept { +#if (QF_MAX_ACTIVE <= 32U) + m_bits[0] = (m_bits[0] & static_cast(~(1U << (n - 1U)))); +#else + if (n <= 32U) { + (m_bits[0] = (m_bits[0] & ~(static_cast(1U) << (n - 1U)))); + } + else { + (m_bits[1] = (m_bits[1] & ~(static_cast(1U) << (n - 33U)))); + } +#endif + } + std::uint_fast8_t findMax() const noexcept { +#if (QF_MAX_ACTIVE <= 32U) + return QF_LOG2(m_bits[0]); +#else + return (m_bits[1] != 0U) + ? (QF_LOG2(m_bits[1]) + 32U) + : (QF_LOG2(m_bits[0])); +#endif + } + +}; // class QPSet + +//============================================================================ +class QSubscrList { +private: + QPSet m_set; + + // friends... + friend class QActive; +}; // class QSubscrList + +//============================================================================ +class QPtrDis { +private: + std::uintptr_t m_ptr_dis; + + // friends... + friend class QTimeEvt; + friend class QXThread; + +public: + QPtrDis(void const * const ptr = nullptr) noexcept; +}; // class QPtrDis + +class QEQueue; // forward declaration + + +//============================================================================ +class QActive : public QP::QAsm { +protected: + std::uint8_t m_prio; + std::uint8_t m_pthre; + +#ifdef QACTIVE_THREAD_TYPE + QACTIVE_THREAD_TYPE m_thread; +#endif // def QACTIVE_THREAD_TYPE + +#ifdef QACTIVE_OS_OBJ_TYPE + QACTIVE_OS_OBJ_TYPE m_osObject; +#endif // def QACTIVE_OS_OBJ_TYPE + +#ifdef QACTIVE_EQUEUE_TYPE + QACTIVE_EQUEUE_TYPE m_eQueue; +#endif // def QACTIVE_EQUEUE_TYPE + +public: + static QActive * registry_[QF_MAX_ACTIVE + 1U]; + static QSubscrList * subscrList_; + static enum_t maxPubSignal_; + + // friends... + friend class QTimeEvt; + friend class QTicker; + friend class QXThread; + friend class QXMutex; + friend class QXSemaphore; + friend class QActiveDummy; + friend class GuiQActive; + friend class GuiQMActive; + friend void schedLock(); + +protected: + explicit QActive(QStateHandler const initial) noexcept + : QAsm(), + m_prio(0U), + m_pthre(0U) + { + m_state.fun = Q_STATE_CAST(&top); + m_temp.fun = initial; + } + +public: + void init( + void const * const e, + std::uint_fast8_t const qsId) override + { + reinterpret_cast(this)->QHsm::init(e, qsId); + } + void init(std::uint_fast8_t const qsId) override { + this->init(nullptr, qsId); + } + void dispatch( + QEvt const * const e, + std::uint_fast8_t const qsId) override + { + reinterpret_cast(this)->QHsm::dispatch(e, qsId); + } + bool isIn(QStateHandler const state) noexcept override { + return reinterpret_cast(this)->QHsm::isIn(state); + } + QStateHandler childState(QStateHandler const parent) noexcept { + return reinterpret_cast(this)->QHsm::childState(parent); + } + void setAttr( + std::uint32_t attr1, + void const * attr2 = nullptr); + void start( + QPrioSpec const prioSpec, + QEvtPtr * const qSto, + std::uint_fast16_t const qLen, + void * const stkSto, + std::uint_fast16_t const stkSize, + void const * const par); + void start( + QPrioSpec const prioSpec, + QEvtPtr * const qSto, + std::uint_fast16_t const qLen, + void * const stkSto, + std::uint_fast16_t const stkSize) + { + this->start(prioSpec, qSto, qLen, stkSto, stkSize, nullptr); + } + +#ifdef QACTIVE_CAN_STOP + void stop(); +#endif // def QACTIVE_CAN_STOP + void register_() noexcept; + void unregister_() noexcept; + bool post_( + QEvt const * const e, + std::uint_fast16_t const margin, + void const * const sender) noexcept; + void postLIFO(QEvt const * const e) noexcept; + QEvt const * get_() noexcept; + static std::uint_fast16_t getQueueMin(std::uint_fast8_t const prio) noexcept; + static void psInit( + QSubscrList * const subscrSto, + enum_t const maxSignal) noexcept; + static void publish_( + QEvt const * const e, + void const * const sender, + std::uint_fast8_t const qsId) noexcept; + void subscribe(enum_t const sig) const noexcept; + void unsubscribe(enum_t const sig) const noexcept; + void unsubscribeAll() const noexcept; + bool defer( + QEQueue * const eq, + QEvt const * const e) const noexcept; + bool recall(QEQueue * const eq) noexcept; + std::uint_fast16_t flushDeferred( + QEQueue * const eq, + std::uint_fast16_t const num = 0xFFFFU) const noexcept; + std::uint_fast8_t getPrio() const noexcept { + return static_cast(m_prio); + } + void setPrio(QPrioSpec const prio) noexcept { + m_prio = static_cast(prio & 0xFFU); + m_pthre = static_cast(prio >> 8U); + } + std::uint_fast8_t getPThre() const noexcept { + return static_cast(m_pthre); + } + +#ifdef QACTIVE_EQUEUE_TYPE + QACTIVE_EQUEUE_TYPE const & getEQueue() const noexcept { + return m_eQueue; + } +#endif // def QACTIVE_EQUEUE_TYPE + +#ifdef QACTIVE_OS_OBJ_TYPE + QACTIVE_OS_OBJ_TYPE const & getOsObject() const noexcept { + return m_osObject; + } +#endif // def QACTIVE_OS_OBJ_TYPE + +#ifdef QACTIVE_THREAD_TYPE + QACTIVE_THREAD_TYPE const & getThread() const noexcept { + return m_thread; + } +#endif // def QACTIVE_THREAD_TYPE + +#ifdef QACTIVE_THREAD_TYPE + void setThread(QACTIVE_THREAD_TYPE const & thr) { + m_thread = thr; + } +#endif // def QACTIVE_THREAD_TYPE + static void evtLoop_(QActive * act); + +#ifdef QF_ISR_API + virtual bool postFromISR( + QEvt const * const e, + std::uint_fast16_t const margin, + void * par, + void const * const sender) noexcept; +#endif // def QF_ISR_API + +#ifdef QF_ISR_API + static void publishFromISR( + QEvt const * e, + void * par, + void const * sender) noexcept; +#endif // def QF_ISR_API + +private: + void postFIFO_( + QEvt const * const e, + void const * const sender); +}; // class QActive + +//============================================================================ +class QMActive : public QP::QActive { +protected: + QMActive(QStateHandler const initial) noexcept; + +public: + void init( + void const * const e, + std::uint_fast8_t const qsId) override + { + reinterpret_cast(this)->QMsm::init(e, qsId); + } + void init(std::uint_fast8_t const qsId) override { + this->init(nullptr, qsId); + } + void dispatch( + QEvt const * const e, + std::uint_fast8_t const qsId) override + { + reinterpret_cast(this)->QMsm::dispatch(e, qsId); + } + bool isIn(QStateHandler const state) noexcept override { + return reinterpret_cast(this)->QMsm::isIn(state); + } + +#ifdef Q_SPY + QStateHandler getStateHandler() noexcept override { + return reinterpret_cast(this)->QMsm::getStateHandler(); + } +#endif // def Q_SPY + QMState const * childStateObj(QMState const * const parent) const noexcept { + return reinterpret_cast(this) + ->QMsm::childStateObj(parent); + } +}; // class QMActive + + +//============================================================================ +class QTimeEvt : public QP::QEvt { +private: + QTimeEvt * volatile m_next; + void * m_act; + QTimeEvtCtr volatile m_ctr; + QTimeEvtCtr m_interval; + std::uint8_t m_tickRate; + std::uint8_t m_flags; + +public: + static QTimeEvt timeEvtHead_[QF_MAX_TICK_RATE]; + +private: + friend class QXThread; + +public: + QTimeEvt( + QActive * const act, + QSignal const sig, + std::uint_fast8_t const tickRate = 0U) noexcept; + void armX( + std::uint32_t const nTicks, + std::uint32_t const interval = 0U) noexcept; + bool disarm() noexcept; + bool rearm(std::uint32_t const nTicks) noexcept; + bool wasDisarmed() noexcept; + void const * getAct() const noexcept { + return m_act; + } + QTimeEvtCtr getCtr() const noexcept { + return m_ctr; + } + QTimeEvtCtr getInterval() const noexcept { + return m_interval; + } + std::uint8_t getTickRate() const noexcept { + return m_tickRate; + } + static void tick( + std::uint_fast8_t const tickRate, + void const * const sender) noexcept; + +#ifdef Q_UTEST + static void tick1_( + std::uint_fast8_t const tickRate, + void const * const sender); +#endif // def Q_UTEST + +#ifdef QF_ISR_API + static void tickFromISR( + std::uint_fast8_t const tickRate, + void * par, + void const * sender) noexcept; +#endif // def QF_ISR_API + static bool noActive(std::uint_fast8_t const tickRate) noexcept; + QActive * toActive() noexcept { + return static_cast(m_act); + } + QTimeEvt * toTimeEvt() noexcept { + return static_cast(m_act); + } + +private: + QTimeEvt() noexcept; + QTimeEvt(QTimeEvt const & other) = delete; + QTimeEvt & operator=(QTimeEvt const & other) = delete; + QTimeEvt *expire_( + QTimeEvt * const prev_link, + QActive const * const act, + std::uint_fast8_t const tickRate) noexcept; +}; // class QTimeEvt + +//============================================================================ +class QTicker : public QP::QActive { +public: + explicit QTicker(std::uint_fast8_t const tickRate) noexcept; + void init( + void const * const e, + std::uint_fast8_t const qsId) override; + void init(std::uint_fast8_t const qsId) override { + this->init(nullptr, qsId); + } + void dispatch( + QEvt const * const e, + std::uint_fast8_t const qsId) override; + void trig_(void const * const sender) noexcept; +}; // class QTicker + +//============================================================================ +namespace QF { + +void init(); +void stop(); + +int_t run(); + +void onStartup(); +void onCleanup(); + +//! @deprecated +inline void psInit( + QSubscrList * const subscrSto, + enum_t const maxSignal) noexcept +{ + QActive::psInit(subscrSto, maxSignal); +} + +//! @deprecated +inline void publish_( + QEvt const * const e, + void const * const sender, + std::uint_fast8_t const qsId) noexcept +{ + QActive::publish_(e, sender, qsId); +} + +//! @deprecated +inline void tick( + std::uint_fast8_t const tickRate, + void const * const sender) noexcept +{ + QTimeEvt::tick(tickRate, sender); +} + +//! @deprecated +inline std::uint_fast16_t getQueueMin(std::uint_fast8_t const prio) noexcept { + return QActive::getQueueMin(prio); +} + +constexpr std::uint_fast16_t NO_MARGIN {0xFFFFU}; + +//============================================================================ +// QF dynamic memory facilities +void poolInit( + void * const poolSto, + std::uint_fast32_t const poolSize, + std::uint_fast16_t const evtSize) noexcept; + +std::uint_fast16_t poolGetMaxBlockSize() noexcept; +std::uint_fast16_t getPoolMin(std::uint_fast8_t const poolNum) noexcept; +QEvt * newX_( + std::uint_fast16_t const evtSize, + std::uint_fast16_t const margin, + enum_t const sig) noexcept; +void gc(QEvt const * const e) noexcept; +QEvt const * newRef_( + QEvt const * const e, + QEvt const * const evtRef) noexcept; + +void deleteRef_(QEvt const * const evtRef) noexcept; + +#ifndef QEVT_PAR_INIT + template + inline evtT_ * q_new(enum_t const sig) { + return static_cast( + QP::QF::newX_(sizeof(evtT_), QP::QF::NO_MARGIN, sig)); + } + template + inline evtT_ * q_new_x( + std::uint_fast16_t const margin, + enum_t const sig) + { + return static_cast(QP::QF::newX_(sizeof(evtT_), margin, sig)); + } +#else + template + inline evtT_ * q_new( + enum_t const sig, + Args... args) + { + evtT_ *e = static_cast( + QP::QF::newX_(sizeof(evtT_), QP::QF::NO_MARGIN, sig)); + e->init(args...); // e cannot be nullptr + return e; + } + template + inline evtT_ * q_new_x( + std::uint_fast16_t const margin, + enum_t const sig, + Args... args) + { + evtT_ *e = static_cast(QP::QF::newX_(sizeof(evtT_), margin, sig)); + if (e != nullptr) { + e->init(args...); + } + return e; + } +#endif // def QEVT_PAR_INIT + +template +inline void q_new_ref( + QP::QEvt const * const e, + evtT_ const *& evtRef) +{ + evtRef = static_cast(QP::QF::newRef_(e, evtRef)); +} + +template +inline void q_delete_ref(evtT_ const *& evtRef) { + QP::QF::deleteRef_(evtRef); + evtRef = nullptr; +} + +#ifdef QF_ISR_API +QEvt * newXfromISR_( + std::uint_fast16_t const evtSize, + std::uint_fast16_t const margin, + enum_t const sig) noexcept; +void gcFromISR(QEvt const * e) noexcept; +#endif // def QF_ISR_API + +} // namespace QF +} // namespace QP + +//============================================================================ +extern "C" { + +//${QF-extern-C::QF_onContextSw} ............................................. +#ifdef QF_ON_CONTEXT_SW +void QF_onContextSw( + QP::QActive * prev, + QP::QActive * next); +#endif // def QF_ON_CONTEXT_SW +} // extern "C" + +//============================================================================ +// QF base facilities + +#define Q_PRIO(prio_, pthre_) \ + (static_cast((prio_) | (pthre_) << 8U)) + +#ifndef QEVT_PAR_INIT + #define Q_NEW(evtT_, sig_) (QP::QF::q_new((sig_))) + #define Q_NEW_X(evtT_, margin_, sig_) (QP::QF::q_new_x((margin_), (sig_))) +#else + #define Q_NEW(evtT_, sig_, ...) (QP::QF::q_new((sig_), __VA_ARGS__)) + #define Q_NEW_X(evtT_, margin_, sig_, ...) (QP::QF::q_new_x((margin_), (sig_), __VA_ARGS__)) +#endif // QEVT_PAR_INIT + +#define Q_NEW_REF(evtRef_, evtT_) (QP::QF::q_new_ref(e, (evtRef_))) +#define Q_DELETE_REF(evtRef_) do { \ + QP::QF::deleteRef_((evtRef_)); \ + (evtRef_) = nullptr; \ +} while (false) + +#ifdef Q_SPY + #define PUBLISH(e_, sender_) \ + publish_((e_), (sender_), (sender_)->getPrio()) + #define POST(e_, sender_) post_((e_), QP::QF::NO_MARGIN, (sender_)) + #define POST_X(e_, margin_, sender_) \ + post_((e_), (margin_), (sender_)) + #define TICK_X(tickRate_, sender_) tick((tickRate_), (sender_)) + #define TRIG(sender_) trig_((sender_)) +#else + #define PUBLISH(e_, dummy) publish_((e_), nullptr, 0U) + #define POST(e_, dummy) post_((e_), QP::QF::NO_MARGIN, nullptr) + #define POST_X(e_, margin_, dummy) post_((e_), (margin_), nullptr) + #define TICK_X(tickRate_, dummy) tick((tickRate_), nullptr) + #define TRIG(sender_) trig_(nullptr) +#endif // ndef Q_SPY + +#define TICK(sender_) TICK_X(0U, (sender_)) + +#ifndef QF_CRIT_EXIT_NOP + #define QF_CRIT_EXIT_NOP() (static_cast(0)) +#endif // ndef QF_CRIT_EXIT_NOP + +//============================================================================ +// memory protection facilities + +#ifdef QF_MEM_ISOLATE + #error Memory isolation not supported in this QP edition, need SafeQP +#endif // def QF_MEM_ISOLATE + +#endif // QP_HPP_ diff --git a/src/qp_pkg.hpp b/src/qp_pkg.hpp new file mode 100644 index 0000000..0806a48 --- /dev/null +++ b/src/qp_pkg.hpp @@ -0,0 +1,81 @@ +//============================================================================ +// QP/C++ Real-Time Embedded Framework (RTEF) +// Version 8.0.2 +// +// Copyright (C) 2005 Quantum Leaps, LLC. All rights reserved. +// +// 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 (GPL) or under the terms of one of the closed- +// source Quantum Leaps commercial licenses. +// +// Redistributions in source code must retain this top-level comment block. +// Plagiarizing this software to sidestep the license obligations is illegal. +// +// NOTE: +// The GPL does NOT permit the incorporation of this code into proprietary +// programs. Please contact Quantum Leaps for commercial licensing options, +// which expressly supersede the GPL and are designed explicitly for +// closed-source distribution. +// +// Quantum Leaps contact information: +// +// +//============================================================================ +#ifndef QP_PKG_HPP_ +#define QP_PKG_HPP_ + +//============================================================================ +#define QF_CONST_CAST_(type_, ptr_) const_cast(ptr_) +#define Q_PTR2UINT_CAST_(ptr_) (reinterpret_cast(ptr_)) +#define QF_PTR_RANGE_(x_, min_, max_) (((min_) <= (x_)) && ((x_) <= (max_))) + +//============================================================================ +namespace QP { +namespace QF { + +class Attr { +public: + +#if (QF_MAX_EPOOL > 0U) + QF_EPOOL_TYPE_ ePool_[QF_MAX_EPOOL]; + std::uint_fast8_t maxPool_; +#else + std::uint8_t dummy; +#endif // (QF_MAX_EPOOL == 0U) +}; // class Attr + +extern QF::Attr priv_; + +void bzero_( + void * const start, + std::uint_fast16_t const len) noexcept; + +} // namespace QF + +//============================================================================ +// Bitmasks are for the QTimeEvt::flags attribute +constexpr std::uint8_t QTE_FLAG_IS_LINKED {1U << 7U}; +constexpr std::uint8_t QTE_FLAG_WAS_DISARMED {1U << 6U}; + +//============================================================================ +inline void QEvt_refCtr_inc_(QEvt const * const e) noexcept { + // NOTE: this function must be called inside a critical section + std::uint8_t rc = e->refCtr_ + 1U; + (QF_CONST_CAST_(QEvt*, e))->refCtr_ = rc; // cast away 'const' +} + +inline void QEvt_refCtr_dec_(QEvt const * const e) noexcept { + // NOTE: this function must be called inside a critical section + std::uint8_t rc = e->refCtr_ - 1U; + (QF_CONST_CAST_(QEvt*, e))->refCtr_ = rc; // cast away 'const' +} + +} // namespace QP + +#endif // QP_PKG_HPP_ diff --git a/src/qp_port.hpp b/src/qp_port.hpp new file mode 100644 index 0000000..ba0cb2e --- /dev/null +++ b/src/qp_port.hpp @@ -0,0 +1,191 @@ +//============================================================================ +// 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-09-07 +//! @version Last updated for: @ref qpcpp_7_3_0 +//! +//! @file +//! @brief QP/C++ port to FreeRTOS 10.x generic C++11 compiler + +#ifndef QP_PORT_HPP_ +#define QP_PORT_HPP_ + +#include // Exact-width types. C++11 Standard + +#include "qp_config.hpp" // external QP configuration + +// no-return function specifier (C++11 Standard) +#define Q_NORETURN [[ noreturn ]] void + +// QActive customization for FreeRTOS +#define QACTIVE_EQUEUE_TYPE QueueHandle_t +#define QACTIVE_OS_OBJ_TYPE StaticQueue_t +#if (QP_STATIC_THREAD == 0) +#define QACTIVE_THREAD_TYPE TaskHandle_t +#else // QP_STATIC_THREAD == 1 +#define QACTIVE_THREAD_TYPE StaticTask_t +#endif // QP_STATIC_THREAD + +// FreeRTOS requires the "FromISR" API in QP/C++ +#define QF_ISR_API 1 + +// QF interrupt disabling/enabling (task level) +#define QF_INT_DISABLE() taskDISABLE_INTERRUPTS() +#define QF_INT_ENABLE() taskENABLE_INTERRUPTS() + +// QF critical section for FreeRTOS (task level), see NOTE2 +#define QF_CRIT_STAT +#define QF_CRIT_ENTRY(dummy) portENTER_CRITICAL(&QF_esp32mux) +#define QF_CRIT_EXIT(dummy) portEXIT_CRITICAL(&QF_esp32mux) + +// include files ------------------------------------------------------------- +#include "freertos/FreeRTOS.h" // FreeRTOS master include file, see NOTE3 +#include "freertos/task.h" // FreeRTOS task management +#include "freertos/queue.h" // FreeRTOS queue management + +#include "qequeue.hpp" // QP event queue (for deferring events) +#include "qmpool.hpp" // QP memory pool (for event pools) +#include "qp.hpp" // QP platform-independent public interface + +/* global spinlock "mutex" for all critical sections in QF (see NOTE3) */ +extern PRIVILEGED_DATA portMUX_TYPE QF_esp32mux; + +#if defined( QP_PINNED_TO_CORE_0 ) + #define QP_CPU_NUM PRO_CPU_NUM +#elif defined( CONFIG_QP_PINNED_TO_CORE_1 ) + #define QP_CPU_NUM APP_CPU_NUM +#else + /* Defaults to APP_CPU */ + #define QP_CPU_NUM APP_CPU_NUM +#endif + +// the "FromISR" versions of the QF APIs, see NOTE3 +#define PUBLISH_FROM_ISR(e_, pxHigherPrioTaskWoken_, dummy) \ + publishFromISR((e_), (pxHigherPrioTaskWoken_), nullptr) + +#define POST_FROM_ISR(e_, pxHigherPrioTaskWoken_, dummy) \ + postFromISR((e_), QP::QF::NO_MARGIN, (pxHigherPrioTaskWoken_), \ + nullptr) + +#define POST_X_FROM_ISR(e_, margin_, pxHigherPrioTaskWoken_, dummy) \ + postFromISR((e_), (margin_), (pxHigherPrioTaskWoken_), nullptr) + +#define TICK_X_FROM_ISR(tickRate_, pxHigherPrioTaskWoken_, dummy) \ + tickFromISR((tickRate_), (pxHigherPrioTaskWoken_), nullptr) + +#define TICK_FROM_ISR(pxHigherPrioTaskWoken_, sender_) \ + TICK_X_FROM_ISR(0U, (pxHigherPrioTaskWoken_), (sender_)) + +#ifdef QEVT_DYN_CTOR + #define Q_NEW_FROM_ISR(evtT_, sig_, ...) \ + (new(QP::QF::newXfromISR_(sizeof(evtT_), QP::QF::NO_MARGIN, (sig_))) \ + evtT_(__VA_ARGS__)) + + #define Q_NEW_X_FROM_ISR(e_, evtT_, margin_, sig_, ...) do { \ + (e_) = static_cast( \ + QP::QF::newXfromISR_(static_cast( \ + sizeof(evtT_)), (margin_), (sig_))); \ + if ((e_) != nullptr) { \ + new((e_)) evtT_(__VA_ARGS__); \ + } \ + } while (false) + +#else // QEvt is a POD (Plain Old Datatype) + #define Q_NEW_FROM_ISR(evtT_, sig_) \ + (static_cast(QP::QF::newXfromISR_( \ + static_cast(sizeof(evtT_)), \ + QP::QF::NO_MARGIN, (sig_)))) + + #define Q_NEW_X_FROM_ISR(e_, evtT_, margin_, sig_) \ + ((e_) = static_cast( \ + QP::QF::newXfromISR_(sizeof(evtT_), (margin_), (sig_)))) +#endif + +namespace QP { + +enum FreeRTOS_TaskAttrs { + TASK_NAME_ATTR +}; + +} // namespace QP + +// FreeRTOS hooks prototypes (not provided by FreeRTOS) +extern "C" { +#if (configUSE_IDLE_HOOK > 0) + void vApplicationIdleHook(void); +#endif +#if (configUSE_TICK_HOOK > 0) + void vApplicationTickHook(void); +#endif +#if (configCHECK_FOR_STACK_OVERFLOW > 0) + void vApplicationStackOverflowHook(TaskHandle_t xTask, char *pcTaskName); +#endif +#if (configSUPPORT_STATIC_ALLOCATION > 0) + void vApplicationGetIdleTaskMemory(StaticTask_t **ppxIdleTaskTCBBuffer, + StackType_t **ppxIdleTaskStackBuffer, + uint32_t *pulIdleTaskStackSize); +#endif +} // extern "C" + +//============================================================================ +// interface used only inside QF, but not in applications + +#ifdef QP_IMPL + #define FREERTOS_TASK_PRIO(qp_prio_) \ + ((UBaseType_t)((qp_prio_) + tskIDLE_PRIORITY)) + + // FreeRTOS scheduler locking for QF_publish_() (task context only) + #define QF_SCHED_STAT_ + #define QF_SCHED_LOCK_(prio_) (vTaskSuspendAll()) + #define QF_SCHED_UNLOCK_() ((void)xTaskResumeAll()) + + // native QF event pool customization + #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_, qsId_) \ + ((e_) = static_cast((p_).get((m_), (qsId_)))) + #define QF_EPOOL_PUT_(p_, e_, qsId_) ((p_).put((e_), (qsId_))) + +#endif // ifdef QP_IMPL + +//============================================================================ +// NOTE2: +// The critical section definition applies only to the FreeRTOS "task level" +// APIs. The "FromISR" APIs are defined separately. +// +// NOTE3: +// The design of FreeRTOS requires using different APIs inside the ISRs +// (the "FromISR" variant) than at the task level. Accordingly, this port +// provides the "FromISR" variants for QP functions and "FROM_ISR" variants +// for QP macros to be used inside ISRs. ONLY THESE "FROM_ISR" VARIANTS +// ARE ALLOWED INSIDE ISRs AND CALLING THE TASK-LEVEL APIs IS AN ERROR. + +#endif // QP_PORT_HPP_ diff --git a/src/qpcpp.hpp b/src/qpcpp.hpp index 6027fd1..0fc1f91 100644 --- a/src/qpcpp.hpp +++ b/src/qpcpp.hpp @@ -1,54 +1,38 @@ -//$file${include::qpcpp.hpp} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv -// -// Model: qpcpp.qm -// File: ${include::qpcpp.hpp} -// -// This code has been generated by QM 5.2.5 . -// DO NOT EDIT THIS FILE MANUALLY. All your changes will be lost. +//============================================================================ +// QP/C++ Real-Time Embedded Framework (RTEF) +// Version 8.0.2 // -// This code is covered by the following QP license: -// License # : LicenseRef-QL-dual -// Issued to : Any user of the QP/C++ real-time embedded framework -// Framework(s) : qpcpp -// Support ends : 2023-12-31 -// License scope: +// Copyright (C) 2005 Quantum Leaps, LLC. All rights reserved. // -// 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: +// This software is dual-licensed under the terms of the open-source GNU +// General Public License (GPL) or under the terms of one of the closed- +// source Quantum Leaps commercial licenses. // // Redistributions in source code must retain this top-level comment block. // Plagiarizing this software to sidestep the license obligations is illegal. // -// Contact information: +// NOTE: +// The GPL does NOT permit the incorporation of this code into proprietary +// programs. Please contact Quantum Leaps for commercial licensing options, +// which expressly supersede the GPL and are designed explicitly for +// closed-source distribution. +// +// Quantum Leaps contact information: // // -// -//$endhead${include::qpcpp.hpp} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -//! @file -//! @brief QP/C++ public interface including backwards-compatibility layer -//! -//! @details -//! This header file must be included directly or indirectly -//! in all application modules (*.cpp files) that use QP/C++. - +//============================================================================ #ifndef QPCPP_HPP_ - #define QPCPP_HPP_ + //============================================================================ -#include "qf_port.hpp" // QF/C++ port from the port directory -#include "qassert.h" // QP assertions +#include "qp_port.hpp" // QP port from the port directory +#include "qsafe.h" // QP Functional Safety (FuSa) Subsystem #ifdef Q_SPY // software tracing enabled? #include "qs_port.hpp" // QS/C++ port from the port directory #else @@ -57,173 +41,76 @@ //============================================================================ #ifndef QP_API_VERSION + #define QP_API_VERSION 0 +#endif // QP_API_VERSION -//! Macro that specifies the backwards compatibility with the -//! QP/C++ API version. -//! -//! @details -//! For example, QP_API_VERSION=540 will cause generating the compatibility -//! layer with QP/C++ version 5.4.0 and newer, but not older than 5.4.0. -//! QP_API_VERSION=0 causes generation of the compatibility layer "from the -//! begining of time", which is the maximum backwards compatibilty. This is -//! the default.
-//! -//! Conversely, QP_API_VERSION=9999 means that no compatibility layer should -//! be generated. This setting is useful for checking if an application -//! complies with the latest QP/C++ API. -#define QP_API_VERSION 0 - -#endif // QP_API_VERSION - -// QP/C++ API compatibility layer... - -#if (QP_API_VERSION < 700) - -//! @deprecated use QP::QF::NO_MARGIN instead -#define QF_NO_MARGIN QP::QF::NO_MARGIN - -//! @deprecated plain 'char' is no longer forbidden in MISRA/AUTOSAR-C++ -using char_t = char; - -//! Static (compile-time) assertion. -//! -//! @deprecated -//! Use Q_ASSERT_STATIC() or better yet `static_assert()` instead. -//! -#define Q_ASSERT_COMPILE(expr_) Q_ASSERT_STATIC(expr_) - +// QP API compatibility layer... //============================================================================ -#if (QP_API_VERSION < 691) - -//! @deprecated enable the QS global filter -#define QS_FILTER_ON(rec_) QS_GLB_FILTER((rec_)) - -//! @deprecated disable the QS global filter -#define QS_FILTER_OFF(rec_) QS_GLB_FILTER(-(rec_)) +#if (QP_API_VERSION < 800) -//! @deprecated enable the QS local filter for SM (state machine) object -#define QS_FILTER_SM_OBJ(obj_) (static_cast(0)) +#define QM_SM_STATE_DECL(subm_, state_) error "submachines no longer supported" +#define qm_super_sub(sm_state_) error "submachines no longer supported" +#define qm_tran_ep(tatbl_) error "submachines no longer supported" +#define qm_tran_xp(xp_, tatbl_) error "submachines no longer supported" +#define qm_sm_exit(sm_state_) error "submachines no longer supported" -//! @deprecated enable the QS local filter for AO (active objects) -#define QS_FILTER_AO_OBJ(obj_) (static_cast(0)) - -//! @deprecated enable the QS local filter for MP (memory pool) object -#define QS_FILTER_MP_OBJ(obj_) (static_cast(0)) - -//! @deprecated enable the QS local filter for EQ (event queue) object -#define QS_FILTER_EQ_OBJ(obj_) (static_cast(0)) - -//! @deprecated enable the QS local filter for TE (time event) object -#define QS_FILTER_TE_OBJ(obj_) (static_cast(0)) - -#ifdef Q_SPY - -//! @deprecated local Filter for a generic application object `obj_`. -#define QS_FILTER_AP_OBJ(obj_) \ - (QP::QS::priv_.locFilter_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_(); \ - 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)) | 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_) (Q_UNUSED_PAR(0)) - -#endif // def Q_SPY +#ifdef QEVT_DYN_CTOR +//! @deprecated #QEVT_DYN_CTOR, please use #QEVT_PAR_INIT +#define QEVT_PAR_INIT +#endif -//============================================================================ -#if (QP_API_VERSION < 680) +//! @deprecated plain 'char' is no longer forbidden in MISRA-C++:2023 +using char_t = char; -//! @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_)). -#define Q_TRAN(target_) (me->tran(Q_STATE_CAST(target_))) +//! @deprecated assertion failure handler +//! Use Q_onError() instead. +#define Q_onAssert(module_, id_) Q_onError(module_, id_) -//! @deprecated -//! Macro to specify a tran-to-history in the "me->" impl-strategy. -//! Instead use the new impl-strategy without the "me->" pointer, where -//! you call tran_hist(Q_STATE_CAST(hist_)). -#define Q_TRAN_HIST(hist_) (me->tran_hist((hist_))) +//! @deprecated #Q_NASSERT preprocessor switch to disable QP assertions +#ifdef Q_NASSERT -//! @deprecated -//! Macro to specify the superstate in the "me->" impl-strategy. -//! Instead use the new impl-strategy without the "me->" pointer, where -//! you call super(state_)). -#define Q_SUPER(state_) (me->super(Q_STATE_CAST(state_))) + // #Q_UNSAFE now replaces the functionality of Q_NASSERT + #define Q_UNSAFE -//! @deprecated -//! Macro to call in a QM state entry-handler. Applicable only to QMSMs. -//! Instead use the new impl-strategy without the "me->" pointer, where -//! the QM-generated code calls qm_entry(Q_STATE_CAST(state_)). -#define QM_ENTRY(state_) (me->qm_entry((state_))) + //! @deprecated general purpose assertion with user-specified ID + //! number that **always** evaluates the `expr_` expression. + #define Q_ALLEGE_ID(id_, expr_) (static_cast(expr_)) -//! @deprecated -//! Macro to call in a QM state exit-handler. Applicable only to QMSMs. -//! Instead use the new impl-strategy without the "me->" pointer, where -//! the QM-generated code calls qm_exit(Q_STATE_CAST(state_)). -#define QM_EXIT(state_) (me->qm_exit((state_))) +#elif defined Q_UNSAFE -//! @deprecated -//! Macro to call in a QM submachine exit-handler. Applicable only to QMSMs. -//! Instead use the new impl-strategy without the "me->" pointer, where -//! the QM-generated code calls qm_sm_exit(Q_STATE_CAST(state_)). -#define QM_SM_EXIT(state_) (me->qm_sm_exit((state_))) + //! @deprecated general purpose assertion with user-specified ID + //! number that **always** evaluates the `expr_` expression. + #define Q_ALLEGE_ID(id_, expr_) (static_cast(expr_)) -//! @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_)). -#define QM_TRAN(tatbl_) (me->qm_tran((tatbl_))) +#else // QP FuSa Subsystem enabled -//! @deprecated -//! Macro to call in a QM state-handler when it executes an initial tran. -//! Instead use the new impl-strategy without the "me->" pointer, where -//! the QM-generated code calls qm_tran_init((tatbl_)). -#define QM_TRAN_INIT(tatbl_) (me->qm_tran_init((tatbl_))) + //! @deprecated 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) -//! @deprecated -//! Macro to call in a QM state-handler when it executes a tran-to-history. -//! Instead use the new impl-strategy without the "me->" pointer, where -//! the QM-generated code calls qm_tran_hist((history_), (tatbl_)). -#define QM_TRAN_HIST(history_, tatbl_) \ - (me->qm_tran_hist((history_), (tatbl_))) +#endif -//! @deprecated -//! Macro to call in a QM state-handler when it executes an initial tran. -//! Instead use the new impl-strategy without the "me->" pointer, where -//! the QM-generated code calls qm_tran_ep((tatbl_)). -#define QM_TRAN_EP(tatbl_) (me->qm_tran_ep((tatbl_))) +//! @deprecated 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 -//! Macro to call in a QM state-handler when it executes a tran-to-exit-point. -//! Instead use the new impl-strategy without the "me->" pointer, where -//! the QM-generated code calls qm_tran_xp((xp_), (tatbl_)). -#define QM_TRAN_XP(xp_, tatbl_) (me->qm_tran_xp((xp_), (tatbl_))) +//! Use Q_ASSERT_STATIC() or better yet `static_assert()` instead. +#define Q_ASSERT_COMPILE(expr_) Q_ASSERT_STATIC(expr_) -//! @deprecated -//! Designates the superstate of a given state in a subclass of QP::QMsm. -//! Instead use the new impl-strategy without the "me->" pointer, where -//! the QM-generated code calls qm_super_sub((state_)). -#define QM_SUPER_SUB(state_) (me->qm_super_sub((state_))) -#endif // QP_API_VERSION < 680 -#endif // QP_API_VERSION < 691 -#endif // QP_API_VERSION < 700 +#endif // QP_API_VERSION < 800 -#endif // QPCPP_HPP_ \ No newline at end of file +#endif // QPCPP_HPP_ diff --git a/src/qpset.hpp b/src/qpset.hpp deleted file mode 100644 index 67e0963..0000000 --- a/src/qpset.hpp +++ /dev/null @@ -1,197 +0,0 @@ -/// @file -/// @brief platform-independent priority sets of 8 or 64 elements. -/// @ingroup qf -/// @cond -///*************************************************************************** -/// Last updated for version 6.8.2 -/// Last updated on 2020-07-18 -/// -/// Q u a n t u m L e a P s -/// ------------------------ -/// Modern Embedded Software -/// -/// Copyright (C) 2005-2019 Quantum Leaps. All rights reserved. -/// -/// This program is open source software: you can redistribute it and/or -/// modify it under the terms of the GNU General Public License as published -/// by the Free Software Foundation, either version 3 of the License, or -/// (at your option) any later version. -/// -/// Alternatively, this program may be distributed and modified under the -/// terms of Quantum Leaps commercial licenses, which expressly supersede -/// the GNU General Public License and are specifically designed for -/// licensees interested in retaining the proprietary status of their code. -/// -/// This program is distributed in the hope that it will be useful, -/// but WITHOUT ANY WARRANTY; without even the implied warranty of -/// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -/// GNU General Public License for more details. -/// -/// You should have received a copy of the GNU General Public License -/// along with this program. If not, see . -/// -/// Contact information: -/// -/// -///*************************************************************************** -/// @endcond - -#ifndef QPSET_HPP -#define QPSET_HPP - -#ifndef QF_MAX_ACTIVE - // default value when NOT defined - #define QF_MAX_ACTIVE 32U -#endif - -namespace QP { - -#if (QF_MAX_ACTIVE < 1U) || (64U < QF_MAX_ACTIVE) - #error "QF_MAX_ACTIVE out of range. Valid range is 1U..64U" -#elif (QF_MAX_ACTIVE <= 8U) - using QPSetBits = std::uint8_t; -#elif (QF_MAX_ACTIVE <= 16U) - using QPSetBits = std::uint16_t; -#else - //! bitmask for the internal representation of QPSet elements - using QPSetBits = std::uint32_t; -#endif - -//**************************************************************************** -// Log-base-2 calculations ... -#ifndef QF_LOG2 - extern "C" std::uint_fast8_t QF_LOG2(QPSetBits x) noexcept; -#endif // QF_LOG2 - -//**************************************************************************** -#if (QF_MAX_ACTIVE <= 32) -//! Priority Set of up to 32 elements */ -/// -/// The priority set represents the set of active objects that are ready to -/// run and need to be considered by the scheduling algorithm. The set is -/// capable of storing up to 32 priority levels. QP::QPSet is specifically -/// declared as a POD (Plain Old Data) for ease of initialization and -/// interfacing with plain "C" code. -/// -struct QPSet { - - QPSetBits volatile m_bits; //!< bitmask with a bit for each element - - //! Makes the priority set @p me_ empty. - void setEmpty(void) noexcept { - m_bits = 0U; - } - - //! Evaluates to true if the priority set is empty - bool isEmpty(void) const noexcept { - return (m_bits == 0U); - } - - //! Evaluates to true if the priority set is not empty - bool notEmpty(void) const noexcept { - return (m_bits != 0U); - } - - //! the function evaluates to TRUE if the priority set has the element n. - bool hasElement(std::uint_fast8_t const n) const noexcept { - return (m_bits & (1U << (n - 1U))) != 0U; - } - - //! insert element @p n into the set, n = 1..QF_MAX_ACTIVE - void insert(std::uint_fast8_t const n) noexcept { - m_bits |= (1U << (n - 1U)); - } - - //! remove element @p n from the set, n = 1..QF_MAX_ACTIVE - /// @note - /// intentionally misspelled ("rmove") to avoid collision with - /// the C++ standard library facility "remove" - void rmove(std::uint_fast8_t const n) noexcept { - m_bits &= - static_cast(~(static_cast(1) << (n - 1U))); - } - - std::uint_fast8_t findMax(void) const noexcept { - return QF_LOG2(m_bits); - } -}; - -#else // QF_MAX_ACTIVE > 32U - -//! Priority Set of up to 64 elements -/// -/// The priority set represents the set of active objects that are ready to -/// run and need to be considered by the scheduling algorithm. The set is -/// capable of storing up to 64 priority levels. QP::QPSet is specifically -/// declared as a POD (Plain Old Data) for ease of initialization and -/// interfacing with plain "C" code. -/// -struct QPSet { - - //! Two 32-bit bitmasks with a bit for each element - std::uint32_t volatile m_bits[2]; - - //! Makes the priority set @p me_ empty. - void setEmpty(void) noexcept { - m_bits[0] = 0U; - m_bits[1] = 0U; - } - - //! Evaluates to true if the priority set is empty - // the following logic avoids UB in volatile access for MISRA compliantce - bool isEmpty(void) const noexcept { - return (m_bits[0] == 0U) ? (m_bits[1] == 0U) : false; - } - - //! Evaluates to true if the priority set is not empty - // the following logic avoids UB in volatile access for MISRA compliantce - bool notEmpty(void) const noexcept { - return (m_bits[0] != 0U) ? true : (m_bits[1] != 0U); - } - - //! the function evaluates to TRUE if the priority set has the element n. - bool hasElement(std::uint_fast8_t const n) const noexcept { - return (n <= 32U) - ? ((m_bits[0] & (static_cast(1) << (n - 1U))) - != 0U) - : ((m_bits[1] & (static_cast(1) << (n - 33U))) - != 0U); - } - - //! insert element @p n into the set, n = 1..64 - void insert(std::uint_fast8_t const n) noexcept { - if (n <= 32U) { - m_bits[0] |= (static_cast(1) << (n - 1U)); - } - else { - m_bits[1] |= (static_cast(1) << (n - 33U)); - } - } - - //! remove element @p n from the set, n = 1..64 - /// @note - /// intentionally misspelled ("rmove") to avoid collision with - /// the C++ standard library facility "remove" - void rmove(std::uint_fast8_t const n) noexcept { - if (n <= 32U) { - (m_bits[0] &= ~(static_cast(1) << (n - 1U))); - } - else { - (m_bits[1] &= ~(static_cast(1) << (n - 33U))); - } - } - - //! find the maximum element in the set, returns zero if the set is empty - std::uint_fast8_t findMax(void) const noexcept { - return (m_bits[1] != 0U) - ? (QF_LOG2(m_bits[1]) + 32U) - : (QF_LOG2(m_bits[0])); - } -}; - -#endif // QF_MAX_ACTIVE - -} // namespace QP - -#endif // QPSET_HPP - diff --git a/src/qs.cpp b/src/qs.cpp deleted file mode 100644 index d980f2f..0000000 --- a/src/qs.cpp +++ /dev/null @@ -1,1051 +0,0 @@ -//$file${src::qs::qs.cpp} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv -// -// Model: qpcpp.qm -// File: ${src::qs::qs.cpp} -// -// This code has been generated by QM 5.2.5 . -// DO NOT EDIT THIS FILE MANUALLY. All your changes will be lost. -// -// This code is covered by the following QP license: -// License # : LicenseRef-QL-dual -// Issued to : Any user of the QP/C++ real-time embedded framework -// Framework(s) : qpcpp -// Support ends : 2023-12-31 -// License scope: -// -// Copyright (C) 2005 Quantum Leaps, LLC . -// -// SPDX-License-Identifier: GPL-3.0-or-later OR LicenseRef-QL-commercial -// -// This software is dual-licensed under the terms of the open source GNU -// General Public License version 3 (or any later version), or alternatively, -// under the terms of one of the closed source Quantum Leaps commercial -// licenses. -// -// The terms of the open source GNU General Public License version 3 -// can be found at: -// -// The terms of the closed source Quantum Leaps commercial licenses -// can be found at: -// -// Redistributions in source code must retain this top-level comment block. -// Plagiarizing this software to sidestep the license obligations is illegal. -// -// Contact information: -// -// -// -//$endhead${src::qs::qs.cpp} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -//! @file -//! @brief QS software tracing services - -#define QP_IMPL // this is QP implementation -#include "qs_port.hpp" // QS port -#include "qs_pkg.hpp" // QS package-scope internal interface -#include "qstamp.hpp" // QP time-stamp -#include "qassert.h" // QP assertions - -// unnamed namespace for local definitions with internal linkage -namespace { -Q_DEFINE_THIS_MODULE("qs") -} // unnamed namespace - -//$skip${QP_VERSION} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv -// Check for the minimum required QP version -#if (QP_VERSION < 700U) || (QP_VERSION != ((QP_RELEASE^4294967295U) % 0x3E8U)) -#error qpcpp version 7.0.0 or higher required -#endif -//$endskip${QP_VERSION} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -//$define${QS::QS-tx} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv -namespace QP { -namespace QS { - -//${QS::QS-tx::priv_} ........................................................ -QStx priv_; - -//${QS::QS-tx::initBuf} ...................................................... -void initBuf( - std::uint8_t * const sto, - std::uint_fast32_t const stoSize) noexcept -{ - // the provided buffer must be at least 8 bytes long - Q_REQUIRE_ID(100, stoSize > 8U); - - // This function initializes all the internal QS variables, so that the - // tracing can start correctly even if the startup code fails to clear - // any uninitialized data (as is required by the C Standard). - // - glbFilter_(-static_cast(QS_ALL_RECORDS));// all global filters OFF - locFilter_(static_cast(QS_ALL_IDS)); // all local filters ON - priv_.locFilter_AP = nullptr; // deprecated "AP-filter" - - priv_.buf = sto; - priv_.end = static_cast(stoSize); - priv_.head = 0U; - priv_.tail = 0U; - priv_.used = 0U; - priv_.seq = 0U; - priv_.chksum = 0U; - priv_.critNest = 0U; - - // produce an empty record to "flush" the QS trace buffer - beginRec_(QS_REC_NUM_(QS_EMPTY)); - endRec_(); - - // produce the Target info QS record - target_info_pre_(0xFFU); - - // wait with flushing after successful initialization (see QS_INIT()) -} - -//${QS::QS-tx::getByte} ...................................................... -std::uint16_t getByte() noexcept { - std::uint16_t ret; - if (priv_.used == 0U) { - ret = QS_EOD; // set End-Of-Data - } - else { - std::uint8_t const * const buf_ = priv_.buf; // put in a temporary - QSCtr tail_ = priv_.tail; // put in a temporary (register) - - // the byte to return - ret = static_cast(buf_[tail_]); - - ++tail_; // advance the tail - if (tail_ == priv_.end) { // tail wrap around? - tail_ = 0U; - } - priv_.tail = tail_; // update the tail - priv_.used = (priv_.used - 1U); // one less byte used - } - return ret; // return the byte or EOD -} - -//${QS::QS-tx::getBlock} ..................................................... -std::uint8_t const * getBlock(std::uint16_t * const pNbytes) noexcept { - QSCtr const used_ = priv_.used; // put in a temporary (register) - std::uint8_t *buf_; - - // any bytes used in the ring buffer? - if (used_ == 0U) { - *pNbytes = 0U; // no bytes available right now - buf_ = nullptr; // no bytes available right now - } - else { - QSCtr tail_ = priv_.tail; // put in a temporary (register) - QSCtr const end_ = priv_.end; // put in a temporary (register) - QSCtr n = static_cast(end_ - tail_); - if (n > used_) { - n = used_; - } - if (n > static_cast(*pNbytes)) { - n = static_cast(*pNbytes); - } - *pNbytes = static_cast(n); // n-bytes available - buf_ = priv_.buf; - buf_ = &buf_[tail_]; // the bytes are at the tail - - priv_.used = (priv_.used - n); - tail_ += n; - if (tail_ == end_) { - tail_ = 0U; - } - priv_.tail = tail_; - } - return buf_; -} - -//${QS::QS-tx::glbFilter_} ................................................... -void glbFilter_(std::int_fast16_t const filter) noexcept { - bool const isRemove = (filter < 0); - std::uint16_t const rec = isRemove - ? static_cast(-filter) - : static_cast(filter); - switch (rec) { - case QS_ALL_RECORDS: { - std::uint8_t const tmp = (isRemove ? 0x00U : 0xFFU); - std::uint_fast8_t i; - // set all global filters (partially unrolled loop) - for (i = 0U; i < Q_DIM(priv_.glbFilter); i += 4U) { - priv_.glbFilter[i ] = tmp; - priv_.glbFilter[i + 1U] = tmp; - priv_.glbFilter[i + 2U] = tmp; - priv_.glbFilter[i + 3U] = tmp; - } - if (isRemove) { - // leave the "not maskable" filters enabled, - // see qs.h, Miscellaneous QS records (not maskable) - // - priv_.glbFilter[0] = 0x01U; - priv_.glbFilter[6] = 0x40U; - priv_.glbFilter[7] = 0xFCU; - priv_.glbFilter[8] = 0x7FU; - } - else { - // never turn the last 3 records on (0x7D, 0x7E, 0x7F) - priv_.glbFilter[15] = 0x1FU; - } - break; - } - case QS_SM_RECORDS: - if (isRemove) { - priv_.glbFilter[0] &= - static_cast(~0xFEU & 0xFFU); - priv_.glbFilter[1] &= - static_cast(~0x03U & 0xFFU); - priv_.glbFilter[6] &= - static_cast(~0x80U & 0xFFU); - priv_.glbFilter[7] &= - static_cast(~0x03U & 0xFFU); - } - else { - priv_.glbFilter[0] |= 0xFEU; - priv_.glbFilter[1] |= 0x03U; - priv_.glbFilter[6] |= 0x80U; - priv_.glbFilter[7] |= 0x03U; - } - break; - case QS_AO_RECORDS: - if (isRemove) { - priv_.glbFilter[1] &= - static_cast(~0xFCU & 0xFFU); - priv_.glbFilter[2] &= - static_cast(~0x07U & 0xFFU); - priv_.glbFilter[5] &= - static_cast(~0x20U & 0xFFU); - } - else { - priv_.glbFilter[1] |= 0xFCU; - priv_.glbFilter[2] |= 0x07U; - priv_.glbFilter[5] |= 0x20U; - } - break; - case QS_EQ_RECORDS: - if (isRemove) { - priv_.glbFilter[2] &= - static_cast(~0x78U & 0xFFU); - priv_.glbFilter[5] &= - static_cast(~0x40U & 0xFFU); - } - else { - priv_.glbFilter[2] |= 0x78U; - priv_.glbFilter[5] |= 0x40U; - } - break; - case QS_MP_RECORDS: - if (isRemove) { - priv_.glbFilter[3] &= - static_cast(~0x03U & 0xFFU); - priv_.glbFilter[5] &= - static_cast(~0x80U & 0xFFU); - } - else { - priv_.glbFilter[3] |= 0x03U; - priv_.glbFilter[5] |= 0x80U; - } - break; - case QS_QF_RECORDS: - if (isRemove) { - priv_.glbFilter[2] &= - static_cast(~0x80U & 0xFFU); - priv_.glbFilter[3] &= - static_cast(~0xFCU & 0xFFU); - priv_.glbFilter[4] &= - static_cast(~0xC0U & 0xFFU); - priv_.glbFilter[5] &= - static_cast(~0x1FU & 0xFFU); - } - else { - priv_.glbFilter[2] |= 0x80U; - priv_.glbFilter[3] |= 0xFCU; - priv_.glbFilter[4] |= 0xC0U; - priv_.glbFilter[5] |= 0x1FU; - } - break; - case QS_TE_RECORDS: - if (isRemove) { - priv_.glbFilter[4] &= - static_cast(~0x3FU & 0xFFU); - } - else { - priv_.glbFilter[4] |= 0x3FU; - } - break; - case QS_SC_RECORDS: - if (isRemove) { - priv_.glbFilter[6] &= - static_cast(~0x3FU & 0xFFU); - } - else { - priv_.glbFilter[6] |= 0x3FU; - } - break; - case QS_SEM_RECORDS: - if (isRemove) { - priv_.glbFilter[8] &= - static_cast(~0x80U & 0xFFU); - priv_.glbFilter[9] &= - static_cast(~0x07U & 0xFFU); - } - else { - priv_.glbFilter[8] |= 0x80U; - priv_.glbFilter[9] |= 0x07U; - } - break; - case QS_MTX_RECORDS: - if (isRemove) { - priv_.glbFilter[9] &= - static_cast(~0xF8U & 0xFFU); - priv_.glbFilter[10] &= - static_cast(~0x01U & 0xFFU); - } - else { - priv_.glbFilter[9] |= 0xF8U; - priv_.glbFilter[10] |= 0x01U; - } - break; - case QS_U0_RECORDS: - if (isRemove) { - priv_.glbFilter[12] &= - static_cast(~0xF0U & 0xFFU); - priv_.glbFilter[13] &= - static_cast(~0x01U & 0xFFU); - } - else { - priv_.glbFilter[12] |= 0xF0U; - priv_.glbFilter[13] |= 0x01U; - } - break; - case QS_U1_RECORDS: - if (isRemove) { - priv_.glbFilter[13] &= - static_cast(~0x3EU & 0xFFU); - } - else { - priv_.glbFilter[13] |= 0x3EU; - } - break; - case QS_U2_RECORDS: - if (isRemove) { - priv_.glbFilter[13] &= - static_cast(~0xC0U & 0xFFU); - priv_.glbFilter[14] &= - static_cast(~0x07U & 0xFFU); - } - else { - priv_.glbFilter[13] |= 0xC0U; - priv_.glbFilter[14] |= 0x07U; - } - break; - case QS_U3_RECORDS: - if (isRemove) { - priv_.glbFilter[14] &= - static_cast(~0xF8U & 0xFFU); - } - else { - priv_.glbFilter[14] |= 0xF8U; - } - break; - case QS_U4_RECORDS: - if (isRemove) { - priv_.glbFilter[15] &= 0x1FU; - } - else { - priv_.glbFilter[15] |= 0x1FU; - } - break; - case QS_UA_RECORDS: - if (isRemove) { - priv_.glbFilter[12] &= - static_cast(~0xF0U & 0xFFU); - priv_.glbFilter[13] = 0U; - priv_.glbFilter[14] = 0U; - priv_.glbFilter[15] &= - static_cast(~0x1FU & 0xFFU); - } - else { - priv_.glbFilter[12] |= 0xF0U; - priv_.glbFilter[13] |= 0xFFU; - priv_.glbFilter[14] |= 0xFFU; - priv_.glbFilter[15] |= 0x1FU; - } - break; - default: - // QS rec number can't exceed 0x7D, so no need for escaping - Q_ASSERT_ID(210, rec < 0x7DU); - - if (isRemove) { - priv_.glbFilter[rec >> 3U] - &= static_cast(~(1U << (rec & 7U)) & 0xFFU); - } - else { - priv_.glbFilter[rec >> 3U] - |= static_cast(1U << (rec & 7U)); - // never turn the last 3 records on (0x7D, 0x7E, 0x7F) - priv_.glbFilter[15] &= 0x1FU; - } - break; - } -} - -//${QS::QS-tx::locFilter_} ................................................... -void locFilter_(std::int_fast16_t const filter) noexcept { - bool const isRemove = (filter < 0); - std::uint16_t const qs_id = isRemove - ? static_cast(-filter) - : static_cast(filter); - std::uint8_t const tmp = (isRemove ? 0x00U : 0xFFU); - std::uint_fast8_t i; - switch (qs_id) { - case QS_ALL_IDS: - // set all global filters (partially unrolled loop) - for (i = 0U; i < Q_DIM(priv_.locFilter); i += 4U) { - priv_.locFilter[i ] = tmp; - priv_.locFilter[i + 1U] = tmp; - priv_.locFilter[i + 2U] = tmp; - priv_.locFilter[i + 3U] = tmp; - } - break; - case QS_AO_IDS: - for (i = 0U; i < 8U; i += 4U) { - priv_.locFilter[i ] = tmp; - priv_.locFilter[i + 1U] = tmp; - priv_.locFilter[i + 2U] = tmp; - priv_.locFilter[i + 3U] = tmp; - } - break; - case QS_EP_IDS: - i = 8U; - priv_.locFilter[i ] = tmp; - priv_.locFilter[i + 1U] = tmp; - break; - case QS_AP_IDS: - i = 12U; - priv_.locFilter[i ] = tmp; - priv_.locFilter[i + 1U] = tmp; - priv_.locFilter[i + 2U] = tmp; - priv_.locFilter[i + 3U] = tmp; - break; - default: - if (qs_id < 0x7FU) { - if (isRemove) { - priv_.locFilter[qs_id >> 3U] &= - static_cast( - ~(1U << (qs_id & 7U)) & 0xFFU); - } - else { - priv_.locFilter[qs_id >> 3U] - |= (1U << (qs_id & 7U)); - } - } - else { - Q_ERROR_ID(310); // incorrect qs_id - } - break; - } - priv_.locFilter[0] |= 0x01U; // leave QS_ID == 0 always on - -} - -//${QS::QS-tx::beginRec_} .................................................... -void beginRec_(std::uint_fast8_t const rec) noexcept { - std::uint8_t const b = priv_.seq + 1U; - std::uint8_t chksum_ = 0U; // reset the checksum - std::uint8_t * const buf_ = priv_.buf; // put in a temporary (register) - QSCtr head_ = priv_.head; // put in a temporary (register) - QSCtr const end_ = priv_.end; // put in a temporary (register) - - priv_.seq = b; // store the incremented sequence num - priv_.used = (priv_.used + 2U); // 2 bytes about to be added - - QS_INSERT_ESC_BYTE_(b) - - chksum_ += static_cast(rec); - QS_INSERT_BYTE_(static_cast(rec)) // no need for escaping - - priv_.head = head_; // save the head - priv_.chksum = chksum_; // save the checksum -} - -//${QS::QS-tx::endRec_} ...................................................... -void endRec_() noexcept { - std::uint8_t * const buf_ = priv_.buf; // put in a temporary (register) - QSCtr head_ = priv_.head; - QSCtr const end_ = priv_.end; - std::uint8_t b = priv_.chksum; - b ^= 0xFFU; // invert the bits in the checksum - - priv_.used = (priv_.used + 2U); // 2 bytes about to be added - - if ((b != QS_FRAME) && (b != QS_ESC)) { - QS_INSERT_BYTE_(b) - } - else { - QS_INSERT_BYTE_(QS_ESC) - QS_INSERT_BYTE_(b ^ QS_ESC_XOR) - priv_.used = (priv_.used + 1U); // account for the ESC byte - } - - QS_INSERT_BYTE_(QS_FRAME) // do not escape this QS_FRAME - - priv_.head = head_; // save the head - if (priv_.used > end_) { // overrun over the old data? - priv_.used = end_; // the whole buffer is used - priv_.tail = head_; // shift the tail to the old data - } -} - -//${QS::QS-tx::u8_raw_} ...................................................... -void u8_raw_(std::uint8_t const d) noexcept { - std::uint8_t chksum_ = priv_.chksum; // put in a temporary (register) - std::uint8_t * const buf_ = priv_.buf; // put in a temporary (register) - QSCtr head_ = priv_.head; // put in a temporary (register) - QSCtr const end_= priv_.end; // put in a temporary (register) - - priv_.used = (priv_.used + 1U); // 1 byte about to be added - QS_INSERT_ESC_BYTE_(d) - - priv_.head = head_; // save the head - priv_.chksum = chksum_; // save the checksum -} - -//${QS::QS-tx::u8u8_raw_} .................................................... -void u8u8_raw_( - std::uint8_t const d1, - std::uint8_t const d2) noexcept -{ - std::uint8_t chksum_ = priv_.chksum; // put in a temporary (register) - std::uint8_t * const buf_ = priv_.buf; // put in a temporary (register) - QSCtr head_ = priv_.head; // put in a temporary (register) - QSCtr const end_= priv_.end; // put in a temporary (register) - - priv_.used = (priv_.used + 2U); // 2 bytes about to be added - QS_INSERT_ESC_BYTE_(d1) - QS_INSERT_ESC_BYTE_(d2) - - priv_.head = head_; // save the head - priv_.chksum = chksum_; // save the checksum -} - -//${QS::QS-tx::u16_raw_} ..................................................... -void u16_raw_(std::uint16_t d) noexcept { - std::uint8_t b = static_cast(d); - std::uint8_t chksum_ = priv_.chksum; // put in a temporary (register) - std::uint8_t * const buf_ = priv_.buf; // put in a temporary (register) - QSCtr head_ = priv_.head; // put in a temporary (register) - QSCtr const end_= priv_.end; // put in a temporary (register) - - priv_.used = (priv_.used + 2U); // 2 bytes about to be added - - QS_INSERT_ESC_BYTE_(b) - - d >>= 8U; - b = static_cast(d); - QS_INSERT_ESC_BYTE_(b) - - priv_.head = head_; // save the head - priv_.chksum = chksum_; // save the checksum -} - -//${QS::QS-tx::u32_raw_} ..................................................... -void u32_raw_(std::uint32_t d) noexcept { - std::uint8_t chksum_ = priv_.chksum; // put in a temporary (register) - std::uint8_t * const buf_ = priv_.buf; // put in a temporary (register) - QSCtr head_ = priv_.head; // put in a temporary (register) - QSCtr const end_= priv_.end; // put in a temporary (register) - - priv_.used = (priv_.used + 4U); // 4 bytes about to be added - for (std::uint_fast8_t i = 4U; i != 0U; --i) { - std::uint8_t const b = static_cast(d); - QS_INSERT_ESC_BYTE_(b) - d >>= 8U; - } - - priv_.head = head_; // save the head - priv_.chksum = chksum_; // save the checksum -} - -//${QS::QS-tx::obj_raw_} ..................................................... -void obj_raw_(void const * const obj) noexcept { - #if (QS_OBJ_PTR_SIZE == 1U) - u8_raw_(reinterpret_cast(obj)); - #elif (QS_OBJ_PTR_SIZE == 2U) - u16_raw_(reinterpret_cast(obj)); - #elif (QS_OBJ_PTR_SIZE == 4U) - u32_raw_(reinterpret_cast(obj)); - #elif (QS_OBJ_PTR_SIZE == 8U) - u64_raw_(reinterpret_cast(obj)); - #else - u32_raw_(reinterpret_cast(obj)); - #endif -} - -//${QS::QS-tx::str_raw_} ..................................................... -void str_raw_(char const * s) noexcept { - std::uint8_t b = static_cast(*s); - std::uint8_t chksum_ = priv_.chksum; // put in a temporary (register) - std::uint8_t * const buf_ = priv_.buf; // put in a temporary (register) - QSCtr head_ = priv_.head; // put in a temporary (register) - QSCtr const end_= priv_.end; // put in a temporary (register) - QSCtr used_ = priv_.used; // put in a temporary (register) - - while (b != 0U) { - chksum_ += b; // update checksum - QS_INSERT_BYTE_(b) // ASCII characters don't need escaping - ++s; - b = static_cast(*s); - ++used_; - } - QS_INSERT_BYTE_(0U) // zero-terminate the string - ++used_; - - priv_.head = head_; // save the head - priv_.chksum = chksum_; // save the checksum - priv_.used = used_; // save # of used buffer space -} - -//${QS::QS-tx::u8_fmt_} ...................................................... -void u8_fmt_( - std::uint8_t const format, - std::uint8_t const d) noexcept -{ - std::uint8_t chksum_ = priv_.chksum; // put in a temporary (register) - std::uint8_t *const buf_ = priv_.buf; // put in a temporary (register) - QSCtr head_ = priv_.head; // put in a temporary (register) - QSCtr const end_= priv_.end; // put in a temporary (register) - - priv_.used = (priv_.used + 2U); // 2 bytes about to be added - - QS_INSERT_ESC_BYTE_(format) - QS_INSERT_ESC_BYTE_(d) - - priv_.head = head_; // save the head - priv_.chksum = chksum_; // save the checksum -} - -//${QS::QS-tx::u16_fmt_} ..................................................... -void u16_fmt_( - std::uint8_t format, - std::uint16_t d) noexcept -{ - std::uint8_t chksum_ = priv_.chksum; // put in a temporary (register) - std::uint8_t * const buf_ = priv_.buf; // put in a temporary (register) - QSCtr head_ = priv_.head; // put in a temporary (register) - QSCtr const end_= priv_.end; // put in a temporary (register) - - priv_.used = (priv_.used + 3U); // 3 bytes about to be added - - QS_INSERT_ESC_BYTE_(format) - - format = static_cast(d); - QS_INSERT_ESC_BYTE_(format) - - d >>= 8U; - format = static_cast(d); - QS_INSERT_ESC_BYTE_(format) - - priv_.head = head_; // save the head - priv_.chksum = chksum_; // save the checksum -} - -//${QS::QS-tx::u32_fmt_} ..................................................... -void u32_fmt_( - std::uint8_t format, - std::uint32_t d) noexcept -{ - std::uint8_t chksum_ = priv_.chksum; // put in a temporary (register) - std::uint8_t * const buf_= priv_.buf; // put in a temporary (register) - QSCtr head_ = priv_.head; // put in a temporary (register) - QSCtr const end_= priv_.end; // put in a temporary (register) - - priv_.used = (priv_.used + 5U); // 5 bytes about to be added - QS_INSERT_ESC_BYTE_(format) // insert the format byte - - for (std::uint_fast8_t i = 4U; i != 0U; --i) { - format = static_cast(d); - QS_INSERT_ESC_BYTE_(format) - d >>= 8U; - } - - priv_.head = head_; // save the head - priv_.chksum = chksum_; // save the checksum -} - -//${QS::QS-tx::str_fmt_} ..................................................... -void str_fmt_(char const * s) noexcept { - std::uint8_t b = static_cast(*s); - std::uint8_t chksum_ = static_cast( - priv_.chksum + static_cast(STR_T)); - std::uint8_t * const buf_= priv_.buf; // put in a temporary (register) - QSCtr head_ = priv_.head; // put in a temporary (register) - QSCtr const end_= priv_.end; // put in a temporary (register) - QSCtr used_ = priv_.used; // put in a temporary (register) - - used_ += 2U; // the format byte and the terminating-0 - - QS_INSERT_BYTE_(static_cast(STR_T)) - while (b != 0U) { - // ASCII characters don't need escaping - chksum_ += b; // update checksum - QS_INSERT_BYTE_(b) - ++s; - b = static_cast(*s); - ++used_; - } - QS_INSERT_BYTE_(0U) // zero-terminate the string - - priv_.head = head_; // save the head - priv_.chksum = chksum_; // save the checksum - priv_.used = used_; // save # of used buffer space -} - -//${QS::QS-tx::mem_fmt_} ..................................................... -void mem_fmt_( - std::uint8_t const * blk, - std::uint8_t size) noexcept -{ - std::uint8_t b = static_cast(MEM_T); - std::uint8_t chksum_ = priv_.chksum + b; - std::uint8_t * const buf_= priv_.buf; // put in a temporary (register) - QSCtr head_ = priv_.head; // put in a temporary (register) - QSCtr const end_= priv_.end; // put in a temporary (register) - - priv_.used = (priv_.used + size + 2U); // size+2 bytes to be added - - QS_INSERT_BYTE_(b) - QS_INSERT_ESC_BYTE_(size) - - // output the 'size' number of bytes - for (; size != 0U; --size) { - b = *blk; - QS_INSERT_ESC_BYTE_(b) - ++blk; - } - - priv_.head = head_; // save the head - priv_.chksum = chksum_; // save the checksum -} - -//${QS::QS-tx::sig_dict_pre_} ................................................ -void sig_dict_pre_( - enum_t const sig, - void const * const obj, - char const * const name) noexcept -{ - QS_CRIT_STAT_ - - QS_CRIT_E_(); - beginRec_(static_cast(QS_SIG_DICT)); - QS_SIG_PRE_(sig); - QS_OBJ_PRE_(obj); - QS_STR_PRE_((*name == '&') ? &name[1] : name); - endRec_(); - QS_CRIT_X_(); - onFlush(); -} - -//${QS::QS-tx::obj_dict_pre_} ................................................ -void obj_dict_pre_( - void const * const obj, - char const * const name) noexcept -{ - QS_CRIT_STAT_ - - QS_CRIT_E_(); - beginRec_(static_cast(QS_OBJ_DICT)); - QS_OBJ_PRE_(obj); - QS_STR_PRE_((*name == '&') ? &name[1] : name); - endRec_(); - QS_CRIT_X_(); - onFlush(); -} - -//${QS::QS-tx::obj_arr_dict_pre_} ............................................ -void obj_arr_dict_pre_( - void const * const obj, - std::uint_fast16_t const idx, - char const * const name) noexcept -{ - Q_REQUIRE_ID(400, idx < 1000U); - - // format idx into a char buffer as "xxx\0" - std::uint8_t idx_str[4]; - std::uint_fast16_t tmp = idx; - std::uint8_t i; - idx_str[3] = 0U; // zero-terminate - idx_str[2] = static_cast( - static_cast('0') + (tmp % 10U)); - tmp /= 10U; - idx_str[1] = static_cast( - static_cast('0') + (tmp % 10U)); - if (idx_str[1] == static_cast('0')) { - i = 2U; - } - else { - tmp /= 10U; - idx_str[0] = static_cast( - static_cast('0') + (tmp % 10U)); - if (idx_str[0] == static_cast('0')) { - i = 1U; - } - else { - i = 0U; - } - } - - QS_CRIT_STAT_ - std::uint8_t j = ((*name == '&') ? 1U : 0U); - - QS_CRIT_E_(); - beginRec_(static_cast(QS_OBJ_DICT)); - QS_OBJ_PRE_(obj); - for (; name[j] != '\0'; ++j) { - QS_U8_PRE_(name[j]); - if (name[j] == '[') { - ++j; - break; - } - } - for (; idx_str[i] != 0U; ++i) { - QS_U8_PRE_(idx_str[i]); - } - // skip chars until ']' - for (; name[j] != '\0'; ++j) { - if (name[j] == ']') { - break; - } - } - for (; name[j] != '\0'; ++j) { - QS_U8_PRE_(name[j]); - } - QS_U8_PRE_(0U); // zero-terminate - endRec_(); - QS_CRIT_X_(); - onFlush(); -} - -//${QS::QS-tx::fun_dict_pre_} ................................................ -void fun_dict_pre_( - QSpyFunPtr fun, - char const * const name) noexcept -{ - QS_CRIT_STAT_ - - QS_CRIT_E_(); - beginRec_(static_cast(QS_FUN_DICT)); - QS_FUN_PRE_(fun); - QS_STR_PRE_((*name == '&') ? &name[1] : name); - endRec_(); - QS_CRIT_X_(); - onFlush(); -} - -//${QS::QS-tx::usr_dict_pre_} ................................................ -void usr_dict_pre_( - enum_t const rec, - char const * const name) noexcept -{ - QS_CRIT_STAT_ - - QS_CRIT_E_(); - beginRec_(static_cast(QS_USR_DICT)); - QS_U8_PRE_(rec); - QS_STR_PRE_(name); - endRec_(); - QS_CRIT_X_(); - onFlush(); -} - -//${QS::QS-tx::enum_dict_pre_} ............................................... -void enum_dict_pre_( - enum_t const value, - std::uint8_t const group, - char const * const name) noexcept -{ - QS_CRIT_STAT_ - - QS_CRIT_E_(); - beginRec_(static_cast(QS_ENUM_DICT)); - QS_2U8_PRE_(static_cast(value), group); - QS_STR_PRE_(name); - endRec_(); - QS_CRIT_X_(); - onFlush(); -} - -//${QS::QS-tx::assertion_pre_} ............................................... -void assertion_pre_( - char const * const module, - int_t const loc, - std::uint32_t const delay) noexcept -{ - QS_BEGIN_NOCRIT_PRE_(QP::QS_ASSERT_FAIL, 0U) - QS_TIME_PRE_(); - QS_U16_PRE_(loc); - QS_STR_PRE_((module != nullptr) ? module : "?"); - QS_END_NOCRIT_PRE_() - QP::QS::onFlush(); - for (std::uint32_t volatile ctr = delay; ctr > 0U; ) { - ctr = (ctr - 1U); - } - QP::QS::onCleanup(); -} - -//${QS::QS-tx::crit_entry_pre_} .............................................. -void crit_entry_pre_() noexcept { - QS_BEGIN_NOCRIT_PRE_(QP::QS_QF_CRIT_ENTRY, 0U) - QS_TIME_PRE_(); - QS::priv_.critNest = (QS::priv_.critNest + 1U); - QS_U8_PRE_(QS::priv_.critNest); - QS_END_NOCRIT_PRE_() -} - -//${QS::QS-tx::crit_exit_pre_} ............................................... -void crit_exit_pre_() noexcept { - QS_BEGIN_NOCRIT_PRE_(QP::QS_QF_CRIT_EXIT, 0U) - QS_TIME_PRE_(); - QS_U8_PRE_(QS::priv_.critNest); - QS::priv_.critNest = (QS::priv_.critNest - 1U); - QS_END_NOCRIT_PRE_() -} - -//${QS::QS-tx::isr_entry_pre_} ............................................... -void isr_entry_pre_( - std::uint8_t const isrnest, - std::uint8_t const prio) noexcept -{ - QS_BEGIN_NOCRIT_PRE_(QP::QS_QF_ISR_ENTRY, 0U) - QS_TIME_PRE_(); - QS_U8_PRE_(isrnest); - QS_U8_PRE_(prio); - QS_END_NOCRIT_PRE_() -} - -//${QS::QS-tx::isr_exit_pre_} ................................................ -void isr_exit_pre_( - std::uint8_t const isrnest, - std::uint8_t const prio) noexcept -{ - QS_BEGIN_NOCRIT_PRE_(QP::QS_QF_ISR_EXIT, 0U) - QS_TIME_PRE_(); - QS_U8_PRE_(isrnest); - QS_U8_PRE_(prio); - QS_END_NOCRIT_PRE_() -} - -//${QS::QS-tx::target_info_pre_} ............................................. -void target_info_pre_(std::uint8_t const isReset) { - static constexpr std::uint8_t ZERO = static_cast('0'); - static std::uint8_t const * const TIME = - reinterpret_cast(&BUILD_TIME[0]); - static std::uint8_t const * const DATE = - reinterpret_cast(&BUILD_DATE[0]); - - QS::beginRec_(static_cast(QS_TARGET_INFO)); - QS::u8_raw_(isReset); - - static union { - std::uint16_t u16; - std::uint8_t u8[2]; - } endian_test; - endian_test.u16 = 0x0102U; - // big endian ? add the 0x8000U flag - QS_U16_PRE_(((endian_test.u8[0] == 0x01U) - ? (0x8000U | QP_VERSION) - : QP_VERSION)); // target endianness + version number - - // send the object sizes... - QS::u8_raw_(Q_SIGNAL_SIZE - | static_cast(QF_EVENT_SIZ_SIZE << 4U)); - - #ifdef QF_EQUEUE_CTR_SIZE - QS::u8_raw_(QF_EQUEUE_CTR_SIZE - | static_cast(QF_TIMEEVT_CTR_SIZE << 4U)); - #else - QS::u8_raw_(static_cast(QF_TIMEEVT_CTR_SIZE << 4U)); - #endif // ifdef QF_EQUEUE_CTR_SIZE - - #ifdef QF_MPOOL_CTR_SIZE - QS::u8_raw_(QF_MPOOL_SIZ_SIZE - | static_cast(QF_MPOOL_CTR_SIZE << 4U)); - #else - QS::u8_raw_(0U); - #endif // ifdef QF_MPOOL_CTR_SIZE - - QS::u8_raw_(QS_OBJ_PTR_SIZE | (QS_FUN_PTR_SIZE << 4U)); - QS::u8_raw_(QS_TIME_SIZE); - - // send the limits... - QS::u8_raw_(QF_MAX_ACTIVE); - QS::u8_raw_(QF_MAX_EPOOL | (QF_MAX_TICK_RATE << 4U)); - - // send the build time in three bytes (sec, min, hour)... - QS::u8_raw_((10U * (TIME[6] - ZERO)) + (TIME[7] - ZERO)); - QS::u8_raw_((10U * (TIME[3] - ZERO)) + (TIME[4] - ZERO)); - if (BUILD_TIME[0] == static_cast(' ')) { - QS::u8_raw_(TIME[1] - ZERO); - } - else { - QS::u8_raw_((10U * (TIME[0] - ZERO)) + (TIME[1] - ZERO)); - } - - // send the build date in three bytes (day, month, year) ... - if (BUILD_DATE[4] == static_cast(' ')) { - QS::u8_raw_(DATE[5] - ZERO); - } - else { - QS::u8_raw_((10U * (DATE[4] - ZERO)) + (DATE[5] - ZERO)); - } - // convert the 3-letter month to a number 1-12 ... - std::uint8_t b; - switch (DATE[0] + DATE[1] + DATE[2]) { - case 'J' + 'a' +'n': - b = 1U; - break; - case 'F' + 'e' + 'b': - b = 2U; - break; - case 'M' + 'a' +'r': - b = 3U; - break; - case 'A' + 'p' + 'r': - b = 4U; - break; - case 'M' + 'a' + 'y': - b = 5U; - break; - case 'J' + 'u' + 'n': - b = 6U; - break; - case 'J' + 'u' + 'l': - b = 7U; - break; - case 'A' + 'u' + 'g': - b = 8U; - break; - case 'S' + 'e' + 'p': - b = 9U; - break; - case 'O' + 'c' + 't': - b = 10U; - break; - case 'N' + 'o' + 'v': - b = 11U; - break; - case 'D' + 'e' + 'c': - b = 12U; - break; - default: - b = 0U; - break; - } - QS::u8_raw_(b); // store the month - QS::u8_raw_((10U * (DATE[9] - ZERO)) + (DATE[10] - ZERO)); - QS::endRec_(); - -} - -} // namespace QS -} // namespace QP -//$enddef${QS::QS-tx} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/src/qs.hpp b/src/qs.hpp deleted file mode 100644 index ef7ee1b..0000000 --- a/src/qs.hpp +++ /dev/null @@ -1,1613 +0,0 @@ -//$file${include::qs.hpp} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv -// -// Model: qpcpp.qm -// File: ${include::qs.hpp} -// -// This code has been generated by QM 5.2.5 . -// DO NOT EDIT THIS FILE MANUALLY. All your changes will be lost. -// -// This code is covered by the following QP license: -// License # : LicenseRef-QL-dual -// Issued to : Any user of the QP/C++ real-time embedded framework -// Framework(s) : qpcpp -// Support ends : 2023-12-31 -// License scope: -// -// Copyright (C) 2005 Quantum Leaps, LLC . -// -// SPDX-License-Identifier: GPL-3.0-or-later OR LicenseRef-QL-commercial -// -// This software is dual-licensed under the terms of the open source GNU -// General Public License version 3 (or any later version), or alternatively, -// under the terms of one of the closed source Quantum Leaps commercial -// licenses. -// -// The terms of the open source GNU General Public License version 3 -// can be found at: -// -// The terms of the closed source Quantum Leaps commercial licenses -// can be found at: -// -// Redistributions in source code must retain this top-level comment block. -// Plagiarizing this software to sidestep the license obligations is illegal. -// -// Contact information: -// -// -// -//$endhead${include::qs.hpp} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -//! @file -//! @brief QS/C++ platform-independent public interface. - -#ifndef QS_HPP_ -#define QS_HPP_ - -#ifndef Q_SPY - #error "Q_SPY must be defined to include qs.hpp" -#endif - -//============================================================================ -// Global namespace... -//$declare${QS-config} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv - -//${QS-config::QS_CTR_SIZE} .................................................. -#ifndef QS_CTR_SIZE -//! The size [bytes] of the internal QS buffer-counters. Valid values: 2U or 4U; -//! default 2U. -//! -//! @details -//! This macro can be defined in the QS port file (qs_port.hpp) to -//! configure the QS::QSCtr type. Here the macro is not defined so the -//! default of 2 byte is chosen. -#define QS_CTR_SIZE 2U -#endif // ndef QS_CTR_SIZE - -//${QS-config::QS_CTR_SIZE defined incorrectly,~} ............................ -#if (QS_CTR_SIZE != 2U) && (QS_CTR_SIZE != 4U) -#error QS_CTR_SIZE defined incorrectly, expected 2U or 4U; -#endif // (QS_CTR_SIZE != 2U) && (QS_CTR_SIZE != 4U) - -//${QS-config::QS_TIME_SIZE} ................................................. -#ifndef QS_TIME_SIZE -//! Size (in bytes) of the QS time stamp -//! -//! @details -//! This macro can be defined in the QS port file (qs_port.hpp) to configure -//! the QP::QSTimeCtr type. Valid values 1U, 2U, 4U. Default 4U. -#define QS_TIME_SIZE 4U -#endif // ndef QS_TIME_SIZE - -//${QS-config::QS_TIME_SIZE defined incorrectly~} ............................ -#if (QS_TIME_SIZE != 1U) && (QS_TIME_SIZE != 2U) && (QS_TIME_SIZE != 4U) -#error QS_TIME_SIZE defined incorrectly, expected 1U, 2U, or 4U; -#endif // (QS_TIME_SIZE != 1U) && (QS_TIME_SIZE != 2U) && (QS_TIME_SIZE != 4U) -//$enddecl${QS-config} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -//============================================================================ -//$declare${QS} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv -namespace QP { - -//${QS::QSCtr} ............................................................... -#if (QS_CTR_SIZE == 2U) -//! QS ring buffer counter and offset type -using QSCtr = std::uint16_t; -#endif // (QS_CTR_SIZE == 2U) - -//${QS::QSCtr} ............................................................... -#if (QS_CTR_SIZE == 4U) -using QSCtr = std::uint32_t; -#endif // (QS_CTR_SIZE == 4U) - -//${QS::QSTimeCtr} ........................................................... -#if (QS_TIME_SIZE == 4U) -//! QS time stamp type, which determines the dynamic range of QS time stamps -using QSTimeCtr = std::uint32_t; -#endif // (QS_TIME_SIZE == 4U) - -//${QS::QSTimeCtr} ........................................................... -#if (QS_TIME_SIZE == 2U) -using QSTimeCtr = std::uint16_t; -#endif // (QS_TIME_SIZE == 2U) - -//${QS::QSTimeCtr} ........................................................... -#if (QS_TIME_SIZE == 1U) -using QSTimeCtr = std::uint8_t; -#endif // (QS_TIME_SIZE == 1U) - -//${QS::QSFun} ............................................................... -#if (QS_FUN_PTR_SIZE == 4U) -//! QS function pointer type (for serializing function pointers) -using QSFun = std::uint32_t; -#endif // (QS_FUN_PTR_SIZE == 4U) - -//${QS::QSFun} ............................................................... -#if (QS_FUN_PTR_SIZE == 8U) -using QSFun = std::uint64_t; -#endif // (QS_FUN_PTR_SIZE == 8U) - -//${QS::QSFun} ............................................................... -#if (QS_FUN_PTR_SIZE == 2U) -using QSFun = std::uint16_t; -#endif // (QS_FUN_PTR_SIZE == 2U) - -//${QS::QSFun} ............................................................... -#if (QS_FUN_PTR_SIZE == 1U) -using QSFun = std::uint8_t; -#endif // (QS_FUN_PTR_SIZE == 1U) - -//${QS::QSpyPre} ............................................................. -//! QS pre-defined record types (TX channel) -//! -//! @details -//! This enumeration specifies the record types used in the QP components. -//! You can specify your own record types starting from QP::QS_USER offset. -//! Currently, the maximum of all records cannot exceed 125. -//! -//! @note -//! The QS records labeled as "not maskable" are always enabled and cannot -//! be turend off with the QS_GLB_FILTER() macro. Other QS trace records -//! can be disabled by means of the "global filters" -//! -//! @sa QS_GLB_FILTER() macro -enum QSpyPre : std::int8_t { - // [0] QS session (not maskable) - QS_EMPTY, //!< QS record for cleanly starting a session - - // [1] SM records - QS_QEP_STATE_ENTRY, //!< a state was entered - QS_QEP_STATE_EXIT, //!< a state was exited - QS_QEP_STATE_INIT, //!< an initial transition was taken in a state - QS_QEP_INIT_TRAN, //!< the top-most initial transition was taken - QS_QEP_INTERN_TRAN, //!< an internal transition was taken - QS_QEP_TRAN, //!< a regular transition was taken - QS_QEP_IGNORED, //!< an event was ignored (silently discarded) - QS_QEP_DISPATCH, //!< an event was dispatched (begin of RTC step) - QS_QEP_UNHANDLED, //!< an event was unhandled due to a guard - - // [10] Active Object (AO) records - QS_QF_ACTIVE_DEFER, //!< AO deferred an event - QS_QF_ACTIVE_RECALL, //!< AO recalled an event - QS_QF_ACTIVE_SUBSCRIBE, //!< an AO subscribed to an event - QS_QF_ACTIVE_UNSUBSCRIBE,//!< an AO unsubscribed to an event - QS_QF_ACTIVE_POST, //!< an event was posted (FIFO) directly to AO - QS_QF_ACTIVE_POST_LIFO, //!< an event was posted (LIFO) directly to AO - QS_QF_ACTIVE_GET, //!< AO got an event and its queue is not empty - QS_QF_ACTIVE_GET_LAST,//!< AO got an event and its queue is empty - QS_QF_ACTIVE_RECALL_ATTEMPT, //!< AO attempted to recall an event - - // [19] Event Queue (EQ) records - QS_QF_EQUEUE_POST, //!< an event was posted (FIFO) to a raw queue - QS_QF_EQUEUE_POST_LIFO, //!< an event was posted (LIFO) to a raw queue - QS_QF_EQUEUE_GET, //!< get an event and queue still not empty - QS_QF_EQUEUE_GET_LAST,//!< get the last event from the queue - - // [23] Framework (QF) records - QS_QF_NEW_ATTEMPT, //!< an attempt to allocate an event failed - - // [24] Memory Pool (MP) records - QS_QF_MPOOL_GET, //!< a memory block was removed from memory pool - QS_QF_MPOOL_PUT, //!< a memory block was returned to memory pool - - // [26] Additional Framework (QF) records - QS_QF_PUBLISH, //!< an event was published to active objects - QS_QF_NEW_REF, //!< new event reference was created - QS_QF_NEW, //!< new event was created - QS_QF_GC_ATTEMPT, //!< garbage collection attempt - QS_QF_GC, //!< garbage collection - QS_QF_TICK, //!< QTimeEvt::tick_() was called - - // [32] Time Event (TE) records - QS_QF_TIMEEVT_ARM, //!< a time event was armed - QS_QF_TIMEEVT_AUTO_DISARM, //!< a time event expired and was disarmed - QS_QF_TIMEEVT_DISARM_ATTEMPT,//!< attempt to disarm a disarmed QTimeEvt - QS_QF_TIMEEVT_DISARM, //!< true disarming of an armed time event - QS_QF_TIMEEVT_REARM, //!< rearming of a time event - QS_QF_TIMEEVT_POST, //!< a time event posted itself directly to an AO - - // [38] Additional Framework (QF) records - QS_QF_DELETE_REF, //!< an event reference is about to be deleted - QS_QF_CRIT_ENTRY, //!< critical section was entered - QS_QF_CRIT_EXIT, //!< critical section was exited - QS_QF_ISR_ENTRY, //!< an ISR was entered - QS_QF_ISR_EXIT, //!< an ISR was exited - QS_QF_INT_DISABLE, //!< interrupts were disabled - QS_QF_INT_ENABLE, //!< interrupts were enabled - - // [45] Additional Active Object (AO) records - QS_QF_ACTIVE_POST_ATTEMPT, //!< attempt to post an evt to AO failed - - // [46] Additional Event Queue (EQ) records - QS_QF_EQUEUE_POST_ATTEMPT, //!< attempt to post evt to QEQueue failed - - // [47] Additional Memory Pool (MP) records - QS_QF_MPOOL_GET_ATTEMPT, //!< attempt to get a memory block failed - - // [48] Scheduler (SC) records - QS_SCHED_PREEMPT, //!< scheduler asynchronously preempted a task - QS_SCHED_RESTORE, //!< scheduler restored preempted task - QS_SCHED_LOCK, //!< scheduler was locked - QS_SCHED_UNLOCK, //!< scheduler was unlocked - QS_SCHED_NEXT, //!< scheduler found next task to execute - QS_SCHED_IDLE, //!< scheduler became idle - - // [54] Miscellaneous QS records (not maskable) - QS_ENUM_DICT, //!< enumeration dictionary entry - - // [55] Additional QEP records - QS_QEP_TRAN_HIST, //!< a tran to history was taken - QS_QEP_TRAN_EP, //!< a tran to entry point into a submachine - QS_QEP_TRAN_XP, //!< a tran to exit point out of a submachine - - // [58] Miscellaneous QS records (not maskable) - QS_TEST_PAUSED, //!< test has been paused - QS_TEST_PROBE_GET, //!< reports that Test-Probe has been used - QS_SIG_DICT, //!< signal dictionary entry - QS_OBJ_DICT, //!< object dictionary entry - QS_FUN_DICT, //!< function dictionary entry - QS_USR_DICT, //!< user QS record dictionary entry - QS_TARGET_INFO, //!< reports the Target information - QS_TARGET_DONE, //!< reports completion of a user callback - QS_RX_STATUS, //!< reports QS data receive status - QS_QUERY_DATA, //!< reports the data from "current object" query - QS_PEEK_DATA, //!< reports the data from the PEEK query - QS_ASSERT_FAIL, //!< assertion failed in the code - QS_QF_RUN, //!< QF_run() was entered - - // [71] Semaphore (SEM) records - QS_SEM_TAKE, //!< a semaphore was taken by a thread - QS_SEM_BLOCK, //!< a semaphore blocked a thread - QS_SEM_SIGNAL, //!< a semaphore was signaled - QS_SEM_BLOCK_ATTEMPT, //!< a semaphore blocked was attempted - - // [75] Mutex (MTX) records - QS_MTX_LOCK, //!< a mutex was locked - QS_MTX_BLOCK, //!< a mutex blocked a thread - QS_MTX_UNLOCK, //!< a mutex was unlocked - QS_MTX_LOCK_ATTEMPT, //!< a mutex lock was attempted - QS_MTX_BLOCK_ATTEMPT, //!< a mutex blocking was attempted - QS_MTX_UNLOCK_ATTEMPT,//!< a mutex unlock was attempted - - // [81] - QS_PRE_MAX //!< the number of predefined signals -}; - -//${QS::QSpyGroups} .......................................................... -//! QS record groups for QS_GLB_FILTER() -enum QSpyGroups : std::int16_t { - QS_ALL_RECORDS = static_cast(0xF0U), //!< all QS records - QS_SM_RECORDS, //!< State Machine QS records - QS_AO_RECORDS, //!< Active Object QS records - QS_EQ_RECORDS, //!< Event Queues QS records - QS_MP_RECORDS, //!< Memory Pools QS records - QS_TE_RECORDS, //!< Time Events QS records - QS_QF_RECORDS, //!< QF QS records - QS_SC_RECORDS, //!< Scheduler QS records - QS_SEM_RECORDS, //!< Semaphore QS records - QS_MTX_RECORDS, //!< Mutex QS records - QS_U0_RECORDS, //!< User Group 100-104 records - QS_U1_RECORDS, //!< User Group 105-109 records - QS_U2_RECORDS, //!< User Group 110-114 records - QS_U3_RECORDS, //!< User Group 115-119 records - QS_U4_RECORDS, //!< User Group 120-124 records - QS_UA_RECORDS //!< All User records -}; - -//${QS::QSpyUserOffsets} ..................................................... -//! QS user record group offsets for QS_GLB_FILTER() -enum QSpyUserOffsets : std::int16_t { - QS_USER = 100, //!< the first record available to QS users - QS_USER0 = QS_USER, //!< offset for User Group 0 - QS_USER1 = QS_USER0 + 5, //!< offset of Group 1 - QS_USER2 = QS_USER1 + 5, //!< offset of Group 2 - QS_USER3 = QS_USER2 + 5, //!< offset of Group 3 - QS_USER4 = QS_USER3 + 5 //!< offset of Group 4 -}; - -//${QS::QSpyIdOffsets} ....................................................... -//! QS ID offsets for QS_LOC_FILTER() -enum QSpyIdOffsets : std::int16_t { - QS_AO_ID = 0, //!< offset for AO priorities - QS_EP_ID = 64, //!< offset for event-pool IDs - QS_EQ_ID = 80, //!< offset for event-queue IDs - QS_AP_ID = 96 //!< offset for Appl-spec IDs -}; - -//${QS::QSpyIdGroups} ........................................................ -//! QS ID groups for QS_LOC_FILTER() -enum QSpyIdGroups : std::int16_t { - QS_ALL_IDS = 0xF0, //!< all QS IDs - QS_AO_IDS = 0x80 + QS_AO_ID, //!< AO IDs (priorities) - QS_EP_IDS = 0x80 + QS_EP_ID, //!< event-pool IDs - QS_EQ_IDS = 0x80 + QS_EQ_ID, //!< event-queue IDs - QS_AP_IDS = 0x80 + QS_AP_ID //!< Application-specific IDs -}; - -//${QS::QSpyFunPtr} .......................................................... -//! function pointer type for fun_dict_pre_() -using QSpyFunPtr = void (*)(); - -//${QS::QSpyId} .............................................................. -//! QS ID type for applying local filtering -struct QSpyId { - std::uint8_t m_prio; //!< "priority" (qs_id) for the QS "local filter" - - //! get the "priority" (qs_id) from the QSpyId opbject - std::uint_fast8_t getPrio() const noexcept { - return static_cast(m_prio); - } -}; -namespace QS { - -//${QS::QS-tx::QStx} ......................................................... -//! QS software tracing, output QS-TX -class QStx { -public: - - //! global on/off QS filter - std::uint8_t glbFilter[16]; - - //! local on/off QS filter - std::uint8_t locFilter[16]; - - //! old local QS filter - // @deprecated - void const * locFilter_AP; - - //! pointer to the start of the QS-TX ring buffer - std::uint8_t * buf; - - //! offset of the end of the ring buffer - QSCtr end; - - //! offset to where next byte will be inserted - QSCtr volatile head; - - //! offset of where next record will be extracted - QSCtr volatile tail; - - //! number of bytes currently in the ring buffer - QSCtr volatile used; - - //! sequence number of the last inserted QS record - std::uint8_t volatile seq; - - //! checksum of the currently inserted record - std::uint8_t volatile chksum; - - //! critical section nesting level - std::uint8_t volatile critNest; - - //! flags for internal use - std::uint8_t flags; -}; // class QStx - -//${QS::QS-tx::QSType} ....................................................... -//! Enumerates data elements for app-specific trace records -enum QSType : std::uint8_t { - I8_ENUM_T, //!< signed 8-bit integer or enum format - U8_T, //!< unsigned 8-bit integer format - I16_T, //!< signed 16-bit integer format - U16_T, //!< unsigned 16-bit integer format - I32_T, //!< signed 32-bit integer format - U32_T, //!< unsigned 32-bit integer format - F32_T, //!< 32-bit floating point format - F64_T, //!< 64-bit floating point format - STR_T, //!< zero-terminated ASCII string format - MEM_T, //!< up to 255-bytes memory block format - SIG_T, //!< event signal format - OBJ_T, //!< object pointer format - FUN_T, //!< function pointer format - I64_T, //!< signed 64-bit integer format - U64_T //!< unsigned 64-bit integer format -}; - -//${QS::QS-tx::priv_} ........................................................ -//! the only instance of the QS-TX object (Singleton) -extern QStx priv_; - -//${QS::QS-tx::force_cast} ................................................... -//! template for forcing cast of member functions for function -//! dictionaries and test probes. -//! -//! @tparam T_OUT type of the returned representation -//! @tparam T_IN type of the provided representation -//! -//! @returns the binary representation of `T_IN` as `T_OUT` -template -inline T_OUT force_cast(T_IN in) { - union TCast { - T_IN in; - T_OUT out; - } u = { in }; - return u.out; -} - -//${QS::QS-tx::initBuf} ...................................................... -//! Initialize the QS data buffer -//! -//! @details -//! This function should be called from QP::QS::onStartup() to provide -//! QS with the data buffer. The first argument `sto[]` is the address -//! of the memory block, and the second argument `stoSize` is the size -//! of this block [in bytes]. Currently the size of the QS buffer cannot -//! exceed 64KB. -//! -//! @note -//! QS can work with quite small data buffers, but you will start losing -//! data if the buffer is too small for the bursts of tracing activity. -//! The right size of the buffer depends on the data production rate and -//! the data output rate. QS offers flexible filtering to reduce the data -//! production rate. -//! -//! @note -//! If the data output rate cannot keep up with the production rate, -//! QS will start overwriting the older data with newer data. This is -//! consistent with the "last-is-best" QS policy. The record sequence -//! counters and check sums on each record allow the QSPY host utility -//! to easily detect any data loss. -void initBuf( - std::uint8_t * const sto, - std::uint_fast32_t const stoSize) noexcept; - -//${QS::QS-tx::getByte} ...................................................... -//! Byte-oriented interface to the QS data buffer -//! -//! @details -//! This function delivers one byte at a time from the QS data buffer. -//! -//! @returns -//! the byte in the least-significant 8-bits of the 16-bit return -//! value if the byte is available. If no more data is available at the -//! time, the function returns ::QS_EOD (End-Of-Data). -//! -//! @note -//! QS::getByte() is NOT protected with a critical section. -std::uint16_t getByte() noexcept; - -//${QS::QS-tx::getBlock} ..................................................... -//! Block-oriented interface to the QS data buffer -//! -//! @details -//! This function delivers a contiguous block of data from the QS data -//! buffer. The function returns the pointer to the beginning of the -//! block, and writes the number of bytes in the block to the location -//! pointed to by `pNbytes`. The argument `pNbytes` is also used as -//! input to provide the maximum size of the data block that the caller -//! can accept. -//! -//! @returns -//! if data is available, the function returns pointer to the -//! contiguous block of data and sets the value pointed to by `pNbytes` -//! to the # available bytes. If data is available at the time the -//! function is called, the function returns NULL pointer and sets the -//! value pointed to by `pNbytes` to zero. -//! -//! @note -//! Only the NULL return from QP::QS::getBlock() indicates that the QS -//! buffer is empty at the time of the call. The non-NULL return often -//! means that the block is at the end of the buffer and you need to call -//! QP::QS::getBlock() again to obtain the rest of the data that -//! "wrapped around" to the beginning of the QS data buffer. -//! -//! @note QP::QS::getBlock() is __not__ protected with a critical section. -std::uint8_t const * getBlock(std::uint16_t * const pNbytes) noexcept; - -//${QS::QS-tx::glbFilter_} ................................................... -//! Set/clear the global Filter for a given QS record -//! or a group of records -//! -//! @details -//! This function sets up the QS filter to enable record types specified -//! in the `filter` parameter. The value #QS_ALL_RECORDS specifies to -//! filter-in all records. This function should be called indirectly -//! through the macro QS_GLB_FILTER() -//! -//! @param[in] filter the QS record-d or group to enable in the filter, -//! if positive or disable, if negative. The record-id -//! numbers must be in the range -127..127. -//! @note -//! Filtering based on the record-type is only the first layer of -//! filtering. The second layer is based on the object-type. Both filter -//! layers must be enabled for the QS record to be inserted in the -//! QS buffer. -//! -//! @sa QP::QS::locFilter_() -void glbFilter_(std::int_fast16_t const filter) noexcept; - -//${QS::QS-tx::locFilter_} ................................................... -//! Set/clear the local Filter for a given object-id -//! or a group of object-ids -//! -//! @details -//! This function sets up the local QS filter to enable or disable the -//! given QS object-id or a group of object-ids @a filter. -//! This function should be called indirectly through the macro -//! QS_LOC_FILTER() -//! -//! @param[in] filter the QS object-id or group to enable in the filter, -//! if positive or disable, if negative. The qs_id numbers -//! must be in the range 1..127. -//! @note -//! Filtering based on the object-id (local filter) is the second layer -//! of filtering. The first layer is based on the QS record-type (global -//! filter). Both filter layers must be enabled for the QS record to be -//! inserted into the QS buffer. -//! -//! @sa QP::QS::glbFilter_() -void locFilter_(std::int_fast16_t const filter) noexcept; - -//${QS::QS-tx::doOutput} ..................................................... -//! Perform the QS-TX output (implemented in some QS ports) -void doOutput(); - -//${QS::QS-tx::beginRec_} .................................................... -//! Mark the begin of a QS record `rec` -//! -//! @details -//! This function must be called at the beginning of each QS record. -//! This function should be called indirectly through the macro -//! QS_BEGIN_ID(), or QS_BEGIN_NOCRIT(), depending if it's called in -//! a normal code or from a critical section. -void beginRec_(std::uint_fast8_t const rec) noexcept; - -//${QS::QS-tx::endRec_} ...................................................... -//! Mark the end of a QS record `rec` -//! -//! @details -//! This function must be called at the end of each QS record. -//! This function should be called indirectly through the macro QS_END(), -//! or QS_END_NOCRIT(), depending if it's called in a normal code or from -//! a critical section. -void endRec_() noexcept; - -//${QS::QS-tx::u8_raw_} ...................................................... -//! output std::uint8_t data element without format information -void u8_raw_(std::uint8_t const d) noexcept; - -//${QS::QS-tx::u8u8_raw_} .................................................... -//! output two std::uint8_t data elements without format information -void u8u8_raw_( - std::uint8_t const d1, - std::uint8_t const d2) noexcept; - -//${QS::QS-tx::u16_raw_} ..................................................... -//! Output std::uint16_t data element without format information -void u16_raw_(std::uint16_t d) noexcept; - -//${QS::QS-tx::u32_raw_} ..................................................... -//! Output std::uint32_t data element without format information -void u32_raw_(std::uint32_t d) noexcept; - -//${QS::QS-tx::obj_raw_} ..................................................... -//! Output object pointer data element without format information -void obj_raw_(void const * const obj) noexcept; - -//${QS::QS-tx::str_raw_} ..................................................... -//! Output zero-terminated ASCII string element without format information -void str_raw_(char const * s) noexcept; - -//${QS::QS-tx::u8_fmt_} ...................................................... -//! Output std::uint8_t data element with format information -//! @sa QS_U8(), QS_I8() -void u8_fmt_( - std::uint8_t const format, - std::uint8_t const d) noexcept; - -//${QS::QS-tx::u16_fmt_} ..................................................... -//! Output std::uint16_t data element with format information -//! @sa QS_U16(), QS_I16() -void u16_fmt_( - std::uint8_t format, - std::uint16_t d) noexcept; - -//${QS::QS-tx::u32_fmt_} ..................................................... -//! Output std::uint32_t data element with format information -//! @sa QS_U32(), QS_I32() -void u32_fmt_( - std::uint8_t format, - std::uint32_t d) noexcept; - -//${QS::QS-tx::str_fmt_} ..................................................... -//! Output zero-terminated ASCII string element with format information -//! @sa QS_STR() -void str_fmt_(char const * s) noexcept; - -//${QS::QS-tx::mem_fmt_} ..................................................... -//! Output memory block of up to 255-bytes with format information -//! @sa QS_MEM() -void mem_fmt_( - std::uint8_t const * blk, - std::uint8_t size) noexcept; - -//${QS::QS-tx::sig_dict_pre_} ................................................ -//! Output signal dictionary record -//! @sa QS_SIG_DICTIONARY() -void sig_dict_pre_( - enum_t const sig, - void const * const obj, - char const * const name) noexcept; - -//${QS::QS-tx::obj_dict_pre_} ................................................ -//! Output object dictionary record -//! @sa QS_OBJ_DICTIONARY() -void obj_dict_pre_( - void const * const obj, - char const * const name) noexcept; - -//${QS::QS-tx::obj_arr_dict_pre_} ............................................ -//! Output predefined object-array dictionary record -//! @sa QS_OBJ_ARR_DICTIONARY() -void obj_arr_dict_pre_( - void const * const obj, - std::uint_fast16_t const idx, - char const * const name) noexcept; - -//${QS::QS-tx::fun_dict_pre_} ................................................ -//! Output function dictionary record -//! @sa QS_FUN_DICTIONARY() -void fun_dict_pre_( - QSpyFunPtr fun, - char const * const name) noexcept; - -//${QS::QS-tx::usr_dict_pre_} ................................................ -//! Output user dictionary record -//! @sa QS_USR_DICTIONARY() -void usr_dict_pre_( - enum_t const rec, - char const * const name) noexcept; - -//${QS::QS-tx::enum_dict_pre_} ............................................... -//! Output enumeration dictionary record -//! @sa QS_ENUM_DICTIONARY() -void enum_dict_pre_( - enum_t const value, - std::uint8_t const group, - char const * const name) noexcept; - -//${QS::QS-tx::assertion_pre_} ............................................... -//! internal function to produce the assertion failure trace record -//! @sa QS_ASSERTION() -void assertion_pre_( - char const * const module, - int_t const loc, - std::uint32_t const delay) noexcept; - -//${QS::QS-tx::crit_entry_pre_} .............................................. -//! internal function to produce the critical section entry record -//! @sa QF_QS_CRIT_ENTRY() -void crit_entry_pre_() noexcept; - -//${QS::QS-tx::crit_exit_pre_} ............................................... -//! internal function to produce the critical section exit record -//! @sa QF_QS_CRIT_EXIT() -void crit_exit_pre_() noexcept; - -//${QS::QS-tx::isr_entry_pre_} ............................................... -//! internal function to produce the ISR entry record -//! @sa QF_QS_ISR_ENTRY() -void isr_entry_pre_( - std::uint8_t const isrnest, - std::uint8_t const prio) noexcept; - -//${QS::QS-tx::isr_exit_pre_} ................................................ -//! internal function to produce the ISR exit record -//! @sa QF_QS_ISR_EXIT() -void isr_exit_pre_( - std::uint8_t const isrnest, - std::uint8_t const prio) noexcept; - -//${QS::QS-tx::target_info_pre_} ............................................. -//! Helper function to output the predefined Target-info trace record. -void target_info_pre_(std::uint8_t const isReset); - -//${QS::QS-tx::onStartup} .................................................... -//! Callback to startup the QS facility -bool onStartup(void const * arg); - -//${QS::QS-tx::onCleanup} .................................................... -//! Callback to cleanup the QS facility -void onCleanup(); - -//${QS::QS-tx::onFlush} ...................................................... -//! Callback to flush the QS trace data to the host -void onFlush(); - -//${QS::QS-tx::onGetTime} .................................................... -//! Callback to obtain a timestamp for a QS record -QSTimeCtr onGetTime(); - -} // namespace QS -namespace QS { - -//${QS::QS-tx-64bit::u64_raw_} ............................................... -//! Output std::uint64_t data element without format information -void u64_raw_(std::uint64_t d) noexcept; - -//${QS::QS-tx-64bit::u64_fmt_} ............................................... -//! Output std::uint64_t data element with format information -//! @sa QS_U64(), QS_I64() -void u64_fmt_( - std::uint8_t format, - std::uint64_t d) noexcept; - -} // namespace QS -namespace QS { - -//${QS::QS-tx-fp::f32_fmt_} .................................................. -//! Output 32-bit floating point data element with format information -//! @sa QS_F32() -void f32_fmt_( - std::uint8_t format, - float32_t d) noexcept; - -//${QS::QS-tx-fp::f64_fmt_} .................................................. -//! Output 64-bit floating point data element with format information -//! @sa QS_F64() -void f64_fmt_( - std::uint8_t format, - float32_t d) noexcept; - -} // namespace QS -namespace QS { - -//${QS::QS-rx::QSrx} ......................................................... -//! QS software tracing parameters for QS input (QS-RX) -class QSrx { -public: - - //! current objects - void * currObj[8]; - - //! pointer to the start of the ring buffer - std::uint8_t * buf; - - //! offset of the end of the ring buffer - QSCtr end; - - //! offset to where next byte will be inserted - QSCtr volatile head; - - //! offset of where next byte will be extracted - QSCtr volatile tail; - -#ifdef Q_UTEST - //! QUTest event loop is running - bool inTestLoop; -#endif // def Q_UTEST -}; // class QSrx - -//${QS::QS-rx::rxPriv_} ...................................................... -//! the only instance of the QS-RX object (Singleton) -extern QSrx rxPriv_; - -//${QS::QS-rx::QSpyObjKind} .................................................. -//! Kinds of objects used in QS::setCurrObj() and QS::queryCurrObj() -enum QSpyObjKind : std::uint8_t { - SM_OBJ, //!< state machine object for QEP - AO_OBJ, //!< active object - MP_OBJ, //!< event pool object - EQ_OBJ, //!< raw queue object - TE_OBJ, //!< time event object - AP_OBJ, //!< generic Application-specific object - MAX_OBJ -}; - -//${QS::QS-rx::OSpyObjCombnation} ............................................ -//! Object combinations for QS::setCurrObj() and QS::queryCurrObj() -enum OSpyObjCombnation : std::uint8_t { - SM_AO_OBJ = MAX_OBJ //!< combination of SM and AO -}; - -//${QS::QS-rx::rxInitBuf} .................................................... -//! Initialize the QS RX data buffer -//! -//! @details -//! This function should be called from QS::onStartup() to provide QS-RX -//! with the receive data buffer. -//! -//! @param[in] sto[] the address of the memory block -//! @param[in] stoSize the size of this block [bytes]. The size of the -//! QS RX buffer cannot exceed 64KB. -//! -//! @note -//! QS-RX can work with quite small data buffers, but you will start -//! losing data if the buffer is not drained fast enough (e.g., in the -//! idle task). -//! -//! @note -//! If the data input rate exceeds the QS-RX processing rate, the data -//! will be lost, but the QS protocol will notice that: -//! (1) that the checksum in the incomplete QS records will fail; and -//! (2) the sequence counter in QS records will show discontinuities. -//! -//! The QS-RX channel will report any data errors by sending the -//! QS_RX_DATA_ERROR trace record. -void rxInitBuf( - std::uint8_t * const sto, - std::uint16_t const stoSize) noexcept; - -//${QS::QS-rx::rxPut} ........................................................ -//! Put one byte into the QS RX lock-free buffer -inline bool rxPut(std::uint8_t const b) noexcept { - QSCtr head = rxPriv_.head + 1U; - if (head == rxPriv_.end) { - head = 0U; - } - if (head != rxPriv_.tail) { // buffer NOT full? - rxPriv_.buf[rxPriv_.head] = b; - rxPriv_.head = head; - return true; // byte placed in the buffer - } - else { - return false; // byte NOT placed in the buffer - } -} - -//${QS::QS-rx::rxGetNfree} ................................................... -//! Obtain the number of free bytes in the QS RX data buffer -//! -//! @details -//! This function is intended to be called from the ISR that reads the -//! QS-RX bytes from the QSPY application. The function returns the -//! conservative number of free bytes currently available in the buffer, -//! assuming that the head pointer is not being moved concurrently. -//! The tail pointer might be moving, meaning that bytes can be -//! concurrently removed from the buffer. -std::uint16_t rxGetNfree() noexcept; - -//${QS::QS-rx::doInput} ...................................................... -//! Perform the QS-RX input (implemented in some QS ports) -void doInput(); - -//${QS::QS-rx::setCurrObj} ................................................... -//! Set the "current object" in the Target -//! -//! @details -//! This function sets the "current object" in the Target. -void setCurrObj( - std::uint8_t obj_kind, - void * obj_ptr) noexcept; - -//${QS::QS-rx::queryCurrObj} ................................................. -//! Query the "current object" in the Target -//! -//! @details -//! This function programmatically generates the response to the query for -//! a "current object". -void queryCurrObj(std::uint8_t obj_kind) noexcept; - -//${QS::QS-rx::rxParse} ...................................................... -//! Parse all bytes present in the QS RX data buffer -void rxParse(); - -//${QS::QS-rx::rxHandleGoodFrame_} ........................................... -//! internal function to handle incoming (QS-RX) packet -void rxHandleGoodFrame_(std::uint8_t const state); - -//${QS::QS-rx::onReset} ...................................................... -//! callback function to reset the Target (to be implemented in the BSP) -void onReset(); - -//${QS::QS-rx::onCommand} .................................................... -//! Callback function to execute user commands (to be implemented in BSP) -void onCommand( - std::uint8_t cmdId, - std::uint32_t param1, - std::uint32_t param2, - std::uint32_t param3); - -} // namespace QS - -} // namespace QP -//$enddecl${QS} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -//============================================================================ -// Global namespace... -//$declare${QS-macros} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv - -//${QS-macros::QS_INIT} ...................................................... -//! Initialize the QS facility -//! -//! @details -//! This macro provides an indirection layer to invoke the QS initialization -//! routine if #Q_SPY is defined, or do nothing if #Q_SPY is not defined. -//! @sa QP::QS::onStartup(), example of setting up a QS filter in -//! QS_GLB_FILTER() -#define QS_INIT(arg_) (QP::QS::onStartup(arg_)) - -//${QS-macros::QS_EXIT} ...................................................... -//! Cleanup the QS facility -//! -//! @details -//! This macro provides an indirection layer to invoke the QS cleanup -//! routine if #Q_SPY is defined, or do nothing if #Q_SPY is not defined. -//! @sa QP::QS::onCleanup() -#define QS_EXIT() (QP::QS::onCleanup()) - -//${QS-macros::QS_OUTPUT} .................................................... -//! macro to handle the QS output from the application -//! -//! @note -//! If this macro is used, the application must define QS::doOutput(). -#define QS_OUTPUT() (QP::QS::doOutput()) - -//${QS-macros::QS_RX_INPUT} .................................................. -//! macro to handle the QS-RX input to the application -//! -//! @note -//! If this macro is used, the application must define QS::doInput(). -#define QS_RX_INPUT() (QP::QS::doInput()) - -//${QS-macros::QS_GLB_FILTER} ................................................ -//! Global Filter ON for a given record type `rec_` -//! -//! @details -//! This macro provides an indirection layer to call QP::QS::filterOn() -//! if #Q_SPY is defined, or do nothing if #Q_SPY is not defined. -//! -//! @sa -//! - QP::QSpyRecordGroups - QS record groups that can be used as `rec_` -//! - QP::QSpyRecords - individual QS records that can be used as `rec_` -//! -//! @usage -//! The following example shows how to use QS filters: -//! @include qs_filter.cpp -#define QS_GLB_FILTER(rec_) \ - (QP::QS::glbFilter_(static_cast(rec_))) - -//${QS-macros::QS_LOC_FILTER} ................................................ -//! Local Filter for a given state machine object `qs_id` -//! @details -//! This macro provides an indirection layer to call QS_locFilter_() -//! if #Q_SPY is defined, or do nothing if #Q_SPY is not defined. -//! -//! @sa -//! - QP::QSpyIdGroups - QS ID groups that can be used as `qs_id_` -//! - QP::QSpyIdOffsets - QS ID offsets for `qs_id_` (e.g., QS_AP_IDS + 5) -//! -//! The following example shows how to use QS filters: -//! @include qs_filter.cpp -#define QS_LOC_FILTER(qs_id_) \ - (QP::QS::locFilter_(static_cast(qs_id_))) - -//${QS-macros::QS_BEGIN_ID} .................................................. -//! Begin an application-specific QS record with entering critical section -//! -//! @details -//! The following example shows how to build a user QS record using the -//! macros QS_BEGIN_ID(), QS_END(), and the formatted output macros: -//! QS_U8(), QS_STR(), etc. -//! -//! @note -//! Must always be used in pair with QS_END() -//! -//! @include qs_user.cpp -#define QS_BEGIN_ID(rec_, qs_id_) \ -if (QS_GLB_CHECK_(rec_) && QS_LOC_CHECK_(qs_id_)) { \ - QS_CRIT_STAT_ \ - QS_CRIT_E_(); \ - QP::QS::beginRec_(static_cast(rec_)); \ - QS_TIME_PRE_(); - -//${QS-macros::QS_END} ....................................................... -//! End an applicationi-specific QS record with exiting critical section. -//! @sa example for QS_BEGIN_ID() -//! @note Must always be used in pair with QS_BEGIN_ID() -#define QS_END() \ - QP::QS::endRec_(); \ - QS_CRIT_X_(); \ -} - -//${QS-macros::QS_FLUSH} ..................................................... -//! Flush the QS trace data to the host -//! -//! @details -//! This macro invokes the QP::QS::flush() platform-dependent callback -//! function to flush the QS trace buffer to the host. The function -//! typically busy-waits until all the data in the buffer is sent to -//! the host. This is acceptable only in the initial transient. -#define QS_FLUSH() (QP::QS::onFlush()) - -//${QS-macros::QS_BEGIN_NOCRIT} .............................................. -//! Begin an application-specific QS record WITHOUT entering critical section -#define QS_BEGIN_NOCRIT(rec_, qs_id_) \ -if (QS_GLB_CHECK_(rec_) && QS_LOC_CHECK_(qs_id_)) { \ - QP::QS::beginRec_(rec_); \ - QS_TIME_PRE_(); - -//${QS-macros::QS_END_NOCRIT} ................................................ -//! End an application-specific QS record WITHOUT exiting critical section. -#define QS_END_NOCRIT() \ - QP::QS::endRec_(); \ -} - -//${QS-macros::QS_GLB_CHECK_} ................................................ -//! Helper macro for checking the global QS filter -#define QS_GLB_CHECK_(rec_) \ -((static_cast(QP::QS::priv_.glbFilter[ \ - static_cast(rec_) >> 3U]) \ - & (static_cast(1U) \ - << (static_cast(rec_) & 7U))) != 0U) - -//${QS-macros::QS_LOC_CHECK_} ................................................ -//! Helper macro for checking the local QS filter -#define QS_LOC_CHECK_(qs_id_) \ -((static_cast(QP::QS::priv_.locFilter \ - [static_cast(qs_id_) >> 3U]) \ - & (static_cast(1U) \ - << (static_cast(qs_id_) & 7U))) != 0U) - -//${QS-macros::QS_REC_DONE} .................................................. -#ifndef QS_REC_DONE -//! Macro to execute user code when a QS record is produced -//! -//! @note -//! This is a dummy definition in case this macro is undefined. -#define QS_REC_DONE() (static_cast(0)) -#endif // ndef QS_REC_DONE - -//${QS-macros::QS_I8} ........................................................ -//! Output formatted std::int8_t to the QS record -#define QS_I8(width_, data_) \ -(QP::QS::u8_fmt_(static_cast( \ - (static_cast(((width_) << 4U) & 0x7U)) \ - | static_cast(QP::QS::I8_ENUM_T)), (data_))) - -//${QS-macros::QS_U8} ........................................................ -//! Output formatted std::uint8_t to the QS record -#define QS_U8(width_, data_) \ -(QP::QS::u8_fmt_(static_cast( \ - (static_cast((width_) << 4U)) \ - | static_cast(QP::QS::U8_T)), (data_))) - -//${QS-macros::QS_I16} ....................................................... -//! Output formatted std::int16_t to the QS record -#define QS_I16(width_, data_) \ -(QP::QS::u16_fmt_(static_cast( \ - (static_cast((width_) << 4U)) \ - | static_cast(QP::QS::I16_T)), (data_))) - -//${QS-macros::QS_U16} ....................................................... -//! Output formatted std::uint16_t to the QS record -#define QS_U16(width_, data_) \ -(QP::QS::u16_fmt_(static_cast((((width_) << 4U)) \ - | static_cast(QP::QS::U16_T)), (data_))) - -//${QS-macros::QS_I32} ....................................................... -//! Output formatted std::int32_t to the QS record -#define QS_I32(width_, data_) \ -(QP::QS::u32_fmt_( \ - static_cast((static_cast((width_) << 4U)) \ - | static_cast(QP::QS::I32_T)), (data_))) - -//${QS-macros::QS_U32} ....................................................... -//! Output formatted std::uint32_t to the QS record -#define QS_U32(width_, data_) \ -(QP::QS::u32_fmt_(static_cast( \ - (static_cast((width_) << 4U)) \ - | static_cast(QP::QS::U32_T)), (data_))) - -//${QS-macros::QS_I64} ....................................................... -//! Output formatted std::int64_t to the QS record -#define QS_I64(width_, data_) \ -(QP::QS::u64_fmt_(static_cast( \ - (static_cast((width_) << 4U)) \ - | static_cast(QP::QS::I64_T)), (data_))) - -//${QS-macros::QS_U64} ....................................................... -//! Output formatted std::uint64_t to the QS record -#define QS_U64(width_, data_) \ -(QP::QS::u64_fmt_(static_cast( \ - (static_cast((width_) << 4U)) \ - | static_cast(QP::QS::U64_T)), (data_))) - -//${QS-macros::QS_F32} ....................................................... -//! Output formatted 32-bit floating point number to the QS record -#define QS_F32(width_, data_) \ -(QP::QS::f32_fmt_(static_cast( \ - (static_cast((width_) << 4U)) \ - | static_cast(QP::QS::F32_T)), (data_))) - -//${QS-macros::QS_F64} ....................................................... -//! Output formatted 64-bit floating point number to the QS record -#define QS_F64(width_, data_) \ -(QP::QS::f64_fmt_(static_cast( \ - (static_cast((width_) << 4U)) \ - | static_cast(QP::QS::F64_T)), (data_))) - -//${QS-macros::QS_STR} ....................................................... -//! Output formatted zero-terminated ASCII string to the QS record -#define QS_STR(str_) (QP::QS::str_fmt_(str_)) - -//${QS-macros::QS_MEM} ....................................................... -//! Output formatted memory block of up to 255 bytes to the QS record -#define QS_MEM(mem_, size_) (QP::QS::mem_fmt_((mem_), (size_))) - -//${QS-macros::QS_ENUM} ...................................................... -//! Output formatted enumeration to the QS record -#define QS_ENUM(group_, value_) \ - (QP::QS::u8_fmt_(static_cast(0x80U | ((group_) << 4U)) \ - | static_cast(QP::QS::I8_ENUM_T),\ - static_cast(value_))) - -//${QS-macros::QS_TIME_PRE_} ................................................. -#if (QS_TIME_SIZE == 4U) -//! Output time stamp to a QS record (used in predefined -//! and application-specific trace records) -#define QS_TIME_PRE_() (QP::QS::u32_raw_(QP::QS::onGetTime())) -#endif // (QS_TIME_SIZE == 4U) - -//${QS-macros::QS_TIME_PRE_} ................................................. -#if (QS_TIME_SIZE == 2U) -#define QS_TIME_PRE_() (QP::QS::u16_raw_(QP::QS::onGetTime())) -#endif // (QS_TIME_SIZE == 2U) - -//${QS-macros::QS_TIME_PRE_} ................................................. -#if (QS_TIME_SIZE == 1U) -#define QS_TIME_PRE_() (QP::QS::u8_raw_(QP::QS::onGetTime())) -#endif // (QS_TIME_SIZE == 1U) - -//${QS-macros::QS_OBJ} ....................................................... -#if (QS_OBJ_PTR_SIZE == 4U) -//! Output formatted object pointer to the QS record -#define QS_OBJ(obj_) (QP::QS::u32_fmt_(QP::QS::OBJ_T, \ - reinterpret_cast(obj_))) -#endif // (QS_OBJ_PTR_SIZE == 4U) - -//${QS-macros::QS_OBJ} ....................................................... -#if (QS_OBJ_PTR_SIZE == 2U) -#define QS_OBJ(obj_) (QP::QS::u16_fmt_(QP::QS::OBJ_T, \ - reinterpret_cast(obj_))) -#endif // (QS_OBJ_PTR_SIZE == 2U) - -//${QS-macros::QS_OBJ} ....................................................... -#if (QS_OBJ_PTR_SIZE == 1U) -#define QS_OBJ(obj_) (QP::QS::u8_fmt_(QP::QS::OBJ_T, \ - reinterpret_cast(obj_))) -#endif // (QS_OBJ_PTR_SIZE == 1U) - -//${QS-macros::QS_OBJ} ....................................................... -#if (QS_OBJ_PTR_SIZE == 8U) -#define QS_OBJ(obj_) (QP::QS::u64_fmt_(QP::QS::OBJ_T, \ - reinterpret_cast(obj_))) -#endif // (QS_OBJ_PTR_SIZE == 8U) - -//${QS-macros::QS_FUN} ....................................................... -#if (QS_FUN_PTR_SIZE == 4U) -//! Output formatted function pointer to the QS record -#define QS_FUN(fun_) (QP::QS::u32_fmt_(QP::QS::FUN_T, \ - reinterpret_cast(fun_))) -#endif // (QS_FUN_PTR_SIZE == 4U) - -//${QS-macros::QS_FUN} ....................................................... -#if (QS_FUN_PTR_SIZE == 2U) -#define QS_FUN(fun_) (QP::QS::u16_fmt_(QP::QS::FUN_T, \ - reinterpret_cast(fun_))) -#endif // (QS_FUN_PTR_SIZE == 2U) - -//${QS-macros::QS_FUN} ....................................................... -#if (QS_FUN_PTR_SIZE == 1U) -#define QS_FUN(fun_) (QP::QS::u8_fmt_(QP::QS::FUN_T, \ - reinterpret_cast(fun_))) -#endif // (QS_FUN_PTR_SIZE == 1U) - -//${QS-macros::QS_FUN} ....................................................... -#if (QS_FUN_PTR_SIZE == 8U) -#define QS_FUN(fun_) (QP::QS::u64_fmt_(QP::QS::FUN_T, \ - reinterpret_cast(fun_))) -#endif // (QS_FUN_PTR_SIZE == 8U) - -//${QS-macros::QS_SIG} ....................................................... -#if (Q_SIGNAL_SIZE == 4U) -//! Output formatted event signal (of type QP::QSignal) and -//! the state machine object to the user QS record -#define QS_SIG(sig_, obj_) \ - QP::QS::u32_fmt_(QP::QS::SIG_T, static_cast(sig_)); \ - QP::QS::obj_raw_(obj_) -#endif // (Q_SIGNAL_SIZE == 4U) - -//${QS-macros::QS_SIG} ....................................................... -#if (Q_SIGNAL_SIZE == 2U) -#define QS_SIG(sig_, obj_) \ - QP::QS::u16_fmt_(QP::QS::SIG_T, static_cast(sig_)); \ - QP::QS::obj_raw_(obj_) -#endif // (Q_SIGNAL_SIZE == 2U) - -//${QS-macros::QS_SIG} ....................................................... -#if (Q_SIGNAL_SIZE == 1U) -#define QS_SIG(sig_, obj_) \ - QP::QS::u8_fmt_(QP::QS::SIG_T, static_cast(sig_)); \ - QP::QS::obj_raw_(obj_) -#endif // (Q_SIGNAL_SIZE == 1U) - -//${QS-macros::QS_SIG_DICTIONARY} ............................................ -//! Output signal dictionary record -//! -//! @details -//! A signal dictionary record associates the numerical value of the signal -//! and the binary address of the state machine that consumes that signal -//! with the human-readable name of the signal. -//! -//! @param[in] sig_ event signal (typically enumerated, e.g. `TIMEOUT_SIG`) -//! @param[in] obj_ pointer to the associated state machine object -//! (might be `nullptr` for globally recognized signals) -//! -//! A signal dictionary entry is associated with both the signal value `sig_` -//! and the state machine `obj_`, because signals are required to be unique -//! only within a given state machine and therefore the same numerical values -//! can represent different signals in different state machines. -//! -//! For the "global" signals that have the same meaning in all state machines -//! (such as globally published signals), you can specify a signal dictionary -//! entry with the `obj_` parameter set to `nullptr`. -//! -//! The following example shows the definition of signal dictionary entries -//! in the initial transition of the Table active object. Please note that -//! signals HUNGRY_SIG and DONE_SIG are associated with the Table state -//! machine only ("me" `obj_` pointer). The EAT_SIG signal, on the other -//! hand, is global (`obj_ == nullptr`): -//! @include qs_sigDic.cpp -//! -//! The following QSpy log example shows the signal dictionary records -//! generated from the Table initial transition and subsequent records that -//! show human-readable names of the signals: -//! @include qs_sigLog.txt -#define QS_SIG_DICTIONARY(sig_, obj_) \ - (QP::QS::sig_dict_pre_((sig_), (obj_), #sig_)) - -//${QS-macros::QS_OBJ_DICTIONARY} ............................................ -//! Output QS object dictionary record -//! -//! @details -//! An object dictionary record associates the binary address of an object -//! in the target's memory with the human-readable name of the object. -//! -//! @param[in] obj_ pointer to the object (any object) -//! -//! The following example shows the definition of object dictionary entry -//! for the Table active object: -//! @include qs_objDic.cpp -#define QS_OBJ_DICTIONARY(obj_) \ - (QP::QS::obj_dict_pre_((obj_), #obj_)) - -//${QS-macros::QS_OBJ_ARR_DICTIONARY} ........................................ -//! Output QS object-array dictionary record -//! -//! @details -//! An object array dictionary record associates the binary address of the -//! object element in the target's memory with the human-readable name -//! of the object. -//! -//! @param[in] obj_ pointer to the object (any object) -//! @param[in] idx_ array index -//! -//! The following example shows the definition of object array dictionary -//! for `Philo::inst[n]` and `Philo::inst[n].m_timeEvt`: -//! @include qs_objArrDic.cpp -#define QS_OBJ_ARR_DICTIONARY(obj_, idx_) \ - (QP::QS::obj_arr_dict_pre_((obj_), (idx_), #obj_)) - -//${QS-macros::QS_FUN_DICTIONARY} ............................................ -//! Output function dictionary record -//! -//! @details -//! A function dictionary record associates the binary address of a function -//! in the target's memory with the human-readable name of the function. -//! -//! Providing a function dictionary QS record can vastly improve readability -//! of the QS log, because instead of dealing with cryptic machine addresses -//! the QSpy host utility can display human-readable function names. -//! -//! The example from #QS_SIG_DICTIONARY shows the definition of a function -//! dictionary. -#define QS_FUN_DICTIONARY(fun_) \ - (QP::QS::fun_dict_pre_( \ - QP::QS::force_cast(fun_), #fun_)) - -//${QS-macros::QS_USR_DICTIONARY} ............................................ -//! Output user QS record dictionary record -//! -//! @details -//! A user QS record dictionary record associates the numerical value of a -//! user record with the human-readable identifier. -#define QS_USR_DICTIONARY(rec_) \ - (QP::QS::usr_dict_pre_((rec_), #rec_)) - -//${QS-macros::QS_ENUM_DICTIONARY} ........................................... -//! Output enumeration dictionary record -//! -//! @details -//! An enum QS record dictionary record associates the numerical value of -//! an enumeration with the human-readable identifier. -#define QS_ENUM_DICTIONARY(value_, group_) \ - (QP::QS::enum_dict_pre_((value_), (group_), #value_)) - -//${QS-macros::QF_QS_CRIT_ENTRY} ............................................. -//! Output the critical section entry record -#define QF_QS_CRIT_ENTRY() (QP::QS::crit_entry_pre_()) - -//${QS-macros::QF_QS_CRIT_EXIT} .............................................. -//! Output the critical section exit record -#define QF_QS_CRIT_EXIT() (QP::QS::crit_exit_pre_()) - -//${QS-macros::QF_QS_ISR_ENTRY} .............................................. -//! Output the interrupt entry record -#define QF_QS_ISR_ENTRY(isrnest_, prio_) \ - (QP::QS::isr_entry_pre_((isrnest_), (prio_))) - -//${QS-macros::QF_QS_ISR_EXIT} ............................................... -//! Output the interrupt exit record -#define QF_QS_ISR_EXIT(isrnest_, prio_) \ - (QP::QS::isr_exit_pre_((isrnest_), (prio_))) - -//${QS-macros::QF_QS_ACTION} ................................................. -//! Execute an action that is only necessary for QS output -#define QF_QS_ACTION(act_) (act_) - -//${QS-macros::QS_ASSERTION} ................................................. -//! Produce the assertion failure trace record -#define QS_ASSERTION(module_, loc_, delay_) \ - (QP::QS::assertion_pre_((module_), (loc_), (delay_))) - -//${QS-macros::QS_EOD} ....................................................... -//! Constant representing End-Of-Data condition returned from the -//! QS::getByte() function. -#define QS_EOD (static_cast(0xFFFFU)) - -//${QS-macros::QS_CMD} ....................................................... -//! Constant representing command enumeration group -//! in QS_ENUM_DICTIONARY() and QS_ENUM() -//! @sa QS::onCommand() -#define QS_CMD (static_cast(7U)) - -//${QS-macros::QS_HEX_FMT} ................................................... -//! Constant representing HEX format for the "width" filed -//! in QS_U8(), QS_U16(), QS_U32(), and QS_U64(). -#define QS_HEX_FMT (static_cast(0x0FU)) -//$enddecl${QS-macros} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -//============================================================================ -// Facilities for QS critical section - -// QS-specific critical section -#ifdef QS_CRIT_ENTRY // separate QS critical section defined? - -#ifndef QS_CRIT_STAT_TYPE - #define QS_CRIT_STAT_ - #define QS_CRIT_E_() QS_CRIT_ENTRY(dummy) - #define QS_CRIT_X_() QS_CRIT_EXIT(dummy); QS_REC_DONE() -#else - #define QS_CRIT_STAT_ QS_CRIT_STAT_TYPE critStat_; - #define QS_CRIT_E_() QS_CRIT_ENTRY(critStat_) - #define QS_CRIT_X_() QS_CRIT_EXIT(critStat_); QS_REC_DONE() -#endif // QS_CRIT_STAT_TYPE - -#else // separate QS critical section not defined--use the QF definition - -#ifndef QF_CRIT_STAT_TYPE - //! This is an internal macro for defining the critical section - //! status type - //! - //! @details - //! The purpose of this macro is to enable writing the same code for the - //! case when critical section status type is defined and when it is not. - //! If the macro #QF_CRIT_STAT_TYPE is defined, this internal macro - //! provides the definition of the critical section status variable. - //! Otherwise this macro is empty. - //! @sa #QF_CRIT_STAT_TYPE - #define QS_CRIT_STAT_ - - //! This is an internal macro for entering a critical section - //! - //! @details - //! The purpose of this macro is to enable writing the same code for the - //! case when critical section status type is defined and when it is not. - //! If the macro #QF_CRIT_STAT_TYPE is defined, this internal macro - //! invokes #QF_CRIT_ENTRY passing the key variable as the parameter. - //! Otherwise #QF_CRIT_ENTRY is invoked with a dummy parameter. - //! @sa #QF_CRIT_ENTRY - #define QS_CRIT_E_() QF_CRIT_ENTRY(dummy) - - //! This is an internal macro for exiting a critical section - //! - //! @details - //! The purpose of this macro is to enable writing the same code for the - //! case when critical section status type is defined and when it is not. - //! If the macro #QF_CRIT_STAT_TYPE is defined, this internal macro - //! invokes #QF_CRIT_EXIT passing the key variable as the parameter. - //! Otherwise #QF_CRIT_EXIT is invoked with a dummy parameter. - //! @sa #QF_CRIT_EXIT - #define QS_CRIT_X_() QF_CRIT_EXIT(dummy); QS_REC_DONE() - -#elif (!defined QS_CRIT_STAT_) - #define QS_CRIT_STAT_ QF_CRIT_STAT_TYPE critStat_; - #define QS_CRIT_E_() QF_CRIT_ENTRY(critStat_) - #define QS_CRIT_X_() QF_CRIT_EXIT(critStat_); QS_REC_DONE() -#endif // simple unconditional interrupt disabling used - -#endif // separate QS critical section not defined - -//============================================================================ -// Macros for use in QUTest only -#ifdef Q_UTEST - -//$declare${QUTest} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv -namespace QP { -namespace QS { - -//${QUTest::QS::QUTEST_ON_POST} .............................................. -//! record ID for posting events -constexpr std::uint8_t QUTEST_ON_POST {124U}; - -//${QUTest::QS::TProbe} ...................................................... -//! Test Probe attributes -struct TProbe { - QSFun addr; //!< pointer to function hosting the Test Probe - std::uint32_t data; //!< data associated with the Test Probe - std::uint8_t idx; //!< index of the Test Probe -}; - -//${QUTest::QS::TestData} .................................................... -//! QUTest data -struct TestData { - TProbe tpBuf[16]; //!< up to 16 Test Probes - std::uint8_t tpNum; //!< # of registered Test Probes - QSTimeCtr testTime; //!< test time stamp -}; - -//${QUTest::QS::testData} .................................................... -//! QUTest data -extern TestData testData; - -//${QUTest::QS::test_pause_} ................................................. -//! internal function to pause test and enter the test event loop -void test_pause_(); - -//${QUTest::QS::getTestProbe_} ............................................... -//! get the test probe data for the given API -std::uint32_t getTestProbe_(QP::QSpyFunPtr const api) noexcept; - -//${QUTest::QS::onTestSetup} ................................................. -//! callback to setup a unit test inside the Target -void onTestSetup(); - -//${QUTest::QS::onTestTeardown} .............................................. -//! callback to teardown after a unit test inside the Target -void onTestTeardown(); - -//${QUTest::QS::onTestEvt} ................................................... -//! callback to "massage" the test event before dispatching/posting it -void onTestEvt(QEvt * e); - -//${QUTest::QS::onTestPost} .................................................. -//! callback to examine an event that is about to be posted -void onTestPost( - void const * sender, - QActive * recipient, - QEvt const * e, - bool status); - -//${QUTest::QS::onTestLoop} .................................................. -//! callback to run the test loop -void onTestLoop(); - -} // namespace QS - -} // namespace QP -//$enddecl${QUTest} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -//---------------------------------------------------------------------------- -// QP-stub for QUTest -// NOTE: The QP-stub is needed for unit testing QP applications, -// but might NOT be needed for testing QP itself. -// -#if Q_UTEST != 0 -//$declare${QUTest-stub::QS} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv -namespace QP { -namespace QS { - -//${QUTest-stub::QS::processTestEvts_} ....................................... -//! internal function to process posted events during test -void processTestEvts_(); - -} // namespace QS -} // namespace QP -//$enddecl${QUTest-stub::QS} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -//$declare${QUTest-stub::QHsmDummy} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv -namespace QP { - -//${QUTest-stub::QHsmDummy} .................................................. -//! Dummy HSM class for testing (inherits QP::QHsm) -//! -//! @details -//! QHsmDummy is a test double for the role of "Orthogonal Components" -//! HSM objects in QUTest unit testing. -class QHsmDummy : public QP::QHsm { -public: - - //! ctor - QHsmDummy(); - void init( - void const * const e, - std::uint_fast8_t const qs_id) override; - void init(std::uint_fast8_t const qs_id) override; - void dispatch( - QEvt const * const e, - std::uint_fast8_t const qs_id) override; -}; // class QHsmDummy - -} // namespace QP -//$enddecl${QUTest-stub::QHsmDummy} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -//$declare${QUTest-stub::QActiveDummy} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv -namespace QP { - -//${QUTest-stub::QActiveDummy} ............................................... -//! Dummy Active Object class for testing (inherits QP::QActive) -//! -//! @details -//! QActiveDummy is a test double for the role of collaborating active -//! objects in QUTest unit testing. -class QActiveDummy : public QP::QActive { -public: - - //! ctor - QActiveDummy(); - void start( - QPrioSpec const prioSpec, - QEvt const * * const qSto, - std::uint_fast16_t const qLen, - void * const stkSto, - std::uint_fast16_t const stkSize, - void const * const par) override; - void start( - QPrioSpec const prioSpec, - QEvt const * * const qSto, - std::uint_fast16_t const qLen, - void * const stkSto, - std::uint_fast16_t const stkSize) override - { - this->start(prioSpec, qSto, qLen, stkSto, stkSize, nullptr); - } - void init( - void const * const e, - std::uint_fast8_t const qs_id) override; - void init(std::uint_fast8_t const qs_id) override; - void dispatch( - QEvt const * const e, - std::uint_fast8_t const qs_id) override; - bool post_( - QEvt const * const e, - std::uint_fast16_t const margin, - void const * const sender) noexcept override; - void postLIFO(QEvt const * const e) noexcept override; -}; // class QActiveDummy - -} // namespace QP -//$enddecl${QUTest-stub::QActiveDummy} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -#endif // Q_UTEST != 0 - -//! QS macro to define the Test-Probe for a given `fun_` -#define QS_TEST_PROBE_DEF(fun_) \ - std::uint32_t const qs_tp_ = \ - QP::QS::getTestProbe_(QP::QS::force_cast(fun_)); - -//! QS macro to apply a Test-Probe -#define QS_TEST_PROBE(code_) \ - if (qs_tp_ != 0U) { code_ } - -//! QS macro to apply a Test-Probe -#define QS_TEST_PROBE_ID(id_, code_) \ - if (qs_tp_ == static_cast(id_)) { code_ } - -//! QS macro to pause test execution and enter the test event loop -#define QS_TEST_PAUSE() (QP::QS::test_pause_()) - -#else - // dummy definitions when not building for QUTEST - #define QS_TEST_PROBE_DEF(fun_) - #define QS_TEST_PROBE(code_) - #define QS_TEST_PROBE_ID(id_, code_) - #define QS_TEST_PAUSE() ((void)0) -#endif // Q_UTEST - -#endif // QS_HPP_ diff --git a/src/qs_dummy.hpp b/src/qs_dummy.hpp index 0593e7c..0cbdfc2 100644 --- a/src/qs_dummy.hpp +++ b/src/qs_dummy.hpp @@ -1,65 +1,53 @@ -//$file${include::qs_dummy.hpp} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv -// -// Model: qpcpp.qm -// File: ${include::qs_dummy.hpp} -// -// This code has been generated by QM 5.2.5 . -// DO NOT EDIT THIS FILE MANUALLY. All your changes will be lost. +//============================================================================ +// QP/C++ Real-Time Embedded Framework (RTEF) +// Version 8.0.2 // -// This code is covered by the following QP license: -// License # : LicenseRef-QL-dual -// Issued to : Any user of the QP/C++ real-time embedded framework -// Framework(s) : qpcpp -// Support ends : 2023-12-31 -// License scope: +// Copyright (C) 2005 Quantum Leaps, LLC. All rights reserved. // -// 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: +// This software is dual-licensed under the terms of the open-source GNU +// General Public License (GPL) or under the terms of one of the closed- +// source Quantum Leaps commercial licenses. // // Redistributions in source code must retain this top-level comment block. // Plagiarizing this software to sidestep the license obligations is illegal. // -// Contact information: +// NOTE: +// The GPL does NOT permit the incorporation of this code into proprietary +// programs. Please contact Quantum Leaps for commercial licensing options, +// which expressly supersede the GPL and are designed explicitly for +// closed-source distribution. +// +// Quantum Leaps contact information: // // -// -//$endhead${include::qs_dummy.hpp} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -//! @file -//! @brief Dummy definitions of the QS macros that avoid code generation from -//! the QS instrumentation. - +//============================================================================ #ifndef QS_DUMMY_HPP_ #define QS_DUMMY_HPP_ #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 + +#ifdef Q_UTEST + error Q_UTEST 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_GET_BYTE(pByte_) (0xFFFFU) -#define QS_GET_BLOCK(pSize_) (nullptr) +#define QS_LOC_FILTER(qsId_) static_cast(0) -#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) @@ -94,83 +82,94 @@ #define QS_OUTPUT() static_cast(0) #define QS_RX_INPUT() static_cast(0) +#define QS_ONLY(code_) static_cast(0) //============================================================================ -//$declare${QS::QSpyIdOffsets} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv -namespace QP { - -//${QS::QSpyIdOffsets} ....................................................... -//! QS ID offsets for QS_LOC_FILTER() -enum QSpyIdOffsets : std::int16_t { - QS_AO_ID = 0, //!< offset for AO priorities - QS_EP_ID = 64, //!< offset for event-pool IDs - QS_EQ_ID = 80, //!< offset for event-queue IDs - QS_AP_ID = 96 //!< offset for Appl-spec IDs -}; - -} // namespace QP -//$enddecl${QS::QSpyIdOffsets} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -//$declare${QS::QSpyIdGroups} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv namespace QP { +namespace QS { + +// QS API used inside applications +#ifndef QS_TIME_SIZE + using QSTimeCtr = std::uint32_t; +#elif (QS_TIME_SIZE == 2U) + using QSTimeCtr = std::uint16_t; +#else + using QSTimeCtr = std::uint32_t; +#endif -//${QS::QSpyIdGroups} ........................................................ -//! QS ID groups for QS_LOC_FILTER() -enum QSpyIdGroups : std::int16_t { - QS_ALL_IDS = 0xF0, //!< all QS IDs - QS_AO_IDS = 0x80 + QS_AO_ID, //!< AO IDs (priorities) - QS_EP_IDS = 0x80 + QS_EP_ID, //!< event-pool IDs - QS_EQ_IDS = 0x80 + QS_EQ_ID, //!< event-queue IDs - QS_AP_IDS = 0x80 + QS_AP_ID //!< Application-specific IDs -}; +void initBuf(std::uint8_t * const sto, + std::uint_fast32_t const stoSize) noexcept; +std::uint16_t getByte() noexcept; +std::uint8_t const *getBlock(std::uint16_t * const pNbytes) noexcept; +void doOutput(); +bool onStartup(void const * arg); +void onCleanup(); +void onFlush(); +QSTimeCtr onGetTime(); + +void rxInitBuf(std::uint8_t * const sto, + std::uint16_t const stoSize) noexcept; +bool rxPut(std::uint8_t const b) noexcept; +void rxParse(); } // namespace QP -//$enddecl${QS::QSpyIdGroups} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -//$declare${QS::QSpyId} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv -namespace QP { +} // namespace QS -//${QS::QSpyId} .............................................................. -//! QS ID type for applying local filtering -struct QSpyId { - std::uint8_t m_prio; //!< "priority" (qs_id) for the QS "local filter" +//============================================================================ +// QS API used inside test fixtures +#ifdef Q_UTEST + +#if (QS_FUN_PTR_SIZE == 2U) + typedef uint16_t QSFun; +#elif (QS_FUN_PTR_SIZE == 4U) + typedef uint32_t QSFun; +#elif (QS_FUN_PTR_SIZE == 8U) + typedef uint64_t QSFun; +#endif - //! get the "priority" (qs_id) from the QSpyId opbject - std::uint_fast8_t getPrio() const noexcept { - return static_cast(m_prio); - } +struct QS_TProbe { + QSFun addr; + uint32_t data; + uint8_t idx; }; -} // namespace QP -//$enddecl${QS::QSpyId} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +void QS_onTestSetup(void); +void QS_onTestTeardown(void); +void QS_onTestEvt(QEvt *e); +void QS_onTestPost(void const *sender, QActive *recipient, + QEvt const *e, bool status); +void QS_onTestLoop(void); -//============================================================================ -// internal QS macros used only in the QP components +#endif // def Q_UTEST +//============================================================================ +// interface used only for internal implementation, but not in applications #ifdef QP_IMPL // predefined QS trace records - #define QS_BEGIN_PRE_(rec_, qs_id_) if (false) { - #define QS_END_PRE_() } - #define QS_BEGIN_NOCRIT_PRE_(rec_, qs_id_) if (false) { - #define QS_END_NOCRIT_PRE_() } - #define QS_U8_PRE_(data_) static_cast(0) - #define QS_2U8_PRE_(data1_, data2_) static_cast(0) - #define QS_U16_PRE_(data_) static_cast(0) - #define QS_U32_PRE_(data_) static_cast(0) - #define QS_TIME_PRE_() static_cast(0) - #define QS_SIG_PRE_(sig_) static_cast(0) - #define QS_EVS_PRE_(size_) static_cast(0) - #define QS_OBJ_PRE_(obj_) static_cast(0) - #define QS_FUN_PRE_(fun_) static_cast(0) - #define QS_EQC_PRE_(ctr_) static_cast(0) - #define QS_MPC_PRE_(ctr_) static_cast(0) - #define QS_MPS_PRE_(size_) static_cast(0) - #define QS_TEC_PRE_(ctr_) static_cast(0) - - #define QS_CRIT_STAT_ - #define QF_QS_CRIT_ENTRY() static_cast(0) - #define QF_QS_CRIT_EXIT() static_cast(0) - #define QF_QS_ISR_ENTRY(isrnest_, prio_) static_cast(0) - #define QF_QS_ISR_EXIT(isrnest_, prio_) static_cast(0) - #define QF_QS_ACTION(act_) static_cast(0) + #define QS_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) + #define QS_U32_PRE(data_) static_cast(0) + #define QS_TIME_PRE() static_cast(0) + #define QS_SIG_PRE(sig_) static_cast(0) + #define QS_EVS_PRE(size_) static_cast(0) + #define QS_OBJ_PRE(obj_) static_cast(0) + #define QS_FUN_PRE(fun_) static_cast(0) + #define QS_EQC_PRE(ctr_) static_cast(0) + #define QS_MPC_PRE(ctr_) static_cast(0) + #define QS_MPS_PRE(size_) static_cast(0) + #define QS_TEC_PRE(ctr_) static_cast(0) + + #define QS_CRIT_STAT + #define QS_CRIT_ENTRY() static_cast(0) + #define QS_CRIT_EXIT() 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/src/qs_fp.cpp b/src/qs_fp.cpp deleted file mode 100644 index 70a1584..0000000 --- a/src/qs_fp.cpp +++ /dev/null @@ -1,137 +0,0 @@ -//$file${src::qs::qs_fp.cpp} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv -// -// Model: qpcpp.qm -// File: ${src::qs::qs_fp.cpp} -// -// This code has been generated by QM 5.2.5 . -// DO NOT EDIT THIS FILE MANUALLY. All your changes will be lost. -// -// This code is covered by the following QP license: -// License # : LicenseRef-QL-dual -// Issued to : Any user of the QP/C++ real-time embedded framework -// Framework(s) : qpcpp -// Support ends : 2023-12-31 -// License scope: -// -// Copyright (C) 2005 Quantum Leaps, LLC . -// -// SPDX-License-Identifier: GPL-3.0-or-later OR LicenseRef-QL-commercial -// -// This software is dual-licensed under the terms of the open source GNU -// General Public License version 3 (or any later version), or alternatively, -// under the terms of one of the closed source Quantum Leaps commercial -// licenses. -// -// The terms of the open source GNU General Public License version 3 -// can be found at: -// -// The terms of the closed source Quantum Leaps commercial licenses -// can be found at: -// -// Redistributions in source code must retain this top-level comment block. -// Plagiarizing this software to sidestep the license obligations is illegal. -// -// Contact information: -// -// -// -//$endhead${src::qs::qs_fp.cpp} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -//! @file -//! @brief QS floating point output implementation - -#define QP_IMPL // this is QF/QK implementation -#include "qs_port.hpp" // QS port -#include "qs_pkg.hpp" // QS package-scope internal interface - -//$skip${QP_VERSION} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv -// Check for the minimum required QP version -#if (QP_VERSION < 700U) || (QP_VERSION != ((QP_RELEASE^4294967295U) % 0x3E8U)) -#error qpcpp version 7.0.0 or higher required -#endif -//$endskip${QP_VERSION} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -//$define${QS::QS-tx-fp} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv -namespace QP { -namespace QS { - -//${QS::QS-tx-fp::f32_fmt_} .................................................. -void f32_fmt_( - std::uint8_t format, - float32_t d) noexcept -{ - union F32Rep { - float32_t f; - std::uint32_t u; - } fu32; // the internal binary representation - std::uint8_t chksum_ = priv_.chksum; // put in a temporary (register) - std::uint8_t * const buf_ = priv_.buf; // put in a temporary (register) - QSCtr head_ = priv_.head; // put in a temporary (register) - QSCtr const end_ = priv_.end; // put in a temporary (register) - - fu32.f = d; // assign the binary representation - - priv_.used = (priv_.used + 5U); // 5 bytes about to be added - QS_INSERT_ESC_BYTE_(format) // insert the format byte - - for (std::uint_fast8_t i = 4U; i != 0U; --i) { - format = static_cast(fu32.u); - QS_INSERT_ESC_BYTE_(format) - fu32.u >>= 8U; - } - - priv_.head = head_; // save the head - priv_.chksum = chksum_; // save the checksum -} - -//${QS::QS-tx-fp::f64_fmt_} .................................................. -void f64_fmt_( - std::uint8_t format, - float32_t d) noexcept -{ - union F64Rep { - float64_t d; - std::uint32_t u[2]; - } fu64; // the internal binary representation - std::uint8_t chksum_ = priv_.chksum; - std::uint8_t * const buf_ = priv_.buf; - QSCtr head_ = priv_.head; - QSCtr const end_ = priv_.end; - std::uint32_t i; - // static constant untion to detect endianness of the machine - static union U32Rep { - std::uint32_t u32; - std::uint8_t u8; - } const endian = { 1U }; - - fu64.d = d; // assign the binary representation - - // is this a big-endian machine? - if (endian.u8 == 0U) { - // swap fu64.u[0] <-> fu64.u[1]... - i = fu64.u[0]; - fu64.u[0] = fu64.u[1]; - fu64.u[1] = i; - } - - priv_.used = (priv_.used + 9U); // 9 bytes about to be added - QS_INSERT_ESC_BYTE_(format) // insert the format byte - - // output 4 bytes from fu64.u[0]... - for (i = 4U; i != 0U; --i) { - QS_INSERT_ESC_BYTE_(static_cast(fu64.u[0])) - fu64.u[0] >>= 8U; - } - - // output 4 bytes from fu64.u[1]... - for (i = 4U; i != 0U; --i) { - QS_INSERT_ESC_BYTE_(static_cast(fu64.u[1])) - fu64.u[1] >>= 8U; - } - - priv_.head = head_; // update the head - priv_.chksum = chksum_; // update the checksum -} - -} // namespace QS -} // namespace QP -//$enddef${QS::QS-tx-fp} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/src/qs_pkg.hpp b/src/qs_pkg.hpp deleted file mode 100644 index 7dbe63a..0000000 --- a/src/qs_pkg.hpp +++ /dev/null @@ -1,304 +0,0 @@ -//$file${include::qs_pkg.hpp} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv -// -// Model: qpcpp.qm -// File: ${include::qs_pkg.hpp} -// -// This code has been generated by QM 5.2.5 . -// DO NOT EDIT THIS FILE MANUALLY. All your changes will be lost. -// -// This code is covered by the following QP license: -// License # : LicenseRef-QL-dual -// Issued to : Any user of the QP/C++ real-time embedded framework -// Framework(s) : qpcpp -// Support ends : 2023-12-31 -// License scope: -// -// Copyright (C) 2005 Quantum Leaps, LLC . -// -// SPDX-License-Identifier: GPL-3.0-or-later OR LicenseRef-QL-commercial -// -// This software is dual-licensed under the terms of the open source GNU -// General Public License version 3 (or any later version), or alternatively, -// under the terms of one of the closed source Quantum Leaps commercial -// licenses. -// -// The terms of the open source GNU General Public License version 3 -// can be found at: -// -// The terms of the closed source Quantum Leaps commercial licenses -// can be found at: -// -// Redistributions in source code must retain this top-level comment block. -// Plagiarizing this software to sidestep the license obligations is illegal. -// -// Contact information: -// -// -// -//$endhead${include::qs_pkg.hpp} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -//! @file -//! @brief Internal (package scope) QS/C++ interface. - -#ifndef QS_PKG_HPP_ -#define QS_PKG_HPP_ - -//============================================================================ -namespace QP { - -//! QS received record types (RX channel) -//! -//! @details -//! This enumeration specifies the record types for the QS receive channel -enum QSpyRxRecords : std::uint8_t { - QS_RX_INFO, //!< query Target info (ver, config, tstamp) - QS_RX_COMMAND, //!< execute a user-defined command in the Target - QS_RX_RESET, //!< reset the Target - QS_RX_TICK, //!< call QF_tick() - QS_RX_PEEK, //!< peek Target memory - QS_RX_POKE, //!< poke Target memory - QS_RX_FILL, //!< fill Target memory - QS_RX_TEST_SETUP, //!< test setup - QS_RX_TEST_TEARDOWN, //!< test teardown - QS_RX_TEST_PROBE, //!< set a Test-Probe in the Target - QS_RX_GLB_FILTER, //!< set global filters in the Target - QS_RX_LOC_FILTER, //!< set local filters in the Target - QS_RX_AO_FILTER, //!< set local AO filter in the Target - QS_RX_CURR_OBJ, //!< set the "current-object" in the Target - QS_RX_TEST_CONTINUE, //!< continue a test after QS_RX_TEST_WAIT() - QS_RX_QUERY_CURR, //!< query the "current object" in the Target - QS_RX_EVENT //!< inject an event to the Target (post/publish) -}; - -//! @brief Frame character of the QS output protocol -constexpr std::uint8_t QS_FRAME = 0x7EU; - -//! @brief Escape character of the QS output protocol -constexpr std::uint8_t QS_ESC = 0x7DU; - -//! @brief Escape modifier of the QS output protocol -//! -//! @details -//! The escaped byte is XOR-ed with the escape modifier before it is inserted -//! into the QS buffer. -constexpr std::uint8_t QS_ESC_XOR = 0x20U; - -//! @brief Escape character of the QS output protocol -constexpr std::uint8_t QS_GOOD_CHKSUM = 0xFFU; - -} // namespace QP - -//============================================================================ -// Macros for use inside other macros or internally in the QP code - -//! Internal QS macro to insert an un-escaped byte into the QS buffer -#define QS_INSERT_BYTE_(b_) \ - buf_[head_] = (b_); \ - ++head_; \ - if (head_ == end_) { \ - head_ = 0U; \ - } - -//! Internal QS macro to insert an escaped byte into the QS buffer -#define QS_INSERT_ESC_BYTE_(b_) \ - chksum_ += (b_); \ - if (((b_) != QS_FRAME) && ((b_) != QS_ESC)) { \ - QS_INSERT_BYTE_(b_) \ - } \ - else { \ - QS_INSERT_BYTE_(QS_ESC) \ - QS_INSERT_BYTE_(static_cast((b_) ^ QS_ESC_XOR)) \ - priv_.used = (priv_.used + 1U); \ - } - -//! Internal QS macro to begin a predefined QS record with critical section. -//! @note -//! This macro is intended to use only inside QP components and NOT -//! at the application level. -//! @sa QS_BEGIN_ID() -//! -#define QS_BEGIN_PRE_(rec_, qs_id_) \ - if (QS_GLB_CHECK_(rec_) && QS_LOC_CHECK_(qs_id_)) { \ - QS_CRIT_E_(); \ - QP::QS::beginRec_(static_cast(rec_)); - -//! Internal QS macro to end a predefined QS record with critical section. -//! @note -//! This macro is intended to use only inside QP components and NOT -//! at the application level. -//! @sa QS_END() -//! -#define QS_END_PRE_() \ - QP::QS::endRec_(); \ - QS_CRIT_X_(); \ - } - -//! Internal QS macro to begin a predefined QS record without critical section -//! -//! @note -//! This macro is intended to use only inside QP components and NOT -//! at the application level. -//! @sa QS_BEGIN_NOCRIT_PRE_() -#define QS_BEGIN_NOCRIT_PRE_(rec_, qs_id_) \ - if (QS_GLB_CHECK_(rec_) && QS_LOC_CHECK_(qs_id_)) { \ - QP::QS::beginRec_(static_cast(rec_)); - -//! Internal QS macro to end a predefiend QS record without critical section -//! -//! @note -//! This macro is intended to use only inside QP components and NOT -//! at the application level. @sa #QS_END_NOCRIT -#define QS_END_NOCRIT_PRE_() \ - QP::QS::endRec_(); \ - } - -#if (Q_SIGNAL_SIZE == 1U) - //! Internal QS macro to output an unformatted event signal data element - //! @note - //! The size of the pointer depends on the macro #Q_SIGNAL_SIZE. - #define QS_SIG_PRE_(sig_) \ - (QP::QS::u8_raw_(static_cast(sig_))) -#elif (Q_SIGNAL_SIZE == 2U) - #define QS_SIG_PRE_(sig_) \ - (QP::QS::u16_raw_(static_cast(sig_))) -#elif (Q_SIGNAL_SIZE == 4U) - #define QS_SIG_PRE_(sig_) \ - (QP::QS::u32_raw_(static_cast(sig_))) -#endif - -//! Internal QS macro to output an unformatted uint8_t data element -#define QS_U8_PRE_(data_) \ - (QP::QS::u8_raw_(static_cast(data_))) - -//! Internal QS macro to output 2 unformatted uint8_t data elements -#define QS_2U8_PRE_(data1_, data2_) \ - (QP::QS::u8u8_raw_(static_cast(data1_), \ - static_cast(data2_))) - -//! Internal QS macro to output an unformatted uint16_t data element -#define QS_U16_PRE_(data_) \ - (QP::QS::u16_raw_(static_cast(data_))) - -//! Internal QS macro to output an unformatted uint32_t data element -#define QS_U32_PRE_(data_) \ - (QP::QS::u32_raw_(static_cast(data_))) - -//! Internal QS macro to output a zero-terminated ASCII string -//! data element -#define QS_STR_PRE_(msg_) (QP::QS::str_raw_(msg_)) - -//! Internal QS macro to output object pointer data element -#define QS_OBJ_PRE_(obj_) (QP::QS::obj_raw_(obj_)) - -#if (QS_FUN_PTR_SIZE == 1U) - #define QS_FUN_PRE_(fun_) \ - (QP::QS::u8_raw_(reinterpret_cast(fun_))) -#elif (QS_FUN_PTR_SIZE == 2U) - #define QS_FUN_PRE_(fun_) \ - (QP::QS::u16_raw_(reinterpret_cast(fun_))) -#elif (QS_FUN_PTR_SIZE == 4U) - #define QS_FUN_PRE_(fun_) \ - (QP::QS::u32_raw_(reinterpret_cast(fun_))) -#elif (QS_FUN_PTR_SIZE == 8U) - #define QS_FUN_PRE_(fun_) \ - (QP::QS::u64_raw_(reinterpret_cast(fun_))) -#else - - //! Internal QS macro to output an unformatted function pointer - //! data element - //! - //! @note - //! The size of the pointer depends on the macro #QS_FUN_PTR_SIZE. - //! If the size is not defined the size of pointer is assumed 4-bytes. - #define QS_FUN_PRE_(fun_) \ - (QP::QS::u32_raw_(reinterpret_cast(fun_))) -#endif - -#if (QF_EQUEUE_CTR_SIZE == 1U) - - //! Internal QS macro to output an unformatted event queue - //! counter data element - //! @note the counter size depends on the macro #QF_EQUEUE_CTR_SIZE. - #define QS_EQC_PRE_(ctr_) \ - QS::u8_raw_(static_cast(ctr_)) -#elif (QF_EQUEUE_CTR_SIZE == 2U) - #define QS_EQC_PRE_(ctr_) \ - QS::u16_raw_(static_cast(ctr_)) -#elif (QF_EQUEUE_CTR_SIZE == 4U) - #define QS_EQC_PRE_(ctr_) \ - QS::u32_raw_(static_cast(ctr_)) -#else - #error "QF_EQUEUE_CTR_SIZE not defined" -#endif - - -#if (QF_EVENT_SIZ_SIZE == 1U) - - //! Internal QS macro to output an unformatted event size - //! data element - //! - //! @note the event size depends on the macro #QF_EVENT_SIZ_SIZE. - #define QS_EVS_PRE_(size_) \ - QS::u8_raw_(static_cast(size_)) -#elif (QF_EVENT_SIZ_SIZE == 2U) - #define QS_EVS_PRE_(size_) \ - QS::u16_raw_(static_cast(size_)) -#elif (QF_EVENT_SIZ_SIZE == 4U) - #define QS_EVS_PRE_(size_) \ - QS::u32_raw_(static_cast(size_)) -#endif - - -#if (QF_MPOOL_SIZ_SIZE == 1U) - - //! Internal QS macro to output an unformatted memory pool - //! block-size data element - //! @note the block-size depends on the macro #QF_MPOOL_SIZ_SIZE. - #define QS_MPS_PRE_(size_) \ - QS::u8_raw_(static_cast(size_)) -#elif (QF_MPOOL_SIZ_SIZE == 2U) - #define QS_MPS_PRE_(size_) \ - QS::u16_raw_(static_cast(size_)) -#elif (QF_MPOOL_SIZ_SIZE == 4U) - #define QS_MPS_PRE_(size_) \ - QS::u32_raw_(static_cast(size_)) -#endif - -#if (QF_MPOOL_CTR_SIZE == 1U) - - //! Internal QS macro to output an unformatted memory pool - //! block-counter data element - //! @note the counter size depends on the macro #QF_MPOOL_CTR_SIZE. - #define QS_MPC_PRE_(ctr_) \ - QS::u8_raw_(static_cast(ctr_)) -#elif (QF_MPOOL_CTR_SIZE == 2U) - #define QS_MPC_PRE_(ctr_) \ - QS::u16_raw_(static_cast(ctr_)) -#elif (QF_MPOOL_CTR_SIZE == 4U) - #define QS_MPC_PRE_(ctr_) \ - QS::u32_raw_(static_cast(ctr_)) -#endif - - -#if (QF_TIMEEVT_CTR_SIZE == 1U) - - //! Internal QS macro to output an unformatted time event - //! tick-counter data element - //! @note the counter size depends on the macro #QF_TIMEEVT_CTR_SIZE. - #define QS_TEC_PRE_(ctr_) \ - QS::u8_raw_(static_cast(ctr_)) -#elif (QF_TIMEEVT_CTR_SIZE == 2U) - #define QS_TEC_PRE_(ctr_) \ - QS::u16_raw_(static_cast(ctr_)) -#elif (QF_TIMEEVT_CTR_SIZE == 4U) - #define QS_TEC_PRE_(ctr_) \ - QS::u32_raw_(static_cast(ctr_)) -#endif - -//! Internal QS macro to cast enumerated QS record number to uint8_t -//! -//! @note Casting from enum to unsigned char violates the MISRA-C++ 2008 rules -//! 5-2-7, 5-2-8 and 5-2-9. Encapsulating this violation in a macro allows to -//! selectively suppress this specific deviation. -#define QS_REC_NUM_(enum_) (static_cast(enum_)) - -#endif // QS_PKG_HPP_ diff --git a/src/qs_port.hpp b/src/qs_port.hpp deleted file mode 100644 index 84581cd..0000000 --- a/src/qs_port.hpp +++ /dev/null @@ -1,59 +0,0 @@ -/// @file -/// @brief QS/C++ port to ARM Cortex-M, generic compiler -/// @cond -///*************************************************************************** -/// Last updated for version 6.6.0 -/// Last updated on 2019-07-30 -/// -/// Q u a n t u m L e a P s -/// ------------------------ -/// Modern Embedded Software -/// -/// Copyright (C) 2005-2019 Quantum Leaps. All rights reserved. -/// -/// This program is open source software: you can redistribute it and/or -/// modify it under the terms of the GNU General Public License as published -/// by the Free Software Foundation, either version 3 of the License, or -/// (at your option) any later version. -/// -/// Alternatively, this program may be distributed and modified under the -/// terms of Quantum Leaps commercial licenses, which expressly supersede -/// the GNU General Public License and are specifically designed for -/// licensees interested in retaining the proprietary status of their code. -/// -/// This program is distributed in the hope that it will be useful, -/// but WITHOUT ANY WARRANTY; without even the implied warranty of -/// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -/// GNU General Public License for more details. -/// -/// You should have received a copy of the GNU General Public License -/// along with this program. If not, see . -/// -/// Contact information: -/// -/// -///*************************************************************************** -/// @endcond - -#ifndef QS_PORT_HPP -#define QS_PORT_HPP - -// QS time-stamp size in bytes -#define QS_TIME_SIZE 4U - -// object pointer size in bytes -#define QS_OBJ_PTR_SIZE 4U - -// function pointer size in bytes -#define QS_FUN_PTR_SIZE 4U - -//**************************************************************************** -// NOTE: QS might be used with or without other QP components, in which case -// the separate definitions of the macros QF_CRIT_STAT_TYPE, QF_CRIT_ENTRY, -// and QF_CRIT_EXIT are needed. In this port QS is configured to be used with -// the other QP component, by simply including "qf_port.hpp" *before* "qs.hpp". -// -#include "qf_port.hpp" // use QS with QF -#include "qs.hpp" // QS platform-independent public interface - -#endif // QS_PORT_HPP diff --git a/src/qs_rx.cpp b/src/qs_rx.cpp deleted file mode 100644 index 36bc5d4..0000000 --- a/src/qs_rx.cpp +++ /dev/null @@ -1,1293 +0,0 @@ -//$file${src::qs::qs_rx.cpp} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv -// -// Model: qpcpp.qm -// File: ${src::qs::qs_rx.cpp} -// -// This code has been generated by QM 5.2.5 . -// DO NOT EDIT THIS FILE MANUALLY. All your changes will be lost. -// -// This code is covered by the following QP license: -// License # : LicenseRef-QL-dual -// Issued to : Any user of the QP/C++ real-time embedded framework -// Framework(s) : qpcpp -// Support ends : 2023-12-31 -// License scope: -// -// Copyright (C) 2005 Quantum Leaps, LLC . -// -// SPDX-License-Identifier: GPL-3.0-or-later OR LicenseRef-QL-commercial -// -// This software is dual-licensed under the terms of the open source GNU -// General Public License version 3 (or any later version), or alternatively, -// under the terms of one of the closed source Quantum Leaps commercial -// licenses. -// -// The terms of the open source GNU General Public License version 3 -// can be found at: -// -// The terms of the closed source Quantum Leaps commercial licenses -// can be found at: -// -// Redistributions in source code must retain this top-level comment block. -// Plagiarizing this software to sidestep the license obligations is illegal. -// -// Contact information: -// -// -// -//$endhead${src::qs::qs_rx.cpp} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -//! @file -//! @brief QS receive channel services - -#define QP_IMPL // this is QP implementation -#include "qs_port.hpp" // QS port -#include "qs_pkg.hpp" // QS package-scope internal interface -#include "qf_pkg.hpp" // QF package-scope internal interface -#include "qassert.h" // QP assertions - -static_assert(QP::QS::MAX_OBJ <= 8U, "QS::MAX_OBJECT below the limit"); - -//============================================================================ -namespace { // unnamed local namespace - -Q_DEFINE_THIS_MODULE("qs_rx") - -//............................................................................ -#if (QS_OBJ_PTR_SIZE == 1U) - using QSObj = std::uint8_t; -#elif (QS_OBJ_PTR_SIZE == 2U) - 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 - -//! @cond -//! Exclude the following internals from the Doxygen documentation -//! Extended-state variables used for parsing various QS-RX Records -struct CmdVar { - std::uint32_t param1; - std::uint32_t param2; - std::uint32_t param3; - std::uint8_t idx; - std::uint8_t cmdId; -}; - -struct TickVar { - std::uint_fast8_t rate; -}; - -struct PeekVar { - std::uint16_t offs; - std::uint8_t size; - std::uint8_t num; - std::uint8_t idx; -}; - -struct PokeVar { - std::uint32_t data; - std::uint16_t offs; - std::uint8_t size; - std::uint8_t num; - std::uint8_t idx; - std::uint8_t fill; -}; - -struct FltVar { - std::uint8_t data[16]; - std::uint8_t idx; - std::uint8_t recId; // global/local -}; - -struct ObjVar { - QSObj addr; - std::uint8_t idx; - std::uint8_t kind; // see qs.hpp, enum QSpyObjKind - std::uint8_t recId; -}; - -struct EvtVar { - QP::QEvt *e; - std::uint8_t *p; - QP::QSignal sig; - std::uint16_t len; - std::uint8_t prio; - std::uint8_t idx; -}; - -// extended-state variables for the current QS-RX state -static struct ExtState { - union Variant { - CmdVar cmd; - TickVar tick; - PeekVar peek; - PokeVar poke; - FltVar flt; - ObjVar obj; - EvtVar evt; -#ifdef Q_UTEST - QP::QS::TProbe tp; -#endif // Q_UTEST - } var; - std::uint8_t state; - std::uint8_t esc; - std::uint8_t seq; - std::uint8_t chksum; -} l_rx; - -enum RxStateEnum : std::uint8_t { - ERROR_STATE, - WAIT4_SEQ, - WAIT4_REC, - WAIT4_INFO_FRAME, - WAIT4_CMD_ID, - WAIT4_CMD_PARAM1, - WAIT4_CMD_PARAM2, - WAIT4_CMD_PARAM3, - WAIT4_CMD_FRAME, - WAIT4_RESET_FRAME, - WAIT4_TICK_RATE, - WAIT4_TICK_FRAME, - WAIT4_PEEK_OFFS, - WAIT4_PEEK_SIZE, - WAIT4_PEEK_NUM, - WAIT4_PEEK_FRAME, - WAIT4_POKE_OFFS, - WAIT4_POKE_SIZE, - WAIT4_POKE_NUM, - WAIT4_POKE_DATA, - WAIT4_POKE_FRAME, - WAIT4_FILL_DATA, - WAIT4_FILL_FRAME, - WAIT4_FILTER_LEN, - WAIT4_FILTER_DATA, - WAIT4_FILTER_FRAME, - WAIT4_OBJ_KIND, - WAIT4_OBJ_ADDR, - WAIT4_OBJ_FRAME, - WAIT4_QUERY_KIND, - WAIT4_QUERY_FRAME, - WAIT4_EVT_PRIO, - WAIT4_EVT_SIG, - WAIT4_EVT_LEN, - WAIT4_EVT_PAR, - WAIT4_EVT_FRAME - -#ifdef Q_UTEST - , - WAIT4_TEST_SETUP_FRAME, - WAIT4_TEST_TEARDOWN_FRAME, - WAIT4_TEST_PROBE_DATA, - WAIT4_TEST_PROBE_ADDR, - WAIT4_TEST_PROBE_FRAME, - WAIT4_TEST_CONTINUE_FRAME -#endif // Q_UTEST -}; - -// internal helper functions... -static void rxParseData_(std::uint8_t const b) noexcept; -static void rxHandleBadFrame_(std::uint8_t const state) noexcept; -static void rxReportAck_(enum QP::QSpyRxRecords const recId) noexcept; -static void rxReportError_(std::uint8_t const code) noexcept; -static void rxReportDone_(enum QP::QSpyRxRecords const recId) noexcept; -static void rxPoke_(void) noexcept; - -//! Internal QS-RX function to take a transition in the QS-RX FSM -static inline void tran_(RxStateEnum const target) noexcept { - l_rx.state = static_cast(target); -} -//! @endcond - -} // unnamed namespace - -//============================================================================ -//$skip${QP_VERSION} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv -// Check for the minimum required QP version -#if (QP_VERSION < 700U) || (QP_VERSION != ((QP_RELEASE^4294967295U) % 0x3E8U)) -#error qpcpp version 7.0.0 or higher required -#endif -//$endskip${QP_VERSION} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -//$define${QS::QS-rx} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv -namespace QP { -namespace QS { - -//${QS::QS-rx::rxPriv_} ...................................................... -QSrx rxPriv_; - -//${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); - rxPriv_.head = 0U; - rxPriv_.tail = 0U; - - rxPriv_.currObj[QS::SM_OBJ] = nullptr; - rxPriv_.currObj[QS::AO_OBJ] = nullptr; - rxPriv_.currObj[QS::MP_OBJ] = nullptr; - rxPriv_.currObj[QS::EQ_OBJ] = nullptr; - rxPriv_.currObj[QS::TE_OBJ] = nullptr; - rxPriv_.currObj[QS::AP_OBJ] = nullptr; - - tran_(WAIT4_SEQ); - l_rx.esc = 0U; - l_rx.seq = 0U; - l_rx.chksum = 0U; - - beginRec_(static_cast(QS_OBJ_DICT)); - QS_OBJ_PRE_(&rxPriv_); - QS_STR_PRE_("QS_RX"); - endRec_(); - // no QS_REC_DONE(), because QS is not running yet - - #ifdef Q_UTEST - QP::QS::testData.tpNum = 0U; - QP::QS::testData.testTime = 0U; - #endif // Q_UTEST -} - -//${QS::QS-rx::rxGetNfree} ................................................... -std::uint16_t rxGetNfree() noexcept { - QSCtr const head = rxPriv_.head; - if (head == rxPriv_.tail) { // buffer empty? - return static_cast(rxPriv_.end - 1U); - } - else if (head < rxPriv_.tail) { - return static_cast(rxPriv_.tail - head - 1U); - } - else { - return static_cast(rxPriv_.end + rxPriv_.tail - - head - 1U); - } -} - -//${QS::QS-rx::setCurrObj} ................................................... -void setCurrObj( - std::uint8_t obj_kind, - void * obj_ptr) noexcept -{ - Q_REQUIRE_ID(100, obj_kind < Q_DIM(rxPriv_.currObj)); - rxPriv_.currObj[obj_kind] = obj_ptr; -} - -//${QS::QS-rx::queryCurrObj} ................................................. -void queryCurrObj(std::uint8_t obj_kind) noexcept { - Q_REQUIRE_ID(200, obj_kind < Q_DIM(rxPriv_.currObj)); - - if (QS::rxPriv_.currObj[obj_kind] != nullptr) { - QS_CRIT_STAT_ - QS_CRIT_E_(); - QS::beginRec_(static_cast(QS_QUERY_DATA)); - QS_TIME_PRE_(); // timestamp - QS_U8_PRE_(obj_kind); // object kind - QS_OBJ_PRE_(QS::rxPriv_.currObj[obj_kind]); // object pointer - switch (obj_kind) { - case SM_OBJ: // intentionally fall through - case AO_OBJ: - QS_FUN_PRE_( - reinterpret_cast( - QS::rxPriv_.currObj[obj_kind])->getStateHandler()); - break; - case QS::MP_OBJ: - QS_MPC_PRE_(reinterpret_cast( - QS::rxPriv_.currObj[obj_kind])->getNFree()); - QS_MPC_PRE_(reinterpret_cast( - QS::rxPriv_.currObj[obj_kind])->getNMin()); - break; - case QS::EQ_OBJ: - QS_EQC_PRE_(reinterpret_cast( - QS::rxPriv_.currObj[obj_kind])->getNFree()); - QS_EQC_PRE_(reinterpret_cast( - QS::rxPriv_.currObj[obj_kind])->getNMin()); - break; - case QS::TE_OBJ: - QS_OBJ_PRE_(reinterpret_cast( - QS::rxPriv_.currObj[obj_kind])->getAct()); - QS_TEC_PRE_(reinterpret_cast( - QS::rxPriv_.currObj[obj_kind])->getCtr()); - QS_TEC_PRE_(reinterpret_cast( - QS::rxPriv_.currObj[obj_kind])->getInterval()); - QS_SIG_PRE_(reinterpret_cast( - QS::rxPriv_.currObj[obj_kind])->sig); - QS_U8_PRE_ (reinterpret_cast( - QS::rxPriv_.currObj[obj_kind])->refCtr_); - break; - default: - break; - } - QS::endRec_(); - QS_CRIT_X_(); - - QS_REC_DONE(); // user callback (if defined) - } - else { - rxReportError_(static_cast(QS_RX_AO_FILTER)); - } -} - -//${QS::QS-rx::rxParse} ...................................................... -void rxParse() { - QSCtr tail = rxPriv_.tail; - while (rxPriv_.head != tail) { // QS-RX buffer NOT empty? - std::uint8_t b = rxPriv_.buf[tail]; - - ++tail; - if (tail == rxPriv_.end) { - tail = 0U; - } - rxPriv_.tail = tail; // update the tail to a *valid* index - - if (l_rx.esc != 0U) { // escaped byte arrived? - l_rx.esc = 0U; - b ^= QS_ESC_XOR; - - l_rx.chksum += b; - rxParseData_(b); - } - else if (b == QS_ESC) { - l_rx.esc = 1U; - } - else if (b == QS_FRAME) { - // get ready for the next frame - b = l_rx.state; // save the current state in b - l_rx.esc = 0U; - tran_(WAIT4_SEQ); - - if (l_rx.chksum == QS_GOOD_CHKSUM) { - l_rx.chksum = 0U; - rxHandleGoodFrame_(b); - } - else { // bad checksum - l_rx.chksum = 0U; - rxReportError_(0x41U); - rxHandleBadFrame_(b); - } - } - else { - l_rx.chksum += b; - rxParseData_(b); - } - } -} - -//${QS::QS-rx::rxHandleGoodFrame_} ........................................... -void rxHandleGoodFrame_(std::uint8_t const state) { - std::uint8_t i; - std::uint8_t *ptr; - QS_CRIT_STAT_ - - switch (state) { - case WAIT4_INFO_FRAME: { - // no need to report Ack or Done - QS_CRIT_E_(); - target_info_pre_(0U); // send only Target info - QS_CRIT_X_(); - break; - } - case WAIT4_RESET_FRAME: { - // no need to report Ack or Done, because Target resets - 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); - #ifdef Q_UTEST - #if Q_UTEST != 0 - QS::processTestEvts_(); // process all events produced - #endif // Q_UTEST != 0 - #endif // Q_UTEST - rxReportDone_(QS_RX_COMMAND); - break; - } - case WAIT4_TICK_FRAME: { - rxReportAck_(QS_RX_TICK); - #ifdef Q_UTEST - QTimeEvt::tick1_( - static_cast(l_rx.var.tick.rate), - &QS::rxPriv_); - #if Q_UTEST != 0 - QS::processTestEvts_(); // process all events produced - #endif // Q_UTEST != 0 - #else - QTimeEvt::tick_( - static_cast(l_rx.var.tick.rate), - &QS::rxPriv_); - #endif // Q_UTEST - rxReportDone_(QS_RX_TICK); - break; - } - case WAIT4_PEEK_FRAME: { - // no need to report Ack or Done - QS_CRIT_E_(); - QS::beginRec_(static_cast(QS_PEEK_DATA)); - ptr = static_cast( - QS::rxPriv_.currObj[QS::AP_OBJ]); - ptr = &ptr[l_rx.var.peek.offs]; - QS_TIME_PRE_(); // timestamp - QS_U16_PRE_(l_rx.var.peek.offs); // data offset - QS_U8_PRE_(l_rx.var.peek.size); // data size - QS_U8_PRE_(l_rx.var.peek.num); // number of data items - for (i = 0U; i < l_rx.var.peek.num; ++i) { - switch (l_rx.var.peek.size) { - case 1: - QS_U8_PRE_(ptr[i]); - break; - case 2: - QS_U16_PRE_( - reinterpret_cast(ptr)[i]); - break; - case 4: - QS_U32_PRE_( - reinterpret_cast(ptr)[i]); - break; - default: - break; - } - } - QS::endRec_(); - QS_CRIT_X_(); - - QS_REC_DONE(); // user callback (if defined) - break; - } - case WAIT4_POKE_DATA: { - // received less than expected poke data items - rxReportError_(static_cast(QS_RX_POKE)); - break; - } - case WAIT4_POKE_FRAME: { - rxReportAck_(QS_RX_POKE); - // no need to report done - break; - } - case WAIT4_FILL_FRAME: { - rxReportAck_(QS_RX_FILL); - ptr = static_cast( - QS::rxPriv_.currObj[QS::AP_OBJ]); - ptr = &ptr[l_rx.var.poke.offs]; - for (i = 0U; i < l_rx.var.poke.num; ++i) { - switch (l_rx.var.poke.size) { - case 1: - ptr[i] = - static_cast(l_rx.var.poke.data); - break; - case 2: - reinterpret_cast(ptr)[i] = - static_cast(l_rx.var.poke.data); - break; - case 4: - reinterpret_cast(ptr)[i] = - l_rx.var.poke.data; - break; - default: - break; - } - } - break; - } - case WAIT4_FILTER_FRAME: { - rxReportAck_(static_cast(l_rx.var.flt.recId)); - - // apply the received filters - if (l_rx.var.flt.recId - == static_cast(QS_RX_GLB_FILTER)) - { - for (i = 0U; - i < static_cast(sizeof(priv_.glbFilter)); - ++i) - { - priv_.glbFilter[i] = l_rx.var.flt.data[i]; - } - // leave the "not maskable" filters enabled, - // see qs.h, Miscellaneous QS records (not maskable) - // - priv_.glbFilter[0] |= 0x01U; - priv_.glbFilter[7] |= 0xFCU; - priv_.glbFilter[8] |= 0x7FU; - - // never enable the last 3 records (0x7D, 0x7E, 0x7F) - priv_.glbFilter[15] &= 0x1FU; - } - else if (l_rx.var.flt.recId - == static_cast(QS_RX_LOC_FILTER)) - { - for (i = 0U; i < Q_DIM(priv_.locFilter); ++i) { - priv_.locFilter[i] = l_rx.var.flt.data[i]; - } - // leave QS_ID == 0 always on - priv_.locFilter[0] |= 0x01U; - } - else { - rxReportError_(l_rx.var.flt.recId); - } - - // no need to report Done - break; - } - case WAIT4_OBJ_FRAME: { - i = l_rx.var.obj.kind; - if (i < static_cast(QS::MAX_OBJ)) { - if (l_rx.var.obj.recId - == static_cast(QS_RX_CURR_OBJ)) - { - rxPriv_.currObj[i] = - reinterpret_cast(l_rx.var.obj.addr); - rxReportAck_(QS_RX_CURR_OBJ); - } - else if (l_rx.var.obj.recId - == static_cast(QS_RX_AO_FILTER)) - { - if (l_rx.var.obj.addr != 0U) { - std::int_fast16_t const filter = - static_cast( - reinterpret_cast( - l_rx.var.obj.addr)->m_prio); - locFilter_((i == 0) - ? filter - :-filter); - rxReportAck_(QS_RX_AO_FILTER); - } - else { - rxReportError_(static_cast(QS_RX_AO_FILTER)); - } - } - else { - rxReportError_(l_rx.var.obj.recId); - } - } - // both SM and AO - else if (i == static_cast(QS::SM_AO_OBJ)) { - if (l_rx.var.obj.recId - == static_cast(QS_RX_CURR_OBJ)) - { - rxPriv_.currObj[SM_OBJ] - = reinterpret_cast(l_rx.var.obj.addr); - rxPriv_.currObj[AO_OBJ] - = reinterpret_cast(l_rx.var.obj.addr); - } - rxReportAck_( - static_cast(l_rx.var.obj.recId)); - } - else { - rxReportError_(l_rx.var.obj.recId); - } - break; - } - case WAIT4_QUERY_FRAME: { - queryCurrObj(l_rx.var.obj.kind); - break; - } - case WAIT4_EVT_FRAME: { - // NOTE: Ack was already reported in the WAIT4_EVT_LEN state - #ifdef Q_UTEST - QS::onTestEvt(l_rx.var.evt.e); // "massage" the event, if needed - #endif // Q_UTEST - // use 'i' as status, 0 == success,no-recycle - i = 0U; - - if (l_rx.var.evt.prio == 0U) { // publish - QActive::publish_(l_rx.var.evt.e, &QS::rxPriv_, 0U); - } - else if (l_rx.var.evt.prio < QF_MAX_ACTIVE) { - if (!QActive::registry_[l_rx.var.evt.prio]->POST_X( - l_rx.var.evt.e, - 0U, // margin - &QS::rxPriv_)) - { - // failed QACTIVE_POST() recycles the event - i = 0x80U; // failure, no recycle - } - } - else if (l_rx.var.evt.prio == 255U) { - // dispatch to the current SM object - if (QS::rxPriv_.currObj[QS::SM_OBJ] != nullptr) { - // increment the ref-ctr to simulate the situation - // when the event is just retreived from a queue. - // This is expected for the following QF::gc() call. - // - QEvt_refCtr_inc_(l_rx.var.evt.e); - - static_cast(QS::rxPriv_.currObj[QS::SM_OBJ]) - ->dispatch(l_rx.var.evt.e, 0U); - i = 0x01U; // success, recycle - } - else { - i = 0x81U; // failure, recycle - } - } - else if (l_rx.var.evt.prio == 254U) { - // init the current SM object" - if (QS::rxPriv_.currObj[QS::SM_OBJ] != nullptr) { - // increment the ref-ctr to simulate the situation - // when the event is just retreived from a queue. - // This is expected for the following QF::gc() call. - // - QEvt_refCtr_inc_(l_rx.var.evt.e); - - static_cast(QS::rxPriv_.currObj[QS::SM_OBJ]) - ->init(l_rx.var.evt.e, 0U); - i = 0x01U; // success, recycle - } - else { - i = 0x81U; // failure, recycle - } - } - else if (l_rx.var.evt.prio == 253U) { - // post to the current AO - if (QS::rxPriv_.currObj[QS::AO_OBJ] != nullptr) { - if (!static_cast( - QS::rxPriv_.currObj[QS::AO_OBJ])->POST_X( - l_rx.var.evt.e, - 0U, // margin - &QS::rxPriv_)) - { - // failed QACTIVE_POST() recycles the event - i = 0x80U; // failure, no recycle - } - } - else { - i = 0x81U; // failure, recycle - } - } - else { - i = 0x81U; // failure, recycle - } - - #if (QF_MAX_EPOOL > 0U) - // recycle needed? - if ((i & 1U) != 0U) { - QF::gc(l_rx.var.evt.e); - } - #endif - // failure? - if ((i & 0x80U) != 0U) { - rxReportError_(static_cast(QS_RX_EVENT)); - } - else { - #ifdef Q_UTEST - #if Q_UTEST != 0 - QS::processTestEvts_(); // process all events produced - #endif // Q_UTEST != 0 - #endif // Q_UTEST - rxReportDone_(QS_RX_EVENT); - } - break; - } - - #ifdef Q_UTEST - case WAIT4_TEST_SETUP_FRAME: { - rxReportAck_(QS_RX_TEST_SETUP); - QP::QS::testData.tpNum = 0U; // clear Test-Probes - QP::QS::testData.testTime = 0U; //clear time tick - // don't clear current objects - QS::onTestSetup(); // application-specific test setup - // no need to report Done - break; - } - case WAIT4_TEST_TEARDOWN_FRAME: { - rxReportAck_(QS_RX_TEST_TEARDOWN); - QS::onTestTeardown(); // application-specific test teardown - // no need to report Done - break; - } - case WAIT4_TEST_CONTINUE_FRAME: { - rxReportAck_(QS_RX_TEST_CONTINUE); - QS::rxPriv_.inTestLoop = false; // exit the QUTest loop - // no need to report Done - break; - } - case WAIT4_TEST_PROBE_FRAME: { - rxReportAck_(QS_RX_TEST_PROBE); - Q_ASSERT_ID(815, - QP::QS::testData.tpNum - < (sizeof(QP::QS::testData.tpBuf) - / sizeof(QP::QS::testData.tpBuf[0]))); - QP::QS::testData.tpBuf[QP::QS::testData.tpNum] = l_rx.var.tp; - ++QP::QS::testData.tpNum; - // no need to report Done - break; - } - #endif // Q_UTEST - - case ERROR_STATE: { - // keep ignoring all bytes until new frame - break; - } - default: { - rxReportError_(0x47U); - break; - } - } -} - -} // namespace QS -} // namespace QP -//$enddef${QS::QS-rx} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -//============================================================================ -namespace { // unnamed local namespace - -//............................................................................ -static void rxParseData_(std::uint8_t const b) noexcept { - switch (l_rx.state) { - case WAIT4_SEQ: { - ++l_rx.seq; - if (l_rx.seq != b) { // not the expected sequence? - rxReportError_(0x42U); - l_rx.seq = b; // update the sequence - } - tran_(WAIT4_REC); - break; - } - case WAIT4_REC: { - switch (b) { - case QP::QS_RX_INFO: - tran_(WAIT4_INFO_FRAME); - break; - case QP::QS_RX_COMMAND: - tran_(WAIT4_CMD_ID); - break; - case QP::QS_RX_RESET: - tran_(WAIT4_RESET_FRAME); - break; - case QP::QS_RX_TICK: - tran_(WAIT4_TICK_RATE); - break; - case QP::QS_RX_PEEK: - if (QP::QS::rxPriv_.currObj[QP::QS::AP_OBJ] != nullptr) { - l_rx.var.peek.offs = 0U; - l_rx.var.peek.idx = 0U; - tran_(WAIT4_PEEK_OFFS); - } - else { - rxReportError_( - static_cast(QP::QS_RX_PEEK)); - tran_(ERROR_STATE); - } - break; - case QP::QS_RX_POKE: - case QP::QS_RX_FILL: - l_rx.var.poke.fill = - (b == static_cast(QP::QS_RX_FILL)) - ? 1U - : 0U; - if (QP::QS::rxPriv_.currObj[QP::QS::AP_OBJ] != nullptr) { - l_rx.var.poke.offs = 0U; - l_rx.var.poke.idx = 0U; - tran_(WAIT4_POKE_OFFS); - } - else { - rxReportError_( - (l_rx.var.poke.fill != 0U) - ? static_cast(QP::QS_RX_FILL) - : static_cast(QP::QS_RX_POKE)); - tran_(ERROR_STATE); - } - break; - case QP::QS_RX_GLB_FILTER: // intentionally fall-through - case QP::QS_RX_LOC_FILTER: - l_rx.var.flt.recId = b; - tran_(WAIT4_FILTER_LEN); - break; - case QP::QS_RX_AO_FILTER: // intentionally fall-through - case QP::QS_RX_CURR_OBJ: - l_rx.var.obj.recId = b; - tran_(WAIT4_OBJ_KIND); - break; - case QP::QS_RX_QUERY_CURR: - l_rx.var.obj.recId = - static_cast(QP::QS_RX_QUERY_CURR); - tran_(WAIT4_QUERY_KIND); - break; - case QP::QS_RX_EVENT: - tran_(WAIT4_EVT_PRIO); - break; - -#ifdef Q_UTEST - case QP::QS_RX_TEST_SETUP: - tran_(WAIT4_TEST_SETUP_FRAME); - break; - case QP::QS_RX_TEST_TEARDOWN: - tran_(WAIT4_TEST_TEARDOWN_FRAME); - break; - case QP::QS_RX_TEST_CONTINUE: - tran_(WAIT4_TEST_CONTINUE_FRAME); - break; - case QP::QS_RX_TEST_PROBE: - if (QP::QS::testData.tpNum - < static_cast( - (sizeof(QP::QS::testData.tpBuf) - / sizeof(QP::QS::testData.tpBuf[0])))) - { - l_rx.var.tp.data = 0U; - l_rx.var.tp.idx = 0U; - tran_(WAIT4_TEST_PROBE_DATA); - } - else { // the number of Test-Probes exceeded - rxReportError_( - static_cast(QP::QS_RX_TEST_PROBE)); - tran_(ERROR_STATE); - } - break; -#endif // Q_UTEST - - default: - rxReportError_(0x43U); - tran_(ERROR_STATE); - break; - } - break; - } - case WAIT4_INFO_FRAME: { - // keep ignoring the data until a frame is collected - break; - } - case WAIT4_CMD_ID: { - l_rx.var.cmd.cmdId = b; - l_rx.var.cmd.idx = 0U; - l_rx.var.cmd.param1 = 0U; - l_rx.var.cmd.param2 = 0U; - l_rx.var.cmd.param3 = 0U; - tran_(WAIT4_CMD_PARAM1); - break; - } - case WAIT4_CMD_PARAM1: { - l_rx.var.cmd.param1 |= - (static_cast(b) << l_rx.var.cmd.idx); - l_rx.var.cmd.idx += 8U; - if (l_rx.var.cmd.idx == (8U*4U)) { - l_rx.var.cmd.idx = 0U; - tran_(WAIT4_CMD_PARAM2); - } - break; - } - case WAIT4_CMD_PARAM2: { - l_rx.var.cmd.param2 |= - static_cast(b) << l_rx.var.cmd.idx; - l_rx.var.cmd.idx += 8U; - if (l_rx.var.cmd.idx == (8U*4U)) { - l_rx.var.cmd.idx = 0U; - tran_(WAIT4_CMD_PARAM3); - } - break; - } - case WAIT4_CMD_PARAM3: { - l_rx.var.cmd.param3 |= - static_cast(b) << l_rx.var.cmd.idx; - l_rx.var.cmd.idx += 8U; - if (l_rx.var.cmd.idx == (8U*4U)) { - l_rx.var.cmd.idx = 0U; - tran_(WAIT4_CMD_FRAME); - } - break; - } - case WAIT4_CMD_FRAME: { - // keep ignoring the data until a frame is collected - break; - } - case WAIT4_RESET_FRAME: { - // keep ignoring the data until a frame is collected - break; - } - case WAIT4_TICK_RATE: { - l_rx.var.tick.rate = static_cast(b); - tran_(WAIT4_TICK_FRAME); - break; - } - case WAIT4_TICK_FRAME: { - // keep ignoring the data until a frame is collected - break; - } - case WAIT4_PEEK_OFFS: { - if (l_rx.var.peek.idx == 0U) { - l_rx.var.peek.offs = static_cast(b); - l_rx.var.peek.idx += 8U; - } - else { - l_rx.var.peek.offs |= static_cast( - static_cast(b) << 8U); - tran_(WAIT4_PEEK_SIZE); - } - break; - } - case WAIT4_PEEK_SIZE: { - if ((b == 1U) || (b == 2U) || (b == 4U)) { - l_rx.var.peek.size = b; - tran_(WAIT4_PEEK_NUM); - } - else { - rxReportError_(static_cast(QP::QS_RX_PEEK)); - tran_(ERROR_STATE); - } - break; - } - case WAIT4_PEEK_NUM: { - l_rx.var.peek.num = b; - tran_(WAIT4_PEEK_FRAME); - break; - } - case WAIT4_PEEK_FRAME: { - // keep ignoring the data until a frame is collected - break; - } - case WAIT4_POKE_OFFS: { - if (l_rx.var.poke.idx == 0U) { - l_rx.var.poke.offs = static_cast(b); - l_rx.var.poke.idx = 1U; - } - else { - l_rx.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; - tran_(WAIT4_POKE_NUM); - } - else { - rxReportError_((l_rx.var.poke.fill != 0U) - ? static_cast(QP::QS_RX_FILL) - : static_cast(QP::QS_RX_POKE)); - tran_(ERROR_STATE); - } - break; - } - case WAIT4_POKE_NUM: { - if (b > 0U) { - l_rx.var.poke.num = b; - l_rx.var.poke.data = 0U; - l_rx.var.poke.idx = 0U; - tran_((l_rx.var.poke.fill != 0U) - ? WAIT4_FILL_DATA - : WAIT4_POKE_DATA); - } - else { - rxReportError_((l_rx.var.poke.fill != 0U) - ? static_cast(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) { - 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) { - rxPoke_(); - --l_rx.var.poke.num; - if (l_rx.var.poke.num == 0U) { - tran_(WAIT4_POKE_FRAME); - } - } - break; - } - case WAIT4_FILL_FRAME: { - // keep ignoring the data until a frame is collected - break; - } - case WAIT4_POKE_FRAME: { - // keep ignoring the data until a frame is collected - break; - } - case WAIT4_FILTER_LEN: { - if (b == static_cast(sizeof(l_rx.var.flt.data))) { - l_rx.var.flt.idx = 0U; - tran_(WAIT4_FILTER_DATA); - } - else { - rxReportError_(l_rx.var.flt.recId); - tran_(ERROR_STATE); - } - break; - } - case WAIT4_FILTER_DATA: { - l_rx.var.flt.data[l_rx.var.flt.idx] = b; - ++l_rx.var.flt.idx; - if (l_rx.var.flt.idx == sizeof(l_rx.var.flt.data)) { - tran_(WAIT4_FILTER_FRAME); - } - break; - } - case WAIT4_FILTER_FRAME: { - // keep ignoring the data until a frame is collected - break; - } - case WAIT4_OBJ_KIND: { - if (b <= static_cast(QP::QS::SM_AO_OBJ)) { - l_rx.var.obj.kind = b; - l_rx.var.obj.addr = 0U; - l_rx.var.obj.idx = 0U; - tran_(WAIT4_OBJ_ADDR); - } - else { - rxReportError_(l_rx.var.obj.recId); - tran_(ERROR_STATE); - } - break; - } - case WAIT4_OBJ_ADDR: { - l_rx.var.obj.addr |= - static_cast(b) << l_rx.var.obj.idx; - l_rx.var.obj.idx += 8U; - if (l_rx.var.obj.idx - == (8U * static_cast(QS_OBJ_PTR_SIZE))) - { - tran_(WAIT4_OBJ_FRAME); - } - break; - } - case WAIT4_OBJ_FRAME: { - // keep ignoring the data until a frame is collected - break; - } - case WAIT4_QUERY_KIND: { - if (b < static_cast(QP::QS::MAX_OBJ)) { - l_rx.var.obj.kind = b; - tran_(WAIT4_QUERY_FRAME); - } - else { - rxReportError_(l_rx.var.obj.recId); - tran_(ERROR_STATE); - } - break; - } - case WAIT4_QUERY_FRAME: { - // keep ignoring the data until a frame is collected - break; - } - case WAIT4_EVT_PRIO: { - l_rx.var.evt.prio = b; - l_rx.var.evt.sig = 0U; - l_rx.var.evt.idx = 0U; - tran_(WAIT4_EVT_SIG); - break; - } - case WAIT4_EVT_SIG: { - l_rx.var.evt.sig |= static_cast( - static_cast(b) << l_rx.var.evt.idx); - l_rx.var.evt.idx += 8U; - if (l_rx.var.evt.idx - == (8U *static_cast(Q_SIGNAL_SIZE))) - { - l_rx.var.evt.len = 0U; - l_rx.var.evt.idx = 0U; - tran_(WAIT4_EVT_LEN); - } - break; - } - case WAIT4_EVT_LEN: { - l_rx.var.evt.len |= static_cast( - static_cast(b) << l_rx.var.evt.idx); - l_rx.var.evt.idx += 8U; - if (l_rx.var.evt.idx == (8U * 2U)) { - if ((l_rx.var.evt.len + sizeof(QP::QEvt)) - <= static_cast( - QP::QF::poolGetMaxBlockSize())) - { - // report Ack before generating any other QS records - rxReportAck_(QP::QS_RX_EVENT); - - l_rx.var.evt.e = QP::QF::newX_( - (static_cast(l_rx.var.evt.len) - + sizeof(QP::QEvt)), - 0U, // margin - static_cast(l_rx.var.evt.sig)); - // event allocated? - if (l_rx.var.evt.e != nullptr) { - l_rx.var.evt.p = - reinterpret_cast(l_rx.var.evt.e); - l_rx.var.evt.p = &l_rx.var.evt.p[sizeof(QP::QEvt)]; - if (l_rx.var.evt.len > 0U) { - tran_(WAIT4_EVT_PAR); - } - else { - tran_(WAIT4_EVT_FRAME); - } - } - else { - rxReportError_( - static_cast(QP::QS_RX_EVENT)); - tran_(ERROR_STATE); - } - } - else { - rxReportError_( - static_cast(QP::QS_RX_EVENT)); - tran_(ERROR_STATE); - } - } - break; - } - case WAIT4_EVT_PAR: { // event parameters - *l_rx.var.evt.p = b; - ++l_rx.var.evt.p; - --l_rx.var.evt.len; - if (l_rx.var.evt.len == 0U) { - tran_(WAIT4_EVT_FRAME); - } - break; - } - case WAIT4_EVT_FRAME: { - // keep ignoring the data until a frame is collected - break; - } - -#ifdef Q_UTEST - case WAIT4_TEST_SETUP_FRAME: { - // keep ignoring the data until a frame is collected - break; - } - case WAIT4_TEST_TEARDOWN_FRAME: { - // keep ignoring the data until a frame is collected - break; - } - case WAIT4_TEST_CONTINUE_FRAME: { - // keep ignoring the data until a frame is collected - break; - } - case WAIT4_TEST_PROBE_DATA: { - l_rx.var.tp.data |= - (static_cast(b) << l_rx.var.tp.idx); - l_rx.var.tp.idx += 8U; - if (l_rx.var.tp.idx == (8U * sizeof(std::uint32_t))) { - l_rx.var.tp.addr = 0U; - l_rx.var.tp.idx = 0U; - tran_(WAIT4_TEST_PROBE_ADDR); - } - break; - } - case WAIT4_TEST_PROBE_ADDR: { - l_rx.var.tp.addr |= - (static_cast(b) << l_rx.var.tp.idx); - l_rx.var.tp.idx += 8U; - if (l_rx.var.tp.idx - == (8U * static_cast(QS_FUN_PTR_SIZE))) - { - tran_(WAIT4_TEST_PROBE_FRAME); - } - break; - } - case WAIT4_TEST_PROBE_FRAME: { - // keep ignoring the data until a frame is collected - break; - } -#endif // Q_UTEST - - case ERROR_STATE: { - // keep ignoring the data until a good frame is collected - break; - } - default: { // unexpected or unimplemented state - rxReportError_(0x45U); - tran_(ERROR_STATE); - break; - } - } -} - -//............................................................................ -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); -#if (QF_MAX_EPOOL > 0U) - QP::QF::gc(l_rx.var.evt.e); // don't leak an allocated event -#endif - break; - } - default: { - break; - } - } -} - -//............................................................................ -static void rxReportAck_(enum QP::QSpyRxRecords const recId) noexcept { - QS_CRIT_STAT_ - QS_CRIT_E_(); - QP::QS::beginRec_(static_cast(QP::QS_RX_STATUS)); - QS_U8_PRE_(recId); // record ID - QP::QS::endRec_(); - QS_CRIT_X_(); - - QS_REC_DONE(); // user callback (if defined) -} - -//............................................................................ -static void rxReportError_(std::uint8_t const code) noexcept { - QS_CRIT_STAT_ - QS_CRIT_E_(); - QP::QS::beginRec_(static_cast(QP::QS_RX_STATUS)); - QS_U8_PRE_(0x80U | code); // error code - QP::QS::endRec_(); - QS_CRIT_X_(); - - QS_REC_DONE(); // user callback (if defined) -} - -//............................................................................ -static void rxReportDone_(enum QP::QSpyRxRecords const recId) noexcept { - QS_CRIT_STAT_ - QS_CRIT_E_(); - QP::QS::beginRec_(static_cast(QP::QS_TARGET_DONE)); - QS_TIME_PRE_(); // timestamp - QS_U8_PRE_(recId); // record ID - QP::QS::endRec_(); - QS_CRIT_X_(); - - QS_REC_DONE(); // user callback (if defined) -} - -//............................................................................ -static void rxPoke_(void) noexcept { - std::uint8_t * ptr = - static_cast(QP::QS::rxPriv_.currObj[QP::QS::AP_OBJ]); - ptr = &ptr[l_rx.var.poke.offs]; - switch (l_rx.var.poke.size) { - case 1: - *ptr = static_cast(l_rx.var.poke.data); - break; - case 2: - *reinterpret_cast(ptr) - = static_cast(l_rx.var.poke.data); - break; - case 4: - *reinterpret_cast(ptr) = l_rx.var.poke.data; - break; - default: - Q_ERROR_ID(900); - break; - } - - l_rx.var.poke.data = 0U; - l_rx.var.poke.idx = 0U; - l_rx.var.poke.offs += static_cast(l_rx.var.poke.size); -} - -} // unnamed namespace diff --git a/src/qsafe.h b/src/qsafe.h new file mode 100644 index 0000000..df6bf21 --- /dev/null +++ b/src/qsafe.h @@ -0,0 +1,125 @@ +//============================================================================ +// SafeQP/C Real-Time Embedded Framework (RTEF) +// Version 8.0.2 +// +// Copyright (C) 2005 Quantum Leaps, LLC. All rights reserved. +// +// Q u a n t u m L e a P s +// ------------------------ +// Modern Embedded Software +// +// SPDX-License-Identifier: LicenseRef-QL-commercial +// +// This software is licensed under the terms of the Quantum Leaps commercial +// licenses. Please contact Quantum Leaps for more information about the +// available licensing options. +// +// RESTRICTIONS +// You may NOT : +// (a) redistribute, encumber, sell, rent, lease, sublicense, or otherwise +// transfer rights in this software, +// (b) remove or alter any trademark, logo, copyright or other proprietary +// notices, legends, symbols or labels present in this software, +// (c) plagiarize this software to sidestep the licensing obligations. +// +// Quantum Leaps contact information: +// +// +//============================================================================ +#ifndef QSAFE_H_ +#define QSAFE_H_ + +// QF-FuSa enabled =========================================================== +#ifndef Q_UNSAFE + +#ifndef QF_CRIT_STAT + #define QF_CRIT_STAT +#endif + +#ifndef QF_CRIT_ENTRY + #define QF_CRIT_ENTRY() ((void)0) +#endif + +#ifndef QF_CRIT_EXIT + #define QF_CRIT_EXIT() ((void)0) +#endif + +#define Q_DEFINE_THIS_MODULE(name_) \ + static char const Q_this_module_[] = name_; + +#define Q_ASSERT_INCRIT(id_, expr_) \ + ((expr_) ? ((void)0) : Q_onError(&Q_this_module_[0], (id_))) + +#define Q_ERROR_INCRIT(id_) \ + (Q_onError(&Q_this_module_[0], (id_))) + +#define Q_ASSERT_ID(id_, expr_) do { \ + QF_CRIT_STAT \ + QF_CRIT_ENTRY(); \ + (expr_) ? ((void)0) : Q_onError(&Q_this_module_[0], (id_)); \ + QF_CRIT_EXIT(); \ +} while (false) + +#define Q_ERROR_ID(id_) do { \ + QF_CRIT_STAT \ + QF_CRIT_ENTRY(); \ + Q_onError(&Q_this_module_[0], (id_)); \ + QF_CRIT_EXIT(); \ +} while (false) + +// QF-FuSa disabled ========================================================== +#else + +#define Q_DEFINE_THIS_MODULE(name_) +#define Q_ASSERT_INCRIT(id_, expr_) ((void)0) +#define Q_ERROR_INCRIT(id_) ((void)0) +#define Q_ASSERT_ID(id_, expr_) ((void)0) +#define Q_ERROR_ID(id_) ((void)0) + +#endif // QF-FuSa disabled + +//============================================================================ +#define Q_DEFINE_THIS_FILE Q_DEFINE_THIS_MODULE(__FILE__) +#define Q_ASSERT(expr_) Q_ASSERT_ID(__LINE__, (expr_)) +#define Q_ERROR() Q_ERROR_ID(__LINE__) +#define Q_REQUIRE_ID(id_, expr_) Q_ASSERT_ID((id_), (expr_)) +#define Q_REQUIRE(expr_) Q_ASSERT(expr_) +#define Q_REQUIRE_INCRIT(id_, expr_) Q_ASSERT_INCRIT((id_), (expr_)) +#define Q_ENSURE_ID(id_, expr_) Q_ASSERT_ID((id_), (expr_)) +#define Q_ENSURE(expr_) Q_ASSERT(expr_) +#define Q_ENSURE_INCRIT(id_, expr_) Q_ASSERT_INCRIT((id_), (expr_)) +#define Q_INVARIANT_ID(id_, expr_) Q_ASSERT_ID((id_), (expr_)) +#define Q_INVARIANT(expr_) Q_ASSERT(expr_) +#define Q_INVARIANT_INCRIT(id_, expr_) Q_ASSERT_INCRIT((id_), (expr_)) + +#ifndef Q_ASSERT_STATIC + #define Q_ASSERT_STATIC(expr_) extern char Q_static_assert_[(expr_) ? 1 : -1] +#endif // ndef Q_ASSERT_STATIC + +#ifndef Q_NORETURN + #define Q_NORETURN _Noreturn void +#endif // ndef Q_NORETURN + +// Is this header file used outside QP? +#ifndef QP_VERSION + #define Q_DIM(array_) (sizeof(array_) / sizeof((array_)[0U])) +#endif // ndef QP_VERSION + +//============================================================================ +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef QP_VERSION + typedef int int_t; +#endif // ndef QP_VERSION + +Q_NORETURN Q_onError( + char const * const module, + int_t const id); + +#ifdef __cplusplus +} +#endif + +#endif // QSAFE_H_ diff --git a/src/qstamp.cpp b/src/qstamp.cpp index c7c1c23..91f43c2 100644 --- a/src/qstamp.cpp +++ b/src/qstamp.cpp @@ -1,56 +1,34 @@ -//$file${src::qs::qstamp.cpp} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv +//============================================================================ +// QP/C++ Real-Time Embedded Framework (RTEF) +// Version 8.0.2 // -// Model: qpcpp.qm -// File: ${src::qs::qstamp.cpp} +// Copyright (C) 2005 Quantum Leaps, LLC. All rights reserved. // -// This code has been generated by QM 5.2.5 . -// DO NOT EDIT THIS FILE MANUALLY. All your changes will be lost. +// Q u a n t u m L e a P s +// ------------------------ +// Modern Embedded Software // -// This code is covered by the following QP license: -// License # : LicenseRef-QL-dual -// Issued to : Any user of the QP/C++ real-time embedded framework -// Framework(s) : qpcpp -// Support ends : 2023-12-31 -// License scope: +// SPDX-License-Identifier: LicenseRef-QL-commercial // -// Copyright (C) 2005 Quantum Leaps, LLC . +// This software is licensed under the terms of the Quantum Leaps commercial +// licenses. Please contact Quantum Leaps for more information about the +// available licensing options. // -// SPDX-License-Identifier: GPL-3.0-or-later OR LicenseRef-QL-commercial +// RESTRICTIONS +// You may NOT : +// (a) redistribute, encumber, sell, rent, lease, sublicense, or otherwise +// transfer rights in this software, +// (b) remove or alter any trademark, logo, copyright or other proprietary +// notices, legends, symbols or labels present in this software, +// (c) plagiarize this software to sidestep the licensing obligations. // -// 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: +// Quantum Leaps contact information: // // -// -//$endhead${src::qs::qstamp.cpp} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -//! @file -//! @brief Application build time-stamp -//! @note -//! This module needs to be re-compiled in every new software build. To achive -//! this, it is recommended to delete the object file (qstamp.o or qstamp.obj) -//! in the build directory before each build. (Most development tools allow -//! you to specify a pre-build action, which is the ideal place to delete -//! the qstamp object file.) - +//============================================================================ #include "qstamp.hpp" namespace QP { -//! the calendar date of the last translation of the form: "Mmm dd yyyy" char const BUILD_DATE[12] = __DATE__; - -//! the time of the last translation of the form: "hh:mm:ss" char const BUILD_TIME[9] = __TIME__; } // namespace QP diff --git a/src/qstamp.hpp b/src/qstamp.hpp index 0611c06..d3c6d2f 100644 --- a/src/qstamp.hpp +++ b/src/qstamp.hpp @@ -1,44 +1,32 @@ -//$file${include::qstamp.hpp} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv +//============================================================================ +// QP/C++ Real-Time Embedded Framework (RTEF) +// Version 8.0.2 // -// Model: qpcpp.qm -// File: ${include::qstamp.hpp} +// Copyright (C) 2005 Quantum Leaps, LLC. All rights reserved. // -// This code has been generated by QM 5.2.5 . -// DO NOT EDIT THIS FILE MANUALLY. All your changes will be lost. -// -// This code is covered by the following QP license: -// License # : LicenseRef-QL-dual -// Issued to : Any user of the QP/C++ real-time embedded framework -// Framework(s) : qpcpp -// Support ends : 2023-12-31 -// License scope: -// -// Copyright (C) 2005 Quantum Leaps, LLC . +// 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: +// This software is dual-licensed under the terms of the open-source GNU +// General Public License (GPL) or under the terms of one of the closed- +// source Quantum Leaps commercial licenses. // // Redistributions in source code must retain this top-level comment block. // Plagiarizing this software to sidestep the license obligations is illegal. // -// Contact information: +// NOTE: +// The GPL does NOT permit the incorporation of this code into proprietary +// programs. Please contact Quantum Leaps for commercial licensing options, +// which expressly supersede the GPL and are designed explicitly for +// closed-source distribution. +// +// Quantum Leaps contact information: // // -// -//$endhead${include::qstamp.hpp} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -//! @file -//! @brief Application build time-stamp interface - +//============================================================================ #ifndef QSTAMP_HPP_ #define QSTAMP_HPP_