Page 1 of 1

Reading the UART

Posted: Mon Mar 23, 2020 3:32 pm
by kbaud1
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?

Re: Reading the UART

Posted: Mon Mar 23, 2020 5:26 pm
by ESP_Sprite
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.

Re: Reading the UART

Posted: Mon Jun 01, 2020 2:12 pm
by WiFive
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.

Re: Reading the UART

Posted: Wed Jun 03, 2020 8:18 am
by ESP_Sprite
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.

Re: Reading the UART

Posted: Mon Jul 11, 2022 12:05 am
by moefear85
why isn't this documented in the errata?

Re: Reading the UART

Posted: Fri Jun 23, 2023 10:23 pm
by kbaud1
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).