espressif / esp-idf

Espressif IoT Development Framework. Official development framework for Espressif SoCs.
Apache License 2.0
12.99k stars 7.12k forks source link

ESP32-S3: USB-JTAG not working after esp32_reset() or Watchdog-Reset when TinyUSB CDC was initialized in application once (IDFGH-8354) #9826

Closed rikaiot closed 10 months ago

rikaiot commented 1 year ago

Answers checklist.

IDF version.

v4.4.2

Operating System used.

Windows

How did you build your project?

Command line with idf.py

If you are using Windows, please specify command line type.

CMD

Development Kit.

ESP32-S3-DevKitM-1

Power Supply used.

USB

What is the expected behavior?

USB-JTAG is enabled by default, so when connecting the device via USB-Plug (not UART) to the PC the board is recognized as a JTAG-Interface in the device manager.

As soon as using TinyUSB CDC driver in the application by calling tinyusb_driver_install() and tusb_cdc_acm_init() the USB-JTAG is disabled and the device is recognized as a serial device.

When pressing the reset button or calling esp_reset() the board should again be recognized as USB-JTAG until the TinyUSB driver is loaded in the application.

What is the actual behavior?

When the TinyUSB has been loaded once, calling esp_reset() or forcing a watchdog reset does not make the device to be recognized as USB-JTAG any more.

Only when the reset button is pressed, the device is recognized as USB-JTAG again.

Steps to reproduce.

  1. Connect the ESP32-S3-DevKitM-1 dev board to the pc via USB-Plug (not UART) - the device is recognized as USB-JTAG.

  2. Initialize TinyUSB in the application by calling tinyusb_driver_install() and tusb_cdc_acm_init() after some delay.

  3. The board is recognized as serial device.

  4. Call esp_restart() or force and watchdog reset.

  5. The board is not recognized as USB-JTAG again.

  6. Press reset button - the board is recognized as USB-JTAG again until the TinyUSB initialization is done.

Debug Logs.

No response

More Information.

It seems that the USB-JTAG is disabled by TinyUSB and is not reactivated again when calling esp_restart().

johnboiles commented 11 months ago

Also running into this. I wonder if there's a bit in a register somewhere we could flip before calling esp_restart that would allow the USB-JTAG device to come back up. Or perhaps adding something like periph_module_disable(usb_otg_periph_signal.module); before rebooting. I'll report back if we're able to figure out a workaround to this.

johnboiles commented 10 months ago

Things i've tried (that haven't worked):

I made a minimal repro case to demonstrate the issue: https://github.com/johnboiles/esp32-tinyusb-reset-bug

The only workaround we've found is to hook up a GPIO to the RESET pin and force the chip to reset itself. But it seems like there's probably just some register somewhere that needs to get reset at shutdown time.

I've been doing my testing on IDF 5.1 (cbce221e88).

The problem seems to be where in usb_new_phy since if you remove that line the USB/JTAG doesn't go away https://github.com/espressif/idf-extra-components/blob/master/usb/esp_tinyusb/tinyusb.c#L58

johnboiles commented 10 months ago

I think I got it! TL;DR is something like this:

#include <esp_private/usb_phy.h>
extern usb_phy_handle_t *phy_hdl_ptr;
usb_del_phy(*phy_hdl_ptr);
usb_phy_config_t phy_conf = {
    .controller = USB_PHY_CTRL_SERIAL_JTAG,
};
usb_phy_handle_t jtag_phy;
usb_new_phy(&phy_conf, &jtag_phy);

Where phy_hdl_ptr is a global from TinyUSB by adding this line to tinyusb.c (since you can't extern the static usb_phy_handle_t phy_hdl; from tinyusb.c)

usb_phy_handle_t *phy_hdl_ptr = &phy_hdl;

So now the question is how to we productionize this. Probably if there were a tinyusb_driver_uninstall in tinyusb.c that would be enough.

johnboiles commented 10 months ago

PR to propose tinyusb_driver_uninstall: https://github.com/espressif/idf-extra-components/pull/229

tore-espressif commented 10 months ago

@rikaiot thank you for reporting and @johnboiles thank you for your thorough investigation! I'll review your changes ASAP. I need to discuss with colleagues whether we want to change (fix) the behaviout of esp_restart(), too

tore-espressif commented 10 months ago

Hello @johnboiles here is some update:

The restart behavior you see is actually intentional. If a user wants to perform a software reset, he usually does not want to re-enumerate as different device (JTAG debug) during the process.

Moreover, changing this would be a functional breaking change, so we will fix this with the changes you proposed in esp_tinyusb component.

Even more info:

johnboiles commented 10 months ago

Makes sense to me that it's not the typical case to re-enumerate the USB/JTAG device after configuring TinyUSB.

Is there anywhere in the documentation that mentions which peripherals maintain their state across calls to esp_restart()?

tore-espressif commented 10 months ago

Is there anywhere in the documentation that mentions which peripherals maintain their state across calls to esp_restart()?

All of them, except Wi-Fi, BT, UART0, SPI1, and legacy timers

https://docs.espressif.com/projects/esp-idf/en/latest/esp32s3/api-reference/system/misc_system_api.html#_CPPv411esp_restartv

EDIT: Documentation is actually not entirely correct, as on ESP32-S2 and newer chips, if memory protection was enabled (and it was enabled by default) we did reset all the digital peripherals. This was fixed by https://github.com/espressif/esp-idf/commit/65bc1ed05530f1de49006f194966ec21007b17e8

tore-espressif commented 10 months ago

esp_tinyusb v1.4.1 relased https://components.espressif.com/components/espressif/esp_tinyusb