diff --git a/components/freertos/esp_additions/freertos_tasks_c_additions.h b/components/freertos/esp_additions/freertos_tasks_c_additions.h index 371a967649..f08eb86264 100644 --- a/components/freertos/esp_additions/freertos_tasks_c_additions.h +++ b/components/freertos/esp_additions/freertos_tasks_c_additions.h @@ -6,6 +6,7 @@ #include "sdkconfig.h" #include "esp_assert.h" +#include "esp_heap_caps.h" #include "freertos/idf_additions.h" #if CONFIG_FREERTOS_ENABLE_TASK_SNAPSHOT #include "esp_private/freertos_debug.h" @@ -1158,3 +1159,102 @@ void * pvTaskGetCurrentTCBForCore( BaseType_t xCoreID ) #endif /* CONFIG_FREERTOS_DEBUG_OCDAWARE */ /*----------------------------------------------------------*/ + +/* ----------------------------------------------------- PSRAM ---------------------------------------------------- */ + +#if CONFIG_SPIRAM + + #if CONFIG_FREERTOS_SMP + BaseType_t prvTaskCreateDynamicAffinitySetWithCaps( TaskFunction_t pxTaskCode, + const char * const pcName, + const configSTACK_DEPTH_TYPE usStackDepth, + void * const pvParameters, + UBaseType_t uxPriority, + UBaseType_t uxCoreAffinityMask, + UBaseType_t uxStackMemoryCaps, + TaskHandle_t * const pxCreatedTask ) + #else + BaseType_t prvTaskCreateDynamicPinnedToCoreWithCaps( TaskFunction_t pxTaskCode, + const char * const pcName, + const configSTACK_DEPTH_TYPE usStackDepth, + void * const pvParameters, + UBaseType_t uxPriority, + const BaseType_t xCoreID, + UBaseType_t uxStackMemoryCaps, + TaskHandle_t * const pxCreatedTask ) + #endif /* if CONFIG_FREERTOS_SMP */ + { + TCB_t * pxNewTCB; + BaseType_t xReturn; + + StackType_t * pxStack; + + configASSERT( uxStackMemoryCaps & ( MALLOC_CAP_8BIT ) ); + configASSERT( ( uxStackMemoryCaps & MALLOC_CAP_SPIRAM ) || + ( uxStackMemoryCaps & MALLOC_CAP_INTERNAL ) ); + + /* Allocate space for the stack used by the task being created. */ + pxStack = heap_caps_malloc( ( ( ( size_t ) usStackDepth ) * sizeof( StackType_t ) ), uxStackMemoryCaps ); + + if( pxStack != NULL ) + { + /* Allocate space for the TCB. */ + pxNewTCB = ( TCB_t * ) pvPortMalloc( sizeof( TCB_t ) ); + + if( pxNewTCB != NULL ) + { + #if CONFIG_FREERTOS_USE_KERNEL_10_5_1 + { + memset( ( void * ) pxNewTCB, 0x00, sizeof( TCB_t ) ); + } + #endif /* CONFIG_FREERTOS_USE_KERNEL_10_5_1 */ + + /* Store the stack location in the TCB. */ + pxNewTCB->pxStack = pxStack; + } + else + { + /* The stack cannot be used as the TCB has not been created. Free it. */ + heap_caps_free( pxStack ); + } + } + else + { + pxNewTCB = NULL; + } + + if( pxNewTCB != NULL ) + { + #if ( tskSTATIC_AND_DYNAMIC_ALLOCATION_POSSIBLE != 0 ) + { + /* Tasks can be created statically or dynamically, so note this + * task was created dynamically in case it is later deleted. */ + pxNewTCB->ucStaticallyAllocated = tskDYNAMICALLY_ALLOCATED_STACK_AND_TCB; + } + #endif /* tskSTATIC_AND_DYNAMIC_ALLOCATION_POSSIBLE */ + + #if CONFIG_FREERTOS_SMP + prvInitialiseNewTask( pxTaskCode, pcName, ( uint32_t ) usStackDepth, pvParameters, uxPriority, pxCreatedTask, pxNewTCB, NULL ); + #if ( ( configNUM_CORES > 1 ) && ( configUSE_CORE_AFFINITY == 1 ) ) + { + /* Set the task's affinity before scheduling it */ + pxNewTCB->uxCoreAffinityMask = uxCoreAffinityMask; + } + #endif + #else + prvInitialiseNewTask( pxTaskCode, pcName, ( uint32_t ) usStackDepth, pvParameters, uxPriority, pxCreatedTask, pxNewTCB, NULL, xCoreID ); + #endif + + prvAddNewTaskToReadyList( pxNewTCB ); + xReturn = pdPASS; + } + else + { + xReturn = errCOULD_NOT_ALLOCATE_REQUIRED_MEMORY; + } + + return xReturn; + } + +#endif // SPIRAM +/*----------------------------------------------------------*/ diff --git a/components/freertos/esp_additions/include/esp_private/freertos_idf_additions_priv.h b/components/freertos/esp_additions/include/esp_private/freertos_idf_additions_priv.h index 4f38f5ec99..596b313c44 100644 --- a/components/freertos/esp_additions/include/esp_private/freertos_idf_additions_priv.h +++ b/components/freertos/esp_additions/include/esp_private/freertos_idf_additions_priv.h @@ -57,15 +57,15 @@ #define prvENTER_CRITICAL_OR_SUSPEND_ALL( x ) ( { vTaskSuspendAll(); ( void ) ( x ); } ) #define prvEXIT_CRITICAL_OR_RESUME_ALL( x ) xTaskResumeAll() - #define prvENTER_CRITICAL_OR_MASK_ISR( pxLock, uxInterruptStatus ) \ - { \ - ( uxInterruptStatus ) = portSET_INTERRUPT_MASK_FROM_ISR(); \ - ( void ) ( pxLock ); \ + #define prvENTER_CRITICAL_OR_MASK_ISR( pxLock, uxInterruptStatus ) \ + { \ + ( uxInterruptStatus ) = portSET_INTERRUPT_MASK_FROM_ISR(); \ + ( void ) ( pxLock ); \ } - #define prvEXIT_CRITICAL_OR_UNMASK_ISR( pxLock, uxInterruptStatus ) \ - { \ - portCLEAR_INTERRUPT_MASK_FROM_ISR( ( uxInterruptStatus ) ); \ - ( void ) ( pxLock ); \ + #define prvEXIT_CRITICAL_OR_UNMASK_ISR( pxLock, uxInterruptStatus ) \ + { \ + portCLEAR_INTERRUPT_MASK_FROM_ISR( ( uxInterruptStatus ) ); \ + ( void ) ( pxLock ); \ } #endif /* ( !CONFIG_FREERTOS_SMP && ( configNUM_CORES == 1 ) ) */ @@ -210,6 +210,88 @@ #endif /* INCLUDE_vTaskPrioritySet == 1 */ +#if CONFIG_SPIRAM + +/** + * Create a new task with stack having the desired heap capabilities and add it to the + * list of tasks that are ready to run. + * + * @note: This is an internal function and meant for usage by pthread only. + * @note: Tasks with stacks not having the default heap capabilities for FreeRTOS may + * not be able to run while flash cache is disabled. + * This is the case if, e.g., any task on the chip is reading or writing + * flash memory. + * + * @note: The difference between this function and xTaskCreatePinnedToCoreWithCaps() + * is the following: + * * A FreeRTOS task created by this function is deleted by normal FreeRTOS deletion functions, + * i.e., vTaskDelete(). + * * A FreeRTOS task created by this function can delete itself. + * + * This function behaves like xTaskCreateAffinitySet(), except that it allocates + * the stack from the heap with the desired heap capabilities. + * + * @param pxTaskCode Pointer to the task entry function. Tasks + * must be implemented to never return (i.e. continuous loop). + * + * @param pcName A descriptive name for the task. This is mainly used to + * facilitate debugging. Max length defined by configMAX_TASK_NAME_LEN - default + * is 16. + * + * @param usStackDepth The size of the task stack specified as the number of bytes. + * + * @param pvParameters Pointer that will be used as the parameter for the task + * being created. + * + * @param uxPriority The priority at which the task should run. Systems that + * include MPU support can optionally create tasks in a privileged (system) + * mode by setting bit portPRIVILEGE_BIT of the priority parameter. For + * example, to create a privileged task at priority 2 the uxPriority parameter + * should be set to ( 2 | portPRIVILEGE_BIT ). + * + * @param xCoreID (only IDF SMP FreeRTOS) + * The core to which the task is pinned to, or tskNO_AFFINITY if + * the task can run on any core. + * + * @param uxCoreAffinityMask (only Amazon SMP FreeRTOS) + * A bitwise value that indicates the cores on which the task can run. + * Cores are numbered from 0 to configNUM_CORES - 1. + * For example, to ensure that a task can run on core 0 and core 1, set + * uxCoreAffinityMask to 0x03. Note that only one of the cores will be used when + * using the IDF SMP version of FreeRTOS instead of Amazon FreeRTOS SMP. + * + * @param uxStackMemoryCaps The heap caps bitfield describing the memory capabilities to + * be used for stack memory. Currently, the capabilities have to include MALLOC_CAP_8BIT + * and one of the following: MALLOC_CAP_SPIRAM, MALLOC_CAP_INTERNAL. + * + * @param pxCreatedTask Used to pass back a handle by which the created task + * can be referenced. + * + * @return pdPASS if the task was successfully created and added to a ready + * list, otherwise an error code defined in the file projdefs.h + */ + #if CONFIG_FREERTOS_SMP + BaseType_t prvTaskCreateDynamicAffinitySetWithCaps( TaskFunction_t pxTaskCode, + const char * const pcName, + const configSTACK_DEPTH_TYPE usStackDepth, + void * const pvParameters, + UBaseType_t uxPriority, + UBaseType_t uxCoreAffinityMask, + UBaseType_t uxStackMemoryCaps, + TaskHandle_t * const pxCreatedTask ); + #else + BaseType_t prvTaskCreateDynamicPinnedToCoreWithCaps( TaskFunction_t pxTaskCode, + const char * const pcName, + const configSTACK_DEPTH_TYPE usStackDepth, + void * const pvParameters, + UBaseType_t uxPriority, + const BaseType_t xCoreID, + UBaseType_t uxStackMemoryCaps, + TaskHandle_t * const pxCreatedTask ); + #endif // CONFIG_FREERTOS_SMP + +#endif // CONFIG_SPIRAM + /* *INDENT-OFF* */ #ifdef __cplusplus } diff --git a/components/freertos/test_apps/freertos/kernel/tasks/test_freertos_psram.c b/components/freertos/test_apps/freertos/kernel/tasks/test_freertos_psram.c new file mode 100644 index 0000000000..93919f676c --- /dev/null +++ b/components/freertos/test_apps/freertos/kernel/tasks/test_freertos_psram.c @@ -0,0 +1,147 @@ +/* + * SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "freertos/idf_additions.h" +#include "esp_private/freertos_idf_additions_priv.h" +#include "unity.h" +#include "esp_heap_caps.h" +#include "unity_test_runner.h" +#include "memory_checks.h" + +#define UNITY_FREERTOS_PRIORITY 5 + +#if CONFIG_SPIRAM + +static void task_delete_itself(void *arg) +{ + printf("starting task\n"); + ulTaskNotifyTake(pdTRUE, portMAX_DELAY); + xTaskNotifyGive((TaskHandle_t)arg); + vTaskDelete(NULL); +} + +static void task_should_never_run(void *arg) +{ + printf("ERROR: this task should not run, aborting\n"); + abort(); +} + +static BaseType_t create_task(TaskFunction_t function, + size_t stack_size, + void *task_arg, + int core_num, + UBaseType_t heap_caps, + TaskHandle_t *task_handle) +{ +#if CONFIG_FREERTOS_SMP + UBaseType_t core_affinity_mask = (core_num == -1) ? tskNO_AFFINITY : 1 << core_num; + return prvTaskCreateDynamicAffinitySetWithCaps(function, + "self_delete", + stack_size, + task_arg, + UNITY_FREERTOS_PRIORITY + 1, + core_affinity_mask, + heap_caps, + task_handle); +#else + const BaseType_t task_core_num = (core_num == -1) ? tskNO_AFFINITY : core_num; + return prvTaskCreateDynamicPinnedToCoreWithCaps(function, + "self_delete", + stack_size, + task_arg, + UNITY_FREERTOS_PRIORITY + 1, + task_core_num, + heap_caps, + task_handle); +#endif +} + +TEST_CASE("Out of memory failure", "[freertos][psram]") +{ + TaskHandle_t task_handle = NULL; + const size_t STACK_SIZE = 0x80000000; // far larger than the virtual address space on ESP32 + UBaseType_t HEAP_CAPS = (MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT); + + BaseType_t result = create_task(task_should_never_run, + STACK_SIZE, + (void *)xTaskGetCurrentTaskHandle(), + -1, + HEAP_CAPS, + &task_handle); + TEST_ASSERT_EQUAL(errCOULD_NOT_ALLOCATE_REQUIRED_MEMORY, result); + (void)task_handle; +} + +TEST_CASE("Task with stack memory in PSRAM", "[freertos][psram]") +{ + TaskHandle_t task_handle = NULL; + const size_t STACK_SIZE = 0x80000; // value is too large for any internal RAM on current chips + UBaseType_t HEAP_CAPS = (MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT); + + BaseType_t result = create_task(task_delete_itself, + STACK_SIZE, + (void *)xTaskGetCurrentTaskHandle(), + -1, + HEAP_CAPS, + &task_handle); + TEST_ASSERT_EQUAL(pdPASS, result); + + // synchronize with the task to make sure we don't return too early, thus giving it enough time + // to delete itself and giving the idle task time to clean up memory (see delay in tearDown()). + xTaskNotifyGive(task_handle); + ulTaskNotifyTake(pdTRUE, portMAX_DELAY); +} + +#if !CONFIG_FREERTOS_UNICORE +typedef struct { + size_t recorded_core_num; + TaskHandle_t parent_handle; +} report_corenum_info_t; + +static void task_report_corenum(void *arg) +{ + report_corenum_info_t *info = (report_corenum_info_t*) arg; + info->recorded_core_num = xTaskGetAffinity(NULL); + xTaskNotifyGive(info->parent_handle); + vTaskSuspend(NULL); +} + +/** + * This test (roughly) verifies that xTaskCreateWithCapsAffinitySet() pins a task to the correct core. + */ +TEST_CASE("Task on specific core works", "[freertos][psram]") +{ + const size_t STACK_SIZE = 0x1000; + report_corenum_info_t corenum_info; + TaskHandle_t task_handle; + + for (int corenum = 0; corenum < CONFIG_SOC_CPU_CORES_NUM; corenum++) { + corenum_info.recorded_core_num = 0xFFFFFFFF; + corenum_info.parent_handle = xTaskGetCurrentTaskHandle(); + UBaseType_t HEAP_CAPS = (MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT); + BaseType_t result = create_task(task_report_corenum, + STACK_SIZE, + (void *) &(corenum_info), + corenum, + HEAP_CAPS, + &task_handle); + + TEST_ASSERT_EQUAL(pdPASS, result); + + // Wait for the created task to finish its job + ulTaskNotifyTake(pdTRUE, portMAX_DELAY); + + TEST_ASSERT_EQUAL((size_t) corenum, corenum_info.recorded_core_num); + + vTaskDelete(task_handle); + } +} +#endif // !CONFIG_FREERTOS_UNICORE + +#endif // SPIRAM