AXI总线,AXI_BRAM读写仿真测试

平台:vivado2017.4

芯片:kintex-7  xc7k325tffg900-2 (active)

准备学习AXI总线。那就从最简单的AXI BRAM学习开始。

新建BLOCK MEMORY GENERATOR,选择接口类型AXI4,这里选择simple

Dual port ram。数据位32位,数据深度1024。模块的例化部分如下

首先需要明白使用AXI封装的RAM的各个接口的作用。AXI总线是一种多通道传输总线,地址,数据,写数据,握手信号。在不同的通道中发送。这是AXI_BRAM的例化模板。

//----------- Begin Cut here for INSTANTIATION Template ---// INST_TAG
blk_mem_gen_0 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_awid(s_axi_awid),        // input wire [3 : 0] s_axi_awid
  .s_axi_awaddr(s_axi_awaddr),    // input wire [31 : 0] s_axi_awaddr
  .s_axi_awlen(s_axi_awlen),      // input wire [7 : 0] s_axi_awlen
  .s_axi_awsize(s_axi_awsize),    // input wire [2 : 0] s_axi_awsize
  .s_axi_awburst(s_axi_awburst),  // input wire [1 : 0] s_axi_awburst
  .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_wlast(s_axi_wlast),      // input wire s_axi_wlast
  .s_axi_wvalid(s_axi_wvalid),    // input wire s_axi_wvalid
  .s_axi_wready(s_axi_wready),    // output wire s_axi_wready
  .s_axi_bid(s_axi_bid),          // output wire [3 : 0] s_axi_bid
  .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_arid(s_axi_arid),        // input wire [3 : 0] s_axi_arid
  .s_axi_araddr(s_axi_araddr),    // input wire [31 : 0] s_axi_araddr
  .s_axi_arlen(s_axi_arlen),      // input wire [7 : 0] s_axi_arlen
  .s_axi_arsize(s_axi_arsize),    // input wire [2 : 0] s_axi_arsize
  .s_axi_arburst(s_axi_arburst),  // input wire [1 : 0] s_axi_arburst
  .s_axi_arvalid(s_axi_arvalid),  // input wire s_axi_arvalid
  .s_axi_arready(s_axi_arready),  // output wire s_axi_arready
  .s_axi_rid(s_axi_rid),          // output wire [3 : 0] s_axi_rid
  .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_rlast(s_axi_rlast),      // output wire s_axi_rlast
  .s_axi_rvalid(s_axi_rvalid),    // output wire s_axi_rvalid
  .s_axi_rready(s_axi_rready)    // input wire s_axi_rready
);

部分接口说明

//全局信号
wire 				rsta_busy			;
wire 				rstb_busy			;
wire 				s_aclk				;//时钟 
wire 				s_aresetn			;//复位 
//写地址通道
reg [3:0]			s_axi_awid			;//写地址ID,这个信号是写地址信号组的IDtag      
reg [31:0]			s_axi_awaddr		;//写地址    
reg [7:0]			s_axi_awlen			;//突发次数
reg [2:0]			s_axi_awsize		;//一次传输字节数。一个时钟节拍传输的数据的最大位。s_axi_awsize = 3'b000,传输1byte。s_axi_awsize = 3'b001,传输2byte。s_axi_awsize = 3'b010,传输4byte。s_axi_awsize = 3'b011,传输8byte。s_axi_awsize = 3'b100,传输16byte。s_axi_awsize = 3'b101,传输32byte。s_axi_awsize = 3'b110,传输64byte。s_axi_awsize = 3'b111,传输128byte。
reg [1:0]			s_axi_awburst		;//突发类型
reg 				s_axi_awvalid		;//握手信号,写地址有效。'1'有效
wire 				s_axi_awready		;//握手。写地址准备好,'1'设备准备好
//写数据通道
reg [31:0]			s_axi_wdata			;//写入数据     
reg [3:0]			s_axi_wstrb			;//写阀门,WSTRB[n]表示的区间为WDATA[(8*n) + 7:(8*n)];说明:s_axi_wstrb[0]表示s_axi_wdata[7:0]有效。依次类推。
reg 				s_axi_wlast			;//最后一个数据  
reg 				s_axi_wvalid		;//写数据有效
wire 				s_axi_wready		;//写数据准备就绪
//写请求通道
wire [3:0]			s_axi_bid			;//响应ID,这个数值必须与AWID的数值匹配。
wire [1:0]			s_axi_bresp			;//写入响应,这个信号指明写事务的状态。可能有的响应:OKAY,EXOKAY,SLVERR,DECERR  
wire 				s_axi_bvalid		;//写响应有效。'1'有效
reg 				s_axi_bready		;//接收响应就绪,该信号表示主机已经能够接受响应信息。'1'主机就绪
//读地址通道
reg [3:0]			s_axi_arid			;//读地址ID      
reg [31:0]			s_axi_araddr		;//低地址
reg [7:0]			s_axi_arlen			;//读地址突发长度
reg [2:0]			s_axi_arsize		;//一次传输字节数
reg [1:0]			s_axi_arburst		;//突发类型
reg 				s_axi_arvalid		;//握手信号,读地址有效。该信号一直保持,直到ARREADY为高。'1'地址和控制信号有效。
wire 				s_axi_arready		;//握手信号,读地址就绪,指明设备已经准备好接收数据了。'1'设备就绪。
//读数据通道
wire [3:0]			s_axi_rid			;//读IDtag。RID的数值必须与ARID的数值匹配
wire [31:0]			s_axi_rdata			;//读数据
wire [1:0]			s_axi_rresp			;//读响应。这个信号指明读传输状态
wire 				s_axi_rlast			;//读取最后一个数据
wire 				s_axi_rvalid		;//读取有效'1'读数据有效
reg 				s_axi_rready		;//读数据就绪'1'主机就绪

首先分析AXI通道。AXI总线最重要的是握手机制,握手机制。再向RAM里面写数据的时候,通过AXI总线握手机制,握手成功后通过写数据发送数据。

关于AXI的握手机制:AXI全部五个通道使用相同的VALID/READY握手机制传输数据以及控制信息。传输源产生VALID信号来指明何时数据或控制信息有效。而且地源产生READY信号来指明已经准备好接受数据或者控制信息。传输发生在VALID和READY信号同时为高的时候。

注意:

  1. 发送方不能等待接收方先给出AWREADY或者WREADY信号后在给出AWVALID或WVALID。发送方准备发送,置起VALID信号是完全主动与独立的。接收方按照协议可以等待发送方的VALID信号,但是如果发送方也等待接收方的信号。就会造成总线锁死。
  2. 且发送方一旦置起VALID信号就不能置低,直到握手完成。
  3. 接收方的READY信号可以等待发送方的VALID信号有效后在置起。且可以在接收方意识到自己忙碌时可再次置低READY信号。只要发送方VALID没有置起。

AXI突发读写类型和在一次突发读写事务内如何计算地址和byte lanes。

s_axi_awlen和s_axi_arlen每一次突发读写传输的数据的个数。

Len = 4’b0000,一次突发传输1个数据。

Len = 4’b0001,一次突发传输2个数据。

Len = 4’b0010,一次突发传输3个数据。

Len = 4’b0011,一次突发传输4个数据。

Len = 4’b0100,一次突发传输5个数据。

Len = 4’b0101,一次突发传输6个数据。

Len = 4’b0110,一次突发传输7个数据。

Len = 4’b0111,一次突发传输8个数据。

Len = 4’b1000,一次突发传输9个数据。

Len = 4’b1001,一次突发传输10个数据。

Len = 4’b1010,一次突发传输11个数据。

Len = 4’b1011,一次突发传输12个数据。

Len = 4’b1100,一次突发传输13个数据。

Len = 4’b1101,一次突发传输14个数据。

Len = 4’b1110,一次突发传输15个数据。

Len = 4’b1111,一次突发传输16个数据。

s_axi_awsize和s_axi_arsize一个时钟节拍所传输的数据的最大位数。

//一次传输字节数。一个时钟节拍传输的数据的最大位。

s_axi_awsize = 3'b000,传输1byte。

s_axi_awsize = 3'b001,传输2byte。

s_axi_awsize = 3'b010,传输4byte。

s_axi_awsize = 3'b011,传输8byte。

s_axi_awsize = 3'b100,传输16byte。

s_axi_awsize = 3'b101,传输32byte。

s_axi_awsize = 3'b110,传输64byte。

s_axi_awsize = 3'b111,传输128byte。

s_axi_awburst和s_axi_arburst指示AXI协议的三种突发读写类型。固定式突发读写,增值式突发读写,包装式突发读写。

  1. Burst = 2’b00 ,FIXED固定式突发读写是指地址是固定的,每一次传输的地址都不变。这样的突发读写式重复的对一个相同的位置进行存取。如FIFO。
  2. Burst = 2’b01,INCR增值式突发读写是指每一次读写的地址都比上一次的地址增加一个固定的值。
  3. Burst = 2’b10,WRAP包装式突发读写跟增值式突发读写类似。包装式突发读写的地址是包数据的低地址当到达一个包边界。

包装式突发读写有两个限制:

起始地址必须以传输的size对齐。

突发式读写的长度必须是2,4,6,8,或者16。

设计代码...实现对AXI_BRAM的单个数据的读写。

根据AXI总线读写的分析。分别设置空闲,写地址,写数据,读地址以及停止状态。

状态机分三步写。

设计两个接口,分别为AXI写使能,和AXI读使能。默认在空闲状态下,等待读写使能。当写使能来到的时候,先进入写地址状态。在写地址状态里面判断是否握手,valid和ready同时为1,退出写地址状态(这里我准备只使用AXI总线的单个读写功能)之后进入写状态,同样在写状态判断是否握手,结束写状态,进入停止状态。当AXI读使能来到的时候,进入读状态,读状态只需要发送读地址即可读出数据,这里同样只握手后退出。

在写通道,首先写入一个地址。

//写地址
                s_axi_awaddr <= #U_DLY s_axi_awaddr + 32'b1;//地址0
                s_axi_awlen	 <= #U_DLY 8'b0;//1个数据
                s_axi_awsize <= #U_DLY 3'b010;//4bytes
                s_axi_awburst<= #U_DLY 2'b0;//突发地址固定模式
                s_axi_awvalid<= #U_DLY 1'b1;//地址有效

结束写通道后地址不清零,保留原值。

在写入数据。

                s_axi_araddr <= #U_DLY s_axi_araddr + 32'b1;
                s_axi_arlen	 <= #U_DLY 8'b0;
                s_axi_arsize <= #U_DLY 3'b010;
                s_axi_arburst<= #U_DLY 2'b0;
                s_axi_arvalid<= #U_DLY 1'b1;

同时。需要拉高s_axi_rready,表示主机接收读数据就绪。

但是在仿真过程中却发现了问题,写地址在一个写期间地址递增。通过观察发现使我们的写地址状态持续了两个时钟周期。这样做导致地址增加了一个。所以上述的写地址赋值方式错误。

AXI总线,AXI_BRAM读写仿真测试_第1张图片

 同理写数据也递增。

同理读地址也递增。读数据不对。

修改代码。

经过分析,我在testbench里面写的过程是:首先向地址0写入32位的数据1。之后立马将地址0的数据读出来。之后在写地址1写32位的数据2,再读出来。

我的逻辑代码里面,首先向地址0写32位数据1,在向地址1写32位数据32位2。一直重复十次。

仿真的结果是:我经过AXI总线读取地址0的数据是我写入的地址3的数据。这里就有了问题。说明我向AXI_RAM里面写入的地址和数据不匹配。

思考:AXI_RAM的写地址是否和DDR等地址线类似。而不与传统的RAM地址递增一致。修改代码,设置AXI_RAM的s_axi_awaddr地址累加32'd4。

//地址累加
always@(posedge lb_clk , negedge lb_rst_n)
begin
        if(!lb_rst_n)
                reg_wr_addr <= #U_DLY 32'b0;
        else if(c_state == wr_addr_valid && s_axi_awvalid == 1'b1 && s_axi_awready == 1'b1 )
                reg_wr_addr <= #U_DLY reg_wr_addr + 32'd4;
        else
                reg_wr_addr <= #U_DLY reg_wr_addr;
end

结果仿真正确。

写数据。

AXI总线,AXI_BRAM读写仿真测试_第2张图片

读数据。

AXI总线,AXI_BRAM读写仿真测试_第3张图片

数据正常。

这样就对了。接下来试试突发写入。Burst = 2’b01,INCR增值式突发读写是指每一次读写的地址都比上一次的地址增加一个固定的值。

首先要明白AXI_BRAM的突发读写,

关于突发读第一种情况。当地址出现在地址总线上后,传输的数据将出现在读数据通道上。从设备将保持VALID为低直到读数据有效。为了表示一次突发式读写完成,设备用RLAST信号来表示最后一个被传输的数据。

AXI总线,AXI_BRAM读写仿真测试_第4张图片

第二种情况,突发读可以一次发送两个读地址。设备会在第一次突发读完成后处理第二次突发读。主机一开始传输了两个地址给设备。设备在完全处理完第一个地址的数据后开始处理第二个地址的数据

AXI总线,AXI_BRAM读写仿真测试_第5张图片

关于AXI_BRAM的突发写。

突发写的过程一开始,主机先将地址和控制信息写到写地址通道中,然后主机发送每一个写数据到写数据通道中。当主机发送最后一个数据是,拉高WLAST信号就变为高。当设备接收完所有数据之后他将一个写响应发送回主机来表明写事务完成。

AXI总线,AXI_BRAM读写仿真测试_第6张图片

好接下来根据上面的时序来实现对AXI_BRAM的突发读写。向地址0突发写16个数据,再读出。

写代码中。

更新状态机。

在写数据状态增加了

reg     [31:0]       write_cnt           ;

用于计数写数据的传输个数。作为主机我们向AXI_BRAM里面写数据首先需要设置写入首地址,设置突发传输次数,设置传输的字节数(小于或等于AXI总线位宽),这里我们突发模式为地址递增模式。主机需要提供最后一个数据信息s_axi_wlast,所以我采样计数器对当前突发传输个数进行计数。等待写通道响应作为写结束信号。

write:
           begin
                   if(s_axi_bvalid == 1'b1)
                           n_state = stop;
                   else
                           n_state = write;
           end

仿真结果如下:

可以看到写结束后并没有结束写状态,写响应也没有更新,这和我们预期的不一致。

更改写状态的结束标志。经过分析代码,更改部分为。

write:
           begin
                   if(s_axi_wlast == 1'b1 && s_axi_wvalid == 1'b1 && s_axi_wready == 1'b1 )//只有在写数据的最后一个数据和最后一个数据握手成功后退出写状态
                           n_state = stop;
                   else
                           n_state = write;
           end

写状态用最后一个数据标志握手成功后退出。

更改写通道部分数据和计数器。

//写数据通道
always@(posedge lb_clk , negedge lb_rst_n)
begin
        if(!lb_rst_n)
        begin
                //写数据通道
                s_axi_wdata  <= #U_DLY 32'b0;
                s_axi_wstrb  <= #U_DLY 4'b0;
                s_axi_wvalid <= #U_DLY 1'b0;
                s_axi_bready <= #U_DLY 1'b0;
                write_cnt    <= #U_DLY 32'd1;
        end
        else if(c_state == write )
        begin
                s_axi_wstrb  <= #U_DLY 4'b1111;//wdata都有效
                s_axi_wvalid <= #U_DLY 1'b1;
                s_axi_bready <= #U_DLY 1'b1;
                //写数据
                if(s_axi_wvalid == 1'b1 && s_axi_wready == 1'b1)
                begin
                        write_cnt   <= #U_DLY write_cnt + 32'd1;
                        s_axi_wdata <= #U_DLY s_axi_wdata + 32'b1;
                end
                else
                begin
                        write_cnt   <= #U_DLY write_cnt;
                        s_axi_wdata <= #U_DLY s_axi_wdata;
                end
        end
        else
        begin
                //写数据通道
                s_axi_wdata  <= #U_DLY 32'b0;
                s_axi_wstrb  <= #U_DLY 4'b0;
                s_axi_wvalid <= #U_DLY 1'b0;
                s_axi_bready <= #U_DLY 1'b1;
                write_cnt    <= #U_DLY 32'd1;
        end
end

计数器默认值为1。数据累加。仿真效果如下图。写数据虽然跟新到了16,但是我们设置了数据长度为16个,所以最后一个数据是没有握手成功的。

AXI总线,AXI_BRAM读写仿真测试_第7张图片

下面看读部分。

同样读地址从0开始。突发读取16个数据,采用地址递增的突发读模式。比较简单就不放代码了。

下面是读的截图。

AXI总线,AXI_BRAM读写仿真测试_第8张图片

多测试几次。

从地址5读取6个数。

AXI总线,AXI_BRAM读写仿真测试_第9张图片

 地址3读取6个数。

 AXI总线,AXI_BRAM读写仿真测试_第10张图片

地址4读取6个数。

AXI总线,AXI_BRAM读写仿真测试_第11张图片

综合上述的仿真结果,AXI突发读取的数据地址位是按4递增的。

0-3地址存储的数据0,4-7地址存储的数据1,依次类推。这点在使用时需要注意一下。

下面附上控制程序和测试文件。

// *********************************************************************************/
// Project Name :
// Author       : i_huyi
// Email        : [email protected]
// Creat Time   : 2021/8/2 14:06:24
// File Name    : .v
// Module Name  : 
// Called By    :
// Abstract     :
// v1.0对AXI_BRAM单独读写模块。
// v1.1更新对AXI_BRAM的突发读写。设置突发长度。
//
// CopyRight(c) 2020, xxx xxx xxx Co., Ltd.. 
// All Rights Reserved
//
// *********************************************************************************/
// Modification History:
// 1. initial
// *********************************************************************************/
// *************************
// MODULE DEFINITION
// *************************
`timescale 1 ps / 1 ps
module bram_control_burst#(
parameter	U_DLY = 1
										)
										(
input	wire		lb_clk				,
input	wire		lb_rst				,
input	wire	    reg_axi_write	    ,		
input	wire    	reg_axi_read        		


										);
//--------------------------------------
// localparam
//--------------------------------------

//配置突发个数
localparam          len             = 8'h0f;//突发16个数据

//状态空间
localparam          idle            = 4'b0000;
localparam          wr_addr         = 4'b0001;
localparam          wr_addr_valid   = 4'b0010;
localparam          write           = 4'b0011;
localparam          write_valid     = 4'b0100;
localparam          read            = 4'b0101;
localparam          read_valid      = 4'b0110;
localparam          read_data       = 4'b0111;
localparam          stop            = 4'b1000;



//--------------------------------------
// register
//--------------------------------------

reg     [3:0]       c_state             ;
reg     [3:0]       n_state             ;

reg     [31:0]      reg_wr_addr         ;
//reg     [31:0]      reg_wr_data         ;
reg     [31:0]      reg_rd_addr         ;



reg     [31:0]       write_cnt           ;

//--------------------------------------
// wire
//--------------------------------------
//全局信号
wire 				rsta_busy			;
wire 				rstb_busy			;
wire 				s_aclk				;//时钟 
wire 				s_aresetn			;//复位 
//写地址通道
reg [3:0]			s_axi_awid			;//写地址ID,这个信号是写地址信号组的IDtag      
reg [31:0]			s_axi_awaddr		;//写地址    
reg [7:0]			s_axi_awlen			;//突发次数
reg [2:0]			s_axi_awsize		;//一次传输字节数。一个时钟节拍传输的数据的最大位。s_axi_awsize = 3'b000,传输1byte。s_axi_awsize = 3'b001,传输2byte。s_axi_awsize = 3'b010,传输4byte。s_axi_awsize = 3'b011,传输8byte。s_axi_awsize = 3'b100,传输16byte。s_axi_awsize = 3'b101,传输32byte。s_axi_awsize = 3'b110,传输64byte。s_axi_awsize = 3'b111,传输128byte。
reg [1:0]			s_axi_awburst		;//突发类型
reg 				s_axi_awvalid		;//握手信号,写地址有效。'1'有效
wire 				s_axi_awready		;//握手。写地址准备好,'1'设备准备好
//写数据通道
reg [31:0]			s_axi_wdata			;//写入数据     
reg [3:0]			s_axi_wstrb			;//写阀门,WSTRB[n]表示的区间为WDATA[(8*n) + 7:(8*n)];说明:s_axi_wstrb[0]表示s_axi_wdata[7:0]有效。依次类推。
reg 				s_axi_wlast			;//最后一个数据  
reg 				s_axi_wvalid		;//写数据有效
wire 				s_axi_wready		;//写数据准备就绪
//写请求通道
wire [3:0]			s_axi_bid			;//响应ID,这个数值必须与AWID的数值匹配。
wire [1:0]			s_axi_bresp			;//写入响应,这个信号指明写事务的状态。可能有的响应:OKAY,EXOKAY,SLVERR,DECERR  
wire 				s_axi_bvalid		;//写响应有效。'1'有效
reg 				s_axi_bready		;//接收响应就绪,该信号表示主机已经能够接受响应信息。'1'主机就绪
//读地址通道
reg [3:0]			s_axi_arid			;//读地址ID      
reg [31:0]			s_axi_araddr		;//低地址
reg [7:0]			s_axi_arlen			;//读地址突发长度
reg [2:0]			s_axi_arsize		;//一次传输字节数
reg [1:0]			s_axi_arburst		;//突发类型
reg 				s_axi_arvalid		;//握手信号,读地址有效。该信号一直保持,直到ARREADY为高。'1'地址和控制信号有效。
wire 				s_axi_arready		;//握手信号,读地址就绪,指明设备已经准备好接收数据了。'1'设备就绪。
//读数据通道
wire [3:0]			s_axi_rid			;//读IDtag。RID的数值必须与ARID的数值匹配
wire [31:0]			s_axi_rdata			;//读数据
wire [1:0]			s_axi_rresp			;//读响应。这个信号指明读传输状态
wire 				s_axi_rlast			;//读取最后一个数据
wire 				s_axi_rvalid		;//读取有效'1'读数据有效
reg 				s_axi_rready		;//读数据就绪'1'主机就绪

//system signal
wire                lb_rst_n            ;




//--------------------------------------
// assign
//--------------------------------------
//system signal
assign      lb_rst_n = ~lb_rst;
assign      s_aclk = lb_clk;
assign      s_aresetn = ~lb_rst;


//------------------------------------------------------------
//------------------------------------------------------------
always@(posedge lb_clk ,negedge lb_rst_n)
begin
    if(!lb_rst_n)
        c_state <= #U_DLY idle;
    else
        c_state <= #U_DLY n_state;
end



always @(*)
begin
       case(c_state)
           idle:
           begin
                   if(reg_axi_write == 1'b1)
                           n_state = wr_addr;
                   else if(reg_axi_read == 1'b1)
                           n_state = read;
                   else
                           n_state = idle;
           end

           wr_addr:
           begin
                   n_state = wr_addr_valid;
           end

           wr_addr_valid:
           begin
                   if(s_axi_awvalid == 1'b1 && s_axi_awready == 1'b1)
                           n_state = write;
                   else
                           n_state = wr_addr_valid;
           end
           
           write:
           begin
                   if(s_axi_wlast == 1'b1 && s_axi_wvalid == 1'b1 && s_axi_wready == 1'b1 )//只有在写数据的最后一个数据和最后一个数据握手成功后退出写状态
                           n_state = stop;
                   else
                           n_state = write;
           end

           read:
                   n_state = read_valid;
           read_valid:
           begin
                   if(s_axi_arvalid == 1'b1 && s_axi_arready == 1'b1)
                           n_state = read_data;
                   else
                           n_state = read_valid;
           end


           read_data:
           begin
                   if(s_axi_rvalid == 1'b1 && s_axi_rready == 1'b1)
                           n_state = stop;
                   else
                           n_state = read_data;
           end


           stop:
                   n_state = idle;
           default:
                   n_state = idle;
       endcase
end

//写地址通道
always@(posedge lb_clk , negedge lb_rst_n)
begin
        if(!lb_rst_n)
        begin
                //写地址通道
                s_axi_awid	 <= #U_DLY 4'b0000;
                s_axi_awaddr <= #U_DLY 32'b0;
                s_axi_awlen	 <= #U_DLY 8'b0;
                s_axi_awsize <= #U_DLY 3'b0;
                s_axi_awburst<= #U_DLY 2'b0;
                reg_wr_addr  <= #U_DLY 32'd0;
        end
        else if(c_state == wr_addr || c_state == wr_addr_valid)
        begin
                //写地址
                s_axi_awaddr <= #U_DLY reg_wr_addr;//地址0
                s_axi_awlen	 <= #U_DLY len;//16个数据
                s_axi_awsize <= #U_DLY 3'b010;//一次传输4bytes
                s_axi_awburst<= #U_DLY 2'b01;//突发地址递增模式
        end
        else
        begin
                //写地址通道
                s_axi_awid	 <= #U_DLY 4'b0000;
                s_axi_awaddr <= #U_DLY s_axi_awaddr;
                s_axi_awlen	 <= #U_DLY s_axi_awlen;
                s_axi_awsize <= #U_DLY s_axi_awsize;
                s_axi_awburst<= #U_DLY s_axi_awburst;
        end
end

//写地址有效
always@(posedge lb_clk , negedge lb_rst_n)
begin
        if(!lb_rst_n)
        begin
                s_axi_awvalid <= #U_DLY 1'b0;
        end
        else if(c_state == wr_addr_valid)
                s_axi_awvalid <= #U_DLY 1'b1;
        else
                s_axi_awvalid <= #U_DLY 1'b0;
end

//写数据通道
always@(posedge lb_clk , negedge lb_rst_n)
begin
        if(!lb_rst_n)
        begin
                //写数据通道
                s_axi_wdata  <= #U_DLY 32'b0;
                s_axi_wstrb  <= #U_DLY 4'b0;
                s_axi_wvalid <= #U_DLY 1'b0;
                s_axi_bready <= #U_DLY 1'b0;
                write_cnt    <= #U_DLY 32'd1;
        end
        else if(c_state == write )
        begin
                s_axi_wstrb  <= #U_DLY 4'b1111;//wdata都有效
                s_axi_wvalid <= #U_DLY 1'b1;
                s_axi_bready <= #U_DLY 1'b1;
                //写数据
                if(s_axi_wvalid == 1'b1 && s_axi_wready == 1'b1)
                begin
                        write_cnt   <= #U_DLY write_cnt + 32'd1;
                        s_axi_wdata <= #U_DLY s_axi_wdata + 32'b1;
                end
                else
                begin
                        write_cnt   <= #U_DLY write_cnt;
                        s_axi_wdata <= #U_DLY s_axi_wdata;
                end
        end
        else
        begin
                //写数据通道
                s_axi_wdata  <= #U_DLY 32'b0;
                s_axi_wstrb  <= #U_DLY 4'b0;
                s_axi_wvalid <= #U_DLY 1'b0;
                s_axi_bready <= #U_DLY 1'b1;
                write_cnt    <= #U_DLY 32'd1;
        end
end

//产生最后一位标志
always@(posedge lb_clk , negedge lb_rst_n)
begin
        if(!lb_rst_n)
                s_axi_wlast	 <= #U_DLY 1'b0;
        else if(c_state == write && write_cnt == {24'h00_0000,len})
                s_axi_wlast	 <= #U_DLY 1'b1;
        else
                s_axi_wlast  <= #U_DLY 1'b0;
end


//读数据通道
always@(posedge lb_clk , negedge lb_rst_n)
begin
        if(!lb_rst_n)
        begin
                //读地址通道
                s_axi_arid	 <= #U_DLY 4'b0;
                s_axi_araddr <= #U_DLY 32'b0;
                s_axi_arlen	 <= #U_DLY 8'b0;
                s_axi_arsize <= #U_DLY 3'b0;
                s_axi_arburst<= #U_DLY 2'b0;
                reg_rd_addr  <= #U_DLY 32'd28;
                //读数据通道
                s_axi_rready <= #U_DLY 1'b1;
        end
        else if(c_state == read || c_state == read_valid)
        begin
                s_axi_araddr <= #U_DLY reg_rd_addr;
                s_axi_arlen	 <= #U_DLY len - 8'd10;
                s_axi_arsize <= #U_DLY 3'b010;
                s_axi_arburst<= #U_DLY 2'b01;
        end
        else
        begin
                //读地址通道
                s_axi_arid	 <= #U_DLY 4'b0;
                s_axi_araddr <= #U_DLY s_axi_araddr;
                s_axi_arlen	 <= #U_DLY s_axi_arlen;
                s_axi_arsize <= #U_DLY s_axi_arsize;
                s_axi_arburst<= #U_DLY s_axi_arburst;
                //读数据通道
                s_axi_rready <= #U_DLY 1'b1;
        end
end

//读地址有效
always@(posedge lb_clk , negedge lb_rst_n)
begin
        if(!lb_rst_n)
                s_axi_arvalid<= #U_DLY 1'b0;
        else if(c_state == read_valid)
                s_axi_arvalid<= #U_DLY 1'b1;
        else
                s_axi_arvalid<= #U_DLY 1'b0;
end

//------------------------------------------------------------
//------------------------------------------------------------

//------------------------------------------------------------
//------------------------------------------------------------
blk_mem_gen_0 u_double_ram (
    //全局信号
    .rsta_busy                  (rsta_busy                  ),          
    .rstb_busy                  (rstb_busy                  ),          
    .s_aclk                     (s_aclk                     ),
    .s_aresetn                  (s_aresetn                  ),
    //写地址通道
    .s_axi_awid                 (s_axi_awid                 ),//写地址ID        
    .s_axi_awaddr               (s_axi_awaddr               ),//写地址    
    .s_axi_awlen                (s_axi_awlen                ),//突发次数
    .s_axi_awsize               (s_axi_awsize               ),//一次传输字节数
    .s_axi_awburst              (s_axi_awburst              ),//突发类型
    .s_axi_awvalid              (s_axi_awvalid              ),//握手有效
    .s_axi_awready              (s_axi_awready              ),//握手准备好    
    //写数据通道 
    .s_axi_wdata                (s_axi_wdata                ),//写入数据     
    .s_axi_wstrb                (s_axi_wstrb                ),//表示写字节通道保存有效,在每8位的写数据总线上有1位被选通。
    .s_axi_wlast                (s_axi_wlast                ),//最后一个数据     
    .s_axi_wvalid               (s_axi_wvalid               ),//写数据有效
    .s_axi_wready               (s_axi_wready               ),//写数据准备就绪
    //写请求通道    
    .s_axi_bid                  (s_axi_bid                  ),//
    .s_axi_bresp                (s_axi_bresp                ),//写入响应   
    .s_axi_bvalid               (s_axi_bvalid               ),//写响应有效
    .s_axi_bready               (s_axi_bready               ),//已准备好写入响应
    //读地址通道
    .s_axi_arid                 (s_axi_arid                 ),//读地址ID      
    .s_axi_araddr               (s_axi_araddr               ),//低地址
    .s_axi_arlen                (s_axi_arlen                ),//读地址突发长度
    .s_axi_arsize               (s_axi_arsize               ),//一次传输字节数
    .s_axi_arburst              (s_axi_arburst              ),
    .s_axi_arvalid              (s_axi_arvalid              ),  
    .s_axi_arready              (s_axi_arready              ), 
    //读数据通道 
    .s_axi_rid                  (s_axi_rid                  ),          
    .s_axi_rdata                (s_axi_rdata                ),      
    .s_axi_rresp                (s_axi_rresp                ),      
    .s_axi_rlast                (s_axi_rlast                ),      
    .s_axi_rvalid               (s_axi_rvalid               ),    
    .s_axi_rready               (s_axi_rready               )
);

//------------------------------------------------------------
//------------------------------------------------------------

//------------------------------------------------------------
//------------------------------------------------------------
endmodule

测试文件

`timescale 1ns / 1ps
//
// Company: 
// Engineer: 
// 
// Create Date: 2021/08/04 09:23:17
// Design Name: 
// Module Name: vtf_bram_control
// Project Name: 
// Target Devices: 
// Tool Versions: 
// Description: 
// 
// Dependencies: 
// 
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
// 
//


module vtf_bram_control_burst;

reg		lb_clk				;
reg		lb_rst				;
reg	    reg_axi_write	    ;		
reg    	reg_axi_read        ;	

localparam      size = 1;
bram_control_burst u_bram_control_burst(
    .lb_clk                     (lb_clk                     ),
    .lb_rst                     (lb_rst                     ),
    .reg_axi_write              (reg_axi_write              ),
    .reg_axi_read               (reg_axi_read               )
    );

initial
begin
        lb_clk = 0;
        lb_rst = 1;
        reg_axi_write = 0;
        reg_axi_read  = 0;


        #100;
        lb_rst = 0;
        #5;

repeat(size)
begin
        #100;
        #10;
        reg_axi_write = 1;
        #10;
        reg_axi_write = 0;
end
        #1000;
repeat(size)
begin
  


        #120;
        #10;
        reg_axi_read = 1;
        #10;
        reg_axi_read = 0;
end
end

always #5 lb_clk = ~lb_clk;




endmodule

下面准备实现AXI接口之间的互相通信,能更加方便的控制AXI_BRAM。

你可能感兴趣的:(AXI总线学习记录,fpga开发)