8000 esp32: Fix ESP32-C3 usb serial/jtag on IDF v5.0.4, clean up native USB-CDC by projectgus · Pull Request #15727 · micropython/micropython · GitHub
[go: up one dir, main page]

Skip to content

esp32: Fix ESP32-C3 usb serial/jtag on IDF v5.0.4, clean up native USB-CDC #15727

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged

Conversation

projectgus
Copy link
Contributor
@projectgus projectgus commented Aug 27, 2024

Summary

  • Closes ESP32_GENERIC_C3 binary does not run if compiled with IDF 5.0.4 #15701 which is a regression introduced with esp32: Update ESP-IDF to v5.2.2, optimise size #15523 when using IDF v5.0.x.
  • Fixes a lower impact regression from the same PR where on ESP-IDF >= 5.1 the ESP32-S3 would come up using the USB Serial/JTAG interface instead of native USB. The obvious fixes for the first regression all made this second regression appear on all ESP-IDF versions.
  • Overall the logic for how the USB function is assigned were a bit hard to follow, and depended on some default ESP-IDF config selections.
  • There are now two new MicroPython config macros, fully independent of ESP-IDF config:
    • MICROPY_HW_USB_CDC for native USB-CDC which defaults to 1 on supported SoCs (currently ESP32-S2,S3).
    • MICROPY_HW_ESP_USB_SERIAL_JTAG to use the serial/JTAG peripheral which defaults to 1 on supported SoCs that don't have native USB-CDC enabled.
  • Combined with the existing MICROPY_HW_ENABLE_UART_REPL this gives full control in the MicroPython board header over which serial options are enabled. This somewhat helps with ESP32-S3 forcing use of USB OTG #14217 although it's not a fix for it.
  • Support in the code for setting CONFIG_ESP_CONSOLE_USB_CDC on S3 has been removed. This already didn't work on master, and no boards used it. Getting both ESP-IDF stdio and MicroPython stdio on the USB-CDC is actually now possible with this PR (didn't work without it), but requires setting both the sdkconfig option and the MicroPython build option independently.

Testing

Build and verified REPL works with ESP-IDF v5.0.4 and v5.2.2 on:

  • ESP32-C3 DevKitC (external USB/serial)
  • Seeed XIAO C3 (internal USB/serial)
  • ESP32-S3 DevKitC (both "UART" and "native USB" ports)

Trade-offs and Alternatives

We could drop ESP-IDF v5.0.4 support now, but nightly builds have only just switched over so good to wait a little while and see if any other issues crop up. There is still the S3 issue to resolve.

There is a weird inconsistency where ESP-IDF stdout (and logs) go to different places than MicroPython stdout. However that's already the case on master and this PR makes it a little easier to reason about (because the ESP-IDF stdio config and the MicroPython stdio config are now fully independent of each other). If necessary then we could add a hook that routes ESP-IDF stdout into MicroPython stdout as part of app_main(), but I think safe to leave for now.

Future Work

  • Disabling both USB config macros doesn't disable the USB port after MicroPython starts (currently, or after this change). PR to follow for this.

This work was funded through GitHub Sponsors.

@projectgus projectgus marked this pull request as draft August 27, 2024 07:09
@projectgus projectgus marked this pull request as ready for review August 27, 2024 07:12
@projectgus
Copy link
Contributor Author

Limitation of this PR on ESP32-S3, even if you set:

#define MICROPY_HW_USB_CDC (0)
#define MICROPY_HW_ESP_USB_SERIAL_JTAG (0)

Then the USB serial/JTAG still enables. This is no worse than the behaviour on master, though, so will submit a follow-up PR for it.

@projectgus projectgus mentioned this pull request Aug 27, 2024
2 tasks
@projectgus
Copy link
Contributor Author
projectgus commented Aug 27, 2024

I had some second thoughts about this last night, because it commits MicroPython to being the canonical source for its stdio routing (i.e. whether to connect stdio to UART serial, USB Serial/JTAG peripheral, and/or USB-CDC device with full USB OTG peripheral). The ESP-IDF stdio is not really kept consistent with this (in current master and in this PR). Currently on master the ESP-IDF/libc stdout can be routed to UART serial and USB serial/JTAG, but not to the USB-CDC serial device - IIUC that "port" only ever gets MicroPython stdio.

This is mostly fine, I think it only really comes up when enabling ESP-IDF logging or if someone links a user C module that calls libc printf() or similar.

However currently in master the ESP-IDF sdkconfig is the canonical source for both stdio configs, and this PR changes it to make MicroPython build macros the canonical source for the MicroPython stdio config. So there's potential for more divergence.

I think the main limitation is that there are currently MicroPython configs that ESP-IDF's stdio can't support with its default Kconfig settings, i.e. output to UART0 and USB-CDC simultaneously. Maybe of more concern, configuring everything in ESP-IDF probably makes it fiddlier to change stdio around at runtime.

So maybe this PR doubling down on the current approach is OK. If we want to sync up ESP-IDF/libc output (so e.g. IDF logs can go to CDC port) then we can do it by installing custom stdout or logging handlers when MicroPython starts up.

#ifndef MICROPY_HW_ESP_USB_SERIAL_JTAG
#define MICROPY_HW_ESP_USB_SERIAL_JTAG (CONFIG_ESP_CONSOLE_USB_SERIAL_JTAG_ENABLED || ( \
ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(5, 1, 0) && ( \
CONFIG_ESP_CONSOLE_SECONDARY_USB_SERIAL_JTAG || \
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think we ever checked/used CONFIG_ESP_CONSOLE_SECONDARY_USB_SERIAL_JTAG, so could probably leave that out of this macro definition. But also doesn't hurt to include it.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually... maybe the inclusion of this SECONDARY option is why S3 changes behaviour?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, this is why S3 changed behaviour. That option is set by default in the ESP-IDF config.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So, isn't it possible to just exclude CONFIG_ESP_CONSOLE_SECONDARY_USB_SERIAL_JTAG from the check, and then it will work on S3?

Said differently: without this PR, S3 works. And the only thing that needs fixing is a rename of CONFIG_ESP_CONSOLE_USB_SERIAL_JTAG_ENABLED to CONFIG_ESP_CONSOLE_USB_SERIAL_JTAG for IDF < 5.1. And the latter can be achieved probably with:

#if CONFIG_ESP_CONSOLE_USB_SERIAL_JTAG
#define CONFIG_ESP_CONSOLE_USB_SERIAL_JTAG_ENABLED 1
#endif

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think that suggested patch is actually equivalent behaviour-wise to the first commit in this PR, although I didn't fully understand that until just now when I went to look into it again:

Serial over S3 native USB port currently works on master in two different ways depending on which ESP-IDF version you have, with <5.1 you get native USB and with >=5.1 you get USB Serial/JTAG peripheral (because both CONFIG_ESP_CONSOLE_SECONDARY_USB_SERIAL_JTAG and CONFIG_ESP_CONSOLE_USB_SERIAL_JTAG_ENABLED are set by default on both S3 and C3).
That particular regression (switching to the USB/JTAG peripheral) was introduced by me unwittingly.

With the patch (either the three line one or the first commit in this PR by itself) then S3 would become USB Serial/JTAG regardless of ESP-IDF version.

So I think the three line patch would indeed fix the immediate issue with C3 on v5.0.4 and wouldn't make anything else obviously worse. Maybe that is worth doing now as a quick PR to unbreak C3 on nightly builds.

However, it also seems pretty clear that we should clean this up so it's easier to reason about. Either with the approach in this PR, or with something else.

@dpgeorge
Copy link
Member

Applying this fix unexpectedly caused ESP32-S3 to come up using the USB Serial/JTAG interface instead of native USB. The config options here were a bit hard to follow, so I added another commit that refactors this as well.

I don't understand why this happened with S3. Also, is this problem with S3 only on IDF 5.0.4, or only on IDF 5.2.2, or both?

@projectgus
Copy link
Contributor Author

I don't understand why this happened with S3. Also, is this problem with S3 only on IDF 5.0.4, or only on IDF 5.2.2, or both?

See reply inline at #15727 (comment) . The first commit changes S3 behaviour on both versions.

I can sit down next week and try to find a cleaner way to refactor this. I tried for this PR but it's fiddly (see #15727 (comment)).

@andrewleech
Copy link
Contributor

I for one fully support stdio management in micropython, rather than via IDF.

Tangential to this change, I've been looking at the way all stm32 stdio is currently run via dupterm (if enabled) during my in-progress efforts to migrate stm32 to tinyusb.

The advantage of this is full runtime / application support for enabling & disabling any combination of peripherals for stdio.
Also, once disconnected from stdio, the cdc object can be used by application code as a standard stream object for custom protocol use.

This will require a stream class wrapper around the usb cdc code and registering it along with any uarts etc with dupterm, which I'm keen to re-implement in the shared tinyusb codebase.

@projectgus projectgus force-pushed the bugfix/esp32c3_usb_serial_jtag branch from 50aa3bb to be77a35 Compare August 30, 2024 05:44
@projectgus
Copy link
Contributor Author

I for one fully support stdio management in micropython, rather than via IDF.

@andrewleech @dpgeorge After thinking about this some more I've rewritten this PR to completely decouple the ESP-IDF stdio config and the MicroPython stdio config. They were already not equivalent(*), so I think this actually makes it way easier to reason about.

(*) And could not be made equivalent because if you switch stdout to USB-CDC in ESP-IDF config then it disables UART stdout.

Have rewritten the PR description as well, and re-ran the tests mentioned there. PTAL.

@andrewleech
Copy link
Contributor

MICROPY_HW_USB_CDC for native USB-CDC which defaults to 1 on supported SoCs (currently ESP32-S3).

This should be S2 as well I believe?

usb_serial_jtag_init();
#elif CONFIG_USB_OTG_SUPPORTED
#elif MICROPY_HW_USB_CDC
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I like this, using MICROPY_HW_xxx config options exclusively makes it easier to follow.

But, it seems this will clash with #15108, which uses MICROPY_HW_ENABLE_USBDEV here instead of MICROPY_HW_USB_CDC. Maybe the former is better because you want USB to be initialised if any USB mode is used (eg just MSC)?

I guess a lot of this PR will clash with #15108, eg the changes to mphalport.c. Don't want to have our efforts here wasted if #15108 will need to change things anyway.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, agree that eventually we want to split this into MICROPY_HW_ENABLE_USBDEV and MICROPY_HW_USB_CDC the same as the rp2 port. This did cross my mind when creating this, but I figured at the moment these two macros would be equivalent - so leave splitting them out for when introducing the functionality that needs to differentiate them. I can see the case for adding it now, though - a little less churn.

@andrewleech Is doing this one way or the other way going to be easier for you?

Copy link
Contributor Author
@projectgus projectgus Sep 3, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looked at #15108 again and can see the point, so I've pushed commit efe88a2 that adds MICROPY_HW_ENABLE_USBDEV. It's a bit odd because if you build with #define MICROPY_HW_ENABLE_USBDEV (1) and #define MICROPY_HW_USB_CDC (0) then the port enumerates as a USB-CDC device (as the descriptor is configured in ESP-IDF) but the port doesn't do anything.

It might be better if we drop that commit from here, and @andrewleech adds it to his branch the next time he rebases #15108. Not sure.

(EDIT: Updated commit hash after rebase and reformat.)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's make the change to use MICROPY_HW_ENABLE_USBDEV in #15108 instead. That keeps this PR simple.

@projectgus
Copy link
Contributor Author
projectgus commented Sep 2, 2024

This should be S2 as well I believe?

I always forget about the ESP32-S2, you're entirely correct! Have updated the PR description.

@projectgus projectgus force-pushed the bugfix/esp32c3_usb_serial_jtag branch from be77a35 to 083f4da Compare September 2, 2024 23:59
Regression in 0a11832 in IDF 5.0.x where macro
CONFIG_ESP_CONSOLE_USB_SERIAL_JTAG_ENABLED is not defined.

With this patch, ESP32-S3 still USB Serial/JTAG incorrectly (now on all
ESP-IDF versions).

Closes micropython#15701

This work was funded through GitHub Sponsors.

Signed-off-by: Angus Gratton <angus@redyak.com.au>
@projectgus projectgus force-pushed the bugfix/esp32c3_usb_serial_jtag branch 2 times, most recently from efe88a2 to 97a5a49 Compare September 3, 2024 04:26
This fixes issue of ESP32-S3 switching its config over to USB serial/JTAG
instead of native USB.

The the existing logic was hard to follow, adding this config macro makes
it easier to see which USB is in use and to have board definitions that
enable/disable different USB levels.

This commit also drops (nominal) support for manually setting
CONFIG_ESP_CONSOLE_USB_CDC in sdkconfig. No included board configs use this
and it didn't seem to work (if secondary console was set to the default USB
Serial/JTAG then there is no serial output on any port, and if secondary
console was set to None then linking fails.) Can be re-added if there's a
use case for it.

This work was funded through GitHub Sponsors.

Signed-off-by: Angus Gratton <angus@redyak.com.au>
@projectgus projectgus force-pushed the bugfix/esp32c3_usb_serial_jtag branch from 97a5a49 to 5e692d0 Compare September 3, 2024 04:28
@projectgus projectgus added this to the release-1.24.0 milestone Sep 3, 2024
@dpgeorge dpgeorge merged commit 5e692d0 into micropython:master Sep 3, 2024
8 checks passed
@dpgeorge
Copy link
Member
dpgeorge commented Sep 3, 2024

Unfortunately it looks like this PR broke ARDUINO_NANO_ESP32:

micropython/shared/tinyusb/mp_usbd_cdc.c: In function 'tud_sof_cb':
micropython/shared/tinyusb/mp_usbd_cdc.c:134:9: error: implicit declaration of function 'tud_sof_cb_enable'; did you mean 'dcd_sof_enable'? [-Werror=implicit-function-declaration]
  134 |         tud_sof_cb_enable(false);
      |         ^~~~~~~~~~~~~~~~~
      |         dcd_sof_enable

That board defines MICROPY_HW_ENABLE_USBDEV...

The option !MICROPY_EXCLUDE_SHARED_TINYUSB_USBD_CDC is no longer used anywhere, maybe it needs to be enabled for this board?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

ESP32_GENERIC_C3 binary does not run if compiled with IDF 5.0.4
3 participants
0