Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support for STM32 #302

Open
wants to merge 42 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 9 commits
Commits
Show all changes
42 commits
Select commit Hold shift + click to select a range
6de22f6
initial test for support for STM32
naichenzhao Nov 7, 2023
256abcc
Point to lingua-franca stm32 branch
lhstrh Nov 7, 2023
92fcfbe
fixed typo
naichenzhao Nov 11, 2023
dca5be8
please merge
naichenzhao Nov 11, 2023
89986a9
hard coded STM32 support
naichenzhao Nov 11, 2023
436b36b
we dont need STM_main anymore
naichenzhao Nov 19, 2023
bcbbf43
updated files to move stuff out of core cmake
naichenzhao Nov 22, 2023
48be057
fixed printing
naichenzhao Dec 7, 2023
31d62b2
changed naming
naichenzhao Dec 18, 2023
73aea2e
updated sleep functions
naichenzhao Jul 15, 2024
7899e6b
updated delay functions
naichenzhao Jul 15, 2024
d11f3ff
rebased in main
naichenzhao Aug 5, 2024
58d2323
change
naichenzhao Aug 5, 2024
5ae3be6
fixed typo
naichenzhao Nov 11, 2023
4bb9231
bruh
naichenzhao Aug 5, 2024
9527537
we dont need STM_main anymore
naichenzhao Nov 19, 2023
8a97bcd
changes
naichenzhao Aug 5, 2024
c46fbcc
fixed printing
naichenzhao Dec 7, 2023
b3097a8
changed naming
naichenzhao Dec 18, 2023
f92307f
updated sleep functions
naichenzhao Jul 15, 2024
f2554dd
updated delay functions
naichenzhao Jul 15, 2024
03124a5
merge
naichenzhao Aug 5, 2024
6d2964c
Merge branch 'stm32' of github.com:lf-lang/reactor-c into stm32
naichenzhao Aug 5, 2024
13ca0d8
Merge branch 'main' of github.com:lf-lang/reactor-c into stm32
naichenzhao Aug 31, 2024
cd0c9c1
quick commit
naichenzhao Aug 31, 2024
6d8d4db
Suppress unused parameter error
edwardalee Aug 9, 2024
bb75db6
Suppress unused parameter error
edwardalee Aug 9, 2024
6a66f47
deleted artifact from rebase
naichenzhao Aug 31, 2024
a285e52
works with newest version
naichenzhao Aug 31, 2024
6ba0f42
did formatting
naichenzhao Sep 7, 2024
a22a4fd
Update low_level_platform/api/platform/lf_STM32f4_support.h
naichenzhao Sep 16, 2024
42fc271
Update low_level_platform/api/platform/lf_STM32f4_support.h
naichenzhao Sep 16, 2024
895a8fb
Update low_level_platform/impl/src/lf_STM32f4_support.c
naichenzhao Sep 16, 2024
0d3af6d
Update low_level_platform/api/platform/lf_STM32f4_support.h
naichenzhao Sep 16, 2024
50603d7
Update low_level_platform/impl/src/lf_STM32f4_support.c
naichenzhao Sep 16, 2024
cba9193
resolved some more comments
naichenzhao Sep 17, 2024
cf36f59
changed macro
naichenzhao Sep 24, 2024
6e9e3c7
Merge branch 'main' into stm32
naichenzhao Oct 4, 2024
6974d8e
Merge branch 'main' into stm32
naichenzhao Oct 5, 2024
a2e1e4b
Merge branch 'stm32' of https://github.com/lf-lang/reactor-c into stm32
naichenzhao Oct 5, 2024
68ec2d7
Run clang-format
erlingrj Oct 5, 2024
2d23659
Update lf-ref to the new lf branch
erlingrj Oct 5, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions core/platform/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ lf_zephyr_support.c
lf_zephyr_clock_counter.c
lf_zephyr_clock_kernel.c
lf_rp2040_support.c
lf_STM32f4_support.c
)

if(${CMAKE_SYSTEM_NAME} STREQUAL "Windows")
Expand All @@ -21,6 +22,8 @@ elseif(${CMAKE_SYSTEM_NAME} STREQUAL "Nrf52")
target_compile_definitions(core PUBLIC PLATFORM_NRF52)
elseif(${CMAKE_SYSTEM_NAME} STREQUAL "Zephyr")
target_compile_definitions(core PUBLIC PLATFORM_ZEPHYR)
elseif(${CMAKE_SYSTEM_NAME} STREQUAL "Stm32")
target_compile_definitions(core PUBLIC PLATFORM_STM32F4)
elseif(${CMAKE_SYSTEM_NAME} STREQUAL "Rp2040")
target_compile_definitions(core PUBLIC PLATFORM_RP2040)
endif()
Expand Down
2 changes: 2 additions & 0 deletions core/platform/Platform.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ elseif(${CMAKE_SYSTEM_NAME} STREQUAL "Windows")
message("Using Windows SDK version ${CMAKE_VS_WINDOWS_TARGET_PLATFORM_VERSION}")
elseif(${CMAKE_SYSTEM_NAME} STREQUAL "Zephyr")
set(LF_PLATFORM_FILE lf_zephyr_support.c)
elseif(${CMAKE_SYSTEM_NAME} STREQUAL "Stm32")
set(LF_PLATFORM_FILE lf_STM32f4_support.c)
elseif(${CMAKE_SYSTEM_NAME} STREQUAL "Rp2040")
message("Using pico-sdk for RP2040 target")
set(LF_PLATFORM_FILE lf_rp2040_support.c)
Expand Down
251 changes: 251 additions & 0 deletions core/platform/lf_STM32f4_support.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,251 @@
#if defined(PLATFORM_STM32F4)
/*************
I hope this software works LOL
naichenzhao marked this conversation as resolved.
Show resolved Hide resolved
naichenzhao marked this conversation as resolved.
Show resolved Hide resolved
***************/

#include "lf_STM32f4_support.h"
#include "platform.h"
#include "utils/util.h"
#include "tag.h"

// + -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- +
// | Important defines and global variables
// + -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- +

static volatile bool _lf_sleep_interrupted = false;
static volatile bool _lf_async_event = false;

#define LF_MAX_SLEEP_NS USEC(UINT32_MAX)
#define LF_MIN_SLEEP_NS USEC(5)
naichenzhao marked this conversation as resolved.
Show resolved Hide resolved

// 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;

// 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();

// + -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- +
// | Code for timer functions
// + -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- +

// We use timer 5 for our clock (probably better than fucking with sysTick)
naichenzhao marked this conversation as resolved.
Show resolved Hide resolved
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;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A timer prescaler is typically loaded without a guard, meaning that it won't take event until the next update event of the clock. This is meant to ensure the prescaler doesn't take effect mid-phase, which could cause strange artifacts in your application. When you enable the peripheral, it will start with whatever was in the prescaler register at the time it was enabled, possibly leading to a large delay before the timer ISR fires. The new prescaler value will be loaded after the preceding update event.

I'm not intimately familiar with this chipset, but generally hardware peripherals are configured first and then enabled. You may consider loading the prescaler before you enable the counter. If that doesn't work, a reset is fine, but I suggest a simpler comment "reset to effect new prescaler".

This is important in safety-critical systems, you need to know when your I/O is stable and safe to use. This is known as "I/O stability".

}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM! But consider changing the prescaler handling.

The function initializes the clock and sets up the timer. Consider changing the prescaler handling as suggested in the existing comment.

-    /* 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;
+    // Reset to effect new prescaler
+    TIM5->CNT = 0xFFFFFFFE;
Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
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;
}
// 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);
// Reset to effect new prescaler
TIM5->CNT = 0xFFFFFFFE;


/**
* ISR for handling timer overflow -> We increment the upper timer
*/
void TIM5_IRQHandler(void) {
if (TIM5->SR & (1 << 1)) {
TIM5->SR &= ~(1 << 1);
_lf_time_us_high += 1;
}
}

/**
* Write the time since boot into time variable
*/
int _lf_clock_now(instant_t *t)
{
// Timer is cooked
if (!t) {
return -1;
naichenzhao marked this conversation as resolved.
Show resolved Hide resolved
}
// Get the current microseconds from TIM5
uint32_t _lf_time_us_low = TIM5->CNT;

// Combine upper and lower timers (Yoinked from lf_nrf52 support)
uint64_t now_us = COMBINE_HI_LO((_lf_time_us_high - 1), _lf_time_us_low);
*t = ((instant_t)now_us) * 1000;
return 0;
}

/**
* Make the STM32 go honk shoo mimimi for set nanoseconds
naichenzhao marked this conversation as resolved.
Show resolved Hide resolved
* I essentially stole this from the lf_nrf52 support
*/
int lf_sleep(interval_t sleep_duration) {
instant_t target_time;
instant_t current_time;

_lf_clock_now(&current_time);
target_time = current_time + sleep_duration;
while (current_time <= target_time)
_lf_clock_now(&current_time);

return 0;
naichenzhao marked this conversation as resolved.
Show resolved Hide resolved
}

/**
* Make the STM32 go honk shoo honk shoo for set nanoseconds
* This one uses a do-while loop. :)
* I essentially stole this from the lf_nrf52 support
*/
static void lf_busy_wait_until(instant_t wakeup_time) {
instant_t now;
do {
_lf_clock_now(&now);
} while (now < wakeup_time);
}

// I am pretty sure this function doesnt work
naichenzhao marked this conversation as resolved.
Show resolved Hide resolved
// Ill try to fix it once i know what the fuck its supposed to do, LOL
/* 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_now(&now);
interval_t duration = wakeup_time - now;

// Edge case handling for super small duration
if (duration <= 0) {
return 0;
} else if (duration < 10) {
naichenzhao marked this conversation as resolved.
Show resolved Hide resolved
lf_busy_wait_until(wakeup_time);
return 0;
}

// Enable interrupts and prepare to wait
_lf_async_event = false;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This method sleeps; what happens if an async event happens during the sleep, and before it is reset here? Is the event lost?

lf_enable_interrupts_nested();

do {
_lf_clock_now(&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 {
LF_PRINT_DEBUG(" *The STM32 rises from sleep* \n");
naichenzhao marked this conversation as resolved.
Show resolved Hide resolved
return -1;
}

}

// + -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- +
// | Code for enabling and disabling Interrupts
// + -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- +

// disables the IRQ (checks if its already disabled)
int lf_disable_interrupts_nested() {
// Disable interrupts if they are currently enabled
if (_lf_num_nested_crit_sec == 0) {
__disable_irq();
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does this disable all interrupts? If so, does this affect timer/counters?

}

// update the depth of disabling interrupts
_lf_num_nested_crit_sec++;
return 0;
}

// enables the IRQ (checks if other programs still want it disabled first)
int lf_enable_interrupts_nested() {
// Somebody fucked up, LOL
naichenzhao marked this conversation as resolved.
Show resolved Hide resolved
if (_lf_num_nested_crit_sec <= 0) {
return 1;
}

// update the depth of disabling interrupts
_lf_num_nested_crit_sec--;

// If we have exited all important programs, we can enable them 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;
}

int test_func(void) {
naichenzhao marked this conversation as resolved.
Show resolved Hide resolved
return 5;
}

// + -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- +
// | Other functions I found -> taken from the generated main.c
// + -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- +
void lf_SystemClock_Config(void) {
RCC_OscInitTypeDef RCC_OscInitStruct = {0};
RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};

/** Configure the main internal regulator output voltage
naichenzhao marked this conversation as resolved.
Show resolved Hide resolved
*/
__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();
}
}

void Error_Handler(void) {
/* USER CODE BEGIN Error_Handler_Debug */
/* User can add his own implementation to report the HAL error return state */
__disable_irq();
while (1) {
naichenzhao marked this conversation as resolved.
Show resolved Hide resolved
}
/* USER CODE END Error_Handler_Debug */
}

#endif
2 changes: 2 additions & 0 deletions include/core/platform.h
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,8 @@ int lf_critical_section_exit(environment_t* env);
#include "platform/lf_zephyr_support.h"
#elif defined(PLATFORM_NRF52)
#include "platform/lf_nrf52_support.h"
#elif defined(PLATFORM_STM32F4)
#include "platform/lf_STM32f4_support.h"
#elif defined(PLATFORM_RP2040)
#include "platform/lf_rp2040_support.h"
#elif defined(WIN32) || defined(_WIN32) || defined(__WIN32__) || defined(__NT__)
Expand Down
23 changes: 23 additions & 0 deletions include/core/platform/lf_STM32f4_support.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
/* STM32 API support for the C target of Lingua Franca. */

#ifndef LF_STM32F4_SUPPORT_H
#define LF_STM32F4_SUPPORT_H

// I have no idea what the fuck TTY is so i guess we dont support it
naichenzhao marked this conversation as resolved.
Show resolved Hide resolved
#define NO_TTY

#include <stm32f4xx_hal.h>

// Defines for formatting time in printf for pico
#define PRINTF_TAG "(" PRINTF_TIME ", " PRINTF_MICROSTEP ")"
#define PRINTF_TIME "%lld"
#define PRINTF_MICROSTEP "%d"

#define LF_TIME_BUFFER_LENGTH 80
#define _LF_TIMEOUT 1
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What units is this #def? Should it be a const instead?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I kinda just copied this over when I compared it against the rp2040 platform - I wasnt sure what this was used for so I kept it just incase. If its not used in LF proper, ill just delete it


#ifdef LF_THREADED
#error "I have no idea how to support threading"
naichenzhao marked this conversation as resolved.
Show resolved Hide resolved
#endif // LF_THREADED

#endif
1 change: 1 addition & 0 deletions lingua-franca-ref.txt
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
single-threaded
lhstrh marked this conversation as resolved.
Show resolved Hide resolved