This is an automated email from the ASF dual-hosted git repository.
xiaoxiang781216 pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/nuttx.git
The following commit(s) were added to refs/heads/master by this push:
new df427b54ae3 arch/xtensa/esp32s3: Don't lose a tickless alarm that is
already due.
df427b54ae3 is described below
commit df427b54ae373dc0277c0648f70124b5e8125906
Author: Max Kriegleder <[email protected]>
AuthorDate: Fri Oct 23 10:48:21 2026 +0200
arch/xtensa/esp32s3: Don't lose a tickless alarm that is already due.
tickless_isr() clears g_timer_started before nxsched_process_timer(), so
a watchdog re-armed from the timer interrupt reaches up_timer_start()
without up_timer_cancel(). The comparator (WORK_EN) is then still enabled
from the alarm that just fired, and tickless_setcounter() loaded the new
target into the running comparator and cleared the alarm interrupt
afterwards. A target already due at load (a short interval, or one the
interrupt reached late) raised the alarm at once and the clear removed
it: no timer interrupt followed and the scheduler slept until some other
interrupt woke it. With 1 us ticks this stops the timer within seconds.
Disable the comparator before loading the new target, then clear and
enable the interrupt, then enable the comparator. This is the order
ESP-IDF's systimer_hal_set_alarm_target() uses on chips with
SOC_SYSTIMER_ALARM_MISS_COMPENSATE, which includes the ESP32-S3.
Assisted-by: Claude:claude-opus-5-5
Signed-off-by: Max Kriegleder <[email protected]>
---
arch/xtensa/src/esp32s3/esp32s3_tickless.c | 20 +++++++++++++++-----
1 file changed, 15 insertions(+), 5 deletions(-)
diff --git a/arch/xtensa/src/esp32s3/esp32s3_tickless.c
b/arch/xtensa/src/esp32s3/esp32s3_tickless.c
index d830ce4017f..5dcb7be23c9 100644
--- a/arch/xtensa/src/esp32s3/esp32s3_tickless.c
+++ b/arch/xtensa/src/esp32s3/esp32s3_tickless.c
@@ -194,6 +194,13 @@ static void IRAM_ATTR tickless_setcounter(uint64_t ticks)
{
uint64_t alarm_ticks = tickless_getcounter() + ticks;
+ /* Disable the alarm while the new target is loaded. The comparator is
+ * still enabled after the previous alarm fired, so a target that is
+ * already due would fire on load, before the stale interrupt is cleared.
+ */
+
+ modifyreg32(SYSTIMER_CONF_REG, SYSTIMER_TARGET0_WORK_EN, 0);
+
/* Select unit0 to comp0 */
modifyreg32(SYSTIMER_TARGET0_CONF_REG, SYSTIMER_TARGET0_TIMER_UNIT_SEL, 0);
@@ -211,14 +218,17 @@ static void IRAM_ATTR tickless_setcounter(uint64_t ticks)
putreg32(SYSTIMER_TIMER_COMP0_LOAD, SYSTIMER_COMP0_LOAD_REG);
- /* Enable alarm */
-
- modifyreg32(SYSTIMER_CONF_REG, 0, SYSTIMER_TARGET0_WORK_EN);
-
- /* Enable interrupt */
+ /* Clear and enable the interrupt before enabling the alarm. A target
+ * that has already passed fires as soon as the alarm is enabled, and
+ * clearing the interrupt after that would lose it.
+ */
modifyreg32(SYSTIMER_INT_CLR_REG, 0, SYSTIMER_TARGET0_INT_CLR);
modifyreg32(SYSTIMER_INT_ENA_REG, 0, SYSTIMER_TARGET0_INT_ENA);
+
+ /* Enable alarm */
+
+ modifyreg32(SYSTIMER_CONF_REG, 0, SYSTIMER_TARGET0_WORK_EN);
}
/****************************************************************************