Skip to content

stm32: UART overrun race condition locks up system #3375

Closed
@hoihu

Description

@hoihu

There is a race condition present in the stm32 port that relates to the way the UART overrun (ORE bit in USART status register) is handled in the IRQ service routine.

If this condition is met, the pyboard completly locks up and cannot be recovered (you have to hard-reset or power cycle).

The reason is because the UART RX ISR function does not clear the ORE flag if the receive buffer is empty and hence the irq is starting again as soon as the handler exits (ORE is also triggering the IRQ). It results in a 100% CPU utilisation.

This situation is explained in the STM32 reference manual page chapter "Overrun error" in the USART description.

Steps to reproduce:

  1. Wire up a pyboard UART Rx pin (e.g. X10) to an USB->serial adapter's TX pin
  2. on the pyboard enter:
from pyb import UART
u=UART(1,230400)
while True: u.read(10)
  1. on the PC enter:
import time, serial, os
u=serial.serial_for_url('COMxx',230400)
while True: u.write(os.urandom(100)); time.sleep(0.01)

you should now see a lot of random data being transferred to pyboard. Since the window time of the critical section (https://github.com/micropython/micropython/blob/master/ports/stm32/uart.c#L486-L494) is somewhere smaller than 1usec it's very hard to hit the bug and normally you won't see any troubles.

However, the raise condition can be forced if another IRQ can be triggered on the pyboard, hence delaying the calling of the UART service routine.

One way that worked for me was to load a file, e.g. boot.py, in a text editor and hit several times "save" (normally 10-20 times should be ok) -> BOOM. The pyboard freezes and continously fires the UART RX IRQ

The way to resolve is to put the following code

    if (__HAL_UART_GET_FLAG(&self->uart, UART_FLAG_ORE) != RESET) {
        if (__HAL_UART_GET_FLAG(&self->uart, UART_FLAG_RXNE) == RESET) {
            // overrun and empty data, just do a dummy read to clear ORE
            // and prevent a raise condition where a continous interrupt stream (due to ORE set) occurs
            // (see chapter "Overrun error" ) in STM32 reference manual
            self->uart.Instance->DR;
        } 
    } 

just before https://github.com/micropython/micropython/blob/master/ports/stm32/uart.c#L486

On the L4, the overrun can be disabled in the UART's control register, so it doesn't need that check.

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions