diff --git a/core/CMakeLists.txt b/core/CMakeLists.txt index 6d938ae0c..6c7f0ffe7 100644 --- a/core/CMakeLists.txt +++ b/core/CMakeLists.txt @@ -184,3 +184,4 @@ define(LF_PACKAGE_DIRECTORY) define(LF_FILE_SEPARATOR) define(WORKERS_NEEDED_FOR_FEDERATE) define(LF_ENCLAVES) + diff --git a/lingua-franca-ref.txt b/lingua-franca-ref.txt index edf165c42..8b25206ff 100644 --- a/lingua-franca-ref.txt +++ b/lingua-franca-ref.txt @@ -1 +1 @@ -fix-concurrency \ No newline at end of file +master \ No newline at end of file diff --git a/low_level_platform/api/CMakeLists.txt b/low_level_platform/api/CMakeLists.txt index 599f87e59..4092f6411 100644 --- a/low_level_platform/api/CMakeLists.txt +++ b/low_level_platform/api/CMakeLists.txt @@ -14,6 +14,8 @@ elseif(${CMAKE_SYSTEM_NAME} STREQUAL "Rp2040") target_link_libraries(lf-low-level-platform-api INTERFACE pico_stdlib) target_link_libraries(lf-low-level-platform-api INTERFACE pico_multicore) target_link_libraries(lf-low-level-platform-api INTERFACE pico_sync) +elseif(${CMAKE_SYSTEM_NAME} STREQUAL "STM32F4") + target_compile_definitions(lf-low-level-platform-api INTERFACE PLATFORM_STM32F4) elseif(${CMAKE_SYSTEM_NAME} STREQUAL "FlexPRET") target_compile_definitions(lf-low-level-platform-api INTERFACE PLATFORM_FLEXPRET) target_link_libraries(lf-low-level-platform-api INTERFACE fp-sdk) diff --git a/low_level_platform/api/low_level_platform.h b/low_level_platform/api/low_level_platform.h index afffd2a9e..1b05e4073 100644 --- a/low_level_platform/api/low_level_platform.h +++ b/low_level_platform/api/low_level_platform.h @@ -52,6 +52,8 @@ int lf_critical_section_exit(environment_t* env); #include "platform/lf_patmos_support.h" #elif defined(PLATFORM_RP2040) #include "platform/lf_rp2040_support.h" +#elif defined(PLATFORM_STM32F4) +#include "platform/lf_STM32f4_support.h" #elif defined(PLATFORM_FLEXPRET) #include "platform/lf_flexpret_support.h" #elif defined(WIN32) || defined(_WIN32) || defined(__WIN32__) || defined(__NT__) diff --git a/low_level_platform/api/platform/lf_STM32f4_support.h b/low_level_platform/api/platform/lf_STM32f4_support.h new file mode 100644 index 000000000..19ff2cfa7 --- /dev/null +++ b/low_level_platform/api/platform/lf_STM32f4_support.h @@ -0,0 +1,20 @@ +/* STM32 API support for the C target of Lingua Franca. */ + +#ifndef LF_STM32F4_SUPPORT_H +#define LF_STM32F4_SUPPORT_H + +#define NO_TTY + +#include + +#define PRINTF_TAG "(" PRINTF_TIME ", " PRINTF_MICROSTEP ")" +#define PRINTF_TIME "%lld" +#define PRINTF_MICROSTEP "%d" + +#define _LF_TIMEOUT 1 + +#ifdef LF_THREADED +#error "Threaded runtime not supported on STM32" +#endif // LF_THREADED + +#endif \ No newline at end of file diff --git a/low_level_platform/impl/CMakeLists.txt b/low_level_platform/impl/CMakeLists.txt index ac035dcf4..32cd760c2 100644 --- a/low_level_platform/impl/CMakeLists.txt +++ b/low_level_platform/impl/CMakeLists.txt @@ -39,6 +39,11 @@ elseif(${CMAKE_SYSTEM_NAME} STREQUAL "Rp2040") ${CMAKE_CURRENT_LIST_DIR}/src/lf_rp2040_support.c ${CMAKE_CURRENT_LIST_DIR}/src/lf_atomic_irq.c ) +elseif(${CMAKE_SYSTEM_NAME} STREQUAL "STM32F4") + set(LF_LOW_LEVEL_PLATFORM_FILES + ${CMAKE_CURRENT_LIST_DIR}/src/lf_STM32f4_support.c + ${CMAKE_CURRENT_LIST_DIR}/src/lf_atomic_irq.c + ) elseif(${CMAKE_SYSTEM_NAME} STREQUAL "FlexPRET") set(LF_LOW_LEVEL_PLATFORM_FILES ${CMAKE_CURRENT_LIST_DIR}/src/lf_flexpret_support.c @@ -50,7 +55,7 @@ elseif(${CMAKE_SYSTEM_NAME} STREQUAL "Patmos") ${CMAKE_CURRENT_LIST_DIR}/src/lf_atomic_irq.c ) else() - message(FATAL_ERROR "Your platform is not supported! The C target supports FlexPRET, Patmos, Linux, MacOS, nRF52, RP2040, Windows, and Zephyr.") + message(FATAL_ERROR "Your platform is not supported! The C target supports FlexPRET, Patmos, Linux, MacOS, nRF52, RP2040, STM32F4, Windows, and Zephyr.") endif() list(APPEND LF_LOW_LEVEL_PLATFORM_FILES ${CMAKE_CURRENT_LIST_DIR}/src/lf_platform_util.c) diff --git a/low_level_platform/impl/src/lf_STM32f4_support.c b/low_level_platform/impl/src/lf_STM32f4_support.c new file mode 100644 index 000000000..2cc8ba5a2 --- /dev/null +++ b/low_level_platform/impl/src/lf_STM32f4_support.c @@ -0,0 +1,224 @@ +#if defined(PLATFORM_STM32F4) + +#include "platform/lf_STM32f4_support.h" +#include "low_level_platform.h" +#include "tag.h" + +static volatile bool _lf_sleep_interrupted = false; +static volatile bool _lf_async_event = false; + +// nested critical section counter +static uint32_t _lf_num_nested_crit_sec = 0; + +// Timer upper half (for overflow) +static uint32_t _lf_time_us_high = 0; + +#define LF_MIN_SLEEP_NS 10 + +// Combine 2 32bit works to a 64 bit word (Takes from nrf52 support) +#define COMBINE_HI_LO(hi, lo) ((((uint64_t)hi) << 32) | ((uint64_t)lo)) + +void lf_SystemClock_Config(); +void Error_Handler(); + +TIM_HandleTypeDef htim5; + +void _lf_initialize_clock(void) { + // Standard initializations from generated code + HAL_Init(); + lf_SystemClock_Config(); + + // Configure TIM5 as our 32-bit clock timer + __HAL_RCC_TIM5_CLK_ENABLE(); // initialize counter + TIM5->CR1 = TIM_CR1_CEN; // enable counter + + // set prescaler to (16 - 1) = 15 + // CPU runs a 16MHz so timer ticks at 1MHz + // Which means period of 1 microsecond + TIM5->PSC = 15; + + // Setup ISR to increment upper bits + TIM5->DIER |= TIM_DIER_CC1IE; + NVIC_EnableIRQ(TIM5_IRQn); + + /* This is to make the Prescaler actually work + * For some reason, the Prescaler only kicks in once the timer has reset once. + * Thus, the current solution is to manually ret the value to a really large + * and force it to reset once. Its really jank but its the only way I could + * find to make it work + */ + TIM5->CNT = 0xFFFFFFFE; +} + +// ISR for handling timer overflow -> We increment the upper timer +void TIM5_IRQHandler(void) { + if (TIM5->SR & TIM_FLAG_CC1) { + TIM5->SR &= ~TIM_FLAG_CC1; + _lf_time_us_high += 1; + } +} + +/** + * Write the time since boot into time variable + */ +int _lf_clock_gettime(instant_t* t) { + // Timer is cooked + if (!t) { + return 1; + } + + // Get the current microseconds from TIM5 + uint32_t now_us_hi_pre = _lf_time_us_high; + uint32_t now_us_low = TIM5->CNT; + uint32_t now_us_hi_post = _lf_time_us_high; + + // Check if we read the time during a wrap + if (now_us_hi_pre != now_us_hi_post) { + // There was a wrap. read again and return + now_us_low = TIM5->CNT; + } + + // Combine upper and lower timers + uint64_t now_us = COMBINE_HI_LO((now_us_hi_post - 1), now_us_low); + *t = ((instant_t)now_us) * 1000; + return 0; +} + +/** + * Make the STM32 sleep for set nanoseconds + * Based on the lf_nrf52 support implementation + */ +int lf_sleep(interval_t sleep_duration) { + instant_t target_time; + instant_t current_time; + + _lf_clock_gettime(¤t_time); + target_time = current_time + sleep_duration; + // HAL_Delay only supports miliseconds. We try to use that for as long as possible + // before switching to another meothd for finer tuned delay times + long delaytime_ms = sleep_duration / 1000000; + HAL_Delay(delaytime_ms); + + while (current_time <= target_time) + _lf_clock_gettime(¤t_time); + + return 0; +} + +/* sleep until wakeup time but wake up if there is an async event + */ +int _lf_interruptable_sleep_until_locked(environment_t* env, instant_t wakeup_time) { + // Get the current time and sleep time + instant_t now; + _lf_clock_gettime(&now); + interval_t duration = wakeup_time - now; + + // Edge case handling for super small duration + if (duration <= 0) { + return 0; + } else if (duration < LF_MIN_SLEEP_NS) { + instant_t current_time; + _lf_clock_gettime(¤t_time); + + // We repurpose the lf_sleep function here, just to better streamline the code + interval_t sleep_duration = wakeup_time - current_time; + lf_sleep(sleep_duration); + return 0; + } + + // Enable interrupts and prepare to wait + _lf_async_event = false; + lf_enable_interrupts_nested(); + + do { + _lf_clock_gettime(&now); + // Exit when the timer is up or there is an exception + } while (!_lf_async_event && (now < wakeup_time)); + + // Disable interrupts again on exit + lf_disable_interrupts_nested(); + + if (!_lf_async_event) { + return 0; + } else { + return 1; + } +} + +// disables the IRQ with support for nested disabling +int lf_disable_interrupts_nested() { + // Disable interrupts if they are currently enabled + if (_lf_num_nested_crit_sec == 0) { + __disable_irq(); + } + + // update the depth of disabling interrupts + _lf_num_nested_crit_sec++; + return 0; +} + +// enables the IRQ (checks if other processes still want it disabled first) +int lf_enable_interrupts_nested() { + // Left the critical section more often than it was entered. + + if (_lf_num_nested_crit_sec <= 0) { + return 1; + } + + // update the depth of disabling interrupts + _lf_num_nested_crit_sec--; + + // We enable interrupts again + if (_lf_num_nested_crit_sec == 0) { + __enable_irq(); + } + return 0; +} + +int _lf_single_threaded_notify_of_event() { + _lf_async_event = true; + return 0; +} + +// The following functions are copied from the STM32F4 HAL library +void lf_SystemClock_Config(void) { + RCC_OscInitTypeDef RCC_OscInitStruct = {0}; + RCC_ClkInitTypeDef RCC_ClkInitStruct = {0}; + + /** Configure the main internal regulator output voltage + */ + __HAL_RCC_PWR_CLK_ENABLE(); + __HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE3); + + /** Initializes the RCC Oscillators according to the specified parameters + * in the RCC_OscInitTypeDef structure. + */ + RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSI; + RCC_OscInitStruct.HSIState = RCC_HSI_ON; + RCC_OscInitStruct.HSICalibrationValue = RCC_HSICALIBRATION_DEFAULT; + RCC_OscInitStruct.PLL.PLLState = RCC_PLL_NONE; + if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK) { + Error_Handler(); + } + + /** Initializes the CPU, AHB and APB buses clocks + */ + RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK | RCC_CLOCKTYPE_SYSCLK | RCC_CLOCKTYPE_PCLK1 | RCC_CLOCKTYPE_PCLK2; + RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_HSI; + RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1; + RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV1; + RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1; + + if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_0) != HAL_OK) { + Error_Handler(); + } +} + +// Called upon error like a hardfault. +void Error_Handler(void) { + __disable_irq(); + while (1) { + } +} + +#endif \ No newline at end of file diff --git a/low_level_platform/impl/src/lf_atomic_irq.c b/low_level_platform/impl/src/lf_atomic_irq.c index 7d78ab445..9bcf6bb0f 100644 --- a/low_level_platform/impl/src/lf_atomic_irq.c +++ b/low_level_platform/impl/src/lf_atomic_irq.c @@ -1,5 +1,5 @@ #if defined(PLATFORM_ARDUINO) || defined(PLATFORM_NRF52) || defined(PLATFORM_ZEPHYR) || defined(PLATFORM_RP2040) || \ - defined(PLATFORM_FLEXPRET) || defined(PLATFORM_PATMOS) + defined(PLATFORM_FLEXPRET) || defined(PLATFORM_PATMOS) || defined(PLATFORM_STM32F4) /** * @author Erling Rennemo Jellum * @copyright (c) 2023