Xlinx-ZYNQ-DMA驱动

Xilinx-Axi-dma驱动

前言

本篇文章基于赛灵思的xczu7ev核心作为实验对象,由于项目要求只做从PL端自定义的逻辑IP核把数据通过DMA搬运到PS端的内存。只有接收通道,当然也发送通道与接收的通道原理一致。文章前面对赛灵思的DMA控制器原理进行简单的介绍,后面为Xlinx Linux DMA的函数使用进行讲解。

本文涉及一点FPGA基础和一点点Linux的知识。

Author:Swing

Date:2023/12/23

参考

参考链接:

https://xilinx-wiki.atlassian.net/wiki/spaces/A/pages/1027702787/Linux+DMA+From+User+Space+2.0

https://www.kernel.org/doc/Documentation/DMA-API.txt

本地文本为参考内核文档:

./Documentation/driver-api/dmaengine/client.rst

./Documentation/driver-api/dmaengine/provider.rst

1. DMA控制器

1.1. DMA控制器介绍

描述:这个没什么好说的,网上的相关介绍太多。简单的一句话,DMA控制器不占用CPU的资源,可以直接从相关的外设控制器搬运到内存或者内存的数据搬运到相关外设控制器。例如常见的:SPI DMA,UART DMA,VIDIO DMA,Camera DMA。

image-20231223161013111

1.2. DMA控制数据传输原理

描述:以Vivado2022.1版本,基于AXI DMA的IP核原型进行原理讲解。

1.2.1. AXI总线

描述:可以看到1.1的图控制器之间通过AXI进行连接通讯。AXI是一种通讯的接口,类似与SPI,I2C,UART,MIPI,HDMI。但是与他们不同的是AXI是在芯片内部进行通讯,所以你想看到他的通讯时序只能通过仿真的方式进行查看。这里不讲解AXI总线的通讯时序,有兴趣可以看Xilinx官网对AXI总线时序的讲解文章。

用我的一句话总结:AXI分为Master_AXISlave_AXI两种,Master_AXI能传输数据的同时具有寻址能力。是AXI总线是一个同步通信接口。如何寻址呢?C语言void * point = 0x …;寻已经映射出来的物理地址,当然要AXI有接到的器件并支持可映射。

链接:https://docs.xilinx.com/v/u/en-US/ug1037-vivado-axi-reference-guide

1.2.2. Slave DMA控制器数据传输

描述:DMA作为slave端,也就是使用发送通道为例,是通过AXI总线进行寻址,寻址后待握手成功后发送数据。

image-20231223163758425

1.2.3. Master DMA控制器数据传输

描述:DMA作为Master端,往PS端(Slave)传输数据。

image-20231223164427426

2. Vivado部分

2.1. 简单介绍

本文章使用DMA Master往PS端的内存进行写数据。首先是自定义的IP核通过AXI往DMA控制传输数据。

image-20231223164956415

然后DMA通过AXI对PS端传输数据。

image-20231223165042290

2.2. PS端如何对DMA控制器进行配置(额外篇)

描述:通过AXI LITE总线

image-20231223165221943

2.2. 注意事项

注意1:PS端到DMA控制器的传输不管是发送还是接收,都是由PS端的AXI LITE对DMA控制器发起的传输通知。

注意2:描述因为PS端跑的Linux系统是具备64位寻址,寻址范围能力大于32位的4G。

所以DMA控制器的寻址宽度需要注意,DMA寻址是寻真实的物理地址,设置32 Bit宽度的话寻址范围只有0~4G。

image-20231223170003852

3. Petalinux部分

3.1. Linux的DMA子系统

image-20231223170640833

描述:这里是Linux的DMA控制器子系统的框图。对Linux的DMA控制器更多的介绍建议参考Linux的说明文档。

说明文档位于内核源码,路径:./Documentation/driver-api/dmaengine/client.rst./Documentation/driver-api/dmaengine/provider.rst

在Linux的DMA子系统中,是一个发起一个异步传输的方式,类似于SPI的。

在发起传输完成后通过自己设置的回调函数,回调函数触发则完成。一般使用

3.2. Xilinx DMA核心层

源码路径位于:drivers/dma/xilinx/xilinx_dma.c

可以看到核心层对DMA控制器的进行配置的函数封装。

image-20231223171425119

例如在我们调用相关结构体内的函数(device_alloc_chan_resources)指针时候,函数指针指向了xilinx_dma_alloc_chan_resources。

由于API众多,这里核心层是xilinx芯片官方适配的,我们不需要去改动什么。只是进行简单的介绍。

DMA的模式分为三种:

  1. dma_cyclic
  2. slave_sg
  3. interleaved_dma

对于这三种模式的函数适配在xilinx_dma.c内

image-20231223171824800

对于不同传输类型的DMA(AXI DMA ,CMDA,AXI MCMDA)的传输适配做成了不同的函数。

对于这三种函数在内核文档有详细的描述

vi ./Documentation/driver-api/dmaengine/client.rst +76

赛灵思的发起通讯传输只能调用device_prep_slave_sg或device_prep_dma_cyclic。

3.3. Xlinx DMA Client

描述:DMA的传输在单通道模式下(非SG模式),需要一段连续的物理内存

Linux下使用DMA的流程如下:
image-20231223174916641
如果不使用SG模式的话,只使用一个传输通道,则在提交传输地址时,第三个参数设置为1.

image-20231223173721682

Client端代码需要自己完成,因为赛灵思对Client端好像比较自由?因为不同的方案对Client端的设计不一致,所以没有统一的client端?我也不是特别清楚,但是可以参考wiki xilinx axi DMA用户空间的说明。

参考网址:https://xilinx-wiki.atlassian.net/wiki/spaces/A/pages/18842418/Linux+DMA+From+User+Space

3.4. 相关API参考

static inline void *dma_alloc_coherent(struct device *dev, size_t size,
		dma_addr_t *dma_handle, gfp_t gfp)
/*
    dev:是指向设备结构的指针,用于指定DMA的上下文信息。
    size:是要分配的内存区域的大小(以字节为单位)。
    dma_handle:是一个输出参数,用于获取分配内存区域的DMA物理地址。
    flag:GFP(Get Free Pages)标志,用于内存分配时的行为控制。
*/
    
void sg_init_one(struct scatterlist *sg, const void *buf, unsigned int buflen);
/*
    sg:需要初始化的 scatterlist 已经分配的结构体指针。
    buf: 当前 scatterlist 描述的内存块的虚拟地址。
    buflen: 当前 scatterlist 描述的内存块的大小(字节数)。
*/

dma_cookie_t (*tx_submit)(struct dma_async_tx_descriptor *tx);
/*
这里建议看结构体dma_async_tx_descriptor
*/
struct dma_async_tx_descriptor {
    struct list_head        tx_list;
    struct dmaengine_slave  *chan;
    dma_cookie_t            cookie;
    enum dma_async_tx_type  tx_type;
    enum dma_transfer_direction direction;
    unsigned long           flags;
    void                    *callback_param;
    dma_async_tx_callback   callback;
};
/*
    tx_list:用来将描述符与其他描述符连接在一起,以便进行链式操作。
    chan:指向 DMA 通道的指针,表示异步传输将在该通道上执行。
    cookie:用于标识异步传输操作的唯一 cookie 值。
    tx_type:描述异步传输类型的枚举值,如 memcpy、xor、crc 等。
    direction:描述数据传输方向的枚举值,如从内存到设备、从设备到内存等。
    flags:用来设置一些标志位,以控制传输的特性。
    callback_param:传递给回调函数的参数。
    callback:回调函数,用于在传输完成时通知调用者。
*/

static inline void dma_async_issue_pending(struct dma_chan *chan)
/*
	chan:指定 DMA 通道,用于向 DMA 引擎提交异步传输操作。
*/

3.5. 相关代码参考

编写Client端代码

参考1:

https://www.kernel.org/doc/Documentation/DMA-API.txt

参考2:

https://github.com/Xilinx-Wiki-Projects/software-prototypes

4. 调试手法

我建议从vivado设计完后,先使用vitis的仿真模式在裸机下验证一下。不然在Linux调不通找问题找半天。

  • 23
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: UG585-Zynq-7000-TRM是一份关于Xilinx Zynq-7000 SoC处理器的技术参考手册。Zynq-7000 SoC处理器是一款配备ARM Cortex-A9双核处理器和Xilinx可编程逻辑部分(PL)的可扩展平台,旨在为嵌入式系统开发者提供卓越的灵活性和可编程性。 TRM代表Technical Reference Manual(技术参考手册),其中包含了该处理器的体系结构和功能特性的详细描述,包括处理器内部的各种模块、外设、总线结构等等。该手册的主要目的是为开发者提供全面的指导和支持,从而加速其系统设计和开发应用,减少错误和风险。 UG585-Zynq-7000-TRM手册的内容包括Zynq-7000的基本特征、处理器硬件和软件架构、可编程逻辑PL和PS之间的通信、外设接口和寄存器映射、中断控制等内容。开发者可以根据手册中的详细说明,了解Zynq-7000的构成和功能,从而基于此设计和实现自己的应用。 总之,UG585-Zynq-7000-TRM手册是一份非常重要的技术参考资料,为开发者提供全面的指导和支持,促进了Zynq-7000处理器的应用和拓展,也为未来的嵌入式系统设计提供了参考和借鉴。 ### 回答2: UG585是Xilinx公司发布的Zynq-7000系列技术手册,全称为“Zynq-7000 All Programmable SoC Technical Reference Manual”。Zynq-7000系列是Xilinx公司推出的一款功能强大的FPGA芯片,集成了双核ARM Cortex-A9处理器和可编程逻辑资源,支持高性能中间件、操作系统和外围设备的支持。该手册详细介绍了Zynq-7000系列SoC的架构、功能、性能、测试和验证、软件和硬件开发等方面的知识。他对于学习、使用和开发Zynq-7000 SoC具有非常重要的意义,使得开发人员能够深入了解这个芯片的细节,掌握它的特性和功能,以便更加高效地使用它进行开发。如果你想要学习和使用Zynq-7000 SoC,UG585是一个非常重要的参考文献,值得认真阅读和研究。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值