ARM技术论坛
直播中

agdhun

9年用户 470经验值
擅长:可编程逻辑 嵌入式技术 EDA/IC设计 处理器/DSP 接口/总线/驱动 RF/无线
私信 关注
[经验]

【MYD-CZU3EG开发板试用体验】ZDMA的使用——Scatter-gather的使用1

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位宽,如下图所示:
1.png
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操作,下述代码即实现此功能:
  1. u64 base_addr = 0x840000000;
  2.     init_platform();
  3.     Xil_DCacheDisable();
  4.     print("Hello Worldnr");

  5.     XZDma_LiDscr *Src_Des = 0x10000;
  6.     XZDma_LiDscr *Dst_Des = 0x20000;

  7.     for(int i=0; i<0x1000; i=i+4)
  8.     {
  9.             Xil_Out32(i+base_addr+0x0100, i);
  10.             Xil_Out32(i+base_addr+0x2100, i+0x2100);
  11.     }

  12.     Src_Des[0].Size    = 0x1000;
  13.     Src_Des[0].Cntl    = 0;
  14.     Src_Des[0].Address = base_addr+0x0100;

  15.     Src_Des[1].Address = base_addr+0x2100;
  16.     Src_Des[1].Size    = 0x1000;
  17.     Src_Des[1].Cntl    = 0;

  18.     Src_Des[2].Address = base_addr+0x1100;
  19.     Src_Des[2].Size    = 0x1000;
  20.     Src_Des[2].Cntl    = 0x10;

  21.     Dst_Des[0].Address = base_addr+0x1100;
  22.     Dst_Des[0].Size    = 0x1000;
  23.     Dst_Des[0].Cntl    = 0;

  24.     Dst_Des[1].Address = base_addr+0x0100;
  25.     Dst_Des[1].Size    = 0x1000;
  26.     Dst_Des[1].Cntl    = 0;

  27.     Dst_Des[2].Address = base_addr+0x2100;
  28.     Dst_Des[2].Size    = 0x1000;
  29.     Dst_Des[2].Cntl    = 0x10;

  30.     if((Xil_In32(ZDMA_ADDR+ZDMA_CH_CTRL0)&0x40)!=0x40)
  31.             Xil_Out32(ZDMA_ADDR+ZDMA_CH_CTRL0, (Xil_In32(ZDMA_ADDR+ZDMA_CH_CTRL0)&0x7f)|0x40);
  32.     Xil_Out32(ZDMA_ADDR+ZDMA_CH_CTRL1, 0);
  33.     Xil_Out32(ZDMA_ADDR+ZDMA_CH_SRC_START_LSB, ((UINTPTR)(&Src_Des[0]))&0xFFFFFFFF);
  34.     Xil_Out32(ZDMA_ADDR+ZDMA_CH_SRC_START_MSB, (((u64)Src_Des)>>32)&0xFFF);

  35.     Xil_Out32(ZDMA_ADDR+ZDMA_CH_DST_START_LSB, ((UINTPTR)(&Dst_Des[0]))&0xFFFFFFFF);
  36.     Xil_Out32(ZDMA_ADDR+ZDMA_CH_DST_START_MSB, (((u64)Dst_Des)>>32)&0xFFF);

  37.     Xil_Out32(ZDMA_ADDR+ZDMA_CH_CTRL2, 1);

  38.     while(Xil_In32(ZDMA_ADDR+ZDMA_CH_STATUS)==2);

  39.     xil_printf("Done Status: %dnr", Xil_In32(ZDMA_ADDR+ZDMA_CH_STATUS));
  40.     for(int i=0; i<0x1000; i=i+4)
  41.         {
  42.             if(Xil_In32(i+base_addr+0x0100)!=i+0x2100)
  43.             {
  44.                     xil_printf("Error: %x in %xnr", i, Xil_In32(i+0x100));
  45.             }
  46.             if(Xil_In32(i+base_addr+0x2100)!=i)
  47.                 {
  48.                         xil_printf("Error: %x in %xnr", i, Xil_In32(i+0x2100));
  49.                 }
  50.         }
结果:
1.png
结果可以看出,两片内存空间值被交换。

src.zip (3.43 KB)
(下载次数: 8, 2019-10-17 19:22 上传)

回帖(1)

飞洋

2019-10-22 16:50:26
感谢分享,楼主辛苦了
举报

更多回帖

发帖
×
20
完善资料,
赚取积分