mirror of
https://github.com/QuantumLeaps/qpcpp.git
synced 2025-01-28 06:02:56 +08:00
6.0.0
This commit is contained in:
parent
2d8b482434
commit
ce37754fbc
121
3rd_party/CMSIS/Include/arm_common_tables.h
vendored
121
3rd_party/CMSIS/Include/arm_common_tables.h
vendored
@ -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 */
|
66
3rd_party/CMSIS/Include/arm_const_structs.h
vendored
66
3rd_party/CMSIS/Include/arm_const_structs.h
vendored
@ -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
|
7257
3rd_party/CMSIS/Include/arm_math.h
vendored
7257
3rd_party/CMSIS/Include/arm_math.h
vendored
File diff suppressed because it is too large
Load Diff
2
3rd_party/CMSIS/README.txt
vendored
2
3rd_party/CMSIS/README.txt
vendored
@ -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.
|
||||
|
||||
|
@ -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 \
|
||||
|
@ -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 \
|
||||
|
@ -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.
|
||||
|
||||
|
@ -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"
|
||||
|
6372
doxygen/metrics.dox
6372
doxygen/metrics.dox
File diff suppressed because it is too large
Load Diff
@ -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 # ......................................................................
|
||||
|
||||
|
@ -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 # ......................................................................
|
||||
|
||||
|
@ -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
|
||||
//--------------------------------------------------------------------------*/
|
||||
|
||||
|
@ -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
|
||||
|
@ -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>
|
@ -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.
|
||||
|
@ -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.
|
||||
|
@ -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"/>
|
||||
|
@ -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>
|
@ -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.
|
||||
|
@ -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.
|
||||
|
@ -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>
|
||||
|
@ -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>
|
@ -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.
|
||||
|
@ -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.
|
||||
|
@ -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>
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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...................
|
||||
|
@ -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
|
||||
|
@ -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................*/
|
||||
|
@ -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...................
|
||||
|
@ -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...................
|
||||
|
@ -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.
|
@ -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
|
@ -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
|
@ -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
|
@ -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
|
@ -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
|
@ -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
|
@ -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
|
199
source/qf_pkg.h
199
source/qf_pkg.h
@ -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
|
306
source/qf_ps.cpp
306
source/qf_ps.cpp
@ -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
|
@ -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
|
@ -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
|
@ -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
|
@ -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
|
466
source/qk.cpp
466
source/qk.cpp
@ -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"
|
@ -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
|
918
source/qs.cpp
918
source/qs.cpp
@ -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
|
@ -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)
|
129
source/qs_fp.cpp
129
source/qs_fp.cpp
@ -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
|
@ -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
|
1217
source/qs_rx.cpp
1217
source/qs_rx.cpp
File diff suppressed because it is too large
Load Diff
@ -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
|
259
source/qv.cpp
259
source/qv.cpp
@ -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
|
567
source/qxk.cpp
567
source/qxk.cpp
@ -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"
|
@ -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
|
@ -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
|
@ -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
|
@ -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"
|
@ -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
|
||||
}
|
||||
}
|
||||
|
@ -1,2 +0,0 @@
|
||||
QP/C++ 5.9.9
|
||||
2017-09-29
|
2
version-6.0.0
Normal file
2
version-6.0.0
Normal file
@ -0,0 +1,2 @@
|
||||
QP/C++ 6.0.0
|
||||
2017-10-13
|
Loading…
x
Reference in New Issue
Block a user