一、EDMA模块介绍
TMS320C6748是一款高性能DSP,非常适合用来跑大数据量的算法,而这样的算法往往需要保证实时性,那么这么大规模的数据怎么进行运算能够让程序跑的最快,我们知道DSP中运算最快的存储单元是RAM区,但是RAM毕竟有限的,无法存储大量数据,因而我们只能把数据存在DDR中,这时我们需要一种工具能够将数据从DDR中导入RAM中运算,算完的结果再发回DDR中存储。大家学习《微机原理》的时候应该听说过DMA吧,简单来说就是CPU把数据传送的源、目的、数据个数等信息交给DMA,让DMA控制数据传输,在此期间,CPU可以运行其他程序,等到DMA控制的数据传输完成,发送中断告诉CPU我的数据传输完成了,你来处理吧,因而提高了算法的速度,Ti公司为TMS320C6748量身设计了增强版直接存储器访问控制器EDMA3。
EDMA3一共有32个通道,通道的优先级可选,可以实现数据传输的无缝链接,为什么是无缝的,后面我们会具体说到。利用EDMA,可以实现片内存储器、片内外设以及外部存储器之间的数据传输。
1、EDMA概述
前面已经介绍过了DMA的原理,下面我们来说EDMA能够完成哪些功能。
(1)一维二维数据传输,包括数据转置,不知道你在写程序时有没有遇到需要转置的数据矩阵,这里的转置其实也是提高算法运算速度的一种手段吧,在DSP配套的MATHLIB文件中提供了一种基于向量寻址的快速算法,我们知道无论什么处理器运算的第一步一定是待计算数据的寻址,然后才是运算,数据寻址其实需要占用非常多的时间,而向量寻址的快速算法可以对一个连续的向量进行高速运算,因为每计算完一次,地址自动加1就行,节省了运算时间。
(2)事件、链式事件触发,也就是说可以利用一个中断,或者事件触发EDMA传输,例如当SPI、UPP传输完数据后给EDMA控制器一个信号,告诉EDMA我的数据准备好了,存入DDR吧。
(3)地址重载,EDMA应用的是PaRAM保存每组要传输数据的信息,源、目的地址,数据个数等信息,其中有一个参量是链接到其他PaRAM的地址,额,有点复杂,下面具体说吧。
(4)乒乓存储,非常实用高效的一种数据流控制方法,简单来说就是将RAM分为两个部分,第一个周期第一段数据由EDMA传入RAM1,第二个周期对RAM1中数据进行处理,同时,第二段数据由EDMA传入RAM2,第三个周期对RAM2中数据进行处理,同时第三段数据由EDMA传入RAM1 ,如此循环,可以对数据进行高效处理。
EDMA通道控制器主要由以下5个部分组成:事件相关寄存器、事件选择器、参数RAM、QDMA检测器、结束和错误检测器。事件寄存器完成对EDMA事件的捕获,一个事件相当于一个同步信号,由它触发某个EDMA通道开始传输数据。如果有多个事件同时发生,由事件选择器对他们进行识别。在EDMA的参数RAM中存放与事件相关的传输参数,这些参数送入传输请求,进而产生对外设读写操作所需要的地址。EDMA的通道控制器和传输器的结构如上图所示。
2、EDMA传输相关
(1)传输数据类型
EDMA3 的传输类型EDMA3 的传输是在3 个维度上定义的:
1. Array (A):一次传输中的一维指ACNT 个连续字节。
2. Frame (B):一次传输中的二维指BCNT 个含有ACNT 个字节的阵列array。 每个在二维传输中的array 是通过SRCBIDX/DSTBIDX 来分隔的。
3. Block (C):一次传输中的三维指CCNT 个含有BCNT 个阵列的帧,每个阵列array 含有ACNT 个字节。在三维上进行的传输是通过SRCCIDX/DSTCIDX 与前次传输进行分隔的。
在这三种维度上,EDMA3 只支持两种同步方式的传输:A 同步传输和AB 同步传输,而EDMA3并不直接支持ABC 同步传输,但可以通过将多个AB 同步传输通过链方式来间接支持。A 同步传输方式相对AB 同步传输方式简单,下面着重分析AB 同步传输方式。
在一个 AB 同步的传输中,EDMA3 同步事件会在2 个维度上即一帧对传输进行初始化。
即每个事件所提交的传输请求会要求传输一帧的数据,即BCNT 个array,每个array 含有ACNT 个连续字节,因此一次传输BCNTxACNT 个字节。因此,在AB 同步传输的PaRAM时,进行传输的次数为CCNT 次,即该参数表对应的事件数为CCNT 个。
阵列 array 间通过SRCBIDX 及DSTBIDX 两个参数进行分隔,如下图所示,阵列N 的起始地址等于阵列N-1 的起始地址加上SRCBIDX/DSTBIDX。帧间通过SRCCIDX 及DSTCIDX 参数进行分隔。对于一个AB 同步传输,一个传输请求TR 被提交后,地址的更新是通过对帧头阵列的地址加SRCCIDX/DSTCIDX来实现的。
(2)传输触发方式
DMA 传输的触发方式有三种方式可以触发在DMA 通道上的传输:
1.Event-triggered transfer request 事件触发传输请求:外设、系统、外部事件产生的传输请求。
2.Manually-triggered transfer request 手动触发传输请求:CPU 向相应的事件置位寄存器(ESR/ESRH)中写1 来触发传输。
3.Chain-triggered transfer request 链触发传输请求:另一传输完成时触发的传输。
外设或引脚产生事件时,事件寄存器中相应位将置1(ER.En = 1)。如果事件使能寄存器(EER)相应位被使能(EER.En = 1),那么EDMA3CC 将会按优先级处理该事件并将其排队。
3、EDMA寄存器
(1)通道控制寄存器
QCHMAPn:[
PAENTRY]QDMA链接到指定的PaRAM,PaRAM一共有128个参数集,也就意味可以配置128组EDMA传输参数。
DMAQNUMn、QDMAQNUM:安排事件到队列0或者队列1中,队列0优先级高于队列1,推荐,高优先级用于短时突发和单元素传输,低优先级用于长时块的搬移。另外一旦一个队列里的请求满了,那么这个队列如果再来请求的时候,EDMA控制器会STALL,所以所有的请求都会不响应,直到那个队列有空闲位置才继续响应事件。所以,我们不要让某个级别的请求太过繁忙,从而导致STALL发生。比较好的情况是:让各个级别的请求的负担基本差不多,这样避免EDMA控制器阻塞。
ER:只读,事件寄存器,但设置相应位后相应的事件被激活。ESR:事件设置寄存器。ECR:事件清除寄存器。CER:事件链接寄存器,也就是说本事件发生的同时,让另一事件对应寄存器ER的位为有效,EDMA处理完第一个事件,将继续处理第二个事件。EER:只读,事件使能寄存器,只有该寄存器的位使能,对应的事件才发生了才能使ER置位,也才能触发EDMA传输。EECR:事件使能清除寄存器,EESR,事件使能设置寄存器。
(2)PaRAM寄存器(参数RAM寄存器)
OPT:传输配置参数
SRC:源地址参数。
A_B_CNT:高16位为2维计数器值,低16位为1维计数器值.
DST:目的地址参数。
SRC_DST_BIDX:高16位为传输目的索引,低16位为传输源的索引。
LINK_BCNTRLD:高16位为二维传输参数重载,也就是说本次EDMA传输完成,该参数会复制到
A_B_CNT的高16位,这样可以再次出=触发该EDMA而传输不同的参数,低16位为参数RAM链接地址。也即是说本次EDMA传输完成,低16位为参数RAM链接地址所指向的PaRAM中的参数复制到当前所使用的PaRAM。
SRC_DST_CIDX:高16位为三维传输目的索引,低16位为三维传输源索引。
CCNT:低16位为3维传输计数器。
索引,简单来说就是在传输完一个数据后源/目的地址加几。
4、EDMA例程
(1) 手动触发事件11,可运行PaRAM11中的传递参数并通过PaRAM.LINK链接到PaRAM10,再手动触发事件11即可运行PaRAM10中的传递参数。
extern void setup_EDMA (
void)//EDMA3初始化,通道10,使用CPU开始中断
{
// Clear EventRegisters
CSL_FINST(edma3ccRegs->ECR, EDMA3CC_ECR_REG, MASK);//清空中断事件
CSL_FINST(edma3ccRegs->SECR, EDMA3CC_SECR_REG, MASK);//清空中断事件
/*---------------------------------- EDMA Event10-------------------------------*/
// Enable Channel 10to DSP (Region 1)
CSL_FINST(edma3ccRegs->DRA[CSL_EDMA3_REGION_1].DRAE,
EDMA3CC_DRAE_E10,ENABLE);//设置DRA1通道的第10个通道
// Assign Channel 10to Queue 0
CSL_FINST(edma3ccRegs->DMAQNUM[1], EDMA3CC_DMAQNUM_E2, Q0);//将通道10放置在队列0中;
//由于只定义了E0-E7,在NUM0时对应E2,在NUM1时对应E10,查表可知
// Ini
tialize
PaRAMTransfer Context for Event 10
// init_PaRAM_event10();//设置参数向量
// Enable Channel 10Event Register
CSL_FINST(edma3ccRegs->EESR, EDMA3CC_EESR_E10, SET);//使能事件10
// Enable Interruptsfor Channel 10
CSL_FINST(edma3ccRegs->IESR, EDMA3CC_IESR_I10, SET);//允许通道10中断
/*---------------------------------- EDMA Event11-------------------------------*/
// Enable Channel 11to DSP (Region 1)
CSL_FINST(edma3ccRegs->DRA[CSL_EDMA3_REGION_1].DRAE,
EDMA3CC_DRAE_E11,ENABLE);//设置DRA1通道的第11个通道
// Assign Channel 11to Queue 0
CSL_FINST(edma3ccRegs->DMAQNUM[1], EDMA3CC_DMAQNUM_E3, Q0);//将通道11放置在队列0中;
//由于只定义了E0-E7,在NUM0时对应E3,在NUM1时对应E11,查表可知
// Initialize PaRAMTransfer Context for Event 11
//init_PaRAM_event11();//设置参数向量
// Enable Channel 11Event Register
CSL_FINST(edma3ccRegs->EESR, EDMA3CC_EESR_E11, SET);//使能事件10
// Enable Interruptsfor Channel 11
CSL_FINST(edma3ccRegs->IESR, EDMA3CC_IESR_I11, SET);//允许通道10中断
}
/*---------------------------------------------------------------------------*/
/* setup_EDMA */
/*---------------------------------------------------------------------------*/
void main(
void)
{
setup_EDMA();//初始化EDMA3寄存器
for(jj=0;jj<2;jj++)
{
for(kk=0;kk<256;kk++)
{
SRC_AA[jj][kk]=kk*(jj+1);
}
}
for(jj=0;jj<2;jj++)
{
for(kk=0;kk<256;kk++)
{
SRC_BB[jj][kk]=kk;
}
}
for(jj=0;jj<256;jj++)
{
SRC_CC[jj]=2*jj;
}
for(jj=0;jj<256;jj++)
{
SRC_DD[jj]=3*jj;
}
EDMA_event11_Buffer_transfer(1,0,2,2,256);//flag(单行转秩1/矩阵转秩other);转换第x行;数据类型;行数;列数
EDMA_event10_Buffer_transfer(1,1,2,2,256);//flag(单行转秩1/矩阵转秩other);转换第x行;数据类型;行数;列数
CSL_FINST(edma3ccRegs->ESR, EDMA3CC_ESR_E11, SET);
delay(10000);
CSL_FINST(edma3ccRegs->ESR, EDMA3CC_ESR_E11, SET);
delay(10000);
}
/*--------------------------------数组转秩-----------------------------------*/
// function:完成通道10单行/矩阵转秩存储,需要在函数中修改源、目的地址,当转秩矩阵时,参数为转换第0行
// parameters:flag(单行转秩1/矩阵转秩other);转换第x行;数据类型;行数;列数
int EDMA_event10_Buffer_transfer(Uint16 flag,Uint16 trans_number,Uint16 type_data,Uint16 row,Uint16 column)//参数RAM配置
{
int time_EDMA=0;
edma3ccRegs->PARAMSET[EDMA_EVENT10].OPT = CSL_EDMA3CC_OPT_RESETVAL;//参数RAM复位
//
ConfigPaRAM OPT (Enable TC Interrupt & ITC Chaining; Set TCC)//允许产生中断,对应事件10
edma3ccRegs->PARAMSET[EDMA_EVENT10].OPT =
CSL_FMKT(EDMA3CC_OPT_ITCCHEN,ENABLE) |
CSL_FMKT(EDMA3CC_OPT_TCINTEN,ENABLE) |
CSL_FMK(EDMA3CC_OPT_TCC,EDMA_EVENT10)|
CSL_FMKT(EDMA3CC_OPT_TCCMOD,NORMAL) |
CSL_FMKT(EDMA3CC_OPT_FWID,8BIT) |//仅仅针对常数位有效
CSL_FMKT(EDMA3CC_OPT_STATIC,NORMAL) |
CSL_FMKT(EDMA3CC_OPT_SYNCDIM,ASYNC) |
CSL_FMKT(EDMA3CC_OPT_DAM,INCR) |
CSL_FMKT(EDMA3CC_OPT_SAM,INCR);
// Initialize EDMAEvent
Src and
Dst Addresses
edma3ccRegs->PARAMSET[EDMA_EVENT10].SRC = (Uint32)&(SRC_AA);//设置源数组地址
edma3ccRegs->PARAMSET[EDMA_EVENT10].DST = (Uint32)&(DST_BB[0][trans_number]);//设置目的数组地址
// Set EDMA EventPaRAM A,B,C CNT
edma3ccRegs->PARAMSET[EDMA_EVENT10].A_B_CNT =
CSL_FMK(EDMA3CC_A_B_CNT_ACNT,type_data) |//设定传输数据类型
CSL_FMK(EDMA3CC_A_B_CNT_BCNT,column);//设置传输数目
if(flag==1)
{
edma3ccRegs->PARAMSET[EDMA_EVENT10].CCNT = 1;
}
else
{
edma3ccRegs->PARAMSET[EDMA_EVENT10].CCNT = row;
}
edma3ccRegs->PARAMSET[EDMA_EVENT10].SRC_DST_BIDX =
CSL_FMK(EDMA3CC_SRC_DST_BIDX_SRCBIDX,type_data) |
CSL_FMK(EDMA3CC_SRC_DST_BIDX_DSTBIDX,row*type_data);//二维索引
edma3ccRegs->PARAMSET[EDMA_EVENT10].SRC_DST_CIDX =
CSL_FMK(EDMA3CC_SRC_DST_CIDX_SRCCIDX,type_data) |
CSL_FMK(EDMA3CC_SRC_DST_CIDX_DSTCIDX,(1-column)*type_data*row+type_data);//三维索引
// Set EDMA EventPaRAM LINK and BCNTRLD
edma3ccRegs->PARAMSET[EDMA_EVENT10].LINK_BCNTRLD =
CSL_FMK(EDMA3CC_LINK_BCNTRLD_LINK,0xFFFF) |
CSL_FMK(EDMA3CC_LINK_BCNTRLD_BCNTRLD,column);
time_EDMA=1;
return time_EDMA;
}
/*--------------------------------数组转秩PaRAM参数-----------------------------------*/
// function:完成通道11单行/矩阵转秩存储,需要在函数中修改源、目的地址,当转秩矩阵时,参数为转换第0行
// parameters:flag(单行转秩1/矩阵转秩other);转换第x行;数据类型;行数;列数
int EDMA_event11_Buffer_transfer(Uint16 flag,Uint16 trans_number,Uint16 type_data,Uint16 row,Uint16 column)//参数RAM配置
{
int time_EDMA=0;
edma3ccRegs->PARAMSET[EDMA_EVENT11].OPT = CSL_EDMA3CC_OPT_RESETVAL;//参数RAM复位
//
ConfigPaRAM OPT (Enable TC Interrupt & ITC Chaining; Set TCC)//允许产生中断,对应事件10
edma3ccRegs->PARAMSET[EDMA_EVENT11].OPT =
CSL_FMKT(EDMA3CC_OPT_ITCCHEN,ENABLE) |
CSL_FMKT(EDMA3CC_OPT_TCINTEN,ENABLE) |
CSL_FMK(EDMA3CC_OPT_TCC,EDMA_EVENT11)|
CSL_FMKT(EDMA3CC_OPT_TCCMOD,NORMAL) |
CSL_FMKT(EDMA3CC_OPT_FWID,8BIT) |//仅仅针对常数位有效
CSL_FMKT(EDMA3CC_OPT_STATIC,NORMAL) |
CSL_FMKT(EDMA3CC_OPT_SYNCDIM,ASYNC) |
CSL_FMKT(EDMA3CC_OPT_DAM,INCR) |
CSL_FMKT(EDMA3CC_OPT_SAM,INCR);
// Initialize EDMAEvent
Src and
Dst Addresses
edma3ccRegs->PARAMSET[EDMA_EVENT11].SRC = (Uint32)&(SRC_CC);//设置源数组地址
edma3ccRegs->PARAMSET[EDMA_EVENT11].DST = (Uint32)&(DST_BB[0][trans_number]);//设置目的数组地址
// Set EDMA EventPaRAM A,B,C CNT
edma3ccRegs->PARAMSET[EDMA_EVENT11].A_B_CNT =
CSL_FMK(EDMA3CC_A_B_CNT_ACNT,type_data) |//设定传输数据类型
CSL_FMK(EDMA3CC_A_B_CNT_BCNT,column);//设置传输数目
if(flag==1)
{
edma3ccRegs->PARAMSET[EDMA_EVENT11].CCNT = 1;
}
else
{
edma3ccRegs->PARAMSET[EDMA_EVENT11].CCNT = row;
}
edma3ccRegs->PARAMSET[EDMA_EVENT11].SRC_DST_BIDX =
CSL_FMK(EDMA3CC_SRC_DST_BIDX_SRCBIDX,type_data) |
CSL_FMK(EDMA3CC_SRC_DST_BIDX_DSTBIDX,row*type_data);//二维索引
edma3ccRegs->PARAMSET[EDMA_EVENT11].SRC_DST_CIDX =
CSL_FMK(EDMA3CC_SRC_DST_CIDX_SRCCIDX,type_data) |
CSL_FMK(EDMA3CC_SRC_DST_CIDX_DSTCIDX,(1-column)*type_data*row+type_data);//三维索引
// Set EDMA EventPaRAM LINK and BCNTRLD
edma3ccRegs->PARAMSET[EDMA_EVENT11].LINK_BCNTRLD =
CSL_FMK(EDMA3CC_LINK_BCNTRLD_LINK,0x4140) |
CSL_FMK(EDMA3CC_LINK_BCNTRLD_BCNTRLD,column);
time_EDMA=1;
return time_EDMA;
}
这里PaRAM.LINK是这么计算的:
每个PaRAM占用32个字,那么PaRAM10就是10*32=320=140h,那么链接地址就是0x4140。