Merge branch 'feature/freertos_plus_posix_SMP' into 'master'

[FreeRTOS/Linux] POSIX/Linux and SMP

Closes IDF-6087

See merge request espressif/esp-idf!21818
This commit is contained in:
Jakob Hasse 2023-04-25 20:21:26 +08:00
commit af2d326a95
39 changed files with 3427 additions and 22 deletions

View File

@ -42,18 +42,24 @@ set(include_dirs
# or #include "esp_private/freertos_idf_additions_priv.h"
"esp_additions/arch/${arch}/include") # For #include "freertos/FreeRTOSConfig_arch.h"
if(CONFIG_FREERTOS_SMP)
list(APPEND include_dirs "${kernel_dir}/portable/${arch}/include/freertos") # Xtensa headers via #include "xx.h"
endif()
set(private_include_dirs
"${kernel_dir}/portable/${arch}/include/freertos"
"${kernel_dir}/portable/${arch}"
"${kernel_dir}/include/freertos" # FreeRTOS headers via #include "xxx.h"
"esp_additions" # For include "freertos_tasks_c_additions.h"
.)
set(private_requirements "")
if(${target} STREQUAL "linux")
list(APPEND srcs
"${kernel_dir}/portable/${arch}/port_idf.c"
"${kernel_dir}/portable/${arch}/utils/wait_for_event.c")
list(APPEND srcs "${kernel_dir}/portable/${arch}/utils/wait_for_event.c")
if(NOT CONFIG_FREERTOS_SMP)
list(APPEND srcs "${kernel_dir}/portable/${arch}/port_idf.c")
endif()
else()
list(APPEND srcs
"app_startup.c"
@ -61,12 +67,8 @@ else()
"port_common.c"
"${kernel_dir}/portable/${arch}/portasm.S")
list(APPEND private_include_dirs
"esp_additions") # For #include "freertos_tasks_c_additions.h"
if(CONFIG_FREERTOS_SMP)
set(ldfragments linker_smp.lf linker_common.lf)
list(APPEND include_dirs "${kernel_dir}/portable/${arch}/include/freertos") # Xtensa headers via #include "xx.h"
else()
list(APPEND srcs
"${kernel_dir}/portable/port_systick.c"

View File

@ -73,8 +73,10 @@
#endif
#ifdef configNEWLIB_REENTRANT_IS_DYNAMIC
#if ( configUSE_NEWLIB_REENTRANT != 1 )
#error configUSE_NEWLIB_REENTRANT must be defined to 1 to enable configNEWLIB_REENTRANT_IS_DYNAMIC
#if configNEWLIB_REENTRANT_IS_DYNAMIC == 1
#if ( configUSE_NEWLIB_REENTRANT != 1 )
#error configUSE_NEWLIB_REENTRANT must be defined to 1 to enable configNEWLIB_REENTRANT_IS_DYNAMIC
#endif
#endif
#else /* configNEWLIB_REENTRANT_IS_DYNAMIC */
#define configNEWLIB_REENTRANT_IS_DYNAMIC 0

View File

@ -0,0 +1,320 @@
/*
* FreeRTOS Kernel V10.4.3
* Copyright 2020 Cambridge Consultants Ltd.
*
* SPDX-FileCopyrightText: 2020 Cambridge Consultants Ltd.
*
* SPDX-License-Identifier: MIT
*
* SPDX-FileContributor: 2023 Espressif Systems (Shanghai) CO LTD
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
* the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
* https://www.FreeRTOS.org
* https://github.com/FreeRTOS
*/
#ifndef PORTMACRO_H
#define PORTMACRO_H
#include <limits.h>
#include <stdint.h>
#include "esp_macros.h"
#include "spinlock.h"
#ifdef __cplusplus
extern "C" {
#endif
/* --------------------------------------------------- Port Types ------------------------------------------------------
* - Port specific types.
* - The settings in this file configure FreeRTOS correctly for the given hardware and compiler.
* - These settings should not be altered.
* - The port types must come before first as they are used further down the file
* ------------------------------------------------------------------------------------------------------------------ */
/* Type definitions. */
#define portCHAR char
#define portFLOAT float
#define portDOUBLE double
#define portLONG long
#define portSHORT short
#define portSTACK_TYPE unsigned long
#define portBASE_TYPE long
#define portPOINTER_SIZE_TYPE intptr_t
typedef portSTACK_TYPE StackType_t;
typedef portBASE_TYPE BaseType_t;
typedef unsigned long UBaseType_t;
typedef unsigned long TickType_t;
#define portMAX_DELAY ( TickType_t ) ULONG_MAX
#define portTICK_TYPE_IS_ATOMIC 1
/* 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 )
/*-----------------------------------------------------------*/
/* ----------------------------------------------- Port Configurations -------------------------------------------------
* - Configurations values supplied by each port
* - Required by FreeRTOS
* ------------------------------------------------------------------------------------------------------------------ */
#define portCRITICAL_NESTING_IN_TCB 1
#define portSTACK_GROWTH ( -1 )
#define portHAS_STACK_OVERFLOW_CHECKING ( 1 ) // Set in non-SMP POSIX port
#define portTICK_PERIOD_MS ( ( TickType_t ) 1000 / configTICK_RATE_HZ )
#define portTICK_RATE_MICROSECONDS ( ( TickType_t ) 1000000 / configTICK_RATE_HZ )
#define portBYTE_ALIGNMENT 8
#define portNOP() __asm volatile (" nop ")
/*-----------------------------------------------------------*/
/* ---------------------------------------------- Forward Declarations -------------------------------------------------
* - Forward declarations of all the port functions and macros need to implement the FreeRTOS porting interface
* - These must come before definition/declaration of the FreeRTOS porting interface
* ------------------------------------------------------------------------------------------------------------------ */
// ---------------------- Spinlocks ------------------------
typedef spinlock_t portMUX_TYPE; /**< Spinlock type used by FreeRTOS critical sections */
/**< Spinlock initializer */
#define portMUX_INITIALIZER_UNLOCKED SPINLOCK_INITIALIZER /**< Spinlock initializer */
#define portMUX_FREE_VAL SPINLOCK_FREE /**< Spinlock is free. [refactor-todo] check if this is still required */
#define portMUX_NO_TIMEOUT SPINLOCK_WAIT_FOREVER /**< When passed for 'timeout_cycles', spin forever if necessary. [refactor-todo] check if this is still required */
#define portMUX_TRY_LOCK SPINLOCK_NO_WAIT /**< Try to acquire the spinlock a single time only. [refactor-todo] check if this is still required */
#define portMUX_INITIALIZE(mux) spinlock_initialize(mux) /*< Initialize a spinlock to its unlocked state */
// ----------------------- Memory --------------------------
// --------------------- Interrupts ------------------------
BaseType_t xPortCheckIfInISR(void);
// ------------------ Critical Sections --------------------
/*
These are always called with interrupts already disabled. We simply need to get/release the spinlocks
*/
extern portMUX_TYPE port_xTaskLock;
extern portMUX_TYPE port_xISRLock;
void vPortTakeLock( portMUX_TYPE *lock );
void vPortReleaseLock( portMUX_TYPE *lock );
// ---------------------- Yielding -------------------------
void vPortYield( void );
extern void vPortYieldFromISR( void );
#define portYIELD_FROM_ISR_CHECK(x) ({ \
if ( (x) == pdTRUE ) { \
vPortYieldFromISR(); \
} \
})
#define portYIELD_FROM_ISR_NO_CHECK() vPortYieldFromISR()
// ----------------------- System --------------------------
static inline BaseType_t __attribute__((always_inline)) xPortGetCoreID( void );
// ----------------------- TCB Cleanup --------------------------
void vPortCleanUpTCB ( void *pxTCB );
/* ----------------------------------------- FreeRTOS SMP Porting Interface --------------------------------------------
* - Contains all the mappings of the macros required by FreeRTOS SMP
* - Must come after forward declare as some porting macros map to declared functions
* ------------------------------------------------------------------------------------------------------------------ */
// ----------------------- Memory --------------------------
// --------------------- Interrupts ------------------------
#define portDISABLE_INTERRUPTS() xPortSetInterruptMask()
#define portENABLE_INTERRUPTS() vPortClearInterruptMask(0)
#define portRESTORE_INTERRUPTS(x) vPortClearInterruptMask(x)
// ------------------ Critical Sections --------------------
#define portGET_TASK_LOCK() vPortTakeLock(&port_xTaskLock)
#define portRELEASE_TASK_LOCK() vPortReleaseLock(&port_xTaskLock)
#define portGET_ISR_LOCK() vPortTakeLock(&port_xISRLock)
#define portRELEASE_ISR_LOCK() vPortReleaseLock(&port_xISRLock)
//Critical sections used by FreeRTOS SMP
extern void vTaskEnterCritical( void );
extern void vTaskExitCritical( void );
#define portENTER_CRITICAL_SMP() vTaskEnterCritical();
#define portEXIT_CRITICAL_SMP() vTaskExitCritical();
#if defined(__cplusplus) && (__cplusplus > 201703L)
#define portENTER_CRITICAL(...) CHOOSE_MACRO_VA_ARG(portENTER_CRITICAL_IDF, portENTER_CRITICAL_SMP __VA_OPT__(,) __VA_ARGS__)(__VA_ARGS__)
#define portEXIT_CRITICAL(...) CHOOSE_MACRO_VA_ARG(portEXIT_CRITICAL_IDF, portEXIT_CRITICAL_SMP __VA_OPT__(,) __VA_ARGS__)(__VA_ARGS__)
#else
#define portENTER_CRITICAL(...) CHOOSE_MACRO_VA_ARG(portENTER_CRITICAL_IDF, portENTER_CRITICAL_SMP, ##__VA_ARGS__)(__VA_ARGS__)
#define portEXIT_CRITICAL(...) CHOOSE_MACRO_VA_ARG(portEXIT_CRITICAL_IDF, portEXIT_CRITICAL_SMP, ##__VA_ARGS__)(__VA_ARGS__)
#endif
extern portBASE_TYPE xPortSetInterruptMask( void );
extern void vPortClearInterruptMask( portBASE_TYPE xMask );
#define portSET_INTERRUPT_MASK_FROM_ISR() ({ \
portBASE_TYPE cur_level; \
cur_level = xPortSetInterruptMask(); \
vTaskEnterCritical(); \
cur_level; \
})
#define portCLEAR_INTERRUPT_MASK_FROM_ISR(x) ({ \
vTaskExitCritical(); \
vPortClearInterruptMask(x); \
})
// ---------------------- Yielding -------------------------
#define portYIELD() vPortYield()
#if defined(__cplusplus) && (__cplusplus > 201703L)
#define portYIELD_FROM_ISR(...) CHOOSE_MACRO_VA_ARG(portYIELD_FROM_ISR_CHECK, portYIELD_FROM_ISR_NO_CHECK __VA_OPT__(,) __VA_ARGS__)(__VA_ARGS__)
#else
#define portYIELD_FROM_ISR(...) CHOOSE_MACRO_VA_ARG(portYIELD_FROM_ISR_CHECK, portYIELD_FROM_ISR_NO_CHECK, ##__VA_ARGS__)(__VA_ARGS__)
#endif
// Note that the Linux port does not provide portYIELD_CORE(x) because purely single core for now.
// ----------------------- System --------------------------
#define portGET_CORE_ID() xPortGetCoreID()
#define portCHECK_IF_IN_ISR() xPortCheckIfInISR()
// ------------------- Run Time Stats ----------------------
extern unsigned long ulPortGetRunTime( void );
#define portCONFIGURE_TIMER_FOR_RUN_TIME_STATS() /* no-op */
#define portGET_RUN_TIME_COUNTER_VALUE() ulPortGetRunTime()
// TODO IDF-5723: no esp_timer implementation
// ------------------- TCB Cleanup ----------------------
#define portCLEAN_UP_TCB( pxTCB ) vPortCleanUpTCB( pxTCB )
/* --------------------------------------------- Inline Implementations ------------------------------------------------
* - Implementation of inline functions of the forward declares
* - Should come after forward declare and FreeRTOS Porting interface, as implementation may use both.
* - For implementation of non-inlined functions, see port.c
* ------------------------------------------------------------------------------------------------------------------ */
// --------------------- Interrupts ------------------------
// ------------------ Critical Sections --------------------
// ---------------------- Yielding -------------------------
// NOTE: vPortYieldCore( BaseType_t xCoreID ) is currently not implemented as the simulator is single core
// ----------------------- System --------------------------
/**
* @brief Get the current core's ID
*
* @note dummy function for freertos simulator, always returns 0.
@ return BaseType_t 0
*/
static inline BaseType_t xPortGetCoreID(void)
{
return (BaseType_t) 0; // POSIX simulator is only single-core at the moment
}
/* ------------------------------------------------ IDF Compatibility --------------------------------------------------
* - These macros and functions need to be defined for IDF to compile
* ------------------------------------------------------------------------------------------------------------------ */
// --------------------- Interrupts ------------------------
static inline BaseType_t xPortInIsrContext(void)
{
//Just call the FreeRTOS port interface version
return xPortCheckIfInISR();
}
// xPortInterruptedFromISRContext() is only used in panic handler and core dump,
// both probably not relevant on POSIX sim.
//BaseType_t xPortInterruptedFromISRContext(void);
// Following functions seem to not be used in SMP:
//static inline UBaseType_t xPortSetInterruptMaskFromISR(void)
//static inline void vPortClearInterruptMaskFromISR(UBaseType_t prev_level)
// Following functions should have been removed according to
// docs/en/migration-guides/release-5.x/5.0/system.rst
//static inline bool __attribute__((always_inline)) vPortCPUAcquireMutexTimeout(portMUX_TYPE *mux, int timeout)
//static inline void __attribute__((always_inline)) vPortCPUReleaseMutex(portMUX_TYPE *mux)
// ------------------ Critical Sections --------------------
void vPortEnterCriticalIDF(void);
void vPortExitCriticalIDF(void);
// We ignore the lock and timeout argument on POSIX simulator since it is single core
//IDF task critical sections
#define portTRY_ENTER_CRITICAL(lock, timeout) ({(void) lock; (void) timeout; vPortEnterCriticalIDF(); pdPASS;})
#define portENTER_CRITICAL_IDF(lock) ({(void) lock; vPortEnterCriticalIDF();})
#define portEXIT_CRITICAL_IDF(lock) ({(void) lock; vPortExitCriticalIDF();})
//IDF ISR critical sections
#define portTRY_ENTER_CRITICAL_ISR(lock, timeout) ({(void) lock; (void) timeout; vPortEnterCriticalIDF(); pdPASS;})
#define portENTER_CRITICAL_ISR(lock) ({(void) lock; vPortEnterCriticalIDF();})
#define portEXIT_CRITICAL_ISR(lock) ({(void) lock; vPortExitCriticalIDF();})
//IDF safe critical sections (they're the same)
#define portENTER_CRITICAL_SAFE(lock) ({(void) lock; vPortEnterCriticalIDF();})
#define portEXIT_CRITICAL_SAFE(lock) ({(void) lock; vPortExitCriticalIDF();})
// ---------------------- Yielding -------------------------
// Added for backward compatibility with IDF
#define portYIELD_WITHIN_API() vTaskYieldWithinAPI()
// ----------------------- System --------------------------
bool portVALID_TCB_MEM(const void *ptr);
bool portVALID_STACK_MEM(const void *ptr);
/*-----------------------------------------------------------*/
/* POSIX-specific port functions */
extern void vPortThreadDying( void *pxTaskToDelete, volatile BaseType_t *pxPendYield );
extern void vPortCancelThread( void *pxTaskToDelete );
#define portPRE_TASK_DELETE_HOOK( pvTaskToDelete, pxPendYield ) vPortThreadDying( ( pvTaskToDelete ), ( pxPendYield ) )
/*-----------------------------------------------------------*/
/*
* Tasks run in their own pthreads and context switches between them
* are always a full memory barrier. ISRs are emulated as signals
* which also imply a full memory barrier.
*
* Thus, only a compilier barrier is needed to prevent the compiler
* reordering.
*/
#define portMEMORY_BARRIER() __asm volatile( "" ::: "memory" )
#ifdef __cplusplus
}
#endif
#endif /* PORTMACRO_H */

View File

@ -0,0 +1,59 @@
/*
* SPDX-FileCopyrightText: 2015-2022 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
/*
* This file provides only very simple stubs to build IDF-based FreeRTOSes which use spinlocks on Linux.
*/
#pragma once
#include <stdint.h>
#include <stdbool.h>
#ifdef __cplusplus
extern "C" {
#endif
#define SPINLOCK_FREE 0xB33FFFFF
#define SPINLOCK_WAIT_FOREVER (-1)
#define SPINLOCK_NO_WAIT 0
#define SPINLOCK_INITIALIZER {.owner = SPINLOCK_FREE,.count = 0}
#define CORE_ID_REGVAL_XOR_SWAP (0xCDCD ^ 0xABAB)
/**
* @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 The simulator is single-core, hence, it doesn't have a proper spinlock implementation.
* @note Keep portMUX_INITIALIZER_UNLOCKED in sync with this struct
*/
typedef struct {
uint32_t owner;
uint32_t count;
}spinlock_t;
static inline void __attribute__((always_inline)) spinlock_initialize(spinlock_t *lock)
{
}
static inline bool __attribute__((always_inline)) spinlock_acquire(spinlock_t *lock, int32_t timeout)
{
return true;
}
static inline void __attribute__((always_inline)) spinlock_release(spinlock_t *lock)
{
}
#ifdef __cplusplus
}
#endif

View File

@ -0,0 +1,823 @@
/*
* SPDX-FileCopyrightText: 2020 Amazon.com, Inc. or its affiliates
*
* SPDX-License-Identifier: MIT
*/
/*
* FreeRTOS Kernel V10.4.3
* Copyright (C) 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
* the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
* https://www.FreeRTOS.org
* https://github.com/FreeRTOS
*
*/
/*-----------------------------------------------------------
* Implementation of functions defined in portable.h for the Posix port.
*
* Each task has a pthread which eases use of standard debuggers
* (allowing backtraces of tasks etc). Threads for tasks that are not
* running are blocked in sigwait().
*
* Task switch is done by resuming the thread for the next task by
* signaling the condition variable and then waiting on a condition variable
* with the current thread.
*
* The timer interrupt uses SIGALRM and care is taken to ensure that
* the signal handler runs only on the thread for the current task.
*
* Use of part of the standard C library requires care as some
* functions can take pthread mutexes internally which can result in
* deadlocks as the FreeRTOS kernel can switch tasks while they're
* holding a pthread mutex.
*
* stdio (printf() and friends) should be called from a single task
* only or serialized with a FreeRTOS primitive such as a binary
* semaphore or mutex.
*----------------------------------------------------------*/
#include <errno.h>
#include <pthread.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/time.h>
#include <sys/times.h>
#include <time.h>
#include <unistd.h>
#include <assert.h>
/* Scheduler includes. */
#include "esp_heap_caps.h"
#include "FreeRTOS.h"
#include "task.h"
#include "esp_task.h"
#include "timers.h"
#include "utils/wait_for_event.h"
#include "esp_log.h"
/*-----------------------------------------------------------*/
#define SIG_RESUME SIGUSR1
typedef struct THREAD
{
pthread_t pthread;
TaskFunction_t pxCode;
void *pvParams;
BaseType_t xDying;
struct event *ev;
} Thread_t;
/*
* The additional per-thread data is stored at the beginning of the
* task's stack.
*/
static inline Thread_t *prvGetThreadFromTask(TaskHandle_t xTask)
{
StackType_t *pxTopOfStack = *(StackType_t **)xTask;
return (Thread_t *)(pxTopOfStack + 1);
}
/*-----------------------------------------------------------*/
static pthread_once_t hSigSetupThread = PTHREAD_ONCE_INIT;
static sigset_t xAllSignals;
static sigset_t xSchedulerOriginalSignalMask;
static pthread_t hMainThread = ( pthread_t )NULL;
// These are saved as part of a thread's state in prvSwitchThread()
static volatile portBASE_TYPE uxCriticalNestingIDF = 0; /* Track nesting calls for IDF style critical sections. FreeRTOS critical section nesting is maintained in the TCB. */
static volatile UBaseType_t uxInterruptNesting = 0; /* Tracks if we are currently in an interrupt. */
static volatile portBASE_TYPE uxInterruptLevel = 0; /* Tracks the current level (i.e., interrupt mask) */
/*-----------------------------------------------------------*/
static portBASE_TYPE xSchedulerEnd = pdFALSE;
/*-----------------------------------------------------------*/
static void prvSetupSignalsAndSchedulerPolicy( void );
static void prvSetupTimerInterrupt( void );
static void *prvWaitForStart( void * pvParams );
static void prvSwitchThread( Thread_t * xThreadToResume,
Thread_t *xThreadToSuspend );
static void prvSuspendSelf( Thread_t * thread);
static void prvResumeThread( Thread_t * xThreadId );
static void vPortSystemTickHandler( int sig );
static void vPortStartFirstTask( void );
/*-----------------------------------------------------------*/
static void prvFatalError( const char *pcCall, int iErrno )
{
fprintf( stderr, "%s: %s\n", pcCall, strerror( iErrno ) );
abort();
}
/*
* See header file for description.
*/
portSTACK_TYPE *pxPortInitialiseStack( portSTACK_TYPE *pxTopOfStack,
portSTACK_TYPE *pxEndOfStack,
TaskFunction_t pxCode, void *pvParameters )
{
Thread_t *thread;
pthread_attr_t xThreadAttributes;
size_t ulStackSize;
int iRet;
(void)pthread_once( &hSigSetupThread, prvSetupSignalsAndSchedulerPolicy );
/*
* Store the additional thread data at the start of the stack.
*/
thread = (Thread_t *)(pxTopOfStack + 1) - 1;
pxTopOfStack = (portSTACK_TYPE *)thread - 1;
ulStackSize = (pxTopOfStack + 1 - pxEndOfStack) * sizeof(*pxTopOfStack);
thread->pxCode = pxCode;
thread->pvParams = pvParameters;
thread->xDying = pdFALSE;
pthread_attr_init( &xThreadAttributes );
pthread_attr_setstack( &xThreadAttributes, pxEndOfStack, ulStackSize );
thread->ev = event_create();
BaseType_t prev_intr_level = xPortSetInterruptMask();
iRet = pthread_create( &thread->pthread, &xThreadAttributes,
prvWaitForStart, thread );
if ( iRet )
{
prvFatalError( "pthread_create", iRet );
}
vPortClearInterruptMask( prev_intr_level );
return pxTopOfStack;
}
/*-----------------------------------------------------------*/
void vPortStartFirstTask( void )
{
Thread_t *pxFirstThread = prvGetThreadFromTask( xTaskGetCurrentTaskHandle() );
/* Start the first task. */
prvResumeThread( pxFirstThread );
}
/*-----------------------------------------------------------*/
/*
* See header file for description.
*/
portBASE_TYPE xPortStartScheduler( void )
{
int iSignal;
sigset_t xSignals;
hMainThread = pthread_self();
/* Start the timer that generates the tick ISR(SIGALRM).
Interrupts are disabled here already. */
prvSetupTimerInterrupt();
/* Start the first task. */
vPortStartFirstTask();
/* Wait until signaled by vPortEndScheduler(). */
sigemptyset( &xSignals );
sigaddset( &xSignals, SIG_RESUME );
while ( !xSchedulerEnd )
{
sigwait( &xSignals, &iSignal );
}
/* Cancel the Idle task and free its resources */
#if ( INCLUDE_xTaskGetIdleTaskHandle == 1 )
vPortCancelThread( xTaskGetIdleTaskHandle() );
#endif
#if ( configUSE_TIMERS == 1 )
/* Cancel the Timer task and free its resources */
vPortCancelThread( xTimerGetTimerDaemonTaskHandle() );
#endif /* configUSE_TIMERS */
/* Restore original signal mask. */
(void)pthread_sigmask( SIG_SETMASK, &xSchedulerOriginalSignalMask, NULL );
return 0;
}
/*-----------------------------------------------------------*/
void vPortEndScheduler( void )
{
struct itimerval itimer;
struct sigaction sigtick;
Thread_t *xCurrentThread;
/* Stop the timer and ignore any pending SIGALRMs that would end
* up running on the main thread when it is resumed. */
itimer.it_value.tv_sec = 0;
itimer.it_value.tv_usec = 0;
itimer.it_interval.tv_sec = 0;
itimer.it_interval.tv_usec = 0;
(void)setitimer( ITIMER_REAL, &itimer, NULL );
sigtick.sa_flags = 0;
sigtick.sa_handler = SIG_IGN;
sigemptyset( &sigtick.sa_mask );
sigaction( SIGALRM, &sigtick, NULL );
/* Signal the scheduler to exit its loop. */
xSchedulerEnd = pdTRUE;
(void)pthread_kill( hMainThread, SIG_RESUME );
xCurrentThread = prvGetThreadFromTask( xTaskGetCurrentTaskHandle() );
prvSuspendSelf(xCurrentThread);
}
/*-----------------------------------------------------------*/
static void vPortDisableInterrupts( void )
{
pthread_sigmask( SIG_BLOCK, &xAllSignals, NULL );
}
/*-----------------------------------------------------------*/
static void vPortEnableInterrupts( void )
{
pthread_sigmask( SIG_UNBLOCK, &xAllSignals, NULL );
}
/*-----------------------------------------------------------*/
void vPortEnterCriticalIDF( void )
{
if ( uxCriticalNestingIDF == 0 && uxInterruptLevel == 0)
{
vPortDisableInterrupts();
}
uxCriticalNestingIDF++;
}
/*-----------------------------------------------------------*/
void vPortExitCriticalIDF( void )
{
uxCriticalNestingIDF--;
/* If we have reached 0 then re-enable the interrupts. */
if( uxCriticalNestingIDF == 0 && uxInterruptLevel == 0)
{
vPortEnableInterrupts();
}
}
/*-----------------------------------------------------------*/
void vPortYieldFromISR( void )
{
Thread_t *xThreadToSuspend;
Thread_t *xThreadToResume;
xThreadToSuspend = prvGetThreadFromTask( xTaskGetCurrentTaskHandle() );
vTaskSwitchContext(xPortGetCoreID());
xThreadToResume = prvGetThreadFromTask( xTaskGetCurrentTaskHandle() );
prvSwitchThread( xThreadToResume, xThreadToSuspend );
}
/*-----------------------------------------------------------*/
void vPortYield( void )
{
BaseType_t prev_intr_level = xPortSetInterruptMask();
vPortYieldFromISR();
vPortClearInterruptMask( prev_intr_level );
}
/*-----------------------------------------------------------*/
/* In SMP code, the disable/enable interrupt macros are calling the set/get interrupt mask functions below.
Hence, we need to call vPortDisableInterrupts() and vPortEnableInterrupts(), otherwise interrupts
are never disabled/enabled. */
portBASE_TYPE xPortSetInterruptMask( void )
{
if (uxInterruptLevel == 0 && uxCriticalNestingIDF == 0) {
vPortDisableInterrupts();
}
portBASE_TYPE prev_intr_level = uxInterruptLevel;
uxInterruptLevel++;
return prev_intr_level;
}
/*-----------------------------------------------------------*/
void vPortClearInterruptMask( portBASE_TYPE xMask )
{
// Only reenable interrupts if xMask is 0
uxInterruptLevel = xMask;
if (uxInterruptLevel == 0 && uxCriticalNestingIDF == 0) {
vPortEnableInterrupts();
}
}
/*-----------------------------------------------------------*/
static uint64_t prvGetTimeNs(void)
{
struct timespec t;
clock_gettime(CLOCK_MONOTONIC, &t);
return t.tv_sec * 1000000000ull + t.tv_nsec;
}
static uint64_t prvStartTimeNs;
/* commented as part of the code below in vPortSystemTickHandler,
* to adjust timing according to full demo requirements */
/* static uint64_t prvTickCount; */
/*
* Setup the systick timer to generate the tick interrupts at the required
* frequency.
*/
void prvSetupTimerInterrupt( void )
{
struct itimerval itimer;
int iRet;
/* Initialise the structure with the current timer information. */
iRet = getitimer( ITIMER_REAL, &itimer );
if ( iRet )
{
prvFatalError( "getitimer", errno );
}
/* Set the interval between timer events. */
itimer.it_interval.tv_sec = 0;
itimer.it_interval.tv_usec = portTICK_RATE_MICROSECONDS;
/* Set the current count-down. */
itimer.it_value.tv_sec = 0;
itimer.it_value.tv_usec = portTICK_RATE_MICROSECONDS;
/* Set-up the timer interrupt. */
iRet = setitimer( ITIMER_REAL, &itimer, NULL );
if ( iRet )
{
prvFatalError( "setitimer", errno );
}
prvStartTimeNs = prvGetTimeNs();
}
/*-----------------------------------------------------------*/
static void vPortSystemTickHandler( int sig )
{
Thread_t *pxThreadToSuspend;
Thread_t *pxThreadToResume;
BaseType_t xSwitchRequired;
/* uint64_t xExpectedTicks; */
// Handling a timer signal, so we are currently in an interrupt.
uxInterruptNesting++;
#if ( configUSE_PREEMPTION == 1 )
pxThreadToSuspend = prvGetThreadFromTask( xTaskGetCurrentTaskHandle() );
#endif
/* Tick Increment, accounting for any lost signals or drift in
* the timer. */
/*
* Comment code to adjust timing according to full demo requirements
* xExpectedTicks = (prvGetTimeNs() - prvStartTimeNs)
* / (portTICK_RATE_MICROSECONDS * 1000);
* do { */
xSwitchRequired = xTaskIncrementTick();
/* prvTickCount++;
* } while (prvTickCount < xExpectedTicks);
*/
#if ( configUSE_PREEMPTION == 1 )
if (xSwitchRequired == pdTRUE) {
/* Select Next Task. */
vTaskSwitchContext(xPortGetCoreID());
pxThreadToResume = prvGetThreadFromTask( xTaskGetCurrentTaskHandle() );
prvSwitchThread(pxThreadToResume, pxThreadToSuspend);
}
#else
(void)xSwitchRequired;
#endif
// Returning from the timer signal handler, so we are exiting the interrupt.
uxInterruptNesting--;
}
/*-----------------------------------------------------------*/
void vPortThreadDying( void *pxTaskToDelete, volatile BaseType_t *pxPendYield )
{
Thread_t *pxThread = prvGetThreadFromTask( pxTaskToDelete );
pxThread->xDying = pdTRUE;
}
void vPortCancelThread( void *pxTaskToDelete )
{
Thread_t *pxThreadToCancel = prvGetThreadFromTask( pxTaskToDelete );
/*
* The thread has already been suspended so it can be safely cancelled.
*/
pthread_cancel( pxThreadToCancel->pthread );
pthread_join( pxThreadToCancel->pthread, NULL );
event_delete( pxThreadToCancel->ev );
}
/*-----------------------------------------------------------*/
static void *prvWaitForStart( void * pvParams )
{
Thread_t *pxThread = pvParams;
prvSuspendSelf(pxThread);
/* Resumed for the first time, thus this thread didn't previously call
* prvSwitchThread(). So we need to initialise the state variables for this
* thread. */
uxCriticalNestingIDF = 0;
uxInterruptNesting = 0;
uxInterruptLevel = 0;
vPortEnableInterrupts();
/* Call the task's entry point. */
pxThread->pxCode( pxThread->pvParams );
/* A function that implements a task must not exit or attempt to return to
* its caller as there is nothing to return to. If a task wants to exit it
* should instead call vTaskDelete( NULL ). Artificially force an assert()
* to be triggered if configASSERT() is defined, so application writers can
* catch the error. */
configASSERT( pdFALSE );
return NULL;
}
/*-----------------------------------------------------------*/
static void prvSwitchThread( Thread_t *pxThreadToResume,
Thread_t *pxThreadToSuspend )
{
BaseType_t uxSavedCriticalNestingIDF;
BaseType_t uxSavedInterruptNesting;
BaseType_t uxSavedInterruptLevel;
if ( pxThreadToSuspend != pxThreadToResume )
{
/* It is possible for prvSwitchThread() to be called...
* - while inside an ISR (i.e., via vPortSystemTickHandler() or vPortYieldFromISR())
* - while interrupts are disabled or in a critical section (i.e., via vPortYield())
*
* So we need to save the various count variables as part of the thread's context.
* They are restored when the pthread switches back. */
uxSavedCriticalNestingIDF = uxCriticalNestingIDF;
uxSavedInterruptNesting = uxInterruptNesting;
uxSavedInterruptLevel = uxInterruptLevel;
prvResumeThread( pxThreadToResume );
if ( pxThreadToSuspend->xDying )
{
pthread_exit( NULL );
}
prvSuspendSelf( pxThreadToSuspend );
uxCriticalNestingIDF = uxSavedCriticalNestingIDF;
uxInterruptNesting = uxSavedInterruptNesting;
uxInterruptLevel = uxSavedInterruptLevel;
}
}
/*-----------------------------------------------------------*/
static void prvSuspendSelf( Thread_t *thread )
{
/*
* Suspend this thread by waiting for a pthread_cond_signal event.
*
* A suspended thread must not handle signals (interrupts) so
* all signals must be blocked by calling this from:
*
* - Inside a critical section (vPortEnterCritical() /
* vPortExitCritical()).
*
* - From a signal handler that has all signals masked.
*
* - A thread with all signals blocked with pthread_sigmask().
*/
event_wait(thread->ev);
}
/*-----------------------------------------------------------*/
static void prvResumeThread( Thread_t *xThreadId )
{
if ( pthread_self() != xThreadId->pthread )
{
event_signal(xThreadId->ev);
}
}
/*-----------------------------------------------------------*/
static void prvSetupSignalsAndSchedulerPolicy( void )
{
struct sigaction sigresume, sigtick;
int iRet;
hMainThread = pthread_self();
/* Initialise common signal masks. */
sigfillset( &xAllSignals );
/* Don't block SIGINT so this can be used to break into GDB while
* in a critical section. */
sigdelset( &xAllSignals, SIGINT );
/*
* Block all signals in this thread so all new threads
* inherits this mask.
*
* When a thread is resumed for the first time, all signals
* will be unblocked.
*/
(void)pthread_sigmask( SIG_SETMASK, &xAllSignals,
&xSchedulerOriginalSignalMask );
/* SIG_RESUME is only used with sigwait() so doesn't need a
handler. */
sigresume.sa_flags = 0;
sigresume.sa_handler = SIG_IGN;
sigfillset( &sigresume.sa_mask );
sigtick.sa_flags = 0;
sigtick.sa_handler = vPortSystemTickHandler;
sigfillset( &sigtick.sa_mask );
iRet = sigaction( SIG_RESUME, &sigresume, NULL );
if ( iRet )
{
prvFatalError( "sigaction", errno );
}
iRet = sigaction( SIGALRM, &sigtick, NULL );
if ( iRet )
{
prvFatalError( "sigaction", errno );
}
}
/*-----------------------------------------------------------*/
unsigned long ulPortGetRunTime( void )
{
struct tms xTimes;
times( &xTimes );
return ( unsigned long ) xTimes.tms_utime;
}
/*-----------------------------------------------------------*/
bool portVALID_TCB_MEM(const void *ptr)
{
return true;
}
bool portVALID_STACK_MEM(const void *ptr)
{
return true;
}
/*-----------------------------------------------------------*/
portMUX_TYPE port_xTaskLock = portMUX_INITIALIZER_UNLOCKED;
portMUX_TYPE port_xISRLock = portMUX_INITIALIZER_UNLOCKED;
static const char *TAG = "port";
/* When configSUPPORT_STATIC_ALLOCATION is set to 1 the application writer can
* use a callback function to optionally provide the memory required by the idle
* and timer tasks. This is the stack that will be used by the timer task. It is
* declared here, as a global, so it can be checked by a test that is implemented
* in a different file. */
StackType_t uxTimerTaskStack[ configTIMER_TASK_STACK_DEPTH ];
BaseType_t xPortCheckIfInISR(void)
{
return (uxInterruptNesting == 0) ? pdFALSE : pdTRUE;
}
void app_main(void);
static void main_task(void* args)
{
app_main();
vTaskDelete(NULL);
}
int main(int argc, const char **argv)
{
// This makes sure that stdio is flushed after each '\n' so that idf.py monitor
// reads the program output on time.
setvbuf(stdout, NULL, _IOLBF, 0);
usleep(1000);
portBASE_TYPE res;
#if ( configNUM_CORES > 1 )
res = xTaskCreateAffinitySet(&main_task, "main",
ESP_TASK_MAIN_STACK, NULL,
ESP_TASK_MAIN_PRIO, ESP_TASK_MAIN_CORE, NULL);
#else
res = xTaskCreate(&main_task, "main",
ESP_TASK_MAIN_STACK, NULL,
ESP_TASK_MAIN_PRIO, NULL);
#endif
assert(res == pdTRUE);
(void)res;
ESP_LOGI(TAG, "Starting SMP scheduler.");
vTaskStartScheduler();
// This line should never be reached
assert(false);
}
void esp_vApplicationIdleHook(void)
{
/* vApplicationIdleHook() will only be called if configUSE_IDLE_HOOK is set
* to 1 in FreeRTOSConfig.h. It will be called on each iteration of the idle
* task. It is essential that code added to this hook function never attempts
* to block in any way (for example, call xQueueReceive() with a block time
* specified, or call vTaskDelay()). If application tasks make use of the
* vTaskDelete() API function to delete themselves then it is also important
* that vApplicationIdleHook() is permitted to return to its calling function,
* because it is the responsibility of the idle task to clean up memory
* allocated by the kernel to any task that has since deleted itself. */
usleep( 15000 );
}
void esp_vApplicationTickHook( void ) { }
#if ( configUSE_TICK_HOOK > 0 )
void vApplicationTickHook( void )
{
esp_vApplicationTickHook();
}
#endif
#if ( configSUPPORT_STATIC_ALLOCATION == 1 )
/* configUSE_STATIC_ALLOCATION is set to 1, so the application must provide an
* implementation of vApplicationGetIdleTaskMemory() to provide the memory that is
* used by the Idle task. */
void vApplicationGetIdleTaskMemory( StaticTask_t ** ppxIdleTaskTCBBuffer,
StackType_t ** ppxIdleTaskStackBuffer,
uint32_t * pulIdleTaskStackSize )
{
/* If the buffers to be provided to the Idle task are declared inside this
* function then they must be declared static - otherwise they will be allocated on
* the stack and so not exists after this function exits. */
static StaticTask_t xIdleTaskTCB;
static StackType_t uxIdleTaskStack[ configMINIMAL_STACK_SIZE ];
/* Pass out a pointer to the StaticTask_t structure in which the Idle task's
* state will be stored. */
*ppxIdleTaskTCBBuffer = &xIdleTaskTCB;
/* Pass out the array that will be used as the Idle task's stack. */
*ppxIdleTaskStackBuffer = uxIdleTaskStack;
/* Pass out the size of the array pointed to by *ppxIdleTaskStackBuffer.
* Note that, as the array is necessarily of type StackType_t,
* configMINIMAL_STACK_SIZE is specified in words, not bytes. */
*pulIdleTaskStackSize = configMINIMAL_STACK_SIZE;
}
#endif // configSUPPORT_STATIC_ALLOCATION == 1
/*-----------------------------------------------------------*/
#if ( configSUPPORT_STATIC_ALLOCATION == 1 )
/* configUSE_STATIC_ALLOCATION and configUSE_TIMERS are both set to 1, so the
* application must provide an implementation of vApplicationGetTimerTaskMemory()
* to provide the memory that is used by the Timer service task. */
void vApplicationGetTimerTaskMemory( StaticTask_t ** ppxTimerTaskTCBBuffer,
StackType_t ** ppxTimerTaskStackBuffer,
uint32_t * pulTimerTaskStackSize )
{
/* If the buffers to be provided to the Timer task are declared inside this
* function then they must be declared static - otherwise they will be allocated on
* the stack and so not exists after this function exits. */
static StaticTask_t xTimerTaskTCB;
/* Pass out a pointer to the StaticTask_t structure in which the Timer
* task's state will be stored. */
*ppxTimerTaskTCBBuffer = &xTimerTaskTCB;
/* Pass out the array that will be used as the Timer task's stack. */
*ppxTimerTaskStackBuffer = uxTimerTaskStack;
/* Pass out the size of the array pointed to by *ppxTimerTaskStackBuffer.
* Note that, as the array is necessarily of type StackType_t,
* configMINIMAL_STACK_SIZE is specified in words, not bytes. */
*pulTimerTaskStackSize = configTIMER_TASK_STACK_DEPTH;
}
#endif // configSUPPORT_STATIC_ALLOCATION == 1
void vPortTakeLock( portMUX_TYPE *lock )
{
spinlock_acquire( lock, portMUX_NO_TIMEOUT);
}
void vPortReleaseLock( portMUX_TYPE *lock )
{
spinlock_release( lock );
}
#define FREERTOS_SMP_MALLOC_CAPS (MALLOC_CAP_INTERNAL|MALLOC_CAP_8BIT)
void *pvPortMalloc( size_t xSize )
{
return heap_caps_malloc(xSize, FREERTOS_SMP_MALLOC_CAPS);
}
void vPortFree( void *pv )
{
heap_caps_free(pv);
}
void __attribute__((weak)) vApplicationStackOverflowHook(TaskHandle_t xTask, char *pcTaskName)
{
#define ERR_STR1 "***ERROR*** A stack overflow in task "
#define ERR_STR2 " has been detected."
const char *str[] = {ERR_STR1, pcTaskName, ERR_STR2};
char buf[sizeof(ERR_STR1) + CONFIG_FREERTOS_MAX_TASK_NAME_LEN + sizeof(ERR_STR2) + 1 /* null char */] = {0};
char *dest = buf;
for (int i = 0; i < sizeof(str) / sizeof(str[0]); i++) {
dest = strcat(dest, str[i]);
}
printf("%s\n", buf);
abort();
}
// ------- Thread Local Storage Pointers Deletion Callbacks -------
#if ( CONFIG_FREERTOS_TLSP_DELETION_CALLBACKS )
void vPortTLSPointersDelCb( void *pxTCB )
{
/* Typecast pxTCB to StaticTask_t type to access TCB struct members.
* pvDummy15 corresponds to pvThreadLocalStoragePointers member of the TCB.
*/
StaticTask_t *tcb = ( StaticTask_t * )pxTCB;
/* The TLSP deletion callbacks are stored at an offset of (configNUM_THREAD_LOCAL_STORAGE_POINTERS/2) */
TlsDeleteCallbackFunction_t *pvThreadLocalStoragePointersDelCallback = ( TlsDeleteCallbackFunction_t * )( &( tcb->pvDummy15[ ( configNUM_THREAD_LOCAL_STORAGE_POINTERS / 2 ) ] ) );
/* We need to iterate over half the depth of the pvThreadLocalStoragePointers area
* to access all TLS pointers and their respective TLS deletion callbacks.
*/
for ( int x = 0; x < ( configNUM_THREAD_LOCAL_STORAGE_POINTERS / 2 ); x++ ) {
if ( pvThreadLocalStoragePointersDelCallback[ x ] != NULL ) { //If del cb is set
// We skip the check if the callback is executable as that is difficult to determine for different
// platforms (compare xtensa and riscv code).
pvThreadLocalStoragePointersDelCallback[ x ]( x, tcb->pvDummy15[ x ] ); //Call del cb
}
}
}
#endif // CONFIG_FREERTOS_TLSP_DELETION_CALLBACKS
void vPortCleanUpTCB ( void *pxTCB )
{
#if ( CONFIG_FREERTOS_TLSP_DELETION_CALLBACKS )
/* Call TLS pointers deletion callbacks */
vPortTLSPointersDelCb( pxTCB );
#endif /* CONFIG_FREERTOS_TLSP_DELETION_CALLBACKS */
vPortCancelThread(pxTCB);
}

View File

@ -0,0 +1,109 @@
/*
* SPDX-FileCopyrightText: 2021 Amazon.com, Inc. or its affiliates
*
* SPDX-License-Identifier: MIT
*/
/*
* FreeRTOS Kernel V10.4.6
* Copyright (C) 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* SPDX-License-Identifier: MIT
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
* the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
* https://www.FreeRTOS.org
* https://github.com/FreeRTOS
*
*/
#include <pthread.h>
#include <stdlib.h>
#include <errno.h>
#include "wait_for_event.h"
struct event
{
pthread_mutex_t mutex;
pthread_cond_t cond;
bool event_triggered;
};
struct event * event_create()
{
struct event * ev = malloc( sizeof( struct event ) );
ev->event_triggered = false;
pthread_mutex_init( &ev->mutex, NULL );
pthread_cond_init( &ev->cond, NULL );
return ev;
}
void event_delete( struct event * ev )
{
pthread_mutex_destroy( &ev->mutex );
pthread_cond_destroy( &ev->cond );
free( ev );
}
bool event_wait( struct event * ev )
{
pthread_mutex_lock( &ev->mutex );
while( ev->event_triggered == false )
{
pthread_cond_wait( &ev->cond, &ev->mutex );
}
ev->event_triggered = false;
pthread_mutex_unlock( &ev->mutex );
return true;
}
bool event_wait_timed( struct event * ev,
time_t ms )
{
struct timespec ts;
int ret = 0;
clock_gettime( CLOCK_REALTIME, &ts );
ts.tv_sec += ms / 1000;
ts.tv_nsec += ((ms % 1000) * 1000000);
pthread_mutex_lock( &ev->mutex );
while( (ev->event_triggered == false) && (ret == 0) )
{
ret = pthread_cond_timedwait( &ev->cond, &ev->mutex, &ts );
if( ( ret == -1 ) && ( errno == ETIMEDOUT ) )
{
return false;
}
}
ev->event_triggered = false;
pthread_mutex_unlock( &ev->mutex );
return true;
}
void event_signal( struct event * ev )
{
pthread_mutex_lock( &ev->mutex );
ev->event_triggered = true;
pthread_cond_signal( &ev->cond );
pthread_mutex_unlock( &ev->mutex );
}

View File

@ -0,0 +1,51 @@
/*
* SPDX-FileCopyrightText: 2021 Amazon.com, Inc. or its affiliates
*
* SPDX-License-Identifier: MIT
*/
/*
* FreeRTOS Kernel V10.4.6
* Copyright (C) 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* SPDX-License-Identifier: MIT
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
* the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
* https://www.FreeRTOS.org
* https://github.com/FreeRTOS
*
*/
#ifndef _WAIT_FOR_EVENT_H_
#define _WAIT_FOR_EVENT_H_
#include <stdbool.h>
#include <time.h>
struct event;
struct event * event_create(void);
void event_delete( struct event * );
bool event_wait( struct event * ev );
bool event_wait_timed( struct event * ev,
time_t ms );
void event_signal( struct event * ev );
#endif /* ifndef _WAIT_FOR_EVENT_H_ */

View File

@ -40,10 +40,12 @@
#include "stack_macros.h"
#ifdef ESP_PLATFORM
#if ( configUSE_NEWLIB_REENTRANT == 1 )
#include "esp_newlib.h" /* required for esp_reent_init() in tasks.c */
#undef _REENT_INIT_PTR
#define _REENT_INIT_PTR esp_reent_init
#endif
#endif
/* Lint e9021, e961 and e750 are suppressed as a MISRA exception justified
* because the MPU ports require MPU_WRAPPERS_INCLUDED_FROM_API_FILE to be defined

View File

@ -95,6 +95,12 @@ static inline BaseType_t xPortInIsrContext(void)
return xPortCheckIfInISR();
}
#if CONFIG_FREERTOS_ENABLE_STATIC_TASK_CLEAN_UP
/* If enabled, users must provide an implementation of vPortCleanUpTCB() */
extern void vPortCleanUpTCB ( void *pxTCB );
#define portCLEAN_UP_TCB( pxTCB ) vPortCleanUpTCB( pxTCB )
#endif /* CONFIG_FREERTOS_ENABLE_STATIC_TASK_CLEAN_UP */
#ifdef __cplusplus
}
#endif

View File

@ -154,6 +154,7 @@ menu "FreeRTOS"
config FREERTOS_ENABLE_BACKWARD_COMPATIBILITY
bool "configENABLE_BACKWARD_COMPATIBILITY"
default n
depends on !IDF_TARGET_LINUX
help
Enable backward compatibility with APIs prior to FreeRTOS v8.0.0. (see
configENABLE_BACKWARD_COMPATIBILITY documentation for more details).

View File

@ -29,10 +29,18 @@
/* Currently not used in Linux POSIX simulator */
#define configMAX_API_CALL_INTERRUPT_PRIORITY 0
/* ---------------- Amazon SMP FreeRTOS -------------------- */
#if CONFIG_FREERTOS_SMP
#define configUSE_MINIMAL_IDLE_HOOK 0 // Not implemented yet, TODO IDF-6654
#endif
/* ----------------------- System -------------------------- */
/* On the Linux simulator, we use the system-provided libc */
#define configUSE_NEWLIB_REENTRANT 0
#define configINCLUDE_FREERTOS_TASK_C_ADDITIONS_H 0
#define configINCLUDE_FREERTOS_TASK_C_ADDITIONS_H 1
/* ----------------------- Memory ------------------------- */
@ -46,6 +54,7 @@
#define INCLUDE_xTaskGetCurrentTaskHandle 0 /* not defined in POSIX simulator */
#define INCLUDE_vTaskDelayUntil 1
#define INCLUDE_uxTaskGetStackHighWaterMark2 0
/* ------------------------------------------------ ESP-IDF Additions --------------------------------------------------
*

View File

@ -25,9 +25,24 @@
#define configMINIMAL_STACK_SIZE ( CONFIG_FREERTOS_IDLE_TASK_STACKSIZE + configSTACK_OVERHEAD_TOTAL )
#define configMAX_API_CALL_INTERRUPT_PRIORITY 0
/* ---------------- Amazon SMP FreeRTOS -------------------- */
#if CONFIG_FREERTOS_SMP
#define configUSE_CORE_AFFINITY 1
/* This is always enabled to call IDF style idle hooks, by can be "--Wl,--wrap"
* if users enable CONFIG_FREERTOS_USE_MINIMAL_IDLE_HOOK. */
#define configUSE_MINIMAL_IDLE_HOOK 1
/* IDF Newlib supports dynamic reentrancy. We provide our own __getreent()
* function. */
#define configNEWLIB_REENTRANT_IS_DYNAMIC 1
#endif
/* ----------------------- System -------------------------- */
#define configUSE_NEWLIB_REENTRANT 1
#define configINCLUDE_FREERTOS_TASK_C_ADDITIONS_H 1
/* ----------------------- Memory ------------------------- */
@ -48,6 +63,7 @@
#define INCLUDE_xTaskDelayUntil 1
#define INCLUDE_xTaskGetCurrentTaskHandle 1
#define INCLUDE_uxTaskGetStackHighWaterMark2 1
/* ------------------------------------------------ ESP-IDF Additions --------------------------------------------------
*

View File

@ -52,9 +52,24 @@
#define configMINIMAL_STACK_SIZE ( CONFIG_FREERTOS_IDLE_TASK_STACKSIZE + configSTACK_OVERHEAD_TOTAL )
#define configMAX_API_CALL_INTERRUPT_PRIORITY XCHAL_EXCM_LEVEL
/* ---------------- Amazon SMP FreeRTOS -------------------- */
#if CONFIG_FREERTOS_SMP
#define configUSE_CORE_AFFINITY 1
/* This is always enabled to call IDF style idle hooks, by can be "--Wl,--wrap"
* if users enable CONFIG_FREERTOS_USE_MINIMAL_IDLE_HOOK. */
#define configUSE_MINIMAL_IDLE_HOOK 1
/* IDF Newlib supports dynamic reentrancy. We provide our own __getreent()
* function. */
#define configNEWLIB_REENTRANT_IS_DYNAMIC 1
#endif
/* ----------------------- System -------------------------- */
#define configUSE_NEWLIB_REENTRANT 1
#define configINCLUDE_FREERTOS_TASK_C_ADDITIONS_H 1
/* ----------------------- Memory ------------------------- */
@ -75,6 +90,7 @@
#define INCLUDE_xTaskDelayUntil 1
#define INCLUDE_xTaskGetCurrentTaskHandle 1
#define INCLUDE_uxTaskGetStackHighWaterMark2 1
/* ------------------------------------------------ ESP-IDF Additions --------------------------------------------------
*

View File

@ -108,8 +108,12 @@
/* ----------------------- System -------------------------- */
#define configMAX_TASK_NAME_LEN CONFIG_FREERTOS_MAX_TASK_NAME_LEN
/* The distinciton between Amazon SMP FreeRTOS and IDF FreeRTOS is necessary because in IDF FreeRTOS,
* TLSP deletion callbacks can be added directly to the TCB, which is not allowed in Amazon SMP FreeRTOS.
* In the latter, the TLSP number is simply doubled to accomodate space for a deletion callback of each TLSP.
*/
#if CONFIG_FREERTOS_SMP
/* Number of TLSP is doubled to store TLSP deletion callbacks */
#define configNUM_THREAD_LOCAL_STORAGE_POINTERS ( CONFIG_FREERTOS_THREAD_LOCAL_STORAGE_POINTERS * 2 )
#else /* CONFIG_FREERTOS_SMP */
#define configNUM_THREAD_LOCAL_STORAGE_POINTERS CONFIG_FREERTOS_THREAD_LOCAL_STORAGE_POINTERS
@ -189,7 +193,6 @@
#define INCLUDE_xSemaphoreGetMutexHolder 1
#define INCLUDE_xTaskGetHandle 1
#define INCLUDE_uxTaskGetStackHighWaterMark 1
#define INCLUDE_uxTaskGetStackHighWaterMark2 1
#define INCLUDE_eTaskGetState 1
#define INCLUDE_xTaskResumeFromISR 1
#define INCLUDE_xTimerPendFunctionCall 1
@ -244,17 +247,8 @@
#else
#define configNUM_CORES 2
#endif /* CONFIG_FREERTOS_UNICORE */
#define configUSE_CORE_AFFINITY 1
#define configRUN_MULTIPLE_PRIORITIES 1
#define configUSE_TASK_PREEMPTION_DISABLE 1
/* This is always enabled to call IDF style idle hooks, by can be "--Wl,--wrap"
* if users enable CONFIG_FREERTOS_USE_MINIMAL_IDLE_HOOK. */
#define configUSE_MINIMAL_IDLE_HOOK 1
/* IDF Newlib supports dynamic reentrancy. We provide our own __getreent()
* function. */
#define configNEWLIB_REENTRANT_IS_DYNAMIC 1
#endif /* CONFIG_FREERTOS_SMP */
/* -------------------------------------------------- IDF FreeRTOS -----------------------------------------------------

View File

@ -38,7 +38,10 @@ This approach uses the `CMock <https://www.throwtheswitch.org/cmock>`_ framework
POSIX/Linux Simulator Approach
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
The `FreeRTOS POSIX/Linux simulator <https://www.freertos.org/FreeRTOS-simulator-for-Linux.html>`_ is available on ESP-IDF as a preview target already. It is the base for the Linux target which is already available as a preview. Using this simulator, IDF components can be implemented on the host to make them available to IDF applications when running on host. Currently, only a limited number of components are ready to be built on Linux. Furthermore the functionality of each component ported to Linux may also be limited or different compared to the functionality when building that component for a chip target. For more information if the desired components are supported on Linux, please refer to :ref:`component-linux-mock-support`.
The `FreeRTOS POSIX/Linux simulator <https://www.freertos.org/FreeRTOS-simulator-for-Linux.html>`_ is available on ESP-IDF as a preview target already. Using this simulator, IDF components can be implemented on the host to make them available to IDF applications when running on host. Currently, only a limited number of components are ready to be built on Linux. Furthermore the functionality of each component ported to Linux may also be limited or different compared to the functionality when building that component for a chip target. For more information if the desired components are supported on Linux, please refer to :ref:`component-linux-mock-support`.
.. note::
The FreeRTOS POSIX/Linux simulator allows configuring the :ref:`amazon_smp_freertos` version. However, the simulation still runs in single-core mode. The main reason allowing Amazon SMP FreeRTOS is to provide API compatibility with IDF applications written for Amazon SMP FreeRTOS.
Requirements
------------

View File

@ -19,6 +19,8 @@ ESP-IDF FreeRTOS is a FreeRTOS implementation based on Vanilla FreeRTOS v10.4.3,
.. note::
ESP-IDF FreeRTOS is currently the default FreeRTOS implementation for ESP-IDF.
.. _amazon_smp_freertos:
Amazon SMP FreeRTOS
^^^^^^^^^^^^^^^^^^^

View File

@ -19,6 +19,10 @@ tools/test_apps/linux_compatible/hello_world_linux_compatible:
enable:
- if: INCLUDE_DEFAULT == 1 or IDF_TARGET == "linux"
tools/test_apps/linux_compatible/linux_freertos:
enable:
- if: IDF_TARGET == "linux"
tools/test_apps/linux_compatible/rmt_mock_build_test:
enable:
- if: IDF_TARGET == "linux"

View File

@ -0,0 +1,9 @@
# For more information about build system see
# https://docs.espressif.com/projects/esp-idf/en/latest/api-guides/build-system.html
# The following five lines of boilerplate have to be in your project's
# CMakeLists in this exact order for cmake to work correctly
cmake_minimum_required(VERSION 3.16)
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
set(COMPONENTS main)
project(linux_freertos)

View File

@ -0,0 +1,26 @@
| Supported Targets | Linux |
| ----------------- | ----- |
# Simple test application for [SMP Linux](TODO) port
Note that the IDF version of the FreeRTOS POSIX/Linux simulator is not tested here, that one is merely the upstream version.
## Build
```
idf.py --preview set-target linux
```
Amazon FReeRTOS SMP configuration is already set via `sdkconfig.defaults`, no need to configure.
```
idf.py build
```
## Run
```
idf.py monitor
```
After the test output, input: `![ignore]` to not run the ignored test

View File

@ -0,0 +1,25 @@
# Register all of the "kernel" tests as a component
# For refactored FreeRTOS unit tests, we need to support #include "xxx.h" of FreeRTOS headers
idf_component_get_property(FREERTOS_ORIG_INCLUDE_PATH freertos ORIG_INCLUDE_PATH)
set(src_dirs
"." # For freertos_test_utils.c
"tasks"
"queue"
"port"
"stream_buffer"
"timers")
set(priv_include_dirs
"." # For portTestMacro.h
"${FREERTOS_ORIG_INCLUDE_PATH}") # FreeRTOS headers via`#include "xxx.h"`
idf_component_register(SRC_DIRS ${src_dirs}
PRIV_INCLUDE_DIRS ${priv_include_dirs}
PRIV_REQUIRES unity
WHOLE_ARCHIVE)
target_compile_options(${COMPONENT_LIB} PRIVATE
-Wno-pointer-to-int-cast
)

View File

@ -0,0 +1,21 @@
/*
* SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include "freertos/FreeRTOS.h"
#include "freertos/semphr.h"
#include "freertos_test_utils.h"
#include <time.h>
uint64_t ref_clock_get(void)
{
struct timespec current_time;
assert(clock_gettime(CLOCK_MONOTONIC, &current_time) == 0);
uint64_t ref_ticks = current_time.tv_sec * 1000000;
ref_ticks += (current_time.tv_nsec / 1000);
return ref_ticks;
}

View File

@ -0,0 +1,12 @@
/*
* SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once
#include <stdint.h>
#include "freertos/FreeRTOS.h"
uint64_t ref_clock_get(void);

View File

@ -0,0 +1,162 @@
/*
* SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include "sdkconfig.h"
#if CONFIG_FREERTOS_SMP
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "unity.h"
/*
Test TLSP Deletion Callbacks
Purpose:
- Test that TLSP Deletion Callbacks can be registered
- Test that TLSP Deletion Callbacks are called when a task is deleted
Procedure:
- Create a task on each core along with an array of integers to act as TLSP data
- Each task should initialize their integers to a particular value (i.e., the index value)
- Each task should register those integers as TLSPs along with a deletion callback
- Each task should self delete to trigger the TLSP deletion callback
- The TLSP deletion callback should indicate that it has run by negating the integer values
Expected:
- The TLSP deletion callback should check that the correct TLSP is provided by checking the TLSPs initialization
value (i.e., should be set to the index value)
- After deletion, the integer values should be negated to indicate deletion callback execution
*/
#define NUM_TLSP CONFIG_FREERTOS_THREAD_LOCAL_STORAGE_POINTERS
static void tlsp_del_cb(int index, void *tlsp)
{
int *val = (int *)tlsp;
// Check that the TLSP's initialization value is correct
TEST_ASSERT_EQUAL(index, *val);
// Set the TLSP's value again to a negative value to indicate that the del cb has ran
*val = -*val;
}
static void tlsp_task(void *arg)
{
int *tlsps = (int *)arg;
for (int index = 0; index < NUM_TLSP; index++) {
// Initialize the TLSPs to a positive value
tlsps[index] = index;
// Set TLSPs and deletion callbacks
vTaskSetThreadLocalStoragePointerAndDelCallback(NULL, index, &tlsps[index], tlsp_del_cb);
}
// Self delete to trigger TLSP del cb
vTaskDelete(NULL);
}
TEST_CASE("Test TLSP deletion callbacks", "[freertos]")
{
TaskHandle_t tasks[portNUM_PROCESSORS];
int tlsps[portNUM_PROCESSORS][NUM_TLSP];
for (int i = 0; i < portNUM_PROCESSORS; i++) {
TEST_ASSERT_EQUAL(pdPASS, xTaskCreatePinnedToCore(tlsp_task, "tlsp_tsk", configMINIMAL_STACK_SIZE * 2, (void *)&tlsps[i], CONFIG_UNITY_FREERTOS_PRIORITY - 1, &tasks[i], i));
}
// Significant delay to let tasks run and delete themselves
vTaskDelay(pdMS_TO_TICKS(100));
// Check the values of the TLSPs to see if the del cb have ran
for (int i = 0; i < portNUM_PROCESSORS; i++) {
for (int index = 0; index < NUM_TLSP; index++) {
// Del cb should have set the TLSP to a negative value
TEST_ASSERT_EQUAL(-index, tlsps[i][index]);
}
}
}
#else // CONFIG_FREERTOS_SMP
// Todo: Remove IDF FreeRTOS Test Case
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/semphr.h"
#include "unity.h"
/* --------Test backported thread local storage pointer and deletion cb feature----------
* vTaskSetThreadLocalStoragePointerAndDelCallback()
* pvTaskGetThreadLocalStoragePointer(),
*
* This test creates a task and set's the task's TLSPs. The task is then deleted
* which should trigger the deletion cb.
*/
#define NO_OF_TLSP configNUM_THREAD_LOCAL_STORAGE_POINTERS
#define TLSP_SET_BASE 0x0F //0b1111 to be bit shifted by index
#define TLSP_DEL_BASE 0x05 //0b0101 to be bit shifted by index
//The variables pointed to by Thread Local Storage Pointer
static uint32_t task_storage[portNUM_PROCESSORS][NO_OF_TLSP] = {0};
/* If static task cleanup is defined, can't set index 0 even if the calling task is not a pthread,
as the cleanup is called for every task.
*/
#if defined(CONFIG_FREERTOS_ENABLE_STATIC_TASK_CLEAN_UP)
static const int skip_index = 0; /*PTHREAD_TLS_INDEX*/
#else
static const int skip_index = -1;
#endif
static void del_cb(int index, void *ptr)
{
*((uint32_t *)ptr) = (TLSP_DEL_BASE << index); //Indicate deletion by setting task storage element to a unique value
}
static void task_cb(void *arg)
{
int core = xPortGetCoreID();
for(int i = 0; i < NO_OF_TLSP; i++){
if (i == skip_index) {
continue;
}
task_storage[core][i] = (TLSP_SET_BASE << i); //Give each element of task_storage a unique number
vTaskSetThreadLocalStoragePointerAndDelCallback(NULL, i, (void *)&task_storage[core][i], del_cb); //Set each TLSP to point to a task storage element
}
for(int i = 0; i < NO_OF_TLSP; i++){
if (i == skip_index) {
continue;
}
uint32_t * tlsp = (uint32_t *)pvTaskGetThreadLocalStoragePointer(NULL, i);
TEST_ASSERT_EQUAL(*tlsp, (TLSP_SET_BASE << i)); //Check if TLSP points to the correct task storage element by checking unique value
}
vTaskDelete(NULL); //Delete Task to Trigger TSLP deletion callback
}
TEST_CASE("Test FreeRTOS thread local storage pointers and del cb", "[freertos]")
{
//Create Task
for(int core = 0; core < portNUM_PROCESSORS; core++){
xTaskCreatePinnedToCore(task_cb, "task", 1024, NULL, CONFIG_UNITY_FREERTOS_PRIORITY+1, NULL, core);
}
vTaskDelay(10); //Delay long enough for tasks to run to completion
for(int core = 0; core < portNUM_PROCESSORS; core++){
for(int i = 0; i < NO_OF_TLSP; i++){
if (i == skip_index) {
continue;
}
TEST_ASSERT_EQUAL((TLSP_DEL_BASE << i), task_storage[core][i]); //Check del_cb ran by checking task storage for unique value
}
}
}
#endif // CONFIG_FREERTOS_SMP

View File

@ -0,0 +1,18 @@
/*
* SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once
#include "freertos_test_utils.h"
#define configTEST_DEFAULT_STACK_SIZE 4096
#define configTEST_UNITY_TASK_PRIORITY CONFIG_UNITY_FREERTOS_PRIORITY
#define portTEST_REF_CLOCK_TYPE uint64_t
#define portTEST_REF_CLOCK_INIT()
#define portTEST_REF_CLOCK_DEINIT()
#define portTEST_REF_CLOCK_GET_TIME() ((uint64_t) ref_clock_get())
#define portTEST_TICKS_TO_REF_CLOCK(ticks) ((ticks) * (1000000/configTICK_RATE_HZ))

View File

@ -0,0 +1,186 @@
/*
* SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <stdio.h>
#include <stdlib.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/queue.h"
#include "freertos/semphr.h"
#include "freertos/idf_additions.h"
#include "unity.h"
#define QUEUE_LEN 4
static void allocate_resources(int num_queues, int queue_len, QueueHandle_t *queue_list_ret, QueueSetHandle_t *queue_set_ret)
{
// Create queue set
*queue_set_ret = xQueueCreateSet(num_queues * queue_len);
TEST_ASSERT_NOT_EQUAL(NULL, *queue_set_ret);
// Create queues and add them to the queue set
for (int i = 0; i < num_queues; i++) {
queue_list_ret[i] = xQueueCreate(queue_len, sizeof(BaseType_t));
TEST_ASSERT_NOT_EQUAL(NULL, queue_list_ret[i]);
TEST_ASSERT_EQUAL(pdPASS, xQueueAddToSet(queue_list_ret[i], *queue_set_ret));
}
}
static void free_resources(int num_queues, QueueHandle_t *queue_list, QueueSetHandle_t queue_set)
{
// Remove queues form queue set and delete the queues
for (int i = 0; i < num_queues; i++) {
TEST_ASSERT_EQUAL(pdPASS, xQueueRemoveFromSet(queue_list[i], queue_set));
vQueueDelete(queue_list[i]);
}
vQueueDelete(queue_set);
}
/*
Test queue sets basic
Purpose:
- Test that queue set works as expected
Procedure:
- Create NUM_QUEUES queues and add them to the same queue set
- Fill each queue sequentially with QUEUE_LEN items
Expected:
- Each call to xQueueSend() should generate a member in the queue set
- The order of the members should match the order in which xQueueSend() was called
- The item sent by the xQueueSend() is correct when read
*/
#define NUM_QUEUES 5
TEST_CASE("Test Queue sets", "[freertos]")
{
// Create queues and queue set
QueueHandle_t queues[NUM_QUEUES];
QueueSetHandle_t queue_set;
allocate_resources(NUM_QUEUES, QUEUE_LEN, queues, &queue_set);
// Fill each queue sequentially with QUEUE_LEN items
for (int i = 0; i < NUM_QUEUES; i++) {
for (int j = 0; j < QUEUE_LEN; j++) {
BaseType_t item = j;
TEST_ASSERT_EQUAL(pdTRUE, xQueueSend(queues[i], &item, 0));
}
}
for (int i = 0; i < NUM_QUEUES; i++) {
for (int j = 0; j < QUEUE_LEN; j++) {
// Check the queue set member
QueueSetMemberHandle_t member = xQueueSelectFromSet(queue_set, 0);
TEST_ASSERT_EQUAL(queues[i], member);
// Check the queue's items
BaseType_t item;
TEST_ASSERT_EQUAL(pdTRUE, xQueueReceive(member, &item, 0));
TEST_ASSERT_EQUAL(j, item);
}
}
// Check that there are no more members
TEST_ASSERT_EQUAL(NULL, xQueueSelectFromSet(queue_set, 0));
// Cleanup queues and queue set
free_resources(NUM_QUEUES, queues, queue_set);
}
#ifndef CONFIG_FREERTOS_UNICORE
/*
Test queue set SMP thread safety
Purpose:
- Test that queue set works when being used from different cores simultaneously
Procedure:
- Create a queue for each core and add them to the same queue set
- Create a task on each core to send QUEUE_LEN items to their assigned queue
- Synchronize the tasks so that the start sending at the same time
Expected:
- Each call to xQueueSend() should generate a member in the queue set
- The item sent by the xQueueSend() is correct when read
*/
static volatile bool start_other_cores;
static SemaphoreHandle_t done_sem = NULL;
static void send_func(void *arg)
{
QueueHandle_t queue = (QueueHandle_t)arg;
BaseType_t core_id = xPortGetCoreID();
if (core_id == 0) {
// We are core 0. Trigger the other cores to start
start_other_cores = true;
} else {
// Wait to be started by main core
while (!start_other_cores) {
;
}
}
// Fill the queue assigned to the current core
for (int i = 0; i < QUEUE_LEN; i++) {
TEST_ASSERT_EQUAL(pdTRUE, xQueueSend(queue, &core_id, 0));
}
if (core_id != 0) {
// Indicate completion to core 0 and self delete
xSemaphoreGive(done_sem);
vTaskDelete(NULL);
}
}
TEST_CASE("Test queue sets multi-core", "[freertos]")
{
// Create done semaphore
done_sem = xSemaphoreCreateCounting(portNUM_PROCESSORS - 1, 0);
TEST_ASSERT_NOT_EQUAL(NULL, done_sem);
// Create queues and queue set
QueueHandle_t queues[portNUM_PROCESSORS];
QueueSetHandle_t queue_set;
allocate_resources(portNUM_PROCESSORS, QUEUE_LEN, queues, &queue_set);
// Create tasks of the same priority for all cores except for core 0
for (int i = 1; i < portNUM_PROCESSORS; i++) {
TEST_ASSERT_EQUAL(pdTRUE, xTaskCreatePinnedToCore(send_func, "send", 2048, (void *)queues[i], CONFIG_UNITY_FREERTOS_PRIORITY, NULL, i));
}
// Core 0 calls send_func as well triggering the simultaneous sends from all cores
send_func((void *)queues[0]);
// Wait for all other cores to be done
for (int i = 1; i < portNUM_PROCESSORS; i++) {
xSemaphoreTake(done_sem, portMAX_DELAY);
}
// Read queues from the queue set, then read an item from the queue
uint32_t queues_check_count[portNUM_PROCESSORS] = {0};
QueueSetMemberHandle_t member = xQueueSelectFromSet(queue_set, 0);
while (member != NULL) {
// Read the core ID from the queue, check that core ID is sane
BaseType_t core_id;
TEST_ASSERT_EQUAL(pdTRUE, xQueueReceive(member, &core_id, 0));
TEST_ASSERT_LESS_THAN(portNUM_PROCESSORS, core_id);
queues_check_count[core_id]++;
// Get next member
member = xQueueSelectFromSet(queue_set, 0);
}
// Check that all items from all queues have been read
for (int i = 0; i < portNUM_PROCESSORS; i++) {
TEST_ASSERT_EQUAL(QUEUE_LEN, queues_check_count[i]);
}
// Cleanup queues and queue set
free_resources(portNUM_PROCESSORS, queues, queue_set);
// Cleanup done sem
vSemaphoreDelete(done_sem);
done_sem = NULL;
}
#endif // CONFIG_FREERTOS_UNICORE

View File

@ -0,0 +1,73 @@
/*
* SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <stdint.h>
#include <stdio.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/semphr.h"
#include "freertos/stream_buffer.h"
#include "unity.h"
#define TEST_NUM_BYTES 100
#define TEST_RECEIVER_TIMEOUT_TICKS pdMS_TO_TICKS(1000) // 1ms timeout for receiver task
typedef struct {
StreamBufferHandle_t stream_buffer;
SemaphoreHandle_t done_sem;
} test_args_t;
static void sender_task(void *arg)
{
test_args_t *test_args = (test_args_t *)arg;
printf("Starting sender task... \n");
for (int i = 0; i < TEST_NUM_BYTES; i++) {
// Send a single byte, with the byte's value being the number of bytes sent thus far
uint8_t data = (uint8_t)i;
TEST_ASSERT_EQUAL(1, xStreamBufferSend(test_args->stream_buffer, &data, 1, 0));
// Short delay to give a chance for receiver task to receive
vTaskDelay(1);
}
xSemaphoreGive(test_args->done_sem);
vTaskDelete(NULL);
}
static void receiver_task(void *arg)
{
test_args_t *test_args = (test_args_t *)arg;
printf("Starting receiver task... \n");
for (int i = 0; i < TEST_NUM_BYTES; i++) {
// Receive a single byte. The received byte's value being the number of bytes sent/received thus far
uint8_t data;
TEST_ASSERT_EQUAL(1, xStreamBufferReceive(test_args->stream_buffer, &data, 1, TEST_RECEIVER_TIMEOUT_TICKS));
TEST_ASSERT_EQUAL(i, data);
}
xSemaphoreGive(test_args->done_sem);
vTaskDelete(NULL);
}
TEST_CASE("Stream Buffer: Send-receive tasks", "[freertos]")
{
test_args_t test_args;
test_args.stream_buffer = xStreamBufferCreate(TEST_NUM_BYTES, 1);
test_args.done_sem = xSemaphoreCreateCounting(2, 0);
TEST_ASSERT_NOT_NULL(test_args.stream_buffer);
TEST_ASSERT_NOT_NULL(test_args.done_sem);
TEST_ASSERT_EQUAL(pdTRUE, xTaskCreatePinnedToCore(sender_task, "sender", 4096, &test_args, CONFIG_UNITY_FREERTOS_PRIORITY + 2, NULL, 0));
TEST_ASSERT_EQUAL(pdTRUE, xTaskCreatePinnedToCore(receiver_task, "receiver", 4096, &test_args, CONFIG_UNITY_FREERTOS_PRIORITY + 1, NULL, 1));
// Wait for both tasks to complete
for (int i = 0; i < 2; i++) {
xSemaphoreTake(test_args.done_sem, portMAX_DELAY);
}
vStreamBufferDelete(test_args.stream_buffer);
vSemaphoreDelete(test_args.done_sem);
}

View File

@ -0,0 +1,91 @@
/*
* SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "unity.h"
/*
Test eTaskGetState()
Purpose:
- Test that eTaskGetState() returns the correct state for a particular task
Procedure:
- Create tasks in every state (and repeat for each core)
- Note: eDeleted is not tested due to needing to control when the idle tasks run
- Call eTaskGetState() on each created task
Expected:
- eTaskGetState() should return the correct state for each created task
*/
static void blocked_task(void *arg)
{
vTaskDelay(portMAX_DELAY-1);
// Shouldn't need to self delete, but added for extra safety
vTaskDelete(NULL);
}
static void suspended_task(void *arg)
{
vTaskSuspend(NULL);
// Shouldn't need to self delete, but added for extra safety
vTaskDelete(NULL);
}
static void loop_task(void *arg)
{
// Short delay to allow other created tasks to run
vTaskDelay(2);
while (1) {
;
}
}
TEST_CASE("Test eTaskGetState", "[freertos]")
{
TaskHandle_t blocked_tasks[portNUM_PROCESSORS];
TaskHandle_t suspended_tasks[portNUM_PROCESSORS];
TaskHandle_t ready_tasks[portNUM_PROCESSORS];
TaskHandle_t running_tasks[portNUM_PROCESSORS];
// Create tasks of each state on each core
for (int i = 0; i < portNUM_PROCESSORS; i++) {
TEST_ASSERT_EQUAL(pdPASS, xTaskCreatePinnedToCore(blocked_task, "blkd", configMINIMAL_STACK_SIZE * 2, NULL, CONFIG_UNITY_FREERTOS_PRIORITY - 1, &blocked_tasks[i], i));
TEST_ASSERT_EQUAL(pdPASS, xTaskCreatePinnedToCore(suspended_task, "susp", configMINIMAL_STACK_SIZE * 2, NULL, CONFIG_UNITY_FREERTOS_PRIORITY - 1, &suspended_tasks[i], i));
TEST_ASSERT_EQUAL(pdPASS, xTaskCreatePinnedToCore(loop_task, "rdy", configMINIMAL_STACK_SIZE * 2, NULL, CONFIG_UNITY_FREERTOS_PRIORITY - 1, &ready_tasks[i], i));
if (i == CONFIG_UNITY_FREERTOS_CPU) {
running_tasks[i] = xTaskGetCurrentTaskHandle();
} else {
xTaskCreatePinnedToCore(loop_task, "run", configMINIMAL_STACK_SIZE * 2, NULL, CONFIG_UNITY_FREERTOS_PRIORITY, &running_tasks[i], i);
}
}
// Short delay to allow created tasks to run
vTaskDelay(10);
// Check the state of the created tasks
for (int i = 0; i < portNUM_PROCESSORS; i++) {
TEST_ASSERT_EQUAL(eBlocked, eTaskGetState(blocked_tasks[i]));
TEST_ASSERT_EQUAL(eSuspended, eTaskGetState(suspended_tasks[i]));
TEST_ASSERT_EQUAL(eReady, eTaskGetState(ready_tasks[i]));
TEST_ASSERT_EQUAL(eRunning, eTaskGetState(running_tasks[i]));
}
// Clean up created tasks
for (int i = 0; i < portNUM_PROCESSORS; i++) {
vTaskDelete(blocked_tasks[i]);
vTaskDelete(suspended_tasks[i]);
vTaskDelete(ready_tasks[i]);
if (i != CONFIG_UNITY_FREERTOS_CPU) {
vTaskDelete(running_tasks[i]);
}
}
// Short delay to allow task memory to be cleaned
vTaskDelay(10);
}

View File

@ -0,0 +1,105 @@
/*
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
/*
* Test backported deletion behavior by creating tasks of various affinities and
* check if the task memory is freed immediately under the correct conditions.
*
* The behavior of vTaskDelete() results in the immediate freeing of task memory
* and the immediate execution of deletion callbacks for tasks which are not
* running, provided they are not pinned to the other core (due to FPU cleanup
* requirements).
*
* If the condition is not met, freeing of task memory and execution of
* deletion callbacks will still be carried out by the Idle Task.
*/
#include <stdio.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/semphr.h"
#include "esp_heap_caps.h"
#include "unity.h"
#include "esp_rom_sys.h"
#define NO_OF_TSKS 3
#define DELAY_TICKS 2
/* Caps of all memory which is allocated from when a task is created */
#define HEAP_CAPS (MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT)
#define DELAY_US_ITERATIONS 1000
typedef struct {
SemaphoreHandle_t sem;
volatile bool deleted; // Check the deleted task doesn't keep running after being deleted
} tsk_blocks_param_t;
/* Task blocks as often as possible
(two or more of these can share the same semaphore and "juggle" it around)
*/
static void tsk_blocks_frequently(void *param)
{
tsk_blocks_param_t *p = (tsk_blocks_param_t *)param;
SemaphoreHandle_t sem = p->sem;
srand(xTaskGetTickCount() ^ (int)xTaskGetCurrentTaskHandle());
while (1) {
assert(!p->deleted);
esp_rom_delay_us(rand() % 10);
assert(!p->deleted);
xSemaphoreTake(sem, portMAX_DELAY);
assert(!p->deleted);
esp_rom_delay_us(rand() % 10);
assert(!p->deleted);
xSemaphoreGive(sem);
}
}
TEST_CASE("FreeRTOS Delete Blocked Tasks", "[freertos][ignore]") // TODO: esp_rom_delay_us is interrupted by signal
{
TaskHandle_t blocking_tasks[portNUM_PROCESSORS + 1]; // one per CPU, plus one unpinned task
tsk_blocks_param_t params[portNUM_PROCESSORS + 1] = { 0 };
esp_rom_delay_us(100);
unsigned before = heap_caps_get_free_size(MALLOC_CAP_8BIT);
printf("Free memory at start %u\n", before);
/* Any bugs will depend on relative timing of destroying the tasks, so create & delete many times.
Stop early if it looks like some resources have not been properly cleaned up.
(1000 iterations takes about 9 seconds on ESP32 dual core)
*/
for(unsigned iter = 0; iter < 1000; iter++) {
// Create everything
SemaphoreHandle_t sem = xSemaphoreCreateMutex();
for(unsigned i = 0; i < portNUM_PROCESSORS + 1; i++) {
params[i].deleted = false;
params[i].sem = sem;
TEST_ASSERT_EQUAL(pdTRUE,
xTaskCreatePinnedToCore(tsk_blocks_frequently, "tsk_block", 4096, &params[i],
CONFIG_UNITY_FREERTOS_PRIORITY - 1, &blocking_tasks[i],
i < portNUM_PROCESSORS ? i : tskNO_AFFINITY));
}
vTaskDelay(5); // Let the tasks juggle the mutex for a bit
for(unsigned i = 0; i < portNUM_PROCESSORS + 1; i++) {
vTaskDelete(blocking_tasks[i]);
params[i].deleted = true;
}
vTaskDelay(4); // Yield to the idle task for cleanup
vSemaphoreDelete(sem);
// Check we haven't leaked resources yet
TEST_ASSERT_GREATER_OR_EQUAL(before - 256, heap_caps_get_free_size(MALLOC_CAP_8BIT));
}
}

View File

@ -0,0 +1,81 @@
/*
* SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
/*
Unit tests for FreeRTOS preemption
*/
#include <esp_types.h>
#include <stdio.h>
#include <inttypes.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/semphr.h"
#include "freertos/queue.h"
#include "freertos/idf_additions.h"
#include "unity.h"
#include "sdkconfig.h"
static volatile bool trigger;
static volatile bool flag;
#ifndef CONFIG_FREERTOS_SMP
#define MAX_YIELD_COUNT 10000
#else
//TODO: IDF-5081
#define MAX_YIELD_COUNT 17000
#endif // CONFIG_FREERTOS_SMP
/* Task:
- Waits for 'trigger' variable to be set
- Reads the cycle count on this CPU
- Pushes it into a queue supplied as a param
- Busy-waits until the main task terminates it
*/
static void task_send_to_queue(void *param)
{
QueueHandle_t queue = (QueueHandle_t) param;
uint32_t ccount;
while(!trigger) {}
ccount = 0;
flag = true;
xQueueSendToBack(queue, &ccount, 0);
/* This is to ensure that higher priority task
won't wake anyhow, due to this task terminating.
The task runs until terminated by the main task.
*/
while(1) {}
}
TEST_CASE("Yield from lower priority task, same CPU", "[freertos]")
{
/* Do this 3 times, mostly for the benchmark value - the first
run includes a cache miss so uses more cycles than it should. */
for (int i = 0; i < 3; i++) {
TaskHandle_t sender_task;
QueueHandle_t queue = xQueueCreate(1, sizeof(uint32_t));
flag = false;
trigger = false;
/* "yield" task sits on our CPU, lower priority to us */
xTaskCreatePinnedToCore(task_send_to_queue, "YIELD", 2048, (void *)queue, CONFIG_UNITY_FREERTOS_PRIORITY - 1, &sender_task, CONFIG_UNITY_FREERTOS_CPU);
vTaskDelay(1); /* make sure everything is set up */
trigger = true;
uint32_t yield_ccount;
TEST_ASSERT( xQueueReceive(queue, &yield_ccount, 100 / portTICK_PERIOD_MS) );
TEST_ASSERT( flag );
vTaskDelete(sender_task);
vQueueDelete(queue);
}
}

View File

@ -0,0 +1,82 @@
/*
* SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "unity.h"
#include "portTestMacro.h"
/* ------------------------------------------------------------------------------------------------------------------ */
/*
Test Priority Scheduling (Single Core)
Purpose:
- Test that the single-core scheduler always schedules the highest priority ready task
Procedure:
- Raise the unityTask priority to (configMAX_PRIORITIES - 1)
- unityTask creates the following lower priority tasks
- task_A (configMAX_PRIORITIES - 2)
- task_B (configMAX_PRIORITIES - 3)
- UnityTask blocks for a short period of time to allow task_A to run
- Clean up and restore unityTask's original priority
Expected:
- task_A should run after unityTask blocks
- task_B should never have run
*/
#if ( configNUM_CORES == 1 )
#define UNITY_TASK_DELAY_TICKS 10
static BaseType_t task_A_ran;
static BaseType_t task_B_ran;
static void task_A(void *arg)
{
task_A_ran = pdTRUE;
/* Keeping spinning to prevent the lower priority task_B from running */
while (1) {
;
}
}
static void task_B(void *arg)
{
/* The following should never run due to task_B having a lower priority */
task_B_ran = pdTRUE;
while (1) {
;
}
}
TEST_CASE("Tasks: Test priority scheduling", "[freertos]")
{
TaskHandle_t task_A_handle;
TaskHandle_t task_B_handle;
task_A_ran = pdFALSE;
task_B_ran = pdFALSE;
/* Raise the priority of the unityTask */
vTaskPrioritySet(NULL, configMAX_PRIORITIES - 1);
/* Create task_A and task_B */
xTaskCreate(task_A, "task_A", configTEST_DEFAULT_STACK_SIZE, (void *)xTaskGetCurrentTaskHandle(), configMAX_PRIORITIES - 2, &task_A_handle);
xTaskCreate(task_B, "task_B", configTEST_DEFAULT_STACK_SIZE, (void *)xTaskGetCurrentTaskHandle(), configMAX_PRIORITIES - 3, &task_B_handle);
/* Block to allow task_A to be scheduled */
vTaskDelay(UNITY_TASK_DELAY_TICKS);
/* Test that only task_A has run */
TEST_ASSERT_EQUAL(pdTRUE, task_A_ran);
TEST_ASSERT_EQUAL(pdFALSE, task_B_ran);
vTaskDelete(task_A_handle);
vTaskDelete(task_B_handle);
/* Restore the priority of the unityTask */
vTaskPrioritySet(NULL, configTEST_UNITY_TASK_PRIORITY);
}
#endif /* configNUM_CORES == 1 */

View File

@ -0,0 +1,180 @@
/*
* SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/semphr.h"
#include "unity.h"
#include "freertos_test_utils.h"
#include "portTestMacro.h"
/* ------------------------------------------------------------------------------------------------------------------ */
/*
Test vTaskDelay
Purpose:
- Test that vTaskDelay is accurate
Procedure:
- The test contains TEST_VTASKDELAY_ITERATIONS number of iterations. For each iteration...
- vTaskDelay(1) to align to next tick boundary
- Store current tick count and current time (using ref clock)
- vTaskDelay for TEST_VTASKDELAY_TICKS
- Get post delay tick count and ref clock time
- For single core, run the test directly from the UnityTask
- For SMP, run the test once on each core (using vTestOnAllCores())
Expected:
- The elapsed ticks should be TEST_VTASKDELAY_TICKS, with TEST_VTASKDELAY_DELTA_TICKS error allowed (in case the
delay and ref clock functions last long enough to cross a tick boundary).
- The elapsed time should be equivalent to TEST_VTASKDELAY_TICKS tick periods, with TEST_VTASKDELAY_TICKS tick
period of error allowed (in case ref clock functions last longer that a tick period).
*/
#if ( INCLUDE_vTaskDelay == 1 )
#define TEST_VTASKDELAY_TICKS 5 // Number of ticks to delay in test
#define TEST_VTASKDELAY_ITERATIONS 5 // Number of iterations in test
#if CONFIG_FREERTOS_SMP
#define TEST_VTASKDELAY_DELTA_TICKS 1 // Number of ticks worth of delta allowed
#else
#define TEST_VTASKDELAY_DELTA_TICKS 2 // Number of ticks worth of delta allowed. We allow 2 ticks in IDF FreeRTOS as each core's tick interrupt could be out of phase
#endif
static void test_vTaskDelay(void *arg)
{
for (int i = 0; i < TEST_VTASKDELAY_ITERATIONS; i++) {
TickType_t tick_start, tick_end;
portTEST_REF_CLOCK_TYPE ref_clock_start, ref_clock_end;
/* Delay until the next tick boundary */
vTaskDelay(1);
/* Get the current tick count and ref clock time */
tick_start = xTaskGetTickCount();
ref_clock_start = portTEST_REF_CLOCK_GET_TIME();
vTaskDelay(TEST_VTASKDELAY_TICKS);
/* Get the post delay tick count and ref clock time */
tick_end = xTaskGetTickCount();
ref_clock_end = portTEST_REF_CLOCK_GET_TIME();
/* Check that elapsed ticks and ref clock is accurate. We allow TEST_VTASKDELAY_DELTA_TICKS of error in case
* vTaskDelay() or portTEST_REF_CLOCK_GET_TIME() last long enough to cross a tick boundary */
#if ( configUSE_16_BIT_TICKS == 1 )
TEST_ASSERT_UINT16_WITHIN(TEST_VTASKDELAY_DELTA_TICKS, TEST_VTASKDELAY_TICKS, tick_end - tick_start);
#else
TEST_ASSERT_UINT32_WITHIN(TEST_VTASKDELAY_DELTA_TICKS, TEST_VTASKDELAY_TICKS, tick_end - tick_start);
#endif
TEST_ASSERT_UINT32_WITHIN(portTEST_TICKS_TO_REF_CLOCK(TEST_VTASKDELAY_DELTA_TICKS),
portTEST_TICKS_TO_REF_CLOCK(TEST_VTASKDELAY_TICKS),
ref_clock_end - ref_clock_start);
}
}
TEST_CASE("Tasks: Test vTaskDelay", "[freertos]")
{
portTEST_REF_CLOCK_INIT();
#if ( configNUM_CORES > 1 )
vTestOnAllCores(test_vTaskDelay, NULL, configTEST_DEFAULT_STACK_SIZE, configTEST_UNITY_TASK_PRIORITY - 1);
#else
/* Test vTaskDelay directly on the current core */
test_vTaskDelay(NULL);
#endif
portTEST_REF_CLOCK_DEINIT();
}
#endif /* ( INCLUDE_vTaskDelay == 1 ) */
/* ------------------------------------------------------------------------------------------------------------------ */
/*
Test vTaskDelayUntil
Purpose:
- Test that vTaskDelayUntil is accurate
Procedure:
- The test contains TEST_VTASKDELAYUNTIL_ITERATIONS number of iterations. For each iteration...
- vTaskDelay(1) to align to next tick boundary
- Store current tick count and current time (using ref clock)
- Call vTaskDelayUntil() for TEST_VTASKDELAYUNTIL_TICKS, using the stored tick count as the previous wake time
- Get post delay tick count and ref clock time
- For single core, run the test directly from the UnityTask
- For SMP, run the test once on each core (using vTestOnAllCores())
Expected:
- The elapsed ticks should be TEST_VTASKDELAYUNTIL_TICKS, with TEST_VTASKDELAYUNTIL_DELTA_TICKS tick of error
allowed (in case the delay and ref clock functions last long enough to cross a tick boundary).
- The elapsed time should be equivalent to TEST_VTASKDELAYUNTIL_TICKS tick periods, with
TEST_VTASKDELAYUNTIL_DELTA_TICKS tick period of error allowed (in case ref clock functions last longer that a tick
period).
*/
#if ( INCLUDE_xTaskDelayUntil == 1 )
#define TEST_VTASKDELAYUNTIL_TICKS 5 // Number of ticks to delay in test
#define TEST_VTASKDELAYUNTIL_ITERATIONS 5 // Number of iterations in test
#if CONFIG_FREERTOS_SMP
#define TEST_VTASKDELAYUNTIL_DELTA_TICKS 1 // Number of ticks worth of delta allowed
#else
#define TEST_VTASKDELAYUNTIL_DELTA_TICKS 2 // Number of ticks worth of delta allowed. We allow 2 ticks in IDF FreeRTOS as each core's tick interrupt could be out of phase
#endif
static void test_vTaskDelayUntil(void *arg)
{
for (int i = 0; i < TEST_VTASKDELAYUNTIL_ITERATIONS; i++) {
TickType_t tick_start, tick_end, last_wake_tick;
portTEST_REF_CLOCK_TYPE ref_clock_start, ref_clock_end;
/* Delay until the next tick boundary */
vTaskDelay(1);
/* Get the current tick count and ref clock time */
tick_start = xTaskGetTickCount();
last_wake_tick = tick_start;
ref_clock_start = portTEST_REF_CLOCK_GET_TIME();
vTaskDelayUntil(&last_wake_tick, TEST_VTASKDELAYUNTIL_TICKS);
/* Get the post delay tick count and ref clock time */
tick_end = xTaskGetTickCount();
ref_clock_end = portTEST_REF_CLOCK_GET_TIME();
/* Check that elapsed ticks and ref clock is accurate. We allow TEST_VTASKDELAYUNTIL_DELTA_TICKS of error in
* case vTaskDelayUntil() or portTEST_REF_CLOCK_GET_TIME() last long enough to cross a tick boundary */
#if ( configUSE_16_BIT_TICKS == 1 )
TEST_ASSERT_UINT16_WITHIN(TEST_VTASKDELAYUNTIL_DELTA_TICKS, TEST_VTASKDELAYUNTIL_TICKS, tick_end - tick_start);
TEST_ASSERT_UINT16_WITHIN(TEST_VTASKDELAYUNTIL_DELTA_TICKS, tick_end, last_wake_tick);
#else
TEST_ASSERT_UINT32_WITHIN(TEST_VTASKDELAYUNTIL_DELTA_TICKS, TEST_VTASKDELAYUNTIL_TICKS, tick_end - tick_start);
TEST_ASSERT_UINT32_WITHIN(TEST_VTASKDELAYUNTIL_DELTA_TICKS, tick_end, last_wake_tick);
#endif
/* Check that the elapsed ref clock time is accurate. We allow TEST_VTASKDELAYUNTIL_DELTA_TICKS time worth of
* error to account for the execution time of the ref clock functions. */
TEST_ASSERT_UINT32_WITHIN(portTEST_TICKS_TO_REF_CLOCK(TEST_VTASKDELAYUNTIL_DELTA_TICKS),
portTEST_TICKS_TO_REF_CLOCK(TEST_VTASKDELAYUNTIL_TICKS),
ref_clock_end - ref_clock_start);
}
}
TEST_CASE("Tasks: Test vTaskDelayUntil", "[freertos]")
{
portTEST_REF_CLOCK_INIT();
#if ( configNUM_CORES > 1 )
vTestOnAllCores(test_vTaskDelayUntil, NULL, configTEST_DEFAULT_STACK_SIZE, configTEST_UNITY_TASK_PRIORITY - 1);
#else
/* Test vTaskDelay directly on the current core */
test_vTaskDelayUntil(NULL);
#endif
portTEST_REF_CLOCK_DEINIT();
}
#endif /* ( INCLUDE_xTaskDelayUntil == 1 ) */

View File

@ -0,0 +1,87 @@
/*
* SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
/*
Unit tests for FreeRTOS task priority get/set
*/
#include <esp_types.h>
#include <stdio.h>
#include <strings.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "unity.h"
static void counter_task(void *param)
{
volatile uint32_t *counter = (volatile uint32_t *)param;
while (1) {
(*counter)++;
}
}
TEST_CASE("Get/Set Priorities", "[freertos]")
{
/* Two tasks per processor */
TaskHandle_t tasks[portNUM_PROCESSORS][2] = { 0 };
unsigned volatile counters[portNUM_PROCESSORS][2] = { 0 };
TEST_ASSERT_EQUAL(CONFIG_UNITY_FREERTOS_PRIORITY, uxTaskPriorityGet(NULL));
/* create a matrix of counter tasks on each core */
for (int cpu = 0; cpu < portNUM_PROCESSORS; cpu++) {
for (int task = 0; task < 2; task++) {
xTaskCreatePinnedToCore(counter_task, "count", 2048, (void *)&(counters[cpu][task]), CONFIG_UNITY_FREERTOS_PRIORITY - task, &(tasks[cpu][task]), cpu);
}
}
/* check they were created with the expected priorities */
for (int cpu = 0; cpu < portNUM_PROCESSORS; cpu++) {
for (int task = 0; task < 2; task++) {
TEST_ASSERT_EQUAL(CONFIG_UNITY_FREERTOS_PRIORITY - task, uxTaskPriorityGet(tasks[cpu][task]));
}
}
vTaskDelay(10);
/* at this point, only the higher priority tasks (first index) should be counting */
for (int cpu = 0; cpu < portNUM_PROCESSORS; cpu++) {
TEST_ASSERT_NOT_EQUAL(0, counters[cpu][0]);
TEST_ASSERT_EQUAL(0, counters[cpu][1]);
}
/* swap priorities! */
for (int cpu = 0; cpu < portNUM_PROCESSORS; cpu++) {
vTaskPrioritySet(tasks[cpu][0], CONFIG_UNITY_FREERTOS_PRIORITY - 1);
vTaskPrioritySet(tasks[cpu][1], CONFIG_UNITY_FREERTOS_PRIORITY);
}
/* check priorities have swapped... */
for (int cpu = 0; cpu < portNUM_PROCESSORS; cpu++) {
TEST_ASSERT_EQUAL(CONFIG_UNITY_FREERTOS_PRIORITY -1, uxTaskPriorityGet(tasks[cpu][0]));
TEST_ASSERT_EQUAL(CONFIG_UNITY_FREERTOS_PRIORITY, uxTaskPriorityGet(tasks[cpu][1]));
}
/* check the tasks which are counting have also swapped now... */
for (int cpu = 0; cpu < portNUM_PROCESSORS; cpu++) {
unsigned old_counters[2];
old_counters[0] = counters[cpu][0];
old_counters[1] = counters[cpu][1];
vTaskDelay(10);
TEST_ASSERT_EQUAL(old_counters[0], counters[cpu][0]);
TEST_ASSERT_NOT_EQUAL(old_counters[1], counters[cpu][1]);
}
/* clean up */
for (int cpu = 0; cpu < portNUM_PROCESSORS; cpu++) {
for (int task = 0; task < 2; task++) {
vTaskDelete(tasks[cpu][task]);
}
}
}

View File

@ -0,0 +1,657 @@
/*
* SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
/*
* Unit tests for FreeRTOS task yielding
*/
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/semphr.h"
#include "unity.h"
#include <string.h>
// Array to store the task ids of the test threads being yielded
static volatile uint32_t task_yield_sequence[3];
// Index variable to access the yield sequence array
static volatile uint32_t idx = 0;
// Lock to protect the shared variables to store task id
static portMUX_TYPE idx_lock;
// Synchronization variable to have a deterministic dispatch sequence of the test threads
static volatile bool task_sequence_ready;
// Synchronization variable between the test threads and the unity task
static volatile uint32_t count;
// Lock variable to create a blocked task scenario
static volatile SemaphoreHandle_t task_mutex;
// This helper macro is used to store the task id atomically
#define STORE_TASK_ID(task_id) ({ \
portENTER_CRITICAL(&idx_lock); \
task_yield_sequence[idx++] = task_id; \
portEXIT_CRITICAL(&idx_lock); \
})
/*
* Test yielding for same priority tasks on the same core.
*
* The test performs the following actions:
* - Creates 2 tasks with the same priority on the same core.
* - Each task pushes its task_id on to a queue and then yields.
* - Unity task checks the sequence of the tasks run once the yield_tasks are done.
*/
static void yield_task1(void *arg)
{
uint32_t task_id = (uint32_t)arg;
/* Store task_id in the sequence array */
STORE_TASK_ID(task_id);
/* Notify the yield_task2 to run */
task_sequence_ready = true;
/* Yield */
taskYIELD();
/* Increment task count to notify unity task */
count++;
/* Delete self */
vTaskDelete(NULL);
}
static void yield_task2(void *arg)
{
uint32_t task_id = (uint32_t)arg;
/* Wait for the other task to run for the test to begin */
while (!task_sequence_ready) {
taskYIELD();
};
/* Store task_id in the sequence array */
STORE_TASK_ID(task_id);
/* Yield */
taskYIELD();
/* Increment task count to notify unity task */
count++;
/* Delete self */
vTaskDelete(NULL);
}
TEST_CASE("Task yield must run the next ready task of the same priority", "[freertos]")
{
/* Reset yield sequence index */
idx = 0;
/* Reset yield sequence array */
memset((void *)task_yield_sequence, 0, sizeof(task_yield_sequence));
/* Initialize idx lock */
portMUX_INITIALIZE(&idx_lock);
/* Reset task count */
count = 0;
/* Reset task sequence flag */
task_sequence_ready = false;
/* Create test tasks */
xTaskCreatePinnedToCore(yield_task1, "yield_task1", 2048, (void *)1, CONFIG_UNITY_FREERTOS_PRIORITY - 1, NULL, CONFIG_UNITY_FREERTOS_CPU);
xTaskCreatePinnedToCore(yield_task2, "yield_task2", 2048, (void *)2, CONFIG_UNITY_FREERTOS_PRIORITY - 1, NULL, CONFIG_UNITY_FREERTOS_CPU);
/* Wait for the tasks to finish up */
while (count != 2) {
vTaskDelay(10);
}
idx = 0;
/* Verify that the yield is successful and the next ready task is run */
TEST_ASSERT_EQUAL(1, task_yield_sequence[idx++]);
TEST_ASSERT_EQUAL(2, task_yield_sequence[idx++]);
}
/*
* Test yielding behavior when a task is blocked
*
* The test performs the following actions:
* - Creates 2 tasks with the same priority on the same core.
* - One task blocks on a mutex.
* - Second task does not contest for a mutex and yields.
* - Unity task verifies that the blocked task is not scheduled unless it is ready to run.
*/
static void test_task1(void *arg)
{
uint32_t task_id = (uint32_t)arg;
/* Block on mutex taken by the unity task */
TEST_ASSERT_EQUAL(pdTRUE, xSemaphoreTake(task_mutex, portMAX_DELAY));
/* Store task_id in the sequence array */
STORE_TASK_ID(task_id);
/* Increment task count to notify unity task */
count++;
/* Release mutex */
TEST_ASSERT_EQUAL(pdTRUE, xSemaphoreGive(task_mutex));
/* Delete self */
vTaskDelete(NULL);
}
static void test_task2(void *arg)
{
uint32_t task_id = (uint32_t)arg;
/* Store task_id in the sequence array */
STORE_TASK_ID(task_id);
/* Yield */
taskYIELD();
/* Store task_id in the sequence array */
STORE_TASK_ID(task_id);
/* Increment task count to notify unity task */
count++;
/* Delete self */
vTaskDelete(NULL);
}
TEST_CASE("Task yield must not run a blocked task", "[freertos]")
{
/* Reset yield sequence index */
idx = 0;
/* Reset yield sequence array */
memset((void *)task_yield_sequence, 0, sizeof(task_yield_sequence));
/* Initialize idx lock */
portMUX_INITIALIZE(&idx_lock);
/* Reset task count */
count = 0;
/* Create mutex and acquire it */
task_mutex = xSemaphoreCreateMutex();
TEST_ASSERT_NOT_EQUAL(NULL, task_mutex);
TEST_ASSERT_EQUAL(pdTRUE, xSemaphoreTake(task_mutex, portMAX_DELAY));
/* Create test_task1. This gets blocked. */
xTaskCreatePinnedToCore(test_task1, "test_task1", 2048, (void *)1, CONFIG_UNITY_FREERTOS_PRIORITY - 1, NULL, CONFIG_UNITY_FREERTOS_CPU);
/* Wait for test_task1 to start up and get blocked */
vTaskDelay(10);
/* Create test_task2. This issues the yield. */
xTaskCreatePinnedToCore(test_task2, "test_task2", 2048, (void *)2, CONFIG_UNITY_FREERTOS_PRIORITY - 1, NULL, CONFIG_UNITY_FREERTOS_CPU);
/* Wait for test_task2 to finish up */
while (count != 1) {
vTaskDelay(10);
}
/* Release mutex. This should unblock test_task1. */
TEST_ASSERT_EQUAL(pdTRUE, xSemaphoreGive(task_mutex));
/* Wait for test_task1 to finish up */
vTaskDelay(10);
idx = 0;
/* Verify that the yield results in the same task running again and not the blocked task */
TEST_ASSERT_EQUAL(2, task_yield_sequence[idx++]);
/* Verify that the task yield did not result in a context switch */
TEST_ASSERT_EQUAL(2, task_yield_sequence[idx++]);
/* Verify that the other task is scheduled once it is unblocked */
TEST_ASSERT_EQUAL(1, task_yield_sequence[idx++]);
/* Cleanup task mutex */
vSemaphoreDelete(task_mutex);
}
/*
* Test yielding behavior when the scheduler is suspended
*
* The test performs the following actions:
* - Creates 2 tasks with the same priority on the same core.
* - One task suspends the scheduler and then yields.
* - Unity task verifies that the yield does not happen until the scheduler is resumed.
*/
static void test_critical_task1(void *arg)
{
uint32_t task_id = (uint32_t)arg;
/* Store task_id in the sequence array */
STORE_TASK_ID(task_id);
/* Suspend scheduler */
vTaskSuspendAll();
/* Set the task sequence flag once test_critical_task1 runs */
task_sequence_ready = true;
/* Yield */
taskYIELD();
/* Store task_id in the sequence array.
* No need for a lock when the scheduler is suspended.
*/
task_yield_sequence[idx++] = task_id;
/* Increment task count to notify unity task */
count++;
/* Resume scheduler */
xTaskResumeAll();
/* Delete self */
vTaskDelete(NULL);
}
static void test_critical_task2(void *arg)
{
uint32_t task_id = (uint32_t)arg;
/* Wait for the other task to run for the test to begin */
while (!task_sequence_ready) {
taskYIELD();
};
/* Store task_id in the sequence array */
STORE_TASK_ID(task_id);
/* Increment task count to notify unity task */
count++;
/* Delete self */
vTaskDelete(NULL);
}
TEST_CASE("Task yield must not happen when scheduler is suspended", "[freertos]")
{
/* Reset yield sequence index */
idx = 0;
/* Reset yield sequence array */
memset((void *)task_yield_sequence, 0, sizeof(task_yield_sequence));
/* Initialize idx lock */
portMUX_INITIALIZE(&idx_lock);
/* Reset task count */
count = 0;
/* Reset task sequence flag */
task_sequence_ready = false;
/* Create test tasks */
xTaskCreatePinnedToCore(test_critical_task1, "test_critical_task1", 2048, (void *)1, CONFIG_UNITY_FREERTOS_PRIORITY - 1, NULL, CONFIG_UNITY_FREERTOS_CPU);
xTaskCreatePinnedToCore(test_critical_task2, "test_critical_task2", 2048, (void *)2, CONFIG_UNITY_FREERTOS_PRIORITY - 1, NULL, CONFIG_UNITY_FREERTOS_CPU);
/* Wait for both the tasks to finish up */
while (count != 2) {
vTaskDelay(10);
}
idx = 0;
/* Verify that test_critical_task1 runs first */
TEST_ASSERT_EQUAL(1, task_yield_sequence[idx++]);
/* Verify that the task yield, when the scheduler is suspended, did not result in a context switch */
TEST_ASSERT_EQUAL(1, task_yield_sequence[idx++]);
/* Verify that test_critical_task2 is scheduled once the scheduler is resumed */
TEST_ASSERT_EQUAL(2, task_yield_sequence[idx++]);
}
/*
* Test yielding behavior when a lower priority task creates a higher priority task
*
* The test performs the following actions:
* - Creates a task with a priority higher than the unity task.
* - Unity task verifies that it yields immediately to the newly created task.
*/
static void high_prio_task(void *arg)
{
uint32_t task_id = (uint32_t)arg;
/* Store task_id in the sequence array */
STORE_TASK_ID(task_id);
/* Increment task count to notify unity task */
count++;
/* Delete self */
vTaskDelete(NULL);
}
TEST_CASE("Task yield must happen when a task creates a higher priority task", "[freertos]")
{
/* Reset yield sequence index */
idx = 0;
/* Reset yield sequence array */
memset((void *)task_yield_sequence, 0, sizeof(task_yield_sequence));
/* Initialize idx lock */
portMUX_INITIALIZE(&idx_lock);
/* Reset task count */
count = 0;
/* Create test task */
xTaskCreatePinnedToCore(high_prio_task, "high_prio_task", 2048, (void *)1, CONFIG_UNITY_FREERTOS_PRIORITY + 1, NULL, CONFIG_UNITY_FREERTOS_CPU);
uint32_t unity_task_id = 2;
/* Store task_id in the sequence array */
STORE_TASK_ID(unity_task_id);
/* Wait for the test task to finish up */
while (count == 0) {
vTaskDelay(10);
}
idx = 0;
/* Verify that the unity task yields as soon as a higher prio task is created */
TEST_ASSERT_EQUAL(1, task_yield_sequence[idx++]);
/* Verify that the unity task_id is stored after the higher priority task runs */
TEST_ASSERT_EQUAL(2, task_yield_sequence[idx++]);
}
/*
* Test yielding behavior when a lower priority task raises the priority of another task
*
* The test performs the following actions:
* - Creates a task with a priority lower than the unity task.
* - Unity task raises the priority of the newly created task.
* - Unity task verifies that it yields once the priority is raised.
*/
static void low_prio_task(void *arg)
{
uint32_t task_id = (uint32_t)arg;
/* Store task_id in the sequence array */
STORE_TASK_ID(task_id);
/* Increment task count to notify unity task */
count++;
/* Delete self */
vTaskDelete(NULL);
}
TEST_CASE("Task yield must happed when a task raises the priority of another priority task", "[freertos]")
{
/* Reset yield sequence index */
idx = 0;
/* Reset yield sequence array */
memset((void *)task_yield_sequence, 0, sizeof(task_yield_sequence));
/* Initialize idx lock */
portMUX_INITIALIZE(&idx_lock);
/* Reset task count */
count = 0;
/* Create test task */
TaskHandle_t task_handle;
xTaskCreatePinnedToCore(low_prio_task, "low_prio_task", 2048, (void *)1, CONFIG_UNITY_FREERTOS_PRIORITY - 1, &task_handle, CONFIG_UNITY_FREERTOS_CPU);
uint32_t unity_task_id = 2;
/* Store task_id in the sequence array */
STORE_TASK_ID(unity_task_id);
/* Raise the priority of the lower priority task */
vTaskPrioritySet(task_handle, CONFIG_UNITY_FREERTOS_PRIORITY + 1);
/* Store unity task_id in the sequence array again */
STORE_TASK_ID(unity_task_id);
/* Wait for the test task to finish up */
while (count == 0) {
vTaskDelay(10);
}
idx = 0;
/* Verify that the unity task does not yield to a lower priority task when it is created */
TEST_ASSERT_EQUAL(2, task_yield_sequence[idx++]);
/* Verify that the unity task_id yielded once the priority of the lower priority task is raised */
TEST_ASSERT_EQUAL(1, task_yield_sequence[idx++]);
/* Verify that the unity task_id is stored again once the test task finishes up */
TEST_ASSERT_EQUAL(2, task_yield_sequence[idx++]);
}
#if (portNUM_PROCESSORS > 1) && !(CONFIG_FREERTOS_UNICORE)
/*
* Test yielding behavior when a task on one core forces an yield on the other core
*
* The test performs the following actions:
* - Creates 2 tasks with the same priority on the core on which unity task is not running.
* - One task spins and does not let the other task run.
* - Force a cross-core yield from the unity task.
* - Verify that the cross-core yield happens and the second task is scheduled to run.
*/
static void other_core_task1(void *arg)
{
uint32_t task_id = (uint32_t)arg;
/* Store task_id in the sequence array */
STORE_TASK_ID(task_id);
while (1) {
vTaskDelay(10);
}
}
static void other_core_task2(void *arg)
{
uint32_t task_id = (uint32_t)arg;
/* Wait for the other task to run for the test to begin */
while (!task_sequence_ready) {
taskYIELD();
};
/* Store task_id in the sequence array */
STORE_TASK_ID(task_id);
/* Increment task count to notify unity task */
count++;
while (1) {
vTaskDelay(10);
}
}
TEST_CASE("Task yield must happen when issued from another core", "[freertos]")
{
TaskHandle_t other_core_taskhandle1;
TaskHandle_t other_core_taskhandle2;
/* Reset yield sequence index */
idx = 0;
/* Reset yield sequence array */
memset((void *)task_yield_sequence, 0, sizeof(task_yield_sequence));
/* Initialize idx lock */
portMUX_INITIALIZE(&idx_lock);
/* Reset task count */
count = 0;
/* Reset task sequence flag */
task_sequence_ready = false;
/* Create test tasks */
xTaskCreatePinnedToCore(other_core_task1, "test_task1", 2048, (void *)1, CONFIG_UNITY_FREERTOS_PRIORITY - 1, &other_core_taskhandle1, !CONFIG_UNITY_FREERTOS_CPU);
xTaskCreatePinnedToCore(other_core_task2, "test_task2", 2048, (void *)2, CONFIG_UNITY_FREERTOS_PRIORITY - 1, &other_core_taskhandle2, !CONFIG_UNITY_FREERTOS_CPU);
/* Wait for everything to be setup */
vTaskDelay(10);
uint32_t idx1 = 0;
/* Verify that other_core_task1 runs first */
TEST_ASSERT_EQUAL(1, task_yield_sequence[idx1++]);
/* Set the task sequence flag once other_core_task1 runs */
task_sequence_ready = true;
/* Force an yield on the other core */
#if CONFIG_FREERTOS_SMP
portYIELD_CORE(!CONFIG_UNITY_FREERTOS_CPU);
#else
vPortYieldOtherCore(!CONFIG_UNITY_FREERTOS_CPU);
#endif
/* Wait for the test task to finish up */
while (count == 0) {
vTaskDelay(10);
}
/* Verify that other_core_task1 yields and other_core_task2 runs */
TEST_ASSERT_EQUAL(2, task_yield_sequence[idx1++]);
/* Cleanup test tasks */
vTaskDelete(other_core_taskhandle1);
vTaskDelete(other_core_taskhandle2);
}
#if !CONFIG_FREERTOS_SMP
static volatile bool yield_triggered = false;
/*
* Test cross-core yielding behavior when the scheduler is suspended
*
* The test performs the following actions:
* - Creates 2 tasks with the same priority on the other core.
* - One task suspends the scheduler.
* - Unity task forces a cross-core yield.
* - Unity task verifies that the yield does not happen until the scheduler is resumed.
*
* Note: This test case is not valid when FreeRTOS SMP is used as the scheduler suspension
* is not per core but across cores and hence the test cannot be executed.
*/
static void other_core_critical_task1(void *arg)
{
uint32_t task_id = (uint32_t)arg;
/* Store task_id in the sequence array */
STORE_TASK_ID(task_id);
/* Suspend scheduler*/
vTaskSuspendAll();
/* Store task_id in the sequence array again.
* No need for a lock when the scheduler is supended.
*/
task_yield_sequence[idx++] = task_id;
/* Set the task sequence flag once other_core_critical_task1 runs */
task_sequence_ready = true;
/* Increment task count to notify unity task */
count++;
while (!yield_triggered) { }
/* Resume scheduler */
xTaskResumeAll();
/* Delete self */
vTaskDelete(NULL);
}
static void other_core_critical_task2(void *arg)
{
uint32_t task_id = (uint32_t)arg;
/* Wait for the other task to run for the test to begin */
while (!task_sequence_ready) {
taskYIELD();
};
/* Store task_id in the sequence array */
STORE_TASK_ID(task_id);
/* Increment task count to notify unity task */
count++;
/* Delete self */
vTaskDelete(NULL);
}
TEST_CASE("Task yield on other core must not happen when scheduler is suspended", "[freertos]")
{
/* Reset yield sequence index */
idx = 0;
/* Reset yield sequence array */
memset((void *)task_yield_sequence, 0, sizeof(task_yield_sequence));
/* Initialize idx lock */
portMUX_INITIALIZE(&idx_lock);
/* Reset task count */
count = 0;
/* Reset task sequence flag */
task_sequence_ready = false;
/* Create test tasks */
xTaskCreatePinnedToCore(other_core_critical_task1, "other_core_critical_task1", 2048, (void *)1, CONFIG_UNITY_FREERTOS_PRIORITY - 1, NULL, !CONFIG_UNITY_FREERTOS_CPU);
xTaskCreatePinnedToCore(other_core_critical_task2, "other_core_critical_task2", 2048, (void *)2, CONFIG_UNITY_FREERTOS_PRIORITY - 1, NULL, !CONFIG_UNITY_FREERTOS_CPU);
/* Wait for at least one of the tasks to finish up */
while (count == 0) {
vTaskDelay(10);
}
/* Force an yield on the other core */
vPortYieldOtherCore(!CONFIG_UNITY_FREERTOS_CPU);
/* Set yield triggered flag */
yield_triggered = true;
uint32_t idx1 = 0;
/* Verify that the first task runs */
TEST_ASSERT_EQUAL(1, task_yield_sequence[idx1++]);
/* Verify that the task yield when the scheduler is suspended did not result in a context switch */
TEST_ASSERT_EQUAL(1, task_yield_sequence[idx1++]);
/* Wait for the second task to finish up */
while (count != 2) {
vTaskDelay(10);
}
/* Verify that the second task is scheduled once the critical section is over */
TEST_ASSERT_EQUAL(2, task_yield_sequence[idx1++]);
}
#endif // !CONFIG_FREERTOS_SMP
#endif // (portNUM_PROCESSORS > 1) && !(CONFIG_FREERTOS_UNICORE)

View File

@ -0,0 +1,91 @@
/*
* SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
/* FreeRTOS timer tests
*/
#include <stdio.h>
#include "unity.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/timers.h"
static void timer_callback(TimerHandle_t timer)
{
volatile int *count;
count = (volatile int *)pvTimerGetTimerID( timer );
(*count)++;
printf("Callback timer %p count %p = %d\n", timer, count, *count);
}
TEST_CASE("Oneshot FreeRTOS timers", "[freertos]")
{
volatile int count = 0;
TimerHandle_t oneshot = xTimerCreate("oneshot", 100 / portTICK_PERIOD_MS, pdFALSE,
(void *)&count, timer_callback);
TEST_ASSERT(oneshot);
TEST_ASSERT_EQUAL(pdFALSE, xTimerIsTimerActive(oneshot));
TEST_ASSERT_EQUAL(0, count);
TEST_ASSERT( xTimerStart(oneshot, 1) );
vTaskDelay(2); /* give the timer task a chance to process the message */
TEST_ASSERT_EQUAL(pdTRUE, xTimerIsTimerActive(oneshot));
TEST_ASSERT_EQUAL(0, count);
vTaskDelay(250 / portTICK_PERIOD_MS); // 2.5 timer periods
TEST_ASSERT_EQUAL(1, count);
TEST_ASSERT_EQUAL(pdFALSE, xTimerIsTimerActive(oneshot));
TEST_ASSERT( xTimerDelete(oneshot, 1) );
}
TEST_CASE("Recurring FreeRTOS timers", "[freertos]")
{
volatile int count = 0;
TimerHandle_t recurring = xTimerCreate("oneshot", 100 / portTICK_PERIOD_MS, pdTRUE,
(void *)&count, timer_callback);
TEST_ASSERT(recurring);
TEST_ASSERT_EQUAL(pdFALSE, xTimerIsTimerActive(recurring));
TEST_ASSERT_EQUAL(0, count);
TEST_ASSERT( xTimerStart(recurring, 1) );
vTaskDelay(2); // let timer task process the queue
TEST_ASSERT_EQUAL(pdTRUE, xTimerIsTimerActive(recurring));
TEST_ASSERT_EQUAL(0, count);
vTaskDelay(250 / portTICK_PERIOD_MS); // 2.5 timer periods
TEST_ASSERT_EQUAL(2, count);
TEST_ASSERT_EQUAL(pdTRUE, xTimerIsTimerActive(recurring));
TEST_ASSERT( xTimerStop(recurring, 1) );
TEST_ASSERT_EQUAL(2, count);
vTaskDelay(100 / portTICK_PERIOD_MS); // One more timer period
TEST_ASSERT_EQUAL(2, count); // hasn't gone up
TEST_ASSERT_EQUAL(pdFALSE, xTimerIsTimerActive(recurring));
TEST_ASSERT( xTimerDelete(recurring, 1) );
}
TEST_CASE("Static timer creation", "[freertos]")
{
StaticTimer_t static_timer;
TimerHandle_t created_timer;
volatile int count = 0;
created_timer = xTimerCreateStatic("oneshot", 100 / portTICK_PERIOD_MS,
pdTRUE,
(void *)&count,
timer_callback,
&static_timer);
TEST_ASSERT_NOT_NULL(created_timer);
}

View File

@ -0,0 +1,3 @@
idf_component_register(SRCS "linux_freertos.c"
INCLUDE_DIRS "."
PRIV_REQUIRES "unity" "kernel_tests")

View File

@ -0,0 +1,36 @@
menu "IDF unit test"
config UNITY_FREERTOS_PRIORITY
int "Priority of Unity test task"
default 5
config UNITY_FREERTOS_CPU
int "CPU to run Unity test task on"
default 0
config UNITY_FREERTOS_STACK_SIZE
int "Stack size of Unity test task, in bytes"
default 8192
config UNITY_WARN_LEAK_LEVEL_GENERAL
int "Leak warning level"
default 255
config UNITY_CRITICAL_LEAK_LEVEL_GENERAL
int "Critical leak"
default 1024
config UNITY_CRITICAL_LEAK_LEVEL_LWIP
int "Critical leak for UT which use LWIP component"
default 4095
config UNITY_IGNORE_PERFORMANCE_TESTS
bool "Ignore performance test results"
default y if IDF_ENV_FPGA
default n
help
If set, performance tests that use TEST_PERFORMANCE_LESS_THAN and
TEST_PERFORMANCE_GREATER_THAN macros will log the performance value
but not fail the test if the threshold is not met.
endmenu

View File

@ -0,0 +1,26 @@
/*
* SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "unity.h"
#include "unity_test_runner.h"
void app_main(void)
{
/*
Some FreeRTOS tests are reliant on the main task being at priority UNITY_FREERTOS_PRIORITY to test scheduling
behavior. Thus, we raise the main task's priority before any tasks are run. See IDF-6088
*/
vTaskPrioritySet(NULL, CONFIG_UNITY_FREERTOS_PRIORITY);
printf(" ______ _____ _______ ____ _____\n");
printf("| ____| | __ \\__ __/ __ \\ / ____|\n");
printf("| |__ _ __ ___ ___| |__) | | | | | | | (___\n");
printf("| __| '__/ _ \\/ _ \\ _ / | | | | | |\\___ \\\n");
printf("| | | | | __/ __/ | \\ \\ | | | |__| |____) |\n");
printf("|_| |_| \\___|\\___|_| \\_\\ |_| \\____/|_____/\n");
unity_run_menu();
}

View File

@ -0,0 +1,13 @@
# SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
# SPDX-License-Identifier: Apache-2.0
import pytest
from pytest_embedded import Dut
@pytest.mark.linux
@pytest.mark.host_test
def test_linux_freertos_SMP(dut: Dut) -> None:
dut.expect_exact('Press ENTER to see the list of tests.')
dut.write('![ignore]')
dut.expect('[0-9][0-9] Tests 0 Failures 0 Ignored')

View File

@ -0,0 +1,2 @@
CONFIG_IDF_TARGET="linux"
CONFIG_FREERTOS_SMP=y