What is and how to configure the eDMA scatter/gather feature

cancel
Showing results for 
Search instead for 
Did you mean: 

What is and how to configure the eDMA scatter/gather feature

No ratings

What is and how to configure the eDMA scatter/gather feature

Hello Kinetis fans,

This time I bring to you a document which explains what is and how to configure scatter/gather feature which is present in the Enhanced Direct Memory Access (eDMA). This document includes an example project for the Kinetis Design Studio (KDS) which works in the FRDM-K64F board but the configuration is the same for any MCU which includes the eDMA peripheral.

If you are interested in the channel linking feature, please take a look into the document What is and how to configure the eDMA channel linking feature​.

pastedImage_1.png

I hope you find this document useful.

Best regards,

Earl Orlando Ramírez-Sánchez

Technical Support Engineer

NXP Semiconductors

Labels (2)
Attachments
Comments

Excellent document earlorlandoramirez-sanchez​. Perfect timing too as I'm trying to understand how to link two DMA channels together.

I'm using the TWR-K65 EDMA example and the following FSDK call but it is not working as expected. Correct me if I am wrong but this should configure the "primary" channel's software TCD with the channel of the "secondary" channel to trigger automatically when a major loop completes.

        EDMA_DRV_PrepareDescriptorChannelLink(stcd, channel2);

The channel1 TCD is configured for a memory to memory SGL transfer:

        EDMA_DRV_ConfigScatterGatherTransfer(

                &chnState, stcd, kEDMAMemoryToMemory,

                EDMA_TRANSFER_SIZE, EDMA_WARTERMARK_LEVEL,

                &srcSG, &destSG,

                EDMA_CHAIN_LENGTH);

The channel2 TCD is configured in a similar way except its source buffer is the destination buffer of channel 1:

        EDMA_DRV_ConfigScatterGatherTransfer(

                  &chnState2, stcd2, kEDMAMemoryToMemory,

                  EDMA_TRANSFER_SIZE, EDMA_WARTERMARK_LEVEL,

                  &srcSG2, &destSG2,

                  EDMA_CHAIN_LENGTH);

Here is the modified main.c for reference:

/*

* Copyright (c) 2013 - 2014, Freescale Semiconductor, Inc.

* All rights reserved.

*

* Redistribution and use in source and binary forms, with or without modification,

* are permitted provided that the following conditions are met:

*

* o Redistributions of source code must retain the above copyright notice, this list

*   of conditions and the following disclaimer.

*

* o Redistributions in binary form must reproduce the above copyright notice, this

*   list of conditions and the following disclaimer in the documentation and/or

*   other materials provided with the distribution.

*

* o Neither the name of Freescale Semiconductor, Inc. nor the names of its

*   contributors may be used to endorse or promote products derived from this

*   software without specific prior written permission.

*

* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND

* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED

* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE

* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR

* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES

* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;

* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON

* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT

* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS

* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

*/

///////////////////////////////////////////////////////////////////////////////

//  Includes

///////////////////////////////////////////////////////////////////////////////

// Standard C Included Files

#include <stdio.h>

// SDK Included Files

#include "board.h"

#include "fsl_edma_driver.h"

#include "fsl_os_abstraction.h"

#include "fsl_debug_console.h"

///////////////////////////////////////////////////////////////////////////////

// Definitions

///////////////////////////////////////////////////////////////////////////////

#define BUFFER_SIZE               16/*! Total transfer size */

#define EDMA_TRANSFER_SIZE        2 /*! Transfer size on basic loop */

#define EDMA_CHAIN_LENGTH         1 /*! Number of srcSG and destSG */

#define EDMA_WARTERMARK_LEVEL     8 /*! number of bytes transfered on each EDMA request(minor loop) */

///////////////////////////////////////////////////////////////////////////////

// Variables

///////////////////////////////////////////////////////////////////////////////

// Source address in flash.

uint8_t srcAddr[BUFFER_SIZE] = {0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15};

// Destination address in ram.

uint8_t destAddr[BUFFER_SIZE] = {0};

uint8_t destAddr2[BUFFER_SIZE] = {0};

semaphore_t sema;

////////////////////////////////////////////////////////////////////////////////

// Code

////////////////////////////////////////////////////////////////////////////////

/*!

* @brief EDMA callback

*/

void EDMA_Callback(void *param, edma_chn_status_t chanStatus)

{

    OSA_SemaPost(&sema);

}

void EDMA_Callback2(void *param, edma_chn_status_t chanStatus)

{

    PRINTF("\n\r Completed EDMA transfer on channel 2\r\n");

}

/*!

* @brief EDMA transfers form memory to memory.

*

* This function use EDMA peripheral to transfer two buffers

*/

int main(void)

{

    edma_chn_state_t     chnState, chnState2;

    edma_software_tcd_t *stcd, *stcd2;

    edma_state_t         edmaState;

    edma_user_config_t   edmaUserConfig;

    edma_scatter_gather_list_t srcSG, srcSG2, destSG, destSG2;

    osa_status_t         syncStatus;

    bool                 result;

    uint32_t             i, channel = 0;

    uint32_t             channel2 = 1;

    // Init hardware.

    hardware_init();

    // Init OSA layer.

    OSA_Init();

    PRINTF("\r\n EDMA transfer from memory to memory \r\n");

    // Init eDMA modules.

    edmaUserConfig.chnArbitration = kEDMAChnArbitrationRoundrobin;

    edmaUserConfig.notHaltOnError = false;

    EDMA_DRV_Init(&edmaState, &edmaUserConfig);

    // Create semaphore to synchronize edma transaction

    OSA_SemaCreate(&sema, 0);

    while (1)

    {

        // EDMA channel request.

        if (EDMA_DRV_RequestChannel(channel, kDmaRequestMux0AlwaysOn62, &chnState) == kEDMAInvalidChannel)

            PRINTF("EDMA_DRV_RequestChannel for channel 1 failed\n");

        if (EDMA_DRV_RequestChannel(channel2, kDmaRequestMux0AlwaysOn62, &chnState2) == kEDMAInvalidChannel)

            PRINTF("EDMA_DRV_RequestChannel for channel 2 failed\n");

        // Fill zero to destination buffer

        for (i = 0; i < BUFFER_SIZE; i ++)

        {

            destAddr[i] = 0x00;

            destAddr2[i] = 0x00;

        }

        // Prepare memory pointing to software TCDs.

        stcd = OSA_MemAllocZero(STCD_SIZE(EDMA_CHAIN_LENGTH));

        stcd2 = OSA_MemAllocZero(STCD_SIZE(EDMA_CHAIN_LENGTH));

        // Configure EDMA channel 1

        srcSG.address  = (uint32_t)srcAddr;

        destSG.address = (uint32_t)destAddr;

        srcSG.length   = BUFFER_SIZE;

        destSG.length  = BUFFER_SIZE;

        // Configure EDMA channel 2

        srcSG2.address  = (uint32_t)destAddr;

        destSG2.address = (uint32_t)destAddr2;

        srcSG2.length  = BUFFER_SIZE;

        destSG2.length  = BUFFER_SIZE;

        // configure single end descriptor chain.

        EDMA_DRV_ConfigScatterGatherTransfer(

                &chnState, stcd, kEDMAMemoryToMemory,

                EDMA_TRANSFER_SIZE, EDMA_WARTERMARK_LEVEL,

                &srcSG, &destSG,

                EDMA_CHAIN_LENGTH);

        EDMA_DRV_ConfigScatterGatherTransfer(

  &chnState2, stcd2, kEDMAMemoryToMemory,

  EDMA_TRANSFER_SIZE, EDMA_WARTERMARK_LEVEL,

  &srcSG2, &destSG2,

  EDMA_CHAIN_LENGTH);

        // Install callback for eDMA handler

        EDMA_DRV_InstallCallback(&chnState, EDMA_Callback, NULL);

        EDMA_DRV_InstallCallback(&chnState2, EDMA_Callback2, NULL);

        // configure channel 1 to start transfer on channel 2 when complete

        if (EDMA_DRV_PrepareDescriptorChannelLink(stcd, channel2) != kStatus_EDMA_Success)

            PRINTF("\r\n EDMA_DRV_PrepareDescriptorChannelLink failed\r\n");

        PRINTF("\r\n Starting EDMA channel No. %d to transfer data from addr 0x%x to addr 0x%x",  \

                                                                                        channel,  \

                                                                                (uint32_t)srcAddr,\

                                                                                (uint32_t)destAddr);

        PRINTF("\r\n Configured EDMA channel %d to transfer when channel %d is complete", channel2, channel);

        // Initialize transfer.

        EDMA_DRV_StartChannel(&chnState);

        // Wait until transfer is complete

        do

        {

            syncStatus = OSA_SemaWait(&sema, OSA_WAIT_FOREVER);

        }while(syncStatus == kStatus_OSA_Idle);

        // Verify destAddr buff

        result = true;

        for (i = 0; i < BUFFER_SIZE; i ++)

        {

            if (destAddr[i] != srcAddr[i])

            {

            PRINTF("\r\n Transfered with eDMA channel %d: fail", channel);

                result = false;

                break;

            }

        }

        for (i = 0; i < BUFFER_SIZE; i ++)

        {

        if (destAddr2[i] != srcAddr[i])

        {

                PRINTF("\r\n Transfered with eDMA channel %d: fail", channel2);

        result = false;

        break;

        }

        }

        if (result == true)

            PRINTF("\r\n Transfers with eDMA: pass\r\n");

        else

            PRINTF("\r\n Transfers with eDMA: fail\r\n");

        // Stop channel

        EDMA_DRV_StopChannel(&chnState);

        EDMA_DRV_StopChannel(&chnState2);

        // Release channel

        EDMA_DRV_ReleaseChannel(&chnState);

        EDMA_DRV_ReleaseChannel(&chnState2);

        // Free stcd

        OSA_MemFree((void *)stcd);

    }

}

///////////////////////////////////////////////////////////////////////////////

// EOF

///////////////////////////////////////////////////////////////////////////////

Hello Lucas,

Thanks for your comments, I am glad you like my document :smileyhappy:.

First of all, does your application really need the scatter/gather feature? I just want to clarify that the scatter/gather and the channel linking are two different features. I suspect that you only need to link the channels, I just created a document that explains it in detail.

What is and how to configure the eDMA channel linking feature

In resume, the channel linking feature is a mechanism where one channel initiates a service request to another channel (i.e. the bit TCDn_CSR[START] is set) when the first channel has terminated its minor or its major loop, depending on the configuration; while the scatter/gather feature loads a new TCD when the first one has finished its execution.

Since the eDMA can only make one transference at the same time, you can't use an always enabled trigger source because you will be making two transactions (in two different channels) at the same time, this will result in an unpredictable behavior.

Also, a coherency model is needed to avoid that both channels try to make transactions at the same time by loading a new TCD through the scatter/gather feature and requesting a transference for the other channel through the channel linking feature.

Best regards,

Earl Ramírez.

Version history
Revision #:
1 of 1
Last update:
‎12-28-2015 01:35 PM
Updated by: