2. Step 2确认寄存器ZDMA_CH_CTRL0 的POINT_TYPE 位为0(这个位控制ZDMA处于简单模式还是Scatter-Gather模式,0表示工作在简单模式)
将数据源buffer的低位地址写入寄存器ZDMA_CH_SRC_DSCR_WORD0,高位地址写入寄存器ZDMA_CH_SRC_DSCR_WORD1
3. Step 3将数据目的buffer的低位地址写入寄存器ZDMA_CH_DST_DSCR_WORD0,高位地址写入寄存器ZDMA_CH_DST_DSCR_WORD1.
4. Step 4将需要传输的源数据的大小写入寄存器ZDMA_CH_SRC_DSCR_WORD2,将需要传输到目的的数据的大小写入寄存器ZDMA_CH_DST_DSCR_WORD2
注:需要保证源数据大小和目的数据大小是相等的。
5. Step 5可以通过设置寄存器ZDMA_CH_DST_DSCR_WORD3和/或ZDMA_CH_SRC_DSCR_WORD3的INTR位来启用中断,这个步骤是可选的,如果使用轮询结果模式,这一步就不需要,当然如果实现将中断启用后,那么使用轮询则需要配置禁用中断。
6. Step 6在将源缓冲区和目标缓冲区分配为缓存一致性或刷新的情况下,无需设置COHRNT。 否则,如果未将源缓冲区和目标缓冲区分配为缓存一致性或未刷新,请分别在ZDMA_CH_SRC_DSCR_WORD3和DMA_CH_DST_DSCR_WORD3寄存器中设置COHRNT。 COHRNT位仅在LPD DMA情况下有效。 FPD DMA不支持一致性。
7. Step 7通过设置寄存器ZDMA_CH_CTRL2的EN位,启用DMA通道来完成DMA传输;在DMA启用后,检测是否出现错误。可能出现一下几种错误:
上述步骤则是ZDMA配置为简单DMA时的操作步骤,在实际使用过程也不仅仅是只配ZDMA,与ZDMA相关联的中断控制器,源地址目的地址对应的内存或外设,如果是DDR,而且还启用了D-Cache,还要处理好D-Cache的内存占用范围等,故详细步骤如下:
1. 准备好源地址数据,判断是不是采用CacheCoherent,如果不是需要刷新源地址区域,将目的地址区域设置为不在DCache的有效范围内,设置ZDMA模式
2. 设置中断回调函数(完成中断和错误中断),设置中断系统(配置ScuGic),如果采用轮询则就禁用中断,
3. 配置ZDMA Over Fetch功能、Outstanding事务大小、Burst类型和长度
4. 配置ZDMA,启动ZDMA
5. 读取ZDMA状态,如果DONE为1,退出
下面做一个从DDR的特定地址搬移到DDR特定地址的程序,由于D-Cache与DDR处理较为繁琐,此处我直接禁用D-Cache, Zynq MPSoc将DDR区域分成两块,低2GB在0x00000000-0x7ffffffff,剩余区域在0x800000000;由于直接在DDR内搬移,最好将PS端程序空间和需要搬移的空间错开,本文处理是程序放在DDR_1区间,搬移DDR_0空间的数据,同时加了简单测试DDR带宽的部分;
- u32 temp = 0;
- Xil_DCacheDisable();
- char *SrcBuf = 0x00000000;
- char *DstBuf = (char *)0x5000000;
- Xil_Out32(ZDMA_ADDR+ZDMA_CH_CTRL2, 0); //Disabled Channel
- //Xil_Out32(XPAR_PSU_GDMA_0_BASEADDR+ZDMA_CH_CTRL0, (over_fetch_en<<7)|(dma_type<<6)|(dma_mode<<4)|(rate_control<<3)|(cont_addr<<2));
- temp = Xil_In32(ZDMA_ADDR+ZDMA_CH_STATUS);
- if(((temp&0x3)!=3)&&((temp&0x3)!=0)) {
- xil_printf("DMA Not In IDLE!");
- }
-
- for(int i = 0; i < 1024; i++)
- {
- *(SrcBuf+i) = i;
- *(DstBuf+i) = 0xff;
- }
- Xil_Out32(ZDMA_ADDR+ZDMA_CH_SRC_DSCR_WORD0, 0x00000000);
- Xil_Out32(ZDMA_ADDR+ZDMA_CH_SRC_DSCR_WORD1, 0x00000000);
- Xil_Out32(ZDMA_ADDR+ZDMA_CH_SRC_DSCR_WORD2, 0x5000000);
- Xil_Out32(ZDMA_ADDR+ZDMA_CH_DST_DSCR_WORD0, 0x5000000);
- Xil_Out32(ZDMA_ADDR+ZDMA_CH_DST_DSCR_WORD1, 0x00000000);
- Xil_Out32(ZDMA_ADDR+ZDMA_CH_DST_DSCR_WORD2, 0x5000000);
-
- Xil_Out32(ZDMA_ADDR+ZDMA_CH_CTRL2, 0x1);
- while(Xil_In32(ZDMA_ADDR+ZDMA_CH_CTRL2)&0x1);
- int status = Xil_In32(ZDMA_ADDR+ZDMA_CH_STATUS)&0x3;
- xil_printf("status: %dn", status);
- if(status == 3)
- {
-
- }
- for(int i = 0 ; i < 1024; i++)
- {
- if(*(SrcBuf+i)!=*(DstBuf+i))
- xil_printf("%d:%d:%d:%d ", i, *(DstBuf+i), *(SrcBuf+i), *((char *)(0x5000000|i)));
- }
-
- print("Hello Worldnr");
-
- Xtime tStart;
- XTime tEnd;
- XTime tDiff;
- double tPerfS;
- Calc: XTime_GetTime(&tStart);
- for(int i = 0; i < 10; i ++)
- {
- Xil_Out32(ZDMA_ADDR+ZDMA_CH_CTRL2, 0x1);
- while((Xil_In32(ZDMA_ADDR+ZDMA_CH_STATUS)&0x3)!=0&&(Xil_In32(ZDMA_ADDR+ZDMA_CH_STATUS)&0x3)!=3);
- // while(Xil_In32(ZDMA_ADDR+ZDMA_CH_CTRL2)&0x1);
- }
- XTime_GetTime(&tEnd);
- tDiff = tEnd - tStart;
- /* Convert tPerf into seconds */
- tPerfS = (1.0 * tDiff / (1.0 * COUNTS_PER_SECOND));
- printf("DDR bandwidth: %f GB/s, Time: %f snr", (10*0x5000000*1.0/tPerfS)/1024/1024/1024, tPerfS);
- goto Calc;
复制代码
测试结果如下:
由于读写同时,最后算出的带宽需要倍增,故约为10.95GB/s,DDR4理论2400MT/s,64位宽接口,带宽为19.2GB.