8. Using the Built-in Real-Time Kernels and Third-Party RTOSes

This QP/C++ Tutorial is adapted from Chapter 1 of Practical UML Statecharts in C/C++, Second Edition
by Miro Samek, the founder and president of Quantum Leaps, LLC.

qp_tutorial.jpg

Prev: 7. Coding Hierarchical State Machines
Next: 9. Using Software Tracing for Testing and Debugging

As you saw in Listing 3-1(18), the main() function eventually gives control to the event-driven framework by calling QF::run() to execute the application. In this section, I briefly explain how QF allocates the CPU cycles to various tasks within the system and what options you have in choosing the execution model.

8.1 Simple Non-Preemptive "Vanilla" Kernel

In the simplest configuration, the "Fly 'n' Shoot" game executes under the simple cooperative "vanilla" kernel, which is provided in the QP. The "vanilla" kernel operates by constantly polling all event queues of active objects in an endless loop. The kernel always selects the highest-priority active object ready to run, which is the highest-priority active object with a non-empty event queue.

Note:
The "vanilla" kernel is so simple that many commercial real-time frameworks don't even call it a kernel . Instead, this configuration is simply referred to as "without an RTOS". However, if you want to understand what it means to execute active objects "without an RTOS" and what execution profile you can expect in this case, you need to realize that a simple cooperative vanilla kernel is indeed involved.

The interrupt service routines (ISRs) can preempt the execution of active objects at any time, but due to the simplistic nature of the "vanilla" kernel, every ISR returns to exactly the preemption point. If the ISR posts or publishes an event to any active object, the processing of this event won't start until the current RTC step completes. The maximum time an event for the highest-priority active object can be delayed this way is called the task-level response. With the non-preemptive "vanilla" kernel, the task-level response is equal to the longest RTC step of all active objects in the system. Please note that the task-level response of the "vanilla" kernel is still a lot better than the traditional "superloop" (a.k.a., main+ISRs) architecture. I'll have more to say about this in the upcoming Section 10. Comparison to the Traditional Approach, where I compare the event-driven "Fly 'n' Shoot" example to the traditionally structured "Quickstart" application.

The task-level response of the simple "vanilla" kernel turns out to be adequate for surprisingly many applications, because state machines by nature handle events quickly without a need to busy-wait for events. (A state machine simply runs-to-completion and becomes dormant until another event arrives). Please also note that often you can make the task-level response as fast as you need by breaking up longer RTC steps into shorter ones (e.g., by using the "Reminder" state pattern described in Chapter 5 of Practical UML Statecharts in C/C++, Second Edition.

8.2 The QK Preemptive Kernel

In some cases, breaking up long RTC steps into short enough pieces might be very difficult, and consequently the task-level response of the non-preemptive "vanilla" kernel might be too long. An example system could be a GPS receiver. Such a receiver performs a lot of floating point number crunching on a fixed-point CPU to calculate the GPS position. At the same time, the GPS receiver must track the GPS satellite signals, which involves closing control loops in sub-millisecond intervals. It turns out that it's not easy to break up the position-fix computation into short enough RTC steps to allow reliable signal tracking. But the RTC semantics of state machine execution does not mean that a state machine has to monopolize the CPU for the duration of the RTC step. A preemptive kernel can perform a context switch in the middle of the long RTC step to allow a higher-priority active object to run. As long as the active objects don't share resources they can run concurrently and complete their RTC steps independently.

The QP event-driven platform includes a tiny, fully preemptive, priority-based real-time kernel component called QK, which is specifically designed for processing events in the RTC fashion. Configuring QP to use the preemptive QK kernel is very easy, but as with any fully preemptive kernel you must be very careful with any resources shared among active objects5. The "Fly 'n' Shoot" example has been purposely designed to avoid any resource sharing among active objects, so the application code does not need to change at all to run on top of the QK preemptive kerel, or any other preemptive kernel or RTOS for that matter. The accompanying code contains the "Fly 'n' Shoot" example with QK in the following directory: <qpcpp>\examples\80x86\qk\watcom\l\game\. You can execute this example in a DOS-console on any standard Windows-based PC.

Prev: 7. Coding Hierarchical State Machines
Next: 9. Using Software Tracing for Testing and Debugging

logo_ql_TM.jpg

Copyright © 2002-2010 Quantum Leaps, LLC. All Rights Reserved.
http://www.state-machine.com

Generated on Tue Mar 16 19:39:10 2010 for QP/C++ by  doxygen 1.6.3