ZYNQ基础----通过AXI4接口向内存中写入数据

AXI4 写相关通道

  在前面的AXI接口部分介绍了有关AXI接口的通道和时序。在这一篇博客实现一个AXI4的接口,用来向内存中写入数据。

在写地址通道,主要进行传输 AXI 的 master 向 slave 中写入数据时的地址。
在写数据通道,主要进行传输 AXI 的 master 向 slave 中写入的数据。
在写响应通道,主要进行传输 AXI 的 master 向 slave 中写入数据时的响应。

  下图是AXI接口的相关通道的连接方式。
ZYNQ基础----通过AXI4接口向内存中写入数据_第1张图片
  AXI接口写时序
ZYNQ基础----通过AXI4接口向内存中写入数据_第2张图片

时序设计

  下面是我设计的一个AXI接口的写时序图,
ZYNQ基础----通过AXI4接口向内存中写入数据_第3张图片

AXI接口IP设计

  在Vivado工具中,创建一个AXI接口的外设。选中AXI4-FULL接口的类型,就会创建出一个实例IP,对这个IP内部的代码进行修改,就可以实现我们所需要设计的功能,使用这种方式来创建IP,主要是为了来使用这个AXI-Full的接口定义。
ZYNQ基础----通过AXI4接口向内存中写入数据_第4张图片
  在示例IP内部,将其实现的逻辑删除,然后实现自己的代码就OK了。

代码实现思路

  在IP内部,例化一个FIFO,FIFO用于缓冲从外部接收到的数据,当FIFO中存在足够的数据时,启动向AXI总线上写数据。其中部分代码如下,指示实现向AXI总线上写数据的代码(Verilog代码长长长)。

//==========================================================
//用户自定义输入输出端口
input 	wire  			pixel_clk 	,//输入像素时钟
input 	wire 			vsync 		,//场同步信号
input 	wire 			data_vld 	,//数据有效信号
input 	wire 	[23:0]	pixel_data	,//像素数据
//==========================================================

//==========================================================
//用户参数定义
parameter THRESHOLD = 128 -2 ;
parameter BURST_MAX = 256 - 1;//突发读写长度
parameter ADD_ADDR  = 2048 	;//突发写数据字节地址增量
parameter FIRST_FRAME = (640 * 512) * 16/8 - ADD_ADDR ;
parameter SECOND_FRAME = (640 * 512) * 2 * 16/8 - ADD_ADDR ;
//==========================================================

reg 	[1:0]	vsync_dd 		;
reg 			sync_flag 		;
reg 			work_on_flag 	;

reg 	[2:0]	state 			;
reg 			wr_start 		;
reg 			burst_start 	;// 开始突发写数据

wire 			wr_en 			;//write fifo data enable
wire 	[31:0]	din 			;//data into fifo 
wire 			full 			;//fifo full 
wire 			empty 			;//fifo empty
wire 			rd_en 			;//read fifo data enable
wire 	[63:0]	dout 			;//data from fifo
wire 	[10:0]	rd_data_count 	;//当前FIFO读端口的数据个数

reg 	[8:0] 	cnt_burst 		;
wire 			add_cnt_burst 	;
wire 			end_cnt_burst 	;

//----------------work_on_flag------------------
// 检测到init_txn_pluse上升沿后才能开始工作
always @(posedge M_AXI_ACLK)begin                                                                          
    if (M_AXI_ARESETN == 0 )begin
    	work_on_flag <= 1'b0;
    end        
    else if (init_txn_pulse == 1'b1) begin
    	work_on_flag <= 1'b1;
    end                                                                                                                                           
end  

//----------------vsync_dd------------------
// 对输入的vsync进行延拍,用于检测同步信号上升沿,以便获取一帧完整的图像
always @(posedge pixel_clk)begin                                                                          
    if (M_AXI_ARESETN == 0 )begin
    	vsync_dd <= 'd0;
    end                                                                               
    else begin  
    	vsync_dd <= {vsync_dd[0],vsync};
    end                                                                      
end   

//----------------sync_flag------------------
// 当前已经同步完成,从现在开始采集,可以得到完整的图像	
always @(posedge pixel_clk)begin                                                                          
    if (M_AXI_ARESETN == 0 )begin
    	sync_flag <= 1'b0;
    end                                                                               
    else if (!vsync_dd[1] && vsync_dd[0]) begin  
    	sync_flag <= 1'b1;
    end                                                                      
end   

// 写入FIFO的数据,需要扩充
assign  din = {8'd0,pixel_data};

// 写FIFO使能,当前系统正常工作,且数据有效
assign  wr_en = sync_flag & data_vld & work_on_flag;

wr_fifo inst_fifo (
 	.wr_clk(pixel_clk),                	// input wire wr_clk
 	.rd_clk(M_AXI_ACLK),                // input wire rd_clk
 	.din(din),                      	// input wire [31 : 0] din
 	.wr_en(wr_en),                  	// input wire wr_en
 	.rd_en(rd_en),                  	// input wire rd_en
 	.dout(dout),                    	// output wire [63 : 0] dout
 	.full(full),                    	// output wire full
 	.empty(empty),                  	// output wire empty
 	.rd_data_count(rd_data_count)  		// output wire [9 : 0] rd_data_count
);

//----------------wr_start------------------
// 当前总线处于空闲状态,且FIFO中有足够的数据时,开始向AXI总线上写数据
always @(posedge M_AXI_ACLK)begin                                                                          
    if (M_AXI_ARESETN == 0 )begin
    	wr_start <= 1'b0;
    end                  
    else if (rd_data_count >= THRESHOLD && axi_awvalid == 1'b0 && axi_wvalid == 1'b0) begin
    	wr_start <= 1'b1;
    end                                                             
    else begin  
    	wr_start <= 1'b0;
    end                                                                      
end   

//----------------axi_awvalid------------------
// axi 写地址有效信号	
always @(posedge M_AXI_ACLK)begin                                                                          
    if (M_AXI_ARESETN == 0 )begin
    	axi_awvalid <= 1'b0;
    end 
    // 当接收到写地址的响应信号后,写地址有效信不使能                   
    else if (axi_awvalid == 1'b1 && M_AXI_AWREADY == 1'b1) begin
    	axi_awvalid <= 1'b0;
    end
    //当接收到写开始信号,并且当前的axi总线处于空闲状态
    else if (wr_start && axi_awvalid == 1'b0 && axi_wvalid == 1'b0 ) begin
    	axi_awvalid <= 1'b1;
    end                                                                                                                               
end  

//----------------axi_awaddr------------------ 
// axi 总线上的地址
always @(posedge M_AXI_ACLK)begin                                                                          
    if (M_AXI_ARESETN == 0 )begin
    	axi_awaddr <= 'd0;
    end                   
    // 每次突发写数据后地址增加(256*64)/8 = 2048
    else if (axi_awvalid == 1'b1 && M_AXI_AWREADY == 1'b1) begin
    	// 地址增加至一幅图像的最大值
    	if(axi_awaddr == STOP_ADDR)begin
    		axi_awaddr <= 'd0;
    	end
    	// 地址每次偏移一定量
    	else begin
    		axi_awaddr <= axi_awaddr + ADD_ADDR;
    	end
    end                                                                                                                                 
end   

//----------------axi_wvalid------------------
// 写数据有效信号
always @(posedge M_AXI_ACLK)begin                                                                          
    if (M_AXI_ARESETN == 0 )begin
    	axi_wvalid <= 1'b0;
    end     
    // 一次突发结束,写数据无效
    else if (end_cnt_burst == 1'b1) begin
    	axi_wvalid <= 1'b0;
    end                      
    // 给出写地址指令后,数据有效                                                    
    else if (axi_awvalid == 1'b1 && M_AXI_AWREADY == 1'b1) begin  
    	axi_wvalid <= 1'b1;
    end                                                                      
end   

//---------------cnt_burst-------------------
always @(posedge M_AXI_ACLK) begin
if (M_AXI_ARESETN == 1'b0) begin
	cnt_burst <= 'd0;
end
else if (add_cnt_burst) begin
	if(end_cnt_burst)
		cnt_burst <= 'd0;
	else
		cnt_burst <= cnt_burst + 1'b1;
end
end

// 当前数据有效,并且axi总线可以接收数据
assign add_cnt_burst = axi_wvalid && M_AXI_WREADY;
// 计数到最大值
assign end_cnt_burst = add_cnt_burst &&	cnt_burst == BURST_MAX;

//----------------rd_en------------------
assign  rd_en = axi_wvalid && M_AXI_WREADY;

//----------------axi_wdata------------------
// axi_wdata 就是从FIFO中读出的数据
always @(*)begin                                                                          
	axi_wdata = dout;                                                                     
end   

//----------------axi_wlast------------------
//指示当前数据是最后一个
always @(*)begin                                                                                         
	if (end_cnt_burst) begin
    	axi_wlast = 1'b1;
    end                                                            
    else begin  
    	axi_wlast = 1'b0;
    end                                                                      
end   


//----------------axi_bready------------------
// 写响应信道	
always @(posedge M_AXI_ACLK)begin                                                                          
    if (M_AXI_ARESETN == 0 )begin
    	axi_bready <= 1'b0;
    end                                                                            
    else begin  
    	axi_bready <= 1'b1;
    end                                                                      
end   

你可能感兴趣的:(ZYNQ)