四、S5PC100 PL330测试例子
由于PL330学习起来略显烦琐,因此建议在理解代码的基础上做实验,这样才能对DMA的学习有一定的深入。篇幅有限这里只给出核心的代码,若读者想参考完整源代码,请去华清远见官方论坛上下载。
下面的代码目标要实现内存间的数据拷贝。对于S5PC100,有3个DMA控制器。要实现内存间的DMA访问,需要使用DMA_mem。
如下图所示为DMAC控制流程。
图DMAC控制流程
配合上面的流程图,可以编写代码如下。
(1)相关的宏定义。
#define MAX 100
#define Inp(addr) (*(volatileunsigned int *)(addr))
#define Outp(addr, data) (*(volatileunsigned int *)(addr) = (data))
extern void printf(const char *fmt, ...);
void int_dma();
volatile char sour[32] = "012345678901234567890123456789\n";
volatile char dest[32] = "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\n";
//最终实现将32个字节从sour的数据传输到dest
(2)设置SAR、CCR、DAR寄存器。
//main函数开始
uart0_init();
volatile char instr_seq[MAX];
int size = 0, x;
int loopstart, loopnum = 2; //每个循环传输16个字节,传输2次
unsigned int source, destination,start, temp;
source = (unsigned int)sour;
destination = (unsigned int)dest;
start = (unsigned int)instr_seq; //记录DMA指令的首地址
/*DMAMOV SAR0*/
instr_seq[size + 0] =(char)(0xbc);
instr_seq[size + 1] = (char)(0x0);
instr_seq[size + 2] =(char)((source>>0) & 0xff); //设置数据源地址
instr_seq[size + 3] =(char)((source>>8) & 0xff);
instr_seq[size + 4] =(char)((source>>16) & 0xff);
instr_seq[size + 5] =(char)((source>>24) & 0xff);
size = 6;
/*DMAMOV DAR0*/
instr_seq[size + 0] = (char)(0xbc);
instr_seq[size + 1] = (char)(0x2);
instr_seq[size + 2] =(char)((destination>>0) & 0xff); //设置数据目标地址
instr_seq[size + 3] =(char)((destination>>8) & 0xff);
instr_seq[size + 4] =(char)((destination>>16) & 0xff);
instr_seq[size + 5] = (char)((destination>>24)& 0xff);
size += 6;
/*DMAMOV CC0. burst_size 8byte,burst_len 2*/
instr_seq[size + 0] =(char)(0xbc);
instr_seq[size + 1] = (char)(0x1);
//设置数据传输规则,每个循环传输burst_size* burst_len、原和目标地址变化规则、burst操作等
instr_seq[size + 2] = (char)(0x17);
instr_seq[size + 3] =(char)(0xc0);
instr_seq[size + 4] = (char)(0x5);
instr_seq[size + 5] = (char)(0x0);
size += 6;
(3)设置指令段的起始地址及执行第一次数据装载并输出FIFO。
/*DMALP LC0*/
instr_seq[size + 0] =(char)(0x20);
instr_seq[size + 1] =(char)(loopnum - 1);// 记录循环的次数
size += 2;
loopstart = size;
/*DMALD*/
instr_seq[size + 0] =(char)(0x04); //从源读数据
size += 1;
/*DMARMB*/
instr_seq[size + 0] =(char)(0x12);
size += 1;
/*DMAST*/
instr_seq[size + 0] =(char)(0x08); //写数据到目标地址
size += 1;
/*DMAWMB*/
instr_seq[size + 0] =(char)(0x13);
size += 1;
(4)产生中断,并延时一段时间。
/*可以在DMA指令执行过程中做延时。此处可以利用延时保证DMA传输完成后再停止DMA*/
/*DMALP LC0*/
instr_seq[size + 0] = (char)(0x20);
instr_seq[size + 1] = (char)(250); //循环次数
size += 2;
loopstart = size;
/*DMANOP*/
instr_seq[size + 0] = (char)(0x18); //DMA的NOP空指令,可以实现延时
size += 1;
/*DMALPEND 0*/
instr_seq[size + 0] = (char)(0x38);
instr_seq[size + 1] = (char)(size - loopstart);
size += 2;
/*DMASEV*/
instr_seq[size + 0] = (char)(0x34);
instr_seq[size + 1] = (char)(1<<3); //通过DMA通道1发出中断申请,也可以选择其它的通道
size += 2;
#endif
(5)结束DMAC控制。
/*DMAEND*/
instr_seq[size + 0] = (char)(0x0);
size += 1;
(6)开始DMAC控制,设置相应的中断处理,并进行测试结果。
VIC0VECADDR18 = (unsigned int)int_dma;//DMA_mem的处理函数
INTERRUPT.VIC0INTENABLE |= 1<<18; //使能中断控制器对应的中断位
Outp(0xE8100000+0x20, 0x2); //使能控制器的1中断通道,此处可以选择其它的通道,要和DMASEV对应
/*DMAGO*/
do{
x = Inp(0xE8100D00);//检测DMA状态,确认可以操作
} while ((x&0x1)==0x1);
Outp(0xE8100D00+0x8,(0<<24)|(0xa0<<16)|(0<<8)|(0<<0));//DBGINST0 通道1
Outp(0xE8100D00+0xC, start);//DBGINST1
Outp(0xE8100D00+0x4, 0);//DBGCMD 执行DBGINST0、1中的DMAGO指令,start为开始地址
while(1);
//main函数结束
(7)ISR函数的实现如下。
void do_irq()
{
printf("in do_irq\n");
((void (*)(void))VIC0ADDRESS)();
}
/*ISR*/
void int_dma()
{
VIC0ADDRESS = 0;
Outp(0xE8100000+0x2C, 0x2);//清处DMA中断挂起位,因为上面选择的是通道1,所以清楚对应的位
printf("DMA Ending!\n");
printf("sour = %s", sour);
printf("dest = %s", dest);
}
实验调试过程与结果:
(1)将程序编译后生成.bin文件,打开终端使用uboot的dnw命令通过USB线将.bin文件下载到0x20008000这个地址,接着使用go命令去执行测试程序。
(2)可以看到下图所示的测试结果,如图所示。
五、小节
本文从PL330出发,介绍了基本的S5PC100下DMA控制器的操作方式和编程模型,旨在将最新的DMA控制技术以最简单的方式呈现。笔者还实现了串口控制器的收发之间使用DMA传输的例程。读者如果感兴趣可以在华清远见研发中心关于FS_S5PC100平台的论坛上下载。硬件控制器关联的DMA和内存间的DMA控制差别比较大的是DMA每次传输依赖控制器发出申请,DMA需要等待到一次申请后才能完成一次传输。而内存间的DMA可以由程序主动发起。
DMA memtomem 单次传输成功。
PL330 的DMA驱动和以往驱动的不同之处:
s3c2440/s3c6410 处理器内部的 DMA 都是提供特殊功能寄存器 SFR 对 DMA 进行编程,但 PL330 不是这样。
PL330 DMA 的基本工作原理是:
a) DMA 有自己的特殊指令集,基本是1-6个字节不定长,主要有 DMAMOV/DMALD/DMAST/DMAGO
b) DMA 的驱动主要包含两个工作:
### 1) 开辟一个buf,在buf里面存放DMA的二进制指令,可以完成数据传输操作。
### 2) 通过执行 DMAGO 指令,使得 DMA 内部的取指部件 pc 跳转到 buf,从 buf 取出并执行相应的指令。
DMAGO 指令的本身的执行,是通过写 S5PV210 芯片的 DBGCMD/DBGINST0/DBGINST1 这3个寄存器实现的。
c) linux 下的 pl330.c 是可以参考使用的驱动,主要包含了一系列 _emit_MOV/_emit_LD/_emit_ST 等类似指令的接口。
这些接口完成类似编译器的工作,把一个DMA操作函数,转换为存放在 buf 数组里面的二进制指令。
d) 驱动编程的关键在于形成 buf 内部的正确机器指令,然后通过对 DBGCMD/DBGINST0/DBGINST1 这3个寄存器进行编程,完成跳转到 buf 执行指令。
DMALP/DMALPEND 还未成功,仍在调试