Files
mlkem-sync/sync_rtl/poly_mul/basecase_mul_pipe.v

145 lines
4.3 KiB
Verilog

// basecase_mul_pipe.v - pipelined NTT-domain degree-1 multiplication
//
// Fixed-latency replacement for the combinational basecase_mul hot path.
// The shared ntt/barrett_mul remains combinational for NTT users; this module
// keeps the extra registers local to poly_mul_sync.
(* use_dsp = "no" *)
module basecase_mul_pipe (
input clk,
input rst_n,
input valid_i,
input [11:0] a0, a1,
input [11:0] b0, b1,
input [11:0] zeta,
output [11:0] c0,
output [11:0] c1,
output valid_o
);
localparam [11:0] Q12 = 12'd3329;
localparam [12:0] K13 = 13'd5039;
localparam [24:0] Q25 = 25'd3329;
function [12:0] barrett_q;
input [23:0] p;
reg [36:0] prod;
begin
prod = {13'd0, p} * K13;
barrett_q = prod[36:24];
end
endfunction
function [11:0] barrett_reduce;
input [23:0] p;
input [12:0] qe;
reg [24:0] q_approx;
reg [24:0] r0;
reg [24:0] r1;
reg [24:0] r2;
begin
q_approx = qe * Q12;
r0 = {1'b0, p} - q_approx;
r1 = (r0 >= Q25) ? (r0 - Q25) : r0;
r2 = (r1 >= Q25) ? (r1 - Q25) : r1;
barrett_reduce = r2[11:0];
end
endfunction
function [11:0] mod_add;
input [11:0] x;
input [11:0] y;
reg [12:0] sum;
begin
sum = {1'b0, x} + {1'b0, y};
mod_add = (sum >= {1'b0, Q12}) ? (sum[11:0] - Q12) : sum[11:0];
end
endfunction
// Stage 1: first-level scalar products.
reg [23:0] p10, p11, p12, p13;
reg [11:0] zeta_s1;
// Stage 2: Barrett quotient estimates for first-level products.
reg [23:0] p20, p21, p22, p23;
reg [12:0] q20, q21, q22, q23;
reg [11:0] zeta_s2;
// Stage 3: first-level reduced products.
reg [11:0] t1_s3, t2_s3, t3_s3, t4_s3;
reg [11:0] zeta_s3;
// Stage 4: zeta product for c0's second Barrett multiply.
reg [11:0] t1_s4, t3_s4, t4_s4;
reg [23:0] pz_s4;
// Stage 5: Barrett quotient estimate for zeta product.
reg [11:0] t1_s5, t3_s5, t4_s5;
reg [23:0] pz_s5;
reg [12:0] qz_s5;
// Stage 6: reduced zeta product.
reg [11:0] t1_s6, t3_s6, t4_s6, tz_s6;
// Stage 7: final modular additions.
reg [11:0] c0_r, c1_r;
reg [6:0] valid_sr;
assign c0 = c0_r;
assign c1 = c1_r;
assign valid_o = valid_sr[6];
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
p10 <= 24'd0; p11 <= 24'd0; p12 <= 24'd0; p13 <= 24'd0; zeta_s1 <= 12'd0;
p20 <= 24'd0; p21 <= 24'd0; p22 <= 24'd0; p23 <= 24'd0;
q20 <= 13'd0; q21 <= 13'd0; q22 <= 13'd0; q23 <= 13'd0; zeta_s2 <= 12'd0;
t1_s3 <= 12'd0; t2_s3 <= 12'd0; t3_s3 <= 12'd0; t4_s3 <= 12'd0; zeta_s3 <= 12'd0;
t1_s4 <= 12'd0; t3_s4 <= 12'd0; t4_s4 <= 12'd0; pz_s4 <= 24'd0;
t1_s5 <= 12'd0; t3_s5 <= 12'd0; t4_s5 <= 12'd0; pz_s5 <= 24'd0; qz_s5 <= 13'd0;
t1_s6 <= 12'd0; t3_s6 <= 12'd0; t4_s6 <= 12'd0; tz_s6 <= 12'd0;
c0_r <= 12'd0; c1_r <= 12'd0; valid_sr <= 7'd0;
end else begin
valid_sr <= {valid_sr[5:0], valid_i};
p10 <= {12'd0, a0} * b0;
p11 <= {12'd0, a1} * b1;
p12 <= {12'd0, a1} * b0;
p13 <= {12'd0, a0} * b1;
zeta_s1 <= zeta;
p20 <= p10; p21 <= p11; p22 <= p12; p23 <= p13;
q20 <= barrett_q(p10);
q21 <= barrett_q(p11);
q22 <= barrett_q(p12);
q23 <= barrett_q(p13);
zeta_s2 <= zeta_s1;
t1_s3 <= barrett_reduce(p20, q20);
t2_s3 <= barrett_reduce(p21, q21);
t3_s3 <= barrett_reduce(p22, q22);
t4_s3 <= barrett_reduce(p23, q23);
zeta_s3 <= zeta_s2;
t1_s4 <= t1_s3;
t3_s4 <= t3_s3;
t4_s4 <= t4_s3;
pz_s4 <= {12'd0, t2_s3} * zeta_s3;
t1_s5 <= t1_s4;
t3_s5 <= t3_s4;
t4_s5 <= t4_s4;
pz_s5 <= pz_s4;
qz_s5 <= barrett_q(pz_s4);
t1_s6 <= t1_s5;
t3_s6 <= t3_s5;
t4_s6 <= t4_s5;
tz_s6 <= barrett_reduce(pz_s5, qz_s5);
c0_r <= mod_add(t1_s6, tz_s6);
c1_r <= mod_add(t3_s6, t4_s6);
end
end
endmodule