Zynq Ultrascale +(简称ZU+)内使用ZDMA做各种外设和内存的数据搬移,从SDK上看,ZDMA被分为了两种,一种是ADMA,用于外设之间的低速传输;另一种是GDMA,用于内存直接高速传输;其中这两种DMA都可以支持两种工作模式:Simple模式和Scatter-Gather(简称SG)模式;Simple模式适合连续地址空间的传输,而SG模式适合多地址空间的传输,SG模式需要一个内存描述来告诉它需要传哪些地址区域,根据下一区域的指示不同,SG模式又可以分为Linear模式和Linked-List模式、混合模式;本文主要基于SG模式的Linear模式展开。
SG模式的Linear模式需要提供一个Buffer Descriptor(简称BD)数组,BD数组的每个元素都是128位宽,如下图所示:
ADDR LSB、ADDR MSB和SIZE很容易理解,CNTL为DMA如何执行下一步提供了必要的信息:
- l [0] Coherency:
- n 0: 生成用于处理描述符有效载荷的AXI事务是被标记为non-coherent.
- n 1: 生成用于处理描述符有效载荷的AXI事务是被标记为coherent.
- 注: 在FPD DMA控制器下这个比特位没有作用。
- l [1] DSCR元素类型:
- 每个描述符可以是128比特或256比特.
- n 0: 当前描述符大小是128比特 (linear)
- n 1: 当前描述符大小是256 比特(linked-list)
- l [2] INTR
- n 0: 不需要完成终端
- n 1 (SRC-side): 在完成这次传输后产生中断;完成意味着数据被读取,但可能在DMAbuffer内(还没有写入目的地址).
- n 1 (DST-side): 在完成这次传输后产生中断;完成意味着数据被写入目的地址并且BRESP已收到。
- l [4:3] CMD
- 这个字段仅仅在源描述符有效,在目的描述符保留
- n 00: 下一个DSCR有效, DMA继续以SG操作模式进行 (in this case). 软件必须确保下一个描述是有效的.
- n 01:在完成当前描述符后暂停. 软件可以使用这命令来暂停DMA操作,更新描述符.
- n 一旦软件完成更新描述符,可以通道从暂停的位置恢复,如果软件更新描述符到一个新的位置,通道可以恢复,并且从新的位置获取描述符;暂停模式允许软件保存通道的状态而避免了重新启用DMA;
- n 10: 在完成当前描述符后停止DMA操作。一旦DMA通道检测到停止,它完成当前描述符报文传输,转变为IDLE状态. 任何后续传输都需要软件遵循启用顺序. 停止不会保留当前通道的状态.
- n 11: 保留
手册上操作模式写的很复杂,参考代码也涉及了大量的Cache处理步骤,针对ZU+将DDR分片的特性,可以指定BD描述符在特定内存地址上创建,这样BD描述符的写入写出不经过Cache,不需要操作Cache;
操作步骤如下:
1. 编程ZDMA_CH_CTRL0的POINT_TYPE位为1(切换到SG模式);
2. 建立两个BD结构体数组,源端和目的端,依次填入需要搬移的内存的地址、大小和操作行为;
3. 将BD结构体数组的首地址写入ZDMA_CH_SRC_START_LSB、ZDMA_CH_SRC_START_MSB、ZDMA_CH_DST_START_LSB、ZDMA_CH_DST_START_MSB
4. 按需要设置其他寄存器,例如OverFetch,有些情况需要有些不需要;
5. 启动通道DMA传输,通过写入1进ZDMA_CH_CTRL2寄存器;
6. 轮询ZDMA_CH_STATUS,如果为0则可以退出,如果为3表示DMA过程出现错误,需要检查ZDMA_CH_ISR查看出现什么错误,如果出现1,则需要Resume,出现1一般是用户在BD里的CNTL设置了暂停操作,具体操作取决于用户自己,为2就表示DMA仍未完成。
通过SGDMA,可以将内存中两块区域的值进行交换,并且只需要执行一次DMA操作,下述代码即实现此功能:
- u64 base_addr = 0x840000000;
- init_platform();
- Xil_DCacheDisable();
- print("Hello Worldnr");
- XZDma_LiDscr *Src_Des = 0x10000;
- XZDma_LiDscr *Dst_Des = 0x20000;
- for(int i=0; i<0x1000; i=i+4)
- {
- Xil_Out32(i+base_addr+0x0100, i);
- Xil_Out32(i+base_addr+0x2100, i+0x2100);
- }
- Src_Des[0].Size = 0x1000;
- Src_Des[0].Cntl = 0;
- Src_Des[0].Address = base_addr+0x0100;
- Src_Des[1].Address = base_addr+0x2100;
- Src_Des[1].Size = 0x1000;
- Src_Des[1].Cntl = 0;
- Src_Des[2].Address = base_addr+0x1100;
- Src_Des[2].Size = 0x1000;
- Src_Des[2].Cntl = 0x10;
- Dst_Des[0].Address = base_addr+0x1100;
- Dst_Des[0].Size = 0x1000;
- Dst_Des[0].Cntl = 0;
- Dst_Des[1].Address = base_addr+0x0100;
- Dst_Des[1].Size = 0x1000;
- Dst_Des[1].Cntl = 0;
- Dst_Des[2].Address = base_addr+0x2100;
- Dst_Des[2].Size = 0x1000;
- Dst_Des[2].Cntl = 0x10;
- if((Xil_In32(ZDMA_ADDR+ZDMA_CH_CTRL0)&0x40)!=0x40)
- Xil_Out32(ZDMA_ADDR+ZDMA_CH_CTRL0, (Xil_In32(ZDMA_ADDR+ZDMA_CH_CTRL0)&0x7f)|0x40);
- Xil_Out32(ZDMA_ADDR+ZDMA_CH_CTRL1, 0);
- Xil_Out32(ZDMA_ADDR+ZDMA_CH_SRC_START_LSB, ((UINTPTR)(&Src_Des[0]))&0xFFFFFFFF);
- Xil_Out32(ZDMA_ADDR+ZDMA_CH_SRC_START_MSB, (((u64)Src_Des)>>32)&0xFFF);
- Xil_Out32(ZDMA_ADDR+ZDMA_CH_DST_START_LSB, ((UINTPTR)(&Dst_Des[0]))&0xFFFFFFFF);
- Xil_Out32(ZDMA_ADDR+ZDMA_CH_DST_START_MSB, (((u64)Dst_Des)>>32)&0xFFF);
- Xil_Out32(ZDMA_ADDR+ZDMA_CH_CTRL2, 1);
- while(Xil_In32(ZDMA_ADDR+ZDMA_CH_STATUS)==2);
- xil_printf("Done Status: %dnr", Xil_In32(ZDMA_ADDR+ZDMA_CH_STATUS));
- for(int i=0; i<0x1000; i=i+4)
- {
- if(Xil_In32(i+base_addr+0x0100)!=i+0x2100)
- {
- xil_printf("Error: %x in %xnr", i, Xil_In32(i+0x100));
- }
- if(Xil_In32(i+base_addr+0x2100)!=i)
- {
- xil_printf("Error: %x in %xnr", i, Xil_In32(i+0x2100));
- }
- }
结果:
结果可以看出,两片内存空间值被交换。
src.zip
(3.43 KB)
(下载次数: 8, 2019-10-17 19:22 上传)