Verilog FIFO转AXI4-Full模块的RTL简单实现 - Master主机

之前看到有大佬说任何协议都可以用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

以下是一个简单FIFOAXI-Stream的Verilog代码: ``` module fifo_to_axi_stream ( input clk, input rst, input [31:0] data_in, input wr_en, output [31:0] data_out, output axi_tvalid, output axi_tlast, output axi_tready ); // AXI-Stream interface signals reg [31:0] axi_data; reg axi_tvalid; reg axi_tlast; wire axi_tready; // FIFO signals reg [31:0] fifo [0:7]; reg [2:0] rd_ptr = 0; reg [2:0] wr_ptr = 0; reg [2:0] count = 0; reg full = 0; reg empty = 1; // Write data to FIFO always @(posedge clk) begin if (rst) begin wr_ptr <= 0; count <= 0; full <= 0; empty <= 1; end else if (wr_en && !full) begin fifo[wr_ptr] <= data_in; wr_ptr <= wr_ptr + 1; count <= count + 1; full <= (count == 8); empty <= 0; end end // Read data from FIFO and send to AXI-Stream always @(posedge clk) begin if (rst) begin rd_ptr <= 0; axi_data <= 0; axi_tvalid <= 0; axi_tlast <= 0; end else if (!empty && axi_tready) begin axi_data <= fifo[rd_ptr]; rd_ptr <= rd_ptr + 1; count <= count - 1; full <= 0; empty <= (count == 0); axi_tvalid <= 1; axi_tlast <= (count == 1); end else begin axi_tvalid <= 0; axi_tlast <= 0; end end // Assign output signals assign data_out = axi_data; assign axi_tready = 1; endmodule ``` 在这个模块中,我们使用了一个8个元素的FIFO,以将输入数据从一个时钟域传递到另一个时钟域。输入数据通过`data_in`信号传递,并且在`wr_en`信号为高电平时写入FIFO。输出数据通过`data_out`信号传递,以便在AXI-Stream时钟域中使用。 AXI-Stream接口由三个信号组成:数据信号(axi_data),有效标志(axi_tvalid)和末尾标志(axi_tlast)。在本例中,我们使用一个简单的流水线协议,其中我们在每个时钟周期中传输一个32位数据。有效标志表示当前传输的数据是否有效,末尾标志表示当前传输的数据是否为流的最后一个数据。在本例中,我们使用FIFO中的数据计数来确定何时传输最后一个数据。 请注意,我们使用了同步复位信号(rst)来确保在系统启动时所有信号都处于已知状态。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值