- QUICK START
QP™ is a family of lightweight, open source software frameworks for building responsive and modular real-time embedded applications as systems of cooperating, event-driven active objects (actors). The QP™ family consists of QP/C, QP/C++, and QP-nano frameworks, which are all strictly quality controlled, superbly documented, and commercially licensable.
All QP™ frameworks can run on "bare-metal" single-chip microcontrollers, completely replacing a traditional Real-Time Operating System (RTOS). Ports and ready-to-use examples are provided for most major CPU families. QP/C and QP/C++ can also work with a traditional OS/RTOS, such as: POSIX (Linux, embedded Linux, QNX, INTEGRITY), Win32 (Windows, Windows embedded, Windows CE), ThreadX, MicroC/OS, etc.
The behavior of active objects is specified in QP by means of hierarchical state machines (UML statecharts). The frameworks support manual coding of UML state machines in C or C++ as well as fully automatic code generation by means of the free graphical QM™ modeling tool.
The QP™ frameworks are used in millions of products worldwide in aerospace, robotics, consumer electronics, wired and wireless telecommunications, industrial automation, transportation, and many more. The QP™ frameworks and the QM™ modeling tool received over 33,000 downloads last year (not even counting downloads of QP ports).
The QP™ frameworks are licensed under the dual licensing model, which combines the best of the open source and proprietary software worlds to make open source a safe choice for the embedded systems vendors. For the commercial licensees this means the accountability for the licensed intellectual property, professional documentation and technical support expected of a traditional software vendor as well as transparent development, availability of source code and active community inherent in open source projects.
NOTE: If your company has a policy forbidding open source in your product, all QP™ frameworks can be licensed commercially, in which case you don't use any open source license and you do not violate your policy.
Raw RTOS tasks and numerous blocking mechanisms, such as semaphores, event flags, etc., are complex and error-prone to work with directly, which means they waste your time. Active objects provide a more productive architecture, which is safer to use, more efficient, and easier to understand, extend, test, and maintain.
Active objects (a.k.a. actors) are event-driven, strictly encapsulated software objects endowed with their own threads of control that communicate with one another asynchronously by exchanging events. The UML specification further proposes the UML variant of hierarchical state machines (UML statecharts) with which to model the behavior of event-driven active objects.
Active objects inherently support and automatically enforce the following best practices of concurrent programming:
Active objects dramatically improve your ability to reason about the concurrent software. In contrast, using raw RTOS tasks directly is trouble for a number of reasons, particularly because raw tasks let you do anything and offer you no help or automation for the best practices. As with all good patterns, active objects rise the level of abstraction above the naked threads and let you express your intent more directly thus improving your productivity.
Historical Note: The concept of autonomous software objects communicating by message passing dates back to the 1970s, when Carl Hewitt at MIT developed a notion of an actor. In the 1990s, methodologies like ROOM adapted actors for real-time computing. More recently, UML has introduced the concept of active objects that is essentially synonymous with the ROOM actor. Today, the actor model is all the rage in the enterprise computing, because it can deliver levels of reliability and fault tolerance unachievable really with the "free threading" approach. A number of actor programming languages (e.g., Erlang, Scala, D) as well as actor libraries and frameworks (e.g., Akka, Killim, Jetlang) are in extensive use. In the real-time embedded space, active objects frameworks provide the backbone of various modeling and code generation tools. Examples include: IBM Rational Rhapsody (with OXF/SXF frameworks) and National Instruments LabVIEW (with LabVIEW Actor Framework).
Active objects cannot operate in a vacuum and require a software infrastructure (framework) that provides, at a minimum, an execution thread for each active object, queuing of events, and event-based timing services. In the resource-constrained embedded systems, the biggest concern has always been about scalability and efficiency of such frameworks, especially that the frameworks accompanying various modeling tools have traditionally been built on top of a conventional RTOS, which adds memory footprint and CPU overhead to the final solution.
The QP frameworks have been designed for efficiency and minimal footprint from the ground up and do not need an RTOS in the stand-alone configuration. In fact, when compared to conventional RTOSes shown on the left, QP frameworks provide the smallest footprint especially in RAM (data space), but also in ROM (code space). This is possible, because active objects don't need to block, so most blocking mechanisms (e.g., semaphores) of a conventional RTOS are not needed.
All these characteristics make event-driven active objects a perfect fit for single-chip microcontrollers (MCUs). Not only you get the productivity boost by working at a higher level of abstraction than raw RTOS tasks, but you get it at a lower resource utilization and better power efficiency, because event-driven systems use the CPU only when processing events and otherwise can put the MCU in a low-power sleep mode.
Each active object has its own event queue and receives all events exclusively through this queue. Events are delivered asynchronously, meaning that an event producer merely posts an event to the event queue of the recipient active object but doesn't wait in line for the actual processing of the event. The event processing occurs always in the thread context of the recipient active object. The QP framework is responsible for delivering and queuing the events in a thread-safe and deterministic manner.
In a sense active objects are the most stringent form of object-oriented programming (OOP), because the asynchronous communication enables active objects to be truly encapsulated. In contrast, the traditional OOP encapsulation, as provided by C++, C# or Java, does not really encapsulate anything in terms of concurrency. Any operation on an object runs in the caller's thread and the attributes of the object are subject to the same race conditions as global data, not encapsulated at all. To become thread-safe, operations need to be explicitly protected by a mutual exclusion mechanism, such as a mutex or a monitor, but this reduces parallelism dramatically, causes contention, and is a natural enemy of scalability.
In contrast, all private attributes of an active object are truly encapsulated without any mutual exclusion mechanism, because they can be only accessed from the active object's own thread. Note that this encapsulation for concurrency is not a programming language feature, so it is no more difficult to achieve in C as in C++, but it requires a programming discipline to avoid sharing resources (shared-nothing principle). However, the event-based communication helps immensely, because instead of sharing a resource, a dedicated active object can become the manager of the resource and the rest of the system can access the resource only via events posted to this manager active object.
Each active object handles events in run-to-completion (RTC) fashion, which also is exactly the semantics universally assumed by all state machine formalisms, including UML statecharts. RTC simply means that an active object handles one event at a time, that is, the active object must complete the processing of an event before it can start processing of the next event from its queue.In the case of active objects, where each object runs in its own thread, it is important to clearly distinguish the notion of RTC from the concept of thread preemption. In particular, RTC does not mean that the active object thread has to monopolize the CPU until the RTC step is complete. Under a preemptive kernel, an RTC step can be preempted by another thread executing on the same CPU. This is determined by the scheduling policy of the underlying kernel, not by the active object model. When the suspended thread is assigned CPU time again, it resumes from the point of preemption and, eventually, completes its event processing. As long as the preempting and the preempted threads don't not share any resources, there are no concurrency hazards.
Most conventional RTOS kernels manage the tasks and all inter-task communication based on blocking, such as waiting on a semaphore. However, blocking is problematic, because while a task is blocked waiting for one type of event, the task is not doing any other work and is not responsive to other events. Such a task cannot be easily extended to handle new events.
In contrast, event-driven active objects don't need to block, because in event-driven systems the control is inverted compared to traditional RTOS tasks. Instead of blocking to wait for an event, an active object simply finishes its RTC step and returns to the framework to be activated when the next event arrives. This arrangement allows active objects to remain responsive to events of all types, which is central to the unprecedented flexibility and extensibility of active object systems.
The QP frameworks provide all mechanisms you might need for non-blocking operation. For example, instead of delaying an active object with a blocking
delay() call, you can use a time event to arrange activation in the specific time in the future.
While the active object model can work with a traditional blocking RTOS, it can also work with a much simpler non-blocking, run-to-completion kernel. The QP frameworks provide such a super-simple and super-fast kernel called QK, which provides fully preemptive multitasking using a single stack for all active object threads.
The fixed-priority, preemptive QK kernel meets all the assumptions of the Rate Monotonic Analysis (RMA) to ensure schedulability of active object's threads. In fact, the non-blocking execution model makes the RMA method much simpler to apply to a system of active objects than to a set of RTOS tasks.
The behavior of each active object in QP is specified by means of a hierarchical state machine (UML statechart), which is the most effective and elegant technique of decomposing event-driven behavior. The most important innovation of UML state machines over classical FSMs is the hierarchical state nesting. The value of state nesting lies in avoiding repetitions, which are inevitable in the traditional "flat" FSM formalism and are the main reason for the "state-transition explosion" in FSMs. The semantics of state nesting allow substates to define only the differences of behavior from the superstates, thus promoting sharing and reusing behavior.
The hallmark of the QP implementation technique is traceability, which is direct, precise, and unambiguous mapping of every state machine element to human-readable C or C++ code. Preserving the traceability from requirements through design to code is essential for mission-critical systems, such as medical devices or avionic systems.
The QP state machine frameworks make also excellent targets for automatic code generation, which is provided by a graphical modeling tool called QM (QP Modeler). QM is a free, cross-platform, graphical UML modeling tool for designing and implementing real-time embedded applications based on the QP active object frameworks. QM is available for Windows, Linux, and Mac OS X.
QM provides intuitive diagramming environment for creating good looking hierarchical state machine diagrams and hierarchical outline of your entire application. QM eliminates coding errors by automatic generation of compact C or C++ code that is 100% traceable from your design.
QS™ (QP Spy) is a real-time tracing instrumentation built into the QP/C and QP/C++ active object frameworks and available also to the applications. QS allows you to gain unprecedented visibility into your application by selectively logging almost all interesting events occurring within state machines, the framework, the kernel, and your application code. QS event logging is minimally intrusive, offers precise time-stamping, sophisticated runtime filtering of events, and good data compression. QS can be configured to send the real-time data out of the serial or Ethernet port of the target device, or even write the data to a file.
The QP/C and QP-nano frameworks comply with most of the MISRA-C:2004 rules while the QP/C++ framework complies with most of the MISRA-C++:2008 rules. All deviations are carefully limited into very specific contexts and are documented with the MISRA Compliance Matrices. The QP frameworks go even beyond MISRA, by complying with the strict type checking of PC-Lint and a very consistent, documented Quantum Leaps Coding Standard.
All QP framework types come with extensive support for automatic rule checking by means of PC-Lint, which is designed not just for proving compliance of the QP framework code, but more importantly, to aid in checking compliance of the application-level code. Any organization engaged in designing safety-related embedded software could benefit from the unprecedented quality infrastructure built around the QP frameworks.
The QP family of active object frameworks comes with the most thorough documentation in the industry. In fact, we have literally written books on active object frameworks and state machines for embedded systems.
The latest book, Practical UML Statecharts in C/C++, 2nd Edition: Event-Driven Programming for Embedded Systems by Miro Samek, is the most popular text on UML statecharts, event-driven programming, and active objects for embedded systems. This ultimate resource describes all the related concepts and provides a very detailed design study of the QP frameworks.
The book is augmented by the extensive library of Application Notes, Articles and a popular "State Space" blog. Additionally, QP Development Kits (QDKs) for specific boards and compilers are accompanied by detailed User Manuals.
Free support is provided through the very active QP discussion forum hosted at SourceForge.net. Posts to this forum are typically answered the same day. Commercial technical support is provided with each commercial license. Training and consulting are available upon request.
|UML-Compliant Event Processor (QEP™)|
|Highly maintainable and traceable boilerplate state machine mapping to C or C++|
|Full support for hierarchical state nesting|
|Full support for automatic entry/exit action execution on arbitrary state transition topology|
|Full support of nested initial transitions|
|Number of states limited only by code space in ROM|
|Support for events with parameters||arbitrary
|Extremely small data requirements (RAM footprint)||1 pointer-to-
plus the VPTR
|Very small code size (ROM footprint)||0.6-1.5 KB2||0.8-2.0 KB2||0.5-1.0 KB2|
|Support for simpler non-hierarchical Finite State Machines with entry/exit actions|
|Fully reentrant event processor code with minimal stack requirements|
|Source code compliant with MISRA-C:2004 guidelines and passing strict analysis with PC-lint|
|QSPY software tracing instrumentation for testability|
|Real-Time Framework (QF®)|
|Support for active object computing with the following number of active objects:||up to 63||up to 63||up to 8|
|Deterministic thread-safe execution of active objects|
|Inherently low-power architecture|
|Direct event delivery with first-in-first-out (FIFO) policy|
|Direct event delivery with last-in-first-out (LIFO) policy|
|Publish-subscribe event delivery with event multicasting capabilities|
|Dynamic events with zero-copy event passing policy for maximum performance||3|
|Automatic recycling of dynamic events (garbage collection for events)||4|
|Efficient zero-copy deferring and recalling events|
|One-shot time events (one-shot timers)||5||5||6|
|Periodic time events (periodic timers)||5||5|
|Thread-safe event queues for task-to-ISR communication||7||7|
|Thread-safe memory partitions (fixed size heaps) for application use|
|Platform Abstraction Layer (PAL) for ease of portability|
|PAL supports integration with a traditional OS/RTOS|
|Cooperative kernel with prioritized execution of active objects|
|Small, scalable code size (ROM footprint)||2-4 KB2||2-4 KB2||0.5-2 KB2|
|Source code 98% compatible with MISRA guidelines and passing strict analysis with PC-lint|
|Assertion-based error handling|
|QSPY™ software tracing instrumentation for testability|
|Preemptive Run-to-Completion Kernel (QK™)|
|Preemptive, priority-based, deterministic execution of one-shot tasks (active objects)||up to 63
|up to 63
|up to 8
|Extremely fast run-to-completion event processing without blocking|
|Single stack for all tasks and interrupts|
|Allows using compiler-generated interrupt service routines|
|Highly portable to most CPUs and compilers|
|Extended context switch for coprocessors|
|QSPY software tracing instrumentation for testability|
|Software Tracing (QS™)|
|Minimally intrusive, thread-safe software tracing for all QP components|
|Good data compression minimizing the buffering and bandwidth requirements|
|Generic mechanisms for logging of application-level activity|
|Sophisticated runtime filtering of records based on record-type and object type|
|Precise time-stamping of trace records|
|Robust HDLC-like data protocol|
|Decoupled data logging and transmission for flexibility and portability|
|Portable host-based application (QSPY) with full source code|
|QSPY MATLAB® interface|
1 QEP-nano supports only fixed-size event parameter(s) of 0 (no parameter), 1, 2, or 4 bytes
2 The actual code size depends on the processor and the compiler, but the given ranges are representative
3 QF-nano copies entire fixed-size events
4 QF-nano does not need automatic event recycling
5 In QF/C and QF/C++ time events are allocated by the application and there is no limit how many could be used
6 QF-nano provides one time event per active object
7 In QF/C and QF/C++ thread-safe event queues are allocated by the application and there is not limit how many could be used
Last updated: October 15, 2013