Skip to content

Commit

Permalink
da1469x/os-tick: Fix slightly incorrect time-keeping
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
apc067 committed Oct 18, 2024
1 parent d730f99 commit d85d3fc
Show file tree
Hide file tree
Showing 2 changed files with 37 additions and 22 deletions.
6 changes: 6 additions & 0 deletions hw/hal/include/hal/hal_os_tick.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
Expand Down
53 changes: 31 additions & 22 deletions hw/mcu/dialog/da1469x/src/hal_os_tick.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
};
Expand Down Expand Up @@ -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;
}
}

Expand All @@ -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);
}
Expand All @@ -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)
{
Expand All @@ -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);
}
Expand All @@ -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;
Expand Down

0 comments on commit d85d3fc

Please sign in to comment.