From fff70361217554119307257aa9c23a774b37d2dc Mon Sep 17 00:00:00 2001 From: Kuan-Wei Chiu Date: Wed, 3 Dec 2025 18:50:58 +0000 Subject: [PATCH] Fix cumulative drift in periodic timers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Currently, periodic timers allow errors to accumulate because the next deadline is calculated relative to the current system tick (now + period). Any latency in the tick handler execution—whether due to interrupt jitter or system load—permanently shifts the phase of all future expirations. Fix the drift by anchoring the timer schedule to an absolute baseline. A new field, last_expected_fire_tick, is introduced to track the theoretical expiration time. When the timer fires, the next deadline is now computed by advancing this expected time by the period, rather than relying on the actual execution time. This ensures the timer maintains its long-term frequency accuracy regardless of short-term scheduling delays. --- include/sys/timer.h | 5 ++++- kernel/timer.c | 8 ++++++-- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/include/sys/timer.h b/include/sys/timer.h index afbe7514..397eb9dd 100644 --- a/include/sys/timer.h +++ b/include/sys/timer.h @@ -26,7 +26,10 @@ typedef enum { typedef struct { /* Timing Parameters */ uint32_t deadline_ticks; /* Expiration time in absolute system ticks */ - uint32_t period_ms; /* Reload period in milliseconds */ + uint32_t last_expected_fire_tick; /* Last calculated expected fire time for + * periodic timer + */ + uint32_t period_ms; /* Reload period in milliseconds */ /* Timer Identification and State */ uint16_t id; /* Unique handle assigned by the kernel */ diff --git a/kernel/timer.c b/kernel/timer.c index 8153d763..f3cc08aa 100644 --- a/kernel/timer.c +++ b/kernel/timer.c @@ -251,7 +251,9 @@ void _timer_tick_handler(void) /* Handle auto-reload timers */ if (t->mode == TIMER_AUTORELOAD) { - t->deadline_ticks = now + MS_TO_TICKS(t->period_ms); + /* Calculate next expected fire tick to prevent cumulative error */ + t->last_expected_fire_tick += MS_TO_TICKS(t->period_ms); + t->deadline_ticks = t->last_expected_fire_tick; timer_sorted_insert(t); /* Re-insert for next expiration */ } else { t->mode = TIMER_DISABLED; /* One-shot timers are done */ @@ -305,6 +307,7 @@ int32_t mo_timer_create(void *(*callback)(void *arg), t->arg = arg; t->period_ms = period_ms; t->deadline_ticks = 0; + t->last_expected_fire_tick = 0; t->mode = TIMER_DISABLED; t->_reserved = 0; @@ -387,7 +390,8 @@ int32_t mo_timer_start(uint16_t id, uint8_t mode) /* Configure and start timer */ t->mode = mode; - t->deadline_ticks = mo_ticks() + MS_TO_TICKS(t->period_ms); + t->last_expected_fire_tick = mo_ticks() + MS_TO_TICKS(t->period_ms); + t->deadline_ticks = t->last_expected_fire_tick; if (unlikely(timer_sorted_insert(t) != ERR_OK)) { t->mode = TIMER_DISABLED;