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,