最近在搞Xilinx的PCIE板卡的时候,研究了一下linux的xdma驱动代码,在此记录一下学习过程。
环境配置:
X86平台的ubuntu2204,内核版本:6.5.0
使用PCIE2.0 x4的板卡和主机通信
1、准备工作
1.1、下载和编译驱动
驱动下载地址:https://github.com/Xilinx/dma_ip_drivers
笔者选择dma_ip_drivers-2019.2版本进行下载。
Xdma ip的寄存器描述:
https://docs.amd.com/r/zh-CN/pg195-pcie-dma/%E5%AF%84%E5%AD%98%E5%99%A8%E7%A9%BA%E9%97%B4
编译和加载驱动:
cd dma_ip_drivers-2019.2/XDMA/linux-kernel/xdma
make
sudo insmod xdma.ko
2.1、编译和测试应用程序
cd dma_ip_drivers-2019.2/XDMA/linux-kernel/tools
gcc dma_from_device.c dma_from_device
gcc dma_from_device.c -o dma_from_device
// 生成测试文件
dd if=/dev/zero of=test_zero.bin bs=4096 count=1
// 主机发送数据给PCIE板卡
./dma_to_device -d /dev/xdma0_h2c_0 -f ./test_zero.bin -s 4096 -a 0 -c 1 #-a 后面跟的地址是pcie域的地址,逻辑开发人员会告诉你
// 主机读取PCIE板卡数据
./dma_from_device -d /dev/xdma0_c2h_0 -f ./test_zero_get.bin -s 4096 -a 0 -c 1
2、XDMA驱动初始化流程
驱动初始化的重点初始化dma的 传输引擎,还要根据Xilinx官方定义的寄存器来定义和申请传输描述符,最后还要创建用户设备文件点/dev/xdma0_c2h_0、/dev/xdma0_control、/dev/xdma0_events_0等。
3、数据传输流程
以主机向PCIE板卡(H2C)传输数据为例,来分析一下xdma的数据传输流程。
数据传输的过程其实涉及很多东西,包括PCIE、DMA、中断、任务调度、数据同步等很多知识。
下面从代码层面大概分析一下:
当H2C传输完成后,PCIE设备会产生中断,CPU的中断服务程序流程如下:
流程还是很复杂的,笔者用了两周时间才把代码看懂并且把大部分逻辑捋顺,为了能完全理解DMA的操作,还写了DMA相关的例程代码,也参考了Xilinx的XDMA驱动代码,可以看笔者的另一篇文章获取代码:
Linux 流式DMA映射(DMA Streaming Mapping)-CSDN博客
4、总结
本文主要从DMA的角度出发去讨论Xilinx的PCIE传输过程。
值得注意的是,在pcie dma传输数据的过程中,驱动程序会申请两次DMA。第一次是在驱动初始化的时候:
engine->desc = dma_alloc_coherent(&xdev->pdev->dev,
XDMA_TRANSFER_MAX_DESC *
sizeof(struct xdma_desc),
&engine->desc_bus, GFP_KERNEL);
申请的是xdma IP规定的dma描述符,其结构体定义如下:
struct xdma_desc {
u32 control;
u32 bytes; /* transfer length in bytes */
u32 src_addr_lo; /* source address (low 32-bit) */
u32 src_addr_hi; /* source address (high 32-bit) */
u32 dst_addr_lo; /* destination address (low 32-bit) */
u32 dst_addr_hi; /* destination address (high 32-bit) */
/*
* next descriptor in the single-linked list of descriptors;
* this is the PCIe (bus) address of the next descriptor in the
* root complex memory
*/
u32 next_lo; /* next desc address (low 32-bit) */
u32 next_hi; /* next desc address (high 32-bit) */
} __packed;
把engine->desc_bus直接写到XDMA IP的0H2C SGDMA Descriptor Low Address和H2C SGDMA Descriptor High Address寄存器,然后xdma ip就可以获取到内存中该结构体对应的内容。
第二次申请是在应用程序和pcie设备传输数据的时候,也就是H2C或C2H的时候。驱动程序会根据应用程序申请内存空间大小进行流式dma映射,然后把pcie设备能操作的总线地址写到struct xdma_desc的src_addr_lo、src_addr_hi、dst_addr_lo和dst_addr_hi,要传输的大小写到bytes,这样pcie设备就可以通过XDMA IP的0H2C SGDMA Descriptor Low Address和H2C SGDMA Descriptor High Address寄存器的地址去获取dma的传输信息了。
5、参考资料
Xilinx官方的XDMA驱动代码和寄存器描述文档:
驱动下载地址:https://github.com/Xilinx/dma_ip_drivers
笔者选择dma_ip_drivers-2019.2版本进行下载。
Xdma ip的寄存器描述:
https://docs.amd.com/r/zh-CN/pg195-pcie-dma/%E5%AF%84%E5%AD%98%E5%99%A8%E7%A9%BA%E9%97%B4