diff --git a/components/freertos/include/esp_additions/freertos/FreeRTOSConfig.h b/components/freertos/include/esp_additions/freertos/FreeRTOSConfig.h index a01a56e9fd..90a0b6ac23 100644 --- a/components/freertos/include/esp_additions/freertos/FreeRTOSConfig.h +++ b/components/freertos/include/esp_additions/freertos/FreeRTOSConfig.h @@ -90,7 +90,6 @@ #define portNUM_PROCESSORS 1 #endif -#define configASSERT_2 0 #define portUSING_MPU_WRAPPERS 0 #define configUSE_MUTEX 1 @@ -206,7 +205,6 @@ #define configGENERATE_RUN_TIME_STATS 1 /* Used by vTaskGetRunTimeStats() */ #endif -#define configUSE_TRACE_FACILITY_2 0 #define configBENCHMARK 0 #define configUSE_16_BIT_TICKS 0 #define configIDLE_SHOULD_YIELD 0 diff --git a/components/freertos/port/port_common.c b/components/freertos/port/port_common.c index 69163739c1..5af0e378e7 100644 --- a/components/freertos/port/port_common.c +++ b/components/freertos/port/port_common.c @@ -1,16 +1,8 @@ -// Copyright 2015-2020 Espressif Systems (Shanghai) PTE LTD -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. +/* + * SPDX-FileCopyrightText: 2021 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ #include #include "FreeRTOS.h" @@ -22,8 +14,9 @@ #include "esp_task_wdt.h" #include "esp_task.h" #include "esp_private/crosscore_int.h" -#include "esp_private/startup_internal.h" +#include "esp_private/startup_internal.h" /* Required by g_spiram_ok. [refactor-todo] for g_spiram_ok */ #include "esp_log.h" +#include "soc/soc_memory_types.h" #include "soc/dport_access.h" #include "sdkconfig.h" @@ -56,7 +49,7 @@ volatile unsigned port_xSchedulerRunning[portNUM_PROCESSORS] = {0}; // // And since this should be true, we can just check for CONFIG_FREERTOS_UNICORE. #if CONFIG_FREERTOS_UNICORE != CONFIG_ESP_SYSTEM_SINGLE_CORE_MODE - #error "FreeRTOS and system configuration mismatch regarding the use of multiple cores." + #error "FreeRTOS and system configuration mismatch regarding the use of multiple cores." #endif static void main_task(void* args); @@ -70,69 +63,85 @@ extern void app_main(void); void esp_startup_start_app_common(void) { #if CONFIG_ESP_INT_WDT - esp_int_wdt_init(); - //Initialize the interrupt watch dog for CPU0. - esp_int_wdt_cpu_init(); + esp_int_wdt_init(); + //Initialize the interrupt watch dog for CPU0. + esp_int_wdt_cpu_init(); #endif - esp_crosscore_int_init(); + esp_crosscore_int_init(); #ifdef CONFIG_ESP_SYSTEM_GDBSTUB_RUNTIME esp_gdbstub_init(); #endif // CONFIG_ESP_SYSTEM_GDBSTUB_RUNTIME - portBASE_TYPE res = xTaskCreatePinnedToCore(&main_task, "main", - ESP_TASK_MAIN_STACK, NULL, - ESP_TASK_MAIN_PRIO, NULL, ESP_TASK_MAIN_CORE); - assert(res == pdTRUE); - (void)res; + portBASE_TYPE res = xTaskCreatePinnedToCore(&main_task, "main", + ESP_TASK_MAIN_STACK, NULL, + ESP_TASK_MAIN_PRIO, NULL, ESP_TASK_MAIN_CORE); + assert(res == pdTRUE); + (void)res; } static void main_task(void* args) { #if !CONFIG_FREERTOS_UNICORE - // Wait for FreeRTOS initialization to finish on APP CPU, before replacing its startup stack - while (port_xSchedulerRunning[1] == 0) { - ; - } + // Wait for FreeRTOS initialization to finish on APP CPU, before replacing its startup stack + while (port_xSchedulerRunning[1] == 0) { + ; + } #endif - // [refactor-todo] check if there is a way to move the following block to esp_system startup - heap_caps_enable_nonos_stack_heaps(); + // [refactor-todo] check if there is a way to move the following block to esp_system startup + heap_caps_enable_nonos_stack_heaps(); - // Now we have startup stack RAM available for heap, enable any DMA pool memory + // Now we have startup stack RAM available for heap, enable any DMA pool memory #if CONFIG_SPIRAM_MALLOC_RESERVE_INTERNAL - if (g_spiram_ok) { - esp_err_t r = esp_spiram_reserve_dma_pool(CONFIG_SPIRAM_MALLOC_RESERVE_INTERNAL); - if (r != ESP_OK) { - ESP_EARLY_LOGE(TAG, "Could not reserve internal/DMA pool (error 0x%x)", r); - abort(); - } - } + if (g_spiram_ok) { + esp_err_t r = esp_spiram_reserve_dma_pool(CONFIG_SPIRAM_MALLOC_RESERVE_INTERNAL); + if (r != ESP_OK) { + ESP_EARLY_LOGE(TAG, "Could not reserve internal/DMA pool (error 0x%x)", r); + abort(); + } + } #endif - //Initialize task wdt if configured to do so + //Initialize task wdt if configured to do so #ifdef CONFIG_ESP_TASK_WDT_PANIC - ESP_ERROR_CHECK(esp_task_wdt_init(CONFIG_ESP_TASK_WDT_TIMEOUT_S, true)); + ESP_ERROR_CHECK(esp_task_wdt_init(CONFIG_ESP_TASK_WDT_TIMEOUT_S, true)); #elif CONFIG_ESP_TASK_WDT - ESP_ERROR_CHECK(esp_task_wdt_init(CONFIG_ESP_TASK_WDT_TIMEOUT_S, false)); + ESP_ERROR_CHECK(esp_task_wdt_init(CONFIG_ESP_TASK_WDT_TIMEOUT_S, false)); #endif - //Add IDLE 0 to task wdt + //Add IDLE 0 to task wdt #ifdef CONFIG_ESP_TASK_WDT_CHECK_IDLE_TASK_CPU0 - TaskHandle_t idle_0 = xTaskGetIdleTaskHandleForCPU(0); - if(idle_0 != NULL){ - ESP_ERROR_CHECK(esp_task_wdt_add(idle_0)); - } + TaskHandle_t idle_0 = xTaskGetIdleTaskHandleForCPU(0); + if(idle_0 != NULL){ + ESP_ERROR_CHECK(esp_task_wdt_add(idle_0)); + } #endif - //Add IDLE 1 to task wdt + //Add IDLE 1 to task wdt #ifdef CONFIG_ESP_TASK_WDT_CHECK_IDLE_TASK_CPU1 - TaskHandle_t idle_1 = xTaskGetIdleTaskHandleForCPU(1); - if(idle_1 != NULL){ - ESP_ERROR_CHECK(esp_task_wdt_add(idle_1)); - } + TaskHandle_t idle_1 = xTaskGetIdleTaskHandleForCPU(1); + if(idle_1 != NULL){ + ESP_ERROR_CHECK(esp_task_wdt_add(idle_1)); + } #endif - app_main(); - vTaskDelete(NULL); + app_main(); + vTaskDelete(NULL); +} + +// -------------------- Heap Related ----------------------- + +bool xPortCheckValidTCBMem(const void *ptr) +{ + return esp_ptr_internal(ptr) && esp_ptr_byte_accessible(ptr); +} + +bool xPortcheckValidStackMem(const void *ptr) +{ +#ifdef CONFIG_SPIRAM_ALLOW_STACK_EXTERNAL_MEMORY + return esp_ptr_byte_accessible(ptr); +#else + return esp_ptr_internal(ptr) && esp_ptr_byte_accessible(ptr); +#endif } diff --git a/components/freertos/port/riscv/include/freertos/portmacro.h b/components/freertos/port/riscv/include/freertos/portmacro.h index cb58b42265..edfd862f79 100644 --- a/components/freertos/port/riscv/include/freertos/portmacro.h +++ b/components/freertos/port/riscv/include/freertos/portmacro.h @@ -24,89 +24,466 @@ * * 1 tab == 4 spaces! */ + #ifndef PORTMACRO_H #define PORTMACRO_H +#ifndef __ASSEMBLER__ + +#include "sdkconfig.h" +#include +#include +#include +#include +#include "soc/spinlock.h" +#include "soc/interrupt_core0_reg.h" +#include "soc/cpu.h" +#include "esp_attr.h" +#include "esp_rom_sys.h" +#include "esp_timer.h" /* required for FreeRTOS run time stats */ +#include "esp_heap_caps.h" +#include "esp_system.h" /* required by esp_get_...() functions in portable.h. [refactor-todo] Update portable.h */ +#include "esp_newlib.h" +#include "portbenchmark.h" + +/* [refactor-todo] These includes are not directly used in this file. They are kept into to prevent a breaking change. Remove these. */ +#include +#ifdef CONFIG_LEGACY_INCLUDE_COMMON_HEADERS +#include "soc/soc_memory_layout.h" +#endif + #ifdef __cplusplus extern "C" { #endif -#ifndef __ASSEMBLER__ -#include -#include -#include -#include -#include -#include "esp_timer.h" /* required for FreeRTOS run time stats */ -#include "sdkconfig.h" -#include "esp_attr.h" -#include "esp_heap_caps.h" -#ifdef CONFIG_LEGACY_INCLUDE_COMMON_HEADERS -#include "soc/soc_memory_layout.h" -#endif -#include "soc/spinlock.h" -#include "soc/interrupt_core0_reg.h" -#include "esp_rom_sys.h" -#include "soc/cpu.h" -#include "esp_system.h" -#include "esp_newlib.h" +/* --------------------------------------------------- Port Types ------------------------------------------------------ + * - Port specific types. + * - The settings in this file configure FreeRTOS correctly for the given hardware and compiler. + * - These settings should not be altered. + * - The port types must come first as they are used further down in this file + * ------------------------------------------------------------------------------------------------------------------ */ -/*----------------------------------------------------------- - * Port specific definitions. - * - * The settings in this file configure FreeRTOS correctly for the - * given hardware and compiler. - * - * These settings should not be altered. - *----------------------------------------------------------- - */ +#define portCHAR uint8_t +#define portFLOAT float +#define portDOUBLE double +#define portLONG int32_t +#define portSHORT int16_t +#define portSTACK_TYPE uint8_t +#define portBASE_TYPE int -/* Type definitions. */ -#define portCHAR uint8_t -#define portFLOAT float -#define portDOUBLE double -#define portLONG int32_t -#define portSHORT int16_t -#define portSTACK_TYPE uint8_t -#define portBASE_TYPE int -// interrupt module will mask interrupt with priority less than threshold -#define RVHAL_EXCM_LEVEL 4 +typedef portSTACK_TYPE StackType_t; +typedef portBASE_TYPE BaseType_t; +typedef unsigned portBASE_TYPE UBaseType_t; -typedef portSTACK_TYPE StackType_t; -typedef portBASE_TYPE BaseType_t; -typedef unsigned portBASE_TYPE UBaseType_t; - -#if( configUSE_16_BIT_TICKS == 1 ) - typedef uint16_t TickType_t; - #define portMAX_DELAY ( TickType_t ) 0xffff +#if (configUSE_16_BIT_TICKS == 1) +typedef uint16_t TickType_t; +#define portMAX_DELAY (TickType_t) 0xffff #else - typedef uint32_t TickType_t; - #define portMAX_DELAY ( TickType_t ) 0xffffffffUL +typedef uint32_t TickType_t; +#define portMAX_DELAY (TickType_t) 0xffffffffUL #endif -/*------------------------------------------------------*/ -/* Architecture specifics. */ -#define portSTACK_GROWTH ( -1 ) -#define portTICK_PERIOD_MS ( ( TickType_t ) (1000 / configTICK_RATE_HZ) ) -#define portBYTE_ALIGNMENT 16 -/*-----------------------------------------------------------*/ -#include "portbenchmark.h" +/* Task function macros as described on the FreeRTOS.org WEB site. */ +#define portTASK_FUNCTION_PROTO(vFunction, pvParameters) void vFunction(void *pvParameters) +#define portTASK_FUNCTION(vFunction, pvParameters) void vFunction(void *pvParameters) -static inline BaseType_t IRAM_ATTR xPortGetCoreID(void) { +// interrupt module will mask interrupt with priority less than threshold +#define RVHAL_EXCM_LEVEL 4 + + + +/* ----------------------------------------------- Port Configurations ------------------------------------------------- + * - Configurations values supplied by each port + * - Required by FreeRTOS + * ------------------------------------------------------------------------------------------------------------------ */ + +#define portCRITICAL_NESTING_IN_TCB 0 +#define portSTACK_GROWTH (-1) +#define portTICK_PERIOD_MS ((TickType_t) (1000 / configTICK_RATE_HZ)) +#define portBYTE_ALIGNMENT 16 +#define portNOP() __asm volatile (" nop ") + + + +/* ---------------------------------------------- Forward Declarations ------------------------------------------------- + * - Forward declarations of all the port functions and macros need to implement the FreeRTOS porting interface + * - These must come before definition/declaration of the FreeRTOS porting interface + * ------------------------------------------------------------------------------------------------------------------ */ + +// --------------------- Interrupts ------------------------ + +/** + * @brief Checks if the current core is in an ISR context + * + * - ISR context consist of Low/Mid priority ISR, or time tick ISR + * - High priority ISRs aren't detected here, but they normally cannot call C code, so that should not be an issue anyway. + * + * @note [refactor-todo] Check if this should be inlined + * @return + * - pdTRUE if in ISR + * - pdFALSE otherwise + */ +BaseType_t xPortInIsrContext(void); + +/** + * @brief Check if in ISR context from High priority ISRs + * + * - Called from High priority ISR + * - Checks if the previous context (before high priority interrupt) was in ISR context (meaning low/med priority) + * + * @note [refactor-todo] Check if this should be inlined + * @return + * - pdTRUE if in previous in ISR context + * - pdFALSE otherwise + */ +BaseType_t xPortInterruptedFromISRContext(void); + +/** + * @brief Disable interrupts in a nested manner + * + * - Cleaner solution allows nested interrupts disabling and restoring via local registers or stack. + * - They can be called from interrupts too. + * - WARNING Only applies to current CPU. + * + * @note [refactor-todo] Define this as portSET_INTERRUPT_MASK_FROM_ISR() instead + * @return unsigned Previous interrupt state + */ +static inline unsigned portENTER_CRITICAL_NESTED(void); + +/* ---------------------- Spinlocks ------------------------ + - Spinlocks added to match API with SMP FreeRTOS. Single core RISC-V does not need spin locks + - Because single core does not have a primitive spinlock data type, we have to implement one here + * @note [refactor-todo] Refactor critical section API so that this is no longer required + * ------------------------------------------------------ */ + +/** + * @brief Spinlock object + * Owner: + * - Set to 0 if uninitialized + * - Set to portMUX_FREE_VAL when free + * - Set to CORE_ID_REGVAL_PRO or CORE_ID_REGVAL_AP when locked + * - Any other value indicates corruption + * Count: + * - 0 if unlocked + * - Recursive count if locked + * + * @note Not a true spinlock as single core RISC-V does not have atomic compare and set instruction + * @note Keep portMUX_INITIALIZER_UNLOCKED in sync with this struct + */ +typedef struct { + uint32_t owner; + uint32_t count; +#ifdef CONFIG_FREERTOS_PORTMUX_DEBUG + const char *lastLockedFn; + int lastLockedLine; +#endif +} portMUX_TYPE; +/**< Spinlock initializer */ +#ifndef CONFIG_FREERTOS_PORTMUX_DEBUG +#define portMUX_INITIALIZER_UNLOCKED { \ + .owner = portMUX_FREE_VAL, \ + .count = 0, \ + } +#else +#define portMUX_INITIALIZER_UNLOCKED { \ + .owner = portMUX_FREE_VAL, \ + .count = 0, \ + .lastLockedFn = "(never locked)", \ + .lastLockedLine = -1 \ + } +#endif /* CONFIG_FREERTOS_PORTMUX_DEBUG */ +#define portMUX_FREE_VAL SPINLOCK_FREE /**< Spinlock is free. [refactor-todo] check if this is still required */ +#define portMUX_NO_TIMEOUT SPINLOCK_WAIT_FOREVER /**< When passed for 'timeout_cycles', spin forever if necessary. [refactor-todo] check if this is still required */ +#define portMUX_TRY_LOCK SPINLOCK_NO_WAIT /**< Try to acquire the spinlock a single time only. [refactor-todo] check if this is still required */ + +/** + * @brief Initialize a spinlock + * + * - Initializes a spinlock that is used by FreeRTOS SMP critical sections + * + * @note [refactor-todo] We can make this inline or consider making it a macro + * @param[in] mux Spinlock + */ +void vPortCPUInitializeMutex(portMUX_TYPE *mux); + +/** + * @brief Acquire a spinlock + * + * @note [refactor-todo] check if we still need this + * @note [refactor-todo] Check if this should be inlined + * @param[in] mux Spinlock + */ +void vPortCPUAcquireMutex(portMUX_TYPE *mux); + +/** + * @brief Acquire a spinlock but with a specified timeout + * + * @note [refactor-todo] Check if we still need this + * @note [refactor-todo] Check if this should be inlined + * @note [refactor-todo] Check if this function should be renamed (due to bool return type) + * @param[in] mux Spinlock + * @param[in] timeout Timeout in number of CPU cycles + * @return true Spinlock acquired + * @return false Timed out + */ +bool vPortCPUAcquireMutexTimeout(portMUX_TYPE *mux, int timeout_cycles); + +/** + * @brief Release a spinlock + * + * @note [refactor-todo] check if we still need this + * @note [refactor-todo] Check if this should be inlined + * @param[in] mux Spinlock + */ +void vPortCPUReleaseMutex(portMUX_TYPE *mux); + +/** + * @brief Wrapper for atomic compare-and-set instruction + * + * @note Isn't a real atomic CAS. + * @note [refactor-todo] check if we still need this + * @note [refactor-todo] Check if this function should be renamed (due to void return type) + * + * @param[inout] addr Pointer to target address + * @param[in] compare Compare value + * @param[inout] set Pointer to set value + */ +static inline void __attribute__((always_inline)) uxPortCompareSet(volatile uint32_t *addr, uint32_t compare, uint32_t *set); + +/** + * @brief Wrapper for atomic compare-and-set instruction in external RAM + * + * @note Isn't a real atomic CAS. + * @note [refactor-todo] check if we still need this + * @note [refactor-todo] Check if this function should be renamed (due to void return type) + * + * @param[inout] addr Pointer to target address + * @param[in] compare Compare value + * @param[inout] set Pointer to set value + */ +static inline void uxPortCompareSetExtram(volatile uint32_t *addr, uint32_t compare, uint32_t *set); + +// ------------------ Critical Sections -------------------- + +/** + * @brief Enter a critical section + * + * - Simply disable interrupts + * - Can be nested + */ +void vPortEnterCritical(void); + +/** + * @brief Exit a critical section + * + * - Reenables interrupts + * - Can be nested + */ +void vPortExitCritical(void); + +// ---------------------- Yielding ------------------------- + +/** + * @brief Set interrupt mask and return current interrupt enable register + * + * @note [refactor-todo] Check if this function should be renamed (due to int return type) + * @return int Current interrupt enable register before set + */ +int vPortSetInterruptMask(void); + +/** + * @brief Clear current interrupt mask and set given mask + * + * @param mask Interrupt mask + */ +void vPortClearInterruptMask(int mask); + +/** + * @brief Perform a context switch from a task + * + * @note [refactor-todo] The rest of ESP-IDF should call taskYield() instead + */ +void vPortYield(void); + +/** + * @brief Perform a context switch from an ISR + */ +void vPortYieldFromISR(void); + +/** + * @brief Yields the other core + * + * @note Added to be compatible with SMP API + * @note [refactor-todo] Put this into private macros as its only called from task.c and is not public API + * @param coreid ID of core to yield + */ +void vPortYieldOtherCore(BaseType_t coreid); + +/** + * @brief Checks if the current core can yield + * + * - A core cannot yield if its in an ISR or in a critical section + * + * @note [refactor-todo] See if this can be separated from port macro + * @note [refactor-todo] Check if this function should be renamed (due to bool return type) + * @return true Core can yield + * @return false Core cannot yield + */ +static inline bool IRAM_ATTR xPortCanYield(void); + +// ------------------- Hook Functions ---------------------- + +extern void esp_vApplicationIdleHook(void); +extern void esp_vApplicationTickHook(void); + +/** + * @brief Hook function called on entry to tickless idle + * + * - Implemented in pm_impl.c + * + * @param xExpectedIdleTime Expected idle time + */ +void vApplicationSleep(TickType_t xExpectedIdleTime); + +// ----------------------- System -------------------------- + +/** + * @brief Get the tick rate per second + * + * @note [refactor-todo] make this inline + * @note [refactor-todo] Check if this function should be renamed (due to uint return type) + * @return uint32_t Tick rate in Hz + */ +uint32_t xPortGetTickRateHz(void); + +/** + * @brief Set a watchpoint to watch the last 32 bytes of the stack + * + * Callback to set a watchpoint on the end of the stack. Called every context switch to change the stack watchpoint + * around. + * + * @param pxStackStart Pointer to the start of the stack + */ +void vPortSetStackWatchpoint(void *pxStackStart); + +/** + * @brief Get the current core's ID + * + * @note Added to be compatible with SMP API + * @note [refactor-todo] IDF should call a FreeRTOS like macro instead of port function directly + * @return BaseType_t Core ID + */ +static inline BaseType_t IRAM_ATTR xPortGetCoreID(void) +{ return cpu_hal_get_core_id(); } -static inline bool IRAM_ATTR xPortCanYield(void) + +/* ------------------------------------------- FreeRTOS Porting Interface ---------------------------------------------- + * - Contains all the mappings of the macros required by FreeRTOS + * - Most come after forward declare as porting macros map to declared functions + * - Maps to forward declared functions + * ------------------------------------------------------------------------------------------------------------------ */ + +// ----------------------- Memory -------------------------- + +/** + * @brief Task memory allocation macros + * + * @note Because the ROM routines don't necessarily handle a stack in external RAM correctly, we force the stack + * memory to always be internal. + * @note [refactor-todo] Update portable.h to match v10.4.3 to use new malloc prototypes + */ +#define portTcbMemoryCaps (MALLOC_CAP_INTERNAL|MALLOC_CAP_8BIT) +#define portStackMemoryCaps (MALLOC_CAP_INTERNAL|MALLOC_CAP_8BIT) +#define pvPortMallocTcbMem(size) pvPortMalloc(size) +#define pvPortMallocStackMem(size) pvPortMalloc(size) + +// --------------------- Interrupts ------------------------ + +#define portEXIT_CRITICAL_NESTED(state) do { portCLEAR_INTERRUPT_MASK_FROM_ISR(state);} while(0); +#define portDISABLE_INTERRUPTS() portSET_INTERRUPT_MASK_FROM_ISR() +#define portENABLE_INTERRUPTS() portCLEAR_INTERRUPT_MASK_FROM_ISR(1) +#define portSET_INTERRUPT_MASK_FROM_ISR() vPortSetInterruptMask() +#define portCLEAR_INTERRUPT_MASK_FROM_ISR(uxSavedStatusValue) vPortClearInterruptMask(uxSavedStatusValue) + +// ------------------ Critical Sections -------------------- + +#define portENTER_CRITICAL(mux) {(void)mux; vPortEnterCritical();} +#define portEXIT_CRITICAL(mux) {(void)mux; vPortExitCritical();} +//In single-core RISC-V, we can use the same critical section API +#define portENTER_CRITICAL_ISR(mux) portENTER_CRITICAL(mux) +#define portEXIT_CRITICAL_ISR(mux) portEXIT_CRITICAL(mux) +/* [refactor-todo] on RISC-V, both ISR and non-ISR cases result in the same call. We can redefine this macro */ +#define portENTER_CRITICAL_SAFE(mux) ({ \ + if (xPortInIsrContext()) { \ + portENTER_CRITICAL_ISR(mux); \ + } else { \ + portENTER_CRITICAL(mux); \ + } \ +}) +#define portEXIT_CRITICAL_SAFE(mux) ({ \ + if (xPortInIsrContext()) { \ + portEXIT_CRITICAL_ISR(mux); \ + } else { \ + portEXIT_CRITICAL(mux); \ + } \ +}) + +// ---------------------- Yielding ------------------------- + +#define portYIELD() vPortYield() +#define portYIELD_FROM_ISR() vPortYieldFromISR() +#define portEND_SWITCHING_ISR(xSwitchRequired) if(xSwitchRequired) vPortYield() +/* Yielding within an API call (when interrupts are off), means the yield should be delayed + until interrupts are re-enabled. + To do this, we use the "cross-core" interrupt as a trigger to yield on this core when interrupts are re-enabled.This + is the same interrupt & code path which is used to trigger a yield between CPUs, although in this case the yield is + happening on the same CPU. +*/ +#define portYIELD_WITHIN_API() portYIELD() + +// ------------------- Hook Functions ---------------------- + +#ifndef CONFIG_FREERTOS_LEGACY_HOOKS +#define vApplicationIdleHook esp_vApplicationIdleHook +#define vApplicationTickHook esp_vApplicationTickHook +#endif /* !CONFIG_FREERTOS_LEGACY_HOOKS */ +#define portSUPPRESS_TICKS_AND_SLEEP(idleTime) vApplicationSleep(idleTime) + +// ------------------- Run Time Stats ---------------------- + +#define portCONFIGURE_TIMER_FOR_RUN_TIME_STATS() +#define portGET_RUN_TIME_COUNTER_VALUE() 0 +#ifdef CONFIG_FREERTOS_RUN_TIME_STATS_USING_ESP_TIMER +/* Coarse resolution time (us) */ +#define portALT_GET_RUN_TIME_COUNTER_VALUE(x) do {x = (uint32_t)esp_timer_get_time();} while(0) +#endif + + + +/* --------------------------------------------- Inline Implementations ------------------------------------------------ + * - Implementation of inline functions of the forward declares + * - Should come after forward declare and FreeRTOS Porting interface, as implementation may use both. + * - For implementation of non-inlined functions, see port.c, port_common.c, or other assembly files + * ------------------------------------------------------------------------------------------------------------------ */ + +// --------------------- Interrupts ------------------------ + +static inline unsigned portENTER_CRITICAL_NESTED(void) { - uint32_t threshold = REG_READ(INTERRUPT_CORE0_CPU_INT_THRESH_REG); - /* when enter critical code, freertos will mask threshold to RVHAL_EXCM_LEVEL - * and exit critical code, will recover threshold value (1). so threshold <= 1 - * means not in critical code - */ - return (threshold <= 1); + unsigned state = portSET_INTERRUPT_MASK_FROM_ISR(); + return state; +} + +// ---------------------- Spinlocks ------------------------ + +static inline void __attribute__((always_inline)) uxPortCompareSet(volatile uint32_t *addr, uint32_t compare, uint32_t *set) +{ + compare_and_set_native(addr, compare, set); } static inline void uxPortCompareSetExtram(volatile uint32_t *addr, uint32_t compare, uint32_t *set) @@ -116,199 +493,56 @@ static inline void uxPortCompareSetExtram(volatile uint32_t *addr, uint32_t comp #endif } -static inline void __attribute__((always_inline)) uxPortCompareSet(volatile uint32_t *addr, uint32_t compare, uint32_t *set) { - compare_and_set_native(addr, compare, set); +// ---------------------- Yielding ------------------------- + +static inline bool IRAM_ATTR xPortCanYield(void) +{ + uint32_t threshold = REG_READ(INTERRUPT_CORE0_CPU_INT_THRESH_REG); + /* when enter critical code, FreeRTOS will mask threshold to RVHAL_EXCM_LEVEL + * and exit critical code, will recover threshold value (1). so threshold <= 1 + * means not in critical code + */ + return (threshold <= 1); } -#define portCRITICAL_NESTING_IN_TCB 0 -/* - * Send an interrupt to another core in order to make the task running - * on it yield for a higher-priority task. + +/* ------------------------------------------------------ Misc --------------------------------------------------------- + * - Miscellaneous porting macros + * - These are not port of the FreeRTOS porting interface, but are used by other FreeRTOS dependent components + * ------------------------------------------------------------------------------------------------------------------ */ + +// -------------------- Heap Related ----------------------- + +/** + * @brief Checks if a given piece of memory can be used to store a task's TCB + * + * - Defined in port_common.c + * + * @param ptr Pointer to memory + * @return true Memory can be used to store a TCB + * @return false Otherwise */ -void vPortYieldOtherCore( BaseType_t coreid); +bool xPortCheckValidTCBMem(const void *ptr); -/* - Callback to set a watchpoint on the end of the stack. Called every context switch to change the stack - watchpoint around. +/** + * @brief Checks if a given piece of memory can be used to store a task's stack + * + * - Defined in port_common.c + * + * @param ptr Pointer to memory + * @return true Memory can be used to store a task stack + * @return false Otherwise */ -void vPortSetStackWatchpoint( void* pxStackStart ); +bool xPortcheckValidStackMem(const void *ptr); -/* - * Returns true if the current core is in ISR context; low prio ISR, med prio ISR or timer tick ISR. High prio ISRs - * aren't detected here, but they normally cannot call C code, so that should not be an issue anyway. - */ -BaseType_t xPortInIsrContext(void); - -/* - * This function will be called in High prio ISRs. Returns true if the current core was in ISR context - * before calling into high prio ISR context. - */ -BaseType_t xPortInterruptedFromISRContext(void); - -/* "mux" data structure (spinlock) */ -typedef struct { - /* owner field values: - * 0 - Uninitialized (invalid) - * portMUX_FREE_VAL - Mux is free, can be locked by either CPU - * CORE_ID_REGVAL_PRO / CORE_ID_REGVAL_APP - Mux is locked to the particular core - * - * - * Any value other than portMUX_FREE_VAL, CORE_ID_REGVAL_PRO, CORE_ID_REGVAL_APP indicates corruption - */ - uint32_t owner; - /* count field: - * If mux is unlocked, count should be zero. - * If mux is locked, count is non-zero & represents the number of recursive locks on the mux. - */ - uint32_t count; -#ifdef CONFIG_FREERTOS_PORTMUX_DEBUG - const char *lastLockedFn; - int lastLockedLine; -#endif -} portMUX_TYPE; - -#define portMUX_FREE_VAL SPINLOCK_FREE - -/* Special constants for vPortCPUAcquireMutexTimeout() */ -#define portMUX_NO_TIMEOUT SPINLOCK_WAIT_FOREVER /* When passed for 'timeout_cycles', spin forever if necessary */ -#define portMUX_TRY_LOCK SPINLOCK_NO_WAIT /* Try to acquire the spinlock a single time only */ - -// Keep this in sync with the portMUX_TYPE struct definition please. -#ifndef CONFIG_FREERTOS_PORTMUX_DEBUG -#define portMUX_INITIALIZER_UNLOCKED { \ - .owner = portMUX_FREE_VAL, \ - .count = 0, \ - } -#else -#define portMUX_INITIALIZER_UNLOCKED { \ - .owner = portMUX_FREE_VAL, \ - .count = 0, \ - .lastLockedFn = "(never locked)", \ - .lastLockedLine = -1 \ - } -#endif - -/* Scheduler utilities. */ -extern void vPortYield( void ); -extern void vPortYieldFromISR( void ); - -#define portYIELD() vPortYield() -#define portYIELD_FROM_ISR() vPortYieldFromISR() - -/* Yielding within an API call (when interrupts are off), means the yield should be delayed - until interrupts are re-enabled. - To do this, we use the "cross-core" interrupt as a trigger to yield on this core when interrupts are re-enabled.This - is the same interrupt & code path which is used to trigger a yield between CPUs, although in this case the yield is - happening on the same CPU. -*/ -#define portYIELD_WITHIN_API() portYIELD() -/*-----------------------------------------------------------*/ - -/* Critical section management. */ -extern int vPortSetInterruptMask(void); -extern void vPortClearInterruptMask( int ); - -void vPortCPUInitializeMutex(portMUX_TYPE *mux); -void vPortCPUAcquireMutex(portMUX_TYPE *mux); -bool vPortCPUAcquireMutexTimeout(portMUX_TYPE *mux, int timeout_cycles); -void vPortCPUReleaseMutex(portMUX_TYPE *mux); - -extern void vPortEnterCritical( void ); -extern void vPortExitCritical( void ); - -#define portDISABLE_INTERRUPTS() portSET_INTERRUPT_MASK_FROM_ISR() -#define portENABLE_INTERRUPTS() portCLEAR_INTERRUPT_MASK_FROM_ISR(1) - -#define portENTER_CRITICAL(mux) {(void)mux; vPortEnterCritical();} -#define portEXIT_CRITICAL(mux) {(void)mux; vPortExitCritical();} - -#define portENTER_CRITICAL_ISR(mux) portENTER_CRITICAL(mux) -#define portEXIT_CRITICAL_ISR(mux) portEXIT_CRITICAL(mux) - -#define portENTER_CRITICAL_SAFE(mux) do { \ - if (xPortInIsrContext()) { \ - portENTER_CRITICAL_ISR(mux); \ - } else { \ - portENTER_CRITICAL(mux); \ - } \ - } while(0) - -#define portEXIT_CRITICAL_SAFE(mux) do { \ - if (xPortInIsrContext()) { \ - portEXIT_CRITICAL_ISR(mux); \ - } else { \ - portEXIT_CRITICAL(mux); \ - } \ - } while(0) - -/*------------------------------------------------------------*/ -#define portSET_INTERRUPT_MASK_FROM_ISR() vPortSetInterruptMask() -#define portCLEAR_INTERRUPT_MASK_FROM_ISR( uxSavedStatusValue ) vPortClearInterruptMask( uxSavedStatusValue ) -#define portEND_SWITCHING_ISR( xSwitchRequired ) if( xSwitchRequired ) vPortYield() - -// Cleaner solution allows nested interrupts disabling and restoring via local registers or stack. -// They can be called from interrupts too. -static inline unsigned portENTER_CRITICAL_NESTED(void) { - unsigned state = portSET_INTERRUPT_MASK_FROM_ISR(); - return state; -} - -#define portEXIT_CRITICAL_NESTED(state) do { portCLEAR_INTERRUPT_MASK_FROM_ISR( state );} while(0); -/*-----------------------------------------------------------*/ - -//Because the ROM routines don't necessarily handle a stack in external RAM correctly, we force -//the stack memory to always be internal. -#define portTcbMemoryCaps (MALLOC_CAP_INTERNAL|MALLOC_CAP_8BIT) -#define portStackMemoryCaps (MALLOC_CAP_INTERNAL|MALLOC_CAP_8BIT) - -#define pvPortMallocTcbMem(size) pvPortMalloc(size) -#define pvPortMallocStackMem(size) pvPortMalloc(size) - -/* Fine resolution time */ -#define portGET_RUN_TIME_COUNTER_VALUE() 0 -#define portCONFIGURE_TIMER_FOR_RUN_TIME_STATS() - -#ifdef CONFIG_FREERTOS_RUN_TIME_STATS_USING_ESP_TIMER -/* Coarse resolution time (us) */ -#define portALT_GET_RUN_TIME_COUNTER_VALUE(x) do {x = (uint32_t)esp_timer_get_time();} while(0) -#endif - -extern void esp_vApplicationIdleHook( void ); -extern void esp_vApplicationTickHook( void ); - -#ifndef CONFIG_FREERTOS_LEGACY_HOOKS -#define vApplicationIdleHook esp_vApplicationIdleHook -#define vApplicationTickHook esp_vApplicationTickHook -#endif /* !CONFIG_FREERTOS_LEGACY_HOOKS */ - -/* Task function macros as described on the FreeRTOS.org WEB site. */ -#define portTASK_FUNCTION_PROTO( vFunction, pvParameters ) void vFunction( void *pvParameters ) -#define portTASK_FUNCTION( vFunction, pvParameters ) void vFunction( void *pvParameters ) - -void vApplicationSleep( TickType_t xExpectedIdleTime ); -#define portSUPPRESS_TICKS_AND_SLEEP( idleTime ) vApplicationSleep( idleTime ) - -#define portNOP() __asm volatile ( " nop " ) - -#define portVALID_TCB_MEM(ptr) esp_ptr_byte_accessible(ptr) -#define portVALID_STACK_MEM(ptr) esp_ptr_byte_accessible(ptr) - -/* Get tick rate per second */ -uint32_t xPortGetTickRateHz(void); - -// configASSERT_2 if requested -#if configASSERT_2 -#include -void exit(int); -#define configASSERT( x ) if (!(x)) { porttracePrint(-1); printf("\nAssertion failed in %s:%d\n", __FILE__, __LINE__); exit(-1); } -#endif - - -#endif //__ASSEMBLER__ +#define portVALID_TCB_MEM(ptr) xPortCheckValidTCBMem(ptr) +#define portVALID_STACK_MEM(ptr) xPortcheckValidStackMem(ptr) #ifdef __cplusplus } #endif +#endif //__ASSEMBLER__ + #endif /* PORTMACRO_H */ diff --git a/components/freertos/port/riscv/port.c b/components/freertos/port/riscv/port.c index 297fab1d2f..2ca3972098 100644 --- a/components/freertos/port/riscv/port.c +++ b/components/freertos/port/riscv/port.c @@ -74,31 +74,35 @@ * Implementation of functions defined in portable.h for the RISC-V port. *----------------------------------------------------------------------*/ -#include -#include "FreeRTOS.h" -#include "task.h" -#include "portmacro.h" - #include "sdkconfig.h" - +#include #include "soc/soc_caps.h" #include "soc/periph_defs.h" #include "soc/system_reg.h" #include "hal/systimer_hal.h" #include "hal/systimer_ll.h" - #include "riscv/rvruntime-frames.h" #include "riscv/riscv_interrupts.h" #include "riscv/interrupt.h" - -#include "port_systick.h" +#include "esp_private/crosscore_int.h" +#include "esp_private/pm_trace.h" +#include "esp_attr.h" #include "esp_system.h" #include "esp_intr_alloc.h" -#include "esp_private/crosscore_int.h" -#include "esp_attr.h" #include "esp_debug_helpers.h" #include "esp_log.h" -#include "esp_private/pm_trace.h" +#include "FreeRTOS.h" /* This pulls in portmacro.h */ +#include "task.h" +#include "portmacro.h" +#include "port_systick.h" + + + +/* ---------------------------------------------------- Variables ------------------------------------------------------ + * + * ------------------------------------------------------------------------------------------------------------------ */ + +static const char *TAG = "cpu_start"; // [refactor-todo]: might be appropriate to change in the future, but /** * @brief A variable is used to keep track of the critical section nesting. @@ -114,33 +118,43 @@ BaseType_t xPortSwitchFlag = 0; __attribute__((aligned(16))) static StackType_t xIsrStack[configISR_STACK_SIZE]; StackType_t *xIsrStackTop = &xIsrStack[0] + (configISR_STACK_SIZE & (~((portPOINTER_SIZE_TYPE)portBYTE_ALIGNMENT_MASK))); -static const char *TAG = "cpu_start"; // [refactor-todo]: might be appropriate to change in the future, but -static void prvTaskExitError(void); + +/* ------------------------------------------------ FreeRTOS Portable -------------------------------------------------- + * - Provides implementation for functions required by FreeRTOS + * - Declared in portable.h + * ------------------------------------------------------------------------------------------------------------------ */ + +// ----------------- Scheduler Start/End ------------------- extern void esprv_intc_int_set_threshold(int); // FIXME, this function is in ROM only - -void vPortEnterCritical(void) +BaseType_t xPortStartScheduler(void) { - BaseType_t state = portENTER_CRITICAL_NESTED(); - uxCriticalNesting++; + uxInterruptNesting = 0; + uxCriticalNesting = 0; + uxSchedulerRunning = 0; - if (uxCriticalNesting == 1) { - uxSavedInterruptState = state; - } + /* Setup the hardware to generate the tick. */ + vPortSetupTimer(); + + esprv_intc_int_set_threshold(1); /* set global INTC masking level */ + riscv_global_interrupts_enable(); + + vPortYield(); + + /*Should not get here*/ + return pdFALSE; } -void vPortExitCritical(void) +void vPortEndScheduler(void) { - if (uxCriticalNesting > 0) { - uxCriticalNesting--; - if (uxCriticalNesting == 0) { - portEXIT_CRITICAL_NESTED(uxSavedInterruptState); - } - } + /* very unlikely this function will be called, so just trap here */ + abort(); } -void prvTaskExitError(void) +// ------------------------ Stack -------------------------- + +static void prvTaskExitError(void) { /* A function that implements a task must not exit or attempt to return to its caller as there is nothing to return to. If a task wants to exit it @@ -153,57 +167,11 @@ void prvTaskExitError(void) abort(); } -/* Clear current interrupt mask and set given mask */ -void vPortClearInterruptMask(int mask) -{ - REG_WRITE(INTERRUPT_CORE0_CPU_INT_THRESH_REG, mask); - /** - * The delay between the moment we unmask the interrupt threshold register - * and the moment the potential requested interrupt is triggered is not - * null: up to three machine cycles/instructions can be executed. - * - * When compilation size optimization is enabled, this function and its - * callers returning void will have NO epilogue, thus the instruction - * following these calls will be executed. - * - * If the requested interrupt is a context switch to a higher priority - * task then the one currently running, we MUST NOT execute any instruction - * before the interrupt effectively happens. - * In order to prevent this, force this routine to have a 3-instruction - * delay before exiting. - */ - asm volatile ( "nop" ); - asm volatile ( "nop" ); - asm volatile ( "nop" ); -} - -/* Set interrupt mask and return current interrupt enable register */ -int vPortSetInterruptMask(void) -{ - int ret; - unsigned old_mstatus = RV_CLEAR_CSR(mstatus, MSTATUS_MIE); - ret = REG_READ(INTERRUPT_CORE0_CPU_INT_THRESH_REG); - REG_WRITE(INTERRUPT_CORE0_CPU_INT_THRESH_REG, RVHAL_EXCM_LEVEL); - RV_SET_CSR(mstatus, old_mstatus & MSTATUS_MIE); - /** - * In theory, this function should not return immediately as there is a - * delay between the moment we mask the interrupt threshold register and - * the moment a potential lower-priority interrupt is triggered (as said - * above), it should have a delay of 2 machine cycles/instructions. - * - * However, in practice, this function has an epilogue of one instruction, - * thus the instruction masking the interrupt threshold register is - * followed by two instructions: `ret` and `csrrs` (RV_SET_CSR). - * That's why we don't need any additional nop instructions here. - */ - return ret; -} - StackType_t *pxPortInitialiseStack(StackType_t *pxTopOfStack, TaskFunction_t pxCode, void *pvParameters) { extern uint32_t __global_pointer$; - uint8_t* task_thread_local_start; - uint8_t* threadptr; + uint8_t *task_thread_local_start; + uint8_t *threadptr; extern char _thread_local_start, _thread_local_end, _flash_rodata_start; /* Byte pointer, so that subsequent calculations don't depend on sizeof(StackType_t). */ @@ -271,78 +239,13 @@ StackType_t *pxPortInitialiseStack(StackType_t *pxTopOfStack, TaskFunction_t pxC return (StackType_t *)frame; } -BaseType_t xPortStartScheduler(void) -{ - uxInterruptNesting = 0; - uxCriticalNesting = 0; - uxSchedulerRunning = 0; - /* Setup the hardware to generate the tick. */ - vPortSetupTimer(); - esprv_intc_int_set_threshold(1); /* set global INTC masking level */ - riscv_global_interrupts_enable(); +/* ---------------------------------------------- Port Implementations ------------------------------------------------- + * + * ------------------------------------------------------------------------------------------------------------------ */ - vPortYield(); - - /*Should not get here*/ - return pdFALSE; -} - -void vPortEndScheduler(void) -{ - /* very unlikely this function will be called, so just trap here */ - abort(); -} - -void vPortYieldOtherCore(BaseType_t coreid) -{ - esp_crosscore_int_send_yield(coreid); -} - -void vPortYieldFromISR( void ) -{ - traceISR_EXIT_TO_SCHEDULER(); - uxSchedulerRunning = 1; - xPortSwitchFlag = 1; -} - -void vPortYield(void) -{ - if (uxInterruptNesting) { - vPortYieldFromISR(); - } else { - - esp_crosscore_int_send_yield(0); - /* There are 3-4 instructions of latency between triggering the software - interrupt and the CPU interrupt happening. Make sure it happened before - we return, otherwise vTaskDelay() may return and execute 1-2 - instructions before the delay actually happens. - - (We could use the WFI instruction here, but there is a chance that - the interrupt will happen while evaluating the other two conditions - for an instant yield, and if that happens then the WFI would be - waiting for the next interrupt to occur...) - */ - while (uxSchedulerRunning && uxCriticalNesting == 0 && REG_READ(SYSTEM_CPU_INTR_FROM_CPU_0_REG) != 0) {} - } - -} - -#define STACK_WATCH_AREA_SIZE 32 -#define STACK_WATCH_POINT_NUMBER (SOC_CPU_WATCHPOINTS_NUM - 1) - -void vPortSetStackWatchpoint(void *pxStackStart) -{ - uint32_t addr = (uint32_t)pxStackStart; - addr = (addr + (STACK_WATCH_AREA_SIZE - 1)) & (~(STACK_WATCH_AREA_SIZE - 1)); - esp_cpu_set_watchpoint(STACK_WATCH_POINT_NUMBER, (char *)addr, STACK_WATCH_AREA_SIZE, ESP_WATCHPOINT_STORE); -} - -uint32_t xPortGetTickRateHz(void) -{ - return (uint32_t)configTICK_RATE_HZ; -} +// --------------------- Interrupts ------------------------ BaseType_t xPortInIsrContext(void) { @@ -355,6 +258,7 @@ BaseType_t IRAM_ATTR xPortInterruptedFromISRContext(void) return uxInterruptNesting; } +// ---------------------- Spinlocks ------------------------ void vPortCPUInitializeMutex(portMUX_TYPE *mux) { @@ -378,6 +282,109 @@ void vPortCPUReleaseMutex(portMUX_TYPE *mux) (void)mux; //TODO: IDF-2393 } +// ------------------ Critical Sections -------------------- + +void vPortEnterCritical(void) +{ + BaseType_t state = portENTER_CRITICAL_NESTED(); + uxCriticalNesting++; + + if (uxCriticalNesting == 1) { + uxSavedInterruptState = state; + } +} + +void vPortExitCritical(void) +{ + if (uxCriticalNesting > 0) { + uxCriticalNesting--; + if (uxCriticalNesting == 0) { + portEXIT_CRITICAL_NESTED(uxSavedInterruptState); + } + } +} + +// ---------------------- Yielding ------------------------- + +int vPortSetInterruptMask(void) +{ + int ret; + unsigned old_mstatus = RV_CLEAR_CSR(mstatus, MSTATUS_MIE); + ret = REG_READ(INTERRUPT_CORE0_CPU_INT_THRESH_REG); + REG_WRITE(INTERRUPT_CORE0_CPU_INT_THRESH_REG, RVHAL_EXCM_LEVEL); + RV_SET_CSR(mstatus, old_mstatus & MSTATUS_MIE); + /** + * In theory, this function should not return immediately as there is a + * delay between the moment we mask the interrupt threshold register and + * the moment a potential lower-priority interrupt is triggered (as said + * above), it should have a delay of 2 machine cycles/instructions. + * + * However, in practice, this function has an epilogue of one instruction, + * thus the instruction masking the interrupt threshold register is + * followed by two instructions: `ret` and `csrrs` (RV_SET_CSR). + * That's why we don't need any additional nop instructions here. + */ + return ret; +} + +void vPortClearInterruptMask(int mask) +{ + REG_WRITE(INTERRUPT_CORE0_CPU_INT_THRESH_REG, mask); + /** + * The delay between the moment we unmask the interrupt threshold register + * and the moment the potential requested interrupt is triggered is not + * null: up to three machine cycles/instructions can be executed. + * + * When compilation size optimization is enabled, this function and its + * callers returning void will have NO epilogue, thus the instruction + * following these calls will be executed. + * + * If the requested interrupt is a context switch to a higher priority + * task then the one currently running, we MUST NOT execute any instruction + * before the interrupt effectively happens. + * In order to prevent this, force this routine to have a 3-instruction + * delay before exiting. + */ + asm volatile ( "nop" ); + asm volatile ( "nop" ); + asm volatile ( "nop" ); +} + +void vPortYield(void) +{ + if (uxInterruptNesting) { + vPortYieldFromISR(); + } else { + + esp_crosscore_int_send_yield(0); + /* There are 3-4 instructions of latency between triggering the software + interrupt and the CPU interrupt happening. Make sure it happened before + we return, otherwise vTaskDelay() may return and execute 1-2 + instructions before the delay actually happens. + + (We could use the WFI instruction here, but there is a chance that + the interrupt will happen while evaluating the other two conditions + for an instant yield, and if that happens then the WFI would be + waiting for the next interrupt to occur...) + */ + while (uxSchedulerRunning && uxCriticalNesting == 0 && REG_READ(SYSTEM_CPU_INTR_FROM_CPU_0_REG) != 0) {} + } +} + +void vPortYieldFromISR( void ) +{ + traceISR_EXIT_TO_SCHEDULER(); + uxSchedulerRunning = 1; + xPortSwitchFlag = 1; +} + +void vPortYieldOtherCore(BaseType_t coreid) +{ + esp_crosscore_int_send_yield(coreid); +} + +// ------------------- Hook Functions ---------------------- + void __attribute__((weak)) vApplicationStackOverflowHook(TaskHandle_t xTask, char *pcTaskName) { #define ERR_STR1 "***ERROR*** A stack overflow in task " @@ -393,6 +400,32 @@ void __attribute__((weak)) vApplicationStackOverflowHook(TaskHandle_t xTask, cha esp_system_abort(buf); } +// ----------------------- System -------------------------- + +uint32_t xPortGetTickRateHz(void) +{ + return (uint32_t)configTICK_RATE_HZ; +} + +#define STACK_WATCH_AREA_SIZE 32 +#define STACK_WATCH_POINT_NUMBER (SOC_CPU_WATCHPOINTS_NUM - 1) + +void vPortSetStackWatchpoint(void *pxStackStart) +{ + uint32_t addr = (uint32_t)pxStackStart; + addr = (addr + (STACK_WATCH_AREA_SIZE - 1)) & (~(STACK_WATCH_AREA_SIZE - 1)); + esp_cpu_set_watchpoint(STACK_WATCH_POINT_NUMBER, (char *)addr, STACK_WATCH_AREA_SIZE, ESP_WATCHPOINT_STORE); +} + + + +/* ---------------------------------------------- Misc Implementations ------------------------------------------------- + * + * ------------------------------------------------------------------------------------------------------------------ */ + +// --------------------- App Start-up ---------------------- + +/* [refactor-todo]: See if we can include this through a header */ extern void esp_startup_start_app_common(void); void esp_startup_start_app(void) diff --git a/components/freertos/port/xtensa/include/freertos/portmacro.h b/components/freertos/port/xtensa/include/freertos/portmacro.h index 248c86d15c..f1520b70c2 100644 --- a/components/freertos/port/xtensa/include/freertos/portmacro.h +++ b/components/freertos/port/xtensa/include/freertos/portmacro.h @@ -24,129 +24,520 @@ * * 1 tab == 4 spaces! */ + #ifndef PORTMACRO_H #define PORTMACRO_H +#ifndef __ASSEMBLER__ + +#include "sdkconfig.h" +#include +#include +#include +#include +#include +#include /* required for xthal_get_ccount. [refactor-todo] use cpu_hal instead */ +#include /* required for XTOS_SET_INTLEVEL. [refactor-todo] add common intr functions to esp_hw_support */ +#include "xt_instr_macros.h" +#include "soc/spinlock.h" +#include "hal/cpu_hal.h" +#include "esp_private/crosscore_int.h" +#include "esp_attr.h" +#include "esp_timer.h" /* required for esp_timer_get_time. [refactor-todo] make this common between archs */ +#include "esp_newlib.h" /* required for esp_reent_init() in tasks.c */ +#include "esp_heap_caps.h" +#include "esp_rom_sys.h" +#include "esp_system.h" /* required by esp_get_...() functions in portable.h. [refactor-todo] Update portable.h */ +#include "portbenchmark.h" + +/* [refactor-todo] These includes are not directly used in this file. They are kept into to prevent a breaking change. Remove these. */ +#include +#include +#include +#include "soc/cpu.h" +#ifdef CONFIG_LEGACY_INCLUDE_COMMON_HEADERS +#include "soc/soc_memory_layout.h" +#endif + #ifdef __cplusplus extern "C" { #endif -#ifndef __ASSEMBLER__ -#include -#include -#include -#include -#include -#include -#include -#include /* required for XSHAL_CLIB */ -#include -#include "esp_private/crosscore_int.h" -#include "esp_timer.h" /* required for FreeRTOS run time stats */ -#include "esp_system.h" -#include "esp_newlib.h" -#include "soc/spinlock.h" -#include -#include "esp_rom_sys.h" -#include "sdkconfig.h" -#include "freertos/xtensa_api.h" -#include "esp_system.h" -#include "soc/cpu.h" -#include -#ifdef CONFIG_LEGACY_INCLUDE_COMMON_HEADERS -#include "soc/soc_memory_layout.h" -#endif +/* --------------------------------------------------- Port Types ------------------------------------------------------ + * - Port specific types. + * - The settings in this file configure FreeRTOS correctly for the given hardware and compiler. + * - These settings should not be altered. + * - The port types must come before first as they are used further down the file + * ------------------------------------------------------------------------------------------------------------------ */ -/*----------------------------------------------------------- - * Port specific definitions. - * - * The settings in this file configure FreeRTOS correctly for the - * given hardware and compiler. - * - * These settings should not be altered. - *----------------------------------------------------------- - */ +#define portCHAR int8_t +#define portFLOAT float +#define portDOUBLE double +#define portLONG int32_t +#define portSHORT int16_t +#define portSTACK_TYPE uint8_t +#define portBASE_TYPE int -#include "esp_system.h" -#include "hal/cpu_hal.h" -#include "xt_instr_macros.h" - -/* Type definitions. */ -#define portCHAR int8_t -#define portFLOAT float -#define portDOUBLE double -#define portLONG int32_t -#define portSHORT int16_t -#define portSTACK_TYPE uint8_t -#define portBASE_TYPE int - -typedef portSTACK_TYPE StackType_t; -typedef portBASE_TYPE BaseType_t; -typedef unsigned portBASE_TYPE UBaseType_t; +typedef portSTACK_TYPE StackType_t; +typedef portBASE_TYPE BaseType_t; +typedef unsigned portBASE_TYPE UBaseType_t; #if( configUSE_16_BIT_TICKS == 1 ) - typedef uint16_t TickType_t; - #define portMAX_DELAY ( TickType_t ) 0xffff +typedef uint16_t TickType_t; +#define portMAX_DELAY ( TickType_t ) 0xffff #else - typedef uint32_t TickType_t; - #define portMAX_DELAY ( TickType_t ) 0xffffffffUL +typedef uint32_t TickType_t; +#define portMAX_DELAY ( TickType_t ) 0xffffffffUL #endif -/*-----------------------------------------------------------*/ -// portbenchmark -#include "portbenchmark.h" +/* Task function macros as described on the FreeRTOS.org WEB site. */ +#define portTASK_FUNCTION_PROTO( vFunction, pvParameters ) void vFunction( void *pvParameters ) +#define portTASK_FUNCTION( vFunction, pvParameters ) void vFunction( void *pvParameters ) -#include "sdkconfig.h" -#include "esp_attr.h" -#include "portmacro_priv.h" -// Cleaner solution allows nested interrupts disabling and restoring via local registers or stack. -// They can be called from interrupts too. -// WARNING: Only applies to current CPU. See notes above. -static inline unsigned portENTER_CRITICAL_NESTED(void) { - unsigned state = XTOS_SET_INTLEVEL(XCHAL_EXCM_LEVEL); - portbenchmarkINTERRUPT_DISABLE(); - return state; -} -#define portEXIT_CRITICAL_NESTED(state) do { portbenchmarkINTERRUPT_RESTORE(state); XTOS_RESTORE_JUST_INTLEVEL(state); } while (0) -/* -Modifications to portENTER_CRITICAL. +/* ----------------------------------------------- Port Configurations ------------------------------------------------- + * - Configurations values supplied by each port + * - Required by FreeRTOS + * ------------------------------------------------------------------------------------------------------------------ */ -For an introduction, see "Critical Sections & Disabling Interrupts" in docs/api-guides/freertos-smp.rst +#define portCRITICAL_NESTING_IN_TCB 0 +#define portSTACK_GROWTH ( -1 ) +#define portTICK_PERIOD_MS ( ( TickType_t ) 1000 / configTICK_RATE_HZ ) +#define portBYTE_ALIGNMENT 4 +#define portNOP() XT_NOP() -The original portENTER_CRITICAL only disabled the ISRs. This is enough for single-CPU operation: by -disabling the interrupts, there is no task switch so no other tasks can meddle in the data, and because -interrupts are disabled, ISRs can't corrupt data structures either. -For multiprocessing, things get a bit more hairy. First of all, disabling the interrupts doesn't stop -the tasks or ISRs on the other processors meddling with our CPU. For tasks, this is solved by adding -a spinlock to the portENTER_CRITICAL macro. A task running on the other CPU accessing the same data will -spinlock in the portENTER_CRITICAL code until the first CPU is done. -For ISRs, we now also need muxes: while portENTER_CRITICAL disabling interrupts will stop ISRs on the same -CPU from meddling with the data, it does not stop interrupts on the other cores from interfering with the -data. For this, we also use a spinlock in the routines called by the ISR, but these spinlocks -do not disable the interrupts (because they already are). +/* ---------------------------------------------- Forward Declarations ------------------------------------------------- + * - Forward declarations of all the port functions and macros need to implement the FreeRTOS porting interface + * - These must come before definition/declaration of the FreeRTOS porting interface + * ------------------------------------------------------------------------------------------------------------------ */ -This all assumes that interrupts are either entirely disabled or enabled. Interrupt priority levels -will break this scheme. +// --------------------- Interrupts ------------------------ -Remark: For the ESP32, portENTER_CRITICAL and portENTER_CRITICAL_ISR both alias vPortEnterCritical, meaning -that either function can be called both from ISR as well as task context. This is not standard FreeRTOS -behaviour; please keep this in mind if you need any compatibility with other FreeRTOS implementations. +/** + * @brief Checks if the current core is in an ISR context + * + * - ISR context consist of Low/Mid priority ISR, or time tick ISR + * - High priority ISRs aren't detected here, but they normally cannot call C code, so that should not be an issue anyway. + * + * @note [refactor-todo] Check if this should be inlined + * @return + * - pdTRUE if in ISR + * - pdFALSE otherwise + */ +BaseType_t xPortInIsrContext(void); + +/** + * @brief Asserts if in ISR context + * + * - Asserts on xPortInIsrContext() internally + * + * @note [refactor-todo] Check if this API is still required + * @note [refactor-todo] Check if this should be inlined + */ +void vPortAssertIfInISR(void); + +/** + * @brief Check if in ISR context from High priority ISRs + * + * - Called from High priority ISR + * - Checks if the previous context (before high priority interrupt) was in ISR context (meaning low/med priority) + * + * @note [refactor-todo] Check if this should be inlined + * @return + * - pdTRUE if in previous in ISR context + * - pdFALSE otherwise + */ +BaseType_t xPortInterruptedFromISRContext(void); + +/** + * @brief Disable interrupts in a nested manner + * + * - Cleaner solution allows nested interrupts disabling and restoring via local registers or stack. + * - They can be called from interrupts too. + * - WARNING Only applies to current CPU. + * @note [refactor-todo] Define this as portSET_INTERRUPT_MASK_FROM_ISR() instead + * @return unsigned Previous interrupt state + */ +static inline unsigned __attribute__((always_inline)) portENTER_CRITICAL_NESTED(void); + +/* ---------------------- Spinlocks ------------------------ + * - Modifications made to critical sections to support SMP + * - See "Critical Sections & Disabling Interrupts" in docs/api-guides/freertos-smp.rst for more details + * - Remark: For the ESP32, portENTER_CRITICAL and portENTER_CRITICAL_ISR both alias vPortEnterCritical, meaning that + * either function can be called both from ISR as well as task context. This is not standard FreeRTOS + * behaviorr; please keep this in mind if you need any compatibility with other FreeRTOS implementations. + * @note [refactor-todo] Check if these comments are still true + * ------------------------------------------------------ */ + +typedef spinlock_t portMUX_TYPE; /**< Spinlock type used by FreeRTOS critical sections */ +#define portMUX_INITIALIZER_UNLOCKED SPINLOCK_INITIALIZER /**< Spinlock initializer */ +#define portMUX_FREE_VAL SPINLOCK_FREE /**< Spinlock is free. [refactor-todo] check if this is still required */ +#define portMUX_NO_TIMEOUT SPINLOCK_WAIT_FOREVER /**< When passed for 'timeout_cycles', spin forever if necessary. [refactor-todo] check if this is still required */ +#define portMUX_TRY_LOCK SPINLOCK_NO_WAIT /**< Try to acquire the spinlock a single time only. [refactor-todo] check if this is still required */ + +/** + * @brief Initialize a spinlock + * + * - Initializes a spinlock that is used by FreeRTOS SMP critical sections + * + * @param[in] mux Spinlock + */ +static inline void __attribute__((always_inline)) vPortCPUInitializeMutex(portMUX_TYPE *mux); + +/** + * @brief Acquire a spinlock + * + * @note [refactor-todo] check if we still need this + * + * @param[in] mux Spinlock + */ +static inline void __attribute__((always_inline)) vPortCPUAcquireMutex(portMUX_TYPE *mux); + +/** + * @brief Acquire a spinlock but with a specified timeout + * + * @note [refactor-todo] check if we still need this + * @note [refactor-todo] Check if this function should be renamed (due to bool return type) + * + * @param[in] mux Spinlock + * @param timeout + * @return true Spinlock acquired + * @return false Timed out + */ +static inline bool __attribute__((always_inline)) vPortCPUAcquireMutexTimeout(portMUX_TYPE *mux, int timeout); + +/** + * @brief Release a spinlock + * + * @note [refactor-todo] check if we still need this + * + * @param[in] mux Spinlock + */ +static inline void __attribute__((always_inline)) vPortCPUReleaseMutex(portMUX_TYPE *mux); + +/** + * @brief Wrapper for atomic compare-and-set instruction + * + * This subroutine will atomically compare *addr to 'compare'. If *addr == compare, *addr is set to *set. *set is + * updated with the previous value of *addr (either 'compare' or some other value.) + * + * @warning From the ISA docs: in some (unspecified) cases, the s32c1i instruction may return the "bitwise inverse" of + * the old mem if the mem wasn't written. This doesn't seem to happen on the ESP32 (portMUX assertions would + * fail). + * + * @note [refactor-todo] check if we still need this + * @note [refactor-todo] Check if this function should be renamed (due to void return type) + * + * @param[inout] addr Pointer to target address + * @param[in] compare Compare value + * @param[inout] set Pointer to set value + */ +static inline void __attribute__((always_inline)) uxPortCompareSet(volatile uint32_t *addr, uint32_t compare, uint32_t *set); + +/** + * @brief Wrapper for atomic compare-and-set instruction in external RAM + * + * Atomic compare-and-set but the target address is placed in external RAM + * + * @note [refactor-todo] check if we still need this + * + * @param[inout] addr Pointer to target address + * @param[in] compare Compare value + * @param[inout] set Pointer to set value + */ +static inline void uxPortCompareSetExtram(volatile uint32_t *addr, uint32_t compare, uint32_t *set); + +// ------------------ Critical Sections -------------------- + +/** + * @brief Enter a SMP critical section + * + * - Disable interrupts + * - Takes spinlock + * - Can be nested + * + * @param[in] mux Spinlock + */ +void vPortEnterCritical(portMUX_TYPE *mux); + +/** + * @brief Exit a SMP critical section + * + * - Releases spinlock + * - Reenables interrupts + * - Can be nested + * + * @param[in] mux Spinlock + */ +void vPortExitCritical(portMUX_TYPE *mux); + +/** + * @brief FreeRTOS compliant version of enter critical + * + * - Ensures that critical section is only entered from task context + * + * @param[in] mux Spinlock + */ +static inline void __attribute__((always_inline)) vPortEnterCriticalCompliance(portMUX_TYPE *mux); + +/** + * @brief FreeRTOS compliant version of exit critical + * + * @param[in] mux Spinlock + */ +static inline void __attribute__((always_inline)) vPortExitCriticalCompliance(portMUX_TYPE *mux); + +/** + * @brief Safe version of enter critical + * + * - This function can be used to enter a critical section from both task and ISR contexts + * + * @param[in] mux Spinlock + */ +static inline void __attribute__((always_inline)) vPortEnterCriticalSafe(portMUX_TYPE *mux); + +/** + * @brief Safe version of exit critical + * + * @param[in] mux Spinlock + */ +static inline void __attribute__((always_inline)) vPortExitCriticalSafe(portMUX_TYPE *mux); + +// ---------------------- Yielding ------------------------- + +/** + * @brief Perform a solicited context switch + * + * - Defined in portasm.S + * + * @note [refactor-todo] The rest of ESP-IDF should call taskYield() instead + */ +void vPortYield( void ); + +/** + * @brief + * + * @note [refactor-todo] Refactor this to avoid va_args + * @param argc + * @param ... Variable arguments to allow for IDF prototype without arguments, and vanilla version WITH argument + */ +void vPortEvaluateYieldFromISR(int argc, ...); + +/** + * @brief Yields the other core + * + * - Send an interrupt to another core in order to make the task running on it yield for a higher-priority task. + * - Can be used to yield current core as well + * + * @note [refactor-todo] Put this into private macros as its only called from task.c and is not public API + * @param coreid ID of core to yield + */ +void vPortYieldOtherCore(BaseType_t coreid); + +/** + * @brief Checks if the current core can yield + * + * - A core cannot yield if its in an ISR or in a critical section + * + * @note [refactor-todo] See if this can be separated from port macro + * @return true Core can yield + * @return false Core cannot yield + */ +static inline bool IRAM_ATTR xPortCanYield(void); + +// ------------------- Hook Functions ---------------------- + +extern void esp_vApplicationIdleHook(void); /* Required by tasks.c */ +extern void esp_vApplicationTickHook(void); /* Required by tasks.c */ + +/** + * @brief Hook function called on entry to tickless idle + * + * - Implemented in pm_impl.c + * + * @param xExpectedIdleTime Expected idle time + */ +void vApplicationSleep(TickType_t xExpectedIdleTime); + +// ----------------------- System -------------------------- + +/** + * @brief Get the tick rate per second + * + * @note [refactor-todo] make this inline + * @return uint32_t Tick rate in Hz + */ +uint32_t xPortGetTickRateHz(void); + +/** + * @brief Set a watchpoint to watch the last 32 bytes of the stack + * + * Callback to set a watchpoint on the end of the stack. Called every context switch to change the stack watchpoint + * around. + * + * @param pxStackStart Pointer to the start of the stack + */ +void vPortSetStackWatchpoint( void *pxStackStart ); + +/** + * @brief Get the current core's ID + * + * @note [refactor-todo] IDF should call a FreeRTOS like macro instead of port function directly + * @return BaseType_t Core ID + */ +static inline BaseType_t IRAM_ATTR xPortGetCoreID(void); + + + +/* ------------------------------------------- FreeRTOS Porting Interface ---------------------------------------------- + * - Contains all the mappings of the macros required by FreeRTOS + * - Most come after forward declare as porting macros map to declared functions + * - Maps to forward declared functions + * ------------------------------------------------------------------------------------------------------------------ */ + +// ----------------------- Memory -------------------------- + +/** + * @brief Task memory allocation macros + * + * @note Because the ROM routines don't necessarily handle a stack in external RAM correctly, we force the stack + * memory to always be internal. + * @note [refactor-todo] Update portable.h to match v10.4.3 to use new malloc prototypes + */ +#define portTcbMemoryCaps (MALLOC_CAP_INTERNAL|MALLOC_CAP_8BIT) +#define portStackMemoryCaps (MALLOC_CAP_INTERNAL|MALLOC_CAP_8BIT) +#define pvPortMallocTcbMem(size) heap_caps_malloc(size, portTcbMemoryCaps) +#define pvPortMallocStackMem(size) heap_caps_malloc(size, portStackMemoryCaps) + +// --------------------- Interrupts ------------------------ + +#define portEXIT_CRITICAL_NESTED(state) do { portbenchmarkINTERRUPT_RESTORE(state); XTOS_RESTORE_JUST_INTLEVEL(state); } while (0) + +/** + * - Only applies to current core + * - These cannot be nested. They should be used with a lot of care and cannot be called from interrupt level. + * + * @note [refactor-todo] replace XTOS_SET_INTLEVEL with more efficient version, if any? + */ +#define portDISABLE_INTERRUPTS() do { XTOS_SET_INTLEVEL(XCHAL_EXCM_LEVEL); portbenchmarkINTERRUPT_DISABLE(); } while (0) +#define portENABLE_INTERRUPTS() do { portbenchmarkINTERRUPT_RESTORE(0); XTOS_SET_INTLEVEL(0); } while (0) + +/** + * ISR versions to enable/disable interrupts + */ +#define portSET_INTERRUPT_MASK_FROM_ISR() portENTER_CRITICAL_NESTED() +#define portCLEAR_INTERRUPT_MASK_FROM_ISR(state) portEXIT_CRITICAL_NESTED(state) + +#define portASSERT_IF_IN_ISR() vPortAssertIfInISR() + +// ------------------ Critical Sections -------------------- + +/** + * @brief FreeRTOS critical section macros + * + * - Added a spinlock argument for SMP + * - Can be nested + * - Compliance versions will assert if regular critical section API is used in ISR context + * - Safe versions can be called from either contexts + */ +#ifdef CONFIG_FREERTOS_CHECK_PORT_CRITICAL_COMPLIANCE +#define portENTER_CRITICAL(mux) vPortEnterCriticalCompliance(mux) +#define portEXIT_CRITICAL(mux) vPortExitCriticalCompliance(mux) +#else +#define portENTER_CRITICAL(mux) vPortEnterCritical(mux) +#define portEXIT_CRITICAL(mux) vPortExitCritical(mux) +#endif /* CONFIG_FREERTOS_CHECK_PORT_CRITICAL_COMPLIANCE */ +#define portENTER_CRITICAL_ISR(mux) vPortEnterCritical(mux) +#define portEXIT_CRITICAL_ISR(mux) vPortExitCritical(mux) +#define portENTER_CRITICAL_SAFE(mux) vPortEnterCriticalSafe(mux) +#define portEXIT_CRITICAL_SAFE(mux) vPortExitCriticalSafe(mux) + +// ---------------------- Yielding ------------------------- + +#define portYIELD() vPortYield() + +/** + * @note The macro below could be used when passing a single argument, or without any argument, + * it was developed to support both usages of portYIELD inside of an ISR. Any other usage form + * might result in undesired behavior + * + * @note [refactor-todo] Refactor this to avoid va_args + */ +#if defined(__cplusplus) && (__cplusplus > 201703L) +#define portYIELD_FROM_ISR(...) vPortEvaluateYieldFromISR(portGET_ARGUMENT_COUNT(__VA_ARGS__) __VA_OPT__(,) __VA_ARGS__) +#else +#define portYIELD_FROM_ISR(...) vPortEvaluateYieldFromISR(portGET_ARGUMENT_COUNT(__VA_ARGS__), ##__VA_ARGS__) +#endif + +/* Yielding within an API call (when interrupts are off), means the yield should be delayed + until interrupts are re-enabled. + + To do this, we use the "cross-core" interrupt as a trigger to yield on this core when interrupts are re-enabled.This + is the same interrupt & code path which is used to trigger a yield between CPUs, although in this case the yield is + happening on the same CPU. */ -/* "mux" data structure (spinlock) */ -typedef spinlock_t portMUX_TYPE; +#define portYIELD_WITHIN_API() esp_crosscore_int_send_yield(xPortGetCoreID()) -#define portMUX_FREE_VAL SPINLOCK_FREE -#define portMUX_NO_TIMEOUT SPINLOCK_WAIT_FOREVER /* When passed for 'timeout_cycles', spin forever if necessary */ -#define portMUX_TRY_LOCK SPINLOCK_NO_WAIT /* Try to acquire the spinlock a single time only */ -#define portMUX_INITIALIZER_UNLOCKED SPINLOCK_INITIALIZER +// ------------------- Hook Functions ---------------------- -#define portCRITICAL_NESTING_IN_TCB 0 +#ifndef CONFIG_FREERTOS_LEGACY_HOOKS +#define vApplicationIdleHook esp_vApplicationIdleHook +#define vApplicationTickHook esp_vApplicationTickHook +#endif /* !CONFIG_FREERTOS_LEGACY_HOOKS */ + +#define portSUPPRESS_TICKS_AND_SLEEP(idleTime) vApplicationSleep(idleTime) + +// ------------------- Run Time Stats ---------------------- + +#define portCONFIGURE_TIMER_FOR_RUN_TIME_STATS() + +/** + * - Fine resolution uses ccount + * - ALT is coarse and uses esp_timer + * @note [refactor-todo] Make fine and alt timers mutually exclusive + */ +#define portGET_RUN_TIME_COUNTER_VALUE() xthal_get_ccount() +#ifdef CONFIG_FREERTOS_RUN_TIME_STATS_USING_ESP_TIMER +#define portALT_GET_RUN_TIME_COUNTER_VALUE(x) do {x = (uint32_t)esp_timer_get_time();} while(0) +#endif + +// -------------- Optimized Task Selection ----------------- + +#if configUSE_PORT_OPTIMISED_TASK_SELECTION == 1 +/* Check the configuration. */ +#if( configMAX_PRIORITIES > 32 ) +#error configUSE_PORT_OPTIMISED_TASK_SELECTION can only be set to 1 when configMAX_PRIORITIES is less than or equal to 32. It is very rare that a system requires more than 10 to 15 different priorities as tasks that share a priority will time slice. +#endif + +/* Store/clear the ready priorities in a bit map. */ +#define portRECORD_READY_PRIORITY( uxPriority, uxReadyPriorities ) ( uxReadyPriorities ) |= ( 1UL << ( uxPriority ) ) +#define portRESET_READY_PRIORITY( uxPriority, uxReadyPriorities ) ( uxReadyPriorities ) &= ~( 1UL << ( uxPriority ) ) +#define portGET_HIGHEST_PRIORITY( uxTopPriority, uxReadyPriorities ) uxTopPriority = ( 31 - __builtin_clz( ( uxReadyPriorities ) ) ) +#endif /* configUSE_PORT_OPTIMISED_TASK_SELECTION */ + + + +/* --------------------------------------------- Inline Implementations ------------------------------------------------ + * - Implementation of inline functions of the forward declares + * - Should come after forward declare and FreeRTOS Porting interface, as implementation may use both. + * - For implementation of non-inlined functions, see port.c + * ------------------------------------------------------------------------------------------------------------------ */ + +// --------------------- Interrupts ------------------------ + +static inline unsigned portENTER_CRITICAL_NESTED(void) +{ + unsigned state = XTOS_SET_INTLEVEL(XCHAL_EXCM_LEVEL); + portbenchmarkINTERRUPT_DISABLE(); + return state; +} + +// ---------------------- Spinlocks ------------------------ static inline void __attribute__((always_inline)) vPortCPUInitializeMutex(portMUX_TYPE *mux) { @@ -168,21 +559,23 @@ static inline void __attribute__((always_inline)) vPortCPUReleaseMutex(portMUX_T spinlock_release(mux); } -void vPortEnterCritical(portMUX_TYPE *mux); -void vPortExitCritical(portMUX_TYPE *mux); +static inline void __attribute__((always_inline)) uxPortCompareSet(volatile uint32_t *addr, uint32_t compare, uint32_t *set) +{ + compare_and_set_native(addr, compare, set); +} -#define portASSERT_IF_IN_ISR() vPortAssertIfInISR() -void vPortAssertIfInISR(void); +static inline void uxPortCompareSetExtram(volatile uint32_t *addr, uint32_t compare, uint32_t *set) +{ +#ifdef CONFIG_SPIRAM + compare_and_set_extram(addr, compare, set); +#endif +} -/* - * Returns true if the current core is in ISR context; low prio ISR, med prio ISR or timer tick ISR. High prio ISRs - * aren't detected here, but they normally cannot call C code, so that should not be an issue anyway. - */ -BaseType_t xPortInIsrContext(void); +// ------------------ Critical Sections -------------------- static inline void __attribute__((always_inline)) vPortEnterCriticalCompliance(portMUX_TYPE *mux) { - if(!xPortInIsrContext()) { + if (!xPortInIsrContext()) { vPortEnterCritical(mux); } else { esp_rom_printf("%s:%d (%s)- port*_CRITICAL called from ISR context!\n", @@ -193,7 +586,7 @@ static inline void __attribute__((always_inline)) vPortEnterCriticalCompliance(p static inline void __attribute__((always_inline)) vPortExitCriticalCompliance(portMUX_TYPE *mux) { - if(!xPortInIsrContext()) { + if (!xPortInIsrContext()) { vPortExitCritical(mux); } else { esp_rom_printf("%s:%d (%s)- port*_CRITICAL called from ISR context!\n", @@ -202,20 +595,6 @@ static inline void __attribute__((always_inline)) vPortExitCriticalCompliance(po } } -#ifdef CONFIG_FREERTOS_CHECK_PORT_CRITICAL_COMPLIANCE -/* Calling port*_CRITICAL from ISR context would cause an assert failure. - * If the parent function is called from both ISR and Non-ISR context then call port*_CRITICAL_SAFE - */ -#define portENTER_CRITICAL(mux) vPortEnterCriticalCompliance(mux) -#define portEXIT_CRITICAL(mux) vPortExitCriticalCompliance(mux) -#else -#define portENTER_CRITICAL(mux) vPortEnterCritical(mux) -#define portEXIT_CRITICAL(mux) vPortExitCritical(mux) -#endif - -#define portENTER_CRITICAL_ISR(mux) vPortEnterCritical(mux) -#define portEXIT_CRITICAL_ISR(mux) vPortExitCritical(mux) - static inline void __attribute__((always_inline)) vPortEnterCriticalSafe(portMUX_TYPE *mux) { if (xPortInIsrContext()) { @@ -234,80 +613,82 @@ static inline void __attribute__((always_inline)) vPortExitCriticalSafe(portMUX_ } } -#define portENTER_CRITICAL_SAFE(mux) vPortEnterCriticalSafe(mux) -#define portEXIT_CRITICAL_SAFE(mux) vPortExitCriticalSafe(mux) +// ---------------------- Yielding ------------------------- + +static inline bool IRAM_ATTR xPortCanYield(void) +{ + uint32_t ps_reg = 0; + + //Get the current value of PS (processor status) register + RSR(PS, ps_reg); + + /* + * intlevel = (ps_reg & 0xf); + * excm = (ps_reg >> 4) & 0x1; + * CINTLEVEL is max(excm * EXCMLEVEL, INTLEVEL), where EXCMLEVEL is 3. + * However, just return true, only intlevel is zero. + */ + + return ((ps_reg & PS_INTLEVEL_MASK) == 0); +} + +// ----------------------- System -------------------------- + +static inline BaseType_t IRAM_ATTR xPortGetCoreID(void) +{ + return cpu_hal_get_core_id(); +} + + + +/* ------------------------------------------------------ Misc --------------------------------------------------------- + * - Miscellaneous porting macros + * - These are not port of the FreeRTOS porting interface, but are used by other FreeRTOS dependent components + * - [refactor-todo] Remove dependency on MPU wrappers by modifying TCB + * ------------------------------------------------------------------------------------------------------------------ */ + +// -------------------- Co-Processor ----------------------- + +// When coprocessors are defined, we maintain a pointer to coprocessors area. +// We currently use a hack: redefine field xMPU_SETTINGS in TCB block as a structure that can hold: +// MPU wrappers, coprocessor area pointer, trace code structure, and more if needed. +// The field is normally used for memory protection. FreeRTOS should create another general purpose field. +typedef struct { +#if XCHAL_CP_NUM > 0 + volatile StackType_t *coproc_area; // Pointer to coprocessor save area; MUST BE FIRST +#endif + +#if portUSING_MPU_WRAPPERS + // Define here mpu_settings, which is port dependent + int mpu_setting; // Just a dummy example here; MPU not ported to Xtensa yet +#endif +} xMPU_SETTINGS; + +// Main hack to use MPU_wrappers even when no MPU is defined (warning: mpu_setting should not be accessed; otherwise move this above xMPU_SETTINGS) +#if (XCHAL_CP_NUM > 0) && !portUSING_MPU_WRAPPERS // If MPU wrappers not used, we still need to allocate coproc area +#undef portUSING_MPU_WRAPPERS +#define portUSING_MPU_WRAPPERS 1 // Enable it to allocate coproc area +#define MPU_WRAPPERS_H // Override mpu_wrapper.h to disable unwanted code +#define PRIVILEGED_FUNCTION +#define PRIVILEGED_DATA +#endif + +void _xt_coproc_release(volatile void *coproc_sa_base); /* - * Wrapper for the Xtensa compare-and-set instruction. This subroutine will atomically compare - * *addr to 'compare'. If *addr == compare, *addr is set to *set. *set is updated with the previous - * value of *addr (either 'compare' or some other value.) + * The structures and methods of manipulating the MPU are contained within the + * port layer. * - * Warning: From the ISA docs: in some (unspecified) cases, the s32c1i instruction may return the - * *bitwise inverse* of the old mem if the mem wasn't written. This doesn't seem to happen on the - * ESP32 (portMUX assertions would fail). + * Fills the xMPUSettings structure with the memory region information + * contained in xRegions. */ -static inline void __attribute__((always_inline)) uxPortCompareSet(volatile uint32_t *addr, uint32_t compare, uint32_t *set) { - compare_and_set_native(addr, compare, set); -} - -// Critical section management. NW-TODO: replace XTOS_SET_INTLEVEL with more efficient version, if any? -// These cannot be nested. They should be used with a lot of care and cannot be called from interrupt level. -// -// Only applies to one CPU. See notes above & below for reasons not to use these. -#define portDISABLE_INTERRUPTS() do { XTOS_SET_INTLEVEL(XCHAL_EXCM_LEVEL); portbenchmarkINTERRUPT_DISABLE(); } while (0) -#define portENABLE_INTERRUPTS() do { portbenchmarkINTERRUPT_RESTORE(0); XTOS_SET_INTLEVEL(0); } while (0) - - -// These FreeRTOS versions are similar to the nested versions above -#define portSET_INTERRUPT_MASK_FROM_ISR() portENTER_CRITICAL_NESTED() -#define portCLEAR_INTERRUPT_MASK_FROM_ISR(state) portEXIT_CRITICAL_NESTED(state) - -//Because the ROM routines don't necessarily handle a stack in external RAM correctly, we force -//the stack memory to always be internal. -#define portTcbMemoryCaps (MALLOC_CAP_INTERNAL|MALLOC_CAP_8BIT) -#define portStackMemoryCaps (MALLOC_CAP_INTERNAL|MALLOC_CAP_8BIT) -#define pvPortMallocTcbMem(size) heap_caps_malloc(size, portTcbMemoryCaps) -#define pvPortMallocStackMem(size) heap_caps_malloc(size, portStackMemoryCaps) - -//xTaskCreateStatic uses these functions to check incoming memory. -#define portVALID_TCB_MEM(ptr) (esp_ptr_internal(ptr) && esp_ptr_byte_accessible(ptr)) -#ifdef CONFIG_SPIRAM_ALLOW_STACK_EXTERNAL_MEMORY -#define portVALID_STACK_MEM(ptr) esp_ptr_byte_accessible(ptr) -#else -#define portVALID_STACK_MEM(ptr) (esp_ptr_internal(ptr) && esp_ptr_byte_accessible(ptr)) +#if( portUSING_MPU_WRAPPERS == 1 ) +struct xMEMORY_REGION; +void vPortStoreTaskMPUSettings( xMPU_SETTINGS *xMPUSettings, const struct xMEMORY_REGION *const xRegions, StackType_t *pxBottomOfStack, uint32_t usStackDepth ) PRIVILEGED_FUNCTION; +void vPortReleaseTaskMPUSettings( xMPU_SETTINGS *xMPUSettings ); #endif - -static inline void uxPortCompareSetExtram(volatile uint32_t *addr, uint32_t compare, uint32_t *set) -{ -#ifdef CONFIG_SPIRAM - compare_and_set_extram(addr, compare, set); -#endif -} - - -/*-----------------------------------------------------------*/ - -/* Architecture specifics. */ -#define portSTACK_GROWTH ( -1 ) -#define portTICK_PERIOD_MS ( ( TickType_t ) 1000 / configTICK_RATE_HZ ) -#define portBYTE_ALIGNMENT 4 -#define portNOP() XT_NOP() -/*-----------------------------------------------------------*/ - -/* Fine resolution time */ -#define portGET_RUN_TIME_COUNTER_VALUE() xthal_get_ccount() -//ccount or esp_timer are initialized elsewhere -#define portCONFIGURE_TIMER_FOR_RUN_TIME_STATS() - -#ifdef CONFIG_FREERTOS_RUN_TIME_STATS_USING_ESP_TIMER -/* Coarse resolution time (us) */ -#define portALT_GET_RUN_TIME_COUNTER_VALUE(x) do {x = (uint32_t)esp_timer_get_time();} while(0) -#endif - -void vPortYield( void ); -void vPortEvaluateYieldFromISR(int argc, ...); -void _frxt_setup_switch( void ); +// -------------------- VA_ARGS Yield ---------------------- /** * Macro to count number of arguments of a __VA_ARGS__ used to support portYIELD_FROM_ISR with, @@ -329,177 +710,37 @@ void _frxt_setup_switch( void ); _Static_assert(portGET_ARGUMENT_COUNT() == 0, "portGET_ARGUMENT_COUNT() result does not match for 0 arguments"); _Static_assert(portGET_ARGUMENT_COUNT(1) == 1, "portGET_ARGUMENT_COUNT() result does not match for 1 argument"); -#define portYIELD() vPortYield() +// -------------------- Heap Related ----------------------- /** - * @note The macro below could be used when passing a single argument, or without any argument, - * it was developed to support both usages of portYIELD inside of an ISR. Any other usage form - * might result in undesired behaviour - */ -#if defined(__cplusplus) && (__cplusplus > 201703L) -#define portYIELD_FROM_ISR(...) vPortEvaluateYieldFromISR(portGET_ARGUMENT_COUNT(__VA_ARGS__) __VA_OPT__(,) __VA_ARGS__) -#else -#define portYIELD_FROM_ISR(...) vPortEvaluateYieldFromISR(portGET_ARGUMENT_COUNT(__VA_ARGS__), ##__VA_ARGS__) -#endif - -/* Yielding within an API call (when interrupts are off), means the yield should be delayed - until interrupts are re-enabled. - - To do this, we use the "cross-core" interrupt as a trigger to yield on this core when interrupts are re-enabled.This - is the same interrupt & code path which is used to trigger a yield between CPUs, although in this case the yield is - happening on the same CPU. -*/ -#define portYIELD_WITHIN_API() esp_crosscore_int_send_yield(xPortGetCoreID()) - -/*-----------------------------------------------------------*/ - -/* Task function macros as described on the FreeRTOS.org WEB site. */ -#define portTASK_FUNCTION_PROTO( vFunction, pvParameters ) void vFunction( void *pvParameters ) -#define portTASK_FUNCTION( vFunction, pvParameters ) void vFunction( void *pvParameters ) - -// When coprocessors are defined, we to maintain a pointer to coprocessors area. -// We currently use a hack: redefine field xMPU_SETTINGS in TCB block as a structure that can hold: -// MPU wrappers, coprocessor area pointer, trace code structure, and more if needed. -// The field is normally used for memory protection. FreeRTOS should create another general purpose field. -typedef struct { - #if XCHAL_CP_NUM > 0 - volatile StackType_t* coproc_area; // Pointer to coprocessor save area; MUST BE FIRST - #endif - - #if portUSING_MPU_WRAPPERS - // Define here mpu_settings, which is port dependent - int mpu_setting; // Just a dummy example here; MPU not ported to Xtensa yet - #endif - - #if configUSE_TRACE_FACILITY_2 - struct { - // Cf. porttraceStamp() - int taskstamp; /* Stamp from inside task to see where we are */ - int taskstampcount; /* A counter usually incremented when we restart the task's loop */ - } porttrace; - #endif -} xMPU_SETTINGS; - -// Main hack to use MPU_wrappers even when no MPU is defined (warning: mpu_setting should not be accessed; otherwise move this above xMPU_SETTINGS) -#if (XCHAL_CP_NUM > 0 || configUSE_TRACE_FACILITY_2) && !portUSING_MPU_WRAPPERS // If MPU wrappers not used, we still need to allocate coproc area - #undef portUSING_MPU_WRAPPERS - #define portUSING_MPU_WRAPPERS 1 // Enable it to allocate coproc area - #define MPU_WRAPPERS_H // Override mpu_wrapper.h to disable unwanted code - #define PRIVILEGED_FUNCTION - #define PRIVILEGED_DATA -#endif - -extern void esp_vApplicationIdleHook( void ); -extern void esp_vApplicationTickHook( void ); - -#ifndef CONFIG_FREERTOS_LEGACY_HOOKS -#define vApplicationIdleHook esp_vApplicationIdleHook -#define vApplicationTickHook esp_vApplicationTickHook -#endif /* !CONFIG_FREERTOS_LEGACY_HOOKS */ - -void vApplicationSleep( TickType_t xExpectedIdleTime ); - -#define portSUPPRESS_TICKS_AND_SLEEP( idleTime ) vApplicationSleep( idleTime ) - -void _xt_coproc_release(volatile void * coproc_sa_base); - -/* Architecture specific optimisations. */ -#if configUSE_PORT_OPTIMISED_TASK_SELECTION == 1 - -/* Check the configuration. */ -#if( configMAX_PRIORITIES > 32 ) - #error configUSE_PORT_OPTIMISED_TASK_SELECTION can only be set to 1 when configMAX_PRIORITIES is less than or equal to 32. It is very rare that a system requires more than 10 to 15 different priorities as tasks that share a priority will time slice. -#endif - -/* Store/clear the ready priorities in a bit map. */ -#define portRECORD_READY_PRIORITY( uxPriority, uxReadyPriorities ) ( uxReadyPriorities ) |= ( 1UL << ( uxPriority ) ) -#define portRESET_READY_PRIORITY( uxPriority, uxReadyPriorities ) ( uxReadyPriorities ) &= ~( 1UL << ( uxPriority ) ) - -/*-----------------------------------------------------------*/ - -#define portGET_HIGHEST_PRIORITY( uxTopPriority, uxReadyPriorities ) uxTopPriority = ( 31 - __builtin_clz( ( uxReadyPriorities ) ) ) - -#endif /* configUSE_PORT_OPTIMISED_TASK_SELECTION */ - -/* - * Send an interrupt to another core in order to make the task running - * on it yield for a higher-priority task. - */ - -void vPortYieldOtherCore( BaseType_t coreid) ; - -/* - Callback to set a watchpoint on the end of the stack. Called every context switch to change the stack - watchpoint around. - */ -void vPortSetStackWatchpoint( void* pxStackStart ); - -/* - * Returns true if the current core is in ISR context; low prio ISR, med prio ISR or timer tick ISR. High prio ISRs - * aren't detected here, but they normally cannot call C code, so that should not be an issue anyway. - */ -BaseType_t xPortInIsrContext(void); - -/* - * This function will be called in High prio ISRs. Returns true if the current core was in ISR context - * before calling into high prio ISR context. - */ -BaseType_t xPortInterruptedFromISRContext(void); - -/* - * The structures and methods of manipulating the MPU are contained within the - * port layer. + * @brief Checks if a given piece of memory can be used to store a task's TCB * - * Fills the xMPUSettings structure with the memory region information - * contained in xRegions. + * - Defined in port_common.c + * + * @param ptr Pointer to memory + * @return true Memory can be used to store a TCB + * @return false Otherwise */ -#if( portUSING_MPU_WRAPPERS == 1 ) - struct xMEMORY_REGION; - void vPortStoreTaskMPUSettings( xMPU_SETTINGS *xMPUSettings, const struct xMEMORY_REGION * const xRegions, StackType_t *pxBottomOfStack, uint32_t usStackDepth ) PRIVILEGED_FUNCTION; - void vPortReleaseTaskMPUSettings( xMPU_SETTINGS *xMPUSettings ); -#endif +bool xPortCheckValidTCBMem(const void *ptr); -/* Multi-core: get current core ID */ -static inline BaseType_t IRAM_ATTR xPortGetCoreID(void) { - return cpu_hal_get_core_id(); -} +/** + * @brief Checks if a given piece of memory can be used to store a task's stack + * + * - Defined in port_common.c + * + * @param ptr Pointer to memory + * @return true Memory can be used to store a task stack + * @return false Otherwise + */ +bool xPortcheckValidStackMem(const void *ptr); -/* Get tick rate per second */ -uint32_t xPortGetTickRateHz(void); - -static inline bool IRAM_ATTR xPortCanYield(void) -{ - uint32_t ps_reg = 0; - - //Get the current value of PS (processor status) register - RSR(PS, ps_reg); - - /* - * intlevel = (ps_reg & 0xf); - * excm = (ps_reg >> 4) & 0x1; - * CINTLEVEL is max(excm * EXCMLEVEL, INTLEVEL), where EXCMLEVEL is 3. - * However, just return true, only intlevel is zero. - */ - - return ((ps_reg & PS_INTLEVEL_MASK) == 0); -} - -// porttrace -#if configUSE_TRACE_FACILITY_2 -#include "porttrace.h" -#endif - -// configASSERT_2 if requested -#if configASSERT_2 -#include -void exit(int); -#define configASSERT( x ) if (!(x)) { porttracePrint(-1); printf("\nAssertion failed in %s:%d\n", __FILE__, __LINE__); exit(-1); } -#endif - -#endif // __ASSEMBLER__ +#define portVALID_TCB_MEM(ptr) xPortCheckValidTCBMem(ptr) +#define portVALID_STACK_MEM(ptr) xPortcheckValidStackMem(ptr) #ifdef __cplusplus } #endif +#endif // __ASSEMBLER__ + #endif /* PORTMACRO_H */ diff --git a/components/freertos/port/xtensa/include/freertos/portmacro_priv.h b/components/freertos/port/xtensa/include/freertos/portmacro_priv.h deleted file mode 100644 index 843456b5aa..0000000000 --- a/components/freertos/port/xtensa/include/freertos/portmacro_priv.h +++ /dev/null @@ -1,78 +0,0 @@ -/* - FreeRTOS V8.2.0 - Copyright (C) 2015 Real Time Engineers Ltd. - All rights reserved - - VISIT http://www.FreeRTOS.org TO ENSURE YOU ARE USING THE LATEST VERSION. - - *************************************************************************** - * * - * FreeRTOS provides completely free yet professionally developed, * - * robust, strictly quality controlled, supported, and cross * - * platform software that has become a de facto standard. * - * * - * Help yourself get started quickly and support the FreeRTOS * - * project by purchasing a FreeRTOS tutorial book, reference * - * manual, or both from: http://www.FreeRTOS.org/Documentation * - * * - * Thank you! * - * * - *************************************************************************** - - This file is part of the FreeRTOS distribution. - - FreeRTOS is free software; you can redistribute it and/or modify it under - the terms of the GNU General Public License (version 2) as published by the - Free Software Foundation >>!AND MODIFIED BY!<< the FreeRTOS exception. - - >>! NOTE: The modification to the GPL is included to allow you to !<< - >>! distribute a combined work that includes FreeRTOS without being !<< - >>! obliged to provide the source code for proprietary components !<< - >>! outside of the FreeRTOS kernel. !<< - - FreeRTOS is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - FOR A PARTICULAR PURPOSE. Full license text is available from the following - link: http://www.freertos.org/a00114.html - - 1 tab == 4 spaces! - - *************************************************************************** - * * - * Having a problem? Start by reading the FAQ "My application does * - * not run, what could be wrong?" * - * * - * http://www.FreeRTOS.org/FAQHelp.html * - * * - *************************************************************************** - - http://www.FreeRTOS.org - Documentation, books, training, latest versions, - license and Real Time Engineers Ltd. contact details. - - http://www.FreeRTOS.org/plus - A selection of FreeRTOS ecosystem products, - including FreeRTOS+Trace - an indispensable productivity tool, a DOS - compatible FAT file system, and our tiny thread aware UDP/IP stack. - - http://www.OpenRTOS.com - Real Time Engineers ltd license FreeRTOS to High - Integrity Systems to sell under the OpenRTOS brand. Low cost OpenRTOS - licenses offer ticketed support, indemnification and middleware. - - http://www.SafeRTOS.com - High Integrity Systems also provide a safety - engineered and independently SIL3 certified version for use in safety and - mission critical applications that require provable dependability. - - 1 tab == 4 spaces! -*/ - - -/* This header holds the macros for porting which should only be used inside FreeRTOS */ - -#pragma once -#include "soc/soc_memory_layout.h" - -//xTaskCreateStatic uses these functions to check incoming memory. -#define portVALID_TCB_MEM(ptr) (esp_ptr_internal(ptr) && esp_ptr_byte_accessible(ptr)) -#ifdef CONFIG_SPIRAM_ALLOW_STACK_EXTERNAL_MEMORY -#define portVALID_STACK_MEM(ptr) esp_ptr_byte_accessible(ptr) -#else -#define portVALID_STACK_MEM(ptr) (esp_ptr_internal(ptr) && esp_ptr_byte_accessible(ptr)) -#endif diff --git a/components/freertos/port/xtensa/port.c b/components/freertos/port/xtensa/port.c index 43ed1cfa4d..778d3b23e6 100644 --- a/components/freertos/port/xtensa/port.c +++ b/components/freertos/port/xtensa/port.c @@ -1,153 +1,124 @@ /* - FreeRTOS V8.2.0 - Copyright (C) 2015 Real Time Engineers Ltd. - All rights reserved + * FreeRTOS Kernel V10.4.3 + * Copyright (C) 2017 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. If you wish to use our Amazon + * FreeRTOS name, please do so in a fair use way that does not cause confusion. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * https://www.FreeRTOS.org + * https://github.com/FreeRTOS + * + * 1 tab == 4 spaces! + */ - VISIT http://www.FreeRTOS.org TO ENSURE YOU ARE USING THE LATEST VERSION. +/* + * Copyright (c) 2015-2019 Cadence Design Systems, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ - This file is part of the FreeRTOS distribution. - - FreeRTOS is free software; you can redistribute it and/or modify it under - the terms of the GNU General Public License (version 2) as published by the - Free Software Foundation >>!AND MODIFIED BY!<< the FreeRTOS exception. - - *************************************************************************** - >>! NOTE: The modification to the GPL is included to allow you to !<< - >>! distribute a combined work that includes FreeRTOS without being !<< - >>! obliged to provide the source code for proprietary components !<< - >>! outside of the FreeRTOS kernel. !<< - *************************************************************************** - - FreeRTOS is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - FOR A PARTICULAR PURPOSE. Full license text is available on the following - link: http://www.freertos.org/a00114.html - - *************************************************************************** - * * - * FreeRTOS provides completely free yet professionally developed, * - * robust, strictly quality controlled, supported, and cross * - * platform software that is more than just the market leader, it * - * is the industry's de facto standard. * - * * - * Help yourself get started quickly while simultaneously helping * - * to support the FreeRTOS project by purchasing a FreeRTOS * - * tutorial book, reference manual, or both: * - * http://www.FreeRTOS.org/Documentation * - * * - *************************************************************************** - - http://www.FreeRTOS.org/FAQHelp.html - Having a problem? Start by reading - the FAQ page "My application does not run, what could be wrong?". Have you - defined configASSERT()? - - http://www.FreeRTOS.org/support - In return for receiving this top quality - embedded software for free we request you assist our global community by - participating in the support forum. - - http://www.FreeRTOS.org/training - Investing in training allows your team to - be as productive as possible as early as possible. Now you can receive - FreeRTOS training directly from Richard Barry, CEO of Real Time Engineers - Ltd, and the world's leading authority on the world's leading RTOS. - - http://www.FreeRTOS.org/plus - A selection of FreeRTOS ecosystem products, - including FreeRTOS+Trace - an indispensable productivity tool, a DOS - compatible FAT file system, and our tiny thread aware UDP/IP stack. - - http://www.FreeRTOS.org/labs - Where new FreeRTOS products go to incubate. - Come and try FreeRTOS+TCP, our new open source TCP/IP stack for FreeRTOS. - - http://www.OpenRTOS.com - Real Time Engineers ltd. license FreeRTOS to High - Integrity Systems ltd. to sell under the OpenRTOS brand. Low cost OpenRTOS - licenses offer ticketed support, indemnification and commercial middleware. - - http://www.SafeRTOS.com - High Integrity Systems also provide a safety - engineered and independently SIL3 certified version for use in safety and - mission critical applications that require provable dependability. - - 1 tab == 4 spaces! -*/ - -/******************************************************************************* -// Copyright (c) 2003-2015 Cadence Design Systems, Inc. -// -// Permission is hereby granted, free of charge, to any person obtaining -// a copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to -// permit persons to whom the Software is furnished to do so, subject to -// the following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. -// IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY -// CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, -// TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE -// SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. --------------------------------------------------------------------------------- -*/ +#include "sdkconfig.h" +#include #include #include +#include #include - -#include "xtensa_rtos.h" - -#include "soc/cpu.h" - -#include "FreeRTOS.h" -#include "task.h" - -#include "esp_debug_helpers.h" -#include "esp_heap_caps.h" -#include "esp_heap_caps_init.h" -#include "esp_private/crosscore_int.h" - -#include "esp_intr_alloc.h" -#include "esp_log.h" -#include "sdkconfig.h" - -#include "esp_task_wdt.h" -#include "esp_task.h" - +#include #include "soc/soc_caps.h" -#include "soc/efuse_reg.h" -#include "soc/dport_access.h" -#include "soc/dport_reg.h" +#include "esp_private/crosscore_int.h" +#include "esp_system.h" +#include "esp_log.h" #include "esp_int_wdt.h" - - -#include "sdkconfig.h" - -#if CONFIG_IDF_TARGET_ESP32 -#include "esp32/spiram.h" -#elif CONFIG_IDF_TARGET_ESP32S2 -#include "esp32s2/spiram.h" -#elif CONFIG_IDF_TARGET_ESP32S3 -#include "esp32s3/spiram.h" -#endif - +#include "esp_app_trace.h" /* Required for esp_apptrace_init. [refactor-todo] */ +#include "FreeRTOS.h" /* This pulls in portmacro.h */ +#include "task.h" /* Required for TaskHandle_t, tskNO_AFFINITY, and vTaskStartScheduler */ #include "port_systick.h" -#include "esp_private/startup_internal.h" // [refactor-todo] for g_spiram_ok -#include "esp_app_trace.h" // [refactor-todo] for esp_app_trace_init - -/* Defined in xtensa_context.S */ -extern void _xt_coproc_init(void); - -static const char* TAG = "cpu_start"; // [refactor-todo]: might be appropriate to change in the future, but - // for now maintain the same log output _Static_assert(tskNO_AFFINITY == CONFIG_FREERTOS_NO_AFFINITY, "incorrect tskNO_AFFINITY value"); -/*-----------------------------------------------------------*/ + +/* ---------------------------------------------------- Variables ------------------------------------------------------ + * + * ------------------------------------------------------------------------------------------------------------------ */ + +static const char *TAG = "cpu_start"; /* [refactor-todo]: might be appropriate to change in the future, but for now maintain the same log output */ extern volatile int port_xSchedulerRunning[portNUM_PROCESSORS]; unsigned port_interruptNesting[portNUM_PROCESSORS] = {0}; // Interrupt nesting level. Increased/decreased in portasm.c, _frxt_int_enter/_frxt_int_exit BaseType_t port_uxCriticalNesting[portNUM_PROCESSORS] = {0}; BaseType_t port_uxOldInterruptState[portNUM_PROCESSORS] = {0}; -/*-----------------------------------------------------------*/ + + +/* ------------------------------------------------ FreeRTOS Portable -------------------------------------------------- + * - Provides implementation for functions required by FreeRTOS + * - Declared in portable.h + * ------------------------------------------------------------------------------------------------------------------ */ + +// ----------------- Scheduler Start/End ------------------- + +/* Defined in xtensa_context.S */ +extern void _xt_coproc_init(void); + +BaseType_t xPortStartScheduler( void ) +{ + // Interrupts are disabled at this point and stack contains PS with enabled interrupts when task context is restored + +#if XCHAL_CP_NUM > 0 + /* Initialize co-processor management for tasks. Leave CPENABLE alone. */ + _xt_coproc_init(); +#endif + + /* Setup the hardware to generate the tick. */ + vPortSetupTimer(); + + port_xSchedulerRunning[xPortGetCoreID()] = 1; + + // Cannot be directly called from C; never returns + __asm__ volatile ("call0 _frxt_dispatch\n"); + + /* Should not get here. */ + return pdTRUE; +} + +void vPortEndScheduler( void ) +{ + /* It is unlikely that the Xtensa port will get stopped. If required simply + disable the tick interrupt here. */ + abort(); +} + +// ------------------------ Stack -------------------------- // User exception dispatcher when exiting void _xt_user_exit(void); @@ -156,219 +127,204 @@ void _xt_user_exit(void); // Wrapper to allow task functions to return (increases stack overhead by 16 bytes) static void vPortTaskWrapper(TaskFunction_t pxCode, void *pvParameters) { - pxCode(pvParameters); - //FreeRTOS tasks should not return. Log the task name and abort. - char * pcTaskName = pcTaskGetTaskName(NULL); - ESP_LOGE("FreeRTOS", "FreeRTOS Task \"%s\" should not return, Aborting now!", pcTaskName); - abort(); + pxCode(pvParameters); + //FreeRTOS tasks should not return. Log the task name and abort. + char *pcTaskName = pcTaskGetTaskName(NULL); + ESP_LOGE("FreeRTOS", "FreeRTOS Task \"%s\" should not return, Aborting now!", pcTaskName); + abort(); } #endif -/* - * Stack initialization - */ #if portUSING_MPU_WRAPPERS StackType_t *pxPortInitialiseStack( StackType_t *pxTopOfStack, TaskFunction_t pxCode, void *pvParameters, BaseType_t xRunPrivileged ) #else StackType_t *pxPortInitialiseStack( StackType_t *pxTopOfStack, TaskFunction_t pxCode, void *pvParameters ) #endif { - StackType_t *sp, *tp; - XtExcFrame *frame; - #if XCHAL_CP_NUM > 0 - uint32_t *p; - #endif - uint32_t *threadptr; - void *task_thread_local_start; - extern int _thread_local_start, _thread_local_end, _flash_rodata_start, _flash_rodata_align; - // TODO: check that TLS area fits the stack - uint32_t thread_local_sz = (uint8_t *)&_thread_local_end - (uint8_t *)&_thread_local_start; + StackType_t *sp, *tp; + XtExcFrame *frame; +#if XCHAL_CP_NUM > 0 + uint32_t *p; +#endif + uint32_t *threadptr; + void *task_thread_local_start; + extern int _thread_local_start, _thread_local_end, _flash_rodata_start, _flash_rodata_align; + // TODO: check that TLS area fits the stack + uint32_t thread_local_sz = (uint8_t *)&_thread_local_end - (uint8_t *)&_thread_local_start; - thread_local_sz = ALIGNUP(0x10, thread_local_sz); + thread_local_sz = ALIGNUP(0x10, thread_local_sz); - /* Initialize task's stack so that we have the following structure at the top: + /* Initialize task's stack so that we have the following structure at the top: - ----LOW ADDRESSES ----------------------------------------HIGH ADDRESSES---------- - task stack | interrupt stack frame | thread local vars | co-processor save area | - ---------------------------------------------------------------------------------- - | | - SP pxTopOfStack + ----LOW ADDRESSES ----------------------------------------HIGH ADDRESSES---------- + task stack | interrupt stack frame | thread local vars | co-processor save area | + ---------------------------------------------------------------------------------- + | | + SP pxTopOfStack - All parts are aligned to 16 byte boundary. */ - sp = (StackType_t *) (((UBaseType_t)pxTopOfStack - XT_CP_SIZE - thread_local_sz - XT_STK_FRMSZ) & ~0xf); + All parts are aligned to 16 byte boundary. */ + sp = (StackType_t *) (((UBaseType_t)pxTopOfStack - XT_CP_SIZE - thread_local_sz - XT_STK_FRMSZ) & ~0xf); - /* Clear the entire frame (do not use memset() because we don't depend on C library) */ - for (tp = sp; tp <= pxTopOfStack; ++tp) - *tp = 0; + /* Clear the entire frame (do not use memset() because we don't depend on C library) */ + for (tp = sp; tp <= pxTopOfStack; ++tp) { + *tp = 0; + } - frame = (XtExcFrame *) sp; + frame = (XtExcFrame *) sp; - /* Explicitly initialize certain saved registers */ - #if CONFIG_FREERTOS_TASK_FUNCTION_WRAPPER - frame->pc = (UBaseType_t) vPortTaskWrapper; /* task wrapper */ - #else - frame->pc = (UBaseType_t) pxCode; /* task entrypoint */ - #endif - frame->a0 = 0; /* to terminate GDB backtrace */ - frame->a1 = (UBaseType_t) sp + XT_STK_FRMSZ; /* physical top of stack frame */ - frame->exit = (UBaseType_t) _xt_user_exit; /* user exception exit dispatcher */ + /* Explicitly initialize certain saved registers */ +#if CONFIG_FREERTOS_TASK_FUNCTION_WRAPPER + frame->pc = (UBaseType_t) vPortTaskWrapper; /* task wrapper */ +#else + frame->pc = (UBaseType_t) pxCode; /* task entrypoint */ +#endif + frame->a0 = 0; /* to terminate GDB backtrace */ + frame->a1 = (UBaseType_t) sp + XT_STK_FRMSZ; /* physical top of stack frame */ + frame->exit = (UBaseType_t) _xt_user_exit; /* user exception exit dispatcher */ - /* Set initial PS to int level 0, EXCM disabled ('rfe' will enable), user mode. */ - /* Also set entry point argument parameter. */ - #ifdef __XTENSA_CALL0_ABI__ - #if CONFIG_FREERTOS_TASK_FUNCTION_WRAPPER - frame->a2 = (UBaseType_t) pxCode; - frame->a3 = (UBaseType_t) pvParameters; - #else - frame->a2 = (UBaseType_t) pvParameters; - #endif - frame->ps = PS_UM | PS_EXCM; - #else - /* + for windowed ABI also set WOE and CALLINC (pretend task was 'call4'd). */ - #if CONFIG_FREERTOS_TASK_FUNCTION_WRAPPER - frame->a6 = (UBaseType_t) pxCode; - frame->a7 = (UBaseType_t) pvParameters; - #else - frame->a6 = (UBaseType_t) pvParameters; - #endif - frame->ps = PS_UM | PS_EXCM | PS_WOE | PS_CALLINC(1); - #endif - - #ifdef XT_USE_SWPRI - /* Set the initial virtual priority mask value to all 1's. */ - frame->vpri = 0xFFFFFFFF; - #endif - - /* Init threadptr register and set up TLS run-time area. - * The diagram in port/riscv/port.c illustrates the calculations below. - */ - task_thread_local_start = (void *)(((uint32_t)pxTopOfStack - XT_CP_SIZE - thread_local_sz) & ~0xf); - memcpy(task_thread_local_start, &_thread_local_start, thread_local_sz); - threadptr = (uint32_t *)(sp + XT_STK_EXTRA); - /* Calculate THREADPTR value. - * The generated code will add THREADPTR value to a constant value determined at link time, - * to get the address of the TLS variable. - * The constant value is calculated by the linker as follows - * (search for 'tpoff' in elf32-xtensa.c in BFD): - * offset = address - tls_section_vma + align_up(TCB_SIZE, tls_section_alignment) - * where TCB_SIZE is hardcoded to 8. - * Note this is slightly different compared to the RISC-V port, where offset = address - tls_section_vma. - */ - const uint32_t tls_section_alignment = (uint32_t) &_flash_rodata_align; /* ALIGN value of .flash.rodata section */ - const uint32_t tcb_size = 8; /* Unrelated to FreeRTOS, this is the constant from BFD */ - const uint32_t base = (tcb_size + tls_section_alignment - 1) & (~(tls_section_alignment - 1)); - *threadptr = (uint32_t)task_thread_local_start - ((uint32_t)&_thread_local_start - (uint32_t)&_flash_rodata_start) - base; - - #if XCHAL_CP_NUM > 0 - /* Init the coprocessor save area (see xtensa_context.h) */ - /* No access to TCB here, so derive indirectly. Stack growth is top to bottom. - * //p = (uint32_t *) xMPUSettings->coproc_area; - */ - p = (uint32_t *)(((uint32_t) pxTopOfStack - XT_CP_SIZE) & ~0xf); - configASSERT( ( uint32_t ) p >= frame->a1 ); - p[0] = 0; - p[1] = 0; - p[2] = (((uint32_t) p) + 12 + XCHAL_TOTAL_SA_ALIGN - 1) & -XCHAL_TOTAL_SA_ALIGN; - #endif - - return sp; -} - -/*-----------------------------------------------------------*/ - -void vPortEndScheduler( void ) -{ - /* It is unlikely that the Xtensa port will get stopped. If required simply - disable the tick interrupt here. */ - abort(); -} - -/*-----------------------------------------------------------*/ - -BaseType_t xPortStartScheduler( void ) -{ - // Interrupts are disabled at this point and stack contains PS with enabled interrupts when task context is restored - - #if XCHAL_CP_NUM > 0 - /* Initialize co-processor management for tasks. Leave CPENABLE alone. */ - _xt_coproc_init(); - #endif - - /* Setup the hardware to generate the tick. */ - vPortSetupTimer(); - - port_xSchedulerRunning[xPortGetCoreID()] = 1; - - // Cannot be directly called from C; never returns - __asm__ volatile ("call0 _frxt_dispatch\n"); - - /* Should not get here. */ - return pdTRUE; -} -/*-----------------------------------------------------------*/ - -void vPortYieldOtherCore( BaseType_t coreid ) { - esp_crosscore_int_send_yield( coreid ); -} - -/*-----------------------------------------------------------*/ - -/* - * Used to set coprocessor area in stack. Current hack is to reuse MPU pointer for coprocessor area. - */ -#if portUSING_MPU_WRAPPERS -void vPortStoreTaskMPUSettings( xMPU_SETTINGS *xMPUSettings, const struct xMEMORY_REGION * const xRegions, StackType_t *pxBottomOfStack, uint32_t usStackDepth ) -{ - #if XCHAL_CP_NUM > 0 - xMPUSettings->coproc_area = ( StackType_t * ) ( ( uint32_t ) ( pxBottomOfStack + usStackDepth - 1 )); - xMPUSettings->coproc_area = ( StackType_t * ) ( ( ( portPOINTER_SIZE_TYPE ) xMPUSettings->coproc_area ) & ( ~( ( portPOINTER_SIZE_TYPE ) portBYTE_ALIGNMENT_MASK ) ) ); - xMPUSettings->coproc_area = ( StackType_t * ) ( ( ( uint32_t ) xMPUSettings->coproc_area - XT_CP_SIZE ) & ~0xf ); - - - /* NOTE: we cannot initialize the coprocessor save area here because FreeRTOS is going to - * clear the stack area after we return. This is done in pxPortInitialiseStack(). - */ - #endif -} - -void vPortReleaseTaskMPUSettings( xMPU_SETTINGS *xMPUSettings ) -{ - /* If task has live floating point registers somewhere, release them */ - _xt_coproc_release( xMPUSettings->coproc_area ); -} + /* Set initial PS to int level 0, EXCM disabled ('rfe' will enable), user mode. */ + /* Also set entry point argument parameter. */ +#ifdef __XTENSA_CALL0_ABI__ +#if CONFIG_FREERTOS_TASK_FUNCTION_WRAPPER + frame->a2 = (UBaseType_t) pxCode; + frame->a3 = (UBaseType_t) pvParameters; +#else + frame->a2 = (UBaseType_t) pvParameters; +#endif + frame->ps = PS_UM | PS_EXCM; +#else /* __XTENSA_CALL0_ABI__ */ + /* + for windowed ABI also set WOE and CALLINC (pretend task was 'call4'd). */ +#if CONFIG_FREERTOS_TASK_FUNCTION_WRAPPER + frame->a6 = (UBaseType_t) pxCode; + frame->a7 = (UBaseType_t) pvParameters; +#else + frame->a6 = (UBaseType_t) pvParameters; +#endif + frame->ps = PS_UM | PS_EXCM | PS_WOE | PS_CALLINC(1); +#endif /* __XTENSA_CALL0_ABI__ */ +#ifdef XT_USE_SWPRI + /* Set the initial virtual priority mask value to all 1's. */ + frame->vpri = 0xFFFFFFFF; #endif -/* - * Returns true if the current core is in ISR context; low prio ISR, med prio ISR or timer tick ISR. High prio ISRs - * aren't detected here, but they normally cannot call C code, so that should not be an issue anyway. - */ -BaseType_t xPortInIsrContext(void) -{ - unsigned int irqStatus; - BaseType_t ret; - irqStatus=portENTER_CRITICAL_NESTED(); - ret=(port_interruptNesting[xPortGetCoreID()] != 0); - portEXIT_CRITICAL_NESTED(irqStatus); - return ret; + /* Init threadptr register and set up TLS run-time area. + * The diagram in port/riscv/port.c illustrates the calculations below. + */ + task_thread_local_start = (void *)(((uint32_t)pxTopOfStack - XT_CP_SIZE - thread_local_sz) & ~0xf); + memcpy(task_thread_local_start, &_thread_local_start, thread_local_sz); + threadptr = (uint32_t *)(sp + XT_STK_EXTRA); + /* Calculate THREADPTR value. + * The generated code will add THREADPTR value to a constant value determined at link time, + * to get the address of the TLS variable. + * The constant value is calculated by the linker as follows + * (search for 'tpoff' in elf32-xtensa.c in BFD): + * offset = address - tls_section_vma + align_up(TCB_SIZE, tls_section_alignment) + * where TCB_SIZE is hardcoded to 8. + * Note this is slightly different compared to the RISC-V port, where offset = address - tls_section_vma. + */ + const uint32_t tls_section_alignment = (uint32_t) &_flash_rodata_align; /* ALIGN value of .flash.rodata section */ + const uint32_t tcb_size = 8; /* Unrelated to FreeRTOS, this is the constant from BFD */ + const uint32_t base = (tcb_size + tls_section_alignment - 1) & (~(tls_section_alignment - 1)); + *threadptr = (uint32_t)task_thread_local_start - ((uint32_t)&_thread_local_start - (uint32_t)&_flash_rodata_start) - base; + +#if XCHAL_CP_NUM > 0 + /* Init the coprocessor save area (see xtensa_context.h) */ + /* No access to TCB here, so derive indirectly. Stack growth is top to bottom. + * //p = (uint32_t *) xMPUSettings->coproc_area; + */ + p = (uint32_t *)(((uint32_t) pxTopOfStack - XT_CP_SIZE) & ~0xf); + configASSERT( ( uint32_t ) p >= frame->a1 ); + p[0] = 0; + p[1] = 0; + p[2] = (((uint32_t) p) + 12 + XCHAL_TOTAL_SA_ALIGN - 1) & -XCHAL_TOTAL_SA_ALIGN; +#endif /* XCHAL_CP_NUM */ + + return sp; +} + + + +/* ---------------------------------------------- Port Implementations ------------------------------------------------- + * + * ------------------------------------------------------------------------------------------------------------------ */ + +// --------------------- Interrupts ------------------------ + +BaseType_t xPortInIsrContext(void) +{ + unsigned int irqStatus; + BaseType_t ret; + irqStatus = portENTER_CRITICAL_NESTED(); + ret = (port_interruptNesting[xPortGetCoreID()] != 0); + portEXIT_CRITICAL_NESTED(irqStatus); + return ret; +} + +void vPortAssertIfInISR(void) +{ + configASSERT(xPortInIsrContext()); } -/* - * This function will be called in High prio ISRs. Returns true if the current core was in ISR context - * before calling into high prio ISR context. - */ BaseType_t IRAM_ATTR xPortInterruptedFromISRContext(void) { - return (port_interruptNesting[xPortGetCoreID()] != 0); + return (port_interruptNesting[xPortGetCoreID()] != 0); } +// ------------------ Critical Sections -------------------- + +void __attribute__((optimize("-O3"))) vPortEnterCritical(portMUX_TYPE *mux) +{ + BaseType_t oldInterruptLevel = portENTER_CRITICAL_NESTED(); + /* Interrupts may already be disabled (because we're doing this recursively) + * but we can't get the interrupt level after + * vPortCPUAquireMutex, because it also may mess with interrupts. + * Get it here first, then later figure out if we're nesting + * and save for real there. + */ + vPortCPUAcquireMutex( mux ); + BaseType_t coreID = xPortGetCoreID(); + BaseType_t newNesting = port_uxCriticalNesting[coreID] + 1; + port_uxCriticalNesting[coreID] = newNesting; + + if ( newNesting == 1 ) { + //This is the first time we get called. Save original interrupt level. + port_uxOldInterruptState[coreID] = oldInterruptLevel; + } +} + +void __attribute__((optimize("-O3"))) vPortExitCritical(portMUX_TYPE *mux) +{ + vPortCPUReleaseMutex( mux ); + BaseType_t coreID = xPortGetCoreID(); + BaseType_t nesting = port_uxCriticalNesting[coreID]; + + if (nesting > 0) { + nesting--; + port_uxCriticalNesting[coreID] = nesting; + + if ( nesting == 0 ) { + portEXIT_CRITICAL_NESTED(port_uxOldInterruptState[coreID]); + } + } +} + +// ---------------------- Yielding ------------------------- + +void vPortYieldOtherCore( BaseType_t coreid ) +{ + esp_crosscore_int_send_yield( coreid ); +} + +extern void _frxt_setup_switch( void ); //Defined in portasm.S + void IRAM_ATTR vPortEvaluateYieldFromISR(int argc, ...) { BaseType_t xYield; va_list ap; va_start(ap, argc); - if(argc) { + if (argc) { xYield = (BaseType_t)va_arg(ap, int); va_end(ap); } else { @@ -380,119 +336,115 @@ void IRAM_ATTR vPortEvaluateYieldFromISR(int argc, ...) } //Yield exists, so need evaluate it first then switch: - if(xYield == pdTRUE) { + if (xYield == pdTRUE) { traceISR_EXIT_TO_SCHEDULER(); _frxt_setup_switch(); } } -void vPortAssertIfInISR(void) +// ------------------- Hook Functions ---------------------- + +void __attribute__((weak)) vApplicationStackOverflowHook( TaskHandle_t xTask, char *pcTaskName ) { - configASSERT(xPortInIsrContext()); +#define ERR_STR1 "***ERROR*** A stack overflow in task " +#define ERR_STR2 " has been detected." + const char *str[] = {ERR_STR1, pcTaskName, ERR_STR2}; + + char buf[sizeof(ERR_STR1) + CONFIG_FREERTOS_MAX_TASK_NAME_LEN + sizeof(ERR_STR2) + 1 /* null char */] = { 0 }; + + char *dest = buf; + for (size_t i = 0 ; i < sizeof(str) / sizeof(str[0]); i++) { + dest = strcat(dest, str[i]); + } + esp_system_abort(buf); } +// ----------------------- System -------------------------- + +uint32_t xPortGetTickRateHz(void) +{ + return (uint32_t)configTICK_RATE_HZ; +} + + #define STACK_WATCH_AREA_SIZE 32 #define STACK_WATCH_POINT_NUMBER (SOC_CPU_WATCHPOINTS_NUM - 1) -void vPortSetStackWatchpoint( void* pxStackStart ) { - //Set watchpoint 1 to watch the last 32 bytes of the stack. - //Unfortunately, the Xtensa watchpoints can't set a watchpoint on a random [base - base+n] region because - //the size works by masking off the lowest address bits. For that reason, we futz a bit and watch the lowest 32 - //bytes of the stack we can actually watch. In general, this can cause the watchpoint to be triggered at most - //28 bytes early. The value 32 is chosen because it's larger than the stack canary, which in FreeRTOS is 20 bytes. - //This way, we make sure we trigger before/when the stack canary is corrupted, not after. - int addr=(int)pxStackStart; - addr=(addr+31)&(~31); - esp_cpu_set_watchpoint(STACK_WATCH_POINT_NUMBER, (char*)addr, 32, ESP_WATCHPOINT_STORE); -} - -uint32_t xPortGetTickRateHz(void) { - return (uint32_t)configTICK_RATE_HZ; -} - -void __attribute__((optimize("-O3"))) vPortEnterCritical(portMUX_TYPE *mux) +void vPortSetStackWatchpoint( void *pxStackStart ) { - BaseType_t oldInterruptLevel = portENTER_CRITICAL_NESTED(); - /* Interrupts may already be disabled (because we're doing this recursively) - * but we can't get the interrupt level after - * vPortCPUAquireMutex, because it also may mess with interrupts. - * Get it here first, then later figure out if we're nesting - * and save for real there. - */ - vPortCPUAcquireMutex( mux ); - BaseType_t coreID = xPortGetCoreID(); - BaseType_t newNesting = port_uxCriticalNesting[coreID] + 1; - port_uxCriticalNesting[coreID] = newNesting; - - if( newNesting == 1 ) - { - //This is the first time we get called. Save original interrupt level. - port_uxOldInterruptState[coreID] = oldInterruptLevel; - } + //Set watchpoint 1 to watch the last 32 bytes of the stack. + //Unfortunately, the Xtensa watchpoints can't set a watchpoint on a random [base - base+n] region because + //the size works by masking off the lowest address bits. For that reason, we futz a bit and watch the lowest 32 + //bytes of the stack we can actually watch. In general, this can cause the watchpoint to be triggered at most + //28 bytes early. The value 32 is chosen because it's larger than the stack canary, which in FreeRTOS is 20 bytes. + //This way, we make sure we trigger before/when the stack canary is corrupted, not after. + int addr = (int)pxStackStart; + addr = (addr + 31) & (~31); + esp_cpu_set_watchpoint(STACK_WATCH_POINT_NUMBER, (char *)addr, 32, ESP_WATCHPOINT_STORE); } -void __attribute__((optimize("-O3"))) vPortExitCritical(portMUX_TYPE *mux) +/* ---------------------------------------------- Misc Implementations ------------------------------------------------- + * + * ------------------------------------------------------------------------------------------------------------------ */ + +// -------------------- Co-Processor ----------------------- + +/* + * Used to set coprocessor area in stack. Current hack is to reuse MPU pointer for coprocessor area. + */ +#if portUSING_MPU_WRAPPERS +void vPortStoreTaskMPUSettings( xMPU_SETTINGS *xMPUSettings, const struct xMEMORY_REGION *const xRegions, StackType_t *pxBottomOfStack, uint32_t usStackDepth ) { - vPortCPUReleaseMutex( mux ); - BaseType_t coreID = xPortGetCoreID(); - BaseType_t nesting = port_uxCriticalNesting[coreID]; +#if XCHAL_CP_NUM > 0 + xMPUSettings->coproc_area = ( StackType_t * ) ( ( uint32_t ) ( pxBottomOfStack + usStackDepth - 1 )); + xMPUSettings->coproc_area = ( StackType_t * ) ( ( ( portPOINTER_SIZE_TYPE ) xMPUSettings->coproc_area ) & ( ~( ( portPOINTER_SIZE_TYPE ) portBYTE_ALIGNMENT_MASK ) ) ); + xMPUSettings->coproc_area = ( StackType_t * ) ( ( ( uint32_t ) xMPUSettings->coproc_area - XT_CP_SIZE ) & ~0xf ); - if(nesting > 0) - { - nesting--; - port_uxCriticalNesting[coreID] = nesting; - if( nesting == 0 ) - { - portEXIT_CRITICAL_NESTED(port_uxOldInterruptState[coreID]); - } - } + /* NOTE: we cannot initialize the coprocessor save area here because FreeRTOS is going to + * clear the stack area after we return. This is done in pxPortInitialiseStack(). + */ +#endif } -void __attribute__((weak)) vApplicationStackOverflowHook( TaskHandle_t xTask, char *pcTaskName ) +void vPortReleaseTaskMPUSettings( xMPU_SETTINGS *xMPUSettings ) { - #define ERR_STR1 "***ERROR*** A stack overflow in task " - #define ERR_STR2 " has been detected." - const char *str[] = {ERR_STR1, pcTaskName, ERR_STR2}; - - char buf[sizeof(ERR_STR1) + CONFIG_FREERTOS_MAX_TASK_NAME_LEN + sizeof(ERR_STR2) + 1 /* null char */] = { 0 }; - - char *dest = buf; - for (size_t i = 0 ; i < sizeof(str)/ sizeof(str[0]); i++) { - dest = strcat(dest, str[i]); - } - esp_system_abort(buf); + /* If task has live floating point registers somewhere, release them */ + _xt_coproc_release( xMPUSettings->coproc_area ); } +#endif /* portUSING_MPU_WRAPPERS */ + +// --------------------- App Start-up ---------------------- #if !CONFIG_FREERTOS_UNICORE void esp_startup_start_app_other_cores(void) { - // For now, we only support up to two core: 0 and 1. - if (xPortGetCoreID() >= 2) { - abort(); - } + // For now, we only support up to two core: 0 and 1. + if (xPortGetCoreID() >= 2) { + abort(); + } - // Wait for FreeRTOS initialization to finish on PRO CPU - while (port_xSchedulerRunning[0] == 0) { - ; - } + // Wait for FreeRTOS initialization to finish on PRO CPU + while (port_xSchedulerRunning[0] == 0) { + ; + } #if CONFIG_APPTRACE_ENABLE - // [refactor-todo] move to esp_system initialization - esp_err_t err = esp_apptrace_init(); - assert(err == ESP_OK && "Failed to init apptrace module on APP CPU!"); + // [refactor-todo] move to esp_system initialization + esp_err_t err = esp_apptrace_init(); + assert(err == ESP_OK && "Failed to init apptrace module on APP CPU!"); #endif #if CONFIG_ESP_INT_WDT - //Initialize the interrupt watch dog for CPU1. - esp_int_wdt_cpu_init(); + //Initialize the interrupt watch dog for CPU1. + esp_int_wdt_cpu_init(); #endif - esp_crosscore_int_init(); + esp_crosscore_int_init(); - ESP_EARLY_LOGI(TAG, "Starting scheduler on APP CPU."); - xPortStartScheduler(); - abort(); /* Only get to here if FreeRTOS somehow very broken */ + ESP_EARLY_LOGI(TAG, "Starting scheduler on APP CPU."); + xPortStartScheduler(); + abort(); /* Only get to here if FreeRTOS somehow very broken */ } #endif // !CONFIG_FREERTOS_UNICORE @@ -502,12 +454,12 @@ void esp_startup_start_app(void) { #if !CONFIG_ESP_INT_WDT #if CONFIG_ESP32_ECO3_CACHE_LOCK_FIX - assert(!soc_has_cache_lock_bug() && "ESP32 Rev 3 + Dual Core + PSRAM requires INT WDT enabled in project config!"); + assert(!soc_has_cache_lock_bug() && "ESP32 Rev 3 + Dual Core + PSRAM requires INT WDT enabled in project config!"); #endif #endif - esp_startup_start_app_common(); + esp_startup_start_app_common(); - ESP_LOGI(TAG, "Starting scheduler on PRO CPU."); - vTaskStartScheduler(); + ESP_LOGI(TAG, "Starting scheduler on PRO CPU."); + vTaskStartScheduler(); } diff --git a/tools/ci/check_copyright_ignore.txt b/tools/ci/check_copyright_ignore.txt index ba948ef930..2ed9b337d0 100644 --- a/tools/ci/check_copyright_ignore.txt +++ b/tools/ci/check_copyright_ignore.txt @@ -1382,10 +1382,10 @@ components/freertos/croutine.c components/freertos/esp_additions/task_snapshot.c components/freertos/event_groups.c components/freertos/freertos_v8_compat.c +components/freertos/include/esp_additions/freertos/FreeRTOSConfig.h components/freertos/include/esp_additions/freertos/task_snapshot.h components/freertos/include/esp_additions/freertos_tasks_c_additions.h components/freertos/include/freertos/FreeRTOS.h -components/freertos/include/freertos/FreeRTOSConfig.h components/freertos/include/freertos/StackMacros.h components/freertos/include/freertos/atomic.h components/freertos/include/freertos/croutine.h @@ -1404,14 +1404,13 @@ components/freertos/include/freertos/stream_buffer.h components/freertos/include/freertos/task.h components/freertos/include/freertos/timers.h components/freertos/list.c -components/freertos/port/linux/include/freertos/FreeRTOSConfig.h +components/freertos/port/linux/include/freertos/FreeRTOSConfig_arch.h components/freertos/port/linux/include/freertos/portmacro.h -components/freertos/port/port_common.c -components/freertos/port/riscv/include/freertos/FreeRTOSConfig.h +components/freertos/port/riscv/include/freertos/FreeRTOSConfig_arch.h components/freertos/port/riscv/include/freertos/portbenchmark.h components/freertos/port/riscv/include/freertos/portmacro.h components/freertos/port/riscv/port.c -components/freertos/port/xtensa/include/freertos/FreeRTOSConfig.h +components/freertos/port/xtensa/include/freertos/FreeRTOSConfig_arch.h components/freertos/port/xtensa/include/freertos/portbenchmark.h components/freertos/port/xtensa/include/freertos/portmacro.h components/freertos/port/xtensa/include/freertos/portmacro_priv.h