Pipelined multiplier: 3-stage LUT-based replaces shift_add, FSM simplified to 2-state

This commit is contained in:
2026-06-25 22:09:20 +08:00
parent b3c0db19b9
commit db9997b473
5 changed files with 162 additions and 102 deletions

View File

@@ -0,0 +1 @@
{"_example": "Fill with {\"file\": \"<path>\", \"reason\": \"<why>\"}. 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."}

View File

@@ -0,0 +1 @@
{"_example": "Fill with {\"file\": \"<path>\", \"reason\": \"<why>\"}. 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."}

View File

@@ -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

View File

@@ -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": {}
}

View File

@@ -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 clk,
input rstn, input rstn,
input start,
input [31:0] a, input [31:0] a,
input [31:0] b, input [31:0] b,
output reg [63:0] p, output reg [63:0] p
output reg done
); );
reg [5:0] cnt; reg [31:0] a_r, b_r;
reg [63:0] a_reg; reg [63:0] prod;
reg [31:0] b_reg;
reg busy;
always @(posedge clk) begin always @(posedge clk) begin
if (!rstn) begin if (!rstn) begin
busy <= 1'b0; a_r <= 32'b0; b_r <= 32'b0;
done <= 1'b0; prod <= 64'b0; p <= 64'b0;
p <= 64'b0;
cnt <= 6'd0;
a_reg<= 64'b0;
b_reg<= 32'b0;
end else begin end else begin
if (start && !busy) begin a_r <= a; // Stage 1: register inputs
a_reg <= {32'b0, a}; b_r <= b;
b_reg <= b; prod <= a_r * b_r; // Stage 2: LUT-based multiply
p <= 64'b0; p <= prod; // Stage 3: register output
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
end end
end end
endmodule endmodule
@@ -323,9 +299,7 @@ module matmul_axi_slave (
// 前向声明引擎的状态 // 前向声明引擎的状态
localparam C_IDLE = 2'd0; localparam C_IDLE = 2'd0;
localparam C_CALC = 2'd1; localparam C_RUN = 2'd1;
localparam C_WAIT_MULT = 2'd2;
localparam C_ACCUM = 2'd3;
reg [1:0] state; reg [1:0] state;
reg cur_bank; reg cur_bank;
reg calc_done_pulse; reg calc_done_pulse;
@@ -344,8 +318,8 @@ module matmul_axi_slave (
done <= 1'b0; done <= 1'b0;
error <= 1'b0; error <= 1'b0;
end else begin end else begin
if (req_0 && state == C_CALC && cur_bank == 1'b0) req_0 <= 1'b0; if (req_0 && state == C_RUN && cur_bank == 1'b0) req_0 <= 1'b0;
if (req_1 && state == C_CALC && cur_bank == 1'b1) req_1 <= 1'b0; if (req_1 && state == C_RUN && cur_bank == 1'b1) req_1 <= 1'b0;
if (calc_done_pulse) begin if (calc_done_pulse) begin
done <= 1'b1; done <= 1'b1;
@@ -524,102 +498,104 @@ module matmul_axi_slave (
// ========================================== // ==========================================
// 计算引擎 (Compute FSM) // 计算引擎 (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]; 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_a [0:3];
reg [31:0] m_in_b [0:3]; reg [31:0] m_in_b [0:3];
genvar i; genvar i;
generate generate
for (i = 0; i < 4; i = i + 1) begin : mult_inst for (i = 0; i < 4; i = i + 1) begin : mult_inst
shift_add_mult u_mult ( pipelined_mult_32x32 u_mult (
.clk (clk), .clk (clk),
.rstn (resetn), .rstn (resetn),
.start (mult_start), .a (m_in_a[i]),
.a (m_in_a[i]), .b (m_in_b[i]),
.b (m_in_b[i]), .p (mult_p[i])
.p (mult_p[i]),
.done (mult_done[i])
); );
end end
endgenerate 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 always @(posedge clk) begin
if (!resetn) begin if (!resetn) begin
state <= C_IDLE; state <= C_IDLE;
busy <= 1'b0; busy <= 1'b0;
cur_bank <= 1'b0; cur_bank <= 1'b0;
calc_idx <= 4'd0;
mult_start <= 1'b0;
calc_done_pulse <= 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 end else begin
calc_done_pulse <= 1'b0; 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) case (state)
C_IDLE: begin C_IDLE: begin
calc_idx <= 4'd0; pipe_cnt <= 5'd0;
if (req_0) begin if (req_0) begin
cur_bank <= 1'b0; cur_bank <= 1'b0;
busy <= 1'b1; busy <= 1'b1;
state <= C_CALC; state <= C_RUN;
end else if (req_1) begin end else if (req_1) begin
cur_bank <= 1'b1; cur_bank <= 1'b1;
busy <= 1'b1; busy <= 1'b1;
state <= C_CALC; state <= C_RUN;
end else begin end else begin
busy <= 1'b0; busy <= 1'b0;
end end
end end
C_CALC: begin C_RUN: begin
if (cur_bank == 1'b0) begin // Issue 4 new operands every cycle (pipe_cnt 0-11, 12 rounds)
m_in_a[0] <= b0_a[{row, 2'd0}]; m_in_b[0] <= b0_b[{2'd0, col}]; if (pipe_cnt < 5'd12) begin
m_in_a[1] <= b0_a[{row, 2'd1}]; m_in_b[1] <= b0_b[{2'd1, col}]; if (cur_bank == 1'b0) begin
m_in_a[2] <= b0_a[{row, 2'd2}]; m_in_b[2] <= b0_b[{2'd2, col}]; 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[3] <= b0_a[{row, 2'd3}]; m_in_b[3] <= b0_b[{2'd3, col}]; m_in_a[1] <= b0_a[{pipe_cnt[3:2], 2'd1}]; m_in_b[1] <= b0_b[{2'd1, pipe_cnt[1:0]}];
end else begin 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[0] <= b1_a[{row, 2'd0}]; m_in_b[0] <= b1_b[{2'd0, col}]; m_in_a[3] <= b0_a[{pipe_cnt[3:2], 2'd3}]; m_in_b[3] <= b0_b[{2'd3, pipe_cnt[1:0]}];
m_in_a[1] <= b1_a[{row, 2'd1}]; m_in_b[1] <= b1_b[{2'd1, col}]; end else begin
m_in_a[2] <= b1_a[{row, 2'd2}]; m_in_b[2] <= b1_b[{2'd2, col}]; 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[3] <= b1_a[{row, 2'd3}]; m_in_b[3] <= b1_b[{2'd3, col}]; 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 end
mult_start <= 1'b1;
state <= C_WAIT_MULT;
end
C_WAIT_MULT: begin // Accumulate results from 4 cycles ago (pipe_cnt 4-15)
mult_start <= 1'b0; if (pipe_cnt >= 5'd4 && pipe_cnt < 5'd16) begin
if (mult_done[0] && mult_done[1] && mult_done[2] && mult_done[3]) begin if (acc_bank_d4 == 1'b0)
mult_p_r[0] <= mult_p[0]; b0_c[acc_idx_d4] <= ({2'b0, mult_p[0]} + {2'b0, mult_p[1]}) +
mult_p_r[1] <= mult_p[1]; ({2'b0, mult_p[2]} + {2'b0, mult_p[3]});
mult_p_r[2] <= mult_p[2]; else
mult_p_r[3] <= mult_p[3]; b1_c[acc_idx_d4] <= ({2'b0, mult_p[0]} + {2'b0, mult_p[1]}) +
state <= C_ACCUM; ({2'b0, mult_p[2]} + {2'b0, mult_p[3]});
end end
end
C_ACCUM: begin if (pipe_cnt == 5'd16) 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
calc_done_pulse <= 1'b1; calc_done_pulse <= 1'b1;
busy <= 1'b0; busy <= 1'b0;
state <= C_IDLE; state <= C_IDLE;
end else begin end else begin
calc_idx <= calc_idx + 4'd1; pipe_cnt <= pipe_cnt + 5'd1;
state <= C_CALC;
end end
end end
endcase endcase