This commit is contained in:
Quantum Leaps 2017-10-13 17:14:47 -04:00
parent 2d8b482434
commit ce37754fbc
64 changed files with 3471 additions and 20744 deletions

View File

@ -1,121 +0,0 @@
/* ----------------------------------------------------------------------
* Project: CMSIS DSP Library
* Title: arm_common_tables.h
* Description: Extern declaration for common tables
*
* $Date: 27. January 2017
* $Revision: V.1.5.1
*
* Target Processor: Cortex-M cores
* -------------------------------------------------------------------- */
/*
* Copyright (C) 2010-2017 ARM Limited or its affiliates. All rights reserved.
*
* SPDX-License-Identifier: Apache-2.0
*
* Licensed under the Apache License, Version 2.0 (the License); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an AS IS BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef _ARM_COMMON_TABLES_H
#define _ARM_COMMON_TABLES_H
#include "arm_math.h"
extern const uint16_t armBitRevTable[1024];
extern const q15_t armRecipTableQ15[64];
extern const q31_t armRecipTableQ31[64];
extern const float32_t twiddleCoef_16[32];
extern const float32_t twiddleCoef_32[64];
extern const float32_t twiddleCoef_64[128];
extern const float32_t twiddleCoef_128[256];
extern const float32_t twiddleCoef_256[512];
extern const float32_t twiddleCoef_512[1024];
extern const float32_t twiddleCoef_1024[2048];
extern const float32_t twiddleCoef_2048[4096];
extern const float32_t twiddleCoef_4096[8192];
#define twiddleCoef twiddleCoef_4096
extern const q31_t twiddleCoef_16_q31[24];
extern const q31_t twiddleCoef_32_q31[48];
extern const q31_t twiddleCoef_64_q31[96];
extern const q31_t twiddleCoef_128_q31[192];
extern const q31_t twiddleCoef_256_q31[384];
extern const q31_t twiddleCoef_512_q31[768];
extern const q31_t twiddleCoef_1024_q31[1536];
extern const q31_t twiddleCoef_2048_q31[3072];
extern const q31_t twiddleCoef_4096_q31[6144];
extern const q15_t twiddleCoef_16_q15[24];
extern const q15_t twiddleCoef_32_q15[48];
extern const q15_t twiddleCoef_64_q15[96];
extern const q15_t twiddleCoef_128_q15[192];
extern const q15_t twiddleCoef_256_q15[384];
extern const q15_t twiddleCoef_512_q15[768];
extern const q15_t twiddleCoef_1024_q15[1536];
extern const q15_t twiddleCoef_2048_q15[3072];
extern const q15_t twiddleCoef_4096_q15[6144];
extern const float32_t twiddleCoef_rfft_32[32];
extern const float32_t twiddleCoef_rfft_64[64];
extern const float32_t twiddleCoef_rfft_128[128];
extern const float32_t twiddleCoef_rfft_256[256];
extern const float32_t twiddleCoef_rfft_512[512];
extern const float32_t twiddleCoef_rfft_1024[1024];
extern const float32_t twiddleCoef_rfft_2048[2048];
extern const float32_t twiddleCoef_rfft_4096[4096];
/* floating-point bit reversal tables */
#define ARMBITREVINDEXTABLE_16_TABLE_LENGTH ((uint16_t)20)
#define ARMBITREVINDEXTABLE_32_TABLE_LENGTH ((uint16_t)48)
#define ARMBITREVINDEXTABLE_64_TABLE_LENGTH ((uint16_t)56)
#define ARMBITREVINDEXTABLE_128_TABLE_LENGTH ((uint16_t)208)
#define ARMBITREVINDEXTABLE_256_TABLE_LENGTH ((uint16_t)440)
#define ARMBITREVINDEXTABLE_512_TABLE_LENGTH ((uint16_t)448)
#define ARMBITREVINDEXTABLE_1024_TABLE_LENGTH ((uint16_t)1800)
#define ARMBITREVINDEXTABLE_2048_TABLE_LENGTH ((uint16_t)3808)
#define ARMBITREVINDEXTABLE_4096_TABLE_LENGTH ((uint16_t)4032)
extern const uint16_t armBitRevIndexTable16[ARMBITREVINDEXTABLE_16_TABLE_LENGTH];
extern const uint16_t armBitRevIndexTable32[ARMBITREVINDEXTABLE_32_TABLE_LENGTH];
extern const uint16_t armBitRevIndexTable64[ARMBITREVINDEXTABLE_64_TABLE_LENGTH];
extern const uint16_t armBitRevIndexTable128[ARMBITREVINDEXTABLE_128_TABLE_LENGTH];
extern const uint16_t armBitRevIndexTable256[ARMBITREVINDEXTABLE_256_TABLE_LENGTH];
extern const uint16_t armBitRevIndexTable512[ARMBITREVINDEXTABLE_512_TABLE_LENGTH];
extern const uint16_t armBitRevIndexTable1024[ARMBITREVINDEXTABLE_1024_TABLE_LENGTH];
extern const uint16_t armBitRevIndexTable2048[ARMBITREVINDEXTABLE_2048_TABLE_LENGTH];
extern const uint16_t armBitRevIndexTable4096[ARMBITREVINDEXTABLE_4096_TABLE_LENGTH];
/* fixed-point bit reversal tables */
#define ARMBITREVINDEXTABLE_FIXED_16_TABLE_LENGTH ((uint16_t)12)
#define ARMBITREVINDEXTABLE_FIXED_32_TABLE_LENGTH ((uint16_t)24)
#define ARMBITREVINDEXTABLE_FIXED_64_TABLE_LENGTH ((uint16_t)56)
#define ARMBITREVINDEXTABLE_FIXED_128_TABLE_LENGTH ((uint16_t)112)
#define ARMBITREVINDEXTABLE_FIXED_256_TABLE_LENGTH ((uint16_t)240)
#define ARMBITREVINDEXTABLE_FIXED_512_TABLE_LENGTH ((uint16_t)480)
#define ARMBITREVINDEXTABLE_FIXED_1024_TABLE_LENGTH ((uint16_t)992)
#define ARMBITREVINDEXTABLE_FIXED_2048_TABLE_LENGTH ((uint16_t)1984)
#define ARMBITREVINDEXTABLE_FIXED_4096_TABLE_LENGTH ((uint16_t)4032)
extern const uint16_t armBitRevIndexTable_fixed_16[ARMBITREVINDEXTABLE_FIXED_16_TABLE_LENGTH];
extern const uint16_t armBitRevIndexTable_fixed_32[ARMBITREVINDEXTABLE_FIXED_32_TABLE_LENGTH];
extern const uint16_t armBitRevIndexTable_fixed_64[ARMBITREVINDEXTABLE_FIXED_64_TABLE_LENGTH];
extern const uint16_t armBitRevIndexTable_fixed_128[ARMBITREVINDEXTABLE_FIXED_128_TABLE_LENGTH];
extern const uint16_t armBitRevIndexTable_fixed_256[ARMBITREVINDEXTABLE_FIXED_256_TABLE_LENGTH];
extern const uint16_t armBitRevIndexTable_fixed_512[ARMBITREVINDEXTABLE_FIXED_512_TABLE_LENGTH];
extern const uint16_t armBitRevIndexTable_fixed_1024[ARMBITREVINDEXTABLE_FIXED_1024_TABLE_LENGTH];
extern const uint16_t armBitRevIndexTable_fixed_2048[ARMBITREVINDEXTABLE_FIXED_2048_TABLE_LENGTH];
extern const uint16_t armBitRevIndexTable_fixed_4096[ARMBITREVINDEXTABLE_FIXED_4096_TABLE_LENGTH];
/* Tables for Fast Math Sine and Cosine */
extern const float32_t sinTable_f32[FAST_MATH_TABLE_SIZE + 1];
extern const q31_t sinTable_q31[FAST_MATH_TABLE_SIZE + 1];
extern const q15_t sinTable_q15[FAST_MATH_TABLE_SIZE + 1];
#endif /* ARM_COMMON_TABLES_H */

View File

@ -1,66 +0,0 @@
/* ----------------------------------------------------------------------
* Project: CMSIS DSP Library
* Title: arm_const_structs.h
* Description: Constant structs that are initialized for user convenience.
* For example, some can be given as arguments to the arm_cfft_f32() function.
*
* $Date: 27. January 2017
* $Revision: V.1.5.1
*
* Target Processor: Cortex-M cores
* -------------------------------------------------------------------- */
/*
* Copyright (C) 2010-2017 ARM Limited or its affiliates. All rights reserved.
*
* SPDX-License-Identifier: Apache-2.0
*
* Licensed under the Apache License, Version 2.0 (the License); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an AS IS BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef _ARM_CONST_STRUCTS_H
#define _ARM_CONST_STRUCTS_H
#include "arm_math.h"
#include "arm_common_tables.h"
extern const arm_cfft_instance_f32 arm_cfft_sR_f32_len16;
extern const arm_cfft_instance_f32 arm_cfft_sR_f32_len32;
extern const arm_cfft_instance_f32 arm_cfft_sR_f32_len64;
extern const arm_cfft_instance_f32 arm_cfft_sR_f32_len128;
extern const arm_cfft_instance_f32 arm_cfft_sR_f32_len256;
extern const arm_cfft_instance_f32 arm_cfft_sR_f32_len512;
extern const arm_cfft_instance_f32 arm_cfft_sR_f32_len1024;
extern const arm_cfft_instance_f32 arm_cfft_sR_f32_len2048;
extern const arm_cfft_instance_f32 arm_cfft_sR_f32_len4096;
extern const arm_cfft_instance_q31 arm_cfft_sR_q31_len16;
extern const arm_cfft_instance_q31 arm_cfft_sR_q31_len32;
extern const arm_cfft_instance_q31 arm_cfft_sR_q31_len64;
extern const arm_cfft_instance_q31 arm_cfft_sR_q31_len128;
extern const arm_cfft_instance_q31 arm_cfft_sR_q31_len256;
extern const arm_cfft_instance_q31 arm_cfft_sR_q31_len512;
extern const arm_cfft_instance_q31 arm_cfft_sR_q31_len1024;
extern const arm_cfft_instance_q31 arm_cfft_sR_q31_len2048;
extern const arm_cfft_instance_q31 arm_cfft_sR_q31_len4096;
extern const arm_cfft_instance_q15 arm_cfft_sR_q15_len16;
extern const arm_cfft_instance_q15 arm_cfft_sR_q15_len32;
extern const arm_cfft_instance_q15 arm_cfft_sR_q15_len64;
extern const arm_cfft_instance_q15 arm_cfft_sR_q15_len128;
extern const arm_cfft_instance_q15 arm_cfft_sR_q15_len256;
extern const arm_cfft_instance_q15 arm_cfft_sR_q15_len512;
extern const arm_cfft_instance_q15 arm_cfft_sR_q15_len1024;
extern const arm_cfft_instance_q15 arm_cfft_sR_q15_len2048;
extern const arm_cfft_instance_q15 arm_cfft_sR_q15_len4096;
#endif

File diff suppressed because it is too large Load Diff

View File

@ -1,7 +1,7 @@
About CMSIS
===========
This folder contains the Cortex Microcontroller Software Interface Standard
(CMSIS) V5.1.0, which provides a single standard across all Cortex-Mx
(CMSIS) V5.1.1, which provides a single standard across all Cortex-M
processor series vendors. It enables code re-use and code sharing across
software projects and reduces time-to-market for new embedded applications.

View File

@ -5,7 +5,7 @@
#---------------------------------------------------------------------------
DOXYFILE_ENCODING = UTF-8
PROJECT_NAME = "QP/C++"
PROJECT_NUMBER = "5.9.9"
PROJECT_NUMBER = "6.0.0"
PROJECT_BRIEF =
PROJECT_LOGO = images/header_logo_ql.png
OUTPUT_DIRECTORY =
@ -151,7 +151,7 @@ EXCLUDE = \
EXCLUDE_SYMLINKS = NO
EXCLUDE_PATTERNS =
EXCLUDE_SYMBOLS =
EXCLUDE_SYMBOLS = QP_IMPL
EXAMPLE_PATH = \
snippets \

View File

@ -5,7 +5,7 @@
#---------------------------------------------------------------------------
DOXYFILE_ENCODING = UTF-8
PROJECT_NAME = "QP/C++"
PROJECT_NUMBER = "5.9.9"
PROJECT_NUMBER = "6.0.0"
PROJECT_BRIEF =
PROJECT_LOGO = images/header_logo_ql.png
OUTPUT_DIRECTORY =
@ -151,7 +151,7 @@ EXCLUDE = \
EXCLUDE_SYMLINKS = NO
EXCLUDE_PATTERNS =
EXCLUDE_SYMBOLS =
EXCLUDE_SYMBOLS = QP_IMPL
EXAMPLE_PATH = \
snippets \

View File

@ -2,6 +2,18 @@ namespace QP {
/** @page history Revision History
@section qpcpp_6_0_0 Version 6.0.0, 2017-10-13
This release fixes two bugs found in the QXK kernel:
- [bug#185](https://sourceforge.net/p/qpc/bugs/185/) "QXK: PendSV_Handler uses inconsistent stack frames for saving and restoring AO for Cortex-M0(+)"
- [bug#186](https://sourceforge.net/p/qpc/bugs/186/) "QXK: Extended thread context switch causes assertion in PendSV_Handler"
Additionally, this release includes a workaround for a [bug indetified in the GNU-ARM toolset](https://bugs.launchpad.net/gcc-arm-embedded/+bug/1722849). This problem affected the ARMv6-M architecture (Cortex-M0/M0+/M1) and manifested itself in generation of incorrect code for the QP critical section at certain gcc optimization levels (such as -O). This bug was first discovered and filed as [bug#184](https://sourceforge.net/p/qpc/bugs/184/). The bug affected the GNU-ARM ports to all built-in kernels @ref qv "QV", @ref qk "QK", and @ref qxk "QXK".
@note
This release no longer contains the directory `qpcpp/source`, which was scheduled to be phased out in QP5. In QP6 the source code is found only in the `qpcpp/src` directory.
-----------------------------------------------------------------------------
@section qpcpp_5_9_9 Version 5.9.9, 2017-09-29
This release implements the feature request [#132 "Extend the QXK mutex to support also simple operation without the priority-ceiling protocol"](https://sourceforge.net/p/qpc/feature-requests/132/).
@ -139,7 +151,7 @@ Finally, this release implements the feature request #125 "Include QPC
Demo application for STM32F4 processor without RTOS" (see
https://sourceforge.net/p/qpc/feature-requests/125/ ). The DPP demo for
the STM32F4-Discovery board has been added in the directory:
qpc\examples\arm-cm\dpp_stm32f4-discovery . This demo includes QV, QK and
qpc/examples/arm-cm/dpp_stm32f4-discovery . This demo includes QV, QK and
QXK kernels and ARM-Keil, GNU-ARM, and IAR-ARM toolsets. The demos support
bi-directional QP/Spy.
@ -160,15 +172,15 @@ Specifically, the QP/C++ source code is now provided in the
`qpc/src/` directory with the following structure:
@verbatim
qpcpp\
+-source\ - exitsting source directory with "flat" structure
qpcpp/
+-source/ - exitsting source directory with "flat" structure
| (for backwards-compatibility)
+-src\ - new source directory grouped by functionality
+-qf\ - core framework (QEP + QF)
+-qk\ - QK kernel
+-qv\ - QV kernel (only one file qv.c)
+-qxk\ - QXK kernel
+-qs\ - QS software tracing
+-src/ - new source directory grouped by functionality
+-qf/ - core framework (QEP + QF)
+-qk/ - QK kernel
+-qv/ - QV kernel (only one file qv.c)
+-qxk/ - QXK kernel
+-qs/ - QS software tracing
+-qf_pkg.h
+-qs_pkg.h
+-qxk_pkg.h
@ -243,7 +255,7 @@ Finally, this release fixes the following bugs:
- bug#159 QP/C/C++ Win32 ports don't work on all x86 CPUs
- bug#157 In QPC ucosii port, conversion of AO's priority to OS task
priority is incorrect.
- bug#152 Typo (qpcpp\ports\arm7-9\qk\gnu\qk_port.s:42) prevents
- bug#152 Typo (qpcpp/ports/arm7-9/qk/gnu/qk_port.s:42) prevents
compilation
- bug#151 QTicker compile error when #Q_EVT_CTOR is defined
- bug#154 qspy.c parses history transitions incorrectly
@ -573,7 +585,7 @@ been extended with a UDP socket, which is open for communication with
various Front-Ends (GUI-based or headless). An example Front-End written
in Tcl/Tk called "QSpyView" has been developed to demonstrate all the
features. The example application located in the directory
qpcpp\examples\arm-cm\dpp_ek-tm4c123gxl\qspy contains customization of
qpcpp/examples/arm-cm/dpp_ek-tm4c123gxl/qspy contains customization of
the "qspyview" script for the DPP application. Please refer to the
documentation of this example
(https://www.state-machine.com/qpcpp/arm-cm_dpp_ek-tm4c123gxl.html ) for
@ -597,29 +609,29 @@ Changes in detail:
1. Modified the QS software tracing component to add new functionality,
such as the QS-RX input channel. Also added new trace records.
2. Added file "qstamp.cpp" (in the qpcpp\include\ folder) to provide
2. Added file "qstamp.cpp" (in the qpcpp/include/ folder) to provide
time-stamp of the application build.
3. Added function QMsm::childStateObj() to the QMsm class and
QHsm::childState() to the QHsm class. These functions have been added to
support the shallow-history mechanism.
4. Modified all example projects (qpcpp\examples\ folder) to include the
4. Modified all example projects (qpcpp/examples/ folder) to include the
"qstamp.cpp" file and force its re-compilation for each new build, so
that every build has an up-to-date and unique time stamp.
5. Extended the DPP on TivaC LauchPad example (directory
qpcpp\examples\arm-cm\dpp_ek-tm4c123gxl\) to demonstrate QS-RX (QS
qpcpp/examples/arm-cm/dpp_ek-tm4c123gxl/) to demonstrate QS-RX (QS
receive channel).
6. Provided example of customizing the "QSpyView" Tcl/Tk script for the
DPP application in the directory
qpcpp\examples\arm-cm\dpp_ek-tm4c123gxl\qspy\
qpcpp/examples/arm-cm/dpp_ek-tm4c123gxl/qspy/
7. Modified all examples (qpcpp\examples\ folder) to call the
7. Modified all examples (qpcpp/examples/ folder) to call the
QS_ASSERTION() macro to the Q_onAssert() callback function.
8. Modified the startup code (in the qpcpp\3rd_party\ folder) for ARM
8. Modified the startup code (in the qpcpp/3rd_party/ folder) for ARM
Cortex-M to invoke the Q_onAssert() callback from the assert_failure()
exception handler. This is to allow application-level code to define
Q_onAssert() for each specific project.
@ -660,7 +672,7 @@ Changes in detail:
8. Updated all Windows examples to the latest QP API by compiling the code with the macro #QP_API_VERSION set to 9999 (latest API without backwards compatibility)
9. Improved the PC-Lint support for checking the application-level code located in in `examples\arm-cm\dpp_ek-tm4c123gxl\lint`
9. Improved the PC-Lint support for checking the application-level code located in in `examples/arm-cm/dpp_ek-tm4c123gxl/lint`
------------------------------------------------------------------------------
@ -677,7 +689,7 @@ Also, this release brings several cosmetic improvements:
2. The `qpcpp/ports/arm-cm/qk/gnu/qk_port.s` ARM Cortex-M port to QK with GNU has been modified to use the CMSIS-compliant symbol __FPU_PRESENT instead of the FPU_VFP_V4_SP_D16 symbol.
3. All Makefiles for the GNU toolset have been cleaned up, whereas any `\` (back-slash) characters in the paths have been repalced with `/` (forward-slash) characters. Also all these Makefiles have been updated to provide the __FPU_PRESENT to C/C++ and assembler when the hardware FPU is used.
3. All Makefiles for the GNU toolset have been cleaned up, whereas any `/` (back-slash) characters in the paths have been repalced with `/` (forward-slash) characters. Also all these Makefiles have been updated to provide the __FPU_PRESENT to C/C++ and assembler when the hardware FPU is used.
4. The file display drver for the EK-LM2S811 board locate at `qpc/3rd_party/ek-lm3s811/display96x16x1.c` has been modified to fix the problem with incorrect hardware delay with the GNU compiler at higher levels of optimization. The in-line assembly for the GNU compiler has been updated such that the delay loop cannot be "optimized away".
@ -752,7 +764,7 @@ Changes in detail:
------------------------------------------------------------------------------
@section qpcpp_5_3_1 Version 5.3.1, 2014-09-20
\note QP/C++ 5.3.1 remains backwards-compatible with all QP/C++ ports
/note QP/C++ 5.3.1 remains backwards-compatible with all QP/C++ ports
and applications
This release fixes the following bugs:
@ -782,7 +794,7 @@ release matches the new QM modeling tool version 3.1.0, which now
supports the "transition to history" connector and the corresponding
code generation for transitions to history.
\note QP/C++ 5.3.0 remains backwards-compatible with QP/C++ applications
/note QP/C++ 5.3.0 remains backwards-compatible with QP/C++ applications
developed for QP/C++ 4.x and QP/5.x. However, any QM models created for
the previous QP/C++ versions require re-generating the code with QM 3.1.0.
@ -834,9 +846,9 @@ transitions to entry point in submachine states).
uint_fast8_t.
8. Added QM models to the "Transition to History" design pattern example
(directory qpcpp\examples\win32\mingw\history) and also added an example
(directory qpcpp/examples/win32/mingw/history) and also added an example
for "Transition to History" with the QMsm class (see
qpcpp\examples\win32\mingw\history_qm).
qpcpp/examples/win32/mingw/history_qm).
9. Changed the m_prio attribute of QActive to uint_fast8_t.
@ -1011,7 +1023,7 @@ to DOS.
------------------------------------------------------------------------------
@section qpcpp_5_1_0 Version 5.1.0, 2013-10-02
\note QP/C++ 5.1.0 remains <b>backwards-compatible</b> with the existing
/note QP/C++ 5.1.0 remains <b>backwards-compatible</b> with the existing
QP/C 4.x applications, except the <b>ARM Cortex-M applications</b> need to
adjust the interrupt priorities. Specifically, you need to set the
interrupt priorities equal or lower than <b>QF_AWARE_ISR_CMSIS_PRI</b>
@ -1173,7 +1185,7 @@ tree to derive QMActive from QActive.
- removed the CMSIS (Cortex Microcontroller Software Interface
Standard) directories from the Cortex-M examples and moved it to
the common location in the %QPC%\ports\arm-cm\cmsis\ directory.
the common location in the %QPC%/ports/arm-cm/cmsis/ directory.
Upgraded the CMSIS to the latest version 3.20.
- added the ARM Cortex-M ports and examples with Keil/ARM MDK to
the QP Baseline Code.
@ -1192,7 +1204,7 @@ tree to derive QMActive from QActive.
- added search box and tree-view panel to the HTML documentation
\note This new policy of disabling interrupts in ARM Cortex-M divides
/note This new policy of disabling interrupts in ARM Cortex-M divides
interrupts into "kernel-unaware" interrupts, which are never disabled,
and "kernel-aware" interrupts, which are disabled in the QP critical
sections. Only "kernel-aware" interrupts are allowed to call QP
@ -1226,7 +1238,7 @@ implementation uses a lookup table.
4. Updated App Notes "QP and ARM Cortex-M with IAR" and "QP and ARM
Cortex-M with GNU".
5. Updated the PC-Lint support files (include\lib-qpcpp.lnt, au-misra2.lnt)
5. Updated the PC-Lint support files (include/lib-qpcpp.lnt, au-misra2.lnt)
to the latest PC-Lint 9.00j.
6. Updated the Application Note: "QP/C++ MISRA-C:2004 Compliance Matrix".
@ -1237,7 +1249,7 @@ several typos.
8. Removed the Qt ports and examples from the QP/C++ Baseline Code and
moved them to the separate QDK/C++-Qt.
\note QP/C++ Version 4.5.04 preserves full compatibility with QM 2.2.03
/note QP/C++ Version 4.5.04 preserves full compatibility with QM 2.2.03
and all existing QDKs for QP/C++ 4.5.xx.
@ -1246,9 +1258,9 @@ and all existing QDKs for QP/C++ 4.5.xx.
This release changes the directory structure of QP ports to various
operating systems, such as POSIX (Linux, BSD, etc.), Win32 (Windows),
Qt, etc. The OS ports are moved from the ports\80x86\ directory one
level up to ports\. Also, the OS examples are moved from the
exampels\80x86\ directory one level up to examples\.
Qt, etc. The OS ports are moved from the ports/80x86/ directory one
level up to ports/. Also, the OS examples are moved from the
exampels/80x86/ directory one level up to examples/.
------------------------------------------------------------------------------
@ -1308,15 +1320,15 @@ Changes in detail:
1. Modified QP port to Win32 and used the free Visual C++ Express
2010 with Platform SDK rather than Visual C++ Pro 2008. Renamed
the port directory from vc2008\ to vc\. Provided a completely
the port directory from vc2008/ to vc/. Provided a completely
revised App Note "QP and Win32".
2. Eliminated QP port to Win32 with one thread (Win32-1T).
3. Consolidated all QP ports to POSIX OSs (Linux, Linux64, Mac_OSX)
into a single port to POSIX and placed it in the directory posix\.
into a single port to POSIX and placed it in the directory posix/.
4. Renamed the port directory qt_1t\ to qt\.
4. Renamed the port directory qt_1t/ to qt/.
5. Added event constructor to qevt.h (controlled by the configuration
macro Q_EVT_CTOR).
@ -1336,8 +1348,8 @@ signals were passed to QP functions.
9. Modified all QP ports distributed in the QP/C++ baseline code
to generate only a single QP library, rather than separate
libraries for QEP, QF, QK, and QS. This includes all QP ports
to the desktop (ports\80x86\ directory) and ARM Cortex-M ports
(ports\arm-cortex\ directory).
to the desktop (ports/80x86/ directory) and ARM Cortex-M ports
(ports/arm-cortex/ directory).
10. Modified all examples to link only one QP library.

View File

@ -1,8 +1,8 @@
@echo off
:: ==========================================================================
:: Product: QP/C++ script for generating Doxygen documentation
:: Last Updated for Version: 5.9.9
:: Date of the Last Update: 2017-09-15
:: Last Updated for Version: 6.0.0
:: Date of the Last Update: 2017-10-13
::
:: Q u a n t u m L e a P s
:: ---------------------------
@ -38,7 +38,7 @@ echo usage:
echo make
echo make -CHM
set VERSION=5.9.9
set VERSION=6.0.0
:: Generate Resource Standard Metrics for QP/C++ .............................
set DOXHOME="C:\tools\doxygen\bin"

View File

@ -1,7 +1,7 @@
/** @page metrics Code Metrics
@code
Standard Code Metrics for QP/C++ 5.9.9
Standard Code Metrics for QP/C++ 6.0.0
Resource Standard Metrics (TM) for C, C++, C# and Java
Version 7.75 - mSquaredTechnologies.com
@ -9,7 +9,7 @@
License Type: Windows Single User License
Licensed To : Quantum Leaps, LLC
License No. : WS2975 License Date: Dec 15, 2013
Build Date : Sep 2 2009 Run Date: Oct 02, 2017
Build Date : Sep 2 2009 Run Date: Oct 13, 2017
(C)1996-2009 M Squared Technologies LLC
________________________________________________________________________
@ -154,7 +154,7 @@
~~ Total File Summary ~~
LOC 259 eLOC 227 lLOC 129 Comment 318 Lines 666
LOC 259 eLOC 227 lLOC 129 Comment 318 Lines 667
------------------------------------------------------------------------
~~ File Functional Summary ~~
@ -1942,7 +1942,7 @@
~~ Total File Summary ~~
LOC 291 eLOC 248 lLOC 127 Comment 290 Lines 567
LOC 292 eLOC 249 lLOC 128 Comment 291 Lines 570
------------------------------------------------------------------------
~~ File Functional Summary ~~
@ -2236,7 +2236,7 @@
~~ Total Project Summary ~~
LOC 4912 eLOC 4346 lLOC 1894 Comment 6481 Lines 11676
LOC 4913 eLOC 4347 lLOC 1895 Comment 6482 Lines 11680
Average per File, metric/37 files
LOC 132 eLOC 117 lLOC 51 Comment 175 Lines 315

View File

@ -1,7 +1,7 @@
##############################################################################
# Product: Makefile for QP/C++, DPP on STM32 NUCLEO-L053R8, QK kernel, GNU-ARM
# Last Updated for Version: 5.9.8
# Date of the Last Update: 2017-09-15
# Last Updated for Version: 6.0.0
# Date of the Last Update: 2017-10-10
#
# Q u a n t u m L e a P s
# ---------------------------
@ -202,11 +202,11 @@ ASFLAGS = $(ARM_CPU) $(ARM_FPU) $(ASM_CPU) $(ASM_FPU)
CFLAGS = $(ARM_CPU) $(ARM_FPU) $(FLOAT_ABI) -mthumb -Wall \
-ffunction-sections -fdata-sections \
-O1 $(INCLUDES) $(DEFINES) -DNDEBUG
-O2 $(INCLUDES) $(DEFINES) -DNDEBUG
CPPFLAGS = $(ARM_CPU) $(ARM_FPU) $(FLOAT_ABI) -mthumb -Wall \
-ffunction-sections -fdata-sections -fno-rtti -fno-exceptions \
-O1 $(INCLUDES) $(DEFINES) -DNDEBUG
-O2 $(INCLUDES) $(DEFINES) -DNDEBUG
else ifeq (spy, $(CONF)) # Spy configuration ................................
@ -218,11 +218,11 @@ ASFLAGS = -g $(ARM_CPU) $(ARM_FPU) $(ASM_CPU) $(ASM_FPU)
CFLAGS = -g $(ARM_CPU) $(ARM_FPU) $(FLOAT_ABI) -mthumb -Wall \
-ffunction-sections -fdata-sections \
-O $(INCLUDES) $(DEFINES) -DQ_SPY
-O2 $(INCLUDES) $(DEFINES) -DQ_SPY
CPPFLAGS = -g $(ARM_CPU) $(ARM_FPU) $(FLOAT_ABI) -mthumb -Wall \
-ffunction-sections -fdata-sections -fno-rtti -fno-exceptions \
-O $(INCLUDES) $(DEFINES) -DQ_SPY
-O2 $(INCLUDES) $(DEFINES) -DQ_SPY
else # default Debug configuration ..........................................
@ -232,11 +232,11 @@ ASFLAGS = -g $(ARM_CPU) $(ARM_FPU) $(ASM_CPU) $(ASM_FPU)
CFLAGS = -g $(ARM_CPU) $(ARM_FPU) $(FLOAT_ABI) -mthumb -Wall \
-ffunction-sections -fdata-sections \
-O $(INCLUDES) $(DEFINES)
-O2 $(INCLUDES) $(DEFINES)
CPPFLAGS = -g $(ARM_CPU) $(ARM_FPU) $(FLOAT_ABI) -mthumb -Wall \
-ffunction-sections -fdata-sections -fno-rtti -fno-exceptions \
-O $(INCLUDES) $(DEFINES)
-O2 $(INCLUDES) $(DEFINES)
endif # ......................................................................

View File

@ -1,7 +1,7 @@
##############################################################################
# Product: Makefile for QP/C++, DPP on STM32 NUCLEO-L053R8, QV kernel, GNU-ARM
# Last Updated for Version: 5.8.1
# Date of the Last Update: 2016-12-12
# Last Updated for Version: 6.0.0
# Date of the Last Update: 2017-10-10
#
# Q u a n t u m L e a P s
# ---------------------------
@ -202,11 +202,11 @@ ASFLAGS = $(ARM_CPU) $(ARM_FPU) $(ASM_CPU) $(ASM_FPU)
CFLAGS = $(ARM_CPU) $(ARM_FPU) $(FLOAT_ABI) -mthumb -Wall \
-ffunction-sections -fdata-sections \
-O1 $(INCLUDES) $(DEFINES) -DNDEBUG
-O2 $(INCLUDES) $(DEFINES) -DNDEBUG
CPPFLAGS = $(ARM_CPU) $(ARM_FPU) $(FLOAT_ABI) -mthumb -Wall \
-ffunction-sections -fdata-sections -fno-rtti -fno-exceptions \
-O1 $(INCLUDES) $(DEFINES) -DNDEBUG
-O2 $(INCLUDES) $(DEFINES) -DNDEBUG
else ifeq (spy, $(CONF)) # Spy configuration ................................
@ -218,11 +218,11 @@ ASFLAGS = -g $(ARM_CPU) $(ARM_FPU) $(ASM_CPU) $(ASM_FPU)
CFLAGS = -g $(ARM_CPU) $(ARM_FPU) $(FLOAT_ABI) -mthumb -Wall \
-ffunction-sections -fdata-sections \
-O $(INCLUDES) $(DEFINES) -DQ_SPY
-O2 $(INCLUDES) $(DEFINES) -DQ_SPY
CPPFLAGS = -g $(ARM_CPU) $(ARM_FPU) $(FLOAT_ABI) -mthumb -Wall \
-ffunction-sections -fdata-sections -fno-rtti -fno-exceptions \
-O $(INCLUDES) $(DEFINES) -DQ_SPY
-O2 $(INCLUDES) $(DEFINES) -DQ_SPY
else # default Debug configuration ..........................................
@ -232,11 +232,11 @@ ASFLAGS = -g $(ARM_CPU) $(ARM_FPU) $(ASM_CPU) $(ASM_FPU)
CFLAGS = -g $(ARM_CPU) $(ARM_FPU) $(FLOAT_ABI) -mthumb -Wall \
-ffunction-sections -fdata-sections \
-O $(INCLUDES) $(DEFINES)
-O2 $(INCLUDES) $(DEFINES)
CPPFLAGS = -g $(ARM_CPU) $(ARM_FPU) $(FLOAT_ABI) -mthumb -Wall \
-ffunction-sections -fdata-sections -fno-rtti -fno-exceptions \
-O $(INCLUDES) $(DEFINES)
-O2 $(INCLUDES) $(DEFINES)
endif # ......................................................................

View File

@ -1,7 +1,7 @@
///***************************************************************************
// Product: DPP example, STM32 NUCLEO-L053R8 board, preemptive QXK kernel
// Last Updated for Version: 5.9.7
// Date of the Last Update: 2017-08-19
// Last Updated for Version: 5.6.0
// Date of the Last Update: 2017-10-12
//
// Q u a n t u m L e a P s
// ---------------------------
@ -422,6 +422,17 @@ void QS::onCommand(uint8_t cmdId, uint32_t param1,
(void)param3;
//TBD
}
/*???
void bug_test(void) {
uint32_t i;
for(i = 0; i < 10; i++) {
QS_BEGIN(123, 0);
QS_U32(8, 0);
QS_END();
}
}
*/
#endif // Q_SPY
//--------------------------------------------------------------------------*/

View File

@ -1,6 +1,6 @@
##############################################################################
# Product: Makefile for QP/C++, DPP on NUCLEO-L053R8, QXK kernel, GNU-ARM
# Last Updated for Version: 5.9.9
# Last Updated for Version: 6.0.0
# Date of the Last Update: 2017-10-09
#
# Q u a n t u m L e a P s

View File

@ -1,60 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<session version="4.0.3">
<group name="locked"/>
<group name="settings">
<item name="tabs">1</item>
<item name="windows">0</item>
<item name="grid">3</item>
<item name="backups">0</item>
</group>
<group name="windows">
<item id="SMs::Calc::SM::operand">0,0,800,564,*</item>
</group>
<group name="search">
<item name="options">2032128</item>
<item name="replace">0</item>
</group>
<group name="vars"/>
<group name="tools">
<group name="tool">
<item name="icon">0</item>
<item name="title"></item>
<item name="command"></item>
<item name="args"></item>
<item name="dir"></item>
<item name="options">0</item>
</group>
<group name="tool">
<item name="icon">0</item>
<item name="title"></item>
<item name="command"></item>
<item name="args"></item>
<item name="dir"></item>
<item name="options">0</item>
</group>
<group name="tool">
<item name="icon">0</item>
<item name="title"></item>
<item name="command"></item>
<item name="args"></item>
<item name="dir"></item>
<item name="options">0</item>
</group>
<group name="tool">
<item name="icon">0</item>
<item name="title"></item>
<item name="command"></item>
<item name="args"></item>
<item name="dir"></item>
<item name="options">0</item>
</group>
<group name="tool">
<item name="icon">0</item>
<item name="title"></item>
<item name="command"></item>
<item name="args"></item>
<item name="dir"></item>
<item name="options">0</item>
</group>
</group>
</session>

View File

@ -6,10 +6,10 @@
// DO NOT EDIT THIS FILE MANUALLY. All your changes will be lost.
//
// This code is covered by the following QP license:
// License # : QPCPP-EVAL-590
// License # : QPCPP-EVAL-171231
// Issued to : Company/individual evaluating the QP/C++ framework
// Framework(s): qpcpp
// Support ends: 2017-06-30
// Support ends: 2017-12-31
// Product(s) :
// This license is available only for evaluation purposes and
// the generated code is still licensed under the terms of GPL.

View File

@ -6,10 +6,10 @@
// DO NOT EDIT THIS FILE MANUALLY. All your changes will be lost.
//
// This code is covered by the following QP license:
// License # : QPCPP-EVAL-590
// License # : QPCPP-EVAL-171231
// Issued to : Company/individual evaluating the QP/C++ framework
// Framework(s): qpcpp
// Support ends: 2017-06-30
// Support ends: 2017-12-31
// Product(s) :
// This license is available only for evaluation purposes and
// the generated code is still licensed under the terms of GPL.

View File

@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<model version="4.0.2" links="0">
<documentation>Calc is the model of the Calculator described in Chapter 4 of PSiCC2</documentation>
<framework name="qpcpp" license="../../../QPCPP-EVAL-590.qlc"/>
<model version="4.0.3" links="0">
<documentation>Calc is the model of the Calculator described in Chapter 4 of PSiCC2.</documentation>
<framework name="qpcpp" license="../../../QPCPP-EVAL-171231.qlc"/>
<package name="Events" stereotype="0x01">
<class name="CalcEvt" superclass="qpcpp::QEvt">
<attribute name="key_code" type="uint8_t" visibility="0x00" properties="0x00"/>

View File

@ -1,60 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<session version="4.0.3">
<group name="locked"/>
<group name="settings">
<item name="tabs">1</item>
<item name="windows">0</item>
<item name="grid">3</item>
<item name="backups">0</item>
</group>
<group name="windows">
<item id="SMs::ToastOven::SM">0,0,800,564,*</item>
</group>
<group name="search">
<item name="options">2032128</item>
<item name="replace">0</item>
</group>
<group name="vars"/>
<group name="tools">
<group name="tool">
<item name="icon">0</item>
<item name="title"></item>
<item name="command"></item>
<item name="args"></item>
<item name="dir"></item>
<item name="options">0</item>
</group>
<group name="tool">
<item name="icon">0</item>
<item name="title"></item>
<item name="command"></item>
<item name="args"></item>
<item name="dir"></item>
<item name="options">0</item>
</group>
<group name="tool">
<item name="icon">0</item>
<item name="title"></item>
<item name="command"></item>
<item name="args"></item>
<item name="dir"></item>
<item name="options">0</item>
</group>
<group name="tool">
<item name="icon">0</item>
<item name="title"></item>
<item name="command"></item>
<item name="args"></item>
<item name="dir"></item>
<item name="options">0</item>
</group>
<group name="tool">
<item name="icon">0</item>
<item name="title"></item>
<item name="command"></item>
<item name="args"></item>
<item name="dir"></item>
<item name="options">0</item>
</group>
</group>
</session>

View File

@ -6,10 +6,10 @@
// DO NOT EDIT THIS FILE MANUALLY. All your changes will be lost.
//
// This code is covered by the following QP license:
// License # : QPCPP-EVAL-590
// License # : QPCPP-EVAL-171231
// Issued to : Company/individual evaluating the QP/C++ framework
// Framework(s): qpcpp
// Support ends: 2017-06-30
// Support ends: 2017-12-31
// Product(s) :
// This license is available only for evaluation purposes and
// the generated code is still licensed under the terms of GPL.

View File

@ -6,10 +6,10 @@
// DO NOT EDIT THIS FILE MANUALLY. All your changes will be lost.
//
// This code is covered by the following QP license:
// License # : QPCPP-EVAL-590
// License # : QPCPP-EVAL-171231
// Issued to : Company/individual evaluating the QP/C++ framework
// Framework(s): qpcpp
// Support ends: 2017-06-30
// Support ends: 2017-12-31
// Product(s) :
// This license is available only for evaluation purposes and
// the generated code is still licensed under the terms of GPL.

View File

@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<model version="4.0.2" links="0">
<model version="4.0.3" links="0">
<documentation>QMsmTst is a contrived state machine from Chapter 2 of the PSiCC2 book for testing the QMsm class implementation.</documentation>
<framework name="qpcpp" license="../../../QPCPP-EVAL-590.qlc"/>
<framework name="qpcpp" license="../../../QPCPP-EVAL-171231.qlc"/>
<package name="SMs" stereotype="0x02">
<class name="ToastOven" superclass="qpcpp::QMsm">
<documentation>Oven state machine</documentation>

View File

@ -1,60 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<session version="4.0.3">
<group name="locked"/>
<group name="settings">
<item name="tabs">1</item>
<item name="windows">0</item>
<item name="grid">3</item>
<item name="backups">0</item>
</group>
<group name="windows">
<item id="SMs::QMsmTst::SM">0,0,800,564,*</item>
</group>
<group name="search">
<item name="options">2032128</item>
<item name="replace">0</item>
</group>
<group name="vars"/>
<group name="tools">
<group name="tool">
<item name="icon">0</item>
<item name="title"></item>
<item name="command"></item>
<item name="args"></item>
<item name="dir"></item>
<item name="options">0</item>
</group>
<group name="tool">
<item name="icon">0</item>
<item name="title"></item>
<item name="command"></item>
<item name="args"></item>
<item name="dir"></item>
<item name="options">0</item>
</group>
<group name="tool">
<item name="icon">0</item>
<item name="title"></item>
<item name="command"></item>
<item name="args"></item>
<item name="dir"></item>
<item name="options">0</item>
</group>
<group name="tool">
<item name="icon">0</item>
<item name="title"></item>
<item name="command"></item>
<item name="args"></item>
<item name="dir"></item>
<item name="options">0</item>
</group>
<group name="tool">
<item name="icon">0</item>
<item name="title"></item>
<item name="command"></item>
<item name="args"></item>
<item name="dir"></item>
<item name="options">0</item>
</group>
</group>
</session>

View File

@ -6,10 +6,10 @@
// DO NOT EDIT THIS FILE MANUALLY. All your changes will be lost.
//
// This code is covered by the following QP license:
// License # : QPCPP-EVAL-590
// License # : QPCPP-EVAL-171231
// Issued to : Company/individual evaluating the QP/C++ framework
// Framework(s): qpcpp
// Support ends: 2017-06-30
// Support ends: 2017-12-31
// Product(s) :
// This license is available only for evaluation purposes and
// the generated code is still licensed under the terms of GPL.

View File

@ -6,10 +6,10 @@
// DO NOT EDIT THIS FILE MANUALLY. All your changes will be lost.
//
// This code is covered by the following QP license:
// License # : QPCPP-EVAL-590
// License # : QPCPP-EVAL-171231
// Issued to : Company/individual evaluating the QP/C++ framework
// Framework(s): qpcpp
// Support ends: 2017-06-30
// Support ends: 2017-12-31
// Product(s) :
// This license is available only for evaluation purposes and
// the generated code is still licensed under the terms of GPL.

View File

@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<model version="4.0.2" links="0">
<model version="4.0.3" links="0">
<documentation>QMsmTst is a test for the QMsm state machine based on Chapter 2 of the PSiCC2 book.</documentation>
<framework name="qpcpp" license="../../../QPCPP-EVAL-590.qlc"/>
<framework name="qpcpp" license="../../../QPCPP-EVAL-171231.qlc"/>
<package name="SMs" stereotype="0x02">
<class name="QMsmTst" superclass="qpcpp::QMsm">
<documentation>Test active object</documentation>

View File

@ -3,8 +3,8 @@
/// @ingroup qep
/// @cond
///***************************************************************************
/// Last updated for version 5.9.9
/// Last updated on 2017-09-27
/// Last updated for version 6.0.0
/// Last updated on 2017-10-12
///
/// Q u a n t u m L e a P s
/// ---------------------------
@ -43,15 +43,16 @@
//! The current QP version as a decimal constant XYZ, where X is a 1-digit
// major version number, Y is a 1-digit minor version number, and Z is
// a 1-digit release number.
#define QP_VERSION 599
#define QP_VERSION 600
//! The current QP version number string of the form X.Y.Z, where X is
// a 1-digit major version number, Y is a 1-digit minor version number,
// and Z is a 1-digit release number.
#define QP_VERSION_STR "5.9.9"
#define QP_VERSION_STR "6.0.0"
//! Tamperproof current QP release (6.0.0) and date (2017-10-13)
#define QP_RELEASE 0x9A117A57U
//! Tamperproof current QP release (5.9.9) and date (2017-09-29)
#define QP_RELEASE 0x9A1E4B98U
//****************************************************************************
#ifndef Q_SIGNAL_SIZE

View File

@ -2,8 +2,8 @@
/// @brief QF/C++ port to ARM Cortex-M, preemptive QK kernel, GNU-ARM toolset
/// @cond
///***************************************************************************
/// Last Updated for Version: 5.9.0
/// Date of the Last Update: 2017-05-05
/// Last Updated for Version: 6.0.0
/// Date of the Last Update: 2017-10-12
///
/// Q u a n t u m L e a P s
/// ---------------------------
@ -51,65 +51,47 @@
#define QF_INT_DISABLE() __asm volatile ("cpsid i")
#define QF_INT_ENABLE() __asm volatile ("cpsie i")
// QF critical section entry/exit (save and restore interrupt status)
// QF critical section (save and restore interrupt status), see NOTE6
#define QF_CRIT_STAT_TYPE uint32_t
#define QF_CRIT_ENTRY(primask_) do { \
QF_GET_PRIMASK((primask_)); \
QF_INT_DISABLE(); \
__asm volatile ("mrs %0,PRIMASK" : "=r" (primask_) ::); \
__asm volatile ("cpsid i" :: "" (primask_) : ); \
} while (0)
#define QF_CRIT_EXIT(primask_) QF_SET_PRIMASK((primask_))
#define QF_CRIT_EXIT(primask_) \
__asm volatile ("msr PRIMASK,%0" :: "r" (primask_) : )
// CMSIS threshold for "QF-aware" interrupts, see NOTE2 and NOTE5
// CMSIS threshold for "QF-aware" interrupts, see NOTE2,4
#define QF_AWARE_ISR_CMSIS_PRI 0
// macro for getting the PRIMASK register
#define QF_GET_PRIMASK(primask_) __asm volatile (\
"mrs %0,PRIMASK" : "=r" (primask_) :: )
// macro for setting the PRIMASK register
#define QF_SET_PRIMASK(primask_) __asm volatile (\
"msr PRIMASK,%0" :: "r" (primask_) : )
#else // Cortex-M3/M4/M7
// Cortex-M3/M4/M7 alternative interrupt disabling with PRIMASK
#define QF_PRIMASK_DISABLE() __asm volatile ("cpsid i")
#define QF_PRIMASK_ENABLE() __asm volatile ("cpsie i")
// Cortex-M3/M4/M7 interrupt disabling policy, see NOTE3,4
#define QF_INT_DISABLE() __asm volatile (\
"cpsid i\n\t" "msr BASEPRI,%0\n\t" "cpsie i" :: "r" (QF_BASEPRI) : )
#define QF_INT_ENABLE() __asm volatile (\
"msr BASEPRI,%0" :: "r" (0) : )
// Cortex-M3/M4/M7 interrupt disabling policy, see NOTE3 and NOTE4
#define QF_INT_DISABLE() do { \
QF_PRIMASK_DISABLE(); \
QF_SET_BASEPRI(QF_BASEPRI); \
QF_PRIMASK_ENABLE(); \
// QF critical section (save and restore interrupt status), see NOTE5,6
#define QF_CRIT_STAT_TYPE uint32_t
#define QF_CRIT_ENTRY(basepri_) do { \
__asm volatile ("mrs %0,BASEPRI" : "=r" (basepri_) :: ); \
__asm volatile ("cpsid i" :: "" (basepri_) : ); \
__asm volatile ("msr BASEPRI,%0" :: "r" (QF_BASEPRI) : ); \
__asm volatile ("cpsie i" :: "" (basepri_) : ); \
} while (0)
#define QF_INT_ENABLE() QF_SET_BASEPRI(0U)
// QF critical section entry/exit (save and restore interrupt status)
#define QF_CRIT_STAT_TYPE unsigned long
#define QF_CRIT_ENTRY(basepri_) do {\
QF_GET_BASEPRI((basepri_)); \
QF_INT_DISABLE(); \
} while (0)
#define QF_CRIT_EXIT(basepri_) QF_SET_BASEPRI((basepri_))
#define QF_CRIT_EXIT(basepri_) \
__asm volatile ("msr BASEPRI,%0" :: "r" (basepri_) : )
// BASEPRI threshold for "QF-aware" interrupts, see NOTE3.
// CAUTION: keep in synch with the value defined in "qk_port.s"
//
#define QF_BASEPRI (0xFFU >> 2)
/* CMSIS threshold for "QF-aware" interrupts, see NOTE4 */
/* CMSIS threshold for "QF-aware" interrupts, see NOTE5 */
#define QF_AWARE_ISR_CMSIS_PRI (QF_BASEPRI >> (8 - __NVIC_PRIO_BITS))
// Cortex-M3/M4/M7 provide the CLZ instruction for fast LOG2
#define QF_LOG2(n_) (static_cast<uint_fast8_t>(32U - __builtin_clz(n_)))
// macro for getting the BASEPRI register
#define QF_GET_BASEPRI(basepri_) __asm volatile (\
"mrs %0,BASEPRI" : "=r" (basepri_) :: )
// macro for setting the BASEPRI register
#define QF_SET_BASEPRI(basepri_) __asm volatile (\
"msr BASEPRI,%0" :: "r" (basepri_) : )
#endif
#define QF_CRIT_EXIT_NOP() __asm volatile ("isb")
@ -142,13 +124,6 @@
// ("QF-aware" interrupts ), can call QF services.
//
// NOTE4:
// The selective disabling of "QF-aware" interrupts with the BASEPRI register
// has a problem on ARM Cortex-M7 core r0p1 (see ARM-EPM-064408, errata
// 837070). The workaround recommended by ARM is to surround MSR BASEPRI with
// the CPSID i/CPSIE i pair, which is implemented in the QF_INT_DISABLE()
// macro. This workaround works also for Cortex-M3/M4 cores.
//
// NOTE5:
// The QF_AWARE_ISR_CMSIS_PRI macro is useful as an offset for enumerating
// the "QF-aware" interrupt priorities in the applications, whereas the
// numerical values of the "QF-aware" interrupts must be greater or equal to
@ -161,5 +136,21 @@
// remains generic and not dependent on the number of implemented priority bits
// implemented in the NVIC.
//
// NOTE5:
// The selective disabling of "QF-aware" interrupts with the BASEPRI register
// has a problem on ARM Cortex-M7 core r0p1 (see ARM-EPM-064408, errata
// 837070). The workaround recommended by ARM is to surround MSR BASEPRI with
// the CPSID i/CPSIE i pair, which is implemented in the QF_INT_DISABLE()
// macro. This workaround works also for Cortex-M3/M4 cores.
//
// NOTE6:
// The critical section for Cortex-M0(+)/M1 (ARMv6-M architecture) is
// specifically defined to avoid the GNU-ARM bug 1722849 (see
// https://bugs.launchpad.net/gcc-arm-embedded/+bug/1722849 ). Specifically,
// the in-line assembly for disabling of interrupts with "cpsid i" has an
// artificial dependency on the (primask_) argument. This ensures that
// the GNU-ARM compiler will evaluate the preceeding "mrs %0,PRIMASK"
// __before__ evaluating "cpsid i".
//
#endif // qf_port_h

View File

@ -2,8 +2,8 @@
/// @brief QF/C++ port to ARM Cortex-M, cooperative QV kernel, GNU-ARM toolset
/// @cond
///***************************************************************************
/// Last Updated for Version: 5.9.0
/// Date of the Last Update: 2017-05-05
/// Last Updated for Version: 6.0.0
/// Date of the Last Update: 2017-10-12
///
/// Q u a n t u m L e a P s
/// ---------------------------
@ -51,65 +51,47 @@
#define QF_INT_DISABLE() __asm volatile ("cpsid i")
#define QF_INT_ENABLE() __asm volatile ("cpsie i")
// QF critical section entry/exit (save and restore interrupt status)
// QF critical section (save and restore interrupt status), see NOTE6
#define QF_CRIT_STAT_TYPE uint32_t
#define QF_CRIT_ENTRY(primask_) do { \
QF_GET_PRIMASK((primask_)); \
QF_INT_DISABLE(); \
__asm volatile ("mrs %0,PRIMASK" : "=r" (primask_) ::); \
__asm volatile ("cpsid i" :: "" (primask_) : ); \
} while (0)
#define QF_CRIT_EXIT(primask_) QF_SET_PRIMASK((primask_))
#define QF_CRIT_EXIT(primask_) \
__asm volatile ("msr PRIMASK,%0" :: "r" (primask_) : )
// CMSIS threshold for "QF-aware" interrupts, see NOTE2 and NOTE5
// CMSIS threshold for "QF-aware" interrupts, see NOTE2,4
#define QF_AWARE_ISR_CMSIS_PRI 0
// macro for getting the PRIMASK register
#define QF_GET_PRIMASK(primask_) __asm volatile (\
"mrs %0,PRIMASK" : "=r" (primask_) :: )
// macro for setting the PRIMASK register
#define QF_SET_PRIMASK(primask_) __asm volatile (\
"msr PRIMASK,%0" :: "r" (primask_) : )
#else // Cortex-M3/M4/M7
// Cortex-M3/M4/M7 alternative interrupt disabling with PRIMASK
#define QF_PRIMASK_DISABLE() __asm volatile ("cpsid i")
#define QF_PRIMASK_ENABLE() __asm volatile ("cpsie i")
// Cortex-M3/M4/M7 interrupt disabling policy, see NOTE3,4
#define QF_INT_DISABLE() __asm volatile (\
"cpsid i\n\t" "msr BASEPRI,%0\n\t" "cpsie i" :: "r" (QF_BASEPRI) : )
#define QF_INT_ENABLE() __asm volatile (\
"msr BASEPRI,%0" :: "r" (0) : )
// Cortex-M3/M4/M7 interrupt disabling policy, see NOTE3 and NOTE4
#define QF_INT_DISABLE() do { \
QF_PRIMASK_DISABLE(); \
QF_SET_BASEPRI(QF_BASEPRI); \
QF_PRIMASK_ENABLE(); \
// QF critical section (save and restore interrupt status), see NOTE5,6
#define QF_CRIT_STAT_TYPE uint32_t
#define QF_CRIT_ENTRY(basepri_) do { \
__asm volatile ("mrs %0,BASEPRI" : "=r" (basepri_) :: ); \
__asm volatile ("cpsid i" :: "" (basepri_) : ); \
__asm volatile ("msr BASEPRI,%0" :: "r" (QF_BASEPRI) : ); \
__asm volatile ("cpsie i" :: "" (basepri_) : ); \
} while (0)
#define QF_INT_ENABLE() QF_SET_BASEPRI(0U)
// QF critical section entry/exit (save and restore interrupt status)
#define QF_CRIT_STAT_TYPE unsigned long
#define QF_CRIT_ENTRY(basepri_) do {\
QF_GET_BASEPRI((basepri_)); \
QF_INT_DISABLE(); \
} while (0)
#define QF_CRIT_EXIT(basepri_) QF_SET_BASEPRI((basepri_))
#define QF_CRIT_EXIT(basepri_) \
__asm volatile ("msr BASEPRI,%0" :: "r" (basepri_) : )
// BASEPRI threshold for "QF-aware" interrupts, see NOTE3.
// CAUTION: keep in synch with the value defined in "qk_port.s"
//
#define QF_BASEPRI (0xFFU >> 2)
/* CMSIS threshold for "QF-aware" interrupts, see NOTE4 */
/* CMSIS threshold for "QF-aware" interrupts, see NOTE5 */
#define QF_AWARE_ISR_CMSIS_PRI (QF_BASEPRI >> (8 - __NVIC_PRIO_BITS))
// Cortex-M3/M4/M7 provide the CLZ instruction for fast LOG2
#define QF_LOG2(n_) (static_cast<uint_fast8_t>(32U - __builtin_clz(n_)))
// macro for getting the BASEPRI register
#define QF_GET_BASEPRI(basepri_) __asm volatile (\
"mrs %0,BASEPRI" : "=r" (basepri_) :: )
// macro for setting the BASEPRI register
#define QF_SET_BASEPRI(basepri_) __asm volatile (\
"msr BASEPRI,%0" :: "r" (basepri_) : )
#endif
#define QF_CRIT_EXIT_NOP() __asm volatile ("isb")
@ -142,13 +124,6 @@
// ("QF-aware" interrupts ), can call QF services.
//
// NOTE4:
// The selective disabling of "QF-aware" interrupts with the BASEPRI register
// has a problem on ARM Cortex-M7 core r0p1 (see ARM-EPM-064408, errata
// 837070). The workaround recommended by ARM is to surround MSR BASEPRI with
// the CPSID i/CPSIE i pair, which is implemented in the QF_INT_DISABLE()
// macro. This workaround works also for Cortex-M3/M4 cores.
//
// NOTE5:
// The QF_AWARE_ISR_CMSIS_PRI macro is useful as an offset for enumerating
// the "QF-aware" interrupt priorities in the applications, whereas the
// numerical values of the "QF-aware" interrupts must be greater or equal to
@ -161,5 +136,21 @@
// remains generic and not dependent on the number of implemented priority bits
// implemented in the NVIC.
//
// NOTE5:
// The selective disabling of "QF-aware" interrupts with the BASEPRI register
// has a problem on ARM Cortex-M7 core r0p1 (see ARM-EPM-064408, errata
// 837070). The workaround recommended by ARM is to surround MSR BASEPRI with
// the CPSID i/CPSIE i pair, which is implemented in the QF_INT_DISABLE()
// macro. This workaround works also for Cortex-M3/M4 cores.
//
// NOTE6:
// The critical section for Cortex-M0(+)/M1 (ARMv6-M architecture) is
// specifically defined to avoid the GNU-ARM bug 1722849 (see
// https://bugs.launchpad.net/gcc-arm-embedded/+bug/1722849 ). Specifically,
// the in-line assembly for disabling of interrupts with "cpsid i" has an
// artificial dependency on the (primask_) argument. This ensures that
// the GNU-ARM compiler will evaluate the preceeding "mrs %0,PRIMASK"
// __before__ evaluating "cpsid i".
//
#endif // qf_port_h

View File

@ -1,7 +1,7 @@
;*****************************************************************************
; Product: QXK port to ARM Cortex-M (M0,M0+,M3,M4,M7), ARM-Keil assembler
; Last Updated for Version: 5.9.6
; Date of the Last Update: 2017-07-28
; Last Updated for Version: 6.0.0
; Date of the Last Update: 2017-10-10
;
; Q u a n t u m L e a P s
; ---------------------------
@ -226,8 +226,8 @@ PendSV_activate
SUBS r2,r2,#1 ; align Thumb-address at halfword (new pc)
LDR r1,=Thread_ret ; return address after the call (new lr)
SUB sp,sp,#8*4 ; reserve space for exception stack frame
ADD r0,sp,#5*4 ; r0 := 5 registers below the top of stack
SUB sp,sp,#(8*4) ; reserve space for exception stack frame
ADD r0,sp,#(5*4) ; r0 := 5 registers below the top of stack
STM r0!,{r1-r3} ; save xpsr,pc,lr
MOVS r0,#6
@ -249,12 +249,15 @@ PendSV_error
; r12 -> QXK_attr_.next
PendSV_save_ao
IF {TARGET_ARCH_THUMB} == 3 ; Cortex-M0/M0+/M1 (v6-M, v6S-M)?
PUSH {r4-r7} ; save the low registers
SUB sp,sp,#(8*4) ; make room for 8 registers r4-r11
MOV r0,sp ; r0 := temporary stack pointer
STMIA r0!,{r4-r7} ; save the low registers
MOV r4,r8 ; move the high registers to low registers...
MOV r5,r9
MOV r6,r10
MOV r7,r11
PUSH {r4-r7} ; save the high registers
STMIA r0!,{r4-r7} ; save the high registers
MOV r0,r12 ; restore QXK_attr_.next in r0
ELSE ; M3/M4/M7
PUSH {r4-r11} ; save r4-r11 on top of the exception frame
IF {TARGET_FPU_VFP} == {TRUE} ; if VFP available...
@ -284,7 +287,7 @@ PendSV_restore_ao
IF {TARGET_ARCH_THUMB} == 3 ; Cortex-M0/M0+/M1 (v6-M, v6S-M)?
MOV r0,sp ; r0 := top of stack
MOV r1,r0
ADDS r1,r1,#(4*4) ; point r0 to the 4 high registers r7-r11
ADDS r1,r1,#(4*4) ; point r1 to the 4 high registers r7-r11
LDMIA r1!,{r4-r7} ; pop the 4 high registers into low registers
MOV r8,r4 ; move low registers into high registers
MOV r9,r5
@ -513,9 +516,9 @@ QXK_stackInit_ FUNCTION
LSLS r3,r0,#3
; make room for the thread's stack frame...
SUBS r3,r3,#16*4 ; r3 := top of the 16-register stack frame
SUBS r3,r3,#(16*4) ; r3 := top of the 16-register stack frame
IF {TARGET_FPU_VFP} == {TRUE} ; if VFP available...
SUBS r3,r3,#2*4 ; r3 := top of the 18-register stack frame
SUBS r3,r3,#(2*4) ; r3 := top of the 18-register stack frame
ENDIF ; VFP available
; pre-fill the unused part of the stack with 0xDEADBEEF...................

View File

@ -2,8 +2,8 @@
/// @brief QF/C++ port to ARM Cortex-M, dual-mode QXK kernel, GNU-ARM toolset
/// @cond
///***************************************************************************
/// Last Updated for Version: 5.9.0
/// Date of the Last Update: 2017-05-05
/// Last Updated for Version: 6.0.0
/// Date of the Last Update: 2017-10-12
///
/// Q u a n t u m L e a P s
/// ---------------------------
@ -51,65 +51,47 @@
#define QF_INT_DISABLE() __asm volatile ("cpsid i")
#define QF_INT_ENABLE() __asm volatile ("cpsie i")
// QF critical section entry/exit (save and restore interrupt status)
// QF critical section (save and restore interrupt status), see NOTE6
#define QF_CRIT_STAT_TYPE uint32_t
#define QF_CRIT_ENTRY(primask_) do { \
QF_GET_PRIMASK((primask_)); \
QF_INT_DISABLE(); \
__asm volatile ("mrs %0,PRIMASK" : "=r" (primask_) ::); \
__asm volatile ("cpsid i" :: "" (primask_) : ); \
} while (0)
#define QF_CRIT_EXIT(primask_) QF_SET_PRIMASK((primask_))
#define QF_CRIT_EXIT(primask_) \
__asm volatile ("msr PRIMASK,%0" :: "r" (primask_) : )
// CMSIS threshold for "QF-aware" interrupts, see NOTE2 and NOTE5
// CMSIS threshold for "QF-aware" interrupts, see NOTE2,4
#define QF_AWARE_ISR_CMSIS_PRI 0
// macro for getting the PRIMASK register
#define QF_GET_PRIMASK(primask_) __asm volatile (\
"mrs %0,PRIMASK" : "=r" (primask_) :: )
// macro for setting the PRIMASK register
#define QF_SET_PRIMASK(primask_) __asm volatile (\
"msr PRIMASK,%0" :: "r" (primask_) : )
#else // Cortex-M3/M4/M7
// Cortex-M3/M4/M7 alternative interrupt disabling with PRIMASK
#define QF_PRIMASK_DISABLE() __asm volatile ("cpsid i")
#define QF_PRIMASK_ENABLE() __asm volatile ("cpsie i")
// Cortex-M3/M4/M7 interrupt disabling policy, see NOTE3,4
#define QF_INT_DISABLE() __asm volatile (\
"cpsid i\n\t" "msr BASEPRI,%0\n\t" "cpsie i" :: "r" (QF_BASEPRI) : )
#define QF_INT_ENABLE() __asm volatile (\
"msr BASEPRI,%0" :: "r" (0) : )
// Cortex-M3/M4/M7 interrupt disabling policy, see NOTE3 and NOTE4
#define QF_INT_DISABLE() do { \
QF_PRIMASK_DISABLE(); \
QF_SET_BASEPRI(QF_BASEPRI); \
QF_PRIMASK_ENABLE(); \
// QF critical section (save and restore interrupt status), see NOTE5,6
#define QF_CRIT_STAT_TYPE uint32_t
#define QF_CRIT_ENTRY(basepri_) do { \
__asm volatile ("mrs %0,BASEPRI" : "=r" (basepri_) :: ); \
__asm volatile ("cpsid i" :: "" (basepri_) : ); \
__asm volatile ("msr BASEPRI,%0" :: "r" (QF_BASEPRI) : ); \
__asm volatile ("cpsie i" :: "" (basepri_) : ); \
} while (0)
#define QF_INT_ENABLE() QF_SET_BASEPRI(0U)
// QF critical section entry/exit (save and restore interrupt status)
#define QF_CRIT_STAT_TYPE unsigned long
#define QF_CRIT_ENTRY(basepri_) do {\
QF_GET_BASEPRI((basepri_)); \
QF_INT_DISABLE(); \
} while (0)
#define QF_CRIT_EXIT(basepri_) QF_SET_BASEPRI((basepri_))
#define QF_CRIT_EXIT(basepri_) \
__asm volatile ("msr BASEPRI,%0" :: "r" (basepri_) : )
// BASEPRI threshold for "QF-aware" interrupts, see NOTE3.
// CAUTION: keep in synch with the value defined in "qk_port.s"
//
#define QF_BASEPRI (0xFFU >> 2)
/* CMSIS threshold for "QF-aware" interrupts, see NOTE4 */
/* CMSIS threshold for "QF-aware" interrupts, see NOTE5 */
#define QF_AWARE_ISR_CMSIS_PRI (QF_BASEPRI >> (8 - __NVIC_PRIO_BITS))
// Cortex-M3/M4/M7 provide the CLZ instruction for fast LOG2
#define QF_LOG2(n_) (static_cast<uint_fast8_t>(32U - __builtin_clz(n_)))
// macro for getting the BASEPRI register
#define QF_GET_BASEPRI(basepri_) __asm volatile (\
"mrs %0,BASEPRI" : "=r" (basepri_) :: )
// macro for setting the BASEPRI register
#define QF_SET_BASEPRI(basepri_) __asm volatile (\
"msr BASEPRI,%0" :: "r" (basepri_) : )
#endif
#define QF_CRIT_EXIT_NOP() __asm volatile ("isb")
@ -143,13 +125,6 @@
// ("QF-aware" interrupts ), can call QF services.
//
// NOTE4:
// The selective disabling of "QF-aware" interrupts with the BASEPRI register
// has a problem on ARM Cortex-M7 core r0p1 (see ARM-EPM-064408, errata
// 837070). The workaround recommended by ARM is to surround MSR BASEPRI with
// the CPSID i/CPSIE i pair, which is implemented in the QF_INT_DISABLE()
// macro. This workaround works also for Cortex-M3/M4 cores.
//
// NOTE5:
// The QF_AWARE_ISR_CMSIS_PRI macro is useful as an offset for enumerating
// the "QF-aware" interrupt priorities in the applications, whereas the
// numerical values of the "QF-aware" interrupts must be greater or equal to
@ -162,5 +137,21 @@
// remains generic and not dependent on the number of implemented priority bits
// implemented in the NVIC.
//
// NOTE5:
// The selective disabling of "QF-aware" interrupts with the BASEPRI register
// has a problem on ARM Cortex-M7 core r0p1 (see ARM-EPM-064408, errata
// 837070). The workaround recommended by ARM is to surround MSR BASEPRI with
// the CPSID i/CPSIE i pair, which is implemented in the QF_INT_DISABLE()
// macro. This workaround works also for Cortex-M3/M4 cores.
//
// NOTE6:
// The critical section for Cortex-M0(+)/M1 (ARMv6-M architecture) is
// specifically defined to avoid the GNU-ARM bug 1722849 (see
// https://bugs.launchpad.net/gcc-arm-embedded/+bug/1722849 ). Specifically,
// the in-line assembly for disabling of interrupts with "cpsid i" has an
// artificial dependency on the (primask_) argument. This ensures that
// the GNU-ARM compiler will evaluate the preceeding "mrs %0,PRIMASK"
// __before__ evaluating "cpsid i".
//
#endif // qf_port_h

View File

@ -1,7 +1,7 @@
/*****************************************************************************
* Product: QXK port to ARM Cortex-M (M0,M0+,M3,M4,M7), GNU-ARM assembler
* Last Updated for Version: 5.9.6
* Date of the Last Update: 2017-07-28
* Last Updated for Version: 6.0.0
* Date of the Last Update: 2017-10-10
*
* Q u a n t u m L e a P s
* ---------------------------
@ -225,8 +225,8 @@ PendSV_activate:
SUBS r2,r2,#1 /* align Thumb-address at halfword (new pc) */
LDR r1,=Thread_ret /* return address after the call (new lr) */
SUB sp,sp,#8*4 /* reserve space for exception stack frame */
ADD r0,sp,#5*4 /* r0 := 5 registers below the top of stack */
SUB sp,sp,#(8*4) /* reserve space for exception stack frame */
ADD r0,sp,#(5*4) /* r0 := 5 registers below the top of stack */
STM r0!,{r1-r3} /* save xpsr,pc,lr */
MOVS r0,#6
@ -249,12 +249,15 @@ PendSV_error:
*/
PendSV_save_ao:
.if __ARM_ARCH == 6 /* Cortex-M0/M0+/M1 (v6-M, v6S-M)? */
PUSH {r4-r7} /* save the low registers */
SUB sp,sp,#(8*4) /* make room for 8 registers r4-r11 */
MOV r0,sp /* r0 := temporary stack pointer */
STMIA r0!,{r4-r7} /* save the low registers */
MOV r4,r8 /* move the high registers to low registers...*/
MOV r5,r9
MOV r6,r10
MOV r7,r11
PUSH {r4-r7} /* save the high registers */
STMIA r0!,{r4-r7} /* save the high registers */
MOV r0,r12 /* restore QXK_attr_.next in r0 */
.else /* M3/M4/M7 */
PUSH {r4-r11} /* save r4-r11 on top of the exception frame */
.ifdef __FPU_PRESENT /* if VFP available... */
@ -285,7 +288,7 @@ PendSV_restore_ao:
.if __ARM_ARCH == 6 /* Cortex-M0/M0+/M1 (v6-M, v6S-M)? */
MOV r0,sp /* r0 := top of stack */
MOV r1,r0
ADDS r1,r1,#(4*4) /* point r0 to the 4 high registers r7-r11 */
ADDS r1,r1,#(4*4) /* point r1 to the 4 high registers r7-r11 */
LDMIA r1!,{r4-r7} /* pop the 4 high registers into low registers*/
MOV r8,r4 /* move low registers into high registers */
MOV r9,r5
@ -531,9 +534,9 @@ QXK_stackInit_:
LSLS r3,r0,#3
/* make room for the thread's stack frame... */
SUBS r3,r3,#16*4 /* r3 := top of the 16-register stack frame */
SUBS r3,r3,#(16*4) /* r3 := top of the 16-register stack frame */
.ifdef __FPU_PRESENT /* if VFP available... */
SUBS r3,r3,#2*4 /* r3 := top of the 18-register stack frame */
SUBS r3,r3,#(2*4) /* r3 := top of the 18-register stack frame */
.endif /* VFP available */
/* pre-fill the unused part of the stack with 0xDEADBEEF................*/

View File

@ -1,7 +1,7 @@
;*****************************************************************************
; Product: QXK port to ARM Cortex-M (M0,M0+,M3,M4,M7), IAR-ARM assembler
; Last Updated for Version: 5.9.6
; Date of the Last Update: 2017-07-28
; Last Updated for Version: 6.0.0
; Date of the Last Update: 2017-10-10
;
; Q u a n t u m L e a P s
; ---------------------------
@ -224,8 +224,8 @@ PendSV_activate:
SUBS r2,r2,#1 ; align Thumb-address at halfword (new pc)
LDR r1,=Thread_ret ; return address after the call (new lr)
SUB sp,sp,#8*4 ; reserve space for exception stack frame
ADD r0,sp,#5*4 ; r0 := 5 registers below the top of stack
SUB sp,sp,#(8*4) ; reserve space for exception stack frame
ADD r0,sp,#(5*4) ; r0 := 5 registers below the top of stack
STM r0!,{r1-r3} ; save xpsr,pc,lr
MOVS r0,#6
@ -247,12 +247,15 @@ PendSV_error:
; r12 -> QXK_attr_.next
PendSV_save_ao:
#if (__CORE__ == __ARM6M__) ; Cortex-M0/M0+/M1 ?
PUSH {r4-r7} ; save the low registers
SUB sp,sp,#(8*4) ; make room for 8 registers r4-r11
MOV r0,sp ; r0 := temporary stack pointer
STMIA r0!,{r4-r7} ; save the low registers
MOV r4,r8 ; move the high registers to low registers...
MOV r5,r9
MOV r6,r10
MOV r7,r11
PUSH {r4-r7} ; save the high registers
STMIA r0!,{r4-r7} ; save the high registers
MOV r0,r12 ; restore QXK_attr_.next in r0
#else ; M3/M4/M7
PUSH {r4-r11} ; save r4-r11 on top of the exception frame
#ifdef __ARMVFP__ ; if VFP available...
@ -282,7 +285,7 @@ PendSV_restore_ao:
#if (__CORE__ == __ARM6M__) ; Cortex-M0/M0+/M1 ?
MOV r0,sp ; r0 := top of stack
MOV r1,r0
ADDS r1,r1,#(4*4) ; point r0 to the 4 high registers r7-r11
ADDS r1,r1,#(4*4) ; point r1 to the 4 high registers r7-r11
LDMIA r1!,{r4-r7} ; pop the 4 high registers into low registers
MOV r8,r4 ; move low registers into high registers
MOV r9,r5
@ -509,9 +512,9 @@ QXK_stackInit_:
LSLS r3,r0,#3
; make room for the thread's stack frame...
SUBS r3,r3,#16*4 ; r3 := top of the 16-register stack frame
SUBS r3,r3,#(16*4) ; r3 := top of the 16-register stack frame
#ifdef __ARMVFP__ ; if VFP available...
SUBS r3,r3,#2*4 ; r3 := top of the 18-register stack frame
SUBS r3,r3,#(2*4) ; r3 := top of the 18-register stack frame
#endif ; VFP available
; pre-fill the unused part of the stack with 0xDEADBEEF...................

View File

@ -1,7 +1,7 @@
;*****************************************************************************
; Product: QXK port to ARM Cortex-M (M0,M0+,M3,M4,M7), TI-ARM assembler
; Last Updated for Version: 5.9.6
; Date of the Last Update: 2017-07-28
; Last Updated for Version: 6.0.0
; Date of the Last Update: 2017-10-10
;
; Q u a n t u m L e a P s
; ---------------------------
@ -229,8 +229,8 @@ PendSV_activate:
SUBS r2,r2,#1 ; align Thumb-address at halfword (new pc)
LDR r1,Thread_ret_addr ; return address after the call (new lr)
SUB sp,sp,#8*4 ; reserve space for exception stack frame
ADD r0,sp,#5*4 ; r0 := 5 registers below the top of stack
SUB sp,sp,#(8*4) ; reserve space for exception stack frame
ADD r0,sp,#(5*4) ; r0 := 5 registers below the top of stack
STM r0!,{r1-r3} ; save xpsr,pc,lr
MOVS r0,#6
@ -261,12 +261,15 @@ PendSV_save_ao:
PUSH {r0,lr} ; save the "aligner" and the EXC_RETURN value
.endif ; VFP available
.else ; M0/M0+
PUSH {r4-r7} ; save the low registers
SUB sp,sp,#(8*4) ; make room for 8 registers r4-r11
MOV r0,sp ; r0 := temporary stack pointer
STMIA r0!,{r4-r7} ; save the low registers
MOV r4,r8 ; move the high registers to low registers...
MOV r5,r9
MOV r6,r10
MOV r7,r11
PUSH {r4-r7} ; save the high registers
STMIA r0!,{r4-r7} ; save the high registers
MOV r0,r12 ; restore QXK_attr_.next in r0
.endif ; M0/M0+
CMP r2,#0
@ -297,7 +300,7 @@ PendSV_restore_ao:
.else ; M0/M0+
MOV r0,sp ; r0 := top of stack
MOV r1,r0
ADDS r1,r1,#(4*4) ; point r0 to the 4 high registers r7-r11
ADDS r1,r1,#(4*4) ; point r1 to the 4 high registers r7-r11
LDMIA r1!,{r4-r7} ; pop the 4 high registers into low registers
MOV r8,r4 ; move low registers into high registers
MOV r9,r5
@ -517,9 +520,9 @@ QXK_stackInit_: .asmfunc
LSLS r3,r0,#3
; make room for the thread's stack frame...
SUBS r3,r3,#16*4 ; r3 := top of the 16-register stack frame
SUBS r3,r3,#(16*4) ; r3 := top of the 16-register stack frame
.if __TI_VFP_SUPPORT__ ; if VFP available...
SUBS r3,r3,#2*4 ; r3 := top of the 18-register stack frame
SUBS r3,r3,#(2*4) ; r3 := top of the 18-register stack frame
.endif ; VFP available
; pre-fill the unused part of the stack with 0xDEADBEEF...................

View File

@ -1,4 +0,0 @@
This 'qpcpp/source' directory is provided only for backwards compatibility
with the existing QP/C++ projects. This directory will be phased out in the
future QP/C++ releases. Please use the new source code structure provided
in the 'qpcpp/src' directory.

View File

@ -1,611 +0,0 @@
/// @file
/// @brief QP::QHsm implementation
/// @ingroup qep
/// @cond
///***************************************************************************
/// Last updated for version 5.9.0
/// Last updated on 2017-05-08
///
/// Q u a n t u m L e a P s
/// ---------------------------
/// innovating embedded systems
///
/// Copyright (C) Quantum Leaps, LLC. All rights reserved.
///
/// This program is open source software: you can redistribute it and/or
/// modify it under the terms of the GNU General Public License as published
/// by the Free Software Foundation, either version 3 of the License, or
/// (at your option) any later version.
///
/// Alternatively, this program may be distributed and modified under the
/// terms of Quantum Leaps commercial licenses, which expressly supersede
/// the GNU General Public License and are specifically designed for
/// licensees interested in retaining the proprietary status of their code.
///
/// This program is distributed in the hope that it will be useful,
/// but WITHOUT ANY WARRANTY; without even the implied warranty of
/// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
/// GNU General Public License for more details.
///
/// You should have received a copy of the GNU General Public License
/// along with this program. If not, see <http://www.gnu.org/licenses/>.
///
/// Contact information:
/// https://state-machine.com
/// mailto:info@state-machine.com
///***************************************************************************
/// @endcond
#define QP_IMPL // this is QP implementation
#include "qep_port.h" // QEP port
#ifdef Q_SPY // QS software tracing enabled?
#include "qs_port.h" // include QS port
#else
#include "qs_dummy.h" // disable the QS software tracing
#endif // Q_SPY
#include "qassert.h" // QP embedded systems-friendly assertions
//! helper macro to trigger internal event in an HSM
#define QEP_TRIG_(state_, sig_) \
((*(state_))(this, &QEP_reservedEvt_[sig_]))
//! helper macro to trigger exit action in an HSM
#define QEP_EXIT_(state_) do { \
if (QEP_TRIG_(state_, Q_EXIT_SIG) == Q_RET_HANDLED) { \
QS_BEGIN_(QS_QEP_STATE_EXIT, QS::priv_.locFilter[QS::SM_OBJ], this) \
QS_OBJ_(this); \
QS_FUN_(state_); \
QS_END_() \
} \
} while (false)
//! helper macro to trigger entry action in an HSM
#define QEP_ENTER_(state_) do { \
if (QEP_TRIG_(state_, Q_ENTRY_SIG) == Q_RET_HANDLED) { \
QS_BEGIN_(QS_QEP_STATE_ENTRY, QS::priv_.locFilter[QS::SM_OBJ], this) \
QS_OBJ_(this); \
QS_FUN_(state_); \
QS_END_() \
} \
} while (false)
namespace QP {
Q_DEFINE_THIS_MODULE("qep_hsm")
//****************************************************************************
enum {
QEP_EMPTY_SIG_ = 0 //!< empty signal for internal use only
};
//***************************************************************************/
/// @description
/// Static, preallocated standard events that the QEP event processor sends
/// to state handler functions of QP::QHsm and QP::QFsm subclasses to execute
/// entry actions, exit actions, and initial transitions.
///
static QEvt const QEP_reservedEvt_[4] = {
#ifdef Q_EVT_CTOR // Is the QEvt constructor provided?
static_cast<QSignal>(0),
static_cast<QSignal>(1),
static_cast<QSignal>(2),
static_cast<QSignal>(3)
#else // QEvt is a POD (Plain Old Datatype)
{ static_cast<QSignal>(0),
static_cast<uint8_t>(0), static_cast<uint8_t>(0) },
{ static_cast<QSignal>(1),
static_cast<uint8_t>(0), static_cast<uint8_t>(0) },
{ static_cast<QSignal>(2),
static_cast<uint8_t>(0), static_cast<uint8_t>(0) },
{ static_cast<QSignal>(3),
static_cast<uint8_t>(0), static_cast<uint8_t>(0) }
#endif
};
//****************************************************************************
/// @description
/// Performs the first step of HSM initialization by assigning the initial
/// pseudostate to the currently active state of the state machine.
///
/// @param[in] initial pointer to the top-most initial state-handler
/// function in the derived state machine
///
QHsm::QHsm(QStateHandler const initial) {
m_state.fun = Q_STATE_CAST(&top);
m_temp.fun = initial;
}
//****************************************************************************
/// @description
/// Virtual destructor of the QHsm state machine and any of its subclasses.
///
QHsm::~QHsm() {
}
//****************************************************************************
/// @description
/// Executes the top-most initial transition in a HSM.
///
/// @param[in] e pointer to the initialization event (might be NULL)
///
/// @note Must be called exactly __once__ before the QP::QHsm::dispatch().
///
void QHsm::init(QEvt const * const e) {
QStateHandler t = m_state.fun;
/// @pre ctor must have been executed and initial tran NOT taken
Q_REQUIRE_ID(200, (m_temp.fun != Q_STATE_CAST(0))
&& (t == Q_STATE_CAST(&QHsm::top)));
// execute the top-most initial transition
QState r = (*m_temp.fun)(this, e);
// the top-most initial transition must be taken
Q_ASSERT_ID(210, r == Q_RET_TRAN);
QS_CRIT_STAT_
QS_BEGIN_(QS_QEP_STATE_INIT, QS::priv_.locFilter[QS::SM_OBJ], this)
QS_OBJ_(this); // this state machine object
QS_FUN_(t); // the source state
QS_FUN_(m_temp.fun); // the target of the initial transition
QS_END_()
// drill down into the state hierarchy with initial transitions...
do {
QStateHandler path[MAX_NEST_DEPTH_]; // tran entry path array
int_fast8_t ip = static_cast<int_fast8_t>(0); // tran entry path index
path[0] = m_temp.fun;
(void)QEP_TRIG_(m_temp.fun, QEP_EMPTY_SIG_);
while (m_temp.fun != t) {
++ip;
Q_ASSERT_ID(220, ip < static_cast<int_fast8_t>(Q_DIM(path)));
path[ip] = m_temp.fun;
(void)QEP_TRIG_(m_temp.fun, QEP_EMPTY_SIG_);
}
m_temp.fun = path[0];
// retrace the entry path in reverse (desired) order...
do {
QEP_ENTER_(path[ip]); // enter path[ip]
--ip;
} while (ip >= static_cast<int_fast8_t>(0));
t = path[0]; // current state becomes the new source
r = QEP_TRIG_(t, Q_INIT_SIG); // execute initial transition
#ifdef Q_SPY
if (r == Q_RET_TRAN) {
QS_BEGIN_(QS_QEP_STATE_INIT,
QS::priv_.locFilter[QS::SM_OBJ], this)
QS_OBJ_(this); // this state machine object
QS_FUN_(t); // the source state
QS_FUN_(m_temp.fun); // the target of the initial transition
QS_END_()
}
#endif /* Q_SPY */
} while (r == Q_RET_TRAN);
QS_BEGIN_(QS_QEP_INIT_TRAN, QS::priv_.locFilter[QS::SM_OBJ], this)
QS_TIME_(); // time stamp
QS_OBJ_(this); // this state machine object
QS_FUN_(t); // the new active state
QS_END_()
m_state.fun = t; // change the current active state
m_temp.fun = t; // mark the configuration as stable
}
//***************************************************************************/
/// @description
/// The QP::QHsm::top() state handler is the ultimate root of state hierarchy
/// in all HSMs derived from QP::QHsm.
///
/// @param[in] e pointer to the event to be dispatched to the HSM
///
/// @returns Always returns #Q_RET_IGNORED, which means that the top state
/// ignores all events.
///
/// @note
/// The arguments to this state handler are not used. They are provided for
/// conformance with the state-handler function signature QP::QStateHandler.
///
QState QHsm::top(void * const, QEvt const * const) {
return Q_RET_IGNORED; // the top state ignores all events
}
//****************************************************************************
/// @description
/// Dispatches an event for processing to a hierarchical state machine (HSM).
/// The processing of an event represents one run-to-completion (RTC) step.
///
/// @param[in] e pointer to the event to be dispatched to the HSM
///
/// @note
/// This state machine must be initialized by calling QP::QHsm::init() exactly
/// __once__ before calling QP::QHsm::dispatch().
///
void QHsm::dispatch(QEvt const * const e) {
QStateHandler t = m_state.fun;
QStateHandler s;
QState r;
QS_CRIT_STAT_
/// @pre the current state must be initialized and
/// the state configuration must be stable
Q_REQUIRE_ID(400, (t != Q_STATE_CAST(0))
&& (t == m_temp.fun));
QS_BEGIN_(QS_QEP_DISPATCH, QS::priv_.locFilter[QS::SM_OBJ], this)
QS_TIME_(); // time stamp
QS_SIG_(e->sig); // the signal of the event
QS_OBJ_(this); // this state machine object
QS_FUN_(t); // the current state
QS_END_()
// process the event hierarchically...
do {
s = m_temp.fun;
r = (*s)(this, e); // invoke state handler s
if (r == Q_RET_UNHANDLED) { // unhandled due to a guard?
QS_BEGIN_(QS_QEP_UNHANDLED, QS::priv_.locFilter[QS::SM_OBJ], this)
QS_SIG_(e->sig); // the signal of the event
QS_OBJ_(this); // this state machine object
QS_FUN_(s); // the current state
QS_END_()
r = QEP_TRIG_(s, QEP_EMPTY_SIG_); // find superstate of s
}
} while (r == Q_RET_SUPER);
// transition taken?
if (r >= Q_RET_TRAN) {
QStateHandler path[MAX_NEST_DEPTH_];
path[0] = m_temp.fun; // save the target of the transition
path[1] = t;
path[2] = s;
// exit current state to transition source s...
for (; t != s; t = m_temp.fun) {
// exit handled?
if (QEP_TRIG_(t, Q_EXIT_SIG) == Q_RET_HANDLED) {
QS_BEGIN_(QS_QEP_STATE_EXIT,
QS::priv_.locFilter[QS::SM_OBJ], this)
QS_OBJ_(this); // this state machine object
QS_FUN_(t); // the exited state
QS_END_()
(void)QEP_TRIG_(t, QEP_EMPTY_SIG_); // find superstate of t
}
}
int_fast8_t ip = hsm_tran(path); // take the HSM transition
#ifdef Q_SPY
if (r == Q_RET_TRAN_HIST) {
QS_BEGIN_(QS_QEP_TRAN_HIST, QS::priv_.locFilter[QS::SM_OBJ], this)
QS_OBJ_(this); // this state machine object
QS_FUN_(t); // the source of the transition
QS_FUN_(path[0]); // the target of the tran. to history
QS_END_()
}
#endif // Q_SPY
// retrace the entry path in reverse (desired) order...
for (; ip >= static_cast<int_fast8_t>(0); --ip) {
QEP_ENTER_(path[ip]); // enter path[ip]
}
t = path[0]; // stick the target into register
m_temp.fun = t; // update the next state
// drill into the target hierarchy...
while (QEP_TRIG_(t, Q_INIT_SIG) == Q_RET_TRAN) {
QS_BEGIN_(QS_QEP_STATE_INIT,
QS::priv_.locFilter[QS::SM_OBJ], this)
QS_OBJ_(this); // this state machine object
QS_FUN_(t); // the source (pseudo)state
QS_FUN_(m_temp.fun); // the target of the transition
QS_END_()
ip = static_cast<int_fast8_t>(0);
path[0] = m_temp.fun;
(void)QEP_TRIG_(m_temp.fun, QEP_EMPTY_SIG_); // find superstate
while (m_temp.fun != t) {
++ip;
path[ip] = m_temp.fun;
(void)QEP_TRIG_(m_temp.fun, QEP_EMPTY_SIG_);// find superstate
}
m_temp.fun = path[0];
// entry path must not overflow
Q_ASSERT_ID(410, ip < static_cast<int_fast8_t>(MAX_NEST_DEPTH_));
// retrace the entry path in reverse (correct) order...
do {
QEP_ENTER_(path[ip]); // enter path[ip]
--ip;
} while (ip >= static_cast<int_fast8_t>(0));
t = path[0];
}
QS_BEGIN_(QS_QEP_TRAN, QS::priv_.locFilter[QS::SM_OBJ], this)
QS_TIME_(); // time stamp
QS_SIG_(e->sig); // the signal of the event
QS_OBJ_(this); // this state machine object
QS_FUN_(s); // the source of the transition
QS_FUN_(t); // the new active state
QS_END_()
}
#ifdef Q_SPY
else if (r == Q_RET_HANDLED) {
QS_BEGIN_(QS_QEP_INTERN_TRAN, QS::priv_.locFilter[QS::SM_OBJ], this)
QS_TIME_(); // time stamp
QS_SIG_(e->sig); // the signal of the event
QS_OBJ_(this); // this state machine object
QS_FUN_(s); // the source state
QS_END_()
}
else {
QS_BEGIN_(QS_QEP_IGNORED, QS::priv_.locFilter[QS::SM_OBJ], this)
QS_TIME_(); // time stamp
QS_SIG_(e->sig); // the signal of the event
QS_OBJ_(this); // this state machine object
QS_FUN_(m_state.fun);// the current state
QS_END_()
}
#endif // Q_SPY
m_state.fun = t; // change the current active state
m_temp.fun = t; // mark the configuration as stable
}
//****************************************************************************
/// @description
/// helper function to execute transition sequence in a hierarchical state
/// machine (HSM).
///
/// @param[in,out] path array of pointers to state-handler functions
/// to execute the entry actions
///
/// @returns the depth of the entry path stored in the @p path parameter.
////
int_fast8_t QHsm::hsm_tran(QStateHandler (&path)[MAX_NEST_DEPTH_]) {
// transition entry path index
int_fast8_t ip = static_cast<int_fast8_t>(-1);
int_fast8_t iq; // helper transition entry path index
QStateHandler t = path[0];
QStateHandler s = path[2];
QState r;
QS_CRIT_STAT_
// (a) check source==target (transition to self)
if (s == t) {
QEP_EXIT_(s); // exit the source
ip = static_cast<int_fast8_t>(0); // cause entering the target
}
else {
(void)QEP_TRIG_(t, QEP_EMPTY_SIG_); // superstate of target
t = m_temp.fun;
// (b) check source==target->super
if (s == t) {
ip = static_cast<int_fast8_t>(0); // cause entering the target
}
else {
(void)QEP_TRIG_(s, QEP_EMPTY_SIG_); // superstate of src
// (c) check source->super==target->super
if (m_temp.fun == t) {
QEP_EXIT_(s); // exit the source
ip = static_cast<int_fast8_t>(0); // cause entering the target
}
else {
// (d) check source->super==target
if (m_temp.fun == path[0]) {
QEP_EXIT_(s); // exit the source
}
else {
// (e) check rest of source==target->super->super..
// and store the entry path along the way
// indicate that the LCA was not found
iq = static_cast<int_fast8_t>(0);
// enter target and its superstate
ip = static_cast<int_fast8_t>(1);
path[1] = t; // save the superstate of target
t = m_temp.fun; // save source->super
// find target->super->super
r = QEP_TRIG_(path[1], QEP_EMPTY_SIG_);
while (r == Q_RET_SUPER) {
++ip;
path[ip] = m_temp.fun; // store the entry path
if (m_temp.fun == s) { // is it the source?
// indicate that the LCA was found
iq = static_cast<int_fast8_t>(1);
// entry path must not overflow
Q_ASSERT_ID(510,
ip < static_cast<int_fast8_t>(MAX_NEST_DEPTH_));
--ip; // do not enter the source
r = Q_RET_HANDLED; // terminate the loop
}
// it is not the source, keep going up
else {
r = QEP_TRIG_(m_temp.fun, QEP_EMPTY_SIG_);
}
}
// LCA found yet?
if (iq == static_cast<int_fast8_t>(0)) {
// entry path must not overflow
Q_ASSERT_ID(520,
ip < static_cast<int_fast8_t>(MAX_NEST_DEPTH_));
QEP_EXIT_(s); // exit the source
// (f) check the rest of source->super
// == target->super->super...
//
iq = ip;
r = Q_RET_IGNORED; // indicate LCA NOT found
do {
// is this the LCA?
if (t == path[iq]) {
r = Q_RET_HANDLED; // indicate LCA found
// do not enter LCA
ip = static_cast<int_fast8_t>(
iq - static_cast<int_fast8_t>(1));
// cause termination of the loop
iq = static_cast<int_fast8_t>(-1);
}
else {
--iq; // try lower superstate of target
}
} while (iq >= static_cast<int_fast8_t>(0));
// LCA not found yet?
if (r != Q_RET_HANDLED) {
// (g) check each source->super->...
// for each target->super...
//
r = Q_RET_IGNORED; // keep looping
do {
// exit t unhandled?
if (QEP_TRIG_(t, Q_EXIT_SIG) == Q_RET_HANDLED)
{
QS_BEGIN_(QS_QEP_STATE_EXIT,
QS::priv_.locFilter[QS::SM_OBJ],
this)
QS_OBJ_(this);
QS_FUN_(t);
QS_END_()
(void)QEP_TRIG_(t, QEP_EMPTY_SIG_);
}
t = m_temp.fun; // set to super of t
iq = ip;
do {
// is this LCA?
if (t == path[iq]) {
// do not enter LCA
ip = static_cast<int_fast8_t>(
iq - static_cast<int_fast8_t>(1));
// break out of inner loop
iq = static_cast<int_fast8_t>(-1);
r = Q_RET_HANDLED; // break outer loop
}
else {
--iq;
}
} while (iq >= static_cast<int_fast8_t>(0));
} while (r != Q_RET_HANDLED);
}
}
}
}
}
}
return ip;
}
//****************************************************************************
/// @description
/// Tests if a state machine derived from QHsm is-in a given state.
///
/// @note
/// For a HSM, to "be in a state" means also to be in a superstate of
/// of the state.
///
/// @param[in] state pointer to the state-handler function to be tested
///
/// @returns 'true' if the HSM is in the @p state and 'false' otherwise
///
bool QHsm::isIn(QStateHandler const s) {
/// @pre state configuration must be stable
Q_REQUIRE_ID(600, m_temp.fun == m_state.fun);
bool inState = false; // assume that this HSM is not in 'state'
QState r;
// scan the state hierarchy bottom-up
do {
// do the states match?
if (m_temp.fun == s) {
inState = true; // match found, return TRUE
r = Q_RET_IGNORED; // cause breaking out of the loop
}
else {
r = QEP_TRIG_(m_temp.fun, QEP_EMPTY_SIG_);
}
} while (r != Q_RET_IGNORED); // QHsm::top() state not reached
m_temp.fun = m_state.fun; // restore the stable state configuration
return inState; // return the status
}
//****************************************************************************
///
/// @description
/// Finds the child state of the given @c parent, such that this child state
/// is an ancestor of the currently active state. The main purpose of this
/// function is to support **shallow history** transitions in state machines
/// derived from QHsm.
///
/// @param[in] parent pointer to the state-handler function
///
/// @returns the child of a given @c parent state, which is an ancestor of
/// the currently active state
///
/// @note this function is designed to be called during state transitions,
/// so it does not necessarily start in a stable state configuration.
/// However, the function establishes stable state configuration upon exit.
///
QStateHandler QHsm::childState(QStateHandler const parent) {
QStateHandler child = m_state.fun; // start with the current state
bool isConfirmed = false; // start with the child not confirmed
QState r;
// establish stable state configuration
m_temp.fun = m_state.fun;
do {
// is this the parent of the current child?
if (m_temp.fun == parent) {
isConfirmed = true; // child is confirmed
r = Q_RET_IGNORED; // cause breaking out of the loop
}
else {
child = m_temp.fun;
r = QEP_TRIG_(m_temp.fun, QEP_EMPTY_SIG_);
}
} while (r != Q_RET_IGNORED); // QHsm::top() state not reached
m_temp.fun = m_state.fun; // establish stable state configuration
/// @post the child must be confirmed
Q_ENSURE_ID(710, isConfirmed);
return child; // return the child
}
} // namespace QP

View File

@ -1,544 +0,0 @@
/// @file
/// @brief QP::QMsm implementation
/// @ingroup qep
/// @cond
///***************************************************************************
/// Last updated for version 5.9.1
/// Last updated on 2017-05-24
///
/// Q u a n t u m L e a P s
/// ---------------------------
/// innovating embedded systems
///
/// Copyright (C) Quantum Leaps. All rights reserved.
///
/// This program is open source software: you can redistribute it and/or
/// modify it under the terms of the GNU General Public License as published
/// by the Free Software Foundation, either version 3 of the License, or
/// (at your option) any later version.
///
/// Alternatively, this program may be distributed and modified under the
/// terms of Quantum Leaps commercial licenses, which expressly supersede
/// the GNU General Public License and are specifically designed for
/// licensees interested in retaining the proprietary status of their code.
///
/// This program is distributed in the hope that it will be useful,
/// but WITHOUT ANY WARRANTY; without even the implied warranty of
/// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
/// GNU General Public License for more details.
///
/// You should have received a copy of the GNU General Public License
/// along with this program. If not, see <http://www.gnu.org/licenses/>.
///
/// Contact information:
/// https://state-machine.com
/// mailto:info@state-machine.com
///***************************************************************************
/// @endcond
#define QP_IMPL // this is QP implementation
#include "qep_port.h" // QEP port
#ifdef Q_SPY // QS software tracing enabled?
#include "qs_port.h" // include QS port
#else
#include "qs_dummy.h" // disable the QS software tracing
#endif // Q_SPY
#include "qassert.h" // QP embedded systems-friendly assertions
//! Internal macro to increment the given action table @p act_
/// @note Incrementing a pointer violates the MISRA-C 2004 Rule 17.4(req),
/// pointer arithmetic other than array indexing. Encapsulating this violation
/// in a macro allows to selectively suppress this specific deviation.
#define QEP_ACT_PTR_INC_(act_) (++(act_))
namespace QP {
Q_DEFINE_THIS_MODULE("qep_msm")
//****************************************************************************
char_t const versionStr[6] = QP_VERSION_STR;
//****************************************************************************
QMState const QMsm::msm_top_s = {
static_cast<QMState const *>(0),
Q_STATE_CAST(0),
Q_ACTION_CAST(0),
Q_ACTION_CAST(0),
Q_ACTION_CAST(0)
};
//****************************************************************************
/// @description
/// Performs the first step of initialization by assigning the initial
/// pseudostate to the currently active state of the state machine.
///
/// @param[in] initial the top-most initial transition for the MSM.
///
/// @note The constructor is protected to prevent direct instantiating
/// of the QP::QMsm objects. This class is intended for subclassing only.
///
/// @sa The QP::QMsm example illustrates how to use the QMsm constructor
/// in the constructor initializer list of the derived state machines.
///
QMsm::QMsm(QStateHandler const initial)
: QHsm(initial)
{
m_state.obj = &msm_top_s;
m_temp.fun = initial;
}
//****************************************************************************
/// @description
/// Executes the top-most initial transition in a MSM.
///
/// @param[in] e a constant pointer to QP::QEvt or a subclass of QP::QEvt
///
/// @attention
/// QP::QMsm::init() must be called exactly __once__ before
/// QP::QMsm::dispatch()
///
void QMsm::init(QEvt const * const e) {
QS_CRIT_STAT_
/// @pre the top-most initial transition must be initialized, and the
/// initial transition must not be taken yet.
Q_REQUIRE_ID(200, (m_temp.fun != Q_STATE_CAST(0))
&& (m_state.obj == &msm_top_s));
QState r = (*m_temp.fun)(this, e); // execute the top-most initial tran.
// initial tran. must be taken
Q_ASSERT_ID(210, r == Q_RET_TRAN_INIT);
QS_BEGIN_(QS_QEP_STATE_INIT, QS::priv_.locFilter[QS::SM_OBJ], this)
QS_OBJ_(this); // this state machine object
QS_FUN_(m_state.obj->stateHandler); // source state handler
QS_FUN_(m_temp.tatbl->target->stateHandler); // target state handler
QS_END_()
// set state to the last tran. target
m_state.obj = m_temp.tatbl->target;
// drill down into the state hierarchy with initial transitions...
do {
r = execTatbl_(m_temp.tatbl); // execute the transition-action table
} while (r >= Q_RET_TRAN_INIT);
QS_BEGIN_(QS_QEP_INIT_TRAN, QS::priv_.locFilter[QS::SM_OBJ], this)
QS_TIME_(); // time stamp
QS_OBJ_(this); // this state machine object
QS_FUN_(m_state.obj->stateHandler); // the new current state
QS_END_()
}
//****************************************************************************
/// @description
/// Dispatches an event for processing to a meta state machine (MSM).
/// The processing of an event represents one run-to-completion (RTC) step.
///
/// @param[in] e pointer to the event to be dispatched to the MSM
///
/// @note Must be called after QP::QMsm::init().
///
void QMsm::dispatch(QEvt const * const e) {
QMState const *s = m_state.obj; // store the current state
QMState const *t = s;
QState r = Q_RET_SUPER;
QS_CRIT_STAT_
/// @pre current state must be initialized
Q_REQUIRE_ID(300, s != static_cast<QMState const *>(0));
QS_BEGIN_(QS_QEP_DISPATCH, QS::priv_.locFilter[QS::SM_OBJ], this)
QS_TIME_(); // time stamp
QS_SIG_(e->sig); // the signal of the event
QS_OBJ_(this); // this state machine object
QS_FUN_(s->stateHandler); // the current state handler
QS_END_()
// scan the state hierarchy up to the top state...
do {
r = (*t->stateHandler)(this, e); // call state handler function
// event handled? (the most frequent case)
if (r >= Q_RET_HANDLED) {
break; // done scanning the state hierarchy
}
// event unhandled and passed to the superstate?
else if (r == Q_RET_SUPER) {
t = t->superstate; // advance to the superstate
}
// event unhandled and passed to a submachine superstate?
else if (r == Q_RET_SUPER_SUB) {
t = m_temp.obj; // current host state of the submachie
}
// event unhandled due to a guard?
else if (r == Q_RET_UNHANDLED) {
QS_BEGIN_(QS_QEP_UNHANDLED, QS::priv_.locFilter[QS::SM_OBJ], this)
QS_SIG_(e->sig); // the signal of the event
QS_OBJ_(this); // this state machine object
QS_FUN_(t->stateHandler); // the current state
QS_END_()
t = t->superstate; // advance to the superstate
}
else {
// no other return value should be produced
Q_ERROR_ID(310);
}
} while (t != static_cast<QMState const *>(0));
// any kind of transition taken?
if (r >= Q_RET_TRAN) {
#ifdef Q_SPY
QMState const *ts = t; // transition source for QS tracing
// the transition source state must not be NULL
Q_ASSERT_ID(320, ts != static_cast<QMState const *>(0));
#endif // Q_SPY
do {
// save the transition-action table before it gets clobbered
QMTranActTable const *tatbl = m_temp.tatbl;
QHsmAttr tmp; // temporary to save intermediate values
// was a regular state transition segment taken?
if (r == Q_RET_TRAN) {
exitToTranSource_(s, t);
r = execTatbl_(tatbl);
}
// was an initial transition segment taken?
else if (r == Q_RET_TRAN_INIT) {
r = execTatbl_(tatbl);
}
// was a transition segment to history taken?
else if (r == Q_RET_TRAN_HIST) {
tmp.obj = m_state.obj; // save history
m_state.obj = s; // restore the original state
exitToTranSource_(s, t);
(void)execTatbl_(tatbl);
r = enterHistory_(tmp.obj);
}
// was a transition segment to an entry point taken?
else if (r == Q_RET_TRAN_EP) {
r = execTatbl_(tatbl);
}
// was a transition segment to an exit point taken?
else if (r == Q_RET_TRAN_XP) {
QActionHandler const act = m_state.act; // save XP action
m_state.obj = s; // restore the original state
r = (*act)(this); // execute the XP action
if (r == Q_RET_TRAN) {
exitToTranSource_(s, t);
tmp.tatbl = m_temp.tatbl; // save XP-Segment
// take the tran-to-XP segment inside submachine
(void)execTatbl_(tatbl);
m_state.obj = s; // restore original state
#ifdef Q_SPY
t = m_temp.tatbl->target; // store for tracing
#endif /* Q_SPY */
// take the XP-Segment from submachine-state
r = execTatbl_(tmp.tatbl);
QS_BEGIN_(QS_QEP_TRAN_XP,
QS::priv_.locFilter[QS::SM_OBJ], this)
QS_OBJ_(this); // this state machine object
QS_FUN_(s); // source handler
QS_FUN_(t); // target handler
QS_END_()
}
}
else {
// no other return value should be produced
Q_ERROR_ID(330);
}
s = m_state.obj;
t = s;
} while (r >= Q_RET_TRAN);
QS_BEGIN_(QS_QEP_TRAN, QS::priv_.locFilter[QS::SM_OBJ], this)
QS_TIME_(); // time stamp
QS_SIG_(e->sig); // the signal of the event
QS_OBJ_(this); // this state machine object
QS_FUN_(ts->stateHandler); // the transition source
QS_FUN_(s->stateHandler); // the new active state
QS_END_()
}
#ifdef Q_SPY
// was the event handled?
else if (r == Q_RET_HANDLED) {
// internal tran. source can't be NULL
Q_ASSERT_ID(340, t != static_cast<QMState const *>(0));
QS_BEGIN_(QS_QEP_INTERN_TRAN, QS::priv_.locFilter[QS::SM_OBJ], this)
QS_TIME_(); // time stamp
QS_SIG_(e->sig); // the signal of the event
QS_OBJ_(this); // this state machine object
QS_FUN_(t->stateHandler); // the source state
QS_END_()
}
// event bubbled to the 'top' state?
else if (t == static_cast<QMState const *>(0)) {
QS_BEGIN_(QS_QEP_IGNORED, QS::priv_.locFilter[QS::SM_OBJ], this)
QS_TIME_(); // time stamp
QS_SIG_(e->sig); // the signal of the event
QS_OBJ_(this); // this state machine object
QS_FUN_(s->stateHandler); // the current state
QS_END_()
}
#endif // Q_SPY
else {
// empty
}
}
//****************************************************************************
/// @description
/// Helper function to execute transition sequence in a tran-action table.
///
/// @param[in] tatbl pointer to the transition-action table
///
/// @returns status of the last action from the transition-action table.
///
/// @note
/// This function is for internal use inside the QEP event processor and
/// should __not__ be called directly from the applications.
///
QState QMsm::execTatbl_(QMTranActTable const * const tatbl) {
QActionHandler const *a;
QState r = Q_RET_NULL;
QS_CRIT_STAT_
/// @pre the transition-action table pointer must not be NULL
Q_REQUIRE_ID(400, tatbl != static_cast<QMTranActTable const *>(0));
for (a = &tatbl->act[0]; *a != Q_ACTION_CAST(0); QEP_ACT_PTR_INC_(a)) {
r = (*(*a))(this); // call the action through the 'a' pointer
#ifdef Q_SPY
if (r == Q_RET_ENTRY) {
QS_BEGIN_(QS_QEP_STATE_ENTRY,
QS::priv_.locFilter[QS::SM_OBJ], this)
QS_OBJ_(this); // this state machine object
QS_FUN_(m_temp.obj->stateHandler); // entered state handler
QS_END_()
}
else if (r == Q_RET_EXIT) {
QS_BEGIN_(QS_QEP_STATE_EXIT,
QS::priv_.locFilter[QS::SM_OBJ], this)
QS_OBJ_(this); // this state machine object
QS_FUN_(m_temp.obj->stateHandler); // exited state handler
QS_END_()
}
else if (r == Q_RET_TRAN_INIT) {
QS_BEGIN_(QS_QEP_STATE_INIT,
QS::priv_.locFilter[QS::SM_OBJ], this)
QS_OBJ_(this); // this state machine object
QS_FUN_(tatbl->target->stateHandler); // source
QS_FUN_(m_temp.tatbl->target->stateHandler); // target
QS_END_()
}
else if (r == Q_RET_TRAN_EP) {
QS_BEGIN_(QS_QEP_TRAN_EP, QS::priv_.locFilter[QS::SM_OBJ], this)
QS_OBJ_(this); // this state machine object
QS_FUN_(tatbl->target->stateHandler); // source
QS_FUN_(m_temp.tatbl->target->stateHandler); // target
QS_END_()
}
else if (r == Q_RET_TRAN_XP) {
QS_BEGIN_(QS_QEP_TRAN_XP, QS::priv_.locFilter[QS::SM_OBJ], this)
QS_OBJ_(this); // this state machine object
QS_FUN_(tatbl->target->stateHandler); // source
QS_FUN_(m_temp.tatbl->target->stateHandler); // target
QS_END_()
}
else {
// empty
}
#endif // Q_SPY
}
if (r >= Q_RET_TRAN_INIT) {
m_state.obj = m_temp.tatbl->target; // the tran. target
}
else {
m_state.obj = tatbl->target; // the tran. target
}
return r;
}
//****************************************************************************
/// @description
/// Helper function to exit the current state configuration to the
/// transition source, which is a hierarchical state machine might be a
/// superstate of the current state.
///
/// @param[in] s pointer to the current state
/// @param[in] ts pointer to the transition source state
///
void QMsm::exitToTranSource_(QMState const *s,
QMState const * const ts)
{
// exit states from the current state to the tran. source state
while (s != ts) {
// exit action provided in state 's'?
if (s->exitAction != Q_ACTION_CAST(0)) {
(void)(*s->exitAction)(this); // execute the exit action
QS_CRIT_STAT_
QS_BEGIN_(QS_QEP_STATE_EXIT,
QS::priv_.locFilter[QS::SM_OBJ], this)
QS_OBJ_(this); // this state machine object
QS_FUN_(s->stateHandler); // the exited state handler
QS_END_()
}
s = s->superstate; // advance to the superstate
// reached the top of a submachine?
if (s == static_cast<QMState const *>(0)) {
s = m_temp.obj; // the superstate from QM_SM_EXIT()
Q_ASSERT_ID(510, s != static_cast<QMState const *>(0));
}
}
}
//****************************************************************************
/// @description
/// Static helper function to execute the segment of transition to history
/// after entering the composite state and
///
/// @param[in] hist pointer to the history substate
///
/// @returns QP::Q_RET_INIT, if an initial transition has been executed in
/// the last entered state or QP::Q_RET_NULL if no such transition was taken.
///
QState QMsm::enterHistory_(QMState const * const hist) {
QMState const *s = hist;
QMState const *ts = m_state.obj; // transition source
QMState const *epath[MAX_ENTRY_DEPTH_];
QState r;
uint_fast8_t i = static_cast<uint_fast8_t>(0); // entry path index
QS_CRIT_STAT_
QS_BEGIN_(QS_QEP_TRAN_HIST, QS::priv_.locFilter[QS::SM_OBJ], this)
QS_OBJ_(this); // this state machine object
QS_FUN_(ts->stateHandler); // source state handler
QS_FUN_(hist->stateHandler); // target state handler
QS_END_()
while (s != ts) {
if (s->entryAction != static_cast<QActionHandler>(0)) {
epath[i] = s;
++i;
Q_ASSERT_ID(620, i <= static_cast<uint_fast8_t>(Q_DIM(epath)));
}
s = s->superstate;
if (s == 0) {
ts = s; /* force exit from the for-loop */
}
}
// retrace the entry path in reverse (desired) order...
while (i > static_cast<uint_fast8_t>(0)) {
--i;
r = (*epath[i]->entryAction)(this); // run entry action in epath[i]
QS_BEGIN_(QS_QEP_STATE_ENTRY, QS::priv_.locFilter[QS::SM_OBJ], this)
QS_OBJ_(this);
QS_FUN_(epath[i]->stateHandler); // entered state handler
QS_END_()
}
m_state.obj = hist; // set current state to the transition target
// initial tran. present?
if (hist->initAction != static_cast<QActionHandler>(0)) {
r = (*hist->initAction)(this); // execute the transition action
}
else {
r = Q_RET_NULL;
}
return r;
}
//****************************************************************************
/// \description
/// Tests if a state machine derived from QMsm is-in a given state.
///
/// \note For a MSM, to "be-in" a state means also to "be-in" a superstate of
/// of the state.
///
/// \arguments
/// \arg[in] \c st pointer to the QMState object that corresponds to the
/// tested state.
///
/// \returns 'true' if the MSM is in the \c st and 'false' otherwise
///
bool QMsm::isInState(QMState const * const st) const {
bool inState = false; // assume that this MSM is not in 'state'
for (QMState const *s = m_state.obj;
s != static_cast<QMState const *>(0);
s = s->superstate)
{
if (s == st) {
inState = true; // match found, return 'true'
break;
}
}
return inState;
}
//****************************************************************************
///
/// @description
/// Finds the child state of the given @c parent, such that this child state
/// is an ancestor of the currently active state. The main purpose of this
/// function is to support **shallow history** transitions in state machines
/// derived from QMsm.
///
/// @param[in] parent pointer to the state-handler object
///
/// @returns the child of a given @c parent state, which is an ancestor of
/// the currently active state
///
QMState const *QMsm::childStateObj(QMState const * const parent) const {
QMState const *child = m_state.obj;
bool isConfirmed = false; // start with the child not confirmed
QMState const *s;
for (s = m_state.obj->superstate;
s != static_cast<QMState const *>(0);
s = s->superstate)
{
if (s == parent) {
isConfirmed = true; // child is confirmed
break;
}
else {
child = s;
}
}
/// @post the child must be confirmed
Q_ENSURE_ID(810, isConfirmed != false);
return child; // return the child
}
} // namespace QP

View File

@ -1,254 +0,0 @@
/// @file
/// @brief QP::QActive services and QF support code
/// @ingroup qf
/// @cond
///***************************************************************************
/// Last updated for version 5.8.0
/// Last updated on 2016-11-19
///
/// Q u a n t u m L e a P s
/// ---------------------------
/// innovating embedded systems
///
/// Copyright (C) Quantum Leaps. All rights reserved.
///
/// This program is open source software: you can redistribute it and/or
/// modify it under the terms of the GNU General Public License as published
/// by the Free Software Foundation, either version 3 of the License, or
/// (at your option) any later version.
///
/// Alternatively, this program may be distributed and modified under the
/// terms of Quantum Leaps commercial licenses, which expressly supersede
/// the GNU General Public License and are specifically designed for
/// licensees interested in retaining the proprietary status of their code.
///
/// This program is distributed in the hope that it will be useful,
/// but WITHOUT ANY WARRANTY; without even the implied warranty of
/// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
/// GNU General Public License for more details.
///
/// You should have received a copy of the GNU General Public License
/// along with this program. If not, see <http://www.gnu.org/licenses/>.
///
/// Contact information:
/// https://state-machine.com
/// mailto:info@state-machine.com
///***************************************************************************
/// @endcond
#define QP_IMPL // this is QP implementation
#include "qf_port.h" // QF port
#include "qf_pkg.h" // QF package-scope interface
#include "qassert.h" // QP embedded systems-friendly assertions
#ifdef Q_SPY // QS software tracing enabled?
#include "qs_port.h" // include QS port
#else
#include "qs_dummy.h" // disable the QS software tracing
#endif // Q_SPY
namespace QP {
Q_DEFINE_THIS_MODULE("qf_act")
// public objects ************************************************************
QActive *QF::active_[QF_MAX_ACTIVE + 1]; // to be used by QF ports only
//****************************************************************************
/// @description
/// This function adds a given active object to the active objects managed
/// by the QF framework. It should not be called by the application directly,
/// only through the function QP::QActive::start().
///
/// @param[in] a pointer to the active object to add to the framework.
///
/// @note The priority of the active object @p a should be set before calling
/// this function.
///
/// @sa QP::QF::remove_()
///
void QF::add_(QActive * const a) {
uint_fast8_t p = a->m_prio;
Q_REQUIRE_ID(100, (static_cast<uint_fast8_t>(0) < p)
&& (p <= static_cast<uint_fast8_t>(QF_MAX_ACTIVE))
&& (active_[p] == static_cast<QActive *>(0)));
QF_CRIT_STAT_
QF_CRIT_ENTRY_();
active_[p] = a; // registger the active object at this priority
QS_BEGIN_NOCRIT_(QS_QF_ACTIVE_ADD, QS::priv_.locFilter[QS::AO_OBJ], a)
QS_TIME_(); // timestamp
QS_OBJ_(a); // the active object
QS_U8_(p); // the priority of the active object
QS_END_NOCRIT_()
QF_CRIT_EXIT_();
}
//****************************************************************************
/// @description
/// This function removes a given active object from the active objects
/// managed by the QF framework. It should not be called by the application
/// directly, only through the function QP::QActive::stop().
///
/// @param[in] a pointer to the active object to remove from the framework.
///
/// @note The active object that is removed from the framework can no longer
/// participate in the publish-subscribe event exchange.
///
/// @sa QP::QF::add_()
///
void QF::remove_(QActive * const a) {
uint_fast8_t p = a->m_prio;
Q_REQUIRE_ID(200, (static_cast<uint_fast8_t>(0) < p)
&& (p <= static_cast<uint_fast8_t>(QF_MAX_ACTIVE))
&& (active_[p] == a));
QF_CRIT_STAT_
QF_CRIT_ENTRY_();
active_[p] = static_cast<QActive *>(0); // free-up the priority level
a->m_state.fun = Q_STATE_CAST(0); // invalidate the state
QS_BEGIN_NOCRIT_(QS_QF_ACTIVE_REMOVE, QS::priv_.locFilter[QS::AO_OBJ], a)
QS_TIME_(); // timestamp
QS_OBJ_(a); // the active object
QS_U8_(p); // the priority of the active object
QS_END_NOCRIT_()
QF_CRIT_EXIT_();
}
//****************************************************************************
/// @description
/// macro to encapsulate pointer increment, which violates MISRA-C:2004
/// required rule 17.4 (pointer arithmetic used).
///
/// @param[in] p_ pointer to be incremented.
///
#define QF_PTR_INC_(p_) (++(p_))
//****************************************************************************
/// @description
/// Clears a memory buffer by writing zeros byte-by-byte.
///
/// @param[in] start pointer to the beginning of a memory buffer.
/// @param[in] len length of the memory buffer to clear (in bytes)
///
/// @note The main application of this function is clearing the internal QF
/// variables upon startup. This is done to avoid problems with non-standard
/// startup code provided with some compilers and toolsets (e.g., TI DSPs or
/// Microchip MPLAB), which does not zero the uninitialized variables, as
/// required by the ANSI C standard.
///
void QF::bzero(void * const start, uint_fast16_t len) {
uint8_t *ptr = static_cast<uint8_t *>(start);
while (len != static_cast<uint_fast16_t>(0)) {
*ptr = static_cast<uint8_t>(0);
QF_PTR_INC_(ptr);
--len;
}
}
/* log-base-2 lookup table **************************************************/
#ifndef QF_LOG2
uint8_t const QF_log2Lkup[256] = {
static_cast<uint8_t>(0),
static_cast<uint8_t>(1),
static_cast<uint8_t>(2), static_cast<uint8_t>(2),
static_cast<uint8_t>(3), static_cast<uint8_t>(3), static_cast<uint8_t>(3),
static_cast<uint8_t>(3),
static_cast<uint8_t>(4), static_cast<uint8_t>(4), static_cast<uint8_t>(4),
static_cast<uint8_t>(4), static_cast<uint8_t>(4), static_cast<uint8_t>(4),
static_cast<uint8_t>(4), static_cast<uint8_t>(4),
static_cast<uint8_t>(5), static_cast<uint8_t>(5), static_cast<uint8_t>(5),
static_cast<uint8_t>(5), static_cast<uint8_t>(5), static_cast<uint8_t>(5),
static_cast<uint8_t>(5), static_cast<uint8_t>(5), static_cast<uint8_t>(5),
static_cast<uint8_t>(5), static_cast<uint8_t>(5), static_cast<uint8_t>(5),
static_cast<uint8_t>(5), static_cast<uint8_t>(5), static_cast<uint8_t>(5),
static_cast<uint8_t>(5),
static_cast<uint8_t>(6), static_cast<uint8_t>(6), static_cast<uint8_t>(6),
static_cast<uint8_t>(6), static_cast<uint8_t>(6), static_cast<uint8_t>(6),
static_cast<uint8_t>(6), static_cast<uint8_t>(6), static_cast<uint8_t>(6),
static_cast<uint8_t>(6), static_cast<uint8_t>(6), static_cast<uint8_t>(6),
static_cast<uint8_t>(6), static_cast<uint8_t>(6), static_cast<uint8_t>(6),
static_cast<uint8_t>(6), static_cast<uint8_t>(6), static_cast<uint8_t>(6),
static_cast<uint8_t>(6), static_cast<uint8_t>(6), static_cast<uint8_t>(6),
static_cast<uint8_t>(6), static_cast<uint8_t>(6), static_cast<uint8_t>(6),
static_cast<uint8_t>(6), static_cast<uint8_t>(6), static_cast<uint8_t>(6),
static_cast<uint8_t>(6), static_cast<uint8_t>(6), static_cast<uint8_t>(6),
static_cast<uint8_t>(6), static_cast<uint8_t>(6),
static_cast<uint8_t>(7), static_cast<uint8_t>(7), static_cast<uint8_t>(7),
static_cast<uint8_t>(7), static_cast<uint8_t>(7), static_cast<uint8_t>(7),
static_cast<uint8_t>(7), static_cast<uint8_t>(7), static_cast<uint8_t>(7),
static_cast<uint8_t>(7), static_cast<uint8_t>(7), static_cast<uint8_t>(7),
static_cast<uint8_t>(7), static_cast<uint8_t>(7), static_cast<uint8_t>(7),
static_cast<uint8_t>(7), static_cast<uint8_t>(7), static_cast<uint8_t>(7),
static_cast<uint8_t>(7), static_cast<uint8_t>(7), static_cast<uint8_t>(7),
static_cast<uint8_t>(7), static_cast<uint8_t>(7), static_cast<uint8_t>(7),
static_cast<uint8_t>(7), static_cast<uint8_t>(7), static_cast<uint8_t>(7),
static_cast<uint8_t>(7), static_cast<uint8_t>(7), static_cast<uint8_t>(7),
static_cast<uint8_t>(7), static_cast<uint8_t>(7), static_cast<uint8_t>(7),
static_cast<uint8_t>(7), static_cast<uint8_t>(7), static_cast<uint8_t>(7),
static_cast<uint8_t>(7), static_cast<uint8_t>(7), static_cast<uint8_t>(7),
static_cast<uint8_t>(7), static_cast<uint8_t>(7), static_cast<uint8_t>(7),
static_cast<uint8_t>(7), static_cast<uint8_t>(7), static_cast<uint8_t>(7),
static_cast<uint8_t>(7), static_cast<uint8_t>(7), static_cast<uint8_t>(7),
static_cast<uint8_t>(7), static_cast<uint8_t>(7), static_cast<uint8_t>(7),
static_cast<uint8_t>(7), static_cast<uint8_t>(7), static_cast<uint8_t>(7),
static_cast<uint8_t>(7), static_cast<uint8_t>(7), static_cast<uint8_t>(7),
static_cast<uint8_t>(7), static_cast<uint8_t>(7), static_cast<uint8_t>(7),
static_cast<uint8_t>(7), static_cast<uint8_t>(7), static_cast<uint8_t>(7),
static_cast<uint8_t>(7),
static_cast<uint8_t>(8), static_cast<uint8_t>(8), static_cast<uint8_t>(8),
static_cast<uint8_t>(8), static_cast<uint8_t>(8), static_cast<uint8_t>(8),
static_cast<uint8_t>(8), static_cast<uint8_t>(8), static_cast<uint8_t>(8),
static_cast<uint8_t>(8), static_cast<uint8_t>(8), static_cast<uint8_t>(8),
static_cast<uint8_t>(8), static_cast<uint8_t>(8), static_cast<uint8_t>(8),
static_cast<uint8_t>(8), static_cast<uint8_t>(8), static_cast<uint8_t>(8),
static_cast<uint8_t>(8), static_cast<uint8_t>(8), static_cast<uint8_t>(8),
static_cast<uint8_t>(8), static_cast<uint8_t>(8), static_cast<uint8_t>(8),
static_cast<uint8_t>(8), static_cast<uint8_t>(8), static_cast<uint8_t>(8),
static_cast<uint8_t>(8), static_cast<uint8_t>(8), static_cast<uint8_t>(8),
static_cast<uint8_t>(8), static_cast<uint8_t>(8), static_cast<uint8_t>(8),
static_cast<uint8_t>(8), static_cast<uint8_t>(8), static_cast<uint8_t>(8),
static_cast<uint8_t>(8), static_cast<uint8_t>(8), static_cast<uint8_t>(8),
static_cast<uint8_t>(8), static_cast<uint8_t>(8), static_cast<uint8_t>(8),
static_cast<uint8_t>(8), static_cast<uint8_t>(8), static_cast<uint8_t>(8),
static_cast<uint8_t>(8), static_cast<uint8_t>(8), static_cast<uint8_t>(8),
static_cast<uint8_t>(8), static_cast<uint8_t>(8), static_cast<uint8_t>(8),
static_cast<uint8_t>(8), static_cast<uint8_t>(8), static_cast<uint8_t>(8),
static_cast<uint8_t>(8), static_cast<uint8_t>(8), static_cast<uint8_t>(8),
static_cast<uint8_t>(8), static_cast<uint8_t>(8), static_cast<uint8_t>(8),
static_cast<uint8_t>(8), static_cast<uint8_t>(8), static_cast<uint8_t>(8),
static_cast<uint8_t>(8), static_cast<uint8_t>(8), static_cast<uint8_t>(8),
static_cast<uint8_t>(8), static_cast<uint8_t>(8), static_cast<uint8_t>(8),
static_cast<uint8_t>(8), static_cast<uint8_t>(8), static_cast<uint8_t>(8),
static_cast<uint8_t>(8), static_cast<uint8_t>(8), static_cast<uint8_t>(8),
static_cast<uint8_t>(8), static_cast<uint8_t>(8), static_cast<uint8_t>(8),
static_cast<uint8_t>(8), static_cast<uint8_t>(8), static_cast<uint8_t>(8),
static_cast<uint8_t>(8), static_cast<uint8_t>(8), static_cast<uint8_t>(8),
static_cast<uint8_t>(8), static_cast<uint8_t>(8), static_cast<uint8_t>(8),
static_cast<uint8_t>(8), static_cast<uint8_t>(8), static_cast<uint8_t>(8),
static_cast<uint8_t>(8), static_cast<uint8_t>(8), static_cast<uint8_t>(8),
static_cast<uint8_t>(8), static_cast<uint8_t>(8), static_cast<uint8_t>(8),
static_cast<uint8_t>(8), static_cast<uint8_t>(8), static_cast<uint8_t>(8),
static_cast<uint8_t>(8), static_cast<uint8_t>(8), static_cast<uint8_t>(8),
static_cast<uint8_t>(8), static_cast<uint8_t>(8), static_cast<uint8_t>(8),
static_cast<uint8_t>(8), static_cast<uint8_t>(8), static_cast<uint8_t>(8),
static_cast<uint8_t>(8), static_cast<uint8_t>(8), static_cast<uint8_t>(8),
static_cast<uint8_t>(8), static_cast<uint8_t>(8), static_cast<uint8_t>(8),
static_cast<uint8_t>(8), static_cast<uint8_t>(8), static_cast<uint8_t>(8),
static_cast<uint8_t>(8), static_cast<uint8_t>(8), static_cast<uint8_t>(8),
static_cast<uint8_t>(8), static_cast<uint8_t>(8), static_cast<uint8_t>(8),
static_cast<uint8_t>(8), static_cast<uint8_t>(8), static_cast<uint8_t>(8),
static_cast<uint8_t>(8), static_cast<uint8_t>(8)
};
#endif // QF_LOG2
} // namespace QP

View File

@ -1,425 +0,0 @@
/// @file
/// @brief QP::QActive native queue operations (based on QP::QEQueue)
///
/// @note
/// this source file is only included in the QF library when the native
/// QF active object queue is used (instead of a message queue of an RTOS).
///
/// @ingroup qf
/// @cond
///***************************************************************************
/// Last updated for version 5.9.8
/// Last updated on 2017-09-20
///
/// Q u a n t u m L e a P s
/// ---------------------------
/// innovating embedded systems
///
/// Copyright (C) Quantum Leaps, LLC. All rights reserved.
///
/// This program is open source software: you can redistribute it and/or
/// modify it under the terms of the GNU General Public License as published
/// by the Free Software Foundation, either version 3 of the License, or
/// (at your option) any later version.
///
/// Alternatively, this program may be distributed and modified under the
/// terms of Quantum Leaps commercial licenses, which expressly supersede
/// the GNU General Public License and are specifically designed for
/// licensees interested in retaining the proprietary status of their code.
///
/// This program is distributed in the hope that it will be useful,
/// but WITHOUT ANY WARRANTY; without even the implied warranty of
/// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
/// GNU General Public License for more details.
///
/// You should have received a copy of the GNU General Public License
/// along with this program. If not, see <http://www.gnu.org/licenses/>.
///
/// Contact information:
/// https://state-machine.com
/// mailto:info@state-machine.com
///***************************************************************************
/// @endcond
#define QP_IMPL // this is QP implementation
#include "qf_port.h" // QF port
#include "qf_pkg.h" // QF package-scope interface
#include "qassert.h" // QP embedded systems-friendly assertions
#ifdef Q_SPY // QS software tracing enabled?
#include "qs_port.h" // include QS port
#else
#include "qs_dummy.h" // disable the QS software tracing
#endif // Q_SPY
namespace QP {
Q_DEFINE_THIS_MODULE("qf_actq")
//****************************************************************************
/// @description
/// Direct event posting is the simplest asynchronous communication method
/// available in QF.
///
/// @param[in,out] e pointer to the event to be posted
/// @param[in] margin number of required free slots in the queue
/// after posting the event. The special value QP::QF_NO_MARGIN
/// means that this function will assert if posting fails.
///
/// @returns
/// 'true' (success) if the posting succeeded (with the provided margin) and
/// 'false' (failure) when the posting fails.
///
/// @attention
/// Should be called only via the macro POST() or POST_X().
///
/// @note
/// The QP::QF_NO_MARGIN value of the @p margin argument is special and
/// denotes situation when the post() operation is assumed to succeed (event
/// delivery guarantee). An assertion fires, when the event cannot be
/// delivered in this case.
///
/// @usage
/// @include qf_post.cpp
///
/// @sa
/// QActive::postLIFO()
///
#ifndef Q_SPY
bool QActive::post_(QEvt const * const e, uint_fast16_t const margin)
#else
bool QActive::post_(QEvt const * const e, uint_fast16_t const margin,
void const * const sender)
#endif
{
bool status;
QF_CRIT_STAT_
/// @pre event pointer must be valid
Q_REQUIRE_ID(100, e != static_cast<QEvt const *>(0));
QF_CRIT_ENTRY_();
QEQueueCtr nFree = m_eQueue.m_nFree; // get volatile into the temporary
if (margin == QF_NO_MARGIN) {
if (nFree > static_cast<QEQueueCtr>(0)) {
status = true; // can post
}
else {
status = false; // cannot post
Q_ERROR_ID(110); // must be able to post the event
}
}
else if (nFree > static_cast<QEQueueCtr>(margin)) {
status = true; // can post
}
else {
status = false; // cannot post
}
if (status) { // can post the event?
QS_BEGIN_NOCRIT_(QS_QF_ACTIVE_POST_FIFO,
QS::priv_.locFilter[QS::AO_OBJ], this)
QS_TIME_(); // timestamp
QS_OBJ_(sender); // the sender object
QS_SIG_(e->sig); // the signal of the event
QS_OBJ_(this); // this active object
QS_2U8_(e->poolId_, e->refCtr_); // pool Id & refCtr of the evt
QS_EQC_(nFree); // number of free entries
QS_EQC_(m_eQueue.m_nMin); // min number of free entries
QS_END_NOCRIT_()
// is it a dynamic event?
if (e->poolId_ != static_cast<uint8_t>(0)) {
QF_EVT_REF_CTR_INC_(e); // increment the reference counter
}
--nFree; // one free entry just used up
m_eQueue.m_nFree = nFree; // update the volatile
if (m_eQueue.m_nMin > nFree) {
m_eQueue.m_nMin = nFree; // update minimum so far
}
// is the queue empty?
if (m_eQueue.m_frontEvt == static_cast<QEvt const *>(0)) {
m_eQueue.m_frontEvt = e; // deliver event directly
QACTIVE_EQUEUE_SIGNAL_(this); // signal the event queue
}
// queue is not empty, insert event into the ring-buffer
else {
// insert event pointer e into the buffer (FIFO)
QF_PTR_AT_(m_eQueue.m_ring, m_eQueue.m_head) = e;
// need to wrap head?
if (m_eQueue.m_head == static_cast<QEQueueCtr>(0)) {
m_eQueue.m_head = m_eQueue.m_end; // wrap around
}
--m_eQueue.m_head;
}
QF_CRIT_EXIT_();
status = true; // event posted successfully
}
else {
/// @note assert if event cannot be posted and dropping events is
/// not acceptable
Q_ASSERT_ID(110, margin != QF_NO_MARGIN);
QS_BEGIN_NOCRIT_(QS_QF_ACTIVE_POST_ATTEMPT,
QS::priv_.locFilter[QS::AO_OBJ], this)
QS_TIME_(); // timestamp
QS_OBJ_(sender); // the sender object
QS_SIG_(e->sig); // the signal of the event
QS_OBJ_(this); // this active object
QS_2U8_(e->poolId_, e->refCtr_); // pool Id & refCtr of the evt
QS_EQC_(nFree); // number of free entries
QS_EQC_(static_cast<QEQueueCtr>(margin)); // margin requested
QS_END_NOCRIT_()
QF_CRIT_EXIT_();
QF::gc(e); // recycle the evnet to avoid a leak
status = false; // event not posted
}
return status;
}
//****************************************************************************
/// @description
/// posts an event to the event queue of the active object using the
/// Last-In-First-Out (LIFO) policy.
///
/// @note
/// The LIFO policy should be used only for self-posting and with caution,
/// because it alters order of events in the queue.
///
/// @param[in] e pointer to the event to post to the queue
///
/// @sa
/// QActive::post_()
///
void QActive::postLIFO(QEvt const * const e) {
QF_CRIT_STAT_
QF_CRIT_ENTRY_();
QEQueueCtr nFree = m_eQueue.m_nFree;// tmp to avoid UB for volatile access
// the queue must be able to accept the event (cannot overflow)
Q_ASSERT_ID(210, nFree != static_cast<QEQueueCtr>(0));
QS_BEGIN_NOCRIT_(QS_QF_ACTIVE_POST_LIFO,
QS::priv_.locFilter[QS::AO_OBJ], this)
QS_TIME_(); // timestamp
QS_SIG_(e->sig); // the signal of this event
QS_OBJ_(this); // this active object
QS_2U8_(e->poolId_, e->refCtr_); // pool Id & refCtr of the evt
QS_EQC_(nFree); // number of free entries
QS_EQC_(m_eQueue.m_nMin); // min number of free entries
QS_END_NOCRIT_()
// is it a dynamic event?
if (e->poolId_ != static_cast<uint8_t>(0)) {
QF_EVT_REF_CTR_INC_(e); // increment the reference counter
}
--nFree; // one free entry just used up
m_eQueue.m_nFree = nFree; // update the volatile
if (m_eQueue.m_nMin > nFree) {
m_eQueue.m_nMin = nFree; // update minimum so far
}
QEvt const *frontEvt = m_eQueue.m_frontEvt;// read volatile into temporary
m_eQueue.m_frontEvt = e; // deliver the event directly to the front
// was the queue empty?
if (frontEvt == static_cast<QEvt const *>(0)) {
QACTIVE_EQUEUE_SIGNAL_(this); // signal the event queue
}
// queue is not empty, leave event in the ring-buffer
else {
++m_eQueue.m_tail;
if (m_eQueue.m_tail == m_eQueue.m_end) { // need to wrap the tail?
m_eQueue.m_tail = static_cast<QEQueueCtr>(0); // wrap around
}
QF_PTR_AT_(m_eQueue.m_ring, m_eQueue.m_tail) = frontEvt;
}
QF_CRIT_EXIT_();
}
//****************************************************************************
/// @description
/// The behavior of this function depends on the kernel used in the QF port.
/// For built-in kernels (Vanilla or QK) the function can be called only when
/// the queue is not empty, so it doesn't block. For a blocking kernel/OS
/// the function can block and wait for delivery of an event.
///
/// @returns
/// A pointer to the received event. The returned pointer is guaranteed to be
/// valid (can't be NULL).
///
/// @note
/// This function is used internally by a QF port to extract events from
/// the event queue of an active object. This function depends on the event
/// queue implementation and is sometimes customized in the QF port
/// (file qf_port.h). Depending on the definition of the macro
/// QACTIVE_EQUEUE_WAIT_(), the function might block the calling thread when
/// no events are available.
///
QEvt const *QActive::get_(void) {
QF_CRIT_STAT_
QF_CRIT_ENTRY_();
QACTIVE_EQUEUE_WAIT_(this); // wait for event to arrive directly
QEvt const *e = m_eQueue.m_frontEvt; // always remove evt from the front
QEQueueCtr nFree = m_eQueue.m_nFree + static_cast<QEQueueCtr>(1);
m_eQueue.m_nFree = nFree; // upate the number of free
// any events in the ring buffer?
if (nFree <= m_eQueue.m_end) {
// remove event from the tail
m_eQueue.m_frontEvt = QF_PTR_AT_(m_eQueue.m_ring, m_eQueue.m_tail);
if (m_eQueue.m_tail == static_cast<QEQueueCtr>(0)) { // need to wrap?
m_eQueue.m_tail = m_eQueue.m_end; // wrap around
}
--m_eQueue.m_tail;
QS_BEGIN_NOCRIT_(QS_QF_ACTIVE_GET,
QS::priv_.locFilter[QS::AO_OBJ], this)
QS_TIME_(); // timestamp
QS_SIG_(e->sig); // the signal of this event
QS_OBJ_(this); // this active object
QS_2U8_(e->poolId_, e->refCtr_); // pool Id & refCtr of the evt
QS_EQC_(nFree); // number of free entries
QS_END_NOCRIT_()
}
else {
// the queue becomes empty
m_eQueue.m_frontEvt = static_cast<QEvt const *>(0);
// all entries in the queue must be free (+1 for fronEvt)
Q_ASSERT_ID(310, nFree ==
(m_eQueue.m_end + static_cast<QEQueueCtr>(1)));
QS_BEGIN_NOCRIT_(QS_QF_ACTIVE_GET_LAST,
QS::priv_.locFilter[QS::AO_OBJ], this)
QS_TIME_(); // timestamp
QS_SIG_(e->sig); // the signal of this event
QS_OBJ_(this); // this active object
QS_2U8_(e->poolId_, e->refCtr_); // pool Id & refCtr of the evt
QS_END_NOCRIT_()
}
QF_CRIT_EXIT_();
return e;
}
//****************************************************************************
/// @description
/// Queries the minimum of free ever present in the given event queue of
/// an active object with priority @p prio, since the active object
/// was started.
///
/// @note
/// QP::QF::getQueueMin() is available only when the native QF event
/// queue implementation is used. Requesting the queue minimum of an unused
/// priority level raises an assertion in the QF. (A priority level becomes
/// used in QF after the call to the QP::QF::add_() function.)
///
/// @param[in] prio Priority of the active object, whose queue is queried
///
/// @returns
/// the minimum of free ever present in the given event queue of an active
/// object with priority @p prio, since the active object was started.
///
uint_fast16_t QF::getQueueMin(uint_fast8_t const prio) {
Q_REQUIRE_ID(400, (prio <= static_cast<uint_fast8_t>(QF_MAX_ACTIVE))
&& (active_[prio] != static_cast<QActive *>(0)));
QF_CRIT_STAT_
QF_CRIT_ENTRY_();
uint_fast16_t min =
static_cast<uint_fast16_t>(active_[prio]->m_eQueue.m_nMin);
QF_CRIT_EXIT_();
return min;
}
//****************************************************************************
QTicker::QTicker(uint_fast8_t const tickRate)
: QActive(Q_STATE_CAST(0))
{
// reuse m_head for tick-rate
m_eQueue.m_head = static_cast<QEQueueCtr>(tickRate);
}
//****************************************************************************
void QTicker::init(QEvt const * const /*e*/) {
m_eQueue.m_tail = static_cast<QEQueueCtr>(0);
}
//****************************************************************************
void QTicker::dispatch(QEvt const * const /*e*/) {
QF_CRIT_STAT_
QF_CRIT_ENTRY_();
QEQueueCtr n = m_eQueue.m_tail; // # ticks since the last call
m_eQueue.m_tail = static_cast<QEQueueCtr>(0); // clear the # ticks
QF_CRIT_EXIT_();
for (; n > static_cast<QEQueueCtr>(0); --n) {
QF::TICK_X(static_cast<uint_fast8_t>(m_eQueue.m_head), this);
}
}
//****************************************************************************
#ifndef Q_SPY
bool QTicker::post_(QEvt const * const /*e*/, uint_fast16_t const /*margin*/)
#else
bool QTicker::post_(QEvt const * const /*e*/, uint_fast16_t const /*margin*/,
void const * const sender)
#endif
{
QF_CRIT_STAT_
QF_CRIT_ENTRY_();
if (m_eQueue.m_frontEvt == static_cast<QEvt const *>(0)) {
#ifdef Q_EVT_CTOR
static QEvt const tickEvt(static_cast<QSignal>(0),
QEvt::STATIC_EVT);
#else
static QEvt const tickEvt = { static_cast<QSignal>(0),
static_cast<uint8_t>(0),
static_cast<uint8_t>(0) };
#endif // Q_EVT_CTOR
m_eQueue.m_frontEvt = &tickEvt; // deliver event directly
--m_eQueue.m_nFree; // one less free event
QACTIVE_EQUEUE_SIGNAL_(this); // signal the event queue
}
++m_eQueue.m_tail; // account for one more tick event
QS_BEGIN_NOCRIT_(QS_QF_ACTIVE_POST_FIFO,
QS::priv_.locFilter[QS::AO_OBJ], this)
QS_TIME_(); // timestamp
QS_OBJ_(sender); // the sender object
QS_SIG_(static_cast<QSignal>(0)); // the signal of the event
QS_OBJ_(this); // this active object
QS_2U8_(static_cast<uint8_t>(0),
static_cast<uint8_t>(0)); // pool Id & refCtr of the evt
QS_EQC_(static_cast<uint8_t>(0)); // number of free entries
QS_EQC_(static_cast<uint8_t>(0)); // min number of free entries
QS_END_NOCRIT_()
QF_CRIT_EXIT_();
return true; // the event is always posted correctly
}
//****************************************************************************
void QTicker::postLIFO(QEvt const * const /*e*/) {
Q_ERROR_ID(900); // operation not allowed
}
} // namespace QP

View File

@ -1,145 +0,0 @@
/// @file
/// @brief QP::QActive::defer() and QP::QActive::recall() definitions.
/// @ingroup qf
/// @cond
///***************************************************************************
/// Last updated for version 5.8.1
/// Last updated on 2017-08-01
///
/// Q u a n t u m L e a P s
/// ---------------------------
/// innovating embedded systems
///
/// Copyright (C) Quantum Leaps, LLC. All rights reserved.
///
/// This program is open source software: you can redistribute it and/or
/// modify it under the terms of the GNU General Public License as published
/// by the Free Software Foundation, either version 3 of the License, or
/// (at your option) any later version.
///
/// Alternatively, this program may be distributed and modified under the
/// terms of Quantum Leaps commercial licenses, which expressly supersede
/// the GNU General Public License and are specifically designed for
/// licensees interested in retaining the proprietary status of their code.
///
/// This program is distributed in the hope that it will be useful,
/// but WITHOUT ANY WARRANTY; without even the implied warranty of
/// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
/// GNU General Public License for more details.
///
/// You should have received a copy of the GNU General Public License
/// along with this program. If not, see <http://www.gnu.org/licenses/>.
///
/// Contact information:
/// https://state-machine.com
/// mailto:info@state-machine.com
///***************************************************************************
/// @endcond
#define QP_IMPL // this is QP implementation
#include "qf_port.h" // QF port
#include "qf_pkg.h" // QF package-scope interface
#include "qassert.h" // QP embedded systems-friendly assertions
namespace QP {
Q_DEFINE_THIS_MODULE("qf_defer")
//****************************************************************************
/// @description
/// This function is part of the event deferral support. An active object
/// uses this function to defer an event @p e to the QF-supported native
/// event queue @p eq. QF correctly accounts for another outstanding
/// reference to the event and will not recycle the event at the end of
/// the RTC step. Later, the active object might recall one event at a
/// time from the event queue.
///
/// @param[in] eq pointer to a "raw" thread-safe queue to recall
/// an event from.
/// @param[in] e pointer to the event to be deferred
///
/// @returns 'true' (success) when the event could be deferred and 'false'
/// (failure) if event deferral failed due to overflowing the queue.
///
/// An active object can use multiple event queues to defer events of
/// different kinds.
///
/// @sa QP::QActive::recall(), QP::QEQueue, QP::QActive::flushDeferred()
///
bool QActive::defer(QEQueue * const eq, QEvt const * const e) const {
return eq->post(e, static_cast<uint_fast16_t>(0)); // non-asserting post
}
//****************************************************************************
/// @description
/// This function is part of the event deferral support. An active object
/// uses this function to recall a deferred event from a given QF
/// event queue. Recalling an event means that it is removed from the
/// deferred event queue @p eq and posted (LIFO) to the event queue of
/// the active object.
///
/// @param[in] eq pointer to a "raw" thread-safe queue to recall
/// an event from.
///
/// @returns 'true' if an event has been recalled and 'false' if not.
///
/// @note An active object can use multiple event queues to defer events of
/// different kinds.
///
/// @sa QP::QActive::recall(), QP::QEQueue, QP::QActive::postLIFO_()
///
bool QActive::recall(QEQueue * const eq) {
QEvt const * const e = eq->get(); // try to get evt from deferred queue
bool const recalled = (e != static_cast<QEvt const *>(0));//evt available?
if (recalled) {
this->postLIFO(e); // post it to the _front_ of the AO's queue
QF_CRIT_STAT_
QF_CRIT_ENTRY_();
// is it a dynamic event?
if (e->poolId_ != static_cast<uint8_t>(0)) {
// after posting to the AO's queue the event must be referenced
// at least twice: once in the deferred event queue (eq->get()
// did NOT decrement the reference counter) and once in the
// AO's event queue.
Q_ASSERT_ID(210, e->refCtr_ > static_cast<uint8_t>(1));
// we need to decrement the reference counter once, to account
// for removing the event from the deferred event queue.
QF_EVT_REF_CTR_DEC_(e); // decrement the reference counter
}
QF_CRIT_EXIT_();
}
return recalled; // event not recalled
}
//****************************************************************************
/// @description
/// This function is part of the event deferral support. An active object
/// can use this function to flush a given QF event queue. The function makes
/// sure that the events are not leaked.
///
/// @param[in] eq pointer to a "raw" thread-safe queue to flush.
///
/// @returns the number of events actually flushed from the queue.
///
///
/// @sa QP::QActive::defer(), QP::QActive::recall(), QP::QEQueue
///
uint_fast16_t QActive::flushDeferred(QEQueue * const eq) const {
uint_fast16_t n = static_cast<uint_fast16_t>(0);
for (QEvt const *e = eq->get();
e != static_cast<QEvt const *>(0);
e = eq->get())
{
QF::gc(e); // garbage collect
++n; // count the flushed event
}
return n;
}
} // namespace QP

View File

@ -1,281 +0,0 @@
/// @file
/// @brief QF/C++ dynamic event management
/// @cond
///***************************************************************************
/// Last updated for version 5.9.7
/// Last updated on 2017-08-25
///
/// Q u a n t u m L e a P s
/// ---------------------------
/// innovating embedded systems
///
/// Copyright (C) Quantum Leaps, LLC. All rights reserved.
///
/// This program is open source software: you can redistribute it and/or
/// modify it under the terms of the GNU General Public License as published
/// by the Free Software Foundation, either version 3 of the License, or
/// (at your option) any later version.
///
/// Alternatively, this program may be distributed and modified under the
/// terms of Quantum Leaps commercial licenses, which expressly supersede
/// the GNU General Public License and are specifically designed for
/// licensees interested in retaining the proprietary status of their code.
///
/// This program is distributed in the hope that it will be useful,
/// but WITHOUT ANY WARRANTY; without even the implied warranty of
/// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
/// GNU General Public License for more details.
///
/// You should have received a copy of the GNU General Public License
/// along with this program. If not, see <http://www.gnu.org/licenses/>.
///
/// Contact information:
/// https://state-machine.com
/// mailo:info@state-machine.com
///***************************************************************************
/// @endcond
#define QP_IMPL // this is QP implementation
#include "qf_port.h" // QF port
#include "qf_pkg.h" // QF package-scope interface
#include "qassert.h" // QP embedded systems-friendly assertions
#ifdef Q_SPY // QS software tracing enabled?
#include "qs_port.h" // include QS port
#else
#include "qs_dummy.h" // disable the QS software tracing
#endif // Q_SPY
namespace QP {
Q_DEFINE_THIS_MODULE("qf_dyn")
// Package-scope objects *****************************************************
QF_EPOOL_TYPE_ QF_pool_[QF_MAX_EPOOL]; // allocate the event pools
uint_fast8_t QF_maxPool_; // number of initialized event pools
//****************************************************************************
/// @description
/// This function initializes one event pool at a time and must be called
/// exactly once for each event pool before the pool can be used.@n
/// @n
/// Many RTOSes provide fixed block-size heaps, a.k.a. memory pools that can
/// be adapted for QF event pools. In case such support is missing, QF
/// provides a native QF event pool implementation. The macro #QF_EPOOL_TYPE_
/// determines the type of event pool used by a particular QF port. See
/// class QP::QMPool for more information.
///
/// @param[in] poolSto pointer to the storage for the event pool
/// @param[in] poolSize size of the storage for the pool in bytes
/// @param[in] evtSize the block-size of the pool in bytes, which determines
/// the maximum size of events that can be allocated
/// from the pool
/// @note
/// You might initialize many event pools by making many consecutive calls
/// to the QF_poolInit() function. However, for the simplicity of the internal
/// implementation, you must initialize event pools in the ascending order of
/// the event size.
///
/// @note The actual number of events available in the pool might be actually
/// less than (@p poolSize / @p evtSize) due to the internal alignment
/// of the blocks that the pool might perform. You can always check the
/// capacity of the pool by calling QF_getPoolMin().
///
/// @note The dynamic allocation of events is optional, meaning that you might
/// choose not to use dynamic events. In that case calling QP::QF::poolInit()
/// and using up memory for the memory blocks is unnecessary.
///
/// @sa QF initialization example for QP::QF::init()
///
void QF::poolInit(void * const poolSto,
uint_fast32_t const poolSize, uint_fast16_t const evtSize)
{
/// @pre cannot exceed the number of available memory pools
Q_REQUIRE_ID(200, QF_maxPool_
< static_cast<uint_fast8_t>(Q_DIM(QF_pool_)));
/// @pre please initialize event pools in ascending order of evtSize
Q_REQUIRE_ID(201, (QF_maxPool_ == static_cast<uint_fast8_t>(0))
|| (QF_EPOOL_EVENT_SIZE_(
QF_pool_[QF_maxPool_ - static_cast<uint_fast8_t>(1)])
< evtSize));
QF_EPOOL_INIT_(QF_pool_[QF_maxPool_], poolSto, poolSize, evtSize);
++QF_maxPool_; // one more pool
}
//****************************************************************************
/// @description
/// Allocates an event dynamically from one of the QF event pools.
///
/// @param[in] evtSize the size (in bytes) of the event to allocate
/// @param[in] margin the number of un-allocated events still available
/// in a given event pool after the allocation completes
/// The special value QP::QF_NO_MARGIN means that this
/// function will assert if allocation fails.
/// @param[in] sig the signal to be assigned to the allocated event
///
/// @returns pointer to the newly allocated event. This pointer can be NULL
/// only if margin!=0 and the event cannot be allocated with the specified
/// margin still available in the given pool.
///
/// @note The internal QF function QP::QF::newX_() raises an assertion when
/// the margin argument is QP::QF_NO_MARGIN and allocation of the event turns
/// out to be impossible due to event pool depletion, or incorrect (too big)
/// size of the requested event.
///
/// @note The application code should not call this function directly.
/// The only allowed use is thorough the macros Q_NEW() or Q_NEW_X().
///
QEvt *QF::newX_(uint_fast16_t const evtSize,
uint_fast16_t const margin, enum_t const sig)
{
uint_fast8_t idx;
// find the pool id that fits the requested event size ...
for (idx = static_cast<uint_fast8_t>(0); idx < QF_maxPool_; ++idx) {
if (evtSize <= QF_EPOOL_EVENT_SIZE_(QF_pool_[idx])) {
break;
}
}
// cannot run out of registered pools
Q_ASSERT_ID(310, idx < QF_maxPool_);
QS_CRIT_STAT_
QS_BEGIN_(QS_QF_NEW, static_cast<void *>(0), static_cast<void *>(0))
QS_TIME_(); // timestamp
QS_EVS_(static_cast<QEvtSize>(evtSize)); // the size of the event
QS_SIG_(static_cast<QSignal>(sig)); // the signal of the event
QS_END_()
// get e -- platform-dependent
QEvt *e;
QF_EPOOL_GET_(QF_pool_[idx], e,
((margin != QF_NO_MARGIN)
? margin
: static_cast<uint_fast16_t>(0)));
// was e allocated correctly?
if (e != static_cast<QEvt const *>(0)) {
e->sig = static_cast<QSignal>(sig); // set the signal
// store pool ID
e->poolId_ = static_cast<uint8_t>(
idx + static_cast<uint_fast8_t>(1));
// initialize the reference counter to 0
e->refCtr_ = static_cast<uint8_t>(0);
}
else {
// event was not allocated, assert that the caller provided non-zero
// margin, which means that they can tollerate bad allocation
Q_ASSERT_ID(320, margin != static_cast<uint_fast16_t>(0));
}
return e;
}
//****************************************************************************
/// @description
/// This function implements a simple garbage collector for dynamic events.
/// Only dynamic events are candidates for recycling. (A dynamic event is one
/// that is allocated from an event pool, which is determined as non-zero
/// e->poolId_ attribute.) Next, the function decrements the reference counter
/// of the event (e->refCtr_), and recycles the event only if the counter
/// drops to zero (meaning that no more references are outstanding for this
/// event). The dynamic event is recycled by returning it to the pool from
/// which it was originally allocated.
///
/// @param[in] e pointer to the event to recycle
///
/// @note
/// QF invokes the garbage collector at all appropriate contexts, when
/// an event can become garbage (automatic garbage collection), so the
/// application code should have no need to call QP::QF::gc() directly.
/// The QP::QF::gc() function is exposed only for special cases when your
/// application sends dynamic events to the "raw" thread-safe queues
/// (see QP::QEQueue). Such queues are processed outside of QF and the
/// automatic garbage collection is **NOT** performed for these events.
/// In this case you need to call QP::QF::gc() explicitly.
///
void QF::gc(QEvt const * const e) {
// is it a dynamic event?
if (e->poolId_ != static_cast<uint8_t>(0)) {
QF_CRIT_STAT_
QF_CRIT_ENTRY_();
// isn't this the last reference?
if (e->refCtr_ > static_cast<uint8_t>(1)) {
QF_EVT_REF_CTR_DEC_(e); // decrement the ref counter
QS_BEGIN_NOCRIT_(QS_QF_GC_ATTEMPT,
static_cast<void *>(0), static_cast<void *>(0))
QS_TIME_(); // timestamp
QS_SIG_(e->sig); // the signal of the event
QS_2U8_(e->poolId_, e->refCtr_);// pool Id & refCtr of the evt
QS_END_NOCRIT_()
QF_CRIT_EXIT_();
}
// this is the last reference to this event, recycle it
else {
uint_fast8_t idx = static_cast<uint_fast8_t>(e->poolId_)
- static_cast<uint_fast8_t>(1);
QS_BEGIN_NOCRIT_(QS_QF_GC,
static_cast<void *>(0), static_cast<void *>(0))
QS_TIME_(); // timestamp
QS_SIG_(e->sig); // the signal of the event
QS_2U8_(e->poolId_, e->refCtr_);// pool Id & refCtr of the evt
QS_END_NOCRIT_()
QF_CRIT_EXIT_();
// pool ID must be in range
Q_ASSERT_ID(410, idx < QF_maxPool_);
#ifdef Q_EVT_VIRTUAL
// explicitly exectute the destructor'
// NOTE: casting 'const' away is legitimate,
// because it's a pool event
QF_EVT_CONST_CAST_(e)->~QEvt(); // xtor,
#endif
// cast 'const' away, which is OK, because it's a pool event
QF_EPOOL_PUT_(QF_pool_[idx], QF_EVT_CONST_CAST_(e));
}
}
}
//****************************************************************************
/// @description
/// Creates and returns a new reference to the current event e
///
/// @param[in] e pointer to the current event
/// @param[in] evtRef the event reference
///
/// @returns the newly created reference to the event `e`
///
/// @note The application code should not call this function directly.
/// The only allowed use is thorough the macro Q_NEW_REF().
///
QEvt const *QF::newRef_(QEvt const * const e, QEvt const * const evtRef) {
//! @pre the event must be dynamic and the provided event reference
//! must not be already in use
Q_REQUIRE_ID(500,
(e->poolId_ != static_cast<uint8_t>(0))
&& (evtRef == static_cast<QEvt const *>(0)));
QF_CRIT_STAT_
QF_CRIT_ENTRY_();
QF_EVT_REF_CTR_INC_(e); // increments the ref counter
QF_CRIT_EXIT_();
return e;
}
//****************************************************************************
/// @description
/// Obtain the block size of any registered event pools
///
uint_fast16_t QF::poolGetMaxBlockSize(void) {
return QF_EPOOL_EVENT_SIZE_(
QF_pool_[QF_maxPool_ - static_cast<uint_fast8_t>(1)]);
}
} // namespace QP

View File

@ -1,309 +0,0 @@
/// @file
/// @brief QF/C++ memory management services
/// @cond
///***************************************************************************
/// Last updated for version 5.9.0
/// Last updated on 2017-05-08
///
/// Q u a n t u m L e a P s
/// ---------------------------
/// innovating embedded systems
///
/// Copyright (C) Quantum Leaps, www.state-machine.com.
///
/// This program is open source software: you can redistribute it and/or
/// modify it under the terms of the GNU General Public License as published
/// by the Free Software Foundation, either version 3 of the License, or
/// (at your option) any later version.
///
/// Alternatively, this program may be distributed and modified under the
/// terms of Quantum Leaps commercial licenses, which expressly supersede
/// the GNU General Public License and are specifically designed for
/// licensees interested in retaining the proprietary status of their code.
///
/// This program is distributed in the hope that it will be useful,
/// but WITHOUT ANY WARRANTY; without even the implied warranty of
/// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
/// GNU General Public License for more details.
///
/// You should have received a copy of the GNU General Public License
/// along with this program. If not, see <http://www.gnu.org/licenses/>.
///
/// Contact information:
/// https://state-machine.com
/// mailto:info@state-machine.com
///***************************************************************************
/// @endcond
#define QP_IMPL // this is QP implementation
#include "qf_port.h" // QF port
#include "qf_pkg.h" // QF package-scope interface
#include "qassert.h" // QP embedded systems-friendly assertions
#ifdef Q_SPY // QS software tracing enabled?
#include "qs_port.h" // include QS port
#else
#include "qs_dummy.h" // disable the QS software tracing
#endif // Q_SPY
namespace QP {
Q_DEFINE_THIS_MODULE("qf_mem")
//****************************************************************************
/// @description
/// Default constructor of a fixed block-size memory pool.
///
/// @note
/// The memory pool is __not__ ready to use directly after instantiation.
/// To become ready, the QP::QMPool::init() must be called to give the pool
/// memory, size of this memory, and the block size to manage.
///
QMPool::QMPool(void)
: m_start(static_cast<void *>(0)),
m_end(static_cast<void *>(0)),
m_free_head(static_cast<void *>(0)),
m_blockSize(static_cast<QMPoolSize>(0)),
m_nTot(static_cast<QMPoolCtr>(0)),
m_nFree(static_cast<QMPoolCtr>(0)),
m_nMin(static_cast<QMPoolCtr>(0))
{}
//****************************************************************************
/// @description
/// Initialize a fixed block-size memory pool by providing it with the pool
/// memory to manage, size of this memory, and the block size.
///
/// @param[in] poolSto pointer to the memory buffer for pool storage
/// @param[in] poolSize size of the storage buffer in bytes
/// @param[in] blockSize fixed-size of the memory blocks in bytes
///
/// @attention
/// The caller of QP::QMPool::init() must make sure that the @p poolSto
/// pointer is properly __aligned__. In particular, it must be possible to
/// efficiently store a pointer at the location pointed to by @p poolSto.
/// Internally, the QP::QMPool::init() function rounds up the block size
/// @p blockSize so that it can fit an integer number of pointers.
/// This is done to achieve proper alignment of the blocks within the pool.
///
/// @note Due to the rounding of block size the actual capacity of the pool
/// might be less than (@p poolSize / @p blockSize). You can check the
/// capacity of the pool by calling the QP::QF::getPoolMin() function.
///
/// @note This function is __not__ protected by a critical section, because
/// it is intended to be called only during the initialization of the system,
/// when interrupts are not allowed yet.
///
/// @note Many QF ports use memory pools to implement the event pools.
///
void QMPool::init(void * const poolSto, uint_fast32_t poolSize,
uint_fast16_t blockSize)
{
/// @pre The memory block must be valid and
/// the poolSize must fit at least one free block and
/// the blockSize must not be too close to the top of the dynamic range
Q_REQUIRE_ID(100, (poolSto != static_cast<void *>(0))
&& (poolSize >= static_cast<uint_fast32_t>(sizeof(QFreeBlock)))
&& (static_cast<uint_fast16_t>(
blockSize + static_cast<uint_fast16_t>(sizeof(QFreeBlock)))
> blockSize));
m_free_head = poolSto;
// round up the blockSize to fit an integer number of pointers...
//start with one
m_blockSize = static_cast<QMPoolSize>(sizeof(QFreeBlock));
//# free blocks in a memory block
uint_fast16_t nblocks = static_cast<uint_fast16_t>(1);
while (m_blockSize < static_cast<QMPoolSize>(blockSize)) {
m_blockSize += static_cast<QMPoolSize>(sizeof(QFreeBlock));
++nblocks;
}
// use rounded-up value
blockSize = static_cast<uint_fast16_t>(m_blockSize);
// the whole pool buffer must fit at least one rounded-up block
Q_ASSERT_ID(110, poolSize >= static_cast<uint_fast32_t>(blockSize));
// chain all blocks together in a free-list...
// don't count the last block
poolSize -= static_cast<uint_fast32_t>(blockSize);
m_nTot = static_cast<QMPoolCtr>(1); // one (the last) block in the pool
// start at the head of the free list
QFreeBlock *fb = static_cast<QFreeBlock *>(m_free_head);
// chain all blocks together in a free-list...
while (poolSize >= static_cast<uint_fast32_t>(blockSize)) {
fb->m_next = &QF_PTR_AT_(fb, nblocks); // setup the next link
fb = fb->m_next; // advance to next block
// reduce the available pool size
poolSize -= static_cast<uint_fast32_t>(blockSize);
++m_nTot; // increment the number of blocks so far
}
fb->m_next = static_cast<QFreeBlock *>(0); // the last link points to NULL
m_nFree = m_nTot; // all blocks are free
m_nMin = m_nTot; // the minimum number of free blocks
m_start = poolSto; // the original start this pool buffer
m_end = fb; // the last block in this pool
QS_CRIT_STAT_
QS_BEGIN_(QS_QF_MPOOL_INIT, QS::priv_.locFilter[QS::MP_OBJ], m_start)
QS_OBJ_(m_start); // the memory managed by this pool
QS_MPC_(m_nTot); // the total number of blocks
QS_END_()
}
//****************************************************************************
/// @description
/// Recycle a memory block to the fixed block-size memory pool.
///
/// @param[in] b pointer to the memory block that is being recycled
///
/// @attention
/// The recycled block must be allocated from the __same__ memory pool
/// to which it is returned.
///
/// @note This function can be called from any task level or ISR level.
///
/// @sa QP::QMPool::get()
///
void QMPool::put(void * const b) {
/// @pre # free blocks cannot exceed the total # blocks and
/// the block pointer must be in range to come from this pool.
///
Q_REQUIRE_ID(200, (m_nFree < m_nTot)
&& QF_PTR_RANGE_(b, m_start, m_end));
QF_CRIT_STAT_
QF_CRIT_ENTRY_();
static_cast<QFreeBlock*>(b)->m_next =
static_cast<QFreeBlock *>(m_free_head); // link into the free list
m_free_head = b; // set as new head of the free list
++m_nFree; // one more free block in this pool
QS_BEGIN_NOCRIT_(QS_QF_MPOOL_PUT,
QS::priv_.locFilter[QS::MP_OBJ], m_start)
QS_TIME_(); // timestamp
QS_OBJ_(m_start); // the memory managed by this pool
QS_MPC_(m_nFree); // the number of free blocks in the pool
QS_END_NOCRIT_()
QF_CRIT_EXIT_();
}
//****************************************************************************
/// @description
/// The function allocates a memory block from the pool and returns a pointer
/// to the block back to the caller.
///
/// @param[in] margin the minimum number of unused blocks still available
/// in the pool after the allocation.
///
/// @note This function can be called from any task level or ISR level.
///
/// @note The memory pool must be initialized before any events can
/// be requested from it. Also, the QP::QMPool::get() function uses internally
/// a QF critical section, so you should be careful not to call it from within
/// a critical section when nesting of critical section is not supported.
///
/// @attention
/// An allocated block must be later returned back to the same pool
/// from which it has been allocated.
///
/// @sa QP::QMPool::put()
///
void *QMPool::get(uint_fast16_t const margin) {
QFreeBlock *fb;
QF_CRIT_STAT_
QF_CRIT_ENTRY_();
// have the than margin?
if (m_nFree > static_cast<QMPoolCtr>(margin)) {
fb = static_cast<QFreeBlock *>(m_free_head); // get a free block
// the pool has some free blocks, so a free block must be available
Q_ASSERT_ID(310, fb != static_cast<QFreeBlock *>(0));
void *fb_next = fb->m_next; // put volatile to a temporary to avoid UB
// is the pool becoming empty?
--m_nFree; // one free block less
if (m_nFree == static_cast<QMPoolCtr>(0)) {
// pool is becoming empty, so the next free block must be NULL
Q_ASSERT_ID(320, fb_next == static_cast<QFreeBlock *>(0));
m_nMin = static_cast<QMPoolCtr>(0);// remember that pool got empty
}
else {
// pool is not empty, so the next free block must be in range
//
// NOTE: the next free block pointer can fall out of range
// when the client code writes past the memory block, thus
// corrupting the next block.
Q_ASSERT_ID(330, QF_PTR_RANGE_(fb_next, m_start, m_end));
// is the number of free blocks the new minimum so far?
if (m_nMin > m_nFree) {
m_nMin = m_nFree; // remember the minimum so far
}
}
m_free_head = fb_next; // adjust list head to the next free block
QS_BEGIN_NOCRIT_(QS_QF_MPOOL_GET,
QS::priv_.locFilter[QS::MP_OBJ], m_start)
QS_TIME_(); // timestamp
QS_OBJ_(m_start); // the memory managed by this pool
QS_MPC_(m_nFree); // the number of free blocks in the pool
QS_MPC_(m_nMin); // the mninimum # free blocks in the pool
QS_END_NOCRIT_()
}
else {
fb = static_cast<QFreeBlock *>(0);
QS_BEGIN_NOCRIT_(QS_QF_MPOOL_GET_ATTEMPT,
QS::priv_.locFilter[QS::MP_OBJ], m_start)
QS_TIME_(); // timestamp
QS_OBJ_(m_start); // the memory managed by this pool
QS_MPC_(m_nFree); // the # free blocks in the pool
QS_MPC_(margin); // the requested margin
QS_END_NOCRIT_()
}
QF_CRIT_EXIT_();
return fb; // return the block or NULL pointer to the caller
}
//****************************************************************************
/// @description
/// This function obtains the minimum number of free blocks in the given
/// event pool since this pool has been initialized by a call to
/// QP::QF::poolInit().
///
/// @param[in] poolId event pool ID in the range 1..QF_maxPool_, where
/// QF_maxPool_ is the number of event pools initialized
/// with the function QP::QF::poolInit().
///
/// @returns the minimum number of unused blocks in the given event pool.
///
uint_fast16_t QF::getPoolMin(uint_fast8_t const poolId) {
/// @pre the poolId must be in range
Q_REQUIRE_ID(400, (static_cast<uint_fast8_t>(1) <= poolId)
&& (poolId <= QF_maxPool_));
QF_CRIT_STAT_
QF_CRIT_ENTRY_();
uint_fast16_t min = static_cast<uint_fast16_t>(
QF_pool_[poolId - static_cast<uint_fast8_t>(1)].m_nMin);
QF_CRIT_EXIT_();
return min;
}
} // namespace QP

View File

@ -1,199 +0,0 @@
/// @file
/// @ingroup qf
/// @brief Internal (package scope) QF/C++ interface.
/// @cond
///***************************************************************************
/// Last updated for version 5.9.7
/// Last updated on 2017-08-25
///
/// Q u a n t u m L e a P s
/// ---------------------------
/// innovating embedded systems
///
/// Copyright (C) Quantum Leaps, LLC. All rights reserved.
///
/// This program is open source software: you can redistribute it and/or
/// modify it under the terms of the GNU General Public License as published
/// by the Free Software Foundation, either version 3 of the License, or
/// (at your option) any later version.
///
/// Alternatively, this program may be distributed and modified under the
/// terms of Quantum Leaps commercial licenses, which expressly supersede
/// the GNU General Public License and are specifically designed for
/// licensees interested in retaining the proprietary status of their code.
///
/// This program is distributed in the hope that it will be useful,
/// but WITHOUT ANY WARRANTY; without even the implied warranty of
/// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
/// GNU General Public License for more details.
///
/// You should have received a copy of the GNU General Public License
/// along with this program. If not, see <http://www.gnu.org/licenses/>.
///
/// Contact information:
/// https://state-machine.com
/// mailto:info@state-machine.com
///***************************************************************************
/// @endcond
#ifndef qf_pkg_h
#define qf_pkg_h
//! helper macro to cast const away from an event pointer @p e_
#define QF_EVT_CONST_CAST_(e_) const_cast<QEvt *>(e_)
// QF-specific critical section...
#ifndef QF_CRIT_STAT_TYPE
//! This is an internal macro for defining the critical section
//! status type.
/// @description
/// The purpose of this macro is to enable writing the same code for the
/// case when critical section status type is defined and when it is not.
/// If the macro #QF_CRIT_STAT_TYPE is defined, this internal macro
/// provides the definition of the critical section status variable.
/// Otherwise this macro is empty.
/// @sa #QF_CRIT_STAT_TYPE
#define QF_CRIT_STAT_
//! This is an internal macro for entering a critical section.
/// @description
/// The purpose of this macro is to enable writing the same code for the
/// case when critical section status type is defined and when it is not.
/// If the macro #QF_CRIT_STAT_TYPE is defined, this internal macro
/// invokes #QF_CRIT_ENTRY passing the key variable as the parameter.
/// Otherwise #QF_CRIT_ENTRY is invoked with a dummy parameter.
/// @sa #QF_CRIT_ENTRY
#define QF_CRIT_ENTRY_() QF_CRIT_ENTRY(dummy)
//! This is an internal macro for exiting a critical section.
/// @description
/// The purpose of this macro is to enable writing the same code for the
/// case when critical section status type is defined and when it is not.
/// If the macro #QF_CRIT_STAT_TYPE is defined, this internal macro
/// invokes #QF_CRIT_EXIT passing the key variable as the parameter.
/// Otherwise #QF_CRIT_EXIT is invoked with a dummy parameter.
/// @sa #QF_CRIT_EXIT
///
#define QF_CRIT_EXIT_() QF_CRIT_EXIT(dummy)
#else
#define QF_CRIT_STAT_ QF_CRIT_STAT_TYPE critStat_;
#define QF_CRIT_ENTRY_() QF_CRIT_ENTRY(critStat_)
#define QF_CRIT_EXIT_() QF_CRIT_EXIT(critStat_)
#endif // QF_CRIT_STAT_TYPE
namespace QP {
// package-scope objects -----------------------------------------------------
extern QF_EPOOL_TYPE_ QF_pool_[QF_MAX_EPOOL]; //!< allocate event pools
extern uint_fast8_t QF_maxPool_; //!< # of initialized event pools
extern QSubscrList *QF_subscrList_; //!< the subscriber list array
extern enum_t QF_maxPubSignal_; //!< the maximum published signal
//............................................................................
//! Structure representing a free block in the Native QF Memory Pool
/// @sa QP::QMPool
struct QFreeBlock {
QFreeBlock * volatile m_next; //!< link to the next free block
};
//****************************************************************************
// internal helper inline functions
//! increment the refCtr_ of an event @p e
inline void QF_EVT_REF_CTR_INC_(QEvt const * const e) {
++(QF_EVT_CONST_CAST_(e))->refCtr_;
}
//! decrement the refCtr_ of an event @p e
inline void QF_EVT_REF_CTR_DEC_(QEvt const * const e) {
--(QF_EVT_CONST_CAST_(e))->refCtr_;
}
//! macro to test that a pointer @p x_ is in range between @p min_ and @p max_
/// @description
/// This macro is specifically and exclusively used for checking the range
/// of a block pointer returned to the pool. Such a check must rely on the
/// pointer arithmetic not compliant with the MISRA-C++:2008 rules ??? and
/// ???. Defining a specific macro for this purpose allows to selectively
/// disable the warnings for this particular case.
#define QF_PTR_RANGE_(x_, min_, max_) (((min_) <= (x_)) && ((x_) <= (max_)))
} // namespace QP
//! access element at index @p i_ from the base pointer @p base_
#define QF_PTR_AT_(base_, i_) (base_[i_])
//****************************************************************************
#ifdef Q_SPY // QS software tracing enabled?
#if (QF_EQUEUE_CTR_SIZE == 1)
//! Internal QS macro to output an unformatted event queue
//! counter data element
/// @note the counter size depends on the macro #QF_EQUEUE_CTR_SIZE.
#define QS_EQC_(ctr_) QS::u8_(static_cast<uint8_t>(ctr_))
#elif (QF_EQUEUE_CTR_SIZE == 2)
#define QS_EQC_(ctr_) QS::u16_(static_cast<uint16_t>(ctr_))
#elif (QF_EQUEUE_CTR_SIZE == 4)
#define QS_EQC_(ctr_) QS::u32_(static_cast<uint32_t>(ctr_))
#else
#error "QF_EQUEUE_CTR_SIZE not defined"
#endif
#if (QF_EVENT_SIZ_SIZE == 1)
//! Internal QS macro to output an unformatted event size
//! data element
/// @note the event size depends on the macro #QF_EVENT_SIZ_SIZE.
#define QS_EVS_(size_) QS::u8_(static_cast<uint8_t>(size_))
#elif (QF_EVENT_SIZ_SIZE == 2)
#define QS_EVS_(size_) QS::u16_(static_cast<uint16_t>(size_))
#elif (QF_EVENT_SIZ_SIZE == 4)
#define QS_EVS_(size_) QS::u32_(static_cast<uint32_t>(size_))
#endif
#if (QF_MPOOL_SIZ_SIZE == 1)
//! Internal QS macro to output an unformatted memory pool
/// block-size data element
/// @note the block-size depends on the macro #QF_MPOOL_SIZ_SIZE.
#define QS_MPS_(size_) QS::u8_(static_cast<uint8_t>(size_))
#elif (QF_MPOOL_SIZ_SIZE == 2)
#define QS_MPS_(size_) QS::u16_(static_cast<uint16_t>(size_))
#elif (QF_MPOOL_SIZ_SIZE == 4)
#define QS_MPS_(size_) QS::u32_(static_cast<uint32_t>(size_))
#endif
#if (QF_MPOOL_CTR_SIZE == 1)
//! Internal QS macro to output an unformatted memory pool
//! block-counter data element
/// @note the counter size depends on the macro #QF_MPOOL_CTR_SIZE.
#define QS_MPC_(ctr_) QS::u8_(static_cast<uint8_t>(ctr_))
#elif (QF_MPOOL_CTR_SIZE == 2)
#define QS_MPC_(ctr_) QS::u16_(static_cast<uint16_t>(ctr_))
#elif (QF_MPOOL_CTR_SIZE == 4)
#define QS_MPC_(ctr_) QS::u32_(static_cast<uint32_t>(ctr_))
#endif
#if (QF_TIMEEVT_CTR_SIZE == 1)
//! Internal QS macro to output an unformatted time event
//! tick-counter data element
/// @note the counter size depends on the macro #QF_TIMEEVT_CTR_SIZE.
#define QS_TEC_(ctr_) QS::u8_(static_cast<uint8_t>(ctr_))
#elif (QF_TIMEEVT_CTR_SIZE == 2)
#define QS_TEC_(ctr_) QS::u16_(static_cast<uint16_t>(ctr_))
#elif (QF_TIMEEVT_CTR_SIZE == 4)
#define QS_TEC_(ctr_) QS::u32_(static_cast<uint32_t>(ctr_))
#endif
#endif // Q_SPY
#endif // qf_pkg_h

View File

@ -1,306 +0,0 @@
/// @file
/// @brief QF/C++ Publish-Subscribe services
/// definitions.
/// @ingroup qf
/// @cond
///***************************************************************************
/// Last updated for version 5.9.0
/// Last updated on 2017-05-08
///
/// Q u a n t u m L e a P s
/// ---------------------------
/// innovating embedded systems
///
/// Copyright (C) Quantum Leaps, LLC. All rights reserved.
///
/// This program is open source software: you can redistribute it and/or
/// modify it under the terms of the GNU General Public License as published
/// by the Free Software Foundation, either version 3 of the License, or
/// (at your option) any later version.
///
/// Alternatively, this program may be distributed and modified under the
/// terms of Quantum Leaps commercial licenses, which expressly supersede
/// the GNU General Public License and are specifically designed for
/// licensees interested in retaining the proprietary status of their code.
///
/// This program is distributed in the hope that it will be useful,
/// but WITHOUT ANY WARRANTY; without even the implied warranty of
/// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
/// GNU General Public License for more details.
///
/// You should have received a copy of the GNU General Public License
/// along with this program. If not, see <http://www.gnu.org/licenses/>.
///
/// Contact information:
/// https://state-machine.com
/// mailto:info@state-machine.com
///***************************************************************************
/// @endcond
#define QP_IMPL // this is QP implementation
#include "qf_port.h" // QF port
#include "qf_pkg.h" // QF package-scope interface
#include "qassert.h" // QP embedded systems-friendly assertions
#ifdef Q_SPY // QS software tracing enabled?
#include "qs_port.h" // include QS port
#else
#include "qs_dummy.h" // disable the QS software tracing
#endif // Q_SPY
namespace QP {
Q_DEFINE_THIS_MODULE("qf_ps")
// Package-scope objects *****************************************************
QSubscrList *QF_subscrList_;
enum_t QF_maxPubSignal_;
//****************************************************************************
/// @description
/// This function initializes the publish-subscribe facilities of QF and must
/// be called exactly once before any subscriptions/publications occur in
/// the application.
///
/// @param[in] subscrSto pointer to the array of subscriber lists
/// @param[in] maxSignal the dimension of the subscriber array and at
/// the same time the maximum signal that can be
/// published or subscribed.
///
/// The array of subscriber-lists is indexed by signals and provides a mapping
/// between the signals and subscriber-lists. The subscriber-lists are
/// bitmasks of type QP::QSubscrList, each bit in the bit mask corresponding
/// to the unique priority of an active object. The size of the
/// QP::QSubscrList bitmask depends on the value of the #QF_MAX_ACTIVE macro.
///
/// @note The publish-subscribe facilities are optional, meaning that you
/// might choose not to use publish-subscribe. In that case calling
/// QF::psInit() and using up memory for the subscriber-lists is unnecessary.
///
/// @sa QP::QSubscrList
///
/// @usage
/// The following example shows the typical initialization sequence of QF:
/// @include qf_main.cpp
///
void QF::psInit(QSubscrList * const subscrSto, enum_t const maxSignal) {
QF_subscrList_ = subscrSto;
QF_maxPubSignal_ = maxSignal;
// zero the subscriber list, so that the framework can start correctly
// even if the startup code fails to clear the uninitialized data
// (as is required by the C++ Standard)
bzero(subscrSto,
static_cast<uint_fast16_t>(static_cast<uint_fast16_t>(maxSignal)
* static_cast<uint_fast16_t>(sizeof(QSubscrList))));
}
//****************************************************************************
/// @description
/// This function posts (using the FIFO policy) the event @a e to **all**
/// active objects that have subscribed to the signal @a e->sig, which is
/// called _multicasting_. The multicasting performed in this function is
/// very efficient based on reference-counting inside the published event
/// ("zero-copy" event multicasting). This function is designed to be
/// callable from any part of the system, including ISRs, device drivers,
/// and active objects.
///
/// @note
/// To avoid any unexpected re-ordering of events posted into AO queues,
/// the event multicasting is performed with scheduler __locked__. However,
/// the scheduler is locked only up to the priority level of the highest-
/// priority subscriber, so any AOs of even higher priority, which did not
/// subscribe to this event are _not_ affected.
///
#ifndef Q_SPY
void QF::publish_(QEvt const * const e) {
#else
void QF::publish_(QEvt const * const e, void const * const sender) {
#endif
/// @pre the published signal must be within the configured range
Q_REQUIRE_ID(100, static_cast<enum_t>(e->sig) < QF_maxPubSignal_);
QF_CRIT_STAT_
QF_CRIT_ENTRY_();
QS_BEGIN_NOCRIT_(QS_QF_PUBLISH,
static_cast<void *>(0), static_cast<void *>(0))
QS_TIME_(); // the timestamp
QS_OBJ_(sender); // the sender object
QS_SIG_(e->sig); // the signal of the event
QS_2U8_(e->poolId_, e->refCtr_); // pool Id & refCtr of the evt
QS_END_NOCRIT_()
// is it a dynamic event?
if (e->poolId_ != static_cast<uint8_t>(0)) {
// NOTE: The reference counter of a dynamic event is incremented to
// prevent premature recycling of the event while the multicasting
// is still in progress. At the end of the function, the garbage
// collector step (QF::gc()) decrements the reference counter and
// recycles the event if the counter drops to zero. This covers the
// case when the event was published without any subscribers.
//
QF_EVT_REF_CTR_INC_(e);
}
// make a local, modifiable copy of the subscriber list
QPSet subscrList = QF_PTR_AT_(QF_subscrList_, e->sig);
QF_CRIT_EXIT_();
if (subscrList.notEmpty()) {
uint_fast8_t p = subscrList.findMax(); // the highest-prio subscriber
QF_SCHED_STAT_
QF_SCHED_LOCK_(p); // lock the scheduler up to prio 'p'
do { // loop over all subscribers */
// the prio of the AO must be registered with the framework
Q_ASSERT_ID(210, active_[p] != static_cast<QActive *>(0));
// POST() asserts internally if the queue overflows
(void)active_[p]->POST(e, sender);
subscrList.remove(p); // remove the handled subscriber
if (subscrList.notEmpty()) { // still more subscribers?
p = subscrList.findMax(); // the highest-prio subscriber
}
else {
p = static_cast<uint_fast8_t>(0); // no more subscribers
}
} while (p != static_cast<uint_fast8_t>(0));
QF_SCHED_UNLOCK_(); // unlock the scheduler
}
// The following garbage collection step decrements the reference counter
// and recycles the event if the counter drops to zero. This covers both
// cases when the event was published with or without any subscribers.
//
gc(e);
}
//****************************************************************************
/// @description
/// This function is part of the Publish-Subscribe event delivery mechanism
/// available in QF. Subscribing to an event means that the framework will
/// start posting all published events with a given signal @p sig to the
/// event queue of the active object.
///
/// @param[in] sig event signal to subscribe
///
/// The following example shows how the Table active object subscribes
/// to three signals in the initial transition:
/// @include qf_subscribe.c
///
/// @sa QP::QF::publish_(), QP::QActive::unsubscribe(), and
/// QP::QActive::unsubscribeAll()
///
void QActive::subscribe(enum_t const sig) const {
uint_fast8_t p = m_prio;
Q_REQUIRE_ID(300, (Q_USER_SIG <= sig)
&& (sig < QF_maxPubSignal_)
&& (static_cast<uint_fast8_t>(0) < p)
&& (p <= static_cast<uint_fast8_t>(QF_MAX_ACTIVE))
&& (QF::active_[p] == this));
QF_CRIT_STAT_
QF_CRIT_ENTRY_();
QS_BEGIN_NOCRIT_(QS_QF_ACTIVE_SUBSCRIBE,
QS::priv_.locFilter[QS::AO_OBJ], this)
QS_TIME_(); // timestamp
QS_SIG_(sig); // the signal of this event
QS_OBJ_(this); // this active object
QS_END_NOCRIT_()
QF_PTR_AT_(QF_subscrList_, sig).insert(p); // insert into subscriber-list
QF_CRIT_EXIT_();
}
//****************************************************************************
/// @description
/// This function is part of the Publish-Subscribe event delivery mechanism
/// available in QF. Un-subscribing from an event means that the framework
/// will stop posting published events with a given signal @p sig to the
/// event queue of the active object.
///
/// @param[in] sig event signal to unsubscribe
///
/// @note Due to the latency of event queues, an active object should NOT
/// assume that a given signal @p sig will never be dispatched to the
/// state machine of the active object after un-subscribing from that signal.
/// The event might be already in the queue, or just about to be posted
/// and the un-subscribe operation will not flush such events.
///
/// @note Un-subscribing from a signal that has never been subscribed in the
/// first place is considered an error and QF will raise an assertion.
///
/// @sa QP::QF::publish_(), QP::QActive::subscribe(), and
/// QP::QActive::unsubscribeAll()
void QActive::unsubscribe(enum_t const sig) const {
uint_fast8_t p = m_prio;
Q_REQUIRE_ID(400, (Q_USER_SIG <= sig)
&& (sig < QF_maxPubSignal_)
&& (static_cast<uint_fast8_t>(0) < p)
&& (p <= static_cast<uint_fast8_t>(QF_MAX_ACTIVE))
&& (QF::active_[p] == this));
QF_CRIT_STAT_
QF_CRIT_ENTRY_();
QS_BEGIN_NOCRIT_(QS_QF_ACTIVE_UNSUBSCRIBE,
QS::priv_.locFilter[QS::AO_OBJ], this)
QS_TIME_(); // timestamp
QS_SIG_(sig); // the signal of this event
QS_OBJ_(this); // this active object
QS_END_NOCRIT_()
QF_PTR_AT_(QF_subscrList_,sig).remove(p); // remove from subscriber-list
QF_CRIT_EXIT_();
}
//****************************************************************************
/// @description
/// This function is part of the Publish-Subscribe event delivery mechanism
/// available in QF. Un-subscribing from all events means that the framework
/// will stop posting any published events to the event queue of the active
/// object.
///
/// @note Due to the latency of event queues, an active object should NOT
/// assume that no events will ever be dispatched to the state machine of
/// the active object after un-subscribing from all events.
/// The events might be already in the queue, or just about to be posted
/// and the un-subscribe operation will not flush such events. Also, the
/// alternative event-delivery mechanisms, such as direct event posting or
/// time events, can be still delivered to the event queue of the active
/// object.
///
/// @sa QP::QF::publish_(), QP::QActive::subscribe(), and
/// QP::QActive::unsubscribe()
///
void QActive::unsubscribeAll(void) const {
uint_fast8_t const p = m_prio;
Q_REQUIRE_ID(500, (static_cast<uint_fast8_t>(0) < p)
&& (p <= static_cast<uint_fast8_t>(QF_MAX_ACTIVE))
&& (QF::active_[p] == this));
for (enum_t sig = Q_USER_SIG; sig < QF_maxPubSignal_; ++sig) {
QF_CRIT_STAT_
QF_CRIT_ENTRY_();
if (QF_PTR_AT_(QF_subscrList_, sig).hasElement(p)) {
QF_PTR_AT_(QF_subscrList_, sig).remove(p);
QS_BEGIN_NOCRIT_(QS_QF_ACTIVE_UNSUBSCRIBE,
QS::priv_.locFilter[QS::AO_OBJ], this)
QS_TIME_(); // timestamp
QS_SIG_(sig); // the signal of this event
QS_OBJ_(this); // this active object
QS_END_NOCRIT_()
}
QF_CRIT_EXIT_();
}
}
} // namespace QP

View File

@ -1,59 +0,0 @@
/// @file
/// @brief QP::QActive::QActive() definition
/// @cond
///***************************************************************************
/// Last updated for version 5.8.0
/// Last updated on 2016-11-19
///
/// Q u a n t u m L e a P s
/// ---------------------------
/// innovating embedded systems
///
/// Copyright (C) Quantum Leaps, www.state-machine.com.
///
/// This program is open source software: you can redistribute it and/or
/// modify it under the terms of the GNU General Public License as published
/// by the Free Software Foundation, either version 3 of the License, or
/// (at your option) any later version.
///
/// Alternatively, this program may be distributed and modified under the
/// terms of Quantum Leaps commercial licenses, which expressly supersede
/// the GNU General Public License and are specifically designed for
/// licensees interested in retaining the proprietary status of their code.
///
/// This program is distributed in the hope that it will be useful,
/// but WITHOUT ANY WARRANTY; without even the implied warranty of
/// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
/// GNU General Public License for more details.
///
/// You should have received a copy of the GNU General Public License
/// along with this program. If not, see <http://www.gnu.org/licenses/>.
///
/// Contact information:
/// Web: www.state-machine.com
/// Email: info@state-machine.com
///***************************************************************************
/// @endcond
#define QP_IMPL // this is QP implementation
#include "qf_port.h" // QF port
namespace QP {
//****************************************************************************
QActive::QActive(QStateHandler const initial)
: QHsm(initial),
m_prio(static_cast<uint_fast8_t>(0))
{
m_state.fun = Q_STATE_CAST(&QHsm::top);
#ifdef QF_OS_OBJECT_TYPE
QF::bzero(&m_osObject, static_cast<uint_fast16_t>(sizeof(m_osObject)));
#endif
#ifdef QF_THREAD_TYPE
QF::bzero(&m_thread, static_cast<uint_fast16_t>(sizeof(m_thread)));
#endif
}
} // namespace QP

View File

@ -1,322 +0,0 @@
/// @file
/// @brief QP::QEQueue implementation
/// @cond
///***************************************************************************
/// Last updated for version 5.9.6
/// Last updated on 2017-08-01
///
/// Q u a n t u m L e a P s
/// ---------------------------
/// innovating embedded systems
///
/// Copyright (C) Quantum Leaps, www.state-machine.com.
///
/// This program is open source software: you can redistribute it and/or
/// modify it under the terms of the GNU General Public License as published
/// by the Free Software Foundation, either version 3 of the License, or
/// (at your option) any later version.
///
/// Alternatively, this program may be distributed and modified under the
/// terms of Quantum Leaps commercial licenses, which expressly supersede
/// the GNU General Public License and are specifically designed for
/// licensees interested in retaining the proprietary status of their code.
///
/// This program is distributed in the hope that it will be useful,
/// but WITHOUT ANY WARRANTY; without even the implied warranty of
/// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
/// GNU General Public License for more details.
///
/// You should have received a copy of the GNU General Public License
/// along with this program. If not, see <http://www.gnu.org/licenses/>.
///
/// Contact information:
/// Web: www.state-machine.com
/// Email: info@state-machine.com
///***************************************************************************
/// @endcond
#define QP_IMPL // this is QP implementation
#include "qf_port.h" // QF port
#include "qf_pkg.h" // QF package-scope interface
#include "qassert.h" // QP embedded systems-friendly assertions
#ifdef Q_SPY // QS software tracing enabled?
#include "qs_port.h" // include QS port
#else
#include "qs_dummy.h" // disable the QS software tracing
#endif // Q_SPY
namespace QP {
Q_DEFINE_THIS_MODULE("qf_qeq")
//****************************************************************************
/// @description
/// Default constructor
///
QEQueue::QEQueue(void)
: m_frontEvt(static_cast<QEvt const *>(0)),
m_ring(static_cast<QEvt const **>(0)),
m_end(static_cast<QEQueueCtr>(0)),
m_head(static_cast<QEQueueCtr>(0)),
m_tail(static_cast<QEQueueCtr>(0)),
m_nFree(static_cast<QEQueueCtr>(0)),
m_nMin(static_cast<QEQueueCtr>(0))
{}
//****************************************************************************
/// @description
/// Initialize the event queue by giving it the storage for the ring buffer.
///
/// @param[in] qSto an array of pointers to QP::QEvt to sereve as the
/// ring buffer for the event queue
/// @param[in] qLen the length of the qSto[] buffer (in QP::QEvt pointers)
///
/// @note The actual capacity of the queue is qLen + 1, because of the extra
/// location forntEvt.
///
/// @note This function is also used to initialize the event queues of active
/// objects in the built-int QK and "Vanilla" kernels, as well as other
/// QP ports to OSes/RTOSes that do provide a suitable message queue.
///
void QEQueue::init(QEvt const *qSto[], uint_fast16_t const qLen) {
m_frontEvt = static_cast<QEvt const *>(0); // no events in the queue
m_ring = &qSto[0];
m_end = static_cast<QEQueueCtr>(qLen);
if (qLen > static_cast<uint_fast16_t>(0)) {
m_head = static_cast<QEQueueCtr>(0);
m_tail = static_cast<QEQueueCtr>(0);
}
m_nFree = static_cast<QEQueueCtr>(
qLen + static_cast<uint_fast16_t>(1)); //+1 for frontEvt
m_nMin = m_nFree;
QS_CRIT_STAT_
QS_BEGIN_(QS_QF_EQUEUE_INIT, QS::priv_.locFilter[QS::EQ_OBJ], this)
QS_OBJ_(this); // this QEQueue object
QS_EQC_(m_end); // the length of the queue
QS_END_()
}
//****************************************************************************
/// @description
/// Post an event to the "raw" thread-safe event queue using the
/// First-In-First-Out (FIFO) order.
///
/// @param[in] e pointer to the event to be posted to the queue
/// @param[in] margin number of required free slots in the queue after
/// posting the event. The special value QP::QF_NO_MARGIN
/// means that this function will assert if posting
/// @note
/// The QP::QF_NO_MARGIN value of the @p margin argument is special and
/// denotes situation when the post() operation is assumed to succeed (event
/// delivery guarantee). An assertion fires, when the event cannot be
/// delivered in this case.
///
/// @returns 'true' (success) when the posting succeeded with the provided
/// margin and 'false' (failure) when the posting fails.
///
/// @note This function can be called from any task context or ISR context.
///
/// @sa QP::QEQueue::postLIFO(), QP::QEQueue::get()
///
bool QEQueue::post(QEvt const * const e, uint_fast16_t const margin) {
bool status;
QF_CRIT_STAT_
/// @pre the event must be valid
Q_REQUIRE_ID(200, e != static_cast<QEvt const *>(0));
QF_CRIT_ENTRY_();
QEQueueCtr nFree = m_nFree; // temporary to avoid UB for volatile access
// margin available?
if (((margin == QF_NO_MARGIN) && (nFree > static_cast<QEQueueCtr>(0)))
|| (nFree > static_cast<QEQueueCtr>(margin)))
{
QS_BEGIN_NOCRIT_(QS_QF_EQUEUE_POST_FIFO,
QS::priv_.locFilter[QS::EQ_OBJ], this)
QS_TIME_(); // timestamp
QS_SIG_(e->sig); // the signal of this event
QS_OBJ_(this); // this queue object
QS_2U8_(e->poolId_, e->refCtr_); // pool Id & refCtr of the evt
QS_EQC_(nFree); // number of free entries
QS_EQC_(m_nMin); // min number of free entries
QS_END_NOCRIT_()
// is it a dynamic event?
if (e->poolId_ != static_cast<uint8_t>(0)) {
QF_EVT_REF_CTR_INC_(e); // increment the reference counter
}
--nFree; // one free entry just used up
m_nFree = nFree; // update the volatile
if (m_nMin > nFree) {
m_nMin = nFree; // update minimum so far
}
// is the queue empty?
if (m_frontEvt == static_cast<QEvt const *>(0)) {
m_frontEvt = e; // deliver event directly
}
// queue is not empty, leave event in the ring-buffer
else {
// insert event into the ring buffer (FIFO)
QF_PTR_AT_(m_ring, m_head) = e; // insert e into buffer
// need to wrap?
if (m_head == static_cast<QEQueueCtr>(0)) {
m_head = m_end; // wrap around
}
--m_head;
}
status = true; // event posted successfully
}
else {
/// @note assert if event cannot be posted and dropping events is
/// not acceptable
Q_ASSERT_ID(210, margin != QF_NO_MARGIN);
QS_BEGIN_NOCRIT_(QS_QF_EQUEUE_POST_ATTEMPT,
QS::priv_.locFilter[QS::EQ_OBJ], this)
QS_TIME_(); // timestamp
QS_SIG_(e->sig); // the signal of this event
QS_OBJ_(this); // this queue object
QS_2U8_(e->poolId_, e->refCtr_); // pool Id & refCtr of the evt
QS_EQC_(nFree); // number of free entries
QS_EQC_(static_cast<QEQueueCtr>(margin)); // margin requested
QS_END_NOCRIT_()
status = false; // event not posted
}
QF_CRIT_EXIT_();
return status;
}
//****************************************************************************
/// @description
/// Post an event to the "raw" thread-safe event queue using the
/// Last-In-First-Out (LIFO) order.
///
/// @param[in] e pointer to the event to be posted to the queue
///
/// @attention The LIFO policy should be used only with great __caution__,
/// because it alters the order of events in the queue.
///
/// @note This function can be called from any task context or ISR context.
///
/// @note this function is used for the "raw" thread-safe queues and __not__
/// for the queues of active objects.
///
/// @sa QP::QEQueue::post(), QP::QEQueue::get(), QP::QActive::defer()
///
void QEQueue::postLIFO(QEvt const * const e) {
QF_CRIT_STAT_
QF_CRIT_ENTRY_();
QEQueueCtr nFree = m_nFree; // temporary to avoid UB for volatile access
/// @pre the queue must be able to accept the event (cannot overflow)
Q_REQUIRE_ID(300, nFree != static_cast<QEQueueCtr>(0));
QS_BEGIN_NOCRIT_(QS_QF_EQUEUE_POST_LIFO,
QS::priv_.locFilter[QS::EQ_OBJ], this)
QS_TIME_(); // timestamp
QS_SIG_(e->sig); // the signal of this event
QS_OBJ_(this); // this queue object
QS_2U8_(e->poolId_, e->refCtr_); // pool Id & refCtr of the evt
QS_EQC_(nFree); // number of free entries
QS_EQC_(m_nMin); // min number of free entries
QS_END_NOCRIT_()
// is it a dynamic event?
if (e->poolId_ != static_cast<uint8_t>(0)) {
QF_EVT_REF_CTR_INC_(e); // increment the reference counter
}
--nFree; // one free entry just used up
m_nFree = nFree; // update the volatile
if (m_nMin > nFree) {
m_nMin = nFree; // update minimum so far
}
QEvt const *frontEvt = m_frontEvt; // read volatile into temporary
m_frontEvt = e; // deliver event directly to the front of the queue
// was the queue not empty?
if (frontEvt != static_cast<QEvt const *>(0)) {
++m_tail;
if (m_tail == m_end) { // need to wrap the tail?
m_tail = static_cast<QEQueueCtr>(0); // wrap around
}
QF_PTR_AT_(m_ring, m_tail) = frontEvt; // buffer the old front evt
}
QF_CRIT_EXIT_();
}
//****************************************************************************
/// @description
/// Retrieves an event from the front of the "raw" thread-safe queue and
/// returns a pointer to this event to the caller.
///
/// @returns pointer to event at the front of the queue, if the queue is
/// not empty and NULL if the queue is empty.
///
/// @note this function is used for the "raw" thread-safe queues and __not__
/// for the queues of active objects.
///
/// @sa QP::QEQueue::post(), QP::QEQueue::postLIFO(), QP::QActive::recall()
///
QEvt const *QEQueue::get(void) {
QEvt const *e;
QF_CRIT_STAT_
QF_CRIT_ENTRY_();
e = m_frontEvt; // always remove the event from the front location
// is the queue not empty?
if (e != static_cast<QEvt const *>(0)) {
QEQueueCtr nFree = m_nFree + static_cast<QEQueueCtr>(1);
m_nFree = nFree; // upate the number of free
// any events in the the ring buffer?
if (nFree <= m_end) {
m_frontEvt = QF_PTR_AT_(m_ring, m_tail); // remove from the tail
if (m_tail == static_cast<QEQueueCtr>(0)) { // need to wrap?
m_tail = m_end; // wrap around
}
--m_tail;
QS_BEGIN_NOCRIT_(QS_QF_EQUEUE_GET,
QS::priv_.locFilter[QS::EQ_OBJ], this)
QS_TIME_(); // timestamp
QS_SIG_(e->sig); // the signal of this event
QS_OBJ_(this); // this queue object
QS_2U8_(e->poolId_, e->refCtr_);// pool Id & refCtr of the evt
QS_EQC_(nFree); // number of free entries
QS_END_NOCRIT_()
}
else {
m_frontEvt = static_cast<QEvt const *>(0); // queue becomes empty
// all entries in the queue must be free (+1 for fronEvt)
Q_ASSERT_ID(410, nFree == (m_end + static_cast<QEQueueCtr>(1)));
QS_BEGIN_NOCRIT_(QS_QF_EQUEUE_GET_LAST,
QS::priv_.locFilter[QS::EQ_OBJ],
this)
QS_TIME_(); // timestamp
QS_SIG_(e->sig); // the signal of this event
QS_OBJ_(this); // this queue object
QS_2U8_(e->poolId_, e->refCtr_);// pool Id & refCtr of the evt
QS_END_NOCRIT_()
}
}
QF_CRIT_EXIT_();
return e;
}
} // namespace QP

View File

@ -1,86 +0,0 @@
/// @file
/// @brief QMActive::QMActive() and virtual functions
/// @cond
///***************************************************************************
/// Last updated for version 5.9.0
/// Last updated on 2017-05-08
///
/// Q u a n t u m L e a P s
/// ---------------------------
/// innovating embedded systems
///
/// Copyright (C) Quantum Leaps, www.state-machine.com.
///
/// This program is open source software: you can redistribute it and/or
/// modify it under the terms of the GNU General Public License as published
/// by the Free Software Foundation, either version 3 of the License, or
/// (at your option) any later version.
///
/// Alternatively, this program may be distributed and modified under the
/// terms of Quantum Leaps commercial licenses, which expressly supersede
/// the GNU General Public License and are specifically designed for
/// licensees interested in retaining the proprietary status of their code.
///
/// This program is distributed in the hope that it will be useful,
/// but WITHOUT ANY WARRANTY; without even the implied warranty of
/// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
/// GNU General Public License for more details.
///
/// You should have received a copy of the GNU General Public License
/// along with this program. If not, see <http://www.gnu.org/licenses/>.
///
/// Contact information:
/// https://state-machine.com
/// mailto:info@state-machine.com
///***************************************************************************
/// @endcond
#define QP_IMPL // this is QP implementation
#include "qf_port.h" // QF port
//! Internal macro to cast a QP::QMActive pointer @p qact_ to QP::QMsm*
/// @note
/// Casting pointer to pointer pointer violates the MISRA-C++ 2008 Rule 5-2-7,
/// cast from pointer to pointer. Additionally this cast violates the MISRA-
/// C++ 2008 Rule 5-2-8 Unusual pointer cast (incompatible indirect types).
/// Encapsulating these violations in a macro allows to selectively suppress
/// this specific deviation.
#define QF_QMACTIVE_TO_QMSM_CAST_(qact_) \
reinterpret_cast<QMsm *>((qact_))
//! Internal macro to cast a QP::QMActive pointer @p qact_ to QP::QMsm const *
#define QF_QMACTIVE_TO_QMSM_CONST_CAST_(qact_) \
reinterpret_cast<QMsm const *>((qact_))
namespace QP {
//****************************************************************************
QMActive::QMActive(QStateHandler const initial)
: QActive(initial)
{
m_state.obj = &QMsm::msm_top_s;
m_temp.fun = initial;
}
//****************************************************************************
void QMActive::init(QEvt const * const e) {
QF_QMACTIVE_TO_QMSM_CAST_(this)->QMsm::init(e);
}
//****************************************************************************
void QMActive::init(void) {
QF_QMACTIVE_TO_QMSM_CAST_(this)->QMsm::init();
}
//****************************************************************************
void QMActive::dispatch(QEvt const * const e) {
QF_QMACTIVE_TO_QMSM_CAST_(this)->QMsm::dispatch(e);
}
//****************************************************************************
bool QMActive::isInState(QMState const * const st) const {
return QF_QMACTIVE_TO_QMSM_CONST_CAST_(this)->QMsm::isInState(st);
}
//****************************************************************************
QMState const *QMActive::childStateObj(QMState const * const parent) const {
return QF_QMACTIVE_TO_QMSM_CONST_CAST_(this)->QMsm::childStateObj(parent);
}
} // namespace QP

View File

@ -1,537 +0,0 @@
/// @file
/// @brief QF/C++ time events and time management services
/// @ingroup qf
/// @cond
///***************************************************************************
/// Last updated for version 5.9.0
/// Last updated on 2017-05-08
///
/// Q u a n t u m L e a P s
/// ---------------------------
/// innovating embedded systems
///
/// Copyright (C) Quantum Leaps, LLC. All rights reserved.
///
/// This program is open source software: you can redistribute it and/or
/// modify it under the terms of the GNU General Public License as published
/// by the Free Software Foundation, either version 3 of the License, or
/// (at your option) any later version.
///
/// Alternatively, this program may be distributed and modified under the
/// terms of Quantum Leaps commercial licenses, which expressly supersede
/// the GNU General Public License and are specifically designed for
/// licensees interested in retaining the proprietary status of their code.
///
/// This program is distributed in the hope that it will be useful,
/// but WITHOUT ANY WARRANTY; without even the implied warranty of
/// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
/// GNU General Public License for more details.
///
/// You should have received a copy of the GNU General Public License
/// along with this program. If not, see <http://www.gnu.org/licenses/>.
///
/// Contact information:
/// http:www.state-machine.com
/// mailto:info@state-machine.com
///***************************************************************************
/// @endcond
#define QP_IMPL // this is QP implementation
#include "qf_port.h" // QF port
#include "qf_pkg.h" // QF package-scope interface
#include "qassert.h" // QP embedded systems-friendly assertions
#ifdef Q_SPY // QS software tracing enabled?
#include "qs_port.h" // include QS port
#else
#include "qs_dummy.h" // disable the QS software tracing
#endif // Q_SPY
namespace QP {
Q_DEFINE_THIS_MODULE("qf_time")
// Package-scope objects *****************************************************
QTimeEvt QF::timeEvtHead_[QF_MAX_TICK_RATE]; // heads of time event lists
//****************************************************************************
/// @description
/// This function must be called periodically from a time-tick ISR or from
/// a task so that QF can manage the timeout events assigned to the given
/// system clock tick rate.
///
/// @param[in] tickRate system clock tick rate serviced in this call.
///
/// @note this function should be called only via the macro TICK_X()
///
/// @note the calls to QP::QF::tickX_() with different tick rate argument can
/// preempt each other. For example, higher clock tick rates might be
/// serviced from interrupts while others from tasks (active objects).
///
/// @sa QP::QTimeEvt.
///
#ifndef Q_SPY
void QF::tickX_(uint_fast8_t const tickRate)
#else
void QF::tickX_(uint_fast8_t const tickRate, void const * const sender)
#endif
{
QTimeEvt *prev = &timeEvtHead_[tickRate];
QF_CRIT_STAT_
QF_CRIT_ENTRY_();
QS_BEGIN_NOCRIT_(QS_QF_TICK, static_cast<void*>(0), static_cast<void*>(0))
QS_TEC_(static_cast<QTimeEvtCtr>(++prev->m_ctr)); // tick ctr
QS_U8_(static_cast<uint8_t>(tickRate)); // tick rate
QS_END_NOCRIT_()
// scan the linked-list of time events at this rate...
for (;;) {
QTimeEvt *t = prev->m_next; // advance down the time evt. list
// end of the list?
if (t == static_cast<QTimeEvt *>(0)) {
// any new time events armed since the last run of QF::tickX_()?
if (timeEvtHead_[tickRate].m_act != static_cast<void *>(0)) {
// sanity check
Q_ASSERT_ID(110, prev != static_cast<QTimeEvt *>(0));
prev->m_next = QF::timeEvtHead_[tickRate].toTimeEvt();
timeEvtHead_[tickRate].m_act = static_cast<void *>(0);
t = prev->m_next; // switch to the new list
}
else {
break; // all currently armed time evts. processed
}
}
// time event scheduled for removal?
if (t->m_ctr == static_cast<QTimeEvtCtr>(0)) {
prev->m_next = t->m_next;
t->refCtr_ &= static_cast<uint8_t>(0x7F); // mark as unlinked
// do NOT advance the prev pointer
QF_CRIT_EXIT_(); // exit crit. section to reduce latency
// prevent merging critical sections, see NOTE1 below
QF_CRIT_EXIT_NOP();
}
else {
--t->m_ctr;
// is time evt about to expire?
if (t->m_ctr == static_cast<QTimeEvtCtr>(0)) {
QActive *act = t->toActive(); // temporary for volatile
// periodic time evt?
if (t->m_interval != static_cast<QTimeEvtCtr>(0)) {
t->m_ctr = t->m_interval; // rearm the time event
prev = t; // advance to this time event
}
// one-shot time event: automatically disarm
else {
prev->m_next = t->m_next;
// mark as unlinked
t->refCtr_ &= static_cast<uint8_t>(0x7F);
// do NOT advance the prev pointer
QS_BEGIN_NOCRIT_(QS_QF_TIMEEVT_AUTO_DISARM,
QS::priv_.locFilter[QS::TE_OBJ], t)
QS_OBJ_(t); // this time event object
QS_OBJ_(act); // the target AO
QS_U8_(static_cast<uint8_t>(tickRate)); // tick rate
QS_END_NOCRIT_()
}
QS_BEGIN_NOCRIT_(QS_QF_TIMEEVT_POST,
QS::priv_.locFilter[QS::TE_OBJ], t)
QS_TIME_(); // timestamp
QS_OBJ_(t); // the time event object
QS_SIG_(t->sig); // signal of this time event
QS_OBJ_(act); // the target AO
QS_U8_(static_cast<uint8_t>(tickRate)); // tick rate
QS_END_NOCRIT_()
QF_CRIT_EXIT_(); // exit crit. section before posting
(void)act->POST(t, sender); // asserts if queue overflows
}
else {
prev = t; // advance to this time event
QF_CRIT_EXIT_(); // exit crit. section to reduce latency
// prevent merging critical sections, see NOTE1 below
QF_CRIT_EXIT_NOP();
}
}
QF_CRIT_ENTRY_(); // re-enter crit. section to continue
}
QF_CRIT_EXIT_();
}
//****************************************************************************
// NOTE1:
// In some QF ports the critical section exit takes effect only on the next
// machine instruction. If this case, the next instruction is another entry
// to a critical section, the critical section won't be really exited, but
// rather the two adjacent critical sections would be merged.
//
// The QF_CRIT_EXIT_NOP() macro contains minimal code required
// to prevent such merging of critical sections in QF ports,
// in which it can occur.
//****************************************************************************
/// @description
/// Find out if any time events are armed at the given clock tick rate.
///
/// @param[in] tickRate system clock tick rate to find out about.
///
/// @returns 'true' if no time events are armed at the given tick rate and
/// 'false' otherwise.
///
/// @note This function should be called in critical section.
///
bool QF::noTimeEvtsActiveX(uint_fast8_t const tickRate) {
/// @pre the tick rate must be in range
Q_REQUIRE_ID(200, tickRate < static_cast<uint_fast8_t>(QF_MAX_TICK_RATE));
bool inactive;
if (timeEvtHead_[tickRate].m_next == static_cast<QTimeEvt *>(0)) {
inactive = false;
}
else if (timeEvtHead_[tickRate].m_act == static_cast<void *>(0)) {
inactive = false;
}
else {
inactive = true;
}
return inactive;
}
//****************************************************************************
/// @description
/// When creating a time event, you must commit it to a specific active object
/// @p act, tick rate @p tickRate and event signal @p sgnl. You cannot change
/// these attributes later.
///
/// @param[in] act pointer to the active object associated with this
/// time event. The time event will post itself to this AO.
/// @param[in] sgnl signal to associate with this time event.
/// @param[in] tickRate system tick rate to associate with this time event.
///
QTimeEvt::QTimeEvt(QActive * const act,
enum_t const sgnl, uint_fast8_t const tickRate)
:
#ifdef Q_EVT_CTOR
QEvt(static_cast<QSignal>(sgnl)),
#endif
m_next(static_cast<QTimeEvt *>(0)),
m_act(act),
m_ctr(static_cast<QTimeEvtCtr>(0)),
m_interval(static_cast<QTimeEvtCtr>(0))
{
/// @pre The signal must be valid and the tick rate in range
Q_REQUIRE_ID(300, (sgnl >= Q_USER_SIG)
&& (tickRate < static_cast<uint_fast8_t>(QF_MAX_TICK_RATE)));
#ifndef Q_EVT_CTOR
sig = static_cast<QSignal>(sgnl); // set QEvt::sig of this time event
#endif
// Setting the POOL_ID event attribute to zero is correct only for
// events not allocated from event pools, which must be the case
// for Time Events.
//
poolId_ = static_cast<uint8_t>(0);
// The reference counter attribute is not used in static events,
// so for the Time Events it is reused to hold the tickRate in the
// bits [0..6] and the linkedFlag in the MSB (bit [7]). The linkedFlag
// is 0 for time events unlinked from any list and 1 otherwise.
//
refCtr_ = static_cast<uint8_t>(tickRate);
}
//****************************************************************************
/// @note
/// private default ctor for internal use only
///
QTimeEvt::QTimeEvt()
:
#ifdef Q_EVT_CTOR
QEvt(static_cast<QSignal>(0)),
#endif // Q_EVT_CTOR
m_next(static_cast<QTimeEvt *>(0)),
m_act(static_cast<QActive *>(0)),
m_ctr(static_cast<QTimeEvtCtr>(0)),
m_interval(static_cast<QTimeEvtCtr>(0))
{
#ifndef Q_EVT_CTOR
sig = static_cast<QSignal>(0);
#endif // Q_EVT_CTOR
// Setting the POOL_ID event attribute to zero is correct only for
// events not allocated from event pools, which must be the case
// for Time Events.
//
poolId_ = static_cast<uint8_t>(0); // not from any event pool
// The reference counter attribute is not used in static events,
// so for the Time Events it is reused to hold the tickRate in the
// bits [0..6] and the linkedFlag in the MSB (bit [7]). The linkedFlag
// is 0 for time events unlinked from any list and 1 otherwise.
//
refCtr_ = static_cast<uint8_t>(0); // default rate 0
}
//****************************************************************************
/// @description
/// Arms a time event to fire in a specified number of clock ticks and with
/// a specified interval. If the interval is zero, the time event is armed for
/// one shot ('one-shot' time event). The time event gets directly posted
/// (using the FIFO policy) into the event queue of the host active object.
///
/// @param[in] nTicks number of clock ticks (at the associated rate)
/// to rearm the time event with.
/// @param[in] interval interval (in clock ticks) for periodic time event.
///
/// @note After posting, a one-shot time event gets automatically disarmed
/// while a periodic time event (interval != 0) is automatically re-armed.
///
/// @note A time event can be disarmed at any time by calling
/// QP::QTimeEvt::disarm(). Also, a time event can be re-armed to fire in a
/// different number of clock ticks by calling the QP::QTimeEvt::rearm()
/// function.
///
/// @usage
/// The following example shows how to arm a one-shot time event from a state
/// machine of an active object:
/// @include qf_state.cpp
///
void QTimeEvt::armX(QTimeEvtCtr const nTicks, QTimeEvtCtr const interval) {
uint_fast8_t tickRate = static_cast<uint_fast8_t>(refCtr_)
& static_cast<uint_fast8_t>(0x7F);
QTimeEvtCtr cntr = m_ctr; // temporary to hold volatile
QF_CRIT_STAT_
/// @pre the host AO must be valid, time evnet must be disarmed,
/// number of clock ticks cannot be zero, and the signal must be valid.
///
Q_REQUIRE_ID(400, (m_act != static_cast<void *>(0))
&& (cntr == static_cast<QTimeEvtCtr>(0))
&& (nTicks != static_cast<QTimeEvtCtr>(0))
&& (tickRate < static_cast<uint_fast8_t>(QF_MAX_TICK_RATE))
&& (static_cast<enum_t>(sig) >= Q_USER_SIG));
QF_CRIT_ENTRY_();
m_ctr = nTicks;
m_interval = interval;
// is the time event unlinked?
// NOTE: For the duration of a single clock tick of the specified tick
// rate a time event can be disarmed and yet still linked into the list,
// because un-linking is performed exclusively in the QF_tickX() function.
//
if ((refCtr_ & static_cast<uint8_t>(0x80)) == static_cast<uint8_t>(0)) {
refCtr_ |= static_cast<uint8_t>(0x80); // mark as linked
// The time event is initially inserted into the separate
// "freshly armed" link list based on QF::timeEvtHead_[tickRate].act.
// Only later, inside the QF::tickX() function, the "freshly armed"
// list is appended to the main list of armed time events based on
// QF::timeEvtHead_[tickRate].next. Again, this is to keep any
// changes to the main list exclusively inside the QF::tickX()
// function.
//
m_next = QF::timeEvtHead_[tickRate].toTimeEvt();
QF::timeEvtHead_[tickRate].m_act = this;
}
QS_BEGIN_NOCRIT_(QS_QF_TIMEEVT_ARM, QS::priv_.locFilter[QS::TE_OBJ], this)
QS_TIME_(); // timestamp
QS_OBJ_(this); // this time event object
QS_OBJ_(m_act); // the active object
QS_TEC_(nTicks); // the number of ticks
QS_TEC_(interval); // the interval
QS_U8_(static_cast<uint8_t>(tickRate)); // tick rate
QS_END_NOCRIT_()
QF_CRIT_EXIT_();
}
//****************************************************************************
/// @description
/// Disarm the time event so it can be safely reused.
///
/// @returns 'true' if the time event was truly disarmed, that is, it
/// was running. The return of 'false' means that the time event was
/// not truly disarmed because it was not running. The 'false' return is only
/// possible for one-shot time events that have been automatically disarmed
/// upon expiration. In this case the 'false' return means that the time event
/// has already been posted or published and should be expected in the
/// active object's state machine.
///
/// @note there is no harm in disarming an already disarmed time event
////
bool QTimeEvt::disarm(void) {
QF_CRIT_STAT_
QF_CRIT_ENTRY_();
bool wasArmed;
// is the time event actually armed?
if (m_ctr != static_cast<QTimeEvtCtr>(0)) {
wasArmed = true;
QS_BEGIN_NOCRIT_(QS_QF_TIMEEVT_DISARM,
QS::priv_.locFilter[QS::TE_OBJ], this)
QS_TIME_(); // timestamp
QS_OBJ_(this); // this time event object
QS_OBJ_(m_act); // the target AO
QS_TEC_(m_ctr); // the number of ticks
QS_TEC_(m_interval); // the interval
// tick rate
QS_U8_(static_cast<uint8_t>(refCtr_& static_cast<uint8_t>(0x7F)));
QS_END_NOCRIT_()
m_ctr = static_cast<QTimeEvtCtr>(0); // schedule removal from the list
}
// the time event was already not running
else {
wasArmed = false;
QS_BEGIN_NOCRIT_(QS_QF_TIMEEVT_DISARM_ATTEMPT,
QS::priv_.locFilter[QS::TE_OBJ], this)
QS_TIME_(); // timestamp
QS_OBJ_(this); // this time event object
QS_OBJ_(m_act); // the target AO
// tick rate
QS_U8_(static_cast<uint8_t>(refCtr_& static_cast<uint8_t>(0x7F)));
QS_END_NOCRIT_()
}
QF_CRIT_EXIT_();
return wasArmed;
}
/****************************************************************************/
/**
* @description
* Rearms a time event with a new number of clock ticks. This function can
* be used to adjust the current period of a periodic time event or to
* prevent a one-shot time event from expiring (e.g., a watchdog time event).
* Rearming a periodic timer leaves the interval unchanged and is a convenient
* method to adjust the phasing of a periodic time event.
*
* @param[in] nTicks number of clock ticks (at the associated rate)
* to rearm the time event with.
*
* @returns 'true' if the time event was running as it
* was re-armed. The 'false' return means that the time event was
* not truly rearmed because it was not running. The 'false' return is only
* possible for one-shot time events that have been automatically disarmed
* upon expiration. In this case the 'false' return means that the time event
* has already been posted or published and should be expected in the
* active object's state machine.
*/
bool QTimeEvt::rearm(QTimeEvtCtr const nTicks) {
uint_fast8_t tickRate = static_cast<uint_fast8_t>(refCtr_)
& static_cast<uint_fast8_t>(0x7F);
QF_CRIT_STAT_
/// @pre AO must be valid, tick rate must be in range, nTicks must not
/// be zero, and the signal of this time event must be valid
///
Q_REQUIRE_ID(600, (m_act != static_cast<void *>(0))
&& (tickRate < static_cast<uint_fast8_t>(QF_MAX_TICK_RATE))
&& (nTicks != static_cast<QTimeEvtCtr>(0))
&& (static_cast<enum_t>(sig) >= Q_USER_SIG));
QF_CRIT_ENTRY_();
bool isArmed;
// is the time evt not running? */
if (m_ctr == static_cast<QTimeEvtCtr>(0)) {
isArmed = false;
// is the time event unlinked?
// NOTE: For a duration of a single clock tick of the specified
// tick rate a time event can be disarmed and yet still linked into
// the list, because unlinking is performed exclusively in the
// QF::tickX() function.
//
if ((refCtr_ & static_cast<uint8_t>(0x80))
== static_cast<uint8_t>(0))
{
refCtr_ |= static_cast<uint8_t>(0x80); // mark as linked
// The time event is initially inserted into the separate
// "freshly armed" list based on QF_timeEvtHead_[tickRate].act.
// Only later, inside the QF_tickX() function, the "freshly armed"
// list is appended to the main list of armed time events based on
// QF_timeEvtHead_[tickRate].next. Again, this is to keep any
// changes to the main list exclusively inside the QF_tickX()
// function.
//
m_next = QF::timeEvtHead_[tickRate].toTimeEvt();
QF::timeEvtHead_[tickRate].m_act = this;
}
}
// the time event is armed
else {
isArmed = true;
}
m_ctr = nTicks; // re-load the tick counter (shift the phasing)
QS_BEGIN_NOCRIT_(QS_QF_TIMEEVT_REARM,
QS::priv_.locFilter[QS::TE_OBJ], this)
QS_TIME_(); // timestamp
QS_OBJ_(this); // this time event object
QS_OBJ_(m_act); // the target AO
QS_TEC_(m_ctr); // the number of ticks
QS_TEC_(m_interval); // the interval
QS_U8_(static_cast<uint8_t>(tickRate)); // the tick rate
if (isArmed) {
QS_U8_(static_cast<uint8_t>(1)); // status: armed
}
else {
QS_U8_(static_cast<uint8_t>(0)); // status: disarmed
}
QS_END_NOCRIT_()
QF_CRIT_EXIT_();
return isArmed;
}
//****************************************************************************
/// @description
/// Useful for checking how many clock ticks (at the tick rate associated
/// with the time event) remain until the time event expires.
///
/// @returns The current value of the down-counter for an armed time event
/// or 0 for an unarmed time event.
///
/// /note The function is thread-safe.
///
QTimeEvtCtr QTimeEvt::ctr(void) const {
QF_CRIT_STAT_
QF_CRIT_ENTRY_();
QTimeEvtCtr ret = m_ctr;
QS_BEGIN_NOCRIT_(QS_QF_TIMEEVT_CTR, QS::priv_.locFilter[QS::TE_OBJ], this)
QS_TIME_(); // timestamp
QS_OBJ_(this); // this time event object
QS_OBJ_(m_act); // the target AO
QS_TEC_(ret); // the current counter
QS_TEC_(m_interval); // the interval
QS_U8_(refCtr_ & static_cast<uint8_t>(0x7F)); // tick rate
QS_END_NOCRIT_()
QF_CRIT_EXIT_();
return ret;
}
} // namespace QP

View File

@ -1,466 +0,0 @@
/// @file
/// @brief QK preemptive kernel core functions
/// @ingroup qk
/// @cond
///***************************************************************************
/// Last updated for version 5.9.7
/// Last updated on 2017-08-18
///
/// Q u a n t u m L e a P s
/// ---------------------------
/// innovating embedded systems
///
/// Copyright (C) 2005-2017 Quantum Leaps, LLC. All rights reserved.
///
/// This program is open source software: you can redistribute it and/or
/// modify it under the terms of the GNU General Public License as published
/// by the Free Software Foundation, either version 3 of the License, or
/// (at your option) any later version.
///
/// Alternatively, this program may be distributed and modified under the
/// terms of Quantum Leaps commercial licenses, which expressly supersede
/// the GNU General Public License and are specifically designed for
/// licensees interested in retaining the proprietary status of their code.
///
/// This program is distributed in the hope that it will be useful,
/// but WITHOUT ANY WARRANTY; without even the implied warranty of
/// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
/// GNU General Public License for more details.
///
/// You should have received a copy of the GNU General Public License
/// along with this program. If not, see <http://www.gnu.org/licenses/>.
///
/// Contact information:
/// https://state-machine.com
/// mailto:info@state-machine.com
///***************************************************************************
/// @endcond
#define QP_IMPL // this is QF/QK implementation
#include "qf_port.h" // QF port
#include "qf_pkg.h" // QF package-scope internal interface
#include "qassert.h" // QP assertions
#ifdef Q_SPY // QS software tracing enabled?
#include "qs_port.h" // include QS port
#else
#include "qs_dummy.h" // disable the QS software tracing
#endif // Q_SPY
// protection against including this source file in a wrong project
#ifndef qk_h
#error "Source file included in a project NOT based on the QK kernel"
#endif // qk_h
// Public-scope objects ******************************************************
extern "C" {
Q_DEFINE_THIS_MODULE("qk")
QK_Attr QK_attr_; // global attributes of the QK kernel
} // extern "C"
namespace QP {
//****************************************************************************
/// @description
/// Initializes QF and must be called exactly once before any other QF
/// function. Typcially, QP::QF::init() is called from main() even before
/// initializing the Board Support Package (BSP).
///
/// @note
/// QP::QF::init() clears the internal QF variables, so that the framework
/// can start correctly even if the startup code fails to clear the
/// uninitialized data (as is required by the C Standard).
///
void QF::init(void) {
QF_maxPool_ = static_cast<uint_fast8_t>(0);
QF_subscrList_ = static_cast<QSubscrList *>(0);
QF_maxPubSignal_ = static_cast<enum_t>(0);
bzero(&QF::timeEvtHead_[0],
static_cast<uint_fast16_t>(sizeof(QF::timeEvtHead_)));
bzero(&active_[0], static_cast<uint_fast16_t>(sizeof(active_)));
bzero(&QK_attr_, static_cast<uint_fast16_t>(sizeof(QK_attr_)));
QK_attr_.actPrio = static_cast<uint_fast8_t>(0); // prio of QK idle loop
QK_attr_.lockPrio = static_cast<uint_fast8_t>(QF_MAX_ACTIVE); // locked
#ifdef QK_INIT
QK_INIT(); // port-specific initialization of the QK kernel
#endif
}
//****************************************************************************
/// @description
/// This function stops the QF application. After calling this function,
/// QF attempts to gracefully stop the application. This graceful shutdown
/// might take some time to complete. The typical use of this function is
/// for terminating the QF application to return back to the operating
/// system or for handling fatal errors that require shutting down
/// (and possibly re-setting) the system.
///
/// @sa QP::QF::onCleanup()
///
void QF::stop(void) {
QF::onCleanup(); // cleanup callback
// nothing else to do for the QK preemptive kernel
}
//****************************************************************************
//! process all events posted during initialization */
static void initial_events(void); // prototype
static void initial_events(void) {
QK_attr_.lockPrio = static_cast<uint_fast8_t>(0); // scheduler unlocked
// any active objects need to be scheduled before starting event loop?
if (QK_sched_() != static_cast<uint_fast8_t>(0)) {
QK_activate_(); // activate AOs to process all events posted so far
}
}
//****************************************************************************
/// @description
///
/// QP::QF::run() is typically called from your startup code after you
/// initialize the QF and start at least one active object with
/// QP::QActive::start().
///
/// @returns In QK, the QP::QF::run() function does not return.
///
int_t QF::run(void) {
QF_INT_DISABLE();
initial_events(); // process all events posted during initialization
onStartup(); // application-specific startup callback
QF_INT_ENABLE();
// the QK idle loop...
for (;;) {
QK::onIdle(); // application-specific QK on-idle callback
}
#ifdef __GNUC__ // GNU compiler?
return static_cast<int_t>(0);
#endif
}
//****************************************************************************
// @description
// Starts execution of the AO and registers the AO with the framework.
//
// @param[in] prio priority at which to start the active object
// @param[in] qSto pointer to the storage for the ring buffer of the
// event queue (used only with the built-in QP::QEQueue)
// @param[in] qLen length of the event queue [events]
// @param[in] stkSto pointer to the stack storage (must be NULL in QK)
// @param[in] stkSize stack size [bytes]
// @param[in] ie pointer to the optional initial event (might be NULL)
//
// @note This function should be called via the macro START().
//
// @usage
// The following example shows starting an AO when a per-task stack is needed:
// @include qf_start.cpp
//
void QActive::start(uint_fast8_t const prio,
QEvt const *qSto[], uint_fast16_t const qLen,
void * const stkSto, uint_fast16_t const,
QEvt const * const ie)
{
/// @pre AO cannot be started from an ISR, the priority must be in range
/// and the stack storage must not be provided, because the QK kernel does
/// not need per-AO stacks.
Q_REQUIRE_ID(300, (!QK_ISR_CONTEXT_())
&& (static_cast<uint_fast8_t>(0) < prio)
&& (prio <= static_cast<uint_fast8_t>(QF_MAX_ACTIVE))
&& (stkSto == static_cast<void *>(0)));
m_eQueue.init(qSto, qLen); // initialize the built-in queue
m_prio = prio; // set the QF priority of this AO
QF::add_(this); // make QF aware of this AO
this->init(ie); // take the top-most initial tran. (virtual)
QS_FLUSH(); // flush the trace buffer to the host
// See if this AO needs to be scheduled in case QK is already running
QF_CRIT_STAT_
QF_CRIT_ENTRY_();
if (QK_sched_() != static_cast<uint_fast8_t>(0)) { // activation needed?
QK_activate_();
}
QF_CRIT_EXIT_();
}
//****************************************************************************
/// @description
/// This function must be called from within the AO that needs to stop.
/// In other words, an AO should stop itself rather than being stopped by
/// someone else. This policy works best, because only the AO itself "knows"
/// when it has reached the appropriate state for the shutdown.
///
/// @note
/// By the time the AO calls QP::QActive::stop(), it should have unsubscribed
/// from all events and no more events should be directly-posted to it.
///
void QActive::stop(void) {
//! @pre QP::QActive::stop() must be called from the AO that wants to stop.
Q_REQUIRE_ID(400, (this == QF::active_[QK_attr_.actPrio]));
QF::remove_(this); // remove this active object from the QF
QF_CRIT_STAT_
QF_CRIT_ENTRY_();
QK_attr_.readySet.remove(m_prio);
if (QK_sched_() != static_cast<uint_fast8_t>(0)) {
QK_activate_();
}
QF_CRIT_EXIT_();
}
//****************************************************************************
///
/// @description
/// This function locks the QK scheduler to the specified ceiling.
///
/// @param[in] ceiling priority ceiling to which the QK scheduler
/// needs to be locked
///
/// @returns
/// The previous QK Scheduler lock status, which is to be used to unlock
/// the scheduler by restoring its previous lock status in
/// QP::QK::schedUnlock().
///
/// @note
/// QP::QK::schedLock() must be always followed by the corresponding
/// QP::QK::schedUnlock().
///
/// @sa QK_schedUnlock()
///
/// @usage
/// The following example shows how to lock and unlock the QK scheduler:
/// @include qk_lock.cpp
///
QSchedStatus QK::schedLock(uint_fast8_t const ceiling) {
QSchedStatus stat;
QF_CRIT_STAT_
QF_CRIT_ENTRY_();
/// @pre The QK scheduler lock:
/// - cannot be called from an ISR;
Q_REQUIRE_ID(600, !QK_ISR_CONTEXT_());
// first store the previous lock prio
if (QK_attr_.lockPrio < ceiling) { // raising the lock prio?
stat = static_cast<QSchedStatus>(QK_attr_.lockPrio << 8);
QK_attr_.lockPrio = ceiling;
QS_BEGIN_NOCRIT_(QS_SCHED_LOCK,
static_cast<void *>(0), static_cast<void *>(0))
QS_TIME_(); // timestamp
QS_2U8_(static_cast<uint8_t>(stat), /* the previous lock prio */
static_cast<uint8_t>(QK_attr_.lockPrio)); // new lock prio
QS_END_NOCRIT_()
// add the previous lock holder priority
stat |= static_cast<QSchedStatus>(QK_attr_.lockHolder);
QK_attr_.lockHolder = QK_attr_.actPrio;
}
else {
stat = static_cast<QSchedStatus>(0xFF);
}
QF_CRIT_EXIT_();
return stat; // return the status to be saved in a stack variable
}
//****************************************************************************
///
/// @description
/// This function unlocks the QK scheduler to the previous status.
///
/// @param[in] stat previous QK Scheduler lock status returned from
/// QP::QK::schedLock()
/// @note
/// QP::QK::schedUnlock() must always follow the corresponding
/// QP::QK::schedLock().
///
/// @sa QP::QK::schedLock()
///
/// @usage
/// The following example shows how to lock and unlock the QK scheduler:
/// @include qk_lock.cpp
///
void QK::schedUnlock(QSchedStatus const stat) {
// has the scheduler been actually locked by the last QK_schedLock()?
if (stat != static_cast<QSchedStatus>(0xFF)) {
uint_fast8_t lockPrio = QK_attr_.lockPrio; // volatilie into tmp
uint_fast8_t prevPrio = static_cast<uint_fast8_t>(stat >> 8);
QF_CRIT_STAT_
QF_CRIT_ENTRY_();
/// @pre The scheduler cannot be unlocked:
/// - from the ISR context; and
/// - the current lock priority must be greater than the previous
Q_REQUIRE_ID(700, (!QK_ISR_CONTEXT_())
&& (lockPrio > prevPrio));
QS_BEGIN_NOCRIT_(QS_SCHED_UNLOCK,
static_cast<void *>(0), static_cast<void *>(0))
QS_TIME_(); // timestamp
QS_2U8_(static_cast<uint8_t>(lockPrio),/* prio before unlocking */
static_cast<uint8_t>(prevPrio));// prio after unlocking
QS_END_NOCRIT_()
// restore the previous lock priority and lock holder
QK_attr_.lockPrio = prevPrio;
QK_attr_.lockHolder =
static_cast<uint_fast8_t>(stat & static_cast<QSchedStatus>(0xFF));
// find the highest-prio thread ready to run
if (QK_sched_() != static_cast<uint_fast8_t>(0)) { // priority found?
QK_activate_(); // activate any unlocked basic threads
}
QF_CRIT_EXIT_();
}
}
} // namespace QP
//============================================================================
extern "C" {
//****************************************************************************
/// @description
/// The QK scheduler finds out the priority of the highest-priority AO
/// that (1) has events to process and (2) has priority that is above the
/// current priority.
///
/// @returns the 1-based priority of the the active object, or zero if
/// no eligible active object is ready to run.
///
/// @attention
/// QK_sched_() must be always called with interrupts **disabled** and
/// returns with interrupts **disabled**.
///
uint_fast8_t QK_sched_(void) {
// find the highest-prio AO with non-empty event queue
uint_fast8_t p = QK_attr_.readySet.findMax();
// is the highest-prio below the active prio?
if (p <= QK_attr_.actPrio) {
p = static_cast<uint_fast8_t>(0); // active object not eligible
}
else if (p <= QK_attr_.lockPrio) { // is it below the lock prio?
p = static_cast<uint_fast8_t>(0); // active object not eligible
}
else {
Q_ASSERT_ID(610, p <= static_cast<uint_fast8_t>(QF_MAX_ACTIVE));
QK_attr_.nextPrio = p; // next AO to run
}
return p;
}
//****************************************************************************
/// @description
/// QK_activate_() activates ready-to run AOs that are above the initial
/// active priority (QK_attr_.actPrio).
///
/// @note
/// The activator might enable interrupts internally, but always returns with
/// interrupts **disabled**.
///
void QK_activate_(void) {
uint_fast8_t pin = QK_attr_.actPrio; // save the active priority
uint_fast8_t p = QK_attr_.nextPrio; /* the next prio to run */
QP::QActive *a;
// QS tracing or thread-local storage?
#ifdef Q_SPY
uint_fast8_t pprev = pin;
#endif // Q_SPY
// QK_attr_.nextPrio must be non-zero upon entry to QK_activate_()
Q_REQUIRE_ID(800, p != static_cast<uint_fast8_t>(0));
QK_attr_.nextPrio = static_cast<uint_fast8_t>(0); // clear for next time
// loop until no more ready-to-run AOs of higher prio than the initial
do {
a = QP::QF::active_[p]; // obtain the pointer to the AO
QK_attr_.actPrio = p; // this becomes the active priority
QS_BEGIN_NOCRIT_(QP::QS_SCHED_NEXT,
QP::QS::priv_.locFilter[QP::QS::AO_OBJ], a)
QS_TIME_(); // timestamp
QS_2U8_(static_cast<uint8_t>(p), // prio of the scheduled AO
static_cast<uint8_t>(pprev)); // previous priority
QS_END_NOCRIT_()
#ifdef Q_SPY
if (p != pprev) { // changing priorities?
pprev = p; // update previous priority
}
#endif // Q_SPY
QF_INT_ENABLE(); // unconditionally enable interrupts
// perform the run-to-completion (RTS) step...
// 1. retrieve the event from the AO's event queue, which by this
// time must be non-empty and QActive_get_() asserts it.
// 2. dispatch the event to the AO's state machine.
// 3. determine if event is garbage and collect it if so
//
QP::QEvt const *e = a->get_();
a->dispatch(e);
QP::QF::gc(e);
// determine the next highest-priority AO ready to run...
QF_INT_DISABLE();
if (a->m_eQueue.isEmpty()) { // empty queue?
QK_attr_.readySet.remove(p);
}
// find new highest-prio AO ready to run...
p = QK_attr_.readySet.findMax();
// is the new priority below the initial preemption threshold?
if (p <= pin) {
p = static_cast<uint_fast8_t>(0); // active object not eligible
}
else if (p <= QK_attr_.lockPrio) { // is it below the lock prio?
p = static_cast<uint_fast8_t>(0); // active object not eligible
}
else {
Q_ASSERT_ID(710, p <= static_cast<uint_fast8_t>(QF_MAX_ACTIVE));
}
} while (p != static_cast<uint_fast8_t>(0));
QK_attr_.actPrio = pin; // restore the active priority
#ifdef Q_SPY
if (pin != static_cast<uint_fast8_t>(0)) { // resuming an active object?
a = QP::QF::active_[pin]; // the pointer to the preempted AO
QS_BEGIN_NOCRIT_(QP::QS_SCHED_RESUME,
QP::QS::priv_.locFilter[QP::QS::AO_OBJ], a)
QS_TIME_(); // timestamp
QS_2U8_(static_cast<uint8_t>(pin), // prio of the resumed AO
static_cast<uint8_t>(pprev)); // previous priority
QS_END_NOCRIT_()
}
else { // resuming priority==0 --> idle
QS_BEGIN_NOCRIT_(QP::QS_SCHED_IDLE,
static_cast<void *>(0), static_cast<void *>(0))
QS_TIME_(); // timestamp
QS_U8_(static_cast<uint8_t>(pprev)); // previous priority
QS_END_NOCRIT_()
}
#endif // Q_SPY
}
} // extern "C"

View File

@ -1,38 +0,0 @@
/// @file
/// @brief QK mutex implementation (obsolete).
/// @ingroup qk
/// @cond
///***************************************************************************
/// Product: QK/C++
/// Last updated for version 5.9.7
/// Last updated on 2017-08-18
///
/// Q u a n t u m L e a P s
/// ---------------------------
/// innovating embedded systems
///
/// Copyright (C) Quantum Leaps, LLC. All rights reserved.
///
/// This program is open source software: you can redistribute it and/or
/// modify it under the terms of the GNU General Public License as published
/// by the Free Software Foundation, either version 3 of the License, or
/// (at your option) any later version.
///
/// Alternatively, this program may be distributed and modified under the
/// terms of Quantum Leaps commercial licenses, which expressly supersede
/// the GNU General Public License and are specifically designed for
/// licensees interested in retaining the proprietary status of their code.
///
/// This program is distributed in the hope that it will be useful,
/// but WITHOUT ANY WARRANTY; without even the implied warranty of
/// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
/// GNU General Public License for more details.
///
/// You should have received a copy of the GNU General Public License
/// along with this program. If not, see <http://www.gnu.org/licenses/>.
///
/// Contact information:
/// https://state-machine.com
/// mailto:info@state-machine.com
///***************************************************************************
/// @endcond

View File

@ -1,918 +0,0 @@
/// @file
/// @brief QS software tracing services
/// @ingroup qs
/// @cond
///***************************************************************************
/// Last updated for version 5.9.0
/// Last updated on 2017-05-16
///
/// Q u a n t u m L e a P s
/// ---------------------------
/// innovating embedded systems
///
/// Copyright (C) Quantum Leaps. All rights reserved.
///
/// This program is open source software: you can redistribute it and/or
/// modify it under the terms of the GNU General Public License as published
/// by the Free Software Foundation, either version 3 of the License, or
/// (at your option) any later version.
///
/// Alternatively, this program may be distributed and modified under the
/// terms of Quantum Leaps commercial licenses, which expressly supersede
/// the GNU General Public License and are specifically designed for
/// licensees interested in retaining the proprietary status of their code.
///
/// This program is distributed in the hope that it will be useful,
/// but WITHOUT ANY WARRANTY; without even the implied warranty of
/// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
/// GNU General Public License for more details.
///
/// You should have received a copy of the GNU General Public License
/// along with this program. If not, see <http://www.gnu.org/licenses/>.
///
/// Contact information:
/// https://state-machine.com
/// mailto:info@state-machine.com
///***************************************************************************
/// @endcond
#define QP_IMPL // this is QP implementation
#include "qs_port.h" // QS port
#include "qs_pkg.h" // QS package-scope internal interface
#include "qassert.h" // QP assertions
namespace QP {
Q_DEFINE_THIS_MODULE("qs")
//****************************************************************************
extern char_t const BUILD_DATE[12];
extern char_t const BUILD_TIME[9];
QS QS::priv_; // QS private data
//****************************************************************************
/// @description
/// This function should be called from QP::QS::onStartup() to provide QS with
/// the data buffer. The first argument @a sto[] is the address of the memory
/// block, and the second argument @a stoSize is the size of this block
/// in bytes. Currently the size of the QS buffer cannot exceed 64KB.
///
/// @note QS can work with quite small data buffers, but you will start losing
/// data if the buffer is too small for the bursts of tracing activity.
/// The right size of the buffer depends on the data production rate and
/// the data output rate. QS offers flexible filtering to reduce the data
/// production rate.
///
/// @note If the data output rate cannot keep up with the production rate,
/// QS will start overwriting the older data with newer data. This is
/// consistent with the "last-is-best" QS policy. The record sequence counters
/// and check sums on each record allow the QSPY host uitiliy to easily detect
/// any data loss.
///
void QS::initBuf(uint8_t sto[], uint_fast16_t const stoSize) {
// the provided buffer must be at least 8 bytes long
Q_REQUIRE_ID(100, stoSize > static_cast<uint_fast16_t>(8));
// This function initializes all the internal QS variables, so that the
// tracing can start correctly even if the startup code fails to clear
// any uninitialized data (as is required by the C Standard).
//
QS_FILTER_OFF(QS_ALL_RECORDS); // disable all maskable filters
priv_.locFilter[SM_OBJ] = static_cast<void *>(0);
priv_.locFilter[AO_OBJ] = static_cast<void *>(0);
priv_.locFilter[MP_OBJ] = static_cast<void *>(0);
priv_.locFilter[EQ_OBJ] = static_cast<void *>(0);
priv_.locFilter[TE_OBJ] = static_cast<void *>(0);
priv_.locFilter[TE_OBJ] = static_cast<void *>(0);
priv_.buf = &sto[0];
priv_.end = static_cast<QSCtr>(stoSize);
priv_.head = static_cast<QSCtr>(0);
priv_.tail = static_cast<QSCtr>(0);
priv_.used = static_cast<QSCtr>(0);
priv_.seq = static_cast<uint8_t>(0);
priv_.chksum = static_cast<uint8_t>(0);
priv_.critNest = static_cast<uint_fast8_t>(0);
// produce an empty record to "flush" the QS trace buffer
beginRec(QS_REC_NUM_(QS_EMPTY));
endRec();
// produce the Target info QS record
QS_target_info_(static_cast<uint8_t>(0xFF));
// wait with flushing after successfull initialization (see QS_INIT())
}
//****************************************************************************
/// @description
/// This function sets up the QS filter to enable the record type @a rec.
/// The argument #QS_ALL_RECORDS specifies to filter-in all records.
/// This function should be called indirectly through the macro QS_FILTER_ON.
///
/// @note Filtering based on the record-type is only the first layer of
/// filtering. The second layer is based on the object-type. Both filter
/// layers must be enabled for the QS record to be inserted in the QS buffer.
///
/// @sa QP::QS::filterOff(), QS_FILTER_SM_OBJ, QS_FILTER_AO_OBJ,
/// QS_FILTER_MP_OBJ, QS_FILTER_EQ_OBJ, and QS_FILTER_TE_OBJ.
///
void QS::filterOn(uint_fast8_t const rec) {
if (rec == static_cast<uint_fast8_t>(QS_ALL_RECORDS)) {
uint_fast8_t i;
for (i = static_cast<uint_fast8_t>(0);
i < static_cast<uint_fast8_t>(15); ++i)
{
priv_.glbFilter[i] = static_cast<uint8_t>(0xFF); // set all bits
}
// never turn the last 3 records on (0x7D, 0x7E, 0x7F)
priv_.glbFilter[sizeof(priv_.glbFilter) - 1U]
= static_cast<uint8_t>(0x1F);
}
else if (rec == static_cast<uint_fast8_t>(QS_SM_RECORDS)) {
priv_.glbFilter[0] |= static_cast<uint8_t>(0xFE);
priv_.glbFilter[1] |= static_cast<uint8_t>(0x03);
priv_.glbFilter[6] |= static_cast<uint8_t>(0x80);
priv_.glbFilter[7] |= static_cast<uint8_t>(0x03);
}
else if (rec == static_cast<uint_fast8_t>(QS_AO_RECORDS)) {
priv_.glbFilter[1] |= static_cast<uint8_t>(0xFC);
priv_.glbFilter[2] |= static_cast<uint8_t>(0x03);
priv_.glbFilter[5] |= static_cast<uint8_t>(0x20);
}
else if (rec == static_cast<uint_fast8_t>(QS_EQ_RECORDS)) {
priv_.glbFilter[2] |= static_cast<uint8_t>(0x7C);
priv_.glbFilter[5] |= static_cast<uint8_t>(0x40);
}
else if (rec == static_cast<uint_fast8_t>(QS_MP_RECORDS)) {
priv_.glbFilter[2] |= static_cast<uint8_t>(0x80);
priv_.glbFilter[3] |= static_cast<uint8_t>(0x03);
priv_.glbFilter[5] |= static_cast<uint8_t>(0x80);
}
else if (rec == static_cast<uint_fast8_t>(QS_QF_RECORDS)) {
priv_.glbFilter[3] |= static_cast<uint8_t>(0xFC);
priv_.glbFilter[4] |= static_cast<uint8_t>(0x80);
priv_.glbFilter[5] |= static_cast<uint8_t>(0x1F);
}
else if (rec == static_cast<uint_fast8_t>(QS_TE_RECORDS)) {
priv_.glbFilter[4] |= static_cast<uint8_t>(0x7F);
}
else if (rec == static_cast<uint_fast8_t>(QS_SC_RECORDS)) {
priv_.glbFilter[6] |= static_cast<uint8_t>(0x7C);
}
else if (rec == static_cast<uint_fast8_t>(QS_U0_RECORDS)) {
priv_.glbFilter[8] |= static_cast<uint8_t>(0xC0);
priv_.glbFilter[9] |= static_cast<uint8_t>(0xFF);
}
else if (rec == static_cast<uint_fast8_t>(QS_U1_RECORDS)) {
priv_.glbFilter[10] |= static_cast<uint8_t>(0xFF);
priv_.glbFilter[11] |= static_cast<uint8_t>(0x03);
}
else if (rec == static_cast<uint_fast8_t>(QS_U2_RECORDS)) {
priv_.glbFilter[11] |= static_cast<uint8_t>(0xFC);
priv_.glbFilter[12] |= static_cast<uint8_t>(0x0F);
}
else if (rec == static_cast<uint_fast8_t>(QS_U3_RECORDS)) {
priv_.glbFilter[12] |= static_cast<uint8_t>(0xF0);
priv_.glbFilter[13] |= static_cast<uint8_t>(0x3F);
}
else if (rec == static_cast<uint_fast8_t>(QS_U4_RECORDS)) {
priv_.glbFilter[13] |= static_cast<uint8_t>(0xC0);
priv_.glbFilter[14] |= static_cast<uint8_t>(0xFF);
}
else if (rec == static_cast<uint_fast8_t>(QS_UA_RECORDS)) {
priv_.glbFilter[8] |= static_cast<uint8_t>(0xC0);
priv_.glbFilter[9] |= static_cast<uint8_t>(0xFF);
priv_.glbFilter[10] |= static_cast<uint8_t>(0xFF);
priv_.glbFilter[11] |= static_cast<uint8_t>(0xFF);
priv_.glbFilter[12] |= static_cast<uint8_t>(0xFF);
priv_.glbFilter[14] |= static_cast<uint8_t>(0xFF);
}
else {
// record numbers can't exceed QS_ESC, so they don't need escaping
Q_ASSERT_ID(210, rec < static_cast<uint_fast8_t>(QS_ESC));
priv_.glbFilter[rec >> 3] |=
static_cast<uint8_t>(1U << (rec & static_cast<uint_fast8_t>(7U)));
}
}
//****************************************************************************
/// @description
/// This function sets up the QS filter to disable the record type @a rec.
/// The argument #QS_ALL_RECORDS specifies to suppress all records.
/// This function should be called indirectly through the macro QS_FILTER_OFF.
///
/// @note Filtering records based on the record-type is only the first layer
/// of filtering. The second layer is based on the object-type. Both filter
/// layers must be enabled for the QS record to be inserted in the QS buffer.
///
void QS::filterOff(uint_fast8_t const rec) {
uint8_t tmp;
if (rec == static_cast<uint_fast8_t>(QS_ALL_RECORDS)) {
// first clear all global filters
for (tmp = static_cast<uint8_t>(15);
tmp > static_cast<uint8_t>(0); --tmp)
{
priv_.glbFilter[tmp] = static_cast<uint8_t>(0);
}
// next leave the specific filters enabled
priv_.glbFilter[0] = static_cast<uint8_t>(0x01);
priv_.glbFilter[7] = static_cast<uint8_t>(0xFC);
priv_.glbFilter[8] = static_cast<uint8_t>(0x3F);
}
else if (rec == static_cast<uint_fast8_t>(QS_SM_RECORDS)) {
priv_.glbFilter[0] &= static_cast<uint8_t>(~0xFEU);
priv_.glbFilter[1] &= static_cast<uint8_t>(~0x03U);
priv_.glbFilter[6] &= static_cast<uint8_t>(~0x80U);
priv_.glbFilter[7] &= static_cast<uint8_t>(~0x03U);
}
else if (rec == static_cast<uint_fast8_t>(QS_AO_RECORDS)) {
priv_.glbFilter[1] &= static_cast<uint8_t>(~0xFCU);
priv_.glbFilter[2] &= static_cast<uint8_t>(~0x03U);
priv_.glbFilter[5] &= static_cast<uint8_t>(~0x20U);
}
else if (rec == static_cast<uint_fast8_t>(QS_EQ_RECORDS)) {
priv_.glbFilter[2] &= static_cast<uint8_t>(~0x7CU);
priv_.glbFilter[5] &= static_cast<uint8_t>(~0x40U);
}
else if (rec == static_cast<uint_fast8_t>(QS_MP_RECORDS)) {
priv_.glbFilter[2] &= static_cast<uint8_t>(~0x80U);
priv_.glbFilter[3] &= static_cast<uint8_t>(~0x03U);
priv_.glbFilter[5] &= static_cast<uint8_t>(~0x80U);
}
else if (rec == static_cast<uint_fast8_t>(QS_QF_RECORDS)) {
priv_.glbFilter[3] &= static_cast<uint8_t>(~0xFCU);
priv_.glbFilter[4] &= static_cast<uint8_t>(~0x80U);
priv_.glbFilter[5] &= static_cast<uint8_t>(~0x1FU);
}
else if (rec == static_cast<uint_fast8_t>(QS_TE_RECORDS)) {
priv_.glbFilter[4] &= static_cast<uint8_t>(~0x7FU);
}
else if (rec == static_cast<uint_fast8_t>(QS_SC_RECORDS)) {
priv_.glbFilter[6] &= static_cast<uint8_t>(~0x7CU);
}
else if (rec == static_cast<uint_fast8_t>(QS_U0_RECORDS)) {
priv_.glbFilter[8] &= static_cast<uint8_t>(~0xC0U);
priv_.glbFilter[9] = static_cast<uint8_t>(0);
}
else if (rec == static_cast<uint_fast8_t>(QS_U1_RECORDS)) {
priv_.glbFilter[10] = static_cast<uint8_t>(0);
priv_.glbFilter[11] &= static_cast<uint8_t>(~0x03U);
}
else if (rec == static_cast<uint_fast8_t>(QS_U2_RECORDS)) {
priv_.glbFilter[11] &= static_cast<uint8_t>(~0xFCU);
priv_.glbFilter[12] &= static_cast<uint8_t>(~0x0FU);
}
else if (rec == static_cast<uint_fast8_t>(QS_U3_RECORDS)) {
priv_.glbFilter[12] &= static_cast<uint8_t>(~0xF0U);
priv_.glbFilter[13] &= static_cast<uint8_t>(~0x3FU);
}
else if (rec == static_cast<uint_fast8_t>(QS_U4_RECORDS)) {
priv_.glbFilter[13] &= static_cast<uint8_t>(~0xC0U);
priv_.glbFilter[14] = static_cast<uint8_t>(0);
}
else if (rec == static_cast<uint_fast8_t>(QS_UA_RECORDS)) {
priv_.glbFilter[8] &= static_cast<uint8_t>(~0xC0U);
priv_.glbFilter[9] = static_cast<uint8_t>(0);
priv_.glbFilter[10] = static_cast<uint8_t>(0);
priv_.glbFilter[11] = static_cast<uint8_t>(0);
priv_.glbFilter[12] = static_cast<uint8_t>(0);
priv_.glbFilter[14] = static_cast<uint8_t>(0);
}
else {
// record IDs can't exceed QS_ESC, so they don't need escaping
Q_ASSERT_ID(310, rec < static_cast<uint_fast8_t>(QS_ESC));
tmp = static_cast<uint8_t>(
1U << (rec & static_cast<uint_fast8_t>(0x07U)));
tmp ^= static_cast<uint8_t>(0xFF); // invert all bits
priv_.glbFilter[rec >> 3] &= tmp;
}
}
//****************************************************************************
/// @description
/// This function must be called at the beginning of each QS record.
/// This function should be called indirectly through the macro #QS_BEGIN,
/// or #QS_BEGIN_NOCRIT, depending if it's called in a normal code or from
/// a critical section.
///
void QS::beginRec(uint_fast8_t const rec) {
uint8_t b = static_cast<uint8_t>(priv_.seq + static_cast<uint8_t>(1));
uint8_t chksum_ = static_cast<uint8_t>(0); // reset the checksum
uint8_t *buf_ = priv_.buf; // put in a temporary (register)
QSCtr head_ = priv_.head; // put in a temporary (register)
QSCtr end_ = priv_.end; // put in a temporary (register)
priv_.seq = b; // store the incremented sequence num
priv_.used += static_cast<QSCtr>(2); // 2 bytes about to be added
QS_INSERT_ESC_BYTE(b)
chksum_ = static_cast<uint8_t>(chksum_ + static_cast<uint8_t>(rec));
QS_INSERT_BYTE(static_cast<uint8_t>(rec)) // rec does not need escaping
priv_.head = head_; // save the head
priv_.chksum = chksum_; // save the checksum
}
//****************************************************************************
/// @description
/// This function must be called at the end of each QS record.
/// This function should be called indirectly through the macro #QS_END,
/// or #QS_END_NOCRIT, depending if it's called in a normal code or from
/// a critical section.
///
void QS::endRec(void) {
uint8_t *buf_ = priv_.buf; // put in a temporary (register)
QSCtr head_ = priv_.head;
QSCtr end_ = priv_.end;
uint8_t b = priv_.chksum;
b ^= static_cast<uint8_t>(0xFF); // invert the bits in the checksum
priv_.used += static_cast<QSCtr>(2); // 2 bytes about to be added
if ((b != QS_FRAME) && (b != QS_ESC)) {
QS_INSERT_BYTE(b)
}
else {
QS_INSERT_BYTE(QS_ESC)
QS_INSERT_BYTE(b ^ QS_ESC_XOR)
++priv_.used; // account for the ESC byte
}
QS_INSERT_BYTE(QS_FRAME) // do not escape this QS_FRAME
priv_.head = head_; // save the head
if (priv_.used > end_) { // overrun over the old data?
priv_.used = end_; // the whole buffer is used
priv_.tail = head_; // shift the tail to the old data
}
}
//****************************************************************************
void QS_target_info_(uint8_t const isReset) {
QS::beginRec(static_cast<uint_fast8_t>(QS_TARGET_INFO));
QS_U8_(isReset);
QS_U16_(QP_VERSION); // two-byte version number
// send the object sizes...
QS_U8_(static_cast<uint8_t>(Q_SIGNAL_SIZE)
| static_cast<uint8_t>(
static_cast<uint8_t>(QF_EVENT_SIZ_SIZE) << 4));
#ifdef QF_EQUEUE_CTR_SIZE
QS_U8_(static_cast<uint8_t>(QF_EQUEUE_CTR_SIZE)
| static_cast<uint8_t>(
static_cast<uint8_t>(QF_TIMEEVT_CTR_SIZE) << 4));
#else
QS_U8_(static_cast<uint8_t>(
static_cast<uint8_t>(QF_TIMEEVT_CTR_SIZE) << 4));
#endif // ifdef QF_EQUEUE_CTR_SIZE
#ifdef QF_MPOOL_CTR_SIZE
QS_U8_(static_cast<uint8_t>(QF_MPOOL_SIZ_SIZE)
| static_cast<uint8_t>(
static_cast<uint8_t>(QF_MPOOL_CTR_SIZE) << 4));
#else
QS_U8_(static_cast<uint8_t>(0));
#endif // ifdef QF_MPOOL_CTR_SIZE
QS_U8_(static_cast<uint8_t>(QS_OBJ_PTR_SIZE)
| static_cast<uint8_t>(
static_cast<uint8_t>(QS_FUN_PTR_SIZE) << 4));
QS_U8_(static_cast<uint8_t>(QS_TIME_SIZE));
// send the limits...
QS_U8_(static_cast<uint8_t>(QF_MAX_ACTIVE));
QS_U8_(static_cast<uint8_t>(QF_MAX_EPOOL)
| static_cast<uint8_t>(
static_cast<uint8_t>(QF_MAX_TICK_RATE) << 4));
// send the build time in three bytes (sec, min, hour)...
QS_U8_(static_cast<uint8_t>(
static_cast<uint8_t>(10)
*(static_cast<uint8_t>(BUILD_TIME[6])
- static_cast<uint8_t>('0')))
+ (static_cast<uint8_t>(BUILD_TIME[7])
- static_cast<uint8_t>('0')));
QS_U8_(static_cast<uint8_t>(
static_cast<uint8_t>(10)
*(static_cast<uint8_t>(BUILD_TIME[3])
- static_cast<uint8_t>('0')))
+ (static_cast<uint8_t>(BUILD_TIME[4])
- static_cast<uint8_t>('0')));
if (static_cast<uint8_t>(BUILD_TIME[0])
== static_cast<uint8_t>(' '))
{
QS_U8_(static_cast<uint8_t>(BUILD_TIME[1])
- static_cast<uint8_t>('0'));
}
else {
QS_U8_(static_cast<uint8_t>(
static_cast<uint8_t>(10)*(
static_cast<uint8_t>(BUILD_TIME[0])
- static_cast<uint8_t>('0')))
+ (static_cast<uint8_t>(BUILD_TIME[1])
- static_cast<uint8_t>('0')));
}
// send the build date in three bytes (day, month, year) ...
if (static_cast<uint8_t>(BUILD_DATE[4])
== static_cast<uint8_t>(' '))
{
QS_U8_(static_cast<uint8_t>(BUILD_DATE[5])
- static_cast<uint8_t>('0'));
}
else {
QS_U8_(static_cast<uint8_t>(
static_cast<uint8_t>(10)*(
static_cast<uint8_t>(BUILD_DATE[4])
- static_cast<uint8_t>('0')))
+ (static_cast<uint8_t>(BUILD_DATE[5])
- static_cast<uint8_t>('0')));
}
// convert the 3-letter month to a number 1-12 ...
uint8_t b;
switch (static_cast<int_t>(BUILD_DATE[0])
+ static_cast<int_t>(BUILD_DATE[1])
+ static_cast<int_t>(BUILD_DATE[2]))
{
case static_cast<int_t>('J')
+ static_cast<int_t>('a')
+ static_cast<int_t>('n'):
b = static_cast<uint8_t>(1);
break;
case static_cast<int_t>('F')
+ static_cast<int_t>('e')
+ static_cast<int_t>('b'):
b = static_cast<uint8_t>(2);
break;
case static_cast<int_t>('M')
+ static_cast<int_t>('a')
+ static_cast<int_t>('r'):
b = static_cast<uint8_t>(3);
break;
case static_cast<int_t>('A')
+ static_cast<int_t>('p')
+ static_cast<int_t>('r'):
b = static_cast<uint8_t>(4);
break;
case static_cast<int_t>('M')
+ static_cast<int_t>('a')
+ static_cast<int_t>('y'):
b = static_cast<uint8_t>(5);
break;
case static_cast<int_t>('J')
+ static_cast<int_t>('u')
+ static_cast<int_t>('n'):
b = static_cast<uint8_t>(6);
break;
case static_cast<int_t>('J')
+ static_cast<int_t>('u')
+ static_cast<int_t>('l'):
b = static_cast<uint8_t>(7);
break;
case static_cast<int_t>('A')
+ static_cast<int_t>('u')
+ static_cast<int_t>('g'):
b = static_cast<uint8_t>(8);
break;
case static_cast<int_t>('S')
+ static_cast<int_t>('e')
+ static_cast<int_t>('p'):
b = static_cast<uint8_t>(9);
break;
case static_cast<int_t>('O')
+ static_cast<int_t>('c')
+ static_cast<int_t>('t'):
b = static_cast<uint8_t>(10);
break;
case static_cast<int_t>('N')
+ static_cast<int_t>('o')
+ static_cast<int_t>('v'):
b = static_cast<uint8_t>(11);
break;
case static_cast<int_t>('D')
+ static_cast<int_t>('e')
+ static_cast<int_t>('c'):
b = static_cast<uint8_t>(12);
break;
default:
b = static_cast<uint8_t>(0);
break;
}
QS_U8_(b); // store the month
QS_U8_(static_cast<uint8_t>(
static_cast<uint8_t>(10)*(
static_cast<uint8_t>(BUILD_DATE[9])
- static_cast<uint8_t>('0')))
+ (static_cast<uint8_t>(BUILD_DATE[10])
- static_cast<uint8_t>('0')));
QS::endRec();
}
//****************************************************************************
/// @description
/// @note This function is only to be used through macros, never in the
/// client code directly.
///
void QS::u8(uint8_t const format, uint8_t const d) {
uint8_t chksum_ = priv_.chksum; // put in a temporary (register)
uint8_t *buf_ = priv_.buf; // put in a temporary (register)
QSCtr head_ = priv_.head; // put in a temporary (register)
QSCtr end_ = priv_.end; // put in a temporary (register)
priv_.used += static_cast<QSCtr>(2); // 2 bytes about to be added
QS_INSERT_ESC_BYTE(format)
QS_INSERT_ESC_BYTE(d)
priv_.head = head_; // save the head
priv_.chksum = chksum_; // save the checksum
}
//****************************************************************************
/// @description
/// This function is only to be used through macros, never in the
/// client code directly.
///
void QS::u16(uint8_t format, uint16_t d) {
uint8_t chksum_ = priv_.chksum; // put in a temporary (register)
uint8_t *buf_ = priv_.buf; // put in a temporary (register)
QSCtr head_ = priv_.head; // put in a temporary (register)
QSCtr end_ = priv_.end; // put in a temporary (register)
priv_.used += static_cast<QSCtr>(3); // 3 bytes about to be added
QS_INSERT_ESC_BYTE(format)
format = static_cast<uint8_t>(d);
QS_INSERT_ESC_BYTE(format)
d >>= 8;
format = static_cast<uint8_t>(d);
QS_INSERT_ESC_BYTE(format)
priv_.head = head_; // save the head
priv_.chksum = chksum_; // save the checksum
}
//****************************************************************************
/// @note This function is only to be used through macros, never in the
/// client code directly.
///
void QS::u32(uint8_t format, uint32_t d) {
uint8_t chksum_ = priv_.chksum; // put in a temporary (register)
uint8_t *buf_ = priv_.buf; // put in a temporary (register)
QSCtr head_ = priv_.head; // put in a temporary (register)
QSCtr end_ = priv_.end; // put in a temporary (register)
priv_.used += static_cast<QSCtr>(5); // 5 bytes about to be added
QS_INSERT_ESC_BYTE(format) // insert the format byte
for (int_t i = static_cast<int_t>(4); i != static_cast<int_t>(0); --i) {
format = static_cast<uint8_t>(d);
QS_INSERT_ESC_BYTE(format)
d >>= 8;
}
priv_.head = head_; // save the head
priv_.chksum = chksum_; // save the checksum
}
//****************************************************************************
/// @note This function is only to be used through macros, never in the
/// client code directly.
///
void QS::u8_(uint8_t const d) {
uint8_t chksum_ = priv_.chksum; // put in a temporary (register)
uint8_t *buf_ = priv_.buf; // put in a temporary (register)
QSCtr head_ = priv_.head; // put in a temporary (register)
QSCtr end_ = priv_.end; // put in a temporary (register)
++priv_.used; // 1 byte about to be added
QS_INSERT_ESC_BYTE(d)
priv_.head = head_; // save the head
priv_.chksum = chksum_; // save the checksum
}
//****************************************************************************
/// @note This function is only to be used through macros, never in the
/// client code directly.
///
void QS::u8u8_(uint8_t const d1, uint8_t const d2) {
uint8_t chksum_ = priv_.chksum; // put in a temporary (register)
uint8_t *buf_ = priv_.buf; // put in a temporary (register)
QSCtr head_ = priv_.head; // put in a temporary (register)
QSCtr end_ = priv_.end; // put in a temporary (register)
priv_.used += static_cast<QSCtr>(2); // 2 bytes about to be added
QS_INSERT_ESC_BYTE(d1)
QS_INSERT_ESC_BYTE(d2)
priv_.head = head_; // save the head
priv_.chksum = chksum_; // save the checksum
}
//****************************************************************************
/// @note This function is only to be used through macros, never in the
/// client code directly.
///
void QS::u16_(uint16_t d) {
uint8_t b = static_cast<uint8_t>(d);
uint8_t chksum_ = priv_.chksum; // put in a temporary (register)
uint8_t *buf_ = priv_.buf; // put in a temporary (register)
QSCtr head_ = priv_.head; // put in a temporary (register)
QSCtr end_ = priv_.end; // put in a temporary (register)
priv_.used += static_cast<QSCtr>(2); // 2 bytes about to be added
QS_INSERT_ESC_BYTE(b)
d >>= 8;
b = static_cast<uint8_t>(d);
QS_INSERT_ESC_BYTE(b)
priv_.head = head_; // save the head
priv_.chksum = chksum_; // save the checksum
}
//****************************************************************************
/// @note This function is only to be used through macros, never in the
/// client code directly.
///
void QS::u32_(uint32_t d) {
uint8_t chksum_ = priv_.chksum; // put in a temporary (register)
uint8_t *buf_ = priv_.buf; // put in a temporary (register)
QSCtr head_ = priv_.head; // put in a temporary (register)
QSCtr end_ = priv_.end; // put in a temporary (register)
priv_.used += static_cast<QSCtr>(4); // 4 bytes about to be added
for (int_t i = static_cast<int_t>(4); i != static_cast<int_t>(0); --i) {
uint8_t b = static_cast<uint8_t>(d);
QS_INSERT_ESC_BYTE(b)
d >>= 8;
}
priv_.head = head_; // save the head
priv_.chksum = chksum_; // save the checksum
}
//****************************************************************************
/// @note This function is only to be used through macros, never in the
/// client code directly.
///
void QS::str_(char_t const *s) {
uint8_t b = static_cast<uint8_t>(*s);
uint8_t chksum_ = priv_.chksum; // put in a temporary (register)
uint8_t *buf_ = priv_.buf; // put in a temporary (register)
QSCtr head_ = priv_.head; // put in a temporary (register)
QSCtr end_ = priv_.end; // put in a temporary (register)
QSCtr used_ = priv_.used; // put in a temporary (register)
while (b != static_cast<uint8_t>(0)) {
chksum_ += b; // update checksum
QS_INSERT_BYTE(b) // ASCII characters don't need escaping
QS_PTR_INC_(s);
b = static_cast<uint8_t>(*s);
++used_;
}
QS_INSERT_BYTE(static_cast<uint8_t>(0)) // zero-terminate the string
++used_;
priv_.head = head_; // save the head
priv_.chksum = chksum_; // save the checksum
priv_.used = used_; // save # of used buffer space
}
//****************************************************************************
/// @description
/// This function delivers one byte at a time from the QS data buffer.
///
/// @returns the byte in the least-significant 8-bits of the 16-bit return
/// value if the byte is available. If no more data is available at the time,
/// the function returns QP::QS_EOD (End-Of-Data).
///
/// @note QP::QS::getByte() is __not__ protected with a critical section.
///
uint16_t QS::getByte(void) {
uint16_t ret;
if (priv_.used == static_cast<QSCtr>(0)) {
ret = QS_EOD; // set End-Of-Data
}
else {
uint8_t *buf_ = priv_.buf; // put in a temporary (register)
QSCtr tail_ = priv_.tail; // put in a temporary (register)
// the byte to return
ret = static_cast<uint16_t>(QS_PTR_AT_(buf_, tail_));
++tail_; // advance the tail
if (tail_ == priv_.end) { // tail wrap around?
tail_ = static_cast<QSCtr>(0);
}
priv_.tail = tail_; // update the tail
--priv_.used; // one less byte used
}
return ret; // return the byte or EOD
}
//****************************************************************************
/// @description
/// This function delivers a contiguous block of data from the QS data buffer.
/// The function returns the pointer to the beginning of the block, and writes
/// the number of bytes in the block to the location pointed to by @a pNbytes.
/// The argument @a pNbytes is also used as input to provide the maximum size
/// of the data block that the caller can accept.
///
/// @returns if data is available, the function returns pointer to the
/// contiguous block of data and sets the value pointed to by @p pNbytes
/// to the # available bytes. If data is available at the time the function is
/// called, the function returns NULL pointer and sets the value pointed to by
/// @p pNbytes to zero.
///
/// @note
/// Only the NULL return from QP::QS::getBlock() indicates that the QS buffer
/// is empty at the time of the call. The non-NULL return often means that
/// the block is at the end of the buffer and you need to call
/// QP::QS::getBlock() again to obtain the rest of the data that
/// "wrapped around" to the beginning of the QS data buffer.
///
/// @note QP::QS::getBlock() is __not__ protected with a critical section.
///
uint8_t const *QS::getBlock(uint16_t * const pNbytes) {
QSCtr used_ = priv_.used; // put in a temporary (register)
uint8_t *buf_;
// any bytes used in the ring buffer?
if (used_ == static_cast<QSCtr>(0)) {
*pNbytes = static_cast<uint16_t>(0); // no bytes available right now
buf_ = static_cast<uint8_t *>(0); // no bytes available right now
}
else {
QSCtr tail_ = priv_.tail; // put in a temporary (register)
QSCtr end_ = priv_.end; // put in a temporary (register)
QSCtr n = static_cast<QSCtr>(end_ - tail_);
if (n > used_) {
n = used_;
}
if (n > static_cast<QSCtr>(*pNbytes)) {
n = static_cast<QSCtr>(*pNbytes);
}
*pNbytes = static_cast<uint16_t>(n); // n-bytes available
buf_ = priv_.buf;
buf_ = &QS_PTR_AT_(buf_, tail_); // the bytes are at the tail
priv_.used -= n;
tail_ += n;
if (tail_ == end_) {
tail_ = static_cast<QSCtr>(0);
}
priv_.tail = tail_;
}
return buf_;
}
//****************************************************************************
/// @note This function is only to be used through macro QS_SIG_DICTIONARY()
///
void QS::sig_dict(enum_t const sig, void const * const obj,
char_t const *name)
{
QS_CRIT_STAT_
if (*name == static_cast<char_t>('&')) {
QS_PTR_INC_(name);
}
QS_CRIT_ENTRY_();
beginRec(static_cast<uint_fast8_t>(QS_SIG_DICT));
QS_SIG_(static_cast<QSignal>(sig));
QS_OBJ_(obj);
QS_STR_(name);
endRec();
QS_CRIT_EXIT_();
onFlush();
}
//****************************************************************************
/// @note This function is only to be used through macro QS_OBJ_DICTIONARY()
///
void QS::obj_dict(void const * const obj,
char_t const *name)
{
QS_CRIT_STAT_
if (*name == static_cast<char_t>('&')) {
QS_PTR_INC_(name);
}
QS_CRIT_ENTRY_();
beginRec(static_cast<uint_fast8_t>(QS_OBJ_DICT));
QS_OBJ_(obj);
QS_STR_(name);
endRec();
QS_CRIT_EXIT_();
onFlush();
}
//****************************************************************************
/// @note This function is only to be used through macro QS_FUN_DICTIONARY()
///
void QS::fun_dict(void (* const fun)(void), char_t const *name) {
QS_CRIT_STAT_
if (*name == static_cast<char_t>('&')) {
QS_PTR_INC_(name);
}
QS_CRIT_ENTRY_();
beginRec(static_cast<uint_fast8_t>(QS_FUN_DICT));
QS_FUN_(fun);
QS_STR_(name);
endRec();
QS_CRIT_EXIT_();
onFlush();
}
//****************************************************************************
/// @note This function is only to be used through macro QS_USR_DICTIONARY()
///
void QS::usr_dict(enum_t const rec,
char_t const * const name)
{
QS_CRIT_STAT_
QS_CRIT_ENTRY_();
beginRec(static_cast<uint_fast8_t>(QS_USR_DICT));
QS_U8_(static_cast<uint8_t>(rec));
QS_STR_(name);
endRec();
QS_CRIT_EXIT_();
onFlush();
}
//****************************************************************************
/// @note This function is only to be used through macros, never in the
/// client code directly.
///
void QS::mem(uint8_t const *blk, uint8_t size) {
uint8_t b = static_cast<uint8_t>(MEM_T);
uint8_t chksum_ = static_cast<uint8_t>(priv_.chksum + b);
uint8_t *buf_ = priv_.buf; // put in a temporary (register)
QSCtr head_ = priv_.head; // put in a temporary (register)
QSCtr end_ = priv_.end; // put in a temporary (register)
priv_.used += (static_cast<QSCtr>(size) // size+2 bytes to be added
+ static_cast<QSCtr>(2));
QS_INSERT_BYTE(b)
QS_INSERT_ESC_BYTE(size)
// output the 'size' number of bytes
while (size != static_cast<uint8_t>(0)) {
b = *blk;
QS_INSERT_ESC_BYTE(b)
QS_PTR_INC_(blk);
--size;
}
priv_.head = head_; // save the head
priv_.chksum = chksum_; // save the checksum
}
//****************************************************************************
/// @note This function is only to be used through macros, never in the
/// client code directly.
///
void QS::str(char_t const *s) {
uint8_t b = static_cast<uint8_t>(*s);
uint8_t chksum_ = static_cast<uint8_t>(
priv_.chksum + static_cast<uint8_t>(STR_T));
uint8_t *buf_ = priv_.buf; // put in a temporary (register)
QSCtr head_ = priv_.head; // put in a temporary (register)
QSCtr end_ = priv_.end; // put in a temporary (register)
QSCtr used_ = priv_.used; // put in a temporary (register)
used_ += static_cast<QSCtr>(2); // the format byte and the terminating-0
QS_INSERT_BYTE(static_cast<uint8_t>(STR_T))
while (b != static_cast<uint8_t>(0)) {
// ASCII characters don't need escaping
chksum_ += b; // update checksum
QS_INSERT_BYTE(b)
QS_PTR_INC_(s);
b = static_cast<uint8_t>(*s);
++used_;
}
QS_INSERT_BYTE(static_cast<uint8_t>(0)) // zero-terminate the string
priv_.head = head_; // save the head
priv_.chksum = chksum_; // save the checksum
priv_.used = used_; // save # of used buffer space
}
} // namespace QP

View File

@ -1,100 +0,0 @@
/// @file
/// @brief QS long-long (64-bit) output
/// @ingroup qs
/// @cond
///***************************************************************************
/// Last updated for version 5.4.0
/// Last updated on 2015-04-29
///
/// Q u a n t u m L e a P s
/// ---------------------------
/// innovating embedded systems
///
/// Copyright (C) Quantum Leaps, www.state-machine.com.
///
/// This program is open source software: you can redistribute it and/or
/// modify it under the terms of the GNU General Public License as published
/// by the Free Software Foundation, either version 3 of the License, or
/// (at your option) any later version.
///
/// Alternatively, this program may be distributed and modified under the
/// terms of Quantum Leaps commercial licenses, which expressly supersede
/// the GNU General Public License and are specifically designed for
/// licensees interested in retaining the proprietary status of their code.
///
/// This program is distributed in the hope that it will be useful,
/// but WITHOUT ANY WARRANTY; without even the implied warranty of
/// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
/// GNU General Public License for more details.
///
/// You should have received a copy of the GNU General Public License
/// along with this program. If not, see <http://www.gnu.org/licenses/>.
///
/// Contact information:
/// Web: www.state-machine.com
/// Email: info@state-machine.com
///***************************************************************************
/// @endcond
#define QP_IMPL // this is QF/QK implementation
#include "qs_port.h" // QS port
#if (QS_OBJ_PTR_SIZE == 8) || (QS_FUN_PTR_SIZE == 8)
#include "qs_pkg.h" // QS package-scope internal interface
namespace QP {
//****************************************************************************
/// @note This function is only to be used through macros, never in the
/// client code directly.
///
void QS::u64_(uint64_t d) {
uint8_t chksum_ = priv_.chksum;
uint8_t *buf_ = priv_.buf;
QSCtr head_ = priv_.head;
QSCtr end_ = priv_.end;
priv_.used += static_cast<QSCtr>(8); // 8 bytes are about to be added
for (int_fast8_t i = static_cast<int_fast8_t>(8);
i != static_cast<int_fast8_t>(0);
--i)
{
uint8_t b = static_cast<uint8_t>(d);
QS_INSERT_ESC_BYTE(b)
d >>= 8;
}
priv_.head = head_; // save the head
priv_.chksum = chksum_; // save the checksum
}
//****************************************************************************
/// @note This function is only to be used through macros, never in the
/// client code directly.
///
void QS::u64(uint8_t format, uint64_t d) {
uint8_t chksum_ = priv_.chksum;
uint8_t *buf_ = priv_.buf;
QSCtr head_ = priv_.head;
QSCtr end_ = priv_.end;
priv_.used += static_cast<QSCtr>(9); // 9 bytes are about to be added
QS_INSERT_ESC_BYTE(format) // insert the format byte
for (int_fast8_t i = static_cast<int_fast8_t>(8);
i != static_cast<int_fast8_t>(0);
--i)
{
format = static_cast<uint8_t>(d);
QS_INSERT_ESC_BYTE(format)
d >>= 8;
}
priv_.head = head_; // save the head
priv_.chksum = chksum_; // save the checksum
}
} // namespace QP
#endif // (QS_OBJ_PTR_SIZE == 8) || (QS_FUN_PTR_SIZE == 8)

View File

@ -1,129 +0,0 @@
/// @file
/// @brief QS floating point output implementation
/// @ingroup qs
/// @cond
///***************************************************************************
/// Product: QS/C++
/// Last updated for version 5.7.0
/// Last updated on 2016-09-08
///
/// Q u a n t u m L e a P s
/// ---------------------------
/// innovating embedded systems
///
/// Copyright (C) Quantum Leaps, www.state-machine.com.
///
/// This program is open source software: you can redistribute it and/or
/// modify it under the terms of the GNU General Public License as published
/// by the Free Software Foundation, either version 3 of the License, or
/// (at your option) any later version.
///
/// Alternatively, this program may be distributed and modified under the
/// terms of Quantum Leaps commercial licenses, which expressly supersede
/// the GNU General Public License and are specifically designed for
/// licensees interested in retaining the proprietary status of their code.
///
/// This program is distributed in the hope that it will be useful,
/// but WITHOUT ANY WARRANTY; without even the implied warranty of
/// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
/// GNU General Public License for more details.
///
/// You should have received a copy of the GNU General Public License
/// along with this program. If not, see <http://www.gnu.org/licenses/>.
///
/// Contact information:
/// Web: www.state-machine.com
/// Email: info@state-machine.com
///***************************************************************************
/// @endcond
#define QP_IMPL // this is QF/QK implementation
#include "qs_port.h" // QS port
#include "qs_pkg.h" // QS package-scope internal interface
namespace QP {
//****************************************************************************
/// @note This function is only to be used through macros, never in the
/// client code directly.
///
void QS::f32(uint8_t format, float32_t const d) {
union F32Rep {
float32_t f;
uint32_t u;
} fu32; // the internal binary representation
uint8_t chksum_ = priv_.chksum; // put in a temporary (register)
uint8_t *buf_ = priv_.buf; // put in a temporary (register)
QSCtr head_ = priv_.head; // put in a temporary (register)
QSCtr end_ = priv_.end; // put in a temporary (register)
fu32.f = d; // assign the binary representation
priv_.used += static_cast<QSCtr>(5); // 5 bytes about to be added
QS_INSERT_ESC_BYTE(format) // insert the format byte
for (int_t i = static_cast<int_t>(4); i != static_cast<int_t>(0); --i) {
format = static_cast<uint8_t>(fu32.u);
QS_INSERT_ESC_BYTE(format)
fu32.u >>= 8;
}
priv_.head = head_; // save the head
priv_.chksum = chksum_; // save the checksum
}
//****************************************************************************
/// @note This function is only to be used through macros, never in the
/// client code directly.
///
void QS::f64(uint8_t format, float64_t const d) {
union F64Rep {
float64_t d;
struct UInt2 {
uint32_t u1;
uint32_t u2;
} i;
} fu64; // the internal binary representation
uint8_t chksum_ = priv_.chksum;
uint8_t *buf_ = priv_.buf;
QSCtr head_ = priv_.head;
QSCtr end_ = priv_.end;
uint32_t i;
// static constant untion to detect endianness of the machine
static union U32Rep {
uint32_t u32;
uint8_t u8;
} const endian = { static_cast<uint32_t>(1) };
fu64.d = d; // assign the binary representation
priv_.used += static_cast<QSCtr>(9); // 9 bytes about to be added
QS_INSERT_ESC_BYTE(format) // insert the format byte
// is this a big-endian machine?
if (endian.u8 == static_cast<uint8_t>(0)) {
// swap fu64.i.u1 <-> fu64.i.u2...
i = fu64.i.u1;
fu64.i.u1 = fu64.i.u2;
fu64.i.u2 = i;
}
// output 4 bytes from fu64.i.u1 ...
for (i = static_cast<uint32_t>(4); i != static_cast<uint32_t>(0); --i) {
format = static_cast<uint8_t>(fu64.i.u1);
QS_INSERT_ESC_BYTE(format)
fu64.i.u1 >>= 8;
}
// output 4 bytes from fu64.i.u2 ...
for (i = static_cast<uint32_t>(4); i != static_cast<uint32_t>(0); --i) {
format = static_cast<uint8_t>(fu64.i.u2);
QS_INSERT_ESC_BYTE(format)
fu64.i.u2 >>= 8;
}
priv_.head = head_; // update the head
priv_.chksum = chksum_; // update the checksum
}
} // namespace QP

View File

@ -1,98 +0,0 @@
/// @file
/// @ingroup qs
/// @brief Internal (package scope) QS/C++ interface.
/// @cond
///***************************************************************************
/// Last updated for version 5.6.0
/// Last updated on 2015-12-26
///
/// Q u a n t u m L e a P s
/// ---------------------------
/// innovating embedded systems
///
/// Copyright (C) Quantum Leaps. All rights reserved.
///
/// This program is open source software: you can redistribute it and/or
/// modify it under the terms of the GNU General Public License as published
/// by the Free Software Foundation, either version 3 of the License, or
/// (at your option) any later version.
///
/// Alternatively, this program may be distributed and modified under the
/// terms of Quantum Leaps commercial licenses, which expressly supersede
/// the GNU General Public License and are specifically designed for
/// licensees interested in retaining the proprietary status of their code.
///
/// This program is distributed in the hope that it will be useful,
/// but WITHOUT ANY WARRANTY; without even the implied warranty of
/// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
/// GNU General Public License for more details.
///
/// You should have received a copy of the GNU General Public License
/// along with this program. If not, see <http://www.gnu.org/licenses/>.
///
/// Contact information:
/// https://state-machine.com
/// mailto:info@state-machine.com
///***************************************************************************
/// @endcond
#ifndef qs_pkg_h
#define qs_pkg_h
//! Internal QS macro to insert an un-escaped byte into the QS buffer
#define QS_INSERT_BYTE(b_) \
QS_PTR_AT_(buf_, head_) = (b_); \
++head_; \
if (head_ == end_) { \
head_ = static_cast<QSCtr>(0); \
}
//! Internal QS macro to insert an escaped byte into the QS buffer
#define QS_INSERT_ESC_BYTE(b_) \
chksum_ += (b_); \
if (((b_) != QS_FRAME) && ((b_) != QS_ESC)) { \
QS_INSERT_BYTE(b_) \
} \
else { \
QS_INSERT_BYTE(QS_ESC) \
QS_INSERT_BYTE(static_cast<uint8_t>((b_) ^ QS_ESC_XOR)) \
++priv_.used; \
}
//! Internal QS macro to increment the given pointer argument @a ptr_
///
/// @note Incrementing a pointer violates the MISRA-C 2004 Rule 17.4(req),
/// pointer arithmetic other than array indexing. Encapsulating this violation
/// in a macro allows to selectively suppress this specific deviation.
#define QS_PTR_INC_(ptr_) (++(ptr_))
//! Internal QS macro to cast enumerated QS record number to uint8_t
///
/// @note Casting from enum to unsigned char violates the MISRA-C++ 2008 rules
/// 5-2-7, 5-2-8 and 5-2-9. Encapsulating this violation in a macro allows to
/// selectively suppress this specific deviation.
#define QS_REC_NUM_(enum_) (static_cast<uint_fast8_t>(enum_))
namespace QP {
/// @brief Frame character of the QS output protocol
uint8_t const QS_FRAME = static_cast<uint8_t>(0x7E);
/// @brief Escape character of the QS output protocol
uint8_t const QS_ESC = static_cast<uint8_t>(0x7D);
/// @brief Escape modifier of the QS output protocol
///
/// The escaped byte is XOR-ed with the escape modifier before it is inserted
/// into the QS buffer.
uint8_t const QS_ESC_XOR = static_cast<uint8_t>(0x20);
/// @brief Escape character of the QS output protocol
uint8_t const QS_GOOD_CHKSUM = static_cast<uint8_t>(0xFF);
//! send the Target info (object sizes, build time-stamp, QP version)
void QS_target_info_(uint8_t const isReset);
} // namespace QP
#endif // qs_pkg_h

File diff suppressed because it is too large Load Diff

View File

@ -1,433 +0,0 @@
/// @file
/// @brief QF/C++ stub for QUTEST unit testing
/// @ingroup qs
/// @cond
///***************************************************************************
/// Last updated for version 5.9.3
/// Last updated on 2017-06-19
///
/// Q u a n t u m L e a P s
/// ---------------------------
/// innovating embedded systems
///
/// Copyright (C) 2005-2017 Quantum Leaps, LLC. All rights reserved.
///
/// This program is open source software: you can redistribute it and/or
/// modify it under the terms of the GNU General Public License as published
/// by the Free Software Foundation, either version 3 of the License, or
/// (at your option) any later version.
///
/// Alternatively, this program may be distributed and modified under the
/// terms of Quantum Leaps commercial licenses, which expressly supersede
/// the GNU General Public License and are specifically designed for
/// licensees interested in retaining the proprietary status of their code.
///
/// This program is distributed in the hope that it will be useful,
/// but WITHOUT ANY WARRANTY; without even the implied warranty of
/// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
/// GNU General Public License for more details.
///
/// You should have received a copy of the GNU General Public License
/// along with this program. If not, see <http://www.gnu.org/licenses/>.
///
/// Contact information:
/// https://state-machine.com
/// mailto:info@state-machine.com
///***************************************************************************
/// @endcond
// only build when Q_UTEST is defined
#ifdef Q_UTEST
#define QP_IMPL // this is QP implementation
#include "qf_port.h" // QF port
#include "qf_pkg.h" // QF package-scope interface
#include "qassert.h" // QP embedded systems-friendly assertions
#include "qs_port.h" // include QS port
namespace QP {
Q_DEFINE_THIS_MODULE("qutest")
// Global objects ============================================================
uint8_t volatile QF_intNest;
// QF functions ==============================================================
void QF::init(void) {
QF_maxPool_ = static_cast<uint_fast8_t>(0);
QF_subscrList_ = static_cast<QSubscrList *>(0);
QF_maxPubSignal_ = static_cast<enum_t>(0);
QF_intNest = static_cast<uint8_t>(0);
bzero(&active_[0], static_cast<uint_fast16_t>(sizeof(active_)));
}
//............................................................................
int_t QF::run(void) {
// function dictionaries for the standard API
QS_FUN_DICTIONARY(&QActive::post_);
QS_FUN_DICTIONARY(&QActive::postLIFO);
QS::onTestLoop(); // run the unit test
QS::onCleanup(); // application cleanup
return 0; // return no error
}
//............................................................................
bool QActive::post_(QEvt const * const e,
uint_fast16_t const margin,
void const * const sender)
{
QS_TEST_PROBE_DEF(&QActive::post_)
bool status;
QF_CRIT_STAT_
/// @pre event pointer must be valid
Q_REQUIRE_ID(100, e != static_cast<QEvt const *>(0));
QF_CRIT_ENTRY_();
QEQueueCtr nFree = m_eQueue.m_nFree; // get volatile into the temporary
// test probe for reaching the margin
QS_TEST_PROBE(
nFree = static_cast<QEQueueCtr>(qs_tp_ - static_cast<uint32_t>(1));
)
// margin available?
if (((margin == QF_NO_MARGIN) && (nFree > static_cast<QEQueueCtr>(0)))
|| (nFree > static_cast<QEQueueCtr>(margin)))
{
QS_BEGIN_NOCRIT_(QS_QF_ACTIVE_POST_FIFO,
QS::priv_.locFilter[QS::AO_OBJ], this)
QS_TIME_(); // timestamp
QS_OBJ_(sender); // the sender object
QS_SIG_(e->sig); // the signal of the event
QS_OBJ_(this); // this active object
QS_2U8_(e->poolId_, e->refCtr_); // pool Id & refCtr of the evt
QS_EQC_(nFree); // number of free entries
QS_EQC_(m_eQueue.m_nMin); // min number of free entries
QS_END_NOCRIT_()
// is it a dynamic event?
if (e->poolId_ != static_cast<uint8_t>(0)) {
QF_EVT_REF_CTR_INC_(e); // increment the reference counter
}
QF_CRIT_EXIT_();
QF::gc(e); // recycle the event to avoid a leak
status = true; // event "posted" successfully
}
else {
/// @note assert if event cannot be posted and dropping events is
/// not acceptable
Q_ASSERT_ID(110, margin != QF_NO_MARGIN);
QS_BEGIN_NOCRIT_(QS_QF_ACTIVE_POST_ATTEMPT,
QS::priv_.locFilter[QS::AO_OBJ], this)
QS_TIME_(); // timestamp
QS_OBJ_(sender); // the sender object
QS_SIG_(e->sig); // the signal of the event
QS_OBJ_(this); // this active object
QS_2U8_(e->poolId_, e->refCtr_); // pool Id & refCtr of the evt
QS_EQC_(nFree); // number of free entries
QS_EQC_(static_cast<QEQueueCtr>(margin)); // margin requested
QS_END_NOCRIT_()
QF_CRIT_EXIT_();
QF::gc(e); // recycle the evnet to avoid a leak
status = false; // event not posted
}
return status;
}
//............................................................................
void QActive::postLIFO(QEvt const * const e) {
QS_TEST_PROBE_DEF(&QActive::postLIFO)
QF_CRIT_STAT_
QF_CRIT_ENTRY_();
QEQueueCtr nFree = m_eQueue.m_nFree;// tmp to avoid UB for volatile access
QF_CRIT_ENTRY_();
nFree = m_eQueue.m_nFree; // get volatile into the temporary
// test probe for queue overflow
QS_TEST_PROBE_ID(1,
Q_ERROR_ID(210);
)
QS_BEGIN_NOCRIT_(QS_QF_ACTIVE_POST_LIFO,
QS::priv_.locFilter[QS::AO_OBJ], this)
QS_TIME_(); // timestamp
QS_SIG_(e->sig); // the signal of this event
QS_OBJ_(this); // this active object
QS_2U8_(e->poolId_, e->refCtr_); // pool Id & refCtr of the evt
QS_EQC_(nFree); // number of free entries
QS_EQC_(m_eQueue.m_nMin); // min number of free entries
QS_END_NOCRIT_()
// is it a dynamic event?
if (e->poolId_ != static_cast<uint8_t>(0)) {
QF_EVT_REF_CTR_INC_(e); // increment the reference counter
}
QF_CRIT_EXIT_();
}
//............................................................................
void QActive::start(uint_fast8_t const prio,
QEvt const *qSto[], uint_fast16_t const qLen,
void * const, uint_fast16_t const,
QEvt const * const ie)
{
Q_REQUIRE_ID(500, ((uint_fast8_t)0 < prio) /* priority must be in range */
&& (prio <= (uint_fast8_t)QF_MAX_ACTIVE));
m_eQueue.init(qSto, qLen); // initialize QEQueue of this AO
m_prio = prio; // set the QF priority of this AO
QF::add_(this); // make QF aware of this AO
this->init(ie); // take the top-most initial tran. (virtual)
QS_FLUSH(); // flush the trace buffer to the host
}
//............................................................................
void QActive::stop(void) {
QF::remove_(this); // remove this active object from the framework
}
//****************************************************************************
QTimeEvtCtr QTimeEvt::ctr(void) const {
QTimeEvtCtr ret;
QF_CRIT_STAT_
QF_CRIT_ENTRY_();
ret = m_ctr;
QS_BEGIN_NOCRIT_(QS_QF_TIMEEVT_CTR, QS::priv_.locFilter[QS::TE_OBJ], this)
QS_TIME_(); // timestamp
QS_OBJ_(this); // this time event object
QS_OBJ_(m_act); // the target AO
QS_TEC_(ret); // the current counter
QS_TEC_(m_interval); // the interval
QS_U8_((uint8_t)(refCtr_ & (uint8_t)0x7F)); // tick rate
QS_END_NOCRIT_()
QF_CRIT_EXIT_();
return ret;
}
//............................................................................
QTimeEvt::QTimeEvt(QActive * const act,
enum_t const sgnl, uint_fast8_t const tickRate)
:
#ifdef Q_EVT_CTOR
QEvt(static_cast<QSignal>(sgnl)),
#endif
m_next(static_cast<QTimeEvt *>(0)),
m_act(act),
m_ctr(static_cast<QTimeEvtCtr>(0)),
m_interval(static_cast<QTimeEvtCtr>(0))
{
/// @pre The signal must be valid and the tick rate in range
Q_REQUIRE_ID(300, (sgnl >= Q_USER_SIG)
&& (tickRate < static_cast<uint8_t>(QF_MAX_TICK_RATE)));
#ifndef Q_EVT_CTOR
sig = static_cast<QSignal>(sgnl); // set QEvt::sig of this time event
#endif
// Setting the POOL_ID event attribute to zero is correct only for
// events not allocated from event pools, which must be the case
// for Time Events.
//
poolId_ = static_cast<uint8_t>(0);
// The reference counter attribute is not used in static events,
// so for the Time Events it is reused to hold the tickRate in the
// bits [0..6] and the linkedFlag in the MSB (bit [7]). The linkedFlag
// is 0 for time events unlinked from any list and 1 otherwise.
//
refCtr_ = static_cast<uint8_t>(tickRate);
}
//............................................................................
void QTimeEvt::armX(QTimeEvtCtr const nTicks, QTimeEvtCtr const interval) {
uint_fast8_t tickRate = static_cast<uint_fast8_t>(refCtr_)
& static_cast<uint_fast8_t>(0x7F);
QTimeEvtCtr cntr = m_ctr; // temporary to hold volatile
QF_CRIT_STAT_
/// @pre the host AO must be valid, time evnet must be disarmed,
/// number of clock ticks cannot be zero, and the signal must be valid.
///
Q_REQUIRE_ID(400, (m_act != static_cast<void *>(0))
&& (cntr == static_cast<QTimeEvtCtr>(0))
&& (nTicks != static_cast<QTimeEvtCtr>(0))
&& (tickRate < static_cast<uint_fast8_t>(QF_MAX_TICK_RATE))
&& (static_cast<enum_t>(sig) >= Q_USER_SIG));
QF_CRIT_ENTRY_();
m_ctr = nTicks;
m_interval = interval;
QS_BEGIN_NOCRIT_(QS_QF_TIMEEVT_ARM, QS::priv_.locFilter[QS::TE_OBJ], this)
QS_TIME_(); // timestamp
QS_OBJ_(this); // this time event object
QS_OBJ_(m_act); // the active object
QS_TEC_(nTicks); // the number of ticks
QS_TEC_(interval); // the interval
QS_U8_(static_cast<uint8_t>(tickRate)); // tick rate
QS_END_NOCRIT_()
QF_CRIT_EXIT_();
}
//............................................................................
bool QTimeEvt::disarm(void) {
QF_CRIT_STAT_
QF_CRIT_ENTRY_();
bool wasArmed;
// is the time event actually armed?
if (m_ctr != static_cast<QTimeEvtCtr>(0)) {
wasArmed = true;
QS_BEGIN_NOCRIT_(QS_QF_TIMEEVT_DISARM,
QS::priv_.locFilter[QS::TE_OBJ], this)
QS_TIME_(); // timestamp
QS_OBJ_(this); // this time event object
QS_OBJ_(m_act); // the target AO
QS_TEC_(m_ctr); // the number of ticks
QS_TEC_(m_interval); // the interval
// tick rate
QS_U8_(static_cast<uint8_t>(refCtr_& static_cast<uint8_t>(0x7F)));
QS_END_NOCRIT_()
m_ctr = static_cast<QTimeEvtCtr>(0); // schedule removal from the list
}
else { // the time event was already not running
wasArmed = false;
QS_BEGIN_NOCRIT_(QS_QF_TIMEEVT_DISARM_ATTEMPT,
QS::priv_.locFilter[QS::TE_OBJ], this)
QS_TIME_(); // timestamp
QS_OBJ_(this); // this time event object
QS_OBJ_(m_act); // the target AO
// tick rate
QS_U8_(static_cast<uint8_t>(refCtr_& static_cast<uint8_t>(0x7F)));
QS_END_NOCRIT_()
}
QF_CRIT_EXIT_();
return wasArmed;
}
//............................................................................
bool QTimeEvt::rearm(QTimeEvtCtr const nTicks) {
uint_fast8_t tickRate = static_cast<uint_fast8_t>(refCtr_)
& static_cast<uint_fast8_t>(0x7F);
QF_CRIT_STAT_
/// @pre AO must be valid, tick rate must be in range, nTicks must not
/// be zero, and the signal of this time event must be valid
///
Q_REQUIRE_ID(600, (m_act != static_cast<void *>(0))
&& (tickRate < static_cast<uint_fast8_t>(QF_MAX_TICK_RATE))
&& (nTicks != static_cast<QTimeEvtCtr>(0))
&& (static_cast<enum_t>(sig) >= Q_USER_SIG));
QF_CRIT_ENTRY_();
bool isArmed;
// is the time evt not running?
if (m_ctr == static_cast<QTimeEvtCtr>(0)) {
isArmed = false;
// is the time event unlinked?
// NOTE: For a duration of a single clock tick of the specified
// tick rate a time event can be disarmed and yet still linked into
// the list, because unlinking is performed exclusively in the
// QF::tickX() function.
//
if ((refCtr_ & static_cast<uint8_t>(0x80))
== static_cast<uint8_t>(0))
{
refCtr_ |= static_cast<uint8_t>(0x80); // mark as linked
}
}
// the time event is armed
else {
isArmed = true;
}
m_ctr = nTicks; // re-load the tick counter (shift the phasing)
QS_BEGIN_NOCRIT_(QS_QF_TIMEEVT_REARM,
QS::priv_.locFilter[QS::TE_OBJ], this)
QS_TIME_(); // timestamp
QS_OBJ_(this); // this time event object
QS_OBJ_(m_act); // the target AO
QS_TEC_(m_ctr); // the number of ticks
QS_TEC_(m_interval); // the interval
QS_U8_(static_cast<uint8_t>(tickRate)); // the tick rate
if (isArmed) {
QS_U8_(static_cast<uint8_t>(1)); // status: armed
}
else {
QS_U8_(static_cast<uint8_t>(0)); // status: disarmed
}
QS_END_NOCRIT_()
QF_CRIT_EXIT_();
return isArmed;
}
//............................................................................
void QF::tickX_(uint_fast8_t const tickRate, void const * const sender) {
QF_CRIT_STAT_
QF_CRIT_ENTRY_();
if (QS::rxPriv_.currObj[QS::TE_OBJ] != static_cast<void *>(0)) {
QTimeEvt *t = (QTimeEvt *)QS::rxPriv_.currObj[QS::TE_OBJ];
QActive *act = (QActive *)t->m_act; // temp. for volatile
if (t->m_interval == (QTimeEvtCtr)0) { // single-shot TE?
t->m_ctr = (QTimeEvtCtr)0; // auto-disarm
// mark as unlinked
t->refCtr_ &= static_cast<uint8_t>(0x7F);
QS_BEGIN_NOCRIT_(QS_QF_TIMEEVT_AUTO_DISARM,
QS::priv_.locFilter[QS::TE_OBJ], t)
QS_OBJ_(t); // this time event object
QS_OBJ_(act); // the target AO
QS_U8_(static_cast<uint8_t>(tickRate)); // tick rate
QS_END_NOCRIT_()
}
QS_BEGIN_NOCRIT_(QS_QF_TIMEEVT_POST,
QS::priv_.locFilter[QS::TE_OBJ], t)
QS_TIME_(); // timestamp
QS_OBJ_(t); // the time event object
QS_SIG_(t->sig); // signal of this time event
QS_OBJ_(act); // the target AO
QS_U8_(static_cast<uint8_t>(tickRate)); // tick rate
QS_END_NOCRIT_()
QF_CRIT_EXIT_(); // exit crit. section before posting
// Post to the 'act' AO (post does not actually happen)
// NOTE: act->POST() asserts internally if the queue overflows
//
(void)act->POST(t, sender); // asserts if queue overflows
// explicitly dispatch the time event
act->dispatch(t);
}
else {
QF_CRIT_EXIT_();
}
}
} // namespace QP
//****************************************************************************
void Q_onAssert(char const * const module, int_t loc) {
QS_ASSERTION(module, loc, (uint32_t)0); // report assertion to QSPY
QP::QS::onTestLoop(); // loop to wait for commands (typically reset)
QP::QS::onReset(); // in case the QUTEST loop ever returns, reset manually
}
#endif // Q_UTEST

View File

@ -1,259 +0,0 @@
/// @file
/// @brief Cooperative QV kernel, definition of QP::QV_readySet_ and
/// implementation of kernel-specific functions.
/// @ingroup qv
/// @cond
///***************************************************************************
/// Last updated for version 5.9.0
/// Last updated on 2017-05-04
///
/// Q u a n t u m L e a P s
/// ---------------------------
/// innovating embedded systems
///
/// Copyright (C) 2005-2017 Quantum Leaps, LLC. All rights reserved.
///
/// This program is open source software: you can redistribute it and/or
/// modify it under the terms of the GNU General Public License as published
/// by the Free Software Foundation, either version 3 of the License, or
/// (at your option) any later version.
///
/// Alternatively, this program may be distributed and modified under the
/// terms of Quantum Leaps commercial licenses, which expressly supersede
/// the GNU General Public License and are specifically designed for
/// licensees interested in retaining the proprietary status of their code.
///
/// This program is distributed in the hope that it will be useful,
/// but WITHOUT ANY WARRANTY; without even the implied warranty of
/// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
/// GNU General Public License for more details.
///
/// You should have received a copy of the GNU General Public License
/// along with this program. If not, see <http://www.gnu.org/licenses/>.
///
/// Contact information:
/// https://state-machine.com
/// mailto:info@state-machine.com
///***************************************************************************
/// @endcond
#define QP_IMPL // this is QP implementation
#include "qf_port.h" // QF port
#include "qf_pkg.h" // QF package-scope internal interface
#include "qassert.h" // QP embedded systems-friendly assertions
#ifdef Q_SPY // QS software tracing enabled?
#include "qs_port.h" // include QS port
#else
#include "qs_dummy.h" // disable the QS software tracing
#endif // Q_SPY
// protection against including this source file in a wrong project
#ifndef qv_h
#error "Source file included in a project NOT based on the QV kernel"
#endif // qv_h
namespace QP {
Q_DEFINE_THIS_MODULE("qv")
/// @note The functions implemented in this module can have a different
/// implementation in other QF ports. The implementations included here
/// are appropriate for the cooperative QV kernel only.
// Package-scope objects *****************************************************
extern "C" {
QPSet QV_readySet_; // ready set of AOs
} // extern "C"
//****************************************************************************
/// @description
/// Initializes QF and must be called exactly once before any other QF
/// function. Typcially, QP::QF::init() is called from main() even before
/// initializing the Board Support Package (BSP).
///
/// @note QP::QF::init() clears the internal QF variables, so that the
/// framework can start correctly even if the startup code fails to clear
/// the uninitialized data (as is required by the C Standard).
///
void QF::init(void) {
QF_maxPool_ = static_cast<uint_fast8_t>(0);
QF_subscrList_ = static_cast<QSubscrList *>(0);
QF_maxPubSignal_ = static_cast<enum_t>(0);
bzero(&QF::timeEvtHead_[0],
static_cast<uint_fast16_t>(sizeof(QF::timeEvtHead_)));
bzero(&active_[0], static_cast<uint_fast16_t>(sizeof(active_)));
bzero(&QV_readySet_, static_cast<uint_fast16_t>(sizeof(QV_readySet_)));
#ifdef QV_INIT
QV_INIT(); // port-specific initialization of the QV kernel
#endif
}
//****************************************************************************
/// @description
/// This function stops the QF application. After calling this function,
/// QF attempts to gracefully stop the application. This graceful shutdown
/// might take some time to complete. The typical use of this function is
/// for terminating the QF application to return back to the operating
/// system or for handling fatal errors that require shutting down
/// (and possibly re-setting) the system.
///
/// @sa QP::QF::onCleanup()
///
void QF::stop(void) {
onCleanup(); // cleanup callback
// nothing else to do for the "vanilla" kernel
}
//****************************************************************************
/// @description
/// QP::QF::run() is typically called from your startup code after you
/// initialize the QF and start at least one active object with
/// QP::QActive::start().
///
/// @returns QP::QF::run() typically does not return in embedded applications.
/// However, when QP runs on top of an operating system, QP::QF::run() might
/// return and in this case the return represents the error code (0 for
/// success). Typically the value returned from QP::QF::run() is subsequently
/// passed on as return from main().
///
/// @note This function is strongly platform-dependent and is not implemented
/// in the QF, but either in the QF port or in the Board Support Package (BSP)
/// for the given application. All QF ports must implement QP::QF::run().
///
int_t QF::run(void) {
#ifdef Q_SPY
uint_fast8_t pprev = static_cast<uint_fast8_t>(0); // previous priority
#endif
onStartup(); // startup callback
// the combined event-loop and background-loop of the QV kernel...
QF_INT_DISABLE();
for (;;) {
// find the maximum priority AO ready to run
if (QV_readySet_.notEmpty()) {
uint_fast8_t p = QV_readySet_.findMax();
QActive *a = active_[p];
#ifdef Q_SPY
QS_BEGIN_NOCRIT_(QS_SCHED_NEXT,
QS::priv_.locFilter[QS::AO_OBJ], a)
QS_TIME_(); // timestamp
QS_2U8_(static_cast<uint8_t>(p), // prio of the scheduled AO
static_cast<uint8_t>(pprev)); // previous priority
QS_END_NOCRIT_()
pprev = p; // update previous priority
#endif // Q_SPY
QF_INT_ENABLE();
// perform the run-to-completion (RTC) step...
// 1. retrieve the event from the AO's event queue, which by this
// time must be non-empty and The "Vanialla" kernel asserts it.
// 2. dispatch the event to the AO's state machine.
// 3. determine if event is garbage and collect it if so
//
QEvt const *e = a->get_();
a->dispatch(e);
gc(e);
QF_INT_DISABLE();
if (a->m_eQueue.isEmpty()) { // empty queue?
QV_readySet_.remove(p);
}
}
else { // no AO ready to run --> idle
#ifdef Q_SPY
if (pprev != static_cast<uint_fast8_t>(0)) {
QS_BEGIN_NOCRIT_(QS_SCHED_IDLE,
static_cast<void *>(0), static_cast<void *>(0))
QS_TIME_(); // timestamp
QS_U8_(static_cast<uint8_t>(pprev)); // previous prio
QS_END_NOCRIT_()
pprev = static_cast<uint_fast8_t>(0); // update previous prio
}
#endif // Q_SPY
// QV::onIdle() must be called with interrupts DISABLED because
// the determination of the idle condition (no events in the
// queues) can change at any time by an interrupt posting events
// to a queue. QV::onIdle() MUST enable interrupts internally,
// perhaps at the same time as putting the CPU into a power-saving
// mode.
QP::QV::onIdle();
QF_INT_DISABLE();
}
}
#ifdef __GNUC__ // GNU compiler?
return static_cast<int_t>(0);
#endif
}
//****************************************************************************
/// @description
/// Starts execution of the AO and registers the AO with the framework.
///
/// @param[in] prio priority at which to start the active object
/// @param[in] qSto pointer to the storage for the ring buffer of the
/// event queue (used only with the built-in QP::QEQueue)
/// @param[in] qLen length of the event queue (in events)
/// @param[in] stkSto pointer to the stack storage (must be NULL in QV)
/// @param[in] stkSize stack size [bytes]
/// @param[in] ie pointer to the optional initial event (might be NULL).
///
/// @note This function should be called via the macro START().
///
/// @usage
/// The following example shows starting an AO when a per-task stack is needed
/// @include qf_start.cpp
///
void QActive::start(uint_fast8_t const prio,
QEvt const *qSto[], uint_fast16_t const qLen,
void * const stkSto, uint_fast16_t const,
QEvt const * const ie)
{
/// @pre the priority must be in range and the stack storage must not
/// be provided, because the QV kernel does not need per-AO stacks.
///
Q_REQUIRE_ID(500, (static_cast<uint_fast8_t>(0) < prio)
&& (prio <= static_cast<uint_fast8_t>(QF_MAX_ACTIVE))
&& (stkSto == static_cast<void *>(0)));
m_eQueue.init(qSto, qLen); // initialize QEQueue of this AO
m_prio = prio; // set the QF priority of this AO
QF::add_(this); // make QF aware of this AO
this->init(ie); // take the top-most initial tran. (virtual call)
QS_FLUSH(); // flush the trace buffer to the host
}
//****************************************************************************
/// @description
/// The preferred way of calling this function is from within the active
/// object that needs to stop. In other words, an active object should stop
/// itself rather than being stopped by someone else. This policy works
/// best, because only the active object itself "knows" when it has reached
/// the appropriate state for the shutdown.
///
/// @note By the time the AO calls QP::QActive::stop(), it should have
/// unsubscribed from all events and no more events should be directly-posted
/// to it.
///
void QActive::stop(void) {
QF::remove_(this); // remove this active object from the QF
QF_CRIT_STAT_
QF_CRIT_ENTRY_();
QV_readySet_.remove(m_prio); // make sure the AO is not ready
QF_CRIT_EXIT_();
}
} // namespace QP

View File

@ -1,567 +0,0 @@
/// @file
/// @brief QXK/C++ preemptive kernel core functions
/// public interface.
/// @ingroup qxk
/// @cond
///***************************************************************************
/// Last updated for version 5.9.7
/// Last updated on 2017-08-20
///
/// Q u a n t u m L e a P s
/// ---------------------------
/// innovating embedded systems
///
/// Copyright (C) 2005-2017 Quantum Leaps, LLC. All rights reserved.
///
/// This program is open source software: you can redistribute it and/or
/// modify it under the terms of the GNU General Public License as published
/// by the Free Software Foundation, either version 3 of the License, or
/// (at your option) any later version.
///
/// Alternatively, this program may be distributed and modified under the
/// terms of Quantum Leaps commercial licenses, which expressly supersede
/// the GNU General Public License and are specifically designed for
/// licensees interested in retaining the proprietary status of their code.
///
/// This program is distributed in the hope that it will be useful,
/// but WITHOUT ANY WARRANTY; without even the implied warranty of
/// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
/// GNU General Public License for more details.
///
/// You should have received a copy of the GNU General Public License
/// along with this program. If not, see <http://www.gnu.org/licenses/>.
///
/// Contact information:
/// https://state-machine.com
/// mailto:info@state-machine.com
///***************************************************************************
/// @endcond
#define QP_IMPL // this is QP implementation
#include "qf_port.h" // QF port
#include "qxk_pkg.h" // QXK package-scope internal interface
#include "qassert.h" // QP embedded systems-friendly assertions
#ifdef Q_SPY // QS software tracing enabled?
#include "qs_port.h" // include QS port
#else
#include "qs_dummy.h" // disable the QS software tracing
#endif // Q_SPY
// protection against including this source file in a wrong project
#ifndef qxk_h
#error "Source file included in a project NOT based on the QXK kernel"
#endif // qxk_h
Q_DEFINE_THIS_MODULE("qxk")
// Public-scope objects ******************************************************
extern "C" {
QXK_Attr QXK_attr_; // global attributes of the QXK kernel
} // extern "C"
namespace QP {
// Local-scope objects *******************************************************
class QXKIdleThread : public QActive {
public:
QXKIdleThread() : QActive(Q_STATE_CAST(0))
{}
};
//****************************************************************************
/// @description
/// Initializes QF and must be called exactly once before any other QF
/// function. Typically, QF::init() is called from main() even before
/// initializing the Board Support Package (BSP).
///
/// @note QF::init() clears the internal QF variables, so that the framework
/// can start correctly even if the startup code fails to clear the
/// uninitialized data (as is required by the C++ Standard).
///
void QF::init(void) {
static QXKIdleThread s_idleThread;
QF_maxPool_ = static_cast<uint_fast8_t>(0);
QF_subscrList_ = static_cast<QSubscrList *>(0);
QF_maxPubSignal_ = static_cast<enum_t>(0);
bzero(&timeEvtHead_[0], static_cast<uint_fast16_t>(sizeof(timeEvtHead_)));
bzero(&active_[0], static_cast<uint_fast16_t>(sizeof(active_)));
bzero(&QXK_attr_, static_cast<uint_fast16_t>(sizeof(QXK_attr_)));
bzero(&s_idleThread, static_cast<uint_fast16_t>(sizeof(s_idleThread)));
// setup the QXK scheduler as initially locked and not running
QXK_attr_.lockPrio = static_cast<uint_fast8_t>(QF_MAX_ACTIVE + 1);
// setup the QXK idle loop...
active_[0] = &s_idleThread; // register the idle thread with QF
QXK_attr_.actPrio = static_cast<uint_fast8_t>(0); // set idle thread prio
#ifdef QXK_INIT
QXK_INIT(); // port-specific initialization of the QXK kernel
#endif
}
//****************************************************************************
/// @description
/// This function stops the QF application. After calling this function,
/// QF attempts to gracefully stop the application. This graceful shutdown
/// might take some time to complete. The typical use of this function is
/// for terminating the QF application to return back to the operating
/// system or for handling fatal errors that require shutting down
/// (and possibly re-setting) the system.
///
/// @sa QF::onCleanup()
///
void QF::stop(void) {
onCleanup(); // application-specific cleanup callback
// nothing else to do for the preemptive QXK kernel
}
//****************************************************************************
//! process all events posted during initialization
static void initial_events(void); // prototype
static void initial_events(void) {
QXK_attr_.lockPrio = static_cast<uint_fast8_t>(0); // unlock the scheduler
// any active objects need to be scheduled before starting event loop?
if (QXK_sched_() != static_cast<uint_fast8_t>(0)) {
QXK_activate_(); // process all events produced so far
}
}
//****************************************************************************
/// @description
/// QF::run() is typically called from main() after you initialize
/// the QF and start at least one active object with QActive::start().
///
/// @returns In QXK, the QF::run() function does not return.
///
int_t QF::run(void) {
QF_INT_DISABLE();
initial_events(); // process all events posted during initialization
onStartup(); // application-specific startup callback
QF_INT_ENABLE();
// the QXK idle loop...
for (;;) {
QXK::onIdle(); // application-specific QXK idle callback
}
#ifdef __GNUC__ // GNU compiler?
return static_cast<int_t>(0);
#endif
}
//****************************************************************************
// @description
// Starts execution of the AO and registers the AO with the framework.
// Also takes the top-most initial transition in the AO's state machine.
// This initial transition is taken in the callee's thread of execution.
//
// @param[in] prio priority at which to start the active object
// @param[in] qSto pointer to the storage for the ring buffer of the
// event queue (used only with the built-in QP::QEQueue)
// @param[in] qLen length of the event queue [events]
// @param[in] stkSto pointer to the stack storage (used only when
// per-AO stack is needed)
// @param[in] stkSize stack size [bytes]
// @param[in] ie pointer to the optional initialization event
// (might be NULL).
//
void QActive::start(uint_fast8_t const prio,
QEvt const *qSto[], uint_fast16_t const qLen,
void * const stkSto, uint_fast16_t const stkSize,
QEvt const * const ie)
{
/// @pre AO cannot be started:
/// - from an ISR;
/// - the priority must be in range;
/// - the stack storage must NOT be provided (because the QXK kernel does
/// not need per-AO stacks).
Q_REQUIRE_ID(200, (!QXK_ISR_CONTEXT_())
&& (static_cast<uint_fast8_t>(0) < prio)
&& (prio <= static_cast<uint_fast8_t>(QF_MAX_ACTIVE))
&& (stkSto == static_cast<void *>(0))
&& (stkSize == static_cast<uint_fast16_t>(0)));
m_eQueue.init(qSto, qLen); // initialize QEQueue of this AO
m_osObject = static_cast<void *>(0); // no private stack for AO
m_prio = prio; // set the QF priority of this AO
m_startPrio = prio; // set the start QF priority of this AO
QF::add_(this); // make QF aware of this AO
this->init(ie); // take the top-most initial tran. (virtual)
QS_FLUSH(); // flush the trace buffer to the host
// see if this AO needs to be scheduled in case QXK is running
QF_CRIT_STAT_
QF_CRIT_ENTRY_();
if (QXK_sched_() != static_cast<uint_fast8_t>(0)) { // activation needed?
QXK_activate_();
}
QF_CRIT_EXIT_();
}
//****************************************************************************
// @description
// This function must be called from within the AO that needs to stop.
// In other words, an AO should stop itself rather than being stopped by
// someone else. This policy works best, because only the AO itself "knows"
// when it has reached the appropriate state for the shutdown.
//
// @note
// By the time the AO calls QP::QActive::stop(), it should have unsubscribed
// from all events and no more events should be directly-posted to it.
//
void QActive::stop(void) {
/// @pre QActive::stop() must be called from the AO that wants to stop.
Q_REQUIRE_ID(300, (this == QF::active_[QXK_attr_.actPrio]));
QF::remove_(this); // remove this active object from the QF
QF_CRIT_STAT_
QF_CRIT_ENTRY_();
QXK_attr_.readySet.remove(m_prio);
if (QXK_sched_() != static_cast<uint_fast8_t>(0)) {
QXK_activate_();
}
QF_CRIT_EXIT_();
}
//****************************************************************************
/// @description
/// This function locks the QXK scheduler to the specified ceiling.
///
/// @param[in] ceiling priority ceiling to which the QXK scheduler
/// needs to be locked
///
/// @returns
/// The previous QXK Scheduler lock status, which is to be used to unlock
/// the scheduler by restoring its previous lock status in QXK::schedUnlock().
///
/// @note
/// QXK::schedLock() must be always followed by the corresponding
/// QXK::schedUnlock().
///
/// @sa QXK::schedUnlock()
///
/// @usage
/// The following example shows how to lock and unlock the QXK scheduler:
/// @include qxk_lock.cpp
///
QSchedStatus QXK::schedLock(uint_fast8_t const ceiling) {
QSchedStatus stat;
QF_CRIT_STAT_
QF_CRIT_ENTRY_();
/// @pre The QXK scheduler lock:
/// - cannot be called from an ISR;
Q_REQUIRE_ID(400, !QXK_ISR_CONTEXT_());
// first store the previous lock prio
if (QXK_attr_.lockPrio < ceiling) { // raising the lock prio?
stat = static_cast<QSchedStatus>(QXK_attr_.lockPrio << 8);
QXK_attr_.lockPrio = ceiling;
QS_BEGIN_NOCRIT_(QS_SCHED_LOCK,
static_cast<void *>(0), static_cast<void *>(0))
QS_TIME_(); // timestamp
QS_2U8_(static_cast<uint8_t>(stat), /* the previous lock prio */
static_cast<uint8_t>(QXK_attr_.lockPrio)); // new lock prio
QS_END_NOCRIT_()
// add the previous lock holder priority
stat |= static_cast<QSchedStatus>(QXK_attr_.lockHolder);
QXK_attr_.lockHolder =
(QXK_attr_.curr != static_cast<QActive *>(0))
? QXK_attr_.curr->m_prio
: static_cast<uint_fast8_t>(0);
}
else {
stat = static_cast<QSchedStatus>(0xFF);
}
QF_CRIT_EXIT_();
return stat; // return the status to be saved in a stack variable
}
//****************************************************************************
///
/// @description
/// This function unlocks the QXK scheduler to the previous status.
///
/// @param[in] stat previous QXK Scheduler lock status returned from
/// QXK::schedLock()
/// @note
/// QXK::schedUnlock() must always follow the corresponding
/// QXK::schedLock().
///
/// @sa QXK::schedLock()
///
/// @usage
/// The following example shows how to lock and unlock the QXK scheduler:
/// @include qxk_lock.cpp
///
void QXK::schedUnlock(QSchedStatus const stat) {
// has the scheduler been actually locked by the last QXK_schedLock()?
if (stat != static_cast<QSchedStatus>(0xFF)) {
uint_fast8_t lockPrio = QXK_attr_.lockPrio; // volatilie into tmp
uint_fast8_t prevPrio = static_cast<uint_fast8_t>(stat >> 8);
QF_CRIT_STAT_
QF_CRIT_ENTRY_();
/// @pre The scheduler cannot be unlocked:
/// - from the ISR context; and
/// - the current lock priority must be greater than the previous
Q_REQUIRE_ID(500, (!QXK_ISR_CONTEXT_())
&& (lockPrio > prevPrio));
QS_BEGIN_NOCRIT_(QS_SCHED_UNLOCK,
static_cast<void *>(0), static_cast<void *>(0))
QS_TIME_(); // timestamp
QS_2U8_(static_cast<uint8_t>(lockPrio),/* prio before unlocking */
static_cast<uint8_t>(prevPrio));// prio after unlocking
QS_END_NOCRIT_()
// restore the previous lock priority and lock holder
QXK_attr_.lockPrio = prevPrio;
QXK_attr_.lockHolder =
static_cast<uint_fast8_t>(stat & static_cast<QSchedStatus>(0xFF));
// find the highest-prio thread ready to run
if (QXK_sched_() != static_cast<uint_fast8_t>(0)) { // priority found?
QXK_activate_(); // activate any unlocked basic threads
}
QF_CRIT_EXIT_();
}
}
} // namespace QP
//============================================================================
extern "C" {
//****************************************************************************
/// @description
/// The QXK scheduler finds the priority of the highest-priority thread
/// that is ready to run.
///
/// @returns the 1-based priority of the the active object to run next,
/// or zero if no eligible active object is found.
///
/// @attention
/// QXK_sched_() must be always called with interrupts **disabled** and
/// returns with interrupts **disabled**.
///
uint_fast8_t QXK_sched_(void) {
// find the highest-prio thread ready to run
uint_fast8_t p = QXK_attr_.readySet.findMax();
if (p <= QXK_attr_.lockPrio) { // is it below the lock prio?
p = QXK_attr_.lockHolder; // prio of the thread holding the lock
}
QP::QActive *next = QP::QF::active_[p];
// the thread found must be registered in QF
Q_ASSERT_ID(610, next != static_cast<QP::QActive *>(0));
// is the current thread a basic-thread?
if (QXK_attr_.curr == static_cast<QP::QActive *>(0)) {
// is next a basic-thread?
if (next->m_osObject == static_cast<void *>(0)) {
if (p <= QXK_attr_.actPrio) {
QXK_attr_.next = static_cast<QP::QActive *>(0);
p = static_cast<uint_fast8_t>(0); // no activation needed
}
else {
QXK_attr_.next = next;
}
}
else { // this is an extened-thread
QS_BEGIN_NOCRIT_(QP::QS_SCHED_NEXT,
QP::QS::priv_.locFilter[QP::QS::AO_OBJ],
QXK_attr_.next)
QS_TIME_(); // timestamp
QS_2U8_(static_cast<uint8_t>(p), // prio of the next thread
static_cast<uint8_t>( // prio of the curent thread
QXK_attr_.actPrio));
QS_END_NOCRIT_()
QXK_attr_.next = next;
p = static_cast<uint_fast8_t>(0); // no activation needed
QXK_CONTEXT_SWITCH_();
}
}
else { // currently executing an extended-thread
// is the new prio different from the current prio?
if (p != QXK_attr_.curr->m_prio) {
QS_BEGIN_NOCRIT_(QP::QS_SCHED_NEXT,
QP::QS::priv_.locFilter[QP::QS::AO_OBJ],
QXK_attr_.next)
QS_TIME_(); // timestamp
QS_2U8_(static_cast<uint8_t>(p), // prio of the next thread
static_cast<uint8_t>(QXK_attr_.curr->m_prio));
QS_END_NOCRIT_()
QXK_attr_.next = next;
p = static_cast<uint_fast8_t>(0); // no activation needed
QXK_CONTEXT_SWITCH_();
}
else {
QXK_attr_.next = static_cast<QP::QActive *>(0);
p = static_cast<uint_fast8_t>(0); // no activation needed
}
}
return p;
}
//****************************************************************************
/// @attention
/// QXK_activate_() must be always called with interrupts **disabled** and
/// returns with interrupts **disabled**.
///
/// @note
/// The activate function might enable interrupts internally, but it always
/// returns with interrupts **disabled**.
///
void QXK_activate_(void) {
uint_fast8_t pin = QXK_attr_.actPrio; // save the initial active prio
uint_fast8_t p = QXK_attr_.next->m_prio;
QP::QActive *a;
// QS tracing or thread-local storage?
#ifdef Q_SPY
uint_fast8_t pprev = pin;
#endif // Q_SPY
// loop until no more ready-to-run AOs of higher prio than the initial
do {
a = QP::QF::active_[p]; // obtain the pointer to the AO
QXK_attr_.actPrio = p; // this becomes the active prio
QXK_attr_.next = static_cast<QP::QActive *>(0); // clear the next AO
QS_BEGIN_NOCRIT_(QP::QS_SCHED_NEXT,
QP::QS::priv_.locFilter[QP::QS::AO_OBJ], a)
QS_TIME_(); // timestamp
QS_2U8_(static_cast<uint8_t>(p), // prio of the next thread
static_cast<uint8_t>(pprev)); // prio of the prev thread
QS_END_NOCRIT_()
#ifdef Q_SPY
if (p != pprev) { // changing priorities?
pprev = p; // update previous priority
}
#endif // Q_SPY
QF_INT_ENABLE(); // unconditionally enable interrupts
// perform the run-to-completion (RTC) step...
// 1. retrieve the event from the AO's event queue, which by this
// time must be non-empty and QActive_get_() asserts it.
// 2. dispatch the event to the AO's state machine.
// 3. determine if event is garbage and collect it if so
//
QP::QEvt const *e = a->get_();
a->dispatch(e);
QP::QF::gc(e);
QF_INT_DISABLE(); // unconditionally disable interrupts
if (a->m_eQueue.isEmpty()) { // empty queue?
QXK_attr_.readySet.remove(p);
}
// find new highest-prio AO ready to run...
p = QXK_attr_.readySet.findMax();
if (p <= QXK_attr_.lockPrio) { // is it below the lock prio?
p = QXK_attr_.lockHolder; // prio of the thread holding lock
}
a = QP::QF::active_[p];
// the AO must be registered in QF
Q_ASSERT_ID(710, a != static_cast<QP::QActive *>(0));
// is the next an AO-thread?
if (a->m_osObject == static_cast<void *>(0)) {
if (p <= pin) {
QXK_attr_.next = static_cast<QP::QActive *>(0);
p = static_cast<uint_fast8_t>(0); // no activation needed
}
else {
QXK_attr_.next = a;
}
}
else { // next is the-extened thread
QS_BEGIN_NOCRIT_(QP::QS_SCHED_NEXT,
QP::QS::priv_.locFilter[QP::QS::AO_OBJ],
QXK_attr_.next)
QS_TIME_(); // timestamp
QS_2U8_(static_cast<uint8_t>(p), /* prio of the next thr */
static_cast<uint8_t>( /* prio of the curent thr */
QXK_attr_.actPrio));
QS_END_NOCRIT_()
QXK_attr_.next = a;
p = static_cast<uint_fast8_t>(0); // no activation needed
QXK_CONTEXT_SWITCH_();
}
} while (p != static_cast<uint_fast8_t>(0)); // while activation needed
QXK_attr_.actPrio = pin; // restore the active priority (!)
#ifdef Q_SPY
if (pin != static_cast<uint_fast8_t>(0)) { // resuming an active object?
a = QP::QF::active_[pin]; // the pointer to the preempted AO
QS_BEGIN_NOCRIT_(QP::QS_SCHED_RESUME,
QP::QS::priv_.locFilter[QP::QS::AO_OBJ], a)
QS_TIME_(); // timestamp
QS_2U8_(static_cast<uint8_t>(p), // prio of the next thread
static_cast<uint8_t>(pprev)); // prio of the prev thread
QS_END_NOCRIT_()
}
else { // resuming priority==0 --> idle
QS_BEGIN_NOCRIT_(QP::QS_SCHED_IDLE,
static_cast<void *>(0), static_cast<void *>(0))
QS_TIME_(); // timestamp
QS_U8_(static_cast<uint8_t>(pprev)); // previous prio
QS_END_NOCRIT_()
}
#endif // Q_SPY
}
//****************************************************************************
QP::QActive *QXK_current(void) {
QP::QActive *curr;
QF_CRIT_STAT_
QF_CRIT_ENTRY_();
/// @pre the QXK kernel must be running
Q_REQUIRE_ID(800,
QXK_attr_.lockPrio <= static_cast<uint_fast8_t>(QF_MAX_ACTIVE));
curr = QXK_attr_.curr;
if (curr == static_cast<QP::QActive *>(0)) { // basic thread?
curr = QP::QF::active_[QXK_attr_.actPrio];
}
QF_CRIT_EXIT_();
//! @post the current thread must be valid
Q_ENSURE_ID(890, curr != static_cast<QP::QActive *>(0));
return curr;
}
} // extern "C"

View File

@ -1,440 +0,0 @@
/// @file
/// @brief Priority-ceiling blocking mutex QP::QXMutex class definition
/// @ingroup qxk
/// @cond
///***************************************************************************
/// Product: QK/C++
/// Last updated for version 5.9.9
/// Last updated on 2017-09-29
///
/// Q u a n t u m L e a P s
/// ---------------------------
/// innovating embedded systems
///
/// Copyright (C) Quantum Leaps, LLC. All rights reserved.
///
/// This program is open source software: you can redistribute it and/or
/// modify it under the terms of the GNU General Public License as published
/// by the Free Software Foundation, either version 3 of the License, or
/// (at your option) any later version.
///
/// Alternatively, this program may be distributed and modified under the
/// terms of Quantum Leaps commercial licenses, which expressly supersede
/// the GNU General Public License and are specifically designed for
/// licensees interested in retaining the proprietary status of their code.
///
/// This program is distributed in the hope that it will be useful,
/// but WITHOUT ANY WARRANTY; without even the implied warranty of
/// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
/// GNU General Public License for more details.
///
/// You should have received a copy of the GNU General Public License
/// along with this program. If not, see <http://www.gnu.org/licenses/>.
///
/// Contact information:
/// https://state-machine.com
/// mailto:info@state-machine.com
///***************************************************************************
/// @endcond
#define QP_IMPL // this is QP implementation
#include "qf_port.h" // QF port
#include "qxk_pkg.h" // QXK package-scope interface
#include "qassert.h" // QP embedded systems-friendly assertions
#ifdef Q_SPY // QS software tracing enabled?
#include "qs_port.h" // include QS port
#else
#include "qs_dummy.h" // disable the QS software tracing
#endif // Q_SPY
// protection against including this source file in a wrong project
#ifndef qxk_h
#error "Source file included in a project NOT based on the QXK kernel"
#endif // qxk_h
namespace QP {
Q_DEFINE_THIS_MODULE("qxk_mutex")
//****************************************************************************
/// @description
/// Initialize the QXK priority ceiling mutex.
///
/// @param[in] ceiling the ceiling-priotity of this mutex or zero.
///
/// @note
/// `ceiling == 0` means that the priority-ceiling protocol shall __not__
/// be used by this mutex. Such mutex will __not__ change (boost) the
/// priority of the holding thread.
///
/// @note
/// `ceiling > 0` means that the priority-ceiling protocol shall be used
/// by this mutex. Such mutex __will__ boost the priority of the holding
/// thread to the `ceiling` level for as long as the thread holds this mutex.
///
/// @attention
/// When the priority-ceiling protocol is used (`ceiling > 0`), the
/// `ceiling` priority must be unused by any other thread or mutex.
/// Also, the `ceiling` priority must be higher than priority of any thread
/// that uses this mutex.
///
/// @usage
/// @include qxk_mux.cpp
///
void QXMutex::init(uint_fast8_t ceiling) {
QF_CRIT_STAT_
QF_CRIT_ENTRY_();
/// @pre the celiling priority of the mutex must:
/// - cannot exceed the maximum #QF_MAX_ACTIVE;
/// - the ceiling priority of the mutex must not be already in use;
/// (QF requires priority to be **unique**).
Q_REQUIRE_ID(100,
(ceiling <= static_cast<uint_fast8_t>(QF_MAX_ACTIVE))
&& ((ceiling == static_cast<uint_fast8_t>(0))
|| (QF::active_[ceiling] == static_cast<QActive *>(0))));
m_ceiling = static_cast<uint8_t>(ceiling);
m_lockNest = static_cast<uint8_t>(0);
m_holderPrio = static_cast<uint8_t>(0);
QF::bzero(&m_waitSet, static_cast<uint_fast16_t>(sizeof(m_waitSet)));
if (ceiling != static_cast<uint_fast8_t>(0)) {
// reserve the ceiling priority level for this mutex
QF::active_[ceiling] = reinterpret_cast<QActive *>(this);
}
QF_CRIT_EXIT_();
}
//****************************************************************************
/// @description
/// Lock the QXK priority ceiling mutex QP::QXMutex.
///
/// @param[in] nTicks number of clock ticks (at the associated rate)
/// to wait for the semaphore. The value of
/// #QXTHREAD_NO_TIMEOUT indicates that no timeout will
/// occur and the semaphore will wait indefinitely.
/// @returns
/// 'true' if the mutex has been acquired and 'false' if a timeout occured.
///
/// @note
/// The mutex locks are allowed to nest, meaning that the same extended thread
/// can lock the same mutex multiple times (< 255). However, each call to
/// QXMutex::lock() must be ballanced by the matching call to
/// QXMutex::unlock().
///
/// @usage
/// @include qxk_mux.cpp
///
bool QXMutex::lock(uint_fast16_t const nTicks) {
QXThread *curr;
QF_CRIT_STAT_
QF_CRIT_ENTRY_();
curr = static_cast<QXThread *>(QXK_attr_.curr);
/// @pre this function must:
/// - NOT be called from an ISR;
/// - be called from an extended thread;
/// - the ceiling priority must not be used; or if used
/// - the thread priority must be below the ceiling of the mutex;
/// - the thread must NOT be holding a scheduler lock;
/// - the thread must NOT be already blocked on any object.
Q_REQUIRE_ID(200, (!QXK_ISR_CONTEXT_()) /* don't call from an ISR! */
&& (curr != static_cast<QXThread *>(0)) /* curr must be extended */
&& ((m_ceiling == static_cast<uint8_t>(0)) /* below ceiling */
|| (curr->m_startPrio < static_cast<uint_fast8_t>(m_ceiling)))
&& (QXK_attr_.lockHolder != curr->m_prio) /* not holding a lock */
&& (curr->m_temp.obj == static_cast<QMState *>(0))); // not blocked
// is the mutex available?
if (m_lockNest == static_cast<uint8_t>(0)) {
m_lockNest = static_cast<uint8_t>(1);
if (m_ceiling != static_cast<uint8_t>(0)) {
// the priority slot must be set to this mutex */
Q_ASSERT_ID(210,
QF::active_[m_ceiling] == reinterpret_cast<QActive *>(this));
// switch the priority of this thread to the mutex ceiling
curr->m_prio = static_cast<uint_fast8_t>(m_ceiling);
QF::active_[m_ceiling] = curr;
QXK_attr_.readySet.remove(curr->m_startPrio);
QXK_attr_.readySet.insert(curr->m_prio);
}
m_holderPrio = static_cast<uint8_t>(curr->m_startPrio);
QS_BEGIN_NOCRIT_(QS_MUTEX_LOCK,
static_cast<void *>(0), static_cast<void *>(0))
QS_TIME_(); // timestamp
QS_2U8_(static_cast<uint8_t>(curr->m_startPrio), /* start prio */
m_ceiling); // current ceiling
QS_END_NOCRIT_()
}
// is the mutex locked by this thread already (nested locking)?
else if (static_cast<uint_fast8_t>(m_holderPrio) == curr->m_startPrio) {
// the nesting level must not exceed 0xFF
Q_ASSERT_ID(220, m_lockNest < static_cast<uint8_t>(0xFF));
++m_lockNest;
}
else { // the mutex is alredy locked by a different thread
// the ceiling holder priority must be valid
Q_ASSERT_ID(230, m_holderPrio != static_cast<uint8_t>(0));
if (m_ceiling != static_cast<uint8_t>(0)) {
// the prio slot must be claimed by the thread holding the mutex
Q_ASSERT_ID(240,
QF::active_[m_ceiling] != static_cast<QActive *>(0));
}
// remove this thr prio from the ready set (block)
// and insert to the waiting set on this mutex
QXK_attr_.readySet.remove(curr->m_prio);
m_waitSet.insert(curr->m_prio);
// store the blocking object (this mutex)
curr->m_temp.obj = reinterpret_cast<QMState *>(this);
curr->teArm_(static_cast<enum_t>(QXK_SEMA_SIG), nTicks);
// schedule the next thread if multitasking started
(void)QXK_sched_();
QF_CRIT_EXIT_();
QF_CRIT_EXIT_NOP(); // BLOCK here
QF_CRIT_ENTRY_();
// the blocking object of the current thread must be this mutex
Q_ASSERT_ID(240,
curr->m_temp.obj == reinterpret_cast<QMState *>(this));
curr->m_temp.obj = static_cast<QMState *>(0); // clear blocking obj.
}
QF_CRIT_EXIT_();
// signal of non-zero means that the time event has not expired
return curr->m_timeEvt.sig != static_cast<QSignal>(0);
}
//****************************************************************************
/// @description
/// Try to lock the QXK priority ceiling mutex QP::QXMutex.
///
/// @returns
/// 'true' if the mutex was successfully locked and 'false' if the mutex was
/// unavailable and was NOT locked.
///
/// @note
/// This function **can** be called from both basic threads (active objects)
/// and extended threads.
///
/// @note
/// The mutex locks are allowed to nest, meaning that the same extended thread
/// can lock the same mutex multiple times (< 255). However, each successful
/// call to QXMutex::tryLock() must be ballanced by the matching call to
/// QXMutex::unlock().
///
bool QXMutex::tryLock(void) {
QActive *curr;
QF_CRIT_STAT_
QF_CRIT_ENTRY_();
curr = QXK_attr_.curr;
if (curr == static_cast<QActive *>(0)) { // called from a basic thread?
curr = QF::active_[QXK_attr_.actPrio];
}
/// @pre this function must:
/// - NOT be called from an ISR;
/// - the QXK kernel must be running;
/// - the calling thread must be valid;
/// - the ceiling must be not used; or
/// - the thread priority must be below the ceiling of the mutex;
/// - the thread must NOT be holding a scheduler lock;
Q_REQUIRE_ID(300, (!QXK_ISR_CONTEXT_()) /* don't call from an ISR! */
&& (QXK_attr_.lockPrio <= static_cast<uint_fast8_t>(QF_MAX_ACTIVE))
&& (curr != static_cast<QActive *>(0)) /* curr thread must be valid */
&& ((m_ceiling == static_cast<uint8_t>(0)) /* below ceiling */
|| (curr->m_startPrio < static_cast<uint_fast8_t>(m_ceiling)))
&& (curr->m_prio != QXK_attr_.lockHolder)); // not holding a lock
// is the mutex available?
if (m_lockNest == static_cast<uint8_t>(0)) {
m_lockNest = static_cast<uint8_t>(1);
if (m_ceiling != static_cast<uint8_t>(0)) {
// the priority slot must be set to this mutex
Q_ASSERT_ID(310,
QF::active_[m_ceiling] == reinterpret_cast<QActive *>(this));
// switch the priority of this thread to the mutex ceiling
curr->m_prio = static_cast<uint_fast8_t>(m_ceiling);
QF::active_[m_ceiling] = curr;
QXK_attr_.readySet.remove(curr->m_startPrio);
QXK_attr_.readySet.insert(curr->m_prio);
}
// make curr thread the new mutex holder
m_holderPrio = static_cast<uint8_t>(curr->m_startPrio);
QS_BEGIN_NOCRIT_(QS_MUTEX_LOCK,
static_cast<void *>(0), static_cast<void *>(0))
QS_TIME_(); // timestamp
QS_2U8_(static_cast<uint8_t>(curr->m_startPrio), /* start prio */
m_ceiling); // current ceiling
QS_END_NOCRIT_()
}
// is the mutex held by this thread already (nested locking)?
else if (static_cast<uint_fast8_t>(m_holderPrio) == curr->m_startPrio) {
// the nesting level must not exceed 0xFF
Q_ASSERT_ID(320, m_lockNest < static_cast<uint8_t>(0xFF));
++m_lockNest;
}
else { // the mutex is alredy locked by a different thread
if (m_ceiling != static_cast<uint8_t>(0)) {
// the prio slot must be claimed by the mutex holder
Q_ASSERT_ID(330, (m_holderPrio != static_cast<uint8_t>(0))
&& (QF::active_[m_ceiling] != QF::active_[m_holderPrio]));
}
curr = static_cast<QActive *>(0); // means that mutex is NOT available
}
QF_CRIT_EXIT_();
return curr != static_cast<QActive *>(0);
}
//****************************************************************************
/// @description
/// Unlock the QXK priority ceiling mutex.
///
/// @note
/// This function **can** be called from both basic threads (active objects)
/// and extended threads.
///
/// @note
/// The mutex locks are allowed to nest, meaning that the same extended thread
/// can lock the same mutex multiple times (< 255). However, each call to
/// QXMutex::lock() or a _successfull_ call to QXMutex::tryLock() must be
/// ballanced by the matching call to QXMutex::unlock().
///
/// @usage
/// @include qxk_mux.cpp
///
void QXMutex::unlock(void) {
QActive *curr;
QF_CRIT_STAT_
QF_CRIT_ENTRY_();
curr = static_cast<QActive *>(QXK_attr_.curr);
if (curr == static_cast<QActive *>(0)) { // called from a basic thread?
curr = QF::active_[QXK_attr_.actPrio];
}
/// @pre this function must:
/// - NOT be called from an ISR;
/// - the calling thread must be valid;
/// - the mutex must be held by this thread;
/// - the ceiling must not be used or
/// - the current thread must have priority equal to the mutex ceiling;
/// - the mutex must be already locked at least once.
Q_REQUIRE_ID(400, (!QXK_ISR_CONTEXT_()) /* don't call from an ISR! */
&& (curr != static_cast<QActive *>(0)) /* curr must be valid */
&& (curr->m_startPrio == static_cast<uint_fast8_t>(m_holderPrio))
&& ((m_ceiling == static_cast<uint8_t>(0)) /* curr at ceiling prio */
|| (curr->m_prio == static_cast<uint_fast8_t>(m_ceiling)))
&& (m_lockNest > static_cast<uint8_t>(0))); // locked at least once
// is this the last nesting level?
if (m_lockNest == static_cast<uint8_t>(1)) {
if (m_ceiling != static_cast<uint8_t>(0)) {
// restore the holding thread's priority to the original
curr->m_prio = curr->m_startPrio;
// remove the boosted priority and insert the original priority
QXK_attr_.readySet.remove(static_cast<uint_fast8_t>(m_ceiling));
QXK_attr_.readySet.insert(curr->m_startPrio);
}
// the mutex no longer held by a thread
m_holderPrio = static_cast<uint8_t>(0);
QS_BEGIN_NOCRIT_(QS_MUTEX_UNLOCK,
static_cast<void *>(0), static_cast<void *>(0))
QS_TIME_(); // timestamp
QS_2U8_(static_cast<uint8_t>(curr->m_startPrio), /* start prio */
m_ceiling); // the mutex ceiling
QS_END_NOCRIT_()
// are any other threads waiting for this mutex?
if (m_waitSet.notEmpty()) {
// find the highest-priority waiting thread
uint_fast8_t p = m_waitSet.findMax();
QXThread *thr = static_cast<QXThread *>(QF::active_[p]);
// the waiting thread must:
// (1) the ceiling must not be used; or if used
// the thread must have priority below the ceiling
// (2) be extended
// (3) not be redy to run
// (4) have still the start priority
// (5) be blocked on this mutex
Q_ASSERT_ID(410,
((m_ceiling == static_cast<uint8_t>(0)) /* below ceiling */
|| (p < static_cast<uint_fast8_t>(m_ceiling)))
&& (thr != static_cast<QXThread *>(0)) /* extended thread */
&& (!QXK_attr_.readySet.hasElement(p))
&& (thr->m_prio == thr->m_startPrio)
&& (thr->m_temp.obj == reinterpret_cast<QMState *>(this)));
// disarm the internal time event
(void)thr->teDisarm_();
// this thread is no longer waiting for the mutex
m_waitSet.remove(p);
if (m_ceiling != static_cast<uint8_t>(0)) {
// switch the priority of this thread to the mutex ceiling
thr->m_prio = static_cast<uint_fast8_t>(m_ceiling);
QF::active_[m_ceiling] = thr;
}
// make thr the new mutex holder
m_holderPrio = static_cast<uint8_t>(thr->m_startPrio);
// make the thread ready to run
QXK_attr_.readySet.insert(thr->m_prio);
QS_BEGIN_NOCRIT_(QS_MUTEX_LOCK,
static_cast<void *>(0), static_cast<void *>(0))
QS_TIME_(); // timestamp
QS_2U8_(static_cast<uint8_t>(thr->m_startPrio),/*start prio*/
m_ceiling); // ceiling prio
QS_END_NOCRIT_()
}
else { // no threads are waiting for this mutex
m_lockNest = static_cast<uint8_t>(0);
if (m_ceiling != static_cast<uint8_t>(0)) {
// put the mutex at the priority ceiling slot
QF::active_[m_ceiling] = reinterpret_cast<QActive *>(this);
}
}
// schedule the next thread if multitasking started
if (QXK_sched_() != static_cast<uint_fast8_t>(0)) {
QXK_activate_(); // activate a basic thread
}
}
else { // releasing the mutex
--m_lockNest; // release one level
}
QF_CRIT_EXIT_();
}
} // namespace QP

View File

@ -1,67 +0,0 @@
/// @file
/// @brief Internal (package scope) QXK/C++ interface.
/// @ingroup qxk
/// @cond
///***************************************************************************
/// Last updated for version 5.9.7
/// Last updated on 2017-08-19
///
/// Q u a n t u m L e a P s
/// ---------------------------
/// innovating embedded systems
///
/// Copyright (C) Quantum Leaps, LLC. All rights reserved.
///
/// This program is open source software: you can redistribute it and/or
/// modify it under the terms of the GNU General Public License as published
/// by the Free Software Foundation, either version 3 of the License, or
/// (at your option) any later version.
///
/// Alternatively, this program may be distributed and modified under the
/// terms of Quantum Leaps commercial licenses, which expressly supersede
/// the GNU General Public License and are specifically designed for
/// licensees interested in retaining the proprietary status of their code.
///
/// This program is distributed in the hope that it will be useful,
/// but WITHOUT ANY WARRANTY; without even the implied warranty of
/// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
/// GNU General Public License for more details.
///
/// You should have received a copy of the GNU General Public License
/// along with this program. If not, see <http://www.gnu.org/licenses/>.
///
/// Contact information:
/// https://state-machine.com
/// mailto:info@state-machine.com
///***************************************************************************
/// @endcond
#ifndef qxk_pkg_h
#define qxk_pkg_h
namespace QP {
//! timeout signals
enum QXK_Timeouts {
QXK_DELAY_SIG = Q_USER_SIG,
QXK_QUEUE_SIG,
QXK_SEMA_SIG
};
} // namespace QP
//****************************************************************************
extern "C" {
//! initialize the private stack of a given AO
void QXK_stackInit_(void *thr, QP::QXThreadHandler handler,
void *stkSto, uint_fast16_t stkSize);
//! called when a thread function returns
void QXK_threadRet_(void);
} // extern "C"
#include "qf_pkg.h" // QF package-scope interface
#endif // qxk_pkg_h

View File

@ -1,240 +0,0 @@
/// @file
/// @brief QXK/C++ preemptive kernel counting semaphore implementation
/// @ingroup qxk
/// @cond
////**************************************************************************
/// Last updated for version 5.9.9
/// Last updated on 2017-09-29
///
/// Q u a n t u m L e a P s
/// ---------------------------
/// innovating embedded systems
///
/// Copyright (C) Quantum Leaps, LLC. All rights reserved.
///
/// This program is open source software: you can redistribute it and/or
/// modify it under the terms of the GNU General Public License as published
/// by the Free Software Foundation, either version 3 of the License, or
/// (at your option) any later version.
///
/// Alternatively, this program may be distributed and modified under the
/// terms of Quantum Leaps commercial licenses, which expressly supersede
/// the GNU General Public License and are specifically designed for
/// licensees interested in retaining the proprietary status of their code.
///
/// This program is distributed in the hope that it will be useful,
/// but WITHOUT ANY WARRANTY; without even the implied warranty of
/// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
/// GNU General Public License for more details.
///
/// You should have received a copy of the GNU General Public License
/// along with this program. If not, see <http://www.gnu.org/licenses/>.
///
/// Contact information:
/// https://state-machine.com
/// mailto:info@state-machine.com
////**************************************************************************
/// @endcond
#define QP_IMPL // this is QP implementation
#include "qf_port.h" // QF port
#include "qxk_pkg.h" // QXK package-scope internal interface
#include "qassert.h" // QP embedded systems-friendly assertions
#ifdef Q_SPY // QS software tracing enabled?
#include "qs_port.h" // include QS port
#else
#include "qs_dummy.h" // disable the QS software tracing
#endif // Q_SPY
// protection against including this source file in a wrong project
#ifndef qxk_h
#error "Source file included in a project NOT based on the QXK kernel"
#endif // qxk_h
namespace QP {
Q_DEFINE_THIS_MODULE("qxk_sema")
//****************************************************************************
/// @description
/// Initializes a semaphore with the specified count and maximum count.
/// If the semaphore is used for resource sharing, both the initial count
/// and maximum count should be set to the number of identical resources
/// guarded by the semaphore. If the semaphore is used as a signaling
/// mechanism, the initial count should set to 0 and maximum count to 1
/// (binary semaphore).
///
/// @param[in] count initial value of the semaphore counter
/// @param[in] max_count maximum value of the semaphore counter.
/// The purpose of the max_count is to limit the counter
/// so that the semaphore cannot unblock more times than
/// the maximum.
/// @note
/// QXSemaphore::init() must be called **before** the semaphore can be used
/// (signaled or waited on).
///
void QXSemaphore::init(uint_fast16_t const count,
uint_fast16_t const max_count)
{
Q_REQUIRE_ID(100, max_count > static_cast<uint_fast16_t>(0));
m_waitSet.setEmpty();
m_count = static_cast<uint16_t>(count);
m_max_count = static_cast<uint16_t>(max_count);
}
//****************************************************************************
/// @description
/// When an extended thread calls QXSemaphore::wait() and the value of the
/// semaphore counter is greater than 0, QXSemaphore_wait() decrements the
/// semaphore counter and returns (true) to its caller. However, if the value
/// of the semaphore counter is 0, the function places the calling thread in
/// the waiting list for the semaphore. The thread waits until the semaphore
/// is signaled by calling QXSemaphore::signal(), or the specified timeout
/// expires. If the semaphore is signaled before the timeout expires, QXK
/// resumes the highest-priority extended thread waiting for the semaphore.
///
/// @param[in] nTicks number of clock ticks (at the associated rate)
/// to wait for the semaphore. The value of
/// QXTHREAD_NO_TIMEOUT indicates that no timeout will
/// occur and the semaphore will wait indefinitely.
/// @returns
/// true if the semaphore has been signaled, and false if the timeout occured.
///
/// @note
/// Multiple extended threads can wait for a given semahpre.
///
bool QXSemaphore::wait(uint_fast16_t const nTicks) {
QF_CRIT_STAT_
QF_CRIT_ENTRY_();
QXThread *curr = static_cast<QXThread *>(QXK_attr_.curr);
/// @pre this function must:
/// (1) NOT be called from an ISR; (2) be called from an extended thread;
/// (3) the thread must NOT be holding a mutex and
/// (4) the thread must NOT be already blocked on any object.
///
Q_REQUIRE_ID(200, (!QXK_ISR_CONTEXT_()) /* can't block inside an ISR */
&& (curr != static_cast<QXThread *>(0)) /* curr must be extended */
&& (QXK_attr_.lockPrio == static_cast<uint_fast8_t>(0)) /* no lock */
&& (curr->m_temp.obj == static_cast<QMState *>(0))); // not blocked
if (m_count > static_cast<uint16_t>(0)) {
--m_count;
curr->m_timeEvt.sig = static_cast<QSignal>(QXK_SEMA_SIG); // non-zero
}
else {
// remember the blocking object
curr->m_temp.obj = reinterpret_cast<QMState const *>(this);
curr->teArm_(static_cast<enum_t>(QXK_SEMA_SIG), nTicks);
m_waitSet.insert(curr->m_prio);
QXK_attr_.readySet.remove(curr->m_prio);
(void)QXK_sched_();
QF_CRIT_EXIT_();
QF_CRIT_EXIT_NOP(); // BLOCK here
QF_CRIT_ENTRY_();
// the blocking object must be this semaphore
Q_ASSERT_ID(210, curr->m_temp.obj
== reinterpret_cast<QMState const *>(this));
curr->m_temp.obj = static_cast<QMState *>(0); // clear
}
QF_CRIT_EXIT_();
// signal of non-zero means that the time event has not expired
return (curr->m_timeEvt.sig != static_cast<QSignal>(0));
}
//****************************************************************************
///
/// @description
/// This operation checks if the semaphore counter is greater than 0,
/// in which case the counter is decremented.
///
/// @returns
/// 'true' if the semaphore has count available and 'false' NOT available.
///
/// @note
/// This function can be called from any context, including ISRs and basic
/// threds (active objects).
///
bool QXSemaphore::tryWait(void) {
bool isAvailable;
QF_CRIT_STAT_
/// @pre the semaphore must be initialized
Q_REQUIRE_ID(300, (m_max_count > static_cast<uint16_t>(0)));
QF_CRIT_ENTRY_();
// is the semaphore available?
if (m_count > static_cast<uint16_t>(0)) {
--m_count;
isAvailable = true;
}
else { // the semaphore is NOT available (would block)
isAvailable = false;
}
QF_CRIT_EXIT_();
return isAvailable;
}
//****************************************************************************
/// @description
/// If the semaphore counter value is 0 or more, it is incremented, and this
/// function returns to its caller. If the extended threads are waiting for
/// the semaphore to be signaled, QXSemaphore_signal() removes the highest-
/// priority thread waiting for the semaphore from the waiting list and makes
/// this thread ready-to-run. The QXK scheduler is then called to determine
/// if the awakened thread is now the highest-priority thread that is
/// ready-to-run.
///
/// @returns true when the semaphore gets signaled and false when
/// the semaphore count exceeded the maximum.
///
/// @note
/// A semaphore can be signaled from many places, including from ISRs, basic
/// threads (AOs), and extended threads.
///
bool QXSemaphore::signal(void) {
bool signaled = true; // assume that the semaphore will be signaled
QF_CRIT_STAT_
/// @pre the semaphore must be initialized
Q_REQUIRE_ID(400, m_max_count > static_cast<uint16_t>(0));
QF_CRIT_ENTRY_();
if (m_waitSet.notEmpty()) {
uint_fast8_t p = m_waitSet.findMax();
QXK_attr_.readySet.insert(p);
m_waitSet.remove(p);
QXThread *thr = static_cast<QXThread *>(QF::active_[p]);
// the thread must be extended and the semaphore count must be zero
Q_ASSERT_ID(410, (thr != static_cast<QXThread *>(0)) /* registered */
&& (thr->m_osObject != static_cast<void *>(0)) /* extended */
&& (m_count == static_cast<uint16_t>(0))); // not signaled
// disarm the internal time event
(void)thr->teDisarm_();
if (!QXK_ISR_CONTEXT_()) { // not inside ISR?
(void)QXK_sched_();
}
}
else {
if (m_count < m_max_count) {
++m_count;
}
else {
signaled = false; // semaphore NOT signaled
}
}
QF_CRIT_EXIT_();
return signaled;
}
} // namespace QP

View File

@ -1,607 +0,0 @@
/// @file
/// @brief QXK/C++ preemptive kernel extended (blocking) thread implementation
/// @ingroup qxk
/// @cond
///***************************************************************************
/// Last updated for version 5.9.9
/// Last updated on 2017-09-29
///
/// Q u a n t u m L e a P s
/// ---------------------------
/// innovating embedded systems
///
/// Copyright (C) 2005-2017 Quantum Leaps, LLC. All rights reserved.
///
/// This program is open source software: you can redistribute it and/or
/// modify it under the terms of the GNU General Public License as published
/// by the Free Software Foundation, either version 3 of the License, or
/// (at your option) any later version.
///
/// Alternatively, this program may be distributed and modified under the
/// terms of Quantum Leaps commercial licenses, which expressly supersede
/// the GNU General Public License and are specifically designed for
/// licensees interested in retaining the proprietary status of their code.
///
/// This program is distributed in the hope that it will be useful,
/// but WITHOUT ANY WARRANTY; without even the implied warranty of
/// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
/// GNU General Public License for more details.
///
/// You should have received a copy of the GNU General Public License
/// along with this program. If not, see <http://www.gnu.org/licenses/>.
///
/// Contact information:
/// https://state-machine.com
/// mailto:info@state-machine.com
///***************************************************************************
/// @endcond
#define QP_IMPL // this is QP implementation
#include "qf_port.h" // QF port
#include "qxk_pkg.h" // QXK package-scope internal interface
#include "qassert.h" // QP embedded systems-friendly assertions
#ifdef Q_SPY // QS software tracing enabled?
#include "qs_port.h" // include QS port
#else
#include "qs_dummy.h" // disable the QS software tracing
#endif // Q_SPY
// protection against including this source file in a wrong project
#ifndef qxk_h
#error "Source file included in a project NOT based on the QXK kernel"
#endif // qxk_h
namespace QP {
Q_DEFINE_THIS_MODULE("qxk_xthr")
//****************************************************************************
/// @description
/// Performs the first step of QXThread initialization by assigning the
/// thread-handler function and the tick rate at which it will handle
/// the timeouts.
///
/// @param[in] handler the thread-handler function
/// @param[in] tickRate the system clock tick rate to use for timeouts
///
/// @note
/// Must be called only ONCE before QXThread::start().
///
/// @usage
/// The following example illustrates how to invoke the QXThread ctor in the
/// main() function:
/// @include qxk_xctor.cpp
///
QXThread::QXThread(QXThreadHandler const handler, uint_fast8_t const tickRate)
: QActive(Q_STATE_CAST(handler)),
m_timeEvt(this, static_cast<enum_t>(QXK_DELAY_SIG),
static_cast<uint_fast8_t>(tickRate))
{
m_state.act = Q_ACTION_CAST(0); // mark as extended thread
}
//****************************************************************************
// QXThread virtual function implementations...
void QXThread::init(QEvt const * const /*e*/) {
Q_ERROR_ID(110);
}
//****************************************************************************
void QXThread::dispatch(QEvt const * const /*e*/) {
Q_ERROR_ID(120);
}
//****************************************************************************
///
/// @description
/// Starts an extended thread and registers it with the framework.
/// The extended thread becomes ready-to-run immediately and is scheduled
/// if the QXK is already running.
///
/// @param[in] prio priority at which to start the extended thread
/// @param[in] qSto pointer to the storage for the ring buffer of the
/// event queue. This cold be NULL, if this extended
/// thread does not use the built-in event queue.
/// @param[in] qLen length of the event queue [in events],
/// or zero if queue not used
/// @param[in] stkSto pointer to the stack storage (must be provided)
/// @param[in] stkSize stack size [in bytes] (must not be zero)
/// @param[in] ie pointer to the initial event (not used).
///
/// @usage
/// The following example shows starting an extended thread:
/// @include qxk_xstart.cpp
///
void QXThread::start(uint_fast8_t const prio,
QEvt const *qSto[], uint_fast16_t const qLen,
void * const stkSto, uint_fast16_t const stkSize,
QEvt const * const /*ie*/)
{
/// @pre this function must:
/// - NOT be called from an ISR;
/// - the thread priority cannot exceed #QF_MAX_ACTIVE;
/// - the stack storage must be provided;
/// - the thread must be instantiated (see QP::QXThread::QXThread()).
Q_REQUIRE_ID(200, (!QXK_ISR_CONTEXT_()) /* don't start AO's in an ISR! */
&& (prio <= static_cast<uint_fast8_t>(QF_MAX_ACTIVE))
&& (stkSto != static_cast<void *>(0))
&& (stkSize != static_cast<uint_fast16_t>(0))
&& (m_state.act == static_cast<QActionHandler>(0)));
// is storage for the queue buffer provided?
if (qSto != static_cast<QEvt const **>(0)) {
m_eQueue.init(qSto, qLen);
}
// extended threads provide their thread function in place of
// the top-most initial transition 'm_temp.act'
QXK_stackInit_(this, reinterpret_cast<QXThreadHandler>(m_temp.act),
stkSto, stkSize);
m_prio = prio;
m_startPrio = prio;
QF::add_(this); // make QF aware of this extended thread
// the new thread is not blocked on any object
m_temp.obj = static_cast<QMState const *>(0);
QF_CRIT_STAT_
QF_CRIT_ENTRY_();
// extended-thread becomes ready immediately
QXK_attr_.readySet.insert(m_prio);
// see if this thread needs to be scheduled in case QXK is running
(void)QXK_sched_();
QF_CRIT_EXIT_();
}
//****************************************************************************
/// @description
/// Direct event posting is the simplest asynchronous communication method
/// available in QF. The following example illustrates how the Philo active
/// object posts directly the HUNGRY event to the Table active object.@n
/// @n
/// The parameter @p margin specifies the minimum number of free slots in
/// the queue that must be available for posting to succeed. The function
/// returns 1 (success) if the posting succeeded (with the provided margin)
/// and 0 (failure) when the posting fails.
///
/// @param[in] e pointer to the event to be posted
/// @param[in] margin number of required free slots in the queue
/// after posting the event. The special value QP::QF_NO_MARGIN
/// means that this function will assert if posting fails.
///
/// @returns
/// 'true' (success) if the posting succeeded (with the provided margin) and
/// 'false' (failure) when the posting fails.
///
/// @attention
/// Should be called only via the macro POST() or POST_X().
///
/// @note
/// The QP::QF_NO_MARGIN value of the @p margin argument is special and
/// denotes situation when the post() operation is assumed to succeed (event
/// delivery guarantee). An assertion fires, when the event cannot be
/// delivered in this case.
///
#ifndef Q_SPY
bool QXThread::post_(QEvt const * const e, uint_fast16_t const margin)
#else
bool QXThread::post_(QEvt const * const e, uint_fast16_t const margin,
void const * const sender)
#endif
{
bool status;
QF_CRIT_STAT_
// is it the private time event?
if (e == &m_timeEvt) {
QF_CRIT_ENTRY_();
status = true;
// the private time event is disarmed and not in any queue,
// so it is safe to change its signal. The signal of 0 means
// that the time event has expired.
m_timeEvt.sig = static_cast<QSignal>(0);
unblock_();
QF_CRIT_EXIT_();
}
// is the event queue provided?
else if (m_eQueue.m_end != static_cast<QEQueueCtr>(0)) {
/// @pre event pointer must be valid
Q_REQUIRE_ID(300, e != static_cast<QEvt const *>(0));
QF_CRIT_ENTRY_();
QEQueueCtr nFree = m_eQueue.m_nFree; // get volatile into temporary
// margin available?
if (nFree > static_cast<QEQueueCtr>(margin)) {
QS_BEGIN_NOCRIT_(QS_QF_ACTIVE_POST_FIFO,
QS::priv_.locFilter[QS::AO_OBJ], this)
QS_TIME_(); // timestamp
QS_OBJ_(sender); // the sender object
QS_SIG_(e->sig); // the signal of the event
QS_OBJ_(this); // this active object
QS_2U8_(e->poolId_, e->refCtr_); // poolID & refCtr of the evt
QS_EQC_(nFree); // number of free entries
QS_EQC_(m_eQueue.m_nMin); // min number of free entries
QS_END_NOCRIT_()
// is it a pool event?
if (e->poolId_ != static_cast<uint8_t>(0)) {
QF_EVT_REF_CTR_INC_(e); // increment the reference counter
}
--nFree; // one free entry just used up
m_eQueue.m_nFree = nFree; // update the volatile
if (m_eQueue.m_nMin > nFree) {
m_eQueue.m_nMin = nFree; // update minimum so far
}
// is the queue empty?
if (m_eQueue.m_frontEvt == static_cast<QEvt const *>(0)) {
m_eQueue.m_frontEvt = e; // deliver event directly
// is this thread blocked on the queue?
if (m_temp.obj
== reinterpret_cast<QMState const *>(&m_eQueue))
{
(void)teDisarm_();
QXK_attr_.readySet.insert(m_prio);
if (!QXK_ISR_CONTEXT_()) {
(void)QXK_sched_();
}
}
}
// queue is not empty, insert event into the ring-buffer
else {
// insert event into the ring buffer (FIFO)
QF_PTR_AT_(m_eQueue.m_ring, m_eQueue.m_head) = e;
// need to wrap head?
if (m_eQueue.m_head == static_cast<QEQueueCtr>(0)) {
m_eQueue.m_head = m_eQueue.m_end; // wrap around
}
--m_eQueue.m_head;
}
QF_CRIT_EXIT_();
status = true; // event posted successfully
}
else {
/// @note assert if event cannot be posted and dropping events is
/// not acceptable
Q_ASSERT_ID(310, margin != static_cast<uint_fast16_t>(0));
QS_BEGIN_NOCRIT_(QS_QF_ACTIVE_POST_ATTEMPT,
QS::priv_.locFilter[QS::AO_OBJ], this)
QS_TIME_(); // timestamp
QS_OBJ_(sender); // the sender object
QS_SIG_(e->sig); // the signal of the event
QS_OBJ_(this); // this active object
QS_2U8_(e->poolId_, e->refCtr_); // poolID & refCtr of the evt
QS_EQC_(nFree); // number of free entries
QS_EQC_(static_cast<QEQueueCtr>(margin)); // margin requested
QS_END_NOCRIT_()
QF_CRIT_EXIT_();
QF::gc(e); // recycle the evnet to avoid a leak
status = false; // event not posted
}
}
else { // the queue is not available
QF::gc(e); // make sure the event is not leaked
status = false;
Q_ERROR_ID(320);
}
return status;
}
//****************************************************************************
/// @description
/// Last-In-First-Out (LIFO) policy is not supported for extened threads.
///
/// @param[in e pointer to the event to post to the queue
///
/// @sa
/// QActive::postLIFO_()
///
void QXThread::postLIFO(QEvt const * const /*e*/) {
Q_ERROR_ID(410);
}
//****************************************************************************
/// @description
/// The QXThread::queueGet() operation allows the calling extended thread to
/// receive QP events directly into its own built-in event queue from an ISR,
/// basic thread (AO), or another extended thread.
///
/// If QXThread::queueGet() is called when no events are present in the
/// thread's private event queue, the operation blocks the current extended
/// thread until either an event is received, or a user-specified timeout
/// expires.
///
/// @param[in] nTicks number of clock ticks (at the associated rate)
/// to wait for the event to arrive. The value of
/// QXTHREAD_NO_TIMEOUT indicates that no timeout will
/// occur and the queue will block indefinitely.
/// @returns
/// A pointer to the event. If the pointer is not NULL, the event
/// was delivered. Otherwise the event pointer of NULL indicates that the
/// queue has timed out.
///
QEvt const *QXThread::queueGet(uint_fast16_t const nTicks) {
QEQueueCtr nFree;
QEvt const *e;
QF_CRIT_STAT_
QF_CRIT_ENTRY_();
QXThread *thr = static_cast<QXThread *>(QXK_attr_.curr);
/// @pre this function must:
/// - NOT be called from an ISR;
/// - be called from an extended thread;
/// - the thread must NOT be holding a scheduler lock;
/// - the thread must NOT be already blocked on any object.
Q_REQUIRE_ID(500, (!QXK_ISR_CONTEXT_()) /* can't block inside an ISR */
&& (thr != static_cast<QXThread *>(0)) /* current must be extended */
&& (QXK_attr_.lockHolder != thr->m_prio) /* not holding a lock */
&& (thr->m_temp.obj == static_cast<QMState const *>(0))); // !blocked
// is the queue empty? -- block and wait for event(s)
if (thr->m_eQueue.m_frontEvt == static_cast<QEvt *>(0)) {
// remember the blocking object (the thread's queue)
thr->m_temp.obj = reinterpret_cast<QMState const *>(&thr->m_eQueue);
thr->teArm_(static_cast<enum_t>(QXK_QUEUE_SIG), nTicks);
QXK_attr_.readySet.remove(thr->m_prio);
(void)QXK_sched_();
QF_CRIT_EXIT_();
QF_CRIT_EXIT_NOP(); // BLOCK here
QF_CRIT_ENTRY_();
// the blocking object must be this queue
Q_ASSERT_ID(510, thr->m_temp.obj ==
reinterpret_cast<QMState const *>(&thr->m_eQueue));
thr->m_temp.obj = static_cast<QMState const *>(0); // clear
}
// is the queue not empty?
if (thr->m_eQueue.m_frontEvt != static_cast<QEvt *>(0)) {
e = thr->m_eQueue.m_frontEvt; // always remove from the front
// volatile into tmp
nFree= thr->m_eQueue.m_nFree + static_cast<QEQueueCtr>(1);
thr->m_eQueue.m_nFree = nFree; // update the number of free
// any events in the ring buffer?
if (nFree <= thr->m_eQueue.m_end) {
// remove event from the tail
thr->m_eQueue.m_frontEvt =
QF_PTR_AT_(thr->m_eQueue.m_ring, thr->m_eQueue.m_tail);
if (thr->m_eQueue.m_tail == static_cast<QEQueueCtr>(0)) {
thr->m_eQueue.m_tail = thr->m_eQueue.m_end; // wrap
}
--thr->m_eQueue.m_tail;
QS_BEGIN_NOCRIT_(QS_QF_ACTIVE_GET,
QS::priv_.locFilter[QS::AO_OBJ], thr)
QS_TIME_(); // timestamp
QS_SIG_(e->sig); // the signal of this event
QS_OBJ_(&thr); // this active object
QS_2U8_(e->poolId_, e->refCtr_); // poolID & ref Count
QS_EQC_(nFree); // number of free entries
QS_END_NOCRIT_()
}
else {
// the queue becomes empty
thr->m_eQueue.m_frontEvt = static_cast<QEvt const *>(0);
// all entries in the queue must be free (+1 for fronEvt)
Q_ASSERT_ID(520, nFree == (thr->m_eQueue.m_end
+ static_cast<QEQueueCtr>(1)));
QS_BEGIN_NOCRIT_(QS_QF_ACTIVE_GET_LAST,
QS::priv_.locFilter[QS::AO_OBJ], thr)
QS_TIME_(); // timestamp
QS_SIG_(e->sig); // the signal of this event
QS_OBJ_(&thr); // this active object
QS_2U8_(e->poolId_, e->refCtr_); // poolID & ref Count
QS_END_NOCRIT_()
}
}
else { // the queue is still empty -- the timeout must have fired
e = static_cast<QEvt const *>(0);
}
QF_CRIT_EXIT_();
return e;
}
//****************************************************************************
/// @description
/// Intenral implementation of blocking the given extended thread.
///
/// @note
/// Must be called from within a critical section
///
void QXThread::block_(void) const {
/// @pre the thread holding the lock cannot block!
Q_REQUIRE_ID(600, (QXK_attr_.lockHolder != m_prio));
QXK_attr_.readySet.remove(m_prio);
(void)QXK_sched_();
}
//****************************************************************************
/// @description
/// Intenral implementation of un-blocking the given extended thread.
///
/// @note
/// must be called from within a critical section
///
void QXThread::unblock_(void) const {
QXK_attr_.readySet.insert(m_prio);
if ((!QXK_ISR_CONTEXT_()) // not inside ISR?
&& (QF::active_[0] != static_cast<QActive *>(0))) // kernel started?
{
(void)QXK_sched_();
}
}
//****************************************************************************
/// @description
/// Intenral implementation of arming the private time event for
/// a given timeout at a given system tick rate.
///
/// @note
/// Must be called from within a critical section
///
void QXThread::teArm_(enum_t const sig, uint_fast16_t const nTicks) {
/// @pre the time event must be unused
Q_REQUIRE_ID(700, m_timeEvt.m_ctr == static_cast<QTimeEvtCtr>(0));
m_timeEvt.sig = static_cast<QSignal>(sig);
if (nTicks != QXTHREAD_NO_TIMEOUT) {
m_timeEvt.m_ctr = static_cast<QTimeEvtCtr>(nTicks);
m_timeEvt.m_interval = static_cast<QTimeEvtCtr>(0);
// is the time event unlinked?
// NOTE: For the duration of a single clock tick of the specified tick
// rate a time event can be disarmed and yet still linked in the list,
// because un-linking is performed exclusively in QF::tickX().
if ((m_timeEvt.refCtr_ & static_cast<uint8_t>(0x80))
== static_cast<uint8_t>(0))
{
uint_fast8_t tickRate =
static_cast<uint_fast8_t>(m_timeEvt.refCtr_);
m_timeEvt.refCtr_ |= static_cast<uint8_t>(0x80); // mark as linked
// The time event is initially inserted into the separate
// "freshly armed" list based on QF::timeEvtHead_[tickRate].act.
// Only later, inside QF::tickX() function, the "freshly armed"
// list is appended to the main list of armed time events based on
// QF_timeEvtHead_[tickRate].next. Again, this is to keep any
// changes to the main list exclusively inside QF::tickX().
m_timeEvt.m_next =
static_cast<QTimeEvt *>(QF::timeEvtHead_[tickRate].m_act);
QF::timeEvtHead_[tickRate].m_act = &m_timeEvt;
}
}
}
//****************************************************************************
/// @description
/// Intenral implementation of disarming the private time event.
///
/// @note
/// Must be called from within a critical section
///
bool QXThread::teDisarm_(void) {
bool wasArmed;
// is the time evt running?
if (m_timeEvt.m_ctr != static_cast<QTimeEvtCtr>(0)) {
wasArmed = true;
// schedule removal from list
m_timeEvt.m_ctr = static_cast<QTimeEvtCtr>(0);
}
// the time event was already automatically disarmed
else {
wasArmed = false;
}
return wasArmed;
}
//****************************************************************************
//! delay (timed blocking of) the current extended thread
bool QXThread::delay(uint_fast16_t const nTicks) {
QF_CRIT_STAT_
QF_CRIT_ENTRY_();
QXThread *thr = static_cast<QXThread *>(QXK_attr_.curr);
/// @pre this function must:
/// - NOT be called from an ISR;
/// - be called from an extended thread;
/// - the thread must NOT be holding a scheduler lock and;
/// - the thread must NOT be already blocked on any object.
Q_REQUIRE_ID(800, (!QXK_ISR_CONTEXT_()) /* can't block inside an ISR */
&& (thr != static_cast<QXThread *>(0)) /* current must be extended */
&& (QXK_attr_.lockHolder != thr->m_prio) /* not holding a lock */
&& (thr->m_temp.obj == static_cast<QMState const *>(0))); // !blocked
// remember the blocking object
thr->m_temp.obj = reinterpret_cast<QMState const *>(&thr->m_timeEvt);
thr->teArm_(static_cast<enum_t>(QXK_DELAY_SIG), nTicks);
thr->block_();
QF_CRIT_EXIT_();
QF_CRIT_EXIT_NOP(); // BLOCK here
QF_CRIT_ENTRY_();
// the blocking object must be the time event
Q_ENSURE_ID(890, thr->m_temp.obj ==
reinterpret_cast<QMState const *>(&thr->m_timeEvt));
thr->m_temp.obj = static_cast<QMState const *>(0); // clear
QF_CRIT_EXIT_();
// signal of zero means that the time event was posted without
// being canceled.
return (thr->m_timeEvt.sig == static_cast<QSignal>(0));
}
//****************************************************************************
//! cancel the delay
bool QXThread::delayCancel(void) {
bool wasArmed;
QF_CRIT_STAT_
QF_CRIT_ENTRY_();
if (m_temp.obj == reinterpret_cast<QMState const *>(&m_timeEvt)) {
wasArmed = teDisarm_();
unblock_();
}
else {
wasArmed = false;
}
QF_CRIT_EXIT_();
return wasArmed;
}
} // namespace QP
//****************************************************************************
extern "C" {
/// @description
/// Called when the extended-thread handler function returns.
///
/// @note
/// Most thread handler functions are structured as endless loops that never
/// return. But it is also possible to structure threads as one-shot functions
/// that perform their job and return. In that case this function peforms
/// cleanup after the thread.
///
void QXK_threadRet_(void) {
uint_fast8_t p;
QF_CRIT_STAT_
QF_CRIT_ENTRY_();
p = QXK_attr_.curr->m_prio;
// remove this thread from the QF
QP::QF::active_[p] = static_cast<QP::QActive *>(0);
QXK_attr_.readySet.remove(p);
(void)QXK_sched_();
QF_CRIT_EXIT_();
}
} // extern "C"

View File

@ -4,8 +4,8 @@
/// @ingroup qxk
/// @cond
///***************************************************************************
/// Last updated for version 5.9.7
/// Last updated on 2017-08-20
/// Last updated for version 6.0.0
/// Last updated on 2017-10-12
///
/// Q u a n t u m L e a P s
/// ---------------------------
@ -416,7 +416,10 @@ uint_fast8_t QXK_sched_(void) {
QXK_CONTEXT_SWITCH_();
}
else {
QXK_attr_.next = static_cast<QP::QActive *>(0);
// the current QXK thread must be the same as 'next'
Q_ASSERT_ID(620, QXK_attr_.curr == next);
QXK_attr_.next = next;
p = static_cast<uint_fast8_t>(0); // no activation needed
}
}

View File

@ -1,2 +0,0 @@
QP/C++ 5.9.9
2017-09-29

2
version-6.0.0 Normal file
View File

@ -0,0 +1,2 @@
QP/C++ 6.0.0
2017-10-13