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.

TMS570LC4357: DMA Control Registers stop being updated when used with SCI RX

Part Number: TMS570LC4357

Hello, 

We are using the DMA for Receive and Transmit over the serial port (sci1, sci2), to work with varying length data frame. Our strategy, as it seems to be common in various threads, is to poll the WCP to verify the number of bytes received. We have adopted a dummy DMA request in order for the WCP to be updated after the channel is arbitrated out. This strategy works well and we are able to have a functional code. However, after a while, the WCP stops being updated, even though data still seems to be received and transferred to the appropriate memory location. This issue does not happen in a constant manner, it may be after only a few hundreds received frame or thousands, but seems to be always appearing if we let the process running for long enough... It is critical for us to know where we are at in the reception, and not just have the data. It is also not possible to use a mechanism where the length of the data to be received will first be sent. 

We initially had the dummy request being low priority (CH31), but also tried High priority (CH0) to verify that it was not an issue of the low priority request being arbitrated out by the higher priority channel used for the DMA SCI RX.  

So our question: what could prevent/stop the WCP to be properly updated, and what could be done in order to ensure a reliable polling of the WCP. We also added a mechanism to verify the FBACTC if the RX channel is active, but it does not solve the issue. (it seems that the FBACTC is the new name of PBACTC as in older threads https://e2e.ti.com/support/microcontrollers/hercules/f/312/t/158061?SCI-using-DMA-in-a-polled-mode)

Additional information about the implementation:

We are testing the code on a LC4357 Devkit. 

We did allocate a memory space for the received data as suggested in : https://e2e.ti.com/support/archive/launchyourdesign/m/boosterpackcontest/666341

We use a circular buffer to receive the data, which uses the AUTO-INIT feature. we have an external mechanism to poll the rx buffer and keep track of where we are in the buffer. 

We use a dummy kickstart with a digital loopback from SCI_TX to SCI_RX to ensure that the first time we go and get the WCP values, they would hold valid data (i.e we know that we would be polling starting at index 1 in the buffer)

here is a simplified version of the code that is used :

#include "SYS/sys.h"
#include "COMM/COMMPORTS/COMMDMA/commDMASCI.h"
#include "COMM/COMMPORTS/commSCI.h"

#include "HL_sys_dma.h"
#include "HL_sci.h"
#include "HL_sys_vim.h"

#define DUMMY_DMA_ENABLE

#define SCI_RX_DMA_CHP1     DMA_CH6     /*!<RX on Port 1 is set to channel 6 */
#define SCI_TX_DMA_CHP1     DMA_CH7     /*!<TX on Port 1 is set to channel 7 */

#define SCI_RX_DMA_RQP1     DMA_REQ28   /*!<RX on Port 1 is request 28 */
#define SCI_TX_DMA_RQP1     DMA_REQ29   /*!<TX on Port 1 is request 29 */


#ifdef DUMMY_DMA_ENABLE
#define SCI_DUMMY_DMA_CH     DMA_CH31     /*!<RX on Port 1 is set to channel 6 */
#endif

#define SCI_MAX_DMA_BUFFER  256        /*!< Maximum buffer for dma transmission */

#pragma SET_DATA_SECTION(".ramDMA")
static char _txBufferP1[SCI_MAX_DMA_BUFFER]; /*!< transmit Buffer for Port 1 */
#pragma SET_DATA_SECTION()

#if ((__little_endian__ == 1) || (__LITTLE_ENDIAN__ == 1))
    uint8 dest_addr_offset=0;       /* 0 for LE */
#else
    uint8 dest_addr_offset=3;       /* 3 for BE */
#endif

static g_dmaCTRL _dma_config_rx1;    /*!< Control Packet for RX on SCI1 */
static g_dmaCTRL _dma_config_tx1;    /*!< Control Packet for TX on SCI1 */


static volatile uint32_t _u32DMACompFlagTX1;  /*!< Control Flag for tX on SCI1 */
static volatile uint32_t _u32DMACompFlagRX1;  /*!< Control Flag for RX on SCI1 */

#ifdef DUMMY_DMA_ENABLE

static g_dmaCTRL _dma_config_DUMMY;    /*!< Control Packet for DUMMY */
static volatile uint32_t _u32DMACompFlagDUMMY;  /*!< Control Flag for DUMMY*/
#pragma SET_DATA_SECTION(".ramDMA")
static uint8_t _u8Dummy[2]={0, 1};
#pragma SET_DATA_SECTION()

static void _sciDmaDummyKickStart(uint8_t u8PortNumber);
static void _sciDmaSetDummyPacket(void);
static void _sciDmaDummyProcess(void);
#endif


static dmaBASE_t* dmaTMPBASE = dmaREG;
static dmaRAMBASE_t* dmaTMPRAMBASE = dmaRAMREG;

/****************************************************************************
function    : scidmaConfig
*//*! \brief \b
Description: Function called to set up dma sci on a port
****************************************************************************/
void scidmaConfig(void)
{

    uint8_t u8SysMode = (_get_CPSR() & 0x1F);

    /* CPU must be in privileged mode to change parameters */
    if (u8SysMode == UNSAFE)
    {
        SYS_MODE_SAFE();
    }

    /* Enable DMA */
    dmaEnable();
#ifdef DUMMY_DMA_ENABLE
    _sciDmaSetDummyPacket();
#endif

	/***********************RX************************************/
    /* Enable Interrupt after reception of data */
    dmaEnableInterrupt(SCI_RX_DMA_CHP1, BTC, DMA_INTA);       /* DMA_CH0 is highest priority */

    /* assigning dma request: channel-0 with request line - 1  - TX*/
    /* DMA Request 29 is for LIN ( SCI2) Transmit */
    /* Refer Data sheet - Default DMA Request Map section */

    dmaReqAssign(SCI_RX_DMA_CHP1, SCI_RX_DMA_RQP1); //request 28 for rx
    dmaSetNotification(SCI_RX_DMA_CHP1, BTC,scidmaCBNotification );

    //setting the control packet for RX
    /* - Populate dma control packets structure */
    _dma_config_rx1.CHCTRL    = 0;                 /* channel control            */
    _dma_config_rx1.ELCNT     = 1;                 /* element count              */
    _dma_config_rx1.ELDOFFSET = 0;                 /* element destination offset */
    _dma_config_rx1.ELSOFFSET = 0;                 /* element source offset      */
    _dma_config_rx1.FRDOFFSET = 0;                 /* frame destination offset   */
    _dma_config_rx1.FRSOFFSET = 0;                 /* frame source offset        */
    _dma_config_rx1.PORTASGN  = PORTB_READ_PORTA_WRITE;                 /* port b                     */
    _dma_config_rx1.RDSIZE    = ACCESS_8_BIT;      /* read size                  */
    _dma_config_rx1.WRSIZE    = ACCESS_8_BIT;      /* write size                 */
    _dma_config_rx1.TTYPE     = FRAME_TRANSFER ;   /* transfer type              */
    _dma_config_rx1.ADDMODERD = ADDR_FIXED;         /* address mode read          */
    _dma_config_rx1.ADDMODEWR = ADDR_INC1;        /* address mode write         */
    _dma_config_rx1.AUTOINIT  = AUTOINIT_ON;      /* autoinit                   */

    /* - Populate dma control packets structure */
    _dma_config_rx1.SADD       = (uint32)(&(SCIREGPORT1->RD))+dest_addr_offset;                       /* source address             */
    /* SCI2 Multibuffer mode      */
    //_dma_config_rx1.DADD   = (uint32)(rxBufferP1);  /* In big endianism devices, the destination address needs to be adjusted */

    _u32DMACompFlagRX1 = 0x55AAD09E;

	/***********************TX************************************/
	dmaSetNotification(SCI_TX_DMA_CHP1, BTC,scidmaCBNotification );

	dmaEnableInterrupt(SCI_TX_DMA_CHP1, BTC, DMA_INTA);       /* DMA_CH0 is highest priority */
	dmaReqAssign(SCI_TX_DMA_CHP1,SCI_TX_DMA_RQP1); //request 29 for tx

	//setting the control packet for TX
	/* - Populate dma control packets structure */
	_dma_config_tx1.CHCTRL    = 0;                 /* channel control            */
	_dma_config_tx1.ELCNT     = 1;                 /* element count              */
	_dma_config_tx1.ELDOFFSET = 0;                 /* element destination offset */
	_dma_config_tx1.ELSOFFSET = 0;                 /* element source offset      */
	_dma_config_tx1.FRDOFFSET = 0;                 /* frame destination offset   */
	_dma_config_tx1.FRSOFFSET = 0;                 /* frame source offset        */
	_dma_config_tx1.PORTASGN  = PORTA_READ_PORTB_WRITE;                 /* port b                     */
	_dma_config_tx1.RDSIZE    = ACCESS_8_BIT;      /* read size                  */
	_dma_config_tx1.WRSIZE    = ACCESS_8_BIT;      /* write size                 */
	_dma_config_tx1.TTYPE     = FRAME_TRANSFER ;   /* transfer type              */
	_dma_config_tx1.ADDMODERD = ADDR_INC1;         /* address mode read          */
	_dma_config_tx1.ADDMODEWR = ADDR_FIXED;        /* address mode write         */
	_dma_config_tx1.AUTOINIT  = AUTOINIT_OFF;      /* autoinit                   */

	_dma_config_tx1.SADD       = (uint32)_txBufferP1;                       /* source address             */
	/* SCI2 Multibuffer mode      */
	_dma_config_tx1.DADD   = (uint32)(&(SCIREGPORT1->TD))+dest_addr_offset;  /* In big endianism devices, the destination address needs to be adjusted */

	_u32DMACompFlagTX1 = 0x55AAD09E;
	}

    /* CPU must be in privileged mode to change parameters */
    if (u8SysMode == UNSAFE)
    {
        SYS_MODE_UNSAFE();
    }

} /* scidmaConfig */


/****************************************************************************
function    : scidmaSend
*//*! \brief \b
Description: Function called to send data through dma on a sciport
\param       u32DataLength:  the number of bytes to send
\param       ucPData: pointer to the data to send
****************************************************************************/
void scidmaSend(char *data, uint32_t u32DataLength)
{
    if (u32DataLength > SCI_MAX_DMA_BUFFER)
    {
        return;
    }

    uint8_t u8SysMode = (_get_CPSR() & 0x1F);

    /* CPU must be in privileged mode to change parameters */
    if (u8SysMode == UNSAFE)
    {
        SYS_MODE_SAFE();
    }

    /* Wait for the DMA to complete any existing transfers */
    while(_u32DMACompFlagTX1 != 0x55AAD09E);
    /* Reset the Flag to not Done*/
    _u32DMACompFlagTX1 = ~0x55AAD09E;

    memcpy(_txBufferP1 , data, u32DataLength);
    /* - Populate dma control packets structure */
    _dma_config_tx1.FRCNT= u32DataLength;

    /* - setting dma control packets for transmit */
    dmaSetCtrlPacket(SCI_TX_DMA_CHP1,_dma_config_tx1);

    /* - setting the dma channel to trigger on h/w request */
    dmaSetChEnable(SCI_TX_DMA_CHP1, DMA_HW);

    /* Enable TX DMA */
    SCIREGPORT1->SETINT = (uint32) (1U << 16U);


    /* CPU must be in privileged mode to change parameters */
    if (u8SysMode == UNSAFE)
    {
        SYS_MODE_UNSAFE();
    }
} /* scidmaSend */

/****************************************************************************
function    : scidmaReceive
*//*! \brief \b
Description: Function called to receive data through dma on a sciport
\param       u32DataLength:  the number of bytes to receive
\param       ucPData: pointer to the data to send
****************************************************************************/
void scidmaReceive(uint32_t u32DataLength, unsigned char * ucPData)
{

    if (u32DataLength > SCI_MAX_DMA_BUFFER)
    {
        return;
    }

    uint8_t u8SysMode = (_get_CPSR() & 0x1F);

    /* CPU must be in privileged mode to change parameters */
    if (u8SysMode == UNSAFE)
    {
        SYS_MODE_SAFE();
    }

    _dma_config_rx1.DADD   = (uint32)(ucPData);
    _dma_config_rx1.FRCNT  = u32DataLength;                       /* frame count                */

    /* - setting dma control packets for transmit */
    dmaSetCtrlPacket(SCI_RX_DMA_CHP1,_dma_config_rx1);

    /* - setting the dma channel to trigger on h/w request */
    dmaSetChEnable(SCI_RX_DMA_CHP1, DMA_HW);

    /* Enable RX DMA */
    SCIREGPORT1->CLEARINT = (uint32)(1U << 9U);
    SCIREGPORT1->SETINT = (uint32)(3U << 17U);

	//ensure that the dma receive wcp are updated with new values
    _sciDmaDummyKickStart();

    /* CPU must be in privileged mode to change parameters */
    if (u8SysMode == UNSAFE)
    {
        SYS_MODE_UNSAFE();
    }
	
}/* scidmaReceive */


/****************************************************************************
function    : scidmaCBNotification
*//*! \brief \b
Description: Function called to from dma interrupt  a call back for receive interrupt
\param       u32Channel: the channel of the interrupt
\param       eIntType:  the type of interrupt
****************************************************************************/
void scidmaCBNotification(uint32 u32Channel, dmaInterrupt_t eIntType)
{
    uint8_t u8SysMode = (_get_CPSR() & 0x1F);
    /* CPU must be in privileged mode to change parameters */
    if (u8SysMode == UNSAFE)
    {
        SYS_MODE_SAFE();
    }

    switch(u32Channel)
    {

    case SCI_TX_DMA_CHP1:
        SCIREGPORT1->CLEARINT = (uint32)(1U << 16U);
        _u32DMACompFlagTX1 = 0x55AAD09E;
        break;
#ifdef DUMMY_DMA_ENABLE
    case SCI_DUMMY_DMA_CH:
        _u32DMACompFlagDUMMY = 0x55AAD09E;
        break;
#endif
    case SCI_RX_DMA_CHP1:
    default:
        break;
    }

    /* CPU must be in privileged mode to change parameters */
    if (u8SysMode == UNSAFE)
    {
        SYS_MODE_UNSAFE();
    }
}

/****************************************************************************
function    : scidmaGetRXCount
*//*! \brief \b
Description: Function called to get the number of frames received on a sci port through DMA
\param       u8PortNumber: the sci port
\param       pu32Count:  pointer to the data to return
****************************************************************************/
void scidmaGetRXCount(uint8_t u8PortNumber, uint32_t* pu32Count)
{
    if (pu32Count == NULL)
    {
        return;
    }

    uint8_t u8SysMode = (_get_CPSR() & 0x1F);
    /* CPU must be in privileged mode to change parameters */
    if (u8SysMode == UNSAFE)
    {
        SYS_MODE_SAFE(); //SET the CPU in privileged mode
    }

#ifdef DUMMY_DMA_ENABLE
    _sciDmaDummyProcess();
#endif

	//verify if we are the working channel 
    if ( (dmaTMPBASE->FBACDADDR >= _dma_config_rx1.DADD) && (dmaTMPBASE->FBACDADDR < (_dma_config_rx1.DADD+_dma_config_rx1.FRCNT) ) )
    {
         *pu32Count = ( ( (dmaTMPBASE->FBACTC) >> 16U ) & 0x1FFF);
    }
    else
    {
		*pu32Count = ( ( (dmaTMPRAMBASE->WCP[SCI_RX_DMA_CHP1].CTCOUNT) >> 16U ) & 0x1FFF);
    }

    /* CPU must be in privileged mode to change parameters */
    if (u8SysMode == UNSAFE)
    {
        SYS_MODE_UNSAFE(); //SET the CPU in user mode
    }

}

#ifdef DUMMY_DMA_ENABLE
void _sciDmaDummyKickStart(void)
{
    //verifiy that there were no transfer in progress on the TXport
    while(_u32DMACompFlagTX1 != 0x55AAD09E);
    sysTimeWait10us( 10 );
    sciEnableLoopback(SCIREGPORT1, Digital_Lbk);
    sysTimeWait10us( 10 );
    sciSendByte(SCIREGPORT1, 0xFF);
    sysTimeWait10us( 10 );
    _sciDmaDummyProcess();
    sysTimeWait10us( 10 );
    sciDisableLoopback(SCIREGPORT1);
    sysTimeWait10us( 10 );
}

void _sciDmaSetDummyPacket()
{
    dmaEnableInterrupt(SCI_DUMMY_DMA_CH, BTC, DMA_INTA);       /* DMA_CH0 is highest priority */

    dmaSetNotification(SCI_DUMMY_DMA_CH, BTC,scidmaCBNotification );

    //setting the control packet for RX
    /* - Populate dma control packets structure */
    _dma_config_DUMMY.CHCTRL    = 0;                 /* channel control            */
    _dma_config_DUMMY.ELCNT     = 1;                 /* element count              */
    _dma_config_DUMMY.ELDOFFSET = 0;                 /* element destination offset */
    _dma_config_DUMMY.ELSOFFSET = 0;                 /* element source offset      */
    _dma_config_DUMMY.FRDOFFSET = 0;                 /* frame destination offset   */
    _dma_config_DUMMY.FRSOFFSET = 0;                 /* frame source offset        */
    _dma_config_DUMMY.PORTASGN  = PORTA_READ_PORTA_WRITE;                 /* port b                     */
    _dma_config_DUMMY.RDSIZE    = ACCESS_8_BIT;      /* read size                  */
    _dma_config_DUMMY.WRSIZE    = ACCESS_8_BIT;      /* write size                 */
    _dma_config_DUMMY.TTYPE     = FRAME_TRANSFER ;   /* transfer type              */
    _dma_config_DUMMY.ADDMODERD = ADDR_FIXED;         /* address mode read          */
    _dma_config_DUMMY.ADDMODEWR = ADDR_FIXED;        /* address mode write         */
    _dma_config_DUMMY.AUTOINIT  = AUTOINIT_OFF;      /* autoinit                   */

    /* - Populate dma control packets structure */
    _dma_config_DUMMY.SADD       = (uint32)(&_u8Dummy[0]);                       /* source address             */
    /* SCI2 Multibuffer mode      */
    _dma_config_DUMMY.DADD   = (uint32)(&_u8Dummy[1]);

    _dma_config_DUMMY.FRCNT  = 1U;                       /* frame count                */

    /* - setting dma control packets for transmit */
    dmaSetCtrlPacket(SCI_DUMMY_DMA_CH,_dma_config_DUMMY);

}

void _sciDmaDummyProcess()
{
    _u8Dummy[0] = ~_u8Dummy[0];

    _u32DMACompFlagDUMMY = ~0x55AAD09E;

    /* - setting the dma channel to trigger on s/w request */
    dmaSetChEnable(SCI_DUMMY_DMA_CH, DMA_SW);

    while(_u32DMACompFlagDUMMY != 0x55AAD09E);

}
#endif //DUMMY_DMA_ENABLE

Thank you, 

  • Further note, in the attached code, we tried switching of the scirxint as someone mentionned in the thread : https://e2e.ti.com/support/microcontrollers/hercules/f/312/t/396924?TMS570-missing-bytes-with-SCI-reception-DMA-involved-

    we have tried with and without this fix and the result is the same

  • Hi Ugo,

    I have been trying to do this as well, but with 2 SCI ports at the same time. What is the baudrate you are using? For us, this happens to our higher baudrate port (115200), but does not appear to happen with our lower baudrate port (9600). At least with tests of up to 3 minutes or so.

    What is the length of time you have to leave the program running in order to first notice the issue? From my testing on the 115200 baudrate, it has worked properly for anywhere from 5-50 seconds before showing issues. The working set can start updating again, but usually I have filled out my circular buffer more than once in that time period. I have done some timing instrumentation and had one test where the working set for the high baudrate channel didn't update for 7.4 seconds. During this time the lower baudrate port's RX DMA working set did update and receive several hundred characters.

    If you do find a solution I would appreciate it if you could share your findings. I'll post my own solution if I find one.

    Adam

  • Hi Adam, 

    We work at 115200, as this is the rate we need the communication to occur. 

    The issue occurs anytime between a few seconds to hours. 

    We are still working on a solution, and will try top post an update.

    In the meantime I will verify if setting a lower baudrate does solve the issue, even though it is not a longterm solution.

    Ugo

  • Hi Ugo,

    I think I have solved this with the help of one of my colleagues. Turns out that because the TMS570 has 2 FIFOs for active channels, you have to trigger 2 simultaneous high priority dummy requests to force an arbitration. Triggering 1 high priority channel isn't enough and will only force arbitration of the channel you want by coincidence.

    I noticed when logging the contents of the WCP and FIFOA / FIFOB working sets that one of my uart channels (whose working set wasn't updating) was sitting in the FIFOB working set and updating there. However, I couldn't just assume that FIFOB working set could be used because the FIFOB active channel was set to 0. Presumably because it wasn't actively servicing a request from the SCI hardware when I checked the register.

    Once I triggered 2 high priority dummy DMA requests, so they would take FIFOA and FIFOB, my communication errors have been resolved. I am currently receiving at a rate of 1500 bytes per second without communication errors for at least 20 minutes. Baudrate is 115200. Previously it was unlikely to get passed 1 minute without a communication error. You may have to trigger both requests in the same register write in order to reliably force an arbitration event.

    Hopefully my solution works for you as well.

    Adam

  • Hi Adam, 

    It does seem that it is a 2 fifo issue as you mentioned. For now it seems to be working. I modified my code so there is no more need to use the dummy request. This is done by looking at fifo A and B current destination address and comparing it to the receive buffer address range. if it is not in the range then it means that the WCP has the proper values. 

    Thanks for the Tip.

    Ugo