QP active object framework for mbed

History:
Last updated for QP 4.5.02 on 05-Sep-2012

Description

QP framework

QP logo and books about QP

QP is a lightweight, open source, active object framework for microcontrollers such as ARM Cortex-M3/M0. QP enables you to build well-structured event-driven programs as systems of concurrently executing, encapsulated state machines (active objects a.k.a. actors). You can think of QP as a modern real-time operating system (RTOS) specifically designed for executing state machines. The QP framework does everything you can expect from a traditional RTOS, such as preemptive multitasking, plus many things an RTOS can't do at all.

QP is also an excellent target for automatic code generation. To this end, QP is now supported by the the free graphical QM modeling tool, which can automatically generate complete mbed applications from UML state diagrams. Thus QP is your entry into graphical programming for the mbed platform.

QP improves productivity, because you no longer need to struggle with convoluted if-else "spaghetti" code and you don't need to worry about semaphores and other such low-level RTOS mechanisms. Instead, you can work at a higher level of abstraction of events and state machines.

Background

Traditionally, mbed programs are written in a sequential manner, which means that whenever a mbed program needs to synchronize with some external event, such as a button press, arrival of a character through the serial port, or a time delay, it explicitly waits in-line for the occurrence of the event. Waiting ''in-line'' means that the mbed processor spends all of its cycles constantly checking for some condition in a tight loop (called the polling loop).

Although this approach is functional in many situations, it doesn't work very well when there are multiple possible sources of events whose arrival times and order you cannot predict and where it is important to handle the events in a timely manner. The fundamental problem is that while a sequential program is waiting for one kind of event (e.g., a button press), it is not doing any other work and is ''not responsive'' to other events (e.g., bytes from the serial port).

Another big problem with the sequential program structure is wastefulness in terms of power dissipation. Regardless of how much or how little actual work is being done, the mbed processor is always running at top speed, which drains the battery quickly and prevents you from making truly long-lasting battery-powered devices.

For these and other reasons experienced programmers turn to the long-know design strategy called event-driven programming, which requires a distinctly different way of thinking than conventional sequential programs. All event-driven programs are naturally divided into the application, which actually handles the events, and the supervisory event-driven infrastructure (framework), which waits for events and dispatches them to the application. The control resides in the event-driven framework, so from the application standpoint, the control is inverted compared to a traditional sequential program.

It turns out that the event-driven QP active object framework beautifully complements the mbed platform and provides everything you need to build responsive, robust, and power-efficient mbed programs based on modern hierarchical state machines. Enjoy!

Example Programs

The qp_dpp (Dining Philosophers Problem) program provides a simple example of using the QP framework. Please refer to the QL Notebook page "Building Applications with QP (qp_dpp Example)" for more information. Among others, the qp_dpp program illustrates the use of the preemptive QK kernel.

The qp_pelican (PEdestrian LIght CONtrolled crossing) program provides a more interesting hierarchical state machine example. Please refer to the QL Notebook page "Hierarchical State Machines (qp_pelican Example)" for more information.

About the QP Port for mbed

The QP framework has been designed from the ground up to be highly portable and to date has been ported to more than a dozen different CPU architectures and many different compilers. The QP library offered for the mbed platform is actually the QP/C++ version of QP (the other QP versions are QP/C and QP-nano). The QP port to mbed is based on the QP/C++ port to ARM Cortex, which has been available for a number of years at state-machine.com/arm.

NOTE

The standard QP/C++ distribution (available for download from SourceForge.org) is intended to be used as a pre-compiled, fine-granularity library, which allows elimination of any unused code at link-time. This baseline QP/C++ version will work "as-is" on the mbed platform. However, it turns out that the mbed compiler recompiles all files of any library provided in source code almost every time any change is made to the including program. To minimize the overhead of this annoying re-compilation, the QP/C++ source code has been consolidated for the mbed platform in just three source files (two .CPP files and one assembly file).

The files comprising the QP/C++ framework for mbed are as follows:

+-qp/                - qp library for mbed
  +-qp_port.h        - QP/C++ port for mbed header file
  +-qp.h             - QP/C++ platform-independent header file
  +-qp.cpp           - QP/C++ platform-independent source code
  +-qk_port.s        - QK port to ARM Cortex-M0/M3, mbed ARM assembler code

Typically you should not need to edit any of the QP source code. Compile-time configuration of the QP library is achieved by a separate header file qp_config.h, which is defined at the application level and included in the qp_port.h header file and is described below.

Configuring QP via the qp_config.h header file

The QP library for mbed is configured through the qp_config.h header file, which must be provided by the application that uses QP. You can use the following version of the qp_config.h header file to copy-and-paste into your applications:

qp_config.h

#ifndef qp_config_h
#define qp_config_h

// enable the Q-SPY software tracing instrumentation
#define Q_SPY

// enable preemptive QK kernel (cooperative kernel is used when not defined)
#define QK_PREEMPTIVE

// The maximum number of active objects in the application (could be up to 63)
#define QF_MAX_ACTIVE         16

// Uncomment the following macros only if you want to change the given default
//#define Q_EVT_CTOR            1
//#define Q_SIGNAL_SIZE         2
//#define QF_MAX_EPOOL          3
//#define QF_EVENT_SIZ_SIZE     2
//#define QF_EQUEUE_CTR_SIZE    1
//#define QF_MPOOL_SIZ_SIZE     2
//#define QF_MPOOL_CTR_SIZE     2
//#define QF_TIMEEVT_CTR_SIZE   2 

#endif                                                          // qp_config_h

The most important options are:

  • Q_SPY: if defined, activates the software tracing instrumentation in the QP framework. This means that virtually any activity inside a framework is output to a circular buffer in memory and can be sent to the host for inspection (please see Section "Software Tracing" below). If the macro Q_SPY is not defined, the software tracing instrumentation becomes inactive both in the framework and in the application. "Inactive" means that the software tracing instrumentation the software tracing instrumentation remains in the code, but stops generating any code, so no overhead of any kind is incurred.
  • QK_PREEMPTIVE: if defined, configures QP to use the preemptive QK kernel. If the macro QK_PREEMPTIVE is not defined, the simpler cooperative "vanilla" kernel is used. NOTE: using the preemptive kernel requires adding special macros (QK_ISR_ENTRY and QK_ISR_EXIT) to every interrupt service routine (ISR). Also, the two kernels use different idle callbacks (QK_onIdle() and QF_onIdle(), respectively).
  • QF_MAX_ACTIVE: determines the maximum number of active objects in the application. The term "active object" comes from the UML and denotes an autonomous object running in its own thread of execution. In QP active objects are state machines with a private event queue and unique priority level. The number of active objects in QP must not exceed 63, inclusive, but setting a lower limit than this maximum saves some memory (RAM).

Free Modeling and Code Generation Tool

All QP examples for the mbed microcontroller have been automatically generated by the QM (QP Modeler) tool. QM is a free, cross-platform, graphical UML modeling tool for designing and implementing real-time embedded applications based on the QP state machine frameworks. QM provides best-in-class, intuitive diagramming environment and generates very compact C or C++ code that is 100% traceable from your design. QM is available for Windows, Linux, and Mac OS X. The steps of obtaining the free QM tool and generating the code are described in the QL Notebook page "Graphical Modeling and Automatic Code Generation ".

The following screen shot shows the Dining Philosopher Problem (DPP) model open in QM (click on the picture to enlarge it to 100%):

DPP model in QM (click to enlarge)

NOTE

The QM models for the mbed projects are available for a separate download from the QP Notebook pages describing the examples, because the mbed project archives accept only source code (.h and .cpp files).

Preemptive Multitasking

The QP framework contains a fully preemptive real-time, priority-based kernel called QK. QK is a special run-to-completion kernel type particularly suitable for executing state machines. QK achieves deterministic execution of high-priority tasks, which is as good as any traditional preemptive kernel. The details of implementing the QK kernel on ARM Cortex are described in the Quantum Leaps Application Note: QP and ARM-Cortex (see also the "Documentation and Resources" at the end of this page).

Software Tracing Instrumentation

One of the biggest problems with developing embedded software is very low visibility into the running code inside the target system. You simply don't know what's going on in there. For generations, embedded developers have been using printf() statements coupled with serial port output to get some feedback from the embedded systems to their host workstations. This technique, known as software tracing is particularly popular with the mbed platform, because the online development system offers no traditional single-step debugger.

Software tracing

However, clogging any time-sensitive code with printf() statements is often too intrusive and simply not workable in many systems. When you think about it, printf(), which performs very time-consuming formatting of binary data to ASCII, is exactly not what you should be doing in the resource-constrained target. Instead, you should perform the absolute minimum of work in the target and let the powerful host computer do the heavy lifting of formatting and pretty-printing of the data. Additional benefit of sending unformatted, binary tracing data from the target is natural data compression. It simply takes much less bytes to send the same information in binary than in ASCII.

The QP framework offers a software tracing system called QP-Spy based on binary data transmission, sophisticated data filtering, flexible data output, decoupling of data collecting from data transmission, use of the idle task for data transmission to minimize the impact on the target, thread-safety, and many other features expected from a professional software tracing system.

As any software tracing system, QP-Spy consists of a target-resident component, called QS, and the application running on the hots workstation, called QSPY.

Target-resident QS Component

On the mbed platform, the QS component sends the tracing data through the UART0, connected to the USB virtual COM port, so the tracing data arrives on the host through the same USB cable that powers mbed. The qp_dpp example program contains fully functional tracing implementation for mbed.

Host-resident QSPY Application

The binary QS output needs to be decompressed and formatted by the QSPY host application, which is available for download from SourceForge.net. The ZIP file contains pre-compiled QSPY for Windows and Linux hosts. The source code is also provided, so you can customize it and compile QSPY on any other platform.

QSPY is a console-style application that you invoke from a command line. It outputs human-readable tracing data to the console and also to a file, if you choose so. The reference manual of QSPY is available online at state-machine.com/doxygen/qspy.

Documentation and Resources

Obviously this Notebook page can only highlight some aspects of a mature software framework like QP. If you are interested to learn more about event-driven programming for embedded systems and modern hierarchical state machines, the following resources can get you started.

Information

Application Note: QP and ARM-Cortex with GNU

The Quantum Leaps Application Note: QP and ARM-Cortex with GNU describes the details of the QP port to the ARM-Cortex architecture, including the preemptive QK kernel as well as a cooperative kernel available in QP. This Application Note uses the GNU compiler and the LCPXpresso-1114 (Cortex-M0) and LPCXpresso-1343 (Cortex-M3) boards, but the discussion is applicable to almost any ARM Cortex device, such as the LPC1768 used in mbed.

Information

ESD Article: "Build a Super Simple Tasker"

The Embedded Systems Design cover story article "Build a Super Simple Tasker" describes the principles of an run-to-completion, preemptive, priority-based real-time kernel. The preemptive QK kernel included in QP works exactly like the "Super Simple Tasker", but does not replicate the event queuing mechanism already available in QP. This ESD article is a good reading for everyone interested in using preemptive multitasking kernel in event-driven systems and with state machines in particular.

Information

book: "Practical UML Statecharts in C/C++, Second Edition"

The book "Practical UML Statecharts in C/C++, Second Edition: Event-Driven Programming for Embedded Systems" (Newnes 2008) is the most popular book about UML statecharts, event-driven programming, and active object computing model for embedded systems. This ultimate resource describes all the related concepts and provides a very detailed design study of the QP frameworks.

This book is presented in two parts. In Part I, you get a practical description of the relevant state machine concepts starting from traditional finite state automata to modern UML state machines followed by state machine coding techniques and state-machine design patterns, all illustrated with executable examples.

In Part II, you find a detailed design study of a generic real-time, active object framework indispensable for combining concurrent, event-driven state machines into robust applications. Part II begins with a clear explanation of the key event-driven programming concepts such as inversion of control ("Hollywood Principle"), blocking versus non-blocking code, thread-safe encapsulation, run-to-completion (RTC) execution semantics, the importance of event queues, dealing with time, and the role of state machines to maintain the context from one event to the next. This background is designed to help software developers in making the transition from the traditional sequential to the modern event-driven programming, which can be one of the trickiest paradigm shifts.

The book is very hands-on and contains many working examples for ARM Cortex-M3, 80x86, MSP430, as well as Linux and traditional RTOS (uC/OS-II).


11 comments on QP active object framework for mbed:

14 Feb 2011

Hello, I downloaded qp_dpp a few hours ago and couldn't get the terminal to behave correctly. In going through the code, I found the baud rate of 115200 which I changed in my terminal emulator. However, it is not operating as intended. I've tried Windows' Hyperterm and Tera Term with various terminal emulator options, encoding and serial control options. Do you have some terminal configuration hints?

thanks...kevin

14 Feb 2011

Hi Kevin, I'm just in the process of adding documentation. The output from the qp_dpp example program is *not* ASCII, so it shows as garbage on any standard terminal.

Instead, the output is *binary* and requires a matching host application to decompress the data and show it in human-readable form. The host application to do so is called QSPY and is available for download from SourceForge.net at http://sourceforge.net/projects/qpc/files/QSPY/. You simply download the ZIP file, uncompress it into any directory, and run QSPY with the following command-line parameters (please read the provided README file to find the executable):

qspy -cCOM7 -b115200

Here I've used the COM7 port, but you need to find out which virtual COM is actually used by your mbed board on your computer.

I hope this helps. I will add some documentation about the QSPY software tracing to the QL Notebook soon.

14 Feb 2011

Got it - it's a DOS application under Windows. So, the qspy display is rapidly showing events as they occur? Sure goes by the screen extremely fast. It's like The Matrix. How do you make sendse of the data as it flys by?

15 Feb 2011

QSPY host application is strictly speaking not DOS, but fully 32-bit console-type application. A version for Linux is also available. You can also build it for Mac OS X, as the source and makefile are provided.

The software tracing data decoded by QSPY can be stored into a file (with the -o option) and analyzed in various ways. The incoming data from the target can also be saved in a Matlab-compatible format and analyzed/graphed in Matlab. Notice that the data is timestamped (the first long number in the scrolling output) with sub-microsecond granularity, so everything can be tied to the common timeline.

Also, please note that the individual events (called trace records to distinguish them from application-level events) can be individually enabled or disabled from the output. The system supports also other kinds of data filters, so you can suppress as many trace records as you see fit. More documentation about the QSPY application is available at http://www.state-machine.com/doxygen/qspy.

05 Oct 2011

This looks perfect for mbed Control and Real-Time applications. Not least, it takes away most of the worry that I'll need a JTAG debugger (when mbed can't be used like that). I'm finding out the hard way about limitations of event-handling in regular C++.

I keep looking at your offer to integrate LwIP and QP and thinking - it's the #1 thing mbed needs most. My TCP/IP application is stuck while I try to find why <400 bytes of HTTP GET (even a single packet response) takes 400ms+ using the LwIP libraries today. Unusable! I suppose the required ethernet driver needs official mbed support really.

08 Feb 2012

Is there a way to put the system to sleep for a set amount of time? A function that would put the system in the "idle state" where the cpu is being put in a low power mode?

08 Feb 2012

The QP framework implements sleeping with the WFI instruction. As the name suggests (Wait For Interrupt), the sleeping continues until an interrupt fires. So, if you want to sleep for a specific time, you make sure that a timer interrupt occurs in the specified amount of time. In QP, you already have the SysTick interrupt setup, so the machine sleeps to the next system clock tick.

Miro

08 Feb 2012

I found the error in my program. If you don't define QF_MAX_ACTIVE as a number the compiler throws 25-35 cryptic erros.

Thank you for your help.

09 Feb 2012

Sir,

Where can I find information as to the use of such macros as *_DICTIONARY() and the macros? Where would I find information on how to apply the subscribe/publish commands.

I'm working to develop a simple program and I'm having difficulty making it do what I want. I have Q_SPY running and it seems to just stop doing anything after initializing the first active object.

Thank you.

10 Feb 2012

The macros QS_???_DICTIONARY() are used for software tracing, which is a bit advanced feature and is unrelated to publish/subscribe and other basic tasks. At the beginning I would recommend just ignoring the QS_???() macros, because they don't generate any code when Q_SPY is not defined.

The QP framework is documented extensively in the PSiCC2 book, introduced above. There is also extensive QP/C++ Reference Manual online at: http://www.state-machine.com/doxygen/qpcpp. I would recommend reading the Tutorial at: http://www.state-machine.com/doxygen/qpcpp/tutorial_page.html.

Finally, when you get a bit more familiar with QP, you can read about software tracing here:

http://www.state-machine.com/doxygen/qpcpp/qs_page.html (QS target component)

and

http://www.state-machine.com/doxygen/qspy (QSPY host component)

Miro

20 May 2012

I'm keen to try QP on MBED and I see that you have published 2 application examples but they are rather different to what I am working on. To get started, I wondered if you have an app example that works with events coming from the network? eg. data arrives from a device via UDP. If certain conditions are met some derived data is sent to another device, again over the network. Thx. Paul

Please log in to post comments.