Merge branch 'feature/freertos_smp_enable_riscv_port' into 'master'

freertos-smp: add support for RISC-V targets on FreeRTOS SMP

See merge request espressif/esp-idf!18128
This commit is contained in:
Sudeep Mohanty
2022-06-14 04:17:54 +08:00
8 changed files with 1481 additions and 3 deletions

View File

@@ -28,6 +28,24 @@ if(CONFIG_FREERTOS_SMP)
"FreeRTOS-Kernel-SMP/portable/xtensa/include/freertos" "FreeRTOS-Kernel-SMP/portable/xtensa/include/freertos"
"FreeRTOS-Kernel-SMP/portable/xtensa" "FreeRTOS-Kernel-SMP/portable/xtensa"
.) .)
elseif(CONFIG_IDF_TARGET_ARCH_RISCV)
set(srcs
"FreeRTOS-Kernel-SMP/portable/riscv/port.c"
"FreeRTOS-Kernel-SMP/portable/riscv/portasm.S")
set(include_dirs
FreeRTOS-Kernel-SMP/include
esp_additions/include/freertos # For files with #include "FreeRTOSConfig.h"
FreeRTOS-Kernel-SMP/portable/riscv/include
esp_additions/include # For files with #include "freertos/FreeRTOSConfig.h"
FreeRTOS-Kernel-SMP/portable/riscv/include/freertos)
set(private_include_dirs
FreeRTOS-Kernel-SMP/portable/riscv/include/freertos
FreeRTOS-Kernel-SMP/portable/riscv
.)
endif() endif()
list(APPEND srcs list(APPEND srcs

View File

@@ -0,0 +1,290 @@
/*
* SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef FREERTOS_CONFIG_SMP_H
#define FREERTOS_CONFIG_SMP_H
#include "sdkconfig.h"
/*
This file get's pulled into assembly sources. Therefore, some includes need to be wrapped in #ifndef __ASSEMBLER__
*/
#ifndef __ASSEMBLER__
#include <assert.h> //For configASSERT()
#endif /* def __ASSEMBLER__ */
/* ------------------------------------------------ ESP-IDF Additions --------------------------------------------------
*
* ------------------------------------------------------------------------------------------------------------------ */
#ifndef configISR_STACK_SIZE
#define configISR_STACK_SIZE (CONFIG_FREERTOS_ISR_STACKSIZE)
#endif
/* ----------------------------------------------------- Helpers -------------------------------------------------------
* - Macros that the FreeRTOS configuration macros depend on
* ------------------------------------------------------------------------------------------------------------------ */
/* Higher stack checker modes cause overhead on each function call */
#if CONFIG_STACK_CHECK_ALL || CONFIG_STACK_CHECK_STRONG
#define STACK_OVERHEAD_CHECKER 256
#else
#define STACK_OVERHEAD_CHECKER 0
#endif
/* with optimizations disabled, scheduler uses additional stack */
#if CONFIG_COMPILER_OPTIMIZATION_NONE
#define STACK_OVERHEAD_OPTIMIZATION 320
#else
#define STACK_OVERHEAD_OPTIMIZATION 0
#endif
/* apptrace mdule increases minimum stack usage */
#if CONFIG_APPTRACE_ENABLE
#define STACK_OVERHEAD_APPTRACE 1280
#else
#define STACK_OVERHEAD_APPTRACE 0
#endif
/* Stack watchpoint decreases minimum usable stack size by up to 60 bytes.
See FreeRTOS FREERTOS_WATCHPOINT_END_OF_STACK option in Kconfig. */
#if CONFIG_FREERTOS_WATCHPOINT_END_OF_STACK
#define STACK_OVERHEAD_WATCHPOINT 60
#else
#define STACK_OVERHEAD_WATCHPOINT 0
#endif
#define configSTACK_OVERHEAD_TOTAL ( \
STACK_OVERHEAD_CHECKER + \
STACK_OVERHEAD_OPTIMIZATION + \
STACK_OVERHEAD_APPTRACE + \
STACK_OVERHEAD_WATCHPOINT \
)
/* ----------------------------------------------------- Helpers -------------------------------------------------------
* - Macros that the FreeRTOS configuration macros depend on
* ------------------------------------------------------------------------------------------------------------------ */
/* ------------------------------------------------- FreeRTOS Config ---------------------------------------------------
* - All Vanilla FreeRTOS configuration goes into this section
* - Keep this section in-sync with the corresponding version of single-core upstream version of FreeRTOS
* - Don't put any SMP or ESP-IDF exclusive FreeRTOS configurations here. Those go into the next section
* - Not all FreeRTOS configuration are listed. Some configurations have default values set in FreeRTOS.h thus don't
* need to be explicitly defined.
* ------------------------------------------------------------------------------------------------------------------ */
/*-----------------------------------------------------------
* Application specific definitions.
*
* These definitions should be adjusted for your particular hardware and
* application requirements.
*
* THESE PARAMETERS ARE DESCRIBED WITHIN THE 'CONFIGURATION' SECTION OF THE
* FreeRTOS API DOCUMENTATION AVAILABLE ON THE FreeRTOS.org WEB SITE.
*
* See http://www.freertos.org/a00110.html
*----------------------------------------------------------*/
// ------------------ Scheduler Related --------------------
#define configUSE_PREEMPTION 1
#define configUSE_TASK_PREEMPTION_DISABLE 1
#define configUSE_TICKLESS_IDLE 0
#define configCPU_CLOCK_HZ (CONFIG_ESP_DEFAULT_CPU_FREQ_MHZ * 1000000)
#define configTICK_RATE_HZ CONFIG_FREERTOS_HZ
#define configMAX_PRIORITIES ( 25 ) //This has impact on speed of search for highest priority
#define configMINIMAL_STACK_SIZE ( 768 + configSTACK_OVERHEAD_TOTAL )
#define configUSE_TIME_SLICING 1
#define configUSE_16_BIT_TICKS 0
#define configIDLE_SHOULD_YIELD 0 //Todo: Check this
#define configKERNEL_INTERRUPT_PRIORITY 1 //Todo: This currently isn't used anywhere
#if __XTENSA__
#define configMAX_API_CALL_INTERRUPT_PRIORITY XCHAL_EXCM_LEVEL
#else // RISC-V
#define configMAX_API_CALL_INTERRUPT_PRIORITY 0
#endif // __XTENSA__
#define configUSE_PORT_OPTIMISED_TASK_SELECTION 0 //SMP currently does not support optimized selection
// -------------------- FreeRTOS SMP -----------------------
#ifdef CONFIG_FREERTOS_UNICORE
#define configNUM_CORES 1
#else
#define configNUM_CORES 2
#endif
#define configUSE_CORE_AFFINITY 1
#define configRUN_MULTIPLE_PRIORITIES 1
#define configUSE_MINIMAL_IDLE_HOOK 1 // This is always enabled to call IDF style idle hooks, by can be "--Wl,--wrap" if users enable CONFIG_FREERTOS_USE_MINIMAL_IDLE_HOOK
// ------------- Synchronization Primitives ----------------
#define configUSE_MUTEXES 1
#define configUSE_RECURSIVE_MUTEXES 1
#define configUSE_COUNTING_SEMAPHORES 1
#define configUSE_QUEUE_SETS 1
#define configQUEUE_REGISTRY_SIZE CONFIG_FREERTOS_QUEUE_REGISTRY_SIZE
#define configUSE_TASK_NOTIFICATIONS 1
#define configTASK_NOTIFICATION_ARRAY_ENTRIES 1
// ----------------------- System --------------------------
#define configMAX_TASK_NAME_LEN CONFIG_FREERTOS_MAX_TASK_NAME_LEN
#if ( CONFIG_FREERTOS_TLSP_DELETION_CALLBACKS )
/* If thread local storage pointer deletion callbacks are registered
* then we double the storage space reserved for the thread local
* storage pointers in the task TCB. The first half of the storage area
* is used to store the TLS pointers themselves while the second half
* is used to store the respective deletion callbacks.
*/
#define configNUM_THREAD_LOCAL_STORAGE_POINTERS ( CONFIG_FREERTOS_THREAD_LOCAL_STORAGE_POINTERS * 2 )
#else
#define configNUM_THREAD_LOCAL_STORAGE_POINTERS CONFIG_FREERTOS_THREAD_LOCAL_STORAGE_POINTERS
#endif // CONFIG_FREERTOS_TLSP_DELETION_CALLBACKS
#define configSTACK_DEPTH_TYPE uint32_t
#define configUSE_NEWLIB_REENTRANT 1
#define configNEWLIB_REENTRANT_IS_DYNAMIC 1 // IDF Newlib supports dynamic reentrancy.
// We provide our own __getreent() function
#define configENABLE_BACKWARD_COMPATIBILITY 0
#define configASSERT(a) assert(a)
#define configINCLUDE_FREERTOS_TASK_C_ADDITIONS_H 1
// ----------------------- Memory -------------------------
#define configSUPPORT_STATIC_ALLOCATION 1
#define configSUPPORT_DYNAMIC_ALLOCATION 1
//We define the heap to span all of the non-statically-allocated shared RAM. ToDo: Make sure there
//is some space left for the app and main cpu when running outside of a thread.
#define configTOTAL_HEAP_SIZE (&_heap_end - &_heap_start)//( ( size_t ) (64 * 1024) )
#define configAPPLICATION_ALLOCATED_HEAP 1
#define configSTACK_ALLOCATION_FROM_SEPARATE_HEAP 0 //Todo: Check this
// ------------------------ Hooks --------------------------
#if CONFIG_FREERTOS_USE_IDLE_HOOK
#define configUSE_IDLE_HOOK 1
#else
#define configUSE_IDLE_HOOK 0
#endif
#define configUSE_TICK_HOOK 1
#if CONFIG_FREERTOS_CHECK_STACKOVERFLOW_NONE
#define configCHECK_FOR_STACK_OVERFLOW 0
#elif CONFIG_FREERTOS_CHECK_STACKOVERFLOW_PTRVAL
#define configCHECK_FOR_STACK_OVERFLOW 1
#elif CONFIG_FREERTOS_CHECK_STACKOVERFLOW_CANARY
#define configCHECK_FOR_STACK_OVERFLOW 2
#endif
#define configRECORD_STACK_HIGH_ADDRESS 1 // This must be set as the port requires TCB.pxEndOfStack
// ------------------- Run-time Stats ----------------------
#ifdef CONFIG_FREERTOS_GENERATE_RUN_TIME_STATS
#define configGENERATE_RUN_TIME_STATS 1 /* Used by vTaskGetRunTimeStats() */
#endif
#ifdef CONFIG_FREERTOS_USE_TRACE_FACILITY
#define configUSE_TRACE_FACILITY 1 /* Used by uxTaskGetSystemState(), and other trace facility functions */
#endif
#ifdef CONFIG_FREERTOS_USE_STATS_FORMATTING_FUNCTIONS
#define configUSE_STATS_FORMATTING_FUNCTIONS 1 /* Used by vTaskList() */
#endif
// -------------------- Co-routines -----------------------
#define configUSE_CO_ROUTINES 0
#define configMAX_CO_ROUTINE_PRIORITIES 2
// ------------------- Software Timer ----------------------
#define configUSE_TIMERS 1
#define configTIMER_TASK_PRIORITY CONFIG_FREERTOS_TIMER_TASK_PRIORITY
#define configTIMER_QUEUE_LENGTH CONFIG_FREERTOS_TIMER_QUEUE_LENGTH
#define configTIMER_TASK_STACK_DEPTH CONFIG_FREERTOS_TIMER_TASK_STACK_DEPTH
// -------------------- API Includes -----------------------
#define configENABLE_BACKWARD_COMPATIBILITY 0
#define INCLUDE_vTaskPrioritySet 1
#define INCLUDE_uxTaskPriorityGet 1
#define INCLUDE_vTaskDelete 1
#define INCLUDE_vTaskSuspend 1
#define INCLUDE_xTaskDelayUntil 1
#define INCLUDE_vTaskDelay 1
#define INCLUDE_xTaskGetIdleTaskHandle 1
#define INCLUDE_xTaskAbortDelay 1
#define INCLUDE_xSemaphoreGetMutexHolder 1
#define INCLUDE_xTaskGetHandle 1
#define INCLUDE_uxTaskGetStackHighWaterMark 1
#define INCLUDE_uxTaskGetStackHighWaterMark2 0
#define INCLUDE_eTaskGetState 1
#define INCLUDE_xTaskResumeFromISR 1
#define INCLUDE_xTimerPendFunctionCall 1
#define INCLUDE_xTaskGetSchedulerState 1
#define INCLUDE_xTaskGetCurrentTaskHandle 1
// -------------------- Trace Macros -----------------------
/*
For trace macros.
Note: Include trace macros here and not above as trace macros are dependent on some of the FreeRTOS configs
*/
#ifndef __ASSEMBLER__
#if CONFIG_SYSVIEW_ENABLE
#include "SEGGER_SYSVIEW_FreeRTOS.h"
#undef INLINE // to avoid redefinition
#endif //CONFIG_SYSVIEW_ENABLE
#endif /* def __ASSEMBLER__ */
/*
Default values for trace macros added by ESP-IDF and are not part of Vanilla FreeRTOS
*/
#ifndef traceISR_EXIT
#define traceISR_EXIT()
#endif
#ifndef traceISR_ENTER
#define traceISR_ENTER(_n_)
#endif
/* ------------------------------------------------ IDF Compatibility --------------------------------------------------
* - We need these in order for ESP-IDF to compile
* ------------------------------------------------------------------------------------------------------------------ */
#define portNUM_PROCESSORS configNUM_CORES
#ifdef CONFIG_FREERTOS_VTASKLIST_INCLUDE_COREID
#define configTASKLIST_INCLUDE_COREID 1
#endif
#ifndef __ASSEMBLER__
#if CONFIG_APPTRACE_SV_ENABLE
extern uint32_t port_switch_flag[];
#define os_task_switch_is_pended(_cpu_) (port_switch_flag[_cpu_])
#else
#define os_task_switch_is_pended(_cpu_) (false)
#endif
#endif
// ---------------------- Features -------------------------
/* These currently aren't required, but could be useful additions in the future */
#if 0
#ifndef configIDLE_TASK_STACK_SIZE
#define configIDLE_TASK_STACK_SIZE CONFIG_FREERTOS_IDLE_TASK_STACKSIZE
#endif
#if CONFIG_FREERTOS_CHECK_MUTEX_GIVEN_BY_OWNER
#define configCHECK_MUTEX_GIVEN_BY_OWNER 1
#else
#define configCHECK_MUTEX_GIVEN_BY_OWNER 0
#endif
#endif //0
// -------------------- Compatibility ----------------------
// backward compatibility for 4.4
#define xTaskRemoveFromUnorderedEventList vTaskRemoveFromUnorderedEventList
#endif /* FREERTOS_CONFIG_SMP_H */

View File

@@ -0,0 +1,373 @@
/*
* SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once
#include "sdkconfig.h"
#include <stdint.h>
#include "spinlock.h"
#include "soc/interrupt_core0_reg.h"
#include "esp_macros.h"
#include "hal/cpu_hal.h"
#include "esp_private/crosscore_int.h"
#ifdef __cplusplus
extern "C" {
#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 first as they are used further down in this file
* ------------------------------------------------------------------------------------------------------------------ */
#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
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
#else
typedef uint32_t TickType_t;
#define portMAX_DELAY ( TickType_t ) 0xffffffffUL
#endif
/* 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 )
// 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 1
#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 needed to implement the FreeRTOS porting interface
* - These must come before definition/declaration of the FreeRTOS porting interface
* ------------------------------------------------------------------------------------------------------------------ */
/* ---------------------- Spinlocks ------------------------
- Spinlocks added to match API with SMP FreeRTOS. Single core RISC-V does not need spin locks
* ------------------------------------------------------ */
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 */
#define portMUX_INITIALIZE(mux) spinlock_initialize(mux) /*< Initialize a spinlock to its unlocked state */
// --------------------- Interrupts ------------------------
BaseType_t xPortCheckIfInISR(void);
// ------------------ Critical Sections --------------------
/*
These are always called with interrupts already disabled. We simply need to get/release the spinlocks
*/
extern portMUX_TYPE port_xTaskLock;
extern portMUX_TYPE port_xISRLock;
void vPortTakeLock( portMUX_TYPE *lock );
void vPortReleaseLock( portMUX_TYPE *lock );
// ---------------------- Yielding -------------------------
void vPortYield( void );
static inline void __attribute__((always_inline)) vPortYieldCore( BaseType_t xCoreID );
/**
* @brief Set interrupt mask and return current interrupt enable register
*
* @return UBaseType_t Current interrupt enable register before set
*/
UBaseType_t ulPortSetInterruptMask(void);
/**
* @brief Clear current interrupt mask and set given mask
*
* @param mask Interrupt mask
*/
void vPortClearInterruptMask(UBaseType_t mask);
/**
* @brief Perform a context switch from an ISR
*/
void vPortYieldFromISR(void);
#define portYIELD_FROM_ISR_CHECK(x) ({ \
if ( (x) == pdTRUE ) { \
vPortYieldFromISR(); \
} \
})
#define portYIELD_FROM_ISR_NO_CHECK() vPortYieldFromISR()
// ----------------------- System --------------------------
static inline BaseType_t __attribute__((always_inline)) xPortGetCoreID( void );
// ----------------------- TCB Cleanup --------------------------
void vPortCleanUpTCB ( void *pxTCB );
/* ------------------------------------------- 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
* ------------------------------------------------------------------------------------------------------------------ */
// --------------------- Interrupts ------------------------
#define portDISABLE_INTERRUPTS() ulPortSetInterruptMask()
#define portENABLE_INTERRUPTS() vPortClearInterruptMask(1)
#define portRESTORE_INTERRUPTS(x) vPortClearInterruptMask(x)
// ------------------ Critical Sections --------------------
#define portGET_TASK_LOCK() vPortTakeLock(&port_xTaskLock)
#define portRELEASE_TASK_LOCK() vPortReleaseLock(&port_xTaskLock)
#define portGET_ISR_LOCK() vPortTakeLock(&port_xISRLock)
#define portRELEASE_ISR_LOCK() vPortReleaseLock(&port_xISRLock)
//Critical sections used by FreeRTOS SMP
extern void vTaskEnterCritical( void );
extern void vTaskExitCritical( void );
#define portENTER_CRITICAL_SMP() vTaskEnterCritical();
#define portEXIT_CRITICAL_SMP() vTaskExitCritical();
#if defined(__cplusplus) && (__cplusplus > 201703L)
#define portENTER_CRITICAL(...) CHOOSE_MACRO_VA_ARG(portENTER_CRITICAL_IDF, portENTER_CRITICAL_SMP __VA_OPT__(,) __VA_ARGS__)(__VA_ARGS__)
#define portEXIT_CRITICAL(...) CHOOSE_MACRO_VA_ARG(portEXIT_CRITICAL_IDF, portEXIT_CRITICAL_SMP __VA_OPT__(,) __VA_ARGS__)(__VA_ARGS__)
#else
#define portENTER_CRITICAL(...) CHOOSE_MACRO_VA_ARG(portENTER_CRITICAL_IDF, portENTER_CRITICAL_SMP, ##__VA_ARGS__)(__VA_ARGS__)
#define portEXIT_CRITICAL(...) CHOOSE_MACRO_VA_ARG(portEXIT_CRITICAL_IDF, portEXIT_CRITICAL_SMP, ##__VA_ARGS__)(__VA_ARGS__)
#endif
#define portSET_INTERRUPT_MASK_FROM_ISR() ({ \
unsigned int cur_level; \
cur_level = REG_READ(INTERRUPT_CORE0_CPU_INT_THRESH_REG); \
vTaskEnterCritical(); \
cur_level; \
})
#define portCLEAR_INTERRUPT_MASK_FROM_ISR(x) ({ \
vTaskExitCritical(); \
portRESTORE_INTERRUPTS(x); \
})
// ---------------------- Yielding -------------------------
#define portYIELD() vPortYield()
#if defined(__cplusplus) && (__cplusplus > 201703L)
#define portYIELD_FROM_ISR(...) CHOOSE_MACRO_VA_ARG(portYIELD_FROM_ISR_CHECK, portYIELD_FROM_ISR_NO_CHECK __VA_OPT__(,) __VA_ARGS__)(__VA_ARGS__)
#else
#define portYIELD_FROM_ISR(...) CHOOSE_MACRO_VA_ARG(portYIELD_FROM_ISR_CHECK, portYIELD_FROM_ISR_NO_CHECK, ##__VA_ARGS__)(__VA_ARGS__)
#endif
#define portYIELD_CORE(x) vPortYieldCore(x)
// ----------------------- System --------------------------
#define portGET_CORE_ID() xPortGetCoreID()
#define portCHECK_IF_IN_ISR() xPortCheckIfInISR()
// ------------------- 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
// ------------------- TCB Cleanup ----------------------
#define portCLEAN_UP_TCB( pxTCB ) vPortCleanUpTCB( pxTCB )
/* --------------------------------------------- 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
* ------------------------------------------------------------------------------------------------------------------ */
// ---------------------- Yielding -------------------------
static inline void __attribute__((always_inline)) vPortYieldCore( BaseType_t xCoreID )
{
esp_crosscore_int_send_yield( xCoreID );
}
// ----------------------- System --------------------------
static inline BaseType_t __attribute__((always_inline)) xPortGetCoreID( void )
{
return (BaseType_t) cpu_hal_get_core_id();
}
/* ------------------------------------------------ IDF Compatibility --------------------------------------------------
* - These macros and functions need to be defined for IDF to compile
* ------------------------------------------------------------------------------------------------------------------ */
static inline BaseType_t xPortInIsrContext(void)
{
//Just call the FreeRTOS port interface version
return xPortCheckIfInISR();
}
// ---------------------- Spinlocks ------------------------
/**
* @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)
{
compare_and_set_native(addr, compare, 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)
{
#if defined(CONFIG_SPIRAM)
compare_and_set_extram(addr, compare, set);
#endif
}
// ------------------ Critical Sections --------------------
/*
IDF style critical sections which are orthogonal to FreeRTOS critical sections. However, on single core, the IDF style
critical sections simply disable interrupts, thus we discard the lock and timeout arguments.
*/
void vPortEnterCriticalIDF(void);
void vPortExitCriticalIDF(void);
//IDF task critical sections
#define portTRY_ENTER_CRITICAL(lock, timeout) {((void) lock; (void) timeout; vPortEnterCriticalIDF(); pdPASS;)}
#define portENTER_CRITICAL_IDF(lock) ({(void) lock; vPortEnterCriticalIDF();})
#define portEXIT_CRITICAL_IDF(lock) ({(void) lock; vPortExitCriticalIDF();})
//IDF ISR critical sections
#define portTRY_ENTER_CRITICAL_ISR(lock, timeout) {((void) lock; (void) timeout; vPortEnterCriticalIDF(); pdPASS;)}
#define portENTER_CRITICAL_ISR(lock) ({(void) lock; vPortEnterCriticalIDF();})
#define portEXIT_CRITICAL_ISR(lock) ({(void) lock; vPortExitCriticalIDF();})
//IDF safe critical sections (they're the same)
#define portENTER_CRITICAL_SAFE(lock) ({(void) lock; vPortEnterCriticalIDF();})
#define portEXIT_CRITICAL_SAFE(lock) ({(void) lock; vPortExitCriticalIDF();})
// ---------------------- 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);
}
// ----------------------- System --------------------------
void vPortSetStackWatchpoint(void *pxStackStart);
#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
#define portTcbMemoryCaps (MALLOC_CAP_INTERNAL|MALLOC_CAP_8BIT)
#define portStackMemoryCaps (MALLOC_CAP_INTERNAL|MALLOC_CAP_8BIT)
/* ------------------------------------------------------ Misc ---------------------------------------------------------
* - Miscellaneous porting macros
* - These are not port of the FreeRTOS porting interface, but are used by other FreeRTOS dependent components
* ------------------------------------------------------------------------------------------------------------------ */
// --------------------- App-Trace -------------------------
#if CONFIG_APPTRACE_SV_ENABLE
extern int xPortSwitchFlag;
#define os_task_switch_is_pended(_cpu_) (xPortSwitchFlag)
#else
#define os_task_switch_is_pended(_cpu_) (false)
#endif
// --------------------- Debugging -------------------------
#if CONFIG_FREERTOS_ASSERT_ON_UNTESTED_FUNCTION
#define UNTESTED_FUNCTION() do{ esp_rom_printf("Untested FreeRTOS function %s\r\n", __FUNCTION__); configASSERT(false); } while(0)
#else
#define UNTESTED_FUNCTION()
#endif
// --------------- Compatibility Includes ------------------
/*
ESP-IDF currently does not have a "Include what you use" policy. A lot of files implicitly pull in API through
portmacro.h. Therefore, we need to keep these headers around for now to allow the rest of IDF to compile.
[refactor-todo] Clean up ESP-IDF inclusion dependencies and add a inclusion check.
*/
#include <stdlib.h>
#include <stdbool.h>
#include <stdarg.h>
#include "esp_attr.h"
#include "esp_newlib.h"
#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 */
/* [refactor-todo] These includes are not directly used in this file. They are kept into to prevent a breaking change. Remove these. */
#include <limits.h>
/* [refactor-todo] introduce a port wrapper function to avoid including esp_timer.h into the public header */
#if CONFIG_FREERTOS_RUN_TIME_STATS_USING_ESP_TIMER
#include "esp_timer.h"
#endif
#ifdef __cplusplus
}
#endif

View File

@@ -0,0 +1,693 @@
/*
* SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include "sdkconfig.h"
#include <string.h>
#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 "esp_private/crosscore_int.h"
#include "esp_attr.h"
#include "esp_system.h"
#include "esp_heap_caps_init.h"
#include "esp_private/esp_int_wdt.h"
#include "esp_task_wdt.h"
#include "esp_task.h"
#include "esp_intr_alloc.h"
#include "esp_log.h"
#include "FreeRTOS.h" /* This pulls in portmacro.h */
#include "task.h"
#include "portmacro.h"
#ifdef CONFIG_FREERTOS_SYSTICK_USES_SYSTIMER
#include "soc/periph_defs.h"
#include "soc/system_reg.h"
#include "hal/systimer_hal.h"
#include "hal/systimer_ll.h"
#endif
#ifdef CONFIG_PM_TRACE
#include "esp_private/pm_trace.h"
#endif //CONFIG_PM_TRACE
/* ---------------------------------------------------- Variables ------------------------------------------------------
*
* ------------------------------------------------------------------------------------------------------------------ */
static const char *TAG = "cpu_start"; // [refactor-todo]: might be appropriate to change in the future, but
BaseType_t uxSchedulerRunning = 0;
volatile UBaseType_t uxInterruptNesting = 0;
portMUX_TYPE port_xTaskLock = portMUX_INITIALIZER_UNLOCKED;
portMUX_TYPE port_xISRLock = portMUX_INITIALIZER_UNLOCKED;
volatile 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)));
// Variables used for IDF style critical sections. These are orthogonal to FreeRTOS critical sections
static UBaseType_t port_uxCriticalNestingIDF = 0;
static UBaseType_t port_uxCriticalOldInterruptStateIDF = 0;
/* ------------------------------------------------ IDF Compatibility --------------------------------------------------
* - These need to be defined for IDF to compile
* ------------------------------------------------------------------------------------------------------------------ */
// ------------------ Critical Sections --------------------
void vPortEnterCriticalIDF(void)
{
// Save current interrupt threshold and disable interrupts
UBaseType_t old_thresh = ulPortSetInterruptMask();
// Update the IDF critical nesting count
port_uxCriticalNestingIDF++;
if (port_uxCriticalNestingIDF == 1) {
// Save a copy of the old interrupt threshold
port_uxCriticalOldInterruptStateIDF = (UBaseType_t) old_thresh;
}
}
void vPortExitCriticalIDF(void)
{
if (port_uxCriticalNestingIDF > 0) {
port_uxCriticalNestingIDF--;
if (port_uxCriticalNestingIDF == 0) {
// Restore the saved interrupt threshold
vPortClearInterruptMask((int)port_uxCriticalOldInterruptStateIDF);
}
}
}
// ----------------------- System --------------------------
#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_CPU_WATCHPOINT_STORE);
}
// ---------------------- Tick Timer -----------------------
BaseType_t xPortSysTickHandler(void);
#if CONFIG_FREERTOS_SYSTICK_USES_SYSTIMER
_Static_assert(SOC_CPU_CORES_NUM <= SOC_SYSTIMER_ALARM_NUM - 1, "the number of cores must match the number of core alarms in SYSTIMER");
void SysTickIsrHandler(void *arg);
static uint32_t s_handled_systicks[portNUM_PROCESSORS] = { 0 };
#define SYSTICK_INTR_ID (ETS_SYSTIMER_TARGET0_EDGE_INTR_SOURCE)
/**
* @brief Set up the systimer peripheral to generate the tick interrupt
*
* Both timer alarms are configured in periodic mode.
* It is done at the same time so SysTicks for both CPUs occur at the same time or very close.
* Shifts a time of triggering interrupts for core 0 and core 1.
*/
void vPortSetupTimer(void)
{
unsigned cpuid = xPortGetCoreID();
#ifdef CONFIG_FREERTOS_CORETIMER_SYSTIMER_LVL3
const unsigned level = ESP_INTR_FLAG_LEVEL3;
#else
const unsigned level = ESP_INTR_FLAG_LEVEL1;
#endif
/* Systimer HAL layer object */
static systimer_hal_context_t systimer_hal;
/* set system timer interrupt vector */
ESP_ERROR_CHECK(esp_intr_alloc(ETS_SYSTIMER_TARGET0_EDGE_INTR_SOURCE + cpuid, ESP_INTR_FLAG_IRAM | level, SysTickIsrHandler, &systimer_hal, NULL));
if (cpuid == 0) {
systimer_hal_init(&systimer_hal);
systimer_ll_set_counter_value(systimer_hal.dev, SYSTIMER_LL_COUNTER_OS_TICK, 0);
systimer_ll_apply_counter_value(systimer_hal.dev, SYSTIMER_LL_COUNTER_OS_TICK);
for (cpuid = 0; cpuid < SOC_CPU_CORES_NUM; cpuid++) {
systimer_hal_counter_can_stall_by_cpu(&systimer_hal, SYSTIMER_LL_COUNTER_OS_TICK, cpuid, false);
}
for (cpuid = 0; cpuid < portNUM_PROCESSORS; ++cpuid) {
uint32_t alarm_id = SYSTIMER_LL_ALARM_OS_TICK_CORE0 + cpuid;
/* configure the timer */
systimer_hal_connect_alarm_counter(&systimer_hal, alarm_id, SYSTIMER_LL_COUNTER_OS_TICK);
systimer_hal_set_alarm_period(&systimer_hal, alarm_id, 1000000UL / CONFIG_FREERTOS_HZ);
systimer_hal_select_alarm_mode(&systimer_hal, alarm_id, SYSTIMER_ALARM_MODE_PERIOD);
systimer_hal_counter_can_stall_by_cpu(&systimer_hal, SYSTIMER_LL_COUNTER_OS_TICK, cpuid, true);
if (cpuid == 0) {
systimer_hal_enable_alarm_int(&systimer_hal, alarm_id);
systimer_hal_enable_counter(&systimer_hal, SYSTIMER_LL_COUNTER_OS_TICK);
#ifndef CONFIG_FREERTOS_UNICORE
// SysTick of core 0 and core 1 are shifted by half of period
systimer_hal_counter_value_advance(&systimer_hal, SYSTIMER_LL_COUNTER_OS_TICK, 1000000UL / CONFIG_FREERTOS_HZ / 2);
#endif
}
}
} else {
uint32_t alarm_id = SYSTIMER_LL_ALARM_OS_TICK_CORE0 + cpuid;
systimer_hal_enable_alarm_int(&systimer_hal, alarm_id);
}
}
/**
* @brief Systimer interrupt handler.
*
* The Systimer interrupt for SysTick works in periodic mode no need to calc the next alarm.
* If a timer interrupt is ever serviced more than one tick late, it is necessary to process multiple ticks.
*/
IRAM_ATTR void SysTickIsrHandler(void *arg)
{
uint32_t cpuid = xPortGetCoreID();
systimer_hal_context_t *systimer_hal = (systimer_hal_context_t *)arg;
#ifdef CONFIG_PM_TRACE
ESP_PM_TRACE_ENTER(TICK, cpuid);
#endif
uint32_t alarm_id = SYSTIMER_LL_ALARM_OS_TICK_CORE0 + cpuid;
do {
systimer_ll_clear_alarm_int(systimer_hal->dev, alarm_id);
uint32_t diff = systimer_hal_get_counter_value(systimer_hal, SYSTIMER_LL_COUNTER_OS_TICK) / systimer_ll_get_alarm_period(systimer_hal->dev, alarm_id) - s_handled_systicks[cpuid];
if (diff > 0) {
if (s_handled_systicks[cpuid] == 0) {
s_handled_systicks[cpuid] = diff;
diff = 1;
} else {
s_handled_systicks[cpuid] += diff;
}
do {
xPortSysTickHandler();
} while (--diff);
}
} while (systimer_ll_is_alarm_int_fired(systimer_hal->dev, alarm_id));
#ifdef CONFIG_PM_TRACE
ESP_PM_TRACE_EXIT(TICK, cpuid);
#endif
}
#endif // CONFIG_FREERTOS_SYSTICK_USES_SYSTIMER
// --------------------- App Start-up ----------------------
extern void app_main(void);
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 (uxSchedulerRunning == 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();
// 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();
}
}
#endif
//Initialize task wdt if configured to do so
#if CONFIG_ESP_TASK_WDT
esp_task_wdt_config_t twdt_config = {
.timeout_ms = CONFIG_ESP_TASK_WDT_TIMEOUT_S * 1000,
.idle_core_mask = 0,
#if CONFIG_ESP_TASK_WDT_PANIC
.trigger_panic = true,
#endif
};
#if CONFIG_ESP_TASK_WDT_CHECK_IDLE_TASK_CPU0
twdt_config.idle_core_mask |= (1 << 0);
#endif
#if CONFIG_ESP_TASK_WDT_CHECK_IDLE_TASK_CPU1
twdt_config.idle_core_mask |= (1 << 1);
#endif
ESP_ERROR_CHECK(esp_task_wdt_init(&twdt_config));
#endif // CONFIG_ESP_TASK_WDT
app_main();
vTaskDelete(NULL);
}
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();
#endif
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;
}
void esp_startup_start_app(void)
{
esp_startup_start_app_common();
ESP_LOGI(TAG, "Starting scheduler.");
vTaskStartScheduler();
}
/* ---------------------------------------------- Port Implementations -------------------------------------------------
* Implementations of Porting Interface functions
* ------------------------------------------------------------------------------------------------------------------ */
// --------------------- Interrupts ------------------------
UBaseType_t ulPortSetInterruptMask(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(UBaseType_t 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" );
}
BaseType_t xPortCheckIfInISR(void)
{
return uxInterruptNesting;
}
// ------------------ Critical Sections --------------------
void IRAM_ATTR vPortTakeLock( portMUX_TYPE *lock )
{
spinlock_acquire( lock, portMUX_NO_TIMEOUT);
}
void IRAM_ATTR vPortReleaseLock( portMUX_TYPE *lock )
{
spinlock_release( lock );
}
// ---------------------- Yielding -------------------------
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 && REG_READ(SYSTEM_CPU_INTR_FROM_CPU_0_REG) != 0) {}
}
}
void vPortYieldFromISR( void )
{
//traceISR_EXIT_TO_SCHEDULER();
uxSchedulerRunning = 1;
xPortSwitchFlag = 1;
}
/* ------------------------------------------------ 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
BaseType_t xPortStartScheduler(void)
{
uxInterruptNesting = 0;
port_uxCriticalNestingIDF = 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();
vPortYield();
/*Should not get here*/
return pdFALSE;
}
void vPortEndScheduler(void)
{
/* very unlikely this function will be called, so just trap here */
abort();
}
// ----------------------- Memory --------------------------
#define FREERTOS_SMP_MALLOC_CAPS (MALLOC_CAP_INTERNAL|MALLOC_CAP_8BIT)
void *pvPortMalloc( size_t xSize )
{
return heap_caps_malloc(xSize, FREERTOS_SMP_MALLOC_CAPS);
}
void vPortFree( void * pv )
{
heap_caps_free(pv);
}
void vPortInitialiseBlocks( void )
{
; //Does nothing, heap is initialized separately in ESP-IDF
}
size_t xPortGetFreeHeapSize( void )
{
return esp_get_free_heap_size();
}
#if( configSTACK_ALLOCATION_FROM_SEPARATE_HEAP == 1 )
void *pvPortMallocStack( size_t xSize )
{
return NULL;
}
void vPortFreeStack( void *pv )
{
}
#endif
#if ( configSUPPORT_STATIC_ALLOCATION == 1 )
void vApplicationGetIdleTaskMemory(StaticTask_t **ppxIdleTaskTCBBuffer,
StackType_t **ppxIdleTaskStackBuffer,
uint32_t *pulIdleTaskStackSize )
{
StaticTask_t *pxTCBBufferTemp;
StackType_t *pxStackBufferTemp;
//Allocate TCB and stack buffer in internal memory
pxTCBBufferTemp = pvPortMalloc(sizeof(StaticTask_t));
pxStackBufferTemp = pvPortMalloc(CONFIG_FREERTOS_IDLE_TASK_STACKSIZE);
assert(pxTCBBufferTemp != NULL);
assert(pxStackBufferTemp != NULL);
//Write back pointers
*ppxIdleTaskTCBBuffer = pxTCBBufferTemp;
*ppxIdleTaskStackBuffer = pxStackBufferTemp;
*pulIdleTaskStackSize = CONFIG_FREERTOS_IDLE_TASK_STACKSIZE;
}
void vApplicationGetTimerTaskMemory(StaticTask_t **ppxTimerTaskTCBBuffer,
StackType_t **ppxTimerTaskStackBuffer,
uint32_t *pulTimerTaskStackSize )
{
StaticTask_t *pxTCBBufferTemp;
StackType_t *pxStackBufferTemp;
//Allocate TCB and stack buffer in internal memory
pxTCBBufferTemp = pvPortMalloc(sizeof(StaticTask_t));
pxStackBufferTemp = pvPortMalloc(configTIMER_TASK_STACK_DEPTH);
assert(pxTCBBufferTemp != NULL);
assert(pxStackBufferTemp != NULL);
//Write back pointers
*ppxTimerTaskTCBBuffer = pxTCBBufferTemp;
*ppxTimerTaskStackBuffer = pxStackBufferTemp;
*pulTimerTaskStackSize = configTIMER_TASK_STACK_DEPTH;
}
#endif //( configSUPPORT_STATIC_ALLOCATION == 1 )
// ------------------------ Stack --------------------------
__attribute__((noreturn)) 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
should instead call vTaskDelete( NULL ).
Artificially force an assert() to be triggered if configASSERT() is
defined, then stop here so application writers can catch the error. */
portDISABLE_INTERRUPTS();
abort();
}
__attribute__((naked)) static void prvTaskExitError(void)
{
asm volatile(".option push\n" \
".option norvc\n" \
"nop\n" \
".option pop");
/* Task entry's RA will point here. Shifting RA into prvTaskExitError is necessary
to make GDB backtrace ending inside that function.
Otherwise backtrace will end in the function laying just before prvTaskExitError in address space. */
_prvTaskExitError();
}
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;
extern char _thread_local_start, _thread_local_end, _flash_rodata_start;
/* Byte pointer, so that subsequent calculations don't depend on sizeof(StackType_t). */
uint8_t *sp = (uint8_t *) pxTopOfStack;
/* Set up TLS area.
* The following diagram illustrates the layout of link-time and run-time
* TLS sections.
*
* +-------------+
* |Section: | Linker symbols:
* |.flash.rodata| ---------------
* 0x0+-------------+ <-- _flash_rodata_start
* ^ | |
* | | Other data |
* | | ... |
* | +-------------+ <-- _thread_local_start
* | |.tbss | ^
* v | | |
* 0xNNNN|int example; | | (thread_local_size)
* |.tdata | v
* +-------------+ <-- _thread_local_end
* | Other data |
* | ... |
* | |
* +-------------+
*
* Local variables of
* pxPortInitialiseStack
* -----------------------
* +-------------+ <-- pxTopOfStack
* |.tdata (*) | ^
* ^ |int example; | |(thread_local_size
* | | | |
* | |.tbss (*) | v
* | +-------------+ <-- task_thread_local_start
* 0xNNNN | | | ^
* | | | |
* | | | |_thread_local_start - _rodata_start
* | | | |
* | | | v
* v +-------------+ <-- threadptr
*
* (*) The stack grows downward!
*/
uint32_t thread_local_sz = (uint32_t) (&_thread_local_end - &_thread_local_start);
thread_local_sz = ALIGNUP(0x10, thread_local_sz);
sp -= thread_local_sz;
task_thread_local_start = sp;
memcpy(task_thread_local_start, &_thread_local_start, thread_local_sz);
threadptr = task_thread_local_start - (&_thread_local_start - &_flash_rodata_start);
/* Simulate the stack frame as it would be created by a context switch interrupt. */
sp -= RV_STK_FRMSZ;
RvExcFrame *frame = (RvExcFrame *)sp;
memset(frame, 0, sizeof(*frame));
/* Shifting RA into prvTaskExitError is necessary to make GDB backtrace ending inside that function.
Otherwise backtrace will end in the function laying just before prvTaskExitError in address space. */
frame->ra = (UBaseType_t)prvTaskExitError + 4/*size of the nop insruction at the beginning of prvTaskExitError*/;
frame->mepc = (UBaseType_t)pxCode;
frame->a0 = (UBaseType_t)pvParameters;
frame->gp = (UBaseType_t)&__global_pointer$;
frame->tp = (UBaseType_t)threadptr;
//TODO: IDF-2393
return (StackType_t *)frame;
}
// ------- Thread Local Storage Pointers Deletion Callbacks -------
#if ( CONFIG_FREERTOS_TLSP_DELETION_CALLBACKS )
void vPortTLSPointersDelCb( void * pxTCB )
{
/* Typecast pxTCB to StaticTask_t type to access TCB struct members.
* pvDummy15 corresponds to pvThreadLocalStoragePointers member of the TCB.
*/
StaticTask_t *tcb = ( StaticTask_t * )pxTCB;
/* The TLSP deletion callbacks are stored at an offset of (configNUM_THREAD_LOCAL_STORAGE_POINTERS/2) */
TlsDeleteCallbackFunction_t *pvThreadLocalStoragePointersDelCallback = ( TlsDeleteCallbackFunction_t * )( &( tcb->pvDummy15[ ( configNUM_THREAD_LOCAL_STORAGE_POINTERS / 2 ) ] ) );
/* We need to iterate over half the depth of the pvThreadLocalStoragePointers area
* to access all TLS pointers and their respective TLS deletion callbacks.
*/
for( int x = 0; x < ( configNUM_THREAD_LOCAL_STORAGE_POINTERS / 2 ); x++ )
{
if ( pvThreadLocalStoragePointersDelCallback[ x ] != NULL ) //If del cb is set
{
/* In case the TLSP deletion callback has been overwritten by a TLS pointer, gracefully abort. */
if ( !esp_ptr_executable( pvThreadLocalStoragePointersDelCallback[ x ] ) ) {
ESP_LOGE("FreeRTOS", "Fatal error: TLSP deletion callback at index %d overwritten with non-excutable pointer %p", x, pvThreadLocalStoragePointersDelCallback[ x ]);
abort();
}
pvThreadLocalStoragePointersDelCallback[ x ]( x, tcb->pvDummy15[ x ] ); //Call del cb
}
}
}
#endif // CONFIG_FREERTOS_TLSP_DELETION_CALLBACKS
// -------------------- Tick Handler -----------------------
extern void esp_vApplicationIdleHook(void);
extern void esp_vApplicationTickHook(void);
BaseType_t xPortSysTickHandler(void)
{
#if configBENCHMARK
portbenchmarkIntLatency();
#endif //configBENCHMARK
traceISR_ENTER(SYSTICK_INTR_ID);
BaseType_t ret = xTaskIncrementTick();
//Manually call the IDF tick hooks
esp_vApplicationTickHook();
if(ret != pdFALSE) {
portYIELD_FROM_ISR();
} else {
traceISR_EXIT();
}
return ret;
}
// ------------------- Hook Functions ----------------------
void __attribute__((weak)) vApplicationStackOverflowHook(TaskHandle_t xTask, char *pcTaskName)
{
#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 (int i = 0; i < sizeof(str) / sizeof(str[0]); i++) {
dest = strcat(dest, str[i]);
}
esp_system_abort(buf);
}
#if ( configUSE_TICK_HOOK > 0 )
void vApplicationTickHook( void )
{
esp_vApplicationTickHook();
}
#endif
#if CONFIG_FREERTOS_USE_MINIMAL_IDLE_HOOK
/*
By default, the port uses vApplicationMinimalIdleHook() to run IDF style idle
hooks. However, users may also want to provide their own vApplicationMinimalIdleHook().
In this case, we use to -Wl,--wrap option to wrap the user provided vApplicationMinimalIdleHook()
*/
extern void __real_vApplicationMinimalIdleHook( void );
void __wrap_vApplicationMinimalIdleHook( void )
{
esp_vApplicationIdleHook(); //Run IDF style hooks
__real_vApplicationMinimalIdleHook(); //Call the user provided vApplicationMinimalIdleHook()
}
#else // CONFIG_FREERTOS_USE_MINIMAL_IDLE_HOOK
void vApplicationMinimalIdleHook( void )
{
esp_vApplicationIdleHook(); //Run IDF style hooks
}
#endif // CONFIG_FREERTOS_USE_MINIMAL_IDLE_HOOK
/*
* Hook function called during prvDeleteTCB() to cleanup any
* user defined static memory areas in the TCB.
*/
void vPortCleanUpTCB ( void *pxTCB )
{
#if ( CONFIG_FREERTOS_TLSP_DELETION_CALLBACKS )
/* Call TLS pointers deletion callbacks */
vPortTLSPointersDelCb( pxTCB );
#endif /* CONFIG_FREERTOS_TLSP_DELETION_CALLBACKS */
}

View File

@@ -0,0 +1,104 @@
/*
* SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
.global uxInterruptNesting
.global uxSchedulerRunning
.global xIsrStackTop
.global pxCurrentTCBs
.global vTaskSwitchContext
.global xPortSwitchFlag
.section .text
/**
* This function makes the RTOS aware about a ISR entering, it takes the
* current task stack saved, places into the TCB, loads the ISR stack
* the interrupted stack must be passed in a0. It needs to receive the
* ISR nesting code improvements
*/
.global rtos_int_enter
.type rtos_int_enter, @function
rtos_int_enter:
/* preserve the return address */
mv t1, ra
mv t2, a0
/* scheduler not enabled, jump directly to ISR handler */
lw t0, uxSchedulerRunning
beq t0,zero, rtos_enter_end
/* increments the ISR nesting count */
la t3, uxInterruptNesting
lw t4, 0x0(t3)
addi t5,t4,1
sw t5, 0x0(t3)
/* If reached here from another low-prio ISR, skip stack pushing to TCB */
bne t4,zero, rtos_enter_end
/* Save current TCB and load the ISR stack */
lw t0, pxCurrentTCBs
sw t2, 0x0(t0)
lw sp, xIsrStackTop
rtos_enter_end:
mv ra, t1
ret
/**
* Recovers the next task to run stack pointer and place it into
* a0, then the interrupt handler can restore the context of
* the next task
*/
.global rtos_int_exit
.type rtos_int_exit, @function
rtos_int_exit:
/* may skip RTOS aware interrupt since scheduler was not started */
lw t0, uxSchedulerRunning
beq t0,zero, rtos_exit_end
/* update nesting interrupts counter */
la t2, uxInterruptNesting
lw t3, 0x0(t2)
/* Already zero, protect against underflow */
beq t3, zero, isr_skip_decrement
addi t3,t3, -1
sw t3, 0x0(t2)
isr_skip_decrement:
/* may still have interrupts pending, skip section below and exit */
bne t3,zero,rtos_exit_end
/* Schedule the next task if a yield is pending */
la t0, xPortSwitchFlag
lw t2, 0x0(t0)
beq t2, zero, no_switch
/* preserve return address and schedule next task
stack pointer for riscv should always be 16 byte aligned */
addi sp,sp,-16
sw ra, 0(sp)
/* vTaskSwitchContext(xCoreID) now expects xCoreID as an argument, so the assembly calls below have been modified. xCoreID is hard-wired to 0 for single-core risc-v. */
li a0, 0
call vTaskSwitchContext
lw ra, 0(sp)
addi sp, sp, 16
/* Clears the switch pending flag */
la t0, xPortSwitchFlag
mv t2, zero
sw t2, 0x0(t0)
no_switch:
/* Recover the stack of next task and prepare to exit : */
lw a0, pxCurrentTCBs
lw a0, 0x0(a0)
rtos_exit_end:
ret

View File

@@ -507,7 +507,7 @@ extern int xPortSwitchFlag;
// --------------------- Debugging ------------------------- // --------------------- Debugging -------------------------
#if CONFIG_FREERTOS_ASSERT_ON_UNTESTED_FUNCTION #if CONFIG_FREERTOS_ASSERT_ON_UNTESTED_FUNCTION
#define UNTESTED_FUNCTION() { esp_rom_printf("Untested FreeRTOS function %s\r\n", __FUNCTION__); configASSERT(false); } while(0) #define UNTESTED_FUNCTION() do{ esp_rom_printf("Untested FreeRTOS function %s\r\n", __FUNCTION__); configASSERT(false); } while(0)
#else #else
#define UNTESTED_FUNCTION() #define UNTESTED_FUNCTION()
#endif #endif

View File

@@ -760,7 +760,7 @@ extern uint32_t port_switch_flag[];
// --------------------- Debugging ------------------------- // --------------------- Debugging -------------------------
#if CONFIG_FREERTOS_ASSERT_ON_UNTESTED_FUNCTION #if CONFIG_FREERTOS_ASSERT_ON_UNTESTED_FUNCTION
#define UNTESTED_FUNCTION() { esp_rom_printf("Untested FreeRTOS function %s\r\n", __FUNCTION__); configASSERT(false); } while(0) #define UNTESTED_FUNCTION() do{ esp_rom_printf("Untested FreeRTOS function %s\r\n", __FUNCTION__); configASSERT(false); } while(0)
#else #else
#define UNTESTED_FUNCTION() #define UNTESTED_FUNCTION()
#endif #endif

View File

@@ -5,7 +5,7 @@ menu "FreeRTOS"
config FREERTOS_SMP config FREERTOS_SMP
bool "Run the SMP FreeRTOS kernel instead (FEATURE UNDER DEVELOPMENT)" bool "Run the SMP FreeRTOS kernel instead (FEATURE UNDER DEVELOPMENT)"
depends on IDF_TARGET_ESP32 depends on IDF_TARGET_ESP32 || IDF_TARGET_ESP32C3
default "n" default "n"
help help
This will cause the FreeRTOS component to compile with the SMP FreeRTOS kernel instead. This will cause the FreeRTOS component to compile with the SMP FreeRTOS kernel instead.