需要在ZYNQ平台上使用DMA驱动,裸机的还到好说,Linux下的DMA驱动框架复杂,这对本身不是搞驱动的我难度太大。自己动手丰衣足食,但是试错成本很大,记录下来希望能给后来者帮助。
开源项目xilinx_axidma
Petalinux2020.2 开发ZYNQ的AXI DMA - 知乎 (zhihu.com)
Linux环境下在用户空间使用AXI-DMA进行传输
vivado2020.1,ubuntu18.04下运行的petalinux
开发板 AXU9EG
FPGA工程:配套工程 dma_loopback
pl.dtsi 中系统生成的DMA设备树:
路径:[项目]/components/plnx_workspace/device-tree/device-tree
#### pl.dtsi中生成的设备树
amba_pl: amba_pl@0 {
#address-cells = <2>;
#size-cells = <2>;
compatible = "simple-bus";
ranges ;
axi_dma_0: dma@80000000 {
#dma-cells = <1>;
clock-names = "s_axi_lite_aclk", "m_axi_mm2s_aclk", "m_axi_s2mm_aclk";
clocks = <&zynqmp_clk 71>, <&zynqmp_clk 71>, <&zynqmp_clk 71>;
compatible = "xlnx,axi-dma-7.1", "xlnx,axi-dma-1.00.a";
interrupt-names = "mm2s_introut", "s2mm_introut";
interrupt-parent = <&gic>;
interrupts = <0 89 4 0 90 4>;
reg = <0x0 0x80000000 0x0 0x10000>;
xlnx,addrwidth = <0x20>;
xlnx,sg-length-width = <0xe>;
dma-channel@80000000 {
compatible = "xlnx,axi-dma-mm2s-channel";
dma-channels = <0x1>;
interrupts = <0 89 4>;
xlnx,datawidth = <0x20>;
xlnx,device-id = <0x0>;
};
dma-channel@80000030 {
compatible = "xlnx,axi-dma-s2mm-channel";
dma-channels = <0x1>;
interrupts = <0 90 4>;
xlnx,datawidth = <0x20>;
xlnx,device-id = <0x0>;
};
};
};
根据生成的DMA在system-user.dtsi做相应修改添加axidma_chrdev设备
路径:[项目]/project-spec/meta-user/recipes-bsp/device-tree/files
/include/ "system-conf.dtsi"
/ {
};
/* SD */
&sdhci1 {
disable-wp;
no-1-8-v;
};
/* USB */
&dwc3_0 {
status = "okay";
dr_mode = "host";
};
&amba_pl{
axidma_chrdev: axidma_chrdev@0 {
compatible = "xlnx,axidma-chrdev";
dmas = <&axi_dma_1 0 &axi_dma_1 1 &axi_dma_0 2 &axi_dma_0 3>;
dma-names = "tx_channel", "rx_channel", "tx_channel", "rx_channel";
};
};
&amba_pl{
axidma_chrdev: axidma_chrdev@0 {
compatible = "xlnx,axidma-chrdev";
dmas = <&axi_dma_0 0 &axi_dma_0 1 >;
dma-names = "tx_channel", "rx_channel";
};
};
&axi_dma_0 {
dma-channel@80000000 {
xlnx,device-id = <0x0>;
};
dma-channel@80000030 {
xlnx,device-id = <0x1>;
};
};
注意两点:
1、根据自己的开发板,修改设备树满足Linux运行基本条件,如这里的SD卡和USB;
2、这里的axi_dma_0为DMA外设,地址要和pl.dtsi中的一致。
按照上面两个教程操作基本没啥问题,如果采用相同版本的话,默认CMA为256MB,添加了DMA,Kernel的DMA也会使能,这里没有特别注意该地方。
1、使用petalinux构建模块:
petalinux-create -t modules --name xilinx-axidma --enable
2、添加驱动源码
由于版本变更,驱动代码需要进行稍微修改,参考Petalinux2020.2 开发ZYNQ的AXI DMA - 知乎 (zhihu.com)
这里我将petalinux下的搭建的module放到CSDN上了,使用的内核版本是(5.4.0), xilinx-axidma驱动模块petalinux2020
3、编译为独立的模块
在 project-spec/meta-user/conf/petalinuxbsp.conf 中添加以下行
RM_WORK_EXCLUDE += "xilinx-axidma"
4、编译驱动模块
petalinux-build -c xilinx-axidma
您可在 [项目]/build/tmp/work/zynqmp_generic-xilinx-linux/xilinx-axidma/1.0-r0 中找到编译的驱动模块
对DMA进行了测试,源码里有两个例子,一个是文件传输 axidma_transfer.c,一个用于测试带宽 axidma_benchmark.c,基于Qt搭建的测试工程也放到驱动链接里面了。
wkxilinx-axidma驱动模块petalinux2020-Linux文档类资源-CSDN文库
注意:测试时发现一个问题,你需要将传输的大小设置 > 4KB 一定要大于4KB数据才正常,等于4KB也不好使。(也可能是buf的大小需要 >4KB,猜测是不是和PAGESIZE有关,4KB)
这样带来了测试困惑:FPGA工程中DMA回环fifo是4KB大小,和测试有冲突,一种方法是增大fifo的深度,另一种方法是先开启DMA读再开启DMA写。
rc = axidma_oneway_transfer(axidma_dev, rx_channel, rx_buf, rx_size,false);
rc = axidma_oneway_transfer(axidma_dev, tx_channel, tx_buf, tx_size,true);