maxikrie opened a new pull request, #21119:
URL: https://github.com/apache/nuttx/pull/21119
## Summary
With `CONFIG_ESP32S3_TICKLESS`, an alarm set from the timer interrupt for a
target that is already due is lost: no timer interrupt follows, and the
scheduler sleeps until some other interrupt wakes it.
- `tickless_isr()` clears `g_timer_started` before calling
`nxsched_process_timer()`. A watchdog re-armed from there reaches
`up_timer_start()`, which then skips `up_timer_cancel()`, so the comparator
(`WORK_EN`) is still enabled from the alarm that just fired.
- `tickless_setcounter()` loaded the new target into that 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.
- Alarms set from a task are not affected: `up_timer_start()` cancels first,
which disables the comparator before the load.
With 1 us ticks the timer stops within seconds under a periodic load; with
the default 10 ms ticks a due target at load is rarer but possible.
This PR disables the comparator before loading the new target, then clears
and enables the interrupt, then enables the comparator. That 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.
## Impact
- ESP32-S3 with `CONFIG_ESP32S3_TICKLESS`: an alarm set from the timer
interrupt for a due target fires at once instead of never.
- No change without tickless. No Kconfig, API or build changes.
## Testing
Host: Debian Linux (kernel 6.1), x86_64.
Toolchain: `xtensa-esp-elf-gcc (crosstool-NG esp-14.2.0_20251107) 14.2.0`
Build: `esp32s3-devkit:tickless` with `USEC_PER_TICK=1`,
`EXTRAFLAGS=-Werror`, no warnings. `tools/checkpatch.sh -g HEAD~1..HEAD`
passes.
Hardware: a custom ESP32-S3 board, console on the native USB CDC-ACM port
(`esp32s3-devkit:usbnsh` plus `ESP32S3_TICKLESS=y`, `USEC_PER_TICK=1`). Two
images, identical except for `esp32s3_tickless.c`: master (before) and this
change (after), built on 39dabe979e; `esp32s3_tickless.c` has not changed on
master since. Both run a small out-of-tree test command, `alarmcheck`
(source below), which waits by busy-polling so a lost alarm cannot be
rescued by the wait itself:
1. A watchdog for the current tick, 100 times (zero interval).
2. The same for a deadline 5 ticks in the past.
3. `up_timer_start()` called directly with 0-64 systimer counts, 50 times
each, checking the alarm's raw interrupt bit once the target has passed.
4. Two watchdog chains whose callbacks re-arm themselves 2-41 ticks ahead,
for 5 s, so every alarm is set from the timer interrupt. 20 ms without
either chain firing counts as a lost alarm, and both are restarted.
Tests 1-3 set the alarm from a task and pass on both; test 4 is the path
this PR fixes.
Before:
```
nsh> alarmcheck
ESP32-S3 tickless alarm check, 100 runs each, 1 us per tick
1. deadline due now: 100 of 100 fired, worst after 59 us: PASS
2. deadline passed 5 ticks ago: 100 of 100 fired, worst after 19 us: PASS
3. intervals of 0-64 counts, 50 each: none lost: PASS
4. alarms set from the timer interrupt, 303 fired in 5 s: 249 lost: FAIL
(timer stopped)
FAIL: 1 of 4 failed
```
After:
```
nsh> alarmcheck
ESP32-S3 tickless alarm check, 100 runs each, 1 us per tick
1. deadline due now: 100 of 100 fired, worst after 60 us: PASS
2. deadline passed 5 ticks ago: 100 of 100 fired, worst after 19 us: PASS
3. intervals of 0-64 counts, 50 each: none lost: PASS
4. alarms set from the timer interrupt, 345871 fired in 5 s: none lost: PASS
PASS: 0 of 4 failed
```
The same change also runs on the ESP32-S3 boards of a UWB network
(tickless, 1 us ticks, a 2.5 ms slot loop on `clock_nanosleep()`), on NuttX
12.13.0 and 13.1.0. On 13.1.0 without it, one of those boards lost its
`clock_nanosleep()` wakeups within seconds.
<details>
<summary>alarmcheck test 4 (core of the test command)</summary>
```c
static struct wdog_s g_chain[2];
static volatile uint32_t g_chain_fires[2];
static const clock_t g_chain_step[2][4] =
{
{ 5, 23, 9, 41 },
{ 13, 2, 31, 7 },
};
static void chain(wdparm_t arg)
{
int i = (int)arg;
uint32_t n = ++g_chain_fires[i];
wd_start(&g_chain[i], g_chain_step[i][n & 3], chain, arg);
}
/* The task starts both chains, then polls with up_udelay(10) for 5 s.
* If the fire count does not change for 20 ms, it counts a lost alarm
* and restarts both chains with wd_cancel()/wd_start().
*/
```
</details>
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: [email protected]
For queries about this service, please contact Infrastructure at:
[email protected]