UDP--DDR--SFP,FPGA实现之ddr axi读写驱动模块

ddr axi读写驱动模块实现介绍

该模块主要功能为:接收数据读写op指令,将其转换为AXI4总线形式
其逻辑较为简单,而关于AXI4的时序,建议读者使用vivado封装两个AXI4的ip核,一个主机,一个从机,进行学习,笔者在这里便不进行赘述,如果有读者想要了解,欢迎在评论区进行讨论,笔者后期可能会进行文章专栏讲解。
其工作原理即是,检测到op指令有效,进行相应的读写操作,AXI4读写数据的流程是
AXI4写:写入地址–>写数据–>检测写响应
AXI4写:写入地址–>读数据
之所以要使用FIFO对数据进行暂时的缓冲,是因为在写数据时候,从机的写ready信号,可能出现中途拉低,此时需要对写数据进行保持,所以要使用FIFO缓冲。

ddr axi读写驱动模块代码编写

通过文字流程描述,逻辑梳理可以将代码清晰的进行写出

`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文件较为混乱,便不再进行展示),对于该模块的编写是较为简单的,在开发中,是十分常用的模块,有必要进行掌握。关于本节代码的问题,以及优化意见,欢迎大家在评论区指出,如果想要对应工程进行学习,欢迎大家私信。

你可能感兴趣的:(udp,fpga开发,ddr,AXI,网络协议)