QP state machine frameworks for FreeRTOS.org™

QDK™ FreeRTOS.org™

Document Revision B September 2008

Copyright © Quantum Leaps, LLC www.quantum-leaps.com www.state-machine.com

Table of Contents

1 Introduction...... 1 1.1 What’s Included in the QDK-FreeRTOS? ...... 2 1.2 Licensing QDK-MSP430-IAR...... 2 2 Getting Started ...... 3 2.1 Installation ...... 3 2.2 Building the QP Libraries ...... 4 2.3 Building the Examples ...... 5 2.4 Running the Examples ...... 6 2.4.1 Minimal FreeRTOS.org Demo Application ...... 7 2.4.2 Dining Philosopher Problem (DPP) Example Application ...... 7 3 The Modified FreeRTOS.org Port STR9x_IAR...... 9 3.1 Augmented FreeRTOS.org Interrupt Processing ...... 9 3.2 General Limitations of FreeRTOS.org Ports...... 10 3.3 Changes in the portmacro.h file...... 10 3.4 Changes in the port. File...... 13 3.5 Demo Application for the Updated FreeRTOS.org Port...... 14 4 The QP Port to FreeRTOS.org ...... 15 4.1 The FreeRTOSConfig.h Header File ...... 15 4.2 The qep_port.h Header File ...... 16 4.3 The qf_port.h Header File...... 16 4.3.1 The Active Object Event Queue and ...... 17 4.3.2 The QF Critical Section ...... 17 4.3.3 FreeRTOS.org Include Files ...... 17 4.3.4 QP Include Files...... 17 4.3.5 Memory Pool ...... 18 4.4 The qf_port.c Source File ...... 18 4.5 ARM/THUMB Compilation ...... 22 5 The Example QP Application...... 23 5.1 The main() Function...... 23 5.2 ISRs ...... 24 5.3 Starting Interrupts in vStartInterrupts()...... 25 5.4 Idle Processing in vApplicationIdleHook() ...... 25 5.5 Assertion Handling Policy in Q_onAssert() ...... 26 6 The Quantum Spy (QS) Instrumentation...... 27 6.1 QS Time Stamp Callback QS_onGetTime()...... 29 6.2 Invoking the QSpy Host Application ...... 30 7 Related Documents and References ...... 31 8 Contact Information...... 32

Copyright © Quantum Leaps, LLC. All Rights Reserved. i

1 Introduction This QP™ Development Kit (QDK) describes how to use QP™ event-driven platform with the FreeRTOS.org™ real-time kernel. The actual hardware/software used in this QDK is described be- low (see also Figure 1):

1. IAR STR912-SK board with STR912F device (ARM966E-S core, 96KB RAM, 512KB ROM). 2. IAR Embedded Workbench for ARM (EWARM) KickStart edition version 5.11. 3. FreeRTOS.org™ version 5.0.0 or higher. 4. QP/C/C++ v4.0 or higher. QS trace data J-Link USB (UART0) LED/Peripheral Debugger Jumpers

Two groups of user LEDs 8 LEDs each STR912F Target Device

USB to PC External Power source power selection Jumper

Figure 1 IAR STR912-SK evaluation board with the J-Link JTAG pod.

Copyright © Quantum Leaps, LLC. All Rights Reserved. 1 of 32 QDK™ FreeRTOS.org™ www.state-machine.com/freertos

As shown in Figure 1, the STR912-SK board is connected via a 20-pin ribbon cable to the J-Link USB debugger. Additionally, the UART0 connector of the STR912-SK board is used in this QDK for QS (“Spy”) software tracing of the live QP applications. You need a straight serial cable with DB9 connectors to connect UART0 to the COM port of your PC.

NOTE: Even though the QDK uses a specific target board (STR912-SK in this case), the QDK has been designed generically to rely on the FreeRTOS.org™ to access the hardware. In other words, the QDK should require minimal adaptation for any other platform supported by FreeR- TOS.org™.

1.1 What’s Included in the QDK-FreeRTOS? This QDK provides a modified FreeRTOS port to STR91-IAR, a minimal demo application that tests this updated FreeRTOS.org port, as well as QP port to FreeRTOS and the Dining Philosopher Prob- lem (DPP) application to test the QP port to FreeRTOS. The DPP example application is described in the Application Note “Dining Philosophers Application” [QL AN-DPP 08] (included in this DPP). The DPP application also demonstrates the QS (“Spy”) software tracing.

NOTE: This QDK covers both the C and C++ version of QP. The concrete code examples are taken from the C version, but the C++ version is essentially identical except for some trivial syntax differences.

1.2 Licensing QDK-MSP430-IAR The Generally Available (GA) distribution of QDK-MSP430-IAR available for download from the www.quantum-leaps.com/msp430 website is offered with the following two licensing options: • The GNU General Public License version 2 (GPL) as published by the Free Software Foundation and appearing in the file GPL.TXT included in the packag- ing of every Quantum Leaps software distribution. The GPL open source li- cense allows you to use the software at no charge under the condition that if you redistribute the original software or applications derived from it, the com- plete source code for your application must be also available under the condi- tions of the GPL (GPL Section 2[b]). • One of several Quantum Leaps commercial licenses, which are designed for customers who wish to retain the proprietary status of their code and therefore cannot use the GNU General Public License. The customers who license Quantum Leaps software under the commercial li- censes do not use the software under the GPL and therefore are not subject to any of its terms.

For more information, please visit the licensing section of our website at: www.quantum- leaps.com/licensing.

Copyright © Quantum Leaps, LLC. All Rights Reserved. 2 of 32 QDK™ FreeRTOS.org™ www.state-machine.com/freertos 2 Getting Started This section describes how to install, build, and use QDK-FreeRTOS. This information is intention- ally included early in this document, so that you could start using the QDK as soon as possible.

NOTE: This QDK assumes that the standard QP distribution, consisting of QEP, QF, and QS, has been installed, before installing this QDK. It is also strongly recommended that you read the QP Tutorial (www.quantum-leaps.com/doxygen/qpc/tutorial_page.html) before you start ex- perimenting with this QDK.

2.1 Installation The QDK code is distributed in a ZIP archive (qdkc_freertos_.zip, where stands for a specific QDK-FreeRTOS version, such as 4.0.01). You should uncompress the archive into the same directory into which you’ve installed all the standard QP components. The installation directory you choose will be referred henceforth as QP Root Directory (). The following Listing 1 shows the directory structure and selected files included in the QDK-FreeRTOS distribution. (Please note that the QP directory structure is described in detail in a separate Application Note: “QP Directory Struc- ture” [QL AN-Directory 07]):

doc\ - Documentation for this QDK | +-AN_DPP.pdf - Application Note “Dining Philosophers Application” +-QDK_FreeRTOS.pdf - This “QDK Programmer’s Manual”

FreeRTOS/ - FreeRTOS.org root directory | +-Demo/ - FreeRTOS.org demo applicaitons | +-ARM9_STR91X_IAR_light/ – Modified ARM9_STR91X_IAR demo application | | +-main.c – the main() function of the application | | +-FreeRTOSConfig.h – FreeRTOS configuration | | +-RTOSDemo.eww – IAR workspace to build the application | | +-. . . – other files in the application | +-Source/ - FreeRTOS.org source files | +-portable/ – FreeRTOS.org ports | | +-IAR/ – ports for the IAR toolset | | | +-STR9x/ – ports for the STR9x MCUs (!! MODIFIED from the original !!) | | | | +-port.c - port-specific code (!! MODIFIED from the original !!) | | | | +-portmacro.c - port-specific macros (!! MODIFIED from the original !!) | | | | +-ISR_Support.h - context-switch support (unchanged) | | | | +-portasm.s79 - vPortStartFirstTask and vPortYieldProcessor (unchanged)

/ - QP root directory (qpcpp for QP/C++) | +-include/ - QP public include files | +-qassert.h – Quantum Assertions platform-independent public include | +-qep.h – QEP platform-independent public include | +-qf.h – QF platform-independent public include | +-qequeue.h – native QF event queue include | +-qmpool.h – native QF memory pool include | +-qpset.h – native QF priority set include | +-ports/ - QP ports | +-arm/ - ARM port | | +-freertos/ - FreeRTOS.org ports | | | +-iar/ - IAR compiler | | | | +-dbg/ - QP libraries for Debug configuration

Copyright © Quantum Leaps, LLC. All Rights Reserved. 3 of 32 QDK™ FreeRTOS.org™ www.state-machine.com/freertos

| | | | +-rel/ - QP libraries for Release configuration | | | | +-spy/ - QP libraries for Spy configuration | | | | +-make_ARM966E-S.bat - make script for building QP libraries (ARM966E-S core) | | | | +-FreeRTOSConfig.h - FreeRTOS.org configuration to use in the QP port | | | | +-qep_port.h - QEP port | | | | +-qf_port.h - QF port to FreeRTOS.org | | | | +-qs_port.h - QS port | | | | +-qp_port.h - QP port | +-examples/ - subdirectory containing the examples | +-arm/ - ARM examples | | +-freertos/ - FreeRTOS examples | | | +-iar/ - IAR EWARM examples | | | | +-dpp-str912-sk/ - Dining Philosophers example for STR912-SK | | | | | +-dbg/ - directory containing the Debug build | | | | | +-rel/ - directory containing the Release build | | | | | +-spy/ - directory containing the Spy build | | | | | +-STR91xlibrary/ - directory containing the ST driver library for STR91x | | | | | | +-include/ - driver library header files | | | | | | +-source/ - driver library source files | | | | | +-dpp-freertos.eww - IAR workspace to build the DPP example | | | | | +-bsp.c - Board Support Package for MSP430 | | | | | +-bsp.h - BSP header file | | | | | +-main.c - the main function | | | | | +-philo.c - the Philosopher active object | | | | | +-dpp.h - the DPP header file | | | | | +-table.c - the Table active object | | | | | +-freertos_task.h - raw FreeRTOS.org task header file | | | | | +-freertos_task.c - raw FreeRTOS.org task implementation Listing 1 Selected QP directories and files after installing QDK-FreeRTOS. The high- lighted elements are included the files included in QDK-FreeRTOS.

2.2 Building the QP Libraries QP is deployed as a set of libraries that you statically link to your application. The pre-built libraries for QEP, QF, and QS are provided inside the \ports\arm\freertos\iar\ directory (see Listing 1). This section describes steps you need to take to rebuild the libraries yourself.

NOTE: To streamline and simplify the QP-library build process, Quantum Leaps software does not use the vendor-specific IDEs, such as the IAR Embedded Workbench IDE, for building the QP libraries. Instead, this QDK provides command-line build process based on simple batch scripts. The build process for your application is largely independent on the QP-library builds. In fact, once you have the QP libraries, you typically don’t need to rebuild them—at least not on the daily basis as you work on your application. This QDK uses the IAR EWARM IDE to build the ex- ample applications, but you are free to use any other build strategy.

The code distribution contains all the batch file make_ARM966E-S.bat for building all the libraries lo- cated in \ports\arm\freertos\iar\ directory. For example, to build the debug version of all the QP libraries for FreeRTOS.org with the IAR ARM compiler, you open a console window on a Win- dows PC, change directory to \ports\freertos\iar\, and invoke the batch script by typing at the command prompt the following command: make_ARM966E-S.bat

Copyright © Quantum Leaps, LLC. All Rights Reserved. 4 of 32 QDK™ FreeRTOS.org™ www.state-machine.com/freertos

The make process should produce the QP libraries in the location: \ports\arm\freertos\iar\- dbg\. The make_ARM966E-S.bat assumes that the IAR ARM toolset has been installed in the directory c:\tools\IAR\ARM_KS_5.11. The batch script also assumes that FreeRTOS is installed in the directory ..\..\..\..\..\FreeRTOS\Source\include relative to the QP port directory (see Listing 1).

NOTE: You need to adjust the symbol IAR_ARM at the top of the make_ARM966E-S.bat file if you’ve installed the IAR toolset in a different directory. You also need to adjust the symbol RTOS_INCDIR if you’ve installed FreeRTOS.org in a different directory.

In order to take advantage of the Q-SPY instrumentation, you need to build the Spy version of the QP libraries. You achieve this by invoking the make_ARM966E-S.bat utility with the “spy” target, like this: make_ARM966E-S spy

The make process should produce the QP libraries in the directory: \ports\arm\freertos\iar\- spy\. You choose the build configuration by providing a target to the make_ARM966E-S.bat utility. The de- fault target is “dbg”. Other targets are “rel”, and “spy” respectively. The following table summa- rizes the targets accepted by make.bat.

Software Version Build command

Debug (default) make_ARM966E-S

Release make_ARM966E-S rel

Spy make_ARM966E-S spy

Table 1 Make targets for the debug, release, and spy software versions

2.3 Building the Examples This QDK-FreeRTOS uses the DPP (“Dining Philosophers Problem”) example described in Application Note [QP AN-DPP 08]. The QDK contains the IAR Embedded Workbench workspaces to build the examples. Each workspace contains three build configurations: Debug, Release, and Spy. The workspaces are located in \examples\arm\freertos\iar\dpp-str92-sk\dpp-freertos.esw. As shown in Figure 2, you select the build configuration from the drop-down box in the top-left corner of the IDE.

Copyright © Quantum Leaps, LLC. All Rights Reserved. 5 of 32 QDK™ FreeRTOS.org™ www.state-machine.com/freertos

Use the drop-down list to select one of the build configurations: Debug, Release, or Spy

Figure 2 IAR Embedded Workbench with the dpp-freertos.eww workspace.

2.4 Running the Examples As mentioned before, this QDK contains two example applications for the STR912-SK board. Both of these examples require setting up the jumpers as shown in Figure 1 and in greater detail in Figure 3.

LED 9 LED 8 LED 7 LED 6 LED 5 LED 4 LED 3 LED 2 LED 1 LED 16 LED 15 LED 14 LED 13 LED 12 LED 11 LED 10 Figure 3 LED/Peripheral jumper setting on the STR912-SK board.

Copyright © Quantum Leaps, LLC. All Rights Reserved. 6 of 32 QDK™ FreeRTOS.org™ www.state-machine.com/freertos

2.4.1 Minimal FreeRTOS.org Demo Application The first example included in this QDK is a minimal FreeRTOS.org demo application for the updated port to the STR1x processor. This example does not use QP and its purpose is to validate the modi- fied FreeRTOS port. This demo application is located in the directory FreeRTOS\Demo\- ARM9_STR91X_IAR_light (see Listing 1).

Figure 4 The RTOSDemo demo application stopped at the IAR debugger

You load and debug the application using the IAR Embedded Workbench (see Figure 4). The appli- cation uses the User LEDs 9-11 to display the status of the three tasks LED-flashing tasks (the standard minimal FreeRTOS.org demo). LED 16 is toggled within the idle callback (vApplica- tionIdleHook()).

2.4.2 Dining Philosopher Problem (DPP) Example Application The second example included in this QDK is the Dining Philosopher Problem (DPP) Example Appli- cation described in the Application Note “Dining Philosophers Application” [QL AN-DPP 08]. This ex- ample demonstrates QP and its purpose is to validate the QP port to FreeRTOS.org. This example application is located in the directory \examples\arm\freertos\iar\dpp-str912-sk\ (see Listing 1).

Copyright © Quantum Leaps, LLC. All Rights Reserved. 7 of 32 QDK™ FreeRTOS.org™ www.state-machine.com/freertos

As the DPP application is running, the user LEDs 9-13 (see Figure 1) should blink indicating the changing status of the Dining Philosophers. Additionally LED 15 should blink once per second with 50% duty cycle. LED 15 is driven from a raw FreeRTOS task running outside the QP framework. Finally, LED 16 should glow at lower intensity than the rest of the LEDs. LED 16 is toggled from the FreeRTOS idle callback (vApplicationIdleHook()) If you downloaded the Spy build configuration to the target board and connected the UAR0 of the STR912-SK board to the COM port on your PC, you could launch the QSPY host utility to observe the output in the human-readable format. You launch the QSPY utility on a Windows PC as follows. Change the directory to the QSPY host utility \tools\qspy\win32\mingw\rel and execute:

qspy –c COM1 –b 115200

This will start the QSPY host application to listen on COM1 serial port with baud rate 115200. (Please use the actual COM port number on your PC.) The following screen shot shows the QSPY output from the DPP run:

command-line options used

timestamp

QS trace record

Figure 5 Screen shot from the QSPY output.

Copyright © Quantum Leaps, LLC. All Rights Reserved. 8 of 32 QDK™ FreeRTOS.org™ www.state-machine.com/freertos 3 The Modified FreeRTOS.org Port STR9x_IAR The standard FreeRTOS.org ports must be modified to work with QP for the following two reasons: 1. FreeRTOS.org uses different APIs for posting to message queues from tasks and ISRs. In con- trast, QP uses the same API in all contexts. 2. FreeRTOS.org generally does not support interrupt nesting and consequently interrupts should never be unlocked inside ISRs. To be able to use the FreeRTOS.org critical section mechanism, QP must not inadvertently unlock interrupts within ISRs (again, because QP uses the same API inside tasks and ISRs).

Unfortunately, because all FreeRTOS ports are hard-coded in the FreeRTOS.h master include file, it is not possible to define the updated port in a different location than the hardwired directory.

NOTE: Because of this design aspect of FreeRTOS.org, you need to manually update the FreeRTOS.org ports each time you update FreeRTOS.org to a newer version.

3.1 Augmented FreeRTOS.org Interrupt Processing The solution to both these issues is to augment FreeRTOS.org ISR processing to keep track of in- terrupt nesting as well as critical section established automatically in hardware upon the interrupt entry. The following

(1) void Your_ISR(void) { (2) portISR_ENTRY(); /* always inform FreeRTOS about entering an ISR */

(3) /* clear the interrupt source... */

(4) Call one of FreeRTOS services allowed in ISRs: vTaskIncrementTick(); vTaskResumeFromISR(); xQueueSendFromISR(); xQueueSendToFrontFromISR(); xQueueSendToBackFromISR(); xQueueReceiveFromISR(); xSemaphoreGiveFromISR();

(5) Call one of QP services allowed in ISRs: QF_tick(); QF_publish(); QActive_postFIFO(); QACtive_postLIFO(); Q_NEW(); QEQueue_get(); QEQueue_postFIFO(); QEQueue_postLIFO();

(6) portISR_EXIT(); /* always inform FreeRTOS about exiting an ISR */ } Listing 2 Augmented interrupt processing for FreeRTOS.org

(1) The ISR is typically entered with interrupts locked in hardware (some CPUs, such as ARM Cor- tex-M3 don’t lock interrupts, but this is an exception). In particular, ARM7/ARM9 processors en- ter the IRQ exception with IRQs locked at the ARM core level.

Copyright © Quantum Leaps, LLC. All Rights Reserved. 9 of 32 QDK™ FreeRTOS.org™ www.state-machine.com/freertos

(2) The macro portISR_ENTRY() informs FreeRTOS.org (and QP) about entering the ISR. This macro is shown in Listing 3.

NOTE: Calling postISR_ENTRY() at the beginning of every ISR is necessary for correct perform- ance of the QP framework.

(3) If your interrupt source requires clearing, you should perform it right after portISR_ENTRY(). (4) You may call any of the FreeRTOS.org services allowed in the ISR context. (5) You may also call any of the QP services allowed in the ISR context. Please note that most QP services allowed in ISRs can also be called from the task level. (2) The macro portISR_EXIT() informs FreeRTOS.org (and QP) about exiting the ISR. This macro is shown in Listing 3. This macro performs also FreeRTOS.org context switch, if preemptive con- figuration is selected.

NOTE: Calling postISR_EXIT() at the beginning of every ISR is necessary for correct perform- ance of the QP framework.

3.2 General Limitations of FreeRTOS.org Ports As of Version 5.0, FreeRTOS.org does not support nesting of interrupts for most CPU architectures, including ARM7/ARM9. This means that you should never enable interrupts inside ISRs, either di- rectly or indirectly inside any function you call from ISRs. Additionally, specifically to ARM7/ARM9 architectures, you should not call any FreeRTOS.org ser- vices from FIQ (“fast interrupt”). Preferably, you should not use FIQ at all. The ARM architecture has no means to protect IRQs from by the FIQ and consequently interrupt nesting can- not be prevented when FIQ is used.

3.3 Changes in the portmacro.h file Listing 3 shows the portmacro.h header file located in the port directory (\Source\portable\IAR\STR91x\) in this case. The explanation section immediately following Listing 3 describes the changes made with respect to the original portmacro.h header file from the standard FreeRTOS distribution. Note that the changes are fairly generic, so they can be copied verbatim to other FreeRTOS.org ports.

#include

#ifdef __cplusplus extern "C" { #endif

/* Type definitions. */ #define portCHAR char #define portFLOAT float #define portDOUBLE double #define portLONG long #define portSHORT short #define portSTACK_TYPE unsigned portLONG #define portBASE_TYPE portLONG

Copyright © Quantum Leaps, LLC. All Rights Reserved. 10 of 32 QDK™ FreeRTOS.org™ www.state-machine.com/freertos

#if( configUSE_16_BIT_TICKS == 1 ) typedef unsigned portSHORT portTickType; #define portMAX_DELAY ( portTickType ) 0xffff #else typedef unsigned portLONG portTickType; #define portMAX_DELAY ( portTickType ) 0xffffffff #endif /*------*/

/* Hardware specifics. */ #define portSTACK_GROWTH ( -1 ) #define portTICK_RATE_MS ( ( portTickType ) 1000 / configTICK_RATE_HZ ) #define portBYTE_ALIGNMENT 4 #define portYIELD() asm ( "SWI 0" ) #define portNOP() asm ( "NOP" ) /*------*/

#define portDISABLE_INTERRUPTS() __disable_interrupt() #define portENABLE_INTERRUPTS() __enable_interrupt()

/* Critical section handling. */ (1) #define portENTER_CRITICAL() do { \ (2) __disable_interrupt(); \ (3) ++ulCriticalNesting; \ } while (0)

(4) #define portEXIT_CRITICAL() \ (5) if ((--ulCriticalNesting) == portNO_CRITICAL_NESTING) { \ (6) __enable_interrupt(); \ (7) } else ((void)0)

(8) #define portISR_ENTRY() do { \ (9) ++ulInterruptNesting; \ (10) ++ulCriticalNesting; \ } while (0)

(11) #if configUSE_PREEMPTION == 1 #define portISR_EXIT() do { \ (12) --ulInterruptNesting; \ (13) --ulCriticalNesting; \ (14) vTaskSwitchContext(); \ } while (0) #else #define portISR_EXIT() do { \ (15) --ulInterruptNesting; \ (16) --ulCriticalNesting; \ } while (0) #endif

/* externals and constants required to handle critical sections */ (17) extern volatile unsigned portLONG ulCriticalNesting; (18) extern volatile unsigned portLONG ulInterruptNesting; (19) #define portNO_CRITICAL_NESTING ((unsigned portLONG)0)

/* application-specific function to start interrupts, including the timer that generates the tick ISR */ (20) void vStartInterrupts(void);

/*------*/

/* Task utilities. */ #define portEND_SWITCHING_ISR(xSwitchRequired) \ if (xSwitchRequired) { \

Copyright © Quantum Leaps, LLC. All Rights Reserved. 11 of 32 QDK™ FreeRTOS.org™ www.state-machine.com/freertos

extern void vTaskSwitchContext(void ); \ vTaskSwitchContext(); \ } \ else ((void)0)

/*------*/

/* Compiler specifics */ #define inline

/* Task function macros as described on the FreeRTOS.org WEB site. */ #define portTASK_FUNCTION_PROTO( vFunction, pvParameters ) \ void vFunction( void * pvParameters ) #define portTASK_FUNCTION( vFunction, pvParameters ) \ void vFunction( void * pvParameters )

#ifdef __cplusplus } #endif Listing 3 Updated portmacro.h header file located in the \Source\portable\IAR\- STR91x\ directory.

(1) The macro portENTER_CRITICAL() has been re-defined to perform all actions inline to eliminate the function call overhead from the original implementation. This slightly increases code size, but avoids flushing the pipeline (5-stage pipeline in ARM9 devices) twice per each portEN- TER_CRITICAL() invocation. The do { … } while(0) loop around the body of the macro is the standard way of syntactically-correct grouping instructions without creating the dangling-else problem. (2) The IAR intrinsic function __disable_interrupt() expands inline to 3 ARM instructions, when the code is compiled to ARM. (The source code of FreeRTOS.org and the QF component of QP are compiled to ARM for best performance.) (3) The global variable ulCriticalNesting is incremented to account for entering a critical section. (4) The macro portEXIT_CRITICAL() has been re-defined to perform all actions inline to eliminate the function call overhead from the original implementation. (5) The global variable ulInterruptNesting is first decremented and then compared to the portNO_CRITICAL_NESTING constant. (6) When ulInterruptNesting drops down to the portNO_CRITICAL_NESTING level it means that the last level of critical section nesting is being exited. Only in this case the interrupts can be unlocked by means of the IAR intrinsic function __enable_interrupt(), which expands inline to 3 ARM instructions, when the code is compiled to ARM. (7) Terminating the if branch with else ((void)0) is the standard way of syntactically-correct grouping instructions without creating the dangling-else problem. (8) The macro portISR_ENTRY() has been introduced in this updated FreeRTOS.org port, as de- scribed in Section 3.1. (9) The global variable ulInterruptNesting is incremented to account for entering an interrupt level. (10) The global variable ulCriticalNesting is incremented to account for entering a critical section already established automatically in hardware.

NOTE: Incrementing of ulCriticalNesting should only be done for processors that actually lock interrupts in hardware before entering an ISR. ARM7/ARM9 cores do lock interrupts.

Copyright © Quantum Leaps, LLC. All Rights Reserved. 12 of 32 QDK™ FreeRTOS.org™ www.state-machine.com/freertos

(11) The behavior of the portISR_EXIT() macro (see Section 3.1) depends whether preemptions are configured or not. (12) The global variable ulInterruptNesting is decremented to account for exiting an interrupt level. This step is the exact reverse of step (2). (13) The global variable ulCriticalNesting is decremented to account for exiting the critical sec- tion, as the ISR return restores the CPU status before the interrupt. This step is the exact re- verse of step (9).

NOTE: Decrementing of ulCriticalNesting should only be done for processors that actually unlock interrupts in hardware after exiting an ISR. ARM7/ARM9 cores do unlock interrupts by restoring CPSR from SPSR.

(14) When preemptions are configured, the FreeRTOS.org context switch is requested by calling vTaskSwitchContext(), to check if a higher-priority task was made ready-to-run and needs to preempt the interrupted task. (15-16) When preemptions are not configured, only the nesting levels are decremented, but the FreeRTOS.org context switch is not requested. (17-18) The global nesting levels are declared as external and volatile, because they can be asyn- chronously modified inside ISRs. (19) The constant portNO_CRITICAL_NESTING is defined for the port (typically zero). (20) The prototype of the new callback function vStartInterrupts() is declared. This function re- places the private function prvSetupTimerInterrupt() defined in the original FreeRTOS.org port. The vStartInterrupts() function is called from the modified port.c file to configure and start interrupts, including the system clock tick. This function is application-specific and cannot be defined at the port level (callback function).

3.4 Changes in the port.c File The changes required in port.c file are just simple consequence of the augmented interrupt han- dling policy described in Section 3.1. In fact, the port.c file has been drastically simplified by re- moving system clock tick ISRs from the port level. It’s more convenient to define all ISRs consis- tently at the application level, including the system clock tick ISR. The following Listing 4 shows how the modified port.c file calls the vStartInterrupts() function from xPortStartScheduler().

portBASE_TYPE xPortStartScheduler( void ) { extern void vPortStartFirstTask ( void );

/* Start interrupts, including the timer that generates the tick ISR. Interrupts are disabled here already. */ vStartInterrupts();

/* Start the first task. */ vPortStartFirstTask();

/* Should not get here! */ return 0; }

Copyright © Quantum Leaps, LLC. All Rights Reserved. 13 of 32 QDK™ FreeRTOS.org™ www.state-machine.com/freertos

Listing 4 Calling vStartInterrupts() from the updated port.c source file.

3.5 Demo Application for the Updated FreeRTOS.org Port The demo application for the updated FreeRTOS.org port has been derived from the demo included in the standard FreeRTOS distribution for the STR91x processor. The demo application is located in the directory \Demo\ARM9_STR91X_IAR_light\. Section 2.4.1 describes how to build and run the demo application. The ARM9_STR91X_IAR_light demo application is minimal and simply spawns three LED-flashing tasks, as implemented in \Demo\Common\Minimal\falsh.c. The application also shows how to crrect the LED interface for the STR912-SK board, which was not working in the original demo. The ARM9_STR91X_IAR_light demo also uses Timer3 as the source of the system clock tick interrupt. The most interesting modifications from the original demo include the augmented interrupt proc- essing, as shown below:

__arm void TIM3_IRQHandler(void) { portISR_ENTRY(); /* always inform FreeRTOS about entering an ISR */

TIM3->OC1R += BSP_TIM3_PERIOD - 1; /* set the output compare register */ TIM3->SR &= ~TIM_IT_OC1; /* clear interrupt source (Timer3) */

vTaskIncrementTick(); /* handle the FreeRTOS.org system clock tick */

portISR_EXIT(); /* always inform FreeRTOS about exiting an ISR */ }

NOTE: The ARM9_STR91X_IAR_light demo uses the modified FreeRTOSConfig.h header file, as de- scribed in Section 4.1.

Copyright © Quantum Leaps, LLC. All Rights Reserved. 14 of 32 QDK™ FreeRTOS.org™ www.state-machine.com/freertos

4 The QP Port to FreeRTOS.org The QP port to FreeRTOS.org allows running QP on top of FreeRTOS.org real-time kernel, using the modified FreeRTOS.org port as described in Section 3.

4.1 The FreeRTOSConfig.h Header File As described in the FreeRTOS.org online documentation [FreeRTOS.org 08] (User Documenta- tion/Configuration section), every FreeRTOS application customizes the FreeRTOS by means of the FreeRTOSConfig.h header file. Consequently, the FreeRTOSConfig.h header file is typically located in the application directory. In the QP port, however, some configuration information contained in the FreeRTOSConfig.h header file is required to build the QP components, because QP depends on FreeRTOS.org for critical sec- tion mechanism, tasks, and queues. Therefore, in case of the QP port, the FreeRTOSConfig.h header file is located in the QP port directory, which is \ports\arm\freertos\iar\ in case of this QDK. The QP applications don’t provide their own FreeRTOSConfig.h header files, but rather must consistent with the configuration contained in the QP port directory.

NOTE: To ensure consistency of configuration you need to rebuild the QP libraries after modify- ing the FreeRTOSConfig.h header file in the QP port directory. Section 2.2 describes how to build QP libraries.

(1) #define configUSE_PREEMPTION 1 (2) #define configUSE_IDLE_HOOK 1 (3) #define configUSE_TICK_HOOK 0 (4) #define configTICK_RATE_HZ ( ( portTickType ) 100 ) (5) #define configMAX_PRIORITIES ( ( unsigned portBASE_TYPE ) 64 ) #define configMINIMAL_STACK_SIZE ( ( unsigned portSHORT ) 64 ) #define configTOTAL_HEAP_SIZE ( ( size_t ) 32000 ) #define configMAX_TASK_NAME_LEN ( 16 ) #define configUSE_TRACE_FACILITY 1 #define configUSE_16_BIT_TICKS 0 #define configIDLE_SHOULD_YIELD 1 #define configUSE_MUTEXES 1

/* Co-routine definitions. */ #define configUSE_CO_ROUTINES 0 #define configMAX_CO_ROUTINE_PRIORITIES ( 2 )

/* Set the following definitions to 1 to include the API function, or zero to exclude the API function. */

#define INCLUDE_vTaskPrioritySet 1 #define INCLUDE_uxTaskPriorityGet 1 #define INCLUDE_vTaskDelete 1 #define INCLUDE_vTaskCleanUpResources 0 #define INCLUDE_vTaskSuspend 1 #define INCLUDE_vTaskDelayUntil 1 #define INCLUDE_vTaskDelay 1 #define INCLUDE_xTaskGetCurrentTaskHandle 1 Listing 5 FreeRTOSConfig.h header file located in \ports\arm\freertos\iar\ directory.

(1) QP can work with either preemptive or cooperative kernel. In this QDK FreeRTOS.org is config- ured to operate in preemptive mode.

Copyright © Quantum Leaps, LLC. All Rights Reserved. 15 of 32 QDK™ FreeRTOS.org™ www.state-machine.com/freertos

(2) QP uses the FreeRTOS.org idle hook to implement software tracing (Q-SPY). (3) The time tick hook is not used, because the modified FreeRTOS port leaves the system clock tick interrupt to be implemented at the application level (see Section 3). The application then is responsible to place the QF_tick() call inside the system clock tick ISR (e.g., see Section ???). (4) The system clock tick rate is only necessary to build FreeRTOS, but is not relevant for QP. (5) The number of FreeRTOS.org priorities is configured to the maximum number of active objects supported in QP, because each active object in QP assumes a unique priority level. This is an unusually high number of priorities for FreeRTOS, which supports priority sharing. This number can be reduced to save RAM.

NOTE: You can reduce configMAX_PRIORITIES to save RAM.

4.2 The qep_port.h Header File The only platform-specific aspect used inside the QEP event processor is exact-width integer types. The IAR compiler is C99-compliant compiler and provides the standard header file.

#include /* exact-width integers, WG14/N843 C99, 7.18.1.1 */

#include "qep.h" /* QEP platform-independent public interface */ Listing 6 The mechanism of allocating and accessing data in ROM in qep_port.h

NOTE: The standard exact-width integer types are closely related to the FreeRTOS.org data types portCHAR, portSHORT, and portLONG (see Listing 3). For pre-standard compilers (no stan- dard file), it would be very convenient to define standard exact-width types in terms of portCHAR, portSHORT, and portLONG, thus making the qep_port.h header file adapt automati- cally to any FreeRTOS.org port. However, the FreeRTOS.org documentation is not clear about the semantics of portCHAR, portSHORT, and portLONG, so it’s not clear if they correspond to the standard types int8_t, int16_t, and int32_t, respectively.

4.3 The qf_port.h Header File The QF header file for the FreeRTOS port to STR91x with the IAR compiler is located in \- ports\arm\freertos\iar\qf_port.h. This QF port is generic, because all platform-specific elements are defined in terms of FreeRTOS.org. Typically, you should not need to modify the qf_port.h header file to adapt it to a different CPU or compiler. The complete qf_port.h header file is shown in Listing 7. The subsections following the listing ex- plain how QF integrates with FreeRTOS.org.

NOTE: Porting of QF to third-party RTOS is explained in Chapter 9 of [PSiCC2].

/* FreeRTOS.org event queue and thread types */ (1) #define QF_EQUEUE_TYPE xQueueHandle (2) #define QF_THREAD_TYPE xTaskHandle

/* The maximum number of active objects in the application */

Copyright © Quantum Leaps, LLC. All Rights Reserved. 16 of 32 QDK™ FreeRTOS.org™ www.state-machine.com/freertos

#define QF_MAX_ACTIVE 63

/* FreeRTOS critical section operations */ (3) /* #define QF_INT_KEY_TYPE */ (4) #define QF_INT_LOCK(dummy) portENTER_CRITICAL() (5) #define QF_INT_UNLOCK(dummy) portEXIT_CRITICAL()

(6) #include "FreeRTOS.h" /* FreeRTOS.org master include file */ (7) #include "task.h" /* FreeRTOS.org task management */ (8) #include "queue.h" /* FreeRTOS.org queue management */

(9) #include "qep_port.h" /* QEP port */ (10) #include "qmpool.h" /* FreeRTOS uses native QF memory-pool */ (11) #include "qequeue.h" /* native QF event queue for deferring events */ (12) #include "qf.h" /* QF platform-independent public interface */

/***************************************************************************** * interface used only inside QF, but not in applications */ /* native QF event pool operations */ (13) #define QF_EPOOL_TYPE_ QMPool #define QF_EPOOL_INIT_(p_, poolSto_, poolSize_, evtSize_) \ QMPool_init(&(p_), poolSto_, poolSize_, evtSize_) #define QF_EPOOL_EVENT_SIZE_(p_) ((p_).blockSize) #define QF_EPOOL_GET_(p_, e_) ((e_) = (QEvent *)QMPool_get(&(p_))) #define QF_EPOOL_PUT_(p_, e_) (QMPool_put(&(p_), e_)) Listing 7 The qf_port.h header file with the generic QF port to FreeRTOS.org.

4.3.1 The Active Object Event Queue and Thread As shown in Listing 7(1-2), QF uses the FreeRTOS.org message queue as event queue for active objects (accessed via xQueueHandle) and each active object executes in a separate FreeRTOS.org task (accessed via xTaskHandle).

4.3.2 The QF Critical Section The most important aspect of the port is the interrupt locking/unlocking policy (QF critical section), which is defined in Listing 7(3-5). The QF critical section is defined in terms of the FreeRTOS.org critical section.

NOTE: The FreeRTOS.org critical section mechanism allows for nesting critical sections.

4.3.3 FreeRTOS.org Include Files To compile QP, the qf_port.h header file must include the following FreeRTOS.org include files: FreeRTOS.h, task.h, and queue.h, as shown in Listing 7(6-8).

4.3.4 QP Include Files The qf_port.h header file includes the QEP port, native QF memory pool, raw event queue for de- ferring events, and the platform-independent QF interface, as shown in Listing 7(9-12).

Copyright © Quantum Leaps, LLC. All Rights Reserved. 17 of 32 QDK™ FreeRTOS.org™ www.state-machine.com/freertos

4.3.5 Memory Pool FreeRTOS.org does not provide a fast and deterministic memory pool for events. The QF port uses the native QF memory pool, as shown in Listing 7(10, 13).

4.4 The qf_port.c Source File The QF port to FreeRTOS.org, as most QF ports to third-party RTOSs, require some glue-code to bolt the framework to the external RTOS. You place such code in the qf_port.c source file, which is located in \ports\arm\freertos\iar\src\, as shown in Listing 8. You typically don’t need to change this file for different FreeRTOS.org ports.

#include "qf_pkg.h" #include "qassert.h"

Q_DEFINE_THIS_MODULE(qf_port)

/* Global objects ------*/

/*...... */ char const Q_ROM * Q_ROM_VAR QF_getPortVersion(void) { static const char Q_ROM Q_ROM_VAR version[] = "4.0.01"; return version; } /*...... */ (1) void QF_init(void) { /* no special initialization required by FreeRTOS.org */ } /*...... */ (2) void QF_run(void) { (3) vTaskStartScheduler(); /* start the FreeRTOS.org scheduler */ Q_ERROR(); /* the FreeRTOS.org scheduler should never return */ } /*...... */ (4) void QF_stop(void) { } /*...... */ (5) static __arm void task_function(void *pvParameters) { /* FreeRTOS signature */ (6) ((QActive *)pvParameters)->running = (uint8_t)1; /* enable thread-loop */ (7) while (((QActive *)pvParameters)->running) { (8) QEvent const *e = QActive_get_((QActive *)pvParameters); (9) QF_ACTIVE_DISPATCH_(&((QActive *)pvParameters)->super, e); (10) QF_gc(e); /* check if the event is garbage, and collect it if so */ }

(11) QF_remove_((QActive *)pvParameters); /* remove AO from the framework */ (12) vTaskDelete(((QActive *)pvParameters)->thread); /* delete FreeRTOS task */ } /*...... */ void QActive_start(QActive *me, uint8_t prio, QEvent const *qSto[], uint32_t qLen, void *stkSto, uint32_t stkSize, QEvent const *ie) { portBASE_TYPE err; unsigned portBASE_TYPE freeRTOS_prio;

(13) Q_REQUIRE((qSto == (QEvent const **)0) && (qLen > 0) (14) && (stkSto == (void *)0) && (stkSize > 0));

(15) me->eQueue = xQueueCreate(qLen, sizeof(QEvent*)); /* create event queue */

Copyright © Quantum Leaps, LLC. All Rights Reserved. 18 of 32 QDK™ FreeRTOS.org™ www.state-machine.com/freertos

(16) Q_ASSERT(me->eQueue != (xQueueHandle)0); /* FreeRTOS queue created */ (17) me->prio = prio; /* save the QF priority */ (18) QF_add_(me); /* make QF aware of this active object */ (19) QF_ACTIVE_INIT_(&me->super, ie); /* execute initial transition */

QS_FLUSH(); /* flush the trace buffer to the host */

(20) freeRTOS_prio = tskIDLE_PRIORITY + me->prio; /* FreeRTOS task priority */ /* create the FreeRTOS.org task for the AO */ (21) err = xTaskCreate(&task_function, /* the task function */ (signed portCHAR *)"AO", /* the name of the task */ (unsigned portSHORT)stkSize/sizeof(portSTACK_TYPE), /* stack */ me, /* the 'pvParameters' parameter */ freeRTOS_prio, /* FreeRTOS task priority */ &me->thread); /* task handle */ (22) Q_ASSERT(err == pdPASS); /* FreeRTOS.org task created */ } /*...... */ void QActive_stop(QActive *me) { (23) me->running = (uint8_t)0; /* stop the thread loop */ (24) vQueueDelete(me->eQueue); /* cleanup the queue */ } /*...... */ void QActive_postFIFO(QActive *me, QEvent const *e) { portBASE_TYPE err; QF_INT_LOCK_KEY_ QF_INT_LOCK_(); (25) if (e->dynamic_ != (uint8_t)0) { (26) ++((QEvent *)e)->dynamic_; }

QS_BEGIN_NOLOCK_(QS_QF_ACTIVE_POST_FIFO, QS_aoObj_, me) QS_TIME_(); /* timestamp */ QS_SIG_(e->sig); /* the signal of the event */ QS_OBJ_(me); /* this active object */ QS_U8_(e->dynamic_); /* the dynamic attribute of the event */ QS_EQC_(0); /* number of free entries (unknown) */ QS_EQC_(0); /* min number of free entries (unknown) */ QS_END_NOLOCK_()

QF_INT_UNLOCK_(); (27) if (ulInterruptNesting == (unsigned portLONG)0) { /* task level? */ (28) err = xQueueSendToBack(me->eQueue, &e, (portTickType)0); } else { /* must be ISR level */ portBASE_TYPE xHigherPriorityTaskWoken; (29) err = xQueueSendToBackFromISR(me->eQueue, &e, &xHigherPriorityTaskWoken); } (30) Q_ASSERT(err == pdPASS); } /*...... */ void QActive_postLIFO(QActive *me, QEvent const *e) { portBASE_TYPE err; QF_INT_LOCK_KEY_ QF_INT_LOCK_(); if (e->dynamic_ != (uint8_t)0) { ++((QEvent *)e)->dynamic_; }

QS_BEGIN_NOLOCK_(QS_QF_ACTIVE_POST_LIFO, QS_aoObj_, me) QS_TIME_(); /* timestamp */ QS_SIG_(e->sig); /* the signal of the event */ QS_OBJ_(me); /* this active object */ QS_U8_(e->dynamic_); /* the dynamic attribute of the event */

Copyright © Quantum Leaps, LLC. All Rights Reserved. 19 of 32 QDK™ FreeRTOS.org™ www.state-machine.com/freertos

QS_EQC_(0); /* number of free entries (unknown) */ QS_EQC_(0); /* min number of free entries (unknown) */ QS_END_NOLOCK_()

QF_INT_UNLOCK_(); if (ulInterruptNesting == (unsigned portLONG)0) { /* task level? */ err = xQueueSendToFront(me->eQueue, &e, (portTickType)0); } else { /* must be ISR level */ portBASE_TYPE xHigherPriorityTaskWoken; err = xQueueSendToFrontFromISR(me->eQueue, &e, &xHigherPriorityTaskWoken); } Q_ASSERT(err == pdPASS); } /*...... */ QEvent const *QActive_get_(QActive *me) { QEvent const *e; /* wait indefinitely, INCLUDE_vTaskSuspend must be set to '1' */ (31) Q_ALLEGE(xQueueReceive(me->eQueue, &e, portMAX_DELAY) == pdPASS);

QS_BEGIN_(QS_QF_ACTIVE_GET, QS_aoObj_, me) QS_TIME_(); /* timestamp */ QS_SIG_(e->sig); /* the signal of this event */ QS_OBJ_(me); /* this active object */ QS_U8_(e->dynamic_); /* the dynamic attribute of the event */ QS_EQC_(0); /* number of free entries (unknown) */ QS_END_()

return e; } Listing 8 The qf_port.c source file for the FreeRTOS.org port.

(1) The function QF_init() initializes the framework. In the case of QF port to FreeRTOS.org, the function has nothing to do. (2-3) The function QF_run() transfers control to the framework to run the application. In the case of QF port to FreeRTOS.org, the function starts multitasking by the FreeRTOS call vTask- StartScheduler(). (4) The function QF_stop() stops the framework. There is nothing you can do to stop FreeRTOS.org, you simply abort. Therefore the only action is to call the cleanup callback. (5) Under a traditional RTOS, all active object threads execute the same function task_function(), which is always structured as an endless loop. The task function has the exact signature ex- pected by FreeRTOS.org. The parameter pvParameters is set to the active object owning in the task.

NOTE: The original FreeRTOS.org port can be configured to execute tasks either in ARM (de- fault) or THUMB. The __arm keyword in front of the task function means that QF port assumes that all FreeRTOS.org tasks will be executed in the ARM state.

(6) The task function sets the QActive.running flag to continue the local . (7) The event loop continues as long as the QActive.running flag is set. (8-10) These are the three steps of the active object thread (waiting for an event, dispatching the event, and recycling the event, see Chapter 7 in [PSiCC2]). (11) After the event loop terminates, the active object is removed from the framework.

Copyright © Quantum Leaps, LLC. All Rights Reserved. 20 of 32 QDK™ FreeRTOS.org™ www.state-machine.com/freertos

(12) The task is deleted by the FreeRTOS.org call vTaskDelete().

(13-14) The assertions makes sure that neither event queue storage nor the stack storage are provided by the caller, because FreeRTOS.org allocates the storage internally. (15) The first step in starting an active object is creating the event queue by the FreeRTOS.org call xQueueCreate(). (16) The queue creation must be successful, otherwise the application cannot continue. (17) The active object’s priority is set. (18) The active object is registered with the QF framework. (19) The active object’s state machine is initialized. (20) The QF priority is mapped to the µC/OS-II task priority.

FreeRTOS.org uses a priority numbering scheme in which tskIDLE_PRIORITY represents the lowest possible priority assigned to the idle task, and higher numerical values represent higher task ur- gency. This happens to be very similar priority numbering as used in the QF framework.

(21) The active object thread is created by the FreeRTOS.org call xTaskCreate().

NOTE: Traditional RTOSs, such as FreeRTOS.org, require per-task stacks. The size of the size is provided in the ‘stkSize’ parameter, but FreeRTOS allocates the stack storage internally, so the parameter ‘stkSto’ is not used.

(22) The task creation must be successful, otherwise the application cannot continue. (23) Clearing the QActive.running flag terminates the event loop and exits the active object thread (see line (6)). (24) The event queue is deleted by the FreeRTOS.org call vQueueDelete().

The QF port to FreeRTOS.org does not use the native QF active object queues. Therefore, the QF implementation of QActive_postFIFO(), QActive_postLIFO(), and QActive_get_() must be replaced by the FreeRTOS-specific code. The rest of the qf_port.c source file defines these three functions for FreeRTOS.org (see also Chapter 8 in [PSiCC2]).

(25-26) Posting an event to a queue must always increment the reference counter of a dy- namic event. This must happen exactly as shown. (27) The choice of the right FreeRTOS.org API to post an event to the queue depends on the con- text (interrupt nesting of zero indicates task level, otherwise it is the ISR level).

As described in Section 3.1, FreeRTOS.org requires using different APIs when accessing a queue from an ISR or from a task. Therefore the function QActive_postFIFO() must distinguish between these two contexts to use the correct FreeRTOS.org API. This determination is based on the ulIn- terruptNesting variable introduced in the augmented FreeRTOS.org port (again see Section 3.1)

NOTE: The variable ulInterruptNesting will distinguish correctly between the ISR and the task contexts only when all ISRs, without exceptions, consistently call the macros portISR_ENTRY() upon entry and portISR_EXIT() upon the exit.

Copyright © Quantum Leaps, LLC. All Rights Reserved. 21 of 32 QDK™ FreeRTOS.org™ www.state-machine.com/freertos

(28) In the task level, the pointer to the event is posted to the FreeRTOS.org message queue with the call xQueueSendToBack(), which uses the standard FIFO policy. The queue is not sup- posed to block if it cannot accept the message (event pointer). (29) In the ISR level, the pointer to the event is posted to the FreeRTOS.org message queue with the call xQueueSendToBackFromISR(), which uses the standard FIFO policy. (30) The assertion makes sure that the event is posted successfully. (31) The event is retrieved from the message queue with the FreeRTOS.org call xQueueReceive(). The second argument to this function is the timeout, whereas timeout of portMAX_DELAY indi- cates indefinite waiting on an empty event queue. The Q_ALLEGE() macro surrounding the FreeRTOS call asserts that the receive operation executed successfully.

4.5 ARM/THUMB Compilation Contrary to some misconceptions THUMB code is not inherently slower than ARM. To the contrary, the higher code density of the THUMB instruction set often makes it actually execute faster. Most QP source files (.C files) that comprise the QP libraries (linked later statically with the applications) can be compiled either to ARM or THUMB.

NOTE: The QP libraries as well as application code must be compiled with the ARM/THUMB in- terworking enabled (IAR option –-interwork).

Generally, code that must often lock/unlock interrupts, or the code that can be executed in the ISR context (both requires ARM instruction set) is certainly faster if compiled to ARM. Therefore, most of the QF modules as well as FreeRTOS modules are compiled to ARM. On the other hand, the QEP event processor, and consequently all state machines in the system (active objects) should be compiled to THUMB.

Copyright © Quantum Leaps, LLC. All Rights Reserved. 22 of 32 QDK™ FreeRTOS.org™ www.state-machine.com/freertos 5 The Example QP Application The QP port to FreeRTOS.org has been tested with the Dining Philosopher Problem (DPP) applica- tion described in the Application Note “Dining Philosophers Application” [QL AN-DPP 08]. The stan- dard DPP application has been augmented to demonstrate an independent FreeRTOS.org task exe- cuting outside of the QP framework. The QP example is located in the directory \examples\- arm\freertos\iar\dpp-str92-sk\.

5.1 The main() Function The main() function of the DPP example for FreeRTOS.org is shown in Listing 9. The function has the very standard structure for any QP application, as described in Chapter 1 of [PSiCC2] and also in the online “QP Tutorial” [QP/C 08].

#include "qp_port.h" #include "dpp.h" #include "bsp.h" #include "freertos_task.h" /* the prototype of the FreeRTOS.h task function */

/* Local-scope objects ------*/ static QSubscrList l_subscrSto[MAX_PUB_SIG];

static union SmallEvent { void *min_size; TableEvt te; /* other event types to go into this pool */ } l_smlPoolSto[2*N_PHILO]; /* storage for the small event pool */

/*...... */ __arm void main(void) { uint8_t n;

Philo_ctor(); /* instantiate all Philosopher active objects */ Table_ctor(); /* instantiate the Table active object */

BSP_init(); /* initialize the Board Support Package */ QF_init(); /* initialize the framework and the underlying RT kernel */

/* object dictionaries... */ QS_OBJ_DICTIONARY(l_smlPoolSto);

QF_psInit(l_subscrSto, Q_DIM(l_subscrSto)); /* init publish-subscribe */

/* initialize event pools... */ QF_poolInit(l_smlPoolSto, sizeof(l_smlPoolSto), sizeof(l_smlPoolSto[0]));

for (n = 0; n < N_PHILO; ++n) { /* start the active objects... */ QActive_start(AO_Philo[n], (uint8_t)(n + 1), (QEvent const **)0, N_PHILO, (void *)0, (configMINIMAL_STACK_SIZE * sizeof(portSTACK_TYPE)), (QEvent *)0); } QActive_start(AO_Table, (uint8_t)(N_PHILO + 1), (QEvent const **)0, N_PHILO, (void *)0, (configMINIMAL_STACK_SIZE * sizeof(portSTACK_TYPE)), (QEvent *)0);

/* create a raw FreeRTOS task, not managed by QP */ xTaskCreate(&vTaskFunction, (signed portCHAR *)"TASK",

Copyright © Quantum Leaps, LLC. All Rights Reserved. 23 of 32 QDK™ FreeRTOS.org™ www.state-machine.com/freertos

configMINIMAL_STACK_SIZE, (void *)0, tskIDLE_PRIORITY + N_PHILO + 2, (xTaskHandle *)0);

QF_run(); /* run the QF application (never returns) */ } Listing 9 The main() function of the DPP application with FreeRTOS.org task.

The only significant differences from the main() function without FreeRTOS.org is creating the raw FreeRTOS.org task shown in bold in Listing 9. Also, you don’t need to allocate storage for event queues and stacks of active object tasks, because FreeRTOS.org allocates this storage internally.

5.2 ISRs The ISRs in the DPP application for FreeRTOS.org are located in the Board Support Package (file bsp.c) and are structured as described in Section 3.1.

/* ISRs ------*/ (1) __arm void TIM3_IRQHandler(void) { (2) portISR_ENTRY(); /* always inform FreeRTOS about entering an ISR */

(3) TIM3->OC1R += BSP_TIM3_PERIOD - 1; /* set the output compare register */ (4) TIM3->SR &= ~TIM_IT_OC1; /* clear interrupt source (Timer3) */

(5) #ifdef Q_SPY { uint16_t currTime16 = (uint16_t)TIM3->CNTR; l_currTime32 += (currTime16 - l_prevTime16) & 0xFFFF; l_prevTime16 = currTime16; } #endif

(6) vTaskIncrementTick(); /* handle the FreeRTOS.org system clock tick */ (7) QF_tick(); /* handle the QF system clock tick */

(8) portISR_EXIT(); /* always inform FreeRTOS about exiting an ISR */ } Listing 10 System time tick interrupt (Timer 3).

(1) In the FreeRTOS.org port, ISRs are regular C functions (no __irq functions), because ISRs are actually called from an assembler wrapper interrupt handler linked to the address 0x18. This particular BSP uses the Output Compare interrupt of Timer 3 as the source for the system clock tick ISR. (2) As described in Section 3.1, every ISR must start with calling the macro portISR_ENTRY(). (3) The Output Compare register must be incremented to ensure that the free-running timer will reach the next deadline in the desired time. (4) The interrupt source (Timer 3 output compare) is then cleared. (5) When the QS software tracing is active, the ISR updates the internal counters, which allow ex- tending the 16-bit free-running Timer 3 to 32-bits to accurately time stamp the trace records. (6) The system clock tick ISR must invoke the FreeRTOS.org vTaskIncrementTick() function to manage timeouts and time delays.

Copyright © Quantum Leaps, LLC. All Rights Reserved. 24 of 32 QDK™ FreeRTOS.org™ www.state-machine.com/freertos

(7) The system clock tick ISR must also invoke QF_tick() to manage all armed time events (tim- ers) in the QP application. (8) As described in Section 3.1, every ISR must end with calling the macro portISR_EXIT().

5.3 Starting Interrupts in vStartInterrupts() As described in Section 3.4, the augmented FreeRTOS.org port calls the function vStartInter- rupts(), just before starting the idle task. The vStartInterrupts() function is designed to start the interrupts, including the system clock tick interrupt. In this BSP only the timer tick interrupt is started..

void vStartInterrupts(void) { /* called with interrupts locked */ TIM_InitTypeDef TIM_InitStruct;

SCU_APBPeriphClockConfig(__TIM23, ENABLE); TIM_DeInit(TIM3);

TIM_StructInit(&TIM_InitStruct); TIM_InitStruct.TIM_Mode = TIM_OCM_CHANNEL_1; TIM_InitStruct.TIM_OC1_Modes = TIM_TIMING; TIM_InitStruct.TIM_Clock_Source = TIM_CLK_APB; TIM_InitStruct.TIM_Clock_Edge = TIM_CLK_EDGE_RISING; TIM_InitStruct.TIM_Prescaler = BSP_TIM3_PRESCALER - 1; TIM_InitStruct.TIM_Pulse_Level_1 = TIM_HIGH; TIM_InitStruct.TIM_Pulse_Length_1 = BSP_TIM3_PERIOD - 1; TIM_Init(TIM3, &TIM_InitStruct);

/* configure the VIC for the TIM3 interrupt */ VIC_Config(TIM3_ITLine, VIC_IRQ, BSP_TICK_PRIO); VIC_ITCmd(TIM3_ITLine, ENABLE);

TIM_ITConfig(TIM3, TIM_IT_OC1, ENABLE); TIM_CounterCmd(TIM3, TIM_CLEAR); TIM_CounterCmd(TIM3, TIM_START); }

5.4 Idle Processing in vApplicationIdleHook() FreeRTOS.org allows to add idle processing to the application through the hook (callback) function vApplicationIdleHook(). In the DPP application, the idle hook is used to output the software trace data to the host (see the next section). The callback can also be used to place the MCU in a low- power sleep mode to conserve power.

NOTE: The STR912F device used in the STR912-SK board supports the Idle and Stop low- power modes. However, when the device is placed in one of these modes, the JTAG debugger stops working and it’s extremely difficult to get control of the device again. Therefore the tran- sition low-power mode is commented out in the vApplicationIdleHook() callback.

void vApplicationIdleHook(void) {

(1) portDISABLE_INTERRUPTS(); (2) LED_ON(16); (3) LED_OFF(16); (4) portENABLE_INTERRUPTS();

Copyright © Quantum Leaps, LLC. All Rights Reserved. 25 of 32 QDK™ FreeRTOS.org™ www.state-machine.com/freertos

(5) #ifdef Q_SPY /* use the idle cycles for QS transmission */ if ((UART0->FR & 0x80) != 0) { /* TX FIFO empty? */ uint16_t nBytes = BSP_UART_TX_FIFO; /* capacity of the TX FIFO */ uint8_t const *block; QF_INT_LOCK(dummy); block = QS_getBlock(&nBytes); /* get the data block to transfer */ QF_INT_UNLOCK(dummy); while (nBytes-- != 0) { UART0->DR = *block++; /* stick the byte to the TX FIFO */ } } #elif defined NDEBUG /* only if not debugging (Release configuraion) */ /* shut down unused peripherals to save power ...*/ (6) // SCU_EnterIdleMode(); /* Power-Management: disable the CPU clock */ // NOTE: idle or sleep mode hangs the J-TAG, it's difficult to // get control of the MCU again!!! #endif } Listing 11 vApplicationIdleHook() in the file bsp.c

(1-4) In order to “see” the activity of the idle task, the idle hook turns LED 16 on and off. These operations occur inside a critical section so that the intensity of the LED accounts only for the frequency of the calls to the idle hook, but not to preemptions by ISRs and tasks. (5) The idle hook performs QS data ouptu to the host (see the next section). (6) The ST driver function SCU_EnterIdleMode() puts the MCU in the low-power idle mode. How- ever, as described above, the idle mode interferes with the J-TAG debugger and that’s why it is commented out.

5.5 Assertion Handling Policy in Q_onAssert() As described in Chapter 6 of [PSiCC2], all QP components use internally assertions to detect errors in the way application is using the QP services. You need to define how the application reacts in case of assertion failure by providing the callback function Q_onAssert(). Typically, you would put the system in fail-safe state and try to reset. It is also a good idea to log some information as to where the assertion failed. The following code fragment shows the Q_onAssert() callback in the bsp.c file. The function simply locks all interrupts and enters a for-ever loop. This policy is only adequate for testing, but probably is not adequate for the final production release.

__arm void Q_onAssert(char const Q_ROM * const Q_ROM_VAR file, int line) { __disable_interrupt(); /* make sure that all interrupts are disabled */ LED_ON_ALL(); /* light up all LEDs */ for (;;) { /* hang here in the for-ever loop */ } }

NOTE: You must carefully design the appropriate course of action in Q_onAssert() for your final production release. Generally, Q_onAssert() should try to put the device in a fail-safe state.

Copyright © Quantum Leaps, LLC. All Rights Reserved. 26 of 32 QDK™ FreeRTOS.org™ www.state-machine.com/freertos 6 The Quantum Spy (QS) Instrumentation This QDK demonstrates how to use the Quantum Spy (QS) to generate real-time trace of a running QP application. Normally, the QS instrumentation is inactive and does not add any overhead to your application, but you can turn the instrumentation on by defining the Q_SPY macro and recom- piling the code. Quantum Spy (QS) is a software tracing facility built into all QP components and also available to the Application code. QS allows you to gain unprecedented visibility into your application by selec- tively logging almost all interesting events occurring within state machines, the framework, the kernel, and your application code. QS software tracing is minimally intrusive, offers precise time- stamping, sophisticated runtime filtering of events, and good data compression (see Chapter 11 in PSiCC2 [PSiCC2]). QS can be configured to send the real-time data out of the serial port of the target device. On the STR912F MCU, QS uses the built-in USART0 to send the trace data out (see Figure 1), so the QSPY host application can conveniently receive the trace data on the host PC. The complete implementa- tion of the QS software-tracing output consists of several steps, which are all summarized in Listing 12 (file bsp.c).

(1) #ifdef Q_SPY (2) static uint32_t l_currTime32; (3) static uint16_t l_prevTime16; (4) enum AppRecords { /* application-specific trace records */ PHILO_STAT = QS_USER }; #endif

/*------*/ #ifdef Q_SPY (5) uint8_t QS_onStartup(void const *arg) { (6) static uint8_t qsBuf[BSP_QS_BUF_SIZE]; /* buffer for Quantum Spy */ GPIO_InitTypeDef GPIO_InitStruct; UART_InitTypeDef UART_InitStruct;

(7) QS_initBuf(qsBuf, sizeof(qsBuf));

/* configure the UART0 for QSPY output ... */ (8) SCU_APBPeriphClockConfig(__UART0, ENABLE); /* enable clock for UART0 */ (9) SCU_APBPeriphClockConfig(__GPIO3, ENABLE); /* enable clock for GPIO3 */

(10) SCU_APBPeriphReset(__UART0, DISABLE); /* remove UART0 from reset */ (11) SCU_APBPeriphReset(__GPIO3, DISABLE); /* remove GPIO3 from reset */

/* configure UART0_TX pin GPIO3.4 ... */ (12) GPIO_DeInit(GPIO3); GPIO_InitStruct.GPIO_Pin = GPIO_Pin_4; GPIO_InitStruct.GPIO_Direction = GPIO_PinOutput; GPIO_InitStruct.GPIO_Type = GPIO_Type_PushPull; GPIO_InitStruct.GPIO_IPConnected = GPIO_IPConnected_Disable; GPIO_InitStruct.GPIO_Alternate = GPIO_OutputAlt3; GPIO_Init(GPIO3, &GPIO_InitStruct);

/* configure UART0... */ (13) UART_DeInit(UART0); /* force UART0 registers to reset values */ UART_InitStruct.UART_WordLength = UART_WordLength_8D; UART_InitStruct.UART_StopBits = UART_StopBits_1; UART_InitStruct.UART_Parity = UART_Parity_No; UART_InitStruct.UART_BaudRate = BSP_QS_BAUD_RATE; UART_InitStruct.UART_HardwareFlowControl = UART_HardwareFlowControl_None;

Copyright © Quantum Leaps, LLC. All Rights Reserved. 27 of 32 QDK™ FreeRTOS.org™ www.state-machine.com/freertos

UART_InitStruct.UART_Mode = UART_Mode_Tx; UART_InitStruct.UART_FIFO = UART_FIFO_Enable; UART_InitStruct.UART_TxFIFOLevel = UART_FIFOLevel_1_8; UART_InitStruct.UART_RxFIFOLevel = UART_FIFOLevel_1_8; UART_Init(UART0, &UART_InitStruct); /* initialize UART0 */ UART_Cmd(UART0, ENABLE); /* enable UART0 */

/* setup the QS filters... */ (14) QS_FILTER_ON(QS_ALL_RECORDS); QS_FILTER_OFF(...); ...

(15) return (uint8_t)1; /* indicate successfull QS initialization */ } /*...... */ (16) void QS_onCleanup(void) { } /*...... */ /* NOTE: getTime is invoked within a critical section (inetrrupts disabled) */ (17) uint32_t QS_onGetTime(void) { (18) uint16_t currTime16 = (uint16_t)TIM3->CNTR; (19) l_currTime32 += (currTime16 - l_prevTime16) & 0xFFFF; (20) l_prevTime16 = currTime16; (21) return l_currTime32; } /*...... */ (22) void QS_onFlush(void) { uint16_t nBytes = BSP_UART_TX_FIFO; /* the capacity of the UART TX FIFO */ uint8_t const *block; (23) while ((block = QS_getBlock(&nBytes)) != (uint8_t *)0) { (24) while ((UART0->FR & 0x80) == 0) { /* TX FIFO not empty? */ } /* keep waiting... */ (25) while (nBytes-- != 0) { UART0->DR = *block++; /* stick the byte to the TX FIFO */ } nBytes = BSP_UART_TX_FIFO; /* for the next time around */ } } #endif /* Q_SPY */ /*------*/ Listing 12 QS implementation to send data out of the UART0 of the STR912 device.

(1) The QS instrumentation is enabled only when the macro Q_SPY is defined (2) The static l_currTime32 variable is used to hold the 32-bit-wide timestamp. (3) The static l_prevTime16 variable is used to hold the last 16-bit-wide reading of the free-running 16-bit Timer 3 (the same used to generate the system clock tick interrupt). (4) This enumeration defines application-specific QS trace record(s), to demonstrate how to use them. (5) You need to define the QS_onStartup() callback to initialize the QS software tracing. (6) You should adjust the QS buffer size (in bytes) to your particular application (7) You always need to call QS_initBuf() from QS_onStartup() to initialize the trace buffer. (8-9) The clock to the UART0 peripheral is enabled. Also, the clock to the GPIO3 peripheral is en- abled. GPIO3 controls the UART0 transmit and receive pins. (10-11) The UART0 and GPIO3 peripherals are removed from reset.

Copyright © Quantum Leaps, LLC. All Rights Reserved. 28 of 32 QDK™ FreeRTOS.org™ www.state-machine.com/freertos

(12) The transmit pin GPIO3.4 is configured as output, alternative function 3 (UART0 Tx) using the ST driver library interface. (13) The UART0 is not properly configured using the ST driver library interface. (14) The QS filters are setup (see Chapter 11 in [PSiCC2] as well as “QP Reference Manual” online). (15) The QS_onStartup() callback returns 1, meaning that the QS initialization was successful. (16) The QS_onCleanup() callback is empty for MSP430 (the application never exits). (17-21) The QS_onGetTime() callback provides a fine-granularity timestamp. The timestamp is dis- cussed in the next section. (22) The QS_onFlush() callback flushes the QS trace buffer to the host. Typically, the function busy- waits for the transfer to complete. It is only used in the initialization phase for sending the QS dictionary records to the host. (23) The implementation of QS for STR912 uses the block-oriented QS-buffer interface QS_getBlock(), which provides up to 16 bytes to fill the FIFO of the UART (see Chapter 11 in [PSiCC2]). (24) The QS_onFlush() callback busy-waits in-line until the transmit buffer is empty. (25) The data byte is inserted into the UART0 data register.

6.1 QS Time Stamp Callback QS_onGetTime() The platform-specific QS port must provide function QS_onGetTime() (Listing 12(17-21)) that re- turns the current time stamp in 32-bit resolution. To provide such a fine-granularity time stamp, the BSP uses the free running Timer 3, which is the same timer already used for generation of the system clock-tick interrupt.

NOTE: The QS_onGetTime() callback is always called with interrupts locked.

Figure 6 shows how the Timer 3 Count Register (TIM3->CNTR) reading is extended to 32 bits. The drawing below shows a free running Timer 3 Count Register (TIM3->CNTR) that counts up from 0 to 0xFFFF and rolls over to 0. If the system tick rate is faster than the rollover rate then you could ‘oversample’ this free-running timer by reading it in the clock tick ISR, as shown in Listing 10(5). The 32-bit variable l_currTime32 contains the sum of the 16-bit deltas from each readout of the free running Timer 3 Count Register. Because of unsigned 16-bit arithmetic used in Listing 12(19), even a ‘small’ current value minus a ‘large’ previous value still results in the proper delta. Note that QS_onGetTime() can actually be called at just about any time and thus, also needs to update l_currTime32 before it returns the current value.

Copyright © Quantum Leaps, LLC. All Rights Reserved. 29 of 32 QDK™ FreeRTOS.org™ www.state-machine.com/freertos

count 32-bit time stamp returned from QS_onGetTime()

System clock tick period

TIM3->CNTR Register

0xFFFF

0x0000 System time clock-tick

Figure 6 Using the Timer 3 Count Register to provide 32-bit QS time stamp.

6.2 Invoking the QSpy Host Application The QSPY host application receives the QS trace data, parses it and displays on the host work- station (currently Windows or ). For the configuration options chosen in this port, you invoke the QSPY host application as follows (please refer to the QSPY section in the “QP Reference Man- ual”):

qspy –c COM1 –b 115200

Copyright © Quantum Leaps, LLC. All Rights Reserved. 30 of 32 QDK™ FreeRTOS.org™ www.state-machine.com/freertos

7 Related Documents and References

Document Location

[Samek 08] “Practical UML Statecharts in Available from most online book retailers, such as C/C++, Second Edition: Event Driven Pro- amazon.com. See also: http://www.quantum- gramming for Embedded Systems”, Miro leaps.com/writings/psicc2.htm Samek, Newnes, 2008

[QP/C 08] “QP/C Reference Manual”, Quan- http://www.quantum-leaps.com/doxygen/qpc/ tum Leaps, LLC, 2008

[FeeRTOS.org 08] FreeRTOS online documen- http://www.freertos.org/ tation.

[QP/C++ 08] “QP/C++ Reference Manual”, http://www.quantum-leaps.com/doxygen/qpcpp/ Quantum Leaps, LLC, 2008

[QL AN-Directory 07] “Application Note: QP http://www.quantum- Directory Structure”, Quantum Leaps, LLC, leaps.com/doc/AN_QP_Directory_Structure.pdf 2007

[QL AN-DPP 08] “Application Note: Dining http://www.quantum-leaps.com/doc/AN_DPP.pdf Philosophers Application”, Quantum Leaps, LLC, 2008

“STR91xFA ARM9®-based www.st.com/stonline/products/- family”, ST Microelectronics, April 2008 literature/rm/13742.pdf

“ARM IAR C/C++ Compiler: Reference The PDF version of this document is included in Guide”, IAR Systems the IAR Embedded Workbench for ARM.

Copyright © Quantum Leaps, LLC. All Rights Reserved. 31 of 32 QDK™ FreeRTOS.org™ www.state-machine.com/freertos

8 Contact Information

Quantum Leaps, LLC “Practical UML 103 Cobble Ridge Drive Statecharts in C/C++, Chapel Hill, NC 27516 Second Edition: Event USA Driven Programming for Embedded Sys- +1 866 450 LEAP (toll free, USA only) tems”, by Miro Samek, +1 919 869-2998 (FAX) Newnes, 2008 e-mail: [email protected] WEB : http://www.quantum-leaps.com http://www.state-machine.com

FreeRTOS.org ARM Ltd. WEB : www.FreeRTOS.org 110 Fulbourn Road Cambridge CB1 9NJ England

Tel: (44) 01223 400400 Fax: (44) 01223 400410 WEB : www.ARM.com

Copyright © Quantum Leaps, LLC. All Rights Reserved. 32 of 32