From 6e8ff03ef764a73db4c35ab23d2fe34b78d5e53a Mon Sep 17 00:00:00 2001 From: dxmake <40735672+dxmake@users.noreply.github.com> Date: Thu, 9 Jul 2026 17:06:44 +0800 Subject: [PATCH] xuartlite: fix race condition and receive overrun in XUartLite_SendBuffer MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This commit addresses two critical bugs found in the XUartLite v3.11 driver when operating in interrupt-driven mode with heavy bidirectional (concurrent TX and RX) traffic on a Zynq-7000 platform. --- Bug 1: Race condition causing Data Abort (bus error) Root Cause: XUartLite_SendBuffer() is called from both normal (non-interrupt) context via XUartLite_Send(), and from interrupt context via the shared TX/RX interrupt handler. The original implementation reads the StatusRegister *before* disabling interrupts, then uses that stale status to drive the FIFO fill loop. If an RX interrupt fires between reading the status register and disabling interrupts, the interrupt handler may also write to the TX FIFO. Both contexts writing to the same 16-byte FIFO concurrently can overflow it, triggering an AXI bus fault and a Data Abort. Fix: After disabling interrupts, re-read StatusRegister before entering the FIFO fill loop. This guarantees the FIFO status used for transmission reflects the hardware state *after* interrupts are disabled, eliminating the window where interrupt-context FIFO writes could race with normal-context writes. As an alternative workaround, callers can bracket XUartLite_Send() with XUartLite_DisableInterrupt() / XUartLite_EnableInterrupt(). --- Bug 2: Receive overrun errors during heavy transmission Root Cause: The original byte-by-byte FIFO fill loop writes at most a few bytes per call. When optimized to batch-write up to the full 16-byte FIFO capacity, filling all 16 slots causes ReceiveOverrunErrors under concurrent full-duplex traffic. This suggests a hardware-level boundary condition in the AXI UART Lite IP core: a completely full TX FIFO interferes with or stalls the RX path, causing incoming data loss. The original byte-by-byte approach masked this because it never filled the FIFO completely in one atomic write sequence. Fix: Limit the batch write to 15 bytes (FIFO depth - 1), leaving one FIFO slot free. This avoids the fully-saturated state that triggers the RX overrun condition. --- Testing: - Platform: Xilinx Zynq-7000 (XC7Z020), AXI UART Lite v3.11 - Baud rate: 921600, full-duplex, -O0 - Bug 1: Data Abort reliably reproduced; eliminated with the re-read-after-disable fix and/or caller-side interrupt bracketing. - Bug 2: Batch size 16 → ReceiveOverrunErrors observed; batch size 15 → zero overruns across >1,000,000 bytes of continuous transfer. Signed-off-by: --- .../drivers/uartlite/src/xuartlite.c | 28 ++++++++++--------- 1 file changed, 15 insertions(+), 13 deletions(-) diff --git a/XilinxProcessorIPLib/drivers/uartlite/src/xuartlite.c b/XilinxProcessorIPLib/drivers/uartlite/src/xuartlite.c index 03c711098d6..5fcf71f3e5d 100644 --- a/XilinxProcessorIPLib/drivers/uartlite/src/xuartlite.c +++ b/XilinxProcessorIPLib/drivers/uartlite/src/xuartlite.c @@ -461,11 +461,6 @@ unsigned int XUartLite_SendBuffer(XUartLite *InstancePtr) u8 StatusRegister; u8 IntrEnableStatus; - /* - * Read the status register to determine if the transmitter is full - */ - StatusRegister = XUartLite_GetSR(InstancePtr); - /* * Enter a critical region by disabling all the UART interrupts to allow * this call to stop a previous operation that may be interrupt driven @@ -481,20 +476,27 @@ unsigned int XUartLite_SendBuffer(XUartLite *InstancePtr) */ IntrEnableStatus = StatusRegister; + /* + * Read the status register to determine if the transmitter is full + */ + StatusRegister = XUartLite_GetSR(InstancePtr); + /* * Fill the FIFO from the the buffer that was specified */ + if(!(StatusRegister & XUL_SR_TX_FIFO_EMPTY)) { + SentCount = 0; + } else if(InstancePtr->SendBuffer.RemainingBytes > 15) { + SentCount = 15; + } else { + SentCount = InstancePtr->SendBuffer.RemainingBytes; + } - while (((StatusRegister & XUL_SR_TX_FIFO_FULL) == 0) && - (SentCount < InstancePtr->SendBuffer.RemainingBytes)) { + for(int i = 0; i < SentCount; i += 1) { XUartLite_WriteReg(InstancePtr->RegBaseAddress, XUL_TX_FIFO_OFFSET, InstancePtr->SendBuffer.NextBytePtr[ - SentCount]); - - SentCount++; - - StatusRegister = XUartLite_GetSR(InstancePtr); + i]); } /* @@ -506,7 +508,7 @@ unsigned int XUartLite_SendBuffer(XUartLite *InstancePtr) /* * Increment associated counters */ - InstancePtr->Stats.CharactersTransmitted += SentCount; + InstancePtr->Stats.CharactersTransmitted += SentCount; /* * Restore the interrupt enable register to it's previous value such