diff --git a/.gitignore b/.gitignore index 3fb1d6429..71f71bdac 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,7 @@ *.qms +*.cov +*.gcov +*.log *.obj *.o *.d @@ -29,6 +32,7 @@ *.chw *.sfr *.ewt +*.csv *.user *.avrsuo *.Debug @@ -36,10 +40,29 @@ *.bak version-* JLink*.* +*.host +*.trg + +dbg/ +rel/ +spy/ +build*/ +settings/ +.settings/ +targetConfigs/ +Debug/ +Release/ +Spy/ +QSpy/ + +lib/ +obj/ +output/ include/qs.h include/qs_pkg.h include/qxk.h + src/qs/qs.c src/qs/qs_64bit.c src/qs/qs_fp.c @@ -49,29 +72,14 @@ src/qxk/qxk.c src/qxk/qxk_mutex.c src/qxk/qxk_sema.c src/qxk/qxk_xthr.c -zephyr/qutest_port.c ports/arm-cm/qxk/ ports/arm-cm/qutest/ ports/posix-qutest/ ports/win32-qutest/ +zephyr/qutest_port.c -priv/ -html/ -latex/ -dbg/ -rel/ -spy/ -build*/ -settings/ -.settings/ -targetConfigs/ - -Debug/ -Release/ -Spy/ -QSpy/ +static-analysis/pclp/ -lib/ -obj/ -output/ +tests/TIN*/ +tests/TUN*/ diff --git a/3rd_party b/3rd_party index b059af35d..1320b7d6c 160000 --- a/3rd_party +++ b/3rd_party @@ -1 +1 @@ -Subproject commit b059af35dafb3d85bcd19ee29fabc222e5fb1661 +Subproject commit 1320b7d6c815450b3f003ca8bfbf05fa31f75916 diff --git a/README.md b/README.md index 9a756142d..b31bdf4f9 100644 --- a/README.md +++ b/README.md @@ -13,8 +13,10 @@ it is recommended that you clone this repo like that: git clone https://github.com/QuantumLeaps/qpc --recurse-submodules --depth 1 ``` -Alternatively, you can also download one of the stable -[QP/C Releases][QP-Rel]. +However, the easiest and most recommended way of +[getting started with QP/C](#getting-started-with-qpc) is to download +the QP-bundle, as [described below](#getting-started-with-qpc). + # About QP/C Real-Time Event Framework QP/C real-time event framework (RTEF) is a lightweight implementation of @@ -25,56 +27,94 @@ of Active Objects (Actors) and a runtime environment for executing the Active Objects in a deterministic, real-time fashion. Additionally, QP/C Framework supports Hierarchical State Machines with which to specify the behavior of Active Objects [UML 2.5], [Sutter:10], [ROOM:94]. The QP/C Framework can be -viewed as a modern, asynchronous, and truly event driven real-time operating +viewed as a modern, asynchronous, and truly event-driven real-time operating system (RTOS). ## QP Framework Family -QP/C framework is part of the larger QP family consisting of the following -QP editions: +QP/C framework is part of the larger QP family, consisting of the following +QP editions (please also see [__QP Frameworks Feature Comparison__](https://www.state-machine.com/products/qp#Features) +): |QP Edition | Language | API | Safety Functions |Certification Artifacts| Licensing |:----------|:-----------:|:-----------------|:-------------------|:----------------|:--------- -| QP/C | C (C11) |same as SafeQP/C |Selected Assertions |Req/Arch/Design | [dual][Lic] -| QP/C++ | C++ (C++17) |same as SafeQP/C++|Selected Assertions |Req/Arch/Design | [dual][Lic] -| SafeQP/C | C (C11) |same as QP/C |All Safety Functions|Extensive
Certification Kit| [commercial][Com] -| SafeQP/C++| C++ (C++17) |same as QP/C++ |All Safety Functions|Extensive
Certification Kit| [commercial][Com] +| QP/C | C (C11) |same as
SafeQP/C |Selected Assertions |Req/Arch/Design | [open-source & commercial][Lic] +| QP/C++ | C++ (C++17) |same as
SafeQP/C++|Selected Assertions |Req/Arch/Design | [open-source & commercial][Lic] +| SafeQP/C | C (C11) |same as
QP/C |All Safety Functions|[SafeQP/C
Certification Kit][CERT-KIT]| [commercial][Com] +| SafeQP/C++| C++ (C++17) |same as
QP/C++ |All Safety Functions|[SafeQP/C++
Certification Kit][CERT-KIT]| [commercial][Com] The **SafeQP/C** and **SafeQP/C++** frameworks were originally derived from QP/C and QP/C++, respectively, but were extensively reengineered for the safety market using compliant Software Safety Lifecycle (SSL). In this process, the QP framework functional model has been subjected to a full Hazard and Risk Analysis, which identified all areas of weakness within -the functional model and API. These findings led to creation of Safety Requirements and risk +the functional model and API. These findings led to the creation of Safety Requirements and risk mitigation by Safety Functions, which were subsequently implemented, verified, and validated. The SafeQP frameworks are accompanied by the "SafeQP Certification Kits", which provide developers with ready-to-use artifacts, enabling them to save time, mitigate risk, and reduce costs during application certification for safety-critical devices in the industrial, medical, -aerospace, and automotive industries. Please [contact Quantum Leaps](#contact-information) +aerospace, and automotive industries. Please [contact Quantum Leaps][Cont] for more information about the SafeQP frameworks and the "Certification Kits". -> **NOTE:** The SafeQP/C edition remain fully API- and functionally compatible with the +> **NOTE:** The SafeQP/C edition remains fully API- and functionally compatible with the corresponding standard QP/C framework. This ensures existing QP/C Applications can transition -seamlessly to SafeQP/C without requiring any modifications. SafeQP/C edition retain QP/C -Frameworks' hallmark features, including a small memory footprint, excellent efficiency, -and hard real-time functionality. +seamlessly to SafeQP/C without requiring any modifications. SafeQP/C edition retains QP/C +Frameworks' hallmark features: a small memory footprint, excellent efficiency, and hard +real-time performance. # Getting Started with QP/C -The most recommended way of obtaining QP/C is by downloading the -[QP-bundle](https://www.state-machine.com/#Downloads), which includes QP/C -as well as the [QM modeling tool][QM] and the [QTools collection][QTools]. -The main advantage of obtaining QP/C bundled together like that is -that you get all components, tools and examples ready to go. - -### Getting Started Resources -- ["QP/C Tutorial"][Tut] -describes a series of progressively advanced QP/C example applications. - +The most recommended way to get started with QP/C is by +[__downloading the QP-bundle__](https://www.state-machine.com/#Downloads), +which includes QP/C as well as the [QM modeling tool][QM] and the +[QTools collection][QTools]. The main advantage of obtaining QP/C bundled +together is that you get all components, tools, and _examples_ all ready to go. + +> **NOTE:** +Perhaps the most important fact to remember is that in embedded systems, +nothing works until everything works. This means that you should always start +with a _working system_ and gradually evolve it, changing one thing at a time +and making sure that it keeps working every step of the way. + +The provided [__QP/C example projects__](examples), such as the super-simple +[Blinky](examples/arm-cm/blinky_nucleo-u545re/), or a bit more advanced +[Dining Philosophers Problem (DPP)](examples/arm-cm/dpp_nucleo-u545re/), +allow you to get started with a __working project__ rather than starting from +scratch. You should also always try one of the unmodified examples on one +of the very inexpensive evaluation boards (such as +[STM32 NUCLEO-U545RE](examples/arm-cm/dpp_nucleo-u545re/stm32-nucleo-u545re.webp)) +that it was designed for, _before_ attempting to immediately adapt the projects +to your specific hardware. Only once an example project is built and runs on +the evaluation board, can you use it as a starting point for your specific hardware +and software development. + + +## Getting Started Resources - [Video: "Getting Started with QP Real-Time Event Frameworks"][Video] provides instructions on how to download, install, and get started with QP. - [AppNote: "Getting Started with QP Real-Time Event Frameworks"][AN] contains also a tutorial, in which you build a simple "Blinky" application. +- ["QP/C Tutorial"][Tut] +describes a series of progressively advanced QP/C example applications. + +## QP/C Extras +The open source GPL distribution of QP/C can be augmented by the +["QP/C Extras"][QPX/C], which provide more advanced QP/C features, such as: +- [QS software tracing][QS] component (QP Spy) +- [QXK real-time kernel][QXK] component +- Static-analysis configuration and automation scripts for PC-Lint-Plus (MISRA-C:2025 compliance) +- Test suite (based on the [QUTest trace-based test harness][QUTest]) +that demonstrates 100% lines of code and 100% MC/DC code coverage for QP/C. + +> **NOTE:** The ["QP/C Extras"][QPX/C] are [licensed commercially][Lic] only +and available to the commercial licensees with the active Support Term. Please contact +[Quantum Leaps technical support][Sup] to get the matching ["QP/C Extras"][QPX/C] +for the public QP/C version. + +> **NOTE:** The ["QP/C Extras"][QPX/C] are also __available for evaluation__ +([upon request][ReqForm]). + + # Licensing The QP/C real-time event framework is licensed under the [dual licensing model](https://www.state-machine.com/licensing), with @@ -101,21 +141,6 @@ any open source license and you do not violate your policy. are licensed commercially only. -## Files Removed from the QP/C Open Source GPL Distribution -Due to the widespread non-compliance with the GPL, as well as infringement on the -[dual-licensing model of QP frameworks][Lic], the following QP/C components -have been **removed from the open-source GPL distribution**: -- QS target-resident software tracing component -- QXK dual-mode kernel - -> NOTE: These components are available to the [commercial licensees][Cust] with -the active Support Term. Please contact [Quantum Leaps technical support][Sup] -to get the complete QP/C framework distribution. - -> **NOTE:** To request **evaluation** of the complete QP/C framework, please contact -Quantum Leaps at: https://www.state-machine.com/contact - - # Documentation The online HTML documentation for the **latest** version of QP/C is located at: https://www.state-machine.com/qpc @@ -126,7 +151,7 @@ To view the offline documentation, open the file [html/index.html](html/index.ht in your web browser. -# Contact Information +# Support & Contact Information - [Free Support Forum](https://sourceforge.net/p/qpc/discussion/668726) - [Bug Reports](https://sourceforge.net/p/qpc/bugs/) - [Feature Requests](https://sourceforge.net/p/qpc/feature-requests/) @@ -142,15 +167,20 @@ If you like this project, please give it a star (in the upper-right corner of yo

+ [Cont]: [RTEF]: [QP]: [QP/C]: [QP/C++]: - [Cert]: + [QPX/C]: + [QPX/C++]: + [CERT-KIT]: [QM]: [QTools]: + [QUTest]: [Lic]: [Com]: + [ReqForm]: [Cust]: [Sup]: [AN]: diff --git a/examples b/examples index 1d92e4285..32556a797 160000 --- a/examples +++ b/examples @@ -1 +1 @@ -Subproject commit 1d92e4285e9ba21abcb035038a8b2cda49944fbf +Subproject commit 32556a7973ed0a7bc5da70d1c3dd66987b991644 diff --git a/include/README.md b/include/README.md index 2c26380d2..6464a8e34 100644 --- a/include/README.md +++ b/include/README.md @@ -1,45 +1,173 @@ -# Files Missing from the QP/C GPL Distribution -Due to the widespread non-compliance with the GPL, as well as infringement -on the [dual-licensing model of QP frameworks][Lic], the following files -have been **removed from the open-source GPL distribution**: +# Available QP/C-Extras +The open source GPL distribution of QP/C can be augmented by the +[QP/C Extras][Extras], which provide more advanced QP/C features, such as: + +- QS software tracing component (QP Spy); +- QXK real-time kernel component; +- Static-analysis configuration and automation scripts for the PC-Lint-Plus static analysis tool; +- Test suite (for the QUTest trace-based test harness?) that demonstrates + 100% lines of code and 100% MC/DC code coverage for QP/C. + +# Licensing and Getting QP/C Extras +The QP/C Extras are [licensed commercially][Lic] and available to the commercial +licensees with the active Support Term. + +> **NOTE:** +If you already are a commercial licensee, please [contact Quantum Leaps technical support][Sup] +to obtain the [QP/C Extras][Extras] matching the indicated public QP/C version. + +> **NOTE:** +The [QP/C Extras][Extras] are also available for evaluation ([upon request][Req]). + +# QP/C Extras Directories & Files +QP/C Extras consist of the following directories and files: ``` -qpc +qpc/ // qpc installation directory +| ++---include/ +| qs.h // QS target-resident software tracing public header file +| qs_pkg.h // QS target-resident software tracing package-scope header +| qxk.h // QXK dual-mode real-time kernel header file | -+---include -| qs.h -| qs_pkg.h -| qxk.h ++---src/ +| | +| +---qs/ // QS target-resident software tracing component implementation +| | qs.c +| | qs_64bit.c +| | qs_fp.c +| | qs_rx.c +| | qutest.c +| | +| \---qxk/ // QXK dual-mode component implementation +| qxk.c +| qxk_mutex.c +| qxk_sema.c +| qxk_xthr.c | -\---src ++---ports/ +| +---arm-cm/ +| | +---qxk/ +| | | +---armclang/ +| | | | qxk_port.c +| | | | qp_port.h +| | | | qs_port.h +| | | | +| | | +---gnu/ +| | | | qxk_port.c +| | | | qp_port.h +| | | | qs_port.h +| | | | +| | | \---iar/ +| | | qxk_port.c +| | | qp_port.h +| | | qs_port.h +| | | +| | \---qutest/ +| | qp_port.h +| | qs_port.h +| | +| +---posix-qutest/ +| | qp_port.h +| | qs_port.h +| | qutest_port.c +| | safe_std.h +| | +| \---win32-qutest/ +| qp_port.h +| qs_port.h +| qutest_port.c +| safe_std.h +| ++---static-analysis/ +| | +| \---pclp +| | au-barr.lnt // BARR-C:2018 stylistic guidelines for C code +| | au-ds.lnt // Dan Saks' recommendations for placing 'const' and 'volatile' +| | au-misra5.lnt // PCLP MISRA-C:2025 (MC5) confiuration for rules checking +| | co-gnu_arm.bat // script to generate configuration for GNU-ARM toolset +| | co-gnu_arm.h // generated PCLP header file for GNU-ARM (14.3 at this time) +| | co-gnu_arm.lnt // generated PCLP configuration file for GNU-ARM (14.3 at this time) +| | co-mingw32.bat // script to generate configuration for MinGW32 toolset +| | co-mingw32.h // generated PCLP header file for MinGW32 (14.2 at this time) +| | co-mingw32.lnt // generated PCLP configuration for MinGW32 (14.2 at this time) +| | make.bat // script to run the PCLP analysis +| | metrics_file.py // Python script to generate file metrics report from PCLP data +| | metrics_func.py // Python script to generate function metrics report from PCLP data +| | options.lnt // PCLP configuration with Deviation Records in QP source code +| | qpcpp.lnt // PCLP configuration with Deviation Permits in QP applications +| | qp_config.h // QP compile-time configuration used for "linting" QP source code +| | std.lnt // PCLP standard configuration used for "linting" QP source code +| | +| +---qk/ +| | qp_port.hpp // "QK port" for "linting" QK kernel code +| | qs_port.hpp // "QS port" for "linting" QK kernel code +| | +| +---qv/ +| | qp_port.hpp // "QV port" for "linting" QV kernel code +| | qs_port.hpp // "QS port" for "linting" QV kernel code +| | +| \---qxk/ +| qp_port.hpp // "QXK port" for "linting" QXK kernel code +| qs_port.hpp // "QS port" for "linting" QXK kernel code +| +\---tests/ | - +---qs - | qs.c - | qs_64bit.c - | qs_fp.c - | qs_rx.c - | qutest.c + | // unit tests (TUN) (host and embedded target) + +---TUN_QP_qep_hsm/ // unit tests (TUN) for the qep_hsm module + | +---src/ // code for exercising the module under test + | | tstsm.c // QM-generated state machine implementation + | | tstsm.h // QM-generated state machine interface + | | tstsm.qm // QM model to generate code + | | tstsm_sf.c // QM-generated state machines for Safety Functions + | | tstsm_sf.h // QM-generated state machines interface + | \---test/ + | | Makefile // makefile to build & run tests on the host + | | nucleo-u545re.mak // makefile to build & run tests on NUCLEO-U545 + | | test_fixture.c // QUTest fixture + | | TUN_QP_qep_hsm.py // QUTest script for main functionality + | | TUN_QP_qep_hsm_sf.py // QUTest script for Safety Functions + | | + +---TUN_QP_qep_msm/ // unit tests (TUN) for the qep_msm module + | ... + +---TUN_QP_qf_act/ // unit tests (TUN) for the qf_act module + | ... + +---TUN_QP_qf_actq/ // unit tests (TUN) for the qf_act module + | ... + +---TUN_QP_qf_defer/ // unit tests (TUN) for the qf_act module + | ... + +---TUN_QP_qf_dyn/ // unit tests (TUN) for the qf_act module + | ... + +---TUN_QP_qf_mem/ // unit tests (TUN) for the qf_act module + | ... + +---TUN_QP_qf_ps/ // unit tests (TUN) for the qf_act module + | ... + +---TUN_QP_qf_qact32/ // unit tests (TUN) for the qf_act module + | ... + +---TUN_QP_qf_qact64/ // unit tests (TUN) for the qf_act module + | ... + +---TUN_QP_qf_qeq/ // unit tests (TUN) for the qf_act module + | ... + +---TUN_QP_qf_qmact/ // unit tests (TUN) for the qf_act module + | ... + +---TUN_QP_qf_time/ // unit tests (TUN) for the qf_act module + | ... + +---TUN_QP_qutest/ // unit tests (TUN) for the qf_act module + | ... | - \---qxk - qxk.c - qxk_mutex.c - qxk_sema.c - qxk_xthr.c + | // integration tests (TIN) (embedded target only) + +---TIN_QP_mem/ // integration tests (TIN) for memory protection + | \---test_mpu/ + +---TIN_QP_qk/ // integration tests (TIN) for QK kernel + | \---test_sched/ + +---TIN_QP_qv/ // integration tests (TIN) for QV kernel + | \---test_sched/ + +---TIN_QP_qxk/ // integration tests (TIN) for QXK kernel + +---test_mutex/ + \---test_sched/ ``` -> NOTE: These files are available to the [commercial licensees][Cust] with -the active Support Term. Please contact [Quantum Leaps technical support][Sup] -to get the complete QP/C framework distribution. - -# QP/C Framework Evaluation -To request **evaluation** of the complete QP/C framework, please contact -Quantum Leaps at: -- https://www.state-machine.com/contact - -# Quantum Leaps Licensing: -To learn more about the open source and commercial licensing options: -- https://www.state-machine.com/licensing - + [Extras]: [Lic]: - [Cust]: + [Req]: [Sup]: diff --git a/include/qequeue.h b/include/qequeue.h index 29ced9cdc..5445713ab 100644 --- a/include/qequeue.h +++ b/include/qequeue.h @@ -46,13 +46,13 @@ struct QEvt; // forward declaration //============================================================================ //! @class QEQueue typedef struct QEQueue { - struct QEvt const * volatile frontEvt; //!< @private @memberof QEQueue - struct QEvt const * * ring; //!< @private @memberof QEQueue - QEQueueCtr end; //!< @private @memberof QEQueue - QEQueueCtr volatile head; //!< @private @memberof QEQueue - QEQueueCtr volatile tail; //!< @private @memberof QEQueue - QEQueueCtr volatile nFree; //!< @private @memberof QEQueue - QEQueueCtr nMin; //!< @private @memberof QEQueue + struct QEvt const *frontEvt; //!< @private @memberof QEQueue + struct QEvt const * *ring; //!< @private @memberof QEQueue + QEQueueCtr end; //!< @private @memberof QEQueue + QEQueueCtr head; //!< @private @memberof QEQueue + QEQueueCtr tail; //!< @private @memberof QEQueue + QEQueueCtr nFree; //!< @private @memberof QEQueue + QEQueueCtr nMin; //!< @private @memberof QEQueue } QEQueue; //! @public @memberof QEQueue @@ -76,18 +76,15 @@ struct QEvt const * QEQueue_get(QEQueue * const me, uint_fast8_t const qsId); //! @public @memberof QEQueue -static inline QEQueueCtr QEQueue_getNFree(QEQueue const * const me) { - return me->nFree; -} +uint16_t QEQueue_getFree(QEQueue const * const me); //! @public @memberof QEQueue -static inline QEQueueCtr QEQueue_getNMin(QEQueue const * const me) { - return me->nMin; -} +uint16_t QEQueue_getUse(QEQueue const * const me); //! @public @memberof QEQueue -static inline bool QEQueue_isEmpty(QEQueue const * const me) { - return me->frontEvt == (struct QEvt *)0; -} +uint16_t QEQueue_getMin(QEQueue const * const me); + +//! @public @memberof QEQueue +bool QEQueue_isEmpty(QEQueue const * const me); #endif // QEQUEUE_H_ diff --git a/include/qk.h b/include/qk.h index 3aec619f7..f53c5a9f4 100644 --- a/include/qk.h +++ b/include/qk.h @@ -42,12 +42,12 @@ typedef uint_fast8_t QSchedStatus; //============================================================================ //! @class QK_Attr typedef struct { - QPSet readySet; //!< @memberof QK_Attr - uint8_t actPrio; //!< @memberof QK_Attr - uint8_t nextPrio; //!< @memberof QK_Attr - uint8_t actThre; //!< @memberof QK_Attr - uint8_t lockCeil; //!< @memberof QK_Attr - uint8_t intNest; //!< @memberof QK_Attr + QPSet readySet; //!< @private @memberof QK_Attr + uint8_t actPrio; //!< @private @memberof QK_Attr + uint8_t nextPrio; //!< @private @memberof QK_Attr + uint8_t actThre; //!< @private @memberof QK_Attr + uint8_t lockCeil; //!< @private @memberof QK_Attr + uint8_t intNest; //!< @private @memberof QK_Attr } QK_Attr; //! @static @private @memberof QK @@ -65,7 +65,7 @@ uint_fast8_t QK_sched_act_( void QK_activate_(void); //! @static @public @memberof QK -QSchedStatus QK_schedLock(uint_fast8_t const ceiling); +QSchedStatus QK_schedLock(uint8_t const ceiling); //! @static @public @memberof QK void QK_schedUnlock(QSchedStatus const prevCeil); @@ -104,15 +104,17 @@ void QK_onIdle(void); } \ } while (false) -// QF event pool customization for QK... -#define QF_EPOOL_TYPE_ QMPool +// QMPool operations +#define QF_EPOOL_TYPE_ QMPool #define QF_EPOOL_INIT_(p_, poolSto_, poolSize_, evtSize_) \ (QMPool_init(&(p_), (poolSto_), (poolSize_), (evtSize_))) -#define QF_EPOOL_EVENT_SIZE_(p_) ((uint_fast16_t)(p_).blockSize) +#define QF_EPOOL_EVENT_SIZE_(p_) ((uint16_t)(p_).blockSize) #define QF_EPOOL_GET_(p_, e_, m_, qsId_) \ ((e_) = (QEvt *)QMPool_get(&(p_), (m_), (qsId_))) -#define QF_EPOOL_PUT_(p_, e_, qsId_) \ - (QMPool_put(&(p_), (e_), (qsId_))) +#define QF_EPOOL_PUT_(p_, e_, qsId_) (QMPool_put(&(p_), (e_), (qsId_))) +#define QF_EPOOL_USE_(ePool_) (QMPool_getUse(ePool_)) +#define QF_EPOOL_FREE_(ePool_) ((ePool_)->nFree) +#define QF_EPOOL_MIN_(ePool_) ((ePool_)->nMin) #endif // QP_IMPL diff --git a/include/qmpool.h b/include/qmpool.h index e48f2388c..551187f9d 100644 --- a/include/qmpool.h +++ b/include/qmpool.h @@ -38,9 +38,9 @@ #define QF_MPOOL_EL(evType_) struct { \ void * sto_[((sizeof(evType_) - 1U) / sizeof(void *)) + \ - (sizeof(evType_) < (2U * sizeof(void *)) ? 2U : 1U)]; \ -} + (sizeof(evType_) < (2U * sizeof(void *)) ? 2U : 1U)]; } +//============================================================================ #if (QF_MPOOL_SIZ_SIZE == 1U) typedef uint8_t QMPoolSize; #elif (QF_MPOOL_SIZ_SIZE == 2U) @@ -64,13 +64,13 @@ //============================================================================ //! @class QMPool typedef struct { - void * * start; //!< @private @memberof QMPool - void * * end; //!< @private @memberof QMPool - void * * volatile freeHead; //!< @private @memberof QMPool - QMPoolSize blockSize; //!< @private @memberof QMPool - QMPoolCtr nTot; //!< @private @memberof QMPool - QMPoolCtr volatile nFree; //!< @private @memberof QMPool - QMPoolCtr nMin; //!< @private @memberof QMPool + void * *start; //!< @private @memberof QMPool + void * *end; //!< @private @memberof QMPool + void * *freeHead; //!< @private @memberof QMPool + QMPoolSize blockSize; //!< @private @memberof QMPool + QMPoolCtr nTot; //!< @private @memberof QMPool + QMPoolCtr nFree; //!< @private @memberof QMPool + QMPoolCtr nMin; //!< @private @memberof QMPool } QMPool; //! @public @memberof QMPool @@ -89,4 +89,13 @@ void QMPool_put(QMPool * const me, void * const block, uint_fast8_t const qsId); -#endif // QMPOOL_H_ +//! @public @memberof QMPool +uint16_t QMPool_getUse(QMPool const * const me); + +//! @public @memberof QMPool +uint16_t QMPool_getFree(QMPool const * const me); + +//! @public @memberof QMPool +uint16_t QMPool_getMin(QMPool const * const me); + +#endif // QMPOOL_H_ diff --git a/include/qp.h b/include/qp.h index cbec0086c..7b249e041 100644 --- a/include/qp.h +++ b/include/qp.h @@ -30,19 +30,15 @@ #define QP_H_ //============================================================================ -#define QP_VERSION_STR "8.0.5" -#define QP_VERSION 805U -// =805 =250811 -#define QP_RELEASE 0x6A81442A +#define QP_VERSION_STR "8.1.0" +#define QP_VERSION 810U +// =810 =250930 +#define QP_RELEASE 0x6A6F1BB5U -//============================================================================ +//---------------------------------------------------------------------------- // default configuration settings //! @cond INTERNAL -#ifndef Q_SIGNAL_SIZE -#define Q_SIGNAL_SIZE 2U -#endif - #ifndef QF_MAX_ACTIVE #define QF_MAX_ACTIVE 32U #endif @@ -85,60 +81,61 @@ //! @endcond -//============================================================================ +//---------------------------------------------------------------------------- // global types/utilities -typedef int int_t; -typedef int enum_t; +typedef int int_t; +typedef int enum_t; #define Q_UNUSED_PAR(par_) ((void)(par_)) #define Q_DIM(array_) (sizeof(array_) / sizeof((array_)[0U])) #define Q_UINT2PTR_CAST(type_, uint_) ((type_ *)(uint_)) -//============================================================================ extern char const QP_versionStr[24]; -// QSignal type -#if (Q_SIGNAL_SIZE == 1U) - typedef uint8_t QSignal; -#elif (Q_SIGNAL_SIZE == 2U) - typedef uint16_t QSignal; -#elif (Q_SIGNAL_SIZE == 4U) - typedef uint32_t QSignal; -#endif +//---------------------------------------------------------------------------- +typedef uint16_t QSignal; -//============================================================================ //! @class QEvt typedef struct QEvt { - QSignal sig; //!< @public @memberof QEvt - uint8_t poolNum_; //!< @private @memberof QEvt - uint8_t volatile refCtr_; //!< @private @memberof QEvt + uint32_t sig : 16; //!< @public @memberof QEvt + uint32_t poolNum_ : 8; //!< @private @memberof QEvt + uint32_t refCtr_ : 8; //!< @private @memberof QEvt + uint32_t filler_; //!< @private @memberof QEvt } QEvt; -#define QEVT_INITIALIZER(sig_) { (QSignal)(sig_), 0x00U, 0xE0U } +#define QEVT_INITIALIZER(sig_) { \ + .sig = (QSignal)(sig_), \ + .poolNum_ = 0x00U, \ + .refCtr_ = 0xE0U, \ + .filler_ = 0xE0E0E0E0U } + //! @public @memberof QEvt -static inline void QEvt_ctor(QEvt * const me, - enum_t const sig) -{ - me->sig = (QSignal)sig; - me->poolNum_ = 0x00U; - me->refCtr_ = 0xE0U; -} +void QEvt_ctor(QEvt * const me, enum_t const sig); //! @public @memberof QEvt -static inline QEvt * QEvt_init(QEvt * const me, - uint8_t const dummy) -{ - Q_UNUSED_PAR(dummy); - return me; -} +QEvt *QEvt_init(QEvt * const me, uint8_t const dummy); + +//! @private @memberof QEvt +void QEvt_refCtr_inc_(QEvt const* const me); + +//! @private @memberof QEvt +void QEvt_refCtr_dec_(QEvt const* const me); + +#ifndef Q_UNSAFE +//! @private @memberof QEvt +void QEvt_update_(QEvt * const me); + +//! @private @memberof QEvt +bool QEvt_verify_(QEvt const * const me); +#endif typedef QEvt const * QEvtPtr; #define QEVT_DYNAMIC ((uint8_t)0) #define Q_EVT_CAST(class_) ((class_ const *)(e)) -//============================================================================ +//---------------------------------------------------------------------------- // QEP (hierarchical event processor) types typedef uint_fast8_t QState; @@ -157,64 +154,56 @@ typedef struct QMState { } QMState; typedef struct QMTranActTable { - QMState const *target; //!< @private @memberof QMTranActTable - QActionHandler const act[1]; //!< @private @memberof QMTranActTable + QMState const *target; //!< @private @memberof QMTranActTable + QActionHandler const act[1]; //!< @private @memberof QMTranActTable } QMTranActTable; union QAsmAttr { - QStateHandler fun; //!< @private @memberof QAsmAttr - QActionHandler act; //!< @private @memberof QAsmAttr - QXThreadHandler thr; //!< @private @memberof QAsmAttr - QMTranActTable const *tatbl; //!< @private @memberof QAsmAttr - struct QMState const *obj; //!< @private @memberof QAsmAttr - uintptr_t uint; //!< @private @memberof QAsmAttr + QStateHandler fun; //!< @private @memberof QAsmAttr + QActionHandler act; //!< @private @memberof QAsmAttr + QXThreadHandler thr; //!< @private @memberof QAsmAttr + QMTranActTable const *tatbl; //!< @private @memberof QAsmAttr + struct QMState const *obj; //!< @private @memberof QAsmAttr + uintptr_t uint; //!< @private @memberof QAsmAttr }; +#define Q_USER_SIG ((enum_t)4) + #define Q_STATE_CAST(handler_) ((QStateHandler)(handler_)) #define Q_ACTION_CAST(action_) ((QActionHandler)(action_)) #define Q_ACTION_NULL ((QActionHandler)0) -//============================================================================ +//---------------------------------------------------------------------------- //! @class QAsm typedef struct { - struct QAsmVtable const * vptr; //!< @protected @memberof QAsm - union QAsmAttr state; //!< @protected @memberof QAsm - union QAsmAttr temp; //!< @protected @memberof QAsm + struct QAsmVtable const * vptr; //!< @protected @memberof QAsm + union QAsmAttr state; //!< @protected @memberof QAsm + union QAsmAttr temp; //!< @protected @memberof QAsm } QAsm; // All possible values returned from state/action handlers... -// NOTE: The ordering is important for algorithmic correctness. - -// unhandled and needs to "bubble up" +// NOTE: The numerical order is important for algorithmic correctness. #define Q_RET_SUPER ((QState)0U) #define Q_RET_UNHANDLED ((QState)1U) - -// handled and does not need to "bubble up" #define Q_RET_HANDLED ((QState)2U) -#define Q_RET_IGNORED ((QState)3U) +#define Q_RET_TRAN ((QState)3U) +#define Q_RET_TRAN_HIST ((QState)4U) -// entry/exit/initial -#define Q_RET_ENTRY ((QState)4U) -#define Q_RET_EXIT ((QState)5U) -#define Q_RET_TRAN_INIT ((QState)6U) +// used in QHsm only... +#define Q_RET_IGNORED ((QState)5U) -// regular tran./tran.-to-history -#define Q_RET_TRAN ((QState)7U) -#define Q_RET_TRAN_HIST ((QState)8U) +// used in QMsm only... +#define Q_RET_ENTRY ((QState)6U) +#define Q_RET_EXIT ((QState)7U) +#define Q_RET_TRAN_INIT ((QState)8U) -// Reserved signals by the QP-framework. +// Reserved signals by the QP-framework (used in QHsm only) #define Q_EMPTY_SIG ((QSignal)0U) #define Q_ENTRY_SIG ((QSignal)1U) #define Q_EXIT_SIG ((QSignal)2U) #define Q_INIT_SIG ((QSignal)3U) -#define Q_USER_SIG ((enum_t)4) -//! @protected @memberof QAsm -static inline void QAsm_ctor(QAsm * const me) { - me->vptr = (struct QAsmVtable *)0; - me->state.fun = (QStateHandler)0; - me->temp.fun = (QStateHandler)0; -} +// NOTE: QAsm_ctor() not declared because this is a purely abstract class struct QAsmVtable { void (*init)(QAsm * const me, void const * const e, @@ -223,7 +212,7 @@ struct QAsmVtable { uint_fast8_t const qsId); bool (*isIn)(QAsm * const me, QStateHandler const stateHndl); #ifdef Q_SPY - QStateHandler (*getStateHandler)(QAsm * const me); + QStateHandler (*getStateHandler)(QAsm const * const me); #endif // Q_SPY }; @@ -243,7 +232,7 @@ struct QAsmVtable { #define Q_ASM_UPCAST(ptr_) ((QAsm *)(ptr_)) -//============================================================================ +//---------------------------------------------------------------------------- //! @class QHsm //! @extends QAsm typedef struct { @@ -255,14 +244,12 @@ void QHsm_ctor(QHsm * const me, QStateHandler const initial); //! @private @memberof QHsm -void QHsm_init_( - QAsm * const me, +void QHsm_init_(QAsm * const me, void const * const e, uint_fast8_t const qsId); //! @private @memberof QHsm -void QHsm_dispatch_( - QAsm * const me, +void QHsm_dispatch_(QAsm * const me, QEvt const * const e, uint_fast8_t const qsId); @@ -273,7 +260,7 @@ bool QHsm_isIn_( #ifdef Q_SPY //! @private @memberof QHsm -QStateHandler QHsm_getStateHandler_(QAsm * const me); +QStateHandler QHsm_getStateHandler_(QAsm const * const me); #endif // def Q_SPY //! @protected @memberof QHsm @@ -281,9 +268,7 @@ QState QHsm_top(QHsm const * const me, QEvt const * const e); //! @public @memberof QHsm -static inline QStateHandler QHsm_state(QHsm const * const me) { - return me->super.state.fun; -} +QStateHandler QHsm_state(QHsm const * const me); //! @public @memberof QHsm QStateHandler QHsm_childState(QHsm * const me, @@ -303,7 +288,7 @@ QStateHandler QHsm_childState(QHsm * const me, #define Q_HANDLED() ((QState)Q_RET_HANDLED) #define Q_UNHANDLED() ((QState)Q_RET_UNHANDLED) -//============================================================================ +//---------------------------------------------------------------------------- //! @class QMsm //! @extends QAsm typedef struct { @@ -333,17 +318,19 @@ bool QMsm_isIn_( #ifdef Q_SPY //! @public @memberof QMsm -QStateHandler QMsm_getStateHandler_(QAsm * const me); +QStateHandler QMsm_getStateHandler_(QAsm const * const me); #endif // def Q_SPY //! @public @memberof QMsm -static inline QMState const * QMsm_stateObj(QMsm const * const me) { - return me->super.state.obj; -} +QMState const * QMsm_topQMState(void); + +//! @public @memberof QMsm +QMState const * QMsm_stateObj(QMsm const * const me); //! @public @memberof QMsm QMState const * QMsm_childStateObj(QMsm const * const me, - QMState const * const parent); + QMState const * const parentHndl); + //============================================================================ // QEP-macros @@ -374,7 +361,7 @@ QMState const * QMsm_childStateObj(QMsm const * const me, #define QM_SUPER() ((QState)Q_RET_SUPER) #define QM_STATE_NULL ((QMState *)0) -//============================================================================ +//---------------------------------------------------------------------------- // QF (active object framework) types typedef uint16_t QPrioSpec; @@ -399,95 +386,37 @@ typedef uint16_t QPrioSpec; uint_fast8_t QF_LOG2(QPSetBits const bitmask); #endif // ndef QF_LOG2 -//============================================================================ +//---------------------------------------------------------------------------- //! @class QPSet typedef struct { //! @private @memberof QPSet - QPSetBits bits[((QF_MAX_ACTIVE + (8U*sizeof(QPSetBits))) - 1U) - / (8U*sizeof(QPSetBits))]; + QPSetBits bits0; +#if (QF_MAX_ACTIVE > 32U) + //! @private @memberof QPSet + QPSetBits bits1; +#endif } QPSet; //! @public @memberof QPSet -static inline void QPSet_setEmpty(QPSet * const me) { - me->bits[0] = 0U; -#if (QF_MAX_ACTIVE > 32) - me->bits[1] = 0U; -#endif -} +void QPSet_setEmpty(QPSet * const me); //! @public @memberof QPSet -static inline bool QPSet_isEmpty(QPSet const * const me) { -#if (QF_MAX_ACTIVE <= 32U) - return (me->bits[0] == 0U); -#else - return (me->bits[0] == 0U) ? (me->bits[1] == 0U) : false; -#endif -} +bool QPSet_isEmpty(QPSet const * const me); //! @public @memberof QPSet -static inline bool QPSet_notEmpty(QPSet const * const me) { -#if (QF_MAX_ACTIVE <= 32U) - return (me->bits[0] != 0U); -#else - return (me->bits[0] != 0U) ? true : (me->bits[1] != 0U); -#endif -} +bool QPSet_notEmpty(QPSet const * const me); //! @public @memberof QPSet -static inline bool QPSet_hasElement(QPSet const * const me, - uint_fast8_t const n) -{ -#if (QF_MAX_ACTIVE <= 32U) - return (me->bits[0] & ((QPSetBits)1U << (n - 1U))) != 0U; -#else - return (n <= 32U) - ? ((me->bits[0] & ((QPSetBits)1U << (n - 1U))) != 0U) - : ((me->bits[1] & ((QPSetBits)1U << (n - 33U))) != 0U); -#endif -} +bool QPSet_hasElement(QPSet const * const me, uint_fast8_t const n); //! @public @memberof QPSet -static inline void QPSet_insert(QPSet * const me, - uint_fast8_t const n) -{ -#if (QF_MAX_ACTIVE <= 32U) - me->bits[0] = (me->bits[0] | ((QPSetBits)1U << (n - 1U))); -#else - if (n <= 32U) { - me->bits[0] = (me->bits[0] | ((QPSetBits)1U << (n - 1U))); - } - else { - me->bits[1] = (me->bits[1] | ((QPSetBits)1U << (n - 33U))); - } -#endif -} +void QPSet_insert(QPSet * const me, uint_fast8_t const n); //! @public @memberof QPSet -static inline void QPSet_remove(QPSet * const me, - uint_fast8_t const n) -{ -#if (QF_MAX_ACTIVE <= 32U) - me->bits[0] = (me->bits[0] & (QPSetBits)(~((QPSetBits)1U << (n - 1U)))); -#else - if (n <= 32U) { - (me->bits[0] = (me->bits[0] & ~((QPSetBits)1U << (n - 1U)))); - } - else { - (me->bits[1] = (me->bits[1] & ~((QPSetBits)1U << (n - 33U)))); - } -#endif -} +void QPSet_remove(QPSet * const me, uint_fast8_t const n); //! @public @memberof QPSet -static inline uint_fast8_t QPSet_findMax(QPSet const * const me) { -#if (QF_MAX_ACTIVE <= 32U) - return QF_LOG2(me->bits[0]); -#else - return (me->bits[1] != 0U) - ? (QF_LOG2(me->bits[1]) + 32U) - : (QF_LOG2(me->bits[0])); -#endif -} +uint_fast8_t QPSet_findMax(QPSet const * const me); //! @struct QSubscrList typedef struct { @@ -496,7 +425,7 @@ typedef struct { struct QEQueue; // forward declaration -//============================================================================ +//---------------------------------------------------------------------------- //! @class QActive //! @extends QAsm typedef struct QActive { @@ -571,7 +500,13 @@ void QActive_publish_( uint_fast8_t const qsId); //! @static @public @memberof QActive -uint_fast16_t QActive_getQueueMin(uint_fast8_t const prio); +uint16_t QActive_getQueueUse(uint_fast8_t const prio); + +//! @static @public @memberof QActive +uint16_t QActive_getQueueFree(uint_fast8_t const prio); + +//! @static @public @memberof QActive +uint16_t QActive_getQueueMin(uint_fast8_t const prio); //! @protected @memberof QActive void QActive_subscribe(QActive const * const me, @@ -594,14 +529,16 @@ bool QActive_recall(QActive * const me, struct QEQueue * const eq); //! @protected @memberof QActive -uint_fast16_t QActive_flushDeferred(QActive const * const me, +uint16_t QActive_flushDeferred(QActive const * const me, struct QEQueue * const eq, uint_fast16_t const num); //! @private @memberof QActive void QActive_evtLoop_(QActive * const me); -//============================================================================ +#define Q_ACTIVE_UPCAST(ptr_) ((QActive *)(ptr_)) + +//---------------------------------------------------------------------------- //! @class QMActive //! @extends QActive typedef struct { @@ -612,18 +549,20 @@ typedef struct { void QMActive_ctor(QMActive * const me, QStateHandler const initial); -//============================================================================ +//---------------------------------------------------------------------------- +#if (QF_MAX_TICK_RATE > 0U) + //! @class QTimeEvt //! @extends QEvt typedef struct QTimeEvt { - QEvt super; //!< @protected @memberof QTimeEvt - - struct QTimeEvt * volatile next; //!< @private @memberof QTimeEvt - void * act; //!< @private @memberof QTimeEvt - QTimeEvtCtr volatile ctr; //!< @private @memberof QTimeEvt - QTimeEvtCtr interval; //!< @private @memberof QTimeEvt - uint8_t tickRate; //!< @private @memberof QTimeEvt - uint8_t flags; //!< @private @memberof QTimeEvt + QEvt super; //!< @protected @memberof QTimeEvt + + struct QTimeEvt *next; //!< @private @memberof QTimeEvt + void * act; //!< @private @memberof QTimeEvt + QTimeEvtCtr ctr; //!< @private @memberof QTimeEvt + QTimeEvtCtr interval; //!< @private @memberof QTimeEvt + uint8_t tickRate; //!< @private @memberof QTimeEvt + uint8_t flags; //!< @private @memberof QTimeEvt } QTimeEvt; //! @public @memberof QTimeEvt @@ -648,7 +587,7 @@ bool QTimeEvt_rearm(QTimeEvt * const me, bool QTimeEvt_wasDisarmed(QTimeEvt * const me); //! @public @memberof QTimeEvt -QTimeEvtCtr QTimeEvt_currCtr(QTimeEvt const * const me); +QTimeEvtCtr QTimeEvt_getCtr(QTimeEvt const * const me); //! @static @private @memberof QTimeEvt void QTimeEvt_init(void); @@ -674,7 +613,7 @@ QTimeEvt * QTimeEvt_expire_(QTimeEvt * const me, //! @static @public @memberof QTimeEvt bool QTimeEvt_noActive(uint_fast8_t const tickRate); -//============================================================================ +//---------------------------------------------------------------------------- //! @class QTicker //! @extends QActive typedef struct { @@ -686,23 +625,26 @@ void QTicker_ctor(QTicker * const me, uint_fast8_t const tickRate); //! @private @memberof QTicker -void QTicker_init_( - QAsm * const me, +void QTicker_init_(QAsm * const me, void const * const par, uint_fast8_t const qsId); //! @private @memberof QTicker -void QTicker_dispatch_( - QAsm * const me, +void QTicker_dispatch_(QAsm * const me, QEvt const * const e, uint_fast8_t const qsId); //! @private @memberof QTicker -void QTicker_trig_( - QTicker * const me, +void QTicker_trig_(QTicker * const me, void const * const sender); -//============================================================================ +#else + +void QTimeEvt_init(void); // dummy init + +#endif // (QF_MAX_TICK_RATE > 0U) + +//---------------------------------------------------------------------------- // QF base facilities //! @static @public @memberof QF @@ -727,10 +669,14 @@ void QF_onCleanup(void); QActive * next); #endif // def QF_ON_CONTEXT_SW -#define Q_PRIO(prio_, pthre_) ((QPrioSpec)((prio_) | ((pthre_) << 8U))) +static inline QPrioSpec Q_PRIO(uint8_t const prio, uint8_t const pthre) { + // combine the QF prio. preemption-threshld pthre in the upper byte + return (QPrioSpec)((uint32_t)prio | ((uint32_t)pthre << 8U)); +} + #define QF_NO_MARGIN ((uint_fast16_t)0xFFFFU) -//============================================================================ +//---------------------------------------------------------------------------- // QF dynamic memory facilities //! @static @public @memberof QF @@ -740,10 +686,16 @@ void QF_poolInit( uint_fast16_t const evtSize); //! @static @public @memberof QF -uint_fast16_t QF_poolGetMaxBlockSize(void); +uint16_t QF_poolGetMaxBlockSize(void); //! @static @public @memberof QF -uint_fast16_t QF_getPoolMin(uint_fast8_t const poolNum); +uint16_t QF_getPoolUse(uint_fast8_t const poolNum); + +//! @static @public @memberof QF +uint16_t QF_getPoolFree(uint_fast8_t const poolNum); + +//! @static @public @memberof QF +uint16_t QF_getPoolMin(uint_fast8_t const poolNum); //! @static @private @memberof QF QEvt * QF_newX_( @@ -814,7 +766,7 @@ void QF_gcFromISR(QEvt const * const e); #define QF_CRIT_EXIT_NOP() ((void)0) #endif // ndef QF_CRIT_EXIT_NOP -//============================================================================ +//---------------------------------------------------------------------------- // memory protection facilities #ifdef QF_MEM_ISOLATE diff --git a/include/qp_pkg.h b/include/qp_pkg.h index c3dd270c5..b334b49a2 100644 --- a/include/qp_pkg.h +++ b/include/qp_pkg.h @@ -29,61 +29,43 @@ #ifndef QP_PKG_H_ #define QP_PKG_H_ -//============================================================================ -// helper macros... -#define QACTIVE_CAST_(ptr_) ((QActive *)(ptr_)) -#define QP_DIS_UPDATE_(T_, org_) ((T_)~(org_)) -#define QP_DIS_VERIFY_(T_, org_, dis_) ((T_)(org_) == (T_)~(dis_)) +#ifdef QP_IMPL -//============================================================================ -typedef struct { -#if (QF_MAX_EPOOL > 0U) - QF_EPOOL_TYPE_ ePool_[QF_MAX_EPOOL]; //!< @private @memberof QF_Attr - uint8_t maxPool_; //!< @private @memberof QF_Attr -#else - uint8_t dummy; //!< @private @memberof QF_Attr -#endif // (QF_MAX_EPOOL == 0U) -} QF_Attr; - -extern QF_Attr QF_priv_; //!< @static @private @memberof QF - -//! @static @private @memberof QF -void QF_bzero_( - void * const start, - uint_fast16_t const len); +// helper macros... +#define QACTIVE_CAST_(ptr_) ((QActive *)(ptr_)) //! @static @private @memberof QActive extern QActive * QActive_registry_[QF_MAX_ACTIVE + 1U]; -//============================================================================ //! @static @private @memberof QActive extern QSubscrList * QActive_subscrList_; //! @static @private @memberof QActive extern QSignal QActive_maxPubSignal_; -//============================================================================ +#if (QF_MAX_TICK_RATE > 0U) //! @static @private @memberof QTimeEvt extern QTimeEvt QTimeEvt_timeEvtHead_[QF_MAX_TICK_RATE]; -//============================================================================ + // Bitmasks are for the QTimeEvt::flags attribute #define QTE_FLAG_IS_LINKED (1U << 7U) #define QTE_FLAG_WAS_DISARMED (1U << 6U) -//============================================================================ -//! @private @memberof QEvt -static inline void QEvt_refCtr_inc_(QEvt const * const me) { - // NOTE: this function must be called inside a critical section - uint8_t const rc = me->refCtr_ + 1U; - ((QEvt *)me)->refCtr_ = rc; // cast away 'const' -} +#endif // (QF_MAX_TICK_RATE > 0U) + +//---------------------------------------------------------------------------- +typedef struct { +#if (QF_MAX_EPOOL > 0U) + QF_EPOOL_TYPE_ ePool_[QF_MAX_EPOOL]; //!< @private @memberof QF_Attr + uint8_t maxPool_; //!< @private @memberof QF_Attr +#else + uint8_t dummy; //!< @private @memberof QF_Attr +#endif // (QF_MAX_EPOOL == 0U) +} QF_Attr; + +extern QF_Attr QF_priv_; //!< @static @private @memberof QF -//! @private @memberof QEvt -static inline void QEvt_refCtr_dec_(QEvt const * const me) { - // NOTE: this function must be called inside a critical section - uint8_t const rc = me->refCtr_ - 1U; - ((QEvt *)me)->refCtr_ = rc; // cast away 'const' -} +#endif // QP_IMPL #endif // QP_PKG_H_ diff --git a/include/qpc.h b/include/qpc.h index 8e85591fb..d9478bd99 100644 --- a/include/qpc.h +++ b/include/qpc.h @@ -31,18 +31,75 @@ #include "qp_port.h" // QP port from the port directory #include "qsafe.h" // QP Functional Safety (FuSa) Subsystem -#ifdef Q_SPY // software tracing enabled? +#ifdef Q_SPY // software tracing enabled? #include "qs_port.h" // QS/C port from the port directory #else - #include "qs_dummy.h" // QS/C dummy interface (inactive) + #include "qs_dummy.h" // QS/C dummy (inactive) interface #endif #ifndef QP_API_VERSION #define QP_API_VERSION 0 -#endif // #ifndef QP_API_VERSION +#endif // QP API compatibility layer... -//============================================================================ + +#ifdef Q_SIGNAL_SIZE +_Static_assert(Q_SIGNAL_SIZE == 2U, + "Q_SIGNAL_SIZE must be 2 bytes (16-bit signal space)"); +#endif + +// version 8.1.0 ------------------------------------------------------------- +#if (QP_API_VERSION < 810) + +#ifdef Q_SPY + +//! @deprecated instead use: QS_Groups +enum QS_Groups_old { + QS_ALL_RECORDS = QS_GRP_ALL, + QS_SM_RECORDS = QS_GRP_SM, + QS_AO_RECORDS = QS_GRP_AO, + QS_EQ_RECORDS = QS_GRP_EQ, + QS_MP_RECORDS = QS_GRP_MP, + QS_TE_RECORDS = QS_GRP_TE, + QS_QF_RECORDS = QS_GRP_QF, + QS_SC_RECORDS = QS_GRP_SC, + QS_SEM_RECORDS = QS_GRP_SEM, + QS_MTX_RECORDS = QS_GRP_MTX, + QS_U0_RECORDS = QS_GRP_U0, + QS_U1_RECORDS = QS_GRP_U1, + QS_U2_RECORDS = QS_GRP_U2, + QS_U3_RECORDS = QS_GRP_U3, + QS_U4_RECORDS = QS_GRP_U4, + QS_UA_RECORDS = QS_GRP_UA, +}; + +//! @deprecated instead use: QS_LocGroups +enum QS_LocGroups_old { + QS_ALL_IDS = QS_IDS_ALL, + QS_AO_IDS = QS_IDS_AO, + QS_EP_IDS = QS_IDS_EP, + QS_EQ_IDS = QS_IDS_EQ, + QS_AP_IDS = QS_IDS_AP, +}; +#define QS_AO_ID QS_ID_AO +#define QS_EP_ID QS_ID_EP +#define QS_EQ_ID QS_ID_EQ +#define QS_AP_ID QS_ID_AP + +//! @deprecated instead use: enum QS_ObjKind +enum QS_ObjKind_old { + SM_OBJ = QS_OBJ_SM, + AO_OBJ = QS_OBJ_AO, + MP_OBJ = QS_OBJ_MP, + EQ_OBJ = QS_OBJ_EQ, + TE_OBJ = QS_OBJ_TE, + AP_OBJ = QS_OBJ_AP, + SM_AO_OBJ = QS_OBJ_SM_AO, +}; + +#endif // Q_SPY + +// version 8.0.0 ------------------------------------------------------------- #if (QP_API_VERSION < 800) #define QM_SUPER_SUB(host_) error "submachines no longer supported" @@ -69,7 +126,7 @@ typedef char char_t; (QXThread_start((QXThread *)(me_), (prioSpec_), \ (qSto_), (qLen_), (stkSto_), (stkSize_), (par_))) -//! @deprecated Assertion failure handler. +//! @deprecated assertion failure handler. //! Use Q_onError() instead. #define Q_onAssert(module_, id_) Q_onError(module_, id_) @@ -143,5 +200,6 @@ static inline void QF_psInit( #define QF_getQueueMin(prio_) (QActive_getQueueMin((prio_))) #endif // QP_API_VERSION < 800 +#endif // QP_API_VERSION < 810 #endif // QPC_H_ diff --git a/include/qs_dummy.h b/include/qs_dummy.h index 22e33ee11..5a90e950a 100644 --- a/include/qs_dummy.h +++ b/include/qs_dummy.h @@ -7,20 +7,19 @@ // ------------------------ // Modern Embedded Software // -// SPDX-License-Identifier: GPL-3.0-or-later OR LicenseRef-QL-commercial +// SPDX-License-Identifier: LicenseRef-QL-commercial // -// This software is dual-licensed under the terms of the open-source GNU -// General Public License (GPL) or under the terms of one of the closed- -// source Quantum Leaps commercial licenses. +// This software is licensed under the terms of the Quantum Leaps commercial +// licenses. Please contact Quantum Leaps for more information about the +// available licensing options. // -// Redistributions in source code must retain this top-level comment block. -// Plagiarizing this software to sidestep the license obligations is illegal. -// -// NOTE: -// The GPL does NOT permit the incorporation of this code into proprietary -// programs. Please contact Quantum Leaps for commercial licensing options, -// which expressly supersede the GPL and are designed explicitly for -// closed-source distribution. +// RESTRICTIONS +// You may NOT : +// (a) redistribute, encumber, sell, rent, lease, sublicense, or otherwise +// transfer rights in this software, +// (b) remove or alter any trademark, logo, copyright or other proprietary +// notices, legends, symbols or labels present in this software, +// (c) plagiarize this software to sidestep the licensing obligations. // // Quantum Leaps contact information: // @@ -43,9 +42,9 @@ #define QS_GLB_FILTER(rec_) ((void)0) #define QS_LOC_FILTER(qsId_) ((void)0) -#define QS_BEGIN_ID(rec_, qsId_) if (false) { +#define QS_BEGIN_ID(rec_, qsId_) { #define QS_END() } -#define QS_BEGIN_INCRIT(rec_, qsId_) if (false) { +#define QS_BEGIN_INCRIT(rec_, qsId_) { #define QS_END_INCRIT() } #define QS_I8(width_, data_) ((void)0) @@ -84,6 +83,10 @@ #define QS_RX_PUT(b_) ((void)0) #define QS_ONLY(code_) ((void)0) +#define QS_CRIT_STAT +#define QS_CRIT_ENTRY() ((void)0) +#define QS_CRIT_EXIT() ((void)0) + //============================================================================ // interface used only for internal implementation, but not in applications #ifdef QP_IMPL @@ -104,10 +107,6 @@ #define QS_MPS_PRE(size_) ((void)0) #define QS_TEC_PRE(ctr_) ((void)0) - #define QS_CRIT_STAT - #define QS_CRIT_ENTRY() ((void)0) - #define QS_CRIT_EXIT() ((void)0) - #define QS_TR_CRIT_ENTRY() ((void)0) #define QS_TR_CRIT_EXIT() ((void)0) #define QS_TR_ISR_ENTRY(isrnest_, prio_) ((void)0) diff --git a/include/qsafe.h b/include/qsafe.h index 5f4e98eee..3bf6f0d2b 100644 --- a/include/qsafe.h +++ b/include/qsafe.h @@ -52,7 +52,7 @@ static char const Q_this_module_[] = name_; #define Q_ASSERT_INCRIT(id_, expr_) \ - ((expr_) ? ((void)0) : Q_onError(&Q_this_module_[0], (id_))) + ((expr_) ? (void)0 : Q_onError(&Q_this_module_[0], (id_))) #define Q_ERROR_INCRIT(id_) \ (Q_onError(&Q_this_module_[0], (id_))) @@ -60,7 +60,7 @@ #define Q_ASSERT_ID(id_, expr_) do { \ QF_CRIT_STAT \ QF_CRIT_ENTRY(); \ - if (!(expr_)) Q_onError(&Q_this_module_[0], (id_)); \ + ((expr_) ? (void)0 : Q_onError(&Q_this_module_[0], (id_))); \ QF_CRIT_EXIT(); \ } while (false) @@ -76,6 +76,54 @@ } \ } while (false) +//---------------------------------------------------------------------------- +// Duplicate Inverse Storage (DIS) facilities + +#ifdef __cplusplus // C++ source code? + +namespace QP { + + template + static T_ dis_update(T_ const org) { + // calculate the Duplicate Inverse Storage (DIS) for the original + // variable org + return static_cast(~org); + } + //........................................................................ + template + static bool dis_verify(T_ const org, T_ const dis) { + // verify that the Duplicate Inverse Storage (DIS) dis matches + // the original variable org + return dis == static_cast(~org); + } + //........................................................................ + static inline std::uintptr_t dis_ptr_update(void const* const ptr) { + // calculate the Duplicate Inverse Storage (DIS) for the original ptr + return static_cast( + ~reinterpret_cast(ptr)); + } + //........................................................................ + static inline bool dis_ptr_verify( + void const* const ptr, std::uintptr_t const dis) + { + // verify that the Duplicate Inverse Storage (DIS) matches + // the original pointer ptr + return dis == static_cast( + ~reinterpret_cast(ptr)); + } + +} // namespace QP + +#else // C source code + +#define QP_DIS_UPDATE(T_, org_) ((T_)(~(org_))) +#define QP_DIS_VERIFY(T_, org_, dis_) ((org_) == (T_)(~(dis_))) +#define QP_DIS_PTR_UPDATE(org_) ((uintptr_t)(~(uintptr_t)(org_))) +#define QP_DIS_PTR_VERIFY(org_, dis_) \ + ((uintptr_t)(org_) == (uintptr_t)(~(dis_))) + +#endif + // QF-FuSa disabled ========================================================== #else diff --git a/include/qv.h b/include/qv.h index 34b7cbac6..61b172f41 100644 --- a/include/qv.h +++ b/include/qv.h @@ -48,7 +48,7 @@ typedef struct { extern QV_Attr QV_priv_; //! @static @public @memberof QV -void QV_schedDisable(uint_fast8_t const ceiling); +void QV_schedDisable(uint8_t const ceiling); //! @static @public @memberof QV void QV_schedEnable(void); @@ -70,14 +70,17 @@ void QV_onIdle(void); #define QACTIVE_EQUEUE_SIGNAL_(me_) \ QPSet_insert(&QV_priv_.readySet, (uint_fast8_t)(me_)->prio) -// QF event pool customization for QV... -#define QF_EPOOL_TYPE_ QMPool +// QMPool operations +#define QF_EPOOL_TYPE_ QMPool #define QF_EPOOL_INIT_(p_, poolSto_, poolSize_, evtSize_) \ (QMPool_init(&(p_), (poolSto_), (poolSize_), (evtSize_))) -#define QF_EPOOL_EVENT_SIZE_(p_) ((uint_fast16_t)(p_).blockSize) +#define QF_EPOOL_EVENT_SIZE_(p_) ((uint16_t)(p_).blockSize) #define QF_EPOOL_GET_(p_, e_, m_, qsId_) \ ((e_) = (QEvt *)QMPool_get(&(p_), (m_), (qsId_))) #define QF_EPOOL_PUT_(p_, e_, qsId_) (QMPool_put(&(p_), (e_), (qsId_))) +#define QF_EPOOL_USE_(ePool_) (QMPool_getUse(ePool_)) +#define QF_EPOOL_FREE_(ePool_) ((uint16_t)(ePool_)->nFree) +#define QF_EPOOL_MIN_(ePool_) ((uint16_t)(ePool_)->nMin) #endif // QP_IMPL diff --git a/ports/arm-cm/config/qp_config.h b/ports/arm-cm/config/qp_config.h index dfff9e33e..f0850fbec 100644 --- a/ports/arm-cm/config/qp_config.h +++ b/ports/arm-cm/config/qp_config.h @@ -78,20 +78,6 @@ // -//.......................................................................... -// QEP Event Processor (Events) -// Events and state machines. - -// Event signal size (Q_SIGNAL_SIZE) -// <1U=>1 -// <2U=>2 (default) -// <4U=>4 -// Size of the QEvt signal for QEP/QF [bytes] -// Default: 2 -#define Q_SIGNAL_SIZE 2U - -// - //.......................................................................... // QF Framework (Active Objects) // Active Object framework diff --git a/ports/arm-cm/qk/armclang/qp_port.h b/ports/arm-cm/qk/armclang/qp_port.h index 998fd568a..267762b13 100644 --- a/ports/arm-cm/qk/armclang/qp_port.h +++ b/ports/arm-cm/qk/armclang/qp_port.h @@ -36,9 +36,12 @@ // no-return function specifier (C11 Standard) #define Q_NORETURN _Noreturn void +// static assertion (C11 Standard) +#define Q_ASSERT_STATIC(expr_) _Static_assert((expr_), "QP static assert") + // QF configuration for QK -- data members of the QActive class... -// QK event-queue used for AOs +// QActive event queue type #define QACTIVE_EQUEUE_TYPE QEQueue // QActive "thread" type used to store the MPU settings in the AO diff --git a/ports/arm-cm/qk/gnu/qp_port.h b/ports/arm-cm/qk/gnu/qp_port.h index 5976b8740..109d80568 100644 --- a/ports/arm-cm/qk/gnu/qp_port.h +++ b/ports/arm-cm/qk/gnu/qp_port.h @@ -36,6 +36,9 @@ // no-return function specifier (C11 Standard) #define Q_NORETURN _Noreturn void +// static assertion (C11 Standard) +#define Q_ASSERT_STATIC(expr_) _Static_assert((expr_), "QP static assert") + // QF configuration for QK -- data members of the QActive class... // QK event-queue used for AOs @@ -163,8 +166,6 @@ void QF_int_enable_(void); void QF_crit_entry_(void); void QF_crit_exit_(void); -extern int32_t volatile QF_int_lock_nest_; - //============================================================================ // NOTE1: // The critical section policy does not use the "saving and restoring" diff --git a/ports/arm-cm/qk/iar/qp_port.h b/ports/arm-cm/qk/iar/qp_port.h index 186ae18d3..2ba15e45b 100644 --- a/ports/arm-cm/qk/iar/qp_port.h +++ b/ports/arm-cm/qk/iar/qp_port.h @@ -37,6 +37,9 @@ // no-return function specifier (C11 Standard) #define Q_NORETURN _Noreturn void +// static assertion (C11 Standard) +#define Q_ASSERT_STATIC(expr_) _Static_assert((expr_), "QP static assert") + // QF configuration for QK -- data members of the QActive class... // QK event-queue used for AOs diff --git a/ports/arm-cm/qv/armclang/qp_port.h b/ports/arm-cm/qv/armclang/qp_port.h index 9ecac5588..e39b3b54b 100644 --- a/ports/arm-cm/qv/armclang/qp_port.h +++ b/ports/arm-cm/qv/armclang/qp_port.h @@ -36,9 +36,12 @@ // no-return function specifier (C11 Standard) #define Q_NORETURN _Noreturn void +// static assertion (C11 Standard) +#define Q_ASSERT_STATIC(expr_) _Static_assert((expr_), "QP static assert") + // QF configuration for QV -- data members of the QActive class... -// QV event-queue used for AOs +// QActive event queue type #define QACTIVE_EQUEUE_TYPE QEQueue // QActive "thread" type used to store the MPU settings in the AO diff --git a/ports/arm-cm/qv/gnu/qp_port.h b/ports/arm-cm/qv/gnu/qp_port.h index 9ecac5588..e39b3b54b 100644 --- a/ports/arm-cm/qv/gnu/qp_port.h +++ b/ports/arm-cm/qv/gnu/qp_port.h @@ -36,9 +36,12 @@ // no-return function specifier (C11 Standard) #define Q_NORETURN _Noreturn void +// static assertion (C11 Standard) +#define Q_ASSERT_STATIC(expr_) _Static_assert((expr_), "QP static assert") + // QF configuration for QV -- data members of the QActive class... -// QV event-queue used for AOs +// QActive event queue type #define QACTIVE_EQUEUE_TYPE QEQueue // QActive "thread" type used to store the MPU settings in the AO diff --git a/ports/arm-cm/qv/iar/qp_port.h b/ports/arm-cm/qv/iar/qp_port.h index b88376364..75bd07e26 100644 --- a/ports/arm-cm/qv/iar/qp_port.h +++ b/ports/arm-cm/qv/iar/qp_port.h @@ -37,9 +37,12 @@ // no-return function specifier (C11 Standard) #define Q_NORETURN _Noreturn void +// static assertion (C11 Standard) +#define Q_ASSERT_STATIC(expr_) _Static_assert((expr_), "QP static assert") + // QF configuration for QV -- data members of the QActive class... -// QV event-queue used for AOs +// QActive event queue type #define QACTIVE_EQUEUE_TYPE QEQueue // QActive "thread" type used to store the MPU settings in the AO diff --git a/ports/arm-cr/config/qp_config.h b/ports/arm-cr/config/qp_config.h index 946637df6..8b6325504 100644 --- a/ports/arm-cr/config/qp_config.h +++ b/ports/arm-cr/config/qp_config.h @@ -78,20 +78,6 @@ // -//.......................................................................... -// QEP Event Processor (Events) -// Events and state machines. - -// Event signal size (Q_SIGNAL_SIZE) -// <1U=>1 -// <2U=>2 (default) -// <4U=>4 -// Size of the QEvt signal for QEP/QF [bytes] -// Default: 2 -#define Q_SIGNAL_SIZE 2U - -// - //.......................................................................... // QF Framework (Active Objects) // Active Object framework diff --git a/ports/arm-cr/qk/gnu/qp_port.h b/ports/arm-cr/qk/gnu/qp_port.h index 0ef8c5059..7c83d99e5 100644 --- a/ports/arm-cr/qk/gnu/qp_port.h +++ b/ports/arm-cr/qk/gnu/qp_port.h @@ -36,9 +36,12 @@ // no-return function specifier (C11 Standard) #define Q_NORETURN _Noreturn void +// static assertion (C11 Standard) +#define Q_ASSERT_STATIC(expr_) _Static_assert((expr_), "QP static assert") + // QF configuration for QK -- data members of the QActive class... -// QK event-queue used for AOs +// QActive event queue type #define QACTIVE_EQUEUE_TYPE QEQueue // QF interrupt disable/enable, see NOTE2 diff --git a/ports/arm-cr/qk/iar/qp_port.h b/ports/arm-cr/qk/iar/qp_port.h index 976a41720..dd93b80d4 100644 --- a/ports/arm-cr/qk/iar/qp_port.h +++ b/ports/arm-cr/qk/iar/qp_port.h @@ -37,9 +37,12 @@ // no-return function specifier (C11 Standard) #define Q_NORETURN _Noreturn void +// static assertion (C11 Standard) +#define Q_ASSERT_STATIC(expr_) _Static_assert((expr_), "QP static assert") + // QF configuration for QK -- data members of the QActive class... -// QK event-queue used for AOs +// QActive event queue type #define QACTIVE_EQUEUE_TYPE QEQueue // QF interrupt disable/enable, see NOTE2 diff --git a/ports/arm-cr/qk/ti/qp_port.h b/ports/arm-cr/qk/ti/qp_port.h index b193df937..751087b38 100644 --- a/ports/arm-cr/qk/ti/qp_port.h +++ b/ports/arm-cr/qk/ti/qp_port.h @@ -36,9 +36,12 @@ // no-return function specifier (C11 Standard) #define Q_NORETURN _Noreturn void +// static assertion (C11 Standard) +#define Q_ASSERT_STATIC(expr_) _Static_assert((expr_), "QP static assert") + // QF configuration for QK -- data members of the QActive class... -// QK event-queue used for AOs +// QActive event queue type #define QACTIVE_EQUEUE_TYPE QEQueue // QF interrupt disable/enable, see NOTE2 diff --git a/ports/arm-cr/qv/gnu/qp_port.h b/ports/arm-cr/qv/gnu/qp_port.h index 7deffd7b4..01e13af4c 100644 --- a/ports/arm-cr/qv/gnu/qp_port.h +++ b/ports/arm-cr/qv/gnu/qp_port.h @@ -36,9 +36,12 @@ // no-return function specifier (C11 Standard) #define Q_NORETURN _Noreturn void +// static assertion (C11 Standard) +#define Q_ASSERT_STATIC(expr_) _Static_assert((expr_), "QP static assert") + // QF configuration for QV -- data members of the QActive class... -// QV event-queue used for AOs +// QActive event queue type #define QACTIVE_EQUEUE_TYPE QEQueue // QF interrupt disable/enable, see NOTE2 diff --git a/ports/arm-cr/qv/iar/qp_port.h b/ports/arm-cr/qv/iar/qp_port.h index 7048a3029..7530d31cd 100644 --- a/ports/arm-cr/qv/iar/qp_port.h +++ b/ports/arm-cr/qv/iar/qp_port.h @@ -37,10 +37,13 @@ // no-return function specifier (C11 Standard) #define Q_NORETURN _Noreturn void +// static assertion (C11 Standard) +#define Q_ASSERT_STATIC(expr_) _Static_assert((expr_), "QP static assert") + // QF configuration for QV -- data members of the QActive class... -// QV event-queue used for AOs -#define QACTIVE_EQUEUE_TYPE QEQueue +// QActive event queue type +#define QACTIVE_EQUEUE_TYPE QEQueue // QF interrupt disable/enable, see NOTE2 #define QF_INT_DISABLE() __disable_irq() diff --git a/ports/arm-cr/qv/ti/qp_port.h b/ports/arm-cr/qv/ti/qp_port.h index 8a591e723..31cf0720b 100644 --- a/ports/arm-cr/qv/ti/qp_port.h +++ b/ports/arm-cr/qv/ti/qp_port.h @@ -36,9 +36,12 @@ // no-return function specifier (C11 Standard) #define Q_NORETURN _Noreturn void +// static assertion (C11 Standard) +#define Q_ASSERT_STATIC(expr_) _Static_assert((expr_), "QP static assert") + // QF configuration for QV -- data members of the QActive class... -// QV event-queue used for AOs +// QActive event queue type #define QACTIVE_EQUEUE_TYPE QEQueue // QF interrupt disable/enable, see NOTE2 diff --git a/ports/config/qp_config.h b/ports/config/qp_config.h index c6031c8b7..d0d750145 100644 --- a/ports/config/qp_config.h +++ b/ports/config/qp_config.h @@ -78,20 +78,6 @@ // -//.......................................................................... -// QEP Event Processor (Events) -// Events and state machines. - -// Event signal size (Q_SIGNAL_SIZE) -// <1U=>1 -// <2U=>2 (default) -// <4U=>4 -// Size of the QEvt signal for QEP/QF [bytes] -// Default: 2 -#define Q_SIGNAL_SIZE 2U - -// - //.......................................................................... // QF Framework (Active Objects) // Active Object framework diff --git a/ports/embos/qf_port.c b/ports/embos/qf_port.c index 0d60a1fb2..18b4153ed 100644 --- a/ports/embos/qf_port.c +++ b/ports/embos/qf_port.c @@ -75,8 +75,6 @@ static void thread_function(void *pVoid) { // embOS signature } //............................................................................ void QF_init(void) { - QF_bzero_(&QF_priv_, sizeof(QF_priv_)); - QF_bzero_(&QActive_registry_[0], sizeof(QActive_registry_)); QTimeEvt_init(); // initialize QTimeEvts OS_InitKern(); // initialize embOS OS_InitHW(); // initialize the hardware used by embOS @@ -227,7 +225,6 @@ bool QActive_post_(QActive * const me, QEvt const * const e, QS_END_PRE() if (e->poolNum_ != 0U) { // is it a pool event? - Q_ASSERT_INCRIT(205, e->refCtr_ < (2U * QF_MAX_ACTIVE)); QEvt_refCtr_inc_(e); // increment the reference counter } QF_CRIT_EXIT(); @@ -271,7 +268,6 @@ void QActive_postLIFO_(QActive * const me, QEvt const * const e) { QS_END_PRE() if (e->poolNum_ != 0U) { // is it a pool event? - Q_ASSERT_INCRIT(305, e->refCtr_ < (2U * QF_MAX_ACTIVE)); QEvt_refCtr_inc_(e); // increment the reference counter } QF_CRIT_EXIT(); @@ -303,6 +299,24 @@ QEvt const *QActive_get_(QActive * const me) { return e; } +//............................................................................ +//! @static @public @memberof QActive +uint16_t QActive_getQueueUse(uint_fast8_t const prio) { + Q_UNUSED_PAR(prio); + return 0U; // current use level in a queue not supported in this RTOS +} +//............................................................................ +//! @static @public @memberof QActive +uint16_t QActive_getQueueFree(uint_fast8_t const prio) { + Q_UNUSED_PAR(prio); + return 0U; // current use level in a queue not supported in this RTOS +} +//............................................................................ +//! @static @public @memberof QActive +uint16_t QActive_getQueueMin(uint_fast8_t const prio) { + Q_UNUSED_PAR(prio); + return 0U; // minimum free entries in a queue not supported in this RTOS +} //============================================================================ // NOTE1: diff --git a/ports/embos/qp_port.h b/ports/embos/qp_port.h index f74dacf18..2ffa4f959 100644 --- a/ports/embos/qp_port.h +++ b/ports/embos/qp_port.h @@ -70,29 +70,31 @@ enum EmbOS_TaskAttrs { #ifdef QP_IMPL - // embOS-specific scheduler locking, see NOTE3 - #define QF_SCHED_STAT_ - #define QF_SCHED_LOCK_(dummy) do { \ - if (OS_INT_InInterrupt() == (OS_BOOL)0) { \ - OS_TASK_EnterRegion(); \ - } \ - } while (false) - - #define QF_SCHED_UNLOCK_() do { \ - if (OS_INT_InInterrupt() == (OS_BOOL)0) { \ - OS_TASK_LeaveRegion(); \ - } \ - } while (false) - - // native QF event pool customization - #define QF_EPOOL_TYPE_ QMPool - #define QF_EPOOL_INIT_(p_, poolSto_, poolSize_, evtSize_) \ - (QMPool_init(&(p_), (poolSto_), (poolSize_), (evtSize_))) - #define QF_EPOOL_EVENT_SIZE_(p_) ((uint_fast16_t)(p_).blockSize) - #define QF_EPOOL_GET_(p_, e_, m_, qsId_) \ - ((e_) = (QEvt *)QMPool_get(&(p_), (m_), (qsId_))) - #define QF_EPOOL_PUT_(p_, e_, qsId_) \ - (QMPool_put(&(p_), (e_), (qsId_))) +// embOS-specific scheduler locking, see NOTE3 +#define QF_SCHED_STAT_ +#define QF_SCHED_LOCK_(dummy) do { \ + if (OS_INT_InInterrupt() == (OS_BOOL)0) { \ + OS_TASK_EnterRegion(); \ + } \ +} while (false) + +#define QF_SCHED_UNLOCK_() do { \ + if (OS_INT_InInterrupt() == (OS_BOOL)0) { \ + OS_TASK_LeaveRegion(); \ + } \ +} while (false) + +// QMPool operations +#define QF_EPOOL_TYPE_ QMPool +#define QF_EPOOL_INIT_(p_, poolSto_, poolSize_, evtSize_) \ + (QMPool_init(&(p_), (poolSto_), (poolSize_), (evtSize_))) +#define QF_EPOOL_EVENT_SIZE_(p_) ((uint16_t)(p_).blockSize) +#define QF_EPOOL_GET_(p_, e_, m_, qsId_) \ + ((e_) = (QEvt *)QMPool_get(&(p_), (m_), (qsId_))) +#define QF_EPOOL_PUT_(p_, e_, qsId_) (QMPool_put(&(p_), (e_), (qsId_))) +#define QF_EPOOL_USE_(ePool_) (QMPool_getUse(ePool_)) +#define QF_EPOOL_FREE_(ePool_) ((uint16_t)(ePool_)->nFree) +#define QF_EPOOL_MIN_(ePool_) ((uint16_t)(ePool_)->nMin) #endif // QP_IMPL diff --git a/ports/freertos/qf_port.c b/ports/freertos/qf_port.c index f5642dc99..e3b49de82 100644 --- a/ports/freertos/qf_port.c +++ b/ports/freertos/qf_port.c @@ -73,8 +73,6 @@ static void task_function(void *pvParameters); // FreeRTOS task signature //============================================================================ void QF_init(void) { - QF_bzero_(&QF_priv_, sizeof(QF_priv_)); - QF_bzero_(&QActive_registry_[0], sizeof(QActive_registry_)); QTimeEvt_init(); // initialize QTimeEvts } //............................................................................ @@ -205,10 +203,6 @@ void QActive_start(QActive * const me, QF_CRIT_ENTRY(); Q_ASSERT_INCRIT(120, me->thread != (TaskHandle_t)0); QF_CRIT_EXIT(); - -#ifdef Q_UNSAFE - Q_UNUSED_PAR(task); -#endif } //............................................................................ #ifdef QACTIVE_CAN_STOP @@ -275,7 +269,6 @@ bool QActive_post_(QActive * const me, QEvt const * const e, #if (QF_MAX_EPOOL > 0U) if (e->poolNum_ != 0U) { // is it a mutable event? - Q_ASSERT_INCRIT(205, e->refCtr_ < (2U * QF_MAX_ACTIVE)); QEvt_refCtr_inc_(e); // increment the reference counter } #endif // (QF_MAX_EPOOL > 0U) @@ -336,7 +329,6 @@ void QActive_postLIFO_(QActive * const me, QEvt const * const e) { #if (QF_MAX_EPOOL > 0U) if (e->poolNum_ != 0U) { // is it a mutable event? - Q_ASSERT_INCRIT(305, e->refCtr_ < (2U * QF_MAX_ACTIVE)); QEvt_refCtr_inc_(e); // increment the reference counter } #endif // (QF_MAX_EPOOL > 0U) @@ -419,7 +411,6 @@ bool QActive_postFromISR_(QActive * const me, QEvt const * const e, #if (QF_MAX_EPOOL > 0U) if (e->poolNum_ != 0U) { // is it a mutable event? - Q_ASSERT_INCRIT(405, e->refCtr_ < (2U * QF_MAX_ACTIVE)); QEvt_refCtr_inc_(e); // increment the reference counter } #endif // (QF_MAX_EPOOL > 0U) @@ -493,13 +484,6 @@ void QActive_publishFromISR_(QEvt const * const e, // is it a mutable event? if (e->poolNum_ != 0U) { - // NOTE: The reference counter of a mutable event is incremented to - // prevent premature recycling of the event while the multicasting - // is still in progress. At the end of the function, the garbage - // collector step (QF_gc()) decrements the reference counter and - // recycles the event if the counter drops to zero. This covers the - // case when the event was published without any subscribers. - Q_ASSERT_INCRIT(515, e->refCtr_ < (2U * QF_MAX_ACTIVE)); QEvt_refCtr_inc_(e); } @@ -556,6 +540,24 @@ void QActive_publishFromISR_(QEvt const * const e, QF_gcFromISR(e); #endif // (QF_MAX_EPOOL > 0U) } +//............................................................................ +//! @static @public @memberof QActive +uint16_t QActive_getQueueUse(uint_fast8_t const prio) { + Q_UNUSED_PAR(prio); + return 0U; // current use level in a queue not supported in this RTOS +} +//............................................................................ +//! @static @public @memberof QActive +uint16_t QActive_getQueueFree(uint_fast8_t const prio) { + Q_UNUSED_PAR(prio); + return 0U; // current use level in a queue not supported in this RTOS +} +//............................................................................ +//! @static @public @memberof QActive +uint16_t QActive_getQueueMin(uint_fast8_t const prio) { + Q_UNUSED_PAR(prio); + return 0U; // minimum free entries in a queue not supported in this RTOS +} //............................................................................ //! @private @memberof QTimeEvt @@ -666,7 +668,7 @@ QEvt *QF_newXFromISR_(uint_fast16_t const evtSize, #ifdef Q_SPY e = QMPool_getFromISR(&QF_priv_.ePool_[poolNum - 1U], ((margin != QF_NO_MARGIN) ? margin : 0U), - (uint_fast8_t)QS_EP_ID + poolNum); + (uint_fast8_t)QS_ID_EP + poolNum); #else e = QMPool_getFromISR(&QF_priv_.ePool_[poolNum - 1U], ((margin != QF_NO_MARGIN) ? margin : 0U), 0U); @@ -679,7 +681,7 @@ QEvt *QF_newXFromISR_(uint_fast16_t const evtSize, #ifdef Q_SPY uxSavedInterruptStatus = portSET_INTERRUPT_MASK_FROM_ISR(); - QS_BEGIN_PRE(QS_QF_NEW, (uint_fast8_t)QS_EP_ID + poolNum) + QS_BEGIN_PRE(QS_QF_NEW, (uint_fast8_t)QS_ID_EP + poolNum) QS_TIME_PRE(); // timestamp QS_EVS_PRE(evtSize); // the size of the event QS_SIG_PRE(sig); // the signal of the event @@ -696,7 +698,7 @@ QEvt *QF_newXFromISR_(uint_fast16_t const evtSize, Q_ASSERT_INCRIT(720, margin != QF_NO_MARGIN); QS_BEGIN_PRE(QS_QF_NEW_ATTEMPT, - (uint_fast8_t)QS_EP_ID + poolNum) + (uint_fast8_t)QS_ID_EP + poolNum) QS_TIME_PRE(); // timestamp QS_EVS_PRE(evtSize); // the size of the event QS_SIG_PRE(sig); // the signal of the event @@ -722,7 +724,7 @@ void QF_gcFromISR(QEvt const * const e) { if (e->refCtr_ > 1U) { // isn't this the last reference? QS_BEGIN_PRE(QS_QF_GC_ATTEMPT, - (uint_fast8_t)QS_EP_ID + poolNum) + (uint_fast8_t)QS_ID_EP + poolNum) QS_TIME_PRE(); // timestamp QS_SIG_PRE(e->sig); // the signal of the event QS_2U8_PRE(poolNum, e->refCtr_); @@ -736,7 +738,7 @@ void QF_gcFromISR(QEvt const * const e) { else { // this is the last reference to this event, recycle it QS_BEGIN_PRE(QS_QF_GC, - (uint_fast8_t)QS_EP_ID + poolNum) + (uint_fast8_t)QS_ID_EP + poolNum) QS_TIME_PRE(); // timestamp QS_SIG_PRE(e->sig); // the signal of the event QS_2U8_PRE(poolNum, e->refCtr_); @@ -751,7 +753,7 @@ void QF_gcFromISR(QEvt const * const e) { #ifdef Q_SPY // cast 'const' away in (QEvt *)e is OK because it's a pool event QMPool_putFromISR(&QF_priv_.ePool_[poolNum - 1U], (QEvt *)e, - (uint_fast8_t)QS_EP_ID + e->poolNum_); + (uint_fast8_t)QS_ID_EP + e->poolNum_); #else QMPool_putFromISR(&QF_priv_.ePool_[poolNum - 1U], (QEvt *)e, 0U); #endif diff --git a/ports/freertos/qp_port.h b/ports/freertos/qp_port.h index 8367d5881..66baf4279 100644 --- a/ports/freertos/qp_port.h +++ b/ports/freertos/qp_port.h @@ -184,23 +184,26 @@ enum FreeRTOS_TaskAttrs { // interface used only inside QF, but not in applications #ifdef QP_IMPL - #define FREERTOS_TASK_PRIO(qp_prio_) \ - ((UBaseType_t)((qp_prio_) + tskIDLE_PRIORITY)) - - // FreeRTOS scheduler locking for QF_publish_() (task context only) - #define QF_SCHED_STAT_ - #define QF_SCHED_LOCK_(prio_) (vTaskSuspendAll()) - #define QF_SCHED_UNLOCK_() ((void)xTaskResumeAll()) - - // native QF event pool customization - #define QF_EPOOL_TYPE_ QMPool - #define QF_EPOOL_INIT_(p_, poolSto_, poolSize_, evtSize_) \ - (QMPool_init(&(p_), (poolSto_), (poolSize_), (evtSize_))) - #define QF_EPOOL_EVENT_SIZE_(p_) ((uint_fast16_t)(p_).blockSize) - #define QF_EPOOL_GET_(p_, e_, m_, qsId_) \ - ((e_) = (QEvt *)QMPool_get(&(p_), (m_), (qsId_))) - #define QF_EPOOL_PUT_(p_, e_, qsId_) \ - (QMPool_put(&(p_), (e_), (qsId_))) + +#define FREERTOS_TASK_PRIO(qp_prio_) \ + ((UBaseType_t)((qp_prio_) + tskIDLE_PRIORITY)) + +// FreeRTOS scheduler locking for QF_publish_() (task context only) +#define QF_SCHED_STAT_ +#define QF_SCHED_LOCK_(prio_) (vTaskSuspendAll()) +#define QF_SCHED_UNLOCK_() ((void)xTaskResumeAll()) + +// QMPool operations +#define QF_EPOOL_TYPE_ QMPool +#define QF_EPOOL_INIT_(p_, poolSto_, poolSize_, evtSize_) \ + (QMPool_init(&(p_), (poolSto_), (poolSize_), (evtSize_))) +#define QF_EPOOL_EVENT_SIZE_(p_) ((uint16_t)(p_).blockSize) +#define QF_EPOOL_GET_(p_, e_, m_, qsId_) \ + ((e_) = (QEvt *)QMPool_get(&(p_), (m_), (qsId_))) +#define QF_EPOOL_PUT_(p_, e_, qsId_) (QMPool_put(&(p_), (e_), (qsId_))) +#define QF_EPOOL_USE_(ePool_) (QMPool_getUse(ePool_)) +#define QF_EPOOL_FREE_(ePool_) ((uint16_t)(ePool_)->nFree) +#define QF_EPOOL_MIN_(ePool_) ((uint16_t)(ePool_)->nMin) #endif // def QP_IMPL diff --git a/ports/msp430/qutest/qp_port.h b/ports/msp430/qutest/qp_port.h index b60291cb8..4e16d1da1 100644 --- a/ports/msp430/qutest/qp_port.h +++ b/ports/msp430/qutest/qp_port.h @@ -42,7 +42,7 @@ // QF configuration for QK -- data members of the QActive class... // QActive event queue type -#define QACTIVE_EQUEUE_TYPE QEQueue +#define QACTIVE_EQUEUE_TYPE QEQueue // QACTIVE_OS_OBJ_TYPE not used in this port // QACTIVE_THREAD_TYPE not used in this port @@ -71,25 +71,27 @@ #ifdef QP_IMPL - // QUTest scheduler locking - #define QF_SCHED_STAT_ QSchedStatus lockStat_; - #define QF_SCHED_LOCK_(ceil_) (lockStat_ = QF_schedLock((ceil_))) - #define QF_SCHED_UNLOCK_() (QF_schedUnlock(lockStat_)) +// QUTest scheduler locking +#define QF_SCHED_STAT_ QSchedStatus lockStat_; +#define QF_SCHED_LOCK_(ceil_) (lockStat_ = QF_schedLock((ceil_))) +#define QF_SCHED_UNLOCK_() (QF_schedUnlock(lockStat_)) - // native QEQueue operations - #define QACTIVE_EQUEUE_WAIT_(me_) ((void)0) - #define QACTIVE_EQUEUE_SIGNAL_(me_) \ - QPSet_insert(&QS_tstPriv_.readySet, (uint_fast8_t)(me_)->prio) +// native QEQueue operations +#define QACTIVE_EQUEUE_WAIT_(me_) ((void)0) +#define QACTIVE_EQUEUE_SIGNAL_(me_) \ + QPSet_insert(&QS_tstPriv_.readySet, (uint_fast8_t)(me_)->prio) - // native QMPool operations - #define QF_EPOOL_TYPE_ QMPool - #define QF_EPOOL_INIT_(p_, poolSto_, poolSize_, evtSize_) \ +// QMPool operations +#define QF_EPOOL_TYPE_ QMPool +#define QF_EPOOL_INIT_(p_, poolSto_, poolSize_, evtSize_) \ (QMPool_init(&(p_), (poolSto_), (poolSize_), (evtSize_))) - #define QF_EPOOL_EVENT_SIZE_(p_) ((uint_fast16_t)(p_).blockSize) - #define QF_EPOOL_GET_(p_, e_, m_, qsId_) \ +#define QF_EPOOL_EVENT_SIZE_(p_) ((uint16_t)(p_).blockSize) +#define QF_EPOOL_GET_(p_, e_, m_, qsId_) \ ((e_) = (QEvt *)QMPool_get(&(p_), (m_), (qsId_))) - #define QF_EPOOL_PUT_(p_, e_, qsId_) \ - (QMPool_put(&(p_), (e_), (qsId_))) +#define QF_EPOOL_PUT_(p_, e_, qsId_) (QMPool_put(&(p_), (e_), (qsId_))) +#define QF_EPOOL_USE_(ePool_) (QMPool_getUse(ePool_)) +#define QF_EPOOL_FREE_(ePool_) ((uint16_t)(ePool_)->nFree) +#define QF_EPOOL_MIN_(ePool_) ((uint16_t)(ePool_)->nMin) #endif // QP_IMPL diff --git a/ports/posix-fd/CMakeLists.txt b/ports/posix-fd/CMakeLists.txt new file mode 100644 index 000000000..079e3b68b --- /dev/null +++ b/ports/posix-fd/CMakeLists.txt @@ -0,0 +1,6 @@ +# ports/posix-qv +target_include_directories(qpc PUBLIC .) +target_sources(qpc PRIVATE + qf_port.c + $<$:${CMAKE_CURRENT_SOURCE_DIR}/qs_port.c> +) \ No newline at end of file diff --git a/ports/posix-fd/README.md b/ports/posix-fd/README.md new file mode 100644 index 000000000..431606466 --- /dev/null +++ b/ports/posix-fd/README.md @@ -0,0 +1,41 @@ +# POSIX-FD (pollfd-based event-loop) + +This port is **NOT** a real-time kernel. + +See other POSIX ports for a better effort at a real-time kernel. + +This port can be used to run a QP/C application within the context of a +POSIX system where events are processed and mediated via a pollfd event-loop. + +This port provides a QF_run() function (like other QP/C kernels) based on +looping around and polling on a file-descriptor (read end of a pipe) and +timing out for the next tick (QF_setTickRate). + +Thus, this port is completely single-threaded (as opposed to POSIX-QV, which +uses a background pthread for clock ticks). The critical sections enter/exit +logic only guards against nesting of critical sections, but does not +protect against concurrent access. + +The main benefit of this port is that event processing of the QP/C +application can be integrated within other frameworks that rely on polling +file-descriptors, as is common in POSIX software. To that end, this port +exposes internal functions that encapsulate the constituent parts of a +main event loop: + + - QF_preRun_: Initializes the main loop (global) variables. + - QF_getReadyFD_: Gets the file-descriptor to poll, indicating QP/C has events to process. + - QF_getExternalFDs_: Gets the list of file-descriptors added by the QP/C application. + - QF_updateNextTick_: Advances the time at which the next tick will occur. + - QF_getNextTimeoutMS_: Gets the number of milliseconds until the next tick (or -1 for infinite timeout). + - QF_onReadySignal_: Processes the next queued event in the QP/C kernel. + - QF_onExternalFDs_: Processes the external events (driven by file descriptors) added by the QP/C application. + - QF_postRun_: Finalizes the main loop, i.e., free resources. + +See QF_run for a basic event-loop that uses the above parts. +The intent is for an external event-loop system, based on polling, to be +able to integrate those parts in a manner appropriate for that framework. + +As a single-threaded port, it is only safe to post or publish events to +QP/C while neither of the above functions (plus QF_onClockTick) are +running. + diff --git a/ports/posix-fd/qf_port.c b/ports/posix-fd/qf_port.c new file mode 100644 index 000000000..0aff2b256 --- /dev/null +++ b/ports/posix-fd/qf_port.c @@ -0,0 +1,498 @@ +//============================================================================ +// QP/C Real-Time Event Framework (RTEF) +// +// Copyright (C) 2005 Quantum Leaps, LLC. All rights reserved. +// +// Q u a n t u m L e a P s +// ------------------------ +// Modern Embedded Software +// +// SPDX-License-Identifier: GPL-3.0-or-later OR LicenseRef-QL-commercial +// +// This software is dual-licensed under the terms of the open-source GNU +// General Public License (GPL) or under the terms of one of the closed- +// source Quantum Leaps commercial licenses. +// +// Redistributions in source code must retain this top-level comment block. +// Plagiarizing this software to sidestep the license obligations is illegal. +// +// NOTE: +// The GPL does NOT permit the incorporation of this code into proprietary +// programs. Please contact Quantum Leaps for commercial licensing options, +// which expressly supersede the GPL and are designed explicitly for +// closed-source distribution. +// +// Quantum Leaps contact information: +// +// +//============================================================================ + +// expose features from the 2008 POSIX standard (IEEE Standard 1003.1-2008) +#define _POSIX_C_SOURCE 200809L + +#define QP_IMPL // this is QP implementation +#include "qp_port.h" // QP port +#include "qp_pkg.h" // QP package-scope interface +#include "qsafe.h" // QP Functional Safety (FuSa) Subsystem +#ifdef Q_SPY // QS software tracing enabled? + #include "qs_port.h" // QS port + #include "qs_pkg.h" // QS package-scope internal interface +#else + #include "qs_dummy.h" // disable the QS software tracing +#endif // Q_SPY + +#include // for memcpy() and memset() +#include +#include +#include +#include +#include +#include +#include + +Q_DEFINE_THIS_MODULE("qf_port"); + +// Local objects ============================================================= + +static bool l_isRunning; // flag indicating when QF is running +static struct timespec l_tick; // structure for the clock tick +static struct timespec l_nextTick; // next clock tick to wake-up for + +#define NSEC_PER_SEC 1000000000L +#define NSEC_PER_MSEC 1000000L +#define DEFAULT_TICKS_PER_SEC 100 + +//............................................................................ +static void sigIntHandler(int dummy); // prototype +static void sigIntHandler(int dummy) { + Q_UNUSED_PAR(dummy); + QF_onCleanup(); + exit(-1); +} + +// Global objects ============================================================ +QPSet QF_readySet_; +int QF_readyPipeWrite_; // Pipe to signal events +int QF_readyPipeRead_; // Pipe to signal events + +#if QF_POSIX_MAX_EXTERNAL_FDS > 0 +typedef struct _QF_ExternalPollFDCallback { + void (*callback)(int, void*); + void* user_data; +} QF_ExternalPollFDCallback; + +static struct pollfd l_externalPollFDs[QF_POSIX_MAX_EXTERNAL_FDS]; +static QF_ExternalPollFDCallback l_externalPollFDCallbacks[QF_POSIX_MAX_EXTERNAL_FDS]; +static size_t l_externalPollFDsLength; +#endif // QF_POSIX_MAX_EXTERNAL_FDS > 0 + +//============================================================================ +// QF functions + +// NOTE: Only check that nesting of critical sections never occurs +// (see QF_enterCriticalSection_()/QF_leaveCriticalSection_() +static int l_critSectNest; // critical section nesting up-down counter + +//............................................................................ +void QF_signalReadyOnPipe_(void) { + const uint8_t write_val = 0xA5; // Magic, ignored. + if (write(QF_readyPipeWrite_, &write_val, 1) < 0) { + Q_ERROR(); + } +} +void QF_clearReadyPipe_(void) { + ssize_t read_sz = 1; + while (read_sz > 0) { + uint8_t read_val = 0; + read_sz = read(QF_readyPipeRead_, &read_val, 1); + } +} + +//............................................................................ +void QF_enterCriticalSection_(void) { + Q_ASSERT_INCRIT(100, l_critSectNest == 0); // NO nesting of crit.sect! + ++l_critSectNest; +} +//............................................................................ +void QF_leaveCriticalSection_(void) { + Q_ASSERT_INCRIT(200, l_critSectNest == 1); // crit.sect. must ballace! + --l_critSectNest; +} + +#if QF_POSIX_MAX_EXTERNAL_FDS > 0 +//............................................................................ +void QF_addExternalFD(int fd, short fd_events, void (*callback)(int, void*), void* user_data) { + QF_CRIT_STAT + QF_CRIT_ENTRY(); + Q_ASSERT_INCRIT(410, l_externalPollFDsLength < QF_POSIX_MAX_EXTERNAL_FDS); // Must have available capacity + Q_ASSERT_INCRIT(411, callback); // Must have a valid callback + Q_ASSERT_INCRIT(412, fd >= 0); // Must have a valid file descriptor + Q_ASSERT_INCRIT(413, fd_events != 0); // Must have an events flag + l_externalPollFDs[l_externalPollFDsLength].fd = fd; + l_externalPollFDs[l_externalPollFDsLength].events = fd_events; + l_externalPollFDs[l_externalPollFDsLength].revents = 0; + l_externalPollFDCallbacks[l_externalPollFDsLength].callback = callback; + l_externalPollFDCallbacks[l_externalPollFDsLength].user_data = user_data; + ++l_externalPollFDsLength; + QF_CRIT_EXIT(); +} + +//............................................................................ +void QF_removeExternalFD(int fd) { + QF_CRIT_STAT + QF_CRIT_ENTRY(); + Q_ASSERT_INCRIT(420, fd >= 0); // Must have a valid file descriptor + // Takes a bit of time, but remove-fd should not be common. + for (size_t i = 0; i < l_externalPollFDsLength; ++i) { + if (l_externalPollFDs[i].fd != fd) { + continue; + } + // Leave a hole for this entry, will be cleaned up on next QF_getExternalFDs_ call. + // We do not move entries [0, len) that have been in the last pollfd set and might still + // be pending. We only reorganize (compress) entries when creating a new pollfd set. + l_externalPollFDs[i].events = 0; + l_externalPollFDCallbacks[i] = (QF_ExternalPollFDCallback){}; + break; + } + QF_CRIT_EXIT(); +} + +//............................................................................ +size_t QF_getExternalFDs_(struct pollfd* pfds, size_t pfds_len) { + QF_CRIT_STAT + QF_CRIT_ENTRY(); + Q_ASSERT_INCRIT(430, pfds_len >= l_externalPollFDsLength); // Must have enough capacity + size_t result = 0; + while (result < l_externalPollFDsLength) { + if ((l_externalPollFDs[result].fd < 0) || (l_externalPollFDs[result].events == 0) || + !l_externalPollFDCallbacks[result].callback) { + // Replace with last entry + --l_externalPollFDsLength; + l_externalPollFDs[result] = l_externalPollFDs[l_externalPollFDsLength]; + l_externalPollFDCallbacks[result] = l_externalPollFDCallbacks[l_externalPollFDsLength]; + // Continue without incrementing result. + continue; + } + pfds[result] = l_externalPollFDs[result]; + ++result; + } + QF_CRIT_EXIT(); + return result; +} + +//............................................................................ +void QF_onExternalFDs_(struct pollfd* pfds, size_t pfds_len) { + QF_CRIT_STAT + QF_CRIT_ENTRY(); + Q_ASSERT_INCRIT(440, pfds_len <= QF_POSIX_MAX_EXTERNAL_FDS); // Must have correct count + for (size_t i = 0; i < pfds_len; ++i) { + if ((pfds[i].revents & pfds[i].events) == 0) { + continue; + } + pfds[i].revents = 0; + if (l_externalPollFDCallbacks[i].callback) { + // Copy before exiting critical section to call user code. + const QF_ExternalPollFDCallback tmp_cb_entry = l_externalPollFDCallbacks[i]; + QF_CRIT_EXIT(); + (*tmp_cb_entry.callback)(pfds[i].fd, tmp_cb_entry.user_data); + QF_CRIT_ENTRY(); + } + } + QF_CRIT_EXIT(); +} +#endif // QF_POSIX_MAX_EXTERNAL_FDS > 0 + +//............................................................................ +void QF_init(void) { + // init the pipe that signals events + int pipe_fds[2] = {-1, -1}; + if (pipe(pipe_fds) != 0) { + Q_ERROR(); + } + QF_readyPipeRead_ = pipe_fds[0]; + QF_readyPipeWrite_ = pipe_fds[1]; + + int flags = fcntl(QF_readyPipeRead_, F_GETFL, 0); + if (flags == -1) { + Q_ERROR(); + } + if (fcntl(QF_readyPipeRead_, F_SETFL, flags | O_NONBLOCK | FD_CLOEXEC) == -1) { + Q_ERROR(); + } + + flags = fcntl(QF_readyPipeWrite_, F_GETFL, 0); + if (flags == -1) { + Q_ERROR(); + } + if (fcntl(QF_readyPipeWrite_, F_SETFL, flags | FD_CLOEXEC) == -1) { + Q_ERROR(); + } + + l_critSectNest = 0; + QPSet_setEmpty(&QF_readySet_); + +#if QF_POSIX_MAX_EXTERNAL_FDS > 0 + // Initialize external pollfd freelist. + memset(&l_externalPollFDs, 0, sizeof(l_externalPollFDs)); + memset(&l_externalPollFDCallbacks, 0, sizeof(l_externalPollFDCallbacks)); + l_externalPollFDsLength = 0; +#endif // QF_POSIX_MAX_EXTERNAL_FDS > 0 + + QTimeEvt_init(); // initialize QTimeEvts + + l_tick.tv_sec = 0; + l_tick.tv_nsec = NSEC_PER_SEC / DEFAULT_TICKS_PER_SEC; // default rate + + // install the SIGINT (Ctrl-C) signal handler + struct sigaction sig_act; + memset(&sig_act, 0, sizeof(sig_act)); + sig_act.sa_handler = &sigIntHandler; + sigaction(SIGINT, &sig_act, NULL); +} + +//............................................................................ +void QF_preRun_(void) { + l_isRunning = true; // QF is running + + QF_onStartup(); // application-specific startup callback + + // produce the QS_QF_RUN trace record + QS_BEGIN_PRE_(QS_QF_RUN, 0U) + QS_END_PRE_() + + if ((l_tick.tv_sec != 0) || (l_tick.tv_nsec != 0)) { + // get the absolute monotonic time for no-drift sleeping + clock_gettime(CLOCK_MONOTONIC, &l_nextTick); + + // round down nanoseconds to the nearest configured period + l_nextTick.tv_nsec -= l_nextTick.tv_nsec % l_tick.tv_nsec; + } +} + +//............................................................................ +int QF_getReadyFD_(void) { + return QF_readyPipeRead_; +} + +//............................................................................ +void QF_updateNextTick_(void) { + if ((l_tick.tv_sec == 0) && (l_tick.tv_nsec == 0)) { + return; + } + // advance to the next tick (absolute time) + l_nextTick.tv_nsec += l_tick.tv_nsec; + if (l_nextTick.tv_nsec >= NSEC_PER_SEC) { + // Can only overflow by one second, since tick rate is a fraction of a second. + l_nextTick.tv_nsec -= NSEC_PER_SEC; + l_nextTick.tv_sec += 1; + } +} + +//............................................................................ +int QF_getNextTimeoutMS_(struct timespec* timeout_spec) { + if ((l_tick.tv_sec == 0) && (l_tick.tv_nsec == 0)) { + return -1; // Infinite. + } + int poll_timeout_ms = -1; + struct timespec now_tick; + clock_gettime(CLOCK_MONOTONIC, &now_tick); + if (l_nextTick.tv_sec < now_tick.tv_sec) { + poll_timeout_ms = 0; + if (timeout_spec) { + timeout_spec->tv_sec = 0; + timeout_spec->tv_nsec = 0; + } + } else if (now_tick.tv_sec == l_nextTick.tv_sec) { + if (timeout_spec) { + timeout_spec->tv_sec = 0; + } + if (l_nextTick.tv_nsec <= now_tick.tv_nsec) { + poll_timeout_ms = 0; + if (timeout_spec) { + timeout_spec->tv_nsec = 0; + } + } else { + poll_timeout_ms = (l_nextTick.tv_nsec - now_tick.tv_nsec) / NSEC_PER_MSEC; + if (timeout_spec) { + timeout_spec->tv_nsec = l_nextTick.tv_nsec - now_tick.tv_nsec; + } + } + } else { + const int32_t poll_timeout_nsec = NSEC_PER_SEC + (l_nextTick.tv_nsec - now_tick.tv_nsec); + poll_timeout_ms = poll_timeout_nsec / NSEC_PER_MSEC; + if (timeout_spec) { + timeout_spec->tv_sec = poll_timeout_nsec / NSEC_PER_SEC; + timeout_spec->tv_nsec = poll_timeout_nsec % NSEC_PER_SEC; + } + } + return poll_timeout_ms; +} + +//............................................................................ +void QF_onReadySignal_(void) { + // Flush out the ready pipe, since we are responding now. + QF_clearReadyPipe_(); + + if (QPSet_isEmpty(&QF_readySet_)) { + return; + } + + QF_CRIT_ENTRY(); + + // find the maximum priority AO ready to run + uint_fast8_t p = QPSet_findMax(&QF_readySet_); + QActive *a = QActive_registry_[p]; + + // the active object 'a' must still be registered in QF + // (e.g., it must not be stopped) + Q_ASSERT_INCRIT(320, a != (QActive *)0); + QF_CRIT_EXIT(); + + QEvt const *e = QActive_get_(a); + QASM_DISPATCH(&a->super, e, a->prio); // dispatch to the HSM + QF_gc(e); + + QF_CRIT_ENTRY(); + if (a->eQueue.frontEvt == (QEvt *)0) { // empty queue? + QPSet_remove(&QF_readySet_, p); + } + QF_CRIT_EXIT(); + + // Re-trigger ready pipe, in case we have more events. + // We do not use a nested loop since we could have a tick to process too. + QF_signalReadyOnPipe_(); +} + +//............................................................................ +void QF_postRun_(void) { + QF_onCleanup(); // cleanup callback + QS_EXIT(); // cleanup the QSPY connection + + close(QF_readyPipeRead_); // cleanup the pipe + close(QF_readyPipeWrite_); // cleanup the pipe +} + +//............................................................................ +int QF_run(void) { + QF_preRun_(); + + while (l_isRunning) { + struct pollfd pfds[1 + QF_POSIX_MAX_EXTERNAL_FDS]; + pfds[0] = (struct pollfd){.fd = QF_getReadyFD_(), .events = POLLIN, .revents = 0}; +#if QF_POSIX_MAX_EXTERNAL_FDS > 0 + const size_t ext_pfds_count = QF_getExternalFDs_(&pfds[1], QF_POSIX_MAX_EXTERNAL_FDS); +#else // QF_POSIX_MAX_EXTERNAL_FDS > 0 + const size_t ext_pfds_count = 0; +#endif // QF_POSIX_MAX_EXTERNAL_FDS > 0 +#if defined(_GNU_SOURCE) + struct timespec poll_timeout_spec; + int poll_timeout_ms = QF_getNextTimeoutMS_(&poll_timeout_spec); + const struct timespec& poll_timeout_spec_ptr = (poll_timeout_ms < 0 ? NULL : &poll_timeout_spec); + int poll_result = ppoll(pfds, 1 + ext_pfds_count, poll_timeout_spec_ptr, NULL); +#else // defined(_GNU_SOURCE) + int poll_timeout_ms = QF_getNextTimeoutMS_(NULL); + int poll_result = poll(pfds, 1 + ext_pfds_count, poll_timeout_ms); +#endif // defined(_GNU_SOURCE) + if (poll_result < 0) { + Q_ERROR(); + } + + // Handle timeout / clock-tick. + if (poll_result == 0) { + QF_onClockTick(); // must call QTIMEEVT_TICK_X() + QF_updateNextTick_(); + } + + // Handle internal QP/C events if any + if ((pfds[0].revents & POLLIN) != 0) { + QF_onReadySignal_(); + } + +#if QF_POSIX_MAX_EXTERNAL_FDS > 0 + // Handle external events if any from pollfds + QF_onExternalFDs_(&pfds[1], ext_pfds_count); +#endif // QF_POSIX_MAX_EXTERNAL_FDS > 0 + } + + QF_postRun_(); + + return 0; // return success +} +//............................................................................ +void QF_stop(void) { + l_isRunning = false; // terminate the main event-loop + + // unblock the event-loop so it can terminate + QPSet_insert(&QF_readySet_, 1U); + QF_signalReadyOnPipe_(); +} +//............................................................................ +void QF_setTickRate(uint32_t ticksPerSec, int tickPrio) { + Q_UNUSED_PAR(tickPrio); + +#if !defined(_GNU_SOURCE) + if (ticksPerSec > NSEC_PER_SEC / NSEC_PER_MSEC) { + Q_ERROR(); + } +#endif // !defined(_GNU_SOURCE) + if (ticksPerSec != 0U) { + l_tick.tv_nsec = NSEC_PER_SEC / ticksPerSec; + } + else { + l_tick.tv_nsec = 0U; // means NO system clock tick + } +} + +// QActive functions ========================================================= + +void QActive_start(QActive * const me, + QPrioSpec const prioSpec, + QEvtPtr * const qSto, uint_fast16_t const qLen, + void * const stkSto, uint_fast16_t const stkSize, + void const * const par) +{ + Q_UNUSED_PAR(stkSto); + Q_UNUSED_PAR(stkSize); + + // no per-AO stack needed for this port + QF_CRIT_STAT + QF_CRIT_ENTRY(); + Q_REQUIRE_INCRIT(800, stkSto == (void *)0); + QF_CRIT_EXIT(); + + me->prio = (uint8_t)(prioSpec & 0xFFU); // QF-priority of the AO + me->pthre = 0U; // preemption-threshold (not used in this port) + QActive_register_(me); // register this AO + + QEQueue_init(&me->eQueue, qSto, qLen); + + // top-most initial tran. (virtual call) + (*me->super.vptr->init)(&me->super, par, me->prio); + QS_FLUSH(); // flush the QS trace buffer to the host +} + +//............................................................................ +#ifdef QACTIVE_CAN_STOP +void QActive_stop(QActive * const me) { + if (QActive_subscrList_ != (QSubscrList *)0) { + QActive_unsubscribeAll(me); // unsubscribe from all events + } + + // make sure the AO is no longer in "ready set" + QF_CRIT_STAT + QF_CRIT_ENTRY(); + QPSet_remove(&QF_readySet_, me->prio); + QF_CRIT_EXIT(); + + QActive_unregister_(me); +} +#endif +//............................................................................ +void QActive_setAttr(QActive *const me, uint32_t attr1, void const *attr2) { + Q_UNUSED_PAR(me); + Q_UNUSED_PAR(attr1); + Q_UNUSED_PAR(attr2); + Q_ERROR_INCRIT(900); // should not be called in this QP port +} + +//============================================================================ diff --git a/ports/posix-fd/qp_port.h b/ports/posix-fd/qp_port.h new file mode 100644 index 000000000..e2b6784d6 --- /dev/null +++ b/ports/posix-fd/qp_port.h @@ -0,0 +1,206 @@ +//============================================================================ +// QP/C Real-Time Event Framework (RTEF) +// +// Copyright (C) 2005 Quantum Leaps, LLC. All rights reserved. +// +// Q u a n t u m L e a P s +// ------------------------ +// Modern Embedded Software +// +// SPDX-License-Identifier: GPL-3.0-or-later OR LicenseRef-QL-commercial +// +// This software is dual-licensed under the terms of the open-source GNU +// General Public License (GPL) or under the terms of one of the closed- +// source Quantum Leaps commercial licenses. +// +// Redistributions in source code must retain this top-level comment block. +// Plagiarizing this software to sidestep the license obligations is illegal. +// +// NOTE: +// The GPL does NOT permit the incorporation of this code into proprietary +// programs. Please contact Quantum Leaps for commercial licensing options, +// which expressly supersede the GPL and are designed explicitly for +// closed-source distribution. +// +// Quantum Leaps contact information: +// +// +//============================================================================ + +// This QP/C port is NOT a real-time kernel. +// See other POSIX ports for a better effort at a real-time kernel. + +// This port can be used to run a QP/C application within the context of a +// POSIX system where events are processed and mediated via a pollfd event-loop. +// +// This port provides a QF_run() function (like other QP/C kernels) based on +// looping around and polling on a file-descriptor (read end of a pipe) and +// timing out for the next tick (QF_setTickRate). +// +// Thus, this port is completely single-threaded (as opposed to POSIX-QV, which +// uses a background pthread for clock ticks). The critical sections enter/exit +// logic only guards against nesting of critical sections, but does not +// protect against concurrent access. +// +// The main benefit of this port is that event processing of the QP/C +// application can be integrated within other frameworks that rely on polling +// file-descriptors, as is common in POSIX software. To that end, this port +// exposes internal functions that encapsulate the constituent parts of a +// main event loop: +// - QF_preRun_: Initializes the main loop (global) variables. +// - QF_getReadyFD_: Gets the file-descriptor to poll, indicating QP/C has events to process. +// - QF_getExternalFDs_: Gets the list of file-descriptors added by the QP/C application. +// - QF_updateNextTick_: Advances the time at which the next tick will occur. +// - QF_getNextTimeoutMS_: Gets the number of milliseconds until the next tick (or -1 for infinite timeout). +// - QF_onReadySignal_: Processes the next queued event in the QP/C kernel. +// - QF_onExternalFDs_: Processes the external events (driven by file descriptors) added by the QP/C application. +// - QF_postRun_: Finalizes the main loop, i.e., free resources. +// See QF_run for a basic event-loop that uses the above parts. +// The intent is for an external event-loop system, based on polling, to be +// able to integrate those parts in a manner appropriate for that framework. +// +// As a single-threaded port, it is only safe to post or publish events to +// QP/C from an external source while neither of the above functions (and QF_onClockTick) +// are running. This means that if other sources of events are used (e.g., /dev/input0), +// they must either be included in the set of file-descriptors being polled +// such that their QP/C events can be posted in the same thread when not +// being inside a call to QF_onReadySignal_ or QF_onClockTick, or if the events +// have to be received in a separate thread, then some mutex synchronization +// would need to mutually exclude those functions. +// + +#ifndef QP_PORT_H_ +#define QP_PORT_H_ + +#include // size_t type. WG14/N1570 C11 Standard +#include // Exact-width types. WG14/N843 C99 Standard +#include // Boolean type. WG14/N843 C99 Standard +#include // timespec type. WG14/N1570 C11 Standard +#include // struct pollfd POSIX.1-2008 + +#include "qp_config.h" // QP configuration from the application + +// Capacity for external file-descriptor events from the system. +// See `QF_addExternalFD`. +#ifndef QF_POSIX_MAX_EXTERNAL_FDS +#define QF_POSIX_MAX_EXTERNAL_FDS 0 +#endif + +// no-return function specifier (C11 Standard) +#define Q_NORETURN _Noreturn void + +// static assertion (C11 Standard) +#define Q_ASSERT_STATIC(expr_) _Static_assert((expr_), "QP static assert") + +// QActive event queue and thread types for POSIX-FD +#define QACTIVE_EQUEUE_TYPE QEQueue +//QACTIVE_OS_OBJ_TYPE not used in this port +//QACTIVE_THREAD_TYPE not used in this port + +// QF critical section for POSIX-FD, see NOTE1 +#define QF_CRIT_STAT +#define QF_CRIT_ENTRY() QF_enterCriticalSection_() +#define QF_CRIT_EXIT() QF_leaveCriticalSection_() +#define QF_CRIT_EST() QF_enterCriticalSection_() + +// QF_LOG2 not defined -- use the internal LOG2() implementation + +// internal functions for critical section management +void QF_enterCriticalSection_(void); +void QF_leaveCriticalSection_(void); + +// Set clock tick rate +// (NOTE ticksPerSec==0 disables the poll timeout) +// (NOTE ticksPerSec is limited to 1000 due to poll timeout resolution in milliseconds on POSIX, +// or it's limited by kernel capabilities on Linux's ppoll timeout resolution) +// (NOTE tickPrio is ignored in this port, no background ticker thread, kept for compat.) +void QF_setTickRate(uint32_t ticksPerSec, int tickPrio); + +// clock tick callback (NOTE not called when ticksPerSec==0) +// must call QTIMEEVT_TICK_X() +void QF_onClockTick(void); + +// Implementation functions that break-down the "QF_run" loop into steps +// Useful to integrate into external pollfd-based event-loop. +// See QF_run in qf_port.c for the appropriate usage pattern. +void QF_preRun_(void); +int QF_getReadyFD_(void); +void QF_updateNextTick_(void); +int QF_getNextTimeoutMS_(struct timespec* timeout_spec); +void QF_onReadySignal_(void); +void QF_postRun_(void); + +#if QF_POSIX_MAX_EXTERNAL_FDS > 0 +// Function to register and unregister pollfds to be polled for external events (e.g. sockets, devices). +// External FDs are added to the QP/C ready FD (`QF_getReadyFD_`) and polled in the main event-loop +// and if the `fd_events` is triggered (POLLIN or POLLOUT), the corresponding `callback` function is +// called with the `user_data` pointer passed to it, along with the FD (to help user identify the +// source of the event). This callback is called in the main (and only) thread. +void QF_addExternalFD(int fd, short fd_events, void (*callback)(int, void*), void* user_data); +void QF_removeExternalFD(int fd); +// Called by POSIX port to collect pollfds before going into poll. +size_t QF_getExternalFDs_(struct pollfd* pfds, size_t pfds_len); +void QF_onExternalFDs_(struct pollfd* pfds, size_t pfds_len); +#endif // QF_POSIX_MAX_EXTERNAL_FDS > 0 + +// include files ------------------------------------------------------------- +#include "qequeue.h" // POSIX-FD needs the native event-queue +#include "qmpool.h" // POSIX-FD needs the native memory-pool +#include "qp.h" // QP platform-independent public interface + +//============================================================================ +// interface used only inside QF implementation, but not in applications + +#ifdef QP_IMPL + +// QF scheduler locking for POSIX-FD (not needed in single-thread port) +#define QF_SCHED_STAT_ +#define QF_SCHED_LOCK_(dummy) ((void)0) +#define QF_SCHED_UNLOCK_() ((void)0) + +// QF event queue customization for POSIX-FD... +#define QACTIVE_EQUEUE_WAIT_(me_) ((void)0) +#define QACTIVE_EQUEUE_SIGNAL_(me_) \ + QPSet_insert(&QF_readySet_, (me_)->prio); \ + QF_signalReadyOnPipe_() + +// QMPool operations +#define QF_EPOOL_TYPE_ QMPool +#define QF_EPOOL_INIT_(p_, poolSto_, poolSize_, evtSize_) \ + (QMPool_init(&(p_), (poolSto_), (poolSize_), (evtSize_))) +#define QF_EPOOL_EVENT_SIZE_(p_) ((uint16_t)(p_).blockSize) +#define QF_EPOOL_GET_(p_, e_, m_, qsId_) \ + ((e_) = (QEvt *)QMPool_get(&(p_), (m_), (qsId_))) +#define QF_EPOOL_PUT_(p_, e_, qsId_) (QMPool_put(&(p_), (e_), (qsId_))) +#define QF_EPOOL_USE_(ePool_) (QMPool_getUse(ePool_)) +#define QF_EPOOL_FREE_(ePool_) ((uint16_t)(ePool_)->nFree) +#define QF_EPOOL_MIN_(ePool_) ((uint16_t)(ePool_)->nMin) + +extern QPSet QF_readySet_; +extern int QF_readyPipeWrite_; // Pipe to signal events +extern int QF_readyPipeRead_; // Pipe to signal events + +void QF_signalReadyOnPipe_(void); + +#endif // QP_IMPL + +//============================================================================ +// NOTE1: +// QP, like all real-time frameworks, needs to execute certain sections of +// code exclusively, meaning that only one thread can execute the code at +// the time. Such sections of code are called "critical sections". +// +// This port uses a pair of functions QF_enterCriticalSection_() / +// QF_leaveCriticalSection_() to enter/leave the critical section, +// respectively. +// +// These functions are implemented in the qf_port.c module. Because this +// port is single-threaded (and synchronized via pollfds), there is no +// need to do anything when entering or leaving a critical section. +// However, this port still checks that there is no nesting of critical +// sections to verify correctness of the application, if it were to +// run on a port with meaningful critical sections (multi-threaded or +// with interrupts). +// + +#endif // QP_PORT_H_ diff --git a/ports/posix-fd/qs_port.c b/ports/posix-fd/qs_port.c new file mode 100644 index 000000000..5ed055a95 --- /dev/null +++ b/ports/posix-fd/qs_port.c @@ -0,0 +1,297 @@ +//============================================================================ +// QP/C Real-Time Event Framework (RTEF) +// +// Copyright (C) 2005 Quantum Leaps, LLC. All rights reserved. +// +// Q u a n t u m L e a P s +// ------------------------ +// Modern Embedded Software +// +// SPDX-License-Identifier: LicenseRef-QL-commercial +// +// This software is licensed under the terms of the Quantum Leaps commercial +// licenses. Please contact Quantum Leaps for more information about the +// available licensing options. +// +// RESTRICTIONS +// You may NOT : +// (a) redistribute, encumber, sell, rent, lease, sublicense, or otherwise +// transfer rights in this software, +// (b) remove or alter any trademark, logo, copyright or other proprietary +// notices, legends, symbols or labels present in this software, +// (c) plagiarize this software to sidestep the licensing obligations. +// +// Quantum Leaps contact information: +// +// +//============================================================================ + +// expose features from the 2008 POSIX standard (IEEE Standard 1003.1-2008) +#define _POSIX_C_SOURCE 200809L + +#ifndef Q_SPY + #error Q_SPY must be defined to compile qs_port.c +#endif // Q_SPY + +#define QP_IMPL // this is QP implementation +#include "qp_port.h" // QP port +#include "qsafe.h" // QP Functional Safety (FuSa) System +#include "qs_port.h" // QS port +#include "qs_pkg.h" // QS package-scope interface + +#include "safe_std.h" // portable "safe" / facilities +#include +#include +#include +#include +#include +#include +#include +#include +#include + +//Q_DEFINE_THIS_MODULE("qs_port") + +#define QS_TX_SIZE (8*1024) +#define QS_RX_SIZE (2*1024) +#define QS_TX_CHUNK QS_TX_SIZE +#define QS_TIMEOUT_MS 10L + +#define INVALID_SOCKET -1 +#define SOCKET_ERROR -1 + +// local variables ........................................................... +static int l_sock = INVALID_SOCKET; +static struct timespec const c_timeout = { 0, QS_TIMEOUT_MS*1000000L }; + +static char *l_rxBuf; +static int l_rxBufLen; + +//---------------------------------------------------------------------------- +uint8_t QS_onStartup(void const *arg) { + char hostName[128]; + char const *serviceName = "6601"; // default QSPY server port + char const *src; + char *dst; + int status; + + struct addrinfo *result = NULL; + struct addrinfo *rp = NULL; + struct addrinfo hints; + int sockopt_bool; + + // initialize the QS transmit and receive buffers + static uint8_t qsBuf[QS_TX_SIZE]; // buffer for QS-TX channel + QS_initBuf(qsBuf, sizeof(qsBuf)); + + static uint8_t qsRxBuf[QS_RX_SIZE]; // buffer for QS-RX channel + QS_rxInitBuf(qsRxBuf, sizeof(qsRxBuf)); + l_rxBuf = (char *)qsRxBuf; + l_rxBufLen = (int)sizeof(qsRxBuf); + + + // extract hostName from 'arg' (hostName:port_remote)... + src = (arg != (void *)0) + ? (char const *)arg + : "localhost"; // default QSPY host + dst = hostName; + while ((*src != '\0') + && (*src != ':') + && (dst < &hostName[sizeof(hostName) - 1])) + { + *dst++ = *src++; + } + *dst = '\0'; // zero-terminate hostName + + // extract serviceName from 'arg' (hostName:serviceName)... + if (*src == ':') { + serviceName = src + 1; + } + + memset(&hints, 0, sizeof(hints)); + hints.ai_family = AF_INET; + hints.ai_socktype = SOCK_STREAM; + hints.ai_protocol = IPPROTO_TCP; + status = getaddrinfo(hostName, serviceName, &hints, &result); + if (status != 0) { + FPRINTF_S(stderr, + " ERROR cannot resolve host Name=%s:%s,Err=%d\n", + hostName, serviceName, status); + goto error; + } + + for (rp = result; rp != NULL; rp = rp->ai_next) { + l_sock = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol); + if (l_sock != INVALID_SOCKET) { + if (connect(l_sock, rp->ai_addr, rp->ai_addrlen) + == SOCKET_ERROR) + { + close(l_sock); + l_sock = INVALID_SOCKET; + } + break; + } + } + + freeaddrinfo(result); + + // socket could not be opened & connected? + if (l_sock == INVALID_SOCKET) { + FPRINTF_S(stderr, " ERROR cannot connect to QSPY at " + "host=%s:%s\n", + hostName, serviceName); + goto error; + } + + // set the socket to non-blocking mode + status = fcntl(l_sock, F_GETFL, 0); + if (status == -1) { + FPRINTF_S(stderr, + " ERROR Socket configuration failed errno=%d\n", + errno); + QS_EXIT(); + goto error; + } + if (fcntl(l_sock, F_SETFL, status | O_NONBLOCK) != 0) { + FPRINTF_S(stderr, " ERROR Failed to set non-blocking socket " + "errno=%d\n", errno); + QF_stop(); // <== stop and exit the application + goto error; + } + + // configure the socket to reuse the address and not to linger + sockopt_bool = 1; + setsockopt(l_sock, SOL_SOCKET, SO_REUSEADDR, + &sockopt_bool, sizeof(sockopt_bool)); + sockopt_bool = 0; // negative option + setsockopt(l_sock, SOL_SOCKET, SO_LINGER, + &sockopt_bool, sizeof(sockopt_bool)); + QS_onFlush(); + + return 1U; // success + +error: + return 0U; // failure +} +//............................................................................ +void QS_onCleanup(void) { + static struct timespec const c_timeout = {0, 10L*QS_TIMEOUT_MS*1000000L }; + nanosleep(&c_timeout, NULL); // allow the last QS output to come out + if (l_sock != INVALID_SOCKET) { + close(l_sock); + l_sock = INVALID_SOCKET; + } + //PRINTF_S("%s\n", " Disconnected from QSPY"); +} +//............................................................................ +void QS_onReset(void) { + QS_onCleanup(); + //PRINTF_S("\n%s\n", "QS_onReset"); + exit(0); +} +//............................................................................ +void QS_onFlush(void) { + // NOTE: + // No critical section in QS::onFlush() to avoid nesting of critical + // sections in case QS::onFlush() is called from Q_onError(). + + if (l_sock == INVALID_SOCKET) { // socket NOT initialized? + FPRINTF_S(stderr, " ERROR %s\n", + "invalid TCP socket"); + QF_stop(); // <== stop and exit the application + return; + } + + uint16_t nBytes = QS_TX_CHUNK; + uint8_t const *data; + while ((data = QS_getBlock(&nBytes)) != (uint8_t *)0) { + for (;;) { // for-ever until break or return + int nSent = send(l_sock, (char const *)data, (int)nBytes, 0); + if (nSent == SOCKET_ERROR) { // sending failed? + if ((errno == EWOULDBLOCK) || (errno == EAGAIN)) { + // sleep for the timeout and then loop back + // to send() the SAME data again + nanosleep(&c_timeout, NULL); + } + else { // some other socket error... + FPRINTF_S(stderr, " ERROR sending data over TCP," + "errno=%d\n", errno); + QF_stop(); // <== stop and exit the application + return; + } + } + else if (nSent < (int)nBytes) { // sent fewer than requested? + nanosleep(&c_timeout, NULL); // sleep for the timeout + // adjust the data and loop back to send() the rest + data += nSent; + nBytes -= (uint16_t)nSent; + } + else { + break; + } + } + // set nBytes for the next call to QS_getBlock() + nBytes = QS_TX_CHUNK; + } +} +//............................................................................ +QSTimeCtr QS_onGetTime(void) { + struct timespec tspec; + clock_gettime(CLOCK_MONOTONIC, &tspec); + + // convert to units of 0.1 microsecond + QSTimeCtr time = (QSTimeCtr)(tspec.tv_sec * 10000000 + tspec.tv_nsec / 100); + return time; +} + +//............................................................................ +void QS_output(void) { + if (l_sock == INVALID_SOCKET) { // socket NOT initialized? + FPRINTF_S(stderr, " ERROR %s\n", + "invalid TCP socket"); + QF_stop(); // <== stop and exit the application + return; + } + + QS_CRIT_STAT + QS_CRIT_ENTRY(); + uint16_t nBytes = QS_TX_CHUNK; + uint8_t const *data = QS_getBlock(&nBytes); + QS_CRIT_EXIT(); + + if (nBytes > 0U) { // any bytes to send? + for (;;) { // for-ever until break or return + int nSent = send(l_sock, (char const *)data, (int)nBytes, 0); + if (nSent == SOCKET_ERROR) { // sending failed? + if ((errno == EWOULDBLOCK) || (errno == EAGAIN)) { + // sleep for the timeout and then loop back + // to send() the SAME data again + nanosleep(&c_timeout, NULL); + } + else { // some other socket error... + FPRINTF_S(stderr, " ERROR sending data over TCP," + "errno=%d\n", errno); + QF_stop(); // <== stop and exit the application + return; + } + } + else if (nSent < (int)nBytes) { // sent fewer than requested? + nanosleep(&c_timeout, NULL); // sleep for the timeout + // adjust the data and loop back to send() the rest + data += nSent; + nBytes -= (uint16_t)nSent; + } + else { + break; // break out of the for-ever loop + } + } + } +} +//............................................................................ +void QS_rx_input(void) { + int len = recv(l_sock, l_rxBuf, l_rxBufLen, 0); + if (len > 0) { // any data received? + QS_rxParseBuf((uint16_t)len); + } +} + diff --git a/ports/posix-fd/qs_port.h b/ports/posix-fd/qs_port.h new file mode 100644 index 000000000..424ecb772 --- /dev/null +++ b/ports/posix-fd/qs_port.h @@ -0,0 +1,54 @@ +//============================================================================ +// QP/C Real-Time Event Framework (RTEF) +// +// Copyright (C) 2005 Quantum Leaps, LLC. All rights reserved. +// +// Q u a n t u m L e a P s +// ------------------------ +// Modern Embedded Software +// +// SPDX-License-Identifier: LicenseRef-QL-commercial +// +// This software is licensed under the terms of the Quantum Leaps commercial +// licenses. Please contact Quantum Leaps for more information about the +// available licensing options. +// +// RESTRICTIONS +// You may NOT : +// (a) redistribute, encumber, sell, rent, lease, sublicense, or otherwise +// transfer rights in this software, +// (b) remove or alter any trademark, logo, copyright or other proprietary +// notices, legends, symbols or labels present in this software, +// (c) plagiarize this software to sidestep the licensing obligations. +// +// Quantum Leaps contact information: +// +// +//============================================================================ +#ifndef QS_PORT_H_ +#define QS_PORT_H_ + +#if defined(__LP64__) || defined(_LP64) // 64-bit architecture? + #define QS_OBJ_PTR_SIZE 8U + #define QS_FUN_PTR_SIZE 8U +#else // 32-bit architecture + #define QS_OBJ_PTR_SIZE 4U + #define QS_FUN_PTR_SIZE 4U +#endif + +void QS_output(void); // handle the QS output +void QS_rx_input(void); // handle the QS-RX input + +//============================================================================ +// NOTE: QS might be used with or without other QP components, in which +// case the separate definitions of the macros QF_CRIT_STAT, QF_CRIT_ENTRY(), +// and QF_CRIT_EXIT() are needed. In this port QS is configured to be used +// with the other QP component, by simply including "qp_port.h" +// *before* "qs.h". +#ifndef QP_PORT_H_ +#include "qp_port.h" // use QS with QP +#endif + +#include "qs.h" // QS platform-independent public interface + +#endif // QS_PORT_H_ diff --git a/ports/posix-fd/safe_std.h b/ports/posix-fd/safe_std.h new file mode 100644 index 000000000..d3332bb57 --- /dev/null +++ b/ports/posix-fd/safe_std.h @@ -0,0 +1,105 @@ +//============================================================================ +// QP/C Real-Time Event Framework (RTEF) +// +// Copyright (C) 2005 Quantum Leaps, LLC. All rights reserved. +// +// Q u a n t u m L e a P s +// ------------------------ +// Modern Embedded Software +// +// SPDX-License-Identifier: GPL-3.0-or-later OR LicenseRef-QL-commercial +// +// This software is dual-licensed under the terms of the open-source GNU +// General Public License (GPL) or under the terms of one of the closed- +// source Quantum Leaps commercial licenses. +// +// Redistributions in source code must retain this top-level comment block. +// Plagiarizing this software to sidestep the license obligations is illegal. +// +// NOTE: +// The GPL does NOT permit the incorporation of this code into proprietary +// programs. Please contact Quantum Leaps for commercial licensing options, +// which expressly supersede the GPL and are designed explicitly for +// closed-source distribution. +// +// Quantum Leaps contact information: +// +// +//============================================================================ +#ifndef SAFE_STD_H_ +#define SAFE_STD_H_ + +#include +#include + +// portable "safe" facilities from and ................ +#ifdef _WIN32 // Windows OS? + +#define MEMMOVE_S(dest_, num_, src_, count_) \ + memmove_s(dest_, num_, src_, count_) + +#define STRNCPY_S(dest_, destsiz_, src_) \ + strncpy_s(dest_, destsiz_, src_, _TRUNCATE) + +#define STRCAT_S(dest_, destsiz_, src_) \ + strcat_s(dest_, destsiz_, src_) + +#define SNPRINTF_S(buf_, bufsiz_, format_, ...) \ + _snprintf_s(buf_, bufsiz_, _TRUNCATE, format_, __VA_ARGS__) + +#define PRINTF_S(format_, ...) \ + (void)printf_s(format_, __VA_ARGS__) + +#define FPRINTF_S(fp_, format_, ...) \ + (void)fprintf_s(fp_, format_, __VA_ARGS__) + +#ifdef _MSC_VER +#define FREAD_S(buf_, bufsiz_, elsiz_, count_, fp_) \ + fread_s(buf_, bufsiz_, elsiz_, count_, fp_) +#else +#define FREAD_S(buf_, bufsiz_, elsiz_, count_, fp_) \ + fread(buf_, elsiz_, count_, fp_) +#endif // _MSC_VER + +#define FOPEN_S(fp_, fName_, mode_) \ +if (fopen_s(&fp_, fName_, mode_) != 0) { \ + fp_ = (FILE *)0; \ +} else (void)0 + +#define LOCALTIME_S(tm_, time_) \ + localtime_s(tm_, time_) + +#else // other OS (Linux, MacOS, etc.) ..................................... + +#define MEMMOVE_S(dest_, num_, src_, count_) \ + memmove(dest_, src_, count_) + +#define STRNCPY_S(dest_, destsiz_, src_) do { \ + strncpy(dest_, src_, destsiz_); \ + dest_[(destsiz_) - 1] = '\0'; \ +} while (false) + +#define STRCAT_S(dest_, destsiz_, src_) \ + strcat(dest_, src_) + +#define SNPRINTF_S(buf_, bufsiz_, format_, ...) \ + snprintf(buf_, bufsiz_, format_, __VA_ARGS__) + +#define PRINTF_S(format_, ...) \ + (void)printf(format_, __VA_ARGS__) + +#define FPRINTF_S(fp_, format_, ...) \ + (void)fprintf(fp_, format_, __VA_ARGS__) + +#define FREAD_S(buf_, bufsiz_, elsiz_, count_, fp_) \ + fread(buf_, elsiz_, count_, fp_) + +#define FOPEN_S(fp_, fName_, mode_) \ + (fp_ = fopen(fName_, mode_)) + +#define LOCALTIME_S(tm_, time_) \ + memcpy(tm_, localtime(time_), sizeof(struct tm)) + +#endif // _WIN32 + +#endif // SAFE_STD_H_ diff --git a/ports/posix-qv/qp_port.h b/ports/posix-qv/qp_port.h index 55c4076a9..590dc989c 100644 --- a/ports/posix-qv/qp_port.h +++ b/ports/posix-qv/qp_port.h @@ -36,6 +36,9 @@ // no-return function specifier (C11 Standard) #define Q_NORETURN _Noreturn void +// static assertion (C11 Standard) +#define Q_ASSERT_STATIC(expr_) _Static_assert((expr_), "QP static assert") + // QActive event queue and thread types for POSIX-QV #define QACTIVE_EQUEUE_TYPE QEQueue //QACTIVE_OS_OBJ_TYPE not used in this port @@ -78,31 +81,33 @@ void QF_onClockTick(void); #ifdef QP_IMPL - // QF scheduler locking for POSIX-QV (not needed in single-thread port) - #define QF_SCHED_STAT_ - #define QF_SCHED_LOCK_(dummy) ((void)0) - #define QF_SCHED_UNLOCK_() ((void)0) - - // QF event queue customization for POSIX-QV... - #define QACTIVE_EQUEUE_WAIT_(me_) ((void)0) - #define QACTIVE_EQUEUE_SIGNAL_(me_) \ - QPSet_insert(&QF_readySet_, (me_)->prio); \ - pthread_cond_signal(&QF_condVar_) - - // native QF event pool operations - #define QF_EPOOL_TYPE_ QMPool - #define QF_EPOOL_INIT_(p_, poolSto_, poolSize_, evtSize_) \ - (QMPool_init(&(p_), (poolSto_), (poolSize_), (evtSize_))) - #define QF_EPOOL_EVENT_SIZE_(p_) ((uint_fast16_t)(p_).blockSize) - #define QF_EPOOL_GET_(p_, e_, m_, qsId_) \ - ((e_) = (QEvt *)QMPool_get(&(p_), (m_), (qsId_))) - #define QF_EPOOL_PUT_(p_, e_, qsId_) \ - (QMPool_put(&(p_), (e_), (qsId_))) - - #include // POSIX-thread API - - extern QPSet QF_readySet_; - extern pthread_cond_t QF_condVar_; // Cond.var. to signal events +// QF scheduler locking for POSIX-QV (not needed in single-thread port) +#define QF_SCHED_STAT_ +#define QF_SCHED_LOCK_(dummy) ((void)0) +#define QF_SCHED_UNLOCK_() ((void)0) + +// QF event queue customization for POSIX-QV... +#define QACTIVE_EQUEUE_WAIT_(me_) ((void)0) +#define QACTIVE_EQUEUE_SIGNAL_(me_) \ + QPSet_insert(&QF_readySet_, (me_)->prio); \ + pthread_cond_signal(&QF_condVar_) + +// QMPool operations +#define QF_EPOOL_TYPE_ QMPool +#define QF_EPOOL_INIT_(p_, poolSto_, poolSize_, evtSize_) \ + (QMPool_init(&(p_), (poolSto_), (poolSize_), (evtSize_))) +#define QF_EPOOL_EVENT_SIZE_(p_) ((uint16_t)(p_).blockSize) +#define QF_EPOOL_GET_(p_, e_, m_, qsId_) \ + ((e_) = (QEvt *)QMPool_get(&(p_), (m_), (qsId_))) +#define QF_EPOOL_PUT_(p_, e_, qsId_) (QMPool_put(&(p_), (e_), (qsId_))) +#define QF_EPOOL_USE_(ePool_) (QMPool_getUse(ePool_)) +#define QF_EPOOL_FREE_(ePool_) ((uint16_t)(ePool_)->nFree) +#define QF_EPOOL_MIN_(ePool_) ((uint16_t)(ePool_)->nMin) + +#include // POSIX-thread API + +extern QPSet QF_readySet_; +extern pthread_cond_t QF_condVar_; // Cond.var. to signal events #endif // QP_IMPL diff --git a/ports/posix-qv/safe_std.h b/ports/posix-qv/safe_std.h index 984849006..d3332bb57 100644 --- a/ports/posix-qv/safe_std.h +++ b/ports/posix-qv/safe_std.h @@ -48,10 +48,10 @@ _snprintf_s(buf_, bufsiz_, _TRUNCATE, format_, __VA_ARGS__) #define PRINTF_S(format_, ...) \ - printf_s(format_, __VA_ARGS__) + (void)printf_s(format_, __VA_ARGS__) #define FPRINTF_S(fp_, format_, ...) \ - fprintf_s(fp_, format_, __VA_ARGS__) + (void)fprintf_s(fp_, format_, __VA_ARGS__) #ifdef _MSC_VER #define FREAD_S(buf_, bufsiz_, elsiz_, count_, fp_) \ @@ -86,10 +86,10 @@ if (fopen_s(&fp_, fName_, mode_) != 0) { \ snprintf(buf_, bufsiz_, format_, __VA_ARGS__) #define PRINTF_S(format_, ...) \ - printf(format_, __VA_ARGS__) + (void)printf(format_, __VA_ARGS__) #define FPRINTF_S(fp_, format_, ...) \ - fprintf(fp_, format_, __VA_ARGS__) + (void)fprintf(fp_, format_, __VA_ARGS__) #define FREAD_S(buf_, bufsiz_, elsiz_, count_, fp_) \ fread(buf_, elsiz_, count_, fp_) diff --git a/ports/posix/qp_port.h b/ports/posix/qp_port.h index 3a46cfd79..33f399e64 100644 --- a/ports/posix/qp_port.h +++ b/ports/posix/qp_port.h @@ -37,6 +37,9 @@ // no-return function specifier (C11 Standard) #define Q_NORETURN _Noreturn void +// static assertion (C11 Standard) +#define Q_ASSERT_STATIC(expr_) _Static_assert((expr_), "QP static assert") + // QActive event queue and thread types for POSIX #define QACTIVE_EQUEUE_TYPE QEQueue #define QACTIVE_OS_OBJ_TYPE pthread_cond_t @@ -78,38 +81,40 @@ void QF_onClockTick(void); #ifdef QP_IMPL - // QF scheduler locking for POSIX (not used at this point, see NOTE2) - #define QF_SCHED_STAT_ - #define QF_SCHED_LOCK_(dummy) ((void)0) - #define QF_SCHED_UNLOCK_() ((void)0) - - // QF event queue customization for POSIX... - #define QACTIVE_EQUEUE_WAIT_(me_) do { \ - while ((me_)->eQueue.frontEvt == (QEvt *)0) { \ - Q_ASSERT_INCRIT(400, QF_critSectNest_ == 1); \ - --QF_critSectNest_; \ - pthread_cond_wait(&(me_)->osObject, &QF_critSectMutex_); \ - Q_ASSERT_INCRIT(401, QF_critSectNest_ == 0); \ - ++QF_critSectNest_; \ - } \ - } while (false) - - #define QACTIVE_EQUEUE_SIGNAL_(me_) \ - pthread_cond_signal(&(me_)->osObject) - - // native QF event pool operations - #define QF_EPOOL_TYPE_ QMPool - #define QF_EPOOL_INIT_(p_, poolSto_, poolSize_, evtSize_) \ - (QMPool_init(&(p_), (poolSto_), (poolSize_), (evtSize_))) - #define QF_EPOOL_EVENT_SIZE_(p_) ((uint_fast16_t)(p_).blockSize) - #define QF_EPOOL_GET_(p_, e_, m_, qsId_) \ - ((e_) = (QEvt *)QMPool_get(&(p_), (m_), (qsId_))) - #define QF_EPOOL_PUT_(p_, e_, qsId_) \ - (QMPool_put(&(p_), (e_), (qsId_))) - - // mutex for QF critical section - extern pthread_mutex_t QF_critSectMutex_; - extern int_t QF_critSectNest_; +// QF scheduler locking for POSIX (not used at this point, see NOTE2) +#define QF_SCHED_STAT_ +#define QF_SCHED_LOCK_(dummy) ((void)0) +#define QF_SCHED_UNLOCK_() ((void)0) + +// QF event queue customization for POSIX... +#define QACTIVE_EQUEUE_WAIT_(me_) do { \ + while ((me_)->eQueue.frontEvt == (QEvt *)0) { \ + Q_ASSERT_INCRIT(400, QF_critSectNest_ == 1); \ + --QF_critSectNest_; \ + pthread_cond_wait(&(me_)->osObject, &QF_critSectMutex_); \ + Q_ASSERT_INCRIT(401, QF_critSectNest_ == 0); \ + ++QF_critSectNest_; \ + } \ +} while (false) + +#define QACTIVE_EQUEUE_SIGNAL_(me_) \ + pthread_cond_signal(&(me_)->osObject) + +// QMPool operations +#define QF_EPOOL_TYPE_ QMPool +#define QF_EPOOL_INIT_(p_, poolSto_, poolSize_, evtSize_) \ + (QMPool_init(&(p_), (poolSto_), (poolSize_), (evtSize_))) +#define QF_EPOOL_EVENT_SIZE_(p_) ((uint16_t)(p_).blockSize) +#define QF_EPOOL_GET_(p_, e_, m_, qsId_) \ + ((e_) = (QEvt *)QMPool_get(&(p_), (m_), (qsId_))) +#define QF_EPOOL_PUT_(p_, e_, qsId_) (QMPool_put(&(p_), (e_), (qsId_))) +#define QF_EPOOL_USE_(ePool_) (QMPool_getUse(ePool_)) +#define QF_EPOOL_FREE_(ePool_) ((uint16_t)(ePool_)->nFree) +#define QF_EPOOL_MIN_(ePool_) ((uint16_t)(ePool_)->nMin) + +// mutex for QF critical section +extern pthread_mutex_t QF_critSectMutex_; +extern int_t QF_critSectNest_; #endif // QP_IMPL diff --git a/ports/posix/safe_std.h b/ports/posix/safe_std.h index 984849006..d3332bb57 100644 --- a/ports/posix/safe_std.h +++ b/ports/posix/safe_std.h @@ -48,10 +48,10 @@ _snprintf_s(buf_, bufsiz_, _TRUNCATE, format_, __VA_ARGS__) #define PRINTF_S(format_, ...) \ - printf_s(format_, __VA_ARGS__) + (void)printf_s(format_, __VA_ARGS__) #define FPRINTF_S(fp_, format_, ...) \ - fprintf_s(fp_, format_, __VA_ARGS__) + (void)fprintf_s(fp_, format_, __VA_ARGS__) #ifdef _MSC_VER #define FREAD_S(buf_, bufsiz_, elsiz_, count_, fp_) \ @@ -86,10 +86,10 @@ if (fopen_s(&fp_, fName_, mode_) != 0) { \ snprintf(buf_, bufsiz_, format_, __VA_ARGS__) #define PRINTF_S(format_, ...) \ - printf(format_, __VA_ARGS__) + (void)printf(format_, __VA_ARGS__) #define FPRINTF_S(fp_, format_, ...) \ - fprintf(fp_, format_, __VA_ARGS__) + (void)fprintf(fp_, format_, __VA_ARGS__) #define FREAD_S(buf_, bufsiz_, elsiz_, count_, fp_) \ fread(buf_, elsiz_, count_, fp_) diff --git a/ports/qep-only/qp_port.h b/ports/qep-only/qp_port.h index 19d5d980e..90ea8c54b 100644 --- a/ports/qep-only/qp_port.h +++ b/ports/qep-only/qp_port.h @@ -32,6 +32,9 @@ // no-return function specifier (C11 Standard) #define Q_NORETURN _Noreturn void +// static assertion (C11 Standard) +#define Q_ASSERT_STATIC(expr_) _Static_assert((expr_), "QP static assert") + // include files ------------------------------------------------------------- #include // Exact-width types. WG14/N843 C99 Standard #include // Boolean type. WG14/N843 C99 Standard diff --git a/ports/qep-only/qs_port.h b/ports/qep-only/qs_port.h new file mode 100644 index 000000000..920c3d156 --- /dev/null +++ b/ports/qep-only/qs_port.h @@ -0,0 +1,54 @@ +//============================================================================ +// QP/C Real-Time Event Framework (RTEF) +// +// Copyright (C) 2005 Quantum Leaps, LLC. All rights reserved. +// +// Q u a n t u m L e a P s +// ------------------------ +// Modern Embedded Software +// +// SPDX-License-Identifier: LicenseRef-QL-commercial +// +// This software is licensed under the terms of the Quantum Leaps commercial +// licenses. Please contact Quantum Leaps for more information about the +// available licensing options. +// +// RESTRICTIONS +// You may NOT : +// (a) redistribute, encumber, sell, rent, lease, sublicense, or otherwise +// transfer rights in this software, +// (b) remove or alter any trademark, logo, copyright or other proprietary +// notices, legends, symbols or labels present in this software, +// (c) plagiarize this software to sidestep the licensing obligations. +// +// Quantum Leaps contact information: +// +// +//============================================================================ +#ifndef QS_PORT_H_ +#define QS_PORT_H_ + +#ifdef _WIN64 // 64-bit architecture? + #define QS_OBJ_PTR_SIZE 8U + #define QS_FUN_PTR_SIZE 8U +#else // 32-bit architecture + #define QS_OBJ_PTR_SIZE 4U + #define QS_FUN_PTR_SIZE 4U +#endif + +void QS_output(void); // handle the QS output +void QS_rx_input(void); // handle the QS-RX input + +//============================================================================ +// NOTE: QS might be used with or without other QP components, in which +// case the separate definitions of the macros QF_CRIT_STAT, QF_CRIT_ENTRY(), +// and QF_CRIT_EXIT() are needed. In this port QS is configured to be used +// with the other QP component, by simply including "qp_port.h" +// *before* "qs.h". +#ifndef QP_PORT_H_ +#include "qp_port.h" // use QS with QP +#endif + +#include "qs.h" // QS platform-independent public interface + +#endif // QS_PORT_H_ diff --git a/ports/qep-only/safe_std.h b/ports/qep-only/safe_std.h index 984849006..d3332bb57 100644 --- a/ports/qep-only/safe_std.h +++ b/ports/qep-only/safe_std.h @@ -48,10 +48,10 @@ _snprintf_s(buf_, bufsiz_, _TRUNCATE, format_, __VA_ARGS__) #define PRINTF_S(format_, ...) \ - printf_s(format_, __VA_ARGS__) + (void)printf_s(format_, __VA_ARGS__) #define FPRINTF_S(fp_, format_, ...) \ - fprintf_s(fp_, format_, __VA_ARGS__) + (void)fprintf_s(fp_, format_, __VA_ARGS__) #ifdef _MSC_VER #define FREAD_S(buf_, bufsiz_, elsiz_, count_, fp_) \ @@ -86,10 +86,10 @@ if (fopen_s(&fp_, fName_, mode_) != 0) { \ snprintf(buf_, bufsiz_, format_, __VA_ARGS__) #define PRINTF_S(format_, ...) \ - printf(format_, __VA_ARGS__) + (void)printf(format_, __VA_ARGS__) #define FPRINTF_S(fp_, format_, ...) \ - fprintf(fp_, format_, __VA_ARGS__) + (void)fprintf(fp_, format_, __VA_ARGS__) #define FREAD_S(buf_, bufsiz_, elsiz_, count_, fp_) \ fread(buf_, elsiz_, count_, fp_) diff --git a/ports/qube/qp_port.h b/ports/qube/qp_port.h index 3471b8da5..6899df57d 100644 --- a/ports/qube/qp_port.h +++ b/ports/qube/qp_port.h @@ -1,5 +1,5 @@ //============================================================================ -// SafeQP/C Real-Time Event Framework (RTEF) +// QP/C Real-Time Event Framework (RTEF) // // Copyright (C) 2005 Quantum Leaps, LLC. All rights reserved. // @@ -7,19 +7,20 @@ // ------------------------ // Modern Embedded Software // -// SPDX-License-Identifier: LicenseRef-QL-commercial +// SPDX-License-Identifier: GPL-3.0-or-later OR LicenseRef-QL-commercial // -// This software is licensed under the terms of the Quantum Leaps commercial -// licenses. Please contact Quantum Leaps for more information about the -// available licensing options. +// This software is dual-licensed under the terms of the open-source GNU +// General Public License (GPL) or under the terms of one of the closed- +// source Quantum Leaps commercial licenses. // -// RESTRICTIONS -// You may NOT : -// (a) redistribute, encumber, sell, rent, lease, sublicense, or otherwise -// transfer rights in this software, -// (b) remove or alter any trademark, logo, copyright or other proprietary -// notices, legends, symbols or labels present in this software, -// (c) plagiarize this software to sidestep the licensing obligations. +// Redistributions in source code must retain this top-level comment block. +// Plagiarizing this software to sidestep the license obligations is illegal. +// +// NOTE: +// The GPL does NOT permit the incorporation of this code into proprietary +// programs. Please contact Quantum Leaps for commercial licensing options, +// which expressly supersede the GPL and are designed explicitly for +// closed-source distribution. // // Quantum Leaps contact information: // @@ -35,7 +36,10 @@ // no-return function specifier (C11 Standard) #define Q_NORETURN _Noreturn void -// Qube event queue and thread types +// static assertion (C11 Standard) +#define Q_ASSERT_STATIC(expr_) _Static_assert((expr_), "QP static assert") + +// QActive event queue type #define QACTIVE_EQUEUE_TYPE QEQueue #define QACTIVE_OS_OBJ_TYPE void * #define QACTIVE_THREAD_TYPE void const * @@ -65,30 +69,32 @@ extern uint_fast8_t QF_intLock_; // interface used only inside QF implementation, but not in applications #ifdef QP_IMPL - extern QPSet QF_readySet_; - extern QPSet QF_readySet_dis_; - - // Qube scheduler locking (not used) - #define QF_SCHED_STAT_ - #define QF_SCHED_LOCK_(dummy) ((void)0) - #define QF_SCHED_UNLOCK_() ((void)0) - - // native event queue operations - #define QACTIVE_EQUEUE_WAIT_(me_) ((void)0) - #define QACTIVE_EQUEUE_SIGNAL_(me_) \ - QPSet_insert(&QF_readySet_, (uint_fast8_t)(me_)->prio) - - // native QF event pool operations - #define QF_EPOOL_TYPE_ QMPool - #define QF_EPOOL_INIT_(p_, poolSto_, poolSize_, evtSize_) \ - (QMPool_init(&(p_), (poolSto_), (poolSize_), (evtSize_))) - #define QF_EPOOL_EVENT_SIZE_(p_) ((uint_fast16_t)(p_).blockSize) - #define QF_EPOOL_GET_(p_, e_, m_, qsId_) \ - ((e_) = (QEvt *)QMPool_get(&(p_), (m_), (qsId_))) - #define QF_EPOOL_PUT_(p_, e_, qsId_) \ - (QMPool_put(&(p_), (e_), (qsId_))) - - #include "qp_pkg.h" // internal QP interface +extern QPSet QF_readySet_; +extern QPSet QF_readySet_dis; + +// Qube scheduler locking (not used) +#define QF_SCHED_STAT_ +#define QF_SCHED_LOCK_(dummy) ((void)0) +#define QF_SCHED_UNLOCK_() ((void)0) + +// native event queue operations +#define QACTIVE_EQUEUE_WAIT_(me_) ((void)0) +#define QACTIVE_EQUEUE_SIGNAL_(me_) \ + QPSet_insert(&QF_readySet_, (uint_fast8_t)(me_)->prio) + +// QMPool operations +#define QF_EPOOL_TYPE_ QMPool +#define QF_EPOOL_INIT_(p_, poolSto_, poolSize_, evtSize_) \ + (QMPool_init(&(p_), (poolSto_), (poolSize_), (evtSize_))) +#define QF_EPOOL_EVENT_SIZE_(p_) ((uint16_t)(p_).blockSize) +#define QF_EPOOL_GET_(p_, e_, m_, qsId_) \ + ((e_) = (QEvt *)QMPool_get(&(p_), (m_), (qsId_))) +#define QF_EPOOL_PUT_(p_, e_, qsId_) (QMPool_put(&(p_), (e_), (qsId_))) +#define QF_EPOOL_USE_(ePool_) (QMPool_getUse(ePool_)) +#define QF_EPOOL_FREE_(ePool_) ((uint16_t)(ePool_)->nFree) +#define QF_EPOOL_MIN_(ePool_) ((uint16_t)(ePool_)->nMin) + +#include "qp_pkg.h" // internal QP interface #endif // QP_IMPL diff --git a/ports/qube/qs_port.h b/ports/qube/qs_port.h index 991190461..a6fee9ac4 100644 --- a/ports/qube/qs_port.h +++ b/ports/qube/qs_port.h @@ -1,5 +1,5 @@ //============================================================================ -// SafeQP/C Real-Time Event Framework (RTEF) +// QP/C Real-Time Event Framework (RTEF) // // Copyright (C) 2005 Quantum Leaps, LLC. All rights reserved. // @@ -7,19 +7,20 @@ // ------------------------ // Modern Embedded Software // -// SPDX-License-Identifier: LicenseRef-QL-commercial +// SPDX-License-Identifier: GPL-3.0-or-later OR LicenseRef-QL-commercial // -// This software is licensed under the terms of the Quantum Leaps commercial -// licenses. Please contact Quantum Leaps for more information about the -// available licensing options. +// This software is dual-licensed under the terms of the open-source GNU +// General Public License (GPL) or under the terms of one of the closed- +// source Quantum Leaps commercial licenses. // -// RESTRICTIONS -// You may NOT : -// (a) redistribute, encumber, sell, rent, lease, sublicense, or otherwise -// transfer rights in this software, -// (b) remove or alter any trademark, logo, copyright or other proprietary -// notices, legends, symbols or labels present in this software, -// (c) plagiarize this software to sidestep the licensing obligations. +// Redistributions in source code must retain this top-level comment block. +// Plagiarizing this software to sidestep the license obligations is illegal. +// +// NOTE: +// The GPL does NOT permit the incorporation of this code into proprietary +// programs. Please contact Quantum Leaps for commercial licensing options, +// which expressly supersede the GPL and are designed explicitly for +// closed-source distribution. // // Quantum Leaps contact information: // diff --git a/ports/qube/qube.c b/ports/qube/qube.c index 04dfa2ffb..4837d62d3 100644 --- a/ports/qube/qube.c +++ b/ports/qube/qube.c @@ -1,5 +1,5 @@ //============================================================================ -// SafeQP/C Real-Time Event Framework (RTEF) +// QP/C Real-Time Event Framework (RTEF) // // Copyright (C) 2005 Quantum Leaps, LLC. All rights reserved. // @@ -7,19 +7,20 @@ // ------------------------ // Modern Embedded Software // -// SPDX-License-Identifier: LicenseRef-QL-commercial +// SPDX-License-Identifier: GPL-3.0-or-later OR LicenseRef-QL-commercial // -// This software is licensed under the terms of the Quantum Leaps commercial -// licenses. Please contact Quantum Leaps for more information about the -// available licensing options. +// This software is dual-licensed under the terms of the open-source GNU +// General Public License (GPL) or under the terms of one of the closed- +// source Quantum Leaps commercial licenses. // -// RESTRICTIONS -// You may NOT : -// (a) redistribute, encumber, sell, rent, lease, sublicense, or otherwise -// transfer rights in this software, -// (b) remove or alter any trademark, logo, copyright or other proprietary -// notices, legends, symbols or labels present in this software, -// (c) plagiarize this software to sidestep the licensing obligations. +// Redistributions in source code must retain this top-level comment block. +// Plagiarizing this software to sidestep the license obligations is illegal. +// +// NOTE: +// The GPL does NOT permit the incorporation of this code into proprietary +// programs. Please contact Quantum Leaps for commercial licensing options, +// which expressly supersede the GPL and are designed explicitly for +// closed-source distribution. // // Quantum Leaps contact information: // @@ -45,7 +46,7 @@ Q_DEFINE_THIS_MODULE("qube") // Global objects ========================================================== QPSet QF_readySet_; -QPSet QF_readySet_dis_; +QPSet QF_readySet_dis; uint_fast8_t QF_intLock_; //............................................................................ @@ -256,15 +257,6 @@ void QF_init(void) { PRINTF_S("Qube %s (c) state-machine.com\n\n%s\n\n", QP_VERSION_STR, HELP_STR); - // Clear 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). - // - QF_bzero_(&QF_priv_, sizeof(QF_priv_)); - QF_bzero_(&QF_readySet_, sizeof(QF_readySet_)); - QF_bzero_(&QF_intLock_, sizeof(QF_intLock_)); - QF_bzero_(&QActive_registry_[0], sizeof(QActive_registry_)); - QTimeEvt_init(); // initialize QTimeEvts l_currAO_name[0] = '\0'; @@ -393,7 +385,7 @@ uint8_t QS_onStartup(void const *arg) { .objPtrSize = QS_OBJ_PTR_SIZE, .funPtrSize = QS_FUN_PTR_SIZE, .tstampSize = QS_TIME_SIZE, - .sigSize = Q_SIGNAL_SIZE, + .sigSize = 2U, .evtSize = QF_EVENT_SIZ_SIZE, .queueCtrSize = QF_EQUEUE_CTR_SIZE, .poolCtrSize = QF_MPOOL_CTR_SIZE, diff --git a/ports/qube/safe_std.h b/ports/qube/safe_std.h index 848942cb0..d3332bb57 100644 --- a/ports/qube/safe_std.h +++ b/ports/qube/safe_std.h @@ -1,5 +1,5 @@ //============================================================================ -// SafeQP/C Real-Time Event Framework (RTEF) +// QP/C Real-Time Event Framework (RTEF) // // Copyright (C) 2005 Quantum Leaps, LLC. All rights reserved. // @@ -7,19 +7,20 @@ // ------------------------ // Modern Embedded Software // -// SPDX-License-Identifier: LicenseRef-QL-commercial +// SPDX-License-Identifier: GPL-3.0-or-later OR LicenseRef-QL-commercial // -// This software is licensed under the terms of the Quantum Leaps commercial -// licenses. Please contact Quantum Leaps for more information about the -// available licensing options. +// This software is dual-licensed under the terms of the open-source GNU +// General Public License (GPL) or under the terms of one of the closed- +// source Quantum Leaps commercial licenses. // -// RESTRICTIONS -// You may NOT : -// (a) redistribute, encumber, sell, rent, lease, sublicense, or otherwise -// transfer rights in this software, -// (b) remove or alter any trademark, logo, copyright or other proprietary -// notices, legends, symbols or labels present in this software, -// (c) plagiarize this software to sidestep the licensing obligations. +// Redistributions in source code must retain this top-level comment block. +// Plagiarizing this software to sidestep the license obligations is illegal. +// +// NOTE: +// The GPL does NOT permit the incorporation of this code into proprietary +// programs. Please contact Quantum Leaps for commercial licensing options, +// which expressly supersede the GPL and are designed explicitly for +// closed-source distribution. // // Quantum Leaps contact information: // @@ -47,10 +48,10 @@ _snprintf_s(buf_, bufsiz_, _TRUNCATE, format_, __VA_ARGS__) #define PRINTF_S(format_, ...) \ - printf_s(format_, __VA_ARGS__) + (void)printf_s(format_, __VA_ARGS__) #define FPRINTF_S(fp_, format_, ...) \ - fprintf_s(fp_, format_, __VA_ARGS__) + (void)fprintf_s(fp_, format_, __VA_ARGS__) #ifdef _MSC_VER #define FREAD_S(buf_, bufsiz_, elsiz_, count_, fp_) \ @@ -85,10 +86,10 @@ if (fopen_s(&fp_, fName_, mode_) != 0) { \ snprintf(buf_, bufsiz_, format_, __VA_ARGS__) #define PRINTF_S(format_, ...) \ - printf(format_, __VA_ARGS__) + (void)printf(format_, __VA_ARGS__) #define FPRINTF_S(fp_, format_, ...) \ - fprintf(fp_, format_, __VA_ARGS__) + (void)fprintf(fp_, format_, __VA_ARGS__) #define FREAD_S(buf_, bufsiz_, elsiz_, count_, fp_) \ fread(buf_, elsiz_, count_, fp_) diff --git a/ports/threadx/qf_port.c b/ports/threadx/qf_port.c index 7be471a56..35da64c80 100644 --- a/ports/threadx/qf_port.c +++ b/ports/threadx/qf_port.c @@ -41,8 +41,6 @@ Q_DEFINE_THIS_MODULE("qf_port") //............................................................................ void QF_init(void) { - QF_bzero_(&QF_priv_, sizeof(QF_priv_)); - QF_bzero_(&QActive_registry_[0], sizeof(QActive_registry_)); QTimeEvt_init(); // initialize QTimeEvts } //............................................................................ @@ -182,7 +180,6 @@ bool QActive_post_(QActive * const me, QEvt const * const e, QS_END_PRE() if (e->poolNum_ != 0U) { // is it a pool event? - Q_ASSERT_INCRIT(205, e->refCtr_ < (2U * QF_MAX_ACTIVE)); QEvt_refCtr_inc_(e); // increment the reference counter } QF_CRIT_EXIT(); @@ -228,7 +225,6 @@ void QActive_postLIFO_(QActive * const me, QEvt const * const e) { QS_END_PRE() if (e->poolNum_ != 0U) { // is it a pool event? - Q_ASSERT_INCRIT(305, e->refCtr_ < (2U * QF_MAX_ACTIVE)); QEvt_refCtr_inc_(e); // increment the reference counter } QF_CRIT_EXIT(); @@ -260,6 +256,24 @@ QEvt const *QActive_get_(QActive * const me) { return e; } +//............................................................................ +//! @static @public @memberof QActive +uint16_t QActive_getQueueUse(uint_fast8_t const prio) { + Q_UNUSED_PAR(prio); + return 0U; // current use level in a queue not supported in this RTOS +} +//............................................................................ +//! @static @public @memberof QActive +uint16_t QActive_getQueueFree(uint_fast8_t const prio) { + Q_UNUSED_PAR(prio); + return 0U; // current use level in a queue not supported in this RTOS +} +//............................................................................ +//! @static @public @memberof QActive +uint16_t QActive_getQueueMin(uint_fast8_t const prio) { + Q_UNUSED_PAR(prio); + return 0U; // minimum free entries in a queue not supported in this RTOS +} //............................................................................ void QFSchedLock_(QFSchedLock * const lockStat, uint_fast8_t prio) { diff --git a/ports/threadx/qp_port.h b/ports/threadx/qp_port.h index a84ab9086..0c6b7616c 100644 --- a/ports/threadx/qp_port.h +++ b/ports/threadx/qp_port.h @@ -79,43 +79,45 @@ enum ThreadX_ThreadAttrs { // interface used only inside QF implementation, but not in applications #ifdef QP_IMPL - // ThreadX-specific scheduler locking (implemented in qf_port.cpp) - #define QF_SCHED_STAT_ QFSchedLock lockStat_; - #define QF_SCHED_LOCK_(prio_) do { \ - if (TX_THREAD_GET_SYSTEM_STATE() != 0U) { \ - lockStat_.lockPrio = 0U; \ - } else { \ - QFSchedLock_(&lockStat_, (prio_)); \ - } \ - } while (false) - - #define QF_SCHED_UNLOCK_() do { \ - if (lockStat_.lockPrio != 0U) { \ - QFSchedUnlock_(&lockStat_); \ - } \ - } while (false) - - typedef struct { - uint_fast8_t lockPrio; // lock prio [QF numbering scheme] - UINT prevThre; // previoius preemption threshold - TX_THREAD *lockHolder; // the thread holding the lock - } QFSchedLock; - - // internal implementation of scheduler locking/unlocking - void QFSchedLock_(QFSchedLock * const lockStat, uint_fast8_t prio); - void QFSchedUnlock_(QFSchedLock const * const lockStat); - // internal TX interrupt counter for TX_THREAD_GET_SYSTEM_STATE() - extern ULONG volatile _tx_thread_system_state; - - // native QF event pool customization - #define QF_EPOOL_TYPE_ QMPool - #define QF_EPOOL_INIT_(p_, poolSto_, poolSize_, evtSize_) \ - (QMPool_init(&(p_), (poolSto_), (poolSize_), (evtSize_))) - #define QF_EPOOL_EVENT_SIZE_(p_) ((uint_fast16_t)(p_).blockSize) - #define QF_EPOOL_GET_(p_, e_, m_, qsId_) \ - ((e_) = (QEvt *)QMPool_get(&(p_), (m_), (qsId_))) - #define QF_EPOOL_PUT_(p_, e_, qsId_) \ - (QMPool_put(&(p_), (e_), (qsId_))) +// ThreadX-specific scheduler locking (implemented in qf_port.cpp) +#define QF_SCHED_STAT_ QFSchedLock lockStat_; +#define QF_SCHED_LOCK_(prio_) do { \ + if (TX_THREAD_GET_SYSTEM_STATE() != 0U) { \ + lockStat_.lockPrio = 0U; \ + } else { \ + QFSchedLock_(&lockStat_, (prio_)); \ + } \ +} while (false) + +#define QF_SCHED_UNLOCK_() do { \ + if (lockStat_.lockPrio != 0U) { \ + QFSchedUnlock_(&lockStat_); \ + } \ +} while (false) + +typedef struct { + uint_fast8_t lockPrio; // lock prio [QF numbering scheme] + UINT prevThre; // previoius preemption threshold + TX_THREAD *lockHolder; // the thread holding the lock +} QFSchedLock; + +// internal implementation of scheduler locking/unlocking +void QFSchedLock_(QFSchedLock * const lockStat, uint_fast8_t prio); +void QFSchedUnlock_(QFSchedLock const * const lockStat); +// internal TX interrupt counter for TX_THREAD_GET_SYSTEM_STATE() +extern ULONG _tx_thread_system_state; + +// QMPool operations +#define QF_EPOOL_TYPE_ QMPool +#define QF_EPOOL_INIT_(p_, poolSto_, poolSize_, evtSize_) \ + (QMPool_init(&(p_), (poolSto_), (poolSize_), (evtSize_))) +#define QF_EPOOL_EVENT_SIZE_(p_) ((uint16_t)(p_).blockSize) +#define QF_EPOOL_GET_(p_, e_, m_, qsId_) \ + ((e_) = (QEvt *)QMPool_get(&(p_), (m_), (qsId_))) +#define QF_EPOOL_PUT_(p_, e_, qsId_) (QMPool_put(&(p_), (e_), (qsId_))) +#define QF_EPOOL_USE_(ePool_) (QMPool_getUse(ePool_)) +#define QF_EPOOL_FREE_(ePool_) ((uint16_t)(ePool_)->nFree) +#define QF_EPOOL_MIN_(ePool_) ((uint16_t)(ePool_)->nMin) #endif // ifdef QP_IMPL diff --git a/ports/uc-os2/qf_port.c b/ports/uc-os2/qf_port.c index 4f76477ff..70672fafb 100644 --- a/ports/uc-os2/qf_port.c +++ b/ports/uc-os2/qf_port.c @@ -44,8 +44,6 @@ static void task_function(void *pdata); // uC-OS2 task signature //............................................................................ void QF_init(void) { - QF_bzero_(&QF_priv_, sizeof(QF_priv_)); - QF_bzero_(&QActive_registry_[0], sizeof(QActive_registry_)); QTimeEvt_init(); // initialize QTimeEvts OSInit(); // initialize uC-OS2 } @@ -188,6 +186,24 @@ void QActive_setAttr(QActive *const me, uint32_t attr1, void const *attr2) { } QF_CRIT_EXIT(); } +//............................................................................ +//! @static @public @memberof QActive +uint16_t QActive_getQueueUse(uint_fast8_t const prio) { + Q_UNUSED_PAR(prio); + return 0U; // current use level in a queue not supported in this RTOS +} +//............................................................................ +//! @static @public @memberof QActive +uint16_t QActive_getQueueFree(uint_fast8_t const prio) { + Q_UNUSED_PAR(prio); + return 0U; // current use level in a queue not supported in this RTOS +} +//............................................................................ +//! @static @public @memberof QActive +uint16_t QActive_getQueueMin(uint_fast8_t const prio) { + Q_UNUSED_PAR(prio); + return 0U; // minimum free entries in a queue not supported in this RTOS +} //............................................................................ bool QActive_post_(QActive * const me, QEvt const * const e, @@ -231,7 +247,6 @@ bool QActive_post_(QActive * const me, QEvt const * const e, QS_END_PRE() if (e->poolNum_ != 0U) { // is it a pool event? - Q_ASSERT_INCRIT(205, e->refCtr_ < (2U * QF_MAX_ACTIVE)); QEvt_refCtr_inc_(e); // increment the reference counter } QF_CRIT_EXIT(); @@ -282,7 +297,6 @@ void QActive_postLIFO_(QActive * const me, QEvt const * const e) { QS_END_PRE() if (e->poolNum_ != 0U) { // is it a pool event? - Q_ASSERT_INCRIT(305, e->refCtr_ < (2U * QF_MAX_ACTIVE)); QEvt_refCtr_inc_(e); // increment the reference counter } QF_CRIT_EXIT(); diff --git a/ports/uc-os2/qp_port.h b/ports/uc-os2/qp_port.h index e5ae65df2..8a0432854 100644 --- a/ports/uc-os2/qp_port.h +++ b/ports/uc-os2/qp_port.h @@ -75,29 +75,31 @@ enum UCOS2_TaskAttrs { // interface used only inside QF implementation, but not in applications #ifdef QP_IMPL - // uC-OS2-specific scheduler locking, see NOTE2 - #define QF_SCHED_STAT_ - #define QF_SCHED_LOCK_(dummy) do { \ - if (OSIntNesting == 0U) { \ - OSSchedLock(); \ - } \ - } while (false) - - #define QF_SCHED_UNLOCK_() do { \ - if (OSIntNesting == 0U) { \ - OSSchedUnlock(); \ - } \ - } while (false) - - // native QF event pool customization - #define QF_EPOOL_TYPE_ QMPool - #define QF_EPOOL_INIT_(p_, poolSto_, poolSize_, evtSize_) \ - (QMPool_init(&(p_), (poolSto_), (poolSize_), (evtSize_))) - #define QF_EPOOL_EVENT_SIZE_(p_) ((uint_fast16_t)(p_).blockSize) - #define QF_EPOOL_GET_(p_, e_, m_, qsId_) \ - ((e_) = (QEvt *)QMPool_get(&(p_), (m_), (qsId_))) - #define QF_EPOOL_PUT_(p_, e_, qsId_) \ - (QMPool_put(&(p_), (e_), (qsId_))) +// uC-OS2-specific scheduler locking, see NOTE2 +#define QF_SCHED_STAT_ +#define QF_SCHED_LOCK_(dummy) do { \ + if (OSIntNesting == 0U) { \ + OSSchedLock(); \ + } \ +} while (false) + +#define QF_SCHED_UNLOCK_() do { \ + if (OSIntNesting == 0U) { \ + OSSchedUnlock(); \ + } \ +} while (false) + +// QMPool operations +#define QF_EPOOL_TYPE_ QMPool +#define QF_EPOOL_INIT_(p_, poolSto_, poolSize_, evtSize_) \ + (QMPool_init(&(p_), (poolSto_), (poolSize_), (evtSize_))) +#define QF_EPOOL_EVENT_SIZE_(p_) ((uint16_t)(p_).blockSize) +#define QF_EPOOL_GET_(p_, e_, m_, qsId_) \ + ((e_) = (QEvt *)QMPool_get(&(p_), (m_), (qsId_))) +#define QF_EPOOL_PUT_(p_, e_, qsId_) (QMPool_put(&(p_), (e_), (qsId_))) +#define QF_EPOOL_USE_(ePool_) (QMPool_getUse(ePool_)) +#define QF_EPOOL_FREE_(ePool_) ((uint16_t)(ePool_)->nFree) +#define QF_EPOOL_MIN_(ePool_) ((uint16_t)(ePool_)->nMin) #endif // QP_IMPL diff --git a/ports/win32-qv/qp_port.h b/ports/win32-qv/qp_port.h index 2c99d1a6d..00860c109 100644 --- a/ports/win32-qv/qp_port.h +++ b/ports/win32-qv/qp_port.h @@ -64,6 +64,9 @@ #endif +// static assertion (C11 Standard) +#define Q_ASSERT_STATIC(expr_) _Static_assert((expr_), "QP static assert") + // QF event queue and thread types for Win32-QV #define QACTIVE_EQUEUE_TYPE QEQueue //QACTIVE_OS_OBJ_TYPE not used in this port @@ -115,44 +118,46 @@ void QF_onClockTick(void); #ifdef QP_IMPL - // QF scheduler locking for Win32-QV (not needed in single-thread port) - #define QF_SCHED_STAT_ - #define QF_SCHED_LOCK_(dummy) ((void)0) - #define QF_SCHED_UNLOCK_() ((void)0) - - // QF event queue customization for Win32-QV... - #define QACTIVE_EQUEUE_WAIT_(me_) ((void)0) - - #define QACTIVE_EQUEUE_SIGNAL_(me_) \ - QPSet_insert(&QF_readySet_, (me_)->prio); \ - (void)SetEvent(QF_win32Event_) - - // native QF event pool operations - #define QF_EPOOL_TYPE_ QMPool - #define QF_EPOOL_INIT_(p_, poolSto_, poolSize_, evtSize_) \ - (QMPool_init(&(p_), (poolSto_), (poolSize_), (evtSize_))) - #define QF_EPOOL_EVENT_SIZE_(p_) ((uint_fast16_t)(p_).blockSize) - #define QF_EPOOL_GET_(p_, e_, m_, qsId_) \ - ((e_) = (QEvt *)QMPool_get(&(p_), (m_), (qsId_))) - #define QF_EPOOL_PUT_(p_, e_, qsId_) \ - (QMPool_put(&(p_), (e_), (qsId_))) - - // Minimum required Windows version is Windows-XP or newer (0x0501) - #ifdef WINVER - #undef WINVER - #endif - #ifdef _WIN32_WINNT - #undef _WIN32_WINNT - #endif +// QF scheduler locking for Win32-QV (not needed in single-thread port) +#define QF_SCHED_STAT_ +#define QF_SCHED_LOCK_(dummy) ((void)0) +#define QF_SCHED_UNLOCK_() ((void)0) + +// QF event queue customization for Win32-QV... +#define QACTIVE_EQUEUE_WAIT_(me_) ((void)0) + +#define QACTIVE_EQUEUE_SIGNAL_(me_) \ + QPSet_insert(&QF_readySet_, (me_)->prio); \ + (void)SetEvent(QF_win32Event_) + +// QMPool operations +#define QF_EPOOL_TYPE_ QMPool +#define QF_EPOOL_INIT_(p_, poolSto_, poolSize_, evtSize_) \ + (QMPool_init(&(p_), (poolSto_), (poolSize_), (evtSize_))) +#define QF_EPOOL_EVENT_SIZE_(p_) ((uint16_t)(p_).blockSize) +#define QF_EPOOL_GET_(p_, e_, m_, qsId_) \ + ((e_) = (QEvt *)QMPool_get(&(p_), (m_), (qsId_))) +#define QF_EPOOL_PUT_(p_, e_, qsId_) (QMPool_put(&(p_), (e_), (qsId_))) +#define QF_EPOOL_USE_(ePool_) (QMPool_getUse(ePool_)) +#define QF_EPOOL_FREE_(ePool_) ((uint16_t)(ePool_)->nFree) +#define QF_EPOOL_MIN_(ePool_) ((uint16_t)(ePool_)->nMin) + +// Minimum required Windows version is Windows-XP or newer (0x0501) +#ifdef WINVER +#undef WINVER +#endif +#ifdef _WIN32_WINNT +#undef _WIN32_WINNT +#endif - #define WINVER _WIN32_WINNT_WINXP - #define _WIN32_WINNT _WIN32_WINNT_WINXP +#define WINVER _WIN32_WINNT_WINXP +#define _WIN32_WINNT _WIN32_WINNT_WINXP - #define WIN32_LEAN_AND_MEAN - #include // Win32 API +#define WIN32_LEAN_AND_MEAN +#include // Win32 API - extern QPSet QF_readySet_; - extern HANDLE QF_win32Event_; // Win32 event to signal events +extern QPSet QF_readySet_; +extern HANDLE QF_win32Event_; // Win32 event to signal events #endif // QP_IMPL diff --git a/ports/win32-qv/safe_std.h b/ports/win32-qv/safe_std.h index 984849006..d3332bb57 100644 --- a/ports/win32-qv/safe_std.h +++ b/ports/win32-qv/safe_std.h @@ -48,10 +48,10 @@ _snprintf_s(buf_, bufsiz_, _TRUNCATE, format_, __VA_ARGS__) #define PRINTF_S(format_, ...) \ - printf_s(format_, __VA_ARGS__) + (void)printf_s(format_, __VA_ARGS__) #define FPRINTF_S(fp_, format_, ...) \ - fprintf_s(fp_, format_, __VA_ARGS__) + (void)fprintf_s(fp_, format_, __VA_ARGS__) #ifdef _MSC_VER #define FREAD_S(buf_, bufsiz_, elsiz_, count_, fp_) \ @@ -86,10 +86,10 @@ if (fopen_s(&fp_, fName_, mode_) != 0) { \ snprintf(buf_, bufsiz_, format_, __VA_ARGS__) #define PRINTF_S(format_, ...) \ - printf(format_, __VA_ARGS__) + (void)printf(format_, __VA_ARGS__) #define FPRINTF_S(fp_, format_, ...) \ - fprintf(fp_, format_, __VA_ARGS__) + (void)fprintf(fp_, format_, __VA_ARGS__) #define FREAD_S(buf_, bufsiz_, elsiz_, count_, fp_) \ fread(buf_, elsiz_, count_, fp_) diff --git a/ports/win32/qp_port.h b/ports/win32/qp_port.h index e82077c1e..82a3b0eb4 100644 --- a/ports/win32/qp_port.h +++ b/ports/win32/qp_port.h @@ -35,7 +35,7 @@ #ifdef __GNUC__ - // no-return function specifier (GCC-ARM compiler) + // no-return function specifier (GCC compiler) #define Q_NORETURN __attribute__ ((noreturn)) void #elif (defined _MSC_VER) @@ -64,6 +64,9 @@ #endif +// static assertion (C11 Standard) +#define Q_ASSERT_STATIC(expr_) _Static_assert((expr_), "QP static assert") + // QActive event queue, os-type, and thread types #define QACTIVE_EQUEUE_TYPE QEQueue #define QACTIVE_OS_OBJ_TYPE void* @@ -114,45 +117,47 @@ void QF_onClockTick(void); #ifdef QP_IMPL - // QF scheduler locking for Win32 (not used at this point, see NOTE2) - #define QF_SCHED_STAT_ - #define QF_SCHED_LOCK_(dummy) ((void)0) - #define QF_SCHED_UNLOCK_() ((void)0) - - // QF event queue customization for Win32... - #define QACTIVE_EQUEUE_WAIT_(me_) \ - while ((me_)->eQueue.frontEvt == (QEvt *)0) { \ - QF_CRIT_EXIT(); \ - (void)WaitForSingleObject((me_)->osObject, (DWORD)INFINITE); \ - QF_CRIT_ENTRY(); \ - } - - #define QACTIVE_EQUEUE_SIGNAL_(me_) \ - (void)SetEvent((me_)->osObject) - - // native QF event pool operations - #define QF_EPOOL_TYPE_ QMPool - #define QF_EPOOL_INIT_(p_, poolSto_, poolSize_, evtSize_) \ - (QMPool_init(&(p_), (poolSto_), (poolSize_), (evtSize_))) - #define QF_EPOOL_EVENT_SIZE_(p_) ((uint_fast16_t)(p_).blockSize) - #define QF_EPOOL_GET_(p_, e_, m_, qsId_) \ - ((e_) = (QEvt *)QMPool_get(&(p_), (m_), (qsId_))) - #define QF_EPOOL_PUT_(p_, e_, qsId_) \ - (QMPool_put(&(p_), (e_), (qsId_))) - - // Minimum required Windows version is Windows-XP or newer (0x0501) - #ifdef WINVER - #undef WINVER - #endif - #ifdef _WIN32_WINNT - #undef _WIN32_WINNT - #endif +// QF scheduler locking for Win32 (not used at this point, see NOTE2) +#define QF_SCHED_STAT_ +#define QF_SCHED_LOCK_(dummy) ((void)0) +#define QF_SCHED_UNLOCK_() ((void)0) + +// QF event queue customization for Win32... +#define QACTIVE_EQUEUE_WAIT_(me_) \ + while ((me_)->eQueue.frontEvt == (QEvt *)0) { \ + QF_CRIT_EXIT(); \ + (void)WaitForSingleObject((me_)->osObject, (DWORD)INFINITE); \ + QF_CRIT_ENTRY(); \ + } + +#define QACTIVE_EQUEUE_SIGNAL_(me_) \ + (void)SetEvent((me_)->osObject) + +// QMPool operations +#define QF_EPOOL_TYPE_ QMPool +#define QF_EPOOL_INIT_(p_, poolSto_, poolSize_, evtSize_) \ + (QMPool_init(&(p_), (poolSto_), (poolSize_), (evtSize_))) +#define QF_EPOOL_EVENT_SIZE_(p_) ((uint16_t)(p_).blockSize) +#define QF_EPOOL_GET_(p_, e_, m_, qsId_) \ + ((e_) = (QEvt *)QMPool_get(&(p_), (m_), (qsId_))) +#define QF_EPOOL_PUT_(p_, e_, qsId_) (QMPool_put(&(p_), (e_), (qsId_))) +#define QF_EPOOL_USE_(ePool_) (QMPool_getUse(ePool_)) +#define QF_EPOOL_FREE_(ePool_) ((uint16_t)(ePool_)->nFree) +#define QF_EPOOL_MIN_(ePool_) ((uint16_t)(ePool_)->nMin) + +// Minimum required Windows version is Windows-XP or newer (0x0501) +#ifdef WINVER +#undef WINVER +#endif +#ifdef _WIN32_WINNT +#undef _WIN32_WINNT +#endif - #define WINVER _WIN32_WINNT_WINXP - #define _WIN32_WINNT _WIN32_WINNT_WINXP +#define WINVER _WIN32_WINNT_WINXP +#define _WIN32_WINNT _WIN32_WINNT_WINXP - #define WIN32_LEAN_AND_MEAN - #include // Win32 API +#define WIN32_LEAN_AND_MEAN +#include // Win32 API #endif // QP_IMPL diff --git a/ports/win32/safe_std.h b/ports/win32/safe_std.h index 984849006..d3332bb57 100644 --- a/ports/win32/safe_std.h +++ b/ports/win32/safe_std.h @@ -48,10 +48,10 @@ _snprintf_s(buf_, bufsiz_, _TRUNCATE, format_, __VA_ARGS__) #define PRINTF_S(format_, ...) \ - printf_s(format_, __VA_ARGS__) + (void)printf_s(format_, __VA_ARGS__) #define FPRINTF_S(fp_, format_, ...) \ - fprintf_s(fp_, format_, __VA_ARGS__) + (void)fprintf_s(fp_, format_, __VA_ARGS__) #ifdef _MSC_VER #define FREAD_S(buf_, bufsiz_, elsiz_, count_, fp_) \ @@ -86,10 +86,10 @@ if (fopen_s(&fp_, fName_, mode_) != 0) { \ snprintf(buf_, bufsiz_, format_, __VA_ARGS__) #define PRINTF_S(format_, ...) \ - printf(format_, __VA_ARGS__) + (void)printf(format_, __VA_ARGS__) #define FPRINTF_S(fp_, format_, ...) \ - fprintf(fp_, format_, __VA_ARGS__) + (void)fprintf(fp_, format_, __VA_ARGS__) #define FREAD_S(buf_, bufsiz_, elsiz_, count_, fp_) \ fread(buf_, elsiz_, count_, fp_) diff --git a/qpc_8.0.5.sha1 b/qpc_8.1.0.sha1 similarity index 54% rename from qpc_8.0.5.sha1 rename to qpc_8.1.0.sha1 index 2975da166..839bc3160 100644 --- a/qpc_8.0.5.sha1 +++ b/qpc_8.1.0.sha1 @@ -1,169 +1,169 @@ -8cc1cf50aef9624decf8cd1545e9734b5c1d8191 *include/qequeue.h -3559dc235f4fe3f16015a26dbdcced1dba7beebc *include/qk.h -5fa3ee98371fd6d0c3aad09a9b121a3063143a65 *include/qmpool.h -7abdb18b633f14df58f0f2f7a4716d1d0a4e9027 *include/qp.h -86b509e3719a7867bbcaa62ff0cc3dee57ecf814 *include/qp_pkg.h -5e8038024653d7355b6e6c01bde8f156ef675faf *include/qpc.h -d1667edfd10b01fe04d57aa1384107279ce27b7d *include/qs.h -62ef68e67df263501a19f6154718218e74927d86 *include/qs_dummy.h -ce8e07b7cdb8286fdd0d276bc609566d1f149772 *include/qs_pkg.h -fa866cf8f14f6c481e9f64a1cf48bcdcf7e81240 *include/qsafe.h +30586f7f01a5bd978c0892f409b0542badbbf7ca *include/qequeue.h +bd63f14424e83d23278e3de89a18b50d77bbc2f7 *include/qk.h +58d7d73db5b4cef3e86dccd72172f28eb64b4a06 *include/qmpool.h +526688b573ce2ea503200261b45e123e9918b00c *include/qp.h +e6f3ababd17825a6bbdd9e657ec4c82b82f66fcd *include/qp_pkg.h +7dbe3b848bad7bbdca50c00d498e7d1f72ade135 *include/qpc.h +4af6a52c76cccc10889ddc029a94f2c0008f3a9f *include/qs.h +b5541f7ed4828b88020f62bd4cf2a9cbe1e20e9f *include/qs_dummy.h +681aa779359c6baabe5e5a69aabcbf0539c5b883 *include/qs_pkg.h +6054504a150163f4a478fa4212c3816469c003dc *include/qsafe.h 014aad797f8b633a2bc0e2e35efd5b3e3b2eb318 *include/qstamp.h -a29f98c426a0e1526f39c370bffb905cf6556830 *include/qv.h -c8442376dd70f1b3eff18eca46a5d3bc7d0f56a9 *include/qxk.h -5886c3a0831996cfbf7141255efb4e9f5b4d8254 *include/README.md +f818617a4860bc194926a34a1611997dd7a1cfc2 *include/qv.h +22fc043da8188d60e8cd582c4a71559ab9231b4e *include/qxk.h 7f54a0933a9dc2fcaca475919fa0067667ed99ed *src/qf/CMakeLists.txt -aeb22a94b9ad3e5958a474456f9760a205e463bb *src/qf/qep_hsm.c -917a47a6575c7c8c2747f9c4acf2094112585f41 *src/qf/qep_msm.c -2616fcd5f5abe8bc94ca3ec397d34c72de5fa4e9 *src/qf/qf_act.c -92b829f22c7bda19f98ba7c7fbaf6a1431e5e8ce *src/qf/qf_actq.c -1fa8ef33b4645f58d146c714389f60177b1d83bf *src/qf/qf_defer.c -d35d5f001e68d8f8dd63e9aa8c10d58272a674fc *src/qf/qf_dyn.c -c70f2ef1c892f3fb2e9a6781a98cdf0c7e96e989 *src/qf/qf_mem.c -16616f6f8e3ad03333ae37b8614eeb96274dd116 *src/qf/qf_ps.c -16cf334c566b1653135ef881ec3a2817ef36560b *src/qf/qf_qact.c -6fa6a664678d7bdf0f8d3df6184d79537e209177 *src/qf/qf_qeq.c -306cd804d04c8855ba8f3cf3a2f1032640fb5dab *src/qf/qf_qmact.c -751b080d7b40f216485a8de6d530c0a0d003082a *src/qf/qf_time.c +3c4c555d53aceb0ec5213e1b8b1ee2b8ad2a63d7 *src/qf/qep_hsm.c +46332e4a849d85ef4033860ac284723e850d24ec *src/qf/qep_msm.c +9040750e4f99836661986a78a6a97f6fc3a0f26b *src/qf/qf_act.c +93601569ee66aa1a8b39d791c8335f5ba73adb5b *src/qf/qf_actq.c +72ed0e6cc2f6ba2e2b7cb6343d43627a370e3830 *src/qf/qf_defer.c +8eb27f536f6b30c071dfd37ff1971a9d1d1ff0d9 *src/qf/qf_dyn.c +e45285663a81ac93b639afd0744fa2e70287af6f *src/qf/qf_mem.c +47cbf4a5a1f5cffe0128ff465c2467af1d634ddf *src/qf/qf_ps.c +2a41f266f7fb60e2deda4779e884c25a8d566e07 *src/qf/qf_qact.c +fcfe58aadf24419f89eb0ebe685e6b6b86f2a79b *src/qf/qf_qeq.c +8489d0055800cba557f0237d00e1bd3fb1063396 *src/qf/qf_qmact.c +64022ced25d11d7dccde974a6d1fa7fd789a9326 *src/qf/qf_time.c 878a737efe234f40dc8c9374acc351e510b4da1e *src/qk/CMakeLists.txt -9306097e83a7b09a23b7c9aa43db82f4fcc8dd7f *src/qk/qk.c +c8ac24ddfe457c52a8d9b27b6ae999bb503194b3 *src/qk/qk.c 68c99a2991d25df7486e57edadaa955b2bda396d *src/qs/CMakeLists.txt -fb9f15f6e5c714d3bbb594c42b60ae7c2d25c7c4 *src/qs/qs.c -fcf5873f819d8f05d12ea119de13a34f89aa78d8 *src/qs/qs_64bit.c -0560472d61afcfaf166568350b2038d4a01152ad *src/qs/qs_fp.c -f3e44eaa2c9c6f61410123b97950035907f80524 *src/qs/qs_rx.c -8c73b815861bf560fdd0497c30924d31a2b2219f *src/qs/qstamp.c -9f1b983e0b8802390cec04f02b06e01a92ca05d2 *src/qs/qutest.c +6590461625e6ed96ed5880c05e0b9a5cb46496be *src/qs/qs.c +b9682b2e5c73987ac777f10db5dd6a6fee2e59fd *src/qs/qs_64bit.c +8a96a1a0c2072cdbd49326893fdd7951e075c39e *src/qs/qs_fp.c +ac53d1332e748b589e1af3b1232dcb630e7ee027 *src/qs/qs_rx.c +d1dd4fc7efd0c7ef0fc85107a00dc822cb82bf44 *src/qs/qstamp.c +d5d6c4c69ebc05a309e21f3122cc6715d3bc566e *src/qs/qutest.c b1d2def9b8f6cde464170af7682ab0f01f4f40f6 *src/qv/CMakeLists.txt -8f2c260f9844d7462b64d8a2d271ff050b27c6fa *src/qv/qv.c -df2ddd5f32b8d108b650dda6adb200c8dd38535d *src/qxk/qxk.c -e4a82244233a24905c292c1a4760498dfa09f3ba *src/qxk/qxk_mutex.c -e0c7eb35f685f1d456b570218580784d18722af6 *src/qxk/qxk_sema.c -321fce23618670efcc8426d3a2ba0061c7c74f4e *src/qxk/qxk_xthr.c -3d5f1775cd9aab44fc518f0697f4063602a9e9e3 *ports/arm-cm/config/qp_config.h +77d7e5755081399426ae4b2f6df56c7c378ab8b9 *src/qv/qv.c +f0015a5bb9bef698dd7438d1da6172ef9f5feece *src/qxk/qxk.c +364d41c441cae7a61a9265c3c184c037f93d5b12 *src/qxk/qxk_mutex.c +3b1cf83b8cd92de9150fb0627420335e71c88287 *src/qxk/qxk_sema.c +ff53e9cb03fa3a6a2166974c1254fb811a52462f *src/qxk/qxk_xthr.c +f05f057626a23e5c90f4913a6607b6a2e4b7041d *ports/arm-cm/config/qp_config.h 80f72c5aabb72c08164f336797fd58ea3f644855 *ports/arm-cm/qk/armclang/qk_port.c -7d7dcf0a51f6e54a69fffefc9ef3c45b058e85bd *ports/arm-cm/qk/armclang/qp_port.h +55bba275743750c0926acd15d758b336fae47f46 *ports/arm-cm/qk/armclang/qp_port.h 4efebcfb83036649c8e7c6feb5a98cda01877ae9 *ports/arm-cm/qk/armclang/qs_port.h ba9b0a55cd6b66b851bc1f98dd17f92907f993fb *ports/arm-cm/qk/gnu/qk_port.c -ad8dbcc434e3a334c2de4f617cfbd691ff818d0e *ports/arm-cm/qk/gnu/qp_port.h +34e3dff9091b5368c7f8421b89788729f05864ce *ports/arm-cm/qk/gnu/qp_port.h 4efebcfb83036649c8e7c6feb5a98cda01877ae9 *ports/arm-cm/qk/gnu/qs_port.h 4e86718c62fd78bc866ffdf855aa1e5a186916b2 *ports/arm-cm/qk/gnu/syscalls.c 27b02a4aaf4abfaeca5d5fec6e259e662d259ff6 *ports/arm-cm/qk/iar/qk_port.c -53647375f4f40d4f49bd21445f970a32b04bec9d *ports/arm-cm/qk/iar/qp_port.h +b57277166a8b77c9c5c3e253088aeb7f2f9c45cd *ports/arm-cm/qk/iar/qp_port.h 4efebcfb83036649c8e7c6feb5a98cda01877ae9 *ports/arm-cm/qk/iar/qs_port.h -b77b3c5bbbf4b4b00ff8599d252dd937de944f5f *ports/arm-cm/qv/armclang/qp_port.h +e43828d0dc86bde43d058497b6b6290f44c70812 *ports/arm-cm/qv/armclang/qp_port.h 4efebcfb83036649c8e7c6feb5a98cda01877ae9 *ports/arm-cm/qv/armclang/qs_port.h ec2b62f28dd14b8f65b06c0e161f4d526da89cf3 *ports/arm-cm/qv/armclang/qv_port.c -b77b3c5bbbf4b4b00ff8599d252dd937de944f5f *ports/arm-cm/qv/gnu/qp_port.h +e43828d0dc86bde43d058497b6b6290f44c70812 *ports/arm-cm/qv/gnu/qp_port.h 4efebcfb83036649c8e7c6feb5a98cda01877ae9 *ports/arm-cm/qv/gnu/qs_port.h 7cdac5d8c505c628acbac8a6e76e216eb24580a5 *ports/arm-cm/qv/gnu/qv_port.c 4e86718c62fd78bc866ffdf855aa1e5a186916b2 *ports/arm-cm/qv/gnu/syscalls.c -f54d83ca93ec2d48869e4276b5e3afbe9f5d70d7 *ports/arm-cm/qv/iar/qp_port.h +68245cbc0dafd0d34086a5b69af14299e56f6b78 *ports/arm-cm/qv/iar/qp_port.h 4efebcfb83036649c8e7c6feb5a98cda01877ae9 *ports/arm-cm/qv/iar/qs_port.h bc9548f9c3b925cce74f388c5a01e67c653cf2bf *ports/arm-cm/qv/iar/qv_port.c -4bc8003db91caac2aacce9cd78751cc12fcd6b70 *ports/arm-cm/qxk/armclang/qp_port.h +3dd0a90ea8749d9277bb1faf14060942fa9f544b *ports/arm-cm/qxk/armclang/qp_port.h 4efebcfb83036649c8e7c6feb5a98cda01877ae9 *ports/arm-cm/qxk/armclang/qs_port.h -771bf5841e23ab0d8e9ea5f03600d165096c7e16 *ports/arm-cm/qxk/armclang/qxk_port.c -82471a74b1920976ccbbb300ceeac75f26e8ddb1 *ports/arm-cm/qxk/gnu/qp_port.h +1317065e30364d873f7d27feaf43636407e2b107 *ports/arm-cm/qxk/armclang/qxk_port.c +7b5000d75605a6664f895c9d184b77586b65ae20 *ports/arm-cm/qxk/gnu/qp_port.h 4efebcfb83036649c8e7c6feb5a98cda01877ae9 *ports/arm-cm/qxk/gnu/qs_port.h -7f9071174b3401b4cb6437166bd31d34fa649eb1 *ports/arm-cm/qxk/gnu/qxk_port.c +5b3de2bcce82d27e07a22c1fdbc5cbc02fd536fc *ports/arm-cm/qxk/gnu/qxk_port.c 4e86718c62fd78bc866ffdf855aa1e5a186916b2 *ports/arm-cm/qxk/gnu/syscalls.c -8ca7b08b3b31b6d3b312c963bd1c038a43cf9ba6 *ports/arm-cm/qxk/iar/qp_port.h +c04bf3f9cd04d8b297db7de4edcd2e5028818008 *ports/arm-cm/qxk/iar/qp_port.h 4efebcfb83036649c8e7c6feb5a98cda01877ae9 *ports/arm-cm/qxk/iar/qs_port.h -0d1fa81b77021804e0f31b3762bcbbf0c4af8076 *ports/arm-cm/qxk/iar/qxk_port.c -7c5c264e508aff8ddc55a4bbbc83a83137473a57 *ports/arm-cm/qutest/qp_port.h +ce6ac5db67a07c5574ab69a84cc81158edaae3f2 *ports/arm-cm/qxk/iar/qxk_port.c +5c83df8aae7e49df1e221a8875c2a48b291b71e5 *ports/arm-cm/qutest/qp_port.h 4efebcfb83036649c8e7c6feb5a98cda01877ae9 *ports/arm-cm/qutest/qs_port.h -56b0de645e0cc33cf6fa901c2949dd8c5d7cffe5 *ports/arm-cr/config/qp_config.h -044cea7a80af05fa6148c7224e47582d032ae0c0 *ports/arm-cr/qk/gnu/qp_port.h +bf3b3291715029baad44b0ff73afd6b1829aa56a *ports/arm-cr/config/qp_config.h +4d6e639f2c7fcc4b863aad6cf316b5c2b70d6a02 *ports/arm-cr/qk/gnu/qp_port.h 4efebcfb83036649c8e7c6feb5a98cda01877ae9 *ports/arm-cr/qk/gnu/qs_port.h -05a3b8ad45dd441eae075d9675b3dd5bde0c4df6 *ports/arm-cr/qk/iar/qp_port.h +93b08d8a730fb7146a7650bd37e6608a2d6288a1 *ports/arm-cr/qk/iar/qp_port.h 4efebcfb83036649c8e7c6feb5a98cda01877ae9 *ports/arm-cr/qk/iar/qs_port.h -e21f6db0a62182eab29d7a5a6c060e97c527ceec *ports/arm-cr/qk/ti/qp_port.h +177a1e096629289b790777d6da35e78fcd5c6d85 *ports/arm-cr/qk/ti/qp_port.h 4efebcfb83036649c8e7c6feb5a98cda01877ae9 *ports/arm-cr/qk/ti/qs_port.h -9b3879913f425dd74d51aa3e6a2252bd40af3388 *ports/arm-cr/qv/gnu/qp_port.h +94dced5e19f3144fe8c621f4e09f6c530faeef38 *ports/arm-cr/qv/gnu/qp_port.h 4efebcfb83036649c8e7c6feb5a98cda01877ae9 *ports/arm-cr/qv/gnu/qs_port.h -c18de9c3dc77dad54bd1478f365013f02c832bc7 *ports/arm-cr/qv/iar/qp_port.h +9843938d8018d0a97752571fde031eb477fe0173 *ports/arm-cr/qv/iar/qp_port.h 4efebcfb83036649c8e7c6feb5a98cda01877ae9 *ports/arm-cr/qv/iar/qs_port.h -92348af8cbf8b1d167075d8abab1b58aff12f668 *ports/arm-cr/qv/ti/qp_port.h +7c3ba51cfe06be8dda7404366f03e293e0110498 *ports/arm-cr/qv/ti/qp_port.h 4efebcfb83036649c8e7c6feb5a98cda01877ae9 *ports/arm-cr/qv/ti/qs_port.h 362fdf1da29aa89547831ed197346385530b7d9a *ports/msp430/qk/qp_port.h becde567ef7edc053a98b11f210d956505e30314 *ports/msp430/qk/qs_port.h 81be47c003d6ad73d3e2f56c286a3740cd69df81 *ports/msp430/qv/qp_port.h becde567ef7edc053a98b11f210d956505e30314 *ports/msp430/qv/qs_port.h -4e4d5daba400266d21f6b9a8dfdac92d4da1ab79 *ports/msp430/qutest/qp_port.h +38a4987e820f91f3a3f45db72d47f3b7f5d7b2b6 *ports/msp430/qutest/qp_port.h becde567ef7edc053a98b11f210d956505e30314 *ports/msp430/qutest/qs_port.h -8dcb7a9e8d01fef9cea3ae9c2d7e6dbc979138b4 *ports/config/qp_config.h +f63a7074c2193ad818e6518f4a09fc435864bc6b *ports/config/qp_config.h 2ee7f5594f6121705bbc57145175b5c5867e0070 *ports/embos/CMakeLists.txt -9b32d32c2389efaf2888539460b9a538a124d169 *ports/embos/qf_port.c -dec344db269481b0244d9e2a845d9a4d86cf6c06 *ports/embos/qp_port.h +74f3e4dc7f02136e2f732dd0b24903fbd6353295 *ports/embos/qf_port.c +7ed882f30c958296972abd9fb9253bc1ae9a299c *ports/embos/qp_port.h 4efebcfb83036649c8e7c6feb5a98cda01877ae9 *ports/embos/qs_port.h 4e86718c62fd78bc866ffdf855aa1e5a186916b2 *ports/embos/syscalls.c e65838e1764bd6b4eb73025be1e8116ac28247b2 *ports/freertos/CMakeLists.txt -0c3778f06097da4f0458b584c2fd2ec2232b760f *ports/freertos/qf_port.c -b2d541fb7bb1721b7e1225862620e63680adf24a *ports/freertos/qp_port.h +1a17ab97044118f580024fe3515c61386f32b0b3 *ports/freertos/qf_port.c +f8395c15aca07b5a741de8aba846affc297045c0 *ports/freertos/qp_port.h 4efebcfb83036649c8e7c6feb5a98cda01877ae9 *ports/freertos/qs_port.h 4e86718c62fd78bc866ffdf855aa1e5a186916b2 *ports/freertos/syscalls.c a01e1f6d49ce056ac4e130d54ae4724fda2ebf32 *ports/threadx/CMakeLists.txt -8a0be00d32308dbd1ef4c7fb6f937d6abd28ef86 *ports/threadx/qf_port.c -8ad6f85fc704c8bdf2b7b87c7ef59ee363fd1045 *ports/threadx/qp_port.h +176564d3b8c0bbdaa3863ce9e154a3addbb6d105 *ports/threadx/qf_port.c +8bea877e31420532344a5e16c858376715648f62 *ports/threadx/qp_port.h 4efebcfb83036649c8e7c6feb5a98cda01877ae9 *ports/threadx/qs_port.h 847cd324346d1d6c9c81422e99045442edcc7f64 *ports/threadx/README.md 4e86718c62fd78bc866ffdf855aa1e5a186916b2 *ports/threadx/syscalls.c d9b6e1ca7a0215a3e141ae43970781d0f4d0d08f *ports/uc-os2/CMakeLists.txt -80d526d65a8ad67ff10d3649ba5be7a91fc5e77f *ports/uc-os2/qf_port.c -bd48b4f7130bdd74a3fd0bad32964e24f292ad21 *ports/uc-os2/qp_port.h +9842862f6b5e30df88baf88bebe83f211f8490dd *ports/uc-os2/qf_port.c +977daadd90030aa528c2a557351eb431ee8a24db *ports/uc-os2/qp_port.h 4efebcfb83036649c8e7c6feb5a98cda01877ae9 *ports/uc-os2/qs_port.h 4e86718c62fd78bc866ffdf855aa1e5a186916b2 *ports/uc-os2/syscalls.c 4a5da9508e2012b2ca3943b87134163e558964cc *ports/qep-only/CMakeLists.txt -3839e0342aa416d1662e421b04991e736961f070 *ports/qep-only/qp_port.h -59c583150a4192afe3dcb8f7afceb7c9796009b9 *ports/qep-only/safe_std.h +d06ad61ba9c86b038538b0090034c52252e4a368 *ports/qep-only/qp_port.h +5a58249c986a9fb10fba89f3de6021603027cc2c *ports/qep-only/qs_port.h +5c9f762b06e3324a581cbc0aab002dadfa22990a *ports/qep-only/safe_std.h 5d7914dfaf44a9c2552afdd5d8de4cfc3ebbc22a *ports/posix/CMakeLists.txt c8aca1a6b3cdb5cd01f164ab79e15c54b90e877d *ports/posix/qf_port.c -eba316c153b2503e6706ae0d86400adf386395cb *ports/posix/qp_port.h +0fbfc94e6314b396887f2e8eac304603df2fe1d1 *ports/posix/qp_port.h 25f52129dee6b46c63c65ffcc990e71e89b67823 *ports/posix/qs_port.c 88b29cc261241229a3918aa05538f96dfce167c9 *ports/posix/qs_port.h 6e33b2e5092d117f58c47b632c59420f382ac39f *ports/posix/README.md -59c583150a4192afe3dcb8f7afceb7c9796009b9 *ports/posix/safe_std.h +5c9f762b06e3324a581cbc0aab002dadfa22990a *ports/posix/safe_std.h 039b1e4066eb7eeac3233070ad6aa2cd9f6d1c69 *ports/posix-qv/CMakeLists.txt 50a87db24c8c461d644c19e5e5f714b1a8c1f048 *ports/posix-qv/qf_port.c -7fcd97fa10d6859e49ab68385bc101f721276405 *ports/posix-qv/qp_port.h +d61b2fa2b8916c15478bcfe3485918152242cfa8 *ports/posix-qv/qp_port.h 25f52129dee6b46c63c65ffcc990e71e89b67823 *ports/posix-qv/qs_port.c 88b29cc261241229a3918aa05538f96dfce167c9 *ports/posix-qv/qs_port.h ab829eb3deed2bc84b3581610f1664777afd3841 *ports/posix-qv/README.md -59c583150a4192afe3dcb8f7afceb7c9796009b9 *ports/posix-qv/safe_std.h +5c9f762b06e3324a581cbc0aab002dadfa22990a *ports/posix-qv/safe_std.h 1ecb2095e8de486c8111a420b5511a4ea0cb097c *ports/posix-qutest/CMakeLists.txt -5e78f59222738a96e6043d0026c620b148c21b49 *ports/posix-qutest/qp_port.h +3e2409b77e11eee792c2fa20ed892242bba48d0f *ports/posix-qutest/qp_port.h 88b29cc261241229a3918aa05538f96dfce167c9 *ports/posix-qutest/qs_port.h 30690074b6dd81acd9ea9c6768a5814f92865001 *ports/posix-qutest/qutest_port.c -59c583150a4192afe3dcb8f7afceb7c9796009b9 *ports/posix-qutest/safe_std.h +5c9f762b06e3324a581cbc0aab002dadfa22990a *ports/posix-qutest/safe_std.h cfea17ea9ab718e9e4f506e90c4b2fc8c1fea858 *ports/win32/CMakeLists.txt f8f41139772ea01df773fd2186a6afa87803e2ba *ports/win32/qf_port.c -4606d14118db2b441acbcbe59addbe764e022bd4 *ports/win32/qp_port.h +84817f3a21e55efb294f27cb34eb701adfd843e0 *ports/win32/qp_port.h 48316693e66912c134ba41316cce8ff89caa497a *ports/win32/qs_port.c 5a58249c986a9fb10fba89f3de6021603027cc2c *ports/win32/qs_port.h 5680a84e4238d723ef9b2d99d85913c4ef8d389a *ports/win32/qwin_gui.c a49f284a53d6dd128390ae9eb536ef5756cc577d *ports/win32/qwin_gui.h 3781ccdce31dea9d08493a801926eb278950786f *ports/win32/README.md -59c583150a4192afe3dcb8f7afceb7c9796009b9 *ports/win32/safe_std.h +5c9f762b06e3324a581cbc0aab002dadfa22990a *ports/win32/safe_std.h 8d589868e287ceb185b4837064c80d4eed8e1a44 *ports/win32-qv/CMakeLists.txt 1f6699f3ca7dab5dc0a166eefd8811f3c24ea15e *ports/win32-qv/qf_port.c -384b0c84fd0bee1a1a8cf1514f215ed9e17b88d6 *ports/win32-qv/qp_port.h +f2d9f5e54882a4754b679032c3ace551d790483c *ports/win32-qv/qp_port.h 48316693e66912c134ba41316cce8ff89caa497a *ports/win32-qv/qs_port.c 5a58249c986a9fb10fba89f3de6021603027cc2c *ports/win32-qv/qs_port.h 5680a84e4238d723ef9b2d99d85913c4ef8d389a *ports/win32-qv/qwin_gui.c a49f284a53d6dd128390ae9eb536ef5756cc577d *ports/win32-qv/qwin_gui.h b57cec85e2fe5c261270f68acc3ae440802a62bd *ports/win32-qv/README.md -59c583150a4192afe3dcb8f7afceb7c9796009b9 *ports/win32-qv/safe_std.h +5c9f762b06e3324a581cbc0aab002dadfa22990a *ports/win32-qv/safe_std.h a04f13d2d9f24ef71d95f997d87f8a3ba9862e45 *ports/win32-qutest/CMakeLists.txt -6591c1cdb65f107d2703f7aff172ccaf03216896 *ports/win32-qutest/qp_port.h +9d549790f059adcd316fdb5f374a3d64fab057bd *ports/win32-qutest/qp_port.h 5a58249c986a9fb10fba89f3de6021603027cc2c *ports/win32-qutest/qs_port.h c3d90fffd08d165b106292f500f0af13b7fb2d8f *ports/win32-qutest/qutest_port.c -59c583150a4192afe3dcb8f7afceb7c9796009b9 *ports/win32-qutest/safe_std.h +5c9f762b06e3324a581cbc0aab002dadfa22990a *ports/win32-qutest/safe_std.h 848a30efa3274ff30fb72059f926fe7963ab2321 *zephyr/CMakeLists.txt 10764710e545dd4d2ce0ddf032711df7f9191937 *zephyr/Kconfig 2eb2a922e18b4760a68151ebee1b6282d20b4692 *zephyr/module.yml -a3b6860c93400cd3a91cd68a29cbdafbe80c2af0 *zephyr/qf_port.c +d96f3ff114d09259b3d40818b0072ddd0c5c93f8 *zephyr/qf_port.c 109c291df50110f185adc17bcdf8becc0a79346c *zephyr/qp-zephyr.jpg -05c2b569d54153d4093171ad48755a4a83266598 *zephyr/qp_port.h +65219bf3c1008f4210bc7862fa6ef455e42f7f67 *zephyr/qp_port.h 4efebcfb83036649c8e7c6feb5a98cda01877ae9 *zephyr/qs_port.h ab487299b61fea18e2926913048a4498b3bfa4b4 *zephyr/README.md diff --git a/sha1.bat b/sha1.bat index febb415ae..ec8020547 100644 --- a/sha1.bat +++ b/sha1.bat @@ -1,20 +1,20 @@ @setlocal -set VERSION=8.0.5 +set FNAME=qpc_8.1.0.sha1 :: usage -@echo Usage: qpc_sha1 [gen] +@echo Usage: sha1 [gen] @echo examples of use: -@echo qpc_sha1 : check the sha1 sums in the file qpc_%VERSION%.sha1 -@echo qpc_sha1 gen : generate the sha1 file qpc_%VERSION%.sha1 +@echo sha1 : check the sha1 sums in the file %FNAME% +@echo sha1 gen : generate the sha1 file %FNAME% @echo. @if NOT "%1"=="gen" ( -sha1sum --check --warn qpc_%VERSION%.sha1 +sha1sum --check --warn %FNAME% goto end ) -@echo generating qpc_%VERSION%.sha1... +@echo generating %FNAME%... @sha1sum ^ include/* ^ src/qf/* src/qk/* src/qs/* src/qv/* src/qxk/* ^ @@ -36,7 +36,7 @@ goto end ports/posix/* ports/posix-qv/* ports/posix-qutest/* ^ ports/win32/* ports/win32-qv/* ports/win32-qutest/* ^ zephyr/* ^ - > qpc_%VERSION%.sha1 + > %FNAME% qclean @echo done diff --git a/src/README.md b/src/README.md deleted file mode 100644 index 2c26380d2..000000000 --- a/src/README.md +++ /dev/null @@ -1,45 +0,0 @@ -# Files Missing from the QP/C GPL Distribution -Due to the widespread non-compliance with the GPL, as well as infringement -on the [dual-licensing model of QP frameworks][Lic], the following files -have been **removed from the open-source GPL distribution**: - -``` -qpc -| -+---include -| qs.h -| qs_pkg.h -| qxk.h -| -\---src - | - +---qs - | qs.c - | qs_64bit.c - | qs_fp.c - | qs_rx.c - | qutest.c - | - \---qxk - qxk.c - qxk_mutex.c - qxk_sema.c - qxk_xthr.c -``` - -> NOTE: These files are available to the [commercial licensees][Cust] with -the active Support Term. Please contact [Quantum Leaps technical support][Sup] -to get the complete QP/C framework distribution. - -# QP/C Framework Evaluation -To request **evaluation** of the complete QP/C framework, please contact -Quantum Leaps at: -- https://www.state-machine.com/contact - -# Quantum Leaps Licensing: -To learn more about the open source and commercial licensing options: -- https://www.state-machine.com/licensing - - [Lic]: - [Cust]: - [Sup]: diff --git a/src/qf/qep_hsm.c b/src/qf/qep_hsm.c index a1005aaca..60453514a 100644 --- a/src/qf/qep_hsm.c +++ b/src/qf/qep_hsm.c @@ -26,8 +26,8 @@ // // //============================================================================ -#define QP_IMPL // this is QP implementation #include "qp_port.h" // QP port +#include "qp_pkg.h" // QP package-scope interface #include "qsafe.h" // QP Functional Safety (FuSa) Subsystem #ifdef Q_SPY // QS software tracing enabled? #include "qs_port.h" // QS port @@ -44,8 +44,8 @@ Q_DEFINE_THIS_MODULE("qep_hsm") //! @cond INTERNAL -// immutable events corresponding to the reserved signals. -static QEvt const l_reservedEvt_[4] = { +// array of immutable events corresponding to the reserved signals +static QEvt const l_resEvt_[4] = { QEVT_INITIALIZER(Q_EMPTY_SIG), QEVT_INITIALIZER(Q_ENTRY_SIG), QEVT_INITIALIZER(Q_EXIT_SIG), @@ -58,8 +58,6 @@ static QEvt const l_reservedEvt_[4] = { //! @cond INTERNAL // internal helper macro to pass a reserved event into the state handler -#define QHSM_RESERVED_EVT_(state_, sig_) \ - ((*(state_))(me, &l_reservedEvt_[(sig_)])) #ifdef Q_SPY // helper macro to trace state action (entry/exit) @@ -136,6 +134,8 @@ static QEvt const l_reservedEvt_[4] = { //! @endcond //============================================================================ +// static and private helper function prototypes... + //! @private @memberof QHsm static int_fast8_t QHsm_tran_simple_( QAsm * const me, @@ -155,7 +155,7 @@ static void QHsm_enter_target_( int_fast8_t const depth, uint_fast8_t const qsId); -//............................................................................ +//============================================================================ //! @protected @memberof QHsm void QHsm_ctor(QHsm * const me, QStateHandler const initial) @@ -168,17 +168,16 @@ void QHsm_ctor(QHsm * const me, ,&QHsm_getStateHandler_ #endif }; - // do not call the QAsm_ctor() here - me->super.vptr = &vtable; - me->super.state.fun = Q_STATE_CAST(&QHsm_top); - me->super.temp.fun = initial; + // no need to call the superclass' constructor QAsm_ctor() here + me->super.vptr = &vtable; // QHsm class' VTABLE + me->super.state.fun = Q_STATE_CAST(&QHsm_top); // the current state (top) + me->super.temp.fun = initial; // the initial tran. handler } //............................................................................ //! @protected @memberof QHsm -QState QHsm_top(QHsm const * const me, - QEvt const * const e) -{ +QState QHsm_top(QHsm const * const me, QEvt const * const e) { + // the top state handler implementation Q_UNUSED_PAR(me); Q_UNUSED_PAR(e); return Q_RET_IGNORED; // the top state ignores all events @@ -186,30 +185,35 @@ QState QHsm_top(QHsm const * const me, //............................................................................ //! @private @memberof QHsm -void QHsm_init_( - QAsm * const me, +void QHsm_init_(QAsm * const me, void const * const e, uint_fast8_t const qsId) { - // produce QS dictionary for QHsm_top() #ifdef Q_SPY + // produce QS dictionary for QHsm_top handler... + // NOTE: the QHsm_top dictionary needs to be produced only once + // and not every time QHsm_init_() is called. QS_CRIT_STAT QS_CRIT_ENTRY(); - bool isDone = true; - if ((QS_priv_.flags & 0x01U) == 0U) { - QS_priv_.flags |= 0x01U; - isDone = false; + bool toDo = false; // assume that dictionary not produced + if ((QS_priv_.flags & 0x01U) == 0U) { // dictionary not produced yet? + QS_priv_.flags |= 0x01U; // mark the QHsm_top dictionary as produced + toDo = true; } QS_CRIT_EXIT(); - if (!isDone) { + if (toDo) { // need to produce the dictionary? QS_FUN_DICTIONARY(&QHsm_top); } #else Q_UNUSED_PAR(qsId); -#endif // def Q_SPY +#endif // Q_SPY QStateHandler const s = me->state.fun; // current state + + // current state must be initialized to QHsm_top in QHsm_ctor() Q_REQUIRE_LOCAL(200, s == Q_STATE_CAST(&QHsm_top)); + + // temp contains the top-most initial tran. handler, which must be valid Q_REQUIRE_LOCAL(210, me->temp.fun != Q_STATE_CAST(0)); // execute the top-most initial tran. @@ -217,30 +221,40 @@ void QHsm_init_( // the top-most initial tran. must be taken Q_ASSERT_LOCAL(240, r == Q_RET_TRAN); + + // the top-most initial tran. must set the target in temp Q_ASSERT_LOCAL(250, me->temp.fun != Q_STATE_CAST(0)); - QS_TRAN_SEG_(QS_QEP_STATE_INIT, s, me->temp.fun); + QS_TRAN_SEG_(QS_QEP_STATE_INIT, s, me->temp.fun); // output QS record // drill down into the state hierarchy with initial transitions... - QStateHandler path[QHSM_MAX_NEST_DEPTH_]; // tran. entry path array - int_fast8_t ip = -1; // entry path index (one below [0]) + QStateHandler path[QHSM_MAX_NEST_DEPTH_]; // entry path array + + int_fast8_t ip = -1; // path index & fixed loop bound (one below [0]) do { - ++ip; // fixed loop bound + ++ip; + + // the entry path index must not overflow the allocated array Q_INVARIANT_LOCAL(260, ip < QHSM_MAX_NEST_DEPTH_); + // the initial tran. must set target in temp Q_ASSERT_LOCAL(270, me->temp.fun != Q_STATE_CAST(0)); + path[ip] = me->temp.fun; // store the entry path - (void)QHSM_RESERVED_EVT_(me->temp.fun, Q_EMPTY_SIG); + + // find the superstate of 'm_temp.fun', ignore result + (void)(*me->temp.fun)(me, &l_resEvt_[Q_EMPTY_SIG]); } while (me->temp.fun != s); + // enter the target (possibly recursively) by initial trans. QHsm_enter_target_(me, &path[0], ip, qsId); - QS_TOP_INIT_(QS_QEP_INIT_TRAN, path[0]); + QS_TOP_INIT_(QS_QEP_INIT_TRAN, path[0]); // output QS record me->state.fun = path[0]; // change the current active state #ifndef Q_UNSAFE // establish stable state configuration - me->temp.uint = (uintptr_t)~me->state.uint; + me->temp.uint = QP_DIS_UPDATE(uintptr_t, me->state.uint); #else Q_UNUSED_PAR(r); #endif @@ -248,8 +262,7 @@ void QHsm_init_( //............................................................................ //! @private @memberof QHsm -void QHsm_dispatch_( - QAsm * const me, +void QHsm_dispatch_(QAsm * const me, QEvt const * const e, uint_fast8_t const qsId) { @@ -257,78 +270,88 @@ void QHsm_dispatch_( Q_UNUSED_PAR(qsId); #endif - Q_INVARIANT_LOCAL(300, me->state.uint == (uintptr_t)(~me->temp.uint)); + // this state machine must be in a stable state configuration + // NOTE: stable state configuration is established after every RTC step. + Q_INVARIANT_LOCAL(300, + QP_DIS_VERIFY(uintptr_t, me->state.uint, me->temp.uint)); + // the event to be dispatched must be valid Q_REQUIRE_LOCAL(310, e != (QEvt *)0); QStateHandler s = me->state.fun; // current state QS_CRIT_STAT - QS_TRAN0_(QS_QEP_DISPATCH, s); + QS_TRAN0_(QS_QEP_DISPATCH, s); // output QS record // process the event hierarchically... - QStateHandler path[QHSM_MAX_NEST_DEPTH_]; + QStateHandler path[QHSM_MAX_NEST_DEPTH_]; // entry path array path[0] = s; // save current state me->temp.fun = s; - QState r; - int_fast8_t ip = QHSM_MAX_NEST_DEPTH_; + QState r; // state handler return value + int_fast8_t ip = QHSM_MAX_NEST_DEPTH_; // path index & fixed loop bound do { - --ip; // fixed loop bound + --ip; + + // the entry path index must stay in range of the path[] array Q_INVARIANT_LOCAL(340, ip >= 0); - s = me->temp.fun; + s = me->temp.fun; // set s to the superstate set previously path[ip] = s; // store the path to potential tran. source - r = (*s)(me, e); // invoke state handler s + r = (*s)(me, e); // try to handle event e in state s if (r == Q_RET_UNHANDLED) { // unhandled due to a guard? - QS_TRAN_ACT_(QS_QEP_UNHANDLED, s); - r = QHSM_RESERVED_EVT_(s, Q_EMPTY_SIG); // superstate of s + QS_TRAN_ACT_(QS_QEP_UNHANDLED, s); // output QS record + + // find the superstate of 's' + r = (*s)(me, &l_resEvt_[Q_EMPTY_SIG]); } - } while (r == Q_RET_SUPER); + } while (r == Q_RET_SUPER); // loop as long as superstate returned - if (r >= Q_RET_TRAN) { // tran. (regular or history) taken? + if (r == Q_RET_HANDLED) { // did the last handler handle event e? + QS_TRAN0_(QS_QEP_INTERN_TRAN, s); // output QS record + } + else if ((r == Q_RET_TRAN) || (r == Q_RET_TRAN_HIST)) { // tran. taken? + // tran. must set temp to the target state Q_ASSERT_LOCAL(350, me->temp.fun != Q_STATE_CAST(0)); #ifdef Q_SPY if (r == Q_RET_TRAN_HIST) { // tran. to history? - QS_TRAN_SEG_(QS_QEP_TRAN_HIST, s, me->temp.fun); + QS_TRAN_SEG_(QS_QEP_TRAN_HIST, s, me->temp.fun);//output QS record } -#endif // Q_SPY +#endif - path[0] = me->temp.fun; // save tran. target + path[0] = me->temp.fun; // save tran. target in path[0] // exit current state to tran. source... for (int_fast8_t iq = QHSM_MAX_NEST_DEPTH_ - 1; iq > ip; --iq) { - // exit from path[iq] - if (QHSM_RESERVED_EVT_(path[iq], Q_EXIT_SIG) == Q_RET_HANDLED) { - QS_STATE_ACT_(QS_QEP_STATE_EXIT, path[iq]); + // exit from 'path[iq]' + if ((*path[iq])(me, &l_resEvt_[Q_EXIT_SIG]) == Q_RET_HANDLED) { + QS_STATE_ACT_(QS_QEP_STATE_EXIT, path[iq]); //output QS record } } path[2] = s; // save tran. source // take the tran... - ip = QHsm_tran_simple_(me, &path[0], qsId); + ip = QHsm_tran_simple_(me, &path[0], qsId); // try simple tran. first if (ip < -1) { // not a simple tran.? ip = QHsm_tran_complex_(me, &path[0], qsId); } + // enter the target (possibly recursively) by initial trans. QHsm_enter_target_(me, &path[0], ip, qsId); - QS_TRAN_END_(QS_QEP_TRAN, s, path[0]); - } - else if (r == Q_RET_HANDLED) { - QS_TRAN0_(QS_QEP_INTERN_TRAN, s); + QS_TRAN_END_(QS_QEP_TRAN, s, path[0]); // output QS record } - else if (r == Q_RET_IGNORED) { - QS_TRAN0_(QS_QEP_IGNORED, me->state.fun); + else if (r == Q_RET_IGNORED) { // was event e ignored? + QS_TRAN0_(QS_QEP_IGNORED, me->state.fun); // output QS record } else { - Q_ERROR_LOCAL(360); + Q_ERROR_LOCAL(360); // last state handler returned impossible value } me->state.fun = path[0]; // change the current active state #ifndef Q_UNSAFE // establish stable state configuration - me->temp.uint = (uintptr_t)~me->state.uint; + me->temp.uint = QP_DIS_UPDATE(uintptr_t, me->state.uint); #endif } @@ -350,15 +373,19 @@ static int_fast8_t QHsm_tran_simple_( // (a) check source==target (tran. to self)... if (s == t) { - // exit source s (external tran. semantics) - if (QHSM_RESERVED_EVT_(s, Q_EXIT_SIG) == Q_RET_HANDLED) { - QS_STATE_ACT_(QS_QEP_STATE_EXIT, s); + // exit source 's' (external tran. semantics) + if ((*s)(me, &l_resEvt_[Q_EXIT_SIG]) == Q_RET_HANDLED) { + QS_STATE_ACT_(QS_QEP_STATE_EXIT, s); // output QS record } } - else { - // find superstate of target - QState const r = QHSM_RESERVED_EVT_(t, Q_EMPTY_SIG); + else { // not a tran. to self + // find superstate of target in 't' + QState const r =(*t)(me, &l_resEvt_[Q_EMPTY_SIG]); + + // state handler t must return the superstate for Q_EMPTY_SIG Q_ASSERT_LOCAL(440, r == Q_RET_SUPER); + + // state handler t must set temp to the superstate Q_ASSERT_LOCAL(450, me->temp.fun != Q_STATE_CAST(0)); #ifdef Q_UNSAFE Q_UNUSED_PAR(r); @@ -367,23 +394,23 @@ static int_fast8_t QHsm_tran_simple_( // (b) check source==target->super... t = me->temp.fun; if (s != t) { - // find superstate of source - (void)QHSM_RESERVED_EVT_(s, Q_EMPTY_SIG); + // find superstate of source 's', ignore the result + (void)(*s)(me, &l_resEvt_[Q_EMPTY_SIG]); // (c) check source->super==target->super... if (me->temp.fun == t) { - // exit source s - if (QHSM_RESERVED_EVT_(s, Q_EXIT_SIG) == Q_RET_HANDLED) { - QS_STATE_ACT_(QS_QEP_STATE_EXIT, s); + // exit source 's' + if ((*s)(me, &l_resEvt_[Q_EXIT_SIG]) == Q_RET_HANDLED) { + QS_STATE_ACT_(QS_QEP_STATE_EXIT, s); // output QS record } } // (d) check source->super==target... else if (me->temp.fun == path[0]) { - // exit source s - if (QHSM_RESERVED_EVT_(s, Q_EXIT_SIG) == Q_RET_HANDLED) { - QS_STATE_ACT_(QS_QEP_STATE_EXIT, s); + // exit source 's' + if ((*s)(me, &l_resEvt_[Q_EXIT_SIG]) == Q_RET_HANDLED) { + QS_STATE_ACT_(QS_QEP_STATE_EXIT, s); // output QS record } - ip = -1; // do not enter the target + ip = -1; // set entry path index not to enter the target } else { path[1] = t; // save the superstate of target @@ -391,15 +418,14 @@ static int_fast8_t QHsm_tran_simple_( } } } - // # levels in path[] for QHsm_enter_target_() + // return: # levels in path[] for QHsm_enter_target_() // or indication to call QHsm_tran_complex_() return ip; } //............................................................................ //! @private @memberof QHsm -static int_fast8_t QHsm_tran_complex_( - QAsm * const me, +static int_fast8_t QHsm_tran_complex_(QAsm * const me, QStateHandler * const path, uint_fast8_t const qsId) { @@ -407,7 +433,7 @@ static int_fast8_t QHsm_tran_complex_( Q_UNUSED_PAR(qsId); #endif - QStateHandler s = path[2]; // source + QStateHandler s = path[2]; // tran. source QStateHandler const ss = me->temp.fun; // source->super me->temp.fun = path[1]; // target->super int_fast8_t iq = 0; // assume that LCA is NOT found @@ -415,27 +441,32 @@ static int_fast8_t QHsm_tran_complex_( // (e) check rest of source == target->super->super... // and store the target entry path along the way - int_fast8_t ip = 0; // entry path index (one below [1]) + int_fast8_t ip = 0; // path index & fixed loop bound (one below [1]) do { - ++ip; // fixed loop bound + ++ip; + + // the entry path index must stay in range of the path[] array Q_INVARIANT_LOCAL(540, ip < QHSM_MAX_NEST_DEPTH_); - path[ip] = me->temp.fun; - r = QHSM_RESERVED_EVT_(me->temp.fun, Q_EMPTY_SIG); - if (me->temp.fun == s) { + path[ip] = me->temp.fun; // store temp in the entry path array + + // find superstate of 'm_temp.fun' + r = (*me->temp.fun)(me, &l_resEvt_[Q_EMPTY_SIG]); + if (me->temp.fun == s) { // is temp the LCA? iq = 1; // indicate that LCA is found break; } - } while (r == Q_RET_SUPER); + } while (r == Q_RET_SUPER); // loop as long as superstate reached if (iq == 0) { // the LCA not found yet? QS_CRIT_STAT - // exit source s #ifndef Q_SPY - (void)QHSM_RESERVED_EVT_(s, Q_EXIT_SIG); + // exit the source 's', ignore the result + (void)(*s)(me, &l_resEvt_[Q_EXIT_SIG]); #else - if (QHSM_RESERVED_EVT_(s, Q_EXIT_SIG) == Q_RET_HANDLED) { + // exit the source 's' + if ((*s)(me, &l_resEvt_[Q_EXIT_SIG]) == Q_RET_HANDLED) { QS_STATE_ACT_(QS_QEP_STATE_EXIT, s); } #endif // def Q_SPY @@ -456,13 +487,12 @@ static int_fast8_t QHsm_tran_complex_( if (r != Q_RET_HANDLED) { // the LCA still not found? // (g) check each source->super->... // for each target->super... - int_fast8_t lbound = QHSM_MAX_NEST_DEPTH_; do { - // exit from s - if (QHSM_RESERVED_EVT_(s, Q_EXIT_SIG) == Q_RET_HANDLED) { + // exit from 's' + if ((*s)(me, &l_resEvt_[Q_EXIT_SIG]) == Q_RET_HANDLED) { QS_STATE_ACT_(QS_QEP_STATE_EXIT, s); - // find superstate of s - (void)QHSM_RESERVED_EVT_(s, Q_EMPTY_SIG); + // find superstate of 's', ignore the result + (void)(*s)(me, &l_resEvt_[Q_EMPTY_SIG]); } s = me->temp.fun; // set to super of s // NOTE: loop bounded per invariant:540 @@ -474,9 +504,6 @@ static int_fast8_t QHsm_tran_complex_( break; } } - - --lbound; // fixed loop bound - Q_INVARIANT_LOCAL(560, lbound >= 0); } while (r != Q_RET_HANDLED); } } @@ -486,8 +513,7 @@ static int_fast8_t QHsm_tran_complex_( //............................................................................ //! @private @memberof QHsm -static void QHsm_enter_target_( - QAsm * const me, +static void QHsm_enter_target_(QAsm * const me, QStateHandler * const path, int_fast8_t const depth, uint_fast8_t const qsId) @@ -500,40 +526,46 @@ static void QHsm_enter_target_( int_fast8_t ip = depth; // execute entry actions from LCA to tran target... for (; ip >= 0; --ip) { - if (QHSM_RESERVED_EVT_(path[ip], Q_ENTRY_SIG) == Q_RET_HANDLED) { + // enter 'path[ip]' + if ((*path[ip])(me, &l_resEvt_[Q_ENTRY_SIG]) == Q_RET_HANDLED) { QS_STATE_ACT_(QS_QEP_STATE_ENTRY, path[ip]); } } QStateHandler t = path[0]; // tran. target // drill into the target hierarchy with nested initial trans... - int_fast8_t lbound = QHSM_MAX_NEST_DEPTH_ - 1; - while (QHSM_RESERVED_EVT_(t, Q_INIT_SIG) == Q_RET_TRAN) { - --lbound; // fixed loop bound - Q_INVARIANT_LOCAL(640, lbound >= 0); + // take initial tran in 't' + while ((*t)(me, &l_resEvt_[Q_INIT_SIG]) == Q_RET_TRAN) { // tran. taken? + // temp holds the target of initial tran. and must be valid Q_ASSERT_LOCAL(650, me->temp.fun != Q_STATE_CAST(0)); - QS_TRAN_SEG_(QS_QEP_STATE_INIT, t, me->temp.fun); + + QS_TRAN_SEG_(QS_QEP_STATE_INIT, t, me->temp.fun); // output QS record // find superstate of initial tran. target... - ip = -1; // entry path index (one below [0]) + ip = -1; // entry path index and fixed loop bound (one below [0]) do { - ++ip; // fixed loop bound + ++ip; + // the entry path index must stay in range of the path[] array Q_INVARIANT_LOCAL(660, ip < QHSM_MAX_NEST_DEPTH_); path[ip] = me->temp.fun; // store the entry path - QState const r = QHSM_RESERVED_EVT_(me->temp.fun, Q_EMPTY_SIG); + + // find superstate of 'temp.fun' + QState const r = (*me->temp.fun)(me, &l_resEvt_[Q_EMPTY_SIG]); + + // the temp superstate handler must return superstate Q_ASSERT_LOCAL(680, r == Q_RET_SUPER); #ifdef Q_UNSAFE Q_UNUSED_PAR(r); #endif - } while (me->temp.fun != t); + } while (me->temp.fun != t); //loop as long as tran.target not reached // retrace the entry path in reverse (correct) order... for (; ip >= 0; --ip) { - // enter path[ip] - if (QHSM_RESERVED_EVT_(path[ip], Q_ENTRY_SIG) == Q_RET_HANDLED) { - QS_STATE_ACT_(QS_QEP_STATE_ENTRY, path[ip]); + // enter 'path[ip]' + if ((*path[ip])(me, &l_resEvt_[Q_ENTRY_SIG]) == Q_RET_HANDLED) { + QS_STATE_ACT_(QS_QEP_STATE_ENTRY, path[ip]);//output QS record } } t = path[0]; // tran. target becomes the new source @@ -542,44 +574,37 @@ static void QHsm_enter_target_( //............................................................................ //! @private @memberof QHsm -bool QHsm_isIn_( - QAsm * const me, +bool QHsm_isIn_(QAsm * const me, QStateHandler const stateHndl) { - Q_INVARIANT_LOCAL(700, me->state.uint == (uintptr_t)(~me->temp.uint)); + // this state machine must be in a stable state configuration + // NOTE: stable state configuration is established after every RTC step. + Q_INVARIANT_LOCAL(700, + QP_DIS_VERIFY(uintptr_t, me->state.uint, me->temp.uint)); bool inState = false; // assume that this HSM is NOT in 'stateHndl' // scan the state hierarchy bottom-up QStateHandler s = me->state.fun; QState r; - int_fast8_t lbound = QHSM_MAX_NEST_DEPTH_; do { if (s == stateHndl) { // do the states match? inState = true; // 'true' means that match found break; // break out of the for-loop } - r = QHSM_RESERVED_EVT_(s, Q_EMPTY_SIG); - s = me->temp.fun; - --lbound; // fixed loop bound - Q_INVARIANT_LOCAL(740, lbound >= 0); + // find superstate of 's' + r = (*s)(me, &l_resEvt_[Q_EMPTY_SIG]); + s = me->temp.fun; } while (r == Q_RET_SUPER); +#ifndef Q_UNSAFE // restore the invariant (stable state configuration) - me->temp.uint = (uintptr_t)~me->state.uint; - + me->temp.uint = QP_DIS_UPDATE(uintptr_t, me->state.uint); +#endif return inState; // return the status } -//............................................................................ -#ifdef Q_SPY -//! @private @memberof QHsm -QStateHandler QHsm_getStateHandler_(QAsm * const me) { - return me->state.fun; -} -#endif // def Q_SPY - //............................................................................ //! @public @memberof QHsm QStateHandler QHsm_childState(QHsm * const me, @@ -592,7 +617,6 @@ QStateHandler QHsm_childState(QHsm * const me, QStateHandler child = me->super.state.fun; // start with current state me->super.temp.fun = child; // establish stable state configuration QState r = Q_RET_SUPER; - int_fast8_t lbound = QHSM_MAX_NEST_DEPTH_; do { // have the parent of the current child? if (me->super.temp.fun == parentHndl) { @@ -602,16 +626,29 @@ QStateHandler QHsm_childState(QHsm * const me, break; } child = me->super.temp.fun; - r = QHSM_RESERVED_EVT_(child, Q_EMPTY_SIG); - --lbound; // fixed loop bound - Q_INVARIANT_LOCAL(840, lbound >= 0); + // find superstate of 'child' + r = (*child)(me, &l_resEvt_[Q_EMPTY_SIG]); } while (r == Q_RET_SUPER); Q_ENSURE_LOCAL(890, isFound); - // restore the invariant (stable state configuration) - me->super.temp.uint = (uintptr_t)~me->super.state.uint; - return child; } +//............................................................................ +//! @public @memberof QHsm +QStateHandler QHsm_state(QHsm const * const me) { + // NOTE: this function does NOT apply critical section, so it can + // be safely called from an already established critical section. + return me->super.state.fun; +} + +//............................................................................ +#ifdef Q_SPY +//! @private @memberof QHsm +QStateHandler QHsm_getStateHandler_(QAsm const * const me) { + // NOTE: this function does NOT apply critical section, so it can + // be safely called from an already established critical section. + return me->state.fun; +} +#endif // def Q_SPY diff --git a/src/qf/qep_msm.c b/src/qf/qep_msm.c index b8232d44a..e09c53c57 100644 --- a/src/qf/qep_msm.c +++ b/src/qf/qep_msm.c @@ -26,8 +26,8 @@ // // //============================================================================ -#define QP_IMPL // this is QP implementation #include "qp_port.h" // QP port +#include "qp_pkg.h" // QP package-scope interface #include "qsafe.h" // QP Functional Safety (FuSa) Subsystem #ifdef Q_SPY // QS software tracing enabled? #include "qs_port.h" // QS port @@ -38,8 +38,8 @@ Q_DEFINE_THIS_MODULE("qep_msm") +// maximum depth of state nesting in a QMsm (including the top level) #define QMSM_MAX_NEST_DEPTH_ ((int_fast8_t)6) -#define QMSM_MAX_TRAN_LENGTH_ ((int_fast8_t)(2*QMSM_MAX_NEST_DEPTH_)) //! @cond INTERNAL @@ -116,6 +116,8 @@ static struct QMState const l_msm_top_s = { //! @endcond //============================================================================ +// static and private helper function prototypes... + static QState QMsm_execTatbl_( QAsm * const me, QMTranActTable const * const tatbl, @@ -132,7 +134,7 @@ static QState QMsm_enterHistory_( QMState const *const hist, uint_fast8_t const qsId); -//............................................................................ +//============================================================================ //! @protected @memberof QMsm void QMsm_ctor(QMsm * const me, QStateHandler const initial) @@ -145,8 +147,8 @@ void QMsm_ctor(QMsm * const me, ,&QMsm_getStateHandler_ #endif }; - // do not call the QAsm_ctor() here - me->super.vptr = &vtable; + // no need to call the superclass' constructor QAsm_ctor() here + me->super.vptr = &vtable; // QMsm class' VTABLE me->super.state.obj = &l_msm_top_s; // the current state (top) me->super.temp.fun = initial; // the initial tran. handler } @@ -162,14 +164,19 @@ void QMsm_init_( Q_UNUSED_PAR(qsId); #endif - Q_REQUIRE_LOCAL(200, me->temp.fun != Q_STATE_CAST(0)); - Q_REQUIRE_LOCAL(210, me->state.obj == &l_msm_top_s); + // current state must be initialized to &l_msm_top_s in QMsm_ctor() + Q_REQUIRE_LOCAL(200, me->state.obj == &l_msm_top_s); + + // temp contains the top-most initial tran. handler, which must be valid + Q_REQUIRE_LOCAL(210, me->temp.fun != Q_STATE_CAST(0)); // execute the top-most initial tran. QState r = (*me->temp.fun)(me, Q_EVT_CAST(QEvt)); // the top-most initial tran. must be taken Q_ASSERT_LOCAL(240, r == Q_RET_TRAN_INIT); + + // the top-most initial tran. must set the tran-action table in temp Q_ASSERT_LOCAL(250, me->temp.tatbl != (struct QMTranActTable *)0); QS_CRIT_STAT @@ -180,18 +187,15 @@ void QMsm_init_( me->state.obj = me->temp.tatbl->target; // drill down into the state hierarchy with initial transitions... - int_fast8_t lbound = QMSM_MAX_NEST_DEPTH_; do { - --lbound; // fixed loop bound - Q_INVARIANT_LOCAL(280, lbound >= 0); r = QMsm_execTatbl_(me, me->temp.tatbl, qsId); } while (r == Q_RET_TRAN_INIT); QS_TOP_INIT_(QS_QEP_INIT_TRAN, me->state.obj->stateHandler); #ifndef Q_UNSAFE - // establish stable state configuration - me->temp.uint = (uintptr_t)~me->state.uint; + // establish stable state configuration at the end of RTC step + me->temp.uint = QP_DIS_UPDATE(uintptr_t, me->state.uint); #endif } @@ -206,8 +210,12 @@ void QMsm_dispatch_( Q_UNUSED_PAR(qsId); #endif - Q_INVARIANT_LOCAL(300, me->state.uint == (uintptr_t)(~me->temp.uint)); + // this state machine must be in a stable state configuration + // NOTE: stable state configuration is established after every RTC step. + Q_INVARIANT_LOCAL(300, + QP_DIS_VERIFY(uintptr_t, me->state.uint, me->temp.uint)); + // the event to be dispatched must be valid Q_REQUIRE_LOCAL(310, e != (QEvt *)0); QMState const *s = me->state.obj; // the current state @@ -216,12 +224,8 @@ void QMsm_dispatch_( QS_TRAN0_(QS_QEP_DISPATCH, s->stateHandler); // scan the state hierarchy up to the top state... - QState r = Q_RET_SUPER; - int_fast8_t lbound = QMSM_MAX_NEST_DEPTH_; + QState r; do { - --lbound; // fixed loop bound - Q_INVARIANT_LOCAL(340, lbound >= 0); - r = (*s->stateHandler)(me, e); // call state handler function if (r >= Q_RET_HANDLED) { // event handled? (the most frequent case) break; // done scanning the state hierarchy @@ -238,27 +242,28 @@ void QMsm_dispatch_( } #endif s = s->superstate; // advance to the superstate + } while (s != (QMState *)0); if (s == (QMState *)0) { // event bubbled to the 'top' state? #ifdef Q_SPY QS_TRAN0_(QS_QEP_IGNORED, t->stateHandler); -#endif // Q_SPY +#endif } - else if (r >= Q_RET_TRAN) { // any kind of tran. taken? + else if (r == Q_RET_HANDLED) { // was the event e handled? + QS_TRAN0_(QS_QEP_INTERN_TRAN, s->stateHandler); // output QS record + } + else if ((r == Q_RET_TRAN) || (r == Q_RET_TRAN_HIST)) { //any tran. taken? #ifdef Q_SPY QMState const * const ts = s; // tran. source for QS tracing #endif // Q_SPY - if (r == Q_RET_TRAN) { + if (r == Q_RET_TRAN) { // tran. taken? struct QMTranActTable const * const tatbl = me->temp.tatbl; QMsm_exitToTranSource_(me, t, s, qsId); r = QMsm_execTatbl_(me, tatbl, qsId); -#ifdef Q_SPY - s = me->state.obj; -#endif // Q_SPY } - else if (r == Q_RET_TRAN_HIST) { // was it tran. to history? + else { // must be tran. to history QMState const * const hist = me->state.obj; // save history me->state.obj = t; // restore the original state @@ -270,38 +275,29 @@ void QMsm_dispatch_( QMsm_exitToTranSource_(me, t, s, qsId); (void)QMsm_execTatbl_(me, tatbl, qsId); r = QMsm_enterHistory_(me, hist, qsId); -#ifdef Q_SPY - s = me->state.obj; -#endif // Q_SPY - } - else { - // empty } +#ifdef Q_SPY + s = me->state.obj; +#endif - lbound = QMSM_MAX_NEST_DEPTH_; while (r == Q_RET_TRAN_INIT) { // initial tran. in the target? r = QMsm_execTatbl_(me, me->temp.tatbl, qsId); #ifdef Q_SPY s = me->state.obj; -#endif // Q_SPY +#endif - --lbound; // fixed loop bound - Q_INVARIANT_LOCAL(350, lbound >= 0); } QS_TRAN_END_(QS_QEP_TRAN, ts->stateHandler, s->stateHandler); } - else if (r == Q_RET_HANDLED) { // was the event handled? - QS_TRAN0_(QS_QEP_INTERN_TRAN, s->stateHandler); - } else { - Q_ERROR_LOCAL(360); + Q_ERROR_LOCAL(360); // last action handler returned impossible value } #ifndef Q_UNSAFE - // establish stable state configuration - me->temp.uint = (uintptr_t)~me->state.uint; + // establish stable state configuration at the end of RTC step + me->temp.uint = QP_DIS_UPDATE(uintptr_t, me->state.uint); #endif } @@ -316,17 +312,16 @@ static QState QMsm_execTatbl_( Q_UNUSED_PAR(qsId); #endif + // the tran-action table parameter must be valid Q_REQUIRE_LOCAL(400, tatbl != (struct QMTranActTable *)0); QS_CRIT_STAT QState r = Q_RET_SUPER; QActionHandler const *a = &tatbl->act[0]; - int_fast8_t lbound = QMSM_MAX_TRAN_LENGTH_; - while (*a != Q_ACTION_CAST(0)) { + while (*a != Q_ACTION_CAST(0)) { // not at the end of the table? r = (*(*a))(me); // call the action through the 'a' pointer ++a; -#ifdef Q_SPY if (r == Q_RET_ENTRY) { QS_STATE_ACT_(QS_QEP_STATE_ENTRY, me->temp.obj->stateHandler); } @@ -339,17 +334,12 @@ static QState QMsm_execTatbl_( me->temp.tatbl->target->stateHandler); } else { - // empty + Q_ERROR_LOCAL(460); //last action handler returned impossible value } -#endif // Q_SPY - --lbound; // fixed loop bound - Q_INVARIANT_LOCAL(480, lbound >= 0); } - me->state.obj = (r >= Q_RET_TRAN) - ? me->temp.tatbl->target - : tatbl->target; + me->state.obj = tatbl->target; // set new current state return r; } @@ -367,17 +357,17 @@ static void QMsm_exitToTranSource_( QS_CRIT_STAT // exit states from the current state to the tran. source state + // NOTE: the following loop does not need the fixed loop bound check + // because the path from the current state to the tran.source has + // been already checked in the invariant 340. QMState const *s = curr_state; - int_fast8_t lbound = QMSM_MAX_NEST_DEPTH_; while (s != tran_source) { if (s->exitAction != Q_ACTION_CAST(0)) { // exit action provided? - (void)(*s->exitAction)(me); // execute the exit action + // exit state s, ignore the result + (void)(*s->exitAction)(me); QS_STATE_ACT_(QS_QEP_STATE_EXIT, me->temp.obj->stateHandler); } s = s->superstate; // advance to the superstate - - --lbound; // fixed loop bound - Q_INVARIANT_LOCAL(580, lbound >= 0); } } @@ -393,93 +383,79 @@ static QState QMsm_enterHistory_( #endif // record the entry path from current state to history - QMState const *epath[QMSM_MAX_NEST_DEPTH_]; + QMState const *path[QMSM_MAX_NEST_DEPTH_]; QMState const *s = hist; int_fast8_t i = -1; // entry path index (one below [0]) - int_fast8_t lbound = QMSM_MAX_NEST_DEPTH_; while (s != me->state.obj) { - if (s->entryAction != Q_ACTION_CAST(0)) { + if (s->entryAction != Q_ACTION_CAST(0)) { // does s have an entry action? ++i; Q_INVARIANT_LOCAL(610, i < QMSM_MAX_NEST_DEPTH_); - epath[i] = s; + path[i] = s; } s = s->superstate; - - --lbound; // fixed loop bound - Q_INVARIANT_LOCAL(620, lbound >= 0); } QS_CRIT_STAT // retrace the entry path in reverse (desired) order... - // NOTE: i the fixed loop bound + // NOTE: i is the fixed loop bound already checked in invariant 610 for (; i >= 0; --i) { - (void)(*epath[i]->entryAction)(me); // enter epath[i] - QS_STATE_ACT_(QS_QEP_STATE_ENTRY, epath[i]->stateHandler); + // enter the state in path[i], ignore the result + (void)(*path[i]->entryAction)(me); + QS_STATE_ACT_(QS_QEP_STATE_ENTRY, path[i]->stateHandler); } me->state.obj = hist; // set current state to the tran. target // initial tran. present? QState r = Q_RET_SUPER; - if (hist->initAction != Q_ACTION_CAST(0)) { - r = (*hist->initAction)(me); // execute the tran. action + if (hist->initAction != Q_ACTION_CAST(0)) { // init. action provided? + r = (*hist->initAction)(me); // execute the init. action QS_TRAN_SEG_(QS_QEP_STATE_INIT, hist->stateHandler, me->temp.tatbl->target->stateHandler); } - return r; + return r; // inform the caller if the init action was taken } +//............................................................................ +//! @public @memberof QMsm +QMState const * QMsm_topQMState(void) { + // return the top state (object pointer) + return &l_msm_top_s; +} //............................................................................ //! @private @memberof QMsm -bool QMsm_isIn_( - QAsm * const me, - QStateHandler const stateHndl) -{ +bool QMsm_isIn_(QAsm * const me, QStateHandler const stateHndl) { bool inState = false; // assume that this SM is not in 'state' QMState const *s = me->state.obj; - int_fast8_t lbound = QMSM_MAX_NEST_DEPTH_; while (s != (QMState *)0) { if (s->stateHandler == stateHndl) { // match found? inState = true; break; } s = s->superstate; // advance to the superstate - - --lbound; // fixed loop bound - Q_INVARIANT_LOCAL(740, lbound >= 0); } return inState; } -//............................................................................ -#ifdef Q_SPY -//! @public @memberof QMsm -QStateHandler QMsm_getStateHandler_(QAsm * const me) { - return me->state.obj->stateHandler; -} -#endif // def Q_SPY - //............................................................................ //! @public @memberof QMsm -QMState const * QMsm_childStateObj(QMsm const * const me, - QMState const * const parent) +QMState const * QMsm_childStateObj( + QMsm const * const me, + QMState const * const parentHndl) { QMState const *s = me->super.state.obj; // start with current state QMState const *child = s; bool isFound = false; // assume the child NOT found - int_fast8_t lbound = QMSM_MAX_NEST_DEPTH_; - while (s != (QMState *)0) { - if (s == parent) { + while (s != (QMState *)0) { // top of state hierarchy not reached yet? + if (s == parentHndl) { isFound = true; // child is found break; } child = s; s = s->superstate; - - --lbound; // fixed loop bound - Q_INVARIANT_LOCAL(840, lbound >= 0); } + // the child state must be found, or the state machine is corrupt Q_ENSURE_LOCAL(890, isFound); #ifdef Q_UNSAFE @@ -488,3 +464,17 @@ QMState const * QMsm_childStateObj(QMsm const * const me, return child; } +//............................................................................ +//! @public @memberof QMsm +QMState const* QMsm_stateObj(QMsm const* const me) { + // return the current state (object pointer) + return me->super.state.obj; +} +//............................................................................ +#ifdef Q_SPY +//! @public @memberof QMsm +QStateHandler QMsm_getStateHandler_(QAsm const * const me) { + // return the current state handler (function pointer) + return me->state.obj->stateHandler; +} +#endif diff --git a/src/qf/qf_act.c b/src/qf/qf_act.c index 5132eed66..3b791c41d 100644 --- a/src/qf/qf_act.c +++ b/src/qf/qf_act.c @@ -37,59 +37,41 @@ #include "qs_dummy.h" // disable the QS software tracing #endif // Q_SPY -//Q_DEFINE_THIS_MODULE("qf_act") +Q_DEFINE_THIS_MODULE("qf_act") // QP version string embedded in the binary image char const QP_versionStr[24] = "QP/C " QP_VERSION_STR; -QF_Attr QF_priv_; - -//! @static @private @memberof QActive -QActive * QActive_registry_[QF_MAX_ACTIVE + 1U]; - +//---------------------------------------------------------------------------- +//! @public @memberof QEvt +void QEvt_ctor(QEvt * const me, enum_t const sig) { + me->sig = (QSignal)sig; + me->poolNum_ = 0x00U; // not a pool event + me->refCtr_ = 0xE0U; // use as an "event marker" +} //............................................................................ -//! @static @private @memberof QF -void QF_bzero_( - void * const start, - uint_fast16_t const len) -{ - uint8_t *ptr = (uint8_t *)start; - for (uint_fast16_t n = len; n > 0U; --n) { - *ptr = 0U; - ++ptr; - } +//! @public @memberof QEvt +QEvt *QEvt_init(QEvt * const me, uint8_t const dummy) { + // initialize a dynamic event without parameters + Q_UNUSED_PAR(dummy); + return me; } - //............................................................................ -#ifndef QF_LOG2 -uint_fast8_t QF_LOG2(QPSetBits const bitmask) { - static uint8_t const log2LUT[16] = { - 0U, 1U, 2U, 2U, 3U, 3U, 3U, 3U, - 4U, 4U, 4U, 4U, 4U, 4U, 4U, 4U - }; - uint_fast8_t n = 0U; - QPSetBits x = bitmask; - QPSetBits tmp; +//! @private @memberof QEvt +void QEvt_refCtr_inc_(QEvt const * const me) { + // NOTE: this function must be called *inside* a critical section -#if (QF_MAX_ACTIVE > 16U) - tmp = (x >> 16U); - if (tmp != 0U) { - n += 16U; - x = tmp; - } -#endif -#if (QF_MAX_ACTIVE > 8U) - tmp = (x >> 8U); - if (tmp != 0U) { - n += 8U; - x = tmp; - } -#endif - tmp = (x >> 4U); - if (tmp != 0U) { - n += 4U; - x = tmp; - } - return n + log2LUT[x]; + // the event reference count must not exceed the number of AOs + // in the system plus each AO possibly holding one event reference + Q_REQUIRE_INCRIT(200, me->refCtr_ < (QF_MAX_ACTIVE + QF_MAX_ACTIVE)); + + QEvt * const mut_me = (QEvt*)me; // cast 'const' away + ++mut_me->refCtr_; +} +//............................................................................ +//! @private @memberof QEvt +void QEvt_refCtr_dec_(QEvt const * const me) { + // NOTE: this function must be called inside a critical section + QEvt * const mut_me = (QEvt*)me; // cast 'const' away + --mut_me->refCtr_; } -#endif // ndef QF_LOG2 diff --git a/src/qf/qf_actq.c b/src/qf/qf_actq.c index 6c3ec8c4d..ba949a960 100644 --- a/src/qf/qf_actq.c +++ b/src/qf/qf_actq.c @@ -40,6 +40,7 @@ Q_DEFINE_THIS_MODULE("qf_actq") //............................................................................ +// static helper function static void QActive_postFIFO_(QActive * const me, QEvt const * const e, void const * const sender); @@ -62,14 +63,15 @@ bool QActive_post_(QActive * const me, QF_CRIT_STAT QF_CRIT_ENTRY(); - Q_REQUIRE_INCRIT(200, e != (QEvt *)0); + // the event to post must not be NULL + Q_REQUIRE_INCRIT(100, e != (QEvt *)0); - QEQueueCtr const nFree = me->eQueue.nFree; // get volatile into temporary + QEQueueCtr const nFree = me->eQueue.nFree; // get member into temporary bool status = (nFree > 0U); if (margin == QF_NO_MARGIN) { // no margin requested? // queue must not overflow - Q_ASSERT_INCRIT(230, status); + Q_ASSERT_INCRIT(130, status); } else { status = (nFree > (QEQueueCtr)margin); @@ -77,7 +79,6 @@ bool QActive_post_(QActive * const me, #if (QF_MAX_EPOOL > 0U) if (e->poolNum_ != 0U) { // is it a mutable event? - Q_ASSERT_INCRIT(240, e->refCtr_ < (2U * QF_MAX_ACTIVE)); QEvt_refCtr_inc_(e); // increment the reference counter } #endif // (QF_MAX_EPOOL > 0U) @@ -139,16 +140,15 @@ void QActive_postLIFO_(QActive * const me, QF_CRIT_STAT QF_CRIT_ENTRY(); - // the posted event must be be valid (which includes not NULL) - Q_REQUIRE_INCRIT(300, e != (QEvt *)0); + // the event to post must be be valid (which includes not NULL) + Q_REQUIRE_INCRIT(200, e != (QEvt *)0); - QEQueueCtr nFree = me->eQueue.nFree; // get volatile into temporary + QEQueueCtr nFree = me->eQueue.nFree; // get member into temporary - // The queue must NOT overflow for the LIFO posting policy. - Q_REQUIRE_INCRIT(330, nFree != 0U); + // the queue must NOT overflow for the LIFO posting policy. + Q_REQUIRE_INCRIT(230, nFree != 0U); if (e->poolNum_ != 0U) { // is it a mutable event? - Q_ASSERT_INCRIT(340, e->refCtr_ < (2U * QF_MAX_ACTIVE)); QEvt_refCtr_inc_(e); // increment the reference counter } @@ -182,7 +182,7 @@ void QActive_postLIFO_(QActive * const me, me->eQueue.frontEvt = e; // deliver the event directly to the front if (frontEvt != (QEvt *)0) { // was the queue NOT empty? - QEQueueCtr tail = me->eQueue.tail; // get volatile into temporary + QEQueueCtr tail = me->eQueue.tail; // get member into temporary ++tail; if (tail == me->eQueue.end) { // need to wrap the tail? tail = 0U; // wrap around @@ -209,9 +209,11 @@ QEvt const * QActive_get_(QActive * const me) { // always remove event from the front QEvt const * const e = me->eQueue.frontEvt; - Q_REQUIRE_INCRIT(410, e != (QEvt *)0); // queue must NOT be empty - QEQueueCtr nFree = me->eQueue.nFree; // get volatile into temporary + // the queue must NOT be empty + Q_REQUIRE_INCRIT(310, e != (QEvt *)0); + + QEQueueCtr nFree = me->eQueue.nFree; // get member into temporary ++nFree; // one more free event in the queue me->eQueue.nFree = nFree; // update the # free @@ -219,10 +221,12 @@ QEvt const * QActive_get_(QActive * const me) { if (nFree <= me->eQueue.end) { // any events in the ring buffer? // remove event from the tail - QEQueueCtr tail = me->eQueue.tail; // get volatile into temporary + QEQueueCtr tail = me->eQueue.tail; // get member into temporary QEvt const * const frontEvt = me->eQueue.ring[tail]; - Q_ASSERT_INCRIT(450, frontEvt != (QEvt *)0); + + // the event queue must not be empty (frontEvt != NULL) + Q_ASSERT_INCRIT(350, frontEvt != (QEvt *)0); QS_BEGIN_PRE(QS_QF_ACTIVE_GET, me->prio) QS_TIME_PRE(); // timestamp @@ -243,7 +247,7 @@ QEvt const * QActive_get_(QActive * const me) { me->eQueue.frontEvt = (QEvt *)0; // queue becomes empty // all entries in the queue must be free (+1 for fronEvt) - Q_ASSERT_INCRIT(460, nFree == (me->eQueue.end + 1U)); + Q_ASSERT_INCRIT(360, nFree == (me->eQueue.end + 1U)); QS_BEGIN_PRE(QS_QF_ACTIVE_GET_LAST, me->prio) QS_TIME_PRE(); // timestamp @@ -269,7 +273,7 @@ static void QActive_postFIFO_(QActive * const me, Q_UNUSED_PAR(sender); #endif - QEQueueCtr nFree = me->eQueue.nFree; // get volatile into temporary + QEQueueCtr nFree = me->eQueue.nFree; // get member into temporary --nFree; // one free entry just used up me->eQueue.nFree = nFree; // update the original @@ -291,10 +295,10 @@ static void QActive_postFIFO_(QActive * const me, me->eQueue.frontEvt = e; // deliver event directly #ifdef QXK_H_ - if (me->super.state.act == Q_ACTION_CAST(0)) { // eXtended thread? + if (me->super.state.act == Q_ACTION_CAST(0)) { // extended thread? QXTHREAD_EQUEUE_SIGNAL_(me); // signal eXtended Thread } - else { + else { // basic thread (AO) QACTIVE_EQUEUE_SIGNAL_(me); // signal the Active Object } #else @@ -302,7 +306,7 @@ static void QActive_postFIFO_(QActive * const me, #endif // def QXK_H_ } else { // queue was not empty, insert event into the ring-buffer - QEQueueCtr head = me->eQueue.head; // get volatile into temporary + QEQueueCtr head = me->eQueue.head; // get member into temporary me->eQueue.ring[head] = e; // insert e into buffer if (head == 0U) { // need to wrap the head? @@ -316,21 +320,83 @@ static void QActive_postFIFO_(QActive * const me, //............................................................................ //! @static @public @memberof QActive -uint_fast16_t QActive_getQueueMin(uint_fast8_t const prio) { +uint16_t QActive_getQueueUse(uint_fast8_t const prio) { + QF_CRIT_STAT + QF_CRIT_ENTRY(); + + // prio must be in range. prio==0 is OK (special case) + Q_REQUIRE_INCRIT(500, prio <= QF_MAX_ACTIVE); + + uint16_t nUse = 0U; + + if (prio > 0U) { + QActive const * const a = QActive_registry_[prio]; + // the AO must be registered (started) + Q_REQUIRE_INCRIT(510, a != (QActive *)0); + + // NOTE: QEQueue_getUse() does NOT apply crit.sect. internally + nUse = QEQueue_getUse(&a->eQueue); + } + else { // special case of prio==0U: use of all AO event queues + for (uint_fast8_t p = QF_MAX_ACTIVE; p > 0U; --p) { + QActive const * const a = QActive_registry_[p]; + if (a != (QActive *)0) { // is the AO registered? + // NOTE: QEQueue_getUse() does NOT apply crit.sect. internally + nUse += QEQueue_getUse(&a->eQueue); + } + } + } + + QF_CRIT_EXIT(); + + return nUse; +} + +//............................................................................ +//! @static @public @memberof QActive +uint16_t QActive_getQueueFree(uint_fast8_t const prio) { QF_CRIT_STAT QF_CRIT_ENTRY(); - Q_REQUIRE_INCRIT(600, prio <= QF_MAX_ACTIVE); + + Q_REQUIRE_INCRIT(600, (0U < prio) && (prio <= QF_MAX_ACTIVE)); QActive const * const a = QActive_registry_[prio]; + // the AO must be registered (started) Q_REQUIRE_INCRIT(610, a != (QActive *)0); - uint_fast16_t const min = (uint_fast16_t)(a->eQueue.nMin); + // NOTE: critical section prevents asynchronous change of the free count + uint16_t const nFree = (uint16_t)a->eQueue.nFree; + + QF_CRIT_EXIT(); + + return nFree; +} + +//............................................................................ +//! @static @public @memberof QActive +uint16_t QActive_getQueueMin(uint_fast8_t const prio) { + QF_CRIT_STAT + QF_CRIT_ENTRY(); + + // the queried prio. must be in range (excluding the idle thread) + Q_REQUIRE_INCRIT(700, (0U < prio) && (prio <= QF_MAX_ACTIVE)); + + QActive const * const a = QActive_registry_[prio]; + // the AO must be registered (started) + Q_REQUIRE_INCRIT(710, a != (QActive *)0); + + // NOTE: critical section prevents asynchronous change of the min count + uint16_t const nMin = (uint16_t)a->eQueue.nMin; + QF_CRIT_EXIT(); - return min; + return nMin; } //============================================================================ +#if (QF_MAX_TICK_RATE > 0U) + +//............................................................................ //! @public @memberof QTicker void QTicker_ctor(QTicker * const me, uint_fast8_t const tickRate) @@ -347,24 +413,26 @@ void QTicker_ctor(QTicker * const me, }; me->super.super.vptr = &vtable; // hook the vptr - // reuse eQueue.head for tick-rate + // reuse super.eQueue.head for tick-rate me->super.eQueue.head = (QEQueueCtr)tickRate; } //............................................................................ //! @private @memberof QTicker -void QTicker_init_( - QAsm * const me, +void QTicker_init_(QAsm * const me, void const * const par, uint_fast8_t const qsId) { - Q_UNUSED_PAR(me); Q_UNUSED_PAR(par); Q_UNUSED_PAR(qsId); QF_CRIT_STAT QF_CRIT_ENTRY(); + // instead of the top-most initial transition, QTicker initializes + // the super.eQueue member inherited from QActive and reused + // to count the number of tick events posted to this QTicker. + // see also: QTicker_trig_() QACTIVE_CAST_(me)->eQueue.tail = 0U; QF_CRIT_EXIT(); @@ -372,8 +440,7 @@ void QTicker_init_( //............................................................................ //! @private @memberof QTicker -void QTicker_dispatch_( - QAsm * const me, +void QTicker_dispatch_(QAsm * const me, QEvt const * const e, uint_fast8_t const qsId) { @@ -383,16 +450,19 @@ void QTicker_dispatch_( QF_CRIT_STAT QF_CRIT_ENTRY(); - // get volatile into temporaries + // get members into temporaries QEQueueCtr nTicks = QACTIVE_CAST_(me)->eQueue.tail; QEQueueCtr const tickRate = QACTIVE_CAST_(me)->eQueue.head; + // QTicker_dispatch_() shall be called only when it has tick events Q_REQUIRE_INCRIT(800, nTicks > 0U); QACTIVE_CAST_(me)->eQueue.tail = 0U; // clear # ticks QF_CRIT_EXIT(); + // instead of dispatching the event, QTicker calls QTimeEvt_tick_() + // processing for the number of times indicated in eQueue.tail. for (; nTicks > 0U; --nTicks) { QTimeEvt_tick_((uint_fast8_t)tickRate, me); } @@ -400,32 +470,36 @@ void QTicker_dispatch_( //............................................................................ //! @private @memberof QTicker -void QTicker_trig_( - QTicker * const me, +void QTicker_trig_(QTicker * const me, void const * const sender) { #ifndef Q_SPY Q_UNUSED_PAR(sender); #endif + // static immutable (const) event to post to the QTicker AO static QEvt const tickEvt = QEVT_INITIALIZER(0); QF_CRIT_STAT QF_CRIT_ENTRY(); - QEQueueCtr nTicks = me->super.eQueue.tail; // get volatile into temporary + QEQueueCtr nTicks = me->super.eQueue.tail; // get member into temporary - if (me->super.eQueue.frontEvt == (QEvt *)0) { // no tick events? - Q_REQUIRE_INCRIT(930, me->super.eQueue.nFree == 1U); - Q_REQUIRE_INCRIT(940, nTicks == 0U); + if (nTicks == 0U) { // no ticks accumulated yet? + // when no ticks accumulated, eQueue.fronEvt must be NULL + Q_REQUIRE_INCRIT(930, me->super.eQueue.frontEvt == (QEvt *)0); me->super.eQueue.frontEvt = &tickEvt; // deliver event directly me->super.eQueue.nFree = 0U; QACTIVE_EQUEUE_SIGNAL_(&me->super); // signal the event queue } - else { - Q_REQUIRE_INCRIT(950, (0U < nTicks) && (nTicks < 0xFFU)); + else { // some tick have accumulated (and were not processed yet) + // when some ticks accumulated, eQueue.fronEvt must be &tickEvt + Q_REQUIRE_INCRIT(940, me->super.eQueue.frontEvt == &tickEvt); + + // the nTicks counter must accept one more count without overflowing + Q_REQUIRE_INCRIT(950, nTicks < 0xFFU); } ++nTicks; // account for one more tick event @@ -443,3 +517,5 @@ void QTicker_trig_( QF_CRIT_EXIT(); } + +#endif // (QF_MAX_TICK_RATE > 0U) diff --git a/src/qf/qf_defer.c b/src/qf/qf_defer.c index 0885def3d..748746104 100644 --- a/src/qf/qf_defer.c +++ b/src/qf/qf_defer.c @@ -45,11 +45,12 @@ bool QActive_defer(QActive const * const me, struct QEQueue * const eq, QEvt const * const e) { + // post with margin==0U to use all available entries in the queue bool const status = QEQueue_post(eq, e, 0U, me->prio); QS_CRIT_STAT QS_CRIT_ENTRY(); - if (status) { + if (status) { // deferring successful? QS_BEGIN_PRE(QS_QF_ACTIVE_DEFER, me->prio) QS_TIME_PRE(); // time stamp QS_OBJ_PRE(me); // this active object @@ -58,7 +59,7 @@ bool QActive_defer(QActive const * const me, QS_2U8_PRE(e->poolNum_, e->refCtr_); QS_END_PRE() } - else { + else { // deferring failed QS_BEGIN_PRE(QS_QF_ACTIVE_DEFER_ATTEMPT, me->prio) QS_TIME_PRE(); // time stamp QS_OBJ_PRE(me); // this active object @@ -77,18 +78,21 @@ bool QActive_defer(QActive const * const me, bool QActive_recall(QActive * const me, struct QEQueue * const eq) { + bool recalled = false; // assume failure + QEvt const * const e = QEQueue_get(eq, me->prio); - bool recalled = false; if (e != (QEvt *)0) { // event available? - QACTIVE_POST_LIFO(me, e); // post it to the front of the AO's queue + // post it to the front of the AO's queue. + // NOTE: asserts internally if the posting fails. + QACTIVE_POST_LIFO(me, e); QF_CRIT_STAT QF_CRIT_ENTRY(); if (e->poolNum_ != 0U) { // mutable event? - // after posting to the AO's queue the event must be referenced + // after posting to the AO's queue, the event must be referenced // at least twice: once in the deferred event queue (eq->get() // did NOT decrement the reference counter) and once in the // AO's event queue. @@ -109,7 +113,7 @@ bool QActive_recall(QActive * const me, QF_CRIT_EXIT(); - recalled = true; + recalled = true; // success } else { QS_CRIT_STAT @@ -128,21 +132,21 @@ bool QActive_recall(QActive * const me, //............................................................................ //! @protected @memberof QActive -uint_fast16_t QActive_flushDeferred(QActive const * const me, +uint16_t QActive_flushDeferred(QActive const * const me, struct QEQueue * const eq, uint_fast16_t const num) { - uint_fast16_t n = 0U; - while (n < num) { + uint16_t n = 0U; // the flushed event counter + while (n < num) { // below the requested number? QEvt const * const e = QEQueue_get(eq, me->prio); - if (e != (QEvt *)0) { + if (e != (QEvt *)0) { // event obtained from the queue? ++n; // count one more flushed event #if (QF_MAX_EPOOL > 0U) QF_gc(e); // garbage collect #endif } - else { - break; + else { // queue ran out of events + break; // done flushing } } diff --git a/src/qf/qf_dyn.c b/src/qf/qf_dyn.c index e46ac7936..716b5cb59 100644 --- a/src/qf/qf_dyn.c +++ b/src/qf/qf_dyn.c @@ -53,8 +53,14 @@ void QF_poolInit( QF_CRIT_STAT QF_CRIT_ENTRY(); + // the maximum of initialized pools so far must be in the configured range Q_REQUIRE_INCRIT(100, poolNum < QF_MAX_EPOOL); - if (poolNum > 0U) { + + if (poolNum > 0U) { // any event pools already initialized? + // the last initialized event pool must have event size smaller + // than the one just being initialized + // NOTE: QF event pools must be initialized in the increasing order + // of their event sizes Q_REQUIRE_INCRIT(110, QF_EPOOL_EVENT_SIZE_(QF_priv_.ePool_[poolNum - 1U]) < evtSize); } @@ -66,9 +72,10 @@ void QF_poolInit( QF_EPOOL_INIT_(QF_priv_.ePool_[poolNum], poolSto, poolSize, evtSize); #ifdef Q_SPY - // generate the object-dictionary entry for the initialized pool + // generate the QS object-dictionary entry for the initialized pool { - uint8_t obj_name[9] = "EvtPool?"; + uint8_t obj_name[9] = "EvtPool?"; // initial event pool name + // replace the "?" with the one-digit pool number (1-based) obj_name[7] = (uint8_t)((uint8_t)'0' + poolNum + 1U); QS_obj_dict_pre_(&QF_priv_.ePool_[poolNum], (char const *)obj_name); } @@ -77,14 +84,17 @@ void QF_poolInit( //............................................................................ //! @static @public @memberof QF -uint_fast16_t QF_poolGetMaxBlockSize(void) { +uint16_t QF_poolGetMaxBlockSize(void) { QF_CRIT_STAT QF_CRIT_ENTRY(); uint8_t const maxPool = QF_priv_.maxPool_; + + // the maximum number of initialized pools must be in configured range Q_REQUIRE_INCRIT(210, (0U < maxPool) && (maxPool <= QF_MAX_EPOOL)); - uint_fast16_t const maxSize = + // set event size from the port-dependent operation + uint16_t const maxSize = QF_EPOOL_EVENT_SIZE_(QF_priv_.ePool_[maxPool - 1U]); QF_CRIT_EXIT(); @@ -92,22 +102,88 @@ uint_fast16_t QF_poolGetMaxBlockSize(void) { } //............................................................................ +#ifdef QF_EPOOL_USE_ //! @static @public @memberof QF -uint_fast16_t QF_getPoolMin(uint_fast8_t const poolNum) { +uint16_t QF_getPoolUse(uint_fast8_t const poolNum) { QF_CRIT_STAT QF_CRIT_ENTRY(); #ifndef Q_UNSAFE uint8_t const maxPool = QF_priv_.maxPool_; + + // the maximum number of initialized pools must be in configured range Q_REQUIRE_INCRIT(310, maxPool <= QF_MAX_EPOOL); - Q_REQUIRE_INCRIT(320, (0U < poolNum) && (poolNum <= maxPool)); + + // the queried poolNum must be one of the initialized pools or 0 + Q_REQUIRE_INCRIT(320, poolNum <= maxPool); #endif - uint_fast16_t const min = (uint_fast16_t)QF_priv_.ePool_[poolNum - 1U].nMin; + uint16_t nUse = 0U; + if (poolNum > 0U) { // event pool number provided? + // set event pool use from the port-dependent operation + nUse = QF_EPOOL_USE_(&QF_priv_.ePool_[poolNum - 1U]); + } + else { // special case of poolNum==0 + // calculate the sum of used entries in all event pools + for (uint_fast8_t pool = QF_priv_.maxPool_; pool > 0U; --pool) { + // add the event pool use from the port-dependent operation + nUse += QF_EPOOL_USE_(&QF_priv_.ePool_[pool - 1U]); + } + } QF_CRIT_EXIT(); - return min; + return nUse; } +#endif // QF_EPOOL_USE_ + +//............................................................................ +#ifdef QF_EPOOL_FREE_ +//! @static @public @memberof QF +uint16_t QF_getPoolFree(uint_fast8_t const poolNum) { + QF_CRIT_STAT + QF_CRIT_ENTRY(); + +#ifndef Q_UNSAFE + uint8_t const maxPool = QF_priv_.maxPool_; + + // the maximum count of initialized pools must be in configured range + Q_REQUIRE_INCRIT(410, maxPool <= QF_MAX_EPOOL); + + // the poolNum paramter must be in range + Q_REQUIRE_INCRIT(420, (0U < poolNum) && (poolNum <= maxPool)); +#endif + uint16_t const nFree = QF_EPOOL_FREE_(&QF_priv_.ePool_[poolNum - 1U]); + + QF_CRIT_EXIT(); + + return nFree; +} +#endif // QF_EPOOL_FREE_ + +//............................................................................ +#ifdef QF_EPOOL_MIN_ +//! @static @public @memberof QF +uint16_t QF_getPoolMin(uint_fast8_t const poolNum) { + QF_CRIT_STAT + QF_CRIT_ENTRY(); + +#ifndef Q_UNSAFE + uint8_t const maxPool = QF_priv_.maxPool_; + + // the maximum count of initialized pools must be in configured range + Q_REQUIRE_INCRIT(510, maxPool <= QF_MAX_EPOOL); + + // the poolNum paramter must be in range + Q_REQUIRE_INCRIT(520, (0U < poolNum) && (poolNum <= maxPool)); +#endif + // call port-specific operation for the minimum of free blocks so far + uint16_t const nMin = QF_EPOOL_MIN_(&QF_priv_.ePool_[poolNum - 1U]); + + QF_CRIT_EXIT(); + + return nMin; +} +#endif // QF_EPOOL_MIN_ //............................................................................ //! @static @private @memberof QF @@ -120,18 +196,22 @@ QEvt * QF_newX_( QF_CRIT_ENTRY(); uint8_t const maxPool = QF_priv_.maxPool_; - Q_REQUIRE_INCRIT(410, maxPool <= QF_MAX_EPOOL); - // find the pool id that fits the requested event size... + // the maximum count of initialized pools must be in configured range + Q_REQUIRE_INCRIT(610, maxPool <= QF_MAX_EPOOL); + + // find the pool that fits the requested event size... uint8_t poolNum = 0U; // zero-based poolNum initially for (; poolNum < maxPool; ++poolNum) { + // call port-specific operation for the event-size in a given pool if (evtSize <= QF_EPOOL_EVENT_SIZE_(QF_priv_.ePool_[poolNum])) { - break; + break; // event pool found } } - // - cannot run out of registered pools - Q_ASSERT_INCRIT(420, poolNum < maxPool); + // event pool must be found, which means that the reqeusted event size + // fits in one of the initialized pools + Q_ASSERT_INCRIT(620, poolNum < maxPool); ++poolNum; // convert to 1-based poolNum @@ -142,7 +222,7 @@ QEvt * QF_newX_( #ifdef Q_SPY QF_EPOOL_GET_(QF_priv_.ePool_[poolNum - 1U], e, ((margin != QF_NO_MARGIN) ? margin : 0U), - (uint_fast8_t)QS_EP_ID + poolNum); + QS_ID_EP + poolNum); #else QF_EPOOL_GET_(QF_priv_.ePool_[poolNum - 1U], e, ((margin != QF_NO_MARGIN) ? margin : 0U), 0U); @@ -151,10 +231,10 @@ QEvt * QF_newX_( if (e != (QEvt *)0) { // was e allocated correctly? e->sig = (QSignal)sig; // set the signal e->poolNum_ = poolNum; - e->refCtr_ = 0U; + e->refCtr_ = 0U; // reference count starts at 0 QS_CRIT_ENTRY(); - QS_BEGIN_PRE(QS_QF_NEW, (uint_fast8_t)QS_EP_ID + poolNum) + QS_BEGIN_PRE(QS_QF_NEW, QS_ID_EP + poolNum) QS_TIME_PRE(); // timestamp QS_EVS_PRE(evtSize); // the size of the event QS_SIG_PRE(sig); // the signal of the event @@ -167,10 +247,9 @@ QEvt * QF_newX_( // This assertion means that the event allocation failed, // and this failure cannot be tolerated. The most frequent // reason is an event leak in the application. - Q_ASSERT_INCRIT(430, margin != QF_NO_MARGIN); + Q_ASSERT_INCRIT(630, margin != QF_NO_MARGIN); - QS_BEGIN_PRE(QS_QF_NEW_ATTEMPT, - (uint_fast8_t)QS_EP_ID + poolNum) + QS_BEGIN_PRE(QS_QF_NEW_ATTEMPT, QS_ID_EP + poolNum) QS_TIME_PRE(); // timestamp QS_EVS_PRE(evtSize); // the size of the event QS_SIG_PRE(sig); // the signal of the event @@ -179,8 +258,8 @@ QEvt * QF_newX_( QF_CRIT_EXIT(); } - // the returned event e is guaranteed to be valid (not NULL) - // if we can't tolerate failed allocation + // if we can't tolerate failed allocation (margin != QF_NO_MARGIN), + // the returned event e is guaranteed to be valid (not NULL). return e; } @@ -190,15 +269,15 @@ void QF_gc(QEvt const * const e) { QF_CRIT_STAT QF_CRIT_ENTRY(); - Q_REQUIRE_INCRIT(500, e != (QEvt *)0); + // the collected event must be valid + Q_REQUIRE_INCRIT(700, e != (QEvt *)0); - uint8_t const poolNum = e->poolNum_; + uint8_t const poolNum = (uint8_t)e->poolNum_; if (poolNum != 0U) { // is it a pool event (mutable)? if (e->refCtr_ > 1U) { // isn't this the last reference? - QS_BEGIN_PRE(QS_QF_GC_ATTEMPT, - (uint_fast8_t)QS_EP_ID + poolNum) + QS_BEGIN_PRE(QS_QF_GC_ATTEMPT, QS_ID_EP + poolNum) QS_TIME_PRE(); // timestamp QS_SIG_PRE(e->sig); // the signal of the event QS_2U8_PRE(poolNum, e->refCtr_); @@ -211,11 +290,14 @@ void QF_gc(QEvt const * const e) { else { // this is the last reference to this event, recycle it #ifndef Q_UNSAFE uint8_t const maxPool = QF_priv_.maxPool_; - Q_ASSERT_INCRIT(540, maxPool <= QF_MAX_EPOOL); - Q_ASSERT_INCRIT(550, poolNum <= maxPool); + + // the maximum count of initialized pools must be in configured range + Q_ASSERT_INCRIT(740, maxPool <= QF_MAX_EPOOL); + + // the event poolNum must be one one the initialized event pools + Q_ASSERT_INCRIT(750, poolNum <= maxPool); #endif - QS_BEGIN_PRE(QS_QF_GC, - (uint_fast8_t)QS_EP_ID + poolNum) + QS_BEGIN_PRE(QS_QF_GC, QS_ID_EP + poolNum) QS_TIME_PRE(); // timestamp QS_SIG_PRE(e->sig); // the signal of the event QS_2U8_PRE(poolNum, e->refCtr_); @@ -223,10 +305,11 @@ void QF_gc(QEvt const * const e) { QF_CRIT_EXIT(); + // call port-specific operation to put the event to a given pool // NOTE: casting 'const' away is legit because 'e' is a pool event #ifdef Q_SPY QF_EPOOL_PUT_(QF_priv_.ePool_[poolNum - 1U], (QEvt *)e, - (uint_fast8_t)QS_EP_ID + poolNum); + QS_ID_EP + poolNum); #else QF_EPOOL_PUT_(QF_priv_.ePool_[poolNum - 1U], (QEvt *)e, 0U); #endif @@ -250,17 +333,24 @@ QEvt const * QF_newRef_( QF_CRIT_STAT QF_CRIT_ENTRY(); - Q_REQUIRE_INCRIT(600, e != (QEvt *)0); - Q_REQUIRE_INCRIT(620, e->refCtr_ < (2U * QF_MAX_ACTIVE)); - Q_REQUIRE_INCRIT(630, evtRef == (void *)0); + // the referenced event must be valid + Q_REQUIRE_INCRIT(800, e != (QEvt *)0); - uint_fast8_t const poolNum = e->poolNum_; - Q_ASSERT_INCRIT(640, poolNum != 0U); + // the event reference count must not exceed the number of AOs + // in the system plus each AO possibly holding one event reference + Q_REQUIRE_INCRIT(820, e->refCtr_ < (QF_MAX_ACTIVE + QF_MAX_ACTIVE)); + + // the event ref must be valid + Q_REQUIRE_INCRIT(830, evtRef == (void *)0); + + uint8_t const poolNum = (uint8_t)e->poolNum_; + + // the referenced event must be a pool event (not an immutable event) + Q_ASSERT_INCRIT(840, poolNum != 0U); QEvt_refCtr_inc_(e); // increments the ref counter - QS_BEGIN_PRE(QS_QF_NEW_REF, - (uint_fast8_t)QS_EP_ID + poolNum) + QS_BEGIN_PRE(QS_QF_NEW_REF, QS_ID_EP + poolNum) QS_TIME_PRE(); // timestamp QS_SIG_PRE(e->sig); // the signal of the event QS_2U8_PRE(poolNum, e->refCtr_); @@ -279,12 +369,14 @@ void QF_deleteRef_(void const * const evtRef) { QF_CRIT_ENTRY(); QEvt const * const e = (QEvt const *)evtRef; - Q_REQUIRE_INCRIT(700, e != (QEvt *)0); + + // the referenced event must be valid + Q_REQUIRE_INCRIT(900, e != (QEvt *)0); #ifdef Q_SPY - uint_fast8_t const poolNum = e->poolNum_; - QS_BEGIN_PRE(QS_QF_DELETE_REF, - (uint_fast8_t)QS_EP_ID + poolNum) + uint8_t const poolNum = (uint8_t)e->poolNum_; + + QS_BEGIN_PRE(QS_QF_DELETE_REF, QS_ID_EP + poolNum) QS_TIME_PRE(); // timestamp QS_SIG_PRE(e->sig); // the signal of the event QS_2U8_PRE(poolNum, e->refCtr_); diff --git a/src/qf/qf_mem.c b/src/qf/qf_mem.c index 7d51859c2..9f0b10cf4 100644 --- a/src/qf/qf_mem.c +++ b/src/qf/qf_mem.c @@ -49,6 +49,7 @@ void QMPool_init(QMPool * const me, QF_CRIT_STAT QF_CRIT_ENTRY(); + // the pool storage must be provided Q_REQUIRE_INCRIT(100, poolSto != (void *)0); me->start = (void * *)poolSto; @@ -56,10 +57,10 @@ void QMPool_init(QMPool * const me, // find # free links in a memory block, see NOTE1 me->blockSize = (QMPoolSize)(2U * sizeof(void *)); - uint_fast16_t inext = 2U; // index of the next block + uint_fast16_t index = 2U; // index of the next block while (me->blockSize < (QMPoolSize)blockSize) { me->blockSize += (QMPoolSize)sizeof(void *); - ++inext; + ++index; } // the pool buffer must fit at least one rounded-up block @@ -74,16 +75,16 @@ void QMPool_init(QMPool * const me, size >= (uint_fast32_t)me->blockSize; size -= (uint_fast32_t)me->blockSize) { - pfb[0] = &pfb[inext]; // set the next link to next free block + pfb[0] = &pfb[index]; // set the next link to next free block #ifndef Q_UNSAFE - pfb[1] = pfb[0]; // update duplicate storage + pfb[1] = pfb[0]; // update Duplicate Storage (NOT inverted) #endif pfb = (void * *)pfb[0]; // advance to the next block ++nTot; // one more free block in the pool } pfb[0] = (void *)0; // the last link points to NULL - // dynamic range check + // the total number of blocks must fit in the configured dynamic range #if (QF_MPOOL_CTR_SIZE == 1U) Q_ASSERT_INCRIT(160, nTot < 0xFFU); #elif (QF_MPOOL_CTR_SIZE == 2U) @@ -96,7 +97,7 @@ void QMPool_init(QMPool * const me, me->nMin = me->nTot; // the minimum # free blocks #ifndef Q_UNSAFE - pfb[1] = pfb[0]; // update duplicate storage + pfb[1] = pfb[0]; // update Duplicate Storage (NOT inverted) #endif QF_CRIT_EXIT(); @@ -115,17 +116,19 @@ void * QMPool_get(QMPool * const me, QF_CRIT_STAT QF_CRIT_ENTRY(); - // get volatile into temporaries + // get members into temporaries void * *pfb = me->freeHead; // pointer to free block - QMPoolCtr nFree = me->nFree; // volatile into temporary + QMPoolCtr nFree = me->nFree; // get member into temporary // have more free blocks than the requested margin? if (nFree > (QMPoolCtr)margin) { + // the free block pointer must be valid Q_ASSERT_INCRIT(330, pfb != (void * *)0); // fast temporary void * * const pfb_next = (void * *)pfb[0]; - // the free block must have integrity (duplicate storage) + + // the free block must have integrity (Duplicate Storage, NOT inverted) Q_INVARIANT_INCRIT(342, pfb_next == pfb[1]); --nFree; // one less free block @@ -154,7 +157,7 @@ void * QMPool_get(QMPool * const me, // than a free block inside the pool. pfb[0] = &me->end[1]; // invalid location beyond the end #ifndef Q_UNSAFE - pfb[1] = (void *)0; // invalidate the duplicate storage + pfb[1] = (void *)0; // invalidate the Duplicate Storage #endif QS_BEGIN_PRE(QS_QF_MPOOL_GET, qsId) @@ -190,22 +193,28 @@ void QMPool_put(QMPool * const me, Q_UNUSED_PAR(qsId); #endif - void * * const pfb = (void * *)block; // pointer to free block + void * * const pfb = (void * *)block; // ptr to free block QF_CRIT_STAT QF_CRIT_ENTRY(); + // the block returned to the pool must be valid Q_REQUIRE_INCRIT(400, pfb != (void * *)0); // the block must be in range of this pool (block from a different pool?) Q_REQUIRE_INCRIT(410, (me->start <= pfb) && (pfb <= me->end)); - // the block must NOT be in the pool already (double free) + + // the block must NOT be in the pool already (double free?) + // NOTE: a block in the pool already matches the duplicate storage, + // so the block not in the pool must NOT match the Duplicate Storage Q_INVARIANT_INCRIT(422, pfb[0] != pfb[1]); - // get volatile into temporaries + // get members into temporaries void * * const freeHead = me->freeHead; QMPoolCtr nFree = me->nFree; + // the number of free blocks must be below the total because + // one more block is just being returned to the pool Q_REQUIRE_INCRIT(450, nFree < me->nTot); ++nFree; // one more free block in this pool @@ -214,7 +223,7 @@ void QMPool_put(QMPool * const me, me->nFree = nFree; pfb[0] = freeHead; // link into the list #ifndef Q_UNSAFE - pfb[1] = freeHead; // update duplicate storage + pfb[1] = freeHead; // update Duplicate Storage (NOT inverted) #endif QS_BEGIN_PRE(QS_QF_MPOOL_PUT, qsId) @@ -226,11 +235,33 @@ void QMPool_put(QMPool * const me, QF_CRIT_EXIT(); } +//............................................................................ +//! @public @memberof QMPool +uint16_t QMPool_getUse(QMPool const * const me) { + // NOTE: this function does NOT apply critical section, so it can + // be safely called from an already established critical section. + return (uint16_t)(me->nTot - me->nFree ); +} +//............................................................................ +//! @public @memberof QMPool +uint16_t QMPool_getFree(QMPool const * const me) { + // NOTE: this function does NOT apply critical section, so it can + // be safely called from an already established critical section. + return (uint16_t)me->nFree; +} +//............................................................................ +//! @public @memberof QMPool +uint16_t QMPool_getMin(QMPool const * const me) { + // NOTE: this function does NOT apply critical section, so it can + // be safely called from an already established critical section. + return (uint16_t)me->nMin; +} + //============================================================================ // NOTE1: // The memory buffer for the pool is organized as an array of void* pointers // (see void * data type). These pointers are used to form a linked-list // of free blocks in the pool. The first location pfb[0] is the actual link. -// The second location pfb[1] is used in SafeQP as the redundant "duplicate -// storage" for the link at pfb[0]. Therefore, the minimum number of void* -// pointers (void * data type) inside a memory block is 2. +// The second location pfb[1] is used in SafeQP as the redundant Duplicate +// Storage (NOT inverted) for the link at pfb[0]. Therefore, the minimum +// number of void* pointers (void * data type) inside a memory block is 2. diff --git a/src/qf/qf_ps.c b/src/qf/qf_ps.c index 9db2a6cf0..404e81f3d 100644 --- a/src/qf/qf_ps.c +++ b/src/qf/qf_ps.c @@ -42,21 +42,31 @@ Q_DEFINE_THIS_MODULE("qf_ps") QSubscrList * QActive_subscrList_; QSignal QActive_maxPubSignal_; + +// static local helper function (declaration) +static void QActive_multicast_( + QPSet * const subscrSet, + QEvt const * const e, + void const * const sender); + //............................................................................ //! @static @public @memberof QActive void QActive_psInit( QSubscrList * const subscrSto, enum_t const maxSignal) { + // provided subscSto must be valid Q_REQUIRE_INCRIT(100, subscrSto != (QSubscrList *)0); + + // provided maximum of subscribed signals must be >= Q_USER_SIG Q_REQUIRE_INCRIT(110, maxSignal >= Q_USER_SIG); QActive_subscrList_ = subscrSto; QActive_maxPubSignal_ = (QSignal)maxSignal; - // initialize the subscriber list + // initialize all signals in the subscriber list... for (enum_t sig = 0; sig < maxSignal; ++sig) { - QPSet_setEmpty(&subscrSto[sig].set); + QPSet_setEmpty(&subscrSto[sig].set); // no subscibers to this signal } } @@ -75,9 +85,12 @@ void QActive_publish_( QF_CRIT_STAT QF_CRIT_ENTRY(); + // the published event must be valid Q_REQUIRE_INCRIT(200, e != (QEvt *)0); - QSignal const sig = e->sig; + QSignal const sig = (QSignal)e->sig; + + // published event signal must not exceed the maximum Q_REQUIRE_INCRIT(240, sig < QActive_maxPubSignal_); // make a local, modifiable copy of the subscriber set @@ -90,68 +103,87 @@ void QActive_publish_( QS_2U8_PRE(e->poolNum_, e->refCtr_); QS_END_PRE() - // is it a mutable event? - if (e->poolNum_ != 0U) { + if (e->poolNum_ != 0U) { // is it a mutable event? // NOTE: The reference counter of a mutable event is incremented to - // prevent premature recycling of the event while the multicasting - // is still in progress. At the end of the function, the garbage - // collector step (QF_gc()) decrements the reference counter and - // recycles the event if the counter drops to zero. This covers the - // case when the event was published without any subscribers. - Q_ASSERT_INCRIT(260, e->refCtr_ < (2U * QF_MAX_ACTIVE)); + // prevent premature recycling of the event while multicasting is + // still in progress. The garbage collector step (QF_gc()) at the + // end of the function decrements the reference counter and recycles + // the event if the counter drops to zero. This covers the case when + // event was published without any subscribers. QEvt_refCtr_inc_(e); } QF_CRIT_EXIT(); if (QPSet_notEmpty(&subscrSet)) { // any subscribers? - // highest-prio subscriber - uint8_t p = (uint8_t)QPSet_findMax(&subscrSet); - - QF_CRIT_ENTRY(); + QActive_multicast_(&subscrSet, e, sender); // multicast to all + } - QActive *a = QActive_registry_[p]; - Q_ASSERT_INCRIT(300, a != (QActive *)0); + // The following garbage collection step decrements the reference counter + // and recycles the event if the counter drops to zero. This covers both + // cases when the event was published with or without any subscribers. +#if (QF_MAX_EPOOL > 0U) + QF_gc(e); // recycle the event to avoid a leak +#endif +} - QF_CRIT_EXIT(); +//............................................................................ +//! @private @memberof QActive +static void QActive_multicast_( + QPSet * const subscrSet, + QEvt const * const e, + void const * const sender) +{ +#ifndef Q_SPY + Q_UNUSED_PAR(sender); +#endif - QF_SCHED_STAT_ - QF_SCHED_LOCK_(p); // lock the scheduler up to AO's prio + // highest-prio subscriber ('subscrSet' guaranteed to be NOT empty) + uint8_t p = (uint8_t)QPSet_findMax(subscrSet); - uint_fast8_t lbound = QF_MAX_ACTIVE + 1U; // fixed loop bound - for (;;) { // loop over all subscribers + QF_CRIT_STAT + QF_CRIT_ENTRY(); - // QACTIVE_POST() asserts internally if the queue overflows - QACTIVE_POST(a, e, sender); + // p != 0 is guaranteed as the result of QPSet_findMax() + Q_ASSERT_INCRIT(300, p <= QF_MAX_ACTIVE); + QActive *a = QActive_registry_[p]; - QPSet_remove(&subscrSet, p); // remove the handled subscriber - if (QPSet_isEmpty(&subscrSet)) { // no more subscribers? - break; - } + // the active object must be registered (started) + Q_ASSERT_INCRIT(310, a != (QActive *)0); - p = (uint8_t)QPSet_findMax(&subscrSet); // highest-prio subscriber + QF_CRIT_EXIT(); - QF_CRIT_ENTRY(); + QF_SCHED_STAT_ + QF_SCHED_LOCK_(p); // lock the scheduler up to AO's prio - a = QActive_registry_[p]; - // the AO must be registered with the framework - Q_ASSERT_INCRIT(340, a != (QActive *)0); + // NOTE: the following loop does not need the fixed loop bound check + // because the local subscriber set 'subscrSet' can hold at most + // QF_MAX_ACTIVE elements (rounded up to the nearest 8), which are + // removed one by one at every pass. + for (;;) { // loop over all subscribers - --lbound; // fixed loop bound - Q_INVARIANT_INCRIT(370, lbound > 0U); + // QACTIVE_POST() asserts internally if the queue overflows + QACTIVE_POST(a, e, sender); - QF_CRIT_EXIT(); + QPSet_remove(subscrSet, p); // remove the handled subscriber + if (QPSet_isEmpty(subscrSet)) { // no more subscribers? + break; } - QF_SCHED_UNLOCK_(); // unlock the scheduler + // find the next highest-prio subscriber + p = (uint8_t)QPSet_findMax(subscrSet); + + QF_CRIT_ENTRY(); + + a = QActive_registry_[p]; + + // the AO must be registered with the framework + Q_ASSERT_INCRIT(340, a != (QActive *)0); + + QF_CRIT_EXIT(); } - // The following garbage collection step decrements the reference counter - // and recycles the event if the counter drops to zero. This covers both - // cases when the event was published with or without any subscribers. -#if (QF_MAX_EPOOL > 0U) - QF_gc(e); // recycle the event to avoid a leak -#endif + QF_SCHED_UNLOCK_(); // unlock the scheduler } //............................................................................ @@ -162,13 +194,18 @@ void QActive_subscribe(QActive const * const me, QF_CRIT_STAT QF_CRIT_ENTRY(); - // check this AO ("me") and the registration... uint8_t const p = me->prio; + + // the AO's prio. must be in range Q_REQUIRE_INCRIT(420, (0U < p) && (p <= QF_MAX_ACTIVE)); + + // the subscriber AO must be registered (started) Q_REQUIRE_INCRIT(440, me == QActive_registry_[p]); - // check sig parameter and subscription list... + // the sig parameter must not overlap reserved signals Q_REQUIRE_INCRIT(460, sig >= Q_USER_SIG); + + // the subscribed signal must be below the maximum of published signals Q_REQUIRE_INCRIT(480, (QSignal)sig < QActive_maxPubSignal_); QS_BEGIN_PRE(QS_QF_ACTIVE_SUBSCRIBE, p) @@ -177,7 +214,7 @@ void QActive_subscribe(QActive const * const me, QS_OBJ_PRE(me); // this active object QS_END_PRE() - // insert the prio. into the subscriber set + // insert the AO's prio. into the subscriber set for the signal QPSet_insert(&QActive_subscrList_[sig].set, p); QF_CRIT_EXIT(); @@ -191,13 +228,18 @@ void QActive_unsubscribe(QActive const * const me, QF_CRIT_STAT QF_CRIT_ENTRY(); - // check this AO ("me") and the registration... uint8_t const p = me->prio; + + // the AO's prio. must be in range Q_REQUIRE_INCRIT(520, (0U < p) && (p <= QF_MAX_ACTIVE)); + + // the subscriber AO must be registered (started) Q_REQUIRE_INCRIT(540, me == QActive_registry_[p]); - // check sig parameter and subscription list... + // the sig parameter must not overlap reserved signals Q_REQUIRE_INCRIT(560, sig >= Q_USER_SIG); + + // the unsubscribed signal must be below the maximum of published signals Q_REQUIRE_INCRIT(580, (QSignal)sig < QActive_maxPubSignal_); QS_BEGIN_PRE(QS_QF_ACTIVE_UNSUBSCRIBE, p) @@ -206,7 +248,7 @@ void QActive_unsubscribe(QActive const * const me, QS_OBJ_PRE(me); // this active object QS_END_PRE() - // remove the prio. from the subscriber set + // remove the AO's prio. from the subscriber set for the signal QPSet_remove(&QActive_subscrList_[sig].set, p); QF_CRIT_EXIT(); @@ -218,21 +260,27 @@ void QActive_unsubscribeAll(QActive const * const me) { QF_CRIT_STAT QF_CRIT_ENTRY(); - // check this AO ("me") and the registration... - uint_fast8_t const p = (uint_fast8_t)me->prio; + uint8_t const p = me->prio; + + // the AO's prio. must be in range Q_REQUIRE_INCRIT(620, (0U < p) && (p <= QF_MAX_ACTIVE)); + + // the subscriber AO must be registered (started) Q_REQUIRE_INCRIT(640, me == QActive_registry_[p]); - // check maxPubSignal_ and subscription list... QSignal const maxPubSig = QActive_maxPubSignal_; + + // the maximum of published signals must not overlap the reserved signals Q_REQUIRE_INCRIT(670, maxPubSig >= (QSignal)Q_USER_SIG); QF_CRIT_EXIT(); + // remove this AO's prio. from subscriber lists of all published signals for (QSignal sig = Q_USER_SIG; sig < maxPubSig; ++sig) { QF_CRIT_ENTRY(); if (QPSet_hasElement(&QActive_subscrList_[sig].set, p)) { + // remove the AO's prio. from the subscriber set for the signal QPSet_remove(&QActive_subscrList_[sig].set, p); QS_BEGIN_PRE(QS_QF_ACTIVE_UNSUBSCRIBE, p) diff --git a/src/qf/qf_qact.c b/src/qf/qf_qact.c index 76a3eb434..a55cf8fac 100644 --- a/src/qf/qf_qact.c +++ b/src/qf/qf_qact.c @@ -39,23 +39,24 @@ Q_DEFINE_THIS_MODULE("qf_qact") -//............................................................................ +//! @static @private @memberof QActive +QActive * QActive_registry_[QF_MAX_ACTIVE + 1U]; + +//! @static @private @memberof QF +QF_Attr QF_priv_; + +//---------------------------------------------------------------------------- //! @protected @memberof QActive void QActive_ctor(QActive * const me, QStateHandler const initial) { - // clear the whole QActive object, 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). - QF_bzero_(me, sizeof(*me)); - - // NOTE: QActive inherits the abstract QAsm class, but it calls the - // constructor of the QHsm subclass. This is because QActive inherits - // the behavior from the QHsm subclass. + // NOTE: QActive indirectly inherits the abstract QAsm base class, + // but it will delegate the state machine behavior to the QHsm class, + // so the following initiaization is identical as in QHsm ctor: QHsm_ctor((QHsm *)(me), initial); // NOTE: this vtable is identical as QHsm, but is provided - // for the QActive subclass to provide a UNIQUE vptr to distinguish + // for the QActive subclass to ensure a UNIQUE vptr to distinguish // subclasses of QActive (e.g., in the debugger). static struct QAsmVtable const vtable = { // QActive virtual table &QHsm_init_, @@ -74,32 +75,38 @@ void QActive_register_(QActive * const me) { QF_CRIT_STAT QF_CRIT_ENTRY(); - uint8_t p = me->prio; if (me->pthre == 0U) { // preemption-threshold not defined? - me->pthre = p; // apply the default + me->pthre = me->prio; // apply the default } - Q_REQUIRE_INCRIT(100, (0U < p) && (p <= QF_MAX_ACTIVE)); - Q_REQUIRE_INCRIT(110, QActive_registry_[p] == (QActive *)0); - Q_REQUIRE_INCRIT(130, p <= me->pthre); + // AO's prio. must be in range + Q_REQUIRE_INCRIT(100, (0U < me->prio) && (me->prio <= QF_MAX_ACTIVE)); + + // the AO must NOT be registered already + Q_REQUIRE_INCRIT(110, QActive_registry_[me->prio] == (QActive *)0); + + // the AO's prio. must not exceed the preemption threshold + Q_REQUIRE_INCRIT(130, me->prio <= me->pthre); #ifndef Q_UNSAFE uint8_t prev_thre = me->pthre; uint8_t next_thre = me->pthre; - for (p = p - 1U; p > 0U; --p) { + for (uint8_t p = me->prio - 1U; p > 0U; --p) { if (QActive_registry_[p] != (QActive *)0) { prev_thre = QActive_registry_[p]->pthre; break; } } - for (p = me->prio + 1U; p <= QF_MAX_ACTIVE; ++p) { + for (uint8_t p = me->prio + 1U; p <= QF_MAX_ACTIVE; ++p) { if (QActive_registry_[p] != (QActive *)0) { next_thre = QActive_registry_[p]->pthre; break; } } + // the preemption threshold of this AO must be between + // preemption threshold of the previous AO and next AO Q_ASSERT_INCRIT(160, (prev_thre <= me->pthre) && (me->pthre <= next_thre)); @@ -117,12 +124,137 @@ void QActive_unregister_(QActive * const me) { QF_CRIT_STAT QF_CRIT_ENTRY(); - uint8_t const p = me->prio; + uint8_t const p = me->prio; // put AO's prio. in a temporary + + // AO's prio. must be in range Q_REQUIRE_INCRIT(210, (0U < p) && (p <= QF_MAX_ACTIVE)); - Q_REQUIRE_INCRIT(230, QActive_registry_[p] == me); + + // this AO must be registered at prio. p + Q_REQUIRE_INCRIT(230, me == QActive_registry_[p]); me->super.state.fun = Q_STATE_CAST(0); // invalidate the state QActive_registry_[p] = (QActive *)0; // free-up the prio. level QF_CRIT_EXIT(); } + +//---------------------------------------------------------------------------- +#ifndef QF_LOG2 +uint_fast8_t QF_LOG2(QPSetBits const bitmask) { + // look-up table for log2(0..15) + static uint8_t const log2LUT[16] = { + 0U, 1U, 2U, 2U, 3U, 3U, 3U, 3U, + 4U, 4U, 4U, 4U, 4U, 4U, 4U, 4U + }; + uint_fast8_t n = 0U; + QPSetBits x = bitmask; + QPSetBits tmp; // temporary for modified bitmask parameter + +#if (QF_MAX_ACTIVE > 16U) + tmp = (x >> 16U); + if (tmp != 0U) { // x > 2^16? + n += 16U; + x = tmp; + } +#endif +#if (QF_MAX_ACTIVE > 8U) + tmp = (x >> 8U); + if (tmp != 0U) { // x > 2^8? + n += 8U; + x = tmp; + } +#endif + tmp = (x >> 4U); + if (tmp != 0U) { // x > 2^4? + n += 4U; + x = tmp; + } + // x is guaranteed to be in the 0..15 range for the look-up + return (uint_fast8_t)(n + log2LUT[x]); +} +#endif // ndef QF_LOG2 + +//---------------------------------------------------------------------------- +//! @public @memberof QPSet +void QPSet_setEmpty(QPSet * const me) { + me->bits0 = 0U; // clear bitmask for elements 1..32 +#if (QF_MAX_ACTIVE > 32) + me->bits1 = 0U; // clear bitmask for elements 33..64 +#endif +} +//............................................................................ +//! @public @memberof QPSet +bool QPSet_isEmpty(QPSet const * const me) { +#if (QF_MAX_ACTIVE <= 32U) + return (me->bits0 == 0U); // check only bitmask for elements 1..32 +#else + return (me->bits0 == 0U) // bitmask for elements 1..32 empty? + ? (me->bits1 == 0U) // check bitmask for for elements 33..64 + : false; // the set is NOT empty +#endif +} +//............................................................................ +//! @public @memberof QPSet +bool QPSet_notEmpty(QPSet const * const me) { +#if (QF_MAX_ACTIVE <= 32U) + return (me->bits0 != 0U); // check only bitmask for elements 1..32 +#else + return (me->bits0 != 0U) // bitmask for elements 1..32 empty? + ? true // the set is NOT empty + : (me->bits1 != 0U); // check bitmask for for elements 33..64 +#endif +} +//............................................................................ +//! @public @memberof QPSet +bool QPSet_hasElement(QPSet const * const me, uint_fast8_t const n) { +#if (QF_MAX_ACTIVE <= 32U) + // check the bit only in bitmask for elements 1..32 + return (me->bits0 & ((QPSetBits)1U << (n - 1U))) != 0U; +#else + return (n <= 32U) // which group of elements (1..32 or 33..64)? + ? ((me->bits0 & ((QPSetBits)1U << (n - 1U))) != 0U) + : ((me->bits1 & ((QPSetBits)1U << (n - 33U))) != 0U); +#endif +} +//............................................................................ +//! @public @memberof QPSet +void QPSet_insert(QPSet * const me, uint_fast8_t const n) { +#if (QF_MAX_ACTIVE <= 32U) + // set the bit only in bitmask for elements 1..32 + me->bits0 = (me->bits0 | ((QPSetBits)1U << (n - 1U))); +#else + if (n <= 32U) { // set the bit in the bitmask for elements 1..32? + me->bits0 = (me->bits0 | ((QPSetBits)1U << (n - 1U))); + } + else { // set the bit in the bitmask for for elements 33..64 + me->bits1 = (me->bits1 | ((QPSetBits)1U << (n - 33U))); + } +#endif +} +//............................................................................ +//! @public @memberof QPSet +void QPSet_remove(QPSet * const me, uint_fast8_t const n) { +#if (QF_MAX_ACTIVE <= 32U) + // clear the bit only in bitmask for elements 1..32 + me->bits0 = (me->bits0 & (QPSetBits)(~((QPSetBits)1U << (n - 1U)))); +#else + if (n <= 32U) { // clear the bit in the bitmask for elements 1..32? + (me->bits0 = (me->bits0 & ~((QPSetBits)1U << (n - 1U)))); + } + else { // clear the bit in the bitmask for for elements 33..64 + (me->bits1 = (me->bits1 & ~((QPSetBits)1U << (n - 33U)))); + } +#endif +} +//............................................................................ +//! @public @memberof QPSet +uint_fast8_t QPSet_findMax(QPSet const * const me) { +#if (QF_MAX_ACTIVE <= 32U) + // check only the bitmask for elements 1..32 + return QF_LOG2(me->bits0); +#else + return (me->bits1 != 0U) // bitmask for elements 32..64 not empty? + ? (32U + QF_LOG2(me->bits1)) // 32 + log2(bits 33..64) + : (QF_LOG2(me->bits0)); // log2(bits 1..32) +#endif +} diff --git a/src/qf/qf_qeq.c b/src/qf/qf_qeq.c index 4209152ec..718e2a2f6 100644 --- a/src/qf/qf_qeq.c +++ b/src/qf/qf_qeq.c @@ -49,18 +49,19 @@ void QEQueue_init(QEQueue * const me, QF_CRIT_ENTRY(); #if (QF_EQUEUE_CTR_SIZE == 1U) - Q_REQUIRE_INCRIT(100, qLen < 0xFFU); + // the qLen paramter must not exceed the dynamic range of uint8_t + Q_REQUIRE_INCRIT(10, qLen < 0xFFU); #endif me->frontEvt = (QEvt *)0; // no events in the queue me->ring = qSto; // the beginning of the ring buffer - me->end = (QEQueueCtr)qLen; - if (qLen > 0U) { - me->head = 0U; - me->tail = 0U; + me->end = (QEQueueCtr)qLen; // index of the last element + if (qLen > 0U) { // queue buffer storage provided? + me->head = 0U; // head index: for removing events + me->tail = 0U; // tail index: for inserting events } me->nFree = (QEQueueCtr)(qLen + 1U); // +1 for frontEvt - me->nMin = me->nFree; + me->nMin = me->nFree; // minimum so far QF_CRIT_EXIT(); } @@ -79,25 +80,27 @@ bool QEQueue_post(QEQueue * const me, QF_CRIT_STAT QF_CRIT_ENTRY(); - Q_REQUIRE_INCRIT(200, e != (QEvt *)0); + // the posted event must be valid + Q_REQUIRE_INCRIT(100, e != (QEvt *)0); - QEQueueCtr nFree = me->nFree; // get volatile into temporary + QEQueueCtr nFree = me->nFree; // get member into temporary bool status = (nFree > 0U); if (margin == QF_NO_MARGIN) { // no margin requested? // queue must not overflow - Q_ASSERT_INCRIT(240, status); + Q_ASSERT_INCRIT(130, status); } else { status = (nFree > (QEQueueCtr)margin); } - if (status) { - // is it a mutable event? - if (e->poolNum_ != 0U) { - Q_ASSERT_INCRIT(250, e->refCtr_ < (2U * QF_MAX_ACTIVE)); + if (status) { // can post the event? + +#if (QF_MAX_EPOOL > 0U) + if (e->poolNum_ != 0U) { // is it a mutable event? QEvt_refCtr_inc_(e); // increment the reference counter } +#endif // (QF_MAX_EPOOL > 0U) --nFree; // one free entry just used up me->nFree = nFree; // update the original @@ -120,14 +123,14 @@ bool QEQueue_post(QEQueue * const me, me->frontEvt = e; // deliver event directly } else { // queue was not empty, insert event into the ring-buffer - QEQueueCtr head = me->head; // get volatile into temporary + QEQueueCtr head = me->head; // get member into temporary me->ring[head] = e; // insert e into buffer if (head == 0U) { // need to wrap the head? head = me->end; } --head; // advance head (counter-clockwise) - me->head = head; // update the original + me->head = head; // update the member original } } else { // event cannot be posted @@ -161,20 +164,21 @@ void QEQueue_postLIFO(QEQueue * const me, QF_CRIT_STAT QF_CRIT_ENTRY(); - Q_REQUIRE_INCRIT(300, e != (QEvt *)0); + // event e to be posted must be valid + Q_REQUIRE_INCRIT(200, e != (QEvt *)0); - QEQueueCtr nFree = me->nFree; // get volatile into temporary + QEQueueCtr nFree = me->nFree; // get member into temporary // must be able to LIFO-post the event - Q_REQUIRE_INCRIT(330, nFree != 0U); + Q_REQUIRE_INCRIT(230, nFree != 0U); if (e->poolNum_ != 0U) { // is it a mutable event? - Q_ASSERT_INCRIT(340, e->refCtr_ < (2U * QF_MAX_ACTIVE)); QEvt_refCtr_inc_(e); // increment the reference counter } --nFree; // one free entry just used up - me->nFree = nFree; // update the original + me->nFree = nFree; // update the member original + if (me->nMin > nFree) { // is this the new minimum? me->nMin = nFree; // update minimum so far } @@ -188,17 +192,17 @@ void QEQueue_postLIFO(QEQueue * const me, QS_EQC_PRE(me->nMin); // min # free entries QS_END_PRE() - QEvt const * const frontEvt = me->frontEvt; + QEvt const * const frontEvt = me->frontEvt; // get member into temporary me->frontEvt = e; // deliver the event directly to the front if (frontEvt != (QEvt *)0) { // was the queue NOT empty? - QEQueueCtr tail = me->tail; // get volatile into temporary + QEQueueCtr tail = me->tail; // get member into temporary ++tail; if (tail == me->end) { // need to wrap the tail? tail = 0U; // wrap around } - me->tail = tail; + me->tail = tail; // update the member original me->ring[tail] = frontEvt; } @@ -219,19 +223,20 @@ struct QEvt const * QEQueue_get(QEQueue * const me, QEvt const * const e = me->frontEvt; // always remove evt from the front - if (e != (QEvt *)0) { // was the queue not empty? - QEQueueCtr nFree = me->nFree; // get volatile into temporary + if (e != (QEvt *)0) { // is the queue NOT empty? + QEQueueCtr nFree = me->nFree; // get member into temporary ++nFree; // one more free event in the queue me->nFree = nFree; // update the # free - // any events in the ring buffer? - if (nFree <= me->end) { + if (nFree <= me->end) { // any events in the ring buffer? // remove event from the tail - QEQueueCtr tail = me->tail; // get volatile into temporary + QEQueueCtr tail = me->tail; // get member into temporary QEvt const * const frontEvt = me->ring[tail]; - Q_ASSERT_INCRIT(450, frontEvt != (QEvt *)0); + + // the queue must have at least one event (at the front) + Q_ASSERT_INCRIT(350, frontEvt != (QEvt *)0); QS_BEGIN_PRE(QS_QF_EQUEUE_GET, qsId) QS_TIME_PRE(); // timestamp @@ -241,19 +246,19 @@ struct QEvt const * QEQueue_get(QEQueue * const me, QS_EQC_PRE(nFree); // # free entries QS_END_PRE() - me->frontEvt = frontEvt; // update the original + me->frontEvt = frontEvt; // update the member original if (tail == 0U) { // need to wrap the tail? - tail = me->end; + tail = me->end; // wrap around } --tail; // advance the tail (counter-clockwise) - me->tail = tail; // update the original + me->tail = tail; // update the member original } else { me->frontEvt = (QEvt *)0; // queue becomes empty // all entries in the queue must be free (+1 for frontEvt) - Q_ASSERT_INCRIT(440, nFree == (me->end + 1U)); + Q_ASSERT_INCRIT(360, nFree == (me->end + 1U)); QS_BEGIN_PRE(QS_QF_EQUEUE_GET_LAST, qsId) QS_TIME_PRE(); // timestamp @@ -268,3 +273,37 @@ struct QEvt const * QEQueue_get(QEQueue * const me, return e; } + +//............................................................................ +//! @public @memberof QEQueue +uint16_t QEQueue_getUse(QEQueue const * const me) { + // NOTE: this function does NOT apply critical section, so it can + // be safely called from an already established critical section. + uint16_t nUse = 0U; + if (me->frontEvt != (QEvt *)0) { // queue not empty? + nUse = (uint16_t)((uint16_t)me->end + 1U - (uint16_t)me->nFree); + } + return nUse; +} +//............................................................................ +//! @public @memberof QEQueue +uint16_t QEQueue_getFree(QEQueue const * const me) { + // NOTE: this function does NOT apply critical section, so it can + // be safely called from an already established critical section. + return (uint16_t)me->nFree; +} +//............................................................................ +//! @public @memberof QEQueue +uint16_t QEQueue_getMin(QEQueue const * const me) { + // NOTE: this function does NOT apply critical section, so it can + // be safely called from an already established critical section. + return (uint16_t)me->nMin; +} +//............................................................................ +//! @public @memberof QEQueue +bool QEQueue_isEmpty(QEQueue const * const me) { + // NOTE: this function does NOT apply critical section, so it can + // be safely called from an already established critical section. + return me->frontEvt == (struct QEvt *)0; +} + diff --git a/src/qf/qf_qmact.c b/src/qf/qf_qmact.c index 41358091d..5460e1e22 100644 --- a/src/qf/qf_qmact.c +++ b/src/qf/qf_qmact.c @@ -44,14 +44,9 @@ void QMActive_ctor(QMActive * const me, QStateHandler const initial) { - // clear the whole QMActive object, 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). - QF_bzero_(me, sizeof(*me)); - - // NOTE: QActive inherits the QActvie class, but it calls the - // constructor of the QMsm subclass. This is because QMActive inherits - // the behavior from the QMsm subclass. + // NOTE: QMActive indirectly inherits the abstract QAsm base class, + // but it will delegate the state machine behavior to the QMsm class, + // so the following initiaization is identical as in QMsm ctor: QMsm_ctor((QMsm *)(me), initial); // NOTE: this vtable is identical as QMsm, but is provided diff --git a/src/qf/qf_time.c b/src/qf/qf_time.c index 7c96de60a..8ead3aa66 100644 --- a/src/qf/qf_time.c +++ b/src/qf/qf_time.c @@ -37,6 +37,9 @@ #include "qs_dummy.h" // disable the QS software tracing #endif // Q_SPY +//============================================================================ +#if (QF_MAX_TICK_RATE > 0U) + Q_DEFINE_THIS_MODULE("qf_time") //............................................................................ @@ -51,20 +54,23 @@ void QTimeEvt_ctorX(QTimeEvt * const me, { QF_CRIT_STAT QF_CRIT_ENTRY(); + + // the signal must be != 0, but other reserved signals are allowed Q_REQUIRE_INCRIT(300, sig != 0); + + // the tick rate must be in the configured range Q_REQUIRE_INCRIT(310, tickRate < QF_MAX_TICK_RATE); QF_CRIT_EXIT(); - QEvt_ctor(&me->super, sig); + QEvt_ctor(&me->super, sig); // the superclass' ctor + me->super.refCtr_ = 0U; // adjust from QEvt_ctor(sig) me->next = (QTimeEvt *)0; - me->act = act; + me->act = act; // might be NULL for a time-event head me->ctr = 0U; me->interval = 0U; me->tickRate = (uint8_t)tickRate; me->flags = 0U; - - me->super.refCtr_ = 0U; // adjust from QEvt_ctor(sig) } //............................................................................ @@ -76,7 +82,7 @@ void QTimeEvt_armX(QTimeEvt * const me, QF_CRIT_STAT QF_CRIT_ENTRY(); - // dynamic range checks + // nTicks and interval parameters must fit in the configured dynamic range #if (QF_TIMEEVT_CTR_SIZE == 1U) Q_REQUIRE_INCRIT(400, nTicks < 0xFFU); Q_REQUIRE_INCRIT(410, interval < 0xFFU); @@ -91,10 +97,17 @@ void QTimeEvt_armX(QTimeEvt * const me, uint8_t const tickRate = me->tickRate; + // nTicks must be != 0 for arming a time event Q_REQUIRE_INCRIT(440, nTicks != 0U); + + // the time event must not be already armed Q_REQUIRE_INCRIT(450, ctr == 0U); + + // the AO associated with this time event must be valid Q_REQUIRE_INCRIT(460, me->act != (void *)0); - Q_REQUIRE_INCRIT(470, tickRate < (uint_fast8_t)QF_MAX_TICK_RATE); + + // the tick rate of this time event must be in range + Q_REQUIRE_INCRIT(470, tickRate < QF_MAX_TICK_RATE); me->ctr = (QTimeEvtCtr)nTicks; me->interval = (QTimeEvtCtr)interval; @@ -104,14 +117,14 @@ void QTimeEvt_armX(QTimeEvt * const me, // rate a time event can be disarmed and yet still linked into the list // because un-linking is performed exclusively in QTimeEvt_tick_(). if ((me->flags & QTE_FLAG_IS_LINKED) == 0U) { - me->flags |= QTE_FLAG_IS_LINKED; // mark as linked - // The time event is initially inserted into the separate // "freshly armed" list based on timeEvtHead_[tickRate].act. // Only later, inside QTimeEvt_tick_(), the "freshly armed" // list is appended to the main list of armed time events based on // timeEvtHead_[tickRate].next. Again, this is to keep any // changes to the main list exclusively inside QTimeEvt_tick_(). + + me->flags |= QTE_FLAG_IS_LINKED; // mark as linked me->next = (QTimeEvt *)QTimeEvt_timeEvtHead_[tickRate].act; QTimeEvt_timeEvtHead_[tickRate].act = me; } @@ -134,7 +147,7 @@ bool QTimeEvt_disarm(QTimeEvt * const me) { QF_CRIT_STAT QF_CRIT_ENTRY(); - QTimeEvtCtr const ctr = me->ctr; + QTimeEvtCtr const ctr = me->ctr; // move member into temporary #ifdef Q_SPY uint_fast8_t const qsId = QACTIVE_CAST_(me->act)->prio; @@ -180,7 +193,7 @@ bool QTimeEvt_rearm(QTimeEvt * const me, QF_CRIT_STAT QF_CRIT_ENTRY(); - // dynamic range checks + // nTicks parameter must fit in the configured dynamic range #if (QF_TIMEEVT_CTR_SIZE == 1U) Q_REQUIRE_INCRIT(600, nTicks < 0xFFU); #elif (QF_TIMEEVT_CTR_SIZE == 2U) @@ -190,8 +203,13 @@ bool QTimeEvt_rearm(QTimeEvt * const me, uint8_t const tickRate = me->tickRate; QTimeEvtCtr const ctr = me->ctr; + // nTicks must be != 0 for arming a time event Q_REQUIRE_INCRIT(610, nTicks != 0U); + + // the AO associated with this time event must be valid Q_REQUIRE_INCRIT(620, me->act != (void *)0); + + // the tick rate of this time event must be in range Q_REQUIRE_INCRIT(630, tickRate < QF_MAX_TICK_RATE); #ifdef Q_SPY @@ -210,7 +228,6 @@ bool QTimeEvt_rearm(QTimeEvt * const me, // is the time event unlinked? if ((me->flags & QTE_FLAG_IS_LINKED) == 0U) { - me->flags |= QTE_FLAG_IS_LINKED; // mark as linked // The time event is initially inserted into the separate // "freshly armed" list based on timeEvtHead_[tickRate].act. @@ -218,6 +235,8 @@ bool QTimeEvt_rearm(QTimeEvt * const me, // list is appended to the main list of armed time events based on // timeEvtHead_[tickRate].next. Again, this is to keep any // changes to the main list exclusively inside QTimeEvt_tick()_. + + me->flags |= QTE_FLAG_IS_LINKED; // mark as linked me->next = (QTimeEvt *)QTimeEvt_timeEvtHead_[tickRate].act; QTimeEvt_timeEvtHead_[tickRate].act = me; } @@ -246,35 +265,14 @@ bool QTimeEvt_wasDisarmed(QTimeEvt * const me) { QF_CRIT_STAT QF_CRIT_ENTRY(); + // was this time-event disarmed automatically upon expiration? bool const wasDisarmed = (me->flags & QTE_FLAG_WAS_DISARMED) != 0U; - me->flags |= QTE_FLAG_WAS_DISARMED; // mark as disarmed - - QF_CRIT_EXIT(); - return wasDisarmed; -} + me->flags |= QTE_FLAG_WAS_DISARMED; // mark as disarmed (SIDE EFFECT!) -//............................................................................ -//! @public @memberof QTimeEvt -QTimeEvtCtr QTimeEvt_currCtr(QTimeEvt const * const me) { - QF_CRIT_STAT - QF_CRIT_ENTRY(); - QTimeEvtCtr const ctr = me->ctr; QF_CRIT_EXIT(); - return ctr; -} - -//............................................................................ -//! @static @private @memberof QTimeEvt -void QTimeEvt_init(void) { - for (uint_fast8_t tickRate = 0U; - tickRate < Q_DIM(QTimeEvt_timeEvtHead_); - ++tickRate) - { - QTimeEvt_ctorX(&QTimeEvt_timeEvtHead_[tickRate], - (QActive *)0, Q_USER_SIG, tickRate); - } + return wasDisarmed; } //............................................................................ @@ -290,6 +288,7 @@ void QTimeEvt_tick_( QF_CRIT_STAT QF_CRIT_ENTRY(); + // the tick rate of this time event must be in range Q_REQUIRE_INCRIT(800, tickRate < Q_DIM(QTimeEvt_timeEvtHead_)); QTimeEvt *prev = &QTimeEvt_timeEvtHead_[tickRate]; @@ -302,8 +301,7 @@ void QTimeEvt_tick_( QS_END_PRE() #endif - // scan the linked-list of time events at this rate... - uint_fast8_t lbound = (2U * QF_MAX_ACTIVE); // fixed loop bound + // scan the linked-list of time events at this tick rate... for (;;) { QTimeEvt *te = prev->next; // advance down the time evt. list @@ -318,7 +316,7 @@ void QTimeEvt_tick_( QTimeEvt_timeEvtHead_[tickRate].act = (void *)0; } - QTimeEvtCtr ctr = te->ctr; // move volatile into temporary + QTimeEvtCtr ctr = te->ctr; // move member into temporary if (ctr == 0U) { // time event scheduled for removal? prev->next = te->next; @@ -352,15 +350,12 @@ void QTimeEvt_tick_( } else { // time event keeps timing out --ctr; // decrement the tick counter - te->ctr = ctr; // update the original + te->ctr = ctr; // update the member original prev = te; // advance to this time event QF_CRIT_EXIT(); // exit crit. section to reduce latency } QF_CRIT_ENTRY(); // re-enter crit. section to continue the loop - - --lbound; // fixed loop bound - Q_INVARIANT_INCRIT(890, lbound > 0U); } QF_CRIT_EXIT(); } @@ -371,19 +366,32 @@ bool QTimeEvt_noActive(uint_fast8_t const tickRate) { // NOTE: this function must be called *inside* critical section Q_REQUIRE_INCRIT(900, tickRate < QF_MAX_TICK_RATE); - bool inactive = false; + bool const noActive = + (QTimeEvt_timeEvtHead_[tickRate].next == (QTimeEvt *)0); - if (QTimeEvt_timeEvtHead_[tickRate].next != (QTimeEvt *)0) { - // empty - } - else if ((QTimeEvt_timeEvtHead_[tickRate].act != (void *)0)) { - // empty - } - else { - inactive = true; - } + return noActive; +} - return inactive; +//............................................................................ +//! @public @memberof QTimeEvt +QTimeEvtCtr QTimeEvt_getCtr(QTimeEvt const * const me) { + // NOTE: this function does NOT apply critical section, so it can + // be safely called from an already established critical section. + return me->ctr; +} + +//............................................................................ +//! @static @private @memberof QTimeEvt +void QTimeEvt_init(void) { + // call ctors for time event heads for all configured tick rates + for (uint_fast8_t tickRate = 0U; + tickRate < Q_DIM(QTimeEvt_timeEvtHead_); + ++tickRate) + { + // time event head has invalid AO and Q_USER_SIG as signal + QTimeEvt_ctorX(&QTimeEvt_timeEvtHead_[tickRate], + (QActive *)0, Q_USER_SIG, tickRate); + } } //............................................................................ @@ -429,3 +437,11 @@ QTimeEvt * QTimeEvt_expire_(QTimeEvt * const me, return prev; } + +#else // (QF_MAX_TICK_RATE > 0U) + +//............................................................................ +void QTimeEvt_init(void) { // dummy init +} + +#endif // (QF_MAX_TICK_RATE > 0U) diff --git a/src/qk/qk.c b/src/qk/qk.c index 111b7b082..5ecec42eb 100644 --- a/src/qk/qk.c +++ b/src/qk/qk.c @@ -49,19 +49,19 @@ QK_Attr QK_priv_; //............................................................................ //! @static @public @memberof QK -QSchedStatus QK_schedLock(uint_fast8_t const ceiling) { +QSchedStatus QK_schedLock(uint8_t const ceiling) { QF_CRIT_STAT QF_CRIT_ENTRY(); + // scheduler should never be locked inside an ISR Q_REQUIRE_INCRIT(100, !QK_ISR_CONTEXT_()); - // first store the previous lock prio QSchedStatus stat = 0xFFU; // assume scheduler NOT locked - if (ceiling > QK_priv_.lockCeil) { // raising the lock ceiling? + if (ceiling > QK_priv_.lockCeil) { // increasing the lock ceiling? QS_BEGIN_PRE(QS_SCHED_LOCK, QK_priv_.actPrio) QS_TIME_PRE(); // timestamp // the previous lock ceiling & new lock ceiling - QS_2U8_PRE(QK_priv_.lockCeil, (uint8_t)ceiling); + QS_2U8_PRE(QK_priv_.lockCeil, ceiling); QS_END_PRE() // previous status of the lock @@ -83,8 +83,11 @@ void QK_schedUnlock(QSchedStatus const prevCeil) { QF_CRIT_STAT QF_CRIT_ENTRY(); + // scheduler should never be unlocked inside an ISR Q_REQUIRE_INCRIT(200, !QK_ISR_CONTEXT_()); - Q_REQUIRE_INCRIT(210, QK_priv_.lockCeil > prevCeil); + + // the current lock-ceiling must be higher than the previous ceiling + Q_REQUIRE_INCRIT(220, QK_priv_.lockCeil > prevCeil); QS_BEGIN_PRE(QS_SCHED_UNLOCK, QK_priv_.actPrio) QS_TIME_PRE(); // timestamp @@ -129,7 +132,7 @@ uint_fast8_t QK_sched_(void) { } } - return p; + return p; // the next priority or 0 } //............................................................................ @@ -145,7 +148,7 @@ uint_fast8_t QK_sched_act_( QPSet_remove(&QK_priv_.readySet, p); } - if (QPSet_isEmpty(&QK_priv_.readySet)) { + if (QPSet_isEmpty(&QK_priv_.readySet)) { // no AOs ready to run? p = 0U; // no activation needed } else { @@ -176,8 +179,11 @@ void QK_activate_(void) { uint8_t const prio_in = QK_priv_.actPrio; // save initial prio. uint8_t p = QK_priv_.nextPrio; // next prio to run - Q_REQUIRE_INCRIT(520, prio_in <= QF_MAX_ACTIVE); - Q_REQUIRE_INCRIT(530, (0U < p) && (p <= QF_MAX_ACTIVE)); + // the activated AO's prio must be in range and cannot be 0 (idle thread) + Q_REQUIRE_INCRIT(520, (0U < p) && (p <= QF_MAX_ACTIVE)); + + // the initial prio. must be lower than the activated AO's prio. + Q_REQUIRE_INCRIT(530, prio_in < p); #if (defined QF_ON_CONTEXT_SW) || (defined Q_SPY) uint8_t pprev = prio_in; @@ -186,8 +192,10 @@ void QK_activate_(void) { QK_priv_.nextPrio = 0U; // clear for the next time uint8_t pthre_in = 0U; // assume preempting the idle thread - if (prio_in != 0U) { // preempting NOT the idle thread + if (prio_in > 0U) { // preempting a regular thread (NOT the idle thread)? QActive const * const a = QActive_registry_[prio_in]; + + // the AO must be registered at prio. prio_in Q_ASSERT_INCRIT(540, a != (QActive *)0); pthre_in = a->pthre; @@ -196,7 +204,9 @@ void QK_activate_(void) { // loop until no more ready-to-run AOs of higher pthre than the initial do { QActive * const a = QActive_registry_[p]; - Q_ASSERT_INCRIT(570, a != (QActive *)0); // the AO must be registered + + // the AO must be registered at prio. p + Q_ASSERT_INCRIT(570, a != (QActive *)0); uint8_t const pthre = a->pthre; // set new active prio. and preemption-threshold @@ -232,7 +242,9 @@ void QK_activate_(void) { // determine the next highest-prio. AO ready to run... QF_INT_DISABLE(); // unconditionally disable interrupts - p = (uint8_t)QK_sched_act_(a, pthre_in); // schedule next AO + + // schedule next AO + p = (uint8_t)QK_sched_act_(a, pthre_in); } while (p != 0U); @@ -270,14 +282,12 @@ void QK_activate_(void) { //............................................................................ //! @static @public @memberof QF void QF_init(void) { - QF_bzero_(&QF_priv_, sizeof(QF_priv_)); - QF_bzero_(&QK_priv_, sizeof(QK_priv_)); - QF_bzero_(&QActive_registry_[0], sizeof(QActive_registry_)); - // setup the QK scheduler as initially locked and not running QK_priv_.lockCeil = (QF_MAX_ACTIVE + 1U); // scheduler locked +#ifndef Q_UNSAFE QTimeEvt_init(); // initialize QTimeEvts +#endif // Q_UNSAFE #ifdef QK_INIT QK_INIT(); // port-specific initialization of the QK kernel @@ -322,12 +332,8 @@ int_t QF_run(void) { QF_onStartup(); // app. callback: configure and enable interrupts for (;;) { // QK idle loop... - QK_onIdle(); // application-specific QK on-idle callback + QK_onIdle(); // application-specific QK idle callback } - -#ifdef __GNUC__ - return 0; -#endif } //............................................................................ @@ -346,13 +352,16 @@ void QActive_start(QActive * const me, QF_CRIT_STAT QF_CRIT_ENTRY(); - Q_REQUIRE_INCRIT(300, stkSto == (void *)0); - Q_REQUIRE_INCRIT(310, me->super.vptr != (struct QAsmVtable *)0); + // the VPTR for this AO must be valid + Q_REQUIRE_INCRIT(900, me->super.vptr != (struct QAsmVtable *)0); + + // stack storage must NOT be provided for an AO (QK does not need it) + Q_REQUIRE_INCRIT(910, stkSto == (void *)0); QF_CRIT_EXIT(); - me->prio = (uint8_t)(prioSpec & 0xFFU); // QF-prio. of the AO + me->prio = (uint8_t)(prioSpec & 0xFFU); // prio. of the AO me->pthre = (uint8_t)(prioSpec >> 8U); // preemption-threshold - QActive_register_(me); // make QF aware of this active object + QActive_register_(me); // register this AO with the framework QEQueue_init(&me->eQueue, qSto, qLen); // init the built-in queue @@ -360,7 +369,7 @@ void QActive_start(QActive * const me, (*me->super.vptr->init)(&me->super, par, me->prio); QS_FLUSH(); // flush the trace buffer to the host - // See if this AO needs to be scheduled if QK is already running + // see if this AO needs to be scheduled if QK is already running QF_CRIT_ENTRY(); if (QK_sched_() != 0U) { // activation needed? QK_activate_(); diff --git a/src/qs/qstamp.c b/src/qs/qstamp.c index f761f8411..c5fee44f8 100644 --- a/src/qs/qstamp.c +++ b/src/qs/qstamp.c @@ -7,19 +7,20 @@ // ------------------------ // Modern Embedded Software // -// SPDX-License-Identifier: LicenseRef-QL-commercial +// SPDX-License-Identifier: GPL-3.0-or-later OR LicenseRef-QL-commercial // -// This software is licensed under the terms of the Quantum Leaps commercial -// licenses. Please contact Quantum Leaps for more information about the -// available licensing options. +// This software is dual-licensed under the terms of the open-source GNU +// General Public License (GPL) or under the terms of one of the closed- +// source Quantum Leaps commercial licenses. // -// RESTRICTIONS -// You may NOT : -// (a) redistribute, encumber, sell, rent, lease, sublicense, or otherwise -// transfer rights in this software, -// (b) remove or alter any trademark, logo, copyright or other proprietary -// notices, legends, symbols or labels present in this software, -// (c) plagiarize this software to sidestep the licensing obligations. +// Redistributions in source code must retain this top-level comment block. +// Plagiarizing this software to sidestep the license obligations is illegal. +// +// NOTE: +// The GPL does NOT permit the incorporation of this code into proprietary +// programs. Please contact Quantum Leaps for commercial licensing options, +// which expressly supersede the GPL and are designed explicitly for +// closed-source distribution. // // Quantum Leaps contact information: // diff --git a/src/qv/qv.c b/src/qv/qv.c index 1f416b1a8..e1472a83f 100644 --- a/src/qv/qv.c +++ b/src/qv/qv.c @@ -49,7 +49,7 @@ QV_Attr QV_priv_; //............................................................................ //! @static @public @memberof QV -void QV_schedDisable(uint_fast8_t const ceiling) { +void QV_schedDisable(uint8_t const ceiling) { QF_CRIT_STAT QF_CRIT_ENTRY(); @@ -89,11 +89,9 @@ void QV_schedEnable(void) { //............................................................................ //! @static @public @memberof QF void QF_init(void) { - QF_bzero_(&QF_priv_, sizeof(QF_priv_)); - QF_bzero_(&QV_priv_, sizeof(QV_priv_)); - QF_bzero_(&QActive_registry_[0], sizeof(QActive_registry_)); - +#ifndef Q_UNSAFE QTimeEvt_init(); // initialize QTimeEvts +#endif // Q_UNSAFE #ifdef QV_INIT QV_INIT(); // port-specific initialization of the QV kernel @@ -206,12 +204,9 @@ int_t QF_run(void) { QF_INT_DISABLE(); // disable interrupts before looping back } } -#ifdef __GNUC__ // GNU compiler? - return 0; -#endif } -//============================================================================ +//---------------------------------------------------------------------------- //! @public @memberof QActive void QActive_start(QActive * const me, QPrioSpec const prioSpec, @@ -226,8 +221,13 @@ void QActive_start(QActive * const me, QF_CRIT_STAT QF_CRIT_ENTRY(); + + // the VPTR for this AO must be valid Q_REQUIRE_INCRIT(300, me->super.vptr != (struct QAsmVtable *)0); + + // stack storage must NOT be provided for the AO (QV does not need it) Q_REQUIRE_INCRIT(310, stkSto == (void *)0); + QF_CRIT_EXIT(); me->prio = (uint8_t)(prioSpec & 0xFFU); // QF-prio. diff --git a/tests/auto_run/log_sect_sep.txt b/tests/auto_run/log_sect_sep.txt new file mode 100644 index 000000000..26e6fe591 --- /dev/null +++ b/tests/auto_run/log_sect_sep.txt @@ -0,0 +1,2 @@ + +############################################################################## diff --git a/tests/auto_run/run_host.bat b/tests/auto_run/run_host.bat new file mode 100644 index 000000000..cedc5ccae --- /dev/null +++ b/tests/auto_run/run_host.bat @@ -0,0 +1,128 @@ +@setlocal +@set HOMEDIR=%CD% + +::@echo off + +@set TRG=host +@echo Target : %TRG% +@echo Target : %TRG% > log_%TRG%.txt +@set TESTDIR=%HOMEDIR%\.. +@set LOGDIR=%HOMEDIR% +@set MAKEFILE=Makefile +@set LOGEXT=log +@set LOGSEP=%LOGDIR%\log_sect_sep.txt + +:: unit tests ================================================================ +set TEST=TUN_QP_qutest +cd %TESTDIR%\%TEST%\test +del *.log *.cov +make -j8 -f %MAKEFILE% LOG=. OPT=c +if %ERRORLEVEL% neq 2 goto err +copy /b/y %LOGDIR%\log_%TRG%.txt + *.log %LOGDIR%\%TEST%-%TRG%.%LOGEXT% + +set TEST=TUN_QP_qep_hsm +cd %TESTDIR%\%TEST%\test +del *.log *.cov +make -j8 -f %MAKEFILE% LOG=. OPT=cx +if %ERRORLEVEL% neq 0 goto err +copy /b/y %LOGDIR%\log_%TRG%.txt + *.cov + %LOGSEP% + *.log + %LOGSEP% + *.c.gcov %LOGDIR%\%TEST%-%TRG%.%LOGEXT% + +set TEST=TUN_QP_qep_msm +cd %TESTDIR%\%TEST%\test +del *.log *.cov +make -j8 -f %MAKEFILE% LOG=. OPT=cx +if %ERRORLEVEL% neq 0 goto err +copy /b/y %LOGDIR%\log_%TRG%.txt + *.cov + %LOGSEP% + *.log + %LOGSEP% + *.c.gcov %LOGDIR%\%TEST%-%TRG%.%LOGEXT% + +set TEST=TUN_QP_qf_act +cd %TESTDIR%\%TEST%\test +del *.log *.cov +make -j8 -f %MAKEFILE% LOG=. OPT=cx +if %ERRORLEVEL% neq 0 goto err +copy /b/y %LOGDIR%\log_%TRG%.txt + *.cov + %LOGSEP% + *.log + %LOGSEP% + *.c.gcov %LOGDIR%\%TEST%-%TRG%.%LOGEXT% + +set TEST=TUN_QP_qf_actq +cd %TESTDIR%\%TEST%\test +del *.log *.cov +make -j8 -f %MAKEFILE% LOG=. OPT=cx +if %ERRORLEVEL% neq 0 goto err +copy /b/y %LOGDIR%\log_%TRG%.txt + *.cov + %LOGSEP% + *.log + %LOGSEP% + *.c.gcov %LOGDIR%\%TEST%-%TRG%.%LOGEXT% + +set TEST=TUN_QP_qf_defer +cd %TESTDIR%\%TEST%\test +del *.log *.cov +make -j8 -f %MAKEFILE% LOG=. OPT=cx +if %ERRORLEVEL% neq 0 goto err +copy /b/y %LOGDIR%\log_%TRG%.txt + *.cov + %LOGSEP% + *.log + %LOGSEP% + *.c.gcov %LOGDIR%\%TEST%-%TRG%.%LOGEXT% + +set TEST=TUN_QP_qf_dyn +cd %TESTDIR%\%TEST%\test +del *.log *.cov +make -j8 -f %MAKEFILE% LOG=. OPT=cx +if %ERRORLEVEL% neq 0 goto err +copy /b/y %LOGDIR%\log_%TRG%.txt + *.cov + %LOGSEP% + *.log + %LOGSEP% + *.c.gcov %LOGDIR%\%TEST%-%TRG%.%LOGEXT% + +set TEST=TUN_QP_qf_mem +cd %TESTDIR%\%TEST%\test +del *.log *.cov +make -j8 -f %MAKEFILE% LOG=. OPT=cx +if %ERRORLEVEL% neq 0 goto err +copy /b/y %LOGDIR%\log_%TRG%.txt + *.cov + %LOGSEP% + *.log + %LOGSEP% + *.c.gcov %LOGDIR%\%TEST%-%TRG%.%LOGEXT% + +set TEST=TUN_QP_qf_ps +cd %TESTDIR%\%TEST%\test +del *.log *.cov +make -j8 -f %MAKEFILE% LOG=. OPT=cx +if %ERRORLEVEL% neq 0 goto err +copy /b/y %LOGDIR%\log_%TRG%.txt + *.cov + %LOGSEP% + *.log + %LOGSEP% + *.c.gcov %LOGDIR%\%TEST%-%TRG%.%LOGEXT% + +set TEST=TUN_QP_qf_qact32 +cd %TESTDIR%\%TEST%\test +del *.log *.cov +make -j8 -f %MAKEFILE% LOG=. OPT=cx +if %ERRORLEVEL% neq 0 goto err +copy /b/y %LOGDIR%\log_%TRG%.txt + *.cov + %LOGSEP% + *.log + %LOGSEP% + *.c.gcov %LOGDIR%\%TEST%-%TRG%.%LOGEXT% + +set TEST=TUN_QP_qf_qact64 +cd %TESTDIR%\%TEST%\test +del *.log *.cov +make -j8 -f %MAKEFILE% LOG=. OPT=cx +if %ERRORLEVEL% neq 0 goto err +copy /b/y %LOGDIR%\log_%TRG%.txt + *.cov + %LOGSEP% + *.log + %LOGSEP% + *.c.gcov %LOGDIR%\%TEST%-%TRG%.%LOGEXT% + +set TEST=TUN_QP_qf_qeq +cd %TESTDIR%\%TEST%\test +del *.log *.cov +make -j8 -f %MAKEFILE% LOG=. OPT=cx +if %ERRORLEVEL% neq 0 goto err +copy /b/y %LOGDIR%\log_%TRG%.txt + *.cov + %LOGSEP% + *.log + %LOGSEP% + *.c.gcov %LOGDIR%\%TEST%-%TRG%.%LOGEXT% + +set TEST=TUN_QP_qf_qmact +cd %TESTDIR%\%TEST%\test +del *.log *.cov +make -j8 -f %MAKEFILE% LOG=. OPT=cx +if %ERRORLEVEL% neq 0 goto err +copy /b/y %LOGDIR%\log_%TRG%.txt + *.cov + %LOGSEP% + *.log + %LOGSEP% + *.c.gcov %LOGDIR%\%TEST%-%TRG%.%LOGEXT% + +set TEST=TUN_QP_qf_time +cd %TESTDIR%\%TEST%\test +del *.log *.cov +make -j8 -f %MAKEFILE% LOG=. OPT=cx +if %ERRORLEVEL% neq 0 goto err +copy /b/y %LOGDIR%\log_%TRG%.txt + *.cov + %LOGSEP% + *.log + %LOGSEP% + *.c.gcov %LOGDIR%\%TEST%-%TRG%.%LOGEXT% + +:cleanup +@echo Final cleanup... +cd %TESTDIR% +@for /d /r . %%d in (build_host) do @if exist "%%d" rd /s /q "%%d" +@echo OK + +@cd /d %HOMEDIR% +@del log_%TRG%.txt +exit /b + +:err +@chdir /d %HOMEDIR% +@echo %DIR% Has Test Errors... 1>&2 + +@endlocal diff --git a/tests/auto_run/run_nucleo-c031c6.bat b/tests/auto_run/run_nucleo-c031c6.bat new file mode 100644 index 000000000..95a45dc4b --- /dev/null +++ b/tests/auto_run/run_nucleo-c031c6.bat @@ -0,0 +1,171 @@ +@setlocal +@set HOMEDIR=%CD% + +::@echo off + +@set TRG=nucleo-c031c6 +@echo Target : %TRG% +@echo Target : %TRG% > log_%TRG%.txt + +@if "%1"=="" ( +@echo usage : run_%TRG%.bat USB-NUCLEO-BOARD +@echo example: run_%TRG%.bat f: +exit /b +) + +@set TESTDIR=%HOMEDIR%\.. +@set LOGDIR=%HOMEDIR% +@set MAKEFILE=%TRG%.mak +@set LOGEXT=log +@set LOGSEP=%HOMEDIR%\log_sect_sep.txt + +:: unit tests ================================================================ +set TEST=TUN_QP_qutest +cd %TESTDIR%\%TEST%\test +del *.log *.cov +make -j8 -f %MAKEFILE% LOG=. OPT=c USB=%1 flash +if %ERRORLEVEL% neq 2 goto err +copy /b/y %LOGDIR%\log_%TRG%.txt + *.log %LOGDIR%\%TEST%-%TRG%.%LOGEXT% + +set TEST=TUN_QP_qep_hsm +cd %TESTDIR%\%TEST%\test +del *.log *.cov +make -j8 -f %MAKEFILE% LOG=. OPT=cx USB=%1 flash +if %ERRORLEVEL% neq 0 goto err +copy /b/y %LOGDIR%\log_%TRG%.txt + *.log %LOGDIR%\%TEST%-%TRG%.%LOGEXT% + +set TEST=TUN_QP_qep_msm +cd %TESTDIR%\%TEST%\test +del *.log *.cov +make -j8 -f %MAKEFILE% LOG=. OPT=cx USB=%1 flash +if %ERRORLEVEL% neq 0 goto err +copy /b/y %LOGDIR%\log_%TRG%.txt + *.log %LOGDIR%\%TEST%-%TRG%.%LOGEXT% + +set TEST=TUN_QP_qf_act +cd %TESTDIR%\%TEST%\test +del *.log *.cov +make -j8 -f %MAKEFILE% LOG=. OPT=cx USB=%1 flash +if %ERRORLEVEL% neq 0 goto err +copy /b/y %LOGDIR%\log_%TRG%.txt + *.log %LOGDIR%\%TEST%-%TRG%.%LOGEXT% + +set TEST=TUN_QP_qf_actq +cd %TESTDIR%\%TEST%\test +del *.log *.cov +make -j8 -f %MAKEFILE% LOG=. OPT=cx USB=%1 flash +if %ERRORLEVEL% neq 0 goto err +copy /b/y %LOGDIR%\log_%TRG%.txt + *.log %LOGDIR%\%TEST%-%TRG%.%LOGEXT% + +set TEST=TUN_QP_qf_defer +cd %TESTDIR%\%TEST%\test +del *.log *.cov +make -j8 -f %MAKEFILE% LOG=. OPT=cx USB=%1 flash +if %ERRORLEVEL% neq 0 goto err +copy /b/y %LOGDIR%\log_%TRG%.txt + *.log %LOGDIR%\%TEST%-%TRG%.%LOGEXT% + +set TEST=TUN_QP_qf_dyn +cd %TESTDIR%\%TEST%\test +del *.log *.cov +make -j8 -f %MAKEFILE% LOG=. OPT=cx USB=%1 flash +if %ERRORLEVEL% neq 0 goto err +copy /b/y %LOGDIR%\log_%TRG%.txt + *.log %LOGDIR%\%TEST%-%TRG%.%LOGEXT% + +set TEST=TUN_QP_qf_mem +cd %TESTDIR%\%TEST%\test +del *.log *.cov +make -j8 -f %MAKEFILE% LOG=. OPT=cx USB=%1 flash +if %ERRORLEVEL% neq 0 goto err +copy /b/y %LOGDIR%\log_%TRG%.txt + *.log %LOGDIR%\%TEST%-%TRG%.%LOGEXT% + +set TEST=TUN_QP_qf_ps +cd %TESTDIR%\%TEST%\test +del *.log *.cov +make -j8 -f %MAKEFILE% LOG=. OPT=cx USB=%1 flash +if %ERRORLEVEL% neq 0 goto err +copy /b/y %LOGDIR%\log_%TRG%.txt + *.log %LOGDIR%\%TEST%-%TRG%.%LOGEXT% + +set TEST=TUN_QP_qf_qact32 +cd %TESTDIR%\%TEST%\test +del *.log *.cov +make -j8 -f %MAKEFILE% LOG=. OPT=cx USB=%1 flash +if %ERRORLEVEL% neq 0 goto err +copy /b/y %LOGDIR%\log_%TRG%.txt + *.log %LOGDIR%\%TEST%-%TRG%.%LOGEXT% + +set TEST=TUN_QP_qf_qact64 +cd %TESTDIR%\%TEST%\test +del *.log *.cov +make -j8 -f %MAKEFILE% LOG=. OPT=cx USB=%1 flash +if %ERRORLEVEL% neq 0 goto err +copy /b/y %LOGDIR%\log_%TRG%.txt + *.log %LOGDIR%\%TEST%-%TRG%.%LOGEXT% + +set TEST=TUN_QP_qf_qeq +cd %TESTDIR%\%TEST%\test +del *.log *.cov +make -j8 -f %MAKEFILE% LOG=. OPT=cx USB=%1 flash +if %ERRORLEVEL% neq 0 goto err +copy /b/y %LOGDIR%\log_%TRG%.txt + *.log %LOGDIR%\%TEST%-%TRG%.%LOGEXT% + +set TEST=TUN_QP_qf_qmact +cd %TESTDIR%\%TEST%\test +del *.log *.cov +make -j8 -f %MAKEFILE% LOG=. OPT=cx USB=%1 flash +if %ERRORLEVEL% neq 0 goto err +copy /b/y %LOGDIR%\log_%TRG%.txt + *.log %LOGDIR%\%TEST%-%TRG%.%LOGEXT% + +set TEST=TUN_QP_qf_time +cd %TESTDIR%\%TEST%\test +del *.log *.cov +make -j8 -f %MAKEFILE% LOG=. OPT=cx USB=%1 flash +if %ERRORLEVEL% neq 0 goto err +copy /b/y %LOGDIR%\log_%TRG%.txt + *.log %LOGDIR%\%TEST%-%TRG%.%LOGEXT% + +:: integration tests ========================================================= +set TEST=TIN_QP_mem +cd %TESTDIR%\%TEST%\test_mpu +del *.log *.cov +make -j8 -f %MAKEFILE% LOG=. OPT=cx USB=%1 flash +if %ERRORLEVEL% neq 0 goto err +copy /b/y %LOGDIR%\log_%TRG%.txt + *.log %LOGDIR%\%TEST%-%TRG%.%LOGEXT% + +set TEST=TIN_QP_qk +cd %TESTDIR%\%TEST%\test_sched +del *.log *.cov +make -j8 -f %MAKEFILE% LOG=. OPT=cx USB=%1 flash +if %ERRORLEVEL% neq 0 goto err +copy /b/y *.log %LOGDIR%\%TEST%_sched-%TRG%.%LOGEXT% + +set TEST=TIN_QP_qv +cd %TESTDIR%\%TEST%\test_sched +del *.log *.cov +make -j8 -f %MAKEFILE% LOG=. OPT=cx USB=%1 flash +if %ERRORLEVEL% neq 0 goto err +copy /b/y *.log %LOGDIR%\%TEST%_sched-%TRG%.%LOGEXT% + +set TEST=TIN_QP_qxk +cd %TESTDIR%\%TEST%\test_sched +del *.log *.cov +make -j8 -f %MAKEFILE% LOG=. OPT=cx USB=%1 flash +if %ERRORLEVEL% neq 0 goto err +copy /b/y *.log %LOGDIR%\%TEST%_sched-%TRG%.%LOGEXT% + +set TEST=TIN_QP_qxk +cd %TESTDIR%\%TEST%\test_mutex +del *.log *.cov +make -j8 -f %MAKEFILE% LOG=. OPT=cx USB=%1 flash +if %ERRORLEVEL% neq 0 goto err +copy /b/y *.log %LOGDIR%\%TEST%_mutex-%TRG%.%LOGEXT% + +:cleanup +@echo Final cleanup... +cd %TESTDIR% +@for /d /r . %%d in (build_%TRG%) do @if exist "%%d" rd /s /q "%%d" +@echo OK + +@cd /d %HOMEDIR% +@del log_%TRG%.txt +exit /b + +:err +@chdir /d %HOMEDIR% +@echo %DIR% Has Test Errors... 1>&2 + +@endlocal diff --git a/tests/auto_run/run_nucleo-u545re.bat b/tests/auto_run/run_nucleo-u545re.bat new file mode 100644 index 000000000..0e689d0bb --- /dev/null +++ b/tests/auto_run/run_nucleo-u545re.bat @@ -0,0 +1,171 @@ +@setlocal +@set HOMEDIR=%CD% + +::@echo off + +@set TRG=nucleo-u545re +@echo Target : %TRG% +@echo Target : %TRG% > log_%TRG%.txt + +@if "%1"=="" ( +@echo usage : run_%TRG%.bat USB-NUCLEO-BOARD +@echo example: run_%TRG%.bat f: +exit /b +) + +@set TESTDIR=%HOMEDIR%\.. +@set LOGDIR=%HOMEDIR% +@set MAKEFILE=%TRG%.mak +@set LOGEXT=log +@set LOGSEP=%LOGDIR%\log_sect_sep.txt + +:: unit tests ================================================================ +set TEST=TUN_QP_qutest +cd %TESTDIR%\%TEST%\test +del *.log *.cov +make -j8 -f %MAKEFILE% LOG=. OPT=c USB=%1 flash +if %ERRORLEVEL% neq 2 goto err +copy /b/y %LOGDIR%\log_%TRG%.txt + *.log %LOGDIR%\%TEST%-%TRG%.%LOGEXT% + +set TEST=TUN_QP_qep_hsm +cd %TESTDIR%\%TEST%\test +del *.log *.cov +make -j8 -f %MAKEFILE% LOG=. OPT=cx USB=%1 flash +if %ERRORLEVEL% neq 0 goto err +copy /b/y %LOGDIR%\log_%TRG%.txt + *.log %LOGDIR%\%TEST%-%TRG%.%LOGEXT% + +set TEST=TUN_QP_qep_msm +cd %TESTDIR%\%TEST%\test +del *.log *.cov +make -j8 -f %MAKEFILE% LOG=. OPT=cx USB=%1 flash +if %ERRORLEVEL% neq 0 goto err +copy /b/y %LOGDIR%\log_%TRG%.txt + *.log %LOGDIR%\%TEST%-%TRG%.%LOGEXT% + +set TEST=TUN_QP_qf_act +cd %TESTDIR%\%TEST%\test +del *.log *.cov +make -j8 -f %MAKEFILE% LOG=. OPT=cx USB=%1 flash +if %ERRORLEVEL% neq 0 goto err +copy /b/y %LOGDIR%\log_%TRG%.txt + *.log %LOGDIR%\%TEST%-%TRG%.%LOGEXT% + +set TEST=TUN_QP_qf_actq +cd %TESTDIR%\%TEST%\test +del *.log *.cov +make -j8 -f %MAKEFILE% LOG=. OPT=cx USB=%1 flash +if %ERRORLEVEL% neq 0 goto err +copy /b/y %LOGDIR%\log_%TRG%.txt + *.log %LOGDIR%\%TEST%-%TRG%.%LOGEXT% + +set TEST=TUN_QP_qf_defer +cd %TESTDIR%\%TEST%\test +del *.log *.cov +make -j8 -f %MAKEFILE% LOG=. OPT=cx USB=%1 flash +if %ERRORLEVEL% neq 0 goto err +copy /b/y %LOGDIR%\log_%TRG%.txt + *.log %LOGDIR%\%TEST%-%TRG%.%LOGEXT% + +set TEST=TUN_QP_qf_dyn +cd %TESTDIR%\%TEST%\test +del *.log *.cov +make -j8 -f %MAKEFILE% LOG=. OPT=cx USB=%1 flash +if %ERRORLEVEL% neq 0 goto err +copy /b/y %LOGDIR%\log_%TRG%.txt + *.log %LOGDIR%\%TEST%-%TRG%.%LOGEXT% + +set TEST=TUN_QP_qf_mem +cd %TESTDIR%\%TEST%\test +del *.log *.cov +make -j8 -f %MAKEFILE% LOG=. OPT=cx USB=%1 flash +if %ERRORLEVEL% neq 0 goto err +copy /b/y %LOGDIR%\log_%TRG%.txt + *.log %LOGDIR%\%TEST%-%TRG%.%LOGEXT% + +set TEST=TUN_QP_qf_ps +cd %TESTDIR%\%TEST%\test +del *.log *.cov +make -j8 -f %MAKEFILE% LOG=. OPT=cx USB=%1 flash +if %ERRORLEVEL% neq 0 goto err +copy /b/y %LOGDIR%\log_%TRG%.txt + *.log %LOGDIR%\%TEST%-%TRG%.%LOGEXT% + +set TEST=TUN_QP_qf_qact32 +cd %TESTDIR%\%TEST%\test +del *.log *.cov +make -j8 -f %MAKEFILE% LOG=. OPT=cx USB=%1 flash +if %ERRORLEVEL% neq 0 goto err +copy /b/y %LOGDIR%\log_%TRG%.txt + *.log %LOGDIR%\%TEST%-%TRG%.%LOGEXT% + +set TEST=TUN_QP_qf_qact64 +cd %TESTDIR%\%TEST%\test +del *.log *.cov +make -j8 -f %MAKEFILE% LOG=. OPT=cx USB=%1 flash +if %ERRORLEVEL% neq 0 goto err +copy /b/y %LOGDIR%\log_%TRG%.txt + *.log %LOGDIR%\%TEST%-%TRG%.%LOGEXT% + +set TEST=TUN_QP_qf_qeq +cd %TESTDIR%\%TEST%\test +del *.log *.cov +make -j8 -f %MAKEFILE% LOG=. OPT=cx USB=%1 flash +if %ERRORLEVEL% neq 0 goto err +copy /b/y %LOGDIR%\log_%TRG%.txt + *.log %LOGDIR%\%TEST%-%TRG%.%LOGEXT% + +set TEST=TUN_QP_qf_qmact +cd %TESTDIR%\%TEST%\test +del *.log *.cov +make -j8 -f %MAKEFILE% LOG=. OPT=cx USB=%1 flash +if %ERRORLEVEL% neq 0 goto err +copy /b/y %LOGDIR%\log_%TRG%.txt + *.log %LOGDIR%\%TEST%-%TRG%.%LOGEXT% + +set TEST=TUN_QP_qf_time +cd %TESTDIR%\%TEST%\test +del *.log *.cov +make -j8 -f %MAKEFILE% LOG=. OPT=cx USB=%1 flash +if %ERRORLEVEL% neq 0 goto err +copy /b/y %LOGDIR%\log_%TRG%.txt + *.log %LOGDIR%\%TEST%-%TRG%.%LOGEXT% + +:: integration tests ========================================================= +set TEST=TIN_QP_mem +cd %TESTDIR%\%TEST%\test_mpu +del *.log *.cov +make -j8 -f %MAKEFILE% LOG=. OPT=cx USB=%1 flash +if %ERRORLEVEL% neq 0 goto err +copy /b/y %LOGDIR%\log_%TRG%.txt + *.log %LOGDIR%\%TEST%-%TRG%.%LOGEXT% + +set TEST=TIN_QP_qk +cd %TESTDIR%\%TEST%\test_sched +del *.log *.cov +make -j8 -f %MAKEFILE% LOG=. OPT=cx USB=%1 flash +if %ERRORLEVEL% neq 0 goto err +copy /b/y *.log %LOGDIR%\%TEST%_sched-%TRG%.%LOGEXT% + +set TEST=TIN_QP_qv +cd %TESTDIR%\%TEST%\test_sched +del *.log *.cov +make -j8 -f %MAKEFILE% LOG=. OPT=cx USB=%1 flash +if %ERRORLEVEL% neq 0 goto err +copy /b/y *.log %LOGDIR%\%TEST%_sched-%TRG%.%LOGEXT% + +set TEST=TIN_QP_qxk +cd %TESTDIR%\%TEST%\test_sched +del *.log *.cov +make -j8 -f %MAKEFILE% LOG=. OPT=cx USB=%1 flash +if %ERRORLEVEL% neq 0 goto err +copy /b/y *.log %LOGDIR%\%TEST%_sched-%TRG%.%LOGEXT% + +set TEST=TIN_QP_qxk +cd %TESTDIR%\%TEST%\test_mutex +del *.log *.cov +make -j8 -f %MAKEFILE% LOG=. OPT=cx USB=%1 flash +if %ERRORLEVEL% neq 0 goto err +copy /b/y *.log %LOGDIR%\%TEST%_mutex-%TRG%.%LOGEXT% + +:cleanup +@echo Final cleanup... +cd %TESTDIR% +@for /d /r . %%d in (build_%TRG%) do @if exist "%%d" rd /s /q "%%d" +@echo OK + +@cd /d %HOMEDIR% +@del log_%TRG%.txt +exit /b + +:err +@chdir /d %HOMEDIR% +@echo %DIR% Has Test Errors... 1>&2 + +@endlocal diff --git a/tests/safe-qp_check.pyi b/tests/safe-qp_check.pyi new file mode 100644 index 000000000..0d6097f03 --- /dev/null +++ b/tests/safe-qp_check.pyi @@ -0,0 +1,6 @@ +QP_SAFE_EDITION = False # this is regular QP Framework +note(f"SafeQP: {QP_SAFE_EDITION}") + +def test_safe_qp(title, opt = 0): + skip(1) + test(f"SafeQP-only test: {title}", opt) diff --git a/zephyr/qf_port.c b/zephyr/qf_port.c index dcb7c888a..0b4ec6182 100644 --- a/zephyr/qf_port.c +++ b/zephyr/qf_port.c @@ -45,9 +45,6 @@ struct k_spinlock QF_spinlock; //............................................................................ void QF_init(void) { QF_spinlock = (struct k_spinlock){}; - - QF_bzero_(&QF_priv_, sizeof(QF_priv_)); - QF_bzero_(&QActive_registry_[0], sizeof(QActive_registry_)); QTimeEvt_init(); // initialize QTimeEvts } //............................................................................ @@ -227,7 +224,6 @@ bool QActive_post_(QActive * const me, QEvt const * const e, QS_END_PRE() if (e->poolNum_ != 0U) { // is it a pool event? - Q_ASSERT_INCRIT(205, e->refCtr_ < (2U * QF_MAX_ACTIVE)); QEvt_refCtr_inc_(e); // increment the reference counter } @@ -274,7 +270,6 @@ void QActive_postLIFO_(QActive * const me, QEvt const * const e) { QS_END_PRE() if (e->poolNum_ != 0U) { // is it a pool event? - Q_ASSERT_INCRIT(305, e->refCtr_ < (2U * QF_MAX_ACTIVE)); QEvt_refCtr_inc_(e); // increment the reference counter } @@ -317,3 +312,21 @@ QEvt const *QActive_get_(QActive * const me) { return e; } +//............................................................................ +//! @static @public @memberof QActive +uint16_t QActive_getQueueUse(uint_fast8_t const prio) { + Q_UNUSED_PAR(prio); + return 0U; // current use level in a queue not supported in this RTOS +} +//............................................................................ +//! @static @public @memberof QActive +uint16_t QActive_getQueueFree(uint_fast8_t const prio) { + Q_UNUSED_PAR(prio); + return 0U; // current use level in a queue not supported in this RTOS +} +//............................................................................ +//! @static @public @memberof QActive +uint16_t QActive_getQueueMin(uint_fast8_t const prio) { + Q_UNUSED_PAR(prio); + return 0U; // minimum free entries in a queue not supported in this RTOS +} diff --git a/zephyr/qp_port.h b/zephyr/qp_port.h index 747776cba..6b2d44bc7 100644 --- a/zephyr/qp_port.h +++ b/zephyr/qp_port.h @@ -37,6 +37,9 @@ // no-return function specifier (C11 Standard) #define Q_NORETURN _Noreturn void +// static assertion (C11 Standard) +#define Q_ASSERT_STATIC(expr_) _Static_assert((expr_), "QP static assert") + // event queue and thread types #define QACTIVE_EQUEUE_TYPE struct k_msgq #define QACTIVE_THREAD_TYPE struct k_thread @@ -56,9 +59,9 @@ #endif // include files ------------------------------------------------------------- -#include "qequeue.h" // used for event deferral -#include "qmpool.h" // this QP port uses the native QF memory pool -#include "qp.h" // QP platform-independent public interface +#include "qequeue.h" // used for event deferral +#include "qmpool.h" // this QP port uses the native QF memory pool +#include "qp.h" // QP platform-independent public interface // Zephyr spinlock for QF critical section extern struct k_spinlock QF_spinlock; @@ -67,29 +70,31 @@ extern struct k_spinlock QF_spinlock; // interface used only inside QF implementation, but not in applications #ifdef QP_IMPL - // scheduler locking, see NOTE2 - #define QF_SCHED_STAT_ - #define QF_SCHED_LOCK_(dummy) do { \ - if (!k_is_in_isr()) { \ - k_sched_lock(); \ - } \ - } while (false) - - #define QF_SCHED_UNLOCK_() do { \ - if (!k_is_in_isr()) { \ - k_sched_unlock(); \ - } \ - } while (false) - - // native QF event pool customization - #define QF_EPOOL_TYPE_ QMPool - #define QF_EPOOL_INIT_(p_, poolSto_, poolSize_, evtSize_) \ - (QMPool_init(&(p_), (poolSto_), (poolSize_), (evtSize_))) - #define QF_EPOOL_EVENT_SIZE_(p_) ((uint_fast16_t)(p_).blockSize) - #define QF_EPOOL_GET_(p_, e_, m_, qsId_) \ - ((e_) = (QEvt *)QMPool_get(&(p_), (m_), (qsId_))) - #define QF_EPOOL_PUT_(p_, e_, qsId_) \ - (QMPool_put(&(p_), (e_), (qsId_))) +// scheduler locking, see NOTE2 +#define QF_SCHED_STAT_ +#define QF_SCHED_LOCK_(dummy) do { \ + if (!k_is_in_isr()) { \ + k_sched_lock(); \ + } \ +} while (false) + +#define QF_SCHED_UNLOCK_() do { \ + if (!k_is_in_isr()) { \ + k_sched_unlock(); \ + } \ +} while (false) + +// QMPool operations +#define QF_EPOOL_TYPE_ QMPool +#define QF_EPOOL_INIT_(p_, poolSto_, poolSize_, evtSize_) \ + (QMPool_init(&(p_), (poolSto_), (poolSize_), (evtSize_))) +#define QF_EPOOL_EVENT_SIZE_(p_) ((uint16_t)(p_).blockSize) +#define QF_EPOOL_GET_(p_, e_, m_, qsId_) \ + ((e_) = (QEvt *)QMPool_get(&(p_), (m_), (qsId_))) +#define QF_EPOOL_PUT_(p_, e_, qsId_) (QMPool_put(&(p_), (e_), (qsId_))) +#define QF_EPOOL_USE_(ePool_) (QMPool_getUse(ePool_)) +#define QF_EPOOL_FREE_(ePool_) ((uint16_t)(ePool_)->nFree) +#define QF_EPOOL_MIN_(ePool_) ((uint16_t)(ePool_)->nMin) #endif // QP_IMPL