之前看到有大佬说任何协议都可以用FIFO实现,想了想还觉得挺有道理
试着写了下FIFO转AXI4-Full的模块,为了避免使用时产生死锁,还是添加了状态机去满足AXI4的时序。
输入:写从机地址FIFO+写数据FIFO、读从机地址FIFO+读数据FIFO
输出:AXI4-Full主机端口
不支持Outstanding和乱序传输
module axi_full_m_module
#(
parameter M_AXI_TARGET_SLAVE_BASE_ADDR = 32'h40000000 ,
parameter M_AXI_BURST_LEN = 16 ,
parameter M_AXI_ID_WIDTH = 1 ,
parameter M_AXI_ADDR_WIDTH = 32 ,
parameter M_AXI_DATA_WIDTH = 32 ,
parameter M_AXI_AWUSER_WIDTH = 0 ,
parameter M_AXI_ARUSER_WIDTH = 0 ,
parameter M_AXI_WUSER_WIDTH = 0 ,
parameter M_AXI_RUSER_WIDTH = 0 ,
parameter M_AXI_BUSER_WIDTH = 0
)
(
input M_AXI_ACLK,
input M_AXI_ARESETn, // AXI协议默认低电平复位
/*********
M_AXI端口
********/
/* 写地址通道 */
output wire [M_AXI_ID_WIDTH-1 : 0] M_AXI_AWID ,
output wire [M_AXI_ADDR_WIDTH-1 : 0] M_AXI_AWADDR ,
output wire [7 : 0] M_AXI_AWLEN ,
output wire [2 : 0] M_AXI_AWSIZE ,
output wire [1 : 0] M_AXI_AWBURST ,
output wire M_AXI_AWLOCK ,
output wire [3 : 0] M_AXI_AWCACHE ,
output wire [2 : 0] M_AXI_AWPROT ,
output wire [3 : 0] M_AXI_AWQOS ,
output wire [M_AXI_AWUSER_WIDTH-1 : 0] M_AXI_AWUSER ,
output wire M_AXI_AWVALID ,
input wire M_AXI_AWREADY ,
/* 写数据通道 */
output wire [M_AXI_DATA_WIDTH-1 : 0] M_AXI_WDATA ,
output wire [M_AXI_DATA_WIDTH/8-1 : 0] M_AXI_WSTRB ,
output wire M_AXI_WLAST ,
output wire [M_AXI_WUSER_WIDTH-1 : 0] M_AXI_WUSER ,
output wire M_AXI_WVALID ,
input wire M_AXI_WREADY ,
/* 写应答通道 */
input wire [M_AXI_ID_WIDTH-1 : 0] M_AXI_BID ,
input wire [1 : 0] M_AXI_BRESP ,
input wire [M_AXI_BUSER_WIDTH-1 : 0] M_AXI_BUSER ,
input wire M_AXI_BVALID ,
output wire M_AXI_BREADY ,
/* 读地址通道 */
output wire [M_AXI_ID_WIDTH-1 : 0] M_AXI_ARID ,
output wire [M_AXI_ADDR_WIDTH-1 : 0] M_AXI_ARADDR ,
output wire [7 : 0] M_AXI_ARLEN ,
output wire [2 : 0] M_AXI_ARSIZE ,
output wire [1 : 0] M_AXI_ARBURST ,
output wire M_AXI_ARLOCK ,
output wire [3 : 0] M_AXI_ARCACHE ,
output wire [2 : 0] M_AXI_ARPROT ,
output wire [3 : 0] M_AXI_ARQOS ,
output wire [M_AXI_ARUSER_WIDTH-1 : 0] M_AXI_ARUSER ,
output wire M_AXI_ARVALID ,
input wire M_AXI_ARREADY ,
/* 读数据通道 */
input wire [M_AXI_ID_WIDTH-1 : 0] M_AXI_RID ,
input wire [M_AXI_DATA_WIDTH-1 : 0] M_AXI_RDATA ,
input wire [1 : 0] M_AXI_RRESP ,
input wire M_AXI_RLAST ,
input wire [M_AXI_RUSER_WIDTH-1 : 0] M_AXI_RUSER ,
input wire M_AXI_RVALID ,
output wire M_AXI_RREADY ,
/*********
FIFO端口
********/
/* 写地址FIFO */
input wire [M_AXI_ADDR_WIDTH-1 : 0] FIFO_AWADDR ,
input wire FIFO_AWEMPTY ,
output wire FIFO_AWREAD ,
/* 写数据FIFO */
input wire [M_AXI_DATA_WIDTH-1 : 0] FIFO_WDATA ,
input wire FIFO_WEMPTY ,
output wire FIFO_WREAD ,
/* 读地址FIFO */
input wire [M_AXI_ADDR_WIDTH-1 : 0] FIFO_ARADDR ,
input wire FIFO_AREMPTY ,
output wire FIFO_ARREAD ,
output wire [M_AXI_DATA_WIDTH-1 : 0] FIFO_RDATA ,
input wire FIFO_RFULL ,
output wire FIFO_RWRITE
);
// 计算二进制位宽
function integer clogb2(integer number);
begin
for (clogb2 = 0; number > 0; clogb2 = clogb2 + 1) begin
number = number >> 1;
end
end
endfunction
/********
参数
********/
localparam M_AXI_DATA_BYTE = M_AXI_DATA_WIDTH / 8;
/********
状态机
********/
reg [3:0] r_s_w_cs; // STATE MACHINE (WRITE) CURRENT STATE
reg [3:0] r_s_w_ns; // STATE MACHINE (WRITE) NEXT STATE
localparam S_W_IDLE = 'b0001;
localparam S_W_ADDR = 'b0010;
localparam S_W_DATA = 'b0100;
localparam S_W_RESB = 'b1000;
reg [2:0] r_s_r_cs;
reg [2:0] r_s_r_ns;
localparam S_R_IDLE = 'b001;
localparam S_R_ADDR = 'b010;
localparam S_R_DATA = 'b100;
/********
寄存器
********/
reg [M_AXI_ADDR_WIDTH-1:0] r_m_axi_awaddr ;
reg r_m_axi_awvalid ;
reg [M_AXI_DATA_WIDTH-1:0] r_m_axi_wdata ;
reg r_m_axi_wlast ;
reg r_m_axi_wvalid ;
reg [clogb2(M_AXI_BURST_LEN)-1:0] r_wcnt ;
reg [M_AXI_ADDR_WIDTH-1:0] r_m_axi_araddr ;
reg r_m_axi_arvalid ;
reg r_m_axi_rready ;
reg [clogb2(M_AXI_BURST_LEN):0] r_rcnt ;
reg [M_AXI_DATA_WIDTH-1:0] r_fifo_rdata ;
reg r_fifo_rwrite ;
/********
网表
********/
wire sys_clk;
wire sys_rst;
/********
组合逻辑
********/
assign sys_clk = M_AXI_ACLK ;
assign sys_rst = ~M_AXI_ARESETn ; // Xilinx FPGA寄存器支持高电平同步复位
assign M_AXI_AWID = 'b0 ;
assign M_AXI_AWADDR = r_m_axi_awaddr + M_AXI_TARGET_SLAVE_BASE_ADDR; // 增加基址偏移量,区分不同从机
assign M_AXI_AWLEN = M_AXI_BURST_LEN ;
assign M_AXI_AWSIZE = clogb2(M_AXI_DATA_BYTE) - 1 ;
assign M_AXI_AWBURST = 'b01 ;
assign M_AXI_AWLOCK = 'b0 ;
assign M_AXI_AWCACHE = 'b0010 ;
assign M_AXI_AWPROT = 'b0 ;
assign M_AXI_AWQOS = 'b0 ;
assign M_AXI_AWUSER = 'b0 ;
assign M_AXI_AWVALID = r_m_axi_awvalid ;
assign M_AXI_WDATA = r_m_axi_wdata ;
assign M_AXI_WSTRB = {M_AXI_DATA_BYTE{1'b1}} ; // 数据位全部有效
assign M_AXI_WLAST = r_m_axi_wlast ;
assign M_AXI_WUSER = 'd0 ;
assign M_AXI_WVALID = r_m_axi_wvalid ;
assign M_AXI_BREADY = 'b1 ; // 响应接收准备好 - 保持置位
assign M_AXI_ARID = 'b0 ;
assign M_AXI_ARADDR = r_m_axi_araddr + M_AXI_TARGET_SLAVE_BASE_ADDR;
assign M_AXI_ARLEN = M_AXI_BURST_LEN ;
assign M_AXI_ARSIZE = clogb2(M_AXI_DATA_BYTE) - 1 ;
assign M_AXI_ARBURST = 'b01 ;
assign M_AXI_ARLOCK = 'b0 ;
assign M_AXI_ARCACHE = 'b0010 ;
assign M_AXI_ARPROT = 'b0 ;
assign M_AXI_ARQOS = 'b0 ;
assign M_AXI_ARUSER = 'b0 ;
assign M_AXI_ARVALID = r_m_axi_arvalid ;
assign M_AXI_RREADY = r_m_axi_rready ;
assign FIFO_AWREAD = (r_s_w_cs == S_W_ADDR) && ~FIFO_AWEMPTY ;
assign FIFO_WREAD = (r_s_w_cs == S_W_DATA) && ~FIFO_AWEMPTY && ~M_AXI_WLAST
&& ((r_wcnt == 'd0) // 读取突发传输的第一个数据
|| ((r_wcnt != 'd0) && M_AXI_WREADY)) ; // 读取突发传输的其他数据
assign FIFO_ARREAD = (r_s_r_cs == S_R_ADDR) && ~FIFO_AREMPTY ;
assign FIFO_RDATA = (r_s_r_cs == S_R_DATA) && r_fifo_rdata ;
assign FIFO_RWRITE = (r_s_r_cs == S_R_DATA) && r_fifo_rwrite ;
/********
例化
********/
/********
状态机跳转逻辑
********/
/* 写状态机 */
always @(posedge sys_clk) begin
if (sys_rst)
r_s_w_cs <= S_W_IDLE;
else
r_s_w_cs <= r_s_w_ns;
end
always @(*) begin
case(r_s_w_cs)
S_W_IDLE: r_s_w_ns = ~FIFO_AWEMPTY ? S_W_ADDR : S_W_IDLE;
S_W_ADDR: r_s_w_ns = (M_AXI_AWVALID && M_AXI_AWREADY) ? S_W_DATA : S_W_ADDR;
S_W_DATA: r_s_w_ns = (M_AXI_WVALID && M_AXI_WREADY && M_AXI_WLAST) ? S_W_RESB : S_W_DATA;
S_W_RESB: r_s_w_ns = (M_AXI_BVALID && M_AXI_BREADY) ? S_W_IDLE : S_W_RESB;
default: r_s_w_ns = S_W_IDLE;
endcase
end
/* 读状态机 */
always @(posedge sys_clk) begin
if (sys_rst)
r_s_r_cs <= S_R_IDLE;
else
r_s_r_cs <= r_s_r_ns;
end
always @(*) begin
case(r_s_r_cs)
S_R_IDLE: r_s_r_ns = ~FIFO_AREMPTY ? S_R_ADDR : S_R_IDLE;
S_R_ADDR: r_s_r_ns = (M_AXI_ARVALID && M_AXI_ARREADY) ? S_R_DATA : S_R_ADDR;
S_R_DATA: r_s_r_ns = (r_rcnt == M_AXI_BURST_LEN) ? S_R_IDLE : S_R_DATA;
default: r_s_r_ns = S_R_IDLE;
endcase
end
/********
进程
********/
always @(posedge sys_clk) begin
if (sys_rst) begin
r_m_axi_awaddr <= 'd0;
r_m_axi_awvalid <= 'b0;
r_m_axi_wdata <= 'd0;
r_m_axi_wvalid <= 'b0;
r_m_axi_wlast <= 'b0;
r_wcnt <= 'd0;
r_m_axi_rready <= 'b0;
end
else begin
case (r_s_w_cs)
S_W_IDLE: begin
r_m_axi_awaddr <= 'd0;
r_m_axi_awvalid <= 'b0;
r_m_axi_wdata <= 'd0;
r_m_axi_wvalid <= 'b0;
r_m_axi_wlast <= 'b0;
r_wcnt <= 'd0;
r_m_axi_rready <= 'b0;
end
S_W_ADDR: begin
if (FIFO_AWREAD) begin
r_m_axi_awaddr <= FIFO_AWADDR;
r_m_axi_awvalid <= 'b1;
end
else if (M_AXI_AWVALID && M_AXI_AWREADY) begin
r_m_axi_awaddr <= 'd0;
r_m_axi_awvalid <= 'b0;
end
else begin
r_m_axi_awaddr <= r_m_axi_awaddr;
r_m_axi_awvalid <= r_m_axi_awvalid;
end
end
S_W_DATA: begin
if (FIFO_WREAD) begin
r_m_axi_wdata <= FIFO_WDATA;
r_m_axi_awvalid <= 'b1;
end
else if (M_AXI_WVALID && M_AXI_WREADY) begin
r_m_axi_awaddr <= 'd0;
r_m_axi_awvalid <= 'b0;
end
else begin
r_m_axi_awaddr <= r_m_axi_awaddr;
r_m_axi_awvalid <= r_m_axi_awvalid;
end
if (FIFO_WREAD)
r_wcnt <= r_wcnt + 'd1;
else if (M_AXI_WLAST)
r_wcnt <= 'd0;
else
r_wcnt <= r_wcnt;
if (FIFO_WREAD && (r_wcnt == M_AXI_BURST_LEN - 1))
r_m_axi_wlast <= 'b1;
else if (M_AXI_WVALID && M_AXI_WREADY && M_AXI_WLAST)
r_m_axi_wlast <= 'b0;
else
r_m_axi_wlast <= r_m_axi_wlast;
end
S_W_RESB: begin
if (~M_AXI_RREADY)
r_m_axi_rready <= 'b1;
else if (M_AXI_RVALID && M_AXI_RREADY)
r_m_axi_rready <= 'b0;
else
r_m_axi_rready <= r_m_axi_rready;
end
endcase
end
end
always @(posedge sys_clk) begin
if (sys_rst) begin
r_m_axi_araddr <= 'd0;
r_m_axi_arvalid <= 'b0;
r_fifo_rdata <= 'd0;
r_fifo_rwrite <= 'b0;
r_rcnt <= 'd0;
end
else begin
case (r_s_r_cs)
S_R_IDLE: begin
r_m_axi_araddr <= 'd0;
r_m_axi_arvalid <= 'b0;
r_fifo_rdata <= 'd0;
r_fifo_rwrite <= 'b0;
r_rcnt <= 'd0;
end
S_R_ADDR: begin
if (FIFO_ARREAD) begin
r_m_axi_araddr <= FIFO_ARADDR;
r_m_axi_arvalid <= 'b1;
end
else if (M_AXI_ARVALID && M_AXI_ARREADY) begin
r_m_axi_araddr <= 'd0;
r_m_axi_arvalid <= 'b0;
end
else begin
r_m_axi_araddr <= r_m_axi_araddr;
r_m_axi_arvalid <= r_m_axi_arvalid;
end
end
S_R_DATA: begin
if (M_AXI_RVALID) begin
r_fifo_rdata <= M_AXI_RDATA;
r_fifo_rwrite <= 'b1;
end
else if (FIFO_RWRITE && ~FIFO_RFULL) begin
r_fifo_rdata <= 'd0;
r_fifo_rwrite <= 'b0;
end
else begin
r_fifo_rdata <= r_fifo_rdata;
r_fifo_rwrite <= r_fifo_rwrite;
end
if (FIFO_RWRITE && ~FIFO_RFULL)
r_rcnt <= r_rcnt + 'd1;
else if (r_rcnt == M_AXI_BURST_LEN)
r_rcnt <= 'd0;
else
r_rcnt <= r_rcnt;
end
default: r_s_r_ns = S_R_IDLE;
endcase
end
end
endmodule