[FL-2480] Use SysTick as the main OS timer (#1140)

* Use SysTick as OS tick timer
* Use LPTIM2 as tickless idle timer
* Remove dummy LPTIM2 IRQ handler
* Clean up clock init code
* Rename os timer to idle timer
* Advance hal ticks along with FreeRTOS's
* Improve SysTick control during tickless idle
* Improve idle timer precision
* Correct SysTick IRQ priority
* Main, FuriHal: move system startup to separate thread
* Minor code cleanup

Co-authored-by: Aleksandr Kutuzov <alleteam@gmail.com>
This commit is contained in:
Georgii Surkov 2022-04-21 16:15:19 +03:00 committed by GitHub
parent ad1ee8a5c6
commit df66f4f6ba
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
12 changed files with 140 additions and 162 deletions

View file

@ -18,7 +18,7 @@ extern uint32_t SystemCoreClock;
#define configUSE_IDLE_HOOK 0
#define configUSE_TICK_HOOK 0
#define configCPU_CLOCK_HZ (SystemCoreClock)
#define configTICK_RATE_HZ ((TickType_t)1024)
#define configTICK_RATE_HZ ((TickType_t)1000)
#define configMAX_PRIORITIES (56)
#define configMINIMAL_STACK_SIZE ((uint16_t)128)

View file

@ -6,32 +6,31 @@
#define TAG "Main"
#ifdef FURI_RAM_EXEC
int main() {
// Initialize FURI layer
furi_init();
// Flipper critical FURI HAL
furi_hal_init_early();
static const osThreadAttr_t init_thread_attr = {
.name = "Init",
.stack_size = 4096,
};
void init_task() {
// Flipper FURI HAL
furi_hal_init();
// Init flipper
flipper_init();
furi_run();
while(1) {
}
osThreadExit();
}
#else
int main() {
// Initialize FURI layer
furi_init();
// Flipper critical FURI HAL
furi_hal_init_early();
#ifdef FURI_RAM_EXEC
osThreadNew(init_task, NULL, &init_thread_attr);
#else
furi_hal_light_sequence("RGB");
// Delay is for button sampling
@ -52,21 +51,16 @@ int main() {
furi_hal_power_reset();
} else {
furi_hal_light_sequence("rgb G");
// Flipper FURI HAL
furi_hal_init();
// Init flipper
flipper_init();
furi_run();
osThreadNew(init_task, NULL, &init_thread_attr);
}
while(1) {
}
}
#endif
// Run Kernel
furi_run();
furi_crash("Kernel is Dead");
}
void Error_Handler(void) {
furi_crash("ErrorHandler");
}

View file

@ -10,6 +10,8 @@ void furi_hal_init_early() {
furi_hal_clock_init_early();
furi_hal_delay_init();
furi_hal_os_init();
furi_hal_resources_init_early();
furi_hal_spi_init_early();
@ -75,9 +77,6 @@ void furi_hal_init() {
furi_hal_bt_init();
furi_hal_compress_icon_init();
// FreeRTOS glue
furi_hal_os_init();
// FatFS driver initialization
MX_FATFS_Init();
FURI_LOG_I(TAG, "FATFS OK");

View file

@ -9,13 +9,15 @@
#define TAG "FuriHalClock"
#define TICK_INT_PRIORITY 0U
#define CPU_CLOCK_HZ_EARLY 4000000
#define CPU_CLOCK_HZ_MAIN 64000000
#define TICK_INT_PRIORITY 15U
#define HS_CLOCK_IS_READY() (LL_RCC_HSE_IsReady() && LL_RCC_HSI_IsReady())
#define LS_CLOCK_IS_READY() (LL_RCC_LSE_IsReady() && LL_RCC_LSI1_IsReady())
void furi_hal_clock_init_early() {
LL_Init1msTick(4000000);
LL_SetSystemCoreClock(4000000);
LL_SetSystemCoreClock(CPU_CLOCK_HZ_EARLY);
LL_Init1msTick(SystemCoreClock);
LL_AHB2_GRP1_EnableClock(LL_AHB2_GRP1_PERIPH_GPIOA);
LL_AHB2_GRP1_EnableClock(LL_AHB2_GRP1_PERIPH_GPIOB);
@ -27,6 +29,8 @@ void furi_hal_clock_init_early() {
LL_APB2_GRP1_EnableClock(LL_APB2_GRP1_PERIPH_SPI1);
LL_APB1_GRP1_EnableClock(LL_APB1_GRP1_PERIPH_SPI2);
LL_APB1_GRP2_EnableClock(LL_APB1_GRP2_PERIPH_LPTIM2);
LL_APB1_GRP1_EnableClock(LL_APB1_GRP1_PERIPH_I2C1);
LL_APB1_GRP1_EnableClock(LL_APB1_GRP1_PERIPH_I2C3);
}
@ -47,7 +51,7 @@ void furi_hal_clock_deinit_early() {
}
void furi_hal_clock_init() {
/* Prepare Flash memory for 64mHz system clock */
/* Prepare Flash memory for 64MHz system clock */
LL_FLASH_SetLatency(LL_FLASH_LATENCY_3);
while(LL_FLASH_GetLatency() != LL_FLASH_LATENCY_3)
;
@ -116,12 +120,13 @@ void furi_hal_clock_init() {
;
/* Update CMSIS variable (which can be updated also through SystemCoreClockUpdate function) */
LL_SetSystemCoreClock(64000000);
LL_SetSystemCoreClock(CPU_CLOCK_HZ_MAIN);
/* Update the time base */
LL_InitTick(64000000, 1000);
LL_Init1msTick(SystemCoreClock);
LL_SYSTICK_EnableIT();
NVIC_SetPriority(SysTick_IRQn, TICK_INT_PRIORITY);
NVIC_SetPriority(
SysTick_IRQn, NVIC_EncodePriority(NVIC_GetPriorityGrouping(), TICK_INT_PRIORITY, 0));
NVIC_EnableIRQ(SysTick_IRQn);
LL_RCC_SetUSARTClockSource(LL_RCC_USART1_CLKSOURCE_PCLK2);
@ -175,7 +180,6 @@ void furi_hal_clock_init() {
// APB1 GRP2
LL_APB1_GRP2_EnableClock(LL_APB1_GRP2_PERIPH_LPUART1);
LL_APB1_GRP2_EnableClock(LL_APB1_GRP2_PERIPH_LPTIM2);
// APB2
// LL_APB2_GRP1_EnableClock(LL_APB2_GRP1_PERIPH_ADC);
@ -217,3 +221,11 @@ void furi_hal_clock_switch_to_pll() {
while(LL_RCC_GetSysClkSource() != LL_RCC_SYS_CLKSOURCE_STATUS_PLL)
;
}
void furi_hal_clock_suspend_tick() {
CLEAR_BIT(SysTick->CTRL, SysTick_CTRL_ENABLE_Msk);
}
void furi_hal_clock_resume_tick() {
SET_BIT(SysTick->CTRL, SysTick_CTRL_ENABLE_Msk);
}

View file

@ -14,3 +14,9 @@ void furi_hal_clock_switch_to_hsi();
/** Switch to PLL clock */
void furi_hal_clock_switch_to_pll();
/** Stop SysTick counter without resetting */
void furi_hal_clock_suspend_tick();
/** Continue SysTick counter operation */
void furi_hal_clock_resume_tick();

View file

@ -6,8 +6,6 @@
#define TAG "FuriHalDelay"
static volatile uint32_t tick_cnt = 0;
void furi_hal_delay_init() {
CoreDebug->DEMCR |= CoreDebug_DEMCR_TRCENA_Msk;
DWT->CTRL |= DWT_CTRL_CYCCNTENA_Msk;
@ -18,12 +16,8 @@ uint32_t furi_hal_delay_instructions_per_microsecond() {
return SystemCoreClock / 1000000;
}
void furi_hal_tick(void) {
tick_cnt++;
}
uint32_t furi_hal_get_tick(void) {
return tick_cnt;
return osKernelGetTickCount();
}
uint32_t furi_hal_ms_to_ticks(float milliseconds) {

View file

@ -0,0 +1,55 @@
#pragma once
#include <stm32wbxx_ll_lptim.h>
#include <stm32wbxx_ll_bus.h>
#include <stm32wbxx_ll_rcc.h>
#include <stdint.h>
// Timer used for tickless idle
#define FURI_HAL_IDLE_TIMER_MAX 0xFFFF
#define FURI_HAL_IDLE_TIMER LPTIM2
#define FURI_HAL_IDLE_TIMER_IRQ LPTIM2_IRQn
static inline void furi_hal_idle_timer_init() {
// Configure clock source
LL_RCC_SetLPTIMClockSource(LL_RCC_LPTIM2_CLKSOURCE_LSE);
// Set interrupt priority and enable them
NVIC_SetPriority(
FURI_HAL_IDLE_TIMER_IRQ, NVIC_EncodePriority(NVIC_GetPriorityGrouping(), 15, 0));
NVIC_EnableIRQ(FURI_HAL_IDLE_TIMER_IRQ);
}
static inline void furi_hal_idle_timer_start(uint32_t count) {
count--;
// Enable timer
LL_LPTIM_Enable(FURI_HAL_IDLE_TIMER);
while(!LL_LPTIM_IsEnabled(FURI_HAL_IDLE_TIMER))
;
// Enable compare match interrupt
LL_LPTIM_EnableIT_CMPM(FURI_HAL_IDLE_TIMER);
// Set compare, autoreload and start counter
// Include some marging to workaround ARRM behaviour
LL_LPTIM_SetCompare(FURI_HAL_IDLE_TIMER, count - 3);
LL_LPTIM_SetAutoReload(FURI_HAL_IDLE_TIMER, count);
LL_LPTIM_StartCounter(FURI_HAL_IDLE_TIMER, LL_LPTIM_OPERATING_MODE_ONESHOT);
}
static inline void furi_hal_idle_timer_reset() {
// Hard reset timer
// THE ONLY RELIABLE WAY to stop it according to errata
LL_LPTIM_DeInit(FURI_HAL_IDLE_TIMER);
// Prevent IRQ handler call
NVIC_ClearPendingIRQ(FURI_HAL_IDLE_TIMER_IRQ);
}
static inline uint32_t furi_hal_idle_timer_get_cnt() {
uint32_t counter = LL_LPTIM_GetCounter(FURI_HAL_IDLE_TIMER);
uint32_t counter_shadow = LL_LPTIM_GetCounter(FURI_HAL_IDLE_TIMER);
while(counter != counter_shadow) {
counter = counter_shadow;
counter_shadow = LL_LPTIM_GetCounter(FURI_HAL_IDLE_TIMER);
}
return counter;
}

View file

@ -1,5 +1,6 @@
#include "furi_hal_interrupt.h"
#include "furi_hal_delay.h"
#include "furi_hal_os.h"
#include <furi.h>
@ -249,7 +250,7 @@ extern void HW_IPCC_Tx_Handler();
extern void HW_IPCC_Rx_Handler();
void SysTick_Handler(void) {
furi_hal_tick();
furi_hal_os_tick();
}
void USB_LP_IRQHandler(void) {
@ -264,4 +265,4 @@ void IPCC_C1_TX_IRQHandler(void) {
void IPCC_C1_RX_IRQHandler(void) {
HW_IPCC_Rx_Handler();
}
}

View file

@ -1,17 +1,22 @@
#include <furi_hal_os.h>
#include <furi_hal_os_timer.h>
#include <furi_hal_clock.h>
#include <furi_hal_power.h>
#include <furi_hal_delay.h>
#include <furi_hal_idle_timer.h>
#include <stm32wbxx_ll_cortex.h>
#include <furi.h>
#define TAG "FuriHalOs"
#define FURI_HAL_OS_CLK_FREQUENCY 32768
#define FURI_HAL_OS_TICK_PER_SECOND 1024
#define FURI_HAL_OS_CLK_PER_TICK (FURI_HAL_OS_CLK_FREQUENCY / FURI_HAL_OS_TICK_PER_SECOND)
#define FURI_HAL_OS_TICK_PER_EPOCH (FURI_HAL_OS_TIMER_MAX / FURI_HAL_OS_CLK_PER_TICK)
#define FURI_HAL_OS_MAX_SLEEP (FURI_HAL_OS_TICK_PER_EPOCH - 1)
#define FURI_HAL_IDLE_TIMER_CLK_HZ 32768
#define FURI_HAL_OS_TICK_HZ configTICK_RATE_HZ
#define FURI_HAL_OS_IDLE_CNT_TO_TICKS(x) ((x * FURI_HAL_OS_TICK_HZ) / FURI_HAL_IDLE_TIMER_CLK_HZ)
#define FURI_HAL_OS_TICKS_TO_IDLE_CNT(x) ((x * FURI_HAL_IDLE_TIMER_CLK_HZ) / FURI_HAL_OS_TICK_HZ)
#define FURI_HAL_IDLE_TIMER_TICK_PER_EPOCH (FURI_HAL_OS_IDLE_CNT_TO_TICKS(FURI_HAL_IDLE_TIMER_MAX))
#define FURI_HAL_OS_MAX_SLEEP (FURI_HAL_IDLE_TIMER_TICK_PER_EPOCH - 1)
#ifdef FURI_HAL_OS_DEBUG
#include <stm32wbxx_ll_gpio.h>
@ -30,48 +35,37 @@ void furi_hal_os_timer_callback() {
extern void xPortSysTickHandler();
volatile uint32_t furi_hal_os_skew = 0;
static volatile uint32_t furi_hal_os_skew = 0;
void furi_hal_os_init() {
LL_DBGMCU_APB1_GRP2_FreezePeriph(LL_DBGMCU_APB1_GRP2_LPTIM2_STOP);
furi_hal_os_timer_init();
furi_hal_os_timer_continuous(FURI_HAL_OS_CLK_PER_TICK);
furi_hal_idle_timer_init();
#ifdef FURI_HAL_OS_DEBUG
LL_GPIO_SetPinMode(LED_SLEEP_PORT, LED_SLEEP_PIN, LL_GPIO_MODE_OUTPUT);
LL_GPIO_SetPinMode(LED_TICK_PORT, LED_TICK_PIN, LL_GPIO_MODE_OUTPUT);
LL_GPIO_SetPinMode(LED_SECOND_PORT, LED_SECOND_PIN, LL_GPIO_MODE_OUTPUT);
osTimerId_t second_timer = osTimerNew(furi_hal_os_timer_callback, osTimerPeriodic, NULL, NULL);
osTimerStart(second_timer, FURI_HAL_OS_TICK_PER_SECOND);
osTimerStart(second_timer, FURI_HAL_OS_TICK_HZ);
#endif
FURI_LOG_I(TAG, "Init OK");
}
void LPTIM2_IRQHandler(void) {
// Autoreload
if(LL_LPTIM_IsActiveFlag_ARRM(FURI_HAL_OS_TIMER)) {
LL_LPTIM_ClearFLAG_ARRM(FURI_HAL_OS_TIMER);
if(xTaskGetSchedulerState() != taskSCHEDULER_NOT_STARTED) {
void furi_hal_os_tick() {
if(xTaskGetSchedulerState() != taskSCHEDULER_NOT_STARTED) {
#ifdef FURI_HAL_OS_DEBUG
LL_GPIO_TogglePin(LED_TICK_PORT, LED_TICK_PIN);
LL_GPIO_TogglePin(LED_TICK_PORT, LED_TICK_PIN);
#endif
xPortSysTickHandler();
}
}
if(LL_LPTIM_IsActiveFlag_CMPM(FURI_HAL_OS_TIMER)) {
LL_LPTIM_ClearFLAG_CMPM(FURI_HAL_OS_TIMER);
xPortSysTickHandler();
}
}
static inline uint32_t furi_hal_os_sleep(TickType_t expected_idle_ticks) {
// Stop ticks
furi_hal_os_timer_reset();
LL_SYSTICK_DisableIT();
furi_hal_clock_suspend_tick();
// Start wakeup timer
furi_hal_os_timer_single(expected_idle_ticks * FURI_HAL_OS_CLK_PER_TICK);
furi_hal_idle_timer_start(FURI_HAL_OS_TICKS_TO_IDLE_CNT(expected_idle_ticks));
#ifdef FURI_HAL_OS_DEBUG
LL_GPIO_ResetOutputPin(LED_SLEEP_PORT, LED_SLEEP_PIN);
@ -85,21 +79,19 @@ static inline uint32_t furi_hal_os_sleep(TickType_t expected_idle_ticks) {
#endif
// Calculate how much time we spent in the sleep
uint32_t after_cnt = furi_hal_os_timer_get_cnt() + furi_hal_os_skew;
uint32_t after_tick = after_cnt / FURI_HAL_OS_CLK_PER_TICK;
furi_hal_os_skew = after_cnt % FURI_HAL_OS_CLK_PER_TICK;
uint32_t after_cnt = furi_hal_idle_timer_get_cnt() + furi_hal_os_skew;
uint32_t after_tick = FURI_HAL_OS_IDLE_CNT_TO_TICKS(after_cnt);
furi_hal_os_skew = after_cnt - (after_cnt / after_tick);
bool cmpm = LL_LPTIM_IsActiveFlag_CMPM(FURI_HAL_OS_TIMER);
bool arrm = LL_LPTIM_IsActiveFlag_ARRM(FURI_HAL_OS_TIMER);
bool cmpm = LL_LPTIM_IsActiveFlag_CMPM(FURI_HAL_IDLE_TIMER);
bool arrm = LL_LPTIM_IsActiveFlag_ARRM(FURI_HAL_IDLE_TIMER);
if(cmpm && arrm) after_tick += expected_idle_ticks;
// Prepare tick timer for new round
furi_hal_os_timer_reset();
furi_hal_idle_timer_reset();
// Resume ticks
LL_SYSTICK_EnableIT();
furi_hal_os_timer_continuous(FURI_HAL_OS_CLK_PER_TICK);
furi_hal_clock_resume_tick();
return after_tick;
}
@ -109,7 +101,7 @@ void vPortSuppressTicksAndSleep(TickType_t expected_idle_ticks) {
return;
}
// Limit mount of ticks to maximum that timer can count
// Limit amount of ticks to maximum that timer can count
if(expected_idle_ticks > FURI_HAL_OS_MAX_SLEEP) {
expected_idle_ticks = FURI_HAL_OS_MAX_SLEEP;
}
@ -125,14 +117,9 @@ void vPortSuppressTicksAndSleep(TickType_t expected_idle_ticks) {
// Sleep and track how much ticks we spent sleeping
uint32_t completed_ticks = furi_hal_os_sleep(expected_idle_ticks);
// Notify system about time spent in sleep
if(completed_ticks > 0) {
if(completed_ticks > expected_idle_ticks) {
vTaskStepTick(expected_idle_ticks);
} else {
vTaskStepTick(completed_ticks);
}
vTaskStepTick(MIN(completed_ticks, expected_idle_ticks));
}
// Reenable IRQ

View file

@ -11,6 +11,10 @@ extern "C" {
*/
void furi_hal_os_init();
/* Advance OS tick counter
*/
void furi_hal_os_tick();
#ifdef __cplusplus
}
#endif
#endif

View file

@ -1,69 +0,0 @@
#pragma once
#include <stm32wbxx_ll_lptim.h>
#include <stm32wbxx_ll_bus.h>
#include <stm32wbxx_ll_rcc.h>
#include <stdint.h>
// Timer used for system ticks
#define FURI_HAL_OS_TIMER_MAX 0xFFFF
#define FURI_HAL_OS_TIMER_REG_LOAD_DLY 0x1
#define FURI_HAL_OS_TIMER LPTIM2
#define FURI_HAL_OS_TIMER_IRQ LPTIM2_IRQn
static inline void furi_hal_os_timer_init() {
// Configure clock source
LL_RCC_SetLPTIMClockSource(LL_RCC_LPTIM2_CLKSOURCE_LSE);
// Set interrupt priority and enable them
NVIC_SetPriority(
FURI_HAL_OS_TIMER_IRQ, NVIC_EncodePriority(NVIC_GetPriorityGrouping(), 15, 0));
NVIC_EnableIRQ(FURI_HAL_OS_TIMER_IRQ);
}
static inline void furi_hal_os_timer_continuous(uint32_t count) {
count--;
// Enable timer
LL_LPTIM_Enable(FURI_HAL_OS_TIMER);
while(!LL_LPTIM_IsEnabled(FURI_HAL_OS_TIMER))
;
// Enable rutoreload match interrupt
LL_LPTIM_EnableIT_ARRM(FURI_HAL_OS_TIMER);
// Set autoreload and start counter
LL_LPTIM_SetAutoReload(FURI_HAL_OS_TIMER, count);
LL_LPTIM_StartCounter(FURI_HAL_OS_TIMER, LL_LPTIM_OPERATING_MODE_CONTINUOUS);
}
static inline void furi_hal_os_timer_single(uint32_t count) {
count--;
// Enable timer
LL_LPTIM_Enable(FURI_HAL_OS_TIMER);
while(!LL_LPTIM_IsEnabled(FURI_HAL_OS_TIMER))
;
// Enable compare match interrupt
LL_LPTIM_EnableIT_CMPM(FURI_HAL_OS_TIMER);
// Set compare, autoreload and start counter
// Include some marging to workaround ARRM behaviour
LL_LPTIM_SetCompare(FURI_HAL_OS_TIMER, count - 3);
LL_LPTIM_SetAutoReload(FURI_HAL_OS_TIMER, count);
LL_LPTIM_StartCounter(FURI_HAL_OS_TIMER, LL_LPTIM_OPERATING_MODE_ONESHOT);
}
static inline void furi_hal_os_timer_reset() {
// Hard reset timer
// THE ONLY RELIABLEWAY to stop it according to errata
LL_LPTIM_DeInit(FURI_HAL_OS_TIMER);
}
static inline uint32_t furi_hal_os_timer_get_cnt() {
uint32_t counter = LL_LPTIM_GetCounter(FURI_HAL_OS_TIMER);
uint32_t counter_shadow = LL_LPTIM_GetCounter(FURI_HAL_OS_TIMER);
while(counter != counter_shadow) {
counter = counter_shadow;
counter_shadow = LL_LPTIM_GetCounter(FURI_HAL_OS_TIMER);
}
return counter;
}

View file

@ -18,11 +18,6 @@ void furi_hal_delay_init();
/** Get instructions per microsecond count */
uint32_t furi_hal_delay_instructions_per_microsecond();
/** Increase tick counter.
* Should be called from SysTick ISR
*/
void furi_hal_tick(void);
/** Get current tick counter
*
* System uptime, may overflow.