Professional Documents
Culture Documents
org
QDK FreeRTOS.org
Document Revision B September 2008
Table of Contents
1 1.1 1.2 Introduction.................................................................................................... 1 Whats Included in the QDK-FreeRTOS? ............................................................... 2 Licensing QDK-MSP430-IAR................................................................................ 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.1 3.2 3.3 3.4 3.5 The Modified FreeRTOS.org Port STR9x_IAR................................................... 9 Augmented FreeRTOS.org Interrupt Processing ..................................................... 9 General Limitations of FreeRTOS.org Ports.......................................................... 10 Changes in the portmacro.h file ......................................................................... 10 Changes in the port.c File................................................................................ 13 Demo Application for the Updated FreeRTOS.org Port........................................... 14
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 Thread .................................................. 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.1 5.2 5.3 5.4 5.5 The Example QP Application.......................................................................... 23 The main() Function ........................................................................................ 23 ISRs ............................................................................................................. 24 Starting Interrupts in vStartInterrupts()............................................................. 25 Idle Processing in vApplicationIdleHook() ........................................................... 25 Assertion Handling Policy in Q_onAssert() .......................................................... 26 The Quantum Spy (QS) Instrumentation....................................................... 27 QS Time Stamp Callback QS_onGetTime().......................................................... 29 Invoking the QSpy Host Application ................................................................... 30 Related Documents and References .............................................................. 31 Contact Information...................................................................................... 32
6 6.1 6.2 7 8
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 below (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. J-Link USB Debugger QS trace data (UART0) LED/Peripheral Jumpers
USB to PC
External power
Figure 1 IAR STR912-SK evaluation board with the J-Link JTAG pod.
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 FreeRTOS.org.
For more information, please visit the licensing section of our website at: www.quantumleaps.com/licensing.
2 of 32
2 Getting Started
This section describes how to install, build, and use QDK-FreeRTOS. This information is intentionally 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 experimenting with this QDK.
2.1 Installation
The QDK code is distributed in a ZIP archive (qdkc_freertos_<ver>.zip, where <ver> stands for a specific QDK-FreeRTOS version, such as 4.0.01). You should uncompress the archive into the same directory into which youve installed all the standard QP components. The installation directory you choose will be referred henceforth as QP Root Directory (<qp>). 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 Structure [QL AN-Directory 07]): doc\ | +-AN_DPP.pdf +-QDK_FreeRTOS.pdf - Documentation for this QDK - Application Note Dining Philosophers Application - This QDK Programmers Manual FreeRTOS.org root directory FreeRTOS.org demo applicaitons Modified ARM9_STR91X_IAR demo application the main() function of the application FreeRTOS configuration IAR workspace to build the application other files in the application FreeRTOS.org source files FreeRTOS.org ports ports for the IAR toolset ports for the STR9x MCUs (!! MODIFIED from the original !!) port-specific code (!! MODIFIED from the original !!) port-specific macros (!! MODIFIED from the original !!) context-switch support (unchanged) vPortStartFirstTask and vPortYieldProcessor (unchanged)
FreeRTOS/ | +-Demo/ | +-ARM9_STR91X_IAR_light/ | | +-main.c | | +-FreeRTOSConfig.h | | +-RTOSDemo.eww | | +-. . . | +-Source/ | +-portable/ | | +-IAR/ | | | +-STR9x/ | | | | +-port.c | | | | +-portmacro.c | | | | +-ISR_Support.h | | | | +-portasm.s79 <qp>/ | +-include/ | +-qassert.h | +-qep.h | +-qf.h | +-qequeue.h | +-qmpool.h | +-qpset.h | +-ports/ | +-arm/ | | +-freertos/ | | | +-iar/ | | | | +-dbg/
- QP root directory (qpcpp for QP/C++) QP public include files Quantum Assertions platform-independent public include QEP platform-independent public include QF platform-independent public include native QF event queue include native QF memory pool include native QF priority set include QP ports ARM port FreeRTOS.org ports IAR compiler QP libraries for Debug configuration 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 highlighted elements are included the files included in QDK-FreeRTOS.
4 of 32
QDK FreeRTOS.org www.state-machine.com/freertos The make process should produce the QP libraries in the location: <qp>\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 youve installed the IAR toolset in a different directory. You also need to adjust the symbol RTOS_INCDIR if youve 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: <qp>\ports\arm\freertos\iar\spy\. You choose the build configuration by providing a target to the make_ARM966E-S.bat utility. The default target is dbg. Other targets are rel, and spy respectively. The following table summarizes the targets accepted by make.bat. Software Version Debug (default) Release Spy Build command make_ARM966E-S make_ARM966E-S rel make_ARM966E-S spy
Table 1 Make targets for the debug, release, and spy software versions
5 of 32
Use the drop-down list to select one of the build configurations: Debug, Release, or Spy
LED 16
LED 15
LED 14
LED 13
LED 12
LED 11
LED 10
LED 9
LED 8
LED 7
LED 6
LED 5
LED 4
LED 3
LED 2
LED 1
6 of 32
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 application 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 (vApplicationIdleHook()).
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 <qp>\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:
timestamp
8 of 32
/* clear the interrupt source... */ Call one of FreeRTOS services allowed in ISRs: vTaskIncrementTick(); vTaskResumeFromISR(); xQueueSendFromISR(); xQueueSendToFrontFromISR(); xQueueSendToBackFromISR(); xQueueReceiveFromISR(); xSemaphoreGiveFromISR(); Call one of QP services allowed in ISRs: QF_tick(); QF_publish(); QActive_postFIFO(); QACtive_postLIFO(); Q_NEW(); QEQueue_get(); QEQueue_postFIFO(); QEQueue_postLIFO(); portISR_EXIT(); } /* always inform FreeRTOS about exiting an ISR */
(5)
(6)
Listing 2 Augmented interrupt processing for FreeRTOS.org (1) The ISR is typically entered with interrupts locked in hardware (some CPUs, such as ARM Cortex-M3 dont lock interrupts, but this is an exception). In particular, ARM7/ARM9 processors enter 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 performance 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 configuration is selected. NOTE: Calling postISR_EXIT() at the beginning of every ISR is necessary for correct performance of the QP framework.
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
} \ 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 <FreeRTOS>\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 portENTER_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 described 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 section, as the ISR return restores the CPU status before the interrupt. This step is the exact reverse 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 asynchronously 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 replaces 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).
QDK FreeRTOS.org www.state-machine.com/freertos Listing 4 Calling vStartInterrupts() from the updated port.c source file.
/* handle the FreeRTOS.org system clock tick */ /* always inform FreeRTOS about exiting an ISR */
NOTE: The ARM9_STR91X_IAR_light demo uses the modified FreeRTOSConfig.h header file, as described in Section 4.1.
14 of 32
0 ( 2 )
/* Set the following definitions to 1 to include the API function, or zero to exclude the API function. */ #define #define #define #define #define #define #define #define INCLUDE_vTaskPrioritySet INCLUDE_uxTaskPriorityGet INCLUDE_vTaskDelete INCLUDE_vTaskCleanUpResources INCLUDE_vTaskSuspend INCLUDE_vTaskDelayUntil INCLUDE_vTaskDelay INCLUDE_xTaskGetCurrentTaskHandle 1 1 1 0 1 1 1 1
Listing 5 FreeRTOSConfig.h header file located in <qp>\ports\arm\freertos\iar\ directory. (1) QP can work with either preemptive or cooperative kernel. In this QDK FreeRTOS.org is configured 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.
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 standard <stdint.h> 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 automatically to any FreeRTOS.org port. However, the FreeRTOS.org documentation is not clear about the semantics of portCHAR, portSHORT, and portLONG, so its not clear if they correspond to the standard types int8_t, int16_t, and int32_t, respectively.
/* 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 (3) /* #define QF_INT_KEY_TYPE */ (4) #define QF_INT_LOCK(dummy) (5) #define QF_INT_UNLOCK(dummy) (6) #include "FreeRTOS.h" (7) #include "task.h" (8) #include "queue.h" (9) (10) (11) (12) #include #include #include #include "qep_port.h" "qmpool.h" "qequeue.h" "qf.h" 63 /* FreeRTOS critical section operations */ portENTER_CRITICAL() portEXIT_CRITICAL() /* FreeRTOS.org master include file */ /* FreeRTOS.org task management */ /* FreeRTOS.org queue management */ /* QEP port /* FreeRTOS uses native QF memory-pool /* native QF event queue for deferring events /* 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.
17 of 32
(1)
(2) (3)
} /*..........................................................................*/ 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; Q_REQUIRE((qSto == (QEvent const **)0) && (qLen > 0) && (stkSto == (void *)0) && (stkSize > 0)); me->eQueue = xQueueCreate(qLen, sizeof(QEvent*)); /* create event queue */ 18 of 32
QDK FreeRTOS.org www.state-machine.com/freertos (16) (17) (18) (19) Q_ASSERT(me->eQueue != (xQueueHandle)0); /* FreeRTOS queue created me->prio = prio; /* save the QF priority QF_add_(me); /* make QF aware of this active object QF_ACTIVE_INIT_(&me->super, ie); /* execute initial transition QS_FLUSH(); (20) (21) */ */ */ */
(22)
freeRTOS_prio = tskIDLE_PRIORITY + me->prio; /* FreeRTOS task priority /* create the FreeRTOS.org task for the AO 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 Q_ASSERT(err == pdPASS); /* FreeRTOS.org task created
(23) (24)
(25) (26)
} /*..........................................................................*/ void QActive_stop(QActive *me) { me->running = (uint8_t)0; /* stop the thread loop */ vQueueDelete(me->eQueue); /* cleanup the queue */ } /*..........................................................................*/ void QActive_postFIFO(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_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_() */ */ */ */ */ */
(27) (28)
(29) (30)
} /*..........................................................................*/ 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 */ */ */ */ 19 of 32
QF_INT_UNLOCK_(); if (ulInterruptNesting == (unsigned portLONG)0) { /* task level? */ err = xQueueSendToBack(me->eQueue, &e, (portTickType)0); } else { /* must be ISR level */ portBASE_TYPE xHigherPriorityTaskWoken; err = xQueueSendToBackFromISR(me->eQueue, &e, &xHigherPriorityTaskWoken); } Q_ASSERT(err == pdPASS);
QDK FreeRTOS.org www.state-machine.com/freertos QS_EQC_(0); QS_EQC_(0); QS_END_NOLOCK_() /* number of free entries (unknown) */ /* min number of free entries (unknown) */
} /*..........................................................................*/ 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 vTaskStartScheduler(). (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 expected 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 (default) 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 event loop. (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. 20 of 32 */ */ */ */ */
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);
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) (17) (18) (19) (20) The queue creation must be successful, otherwise the application cannot continue. The active objects priority is set. The active object is registered with the QF framework. The active objects state machine is initialized. 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 urgency. 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 dynamic 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 context (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 ulInterruptNesting 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 supposed 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 indicates indefinite waiting on an empty event queue. The Q_ALLEGE() macro surrounding the FreeRTOS call asserts that the receive operation executed successfully.
22 of 32
/* 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(); Table_ctor(); BSP_init(); QF_init(); /* instantiate all Philosopher active objects */ /* instantiate the Table active object */ /* initialize the Board Support Package */ /* 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(); } 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 dont need to allocate storage for event queues and stacks of active object tasks, because FreeRTOS.org allocates this storage internally. /* run the QF application (never returns) */
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) (4) TIM3->OC1R += BSP_TIM3_PERIOD - 1; TIM3->SR &= ~TIM_IT_OC1; /* set the output compare register */ /* 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) (7) (8) } vTaskIncrementTick(); QF_tick(); portISR_EXIT(); /* handle the FreeRTOS.org system clock tick */ /* handle the QF system clock tick */ /* 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 extending 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 (timers) in the QP application. (8) As described in Section 3.1, every ISR must end with calling the macro portISR_EXIT().
SCU_APBPeriphClockConfig(__TIM23, ENABLE); TIM_DeInit(TIM3); TIM_StructInit(&TIM_InitStruct); TIM_InitStruct.TIM_Mode TIM_InitStruct.TIM_OC1_Modes TIM_InitStruct.TIM_Clock_Source TIM_InitStruct.TIM_Clock_Edge TIM_InitStruct.TIM_Prescaler TIM_InitStruct.TIM_Pulse_Level_1 TIM_InitStruct.TIM_Pulse_Length_1 TIM_Init(TIM3, &TIM_InitStruct); = = = = = = = TIM_OCM_CHANNEL_1; TIM_TIMING; TIM_CLK_APB; TIM_CLK_EDGE_RISING; BSP_TIM3_PRESCALER - 1; TIM_HIGH; BSP_TIM3_PERIOD - 1;
/* 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);
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. However, as described above, the idle mode interferes with the J-TAG debugger and thats why it is commented out. */ */ */ */ */ */ */
26 of 32
/*--------------------------------------------------------------------------*/ #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) (8) (9) (10) (11) (12) QS_initBuf(qsBuf, sizeof(qsBuf)); /* configure the UART0 for QSPY output ... */ SCU_APBPeriphClockConfig(__UART0, ENABLE); /* enable clock for UART0 */ SCU_APBPeriphClockConfig(__GPIO3, ENABLE); /* enable clock for GPIO3 */ SCU_APBPeriphReset(__UART0, DISABLE); SCU_APBPeriphReset(__GPIO3, DISABLE); /* remove UART0 from reset */ /* remove GPIO3 from reset */
/* configure UART0_TX pin GPIO3.4 ... */ 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... */ 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; 27 of 32
(13)
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 */ (14) QS_FILTER_ON(QS_ALL_RECORDS); QS_FILTER_OFF(...); ... /* setup the QS filters... */
(15) (16)
return (uint8_t)1; /* indicate successfull QS initialization */ } /*..........................................................................*/ void QS_onCleanup(void) { } /*..........................................................................*/ /* NOTE: getTime is invoked within a critical section (inetrrupts disabled) */ uint32_t QS_onGetTime(void) { uint16_t currTime16 = (uint16_t)TIM3->CNTR; l_currTime32 += (currTime16 - l_prevTime16) & 0xFFFF; l_prevTime16 = currTime16; return l_currTime32; } /*..........................................................................*/ void QS_onFlush(void) { uint16_t nBytes = BSP_UART_TX_FIFO; /* the capacity of the UART TX FIFO */ uint8_t const *block; while ((block = QS_getBlock(&nBytes)) != (uint8_t *)0) { while ((UART0->FR & 0x80) == 0) { /* TX FIFO not empty? */ } /* keep waiting... */ 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 enabled. GPIO3 controls the UART0 transmit and receive pins. (10-11) The UART0 and GPIO3 peripherals are removed from reset.
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 discussed in the next section. (22) The QS_onFlush() callback flushes the QS trace buffer to the host. Typically, the function busywaits 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.
29 of 32
0x0000
System clock-tick
time
Figure 6 Using the Timer 3 Count Register to provide 32-bit QS time stamp.
30 of 32
www.st.com/stonline/products/literature/rm/13742.pdf The PDF version of this document is included in the IAR Embedded Workbench for ARM.
31 of 32
8 Contact Information
Quantum Leaps, LLC 103 Cobble Ridge Drive Chapel Hill, NC 27516 USA +1 866 450 LEAP (toll free, USA only) +1 919 869-2998 (FAX) e-mail: info@quantum-leaps.com WEB : http://www.quantum-leaps.com http://www.state-machine.com Practical UML Statecharts in C/C++, Second Edition: Event Driven Programming for Embedded Systems, by Miro Samek, Newnes, 2008
ARM Ltd. 110 Fulbourn Road Cambridge CB1 9NJ England Tel: (44) 01223 400400 Fax: (44) 01223 400410 WEB : www.ARM.com
32 of 32