Symmetric Multiprocessing (SMP) with FreeRTOS
SMP support in the FreeRTOS Kernel enables one instance of the FreeRTOS kernel to schedule tasks across multiple identical processor cores.
The core architectures must be identical and share the same memory.
On this page:
Getting Started with FreeRTOS and SMP
The simplest way to get started is to use one of the following pre-configured
example projects:
Modifying an Application to use FreeRTOS SMP Functionality
The FreeRTOS API remains substantially the same between single core and SMP versions except for
these additions. Therefore, an application written for the FreeRTOS single
core version should compile with the SMP version with minimal to no effort. However,
there may be some functional issues, as application design assumptions true
for single core applications may no longer be true for multi-core applications.
One such common assumption is that a lower priority task cannot run while
a higher priority task is running. While this was true on a single core,
it is no longer true for multi-cores, as multiple tasks can be running
simultaneously. If the application relies on relative task priorities to provide
mutual exclusion, it may observe unexpected results in a multi-core environment.
The application writer has couple of options to address this:
- The best option is to update the application so that it does not rely on task priorities
and uses synchronization primitives instead.
- Another option is to pin all the tasks which must not be running
simultaneously to one core using the
vTaskCoreAffinitySet
API.
- Another option is to define
configRUN_MULTIPLE_PRIORITIES
to 0
which
ensures that multiple tasks will run simultaneously only if they have the same
priority. Note that this may result in under utilization and put some cores to
idle when they could be used to run other low priority tasks.
One other common assumption is that ISRs cannot run simultaneously with each other or with other tasks.
This is no longer true in a multi-core environment and the application writer needs to ensure proper
mutual exclusion while accessing data shared between tasks and ISRs. The macros
taskENTER_CRITICAL_FROM_ISR()
and taskEXIT_CRITICAL_FROM_ISR()
can be used in
ISRs and the macros taskENTER_CRITICAL()
and taskEXIT_CRITICAL()
can be used
in tasks to provide such mutual exclusion.
SMP Specific APIs
These additional APIs are available to the FreeRTOS-SMP kernel:
xTaskCreateAffinitySet
BaseType_t xTaskCreateAffinitySet( TaskFunction_t pxTaskCode,
const char * const pcName,
const configSTACK_DEPTH_TYPE usStackDepth,
void * const pvParameters,
UBaseType_t uxPriority,
UBaseType_t uxCoreAffinityMask,
TaskHandle_t * const pxCreatedTask );
This function is an extension of xTaskCreate and is used to
create a new task with an affinity mask and add it to the list of tasks that are ready to run.
configUSE_CORE_AFFINITY
must be defined as 1 for this function to be available.
Parameters:
uxCoreAffinityMask
- A bitwise value that indicates the cores on which the task
can run. Cores are numbered from 0 to (configNUMBER_OF_CORES - 1
). For example, to
ensure that a task can run on core 0 and core 1, set uxCoreAffinityMask
to 0x03.
xTaskCreateStaticAffinitySet
TaskHandle_t xTaskCreateStaticAffinitySet( TaskFunction_t pxTaskCode,
const char * const pcName,
const configSTACK_DEPTH_TYPE uxStackDepth,
void * const pvParameters,
UBaseType_t uxPriority,
StackType_t * const puxStackBuffer,
StaticTask_t * const pxTaskBuffer,
UBaseType_t uxCoreAffinityMask );
This function is an extension of xTaskCreateStatic
and is used to create a new task with affinity mask and add it to the list of tasks that are ready to run.
configUSE_CORE_AFFINITY
must be defined as 1 for this function to be available.
Parameters:
uxCoreAffinityMask
- A bitwise value that indicates the cores on which the task
can run. Cores are numbered from 0 to (configNUMBER_OF_CORES - 1
). For example, to
ensure that a task can run on core 0 and core 1, set uxCoreAffinityMask
to 0x03.
xTaskCreateRestrictedAffinitySet
BaseType_t xTaskCreateRestrictedAffinitySet( const TaskParameters_t * const pxTaskDefinition,
UBaseType_t uxCoreAffinityMask,
TaskHandle_t * pxCreatedTask );
This function is an extension of
xTaskCreateRestricted and is used to create
a Memory Protection Unit (MPU) restricted task with affinity mask and add it to the list of tasks
that are ready to run. configUSE_CORE_AFFINITY
must be defined as 1 for this function
to be available.
Parameters:
uxCoreAffinityMask
- A bitwise value that indicates the cores on which the task
can run. Cores are numbered from 0 to (configNUMBER_OF_CORES - 1
). For example, to
ensure that a task can run on core 0 and core 1, set uxCoreAffinityMask
to 0x03.
xTaskCreateRestrictedStaticAffinitySet
BaseType_t xTaskCreateRestrictedStaticAffinitySet( const TaskParameters_t * const pxTaskDefinition,
UBaseType_t uxCoreAffinityMask,
TaskHandle_t * pxCreatedTask );
This function is an extension of
xTaskCreateRestrictedStatic
and is used to create a Memory Protection Unit (MPU) restricted task with affinity mask and add it to the list
of tasks that are ready to run. configUSE_CORE_AFFINITY
must be defined as 1 for this function to be
available.
Parameters:
uxCoreAffinityMask
- A bitwise value that indicates the cores on which the task
can run. Cores are numbered from 0 to (configNUMBER_OF_CORES - 1
). For example, to
ensure that a task can run on core 0 and core 1, set uxCoreAffinityMask
to 0x03.
vTaskCoreAffinitySet
void vTaskCoreAffinitySet( const TaskHandle_t xTask, UBaseType_t uxCoreAffinityMask );
configUSE_CORE_AFFINITY
must be defined as 1
for this function to be available.
Sets the core affinity mask for a task, i.e. the cores on which a task can run.
Parameters:
xTask
- The handle of the task that the core affinity mask is for. Passing NULL
will set the core affinity mask for the calling task.
uxCoreAffinityMask
- A bitwise value that indicates the cores on which the task
can run. Cores are numbered from 0
to
(configNUMBER_OF_CORES - 1
). For example, to ensure that a task
can run on core 0
and core 1
, set uxCoreAffinityMask
to 0x03
.
Example Usage:
void vAFunction( void )
{
TaskHandle_t xHandle;
UBaseType_t uxCoreAffinityMask;
xTaskCreate( vTaskCode, "NAME", STACK_SIZE, NULL, tskIDLE_PRIORITY, &( xHandle ) );
uxCoreAffinityMask = ( ( 1 << 0 ) | ( 1 << 2 ) );
vTaskCoreAffinitySet( xHandle, uxCoreAffinityMask );
}
vTaskCoreAffinityGet
UBaseType_t vTaskCoreAffinityGet( const TaskHandle_t xTask );
configUSE_CORE_AFFINITY
must be defined as 1
for this function to be available.
Gets the core affinity mask for a task, i.e. the cores on which a task can run.
Parameters:
Returns:
The core affinity mask, which is a bitwise value that indicates the cores on
which a task can run. Cores are numbered from 0
to (configNUMBER_OF_CORES - 1
).
For example, if a task can run on core 0
and core 1
, the core affinity mask
is 0x03
.
Example Usage:
TaskHandle_t xNetworkingTaskHandle;
void vAFunction( void )
{
TaskHandle_t xHandle;
UBaseType_t uxNetworkingCoreAffinityMask;
xTaskCreate( vTaskCode, "NAME", STACK_SIZE, NULL, tskIDLE_PRIORITY, &( xHandle ) );
uxNetworkingCoreAffinityMask = vTaskCoreAffinityGet( xNetworkingTaskHandle );
if( ( uxNetworkingCoreAffinityMask & ( 1 << 0 ) ) != 0 )
{
vTaskCoreAffinitySet( xHandle, ( 1 << 1 ) );
}
else
{
vTaskCoreAffinitySet( xHandle, ( 1 << 0 ) );
}
}
vTaskPreemptionDisable
void vTaskPreemptionDisable( const TaskHandle_t xTask );
configUSE_TASK_PREEMPTION_DISABLE
must be defined as 1
for this function to be available.
Disables preemption for a task.
Parameters:
Example Usage:
void vTaskCode( void *pvParameters )
{
( void ) pvParameters;
for( ;; )
{
vTaskPreemptionDisable( NULL );
vTaskPreemptionEnable( NULL );
}
}
vTaskPreemptionEnable
void vTaskPreemptionEnable( const TaskHandle_t xTask );
configUSE_TASK_PREEMPTION_DISABLE
must be defined as 1
for this function to be available.
Enables preemption for a task.
Parameters:
Example Usage:
void vTaskCode( void *pvParameters )
{
( void ) pvParameters;
for( ;; )
{
vTaskPreemptionDisable( NULL );
vTaskPreemptionEnable( NULL );
}
}
SMP Specific Hook and Callback Functions
Passive Idle Hook Function
The FreeRTOS SMP kernel has two types of Idle tasks:
- Idle Task - There is the standard Idle task used in single core FreeRTOS applications.
- Passive Idle Tasks - There are (
configNUMBER_OF_CORES - 1
) passive Idle tasks which
are run on idle cores and which do nothing.
The passive idle tasks can optionally call an application-defined hook
(or callback) function - the passive idle hook. The passive idle tasks run at
the very lowest priority, so such an idle hook function will only run
when there are no tasks of higher priority that are able to run.
The passive idle hook will only get called if configUSE_PASSIVE_IDLE_HOOK
is
set to 1
within FreeRTOSConfig.h
. When this is set, the application must
provide the hook function with the following prototype:
void vApplicationPassiveIdleHook( void );
The passive idle hook is called repeatedly by the passive idle tasks as
long as any one of them is running. It is paramount that the passive idle hook
function does not call any API functions that could cause it to block.
Passive Idle Tasks Memory Callback Function
If configSUPPORT_STATIC_ALLOCATION
is set to 1 then the application writer needs to provide
vApplicationGetPassiveIdleTaskMemory
to provide the memory for use by (configNUMBER_OF_CORES - 1
)
passive Idle tasks. An example is provided below:
void vApplicationGetPassiveIdleTaskMemory( StaticTask_t **ppxIdleTaskTCBBuffer,
StackType_t **ppxIdleTaskStackBuffer,
uint32_t *pulIdleTaskStackSize
BaseType_t xPassiveIdleTaskIndex )
{
static StaticTask_t xIdleTaskTCBs[ configNUMBER_OF_CORES - 1 ];
static StackType_t uxIdleTaskStacks[ configNUMBER_OF_CORES - 1 ][ configMINIMAL_STACK_SIZE ];
*ppxIdleTaskTCBBuffer = &( xIdleTaskTCBs[ xPassiveIdleTaskIndex ] );
*ppxIdleTaskStackBuffer = &( uxIdleTaskStacks[ xPassiveIdleTaskIndex ][ 0 ] );
*pulIdleTaskStackSize = configMINIMAL_STACK_SIZE;
}
Alternatively, the application writer can set configKERNEL_PROVIDED_STATIC_MEMORY
to 1 in their
FreeRTOSConfig.h file to use the kernel provided default implementations of this callback.
SMP Specific Configuration Options
These additional configuration options are available to the FreeRTOS-SMP Kernel:
configNUMBER_OF_CORES
Sets the number of available processor cores.
configRUN_MULTIPLE_PRIORITIES
In a single core FreeRTOS application, a lower priority task will never run if
there is a higher priority task that is able to run. In an SMP FreeRTOS application
the RTOS kernel will run as many tasks as there are cores available - so it is possible
that a lower priority task will run on one core at the same time as a higher priority task
runs on another core. That can cause a problem if your application or library was
written for a single core environment, and so makes assumptions about the order in
which tasks execute. Therefore configRUN_MULTIPLE_PRIORITIES is provided to
control this behavior.
If configRUN_MULTIPLE_PRIORITIES
is defined as 0
, multiple tasks
may run simultaneously only if they have equal priority - maintaining the paradigm of
a lower priority task never running if there is a higher priority task that is able to run.
If configRUN_MULTIPLE_PRIORITIES
is defined as 1
, multiple tasks
with different priorities may run simultaneously - so a higher and lower priority task may run
on different cores at the same time.
configUSE_CORE_AFFINITY
Allows the application writer to control which cores a task can run on.
If configUSE_CORE_AFFINITY
is defined as 1
, vTaskCoreAffinitySet
can be used to control which cores a task can run on, and vTaskCoreAffinityGet
can
be used to query which cores a task can run on. If configUSE_CORE_AFFINITY
is 0
then the FreeRTOS scheduler is free to run any task on any available core.
configUSE_TASK_PREEMPTION_DISABLE
In a single core FreeRTOS application, the FreeRTOS scheduler can be configured to
be either pre-emptive or co-operative— see the definition of configUSE_PREEMPTION.
In an SMP FreeRTOS application, if configUSE_TASK_PREEMPTION_DISABLE
is defined as 1
,
then individual tasks can be set to either pre-emptive or co-operative mode using the vTaskPreemptionDisable
and vTaskPreemptionEnable
API functions.
configUSE_PASSIVE_IDLE_HOOK
A passive idle task hook is a function that is called during each cycle of a passive idle task. The application writer
needs to do the following to create a passive idle task hook:
- Set configUSE_PASSIVE_IDLE_HOOK to 1 in FreeRTOSConfig.h.
- Define a function that has the following name and prototype:
void vApplicationPassiveIdleHook( void );
The application writer can use the passive idle task hook to add background functionality without the overhead of
a separate task.
If configUSE_PASSIVE_IDLE_HOOK
is left undefined, it will default to 0.
configTIMER_SERVICE_TASK_CORE_AFFINITY
configTIMER_SERVICE_TASK_CORE_AFFINITY
allows the application writer to set the core affinity of the RTOS
Daemon/Timer Service task on SMP systems.
If configTIMER_SERVICE_TASK_CORE_AFFINITY
is left undefined, it will default to tskNO_AFFINITY.
Copyright (C) Amazon Web Services, Inc. or its affiliates. All rights reserved.