ZYNQ AXI-DMA Linux Cache 一致


概括

平台为 ZYNQ MPSOC
项目使用到AXI-DMA ,ADC模块传输数据到DDR,应用层进行数据的读取。在此做些记录
用到了AXI-Stream , IP核用的 米联客的ui_axisbufw,可以把流数据转为AXI-Stream 接口

比较重要的参考链接
1.UltraScale+MPSoC+Cache+Coherency
https://xilinx-wiki.atlassian.net/wiki/spaces/A/pages/18842098/Zynq+UltraScale+MPSoC+Cache+Coherency
2.MPSoC逻辑加速模块数据通道快速设计
https://cloud.tencent.com/developer/article/1663578
3.git上开源的 axi-dma Linux 驱动, 实现零拷贝
https://github.com/bperez77/xilinx_axidma
4.
Petalinux 加速axi-dma内核驱动缓冲区读过程

一、FPGA部分关键点

1.AXI-DMA IP核的配置,配置好一次最大传输字节数,这里没有使用SG模式,一次最大传输字节数设置为了2的26次方也就是一次DMA最大传输64MB数据,地址宽度设置为64bit,如果设置为32bit 可能会出现DMA 访问不了 ARM 的 另一块内存的问题。(当然Linux cma内存预留的时候 可以指定到32位地址空间)
在这里插入图片描述
测试发现使用 bperez77/xilinx_axidma 的内核驱动, 开启SG模式,会有接收错误,关闭SG模式,正常,问题还待解决(当然不解决也能用哈哈)。
这里只使能了 DMA 写入通道,也就是从PL发送到PS的DDR, 也就是S2MM(stream to mem map)。

2.AXI-DMA 与 ARM 连接
在这里插入图片描述
这里最好是使用AXI SmartConnect 进行连接, 以减少出错的可能。ARM端使用HPC0进行连接
3.AXCACHE 与AXPROT
这一部分涉及 Cache 同步问题, 也就是FPGA 刷新 应用DDR内存的问题, FPGA 刷新完 内存, CPU可能不知道,CPU再次读的时候有可能Cache命中 读取到了旧数据。
这部分主要是为了cache 同步,测试发现这个最好是做,当然不做cache 同步 也好像没啥问题, 但是 可能会突然给你点儿小惊喜。
cache 同步需要 比较繁杂的配置,参考官方wiki 的8.2

在这里插入图片描述
主要是配置这4个
①PL部分配置 AXCACHE 设置 0xF
②AxPORT 配置为 0x02,也就是第2bit 为1 ,不安全模式
③是能cache侦听, 需要配合vitis,在 FSBL部分进行处理
④设备树增加 dma-coherent
这几步可以参考 一开始提到的 MPSoC逻辑加速模块数据通道快速设计

二、ARM部分的关键配置

1.设备树

设备(示例):
这里需要注意 dma-coherent; 这个属性得加一下,然后axi_dma 部分参考 vitis 生成的就行。
a0000000 为 ip和分配的地址,需要对应起来。

	/* AXI-DMA */
	axidma_chrdev: axidma_chrdev@0 {
		#dma-cells = <1>;
		compatible = "xlnx,axidma-chrdev";
		dmas       =  <&axi_dma_0 0 &axi_dma_0 1>;
		dma-names  = "tx_channel", "rx_channel";
		dma-coherent;
	};

	axi_dma_0: dma@a0000000 {
		status = "okay";
		#dma-cells = <1>;
		clock-names = "s_axi_lite_aclk", "m_axi_mm2s_aclk", "m_axi_s2mm_aclk", "m_axi_sg_aclk";
		clocks = <&zynqmp_clk 71>, <&zynqmp_clk 71>, <&zynqmp_clk 71>, <&zynqmp_clk 71>;
		compatible = "xlnx,axi-dma-1.00.a";
		interrupt-names = "mm2s_introut", "s2mm_introut";
		interrupt-parent = <&gic>;
		interrupts = <0 91 4>, <0 92 4>;
		reg = <0x0 0xa0000000 0x0 0x10000>;
		xlnx,addrwidth = <0x40>;//64bit addr 
		xlnx,sg-length-width = <0x1A>;//buffer len    26 bit 64M  <0x17>;//buffer len  23 bit 8M
		dma-coherent;
		dma-channel@a0000000 {
			compatible = "xlnx,axi-dma-mm2s-channel";
			dma-channels = <0x1>;
			interrupt-parent = <&gic>;
			interrupts = <0 91 4>;
			xlnx,datawidth = <0x80>;//data width 128
			xlnx,device-id = <0x0>;
		};
		
		dma-channel@a0000030 {
			compatible = "xlnx,axi-dma-s2mm-channel";
			dma-channels = <0x1>;
			interrupt-parent = <&gic>;
			interrupts = <0 92 4>;
			xlnx,datawidth = <0x80>;//data width 128
			xlnx,device-id = <0x1>;
		};
		
	};

2.AXI-DMA 应用层 驱动

git 上 大佬做的 xilinx_axidma 驱动 , 以下可能需要修改的地方,因为我的内核是4.19。
of_dma_configure的第一个参数 不要搞错了, 不然 会出现 应用层读buf 缓慢的现象,当时博主也是找问题找了好久。
剩下的就是 可能要包含以下 of 的头文件了。
在这里插入图片描述
在这里插入图片描述
注意!!!不要按上述的patch 去修改 ,否则会有问题!!!!DMA mask not set 这个log
好像没啥影响。

3.应用层编程

应用层主要使用的api 可以参考 xilinx_axidma 的demo,主要用到了以下的API

    axidma_init(); //初始化 axi dma 设备,获取fd
    axidma_malloc(axidma_dev, tx_size);//这里会做内存映射,映射cma 内存到用户空间
    axidma_oneway_transfer(dev, rx_channel, rx_buf, rx_size, true);//单向传输,可以发送、接收
    axidma_free(axidma_dev, tx_buf, tx_size);//释放内存映射,也就是cma 内存
    axidma_destroy(axidma_dev);//关闭设备

总结

1.AXI-DMA 个人理解为PL到PS交互提供了一个桥梁,如果只是控制寄存器的话,使用AXI-Lite就可以,AXI-Stream 适合数据流交互(PL2PS PS2PL 都是互通的)。

2.xilinx 虽然做了axi-dma 的驱动,但是为应用层提供接口的驱动还不是很完善(虽然wiki上有xilinx官方开源的)。

3.使用 https://github.com/bperez77/xilinx_axidma 这个axidma 可以很方便的实现 应用层的dma 数据零拷贝,直接dma 到 内核的CMA 内存区域,其实这个CMA内存也是在DDR上,零拷贝的实现是通过内核空间内存映射实现的,所以是0拷贝,最终测试下来 内存的读写速度和读写DDR基本一致。

在其中mmap 函数在其中发挥了很大作用,用户层调用mmap,转到驱动的 mmap,驱动的mmap 拿到用户空间的地址和大小之后 对内核的 cma 内存进行dma 映射。

4.Cache一致 配置的可能稍微复杂一点儿,但是 最终做下来 也能达到相应的效果,并且能对 ZYNQ这个 芯片架构更了解一些。关于cache 同步可以参考这个
关于linux内存管理中DMA ZONE和dma_alloc_coherent若干误解的澄清

  • 4
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 5
    评论
Zynq SoC 上,AXI DMA(Direct Memory Access)是一种能够在外设和存储器之间进行高速数据传输的IP核。Cyclic DMA 模式允许通过循环传输数据,从而实现连续的数据流。下面是一些关于在 Zynq SoC 上编程 AXI DMA 的基本步骤: 1. 配置 AXI DMA IP:首先,在 Vivado 中配置和实例化 AXI DMA IP 核,并连接到 Zynq SoC 的 PS(Processing System)部分。确保正确连接 AXI DMA 的信号和端口,包括数据输入/输出端口、中断线和控制寄存器。 2. 设置 DMA 控制寄存器:使用 AXI DMA 的控制寄存器来配置 DMA 的工作模式和参数。在 Cyclic DMA 模式下,你需要设置循环传输模式,并指定传输的数据长度和起始地址。 3. 配置 DMA 缓冲区:为 DMA 设置输入和输出缓冲区。这些缓冲区将用于在外设和存储器之间传输数据。 4. 启动 DMA 传输:通过设置 AXI DMA 控制寄存器中的位来启动数据传输。在 Cyclic DMA 模式下,你可以选择是否在每次传输完成后自动重启传输。 5. 处理中断:如果需要,在 AXI DMA 完成传输时,你可以使用中断机制来通知处理器。在中断处理程序中,你可以执行必要的操作,如处理传输的数据或触发下一次传输。 需要注意的是,以上步骤只是一个基本的框架,具体的实现可能因应用需求而有所差异。你可以参考 Xilinx 提供的文档和示例代码来详细了解 AXI DMA 的编程和配置方法。希望对你有所帮助!

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值