详解并掌握AXI4总线协议(三)、基于AXI4_FULL接口的BRAM读写仿真验证

系列文章目录

详解并掌握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 观察仿真结果

   先看写方面的信号

在这里插入图片描述

  1. 首先外部传入start_write信号,状态机开始跳转
  2. 然后请求start_single_burst_write信号
  3. 地址VALID信号开始拉高,直到与从机的READY信号握手成功,每次握手成功,写地址累加64
  4. 然后主机开始拉高写数据的VALID信号,每当与写数据的READY信号握手成功,写数据从66开始累加1
  5. 一次突发到最后一个数据时,拉高WLAST信号
  6. 当一次突发写完成后,拉高BREADY信号,等待从机回复VALID信号以及BRESP信号,观测是否写入成功
  7. 当BREADY与BVALID信号握手成功后,开始下一次突发写,以此循环

在这里插入图片描述
   把界面拉到后面,我们可以看到当我们突发写了64次后,写完成信号拉高,接下来我们看读数据方面波形。

在这里插入图片描述

  1. 在外部开始读信号拉高后,状态机开始跳转到读状态
  2. 随后拉高start_single_burst_read信号,然后拉高读地址valid信号以及读初始地址
  3. 当读地址握手成功后,就拉高RREADY信号等待与从机的RVALID握手
  4. 当最后一个读数据RLAST信号同时握手成功后就等待下一次突发读,第一次读出来的数据是从66开始累加,符合预期

   把界面拉到后面,我们观察read_done信号附近的波形图。

在这里插入图片描述
   最后一次突发读出来的数据是从1074到1089,和最后一次写入的数据一致。因此整个AXI4_FULL接口的BRAM读写验证已经完成。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

爱奔跑的虎子

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值