|
|
|
|
@@ -99,9 +99,59 @@ module mlkem_top #(
|
|
|
|
|
localparam PA_AW = 12; // bank_a addr width (4096)
|
|
|
|
|
localparam PSE_AW = 11; // bank_se addr width (2048)
|
|
|
|
|
localparam PT_AW = 10; // bank_t addr width (1024)
|
|
|
|
|
reg [11:0] bank_a [0:(1<<PA_AW)-1];
|
|
|
|
|
reg [11:0] bank_se [0:(1<<PSE_AW)-1];
|
|
|
|
|
reg [11:0] bank_t [0:(1<<PT_AW)-1];
|
|
|
|
|
|
|
|
|
|
// bank_a: A_hat. Reader = ST_M load (pm_a) + dbg. Writer = ST_A.
|
|
|
|
|
// Read port muxed by phase; sd_bram's internal rd_addr_r register is the
|
|
|
|
|
// 1-cycle read pipeline (replaces the explicit pm_a_rd reg). Write port is
|
|
|
|
|
// driven combinationally (sd_bram registers the write at posedge, matching
|
|
|
|
|
// the old reg-array nonblocking write timing).
|
|
|
|
|
wire [PA_AW-1:0] ba_rd_addr;
|
|
|
|
|
wire [11:0] ba_rd_data;
|
|
|
|
|
wire ba_we;
|
|
|
|
|
wire [PA_AW-1:0] ba_wa;
|
|
|
|
|
wire [11:0] ba_wd;
|
|
|
|
|
sd_bram #(.W(12), .D(1<<PA_AW), .A(PA_AW)) u_bank_a (
|
|
|
|
|
.clk(clk), .rd_addr(ba_rd_addr), .rd_data(ba_rd_data),
|
|
|
|
|
.wr_en(ba_we), .wr_addr(ba_wa), .wr_data(ba_wd)
|
|
|
|
|
);
|
|
|
|
|
// ST_A write: commit snt_coeff to A_hat[a_slot] the cycle it is accepted.
|
|
|
|
|
assign ba_we = (st == ST_A) && a_busy && snt_vo && snt_ack;
|
|
|
|
|
assign ba_wa = (a_slot*256 + a_widx) & ((1<<PA_AW)-1);
|
|
|
|
|
assign ba_wd = snt_coeff;
|
|
|
|
|
|
|
|
|
|
// bank_se: s_hat || e_hat. Readers = ST_N load, ST_M load (pm_b, s_hat[j]),
|
|
|
|
|
// ST_M acc (e_hat, j==0), ST_E dk-half, dbg. Writers = ST_C (CBD), ST_N
|
|
|
|
|
// writeback (NTT in place). All readers already registered; read port muxed
|
|
|
|
|
// by phase (within ST_M, load vs accumulate never overlap). Write port
|
|
|
|
|
// combinational (ST_C and ST_N are disjoint states).
|
|
|
|
|
wire [PSE_AW-1:0] bse_rd_addr;
|
|
|
|
|
wire [11:0] bse_rd_data;
|
|
|
|
|
wire bse_we;
|
|
|
|
|
wire [PSE_AW-1:0] bse_wa;
|
|
|
|
|
wire [11:0] bse_wd;
|
|
|
|
|
sd_bram #(.W(12), .D(1<<PSE_AW), .A(PSE_AW)) u_bank_se (
|
|
|
|
|
.clk(clk), .rd_addr(bse_rd_addr), .rd_data(bse_rd_data),
|
|
|
|
|
.wr_en(bse_we), .wr_addr(bse_wa), .wr_data(bse_wd)
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
// bank_t: t_hat. Readers = ST_M acc (j>0 running t_hat) + ST_E ek-half + dbg.
|
|
|
|
|
// Writer = ST_M acc. Read port muxed by phase; sd_bram's rd_addr_r is the
|
|
|
|
|
// 1-cycle read register. RMW is safe: the acc read addr leads the write
|
|
|
|
|
// addr by 1 (m_oidx+1 vs m_oidx), so read/write never alias in a cycle and
|
|
|
|
|
// sd_bram write-first == reg-array read-old. Write port combinational.
|
|
|
|
|
wire [PT_AW-1:0] bt_rd_addr;
|
|
|
|
|
wire [11:0] bt_rd_data;
|
|
|
|
|
wire bt_we;
|
|
|
|
|
wire [PT_AW-1:0] bt_wa;
|
|
|
|
|
wire [11:0] bt_wd;
|
|
|
|
|
sd_bram #(.W(12), .D(1<<PT_AW), .A(PT_AW)) u_bank_t (
|
|
|
|
|
.clk(clk), .rd_addr(bt_rd_addr), .rd_data(bt_rd_data),
|
|
|
|
|
.wr_en(bt_we), .wr_addr(bt_wa), .wr_data(bt_wd)
|
|
|
|
|
);
|
|
|
|
|
// ST_M accumulate write: t_hat[m_i][m_oidx] <= (acc + product) mod Q when pm_vo.
|
|
|
|
|
assign bt_we = (st == ST_M) && pm_vo;
|
|
|
|
|
assign bt_wa = (m_i*256 + m_oidx) & ((1<<PT_AW)-1);
|
|
|
|
|
assign bt_wd = m_accq;
|
|
|
|
|
|
|
|
|
|
// Debug readback (registered for timing)
|
|
|
|
|
reg [11:0] dbg_coeff_r;
|
|
|
|
|
@@ -111,10 +161,31 @@ module mlkem_top #(
|
|
|
|
|
wire [13:0] dbg_a_addr = dbg_slot_i*256 + dbg_idx_i;
|
|
|
|
|
wire [13:0] dbg_se_addr = (dbg_slot_i - slot_s_rt)*256 + dbg_idx_i;
|
|
|
|
|
wire [13:0] dbg_t_addr = (dbg_slot_i - slot_t_rt)*256 + dbg_idx_i;
|
|
|
|
|
// bank_a read port: ST_M load drives pm_a_full; otherwise debug index.
|
|
|
|
|
assign ba_rd_addr = (st == ST_M) ? pm_a_full[PA_AW-1:0] : dbg_a_addr[PA_AW-1:0];
|
|
|
|
|
// bank_t read port: ST_M acc drives the t_hat accumulate addr; ST_E ek-half
|
|
|
|
|
// drives the byteEncode addr; otherwise debug index.
|
|
|
|
|
assign bt_rd_addr = (st == ST_M) ? m_tacc_full[PT_AW-1:0] :
|
|
|
|
|
(st == ST_E) ? e_rd_full[PT_AW-1:0] :
|
|
|
|
|
dbg_t_addr[PT_AW-1:0];
|
|
|
|
|
// bank_se read port: ST_N load (s/e NTT), ST_M load (pm_b s_hat[j]) vs
|
|
|
|
|
// accumulate (e_hat, mutually exclusive via m_loading), ST_E dk-half, dbg.
|
|
|
|
|
assign bse_rd_addr = (st == ST_N) ? ntt_rd_full[PSE_AW-1:0] :
|
|
|
|
|
(st == ST_M) ? (m_loading ? pm_b_full[PSE_AW-1:0]
|
|
|
|
|
: m_eacc_full[PSE_AW-1:0]) :
|
|
|
|
|
(st == ST_E) ? e_rd_full[PSE_AW-1:0] :
|
|
|
|
|
dbg_se_addr[PSE_AW-1:0];
|
|
|
|
|
// bank_se write port: ST_C CBD store (rel slot c_poly), ST_N NTT writeback
|
|
|
|
|
// (rel slot n_slot). Disjoint states.
|
|
|
|
|
assign bse_we = ((st == ST_C) && c_busy && cbd_vo && cbd_ack) ||
|
|
|
|
|
((st == ST_N) && ntt_vo);
|
|
|
|
|
assign bse_wa = (st == ST_N) ? ((n_slot*256 + n_widx) & ((1<<PSE_AW)-1))
|
|
|
|
|
: ((c_poly*256 + c_widx) & ((1<<PSE_AW)-1));
|
|
|
|
|
assign bse_wd = (st == ST_N) ? ntt_coeff : cbd_modq;
|
|
|
|
|
always @(posedge clk) begin
|
|
|
|
|
if (dbg_slot_i >= slot_t_rt) dbg_coeff_r <= bank_t [dbg_t_addr[PT_AW-1:0]];
|
|
|
|
|
else if (dbg_slot_i >= slot_s_rt) dbg_coeff_r <= bank_se[dbg_se_addr[PSE_AW-1:0]];
|
|
|
|
|
else dbg_coeff_r <= bank_a [dbg_a_addr[PA_AW-1:0]];
|
|
|
|
|
if (dbg_slot_i >= slot_t_rt) dbg_coeff_r <= bt_rd_data; // bank_t (sd_bram)
|
|
|
|
|
else if (dbg_slot_i >= slot_s_rt) dbg_coeff_r <= bse_rd_data; // bank_se (sd_bram)
|
|
|
|
|
else dbg_coeff_r <= ba_rd_data; // bank_a (sd_bram)
|
|
|
|
|
end
|
|
|
|
|
assign dbg_coeff_o = dbg_coeff_r;
|
|
|
|
|
|
|
|
|
|
@@ -411,17 +482,16 @@ module mlkem_top #(
|
|
|
|
|
reg n_valid; // feeding coeffs to ntt_core (delayed 1 cyc vs n_ridx)
|
|
|
|
|
reg n_loading; // 1 while presenting load addresses to bank_se
|
|
|
|
|
reg n_pending; // waiting for ntt_core IDLE to start next slot
|
|
|
|
|
reg [11:0] n_rd_data; // registered bank_se read (== sd_bram timing)
|
|
|
|
|
wire [SAW-1:0] n_slot_addr = slot_s_rt + n_slot; // s_hat then e_hat contiguous
|
|
|
|
|
|
|
|
|
|
wire ntt_ready;
|
|
|
|
|
wire [11:0] ntt_coeff;
|
|
|
|
|
wire ntt_vo;
|
|
|
|
|
wire ntt_done;
|
|
|
|
|
// bank_se read addr for the NTT load (relative slot = n_slot); registered
|
|
|
|
|
// into n_rd_data, which feeds ntt_core 1 cycle later.
|
|
|
|
|
// bank_se read addr for the NTT load (relative slot = n_slot); sd_bram
|
|
|
|
|
// registers it into bse_rd_data, which feeds ntt_core 1 cycle later.
|
|
|
|
|
wire [13:0] ntt_rd_full = n_slot*256 + n_ridx[7:0];
|
|
|
|
|
wire [11:0] ntt_in = n_rd_data;
|
|
|
|
|
wire [11:0] ntt_in = bse_rd_data;
|
|
|
|
|
|
|
|
|
|
ntt_core u_ntt (
|
|
|
|
|
.clk(clk), .rst_n(rst_n),
|
|
|
|
|
@@ -450,28 +520,34 @@ module mlkem_top #(
|
|
|
|
|
// bytes || rho; dk_pke = s_hat[0..K-1] bytes. Walk coeff pairs per poly.
|
|
|
|
|
reg [4:0] e_poly; // 0..2K-1: [0,K) = t_hat -> ek; [K,2K) = s_hat -> dk_pke
|
|
|
|
|
reg [7:0] e_pair; // 0..127 coeff-pair within poly
|
|
|
|
|
reg [1:0] e_byte; // 0..2 byte within the current pair (BRAM: 1 write/cycle)
|
|
|
|
|
reg [9:0] e_rho; // 0..31 rho byte copy index (ek tail)
|
|
|
|
|
reg e_done; // serialization complete
|
|
|
|
|
// source poly slot: t_hat[e_poly] for ek half, s_hat[e_poly-K] for dk half
|
|
|
|
|
wire e_is_dk = (e_poly >= {1'b0, k_r});
|
|
|
|
|
wire [4:0] e_pidx = e_is_dk ? (e_poly - {1'b0, k_r}) : e_poly; // index within target
|
|
|
|
|
wire [SAW-1:0] e_slot = e_is_dk ? (slot_s_rt + e_pidx) : (slot_t_rt + e_pidx);
|
|
|
|
|
// two coeffs of the current pair: ek half reads t_hat (bank_t), dk half
|
|
|
|
|
// reads s_hat (bank_se). Relative index within the bank = e_pidx.
|
|
|
|
|
wire [13:0] e_rd0_full = e_pidx*256 + {e_pair, 1'b0};
|
|
|
|
|
wire [13:0] e_rd1_full = e_pidx*256 + {e_pair, 1'b1};
|
|
|
|
|
wire [11:0] e_c0 = e_is_dk ? bank_se[e_rd0_full[PSE_AW-1:0]] : bank_t[e_rd0_full[PT_AW-1:0]];
|
|
|
|
|
wire [11:0] e_c1 = e_is_dk ? bank_se[e_rd1_full[PSE_AW-1:0]] : bank_t[e_rd1_full[PT_AW-1:0]];
|
|
|
|
|
// 3 packed bytes
|
|
|
|
|
wire [7:0] e_b0 = e_c0[7:0];
|
|
|
|
|
wire [7:0] e_b1 = {e_c1[3:0], e_c0[11:8]};
|
|
|
|
|
wire [7:0] e_b2 = e_c1[11:4];
|
|
|
|
|
// byte base offset within target memory: poly index *384 (= 128 pairs *3)
|
|
|
|
|
wire [11:0] e_base = e_pidx * 12'd384;
|
|
|
|
|
// Registered single-port read (sd_bram timing). One bank read per cycle:
|
|
|
|
|
// ek half reads bank_t (t_hat), dk half reads bank_se (s_hat). The two
|
|
|
|
|
// coeffs of a pair are serialized across a 4-cycle micro-phase e_ph:
|
|
|
|
|
// 0 = fetch c0
|
|
|
|
|
// 1 = c0 ready: write b0, save c0[11:8], fetch c1
|
|
|
|
|
// 2 = c1 ready: write b1, save c1[11:4]
|
|
|
|
|
// 3 = write b2, advance pair
|
|
|
|
|
reg [1:0] e_ph; // per-pair micro-phase 0..3
|
|
|
|
|
reg [3:0] e_c0_hi; // saved c0[11:8] for b1
|
|
|
|
|
reg [7:0] e_c1_hi; // saved c1[11:4] for b2
|
|
|
|
|
wire e_rd_half = (e_ph == 2'd1); // ph0 -> c0, ph1 -> c1
|
|
|
|
|
wire [13:0] e_rd_full = e_pidx*256 + {e_pair, e_rd_half}; // bse/bt rd addr in ST_E
|
|
|
|
|
// dk half reads bank_se (bse_rd_data), ek half reads bank_t (bt_rd_data);
|
|
|
|
|
// both registered inside their sd_bram with the same 1-cycle latency, so
|
|
|
|
|
// the coeff for the addr presented last cycle is selected here.
|
|
|
|
|
wire [11:0] e_rd_coeff = e_is_dk ? bse_rd_data : bt_rd_data;
|
|
|
|
|
// byteEncode write byte offset within the target memory: pair*3 + byte index.
|
|
|
|
|
wire [11:0] e_base = e_pidx * 12'd384; // poly index *384 (=128 pairs*3)
|
|
|
|
|
wire [11:0] e_boff = e_base + {e_pair, 1'b0} + {2'b0, e_pair}; // pair*3
|
|
|
|
|
// BRAM write is 1 byte/cycle: select packed byte by e_byte sub-counter
|
|
|
|
|
wire [7:0] e_byte_d = (e_byte == 2'd0) ? e_b0 : (e_byte == 2'd1) ? e_b1 : e_b2;
|
|
|
|
|
wire [1:0] e_wb = e_ph - 2'd1; // ph1->byte0, ph2->byte1, ph3->byte2
|
|
|
|
|
wire [7:0] e_wbyte = (e_ph == 2'd1) ? e_rd_coeff[7:0] // b0 = c0[7:0]
|
|
|
|
|
: (e_ph == 2'd2) ? {e_rd_coeff[3:0], e_c0_hi} // b1 = {c1[3:0],c0[11:8]}
|
|
|
|
|
: e_c1_hi; // b2 = c1[11:4]
|
|
|
|
|
|
|
|
|
|
wire [SAW-1:0] m_aslot = m_i*k_r + m_j; // A_hat[i][j] slot = i*k + j
|
|
|
|
|
wire [SAW-1:0] m_sslot = slot_s_rt + m_j; // s_hat[j]
|
|
|
|
|
@@ -484,13 +560,12 @@ module mlkem_top #(
|
|
|
|
|
wire pm_vo;
|
|
|
|
|
// pm_a: A_hat[i][j] in bank_a (abs slot m_aslot). pm_b: s_hat[j] in
|
|
|
|
|
// bank_se (relative slot = m_sslot - slot_s_rt = m_j). m_ld is a read-ahead
|
|
|
|
|
// pointer; both bank reads are registered into pm_a_rd/pm_b_rd and fed to
|
|
|
|
|
// poly_mul one cycle later (pm_valid delayed 1 cyc), like ST_N.
|
|
|
|
|
// pointer; both bank reads come from their sd_bram (registered, 1-cycle
|
|
|
|
|
// latency) and feed poly_mul one cycle later (pm_valid delayed 1 cyc).
|
|
|
|
|
wire [13:0] pm_a_full = m_aslot*256 + m_ld[7:0];
|
|
|
|
|
wire [13:0] pm_b_full = m_j*256 + m_ld[7:0];
|
|
|
|
|
reg [11:0] pm_a_rd, pm_b_rd; // registered bank reads (sd_bram timing)
|
|
|
|
|
wire [11:0] pm_a_in = pm_a_rd;
|
|
|
|
|
wire [11:0] pm_b_in = pm_b_rd;
|
|
|
|
|
wire [11:0] pm_a_in = ba_rd_data; // bank_a sd_bram registered read
|
|
|
|
|
wire [11:0] pm_b_in = bse_rd_data; // bank_se sd_bram registered read (load phase)
|
|
|
|
|
|
|
|
|
|
poly_mul_sync u_pmul (
|
|
|
|
|
.clk(clk), .rst_n(rst_n),
|
|
|
|
|
@@ -505,21 +580,19 @@ module mlkem_top #(
|
|
|
|
|
|
|
|
|
|
// accumulator source: e_hat[i] for first term (j==0), else running t_hat[i].
|
|
|
|
|
// e_hat[i] lives in bank_se at relative slot (slot_e_rt-slot_s_rt + m_i) = K+m_i.
|
|
|
|
|
// t_hat[i] lives in bank_t at relative slot m_i.
|
|
|
|
|
// Registered read-ahead: present the index that the NEXT pm_vo will consume
|
|
|
|
|
// t_hat[i] lives in bank_t at relative slot m_i. Both reads come from their
|
|
|
|
|
// sd_bram (bse_rd_data / bt_rd_data) with the same 1-cycle latency.
|
|
|
|
|
// Registered read-ahead: present the index the NEXT pm_vo will consume
|
|
|
|
|
// m_acc_radr = pm_vo ? m_oidx+1 : m_oidx
|
|
|
|
|
// and register the selected bank into m_acc_rd, consumed one cycle later when
|
|
|
|
|
// pm_vo is high. The same-index read-modify-write on bank_t is read-old:
|
|
|
|
|
// the running t_hat source was written by the previous (j-1) term, long
|
|
|
|
|
// settled; the value consumed this cycle was registered before this cycle's
|
|
|
|
|
// write to m_oidx. Cadence is CALC(vo=0)/C0(vo=1)/C1(vo=1).
|
|
|
|
|
// The j-select is applied on the registered outputs (m_jq = (m_j==0)
|
|
|
|
|
// delayed 1 cyc to align with the read latency). RMW read-old holds: read
|
|
|
|
|
// addr (m_oidx+1) leads write addr (m_oidx). Cadence CALC/C0/C1.
|
|
|
|
|
wire [7:0] m_acc_radr = pm_vo ? (m_oidx + 8'd1) : m_oidx;
|
|
|
|
|
wire [13:0] m_eacc_full = ({2'b0, k_r} + {2'b0, m_i})*256 + m_acc_radr; // K+m_i
|
|
|
|
|
wire [13:0] m_tacc_full = m_i*256 + m_acc_radr;
|
|
|
|
|
wire [11:0] m_acc_rd_next = (m_j == 2'd0) ? bank_se[m_eacc_full[PSE_AW-1:0]]
|
|
|
|
|
: bank_t [m_tacc_full[PT_AW-1:0]];
|
|
|
|
|
reg [11:0] m_acc_rd;
|
|
|
|
|
wire [11:0] m_acc_src = m_acc_rd;
|
|
|
|
|
wire [13:0] m_eacc_full = ({2'b0, k_r} + {2'b0, m_i})*256 + m_acc_radr; // K+m_i (bse_rd_addr in acc)
|
|
|
|
|
wire [13:0] m_tacc_full = m_i*256 + m_acc_radr; // bt_rd_addr
|
|
|
|
|
reg m_jq; // (m_j==0) delayed 1 cyc to match read latency
|
|
|
|
|
// selected accumulator source aligned with pm_coeff
|
|
|
|
|
wire [11:0] m_acc_src = m_jq ? bse_rd_data : bt_rd_data;
|
|
|
|
|
// (a + b) mod Q (both < Q, sum < 2Q): one conditional subtract
|
|
|
|
|
wire [12:0] m_sum = {1'b0, m_acc_src} + {1'b0, pm_coeff};
|
|
|
|
|
wire [11:0] m_accq = (m_sum >= 13'(Q)) ? (m_sum - 13'(Q)) : m_sum[11:0];
|
|
|
|
|
@@ -565,7 +638,6 @@ module mlkem_top #(
|
|
|
|
|
n_widx <= 8'd0;
|
|
|
|
|
n_valid <= 1'b0;
|
|
|
|
|
n_loading <= 1'b0;
|
|
|
|
|
n_rd_data <= 12'd0;
|
|
|
|
|
n_pending <= 1'b0;
|
|
|
|
|
m_i <= 2'd0;
|
|
|
|
|
m_j <= 2'd0;
|
|
|
|
|
@@ -574,12 +646,12 @@ module mlkem_top #(
|
|
|
|
|
m_loading <= 1'b0;
|
|
|
|
|
m_pending <= 1'b0;
|
|
|
|
|
pm_valid <= 1'b0;
|
|
|
|
|
pm_a_rd <= 12'd0;
|
|
|
|
|
pm_b_rd <= 12'd0;
|
|
|
|
|
m_acc_rd <= 12'd0;
|
|
|
|
|
m_jq <= 1'b0;
|
|
|
|
|
e_poly <= 3'd0;
|
|
|
|
|
e_pair <= 8'd0;
|
|
|
|
|
e_byte <= 2'd0;
|
|
|
|
|
e_ph <= 2'd0;
|
|
|
|
|
e_c0_hi <= 4'd0;
|
|
|
|
|
e_c1_hi <= 8'd0;
|
|
|
|
|
e_rho <= 10'd0;
|
|
|
|
|
e_done <= 1'b0;
|
|
|
|
|
ek_we <= 1'b0;
|
|
|
|
|
@@ -639,9 +711,11 @@ module mlkem_top #(
|
|
|
|
|
a_busy <= 1'b1;
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
// store each output coefficient only while busy (ignore stale last coeff from prior poly)
|
|
|
|
|
// store each output coefficient only while busy (ignore stale
|
|
|
|
|
// last coeff from prior poly). The bank_a write itself is the
|
|
|
|
|
// combinational ba_we/ba_wa/ba_wd assigns above; here we only
|
|
|
|
|
// advance the write index and (i,j) bookkeeping.
|
|
|
|
|
if (a_busy && snt_vo && snt_ack) begin
|
|
|
|
|
bank_a[(a_slot*256 + a_widx) & ((1<<PA_AW)-1)] <= snt_coeff;
|
|
|
|
|
if (snt_last) begin
|
|
|
|
|
// finished this poly; advance (i,j) in row-major order
|
|
|
|
|
a_pair <= a_pair + 5'd1;
|
|
|
|
|
@@ -678,8 +752,8 @@ module mlkem_top #(
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
if (c_busy && cbd_vo && cbd_ack) begin
|
|
|
|
|
// bank_se relative slot = c_slot - slot_s_rt = c_poly
|
|
|
|
|
bank_se[(c_poly*256 + c_widx) & ((1<<PSE_AW)-1)] <= cbd_modq;
|
|
|
|
|
// bank_se write is the combinational bse_we/bse_wa/bse_wd
|
|
|
|
|
// assigns (rel slot c_poly); here only advance counters.
|
|
|
|
|
if (cbd_last) begin
|
|
|
|
|
c_poly <= c_poly + 3'd1;
|
|
|
|
|
c_widx <= 8'd0;
|
|
|
|
|
@@ -692,8 +766,8 @@ module mlkem_top #(
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
// Arm N stage when C finishes: prime load of slot S0. n_ridx is a
|
|
|
|
|
// read-ahead pointer; bank_se read is registered into n_rd_data and
|
|
|
|
|
// fed to ntt_core one cycle later, so valid starts low (priming).
|
|
|
|
|
// read-ahead pointer; bank_se read is registered inside sd_bram (bse_rd_data)
|
|
|
|
|
// and fed to ntt_core one cycle later, so valid starts low (priming).
|
|
|
|
|
if (st == ST_C && st_next == ST_N) begin
|
|
|
|
|
n_slot <= 3'd0;
|
|
|
|
|
n_ridx <= 9'd0;
|
|
|
|
|
@@ -705,26 +779,25 @@ module mlkem_top #(
|
|
|
|
|
|
|
|
|
|
// ---- ST_N: forward NTT each of S0,S1,E0,E1 in place ----
|
|
|
|
|
if (st == ST_N) begin
|
|
|
|
|
// LOAD phase: present read-ahead addr to bank_se; the value
|
|
|
|
|
// registered last cycle (n_rd_data) is consumed by ntt_core
|
|
|
|
|
// this cycle (n_valid). Cores hold ready high through LOAD, so
|
|
|
|
|
// a fixed 1-cycle skew suffices (no backpressure gating).
|
|
|
|
|
// LOAD phase: present read-ahead addr to bank_se (bse_rd_addr);
|
|
|
|
|
// sd_bram registers it, so bse_rd_data is consumed by ntt_core
|
|
|
|
|
// one cycle later (n_valid). Cores hold ready high through LOAD,
|
|
|
|
|
// so a fixed 1-cycle skew suffices (no backpressure gating).
|
|
|
|
|
if (n_loading) begin
|
|
|
|
|
if (n_ridx == 9'd256) begin
|
|
|
|
|
// 256th coeff (bank_se[255]) consumed this cycle; stop
|
|
|
|
|
// 256th coeff consumed this cycle; stop presenting addr
|
|
|
|
|
n_loading <= 1'b0;
|
|
|
|
|
n_valid <= 1'b0;
|
|
|
|
|
end else begin
|
|
|
|
|
n_rd_data <= bank_se[ntt_rd_full[PSE_AW-1:0]];
|
|
|
|
|
n_ridx <= n_ridx + 9'd1;
|
|
|
|
|
n_valid <= 1'b1; // data presented last cycle is valid
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
// OUTPUT phase: collect 256 results, write back to same slot
|
|
|
|
|
// OUTPUT phase: collect 256 results, write back to same slot.
|
|
|
|
|
// The bank_se write is the combinational bse_we/bse_wa/bse_wd
|
|
|
|
|
// assigns (rel slot n_slot); here only advance the write index.
|
|
|
|
|
if (ntt_vo) begin
|
|
|
|
|
// bank_se relative slot = n_slot_addr - slot_s_rt = n_slot
|
|
|
|
|
bank_se[(n_slot*256 + n_widx) & ((1<<PSE_AW)-1)] <= ntt_coeff;
|
|
|
|
|
n_widx <= n_widx + 8'd1; // wraps 255->0 after last
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
@@ -749,8 +822,8 @@ module mlkem_top #(
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
// Arm M stage when N finishes: prime first (i=0,j=0) poly_mul load.
|
|
|
|
|
// m_ld read-ahead pointer; pm_a_rd/pm_b_rd registered, pm_valid
|
|
|
|
|
// asserted one cycle after an address is presented (like ST_N).
|
|
|
|
|
// m_ld read-ahead pointer; bank_a/bank_se sd_bram reads land 1 cyc
|
|
|
|
|
// later, pm_valid asserted one cycle after an address is presented.
|
|
|
|
|
if (st == ST_N && st_next == ST_M) begin
|
|
|
|
|
m_i <= 2'd0;
|
|
|
|
|
m_j <= 2'd0;
|
|
|
|
|
@@ -763,14 +836,15 @@ module mlkem_top #(
|
|
|
|
|
|
|
|
|
|
// ---- ST_M: t_hat[i] = e_hat[i] + sum_j A[i][j] o s_hat[j] ----
|
|
|
|
|
if (st == ST_M) begin
|
|
|
|
|
// registered accumulator read (sd_bram timing): captures the
|
|
|
|
|
// index the next pm_vo will consume. Free-running within ST_M;
|
|
|
|
|
// primed during LOAD (m_oidx held at 0).
|
|
|
|
|
m_acc_rd <= m_acc_rd_next;
|
|
|
|
|
// accumulator reads come from sd_bram (bse_rd_data e_hat /
|
|
|
|
|
// bt_rd_data t_hat). m_jq aligns the j-select with the 1-cycle
|
|
|
|
|
// read latency. (bse_rd_addr muxes load vs acc by m_loading.)
|
|
|
|
|
m_jq <= (m_j == 2'd0);
|
|
|
|
|
|
|
|
|
|
// LOAD: present read-ahead addr to bank_a/bank_se; the pair
|
|
|
|
|
// registered last cycle (pm_a_rd/pm_b_rd) is consumed by
|
|
|
|
|
// poly_mul this cycle (pm_valid). poly_mul holds ready high
|
|
|
|
|
// LOAD: present read-ahead addr to bank_a/bank_se via their
|
|
|
|
|
// sd_bram read ports (ba_rd_addr=pm_a_full, bse_rd_addr=pm_b_full
|
|
|
|
|
// while m_loading); ba_rd_data/bse_rd_data land next cycle and
|
|
|
|
|
// are consumed by poly_mul (pm_valid). poly_mul holds ready high
|
|
|
|
|
// through LOAD, so a fixed 1-cycle skew suffices.
|
|
|
|
|
if (m_loading) begin
|
|
|
|
|
if (m_ld == 9'd256) begin
|
|
|
|
|
@@ -779,17 +853,15 @@ module mlkem_top #(
|
|
|
|
|
m_ld <= 9'd0;
|
|
|
|
|
m_oidx <= 8'd0;
|
|
|
|
|
end else begin
|
|
|
|
|
pm_a_rd <= bank_a [pm_a_full[PA_AW-1:0]];
|
|
|
|
|
pm_b_rd <= bank_se[pm_b_full[PSE_AW-1:0]];
|
|
|
|
|
m_ld <= m_ld + 9'd1;
|
|
|
|
|
pm_valid <= 1'b1; // pair presented last cycle is valid
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
// ACCUMULATE: each product coeff += e_hat (j==0) or running t_hat
|
|
|
|
|
// ACCUMULATE: each product coeff += e_hat (j==0) or running t_hat.
|
|
|
|
|
// The bank_t write is the combinational bt_we/bt_wa/bt_wd assigns
|
|
|
|
|
// below; here we only advance the accumulate index / (i,j).
|
|
|
|
|
if (pm_vo) begin
|
|
|
|
|
// bank_t relative slot = m_tslot - slot_t_rt = m_i
|
|
|
|
|
bank_t[(m_i*256 + m_oidx) & ((1<<PT_AW)-1)] <= m_accq;
|
|
|
|
|
if (m_oidx == 8'd255) begin
|
|
|
|
|
// finished this (i,j) term; advance
|
|
|
|
|
if (m_j + 2'd1 < k_r) begin
|
|
|
|
|
@@ -819,27 +891,39 @@ module mlkem_top #(
|
|
|
|
|
if (st == ST_M && st_next == ST_E) begin
|
|
|
|
|
e_poly <= 3'd0;
|
|
|
|
|
e_pair <= 8'd0;
|
|
|
|
|
e_byte <= 2'd0;
|
|
|
|
|
e_ph <= 2'd0; // start at fetch-c0 of first pair
|
|
|
|
|
e_rho <= 10'd0;
|
|
|
|
|
e_done <= 1'b0;
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
// ---- ST_E: byteEncode12 t_hat -> ek, s_hat -> dk_pke, 1 byte/cycle ----
|
|
|
|
|
// ---- ST_E: byteEncode12 t_hat -> ek, s_hat -> dk_pke ----
|
|
|
|
|
// Single registered bank read per cycle; 4-cycle micro-phase per
|
|
|
|
|
// coeff pair (ph0 fetch c0, ph1 write b0 + fetch c1, ph2 write b1,
|
|
|
|
|
// ph3 write b2). The coeff for the addr presented last cycle is
|
|
|
|
|
// e_rd_coeff (= bse_rd_data for dk-half, bt_rd_data for ek-half),
|
|
|
|
|
// both registered inside their sd_bram (bse_rd_addr/bt_rd_addr =
|
|
|
|
|
// e_rd_full in ST_E).
|
|
|
|
|
if (st == ST_E && !e_done) begin
|
|
|
|
|
if (e_poly < {1'b0, k_r, 1'b0}) begin
|
|
|
|
|
// write current byte (e_byte_d) to target BRAM
|
|
|
|
|
if (!e_is_dk) begin
|
|
|
|
|
ek_we <= 1'b1;
|
|
|
|
|
ek_wa <= e_boff[10:0] + {9'd0, e_byte};
|
|
|
|
|
ek_wd <= e_byte_d;
|
|
|
|
|
end else begin
|
|
|
|
|
dkp_we <= 1'b1;
|
|
|
|
|
dkp_wa <= e_boff[10:0] + {9'd0, e_byte};
|
|
|
|
|
dkp_wd <= e_byte_d;
|
|
|
|
|
// ph0: fetch only (prime). ph1..3: write one packed byte.
|
|
|
|
|
if (e_ph != 2'd0) begin
|
|
|
|
|
if (!e_is_dk) begin
|
|
|
|
|
ek_we <= 1'b1;
|
|
|
|
|
ek_wa <= e_boff[10:0] + {9'd0, e_wb};
|
|
|
|
|
ek_wd <= e_wbyte;
|
|
|
|
|
end else begin
|
|
|
|
|
dkp_we <= 1'b1;
|
|
|
|
|
dkp_wa <= e_boff[10:0] + {9'd0, e_wb};
|
|
|
|
|
dkp_wd <= e_wbyte;
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
// advance sub-counters
|
|
|
|
|
if (e_byte == 2'd2) begin
|
|
|
|
|
e_byte <= 2'd0;
|
|
|
|
|
// save coeff high nibbles as they become available
|
|
|
|
|
if (e_ph == 2'd1) e_c0_hi <= e_rd_coeff[11:8]; // c0[11:8]
|
|
|
|
|
if (e_ph == 2'd2) e_c1_hi <= e_rd_coeff[11:4]; // c1[11:4]
|
|
|
|
|
|
|
|
|
|
// advance micro-phase / pair / poly
|
|
|
|
|
if (e_ph == 2'd3) begin
|
|
|
|
|
e_ph <= 2'd0;
|
|
|
|
|
if (e_pair == 8'd127) begin
|
|
|
|
|
e_pair <= 8'd0;
|
|
|
|
|
e_poly <= e_poly + 5'd1; // next poly or -> rho phase
|
|
|
|
|
@@ -847,7 +931,7 @@ module mlkem_top #(
|
|
|
|
|
e_pair <= e_pair + 8'd1;
|
|
|
|
|
end
|
|
|
|
|
end else begin
|
|
|
|
|
e_byte <= e_byte + 2'd1;
|
|
|
|
|
e_ph <= e_ph + 2'd1;
|
|
|
|
|
end
|
|
|
|
|
end else begin
|
|
|
|
|
// rho copy: ek[384*K + r] = rho byte r (r = 0..31), 1 byte/cycle
|
|
|
|
|
|