From bfbbd9d7901eca8bbed824919482605f2ee03219 Mon Sep 17 00:00:00 2001 From: Jakob Hasse Date: Tue, 6 Sep 2022 16:09:23 +0200 Subject: [PATCH] feat(freertos): Added FreeRTOS POSIX/Linux Simulator * Added port layer from the FreeRTOS POSIX port, added additional port code for ESP-IDF. * Created another hello world example using that POSIX port in tools/test_apps. * Removed old linux app --- .gitlab/ci/host-test.yml | 17 +- components/freertos/CMakeLists.txt | 37 ++ .../linux/include/freertos/portmacro.h | 145 ++++- .../linux/include/freertos/portmacro_idf.h | 98 +++ .../portable/linux/include/spinlock.h | 59 ++ .../FreeRTOS-Kernel/portable/linux/port.c | 564 ++++++++++++++++++ .../FreeRTOS-Kernel/portable/linux/port_idf.c | 164 +++++ .../portable/linux/utils/wait_for_event.c | 109 ++++ .../portable/linux/utils/wait_for_event.h | 51 ++ components/freertos/Kconfig | 3 +- .../include/freertos/FreeRTOSConfig.h | 30 +- docs/en/api-guides/linux-host-testing.rst | 7 +- examples/build_system/.build-test-rules.yml | 5 - .../cmake/linux_host_app/CMakeLists.txt | 10 - .../cmake/linux_host_app/README.md | 38 -- .../cmake/linux_host_app/main/CMakeLists.txt | 1 - .../linux_host_app/main/linux_host_app.cpp | 30 - .../cmake/linux_host_app/sdkconfig.defaults | 3 - tools/ci/check_copyright_ignore.txt | 1 - tools/mocks/freertos/CMakeLists.txt | 13 +- tools/mocks/freertos/Kconfig | 29 +- tools/mocks/freertos/include/FreeRTOSConfig.h | 25 - tools/test_apps/.build-test-rules.yml | 8 + .../CMakeLists.txt | 7 + .../hello_world_linux_compatible/README.md | 45 ++ .../main/CMakeLists.txt | 4 + .../main/hello_world_main.c | 24 + .../pytest_hello_world_linux_compatible.py | 15 + 28 files changed, 1377 insertions(+), 165 deletions(-) create mode 100644 components/freertos/FreeRTOS-Kernel/portable/linux/include/freertos/portmacro_idf.h create mode 100644 components/freertos/FreeRTOS-Kernel/portable/linux/include/spinlock.h create mode 100644 components/freertos/FreeRTOS-Kernel/portable/linux/port.c create mode 100644 components/freertos/FreeRTOS-Kernel/portable/linux/port_idf.c create mode 100644 components/freertos/FreeRTOS-Kernel/portable/linux/utils/wait_for_event.c create mode 100644 components/freertos/FreeRTOS-Kernel/portable/linux/utils/wait_for_event.h delete mode 100644 examples/build_system/cmake/linux_host_app/CMakeLists.txt delete mode 100644 examples/build_system/cmake/linux_host_app/README.md delete mode 100644 examples/build_system/cmake/linux_host_app/main/CMakeLists.txt delete mode 100644 examples/build_system/cmake/linux_host_app/main/linux_host_app.cpp delete mode 100644 examples/build_system/cmake/linux_host_app/sdkconfig.defaults delete mode 100644 tools/mocks/freertos/include/FreeRTOSConfig.h create mode 100644 tools/test_apps/linux_compatible/hello_world_linux_compatible/CMakeLists.txt create mode 100644 tools/test_apps/linux_compatible/hello_world_linux_compatible/README.md create mode 100644 tools/test_apps/linux_compatible/hello_world_linux_compatible/main/CMakeLists.txt create mode 100644 tools/test_apps/linux_compatible/hello_world_linux_compatible/main/hello_world_main.c create mode 100644 tools/test_apps/linux_compatible/hello_world_linux_compatible/pytest_hello_world_linux_compatible.py diff --git a/.gitlab/ci/host-test.yml b/.gitlab/ci/host-test.yml index 10096fce8f..f335a389c9 100644 --- a/.gitlab/ci/host-test.yml +++ b/.gitlab/ci/host-test.yml @@ -380,6 +380,15 @@ test_esp_event: - idf.py build - build/test_esp_event_host.elf +test_hello_world_linux_compatible_example: + extends: .host_test_template + script: + - cd ${IDF_PATH}/tools/test_apps/linux_compatible/hello_world_linux_compatible + - idf.py --preview set-target linux + - idf.py build + - timeout 15 build/hello_world.elf > test.log + - grep "Hello world!" test.log + test_esp_timer_cxx: extends: .host_test_template script: @@ -429,14 +438,6 @@ test_system_cxx: - idf.py build - build/test_system_cxx_host.elf -test_linux_example: - extends: .host_test_template - script: - - cd ${IDF_PATH}/examples/build_system/cmake/linux_host_app - - idf.py build - - timeout 5 ./build/linux_host_app.elf >test.log || true - - grep "Restarting" test.log - test_partition_api_host: extends: .host_test_template script: diff --git a/components/freertos/CMakeLists.txt b/components/freertos/CMakeLists.txt index 78a8e4ae4e..8eda6517a7 100644 --- a/components/freertos/CMakeLists.txt +++ b/components/freertos/CMakeLists.txt @@ -6,6 +6,43 @@ endif() idf_build_get_property(target IDF_TARGET) +if(${target} STREQUAL "linux") + set(kernel_dir "FreeRTOS-Kernel") + set(srcs + "${kernel_dir}/portable/linux/port.c" + "${kernel_dir}/portable/linux/port_idf.c" + "${kernel_dir}/portable/linux/utils/wait_for_event.c" + "${kernel_dir}/list.c" + "${kernel_dir}/queue.c" + "${kernel_dir}/tasks.c" + "${kernel_dir}/timers.c" + ) + + set(include_dirs + ${kernel_dir}/include + ${kernel_dir}/portable/linux/include # For arch-specific FreeRTOSConfig_arch.h in portable//include + ${kernel_dir}/portable/linux/include/freertos + "esp_additions/include/freertos" # For config via #include "FreeRTOSConfig.h" + "esp_additions/include" # For #include "freertos/task_snapshot.h" and #include "freertos/FreeRTOSConfig.h" + ) + + set(private_include_dirs + ${kernel_dir}/portable/linux + ${kernel_dir}/portable/priv_include + ${kernel_dir}/include/freertos + . + ) + + idf_component_register(SRCS "${srcs}" + INCLUDE_DIRS ${include_dirs} + PRIV_INCLUDE_DIRS ${private_include_dirs}) + + target_compile_definitions(${COMPONENT_LIB} PUBLIC "projCOVERAGE_TEST=0") + target_link_libraries(${COMPONENT_LIB} PUBLIC pthread) + + return() +endif() + if(CONFIG_FREERTOS_SMP) set(ldfragments linker_smp.lf) if(CONFIG_IDF_TARGET_ARCH_XTENSA) diff --git a/components/freertos/FreeRTOS-Kernel/portable/linux/include/freertos/portmacro.h b/components/freertos/FreeRTOS-Kernel/portable/linux/include/freertos/portmacro.h index bbd9d43c17..085967281b 100644 --- a/components/freertos/FreeRTOS-Kernel/portable/linux/include/freertos/portmacro.h +++ b/components/freertos/FreeRTOS-Kernel/portable/linux/include/freertos/portmacro.h @@ -1,42 +1,141 @@ /* - * SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD + * FreeRTOS Kernel V10.4.6 + * Copyright 2020 Cambridge Consultants Ltd. * - * SPDX-License-Identifier: Apache-2.0 + * SPDX-FileCopyrightText: 2020 Cambridge Consultants Ltd. + * + * 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 */ -#pragma once -#include "esp_attr.h" -#include +#ifndef PORTMACRO_H +#define PORTMACRO_H + +#include #ifdef __cplusplus extern "C" { #endif -#define portBYTE_ALIGNMENT 16 -#define portTICK_TYPE_IS_ATOMIC 1 +/*----------------------------------------------------------- + * Port specific definitions. + * + * The settings in this file configure FreeRTOS correctly for the + * given hardware and compiler. + * + * These settings should not be altered. + *----------------------------------------------------------- + */ /* Type definitions. */ -#define portCHAR uint8_t -#define portFLOAT float -#define portDOUBLE double -#define portLONG int32_t -#define portSHORT int16_t -#define portSTACK_TYPE uint8_t -#define portBASE_TYPE int -// interrupt module will mask interrupt with priority less than threshold -#define RVHAL_EXCM_LEVEL 4 +#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 portBASE_TYPE UBaseType_t; -typedef uint32_t TickType_t; -#define portMAX_DELAY ( TickType_t ) 0xffffffffUL +typedef portSTACK_TYPE StackType_t; +typedef long BaseType_t; +typedef unsigned long UBaseType_t; -typedef int portMUX_TYPE; +typedef unsigned long TickType_t; +#define portMAX_DELAY ( TickType_t ) ULONG_MAX -#define portTICK_PERIOD_MS ( ( TickType_t ) 1 ) +#define portTICK_TYPE_IS_ATOMIC 1 + +/*-----------------------------------------------------------*/ + +/* Architecture specifics. */ +#define portSTACK_GROWTH ( -1 ) +#define portHAS_STACK_OVERFLOW_CHECKING ( 1 ) +#define portTICK_PERIOD_MS ( ( TickType_t ) 1000 / configTICK_RATE_HZ ) +#define portTICK_RATE_MICROSECONDS ( ( TickType_t ) 1000000 / configTICK_RATE_HZ ) +#define portBYTE_ALIGNMENT 8 +/*-----------------------------------------------------------*/ + +/* Scheduler utilities. */ +extern void vPortYield( void ); + +#define portYIELD() vPortYield() + +#define portEND_SWITCHING_ISR( xSwitchRequired ) if( xSwitchRequired != pdFALSE ) vPortYield() +#define portYIELD_FROM_ISR( x ) portEND_SWITCHING_ISR( x ) +/*-----------------------------------------------------------*/ + +/* Critical section management. */ +extern void vPortDisableInterrupts( void ); +extern void vPortEnableInterrupts( void ); +#define portSET_INTERRUPT_MASK() ( vPortDisableInterrupts() ) +#define portCLEAR_INTERRUPT_MASK() ( vPortEnableInterrupts() ) + +extern portBASE_TYPE xPortSetInterruptMask( void ); +extern void vPortClearInterruptMask( portBASE_TYPE xMask ); + +extern void vPortEnterCritical( void ); +extern void vPortExitCritical( void ); +#define portSET_INTERRUPT_MASK_FROM_ISR() xPortSetInterruptMask() +#define portCLEAR_INTERRUPT_MASK_FROM_ISR(x) vPortClearInterruptMask(x) +#define portDISABLE_INTERRUPTS() portSET_INTERRUPT_MASK() +#define portENABLE_INTERRUPTS() portCLEAR_INTERRUPT_MASK() +#define portENTER_CRITICAL(mux) {(void)mux; vPortEnterCritical();} +#define portEXIT_CRITICAL(mux) {(void)mux; vPortExitCritical();} +#define portENTER_CRITICAL_ISR(mux) portENTER_CRITICAL(mux) +#define portEXIT_CRITICAL_ISR(mux) portEXIT_CRITICAL(mux) + +/*-----------------------------------------------------------*/ + +extern void vPortThreadDying( void *pxTaskToDelete, volatile BaseType_t *pxPendYield ); +extern void vPortCancelThread( void *pxTaskToDelete ); +#define portPRE_TASK_DELETE_HOOK( pvTaskToDelete, pxPendYield ) vPortThreadDying( ( pvTaskToDelete ), ( pxPendYield ) ) +#define portCLEAN_UP_TCB( pxTCB ) vPortCancelThread( pxTCB ) +/*-----------------------------------------------------------*/ + +#define portTASK_FUNCTION_PROTO( vFunction, pvParameters ) void vFunction( void *pvParameters ) +#define portTASK_FUNCTION( vFunction, pvParameters ) void vFunction( void *pvParameters ) +/*-----------------------------------------------------------*/ + +/* + * 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" ) + +extern unsigned long ulPortGetRunTime( void ); +#define portCONFIGURE_TIMER_FOR_RUN_TIME_STATS() /* no-op */ +#define portGET_RUN_TIME_COUNTER_VALUE() ulPortGetRunTime() #ifdef __cplusplus } #endif + +// We need additional definitions for ESP-IDF code +#include "portmacro_idf.h" + +#endif /* PORTMACRO_H */ diff --git a/components/freertos/FreeRTOS-Kernel/portable/linux/include/freertos/portmacro_idf.h b/components/freertos/FreeRTOS-Kernel/portable/linux/include/freertos/portmacro_idf.h new file mode 100644 index 0000000000..c00c8c6d2e --- /dev/null +++ b/components/freertos/FreeRTOS-Kernel/portable/linux/include/freertos/portmacro_idf.h @@ -0,0 +1,98 @@ +/* + * SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/* + * This is the "IDF-part" of the POSIX portmacro. + * We need additional definitions for code in ESP-IDF which is kept here to separate the original + * FreeRTOS POSIX port code from the additional IDF POSIX port code. + */ + +#pragma once + +#include +#include "sdkconfig.h" +#include "esp_attr.h" +#include "spinlock.h" + +#ifdef __cplusplus +extern "C" { +#endif + +// TODO: IDF-5983 From esp_task.h, should later be used from there +// or be refactored in IDF (e.g. move esp_task.h to freertos) +// See also configMINIMAL_STACK_SIZE for more information. +#define CONFIG_ESP_MAIN_TASK_STACK_SIZE ( ( unsigned short ) (0x4000 + 40) / sizeof(portSTACK_TYPE) ) // should be in Kconfig again +#define CONFIG_ESP_MAIN_TASK_AFFINITY 0 + +#define ESP_TASK_PRIO_MAX (configMAX_PRIORITIES) +#define ESP_TASK_PRIO_MIN (0) +#define ESP_TASK_MAIN_PRIO (ESP_TASK_PRIO_MIN + 1) +#define ESP_TASK_MAIN_STACK (CONFIG_ESP_MAIN_TASK_STACK_SIZE) +#define ESP_TASK_MAIN_CORE CONFIG_ESP_MAIN_TASK_AFFINITY + +// interrupt module will mask interrupt with priority less than threshold +#define RVHAL_EXCM_LEVEL 4 + +typedef spinlock_t portMUX_TYPE; + +/**< Spinlock initializer */ +#define portMUX_INITIALIZER_UNLOCKED { \ + .owner = portMUX_FREE_VAL, \ + .count = 0, \ + } +#define portMUX_FREE_VAL SPINLOCK_FREE /**< Spinlock is free. [refactor-todo] check if this is still required */ + +void vPortYieldFromISR(void); +void vPortYieldOtherCore(BaseType_t coreid); + +#define portMUX_INITIALIZE(mux) spinlock_initialize(mux) /*< Initialize a spinlock to its unlocked state */ + +/** + * @brief Get the current core's ID + * + * @note dummy function for freertos simulator, always returns 0. + @ return BaseType_t 0 + */ +static inline BaseType_t IRAM_ATTR xPortGetCoreID(void) +{ + return (BaseType_t) 0; +} + +static inline bool portVALID_TCB_MEM(const void *ptr) +{ + return true; +} + +static inline bool portVALID_STACK_MEM(const void *ptr) +{ + return true; +} + +#define pvPortMallocTcbMem(size) pvPortMalloc(size) +#define pvPortMallocStackMem(size) pvPortMalloc(size) + +BaseType_t xPortCheckIfInISR(void); + +/** + * @brief Checks if the current core is in an ISR context + * + * - ISR context consist of Low/Mid priority ISR, or time tick ISR + * - High priority ISRs aren't detected here, but they normally cannot call C code, so that should not be an issue anyway. + * + * @note [refactor-todo] Check if this should be inlined + * @return + * - pdTRUE if in ISR + * - pdFALSE otherwise + */ +static inline BaseType_t xPortInIsrContext(void) +{ + //Just call the FreeRTOS port interface version + return xPortCheckIfInISR(); +} + +#ifdef __cplusplus +} +#endif diff --git a/components/freertos/FreeRTOS-Kernel/portable/linux/include/spinlock.h b/components/freertos/FreeRTOS-Kernel/portable/linux/include/spinlock.h new file mode 100644 index 0000000000..54d2072af0 --- /dev/null +++ b/components/freertos/FreeRTOS-Kernel/portable/linux/include/spinlock.h @@ -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 +#include + +#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 Not a true spinlock as single core RISC-V does not have atomic compare and set instruction + * @note Keep portMUX_INITIALIZER_UNLOCKED in sync with this struct + */ +typedef struct { + uint32_t owner; + uint32_t count; +}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 diff --git a/components/freertos/FreeRTOS-Kernel/portable/linux/port.c b/components/freertos/FreeRTOS-Kernel/portable/linux/port.c new file mode 100644 index 0000000000..6727ce3f1e --- /dev/null +++ b/components/freertos/FreeRTOS-Kernel/portable/linux/port.c @@ -0,0 +1,564 @@ +/* + * 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 +#include +#include +#include +#include +#include +#include +#include +#include + +/* Scheduler includes. */ +#include "FreeRTOS.h" +#include "task.h" +#include "timers.h" +#include "utils/wait_for_event.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; +static volatile portBASE_TYPE uxCriticalNesting; +/*-----------------------------------------------------------*/ + +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(); + + vPortEnterCritical(); + + iRet = pthread_create( &thread->pthread, &xThreadAttributes, + prvWaitForStart, thread ); + if ( iRet ) + { + prvFatalError( "pthread_create", iRet ); + } + + vPortExitCritical(); + + 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); +} +/*-----------------------------------------------------------*/ + +void vPortEnterCritical( void ) +{ + if ( uxCriticalNesting == 0 ) + { + vPortDisableInterrupts(); + } + uxCriticalNesting++; +} +/*-----------------------------------------------------------*/ + +void vPortExitCritical( void ) +{ + uxCriticalNesting--; + + /* If we have reached 0 then re-enable the interrupts. */ + if( uxCriticalNesting == 0 ) + { + vPortEnableInterrupts(); + } +} +/*-----------------------------------------------------------*/ + +void vPortYieldFromISR( void ) +{ +Thread_t *xThreadToSuspend; +Thread_t *xThreadToResume; + + xThreadToSuspend = prvGetThreadFromTask( xTaskGetCurrentTaskHandle() ); + + vTaskSwitchContext(); + + xThreadToResume = prvGetThreadFromTask( xTaskGetCurrentTaskHandle() ); + + prvSwitchThread( xThreadToResume, xThreadToSuspend ); +} +/*-----------------------------------------------------------*/ + +void vPortYield( void ) +{ + vPortEnterCritical(); + + vPortYieldFromISR(); + + vPortExitCritical(); +} +/*-----------------------------------------------------------*/ + +void vPortDisableInterrupts( void ) +{ + pthread_sigmask( SIG_BLOCK, &xAllSignals, NULL ); +} +/*-----------------------------------------------------------*/ + +void vPortEnableInterrupts( void ) +{ + pthread_sigmask( SIG_UNBLOCK, &xAllSignals, NULL ); +} +/*-----------------------------------------------------------*/ + +portBASE_TYPE xPortSetInterruptMask( void ) +{ + /* Interrupts are always disabled inside ISRs (signals + handlers). */ + return pdTRUE; +} +/*-----------------------------------------------------------*/ + +void vPortClearInterruptMask( portBASE_TYPE xMask ) +{ +} +/*-----------------------------------------------------------*/ + +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; +/* uint64_t xExpectedTicks; */ + + uxCriticalNesting++; /* Signals are blocked in this signal handler. */ + +#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 { */ + xTaskIncrementTick(); +/* prvTickCount++; + * } while (prvTickCount < xExpectedTicks); +*/ + +#if ( configUSE_PREEMPTION == 1 ) + /* Select Next Task. */ + vTaskSwitchContext(); + + pxThreadToResume = prvGetThreadFromTask( xTaskGetCurrentTaskHandle() ); + + prvSwitchThread(pxThreadToResume, pxThreadToSuspend); +#endif + + uxCriticalNesting--; +} +/*-----------------------------------------------------------*/ + +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, unblocks all signals. */ + uxCriticalNesting = 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 uxSavedCriticalNesting; + + if ( pxThreadToSuspend != pxThreadToResume ) + { + /* + * Switch tasks. + * + * The critical section nesting is per-task, so save it on the + * stack of the current (suspending thread), restoring it when + * we switch back to this task. + */ + uxSavedCriticalNesting = uxCriticalNesting; + + prvResumeThread( pxThreadToResume ); + if ( pxThreadToSuspend->xDying ) + { + pthread_exit( NULL ); + } + prvSuspendSelf( pxThreadToSuspend ); + + uxCriticalNesting = uxSavedCriticalNesting; + } +} +/*-----------------------------------------------------------*/ + +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; +} +/*-----------------------------------------------------------*/ diff --git a/components/freertos/FreeRTOS-Kernel/portable/linux/port_idf.c b/components/freertos/FreeRTOS-Kernel/portable/linux/port_idf.c new file mode 100644 index 0000000000..bb4db35dcb --- /dev/null +++ b/components/freertos/FreeRTOS-Kernel/portable/linux/port_idf.c @@ -0,0 +1,164 @@ +/* + * SPDX-FileCopyrightText: 2015-2022 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/* + * This file contains most of the code located in the demo application in the + * upstream FreeRTOS repository. It is put here so that IDF applications can + * seamlessly switch between Linux and chip targets without the need to provide + * or implement additional functionality if the target is the Linux target. + */ + +#include +#include +#include +#include +#include +#include + +/* Scheduler includes. */ +#include "FreeRTOS.h" +#include "task.h" +#include "utils/wait_for_event.h" +#include "esp_log.h" + +static const char *TAG = "port"; + +static volatile UBaseType_t uxInterruptNesting = 0; + +/* 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; +} + +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 = xTaskCreatePinnedToCore(&main_task, "main", + ESP_TASK_MAIN_STACK, NULL, + ESP_TASK_MAIN_PRIO, NULL, ESP_TASK_MAIN_CORE); + assert(res == pdTRUE); + (void)res; + + ESP_LOGI(TAG, "Starting 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 + +void vPortYieldOtherCore( BaseType_t coreid ) { } // trying to skip for now + +/* 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; +} +/*-----------------------------------------------------------*/ + +/* 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; +} + +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(); +} diff --git a/components/freertos/FreeRTOS-Kernel/portable/linux/utils/wait_for_event.c b/components/freertos/FreeRTOS-Kernel/portable/linux/utils/wait_for_event.c new file mode 100644 index 0000000000..d3e5f03226 --- /dev/null +++ b/components/freertos/FreeRTOS-Kernel/portable/linux/utils/wait_for_event.c @@ -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 +#include +#include + +#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 ); +} diff --git a/components/freertos/FreeRTOS-Kernel/portable/linux/utils/wait_for_event.h b/components/freertos/FreeRTOS-Kernel/portable/linux/utils/wait_for_event.h new file mode 100644 index 0000000000..45336238c5 --- /dev/null +++ b/components/freertos/FreeRTOS-Kernel/portable/linux/utils/wait_for_event.h @@ -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 +#include + +struct event; + +struct event * event_create(); +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_ */ diff --git a/components/freertos/Kconfig b/components/freertos/Kconfig index a97e5ad06e..8e578c9982 100644 --- a/components/freertos/Kconfig +++ b/components/freertos/Kconfig @@ -21,7 +21,7 @@ menu "FreeRTOS" config FREERTOS_UNICORE # Todo: Replace with CONFIG_NUM_CORES (IDF-4986) bool "Run FreeRTOS only on first core" - default "y" if IDF_TARGET_ESP32S2 + default "y" if IDF_TARGET_ESP32S2 || IDF_TARGET_LINUX select ESP_SYSTEM_SINGLE_CORE_MODE help This version of FreeRTOS normally takes control of all cores of the CPU. Select this if you only want @@ -169,6 +169,7 @@ menu "FreeRTOS" config FREERTOS_TIMER_TASK_STACK_DEPTH int "configTIMER_TASK_STACK_DEPTH" range 1536 32768 + default 2053 if IDF_TARGET_LINUX default 2048 help Set the timer task's stack size (see configTIMER_TASK_STACK_DEPTH documentation for more details). diff --git a/components/freertos/esp_additions/include/freertos/FreeRTOSConfig.h b/components/freertos/esp_additions/include/freertos/FreeRTOSConfig.h index c9651b0b18..7d8e19f308 100644 --- a/components/freertos/esp_additions/include/freertos/FreeRTOSConfig.h +++ b/components/freertos/esp_additions/include/freertos/FreeRTOSConfig.h @@ -102,8 +102,17 @@ This file get's pulled into assembly sources. Therefore, some includes need to b #endif //configUSE_TICKLESS_IDLE #define configCPU_CLOCK_HZ (CONFIG_ESP_DEFAULT_CPU_FREQ_MHZ * 1000000) #define configTICK_RATE_HZ CONFIG_FREERTOS_HZ +#ifdef CONFIG_IDF_TARGET_LINUX +#define configMAX_PRIORITIES ( 7 ) // Default in upstream simulator +/* The stack allocated by FreeRTOS will be passed passed to a pthread. + pthread has a minimal stack size which currently is 16KB. + The rest is for additional structures of the POSIX/Linux port. + This is a magic number since PTHREAD_STACK_MIN seems to not be a constant. */ +#define configMINIMAL_STACK_SIZE ( ( unsigned short ) (0x4000 + 40) / sizeof(portSTACK_TYPE) ) +#else #define configMAX_PRIORITIES ( 25 ) //This has impact on speed of search for highest priority #define configMINIMAL_STACK_SIZE ( 768 + configSTACK_OVERHEAD_TOTAL ) +#endif #define configUSE_TIME_SLICING 1 #define configUSE_16_BIT_TICKS 0 #define configIDLE_SHOULD_YIELD 0 @@ -123,9 +132,12 @@ This file get's pulled into assembly sources. Therefore, some includes need to b #define configMAX_TASK_NAME_LEN CONFIG_FREERTOS_MAX_TASK_NAME_LEN #define configNUM_THREAD_LOCAL_STORAGE_POINTERS CONFIG_FREERTOS_THREAD_LOCAL_STORAGE_POINTERS -#ifndef CONFIG_IDF_TARGET_LINUX #define configSTACK_DEPTH_TYPE uint32_t +#ifndef CONFIG_IDF_TARGET_LINUX #define configUSE_NEWLIB_REENTRANT 1 +#define configINCLUDE_FREERTOS_TASK_C_ADDITIONS_H 1 +#else +#define configINCLUDE_FREERTOS_TASK_C_ADDITIONS_H 0 // Default in upstream simulator #endif #if CONFIG_FREERTOS_ENABLE_BACKWARD_COMPATIBILITY #define configENABLE_BACKWARD_COMPATIBILITY 1 @@ -133,15 +145,18 @@ This file get's pulled into assembly sources. Therefore, some includes need to b #define configENABLE_BACKWARD_COMPATIBILITY 0 #endif #define configASSERT(a) assert(a) -#define configINCLUDE_FREERTOS_TASK_C_ADDITIONS_H 1 // ----------------------- Memory ------------------------- #define configSUPPORT_STATIC_ALLOCATION 1 #define configSUPPORT_DYNAMIC_ALLOCATION 1 +#ifdef CONFIG_IDF_TARGET_LINUX +#define configTOTAL_HEAP_SIZE ( ( size_t ) ( 65 * 1024 ) ) // Default in upstream simulator +#else //We define the heap to span all of the non-statically-allocated shared RAM. ToDo: Make sure there //is some space left for the app and main cpu when running outside of a thread. #define configTOTAL_HEAP_SIZE (&_heap_end - &_heap_start)//( ( size_t ) (64 * 1024) ) +#endif #define configAPPLICATION_ALLOCATED_HEAP 1 #define configSTACK_ALLOCATION_FROM_SEPARATE_HEAP 0 @@ -163,9 +178,13 @@ This file get's pulled into assembly sources. Therefore, some includes need to b #ifdef CONFIG_FREERTOS_GENERATE_RUN_TIME_STATS #define configGENERATE_RUN_TIME_STATS 1 /* Used by vTaskGetRunTimeStats() */ #endif +#ifdef CONFIG_IDF_TARGET_LINUX +#define configUSE_TRACE_FACILITY 1 +#else #ifdef CONFIG_FREERTOS_USE_TRACE_FACILITY #define configUSE_TRACE_FACILITY 1 /* Used by uxTaskGetSystemState(), and other trace facility functions */ #endif +#endif #ifdef CONFIG_FREERTOS_USE_STATS_FORMATTING_FUNCTIONS #define configUSE_STATS_FORMATTING_FUNCTIONS 1 /* Used by vTaskList() */ #endif @@ -194,7 +213,6 @@ This file get's pulled into assembly sources. Therefore, some includes need to b #define INCLUDE_uxTaskPriorityGet 1 #define INCLUDE_vTaskDelete 1 #define INCLUDE_vTaskSuspend 1 -#define INCLUDE_xTaskDelayUntil 1 #define INCLUDE_vTaskDelay 1 #define INCLUDE_xTaskGetIdleTaskHandle 1 #define INCLUDE_xTaskAbortDelay 1 @@ -206,7 +224,13 @@ This file get's pulled into assembly sources. Therefore, some includes need to b #define INCLUDE_xTaskResumeFromISR 1 #define INCLUDE_xTimerPendFunctionCall 1 #define INCLUDE_xTaskGetSchedulerState 1 +#ifdef CONFIG_IDF_TARGET_LINUX +#define INCLUDE_xTaskGetCurrentTaskHandle 0 // not defined in POSIX simulator +#define INCLUDE_vTaskDelayUntil 1 +#else +#define INCLUDE_xTaskDelayUntil 1 #define INCLUDE_xTaskGetCurrentTaskHandle 1 +#endif //Unlisted #define INCLUDE_pxTaskGetStackStart 1 diff --git a/docs/en/api-guides/linux-host-testing.rst b/docs/en/api-guides/linux-host-testing.rst index 57ae25382d..ba9dad1381 100644 --- a/docs/en/api-guides/linux-host-testing.rst +++ b/docs/en/api-guides/linux-host-testing.rst @@ -36,10 +36,11 @@ The current focus of the Linux host tests is on creating isolated unit tests of A complete implementation of IDF to run on Linux does not exist currently. -There are currently two examples for running IDF-built code on Linux host: +Examples for running IDF-built code on Linux host include (non-exhaustive list): -- An example :example_file:`hello-world application ` -- A :component_file:`unit test for NVS `. +- :component_file:`unit test for the NVS Page class `. +- :component_file:`unit test for esp_event `. +- :component_file:`unit test for mqtt `. Inside the component which should be tested, there is a separate directory ``host_test``, besides the "traditional" ``test`` directory or the ``test_apps`` directory. It has one or more subdirectories:: diff --git a/examples/build_system/.build-test-rules.yml b/examples/build_system/.build-test-rules.yml index 67ee6592c2..6388351100 100644 --- a/examples/build_system/.build-test-rules.yml +++ b/examples/build_system/.build-test-rules.yml @@ -6,11 +6,6 @@ examples/build_system/cmake/import_lib: temporary: true reason: lack of runners -examples/build_system/cmake/linux_host_app: - enable: - - if: IDF_TARGET == "linux" - reason: only test on linux - examples/build_system/cmake/plugins: disable_test: - if: IDF_TARGET not in ["esp32", "esp32c3"] diff --git a/examples/build_system/cmake/linux_host_app/CMakeLists.txt b/examples/build_system/cmake/linux_host_app/CMakeLists.txt deleted file mode 100644 index 060c9e4250..0000000000 --- a/examples/build_system/cmake/linux_host_app/CMakeLists.txt +++ /dev/null @@ -1,10 +0,0 @@ -cmake_minimum_required(VERSION 3.16) - -include($ENV{IDF_PATH}/tools/cmake/project.cmake) -set(COMPONENTS main) - -# Freertos is included via common components, however, currently only the mock component is compatible with linux -# target. -list(APPEND EXTRA_COMPONENT_DIRS "$ENV{IDF_PATH}/tools/mocks/freertos/") - -project(linux_host_app) diff --git a/examples/build_system/cmake/linux_host_app/README.md b/examples/build_system/cmake/linux_host_app/README.md deleted file mode 100644 index 02fbd5b5bd..0000000000 --- a/examples/build_system/cmake/linux_host_app/README.md +++ /dev/null @@ -1,38 +0,0 @@ -| Supported Targets | Linux | -| ----------------- | ----- | - -This hello-world example builds a simple hello-world application for Linux. -The compiler used is the Linux-gcc. - -There are two major differences to an IDF application built for an ESP chip compared to an application build for Linux: - -1. The entry-point on Linux is `int main(int argc, char **argv)`, instead of `void app_main(void)` on an ESP chip. - In this example for Linux, the `void app_main(void)` function is still included to make the connection to the IDF entry point clearer. - However, it is simply called by `int main(int argc, char **argv)`. - Refer to the source file [linux_host_app.cpp](main/linux_host_app.cpp) to see how it is used. - -2. The project-level [CMakeLists.txt](CMakeLists.txt) for Linux is different from that of a normal IDF application for an ESP chip. - On Linux, there is an additional line `set(COMPONENTS main)`, which clears the common requirements (default dependencies usually included in all IDF applications). - This is currently necessary as the Linux-host feature is still under development. - Otherwise, a lot of hardware-dependent code would be pulled in. - -# Requirements -Currently, Ruby is required for the mock override of FreeRTOS. - -# Build -Source the IDF environment as usual, then set the Linux target: -```bash -idf.py --preview set-target linux -``` -sdkconfig.defaults sets the Linux target by default, so this not strictly necessary. - -Once this is done, build the application: -```bash -idf.py build -``` -Since this application runs on host, the flashing step is unnecessary. - -# Run -```bash -`build/linux_host_app.elf` -``` diff --git a/examples/build_system/cmake/linux_host_app/main/CMakeLists.txt b/examples/build_system/cmake/linux_host_app/main/CMakeLists.txt deleted file mode 100644 index 25db9e0ab7..0000000000 --- a/examples/build_system/cmake/linux_host_app/main/CMakeLists.txt +++ /dev/null @@ -1 +0,0 @@ -idf_component_register(SRCS "linux_host_app.cpp") diff --git a/examples/build_system/cmake/linux_host_app/main/linux_host_app.cpp b/examples/build_system/cmake/linux_host_app/main/linux_host_app.cpp deleted file mode 100644 index 7c646d1743..0000000000 --- a/examples/build_system/cmake/linux_host_app/main/linux_host_app.cpp +++ /dev/null @@ -1,30 +0,0 @@ -/* Hello World Example - - This example code is in the Public Domain (or CC0 licensed, at your option.) - - Unless required by applicable law or agreed to in writing, this - software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR - CONDITIONS OF ANY KIND, either express or implied. -*/ - -#include "stdio.h" -#include - -void app_main() { - while(1) { - printf("Hello, Host!\n"); - - for (int i = 10; i >= 0; i--) { - printf("Restarting in %d seconds...\n", i); - sleep(1); - } - } -} - -int main(int argc, char **argv) -{ - setbuf(stdout, NULL); - app_main(); - - return 0; -} diff --git a/examples/build_system/cmake/linux_host_app/sdkconfig.defaults b/examples/build_system/cmake/linux_host_app/sdkconfig.defaults deleted file mode 100644 index f14d0b8caa..0000000000 --- a/examples/build_system/cmake/linux_host_app/sdkconfig.defaults +++ /dev/null @@ -1,3 +0,0 @@ -CONFIG_UNITY_ENABLE_IDF_TEST_RUNNER=n -CONFIG_IDF_TARGET="linux" -CONFIG_COMPILER_CXX_EXCEPTIONS=y diff --git a/tools/ci/check_copyright_ignore.txt b/tools/ci/check_copyright_ignore.txt index 7160ebc59e..a7f8d1eba0 100644 --- a/tools/ci/check_copyright_ignore.txt +++ b/tools/ci/check_copyright_ignore.txt @@ -1582,7 +1582,6 @@ examples/build_system/cmake/import_prebuilt/main/main.c examples/build_system/cmake/import_prebuilt/prebuilt/components/prebuilt/prebuilt.c examples/build_system/cmake/import_prebuilt/prebuilt/components/prebuilt/prebuilt.h examples/build_system/cmake/import_prebuilt/prebuilt/main/main.c -examples/build_system/cmake/linux_host_app/main/linux_host_app.cpp examples/build_system/cmake/multi_config/main/func.h examples/build_system/cmake/multi_config/main/func_dev.c examples/build_system/cmake/multi_config/main/func_prod.c diff --git a/tools/mocks/freertos/CMakeLists.txt b/tools/mocks/freertos/CMakeLists.txt index 410855eace..a72dee3577 100644 --- a/tools/mocks/freertos/CMakeLists.txt +++ b/tools/mocks/freertos/CMakeLists.txt @@ -4,13 +4,15 @@ message(STATUS "building FREERTOS MOCKS (only task, event-groups and queue)") idf_component_get_property(original_freertos_dir freertos COMPONENT_OVERRIDEN_DIR) +set(kernel_dir "${original_freertos_dir}/FreeRTOS-Kernel") + set(include_dirs - "include" - "${original_freertos_dir}/FreeRTOS-Kernel/include" + "${kernel_dir}/include" "${original_freertos_dir}/esp_additions/include" "${original_freertos_dir}/esp_additions/include/freertos" - "${original_freertos_dir}/FreeRTOS-Kernel/include/freertos" # this is due to the way includes are generated in CMock - "${original_freertos_dir}/FreeRTOS-Kernel/portable/linux/include") + "${kernel_dir}/portable/linux/include" # For FreeRTOSConfig_arch.h + "${kernel_dir}/include/freertos" # this is due to the way includes are generated in CMock (without freertos prefix) +) idf_component_mock(INCLUDE_DIRS ${include_dirs} REQUIRES esp_common @@ -18,3 +20,6 @@ idf_component_mock(INCLUDE_DIRS ${include_dirs} ${original_freertos_dir}/FreeRTOS-Kernel/include/freertos/task.h ${original_freertos_dir}/FreeRTOS-Kernel/include/freertos/event_groups.h ${original_freertos_dir}/FreeRTOS-Kernel/include/freertos/queue.h) + +idf_component_get_property(freertos_lib freertos COMPONENT_LIB) +target_compile_definitions(${freertos_lib} PUBLIC "projCOVERAGE_TEST=0") diff --git a/tools/mocks/freertos/Kconfig b/tools/mocks/freertos/Kconfig index ddb265003a..3a2d4786a7 100644 --- a/tools/mocks/freertos/Kconfig +++ b/tools/mocks/freertos/Kconfig @@ -1,13 +1,22 @@ menu "FreeRTOS" - config FREERTOS_MAX_TASK_NAME_LEN - int "Maximum task name length" - range 1 256 - default 16 - help - Changes the maximum task name length. Each task allocated will - include this many bytes for a task name. Using a shorter value - saves a small amount of RAM, a longer value allows more complex - names. - For most uses, the default of 16 is OK. + menu "Kernel" + config FREERTOS_HZ + int "configTICK_RATE_HZ" + range 1 1000 + default 1000 + help + Sets the FreeRTOS tick interrupt frequency in Hz (see configTICK_RATE_HZ documentation for more + details). + + config FREERTOS_MAX_TASK_NAME_LEN + int "configMAX_TASK_NAME_LEN" + range 1 256 + default 16 + help + Sets the maximum number of characters for task names (see configMAX_TASK_NAME_LEN documentation for + more details). + + Note: For most uses, the default of 16 characters is sufficient. + endmenu endmenu diff --git a/tools/mocks/freertos/include/FreeRTOSConfig.h b/tools/mocks/freertos/include/FreeRTOSConfig.h deleted file mode 100644 index eb1f4defe2..0000000000 --- a/tools/mocks/freertos/include/FreeRTOSConfig.h +++ /dev/null @@ -1,25 +0,0 @@ -/* - * SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD - * - * SPDX-License-Identifier: Apache-2.0 - */ -#define STACK_OVERHEAD_CHECKER 256 -#define STACK_OVERHEAD_OPTIMIZATION 320 -#define STACK_OVERHEAD_APPTRACE 1280 -#define STACK_OVERHEAD_WATCHPOINT 60 -#define configSTACK_OVERHEAD_TOTAL ( \ - STACK_OVERHEAD_CHECKER + \ - STACK_OVERHEAD_OPTIMIZATION + \ - STACK_OVERHEAD_APPTRACE + \ - STACK_OVERHEAD_WATCHPOINT \ - ) -#define configMINIMAL_STACK_SIZE ( 768 + configSTACK_OVERHEAD_TOTAL ) -#define configMAX_PRIORITIES ( 25 ) //This has impact on speed of search for highest priority -#define configUSE_PREEMPTION 1 -#define configUSE_IDLE_HOOK CONFIG_FREERTOS_USE_IDLE_HOOK -#define configUSE_TICK_HOOK CONFIG_FREERTOS_USE_TICK_HOOK -#define configUSE_16_BIT_TICKS 0 -#define configUSE_TRACE_FACILITY 1 -#define configSUPPORT_DYNAMIC_ALLOCATION 1 -#define configUSE_MUTEXES 1 -#define configUSE_RECURSIVE_MUTEXES 1 diff --git a/tools/test_apps/.build-test-rules.yml b/tools/test_apps/.build-test-rules.yml index 8bc316fe55..8722fd678e 100644 --- a/tools/test_apps/.build-test-rules.yml +++ b/tools/test_apps/.build-test-rules.yml @@ -11,6 +11,14 @@ tools/test_apps/build_system/ldgen_test: temporary: true reason: target esp32c2 is not supported yet +tools/test_apps/linux_compatible/hello_world_linux_compatible: + enable: + - if: INCLUDE_DEFAULT == 1 or IDF_TARGET == "linux" + disable_test: + - if: IDF_TARGET not in ["esp32", "esp32c3"] + temporary: true + reason: pytest doesn't support linux target yet, hence, it's tested independenly in the host_tests stage + tools/test_apps/peripherals/usb: enable: - if: IDF_TARGET in ["esp32s2", "esp32s3"] diff --git a/tools/test_apps/linux_compatible/hello_world_linux_compatible/CMakeLists.txt b/tools/test_apps/linux_compatible/hello_world_linux_compatible/CMakeLists.txt new file mode 100644 index 0000000000..827441ca4c --- /dev/null +++ b/tools/test_apps/linux_compatible/hello_world_linux_compatible/CMakeLists.txt @@ -0,0 +1,7 @@ +# The following 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(hello_world) diff --git a/tools/test_apps/linux_compatible/hello_world_linux_compatible/README.md b/tools/test_apps/linux_compatible/hello_world_linux_compatible/README.md new file mode 100644 index 0000000000..2a5c7d4293 --- /dev/null +++ b/tools/test_apps/linux_compatible/hello_world_linux_compatible/README.md @@ -0,0 +1,45 @@ +| Supported Targets | ESP32 | ESP32-C2 | ESP32-C3 | ESP32-S2 | ESP32-S3 | Linux | +| ----------------- | ----- | -------- | -------- | -------- | -------- | ----- | + +# Hello World Example Compatible with POSIX-port + +This is a version of the "Hello World" example compatible with the linux target. Just by using `idf.py (--preview) set-target `, it can be compiled for chip targets as well as for the [FreeRTOS POSIX/Linux simulator](https://www.freertos.org/FreeRTOS-simulator-for-Linux.html), i.e., for running it on Linux. The applications can then be run on the chosen target. + +## Requirements + +If you want to use this example on Linux, you need a Linux machine as host. The remaining requirements are the same requirements as for [Unit Testing on Linux (using cmock)](https://docs.espressif.com/projects/esp-idf/en/stable/esp32/api-guides/linux-host-testing.html#requirements), except you do not need Ruby. + +## How to use example + +### Configure the project + +No special configuration is required, we also do not recommend changing configuration when compiled for the POSIX/Linux simulator as this is still in preview. If you have to configure something, use the usual IDF menuconfig: +``` +idf.py menuconfig +``` + +### Build and Flash + +You can compile this example for chip targets, e.g. ESP32 and then run it by using: +``` +idf.py set-target esp32 +idf.py build +idf.py -p flash monitor +``` + +If you want to build this example for the linux target and run it, use the same commands except setting the linux target and omitting the flash command: +``` +idf.py --preview set-target linux +idf.by build +idf.py monitor +``` +The linux target is still in preview, hence the necessary `--preview` argument. Flashing can be omitted on Linux. + + +## Example folder contents + +The files in this project have the same structure as the files in the [original Hello World application](../../../../examples/get-started/hello_world/). + +## Example Output + +The output is similar to the output of the [original Hello World application](../../../../examples/get-started/hello_world/), except that no chip information is printed and there won't be any bootloader output on the linux target. diff --git a/tools/test_apps/linux_compatible/hello_world_linux_compatible/main/CMakeLists.txt b/tools/test_apps/linux_compatible/hello_world_linux_compatible/main/CMakeLists.txt new file mode 100644 index 0000000000..392c049cbf --- /dev/null +++ b/tools/test_apps/linux_compatible/hello_world_linux_compatible/main/CMakeLists.txt @@ -0,0 +1,4 @@ +idf_component_register(SRCS "hello_world_main.c" + INCLUDE_DIRS "") + +target_compile_options(${COMPONENT_LIB} PRIVATE "-Wno-format") diff --git a/tools/test_apps/linux_compatible/hello_world_linux_compatible/main/hello_world_main.c b/tools/test_apps/linux_compatible/hello_world_linux_compatible/main/hello_world_main.c new file mode 100644 index 0000000000..988ea320fc --- /dev/null +++ b/tools/test_apps/linux_compatible/hello_world_linux_compatible/main/hello_world_main.c @@ -0,0 +1,24 @@ +/* + * SPDX-FileCopyrightText: 2010-2022 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: CC0-1.0 + */ + +#include +#include +#include "sdkconfig.h" +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" + +void app_main(void) +{ + printf("Hello world!\n"); + + for (int i = 10; i >= 0; i--) { + printf("Restarting in %d seconds...\n", i); + vTaskDelay(1000 / portTICK_PERIOD_MS); + } + printf("Restarting now.\n"); + fflush(stdout); + exit(0); +} diff --git a/tools/test_apps/linux_compatible/hello_world_linux_compatible/pytest_hello_world_linux_compatible.py b/tools/test_apps/linux_compatible/hello_world_linux_compatible/pytest_hello_world_linux_compatible.py new file mode 100644 index 0000000000..08e67cea7a --- /dev/null +++ b/tools/test_apps/linux_compatible/hello_world_linux_compatible/pytest_hello_world_linux_compatible.py @@ -0,0 +1,15 @@ +# SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD +# SPDX-License-Identifier: CC0-1.0 + +import pytest +from pytest_embedded_idf.dut import IdfDut + +# Note that support for Linux target console applications hasn't been implemented for pytest-embedded yet +# (https://github.com/espressif/pytest-embedded/issues/106) + + +@pytest.mark.esp32 +@pytest.mark.esp32c3 +@pytest.mark.generic +def test_hello_world_linux_compatible(dut: IdfDut) -> None: + dut.expect('Hello world!')