diff --git a/.gitignore b/.gitignore index a6f61ddaf..dbc24cdb2 100644 --- a/.gitignore +++ b/.gitignore @@ -11,3 +11,4 @@ cmake-build-release doc/html doc/latex doc/markdown/platform +sdkconfig \ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt index 66b128bc7..bc7c7a578 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -66,6 +66,15 @@ elseif (PLATFORM STREQUAL "PICO") elseif (PLATFORM STREQUAL "PATMOS") add_library(reactor-uc STATIC ${SOURCES}) +elseif( PLATFORM STREQUAL "FREERTOS") + add_library(reactor-uc STATIC ${SOURCES}) + # Include FreeRTOSConfig directory + target_include_directories(reactor-uc PUBLIC ${FREERTOS_CONFIG_PATH}) + + target_link_libraries(reactor-uc PUBLIC FreeRTOS-Kernel FreeRTOS-Kernel-Heap1) +elseif (PLATFORM STREQUAL "ESP_IDF") + add_library(reactor-uc STATIC ${SOURCES}) + target_link_libraries(reactor-uc PUBLIC idf::freertos idf::spi_flash idf::esp_timer) else () message(FATAL_ERROR "No valid platform specified") endif () diff --git a/examples/README.md b/examples/README.md new file mode 100644 index 000000000..481f2e056 --- /dev/null +++ b/examples/README.md @@ -0,0 +1,86 @@ +# Reactor-UC Examples + +This directory contains example applications for the Reactor-UC runtime across various platforms. These examples demonstrate the core functionality of the framework and can serve as a starting point for exploring Reactor-UC. + +## Directory Structure + +``` +examples/ +├── common/ # Shared header files used across examples +├── esp-idf/ # Espressif ESP-IDF examples +├── freertos/ # FreeRTOS examples +├── posix/ # POSIX-compliant systems (Linux, macOS) +├── pico/ # Raspberry Pi Pico examples +├── zephyr/ # Zephyr RTOS examples +├── riot/ # RIOT OS examples +├── flexpret/ # FlexPRET platform examples +└── fed-template/ # Federated application templates +``` + +## Building All Examples + +Each platform directory contains a `buildAll.sh` script to build all examples for that platform: + +```sh +cd examples/ +./buildAll.sh +``` + +To build all examples across all platforms (requires all platform dependencies installed): + +```sh +cd examples +./runAll.sh +``` + +## WSL (Windows Subsystem for Linux) Considerations + +If you're developing on Windows using WSL, there are important considerations when working with embedded devices. + +### Serial Port Access + +WSL2 doesn't natively support USB device passthrough. To access serial ports from your embedded devices, you can follow the [Microsoft WSL USB guide](https://learn.microsoft.com/en-us/windows/wsl/connect-usb). + +### Configuring Runtime Duration for Serial Monitoring + +When using WSL with external serial ports, it's convenient to configure your application to run indefinitely. Otherwise, the program may terminate before you can connect your serial monitor. + +Edit the timeout in the `LF_ENTRY_POINT` macro in the `examples/common/timer_source.h` file: + +```c +// Run for 1 second (default) +LF_ENTRY_POINT(TimerSource, 32, 32, SEC(1), false, false); + +// Run indefinitely (recommended for WSL with external serial monitors) +LF_ENTRY_POINT(TimerSource, 32, 32, FOREVER, false, false); + +// Run for other durations +LF_ENTRY_POINT(TimerSource, 32, 32, MINS(5), false, false); // 5 minutes +LF_ENTRY_POINT(TimerSource, 32, 32, HOUR(1), false, false); // 1 hour +``` + +This ensures your device continues outputting serial data, giving you enough time to connect your serial monitor on the Windows side. + +## Troubleshooting + +### Build Failures + +If you encounter build issues: + +1. Clean the build directory: + ```sh + rm -rf build && mkdir build + ``` + +2. Verify all prerequisites are installed for your platform + +3. Check that platform-specific environment variables are set correctly + +## Contributing + +When adding new examples: + +1. Place shared code in `common/` +2. Follow the existing directory structure for platform-specific examples +3. Add a `buildAll.sh` script for batch building +4. Test on the target platform before submitting a PR \ No newline at end of file diff --git a/examples/common/timer_source.h b/examples/common/timer_source.h index cbdd6ceac..1841371d9 100644 --- a/examples/common/timer_source.h +++ b/examples/common/timer_source.h @@ -1,25 +1,45 @@ #include "reactor-uc/reactor-uc.h" #include "reactor-uc/schedulers/dynamic/scheduler.h" -LF_DEFINE_TIMER_STRUCT(TimerSource, t, 1, 0); -LF_DEFINE_TIMER_CTOR(TimerSource, t, 1, 0); +LF_DEFINE_REACTION_STRUCT(TimerSource, s, 0); LF_DEFINE_REACTION_STRUCT(TimerSource, r, 0); -LF_DEFINE_REACTION_CTOR(TimerSource, r, 0, NULL, NULL); + +LF_DEFINE_TIMER_STRUCT(TimerSource, t, 1, 0); +LF_DEFINE_STARTUP_STRUCT(TimerSource, 1, 0); typedef struct { Reactor super; + LF_TIMER_INSTANCE(TimerSource, t); LF_REACTION_INSTANCE(TimerSource, r); - LF_REACTOR_BOOKKEEPING_INSTANCES(1,1,0); + + LF_REACTION_INSTANCE(TimerSource, s); + LF_STARTUP_INSTANCE(TimerSource); + + LF_REACTOR_BOOKKEEPING_INSTANCES(2,2,0); } TimerSource; +LF_DEFINE_REACTION_CTOR(TimerSource, s, 0, NULL, NULL); +LF_DEFINE_REACTION_CTOR(TimerSource, r, 1, NULL, NULL); + +LF_DEFINE_STARTUP_CTOR(TimerSource); +LF_DEFINE_TIMER_CTOR(TimerSource, t, 1, 0); LF_REACTOR_CTOR_SIGNATURE(TimerSource) { LF_REACTOR_CTOR_PREAMBLE(); LF_REACTOR_CTOR(TimerSource); - LF_INITIALIZE_REACTION(TimerSource, r, NEVER); + LF_INITIALIZE_TIMER(TimerSource, t, MSEC(0), MSEC(500)); + LF_INITIALIZE_STARTUP(TimerSource); + + LF_INITIALIZE_REACTION(TimerSource, s, NEVER); + LF_STARTUP_REGISTER_EFFECT(self->s); + + LF_INITIALIZE_REACTION(TimerSource, r, NEVER); LF_TIMER_REGISTER_EFFECT(self->t, self->r); + } +// Replace SEC(1) with FOREVER to run indefinitely. +// You can also use MINS(...) or HOURS(...) to specify other durations. LF_ENTRY_POINT(TimerSource,32,32, SEC(1), false, false); \ No newline at end of file diff --git a/examples/esp-idf/blink/CMakeLists.txt b/examples/esp-idf/blink/CMakeLists.txt new file mode 100644 index 000000000..3aa83d65f --- /dev/null +++ b/examples/esp-idf/blink/CMakeLists.txt @@ -0,0 +1,38 @@ +cmake_minimum_required(VERSION 3.20.0) +set(PLATFORM "ESP_IDF" CACHE STRING "Platform to target") +set(BUILD_EXAMPLES OFF CAHCHE BOOL) + +set(IDF_COMPONENTS "freertos" "esptool_py" "driver" "led_strip") + +if(DEFINED ENV{IDF_PATH}) +else() + message(FATAL_ERROR "IDF_PATH environment variable not set") +endif() + +# Include for ESP-IDF build system functions +include($ENV{IDF_PATH}/tools/cmake/idf.cmake) +# Set led_strip component path +set(EXTRA_COMPONENT_DIRS $ENV{IDF_PATH}/../idf-extra-components/led_strip) +idf_build_component($ENV{IDF_PATH}/../idf-extra-components/led_strip) + +project(blink) + +# Create idf::{target} and idf::freertos static libraries +idf_build_process(${IDF_TARGET} + COMPONENTS ${IDF_COMPONENTS} + SDKCONFIG ${CMAKE_CURRENT_LIST_DIR}/sdkconfig + BUILD_DIR ${CMAKE_BINARY_DIR}) + +add_subdirectory(../../../ reactor-uc) + +# Suppress unused parameter warnings for reactor-uc and main executable +# This is needed because ESP-IDF headers (e.g., spinlock.h) have unused parameters +target_compile_options(reactor-uc PRIVATE -Wno-unused-parameter) + +set(elf_file ${CMAKE_PROJECT_NAME}.elf) +add_executable(${elf_file} main.c) +target_link_libraries(${elf_file} PRIVATE reactor-uc idf::led_strip idf::driver) + +idf_build_executable(${elf_file}) + + \ No newline at end of file diff --git a/examples/esp-idf/blink/main.c b/examples/esp-idf/blink/main.c new file mode 100644 index 000000000..19ab8ce2a --- /dev/null +++ b/examples/esp-idf/blink/main.c @@ -0,0 +1,106 @@ +#include +#include +#include "sdkconfig.h" +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "esp_chip_info.h" +#include "esp_flash.h" +#include "esp_system.h" +#include "reactor-uc/reactor-uc.h" +#include "sys/time.h" +#include "esp_timer.h" +#include "../../common/timer_source.h" +#include "led_strip.h" +#include "driver/gpio.h" + +#define BLINK_GPIO 8 +#define CONFIG_BLINK_LED_STRIP 1 +#define CONFIG_BLINK_LED_STRIP_BACKEND_SPI 1 +static uint8_t s_led_state = 0; + +#ifdef CONFIG_BLINK_LED_STRIP + +static led_strip_handle_t led_strip; + +static void blink_led(void) +{ + /* If the addressable LED is enabled */ + if (s_led_state) { + /* Set the LED pixel using RGB from 0 (0%) to 255 (100%) for each color */ + led_strip_set_pixel(led_strip, 0, 16, 16, 16); + /* Refresh the strip to send data */ + led_strip_refresh(led_strip); + } else { + /* Set all LED off to clear all pixels */ + led_strip_clear(led_strip); + } +} + +static void configure_led(void) +{ + printf("Example configured to blink addressable LED!\n"); + /* LED strip initialization with the GPIO and pixels number*/ + led_strip_config_t strip_config = { + .strip_gpio_num = BLINK_GPIO, + .max_leds = 1, // at least one LED on board + }; +#if CONFIG_BLINK_LED_STRIP_BACKEND_RMT + led_strip_rmt_config_t rmt_config = { + .resolution_hz = 10 * 1000 * 1000, // 10MHz + .flags.with_dma = false, + }; + ESP_ERROR_CHECK(led_strip_new_rmt_device(&strip_config, &rmt_config, &led_strip)); +#elif CONFIG_BLINK_LED_STRIP_BACKEND_SPI + led_strip_spi_config_t spi_config = { + .spi_bus = SPI2_HOST, + .flags.with_dma = true, + }; + ESP_ERROR_CHECK(led_strip_new_spi_device(&strip_config, &spi_config, &led_strip)); +#else +#error "unsupported LED strip backend" +#endif + /* Set all LED off to clear all pixels */ + led_strip_clear(led_strip); +} + +#elif CONFIG_BLINK_LED_GPIO + +static void blink_led(void) +{ + /* Set the GPIO level according to the state (LOW or HIGH)*/ + gpio_set_level(BLINK_GPIO, s_led_state); +} + +static void configure_led(void) +{ + ESP_LOGI(TAG, "Example configured to blink GPIO LED!"); + gpio_reset_pin(BLINK_GPIO); + /* Set the GPIO as a push/pull output */ + gpio_set_direction(BLINK_GPIO, GPIO_MODE_OUTPUT); +} + +#else +#error "unsupported LED type" +#endif + +LF_DEFINE_REACTION_BODY(TimerSource, r) { + LF_SCOPE_SELF(TimerSource); + LF_SCOPE_ENV(); + printf("Turning the LED %s! @ lt=%lld, pt=%lld\n", s_led_state == true ? "ON" : "OFF", env->get_elapsed_logical_time(env), env->get_physical_time(env)); + blink_led(); + /* Toggle the LED state */ + s_led_state = !s_led_state; +} + +LF_DEFINE_REACTION_BODY(TimerSource, s) { + LF_SCOPE_SELF(TimerSource); + LF_SCOPE_ENV(); + LF_SCOPE_STARTUP(TimerSource); + // Configure the LED on startup + configure_led(); +} + +void app_main(void) +{ + lf_start(); +} diff --git a/examples/esp-idf/buildAll.sh b/examples/esp-idf/buildAll.sh new file mode 100755 index 000000000..5ea7efd5b --- /dev/null +++ b/examples/esp-idf/buildAll.sh @@ -0,0 +1,18 @@ +#!/bin/bash +set -e + +# Set the target platform +IDF_TARGET="esp32c6" +# List of folders +FOLDERS=("hello" "blink") + +# Iterate over each folder and execute the command +for dir in "${FOLDERS[@]}"; do + echo "Entering $dir" + pushd $dir + + rm -rf build && mkdir build && cd build + cmake .. -DCMAKE_TOOLCHAIN_FILE=$IDF_PATH/tools/cmake/toolchain-${IDF_TARGET}.cmake -DCMAKE_EXPORT_COMPILE_COMMANDS=ON -DPLATFORM=ESP_IDF -DIDF_TARGET=${IDF_TARGET} -GNinja + cmake --build . -j + popd +done \ No newline at end of file diff --git a/examples/esp-idf/hello/CMakeLists.txt b/examples/esp-idf/hello/CMakeLists.txt new file mode 100644 index 000000000..f5fa4cd87 --- /dev/null +++ b/examples/esp-idf/hello/CMakeLists.txt @@ -0,0 +1,33 @@ +cmake_minimum_required(VERSION 3.20.0) +set(PLATFORM "ESP_IDF" CACHE STRING "Platform to target") +set(BUILD_EXAMPLES OFF CAHCHE BOOL) + +set(IDF_COMPONENTS "freertos" "esptool_py") + +if(DEFINED ENV{IDF_PATH}) +else() + message(FATAL_ERROR "IDF_PATH environment variable not set") +endif() + +# Include for ESP-IDF build system functions +include($ENV{IDF_PATH}/tools/cmake/idf.cmake) + +project(hello) + +# Create idf::{target} and idf::freertos static libraries +idf_build_process(${IDF_TARGET} + COMPONENTS ${IDF_COMPONENTS} + SDKCONFIG ${CMAKE_CURRENT_LIST_DIR}/sdkconfig + BUILD_DIR ${CMAKE_BINARY_DIR}) + +add_subdirectory(../../../ reactor-uc) + +# Suppress unused parameter warnings for reactor-uc and main executable +# This is needed because ESP-IDF headers (e.g., spinlock.h) have unused parameters +target_compile_options(reactor-uc PRIVATE -Wno-unused-parameter) + +set(elf_file ${CMAKE_PROJECT_NAME}.elf) +add_executable(${elf_file} main.c) +target_link_libraries(${elf_file} PRIVATE reactor-uc) + +idf_build_executable(${elf_file}) \ No newline at end of file diff --git a/examples/esp-idf/hello/main.c b/examples/esp-idf/hello/main.c new file mode 100644 index 000000000..0d81f8a23 --- /dev/null +++ b/examples/esp-idf/hello/main.c @@ -0,0 +1,56 @@ +#include +#include +#include "sdkconfig.h" +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "esp_chip_info.h" +#include "esp_flash.h" +#include "esp_system.h" +#include "reactor-uc/reactor-uc.h" +#include "sys/time.h" +#include "esp_timer.h" +#include "../../common/timer_source.h" + +LF_DEFINE_REACTION_BODY(TimerSource, s) { + LF_SCOPE_SELF(TimerSource); + LF_SCOPE_ENV(); + LF_SCOPE_STARTUP(TimerSource); + + /* Print chip information */ + esp_chip_info_t chip_info; + uint32_t flash_size; + esp_chip_info(&chip_info); + + printf("This is %s chip with %d CPU core(s), %s%s%s%s, ", + CONFIG_IDF_TARGET, + chip_info.cores, + (chip_info.features & CHIP_FEATURE_WIFI_BGN) ? "WiFi/" : "", + (chip_info.features & CHIP_FEATURE_BT) ? "BT" : "", + (chip_info.features & CHIP_FEATURE_BLE) ? "BLE" : "", + (chip_info.features & CHIP_FEATURE_IEEE802154) ? ", 802.15.4 (Zigbee/Thread)" : ""); + + unsigned major_rev = chip_info.revision / 100; + unsigned minor_rev = chip_info.revision % 100; + printf("silicon revision v%d.%d, ", major_rev, minor_rev); + + if(esp_flash_get_size(NULL, &flash_size) != ESP_OK) { + printf("Get flash size failed"); + return; + } + + printf("%" PRIu32 "MB %s flash\n", flash_size / (uint32_t)(1024 * 1024), + (chip_info.features & CHIP_FEATURE_EMB_FLASH) ? "embedded" : "external"); + + printf("Minimum free heap size: %" PRIu32 " bytes\n", esp_get_minimum_free_heap_size()); +} + +LF_DEFINE_REACTION_BODY(TimerSource, r) { + LF_SCOPE_SELF(TimerSource); + LF_SCOPE_ENV(); + printf("TimerSource World @ lt=%lld, pt=%lld\n", env->get_elapsed_logical_time(env), env->get_physical_time(env)); +} + +void app_main(void) +{ + lf_start(); +} diff --git a/examples/freertos/buildAll.sh b/examples/freertos/buildAll.sh new file mode 100755 index 000000000..360945d51 --- /dev/null +++ b/examples/freertos/buildAll.sh @@ -0,0 +1,13 @@ +#!/bin/bash +set -e + +# Set the target board +PICO_BOARD="pico_w" +PICO_FOLDER="pico" +echo "Entering $PICO_FOLDER" + pushd $PICO_FOLDER + +rm -rf build && mkdir build && cd build +cmake -DPICO_BOARD=pico_w -DPLATFORM=FREERTOS .. +cmake --build . -j + popd diff --git a/examples/freertos/pico/CMakeLists.txt b/examples/freertos/pico/CMakeLists.txt new file mode 100644 index 000000000..75e507318 --- /dev/null +++ b/examples/freertos/pico/CMakeLists.txt @@ -0,0 +1,40 @@ +cmake_minimum_required(VERSION 3.20.0) +set(PLATFORM "FREERTOS" CACHE STRING "Platform to target") +set(BUILD_EXAMPLES OFF CAHCHE BOOL) + +if (DEFINED ENV{PICO_SDK_PATH}) + include("$ENV{PICO_SDK_PATH}/pico_sdk_init.cmake") +else() + message(FATAL_ERROR "PICO_SDK_PATH environment variable not set") +endif() + +if (DEFINED ENV{FREERTOS_KERNEL_PATH}) + include("$ENV{FREERTOS_KERNEL_PATH}/portable/ThirdParty/GCC/RP2040/FreeRTOS_Kernel_import.cmake") +else() + message(FATAL_ERROR "FREERTOS_KERNEL_PATH environment variable not set") +endif() + +if (NOT DEFINED ENV{FREERTOS_DEMO_PATH}) + message(FATAL_ERROR "FREERTOS_DEMO_PATH environment variable not set") +endif() + +project(reactor-uc-pico) +pico_sdk_init() + +add_executable(blinky src/blinky.c) + +pico_enable_stdio_usb(blinky 1) +pico_enable_stdio_uart(blinky 0) +target_include_directories(blinky PRIVATE + ${CMAKE_CURRENT_LIST_DIR} + $ENV{FREERTOS_DEMO_PATH}/Common/include) + +# Set FreeRTOSConfig path for the reactor-uc library +set(FREERTOS_CONFIG_PATH ${CMAKE_CURRENT_LIST_DIR}/include) + +add_subdirectory(../../../ reactor-uc) +target_link_libraries(blinky PRIVATE reactor-uc pico_stdlib pico_sync) + +if (PICO_CYW43_SUPPORTED) + target_link_libraries(blinky PRIVATE pico_cyw43_arch_none) +endif() diff --git a/examples/freertos/pico/include/FreeRTOSConfig.h b/examples/freertos/pico/include/FreeRTOSConfig.h new file mode 100644 index 000000000..0b436f913 --- /dev/null +++ b/examples/freertos/pico/include/FreeRTOSConfig.h @@ -0,0 +1,139 @@ +/* + * FreeRTOS V202212.00 + * 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 + * + */ + +#ifndef FREERTOS_CONFIG_H +#define FREERTOS_CONFIG_H + +/*----------------------------------------------------------- + * Application specific definitions. + * + * These definitions should be adjusted for your particular hardware and + * application requirements. + * + * THESE PARAMETERS ARE DESCRIBED WITHIN THE 'CONFIGURATION' SECTION OF THE + * FreeRTOS API DOCUMENTATION AVAILABLE ON THE FreeRTOS.org WEB SITE. + * + * See http://www.freertos.org/a00110.html + *----------------------------------------------------------*/ + +/* Scheduler Related */ +#define configUSE_PREEMPTION 1 +#define configUSE_TICKLESS_IDLE 0 +#define configUSE_IDLE_HOOK 0 +#define configUSE_TICK_HOOK 1 +#define configCPU_CLOCK_HZ ( 133000000 ) +#define configTICK_RATE_HZ ( ( TickType_t ) 100 ) +#define configMAX_PRIORITIES 32 +#define configMINIMAL_STACK_SIZE ( configSTACK_DEPTH_TYPE ) 256 +#define configUSE_16_BIT_TICKS 0 + +#define configIDLE_SHOULD_YIELD 1 + +/* Synchronization Related */ +#define configUSE_MUTEXES 1 +#define configUSE_RECURSIVE_MUTEXES 1 +#define configUSE_APPLICATION_TASK_TAG 0 +#define configUSE_COUNTING_SEMAPHORES 1 +#define configQUEUE_REGISTRY_SIZE 8 +#define configUSE_QUEUE_SETS 1 +#define configUSE_TIME_SLICING 1 +#define configUSE_NEWLIB_REENTRANT 0 +#define configENABLE_BACKWARD_COMPATIBILITY 0 +#define configNUM_THREAD_LOCAL_STORAGE_POINTERS 5 + +/* System */ +#define configSTACK_DEPTH_TYPE uint32_t +#define configMESSAGE_BUFFER_LENGTH_TYPE size_t + +/* Memory allocation related definitions. */ +#define configSUPPORT_STATIC_ALLOCATION 0 +#define configSUPPORT_DYNAMIC_ALLOCATION 1 +#define configTOTAL_HEAP_SIZE (128*1024) +#define configAPPLICATION_ALLOCATED_HEAP 0 + +/* Hook function related definitions. */ +#define configCHECK_FOR_STACK_OVERFLOW 2 +#define configUSE_MALLOC_FAILED_HOOK 1 +#define configUSE_DAEMON_TASK_STARTUP_HOOK 0 + +/* Run time and task stats gathering related definitions. */ +#define configGENERATE_RUN_TIME_STATS 0 +#define configUSE_TRACE_FACILITY 1 +#define configUSE_STATS_FORMATTING_FUNCTIONS 0 + +/* Co-routine related definitions. */ +#define configUSE_CO_ROUTINES 0 +#define configMAX_CO_ROUTINE_PRIORITIES 1 + +/* Software timer related definitions. */ +#define configUSE_TIMERS 1 +#define configTIMER_TASK_PRIORITY ( configMAX_PRIORITIES - 1 ) +#define configTIMER_QUEUE_LENGTH 10 +#define configTIMER_TASK_STACK_DEPTH 1024 + +/* Interrupt nesting behaviour configuration. */ +/* +#define configKERNEL_INTERRUPT_PRIORITY [dependent of processor] +#define configMAX_SYSCALL_INTERRUPT_PRIORITY [dependent on processor and application] +#define configMAX_API_CALL_INTERRUPT_PRIORITY [dependent on processor and application] +*/ + +/* SMP port only */ +#define configNUMBER_OF_CORES 1 +#define configTICK_CORE 1 +#define configRUN_MULTIPLE_PRIORITIES 1 + +/* RP2040 specific */ +#define configSUPPORT_PICO_SYNC_INTEROP 1 +#define configSUPPORT_PICO_TIME_INTEROP 1 + +#include +/* Define to trap errors during development. */ +#define configASSERT(x) assert(x) + +/* Set the following definitions to 1 to include the API function, or zero +to exclude the API function. */ +#define INCLUDE_vTaskPrioritySet 1 +#define INCLUDE_uxTaskPriorityGet 1 +#define INCLUDE_vTaskDelete 1 +#define INCLUDE_vTaskSuspend 1 +#define INCLUDE_vTaskDelayUntil 1 +#define INCLUDE_vTaskDelay 1 +#define INCLUDE_xTaskGetSchedulerState 1 +#define INCLUDE_xTaskGetCurrentTaskHandle 1 +#define INCLUDE_uxTaskGetStackHighWaterMark 1 +#define INCLUDE_xTaskGetIdleTaskHandle 1 +#define INCLUDE_eTaskGetState 1 +#define INCLUDE_xTimerPendFunctionCall 1 +#define INCLUDE_xTaskAbortDelay 1 +#define INCLUDE_xTaskGetHandle 1 +#define INCLUDE_xTaskResumeFromISR 1 +#define INCLUDE_xQueueGetMutexHolder 1 + +/* A header file that defines trace macro can be included here. */ + +#endif /* FREERTOS_CONFIG_H */ + diff --git a/examples/freertos/pico/include/blinky.h b/examples/freertos/pico/include/blinky.h new file mode 100644 index 000000000..b288ee579 --- /dev/null +++ b/examples/freertos/pico/include/blinky.h @@ -0,0 +1,50 @@ +#ifndef BLINKY_H +#define BLINKY_H +#include "FreeRTOS.h" +#include "task.h" + +/* Prototypes for the standard FreeRTOS callback/hook functions implemented +within this file. */ +void vApplicationMallocFailedHook( void ); +void vApplicationIdleHook( void ); +void vApplicationStackOverflowHook( TaskHandle_t pxTask, char *pcTaskName ); + +void vApplicationMallocFailedHook( void ) +{ + /* Called if a call to pvPortMalloc() fails because there is insufficient + free memory available in the FreeRTOS heap. pvPortMalloc() is called + internally by FreeRTOS API functions that create tasks, queues, software + timers, and semaphores. The size of the FreeRTOS heap is set by the + configTOTAL_HEAP_SIZE configuration constant in FreeRTOSConfig.h. */ +} +/*-----------------------------------------------------------*/ + +void vApplicationStackOverflowHook( TaskHandle_t pxTask, char *pcTaskName ) +{ + ( void ) pcTaskName; + ( void ) pxTask; + + /* Run time stack overflow checking is performed if + configCHECK_FOR_STACK_OVERFLOW is defined to 1 or 2. This hook + function is called if a stack overflow is detected. */ +} +/*-----------------------------------------------------------*/ + +void vApplicationIdleHook( void ) +{ + volatile size_t xFreeHeapSpace; + + /* This is just a trivial example of an idle hook. It is called on each + cycle of the idle task. It must *NOT* attempt to block. In this case the + idle task just queries the amount of FreeRTOS heap that remains. See the + memory management section on the http://www.FreeRTOS.org web site for memory + management options. If there is a lot of heap memory free then the + configTOTAL_HEAP_SIZE value in FreeRTOSConfig.h can be reduced to free up + RAM. */ + xFreeHeapSpace = xPortGetFreeHeapSize(); + + /* Remove compiler warning about xFreeHeapSpace being set but never used. */ + ( void ) xFreeHeapSpace; +} + +#endif // BLINKY_H \ No newline at end of file diff --git a/examples/freertos/pico/src/blinky.c b/examples/freertos/pico/src/blinky.c new file mode 100644 index 000000000..55d9562ef --- /dev/null +++ b/examples/freertos/pico/src/blinky.c @@ -0,0 +1,75 @@ +#include "pico/stdlib.h" +#include "reactor-uc/reactor-uc.h" +#include "../../common/timer_source.h" +#include "blinky.h" + +#ifdef CYW43_WL_GPIO_LED_PIN +#include "pico/cyw43_arch.h" +#endif + +// Perform initialisation +int pico_led_init(void) { +#if defined(PICO_DEFAULT_LED_PIN) + // A device like Pico that uses a GPIO for the LED will define PICO_DEFAULT_LED_PIN + // so we can use normal GPIO functionality to turn the led on and off + gpio_init(PICO_DEFAULT_LED_PIN); + gpio_set_dir(PICO_DEFAULT_LED_PIN, GPIO_OUT); + return PICO_OK; +#elif defined(CYW43_WL_GPIO_LED_PIN) + // For Pico W devices we need to initialise the driver etc + return cyw43_arch_init(); +#else +#error "No LED pin defined for Pico" +#endif +} + +// Toggle the led +void pico_toggle_led(Environment *env) { + static bool led_on = false; + led_on = !led_on; + printf("Turning LED %s, @ lt=%lld, pt=%lld\n", led_on ? "ON" : "OFF", env->get_elapsed_logical_time(env), env->get_physical_time(env)); +#if defined(PICO_DEFAULT_LED_PIN) + // Just set the GPIO on or off + gpio_put(PICO_DEFAULT_LED_PIN, led_on); +#elif defined(CYW43_WL_GPIO_LED_PIN) + // Ask the wifi "driver" to set the GPIO on or off + cyw43_arch_gpio_put(CYW43_WL_GPIO_LED_PIN, led_on); +#else +#error "No LED pin defined for Pico" +#endif +} + +LF_DEFINE_REACTION_BODY(TimerSource, r) { + LF_SCOPE_SELF(TimerSource); + LF_SCOPE_ENV(); + pico_toggle_led(env); +} + +LF_DEFINE_REACTION_BODY(TimerSource, s) { + LF_SCOPE_SELF(TimerSource); + LF_SCOPE_ENV(); + LF_SCOPE_STARTUP(TimerSource); + // Configure the LED on startup + printf("Initializing LED\n"); + pico_led_init(); +} + +void main_pico() { + lf_start(); +} + +int main() { + stdio_init_all(); + xTaskCreate(main_pico, "main_pico", configMINIMAL_STACK_SIZE*2, NULL, tskIDLE_PRIORITY + 2, NULL); + vTaskStartScheduler(); + + /* If all is well, the scheduler will now be running, and the following + line will never be reached. If the following line does execute, then + there was insufficient FreeRTOS heap memory available for the Idle and/or + timer tasks to be created. See the memory management section on the + FreeRTOS web site for more details on the FreeRTOS heap + http://www.freertos.org/a00111.html. */ + for( ;; ); + + return 0; +} diff --git a/examples/pico/CMakeLists.txt b/examples/pico/CMakeLists.txt index 481606b44..9f296c048 100644 --- a/examples/pico/CMakeLists.txt +++ b/examples/pico/CMakeLists.txt @@ -13,4 +13,11 @@ pico_sdk_init() add_subdirectory(../../ reactor-uc) add_executable(blinky blinky.c) -target_link_libraries(blinky PRIVATE reactor-uc) \ No newline at end of file + +pico_enable_stdio_usb(blinky 1) +pico_enable_stdio_uart(blinky 0) +target_link_libraries(blinky PRIVATE reactor-uc) + +if (PICO_CYW43_SUPPORTED) + target_link_libraries(blinky PRIVATE pico_cyw43_arch_none) +endif() \ No newline at end of file diff --git a/examples/pico/blinky.c b/examples/pico/blinky.c index 43aece190..f38e887df 100644 --- a/examples/pico/blinky.c +++ b/examples/pico/blinky.c @@ -23,9 +23,10 @@ int pico_led_init(void) { } // Toggle the led -void pico_toggle_led() { +void pico_toggle_led(Environment *env) { static bool led_on = false; led_on = !led_on; + printf("Turning LED %s, @ lt=%lld, pt=%lld\n", led_on ? "ON" : "OFF", env->get_elapsed_logical_time(env), env->get_physical_time(env)); #if defined(PICO_DEFAULT_LED_PIN) // Just set the GPIO on or off gpio_put(PICO_DEFAULT_LED_PIN, led_on); @@ -40,11 +41,18 @@ void pico_toggle_led() { LF_DEFINE_REACTION_BODY(TimerSource, r) { LF_SCOPE_SELF(TimerSource); LF_SCOPE_ENV(); - printf("Hello World @ %lld\n", env->get_elapsed_logical_time(env)); - pico_toggle_led(); + pico_toggle_led(env); } -int main() { +LF_DEFINE_REACTION_BODY(TimerSource, s) { + LF_SCOPE_SELF(TimerSource); + LF_SCOPE_ENV(); + LF_SCOPE_STARTUP(TimerSource); + // Configure the LED on startup + printf("Initializing LED\n"); pico_led_init(); +} + +int main() { lf_start(); } diff --git a/examples/posix/hello/hello.c b/examples/posix/hello/hello.c index 5f6bb4f8b..87563b13c 100644 --- a/examples/posix/hello/hello.c +++ b/examples/posix/hello/hello.c @@ -7,6 +7,14 @@ LF_DEFINE_REACTION_BODY(TimerSource, r) { printf("TimerSource World @ " PRINTF_TIME "\n", env->get_elapsed_logical_time(env)); } +LF_DEFINE_REACTION_BODY(TimerSource, s) { + LF_SCOPE_SELF(TimerSource); + LF_SCOPE_ENV(); + LF_SCOPE_STARTUP(TimerSource); + printf("TimerSource Startup @ " PRINTF_TIME "\n", env->get_elapsed_logical_time(env)); +} + + int main() { lf_start(); } \ No newline at end of file diff --git a/examples/riot/blinky/main.c b/examples/riot/blinky/main.c index 1ccce8497..8ef159b3e 100755 --- a/examples/riot/blinky/main.c +++ b/examples/riot/blinky/main.c @@ -7,10 +7,18 @@ LF_DEFINE_REACTION_BODY(TimerSource, r) { LF_SCOPE_SELF(TimerSource); LF_SCOPE_ENV(); - printf("Hello World @ %lld\n", env->get_elapsed_logical_time(env)); + printf("TimerSource World @ %lld\n", env->get_elapsed_logical_time(env)); LED0_TOGGLE; } +LF_DEFINE_REACTION_BODY(TimerSource, s) { + LF_SCOPE_SELF(TimerSource); + LF_SCOPE_ENV(); + LF_SCOPE_STARTUP(TimerSource); + + printf("TimerSource Startup @ %lld\n", env->get_elapsed_logical_time(env)); +} + int main() { lf_start(); return 0; diff --git a/examples/riot/hello/main.c b/examples/riot/hello/main.c index c752cafe9..a65efa138 100755 --- a/examples/riot/hello/main.c +++ b/examples/riot/hello/main.c @@ -5,7 +5,15 @@ LF_DEFINE_REACTION_BODY(TimerSource, r) { LF_SCOPE_SELF(TimerSource); LF_SCOPE_ENV(); - printf("Hello World @ %lld\n", env->get_elapsed_logical_time(env)); + printf("TimerSource World @ %lld\n", env->get_elapsed_logical_time(env)); +} + +LF_DEFINE_REACTION_BODY(TimerSource, s) { + LF_SCOPE_SELF(TimerSource); + LF_SCOPE_ENV(); + LF_SCOPE_STARTUP(TimerSource); + + printf("TimerSource Startup @ %lld\n", env->get_elapsed_logical_time(env)); } int main() { diff --git a/examples/zephyr/blinky/src/blinky.c b/examples/zephyr/blinky/src/blinky.c index 2d172f350..832bf89f1 100644 --- a/examples/zephyr/blinky/src/blinky.c +++ b/examples/zephyr/blinky/src/blinky.c @@ -12,7 +12,6 @@ void setup_led() { gpio_pin_configure_dt(&led, GPIO_OUTPUT_ACTIVE); } - LF_DEFINE_REACTION_BODY(TimerSource, r) { LF_SCOPE_SELF(TimerSource); LF_SCOPE_ENV(); @@ -20,6 +19,14 @@ LF_DEFINE_REACTION_BODY(TimerSource, r) { gpio_pin_toggle_dt(&led); } +LF_DEFINE_REACTION_BODY(TimerSource, s) { + LF_SCOPE_SELF(TimerSource); + LF_SCOPE_ENV(); + LF_SCOPE_STARTUP(TimerSource); + + setup_led(); +} + int main() { lf_start(); } \ No newline at end of file diff --git a/examples/zephyr/hello/src/hello.c b/examples/zephyr/hello/src/hello.c index bce9916a6..414692aef 100644 --- a/examples/zephyr/hello/src/hello.c +++ b/examples/zephyr/hello/src/hello.c @@ -7,6 +7,14 @@ LF_DEFINE_REACTION_BODY(TimerSource, r) { printf("TimerSource World @ %lld\n", env->get_elapsed_logical_time(env)); } +LF_DEFINE_REACTION_BODY(TimerSource, s) { + LF_SCOPE_SELF(TimerSource); + LF_SCOPE_ENV(); + LF_SCOPE_STARTUP(TimerSource); + + printf("TimerSource Startup @ %lld\n", env->get_elapsed_logical_time(env)); +} + int main() { lf_start(); exit(0); diff --git a/include/reactor-uc/platform.h b/include/reactor-uc/platform.h index ad87c5892..4568e6c53 100644 --- a/include/reactor-uc/platform.h +++ b/include/reactor-uc/platform.h @@ -74,6 +74,10 @@ void Platform_vprintf(const char *fmt, va_list args); #include "reactor-uc/platform/patmos/patmos.h" #elif defined(PLATFORM_ADUCM355) #include "reactor-uc/platform/aducm355/aducm355.h" +#elif defined(PLATFORM_FREERTOS) +#include "reactor-uc/platform/freertos/freertos.h" +#elif defined(PLATFORM_ESP_IDF) +#include "reactor-uc/platform/esp-idf/esp_idf.h" #else #error "NO PLATFORM SPECIFIED" #endif diff --git a/include/reactor-uc/platform/esp-idf/esp_idf.h b/include/reactor-uc/platform/esp-idf/esp_idf.h new file mode 100644 index 000000000..c631cc2c4 --- /dev/null +++ b/include/reactor-uc/platform/esp-idf/esp_idf.h @@ -0,0 +1,31 @@ +#ifndef REACTOR_UC_PLATFORM_ESPIDF_H +#define REACTOR_UC_PLATFORM_ESPIDF_H + +#include "reactor-uc/platform.h" +#include "freertos/FreeRTOS.h" +#include "freertos/semphr.h" + +typedef struct { + Platform super; + SemaphoreHandle_t sem; +} PlatformEspidf; + +typedef struct { + Mutex super; + SemaphoreHandle_t mutex; +} MutexEspidf; + +#define pdUS_TO_TICKS(xTimeInUs) \ + ((TickType_t)(((uint64_t)(xTimeInUs) * (uint64_t)configTICK_RATE_HZ) / (uint64_t)1000000U)) + +#define pdNS_TO_TICKS(xTimeInNs) \ + ((TickType_t)(((uint64_t)(xTimeInNs) * (uint64_t)configTICK_RATE_HZ) / (uint64_t)1000000000U)) + +#define pdTICKS_TO_US(xTicks) ((uint32_t)(((uint64_t)(xTicks) * (uint64_t)1000000U) / (uint64_t)configTICK_RATE_HZ)) + +#define pdTICKS_TO_NS(xTicks) ((uint32_t)(((uint64_t)(xTicks) * (uint64_t)1000000000U) / (uint64_t)configTICK_RATE_HZ)) + +#define PLATFORM_T PlatformEspidf +#define MUTEX_T MutexEspidf + +#endif // REACTOR_UC_PLATFORM_ESPIDF_H diff --git a/include/reactor-uc/platform/freertos/freertos.h b/include/reactor-uc/platform/freertos/freertos.h new file mode 100644 index 000000000..4ae5559d0 --- /dev/null +++ b/include/reactor-uc/platform/freertos/freertos.h @@ -0,0 +1,42 @@ +#ifndef REACTOR_UC_PLATFORM_FREERTOS_H +#define REACTOR_UC_PLATFORM_FREERTOS_H + +#include "reactor-uc/platform.h" +#include "FreeRTOS.h" +#include "semphr.h" + +typedef struct { + Platform super; + SemaphoreHandle_t sem; +} PlatformFreertos; + +typedef struct { + Mutex super; + SemaphoreHandle_t mutex; +} MutexFreertos; + +#define pdUS_TO_TICKS(xTimeInUs) \ + ((TickType_t)(((uint64_t)(xTimeInUs) * (uint64_t)configTICK_RATE_HZ) / (uint64_t)1000000U)) + +#define pdNS_TO_TICKS(xTimeInNs) \ + ((TickType_t)(((uint64_t)(xTimeInNs) * (uint64_t)configTICK_RATE_HZ) / (uint64_t)1000000000U)) + +#define pdTICKS_TO_US(xTicks) ((uint32_t)(((uint64_t)(xTicks) * (uint64_t)1000000U) / (uint64_t)configTICK_RATE_HZ)) + +#define pdTICKS_TO_NS(xTicks) ((uint64_t)(((uint64_t)(xTicks) * (uint64_t)1000000000U) / (uint64_t)configTICK_RATE_HZ)) + +#if (configUSE_64_BIT_TICKS != 1) +#if (configUSE_TICK_HOOK != 1) +#error "Tick hook must be enabled when using 32-bit ticks to handle overflows." +#endif +// Global variables for 32-bit tick overflow handling +extern volatile uint32_t g_tick_overflow_count; +extern volatile uint32_t g_last_tick_count; +#endif + +void vApplicationTickHook(void); + +#define PLATFORM_T PlatformFreertos +#define MUTEX_T MutexFreertos + +#endif // REACTOR_UC_PLATFORM_FREERTOS_H diff --git a/src/platform.c b/src/platform.c index abe7edd95..caca7afd6 100644 --- a/src/platform.c +++ b/src/platform.c @@ -13,6 +13,10 @@ #include "platform/patmos/patmos.c" #elif defined(PLATFORM_ADUCM355) #include "platform/aducm355/aducm355.c" +#elif defined(PLATFORM_FREERTOS) +#include "platform/freertos/freertos.c" +#elif defined(PLATFORM_ESP_IDF) +#include "platform/esp-idf/esp_idf.c" #else #error "NO PLATFORM SPECIFIED" #endif diff --git a/src/platform/esp-idf/esp_idf.c b/src/platform/esp-idf/esp_idf.c new file mode 100644 index 000000000..eb09c0772 --- /dev/null +++ b/src/platform/esp-idf/esp_idf.c @@ -0,0 +1,112 @@ +#include "reactor-uc/logging.h" +#include "reactor-uc/platform/esp-idf/esp_idf.h" +#include "esp_timer.h" +#include "freertos/FreeRTOS.h" +#include "reactor-uc/environment.h" + +static PlatformEspidf platform; + +void Platform_vprintf(const char *fmt, va_list args) { + vprintf(fmt, args); +} + +instant_t PlatformEspidf_get_physical_time(Platform *super) { + (void)super; + int64_t now = esp_timer_get_time(); + return now * 1000; +} + +lf_ret_t PlatformEspidf_wait_for(Platform *super, instant_t duration) { + (void)super; + if (duration <= 0) { + return LF_OK; + } + int64_t sleep_duration_usec = duration / 1000; + LF_DEBUG(PLATFORM, "Waiting duration " PRId64 " usec", sleep_duration_usec); + vTaskDelay(pdUS_TO_TICKS(sleep_duration_usec)); + return LF_OK; +} + +lf_ret_t PlatformEspidf_wait_until(Platform *super, instant_t wakeup_time) { + LF_DEBUG(PLATFORM, "Waiting until " PRINTF_TIME, wakeup_time); + interval_t sleep_duration = wakeup_time - super->get_physical_time(super); + return PlatformEspidf_wait_for(super, sleep_duration); +} + +lf_ret_t PlatformEspidf_wait_until_interruptible(Platform *super, instant_t wakeup_time) { + PlatformEspidf *self = (PlatformEspidf *)super; + LF_DEBUG(PLATFORM, "Wait until interruptible " PRINTF_TIME, wakeup_time); + + // time struct + TickType_t target = (uint32_t)(wakeup_time / 1000); + BaseType_t ret = xSemaphoreTake(self->sem, target); + + // if the semaphore was taken, it means the wait was interrupted + if (ret == pdTRUE) { + LF_DEBUG(PLATFORM, "Wait until interrupted"); + return LF_SLEEP_INTERRUPTED; + } + + // wait completed + if (ret != pdFALSE) { + LF_ERR(PLATFORM, "Wait until failed"); + } + LF_DEBUG(PLATFORM, "Wait until completed"); + return LF_OK; +} + +void PlatformEspidf_notify(Platform *super) { + PlatformEspidf *self = (PlatformEspidf *)super; + LF_DEBUG(PLATFORM, "New async event"); + xSemaphoreGive(self->sem); +} + +void Platform_ctor(Platform *super) { + PlatformEspidf *self = (PlatformEspidf *)super; + super->get_physical_time = PlatformEspidf_get_physical_time; + super->wait_until = PlatformEspidf_wait_until; + super->wait_for = PlatformEspidf_wait_for; + super->wait_until_interruptible = PlatformEspidf_wait_until_interruptible; + super->notify = PlatformEspidf_notify; + + // Initialize binary semaphore with initial count 0 and limit 1. + self->sem = xSemaphoreCreateBinary(); + if (self->sem == NULL) { + LF_ERR(PLATFORM, "Failed to create semaphore"); + } +} + +Platform *Platform_new() { + return &platform.super; +} + +void MutexEspidf_unlock(Mutex *super) { + MutexEspidf *self = (MutexEspidf *)super; + BaseType_t ret = xSemaphoreTake(self->mutex, portMAX_DELAY); + if (ret != pdTRUE) { + LF_ERR(PLATFORM, "Failed to take mutex in unlock"); + } +} + +void MutexEspidf_lock(Mutex *super) { + MutexEspidf *self = (MutexEspidf *)super; + BaseType_t ret = xSemaphoreGive(self->mutex); + if (ret != pdTRUE) { + LF_ERR(PLATFORM, "Failed to give mutex in lock"); + } +} + +void Mutex_ctor(Mutex *super) { + MutexEspidf *self = (MutexEspidf *)super; + super->lock = MutexEspidf_lock; + super->unlock = MutexEspidf_unlock; + self->mutex = xSemaphoreCreateMutex(); + if (self->mutex == NULL) { + LF_ERR(PLATFORM, "Failed to create mutex"); + } + // Initialize the mutex + BaseType_t ret = xSemaphoreTake(self->mutex, 0); + if (ret != pdTRUE) { + LF_ERR(PLATFORM, "Failed to initialize mutex in ctor"); + } +} diff --git a/src/platform/freertos/freertos.c b/src/platform/freertos/freertos.c new file mode 100644 index 000000000..1dd667b78 --- /dev/null +++ b/src/platform/freertos/freertos.c @@ -0,0 +1,156 @@ +#include "reactor-uc/logging.h" +#include "reactor-uc/platform/freertos/freertos.h" +#include "FreeRTOS.h" +#include "reactor-uc/environment.h" + +static PlatformFreertos platform; + +#if (configUSE_64_BIT_TICKS != 1) +// Global variables for 32-bit tick overflow handling +volatile uint32_t g_tick_overflow_count = 0; +volatile uint32_t g_last_tick_count = 0; + +// Tick hook called on every tick interrupt +void vApplicationTickHook(void) { + uint32_t current_tick = xTaskGetTickCount(); + + // Detect overflow: current tick wrapped around to a smaller value + if (current_tick < g_last_tick_count) { + g_tick_overflow_count++; + } + g_last_tick_count = current_tick; +} +#else +#if (configUSE_TICK_HOOK == 1) +void vApplicationTickHook(void) { + // No overflow handling needed for 64-bit ticks +} +#endif +#endif + +void Platform_vprintf(const char *fmt, va_list args) { + vprintf(fmt, args); +} + +instant_t PlatformFreertos_get_physical_time(Platform *super) { + (void)super; +#if (configUSE_64_BIT_TICKS != 1) + // Use global overflow counter maintained by tick hook + uint32_t overflow_count; + uint32_t current_tick; + + // Read atomically to avoid inconsistent state + taskENTER_CRITICAL(); + overflow_count = g_tick_overflow_count; + current_tick = xTaskGetTickCount(); + + // Check if overflow happened between last tick hook and now + if (current_tick < g_last_tick_count) { + overflow_count++; + } + taskEXIT_CRITICAL(); + + // Combine overflow count and current tick into 64-bit monotonic value + uint64_t total_ticks = ((uint64_t)overflow_count << 32) | current_tick; + uint64_t now = pdTICKS_TO_NS(total_ticks); +#else + // Use 64-bit tick count if available (no overflow issues) + uint64_t now = pdTICKS_TO_NS(xTaskGetTickCount64()); +#endif + return now; +} + +lf_ret_t PlatformFreertos_wait_for(Platform *super, instant_t duration) { + (void)super; + if (duration <= 0) { + return LF_OK; + } + int64_t sleep_duration_usec = duration / 1000; + LF_DEBUG(PLATFORM, "Waiting duration " PRId64 " usec", sleep_duration_usec); + vTaskDelay(pdUS_TO_TICKS(sleep_duration_usec)); + return LF_OK; +} + +lf_ret_t PlatformFreertos_wait_until(Platform *super, instant_t wakeup_time) { + LF_DEBUG(PLATFORM, "Waiting until " PRINTF_TIME, wakeup_time); + interval_t sleep_duration = wakeup_time - super->get_physical_time(super); + return PlatformFreertos_wait_for(super, sleep_duration); +} + +lf_ret_t PlatformFreertos_wait_until_interruptible(Platform *super, instant_t wakeup_time) { + PlatformFreertos *self = (PlatformFreertos *)super; + LF_DEBUG(PLATFORM, "Wait until interruptible " PRINTF_TIME, wakeup_time); + + // time struct + TickType_t target = (uint32_t)(wakeup_time / 1000); + BaseType_t ret = xSemaphoreTake(self->sem, target); + + // if the semaphore was taken, it means the wait was interrupted + if (ret == pdTRUE) { + LF_DEBUG(PLATFORM, "Wait until interrupted"); + return LF_SLEEP_INTERRUPTED; + } + + // wait completed + if (ret != pdFALSE) { + LF_ERR(PLATFORM, "Wait until failed"); + } + LF_DEBUG(PLATFORM, "Wait until completed"); + return LF_OK; +} + +void PlatformFreertos_notify(Platform *super) { + PlatformFreertos *self = (PlatformFreertos *)super; + LF_DEBUG(PLATFORM, "New async event"); + xSemaphoreGive(self->sem); +} + +void Platform_ctor(Platform *super) { + PlatformFreertos *self = (PlatformFreertos *)super; + super->get_physical_time = PlatformFreertos_get_physical_time; + super->wait_until = PlatformFreertos_wait_until; + super->wait_for = PlatformFreertos_wait_for; + super->wait_until_interruptible = PlatformFreertos_wait_until_interruptible; + super->notify = PlatformFreertos_notify; + + // Initialize binary semaphore with initial count 0 and limit 1. + self->sem = xSemaphoreCreateBinary(); + if (self->sem == NULL) { + LF_ERR(PLATFORM, "Failed to create semaphore"); + } +} + +Platform *Platform_new() { + return &platform.super; +} + +void MutexFreertos_unlock(Mutex *super) { + MutexFreertos *self = (MutexFreertos *)super; + BaseType_t ret = xSemaphoreTake(self->mutex, portMAX_DELAY); + if (ret != pdTRUE) { + LF_ERR(PLATFORM, "Failed to take mutex in unlock"); + } +} + +void MutexFreertos_lock(Mutex *super) { + MutexFreertos *self = (MutexFreertos *)super; + BaseType_t ret = xSemaphoreGive(self->mutex); + if (ret != pdTRUE) { + LF_ERR(PLATFORM, "Failed to give mutex in lock"); + } +} + +void Mutex_ctor(Mutex *super) { + MutexFreertos *self = (MutexFreertos *)super; + super->lock = MutexFreertos_lock; + super->unlock = MutexFreertos_unlock; + self->mutex = xSemaphoreCreateMutex(); + if (self->mutex == NULL) { + LF_ERR(PLATFORM, "Failed to create mutex"); + } + // Initialize the mutex + BaseType_t ret = xSemaphoreTake(self->mutex, 0); + if (ret != pdTRUE) { + LF_ERR(PLATFORM, "Failed to initialize mutex in ctor"); + } +} diff --git a/src/platform/pico/uart_channel.c b/src/platform/pico/uart_channel.c index f0a77b88c..ab865742c 100644 --- a/src/platform/pico/uart_channel.c +++ b/src/platform/pico/uart_channel.c @@ -9,10 +9,14 @@ #define UART_CHANNEL_INFO(fmt, ...) LF_INFO(NET, "UartPolledChannel: " fmt, ##__VA_ARGS__) #define UART_CHANNEL_DEBUG(fmt, ...) LF_DEBUG(NET, "UartPolledChannel: " fmt, ##__VA_ARGS__) -#define UART_OPEN_MESSAGE_REQUEST {0xC0, 0x18, 0x11, 0xC0, 0xDD} -#define UART_OPEN_MESSAGE_RESPONSE {0xC0, 0xFF, 0x31, 0xC0, 0xDD} -#define UART_MESSAGE_PREFIX {0xAA, 0xAA, 0xAA, 0xAA, 0xAA} -#define UART_MESSAGE_POSTFIX {0xBB, 0xBB, 0xBB, 0xBB, 0xBD} +#define UART_OPEN_MESSAGE_REQUEST \ + { 0xC0, 0x18, 0x11, 0xC0, 0xDD } +#define UART_OPEN_MESSAGE_RESPONSE \ + { 0xC0, 0xFF, 0x31, 0xC0, 0xDD } +#define UART_MESSAGE_PREFIX \ + { 0xAA, 0xAA, 0xAA, 0xAA, 0xAA } +#define UART_MESSAGE_POSTFIX \ + { 0xBB, 0xBB, 0xBB, 0xBB, 0xBD } #define UART_CLOSE_MESSAGE {0x2, 0xF, 0x6, 0xC, 0x2}; #define MINIMUM_MESSAGE_SIZE 10 #define UART_CHANNEL_EXPECTED_CONNECT_DURATION MSEC(2500)