From d85d3fcc6b34e87d8594e85053ebad1d1575466d Mon Sep 17 00:00:00 2001 From: Peter Csaszar Date: Thu, 17 Oct 2024 16:26:42 -0700 Subject: [PATCH] da1469x/os-tick: Fix slightly incorrect time-keeping If the LP clock's frequency is not an integer multiple of the specified OS tick frequency, the period of OS ticks will be incorrect due to integer division truncation errors (to the tune of almost 1% for RCX), resulting in OS Time gradually creeping away from the actual time. This commit fixes the issue, by maintaining the fractional OS tick value and advance OS Time accordingly. --- hw/hal/include/hal/hal_os_tick.h | 6 +++ hw/mcu/dialog/da1469x/src/hal_os_tick.c | 53 +++++++++++++++---------- 2 files changed, 37 insertions(+), 22 deletions(-) diff --git a/hw/hal/include/hal/hal_os_tick.h b/hw/hal/include/hal/hal_os_tick.h index c48069ad20..254a480998 100644 --- a/hw/hal/include/hal/hal_os_tick.h +++ b/hw/hal/include/hal/hal_os_tick.h @@ -49,6 +49,12 @@ void os_tick_init(uint32_t os_ticks_per_sec, int prio); */ void os_tick_idle(os_time_t n); +/** + * Calculate the OS tick parameters. + * + * @param os_ticks_per_sec The number of OS ticks per second + */ +void hal_os_tick_calc_params(uint32_t os_ticks_per_sec); #ifdef __cplusplus } diff --git a/hw/mcu/dialog/da1469x/src/hal_os_tick.c b/hw/mcu/dialog/da1469x/src/hal_os_tick.c index 64bc53653b..1c2641cef8 100644 --- a/hw/mcu/dialog/da1469x/src/hal_os_tick.c +++ b/hw/mcu/dialog/da1469x/src/hal_os_tick.c @@ -28,7 +28,9 @@ #include "mcu/mcu.h" struct hal_os_tick { - int ticks_per_ostick; + uint32_t cycles_per_ostick; /* For generating the OS ticks */ + uint32_t cycles_per_256_osticks; /* For more precise OS Time calculation */ + uint32_t os_tick_residual; os_time_t max_idle_ticks; uint32_t last_trigger_val; }; @@ -88,7 +90,7 @@ hal_os_tick_set_timer_trigger_val(uint32_t trigger_val) break; } - trigger_val += g_hal_os_tick.ticks_per_ostick; + trigger_val += g_hal_os_tick.cycles_per_ostick; } } @@ -97,28 +99,28 @@ hal_os_tick_handler(void) { uint32_t primask; uint32_t timer_val; - int delta; - int ticks; + uint32_t delta_x256; + uint32_t ticks; __HAL_DISABLE_INTERRUPTS(primask); - /* Calculate elapsed ticks and advance OS time. */ + /* Calculate elapsed cycles of Timer 2 & record its current value */ timer_val = hal_os_tick_get_timer_val(); - delta = sub24(timer_val, g_hal_os_tick.last_trigger_val); - ticks = delta / g_hal_os_tick.ticks_per_ostick; - os_time_advance(ticks); + delta_x256 = ((timer_val - g_hal_os_tick.last_trigger_val) & 0xffffff) << 8; + g_hal_os_tick.last_trigger_val = timer_val; /* Clear timer interrupt */ TIMER2->TIMER2_CLEAR_IRQ_REG = 1; - /* Update the time associated with the most recent tick */ - g_hal_os_tick.last_trigger_val = (g_hal_os_tick.last_trigger_val + - (ticks * g_hal_os_tick.ticks_per_ostick)) & - 0xffffff; + /* Re-arm Timer 2 for the next OS tick */ + hal_os_tick_set_timer_trigger_val(timer_val + g_hal_os_tick.cycles_per_ostick); - /* Update timer trigger value for interrupt at the next tick */ - hal_os_tick_set_timer_trigger_val(g_hal_os_tick.last_trigger_val + - g_hal_os_tick.ticks_per_ostick); + /* Update OS Time */ + ticks = delta_x256 / g_hal_os_tick.cycles_per_256_osticks; + g_hal_os_tick.os_tick_residual += delta_x256 % g_hal_os_tick.cycles_per_256_osticks; + ticks += g_hal_os_tick.os_tick_residual / g_hal_os_tick.cycles_per_256_osticks; + g_hal_os_tick.os_tick_residual %= g_hal_os_tick.cycles_per_256_osticks; + os_time_advance(ticks); __HAL_ENABLE_INTERRUPTS(primask); } @@ -133,6 +135,17 @@ hal_os_tick_timer2_isr(void) os_trace_isr_exit(); } +void +hal_os_tick_calc_params(uint32_t os_ticks_per_sec) +{ + uint32_t lp_freq; + + lp_freq = da1469x_clock_lp_freq_get(); + g_hal_os_tick.cycles_per_256_osticks = (lp_freq << 8) / os_ticks_per_sec; + g_hal_os_tick.cycles_per_ostick = g_hal_os_tick.cycles_per_256_osticks >> 8; + g_hal_os_tick.max_idle_ticks = (1UL << 22) / g_hal_os_tick.cycles_per_ostick; +} + void os_tick_idle(os_time_t ticks) { @@ -146,7 +159,7 @@ os_tick_idle(os_time_t ticks) } new_trigger_val = g_hal_os_tick.last_trigger_val + - (ticks * g_hal_os_tick.ticks_per_ostick); + (ticks * g_hal_os_tick.cycles_per_ostick); hal_os_tick_set_timer_trigger_val(new_trigger_val); } @@ -164,12 +177,8 @@ os_tick_init(uint32_t os_ticks_per_sec, int prio) uint32_t primask; g_hal_os_tick.last_trigger_val = 0; -#if MYNEWT_VAL_CHOICE(MCU_LPCLK_SOURCE, RCX) - g_hal_os_tick.ticks_per_ostick = da1469x_clock_lp_rcx_freq_get() / os_ticks_per_sec; -#else - g_hal_os_tick.ticks_per_ostick = 32768 / os_ticks_per_sec; -#endif - g_hal_os_tick.max_idle_ticks = (1UL << 22) / g_hal_os_tick.ticks_per_ostick; + g_hal_os_tick.os_tick_residual = 0; + hal_os_tick_calc_params(os_ticks_per_sec); TIMER2->TIMER2_CTRL_REG = 0; TIMER2->TIMER2_PRESCALER_REG = 0;