feat(freertos): Add config to move additional functions into Flash

This commit adds a new Kconfig option, viz.,
CONFIG_FREERTOS_PLACE_ISR_FUNCTIONS_INTO_FLASH, which places
additional FreeRTOS functions, such as those which can be called from
and ISR context, into Flash memory. This feature utilizes the Flash auto
suspend/resume feature of the Flash chip.
This commit is contained in:
Sudeep Mohanty
2025-03-06 13:00:00 +01:00
parent 578c8b8507
commit 71a79ac0b1
7 changed files with 205 additions and 14 deletions

View File

@@ -563,6 +563,15 @@ menu "FreeRTOS"
When enabled the selected Non-ISR FreeRTOS functions will be placed into Flash memory instead of IRAM.
This saves up to 8KB of IRAM depending on which functions are used.
config FREERTOS_PLACE_ISR_FUNCTIONS_INTO_FLASH
bool "Place FreeRTOS functions called from ISR context into Flash"
depends on SPI_FLASH_AUTO_SUSPEND && FREERTOS_PLACE_FUNCTIONS_INTO_FLASH
default n
help
When enabled additional FreeRTOS functions which maybe called from an ISR context are
also placed in Flash, thus freeing up more IRAM.
This option is only applicable when the SPI_FLASH_AUTO_SUSPEND feature is supported.
config FREERTOS_CHECK_PORT_CRITICAL_COMPLIANCE
# Todo: Check if we still need this (IDF-4986)
bool "Tests compliance with Vanilla FreeRTOS port*_CRITICAL calls"
@@ -650,4 +659,9 @@ menu "FreeRTOS"
default 1 if FREERTOS_UNICORE
default 2 if !FREERTOS_UNICORE
config FREERTOS_IN_IRAM
# Invisible option enabled by default to place all freertos functions in internal RAM
# Todo: See linker.lf (IDF-12695)
bool
default y
endmenu # FreeRTOS

View File

@@ -7,12 +7,31 @@
# - CONFIG_FREERTOS_PLACE_FUNCTIONS_INTO_FLASH:
# - Place functions in flash if they are never called from an ISR context (directly or indirectly).
# - Some functions that are called often (such as critical sections) are placed in internal RAM for speed.
# - CONFIG_FREERTOS_PLACE_ISR_FUNCTIONS_INTO_FLASH:
# - Place additional functions such as the FromISR() variants in flash as well.
#
# TODO: In IDF 6.0, evaluate the possibility of (IDF-12695):
# 1. Default behavior: All FreeRTOS functions should be placed in flash by default, except for:
# - FromISR() functions
# - Performance-critical functions, such as critical section APIs and code related to context switching
# 2. Deprecate the FREERTOS_PLACE_FUNCTIONS_INTO_FLASH Kconfig option and unhide the FREERTOS_IN_IRAM Kconfig
# option. Present FREERTOS_IN_IRAM config option to users as a performance optimization feature, with the
# trade-off of reduced internal RAM availability. This option would be disabled by default.
# 3. FREERTOS_IN_IRAM config option should be user-configurable only if SPI_FLASH_AUTO_SUSPEND is supported.
# If not supported, it should be enabled by default and follow the placement rules stated above.
# 4. If SPI_FLASH_AUTO_SUSPEND is supported, place additional FreeRTOS functions - including FromISR()
# variants, in Flash. Control this behavior using the FREERTOS_PLACE_ISR_FUNCTIONS_INTO_FLASH
# config option.
# ----------------------------------------------------------------------------------------------------------------------
[mapping:freertos_idf]
archive: libfreertos.a
entries:
* (noflash_text) # Default all FreeRTOS functions to IRAM
if FREERTOS_IN_IRAM = y:
* (noflash_text) # All FreeRTOS functions to IRAM
#else
# TODO: Enable in IDF 6.0
# * (default) # All FreeRTOS functions to Flash
if FREERTOS_PLACE_FUNCTIONS_INTO_FLASH = y:
# --------------------------------------------------------------------------------------------------------------
# event_groups.c
@@ -25,26 +44,41 @@ entries:
event_groups:xEventGroupClearBits (default)
event_groups:xEventGroupSetBits (default)
event_groups:vEventGroupDelete (default)
event_groups: xEventGroupGetStaticBuffer (default)
event_groups:vEventGroupSetBitsCallback (default)
event_groups:vEventGroupClearBitsCallback (default)
event_groups:prvTestWaitCondition (default)
if FREERTOS_USE_TRACE_FACILITY = y:
event_groups: uxEventGroupGetNumber (default)
event_groups: vEventGroupSetNumber (default)
if FREERTOS_PLACE_ISR_FUNCTIONS_INTO_FLASH = y:
event_groups: xEventGroupClearBitsFromISR (default)
event_groups: xEventGroupGetBitsFromISR (default)
event_groups: xEventGroupSetBitsFromISR (default)
# --------------------------------------------------------------------------------------------------------------
# list.c
# - List/List Item initialization functions are never called from ISR
# - vListInsert is never called from an ISR context
# - Remaining List insertion/removal functions can be called from an ISR context and hence place them in flash
# only when CONFIG_FREERTOS_PLACE_ISR_FUNCTIONS_INTO_FLASH is enabled
# --------------------------------------------------------------------------------------------------------------
list:vListInitialise (default)
list:vListInitialiseItem (default)
list:vListInsert (default)
if FREERTOS_PLACE_ISR_FUNCTIONS_INTO_FLASH = y:
list:vListInsertEnd (default)
list:uxListRemove (default)
# --------------------------------------------------------------------------------------------------------------
# queue.c
# - Keep all ...FromISR() functions (and their prv... calls) in internal RAM
# - All other functions can be moved to flash
# - Queue lock related functions are only used in single core builds
# - If CONFIG_FREERTOS_PLACE_ISR_FUNCTIONS_INTO_FLASH is enabled, place all FromISR() functions and their
# dependents in flash as well
# --------------------------------------------------------------------------------------------------------------
queue:xQueueGenericReset (default)
queue:xQueueGenericCreateStatic (default)
queue:xQueueGenericGetStaticBuffers (default)
queue:xQueueGenericCreate (default)
queue:prvInitialiseNewQueue (default)
queue:prvInitialiseMutex (default)
@@ -80,14 +114,31 @@ entries:
queue:xQueueAddToSet (default)
queue:xQueueRemoveFromSet (default)
queue:xQueueSelectFromSet (default)
if FREERTOS_PLACE_ISR_FUNCTIONS_INTO_FLASH = y:
queue:xQueueGetMutexHolderFromISR (default)
queue:xQueueGenericSendFromISR (default)
queue:prvCopyDataToQueue (default)
queue:prvNotifyQueueSetContainer (default)
queue:xQueueGiveFromISR (default)
queue:xQueueReceiveFromISR (default)
queue:prvCopyDataFromQueue (default)
queue:xQueuePeekFromISR (default)
queue:uxQueueMessagesWaitingFromISR (default)
queue:xQueueIsQueueEmptyFromISR (default)
queue:xQueueIsQueueFullFromISR (default)
queue:xQueueSelectFromSetFromISR (default)
# --------------------------------------------------------------------------------------------------------------
# stream_buffer.c
# - If CONFIG_FREERTOS_PLACE_ISR_FUNCTIONS_INTO_FLASH is enabled, place all FromISR() functions and their
# dependents in flash as well
# --------------------------------------------------------------------------------------------------------------
stream_buffer:xStreamBufferGenericCreate (default)
stream_buffer:xStreamBufferGenericCreateStatic (default)
stream_buffer:xStreamBufferGetStaticBuffers (default)
stream_buffer:vStreamBufferDelete (default)
stream_buffer:xStreamBufferReset (default)
stream_buffer:xStreamBufferSetTriggerLevel (default)
stream_buffer:xStreamBufferSpacesAvailable (default)
stream_buffer:xStreamBufferBytesAvailable (default)
stream_buffer:xStreamBufferSend (default)
stream_buffer:xStreamBufferReceive (default)
@@ -101,14 +152,31 @@ entries:
stream_buffer:uxStreamBufferGetStreamBufferNumber (default)
stream_buffer:vStreamBufferSetStreamBufferNumber (default)
stream_buffer:ucStreamBufferGetStreamBufferType (default)
if FREERTOS_PLACE_ISR_FUNCTIONS_INTO_FLASH = y:
stream_buffer:xStreamBufferSendFromISR (default)
stream_buffer:prvWriteMessageToBuffer (default)
stream_buffer:xStreamBufferReceiveFromISR (default)
stream_buffer:prvReadMessageFromBuffer (default)
stream_buffer:xStreamBufferSendCompletedFromISR (default)
stream_buffer:xStreamBufferReceiveCompletedFromISR (default)
stream_buffer:prvBytesInBuffer (default)
# --------------------------------------------------------------------------------------------------------------
# tasks.c
# - Tickless idle functions (i.e., step tick) are left in internal RAM for speed
# - The following functions are always kept in internal RAM as they are frequently called during context switches
# - xTaskIncrementTick
# - prvSelectHighestPriorityTaskSMP
# - vTaskSwitchContext
# - Tickless idle functions (i.e., step tick) are left in internal RAM for speed unless
# CONFIG_FREERTOS_PLACE_ISR_FUNCTIONS_INTO_FLASH is enabled
# - Place all functions that are called from an ISR context into Flash if
# CONFIG_FREERTOS_PLACE_ISR_FUNCTIONS_INTO_FLASH is enabled
# - The following functions are called when the cache is disabled, thus they are excluded from the list below
# (i.e., called after "spi_flash_disable_interrupts_caches_and_other_cpu()" is called).
# - "xTaskGetSchedulerState"
# - "xTaskGetTickCount"
# --------------------------------------------------------------------------------------------------------------
tasks:xTaskCreateRestrictedStatic (default)
tasks:xTaskCreateRestricted (default)
tasks:prvInitialiseNewTask (default)
tasks:prvAddNewTaskToReadyList (default)
tasks:vTaskDelete (default)
@@ -119,22 +187,26 @@ entries:
tasks:vTaskPrioritySet (default)
tasks:vTaskSuspend (default)
tasks:vTaskResume (default)
tasks:prvCreateIdleTasks (default)
tasks:vTaskStartScheduler (default)
tasks:vTaskEndScheduler (default)
tasks:vTaskSuspendAll (default)
if CONFIG_FREERTOS_USE_TICKLESS_IDLE = y:
if FREERTOS_USE_TICKLESS_IDLE = y:
tasks:prvGetExpectedIdleTime (default)
tasks:eTaskConfirmSleepModeStatus (default)
tasks:xTaskResumeAll (default)
tasks:uxTaskGetNumberOfTasks (default)
tasks:pcTaskGetName (default)
tasks:prvSearchForNameWithinSingleList (default)
tasks:xTaskGetHandle (default)
tasks:xTaskGetStaticBuffers (default)
tasks:xTaskGetIdleTaskHandle (default)
tasks:xTaskCatchUpTicks (default)
tasks:xTaskAbortDelay (default)
# IDF-6410 Application tags not supported yet
#tasks:vTaskSetApplicationTaskTag (default)
#tasks:xTaskGetApplicationTaskTag (default)
#tasks:xTaskCallApplicationTaskHook (default)
if FREERTOS_USE_APPLICATION_TASK_TAG = y:
tasks:vTaskSetApplicationTaskTag (default)
tasks:xTaskGetApplicationTaskTag (default)
tasks:xTaskCallApplicationTaskHook (default)
tasks:vTaskPlaceOnEventList (default)
tasks:vTaskPlaceOnUnorderedEventList (default)
tasks:vTaskPlaceOnEventListRestricted (default)
@@ -179,20 +251,36 @@ entries:
tasks:vTaskSetTaskNumber (default)
tasks:vTaskGetInfo (default)
tasks:prvListTasksWithinSingleList (default)
if FREERTOS_PLACE_ISR_FUNCTIONS_INTO_FLASH = y:
tasks:prvIsYieldRequiredSMP (default)
tasks:prvCheckTaskCanBeScheduledSMP (default)
tasks:uxTaskPriorityGetFromISR (default)
tasks:prvTaskIsTaskSuspended (default)
tasks:xTaskResumeFromISR (default)
tasks:xTaskGetTickCountFromISR (default)
tasks:xTaskGetApplicationTaskTagFromISR (default)
tasks:xTaskRemoveFromEventList (default)
tasks:prvResetNextTaskUnblockTime (default)
tasks:xTaskGenericNotifyFromISR (default)
tasks:vTaskGenericNotifyGiveFromISR (default)
if FREERTOS_USE_TICKLESS_IDLE = y:
tasks:vTaskStepTick (default)
# --------------------------------------------------------------------------------------------------------------
# timers.c
# - xTimerGenericCommand() is used for ISR calls as well. Thus leave it (and its dependents) in internal RAM
# unless CONFIG_FREERTOS_PLACE_ISR_FUNCTIONS_INTO_FLASH is enabled
# --------------------------------------------------------------------------------------------------------------
timers:xTimerCreateTimerTask (default)
timers:xTimerCreate (default)
timers:xTimerCreateStatic (default)
timers:prvInitialiseNewTimer (default)
timers:xTimerGenericCommand (default)
timers:xTimerGetTimerDaemonTaskHandle (default)
timers:xTimerGetPeriod (default)
timers:vTimerSetReloadMode (default)
timers:xTimerGetReloadMode (default)
timers:uxTimerGetReloadMode (default)
timers:xTimerGetExpiryTime (default)
timers:xTimerGetStaticBuffer (default)
timers:pcTimerGetName (default)
timers:prvReloadTimer (default)
timers:prvProcessExpiredTimer (default)
@@ -211,30 +299,78 @@ entries:
if FREERTOS_USE_TRACE_FACILITY = y:
timers:uxTimerGetTimerNumber (default)
timers:vTimerSetTimerNumber (default)
if FREERTOS_PLACE_ISR_FUNCTIONS_INTO_FLASH = y:
timers:xTimerGenericCommand (default)
timers:xTimerPendFunctionCallFromISR (default)
# --------------------------------------------------------------------------------------------------------------
# portable/xtensa/port.c
# - Most functions are called from an ISR context, except for scheduler/task init/deinit functions
# - MPU/Coproc currently only exists on ESP32/S3
# - Most functions are called from an ISR context, except for scheduler/task init/deinit functions. Functions
# called from ISR context are placed in flash only when CONFIG_FREERTOS_PLACE_ISR_FUNCTIONS_INTO_FLASH
# is enabled
# - Critical sections functions are always placed in internal RAM for better performance
# - The following functions are also placed in internal RAM as they are called from vTaskSwitchContext, which is
# also always placed in internal RAM
# - vApplicationStackOverflowHook
# - vPortSetStackWatchpoint
# --------------------------------------------------------------------------------------------------------------
if IDF_TARGET_ARCH_XTENSA = y:
port:xPortStartScheduler (default)
port:vPortEndScheduler (default)
if FREERTOS_TASK_FUNCTION_WRAPPER = y:
port:vPortTaskWrapper (default)
if SOC_CPU_COPROC_NUM > 0:
port:uxInitialiseStackCPSA (default)
port:uxInitialiseStackTLS (default)
port:uxInitialiseStackFrame (default)
port:pxPortInitialiseStack (default)
port:xPortGetTickRateHz (default)
if FREERTOS_TLSP_DELETION_CALLBACKS = y:
port:vPortTLSPointersDelCb (default)
if IDF_TARGET_ESP32 = y || IDF_TARGET_ESP32S3 = y :
if SOC_CPU_COPROC_NUM > 0:
port:vPortCleanUpCoprocArea (default)
port:vPortTCBPreDeleteHook (default)
if FREERTOS_PLACE_ISR_FUNCTIONS_INTO_FLASH = y:
port:xPortInIsrContext (default)
port:vPortAssertIfInISR (default)
port:xPortInterruptedFromISRContext (default)
port:vPortYieldOtherCore (default)
# --------------------------------------------------------------------------------------------------------------
# portable/riscv/port.c
# - Most functions are called from an ISR context, except for scheduler/task init/deinit functions
# - Most functions are called from an ISR context, except for scheduler/task init/deinit functions. Functions
# called from ISR context are placed in flash only when CONFIG_FREERTOS_PLACE_ISR_FUNCTIONS_INTO_FLASH
# is enabled
# - Critical sections functions are always placed in internal RAM for better performance
# - The following functions are also placed in internal RAM as they are called from vTaskSwitchContext, which is
# also always placed in internal RAM
# - vApplicationStackOverflowHook
# - vPortSetStackWatchpoint
# - vPortCoprocUsedInISR is directly called from RISC-V assembly code with a direct branch instruction which
# may be too far when placed in Flash. Hence, it is always placed in internal RAM
# --------------------------------------------------------------------------------------------------------------
if IDF_TARGET_ARCH_RISCV = y:
port:xPortStartScheduler (default)
port:vPortEndScheduler (default)
port:uxInitialiseStackTLS (default)
if FREERTOS_TASK_FUNCTION_WRAPPER = y:
port:vPortTaskWrapper (default)
if SOC_CPU_COPROC_NUM > 0:
port:pxRetrieveCoprocSaveAreaFromStackPointer (default)
port:uxInitialiseCoprocSaveArea (default)
port:vPortCleanUpCoprocArea (default)
port:pxPortGetCoprocArea (default)
port:pxPortUpdateCoprocOwner (default)
if FREERTOS_UNICORE = n:
port:vPortTaskPinToCore (default)
port:uxInitialiseStackFrame (default)
port:pxPortInitialiseStack (default)
port:vPortYield (default)
port:xPortGetTickRateHz (default)
if FREERTOS_TLSP_DELETION_CALLBACKS = y:
port:vPortTLSPointersDelCb (default)
port:vPortTCBPreDeleteHook (default)
if FREERTOS_PLACE_ISR_FUNCTIONS_INTO_FLASH = y:
port:xPortInIsrContext (default)
port:vPortAssertIfInISR (default)
port:xPortInterruptedFromISRContext (default)
port:vPortYieldFromISR (default)
port:vPortYieldOtherCore (default)

View File

@@ -10,11 +10,14 @@ entries:
# - Default: Place all functions in internal RAM.
# - CONFIG_FREERTOS_PLACE_FUNCTIONS_INTO_FLASH: Place functions in flash if they are never called from an ISR
# context (directly or indirectly).
# - xTaskIncrementTickOtherCores is always placed in internal RAM for better performance
# Placement Rules (Task Snapshot):
# - Default: Place all functions in internal RAM.
# - CONFIG_FREERTOS_PLACE_SNAPSHOT_FUNS_INTO_FLASH: Place functions in flash
# - vTaskGetSnapshot is omitted on purpose as it is used to by the Task Watchdog (TWDT) interrupt handler, we want
# to always keep it in IRAM
# to always keep it in IRAM unless CONFIG_FREERTOS_PLACE_ISR_FUNCTIONS_INTO_FLASH is enabled in which case
# we can place it in flash.
#
# ------------------------------------------------------------------------------------------------------------------
if FREERTOS_PLACE_FUNCTIONS_INTO_FLASH = y:
# Kernel Control
@@ -37,11 +40,21 @@ entries:
# TLSP Deletion Callbacks
if FREERTOS_TLSP_DELETION_CALLBACKS = y:
tasks:vTaskSetThreadLocalStoragePointerAndDelCallback (default)
# Newlib re-entrancy
tasks:__getreent (default)
# Miscellaneous
tasks:pvTaskGetCurrentTCBForCore (default)
if SPIRAM = y:
# PSRAM
tasks:prvTaskCreateDynamicPinnedToCoreWithCaps (default)
# Task Snapshot
if FREERTOS_PLACE_SNAPSHOT_FUNS_INTO_FLASH = y:
tasks:pxGetTaskListByIndex (default)
tasks:xTaskGetNext (default)
tasks:uxTaskGetSnapshotAll (default)
if FREERTOS_PLACE_ISR_FUNCTIONS_INTO_FLASH = y:
tasks:vTaskGetSnapshot (default)
# ------------------------------------------------------------------------------------------------------------------
# freertos_compatibility.c
@@ -55,6 +68,12 @@ entries:
# ------------------------------------------------------------------------------------------------------------------
idf_additions (default)
# ------------------------------------------------------------------------------------------------------------------
# idf_additions_event_groups.c
# Placement Rules: Functions always in flash as they are never called from an ISR
# ------------------------------------------------------------------------------------------------------------------
idf_additions_event_groups (default)
# ------------------------------------------------------------------------------------------------------------------
# app_startup.c
# Placement Rules: Functions always in flash as they are never called from an ISR
@@ -74,6 +93,7 @@ entries:
heap_idf:vPortFree (default)
heap_idf:xPortGetFreeHeapSize (default)
heap_idf:xPortGetMinimumEverFreeHeapSize (default)
heap_idf:xPortCheckValidListMem (default)
if FREERTOS_SMP = n:
heap_idf:xPortCheckValidTCBMem (default)
heap_idf:xPortcheckValidStackMem (default)

View File

@@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2022-2024 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2022-2025 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/

View File

@@ -56,6 +56,19 @@ def test_freertos(dut: Dut) -> None:
dut.run_all_single_board_cases()
@pytest.mark.generic
@pytest.mark.flash_suspend
@idf_parametrize(
'config,target',
[
('flash_auto_suspend', 'esp32c3'),
],
indirect=['config', 'target'],
)
def test_freertos_flash_auto_suspend(dut: Dut) -> None:
dut.run_all_single_board_cases()
@pytest.mark.generic
@pytest.mark.parametrize('config', ['freertos_options'], indirect=True)
@idf_parametrize('target', ['supported_targets'], indirect=['target'])

View File

@@ -0,0 +1,7 @@
# Enable Flash auto suspend/resume feature
CONFIG_SPI_FLASH_AUTO_SUSPEND=y
CONFIG_SPI_FLASH_FORCE_ENABLE_XMC_C_SUSPEND=y
# Enable FreeRTOS in Flash feature
CONFIG_FREERTOS_PLACE_FUNCTIONS_INTO_FLASH=y
CONFIG_FREERTOS_PLACE_ISR_FUNCTIONS_INTO_FLASH=y

View File

@@ -9,6 +9,7 @@ CONFIG_COMPILER_OPTIMIZATION_SIZE=y
# Options that will enable IRAM reduction option that are not necessarily safe for all use-cases
CONFIG_RINGBUF_PLACE_ISR_FUNCTIONS_INTO_FLASH=y
CONFIG_FREERTOS_PLACE_ISR_FUNCTIONS_INTO_FLASH=y
# Options that will enable IRAM reduction option that are only usable together with flash auto-suspend
CONFIG_SPI_FLASH_AUTO_SUSPEND=y