ZYNQ的DMA
1 DMA的特点和体系结构
DMA外设特点:
- DMA引擎拥有一个灵活的指令设置DMA的传输;
- 拥有8个cache线,每一个cache线宽度是4个字;
- 拥有8个可以并行的DMA通道线程;
- 拥有8个中断给中断控制器;
- 拥有8个DMA触发事件并且可以编码控制;
- 128个(64bit)的MFIFO,在传输的时候读写端可写入到此FIFO;
- 支持任意内存到内存的传输;
整个系统中的DMA控制器如图1所示,
图1 DMA控制器系统图
DMAC包含一个指令处理单元,其能够编码控制DMA传输,每一个线程包含一个独立的状态机处理各自的DMA事件,包括通道仲裁,通道优先级。
当一个通道线程执行加载或者存储指令的时候控制器会将指令增加到读队列和写队列中,控制器使用这些队列来存储指令,并且按队列指令顺序在AXI总线上完成传输。
DMA在AXI总线上传输:
所有的DMA传输使用AXI接口移动数据,包括片上内存的移动,DDR内存,以及PL上的从外设内存。PL端的从外设正常连接到DMAC外设接口上控制其数据流。DMAC在PS端可以访问到IOPs,但是正常情况下不会使用,因为这些路径不会提供数据流信号。DMAC数据路径正常使用情况如图2所示,没一个AXI路径可以执行一个读或者一个写,其中拥有好多组合,典型的DMA传输有:1,内存到内存的传输(片上内存到DDR内存);2,内存到PL端外设或者PL端外设到内存(DDR内存到PL端外设)。
图2 数据流传输
DMA的管理:
DMAC实时操作时,用户可以通过下面的指令设置DMA的传输:
DMAGO:开始一个用户指定通道的DMA传输;DMASEV:用户指定的一个事件或者中断发生时给出信号;DMAKILL:终止一个线程。
当DMA管理器接受到一个从APB从接口的指令后,会等待若干个时钟周期,在执行指令之前(pipeline是处于忙的状态,在执行其他指令)。
多通道数据FIFO(MFIFO):
MFIFO是一个当前所有活动通道共享的,基于先进入先服务的共享资源。对于编程角度来讲,它是以份额深度可变的并行的FIFO集合,每个通道都有一个FIFO,但是所有FIFO的总深度不能超多MFIFO的大小,DMAC的MFIFO深度最大为128个64bit的大小。
事件和中断:
DMAC支持16个事件,开始的8个事件是中断信号,IRQs[7:0],这8个中断都会输出到PS或者PL的中断控制器。这些事件使用内部的DMA引擎通道与通道之间的传输。EMAC的中断事件表如图3所示。
图3 事件与中断
2 DMA的配置实例
配置DMA做内存到内存的传输实例。
DMA配置步骤:
- 初始化dma的命令数据结构,主要配置传输源地址,目的地址,传输长度,burst的大小等信息;
- 通过DMA的ID信息,找到DMA外设信息;
- 初始化dma的数据结构;
- 连接到硬件中断,将GIC中断映射到中断向量表中;
- 通过GIC的ID信息,找到GIC外设信息;
- 链接DMA中断和GIC,将DMA中断映射到GIC控制器上;
- 时能GIC中断;
- 使能硬件中断;
- 设置中断服务函数的映射配置;
- 开始DMA的传输;
- 等待DMA的传输完成;
程序源码:
XDmaPs_Config *DmaConfigPtr;
XScuGic_Config *GicConfigPtr;
XDmaPs_Cmd DmaCmd;
volatile int Checked = 0;
int Index = 0;
memset(&DmaCmd, 0, sizeof(XDmaPs_Cmd));
DmaCmd.ChanCtrl.SrcBurstSize = 4;
DmaCmd.ChanCtrl.SrcBurstLen = 4;
DmaCmd.ChanCtrl.SrcInc = 1;
DmaCmd.ChanCtrl.DstBurstSize = 4;
DmaCmd.ChanCtrl.DstBurstLen = 4;
DmaCmd.ChanCtrl.DstInc = 1;
DmaCmd.BD.SrcAddr = (u32) Src;
DmaCmd.BD.DstAddr = (u32) Dst;
DmaCmd.BD.Length = DMA_LENGTH * sizeof(int);
//find device
DmaConfigPtr = XDmaPs_LookupConfig(XPAR_XDMAPS_1_DEVICE_ID);
//config xdmaps data
XDmaPs_CfgInitialize(&Dma,DmaConfigPtr,DmaConfigPtr->BaseAddress);
//config gic
//config hardware interrupt
Xil_ExceptionInit();
Xil_ExceptionRegisterHandler(XIL_EXCEPTION_ID_IRQ_INT,(Xil_ExceptionHandler)XScuGic_InterruptHandler,(void *)&Gic);
//find device
GicConfigPtr = XScuGic_LookupConfig(XPAR_SCUGIC_SINGLE_DEVICE_ID);
//config gic data
XScuGic_CfgInitialize(&Gic,GicConfigPtr,GicConfigPtr->CpuBaseAddress);
//connect gic handler
XScuGic_Connect(&Gic,XPAR_XDMAPS_0_FAULT_INTR,(Xil_InterruptHandler)XDmaPs_FaultISR, (void *)&Dma);
XScuGic_Connect(&Gic,DMA_DONE_INTR_0,(Xil_InterruptHandler)XDmaPs_DoneISR_0, (void *)&Dma);
//enable gic
XScuGic_Enable(&Gic,XPAR_XDMAPS_0_FAULT_INTR);
XScuGic_Enable(&Gic,DMA_DONE_INTR_0);
//enable hardware interrupt
Xil_ExceptionEnable();
//handler connect
XDmaPs_SetDoneHandler(&Dma,0,(XDmaPsDoneHandler)DmaDoneHandler,(void *)&Checked);
/* Initialize source */
for (Index = 0; Index < DMA_LENGTH; Index++)
Src[Index] = DMA_LENGTH - Index;
/* Clear destination */
for (Index = 0; Index < DMA_LENGTH; Index++)
Dst[Index] = 0;
//start dma tran
XDmaPs_Start(&Dma, 0,&DmaCmd,0);
//wait tran over
while(1)
{
if(Checked == 1)
{
print("tran over!");
break;
}
}