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);
 }
 
 /****************************************************************************

Reply via email to