概括
平台为 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 映射。文章来源:https://www.toymoban.com/news/detail-435592.html
4.Cache一致 配置的可能稍微复杂一点儿,但是 最终做下来 也能达到相应的效果,并且能对 ZYNQ这个 芯片架构更了解一些。关于cache 同步可以参考这个
关于linux内存管理中DMA ZONE和dma_alloc_coherent若干误解的澄清文章来源地址https://www.toymoban.com/news/detail-435592.html
到了这里,关于ZYNQ AXI-DMA Linux Cache 一致的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!