ZYNQ基础----通过AXI4接口从内存中读出数据

AXI接口读时序

  在前面的博客中,介绍了AXI接口的基础的一些概念。但是并没有具体实现的例子,今天就通过一个AXI4接口的读时序,来完成从内存中读出数据的这么一个操作。AXI4接口的读时序如下图所示,首先给出读取的内存地址,然后将数据从内存中读出。
ZYNQ基础----通过AXI4接口从内存中读出数据_第1张图片

创建AXI4 IP

  在Vivado软件中自定义一个AXI4接口的IP。
ZYNQ基础----通过AXI4接口从内存中读出数据_第2张图片
  设置接口模式为主机模式,接口类型为AXI4类型,这里接口类型还有AXI_LITE和AXI_Stream类型。
ZYNQ基础----通过AXI4接口从内存中读出数据_第3张图片
  进入到IP编辑界面可以看到该IP的文件结构如下,若要对IP的功能尽心修改,只需要在这两个文件中进行修改即可。进入到子模块中,将实例代码中的功能代码删除,然后自己实现一个AXI4 IP内部的功能。
ZYNQ基础----通过AXI4接口从内存中读出数据_第4张图片
  在内部实现如下功能:就是将在FIFO内部的数据少于一个门限值时,启动一次突发读操作,将内存中的数据读出到FIFO中。一次突发读写的数据个数为256。
ZYNQ基础----通过AXI4接口从内存中读出数据_第5张图片

关键部分代码如下

	//一些参数的定义
	parameter 	ADD_ADDR = 256 * 64/8;
	parameter 	STOP_ADDR = (1024 * 768) * 32/8 - ADD_ADDR;
	parameter 	THRESHOLD = 1026;
	parameter 	BURST_MAX = 256 - 1;
		
	reg 			rd_start 		;//读起始信号
	wire 			wr_en 			;//写FIFO使能
	wire 			full 			;
	wire 			empty 			;
	wire 	[32:0] 	dout 			;//从FIFO中读出的数据
	wire 	[11:0] 	rd_data_count 	;
	wire 	[10:0] 	wr_data_count 	;
	reg 			work_on_flag 	;//复位结束信号
	reg 	[10:0] 	cnt_burst 		;//计数突发长度
	wire 			add_cnt_burst 	;
	wire 			end_cnt_burst 	;

	//--------------------
	//Write Address Channel
	//--------------------
//----------------work_on_flag------------------
	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  

//----------------wr_en------------------ 
assign wr_en = work_on_flag & M_AXI_RVALID & axi_rready;

//----------------rd_start------------------
// 读起始信号
	always @(posedge M_AXI_ACLK)begin                                                                          
	    if (M_AXI_ARESETN == 0 )begin
	    	rd_start <= 1'b0;
	    end                  
	    //FIFO中不足两行数据,且当前处于空闲状态,开始读数据
	    else if (work_on_flag && (wr_data_count <= THRESHOLD) && axi_arvalid == 1'b0 && axi_rready == 1'b0) begin
	    	rd_start <= 1'b1;
	    end                                                             
	    else begin  
	    	rd_start <= 1'b0;
	    end                                                                      
	end   

//----------------axi_arvalid------------------
	always @(posedge M_AXI_ACLK)begin                                                                          
	    if (M_AXI_ARESETN == 0 )begin
	    	axi_arvalid <= 1'b0;
	    end                     
	    // 当前已经发起了一次读操作
	    else if (axi_arvalid == 1'b1 && M_AXI_ARREADY == 1'b1) begin
	    	axi_arvalid <= 1'b0;
	    end
	    // 接收到读起始信号,且当前处于空闲状态
	    else if (rd_start == 1'b1 && axi_arvalid == 1'b0 && axi_rready == 1'b0) begin
	    	axi_arvalid <= 1'b1;
	    end                                                                                                                              
	end   

//----------------axi_araddr------------------
//读地址
	always @(posedge M_AXI_ACLK)begin                                                                          
	    if (M_AXI_ARESETN == 0 )begin
	    	axi_araddr <= 'd0;
	    end             
	    // 每次发起读数据,地址偏移相同的长度      
	    else if (axi_arvalid == 1'b1 && M_AXI_ARREADY == 1'b1) begin
	    	//计数到图像最大值,清零
	    	if (axi_araddr == STOP_ADDR) begin
	    		axi_araddr <= 'd0;
	    	end
	    	else begin
	    		axi_araddr <= axi_araddr + ADD_ADDR;
	    	end
	    end                                                                                                                                 
	end   

//----------------axi_rready------------------
	always @(posedge M_AXI_ACLK)begin                                                                          
	    if (M_AXI_ARESETN == 0 )begin
	    	axi_rready <= 1'b0;
	    end       
	    // 一次突发结束             
	    else if (end_cnt_burst == 1'b1) begin
	    	axi_rready <= 1'b0;
	    end
	    // 一次读开始,接收准备好信号有效
	    else if (axi_arvalid == 1'b1 && M_AXI_ARREADY == 1'b1) begin
	    	axi_rready <= 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
// 当前处于传输信号时,计数器加一
assign add_cnt_burst = 	axi_rready && M_AXI_RVALID;
//计数器计数到最大值
assign end_cnt_burst = add_cnt_burst &&	cnt_burst == BURST_MAX;


rd_fifo inst_rd_fifo (
  	.wr_clk(M_AXI_ACLK),                // input wire wr_clk
  	.rd_clk(hdmi_tx_clk),                // input wire rd_clk
  	.din(M_AXI_RDATA),                      // input wire [63 : 0] din
  	.wr_en(wr_en),                  // input wire wr_en
  	.rd_en(rd_en),                  // input wire rd_en
  	.dout(dout),                    // output wire [31 : 0] dout
  	.full(full),                    // output wire full
  	.empty(empty),                  // output wire empty
  	.rd_data_count(rd_data_count),  // output wire [11 : 0] rd_data_count
    .wr_data_count(wr_data_count)   // output wire [10 : 0] wr_data_count
);

显示模块

  由于想要将内存中的数据直观地显示出来,由上位机发送数据给FPGA缓存到内存中,然后由前面的AXI读数据模块将数据从内存中读出,并且将读出的数据给到VGA模块使用。在VGA模块后链接一个rgb2dvi的IP将VGA时序信号进行编码转换为tmds通道信号,最终在HDMI上能够显示图像。

module hdmi_out(
	input	wire			  	rst    			,//时钟复位
	input	wire			  	hdmi_tx_clk    	,//像素时钟
	output	reg			      	hdmi_tx_de      ,//输出数据有效信号
	output	reg			      	hdmi_tx_hs      ,//行同步信号
	output	reg			      	hdmi_tx_vs      ,//场同步信号
	output	reg      [23:0]	  	hdmi_td         ,//输出图像值
	
	output 	wire 				rd_en			,//从buffer中读取数据请求
	input	wire	 [23:0]		rd_data 		 //从buffer中读取的数据 	
);

//------------------------------------------------------------
//1024*768@60Hz
//------------------------------------------------------------
parameter       H_TOTAL  = 1344    	;//一行总共需要计数的值
parameter       H_SYNC  = 136       	;//行同步计数值
parameter       H_START  = 296     	;//行图像数据有效开始计数值
parameter       H_END  = 1320      	;//行图像数据有效结束计数值
parameter       V_TOTAL  = 806    	;//场总共需要计数的值
parameter       V_SYNC  = 6        	;//场同步计数值
parameter       V_START  = 35      	;//场图像数据有效开始计数值
parameter       V_END  = 803     ;//场图像数据有效结束计数值
parameter       SQUARE_X    =   256 ;//方块的宽度
parameter       SQUARE_Y    =   256 ;//方块的长度
parameter       SCREEN_X    =   1024;//屏幕水平长度
parameter       SCREEN_Y    =   768;//屏幕垂直长度


//=======================================================
//  internal Signal declarations
//=======================================================
reg [12:0]	cnt_h;//行计数器
reg [12:0]	cnt_v;//场计数器
reg [11:0]	x    ;//方块左上角横坐标
reg 		flag_x;//方块水平移动方向指示信号
reg [11:0]	y    ;//方块左上角纵坐标
reg 		flag_y;//方块垂直移动方向指示信号
wire locked1;       //时钟稳定信号

 //行计数器
always @(posedge hdmi_tx_clk ) begin
	if (rst==1'b1) begin
		cnt_h <= 'd0;
	end
	else if (cnt_h == H_TOTAL - 1 ) begin//计数到最大值,清零
		cnt_h <= 'd0;
	end
	else if(cnt_h != H_TOTAL - 1 ) begin//还没有计数到最大值,每个时钟周期加一
		cnt_h <= cnt_h + 1'b1;
	end
end

//场计数器
always @(posedge hdmi_tx_clk ) begin
	if (rst==1'b1) begin
		cnt_v <='d0;
	end
	else if (cnt_v == V_TOTAL - 1 && cnt_h == H_TOTAL - 1) begin//场计数器计数到最大值,清零(一帧结束)
		cnt_v <= 'd0;
	end
	else if(cnt_h == H_TOTAL - 1) begin//一行扫描结束,场计数器加一
		cnt_v <= cnt_v + 1'b1;
	end
end

//行同步信号
always @(posedge hdmi_tx_clk ) begin
	if (rst==1'b1) begin
		hdmi_tx_hs <= 1'b0;
	end
	else if (cnt_h == H_TOTAL - 1) begin
		hdmi_tx_hs <= 1'b1;
	end
	else if (cnt_h == H_SYNC - 1) begin
		hdmi_tx_hs <= 1'b0;
	end
end

//场同步信号
always @(posedge hdmi_tx_clk ) begin
	if (rst==1'b1) begin
		hdmi_tx_vs <= 1'b0;
	end
	else if (cnt_v == V_TOTAL - 1 && cnt_h == H_TOTAL - 1) begin
		hdmi_tx_vs <= 1'b1;
	end 
	else if (cnt_v == V_SYNC - 1 && cnt_h == H_TOTAL - 1) begin
		hdmi_tx_vs <=  1'b0;
	end
end

//数据有效信号
always @(posedge hdmi_tx_clk) begin
	if (rst) begin
		hdmi_tx_de <= 1'b0;
	end
	else if ((cnt_h >= H_START - 1) && (cnt_h < H_END - 1) && (cnt_v >= V_START - 1) && (cnt_v < V_END - 1)) begin
		hdmi_tx_de <= 1'b1;
	end
	else begin
		hdmi_tx_de <= 1'b0;
	end
end

//hdmi_td
always @(posedge hdmi_tx_clk ) begin
	if (rst==1'b1) begin
		hdmi_td <='d0;
	end
	else if(cnt_h >=(H_START  - 1)  && cnt_h <(H_END - 1) && cnt_v >=(V_START - 1 )&& cnt_v <V_END - 1)begin
		hdmi_td <= rd_data;//输出方块图像
	end
	else begin
		hdmi_td <= 'd0;
	end
end
endmodule

你可能感兴趣的:(ZYNQ,verilog,fpga)