// ═══════════════════════════════════════════════════════ // 基础时序乘法器模块 (无 DSP,移位累加,32个周期) // ═══════════════════════════════════════════════════════ module shift_add_mult ( input clk, input rstn, input start, input [31:0] a, input [31:0] b, output reg [63:0] p, output reg done ); reg [5:0] cnt; reg [63:0] a_reg; reg [31:0] b_reg; reg busy; always @(posedge clk) begin if (!rstn) begin busy <= 1'b0; done <= 1'b0; p <= 64'b0; cnt <= 6'd0; a_reg<= 64'b0; b_reg<= 32'b0; end else begin if (start && !busy) begin a_reg <= {32'b0, a}; b_reg <= b; p <= 64'b0; cnt <= 6'd0; busy <= 1'b1; done <= 1'b0; end else if (busy) begin if (cnt == 6'd32) begin busy <= 1'b0; done <= 1'b1; end else begin // 如果 b 的当前最低位为 1,则累加 a if (b_reg[0]) begin p <= p + a_reg; end a_reg <= a_reg << 1; b_reg <= b_reg >> 1; cnt <= cnt + 6'd1; end end else begin done <= 1'b0; end end end endmodule // ═══════════════════════════════════════════════════════ // 矩阵乘法器顶层模块 (AXI Burst Slave + Ping-Pong) // ═══════════════════════════════════════════════════════ module matmul_axi_slave ( input clk, input resetn, // AXI Write Address Channel input [4:0] s_axi_awid, input [31:0] s_axi_awaddr, input [7:0] s_axi_awlen, input [2:0] s_axi_awsize, input [1:0] s_axi_awburst, input s_axi_awlock, input [3:0] s_axi_awcache, input [2:0] s_axi_awprot, input s_axi_awvalid, output reg s_axi_awready, // AXI Write Data Channel input [4:0] s_axi_wid, input [31:0] s_axi_wdata, input [3:0] s_axi_wstrb, input s_axi_wlast, input s_axi_wvalid, output reg s_axi_wready, // AXI Write Response Channel output [4:0] s_axi_bid, output [1:0] s_axi_bresp, output reg s_axi_bvalid, input s_axi_bready, // AXI Read Address Channel input [4:0] s_axi_arid, input [31:0] s_axi_araddr, input [7:0] s_axi_arlen, input [2:0] s_axi_arsize, input [1:0] s_axi_arburst, input s_axi_arlock, input [3:0] s_axi_arcache, input [2:0] s_axi_arprot, input s_axi_arvalid, output reg s_axi_arready, // AXI Read Data Channel output reg [4:0] s_axi_rid, output reg [31:0] s_axi_rdata, output [1:0] s_axi_rresp, output reg s_axi_rlast, output reg s_axi_rvalid, input s_axi_rready ); // ========================================== // 内存地址映射 (Memory Map) // ========================================== localparam ADDR_CTRL = 12'h000; localparam ADDR_STATUS = 12'h004; // Bank 0 (可完全当作原来的单缓冲使用) localparam ADDR_B0_A = 12'h100; localparam ADDR_B0_B = 12'h140; localparam ADDR_B0_C = 12'h180; // Bank 1 (供无等待计算扩展使用) localparam ADDR_B1_A = 12'h300; localparam ADDR_B1_B = 12'h340; localparam ADDR_B1_C = 12'h380; // ========================================== // 寄存器与数据缓冲 // ========================================== reg req_0, req_1; reg busy; reg done; reg error; reg [31:0] b0_a [0:15]; reg [31:0] b0_b [0:15]; reg [65:0] b0_c [0:15]; reg [31:0] b1_a [0:15]; reg [31:0] b1_b [0:15]; reg [65:0] b1_c [0:15]; function [31:0] apply_wstrb; input [31:0] old_value; input [31:0] new_value; input [3:0] strobe; begin apply_wstrb = old_value; if (strobe[0]) apply_wstrb[7:0] = new_value[7:0]; if (strobe[1]) apply_wstrb[15:8] = new_value[15:8]; if (strobe[2]) apply_wstrb[23:16] = new_value[23:16]; if (strobe[3]) apply_wstrb[31:24] = new_value[31:24]; end endfunction // ========================================== // AXI Write 通道逻辑 (支持 INCR Burst) // ========================================== localparam W_IDLE = 2'd0; localparam W_DATA = 2'd1; localparam W_RESP = 2'd2; reg [1:0] w_state; reg [31:0] w_addr; reg [4:0] w_id; reg [1:0] w_burst; wire [11:0] w_offset = w_addr[11:0]; assign s_axi_bid = w_id; assign s_axi_bresp = 2'b00; // 前向声明引擎的状态 localparam C_IDLE = 2'd0; localparam C_CALC = 2'd1; localparam C_WAIT_MULT = 2'd2; localparam C_ACCUM = 2'd3; reg [1:0] state; reg cur_bank; reg calc_done_pulse; always @(posedge clk) begin if (!resetn) begin w_state <= W_IDLE; s_axi_awready <= 1'b1; s_axi_wready <= 1'b0; s_axi_bvalid <= 1'b0; req_0 <= 1'b0; req_1 <= 1'b0; done <= 1'b0; error <= 1'b0; end else begin // 引擎接管请求后清零 if (req_0 && state == C_CALC && cur_bank == 1'b0) req_0 <= 1'b0; if (req_1 && state == C_CALC && cur_bank == 1'b1) req_1 <= 1'b0; // 引擎计算完成,拉高通用 done 标志 if (calc_done_pulse) begin done <= 1'b1; end case (w_state) W_IDLE: begin s_axi_awready <= 1'b1; if (s_axi_awvalid && s_axi_awready) begin s_axi_awready <= 1'b0; s_axi_wready <= 1'b1; w_addr <= s_axi_awaddr; w_id <= s_axi_awid; w_burst <= s_axi_awburst; w_state <= W_DATA; end end W_DATA: begin if (s_axi_wvalid && s_axi_wready) begin // ===== 写地址译码与数据装载 ===== if (w_offset == ADDR_CTRL) begin if (s_axi_wdata[0]) req_0 <= 1'b1; // 启动 Bank 0 if (s_axi_wdata[1]) req_1 <= 1'b1; // 启动 Bank 1 end else if (w_offset == ADDR_STATUS) begin if (s_axi_wdata[1]) done <= 1'b0; // 写 1 清零 Done (兼容旧版TB) if (s_axi_wdata[2]) error <= 1'b0; // 写 1 清零 Error end else if (w_offset >= ADDR_B0_A && w_offset < ADDR_B0_B) b0_a[(w_offset - ADDR_B0_A) >> 2] <= apply_wstrb(b0_a[(w_offset - ADDR_B0_A) >> 2], s_axi_wdata, s_axi_wstrb); else if (w_offset >= ADDR_B0_B && w_offset < ADDR_B0_C) b0_b[(w_offset - ADDR_B0_B) >> 2] <= apply_wstrb(b0_b[(w_offset - ADDR_B0_B) >> 2], s_axi_wdata, s_axi_wstrb); else if (w_offset >= ADDR_B1_A && w_offset < ADDR_B1_B) b1_a[(w_offset - ADDR_B1_A) >> 2] <= apply_wstrb(b1_a[(w_offset - ADDR_B1_A) >> 2], s_axi_wdata, s_axi_wstrb); else if (w_offset >= ADDR_B1_B && w_offset < ADDR_B1_C) b1_b[(w_offset - ADDR_B1_B) >> 2] <= apply_wstrb(b1_b[(w_offset - ADDR_B1_B) >> 2], s_axi_wdata, s_axi_wstrb); else begin // 访问无效地址,拉高 error 标志 error <= 1'b1; end // ===== Burst 地址累加 ===== if (w_burst == 2'b01) w_addr <= w_addr + 32'd4; if (s_axi_wlast) begin s_axi_wready <= 1'b0; s_axi_bvalid <= 1'b1; w_state <= W_RESP; end end end W_RESP: begin if (s_axi_bvalid && s_axi_bready) begin s_axi_bvalid <= 1'b0; s_axi_awready <= 1'b1; w_state <= W_IDLE; end end default: w_state <= W_IDLE; endcase end end // ========================================== // AXI Read 通道逻辑 (支持 INCR Burst) // ========================================== localparam R_IDLE = 2'd0; localparam R_DATA = 2'd1; reg [1:0] r_state; reg [31:0] r_addr; reg [7:0] r_cnt; reg [7:0] r_len; reg [1:0] r_burst; wire [11:0] r_offset = r_addr[11:0]; assign s_axi_rresp = 2'b00; wire [5:0] c0_offset = (r_offset - ADDR_B0_C) >> 2; wire [3:0] c0_elem = c0_offset / 3; wire [1:0] c0_part = c0_offset % 3; wire [65:0] target_c0 = b0_c[c0_elem]; wire [5:0] c1_offset = (r_offset - ADDR_B1_C) >> 2; wire [3:0] c1_elem = c1_offset / 3; wire [1:0] c1_part = c1_offset % 3; wire [65:0] target_c1 = b1_c[c1_elem]; always @(posedge clk) begin if (!resetn) begin r_state <= R_IDLE; s_axi_arready <= 1'b1; s_axi_rvalid <= 1'b0; s_axi_rlast <= 1'b0; s_axi_rid <= 5'b0; end else begin case (r_state) R_IDLE: begin s_axi_arready <= 1'b1; if (s_axi_arvalid && s_axi_arready) begin s_axi_arready <= 1'b0; r_addr <= s_axi_araddr; r_len <= s_axi_arlen; r_burst <= s_axi_arburst; r_cnt <= 8'd0; s_axi_rvalid <= 1'b1; s_axi_rlast <= (s_axi_arlen == 8'd0); s_axi_rid <= s_axi_arid; r_state <= R_DATA; end end R_DATA: begin if (s_axi_rvalid && s_axi_rready) begin if (r_cnt == r_len) begin s_axi_rvalid <= 1'b0; s_axi_rlast <= 1'b0; s_axi_arready <= 1'b1; r_state <= R_IDLE; end else begin r_cnt <= r_cnt + 8'd1; s_axi_rlast <= (r_cnt + 8'd1 == r_len); if (r_burst == 2'b01) r_addr <= r_addr + 32'd4; end end end default: r_state <= R_IDLE; endcase end end // 修复:将 wire 声明提取到 always 块外部 wire [11:0] current_r_offset = (r_state == R_DATA) ? r_offset : s_axi_araddr[11:0]; // 组合逻辑准备 Read 数据 always @(*) begin s_axi_rdata = 32'b0; if (r_state == R_DATA || (r_state == R_IDLE && s_axi_arvalid)) begin if (current_r_offset == ADDR_CTRL) s_axi_rdata = {30'b0, req_1, req_0}; else if (current_r_offset == ADDR_STATUS) s_axi_rdata = {29'b0, error, done, busy}; // 完美兼容旧 TB else if (current_r_offset >= ADDR_B0_A && current_r_offset < ADDR_B0_B) s_axi_rdata = b0_a[(current_r_offset - ADDR_B0_A) >> 2]; else if (current_r_offset >= ADDR_B0_B && current_r_offset < ADDR_B0_C) s_axi_rdata = b0_b[(current_r_offset - ADDR_B0_B) >> 2]; else if (current_r_offset >= ADDR_B0_C && current_r_offset < (ADDR_B0_C + 12'h0C0)) begin if (c0_part == 2'd0) s_axi_rdata = target_c0[31:0]; else if (c0_part == 2'd1) s_axi_rdata = target_c0[63:32]; else s_axi_rdata = {30'b0, target_c0[65:64]}; end else if (current_r_offset >= ADDR_B1_A && current_r_offset < ADDR_B1_B) s_axi_rdata = b1_a[(current_r_offset - ADDR_B1_A) >> 2]; else if (current_r_offset >= ADDR_B1_B && current_r_offset < ADDR_B1_C) s_axi_rdata = b1_b[(current_r_offset - ADDR_B1_B) >> 2]; else if (current_r_offset >= ADDR_B1_C && current_r_offset < (ADDR_B1_C + 12'h0C0)) begin if (c1_part == 2'd0) s_axi_rdata = target_c1[31:0]; else if (c1_part == 2'd1) s_axi_rdata = target_c1[63:32]; else s_axi_rdata = {30'b0, target_c1[65:64]}; end end end // ========================================== // 计算引擎 (Compute FSM) // ========================================== reg [3:0] calc_idx; wire [1:0] row = calc_idx[3:2]; wire [1:0] col = calc_idx[1:0]; reg mult_start; wire mult_done [0:3]; wire [63:0] mult_p [0:3]; reg [63:0] mult_p_r [0:3]; // registered for stable accumulation reg [31:0] m_in_a [0:3]; reg [31:0] m_in_b [0:3]; genvar i; generate for (i = 0; i < 4; i = i + 1) begin : mult_inst shift_add_mult u_mult ( .clk (clk), .rstn (resetn), .start (mult_start), .a (m_in_a[i]), .b (m_in_b[i]), .p (mult_p[i]), .done (mult_done[i]) ); end endgenerate always @(posedge clk) begin if (!resetn) begin state <= C_IDLE; busy <= 1'b0; cur_bank <= 1'b0; calc_idx <= 4'd0; mult_start <= 1'b0; calc_done_pulse <= 1'b0; end else begin calc_done_pulse <= 1'b0; case (state) C_IDLE: begin calc_idx <= 4'd0; if (req_0) begin cur_bank <= 1'b0; busy <= 1'b1; state <= C_CALC; end else if (req_1) begin cur_bank <= 1'b1; busy <= 1'b1; state <= C_CALC; end else begin busy <= 1'b0; end end C_CALC: begin if (cur_bank == 1'b0) begin m_in_a[0] <= b0_a[{row, 2'd0}]; m_in_b[0] <= b0_b[{2'd0, col}]; m_in_a[1] <= b0_a[{row, 2'd1}]; m_in_b[1] <= b0_b[{2'd1, col}]; m_in_a[2] <= b0_a[{row, 2'd2}]; m_in_b[2] <= b0_b[{2'd2, col}]; m_in_a[3] <= b0_a[{row, 2'd3}]; m_in_b[3] <= b0_b[{2'd3, col}]; end else begin m_in_a[0] <= b1_a[{row, 2'd0}]; m_in_b[0] <= b1_b[{2'd0, col}]; m_in_a[1] <= b1_a[{row, 2'd1}]; m_in_b[1] <= b1_b[{2'd1, col}]; m_in_a[2] <= b1_a[{row, 2'd2}]; m_in_b[2] <= b1_b[{2'd2, col}]; m_in_a[3] <= b1_a[{row, 2'd3}]; m_in_b[3] <= b1_b[{2'd3, col}]; end mult_start <= 1'b1; state <= C_WAIT_MULT; end C_WAIT_MULT: begin mult_start <= 1'b0; if (mult_done[0] && mult_done[1] && mult_done[2] && mult_done[3]) begin mult_p_r[0] <= mult_p[0]; mult_p_r[1] <= mult_p[1]; mult_p_r[2] <= mult_p[2]; mult_p_r[3] <= mult_p[3]; state <= C_ACCUM; end end C_ACCUM: begin if (cur_bank == 1'b0) b0_c[calc_idx] <= ({2'b0, mult_p_r[0]} + {2'b0, mult_p_r[1]}) + ({2'b0, mult_p_r[2]} + {2'b0, mult_p_r[3]}); else b1_c[calc_idx] <= ({2'b0, mult_p_r[0]} + {2'b0, mult_p_r[1]}) + ({2'b0, mult_p_r[2]} + {2'b0, mult_p_r[3]}); $display("ACCUM calc_idx=%d expr=%h", calc_idx, ({2'b0, mult_p_r[0]} + {2'b0, mult_p_r[1]}) + ({2'b0, mult_p_r[2]} + {2'b0, mult_p_r[3]})); if (calc_idx == 4'd15) begin calc_done_pulse <= 1'b1; busy <= 1'b0; state <= C_IDLE; end else begin calc_idx <= calc_idx + 4'd1; state <= C_CALC; end end endcase end end endmodule