本文以Vivado RAM IP的读写为例,实战AXI总线。

1.RAM IP配置

在IP Catalog中选择Block Memory Generator.

配置接口为AXI4-lite。

配置位宽为32bits,深度为1024。

2.RAM IP基本操作

在Source->IP Source->Instantiation Template->BRAM.veo中查看生成的例化模板:

//----------- Begin Cut here for INSTANTIATION Template ---// INST_TAG
BRAM your_instance_name (
  .rsta_busy(rsta_busy),          // output wire rsta_busy
  .rstb_busy(rstb_busy),          // output wire rstb_busy
  .s_aclk(s_aclk),                // input wire s_aclk
  .s_aresetn(s_aresetn),          // input wire s_aresetn
  .s_axi_awaddr(s_axi_awaddr),    // input wire [31 : 0] s_axi_awaddr
  .s_axi_awvalid(s_axi_awvalid),  // input wire s_axi_awvalid
  .s_axi_awready(s_axi_awready),  // output wire s_axi_awready
  .s_axi_wdata(s_axi_wdata),      // input wire [31 : 0] s_axi_wdata
  .s_axi_wstrb(s_axi_wstrb),      // input wire [3 : 0] s_axi_wstrb
  .s_axi_wvalid(s_axi_wvalid),    // input wire s_axi_wvalid
  .s_axi_wready(s_axi_wready),    // output wire s_axi_wready
  .s_axi_bresp(s_axi_bresp),      // output wire [1 : 0] s_axi_bresp
  .s_axi_bvalid(s_axi_bvalid),    // output wire s_axi_bvalid
  .s_axi_bready(s_axi_bready),    // input wire s_axi_bready
  .s_axi_araddr(s_axi_araddr),    // input wire [31 : 0] s_axi_araddr
  .s_axi_arvalid(s_axi_arvalid),  // input wire s_axi_arvalid
  .s_axi_arready(s_axi_arready),  // output wire s_axi_arready
  .s_axi_rdata(s_axi_rdata),      // output wire [31 : 0] s_axi_rdata
  .s_axi_rresp(s_axi_rresp),      // output wire [1 : 0] s_axi_rresp
  .s_axi_rvalid(s_axi_rvalid),    // output wire s_axi_rvalid
  .s_axi_rready(s_axi_rready)    // input wire s_axi_rready
);
// INST_TAG_END ------ End INSTANTIATION Template ---------

输入为主机信号,输出为从机信号,主机即我们自己写的代码,从机即此IP,具体信号含义可参见上篇博文《AXI总线简介1》。

从IP手册可以看出使用AXI4-lite读写的时序,如下:

读时序

写时序

可见其读写时序符合上篇博文提出的AXI握手时序。

3.使用状态机实现读写时序

状态转移图:

三段式状态机代码:

parameter  S0   =6'b000001;
parameter  S1   =6'b000010;
parameter  S2   =6'b000100;
parameter  S3   =6'b001000;
parameter  S4   =6'b010000;
parameter  S5   =6'b100000;

logic [5:0] state;
logic [5:0] nextstate;

always @(*)
begin
	case (state)
	S0:
	STATE = "S0";
	S1:
	STATE = "S1";
	S2:
	STATE = "S2";
	S3:
	STATE = "S3";
	S4:
	STATE = "S4";
	S5:
	STATE = "S5";
		default : STATE = "ERROR";
	endcase
end



always @(posedge s_aclk,negedge s_aresetn)
begin
	if(!s_aresetn)
		state <= S0;
	else
		state <= nextstate;
end
always @(*)
begin
   case(state)
       S0:
          begin   
       		if(start_read)
       			nextstate = S1;
       		else if(start_write)
       			nextstate = S3;
       		else
       			nextstate = state;
           end
       S1: begin 
       			if(s_axi_arready)
       				nextstate = S2;
       			else
       				nextstate = state;
            end
       S2:  nextstate <= S0;
       S3:  begin 
       			if(s_axi_awready )
       				nextstate = S4;
       		else
       			nextstate = state;
       		end
       	S4: begin  
       			if(s_axi_wready)
       				nextstate = S5;
       			else
       				nextstate = state;
       		end
       	S5: nextstate = S0;
       default: nextstate   = S0;
   endcase
end


always @(posedge s_aclk ,negedge s_aresetn)
begin	
	if(!s_aresetn)
		begin
	  		s_axi_awaddr = 32'b0;
			s_axi_wstrb = 4'b1111;
			s_axi_awvalid = 0;
			s_axi_wdata = 32'b0;
			s_axi_bready = 0;
			s_axi_araddr = 0;
			readAddr = 32'b0;
			s_axi_arvalid = 0;
			s_axi_rready = 0;
			s_axi_wvalid = 0;
			data = 32'b0;
			read_data = 32'b0;
			start_read = 0;
			start_write = 0;
		end
	else
		begin
			case (nextstate)
			S0:begin 
			   s_axi_wstrb = 4'b1111;
			   s_axi_awvalid = 0;
			   s_axi_bready = 1;
			   s_axi_arvalid = 0;
			   s_axi_rready = 0;
			   s_axi_wvalid = 0;
			   if(writeCnt == 1024) //读
			   begin
			   	   start_write <= 0;
			   	   start_read <= 1;
			   	   writeCnt <= writeCnt;
			   	   readCnt <= readCnt + 1;
			   	   readAddr  <= readAddr + 4;
			   	   data <= 0;
			   end
			   else //写
			   begin 
			       start_read <= 0; 
			       start_write <= 1;
			       writeCnt <= writeCnt+1;
			       addar <= addar + 4;
			       read_data <= 0;
			   end	
			end
	        S1:begin //开始一次读事务
	           s_axi_arvalid <=1;
	           s_axi_araddr <= readAddr;
	        end
	        S2:begin
	        	s_axi_rready <= 1;
	        	read_data <= s_axi_rdata;
	        end
	        S3:begin //开始一次写事务
	        	s_axi_awvalid <= 1;
	        	s_axi_awaddr <= addar;	
	        end
	        S4:begin 
	        	s_axi_awvalid <= 1;
	        	s_axi_awaddr <= addar;
	        	data <= writeData[writeCnt];
	        	s_axi_wvalid <=1 ;
	        	s_axi_wdata <= data;
	        	
	        end
	        S5:begin //写响应
	        	s_axi_bready <= 1;
	        end
				default :
				begin
	  		   		s_axi_awaddr = 32'b0;
			   		s_axi_wstrb = 4'b1111;
			   		s_axi_awvalid = 0;
			   		s_axi_wdata = 32'b0;
			   		s_axi_bready = 0;
			   		s_axi_araddr = 0;
			   		s_axi_arvalid = 0;
			   		s_axi_rready = 0;
			   		s_axi_wvalid = 0;
	 		  		start_read = 0;
	 		  		start_write = 0;
	 		  		addar = 32'b0;
				end
			endcase
		end
end

4.仿真时序图

1.读时序:

2.写时序

5.仿真结果

1.写入数据为WriteData.txt文档内数据,读出数据为ReadData.txt数据,对比两个文件内数据,可验证仿真结果的正确性。

注意事项:

1.在Testbench中读取和写入的数据在工程文件夹如下路径:

.\RAM_AXI\RAM_AXI.sim\sim_1\behav\xsim

2.注意各类成对的准备信号(xxx_ready)和有效信号(xxx_valid)之间,准备信号不必(有时也不能)等待有效信号。

参考文献:

1.Xilinx pg058

工程文件:

https://gitee.com/ICTbeginner/fpga/tree/master/RAM_AXI