平台: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信号同时为高的时候。
注意:
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协议的三种突发读写类型。固定式突发读写,增值式突发读写,包装式突发读写。
包装式突发读写有两个限制:
起始地址必须以传输的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,表示主机接收读数据就绪。
但是在仿真过程中却发现了问题,写地址在一个写期间地址递增。通过观察发现使我们的写地址状态持续了两个时钟周期。这样做导致地址增加了一个。所以上述的写地址赋值方式错误。
同理写数据也递增。
同理读地址也递增。读数据不对。
修改代码。
经过分析,我在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
结果仿真正确。
写数据。
读数据。
数据正常。
这样就对了。接下来试试突发写入。Burst = 2’b01,INCR增值式突发读写是指每一次读写的地址都比上一次的地址增加一个固定的值。
首先要明白AXI_BRAM的突发读写,
关于突发读第一种情况。当地址出现在地址总线上后,传输的数据将出现在读数据通道上。从设备将保持VALID为低直到读数据有效。为了表示一次突发式读写完成,设备用RLAST信号来表示最后一个被传输的数据。
第二种情况,突发读可以一次发送两个读地址。设备会在第一次突发读完成后处理第二次突发读。主机一开始传输了两个地址给设备。设备在完全处理完第一个地址的数据后开始处理第二个地址的数据。
关于AXI_BRAM的突发写。
突发写的过程一开始,主机先将地址和控制信息写到写地址通道中,然后主机发送每一个写数据到写数据通道中。当主机发送最后一个数据是,拉高WLAST信号就变为高。当设备接收完所有数据之后他将一个写响应发送回主机来表明写事务完成。
好接下来根据上面的时序来实现对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个,所以最后一个数据是没有握手成功的。
下面看读部分。
同样读地址从0开始。突发读取16个数据,采用地址递增的突发读模式。比较简单就不放代码了。
下面是读的截图。
多测试几次。
从地址5读取6个数。
地址3读取6个数。
地址4读取6个数。
综合上述的仿真结果,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。