文章目录
前文是 AXI4 FULL SLAVE的Verilog实现(一)。这篇文章在之前的基础上作了一些改进。
参考官方IP之后的改进思路
如果主机发送不合理信号怎么办? 我们的从机必须要有一定的鲁棒性,考虑到之前在配置BRAM的时候,有一个busy信号我们不清楚它的用途(详见Vivado Block Memory Generator v8.4学习总结),结合官方IP的代码,我们发现这两个变量*可以用于指示slave是否正在读写*。如果slave正在读写,那么master即使发送ARVALID,也不会导致arready的拉高。
代码及改进效果
`timescale 1ns / 1ps
module noFSM_axi4_slave
/*
#(
parameter integer DATA_BUS_WIDTH = 32,
parameter integer ID_WIDTH = 4,
parameter integer ADDR_WIDTH = 32
)
*/
(
//global sig
input wire aclk,
input wire arstn,
//aw channel
input wire [3:0] awid,
input wire [31:0] awaddr,
input wire [7:0] awlen,
input wire [2:0] awsize,
input wire [1:0] awburst,
input wire awvalid,
output reg awready,
//w channel
input wire [31:0] wdata,
input wire [3:0] wstrb,
input wire wlast,
input wire wvalid,
output reg wready,
//b channel
input wire bready,
output reg [3:0] bid,
output reg [1:0] bresp,
output reg bvalid,
//ar channel
input wire [3:0] arid,
input wire [31:0] araddr,
input wire [7:0] arlen,
input wire [2:0] arsize,
input wire [1:0] arburst,
input wire arvalid,
output reg arready,
//r channel
input wire rready,
output reg [3:0] rid,
output reg [31:0] rdata,
output reg [1:0] rresp,
output reg rlast,
output reg rvalid,
//busy sig
output reg is_writing,
output reg is_reading
);
//new idea: these 5 channels are seperated, so we just need to check the signals every clock cycle
//to know what every channel is doing, and generate signals and operations.
//difficulties: some signals have dependency on others; read and write process are quite annoying in AXI4-FULL;
//unexpcted situations should be considered
//RAM
reg [31:0] mem [0:1023];
//addr reg and temp reg
reg [31:0] waddr_reg;
reg [31:0] start_waddr;
reg [31:0] raddr_reg;
reg [31:0] start_raddr;
reg [7:0] rcnt;
reg [31:0] WWRAP_Boundary;
reg [31:0] RWRAP_Boundary;
//para reg
reg [7:0] AWLEN;
reg [7:0] ARLEN;
reg [2:0] AWSIZE;
reg [2:0] ARSIZE;
reg [3:0] AWID;
reg [3:0] ARID;
reg [3:0] WSTRB;
//initializing the mem
/*
always @(posedge aclk) begin
if (!arstn) begin
mem[0] <= 32'h33221100;
mem[1] <= 32'h77665544;
mem[2] <= 32'hBBAA9988;
mem[3] <= 32'hFFEEDDCC;
mem[4] <= 32'h00112233;
mem[5] <= 32'h44556677;
mem[6] <= 32'h8899AABB;
mem[7] <= 32'hCCDDEEFF;
end
end
*/
//write addr channel
always @(posedge aclk) begin
if (!arstn) begin
awready <= 1'b0;
waddr_reg <= 32'hFFFFFFFF;
is_writing <= 1'b0;
end else if (!awready && awvalid == 1'b1 && !is_writing && !is_reading) begin
awready <= 1'b1;
is_writing <= 1'b1;
end else
awready <= 1'b0;
end
//para transport
always @(posedge aclk) begin
if (!arstn) begin
AWLEN <= 8'b0;
AWSIZE <= 3'b0;
waddr_reg <= 32'b0;
end else if(awvalid == 1'b1 && !is_writing) begin
AWLEN <= awlen;
AWSIZE <= awsize;
AWID <= awid;
WSTRB <= wstrb;
waddr_reg <= (awaddr >> AWSIZE) << AWSIZE;//align the address
start_waddr <= awaddr;
end
end
//write data channel
always @(posedge aclk) begin
if (!arstn)
wready <= 1'b0;
else if (awvalid == 1'b1 && awready == 1'b1) begin
wready <= 1'b1;
awready <= 1'b0;
end else if (wlast == 1'b1) begin
wready <= 1'b0;
is_writing <= 1'b0;
end
end
//write to the memory
always @(posedge aclk) begin
if (!arstn) begin
wready <= 1'b0;
waddr_reg <= 32'h0;
end else if (wready == 1'b1 && wvalid == 1'b1) begin
bid <= AWID;
WSTRB <= wstrb;
//calculate where to write and update the address register according to the type
WWRAP_Boundary <= (start_waddr >> (AWLEN * 2**(AWSIZE))) << (AWLEN * 2**(AWSIZE));
//calculate next addr
case (awburst)
2'b00 : begin
//FIXED
waddr_reg <= waddr_reg;
end
2'b01 : begin
//INCR
if (!wlast) begin
waddr_reg <= waddr_reg + 2**(AWSIZE);
end
end
2'b10 : begin
//WRAP
if (!wlast) begin
if (waddr_reg >= WWRAP_Boundary + AWLEN * 2**(AWSIZE)) begin
waddr_reg <= WWRAP_Boundary;
end else begin
waddr_reg <= waddr_reg + 2**(AWSIZE);
end
end
end
//default: ;
endcase
if (WSTRB[0] == 1'b1) begin
mem[waddr_reg[31:2]][7:0] <= wdata[7:0];
end
if (WSTRB[1] == 1'b1) begin
mem[waddr_reg[31:2]][15:8] <= wdata[15:8];
end
if (WSTRB[2] == 1'b1) begin
mem[waddr_reg[31:2]][23:16] <= wdata[23:16];
end
if (WSTRB[3] == 1'b1) begin
mem[waddr_reg[31:2]][31:24] <= wdata[31:24];
end
end
end
//write response channel
always @(posedge aclk) begin
if (!arstn) begin
bvalid <= 1'b0;
bresp <= 2'b00;
end else if (wlast == 1'b1 && wready == 1'b1 && wvalid == 1'b1) begin
bvalid <= 1'b1;
bresp <= 2'b00;
end else if (bvalid == 1'b1 && bready == 1'b1) begin
bvalid <= 1'b0;
end
end
//read addr channel
always @(posedge aclk) begin
if (!arstn) begin
arready <= 1'b0;
is_reading <=1'b0;
end else if (arvalid == 1'b1 && !arready && !is_writing && !is_reading) begin
arready <= 1'b1;
is_reading <=1'b1;
end else
arready <= 1'b0;
end
//para transport
always @(posedge aclk) begin
if (!arstn) begin
ARLEN <= 8'b0;
ARSIZE <= 3'b0;
raddr_reg <= 32'hFFFFFFFF;
end else if (arvalid == 1'b1 && !is_reading) begin
ARLEN <= arlen;
ARSIZE <= arsize;
ARID <= arid;
raddr_reg <= araddr;
start_raddr <= araddr;
end
end
//read data channel
always @(posedge aclk) begin
if (!arstn)
rvalid <= 1'b0;
else if (arready == 1'b1 && arvalid == 1'b1) begin
rvalid <= 1'b1;
arready <= 1'b0;
rid <= ARID;
end
end
//caculate address
always @(posedge aclk) begin
if (!arstn) begin
rvalid <= 1'b0;
rresp <= 2'b00;
end else if(rvalid == 1'b1 && rready == 1'b1) begin
rresp <= 2'b00;
RWRAP_Boundary <= (start_raddr >> (ARLEN * 2**(ARSIZE))) << (ARLEN * 2**(ARSIZE));
raddr_reg <= (raddr_reg >> ARSIZE) << ARSIZE;//align the address with byte
case (arburst)
2'b00 : begin
raddr_reg <= raddr_reg;
end
2'b01 : begin
if (!rlast) begin
raddr_reg <= raddr_reg + 2**(ARSIZE);
end
end
2'b10 : begin
if (raddr_reg <= araddr + ARLEN * 2**(ARSIZE)) begin
raddr_reg <= raddr_reg + 2**(ARSIZE);
end else begin
raddr_reg <= RWRAP_Boundary;
end
end
//default:
endcase
end
end
//data out
always @(posedge aclk) begin
if (!arstn) begin
rdata <= 32'h0;
rcnt <= 8'd0;
rlast <= 1'b0;
end else if (rvalid == 1'b1 && rready == 1'b1) begin
rdata <= mem[raddr_reg[31:2]];
if (rcnt < ARLEN) begin
rcnt <= rcnt + 8'd1;
end else begin
rlast <= 1'b1;
rcnt <= 8'b0;
end
end
end
always @(posedge aclk) begin
if (!arstn) begin
rlast <= 1'b0;
end else if (rlast == 1'b1) begin
rlast <= 1'b0;
rvalid <= 1'b0;
is_reading <= 1'b0;
end
end
endmodule
通过is_writing和is_reading这两个变量的指示,判断slave是否能够接收下一个地址。
仿真结果如下所示:
按照之前的逻辑,黄线处本应拉高arready,但由于此时正在读写,如果此时参数发生变化,则会导致错误;加入状态指示变量之后,即使主机发送了不合理的ARVALID,arready信号也不会拉高,避免了类似的错误,提高了鲁棒性。