Skip to content
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

UART driver: large RX buffer may be ineffective (IDFGH-11220) #12386

Closed
3 tasks done
umisef opened this issue Oct 11, 2023 · 0 comments
Closed
3 tasks done

UART driver: large RX buffer may be ineffective (IDFGH-11220) #12386

umisef opened this issue Oct 11, 2023 · 0 comments
Assignees
Labels
Resolution: NA Issue resolution is unavailable Status: Done Issue is done internally Type: Bug bugs in IDF

Comments

@umisef
Copy link

umisef commented Oct 11, 2023

Answers checklist.

  • I have read the documentation ESP-IDF Programming Guide and the issue is not addressed there.
  • I have updated my IDF branch (master or release) to the latest version and checked that the issue is present there.
  • I have searched the issue tracker for a similar issue and not found a similar issue.

IDF version.

v5.1.1

Espressif SoC revision.

ESP32C3

Operating System used.

macOS

How did you build your project?

Command line with Make

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

None

Development Kit.

Custom design

Power Supply used.

USB

What is the expected behavior?

Using a large (in our case 180kB) RX buffer with the UART driver should cause no issues while read()ing is permanently running, say, 160kB "behind" the data being received.

What is the actual behavior?

After a while, the UART driver reports "RX Buffer Full", and then stops removing data from the UART's hardware FIFO, leading to reports of FIFO overflow.

The UART RX only resumes once the reader has read through the whole amount it is typically behind.

This is caused by the UART driver using xRingBufferReceive() on a byte ring buffer, which will give ownership of ALL the data currently in the ring buffer, or all the data up to the end of the storage area (if the data wraps), to the UART driver. The driver then parcels this data out a few bytes at a time in read() requests, but has no way of returning no-longer-needed data to the ring buffer until it has used ALL bytes for read()s.

When the reader is running considerably behind the UART RX, this can result in almost all of the RX buffer being owned by the UART, driver, and being held for an unreasonable amount of time.

Steps to reproduce.

  1. Initialise UART driver with 100kB RX buffer
  2. Have external source continuously send data to the UART
  3. In the app's main loop, use uart_get_buffered_data_len() to check how much data is available in the RX buffer
  4. Only call uart_read_bytes() whenever at least 80kB are available
  5. Note how the UART driver reports "RX buffer full" while uart_get_buffered_data_len() reports just over 80kB used, so almost 20kB unused
  6. Note also that the UART driver does not recover, and reception of data stops completely

Debug Logs.

The behaviour is completely fixed with the following modification to the UART driver, which limits the portion of the buffer owned by the driver at any one time to 1/16th:

`diff --git a/components/driver/uart/uart.c b/components/driver/uart/uart.c
index 31dc602919..3e2334a16d 100644
--- a/components/driver/uart/uart.c
+++ b/components/driver/uart/uart.c
@@ -1253,7 +1253,19 @@ int uart_read_bytes(uart_port_t uart_num, void *buf, uint32_t length, TickType_t
     }
     while (length) {
         if (p_uart_obj[uart_num]->rx_cur_remain == 0) {
-            data = (uint8_t *) xRingbufferReceive(p_uart_obj[uart_num]->rx_ring_buf, &size, (TickType_t) ticks_to_wait);
+            // EVERY byte received here is unavailable to the ISR until ALL bytes have been read.
+            // So we need to make sure we don't accidentally lock up most of the rxbuf. Instead,
+            // limit what we claim ownership of to 1/16th of the rx_buffer, or how much the current
+            // read is about to consume, whichever is more.
+            // TODO --- is there ever a reason to ask for more than 'length'? The
+            // xRingbufferReceiveUpTo() is fairly lightweight, so is it really worth avoiding it?
+            // Especially if never asking for more than length would make it possible to avoid
+            // the whole rx_head_ptr/rx_prt/rx_cur_remain overhead in the UART object?
+            size_t max_receive=p_uart_obj[uart_num]->rx_buf_size/16;
+            if (max_receive<length)
+                max_receive=length;
+
+            data = (uint8_t *) xRingbufferReceiveUpTo(p_uart_obj[uart_num]->rx_ring_buf, &size, (TickType_t) ticks_to_wait, max_receive);
             if (data) {
                 p_uart_obj[uart_num]->rx_head_ptr = data;
                 p_uart_obj[uart_num]->rx_ptr = data;
`

More Information.

No response

@umisef umisef added the Type: Bug bugs in IDF label Oct 11, 2023
@github-actions github-actions bot changed the title UART driver: large RX buffer may be ineffective UART driver: large RX buffer may be ineffective (IDFGH-11220) Oct 11, 2023
@espressif-bot espressif-bot added the Status: Opened Issue is new label Oct 11, 2023
@espressif-bot espressif-bot added Status: Selected for Development Issue is selected for development and removed Status: Opened Issue is new labels Nov 12, 2023
@espressif-bot espressif-bot added Status: Reviewing Issue is being reviewed Status: Done Issue is done internally Resolution: NA Issue resolution is unavailable and removed Status: Selected for Development Issue is selected for development Status: Reviewing Issue is being reviewed labels Nov 21, 2023
movsb pushed a commit to movsb/esp-idf that referenced this issue Dec 1, 2023
Ringbuffer usage becomes more efficient with the use of xRingbufferReceiveUpTo

Closes espressif#12386
espressif-bot pushed a commit that referenced this issue Dec 8, 2023
Ringbuffer usage becomes more efficient with the use of xRingbufferReceiveUpTo

Closes #12386
espressif-bot pushed a commit that referenced this issue Jan 3, 2024
Ringbuffer usage becomes more efficient with the use of xRingbufferReceiveUpTo

Closes #12386
espressif-bot pushed a commit that referenced this issue Jan 4, 2024
Ringbuffer usage becomes more efficient with the use of xRingbufferReceiveUpTo

Closes #12386
espressif-bot pushed a commit that referenced this issue Jan 5, 2024
Ringbuffer usage becomes more efficient with the use of xRingbufferReceiveUpTo

Closes #12386
hathach pushed a commit to adafruit/esp-idf that referenced this issue Mar 27, 2024
Ringbuffer usage becomes more efficient with the use of xRingbufferReceiveUpTo

Closes espressif#12386
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Resolution: NA Issue resolution is unavailable Status: Done Issue is done internally Type: Bug bugs in IDF
Projects
None yet
Development

No branches or pull requests

3 participants