I am programming on a TM4C1294. I have a fairly complicated project that has been working well for a number of months until a recently discovered bug described below.
I use SPI2 and uDMA to receive data from a secondary processor (Tiva as slave). The SPI receives 32 bit data at 40KHz, but uses the uDMA to ping-pong transfer this data into a buffer and interrupt the processor at 1kHz. Since the SPI accepts up to 16 bit words, the uDMA is set to transfer 80 words before it interrupts the uP.
I use the USB to receive commands from the PC. Everything was working until I introduced a longer command (~300 bytes). When I send this command usually the processor locks up. If I reduce the length of the command a little bit the lock up becomes less frequent. Also, if I disable SSI interrupts, the command seems to always be received correctly at its full length.
In the SPI interrupt routine, I placed a GPIO toggle and noticed that I enter the ISR at the expected 1khz normally, but after I send my long USB command, I begin to enter the ISR much faster (like 184 khz). I have attached my SPI init and ISR code for reference.
Possible work-around seems to be making my command smaller or disabling data receipt from the secondary processor, both of which are feasible. I just don't want to mask another bigger problem.
//*****************************************************************************
//
// The control table used by the uDMA controller. This table must be aligned
// to a 1024 byte boundary.
//
//*****************************************************************************
#if defined(ewarm)
#pragma data_alignment=1024
uint8_t pui8ControlTable[1024];
#elif defined(ccs)
#pragma DATA_ALIGN(pui8ControlTable, 1024)
uint8_t pui8ControlTable[1024];
#else
uint8_t pui8ControlTable[1024] __attribute__ ((aligned(1024)));
#endif
static uint16 PeakDataBufferA[DATA_BUFFER_SIZE]; //Transfer data here to give pointer to data processor to handle
static uint16 PeakDataBufferB[DATA_BUFFER_SIZE];
static uint16 blockSize = 0;
//*****************************************************************************
//
// The interrupt handler for uDMA errors. This interrupt will occur if the
// uDMA encounters a bus error while trying to perform a transfer. This
// handler just increments a counter if an error occurs.
//
//*****************************************************************************
static uint32_t g_ui32uDMAErrCount = 0;
static Void uDMAErrorHandler(UArg arg)
{
uint32_t ui32Status;
//
// Check for uDMA error bit
//
ui32Status = MAP_uDMAErrorStatusGet();
//
// If there is a uDMA error, then clear the error and increment
// the error counter.
//
if(ui32Status)
{
MAP_uDMAErrorStatusClear();
g_ui32uDMAErrCount++;
}
}
//*****************************************************************************
//
// The interrupt handler for uDMA interrupts from the SPI channel.
// DISPATCHED FROM THE SPI MODULE.
// This interrupt will notify the data Processor.
//
//*****************************************************************************
static Void uDMAIntHandler(UArg arg)
{
uint32_t ui32Status;
uint32_t ui32Mode;
MAP_GPIOPinWrite(GPIO_PORTA_BASE, GPIO_PIN_4, ~0);
//
// Read the interrupt status of the SSI.
//
ui32Status = MAP_SSIIntStatus(SSI2_BASE, 1);
//
// Clear any pending status, even though there should be none since no SSI
// interrupts were enabled. If SSI error interrupts were enabled, then
// those interrupts could occur here and should be handled. Since uDMA is
// used for both the RX and TX, then neither of those interrupts should be
// enabled.
//
MAP_SSIIntClear(SSI2_BASE, ui32Status);
MAP_GPIOPinWrite(GPIO_PORTA_BASE, GPIO_PIN_4, 0);
//
// Check the DMA control table to see if the ping-pong "A" transfer is
// complete. The "A" transfer uses receive buffer "A", and the primary
// control structure.
//
ui32Mode = MAP_uDMAChannelModeGet(UDMA_CHANNEL_SSI2RX | UDMA_PRI_SELECT);
//
// If the primary control structure indicates stop, that means the "A"
// receive buffer is done. The uDMA controller should still be receiving
// data into the "B" buffer.
//
if(ui32Mode == UDMA_MODE_STOP)
{
if(controlWord & CONTROL_WORD_DataMode)
{
DataProcessorTask_NotifyVideoDataReady(&PeakDataBufferA[0], blockSize);
}
else
{
DataProcessorTask_NotifyDataReady(&PeakDataBufferA[0], blockSize);
}
//
// Set up the next transfer for the "A" buffer, using the primary
// control structure. When the ongoing receive into the "B" buffer is
// done, the uDMA controller will switch back to this one.
MAP_uDMAChannelTransferSet(UDMA_CHANNEL_SSI2RX | UDMA_PRI_SELECT,
UDMA_MODE_PINGPONG,
(void *)(DATA_SSI_BASE + SSI_O_DR),
&PeakDataBufferA[0], blockSize);
}
//
// Check the DMA control table to see if the ping-pong "B" transfer is
// complete. The "B" transfer uses receive buffer "B", and the alternate
// control structure.
//
ui32Mode = MAP_uDMAChannelModeGet(UDMA_CHANNEL_SSI2RX | UDMA_ALT_SELECT);
//
// If the alternate control structure indicates stop, that means the "B"
// receive buffer is done. The uDMA controller should still be receiving
// data into the "A" buffer.
//
if(ui32Mode == UDMA_MODE_STOP)
{
if(controlWord & CONTROL_WORD_DataMode)
{
DataProcessorTask_NotifyVideoDataReady(&PeakDataBufferB[0], blockSize);
}
else
{
DataProcessorTask_NotifyDataReady(&PeakDataBufferB[0], blockSize);
}
//
// Set up the next transfer for the "B" buffer, using the alternate
// control structure. When the ongoing receive into the "A" buffer is
// done, the uDMA controller will switch back to this one.
MAP_uDMAChannelTransferSet(UDMA_CHANNEL_SSI2RX | UDMA_ALT_SELECT,
UDMA_MODE_PINGPONG,
(void *)(DATA_SSI_BASE + SSI_O_DR),
&PeakDataBufferB[0], blockSize);
}
}
//--------------------------------------------------------------------
// FPGA Enable Displacement Data transmission
//--------------------------------------------------------------------
void FPGA_DataChannel_Initialize(void)
{
MAP_SysCtlPeripheralEnable(SYSCTL_PERIPH_UDMA);
//--------------------------------------
// Set up SSI port for FGPA data channel
//--------------------------------------
MAP_SysCtlPeripheralEnable(SYSCTL_PERIPH_SSI2);
// Configure and enable the SSI port for SPI slave mode. Use SSI2,
// system clock supply, idle clock level low and active low clock in
// freescale SPI mode, slave mode, 10MHz SSI frequency, and 16-bit data.
// For SPI mode, you can set the polarity of the SSI clock when the SSI
// unit is idle. You can also configure what clock edge you want to
// capture data on. Please reference the datasheet for more information on
// the different SPI modes.
MAP_SSIConfigSetExpClk(DATA_SSI_BASE, PRM_GetSystemClockFrequency(), SSI_FRF_MOTO_MODE_0,
SSI_MODE_SLAVE, 10000000, 16);
// Enable the uDMA controller error interrupt. This interrupt will occur
// if there is a bus error during a transfer.
MAP_IntEnable(INT_UDMAERR);
// Enable the uDMA controller.
MAP_uDMAEnable();
// Point at the control table to use for channel control structures.
MAP_uDMAControlBaseSet(pui8ControlTable);
MAP_uDMAChannelAssign(UDMA_CH12_SSI2RX);
// Enable the SSI module
MAP_SSIEnable(DATA_SSI_BASE);
MAP_SSIDMAEnable(DATA_SSI_BASE, SSI_DMA_RX);
// Put the attributes in a known state for the uDMA software channel.
// These should already be disabled by default.
MAP_uDMAChannelAttributeDisable(UDMA_CHANNEL_SSI2RX,
UDMA_ATTR_USEBURST | UDMA_ATTR_ALTSELECT |
(UDMA_ATTR_HIGH_PRIORITY | UDMA_ATTR_REQMASK));
// Configure the control parameters for the SSI channel. The channel
// will be used to transfer between the SPI RX FIFO and memory buffers,
// 16 bits at a time. Therefore the data size is 16 bits, and the address
// increment is 16 bits for the destination. The arbitration size will be
// set to 4, which causes the uDMA controller to rearbitrate after 4 items
// are transferred. This keeps this channel from hogging the uDMA controller
// once the transfer is started, and allows other channels cycles if they
// are higher priority.
//
MAP_uDMAChannelControlSet(UDMA_CHANNEL_SSI2RX | UDMA_PRI_SELECT,
UDMA_SIZE_16 | UDMA_SRC_INC_NONE | UDMA_DST_INC_16 |
UDMA_ARB_4);
MAP_uDMAChannelControlSet(UDMA_CHANNEL_SSI2RX | UDMA_ALT_SELECT,
UDMA_SIZE_16 | UDMA_SRC_INC_NONE | UDMA_DST_INC_16 |
UDMA_ARB_4);
//Set-up uDMA interrupts:
Hwi_Handle hwi;
hwi = Hwi_create(INT_UDMAERR, uDMAErrorHandler, NULL, NULL);
if (!hwi)
{
System_abort("Couldn't create uDMA error hwi");
}
hwi = Hwi_create(INT_SSI2, uDMAIntHandler, NULL, NULL);
if (!hwi)
{
System_abort("Couldn't create uDMA hwi");
}
//Set-up interrupt for pin used to frame video data:
MAP_GPIOIntTypeSet(GPIO_PORTP_BASE, GPIO_PIN_5, GPIO_DISCRETE_INT | GPIO_FALLING_EDGE);
hwi = Hwi_create(INT_GPIOP5, gpioP5_IntHandler, NULL, NULL);
if(!hwi)
{
System_abort("Couldn't create gpio P5 hwi");
}
//Set-up interrupt for pin used to frame peak data:
MAP_GPIOIntTypeSet(GPIO_PORTP_BASE, GPIO_PIN_0, GPIO_DISCRETE_INT | GPIO_FALLING_EDGE);
hwi = Hwi_create(INT_GPIOP0, gpioP0_IntHandler, NULL, NULL);
if(!hwi)
{
System_abort("Couldn't create gpio P0 hwi");
}
}