AXI_DMA协议——手写握手协议读取DDR中数据


前言

我们要做的是由 PL(FPGA)从PS(ARM)端DDR处读数据,使用zynq7平台,读数据采用AXI-full接口,握手协议以及异步FIFO处理均设涉及。有助于您了解AXI接口、AXI_DMA读数据操作、异步FIFO的使用。

一、AXI接口类型有哪些?有什么区别?

AXI 全称是Advance eXtensible Interface 的缩写,是一种面向高性能、高带宽、低延迟的片内总线。主要分为三个类型接口:AXI-Lite 、AXI_Full、AXI-Stream。(注AXI3 AXI4 只是AXI Full的不同版本,按照我这样分类你就不会迷茫)

AXI-Lite : 单词Lite指代轻量化、精简化,所以AXI-Lite接口是一个轻量级、结构简单的接口。适用于传输小批量的数据,可以进行数据和地址的传输。

AXI_Full :单词Full指满的,完整的,所以AXI_Full接口是一个标准的接口,适合传输大量的数据,主要用于访问存储单元,支持突发模式,可以进行数据和地址的传输。

AXI-Stream :单词Stream指的是串、数据流,所以AXI-Stream接口是用于传输高速数据,由于不需要传输地址,使得AXI-Stream可以一直读或者写,适用于视频、高速AD\DA 、DMA等场合。

在这里插入图1图片描述

                                   图1 :创建AXI接口时的三个接口类型的选项

二、AXI握手协议

首先说明Vivado有AXI_DMA ip核,该ip支持上述三种接口的读写,支持多数据位宽,应用该ip核需要配置相关参数。官方ip核在传输一次突发数据之后需要PS端重新启动ip核的读写操作,在传输大量连续数据时,会力不从心,我们通过创建一个AXI_Full接口的ip(见图1),在这个基础上修改里面的传输代码,使得新的模块能重复读取某一地区的突发数据,不需要PS端一直给与操作信号。(我们所创的ip是一个面向AXI_Full接口的数据传输模块,然后我们在其基础上重新写握手协议和数据读取操作,而官方AXI_DMA ip核是一个拥有灵活配置的、适用于DMA读写的ip。另外需要说明的是,无论我们怎么去修改,握手协议都是一样的。)

数据通道和信号:该协议有5个通道,写地址通道、写数据通道、写回应通道、读地址通道、读数据通道,每一个通道又有好几种信号。大家可以看到下面作者的信号介绍(那么多信号,我们如何去把握,大家目前只需要重点关注我说到的信号,至于以后,或许我和大家对这些信号都会有新的认识)。(AXI总线协议参考这位作者https://blog.csdn.net/ivy_reny/article/details/56274238)

VALID/READY 机制:(这里的VALID和READY代指我们看到的各种valid 和ready信号,待会我会介绍如何分清这些信号)
VALID和READY 互不依赖,两者的先后顺序并无要求,记住,只有当VALID和READY 都为高时才能传输数据或者地址。比如主机要在从机处读数据,则当主机的axi_arvalid 和从机M_AXI_ARREADY都拉高才能将要读的地址告诉从机,告诉地址之后就是从机将该地址的数据给主机,这时只有从机的M_AXI_RVALID和主机的axi_rready都为高才能将数据传输给主机。(具体握手协议参考这位作者https://zhuanlan.zhihu.com/p/44766356)

分清每一个信号 :我觉得这一块是阻碍大家学习的一个麻烦点,首先我们先不去管字母的大小写,这么记,信号名称前面带 a就是有关地址(address)的信号,信号名称前面不带a就是关于数据信号,接下来一位就是看是否带w,带w就是写(write)信号,带r就是读(read)信号,后面再和ready、valid、address、data自由组合。例如:axi_arvalid,就是地址的读有效信号,axi_awaddr就是写地址的地址数据,M_AXI_RVALID就是读数据有效信号,M_AXI_WREADY就是写数据反馈信号。
读和写都是相对的,不需要记住某个信号到底是主机对从机还是反过来,参照第一位博主的信号解释应该没啥问题,后面会有实例,带入到实例里面大家就会豁然开朗。

三、实例操作

1.总体框图

在这里插入图片描述

                                         图2  总体框图 

步骤如下:
1 PS端发送Init_txn_pulse信号给axi_dma_rd(属于PL)模块,表示DDR中已经放好了需要的数据,可以开始读取数据了。
2 接着axi_dma_rd信号和右边的“PS侧DDR”模块进行握手并读取数据。
3 axi_dma_rd读取到的数据送入FIFO中。
4 将FIFO中的数据取出来送入我们需要的模块(可以是任何要该数据的模块)

2.握手并读取数据

在这里插入图片描述

                                              图3 :详细信号图

图3中的所有信号都是写在axi_dma_rd.v这个verilog文件里面的,以下端口是由我们自己添加得到,其他信号都是创建axi_dma_rd这个ip时系统自带的,例如axi_arvalid信号就是自带的,另外我们去仔细查看系统自带的信号是会发现有M_AXI_ARVALID,其实内部是 assign M_AXI_ARVALID = axi_arvalid ,可以认为操作axi_arvalid 这样的寄存器就行,但是对于M_AXI_ARREADY这是一个输入信号,我们就直接拿来使用,可以简单通过字母大小写区分是输入还是输出。
代码如下(示例):

		input  wire  [31:0] end_addra,   //传输结束地址
		input  wire         data_clk,  //输入的另外一个时钟域
		input  wire         ap_rst_n,   //另外一个时钟域的复位信号
		input  wire         data_tready,  //其他模块告诉axi_dma_rd,已经准备好读取数据
		output wire  [31:0] data_tdata,   //将从DDR得到的数据传输给其他模块
		output wire         data_tvalid,  //数据有效信号

在这里插入图片描述

                                                  图4 :信号流程图

信号流程详细分析 :以下分析结合图4理解(刚开始看这个图确实很烦,但请耐心跟着我走一遍)。
AXI_CLK是axi_dma_rd的系统时钟,Init_txn_pulse是PS端给的脉冲信号,当Init_txn_pulse拉高代表axi_dma_rd可以去读取DDR中信号了。

Init_txn_pulse拉高后,我们拉高内部寄存器axi_read_cycle(由我们自己定义的),表示可以去读DDR数据,此后不再拉低

检查wr_data_count里面的数据是否小于2(小于2判断FIFO为空),这时我们就可以开始真正去读DDR(如果FIFO不为空,我们读入的数据放到FIFO里面,再从FIFO里面读出就会和之前里面的数据弄混淆)

当满足 wr_data_count<2 && axi_arvalid ==1’b0 && M_AXI_ARREADY == 1‘b0 && axi_read_cycle == 1’b1 时,我们拉高axi_arvalid 信号,告诉从机现在主机地址是有效的。当axi_arvalid 和M_AXI_ARREADY 都为1,就拉低axi_arvalid ,因为我们设置一次突发长度是256,所以每传输一次突发拉高一次axi_arvalid 和M_AXI_ARREADY信号就行,只需要传输一次初始地址,数据会自动填充在地址上。

M_AXI_ARREADY是从机给主机的输入信号,表示可以接收地址,当axi_arvalid 和M_AXI_ARREADY同时为高时就传输一个地址给从机地址

axi_araddr 传输地址,由图4可以看到每一次传输地址都会加1024,这里注意地址是按照字节来加,我已经规定我按照最大突发长度256进行传输,我每一个数据是16位,就是4字节,4x256=1024,就是每次突发传输,地址加1024,地址清零是在end_addra - 'd1024 ,比如我有5次突发,end_addra就是1024x5=5120.因为我第一次加1024其实是对应第二次传输的起始地址(第一次传输起始地址为0),第二次加1024对应第三次起始地址,第三次加1024对应第4次起始地址,第四次加1024对应第五次起始地址,第五次我就要清零,此时在第四次已经得到了第五次的起始地址,我的数据来了之后往里面填充即可,而此时地址已经加了4次end_addra = 0+1024*4=end_addra-1024

传输完地址之后就是传输数据(针对每一次突发,先传地址,并不是5次地址都传输完才传数据),M_AXI_RVALID由从机输入,表示已经准备好数据,axi_rready由主机发出,表示能接收数据。(可以看到握手信号都是带有r,表示主机在读数据,虽然传输数据时,是由从机将数据给主机,但是我们站在主机的角度始终是在读取数据,所以都是带r)M_AXI_RDATA这个接口会接收到数据。

burst_cnt用来计数已经传输率多少个数据 ,M_AXI_RLAST在传输255个数据后就拉高。

3.异步FIFO的使用

代码如下(示例):

    asfifo_w32x512_r32x512 asfifo_w32x512_r32x512_inst (
  .wr_clk(M_AXI_ACLK),                // 输入的主时钟
  .rd_clk(data_clk),                // 输入的其他模块的时钟
  .din(wr_fifo_data),                      // 数据输入FIFO
  .wr_en(wr_fifo_en),                  // 输入数据使能
  .rd_en(rd_fifo_en),                  //读出数据使能
  .dout(rd_fifo_data),                    // 读出数据
  .full(full),                    // FIFO满信号,没有涉及
  .empty(empty),                  // FIFO空信号,没有涉及
  .rd_data_count(rd_data_count),  //读出数据时计数
  .wr_data_count(wr_data_count)  // 写入数据时计数
);
assign wr_fifo_en = axi_rready & M_AXI_RVALID ;  //当数据的valid和ready都有效就使能
assign wr_fifo_data = M_AXI_RDATA ;  //将从DDR读出的数据放入FIFO中

assign rd_fifo_en = data_tvalid&data_tready ; //当data_tready 和data_tvalid信号都有效就使能
assign data_tdata = rd_fifo_data ;//将FIFO里面的数据取出

总结

主要通过这次实例学习到握手协议的具体操作,已及AXI_DMA是如何传输数据。
  • 13
    点赞
  • 69
    收藏
    觉得还不错? 一键收藏
  • 5
    评论
FPGA(现场可编程门阵列)是一种灵活可编程的硬件平台,可以用于实现各种不同的电路功能。而AXI(Advanced eXtensible Interface)是一种高性能、低功耗的总线接口协议,用于连接FPGA与外部设备,如DDR3(双倍数据速率3代)内存。 要通过AXI读取DDR3内存数据,首先需要在FPGA上实例化AXI接口和DDR3控制器模块。AXI接口模块负责与外部设备通信,而DDR3控制器模块则负责管理DDR3内存存取操作。 在设计,需要按照AXI协议规范进行接口的连接和配置。AXI协议定义了操作的时序和数据传输约束。通过连接AXI接口与DDR3控制器,FPGA可以通过AXI总线发送读取指令到DDR3内存,然后读取数据返回。 具体而言,通过AXI读取DDR3的操作步骤如下: 1. 配置AXI接口和DDR3控制器模块,并确保其正确连接。 2. 在FPGA编写相应的代码,按照AXI协议要求构建读取指令。 3. 将读取指令通过AXI接口发送到DDR3控制器模块。 4. DDR3控制器模块接收到读取指令后,根据指令的地址信息,从DDR3内存读取相应数据。 5. 读取数据通过AXI接口返回给FPGA,供后续处理使用。 需要注意的是,AXIDDR3之间的通信速度和性能受到FPGA资源、时钟频率、数据宽度等因素的影响。因此,在设计需要根据实际情况进行综合考虑,以保证数据的准确读取和传输。同时,还需要确保AXI接口和DDR3控制器模块的接口匹配和正确配置,以确保数据的正确传输和存取。
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值