|Hint: Use the tree menu to navigate groups of related pages|
Using FreeRTOS on ARM Cortex-A9 Embedded Processors
That incorporate a Generic Interrupt Controller (GIC)
IntroductionThe information on this page is relevant to both the 32-bit ARMv7-A and 64-bit ARMv8-A RTOS ports.
Some ARM Cortex-A processors incorporate ARM’s own Generic Interrupt Controller (GIC), while others incorporate proprietary interrupt controllers. Separate web pages are provided to give instructions on using the RTOS in both scenarios. This page provides information on running the RTOS on an ARM Cortex-A embedded processor that uses the ARM GIC. See also the web page that describes running the RTOS on an ARM Cortex-A embedded processor that uses a proprietary interrupt controller.
On this page:
- Features of the FreeRTOS ARM Cortex-A kernel port
- Using the floating point unit (FPU)
- Essential information on interrupt priorities
- ARM Cortex-A specific FreeRTOSConfig.h settings
- Configuring and installing the RTOS tick interrupt
- Interrupt handling
- Installing the FreeRTOS IRQ and SWI (SVC) interrupt handlers
- Cortex-A processor modes and stacks
- Extends the use of the familiar, small, simple, deterministic, de facto standard FreeRTOS kernel beyond the microcontroller market
- Implements a full interrupt nesting model
- Allows a subset of interrupts to remain enabled even inside RTOS critical sections #1
- Includes hardware floating point support
- Uses a flat/linear memory model (the MMU is not supported).
A newly created task will not have a floating point context if:
- You are using a FreeRTOS version older than V9.0.0, or
- You are using a compiler other than GCC, or
- configUSE_TASK_FPU_SUPPORT is set to 1 in FreeRTOSConfig.h, or
- configUSE_TASK_FPU_SUPPORT is undefined.
A newly created task will already have a floating point context if:
- You are using FreeRTOS V9.0.0 or later, and
- The GCC compiler is used, and
- configUSE_TASK_FPU_SUPPORT is set to 2 in FreeRTOSConfig.h.
A task that does not have a floating point context must create a floating point context for itself by calling portTASK_USES_FLOATING_POINT() before any floating point calculations are executed. It is only necessary to call portTASK_USES_FLOATING_POINT() once per task. For example:
Important note for GCC (and possibly other compiler) users:Some GCC libraries optimise memory copy and memory set (and possibly other) functions by making use of the wide floating point registers. Therefore, by default, any task that uses functions such as memcpy(), memcmp() or memset(), or uses a FreeRTOS API function such as xQueueSend() which itself uses memcpy(), will inadvertently corrupt the floating point registers. Further, any interrupt that calls a FreeRTOS queue or semaphore function will also corrupt the floating point context as they too make use of memcpy().
To avoid this either:
Use library functions that do not use the floating point registers, and if
that is not possible provide your own implementation of memcpy(), memcmp()
and memset() to ensure the versions provided by the library are not used.
- Make use of the portTASK_USES_FLOATING_POINT() and vApplicationFPUSafeIRQHandler() functions described on this page.
Interrupts assigned a priority at or below the priority set by configMAX_API_CALL_INTERRUPT_PRIORITY can call interrupt safe FreeRTOS API function, and will nest. Interrupts safe FreeRTOS API function are those that end in “FromISR” (FreeRTOS maintains a separate interrupt API to ensure interrupt entry is as efficient and simple as possible).
Interrupts assigned a priority above the configMAX_API_CALL_INTERRUPT_PRIORITY setting will not be effected by RTOS critical sections, and will nest, but cannot call any FreeRTOS API functions.
Special Note 1: In ARM interrupt controllers, higher numeric interrupt priority values denote lower logical interrupt priorities. Therefore an interrupt priority of 5 is lower than an interrupt priority of 4. If configMAX_API_CALL_INTERRUPT_PRIORITY is set to 5, then it is correct for an interrupt handler that calls an interrupt safe API function to be assigned a priority of 5 or 6, but it is not correct for it to be assigned a priority of 4.
Special Note 2: The interrupt controller’s internal representation of priority bits can be ignored. If the interrupt controller implements 32 unique priority levels, then the only valid values for configMAX_API_CALL_INTERRUPT_PRIORITY are 1 to 30. Likewise, if the interrupt controller implements 16 unique priority levels then valid values are 1 to 14.
Special Note 3: Interrupt controllers that can sub-divide interrupt priority bits into preemption priorities and sub-priorities should have all bits configured as preemption priority. In the ARM generic interrupt controller (GIC) this means the interrupt controller’s binary point register must be set to 0.
Must be set to the base address of the ARM Generic Interrupt Controller (GIC)
The offset from configINTERRUPT_CONTROLLER_BASE_ADDRESS at which the GIC’s CPU interface starts. Typically this will be 0x1000.
The number of unique priorities that can be specified in the GIC.
See the interrupt priorities section.
Note: If there is an official demo for the Cortex-A9 processor you are using then the FreeRTOSConfig.h file provided with the demo will already contain the correct settings.
The macro configSETUP_TICK_INTERRUPT() is called by the RTOS kernel port layer. configSETUP_TICK_INTERRUPT() must be #defined in FreeRTOSConfig.h to configure a peripheral to generate a periodic interrupt at the frequency set by the configTICK_RATE_HZ FreeRTOSConfig.h setting. FreeRTOS_Tick_Handler() must then be installed as the interrupt’s handler function. For example:
Interrupt entry, nesting, and exit code is provided by the RTOS kernel port layer. After an interrupt has been entered the RTOS port layer executes a callback function that must be provided by the application writer. Individual interrupt service routines can then be called by the callback function. An example is provided below.
The callback function can be called either vApplicationIRQHandler(), or if you are using GCC and FreeRTOS V9.0.0 or later, vApplicationFPUSafeIRQHandler().
If the application writer provides a callback function called vApplicationIRQHandler() then floating point registers will not be saved on entry to the interrupt, and the interrupt must not use any floating point instructions or registers. If the application writer instead provides a callback function called vApplicationFPUSafeIRQHandler() then floating point registers will be saved on entry to each (potentially nested) interrupt, which will use more stack space and slow down interrupt entry, but allow interrupt handlers to use FPU registers. See the “Using the floating point unit (FPU) in interrupts” section above.
The callback function (vApplicationIRQHandler() or vApplicationFPUSafeIRQHandler()) is called with interrupts disabled, but can (and in most cases should) enable interrupts. Below is an example implementation:
FreeRTOS_SWI_Handler() must be installed as the Cortex-A’s SWI (SVC) handler.
If it is not possible to edit the interrupt vector code then map the FreeRTOS handlers to the required handler names using #defines in FreeRTOSConfig.h. For example, if the installed handlers are called IRQ_Handler() and SWI_Handler() respectively, then the FreeRTOS handlers can be mapped to these names by adding the following two lines to FreeRTOSConfig.h.
It is not necessary to allocate a stack to User/System mode unless main() is called from System mode (main() must not be called from User mode). If a stack is allocated to User/System mode it will not be used after the RTOS kernel has been started.
The RTOS stack overflow detection functionality only detects overflows in task stacks, not IRQ or Supervisor stacks.
- The RTOS executes at EL3 (Exception Level 3), and uses the EL3 stack.
- RTOS tasks execute at EL3 and use the EL1 stack.