Skip to content

Commit ba92377

Browse files
Decrease size of used FIFO in SerialUART (#471)
Fixes #468 The FIFO limit was set to 1/2, or 16 bytes on POR and not set by the core, so for low baud this could result in a LONG time without data moving from hardware FIFO to the SW ring buffer and timeouts/etc. Now use the API call which sets it to 1/8, or 4 bytes of data to speed up the transfer 4x. Also avoid using the divider in the IRQ routine because it is not clear from the docs of the Pico SDK IRQ callback routine preserves divider state or not. If not, doing division in an IRQ could result in random data corruption in the main app. Add memory barriers to ensure the order of data into RAM is preserved and that GCC doesn't reorder writes.
1 parent acbe190 commit ba92377

File tree

2 files changed

+26
-6
lines changed

2 files changed

+26
-6
lines changed

cores/rp2040/SerialPIO.cpp

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -117,7 +117,13 @@ void __not_in_flash_func(SerialPIO::_handleIRQ)() {
117117
if ((_writer + 1) % _fifosize != _reader) {
118118
_queue[_writer] = val & ((1 << _bits) - 1);
119119
asm volatile("" ::: "memory"); // Ensure the queue is written before the written count advances
120-
_writer = (_writer + 1) % _fifosize;
120+
// Avoid using division or mod because the HW divider could be in use
121+
auto next_writer = _writer + 1;
122+
if (next_writer == _fifosize) {
123+
next_writer = 0;
124+
}
125+
asm volatile("" ::: "memory"); // Ensure the reader value is only written once, correctly
126+
_writer = next_writer;
121127
} else {
122128
// TODO: Overflow
123129
}
@@ -271,7 +277,10 @@ int SerialPIO::read() {
271277
}
272278
if (_writer != _reader) {
273279
auto ret = _queue[_reader];
274-
_reader = (_reader + 1) % _fifosize;
280+
asm volatile("" ::: "memory"); // Ensure the value is read before advancing
281+
auto next_reader = (_reader + 1) % _fifosize;
282+
asm volatile("" ::: "memory"); // Ensure the reader value is only written once, correctly
283+
_reader = next_reader;
275284
return ret;
276285
}
277286
return -1;

cores/rp2040/SerialUART.cpp

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -135,7 +135,8 @@ void SerialUART::begin(unsigned long baud, uint16_t config) {
135135
irq_set_exclusive_handler(UART1_IRQ, _uart1IRQ);
136136
irq_set_enabled(UART1_IRQ, true);
137137
}
138-
uart_get_hw(_uart)->imsc |= UART_UARTIMSC_RXIM_BITS | UART_UARTIMSC_RTIM_BITS;
138+
// Set the IRQ enables and FIFO level to minimum
139+
uart_set_irq_enables(_uart, true, false);
139140
_running = true;
140141
}
141142

@@ -171,7 +172,10 @@ int SerialUART::read() {
171172
}
172173
if (_writer != _reader) {
173174
auto ret = _queue[_reader];
174-
_reader = (_reader + 1) % _fifoSize;
175+
asm volatile("" ::: "memory"); // Ensure the value is read before advancing
176+
auto next_reader = (_reader + 1) % _fifoSize;
177+
asm volatile("" ::: "memory"); // Ensure the reader value is only written once, correctly
178+
_reader = next_reader;
175179
return ret;
176180
}
177181
return -1;
@@ -245,13 +249,20 @@ void arduino::serialEvent2Run(void) {
245249

246250
// IRQ handler, called when FIFO > 1/4 full or when it had held unread data for >32 bit times
247251
void __not_in_flash_func(SerialUART::_handleIRQ)() {
248-
uart_get_hw(_uart)->icr |= UART_UARTICR_RTIC_BITS | UART_UARTICR_RXIC_BITS;
252+
// ICR is write-to-clear
253+
uart_get_hw(_uart)->icr = UART_UARTICR_RTIC_BITS | UART_UARTICR_RXIC_BITS;
249254
while (uart_is_readable(_uart)) {
250255
auto val = uart_getc(_uart);
251256
if ((_writer + 1) % _fifoSize != _reader) {
252257
_queue[_writer] = val;
253258
asm volatile("" ::: "memory"); // Ensure the queue is written before the written count advances
254-
_writer = (_writer + 1) % _fifoSize;
259+
// Avoid using division or mod because the HW divider could be in use
260+
auto next_writer = _writer + 1;
261+
if (next_writer == _fifoSize) {
262+
next_writer = 0;
263+
}
264+
asm volatile("" ::: "memory"); // Ensure the reader value is only written once, correctly
265+
_writer = next_writer;
255266
} else {
256267
// TODO: Overflow
257268
}

0 commit comments

Comments
 (0)