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 mixed peripheral and memory scatter-gather

Part Number: DK-TM4C129X

I want to setup a scatter gather operation that includes both peripheral and memory scatter-gather on an ADC DMA channel

  1. transfer data from ADC FIFO to buffer 1 (peripheral)
  2. transfer 0x1 from memory to another memory location to act as a buffer 1 done flag (memory)
  3. transfer data from ADC FIFO to buffer 2 (peripheral)
  4. transfer 0x1 from memory to another memory location to act as a buffer 2 done flag (memory)
  5. transfer scatter-gather task 1 into primary channel structure to continue looping without any interrupts (memory)

If I remove items 2 and 4, the scatter-gather looping works. When items 2 and 4 are included, buffer 1 is filled with ADC data and buffer 1 done flag is set from 0 to 1 but, nothing else happens.

Task list:


// DMA Task used to reload DMA task list tDMAControlTable dmaADCReload; // DMA Task list to transfer tDMAControlTable dmaADCTasks[4] = { uDMATaskStructEntry(512, UDMA_SIZE_32, UDMA_SRC_INC_NONE, (void *)(ADC1_BASE + ADC_O_SSFIFO0), UDMA_DST_INC_32, &u32_adc_results_buf_a[0], UDMA_ARB_4, UDMA_MODE_PER_SCATTER_GATHER), uDMATaskStructEntry(1, UDMA_SIZE_32, UDMA_SRC_INC_NONE, &u32_true_flag, UDMA_DST_INC_NONE, &u32_chk_adc_buf_a, UDMA_ARB_4, UDMA_MODE_MEM_SCATTER_GATHER), uDMATaskStructEntry(512, UDMA_SIZE_32, UDMA_SRC_INC_NONE, (void *)(ADC1_BASE + ADC_O_SSFIFO0), UDMA_DST_INC_32, &u32_adc_results_buf_b[0], UDMA_ARB_4, UDMA_MODE_PER_SCATTER_GATHER),
    uDMATaskStructEntry(1, UDMA_SIZE_32,
    UDMA_SRC_INC_NONE, &u32_true_flag,
    UDMA_DST_INC_NONE, &u32_chk_adc_buf_b,
    UDMA_ARB_1, UDMA_MODE_MEM_SCATTER_GATHER),
    uDMATaskStructEntry(4, UDMA_SIZE_32, 
UDMA_SRC_INC_32, &dmaADCReload,
UDMA_DST_INC_32, 0,
UDMA_ARB_4, UDMA_MODE_MEM_SCATTER_GATHER)
};

Initialization:

    // ADC 1 Sequence 0 uDMA Configuration
    MAP_uDMAChannelAssign(UDMA_CH24_ADC1_0); // Assign channel 24 to ADC1 SS0
    MAP_uDMAChannelAttributeEnable(UDMA_SEC_CHANNEL_ADC10, UDMA_ATTR_USEBURST);

    /*
    * Configures primary control structure in memory scatter-gather mode
    */
    MAP_uDMAChannelScatterGatherSet(UDMA_SEC_CHANNEL_ADC10, 4, &dmaADCTasks, true);

    // Copy primary control structure to reload task structure
    dmaADCReload.pvSrcEndAddr = ((tDMAControlTable *)_pu8_dma_control_table)[UDMA_SEC_CHANNEL_ADC10].pvSrcEndAddr;
    dmaADCReload.pvDstEndAddr = ((tDMAControlTable *)_pu8_dma_control_table)[UDMA_SEC_CHANNEL_ADC10].pvDstEndAddr;
    dmaADCReload.ui32Control = ((tDMAControlTable *)_pu8_dma_control_table)[UDMA_SEC_CHANNEL_ADC10].ui32Control;
    dmaADCReload.ui32Spare = ((tDMAControlTable *)_pu8_dma_control_table)[UDMA_SEC_CHANNEL_ADC10].ui32Spare;

    // Set destination address of looping task
    dmaADCTasks[2].pvDstEndAddr = _pu8_dma_control_table + (sizeof(tDMAControlTable) * (UDMA_SEC_CHANNEL_ADC10 + 1)) - 1;

    // Enable sequence 0
    MAP_uDMAChannelEnable(UDMA_SEC_CHANNEL_ADC10);

 

  • Hi Patrick,

      I hope this udma scatter-gather example will get you started. udma_scatter_gather.zip

  • Hi Charles,

    Thanks for the example. I actually continued to think through how to configure scatter-gather for my requirements and came up with something that is working. I'll include the code for reference for anyone else who needs it. I would say this implementation is ping-pong without any processor intervention to reset the DMA channels.

    To summarize what the code is doing - Two scatter-gather task lists are configured for buffer A and buffer B; the buffer A task list starts with a peripheral scatter-gather to do the FIFO to buffer A memory transfer, the second task sets a buffer A done flag and the third task transfers the first task of buffer B task list into the primary control structure. The buffer B task list is the similar to the buffer A task list with a few changes; the peripheral scatter-gather transfers to buffer B, the second task set a buffer B done flag and the third task transfers the first task of the buffer A task list into the primary control structure.

    First, declare two DMA reload vars and define two task lists, one for each buffer.

    // DMA Task used to reload DMA task list
    tDMAControlTable dmaADCReloadA;
    tDMAControlTable dmaADCReloadB;
    
    // DMA scatter-gather task list for buffer A
    tDMAControlTable dmaADCTasksA[3] =
    {
        uDMATaskStructEntry(512, UDMA_SIZE_32,
        UDMA_SRC_INC_NONE, (void *)(ADC1_BASE + ADC_O_SSFIFO0),
        UDMA_DST_INC_32, &u32_adc_results_buf_a[0],
        UDMA_ARB_4, UDMA_MODE_PER_SCATTER_GATHER),
    
        uDMATaskStructEntry(1, UDMA_SIZE_32,
        UDMA_SRC_INC_NONE, &u32_true_flag,
        UDMA_DST_INC_NONE, &u32_chk_adc_buf_a,
        UDMA_ARB_1, UDMA_MODE_MEM_SCATTER_GATHER),
    
        uDMATaskStructEntry(4, UDMA_SIZE_32,
        UDMA_SRC_INC_32, &dmaADCReloadB,
        UDMA_DST_INC_32, 0,
        UDMA_ARB_4, UDMA_MODE_MEM_SCATTER_GATHER)
    };
    
    // DMA scatter-gather task list for buffer B
    tDMAControlTable dmaADCTasksB[3] =
    {
        uDMATaskStructEntry(512, UDMA_SIZE_32,
        UDMA_SRC_INC_NONE, (void *)(ADC1_BASE + ADC_O_SSFIFO0),
        UDMA_DST_INC_32, &u32_adc_results_buf_b[0],
        UDMA_ARB_4, UDMA_MODE_PER_SCATTER_GATHER),
    
        uDMATaskStructEntry(1, UDMA_SIZE_32,
        UDMA_SRC_INC_NONE, &u32_true_flag,
        UDMA_DST_INC_NONE, &u32_chk_adc_buf_b,
        UDMA_ARB_1, UDMA_MODE_MEM_SCATTER_GATHER),
    
        uDMATaskStructEntry(4, UDMA_SIZE_32,
        UDMA_SRC_INC_32, &dmaADCReloadA,
        UDMA_DST_INC_32, 0,
        UDMA_ARB_4, UDMA_MODE_MEM_SCATTER_GATHER)
    };

    DMA initialization

        // ADC 1 Sequence 0 uDMA Configuration
        MAP_uDMAChannelAssign(UDMA_CH24_ADC1_0); // Assign channel 24 to ADC1 SS0
        MAP_uDMAChannelAttributeEnable(UDMA_SEC_CHANNEL_ADC10, UDMA_ATTR_USEBURST);
    
        dmaADCTasksA[2].pvDstEndAddr = _pu8_dma_control_table + (sizeof(tDMAControlTable) * (UDMA_SEC_CHANNEL_ADC10 + 1)) - 1;
        dmaADCTasksB[2].pvDstEndAddr = _pu8_dma_control_table + (sizeof(tDMAControlTable) * (UDMA_SEC_CHANNEL_ADC10 + 1)) - 1;
    
        // Setup scatter gather for buffer B task list
        MAP_uDMAChannelScatterGatherSet(UDMA_SEC_CHANNEL_ADC10, 3, &dmaADCTasksB, true);
    
        // Copy primary control structure to reload B task structure
        dmaADCReloadB.pvSrcEndAddr = ((tDMAControlTable *)_pu8_dma_control_table)[UDMA_SEC_CHANNEL_ADC10].pvSrcEndAddr;
        dmaADCReloadB.pvDstEndAddr = ((tDMAControlTable *)_pu8_dma_control_table)[UDMA_SEC_CHANNEL_ADC10].pvDstEndAddr;
        dmaADCReloadB.ui32Control = ((tDMAControlTable *)_pu8_dma_control_table)[UDMA_SEC_CHANNEL_ADC10].ui32Control;
        dmaADCReloadB.ui32Spare = ((tDMAControlTable *)_pu8_dma_control_table)[UDMA_SEC_CHANNEL_ADC10].ui32Spare;
    
        // Setup scatter gather for buffer A task list
        MAP_uDMAChannelScatterGatherSet(UDMA_SEC_CHANNEL_ADC10, 3, &dmaADCTasksA, true);
    
        // Copy primary control structure to reload A task structure
        dmaADCReloadA.pvSrcEndAddr = ((tDMAControlTable *)_pu8_dma_control_table)[UDMA_SEC_CHANNEL_ADC10].pvSrcEndAddr;
        dmaADCReloadA.pvDstEndAddr = ((tDMAControlTable *)_pu8_dma_control_table)[UDMA_SEC_CHANNEL_ADC10].pvDstEndAddr;
        dmaADCReloadA.ui32Control = ((tDMAControlTable *)_pu8_dma_control_table)[UDMA_SEC_CHANNEL_ADC10].ui32Control;
        dmaADCReloadA.ui32Spare = ((tDMAControlTable *)_pu8_dma_control_table)[UDMA_SEC_CHANNEL_ADC10].ui32Spare;
    
        // Enable sequence 0
        MAP_uDMAChannelEnable(UDMA_SEC_CHANNEL_ADC10);