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.
I hope you find this document useful.
Best regards,
Earl Orlando Ramírez-Sánchez
Technical Support Engineer
NXP Semiconductors
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.