该模块主要功能为:接收数据读写op指令,将其转换为AXI4总线形式
其逻辑较为简单,而关于AXI4的时序,建议读者使用vivado封装两个AXI4的ip核,一个主机,一个从机,进行学习,笔者在这里便不进行赘述,如果有读者想要了解,欢迎在评论区进行讨论,笔者后期可能会进行文章专栏讲解。
其工作原理即是,检测到op指令有效,进行相应的读写操作,AXI4读写数据的流程是
AXI4写:写入地址–>写数据–>检测写响应
AXI4写:写入地址–>读数据
之所以要使用FIFO对数据进行暂时的缓冲,是因为在写数据时候,从机的写ready信号,可能出现中途拉低,此时需要对写数据进行保持,所以要使用FIFO缓冲。
通过文字流程描述,逻辑梳理可以将代码清晰的进行写出
`timescale 1ns / 1ps
//
// Company:
// Engineer:
//
// Create Date: 2025/04/24 20:53:36
// Design Name:
// Module Name: ddr_axi_rw
// Project Name:
// Target Devices:
// Tool Versions:
// Description: latnecy = 4 i_write_data-->M_AXI_WDATA
//
// Dependencies:
//
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
//
//
module ddr_axi_rw#(
parameter C_M_TARGET_SLAVE_BASE_ADDR = 32'h00000000 ,
parameter integer C_M_AXI_BURST_LEN = 256 ,
parameter integer C_M_AXI_ID_WIDTH = 1 ,
parameter integer C_M_AXI_ADDR_WIDTH = 30 ,
parameter integer C_M_AXI_DATA_WIDTH = 32 ,
parameter integer C_M_AXI_AWUSER_WIDTH = 0 ,
parameter integer C_M_AXI_ARUSER_WIDTH = 0 ,
parameter integer C_M_AXI_WUSER_WIDTH = 0 ,
parameter integer C_M_AXI_RUSER_WIDTH = 0 ,
parameter integer C_M_AXI_BUSER_WIDTH = 0
)(
input init_calib_complete ,
input [1 :0] i_op_cmd ,
input [29:0] i_op_waddr ,
input [29:0] i_op_raddr ,
input i_op_valid ,
output o_op_ready ,
input [31:0] i_write_data ,
input i_write_valid ,
output [31:0] o_read_data ,
output o_read_valid ,
input M_AXI_ACLK ,
input M_AXI_ARESETN ,
output [C_M_AXI_ID_WIDTH-1 :0] M_AXI_AWID ,
output [C_M_AXI_ADDR_WIDTH-1 :0] M_AXI_AWADDR ,
output [7 :0] M_AXI_AWLEN ,
output [2 :0] M_AXI_AWSIZE ,
output [1 :0] M_AXI_AWBURST ,
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 M_AXI_AWVALID ,
input M_AXI_AWREADY ,
output [C_M_AXI_DATA_WIDTH-1 :0] M_AXI_WDATA ,
output [C_M_AXI_DATA_WIDTH/8-1 :0] M_AXI_WSTRB ,
output M_AXI_WLAST ,
output [C_M_AXI_WUSER_WIDTH-1 :0] M_AXI_WUSER ,
output M_AXI_WVALID ,
input M_AXI_WREADY ,
input [C_M_AXI_ID_WIDTH-1 :0] M_AXI_BID ,
input [1 :0] M_AXI_BRESP ,
input [C_M_AXI_BUSER_WIDTH-1 :0] M_AXI_BUSER ,
input M_AXI_BVALID ,
output M_AXI_BREADY ,
output [C_M_AXI_ID_WIDTH-1 :0] M_AXI_ARID ,
output [C_M_AXI_ADDR_WIDTH-1 :0] M_AXI_ARADDR ,
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 M_AXI_ARVALID ,
input M_AXI_ARREADY ,
input [C_M_AXI_ID_WIDTH-1 :0] M_AXI_RID ,
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 M_AXI_RREADY
);
reg [7 :0] r_init ;
reg [1 :0] ri_op_cmd ;
reg [29:0] ri_op_waddr ;
reg [29:0] ri_op_raddr ;
reg ri_op_valid ;
reg ro_op_ready ;
reg [31:0] ri_write_data ;
reg ri_write_valid ;
reg ri_write_last ;
wire w_rst ;
assign w_rst = ~M_AXI_ARESETN ;
assign o_read_data = M_AXI_RDATA ;
assign o_read_valid = M_AXI_RVALID & M_AXI_RREADY;
assign o_op_ready = ro_op_ready ;
always @(posedge M_AXI_ACLK,posedge w_rst)
begin
if(w_rst)
r_init <= 8'h0;
else
r_init <= {r_init[6 :0],init_calib_complete};
end
always @(posedge M_AXI_ACLK,posedge w_rst)
begin
if(w_rst) begin
ri_op_waddr <= 'd0;
ri_op_raddr <= 'd0;
end
else if(i_op_valid) begin
ri_op_waddr <= i_op_waddr;
ri_op_raddr <= i_op_raddr;
end
end
/*数据读写完毕后再进行下一次操作*/
// always @(posedge M_AXI_ACLK,posedge w_rst)
// begin
// if(w_rst)
// ro_op_ready <= 1'b0;
// else if(!M_AXI_WVALID && !M_AXI_RVALID && !M_AXI_AWVALID && !M_AXI_ARVALID)
// ro_op_ready <= 1'b1;
// else
// ro_op_ready <= 1'b0;
// end
/*地址读写完成后,即可进行下一次操作*/
always @(posedge M_AXI_ACLK,posedge w_rst)
begin
if(w_rst)
ro_op_ready <= 1'b0;
else if(!M_AXI_AWVALID && !M_AXI_ARVALID)
ro_op_ready <= 1'b1;
else
ro_op_ready <= 1'b0;
end
always @(posedge M_AXI_ACLK,posedge w_rst)
begin
if(w_rst) begin
ri_op_cmd <= 'd0;
ri_op_valid <= 'd0;
end
else begin
ri_op_cmd <= i_op_cmd ;
ri_op_valid <= i_op_valid;
end
end
always @(posedge M_AXI_ACLK,posedge w_rst)
begin
if(w_rst) begin
ri_write_data <= 32'd0;
ri_write_valid <= 1'b0 ;
end
else begin
ri_write_data <= i_write_data ;
ri_write_valid <= i_write_valid;
end
end
function integer clogb2 (input integer bit_depth);
begin
for(clogb2 = 0; bit_depth > 0;clogb2 = clogb2 + 1)
bit_depth = bit_depth >> 1;
end
endfunction
localparam integer C_TRANSACTIONS_NUM = clogb2(C_M_AXI_BURST_LEN-1) + 1;
reg [C_M_AXI_ADDR_WIDTH-1 : 0] r_axi_awaddr ;
reg r_axi_awvalid ;
reg [C_M_AXI_DATA_WIDTH-1 : 0] r_axi_wdata ;
reg r_axi_wlast ;
reg r_axi_wvalid ;
reg r_axi_bready ;
reg [C_M_AXI_ADDR_WIDTH-1 : 0] r_axi_araddr ;
reg r_axi_arvalid ;
reg r_axi_rready ;
reg r_write_start ;
reg r_read_start ;
reg [C_TRANSACTIONS_NUM : 0] r_write_index ;
reg [C_TRANSACTIONS_NUM : 0] r_read_index ;
wire [C_TRANSACTIONS_NUM+2 : 0] w_burst_size_bytes ;
wire w_fifo_rden ;
wire [31 :0] w_fifo_dout ;
assign w_burst_size_bytes = C_M_AXI_BURST_LEN * C_M_AXI_ADDR_WIDTH/8;
assign M_AXI_AWID = 'b0;
assign M_AXI_AWADDR = C_M_TARGET_SLAVE_BASE_ADDR + r_axi_awaddr;
assign M_AXI_AWLEN = C_M_AXI_BURST_LEN - 1;
assign M_AXI_AWSIZE = clogb2((C_M_AXI_DATA_WIDTH/8)-1);
assign M_AXI_AWBURST = 2'b01 ;
assign M_AXI_AWLOCK = 1'b0 ;
assign M_AXI_AWCACHE = 4'b0010 ;
assign M_AXI_AWPROT = 3'h0 ;
assign M_AXI_AWQOS = 4'h0 ;
assign M_AXI_AWUSER = 'b1 ;
assign M_AXI_AWVALID = r_axi_awvalid ;
assign M_AXI_WDATA = w_fifo_dout ;
assign M_AXI_WSTRB = {(C_M_AXI_DATA_WIDTH/8){1'b1}};
assign M_AXI_WLAST = r_axi_wlast ;
assign M_AXI_WUSER = 'b0 ;
assign M_AXI_WVALID = r_axi_wvalid ;
assign M_AXI_BREADY = r_axi_bready ;
assign M_AXI_ARID = 'b0;
assign M_AXI_ARADDR = C_M_TARGET_SLAVE_BASE_ADDR + r_axi_araddr;
assign M_AXI_ARLEN = C_M_AXI_BURST_LEN - 1;
assign M_AXI_ARSIZE = clogb2((C_M_AXI_DATA_WIDTH/8)-1);
assign M_AXI_ARBURST = 2'b01;
assign M_AXI_ARLOCK = 1'b0;
assign M_AXI_ARCACHE = 4'b0010;
assign M_AXI_ARPROT = 3'h0;
assign M_AXI_ARQOS = 4'h0;
assign M_AXI_ARUSER = 'b1 ;
assign M_AXI_ARVALID = r_axi_arvalid ;
assign M_AXI_RREADY = r_axi_rready ;
wire w_activate ;
wire r_activate ;
wire write_resp_error;
assign w_activate = r_axi_wvalid & M_AXI_WREADY;
assign r_activate = M_AXI_RVALID & r_axi_rready;
assign w_fifo_rden = w_activate ;
assign write_resp_error= r_axi_bready & M_AXI_BVALID & M_AXI_BRESP[1];
SYNC_FWFT_FIFO_32X256 SYNC_FWFT_FIFO_32X256_U0 (
.clk (M_AXI_ACLK ),
.din (i_write_data ),
.wr_en (i_write_valid ),
.rd_en (w_fifo_rden ),
.dout (w_fifo_dout ),
.full (),
.empty ()
);
always @(posedge M_AXI_ACLK,posedge w_rst)
begin
if(w_rst)
r_write_start <= 1'b0;
else if(ri_op_cmd == 2'b01 && ri_op_valid)
r_write_start <= 1'b1;
else
r_write_start <= 1'b0;
end
always @(posedge M_AXI_ACLK,posedge w_rst)
begin
if(w_rst)
r_read_start <= 1'b0;
else if(ri_op_cmd == 2'b10 && ri_op_valid)
r_read_start <= 1'b1;
else
r_read_start <= 1'b0;
end
/*Write*/
always @(posedge M_AXI_ACLK)
begin
if(w_rst == 1'b1)
r_axi_awvalid <= 1'b0;
else if(~r_axi_awvalid && r_write_start)
r_axi_awvalid <= 1'b1;
else if(r_axi_awvalid && M_AXI_AWREADY)
r_axi_awvalid <= 1'b0;
end
always @(posedge M_AXI_ACLK)
begin
if(w_rst == 1'b1)
r_axi_awaddr <= 'd0;
else if(~r_axi_awvalid && r_write_start)
r_axi_awaddr <= ri_op_waddr;
end
always @(posedge M_AXI_ACLK)
begin
if(w_rst == 1'b1)
r_axi_wvalid <= 1'b0;
else if(r_axi_awvalid && M_AXI_AWREADY)
r_axi_wvalid <= 1'b1;
else if(r_axi_wlast && w_activate)
r_axi_wvalid <= 1'b0;
end
always @(posedge M_AXI_ACLK)
begin
if(w_rst == 1'b1)
r_axi_wlast <= 1'b0;
else if((r_write_index == C_M_AXI_BURST_LEN - 2 && C_M_AXI_BURST_LEN >= 2) && w_activate)
r_axi_wlast <= 1'b1;
else if(w_activate)
r_axi_wlast <= 1'b0;
else if(r_axi_wlast)
r_axi_wlast <= 1'b0;
end
always @(posedge M_AXI_ACLK)
begin
if(w_rst == 1'b1 || r_write_start)
r_write_index <= 'd0;
else if(w_activate && (r_write_index != C_M_AXI_BURST_LEN - 1))
r_write_index <= r_write_index + 1'b1;
end
always @(posedge M_AXI_ACLK)
begin
if(w_rst == 1'b1)
r_axi_bready <= 1'b0;
else if(M_AXI_BVALID && ~r_axi_bready)
r_axi_bready <= 1'b1;
else if(r_axi_bready)
r_axi_bready <= 1'b0;
end
wire read_resp_error;
assign read_resp_error = r_axi_rready & M_AXI_RVALID & M_AXI_RRESP[1];
/*Read*/
always @(posedge M_AXI_ACLK)
begin
if(w_rst == 1'b1)
r_axi_arvalid <= 1'b0;
else if(r_axi_arvalid && M_AXI_ARREADY)
r_axi_arvalid <= 1'b0;
else if(~r_axi_arvalid && r_read_start)
r_axi_arvalid <= 1'b1;
end
always @(posedge M_AXI_ACLK)
begin
if(w_rst == 1'b1)
r_axi_araddr <= 'd0;
else if(~r_axi_arvalid && r_read_start)
r_axi_araddr <= ri_op_raddr;
end
always @(posedge M_AXI_ACLK)
begin
if(w_rst == 1'b1 || r_read_start)
r_read_index <= 'd0;
else if(r_activate && (r_read_index != C_M_AXI_BURST_LEN - 1))
r_read_index <= r_read_index + 1'b1;
end
always @(posedge M_AXI_ACLK)
begin
if(w_rst == 1'b1)
r_axi_rready <= 1'b0;
else if(M_AXI_RVALID) begin
if(M_AXI_RLAST && r_axi_rready)
r_axi_rready <= 1'b0;
else
r_axi_rready <= 1'b1;
end
end
endmodule
本节代码便暂时不进行仿真测试展示(实际笔者进行过测试,但由于tb文件较为混乱,便不再进行展示),对于该模块的编写是较为简单的,在开发中,是十分常用的模块,有必要进行掌握。关于本节代码的问题,以及优化意见,欢迎大家在评论区指出,如果想要对应工程进行学习,欢迎大家私信。