EDMA的全称是:Enhanced Direct Memory Access(增强型dma),增强型直接内存存取(EDMA)是数字信号处理器(DSP)中用于快速数据交换的重要技术,具有独立于CPU的后台批量数据传输的能力,能够满足实时图像处理中高速数据传输的要求。以TI公司的TMS320C6713型DSP为例,介绍EDMA控制器的特点。结合实例给出EDMA在数据实时传输中的具体控制和实现方法。实验结果表明,通过灵活控制EDMA不仅能够提高数据的传输效率,而且能够充分发挥:DSP的高速性能。也就是在我们一般的dma的基础上做了一下加工以完成特定的功能。比如我们dma传送数据一般是连续的一片数据块,但是我们想间隔着传送就不行了。还有就是增强型的dma可以实现二维的传送和一个事件可以发起两次传送,这样就可以很好的服务我们的数据传送要求!
如图EDMA结构图:
可以看到我们配置自己的edma之后,就等待传送事件的到来,当传送事件到来的时候edma的控制器就接受到一个触发信号,就进入传送过程。传送的规则定义在edma的每一个通道的参数设置里面,我们可以通过控制传送的参数来达到我们要传送的数据的控制的目的!传送参数表如图:
第一个双字就是控制edma传送的规则,比如:一维单元同步,一维帧同步,二维等等。这些控制属性得在这个域里面对其进行说明。第二个双字就是告诉edma要传送数据的源地址,第三个双字的帧的数目和单元的数目,第四个双字就是告诉edma要传送数据的目的地址。第五个双字告诉edma下一次触发时地址的偏移量的量。第六个双字就是当前的单元传送完成之后的单元数目的重载值和是否edma是否需要要链接到下一个edma(注意:这儿是通道链接)我们先来理解几个概念:
事件链接:实质就是想一个事件触发之后引起两次edma的搬移,当然这两次搬移不是同一个edma通道,如果是同一个edma通道,相当于同样的数据搬移两次,这样不仅没有意义,还会占用内部总线带宽,印象片内数据的流动的速度。那么TI设计的是将一个事件先链接到第一个通道,然后将第一个通道的完成中断事件链接到第二个edma通道。相当于当一个事件触发之后,就触发第一个edma通道搬移数据,当第一个通道完成之后就发出一个事件触发第二个通道搬移数据,最后可以产生一个中断通知CPU。这样一般用视频的FIFO同步。注:大家可能会用疑问,不是是要同时搬移两块吗,怎么不是同时的呢?大家可以想想DSP内部一般就只有一条数据总线供edma使用。那么在同一时刻只用一个数占用总线,如果我们有两个设备驱动总线数据,那么数据就不对了。所以edma 事件同步就是先让第一个通道搬移完成触发第二个通道搬移。
通道链接:实质就是由于edma搬移的数据块就只有那么大,那么当想搬移很多很多的时候怎么办呢?当然可以在完成中断中重新设置edma参数,这样当事件到来时,就可以重新开始搬移了。其实也可以用edma的通道链接这个属性,当这个通道的参数设置的完成时,通过链接到自己或者别人这样就不用重新设置来达到事件到来时搬移数据的目的。通道链接的时候一般下一个通道的事件触发器是同一个,这样就不会丢失事件。这种属性一般应用于大量数据从一个固定地址到另一个固定地址!还要就是一个提高处理速度的乒乓结构的数据的搬移。
乒乓结构:在做一些设计的时候,一般要考虑两种因素:速度和面积。速度当然是指我们要求的处理数据的速度,面积就是存储数据的要用的内存大小(成本问题)。在这两者之间权衡我们的算法。在这儿讲的乒乓结构就是用面积换取速度的一种做法!我们用两块内存和两个edma通道(相互链接,同一个事件触发),第一块取名为乒,第二块取名为乓。当一个事件到来的时候触发我们的乒通道搬移数据到乒的内存块,等待一系列事件完之后,乒通道完成就连接到乓通道。这时就是产生一个中断告诉cpu第一个块数据已经准备好在乒内存块了,让其进行数据算法处理。与此同时乓通道也在搬移数据到乓内存块,完成之后通知cpu,链接到乒通道······这样形成一个循环。就好像生活中打乒乓一样!如图:
四种数据搬移方式:
一维单元同步搬移(1D-to-1D):
设置一个edma通道为一维数据单元同步搬移时,当一个事件到来触发时一次edma搬移数据的大小就是一个单元。那么edma数据块搬移完成之后总体大小为:ESIZE*FRMCNT*ELERLD,当下一个事件到来时,下一个源地址和目的地址的偏移由opt中sum,dum控制。例如:ESIZE=00b,SUM=01b,DUM=01b,FRMCNT=02h,ELERLD=04h,这样搬移的数据总量为2*4=8字节。
一维帧同步搬移(1D-to-1D):
设置一个edma通道为一维数据帧同步搬移时,当一个事件到来触发时一次edma 搬移数据的大小就是一帧,也就是一帧中所有单元数据总和。那么edma数据块搬移完成之后总体大小为:ESIZE*FRMCNT*ELERLD,当下一个事件到来时,下一个源地址和目的地址的偏移由opt中sum,dum控制。例如:ESIZE=00b,SUM=01b,DUM=01b,FRMCNT=02h,ELERLD=04h,这样搬移的数据总量为2*4=8字节。
二维数组同步搬移(2D-to-2D):
设置一个edma通道为一维数据帧同步搬移时,当一个事件到来触发时一次edma 搬移数据的大小就是一个数组,也就是一帧中所有单元数据总和。那么edma数据块搬移完成之后总体大小为:ESIZE*FRMCNT*ELERLD,当下一个事件到来时,下一个源地址和目的地址的偏移由opt中sum,dum控制。例如:ESIZE=00b,SUM=01b,DUM=01b,FRMCNT=02h,ELECNT=04h,这样搬移的数据总量为2*4=8字节。
二维数组同步搬移(2D-to-2D):
设置一个edma通道为一维数据帧同步搬移时,当一个事件到来触发时一次edma 搬移数据的大小就是一个整个数据块,也就是一个edma通道中所有数据总和。那么edma数据块搬移完成之后总体大小为:ESIZE*FRMCNT*ELERLD,当下一个事件到来时,下一个源地址和目的地址的偏移由opt中sum,dum控制。例如:ESIZE=00b,SUM=01b,DUM=01b,FRMCNT=02h,ELECNT=04h,这样搬移的数据总量为2*4=8字节。
实例源代码,使用timer1作为事件触发源,通过1D-1D单元同步来实现乒乓结构:
/*
* Copyright 2003 by Texas Instruments Incorporated.
* All rights reserved. Property of Texas Instruments Incorporated.
* Restricted rights to use, duplicate or disclose this code are
* granted through contract.
*
*/
/*
*———main_edma1.c———
* This program uses the timers to trigger EDMA events. These events in turn * trigger linked EDMA parameter tables to fill a ping pong buffer structure.
* Set a breakpoint on ?processbuff‘ function. Then open two memory windows. * Use ping as the address for one memory window and pong for the other. Then
* run the application. You‘ll note that the program bounces between the ping * and pong buffers filling each with a value that comes from the source.
* (Note: This example runs with CACHE enable).
*/
#include
#include
#include
#include
#include
#include
/*—————————————————————————-*/
/* Pick which EDMA transfer completion interrupt we want to use */
#define TCCINTNUM 10
/* define the constants */
#define BUFF_SZ 256 /* ping-pong buffer sizes in # of ints */
#define FCPU 150000000 /* CPU clock frequency */
#define SRATE 8000 /* data sample rate (simulated w/timer */ #define TPRD (FCPU/(4*SRATE)) /* timer period */
#define TRANSFER_CNT 20 /* Transfer count */
/* Create the buffers. We want to align the buffers to be cache friendly */
/* by aligning them on an L2 cache line boundary. */
#pragma DATA_SECTION (ping, ―.buffers‖);
#pragma DATA_SECTION (pong, ―.buffers‖);
#pragma DATA_SECTION (outbuff, ―.buffers‖);
#pragma DATA_ALIGN(ping,128);
#pragma DATA_ALIGN(pong,128);
#pragma DATA_ALIGN(outbuff,128);
int ping[BUFF_SZ];
int pong[BUFF_SZ];
int outbuff[BUFF_SZ];
/* These two variables serve as the data sources for this example. */
/* Also want to align these on a cache line boundary since they */
/* sources of EDMA transfers. */
#pragma DATA_SECTION (ping_data, ―.buffers‖);
#pragma DATA_SECTION (pong_data, ―.buffers‖);
#pragma DATA_ALIGN(ping_data,128);
#pragma DATA_ALIGN(pong_data,128);
static int ping_data;
static int pong_data;
/* global variable used to track the ping-pong‘ing */
static int pingpong = 0;
volatile int transferCount = 0;
extern far void vectors();
void setupInterrupts(void);
void stopEdma(void); /* function used to stop EDMA */
/*————————————————————————-*/
/* declare the CSL objects */
TIMER_Handle hTimer; /* Handle for the timer device */ EDMA_Handle hEdma; /* Handle for the EDMA channel */ EDMA_Handle hEdmaPing; /* Handle for the ping EDMA reload parameters */
EDMA_Handle hEdmaPong; /* Handle for the pong EDMA reload parameters */
EDMA_Config cfgEdma; /* EDMA configuration structure */
/* Create the EDMA configuration structure for ping transfers */
EDMA_Config cfgEdmaPing = {
EDMA_OPT_RMK( /* Making Options parameter register –EDMA_OPT */
EDMA_OPT_PRI_LOW, /* Priority levels for EDMA events:-
EDMA_OPT_PRI_LOW – Low priority EDMA transfer
EDMA_OPT_PRI_HIGH – High priority EDMA transfer*/
EDMA_OPT_ESIZE_32BIT,/* Element size :-
EDMA_OPT_ESIZE_32BIT – 32 bit word
EDMA_OPT_ESIZE_16BIT – 16 bit word
EDMA_OPT_ESIZE_8BIT - 8 bit word */
EDMA_OPT_2DS_NO, /* Source dimension :-
EDMA_OPT_2DS_NO – 1-dimensional source
EDMA_OPT_2DS_YES – 2-dimensional source */
EDMA_OPT_SUM_NONE, /* Source address update mode :-
EDMA_OPT_SUM_NONE – Fixed address mode
EDMA_OPT_SUM_INC – Increment address mode
EDMA_OPT_SUM_DEC – Decrement address mode
EDMA_OPT_SUM_IDX – Address modified by element
index or frame Index */
EDMA_OPT_2DD_NO, /* Destination dimension :-
EDMA_OPT_2DD_NO – 1-dimensional source
EDMA_OPT_2DD_YES – 2-dimensional source */
EDMA_OPT_DUM_INC, /* Destination address update
mode :-
EDMA_OPT_DUM_NONE – Fixed address mode
EDMA_OPT_DUM_INC – Increment address mode
EDMA_OPT_DUM_DEC – Decrement address mode
EDMA_OPT_DUM_IDX – Address modified by element
index or frame Index */
EDMA_OPT_TCINT_YES, /* Transfer complete interrupt :-
EDMA_OPT_TCINT_NO – Indication disabled
EDMA_OPT_TCINT_YES – Indication enabled */
EDMA_OPT_TCC_OF(TCCINTNUM),/* Transfer complete
code */
EDMA_OPT_LINK_YES, /* Linking of event parameters
EDMA_OPT_LINK_NO - Disabled
EDMA_OPT_LINK_YES - Enabled */
EDMA_OPT_FS_NO /* Frame synchronization
EDMA_OPT_FS_NO – Channel is element/array
synchronized
EDMA_OPT_FS_YES - Channel is frame synchronized*/ ),
EDMA_SRC_OF(&ping_data),/* Source address register
&ping_data – source address */
EDMA_CNT_OF(BUFF_SZ), /* Transfer count parameter
BUFF_SZ – buffer sizes in # of ints */
EDMA_DST_OF(ping), /* Destination address parameter
ping – destination address */
EDMA_IDX_OF(0×00000004),/* Index parameter */
EDMA_RLD_OF(0×00000000) /* Count reload/link
parameter */
};
/* Create the EDMA configuration structure for pong transfers */
EDMA_Config cfgEdmaPong = {
EDMA_OPT_RMK( /* Making Options parameter register –
EDMA_OPT */
EDMA_OPT_PRI_LOW, /* Priority levels for EDMA events:-
EDMA_OPT_PRI_LOW – Low priority EDMA transfer
EDMA_OPT_PRI_HIGH – High priority EDMA transfer */
EDMA_OPT_ESIZE_32BIT,/* Element size :-
EDMA_OPT_ESIZE_32BIT – 32 bit word
EDMA_OPT_ESIZE_16BIT – 16 bit word
EDMA_OPT_ESIZE_8BIT - 8 bit word */
EDMA_OPT_2DS_NO, /* Source dimension :-
EDMA_OPT_2DS_NO – 1-dimensional source
EDMA_OPT_2DS_YES – 2-dimensional source */ EDMA_OPT_SUM_NONE, /* Source address update mode :-
EDMA_OPT_SUM_NONE – Fixed address mode
EDMA_OPT_SUM_INC – Increment address mode
EDMA_OPT_SUM_DEC – Decrement address mode
EDMA_OPT_SUM_IDX – Address modified by element
index or frame Index */
EDMA_OPT_2DD_NO, /* Destination dimension :-
EDMA_OPT_2DD_NO – 1-dimensional source
EDMA_OPT_2DD_YES – 2-dimensional source */
EDMA_OPT_DUM_INC, /* Destination address update
mode :-
EDMA_OPT_DUM_NONE – Fixed address mode
EDMA_OPT_DUM_INC – Increment address mode
EDMA_OPT_DUM_DEC – Decrement address mode
EDMA_OPT_DUM_IDX – Address modified by element
index or frame Index */
EDMA_OPT_TCINT_YES, /* Transfer complete interrupt :-
EDMA_OPT_TCINT_NO – Indication disabled
EDMA_OPT_TCINT_YES – Indication enabled */ EDMA_OPT_TCC_OF(TCCINTNUM),/* Transfer complete
code */
EDMA_OPT_LINK_YES, /* Linking of event parameters
EDMA_OPT_LINK_NO - Disabled
EDMA_OPT_LINK_YES - Enabled */
EDMA_OPT_FS_NO /* Frame synchronization
EDMA_OPT_FS_NO – Channel is element/array
synchronized
EDMA_OPT_FS_YES - Channel is frame synchronized*/ ),
EDMA_SRC_OF(&pong_data),/* Source address register
&ping_data – source address */
EDMA_CNT_OF(BUFF_SZ), /* Transfer count parameter
BUFF_SZ – buffer sizes in # of ints */
EDMA_DST_OF(pong), /* Destination address parameter
ping – destination address */
EDMA_IDX_OF(0×00000004),/* Index parameter */
EDMA_RLD_OF(0×00000000) /* Count reload/link
parameter */
};
/*—————————————————————————-*/
void main(){
/* initialize the CSL library */
CSL_init();
/* Configure L2 for 64K Cache and enable caching of external memory*/ CACHE_setL2Mode(CACHE_64KCACHE);
CACHE_enableCaching(CACHE_CE00);
/* initialize the input source data */
ping_data=0×00000000;
pong_data=0×80000000;
/* Since these variables are the source of an EDMA transfer, we */
/* need to flush them out of the cache since we just wrote to them. */ CACHE_wbInvL2(&ping_data, 4, CACHE_WAIT);
CACHE_wbInvL2(&pong_data, 4, CACHE_WAIT);
/* Let‘s disable/clear related interrupts just in case they are pending */
/* from a previous run of the program. */ setupInterrupts(); /* defined below */
/* Although not required, let‘s clear all of the EDMA parameter RAM. */ /* This makes it easier to view the RAM and see the changes as we */ /* configure it. */
EDMA_clearPram(0×00000000);
/* Let‘s open up a timer device, we‘ll use this to simulate input events */ /* at a given sample rate. */
hTimer = TIMER_open(TIMER_DEV1, TIMER_OPEN_RESET);
/* Lets open up the EDMA channel associated with timer #1. */
hEdma = EDMA_open(EDMA_CHA_TINT1, EDMA_OPEN_RESET);
/* We also need two EDMA reload parameter sets so let‘s allocate them */ /* here. Notice the -1, this means allocate any availale table. */ hEdmaPing = EDMA_allocTable(-1);
hEdmaPong = EDMA_allocTable(-1);
/* Let‘s copy the ping reload configuration structure to an */
/* intermediate configuration structure. */
cfgEdma = cfgEdmaPing;
/* Let‘s initialize the link fields of the configuration structures */ cfgEdmaPing.rld = EDMA_RLD_RMK(0,hEdmaPing);
cfgEdmaPong.rld = EDMA_RLD_RMK(0,hEdmaPong);
cfgEdma.rld = EDMA_RLD_RMK(0,hEdmaPong);
/* Now let‘s program up the EDMA channel with the configuration structure */
EDMA_config(hEdma, &cfgEdma);
/* Let‘s also configure th e reload parameter tables in the EDMA PRAM */ /* with the values in the configuration structures. */
EDMA_config(hEdmaPing, &cfgEdmaPing);
EDMA_config(hEdmaPong, &cfgEdmaPong);
/* Configure up the timer. */
TIMER_configArgs(hTimer,
TIMER_CTL_OF(0×00000200),
TIMER_PRD_OF(TPRD), /* timer period */
TIMER_CNT_OF(0)
);
/* Enable the related interrupts */
IRQ_enable(IRQ_EVT_EDMAINT);
EDMA_intDisable(TCCINTNUM);
EDMA_intClear(TCCINTNUM);
EDMA_intEnable(TCCINTNUM);
/* Enable the EDMA channel */
EDMA_enableChannel(hEdma);
/* Finally, enable the timer which will drive everything. */
TIMER_start(hTimer);
while(transferCount <= TRANSFER_CNT); /* waiting for interrupts */
}
/*—————————————————————————-*/
void processbuff(int arg){
int *inbuff;
int x;
printf(―\n %2d -‖,transferCount);
if (pingpong){
/* If pingpong is 0, then we own the ping input buffer */
inbuff = ping;
printf(‖ Ping ―);
}else{
/* If pingpong is 1, then we own the pong input buffer */
inbuff = pong;
printf(‖ Pong ‖ );
}
transferCount++;
/* Now let‘s process the input buffer, for simplicity, we‘ll */
/* just copy it to the output buffer. */
for (x=0; x outbuff[x] = inbuff[x]; } /* If this example is enhanced to actually do something with the */ /* output buffer such as DMA it somewhere, you will want to flush */ /* it out of the cache first. */ CACHE_wbInvL2(outbuff, (BUFF_SZ << 2), CACHE_WAIT); /* Since we‘re done processing the input buffer, clean it from cache, */ /* this invalidates it from cache to ensure we read a fresh version */ /* the next time. */ CACHE_wbInvL2(inbuff, (BUFF_SZ << 2), CACHE_WAIT); } /*—————————————————————————-*/ /************************************************************************ \ name: SetInterruptsEdma purpose: Sets up interrupts to service EDMA transfers inputs: void returns: void \************************************************************************ void setupInterrupts(void) { IRQ_setVecs(vectors); /* point to the IRQ vector table */ IRQ_nmiEnable(); IRQ_globalEnable(); IRQ_map(IRQ_EVT_EDMAINT, 8); IRQ_reset(IRQ_EVT_EDMAINT); } /* End of SetInterruptsEdma() */ /************************************************************************ \ name: Interrupt Service Routine c_int08 purpose: ISR to service EDMAINT. vecs.asm must be modified to include c_int08 entry. inputs: n/a returns: n/a \************************************************************************ / interrupt void c_int08(void) { /* Clear the pending interrupt from the EDMA interrupt pending register */ EDMA_intClear(TCCINTNUM); /* Perform ping-pong */ pingpong = (pingpong + 1) & 1; /*Exit from the program if certain no of transfres are done*/ if (transferCount >= TRANSFER_CNT) { TIMER_pause(hTimer); stopEdma(); TIMER_close(hTimer); printf (―\nDone…..‖); exit(0); } /* Based on if we ping‘ed or pong‘ed, we need to set the EDMA channel */ /* link address for the NEXT frame. */ if (pingpong){ /* Currently doing pong so setup next frame for ping */ /* Modify the input data source, this just simulates */ /* the input data changing. */ ping_data++; /* Rememer to flush this variable out of the cache */ /* since it‘s the source of an EDMA transfer*/ CACHE_wbInvL2(&ping_data, 4, CACHE_WAIT); /* Now filling pong so set link to ping */ EDMA_link(hEdma,hEdmaPing); }else{ /* Currently doing ping so setup next frame for pong */ /* Modify the output data source, this just simulates */ /* the input data changing. */ pong_data++; /* Rememer to flush this variable out of the cache */ /* since it‘s the source of an EDMA transfer*/ CACHE_wbInvL2(&pong_data, 4, CACHE_WAIT); /* Now filling ping so set link to pong */ EDMA_link(hEdma,hEdmaPong); } processbuff(0); return; } /* end c_int08 */ /************************************************************************ name: stopEdma purpose: Stops the EDMA service. inputs: void returns: void \************************************************************************ / void stopEdma(void) { /*Disable interrupts, close EDMA channel before exit of the program*/ IRQ_disable(IRQ_EVT_EDMAINT); EDMA_RSET(CCER,0×00000000); EDMA_disableChannel(hEdma); EDMA_intDisable(TCCINTNUM); EDMA_intClear(TCCINTNUM); EDMA_close(hEdma); EDMA_resetAll(); EDMA_RSET(CIPR,0xFFFFFFFF); EDMA_RSET(ECR,0xFFFFFFFF); }