MSPM33C321A: UART DMA interface misses last bytes when configured for variable length reception

Part Number: MSPM33C321A
Other Parts Discussed in Thread: SYSCONFIG

Hi Team, 
I have a question regarding UART DMA reception using the RX TIMEOUT trigger on the MSPM33C321x.
 
I am implementing variable-length UART reception and have configured DMA as follows:
  • DMA trigger source: UART RX TIMEOUT
  • DMA transfer size: Maximum receive buffer size
  • Packet length: Variable
  • My timeout count is set to 15.
During testing, I observed that when a packet is received, the RX TIMEOUT event occurs before the final byte is transferred by DMA. As a result, the last byte appears to be missing from the DMA buffer.
 
I also checked whether any data remained in the UART RX FIFO when the RX TIMEOUT interrupt occurred, but the FIFO was empty. 
Also tried by setting RX FIFO threshold level to >=1/4 Full and 1/2 Full with no success.
 
Could you please clarify the intended behavior of the RX TIMEOUT DMA trigger?
 
Specifically:
  • Is it expected that the RX TIMEOUT event can occur before the final byte is transferred to memory by DMA?
  • When RX TIMEOUT is used as the DMA trigger source, should the final byte already be transferred before the timeout event is generated?
  • Is there a recommended approach for handling variable-length UART packets with DMA reception?
  • Are there any known limitations or example projects demonstrating variable-length UART reception using DMA and RX TIMEOUT?
In my current setup, I consistently observe one fewer byte in the DMA buffer than the number of bytes transmitted, which suggests the final byte may still be in the UART receive path when the timeout event occurs.
 
Any guidance would be greatly appreciated.
  • Hi Rajeshwary,

    The RX TIMEOUT event should be treated as an indication that no additional UART character was received within the configured timeout. It should not be treated as a guarantee that all pending DMA writes to the receive buffer have completed. If RX TIMEOUT is selected as the DMA trigger, then the timeout event itself can be the event that initiates the DMA request for the remaining byte/data in the UART receive path. Therefore, software may observe the timeout condition before the final DMA write is visible in memory.

    For variable-length UART reception, we recommend using the normal UART RX DMA trigger to move incoming bytes into the DMA buffer, and using RX TIMEOUT as an end-of-packet indication. On timeout, the application should stop/synchronize the DMA channel, read the remaining DMA transfer count, calculate the received length, and then process the buffer.

    In other words, RX TIMEOUT is useful to detect the end of a variable-length frame, but it should not be used alone as proof that the DMA buffer is already complete. Also, FIFO-empty status alone is not sufficient to confirm the data is already committed to SRAM, because the DMA may have already removed the byte from the FIFO while the memory write/count update is still pending.

    Please also ensure the application is not disabling the DMA channel or clearing the timeout/DMA event before the timeout-triggered DMA request has completed. If the ISR disables DMA immediately on RX TIMEOUT, it may race with the final DMA transfer.

    We would suggest testing with UART RX DMA trigger enabled for normal reception and RX TIMEOUT interrupt used only to delimit the packet. The MSPM33 SDK also includes UART RX multibyte FIFO DMA interrupt examples that can be used as a starting point for DMA-based UART reception.

  • Hi Jojo,

    I tried the recommended approach of using the UART RX DMA trigger to move incoming bytes into the DMA buffer and using RX TIMEOUT as an end-of-packet indication.
    I configured the DMA transfer size to 64 bytes, while the actual RX packet size is currently 5 bytes. However, when I read the remaining DMA transfer count in the DL_UART_INTERRUPT_RX_TIMEOUT_ERROR interrupt using the DL_DMA_getTransferSize(DMA0, DMA0_CH1_CHAN_ID) API, the returned value is 0 instead of 5. I also checked the transfer count in the DL_UART_IIDX_DMA_DONE_RX interrupt and observed the same result, with the count still reported as 0.
    I am attaching my SysConfig configuration for UART and DMA for your reference.
    Please note that I am able to receive a fixed number of bytes using DMA. However, my application receives multiple commands with variable lengths, and I need a reliable way to determine the actual number of bytes received. I would appreciate a quick resolution, as this is an urgent issue.

    Below is the code patch for reading DMA transfer size in DL_UART_INTERRUPT_RX_TIMEOUT_ERROR  interrupt

    #define MPESTI_T_DMA_RX_WINDOW 64U
    // Patch to read DMA transfer size
              DL_DMA_disableChannel(DMA0, DMA0_CH1_CHAN_ID);
       
                volatile uint32_t remaining;
                volatile uint8_t dmaReceived;
               
                /* RX timeout means frame boundary in DMA mode. */
                remaining = DL_DMA_getTransferSize(DMA0, DMA0_CH1_CHAN_ID);  ==> return size as zero
                dmaReceived = MPESTI_T_DMA_RX_WINDOW - remaining;
                target->nwk.rxLen = dmaReceived;

    Regards,

    Rajeshwary

  • Hi Rajeshwary,

    Thanks for the update and the SysConfig screenshot. The new approach of using UART RX interrupt as the DMA RX trigger and RX TIMEOUT as the frame boundary is the right direction.

    A few things to check:

    1. In single-transfer DMA mode, the DMA transfer size of 64 means 64 UART RX trigger events are required before the DMA block is complete. If the actual received packet is 5 bytes, the expected remaining count would be 59, not 5. The received length would then be 64 - 59 = 5 bytes.

    2. Please read the DMA transfer size before disabling the DMA channel. In the code patch, the DMA channel is disabled before calling DL_DMA_getTransferSize(). Disabling the DMA channel may affect the live channel state, so the count should be captured first.

    3. The screenshot shows “Configure Transfer Size” unchecked. If SysConfig is not setting the transfer size, please make sure the application calls DL_DMA_setTransferSize(DMA0, DMA0_CH1_CHAN_ID, 64) every time before enabling the DMA channel.

    4. Please also confirm that the RX DMA destination address increments after each transfer. For UART RX DMA, the source address should remain unchanged at UART RXDATA, but the destination address must increment so each received byte is stored into the next buffer location.

    As an alternative to using DL_DMA_getTransferSize(), we recommend reading the current DMA destination address at RX TIMEOUT and calculating the received length as:

    rxLen = current DMA destination address - start address of RX buffer

    This avoids ambiguity around reading the transfer-size register after the DMA channel is disabled or after DMA completion.

    Recommended timeout handling order:

    - Disable the UART RX DMA event to prevent new DMA requests
    - Read the DMA destination address or transfer count
    - Calculate rxLen
    - Disable/re-arm the DMA channel for the next packet
    - Re-enable UART RX DMA event

  • Hi Jojo,

    I tried the with recommended timeout handling order as suggested with DMA RX trigger source as UART RX interrupt. Still, I always receive DL_DMA_getTransferSize() output as 0. I checked the DMA destination address in the RX Timeout as below

    current DMA destination address = 0x3000800D

    start address of RX buffer = 0x30007FCD

    rxLen = 64.

    Once I move my DMA trigger source to UART RX Timeout interrupt I always receive DL_DMA_getTransferSize() output as 4 and 

    current DMA destination address = 0x30007FD1

    start address of RX buffer = 0x30007FCD

    rxLen = 4.

    Have you tried the suggested solution? Can you provide me test code for it?

    Looking for quick resolution of the issue.

    Regards,

    Rajeshwary

**Attention** This is a public forum