前面对AXI4协议进行了比较详细的分析,本篇文章将会写一个主机代码来实现AXI4协议的时序。
设计思路:本次设计的主要目的是验证AXI4_FULL总线的时序,并且提升对AXI4_FULL总线协议的理解,因此可以采用状态机来控制,先向一个地址写入数据,然后读出该地址的数据验证数据的正确性,如果没有错再向下一个地址中写入数据,如此反复执行。
状态转换如下图所示:
状态机初始位于空闲状态,下个时钟跳转到写突发状态,当写应答通道握手成功时,表示写突发完成,跳转到读突发状态,当读出数据与写入数据错误时,跳转到错误状态。当读写数据一致且读突发传输最后一个数据,表示读突发完成,跳转到空闲状态开始下一次读写操作。
当读、写首地址发送之后,突发地址需要增加突发长度乘以突发字节数,作为下一次突发传输的首地址。
具体实现代码如下:
`timescale 1ns / 1ps
module axi_full_master#(
parameter C_M_TARGET_SLAVE_BASE_ADDR = 32'h40000000 , //读写目标从机的基地址
parameter C_M_AXI_BURST_LEN = 16 , //一次突发读写长度
parameter C_M_AXI_ID_WIDTH = 1 , //ID长度
parameter C_M_AXI_DATA_WIDTH = 32 , //读写数据位宽
parameter C_M_AXI_ADDR_WIDTH = 32 , //读写地址位宽
parameter C_M_AXI_AWUSER_WIDTH = 0 , //用户写地址总线位宽
parameter C_M_AXI_ARUSER_WIDTH = 0 , //用户读地址总线位宽
parameter C_M_AXI_WUSER_WIDTH = 0 , //用户写数据总线位宽
parameter C_M_AXI_RUSER_WIDTH = 0 , //用户读数据总线位宽
parameter C_M_AXI_BUSER_WIDTH = 0 //用户写响应总线位宽
)
(
input M_AXI_ACLK , //AXI时钟时钟
input M_AXI_ARESETN , //AXI复位信号
//AXI写地址通道
output reg[C_M_AXI_ADDR_WIDTH-1:0] M_AXI_AWADDR , //写地址通道地址信号
output [C_M_AXI_ID_WIDTH-1:0] M_AXI_AWID , //写地址通道ID
output [1:0] M_AXI_AWBURST , //写地址通道突发类型
output [2:0] M_AXI_AWSIZE , //写地址通道突发大小信号,该信号指示突发传输中每次传输的大小数据
output [7:0] M_AXI_AWLEN , //写地址通道突发长度信号
output M_AXI_AWLOCK , //写地址通道锁信号
output [3:0] M_AXI_AWCACHE , //写地址通道内存类型信号
output [2:0] M_AXI_AWPROT , //写地址通道保护类型信号
output [3:0] M_AXI_AWQOS , //写地址通道服务质量类型信号
output [C_M_AXI_AWUSER_WIDTH-1:0] M_AXI_AWUSER , //写地址通道用户自定义信号
output reg M_AXI_AWVALID , //写地址通道有效指示信号
input M_AXI_AWREADY , //写地址通道地址应答信号从机发送给主机
//写数据通道
output reg[C_M_AXI_DATA_WIDTH-1:0] M_AXI_WDATA , //写数据通道数据
output [C_M_AXI_DATA_WIDTH/8-1:0] M_AXI_WSTRB , //写数据通道掩码信号
output reg M_AXI_WLAST , //写数据通道最后一位数据信号
output [C_M_AXI_WUSER_WIDTH-1:0] M_AXI_WUSER , //写数据通道用户自定义信号
output reg M_AXI_WVALID , //写数据通道有效指示信号
input M_AXI_WREADY , //写数据通道数据应答信号
//写响应通道
input M_AXI_BVALID , //写响应通道有效指示信号
input [C_M_AXI_ID_WIDTH-1:0] M_AXI_BID , //写响应通道ID
input [1:0] M_AXI_BRESP , //写响应通道回复信号
input [C_M_AXI_BUSER_WIDTH-1:0] M_AXI_BUSER , //写响应通道用户自定义信号
output reg M_AXI_BREADY , //写响应通道应答信号
//读地址通道
output reg[C_M_AXI_ADDR_WIDTH-1:0] M_AXI_ARADDR , //读地址通道地址信号
output [C_M_AXI_ID_WIDTH-1:0] M_AXI_ARID , //读地址通道ID
output [7:0] M_AXI_ARLEN , //读地址通道突发长度
output [2:0] M_AXI_ARSIZE , //读地址通道突发大小信号
output [1:0] M_AXI_ARBURST , //读地址通道突发类型
output M_AXI_ARLOCK , //读地址通道锁信号
output [3:0] M_AXI_ARCACHE , //读地址通道内存类型信号
output [2:0] M_AXI_ARPROT , //读地址通道保护类型信号
output [3:0] M_AXI_ARQOS , //读地址通道服务质量类型信号
output [C_M_AXI_ARUSER_WIDTH-1:0] M_AXI_ARUSER , //读地址用户自定义信号
output reg M_AXI_ARVALID , //读地址通道有效指示信号
input M_AXI_ARREADY , //读地址通道地址应答信号
//读数据通道
input [C_M_AXI_ID_WIDTH-1:0] M_AXI_RID , //读数据通道ID
input [C_M_AXI_DATA_WIDTH-1:0] M_AXI_RDATA , //读数据通道数据
input [1:0] M_AXI_RRESP , //读数据通道回复信号
input M_AXI_RLAST , //读数据通道最后一位数据信号
input [C_M_AXI_RUSER_WIDTH-1:0] M_AXI_RUSER , //读数据通道用户自定义信号
input M_AXI_RVALID , //读数据通道有效指示信号
output reg M_AXI_RREADY //读数据通道数据应答信号
);
parameter IDLE = 4'b0001 ; //空闲状态
parameter WRITE = 4'b0010 ; //写数据状态
parameter READ = 4'b0100 ; //读数据状态
parameter ERROR = 4'b1000 ; //数据错误状态
parameter SIZE = clogb2(C_M_AXI_DATA_WIDTH/8-1);//突发数据字节位宽数
parameter CNT_W = clogb2(C_M_AXI_BURST_LEN - 1); //通过突发长度计算计数器位宽
reg [3:0] state ; //现态
reg [3:0] next_state ; //次态
reg error_flag ; //错误指示信号
reg [CNT_W-1:0] wr_cnt ; //
reg [CNT_W-1:0] rd_cnt ; //
reg [1:0] cnt ;
//写地址通道
assign M_AXI_AWID = 0 ; //写地址ID
assign M_AXI_AWBURST = 2'b01 ; //该突发类型为递增类型,即读写地址依次递增
assign M_AXI_AWSIZE = SIZE ; //突发数据的位宽字节数
assign M_AXI_AWLEN = C_M_AXI_BURST_LEN - 1 ; //写地址突发长度
assign M_AXI_AWLOCK = 0 ; //写地址通道锁信号
assign M_AXI_AWPROT = 3'd0 ; //写地址端口为0
assign M_AXI_AWQOS = 4'd0 ; //不使用
assign M_AXI_AWCACHE = 4'b0010 ; //不使用缓存
assign M_AXI_AWUSER = 0 ; //不使用写地址通道用户信号
//写数据通道
assign M_AXI_WSTRB = {{C_M_AXI_DATA_WIDTH/8}{1'b1}} ; //将掩码信号拉高,即不使用掩码
assign M_AXI_WUSER = 0 ; //不使用写数据通道用户信号
//读地址通道
assign M_AXI_ARID = 0 ; //写地址ID
assign M_AXI_ARBURST = 2'b01 ; //该突发类型为递增类型,即读写地址依次递增
assign M_AXI_ARSIZE = SIZE ; //突发数据的位宽字节数
assign M_AXI_ARLEN = C_M_AXI_BURST_LEN - 1 ; //写地址突发长度
assign M_AXI_ARLOCK = 0 ; //写地址通道锁信号
assign M_AXI_ARPROT = 3'd0 ; //写地址端口为0
assign M_AXI_ARQOS = 4'd0 ; //不使用
assign M_AXI_ARCACHE = 4'b0010 ; //不使用缓存
assign M_AXI_ARUSER = 0 ; //不使用写地址通道用户信号
//自动计算位宽函数。
function integer clogb2(input integer depth);begin
if(depth == 0)
clogb2 = 1;
else if(depth != 0)
for(clogb2=0 ; depth>0 ; clogb2=clogb2+1)
depth=depth >> 1;
end
endfunction
always @(negedge M_AXI_ACLK or negedge M_AXI_ARESETN) begin
if(!M_AXI_ARESETN)
state <= IDLE;
else
state <= next_state;
end
always @(*) begin
next_state = IDLE;
case(state)
IDLE: next_state = WRITE;
WRITE: begin
if(M_AXI_BREADY && M_AXI_BVALID) //写响应通道握手成功
next_state = READ;
else
next_state = WRITE;
end
READ: begin
if(M_AXI_RLAST && M_AXI_ARVALID) //当前数据是最后一位且读地址有效信号拉高
next_state = IDLE;
else if(error_flag) //错误信号拉高
next_state = ERROR;
else
next_state = READ;
end
ERROR:
next_state = ERROR;
default: next_state = IDLE;
endcase
end
//写地址通道信号
always @(posedge M_AXI_ACLK or negedge M_AXI_ARESETN) begin
if(!M_AXI_ARESETN)
M_AXI_AWADDR <= C_M_TARGET_SLAVE_BASE_ADDR; //复位后为基地址
else if(M_AXI_BREADY && M_AXI_BVALID) //写响应通道成功响应,注意此处不是写地址通道成功握手
M_AXI_AWADDR <= M_AXI_AWADDR + (C_M_AXI_BURST_LEN * C_M_AXI_DATA_WIDTH/8); //跳过被写数据的地址
end
always @(posedge M_AXI_ACLK or negedge M_AXI_ARESETN) begin
if(!M_AXI_ARESETN)
M_AXI_AWVALID <= 1'b0;
else if(M_AXI_AWREADY) //当应答信号有效时拉低
M_AXI_AWVALID <= 1'b0;
else if(state == IDLE && next_state == WRITE) //进入写状态拉高
M_AXI_AWVALID <= 1'b1;
end
//写数据通道信号
always @(posedge M_AXI_ACLK or negedge M_AXI_ARESETN) begin
if(!M_AXI_ARESETN)
wr_cnt <= 'd0;
else if(state != WRITE) //当前状态不处于写状态清零
wr_cnt <= 'd0;
else if(M_AXI_WREADY && M_AXI_WVALID) //写入一次数据
wr_cnt <= wr_cnt + 1'b1;
end
always @(posedge M_AXI_ACLK or negedge M_AXI_ARESETN) begin
if(!M_AXI_ARESETN)
M_AXI_WDATA <= 'd0;
else if(M_AXI_WREADY && M_AXI_WVALID) //每写入一次数据加一
M_AXI_WDATA <= M_AXI_WDATA + 1'b1;
end
always @(posedge M_AXI_ACLK or negedge M_AXI_ARESETN) begin
if(!M_AXI_ARESETN)
M_AXI_WVALID <= 1'b0;
else if(wr_cnt == C_M_AXI_BURST_LEN-1 && M_AXI_WVALID && M_AXI_WREADY) //写完最后一个数据且写通道握手成功
M_AXI_WVALID <= 1'b0;
else if(state == IDLE && next_state == WRITE) //进入写状态拉高
M_AXI_WVALID <= 1'b1;
end
always @(posedge M_AXI_ACLK or negedge M_AXI_ARESETN) begin
if(!M_AXI_ARESETN)
M_AXI_WLAST <= 1'b0;
else if(M_AXI_WREADY && M_AXI_WVALID)
if(wr_cnt == C_M_AXI_BURST_LEN-1) //最后一次数据写入完成后拉低
M_AXI_WLAST <= 1'b0;
else if(wr_cnt == C_M_AXI_BURST_LEN-2) //将要写最后一次数据的时候拉高
M_AXI_WLAST <= 1'b1;
else
M_AXI_WLAST <= 1'b0; //否则保持为低
end
//写响应通道
always @(posedge M_AXI_ACLK or negedge M_AXI_ARESETN) begin
if(!M_AXI_ARESETN)
M_AXI_BREADY <= 1'b0;
else if(M_AXI_BVALID) //与从机握手成功后拉低
M_AXI_BREADY <= 1'b0;
else if(M_AXI_WLAST && M_AXI_WREADY && M_AXI_WVALID) //写入最后一位数据后拉高
M_AXI_BREADY <= 1'b1;
end
//读地址通道
always @(posedge M_AXI_ACLK or negedge M_AXI_ARESETN) begin
if(!M_AXI_ARESETN)
M_AXI_ARADDR <= 'd0;
else if(M_AXI_RLAST) //读完最后一个数据跳过已经读过的地址
M_AXI_ARADDR <= M_AXI_ARADDR + (C_M_AXI_BURST_LEN * C_M_AXI_DATA_WIDTH/8);
end
always @(posedge M_AXI_ACLK or negedge M_AXI_ARESETN) begin
if(!M_AXI_ARESETN)
cnt <= 2'd0;
else if(state == READ)
if(cnt == 2'd2)
cnt <= cnt;
else
cnt <= cnt + 1'b1;
else
cnt <= 2'd0;
end
always @(posedge M_AXI_ACLK or negedge M_AXI_ARESETN) begin
if(!M_AXI_ARESETN)
M_AXI_ARVALID <= 1'b0;
else if(M_AXI_ARREADY) //握手成功后拉低
M_AXI_ARVALID <= 1'b0;
else if(cnt == 2'd1) //进入读状态后拉高
M_AXI_ARVALID <= 1'b1;
end
//读数据通道
always @(posedge M_AXI_ACLK or negedge M_AXI_ARESETN) begin
if(!M_AXI_ARESETN)
rd_cnt <= 'd0;
else if(wr_cnt == C_M_AXI_BURST_LEN-1)
rd_cnt <= 'd0;
else if(M_AXI_RREADY && M_AXI_RVALID) //每读出一个数据加一
rd_cnt <= rd_cnt + 1'b1;
end
always @(posedge M_AXI_ACLK or negedge M_AXI_ARESETN) begin
if(!M_AXI_ARESETN)
M_AXI_RREADY <= 1'b0;
else if(M_AXI_RLAST) //读出最后一个数据后拉低
M_AXI_RREADY <= 1'b0;
else if(M_AXI_ARVALID && M_AXI_ARREADY) //写地址通道握手成功后拉高,代表可以接受数据
M_AXI_RREADY <= 1'b1;
end
//错误指示信号
always @(posedge M_AXI_ACLK or negedge M_AXI_ARESETN) begin
if(!M_AXI_ARESETN)
error_flag <= 1'b0;
else if(M_AXI_WVALID && M_AXI_WREADY && state == READ)
error_flag <= (M_AXI_ARADDR + C_M_TARGET_SLAVE_BASE_ADDR - rd_cnt != M_AXI_RDATA);//读出的数据不等于写入数据
end
endmodule
`timescale 1 ns / 1 ps
module axi_full_slave #
(
// Users to add parameters here
// User parameters ends
// Do not modify the parameters beyond this line
// Width of ID for for write address, write data, read address and read data
parameter integer C_S_AXI_ID_WIDTH = 1,
// Width of S_AXI data bus
parameter integer C_S_AXI_DATA_WIDTH = 32,
// Width of S_AXI address bus
parameter integer C_S_AXI_ADDR_WIDTH = 10,
// Width of optional user defined signal in write address channel
parameter integer C_S_AXI_AWUSER_WIDTH = 0,
// Width of optional user defined signal in read address channel
parameter integer C_S_AXI_ARUSER_WIDTH = 0,
// Width of optional user defined signal in write data channel
parameter integer C_S_AXI_WUSER_WIDTH = 0,
// Width of optional user defined signal in read data channel
parameter integer C_S_AXI_RUSER_WIDTH = 0,
// Width of optional user defined signal in write response channel
parameter integer C_S_AXI_BUSER_WIDTH = 0
)
(
// Users to add ports here
// User ports ends
// Do not modify the ports beyond this line
// Global Clock Signal
input wire S_AXI_ACLK,
// Global Reset Signal. This Signal is Active LOW
input wire S_AXI_ARESETN,
// Write Address ID
input wire [C_S_AXI_ID_WIDTH-1 : 0] S_AXI_AWID,
// Write address
input wire [C_S_AXI_ADDR_WIDTH-1 : 0] S_AXI_AWADDR,
// Burst length. The burst length gives the exact number of transfers in a burst
input wire [7 : 0] S_AXI_AWLEN,
// Burst size. This signal indicates the size of each transfer in the burst
input wire [2 : 0] S_AXI_AWSIZE,
// Burst type. The burst type and the size information,
// determine how the address for each transfer within the burst is calculated.
input wire [1 : 0] S_AXI_AWBURST,
// Lock type. Provides additional information about the
// atomic characteristics of the transfer.
input wire S_AXI_AWLOCK,
// Memory type. This signal indicates how transactions
// are required to progress through a system.
input wire [3 : 0] S_AXI_AWCACHE,
// Protection type. This signal indicates the privilege
// and security level of the transaction, and whether
// the transaction is a data access or an instruction access.
input wire [2 : 0] S_AXI_AWPROT,
// Quality of Service, QoS identifier sent for each
// write transaction.
input wire [3 : 0] S_AXI_AWQOS,
// Region identifier. Permits a single physical interface
// on a slave to be used for multiple logical interfaces.
input wire [3 : 0] S_AXI_AWREGION,
// Optional User-defined signal in the write address channel.
input wire [C_S_AXI_AWUSER_WIDTH-1 : 0] S_AXI_AWUSER,
// Write address valid. This signal indicates that
// the channel is signaling valid write address and
// control information.
input wire S_AXI_AWVALID,
// Write address ready. This signal indicates that
// the slave is ready to accept an address and associated
// control signals.
output wire S_AXI_AWREADY,
// Write Data
input wire [C_S_AXI_DATA_WIDTH-1 : 0] S_AXI_WDATA,
// Write strobes. This signal indicates which byte
// lanes hold valid data. There is one write strobe
// bit for each eight bits of the write data bus.
input wire [(C_S_AXI_DATA_WIDTH/8)-1 : 0] S_AXI_WSTRB,
// Write last. This signal indicates the last transfer
// in a write burst.
input wire S_AXI_WLAST,
// Optional User-defined signal in the write data channel.
input wire [C_S_AXI_WUSER_WIDTH-1 : 0] S_AXI_WUSER,
// Write valid. This signal indicates that valid write
// data and strobes are available.
input wire S_AXI_WVALID,
// Write ready. This signal indicates that the slave
// can accept the write data.
output wire S_AXI_WREADY,
// Response ID tag. This signal is the ID tag of the
// write response.
output wire [C_S_AXI_ID_WIDTH-1 : 0] S_AXI_BID,
// Write response. This signal indicates the status
// of the write transaction.
output wire [1 : 0] S_AXI_BRESP,
// Optional User-defined signal in the write response channel.
output wire [C_S_AXI_BUSER_WIDTH-1 : 0] S_AXI_BUSER,
// Write response valid. This signal indicates that the
// channel is signaling a valid write response.
output wire S_AXI_BVALID,
// Response ready. This signal indicates that the master
// can accept a write response.
input wire S_AXI_BREADY,
// Read address ID. This signal is the identification
// tag for the read address group of signals.
input wire [C_S_AXI_ID_WIDTH-1 : 0] S_AXI_ARID,
// Read address. This signal indicates the initial
// address of a read burst transaction.
input wire [C_S_AXI_ADDR_WIDTH-1 : 0] S_AXI_ARADDR,
// Burst length. The burst length gives the exact number of transfers in a burst
input wire [7 : 0] S_AXI_ARLEN,
// Burst size. This signal indicates the size of each transfer in the burst
input wire [2 : 0] S_AXI_ARSIZE,
// Burst type. The burst type and the size information,
// determine how the address for each transfer within the burst is calculated.
input wire [1 : 0] S_AXI_ARBURST,
// Lock type. Provides additional information about the
// atomic characteristics of the transfer.
input wire S_AXI_ARLOCK,
// Memory type. This signal indicates how transactions
// are required to progress through a system.
input wire [3 : 0] S_AXI_ARCACHE,
// Protection type. This signal indicates the privilege
// and security level of the transaction, and whether
// the transaction is a data access or an instruction access.
input wire [2 : 0] S_AXI_ARPROT,
// Quality of Service, QoS identifier sent for each
// read transaction.
input wire [3 : 0] S_AXI_ARQOS,
// Region identifier. Permits a single physical interface
// on a slave to be used for multiple logical interfaces.
input wire [3 : 0] S_AXI_ARREGION,
// Optional User-defined signal in the read address channel.
input wire [C_S_AXI_ARUSER_WIDTH-1 : 0] S_AXI_ARUSER,
// Write address valid. This signal indicates that
// the channel is signaling valid read address and
// control information.
input wire S_AXI_ARVALID,
// Read address ready. This signal indicates that
// the slave is ready to accept an address and associated
// control signals.
output wire S_AXI_ARREADY,
// Read ID tag. This signal is the identification tag
// for the read data group of signals generated by the slave.
output wire [C_S_AXI_ID_WIDTH-1 : 0] S_AXI_RID,
// Read Data
output wire [C_S_AXI_DATA_WIDTH-1 : 0] S_AXI_RDATA,
// Read response. This signal indicates the status of
// the read transfer.
output wire [1 : 0] S_AXI_RRESP,
// Read last. This signal indicates the last transfer
// in a read burst.
output wire S_AXI_RLAST,
// Optional User-defined signal in the read address channel.
output wire [C_S_AXI_RUSER_WIDTH-1 : 0] S_AXI_RUSER,
// Read valid. This signal indicates that the channel
// is signaling the required read data.
output wire S_AXI_RVALID,
// Read ready. This signal indicates that the master can
// accept the read data and response information.
input wire S_AXI_RREADY
);
// AXI4FULL signals
reg [C_S_AXI_ADDR_WIDTH-1 : 0] axi_awaddr;
reg axi_awready;
reg axi_wready;
reg [1 : 0] axi_bresp;
reg [C_S_AXI_BUSER_WIDTH-1 : 0] axi_buser;
reg axi_bvalid;
reg [C_S_AXI_ADDR_WIDTH-1 : 0] axi_araddr;
reg axi_arready;
reg [C_S_AXI_DATA_WIDTH-1 : 0] axi_rdata;
reg [1 : 0] axi_rresp;
reg axi_rlast;
reg [C_S_AXI_RUSER_WIDTH-1 : 0] axi_ruser;
reg axi_rvalid;
// aw_wrap_en determines wrap boundary and enables wrapping
wire aw_wrap_en;
// ar_wrap_en determines wrap boundary and enables wrapping
wire ar_wrap_en;
// aw_wrap_size is the size of the write transfer, the
// write address wraps to a lower address if upper address
// limit is reached
wire [31:0] aw_wrap_size ;
// ar_wrap_size is the size of the read transfer, the
// read address wraps to a lower address if upper address
// limit is reached
wire [31:0] ar_wrap_size ;
// The axi_awv_awr_flag flag marks the presence of write address valid
reg axi_awv_awr_flag;
//The axi_arv_arr_flag flag marks the presence of read address valid
reg axi_arv_arr_flag;
// The axi_awlen_cntr internal write address counter to keep track of beats in a burst transaction
reg [7:0] axi_awlen_cntr;
//The axi_arlen_cntr internal read address counter to keep track of beats in a burst transaction
reg [7:0] axi_arlen_cntr;
reg [1:0] axi_arburst;
reg [1:0] axi_awburst;
reg [7:0] axi_arlen;
reg [7:0] axi_awlen;
//local parameter for addressing 32 bit / 64 bit C_S_AXI_DATA_WIDTH
//ADDR_LSB is used for addressing 32/64 bit registers/memories
//ADDR_LSB = 2 for 32 bits (n downto 2)
//ADDR_LSB = 3 for 42 bits (n downto 3)
localparam integer ADDR_LSB = (C_S_AXI_DATA_WIDTH/32)+ 1;
localparam integer OPT_MEM_ADDR_BITS = 7;
localparam integer USER_NUM_MEM = 1;
//----------------------------------------------
//-- Signals for user logic memory space example
//------------------------------------------------
wire [OPT_MEM_ADDR_BITS:0] mem_address;
wire [USER_NUM_MEM-1:0] mem_select;
reg [C_S_AXI_DATA_WIDTH-1:0] mem_data_out[0 : USER_NUM_MEM-1];
genvar i;
genvar j;
genvar mem_byte_index;
// I/O Connections assignments
assign S_AXI_AWREADY= axi_awready;
assign S_AXI_WREADY = axi_wready;
assign S_AXI_BRESP = axi_bresp;
assign S_AXI_BUSER = axi_buser;
assign S_AXI_BVALID = axi_bvalid;
assign S_AXI_ARREADY = axi_arready;
assign S_AXI_RDATA = axi_rdata;
assign S_AXI_RRESP = axi_rresp;
assign S_AXI_RLAST = axi_rlast;
assign S_AXI_RUSER = axi_ruser;
assign S_AXI_RVALID = axi_rvalid;
assign S_AXI_BID = S_AXI_AWID;
assign S_AXI_RID = S_AXI_ARID;
assign aw_wrap_size = (C_S_AXI_DATA_WIDTH/8 * (axi_awlen));
assign ar_wrap_size = (C_S_AXI_DATA_WIDTH/8 * (axi_arlen));
assign aw_wrap_en = ((axi_awaddr & aw_wrap_size) == aw_wrap_size)? 1'b1: 1'b0;
assign ar_wrap_en = ((axi_araddr & ar_wrap_size) == ar_wrap_size)? 1'b1: 1'b0;
// Implement axi_awready generation
// axi_awready is asserted for one S_AXI_ACLK clock cycle when both
// S_AXI_AWVALID and S_AXI_WVALID are asserted. axi_awready is
// de-asserted when reset is low.
always @( posedge S_AXI_ACLK )
begin
if ( S_AXI_ARESETN == 1'b0 )
begin
axi_awready <= 1'b0;
axi_awv_awr_flag <= 1'b0;
end
else
begin
if (~axi_awready && S_AXI_AWVALID && ~axi_awv_awr_flag && ~axi_arv_arr_flag)
//if (~axi_awready && S_AXI_AWVALID)
begin
// slave is ready to accept an address and
// associated control signals
axi_awready <= 1'b1;
axi_awv_awr_flag <= 1'b1;
// used for generation of bresp() and bvalid
end
else if (S_AXI_WLAST && axi_wready)
// preparing to accept next address after current write burst tx completion
begin
axi_awv_awr_flag <= 1'b0;
end
else
begin
axi_awready <= 1'b0;
end
end
end
// Implement axi_awaddr latching
// This process is used to latch the address when both
// S_AXI_AWVALID and S_AXI_WVALID are valid.
always @( posedge S_AXI_ACLK )
begin
if ( S_AXI_ARESETN == 1'b0 )
begin
axi_awaddr <= 0;
axi_awlen_cntr <= 0;
axi_awburst <= 0;
axi_awlen <= 0;
end
else
begin
if (~axi_awready && S_AXI_AWVALID && ~axi_awv_awr_flag)
begin
// address latching
axi_awaddr <= S_AXI_AWADDR[C_S_AXI_ADDR_WIDTH - 1:0];
axi_awburst <= S_AXI_AWBURST;
axi_awlen <= S_AXI_AWLEN;
// start address of transfer
axi_awlen_cntr <= 0;
end
else if((axi_awlen_cntr <= axi_awlen) && axi_wready && S_AXI_WVALID)
begin
axi_awlen_cntr <= axi_awlen_cntr + 1;
case (axi_awburst)
2'b00: // fixed burst
// The write address for all the beats in the transaction are fixed
begin
axi_awaddr <= axi_awaddr;
//for awsize = 4 bytes (010)
end
2'b01: //incremental burst
// The write address for all the beats in the transaction are increments by awsize
begin
axi_awaddr[C_S_AXI_ADDR_WIDTH - 1:ADDR_LSB] <= axi_awaddr[C_S_AXI_ADDR_WIDTH - 1:ADDR_LSB] + 1;
//awaddr aligned to 4 byte boundary
axi_awaddr[ADDR_LSB-1:0] <= {ADDR_LSB{1'b0}};
//for awsize = 4 bytes (010)
end
2'b10: //Wrapping burst
// The write address wraps when the address reaches wrap boundary
if (aw_wrap_en)
begin
axi_awaddr <= (axi_awaddr - aw_wrap_size);
end
else
begin
axi_awaddr[C_S_AXI_ADDR_WIDTH - 1:ADDR_LSB] <= axi_awaddr[C_S_AXI_ADDR_WIDTH - 1:ADDR_LSB] + 1;
axi_awaddr[ADDR_LSB-1:0] <= {ADDR_LSB{1'b0}};
end
default: //reserved (incremental burst for example)
begin
axi_awaddr <= axi_awaddr[C_S_AXI_ADDR_WIDTH - 1:ADDR_LSB] + 1;
//for awsize = 4 bytes (010)
end
endcase
end
end
end
// Implement axi_wready generation
// axi_wready is asserted for one S_AXI_ACLK clock cycle when both
// S_AXI_AWVALID and S_AXI_WVALID are asserted. axi_wready is
// de-asserted when reset is low.
always @( posedge S_AXI_ACLK )
begin
if ( S_AXI_ARESETN == 1'b0 )
begin
axi_wready <= 1'b0;
end
else
begin
if ( ~axi_wready && S_AXI_WVALID && axi_awv_awr_flag)
begin
// slave can accept the write data
axi_wready <= 1'b1;
end
//else if (~axi_awv_awr_flag)
else if (S_AXI_WLAST && axi_wready)
begin
axi_wready <= 1'b0;
end
end
end
// Implement write response logic generation
// The write response and response valid signals are asserted by the slave
// when axi_wready, S_AXI_WVALID, axi_wready and S_AXI_WVALID are asserted.
// This marks the acceptance of address and indicates the status of
// write transaction.
always @( posedge S_AXI_ACLK )
begin
if ( S_AXI_ARESETN == 1'b0 )
begin
axi_bvalid <= 0;
axi_bresp <= 2'b0;
axi_buser <= 0;
end
else
begin
if (axi_awv_awr_flag && axi_wready && S_AXI_WVALID && ~axi_bvalid && S_AXI_WLAST )
begin
axi_bvalid <= 1'b1;
axi_bresp <= 2'b0;
// 'OKAY' response
end
else
begin
if (S_AXI_BREADY && axi_bvalid)
//check if bready is asserted while bvalid is high)
//(there is a possibility that bready is always asserted high)
begin
axi_bvalid <= 1'b0;
end
end
end
end
// Implement axi_arready generation
// axi_arready is asserted for one S_AXI_ACLK clock cycle when
// S_AXI_ARVALID is asserted. axi_awready is
// de-asserted when reset (active low) is asserted.
// The read address is also latched when S_AXI_ARVALID is
// asserted. axi_araddr is reset to zero on reset assertion.
always @( posedge S_AXI_ACLK )
begin
if ( S_AXI_ARESETN == 1'b0 )
begin
axi_arready <= 1'b0;
axi_arv_arr_flag <= 1'b0;
end
else
begin
if (~axi_arready && S_AXI_ARVALID && ~axi_awv_awr_flag && ~axi_arv_arr_flag)
begin
axi_arready <= 1'b1;
axi_arv_arr_flag <= 1'b1;
end
else if (axi_rvalid && S_AXI_RREADY && axi_arlen_cntr == axi_arlen)
// preparing to accept next address after current read completion
begin
axi_arv_arr_flag <= 1'b0;
end
else
begin
axi_arready <= 1'b0;
end
end
end
// Implement axi_araddr latching
//This process is used to latch the address when both
//S_AXI_ARVALID and S_AXI_RVALID are valid.
always @( posedge S_AXI_ACLK )
begin
if ( S_AXI_ARESETN == 1'b0 )
begin
axi_araddr <= 0;
axi_arlen_cntr <= 0;
axi_arburst <= 0;
axi_arlen <= 0;
axi_rlast <= 1'b0;
axi_ruser <= 0;
end
else
begin
if (~axi_arready && S_AXI_ARVALID && ~axi_arv_arr_flag)
begin
// address latching
axi_araddr <= S_AXI_ARADDR[C_S_AXI_ADDR_WIDTH - 1:0];
axi_arburst <= S_AXI_ARBURST;
axi_arlen <= S_AXI_ARLEN;
// start address of transfer
axi_arlen_cntr <= 0;
axi_rlast <= 1'b0;
end
else if((axi_arlen_cntr <= axi_arlen) && axi_rvalid && S_AXI_RREADY)
begin
axi_arlen_cntr <= axi_arlen_cntr + 1;
axi_rlast <= 1'b0;
case (axi_arburst)
2'b00: // fixed burst
// The read address for all the beats in the transaction are fixed
begin
axi_araddr <= axi_araddr;
//for arsize = 4 bytes (010)
end
2'b01: //incremental burst
// The read address for all the beats in the transaction are increments by awsize
begin
axi_araddr[C_S_AXI_ADDR_WIDTH - 1:ADDR_LSB] <= axi_araddr[C_S_AXI_ADDR_WIDTH - 1:ADDR_LSB] + 1;
//araddr aligned to 4 byte boundary
axi_araddr[ADDR_LSB-1:0] <= {ADDR_LSB{1'b0}};
//for awsize = 4 bytes (010)
end
2'b10: //Wrapping burst
// The read address wraps when the address reaches wrap boundary
if (ar_wrap_en)
begin
axi_araddr <= (axi_araddr - ar_wrap_size);
end
else
begin
axi_araddr[C_S_AXI_ADDR_WIDTH - 1:ADDR_LSB] <= axi_araddr[C_S_AXI_ADDR_WIDTH - 1:ADDR_LSB] + 1;
//araddr aligned to 4 byte boundary
axi_araddr[ADDR_LSB-1:0] <= {ADDR_LSB{1'b0}};
end
default: //reserved (incremental burst for example)
begin
axi_araddr <= axi_araddr[C_S_AXI_ADDR_WIDTH - 1:ADDR_LSB]+1;
//for arsize = 4 bytes (010)
end
endcase
end
else if((axi_arlen_cntr == axi_arlen) && ~axi_rlast && axi_arv_arr_flag )
begin
axi_rlast <= 1'b1;
end
else if (S_AXI_RREADY)
begin
axi_rlast <= 1'b0;
end
end
end
// Implement axi_arvalid generation
// axi_rvalid is asserted for one S_AXI_ACLK clock cycle when both
// S_AXI_ARVALID and axi_arready are asserted. The slave registers
// data are available on the axi_rdata bus at this instance. The
// assertion of axi_rvalid marks the validity of read data on the
// bus and axi_rresp indicates the status of read transaction.axi_rvalid
// is deasserted on reset (active low). axi_rresp and axi_rdata are
// cleared to zero on reset (active low).
always @( posedge S_AXI_ACLK )
begin
if ( S_AXI_ARESETN == 1'b0 )
begin
axi_rvalid <= 0;
axi_rresp <= 0;
end
else
begin
if (axi_arv_arr_flag && ~axi_rvalid)
begin
axi_rvalid <= 1'b1;
axi_rresp <= 2'b0;
// 'OKAY' response
end
else if (axi_rvalid && S_AXI_RREADY)
begin
axi_rvalid <= 1'b0;
end
end
end
// ------------------------------------------
// -- Example code to access user logic memory region
// ------------------------------------------
generate
if (USER_NUM_MEM >= 1)
begin
assign mem_select = 1;
assign mem_address = (axi_arv_arr_flag? axi_araddr[ADDR_LSB+OPT_MEM_ADDR_BITS:ADDR_LSB]:(axi_awv_awr_flag? axi_awaddr[ADDR_LSB+OPT_MEM_ADDR_BITS:ADDR_LSB]:0));
end
endgenerate
// implement Block RAM(s)
generate
for(i=0; i<= USER_NUM_MEM-1; i=i+1)
begin:BRAM_GEN
wire mem_rden;
wire mem_wren;
assign mem_wren = axi_wready && S_AXI_WVALID ;
assign mem_rden = axi_arv_arr_flag ; //& ~axi_rvalid
for(mem_byte_index=0; mem_byte_index<= (C_S_AXI_DATA_WIDTH/8-1); mem_byte_index=mem_byte_index+1)
begin:BYTE_BRAM_GEN
wire [8-1:0] data_in ;
wire [8-1:0] data_out;
reg [8-1:0] byte_ram [0 : 255];
integer j;
//assigning 8 bit data
assign data_in = S_AXI_WDATA[(mem_byte_index*8+7) -: 8];
assign data_out = byte_ram[mem_address];
always @( posedge S_AXI_ACLK )
begin
if (mem_wren && S_AXI_WSTRB[mem_byte_index])
begin
byte_ram[mem_address] <= data_in;
end
end
always @( posedge S_AXI_ACLK )
begin
if (mem_rden)
begin
mem_data_out[i][(mem_byte_index*8+7) -: 8] <= data_out;
end
end
end
end
endgenerate
//Output register or memory read data
always @( mem_data_out, axi_rvalid)
begin
if (axi_rvalid)
begin
// Read address mux
axi_rdata <= mem_data_out[0];
end
else
begin
axi_rdata <= 32'h00000000;
end
end
// Add user logic here
// User logic ends
endmodule
`timescale 1ns / 1ps
module tb_axi_full_master;
parameter CYCLE = 10 ;//系统时钟周期,单位ns,默认10ns;
parameter RST_TIME = 10 ;//系统复位持续时间,默认10个系统时钟周期;
// axi_full_master Parameters
parameter PERIOD = 10 ;
parameter C_M_TARGET_SLAVE_BASE_ADDR = 32'h40000000 ;
parameter C_M_AXI_BURST_LEN = 16 ;
parameter C_M_AXI_ID_WIDTH = 1 ;
parameter C_M_AXI_DATA_WIDTH = 32 ;
parameter C_M_AXI_ADDR_WIDTH = 32 ;
parameter C_M_AXI_AWUSER_WIDTH = 0 ;
parameter C_M_AXI_ARUSER_WIDTH = 0 ;
parameter C_M_AXI_WUSER_WIDTH = 0 ;
parameter C_M_AXI_RUSER_WIDTH = 0 ;
parameter C_M_AXI_BUSER_WIDTH = 0 ;
// axi_full_master Inputs
reg clk = 0 ;
reg rst_n = 0 ;
wire M_AXI_AWREADY ;
wire M_AXI_WREADY ;
wire M_AXI_BVALID ;
wire [C_M_AXI_ID_WIDTH-1:0] M_AXI_BID ;
wire [1:0] M_AXI_BRESP ;
wire [C_M_AXI_BUSER_WIDTH-1:0] M_AXI_BUSER;
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 [C_M_AXI_RUSER_WIDTH-1:0] M_AXI_RUSER;
wire M_AXI_RVALID ;
// axi_full_master Outputs
wire [C_M_AXI_ADDR_WIDTH-1:0]M_AXI_AWADDR ;
wire [C_M_AXI_ID_WIDTH-1:0] M_AXI_AWID ;
wire [1:0] M_AXI_AWBURST ;
wire [2:0] M_AXI_AWSIZE ;
wire [7:0] M_AXI_AWLEN ;
wire M_AXI_AWLOCK ;
wire [3:0] M_AXI_AWCACHE ;
wire [2:0] M_AXI_AWPROT ;
wire [3:0] M_AXI_AWQOS ;
wire [C_M_AXI_AWUSER_WIDTH-1:0] M_AXI_AWUSER ;
wire M_AXI_AWVALID ;
wire [C_M_AXI_DATA_WIDTH-1:0] M_AXI_WDATA ;
wire [C_M_AXI_DATA_WIDTH/8-1:0] M_AXI_WSTRB ;
wire M_AXI_WLAST ;
wire [C_M_AXI_WUSER_WIDTH-1:0] M_AXI_WUSER ;
wire M_AXI_WVALID ;
wire M_AXI_BREADY ;
wire [C_M_AXI_ADDR_WIDTH-1:0] M_AXI_ARADDR ;
wire [C_M_AXI_ID_WIDTH-1:0] M_AXI_ARID ;
wire [7:0] M_AXI_ARLEN ;
wire [2:0] M_AXI_ARSIZE ;
wire [1:0] M_AXI_ARBURST ;
wire M_AXI_ARLOCK ;
wire [3:0] M_AXI_ARCACHE ;
wire [2:0] M_AXI_ARPROT ;
wire [3:0] M_AXI_ARQOS ;
wire [C_M_AXI_ARUSER_WIDTH-1:0] M_AXI_ARUSER ;
wire M_AXI_ARVALID ;
wire M_AXI_RREADY ;
initial
begin
forever #(PERIOD/2) clk=~clk;
end
axi_full_master #(
.C_M_TARGET_SLAVE_BASE_ADDR ( C_M_TARGET_SLAVE_BASE_ADDR ),
.C_M_AXI_BURST_LEN ( C_M_AXI_BURST_LEN ),
.C_M_AXI_ID_WIDTH ( C_M_AXI_ID_WIDTH ),
.C_M_AXI_DATA_WIDTH ( C_M_AXI_DATA_WIDTH ),
.C_M_AXI_ADDR_WIDTH ( C_M_AXI_ADDR_WIDTH ),
.C_M_AXI_AWUSER_WIDTH ( C_M_AXI_AWUSER_WIDTH ),
.C_M_AXI_ARUSER_WIDTH ( C_M_AXI_ARUSER_WIDTH ),
.C_M_AXI_WUSER_WIDTH ( C_M_AXI_WUSER_WIDTH ),
.C_M_AXI_RUSER_WIDTH ( C_M_AXI_RUSER_WIDTH ),
.C_M_AXI_BUSER_WIDTH ( C_M_AXI_BUSER_WIDTH )
)
u_axi_full_master (
.M_AXI_ACLK ( clk ),
.M_AXI_ARESETN ( rst_n ),
.M_AXI_AWREADY ( M_AXI_AWREADY ),
.M_AXI_WREADY ( M_AXI_WREADY ),
.M_AXI_BVALID ( M_AXI_BVALID ),
.M_AXI_BID ( M_AXI_BID ),
.M_AXI_BRESP ( M_AXI_BRESP ),
.M_AXI_BUSER ( M_AXI_BUSER ),
.M_AXI_ARREADY ( M_AXI_ARREADY ),
.M_AXI_RID ( M_AXI_RID ),
.M_AXI_RDATA ( M_AXI_RDATA ),
.M_AXI_RRESP ( M_AXI_RRESP ),
.M_AXI_RLAST ( M_AXI_RLAST ),
.M_AXI_RUSER ( M_AXI_RUSER ),
.M_AXI_RVALID ( M_AXI_RVALID ),
.M_AXI_AWADDR ( M_AXI_AWADDR ),
.M_AXI_AWID ( M_AXI_AWID ),
.M_AXI_AWBURST ( M_AXI_AWBURST ),
.M_AXI_AWSIZE ( M_AXI_AWSIZE ),
.M_AXI_AWLEN ( M_AXI_AWLEN ),
.M_AXI_AWLOCK ( M_AXI_AWLOCK ),
.M_AXI_AWCACHE ( M_AXI_AWCACHE ),
.M_AXI_AWPROT ( M_AXI_AWPROT ),
.M_AXI_AWQOS ( M_AXI_AWQOS ),
.M_AXI_AWUSER ( M_AXI_AWUSER ),
.M_AXI_AWVALID ( M_AXI_AWVALID ),
.M_AXI_WDATA ( M_AXI_WDATA ),
.M_AXI_WSTRB ( M_AXI_WSTRB ),
.M_AXI_WLAST ( M_AXI_WLAST ),
.M_AXI_WUSER ( M_AXI_WUSER ),
.M_AXI_WVALID ( M_AXI_WVALID ),
.M_AXI_BREADY ( M_AXI_BREADY ),
.M_AXI_ARADDR ( M_AXI_ARADDR ),
.M_AXI_ARID ( M_AXI_ARID ),
.M_AXI_ARLEN ( M_AXI_ARLEN ),
.M_AXI_ARSIZE ( M_AXI_ARSIZE ),
.M_AXI_ARBURST ( M_AXI_ARBURST ),
.M_AXI_ARLOCK ( M_AXI_ARLOCK ),
.M_AXI_ARCACHE ( M_AXI_ARCACHE ),
.M_AXI_ARPROT ( M_AXI_ARPROT ),
.M_AXI_ARQOS ( M_AXI_ARQOS ),
.M_AXI_ARUSER ( M_AXI_ARUSER ),
.M_AXI_ARVALID ( M_AXI_ARVALID ),
.M_AXI_RREADY ( M_AXI_RREADY )
);
//例化AXI从机模块
axi_full_slave #(
.C_S_AXI_ID_WIDTH ( C_M_AXI_ID_WIDTH ),
.C_S_AXI_DATA_WIDTH ( C_M_AXI_ADDR_WIDTH ),
.C_S_AXI_ADDR_WIDTH ( C_M_AXI_DATA_WIDTH ),
.C_S_AXI_AWUSER_WIDTH ( C_M_AXI_AWUSER_WIDTH ),
.C_S_AXI_ARUSER_WIDTH ( C_M_AXI_ARUSER_WIDTH ),
.C_S_AXI_WUSER_WIDTH ( C_M_AXI_WUSER_WIDTH ),
.C_S_AXI_RUSER_WIDTH ( C_M_AXI_RUSER_WIDTH ),
.C_S_AXI_BUSER_WIDTH ( C_M_AXI_BUSER_WIDTH )
)
u_axi_full_slave (
.S_AXI_ACLK ( clk ),
.S_AXI_ARESETN ( rst_n ),
//AXI写地址通道
.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_AWLOCK ( M_AXI_AWLOCK ),
.S_AXI_AWCACHE ( M_AXI_AWCACHE ),
.S_AXI_AWPROT ( M_AXI_AWPROT ),
.S_AXI_AWQOS ( M_AXI_AWQOS ),
.S_AXI_AWREGION ( 4'd0 ),
.S_AXI_AWUSER ( M_AXI_AWUSER ),
.S_AXI_AWVALID ( M_AXI_AWVALID ),
.S_AXI_AWREADY ( M_AXI_AWREADY ),
//AXI写数据通道。
.S_AXI_WDATA ( M_AXI_WDATA ),
.S_AXI_WSTRB ( M_AXI_WSTRB ),
.S_AXI_WLAST ( M_AXI_WLAST ),
.S_AXI_WUSER ( M_AXI_WUSER ),
.S_AXI_WVALID ( M_AXI_WVALID ),
.S_AXI_WREADY ( M_AXI_WREADY ),
//AXI写应答通道。
.S_AXI_BREADY ( M_AXI_BREADY ),
.S_AXI_BID ( M_AXI_BID ),
.S_AXI_BRESP ( M_AXI_BRESP ),
.S_AXI_BUSER ( M_AXI_BUSER ),
.S_AXI_BVALID ( M_AXI_BVALID ),
//AXI读地址通道。
.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_ARLOCK ( M_AXI_ARLOCK ),
.S_AXI_ARCACHE ( M_AXI_ARCACHE ),
.S_AXI_ARPROT ( M_AXI_ARPROT ),
.S_AXI_ARQOS ( M_AXI_ARQOS ),
.S_AXI_ARREGION ( 4'd0 ),
.S_AXI_ARUSER ( M_AXI_ARUSER ),
.S_AXI_ARVALID ( M_AXI_ARVALID ),
.S_AXI_ARREADY ( M_AXI_ARREADY ),
//AXI读数据通道。
.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_RUSER ( M_AXI_RUSER ),
.S_AXI_RVALID ( M_AXI_RVALID ),
.S_AXI_RREADY ( M_AXI_RREADY )
);
initial begin
rst_n = 1;
#2;
rst_n = 0;//开始时复位10个时钟;
#(RST_TIME*CYCLE);
rst_n = 1;
#10000;
$stop;
end
endmodule
仿真结果如下图所示:
首先写地址通道握手,接着写数据通道完成握手,需要注意的是——一旦复位完成,写地址有效信号和写数据有效信号立马拉高,等待从机的准备完成信号。
然后向对应地址中写入数据,当最后一位数据写入时将WLAST信号拉高,表示当前写入数据为最后一位,下一个时钟周期写响应通道完成握手,进入READ状态。
读地址通道首先完成握手,下一个时钟周期读数据通道再完成握手,开始读取数据,需要注意的是,每读出一次数据读数据通道完成一次握手,直到读最后一个数据的时候拉高RLAST,本次验证结束。
问题总结:首先仿真的时候我隔了半个时钟周期就把复位信号拉高了,就得到了如图所示的波形,把复位的时间延长之后写地址有效信号才成功拉高,
但是拉高之后的波形如下图所示,可以看到读地址通道的有效信号在复位结束的时候同时拉高,检查代码后发现是代码的问题,写代码的时候逻辑一定要清晰。
然后代码并未成功,我发现有几个信号在本来应该是高电平的时候却变成了未知态,检查了好久才发现是testbench里面对信号进行了多次赋值,因为我的代码是VScode自动生成的,因此会对所有输入信号进行赋值,上次好像就遇到过相同的问题。