From eedddaeeb2114d029eda84f0873df468bf3d223c Mon Sep 17 00:00:00 2001 From: Nicolas Pitre Date: Fri, 21 Aug 2026 11:13:21 -0400 Subject: [PATCH] native: hw_timer: add an absolute deadline for the tick interrupt The tick interrupt can only be had as a period, via hwtimer_enable(), with hwtimer_set_silent_ticks() to pass over the expiries not wanted. Most real timers are not that shape: the ARM architected timer is a free running counter plus an absolute compare (CNTVCT_EL0, CNTV_CVAL_EL0), and the RISC-V machine timer is mtime plus mtimecmp. Both fire once the counter reaches or passes the compare. A driver for either holds an absolute deadline, and here has to round it onto the period grid or reprogram the period on every timeout. hwtimer_set_tick_deadline() raises the same interrupt when simulated time reaches an absolute time, which nsi_hws_get_time() already hands out. A deadline already passed fires at once and NSI_NEVER cancels. It uses an event of its own, so the periodic tick, the awake timer and the real time pacing are untouched. Signed-off-by: Nicolas Pitre --- native/src/include/nsi_timer_model.h | 1 + native/src/timer_model.c | 30 ++++++++++++++++++++++++++++ 2 files changed, 31 insertions(+) diff --git a/native/src/include/nsi_timer_model.h b/native/src/include/nsi_timer_model.h index 2c59653..793d21c 100644 --- a/native/src/include/nsi_timer_model.h +++ b/native/src/include/nsi_timer_model.h @@ -23,6 +23,7 @@ void hwtimer_adjust_rt_ratio(double ratio_correction); void hwtimer_wake_in_time(uint64_t time); void hwtimer_set_silent_ticks(int64_t sys_ticks); void hwtimer_enable(uint64_t period); +void hwtimer_set_tick_deadline(uint64_t deadline); int64_t hwtimer_get_pending_silent_ticks(void); void hwtimer_reset_rtc(void); diff --git a/native/src/timer_model.c b/native/src/timer_model.c index 082a3f2..b18f992 100644 --- a/native/src/timer_model.c +++ b/native/src/timer_model.c @@ -65,6 +65,7 @@ static uint64_t hw_timer_timer; /* Event timer exposed to the HW scheduler */ static uint64_t hw_timer_tick_timer; static uint64_t hw_timer_rt_timer; static uint64_t hw_timer_awake_timer; +static uint64_t hw_timer_deadline_timer; static uint64_t tick_p; /* Period of the ticker */ static int64_t silent_ticks; @@ -129,6 +130,7 @@ static void hwtimer_update_timer(void) { hw_timer_timer = NSI_MIN(hw_timer_tick_timer, hw_timer_awake_timer); hw_timer_timer = NSI_MIN(hw_timer_timer, hw_timer_rt_timer); + hw_timer_timer = NSI_MIN(hw_timer_timer, hw_timer_deadline_timer); } static inline void host_clock_gettime(struct timespec *tv) @@ -173,6 +175,7 @@ static void hwtimer_init(void) silent_ticks = 0; hw_timer_tick_timer = NSI_NEVER; hw_timer_awake_timer = NSI_NEVER; + hw_timer_deadline_timer = NSI_NEVER; hwtimer_update_timer(); if (!reset_rtc) { @@ -269,6 +272,13 @@ static void hwtimer_awake_timer_reached(void) hw_irq_ctrl_set_irq(PHONY_HARD_IRQ); } +static void hwtimer_deadline_timer_reached(void) +{ + hw_timer_deadline_timer = NSI_NEVER; + hwtimer_update_timer(); + hw_irq_ctrl_set_irq(TIMER_TICK_IRQ); +} + static void hwtimer_timer_reached(void) { uint64_t Now = hw_timer_timer; @@ -281,6 +291,10 @@ static void hwtimer_timer_reached(void) hwtimer_rt_timer_reached(); } + if (hw_timer_deadline_timer == Now) { + hwtimer_deadline_timer_reached(); + } + if (hw_timer_tick_timer == Now) { hwtimer_tick_timer_reached(); } @@ -312,6 +326,22 @@ void hwtimer_wake_in_time(uint64_t time) } } +/** + * Raise the tick interrupt when simulated time reaches , an absolute + * time in microseconds since boot. A deadline already passed fires at once. + * NSI_NEVER cancels without setting a new one. + * + * One-shot alternative to hwtimer_enable()'s periodic tick. Do not use both. + */ +void hwtimer_set_tick_deadline(uint64_t deadline) +{ + uint64_t now = nsi_hws_get_time(); + + hw_timer_deadline_timer = NSI_MAX(deadline, now); + hwtimer_update_timer(); + nsi_hws_find_next_event(); +} + /** * The kernel wants to skip the next sys_ticks tick interrupts * If sys_ticks == 0, the next interrupt will be raised.