Skip to content

Fix issues about watchdog timers on ESP32, ESP32-S2 and ESP32-S3#18968

Merged
xiaoxiang781216 merged 5 commits into
apache:masterfrom
tmedicci:bugfix/espressif_wdts
May 27, 2026
Merged

Fix issues about watchdog timers on ESP32, ESP32-S2 and ESP32-S3#18968
xiaoxiang781216 merged 5 commits into
apache:masterfrom
tmedicci:bugfix/espressif_wdts

Conversation

@tmedicci
Copy link
Copy Markdown
Contributor

Summary

Our internal CI spotted some bugs related to the watchdog timers. The general functionality was working as expected, but minor issues have appeared:

  • xtensa/esp32: Fix RWDT register offsets.

  • The RWDT register offsets were incorrectly set to ESP32-S3 values instead of ESP32 values. This was introduced when the code was
    refactored from using the local NuttX header hardware/esp32_rtccntl.h which had the correct offsets) to using the HAL library headers, and
    the offsets were moved inline into esp32_wdt.c with wrong values. Correct the offsets to match the actual ESP32 register layout from
    soc/rtc_cntl_reg.h (RTC_CNTL_WDTCONFIG0_REG at 0x8c, INT_ENA at 0x3c, etc). Without this fix, all RWDT operations (enable, configure
    timeout, enable interrupt, acknowledge interrupt, feed, write-protect) were targeting wrong memory addresses, rendering the RWDT completely
    non-functional.

  • xtensa/esp32: Fix divide-by-zero in RTC WDT clock calibration.

    • Under the BOARD_LATE_INITIALIZE boot flow, rtc_clk_xtal_freq_get() may return 0 (SOC_XTAL_FREQ_AUTO) because the XTAL frequency has
      not yet been stored to the RTC register when the WDT driver is initialized. This causes rtc_clk_cal() to divide by zero internally (EXCCAUSE=0006) when computing the RTC slow clock period. Fix by explicitly calling rtc_clk_xtal_freq_update() with the board's configured crystal frequency if rtc_clk_xtal_freq_get() returns 0. Also add a guard in esp32_wdt_settimeout() to return -EIO if ESP32_RWDT_CLK() still returns 0 cycles/ms, preventing a subsequent divide-by-zero when computing the maximum timeout.
  • xtensa/esp32: Re-apply WDT prescaler and timeout at start.

    • Same issue as ESP32-S3/S2.
  • xtensa/esp32s2: Re-apply WDT prescaler and timeout at start.

    • Same issue as ESP32-S3
  • xtensa/esp32s3: Re-apply WDT prescaler and timeout at start.

    • With the BOARD_LATE_INITIALIZE flow, board_late_initialize() runs on a kernel thread and sets the MWDT0 prescaler. However, after it
      returns, the init task (NSH) is spawned and lib_cxx_initialize() invokes constructor functions, including enable_timer_group0_for_calibration() from esp-hal-3rdparty which calls timg_ll_reset_register(0), resetting all Timer Group 0 registers (including the WDT prescaler) back to their defaults. Re-apply the prescaler, timeout, and feed the WDT counter in wdt_lh_start() just before enabling the timer. This ensures correct WDT configuration regardless of any intermediate register resets by external code.

Impact

Impact on user: Yes. It prevented setting a watchdog handler on watchdog timer 0. Now fixed.

Impact on build: No.

Impact on hardware: Yes. Xtensa-based Espressif's SoCs. Now fixed.

Impact on documentation: No.

Impact on security: No.

Impact on compatibility: No.

Testing

Just build the watchdog defconfig for such devices. For ESP32-S3, for instance:

Building

make -j distclean
./tools/configure.sh -S esp32s3-devkit:watchdog
make flash ESPTOOL_PORT=/dev/ttyUSB0 -s -j$(nproc)
picocom -b 115200 /dev/ttyUSB0

Running

Run wdog -i /dev/watchdog0 to ensure watchdog is triggering properly.

Results

nsh> wdog -i /dev/watchdog0
  ping elapsed=0
  ping elapsed=500
  ping elapsed=1000
  ping elapsed=1500
  ping elapsed=2000
  ping elapsed=2500
  ping elapsed=3000
  ping elapsed=3500
  ping elapsed=4000
  ping elapsed=4500
  NO ping elapsed=5000
  NO ping elapsed=5500
  NO ping elapsed=6000
ESP-ROM:esp32s3-20210327
Build:Mar 27 2021
rst:0x7 (TG0WDT_SYS_RST),boot:0x8 (SPI_FAST_FLASH_BOOT)
Saved PC:0x420257f6
SPIWP:0xee
mode:DIO, clock div:2
load:0x3fc8e560,len:0x167c
load:0x40374000,len:0x8224
load:0x50000000,len:0x24
SHA-256 comparison failed:
Calculated: b20c9f31f4bc9eaa14c7e93b25b388f5f4888e1aa350184ecc4246c6e449d543
Expected: 00000000f0660000000000000000000000000000000000000000000000000000
Attempting to boot anyway...
entry 0x40374ed0
*** Booting NuttX ***
dram: lma 0x00000020 vma 0x3fc8e560 len 0x167c   (5756)
iram: lma 0x000016a4 vma 0x40374000 len 0x8224   (33316)
rtc: lma 0x000098d0 vma 0x50000000 len 0x24     (36)
padd: lma 0x00009908 vma 0x00000000 len 0x66f0   (26352)
imap: lma 0x00010000 vma 0x42010000 len 0x163a0  (91040)
padd: lma 0x000263a8 vma 0x00000000 len 0x9c50   (40016)
dmap: lma 0x00030000 vma 0x3c040000 len 0x34a0   (13472)
total segments stored 7

NuttShell (NSH) NuttX-10.4.0
nsh> 

tmedicci added 5 commits May 26, 2026 13:28
With the BOARD_LATE_INITIALIZE flow, board_late_initialize() runs
on a kernel thread and sets the MWDT0 prescaler. However, after it
returns, the init task (NSH) is spawned and lib_cxx_initialize()
invokes constructor functions, including
enable_timer_group0_for_calibration() from esp-hal-3rdparty which
calls timg_ll_reset_register(0), resetting all Timer Group 0
registers (including the WDT prescaler) back to their defaults.

Re-apply the prescaler, timeout, and feed the WDT counter in
wdt_lh_start() just before enabling the timer. This ensures
correct WDT configuration regardless of any intermediate register
resets by external code.

Signed-off-by: Tiago Medicci Serrano <tiago.medicci@espressif.com>
Same issue as ESP32-S3: with BOARD_LATE_INITIALIZE, the constructor
function enable_timer_group0_for_calibration() resets Timer Group 0
registers after board_late_initialize() has configured the MWDT0
prescaler, causing the watchdog to fire at an incorrect rate.

Re-apply the prescaler, timeout, and feed the WDT counter in
wdt_lh_start() just before enabling the timer.

Signed-off-by: Tiago Medicci Serrano <tiago.medicci@espressif.com>
Same issue as ESP32-S3/S2: with BOARD_LATE_INITIALIZE, the
constructor function enable_timer_group0_for_calibration() resets
Timer Group 0 registers after board_late_initialize() has
configured the MWDT0 prescaler, causing the watchdog to fire at an
incorrect rate.

Re-apply the prescaler, timeout, and feed the WDT counter in
esp32_wdt_start() just before enabling the timer.

Signed-off-by: Tiago Medicci Serrano <tiago.medicci@espressif.com>
Under the BOARD_LATE_INITIALIZE boot flow, rtc_clk_xtal_freq_get()
may return 0 (SOC_XTAL_FREQ_AUTO) because the XTAL frequency has
not yet been stored to the RTC register when the WDT driver is
initialized. This causes rtc_clk_cal() to divide by zero
internally (EXCCAUSE=0006) when computing the RTC slow clock
period.

Fix by explicitly calling rtc_clk_xtal_freq_update() with the
board's configured crystal frequency if rtc_clk_xtal_freq_get()
returns 0. Also add a guard in esp32_wdt_settimeout() to return
-EIO if ESP32_RWDT_CLK() still returns 0 cycles/ms, preventing
a subsequent divide-by-zero when computing the maximum timeout.

Signed-off-by: Tiago Medicci Serrano <tiago.medicci@espressif.com>
The RWDT register offsets were incorrectly set to ESP32-S3 values
instead of ESP32 values. This was introduced when the code was
refactored from using the local NuttX header hardware/esp32_rtccntl.h
(which had the correct offsets) to using the HAL library headers, and
the offsets were moved inline into esp32_wdt.c with wrong values.

Correct the offsets to match the actual ESP32 register layout from
soc/rtc_cntl_reg.h (RTC_CNTL_WDTCONFIG0_REG at 0x8c, INT_ENA at
0x3c, etc). Without this fix, all RWDT operations (enable, configure
timeout, enable interrupt, acknowledge interrupt, feed, write-protect)
were targeting wrong memory addresses, rendering the RWDT completely
non-functional.

Signed-off-by: Tiago Medicci Serrano <tiago.medicci@espressif.com>
@github-actions github-actions Bot added Arch: xtensa Issues related to the Xtensa architecture Size: M The size of the change in this PR is medium labels May 26, 2026
@xiaoxiang781216 xiaoxiang781216 merged commit fc5c01d into apache:master May 27, 2026
15 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Arch: xtensa Issues related to the Xtensa architecture Size: M The size of the change in this PR is medium

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants