diff --git a/.trellis/tasks/06-25-pipelined-multiplier-for-matmul-ip/check.jsonl b/.trellis/tasks/06-25-pipelined-multiplier-for-matmul-ip/check.jsonl new file mode 100644 index 0000000..9dd3234 --- /dev/null +++ b/.trellis/tasks/06-25-pipelined-multiplier-for-matmul-ip/check.jsonl @@ -0,0 +1 @@ +{"_example": "Fill with {\"file\": \"\", \"reason\": \"\"}. Put spec/research files only — no code paths. Run `python3 .trellis/scripts/get_context.py --mode packages` to list available specs. Delete this line once real entries are added."} diff --git a/.trellis/tasks/06-25-pipelined-multiplier-for-matmul-ip/implement.jsonl b/.trellis/tasks/06-25-pipelined-multiplier-for-matmul-ip/implement.jsonl new file mode 100644 index 0000000..9dd3234 --- /dev/null +++ b/.trellis/tasks/06-25-pipelined-multiplier-for-matmul-ip/implement.jsonl @@ -0,0 +1 @@ +{"_example": "Fill with {\"file\": \"\", \"reason\": \"\"}. Put spec/research files only — no code paths. Run `python3 .trellis/scripts/get_context.py --mode packages` to list available specs. Delete this line once real entries are added."} diff --git a/.trellis/tasks/06-25-pipelined-multiplier-for-matmul-ip/prd.md b/.trellis/tasks/06-25-pipelined-multiplier-for-matmul-ip/prd.md new file mode 100644 index 0000000..79f2542 --- /dev/null +++ b/.trellis/tasks/06-25-pipelined-multiplier-for-matmul-ip/prd.md @@ -0,0 +1,56 @@ +# Pipelined Multiplier for Matmul IP + +## Goal + +Replace 4× shift_add_mult (32 cycle/op) with 4× 3-stage pipelined multipliers (1 result/cycle after fill), reducing compute from 420 cycle/group to ~15 cycle/group. + +## Requirements + +1. **Multiplier**: 32×32→64, 3-stage pipeline, LUT-only (no DSP) +2. **FSM**: simplified from 4-state (C_IDLE/C_CALC/C_WAIT_MULT/C_ACCUM) to 2-state (C_IDLE/C_RUN) +3. **Pipeline matching**: 3-entry shift register for calc_idx/cur_bank delay line +4. **Accumulation**: unchanged (4-way sum registered) +5. **Interface**: same ports as shift_add_mult (clk, rstn, start, a, b, p, done) for drop-in replacement + +## Acceptance Criteria + +- [ ] Simulation: CRC32 matches (10 groups) +- [ ] Lint clean +- [ ] Compile passes +- [ ] No DSP used +- [ ] WNS ≥ 0 (timing passes for sys_clk=50MHz) +- [ ] Expected time improvement: compute ~420 cycle → ~15 cycle + +## Technical Approach + +### Multiplier Design + +```verilog +(* use_dsp = "no" *) +module pipelined_mult ( + input clk, rstn, start, + input [31:0] a, b, + output [63:0] p, + output done +); + // 3-stage: reg(a,b) → compute product → output +endmodule +``` + +### FSM Reduction + +``` +Before: C_IDLE → C_CALC(1) → C_WAIT_MULT(32) → C_ACCUM(1) + ←──────── 12 rounds ────────→ = 420 cycles + +After: C_IDLE → C_RUN(12 + 3 pipe delay) → C_IDLE + Issue 4 mults/cycle for 12 cycles + Results arrive 3 cycles later → accumulate + Total: 12 + 3 + 1 = 16 cycles +``` + +## Out of Scope + +- More than 4 multipliers +- Changes to DMA, CRC32, interrupts +- FFT integration diff --git a/.trellis/tasks/06-25-pipelined-multiplier-for-matmul-ip/task.json b/.trellis/tasks/06-25-pipelined-multiplier-for-matmul-ip/task.json new file mode 100644 index 0000000..5a9a9d1 --- /dev/null +++ b/.trellis/tasks/06-25-pipelined-multiplier-for-matmul-ip/task.json @@ -0,0 +1,26 @@ +{ + "id": "pipelined-multiplier-for-matmul-ip", + "name": "pipelined-multiplier-for-matmul-ip", + "title": "Pipelined multiplier for matmul IP", + "description": "", + "status": "in_progress", + "dev_type": null, + "scope": null, + "package": null, + "priority": "P2", + "creator": "fallensigh", + "assignee": "fallensigh", + "createdAt": "2026-06-25", + "completedAt": null, + "branch": null, + "base_branch": "no-fft", + "worktree_path": null, + "commit": null, + "pr_url": null, + "subtasks": [], + "children": [], + "parent": null, + "relatedFiles": [], + "notes": "", + "meta": {} +} \ No newline at end of file diff --git a/rtl/ip/matmul/matmul_axi_slave.v b/rtl/ip/matmul/matmul_axi_slave.v index 3897fdf..ef010ab 100644 --- a/rtl/ip/matmul/matmul_axi_slave.v +++ b/rtl/ip/matmul/matmul_axi_slave.v @@ -1,51 +1,27 @@ // ═══════════════════════════════════════════════════════ -// 基础时序乘法器模块 (无 DSP,移位累加,32个周期) +// 3-stage pipelined multiplier (LUT-based, no DSP) +// latency=3, throughput=1 result/cycle // ═══════════════════════════════════════════════════════ -module shift_add_mult ( +(* use_dsp = "no" *) +module pipelined_mult_32x32 ( input clk, input rstn, - input start, input [31:0] a, input [31:0] b, - output reg [63:0] p, - output reg done + output reg [63:0] p ); - reg [5:0] cnt; - reg [63:0] a_reg; - reg [31:0] b_reg; - reg busy; - + reg [31:0] a_r, b_r; + reg [63:0] prod; + 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; + a_r <= 32'b0; b_r <= 32'b0; + prod <= 64'b0; p <= 64'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 - 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 + a_r <= a; // Stage 1: register inputs + b_r <= b; + prod <= a_r * b_r; // Stage 2: LUT-based multiply + p <= prod; // Stage 3: register output end end endmodule @@ -323,9 +299,7 @@ module matmul_axi_slave ( // 前向声明引擎的状态 localparam C_IDLE = 2'd0; - localparam C_CALC = 2'd1; - localparam C_WAIT_MULT = 2'd2; - localparam C_ACCUM = 2'd3; + localparam C_RUN = 2'd1; reg [1:0] state; reg cur_bank; reg calc_done_pulse; @@ -344,8 +318,8 @@ module matmul_axi_slave ( 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; + if (req_0 && state == C_RUN && cur_bank == 1'b0) req_0 <= 1'b0; + if (req_1 && state == C_RUN && cur_bank == 1'b1) req_1 <= 1'b0; if (calc_done_pulse) begin done <= 1'b1; @@ -524,102 +498,104 @@ module matmul_axi_slave ( // ========================================== // 计算引擎 (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]; 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]) + pipelined_mult_32x32 u_mult ( + .clk (clk), + .rstn (resetn), + .a (m_in_a[i]), + .b (m_in_b[i]), + .p (mult_p[i]) ); end endgenerate + // Pipeline delay shift registers for accumulation (4 stages = 3 multiplier pipe + 1) + reg [3:0] acc_idx_d1, acc_idx_d2, acc_idx_d3, acc_idx_d4; + reg acc_bank_d1, acc_bank_d2, acc_bank_d3, acc_bank_d4; + reg [4:0] pipe_cnt; + 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; + pipe_cnt <= 5'd0; + acc_idx_d1 <= 4'd0; + acc_idx_d2 <= 4'd0; + acc_idx_d3 <= 4'd0; + acc_idx_d4 <= 4'd0; + acc_bank_d1 <= 1'b0; + acc_bank_d2 <= 1'b0; + acc_bank_d3 <= 1'b0; + acc_bank_d4 <= 1'b0; end else begin calc_done_pulse <= 1'b0; - + + // Pipeline shift: issue_idx → d1 → d2 → d3 → d4 (4 cycles total) + acc_idx_d4 <= acc_idx_d3; + acc_idx_d3 <= acc_idx_d2; + acc_idx_d2 <= acc_idx_d1; + acc_idx_d1 <= pipe_cnt[3:0]; + acc_bank_d4 <= acc_bank_d3; + acc_bank_d3 <= acc_bank_d2; + acc_bank_d2 <= acc_bank_d1; + acc_bank_d1 <= cur_bank; + case (state) C_IDLE: begin - calc_idx <= 4'd0; + pipe_cnt <= 5'd0; if (req_0) begin cur_bank <= 1'b0; busy <= 1'b1; - state <= C_CALC; + state <= C_RUN; end else if (req_1) begin cur_bank <= 1'b1; busy <= 1'b1; - state <= C_CALC; + state <= C_RUN; 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}]; + + C_RUN: begin + // Issue 4 new operands every cycle (pipe_cnt 0-11, 12 rounds) + if (pipe_cnt < 5'd12) begin + if (cur_bank == 1'b0) begin + m_in_a[0] <= b0_a[{pipe_cnt[3:2], 2'd0}]; m_in_b[0] <= b0_b[{2'd0, pipe_cnt[1:0]}]; + m_in_a[1] <= b0_a[{pipe_cnt[3:2], 2'd1}]; m_in_b[1] <= b0_b[{2'd1, pipe_cnt[1:0]}]; + m_in_a[2] <= b0_a[{pipe_cnt[3:2], 2'd2}]; m_in_b[2] <= b0_b[{2'd2, pipe_cnt[1:0]}]; + m_in_a[3] <= b0_a[{pipe_cnt[3:2], 2'd3}]; m_in_b[3] <= b0_b[{2'd3, pipe_cnt[1:0]}]; + end else begin + m_in_a[0] <= b1_a[{pipe_cnt[3:2], 2'd0}]; m_in_b[0] <= b1_b[{2'd0, pipe_cnt[1:0]}]; + m_in_a[1] <= b1_a[{pipe_cnt[3:2], 2'd1}]; m_in_b[1] <= b1_b[{2'd1, pipe_cnt[1:0]}]; + m_in_a[2] <= b1_a[{pipe_cnt[3:2], 2'd2}]; m_in_b[2] <= b1_b[{2'd2, pipe_cnt[1:0]}]; + m_in_a[3] <= b1_a[{pipe_cnt[3:2], 2'd3}]; m_in_b[3] <= b1_b[{2'd3, pipe_cnt[1:0]}]; + end 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; + + // Accumulate results from 4 cycles ago (pipe_cnt 4-15) + if (pipe_cnt >= 5'd4 && pipe_cnt < 5'd16) begin + if (acc_bank_d4 == 1'b0) + b0_c[acc_idx_d4] <= ({2'b0, mult_p[0]} + {2'b0, mult_p[1]}) + + ({2'b0, mult_p[2]} + {2'b0, mult_p[3]}); + else + b1_c[acc_idx_d4] <= ({2'b0, mult_p[0]} + {2'b0, mult_p[1]}) + + ({2'b0, mult_p[2]} + {2'b0, mult_p[3]}); 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]}); - - if (calc_idx == 4'd15) begin + + if (pipe_cnt == 5'd16) begin calc_done_pulse <= 1'b1; busy <= 1'b0; state <= C_IDLE; end else begin - calc_idx <= calc_idx + 4'd1; - state <= C_CALC; + pipe_cnt <= pipe_cnt + 5'd1; end end endcase