module matmul_axi_slave ( input clk, input resetn, 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, 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, output [4:0] s_axi_bid, output [1:0] s_axi_bresp, output reg s_axi_bvalid, input s_axi_bready, 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, 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 ); localparam ADDR_CTRL = 12'h000; localparam ADDR_STATUS = 12'h004; localparam ADDR_SRC_BASE = 12'h008; localparam ADDR_DST_BASE = 12'h00c; localparam ADDR_GROUP_NUM = 12'h010; localparam ADDR_A_BASE = 12'h020; localparam ADDR_B_BASE = 12'h060; localparam ADDR_C_BASE = 12'h0a0; reg [31:0] src_base; reg [31:0] dst_base; reg [31:0] group_num; reg [31:0] a_data [0:15]; reg [31:0] b_data [0:15]; reg [65:0] c_data [0:15]; // ══ Write burst state machine (same pattern as fft_controller) ══ 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; wire [11:0] w_offset = w_addr[11:0]; wire w_active = (w_state == W_DATA) && s_axi_wvalid && s_axi_wready; wire w_start = (w_offset == ADDR_CTRL) && s_axi_wdata[0] && !busy; assign s_axi_bid = w_id; assign s_axi_bresp = 2'b00; // ══ Read burst state machine ══ localparam R_IDLE = 2'd0; localparam R_DATA = 2'd1; reg [1:0] r_state; reg [31:0] r_addr; reg [4:0] r_id; reg [7:0] r_cnt; wire [11:0] r_offset = r_addr[11:0]; assign s_axi_rresp = 2'b00; // ══ Compute engine ══ reg busy; reg done; reg error; reg [3:0] calc_index; 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 function [63:0] mul_u32_shift_add; input [31:0] lhs; input [31:0] rhs; integer bit_index; begin mul_u32_shift_add = 64'b0; for (bit_index = 0; bit_index < 32; bit_index = bit_index + 1) begin if (rhs[bit_index]) begin mul_u32_shift_add = mul_u32_shift_add + ({32'b0, lhs} << bit_index); end end end endfunction function [65:0] calc_c_element; input [3:0] index; reg [1:0] row; reg [1:0] col; begin row = index[3:2]; col = index[1:0]; calc_c_element = {2'b0, mul_u32_shift_add(a_data[{row, 2'd0}], b_data[{2'd0, col}])} + {2'b0, mul_u32_shift_add(a_data[{row, 2'd1}], b_data[{2'd1, col}])} + {2'b0, mul_u32_shift_add(a_data[{row, 2'd2}], b_data[{2'd2, col}])} + {2'b0, mul_u32_shift_add(a_data[{row, 2'd3}], b_data[{2'd3, col}])}; end endfunction function [31:0] c_read_word; input [5:0] word_index; begin case (word_index) 6'd0: c_read_word = c_data[0][31:0]; 6'd1: c_read_word = c_data[0][63:32]; 6'd2: c_read_word = {30'b0, c_data[0][65:64]}; 6'd3: c_read_word = c_data[1][31:0]; 6'd4: c_read_word = c_data[1][63:32]; 6'd5: c_read_word = {30'b0, c_data[1][65:64]}; 6'd6: c_read_word = c_data[2][31:0]; 6'd7: c_read_word = c_data[2][63:32]; 6'd8: c_read_word = {30'b0, c_data[2][65:64]}; 6'd9: c_read_word = c_data[3][31:0]; 6'd10: c_read_word = c_data[3][63:32]; 6'd11: c_read_word = {30'b0, c_data[3][65:64]}; 6'd12: c_read_word = c_data[4][31:0]; 6'd13: c_read_word = c_data[4][63:32]; 6'd14: c_read_word = {30'b0, c_data[4][65:64]}; 6'd15: c_read_word = c_data[5][31:0]; 6'd16: c_read_word = c_data[5][63:32]; 6'd17: c_read_word = {30'b0, c_data[5][65:64]}; 6'd18: c_read_word = c_data[6][31:0]; 6'd19: c_read_word = c_data[6][63:32]; 6'd20: c_read_word = {30'b0, c_data[6][65:64]}; 6'd21: c_read_word = c_data[7][31:0]; 6'd22: c_read_word = c_data[7][63:32]; 6'd23: c_read_word = {30'b0, c_data[7][65:64]}; 6'd24: c_read_word = c_data[8][31:0]; 6'd25: c_read_word = c_data[8][63:32]; 6'd26: c_read_word = {30'b0, c_data[8][65:64]}; 6'd27: c_read_word = c_data[9][31:0]; 6'd28: c_read_word = c_data[9][63:32]; 6'd29: c_read_word = {30'b0, c_data[9][65:64]}; 6'd30: c_read_word = c_data[10][31:0]; 6'd31: c_read_word = c_data[10][63:32]; 6'd32: c_read_word = {30'b0, c_data[10][65:64]}; 6'd33: c_read_word = c_data[11][31:0]; 6'd34: c_read_word = c_data[11][63:32]; 6'd35: c_read_word = {30'b0, c_data[11][65:64]}; 6'd36: c_read_word = c_data[12][31:0]; 6'd37: c_read_word = c_data[12][63:32]; 6'd38: c_read_word = {30'b0, c_data[12][65:64]}; 6'd39: c_read_word = c_data[13][31:0]; 6'd40: c_read_word = c_data[13][63:32]; 6'd41: c_read_word = {30'b0, c_data[13][65:64]}; 6'd42: c_read_word = c_data[14][31:0]; 6'd43: c_read_word = c_data[14][63:32]; 6'd44: c_read_word = {30'b0, c_data[14][65:64]}; 6'd45: c_read_word = c_data[15][31:0]; 6'd46: c_read_word = c_data[15][63:32]; 6'd47: c_read_word = {30'b0, c_data[15][65:64]}; default: c_read_word = 32'b0; endcase end endfunction integer i; always @(posedge clk) begin if (!resetn) begin src_base <= 32'b0; dst_base <= 32'b0; group_num <= 32'd1; w_state <= W_IDLE; s_axi_awready <= 1'b1; s_axi_wready <= 1'b0; s_axi_bvalid <= 1'b0; w_addr <= 32'b0; w_id <= 5'b0; r_state <= R_IDLE; s_axi_arready <= 1'b1; s_axi_rvalid <= 1'b0; s_axi_rlast <= 1'b0; r_addr <= 32'b0; r_id <= 5'b0; r_cnt <= 8'b0; s_axi_rid <= 5'b0; s_axi_rdata <= 32'b0; busy <= 1'b0; done <= 1'b0; error <= 1'b0; calc_index <= 4'b0; for (i = 0; i < 16; i = i + 1) begin a_data[i] <= 32'b0; b_data[i] <= 32'b0; c_data[i] <= 66'b0; end end else begin // ═══════════════════════════════════════════════════════ // Write burst FSM (FFT pattern) // ═══════════════════════════════════════════════════════ 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_state <= W_DATA; end end W_DATA: begin if (s_axi_wvalid && s_axi_wready) begin // Register write if (w_offset == ADDR_SRC_BASE) begin src_base <= apply_wstrb(src_base, s_axi_wdata, s_axi_wstrb); end else if (w_offset == ADDR_DST_BASE) begin dst_base <= apply_wstrb(dst_base, s_axi_wdata, s_axi_wstrb); end else if (w_offset == ADDR_GROUP_NUM) begin group_num <= apply_wstrb(group_num, s_axi_wdata, s_axi_wstrb); end else if ((w_offset >= ADDR_A_BASE) && (w_offset < ADDR_B_BASE)) begin a_data[(w_offset - ADDR_A_BASE) >> 2] <= apply_wstrb(a_data[(w_offset - ADDR_A_BASE) >> 2], s_axi_wdata, s_axi_wstrb); end else if ((w_offset >= ADDR_B_BASE) && (w_offset < ADDR_C_BASE)) begin b_data[(w_offset - ADDR_B_BASE) >> 2] <= apply_wstrb(b_data[(w_offset - ADDR_B_BASE) >> 2], s_axi_wdata, s_axi_wstrb); end else if (w_offset == ADDR_STATUS) begin if (s_axi_wdata[1]) done <= 1'b0; if (s_axi_wdata[2]) error <= 1'b0; end else if ((w_offset != ADDR_CTRL) && (w_offset < ADDR_C_BASE)) begin error <= 1'b1; end // Auto-increment address for next beat 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 // ═══════════════════════════════════════════════════════ // Read burst FSM (FFT pattern, no BRAM latency) // ═══════════════════════════════════════════════════════ 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_id <= s_axi_arid; r_cnt <= 8'd0; s_axi_rvalid <= 1'b1; s_axi_rlast <= (s_axi_arlen == 8'd0); // Output data for current address if (r_offset == ADDR_CTRL) s_axi_rdata <= 32'b0; else if (r_offset == ADDR_STATUS) s_axi_rdata <= {29'b0, error, done, busy}; else if (r_offset == ADDR_SRC_BASE) s_axi_rdata <= src_base; else if (r_offset == ADDR_DST_BASE) s_axi_rdata <= dst_base; else if (r_offset == ADDR_GROUP_NUM) s_axi_rdata <= group_num; else if ((r_offset >= ADDR_A_BASE) && (r_offset < ADDR_B_BASE)) s_axi_rdata <= a_data[(r_offset - ADDR_A_BASE) >> 2]; else if ((r_offset >= ADDR_B_BASE) && (r_offset < ADDR_C_BASE)) s_axi_rdata <= b_data[(r_offset - ADDR_B_BASE) >> 2]; else if ((r_offset >= ADDR_C_BASE) && (r_offset < (ADDR_C_BASE + 12'h0c0))) s_axi_rdata <= c_read_word((r_offset - ADDR_C_BASE) >> 2); else s_axi_rdata <= 32'b0; 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 == s_axi_arlen) 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; r_addr <= r_addr + 32'd4; s_axi_rlast <= (r_cnt + 8'd1 == s_axi_arlen); // Output next beat data if ((r_offset + 12'd4) == ADDR_CTRL) s_axi_rdata <= 32'b0; else if ((r_offset + 12'd4) == ADDR_STATUS) s_axi_rdata <= {29'b0, error, done, busy}; else if ((r_offset + 12'd4) == ADDR_SRC_BASE) s_axi_rdata <= src_base; else if ((r_offset + 12'd4) == ADDR_DST_BASE) s_axi_rdata <= dst_base; else if ((r_offset + 12'd4) == ADDR_GROUP_NUM) s_axi_rdata <= group_num; else if (((r_offset + 12'd4) >= ADDR_A_BASE) && ((r_offset + 12'd4) < ADDR_B_BASE)) s_axi_rdata <= a_data[((r_offset + 12'd4) - ADDR_A_BASE) >> 2]; else if (((r_offset + 12'd4) >= ADDR_B_BASE) && ((r_offset + 12'd4) < ADDR_C_BASE)) s_axi_rdata <= b_data[((r_offset + 12'd4) - ADDR_B_BASE) >> 2]; else if (((r_offset + 12'd4) >= ADDR_C_BASE) && ((r_offset + 12'd4) < (ADDR_C_BASE + 12'h0c0))) s_axi_rdata <= c_read_word(((r_offset + 12'd4) - ADDR_C_BASE) >> 2); else s_axi_rdata <= 32'b0; end end end default: r_state <= R_IDLE; endcase // ═══════════════════════════════════════════════════════ // Compute engine (unchanged) // ═══════════════════════════════════════════════════════ if (w_start) begin busy <= 1'b1; done <= 1'b0; error <= 1'b0; calc_index <= 4'd0; end else if (busy) begin c_data[calc_index] <= calc_c_element(calc_index); if (calc_index == 4'd15) begin busy <= 1'b0; done <= 1'b1; calc_index <= 4'd0; end else begin calc_index <= calc_index + 4'd1; end end end end endmodule