Reading the UART

kbaud1
Posts: 71
Joined: Wed Jan 17, 2018 11:55 pm

Reading the UART

Postby kbaud1 » Mon Mar 23, 2020 3:32 pm

I ran into a problem today with the ESP32 that had me pulling out my hair for quite some hours. I ended up devising the following test program in trying to trace it:

Code: Select all

void app_main()
{
// Config 2 UARTs
 uart_config_t uart_config = {1020833, UART_DATA_8_BITS,
  UART_PARITY_DISABLE, UART_STOP_BITS_1, UART_HW_FLOWCTRL_DISABLE};
 uart_param_config(UART_NUM_1, &uart_config);
 uart_param_config(UART_NUM_2, &uart_config);
 uart_set_pin (UART_NUM_1, GPIO_NUM_13, GPIO_NUM_14, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE);
 uart_set_pin (UART_NUM_2, GPIO_NUM_22, GPIO_NUM_23, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE);
 UART1.conf0.err_wr_mask = 1;        // require stop bit on byte received
 UART2.conf0.err_wr_mask = 1;
 
while (1)
 {
  WRITE_PERI_REG (UART_FIFO_AHB_REG (UART_NUM_1), 0x8D);
 
 while (UART1.status.rxfifo_cnt < 2);
  int b1 = READ_PERI_REG (UART_FIFO_REG (UART_NUM_1));
//int cnt = UART1.status.rxfifo_cnt;
  int b2 = READ_PERI_REG (UART_FIFO_REG (UART_NUM_1));
int cnt = UART1.status.rxfifo_cnt;
 
// this reads first byte ok but instead of second byte it gets a copy of the first byte (while still clearing it out of the FIFO and decrementing the count)
// if the first line is uncommented and the second commented, then it reads both bytes correctly
 
 printf ("%d - %d, %d\n", cnt, b1, b2);
  vTaskDelay (100 / portTICK_RATE_MS);
 }
}
The ESP32 is connected to an EFM8 that sends back 2 bytes when the above single byte command (0x8D) is received. As you can see from the code, the ESP32 sends the byte and then waits for the FIFO counter to indicate it has received two bytes back before reading them. For some unknown reason the second byte is lost unless I insert something that reads the FIFO counter in between the two reads. The behavior of the ESP32 makes no sense here. I wonder if I remember reading somewhere that you have to read the FIFO counter each time between reading bytes from the FIFO? My memory is fuzzy, and I could not find mention of it when searching.

The only thing I could find from searching was the following document "Workarounds for Bugs":
https://www.espressif.com/sites/default ... p32_en.pdf

On page 9 it says "Some ESP32 peripherals are mapped to two internal memory buses (AHB & DPORT). When written via DPORT, consecutive writes to the same address may be lost. When writing the same register address (i.e., FIFO-like addresses) in sequential instructions, use the equivalent AHB address not the DPORT address. (For other kinds of register writes, using DPORT registers will give better write performance.)" The table includes the UART FIFOs in the affected items.

Well I am not sequentially writting to the FIFO but rather sequentially reading from it, so it is not the same thing. However, I noticed that my code uses UART_FIFO_AHB_REG to get the address to write to, whereas it uses UART_FIFO_REG to get the address to read from. This seems to match the ESP's own drivers and most of the code samples I looked at. However, I tried changing the read to use UART_FIFO_AHB_REG instead, and it made the bug described stop! This is totally undocumented as far as I can tell, so I don't know that it is an acceptable solution.

So I am wondering the following:
• Do you really have to read the FIFO counter between each sequential FIFO read in order to actually get the next byte, versusgetting the previous byte and losing the actual one?
• Does reading from the UART_FIFO_AHB_REG instead of the UART_FIFO_REG work around the bug in the same way that sequentially writing to it apparently does?
• If either of the above workarounds are viable, which would be better to use?
• Is there some other explanation why the code sample above malfunctions that I am missing?

ESP_Sprite
Posts: 9775
Joined: Thu Nov 26, 2015 4:08 am

Re: Reading the UART

Postby ESP_Sprite » Mon Mar 23, 2020 5:26 pm

I think you're running into a design quirk of the Xtensa processor when it relates to our architecture. The thing is that the D-port can do speculative reads, that is, you can get 'fake reads' in advance if the core thinks future instructions are going to do a 'real read' from that address. (Reason, if I recall correctly, is so any caches can get to work to get the word accessible, in order for the 'real' read to go faster.) The issue with FIFOs is that this does not work: the speculative read will pop one word, the 'real' read will pop the next one.

Solution is indeed to use the APB memory range for FIFO reads.

WiFive
Posts: 3529
Joined: Tue Dec 01, 2015 7:35 am

Re: Reading the UART

Postby WiFive » Mon Jun 01, 2020 2:12 pm

ESP_Sprite wrote:
Mon Mar 23, 2020 5:26 pm
Solution is indeed to use the APB memory range for FIFO reads.
Meanwhile...
ECO V2.1 wrote:Notice: Software cannot use AHB addresses to read FIFO.

ESP_Sprite
Posts: 9775
Joined: Thu Nov 26, 2015 4:08 am

Re: Reading the UART

Postby ESP_Sprite » Wed Jun 03, 2020 8:18 am

Well, that's new to me, but there is some internal discussion going on on this. The new version of the TRM will likely have instructions on how to do this properly.

moefear85
Posts: 44
Joined: Sun Sep 05, 2021 4:55 pm

Re: Reading the UART

Postby moefear85 » Mon Jul 11, 2022 12:05 am

why isn't this documented in the errata?

kbaud1
Posts: 71
Joined: Wed Jan 17, 2018 11:55 pm

Re: Reading the UART

Postby kbaud1 » Fri Jun 23, 2023 10:23 pm

This appears to be described in 3.16 item #3: https://www.espressif.com/sites/default ... ata_en.pdf
The solution is to have enough instructions or NOPs between successive FIFO reads (check the assembly to be sure).

Who is online

Users browsing this forum: No registered users and 87 guests