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

接下来就是B矩阵接口的设计了,这个是调试时间最久,最鲁棒,但是还有待优化的一个接口。目的是在复盘中找出一套实现接口高鲁棒性的方法论。
一、设计前观测
B矩阵虽然和A矩阵都是只读AXI接口。但是在波形上要求是完全不一样的,A矩阵的各个通道都是从0递增,步长为1,具有极大规律性,不具有普适性。B矩阵的ptr通道是每次读两个连续的地址,col和val通道每次都一定的范围内递增1,但是也常常跳地址。并且由于这种特性,还要考虑4kb边界问题。不过还是幸好三个通道在一定范围内递增1,不然要是一直跨地址,使用AXI4无疑是浪费性能。
二、设计思路
由于ptr每次读两个连续地址,因此每次都得等ptr_addrv到来时再发出读请求。由于每次访问HBM都要消耗上百周期时间,这导致了带宽利用率特别低(貌似没有什么好办法,有可能加Cache能改善一下),col和val就是偶尔发一个新地址,之后的地址就保持递增1,然后在连续递增几次后又会突然跳转地址。面对这种情况,就得对input的addr进行检测,检测是否为下一地址,如果是就继续读fifo,如果否,就重新向HBM发出读请求,并且把fifo的数据清空。这里有条件可以加缓存,因为地址可能被复用。这里不加缓存。
三、工程实现
1、在这里使用FIFO IP核来降低布线拥挤,

点击查看代码
new_fifo b_ptr_fifo (.clk(M_AXI_ACLK), .srst(!M_AXI_ARESETN || ptr_fifo_flush), .din(M_AXI_RDATA), .wr_en(M_AXI_RVALID && (M_AXI_RID == 2'b10)), .rd_en(b_ptr_fifo_re), .dout(b_ptr_data), .empty(b_ptr_empty), .wr_rst_busy(b_ptr_wr_busy), .rd_rst_busy(b_ptr_rd_busy), .data_count(b_ptr_cnt), .full(b_ptr_full));
2、首先必须要把正常情况列举出来 AXI4接口有两个通道,先从通道切开,找到AR通道。什么情况下需要发出读请求,什么时候不能发出读请求呢(这是个必过的点,否则会导致数据还没到的时候反复发送读请求)? 答:在(addrv &&(last_ptr != addr)) || fifo_empty的时候发出读请求,在(addrv && (last_ptr == addr) && fifo_empty) || channel_active)的时候不能发出读请求。因为突发长度是16,有可能连续读的数量不够,读到第十七个数,就得重新访问HBM,而当请求发出去后,如果你当前通道已经被占用了,就不能再次发送,这样可能会出问题,事实上我也在这里出问题了,;因为首先逻辑不能完美闭合:那channel_active什么时候拉低呢?是要last还是有读数据进来进行,如果是last那太浪费时间,如果是有读数据进来立刻就通过id拉低active,那fifo是fwft,第一个数据刚进来就出去了,empty状态无法预测(仿真可以,上板难预测),因此我选择加一个更稳健的方法:地址锁。当数据还没回来时,地址锁变为ar通道握手的地址,直到换行才解锁,这样有什么用呢,可以保证在当前行这个通道不会再次发送地址读请求。 然后再来R通道,这个相对简单,因为只要通过ID识通道即可,但是危险的是这里换了fifo,那么就会有rd_rst_busy和wr_rst_busy。这个是读的所有数据不一定都是有用的,必须有个flush来冲刷fifo,但是fifo ip核你不知道busy啥时候结束。这里有两种方法:拉低ready信号,可能造成总线堵塞,但是相对安全。另一个是专门设计(我用的),已知flush是在换行时拉高,那么看是数据在途还是r通道空闲,如果是数据在途,那就得拉高到空闲时;如果是r通道空闲,就拉高一周期即可。而由于是因为换行新地址到来才flush,因此这个地址有效和地址信号必须存起来,等到busy拉低再发送,这样能保证fifo肯定是!busy情况下收到数据,当然了。 特殊情况考虑: 高并发:当ptr,col,val同时高频率拉高时怎么办,这个时候需要仲裁,这里ptr优先,col,val优先级低,所以必须有寄存器把他们存储起来然后在优先级高的读请求发送完在发送他们。 4kb边界限制:必须在地址进来时进行地址检测,看看突发长度会不会越过4kb边界,如果越过了,直接夭折突发长度,只读到4kb。 时序优化:这里只优化了最大杀手——4kb边界识别,由于需要加法器来计算访存地址,需要减法器来计算是否越界,再进入逻辑判断,这导致时序复杂度非常高,因此直接拆,对于这个接口来说就像是addrv和addr同时晚一俩周期进来。 可改进的点:4kb边界识别可以先不计算访存地址,直接减法,这样识别不到的就可以晚一个周期到来而不用两个周期。 在写AXI接口时,最好多一些标记信号:当前数据是否在途,是否结束,pe是否需要换行,是否继续读,这些脉冲信号或者状态信号是可以在工程之初先严谨的标注出来的,这样能极大帮助后面的逻辑拓展。 展示猪肝:
点击查看代码
`timescale 1 ns / 1 ps

module pe_system_v1_0_m01_axi #
(
    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 B PE 接口 ---
    input  wire [31:0] b_ptr_addr, input  wire b_ptr_addr_v, 
    input  wire [31:0] b_col_addr, input  wire b_col_addr_v, 
    input  wire [31:0] b_val_addr, input  wire b_val_addr_v, 

    output wire [31:0] b_ptr_data, output wire b_ptr_data_v,
    output wire [31:0] b_col_data, output wire b_col_data_v,
    output wire [31:0] b_val_data, output wire b_val_data_v,

    input  wire [31:0] b_ptr_base_addr,
    input  wire [31:0] b_col_base_addr,
    input  wire [31:0] b_val_base_addr,

    // --- AXI 总线 ---
    input  wire M_AXI_ACLK, input wire M_AXI_ARESETN,
    output wire [1:0]  M_AXI_ARID, output wire [31:0] M_AXI_ARADDR,
    output wire [7:0]  M_AXI_ARLEN, output wire [2:0] M_AXI_ARSIZE,
    output wire        M_AXI_ARVALID, input wire M_AXI_ARREADY,
    input  wire [1:0]  M_AXI_RID, input wire [31:0] M_AXI_RDATA,
    input  wire [1:0]  M_AXI_RRESP,
    input  wire        M_AXI_RLAST,
    input  wire        M_AXI_RVALID, output wire M_AXI_RREADY,

    output wire [1:0]  M_AXI_ARBURST,
    output wire        TXN_DONE, output reg ERROR
);

    // --- 静态 AXI 信号 ---
    assign M_AXI_ARSIZE  = 3'b010; // 4 Bytes
    assign M_AXI_ARBURST = 2'b01;  // INCR mode
    assign M_AXI_RREADY  = 1'b1;
    assign TXN_DONE      = 1'b0;

    // ==============================================================================
    // 1. 信号声明
    // ==============================================================================
    reg [31:0] last_ptr, last_col, last_val;
    reg [31:0] ptr_addr_reg, col_addr_reg, val_addr_reg;
    reg        pending_ptr, pending_col, pending_val;
    reg        ptr_fifo_flush, col_fifo_flush, val_fifo_flush;
    reg [2:0]  data_used; 
    reg [7:0]  read_index;

    reg ptr_is_jumping, col_is_jumping, val_is_jumping;
    wire b_ptr_empty, b_col_empty, b_val_empty;

    // 跳转检测:统一使用流水线第二拍寄存器
    wire ptr_addr_false = b_ptr_addrv_r2 && (b_ptr_empty || (b_ptr_addr_t != last_ptr + 1));
    wire col_addr_false = b_col_addrv_r2 && (b_col_empty || (b_col_addr_t != last_col + 1));
    wire val_addr_false = b_val_addrv_r2 && (b_val_empty || (b_val_addr_t != last_val + 1));

    // ==============================================================================
    // 2. 跳转识别逻辑 (组合逻辑)
    // ==============================================================================
    always @(*) begin
        if (ptr_addr_false) ptr_is_jumping = 1'b1;
        else if (M_AXI_RVALID && M_AXI_RREADY && (M_AXI_RID == 2'b10) && (read_index == 0)) ptr_is_jumping = 1'b0;
        else ptr_is_jumping = ptr_is_jumping; 
    end
    always @(*) begin
        if (col_addr_false) col_is_jumping = 1'b1;
        else if (M_AXI_RVALID && M_AXI_RREADY && (M_AXI_RID == 2'b01) && (read_index == 0)) col_is_jumping = 1'b0;
        else col_is_jumping = col_is_jumping;
    end
    always @(*) begin
        if (val_addr_false) val_is_jumping = 1'b1;
        else if (M_AXI_RVALID && M_AXI_RREADY && (M_AXI_RID == 2'b00) && (read_index == 0)) val_is_jumping = 1'b0;
        else val_is_jumping = val_is_jumping;
    end

    // ==============================================================================
    // 3. FIFO 控制逻辑
    // ==============================================================================
    wire b_ptr_fifo_re = (b_ptr_addrv_r2 || pending_ptr) && !b_ptr_empty && !ptr_fifo_flush && !ptr_is_jumping && !b_ptr_rd_busy;
    wire b_col_fifo_re = (b_col_addrv_r2 || pending_col) && !b_col_empty && !col_fifo_flush && !col_is_jumping && !b_col_rd_busy;
    wire b_val_fifo_re = (b_val_addrv_r2 || pending_val) && !b_val_empty && !val_fifo_flush && !val_is_jumping && !b_val_rd_busy;

    assign b_ptr_data_v = b_ptr_fifo_re;
    assign b_col_data_v = b_col_fifo_re;
    assign b_val_data_v = b_val_fifo_re;

    always @(posedge M_AXI_ACLK) begin
        if (!M_AXI_ARESETN || (M_AXI_RVALID && M_AXI_RREADY && M_AXI_RLAST))
            read_index <= 8'd0;
        else if (M_AXI_RVALID && M_AXI_RREADY)
            read_index <= read_index + 1'b1;
    end

    always @(posedge M_AXI_ACLK) begin
        if (!M_AXI_ARESETN) begin
            last_ptr <= 0; last_col <= 0; last_val <= 0;
            data_used <= 3'b0;
        end else begin
            if (ptr_addr_false) last_ptr <= b_ptr_addr_t; else if (b_ptr_addrv_r2) last_ptr <= last_ptr + 1'b1;
            if (col_addr_false) last_col <= b_col_addr_t; else if (b_col_addrv_r2) last_col <= last_col + 1'b1;
            if (val_addr_false) last_val <= b_val_addr_t; else if (b_val_addrv_r2) last_val <= last_val + 1'b1;
            
            if (ptr_addr_false) data_used[0] <= 1'b0;
            else if (M_AXI_RVALID && M_AXI_RREADY && (M_AXI_RID == 2'b10) && (read_index == 0)) data_used[0] <= 1'b1;
            if (col_addr_false) data_used[1] <= 1'b0;
            else if (M_AXI_RVALID && M_AXI_RREADY && (M_AXI_RID == 2'b01) && (read_index == 0)) data_used[1] <= 1'b1;
            if (val_addr_false) data_used[2] <= 1'b0;
            else if (M_AXI_RVALID && M_AXI_RREADY && (M_AXI_RID == 2'b00) && (read_index == 0)) data_used[2] <= 1'b1;
        end
    end

    always @(posedge M_AXI_ACLK) begin
        if (!M_AXI_ARESETN) begin
            pending_ptr <= 0; ptr_addr_reg <= 0; ptr_fifo_flush <= 0;
            pending_col <= 0; col_addr_reg <= 0; col_fifo_flush <= 0;
            pending_val <= 0; val_addr_reg <= 0; val_fifo_flush <= 0;
            ERROR <= 0;
        end else begin
            if (ptr_addr_false) begin pending_ptr <= 1'b1; ptr_addr_reg <= b_ptr_addr_t; ptr_fifo_flush <= 1'b1; end 
            else if (b_ptr_fifo_re) pending_ptr <= 1'b0;
            if (!ptr_active && ptr_fifo_flush) ptr_fifo_flush <= 1'b0;

            if (col_addr_false) begin pending_col <= 1'b1; col_addr_reg <= b_col_addr_t; col_fifo_flush <= 1'b1; end 
            else if (b_col_fifo_re) pending_col <= 1'b0;
            if (!col_active && col_fifo_flush) col_fifo_flush <= 1'b0;

            if (val_addr_false) begin pending_val <= 1'b1; val_addr_reg <= b_val_addr_t; val_fifo_flush <= 1'b1; end 
            else if (b_val_fifo_re) pending_val <= 1'b0;
            if (!val_active && val_fifo_flush) val_fifo_flush <= 1'b0;
        end
    end

    // ==============================================================================
    // 4. AXI 读状态机流水线 (阻塞赋值)
    // ==============================================================================
    parameter [1:0] IDLE = 2'b00, ISSUE = 2'b10;
    reg [1:0]  mst_exec_state;
    reg [31:0] araddr_r; reg [7:0] arlen_r; reg [1:0] arid_r; reg arvalid_r;

    // 内部流水线寄存器定义
    reg [31:0] b_ptr_addr_final, b_ptr_addr_s, b_ptr_addr_t, b_ptr_addr_final_reg;
    reg [31:0] b_col_addr_final, b_col_addr_s, b_col_addr_t, b_col_addr_final_reg;
    reg [31:0] b_val_addr_final, b_val_addr_s, b_val_addr_t, b_val_addr_final_reg;
    reg [11:0] b_ptr_limit, b_col_limit, b_val_limit;
    reg        b_ptr_addrv_r1, b_ptr_addrv_r2, b_col_addrv_r1, b_col_addrv_r2, b_val_addrv_r1, b_val_addrv_r2;
    reg [7:0]  b_arlen_ptr_final, b_arlen_col_final, b_arlen_val_final;

    // --- 流水线周期 1:阻塞计算 ---
    always @(posedge M_AXI_ACLK) begin
        if (!M_AXI_ARESETN) begin
            b_ptr_addr_final = 0; b_ptr_limit = 0; b_ptr_addrv_r1 <= 0; b_ptr_addr_s <= 0;
            b_col_addr_final = 0; b_col_limit = 0; b_col_addrv_r1 <= 0; b_col_addr_s <= 0;
            b_val_addr_final = 0; b_val_limit = 0; b_val_addrv_r1 <= 0; b_val_addr_s <= 0;
        end else begin
            // Ptr
            b_ptr_addr_final = b_ptr_base_addr + ((b_ptr_addr_v ? b_ptr_addr : ptr_addr_reg) << 2);
            b_ptr_limit      = (12'hFFF - b_ptr_addr_final[11:0]) >> 2;
            b_ptr_addrv_r1   <= b_ptr_addr_v;
            b_ptr_addr_s     <= b_ptr_addr;
            // Col
            b_col_addr_final = b_col_base_addr + ((b_col_addr_v ? b_col_addr : col_addr_reg) << 2);
            b_col_limit      = (12'hFFF - b_col_addr_final[11:0]) >> 2;
            b_col_addrv_r1   <= b_col_addr_v;
            b_col_addr_s     <= b_col_addr;
            // Val
            b_val_addr_final = b_val_base_addr + ((b_val_addr_v ? b_val_addr : val_addr_reg) << 2);
            b_val_limit      = (12'hFFF - b_val_addr_final[11:0]) >> 2;
            b_val_addrv_r1   <= b_val_addr_v;
            b_val_addr_s     <= b_val_addr;
        end
    end
    
    // --- 流水线周期 2:打拍寄存 ---
    always @(posedge M_AXI_ACLK) begin
        if (!M_AXI_ARESETN) begin
            b_arlen_ptr_final <= 0; b_ptr_addr_final_reg <= 0; b_ptr_addrv_r2 <= 0; b_ptr_addr_t <= 0;
            b_arlen_col_final <= 0; b_col_addr_final_reg <= 0; b_col_addrv_r2 <= 0; b_col_addr_t <= 0;
            b_arlen_val_final <= 0; b_val_addr_final_reg <= 0; b_val_addrv_r2 <= 0; b_val_addr_t <= 0;
        end else begin
            b_arlen_ptr_final   <= (8'd255 > b_ptr_limit) ? b_ptr_limit[7:0] : 8'd255;
            b_ptr_addr_final_reg <= b_ptr_addr_final;
            b_ptr_addrv_r2       <= b_ptr_addrv_r1;
            b_ptr_addr_t         <= b_ptr_addr_s;

            b_arlen_col_final   <= (8'd255 > b_col_limit) ? b_col_limit[7:0] : 8'd255;
            b_col_addr_final_reg <= b_col_addr_final;
            b_col_addrv_r2       <= b_col_addrv_r1;
            b_col_addr_t         <= b_col_addr_s;

            b_arlen_val_final   <= (8'd255 > b_val_limit) ? b_val_limit[7:0] : 8'd255;
            b_val_addr_final_reg <= b_val_addr_final;
            b_val_addrv_r2       <= b_val_addrv_r1;
            b_val_addr_t         <= b_val_addr_s;
        end
    end

    // --- AXI 读状态机 (使用流水线输出) ---
    always @(posedge M_AXI_ACLK) begin
        if (!M_AXI_ARESETN) begin
            mst_exec_state <= IDLE; arvalid_r <= 0; arid_r <= 0; arlen_r <= 0; araddr_r <= 0;
        end else begin
            case (mst_exec_state)
                IDLE: begin
                    if (!arvalid_r) begin
                        if ((b_ptr_addrv_r2 || pending_ptr) && (ptr_is_jumping || b_ptr_empty) && (!ptr_active) && !b_ptr_wr_busy && !ptr_addr_false && !ptr_fifo_flush && (b_ptr_addr_issued != b_ptr_addr_final_reg)) begin
                            arid_r <= 2'b10; arlen_r <= b_arlen_ptr_final; araddr_r <= b_ptr_addr_final_reg; mst_exec_state <= ISSUE;
                        end
                        else if ((b_col_addrv_r2 || pending_col) && (col_is_jumping || b_col_empty) && (!col_active) && !b_col_wr_busy && !col_addr_false && !col_fifo_flush && (b_col_addr_issued != b_col_addr_final_reg)) begin
                            arid_r <= 2'b01; arlen_r <= b_arlen_col_final; araddr_r <= b_col_addr_final_reg; mst_exec_state <= ISSUE;
                        end
                        else if ((b_val_addrv_r2 || pending_val) && (val_is_jumping || b_val_empty) && (!val_active) && !b_val_wr_busy && !val_addr_false && !val_fifo_flush && (b_val_addr_issued != b_val_addr_final_reg)) begin
                            arid_r <= 2'b00; arlen_r <= b_arlen_val_final; araddr_r <= b_val_addr_final_reg; mst_exec_state <= ISSUE;
                        end
                    end
                end
                ISSUE: begin
                    arvalid_r <= 1'b1;
                    if (M_AXI_ARREADY && arvalid_r) begin arvalid_r <= 1'b0; mst_exec_state <= IDLE; end
                end
            endcase
        end
    end

    // --- 三路地址契约锁 (issued 寄存器) ---
    reg [31:0] b_ptr_addr_issued;
    reg [31:0] b_col_addr_issued;
    reg [31:0] b_val_addr_issued;

    always @(posedge M_AXI_ACLK) begin
        if (!M_AXI_ARESETN) begin
			b_ptr_addr_issued <= 32'hFFFFFFFF;
			b_col_addr_issued <= 32'hFFFFFFFF;
			b_val_addr_issued <= 32'hFFFFFFFF;
		end
		else begin
			if(b_ptr_fifo_re || ptr_addr_false || b_col_fifo_re || col_addr_false || b_val_fifo_re || val_addr_false)begin
				if(b_ptr_fifo_re || ptr_addr_false)begin
					b_ptr_addr_issued <= 32'hFFFFFFFF;
				end
				if(b_col_fifo_re || col_addr_false)begin
					b_col_addr_issued <= 32'hFFFFFFFF;
				end
				if(b_val_fifo_re || val_addr_false)begin
					b_val_addr_issued <= 32'hFFFFFFFF;
				end
			end
			else begin
			if(!arvalid_r && (b_ptr_addrv_r2 || pending_ptr) && (ptr_is_jumping || b_ptr_empty) && (!ptr_active) && !b_ptr_wr_busy && !ptr_addr_false && !ptr_fifo_flush && (b_ptr_addr_issued != b_ptr_addr_final_reg) && (mst_exec_state == IDLE))begin
				b_ptr_addr_issued <= b_ptr_addr_final_reg;
			end
			else if(!arvalid_r && (b_col_addrv_r2 || pending_col) && (col_is_jumping || b_col_empty) && (!col_active) && !b_col_wr_busy && !col_addr_false && !col_fifo_flush && (b_col_addr_issued != b_col_addr_final_reg)&&(mst_exec_state == IDLE))begin
				b_col_addr_issued <= b_col_addr_final_reg;
			end
			else if(!arvalid_r && (b_val_addrv_r2 || pending_val) && (val_is_jumping || b_val_empty) && (!val_active) && !b_val_wr_busy && !val_addr_false && !val_fifo_flush && (b_val_addr_issued != b_val_addr_final_reg)&&(mst_exec_state == IDLE))begin
				b_val_addr_issued <= b_val_addr_final_reg;
			end
			end
		end
	end

    assign M_AXI_ARID    = arid_r; 
    assign M_AXI_ARADDR  = araddr_r; 
    assign M_AXI_ARLEN   = arlen_r;
    assign M_AXI_ARVALID = arvalid_r; 

    // ==============================================================================
    // 5. FIFO IP 实例化
    // ==============================================================================
    wire [10:0] b_ptr_cnt, b_col_cnt, b_val_cnt;
    wire b_ptr_full, b_col_full, b_val_full;
    wire b_ptr_wr_busy, b_col_wr_busy, b_val_wr_busy;
    wire b_ptr_rd_busy, b_col_rd_busy, b_val_rd_busy;

    new_fifo b_ptr_fifo (.clk(M_AXI_ACLK), .srst(!M_AXI_ARESETN || ptr_fifo_flush), .din(M_AXI_RDATA), .wr_en(M_AXI_RVALID && (M_AXI_RID == 2'b10)), .rd_en(b_ptr_fifo_re), .dout(b_ptr_data), .empty(b_ptr_empty), .wr_rst_busy(b_ptr_wr_busy), .rd_rst_busy(b_ptr_rd_busy), .data_count(b_ptr_cnt), .full(b_ptr_full));
    new_fifo b_col_fifo (.clk(M_AXI_ACLK), .srst(!M_AXI_ARESETN || col_fifo_flush), .din(M_AXI_RDATA), .wr_en(M_AXI_RVALID && (M_AXI_RID == 2'b01)), .rd_en(b_col_fifo_re), .dout(b_col_data), .empty(b_col_empty), .wr_rst_busy(b_col_wr_busy), .rd_rst_busy(b_col_rd_busy), .data_count(b_col_cnt), .full(b_col_full));
    new_fifo b_val_fifo (.clk(M_AXI_ACLK), .srst(!M_AXI_ARESETN || val_fifo_flush), .din(M_AXI_RDATA), .wr_en(M_AXI_RVALID && (M_AXI_RID == 2'b00)), .rd_en(b_val_fifo_re), .dout(b_val_data), .empty(b_val_empty), .wr_rst_busy(b_val_wr_busy), .rd_rst_busy(b_val_rd_busy), .data_count(b_val_cnt), .full(b_val_full));

    // ==============================================================================
    // 6. 在途计数器逻辑
    // ==============================================================================
    reg [1:0] ptr_flight_cnt, col_flight_cnt, val_flight_cnt;
    always @(posedge M_AXI_ACLK) begin
        if (!M_AXI_ARESETN) {ptr_flight_cnt, col_flight_cnt, val_flight_cnt} <= 6'b0;
        else begin
            case ({ (M_AXI_ARVALID && M_AXI_ARREADY && (M_AXI_ARID == 2'b10)), (M_AXI_RVALID && M_AXI_RREADY && (M_AXI_RID == 2'b10) && M_AXI_RLAST) })
                2'b10: ptr_flight_cnt <= ptr_flight_cnt + 1'b1;
                2'b01: ptr_flight_cnt <= ptr_flight_cnt - 1'b1;
            endcase
            case ({ (M_AXI_ARVALID && M_AXI_ARREADY && (M_AXI_ARID == 2'b01)), (M_AXI_RVALID && M_AXI_RREADY && (M_AXI_RID == 2'b01) && M_AXI_RLAST) })
                2'b10: col_flight_cnt <= col_flight_cnt + 1'b1;
                2'b01: col_flight_cnt <= col_flight_cnt - 1'b1;
            endcase
            case ({ (M_AXI_ARVALID && M_AXI_ARREADY && (M_AXI_ARID == 2'b00)), (M_AXI_RVALID && M_AXI_RREADY && (M_AXI_RID == 2'b00) && M_AXI_RLAST) })
                2'b10: val_flight_cnt <= val_flight_cnt + 1'b1;
                2'b01: val_flight_cnt <= val_flight_cnt - 1'b1;
            endcase
        end
    end

    wire ptr_active = (ptr_flight_cnt > 2'b0);
    wire col_active = (col_flight_cnt > 2'b0);
    wire val_active = (val_flight_cnt > 2'b0);

endmodule
posted @ 2026-03-24 10:57  hanhuang  阅读(7)  评论(0)    收藏  举报