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.

TMS320F28388D: uDMA finished reading UART before all expected data received

Part Number: TMS320F28388D

I am currently developing the UART communication on the CM core with the usage of the uDMA.

  • Arm Compiler Version   18.12.5
  • F2838x Support Library v3.04.00.00

I try to receive data from the RX channel of the UART in a mix between reading out the FIFO directly and using the uDMA. The reason I do this mix is because i have different frames with different lenghts. For this purpose, the first 3 bytes (header) of each frame are read directly from the FIFO with the driverLib. The header describes the information of the following data. After that I know exactly how much data follow. Now I pass this task on to the uDMA in order to receive the further data. Here the udma reports back it has received all the data but it happens too early (not all data are on the UART already).

 Frame:

A frame consists of a header, data and one CRC byte. The header contains the type of frame, a counter of the frame and, most importantly, the length of the following data.

For this example i have choose a small frame to represent the problem. In the image i have a logic analyser recording how the example frames look like.

The header is processed as follows. As soon as the FIFO is filled to 2/8 (UART_FIFO_RX2_8) an interrupt (UART_INT_RX) signals that the header should be in the FIFO. They are read out with the usage of driverlib and processed. This code is shown below.

Now I know exactly how much data has to be received to collect the full frame. This should be done by the uDMA. The uDMA starts to receive data correctly, but reports back a finish much too early. Not all data of the frame are on the UART at this time. This is shown in the next recording.

According to the UDMA_ControlTable, the uDMA is also configured as i expect.

The data are copyed in my buffer. I have partially filled the buffer with 0xFF before, that can be ignored.

0xC4, 0x00 and 0x09 are received from the FIFO with the driverLib. After this the uDMA starts work and collect the following bytes. After the 0x02 is received, the uDMA sends the interrupt (UART_INT_DMATX). In this moment i have a capture of the memory.

The uDMA fills the buffer with the wrong data until it reaches the dstEndAdress. What is the reason for it? Can this happen if the rx FIFO is empty in the moment it want to read?

 

The initialization of the UART and the uDMA.

    void initUart()
    {
        SysCtl_enablePeripheral(SYSCTL_PERIPH_CLK_UART0);

        UART_setConfig(UART0_BASE, CM_CLK_FREQ , 3125000, (UART_CONFIG_WLEN_8 | UART_CONFIG_STOP_ONE | UART_CONFIG_PAR_EVEN));

        UART_enableModule(UART0_BASE);
        UART_enableFIFO(UART0_BASE);

        // Loop Back disabled
        UART_disableLoopback(UART0_BASE);

        UART_registerInterrupt(INT_UART0, &sioRxTxIntHandler);

        UART_setFIFOLevel(UART0_BASE, UART_FIFO_TX1_8, UART_FIFO_RX2_8);
        UART_clearInterruptStatus(UART0_BASE, UART_INT_RX | UART_INT_DMARX | UART_INT_DMATX);

        UART_enableInterrupt(UART0_BASE, UART_INT_RX | UART_INT_DMATX);

    }

 void initDma()
    {
        SysCtl_enablePeripheral(SYSCTL_PERIPH_CLK_UDMA);

        // Enable DMA for Tx and Rx events
        //
        UART_enableDMA(UART0_BASE, UART_DMA_TX | UART_DMA_RX);

        //
        // Enable UDMA
        //
        UDMA_enable(UDMA_BASE);

        //
        // Point at the control table to use for channel control structures.
        //
        UDMA_setControlBase(UDMA_BASE, ucControlTable);

        UDMA_registerInterrupt(UDMA_ERR_INT, &dmaErrorInteruptHandler);

        //
        // Put the attributes in a known state for the uDMA software channel.
        // These should already be disabled by default.
        //
        UDMA_disableChannelAttribute(UDMA_BASE, UDMA_CHANNEL_UART0_TX, UDMA_CH_ATTR_ALL);
        UDMA_disableChannelAttribute(UDMA_BASE, UDMA_CHANNEL_UART0_RX, UDMA_CH_ATTR_ALL);

        UDMA_enableChannelAttribute(UDMA_BASE, UDMA_CHANNEL_UART0_RX, UDMA_CH_ATTR_HIGH_PRIORITY | UDMA_CH_ATTR_USEBURST);

        UDMA_setChannelControlParams(UDMA_BASE, (UDMA_CHANNEL_UART0_TX | UDMA_PRI_SELECT),
                                    (UDMA_SIZE_8 | UDMA_SRC_INC_8 | UDMA_DST_INC_NONE
                                     | UDMA_ARB_16));

        UDMA_setChannelControlParams(UDMA_BASE, (UDMA_CHANNEL_UART0_RX | UDMA_PRI_SELECT),
                                    (UDMA_SIZE_8 | UDMA_SRC_INC_NONE | UDMA_DST_INC_8
                                      | UDMA_ARB_16 | UDMA_NEXT_USEBURST));
    }

The transfer parameters of the uDMA filled like in code below. I have try the UDMA_MODE_AUTO and the UDMA_MODE_BASIC with the same results. 

    void rxConfigDMA(uint8_t* rxBufferDma, uint8_t transferSize)
    {
        UDMA_setChannelTransferParams(UDMA_BASE, (UDMA_CHANNEL_UART0_RX | UDMA_PRI_SELECT),
                                     (void *)(UART0_BASE + UART_O_DR), rxBufferDma, UDMA_MODE_AUTO,
                                     transferSize);
    }

The Interrupt handler looks like:

  __attribute__((ramfunc)) __interrupt void sioRxTxIntHandler(void)
    {
        isrStatusMask = UART_getInterruptStatus(UART0_BASE, UART_MASKED_INT);

        // Received Header + Data Byte
        // Interrupt called if 4 Byte in FIFO (3 byte Header + 1 Data byte)
        if ((isrStatusMask & UART_INT_RX) == UART_INT_RX)
        {
            UART_clearInterruptStatus(UART0_BASE, UART_INT_RX);

            while (UART_isDataAvailable(UART0_BASE))
            {
                isrByteRead = UART_readCharNonBlocking(UART0_BASE);

                // reads data until a new frame is detected ore fifo empty 
                if (!(stsCtrl.Control.Field.ReceivingFrame))  // only a control flag 
                {
                    // DSO2X_FT_CYCLIC_DATA_EXCH == 0xC4
                    if ((isrByteRead == DSO2X_FT_CYCLIC_DATA_EXCH) || (isrByteRead == DSO2X_FT_ACYCLIC) || (isrByteRead == DSO2X_FT_ERROR_FRAME))
                    {
                        // reset my buffer 
                        clearSioBuffer(&DSO2X_RxBuf[stsCtrl.Control.Field.RxWrBufIdx]);
                        // now a frame is in receiving mode
                        stsCtrl.Control.Field.ReceivingFrame = 1; // only a flag for control 
                    }
                
	            // add received Byte to buffer
                if (stsCtrl.Control.Field.ReceivingFrame)
                {
                    DSO2X_RxBuf[stsCtrl.Control.Field.RxWrBufIdx].F.raw[DSO2X_RxBuf[stsCtrl.Control.Field.RxWrBufIdx].ByteCount] = isrByteRead;
                    DSO2X_RxBuf[stsCtrl.Control.Field.RxWrBufIdx].ByteCount++;
                }

                // DSO2X_FRAME_HEAD_SIZE = 3
                if (DSO2X_RxBuf[stsCtrl.Control.Field.RxWrBufIdx].ByteCount >= DSO2X_FRAME_HEAD_SIZE)
                {
                    // calculate the lenght that should received by the uDMA       DSO2X_FRAME_HEAD_SIZE = 3     DSO2X_FRAME_BCC_SIZE = 1    
                    isrDmaReadLenght = DSO2X_RxBuf[stsCtrl.Control.Field.RxWrBufIdx].F.header.BLen + DSO2X_FRAME_HEAD_SIZE - DSO2X_RxBuf[stsCtrl.Control.Field.RxWrBufIdx].ByteCount + DSO2X_FRAME_BCC_SIZE;
                    if ((isrDmaReadLenght > 0) && ((isrDmaReadLenght + DSO2X_RxBuf[stsCtrl.Control.Field.RxWrBufIdx].ByteCount) < DSO2X_MAX_FRAME_SIZE))
                    {
                        if (!UDMA_isChannelEnabled(UDMA_BASE, UDMA_CHANNEL_UART0_RX))
                        {
                            UART_disableInterrupt(UART0_BASE, UART_INT_RX);
                            UART_enableInterrupt(UART0_BASE, UART_INT_DMARX);
                            UART_enableDMA(UART0_BASE, UART_DMA_RX);
                            rxConfigDMA(DSO2X_RxBuf[stsCtrl.Control.Field.RxWrBufIdx].F.raw + DSO2X_RxBuf[stsCtrl.Control.Field.RxWrBufIdx].ByteCount, isrDmaReadLenght);
                            DSO2X_RxBuf[stsCtrl.Control.Field.RxWrBufIdx].ByteCount += isrDmaReadLenght;
                            UDMA_enableChannel(UDMA_BASE, UDMA_CHANNEL_UART0_RX);
                        }

                        break;
                    }
                    else
                    {
                        stsCtrl.Control.Field.RxWrBufIdx++;
                        stsCtrl.Control.Field.ReceivingFrame = 0;
                    }
                }
            }
        }

        if ((isrStatusMask & UART_INT_DMARX) == UART_INT_DMARX)  // DMA Controller Finished Receiving Frame
        {
            UART_disableDMA(UART0_BASE, UART_DMA_RX);

            // reset interrupt mask
            UART_clearInterruptStatus(UART0_BASE, UART_INT_DMARX);
	        // disable the dma interrupt
            UART_disableInterrupt(UART0_BASE, UART_INT_DMARX);
	        // enable interrupt to receive the next header
            UART_enableInterrupt(UART0_BASE, UART_INT_RX);
	
	        // here are done some with the received data
	        // …

        }
    }

Additional Note

If I compile my code with optlevel=off, the code is slower. That leads to, that the uDMA starts here work later. It means the uDMA begin to collect data from the FIFO when the complete frame already there. In this case it works but it is no oppertunity because i must be fast and the frames can be much bigger as in the example.


  • Hi,

    Look like uDMA start to collect the data even before data is received in FIFO hence it's reading the stale data. Generally DMA should get triggered only after all the data is in FIFO which DMA will read. You could delay the DMA trigger or check for RXFF bit status to see if FIFO is full and then start the DMA. 

    Regards,

    Vivek Singh

  • That means it is not possible to let uDMA receive more data than the FIFO size? What can I do if my data bigger than the FIFO size? I have frames that are up to 100 Byte.

    Is that the way you prefere: I start the uDMA when the FIFO is full, read the data with the uDMA until the FIFO is empty. I repeat that until all data are received. I see multiple problems with that solution. I can not count the received data and i dont know when my frame is completed.  

    The other Problem: If I restart and config the uDMA and analyse the data after every 16 byte, i think i dont be faster as if i receive all data about the UARTs driverlib functions and handle the logic by myself.

    My main goal: i hoped that i can decrease my processor workload by the same or better performance by the usage of the uDMA.

  • Generally the way it works is that you program DMA trigger and when DMA receives the trigger, it does the required transfer and on next trigger it again does the transfer and it continues until full transfer (no of transfer programmed) is complete. But here, you are not using trigger instead initiating the transfer via SW hence this issue.