qpcpp/doxygen/ports_rtos.dox
2022-10-26 19:47:39 -04:00

325 lines
16 KiB
Plaintext

namespace QP {
/*! @page ports_rtos Ports to Third-Party RTOS
<p>The most important reason why you might consider using a traditional RTOS kernel for executing event-driven QP/C++ applications is compatibility with the existing software. For example, most communication stacks (TCP/IP, USB, CAN, etc.) are designed for a traditional **blocking** kernel. In addition, a lot of legacy code requires blocking mechanisms, such as semaphores or time-delays. A conventional RTOS allows you to run the existing software components as regular "blocking" tasks in parallel to the event-driven QP/C++ application.
</p>
Another reason you might be interested in running QP/C++ on top of a conventional RTOS is **safety certification**, which your RTOS kernel might have but the built-in QP kernels currently don't provide.
@note
You do **not** need to use a traditional RTOS just to achieve preemptive multitasking with QP. The @ref comp_qk "preemptive QK kernel", available as part of the QP package, supports preemptive priority-based multitasking and is fully compatible with Rate Monotonic Scheduling to achieve guaranteed, hard real-time performance. The preemptive, run-to-completion QK kernel perfectly matches the run-to-completion execution semantics of active objects, yet it is simpler, faster, and more efficient than any traditional blocking kernel.
@attention
QP/C++ 6.x includes a small, preemptive, priority-based, @ref srs_qxk "dual-mode blocking QXK kernel" that executes active objects like the QK kernel (@ref srs_qxk_basic "basic threads"), but can also execute traditional blocking threads (@ref srs_qxk_extended "extended threads"). In this respect, QXK behaves exactly like a conventional RTOS. The QXK kernel is recommended as the preferred RTOS kernel for applications that need to mix active objects with traditional blocking code. Due to the tight and optimal integration between QXK and the rest of QP, QXK offers better performance and smaller memory footprint than any @ref ports_rtos "QP port to a 3rd-party RTOS". Additionally, QXK is already included in QP, so you avoid additional licensing costs of 3rd-party kernels.
The QP/C++ framework can work with virtually any traditional real-time operating
system (RTOS). The currently supported 3rd-party RTOS kernels are:
- @subpage embos
- @subpage freertos
- @subpage threadx
- @subpage uc-os2
- @subpage zephyr
- <a href="https://www.erika-enterprise.com" target="_blank" class="extern">OSEK/VDX RTOS ERIKA Enterprise</a>
Combined with a conventional RTOS, QP/C++ takes full advantage of the multitasking capabilities of the RTOS by executing each active object in a separate RTOS task. The QP/C++ Platform Abstraction Layer (PAL) includes an abstract RTOS interface to enable integration between QP/C++ and the underlying RTOS. Specifically, the PAL allows adapting most message queue variants as event queues of active objects as well as most memory partitions as QP/C++ event pools.
@next{embos}
*/
/*##########################################################################*/
/*! @page embos embOS
![SEGGER embOS](logo_embos.png)
The QP/C/C++ ports and examples for embOS are described in the Quantum Leaps Application Note @webref{doc/AN_RTOS-embOS.pdf, QP and embOS}.
[![Application Note: QP and embOS](img/AN.jpg)](https://www.state-machine.com/doc/AN_RTOS-embOS.pdf)
@caption{[Application Note: "QP and embOS"](https://www.state-machine.com/doc/AN_RTOS-embOS.pdf)}
@next{freertos}
*/
/*##########################################################################*/
/*! @page freertos FreeRTOS
![FreeRTOS](logo_freertos.png)
@section freertos_about About the QP Port to FreeRTOS
The <span class="img folder">ports/freertos/</span> directory contains a generic platform-independent QP/C++ port to <a href="https://freertos.org" target="_blank" class="extern">FreeRTOS kernel</a> (version 10). The provided QP port to FreeRTOS has been designed *generically* to rely exclusively on the existing FreeRTOS API. This means that the port should run without changes on any CPU/compiler platform supported by FreeRTOS.
The QP-FreeRTOS port works as follows:
- The QP port uses the [static memory allocation of FreeRTOS](https://freertos.org/Static_Vs_Dynamic_Memory_Allocation.html). This requires the FreeRTOS configuration to define the [configSUPPORT_STATIC_ALLOCATION](https://freertos.org/a00110.html#configSUPPORT_STATIC_ALLOCATION)
- Each QP active object executes in a separate FreeRTOS task (`StaticTask_t`) and requires a private stack space.
- The task-level critical section used in QF and QS is based on the FreeRTOS APIs `taskENTER_CRITICAL()`/`taskEXIT_CRITICAL()`.
- The ISR-level critical section used in QF and QS is based on the FreeRTOS APIs `taskENTER_CRITICAL_FROM_ISR()`/`taskEXIT_CRITICAL_FROM_ISR()`.
- The QP port to FreeRTOS provides new "FromISR" APIs, which must be used in the ISRs (but cannot be used at the task-level)
@attention
The design of FreeRTOS requires the use of special "FromISR" API inside ISRs, which imposes the requirement to also provide the "FromISR" variants of the QP APIs, such as `QACTIVE_POST_FROM_ISR()`, `QF_PUBLISH_FROM_ISR()`, etc. These "FromISR" QP APIs must be used inside ISRs instead of the task-level APIs (`QACTIVE_POST()`, `QF_PUBLISH()`, etc.) and conversely, they cannot be used inside tasks and active objects. Unfortunately, FreeRTOS provides no generic way to enforce the proper API use via assertions.
- The QP port uses the FreeRTOS message queue (`StaticQueue_t`) for active object event queues.
- The QP port uses the native QF memory pool (::QMPool) to implement event pools.
- The QP port does not mandate any specific method to manage the QP time events, but the provided examples use the FreeRTOS "hook" `vApplicationTickHook()` to periodically invoke the QP clock tick QF_TICK_X_FROM_ISR(). (NOTE: the `vApplicationTickHook()` executes in the ISR context and therefore mandates the use of the "FromISR" APIs).
@section freertos_examples Example Code
The QP port to FreeRTOS comes with examples located in the directory `qpcpp/examples/freertos`. Currently, the examples are provided for the following boards and development toolchains:
- EK-TM4C123GXL (ARM Cortex-M4F), ARM-KEIL, GNU-ARM, IAR-ARM
- STM32F746G-Discovery (ARM Cortex-M7), ARM-KEIL, GNU-ARM, IAR-ARM
@subsection freertos_isr Writing ISRs for QP/FreeRTOS
The provided examples show how to write regular "kernel-aware" ISRs as well as "kernel-unaware" ISRs for QP/FreeRTOS. (See also the FreeRTOS documentation for [configMAX_SYSCALL_INTERRUPT_PRIORITY](https://www.freertos.org/a00110.html#kernel_priority).
Here is an example of a regular "kernel-aware" ISR (note the use of the `FromISR` suffix in the QP APIs):
@code{.cpp}
// NOTE: only the "FromISR" API variants are allowed in the ISRs!
void GPIOPortA_IRQHandler(void) {
BaseType_t xHigherPriorityTaskWoken = pdFALSE;
// for testing...
AO_Table->POST_FROM_ISR(
Q_NEW_FROM_ISR(QP::QEvt, DPP::MAX_PUB_SIG),
&xHigherPriorityTaskWoken,
&l_GPIOPortA_IRQHandler);
// the usual end of FreeRTOS ISR...
portEND_SWITCHING_ISR(xHigherPriorityTaskWoken);
}
@endcode
Here is an example of a "kernel-unaware" ISR (See also the FreeRTOS documentation for [configMAX_SYSCALL_INTERRUPT_PRIORITY](https://www.freertos.org/a00110.html#kernel_priority):
@code{.cpp}
// ISR for receiving bytes from the QSPY Back-End
// NOTE: This ISR is "kernel-unaware" meaning that it does not interact with
// the FreeRTOS or QP and is not disabled. Such ISRs don't need to call
// portEND_SWITCHING_ISR(() at the end, but they also cannot call any
// FreeRTOS or QP APIs.
//
void UART0_IRQHandler(void); // prototype
void UART0_IRQHandler(void) {
uint32_t status = UART0->RIS; // get the raw interrupt status
UART0->ICR = status; // clear the asserted interrupts
while ((UART0->FR & UART_FR_RXFE) == 0) { // while RX FIFO NOT empty
uint32_t b = UART0->DR;
QP::QS::rxPut(b);
}
}
@endcode
@subsection freertos_hook Writing FreeRTOS Hooks Running in ISR Context
FreeRTOS provides "hooks" that are user functions that execute in the ISR context (e.g., `vApplicationTickHook()`). Such ISR-level functions are closely related to ISRs and should also use exclusively only the "FromISR" APIs. Here is an example of the `vApplicationTickHook()`:
@code{.cpp}
// NOTE: only the "FromISR" API variants are allowed in vApplicationTickHook
void vApplicationTickHook(void) {
BaseType_t xHigherPriorityTaskWoken = pdFALSE;
// process time events for rate 0
QP::QTimeEvt::TICK_X_FROM_ISR(0U, &xHigherPriorityTaskWoken, &l_TickHook);
. . .
// notify FreeRTOS to perform context switch from ISR, if needed
portEND_SWITCHING_ISR(xHigherPriorityTaskWoken);
}
@endcode
@subsection freertos_ao Starting Active Objects in QP/FreeRTOS
As mentioned in the @ref freertos_about "FreeRTOS port summary", the QP port to FreeRTOS uses the [static memory allocation of FreeRTOS](https://freertos.org/Static_Vs_Dynamic_Memory_Allocation.html). This means that all memory for an active object, including the private queue buffer and the private **stack** for the the associated FreeRTOS task must be allocated by the user. Here is an example code that starts an active object:
@code{.cpp}
int main() {
. . .
static QEvt const *tableQueueSto[N_PHILO];
static StackType_t tableStack[configMINIMAL_STACK_SIZE];
. . .
DPP::AO_Table->setAttr(QP::TASK_NAME_ATTR, "Table");
DPP::AO_Table->start(
static_cast<uint_fast8_t>(N_PHILO + 1), // QP priority of the AO
tableQueueSto, // event queue storage
Q_DIM(tableQueueSto), // queue length [events]
tableStack, // stack storage
sizeof(tableStack)); // stack size [bytes]
. . .
return QP::QF::run(); // run the QF application
}
@endcode
@next{threadx}
*/
/*##########################################################################*/
/*! @page threadx ThreadX
![ThreadX](logo_threadx.jpg)
The QP/C/C++ ports and examples for ThreadX (now Azure RTOS) are described in the Quantum Leaps Application Note @webref{doc/AN_RTOS-ThreadX.pdf, QP and ThreadX}.
[![Application Note: QP and ThreadX](img/AN.jpg)](https://www.state-machine.com/doc/AN_RTOS-ThreadX.pdf)
@caption{[Application Note: "QP and ThreadX"](https://www.state-machine.com/doc/AN_RTOS-ThreadX.pdf)}
@next{uc-os2}
*/
/*##########################################################################*/
/*! @page uc-os2 uC-OS2
![uC-OS2](logo_uc-os2.jpg)
@section uc-os2_about About the QP Port to uC-OS2
This directory contains a generic platform-independent QP/C port to uC-OS2 V2.92.
Typically, you should not need to change the files in this directory to adapt the QP-uC-OS2 port on any CPU/Compiler to which uC-OS2 has been ported, because all the CPU and compiler specifics are handled by the uC-OS2 RTOS.
@section uc-os2_source uC-OS2 Source and ARM Cortex-M3/M4 Ports
The uC-OS2 V2.92 source code and ports are located in `3rd_party/uc-os2`. Please make sure to read about uC-OS2 licensing in the README file found in this directory.
@note
The official Micrium ARM-Cortex-M ports have been modified by Quantum Leaps to remove the dependencies on the Micrium's uC-CPU and uC-LIB components, and instead to inter-operate with the Cortex Microcontroller Software Interface Standard (CMSIS).
@section uc-os2_using Using this QP Port to uC-OS2
The example projects for this port are located in @ref exa_uc-os2 "examples/uc-os2/arm-cm/dpp_ek-tm4c123gxl".
Currently, ARM-KEIL and IAR-ARM toolsets are supported (in the <span class="img folder">arm</span> and <span class="img folder">iar</span> sub-directories within this example project).
The example projects use this port by directly including the QP source code (and this port) in the application projects. There is no QP library to build.
However, this port can also be used as a library, in which case you need to build the QP library yourself and include in your project.
@subsection uc-os2_build QP Source Files Needed in this QP Port
Whether you use this QP port as source files or as a library, it is important to note that not all QP source files should be included. Here is the list of QP source files needed:
@verbatim
qpcpp/
+-src/
| | +-qf/
| | | +-qep_hsm.cpp
| | | +-qep_msm.cpp
| | | +-qf_act.cpp
| | | +-qf_actq.cpp - NOT included (implemented in uC-OS2)
| | | +-qf_defer.cpp
| | | +-qf_dyn.cpp
| | | +-qf_mem.cpp - NOT included (implemented in uC-OS2)
| | | +-qf_ps.cpp
| | | +-qf_qeq.cpp
| | | +-qf_qmact.cpp
| | | +-qf_time.cpp
| |
| | +-qs/
| | | +-qs.cpp - included only in the Spy build configuration
| | | +-qs_fp.cpp - included only in the Spy build configuration
|
+-ports
| +-uc-os2
| | +-qf_port.cpp
|
@endverbatim
@note
Specifically, the QP source files qf_actq.cpp and qf_mem.cpp must **NOT** be included in the build, because this functionality is taken from uC-OS2.
The QP/C/C++ ports and examples for uC-OS2 are described in the Quantum Leaps Application Note @webref{doc/AN_RTOS-uCOS2.pdf, QP and uC-OS2}.
[![QP and uC-OS2](img/AN.jpg)](https://www.state-machine.com/doc/AN_RTOS-uCOS2.pdf)
@caption{[Application Note: QP and uC-OS2](https://www.state-machine.com/doc/AN_RTOS-uCOS2.pdf)}
@next{zephyr}
*/
/*##########################################################################*/
/*! @page zephyr Zephyr
![Zephyr Project](logo_zephyr.jpg)
@section zephyr_about About the QP Port to Zephyr
This directory contains a generic platform-independent QP/C++ port to the [Zephyr RTOS](https://zephyrproject.org).
Typically, you should not need to change the files in this directory to adapt the QP-Zephyr port on any CPU/Compiler to which Zephyr has been ported, because all the CPU and compiler specifics are handled by the Zephyr RTOS.
The QP-Zephyr port works as follows:
- The critical section used in this port is based on `k_spin_lock()/k_spin_unlock()` Zephyr API. This is the modern Zephyr critical section API, which is ready for SMP (symmetric multiprocessing).
- Each QP active object executes in a separate Zephyr thread (`struct k_thread`) and requires a private stack space.
@note
As demonstrated in the @ref exa_zephyr "provided examples", the private stacks for active objects in this port must be allocated with the Zephyr `K_THREAD_STACK_DEFINE()` macro. Also, the stack size passed to QACTIVE_START() must be calculated with the Zephyr `K_THREAD_STACK_SIZEOF()` macro. Failure to use these macros can lead to memory corruption in the application.
- The active object event queue is implemented with the Zephyr message queue (`struct k_msgq`).
@note
The Zephyr message queue currently supports only the FIFO policy and does NOT support the LIFO policy. For that reason, the QActive_postLIFO_() implementation in this port uses the FIFO policy. A feature request has been filed in the Zephyr project for adding the LIFO policy, so perhaps this can be improved, if the feature is added.
- The QP port uses Zephyr scheduler locking (`k_sched_lock()/k_sched_unlock()`), which locks all threads indiscriminately. Currently Zephyr does not provide a selective scheduler locking.
- The QP port uses the native QF memory pool (::QMPool) to implement event pools.
- The QP port does not mandate any specific method to manage the QP time events, but the provided examples use the Zephyr timer (`struct k_timer`) to tick periodically and invoke QP::QTimeEvt::TICK_X().
@subsection zephyr_build QP Source Files Needed in this QP Port
It is important to note that **NOT** all QP source files should be included. Here is the list of QP source files needed:
@verbatim
qpcpp/
+-src/
| | +-qf/
| | | +-qep_hsm.cpp
| | | +-qep_msm.cpp
| | | +-qf_act.cpp
| | | +-qf_actq.cpp - NOT included (implemented in Zephyr)
| | | +-qf_defer.cpp
| | | +-qf_dyn.cpp
| | | +-qf_mem.cpp
| | | +-qf_ps.cpp
| | | +-qf_qeq.cpp
| | | +-qf_qmact.cpp
| | | +-qf_time.cpp
| |
| | +-qs/
| | | +-qs.cpp - included only in the Spy build configuration
| | | +-qs_fp.cpp - included only in the Spy build configuration
|
+-ports
| +-zephyr
| | +-qep_port.hpp
| | +-qf_port.hpp
| | +-qf_port.cpp - implementation of the Zephyr port
| | +-qs_port.hpp
|
@endverbatim
@note
Specifically, the QP source files qf_actq.cpp must **NOT** be included in the build, because this functionality is taken from Zephyr.
@section zephyr_exa Examples for the Zephyr port
The example projects for this port are located in @ref exa_zephyr "examples/zephyr".
@next{ports_os}
*/
} // namespace QP