From 6526f3661ddce18195bfba686d7a97c9e7317d4e Mon Sep 17 00:00:00 2001 From: Victor Chavez Date: Tue, 23 May 2023 01:36:54 +0200 Subject: [PATCH] Updated to QP CPP v7.2.2 --- examples/blinky_bsp-esp32/bsp.cpp | 4 +- examples/dpp_bsp-esp32/bsp.cpp | 4 +- library.properties | 4 +- src/qassert.h | 611 +++---- src/qep.hpp | 1217 ++++++++------ src/qep_hsm.cpp | 613 ++++--- src/qep_msm.cpp | 425 ++--- src/qequeue.hpp | 457 +++--- src/qf.hpp | 2511 ++++++++++++++++++++--------- src/qf_act.cpp | 229 +-- src/qf_actq.cpp | 385 ++--- src/qf_defer.cpp | 179 +- src/qf_dyn.cpp | 353 ++-- src/qf_mem.cpp | 301 ++-- src/qf_pkg.hpp | 304 ++-- src/qf_port.cpp | 109 +- src/qf_port.hpp | 12 +- src/qf_ps.cpp | 321 ++-- src/qf_qact.cpp | 300 +++- src/qf_qeq.cpp | 263 ++- src/qf_qmact.cpp | 156 +- src/qf_time.cpp | 675 ++++---- src/qmpool.hpp | 337 ++-- src/qpcpp.hpp | 206 +-- src/qs.cpp | 1076 ++++++------ src/qs.hpp | 2174 +++++++++++++++---------- src/qs_dummy.hpp | 145 +- src/qs_fp.cpp | 113 +- src/qs_pkg.hpp | 234 +-- src/qs_rx.cpp | 1669 +++++++++---------- src/qstamp.cpp | 57 +- src/qstamp.hpp | 51 +- 32 files changed, 8590 insertions(+), 6905 deletions(-) diff --git a/examples/blinky_bsp-esp32/bsp.cpp b/examples/blinky_bsp-esp32/bsp.cpp index e7c6c83..67aa8de 100644 --- a/examples/blinky_bsp-esp32/bsp.cpp +++ b/examples/blinky_bsp-esp32/bsp.cpp @@ -36,7 +36,7 @@ static void tickHook_ESP32(void) { BaseType_t xHigherPriorityTaskWoken = pdFALSE; /* process time events for rate 0 */ - QF::TICK_FROM_ISR(&xHigherPriorityTaskWoken, &l_TickHook); + QTimeEvt::TICK_X_FROM_ISR(0U, &xHigherPriorityTaskWoken, &l_TickHook); /* notify FreeRTOS to perform context switch from ISR, if needed */ if(xHigherPriorityTaskWoken) { portYIELD_FROM_ISR(); @@ -165,4 +165,4 @@ void QP::QS::onFlush(void) { //............................................................................ void QP::QS::onReset(void) { esp_restart(); -} +} \ No newline at end of file diff --git a/examples/dpp_bsp-esp32/bsp.cpp b/examples/dpp_bsp-esp32/bsp.cpp index 0773bc6..08c1de4 100644 --- a/examples/dpp_bsp-esp32/bsp.cpp +++ b/examples/dpp_bsp-esp32/bsp.cpp @@ -27,6 +27,7 @@ static constexpr unsigned LED_BUILTIN=13U; #endif using namespace QP; +static uint8_t const l_TickHook = static_cast(0); //............................................................................ // QS facilities @@ -49,7 +50,7 @@ static void tickHook_ESP32(void) { BaseType_t xHigherPriorityTaskWoken = pdFALSE; /* process time events for rate 0 */ - QF::TICK_FROM_ISR(&xHigherPriorityTaskWoken, &l_TIMER_ID); + QTimeEvt::tickFromISR_(0U,&xHigherPriorityTaskWoken, &l_TickHook); /* notify FreeRTOS to perform context switch from ISR, if needed */ if(xHigherPriorityTaskWoken) { portYIELD_FROM_ISR(); @@ -167,6 +168,7 @@ void QSpy_Task(void *) { 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 */ diff --git a/library.properties b/library.properties index bd76811..db015fd 100644 --- a/library.properties +++ b/library.properties @@ -1,9 +1,9 @@ name=QPESP32 -version=0.1.5 +version=0.2.0 author=Victor Chavez maintainer=Victor Chavez sentence=QP/C++ Port for ESP32. -paragraph=QP/C++ Port for the ESP32. Based on v6.9.4 from QP/C++ +paragraph=QP/C++ Port for the ESP32. Based on v7.2.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 index a88bbfa..43aafec 100644 --- a/src/qassert.h +++ b/src/qassert.h @@ -1,340 +1,395 @@ -/** -* @file -* @brief Customizable and memory-efficient assertions for embedded systems -* @cond -****************************************************************************** -* Last updated for version 6.8.2 -* Last updated on 2020-07-07 -* -* Q u a n t u m L e a P s -* ------------------------ -* Modern Embedded Software -* -* Copyright (C) 2005-2020 Quantum Leaps, LLC. All rights reserved. -* -* This program is open source software: you can redistribute it and/or -* modify it under the terms of the GNU General Public License as published -* by the Free Software Foundation, either version 3 of the License, or -* (at your option) any later version. -* -* Alternatively, this program may be distributed and modified under the -* terms of Quantum Leaps commercial licenses, which expressly supersede -* the GNU General Public License and are specifically designed for -* licensees interested in retaining the proprietary status of their code. -* -* This program is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU General Public License for more details. -* -* You should have received a copy of the GNU General Public License -* along with this program. If not, see . +/*$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: * * -****************************************************************************** -* @endcond */ -#ifndef QASSERT_H -#define QASSERT_H - -/** +/*$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. +* @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 Q_NASSERT /* Q_NASSERT defined--assertion checking disabled */ - - /* provide dummy (empty) definitions that don't generate any code... */ - #define Q_DEFINE_THIS_FILE - #define Q_DEFINE_THIS_MODULE(name_) - #define Q_ASSERT(test_) ((void)0) - #define Q_ASSERT_ID(id_, test_) ((void)0) - #define Q_ALLEGE(test_) ((void)(test_)) - #define Q_ALLEGE_ID(id_, test_) ((void)(test_)) - #define Q_ERROR() ((void)0) - #define Q_ERROR_ID(id_) ((void)0) - -#else /* Q_NASSERT not defined--assertion checking enabled */ - -#ifndef QP_VERSION /* is quassert.h used outside QP? */ - - /* provide typedefs so that qassert.h could be used "standalone"... */ - - /*! typedef for character strings. */ - /** - * @description - * This typedef specifies character type for exclusive use in character - * strings. Use of this type, rather than plain 'char', is in compliance - * with the MISRA-C 2004 Rules 6.1(req), 6.3(adv). - */ - typedef char char_t; - - /*! typedef for assertions-ids and line numbers in assertions. */ - /** - * @description - * This typedef specifies integer type for exclusive use in assertions. - * Use of this type, rather than plain 'int', is in compliance - * with the MISRA-C 2004 Rules 6.1(req), 6.3(adv). - */ - typedef int int_t; - +#ifdef __cplusplus + extern "C" { #endif - /*! Define the file name (with `__FILE__`) for assertions in this file. */ - /** - * @description - * Macro to be placed at the top of each C/C++ module to define the - * single instance of the file name string to be used in reporting - * assertions in this module. - * - * @note The file name string literal is defined by means of the standard - * preprocessor macro `__FILE__`. However, please note that, depending - * on the compiler, the `__FILE__` macro might contain the whole path name - * to the file, which might be inconvenient to log assertions. - * @note This macro should __not__ be terminated by a semicolon. - * @sa Q_DEFINE_THIS_MODULE() - */ - #define Q_DEFINE_THIS_FILE \ - static char_t const Q_this_module_[] = __FILE__; - - /*! Define the user-specified module name for assertions in this file. */ - /** - * @description - * Macro to be placed at the top of each C/C++ module to define the - * single instance of the module name string to be used in reporting - * assertions in this module. This macro takes the user-supplied parameter - * @p name_ instead of `__FILE__` to precisely control the name of the - * module. - * - * @param[in] name_ string constant representing the module name - * - * @note This macro should __not__ be terminated by a semicolon. - */ - #define Q_DEFINE_THIS_MODULE(name_) \ - static char_t const Q_this_module_[] = name_; +#ifndef Q_NASSERT +/*$declare${DbC::active} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv*/ - /*! General purpose assertion. */ - /** - * @description - * Makes sure the @p test_ parameter is TRUE. Calls the Q_onAssert() - * callback if the @p test_ expression evaluates to FALSE. This - * macro identifies the assertion location within the file by means - * of the standard `__LINE__` macro. - * - * @param[in] test_ Boolean expression - * - * @note the @p test_ is __not__ evaluated if assertions are disabled - * with the #Q_NASSERT switch. - */ - #define Q_ASSERT(test_) ((test_) \ - ? (void)0 : Q_onAssert(&Q_this_module_[0], __LINE__)) +/*${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_; - /*! General purpose assertion with user-specified assertion-id. */ - /** - * @description - * Makes sure the @p test_ parameter is TRUE. Calls the Q_onAssert() - * callback if the @p test_ evaluates to FALSE. This assertion takes the - * user-supplied parameter @p id_ to identify the location of this - * assertion within the file. This avoids the volatility of using line - * numbers, which change whenever a line of code is added or removed - * upstream from the assertion. - * - * @param[in] id_ ID number (unique within the module) of the assertion - * @param[in] test_ Boolean expression - * - * @note the @p test_ expression is __not__ evaluated if assertions are - * disabled with the #Q_NASSERT switch. - */ - #define Q_ASSERT_ID(id_, test_) ((test_) \ - ? (void)0 : Q_onAssert(&Q_this_module_[0], (id_))) +/*${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_))) - /*! General purpose assertion that __always__ evaluates the @p test_ - * expression. */ - /** - * @description - * Like the Q_ASSERT() macro, except it __always__ evaluates the @p test_ - * expression even when assertions are disabled with the #Q_NASSERT macro. - * However, when the #Q_NASSERT macro is defined, the Q_onAssert() - * callback is __not__ called, even if @p test_ evaluates to FALSE. - * - * @param[in] test_ Boolean expression (__always__ evaluated) - * - * @sa #Q_ALLEGE_ID - */ - #define Q_ALLEGE(test_) Q_ASSERT(test_) +/*${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_)) - /*! General purpose assertion with user-specified assertion-id that - * __always__ evaluates the @p test_ expression. */ - /** - * @description - * Like the Q_ASSERT_ID() macro, except it __always__ evaluates the - * @p test_ expression even when assertions are disabled with the - * #Q_NASSERT macro. However, when the #Q_NASSERT macro is defined, the - * Q_onAssert() callback is __not__ called, even if @p test_ evaluates - * to FALSE. - * - * @param[in] id_ ID number (unique within the module) of the assertion - * @param[in] test_ Boolean expression - */ - #define Q_ALLEGE_ID(id_, test_) Q_ASSERT_ID((id_), (test_)) +/*${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*/ - /*! Assertion for a wrong path through the code. */ - /** - * @description - * Calls the Q_onAssert() callback if ever executed. - * - * @note Does noting if assertions are disabled with the #Q_NASSERT switch. - */ - #define Q_ERROR() \ - Q_onAssert(&Q_this_module_[0], __LINE__) +/*${DbC::inactive::Q_DEFINE_THIS_MODULE} ...................................*/ +/*! inactive version of Q_DEFINE_THIS_MODULE() */ +#define Q_DEFINE_THIS_MODULE(name_) - /*! Assertion with user-specified assertion-id for a wrong path. */ - /** - * @description - * Calls the Q_onAssert() callback if ever executed. This assertion - * takes the user-supplied parameter @p id_ to identify the location of - * this assertion within the file. This avoids the volatility of using - * line numbers, which change whenever a line of code is added or removed - * upstream from the assertion. - * - * @param[in] id_ ID number (unique within the module) of the assertion - * - * @note Does noting if assertions are disabled with the #Q_NASSERT switch. - */ - #define Q_ERROR_ID(id_) \ - Q_onAssert(&Q_this_module_[0], (id_)) +/*${DbC::inactive::Q_ASSERT_ID} ............................................*/ +/*! inactive version of Q_ASSERT_ID() */ +#define Q_ASSERT_ID(id_, expr_) ((void)0) -#endif /* Q_NASSERT */ +/*${DbC::inactive::Q_ERROR_ID} .............................................*/ +/*! inactive version of Q_ERROR_ID() */ +#define Q_ERROR_ID(id_) ((void)0) -/****************************************************************************/ -#ifdef __cplusplus - extern "C" { +/*${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 -#ifndef Q_NORETURN - /*! no-return function specifier */ - #define Q_NORETURN void -#endif /* Q_NORETURN */ +/*$declare1${DbC} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv*/ -/*! Callback function invoked in case of any assertion failure. */ -/** -* @description -* This is an application-specific callback function needs to be defined in -* the application to perform the clean system shutdown and perhaps a reset. -* -* @param[in] module name of the file/module in which the assertion failed -* (constant, zero-terminated C string) -* @param[in] location location of the assertion within the module. This could -* be a line number or a user-specified ID-number. +/*${DbC::Q_DEFINE_THIS_FILE} ...............................................*/ +/*! Define the file name (with `__FILE__`) for assertions in this file * -* @note This callback function should _not_ return, as continuation after -* an assertion failure does not make sense. +* @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 Q_onAssert() function is the last line of defense after the -* system failure and its implementation shouild be very __carefully__ -* designed and __tested__ under various fault conditions, including but -* not limited to: stack overflow, stack corruption, or calling Q_onAssert() -* from an interrupt. +* @note +* The file name string literal is defined by means of the standard +* preprocessor macro `__FILE__`. However, please note that, depending +* on the compiler, the `__FILE__` macro might contain the whole path name +* to the file, which might be inconvenient to log assertions. * -* @note It is typically a __bad idea__ to implement Q_onAssert() as an -* endless loop that ties up the CPU. During debuggin, Q_onAssert() is an -* ideal place to put a breakpoint. +* @attention +* This macro should **not** be terminated by a semicolon. * -* Called by the following macros: #Q_ASSERT, #Q_REQUIRE, #Q_ENSURE, -* #Q_ERROR, #Q_ALLEGE as well as #Q_ASSERT_ID, #Q_REQUIRE_ID, #Q_ENSURE_ID, -* #Q_ERROR_ID, and #Q_ALLEGE_ID. +* @sa Q_DEFINE_THIS_MODULE() */ -Q_NORETURN Q_onAssert(char_t const * const module, int_t const location); +#define Q_DEFINE_THIS_FILE Q_DEFINE_THIS_MODULE(__FILE__) -#ifdef __cplusplus - } -#endif +/*${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_)) -/*! Assertion for checking preconditions. */ -/** -* @description -* This macro is equivalent to #Q_ASSERT, except the name provides a better -* documentation of the intention of this assertion. +/*${DbC::Q_ERROR} ..........................................................*/ +/*! Assertion for a wrong path through the code * -* @param[in] test_ Boolean expression +* @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_REQUIRE(test_) Q_ASSERT(test_) +#define Q_ERROR() Q_ERROR_ID(__LINE__) -/*! Assertion for checking preconditions with user-specified assertion-id. */ -/** -* @description -* Equivalent to #Q_ASSERT_ID, except the macro name provides a better +/*${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] test_ Boolean expression +* @param[in] expr_ Boolean expression */ -#define Q_REQUIRE_ID(id_, test_) Q_ASSERT_ID((id_), (test_)) +#define Q_REQUIRE_ID(id_, expr_) Q_ASSERT_ID((id_), (expr_)) -/*! Assertion for checking postconditions. */ -/** Equivalent to #Q_ASSERT, except the macro name provides a better -* documentation of the intention of this assertion. +/*${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] test_ Boolean expression +* @param[in] expr_ Boolean expression */ -#define Q_ENSURE(test_) Q_ASSERT(test_) +#define Q_REQUIRE(expr_) Q_ASSERT(expr_) -/*! Assertion for checking postconditions with user-specified assertion-id. */ -/** -* @description -* Equivalent to #Q_ASSERT_ID, except the name provides a better documentation +/*${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] test_ Boolean expression +* @param[in] expr_ Boolean expression */ -#define Q_ENSURE_ID(id_, test_) Q_ASSERT_ID((id_), (test_)) +#define Q_ENSURE_ID(id_, expr_) Q_ASSERT_ID((id_), (expr_)) -/*! Assertion for checking invariants. */ -/** -* @description -* Equivalent to #Q_ASSERT, except the macro name provides a better -* documentation of the intention of this assertion. +/*${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] test_ Boolean expression +* @param[in] expr_ Boolean expression */ -#define Q_INVARIANT(test_) Q_ASSERT(test_) +#define Q_ENSURE(expr_) Q_ASSERT(expr_) -/*! Assertion for checking invariants with user-specified assertion-id. */ -/** -* @description -* Equivalent to #Q_ASSERT_ID, except the macro name provides a better +/*${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] test_ Boolean expression +* @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_INVARIANT_ID(id_, test_) Q_ASSERT_ID((id_), (test_)) +#define Q_ALLEGE(expr_) Q_ALLEGE_ID(__LINE__, (expr_)) -/*! Static (compile-time) assertion. */ -/** -* @description +/*${DbC::Q_ASSERT_STATIC} ..................................................*/ +/*! Static (compile-time) assertion. +* +* @details * This type of assertion deliberately causes a compile-time error when -* the @p test_ evaluates to FALSE. The macro exploits the fact that in C/C++ -* a dimension of an array cannot be negative. The compile-time assertion has -* no runtime side effects. +* 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. * -* @param[in] test_ Compile-time Boolean expression +* @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_ASSERT_STATIC(test_) \ - extern int_t Q_assert_static[(test_) ? 1 : -1] +#define Q_NORETURN _Noreturn void +#endif /* ndef Q_NORETURN */ -#define Q_ASSERT_COMPILE(test_) Q_ASSERT_STATIC(test_) +/*${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 */ -/*! Helper macro to calculate static dimension of a 1-dim @p array_ */ +/*${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} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ -#endif /* QASSERT_H */ +#ifdef __cplusplus + } +#endif +#endif /* QASSERT_H_ */ diff --git a/src/qep.hpp b/src/qep.hpp index ac5bac6..e578321 100644 --- a/src/qep.hpp +++ b/src/qep.hpp @@ -1,240 +1,262 @@ -/// @file -/// @brief QEP/C++ platform-independent public interface. -/// @ingroup qep -/// @cond -///*************************************************************************** -/// Last updated for version 6.9.4 -/// Last updated on 2022-01-14 -/// -/// Q u a n t u m L e a P s -/// ------------------------ -/// Modern Embedded Software -/// -/// Copyright (C) 2005-2022 Quantum Leaps. All rights reserved. -/// -/// This program is open source software: you can redistribute it and/or -/// modify it under the terms of the GNU General Public License as published -/// by the Free Software Foundation, either version 3 of the License, or -/// (at your option) any later version. -/// -/// Alternatively, this program may be distributed and modified under the -/// terms of Quantum Leaps commercial licenses, which expressly supersede -/// the GNU General Public License and are specifically designed for -/// licensees interested in retaining the proprietary status of their code. -/// -/// This program is distributed in the hope that it will be useful, -/// but WITHOUT ANY WARRANTY; without even the implied warranty of -/// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -/// GNU General Public License for more details. -/// -/// You should have received a copy of the GNU General Public License -/// along with this program. If not, see . -/// -/// Contact information: -/// -/// -///*************************************************************************** -/// @endcond - -#ifndef QEP_HPP -#define QEP_HPP - -//**************************************************************************** -//! The current QP version as a decimal constant XXYZ, where XX is a 2-digit +//$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 694U +// +#define QP_VERSION 722U -//! The current QP version number string of the form XX.Y.Z, where XX is -// a 2-digit major version number, Y is a 1-digit minor version number, -// and Z is a 1-digit release number. -#define QP_VERSION_STR "6.9.4" - -//! Encrypted current QP release (6.9.4) and date (2022-01-14) -#define QP_RELEASE 0x7CCD4229U +//! 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 -//**************************************************************************** -#ifndef Q_SIGNAL_SIZE - //! The size (in bytes) of the signal of an event. Valid values: - //! 1U, 2U, or 4U; default 2U - /// @description - /// This macro can be defined in the QEP port file (qep_port.hpp) to - /// configure the QP::QSignal type. When the macro is not defined, the - /// default of 2 bytes is applied. - #define Q_SIGNAL_SIZE 2U -#endif - -//**************************************************************************** -// Aliases for basic numerical types; MISRA-C++ 2008 rule 3-9-2(req). - -//! alias for characters and character strings. -/// @description -/// This alias specifies character type for exclusive use in character -/// strings. Use of this type, rather than plain 'char', is in compliance -/// with the MISRA-C 2004 Rules 6.1(req), 6.3(adv). -using char_t = char; +//============================================================================ +// 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; +using int_t = int; +//${glob-types::enum_t} ...................................................... //! alias for enumerations used for event signals -using enum_t = int; +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-level -/// trace records. -/// -using float32_t = float; - +//! +//! @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-level -/// trace records. -/// -using float64_t = double; - -//! Perform downcast of an event onto a subclass of QEvt @p class_ -/// @description -/// This macro encapsulates the downcast of QEvt pointers, which violates -/// MISRA-C 2004 rule 11.4(advisory). This macro helps to localize this -/// deviation. -/// -#define Q_EVT_CAST(class_) (static_cast(e)) - -//! Perform cast from unsigned integer @p uint_ to pointer of type @p type_. -/// @description -/// This macro encapsulates the cast to (type_ *), which QP ports or -/// application might use to access embedded hardware registers. -/// Such uses can trigger PC-Lint "Note 923: cast from int to pointer" -/// and this macro helps to encapsulate this deviation. -/// -#define Q_UINT2PTR_CAST(type_, uint_) (reinterpret_cast(uint_)) - -//! Initializer of static constant QEvt instances -/// @description -/// This macro encapsulates the ugly casting of enumerated signals -/// to QSignal and constants for QEvt.poolID and QEvt.refCtr_. -/// -#define QEVT_INITIALIZER(sig_) { static_cast(sig_), 0U, 0U } - - -//**************************************************************************** -//! namespace associated with the QP/C++ framework -/// @ingroup qep qf qs qv qk qxk +//! +//! @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 { - //! the current QP version number string based on QP_VERSION_STR - constexpr char_t const versionStr[]{QP_VERSION_STR}; - +//${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; -#elif (Q_SIGNAL_SIZE == 2U) - //! QSignal represents the signal of an event. - /// @description - /// The relationship between an event and a signal is as follows. A signal - /// in UML is the specification of an asynchronous stimulus that triggers - /// reactions, and as such is an essential part of an event. (The signal - /// conveys the type of the occurrence--what happened?) However, an event - /// can also contain additional quantitative information about the - /// occurrence in form of event parameters. - using QSignal = std::uint16_t; -#elif (Q_SIGNAL_SIZE == 4U) - using QSignal = std::uint32_t; -#else - #error "Q_SIGNAL_SIZE defined incorrectly, expected 1U, 2U, or 4U" -#endif - -#ifdef Q_EVT_CTOR // Provide the constructor for the QEvt class? - - //************************************************************************ - class QEvt { - public: - //! public constructor (overload for dynamic events) - QEvt(QSignal const s) noexcept - : sig(s) - // poolId_/refCtr_ intentionally uninitialized - {} - - //! public constructor (overload for static events) - enum StaticEvt : std::uint8_t { STATIC_EVT }; - constexpr QEvt(QSignal const s, StaticEvt /*dummy*/) noexcept - : sig(s), - poolId_(0U), - refCtr_(0U) - {} +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: -#ifdef Q_EVT_VIRTUAL - //! virtual destructor - virtual ~QEvt() noexcept {} -#endif // Q_EVT_VIRTUAL - - public: - QSignal sig; //!< signal of the event instance - - private: - std::uint8_t poolId_; //!< pool ID (0 for static event) - std::uint8_t volatile refCtr_; //!< reference counter - - friend class QF; - friend class QActive; - friend class QMActive; - friend class QTimeEvt; - friend class QEQueue; - friend class QTicker; - friend class QXThread; - friend std::uint8_t QF_EVT_POOL_ID_ (QEvt const * const e) noexcept; - friend std::uint8_t QF_EVT_REF_CTR_ (QEvt const * const e) noexcept; - friend void QF_EVT_REF_CTR_INC_(QEvt const * const e) noexcept; - friend void QF_EVT_REF_CTR_DEC_(QEvt const * const e) noexcept; - }; + //! signal of the event instance + //! @tr{RQP002} + QSignal sig; -#else // QEvt is a POD (Plain Old Datatype) - - //************************************************************************ - //! QEvt base class. - /// @description - /// QEvt represents events without parameters and serves as the - /// base class for derivation of events with parameters. - /// - /// @usage - /// The following example illustrates how to add an event parameter by - /// inheriting from the QEvt class. - /// @include qep_qevt.cpp - struct QEvt { - QSignal sig; //!< signal of the event instance - std::uint8_t poolId_; //!< pool ID (0 for static event) - std::uint8_t volatile refCtr_; //!< reference counter - }; + //! pool ID (0 for static, immutable event) + //! @tr{RQP003} + std::uint8_t poolId_; -#endif // Q_EVT_CTOR + //! reference counter (only used for dynamic, mutable events) + //! @tr{RQP003} + std::uint8_t volatile refCtr_; -// forward declarations... -struct QMState; -struct QMTranActTable; -class QXThread; +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); -//! Pointer to a thread-handler function +//${QEP::QXThread} ........................................................... +//! forward declaration +class QXThread; + +//${QEP::QXThreadHandler} .................................................... +//! Pointer to an extended thread-handler function using QXThreadHandler = void (*)(QXThread * const me); -//! Attribute of for the QHsm class (Hierarchical State Machine). -/// @description -/// This union represents possible values stored in the 'state' and 'temp' -/// attributes of the QHsm and QMsm classes. +//${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 @@ -243,113 +265,228 @@ union QHsmAttr { QMTranActTable const *tatbl; //!< transition-action table }; - -//**************************************************************************** -//! Hierarchical State Machine base class -/// -/// @description -/// QHsm represents a Hierarchical State Machine (HSM) with full support for -/// hierarchical nesting of states, entry/exit actions, and initial -/// transitions in any composite state. QHsm inherits QMsm without adding -/// new attributes, so it takes the same amount of RAM as QMsm. -/// @n -/// QHsm is also the base class for the QMsm state machine, which provides -/// a superior efficiency, but requries the use of the QM modeling tool to -/// generate code. -/// -/// @note -/// QHsm is not intended to be instantiated directly, but rather serves as -/// the base class for derivation of state machines in the application code. -/// -/// @usage -/// The following example illustrates how to derive a state machine class -/// from QHsm. -/// @include qep_qhsm.cpp -/// +//${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 { - QHsmAttr m_state; //!< current active state (state-variable) - QHsmAttr m_temp; //!< temporary: transition chain, target state, etc. +private: + + //! current active state (the state-variable) + QHsmAttr m_state; + + //! temporary: transition chain, target state, etc. + QHsmAttr m_temp; public: - //! virtual destructor - virtual ~QHsm(); - //! executes the top-most initial transition in QP::QHsm - virtual void init(void const * const e, - std::uint_fast8_t const qs_id); + //! Maximum nesting depth of states in HSM + static constexpr std::int_fast8_t MAX_NEST_DEPTH_{6}; - //! overloaded init(qs_id) - virtual void init(std::uint_fast8_t const qs_id) { - this->init(nullptr, qs_id); - } +private: - //! Dispatches an event to QHsm - virtual void dispatch(QEvt const * const e, - std::uint_fast8_t const qs_id); + // friends... + friend class QMsm; + friend class QActive; + friend class QMActive; + friend class QXThread; + friend class QXMutex; + friend class QXSemaphore; - //! Tests if a given state is part of the current active state - //! configuration - bool isIn(QStateHandler const s) noexcept; +#ifdef Q_UTEST + friend class QHsmDummy; +#endif // def Q_UTEST - //! Obtain the current state (state handler function) - //! @note used in the QM code generation - QStateHandler state(void) const noexcept { - return m_state.fun; - } +#ifdef Q_UTEST + friend class QActiveDummy; +#endif // def Q_UTEST - //! Obtain the current active child state of a given parent - //! @note used in the QM code generation - QStateHandler childState(QStateHandler const parent) noexcept; +public: - //! the top-state. - static QState top(void * const me, QEvt const * const e) noexcept; + //! 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 + }; -protected: - //! Protected constructor of QHsm. - explicit QHsm(QStateHandler const initial) noexcept; + //! 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 -public: -// facilities for the QHsm implementation strategy... - //! event passed to the superstate to handle - static constexpr QState Q_RET_SUPER {static_cast(0)}; + // 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) - //! event passed to submachine superstate - static constexpr QState Q_RET_SUPER_SUB {static_cast(1)}; + // entry/exit + Q_RET_ENTRY, //!< state entry action executed + Q_RET_EXIT, //!< state exit action executed - //! event unhandled due to a guard evaluating to 'false' - static constexpr QState Q_RET_UNHANDLED {static_cast(2)}; + // no side effects + Q_RET_NULL, //!< return value without any effect - //! event handled (internal transition) - static constexpr QState Q_RET_HANDLED {static_cast(3)}; + // 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 - //! event silently ignored (bubbled up to top) - static constexpr QState Q_RET_IGNORED {static_cast(4)}; + // 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: - //! state entry action executed - static constexpr QState Q_RET_ENTRY {static_cast(5)}; + //! protected constructor of QHsm + explicit QHsm(QStateHandler const initial) noexcept; + +public: - //! state exit action executed - static constexpr QState Q_RET_EXIT {static_cast(6)}; +#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); - //! return value without any effect - static constexpr QState Q_RET_NULL {static_cast(7)}; + //! 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); + } - //! regular transition taken - static constexpr QState Q_RET_TRAN {static_cast(8)}; + //! 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; - //! initial transition taken - static constexpr QState Q_RET_TRAN_INIT {static_cast(9)}; + //! Obtain the current state (state handler function) + //! + //! @note used in the QM code generation + QStateHandler state() const noexcept { + return m_state.fun; + } - //! entry-point transition into a submachine - static constexpr QState Q_RET_TRAN_EP {static_cast(10)}; +#ifdef Q_SPY + //! Get the current state handler of the QP::QHsm + virtual QStateHandler getStateHandler() noexcept { + return m_state.fun; + } +#endif // def Q_SPY - //! transition to history of a given state - static constexpr QState Q_RET_TRAN_HIST {static_cast(11)}; + //! 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; - //! exit-point transition out of a submachine - static constexpr QState Q_RET_TRAN_XP {static_cast(12)}; + //! 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; @@ -368,13 +505,6 @@ class QHsm { return Q_RET_SUPER; } - enum ReservedHsmSignals : QSignal { - Q_ENTRY_SIG = 1, //!< signal for entry actions - Q_EXIT_SIG, //!< signal for exit actions - Q_INIT_SIG //!< signal for nested initial transitions - }; - -// protected facilities for the QMsm implementation strategy... //! Helper function to specify a regular state transition //! in a QM state-handler QState qm_tran(void const * const tatbl) noexcept { @@ -382,23 +512,24 @@ class QHsm { 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 + QState qm_tran_hist( + QMState const * const hist, + void const * const tatbl) noexcept { m_state.obj = hist; m_temp.tatbl = static_cast(tatbl); return Q_RET_TRAN_HIST; } - //! Helper function to specify an initial state transition - //! in a QM state-handler - QState qm_tran_init(void const * const tatbl) noexcept { - m_temp.tatbl = static_cast(tatbl); - return Q_RET_TRAN_INIT; - } - //! Helper function to specify a transition to an entry point //! to a submachine state in a QM state-handler QState qm_tran_ep(void const * const tatbl) noexcept { @@ -408,8 +539,9 @@ class QHsm { //! 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 + QState qm_tran_xp( + QActionHandler const xp, + void const * const tatbl) noexcept { m_state.act = xp; m_temp.tatbl = static_cast(tatbl); @@ -422,28 +554,31 @@ class QHsm { 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 - //! Get the current state handler of the HSM - virtual QStateHandler getStateHandler() noexcept; -#else - //! Helper function to specify a state entry in a QM state-handler - QState qm_entry(QMState const * const s) noexcept { - (void)s; - return Q_RET_ENTRY; - } - +#ifndef Q_SPY //! Helper function to specify a state exit in a QM state-handler QState qm_exit(QMState const * const s) noexcept { - (void)s; + static_cast(s); // unused parameter return Q_RET_EXIT; } -#endif +#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 { @@ -459,204 +594,299 @@ class QHsm { } private: - //!< maximum nesting depth of states in HSM - static constexpr std::int_fast8_t MAX_NEST_DEPTH_{6}; - //! internal helper function to take a transition in QP::QHsm - std::int_fast8_t hsm_tran(QStateHandler (&path)[MAX_NEST_DEPTH_], - std::uint_fast8_t const qs_id); + //! @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; - friend class QMsm; - friend class QActive; + // friends... friend class QMActive; - friend class QF; - friend class QS; - friend class QXK; - friend class QXThread; - friend class QXMutex; - friend class QXSemaphore; -#ifdef Q_UTEST - friend class QActiveDummy; -#endif // Q_UTEST -}; -//**************************************************************************** -//! QM State Machine implementation strategy -/// @description -/// QMsm (QM State Machine) provides a more efficient state machine -/// implementation strategy than QHsm, but requires the use of the QM -/// modeling tool, but are the fastest and need the least run-time -/// support (the smallest event-processor taking up the least code space). -/// -/// @note -/// QMsm is not intended to be instantiated directly, but rather serves as -/// the base class for derivation of state machines in the application code. -/// -/// @usage -/// The following example illustrates how to derive a state machine class -/// from QMsm. Please note that the QMsm member 'super' is defined as the -/// _first_ member of the derived struct. -/// @include qep_qmsm.cpp -/// -class QMsm : public QHsm { public: + + //! 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. - void init(void const * const e, - std::uint_fast8_t const qs_id) override; + //! 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 { - this->init(nullptr, qs_id); + QMsm::init(nullptr, qs_id); } - //! Dispatches an event to a HSM - void dispatch(QEvt const * const e, - std::uint_fast8_t const qs_id) override; + //! 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(void) const noexcept { + QMState const * stateObj() const noexcept { return m_state.obj; } //! Obtain the current active child state of a given parent (read only) - QMState const *childStateObj(QMState const * const parent) const noexcept; + //! + //! @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: - //! Protected constructor - explicit QMsm(QStateHandler const initial) noexcept; #ifdef Q_SPY //! Get the current state handler of the QMsm - QStateHandler getStateHandler() noexcept override; -#endif + QStateHandler getStateHandler() noexcept override { + return m_state.obj->stateHandler; + } +#endif // def Q_SPY private: - //! disallow inhertited isIn() function in QP::QMsm and subclasses - //! @sa QP::QMsm::isInState() - bool isIn(QStateHandler const s) noexcept = delete; - - //! disallow inhertited state() function in QP::QMsm and subclasses - //! @sa QP::QMsm::stateObj() - QStateHandler state(void) const noexcept = delete; - - //! disallow inhertited childState() function in QP::QMsm and subclasses - //! @sa QP::QMsm::childStateObj() - QStateHandler childState(QStateHandler const parent) noexcept = delete; - - //! disallow inhertited top() function in QP::QMsm and subclasses - //! @sa QP::QMsm::msm_top_s - static QState top(void * const me, QEvt const * const e) noexcept = delete; //! Internal helper function to execute a transition-action table - QState execTatbl_(QMTranActTable const * const tatbl, - std::uint_fast8_t const qs_id); + //! + //! @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 - void exitToTranSource_(QMState const *s, - QMState const * const ts, - std::uint_fast8_t const qs_id); + //! + //! @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 - QState enterHistory_(QMState const * const hist, - std::uint_fast8_t const qs_id); - - //! maximum depth of implemented entry levels for transitions to history - static constexpr std::int_fast8_t MAX_ENTRY_DEPTH_ {4}; - - //! the top state object for the QMsm - static QMState const msm_top_s; - - friend class QMActive; -}; - -//! State object for the QP::QMsm class (QM State Machine). -/// @description -/// This class groups together the attributes of a QP::QMsm state, such as -/// the parent state (state nesting), the associated state handler function -/// and the exit action handler function. These attributes are used inside -/// the QP::QMsm::dispatch() and QP::QMsm::init() functions. -/// -/// @attention -/// The QP::QMState class is only intended for the QM code generator and -/// should not be used in hand-crafted code. -struct QMState { - QMState const *superstate; //!< superstate of this state - QStateHandler const stateHandler; //!< state handler function - QActionHandler const entryAction; //!< entry action handler function - QActionHandler const exitAction; //!< exit action handler function - QActionHandler const initAction; //!< init action handler function -}; - -//! Transition-Action Table for the QP::QMsm State Machine. -struct QMTranActTable { - QMState const *target; - QActionHandler const act[1]; -}; - - -//**************************************************************************** -//! Provides miscellaneous QEP services. -class QEP { -public: - //! get the current QEP version number string of the form "X.Y.Z" - static char_t const *getVersion(void) noexcept { - return versionStr; - } -}; - -//! Offset or the user signals -constexpr enum_t Q_USER_SIG {4}; + //! + //! @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 -//**************************************************************************** -// Macros for coding QHsm-style state machines... - +//${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) -//! Macro to generate a definition of a state-handler for a given state -//! in a subclass of QP::QHsm. +//${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) {\ + 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) +#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) - +#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. -/// @description -/// This macro encapsulates the cast of a specific state handler function -/// pointer to QStateHandler, which violates MISRA-C 2004 rule 11.4(advisory). -/// This macro helps to localize this deviation. +//! +//! @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. -/// @description -/// This macro encapsulates the cast of a specific action handler function -/// pointer to QActionHandler, which violates MISRA-C2004 rule 11.4(advisory). -/// This macro helps to localize this deviation. -#define Q_ACTION_CAST(act_) (reinterpret_cast(act_)) - -//! Macro to provide strictly-typed zero-action to terminate action lists -//! in the transition-action-tables -#define Q_ACTION_NULL (nullptr) - - -//**************************************************************************** -// Macros for coding QMsm-style state machines... - +//! +//! @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_) \ @@ -664,46 +894,89 @@ constexpr enum_t Q_USER_SIG {4}; 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); \ + 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(void); \ + 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) {\ + 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_) \ +#define QM_ACTION_DEF(subclass_, action_) \ QP::QState subclass_::action_(void * const me) { \ return static_cast(me)->action_ ## _h(); } \ - QP::QState subclass_::action_ ## _h(void) + 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) +#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_UNHANDLED) +#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) +#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) - -#endif // QEP_HPP +#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 b72f71c..4afcf5e 100644 --- a/src/qep_hsm.cpp +++ b/src/qep_hsm.cpp @@ -1,40 +1,45 @@ -/// @file -/// @brief QP::QHsm implementation -/// @ingroup qep -/// @cond -///*************************************************************************** -/// Last updated for version 6.9.2 -/// Last updated on 2020-12-17 -/// -/// Q u a n t u m L e a P s -/// ------------------------ -/// Modern Embedded Software -/// -/// Copyright (C) 2005-2020 Quantum Leaps. All rights reserved. -/// -/// This program is open source software: you can redistribute it and/or -/// modify it under the terms of the GNU General Public License as published -/// by the Free Software Foundation, either version 3 of the License, or -/// (at your option) any later version. -/// -/// Alternatively, this program may be distributed and modified under the -/// terms of Quantum Leaps commercial licenses, which expressly supersede -/// the GNU General Public License and are specifically designed for -/// licensees interested in retaining the proprietary status of their code. -/// -/// This program is distributed in the hope that it will be useful, -/// but WITHOUT ANY WARRANTY; without even the implied warranty of -/// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -/// GNU General Public License for more details. -/// -/// You should have received a copy of the GNU General Public License -/// along with this program. If not, see . -/// -/// Contact information: -/// -/// -///*************************************************************************** -/// @endcond +//$file${src::qf::qep_hsm.cpp} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv +// +// Model: qpcpp.qm +// File: ${src::qf::qep_hsm.cpp} +// +// This code has been generated by QM 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::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 @@ -46,100 +51,157 @@ #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} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -//! helper macro to trigger internal event in an HSM -#define QEP_TRIG_(state_, sig_) \ - ((*(state_))(this, &QEP_reservedEvt_[sig_])) - -//! helper macro to trigger exit action in an HSM -#define QEP_EXIT_(state_) do { \ - if (QEP_TRIG_(state_, Q_EXIT_SIG) == Q_RET_HANDLED) { \ - QS_BEGIN_PRE_(QS_QEP_STATE_EXIT, qs_id) \ - QS_OBJ_PRE_(this); \ - QS_FUN_PRE_(state_); \ - QS_END_PRE_() \ - } \ -} while (false) - -//! helper macro to trigger entry action in an HSM -#define QEP_ENTER_(state_) do { \ - if (QEP_TRIG_(state_, Q_ENTRY_SIG) == Q_RET_HANDLED) { \ - QS_BEGIN_PRE_(QS_QEP_STATE_ENTRY, qs_id) \ - QS_OBJ_PRE_(this); \ - QS_FUN_PRE_(state_); \ - QS_END_PRE_() \ - } \ -} while (false) - - +//$define${QEP::versionStr[]} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv namespace QP { -Q_DEFINE_THIS_MODULE("qep_hsm") - -//**************************************************************************** -//char_t const versionStr[7] = QP_VERSION_STR; +} // namespace QP +//$enddef${QEP::versionStr[]} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -//**************************************************************************** -enum : QSignal { - //! empty signal for internal use only - QEP_EMPTY_SIG_ = 0U -}; +//============================================================================ +// unnamed namespace for local definitions with internal linkage +namespace { +Q_DEFINE_THIS_MODULE("qep_hsm") -//**************************************************************************** -/// @description -/// Static, preallocated standard events that the QEP event processor sends -/// to state handler functions of QP::QHsm and QP::QFsm subclasses to execute -/// entry actions, exit actions, and initial transitions. -/// -static QEvt const QEP_reservedEvt_[4] { +//---------------------------------------------------------------------------- +//! 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. +//! +static QP::QEvt const l_reservedEvt_[4] { #ifdef Q_EVT_CTOR // Is the QEvt constructor provided? - QEvt(0U, QEvt::STATIC_EVT), - QEvt(1U, QEvt::STATIC_EVT), - QEvt(2U, QEvt::STATIC_EVT), - QEvt(3U, QEvt::STATIC_EVT) + 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) - { 0U, 0U, 0U }, - { 1U, 0U, 0U }, - { 2U, 0U, 0U }, - { 3U, 0U, 0U } + { 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 }; +//---------------------------------------------------------------------------- +// inline helper functions + +//............................................................................ +//! 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]); +} + +//............................................................................ +//! 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 +} -//**************************************************************************** -/// @description -/// Performs the first step of HSM initialization by assigning the initial -/// pseudostate to the currently active state of the state machine. -/// -/// @param[in] initial pointer to the top-most initial state-handler -/// function in the derived state machine -/// -QHsm::QHsm(QStateHandler const initial) noexcept { +//............................................................................ +//! 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; +#else + Q_UNUSED_PAR(qs_id); + return (*state)(me, &l_reservedEvt_[QP::QHsm::Q_EXIT_SIG]); +#endif // Q_SPY +} + +} // unnamed namespace + +//$define${QEP::QHsm} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv +namespace QP { + +//${QEP::QHsm} ............................................................... + +//${QEP::QHsm::QHsm} ......................................................... +QHsm::QHsm(QStateHandler const initial) noexcept{ m_state.fun = Q_STATE_CAST(&top); m_temp.fun = initial; } -//**************************************************************************** -/// @description -/// Virtual destructor of the QHsm state machine and any of its subclasses. -/// -QHsm::~QHsm() { -} +//${QEP::QHsm::init} ......................................................... +void QHsm::init( + void const * const e, + std::uint_fast8_t const qs_id) +{ + #ifdef Q_SPY + if ((QS::priv_.flags & 0x01U) == 0U) { + QS::priv_.flags |= 0x01U; + QS_FUN_DICTIONARY(&QP::QHsm::top); + } + #else + Q_UNUSED_PAR(qs_id); + #endif -//**************************************************************************** -/// @description -/// Executes the top-most initial transition in a HSM. -/// -/// @param[in] e pointer to an extra parameter (might be NULL) -/// @param[in] qs_id QS-id of this state machine (for QS local filter) -/// -/// @note -/// Must be called exactly __once__ before the QP::QHsm::dispatch(). -/// -void QHsm::init(void const * const e, std::uint_fast8_t const qs_id) { QStateHandler t = m_state.fun; - /// @pre ctor must have been executed and initial tran NOT taken + //! @pre ctor must have been executed and initial tran NOT taken Q_REQUIRE_ID(200, (m_temp.fun != nullptr) && (t == Q_STATE_CAST(&top))); @@ -162,27 +224,26 @@ void QHsm::init(void const * const e, std::uint_fast8_t const qs_id) { std::int_fast8_t ip = 0; // entry path index path[0] = m_temp.fun; - static_cast(QEP_TRIG_(m_temp.fun, QEP_EMPTY_SIG_)); + static_cast(hsm_reservedEvt_(this, m_temp.fun, Q_EMPTY_SIG)); while (m_temp.fun != t) { ++ip; - Q_ASSERT_ID(220, - ip < static_cast(Q_DIM(path))); + Q_ASSERT_ID(220, ip < MAX_NEST_DEPTH_); path[ip] = m_temp.fun; - static_cast(QEP_TRIG_(m_temp.fun, QEP_EMPTY_SIG_)); + 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 { - QEP_ENTER_(path[ip]); // enter path[ip] + 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 = QEP_TRIG_(t, Q_INIT_SIG); // execute initial transition + r = hsm_reservedEvt_(this, t, Q_INIT_SIG); // execute initial tran. -#ifdef Q_SPY + #ifdef Q_SPY if (r == Q_RET_TRAN) { QS_BEGIN_PRE_(QS_QEP_STATE_INIT, qs_id) QS_OBJ_PRE_(this); // this state machine object @@ -190,7 +251,7 @@ void QHsm::init(void const * const e, std::uint_fast8_t const qs_id) { QS_FUN_PRE_(m_temp.fun); // the target of the initial tran. QS_END_PRE_() } -#endif // Q_SPY + #endif // Q_SPY } while (r == Q_RET_TRAN); @@ -202,49 +263,22 @@ void QHsm::init(void const * const e, std::uint_fast8_t const qs_id) { m_state.fun = t; // change the current active state m_temp.fun = t; // mark the configuration as stable - static_cast(qs_id); // unused parameter (if Q_SPY not defined) } -//***************************************************************************/ -/// @description -/// The QP::QHsm::top() state handler is the ultimate root of state hierarchy -/// in all HSMs derived from QP::QHsm. -/// -/// @param[in] me pointer to the HSM instance -/// @param[in] e pointer to the event to be dispatched to the HSM -/// -/// @returns -/// Always returns #Q_RET_IGNORED, which means that the top state ignores -/// all events. -/// -/// @note -/// The arguments to this state handler are not used. They are provided for -/// conformance with the state-handler function signature QP::QStateHandler. -/// -QState QHsm::top(void * const me, QEvt const * const e) noexcept { - static_cast(me); // unused parameter - static_cast(e); // unused parameter - return Q_RET_IGNORED; // the top state ignores all events -} +//${QEP::QHsm::dispatch} ..................................................... +void QHsm::dispatch( + QEvt const * const e, + std::uint_fast8_t const qs_id) +{ + #ifndef Q_SPY + Q_UNUSED_PAR(qs_id); + #endif -//**************************************************************************** -/// @description -/// Dispatches an event for processing to a hierarchical state machine (HSM). -/// The processing of an event represents one run-to-completion (RTC) step. -/// -/// @param[in] e pointer to the event to be dispatched to the HSM -/// @param[in] qs_id QS-id of this state machine (for QS local filter) -/// -/// @note -/// This state machine must be initialized by calling QP::QHsm::init() exactly -/// __once__ before calling QP::QHsm::dispatch(). -/// -void QHsm::dispatch(QEvt const * const e, std::uint_fast8_t const qs_id) { QStateHandler t = m_state.fun; QS_CRIT_STAT_ - /// @pre the current state must be initialized and - /// the state configuration must be stable + //! @pre the current state must be initialized and + //! the state configuration must be stable Q_REQUIRE_ID(400, (t != nullptr) && (t == m_temp.fun)); @@ -258,6 +292,7 @@ void QHsm::dispatch(QEvt const * const e, std::uint_fast8_t const qs_id) { QStateHandler s; QState r; // process the event hierarchically... + //! @tr{RQP120A} do { s = m_temp.fun; r = (*s)(this, e); // invoke state handler s @@ -270,11 +305,12 @@ void QHsm::dispatch(QEvt const * const e, std::uint_fast8_t const qs_id) { QS_FUN_PRE_(s); // the current state QS_END_PRE_() - r = QEP_TRIG_(s, QEP_EMPTY_SIG_); // find superstate of s + r = hsm_reservedEvt_(this, s, Q_EMPTY_SIG); // find superstate of s } } while (r == Q_RET_SUPER); - // transition taken? + // regular transition taken? + //! @tr{RQP120E} if (r >= Q_RET_TRAN) { QStateHandler path[MAX_NEST_DEPTH_]; @@ -283,22 +319,18 @@ void QHsm::dispatch(QEvt const * const e, std::uint_fast8_t const qs_id) { path[2] = s; // exit current state to transition source s... + //! @tr{RQP120C} for (; t != s; t = m_temp.fun) { // exit handled? - if (QEP_TRIG_(t, Q_EXIT_SIG) == Q_RET_HANDLED) { - QS_BEGIN_PRE_(QS_QEP_STATE_EXIT, qs_id) - QS_OBJ_PRE_(this); // this state machine object - QS_FUN_PRE_(t); // the exited state - QS_END_PRE_() - + if (hsm_state_exit_(this, t, qs_id)) { // find superstate of t - static_cast(QEP_TRIG_(t, QEP_EMPTY_SIG_)); + static_cast(hsm_reservedEvt_(this, t, Q_EMPTY_SIG)); } } std::int_fast8_t ip = hsm_tran(path, qs_id); // the HSM transition -#ifdef Q_SPY + #ifdef Q_SPY if (r == Q_RET_TRAN_HIST) { QS_BEGIN_PRE_(QS_QEP_TRAN_HIST, qs_id) @@ -308,17 +340,19 @@ void QHsm::dispatch(QEvt const * const e, std::uint_fast8_t const qs_id) { QS_END_PRE_() } -#endif // Q_SPY + #endif // Q_SPY - // retrace the entry path in reverse (desired) order... + // execute state entry actions in the desired order... + //! @tr{RQP120B} for (; ip >= 0; --ip) { - QEP_ENTER_(path[ip]); // enter path[ip] + hsm_state_entry_(this, path[ip], qs_id); // enter path[ip] } t = path[0]; // stick the target into register m_temp.fun = t; // update the next state // drill into the target hierarchy... - while (QEP_TRIG_(t, Q_INIT_SIG) == Q_RET_TRAN) { + //! @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 @@ -330,13 +364,14 @@ void QHsm::dispatch(QEvt const * const e, std::uint_fast8_t const qs_id) { path[0] = m_temp.fun; // find superstate - static_cast(QEP_TRIG_(m_temp.fun, QEP_EMPTY_SIG_)); + 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(QEP_TRIG_(m_temp.fun, QEP_EMPTY_SIG_)); + static_cast( + hsm_reservedEvt_(this, m_temp.fun, Q_EMPTY_SIG)); } m_temp.fun = path[0]; @@ -345,7 +380,7 @@ void QHsm::dispatch(QEvt const * const e, std::uint_fast8_t const qs_id) { // retrace the entry path in reverse (correct) order... do { - QEP_ENTER_(path[ip]); // enter path[ip] + hsm_state_entry_(this, path[ip], qs_id); // enter path[ip] --ip; } while (ip >= 0); @@ -361,7 +396,7 @@ void QHsm::dispatch(QEvt const * const e, std::uint_fast8_t const qs_id) { QS_END_PRE_() } -#ifdef Q_SPY + #ifdef Q_SPY else if (r == Q_RET_HANDLED) { QS_BEGIN_PRE_(QS_QEP_INTERN_TRAN, qs_id) @@ -382,43 +417,101 @@ void QHsm::dispatch(QEvt const * const e, std::uint_fast8_t const qs_id) { QS_END_PRE_() } -#endif // Q_SPY + #else + Q_UNUSED_PAR(qs_id); // when Q_SPY not defined + #endif // Q_SPY m_state.fun = t; // change the current active state m_temp.fun = t; // mark the configuration as stable - static_cast(qs_id); // unused parameter (if Q_SPY not defined) } -//**************************************************************************** -/// @description -/// helper function to execute transition sequence in a hierarchical state -/// machine (HSM). -/// -/// @param[in,out] path array of pointers to state-handler functions -/// to execute the entry actions -/// @param[in] qs_id QS-id of this state machine (for QS local filter) -/// -/// @returns -/// the depth of the entry path stored in the @p path parameter. -//// -std::int_fast8_t QHsm::hsm_tran(QStateHandler (&path)[MAX_NEST_DEPTH_], - std::uint_fast8_t const qs_id) +//${QEP::QHsm::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' + + // 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 + } + 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 + + 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? + 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); + } + } 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 + + return child; // return the child +} + +//${QEP::QHsm::hsm_tran} ..................................................... +std::int_fast8_t QHsm::hsm_tran( + QStateHandler (&path)[MAX_NEST_DEPTH_], + std::uint_fast8_t const qs_id) { + #ifndef Q_SPY + Q_UNUSED_PAR(qs_id); + #endif + std::int_fast8_t ip = -1; // transition entry path index - std::int_fast8_t iq; // helper transition entry path index QStateHandler t = path[0]; QStateHandler const s = path[2]; - QState r; - QS_CRIT_STAT_ // (a) check source==target (transition to self) if (s == t) { - QEP_EXIT_(s); // exit the source + // exit the source + static_cast(hsm_state_exit_(this, s, qs_id)); ip = 0; // enter the target } else { // superstate of target - static_cast(QEP_TRIG_(t, QEP_EMPTY_SIG_)); + static_cast(hsm_reservedEvt_(this, t, Q_EMPTY_SIG)); t = m_temp.fun; // (b) check source==target->super @@ -427,29 +520,30 @@ std::int_fast8_t QHsm::hsm_tran(QStateHandler (&path)[MAX_NEST_DEPTH_], } else { // superstate of src - static_cast(QEP_TRIG_(s, QEP_EMPTY_SIG_)); + static_cast(hsm_reservedEvt_(this, s, Q_EMPTY_SIG)); // (c) check source->super==target->super if (m_temp.fun == t) { - QEP_EXIT_(s); // exit the source + // exit the source + static_cast(hsm_state_exit_(this, s, qs_id)); ip = 0; // enter the target } else { // (d) check source->super==target if (m_temp.fun == path[0]) { - QEP_EXIT_(s); // exit the source + // exit the source + static_cast(hsm_state_exit_(this, s, qs_id)); } else { // (e) check rest of source==target->super->super.. // and store the entry path along the way - - iq = 0; // indicate that the LCA was not found + std::int_fast8_t iq = 0; // indicate that LCA was found ip = 1; // enter target and its superstate path[1] = t; // save the superstate of target t = m_temp.fun; // save source->super // find target->super->super - r = QEP_TRIG_(path[1], QEP_EMPTY_SIG_); + 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 @@ -464,7 +558,7 @@ std::int_fast8_t QHsm::hsm_tran(QStateHandler (&path)[MAX_NEST_DEPTH_], } // it is not the source, keep going up else { - r = QEP_TRIG_(m_temp.fun, QEP_EMPTY_SIG_); + r = hsm_reservedEvt_(this, m_temp.fun, Q_EMPTY_SIG); } } @@ -473,7 +567,8 @@ std::int_fast8_t QHsm::hsm_tran(QStateHandler (&path)[MAX_NEST_DEPTH_], // entry path must not overflow Q_ASSERT_ID(520, ip < MAX_NEST_DEPTH_); - QEP_EXIT_(s); // exit the source + // exit the source + static_cast(hsm_state_exit_(this, s, qs_id)); // (f) check the rest of source->super // == target->super->super... @@ -499,16 +594,11 @@ std::int_fast8_t QHsm::hsm_tran(QStateHandler (&path)[MAX_NEST_DEPTH_], // r = Q_RET_IGNORED; // keep looping do { - // exit t unhandled? - if (QEP_TRIG_(t, Q_EXIT_SIG) == Q_RET_HANDLED) - { - QS_BEGIN_PRE_(QS_QEP_STATE_EXIT, qs_id) - QS_OBJ_PRE_(this); - QS_FUN_PRE_(t); - QS_END_PRE_() - + // exit from t handled? + if (hsm_state_exit_(this, t, qs_id)) { + // find superstate of t static_cast( - QEP_TRIG_(t, QEP_EMPTY_SIG_)); + hsm_reservedEvt_(this, t, Q_EMPTY_SIG)); } t = m_temp.fun; // set to super of t iq = ip; @@ -530,102 +620,9 @@ std::int_fast8_t QHsm::hsm_tran(QStateHandler (&path)[MAX_NEST_DEPTH_], } } } - static_cast(qs_id); // unused parameter (if Q_SPY not defined) return ip; -} -//**************************************************************************** -#ifdef Q_SPY - QStateHandler QHsm::getStateHandler() noexcept { - return m_state.fun; - } -#endif - -//**************************************************************************** -/// @description -/// Tests if a state machine derived from QHsm is-in a given state. -/// -/// @note -/// For a HSM, to "be in a state" means also to be in a superstate of -/// of the state. -/// -/// @param[in] s pointer to the state-handler function to be tested -/// -/// @returns -/// 'true' if the HSM is in the @p state and 'false' otherwise -/// -bool QHsm::isIn(QStateHandler const s) noexcept { - - /// @pre state configuration must be stable - Q_REQUIRE_ID(600, m_temp.fun == m_state.fun); - - bool inState = false; // assume that this HSM is not in 'state' - QState r; - - // scan the state hierarchy bottom-up - do { - // do the states match? - if (m_temp.fun == s) { - inState = true; // 'true' means that match found - r = Q_RET_IGNORED; // cause breaking out of the loop - } - else { - r = QEP_TRIG_(m_temp.fun, QEP_EMPTY_SIG_); - } - } while (r != Q_RET_IGNORED); // QHsm::top() state not reached - m_temp.fun = m_state.fun; // restore the stable state configuration - - return inState; // return the status -} - -//**************************************************************************** -/// -/// @description -/// Finds the child state of the given @c parent, such that this child state -/// is an ancestor of the currently active state. The main purpose of this -/// function is to support **shallow history** transitions in state machines -/// derived from QHsm. -/// -/// @param[in] parent pointer to the state-handler function -/// -/// @returns -/// the child of a given @c parent state, which is an ancestor of the -/// currently active state -/// -/// @note -/// this function is designed to be called during state transitions, so it -/// does not necessarily start in a stable state configuration. -/// However, the function establishes stable state configuration upon exit. -/// -QStateHandler QHsm::childState(QStateHandler const parent) noexcept { - QStateHandler child = m_state.fun; // start with the current state - bool isFound = false; // start with the child not found - QState r; - - // establish stable state configuration - m_temp.fun = m_state.fun; - do { - // is this the parent of the current child? - if (m_temp.fun == parent) { - isFound = true; // child is found - r = Q_RET_IGNORED; // cause breaking out of the loop - } - else { - child = m_temp.fun; - r = QEP_TRIG_(m_temp.fun, QEP_EMPTY_SIG_); - } - } while (r != Q_RET_IGNORED); // QHsm::top() state not reached - m_temp.fun = m_state.fun; // establish stable state configuration - - /// @post the child must be confirmed - Q_ENSURE_ID(810, isFound); -#ifdef Q_NASSERT - // avoid compiler warning about unused variable - static_cast(isFound); -#endif - - return child; // return the child } } // namespace QP - +//$enddef${QEP::QHsm} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/src/qep_msm.cpp b/src/qep_msm.cpp index 909e33e..7799bf2 100644 --- a/src/qep_msm.cpp +++ b/src/qep_msm.cpp @@ -1,40 +1,43 @@ -/// @file -/// @brief QP::QMsm implementation -/// @ingroup qep -/// @cond -///*************************************************************************** -/// Last updated for version 6.9.2 -/// Last updated on 2020-12-17 -/// -/// Q u a n t u m L e a P s -/// ------------------------ -/// Modern Embedded Software -/// -/// Copyright (C) 2005-2020 Quantum Leaps. All rights reserved. -/// -/// This program is open source software: you can redistribute it and/or -/// modify it under the terms of the GNU General Public License as published -/// by the Free Software Foundation, either version 3 of the License, or -/// (at your option) any later version. -/// -/// Alternatively, this program may be distributed and modified under the -/// terms of Quantum Leaps commercial licenses, which expressly supersede -/// the GNU General Public License and are specifically designed for -/// licensees interested in retaining the proprietary status of their code. -/// -/// This program is distributed in the hope that it will be useful, -/// but WITHOUT ANY WARRANTY; without even the implied warranty of -/// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -/// GNU General Public License for more details. -/// -/// You should have received a copy of the GNU General Public License -/// along with this program. If not, see . -/// -/// Contact information: -/// -/// -///*************************************************************************** -/// @endcond +//$file${src::qf::qep_msm.cpp} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv +// +// Model: qpcpp.qm +// File: ${src::qf::qep_msm.cpp} +// +// This code has been generated by QM 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::qep_msm.cpp} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +//! @file +//! @brief QP::QMsm implementation #define QP_IMPL // this is QP implementation #include "qep_port.hpp" // QEP port @@ -46,18 +49,24 @@ #endif // Q_SPY #include "qassert.h" // QP embedded systems-friendly assertions -//! Internal macro to increment the given action table @p act_ -/// @note Incrementing a pointer violates the MISRA-C 2004 Rule 17.4(req), -/// pointer arithmetic other than array indexing. Encapsulating this violation -/// in a macro allows to selectively suppress this specific deviation. -#define QEP_ACT_PTR_INC_(act_) (++(act_)) +// unnamed namespace for local definitions with internal linkage +namespace { +Q_DEFINE_THIS_MODULE("qep_msm") +} // unnamed namespace -namespace QP { +//============================================================================ +//$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} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -Q_DEFINE_THIS_MODULE("qep_msm") +//$define${QEP::QMsm} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv +namespace QP { -//**************************************************************************** -QMState const QMsm::msm_top_s = { +//${QEP::QMsm} ............................................................... +QMState const QMsm::msm_top_s = { nullptr, nullptr, nullptr, @@ -65,44 +74,14 @@ QMState const QMsm::msm_top_s = { nullptr }; -//**************************************************************************** -/// @description -/// Performs the first step of initialization by assigning the initial -/// pseudostate to the currently active state of the state machine. -/// -/// @param[in] initial the top-most initial transition for the MSM. -/// -/// @note -/// The constructor is protected to prevent direct instantiating of the -/// QP::QMsm objects. This class is intended for subclassing only. -/// -/// @sa -/// The QP::QMsm example illustrates how to use the QMsm constructor -/// in the constructor initializer list of the derived state machines. -/// -QMsm::QMsm(QStateHandler const initial) noexcept - : QHsm(initial) -{ - m_state.obj = &msm_top_s; - m_temp.fun = initial; -} - -//**************************************************************************** -/// @description -/// Executes the top-most initial transition in a MSM. -/// -/// @param[in] e pointer to an extra parameter (might be nullptr) -/// @param[in] qs_id QS-id of this state machine (for QS local filter) -/// -/// @attention -/// QP::QMsm::init() must be called exactly __once__ before -/// QP::QMsm::dispatch() -/// -void QMsm::init(void const * const e, std::uint_fast8_t const qs_id) { - QS_CRIT_STAT_ - /// @pre the top-most initial transition must be initialized, and the - /// initial transition must not be taken yet. +//${QEP::QMsm::init} ......................................................... +void QMsm::init( + void const * const e, + std::uint_fast8_t const qs_id) +{ + //! @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)); @@ -112,6 +91,7 @@ void QMsm::init(void const * const e, std::uint_fast8_t const qs_id) { // initial tran. must be taken Q_ASSERT_ID(210, r == Q_RET_TRAN_INIT); + 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 @@ -132,29 +112,19 @@ void QMsm::init(void const * const e, std::uint_fast8_t const qs_id) { QS_FUN_PRE_(m_state.obj->stateHandler); // the new current state QS_END_PRE_() - static_cast(qs_id); // unused parameter (if Q_SPY not defined) + Q_UNUSED_PAR(qs_id); // when Q_SPY not defined } -//**************************************************************************** -/// @description -/// Dispatches an event for processing to a meta state machine (MSM). -/// The processing of an event represents one run-to-completion (RTC) step. -/// -/// @param[in] e pointer to the event to be dispatched to the MSM -/// @param[in] qs_id QS-id of this state machine (for QS local filter) -/// -/// @note -/// Must be called after QP::QMsm::init(). -/// -void QMsm::dispatch(QEvt const * const e, std::uint_fast8_t const qs_id) { +//${QEP::QMsm::dispatch} ..................................................... +void QMsm::dispatch( + QEvt const * const e, + std::uint_fast8_t const qs_id) +{ QMState const *s = m_state.obj; // store the current state - QMState const *t = s; - QState r; - QS_CRIT_STAT_ - - /// @pre current state must be initialized + //! @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 @@ -163,6 +133,8 @@ void QMsm::dispatch(QEvt const * const e, std::uint_fast8_t const qs_id) { QS_END_PRE_() // scan the state hierarchy up to the top state... + QMState const *t = s; + QState r; do { r = (*t->stateHandler)(this, e); // call state handler function @@ -197,12 +169,12 @@ void QMsm::dispatch(QEvt const * const e, std::uint_fast8_t const qs_id) { // any kind of transition taken? if (r >= Q_RET_TRAN) { -#ifdef Q_SPY + #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 + #endif // Q_SPY do { // save the transition-action table before it gets clobbered @@ -211,6 +183,7 @@ void QMsm::dispatch(QEvt const * const e, std::uint_fast8_t const qs_id) { // 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; @@ -230,29 +203,29 @@ void QMsm::dispatch(QEvt const * const e, std::uint_fast8_t const qs_id) { m_state.obj = s; // restore the original state r = (*tmp.act)(this); // execute the XP action if (r == Q_RET_TRAN) { // XP -> TRAN ? -#ifdef Q_SPY + #ifdef Q_SPY tmp.tatbl = m_temp.tatbl; // save m_temp -#endif // Q_SPY + #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 + #ifdef Q_SPY m_temp.tatbl = tmp.tatbl; // restore m_temp -#endif // Q_SPY + #endif // Q_SPY } else if (r == Q_RET_TRAN_HIST) { // XP -> HIST ? tmp.obj = m_state.obj; // save the history m_state.obj = s; // restore the original state -#ifdef Q_SPY + #ifdef Q_SPY s = m_temp.obj; // save m_temp -#endif // Q_SPY + #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 + #ifdef Q_SPY m_temp.obj = s; // restore me->temp -#endif // Q_SPY + #endif // Q_SPY s = m_state.obj; m_state.obj = tmp.obj; // restore the history } @@ -279,7 +252,7 @@ void QMsm::dispatch(QEvt const * const e, std::uint_fast8_t const qs_id) { QS_END_PRE_() } -#ifdef Q_SPY + #ifdef Q_SPY // was the event handled? else if (r == Q_RET_HANDLED) { // internal tran. source can't be nullptr @@ -304,52 +277,81 @@ void QMsm::dispatch(QEvt const * const e, std::uint_fast8_t const qs_id) { QS_END_PRE_() } -#endif // Q_SPY + #endif // Q_SPY else { // empty } - static_cast(qs_id); // unused parameter (if Q_SPY not defined) + Q_UNUSED_PAR(qs_id); // when Q_SPY not defined } -//**************************************************************************** -#ifdef Q_SPY - /// @description - /// Helper function to get the current state handler of QMsm. - /// - QStateHandler QMsm::getStateHandler() noexcept { - return m_state.obj->stateHandler; +//${QEP::QMsm::isInState} .................................................... +bool QMsm::isInState(QMState const * const st) const noexcept { + bool inState = false; // assume that this MSM is not in 'state' + + for (QMState const *s = m_state.obj; + s != nullptr; + s = s->superstate) + { + if (s == st) { + inState = true; // match found, return 'true' + break; + } } -#endif + return inState; +} -//**************************************************************************** -/// @description -/// Helper function to execute transition sequence in a tran-action table. -/// -/// @param[in] tatbl pointer to the transition-action table -/// @param[in] qs_id QS-id of this state machine (for QS local filter) -/// -/// @returns -/// the status of the last action from the transition-action table. -/// -/// @note -/// This function is for internal use inside the QEP event processor and -/// should __not__ be called directly from the applications. -/// -QState QMsm::execTatbl_(QMTranActTable const * const tatbl, - std::uint_fast8_t const qs_id) -{ - QActionHandler const *a; - QState r = Q_RET_NULL; - QS_CRIT_STAT_ +//${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; - /// @pre the transition-action table pointer must not be nullptr + for (s = m_state.obj; s != nullptr; s = s->superstate) { + 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; + } + } + } + + //! @post the child must be found + Q_ENSURE_ID(810, isFound); + + #ifdef Q_NASSERT + Q_UNUSED_PAR(isFound); + #endif + + return child; // return the child +} + +//${QEP::QMsm::execTatbl_} ................................................... +QState QMsm::execTatbl_( + QMTranActTable const * const tatbl, + std::uint_fast8_t const qs_id) +{ + //! @pre the transition-action table pointer must not be nullptr Q_REQUIRE_ID(400, tatbl != nullptr); - for (a = &tatbl->act[0]; *a != nullptr; QEP_ACT_PTR_INC_(a)) { + QState r = Q_RET_NULL; + QS_CRIT_STAT_ + for (QActionHandler const *a = &tatbl->act[0]; *a != nullptr; ++a) { r = (*(*a))(this); // call the action through the 'a' pointer -#ifdef Q_SPY + #ifdef Q_SPY if (r == Q_RET_ENTRY) { QS_BEGIN_PRE_(QS_QEP_STATE_ENTRY, qs_id) @@ -391,37 +393,28 @@ QState QMsm::execTatbl_(QMTranActTable const * const tatbl, else { // empty } -#endif // Q_SPY + #endif // Q_SPY } - static_cast(qs_id); // unused parameter (if Q_SPY not defined) - + 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; } -//**************************************************************************** -/// @description -/// Helper function to exit the current state configuration to the -/// transition source, which is a hierarchical state machine might be a -/// superstate of the current state. -/// -/// @param[in] s pointer to the current state -/// @param[in] ts pointer to the transition source state -/// @param[in] qs_id QS-id of this state machine (for QS local filter) -/// -void QMsm::exitToTranSource_(QMState const *s, - QMState const * const ts, - std::uint_fast8_t const qs_id) +//${QEP::QMsm::exitToTranSource_} ............................................ +void QMsm::exitToTranSource_( + QMState const * s, + QMState const * const ts, + std::uint_fast8_t const qs_id) { // exit states from the current state to the tran. source state while (s != ts) { // exit action provided in state 's'? if (s->exitAction != nullptr) { // execute the exit action - static_cast((*s->exitAction)(this)); + (*s->exitAction)(this); QS_CRIT_STAT_ QS_BEGIN_PRE_(QS_QEP_STATE_EXIT, qs_id) @@ -438,29 +431,18 @@ void QMsm::exitToTranSource_(QMState const *s, Q_ASSERT_ID(510, s != nullptr); } } - static_cast(qs_id); // unused parameter (if Q_SPY not defined) + Q_UNUSED_PAR(qs_id); // when Q_SPY not defined } -//**************************************************************************** -/// @description -/// Static helper function to execute the segment of transition to history -/// after entering the composite state and -/// -/// @param[in] hist pointer to the history substate -/// @param[in] qs_id QS-id of this state machine (for QS local filter) -/// -/// @returns -/// QP::Q_RET_INIT, if an initial transition has been executed in the last -/// entered state or QP::Q_RET_NULL if no such transition was taken. -/// -QState QMsm::enterHistory_(QMState const * const hist, - std::uint_fast8_t const qs_id) +//${QEP::QMsm::enterHistory_} ................................................ +QState QMsm::enterHistory_( + QMState const * const hist, + std::uint_fast8_t const qs_id) { QMState const *s = hist; QMState const *ts = m_state.obj; // transition source QMState const *epath[MAX_ENTRY_DEPTH_]; - QState r; - std::uint_fast8_t i = 0U; // entry path index + QS_CRIT_STAT_ QS_BEGIN_PRE_(QS_QEP_TRAN_HIST, qs_id) @@ -469,12 +451,12 @@ QState QMsm::enterHistory_(QMState const * const hist, QS_FUN_PRE_(hist->stateHandler); // target state handler QS_END_PRE_() + std::int_fast8_t i = 0; // entry path index while (s != ts) { if (s->entryAction != nullptr) { + Q_ASSERT_ID(620, i < MAX_ENTRY_DEPTH_); epath[i] = s; ++i; - Q_ASSERT_ID(620, - i <= static_cast(Q_DIM(epath))); } s = s->superstate; if (s == nullptr) { @@ -483,10 +465,10 @@ QState QMsm::enterHistory_(QMState const * const hist, } // retrace the entry path in reverse (desired) order... - while (i > 0U) { + while (i > 0) { --i; // run entry action in epath[i] - static_cast((*epath[i]->entryAction)(this)); + (*epath[i]->entryAction)(this); QS_BEGIN_PRE_(QS_QEP_STATE_ENTRY, qs_id) QS_OBJ_PRE_(this); @@ -497,6 +479,7 @@ QState QMsm::enterHistory_(QMState const * const hist, m_state.obj = hist; // set current state to the transition target // initial tran. present? + QState r; if (hist->initAction != nullptr) { r = (*hist->initAction)(this); // execute the transition action } @@ -504,81 +487,9 @@ QState QMsm::enterHistory_(QMState const * const hist, r = Q_RET_NULL; } - static_cast(qs_id); // unused parameter (if Q_SPY not defined) + Q_UNUSED_PAR(qs_id); // when Q_SPY not defined return r; } -//**************************************************************************** -/// @description -/// Tests if a state machine derived from QMsm is-in a given state. -/// -/// @note -/// For a MSM, to "be-in" a state means also to "be-in" a superstate of -/// of the state. -/// -/// @param[in] st pointer to the QMState object that corresponds to the -/// tested state. -/// @returns -/// 'true' if the MSM is in the \c st and 'false' otherwise -/// -bool QMsm::isInState(QMState const * const st) const noexcept { - bool inState = false; // assume that this MSM is not in 'state' - - for (QMState const *s = m_state.obj; - s != nullptr; - s = s->superstate) - { - if (s == st) { - inState = true; // match found, return 'true' - break; - } - } - return inState; -} - -//**************************************************************************** -/// -/// @description -/// Finds the child state of the given @c parent, such that this child state -/// is an ancestor of the currently active state. The main purpose of this -/// function is to support **shallow history** transitions in state machines -/// derived from QMsm. -/// -/// @param[in] parent pointer to the state-handler object -/// -/// @returns -/// the child of a given @c parent state, which is an ancestor of the -/// currently active state. For the corner case when the currently active -/// state is the given @c parent state, function returns the @c parent state. -/// -QMState const *QMsm::childStateObj(QMState const * const parent) - const noexcept -{ - QMState const *child = m_state.obj; - bool isFound = false; // start with the child not found - - for (QMState const *s = m_state.obj; - s != nullptr; - s = s->superstate) - { - if (s == parent) { - isFound = true; // child is found - break; - } - else { - child = s; - } - } - - /// @post the child must be found - Q_ENSURE_ID(810, isFound); -#ifdef Q_NASSERT - // avoid compiler warning about unused variable - static_cast(isFound); -#endif - - return child; // return the child -} - } // namespace QP - +//$enddef${QEP::QMsm} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/src/qequeue.hpp b/src/qequeue.hpp index 547bf6f..03fd257 100644 --- a/src/qequeue.hpp +++ b/src/qequeue.hpp @@ -1,74 +1,77 @@ -/// @file -/// @brief platform-independent fast "raw" thread-safe event queue interface -/// @ingroup qf -/// @cond -///*************************************************************************** -/// Last updated for version 6.9.1 -/// Last updated on 2020-09-15 -/// -/// Q u a n t u m L e a P s -/// ------------------------ -/// Modern Embedded Software -/// -/// Copyright (C) 2005-2020 Quantum Leaps. All rights reserved. -/// -/// This program is open source software: you can redistribute it and/or -/// modify it under the terms of the GNU General Public License as published -/// by the Free Software Foundation, either version 3 of the License, or -/// (at your option) any later version. -/// -/// Alternatively, this program may be distributed and modified under the -/// terms of Quantum Leaps commercial licenses, which expressly supersede -/// the GNU General Public License and are specifically designed for -/// licensees interested in retaining the proprietary status of their code. -/// -/// This program is distributed in the hope that it will be useful, -/// but WITHOUT ANY WARRANTY; without even the implied warranty of -/// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -/// GNU General Public License for more details. -/// -/// You should have received a copy of the GNU General Public License -/// along with this program. If not, see . -/// -/// Contact information: -/// -/// -///*************************************************************************** -/// @endcond +//$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. +// +// 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::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 - -/// @description -/// This header file must be included in all QF ports that use native QF -/// event queue for active objects. Also, this file needs to be included -/// in the QP/C++ library when the application uses QP::QMActive::defer() / -/// QP::QMActive::recall(). Finally, this file is also needed when the "raw" -/// thread-safe queues are used for communication between active objects -/// and non-framework entities, such as ISRs, device drivers, or legacy -/// code. +#ifndef QEQUEUE_HPP_ +#define QEQUEUE_HPP_ #ifndef QF_EQUEUE_CTR_SIZE //! The size (in bytes) of the ring-buffer counters used in the //! native QF event queue implementation. Valid values: 1U, 2U, or 4U; //! default 1U. - /// @description - /// This macro can be defined in the QF port file (qf_port.hpp) to - /// configure the QP::QEQueueCtr type. Here the macro is not defined - /// so the default of 1 byte is chosen. + //! @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. - /// @description - /// The dynamic range of this data type determines the maximum length - /// of the ring buffer managed by the native QF event queue. + //! @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; @@ -78,65 +81,73 @@ namespace QP { #error "QF_EQUEUE_CTR_SIZE defined incorrectly, expected 1U, 2U, or 4U" #endif +} // namespace QP -//**************************************************************************** +//============================================================================ +//$declare${QF::QEQueue} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv +namespace QP { + +//${QF::QEQueue} ............................................................. //! Native QF Event Queue class -/// @description -/// This structure describes the native QF event queue, which can be used as -/// the event queue for active objects, or as a simple "raw" event queue for -/// thread-safe event passing among non-framework entities, such as ISRs, -/// device drivers, or other third-party components.@n -/// @n -/// The native QF event queue is configured by defining the macro -/// #QF_EQUEUE_TYPE as QP::QEQueue in the specific QF port header file.@n -/// @n -/// The QP::QEQueue class contains only data members for managing an event -/// queue, but does not contain the storage for the queue buffer, which must -/// be provided externally during the queue initialization.@n -/// @n -/// The event queue can store only event pointers, not the whole events. The -/// internal implementation uses the standard ring-buffer plus one external -/// location that optimizes the queue operation for the most frequent case -/// of empty queue.@n -/// @n -/// The QP::QEQueue class is used with two sets of functions. One set is for -/// the active object event queue, which needs to block the active object -/// task when the event queue is empty and unblock it when events are posted -/// to the queue. The interface for the native active object event queue -/// consists of the following functions: QP::QMActive::post(), -/// QP::QMActive::postLIFO(), and QP::QMActive::get_(). Additionally the -/// function QP::QEQueue::init() is used to initialize the queue.@n -/// @n -/// The other set of functions, uses this class as a simple "raw" event -/// queue to pass events between entities other than active objects, such as -/// ISRs. The "raw" event queue is not capable of blocking on the get() -/// operation, but is still thread-safe because it uses QF critical section -/// to protect its integrity. The interface for the "raw" thread-safe queue -/// consists of the following functions: QP::QEQueue::post(), -/// QP::QEQueue::postLIFO(), and QP::QEQueue::get(). Additionally the -/// function QP::QEQueue::init() is used to initialize the queue.@n -/// @n -/// @note Most event queue operations (both the active object queues and -/// the "raw" queues) internally use the QF critical section. You should be -/// careful not to invoke those operations from other critical sections when -/// nesting of critical sections is not supported. +//! +//! @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 - /// @description - /// All incoming and outgoing events pass through the m_frontEvt location. - /// When the queue is empty (which is most of the time), the extra - /// m_frontEvt location allows to bypass the ring buffer altogether, - /// greatly optimizing the performance of the queue. Only bursts of events - /// engage the ring buffer.@n - /// @n - /// The additional role of this attribute is to indicate the empty status - /// of the queue. The queue is empty if the m_frontEvt location is NULL. + //! + //! @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; + QEvt const ** m_ring; //! offset of the end of the ring buffer from the start of the buffer QEQueueCtr m_end; @@ -151,125 +162,173 @@ class QEQueue { 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(). + //! @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; + friend class QActive; + friend class QTicker; + friend class QXMutex; + friend class QXThread; public: + //! public default constructor - QEQueue(void) noexcept; + QEQueue() noexcept; //! Initializes the native QF event queue - /// @description - /// The parameters are as follows: @p qSto[] is the ring buffer storage, - /// @p qLen is the length of the ring buffer in the units of event- - /// pointers. - /// - /// @note The actual capacity of the queue is qLen + 1, because of the - /// extra location fornEvt_. - void init(QEvt const *qSto[], std::uint_fast16_t const qLen) noexcept; + //! + //! @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. + void init( + QEvt const * qSto[], + std::uint_fast16_t const qLen) noexcept; - //! "raw" thread-safe QF event queue implementation for the event - //! posting (FIFO). You can call this function from any task context or - //! ISR context. This function uses internally a critical section. - /// @description - /// The argument @p margin specifies the minimum number of free entries - /// in the queue that must be available for posting to succeed. The - /// function returns true (success) if the posting succeeded (with the - /// provided margin) and false (failure) when the posting fails. - /// - /// @note - /// The function raises an assertion if the @p margin is zero and the - /// queue becomes full and cannot accept the event. - /// - /// @sa QP::QEQueue::postLIFO(), QP::QEQueue::get() - bool post(QEvt const * const e, std::uint_fast16_t const margin, - std::uint_fast8_t const qs_id) noexcept; + //! 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; - //! "raw" thread-safe QF event queue implementation for the - //! First-In-First-Out (FIFO) event posting. You can call this function - //! from any task context or ISR context. Please note that this function - //! uses internally a critical section. - /// @note The function raises an assertion if the native QF queue becomes - /// full and cannot accept the event. - /// - /// @sa QP::QEQueue::postLIFO(), QP::QEQueue::get() - void postLIFO(QEvt const * const e, - std::uint_fast8_t const qs_id) noexcept; + //! 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() + void postLIFO( + QEvt const * const e, + std::uint_fast8_t const qs_id) noexcept; - //! "raw" thread-safe QF event queue implementation for the - //! Last-In-First-Out (LIFO) event posting. - /// @note - /// The LIFO policy should be used only with great caution because it - /// alters order of events in the queue. - /// @note - /// The function raises an assertion if the native QF queue becomes - /// full and cannot accept the event. You can call this function from - /// any task context or ISR context. Please note that this function uses - /// internally a critical section. - /// - /// @sa QP::QEQueue::post(), QP::QEQueue::postLIFO(), QP::QEQueue::get() - QEvt const *get(std::uint_fast8_t const qs_id) noexcept; + //! 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; - //! "raw" thread-safe QF event queue operation for obtaining the number - //! of free entries still available in the queue. - /// @note - /// This operation needs to be used with caution because the - /// number of free entries can change unexpectedly. The main intent for - /// using this operation is in conjunction with event deferral. In this - /// case the queue is accessed only from a single thread (by a single AO), - /// so the number of free entries cannot change unexpectedly. - /// - /// @sa QP::QMActive::defer(), QP::QMActive::recall() - QEQueueCtr getNFree(void) const noexcept { + //! 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() + QEQueueCtr getNFree() const noexcept { return m_nFree; } //! "raw" thread-safe QF event queue operation for obtaining the minimum //! number of free entries ever in the queue (a.k.a. "low-watermark"). - /// - /// @description - /// This operation needs to be used with caution because the - /// "low-watermark" can change unexpectedly. The main intent for using - /// this operation is to get an idea of queue usage to size the queue - /// adequately. - /// - /// @returns the minimum number of free entries ever in the queue - /// since init. - QEQueueCtr getNMin(void) const noexcept { + //! + //! @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(void) const noexcept { + //! @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 QEQueue - QEQueue(QEQueue const &) = delete; - //! disallow assignment of QEQueue - QEQueue & operator=(QEQueue const &) = delete; + //! disallow copying of QP::QEQueue + QEQueue(QEQueue const & other) = delete; - friend class QF; - friend class QActive; - friend class QXThread; - friend class QTicker; - friend class QS; -}; + //! disallow copying of QP::QEQueue + QEQueue & operator=(QEQueue const & other) = delete; +}; // class QEQueue } // namespace QP +//$enddecl${QF::QEQueue} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -#endif // QEQUEUE_HPP - +#endif // QEQUEUE_HPP_ diff --git a/src/qf.hpp b/src/qf.hpp index 3665941..aabb9f4 100644 --- a/src/qf.hpp +++ b/src/qf.hpp @@ -1,913 +1,1880 @@ -/// @file -/// @brief QF/C++ platform-independent public interface. -/// @ingroup qf -/// @cond -///*************************************************************************** -/// Last updated for version 6.9.3 -/// Last updated on 2021-02-26 -/// -/// Q u a n t u m L e a P s -/// ------------------------ -/// Modern Embedded Software -/// -/// Copyright (C) 2005-2021 Quantum Leaps. All rights reserved. -/// -/// This program is open source software: you can redistribute it and/or -/// modify it under the terms of the GNU General Public License as published -/// by the Free Software Foundation, either version 3 of the License, or -/// (at your option) any later version. -/// -/// Alternatively, this program may be distributed and modified under the -/// terms of Quantum Leaps commercial licenses, which expressly supersede -/// the GNU General Public License and are specifically designed for -/// licensees interested in retaining the proprietary status of their code. -/// -/// This program is distributed in the hope that it will be useful, -/// but WITHOUT ANY WARRANTY; without even the implied warranty of -/// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -/// GNU General Public License for more details. -/// -/// You should have received a copy of the GNU General Public License -/// along with this program. If not, see . -/// -/// Contact information: -/// -/// -///*************************************************************************** -/// @endcond - -#ifndef QF_HPP -#define QF_HPP - -//**************************************************************************** -#ifndef QPSET_HPP -#include "qpset.hpp" -#endif +//$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 placement new +#include // for va_list #endif // Q_EVT_CTOR -//**************************************************************************** -// apply defaults for all undefined configuration parameters -// -#ifndef QF_EVENT_SIZ_SIZE - //! Default value of the macro configurable value in qf_port.hpp - #define QF_EVENT_SIZ_SIZE 2U -#endif +//============================================================================ +// Global namespace... +//$declare${QF-config} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv -#ifndef QF_MAX_EPOOL - //! Default value of the macro configurable value in qf_port.hpp - #define QF_MAX_EPOOL 3U -#endif +//${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 - //! Default value of the macro configurable value in qf_port.hpp - //! Valid values: [0U..15U]; default 1U - #define QF_MAX_TICK_RATE 1U -#elif (QF_MAX_TICK_RATE > 15U) - #error "QF_MAX_TICK_RATE exceeds the maximum of 15U" -#endif +//! 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 - //! macro to override the default QTimeEvtCtr size. - //! Valid values 1U, 2U, or 4U; default 2U - #define QF_TIMEEVT_CTR_SIZE 2U -#endif +//! 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 { -#if (QF_EVENT_SIZ_SIZE == 1U) - using QEvtSize = std::uint8_t; -#elif (QF_EVENT_SIZ_SIZE == 2U) - //! The data type to store the block-size defined based on - //! the macro #QF_EVENT_SIZ_SIZE. - /// @description - /// The dynamic range of this data type determines the maximum block - /// size that can be managed by the pool. - using QEvtSize = std::uint16_t; -#elif (QF_EVENT_SIZ_SIZE == 4U) - using QEvtSize = std::uint32_t; -#else - #error "QF_EVENT_SIZ_SIZE defined incorrectly, expected 1U, 2U, or 4U" -#endif - -//**************************************************************************** -#if (QF_TIMEEVT_CTR_SIZE == 1U) - using QTimeEvtCtr = std::uint8_t; -#elif (QF_TIMEEVT_CTR_SIZE == 2U) - //! type of the Time Event counter, which determines the dynamic - //! range of the time delays measured in clock ticks. - /// @description - /// This alias is configurable via the preprocessor switch - /// #QF_TIMEEVT_CTR_SIZE. The other possible values of this type are - /// as follows: @n - /// std::uint8_t when (QF_TIMEEVT_CTR_SIZE == 1U), and @n - /// std::uint32_t when (QF_TIMEEVT_CTR_SIZE == 4U). - using QTimeEvtCtr = std::uint16_t; -#elif (QF_TIMEEVT_CTR_SIZE == 4U) - using QTimeEvtCtr = std::uint32_t; -#else - #error "QF_TIMEEVT_CTR_SIZE defined incorrectly, expected 1U, 2U, or 4U" -#endif - -class QEQueue; // forward declaration - -//**************************************************************************** -//! QActive active object (based on QP::QHsm implementation) -/// @description -/// Active objects in QP are encapsulated tasks (each embedding a state -/// machine and an event queue) that communicate with one another -/// asynchronously by sending and receiving events. Within an active object, -/// events are processed in a run-to-completion (RTC) fashion, while QF -/// encapsulates all the details of thread-safe event exchange and queuing. -/// @n@n -/// QP::QActive represents an active object that uses the QP::QHsm-style -/// implementation strategy for state machines. This strategy is tailored -/// to manual coding, but it is also supported by the QM modeling tool. -/// The resulting code is slower than in the QP::QMsm-style implementation -/// strategy. -/// -/// @note -/// QP::QActive is not intended to be instantiated directly, but rather serves -/// as the base class for derivation of active objects in the applications. -/// -/// @sa QP::QMActive -/// -/// @usage -/// The following example illustrates how to derive an active object from -/// QP::QActive. -/// @include qf_qactive.cpp -/// -class QActive : public QHsm { -public: // for access from extern "C" functions +//${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. - /// @description - /// The type of the queue depends on the underlying operating system or - /// a kernel. Many kernels support "message queues" that can be adapted - /// to deliver QF events to the active object. Alternatively, QF provides - /// a native event queue implementation that can be used as well. - /// - /// @note - /// The native QF event queue is configured by defining the macro - /// #QF_EQUEUE_TYPE as QP::QEQueue. + //! 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 +#endif // def QF_EQUEUE_TYPE #ifdef QF_OS_OBJECT_TYPE - //! OS-dependent per-thread object. - /// @description - /// This data might be used in various ways, depending on the QF port. - /// In some ports m_osObject is used to block the calling thread when - /// the native QF queue is empty. In other QF ports the OS-dependent - /// object might be used differently. + //! 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 +#endif // def QF_OS_OBJECT_TYPE #ifdef QF_THREAD_TYPE - //! OS-dependent representation of the thread of the active object. - /// @description - /// This data might be used in various ways, depending on the QF port. - /// In some ports m_thread is used store the thread handle. In other ports - /// m_thread can be a pointer to the Thread-Local-Storage (TLS). + //! 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 - -#ifdef QXK_HPP // QXK kernel used? - //! QXK dynamic priority (1..#QF_MAX_ACTIVE) of AO/thread. - std::uint8_t m_dynPrio; -#endif +#endif // def QF_THREAD_TYPE - //! QF priority (1..#QF_MAX_ACTIVE) of this active object. + //! 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. - virtual void start(std::uint_fast8_t const prio, - QEvt const * * const qSto, std::uint_fast16_t const qLen, - void * const stkSto, std::uint_fast16_t const stkSize, + //! 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 event) - virtual void start(std::uint_fast8_t const prio, - QEvt const * * const qSto, std::uint_fast16_t const qLen, - void * const stkSto, std::uint_fast16_t const stkSize) + //! 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(prio, qSto, qLen, stkSto, stkSize, nullptr); + 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(void); -#endif + //! 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; -#ifndef Q_SPY - //! Posts an event @p e directly to the event queue of the active - //! object @p me using the First-In-First-Out (FIFO) policy. - virtual bool post_(QEvt const * const e, - std::uint_fast16_t const margin) noexcept; -#else - virtual bool post_(QEvt const * const e, std::uint_fast16_t const margin, - void const * const sender) noexcept; -#endif - - //! Posts an event directly to the event queue of the active object + //! 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; - //! Un-subscribes from the delivery of all signals to the active object. - void unsubscribeAll(void) const noexcept; - - //! Subscribes for delivery of signal @p sig to the active object + //! 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; - //! Un-subscribes from the delivery of signal @p sig to the active object. + //! 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; - //! Defer an event to a given separate event queue. - bool defer(QEQueue * const eq, QEvt const * const e) const noexcept; - - //! Recall a deferred event from a given event queue. + //! 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'. + //! 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(void) const noexcept { + std::uint_fast8_t getPrio() const noexcept { return static_cast(m_prio); } +protected: + //! Set the priority of the active object. - void setPrio(std::uint_fast8_t const prio) { - m_prio = static_cast(prio); + 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); + void setAttr( + std::uint32_t attr1, + void const * attr2 = nullptr); #ifdef QF_OS_OBJECT_TYPE //! accessor to the OS-object for extern "C" functions, such as //! the QK or QXK schedulers - QF_OS_OBJECT_TYPE &getOsObject(void) noexcept { return m_osObject; } -#endif + 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(void) noexcept { return m_thread; } -#endif - - //! Get an event from the event queue of an active object. - QEvt const *get_(void) noexcept; + 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; -// duplicated API to be used exclusively inside ISRs (useful in some QP ports) #ifdef QF_ISR_API -#ifdef Q_SPY - virtual bool postFromISR_(QEvt const * const e, - std::uint_fast16_t const margin, void *par, + //! 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; -#else - virtual bool postFromISR_(QEvt const * const e, - std::uint_fast16_t const margin, void *par) noexcept; -#endif // Q_SPY -#endif // QF_ISR_API +#endif // def QF_ISR_API -// friendships... -private: - friend class QF; - friend class QTimeEvt; - friend class QTicker; -#ifdef QK_HPP - friend class QMutex; -#endif // QK_HPP -#ifdef QXK_HPP - friend class QXK; - friend class QXThread; - friend class QXMutex; - friend class QXSemaphore; -#endif // QXK_HPP -#ifdef Q_UTEST - friend class QActiveDummy; -#endif // Q_UTEST -}; +#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) -/// @description -/// QP::QMActive represents an active object that uses the QP::QMsm-style -/// state machine implementation strategy. This strategy requires the use of -/// the QM modeling tool to generate state machine code automatically, but -/// the code is faster than in the QP::QHsm-style implementation strategy -/// and needs less run-time support (smaller event-processor). -/// -/// @note -/// QP::QMActive is not intended to be instantiated directly, but rather -/// serves as the base class for derivation of active objects in the -/// applications. -/// -/// @sa QP::QActive -/// -/// @usage -/// The following example illustrates how to derive an active object from -/// QP::QMActive. -/// @include qf_qmactive.cpp -/// -class QMActive : public QActive { +//! +//! @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: - // all the following operations delegate to the QHsm class... - void init(void const * const e, - std::uint_fast8_t const qs_id) override; + + //! 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; - void dispatch(QEvt const * const e, - std::uint_fast8_t const qs_id) override; -#ifdef Q_SPY - //! Get the current state handler of the QMsm - QStateHandler getStateHandler() noexcept override; -#endif + //! 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(void) const noexcept { + QMState const * stateObj() const noexcept { return m_state.obj; } - //! Obtain the current active child state of a given parent (read only) - QMState const *childStateObj(QMState const * const parent) const noexcept; + //! Return the current active state object (read only) + QMState const * childStateObj(QMState const * const parent) const noexcept; -protected: - //! protected constructor (abstract class) - QMActive(QStateHandler const initial) noexcept; +#ifdef Q_SPY + //! Get the current state handler of the QP::QMsm + QStateHandler getStateHandler() noexcept override; +#endif // def Q_SPY +}; // class QMActive -private: - //! operations inherited from QP::QHsm, but disallowed in QP::QMActive - using QHsm::isIn; - using QHsm::state; - using QHsm::childState; -}; - - -//**************************************************************************** -//! Time Event class -/// @description -/// Time events are special QF events equipped with the notion of time -/// passage. The basic usage model of the time events is as follows. An -/// active object allocates one or more QTimeEvt objects (provides the -/// storage for them). When the active object needs to arrange for a timeout, -/// it arms one of its time events to fire either just once (one-shot) or -/// periodically. Each time event times out independently from the others, -/// so a QF application can make multiple parallel timeout requests (from the -/// same or different active objects). When QF detects that the appropriate -/// moment has arrived, it inserts the time event directly into the -/// recipient's event queue. The recipient then processes the time event just -/// like any other event.@n -/// @n -/// Time events, as any other QF events derive from the QP::QEvt base -/// class. Typically, you will use a time event as-is, but you can also -/// further derive more specialized time events from it by adding some more -/// data members and/or specialized functions that operate on the specialized -/// time events.@n -/// @n -/// Internally, the armed time events are organized into a bi-directional -/// linked list. This linked list is scanned in every invocation of the -/// QP::QF::tickX_() function. Only armed (timing out) time events are in the -/// list, so only armed time events consume CPU cycles. -/// -/// @note -/// QF manages the time events in the macro TICK_X(), which must be called -/// periodically, from the clock tick ISR or from the special QP::QTicker -/// active object. -/// -/// @note -/// Even though QP::QTimeEvt is a subclass of QP::QEvt, QP::QTimeEvt instances -/// can NOT be allocated dynamically from event pools. In other words, it is -/// illegal to allocate QP::QTimeEvt instances with the Q_NEW() or Q_NEW_X() -/// macros. -/// -class QTimeEvt : public QEvt { +} // 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 - /// @description - /// The m_act pointer is reused inside the QP implementation to hold - /// the head of the list of newly armed time events. - void * volatile m_act; - - //! the internal down-counter of the time event. - /// @description - /// The down-counter is decremented by 1 in every TICK_X() - /// invocation. The time event fires (gets posted or published) when - /// the down-counter reaches zero. + //! + //! @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). - /// @description - /// The value of the interval is re-loaded to the internal - /// down-counter when the time event expires, so that the time event - /// keeps timing out periodically. + //! 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: - //! The Time Event constructor. - QTimeEvt(QActive * const act, enum_t const sgnl, - std::uint_fast8_t const tickRate = 0U) noexcept; - - //! Arm a time event (one shot or periodic) for event posting. - void armX(QTimeEvtCtr const nTicks, - QTimeEvtCtr const interval = 0U) noexcept; - - //! Disarm a time event. - bool disarm(void) noexcept; - - //! Rearm a time event. - bool rearm(QTimeEvtCtr const nTicks) noexcept; - - //! Check the "was disarmed" status of a time event. - bool wasDisarmed(void) noexcept; - - //! Get the current value of the down-counter of a time event. - QTimeEvtCtr currCtr(void) const noexcept; + //! heads of linked lists of time events, one for every clock tick rate + static QTimeEvt timeEvtHead_[QF_MAX_TICK_RATE]; private: - //! private default constructor only for friends - QTimeEvt(void) noexcept; + friend class QXThread; - //! private copy constructor to disallow copying of QTimeEvts - QTimeEvt(QTimeEvt const &) = delete; +public: - //! private assignment operator to disallow assigning of QTimeEvts - QTimeEvt & operator=(QTimeEvt const &) = delete; + //! 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; - //! encapsulate the cast the m_act attribute to QActive* - QActive *toActive(void) noexcept { - return static_cast(m_act); + //! 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; } - //! encapsulate the cast the m_act attribute to QTimeEvt* - QTimeEvt *toTimeEvt(void) noexcept { - return static_cast(m_act); + //! Gets the current count of the time event + QTimeEvtCtr getCtr() const noexcept { + return m_ctr; } - friend class QF; - friend class QS; -#ifdef QXK_HPP - friend class QXThread; - friend void QXK_activate_(void); -#endif // QXK_HPP -}; - - -//**************************************************************************** -//! Subscriber List -/// @description -/// This data type represents a set of active objects that subscribe to -/// a given signal. The set is represented as priority-set, where each -/// bit corresponds to the unique priority of an active object. -using QSubscrList = QPSet; - - -//**************************************************************************** -//! QF services. -/// @description -/// This class groups together QF services. It has only static members and -/// should not be instantiated. -class QF { -public: - - //! get the current QF version number string of the form X.Y.Z - static char_t const *getVersion(void) noexcept { - return versionStr; + //! Gets the interval of the time event + QTimeEvtCtr getInterval() const noexcept { + return m_interval; } - //! QF initialization. - static void init(void); - - //! Publish-subscribe initialization. - static void psInit(QSubscrList * const subscrSto, - enum_t const maxSignal) noexcept; - - //! Event pool initialization for dynamic allocation of events. - static void poolInit(void * const poolSto, - std::uint_fast32_t const poolSize, - std::uint_fast16_t const evtSize) noexcept; - - //! Obtain the block size of any registered event pools - static std::uint_fast16_t poolGetMaxBlockSize(void) noexcept; - - - //! Transfers control to QF to run the application. - static int_t run(void); - - //! Startup QF callback. - static void onStartup(void); - - //! Cleanup QF callback. - static void onCleanup(void); + //! 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; - //! Function invoked by the application layer to stop the QF - //! application and return control to the OS/Kernel. - static void stop(void); +#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 -#ifndef Q_SPY - static void publish_(QEvt const * const e) noexcept; - static void tickX_(std::uint_fast8_t const tickRate) noexcept; -#else - //! Publish event to the framework. - static void publish_(QEvt const * const e, - void const * const sender, - std::uint_fast8_t const qs_id) noexcept; - - //! Processes all armed time events at every clock tick. - static void tickX_(std::uint_fast8_t const tickRate, - void const * const sender) noexcept; -#endif // Q_SPY +#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. - static bool noTimeEvtsActiveX(std::uint_fast8_t const tickRate) noexcept; - - //! This function returns the minimum of free entries of the given - //! event pool. - static std::uint_fast16_t getPoolMin(std::uint_fast8_t const poolId) - noexcept; - - //! This function returns the minimum of free entries of the given - //! event queue. - static std::uint_fast16_t getQueueMin(std::uint_fast8_t const prio) - noexcept; - - //! Internal QF implementation of creating new dynamic event. - static QEvt *newX_(std::uint_fast16_t const evtSize, - std::uint_fast16_t const margin, - enum_t const sig) noexcept; + //! 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; - //! Recycle a dynamic event. - static void gc(QEvt const * const e) noexcept; + //! encapsulate the cast the m_act attribute to QActive* + QActive * toActive() noexcept { + return static_cast(m_act); + } - //! Internal QF implementation of creating new event reference. - static QEvt const *newRef_(QEvt const * const e, - QEvt const * const evtRef) noexcept; + //! encapsulate the cast the `QTimeEvt.m_act` attribute + QTimeEvt * toTimeEvt() noexcept { + return static_cast(m_act); + } - //! Internal QF implementation of deleting event reference. - static void deleteRef_(QEvt const * const evtRef) noexcept; +private: - //! Remove the active object from the framework. - static void remove_(QActive * const a) noexcept; + //! private default constructor only for friends + //! + //! @note + //! private default ctor for internal use only + QTimeEvt(); - //! array of registered active objects - static QActive *active_[QF_MAX_ACTIVE + 1U]; + //! private copy constructor to disallow copying of QTimeEvts + QTimeEvt(QTimeEvt const & other) = delete; - //! Thread routine for executing an active object @p act. - static void thread_(QActive *act); + //! disallow copying of QP::QTimeEvt + QTimeEvt & operator=(QTimeEvt const & other) = delete; +}; // class QTimeEvt - //! Register an active object to be managed by the framework - static void add_(QActive * const a) noexcept; +} // namespace QP +//$enddecl${QF::QTimeEvt} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +//$declare${QF::QTicker} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv +namespace QP { - //! Clear a specified region of memory to zero. - static void bzero(void * const start, - std::uint_fast16_t const len) noexcept; +//${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: -// API to be used exclusively inside ISRs (useful in some QP ports) -#ifdef QF_ISR_API -#ifdef Q_SPY - static void publishFromISR_(QEvt const *e, void *par, - void const *sender) noexcept; - static void tickXfromISR_(std::uint_fast8_t const tickRate, void *par, - void const * const sender) noexcept; -#else - static void publishFromISR_(QEvt const *e, void *par) noexcept; - static void tickXfromISR_(std::uint_fast8_t const tickRate, - void *par) noexcept; -#endif // Q_SPY - - static QEvt *newXfromISR_(std::uint_fast16_t const evtSize, - std::uint_fast16_t const margin, - enum_t const sig) noexcept; - static void gcFromISR(QEvt const *e) noexcept; - -#endif // QF_ISR_API - -// to be used in QF ports only... -private: - //! heads of linked lists of time events, one for every clock tick rate - static QTimeEvt timeEvtHead_[QF_MAX_TICK_RATE]; + //! 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 - friend class QActive; - friend class QTimeEvt; - friend class QS; -#ifdef QXK_HPP - friend class QXThread; -#endif // QXK_HPP -}; +} // namespace QP +//$enddecl${QF::QTicker} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -//! special value of margin that causes asserting failure in case +//$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 -std::uint_fast16_t const QF_NO_MARGIN = 0xFFFFU; - - -//**************************************************************************** -//! Ticker Active Object class -/// @description -/// QP::QTicker is an efficient active object specialized to process -/// QF system clock tick at a specified tick frequency [0..#QF_MAX_TICK_RATE]. -/// Placing system clock tick processing in an active object allows you -/// to remove the non-deterministic TICK_X() processing from the interrupt -/// level and move it into the thread-level, where you can prioritize it -/// as low as you wish. -/// -class QTicker : public QActive { -public: - explicit QTicker(std::uint_fast8_t const tickRate) noexcept; // ctor - - void init(void const * const e, - std::uint_fast8_t const qs_id) noexcept override; - void init(std::uint_fast8_t const qs_id) noexcept override { - this->init(qs_id); - } - void dispatch(QEvt const * const e, - std::uint_fast8_t const qs_id) noexcept override; -#ifndef Q_SPY - bool post_(QEvt const * const e, - std::uint_fast16_t const margin) noexcept override; -#else - bool post_(QEvt const * const e, std::uint_fast16_t const margin, - void const * const sender) noexcept override; -#endif - void postLIFO(QEvt const * const e) noexcept override; -}; +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 -//**************************************************************************** -#ifndef QF_CRIT_EXIT_NOP - //! No-operation for exiting a critical section - /// @description - /// In some QF ports the critical section exit takes effect only on the - /// next machine instruction. If this next instruction is another entry - /// to a critical section, the critical section won't be really exited, - /// but rather the two adjecent critical sections would be merged. - /// The #QF_CRIT_EXIT_NOP() macro contains minimal code required to - /// prevent such merging of critical sections in such merging of - /// critical sections in QF ports, in which it can occur. - #define QF_CRIT_EXIT_NOP() (static_cast(0)) -#endif - -//**************************************************************************** -// Provide the constructor for the QEvt class? +} // 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 - - #define Q_NEW(evtT_, sig_, ...) \ - (new(QP::QF::newX_(sizeof(evtT_), QP::QF_NO_MARGIN, 0)) \ - evtT_((sig_), ##__VA_ARGS__)) - - #define Q_NEW_X(e_, evtT_, margin_, sig_, ...) do { \ - (e_) = static_cast( \ - QP::QF::newX_(sizeof(evtT_), (margin_), 0)); \ - if ((e_) != nullptr) { \ - new((e_)) evtT_((sig_), ##__VA_ARGS__); \ - } \ - } while (false) - -#else // QEvt is a POD (Plain Old Datatype) - - //! Allocate a dynamic event. - /// @description - /// The macro calls the internal QF function QP::QF::newX_() with - /// margin == QP::QF_NO_MARGIN, which causes an assertion when the event - /// cannot be successfully allocated. - /// - /// @param[in] evtT_ event type (class name) of the event to allocate - /// @param[in] sig_ signal to assign to the newly allocated event - /// - /// @returns a valid event pointer cast to the type @p evtT_. - /// - /// @note - /// If #Q_EVT_CTOR is defined, the Q_NEW() macro becomes variadic and - /// takes all the arguments needed by the constructor of the event - /// class being allocated. The constructor is then called by means - /// of the placement-new operator. - /// - /// @usage - /// The following example illustrates dynamic allocation of an event: - /// @include qf_post.cpp - #define Q_NEW(evtT_, sig_) (static_cast( \ - QP::QF::newX_(sizeof(evtT_), QP::QF_NO_MARGIN, (sig_)))) - - //! Allocate a dynamic event (non-asserting version). - /// @description - /// This macro allocates a new event and sets the pointer @p e_, while - /// leaving at least @p margin_ of events still available in the pool - /// - /// @param[out] e_ pointer to the newly allocated event - /// @param[in] evtT_ event type (class name) of the event to allocate - /// @param[in] margin_ number of events that must remain available - /// in the given pool after this allocation. The - /// special value QP::QF_NO_MARGIN causes asserting - /// failure in case event allocation fails. - /// @param[in] sig_ signal to assign to the newly allocated event - /// - /// @returns an event pointer cast to the type @p evtT_ or NULL if the - /// event cannot be allocated with the specified @p margin. - /// - /// @note - /// If #Q_EVT_CTOR is defined, the Q_NEW_X() macro becomes variadic and - /// takes all the arguments needed by the constructor of the event - /// class being allocated. The constructor is then called by means - /// of the placement-new operator. - /// - /// @usage - /// The following example illustrates dynamic allocation of an event: - /// @include qf_postx.cpp - #define Q_NEW_X(e_, evtT_, margin_, sig_) \ - ((e_) = static_cast(QP::QF::newX_( \ - sizeof(evtT_), (margin_), (sig_)))) -#endif - -//! Create a new reference of the current event `e` */ -/// @description -/// The current event processed by an active object is available only for -/// the duration of the run-to-completion (RTC) step. After that step, the -/// current event is no longer available and the framework might recycle -/// (garbage-collect) the event. The macro Q_NEW_REF() explicitly creates -/// a new reference to the current event that can be stored and used beyond -/// the current RTC step, until the reference is explicitly recycled by -/// means of the macro Q_DELETE_REF(). -/// -/// @param[in,out] evtRef_ event reference to create -/// @param[in] evtT_ event type (class name) of the event refrence -/// -/// @usage -/// The example **defer** in the directory `examples/win32/defer` illustrates -/// the use of Q_NEW_REF() -/// -/// @sa Q_DELETE_REF() -/// -#define Q_NEW_REF(evtRef_, evtT_) \ +//! 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_)))) -//! Delete the event reference */ -/// @description -/// Every event reference created with the macro Q_NEW_REF() needs to be -/// eventually deleted by means of the macro Q_DELETE_REF() to avoid leaking -/// the event. -/// -/// @param[in,out] evtRef_ event reference to delete -/// -/// @usage -/// The example **defer** in the directory `examples/win32/defer` illustrates -/// the use of Q_DELETE_REF() -/// -/// @sa Q_NEW_REF() -/// +//${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; \ + (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 -//**************************************************************************** -// QS software tracing integration, only if enabled +//${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 - //! Invoke the system clock tick processing QP::QF::tickX_(). - /// @description - /// This macro is the recommended way of invoking clock tick processing, - /// because it provides the vital information for software tracing and - /// avoids any overhead when the tracing is disabled. - /// - /// @param[in] tickRate_ clock tick rate to be serviced through this call - /// @param[in] sender_ pointer to the sender object. This parameter - /// is actually only used when QS software tracing is enabled - /// (macro #Q_SPY is defined) - /// @note - /// When QS software tracing is disabled, the macro calls QF_tickX_() - /// without the @p sender parameter, so the overhead of passing this - /// extra parameter is entirely avoided. - /// - /// @note - /// The pointer to the sender object is not necessarily a pointer - /// to an active object. In fact, when TICK_X() is called from - /// an interrupt, you would create a unique object just to unambiguously - /// identify the ISR as the sender of the time events. - /// - /// @sa QP::QF::tickX_() - #define TICK_X(tickRate_, sender_) tickX_((tickRate_), (sender_)) - - //! Invoke the event publishing facility QP::QF::publish_(). This macro - /// @description - /// This macro is the recommended way of publishing events, because it - /// provides the vital information for software tracing and avoids any - /// overhead when the tracing is disabled. - /// - /// @param[in] e_ pointer to the posted event - /// @param[in] sender_ pointer to the sender object. This parameter is - /// actually only used when QS software tracing is enabled - /// (macro #Q_SPY is defined). When QS software tracing is - /// disabled, the macro calls QF_publish_() without the - /// @p sender_ parameter, so the overhead of passing this - /// extra parameter is entirely avoided. - /// - /// @note - /// The pointer to the sender object is not necessarily a pointer - /// to an active object. In fact, if QF_PUBLISH() is called from an - /// interrupt or other context, you can create a unique object just to - /// unambiguously identify the publisher of the event. - /// - /// @sa QP::QF::publish_() - #define PUBLISH(e_, sender_) \ - publish_((e_), (sender_), (sender_)->getPrio()) - - //! Invoke the direct event posting facility QP::QActive::post_(). - /// @description - /// This macro asserts if the queue overflows and cannot accept the event. - /// - /// @param[in] e_ pointer to the event to post - /// @param[in] sender_ pointer to the sender object. - /// - /// @note - /// The @p sendedr_ parameter is actually only used when QS tracing - /// is enabled (macro #Q_SPY is defined). When QS software tracing is - /// disenabled, the POST() macro does not pass the @p sender_ - /// parameter, so the overhead of passing this extra parameter is entirely - /// avoided. - /// - /// @note the pointer to the sender object is not necessarily a pointer - /// to an active object. In fact, if POST() is called from an interrupt - /// or other context, you can create a unique object just to - /// unambiguously identify the sender of the event. - /// - /// @sa QP::QActive::post_() - #define POST(e_, sender_) post_((e_), QP::QF_NO_MARGIN, (sender_)) - - //! Invoke the direct event posting facility QP::QActive::post_() - //! without delivery guarantee. - /// @description - /// This macro does not assert if the queue overflows and cannot accept - /// the event with the specified margin of free slots remaining. - /// - /// @param[in] e_ pointer to the event to post - /// @param[in] margin_ the minimum free slots in the queue, which - /// must still be available after posting the event. - /// The special value QP::QF_NO_MARGIN causes - /// asserting failure in case event posting fails. - /// @param[in] sender_ pointer to the sender object. - /// - /// @returns - /// 'true' if the posting succeeded, and 'false' if the posting - /// failed due to insufficient margin of free entries available in - /// the queue. - /// - /// @note - /// The @p sender_ parameter is actually only used when QS tracing - /// is enabled (macro #Q_SPY is defined). When QS software tracing is - /// disabled, the POST_X() macro does not pass the @p sender_ parameter, - /// so the overhead of passing this extra parameter is entirely avoided. - /// - /// @note - /// The pointer to the sender object is not necessarily a pointer - /// to an active object. In fact, if POST_X() is called from an - /// interrupt or other context, you can create a unique object just to - /// unambiguously identify the sender of the event. - /// - /// @usage - /// @include qf_postx.cpp - #define POST_X(e_, margin_, sender_) \ - post_((e_), (margin_), (sender_)) - -#else - - #define PUBLISH(e_, dummy_) publish_((e_)) - #define POST(e_, dummy_) post_((e_), QP::QF_NO_MARGIN) - #define POST_X(e_, margin_, dummy_) post_((e_), (margin_)) - #define TICK_X(tickRate_, dummy_) tickX_((tickRate_)) - -#endif // Q_SPY +//${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() +//! @sa TICK_X() #define TICK(sender_) TICK_X(0U, (sender_)) -#endif // QF_HPP - +//${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 55a8855..bf98f6f 100644 --- a/src/qf_act.cpp +++ b/src/qf_act.cpp @@ -1,184 +1,45 @@ -/// @file -/// @brief QP::QActive services and QF support code -/// @ingroup qf -/// @cond -///*************************************************************************** -/// Last updated for version 6.9.1 -/// Last updated on 2020-09-18 -/// -/// Q u a n t u m L e a P s -/// ------------------------ -/// Modern Embedded Software -/// -/// Copyright (C) 2005-2020 Quantum Leaps. All rights reserved. -/// -/// This program is open source software: you can redistribute it and/or -/// modify it under the terms of the GNU General Public License as published -/// by the Free Software Foundation, either version 3 of the License, or -/// (at your option) any later version. -/// -/// Alternatively, this program may be distributed and modified under the -/// terms of Quantum Leaps commercial licenses, which expressly supersede -/// the GNU General Public License and are specifically designed for -/// licensees interested in retaining the proprietary status of their code. -/// -/// This program is distributed in the hope that it will be useful, -/// but WITHOUT ANY WARRANTY; without even the implied warranty of -/// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -/// GNU General Public License for more details. -/// -/// You should have received a copy of the GNU General Public License -/// along with this program. If not, see . -/// -/// Contact information: -/// -/// -///*************************************************************************** -/// @endcond - -#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 - -//**************************************************************************** -/// @description -/// macro to encapsulate pointer increment, which violates MISRA-C:2004 -/// required rule 17.4 (pointer arithmetic used). -/// -/// @param[in] p_ pointer to be incremented. -/// -#define QF_PTR_INC_(p_) (++(p_)) - -namespace QP { - -Q_DEFINE_THIS_MODULE("qf_act") - -// public objects ************************************************************ -QActive *QF::active_[QF_MAX_ACTIVE + 1U]; // to be used by QF ports only - -//**************************************************************************** -/// @description -/// This function adds a given active object to the active objects managed -/// by the QF framework. It should not be called by the application directly, -/// only through the function QP::QActive::start(). -/// -/// @param[in] a pointer to the active object to add to the framework. -/// -/// @note The priority of the active object @p a should be set before calling -/// this function. -/// -/// @sa QP::QF::remove_() -/// -void QF::add_(QActive * const a) noexcept { - std::uint_fast8_t const p = static_cast(a->m_prio); - - Q_REQUIRE_ID(100, (0U < p) && (p <= QF_MAX_ACTIVE) - && (active_[p] == nullptr)); - QF_CRIT_STAT_ - QF_CRIT_E_(); - active_[p] = a; // registger the active object at this priority - QF_CRIT_X_(); -} - -//**************************************************************************** -/// @description -/// This function removes a given active object from the active objects -/// managed by the QF framework. It should not be called by the QP ports. -/// -/// @param[in] a pointer to the active object to remove from the framework. -/// -/// @note -/// The active object that is removed from the framework can no longer -/// participate in the publish-subscribe event exchange. -/// -/// @sa QP::QF::add_() -/// -void QF::remove_(QActive * const a) noexcept { - std::uint_fast8_t const p = static_cast(a->m_prio); - - Q_REQUIRE_ID(200, (0U < p) && (p <= QF_MAX_ACTIVE) - && (active_[p] == a)); - - QF_CRIT_STAT_ - QF_CRIT_E_(); - active_[p] = nullptr; // free-up the priority level - a->m_state.fun = nullptr; // invalidate the state - QF_CRIT_X_(); -} - -//**************************************************************************** -/// @description -/// Clears a memory buffer by writing zeros byte-by-byte. -/// -/// @param[in] start pointer to the beginning of a memory buffer. -/// @param[in] len length of the memory buffer to clear (in bytes) -/// -/// @note The main application of this function is clearing the internal QF -/// variables upon startup. This is done to avoid problems with non-standard -/// startup code provided with some compilers and toolsets (e.g., TI DSPs or -/// Microchip MPLAB), which does not zero the uninitialized variables, as -/// required by the ANSI C standard. -/// -void QF::bzero(void * const start, std::uint_fast16_t const len) noexcept { - std::uint8_t *ptr = static_cast(start); - for (std::uint_fast16_t n = len; n > 0U; --n) { - *ptr = 0U; - QF_PTR_INC_(ptr); - } -} - -} // namespace QP - -// Log-base-2 calculations ... -#ifndef QF_LOG2 - -//! function that returns (log2(x) + 1), where @p x is a 32-bit bitmask -/// -/// @description -/// This function returns the 1-based number of the most significant 1-bit -/// of a 32-bit bitmask. This function can be replaced in the QP ports, if -/// the CPU has special instructions, such as CLZ (count leading zeros). -/// -extern "C" { - - std::uint_fast8_t QF_LOG2(QP::QPSetBits x) noexcept { - static std::uint8_t const log2LUT[16] = { - 0U, 1U, 2U, 2U, 3U, 3U, 3U, 3U, - 4U, 4U, 4U, 4U, 4U, 4U, 4U, 4U - }; - std::uint_fast8_t n = 0U; - QP::QPSetBits t; - -#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]; - } - -} // extern "C" - -#endif // QF_LOG2 - +//$file${src::qf::qf_act.cpp} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv +// +// Model: qpcpp.qm +// File: ${src::qf::qf_act.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_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 diff --git a/src/qf_actq.cpp b/src/qf_actq.cpp index 59b4854..daa48a8 100644 --- a/src/qf_actq.cpp +++ b/src/qf_actq.cpp @@ -1,45 +1,49 @@ -/// @file -/// @brief QP::QActive native queue operations (based on QP::QEQueue) -/// -/// @note -/// this source file is only included in the QF library when the native -/// QF active object queue is used (instead of a message queue of an RTOS). -/// -/// @ingroup qf -/// @cond -///*************************************************************************** -/// Last updated for version 6.9.1 -/// Last updated on 2020-09-17 -/// -/// Q u a n t u m L e a P s -/// ------------------------ -/// Modern Embedded Software -/// -/// Copyright (C) 2005-2020 Quantum Leaps. All rights reserved. -/// -/// This program is open source software: you can redistribute it and/or -/// modify it under the terms of the GNU General Public License as published -/// by the Free Software Foundation, either version 3 of the License, or -/// (at your option) any later version. -/// -/// Alternatively, this program may be distributed and modified under the -/// terms of Quantum Leaps commercial licenses, which expressly supersede -/// the GNU General Public License and are specifically designed for -/// licensees interested in retaining the proprietary status of their code. -/// -/// This program is distributed in the hope that it will be useful, -/// but WITHOUT ANY WARRANTY; without even the implied warranty of -/// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -/// GNU General Public License for more details. -/// -/// You should have received a copy of the GNU General Public License -/// along with this program. If not, see . -/// -/// Contact information: -/// -/// -///*************************************************************************** -/// @endcond +//$file${src::qf::qf_actq.cpp} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv +// +// Model: qpcpp.qm +// File: ${src::qf::qf_actq.cpp} +// +// This code has been generated by QM 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 @@ -52,65 +56,46 @@ #include "qs_dummy.hpp" // disable the QS software tracing #endif // Q_SPY -namespace QP { - +//============================================================================ +// unnamed namespace for local definitions with internal linkage +namespace { Q_DEFINE_THIS_MODULE("qf_actq") +} // unnamed namespace -#ifdef Q_SPY -//**************************************************************************** -/// @description -/// Direct event posting is the simplest asynchronous communication method -/// available in QF. -/// -/// @param[in,out] e pointer to the event to be posted -/// @param[in] margin number of required free slots in the queue -/// after posting the event. The special value QP::QF_NO_MARGIN -/// means that this function will assert if posting fails. -/// @param[in] sender pointer to a sender object (used in QS only) -/// -/// @returns -/// 'true' (success) if the posting succeeded (with the provided margin) and -/// 'false' (failure) when the posting fails. -/// -/// @attention -/// Should be called only via the macro POST() or POST_X(). -/// -/// @note -/// The QP::QF_NO_MARGIN value of the @p margin argument is special and -/// denotes situation when the post() operation is assumed to succeed (event -/// delivery guarantee). An assertion fires, when the event cannot be -/// delivered in this case. -/// -/// @usage -/// @include qf_post.cpp -/// -/// @sa -/// QActive::postLIFO() -/// -bool QActive::post_(QEvt const * const e, - std::uint_fast16_t const margin, - void const * const sender) noexcept -#else -bool QActive::post_(QEvt const * const e, - std::uint_fast16_t const margin) noexcept +//============================================================================ +//$skip${QP_VERSION} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv +// Check for the minimum required QP version +#if (QP_VERSION < 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 { - bool status; - QF_CRIT_STAT_ - QS_TEST_PROBE_DEF(&QActive::post_) + Q_UNUSED_PAR(sender); // when Q_SPY not defined - /// @pre event pointer must be valid + //! @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 volatile into the temporary + 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; ) - if (margin == QF_NO_MARGIN) { + bool status; + if (margin == QF::NO_MARGIN) { if (nFree > 0U) { status = true; // can post } @@ -128,13 +113,13 @@ bool QActive::post_(QEvt const * const e, // is it a dynamic event? if (e->poolId_ != 0U) { - QF_EVT_REF_CTR_INC_(e); // increment the reference counter + QEvt_refCtr_inc_(e); // increment the reference counter } if (status) { // can post the event? --nFree; // one free entry just used up - m_eQueue.m_nFree = nFree; // update the volatile + m_eQueue.m_nFree = nFree; // update the original if (m_eQueue.m_nMin > nFree) { m_eQueue.m_nMin = nFree; // update minimum so far } @@ -149,17 +134,15 @@ bool QActive::post_(QEvt const * const e, QS_EQC_PRE_(m_eQueue.m_nMin); // min number of free entries QS_END_NOCRIT_PRE_() -#ifdef Q_UTEST + #ifdef Q_UTEST // callback to examine the posted event under the same conditions // as producing the #QS_QF_ACTIVE_POST trace record, which are: // the local filter for this AO ('me->prio') is set // - if ((QS::priv_.locFilter[m_prio >> 3U] - & static_cast(1U << (m_prio & 7U))) != 0U) - { + if (QS_LOC_CHECK_(m_prio)) { QS::onTestPost(sender, this, e, status); } -#endif + #endif // empty queue? if (m_eQueue.m_frontEvt == nullptr) { m_eQueue.m_frontEvt = e; // deliver event directly @@ -168,13 +151,14 @@ bool QActive::post_(QEvt const * const e, // queue is not empty, insert event into the ring-buffer else { // insert event pointer e into the buffer (FIFO) - QF_PTR_AT_(m_eQueue.m_ring, m_eQueue.m_head) = e; + 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 } - --m_eQueue.m_head; // advance the head (counter clockwise) + // advance the head (counter clockwise) + m_eQueue.m_head = (m_eQueue.m_head - 1U); } QF_CRIT_X_(); @@ -191,47 +175,38 @@ bool QActive::post_(QEvt const * const e, QS_EQC_PRE_(margin); // margin requested QS_END_NOCRIT_PRE_() -#ifdef Q_UTEST + #ifdef Q_UTEST // callback to examine the posted event under the same conditions // as producing the #QS_QF_ACTIVE_POST trace record, which are: // the local filter for this AO ('me->prio') is set // - if ((QS::priv_.locFilter[m_prio >> 3U] - & static_cast(1U << (m_prio & 7U))) != 0U) - { + if (QS_LOC_CHECK_(m_prio)) { QS::onTestPost(sender, this, e, status); } -#endif + #endif QF_CRIT_X_(); + #if (QF_MAX_EPOOL > 0U) QF::gc(e); // recycle the event to avoid a leak + #endif } return status; } -//**************************************************************************** -/// @description -/// posts an event to the event queue of the active object using the -/// Last-In-First-Out (LIFO) policy. -/// -/// @note -/// The LIFO policy should be used only for self-posting and with caution, -/// because it alters order of events in the queue. -/// -/// @param[in] e pointer to the event to post to the queue -/// -/// @sa -/// QActive::post_() -/// +} // namespace QP +//$enddef${QF::QActive::post_} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +//$define${QF::QActive::postLIFO} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv +namespace QP { + +//${QF::QActive::postLIFO} ................................................... void QActive::postLIFO(QEvt const * const e) noexcept { QF_CRIT_STAT_ - QS_TEST_PROBE_DEF(&QActive::postLIFO) - QF_CRIT_E_(); - QEQueueCtr nFree = m_eQueue.m_nFree;// tmp to avoid UB for volatile access + QEQueueCtr nFree = m_eQueue.m_nFree; + QS_TEST_PROBE_DEF(&QActive::postLIFO) QS_TEST_PROBE_ID(1, nFree = 0U; ) @@ -241,11 +216,11 @@ void QActive::postLIFO(QEvt const * const e) noexcept { // is it a dynamic event? if (e->poolId_ != 0U) { - QF_EVT_REF_CTR_INC_(e); // increment the reference counter + QEvt_refCtr_inc_(e); // increment the reference counter } --nFree; // one free entry just used up - m_eQueue.m_nFree = nFree; // update the volatile + m_eQueue.m_nFree = nFree; // update the original if (m_eQueue.m_nMin > nFree) { m_eQueue.m_nMin = nFree; // update minimum so far } @@ -259,19 +234,16 @@ void QActive::postLIFO(QEvt const * const e) noexcept { QS_EQC_PRE_(m_eQueue.m_nMin); // min number of free entries QS_END_NOCRIT_PRE_() -#ifdef Q_UTEST + #ifdef Q_UTEST // callback to examine the posted event under the same conditions // as producing the #QS_QF_ACTIVE_POST trace record, which are: // the local filter for this AO ('me->prio') is set // - if ((QS::priv_.locFilter[m_prio >> 3U] - & static_cast(1U << (m_prio & 7U))) != 0U) - { + if (QS_LOC_CHECK_(m_prio)) { QS::onTestPost(nullptr, this, e, true); } -#endif + #endif - // read volatile into temporary QEvt const * const frontEvt = m_eQueue.m_frontEvt; m_eQueue.m_frontEvt = e; // deliver the event directly to the front @@ -281,38 +253,25 @@ void QActive::postLIFO(QEvt const * const e) noexcept { } // queue was not empty, leave the event in the ring-buffer else { - ++m_eQueue.m_tail; + m_eQueue.m_tail = (m_eQueue.m_tail + 1U); if (m_eQueue.m_tail == m_eQueue.m_end) { // need to wrap the tail? m_eQueue.m_tail = 0U; // wrap around } - QF_PTR_AT_(m_eQueue.m_ring, m_eQueue.m_tail) = frontEvt; + m_eQueue.m_ring[m_eQueue.m_tail] = frontEvt; } QF_CRIT_X_(); + } -//**************************************************************************** -/// @description -/// The behavior of this function depends on the kernel used in the QF port. -/// For built-in kernels (Vanilla or QK) the function can be called only when -/// the queue is not empty, so it doesn't block. For a blocking kernel/OS -/// the function can block and wait for delivery of an event. -/// -/// @returns -/// A pointer to the received event. The returned pointer is guaranteed to be -/// valid (can't be NULL). -/// -/// @note -/// This function is used internally by a QF port to extract events from -/// the event queue of an active object. This function depends on the event -/// queue implementation and is sometimes customized in the QF port -/// (file qf_port.hpp). Depending on the definition of the macro -/// QACTIVE_EQUEUE_WAIT_(), the function might block the calling thread when -/// no events are available. -/// -QEvt const *QActive::get_(void) noexcept { - QF_CRIT_STAT_ +} // 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 @@ -325,11 +284,11 @@ QEvt const *QActive::get_(void) noexcept { if (nFree <= m_eQueue.m_end) { // remove event from the tail - m_eQueue.m_frontEvt = QF_PTR_AT_(m_eQueue.m_ring, m_eQueue.m_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 = (m_eQueue.m_tail - 1U); QS_BEGIN_NOCRIT_PRE_(QS_QF_ACTIVE_GET, m_prio) QS_TIME_PRE_(); // timestamp @@ -357,59 +316,66 @@ QEvt const *QActive::get_(void) noexcept { return e; } -//**************************************************************************** -/// @description -/// Queries the minimum of free ever present in the given event queue of -/// an active object with priority @p prio, since the active object -/// was started. -/// -/// @note -/// QP::QF::getQueueMin() is available only when the native QF event -/// queue implementation is used. Requesting the queue minimum of an unused -/// priority level raises an assertion in the QF. (A priority level becomes -/// used in QF after the call to the QP::QF::add_() function.) -/// -/// @param[in] prio Priority of the active object, whose queue is queried -/// -/// @returns -/// the minimum of free ever present in the given event queue of an active -/// object with priority @p prio, since the active object was started. -/// -std::uint_fast16_t QF::getQueueMin(std::uint_fast8_t const prio) noexcept { +} // namespace QP +//$enddef${QF::QActive::get_} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - Q_REQUIRE_ID(400, (prio <= QF_MAX_ACTIVE) - && (active_[prio] != nullptr)); +//$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(active_[prio]->m_eQueue.m_nMin); + 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) +: QActive(nullptr) { // reuse m_head for tick-rate m_eQueue.m_head = static_cast(tickRate); } -//............................................................................ -void QTicker::init(void const * const e, - std::uint_fast8_t const qs_id) noexcept + +//${QF::QTicker::init} ....................................................... +void QTicker::init( + void const * const e, + std::uint_fast8_t const qs_id) { - static_cast(e); // unused parameter - static_cast(qs_id); // unused parameter + Q_UNUSED_PAR(e); + Q_UNUSED_PAR(qs_id); m_eQueue.m_tail = 0U; } -//............................................................................ -void QTicker::dispatch(QEvt const * const e, - std::uint_fast8_t const qs_id) noexcept + +//${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) { - static_cast(e); // unused parameter - static_cast(qs_id); // unused parameter + Q_UNUSED_PAR(e); + Q_UNUSED_PAR(qs_id); QF_CRIT_STAT_ QF_CRIT_E_(); @@ -418,42 +384,39 @@ void QTicker::dispatch(QEvt const * const e, QF_CRIT_X_(); for (; nTicks > 0U; --nTicks) { - QF::TICK_X(static_cast(m_eQueue.m_head), this); + QTimeEvt::TICK_X(static_cast(m_eQueue.m_head), + this); } } -//............................................................................ -#ifdef Q_SPY -//**************************************************************************** -/// @sa -/// QActive::post_() -/// -bool QTicker::post_(QEvt const * const e, std::uint_fast16_t const margin, - void const * const sender) noexcept -#else -bool QTicker::post_(QEvt const * const e, std::uint_fast16_t const margin) - noexcept -#endif + +//${QF::QTicker::post_} ...................................................... +bool QTicker::post_( + QEvt const * const e, + std::uint_fast16_t const margin, + void const * const sender) noexcept { - static_cast(e); // unused parameter - static_cast(margin); // unused parameter + 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, QEvt::STATIC_EVT); -#else + #ifdef Q_EVT_CTOR + static QEvt const tickEvt(0U, 0U); + #else static QEvt const tickEvt = { 0U, 0U, 0U }; -#endif // Q_EVT_CTOR + #endif // Q_EVT_CTOR m_eQueue.m_frontEvt = &tickEvt; // deliver event directly - --m_eQueue.m_nFree; // one less free event + m_eQueue.m_nFree = (m_eQueue.m_nFree - 1U); // one less free event QACTIVE_EQUEUE_SIGNAL_(this); // signal the event queue } - ++m_eQueue.m_tail; // account for one more tick event + // account for one more tick event + m_eQueue.m_tail = (m_eQueue.m_tail + 1U); QS_BEGIN_NOCRIT_PRE_(QS_QF_ACTIVE_POST, m_prio) QS_TIME_PRE_(); // timestamp @@ -470,11 +433,5 @@ bool QTicker::post_(QEvt const * const e, std::uint_fast16_t const margin) return true; // the event is always posted correctly } -//**************************************************************************** -void QTicker::postLIFO(QEvt const * const e) noexcept { - static_cast(e); // unused parameter - Q_ERROR_ID(900); // operation not allowed -} - } // namespace QP - +//$enddef${QF::QTicker} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/src/qf_defer.cpp b/src/qf_defer.cpp index f572de0..189731e 100644 --- a/src/qf_defer.cpp +++ b/src/qf_defer.cpp @@ -1,40 +1,44 @@ -/// @file -/// @brief QP::QActive::defer() and QP::QActive::recall() definitions. -/// @ingroup qf -/// @cond -///*************************************************************************** -/// Last updated for version 6.9.1 -/// Last updated on 2020-09-17 -/// -/// Q u a n t u m L e a P s -/// ------------------------ -/// Modern Embedded Software -/// -/// Copyright (C) 2005-2020 Quantum Leaps. All rights reserved. -/// -/// This program is open source software: you can redistribute it and/or -/// modify it under the terms of the GNU General Public License as published -/// by the Free Software Foundation, either version 3 of the License, or -/// (at your option) any later version. -/// -/// Alternatively, this program may be distributed and modified under the -/// terms of Quantum Leaps commercial licenses, which expressly supersede -/// the GNU General Public License and are specifically designed for -/// licensees interested in retaining the proprietary status of their code. -/// -/// This program is distributed in the hope that it will be useful, -/// but WITHOUT ANY WARRANTY; without even the implied warranty of -/// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -/// GNU General Public License for more details. -/// -/// You should have received a copy of the GNU General Public License -/// along with this program. If not, see . -/// -/// Contact information: -/// -/// -///*************************************************************************** -/// @endcond +//$file${src::qf::qf_defer.cpp} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv +// +// Model: qpcpp.qm +// File: ${src::qf::qf_defer.cpp} +// +// This code has been generated by QM 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_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 @@ -47,34 +51,26 @@ #include "qs_dummy.hpp" // disable the QS software tracing #endif // Q_SPY -namespace QP { - +// unnamed namespace for local definitions with internal linkage +namespace { Q_DEFINE_THIS_MODULE("qf_defer") +} // unnamed namespace + +//$skip${QP_VERSION} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv +// Check for the minimum required QP version +#if (QP_VERSION < 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 { -//**************************************************************************** -/// @description -/// This function is part of the event deferral support. An active object -/// uses this function to defer an event @p e to the QF-supported native -/// event queue @p eq. QF correctly accounts for another outstanding -/// reference to the event and will not recycle the event at the end of -/// the RTC step. Later, the active object might recall one event at a -/// time from the event queue. -/// -/// @param[in] eq pointer to a "raw" thread-safe queue to recall -/// an event from. -/// @param[in] e pointer to the event to be deferred -/// -/// @returns -/// 'true' (success) when the event could be deferred and 'false' -/// (failure) if event deferral failed due to overflowing the queue. -/// -/// An active object can use multiple event queues to defer events of -/// different kinds. -/// -/// @sa -/// QP::QActive::recall(), QP::QEQueue, QP::QActive::flushDeferred() -/// -bool QActive::defer(QEQueue * const eq, QEvt const * const e) const noexcept { +//${QF::QActive::defer} ...................................................... +bool QActive::defer( + QEQueue * const eq, + QEvt const * const e) const noexcept +{ bool const status = eq->post(e, 0U, m_prio); QS_CRIT_STAT_ @@ -89,27 +85,12 @@ bool QActive::defer(QEQueue * const eq, QEvt const * const e) const noexcept { return status; } -//**************************************************************************** -/// @description -/// This function is part of the event deferral support. An active object -/// uses this function to recall a deferred event from a given QF -/// event queue. Recalling an event means that it is removed from the -/// deferred event queue @p eq and posted (LIFO) to the event queue of -/// the active object. -/// -/// @param[in] eq pointer to a "raw" thread-safe queue to recall -/// an event from. -/// -/// @returns -/// 'true' if an event has been recalled and 'false' if not. -/// -/// @note -/// An active object can use multiple event queues to defer events of -/// different kinds. -/// -/// @sa -/// QP::QActive::recall(), QP::QEQueue, QP::QActive::postLIFO_() -/// +} // namespace QP +//$enddef${QF::QActive::defer} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +//$define${QF::QActive::recall} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv +namespace QP { + +//${QF::QActive::recall} ..................................................... bool QActive::recall(QEQueue * const eq) noexcept { QEvt const * const e = eq->get(m_prio); // get evt from deferred queue bool recalled; @@ -132,7 +113,7 @@ bool QActive::recall(QEQueue * const eq) noexcept { // we need to decrement the reference counter once, to account // for removing the event from the deferred event queue. - QF_EVT_REF_CTR_DEC_(e); // decrement the reference counter + QEvt_refCtr_dec_(e); // decrement the reference counter } QS_BEGIN_NOCRIT_PRE_(QS_QF_ACTIVE_RECALL, m_prio) @@ -158,34 +139,28 @@ bool QActive::recall(QEQueue * const eq) noexcept { recalled = false; } return recalled; + } -//**************************************************************************** -/// @description -/// This function is part of the event deferral support. An active object -/// can use this function to flush a given QF event queue. The function makes -/// sure that the events are not leaked. -/// -/// @param[in] eq pointer to a "raw" thread-safe queue to flush. -/// -/// @returns -/// the number of events actually flushed from the queue. -/// -/// -/// @sa -/// QP::QActive::defer(), QP::QActive::recall(), QP::QEQueue -/// +} // 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 n = 0U; for (QEvt const *e = eq->get(m_prio); e != nullptr; e = eq->get(m_prio)) { - QF::gc(e); // garbage collect ++n; // count the flushed event + #if (QF_MAX_EPOOL > 0U) + QF::gc(e); // garbage collect + #endif } return n; } } // namespace QP - +//$enddef${QF::QActive::flushDeferred} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/src/qf_dyn.cpp b/src/qf_dyn.cpp index 66bb5e4..f7701ae 100644 --- a/src/qf_dyn.cpp +++ b/src/qf_dyn.cpp @@ -1,39 +1,43 @@ -/// @file -/// @brief QF/C++ dynamic event management -/// @cond -///*************************************************************************** -/// Last updated for version 6.9.3 -/// Last updated on 2021-04-09 -/// -/// Q u a n t u m L e a P s -/// ------------------------ -/// Modern Embedded Software -/// -/// Copyright (C) 2005-2021 Quantum Leaps. All rights reserved. -/// -/// This program is open source software: you can redistribute it and/or -/// modify it under the terms of the GNU General Public License as published -/// by the Free Software Foundation, either version 3 of the License, or -/// (at your option) any later version. -/// -/// Alternatively, this program may be distributed and modified under the -/// terms of Quantum Leaps commercial licenses, which expressly supersede -/// the GNU General Public License and are specifically designed for -/// licensees interested in retaining the proprietary status of their code. -/// -/// This program is distributed in the hope that it will be useful, -/// but WITHOUT ANY WARRANTY; without even the implied warranty of -/// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -/// GNU General Public License for more details. -/// -/// You should have received a copy of the GNU General Public License -/// along with this program. If not, see . -/// -/// Contact information: -/// -/// -///*************************************************************************** -/// @endcond +//$file${src::qf::qf_dyn.cpp} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv +// +// Model: qpcpp.qm +// File: ${src::qf::qf_dyn.cpp} +// +// This code has been generated by QM 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_dyn.cpp} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +//! @file +//! @brief QF/C++ dynamic event management #define QP_IMPL // this is QP implementation #include "qf_port.hpp" // QF port @@ -46,124 +50,107 @@ #include "qs_dummy.hpp" // disable the QS software tracing #endif // Q_SPY -namespace QP { +#if (QF_MAX_EPOOL > 0U) // dynamic events configured? +// unnamed namespace for local definitions with internal linkage +namespace { Q_DEFINE_THIS_MODULE("qf_dyn") +} // unnamed namespace -// Package-scope objects ***************************************************** -QF_EPOOL_TYPE_ QF_pool_[QF_MAX_EPOOL]; // allocate the event pools -std::uint_fast8_t QF_maxPool_; // number of initialized event pools - -//**************************************************************************** -/// @description -/// This function initializes one event pool at a time and must be called -/// exactly once for each event pool before the pool can be used. -/// -/// @param[in] poolSto pointer to the storage for the event pool -/// @param[in] poolSize size of the storage for the pool in bytes -/// @param[in] evtSize the block-size of the pool in bytes, which determines -/// the maximum size of events that can be allocated -/// from the pool -/// @note -/// You might initialize many event pools by making many consecutive calls -/// to the QF_poolInit() function. However, for the simplicity of the internal -/// implementation, you must initialize event pools in the ascending order of -/// the event size. -/// -/// @note The actual number of events available in the pool might be actually -/// less than (@p poolSize / @p evtSize) due to the internal alignment -/// of the blocks that the pool might perform. You can always check the -/// capacity of the pool by calling QF_getPoolMin(). -/// -/// @note The dynamic allocation of events is optional, meaning that you might -/// choose not to use dynamic events. In that case calling QP::QF::poolInit() -/// and using up memory for the memory blocks is unnecessary. -/// -/// @sa QF initialization example for QP::QF::init() -/// -void QF::poolInit(void * const poolSto, - std::uint_fast32_t const poolSize, - std::uint_fast16_t const evtSize) noexcept -{ - /// @pre cannot exceed the number of available memory pools - Q_REQUIRE_ID(200, QF_maxPool_ - < static_cast(Q_DIM(QF_pool_))); +//============================================================================ +//$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]} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - // last initialized event size - std::uint_fast16_t const lastEvtSize = ((QF_maxPool_ == 0U) - ? 0U - : QF_EPOOL_EVENT_SIZE_(QF_pool_[QF_maxPool_ - 1U])); +//============================================================================ +//$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); - /// @pre please initialize event pools in ascending order of evtSize - Q_REQUIRE_ID(201, (QF_maxPool_ == 0U) - || (lastEvtSize < evtSize)); + //! @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); + } - QF_EPOOL_INIT_(QF_pool_[QF_maxPool_], poolSto, poolSize, evtSize); - ++QF_maxPool_; // one more pool + QF_EPOOL_INIT_(QF::ePool_[QF::maxPool_], poolSto, poolSize, evtSize); + ++QF::maxPool_; // one more pool -#ifdef Q_SPY + #ifdef Q_SPY // generate the object-dictionary entry for the initialized pool - char_t obj_name[9] = "EvtPool?"; - obj_name[7] = static_cast( + char obj_name[9] = "EvtPool?"; + obj_name[7] = static_cast( static_cast('0') - + static_cast(QF_maxPool_)); - QS::obj_dict_pre_(&QF_pool_[QF_maxPool_ - 1U], &obj_name[0]); -#endif // Q_SPY + + static_cast(QF::maxPool_)); + QS::obj_dict_pre_(&QF::ePool_[QF::maxPool_ - 1U], &obj_name[0]); + #endif // Q_SPY } -//**************************************************************************** -/// @description -/// Allocates an event dynamically from one of the QF event pools. -/// -/// @param[in] evtSize the size (in bytes) of the event to allocate -/// @param[in] margin the number of un-allocated events still available -/// in a given event pool after the allocation completes -/// The special value QP::QF_NO_MARGIN means that this -/// function will assert if allocation fails. -/// @param[in] sig the signal to be assigned to the allocated event -/// -/// @returns -/// pointer to the newly allocated event. This pointer can be NULL -/// only if margin!=0 and the event cannot be allocated with the specified -/// margin still available in the given pool. -/// -/// @note -/// The internal QF function QP::QF::newX_() raises an assertion when -/// the margin argument is QP::QF_NO_MARGIN and allocation of the event turns -/// out to be impossible due to event pool depletion, or incorrect (too big) -/// size of the requested event. -/// -/// @note -/// The application code should not call this function directly. -/// The only allowed use is thorough the macros Q_NEW() or Q_NEW_X(). -/// -QEvt *QF::newX_(std::uint_fast16_t const evtSize, - std::uint_fast16_t const margin, enum_t const sig) noexcept +//${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; // find the pool id that fits the requested event size ... - for (idx = 0U; idx < QF_maxPool_; ++idx) { - if (evtSize <= QF_EPOOL_EVENT_SIZE_(QF_pool_[idx])) { + 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(310, idx < QF_maxPool_); + Q_ASSERT_ID(310, idx < QF::maxPool_); - QS_CRIT_STAT_ // get e -- platform-dependent QEvt *e; -#ifdef Q_SPY - QF_EPOOL_GET_(QF_pool_[idx], e, ((margin != QF_NO_MARGIN) ? margin : 0U), + #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_pool_[idx], e, ((margin != QF_NO_MARGIN) ? margin : 0U), + #else + QF_EPOOL_GET_(QF::ePool_[idx], e, ((margin != QF::NO_MARGIN) ? margin : 0U), 0U); -#endif + #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 @@ -181,7 +168,7 @@ QEvt *QF::newX_(std::uint_fast16_t const evtSize, // This assertion means that the event allocation failed, // and this failure cannot be tolerated. The most frequent // reason is an event leak in the application. - Q_ASSERT_ID(320, margin != QF_NO_MARGIN); + Q_ASSERT_ID(320, margin != QF::NO_MARGIN); QS_BEGIN_PRE_(QS_QF_NEW_ATTEMPT, static_cast(QS_EP_ID) + idx + 1U) @@ -193,30 +180,8 @@ QEvt *QF::newX_(std::uint_fast16_t const evtSize, return e; // can't be NULL if we can't tolerate bad allocation } -//**************************************************************************** -/// @description -/// This function implements a simple garbage collector for dynamic events. -/// Only dynamic events are candidates for recycling. (A dynamic event is one -/// that is allocated from an event pool, which is determined as non-zero -/// e->poolId_ attribute.) Next, the function decrements the reference counter -/// of the event (e->refCtr_), and recycles the event only if the counter -/// drops to zero (meaning that no more references are outstanding for this -/// event). The dynamic event is recycled by returning it to the pool from -/// which it was originally allocated. -/// -/// @param[in] e pointer to the event to recycle -/// -/// @note -/// QF invokes the garbage collector at all appropriate contexts, when -/// an event can become garbage (automatic garbage collection), so the -/// application code should have no need to call QP::QF::gc() directly. -/// The QP::QF::gc() function is exposed only for special cases when your -/// application sends dynamic events to the "raw" thread-safe queues -/// (see QP::QEQueue). Such queues are processed outside of QF and the -/// automatic garbage collection is **NOT** performed for these events. -/// In this case you need to call QP::QF::gc() explicitly. -/// -void QF::gc(QEvt const * const e) noexcept { +//${QF::QF-dyn::gc} .......................................................... +void gc(QEvt const * const e) noexcept { // is it a dynamic event? if (e->poolId_ != 0U) { QF_CRIT_STAT_ @@ -233,7 +198,7 @@ void QF::gc(QEvt const * const e) noexcept { QS_2U8_PRE_(e->poolId_, e->refCtr_); // pool Id & refCtr QS_END_NOCRIT_PRE_() - QF_EVT_REF_CTR_DEC_(e); // decrement the ref counter + QEvt_refCtr_dec_(e); // decrement the ref counter QF_CRIT_X_(); } @@ -253,43 +218,36 @@ void QF::gc(QEvt const * const e) noexcept { QF_CRIT_X_(); // pool ID must be in range - Q_ASSERT_ID(410, idx < QF_maxPool_); + Q_ASSERT_ID(410, idx < QF::maxPool_); -#ifdef Q_EVT_VIRTUAL + #ifdef Q_EVT_XTOR // explicitly exectute the destructor' // NOTE: casting 'const' away is legitimate, // because it's a pool event - QF_EVT_CONST_CAST_(e)->~QEvt(); // xtor, -#endif + QF_CONST_CAST_(QEvt*, e)->~QEvt(); // xtor, + #endif -#ifdef Q_SPY + #ifdef Q_SPY // cast 'const' away, which is OK, because it's a pool event - QF_EPOOL_PUT_(QF_pool_[idx], QF_EVT_CONST_CAST_(e), + 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_pool_[idx], QF_EVT_CONST_CAST_(e), 0U); -#endif + #else + QF_EPOOL_PUT_(QF::ePool_[idx], QF_CONST_CAST_(QEvt*, e), 0U); + #endif } } } -//**************************************************************************** -/// @description -/// Creates and returns a new reference to the current event e -/// -/// @param[in] e pointer to the current event -/// @param[in] evtRef the event reference -/// -/// @returns -/// the newly created reference to the event `e` -/// -/// @note -/// The application code should not call this function directly. -/// The only allowed use is thorough the macro Q_NEW_REF(). -/// -QEvt const *QF::newRef_(QEvt const * const e, - QEvt const * const evtRef) noexcept +//${QF::QF-dyn::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 @@ -299,7 +257,7 @@ QEvt const *QF::newRef_(QEvt const * const e, QF_CRIT_STAT_ QF_CRIT_E_(); - QF_EVT_REF_CTR_INC_(e); // increments the ref counter + QEvt_refCtr_inc_(e); // increments the ref counter QS_BEGIN_NOCRIT_PRE_(QS_QF_NEW_REF, static_cast(QS_EP_ID) @@ -314,19 +272,9 @@ QEvt const *QF::newRef_(QEvt const * const e, return e; } -//**************************************************************************** -/// @description -/// Deletes an existing reference to the event e -/// -/// @param[in] evtRef the event reference -/// -/// @note -/// The application code should not call this function directly. -/// The only allowed use is thorough the macro Q_DELETE_REF(). -/// -void QF::deleteRef_(QEvt const * const evtRef) noexcept { +//${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_)) @@ -335,16 +283,27 @@ void QF::deleteRef_(QEvt const * const evtRef) noexcept { 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 } -//**************************************************************************** -/// @description -/// Obtain the block size of any registered event pools -/// -std::uint_fast16_t QF::poolGetMaxBlockSize(void) noexcept { - return QF_EPOOL_EVENT_SIZE_(QF_pool_[QF_maxPool_ - 1U]); +//${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_(); + + return min; } +} // namespace QF } // namespace QP +//$enddef${QF::QF-dyn} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +#endif // (QF_MAX_EPOOL > 0U) dynamic events configured diff --git a/src/qf_mem.cpp b/src/qf_mem.cpp index ef1689e..e37f5f4 100644 --- a/src/qf_mem.cpp +++ b/src/qf_mem.cpp @@ -1,39 +1,43 @@ -/// @file -/// @brief QF/C++ memory management services -/// @cond -///*************************************************************************** -/// Last updated for version 6.9.1 -/// Last updated on 2020-09-17 -/// -/// Q u a n t u m L e a P s -/// ------------------------ -/// Modern Embedded Software -/// -/// Copyright (C) 2005-2020 Quantum Leaps. All rights reserved. -/// -/// This program is open source software: you can redistribute it and/or -/// modify it under the terms of the GNU General Public License as published -/// by the Free Software Foundation, either version 3 of the License, or -/// (at your option) any later version. -/// -/// Alternatively, this program may be distributed and modified under the -/// terms of Quantum Leaps commercial licenses, which expressly supersede -/// the GNU General Public License and are specifically designed for -/// licensees interested in retaining the proprietary status of their code. -/// -/// This program is distributed in the hope that it will be useful, -/// but WITHOUT ANY WARRANTY; without even the implied warranty of -/// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -/// GNU General Public License for more details. -/// -/// You should have received a copy of the GNU General Public License -/// along with this program. If not, see . -/// -/// Contact information: -/// -/// -///*************************************************************************** -/// @endcond +//$file${src::qf::qf_mem.cpp} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv +// +// Model: qpcpp.qm +// File: ${src::qf::qf_mem.cpp} +// +// This code has been generated by QM 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_mem.cpp} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +//! @file +//! @brief QF/C++ memory management services #define QP_IMPL // this is QP implementation #include "qf_port.hpp" // QF port @@ -46,21 +50,25 @@ #include "qs_dummy.hpp" // disable the QS software tracing #endif // Q_SPY +// unnamed namespace for local definitions with internal linkage +namespace { +Q_DEFINE_THIS_MODULE("qf_mem") +} // unnamed namespace + +//$skip${QP_VERSION} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv +// Check for the minimum required QP version +#if (QP_VERSION < 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 { -Q_DEFINE_THIS_MODULE("qf_mem") +//${QF::QMPool} .............................................................. -//**************************************************************************** -/// @description -/// Default constructor of a fixed block-size memory pool. -/// -/// @note -/// The memory pool is __not__ ready to use directly after instantiation. -/// To become ready, the QP::QMPool::init() must be called to give the pool -/// memory, size of this memory, and the block size to manage. -/// -QMPool::QMPool(void) +//${QF::QMPool::QMPool} ...................................................... +QMPool::QMPool() : m_start(nullptr), m_end(nullptr), m_free_head(nullptr), @@ -70,44 +78,17 @@ QMPool::QMPool(void) m_nMin(0U) {} -//**************************************************************************** -/// @description -/// Initialize a fixed block-size memory pool by providing it with the pool -/// memory to manage, size of this memory, and the block size. -/// -/// @param[in] poolSto pointer to the memory buffer for pool storage -/// @param[in] poolSize size of the storage buffer in bytes -/// @param[in] blockSize fixed-size of the memory blocks in bytes -/// -/// @attention -/// The caller of QP::QMPool::init() must make sure that the @p poolSto -/// pointer is properly __aligned__. In particular, it must be possible to -/// efficiently store a pointer at the location pointed to by @p poolSto. -/// Internally, the QP::QMPool::init() function rounds up the block size -/// @p blockSize so that it can fit an integer number of pointers. -/// This is done to achieve proper alignment of the blocks within the pool. -/// -/// @note -/// Due to the rounding of block size the actual capacity of the pool -/// might be less than (@p poolSize / @p blockSize). You can check the -/// capacity of the pool by calling the QP::QF::getPoolMin() function. -/// -/// @note -/// This function is __not__ protected by a critical section, because -/// it is intended to be called only during the initialization of the system, -/// when interrupts are not allowed yet. -/// -/// @note -/// Many QF ports use memory pools to implement the event pools. -/// -void QMPool::init(void * const poolSto, std::uint_fast32_t poolSize, - std::uint_fast16_t blockSize) noexcept +//${QF::QMPool::init} ........................................................ +void QMPool::init( + void * const poolSto, + std::uint_fast32_t poolSize, + std::uint_fast16_t 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 + //! @pre The memory block must be valid and + //! the poolSize must fit at least one free block and + //! the blockSize must not be too close to the top of the dynamic range Q_REQUIRE_ID(100, (poolSto != nullptr) - && (poolSize >= sizeof(QFreeBlock)) + && (poolSize >= static_cast(sizeof(QFreeBlock))) && (static_cast(blockSize + sizeof(QFreeBlock)) > blockSize)); @@ -140,7 +121,7 @@ void QMPool::init(void * const poolSto, std::uint_fast32_t poolSize, // chain all blocks together in a free-list... while (poolSize >= blockSize) { - fb->m_next = &QF_PTR_AT_(fb, nblocks); // setup the next link + 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); @@ -154,92 +135,28 @@ void QMPool::init(void * const poolSto, std::uint_fast32_t poolSize, m_end = fb; // the last block in this pool } -//**************************************************************************** -/// @description -/// Recycle a memory block to the fixed block-size memory pool. -/// -/// @param[in] b pointer to the memory block that is being recycled -/// @param[in] qs_id QS-id of this state machine (for QS local filter) -/// -/// @attention -/// The recycled block must be allocated from the __same__ memory pool -/// to which it is returned. -/// -/// @note -/// This function can be called from any task level or ISR level. -/// -/// @sa -/// QP::QMPool::get() -/// -void QMPool::put(void * const b, std::uint_fast8_t const qs_id) noexcept { - - /// @pre # free blocks cannot exceed the total # blocks and - /// the block pointer must be in range to come from this pool. - /// - Q_REQUIRE_ID(200, (m_nFree < m_nTot) - && QF_PTR_RANGE_(b, m_start, m_end)); - QF_CRIT_STAT_ - - QF_CRIT_E_(); - static_cast(b)->m_next = - static_cast(m_free_head); // link into the free list - m_free_head = b; // set as new head of the free list - ++m_nFree; // one more free block in this pool - - QS_BEGIN_NOCRIT_PRE_(QS_QF_MPOOL_PUT, qs_id) - QS_TIME_PRE_(); // timestamp - QS_OBJ_PRE_(this); // this memory pool - QS_MPC_PRE_(m_nFree); // the number of free blocks in the pool - QS_END_NOCRIT_PRE_() - - QF_CRIT_X_(); - static_cast(qs_id); // unused parameter, if Q_SPY not defined -} - -//**************************************************************************** -/// @description -/// The function allocates a memory block from the pool and returns a pointer -/// to the block back to the caller. -/// -/// @param[in] margin the minimum number of unused blocks still available -/// in the pool after the allocation. -/// @param[in] qs_id QS-id of this state machine (for QS local filter) -/// -/// @note -/// This function can be called from any task level or ISR level. -/// -/// @note -/// The memory pool must be initialized before any events can -/// be requested from it. Also, the QP::QMPool::get() function uses internally -/// a QF critical section, so you should be careful not to call it from within -/// a critical section when nesting of critical section is not supported. -/// -/// @attention -/// An allocated block must be later returned back to the same pool -/// from which it has been allocated. -/// -/// @sa -/// QP::QMPool::put() -/// -void *QMPool::get(std::uint_fast16_t const margin, - std::uint_fast8_t const qs_id) noexcept +//${QF::QMPool::get} ......................................................... +void * QMPool::get( + std::uint_fast16_t const margin, + std::uint_fast8_t const qs_id) noexcept { - QFreeBlock *fb; - QF_CRIT_STAT_ + Q_UNUSED_PAR(qs_id); // when Q_SPY not defined + QF_CRIT_STAT_ QF_CRIT_E_(); + // have the than margin? + QFreeBlock *fb; 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_CRIT_(310, fb != nullptr); - // put volatile to a temporary to avoid UB void * const fb_next = fb->m_next; // is the pool becoming empty? - --m_nFree; // one free block less + m_nFree = (m_nFree - 1U); // one free block less if (m_nFree == 0U) { // pool is becoming empty, so the next free block must be NULL Q_ASSERT_CRIT_(320, fb_next == nullptr); @@ -247,9 +164,13 @@ void *QMPool::get(std::uint_fast16_t const margin, m_nMin = 0U;// remember that pool got empty } else { - // pool is not empty, so the next free block must be in range - // - // NOTE: the next free block pointer can fall out of range + //! @invariant + //! The pool is not empty, so the next free-block pointer, + //! so the next free block must be in range. + //! + //! @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)); @@ -264,9 +185,9 @@ void *QMPool::get(std::uint_fast16_t const margin, QS_BEGIN_NOCRIT_PRE_(QS_QF_MPOOL_GET, qs_id) QS_TIME_PRE_(); // timestamp - QS_OBJ_PRE_(this); // this memory pool - QS_MPC_PRE_(m_nFree); // the number of free blocks in the pool - QS_MPC_PRE_(m_nMin); // the mninimum # free blocks in the pool + QS_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_() } // don't have enough free blocks at this point @@ -282,38 +203,42 @@ void *QMPool::get(std::uint_fast16_t const margin, } QF_CRIT_X_(); - static_cast(qs_id); // unused parameter, if Q_SPY not defined - return fb; // return the block or NULL pointer to the caller + } -//**************************************************************************** -/// @description -/// This function obtains the minimum number of free blocks in the given -/// event pool since this pool has been initialized by a call to -/// QP::QF::poolInit(). -/// -/// @param[in] poolId event pool ID in the range 1..QF_maxPool_, where -/// QF_maxPool_ is the number of event pools initialized -/// with the function QP::QF::poolInit(). -/// -/// @returns -/// the minimum number of unused blocks in the given event pool. -/// -std::uint_fast16_t QF::getPoolMin(std::uint_fast8_t const poolId) noexcept { - - /// @pre the poolId must be in range - Q_REQUIRE_ID(400, (QF_maxPool_ <= Q_DIM(QF_pool_)) - && (1U <= poolId) - && (poolId <= QF_maxPool_)); +//${QF::QMPool::put} ......................................................... +void QMPool::put( + void * const b, + std::uint_fast8_t const qs_id) 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_(); - std::uint_fast16_t const min = static_cast( - QF_pool_[poolId - 1U].m_nMin); + 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_(); +} - return min; +//${QF::QMPool::getBlockSize} ................................................ +QMPoolSize QMPool::getBlockSize() const noexcept { + return m_blockSize; } } // namespace QP - +//$enddef${QF::QMPool} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/src/qf_pkg.hpp b/src/qf_pkg.hpp index 0404c1f..b2aada2 100644 --- a/src/qf_pkg.hpp +++ b/src/qf_pkg.hpp @@ -1,79 +1,161 @@ -/// @file -/// @ingroup qf -/// @brief Internal (package scope) QF/C++ interface. -/// @cond -///*************************************************************************** -/// Last updated for version 6.9.1 -/// Last updated on 2020-09-17 -/// -/// Q u a n t u m L e a P s -/// ------------------------ -/// Modern Embedded Software -/// -/// Copyright (C) 2005-2020 Quantum Leaps. All rights reserved. -/// -/// This program is open source software: you can redistribute it and/or -/// modify it under the terms of the GNU General Public License as published -/// by the Free Software Foundation, either version 3 of the License, or -/// (at your option) any later version. -/// -/// Alternatively, this program may be distributed and modified under the -/// terms of Quantum Leaps commercial licenses, which expressly supersede -/// the GNU General Public License and are specifically designed for -/// licensees interested in retaining the proprietary status of their code. -/// -/// This program is distributed in the hope that it will be useful, -/// but WITHOUT ANY WARRANTY; without even the implied warranty of -/// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -/// GNU General Public License for more details. -/// -/// You should have received a copy of the GNU General Public License -/// along with this program. If not, see . -/// -/// Contact information: -/// -/// -///*************************************************************************** -/// @endcond - -#ifndef QF_PKG_HPP -#define QF_PKG_HPP - -//! helper macro to cast const away from an event pointer @p e_ -#define QF_EVT_CONST_CAST_(e_) const_cast(e_) +//$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. - /// @description - /// The purpose of this macro is to enable writing the same code for the - /// case when critical section status type is defined and when it is not. - /// If the macro #QF_CRIT_STAT_TYPE is defined, this internal macro - /// provides the definition of the critical section status variable. - /// Otherwise this macro is empty. - /// @sa #QF_CRIT_STAT_TYPE + //! + //! @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. - /// @description - /// The purpose of this macro is to enable writing the same code for the - /// case when critical section status type is defined and when it is not. - /// If the macro #QF_CRIT_STAT_TYPE is defined, this internal macro - /// invokes QF_CRIT_ENTRY() passing the key variable as the parameter. - /// Otherwise QF_CRIT_ENTRY() is invoked with a dummy parameter. - /// @sa QF_CRIT_ENTRY() + //! + //! @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. - /// @description - /// The purpose of this macro is to enable writing the same code for the - /// case when critical section status type is defined and when it is not. - /// If the macro #QF_CRIT_STAT_TYPE is defined, this internal macro - /// invokes QF_CRIT_EXIT() passing the key variable as the parameter. - /// Otherwise QF_CRIT_EXIT() is invoked with a dummy parameter. - /// @sa QF_CRIT_EXIT() - /// + //! + //! @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_) @@ -82,92 +164,40 @@ #define QF_CRIT_X_() QF_CRIT_EXIT(critStat_) #endif // QF_CRIT_STAT_TYPE -// Assertions inside the crticial section ------------------------------------ +// Assertions inside the critical section ------------------------------------ #ifdef Q_NASSERT // Q_NASSERT defined--assertion checking disabled - #define Q_ASSERT_CRIT_(id_, test_) ((void)0) - #define Q_REQUIRE_CRIT_(id_, test_) ((void)0) - #define Q_ERROR_CRIT_(id_) ((void)0) + #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_(); \ + #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_)); \ + #define Q_ERROR_CRIT_(id_) do { \ + QF_CRIT_X_(); \ + Q_onAssert(&Q_this_module_[0], static_cast(id_)); \ } while (false) #endif // Q_NASSERT - -namespace QP { - -// package-scope objects ----------------------------------------------------- -extern QF_EPOOL_TYPE_ QF_pool_[QF_MAX_EPOOL]; //!< allocate event pools -extern std::uint_fast8_t QF_maxPool_; //!< # of initialized event pools -extern QSubscrList *QF_subscrList_; //!< the subscriber list array -extern enum_t QF_maxPubSignal_; //!< the maximum published signal - -//............................................................................ -//! Structure representing a free block in the Native QF Memory Pool -/// @sa QP::QMPool -struct QFreeBlock { - QFreeBlock * volatile m_next; //!< link to the next free block -}; - -//............................................................................ -// The following flags and bitmasks are for the fields of the @c refCtr_ -// attribute of the QP::QTimeEvt class (inherited from QEvt). This attribute -// is NOT used for reference counting in time events, because the @c poolId_ -// attribute is zero ("static events"). -// -constexpr std::uint8_t TE_IS_LINKED = 1U << 7U; // flag -constexpr std::uint8_t TE_WAS_DISARMED = 1U << 6U; // flag -constexpr std::uint8_t TE_TICK_RATE = 0x0FU; // bitmask - -//**************************************************************************** -// internal helper inline functions - -//! return the Pool-ID of an event @p e -inline std::uint8_t QF_EVT_POOL_ID_ (QEvt const * const e) noexcept { - return e->poolId_; -} - -//! return the Reference Conter of an event @p e -inline std::uint8_t QF_EVT_REF_CTR_ (QEvt const * const e) noexcept { - return e->refCtr_; -} - -//! increment the refCtr_ of an event @p e -inline void QF_EVT_REF_CTR_INC_(QEvt const * const e) noexcept { - ++(QF_EVT_CONST_CAST_(e))->refCtr_; -} - -//! decrement the refCtr_ of an event @p e -inline void QF_EVT_REF_CTR_DEC_(QEvt const * const e) noexcept { - --(QF_EVT_CONST_CAST_(e))->refCtr_; -} - -} // namespace QP - -//! macro to test that a pointer @p x_ is in range between @p min_ and @p max_ -/// @description -/// This macro is specifically and exclusively used for checking the range -/// of a block pointer returned to the pool. Such a check must rely on the -/// pointer arithmetic not compliant with the MISRA-C++:2008 rules ??? and -/// ???. Defining a specific macro for this purpose allows to selectively -/// disable the warnings for this particular case. +//! 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_))) -//! access element at index @p i_ from the base pointer @p base_ -#define QF_PTR_AT_(base_, i_) (base_[i_]) - -#endif // QF_PKG_HPP +#endif // QF_PKG_HPP_ diff --git a/src/qf_port.cpp b/src/qf_port.cpp index 343ee5a..78e2ef6 100644 --- a/src/qf_port.cpp +++ b/src/qf_port.cpp @@ -52,6 +52,10 @@ #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 @@ -88,40 +92,37 @@ void QF::stop(void) { onCleanup(); /* cleanup callback */ } /*..........................................................................*/ -void QActive::start(std::uint_fast8_t const prio, +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) { - - /* task name provided by the user in QF_setTaskName() or default name */ - char_t const *taskName = (m_thread != nullptr) - ? static_cast(m_thread) - : static_cast("AO"); - - Q_REQUIRE_ID(200, (0U < prio) - && (prio <= QF_MAX_ACTIVE) /* in range */ - && (qSto != nullptr) /* queue storage must be provided */ - && (qLen > 0U) /* queue size must be provided */ - // && (stkSto != nullptr) /* stack storage must be provided */ - && (stkSize > 0U)); /* stack size must be provided */ + 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 = prio; /* save the QF priority */ - QF::add_(this); // make QF aware of this active object + 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) + ? static_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 */ - prio + tskIDLE_PRIORITY, /* FreeRTOS priority */ + m_prio + tskIDLE_PRIORITY, /* FreeRTOS priority */ //static_cast(stkSto), /* stack storage */ &m_thread, /* task buffer */ static_cast(QP_CPU_NUM)); /* CPU number */ @@ -130,7 +131,7 @@ void QActive::start(std::uint_fast8_t const prio, } /*..........................................................................*/ void QActive::setAttr(std::uint32_t attr1, void const *attr2) { - /* this function must be called before QACTIVE_START(), + /* 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); @@ -144,31 +145,24 @@ void QActive::setAttr(std::uint32_t attr1, void const *attr2) { } // thread for active objects ------------------------------------------------- -void QF::thread_(QActive *act) { +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 - gc(e); // check if the event is garbage, and collect it if so + QF::gc(e); // check if the event is garbage, and collect it if so } } /*..........................................................................*/ static void task_function(void *pvParameters) { /* FreeRTOS task signature */ - QActive *act = reinterpret_cast(pvParameters); - QF::thread_(act); + QP::QActive::thread_(reinterpret_cast(pvParameters)); } /*==========================================================================*/ /* The "FromISR" QP APIs for the FreeRTOS port... */ -#ifdef Q_SPY bool QActive::postFromISR_(QEvt const * const e, std::uint_fast16_t const margin, void *par, void const * const sender) noexcept -#else -bool QActive::postFromISR_(QEvt const * const e, - std::uint_fast16_t const margin, - void *par) noexcept -#endif { QEQueueCtr nFree; /* temporary to avoid UB for volatile access */ bool status; @@ -178,13 +172,13 @@ bool QActive::postFromISR_(QEvt const * const e, portENTER_CRITICAL_ISR(&QF_esp32mux); nFree = m_eQueue.m_nFree; /* get volatile into the temporary */ - if (margin == QF_NO_MARGIN) { + if (margin == QF::NO_MARGIN) { if (nFree > 0U) { status = true; /* can post */ } else { status = false; /* cannot post */ - Q_ERROR_ID(410); /* must be able to post the event */ + Q_ERROR_ID(810); /* must be able to post the event */ } } else if (nFree > static_cast(margin)) { @@ -207,7 +201,7 @@ bool QActive::postFromISR_(QEvt const * const e, /* is it a pool event? */ if (e->poolId_ != 0U) { - QF_EVT_REF_CTR_INC_(e); /* increment the reference counter */ + QEvt_refCtr_inc_(e); /* increment the reference counter */ } --nFree; /* one free entry just used up */ m_eQueue.m_nFree = nFree; /* update the volatile */ @@ -226,7 +220,7 @@ bool QActive::postFromISR_(QEvt const * const e, /* queue is not empty, insert event into the ring-buffer */ else { /* insert event into the ring buffer (FIFO) */ - QF_PTR_AT_(m_eQueue.m_ring, m_eQueue.m_head) = e; + 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 */ } @@ -255,17 +249,13 @@ bool QActive::postFromISR_(QEvt const * const e, } /*..........................................................................*/ -#ifdef Q_SPY -void QF::publishFromISR_(QEvt const *e, void *par, +void QActive::publishFromISR_(QEvt const *e, void *par, void const * const sender) noexcept -#else -void QF::publishFromISR_(QEvt const *e, void *par) noexcept -#endif { 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) < QF_maxPubSignal_); + Q_REQUIRE_ID(500, static_cast(e->sig) < QActive::maxPubSignal_); portENTER_CRITICAL_ISR(&QF_esp32mux); @@ -285,11 +275,11 @@ void QF::publishFromISR_(QEvt const *e, void *par) noexcept * recycles the event if the counter drops to zero. This covers the * case when the event was published without any subscribers. */ - QF_EVT_REF_CTR_INC_(e); + QEvt_refCtr_inc_(e); } /* make a local, modifiable copy of the subscriber list */ - subscrList = QF_PTR_AT_(QF_subscrList_, e->sig); + subscrList = QActive::subscrList_[e->sig]; portEXIT_CRITICAL_ISR(&QF_esp32mux); if (subscrList.notEmpty()) { /* any subscribers? */ @@ -299,12 +289,12 @@ void QF::publishFromISR_(QEvt const *e, void *par) noexcept /* 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, active_[p] != (QActive *)0); + Q_ASSERT_ID(510, registry_[p] != nullptr); // POST_FROM_ISR() asserts internally if the queue overflows - (void)active_[p]->POST_FROM_ISR(e, par, sender); + registry_[p]->POST_FROM_ISR(e, par, sender); - subscrList.rmove(p); // remove the handled subscriber + subscrList.remove(p); // remove the handled subscriber if (subscrList.notEmpty()) { // still more subscribers? p = subscrList.findMax(); // the highest-prio subscriber } @@ -319,17 +309,12 @@ void QF::publishFromISR_(QEvt const *e, void *par) noexcept * and recycles the event if the counter drops to zero. This covers both * cases when the event was published with or without any subscribers. */ - gcFromISR(e); + QF::gcFromISR(e); } - /*..........................................................................*/ -#ifdef Q_SPY -void QF::tickXfromISR_(std::uint_fast8_t const tickRate, void *par, +void QTimeEvt::tickFromISR_(std::uint_fast8_t const tickRate, void *par, void const * const sender) noexcept -#else -void QF::tickXfromISR_(std::uint_fast8_t const tickRate, void *par) noexcept -#endif { QTimeEvt *prev = &timeEvtHead_[tickRate]; @@ -353,7 +338,7 @@ void QF::tickXfromISR_(std::uint_fast8_t const tickRate, void *par) noexcept /* sanity check */ Q_ASSERT_ID(610, prev != nullptr); - prev->m_next = QF::timeEvtHead_[tickRate].toTimeEvt(); + prev->m_next = timeEvtHead_[tickRate].toTimeEvt(); timeEvtHead_[tickRate].m_act = nullptr; t = prev->m_next; /* switch to the new list */ } @@ -434,24 +419,24 @@ QEvt *QF::newXfromISR_(std::uint_fast16_t const evtSize, 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_pool_[idx])) { + 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_); + Q_ASSERT_ID(710, idx < QF::maxPool_); /* get e -- platform-dependent */ #ifdef Q_SPY e = static_cast( - QF_pool_[idx].getFromISR(((margin != QF_NO_MARGIN) + QF::ePool_[idx].getFromISR(((margin != QF::NO_MARGIN) ? margin : 0U), static_cast(QS_EP_ID) + idx + 1U)); #else e = static_cast( - QF_pool_[idx].getFromISR(((margin != QF_NO_MARGIN) + QF::ePool_[idx].getFromISR(((margin != QF::NO_MARGIN) ? margin : 0U), 0U)); #endif @@ -474,7 +459,7 @@ QEvt *QF::newXfromISR_(std::uint_fast16_t const evtSize, /* event cannot be allocated */ else { /* must tolerate bad alloc. */ - Q_ASSERT_ID(720, margin != QF_NO_MARGIN); + Q_ASSERT_ID(720, margin != QF::NO_MARGIN); #ifdef Q_SPY portENTER_CRITICAL_ISR(&QF_esp32mux); @@ -497,7 +482,7 @@ void QF::gcFromISR(QEvt const * const e) noexcept { /* isn't this the last ref? */ if (e->refCtr_ > 1U) { - QF_EVT_REF_CTR_DEC_(e); /* decrements the ref counter */ + QEvt_refCtr_dec_(e); /* decrements the ref counter */ QS_BEGIN_NOCRIT_PRE_(QS_QF_GC_ATTEMPT, static_cast(e->poolId_)) @@ -523,7 +508,7 @@ void QF::gcFromISR(QEvt const * const e) noexcept { portEXIT_CRITICAL_ISR(&QF_esp32mux); /* pool ID must be in range */ - Q_ASSERT_ID(810, idx < QF_maxPool_); + Q_ASSERT_ID(810, idx < QF::maxPool_); #ifdef Q_EVT_VIRTUAL // explicitly exectute the destructor' // NOTE: casting 'const' away is legitimate, @@ -532,10 +517,10 @@ void QF::gcFromISR(QEvt const * const e) noexcept { #endif #ifdef Q_SPY // cast 'const' away, which is OK, because it's a pool event - QF_pool_[idx].putFromISR(QF_EVT_CONST_CAST_(e), + QF::ePool_[idx].putFromISR(QF_CONST_CAST_(QEvt*, e), static_cast(QS_EP_ID) + e->poolId_); #else - QF_pool_[idx].putFromISR(QF_EVT_CONST_CAST_(e), 0U); + QF::ePool_[idx].putFromISR(QF_CONST_CAST_(QEvt*, e), 0U); #endif } } diff --git a/src/qf_port.hpp b/src/qf_port.hpp index 0586974..461b2b8 100644 --- a/src/qf_port.hpp +++ b/src/qf_port.hpp @@ -89,7 +89,7 @@ extern PRIVILEGED_DATA portMUX_TYPE QF_esp32mux; publishFromISR_((e_), (pxHigherPrioTaskWoken_),(sender_)) #define POST_FROM_ISR(e_, pxHigherPrioTaskWoken_, sender_) \ - postFromISR_((e_), QP::QF_NO_MARGIN, \ + postFromISR_((e_), QP::QF::NO_MARGIN, \ (pxHigherPrioTaskWoken_), (sender_)) #define POST_X_FROM_ISR(e_, margin_, pxHigherPrioTaskWoken_, sender_) \ @@ -102,7 +102,7 @@ extern PRIVILEGED_DATA portMUX_TYPE QF_esp32mux; publishFromISR_((e_), (pxHigherPrioTaskWoken_)) #define POST_FROM_ISR(e_, pxHigherPrioTaskWoken_, dummy) \ - postFromISR_((e_), QP::QF_NO_MARGIN, (pxHigherPrioTaskWoken_)) + postFromISR_((e_), QP::QF::NO_MARGIN, (pxHigherPrioTaskWoken_)) #define POST_X_FROM_ISR(me_, e_, margin_, \ pxHigherPrioTaskWoken_, dummy) \ @@ -118,11 +118,11 @@ extern PRIVILEGED_DATA portMUX_TYPE QF_esp32mux; #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)) \ + (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_ *)QF_newXFromISR_(sizeof(evtT_), \ + (e_) = (evtT_ *)QP::QF::newXfromISR_(sizeof(evtT_), \ (margin_), 0); \ if ((e_) != (evtT_ *)0) { \ evtT_##_ctor((e_), (sig_), ##__VA_ARGS__); \ @@ -134,10 +134,10 @@ extern PRIVILEGED_DATA portMUX_TYPE QF_esp32mux; #define Q_NEW_FROM_ISR(evtT_, sig_) \ (static_cast(QP::QF::newXfromISR_( \ static_cast(sizeof(evtT_)), \ - QP::QF_NO_MARGIN, (sig_)))) + QP::QF::NO_MARGIN, (sig_)))) #define Q_NEW_X_FROM_ISR(e_, evtT_, margin_, sig_) ((e_) = \ - (evtT_ *)QF_newXFromISR_((uint_fast16_t)sizeof(evtT_), \ + (evtT_ *)QP::QF::newXfromISR_((uint_fast16_t)sizeof(evtT_), \ (margin_), (sig_))) #endif /* Q_EVT_CTOR */ diff --git a/src/qf_ps.cpp b/src/qf_ps.cpp index b1ad29f..79caa83 100644 --- a/src/qf_ps.cpp +++ b/src/qf_ps.cpp @@ -1,41 +1,44 @@ -/// @file -/// @brief QF/C++ Publish-Subscribe services -/// definitions. -/// @ingroup qf -/// @cond -///*************************************************************************** -/// Last updated for version 6.9.3 -/// Last updated on 2021-02-26 -/// -/// Q u a n t u m L e a P s -/// ------------------------ -/// Modern Embedded Software -/// -/// Copyright (C) 2005-2021 Quantum Leaps. All rights reserved. -/// -/// This program is open source software: you can redistribute it and/or -/// modify it under the terms of the GNU General Public License as published -/// by the Free Software Foundation, either version 3 of the License, or -/// (at your option) any later version. -/// -/// Alternatively, this program may be distributed and modified under the -/// terms of Quantum Leaps commercial licenses, which expressly supersede -/// the GNU General Public License and are specifically designed for -/// licensees interested in retaining the proprietary status of their code. -/// -/// This program is distributed in the hope that it will be useful, -/// but WITHOUT ANY WARRANTY; without even the implied warranty of -/// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -/// GNU General Public License for more details. -/// -/// You should have received a copy of the GNU General Public License -/// along with this program. If not, see . -/// -/// Contact information: -/// -/// -///*************************************************************************** -/// @endcond +//$file${src::qf::qf_ps.cpp} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv +// +// Model: qpcpp.qm +// File: ${src::qf::qf_ps.cpp} +// +// This code has been generated by QM 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_ps.cpp} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +//! @file +//! @brief QF/C++ Publish-Subscribe services +//! definitions. #define QP_IMPL // this is QP implementation #include "qf_port.hpp" // QF port @@ -48,83 +51,61 @@ #include "qs_dummy.hpp" // disable the QS software tracing #endif // Q_SPY +// unnamed namespace for local definitions with internal linkage +namespace { +Q_DEFINE_THIS_MODULE("qf_ps") +} // unnamed namespace + +//$skip${QP_VERSION} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv +// Check for the minimum required QP version +#if (QP_VERSION < 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_; -Q_DEFINE_THIS_MODULE("qf_ps") +} // namespace QP +//$enddef${QF::QActive::subscrList_} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +//$define${QF::QActive::maxPubSignal_} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv +namespace QP { +enum_t QActive::maxPubSignal_; -// Package-scope objects ***************************************************** -QSubscrList *QF_subscrList_; -enum_t QF_maxPubSignal_; - -//**************************************************************************** -/// @description -/// This function initializes the publish-subscribe facilities of QF and must -/// be called exactly once before any subscriptions/publications occur in -/// the application. -/// -/// @param[in] subscrSto pointer to the array of subscriber lists -/// @param[in] maxSignal the dimension of the subscriber array and at -/// the same time the maximum signal that can be -/// published or subscribed. -/// -/// The array of subscriber-lists is indexed by signals and provides a mapping -/// between the signals and subscriber-lists. The subscriber-lists are -/// bitmasks of type QP::QSubscrList, each bit in the bit mask corresponding -/// to the unique priority of an active object. The size of the -/// QP::QSubscrList bitmask depends on the value of the #QF_MAX_ACTIVE macro. -/// -/// @note -/// The publish-subscribe facilities are optional, meaning that you might -/// choose not to use publish-subscribe. In that case calling QF::psInit() -/// and using up memory for the subscriber-lists is unnecessary. -/// -/// @sa -/// QP::QSubscrList -/// -/// @usage -/// The following example shows the typical initialization sequence of QF: -/// @include qf_main.cpp -/// -void QF::psInit(QSubscrList * const subscrSto, - enum_t const maxSignal) noexcept -{ - QF_subscrList_ = subscrSto; - QF_maxPubSignal_ = maxSignal; +} // namespace QP +//$enddef${QF::QActive::maxPubSignal_} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +//$define${QF::QActive::psInit} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv +namespace QP { - // zero the subscriber list, so that the framework can start correctly - // even if the startup code fails to clear the uninitialized data - // (as is required by the C++ Standard) - bzero(subscrSto, static_cast(maxSignal) * sizeof(QSubscrList)); +//${QF::QActive::psInit} ..................................................... +void QActive::psInit( + QSubscrList * const subscrSto, + enum_t const maxSignal) noexcept +{ + subscrList_ = subscrSto; + maxPubSignal_ = maxSignal; + QF::bzero(subscrSto, static_cast(maxSignal) + * sizeof(QSubscrList)); } -//**************************************************************************** -/// @description -/// This function posts (using the FIFO policy) the event @a e to **all** -/// active objects that have subscribed to the signal @a e->sig, which is -/// called _multicasting_. The multicasting performed in this function is -/// very efficient based on reference-counting inside the published event -/// ("zero-copy" event multicasting). This function is designed to be -/// callable from any part of the system, including ISRs, device drivers, -/// and active objects. -/// -/// @note -/// To avoid any unexpected re-ordering of events posted into AO queues, -/// the event multicasting is performed with scheduler __locked__. However, -/// the scheduler is locked only up to the priority level of the highest- -/// priority subscriber, so any AOs of even higher priority, which did not -/// subscribe to this event are _not_ affected. -/// -#ifndef Q_SPY -void QF::publish_(QEvt const * const e) noexcept { -#else -void QF::publish_(QEvt const * const e, - void const * const sender, - std::uint_fast8_t const qs_id) noexcept +} // namespace QP +//$enddef${QF::QActive::psInit} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +//$define${QF::QActive::publish_} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv +namespace QP { + +//${QF::QActive::publish_} ................................................... +void QActive::publish_( + QEvt const * const e, + void const * const sender, + std::uint_fast8_t const qs_id) noexcept { -#endif - /// @pre the published signal must be within the configured range - Q_REQUIRE_ID(100, static_cast(e->sig) < QF_maxPubSignal_); + Q_UNUSED_PAR(sender); // when Q_SPY not defined + Q_UNUSED_PAR(qs_id); // when Q_SPY not defined + + //! @pre the published signal must be within the configured range + Q_REQUIRE_ID(100, static_cast(e->sig) < maxPubSignal_); QF_CRIT_STAT_ QF_CRIT_E_(); @@ -145,29 +126,31 @@ void QF::publish_(QEvt const * const e, // recycles the event if the counter drops to zero. This covers the // case when the event was published without any subscribers. // - QF_EVT_REF_CTR_INC_(e); + QEvt_refCtr_inc_(e); } // make a local, modifiable copy of the subscriber list - QPSet subscrList = QF_PTR_AT_(QF_subscrList_, e->sig); + QPSet subscrList = subscrList_[e->sig]; QF_CRIT_X_(); if (subscrList.notEmpty()) { // any subscribers? // the highest-prio subscriber std::uint_fast8_t p = subscrList.findMax(); + QActive *a = registry_[p]; QF_SCHED_STAT_ - QF_SCHED_LOCK_(p); // lock the scheduler up to prio 'p' - do { // loop over all subscribers */ + 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, active_[p] != nullptr); + Q_ASSERT_ID(210, a != nullptr); // POST() asserts internally if the queue overflows - static_cast(active_[p]->POST(e, sender)); + a->POST(e, sender); - subscrList.rmove(p); // remove the handled subscriber + subscrList.remove(p); // remove the handled subscriber if (subscrList.notEmpty()) { // still more subscribers? p = subscrList.findMax(); // the highest-prio subscriber + a = registry_[p]; } else { p = 0U; // no more subscribers @@ -180,33 +163,24 @@ void QF::publish_(QEvt const * const e, // and recycles the event if the counter drops to zero. This covers both // cases when the event was published with or without any subscribers. // - gc(e); + #if (QF_MAX_EPOOL > 0U) + QF::gc(e); + #endif } +} // namespace QP +//$enddef${QF::QActive::publish_} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +//$define${QF::QActive::subscribe} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv +namespace QP { -//**************************************************************************** -/// @description -/// This function is part of the Publish-Subscribe event delivery mechanism -/// available in QF. Subscribing to an event means that the framework will -/// start posting all published events with a given signal @p sig to the -/// event queue of the active object. -/// -/// @param[in] sig event signal to subscribe -/// -/// The following example shows how the Table active object subscribes -/// to three signals in the initial transition: -/// @include qf_subscribe.cpp -/// -/// @sa -/// QP::QF::publish_(), QP::QActive::unsubscribe(), and -/// QP::QActive::unsubscribeAll() -/// +//${QF::QActive::subscribe} .................................................. void QActive::subscribe(enum_t const sig) const noexcept { std::uint_fast8_t const p = static_cast(m_prio); + Q_REQUIRE_ID(300, (Q_USER_SIG <= sig) - && (sig < QF_maxPubSignal_) + && (sig < maxPubSignal_) && (0U < p) && (p <= QF_MAX_ACTIVE) - && (QF::active_[p] == this)); + && (registry_[p] == this)); QF_CRIT_STAT_ QF_CRIT_E_(); @@ -217,43 +191,25 @@ void QActive::subscribe(enum_t const sig) const noexcept { QS_OBJ_PRE_(this); // this active object QS_END_NOCRIT_PRE_() - QF_PTR_AT_(QF_subscrList_, sig).insert(p); // insert into subscriber-list + subscrList_[sig].insert(p); // insert into subscriber-list QF_CRIT_X_(); } -//**************************************************************************** -/// @description -/// This function is part of the Publish-Subscribe event delivery mechanism -/// available in QF. Un-subscribing from an event means that the framework -/// will stop posting published events with a given signal @p sig to the -/// event queue of the active object. -/// -/// @param[in] sig event signal to unsubscribe -/// -/// @note -/// Due to the latency of event queues, an active object should NOT -/// assume that a given signal @p sig will never be dispatched to the -/// state machine of the active object after un-subscribing from that signal. -/// The event might be already in the queue, or just about to be posted -/// and the un-subscribe operation will not flush such events. -/// -/// @note -/// Un-subscribing from a signal that has never been subscribed in the -/// first place is considered an error and QF will raise an assertion. -/// -/// @sa -/// QP::QF::publish_(), QP::QActive::subscribe(), and -/// QP::QActive::unsubscribeAll() -/// +} // namespace QP +//$enddef${QF::QActive::subscribe} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +//$define${QF::QActive::unsubscribe} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv +namespace QP { + +//${QF::QActive::unsubscribe} ................................................ void QActive::unsubscribe(enum_t const sig) const noexcept { std::uint_fast8_t const p = static_cast(m_prio); - //! @pre the singal and the prioriy must be in ragne, the AO must also + //! @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 < QF_maxPubSignal_) + && (sig < maxPubSignal_) && (0U < p) && (p <= QF_MAX_ACTIVE) - && (QF::active_[p] == this)); + && (registry_[p] == this)); QF_CRIT_STAT_ QF_CRIT_E_(); @@ -264,43 +220,28 @@ void QActive::unsubscribe(enum_t const sig) const noexcept { QS_OBJ_PRE_(this); // this active object QS_END_NOCRIT_PRE_() - QF_PTR_AT_(QF_subscrList_,sig).rmove(p); // remove from subscriber-list + subscrList_[sig].remove(p); // remove from subscriber-list QF_CRIT_X_(); } -//**************************************************************************** -/// @description -/// This function is part of the Publish-Subscribe event delivery mechanism -/// available in QF. Un-subscribing from all events means that the framework -/// will stop posting any published events to the event queue of the active -/// object. -/// -/// @note -/// Due to the latency of event queues, an active object should NOT -/// assume that no events will ever be dispatched to the state machine of -/// the active object after un-subscribing from all events. -/// The events might be already in the queue, or just about to be posted -/// and the un-subscribe operation will not flush such events. Also, the -/// alternative event-delivery mechanisms, such as direct event posting or -/// time events, can be still delivered to the event queue of the active -/// object. -/// -/// @sa -/// QP::QF::publish_(), QP::QActive::subscribe(), and -/// QP::QActive::unsubscribe() -/// -void QActive::unsubscribeAll(void) const noexcept { +} // namespace QP +//$enddef${QF::QActive::unsubscribe} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +//$define${QF::QActive::unsubscribeAll} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv +namespace QP { + +//${QF::QActive::unsubscribeAll} ............................................. +void QActive::unsubscribeAll() const noexcept { std::uint_fast8_t const p = static_cast(m_prio); Q_REQUIRE_ID(500, (0U < p) && (p <= QF_MAX_ACTIVE) - && (QF::active_[p] == this)); + && (registry_[p] == this)); - for (enum_t sig = Q_USER_SIG; sig < QF_maxPubSignal_; ++sig) { + for (enum_t sig = Q_USER_SIG; sig < maxPubSignal_; ++sig) { QF_CRIT_STAT_ QF_CRIT_E_(); - if (QF_PTR_AT_(QF_subscrList_, sig).hasElement(p)) { - QF_PTR_AT_(QF_subscrList_, sig).rmove(p); + if (subscrList_[sig].hasElement(p)) { + subscrList_[sig].remove(p); QS_BEGIN_NOCRIT_PRE_(QS_QF_ACTIVE_UNSUBSCRIBE, m_prio) QS_TIME_PRE_(); // timestamp @@ -317,4 +258,4 @@ void QActive::unsubscribeAll(void) const noexcept { } } // namespace QP - +//$enddef${QF::QActive::unsubscribeAll} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/src/qf_qact.cpp b/src/qf_qact.cpp index f6a8516..47c7d40 100644 --- a/src/qf_qact.cpp +++ b/src/qf_qact.cpp @@ -1,60 +1,258 @@ -/// @file -/// @brief QP::QActive::QActive() definition -/// @cond -///*************************************************************************** -/// Last updated for version 6.8.0 -/// Last updated on 2020-01-13 -/// -/// Q u a n t u m L e a P s -/// ------------------------ -/// Modern Embedded Software -/// -/// Copyright (C) 2005-2020 Quantum Leaps. All rights reserved. -/// -/// This program is open source software: you can redistribute it and/or -/// modify it under the terms of the GNU General Public License as published -/// by the Free Software Foundation, either version 3 of the License, or -/// (at your option) any later version. -/// -/// Alternatively, this program may be distributed and modified under the -/// terms of Quantum Leaps commercial licenses, which expressly supersede -/// the GNU General Public License and are specifically designed for -/// licensees interested in retaining the proprietary status of their code. -/// -/// This program is distributed in the hope that it will be useful, -/// but WITHOUT ANY WARRANTY; without even the implied warranty of -/// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -/// GNU General Public License for more details. -/// -/// You should have received a copy of the GNU General Public License -/// along with this program. If not, see . -/// -/// Contact information: -/// -/// -///*************************************************************************** -/// @endcond - -#define QP_IMPL // this is QP implementation -#include "qf_port.hpp" // QF port - -namespace QP { - -//**************************************************************************** +//$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. +// +// 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_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 +#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 + +//============================================================================ +namespace { // unnamed local 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_prio(0U), + m_pthre(0U) { - m_state.fun = Q_STATE_CAST(&QHsm::top); + #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_OS_OBJECT_TYPE + QF::bzero(&m_osObject, sizeof(m_osObject)); + #endif -#ifdef QF_THREAD_TYPE - QF::bzero(&m_thread, sizeof(m_thread)); -#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 { + 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)); + std::uint8_t prev_thre = m_pthre; + std::uint8_t next_thre = m_pthre; + std::uint_fast8_t p; + + for (p = static_cast(m_prio) - 1U; p > 0U; --p) { + if (registry_[p] != nullptr) { + prev_thre = registry_[p]->m_pthre; + break; + } + } + for (p = static_cast(m_prio) + 1U; + p <= QF_MAX_ACTIVE; ++p) + { + if (registry_[p] != nullptr) { + next_thre = registry_[p]->m_pthre; + break; + } + } + + //! @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 + + QF_CRIT_STAT_ + QF_CRIT_E_(); + // register the AO at the "QF-priority" + registry_[m_prio] = this; + QF_CRIT_X_(); +} + +} // namespace QP +//$enddef${QF::QActive::register_} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +//$define${QF::QActive::unregister_} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv +namespace QP { + +//${QF::QActive::unregister_} ................................................ +void QActive::unregister_() noexcept { + std::uint_fast8_t const p = static_cast(m_prio); + + Q_REQUIRE_ID(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]; +} + +#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 80d9c5b..d9ccc99 100644 --- a/src/qf_qeq.cpp +++ b/src/qf_qeq.cpp @@ -1,39 +1,43 @@ -/// @file -/// @brief QP::QEQueue implementation -/// @cond -///*************************************************************************** -/// Last updated for version 6.9.1 -/// Last updated on 2020-09-17 -/// -/// Q u a n t u m L e a P s -/// ------------------------ -/// Modern Embedded Software -/// -/// Copyright (C) 2005-2020 Quantum Leaps. All rights reserved. -/// -/// This program is open source software: you can redistribute it and/or -/// modify it under the terms of the GNU General Public License as published -/// by the Free Software Foundation, either version 3 of the License, or -/// (at your option) any later version. -/// -/// Alternatively, this program may be distributed and modified under the -/// terms of Quantum Leaps commercial licenses, which expressly supersede -/// the GNU General Public License and are specifically designed for -/// licensees interested in retaining the proprietary status of their code. -/// -/// This program is distributed in the hope that it will be useful, -/// but WITHOUT ANY WARRANTY; without even the implied warranty of -/// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -/// GNU General Public License for more details. -/// -/// You should have received a copy of the GNU General Public License -/// along with this program. If not, see . -/// -/// Contact information: -/// -/// -///*************************************************************************** -/// @endcond +//$file${src::qf::qf_qeq.cpp} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv +// +// Model: qpcpp.qm +// File: ${src::qf::qf_qeq.cpp} +// +// This code has been generated by QM 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_qeq.cpp} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +//! @file +//! @brief QP::QEQueue implementation #define QP_IMPL // this is QP implementation #include "qf_port.hpp" // QF port @@ -46,16 +50,25 @@ #include "qs_dummy.hpp" // disable the QS software tracing #endif // Q_SPY -namespace QP { - +// unnamed namespace for local definitions with internal linkage +namespace { Q_DEFINE_THIS_MODULE("qf_qeq") +} // unnamed namespace + +//$skip${QP_VERSION} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv +// Check for the minimum required QP version +#if (QP_VERSION < 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} ............................................................. -//**************************************************************************** -/// @description -/// Default constructor -/// -QEQueue::QEQueue(void) noexcept +//${QF::QEQueue::QEQueue} .................................................... +QEQueue::QEQueue() noexcept : m_frontEvt(nullptr), m_ring(nullptr), m_end(0U), @@ -65,24 +78,10 @@ QEQueue::QEQueue(void) noexcept m_nMin(0U) {} -//**************************************************************************** -/// @description -/// Initialize the event queue by giving it the storage for the ring buffer. -/// -/// @param[in] qSto an array of pointers to QP::QEvt to sereve as the -/// ring buffer for the event queue -/// @param[in] qLen the length of the qSto[] buffer (in QP::QEvt pointers) -/// -/// @note -/// The actual capacity of the queue is qLen + 1, because of the extra -/// location forntEvt. -/// -/// @note This function is also used to initialize the event queues of active -/// objects in the built-int QV, QK and QXK kernels, as well as other -/// QP ports to OSes/RTOSes that do provide a suitable message queue. -/// -void QEQueue::init(QEvt const *qSto[], - std::uint_fast16_t const qLen) noexcept +//${QF::QEQueue::init} ....................................................... +void QEQueue::init( + QEvt const * qSto[], + std::uint_fast16_t const qLen) noexcept { m_frontEvt = nullptr; // no events in the queue m_ring = &qSto[0]; @@ -95,54 +94,31 @@ void QEQueue::init(QEvt const *qSto[], m_nMin = m_nFree; } -//**************************************************************************** -/// @description -/// Post an event to the "raw" thread-safe event queue using the -/// First-In-First-Out (FIFO) order. -/// -/// @param[in] e pointer to the event to be posted to the queue -/// @param[in] margin number of required free slots in the queue after -/// posting the event. The special value QP::QF_NO_MARGIN -/// means that this function will assert if posting -/// @param[in] qs_id QS-id of this state machine (for QS local filter) -/// -/// @note -/// The QP::QF_NO_MARGIN value of the @p margin argument is special and -/// denotes situation when the post() operation is assumed to succeed (event -/// delivery guarantee). An assertion fires, when the event cannot be -/// delivered in this case. -/// -/// @returns 'true' (success) when the posting succeeded with the provided -/// margin and 'false' (failure) when the posting fails. -/// -/// @note This function can be called from any task context or ISR context. -/// -/// @sa QP::QEQueue::postLIFO(), QP::QEQueue::get() -/// -bool QEQueue::post(QEvt const * const e, - std::uint_fast16_t const margin, - std::uint_fast8_t const qs_id) noexcept +//${QF::QEQueue::post} ....................................................... +bool QEQueue::post( + QEvt const * const e, + std::uint_fast16_t const margin, + std::uint_fast8_t const qs_id) noexcept { - bool status; - QF_CRIT_STAT_ - - /// @pre event must be valid + //! @pre event must be valid Q_REQUIRE_ID(200, e != nullptr); + QF_CRIT_STAT_ QF_CRIT_E_(); - QEQueueCtr nFree = m_nFree; // temporary to avoid UB for volatile access + QEQueueCtr nFree = m_nFree; // margin available? - if (((margin == QF_NO_MARGIN) && (nFree > 0U)) + bool status; + if (((margin == QF::NO_MARGIN) && (nFree > 0U)) || (nFree > static_cast(margin))) { // is it a dynamic event? if (e->poolId_ != 0U) { - QF_EVT_REF_CTR_INC_(e); // increment the reference counter + QEvt_refCtr_inc_(e); // increment the reference counter } --nFree; // one free entry just used up - m_nFree = nFree; // update the volatile + m_nFree = nFree; // update the original if (m_nMin > nFree) { m_nMin = nFree; // update minimum so far } @@ -163,20 +139,20 @@ bool QEQueue::post(QEvt const * const e, // queue is not empty, leave event in the ring-buffer else { // insert event into the ring buffer (FIFO) - QF_PTR_AT_(m_ring, m_head) = e; // insert e into buffer + m_ring[m_head] = e; // insert e into buffer // need to wrap? if (m_head == 0U) { m_head = m_end; // wrap around } - --m_head; + m_head = (m_head - 1U); } status = true; // event posted successfully } else { - /// @note assert if event cannot be posted and dropping events is - /// not acceptable - Q_ASSERT_CRIT_(210, margin != QF_NO_MARGIN); + //! @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 @@ -191,51 +167,32 @@ bool QEQueue::post(QEvt const * const e, } QF_CRIT_X_(); - static_cast(qs_id); // unused parameter, if Q_SPY not defined + Q_UNUSED_PAR(qs_id); // when Q_SPY not defined return status; } -//**************************************************************************** -/// @description -/// Post an event to the "raw" thread-safe event queue using the -/// Last-In-First-Out (LIFO) order. -/// -/// @param[in] e pointer to the event to be posted to the queue -/// @param[in] qs_id QS-id of this state machine (for QS local filter) -/// -/// @attention -/// The LIFO policy should be used only with great __caution__, -/// because it alters the order of events in the queue. -/// -/// @note -/// This function can be called from any task context or ISR context. -/// -/// @note -/// This function is used for the "raw" thread-safe queues and __not__ -/// for the queues of active objects. -/// -/// @sa -/// QP::QEQueue::post(), QP::QEQueue::get(), QP::QActive::defer() -/// -void QEQueue::postLIFO(QEvt const * const e, - std::uint_fast8_t const qs_id) noexcept +//${QF::QEQueue::postLIFO} ................................................... +void QEQueue::postLIFO( + QEvt const * const e, + std::uint_fast8_t const qs_id) noexcept { - QF_CRIT_STAT_ + Q_UNUSED_PAR(qs_id); // when Q_SPY not defined + QF_CRIT_STAT_ QF_CRIT_E_(); - QEQueueCtr nFree = m_nFree; // temporary to avoid UB for volatile access + QEQueueCtr nFree = m_nFree; - /// @pre the queue must be able to accept the event (cannot overflow) + //! @pre the queue must be able to accept the event (cannot overflow) Q_REQUIRE_CRIT_(300, nFree != 0U); // is it a dynamic event? if (e->poolId_ != 0U) { - QF_EVT_REF_CTR_INC_(e); // increment the reference counter + QEvt_refCtr_inc_(e); // increment the reference counter } --nFree; // one free entry just used up - m_nFree = nFree; // update the volatile + m_nFree = nFree; // update the original if (m_nMin > nFree) { m_nMin = nFree; // update minimum so far } @@ -249,47 +206,27 @@ void QEQueue::postLIFO(QEvt const * const e, QS_EQC_PRE_(m_nMin); // min number of free entries QS_END_NOCRIT_PRE_() - QEvt const * const frontEvt = m_frontEvt; // read volatile into temporary + QEvt const * const frontEvt = m_frontEvt; // read into temporary m_frontEvt = e; // deliver event directly to the front of the queue // was the queue not empty? if (frontEvt != nullptr) { - ++m_tail; + m_tail = (m_tail + 1U); if (m_tail == m_end) { // need to wrap the tail? m_tail = 0U; // wrap around } - QF_PTR_AT_(m_ring, m_tail) = frontEvt; // buffer the old front evt + m_ring[m_tail] = frontEvt; // buffer the old front evt } - - static_cast(qs_id); // unused parameter, if Q_SPY not defined - QF_CRIT_X_(); } -//**************************************************************************** -/// @description -/// Retrieves an event from the front of the "raw" thread-safe queue and -/// returns a pointer to this event to the caller. -/// -/// @param[in] qs_id QS-id of this state machine (for QS local filter) -/// -/// @returns -/// pointer to event at the front of the queue, if the queue is -/// not empty and NULL if the queue is empty. -/// -/// @note -/// this function is used for the "raw" thread-safe queues and __not__ -/// for the queues of active objects. -/// -/// @sa -/// QP::QEQueue::post(), QP::QEQueue::postLIFO(), QP::QActive::recall() -/// -QEvt const *QEQueue::get(std::uint_fast8_t const qs_id) noexcept { - QEvt const *e; - QF_CRIT_STAT_ +//${QF::QEQueue::get} ........................................................ +QEvt const * QEQueue::get(std::uint_fast8_t const qs_id) noexcept { + Q_UNUSED_PAR(qs_id); // when Q_SPY not defined + QF_CRIT_STAT_ QF_CRIT_E_(); - e = m_frontEvt; // always remove the event from the front location + QEvt const * const e = m_frontEvt; // always remove evt from the front // is the queue not empty? if (e != nullptr) { @@ -298,11 +235,11 @@ QEvt const *QEQueue::get(std::uint_fast8_t const qs_id) noexcept { // any events in the the ring buffer? if (nFree <= m_end) { - m_frontEvt = QF_PTR_AT_(m_ring, m_tail); // remove from the tail + m_frontEvt = m_ring[m_tail]; // remove from the tail if (m_tail == 0U) { // need to wrap? m_tail = m_end; // wrap around } - --m_tail; + m_tail = (m_tail - 1U); QS_BEGIN_NOCRIT_PRE_(QS_QF_EQUEUE_GET, qs_id) QS_TIME_PRE_(); // timestamp @@ -328,10 +265,8 @@ QEvt const *QEQueue::get(std::uint_fast8_t const qs_id) noexcept { } QF_CRIT_X_(); - static_cast(qs_id); // unused parameter, if Q_SPY not defined - return e; } } // namespace QP - +//$enddef${QF::QEQueue} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/src/qf_qmact.cpp b/src/qf_qmact.cpp index 54c11aa..3d297e5 100644 --- a/src/qf_qmact.cpp +++ b/src/qf_qmact.cpp @@ -1,100 +1,120 @@ -/// @file -/// @brief QMActive::QMActive() and virtual functions -/// @cond -///*************************************************************************** -/// Last updated for version 6.9.2 -/// Last updated on 2020-12-17 -/// -/// Q u a n t u m L e a P s -/// ------------------------ -/// Modern Embedded Software -/// -/// Copyright (C) 2005-2020 Quantum Leaps. All rights reserved. -/// -/// This program is open source software: you can redistribute it and/or -/// modify it under the terms of the GNU General Public License as published -/// by the Free Software Foundation, either version 3 of the License, or -/// (at your option) any later version. -/// -/// Alternatively, this program may be distributed and modified under the -/// terms of Quantum Leaps commercial licenses, which expressly supersede -/// the GNU General Public License and are specifically designed for -/// licensees interested in retaining the proprietary status of their code. -/// -/// This program is distributed in the hope that it will be useful, -/// but WITHOUT ANY WARRANTY; without even the implied warranty of -/// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -/// GNU General Public License for more details. -/// -/// You should have received a copy of the GNU General Public License -/// along with this program. If not, see . -/// -/// Contact information: -/// -/// -///*************************************************************************** -/// @endcond +//$file${src::qf::qf_qmact.cpp} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv +// +// Model: qpcpp.qm +// File: ${src::qf::qf_qmact.cpp} +// +// This code has been generated by QM 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_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 @p qact_ to QP::QMsm* -/// @note -/// Casting pointer to pointer pointer violates the MISRA-C++ 2008 Rule 5-2-7, -/// cast from pointer to pointer. Additionally this cast violates the MISRA- -/// C++ 2008 Rule 5-2-8 Unusual pointer cast (incompatible indirect types). -/// Encapsulating these violations in a macro allows to selectively suppress -/// this specific deviation. +//! 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 @p qact_ to QP::QMsm const * +//! Internal macro to cast a QP::QMActive pointer `qact_` to QP::QMsm const * #define QF_QMACTIVE_TO_QMSM_CONST_CAST_(qact_) \ reinterpret_cast((qact_)) +// 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 { -//Q_DEFINE_THIS_MODULE("qf_qmact") +//${QF::QMActive} ............................................................ -//............................................................................ -QMActive::QMActive(QStateHandler const initial) noexcept - : QActive(initial) +//${QF::QMActive::init} ...................................................... +void QMActive::init( + void const * const e, + std::uint_fast8_t const qs_id) { - m_temp.fun = initial; -} - -//............................................................................ -void QMActive::init(void const * const e, std::uint_fast8_t const qs_id) { m_state.obj = &QMsm::msm_top_s; QF_QMACTIVE_TO_QMSM_CAST_(this)->QMsm::init(e, qs_id); } -//............................................................................ + +//${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); } -//............................................................................ -void QMActive::dispatch(QEvt const * const e, std::uint_fast8_t const 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); } -//............................................................................ -QMState const *QMActive::childStateObj(QMState const * const parent) - const noexcept -{ - return QF_QMACTIVE_TO_QMSM_CONST_CAST_(this)->QMsm::childStateObj(parent); + +//${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 +QStateHandler QMActive::getStateHandler() noexcept { + return QF_QMACTIVE_TO_QMSM_CAST_(this)->QMsm::getStateHandler(); +} -} // namespace QP +#endif // def Q_SPY +} // namespace QP +//$enddef${QF::QMActive} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/src/qf_time.cpp b/src/qf_time.cpp index 0836fa5..2e1fcc8 100644 --- a/src/qf_time.cpp +++ b/src/qf_time.cpp @@ -1,40 +1,43 @@ -/// @file -/// @brief QF/C++ time events and time management services -/// @ingroup qf -/// @cond -///*************************************************************************** -/// Last updated for version 6.9.1 -/// Last updated on 2020-09-17 -/// -/// Q u a n t u m L e a P s -/// ------------------------ -/// Modern Embedded Software -/// -/// Copyright (C) 2005-2020 Quantum Leaps. All rights reserved. -/// -/// This program is open source software: you can redistribute it and/or -/// modify it under the terms of the GNU General Public License as published -/// by the Free Software Foundation, either version 3 of the License, or -/// (at your option) any later version. -/// -/// Alternatively, this program may be distributed and modified under the -/// terms of Quantum Leaps commercial licenses, which expressly supersede -/// the GNU General Public License and are specifically designed for -/// licensees interested in retaining the proprietary status of their code. -/// -/// This program is distributed in the hope that it will be useful, -/// but WITHOUT ANY WARRANTY; without even the implied warranty of -/// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -/// GNU General Public License for more details. -/// -/// You should have received a copy of the GNU General Public License -/// along with this program. If not, see . -/// -/// Contact information: -/// -/// -///*************************************************************************** -/// @endcond +//$file${src::qf::qf_time.cpp} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv +// +// Model: qpcpp.qm +// File: ${src::qf::qf_time.cpp} +// +// This code has been generated by QM 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_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 @@ -47,207 +50,47 @@ #include "qs_dummy.hpp" // disable the QS software tracing #endif // Q_SPY -namespace QP { - +// unnamed namespace for local definitions with internal linkage +namespace { Q_DEFINE_THIS_MODULE("qf_time") +} // unnamed namespace -// Package-scope objects ***************************************************** -QTimeEvt QF::timeEvtHead_[QF_MAX_TICK_RATE]; // heads of time event lists - -#ifdef Q_SPY -//**************************************************************************** -/// @description -/// This function must be called periodically from a time-tick ISR or from -/// a task so that QF can manage the timeout events assigned to the given -/// system clock tick rate. -/// -/// @param[in] tickRate system clock tick rate serviced in this call [1..15]. -/// @param[in] sender pointer to a sender object (used in QS only). -/// -/// @note -/// this function should be called only via the macro TICK_X() -/// -/// @note -/// the calls to QP::QF::tickX_() with different @p tickRate parameter can -/// preempt each other. For example, higher clock tick rates might be -/// serviced from interrupts while others from tasks (active objects). -/// -/// @sa -/// QP::QTimeEvt. -/// -void QF::tickX_(std::uint_fast8_t const tickRate, - void const * const sender) noexcept -#else -void QF::tickX_(std::uint_fast8_t const tickRate) noexcept +//$skip${QP_VERSION} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv +// Check for the minimum required QP version +#if (QP_VERSION < 700U) || (QP_VERSION != ((QP_RELEASE^4294967295U) % 0x3E8U)) +#error qpcpp version 7.0.0 or higher required #endif -{ - QTimeEvt *prev = &timeEvtHead_[tickRate]; - QF_CRIT_STAT_ - - QF_CRIT_E_(); - - QS_BEGIN_NOCRIT_PRE_(QS_QF_TICK, 0U) - ++prev->m_ctr; - QS_TEC_PRE_(prev->m_ctr); // tick ctr - QS_U8_PRE_(tickRate); // tick rate - QS_END_NOCRIT_PRE_() - - // scan the linked-list of time events at this rate... - for (;;) { - QTimeEvt *t = prev->m_next; // advance down the time evt. list - - // end of the list? - if (t == nullptr) { - - // any new time events armed since the last run of QF::tickX_()? - if (timeEvtHead_[tickRate].m_act != nullptr) { - - // sanity check - Q_ASSERT_CRIT_(110, prev != nullptr); - prev->m_next = QF::timeEvtHead_[tickRate].toTimeEvt(); - timeEvtHead_[tickRate].m_act = nullptr; - t = prev->m_next; // switch to the new list - } - else { - break; // all currently armed time evts. processed - } - } - - // time event scheduled for removal? - if (t->m_ctr == 0U) { - prev->m_next = t->m_next; - // mark time event 't' as NOT linked - t->refCtr_ &= static_cast(~TE_IS_LINKED); - // do NOT advance the prev pointer - QF_CRIT_X_(); // exit crit. section to reduce latency - - // prevent merging critical sections, see NOTE1 below - QF_CRIT_EXIT_NOP(); - } - else { - --t->m_ctr; - - // is time evt about to expire? - if (t->m_ctr == 0U) { - QActive * const act = t->toActive(); // temporary for volatile - - // periodic time evt? - if (t->m_interval != 0U) { - t->m_ctr = t->m_interval; // rearm the time event - prev = t; // advance to this time event - } - // one-shot time event: automatically disarm - else { - prev->m_next = t->m_next; - - // mark time event 't' as NOT linked - t->refCtr_ &= static_cast(~TE_IS_LINKED); - // do NOT advance the prev pointer - - QS_BEGIN_NOCRIT_PRE_(QS_QF_TIMEEVT_AUTO_DISARM, - act->m_prio) - QS_OBJ_PRE_(t); // this time event object - QS_OBJ_PRE_(act); // the target AO - QS_U8_PRE_(tickRate); // tick rate - QS_END_NOCRIT_PRE_() - } - - 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 +//$endskip${QP_VERSION} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - // asserts if queue overflows - static_cast(act->POST(t, sender)); - } - else { - prev = t; // advance to this time event - QF_CRIT_X_(); // exit crit. section to reduce latency +//$define${QF::QTimeEvt} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv +namespace QP { - // prevent merging critical sections, see NOTE1 below - QF_CRIT_EXIT_NOP(); - } - } - QF_CRIT_E_(); // re-enter crit. section to continue - } - QF_CRIT_X_(); -} +//${QF::QTimeEvt} ............................................................ +QTimeEvt QTimeEvt::timeEvtHead_[QF_MAX_TICK_RATE]; -//**************************************************************************** -// NOTE1: -// In some QF ports the critical section exit takes effect only on the next -// machine instruction. If this case, the next instruction is another entry -// to a critical section, the critical section won't be really exited, but -// rather the two adjacent critical sections would be merged. -// -// The QF_CRIT_EXIT_NOP() macro contains minimal code required -// to prevent such merging of critical sections in QF ports, -// in which it can occur. - - -//**************************************************************************** -/// @description -/// Find out if any time events are armed at the given clock tick rate. -/// -/// @param[in] tickRate system clock tick rate to find out about. -/// -/// @returns -/// 'true' if no time events are armed at the given tick rate and -/// 'false' otherwise. -/// -/// @note -/// This function should be called in critical section. -/// -bool QF::noTimeEvtsActiveX(std::uint_fast8_t const tickRate) noexcept { - bool inactive; - if (timeEvtHead_[tickRate].m_next != nullptr) { - inactive = false; - } - else if (timeEvtHead_[tickRate].m_act != nullptr) { - inactive = false; - } - else { - inactive = true; - } - return inactive; -} - -//**************************************************************************** -/// @description -/// When creating a time event, you must commit it to a specific active object -/// @p act, tick rate @p tickRate and event signal @p sgnl. You cannot change -/// these attributes later. -/// -/// @param[in] act pointer to the active object associated with this -/// time event. The time event will post itself to this AO. -/// @param[in] sgnl signal to associate with this time event. -/// @param[in] tickRate system tick rate to associate with this time event. -/// -QTimeEvt::QTimeEvt(QActive * const act, - enum_t const sgnl, std::uint_fast8_t const tickRate) noexcept - : -#ifdef Q_EVT_CTOR - QEvt(static_cast(sgnl), QEvt::STATIC_EVT), -#else +//${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 m_next(nullptr), m_act(act), m_ctr(0U), m_interval(0U) { - /// @pre The signal must be valid and the tick rate in range - Q_REQUIRE_ID(300, (sgnl >= Q_USER_SIG) + //! @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 + #ifndef Q_EVT_CTOR sig = static_cast(sgnl); // set QEvt::sig of this time event -#endif + #endif // Setting the POOL_ID event attribute to zero is correct only for // events not allocated from event pools, which must be the case @@ -261,85 +104,26 @@ QTimeEvt::QTimeEvt(QActive * const act, refCtr_ = static_cast(tickRate); } -//**************************************************************************** -/// @note -/// private default ctor for internal use only -/// -QTimeEvt::QTimeEvt() noexcept - : -#ifdef Q_EVT_CTOR - QEvt(0U, QEvt::STATIC_EVT), -#else - QEvt(), -#endif // Q_EVT_CTOR - m_next(nullptr), - m_act(nullptr), - m_ctr(0U), - m_interval(0U) -{ -#ifndef Q_EVT_CTOR - sig = 0U; - - // Setting the POOL_ID event attribute to zero is correct only for - // events not allocated from event pools, which must be the case - // for Time Events. - // - poolId_ = 0U; // not from any event pool - - // The refCtr_ attribute is not used in time events, so it is - // reused to hold the tickRate as well as other information - // - refCtr_ = 0U; // default rate 0 - -#endif // Q_EVT_CTOR - -} - -//**************************************************************************** -/// @description -/// Arms a time event to fire in a specified number of clock ticks and with -/// a specified interval. If the interval is zero, the time event is armed -/// for one shot ('one-shot' time event). The time event gets directly posted -/// (using the FIFO policy) into the event queue of the host active object. -/// -/// @param[in] nTicks number of clock ticks (at the associated rate) -/// to rearm the time event with. -/// @param[in] interval interval (in clock ticks) for periodic time event. -/// -/// @note -/// After posting, a one-shot time event gets automatically disarmed -/// while a periodic time event (interval != 0) is automatically re-armed. -/// -/// @note -/// A time event can be disarmed at any time by calling -/// QP::QTimeEvt::disarm(). Also, a time event can be re-armed to fire in a -/// different number of clock ticks by calling the QP::QTimeEvt::rearm() -/// function. -/// -/// @usage -/// The following example shows how to arm a one-shot time event from a state -/// machine of an active object: -/// @include qf_state.cpp -/// -void QTimeEvt::armX(QTimeEvtCtr const nTicks, - QTimeEvtCtr const interval) noexcept +//${QF::QTimeEvt::armX} ...................................................... +void QTimeEvt::armX( + QTimeEvtCtr const nTicks, + QTimeEvtCtr const interval) noexcept { std::uint8_t const tickRate = refCtr_ & TE_TICK_RATE; - QTimeEvtCtr const ctr = m_ctr; // temporary to hold volatile - QF_CRIT_STAT_ + QTimeEvtCtr const ctr = m_ctr; - /// @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. - /// + //! @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 + #ifdef Q_NASSERT (void)ctr; // avoid compiler warning about unused variable -#endif + #endif + QF_CRIT_STAT_ QF_CRIT_E_(); m_ctr = nTicks; m_interval = interval; @@ -352,23 +136,22 @@ void QTimeEvt::armX(QTimeEvtCtr const nTicks, if (static_cast( static_cast(refCtr_) & TE_IS_LINKED) == 0U) { - refCtr_ |= TE_IS_LINKED; // mark as linked + // mark as linked + refCtr_ = static_cast(refCtr_ | TE_IS_LINKED); // The time event is initially inserted into the separate - // "freshly armed" link list based on QF::timeEvtHead_[tickRate].act. - // Only later, inside the QF::tickX() function, the "freshly armed" + // "freshly armed" list based on timeEvtHead_[tickRate].act. + // Only later, inside QTimeEvt::tick_(), the "freshly armed" // list is appended to the main list of armed time events based on - // QF::timeEvtHead_[tickRate].next. Again, this is to keep any - // changes to the main list exclusively inside the QF::tickX() - // function. - // - m_next = QF::timeEvtHead_[tickRate].toTimeEvt(); - QF::timeEvtHead_[tickRate].m_act = this; + // timeEvtHead_[tickRate].next. Again, this is to keep any + // changes to the main list exclusively inside QTimeEvt::tick_(). + m_next = timeEvtHead_[tickRate].toTimeEvt(); + timeEvtHead_[tickRate].m_act = this; } -#ifdef Q_SPY + #ifdef Q_SPY std::uint_fast8_t const qs_id = static_cast(m_act)->m_prio; -#endif + #endif QS_BEGIN_NOCRIT_PRE_(QS_QF_TIMEEVT_ARM, qs_id) QS_TIME_PRE_(); // timestamp QS_OBJ_PRE_(this); // this time event object @@ -381,34 +164,19 @@ void QTimeEvt::armX(QTimeEvtCtr const nTicks, QF_CRIT_X_(); } -//**************************************************************************** -/// @description -/// Disarm the time event so it can be safely reused. -/// -/// @returns -/// 'true' if the time event was truly disarmed, that is, it was running. -/// The return of 'false' means that the time event was not truly disarmed, -/// because it was not running. The 'false' return is only possible for one- -///shot time events that have been automatically disarmed upon expiration. -/// In this case the 'false' return means that the time event has already -/// been posted or published and should be expected in the active object's -/// state machine. -/// -/// @note -/// there is no harm in disarming an already disarmed time event -/// -bool QTimeEvt::disarm(void) noexcept { +//${QF::QTimeEvt::disarm} .................................................... +bool QTimeEvt::disarm() noexcept { QF_CRIT_STAT_ QF_CRIT_E_(); - bool wasArmed; -#ifdef Q_SPY + #ifdef Q_SPY std::uint_fast8_t const qs_id = static_cast(m_act)->m_prio; -#endif + #endif // is the time event actually armed? + bool wasArmed; if (m_ctr != 0U) { wasArmed = true; - refCtr_ |= TE_WAS_DISARMED; + refCtr_ = static_cast(refCtr_ | TE_WAS_DISARMED); QS_BEGIN_NOCRIT_PRE_(QS_QF_TIMEEVT_DISARM, qs_id) QS_TIME_PRE_(); // timestamp @@ -423,7 +191,8 @@ bool QTimeEvt::disarm(void) noexcept { } else { // the time event was already disarmed automatically wasArmed = false; - refCtr_ &= static_cast(~TE_WAS_DISARMED); + refCtr_ = static_cast(refCtr_ + & static_cast(~TE_WAS_DISARMED)); QS_BEGIN_NOCRIT_PRE_(QS_QF_TIMEEVT_DISARM_ATTEMPT, qs_id) QS_TIME_PRE_(); // timestamp @@ -434,45 +203,26 @@ bool QTimeEvt::disarm(void) noexcept { } QF_CRIT_X_(); + return wasArmed; } -//**************************************************************************** -/// -/// @description -/// Rearms a time event with a new number of clock ticks. This function can -/// be used to adjust the current period of a periodic time event or to -/// prevent a one-shot time event from expiring (e.g., a watchdog time event). -/// Rearming a periodic timer leaves the interval unchanged and is a -/// convenient method to adjust the phasing of a periodic time event. -/// -/// @param[in] nTicks number of clock ticks (at the associated rate) -/// to rearm the time event with. -/// -/// @returns -/// 'true' if the time event was running as it was re-armed. The 'false' -/// return means that the time event was not truly rearmed because it was -/// not running. The 'false' return is only possible for one-shot time events -/// that have been automatically disarmed upon expiration. In this case the -/// 'false' return means that the time event has already been posted or -/// published and should be expected in the active object's state machine. -/// +//${QF::QTimeEvt::rearm} ..................................................... bool QTimeEvt::rearm(QTimeEvtCtr const nTicks) noexcept { std::uint8_t const tickRate = refCtr_ & TE_TICK_RATE; - QF_CRIT_STAT_ - /// @pre AO must be valid, tick rate must be in range, nTicks must not - /// be zero, and the signal of this time event must be valid - /// + //! @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)); + QF_CRIT_STAT_ QF_CRIT_E_(); - bool wasArmed; // is the time evt not running? + bool wasArmed; if (m_ctr == 0U) { wasArmed = false; @@ -480,21 +230,19 @@ bool QTimeEvt::rearm(QTimeEvtCtr const nTicks) noexcept { // NOTE: For a duration of a single clock tick of the specified // tick rate a time event can be disarmed and yet still linked into // the list, because unlinking is performed exclusively in the - // QF::tickX() function. - // - if ((refCtr_ & TE_IS_LINKED) == 0U) { - refCtr_ |= TE_IS_LINKED; // mark as linked + // QTimeEvt::tickX() function. + if (static_cast(refCtr_ & TE_IS_LINKED) == 0U) { + // mark as linked + refCtr_ = static_cast(refCtr_ | TE_IS_LINKED); // The time event is initially inserted into the separate - // "freshly armed" list based on QF_timeEvtHead_[tickRate].act. - // Only later, inside the QF_tickX() function, the "freshly armed" + // "freshly armed" list based on timeEvtHead_[tickRate].act. + // Only later, inside QTimeEvt::tick_(), the "freshly armed" // list is appended to the main list of armed time events based on - // QF_timeEvtHead_[tickRate].next. Again, this is to keep any - // changes to the main list exclusively inside the QF::tickX() - // function. - // - m_next = QF::timeEvtHead_[tickRate].toTimeEvt(); - QF::timeEvtHead_[tickRate].m_act = this; + // timeEvtHead_[tickRate].next. Again, this is to keep any + // changes to the main list exclusively inside QTimeEvt::tick_(). + m_next = timeEvtHead_[tickRate].toTimeEvt(); + timeEvtHead_[tickRate].m_act = this; } } else { // the time event is being disarmed @@ -502,9 +250,9 @@ bool QTimeEvt::rearm(QTimeEvtCtr const nTicks) noexcept { } m_ctr = nTicks; // re-load the tick counter (shift the phasing) -#ifdef Q_SPY + #ifdef Q_SPY std::uint_fast8_t const qs_id = static_cast(m_act)->m_prio; -#endif + #endif QS_BEGIN_NOCRIT_PRE_(QS_QF_TIMEEVT_REARM, qs_id) QS_TIME_PRE_(); // timestamp QS_OBJ_PRE_(this); // this time event object @@ -515,57 +263,178 @@ bool QTimeEvt::rearm(QTimeEvtCtr const nTicks) noexcept { QS_END_NOCRIT_PRE_() QF_CRIT_X_(); + return wasArmed; } -//**************************************************************************** -/// -/// @description -/// Useful for checking whether a one-shot time event was disarmed in the -/// QTimeEvt_disarm() operation. -/// -/// @returns -/// 'true' if the time event was truly disarmed in the last QTimeEvt::disarm() -/// operation. The 'false' return means that the time event was not truly -/// disarmed, because it was not running at that time. The 'false' return is -/// only possible for one-shot time events that have been automatically -/// disarmed upon expiration. In this case the 'false' return means that the -/// time event has already been posted or published and should be expected -/// in the active object's event queue. -/// -/// @note -/// This function has a **side effect** of setting the "was disarmed" status, -/// which means that the second and subsequent times this function is called -/// the function will return 'true'. -/// -bool QTimeEvt::wasDisarmed(void) noexcept { +//${QF::QTimeEvt::wasDisarmed} ............................................... +bool QTimeEvt::wasDisarmed() noexcept { std::uint8_t const isDisarmed = refCtr_ & TE_WAS_DISARMED; - refCtr_ |= TE_WAS_DISARMED; // set the flag + // mark as disarmed + refCtr_ = static_cast(refCtr_ | TE_WAS_DISARMED); return isDisarmed != 0U; } -//**************************************************************************** -/// @description -/// Useful for checking how many clock ticks (at the tick rate associated -/// with the time event) remain until the time event expires. -/// -/// @returns -/// For an armed time event, the function returns the current value of the -/// down-counter of the given time event. If the time event is not armed, -/// the function returns 0. -/// -/// @note -/// The function is thread-safe. -/// -QTimeEvtCtr QTimeEvt::currCtr(void) const noexcept { - QF_CRIT_STAT_ +//${QF::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 + + QTimeEvt *prev = &timeEvtHead_[tickRate]; + QF_CRIT_STAT_ QF_CRIT_E_(); - QTimeEvtCtr const ret = m_ctr; + + QS_BEGIN_NOCRIT_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_() + + // 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 tick_()? + if (timeEvtHead_[tickRate].m_act != nullptr) { + + // 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 + } + } + + // 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 + + // prevent merging critical sections, see NOTE1 below + QF_CRIT_EXIT_NOP(); + } + 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 { + 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_E_(); // re-enter crit. section to continue + } QF_CRIT_X_(); +} + +//${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); - return ret; + bool inactive; + if (timeEvtHead_[tickRate].m_next != nullptr) { + inactive = false; + } + else if (timeEvtHead_[tickRate].m_act != nullptr) { + inactive = false; + } + else { + inactive = true; + } + return inactive; } -} // namespace QP +//${QF::QTimeEvt::QTimeEvt} .................................................. +QTimeEvt::QTimeEvt() + : +#ifdef Q_EVT_CTOR + QEvt(0U, 0U), +#else + QEvt(), +#endif // Q_EVT_CTOR + m_next(nullptr), + m_act(nullptr), + m_ctr(0U), + m_interval(0U) +{ + #ifndef Q_EVT_CTOR + sig = 0U; + // Setting the POOL_ID event attribute to zero is correct only for + // events not allocated from event pools, which must be the case + // for Time Events. + // + poolId_ = 0U; // not from any event pool + + // The refCtr_ attribute is not used in time events, so it is + // reused to hold the tickRate as well as other information + // + refCtr_ = 0U; // default rate 0 + #endif // Q_EVT_CTOR +} + +} // namespace QP +//$enddef${QF::QTimeEvt} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/src/qmpool.hpp b/src/qmpool.hpp index f0088af..91faa01 100644 --- a/src/qmpool.hpp +++ b/src/qmpool.hpp @@ -1,47 +1,50 @@ -/// @file -/// @brief platform-independent memory pool QP::QMPool interface. -/// @ingroup qf -/// @cond -///*************************************************************************** -/// Last updated for version 6.9.1 -/// Last updated on 2020-09-14 -/// -/// Q u a n t u m L e a P s -/// ------------------------ -/// Modern Embedded Software -/// -/// Copyright (C) 2005-2020 Quantum Leaps. All rights reserved. -/// -/// This program is open source software: you can redistribute it and/or -/// modify it under the terms of the GNU General Public License as published -/// by the Free Software Foundation, either version 3 of the License, or -/// (at your option) any later version. -/// -/// Alternatively, this program may be distributed and modified under the -/// terms of Quantum Leaps commercial licenses, which expressly supersede -/// the GNU General Public License and are specifically designed for -/// licensees interested in retaining the proprietary status of their code. -/// -/// This program is distributed in the hope that it will be useful, -/// but WITHOUT ANY WARRANTY; without even the implied warranty of -/// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -/// GNU General Public License for more details. -/// -/// You should have received a copy of the GNU General Public License -/// along with this program. If not, see . -/// -/// Contact information: -/// -/// -///*************************************************************************** -/// @endcond - -#ifndef QMPOOL_HPP -#define QMPOOL_HPP +//$file${include::qmpool.hpp} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv +// +// Model: qpcpp.qm +// File: ${include::qmpool.hpp} +// +// This code has been generated by QM 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::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 + //! Valid values 1U, 2U, or 4U; default 2U #define QF_MPOOL_SIZ_SIZE 2U #endif @@ -51,15 +54,22 @@ #define QF_MPOOL_CTR_SIZE 2 #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]; } + +//============================================================================ namespace QP { + #if (QF_MPOOL_SIZ_SIZE == 1U) using QMPoolSize = std::uint8_t; #elif (QF_MPOOL_SIZ_SIZE == 2U) //! The data type to store the block-size based on the macro - //! #QF_MPOOL_SIZ_SIZE. - /// @description - /// The dynamic range of this data type determines the maximum size - /// of blocks that can be managed by the native QF event pool. + //! #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; @@ -71,10 +81,11 @@ namespace QP { using QMPoolCtr = std::uint8_t; #elif (QF_MPOOL_CTR_SIZE == 2U) //! The data type to store the block-counter based on the macro - //! #QF_MPOOL_CTR_SIZE. - /// @description - /// The dynamic range of this data type determines the maximum number - /// of blocks that can be stored in the pool. + //! #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; @@ -82,35 +93,42 @@ namespace QP { #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 -/// @description -/// A fixed block-size memory pool is a very fast and efficient data -/// structure for dynamic allocation of fixed block-size chunks of memory. -/// A memory pool offers fast and deterministic allocation and recycling of -/// memory blocks and is not subject to fragmenation.@n -/// @n -/// The QP::QMPool class describes the native QF memory pool, which can be -/// used as the event pool for dynamic event allocation, or as a fast, -/// deterministic fixed block-size heap for any other objects in your -/// application. -/// -/// @note -/// The QP::QMPool class contains only data members for managing a memory -/// pool, but does not contain the pool storage, which must be provided -/// externally during the pool initialization. -/// -/// @note -/// The native QF event pool is configured by defining the macro -/// #QF_EPOOL_TYPE_ as QP::QMPool in the specific QF port header file. +//! +//! @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; + void * m_start; //! end of the memory managed by this memory pool - void *m_end; + void * m_end; //! head of linked list of free blocks void * volatile m_free_head; @@ -125,56 +143,165 @@ class QMPool { QMPoolCtr volatile m_nFree; //! minimum number of free blocks ever present in this pool - /// @note - /// This attribute remembers the low watermark of the pool, - /// which provides a valuable information for sizing event pools. - /// - /// @sa QP::QF::getPoolMin(). + //! + //! @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: - QMPool(void); //!< public default constructor + + //! public default constructor + QMPool(); //! Initializes the native QF event pool - void init(void * const poolSto, std::uint_fast32_t poolSize, - std::uint_fast16_t blockSize) noexcept; + //! + //! @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. + void init( + void * const poolSto, + std::uint_fast32_t poolSize, + std::uint_fast16_t blockSize) noexcept; - //! Obtains a memory block from a memory pool. - void *get(std::uint_fast16_t const margin, - std::uint_fast8_t const qs_id) noexcept; + //! 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() + void * get( + std::uint_fast16_t const margin, + std::uint_fast8_t const qs_id) noexcept; - //! Returns a memory block back to a memory pool. - void put(void * const b, std::uint_fast8_t const qs_id) noexcept; + //! 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() + void put( + void * const b, + std::uint_fast8_t const qs_id) noexcept; //! return the fixed block-size of the blocks managed by this pool - QMPoolSize getBlockSize(void) const noexcept { - return m_blockSize; + 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. + QMPoolCtr getNMin() const noexcept { + return m_nMin; } -// duplicated API to be used exclusively inside ISRs (useful in some QP ports) -#ifdef QF_ISR_API - void *getFromISR(std::uint_fast16_t const margin, - std::uint_fast8_t const qs_id) noexcept; - void putFromISR(void * const b, - std::uint_fast8_t const qs_id) noexcept; -#endif // QF_ISR_API + //! 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 QMPools - QMPool(QMPool const &) = delete; - //!< disallow assigning of QMPools - QMPool &operator=(QMPool const &) = delete; + //! disallow copying of QP::QMPool + QMPool(QEQueue const & other) = delete; - friend class QF; - friend class QS; -}; + //! disallow copying of QP::QMPool + QMPool & operator=(QMPool const & other) = delete; -} // namespace QP +public: -//! Memory pool element to allocate correctly aligned storage for QP::QMPool -#define QF_MPOOL_EL(type_) \ - struct { void *sto_[((sizeof(type_) - 1U)/sizeof(void*)) + 1U]; } +#ifdef QF_ISR_API + //! 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; +#endif // def QF_ISR_API -#endif // QMPOOL_HPP +#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; +#endif // def QF_ISR_API +}; // class QMPool + +} // namespace QP +//$enddecl${QF::QMPool} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +#endif // QMPOOL_HPP_ diff --git a/src/qpcpp.hpp b/src/qpcpp.hpp index 47671e6..6027fd1 100644 --- a/src/qpcpp.hpp +++ b/src/qpcpp.hpp @@ -1,49 +1,52 @@ -/// @file -/// @brief QP/C++ public interface including backwards-compatibility layer -/// @ingroup qep qf qv qk qxk qs -/// @cond -///*************************************************************************** -/// Last updated for version 6.9.1 -/// Last updated on 2020-09-30 -/// -/// Q u a n t u m L e a P s -/// ------------------------ -/// Modern Embedded Software -/// -/// Copyright (C) 2005-2020 Quantum Leaps. All rights reserved. -/// -/// This program is open source software: you can redistribute it and/or -/// modify it under the terms of the GNU General Public License as published -/// by the Free Software Foundation, either version 3 of the License, or -/// (at your option) any later version. -/// -/// Alternatively, this program may be distributed and modified under the -/// terms of Quantum Leaps commercial licenses, which expressly supersede -/// the GNU General Public License and are specifically designed for -/// licensees interested in retaining the proprietary status of their code. -/// -/// This program is distributed in the hope that it will be useful, -/// but WITHOUT ANY WARRANTY; without even the implied warranty of -/// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -/// GNU General Public License for more details. -/// -/// You should have received a copy of the GNU General Public License -/// along with this program. If not, see . -/// -/// Contact information: -/// -/// -///*************************************************************************** -/// @endcond - -#ifndef qpcpp_h -#define qpcpp_h - -/// @description -/// This header file must be included directly or indirectly -/// in all application modules (*.cpp files) that use QP/C++. - -//**************************************************************************** +//$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. +// +// 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::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 #ifdef Q_SPY // software tracing enabled? @@ -52,28 +55,44 @@ #include "qs_dummy.hpp" // QS/C++ dummy (inactive) interface #endif -//**************************************************************************** +//============================================================================ #ifndef QP_API_VERSION //! Macro that specifies the backwards compatibility with the //! QP/C++ API version. -/// @description -/// For example, QP_API_VERSION=540 will cause generating the compatibility -/// layer with QP/C++ version 5.4.0 and newer, but not older than 5.4.0. -/// QP_API_VERSION=0 causes generation of the compatibility layer "from the -/// begining of time", which is the maximum backwards compatibilty. This is -/// the default.@n -/// @n -/// Conversely, QP_API_VERSION=9999 means that no compatibility layer should -/// be generated. This setting is useful for checking if an application -/// complies with the latest QP/C++ API. +//! +//! @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_) + +//============================================================================ #if (QP_API_VERSION < 691) //! @deprecated enable the QS global filter @@ -99,7 +118,7 @@ #ifdef Q_SPY -//! @deprecated local Filter for a generic application object @p obj_. +//! @deprecated local Filter for a generic application object `obj_`. #define QS_FILTER_AP_OBJ(obj_) \ (QP::QS::priv_.locFilter_AP = (obj_)) @@ -115,99 +134,96 @@ QS_TIME_PRE_(); //! @deprecated output hex-formatted std::uint32_t to the QS record -#define QS_U32_HEX(width_, data_) \ - (QP::QS::u32_fmt_(static_cast( \ - (static_cast((width_) << 4)) \ - | static_cast(0xFU)), (data_))) - - +#define QS_U32_HEX(width_, data_) \ + (QP::QS::u32_fmt_(static_cast( \ + (static_cast((width_) << 4)) | QS_HEX_FMT), (data_))) #else #define QS_FILTER_AP_OBJ(obj_) (static_cast(0)) #define QS_BEGIN(rec_, obj_) if (false) { -#define QS_U32_HEX(width_, data_) (static_cast(0)) +#define QS_U32_HEX(width_, data_) (Q_UNUSED_PAR(0)) -#endif +#endif // def Q_SPY -//**************************************************************************** +//============================================================================ #if (QP_API_VERSION < 680) //! @deprecated //! Macro to specify a transition in the "me->" impl-strategy. -/// Instead use the new impl-strategy without the "me->" pointer, where -/// you call tran(Q_STATE_CAST(target_)). +//! Instead use the new impl-strategy without the "me->" pointer, where +//! you call tran(Q_STATE_CAST(target_)). #define Q_TRAN(target_) (me->tran(Q_STATE_CAST(target_))) //! @deprecated //! Macro to specify a tran-to-history in the "me->" impl-strategy. -/// Instead use the new impl-strategy without the "me->" pointer, where -/// you call tran_hist(Q_STATE_CAST(hist_)). +//! Instead use the new impl-strategy without the "me->" pointer, where +//! you call tran_hist(Q_STATE_CAST(hist_)). #define Q_TRAN_HIST(hist_) (me->tran_hist((hist_))) //! @deprecated //! Macro to specify the superstate in the "me->" impl-strategy. -/// Instead use the new impl-strategy without the "me->" pointer, where -/// you call super(state_)). -#define Q_SUPER(state_) (me->super((state_))) +//! Instead use the new impl-strategy without the "me->" pointer, where +//! you call super(state_)). +#define Q_SUPER(state_) (me->super(Q_STATE_CAST(state_))) //! @deprecated //! Macro to call in a QM state entry-handler. Applicable only to QMSMs. -/// Instead use the new impl-strategy without the "me->" pointer, where -/// the QM-generated code calls qm_entry(Q_STATE_CAST(state_)). +//! Instead use the new impl-strategy without the "me->" pointer, where +//! the QM-generated code calls qm_entry(Q_STATE_CAST(state_)). #define QM_ENTRY(state_) (me->qm_entry((state_))) //! @deprecated //! Macro to call in a QM state exit-handler. Applicable only to QMSMs. -/// Instead use the new impl-strategy without the "me->" pointer, where -/// the QM-generated code calls qm_exit(Q_STATE_CAST(state_)). +//! Instead use the new impl-strategy without the "me->" pointer, where +//! the QM-generated code calls qm_exit(Q_STATE_CAST(state_)). #define QM_EXIT(state_) (me->qm_exit((state_))) //! @deprecated //! Macro to call in a QM submachine exit-handler. Applicable only to QMSMs. -/// Instead use the new impl-strategy without the "me->" pointer, where -/// the QM-generated code calls qm_sm_exit(Q_STATE_CAST(state_)). +//! Instead use the new impl-strategy without the "me->" pointer, where +//! the QM-generated code calls qm_sm_exit(Q_STATE_CAST(state_)). #define QM_SM_EXIT(state_) (me->qm_sm_exit((state_))) //! @deprecated //! Macro to call in a QM state-handler when it executes a transition. -/// Instead use the new impl-strategy without the "me->" pointer, where -/// the QM-generated code calls qm_tran((tatbl_)). +//! Instead use the new impl-strategy without the "me->" pointer, where +//! the QM-generated code calls qm_tran((tatbl_)). #define QM_TRAN(tatbl_) (me->qm_tran((tatbl_))) //! @deprecated //! Macro to call in a QM state-handler when it executes an initial tran. -/// Instead use the new impl-strategy without the "me->" pointer, where -/// the QM-generated code calls qm_tran_init((tatbl_)). +//! Instead use the new impl-strategy without the "me->" pointer, where +//! the QM-generated code calls qm_tran_init((tatbl_)). #define QM_TRAN_INIT(tatbl_) (me->qm_tran_init((tatbl_))) //! @deprecated //! Macro to call in a QM state-handler when it executes a tran-to-history. -/// Instead use the new impl-strategy without the "me->" pointer, where -/// the QM-generated code calls qm_tran_hist((history_), (tatbl_)). +//! Instead use the new impl-strategy without the "me->" pointer, where +//! the QM-generated code calls qm_tran_hist((history_), (tatbl_)). #define QM_TRAN_HIST(history_, tatbl_) \ (me->qm_tran_hist((history_), (tatbl_))) //! @deprecated //! Macro to call in a QM state-handler when it executes an initial tran. -/// Instead use the new impl-strategy without the "me->" pointer, where -/// the QM-generated code calls qm_tran_ep((tatbl_)). +//! Instead use the new impl-strategy without the "me->" pointer, where +//! the QM-generated code calls qm_tran_ep((tatbl_)). #define QM_TRAN_EP(tatbl_) (me->qm_tran_ep((tatbl_))) //! @deprecated //! Macro to call in a QM state-handler when it executes a tran-to-exit-point. -/// Instead use the new impl-strategy without the "me->" pointer, where -/// the QM-generated code calls qm_tran_xp((xp_), (tatbl_)). +//! Instead use the new impl-strategy without the "me->" pointer, where +//! the QM-generated code calls qm_tran_xp((xp_), (tatbl_)). #define QM_TRAN_XP(xp_, tatbl_) (me->qm_tran_xp((xp_), (tatbl_))) //! @deprecated //! Designates the superstate of a given state in a subclass of QP::QMsm. -/// Instead use the new impl-strategy without the "me->" pointer, where -/// the QM-generated code calls qm_super_sub((state_)). +//! Instead use the new impl-strategy without the "me->" pointer, where +//! the QM-generated code calls qm_super_sub((state_)). #define QM_SUPER_SUB(state_) (me->qm_super_sub((state_))) #endif // QP_API_VERSION < 680 #endif // QP_API_VERSION < 691 +#endif // QP_API_VERSION < 700 -#endif // qpcpp_h - +#endif // QPCPP_HPP_ \ No newline at end of file diff --git a/src/qs.cpp b/src/qs.cpp index 4dd54ef..d980f2f 100644 --- a/src/qs.cpp +++ b/src/qs.cpp @@ -1,40 +1,43 @@ -/// @file -/// @brief QS software tracing services -/// @ingroup qs -/// @cond -///*************************************************************************** -/// Last updated for version 6.9.1 -/// Last updated on 2020-09-19 -/// -/// Q u a n t u m L e a P s -/// ------------------------ -/// Modern Embedded Software -/// -/// Copyright (C) 2005-2020 Quantum Leaps. All rights reserved. -/// -/// This program is open source software: you can redistribute it and/or -/// modify it under the terms of the GNU General Public License as published -/// by the Free Software Foundation, either version 3 of the License, or -/// (at your option) any later version. -/// -/// Alternatively, this program may be distributed and modified under the -/// terms of Quantum Leaps commercial licenses, which expressly supersede -/// the GNU General Public License and are specifically designed for -/// licensees interested in retaining the proprietary status of their code. -/// -/// This program is distributed in the hope that it will be useful, -/// but WITHOUT ANY WARRANTY; without even the implied warranty of -/// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -/// GNU General Public License for more details. -/// -/// You should have received a copy of the GNU General Public License -/// along with this program. If not, see . -/// -/// Contact information: -/// -/// -///*************************************************************************** -/// @endcond +//$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 @@ -42,34 +45,29 @@ #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 { -Q_DEFINE_THIS_MODULE("qs") +//${QS::QS-tx::priv_} ........................................................ +QStx priv_; -//**************************************************************************** -QS QS::priv_; // QS private data - -//**************************************************************************** -/// @description -/// This function should be called from QP::QS::onStartup() to provide QS with -/// the data buffer. The first argument @a sto[] is the address of the memory -/// block, and the second argument @a stoSize is the size of this block -/// in bytes. Currently the size of the QS buffer cannot exceed 64KB. -/// -/// @note QS can work with quite small data buffers, but you will start losing -/// data if the buffer is too small for the bursts of tracing activity. -/// The right size of the buffer depends on the data production rate and -/// the data output rate. QS offers flexible filtering to reduce the data -/// production rate. -/// -/// @note If the data output rate cannot keep up with the production rate, -/// QS will start overwriting the older data with newer data. This is -/// consistent with the "last-is-best" QS policy. The record sequence counters -/// and check sums on each record allow the QSPY host uitiliy to easily detect -/// any data loss. -/// -void QS::initBuf(std::uint8_t * const sto, - std::uint_fast16_t const stoSize) noexcept +//${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); @@ -78,8 +76,8 @@ void QS::initBuf(std::uint8_t * const sto, // tracing can start correctly even if the startup code fails to clear // any uninitialized data (as is required by the C Standard). // - glbFilter_(-QS_ALL_RECORDS); // all global filters OFF - locFilter_(QS_ALL_IDS); // all local filters ON + 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; @@ -96,29 +94,70 @@ void QS::initBuf(std::uint8_t * const sto, endRec_(); // produce the Target info QS record - QS_target_info_(0xFFU); + 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 - // wait with flushing after successfull initialization (see QS_INIT()) + priv_.used = (priv_.used - n); + tail_ += n; + if (tail_ == end_) { + tail_ = 0U; + } + priv_.tail = tail_; + } + return buf_; } -//**************************************************************************** -/// @description -/// This function sets up the QS filter to enable the record type @a filter. -/// The argument #QS_ALL_RECORDS specifies to filter-in all records. -/// This function should be called indirectly through the macro -/// QS_GLB_FILTER() -/// -/// @param[in] filter the QS record-d or group to enable in the filter, -/// if positive or disable, if negative. The record-id -/// numbers must be in the range -127..127. -/// @note -/// Filtering based on the record-type is only the first layer of filtering. -/// The second layer is based on the object-type. Both filter layers must -/// be enabled for the QS record to be inserted in the QS buffer. -/// -/// @sa QP::QS::locFilter_() -/// -void QS::glbFilter_(std::int_fast16_t const filter) noexcept { +//${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) @@ -139,6 +178,7 @@ void QS::glbFilter_(std::int_fast16_t const filter) noexcept { // see qs.h, Miscellaneous QS records (not maskable) // priv_.glbFilter[0] = 0x01U; + priv_.glbFilter[6] = 0x40U; priv_.glbFilter[7] = 0xFCU; priv_.glbFilter[8] = 0x7FU; } @@ -235,10 +275,34 @@ void QS::glbFilter_(std::int_fast16_t const filter) noexcept { case QS_SC_RECORDS: if (isRemove) { priv_.glbFilter[6] &= - static_cast(~0x7FU & 0xFFU); + 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[6] |= 0x7FU; + priv_.glbFilter[9] |= 0xF8U; + priv_.glbFilter[10] |= 0x01U; } break; case QS_U0_RECORDS: @@ -325,25 +389,8 @@ void QS::glbFilter_(std::int_fast16_t const filter) noexcept { } } -//**************************************************************************** -/// @description -/// This function sets up the local QS filter to enable or disable the -/// given QS object-id or a group of object-ids @a filter. -/// This function should be called indirectly through the macro -/// QS_LOC_FILTER() -/// -/// @param[in] filter the QS object-id or group to enable in the filter, -/// if positive or disable, if negative. The qs_id numbers -/// must be in the range 1..127. -/// @note -/// Filtering based on the object-id (local filter) is the second layer of -/// filtering. The first layer is based on the QS record-type (gloabl filter). -/// Both filter layers must be enabled for the QS record to be inserted into -/// the QS buffer. -/// -/// @sa QP::QS::glbFilter_() -/// -void QS::locFilter_(std::int_fast16_t const filter) noexcept { +//${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) @@ -398,16 +445,11 @@ void QS::locFilter_(std::int_fast16_t const filter) noexcept { break; } priv_.locFilter[0] |= 0x01U; // leave QS_ID == 0 always on + } -//**************************************************************************** -/// @description -/// This function must be called at the beginning of each QS record. -/// This function should be called indirectly through the macro QS_BEGIN_ID(), -/// or QS_BEGIN_NOCRIT(), depending if it's called in a normal code or from -/// a critical section. -/// -void QS::beginRec_(std::uint_fast8_t const rec) noexcept { +//${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) @@ -415,7 +457,7 @@ void QS::beginRec_(std::uint_fast8_t const rec) noexcept { QSCtr const end_ = priv_.end; // put in a temporary (register) priv_.seq = b; // store the incremented sequence num - priv_.used += 2U; // 2 bytes about to be added + priv_.used = (priv_.used + 2U); // 2 bytes about to be added QS_INSERT_ESC_BYTE_(b) @@ -426,21 +468,15 @@ void QS::beginRec_(std::uint_fast8_t const rec) noexcept { priv_.chksum = chksum_; // save the checksum } -//**************************************************************************** -/// @description -/// This function must be called at the end of each QS record. -/// This function should be called indirectly through the macro QS_END(), -/// or QS_END_NOCRIT(), depending if it's called in a normal code or from -/// a critical section. -/// -void QS::endRec_(void) noexcept { +//${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 += 2U; // 2 bytes about to be added + priv_.used = (priv_.used + 2U); // 2 bytes about to be added if ((b != QS_FRAME) && (b != QS_ESC)) { QS_INSERT_BYTE_(b) @@ -448,7 +484,7 @@ void QS::endRec_(void) noexcept { else { QS_INSERT_BYTE_(QS_ESC) QS_INSERT_BYTE_(b ^ QS_ESC_XOR) - ++priv_.used; // account for the ESC byte + priv_.used = (priv_.used + 1U); // account for the ESC byte } QS_INSERT_BYTE_(QS_FRAME) // do not escape this QS_FRAME @@ -460,129 +496,126 @@ void QS::endRec_(void) noexcept { } } -//**************************************************************************** -void QS_target_info_(std::uint8_t const isReset) noexcept { - static constexpr std::uint8_t ZERO = static_cast('0'); - static std::uint8_t const * const TIME = - reinterpret_cast(&BUILD_TIME[0]); - static std::uint8_t const * const DATE = - reinterpret_cast(&BUILD_DATE[0]); +//${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) - QS::beginRec_(static_cast(QS_TARGET_INFO)); - QS::u8_raw_(isReset); + priv_.used = (priv_.used + 1U); // 1 byte about to be added + QS_INSERT_ESC_BYTE_(d) - static union { - std::uint16_t u16; - std::uint8_t u8[2]; - } endian_test; - endian_test.u16 = 0x0102U; - // big endian ? add the 0x8000U flag - QS_U16_PRE_(((endian_test.u8[0] == 0x01U) - ? (0x8000U | QP_VERSION) - : QP_VERSION)); // target endianness + version number + priv_.head = head_; // save the head + priv_.chksum = chksum_; // save the checksum +} - // send the object sizes... - QS::u8_raw_(Q_SIGNAL_SIZE - | static_cast(QF_EVENT_SIZ_SIZE << 4U)); +//${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) -#ifdef QF_EQUEUE_CTR_SIZE - QS::u8_raw_(QF_EQUEUE_CTR_SIZE - | static_cast(QF_TIMEEVT_CTR_SIZE << 4U)); -#else - QS::u8_raw_(static_cast(QF_TIMEEVT_CTR_SIZE << 4U)); -#endif // ifdef QF_EQUEUE_CTR_SIZE + priv_.used = (priv_.used + 2U); // 2 bytes about to be added + QS_INSERT_ESC_BYTE_(d1) + QS_INSERT_ESC_BYTE_(d2) -#ifdef QF_MPOOL_CTR_SIZE - QS::u8_raw_(QF_MPOOL_SIZ_SIZE - | static_cast(QF_MPOOL_CTR_SIZE << 4U)); -#else - QS::u8_raw_(0U); -#endif // ifdef QF_MPOOL_CTR_SIZE + priv_.head = head_; // save the head + priv_.chksum = chksum_; // save the checksum +} - QS::u8_raw_(QS_OBJ_PTR_SIZE | (QS_FUN_PTR_SIZE << 4U)); - QS::u8_raw_(QS_TIME_SIZE); +//${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) - // send the limits... - QS::u8_raw_(QF_MAX_ACTIVE); - QS::u8_raw_(QF_MAX_EPOOL | (QF_MAX_TICK_RATE << 4U)); + priv_.used = (priv_.used + 2U); // 2 bytes about to be added - // send the build time in three bytes (sec, min, hour)... - QS::u8_raw_((10U * (TIME[6] - ZERO)) + (TIME[7] - ZERO)); - QS::u8_raw_((10U * (TIME[3] - ZERO)) + (TIME[4] - ZERO)); - if (BUILD_TIME[0] == ' ') { - QS::u8_raw_(TIME[1] - ZERO); - } - else { - QS::u8_raw_((10U * (TIME[0] - ZERO)) + (TIME[1] - ZERO)); - } + QS_INSERT_ESC_BYTE_(b) - // send the build date in three bytes (day, month, year) ... - if (BUILD_DATE[4] == ' ') { - QS::u8_raw_(DATE[5] - ZERO); - } - else { - QS::u8_raw_((10U * (DATE[4] - ZERO)) + (DATE[5] - ZERO)); - } - // convert the 3-letter month to a number 1-12 ... - std::uint8_t b; - switch (DATE[0] + DATE[1] + DATE[2]) { - case 'J' + 'a' +'n': - b = 1U; - break; - case 'F' + 'e' + 'b': - b = 2U; - break; - case 'M' + 'a' +'r': - b = 3U; - break; - case 'A' + 'p' + 'r': - b = 4U; - break; - case 'M' + 'a' + 'y': - b = 5U; - break; - case 'J' + 'u' + 'n': - b = 6U; - break; - case 'J' + 'u' + 'l': - b = 7U; - break; - case 'A' + 'u' + 'g': - b = 8U; - break; - case 'S' + 'e' + 'p': - b = 9U; - break; - case 'O' + 'c' + 't': - b = 10U; - break; - case 'N' + 'o' + 'v': - b = 11U; - break; - case 'D' + 'e' + 'c': - b = 12U; - break; - default: - b = 0U; - break; - } - QS::u8_raw_(b); // store the month - QS::u8_raw_((10U * (DATE[9] - ZERO)) + (DATE[10] - ZERO)); - QS::endRec_(); + d >>= 8U; + b = static_cast(d); + QS_INSERT_ESC_BYTE_(b) + + priv_.head = head_; // save the head + priv_.chksum = chksum_; // save the checksum +} + +//${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 } -//**************************************************************************** -/// @description -/// @note This function is only to be used through macros, never in the -/// client code directly. -/// -void QS::u8_fmt_(std::uint8_t const format, std::uint8_t const d) noexcept { +//${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 += 2U; // 2 bytes about to be added + priv_.used = (priv_.used + 2U); // 2 bytes about to be added QS_INSERT_ESC_BYTE_(format) QS_INSERT_ESC_BYTE_(d) @@ -591,18 +624,17 @@ void QS::u8_fmt_(std::uint8_t const format, std::uint8_t const d) noexcept { priv_.chksum = chksum_; // save the checksum } -//**************************************************************************** -/// @description -/// This function is only to be used through macros, never in the -/// client code directly. -/// -void QS::u16_fmt_(std::uint8_t format, std::uint16_t d) noexcept { +//${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 += 3U; // 3 bytes about to be added + priv_.used = (priv_.used + 3U); // 3 bytes about to be added QS_INSERT_ESC_BYTE_(format) @@ -617,17 +649,17 @@ void QS::u16_fmt_(std::uint8_t format, std::uint16_t d) noexcept { priv_.chksum = chksum_; // save the checksum } -//**************************************************************************** -/// @note This function is only to be used through macros, never in the -/// client code directly. -/// -void QS::u32_fmt_(std::uint8_t format, std::uint32_t d) noexcept { +//${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 += static_cast(5); // 5 bytes about to be added + priv_.used = (priv_.used + 5U); // 5 bytes about to be added QS_INSERT_ESC_BYTE_(format) // insert the format byte for (std::uint_fast8_t i = 4U; i != 0U; --i) { @@ -640,64 +672,17 @@ void QS::u32_fmt_(std::uint8_t format, std::uint32_t d) noexcept { priv_.chksum = chksum_; // save the checksum } -//**************************************************************************** -/// @note This function is only to be used through macro QS_USR_DICTIONARY() -/// -void QS::usr_dict_pre_(enum_t const rec, - char_t const * const name) noexcept -{ - QS_CRIT_STAT_ - QS_CRIT_E_(); - beginRec_(static_cast(QS_USR_DICT)); - QS_U8_PRE_(rec); - QS_STR_PRE_(name); - endRec_(); - QS_CRIT_X_(); - onFlush(); -} - -//**************************************************************************** -/// @note This function is only to be used through macros, never in the -/// client code directly. -/// -void QS::mem_fmt_(std::uint8_t const *blk, std::uint8_t size) noexcept { - std::uint8_t b = static_cast(MEM_T); - std::uint8_t chksum_ = priv_.chksum + b; +//${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 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) - // size+2 bytes to be added - priv_.used += static_cast(size + 2U); - - QS_INSERT_BYTE_(b) - QS_INSERT_ESC_BYTE_(size) - - // output the 'size' number of bytes - for (; size != 0U; --size) { - b = *blk; - QS_INSERT_ESC_BYTE_(b) - ++blk; - } - - priv_.head = head_; // save the head - priv_.chksum = chksum_; // save the checksum -} - -//**************************************************************************** -/// @note This function is only to be used through macros, never in the -/// client code directly. -/// -void QS::str_fmt_(char_t const *s) noexcept { - std::uint8_t b = static_cast(*s); - std::uint8_t chksum_ = static_cast( - priv_.chksum + static_cast(STR_T)); - std::uint8_t * const buf_= priv_.buf; // put in a temporary (register) - QSCtr head_ = priv_.head; // put in a temporary (register) - QSCtr const end_= priv_.end; // put in a temporary (register) - QSCtr used_ = priv_.used; // put in a temporary (register) - - used_ += 2U; // the format byte and the terminating-0 + used_ += 2U; // the format byte and the terminating-0 QS_INSERT_BYTE_(static_cast(STR_T)) while (b != 0U) { @@ -715,284 +700,185 @@ void QS::str_fmt_(char_t const *s) noexcept { priv_.used = used_; // save # of used buffer space } -//**************************************************************************** -/// @note This function is only to be used through macros, never in the -/// client code directly. -/// -void QS::u8_raw_(std::uint8_t const d) noexcept { - std::uint8_t chksum_ = priv_.chksum; // put in a temporary (register) - std::uint8_t * const buf_ = priv_.buf; // put in a temporary (register) - QSCtr head_ = priv_.head; // put in a temporary (register) - QSCtr const end_= priv_.end; // put in a temporary (register) - - priv_.used += 1U; // 1 byte about to be added - QS_INSERT_ESC_BYTE_(d) - - priv_.head = head_; // save the head - priv_.chksum = chksum_; // save the checksum -} - -//**************************************************************************** -/// @note This function is only to be used through macros, never in the -/// client code directly. -/// -void QS::u8u8_raw_(std::uint8_t const d1, std::uint8_t const d2) noexcept { - std::uint8_t chksum_ = priv_.chksum; // put in a temporary (register) - std::uint8_t * const buf_ = priv_.buf; // put in a temporary (register) - QSCtr head_ = priv_.head; // put in a temporary (register) - QSCtr const end_= priv_.end; // put in a temporary (register) - - priv_.used += 2U; // 2 bytes about to be added - QS_INSERT_ESC_BYTE_(d1) - QS_INSERT_ESC_BYTE_(d2) - - priv_.head = head_; // save the head - priv_.chksum = chksum_; // save the checksum -} - -//**************************************************************************** -/// @note This function is only to be used through macros, never in the -/// client code directly. -/// -void QS::u16_raw_(std::uint16_t d) noexcept { - std::uint8_t b = static_cast(d); - std::uint8_t chksum_ = priv_.chksum; // put in a temporary (register) - std::uint8_t * const buf_ = priv_.buf; // put in a temporary (register) - QSCtr head_ = priv_.head; // put in a temporary (register) - QSCtr const end_= priv_.end; // put in a temporary (register) - - priv_.used += 2U; // 2 bytes about to be added - - QS_INSERT_ESC_BYTE_(b) - - d >>= 8U; - b = static_cast(d); - QS_INSERT_ESC_BYTE_(b) +//${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_.head = head_; // save the head - priv_.chksum = chksum_; // save the checksum -} + priv_.used = (priv_.used + size + 2U); // size+2 bytes to be added -//**************************************************************************** -/// @note This function is only to be used through macros, never in the -/// client code directly. -/// -void QS::u32_raw_(std::uint32_t d) noexcept { - std::uint8_t chksum_ = priv_.chksum; // put in a temporary (register) - std::uint8_t * const buf_ = priv_.buf; // put in a temporary (register) - QSCtr head_ = priv_.head; // put in a temporary (register) - QSCtr const end_= priv_.end; // put in a temporary (register) + QS_INSERT_BYTE_(b) + QS_INSERT_ESC_BYTE_(size) - priv_.used += 4U; // 4 bytes about to be added - for (std::uint_fast8_t i = 4U; i != 0U; --i) { - std::uint8_t const b = static_cast(d); + // output the 'size' number of bytes + for (; size != 0U; --size) { + b = *blk; QS_INSERT_ESC_BYTE_(b) - d >>= 8U; + ++blk; } priv_.head = head_; // save the head priv_.chksum = chksum_; // save the checksum } -//**************************************************************************** -/// @note This function is only to be used through macros, never in the -/// client code directly. -/// -void QS::obj_raw_(void const * const obj) noexcept { -#if (QS_OBJ_PTR_SIZE == 1U) - u8_raw_(reinterpret_cast(obj)); -#elif (QS_OBJ_PTR_SIZE == 2U) - u16_raw_(reinterpret_cast(obj)); -#elif (QS_OBJ_PTR_SIZE == 4U) - u32_raw_(reinterpret_cast(obj)); -#elif (QS_OBJ_PTR_SIZE == 8U) - u64_raw_(reinterpret_cast(obj)); -#else - u32_raw_(reinterpret_cast(obj)); -#endif -} +//${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_ -//**************************************************************************** -/// @note This function is only to be used through macros, never in the -/// client code directly. -/// -void QS::str_raw_(char_t const *s) noexcept { - std::uint8_t b = static_cast(*s); - std::uint8_t chksum_ = priv_.chksum; // put in a temporary (register) - std::uint8_t * const buf_ = priv_.buf; // put in a temporary (register) - QSCtr head_ = priv_.head; // put in a temporary (register) - QSCtr const end_= priv_.end; // put in a temporary (register) - QSCtr used_ = priv_.used; // put in a temporary (register) + 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(); +} - 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_; +//${QS::QS-tx::obj_dict_pre_} ................................................ +void obj_dict_pre_( + void const * const obj, + char const * const name) noexcept +{ + QS_CRIT_STAT_ - priv_.head = head_; // save the head - priv_.chksum = chksum_; // save the checksum - priv_.used = used_; // save # of used buffer space + 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(); } -//**************************************************************************** -/// @description -/// This function delivers one byte at a time from the QS data buffer. -/// -/// @returns the byte in the least-significant 8-bits of the 16-bit return -/// value if the byte is available. If no more data is available at the time, -/// the function returns QP::QS_EOD (End-Of-Data). -/// -/// @note QP::QS::getByte() is __not__ protected with a critical section. -/// -std::uint16_t QS::getByte(void) noexcept { - std::uint16_t ret; - if (priv_.used == 0U) { - ret = QS_EOD; // set End-Of-Data +//${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 { - std::uint8_t * const buf_ = priv_.buf; //put in a temporary (register) - 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; + tmp /= 10U; + idx_str[0] = static_cast( + static_cast('0') + (tmp % 10U)); + if (idx_str[0] == static_cast('0')) { + i = 1U; + } + else { + i = 0U; } - priv_.tail = tail_; // update the tail - --priv_.used; // one less byte used } - return ret; // return the byte or EOD -} -//**************************************************************************** -/// @description -/// This function delivers a contiguous block of data from the QS data buffer. -/// The function returns the pointer to the beginning of the block, and writes -/// the number of bytes in the block to the location pointed to by @a pNbytes. -/// The argument @a pNbytes is also used as input to provide the maximum size -/// of the data block that the caller can accept. -/// -/// @returns if data is available, the function returns pointer to the -/// contiguous block of data and sets the value pointed to by @p pNbytes -/// to the # available bytes. If data is available at the time the function is -/// called, the function returns NULL pointer and sets the value pointed to by -/// @p pNbytes to zero. -/// -/// @note -/// Only the NULL return from QP::QS::getBlock() indicates that the QS buffer -/// is empty at the time of the call. The non-NULL return often means that -/// the block is at the end of the buffer and you need to call -/// QP::QS::getBlock() again to obtain the rest of the data that -/// "wrapped around" to the beginning of the QS data buffer. -/// -/// @note QP::QS::getBlock() is __not__ protected with a critical section. -/// -std::uint8_t const *QS::getBlock(std::uint16_t * const pNbytes) noexcept { - QSCtr const used_ = priv_.used; // put in a temporary (register) - std::uint8_t *buf_; + QS_CRIT_STAT_ + std::uint8_t j = ((*name == '&') ? 1U : 0U); - // 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); + 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; } - *pNbytes = static_cast(n); // n-bytes available - buf_ = priv_.buf; - buf_ = &buf_[tail_]; // the bytes are at the tail - - priv_.used -= n; - tail_ += n; - if (tail_ == end_) { - tail_ = 0U; + } + for (; idx_str[i] != 0U; ++i) { + QS_U8_PRE_(idx_str[i]); + } + // skip chars until ']' + for (; name[j] != '\0'; ++j) { + if (name[j] == ']') { + break; } - priv_.tail = tail_; } - return buf_; + for (; name[j] != '\0'; ++j) { + QS_U8_PRE_(name[j]); + } + QS_U8_PRE_(0U); // zero-terminate + endRec_(); + QS_CRIT_X_(); + onFlush(); } -//**************************************************************************** -/// @note This function is only to be used through macro QS_SIG_DICTIONARY() -/// -void QS::sig_dict_pre_(enum_t const sig, void const * const obj, - char_t const *name) noexcept +//${QS::QS-tx::fun_dict_pre_} ................................................ +void fun_dict_pre_( + QSpyFunPtr fun, + char const * const name) noexcept { QS_CRIT_STAT_ - if (*name == '&') { - ++name; - } QS_CRIT_E_(); - beginRec_(static_cast(QS_SIG_DICT)); - QS_SIG_PRE_(sig); - QS_OBJ_PRE_(obj); - QS_STR_PRE_(name); + beginRec_(static_cast(QS_FUN_DICT)); + QS_FUN_PRE_(fun); + QS_STR_PRE_((*name == '&') ? &name[1] : name); endRec_(); QS_CRIT_X_(); onFlush(); } -//**************************************************************************** -/// @note This function is only to be used through macro QS_OBJ_DICTIONARY() -/// -void QS::obj_dict_pre_(void const * const obj, - char_t const *name) noexcept +//${QS::QS-tx::usr_dict_pre_} ................................................ +void usr_dict_pre_( + enum_t const rec, + char const * const name) noexcept { QS_CRIT_STAT_ - if (*name == '&') { - ++name; - } QS_CRIT_E_(); - beginRec_(static_cast(QS_OBJ_DICT)); - QS_OBJ_PRE_(obj); + beginRec_(static_cast(QS_USR_DICT)); + QS_U8_PRE_(rec); QS_STR_PRE_(name); endRec_(); QS_CRIT_X_(); onFlush(); } -//**************************************************************************** -/// @note This function is only to be used through macro QS_FUN_DICTIONARY() -/// -void QS::fun_dict_pre_(void (* const fun)(void), - char_t const *name) noexcept +//${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_ - if (*name == '&') { - ++name; - } QS_CRIT_E_(); - beginRec_(static_cast(QS_FUN_DICT)); - QS_FUN_PRE_(fun); + beginRec_(static_cast(QS_ENUM_DICT)); + QS_2U8_PRE_(static_cast(value), group); QS_STR_PRE_(name); endRec_(); QS_CRIT_X_(); onFlush(); } -//**************************************************************************** -/// @note This function is only to be used through macro QS_ASSERTION() -/// -void QS::assertion_pre_(char_t const * const module, int_t const loc, - std::uint32_t delay) +//${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_(); @@ -1000,32 +886,34 @@ void QS::assertion_pre_(char_t const * const module, int_t const loc, QS_STR_PRE_((module != nullptr) ? module : "?"); QS_END_NOCRIT_PRE_() QP::QS::onFlush(); - for (std::uint32_t volatile ctr = delay; ctr > 0U; --ctr) { + for (std::uint32_t volatile ctr = delay; ctr > 0U; ) { + ctr = (ctr - 1U); } QP::QS::onCleanup(); } -//............................................................................ -void QS::crit_entry_pre_(void) { +//${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 = (QS::priv_.critNest + 1U); QS_U8_PRE_(QS::priv_.critNest); QS_END_NOCRIT_PRE_() } -//............................................................................ -void QS::crit_exit_pre_(void) { +//${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 = (QS::priv_.critNest - 1U); QS_END_NOCRIT_PRE_() } -//............................................................................ -void QS::isr_entry_pre_(std::uint8_t const isrnest, - std::uint8_t const prio) +//${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_(); @@ -1034,9 +922,10 @@ void QS::isr_entry_pre_(std::uint8_t const isrnest, QS_END_NOCRIT_PRE_() } -//............................................................................ -void QS::isr_exit_pre_(std::uint8_t const isrnest, - std::uint8_t const prio) +//${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_(); @@ -1045,5 +934,118 @@ void QS::isr_exit_pre_(std::uint8_t const isrnest, QS_END_NOCRIT_PRE_() } -} // namespace QP +//${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 index bd066e2..ef7ee1b 100644 --- a/src/qs.hpp +++ b/src/qs.hpp @@ -1,65 +1,155 @@ -/// @file -/// @brief QS/C++ platform-independent public interface. -/// @ingroup qs -/// @cond -///*************************************************************************** -/// Last updated for version 6.9.3 -/// Last updated on 2021-02-26 -/// -/// Q u a n t u m L e a P s -/// ------------------------ -/// Modern Embedded Software -/// -/// Copyright (C) 2005-2021 Quantum Leaps. All rights reserved. -/// -/// This program is open source software: you can redistribute it and/or -/// modify it under the terms of the GNU General Public License as published -/// by the Free Software Foundation, either version 3 of the License, or -/// (at your option) any later version. -/// -/// Alternatively, this program may be distributed and modified under the -/// terms of Quantum Leaps commercial licenses, which expressly supersede -/// the GNU General Public License and are specifically designed for -/// licensees interested in retaining the proprietary status of their code. -/// -/// This program is distributed in the hope that it will be useful, -/// but WITHOUT ANY WARRANTY; without even the implied warranty of -/// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -/// GNU General Public License for more details. -/// -/// You should have received a copy of the GNU General Public License -/// along with this program. If not, see . -/// -/// Contact information: -/// -/// -///*************************************************************************** -/// @endcond - -#ifndef QS_HPP -#define QS_HPP +//$file${include::qs.hpp} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv +// +// Model: qpcpp.qm +// File: ${include::qs.hpp} +// +// This code has been generated by QM 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 { -//! Quantum Spy record types. -/// @description -/// This enumeration specifies the record types used in the QP components. -/// You can specify your own record types starting from QP::QS_USER offset. -/// Currently, the maximum of all records cannot exceed 125. -/// -/// @note -/// The QS records labeled as "not maskable" are always enabled and cannot -/// be turend off with the QS_GLB_FILTER() macro. Other QS trace records -/// can be disabled by means of the "global filters" -/// -/// @sa QS_GLB_FILTER() macro - -enum QSpyRecords : std::int8_t { +//${QS::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 @@ -91,7 +181,7 @@ enum QSpyRecords : std::int8_t { QS_QF_EQUEUE_GET, //!< get an event and queue still not empty QS_QF_EQUEUE_GET_LAST,//!< get the last event from the queue - // [23] Framework (QF) records */ + // [23] Framework (QF) records QS_QF_NEW_ATTEMPT, //!< an attempt to allocate an event failed // [24] Memory Pool (MP) records @@ -99,12 +189,12 @@ enum QSpyRecords : std::int8_t { QS_QF_MPOOL_PUT, //!< a memory block was returned to memory pool // [26] Additional Framework (QF) records - QS_QF_PUBLISH, //!< an event was published + QS_QF_PUBLISH, //!< an event was published to active objects QS_QF_NEW_REF, //!< new event reference was created QS_QF_NEW, //!< new event was created QS_QF_GC_ATTEMPT, //!< garbage collection attempt QS_QF_GC, //!< garbage collection - QS_QF_TICK, //!< QP::QF::tickX() was called + QS_QF_TICK, //!< QTimeEvt::tick_() was called // [32] Time Event (TE) records QS_QF_TIMEEVT_ARM, //!< a time event was armed @@ -127,19 +217,21 @@ enum QSpyRecords : std::int8_t { QS_QF_ACTIVE_POST_ATTEMPT, //!< attempt to post an evt to AO failed // [46] Additional Event Queue (EQ) records - QS_QF_EQUEUE_POST_ATTEMPT, //!< attempt to post an evt to QEQueue failed + QS_QF_EQUEUE_POST_ATTEMPT, //!< attempt to post evt to QEQueue failed // [47] Additional Memory Pool (MP) records QS_QF_MPOOL_GET_ATTEMPT, //!< attempt to get a memory block failed // [48] Scheduler (SC) records - QS_MUTEX_LOCK, //!< a mutex was locked - QS_MUTEX_UNLOCK, //!< a mutex was unlocked + QS_SCHED_PREEMPT, //!< scheduler asynchronously preempted a task + QS_SCHED_RESTORE, //!< scheduler restored preempted task QS_SCHED_LOCK, //!< scheduler was locked QS_SCHED_UNLOCK, //!< scheduler was unlocked QS_SCHED_NEXT, //!< scheduler found next task to execute QS_SCHED_IDLE, //!< scheduler became idle - QS_SCHED_RESUME, //!< scheduler resumed previous task (not idle) + + // [54] Miscellaneous QS records (not maskable) + QS_ENUM_DICT, //!< enumeration dictionary entry // [55] Additional QEP records QS_QEP_TRAN_HIST, //!< a tran to history was taken @@ -161,43 +253,27 @@ enum QSpyRecords : std::int8_t { QS_ASSERT_FAIL, //!< assertion failed in the code QS_QF_RUN, //!< QF_run() was entered - // [71] Reserved QS records - QS_RESERVED_71, - QS_RESERVED_72, - QS_RESERVED_73, - QS_RESERVED_74, - QS_RESERVED_75, - QS_RESERVED_76, - QS_RESERVED_77, - QS_RESERVED_78, - QS_RESERVED_79, - QS_RESERVED_80, - QS_RESERVED_81, - QS_RESERVED_82, - QS_RESERVED_83, - QS_RESERVED_84, - QS_RESERVED_85, - QS_RESERVED_86, - QS_RESERVED_87, - QS_RESERVED_88, - QS_RESERVED_89, - QS_RESERVED_90, - QS_RESERVED_91, - QS_RESERVED_92, - QS_RESERVED_93, - QS_RESERVED_94, - QS_RESERVED_95, - QS_RESERVED_96, - QS_RESERVED_97, - QS_RESERVED_98, - QS_RESERVED_99, - - // [100] Application-specific (User) QS records - QS_USER //!< the first record available to QS users + // [71] Semaphore (SEM) records + QS_SEM_TAKE, //!< a semaphore was taken by a thread + QS_SEM_BLOCK, //!< a semaphore blocked a thread + QS_SEM_SIGNAL, //!< a semaphore was signaled + QS_SEM_BLOCK_ATTEMPT, //!< a semaphore blocked was attempted + + // [75] Mutex (MTX) records + QS_MTX_LOCK, //!< a mutex was locked + QS_MTX_BLOCK, //!< a mutex blocked a thread + QS_MTX_UNLOCK, //!< a mutex was unlocked + QS_MTX_LOCK_ATTEMPT, //!< a mutex lock was attempted + QS_MTX_BLOCK_ATTEMPT, //!< a mutex blocking was attempted + QS_MTX_UNLOCK_ATTEMPT,//!< a mutex unlock was attempted + + // [81] + QS_PRE_MAX //!< the number of predefined signals }; +//${QS::QSpyGroups} .......................................................... //! QS record groups for QS_GLB_FILTER() -enum QSpyRecordGroups : std::int16_t { +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 @@ -206,6 +282,8 @@ enum QSpyRecordGroups : std::int16_t { 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 @@ -214,386 +292,543 @@ enum QSpyRecordGroups : std::int16_t { 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_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_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_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; - std::uint_fast8_t getPrio(void) const noexcept { + 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 { -} // namespace QP ************************************************************ - -#ifndef QS_TIME_SIZE - - //! The size (in bytes) of the QS time stamp. Valid values: 1U, 2U, - //! or 4U; default 4U. - /// @description - /// This macro can be defined in the QS port file (qs_port.hpp) to - /// configure the QP::QSTimeCtr type. Here the macro is not defined so - /// the default of 4 byte is chosen. - - #define QS_TIME_SIZE 4U -#endif - -#if (QS_TIME_SIZE == 1U) - #define QS_TIME_PRE_() (QP::QS::u8_raw_(QP::QS::onGetTime())) -#elif (QS_TIME_SIZE == 2U) - #define QS_TIME_PRE_() (QP::QS::u16_raw_(QP::QS::onGetTime())) -#elif (QS_TIME_SIZE == 4U) - //! Internal macro to output time stamp to a QS record - #define QS_TIME_PRE_() (QP::QS::u32_raw_(QP::QS::onGetTime())) -#else - #error "QS_TIME_SIZE defined incorrectly, expected 1U, 2U, or 4U" -#endif - - -//**************************************************************************** -namespace QP { - -#if (QS_TIME_SIZE == 1U) - using QSTimeCtr = std::uint8_t; -#elif (QS_TIME_SIZE == 2U) - using QSTimeCtr = std::uint16_t; -#elif (QS_TIME_SIZE == 4U) - //! The type of the QS time stamp. This type determines the dynamic - // range of QS time stamps - // - using QSTimeCtr = std::uint32_t; -#endif - -//! QS ring buffer counter and offset type -using QSCtr = std::uint_fast16_t; - -//! Constant representing End-Of-Data condition returned from the -//! QP::QS::getByte() function. -constexpr std::uint16_t QS_EOD = 0xFFFFU; - -//! QS logging facilities -/// @description -/// This class groups together QS services. It has only static members and -/// should not be instantiated. -class QS { +//${QS::QS-tx::QStx} ......................................................... +//! QS software tracing, output QS-TX +class QStx { public: - //! Initialize the QS data buffer. - static void initBuf(std::uint8_t * const sto, - std::uint_fast16_t const stoSize) noexcept; - - //! Set/clear the global Filter for a given QS record - // or a group of records. - static void glbFilter_(std::int_fast16_t const filter) noexcept; - - //! Set/clear the local Filter for a given object-id - // or a group of object-ids. - static void locFilter_(std::int_fast16_t const filter) noexcept; - - //! Mark the begin of a QS record @p rec - static void beginRec_(std::uint_fast8_t const rec) noexcept; - - //! Mark the end of a QS record @p rec - static void endRec_(void) noexcept; - - // raw (unformatted) output of data elements ............................. - - //! output std::uint8_t data element without format information - static void u8_raw_(std::uint8_t const d) noexcept; - - //! output two std::uint8_t data elements without format information - static void u8u8_raw_(std::uint8_t const d1, - std::uint8_t const d2) noexcept; - - //! Output std::uint16_t data element without format information - static void u16_raw_(std::uint16_t d) noexcept; - - //! Output std::uint32_t data element without format information - static void u32_raw_(std::uint32_t d) noexcept; - - //! Output obj pointer data element without format information - static void obj_raw_(void const * const obj) noexcept; - - //! Output zero-terminated ASCII string element without format information - static void str_raw_(char_t const *s) noexcept; - - - // formatted data elements output ........................................ - - //! Output std::uint8_t data element with format information - static void u8_fmt_(std::uint8_t const format, - std::uint8_t const d) noexcept; - - //! output std::uint16_t data element with format information - static void u16_fmt_(std::uint8_t format, std::uint16_t d) noexcept; - - //! Output std::uint32_t data element with format information - static void u32_fmt_(std::uint8_t format, std::uint32_t d) noexcept; - - //! Output 32-bit floating point data element with format information - static void f32_fmt_(std::uint8_t format, float32_t const d) noexcept; - - //! Output 64-bit floating point data element with format information - static void f64_fmt_(std::uint8_t format, float64_t const d) noexcept; - - //! Output zero-terminated ASCII string element with format information - static void str_fmt_(char_t const *s) noexcept; - - //! Output memory block of up to 255-bytes with format information - static void mem_fmt_(std::uint8_t const *blk, std::uint8_t size) noexcept; - - //! Output uint64_t data element without format information - static void u64_raw_(std::uint64_t d) noexcept; - - //! Output uint64_t data element with format information - static void u64_fmt_(std::uint8_t format, std::uint64_t d) noexcept; - - //! Output signal dictionary record - static void sig_dict_pre_(enum_t const sig, void const * const obj, - char_t const *name) noexcept; - - //! Output object dictionary record - static void obj_dict_pre_(void const * const obj, - char_t const *name) noexcept; - - //! Output function dictionary record - static void fun_dict_pre_(void (* const fun)(void), - char_t const *name) noexcept; - - //! Output user dictionary record - static void usr_dict_pre_(enum_t const rec, - char_t const * const name) noexcept; - - //! Initialize the QS RX data buffer - static void rxInitBuf(std::uint8_t * const sto, - std::uint16_t const stoSize) noexcept; - - //! Parse all bytes present in the QS RX data buffer - static void rxParse(void); - - //! Obtain the number of free bytes in the QS RX data buffer - static std::uint16_t rxGetNfree(void) noexcept; - - //! Put one byte into the QS RX lock-free buffer - static bool rxPut(std::uint8_t const b) noexcept; - - //! Set the "current object" in the Target - static void setCurrObj(std::uint8_t obj_kind, void *obj_ptr) noexcept; - - //! Query the "current object" in the Target - static void queryCurrObj(std::uint8_t obj_kind) noexcept; - - // QS buffer access ...................................................... - //! Byte-oriented interface to the QS data buffer. - static std::uint16_t getByte(void) noexcept; - - //! Block-oriented interface to the QS data buffer. - static std::uint8_t const *getBlock( - std::uint16_t * const pNbytes) noexcept; - - // platform-dependent callback functions to be implemented by clients .... - - //! Callback to startup the QS facility - static bool onStartup(void const *arg); - - //! Callback to cleanup the QS facility - static void onCleanup(void); - //! Callback to flush the QS trace data to the host - static void onFlush(void); - - //! Callback to obtain a timestamp for a QS record. - static QSTimeCtr onGetTime(void); - - //! callback function to reset the Target (to be implemented in the BSP) - static void onReset(void); - - //! Callback function to execute user commands (to be implemented in BSP) - static void onCommand(std::uint8_t cmdId, - std::uint32_t param1, - std::uint32_t param2, - std::uint32_t param3); - - //! internal function to handle incoming (QS-RX) packet - static void rxHandleGoodFrame_(std::uint8_t const state); - - //! internal function to produce the assertion failure trace record - static void assertion_pre_(char_t const * const module, int_t const loc, - std::uint32_t delay); - - //! internal function to produce the critical section entry record - static void crit_entry_pre_(void); - - //! internal function to produce the critical section exit record - static void crit_exit_pre_(void); - - //! internal function to produce the ISR entry record - static void isr_entry_pre_(std::uint8_t const isrnest, - std::uint8_t const prio); - - //! internal function to produce the ISR exit record - static void isr_exit_pre_(std::uint8_t const isrnest, - std::uint8_t const prio); - -#ifdef Q_UTEST - //! callback to setup a unit test inside the Target - static void onTestSetup(void); - - //! callback to teardown after a unit test inside the Target - static void onTestTeardown(void); + //! 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 +}; - //! callback to "massage" the test event before dispatching/posting it - static void onTestEvt(QEvt *e); +//${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; +} - // callback to examine an event that is about to be posted - static void onTestPost(void const *sender, QActive *recipient, - QEvt const *e, bool status); +//${QS::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; - //! callback to run the test loop - static void onTestLoop(void); +//${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: - //! internal function to process posted events during test - static void processTestEvts_(void); + //! current objects + void * currObj[8]; - //! internal function to process armed time events during test - static void tickX_(std::uint_fast8_t const tickRate, - void const * const sender) noexcept; + //! pointer to the start of the ring buffer + std::uint8_t * buf; - //! internal function to get the Test-Probe for a given API - static std::uint32_t getTestProbe_(void (* const api)(void)) noexcept; + //! offset of the end of the ring buffer + QSCtr end; -#endif // Q_UTEST + //! offset to where next byte will be inserted + QSCtr volatile head; - //! Enumerates data formats recognized by QS - /// @description - /// QS uses this enumeration is used only internally for the formatted - /// user data elements. - enum QSType : std::uint8_t { - I8_T, //!< signed 8-bit integer format - U8_T, //!< unsigned 8-bit integer format - I16_T, //!< signed 16-bit integer format - U16_T, //!< unsigned 16-bit integer format - I32_T, //!< signed 32-bit integer format - U32_T, //!< unsigned 32-bit integer format - F32_T, //!< 32-bit floating point format - F64_T, //!< 64-bit floating point format - STR_T, //!< zero-terminated ASCII string format - MEM_T, //!< up to 255-bytes memory block format - SIG_T, //!< event signal format - OBJ_T, //!< object pointer format - FUN_T, //!< function pointer format - I64_T, //!< signed 64-bit integer format - U64_T, //!< unsigned 64-bit integer format - HEX_FMT //!< HEX format for the "width" filed - }; - - //! Kinds of objects used in QS - enum QSpyObjKind : std::uint8_t { - SM_OBJ, //!< state machine object for QEP - AO_OBJ, //!< active object - MP_OBJ, //!< event pool object - EQ_OBJ, //!< raw queue object - TE_OBJ, //!< time event object - AP_OBJ, //!< generic Application-specific object - MAX_OBJ - }; - - enum OSpyObjCombnation : std::uint8_t { - SM_AO_OBJ = MAX_OBJ //!< combination of SM and AO - }; - - //! template for forcing cast of member functions for function - //! dictionaries and test probes. - template - static T_OUT force_cast(T_IN in) { - union TCast { - T_IN in; - T_OUT out; - } u = { in }; - return u.out; - } + //! offset of where next byte will be extracted + QSCtr volatile tail; - // private QS attributes ................................................. - std::uint8_t glbFilter[16]; //!< global on/off QS filter - std::uint8_t locFilter[16]; //!< lobal on/off QS filter - void const *locFilter_AP; //!< deprecated local QS filter - std::uint8_t *buf; //!< pointer to the start of the ring buffer - QSCtr end; //!< offset of the end of the ring buffer - QSCtr head; //!< offset to where next byte will be inserted - QSCtr tail; //!< offset of where next record will be extracted - QSCtr used; //!< number of bytes currently in the ring buffer - std::uint8_t seq; //!< the record sequence number - std::uint8_t chksum; //!< the checksum of the current record - std::uint8_t full; //!< the ring buffer is temporarily full - - std::uint_fast8_t critNest; //!< critical section nesting level - - static QS priv_; - - static struct QSrxPriv { - void *currObj[MAX_OBJ]; //!< current objects - std::uint8_t *buf; //!< pointer to the start of the ring buffer - QSCtr end; //!< offset of the end of the ring buffer - QSCtr head; //!< offset to where next byte will be inserted - QSCtr tail; //!< offset of where next byte will be extracted #ifdef Q_UTEST - QP::QPSet readySet; //!< QUTEST ready-set of active objects - bool inTestLoop; //!< QUTest event loop is running -#endif - } rxPriv_; + //! 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 receive channel - -//! Quantum Spy Receive (RX) record types -/// @description -/// This enumeration specifies the record types for the QS receive channel -enum QSpyRxRecords : std::uint8_t { - QS_RX_INFO, //!< query Target info (ver, config, tstamp) - QS_RX_COMMAND, //!< execute a user-defined command in the Target - QS_RX_RESET, //!< reset the Target - QS_RX_TICK, //!< call QF_tick() - QS_RX_PEEK, //!< peek Target memory - QS_RX_POKE, //!< poke Target memory - QS_RX_FILL, //!< fill Target memory - QS_RX_TEST_SETUP, //!< test setup - QS_RX_TEST_TEARDOWN, //!< test teardown - QS_RX_TEST_PROBE, //!< set a Test-Probe in the Target - QS_RX_GLB_FILTER, //!< set global filters in the Target - QS_RX_LOC_FILTER, //!< set local filters in the Target - QS_RX_AO_FILTER, //!< set local AO filter in the Target - QS_RX_CURR_OBJ, //!< set the "current-object" in the Target - QS_RX_TEST_CONTINUE, //!< continue a test after QS_RX_TEST_WAIT() - QS_RX_QUERY_CURR, //!< query the "current object" in the Target - QS_RX_EVENT //!< inject an event to the Target (post/publish) +//${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 }; -//! put one byte into the QS RX lock-free buffer -inline bool QS::rxPut(std::uint8_t const b) noexcept { +//${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; @@ -608,482 +843,764 @@ inline bool QS::rxPut(std::uint8_t const b) noexcept { } } - -//**************************************************************************** -#ifdef Q_UTEST - -//! Dummy Active Object class -/// @description -/// QActiveDummy is a test double for the role of collaborating active -/// objects in QUTest unit testing. -/// -class QActiveDummy : public QActive { -public: - QActiveDummy(void); // ctor - - void start(std::uint_fast8_t const prio, - QEvt const * * const qSto, std::uint_fast16_t const qLen, - void * const stkSto, std::uint_fast16_t const stkSize, - void const * const par) override; - - //! Overloaded start function (no initialization event) - void start(std::uint_fast8_t const prio, - QEvt const * * const qSto, std::uint_fast16_t const qLen, - void * const stkSto, std::uint_fast16_t const stkSize) override - { - this->start(prio, qSto, qLen, stkSto, stkSize, nullptr); - } - - void init(void const * const e, - std::uint_fast8_t const qs_id) noexcept override; - void init(std::uint_fast8_t const qs_id) noexcept override { - this->init(qs_id); - } - void dispatch(QEvt const * const e, - std::uint_fast8_t const qs_id) noexcept override; - bool post_(QEvt const * const e, - std::uint_fast16_t const margin, - void const * const sender) noexcept override; - void postLIFO(QEvt const * const e) noexcept override; -}; - -constexpr std::uint8_t QUTEST_ON_POST {124U}; - -// interrupt nesting up-down counter -extern std::uint8_t volatile QF_intNest; - -#endif // Q_UTEST +//${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()) -//**************************************************************************** -// Macros for adding QS instrumentation to the client code - -//! Initialize the QS facility. -/// @description -/// This macro provides an indirection layer to invoke the QS initialization -/// routine if #Q_SPY is defined, or do nothing if #Q_SPY is not defined. -/// @sa QP::QS::onStartup(), example of setting up a QS filter in -/// QS_GLB_FILTER() -#define QS_INIT(arg_) (QP::QS::onStartup(arg_)) - -//! Cleanup the QS facility. -/// @description -/// This macro provides an indirection layer to invoke the QS cleanup -/// routine if #Q_SPY is defined, or do nothing if #Q_SPY is not defined. -/// @sa QP::QS::onCleanup() -#define QS_EXIT() (QP::QS::onCleanup()) - -//! Global Filter ON for a given record type @p rec. -/// @description -/// This macro provides an indirection layer to call QP::QS::filterOn() -/// if #Q_SPY is defined, or do nothing if #Q_SPY is not defined. -/// -/// @usage -/// The following example shows how to use QS filters: -/// @include qs_filter.cpp +//${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_))) -//! Local Filter for a given state machine object @p qs_id. -/// @description -/// This macro provides an indirection layer to call QS_locFilter_() -/// if #Q_SPY is defined, or do nothing if #Q_SPY is not defined. -/// -/// The following example shows how to use QS filters: -/// @include qs_filter.cpp -#define QS_LOC_FILTER(qs_id_) \ +//${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_(); \ +} -//**************************************************************************** -// Macros to generate application-specific (user) QS records - -//! Begin a user QS record with entering critical section. -/// @description -/// The following example shows how to build a user QS record using the -/// macros QS_BEGIN_ID(), QS_END(), and the formatted output macros: -/// QS_U8(), QS_STR(), etc. -/// -/// @note -/// Must always be used in pair with QS_END() -/// -/// @include qs_user.cpp -#define QS_BEGIN_ID(rec_, qs_id_) \ - if (QS_GLB_CHECK_(rec_) && QS_LOC_CHECK_(qs_id_)) { \ - QS_CRIT_STAT_ \ - QS_CRIT_E_(); \ - QP::QS::beginRec_(static_cast(rec_)); \ - QS_TIME_PRE_(); - -//! End a QS record with exiting critical section. -/// @sa example for QS_BEGIN_ID() -/// @note Must always be used in pair with QS_BEGIN_ID() -#define QS_END() \ - QP::QS::endRec_(); \ - QS_CRIT_X_(); \ - } - -//! Begin a QS user record without entering critical section. -#define QS_BEGIN_NOCRIT(rec_, qs_id_) \ - if (QS_GLB_CHECK_(rec_) && QS_LOC_CHECK_(qs_id_)) { \ - QP::QS::beginRec_(rec_); \ - QS_TIME_PRE_(); - -//! End a QS user record without exiting critical section. -#define QS_END_NOCRIT() \ - QP::QS::endRec_(); \ - } +//${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 hook up user code when a QS record is produced - #define QS_REC_DONE() (static_cast(0)) -#endif // QS_REC_DONE - -//! helper macro for checking the global QS filter -#define QS_GLB_CHECK_(rec_) \ - ((QP::QS::priv_.glbFilter[static_cast(rec_) >> 3] \ - & static_cast(1U \ - << (static_cast(rec_) & 7U))) != 0U) - -//! helper macro for checking the local QS filter -#define QS_LOC_CHECK_(qs_id_) \ - ((QP::QS::priv_.locFilter[static_cast(qs_id_) >> 3] \ - & static_cast(1U \ - << (static_cast(qs_id_) & 7U))) != 0U) - - -//**************************************************************************** -// Facilities for QS ciritical section - -// QS-specific critical section -#ifdef QS_CRIT_ENTRY // separate QS critical section defined? - -#ifndef QS_CRIT_STAT_TYPE - #define QS_CRIT_STAT_ - #define QS_CRIT_E_() QS_CRIT_ENTRY(dummy) - #define QS_CRIT_X_() QS_CRIT_EXIT(dummy); QS_REC_DONE() -#else - #define QS_CRIT_STAT_ QS_CRIT_STAT_TYPE critStat_; - #define QS_CRIT_E_() QS_CRIT_ENTRY(critStat_) - #define QS_CRIT_X_() QS_CRIT_EXIT(critStat_); QS_REC_DONE() -#endif // QS_CRIT_STAT_TYPE - -#else // separate QS critical section not defined--use the QF definition -#ifndef QF_CRIT_STAT_TYPE - //! This is an internal macro for defining the critical section - //! status type. - /// @description - /// The purpose of this macro is to enable writing the same code for the - /// case when critical section status type is defined and when it is not. - /// If the macro #QF_CRIT_STAT_TYPE is defined, this internal macro - /// provides the definition of the critical section status variable. - /// Otherwise this macro is empty. - /// @sa #QF_CRIT_STAT_TYPE - #define QS_CRIT_STAT_ - - //! This is an internal macro for entering a critical section. - /// @description - /// The purpose of this macro is to enable writing the same code for the - /// case when critical section status type is defined and when it is not. - /// If the macro #QF_CRIT_STAT_TYPE is defined, this internal macro - /// invokes #QF_CRIT_ENTRY passing the key variable as the parameter. - /// Otherwise #QF_CRIT_ENTRY is invoked with a dummy parameter. - /// @sa #QF_CRIT_ENTRY - #define QS_CRIT_E_() QF_CRIT_ENTRY(dummy) - - //! This is an internal macro for exiting a critical section. - /// @description - /// The purpose of this macro is to enable writing the same code for the - /// case when critical section status type is defined and when it is not. - /// If the macro #QF_CRIT_STAT_TYPE is defined, this internal macro - /// invokes #QF_CRIT_EXIT passing the key variable as the parameter. - /// Otherwise #QF_CRIT_EXIT is invoked with a dummy parameter. - /// @sa #QF_CRIT_EXIT - #define QS_CRIT_X_() QF_CRIT_EXIT(dummy); QS_REC_DONE() - -#elif (!defined QS_CRIT_STAT_) - #define QS_CRIT_STAT_ QF_CRIT_STAT_TYPE critStat_; - #define QS_CRIT_E_() QF_CRIT_ENTRY(critStat_) - #define QS_CRIT_X_() QF_CRIT_EXIT(critStat_); QS_REC_DONE() -#endif // simple unconditional interrupt disabling used - -#endif // separate QS critical section not defined - - -//**************************************************************************** -// Macros for use in the client code - +//! 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_) << 4)) \ - | static_cast(QP::QS::I8_T)), (data_))) +#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_) << 4)) \ - | static_cast(QP::QS::U8_T)), (data_))) +#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_) << 4)) \ - | static_cast(QP::QS::I16_T)), (data_))) +#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_) << 4)) \ - | static_cast(QP::QS::U16_T)), (data_))) +#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_) << 4)) \ - | static_cast(QP::QS::I32_T)), (data_))) +#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_) << 4)) \ - | static_cast(QP::QS::U32_T)), (data_))) +#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_) << 4)) \ - | static_cast(QP::QS::I64_T)), (data_))) +#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_) << 4)) \ - | static_cast(QP::QS::U64_T)), (data_))) +#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_) << 4)) \ - | static_cast(QP::QS::F32_T)), (data_))) +#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_) << 4)) \ - | static_cast(QP::QS::F64_T)), (data_))) +#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_)) +#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, \ +#define QS_OBJ(obj_) (QP::QS::u8_fmt_(QP::QS::OBJ_T, \ reinterpret_cast(obj_))) -#elif (QS_OBJ_PTR_SIZE == 2U) - #define QS_OBJ(obj_) (QP::QS::u16_fmt_(QP::QS::OBJ_T, \ - reinterpret_cast(obj_))) -#elif (QS_OBJ_PTR_SIZE == 4U) - #define QS_OBJ(obj_) (QP::QS::u32_fmt_(QP::QS::OBJ_T, \ - reinterpret_cast(obj_))) -#elif (QS_OBJ_PTR_SIZE == 8U) - #define QS_OBJ(obj_) (QP::QS::u64_fmt_(QP::QS::OBJ_T, \ +#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_))) -#else - //! Output formatted object pointer to the QS record - #define QS_OBJ(obj_) (QP::QS::u32_fmt_(QP::QS::OBJ_T, \ - reinterpret_cast(obj_))) -#endif +#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) -#if (QS_FUN_PTR_SIZE == 1U) - #define QS_FUN(fun_) (QP::QS::u8_fmt_(QP::QS::FUN_T, \ - reinterpret_cast(fun_))) -#elif (QS_FUN_PTR_SIZE == 2U) - #define QS_FUN(fun_) (QP::QS::u16_fmt_(QP::QS::FUN_T, \ +//${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_))) -#elif (QS_FUN_PTR_SIZE == 4U) - #define QS_FUN(fun_) (QP::QS::u32_fmt_(QP::QS::FUN_T, \ - reinterpret_cast(fun_))) -#elif (QS_FUN_PTR_SIZE == 8U) - #define QS_FUN(fun_) (QP::QS::u64_fmt_(QP::QS::FUN_T, \ - reinterpret_cast(fun_))) -#else - //! Output formatted function pointer to the QS record - #define QS_FUN(fun_) (QP::QS::u32_fmt_(QP::QS::FUN_T, \ - reinterpret_cast(fun_))) -#endif +#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_) -#elif (Q_SIGNAL_SIZE == 2U) - #define QS_SIG(sig_, obj_) \ - QP::QS::u16_fmt_(QP::QS::SIG_T, static_cast(sig_)); \ - QP::QS::obj_raw_(obj_) -#elif (Q_SIGNAL_SIZE == 4U) - #define QS_SIG(sig_, obj_) \ - QP::QS::u32_fmt_(QP::QS::SIG_T, static_cast(sig_)); \ - QP::QS::obj_raw_(obj_) -#else - //! Output formatted event signal (of type QP::QSignal) and - //! the state machine object to the user QS record - #define QS_SIG(sig_, obj_) \ - QP::QS::u16_fmt_(QP::QS::SIG_T, static_cast(sig_)); \ - QP::QS::obj_raw_(obj_) -#endif - - -////////////////////////////////////////////////////////////////////////////// +#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 -/// -/// A signal dictionary record associates the numerical value of the signal -/// and the binary address of the state machine that consumes that signal -/// with the human-readable name of the signal. -/// -/// Providing a signal dictionary QS record can vastly improve readability of -/// the QS log, because instead of dealing with cryptic machine addresses the -/// QSpy host utility can display human-readable names. -/// -/// A signal dictionary entry is associated with both the signal value @p sig_ -/// and the state machine @p obj_, because signals are required to be unique -/// only within a given state machine and therefore the same numerical values -/// can represent different signals in different state machines. -/// -/// For the "global" signals that have the same meaning in all state machines -/// (such as globally published signals), you can specify a signal dictionary -/// entry with the @p obj_ parameter set to NULL. -/// -/// The following example shows the definition of signal dictionary entries -/// in the initial transition of the Table active object. Please note that -/// signals HUNGRY_SIG and DONE_SIG are associated with the Table state -/// machine only ("me" @p obj_ pointer). The EAT_SIG signal, on the other -/// hand, is global (0 @p obj_ pointer): -/// @include qs_sigDic.cpp -/// -/// @note The QSpy log utility must capture the signal dictionary record -/// in order to use the human-readable information. You need to connect to -/// the target before the dictionary entries have been transmitted. -/// -/// The following QSpy log example shows the signal dictionary records -/// generated from the Table initial transition and subsequent records that -/// show human-readable names of the signals: -/// @include qs_sigLog.txt -/// -/// The following QSpy log example shows the same sequence of records, but -/// with dictionary records removed. The human-readable signal names are not -/// available. -/// @include qs_sigLog0.txt +//! +//! @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_)) -//! Output object dictionary record -/// -/// An object dictionary record associates the binary address of an object -/// in the target's memory with the human-readable name of the object. -/// -/// Providing an object dictionary QS record can vastly improve readability of -/// the QS log, because instead of dealing with cryptic machine addresses the -/// QSpy host utility can display human-readable object names. -/// -/// The following example shows the definition of object dictionary entry -/// for the Table active object: -/// @include qs_objDic.cpp +//${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 -/// -/// 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. +//! +//! @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 -/// -/// A user QS record dictionary record associates the numerical value of a -/// user record with the human-readable identifier. -#define QS_USR_DICTIONARY(rec_) do { \ - static char_t const usr_name_[] = #rec_; \ - QP::QS::usr_dict_pre_((rec_), &usr_name_[0]); \ -} while (false) - -//! Produce the assertion failure trace record -#define QS_ASSERTION(module_, loc_, delay_) \ - (QP::QS::assertion_pre_((module_), (loc_), (delay_))) - +//! +//! @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_()) +#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_))) -//! Flush the QS trace data to the host -/// -/// This macro invokes the QP::QS::flush() platform-dependent callback -/// function to flush the QS trace buffer to the host. The function -/// typically busy-waits until all the data in the buffer is sent to -/// the host. This is acceptable only in the initial transient. -#define QS_FLUSH() (QP::QS::onFlush()) - +//${QS-macros::QF_QS_ACTION} ................................................. //! Execute an action that is only necessary for QS output -#define QF_QS_ACTION(act_) (act_) +#define QF_QS_ACTION(act_) (act_) -//! macro to handle the QS output from the application -//! NOTE: if this macro is used, the application must define QS_output(). -#define QS_OUTPUT() (QS_output()) +//${QS-macros::QS_ASSERTION} ................................................. +//! Produce the assertion failure trace record +#define QS_ASSERTION(module_, loc_, delay_) \ + (QP::QS::assertion_pre_((module_), (loc_), (delay_))) -//! macro to handle the QS-RX input to the application -//! NOTE: if this macro is used, the application must define QS_rx_input(). -#define QS_RX_INPUT() (QS_rx_input()) +//${QS-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)) -//**************************************************************************** -// Macros for use in QUTest only +//${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 - //! QS macro to define the Test-Probe for a given @p fun_ - #define QS_TEST_PROBE_DEF(fun_) \ - std::uint32_t const qs_tp_ = \ - QP::QS::getTestProbe_(QP::QS::force_cast(fun_)); - - //! QS macro to apply a Test-Probe - #define QS_TEST_PROBE(code_) \ - if (qs_tp_ != 0U) { code_ } - - //! QS macro to apply a Test-Probe - #define QS_TEST_PROBE_ID(id_, code_) \ - if (qs_tp_ == static_cast(id_)) { code_ } - - //! QS macro to pause test execution and enter the test event loop - #define QS_TEST_PAUSE() do { \ - QP::QS::beginRec_( \ - static_cast(QP::QS_TEST_PAUSED)); \ - QP::QS::endRec_(); \ - QP::QS::onTestLoop(); \ - } while (false) + +//$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 @@ -1093,5 +1610,4 @@ extern std::uint8_t volatile QF_intNest; #define QS_TEST_PAUSE() ((void)0) #endif // Q_UTEST -#endif // QS_HPP - +#endif // QS_HPP_ diff --git a/src/qs_dummy.hpp b/src/qs_dummy.hpp index 6e8b730..0593e7c 100644 --- a/src/qs_dummy.hpp +++ b/src/qs_dummy.hpp @@ -1,47 +1,50 @@ -/// @file -/// @brief Dummy definitions of the QS macros that avoid code generation from -/// the QS instrumentation. -/// @ingroup qs qpspy -/// @cond -///*************************************************************************** -/// Last updated for version 6.9.1 -/// Last updated on 2020-09-30 -/// -/// Q u a n t u m L e a P s -/// ------------------------ -/// Modern Embedded Software -/// -/// Copyright (C) 2005-2020 Quantum Leaps. All rights reserved. -/// -/// This program is open source software: you can redistribute it and/or -/// modify it under the terms of the GNU General Public License as published -/// by the Free Software Foundation, either version 3 of the License, or -/// (at your option) any later version. -/// -/// Alternatively, this program may be distributed and modified under the -/// terms of Quantum Leaps commercial licenses, which expressly supersede -/// the GNU General Public License and are specifically designed for -/// licensees interested in retaining the proprietary status of their code. -/// -/// This program is distributed in the hope that it will be useful, -/// but WITHOUT ANY WARRANTY; without even the implied warranty of -/// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -/// GNU General Public License for more details. -/// -/// You should have received a copy of the GNU General Public License -/// along with this program. If not, see . -/// -/// Contact information: -/// -/// -///*************************************************************************** -/// @endcond - -#ifndef QS_DUMMY_HPP -#define QS_DUMMY_HPP +//$file${include::qs_dummy.hpp} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv +// +// Model: qpcpp.qm +// File: ${include::qs_dummy.hpp} +// +// This code has been generated by QM 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_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 #define QS_INIT(arg_) (true) @@ -66,7 +69,9 @@ #define QS_U32(width_, data_) static_cast(0) #define QS_F32(width_, data_) static_cast(0) #define QS_F64(width_, data_) static_cast(0) +#define QS_I64(width_, data_) static_cast(0) #define QS_U64(width_, data_) static_cast(0) +#define QS_ENUM(group_, value_) static_cast(0) #define QS_STR(str_) static_cast(0) #define QS_MEM(mem_, size_) static_cast(0) #define QS_SIG(sig_, obj_) static_cast(0) @@ -75,8 +80,10 @@ #define QS_SIG_DICTIONARY(sig_, obj_) static_cast(0) #define QS_OBJ_DICTIONARY(obj_) static_cast(0) +#define QS_OBJ_ARR_DICTIONARY(obj_, idx_) static_cast(0) #define QS_FUN_DICTIONARY(fun_) static_cast(0) #define QS_USR_DICTIONARY(rec_) static_cast(0) +#define QS_ENUM_DICTIONARY(value_, group_) static_cast(0) #define QS_ASSERTION(module_, loc_, delay_) static_cast(0) #define QS_FLUSH() static_cast(0) @@ -88,12 +95,59 @@ #define QS_OUTPUT() static_cast(0) #define QS_RX_INPUT() 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 { + +//${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 +}; + +} // namespace QP +//$enddecl${QS::QSpyIdGroups} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +//$declare${QS::QSpyId} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv +namespace QP { + +//${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 QP +//$enddecl${QS::QSpyId} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +//============================================================================ // internal QS macros used only in the QP components #ifdef QP_IMPL // predefined QS trace records - #define QS_BEGIN_PRE_(rec_, qs_id_) if (false) { + #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_() } @@ -119,5 +173,4 @@ #define QF_QS_ACTION(act_) static_cast(0) #endif // QP_IMPL -#endif // QS_DUMMY_HPP - +#endif // QS_DUMMY_HPP_ diff --git a/src/qs_fp.cpp b/src/qs_fp.cpp index 4368fa4..70a1584 100644 --- a/src/qs_fp.cpp +++ b/src/qs_fp.cpp @@ -1,52 +1,64 @@ -/// @file -/// @brief QS floating point output implementation -/// @ingroup qs -/// @cond -///*************************************************************************** -/// Last updated for version 6.9.2 -/// Last updated on 2021-01-13 -/// -/// Q u a n t u m L e a P s -/// ------------------------ -/// Modern Embedded Software -/// -/// Copyright (C) 2005-2021 Quantum Leaps. All rights reserved. -/// -/// This program is open source software: you can redistribute it and/or -/// modify it under the terms of the GNU General Public License as published -/// by the Free Software Foundation, either version 3 of the License, or -/// (at your option) any later version. -/// -/// Alternatively, this program may be distributed and modified under the -/// terms of Quantum Leaps commercial licenses, which expressly supersede -/// the GNU General Public License and are specifically designed for -/// licensees interested in retaining the proprietary status of their code. -/// -/// This program is distributed in the hope that it will be useful, -/// but WITHOUT ANY WARRANTY; without even the implied warranty of -/// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -/// GNU General Public License for more details. -/// -/// You should have received a copy of the GNU General Public License -/// along with this program. If not, see . -/// -/// Contact information: -/// -/// -///*************************************************************************** -/// @endcond +//$file${src::qs::qs_fp.cpp} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv +// +// Model: qpcpp.qm +// File: ${src::qs::qs_fp.cpp} +// +// This code has been generated by QM 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 { -//**************************************************************************** -/// @note This function is only to be used through macros, never in the -/// client code directly. -/// -void QS::f32_fmt_(std::uint8_t format, float32_t const d) noexcept { +//${QS::QS-tx-fp::f32_fmt_} .................................................. +void f32_fmt_( + std::uint8_t format, + float32_t d) noexcept +{ union F32Rep { float32_t f; std::uint32_t u; @@ -58,7 +70,7 @@ void QS::f32_fmt_(std::uint8_t format, float32_t const d) noexcept { fu32.f = d; // assign the binary representation - priv_.used += 5U; // 5 bytes about to be added + priv_.used = (priv_.used + 5U); // 5 bytes about to be added QS_INSERT_ESC_BYTE_(format) // insert the format byte for (std::uint_fast8_t i = 4U; i != 0U; --i) { @@ -71,11 +83,11 @@ void QS::f32_fmt_(std::uint8_t format, float32_t const d) noexcept { priv_.chksum = chksum_; // save the checksum } -//**************************************************************************** -/// @note This function is only to be used through macros, never in the -/// client code directly. -/// -void QS::f64_fmt_(std::uint8_t format, float64_t const d) noexcept { +//${QS::QS-tx-fp::f64_fmt_} .................................................. +void f64_fmt_( + std::uint8_t format, + float32_t d) noexcept +{ union F64Rep { float64_t d; std::uint32_t u[2]; @@ -101,7 +113,7 @@ void QS::f64_fmt_(std::uint8_t format, float64_t const d) noexcept { fu64.u[1] = i; } - priv_.used += 9U; // 9 bytes about to be added + priv_.used = (priv_.used + 9U); // 9 bytes about to be added QS_INSERT_ESC_BYTE_(format) // insert the format byte // output 4 bytes from fu64.u[0]... @@ -120,5 +132,6 @@ void QS::f64_fmt_(std::uint8_t format, float64_t const d) noexcept { 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 index c2a2404..7dbe63a 100644 --- a/src/qs_pkg.hpp +++ b/src/qs_pkg.hpp @@ -1,43 +1,94 @@ -/// @file -/// @ingroup qs -/// @brief Internal (package scope) QS/C++ interface. -/// @cond -///*************************************************************************** -/// Last updated for version 6.9.1 -/// Last updated on 2020-09-18 -/// -/// Q u a n t u m L e a P s -/// ------------------------ -/// Modern Embedded Software -/// -/// Copyright (C) 2005-2020 Quantum Leaps. All rights reserved. -/// -/// This program is open source software: you can redistribute it and/or -/// modify it under the terms of the GNU General Public License as published -/// by the Free Software Foundation, either version 3 of the License, or -/// (at your option) any later version. -/// -/// Alternatively, this program may be distributed and modified under the -/// terms of Quantum Leaps commercial licenses, which expressly supersede -/// the GNU General Public License and are specifically designed for -/// licensees interested in retaining the proprietary status of their code. -/// -/// This program is distributed in the hope that it will be useful, -/// but WITHOUT ANY WARRANTY; without even the implied warranty of -/// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -/// GNU General Public License for more details. -/// -/// You should have received a copy of the GNU General Public License -/// along with this program. If not, see . -/// -/// Contact information: -/// -/// -///*************************************************************************** -/// @endcond - -#ifndef QS_PKG_HPP -#define QS_PKG_HPP +//$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_) \ @@ -56,55 +107,54 @@ else { \ QS_INSERT_BYTE_(QS_ESC) \ QS_INSERT_BYTE_(static_cast((b_) ^ QS_ESC_XOR)) \ - ++priv_.used; \ + priv_.used = (priv_.used + 1U); \ } -//**************************************************************************** -// Macros for use inside other macros or internally in the QP code - //! Internal QS macro to begin a predefined QS record with critical section. -/// @note -/// This macro is intended to use only inside QP components and NOT -/// at the application level. -/// @sa QS_BEGIN_ID() -/// +//! @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() -/// +//! @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_() +//! 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 +//! 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. + //! @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) @@ -133,7 +183,7 @@ (QP::QS::u32_raw_(static_cast(data_))) //! Internal QS macro to output a zero-terminated ASCII string -/// data element +//! data element #define QS_STR_PRE_(msg_) (QP::QS::str_raw_(msg_)) //! Internal QS macro to output object pointer data element @@ -155,9 +205,10 @@ //! 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. + //! + //! @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 @@ -166,7 +217,7 @@ //! Internal QS macro to output an unformatted event queue //! counter data element - /// @note the counter size depends on the macro #QF_EQUEUE_CTR_SIZE. + //! @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) @@ -184,7 +235,8 @@ //! Internal QS macro to output an unformatted event size //! data element - /// @note the event size depends on the macro #QF_EVENT_SIZ_SIZE. + //! + //! @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) @@ -199,8 +251,8 @@ #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. + //! 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) @@ -215,7 +267,7 @@ //! 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. + //! @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) @@ -231,7 +283,7 @@ //! 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. + //! @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) @@ -243,32 +295,10 @@ #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. +//! +//! @note Casting from enum to unsigned char violates the MISRA-C++ 2008 rules +//! 5-2-7, 5-2-8 and 5-2-9. Encapsulating this violation in a macro allows to +//! selectively suppress this specific deviation. #define QS_REC_NUM_(enum_) (static_cast(enum_)) -namespace QP { - -/// @brief Frame character of the QS output protocol -constexpr std::uint8_t QS_FRAME = 0x7EU; - -/// @brief Escape character of the QS output protocol -constexpr std::uint8_t QS_ESC = 0x7DU; - -/// @brief Escape modifier of the QS output protocol -/// -/// The escaped byte is XOR-ed with the escape modifier before it is inserted -/// into the QS buffer. -constexpr std::uint8_t QS_ESC_XOR = 0x20U; - -/// @brief Escape character of the QS output protocol -constexpr std::uint8_t QS_GOOD_CHKSUM = 0xFFU; - -//! send the Target info (object sizes, build time-stamp, QP version) -void QS_target_info_(std::uint8_t const isReset) noexcept; - -} // namespace QP - -#endif // QS_PKG_HPP +#endif // QS_PKG_HPP_ diff --git a/src/qs_rx.cpp b/src/qs_rx.cpp index b9ccc6d..36bc5d4 100644 --- a/src/qs_rx.cpp +++ b/src/qs_rx.cpp @@ -1,40 +1,43 @@ -/// @file -/// @brief QS receive channel services -/// @ingroup qs -/// @cond -///*************************************************************************** -/// Last updated for version 6.9.2a -/// Last updated on 2021-01-28 -/// -/// Q u a n t u m L e a P s -/// ------------------------ -/// Modern Embedded Software -/// -/// Copyright (C) 2005-2021 Quantum Leaps. All rights reserved. -/// -/// This program is open source software: you can redistribute it and/or -/// modify it under the terms of the GNU General Public License as published -/// by the Free Software Foundation, either version 3 of the License, or -/// (at your option) any later version. -/// -/// Alternatively, this program may be distributed and modified under the -/// terms of Quantum Leaps commercial licenses, which expressly supersede -/// the GNU General Public License and are specifically designed for -/// licensees interested in retaining the proprietary status of their code. -/// -/// This program is distributed in the hope that it will be useful, -/// but WITHOUT ANY WARRANTY; without even the implied warranty of -/// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -/// GNU General Public License for more details. -/// -/// You should have received a copy of the GNU General Public License -/// along with this program. If not, see . -/// -/// Contact information: -/// -/// -///*************************************************************************** -/// @endcond +//$file${src::qs::qs_rx.cpp} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv +// +// Model: qpcpp.qm +// File: ${src::qs::qs_rx.cpp} +// +// This code has been generated by QM 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 @@ -42,14 +45,14 @@ #include "qf_pkg.hpp" // QF package-scope internal interface #include "qassert.h" // QP assertions -namespace QP { +static_assert(QP::QS::MAX_OBJ <= 8U, "QS::MAX_OBJECT below the limit"); -Q_DEFINE_THIS_MODULE("qs_rx") +//============================================================================ +namespace { // unnamed local namespace -//**************************************************************************** -struct QS::QSrxPriv QS::rxPriv_; // QS-RX private data +Q_DEFINE_THIS_MODULE("qs_rx") -//**************************************************************************** +//............................................................................ #if (QS_OBJ_PTR_SIZE == 1U) using QSObj = std::uint8_t; #elif (QS_OBJ_PTR_SIZE == 2U) @@ -60,19 +63,9 @@ struct QS::QSrxPriv QS::rxPriv_; // QS-RX private data using QSObj = std::uint64_t; #endif -#if (QS_FUN_PTR_SIZE == 1U) - using QSFun = std::uint8_t; -#elif (QS_FUN_PTR_SIZE == 2U) - using QSFun = std::uint16_t; -#elif (QS_FUN_PTR_SIZE == 4U) - using QSFun = std::uint32_t; -#elif (QS_FUN_PTR_SIZE == 8U) - using QSFun = std::uint64_t; -#endif - -/// @cond -/// Exlcude the following internals from the Doxygen documentation -/// Extended-state variables used for parsing various QS-RX Records +//! @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; @@ -114,20 +107,10 @@ struct ObjVar { std::uint8_t recId; }; -struct TPVar { // Test-Probe - QSFun addr; - std::uint32_t data; - std::uint8_t idx; -}; - -struct AFltVar { - std::uint8_t prio; -}; - struct EvtVar { - QEvt *e; + QP::QEvt *e; std::uint8_t *p; - QSignal sig; + QP::QSignal sig; std::uint16_t len; std::uint8_t prio; std::uint8_t idx; @@ -141,10 +124,11 @@ static struct ExtState { PeekVar peek; PokeVar poke; FltVar flt; - AFltVar aFlt; ObjVar obj; EvtVar evt; - TPVar tp; +#ifdef Q_UTEST + QP::QS::TProbe tp; +#endif // Q_UTEST } var; std::uint8_t state; std::uint8_t esc; @@ -153,6 +137,7 @@ static struct ExtState { } l_rx; enum RxStateEnum : std::uint8_t { + ERROR_STATE, WAIT4_SEQ, WAIT4_REC, WAIT4_INFO_FRAME, @@ -187,61 +172,54 @@ enum RxStateEnum : std::uint8_t { WAIT4_EVT_SIG, WAIT4_EVT_LEN, WAIT4_EVT_PAR, - WAIT4_EVT_FRAME, + WAIT4_EVT_FRAME + +#ifdef Q_UTEST + , WAIT4_TEST_SETUP_FRAME, WAIT4_TEST_TEARDOWN_FRAME, WAIT4_TEST_PROBE_DATA, WAIT4_TEST_PROBE_ADDR, WAIT4_TEST_PROBE_FRAME, - WAIT4_TEST_CONTINUE_FRAME, - ERROR_STATE -}; - -#ifdef Q_UTEST - static struct TestData { - TPVar tpBuf[16]; // buffer of Test-Probes received so far - std::uint8_t tpNum; // current number of Test-Probes - QSTimeCtr testTime; // test time (tick counter) - } l_testData; + WAIT4_TEST_CONTINUE_FRAME #endif // Q_UTEST +}; // internal helper functions... static void rxParseData_(std::uint8_t const b) noexcept; static void rxHandleBadFrame_(std::uint8_t const state) noexcept; -static void rxReportAck_(enum QSpyRxRecords const recId) noexcept; +static void rxReportAck_(enum QP::QSpyRxRecords const recId) noexcept; static void rxReportError_(std::uint8_t const code) noexcept; -static void rxReportDone_(enum QSpyRxRecords const recId) noexcept; +static void 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 - - -//**************************************************************************** -/// @description -/// This function should be called from QS::onStartup() to provide QS-RX with -/// the receive data buffer. -/// -/// @param[in] sto[] the address of the memory block -/// @param[in] stoSize the size of this block [bytes]. The size of the -/// QS RX buffer cannot exceed 64KB. -/// -/// @note QS-RX can work with quite small data buffers, but you will start -/// losing data if the buffer is not drained fast enough in the idle task. -/// -/// @note If the data input rate exceeds the QS-RX processing rate, the data -/// will be lost, but the QS protocol will notice that: -/// (1) that the checksum in the incomplete QS records will fail; and -/// (2) the sequence counter in QS records will show discontinuities. -/// -/// The QS-RX channel will report any data errors by sending the -/// QS_RX_DATA_ERROR trace record. -/// -void QS::rxInitBuf(std::uint8_t * const sto, - std::uint16_t const stoSize) noexcept +//! @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); @@ -266,22 +244,15 @@ void QS::rxInitBuf(std::uint8_t * const sto, endRec_(); // no QS_REC_DONE(), because QS is not running yet -#ifdef Q_UTEST - l_testData.tpNum = 0U; - l_testData.testTime = 0U; -#endif // Q_UTEST + #ifdef Q_UTEST + QP::QS::testData.tpNum = 0U; + QP::QS::testData.testTime = 0U; + #endif // Q_UTEST } -//**************************************************************************** -/// @description -/// This function is intended to be called from the ISR that reads the QS-RX -/// bytes from the QSPY application. The function returns the conservative -/// number of free bytes currently available in the buffer, assuming that -/// the head pointer is not being moved concurrently. The tail pointer might -/// be moving, meaning that bytes can be concurrently removed from the buffer. -/// -std::uint16_t QS::rxGetNfree(void) noexcept { - QSCtr head = rxPriv_.head; +//${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); } @@ -294,24 +265,17 @@ std::uint16_t QS::rxGetNfree(void) noexcept { } } -//**************************************************************************** -/// -/// @description -/// This function programmatically sets the "current object" in the Target. -/// -void QS::setCurrObj(std::uint8_t obj_kind, void *obj_ptr) noexcept { - +//${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; } -//**************************************************************************** -/// -/// @description -/// This function programmatically generates the response to the query for -/// a "current object". -/// -void QS::queryCurrObj(std::uint8_t obj_kind) noexcept { +//${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) { @@ -330,23 +294,23 @@ void QS::queryCurrObj(std::uint8_t obj_kind) noexcept { break; case QS::MP_OBJ: QS_MPC_PRE_(reinterpret_cast( - QS::rxPriv_.currObj[obj_kind])->m_nFree); + QS::rxPriv_.currObj[obj_kind])->getNFree()); QS_MPC_PRE_(reinterpret_cast( - QS::rxPriv_.currObj[obj_kind])->m_nMin); + QS::rxPriv_.currObj[obj_kind])->getNMin()); break; case QS::EQ_OBJ: QS_EQC_PRE_(reinterpret_cast( - QS::rxPriv_.currObj[obj_kind])->m_nFree); + QS::rxPriv_.currObj[obj_kind])->getNFree()); QS_EQC_PRE_(reinterpret_cast( - QS::rxPriv_.currObj[obj_kind])->m_nMin); + QS::rxPriv_.currObj[obj_kind])->getNMin()); break; case QS::TE_OBJ: QS_OBJ_PRE_(reinterpret_cast( - QS::rxPriv_.currObj[obj_kind])->m_act); + QS::rxPriv_.currObj[obj_kind])->getAct()); QS_TEC_PRE_(reinterpret_cast( - QS::rxPriv_.currObj[obj_kind])->m_ctr); + QS::rxPriv_.currObj[obj_kind])->getCtr()); QS_TEC_PRE_(reinterpret_cast( - QS::rxPriv_.currObj[obj_kind])->m_interval); + QS::rxPriv_.currObj[obj_kind])->getInterval()); QS_SIG_PRE_(reinterpret_cast( QS::rxPriv_.currObj[obj_kind])->sig); QS_U8_PRE_ (reinterpret_cast( @@ -363,11 +327,10 @@ void QS::queryCurrObj(std::uint8_t obj_kind) noexcept { else { rxReportError_(static_cast(QS_RX_AO_FILTER)); } - // no need to report Done } -//**************************************************************************** -void QS::rxParse(void) { +//${QS::QS-rx::rxParse} ...................................................... +void rxParse() { QSCtr tail = rxPriv_.tail; while (rxPriv_.head != tail) { // QS-RX buffer NOT empty? std::uint8_t b = rxPriv_.buf[tail]; @@ -400,7 +363,7 @@ void QS::rxParse(void) { } else { // bad checksum l_rx.chksum = 0U; - rxReportError_(0x00U); + rxReportError_(0x41U); rxHandleBadFrame_(b); } } @@ -411,824 +374,851 @@ void QS::rxParse(void) { } } -//**************************************************************************** -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 QS_RX_INFO: - tran_(WAIT4_INFO_FRAME); - break; - case QS_RX_COMMAND: - tran_(WAIT4_CMD_ID); - break; - case QS_RX_RESET: - tran_(WAIT4_RESET_FRAME); - break; - case QS_RX_TICK: - tran_(WAIT4_TICK_RATE); - break; - case QS_RX_PEEK: - if (QS::rxPriv_.currObj[QS::AP_OBJ] != nullptr) { - l_rx.var.peek.offs = 0U; - l_rx.var.peek.idx = 0U; - tran_(WAIT4_PEEK_OFFS); - } - else { - rxReportError_(static_cast(QS_RX_PEEK)); - tran_(ERROR_STATE); - } - break; - case QS_RX_POKE: - case QS_RX_FILL: - l_rx.var.poke.fill = - (b == static_cast(QS_RX_FILL)) - ? 1U - : 0U; - if (QS::rxPriv_.currObj[QS::AP_OBJ] != nullptr) { - l_rx.var.poke.offs = 0U; - l_rx.var.poke.idx = 0U; - tran_(WAIT4_POKE_OFFS); - } - else { - rxReportError_( - (l_rx.var.poke.fill != 0U) - ? static_cast(QS_RX_FILL) - : static_cast(QS_RX_POKE)); - tran_(ERROR_STATE); - } - break; - case QS_RX_GLB_FILTER: // intentionally fall-through - case QS_RX_LOC_FILTER: - l_rx.var.flt.recId = b; - tran_(WAIT4_FILTER_LEN); - break; - case QS_RX_AO_FILTER: // intentionally fall-through - case QS_RX_CURR_OBJ: - l_rx.var.obj.recId = b; - tran_(WAIT4_OBJ_KIND); - break; - case QS_RX_QUERY_CURR: - l_rx.var.obj.recId = - static_cast(QS_RX_QUERY_CURR); - tran_(WAIT4_QUERY_KIND); - break; - case QS_RX_EVENT: - tran_(WAIT4_EVT_PRIO); - break; - -#ifdef Q_UTEST - case QS_RX_TEST_SETUP: - tran_(WAIT4_TEST_SETUP_FRAME); - break; - case QS_RX_TEST_TEARDOWN: - tran_(WAIT4_TEST_TEARDOWN_FRAME); - break; - case QS_RX_TEST_CONTINUE: - tran_(WAIT4_TEST_CONTINUE_FRAME); - break; - case QS_RX_TEST_PROBE: - if (l_testData.tpNum - < static_cast( - (sizeof(l_testData.tpBuf) - / sizeof(l_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(QS_RX_TEST_PROBE)); - tran_(ERROR_STATE); - } - break; -#endif // Q_UTEST +//${QS::QS-rx::rxHandleGoodFrame_} ........................................... +void rxHandleGoodFrame_(std::uint8_t const state) { + std::uint8_t i; + std::uint8_t *ptr; + QS_CRIT_STAT_ - default: - rxReportError_(0x43U); - tran_(ERROR_STATE); - break; - } - break; - } + switch (state) { 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 + // 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: { - // keep ignoring the data until a frame is collected + // no need to report Ack or Done, because Target resets + QS::onReset(); // reset the Target break; } - case WAIT4_TICK_RATE: { - l_rx.var.tick.rate = static_cast(b); - tran_(WAIT4_TICK_FRAME); + 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: { - // keep ignoring the data until a frame is collected + 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_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); - } + 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_PEEK_SIZE: { - if ((b == 1U) || (b == 2U) || (b == 4U)) { - l_rx.var.peek.size = b; - tran_(WAIT4_PEEK_NUM); - } - else { - rxReportError_(static_cast(QS_RX_PEEK)); - tran_(ERROR_STATE); - } + case WAIT4_POKE_DATA: { + // received less than expected poke data items + rxReportError_(static_cast(QS_RX_POKE)); break; } - case WAIT4_PEEK_NUM: { - l_rx.var.peek.num = b; - tran_(WAIT4_PEEK_FRAME); + case WAIT4_POKE_FRAME: { + rxReportAck_(QS_RX_POKE); + // no need to report done break; } - case WAIT4_PEEK_FRAME: { - // keep ignoring the data until a frame is collected + 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_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); + 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; } - break; - } - case WAIT4_POKE_SIZE: { - if ((b == 1U) - || (b == 2U) - || (b == 4U)) + else if (l_rx.var.flt.recId + == static_cast(QS_RX_LOC_FILTER)) { - l_rx.var.poke.size = b; - tran_(WAIT4_POKE_NUM); + 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.poke.fill != 0U) - ? static_cast(QS_RX_FILL) - : static_cast(QS_RX_POKE)); - tran_(ERROR_STATE); + rxReportError_(l_rx.var.flt.recId); } + + // no need to report Done 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); + 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.poke.fill != 0U) - ? static_cast(QS_RX_FILL) - : static_cast(QS_RX_POKE)); - tran_(ERROR_STATE); + rxReportError_(l_rx.var.obj.recId); } 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); - } + case WAIT4_QUERY_FRAME: { + queryCurrObj(l_rx.var.obj.kind); 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); + 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; } - case WAIT4_FILL_FRAME: { - // keep ignoring the data until a frame is collected + + #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_POKE_FRAME: { - // keep ignoring the data until a frame is collected + case WAIT4_TEST_TEARDOWN_FRAME: { + rxReportAck_(QS_RX_TEST_TEARDOWN); + QS::onTestTeardown(); // application-specific test teardown + // no need to report Done 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); - } + 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_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); - } + 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; } - case WAIT4_FILTER_FRAME: { - // keep ignoring the data until a frame is collected + #endif // Q_UTEST + + case ERROR_STATE: { + // keep ignoring all bytes until new frame break; } - case WAIT4_OBJ_KIND: { - if (b <= static_cast(QS::SM_AO_OBJ)) { - l_rx.var.obj.kind = b; - l_rx.var.obj.addr = 0U; - l_rx.var.obj.idx = 0U; - tran_(WAIT4_OBJ_ADDR); - } - else { - rxReportError_(l_rx.var.obj.recId); - tran_(ERROR_STATE); - } + default: { + rxReportError_(0x47U); 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); + } +} + +} // 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_OBJ_FRAME: { - // keep ignoring the data until a frame is collected - break; - } - case WAIT4_QUERY_KIND: { - if (b < static_cast(QS::MAX_OBJ)) { - l_rx.var.obj.kind = b; - tran_(WAIT4_QUERY_FRAME); - } - else { - rxReportError_(l_rx.var.obj.recId); - tran_(ERROR_STATE); + 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_QUERY_FRAME: { + case WAIT4_INFO_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); + 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_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); + 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_EVT_LEN: { - l_rx.var.evt.len |= static_cast( - static_cast(b) << l_rx.var.evt.idx); - l_rx.var.evt.idx += 8U; - if (l_rx.var.evt.idx == (8U * 2U)) { - if ((l_rx.var.evt.len + sizeof(QEvt)) - <= static_cast(QF::poolGetMaxBlockSize())) - { - // report Ack before generating any other QS records - rxReportAck_(QS_RX_EVENT); - - l_rx.var.evt.e = QF::newX_( - (static_cast(l_rx.var.evt.len) - + sizeof(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 += sizeof(QEvt); - if (l_rx.var.evt.len > 0U) { - tran_(WAIT4_EVT_PAR); - } - else { - tran_(WAIT4_EVT_FRAME); - } - } - else { - rxReportError_(static_cast(QS_RX_EVENT)); - tran_(ERROR_STATE); - } - } - else { - rxReportError_(static_cast(QS_RX_EVENT)); - tran_(ERROR_STATE); - } + 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_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); + 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_EVT_FRAME: { + case WAIT4_CMD_FRAME: { // keep ignoring the data until a frame is collected break; } - -#ifdef Q_UTEST - case WAIT4_TEST_SETUP_FRAME: { + case WAIT4_RESET_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 + case WAIT4_TICK_RATE: { + l_rx.var.tick.rate = static_cast(b); + tran_(WAIT4_TICK_FRAME); break; } - case WAIT4_TEST_CONTINUE_FRAME: { + case WAIT4_TICK_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); + 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; } - 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); + else { + l_rx.var.peek.offs |= static_cast( + static_cast(b) << 8U); + tran_(WAIT4_PEEK_SIZE); } break; } - case WAIT4_TEST_PROBE_FRAME: { - // keep ignoring the data until a frame is collected + 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; } -#endif // Q_UTEST - - case ERROR_STATE: { - // keep ignoring the data until a good frame is collected + case WAIT4_PEEK_NUM: { + l_rx.var.peek.num = b; + tran_(WAIT4_PEEK_FRAME); break; } - default: { // unexpected or unimplemented state - rxReportError_(0x45U); - tran_(ERROR_STATE); + case WAIT4_PEEK_FRAME: { + // keep ignoring the data until a frame is collected break; } - } -} - -//**************************************************************************** -void QS::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_(); - QS_target_info_(0U); // send only Target info - QS_CRIT_X_(); + 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_RESET_FRAME: { - // no need to report Ack or Done, because Target resets - QS::onReset(); // reset the Target + 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_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 - QS::processTestEvts_(); // process all events produced -#endif - rxReportDone_(QS_RX_COMMAND); + 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_TICK_FRAME: { - rxReportAck_(QS_RX_TICK); -#ifdef Q_UTEST - QS::tickX_(l_rx.var.tick.rate, &QS::rxPriv_); // process tick - QS::processTestEvts_(); // process all events produced -#else - QF::tickX_(static_cast(l_rx.var.tick.rate), - &QS::rxPriv_); -#endif - rxReportDone_(QS_RX_TICK); + 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_PEEK_FRAME: { - // no need to report Ack or Done - QS_CRIT_E_(); - QS::beginRec_(static_cast(QS_PEEK_DATA)); - ptr = (static_cast( - QS::rxPriv_.currObj[QS::AP_OBJ]) - + l_rx.var.peek.offs); - QS_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; - } + 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); } - 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)); + case WAIT4_FILL_FRAME: { + // keep ignoring the data until a frame is collected break; } case WAIT4_POKE_FRAME: { - rxReportAck_(QS_RX_POKE); - // no need to report done + // keep ignoring the data until a frame is collected break; } - case WAIT4_FILL_FRAME: { - rxReportAck_(QS_RX_FILL); - ptr = (static_cast( - QS::rxPriv_.currObj[QS::AP_OBJ]) - + l_rx.var.poke.offs); - for (i = 0U; i < l_rx.var.poke.num; ++i) { - switch (l_rx.var.poke.size) { - 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; - } + 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: { - 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; + // 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 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.obj.recId); + tran_(ERROR_STATE); } - else { - rxReportError_(l_rx.var.flt.recId); + 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); } - - // 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 filter = - static_cast( - reinterpret_cast( - l_rx.var.obj.addr)->m_prio); - locFilter_((i == 0) - ? filter - :-filter); - rxReportAck_(QS_RX_AO_FILTER); - } - else { - rxReportError_(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] = (void *)l_rx.var.obj.addr; - rxPriv_.currObj[AO_OBJ] = (void *)l_rx.var.obj.addr; - } - rxReportAck_( - static_cast(l_rx.var.obj.recId)); + // 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: { - queryCurrObj(l_rx.var.obj.kind); + // keep ignoring the data until a frame is collected 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 - QF::publish_(l_rx.var.evt.e, &QS::rxPriv_, 0U); + 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); } - else if (l_rx.var.evt.prio < QF_MAX_ACTIVE) { - if (!QF::active_[l_rx.var.evt.prio]->POST_X( - l_rx.var.evt.e, - 0U, // margin - &QS::rxPriv_)) + 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())) { - // 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. - // - QF_EVT_REF_CTR_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. - // - QF_EVT_REF_CTR_INC_(l_rx.var.evt.e); + // report Ack before generating any other QS records + rxReportAck_(QP::QS_RX_EVENT); - 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 + 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 { - i = 0x81U; // failure, recycle + rxReportError_( + static_cast(QP::QS_RX_EVENT)); + tran_(ERROR_STATE); } } - else { - i = 0x81U; // failure, recycle - } - - // recycle needed? - if ((i & 1U) != 0U) { - QF::gc(l_rx.var.evt.e); - } - // failure? - if ((i & 0x80U) != 0U) { - rxReportError_(static_cast(QS_RX_EVENT)); - } - else { -#ifdef Q_UTEST - QS::processTestEvts_(); // process all events produced -#endif - rxReportDone_(QS_RX_EVENT); + 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: { - rxReportAck_(QS_RX_TEST_SETUP); - l_testData.tpNum = 0U; // clear Test-Probes - l_testData.testTime = 0U; //clear time tick - // don't clear current objects - QS::onTestSetup(); // application-specific test setup - // no need to report Done + // keep ignoring the data until a frame is collected break; } case WAIT4_TEST_TEARDOWN_FRAME: { - rxReportAck_(QS_RX_TEST_TEARDOWN); - QS::onTestTeardown(); // application-specific test teardown - // no need to report Done + // keep ignoring the data until a frame is collected break; } case WAIT4_TEST_CONTINUE_FRAME: { - rxReportAck_(QS_RX_TEST_CONTINUE); - QS::rxPriv_.inTestLoop = false; // exit the QUTest loop - // no need to report Done + // 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: { - rxReportAck_(QS_RX_TEST_PROBE); - Q_ASSERT_ID(815, - l_testData.tpNum - < (sizeof(l_testData.tpBuf) / sizeof(l_testData.tpBuf[0]))); - l_testData.tpBuf[l_testData.tpNum] = l_rx.var.tp; - ++l_testData.tpNum; - // no need to report Done + // keep ignoring the data until a frame is collected break; } #endif // Q_UTEST case ERROR_STATE: { - // keep ignoring all bytes until new frame + // keep ignoring the data until a good frame is collected break; } - default: { - rxReportError_(0x47U); + 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); - QF::gc(l_rx.var.evt.e); // don't leak an allocated event +#if (QF_MAX_EPOOL > 0U) + QP::QF::gc(l_rx.var.evt.e); // don't leak an allocated event +#endif break; } default: { @@ -1237,48 +1227,48 @@ static void rxHandleBadFrame_(std::uint8_t const state) noexcept { } } -//**************************************************************************** -static void rxReportAck_(enum QSpyRxRecords const recId) noexcept { +//............................................................................ +static void rxReportAck_(enum QP::QSpyRxRecords const recId) noexcept { QS_CRIT_STAT_ QS_CRIT_E_(); - QS::beginRec_(static_cast(QS_RX_STATUS)); + QP::QS::beginRec_(static_cast(QP::QS_RX_STATUS)); QS_U8_PRE_(recId); // record ID - QS::endRec_(); + 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_(); - QS::beginRec_(static_cast(QS_RX_STATUS)); + QP::QS::beginRec_(static_cast(QP::QS_RX_STATUS)); QS_U8_PRE_(0x80U | code); // error code - QS::endRec_(); + QP::QS::endRec_(); QS_CRIT_X_(); QS_REC_DONE(); // user callback (if defined) } -//**************************************************************************** -static void rxReportDone_(enum QSpyRxRecords const recId) noexcept { +//............................................................................ +static void rxReportDone_(enum QP::QSpyRxRecords const recId) noexcept { QS_CRIT_STAT_ QS_CRIT_E_(); - QS::beginRec_(static_cast(QS_TARGET_DONE)); + QP::QS::beginRec_(static_cast(QP::QS_TARGET_DONE)); QS_TIME_PRE_(); // timestamp QS_U8_PRE_(recId); // record ID - QS::endRec_(); + QP::QS::endRec_(); QS_CRIT_X_(); QS_REC_DONE(); // user callback (if defined) } -//**************************************************************************** +//............................................................................ static void rxPoke_(void) noexcept { - std::uint8_t * const ptr = - (static_cast(QS::rxPriv_.currObj[QS::AP_OBJ]) - + l_rx.var.poke.offs); + 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); @@ -1300,55 +1290,4 @@ static void rxPoke_(void) noexcept { l_rx.var.poke.offs += static_cast(l_rx.var.poke.size); } -//============================================================================ -#ifdef Q_UTEST - -//**************************************************************************** -/// @description -/// This function obtains the Test-Probe for a given API. -/// -/// @param[in] api pointer to the API function that requests its Test-Probe -/// -/// @returns Test-Probe data that has been received for the given API -/// from the Host (running qutest). For any ginve API, the function returns -/// the Test-Probe data in the same order as it was received from the Host. -/// If there is no Test-Probe for a ginve API, or no more Test-Probes for -/// a given API, the function returns zero. -/// -std::uint32_t QS::getTestProbe_(void (* const api)(void)) noexcept { - std::uint32_t data = 0U; - for (std::uint8_t i = 0U; i < l_testData.tpNum; ++i) { - if (l_testData.tpBuf[i].addr == reinterpret_cast(api)) { - data = l_testData.tpBuf[i].data; - - QS_CRIT_STAT_ - QS_CRIT_E_(); - QS::beginRec_(static_cast(QS_TEST_PROBE_GET)); - QS_TIME_PRE_(); // timestamp - QS_FUN_PRE_(api); // the calling API - QS_U32_PRE_(data); // the Test-Probe data - QS::endRec_(); - QS_CRIT_X_(); - - QS_REC_DONE(); // user callback (if defined) - - --l_testData.tpNum; // one less Test-Probe - // move all remaining entries in the buffer up by one - for (std::uint8_t j = i; j < l_testData.tpNum; ++j) { - l_testData.tpBuf[j] = l_testData.tpBuf[j + 1U]; - } - break; // we are done (Test-Probe retreived) - } - } - return data; -} - -//**************************************************************************** -QSTimeCtr QS::onGetTime(void) { - return (++l_testData.testTime); -} - -#endif // Q_UTEST - -} // namespace QP - +} // unnamed namespace diff --git a/src/qstamp.cpp b/src/qstamp.cpp index 05d86d1..c7c1c23 100644 --- a/src/qstamp.cpp +++ b/src/qstamp.cpp @@ -1,21 +1,56 @@ -/// @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.) +//$file${src::qs::qstamp.cpp} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv +// +// Model: qpcpp.qm +// File: ${src::qs::qstamp.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::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 bb2b599..0611c06 100644 --- a/src/qstamp.hpp +++ b/src/qstamp.hpp @@ -1,15 +1,50 @@ -/// @file -/// @brief Application build time-stamp interface +//$file${include::qstamp.hpp} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv +// +// Model: qpcpp.qm +// File: ${include::qstamp.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::qstamp.hpp} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +//! @file +//! @brief Application build time-stamp interface -#ifndef QSTAMP_HPP -#define QSTAMP_HPP +#ifndef QSTAMP_HPP_ +#define QSTAMP_HPP_ namespace QP { - extern char const BUILD_DATE[12]; extern char const BUILD_TIME[9]; - } // namespace QP -#endif // QSTAMP - +#endif // QSTAMP_HPP_