feat(freertos): added POSIX/Linux simulator port to FreeRTOS SMP

* Added a POSIX/Linux simulator that is compatible with the Amazon
  FreeRTOS SMP API. Note that the simulator itself is still
  single core, like the IDF FreeRTOS POSIX/Linux simulator.
This commit is contained in:
Jakob Hasse 2023-04-21 11:08:27 +08:00
parent 03c5274368
commit 550ecbe37a
16 changed files with 1437 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
^^^^^^^^^^^^^^^^^^^