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.

DK-TM4C129X: µDMA SG double write bug workaround

Part Number: DK-TM4C129X

This issue is to document what appears to be a bug in the scatter-gather ,of the TM4C129X, along with a workaround for that bug.  No help is requested.

The SG code below loops on two transfers, each of which

1) reads a block of SSI data from the FIFO into memory,
2) reads the EMAC timestamp registers into memory, and
3) generates an interrupt by unmasking the SSI EOT interrupt.

In the interest of saving time, there is no attempt here to describe the supporting software -- it is hoped that the code is fairly self-explanatory. There is a looped
µDMA task, not shown here, which does four 14-bit SSI output transfers to cause the SSI input transfers on the rising edge of an external GPIO pin.

The overall transfer flow works as designed, except that if the "dummy" task, shown below in red, is not included, the sixth 16-bit transfer of each SSI input task
which is preceded by the timestamp copy and interrupt request, happens twice, inserting an erroneous halfword in the SSI input stream. The transfer doubling
doesn't happen for the first transfer, at startup time. Not knowing the logic structure, I can't state what is happening internal to the MCU, but it looks like
a timing conflict between write buffering and the succeeding SSI input transfer.

The scatter-gather mechanism is an excellent idea and saves significant hardware in the design involved. It is much appreciated. The dummy task
workaround seems to completely fix the problem, allowing the MCU to run overnight without lockup or apparent data corruption. No action is requested of TI
on this issue. TI might, however, consider a fuller suite of examples and documentation to permit the average user to access the full power of SG DMA.

Data structures:


static const uint32_t packQueueDepth = 2; struct { uint32_t b[ni::ssiPackInLength / sizeof(uint32_t)]; //! Packable block of ADC data uint32_t emactimsec; //! EMAC register contents uint32_t emactimnano; //! EMAC register contents } adcBlockUnpacked[packQueueDepth]; //! ADC input data from hardware //! Input uDMA task list struct { struct { tDMAControlTable dummy; //! Following intReq, adcReceive can experience duplication of writes //! without this dummy task present. tDMAControlTable adcReceive; //! Input ADC data to memory. tDMAControlTable timeStampCopy; //! Copy timestamp to memory. tDMAControlTable intReq; //! Request interrupt to processor. } packQueue[packQueueDepth]; tDMAControlTable loop; //! Loop primary entry to repeat. } coilInUdmaTask; //! Coil input primary uDMA entry pointing to task list const tDMAControlTable coilInputUdmaPrimary = { ((char *)&(coilInUdmaTask))+sizeof(coilInUdmaTask)-1, &(uDmaCcs[ni::udmaAlternate][ni::adcRxUdmaChannel].ui32Spare), UDMA_CHCTL_DSTINC_32 | UDMA_CHCTL_DSTSIZE_32 | UDMA_CHCTL_SRCINC_32 | UDMA_CHCTL_SRCSIZE_32 | UDMA_CHCTL_ARBSIZE_4 | ((sizeof(coilInUdmaTask)/sizeof(uint32_t)-1) << UDMA_CHCTL_XFERSIZE_S) | UDMA_CHCTL_XFERMODE_PER_SG, 0 }; uint32_t coilInputUdmaPrimaryInit; //! Udma source for coil input uDMA copy
Initialization code:
// Point primary ADC input uDMA to task list. uDmaCcs[ni::udmaPrimary][ni::adcRxUdmaChannel] = coilInputUdmaPrimary; // Save off reinitialization value for loop. coilInputUdmaPrimaryInit = coilInputUdmaPrimary.ui32Control; // For each block in pack queue for (int b=0; b<packQueueDepth; ++b) { // Dummy task prevents duplication of samples when adcReceive // immediately follows timeStampCopy and intReq. uDMATaskAssign( coilInUdmaTask.packQueue[b].dummy, 8, UDMA_SIZE_32, // Read/write using unused part of uDMA control structure UDMA_SRC_INC_NONE, (void *)&(coilInUdmaTask.packQueue[b].dummy.ui32Spare), UDMA_DST_INC_NONE, (void *)&(coilInUdmaTask.packQueue[b].dummy.ui32Spare), // Read/write which doesn't touch RAM and doesn't affect registers //UDMA_SRC_INC_NONE, (void *)(ni::adcSsi+SSI_O_RIS), //UDMA_DST_INC_NONE, (void *)(ni::adcSsi+SSI_O_RIS), UDMA_ARB_8, UDMA_MODE_PER_SCATTER_GATHER ); // Block of ADC samples uDMATaskAssign( coilInUdmaTask.packQueue[b].adcReceive, ni::ssiPackInLength/sizeof(uint16_t), UDMA_SIZE_16, UDMA_SRC_INC_NONE, (void *)(ni::adcSsi + SSI_O_DR), UDMA_DST_INC_16, adcBlockUnpacked[b].b, UDMA_ARB_1, UDMA_MODE_PER_SCATTER_GATHER ); // Timestamp uDMATaskAssign( coilInUdmaTask.packQueue[b].timeStampCopy, 2, UDMA_SIZE_32, UDMA_SRC_INC_32, (void *)(EMAC0_BASE + EMAC_O_TIMSEC), UDMA_DST_INC_32, &(adcBlockUnpacked[b].emactimsec), UDMA_ARB_1, UDMA_MODE_MEM_SCATTER_GATHER ); // Interrupt uDMATaskAssign( coilInUdmaTask.packQueue[b].intReq, 1, UDMA_SIZE_32, UDMA_SRC_INC_NONE, &ssiImEotim, UDMA_DST_INC_NONE, (void *)(ni::adcSsi + SSI_O_IM), UDMA_ARB_1, UDMA_MODE_MEM_SCATTER_GATHER ); } // Loop back to beginning of transfer. uDMATaskAssign( coilInUdmaTask.loop, 1, UDMA_SIZE_32, UDMA_SRC_INC_32, &coilInputUdmaPrimaryInit, UDMA_DST_INC_32, (void *)&(uDmaCcs[ni::udmaPrimary][ni::adcRxUdmaChannel].ui32Control), UDMA_ARB_4, UDMA_MODE_MEM_SCATTER_GATHER );