Skip to content

uart_stm32.c: clearing of IDLE flag when using async #91070

Open
@besteinm

Description

@besteinm

Describe the bug

I have been experiencing strange behaviour when using UART Async mode on a STM32 L462 MCU. I am using Zephyr 3.6.0

I have an application communicating via UART (230400 bps, 8N1, with RTS/CTS hw flow control). The MCU is running at 48 MHz. I use the UART Async API.

The application periodically receives messages of 9 bytes. I am supplying buffers of size 13 to the Async API (first with uart_rx_enable, and afterwards via uart_rx_buf_rsp). The messages are received as expected: when the message fits entirely within a single buffer I get a UART_RX_RDY with 9 bytes; if it does not fit into a single buffer (and overflows), then I get for example a UART_RX_RDY with 5 bytes, then a UART_RX_RDY with 4 bytes (with some UART_RX_BUF_RELEASED and UART_RX_BUF_REQUEST in between).

However, if the message overflows with exactly one byte spilling into the next buffer (i.e 8 bytes fit into the currently active buffer, then the uart driver releases and requests a new buffer, then 1 byte is put into the new active buffer), then the second UART_RX_RDY (the one that is supposed to have 1 byte) is not triggered. I only receive UART_RX_RDY for the first chunk of 8 bytes.

This is the corresponding log output (with CONFIG_UART_LOG_LEVEL_DBG=y)

[00:00:30.675,000] <dbg> uart_stm32: uart_stm32_async_rx_buf_rsp: replace buffer (13)
[00:00:30.675,000] <dbg> uart_stm32: uart_stm32_async_rx_enable: async rx enabled
...
[00:00:45.677,000] <dbg> uart_stm32: uart_stm32_isr: idle interrupt occurred
[00:00:45.677,000] <dbg> uart_stm32: async_evt_rx_rdy: rx_rdy: (0 9)
...
[00:01:00.677,000] <dbg> uart_stm32: async_evt_rx_rdy: rx_rdy: (9 13)
[00:01:00.677,000] <dbg> uart_stm32: uart_stm32_dma_replace_buffer: Replacing RX buffer: 13
[00:01:00.677,000] <dbg> uart_stm32: uart_stm32_async_rx_buf_rsp: replace buffer (13)
[00:01:00.677,000] <dbg> uart_stm32: uart_stm32_isr: idle interrupt occurred
[00:01:00.677,000] <dbg> uart_stm32: async_evt_rx_rdy: rx_rdy: (0 5)
...
[00:01:15.678,000] <dbg> uart_stm32: async_evt_rx_rdy: rx_rdy: (5 13)
[00:01:15.678,000] <dbg> uart_stm32: uart_stm32_dma_replace_buffer: Replacing RX buffer: 13
[00:01:15.678,000] <dbg> uart_stm32: uart_stm32_async_rx_buf_rsp: replace buffer (13)
...    <----  no rx_rdy(0 1)

I had a closer look at uart_stm32.c and in particular the function uart_stm32_dma_replace_buffer. There is a suspicious line that clears the IDLE flag when requesting a new buffer:

static void uart_stm32_dma_replace_buffer(const struct device *dev)
{
	const struct uart_stm32_config *config = dev->config;
	struct uart_stm32_data *data = dev->data;

	/* Replace the buffer and reload the DMA */
	LOG_DBG("Replacing RX buffer: %d", data->rx_next_buffer_len);

	/* reload DMA */
	data->dma_rx.offset = 0;
	data->dma_rx.counter = 0;
	data->dma_rx.buffer = data->rx_next_buffer;
	data->dma_rx.buffer_length = data->rx_next_buffer_len;
	data->dma_rx.blk_cfg.block_size = data->dma_rx.buffer_length;
	data->dma_rx.blk_cfg.dest_address = (uint32_t)data->dma_rx.buffer;
	data->rx_next_buffer = NULL;
	data->rx_next_buffer_len = 0;

	dma_reload(data->dma_rx.dma_dev, data->dma_rx.dma_channel,
			data->dma_rx.blk_cfg.source_address,
			data->dma_rx.blk_cfg.dest_address,
			data->dma_rx.blk_cfg.block_size);

	dma_start(data->dma_rx.dma_dev, data->dma_rx.dma_channel);

---->	LL_USART_ClearFlag_IDLE(config->usart);   <----------------------

	/* Request next buffer */
	async_evt_rx_buf_request(data);
}

What is the intention of clearing the Idle flag when requesting a new buffer? When I remove that line then everything works nicely; I get the UART_RX_RDY notification even in the 8-1 split case (single byte in the new buffer):

[00:00:33.147,000] <dbg> uart_stm32: uart_stm32_async_rx_buf_rsp: replace buffer (13)
[00:00:33.147,000] <dbg> uart_stm32: uart_stm32_async_rx_enable: async rx enabled
...
[00:00:48.149,000] <dbg> uart_stm32: uart_stm32_isr: idle interrupt occurred
[00:00:48.149,000] <dbg> uart_stm32: async_evt_rx_rdy: rx_rdy: (0 9)
...
[00:01:03.150,000] <dbg> uart_stm32: async_evt_rx_rdy: rx_rdy: (9 13)
[00:01:03.150,000] <dbg> uart_stm32: uart_stm32_dma_replace_buffer: Replacing RX buffer: 13
[00:01:03.150,000] <dbg> uart_stm32: uart_stm32_async_rx_buf_rsp: replace buffer (13)
[00:01:03.150,000] <dbg> uart_stm32: uart_stm32_isr: idle interrupt occurred
[00:01:03.150,000] <dbg> uart_stm32: async_evt_rx_rdy: rx_rdy: (0 2)
[00:01:03.150,000] <dbg> uart_stm32: uart_stm32_isr: idle interrupt occurred
[00:01:03.150,000] <dbg> uart_stm32: async_evt_rx_rdy: rx_rdy: (2 5)
...
[00:01:18.151,000] <dbg> uart_stm32: async_evt_rx_rdy: rx_rdy: (5 13)
[00:01:18.151,000] <dbg> uart_stm32: uart_stm32_dma_replace_buffer: Replacing RX buffer: 13
[00:01:18.151,000] <dbg> uart_stm32: uart_stm32_async_rx_buf_rsp: replace buffer (13)
[00:01:18.151,000] <dbg> uart_stm32: uart_stm32_isr: idle interrupt occurred
[00:01:18.151,000] <dbg> uart_stm32: async_evt_rx_rdy: rx_rdy: (0 1)           <------------------ ok
...
[00:01:33.151,000] <dbg> uart_stm32: uart_stm32_isr: idle interrupt occurred
[00:01:33.151,000] <dbg> uart_stm32: async_evt_rx_rdy: rx_rdy: (1 10)

Is it possible that at 230400 bps, receiving the single byte of the second chunk happens so fast that the subsequent setting of the IDLE interrupt flag gets wrongfully cleared by uart_stm32_dma_replace_buffer (and thus prevents uart_stm32_isr from properly generating the UART_RX_RDY event?)

Regression

  • This is a regression.

Steps to reproduce

No response

Relevant log output

Impact

Annoyance – Minor irritation; no significant impact on usability or functionality.

Environment

No response

Additional Context

No response

Metadata

Metadata

Assignees

Labels

area: UARTUniversal Asynchronous Receiver-TransmitterbugThe issue is a bug, or the PR is fixing a bugplatform: STM32ST Micro STM32priority: lowLow impact/importance bug

Type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions