Merge branch 'refactor/freertos_port_files' into 'master'

FreeRTOS: Tidy up existing port files

Closes IDF-3919

See merge request espressif/esp-idf!15199
This commit is contained in:
Darian
2021-09-27 03:34:43 +00:00
8 changed files with 1665 additions and 1277 deletions

View File

@@ -90,7 +90,6 @@
#define portNUM_PROCESSORS 1 #define portNUM_PROCESSORS 1
#endif #endif
#define configASSERT_2 0
#define portUSING_MPU_WRAPPERS 0 #define portUSING_MPU_WRAPPERS 0
#define configUSE_MUTEX 1 #define configUSE_MUTEX 1
@@ -206,7 +205,6 @@
#define configGENERATE_RUN_TIME_STATS 1 /* Used by vTaskGetRunTimeStats() */ #define configGENERATE_RUN_TIME_STATS 1 /* Used by vTaskGetRunTimeStats() */
#endif #endif
#define configUSE_TRACE_FACILITY_2 0
#define configBENCHMARK 0 #define configBENCHMARK 0
#define configUSE_16_BIT_TICKS 0 #define configUSE_16_BIT_TICKS 0
#define configIDLE_SHOULD_YIELD 0 #define configIDLE_SHOULD_YIELD 0

View File

@@ -1,16 +1,8 @@
// Copyright 2015-2020 Espressif Systems (Shanghai) PTE LTD /*
// * SPDX-FileCopyrightText: 2021 Espressif Systems (Shanghai) CO LTD
// Licensed under the Apache License, Version 2.0 (the "License"); *
// you may not use this file except in compliance with the License. * SPDX-License-Identifier: Apache-2.0
// 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.
#include <string.h> #include <string.h>
#include "FreeRTOS.h" #include "FreeRTOS.h"
@@ -22,8 +14,9 @@
#include "esp_task_wdt.h" #include "esp_task_wdt.h"
#include "esp_task.h" #include "esp_task.h"
#include "esp_private/crosscore_int.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 "esp_log.h"
#include "soc/soc_memory_types.h"
#include "soc/dport_access.h" #include "soc/dport_access.h"
#include "sdkconfig.h" #include "sdkconfig.h"
@@ -136,3 +129,19 @@ static void main_task(void* args)
app_main(); app_main();
vTaskDelete(NULL); 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
}

View File

@@ -24,46 +24,47 @@
* *
* 1 tab == 4 spaces! * 1 tab == 4 spaces!
*/ */
#ifndef PORTMACRO_H #ifndef PORTMACRO_H
#define PORTMACRO_H #define PORTMACRO_H
#ifndef __ASSEMBLER__
#include "sdkconfig.h"
#include <stdint.h>
#include <stdlib.h>
#include <stdbool.h>
#include <stdio.h>
#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 <limits.h>
#ifdef CONFIG_LEGACY_INCLUDE_COMMON_HEADERS
#include "soc/soc_memory_layout.h"
#endif
#ifdef __cplusplus #ifdef __cplusplus
extern "C" { extern "C" {
#endif #endif
#ifndef __ASSEMBLER__
#include <stdint.h>
#include <stdlib.h>
#include <stdbool.h>
#include <stdio.h>
#include <limits.h>
#include "esp_timer.h" /* required for FreeRTOS run time stats */
#include "sdkconfig.h" /* --------------------------------------------------- Port Types ------------------------------------------------------
#include "esp_attr.h" * - Port specific types.
#include "esp_heap_caps.h" * - The settings in this file configure FreeRTOS correctly for the given hardware and compiler.
#ifdef CONFIG_LEGACY_INCLUDE_COMMON_HEADERS * - These settings should not be altered.
#include "soc/soc_memory_layout.h" * - The port types must come first as they are used further down in this file
#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 specific definitions.
*
* The settings in this file configure FreeRTOS correctly for the
* given hardware and compiler.
*
* These settings should not be altered.
*-----------------------------------------------------------
*/
/* Type definitions. */
#define portCHAR uint8_t #define portCHAR uint8_t
#define portFLOAT float #define portFLOAT float
#define portDOUBLE double #define portDOUBLE double
@@ -71,8 +72,6 @@ extern "C" {
#define portSHORT int16_t #define portSHORT int16_t
#define portSTACK_TYPE uint8_t #define portSTACK_TYPE uint8_t
#define portBASE_TYPE int #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 portSTACK_TYPE StackType_t;
typedef portBASE_TYPE BaseType_t; typedef portBASE_TYPE BaseType_t;
@@ -85,96 +84,103 @@ typedef unsigned portBASE_TYPE UBaseType_t;
typedef uint32_t TickType_t; typedef uint32_t TickType_t;
#define portMAX_DELAY (TickType_t) 0xffffffffUL #define portMAX_DELAY (TickType_t) 0xffffffffUL
#endif #endif
/*------------------------------------------------------*/
/* Architecture specifics. */ /* 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 0
#define portSTACK_GROWTH (-1) #define portSTACK_GROWTH (-1)
#define portTICK_PERIOD_MS ((TickType_t) (1000 / configTICK_RATE_HZ)) #define portTICK_PERIOD_MS ((TickType_t) (1000 / configTICK_RATE_HZ))
#define portBYTE_ALIGNMENT 16 #define portBYTE_ALIGNMENT 16
/*-----------------------------------------------------------*/ #define portNOP() __asm volatile (" nop ")
#include "portbenchmark.h"
static inline BaseType_t IRAM_ATTR xPortGetCoreID(void) {
return cpu_hal_get_core_id();
}
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);
}
static inline void uxPortCompareSetExtram(volatile uint32_t *addr, uint32_t compare, uint32_t *set) /* ---------------------------------------------- Forward Declarations -------------------------------------------------
{ * - Forward declarations of all the port functions and macros need to implement the FreeRTOS porting interface
#if defined(CONFIG_SPIRAM) * - These must come before definition/declaration of the FreeRTOS porting interface
compare_and_set_extram(addr, compare, set); * ------------------------------------------------------------------------------------------------------------------ */
#endif
}
static inline void __attribute__((always_inline)) uxPortCompareSet(volatile uint32_t *addr, uint32_t compare, uint32_t *set) { // --------------------- Interrupts ------------------------
compare_and_set_native(addr, compare, set);
}
#define portCRITICAL_NESTING_IN_TCB 0 /**
* @brief Checks if the current core is in an ISR context
/* *
* Send an interrupt to another core in order to make the task running * - ISR context consist of Low/Mid priority ISR, or time tick ISR
* on it yield for a higher-priority task. * - High priority ISRs aren't detected here, but they normally cannot call C code, so that should not be an issue anyway.
*/ *
void vPortYieldOtherCore( BaseType_t coreid); * @note [refactor-todo] Check if this should be inlined
* @return
/* * - pdTRUE if in ISR
Callback to set a watchpoint on the end of the stack. Called every context switch to change the stack * - pdFALSE otherwise
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); BaseType_t xPortInIsrContext(void);
/* /**
* This function will be called in High prio ISRs. Returns true if the current core was in ISR context * @brief Check if in ISR context from High priority ISRs
* before calling into high prio ISR context. *
* - 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); BaseType_t xPortInterruptedFromISRContext(void);
/* "mux" data structure (spinlock) */ /**
* @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 { 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; 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; uint32_t count;
#ifdef CONFIG_FREERTOS_PORTMUX_DEBUG #ifdef CONFIG_FREERTOS_PORTMUX_DEBUG
const char *lastLockedFn; const char *lastLockedFn;
int lastLockedLine; int lastLockedLine;
#endif #endif
} portMUX_TYPE; } portMUX_TYPE;
/**< Spinlock initializer */
#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 #ifndef CONFIG_FREERTOS_PORTMUX_DEBUG
#define portMUX_INITIALIZER_UNLOCKED { \ #define portMUX_INITIALIZER_UNLOCKED { \
.owner = portMUX_FREE_VAL, \ .owner = portMUX_FREE_VAL, \
@@ -187,15 +193,251 @@ typedef struct {
.lastLockedFn = "(never locked)", \ .lastLockedFn = "(never locked)", \
.lastLockedLine = -1 \ .lastLockedLine = -1 \
} }
#endif #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 */
/* Scheduler utilities. */ /**
extern void vPortYield( void ); * @brief Initialize a spinlock
extern void vPortYieldFromISR( void ); *
* - 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();
}
/* ------------------------------------------- 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() vPortYield()
#define portYIELD_FROM_ISR() vPortYieldFromISR() #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 /* Yielding within an API call (when interrupts are off), means the yield should be delayed
until interrupts are re-enabled. 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 To do this, we use the "cross-core" interrupt as a trigger to yield on this core when interrupts are re-enabled.This
@@ -203,112 +445,104 @@ extern void vPortYieldFromISR( void );
happening on the same CPU. happening on the same CPU.
*/ */
#define portYIELD_WITHIN_API() portYIELD() #define portYIELD_WITHIN_API() portYIELD()
/*-----------------------------------------------------------*/
/* Critical section management. */ // ------------------- Hook Functions ----------------------
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 #ifndef CONFIG_FREERTOS_LEGACY_HOOKS
#define vApplicationIdleHook esp_vApplicationIdleHook #define vApplicationIdleHook esp_vApplicationIdleHook
#define vApplicationTickHook esp_vApplicationTickHook #define vApplicationTickHook esp_vApplicationTickHook
#endif /* !CONFIG_FREERTOS_LEGACY_HOOKS */ #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 portSUPPRESS_TICKS_AND_SLEEP(idleTime) vApplicationSleep(idleTime)
#define portNOP() __asm volatile ( " nop " ) // ------------------- Run Time Stats ----------------------
#define portVALID_TCB_MEM(ptr) esp_ptr_byte_accessible(ptr) #define portCONFIGURE_TIMER_FOR_RUN_TIME_STATS()
#define portVALID_STACK_MEM(ptr) esp_ptr_byte_accessible(ptr) #define portGET_RUN_TIME_COUNTER_VALUE() 0
#ifdef CONFIG_FREERTOS_RUN_TIME_STATS_USING_ESP_TIMER
/* Get tick rate per second */ /* Coarse resolution time (us) */
uint32_t xPortGetTickRateHz(void); #define portALT_GET_RUN_TIME_COUNTER_VALUE(x) do {x = (uint32_t)esp_timer_get_time();} while(0)
// configASSERT_2 if requested
#if configASSERT_2
#include <stdio.h>
void exit(int);
#define configASSERT( x ) if (!(x)) { porttracePrint(-1); printf("\nAssertion failed in %s:%d\n", __FILE__, __LINE__); exit(-1); }
#endif #endif
#endif //__ASSEMBLER__
/* --------------------------------------------- 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)
{
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)
{
#if defined(CONFIG_SPIRAM)
compare_and_set_extram(addr, compare, set);
#endif
}
// ---------------------- 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);
}
/* ------------------------------------------------------ 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
*/
bool xPortCheckValidTCBMem(const void *ptr);
/**
* @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);
#define portVALID_TCB_MEM(ptr) xPortCheckValidTCBMem(ptr)
#define portVALID_STACK_MEM(ptr) xPortcheckValidStackMem(ptr)
#ifdef __cplusplus #ifdef __cplusplus
} }
#endif #endif
#endif //__ASSEMBLER__
#endif /* PORTMACRO_H */ #endif /* PORTMACRO_H */

View File

@@ -74,31 +74,35 @@
* Implementation of functions defined in portable.h for the RISC-V port. * Implementation of functions defined in portable.h for the RISC-V port.
*----------------------------------------------------------------------*/ *----------------------------------------------------------------------*/
#include <string.h>
#include "FreeRTOS.h"
#include "task.h"
#include "portmacro.h"
#include "sdkconfig.h" #include "sdkconfig.h"
#include <string.h>
#include "soc/soc_caps.h" #include "soc/soc_caps.h"
#include "soc/periph_defs.h" #include "soc/periph_defs.h"
#include "soc/system_reg.h" #include "soc/system_reg.h"
#include "hal/systimer_hal.h" #include "hal/systimer_hal.h"
#include "hal/systimer_ll.h" #include "hal/systimer_ll.h"
#include "riscv/rvruntime-frames.h" #include "riscv/rvruntime-frames.h"
#include "riscv/riscv_interrupts.h" #include "riscv/riscv_interrupts.h"
#include "riscv/interrupt.h" #include "riscv/interrupt.h"
#include "esp_private/crosscore_int.h"
#include "port_systick.h" #include "esp_private/pm_trace.h"
#include "esp_attr.h"
#include "esp_system.h" #include "esp_system.h"
#include "esp_intr_alloc.h" #include "esp_intr_alloc.h"
#include "esp_private/crosscore_int.h"
#include "esp_attr.h"
#include "esp_debug_helpers.h" #include "esp_debug_helpers.h"
#include "esp_log.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. * @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]; __attribute__((aligned(16))) static StackType_t xIsrStack[configISR_STACK_SIZE];
StackType_t *xIsrStackTop = &xIsrStack[0] + (configISR_STACK_SIZE & (~((portPOINTER_SIZE_TYPE)portBYTE_ALIGNMENT_MASK))); 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 extern void esprv_intc_int_set_threshold(int); // FIXME, this function is in ROM only
BaseType_t xPortStartScheduler(void)
void vPortEnterCritical(void)
{ {
BaseType_t state = portENTER_CRITICAL_NESTED(); uxInterruptNesting = 0;
uxCriticalNesting++; uxCriticalNesting = 0;
uxSchedulerRunning = 0;
if (uxCriticalNesting == 1) { /* Setup the hardware to generate the tick. */
uxSavedInterruptState = state; 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) { /* very unlikely this function will be called, so just trap here */
uxCriticalNesting--; abort();
if (uxCriticalNesting == 0) {
portEXIT_CRITICAL_NESTED(uxSavedInterruptState);
}
}
} }
void prvTaskExitError(void) // ------------------------ Stack --------------------------
static void prvTaskExitError(void)
{ {
/* A function that implements a task must not exit or attempt to return to /* 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 its caller as there is nothing to return to. If a task wants to exit it
@@ -153,52 +167,6 @@ void prvTaskExitError(void)
abort(); 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) StackType_t *pxPortInitialiseStack(StackType_t *pxTopOfStack, TaskFunction_t pxCode, void *pvParameters)
{ {
extern uint32_t __global_pointer$; extern uint32_t __global_pointer$;
@@ -271,78 +239,13 @@ StackType_t *pxPortInitialiseStack(StackType_t *pxTopOfStack, TaskFunction_t pxC
return (StackType_t *)frame; 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 */ /* ---------------------------------------------- Port Implementations -------------------------------------------------
riscv_global_interrupts_enable(); *
* ------------------------------------------------------------------------------------------------------------------ */
vPortYield(); // --------------------- Interrupts ------------------------
/*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;
}
BaseType_t xPortInIsrContext(void) BaseType_t xPortInIsrContext(void)
{ {
@@ -355,6 +258,7 @@ BaseType_t IRAM_ATTR xPortInterruptedFromISRContext(void)
return uxInterruptNesting; return uxInterruptNesting;
} }
// ---------------------- Spinlocks ------------------------
void vPortCPUInitializeMutex(portMUX_TYPE *mux) void vPortCPUInitializeMutex(portMUX_TYPE *mux)
{ {
@@ -378,6 +282,109 @@ void vPortCPUReleaseMutex(portMUX_TYPE *mux)
(void)mux; //TODO: IDF-2393 (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) void __attribute__((weak)) vApplicationStackOverflowHook(TaskHandle_t xTask, char *pcTaskName)
{ {
#define ERR_STR1 "***ERROR*** A stack overflow in task " #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); 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); extern void esp_startup_start_app_common(void);
void esp_startup_start_app(void) void esp_startup_start_app(void)

View File

@@ -24,56 +24,54 @@
* *
* 1 tab == 4 spaces! * 1 tab == 4 spaces!
*/ */
#ifndef PORTMACRO_H #ifndef PORTMACRO_H
#define PORTMACRO_H #define PORTMACRO_H
#ifndef __ASSEMBLER__
#include "sdkconfig.h"
#include <stdint.h>
#include <stdlib.h>
#include <stdbool.h>
#include <stdarg.h>
#include <xtensa/config/core.h>
#include <xtensa/hal.h> /* required for xthal_get_ccount. [refactor-todo] use cpu_hal instead */
#include <xtensa/xtruntime.h> /* 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 <limits.h>
#include <xtensa/config/system.h>
#include <xtensa/xtensa_api.h>
#include "soc/cpu.h"
#ifdef CONFIG_LEGACY_INCLUDE_COMMON_HEADERS
#include "soc/soc_memory_layout.h"
#endif
#ifdef __cplusplus #ifdef __cplusplus
extern "C" { extern "C" {
#endif #endif
#ifndef __ASSEMBLER__
#include <sdkconfig.h>
#include <stdint.h>
#include <stdlib.h>
#include <stdbool.h>
#include <stdarg.h>
#include <xtensa/hal.h>
#include <xtensa/config/core.h>
#include <xtensa/config/system.h> /* required for XSHAL_CLIB */
#include <xtensa/xtruntime.h>
#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 <esp_heap_caps.h>
#include "esp_rom_sys.h"
#include "sdkconfig.h"
#include "freertos/xtensa_api.h"
#include "esp_system.h"
#include "soc/cpu.h"
#include <limits.h>
#ifdef CONFIG_LEGACY_INCLUDE_COMMON_HEADERS /* --------------------------------------------------- Port Types ------------------------------------------------------
#include "soc/soc_memory_layout.h" * - Port specific types.
#endif * - 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.
*-----------------------------------------------------------
*/
#include "esp_system.h"
#include "hal/cpu_hal.h"
#include "xt_instr_macros.h"
/* Type definitions. */
#define portCHAR int8_t #define portCHAR int8_t
#define portFLOAT float #define portFLOAT float
#define portDOUBLE double #define portDOUBLE double
@@ -93,60 +91,453 @@ typedef unsigned portBASE_TYPE UBaseType_t;
typedef uint32_t TickType_t; typedef uint32_t TickType_t;
#define portMAX_DELAY ( TickType_t ) 0xffffffffUL #define portMAX_DELAY ( TickType_t ) 0xffffffffUL
#endif #endif
/*-----------------------------------------------------------*/
// portbenchmark /* Task function macros as described on the FreeRTOS.org WEB site. */
#include "portbenchmark.h" #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. /* ----------------------------------------------- Port Configurations -------------------------------------------------
// WARNING: Only applies to current CPU. See notes above. * - Configurations values supplied by each port
static inline unsigned portENTER_CRITICAL_NESTED(void) { * - 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 4
#define portNOP() XT_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 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.
*/
#define portYIELD_WITHIN_API() esp_crosscore_int_send_yield(xPortGetCoreID())
// ------------------- 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()
/**
* - 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); unsigned state = XTOS_SET_INTLEVEL(XCHAL_EXCM_LEVEL);
portbenchmarkINTERRUPT_DISABLE(); portbenchmarkINTERRUPT_DISABLE();
return state; return state;
} }
#define portEXIT_CRITICAL_NESTED(state) do { portbenchmarkINTERRUPT_RESTORE(state); XTOS_RESTORE_JUST_INTLEVEL(state); } while (0)
/* // ---------------------- Spinlocks ------------------------
Modifications to portENTER_CRITICAL.
For an introduction, see "Critical Sections & Disabling Interrupts" in docs/api-guides/freertos-smp.rst
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).
This all assumes that interrupts are either entirely disabled or enabled. Interrupt priority levels
will break this scheme.
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.
*/
/* "mux" data structure (spinlock) */
typedef spinlock_t portMUX_TYPE;
#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
#define portCRITICAL_NESTING_IN_TCB 0
static inline void __attribute__((always_inline)) vPortCPUInitializeMutex(portMUX_TYPE *mux) static inline void __attribute__((always_inline)) vPortCPUInitializeMutex(portMUX_TYPE *mux)
{ {
@@ -168,17 +559,19 @@ static inline void __attribute__((always_inline)) vPortCPUReleaseMutex(portMUX_T
spinlock_release(mux); spinlock_release(mux);
} }
void vPortEnterCritical(portMUX_TYPE *mux); static inline void __attribute__((always_inline)) uxPortCompareSet(volatile uint32_t *addr, uint32_t compare, uint32_t *set)
void vPortExitCritical(portMUX_TYPE *mux); {
compare_and_set_native(addr, compare, set);
}
#define portASSERT_IF_IN_ISR() vPortAssertIfInISR() static inline void uxPortCompareSetExtram(volatile uint32_t *addr, uint32_t compare, uint32_t *set)
void vPortAssertIfInISR(void); {
#ifdef CONFIG_SPIRAM
compare_and_set_extram(addr, compare, set);
#endif
}
/* // ------------------ Critical Sections --------------------
* 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);
static inline void __attribute__((always_inline)) vPortEnterCriticalCompliance(portMUX_TYPE *mux) static inline void __attribute__((always_inline)) vPortEnterCriticalCompliance(portMUX_TYPE *mux)
{ {
@@ -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) static inline void __attribute__((always_inline)) vPortEnterCriticalSafe(portMUX_TYPE *mux)
{ {
if (xPortInIsrContext()) { if (xPortInIsrContext()) {
@@ -234,80 +613,82 @@ static inline void __attribute__((always_inline)) vPortExitCriticalSafe(portMUX_
} }
} }
#define portENTER_CRITICAL_SAFE(mux) vPortEnterCriticalSafe(mux) // ---------------------- Yielding -------------------------
#define portEXIT_CRITICAL_SAFE(mux) vPortExitCriticalSafe(mux)
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);
/* /*
* Wrapper for the Xtensa compare-and-set instruction. This subroutine will atomically compare * intlevel = (ps_reg & 0xf);
* *addr to 'compare'. If *addr == compare, *addr is set to *set. *set is updated with the previous * excm = (ps_reg >> 4) & 0x1;
* value of *addr (either 'compare' or some other value.) * CINTLEVEL is max(excm * EXCMLEVEL, INTLEVEL), where EXCMLEVEL is 3.
* * However, just return true, only intlevel is zero.
* 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).
*/ */
static inline void __attribute__((always_inline)) uxPortCompareSet(volatile uint32_t *addr, uint32_t compare, uint32_t *set) {
compare_and_set_native(addr, compare, set); return ((ps_reg & PS_INTLEVEL_MASK) == 0);
} }
// Critical section management. NW-TODO: replace XTOS_SET_INTLEVEL with more efficient version, if any? // ----------------------- System --------------------------
// 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)
static inline BaseType_t IRAM_ATTR xPortGetCoreID(void)
// 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))
#endif
static inline void uxPortCompareSetExtram(volatile uint32_t *addr, uint32_t compare, uint32_t *set)
{ {
#ifdef CONFIG_SPIRAM return cpu_hal_get_core_id();
compare_and_set_extram(addr, compare, set);
#endif
} }
/*-----------------------------------------------------------*/
/* Architecture specifics. */ /* ------------------------------------------------------ Misc ---------------------------------------------------------
#define portSTACK_GROWTH ( -1 ) * - Miscellaneous porting macros
#define portTICK_PERIOD_MS ( ( TickType_t ) 1000 / configTICK_RATE_HZ ) * - These are not port of the FreeRTOS porting interface, but are used by other FreeRTOS dependent components
#define portBYTE_ALIGNMENT 4 * - [refactor-todo] Remove dependency on MPU wrappers by modifying TCB
#define portNOP() XT_NOP() * ------------------------------------------------------------------------------------------------------------------ */
/*-----------------------------------------------------------*/
/* Fine resolution time */ // -------------------- Co-Processor -----------------------
#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 // When coprocessors are defined, we maintain a pointer to coprocessors area.
/* Coarse resolution time (us) */ // We currently use a hack: redefine field xMPU_SETTINGS in TCB block as a structure that can hold:
#define portALT_GET_RUN_TIME_COUNTER_VALUE(x) do {x = (uint32_t)esp_timer_get_time();} while(0) // 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 #endif
void vPortYield( void ); #if portUSING_MPU_WRAPPERS
void vPortEvaluateYieldFromISR(int argc, ...); // Define here mpu_settings, which is port dependent
void _frxt_setup_switch( void ); 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);
/*
* The structures and methods of manipulating the MPU are contained within the
* port layer.
*
* Fills the xMPUSettings structure with the memory region information
* contained in xRegions.
*/
#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
// -------------------- VA_ARGS Yield ----------------------
/** /**
* Macro to count number of arguments of a __VA_ARGS__ used to support portYIELD_FROM_ISR with, * 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() == 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"); _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, * @brief Checks if a given piece of memory can be used to store a task's TCB
* 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.
* *
* Fills the xMPUSettings structure with the memory region information * - Defined in port_common.c
* contained in xRegions. *
* @param ptr Pointer to memory
* @return true Memory can be used to store a TCB
* @return false Otherwise
*/ */
#if( portUSING_MPU_WRAPPERS == 1 ) bool xPortCheckValidTCBMem(const void *ptr);
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
/* Multi-core: get current core ID */ /**
static inline BaseType_t IRAM_ATTR xPortGetCoreID(void) { * @brief Checks if a given piece of memory can be used to store a task's stack
return cpu_hal_get_core_id(); *
} * - Defined in port_common.c
*
/* Get tick rate per second */ * @param ptr Pointer to memory
uint32_t xPortGetTickRateHz(void); * @return true Memory can be used to store a task stack
* @return false Otherwise
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.
*/ */
bool xPortcheckValidStackMem(const void *ptr);
return ((ps_reg & PS_INTLEVEL_MASK) == 0); #define portVALID_TCB_MEM(ptr) xPortCheckValidTCBMem(ptr)
} #define portVALID_STACK_MEM(ptr) xPortcheckValidStackMem(ptr)
// porttrace
#if configUSE_TRACE_FACILITY_2
#include "porttrace.h"
#endif
// configASSERT_2 if requested
#if configASSERT_2
#include <stdio.h>
void exit(int);
#define configASSERT( x ) if (!(x)) { porttracePrint(-1); printf("\nAssertion failed in %s:%d\n", __FILE__, __LINE__); exit(-1); }
#endif
#endif // __ASSEMBLER__
#ifdef __cplusplus #ifdef __cplusplus
} }
#endif #endif
#endif // __ASSEMBLER__
#endif /* PORTMACRO_H */ #endif /* PORTMACRO_H */

View File

@@ -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

View File

@@ -1,153 +1,124 @@
/* /*
FreeRTOS V8.2.0 - Copyright (C) 2015 Real Time Engineers Ltd. * FreeRTOS Kernel V10.4.3
All rights reserved * Copyright (C) 2017 Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
VISIT http://www.FreeRTOS.org TO ENSURE YOU ARE USING THE LATEST VERSION. * 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
This file is part of the FreeRTOS distribution. * the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
FreeRTOS is free software; you can redistribute it and/or modify it under * the Software, and to permit persons to whom the Software is furnished to do so,
the terms of the GNU General Public License (version 2) as published by the * subject to the following conditions:
Free Software Foundation >>!AND MODIFIED BY!<< the FreeRTOS exception. *
* 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
>>! NOTE: The modification to the GPL is included to allow you to !<< * FreeRTOS name, please do so in a fair use way that does not cause confusion.
>>! distribute a combined work that includes FreeRTOS without being !<< *
>>! obliged to provide the source code for proprietary components !<< * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
>>! outside of the FreeRTOS kernel. !<< * 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
FreeRTOS is distributed in the hope that it will be useful, but WITHOUT ANY * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
FOR A PARTICULAR PURPOSE. Full license text is available on the following *
link: http://www.freertos.org/a00114.html * https://www.FreeRTOS.org
* https://github.com/FreeRTOS
*************************************************************************** *
* * * 1 tab == 4 spaces!
* 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. * Copyright (c) 2015-2019 Cadence Design Systems, Inc.
// *
// Permission is hereby granted, free of charge, to any person obtaining * Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the * a copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including * "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish, * without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to * distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so, subject to * permit persons to whom the Software is furnished to do so, subject to
// the following conditions: * the following conditions:
// *
// The above copyright notice and this permission notice shall be included * The above copyright notice and this permission notice shall be included
// in all copies or substantial portions of the Software. * in all copies or substantial portions of the Software.
// *
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
// IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
// CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
// TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
// SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
--------------------------------------------------------------------------------
*/ */
#include "sdkconfig.h"
#include <stdint.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include <stdarg.h>
#include <xtensa/config/core.h> #include <xtensa/config/core.h>
#include <xtensa/xtensa_context.h>
#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 "soc/soc_caps.h" #include "soc/soc_caps.h"
#include "soc/efuse_reg.h" #include "esp_private/crosscore_int.h"
#include "soc/dport_access.h" #include "esp_system.h"
#include "soc/dport_reg.h" #include "esp_log.h"
#include "esp_int_wdt.h" #include "esp_int_wdt.h"
#include "esp_app_trace.h" /* Required for esp_apptrace_init. [refactor-todo] */
#include "FreeRTOS.h" /* This pulls in portmacro.h */
#include "sdkconfig.h" #include "task.h" /* Required for TaskHandle_t, tskNO_AFFINITY, and vTaskStartScheduler */
#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 "port_systick.h" #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"); _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]; 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 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_uxCriticalNesting[portNUM_PROCESSORS] = {0};
BaseType_t port_uxOldInterruptState[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 // User exception dispatcher when exiting
void _xt_user_exit(void); void _xt_user_exit(void);
@@ -164,9 +135,6 @@ static void vPortTaskWrapper(TaskFunction_t pxCode, void *pvParameters)
} }
#endif #endif
/*
* Stack initialization
*/
#if portUSING_MPU_WRAPPERS #if portUSING_MPU_WRAPPERS
StackType_t *pxPortInitialiseStack( StackType_t *pxTopOfStack, TaskFunction_t pxCode, void *pvParameters, BaseType_t xRunPrivileged ) StackType_t *pxPortInitialiseStack( StackType_t *pxTopOfStack, TaskFunction_t pxCode, void *pvParameters, BaseType_t xRunPrivileged )
#else #else
@@ -198,8 +166,9 @@ StackType_t *pxPortInitialiseStack( StackType_t *pxTopOfStack, TaskFunction_t px
sp = (StackType_t *) (((UBaseType_t)pxTopOfStack - XT_CP_SIZE - thread_local_sz - XT_STK_FRMSZ) & ~0xf); 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) */ /* Clear the entire frame (do not use memset() because we don't depend on C library) */
for (tp = sp; tp <= pxTopOfStack; ++tp) for (tp = sp; tp <= pxTopOfStack; ++tp) {
*tp = 0; *tp = 0;
}
frame = (XtExcFrame *) sp; frame = (XtExcFrame *) sp;
@@ -223,7 +192,7 @@ StackType_t *pxPortInitialiseStack( StackType_t *pxTopOfStack, TaskFunction_t px
frame->a2 = (UBaseType_t) pvParameters; frame->a2 = (UBaseType_t) pvParameters;
#endif #endif
frame->ps = PS_UM | PS_EXCM; frame->ps = PS_UM | PS_EXCM;
#else #else /* __XTENSA_CALL0_ABI__ */
/* + for windowed ABI also set WOE and CALLINC (pretend task was 'call4'd). */ /* + for windowed ABI also set WOE and CALLINC (pretend task was 'call4'd). */
#if CONFIG_FREERTOS_TASK_FUNCTION_WRAPPER #if CONFIG_FREERTOS_TASK_FUNCTION_WRAPPER
frame->a6 = (UBaseType_t) pxCode; frame->a6 = (UBaseType_t) pxCode;
@@ -232,7 +201,7 @@ StackType_t *pxPortInitialiseStack( StackType_t *pxTopOfStack, TaskFunction_t px
frame->a6 = (UBaseType_t) pvParameters; frame->a6 = (UBaseType_t) pvParameters;
#endif #endif
frame->ps = PS_UM | PS_EXCM | PS_WOE | PS_CALLINC(1); frame->ps = PS_UM | PS_EXCM | PS_WOE | PS_CALLINC(1);
#endif #endif /* __XTENSA_CALL0_ABI__ */
#ifdef XT_USE_SWPRI #ifdef XT_USE_SWPRI
/* Set the initial virtual priority mask value to all 1's. */ /* Set the initial virtual priority mask value to all 1's. */
@@ -269,80 +238,19 @@ StackType_t *pxPortInitialiseStack( StackType_t *pxTopOfStack, TaskFunction_t px
p[0] = 0; p[0] = 0;
p[1] = 0; p[1] = 0;
p[2] = (((uint32_t) p) + 12 + XCHAL_TOTAL_SA_ALIGN - 1) & -XCHAL_TOTAL_SA_ALIGN; p[2] = (((uint32_t) p) + 12 + XCHAL_TOTAL_SA_ALIGN - 1) & -XCHAL_TOTAL_SA_ALIGN;
#endif #endif /* XCHAL_CP_NUM */
return sp; 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 /* ---------------------------------------------- Port Implementations -------------------------------------------------
* clear the stack area after we return. This is done in pxPortInitialiseStack(). *
*/ * ------------------------------------------------------------------------------------------------------------------ */
#endif
}
void vPortReleaseTaskMPUSettings( xMPU_SETTINGS *xMPUSettings ) // --------------------- Interrupts ------------------------
{
/* If task has live floating point registers somewhere, release them */
_xt_coproc_release( xMPUSettings->coproc_area );
}
#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) BaseType_t xPortInIsrContext(void)
{ {
unsigned int irqStatus; unsigned int irqStatus;
@@ -353,15 +261,63 @@ BaseType_t xPortInIsrContext(void)
return ret; return ret;
} }
/* void vPortAssertIfInISR(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. configASSERT(xPortInIsrContext());
*/ }
BaseType_t IRAM_ATTR xPortInterruptedFromISRContext(void) 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, ...) void IRAM_ATTR vPortEvaluateYieldFromISR(int argc, ...)
{ {
BaseType_t xYield; BaseType_t xYield;
@@ -386,68 +342,7 @@ void IRAM_ATTR vPortEvaluateYieldFromISR(int argc, ...)
} }
} }
void vPortAssertIfInISR(void) // ------------------- Hook Functions ----------------------
{
configASSERT(xPortInIsrContext());
}
#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)
{
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]);
}
}
}
void __attribute__((weak)) vApplicationStackOverflowHook( TaskHandle_t xTask, char *pcTaskName ) void __attribute__((weak)) vApplicationStackOverflowHook( TaskHandle_t xTask, char *pcTaskName )
{ {
@@ -464,6 +359,63 @@ void __attribute__((weak)) vApplicationStackOverflowHook( TaskHandle_t xTask, c
esp_system_abort(buf); 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);
}
/* ---------------------------------------------- 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 )
{
#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 );
}
#endif /* portUSING_MPU_WRAPPERS */
// --------------------- App Start-up ----------------------
#if !CONFIG_FREERTOS_UNICORE #if !CONFIG_FREERTOS_UNICORE
void esp_startup_start_app_other_cores(void) void esp_startup_start_app_other_cores(void)
{ {

View File

@@ -1382,10 +1382,10 @@ components/freertos/croutine.c
components/freertos/esp_additions/task_snapshot.c components/freertos/esp_additions/task_snapshot.c
components/freertos/event_groups.c components/freertos/event_groups.c
components/freertos/freertos_v8_compat.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/task_snapshot.h
components/freertos/include/esp_additions/freertos_tasks_c_additions.h components/freertos/include/esp_additions/freertos_tasks_c_additions.h
components/freertos/include/freertos/FreeRTOS.h components/freertos/include/freertos/FreeRTOS.h
components/freertos/include/freertos/FreeRTOSConfig.h
components/freertos/include/freertos/StackMacros.h components/freertos/include/freertos/StackMacros.h
components/freertos/include/freertos/atomic.h components/freertos/include/freertos/atomic.h
components/freertos/include/freertos/croutine.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/task.h
components/freertos/include/freertos/timers.h components/freertos/include/freertos/timers.h
components/freertos/list.c 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/linux/include/freertos/portmacro.h
components/freertos/port/port_common.c components/freertos/port/riscv/include/freertos/FreeRTOSConfig_arch.h
components/freertos/port/riscv/include/freertos/FreeRTOSConfig.h
components/freertos/port/riscv/include/freertos/portbenchmark.h components/freertos/port/riscv/include/freertos/portbenchmark.h
components/freertos/port/riscv/include/freertos/portmacro.h components/freertos/port/riscv/include/freertos/portmacro.h
components/freertos/port/riscv/port.c 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/portbenchmark.h
components/freertos/port/xtensa/include/freertos/portmacro.h components/freertos/port/xtensa/include/freertos/portmacro.h
components/freertos/port/xtensa/include/freertos/portmacro_priv.h components/freertos/port/xtensa/include/freertos/portmacro_priv.h