系列文章目录
详解并掌握AXI4总线协议(一)、AXI4-FULL接口介绍
详解并掌握AXI4总线协议(二)、AXI4_FULL_MASTER接口源码分析以及仿真验证
详解并掌握AXI4总线协议(四)、AXI4_FULL_SLAVE接口源码分析以及仿真验证
一、前言
在之前的文章中 详解Xilinx 基于Native接口的Block Memory Generator核生成ROM以及RAM的仿真验证,我们学会了如何生成Native接口的BRAM,现在我们学会了AXI4的接口协议以及仿真验证,那么本文就来使用AXI_FULL_MAXTER来控制AXI4接口的BRAM。
二、生成AXI4_FULL接口的BRAM
打开Block Memory Generator,选择接口类型为AXI4。
AXI4界面,选择AXI4,ID位宽默认就好,其它的配置也是默认。
三、编写AXI4_FULL_MASTER代码
在上篇文章官方有现成的AXI4_FULL_MASTE模板,我们可以参考这个模板,编写一个属于我们自己的控制代码。将一些没用的信号删除掉,然后编写控制程序:突发长度16,RAM地址容量1024,从地址0开始写入数据66,然后地址和数据都累加1,写64次突发。写完后读64次突发,代码如下:
`timescale 1ns / 1ns
module bram_ctrl_axi4_master(
input clk ,
input rst_n ,
input start_write , //开始写信号
input start_read //开始读信号
);
parameter C_M_AXI_BURST_LEN = 16; //突发长度16
parameter C_M_AXI_ID_WIDTH = 4 ; //ID位宽默认4
parameter C_M_AXI_ADDR_WIDTH = 32; //地址位宽32
parameter C_M_AXI_DATA_WIDTH = 32; //数据位宽32
parameter [4:0] IDLE = 5'b00001,
WRITE = 5'b00010,
READ = 5'b00100,
WRITE_DONE = 5'b01000,
READ_DONE = 5'b10000;
reg [4:0] state ;
reg read_done ; //读完成信号
reg write_done ; //写完成信号
wire rsta_busy ;
wire rstb_busy ;
reg [4 : 0] write_index ; //一次突发写中,写入的数据数量计数器
reg [4 : 0] read_index ; //一次突发读中,读出的数据数量计数器
reg start_single_burst_write ; //开始一次突发写
reg start_single_burst_read ; //开始一次突发读
reg [6:0] write_burst_counter ; //写突发次数计数器
reg [6:0] read_burst_counter ; //读突发次数计数器
reg burst_write_active ; //突发写有效信号
reg burst_read_active ; //突发读有效信号
/**********************AXI SIGNAL***********************/
//全局信号
wire M_AXI_ACLK ;
wire M_AXI_ARESETN ;
//写地址通道
wire [C_M_AXI_ID_WIDTH-1 : 0] M_AXI_AWID ;
reg [C_M_AXI_ADDR_WIDTH-1 : 0] M_AXI_AWADDR ;
wire [7 : 0] M_AXI_AWLEN ;
wire [2 : 0] M_AXI_AWSIZE ;
wire [1 : 0] M_AXI_AWBURST ;
reg M_AXI_AWVALID ;
wire M_AXI_AWREADY ;
//写数据通道
reg [C_M_AXI_DATA_WIDTH-1 : 0] M_AXI_WDATA ;
wire [C_M_AXI_DATA_WIDTH/8-1 : 0] M_AXI_WSTRB ;
reg M_AXI_WLAST ;
reg M_AXI_WVALID ;
wire M_AXI_WREADY ;
//写响应通道
wire [C_M_AXI_ID_WIDTH-1 : 0] M_AXI_BID ;
wire [1 : 0] M_AXI_BRESP ;
wire M_AXI_BVALID ;
reg M_AXI_BREADY ;
//读地址通道
wire [C_M_AXI_ID_WIDTH-1 : 0] M_AXI_ARID ;
reg [C_M_AXI_ADDR_WIDTH-1 : 0] M_AXI_ARADDR ;
wire [7 : 0] M_AXI_ARLEN ;
wire [2 : 0] M_AXI_ARSIZE ;
wire [1 : 0] M_AXI_ARBURST ;
reg M_AXI_ARVALID ;
wire M_AXI_ARREADY ;
//读数据通道
wire [C_M_AXI_ID_WIDTH-1 : 0] M_AXI_RID ;
wire [C_M_AXI_DATA_WIDTH-1 : 0] M_AXI_RDATA ;
wire [1 : 0] M_AXI_RRESP ;
wire M_AXI_RLAST ;
wire M_AXI_RVALID ;
reg M_AXI_RREADY ;
/********************combinational logic ****************************/
assign M_AXI_ACLK = clk ;
assign M_AXI_ARESETN = rst_n ;
assign M_AXI_AWID = 'd0 ;
assign M_AXI_AWLEN = C_M_AXI_BURST_LEN - 1 ;
assign M_AXI_AWSIZE = 3'b010 ;
assign M_AXI_AWBURST = 2'b01 ;
assign M_AXI_WSTRB = {(C_M_AXI_DATA_WIDTH/8){1'b1}} ;
assign M_AXI_BID = 'd0 ;
assign M_AXI_ARID = 'd0 ;
assign M_AXI_ARLEN = C_M_AXI_BURST_LEN - 1 ;
assign M_AXI_ARSIZE = 3'b010 ;
assign M_AXI_ARBURST = 2'b01 ;
assign M_AXI_RID = 'd0 ;
always @(posedge M_AXI_ACLK) begin
if(M_AXI_ARESETN == 1'b0)begin
state <= IDLE;
start_single_burst_write <= 1'b0;
start_single_burst_read <= 1'b0;
end
else begin
case (state)
IDLE: begin
if(start_read == 1'b1)
state <= READ;
else if(start_write == 1'b1)
state <= WRITE;
else
state <= IDLE;
end
READ:begin
if(read_done == 1'b1)
state <= READ_DONE;
else
state <= READ;
if(~M_AXI_ARVALID && ~start_single_burst_read && ~burst_read_active && (read_burst_counter < 64))
start_single_burst_read <= 1'b1;
else
start_single_burst_read <= 1'b0;
end
WRITE:begin
if(write_done == 1'b1)
state <= WRITE_DONE;
else
state <= WRITE;
if(~M_AXI_AWVALID && ~start_single_burst_write && ~burst_write_active && (write_burst_counter < 64))
start_single_burst_write <= 1'b1;
else
start_single_burst_write <= 1'b0;
end
default: state <= IDLE;
endcase
end
end
//写地址通道
always @(posedge M_AXI_ACLK)begin
if((M_AXI_ARESETN == 1'b0)||(state == WRITE_DONE))
M_AXI_AWVALID <= 1'b0;
else if(~M_AXI_AWVALID && (start_single_burst_write == 1'b1))
M_AXI_AWVALID <= 1'b1;
else if(M_AXI_AWREADY && M_AXI_AWVALID)
M_AXI_AWVALID <= 1'b0;
else
M_AXI_AWVALID <= M_AXI_AWVALID;
end
always @(posedge M_AXI_ACLK) begin
if((M_AXI_ARESETN == 1'b0)||(state == WRITE_DONE))
M_AXI_AWADDR <= 'd0;
else if(M_AXI_AWREADY && M_AXI_AWVALID)
M_AXI_AWADDR <= M_AXI_AWADDR + 32'd64;
else
M_AXI_AWADDR <= M_AXI_AWADDR;
end
//写数据通道
always @(posedge M_AXI_ACLK) begin
if((M_AXI_ARESETN == 1'b0)||(state == WRITE_DONE))
M_AXI_WVALID <= 1'b0;
else if(~M_AXI_WVALID && (start_single_burst_write == 1'b1))
M_AXI_WVALID <= 1'b1;
else if(M_AXI_WVALID && M_AXI_WREADY && M_AXI_WLAST)
M_AXI_WVALID <= 1'b0;
else
M_AXI_WVALID <= M_AXI_WVALID;
end
always @(posedge M_AXI_ACLK) begin
if((M_AXI_ARESETN == 1'b0)||(state == WRITE_DONE))
M_AXI_WDATA <= 'd66;
else if(M_AXI_WVALID && M_AXI_WREADY)
M_AXI_WDATA <= M_AXI_WDATA + 1'b1;
else
M_AXI_WDATA <= M_AXI_WDATA;
end
always @(posedge M_AXI_ACLK) begin
if((M_AXI_ARESETN == 1'b0)||(start_single_burst_write == 1'b1))
write_index <= 0;
else if(M_AXI_WVALID && M_AXI_WREADY && (write_index != M_AXI_AWLEN))
write_index <= write_index + 1'b1;
else
write_index <= write_index;
end
always @(posedge M_AXI_ACLK) begin
if((M_AXI_ARESETN == 1'b0)||(state == WRITE_DONE))
M_AXI_WLAST <= 1'b0;
else if((M_AXI_WVALID && M_AXI_WREADY && (write_index == M_AXI_AWLEN-1) && (M_AXI_AWLEN >=1)) || (M_AXI_AWLEN == 0))
M_AXI_WLAST <= 1'b1;
else if(M_AXI_WVALID && M_AXI_WREADY)
M_AXI_WLAST <= 1'b0;
else if((M_AXI_WLAST == 1'b1)&&(M_AXI_AWLEN == 0))
M_AXI_WLAST <= 1'b0;
else
M_AXI_WLAST <= M_AXI_WLAST;
end
//写响应通道
always @(posedge M_AXI_ACLK)begin
if(M_AXI_ARESETN == 0)
M_AXI_BREADY <= 1'b0;
else if(M_AXI_BVALID && ~M_AXI_BREADY)
M_AXI_BREADY <= 1'b1;
else if(M_AXI_BREADY)
M_AXI_BREADY <= 1'b0;
else
M_AXI_BREADY <= M_AXI_BREADY;
end
//读地址通道
always @(posedge M_AXI_ACLK) begin
if(M_AXI_ARESETN == 0)
M_AXI_ARVALID <= 1'b0;
else if(~M_AXI_ARVALID && start_single_burst_read)
M_AXI_ARVALID <= 1'b1;
else if(M_AXI_ARREADY && M_AXI_ARVALID)
M_AXI_ARVALID <= 1'b0;
else
M_AXI_ARVALID <= M_AXI_ARVALID;
end
always @(posedge M_AXI_ACLK) begin
if((M_AXI_ARESETN == 0) || (state == READ_DONE))
M_AXI_ARADDR <= 'd0;
else if(M_AXI_ARREADY && M_AXI_ARVALID)
M_AXI_ARADDR <= M_AXI_ARADDR + 'd64;
else
M_AXI_ARADDR <= M_AXI_ARADDR;
end
//读数据通道
always @(posedge M_AXI_ACLK) begin
if((M_AXI_ARESETN == 0) || (state == READ_DONE))
M_AXI_RREADY <= 1'b0;
else if(M_AXI_RVALID) begin
if((M_AXI_RLAST == 1'b1) && (M_AXI_RREADY == 1'b1))
M_AXI_RREADY <= 1'b0;
else
M_AXI_RREADY <= 1'b1;
end
else
M_AXI_RREADY <= M_AXI_RREADY;
end
//写控制信号
always @(posedge M_AXI_ACLK) begin
if((M_AXI_ARESETN == 0) || (state == WRITE_DONE))
write_burst_counter <= 'd0;
else if(M_AXI_AWVALID && M_AXI_AWREADY && (write_burst_counter < 64))
write_burst_counter <= write_burst_counter + 1'b1;
else
write_burst_counter <= write_burst_counter;
end
always @(posedge M_AXI_ACLK) begin
if(M_AXI_ARESETN == 0)
write_done <= 1'b0;
else if(M_AXI_BVALID && M_AXI_BREADY && (write_burst_counter == 64))
write_done <= 1'b1;
else
write_done <= 1'b0;
end
always @(posedge M_AXI_ACLK) begin
if((M_AXI_ARESETN == 0) || (state == WRITE_DONE))
burst_write_active <= 1'b0;
else if (start_single_burst_write)
burst_write_active <= 1'b1;
else if (M_AXI_BVALID && M_AXI_BREADY)
burst_write_active <= 0;
end
//读控制信号
always @(posedge M_AXI_ACLK) begin
if((M_AXI_ARESETN == 0) || (state == READ_DONE))
read_burst_counter <= 'd0;
else if(M_AXI_ARVALID && M_AXI_ARREADY && (read_burst_counter < 64))
read_burst_counter <= read_burst_counter + 1'b1;
else
read_burst_counter <= read_burst_counter;
end
always @(posedge M_AXI_ACLK) begin
if((M_AXI_ARESETN == 0) || (start_single_burst_read == 1'b1))
read_index <= 1'b0;
else if(M_AXI_RVALID && M_AXI_RREADY && (read_index != C_M_AXI_BURST_LEN-1))
read_index <= read_index + 1'b1;
else
read_index <= read_index;
end
always @(posedge M_AXI_ACLK) begin
if(M_AXI_ARESETN == 0)
read_done <= 1'b0;
else if((read_burst_counter == 64) &&(read_index == C_M_AXI_BURST_LEN-1))
read_done <= 1'b1;
else
read_done <= 1'b0;
end
always @(posedge M_AXI_ACLK) begin
if((M_AXI_ARESETN == 0) || (state == READ_DONE))
burst_read_active <= 1'b0;
else if(start_single_burst_read)
burst_read_active <= 1'b1;
else if(M_AXI_RVALID && M_AXI_RREADY && M_AXI_RLAST)
burst_read_active <= 0;
end
axi4_test_ram u1_axi4_test_ram (
.rsta_busy (rsta_busy ),
.rstb_busy (rstb_busy ),
.s_aclk (M_AXI_ACLK ),
.s_aresetn (M_AXI_ARESETN ),
.s_axi_awid (M_AXI_AWID ),
.s_axi_awaddr (M_AXI_AWADDR ),
.s_axi_awlen (M_AXI_AWLEN ),
.s_axi_awsize (M_AXI_AWSIZE ),
.s_axi_awburst (M_AXI_AWBURST ),
.s_axi_awvalid (M_AXI_AWVALID ),
.s_axi_awready (M_AXI_AWREADY ),
.s_axi_wdata (M_AXI_WDATA ),
.s_axi_wstrb (M_AXI_WSTRB ),
.s_axi_wlast (M_AXI_WLAST ),
.s_axi_wvalid (M_AXI_WVALID ),
.s_axi_wready (M_AXI_WREADY ),
.s_axi_bid (M_AXI_BID ),
.s_axi_bresp (M_AXI_BRESP ),
.s_axi_bvalid (M_AXI_BVALID ),
.s_axi_bready (M_AXI_BREADY ),
.s_axi_arid (M_AXI_ARID ),
.s_axi_araddr (M_AXI_ARADDR ),
.s_axi_arlen (M_AXI_ARLEN ),
.s_axi_arsize (M_AXI_ARSIZE ),
.s_axi_arburst (M_AXI_ARBURST ),
.s_axi_arvalid (M_AXI_ARVALID ),
.s_axi_arready (M_AXI_ARREADY ),
.s_axi_rid (M_AXI_RID ),
.s_axi_rdata (M_AXI_RDATA ),
.s_axi_rresp (M_AXI_RRESP ),
.s_axi_rlast (M_AXI_RLAST ),
.s_axi_rvalid (M_AXI_RVALID ),
.s_axi_rready (M_AXI_RREADY )
);
endmodule
四、仿真验证
4.1 TB文件的编写
激励文件主要是控制start_write 和start_read信号,先写后读,代码如下:
`timescale 1ns / 1ns
module tb_bram_ctrl_axi4_master();
reg clk ;
reg rst_n ;
reg start_read ;
reg start_write ;
initial begin
clk = 0;
rst_n = 0;
start_read = 0;
start_write = 0;
#120
rst_n = 1;
#500
start_write= 1'b1;
#20
start_write= 1'b0;
#30000
start_read = 1'b1;
#20
start_read = 1'b0;
end
always #10 clk = ~clk;
bram_ctrl_axi4_master u_bram_ctrl_axi4_master(
.clk ( clk ),
.rst_n ( rst_n ),
.start_write ( start_write ),
.start_read ( start_read )
);
endmodule
4.2 观察仿真结果
先看写方面的信号
- 首先外部传入start_write信号,状态机开始跳转
- 然后请求start_single_burst_write信号
- 地址VALID信号开始拉高,直到与从机的READY信号握手成功,每次握手成功,写地址累加64
- 然后主机开始拉高写数据的VALID信号,每当与写数据的READY信号握手成功,写数据从66开始累加1
- 一次突发到最后一个数据时,拉高WLAST信号
- 当一次突发写完成后,拉高BREADY信号,等待从机回复VALID信号以及BRESP信号,观测是否写入成功
- 当BREADY与BVALID信号握手成功后,开始下一次突发写,以此循环
把界面拉到后面,我们可以看到当我们突发写了64次后,写完成信号拉高,接下来我们看读数据方面波形。
- 在外部开始读信号拉高后,状态机开始跳转到读状态
- 随后拉高start_single_burst_read信号,然后拉高读地址valid信号以及读初始地址
- 当读地址握手成功后,就拉高RREADY信号等待与从机的RVALID握手
- 当最后一个读数据RLAST信号同时握手成功后就等待下一次突发读,第一次读出来的数据是从66开始累加,符合预期
把界面拉到后面,我们观察read_done信号附近的波形图。
最后一次突发读出来的数据是从1074到1089,和最后一次写入的数据一致。因此整个AXI4_FULL接口的BRAM读写验证已经完成。