本文以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)之间,准备信号不必(有时也不能)等待有效信号。
参考文献:
工程文件:
https://gitee.com/ICTbeginner/fpga/tree/master/RAM_AXI