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"); } }