Skip to content

Commit 8298cdc

Browse files
committed
fix(usb_resets): Fix resetting in USB-OTG and USB-Serial/JTAG modes
Closes espressif#970
1 parent 1b15738 commit 8298cdc

File tree

11 files changed

+222
-74
lines changed

11 files changed

+222
-74
lines changed

.gitlab-ci.yml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -297,6 +297,8 @@ target_esp32s2_usbcdc:
297297
extends: .target_esptool_test
298298
tags:
299299
- esptool_esp32s2_cdc_target
300+
variables:
301+
ESPTOOL_TEST_USB_OTG: "1"
300302
script:
301303
- coverage run --parallel-mode -m pytest ${CI_PROJECT_DIR}/test/test_esptool.py --port /dev/serial_ports/ESP32S2_USBCDC --chip esp32s2 --baud 115200
302304

@@ -347,6 +349,8 @@ target_esp32s3_usbcdc:
347349
extends: .target_esptool_test
348350
tags:
349351
- esptool_esp32s3_cdc_target
352+
variables:
353+
ESPTOOL_TEST_USB_OTG: "1"
350354
script:
351355
- coverage run --parallel-mode -m pytest ${CI_PROJECT_DIR}/test/test_esptool.py --port /dev/serial_ports/ESP32S3_USBCDC --chip esp32s3 --baud 115200
352356

docs/en/esptool/advanced-options.rst

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,8 +31,8 @@ The ``--after`` argument allows you to specify whether the chip should be reset
3131

3232
.. list::
3333

34-
* ``--after hard_reset`` is the default. The DTR serial control line is used to reset the chip into a normal boot sequence.
35-
:esp8266:* ``--after soft_reset`` This runs the user firmware, but any subsequent reset will return to the serial bootloader. This was the reset behaviour in esptool v1.x.
34+
* ``--after hard_reset`` is the default. The RTS serial control line is used to reset the chip into a normal boot sequence.
35+
:esp8266: * ``--after soft_reset`` runs the user firmware, but any subsequent reset will return to the serial bootloader. This was the reset behaviour in esptool v1.x.
3636
* ``--after no_reset`` leaves the chip in the serial bootloader, no reset is performed.
3737
* ``--after no_reset_stub`` leaves the chip in the stub bootloader, no reset is performed.
3838

esptool/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
"merge_bin",
1919
"read_flash",
2020
"read_flash_status",
21+
"read_flash_sfdp",
2122
"read_mac",
2223
"read_mem",
2324
"run",

esptool/targets/esp32c2.py

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# SPDX-FileCopyrightText: 2014-2022 Fredrik Ahlberg, Angus Gratton,
1+
# SPDX-FileCopyrightText: 2014-2024 Fredrik Ahlberg, Angus Gratton,
22
# Espressif Systems (Shanghai) CO LTD, other contributors as noted.
33
#
44
# SPDX-License-Identifier: GPL-2.0-or-later
@@ -63,6 +63,12 @@ class ESP32C2ROM(ESP32C3ROM):
6363
[0x4037C000, 0x403C0000, "IRAM"],
6464
]
6565

66+
RTCCNTL_BASE_REG = 0x60008000
67+
RTC_CNTL_WDTCONFIG0_REG = RTCCNTL_BASE_REG + 0x0084
68+
RTC_CNTL_WDTCONFIG1_REG = RTCCNTL_BASE_REG + 0x0088
69+
RTC_CNTL_WDTWPROTECT_REG = RTCCNTL_BASE_REG + 0x009C
70+
RTC_CNTL_WDT_WKEY = 0x50D83AA1
71+
6672
UF2_FAMILY_ID = 0x2B88D29C
6773

6874
KEY_PURPOSES: Dict[int, str] = {}
@@ -130,6 +136,9 @@ def _post_connect(self):
130136
self.stub_is_disabled = True
131137
self.IS_STUB = False
132138

139+
def hard_reset(self):
140+
ESPLoader.hard_reset(self)
141+
133142
""" Try to read (encryption key) and check if it is valid """
134143

135144
def is_flash_encryption_key_valid(self):

esptool/targets/esp32c3.py

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# SPDX-FileCopyrightText: 2014-2022 Fredrik Ahlberg, Angus Gratton,
1+
# SPDX-FileCopyrightText: 2014-2024 Fredrik Ahlberg, Angus Gratton,
22
# Espressif Systems (Shanghai) CO LTD, other contributors as noted.
33
#
44
# SPDX-License-Identifier: GPL-2.0-or-later
@@ -83,6 +83,7 @@ class ESP32C3ROM(ESP32ROM):
8383
RTC_CNTL_SWD_WKEY = 0x8F1D312A
8484

8585
RTC_CNTL_WDTCONFIG0_REG = RTCCNTL_BASE_REG + 0x0090
86+
RTC_CNTL_WDTCONFIG1_REG = RTCCNTL_BASE_REG + 0x0094
8687
RTC_CNTL_WDTWPROTECT_REG = RTCCNTL_BASE_REG + 0x00A8
8788
RTC_CNTL_WDT_WKEY = 0x50D83AA1
8889

@@ -252,6 +253,21 @@ def _post_connect(self):
252253
if not self.sync_stub_detected: # Don't run if stub is reused
253254
self.disable_watchdogs()
254255

256+
def hard_reset(self):
257+
if self.uses_usb_jtag_serial():
258+
self.rtc_wdt_reset()
259+
else:
260+
ESPLoader.hard_reset(self)
261+
262+
def rtc_wdt_reset(self):
263+
print("Hard resetting with RTC WDT...")
264+
self.write_reg(self.RTC_CNTL_WDTWPROTECT_REG, self.RTC_CNTL_WDT_WKEY) # unlock
265+
self.write_reg(self.RTC_CNTL_WDTCONFIG1_REG, 5000) # set WDT timeout
266+
self.write_reg(
267+
self.RTC_CNTL_WDTCONFIG0_REG, (1 << 31) | (5 << 28) | (1 << 8) | 2
268+
) # enable WDT
269+
self.write_reg(self.RTC_CNTL_WDTWPROTECT_REG, 0) # lock
270+
255271
def check_spi_connection(self, spi_connection):
256272
if not set(spi_connection).issubset(set(range(0, 22))):
257273
raise FatalError("SPI Pin numbers must be in the range 0-21.")

esptool/targets/esp32c6.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# SPDX-FileCopyrightText: 2022 Fredrik Ahlberg, Angus Gratton,
1+
# SPDX-FileCopyrightText: 2024 Fredrik Ahlberg, Angus Gratton,
22
# Espressif Systems (Shanghai) CO LTD, other contributors as noted.
33
#
44
# SPDX-License-Identifier: GPL-2.0-or-later
@@ -72,6 +72,7 @@ class ESP32C6ROM(ESP32C3ROM):
7272

7373
DR_REG_LP_WDT_BASE = 0x600B1C00
7474
RTC_CNTL_WDTCONFIG0_REG = DR_REG_LP_WDT_BASE + 0x0 # LP_WDT_RWDT_CONFIG0_REG
75+
RTC_CNTL_WDTCONFIG1_REG = DR_REG_LP_WDT_BASE + 0x0004 # LP_WDT_RWDT_CONFIG1_REG
7576
RTC_CNTL_WDTWPROTECT_REG = DR_REG_LP_WDT_BASE + 0x0018 # LP_WDT_RWDT_WPROTECT_REG
7677

7778
RTC_CNTL_SWD_CONF_REG = DR_REG_LP_WDT_BASE + 0x001C # LP_WDT_SWD_CONFIG_REG

esptool/targets/esp32h2.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,12 @@
1-
# SPDX-FileCopyrightText: 2022 Fredrik Ahlberg, Angus Gratton,
1+
# SPDX-FileCopyrightText: 2024 Fredrik Ahlberg, Angus Gratton,
22
# Espressif Systems (Shanghai) CO LTD, other contributors as noted.
33
#
44
# SPDX-License-Identifier: GPL-2.0-or-later
55

66
from typing import Dict
77

88
from .esp32c6 import ESP32C6ROM
9+
from ..loader import ESPLoader
910
from ..util import FatalError
1011

1112

@@ -18,6 +19,7 @@ class ESP32H2ROM(ESP32C6ROM):
1819

1920
DR_REG_LP_WDT_BASE = 0x600B1C00
2021
RTC_CNTL_WDTCONFIG0_REG = DR_REG_LP_WDT_BASE + 0x0 # LP_WDT_RWDT_CONFIG0_REG
22+
RTC_CNTL_WDTCONFIG1_REG = DR_REG_LP_WDT_BASE + 0x0004 # LP_WDT_RWDT_CONFIG1_REG
2123
RTC_CNTL_WDTWPROTECT_REG = DR_REG_LP_WDT_BASE + 0x001C # LP_WDT_RWDT_WPROTECT_REG
2224

2325
RTC_CNTL_SWD_CONF_REG = DR_REG_LP_WDT_BASE + 0x0020 # LP_WDT_SWD_CONFIG_REG
@@ -77,6 +79,9 @@ def get_crystal_freq(self):
7779
# ESP32H2 XTAL is fixed to 32MHz
7880
return 32
7981

82+
def hard_reset(self):
83+
ESPLoader.hard_reset(self)
84+
8085
def check_spi_connection(self, spi_connection):
8186
if not set(spi_connection).issubset(set(range(0, 28))):
8287
raise FatalError("SPI Pin numbers must be in the range 0-27.")

esptool/targets/esp32p4.py

Lines changed: 67 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# SPDX-FileCopyrightText: 2023 Fredrik Ahlberg, Angus Gratton,
1+
# SPDX-FileCopyrightText: 2024 Fredrik Ahlberg, Angus Gratton,
22
# Espressif Systems (Shanghai) CO LTD, other contributors as noted.
33
#
44
# SPDX-License-Identifier: GPL-2.0-or-later
@@ -72,6 +72,10 @@ class ESP32P4ROM(ESP32ROM):
7272

7373
FLASH_ENCRYPTED_WRITE_ALIGN = 16
7474

75+
UARTDEV_BUF_NO = 0x4FF3FEC8 # Variable in ROM .bss which indicates the port in use
76+
UARTDEV_BUF_NO_USB_OTG = 5 # The above var when USB-OTG is used
77+
UARTDEV_BUF_NO_USB_JTAG_SERIAL = 6 # The above var when USB-JTAG/Serial is used
78+
7579
MEMORY_MAP = [
7680
[0x00000000, 0x00010000, "PADDING"],
7781
[0x40000000, 0x4C000000, "DROM"],
@@ -105,6 +109,17 @@ class ESP32P4ROM(ESP32ROM):
105109
12: "KM_INIT_KEY",
106110
}
107111

112+
DR_REG_LP_WDT_BASE = 0x50116000
113+
RTC_CNTL_WDTCONFIG0_REG = DR_REG_LP_WDT_BASE + 0x0 # LP_WDT_CONFIG0_REG
114+
RTC_CNTL_WDTCONFIG1_REG = DR_REG_LP_WDT_BASE + 0x0004 # LP_WDT_CONFIG1_REG
115+
RTC_CNTL_WDTWPROTECT_REG = DR_REG_LP_WDT_BASE + 0x0018 # LP_WDT_WPROTECT_REG
116+
RTC_CNTL_WDT_WKEY = 0x50D83AA1
117+
118+
RTC_CNTL_SWD_CONF_REG = DR_REG_LP_WDT_BASE + 0x001C # RTC_WDT_SWD_CONFIG_REG
119+
RTC_CNTL_SWD_AUTO_FEED_EN = 1 << 18
120+
RTC_CNTL_SWD_WPROTECT_REG = DR_REG_LP_WDT_BASE + 0x0020 # RTC_WDT_SWD_WPROTECT_REG
121+
RTC_CNTL_SWD_WKEY = 0x50D83AA1 # RTC_WDT_SWD_WKEY, same as WDT key in this case
122+
108123
def get_pkg_version(self):
109124
num_word = 2
110125
return (self.read_reg(self.EFUSE_BLOCK1_ADDR + (4 * num_word)) >> 20) & 0x07
@@ -191,10 +206,42 @@ def change_baud(self, baud):
191206
ESPLoader.change_baud(self, baud)
192207

193208
def _post_connect(self):
194-
pass
195-
# TODO: Disable watchdogs when USB modes are supported in the stub
196-
# if not self.sync_stub_detected: # Don't run if stub is reused
197-
# self.disable_watchdogs()
209+
if not self.sync_stub_detected: # Don't run if stub is reused
210+
self.disable_watchdogs()
211+
212+
def uses_usb_otg(self):
213+
"""
214+
Check the UARTDEV_BUF_NO register to see if USB-OTG console is being used
215+
"""
216+
if self.secure_download_mode:
217+
return False # can't detect native USB in secure download mode
218+
return self.get_uart_no() == self.UARTDEV_BUF_NO_USB_OTG
219+
220+
def uses_usb_jtag_serial(self):
221+
"""
222+
Check the UARTDEV_BUF_NO register to see if USB-JTAG/Serial is being used
223+
"""
224+
if self.secure_download_mode:
225+
return False # can't detect USB-JTAG/Serial in secure download mode
226+
return self.get_uart_no() == self.UARTDEV_BUF_NO_USB_JTAG_SERIAL
227+
228+
def disable_watchdogs(self):
229+
# When USB-JTAG/Serial is used, the RTC WDT and SWD watchdog are not reset
230+
# and can then reset the board during flashing. Disable them.
231+
if self.uses_usb_jtag_serial():
232+
# Disable RTC WDT
233+
self.write_reg(self.RTC_CNTL_WDTWPROTECT_REG, self.RTC_CNTL_SWD_WKEY)
234+
self.write_reg(self.RTC_CNTL_WDTCONFIG0_REG, 0)
235+
self.write_reg(self.RTC_CNTL_WDTWPROTECT_REG, 0)
236+
237+
# Automatically feed SWD
238+
self.write_reg(self.RTC_CNTL_SWD_WPROTECT_REG, self.RTC_CNTL_SWD_WKEY)
239+
self.write_reg(
240+
self.RTC_CNTL_SWD_CONF_REG,
241+
self.read_reg(self.RTC_CNTL_SWD_CONF_REG)
242+
| self.RTC_CNTL_SWD_AUTO_FEED_EN,
243+
)
244+
self.write_reg(self.RTC_CNTL_SWD_WPROTECT_REG, 0)
198245

199246
def check_spi_connection(self, spi_connection):
200247
if not set(spi_connection).issubset(set(range(0, 55))):
@@ -205,6 +252,21 @@ def check_spi_connection(self, spi_connection):
205252
"consider using other pins for SPI flash connection."
206253
)
207254

255+
def rtc_wdt_reset(self):
256+
print("Hard resetting with RTC WDT...")
257+
self.write_reg(self.RTC_CNTL_WDTWPROTECT_REG, self.RTC_CNTL_WDT_WKEY) # unlock
258+
self.write_reg(self.RTC_CNTL_WDTCONFIG1_REG, 5000) # set WDT timeout
259+
self.write_reg(
260+
self.RTC_CNTL_WDTCONFIG0_REG, (1 << 31) | (5 << 28) | (1 << 8) | 2
261+
) # enable WDT
262+
self.write_reg(self.RTC_CNTL_WDTWPROTECT_REG, 0) # lock
263+
264+
def hard_reset(self):
265+
if self.uses_usb_jtag_serial():
266+
self.rtc_wdt_reset()
267+
else:
268+
ESPLoader.hard_reset(self)
269+
208270

209271
class ESP32P4StubLoader(ESP32P4ROM):
210272
"""Access class for ESP32P4 stub loader, runs on top of ROM.

esptool/targets/esp32s2.py

Lines changed: 25 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,8 @@
1-
# SPDX-FileCopyrightText: 2014-2023 Fredrik Ahlberg, Angus Gratton,
1+
# SPDX-FileCopyrightText: 2014-2024 Fredrik Ahlberg, Angus Gratton,
22
# Espressif Systems (Shanghai) CO LTD, other contributors as noted.
33
#
44
# SPDX-License-Identifier: GPL-2.0-or-later
55

6-
import os
76
import struct
87
from typing import Dict
98

@@ -82,11 +81,17 @@ class ESP32S2ROM(ESP32ROM):
8281
USB_RAM_BLOCK = 0x800 # Max block size USB-OTG is used
8382

8483
GPIO_STRAP_REG = 0x3F404038
85-
GPIO_STRAP_SPI_BOOT_MASK = 0x8 # Not download mode
84+
GPIO_STRAP_SPI_BOOT_MASK = 1 << 3 # Not download mode
8685
GPIO_STRAP_VDDSPI_MASK = 1 << 4
8786
RTC_CNTL_OPTION1_REG = 0x3F408128
8887
RTC_CNTL_FORCE_DOWNLOAD_BOOT_MASK = 0x1 # Is download mode forced over USB?
8988

89+
RTCCNTL_BASE_REG = 0x3F408000
90+
RTC_CNTL_WDTCONFIG0_REG = RTCCNTL_BASE_REG + 0x0094
91+
RTC_CNTL_WDTCONFIG1_REG = RTCCNTL_BASE_REG + 0x0098
92+
RTC_CNTL_WDTWPROTECT_REG = RTCCNTL_BASE_REG + 0x00AC
93+
RTC_CNTL_WDT_WKEY = 0x50D83AA1
94+
9095
MEMORY_MAP = [
9196
[0x00000000, 0x00010000, "PADDING"],
9297
[0x3F000000, 0x3FF80000, "DROM"],
@@ -282,32 +287,27 @@ def _post_connect(self):
282287
if self.uses_usb_otg():
283288
self.ESP_RAM_BLOCK = self.USB_RAM_BLOCK
284289

285-
def _check_if_can_reset(self):
286-
"""
287-
Check the strapping register to see if we can reset out of download mode.
288-
"""
289-
if os.getenv("ESPTOOL_TESTING") is not None:
290-
print("ESPTOOL_TESTING is set, ignoring strapping mode check")
291-
# Esptool tests over USB-OTG run with GPIO0 strapped low,
292-
# don't complain in this case.
293-
return
294-
strap_reg = self.read_reg(self.GPIO_STRAP_REG)
295-
force_dl_reg = self.read_reg(self.RTC_CNTL_OPTION1_REG)
296-
if (
297-
strap_reg & self.GPIO_STRAP_SPI_BOOT_MASK == 0
298-
and force_dl_reg & self.RTC_CNTL_FORCE_DOWNLOAD_BOOT_MASK == 0
299-
):
300-
raise SystemExit(
301-
f"Error: {self.get_chip_description()} chip was placed into download "
302-
"mode using GPIO0.\nesptool.py can not exit the download mode over "
303-
"USB. To run the app, reset the chip manually.\n"
304-
"To suppress this note, set --after option to 'no_reset'."
305-
)
290+
def rtc_wdt_reset(self):
291+
print("Hard resetting with RTC WDT...")
292+
self.write_reg(self.RTC_CNTL_WDTWPROTECT_REG, self.RTC_CNTL_WDT_WKEY) # unlock
293+
self.write_reg(self.RTC_CNTL_WDTCONFIG1_REG, 5000) # set WDT timeout
294+
self.write_reg(
295+
self.RTC_CNTL_WDTCONFIG0_REG, (1 << 31) | (5 << 28) | (1 << 8) | 2
296+
) # enable WDT
297+
self.write_reg(self.RTC_CNTL_WDTWPROTECT_REG, 0) # lock
306298

307299
def hard_reset(self):
308300
uses_usb_otg = self.uses_usb_otg()
309301
if uses_usb_otg:
310-
self._check_if_can_reset()
302+
# Check the strapping register to see if we can perform RTC WDT reset
303+
strap_reg = self.read_reg(self.GPIO_STRAP_REG)
304+
force_dl_reg = self.read_reg(self.RTC_CNTL_OPTION1_REG)
305+
if (
306+
strap_reg & self.GPIO_STRAP_SPI_BOOT_MASK == 0 # GPIO0 low
307+
and force_dl_reg & self.RTC_CNTL_FORCE_DOWNLOAD_BOOT_MASK == 0
308+
):
309+
self.rtc_wdt_reset()
310+
return
311311

312312
ESPLoader.hard_reset(self, uses_usb_otg)
313313

0 commit comments

Comments
 (0)