esp32: Fix USB Serial/JTAG TX stall due to full EP buffer - #19656
Open
projectgus wants to merge 3 commits into
Open
esp32: Fix USB Serial/JTAG TX stall due to full EP buffer#19656projectgus wants to merge 3 commits into
projectgus wants to merge 3 commits into
Conversation
Update to the fix in eb5d89c. The sequence of events which caused this problem: 1. USJ has no data to send to host (TXFIFO/IN EP), so the SERIAL_IN_EMPTY interrupt bit is set. 2. MicroPython sends exactly 64 bytes to host, queued into the TXFIFO. The hardware will start flushing to the host. 3. The SERIAL_IN_EMPTY interrupt is enabled, but because the bit was already set it triggers. 4. ISR calls txfifo_flush() but this is a no-op as the TXFIFO is already being flushed. 5. ISR is now disabled until the next time MicroPython tries to send data. Because there is exactly 64 bytes in the buffer (i.e. EP size), the host will assume there is more data to come and will wait for either a Zero Length Packet (ZLP) or more data. This won't happen and the data won't be received until the next time MicroPython tries to TX serial data to the host. Fix is to clear the SERIAL_IN_EMPTY interrupt status to avoid stale interrupts, and to be explicit about when we flush immediately on send versus when we need to wait for the TXFIFO to empty. This work was funded through GitHub Sponsors. Signed-off-by: Angus Gratton <angus@redyak.com.au>
Necessary on ESP32 when testing a native USB or USJ, as the UART TX buffer can be quite large and is flushed before the soft reset completes. (Which from the perspective of the native port just looks like it doesn't respond.) This work was funded through GitHub Sponsors. Signed-off-by: Angus Gratton <angus@redyak.com.au>
Contributor
Author
|
@Josverl if you have a chance, could you please run through the reproducers you have with this PR and see if you can break it? Thanks! |
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## master #19656 +/- ##
=======================================
Coverage 98.58% 98.58%
=======================================
Files 182 182
Lines 23322 23322
Branches 5 5
=======================================
Hits 22993 22993
Misses 328 328
Partials 1 1
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
This work was funded through GitHub Sponsors. Signed-off-by: Angus Gratton <angus@redyak.com.au>
Contributor
Author
|
Pushed one more one-line commit here to fix an integer overflow in the host timeout that was bugging me. 😁 |
|
Code size report: |
Contributor
|
Many thanks for the quick fix. I have tested on WIndows11 & WSL2 Ubuntu
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Closes #19651.
I wasn't able to reproduce using standalone reproducer from this issue, but I was able to reproduce by installing
micropython-stubber==1.28.3and runningstubber mcu --format py -s /dev/ttyACM0as per issue description.This failure is also visible by running
./tests/serial_test.pyon the Espressif USB serial/jtag port. The echo test fails when the length is 64 bytes (a clue!) and is out of sync from then on:The Espressif USB Serial/JTAG peripheral (USJ) manages a 64 byte TX FIFO of bytes to send to the host (via the IN endpoint of the USB device). This TXFIFO automatically flushes whenever it is full, but if exactly 64 bytes (the endpoint length) is queued then it expects a manually triggered txfifo flush after it empties, in order to send a ZLP (zero length packet) to the host to signal that the transfer is done. Otherwise the host will assume there is more data coming and wait.
The bug is caused by this sequence of evens:
Because there is exactly 64 bytes in the buffer (i.e. EP size), the USB host will assume there is more data to come and will wait for either a ZLP or more data. Because the interrupt is disabled this won't happen, and that data won't be received on the host until the next time MicroPython tries to TX serial data to the host.
Fix is to clear the SERIAL_IN_EMPTY interrupt status to avoid stale interrupts, and to be explicit about when we flush immediately on send versus when we need to wait for the TXFIFO to empty.
(Debugging tools used to figure this out were the Linux
usbmonkernel module with Wireshark, and the "grabserial" tool to compare timings on the traditional UART - viagrabserial -d /dev/ttyUSB0 -b 115200 --hex-ascii -T.)As per the issue report, this is not a v1.29 regression (double checked by reproducing on ESP32_GENERIC_C3, MicroPython v1.28, and IDF v5.5.1.)
Test fix included
Second commit is a fix for the
serial_test.pyprogram when testing the USB Serial/JTAG Peripheral, as the DATA IN test would time out waiting for a soft reset when starting the next test in the sequence. This is because MicroPython flushes the plain UART peripheral on soft reset, and this could take more than the 0.1 seconds that was allowed for (for the 16384 byte test it could take a couple of seconds).Testing
tools/mpremote/tests/run-mpremote-tests.pyon all three boards. Pass before and after this fix.Trade-offs and Alternatives
Generative AI
I did not use generative AI tools when creating this PR.