zcu102_8_AXI_STREAM实现AXI_DMA

本文详细介绍了如何在ZCU102平台上使用AXI_STREAM接口和AXI DMA模块进行数据传输。讨论了AXI_STREAM的时序、AXI DMA的工作原理以及配置PS模块的细节。还提到了AXI4-Stream Data FIFO的使用,自定义数据源IP的创建,并提供了PS编程的示例代码。强调了在设计中需要注意的中断同步、数据包格式和数据量一致性等问题。
摘要由CSDN通过智能技术生成


本文配套源码工程已上传至 https://download.csdn.net/download/botao_li/10907037

AXI_STREAM的时序

AXI_STREAM接口一般用于大规模持续的无地址映射关系的流数据传输

数据从master单向流动至slave,

参考自定义IP生成的示例代码进行说明

master端接口如下:

// Master Stream Ports. TVALID indicates that the master is driving a valid transfer, A transfer takes place when both TVALID and TREADY are asserted. 
output wire  M_AXIS_TVALID,
// TDATA is the primary payload that is used to provide the data that is passing across the interface from the master.
output wire [C_M_AXIS_TDATA_WIDTH-1 : 0] M_AXIS_TDATA,
// TSTRB is the byte qualifier that indicates whether the content of the associated byte of TDATA is processed as a data byte or a position byte.
output wire [(C_M_AXIS_TDATA_WIDTH/8)-1 : 0] M_AXIS_TSTRB,
// TLAST indicates the boundary of a packet.
output wire  M_AXIS_TLAST,
// TREADY indicates that the slave can accept a transfer in the current cycle.
input wire  M_AXIS_TREADY

slave端接口如下:

// Ready to accept data in
output wire  S_AXIS_TREADY,
// Data in
input wire [C_S_AXIS_TDATA_WIDTH-1 : 0] S_AXIS_TDATA,
// Byte qualifier
input wire [(C_S_AXIS_TDATA_WIDTH/8)-1 : 0] S_AXIS_TSTRB,
// Indicates boundary of last packet
input wire  S_AXIS_TLAST,
// Data is in valid
input wire  S_AXIS_TVALID

在master端:

M_AXIS_TDATA, M_AXIS_TSTRB, M_AXIS_TLAST信号全部与M_AXIS_TVALID同步,在M_AXIS_TVALID与M_AXIS_TREADY同时为1的情况下完成M_AXIS_TDATA的一次传输

M_AXIS_TLAST用于指示此次流传输的最后1个M_AXIS_TDATA

在slave端:

在S_AXIS_TREADY和S_AXIS_TVALID都为1时取用数据

S_AXIS_TSTRB用于指示当前S_AXIS_TDATA内的有效字节,S_AXIS_TLAST表示此次流传输结束,根据功能需要执行流传输结束的相应处理

代码实现时,master和slave主要通过tvalid和tready相互配合完成传输,可以在master端先设置tvalid为1等待slave端的tready,也可以slave端将tready设置为1等待master端的tvalid

注意:

  • 由于PS模块的AXI接口是地址映射接口,如果PL需要通过axi_stream与PS进行数据传输,则需要经由dma模块进行axi_stream与axi_full传输协议的相互转化
  • 在AXI术语中,TVALID指示有效的TDATA被正确响应表示一次Burst,多个Burst组成1个Packet,Packet中用TLAST表示最后1个Burst对应的TVALID位置

AXI Direct Memory Access

axi_dma用于实现axi_stream流传输的地址映射

在Block Design中加入axi_dma模块,进行如下图所示的配置
在这里插入图片描述

  • Scatter Gather Engine用于散内存传输,使用连续的虚拟内存地址映射至非连续的物理内存地址,效率较低并且使用更复杂,暂不研究
  • Micro DMA是简化的DMA,暂不研究
  • Width of Buffer Length Register是DMA的32位传输长度寄存器中用于表示一次传输(Transfer)字节数目的位数,上图配置为10,表示调用传输函数一次最大传输的数据量为210-1字节
  • Max Burst Size表示axi_dma模块所有AXI接口可接受的最大的Burst数目,只有小于该值的Burst数目的传输才能被axi_dma模块正确解析

在Block Design中的axi_dma端口如下:
在这里插入图片描述

  • S_AXI_LITE:用于与PS端的AXI Master连接,接收PS的控制指令,执行DMA写入或者DMA接收
  • S_AXIS_S2MM:axi_dma模块的写接口,将数据从PL发送给PS
  • M_AXIS_MM2S:axi_dma模块的读接口,将数据从PS送入PL
  • M_AXI_S2MM:与PS的AXI SLAVE连接,用于PL向PS写入数据
  • M_AXI_MM2S:与PS的AXI SLAVE连接,用于PL读入PS数据
  • s2mm_introut:完成一次数据从PL发送给PS的Transfer后产生的中断信号,连接PS的PL中断接口
  • mm2s_introut:完成一次数据从PS送入PL的Transfer后产生的中断信号,连接PS的PL中断接口

注意:

mm表示memory mapped,即内存映射,代表AXI_FULL协议,对应PS端的AXI接口

s表示stream,代码AXI_STREAM协议

AXIS中的S表示Stream

数据由PL至PS的工作流程:S2MM

  1. PS通过S_AXI_LITE接口送入指令,指令包含目标内存地址,即memory map
  2. S_AXIS_S2MM的s_axis_s2mm_tready信号拉高,允许数据进入axi_dma模块,外部与之连接的MASTER可以通过该tready信号判断写入时机
  3. M_AXI_S2MM按照AXI_FULL的协议,用写指令将数据送给PS模块

数据由PS至PL的工作流程:MM2S

  1. PS通过S_AXI_LITE接口送入指令,指令包含数据源内存地址,即memory map
  2. M_AXI_MM2S按照AXI_FULL的协议,用读指令从PS模块中读出数据
  3. M_AXIS_MM2S将m_axis_mm2s_tvalid信号拉高,表示有数据可以送出,外部与之连接的SLAVE可以通过该tvalid信号判断读出时机

AXI4-Stream Data FIFO

axi_dma不具备数据缓冲的能力,高速数据传输时PL很难完全配合PS发送DMA指令的时机,因此需要使用FIFO进行数据缓冲

本实验对PL至PS的数据进行FIFO缓冲

在Block Design中加入AXI4-Stream Data FIFO模块,进行如下图所示的配置
在这里插入图片描述

  • Enabele Packet Mode表示按Packet传输,见AXI_STREAM的时序说明
  • Signal Porperties与FIFO出入数据的AXI接口对应就可以了
  • TID,TDEST,TUSER暂不考虑

在Block Design中的模块端口如下:
在这里插入图片描述

  • S_AXIS:数据写接口,FIFO可写入的情况下s_axis_tready信号会拉高,只要对端MASTER送入s_axis_tvalid高电平即可以进行数据写入
  • M_AXIS:数据读接口,FIFO数据可读出的情况下m_axis_tvalid信号会拉高,只要对端SLAVE送入m_axis_tready高电平即可进行数据读取
  • 读接口Packet中的Burst数目与写接口保持一致

自定义数据源IP

在Vivado的Tools菜单,选择Create and Package New IP,建立Create a new AXI peripheral
在这里插入图片描述

根据之前文档的说明建立AXI STREAM协议的Master接口模块

代码如下:注意第39行及其注释,见[Block Design中的说明](#完整Block Design)

`timescale 1 ns / 1 ps

	module data_src_v1_0_M00_AXIS #
	(
		// Users to add parameters here

		// User parameters ends
		// Do not modify the parameters beyond this line

		// Width of S_AXIS address bus. The slave accepts the read and write addresses of width C_M_AXIS_TDATA_WIDTH.
		parameter integer C_M_AXIS_TDATA_WIDTH	= 32,
		// Start count is the number of clock cycles the master will wait before initiating/issuing any transaction.
		parameter integer C_M_START_COUNT	= 32
	)
	(
		// Users to add ports here

		// User ports ends
		// Do not modify the ports beyond this line

		// Global ports
		input wire  M_AXIS_ACLK,
		// 
		input wire  M_AXIS_ARESETN,
		// Master Stream Ports. TVALID indicates that the master is driving a valid transfer, A transfer takes place when both TVALID and TREADY are asserted. 
		output wire  M_AXIS_TVALID,
		// TDATA is the primary payload that is used to provide the data that is passing across the interface from the master.
		output wire [C_M_AXIS_TDATA_WIDTH-1 : 0] M_AXIS_TDATA,
		// TSTRB is the byte qualifier that indicates whether the content of the associated byte of TDATA is processed as a data byte or a position byte.
		output wire [(C_M_AXIS_TDATA_WIDTH/8)-1 : 0] M_AXIS_TSTRB,
		// TLAST indicates the boundary of a packet.
		output wire  M_AXIS_TLAST,
		// TREADY indicates that the slave can accept a transfer in the current cycle.
		input wire  M_AXIS_TREADY
	);
	
	//BURST数目,必须与axi_dma模块的s2mm一次transfer的数据量大小一致,目前在SDK中设置为128字节
	parameter BURST_NUM = 8'd32;
	
	//BUSRT数目减1,用于生成tlast
	//注意不能用计算公式用于比较判断,仿真正确,但是在Block Design中可能会自动扩位导致出错
	wire [7:0] BURST_NUM_M1;
	assign BURST_NUM_M1 = (BURST_NUM-8'd1);
	
	//导入时钟
	wire clk;
	assign clk = M_AXIS_ACLK;
	
	//导入复位
	wire rstn;
	assign rstn = M_AXIS_ARESETN;
	
	//定义输出端口
	reg tvalid = 1'b0;
	reg [31:0] tdata = 32'd0;
	reg tlast = 1'b0;
	
	assign M_AXIS_TVALID = tvalid;
	assign M_AXIS_TDATA = tdata;
	assign M_AXIS_TLAST = tlast;
	
	assign M_AXIS_TSTRB = 4'b1111;
	
	//burst计数器
	reg [7:0] cnt_burst = 8'd1;
	
	//测试状态机
	reg state = 1'b0;
	
	always @(posedge clk) begin
		if (rstn == 1'b0) begin
			state <= 1'b0;
		end
		else begin
			case (state)
				1'b0: begin
					if (cnt_burst == 8'd10) begin
						state <= 1;
					end
					else begin
						state <= state;
					end
				end
				
				1'b1: begin
					if ({tvalid, M_AXIS_TREADY, cnt_burst} == {1'b1, 1'b1, BURST_NUM}) begin
						state <= 1'b0;
					end
					else begin
						state <= state;
					end
				end
			endcase
		end
	end
	
	//计数器用于控制状态机转移
	always @(posedge clk) begin
		if (rstn == 1'b0) begin
			cnt_burst <= 8'd1;
		end
		else begin
			case (state)
				1'b0: begin
					if (cnt_burst == 8'd10) begin
						cnt_burst <= 8'd1;
					end
					else begin
						cnt_burst <= cnt_burst+8'd1;
					end
				end
				
				1'b1: begin
					if ({tvalid, M_AXIS_TREADY} == 2'b11) begin
						if (cnt_burst == BURST_NUM) begin
							//一次流传输最后1个burst被slave接收
							cnt_burst <= 8'd1;
						end
						else begin
							cnt_burst <= cnt_burst+8'd1;
						end
					end
					else begin
						cnt_burst <= cnt_burst;
					end
				end
				
				default: begin
					cnt_burst <= 8'd1;
				end
			endcase
		end
	end
	
	//输出接口
	always @(posedge clk) begin
		if (rstn == 1'b0) begin
			tvalid <= 1'b0;
			tdata <= 32'd0;
			tlast <= 1'b0;
		end
		else begin
			case (state)
				1'b1: begin
					if ({tvalid, M_AXIS_TREADY, cnt_burst} == {1'b1, 1'b1, BURST_NUM}) begin
						//与状态转移同步
						tvalid <= 1'b0;
					end
					else begin
						tvalid <= 1'b1;
					end
					
					//slave确认接收数据后,数值增1
					if ({tvalid, M_AXIS_TREADY} == 2'b11) begin
						tdata <= tdata+32'd1;
					end
					else begin
						tdata <= tdata;
					end
					
					//倒数第2个burst被接收
					if ({tlast, tvalid, M_AXIS_TREADY, cnt_burst} == {1'b0, 1'b1, 1'b1, BURST_NUM_M1}) begin
						tlast <= 1'b1;
					end
					else if ({tlast, tvalid, M_AXIS_TREADY, cnt_burst} == {1'b1, 1'b1, 1'b1, BURST_NUM}) begin
						//与状态转移同步
						tlast <= 1'b0;
					end
					else begin
						//保持
						tlast <= tlast;
					end
				end
				
				default: begin
					tvalid <= 1'b0;
					tlast <= 1'b0;
					tdata <= tdata;
				end
			endcase
		end
	end
	

	endmodule

关于上述代码有以下几点说明:

  • module关键字后接#的方式声明parameter可以让parameter被Block Design识别,在Block Design双击模块可以对parameter进行数值设置
  • 无论输入输出端口都必须加上wire关键字声明
  • 上述代码第163行的cnt_burst与BURST_NUM_M1的比较不能在比较代码中用计算公式
    • 使用(BURST_NUM-8’d1)进行比较࿰
  • 34
    点赞
  • 171
    收藏
    觉得还不错? 一键收藏
  • 34
    评论
评论 34
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值