From dd331c91d3e4c34c074658187ce6ba6b9ce133d6 Mon Sep 17 00:00:00 2001 From: Francisco Molina Date: Mon, 15 Jun 2020 16:24:48 +0200 Subject: [PATCH 1/5] cpu/stm32/uart: enable irq for non blocking uart --- cpu/stm32/periph/uart.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/cpu/stm32/periph/uart.c b/cpu/stm32/periph/uart.c index ea0d8b26ae..36b207ad87 100644 --- a/cpu/stm32/periph/uart.c +++ b/cpu/stm32/periph/uart.c @@ -207,6 +207,10 @@ int uart_init(uart_t uart, uint32_t baudrate, uart_rx_cb_t rx_cb, void *arg) dev(uart)->CR1 = (USART_CR1_UE | USART_CR1_TE); } +#ifdef MODULE_PERIPH_UART_NONBLOCKING + NVIC_EnableIRQ(uart_config[uart].irqn); +#endif + #ifdef MODULE_PERIPH_UART_HW_FC if (uart_config[uart].cts_pin != GPIO_UNDEF) { dev(uart)->CR3 |= USART_CR3_CTSE; From 0b8adb2d27d5271949d56e09b1f30f5e9c4044e1 Mon Sep 17 00:00:00 2001 From: Francisco Molina Date: Mon, 15 Jun 2020 16:45:44 +0200 Subject: [PATCH 2/5] cpu/sam0-stm32/uart: rename tx buf size to UART_TXBUF_SIZE --- cpu/sam0_common/include/periph_cpu_common.h | 4 ++-- cpu/sam0_common/periph/uart.c | 4 ++-- cpu/stm32/include/periph_cpu.h | 4 ++-- cpu/stm32/periph/uart.c | 4 ++-- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/cpu/sam0_common/include/periph_cpu_common.h b/cpu/sam0_common/include/periph_cpu_common.h index 495a031399..b23ff27c56 100644 --- a/cpu/sam0_common/include/periph_cpu_common.h +++ b/cpu/sam0_common/include/periph_cpu_common.h @@ -192,8 +192,8 @@ typedef enum { /** * @brief Size of the UART TX buffer for non-blocking mode. */ -#ifndef SAM0_UART_TXBUF_SIZE -#define SAM0_UART_TXBUF_SIZE (64) +#ifndef UART_TXBUF_SIZE +#define UART_TXBUF_SIZE (64) #endif /** diff --git a/cpu/sam0_common/periph/uart.c b/cpu/sam0_common/periph/uart.c index 41ebccec1d..624f872ab3 100644 --- a/cpu/sam0_common/periph/uart.c +++ b/cpu/sam0_common/periph/uart.c @@ -41,7 +41,7 @@ #ifdef MODULE_PERIPH_UART_NONBLOCKING #include "tsrb.h" static tsrb_t uart_tx_rb[UART_NUMOF]; -static uint8_t uart_tx_rb_buf[UART_NUMOF][SAM0_UART_TXBUF_SIZE]; +static uint8_t uart_tx_rb_buf[UART_NUMOF][UART_TXBUF_SIZE]; #endif static uart_isr_ctx_t uart_ctx[UART_NUMOF]; @@ -68,7 +68,7 @@ int uart_init(uart_t uart, uint32_t baudrate, uart_rx_cb_t rx_cb, void *arg) #ifdef MODULE_PERIPH_UART_NONBLOCKING /* set up the TX buffer */ - tsrb_init(&uart_tx_rb[uart], uart_tx_rb_buf[uart], SAM0_UART_TXBUF_SIZE); + tsrb_init(&uart_tx_rb[uart], uart_tx_rb_buf[uart], UART_TXBUF_SIZE); #endif /* configure pins */ diff --git a/cpu/stm32/include/periph_cpu.h b/cpu/stm32/include/periph_cpu.h index 2b3f6e60a3..c9bc9f1007 100644 --- a/cpu/stm32/include/periph_cpu.h +++ b/cpu/stm32/include/periph_cpu.h @@ -550,8 +550,8 @@ typedef enum { /** * @brief Size of the UART TX buffer for non-blocking mode. */ -#ifndef STM32_UART_TXBUF_SIZE -#define STM32_UART_TXBUF_SIZE (64) +#ifndef UART_TXBUF_SIZE +#define UART_TXBUF_SIZE (64) #endif #ifndef DOXYGEN diff --git a/cpu/stm32/periph/uart.c b/cpu/stm32/periph/uart.c index 36b207ad87..34aabfa7d9 100644 --- a/cpu/stm32/periph/uart.c +++ b/cpu/stm32/periph/uart.c @@ -61,7 +61,7 @@ * @brief Allocate for tx ring buffers */ static tsrb_t uart_tx_rb[UART_NUMOF]; -static uint8_t uart_tx_rb_buf[UART_NUMOF][STM32_UART_TXBUF_SIZE]; +static uint8_t uart_tx_rb_buf[UART_NUMOF][UART_TXBUF_SIZE]; #endif /** @@ -168,7 +168,7 @@ int uart_init(uart_t uart, uint32_t baudrate, uart_rx_cb_t rx_cb, void *arg) #ifdef MODULE_PERIPH_UART_NONBLOCKING /* set up the TX buffer */ - tsrb_init(&uart_tx_rb[uart], uart_tx_rb_buf[uart], STM32_UART_TXBUF_SIZE); + tsrb_init(&uart_tx_rb[uart], uart_tx_rb_buf[uart], UART_TXBUF_SIZE); #endif uart_init_pins(uart, rx_cb); From f7d124e14120e6d64e562c7d7b16569c5bde2da2 Mon Sep 17 00:00:00 2001 From: Francisco Molina Date: Mon, 15 Jun 2020 16:47:10 +0200 Subject: [PATCH 3/5] tests/periph_uart_nonblocking: test print with disabled irq --- tests/periph_uart_nonblocking/main.c | 13 +++++++++++++ tests/periph_uart_nonblocking/tests/01-run.py | 1 + 2 files changed, 14 insertions(+) diff --git a/tests/periph_uart_nonblocking/main.c b/tests/periph_uart_nonblocking/main.c index 62e2f6fabb..d5e1bbf631 100644 --- a/tests/periph_uart_nonblocking/main.c +++ b/tests/periph_uart_nonblocking/main.c @@ -30,8 +30,21 @@ static inline uint32_t puts_delay(const char* str) return LINE_DELAY_MS * 1000; } +static void _irq_disabled_print(void) +{ + unsigned state = irq_disable(); + /* fill the transmit buffer */ + for (uint8_t i = 0; i < UART_TXBUF_SIZE; i++) { + printf(" "); + } + puts("\nputs with disabled interrupts and a full transmit buffer"); + irq_restore(state); +} + int main(void) { + _irq_disabled_print(); + uint32_t total_us = 0; xtimer_ticks32_t counter = xtimer_now(); diff --git a/tests/periph_uart_nonblocking/tests/01-run.py b/tests/periph_uart_nonblocking/tests/01-run.py index 0a412e5086..028db2dcdf 100755 --- a/tests/periph_uart_nonblocking/tests/01-run.py +++ b/tests/periph_uart_nonblocking/tests/01-run.py @@ -14,6 +14,7 @@ PRECISION = 1.002 def testfunc(child): + child.expect_exact("puts with disabled interrupts and a full transmit buffer") child.expect(r'== printed in (\d+)/(\d+) µs ==') time_actual = int(child.match.group(1)) time_expect = int(child.match.group(2)) From 09f0fd4526f55999d40993f4572a6b588e04dd22 Mon Sep 17 00:00:00 2001 From: Francisco Molina Date: Sun, 14 Jun 2020 19:50:48 +0200 Subject: [PATCH 4/5] cpu/stm32: avoid deadlock on nonblocking write If a write to a full tsrb is attempted with disabled interrupts or in a interrupt then a deadlock will occure. To avoid this make space in the ringbuffer by synchrnously writing to uart. --- cpu/stm32/periph/uart.c | 25 ++++++++++++++++--------- 1 file changed, 16 insertions(+), 9 deletions(-) diff --git a/cpu/stm32/periph/uart.c b/cpu/stm32/periph/uart.c index 34aabfa7d9..d7888971fb 100644 --- a/cpu/stm32/periph/uart.c +++ b/cpu/stm32/periph/uart.c @@ -322,22 +322,18 @@ static inline void uart_init_lpuart(uart_t uart, uint32_t baudrate) #endif /* MODULE_PERIPH_LPUART */ #endif /* STM32L0 || STM32L4 || STM32WB */ -#ifndef MODULE_PERIPH_UART_NONBLOCKING static inline void send_byte(uart_t uart, uint8_t byte) { while (!(dev(uart)->ISR_REG & ISR_TXE)) {} dev(uart)->TDR_REG = byte; } -#endif +#ifndef MODULE_PERIPH_UART_NONBLOCKING static inline void wait_for_tx_complete(uart_t uart) { -#ifdef MODULE_PERIPH_UART_NONBLOCKING - (void) uart; -#else while (!(dev(uart)->ISR_REG & ISR_TC)) {} -#endif } +#endif void uart_write(uart_t uart, const uint8_t *data, size_t len) { @@ -387,17 +383,28 @@ void uart_write(uart_t uart, const uint8_t *data, size_t len) return; } #endif - for (size_t i = 0; i < len; i++) { #ifdef MODULE_PERIPH_UART_NONBLOCKING + for (size_t i = 0; i < len; i++) { dev(uart)->CR1 |= (USART_CR1_TCIE); - while (tsrb_add_one(&uart_tx_rb[uart], data[i]) < 0) {} + if (irq_is_in() || __get_PRIMASK()) { + /* if ring buffer is full free up a spot */ + if (tsrb_full(&uart_tx_rb[uart])) { + send_byte(uart, tsrb_get_one(&uart_tx_rb[uart])); + } + tsrb_add_one(&uart_tx_rb[uart], data[i]); + } + else { + while (tsrb_add_one(&uart_tx_rb[uart], data[i]) < 0) {} + } + } #else + for (size_t i = 0; i < len; i++) { send_byte(uart, data[i]); -#endif } /* make sure the function is synchronous by waiting for the transfer to * finish */ wait_for_tx_complete(uart); +#endif } void uart_poweron(uart_t uart) From 80d682becd909d356742810e00eeb36d62380be1 Mon Sep 17 00:00:00 2001 From: Francisco Molina Date: Sun, 14 Jun 2020 19:52:21 +0200 Subject: [PATCH 5/5] cpu/sam0: avoid deadlock on nonblocking write If a write to a full tsrb is attempted with disabled interrupts or in a interrupt then a deadlock will occure. To avoid this make space in the ringbuffer by synchronously writing to uart. --- cpu/sam0_common/periph/uart.c | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/cpu/sam0_common/periph/uart.c b/cpu/sam0_common/periph/uart.c index 624f872ab3..9faa58e7f5 100644 --- a/cpu/sam0_common/periph/uart.c +++ b/cpu/sam0_common/periph/uart.c @@ -164,7 +164,17 @@ void uart_write(uart_t uart, const uint8_t *data, size_t len) { #ifdef MODULE_PERIPH_UART_NONBLOCKING for (const void* end = data + len; data != end; ++data) { - while (tsrb_add_one(&uart_tx_rb[uart], *data) < 0) {} + if (irq_is_in() || __get_PRIMASK()) { + /* if ring buffer is full free up a spot */ + if (tsrb_full(&uart_tx_rb[uart])) { + while (!dev(uart)->INTFLAG.bit.DRE) {} + dev(uart)->DATA.reg = tsrb_get_one(&uart_tx_rb[uart]); + } + tsrb_add_one(&uart_tx_rb[uart], *data); + } + else { + while (tsrb_add_one(&uart_tx_rb[uart], *data) < 0) {} + } dev(uart)->INTENSET.reg = SERCOM_USART_INTENSET_DRE; } #else