I have a SPI flash on SPI0 on a TM4C1294. The transfers varies in length from single byte commands to a couple of kb when reading blocks.
I'm controlling the frame select pin with a GPIO from software, and using the SSI_DMARX interrupt to finish the transfer and shut off the frame pin.
This works great 100% of the time with transfers that are more than a few bytes, but for short transfers the interrupt doesn't trigger every time.
I'm guessing I have a race condition somehow, but I've tried everything I and think of, so now reaching out for help.
Here's the init code:
// Set up uDMA for external serial memories ROM_uDMAEnable(); memset(uDMAControlTable, 0, sizeof(uDMAControlTable)); ROM_uDMAControlBaseSet(uDMAControlTable); ROM_IntEnable(INT_UDMAERR); // SSI0 FOR EXT FLASH & FRAM SSIConfigSetExpClk(SSI0_BASE, SystemCoreClock, SSI_FRF_MOTO_MODE_0, SSI_MODE_MASTER, 16000000, 8); ROM_SSIEnable(SSI0_BASE); ROM_uDMAChannelAssign(UDMA_CH10_SSI0RX); ROM_uDMAChannelAssign(UDMA_CH11_SSI0TX); ROM_uDMAChannelAttributeDisable(UDMA_CH10_SSI0RX, UDMA_ATTR_ALL); //UDMA_ATTR_ALTSELECT | UDMA_ATTR_USEBURST | UDMA_ATTR_HIGH_PRIORITY | UDMA_ATTR_REQMASK); ROM_uDMAChannelAttributeEnable(UDMA_CH10_SSI0RX, UDMA_ATTR_USEBURST); ROM_uDMAChannelControlSet(UDMA_CH10_SSI0RX | UDMA_PRI_SELECT, UDMA_SIZE_8 | UDMA_SRC_INC_NONE | UDMA_DST_INC_8 | UDMA_ARB_4); ROM_uDMAChannelAttributeDisable(UDMA_CH11_SSI0TX, UDMA_ATTR_ALL); //UDMA_ATTR_ALTSELECT | UDMA_ATTR_USEBURST | UDMA_ATTR_HIGH_PRIORITY | UDMA_ATTR_REQMASK); ROM_uDMAChannelAttributeEnable(UDMA_CH11_SSI0TX, UDMA_ATTR_USEBURST); ROM_uDMAChannelControlSet(UDMA_CH11_SSI0TX | UDMA_PRI_SELECT, UDMA_SIZE_8 | UDMA_DST_INC_NONE | UDMA_SRC_INC_8 | UDMA_ARB_4); ROM_SSIIntEnable(SSI0_BASE, SSI_DMARX); ROM_IntEnable(INT_SSI0);
..and here's the function that runs a transfer:
void bsp_ssimem(uint16_t n, uint8_t *buf)
{
uint16_t c, r;
uint32_t a;
r = n;
while(r) {
// DMA can only do 1024 bytes in one go, so chunk it up:
if(r > 1024)
c = 1024;
else
c = r;
ROM_SSIDMADisable(SSI0_BASE, SSI_DMA_RX | SSI_DMA_TX); // Disable while reconfiguring
EVENT_CLEAR(bsp_events, EV_SSI); // make sure flag is down
HWREG(SSI0_BASE + SSI_O_CR1) &= ~SSI_CR1_EOT; // See errata
while(ROM_SSIDataGetNonBlocking(SSI0_BASE, &a)) {} // Flush FIFO
// Set up DMA to send and receive
ROM_uDMAChannelTransferSet(UDMA_CH11_SSI0TX | UDMA_PRI_SELECT, UDMA_MODE_BASIC, buf, (void *)(SSI0_BASE + SSI_O_DR), c);
ROM_uDMAChannelTransferSet(UDMA_CH10_SSI0RX | UDMA_PRI_SELECT, UDMA_MODE_BASIC, (void *)(SSI0_BASE + SSI_O_DR), buf, c);
ROM_uDMAChannelEnable(UDMA_CH10_SSI0RX);
ROM_uDMAChannelEnable(UDMA_CH11_SSI0TX);
ROM_SSIDMAEnable(SSI0_BASE, SSI_DMA_RX | SSI_DMA_TX); // GO!
if(EVENT_WAIT_TIMEOUT(bsp_events, EV_SSI, 5) == 0) // Wait for interrupt, but no more than 50ms
DMAErrCount++;
r -= c;
buf += c;
}
}
Finally here's the ISR.
void SSI0_IRQHandler(void) // ext flash/ram
{
uint32_t f;
ROM_SSIDMADisable(SSI0_BASE, SSI_DMA_RX | SSI_DMA_TX);
f = ROM_SSIIntStatus(SSI0_BASE, false);
ROM_SSIIntClear(SSI0_BASE, f); // Clear any interrupts
EVENT_SET(bsp_events, EV_SSI);
}
This works 99.9% of the time, but randomly the interrupt is not triggered, and it's more frequent for shorter transfers. When I slow down the SPI clock to 1MHz, it works better, but I still have single byte transfers failing occasionally.
I would very much appreciate any ideas or recommendations.
Thanks,
// Anders