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:
A newly created task will not have a floating point context if:
A newly created task will already have a floating point context if:
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:
void vATaskFunction( void *pvParameters ) { double x, y; /* This task is going to use floating point operations. Therefore it calls portTASK_USES_FLOATING_POINT() once on task entry, before entering the loop that implements its functionality. From this point on the task has a floating point context. */ portTASK_USES_FLOATING_POINT(); /* Enter the loop that implements the task's function. */ for( ;; ) { /* portTASK_USES_FLOATING_POINT() has already been called, so it is safe to use the floating point x and y variables here... */ x = [whatever]; y = [whatever]; } } Calling portTASK_USES_FLOATING_POINT() before any floating point calculations are performed
To avoid this either:
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:
/* Implement a function in a C file to generate a periodic interrupt at the required frequency. */ void vSetupTickInterrupt( void ) { /* FreeRTOS_Tick_Handler() is itself defined in the RTOS port layer. An extern declaration is required to allow the following code to compile. */ extern void FreeRTOS_Tick_Handler( void ); /* Assume TIMER1_configure() configures a hypothetical timer peripheral called TIMER1 to generate a periodic interrupt with a frequency set by its parameter. */ TIMER1_configure( configTICK_RATE_HZ ); /* Next assume Install_Interrupt() installs the function passed as its second parameter as the handler for the peripheral passed as its first parameter. */ Install_Interrupt( TIMER1, FreeRTOS_Tick_Handler ); } Defining a function that configures a timer to generate a periodic tick
/* Given the function definition above, add the following line to FreeRTOSConfig.h. */ #define configSETUP_TICK_INTERRUPT() vSetupTickInterrupt() #defining configSETUP_TICK_INTERRUPT() to the function that generates the periodic tick
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(). /* The two callback function options. In both cases ulICCIAR is passed as the value of the interrupt controller's ICCIAR (interrupt acknowledge register). */ void vApplicationIRQHandler( uint32_t ulICCIAR ); void vApplicationFPUSafeIRQHandler( uint32_t ulICCIAR ); The handler function called after interrupt entry housekeeping tasks have already been performed
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:
/* vApplicationIRQHandler() is just a normal C function. */ void vApplicationIRQHandler( uint32_t ulICCIAR ) { uint32_t ulInterruptID; /* In the 64-bit Cortex-A RTOS port it is necessary to clear the source of the interrupt BEFORE interrupts are re-enabled. */ ClearInterruptSource(); /* Re-enable interrupts. */ __asm volatile( "CPSIE I" ); /* The ID of the interrupt can be obtained by bitwise ANDing the ICCIAR value with 0x3FF. */ ulInterruptID = ulICCIAR & 0x3FFUL; /* On the assumption that handlers for each interrupt are stored in an array called InterruptHandlerFunctionTable, use the interrupt ID to index to and call the relevant handler function. */ InterruptHandlerFunctionTable[ ulInterruptID ](); } An example implementation of vApplicationIRQHandler()
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.
#define FreeRTOS_IRQ_Handler IRQ_Handler #define FreeRTOS_SWI_Handler SWI_Handler Mapping the FreeRTOS interrupt handler names to alternative handler names
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.