This thread has been locked.

If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.

MCU-PLUS-SDK-AM263X: AM263x UART DMA RX – Data corruption after 64 bytes when re-arming variable-length reception

Hi Team,

I am working on AM263x using MCU+ SDK version mcu_plus_sdk_am263x_09_02_00_55.

I am using UART in callback mode with DMA enabled.

 

Requirement

My receive length is dynamic:

  • First, I receive 4 bytes (header).

  • The 2nd and 3rd bytes determine the payload length.

  • Based on that, I update UART_Transaction.count.

  • Then I call UART_read() again to receive the remaining payload.

  • After payload reception completes, I restart reception for the next 4-byte header.

The incoming data stream is continuous from the external device.

Issue Observed

The receive buffer fills correctly up to 64 bytes.

After 64 bytes:

  • The 65th byte onwards appears corrupted.

  • Data looks like it is restarting from index 0 or overwritten.

  • Behavior suggests possible FIFO overflow (UART FIFO size appears to be 64 bytes).

To temporarily recover, I manually flush the UART FIFO by reading the RHR register when LSR indicates data ready, but I am not sure if this is the correct approach when using the UART driver with DMA.



this is my code

static uint8_t gRadioComReceivebuffer[UART_COM_BUFSIZE]
    __attribute__((aligned(CacheP_CACHELINE_ALIGNMENT))) = {0U};

static uint8_t gRadioCOMCopyDMA[1024] = {0U};

#define UART_COM_BUFSIZE (1024)

static void SL_SetRadioCOMByteCount(uint16_t bCount)
{
    RMC_RxTrans.buf   = gRadioComReceivebuffer;
    RMC_RxTrans.count = bCount;
}

static void RADIO_MODEM_RX_CALLBACK(UART_Handle handle,
                                    UART_Transaction *trans)
{
    printf("received count = %d\n", trans->count);
    RMCRxBuf.rxCompleteFlag = 1U;
}

Processing function:

static void CopyReceiveData(void)
{
    if (RMCRxBuf.rxCompleteFlag == 1U)
    {
        RMCRxBuf.rxCompleteFlag = 0U;

        CacheP_inv((void *)gRadioComReceivebuffer,
                   RadioRXDMALen,
                   CacheP_TYPE_ALL);

        if (RadioRXDMALen == 4U)
        {
            for(uint8_t i = 0; i < 4; i++)
            {
                gRadioCOMCopyDMA[i] = gRadioComReceivebuffer[i];
            }

            RadioRXDMALen =
                ((gRadioCOMCopyDMA[2] << 8) |
                 (gRadioCOMCopyDMA[3])) + 5U;

            SL_SetRadioCOMByteCount(RadioRXDMALen);

            UART_read(gUartHandle[RADIO_COMM_PORT], &RMC_RxTrans);
        }
        else
        {
            for(uint16_t i = 0; i < RadioRXDMALen; i++)
            {
                gRadioCOMCopyDMA[i + 4U] =
                    gRadioComReceivebuffer[i];
            }

            RadioRXDMALen = 4U;
            SL_SetRadioCOMByteCount(RadioRXDMALen);

            UART_read(gUartHandle[RADIO_COMM_PORT], &RMC_RxTrans);

            VadateDMAmodel1(radioCOMRxByteCount);
            radioCOMRxByteCount = 0U;
        }
    }
}

uart configuration:
            .baudRate           = 57600,
            .dataLength         = UART_LEN_8,
            .stopBits           = UART_STOPBITS_1,
            .parityType         = UART_PARITY_NONE,
            .readMode           = UART_TRANSFER_MODE_CALLBACK,
            .readReturnMode     = UART_READ_RETURN_MODE_PARTIAL,
            .writeMode          = UART_TRANSFER_MODE_CALLBACK,
            .readCallbackFxn    = RADIO_MODEM_RX_CALLBACK,
            .writeCallbackFxn   = RADIO_MODEM_TX_CALLBACK,
            .hwFlowControl      = FALSE,//TRUE,
            .hwFlowControlThr   = UART_RXTRIGLVL_16,
            .transferMode       = UART_CONFIG_MODE_DMA,
            .skipIntrReg         = FALSE,
            .uartDmaIndex = 1,
            .intrNum            = 39U,
            .intrPriority       = 8U,
            .operMode           = UART_OPER_MODE_16X,
            .rxTrigLvl          = UART_RXTRIGLVL_1,
            .txTrigLvl          = UART_TXTRIGLVL_1,
            .rxEvtNum           = 0U,
            .txEvtNum           = 0U
  • Hi,

    Thank you for your query. Please note that we will not be able to fully debug the custom application for you. We can definitely offer pointers on debug.

    The SDK documentation explicitly describes your scenario as a known issue:

    "In UART DMA mode, after a transaction is finished, if the remote end keeps sending data, that data will be received in the FIFO. If the FIFO becomes full, a FIFO overflow error will occur. This is because the DMA is disabled at the end of the transaction, and no more data is transferred from the FIFO to the main memory." [3]

    With continuous data streams, there's a critical window between when one DMA transaction completes and the next begins. During this time, incoming data accumulates in the FIFO, and if it exceeds 64 bytes, overflow occurs [4].

    Reference:

    1. UART DMA Known Issues - AM263x MCU+ SDK User Guide
    2. UART DMA Continuous Data Reception - AM263x SDK User Guide
  • Transaction Count Must Match Trigger Level

    An additional constraint when using DMA is that UART_Transaction.count must be a multiple of the configured receive trigger level (rxTrigLvl):

    It is important that the UART_Transaction.count is a multiple of the DMA transfer size, which is configured by the UART_Attrs.rxTrigLvl parameter.

    If your dynamic payload lengths don't align with this requirement, the DMA cannot properly transfer remaining bytes, contributing to data corruption.UART Transaction Count Requirements - AM263x SDK User Guide

    Recommended Solution

    Approach 1: Queue Next Read from Callback (Recommended)

    To prevent FIFO overflow with continuous data streams, queue the next read request directly from the read callback function:

    void uartReadCallback(UART_Handle handle, UART_Transaction *transaction)
    {
        if (transaction->status == UART_TRANSFER_STATUS_SUCCESS)
        {
            if (receivingHeader)
            {
                // Process header to determine payload length
                uint16_t payloadLength = (rxBuffer[1] << 8) | rxBuffer[2];
                
                // Prepare NEW transaction structure for payload
                UART_Transaction payloadTrans;
                UART_Transaction_init(&payloadTrans);
                payloadTrans.buf = &rxBuffer[4];  // Start after header
                payloadTrans.count = payloadLength;
                payloadTrans.timeout = UART_WAIT_FOREVER;
                
                receivingHeader = false;
                
                // Queue payload read immediately from callback
                UART_read(handle, &payloadTrans);
            }
            else
            {
                // Payload received, process data
                processPayload();
                
                // Prepare NEW transaction structure for next header
                UART_Transaction headerTrans;
                UART_Transaction_init(&headerTrans);
                headerTrans.buf = rxBuffer;
                headerTrans.count = 4;
                headerTrans.timeout = UART_WAIT_FOREVER;
                
                receivingHeader = true;
                
                // Queue next header read immediately from callback
                UART_read(handle, &headerTrans);
            }
        }
    }

    This approach ensures the DMA is always armed to receive data, preventing FIFO overflow [6].

    Approach 2: Use UART_readCancel() to Flush FIFO

    If you cannot restructure your code to queue reads from callbacks, use the proper API to flush the FIFO.The application can call UART_readCancel() to terminate the transaction and flush the FIFO to avoid this

    UART DMA Known Issues - AM263x MCU+ SDK User Guide

    // After processing header, before starting payload read
    UART_readCancel(uartHandle);  // Flush any accumulated data in FIFO
    
    // Now start payload read with new transaction
    UART_Transaction payloadTrans;
    UART_Transaction_init(&payloadTrans);
    payloadTrans.buf = payloadBuffer;
    payloadTrans.count = payloadLength;
    UART_read(uartHandle, &payloadTrans);

    Approach 3: Use Partial Read Mode for Unknown Lengths

    For truly dynamic lengths where you don't know the exact byte count, consider using UART_READ_RETURN_MODE_PARTIAL:

    "The UART driver supports UART_READ_RETURN_MODE_PARTIAL which unblocks or performs a callback whenever a read timeout error occurs on the UART peripheral. The read timeout occurs if the read FIFO is non-empty and no new data has been received for a specific number of clock cycles. This mode can be used when the exact number of bytes to be read is not known." [7]

    Why Manual FIFO Flushing is Incorrect

    Your current approach of manually reading the RHR register is not recommended when using the high-level UART driver with DMA:

    The UART_getCharFifo() low-level API that reads the RHR register [8] bypasses the driver's state management. When using the UART driver in callback mode with DMA, manually reading the RHR register interferes with the DMA controller's operation and the driver's internal buffer management, causing synchronization issues and data corruption [9].

    Implementation Checklist

    1. Never modify UART_Transaction structure members between UART_read() call and callback completion
    2. Always use separate UART_Transaction structures for header and payload reads
    3. Queue the next read from within the callback function to minimize FIFO accumulation time
    4. Ensure UART_Transaction.count is a multiple of rxTrigLvl when using DMA
    5. Use UART_readCancel() API (not manual RHR reads) if you need to flush the FIFO
    6. Consider UART_READ_RETURN_MODE_PARTIAL for variable-length protocols

    Additional Considerations

    Verify your rxTrigLvl configuration in UART_Attrs. For continuous high-speed data streams, a lower trigger level (e.g., 1 or 8 bytes) may help reduce FIFO accumulation, though this increases interrupt/DMA overhead

    References:

    1. UART FIFO Size Definition - AM263x SDK API Guide
    2. UART_read() API Documentation - AM263x SDK
    3. UART DMA Known Issues - AM263x MCU+ SDK User Guide
    4. UART DMA Continuous Data Reception - AM263x SDK User Guide
    5. UART Transaction Count Requirements - AM263x SDK User Guide
    6. UART Callback Mode Operation - AM263x SDK API Guide
    7. UART Partial Read Mode - AM263x SDK API Guide
    8. UART_getCharFifo() Low-Level API - AM263x SDK
    9. UART DMA Manual Trigger Example - AM263x MCU+ SDK User Guide