自研PE单元AXI接口记录(2)

紧接着上一篇,这期记录我实现PE单元C矩阵的AXI接口的过程。
一、设计前
PE的C矩阵信号端为(outputc_ptr_addr ,c_ptr_addr_v ,c_data_v,c_data),并且也是地址递增,结合AXI4特性,我们应该怎么做呢
二、设计思路
这里就简要带过了,发出写使能时存入fifo,然后在almost_full时发出写请求,这里写请求必须独立发出了,幸好datav频率没那么高,不然超出ping-pong buffer的fifo发出写请求是一个很危险的事。
还要注意的是,如果我们这里使用的base_addr都是以0x000结尾的,因此只要按照固定2的指数个突发长度读或者写,就不会触发4kb边界报错。但是我们要注意pe计算结束时必须清空fifo,所以写请求条件除了almost_full还必须加上pe_done。
三、工程代码实现(让ai缩短了)

点击查看代码
`timescale 1 ns / 1 ps

module pe_system_v1_0_m02_axi #
(
    parameter integer C_M_AXI_BURST_LEN	    = 256,
    parameter integer C_M_AXI_ID_WIDTH	    = 2,
    parameter integer C_M_AXI_ADDR_WIDTH	= 32,
    parameter integer C_M_AXI_DATA_WIDTH	= 32
)
(
    // --- Matrix C PE Interface ---
    input wire [C_M_AXI_ADDR_WIDTH-1:0]  c_ptr_addr,
    input wire                           c_ptr_addr_v, 
    input wire [C_M_AXI_ADDR_WIDTH-1:0]  c_col_addr,
    input wire                           c_col_addr_v, 
    input wire [C_M_AXI_ADDR_WIDTH-1:0]  c_val_addr,
    input wire                           c_val_addr_v, 

    input wire [C_M_AXI_DATA_WIDTH-1:0]  c_ptr_data,
    input wire           				 c_ptr_data_v,
    input wire [C_M_AXI_DATA_WIDTH-1:0]  c_col_data,
    input wire           				 c_col_data_v,
    input wire [C_M_AXI_DATA_WIDTH-1:0]  c_val_data,
    input wire           				 c_val_data_v,

    input wire [C_M_AXI_ADDR_WIDTH-1:0]  c_ptr_base_addr,
    input wire [C_M_AXI_ADDR_WIDTH-1:0]  c_col_base_addr,
    input wire [C_M_AXI_ADDR_WIDTH-1:0]  c_val_base_addr,
    input wire                           pe_done,

    // --- AXI4 写物理接口 ---
    input wire  M_AXI_ACLK,
    input wire  M_AXI_ARESETN,
    output wire [C_M_AXI_ID_WIDTH-1 : 0]    M_AXI_AWID,
    output wire [C_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_AWVALID,
    input  wire                             M_AXI_AWREADY,

    output wire [C_M_AXI_DATA_WIDTH-1 : 0]  M_AXI_WDATA,
    output wire [C_M_AXI_DATA_WIDTH/8-1 : 0] M_AXI_WSTRB,
    output wire                             M_AXI_WLAST,
    output wire                             M_AXI_WVALID,
    input  wire                             M_AXI_WREADY,

    input  wire [C_M_AXI_ID_WIDTH-1 : 0]    M_AXI_BID,
    input  wire [1 : 0]                     M_AXI_BRESP,
    input  wire                             M_AXI_BVALID,
    output wire                             M_AXI_BREADY,

    output wire                             TXN_DONE,
    output reg                              ERROR
);

    // --- 内部寄存器定义 ---
    parameter [1:0] IDLE = 2'b00, BUSY = 2'b01;
    reg [1:0] mst_exec_state;
    reg [C_M_AXI_ADDR_WIDTH-1 : 0]  axi_awaddr_reg;
    reg                             axi_awvalid;
    reg [7:0]                       axi_awlen_reg;
    reg [C_M_AXI_ID_WIDTH-1 : 0]    current_awid;
    reg [7:0]                       write_index;
    reg                             burst_write_active;
    reg                             w_active; // 新增:控制 W 通道数据流的开启

    // 三路独立偏移量 (复刻 M00 逻辑)
    reg [C_M_AXI_ADDR_WIDTH-1:0] ptr_offset, col_offset, val_offset;

    // FIFO 信号声明
    wire [10:0] c_ptr_data_cnt, c_col_data_cnt, c_val_data_cnt;
    wire c_ptr_fifo_rd_en, c_col_fifo_rd_en, c_val_fifo_rd_en;
    wire [31:0] c_ptr_fifo_dout, c_col_fifo_dout, c_val_fifo_dout;
    wire c_ptr_empty, c_col_empty, c_val_empty;
    wire c_ptr_almost_full, c_col_almost_full, c_val_almost_full;

    // --- AXI 物理接口赋值 ---
    assign M_AXI_AWID    = current_awid;
    assign M_AXI_AWADDR  = axi_awaddr_reg;
    assign M_AXI_AWLEN   = axi_awlen_reg;
    assign M_AXI_AWSIZE  = 3'b010; // 4 bytes
    assign M_AXI_AWBURST = 2'b01;
    assign M_AXI_AWLOCK  = 1'b0;
    assign M_AXI_AWCACHE = 4'b0011;
    assign M_AXI_AWPROT  = 3'h0;
    assign M_AXI_AWQOS   = 4'h0;
    assign M_AXI_AWVALID = axi_awvalid;

    assign M_AXI_WSTRB   = 4'b1111;
    assign M_AXI_BREADY  = 1'b1;
    assign TXN_DONE      = pe_done && c_ptr_empty && c_col_empty && c_val_empty && !burst_write_active;

    // --- W 通道核心控制 (解决 STABLE 错误) ---
    // 只有在 w_active 期间且对应 FIFO 不为空时,才允许拉高 WVALID
    assign M_AXI_WVALID = w_active && 
                         ((current_awid == 2'b00 && !c_ptr_empty) || 
                          (current_awid == 2'b01 && !c_col_empty) || 
                          (current_awid == 2'b10 && !c_val_empty));

    assign M_AXI_WDATA  = (current_awid == 2'b00) ? c_ptr_fifo_dout :
                         (current_awid == 2'b01) ? c_col_fifo_dout : c_val_fifo_dout;

    assign M_AXI_WLAST  = (write_index == axi_awlen_reg);

    // FIFO 读使能:只有总线真正握手成功(收走数据)时,才从 FIFO 弹出下一个
    // 这保证了 empty 信号只在时钟沿更新,且 WVALID 不会在 WREADY 为低时掉下来
    assign c_ptr_fifo_rd_en = (M_AXI_WVALID && M_AXI_WREADY && (current_awid == 2'b00));
    assign c_col_fifo_rd_en = (M_AXI_WVALID && M_AXI_WREADY && (current_awid == 2'b01));
    assign c_val_fifo_rd_en = (M_AXI_WVALID && M_AXI_WREADY && (current_awid == 2'b10));

    // --- 地址偏移量自增 (由 AW 握手驱动) ---
    always @(posedge M_AXI_ACLK) begin
        if (!M_AXI_ARESETN) begin
            ptr_offset <= 0; col_offset <= 0; val_offset <= 0;
        end else if (M_AXI_AWVALID && M_AXI_AWREADY) begin
            case(current_awid)
                2'b00: ptr_offset <= ptr_offset + ((axi_awlen_reg + 1) << 2);
                2'b01: col_offset <= col_offset + ((axi_awlen_reg + 1) << 2);
                2'b10: val_offset <= val_offset + ((axi_awlen_reg + 1) << 2);
            endcase
        end
    end

    // --- 状态机控制 ---
    always @(posedge M_AXI_ACLK) begin
        if (!M_AXI_ARESETN) begin
            mst_exec_state <= IDLE;
            axi_awvalid    <= 1'b0;
            axi_awaddr_reg <= 0;
            axi_awlen_reg  <= 0;
            current_awid   <= 0;
        end else begin
            case (mst_exec_state)
                IDLE: begin
                    if (!burst_write_active && !axi_awvalid) begin
                        if (c_ptr_almost_full || (pe_done && !c_ptr_empty)) begin
                            current_awid   <= 2'b00;
                            axi_awaddr_reg <= c_ptr_base_addr + ptr_offset;
                            axi_awlen_reg <= (c_ptr_data_cnt >= 256) ? 8'd255 : (c_ptr_data_cnt > 0 ? c_ptr_data_cnt - 1 : 8'd0);
                            axi_awvalid    <= 1'b1;
                            mst_exec_state <= BUSY;
                        end else if (c_col_almost_full || (pe_done && !c_col_empty)) begin
                            current_awid   <= 2'b01;
                            axi_awaddr_reg <= c_col_base_addr + col_offset;
                            axi_awlen_reg  <= (c_col_data_cnt >= 256) ? 8'd255 : (c_col_data_cnt > 0 ? c_col_data_cnt - 1 : 8'd0);
                            axi_awvalid    <= 1'b1;
                            mst_exec_state <= BUSY;
                        end else if (c_val_almost_full || (pe_done && !c_val_empty)) begin
                            current_awid   <= 2'b10;
                            axi_awaddr_reg <= c_val_base_addr + val_offset;
                            axi_awlen_reg  <= (c_val_data_cnt >= 256) ? 8'd255 : (c_val_data_cnt > 0 ? c_val_data_cnt - 1 : 8'd0);
                            axi_awvalid    <= 1'b1;
                            mst_exec_state <= BUSY;
                        end
                    end
                end

                BUSY: begin
                    if (M_AXI_AWREADY && axi_awvalid) axi_awvalid <= 1'b0;
                    // 当收到最后一个数据的写响应 BVALID 后,才返回 IDLE
                    if (!burst_write_active && !axi_awvalid) mst_exec_state <= IDLE;
                end
            endcase
        end
    end

    // 写数据活跃状态:AW握手开始,WLAST握手结束
    always @(posedge M_AXI_ACLK) begin
        if (!M_AXI_ARESETN) w_active <= 0;
        else if (axi_awvalid && M_AXI_AWREADY) w_active <= 1;
        else if (M_AXI_WVALID && M_AXI_WREADY && M_AXI_WLAST) w_active <= 0;
    end

    // 整个写突发活跃状态:从 AWVALID 到 BVALID
    always @(posedge M_AXI_ACLK) begin
        if (!M_AXI_ARESETN) burst_write_active <= 0;
        else if (axi_awvalid && M_AXI_AWREADY) burst_write_active <= 1;
        else if (M_AXI_BVALID && M_AXI_BREADY) burst_write_active <= 0;
    end

    // 写索引计数器
    always @(posedge M_AXI_ACLK) begin
        if (!M_AXI_ARESETN || (axi_awvalid && M_AXI_AWREADY)) begin
            write_index <= 0;
        end else if (M_AXI_WVALID && M_AXI_WREADY) begin
            write_index <= write_index + 1;
        end
    end

    // --- FIFO 实例化集群 ---
// ==========================================================
    // --- Matrix C FIFO 实例化集群 (替换 sync_fifo) ---
    // ==========================================================

    // C_PTR FIFO
    new_fifo c_ptr_fifo (
        .clk           (M_AXI_ACLK),
        .srst          (!M_AXI_ARESETN),   // 高电平复位
        .din           (c_ptr_data),
        .wr_en         (c_ptr_data_v),
        .rd_en         (c_ptr_fifo_rd_en),
        .dout          (c_ptr_fifo_dout),
        .empty         (c_ptr_empty),
        .full          (),                 // 留空
        .almost_full   (),                 // 留空(改用 prog_full)
        .almost_empty  (),
        .valid         (),
        .data_count    (c_ptr_data_cnt),
        .prog_full     (c_ptr_almost_full), // 对接逻辑用的 almost_full
        .prog_empty    (),
        .wr_rst_busy   (),
        .rd_rst_busy   ()
    );

    // C_COL FIFO
    new_fifo c_col_fifo (
        .clk           (M_AXI_ACLK),
        .srst          (!M_AXI_ARESETN),
        .din           (c_col_data),
        .wr_en         (c_col_data_v),
        .rd_en         (c_col_fifo_rd_en),
        .dout          (c_col_fifo_dout),
        .empty         (c_col_empty),
        .full          (),
        .almost_full   (),
        .almost_empty  (),
        .valid         (),
        .data_count    (c_col_data_cnt),
        .prog_full     (c_col_almost_full),
        .prog_empty    (),
        .wr_rst_busy   (),
        .rd_rst_busy   ()
    );

    // C_VAL FIFO
    new_fifo c_val_fifo (
        .clk           (M_AXI_ACLK),
        .srst          (!M_AXI_ARESETN),
        .din           (c_val_data),
        .wr_en         (c_val_data_v),
        .rd_en         (c_val_fifo_rd_en),
        .dout          (c_val_fifo_dout),
        .empty         (c_val_empty),
        .full          (),
        .almost_full   (),
        .almost_empty  (),
        .valid         (),
        .data_count    (c_val_data_cnt),
        .prog_full     (c_val_almost_full),
        .prog_empty    (),
        .wr_rst_busy   (),
        .rd_rst_busy   ()
    );
endmodule
posted @ 2026-03-23 23:15  hanhuang  阅读(6)  评论(0)    收藏  举报