This repository has been archived on 2026-08-21. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
ciciec2026/rtl/ip/matmul/matmul_axi_slave.v

466 lines
18 KiB
Verilog
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// ═══════════════════════════════════════════════════════
// 基础时序乘法器模块 (无 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