diff --git a/.claude/plans/decaps_plan.md b/.claude/plans/decaps_plan.md index 007922c..fc6c8ea 100644 --- a/.claude/plans/decaps_plan.md +++ b/.claude/plans/decaps_plan.md @@ -64,8 +64,8 @@ - **D0 — 脚手架 + dk/c 载入 + 解析** ✅:op_i 加宽 2-bit(00 KG/01 Enc/10 Dec),ST_DEC_LOAD(D0 暂直接→DONE)。dk 流入按 region 路由:dk_pke→dkp_bram、ek_pke→ek_bram、h→hek_r、z→z_r;ct→c_in_bram(独立于 ct_bram)。dbg 验证 h/z/ek_pke/dk_pke。**踩坑1:载入路由用 k_r 但 k_r 在 start_i 才锁存 → 预载期 region 边界全 0,路由全错。改用 LIVE k_i 边界(dkp_bytes_ld 等)。踩坑2:旧 KG/Enc TB 未接新端口(dk_in_*/c_in_*/dbg_*)→ X 漂入 write mux,KeyGen/Encaps 超时回归。补 tie-off 0。** runner = `./run_tb.sh dec [K] [CASE]`。K=2/3/4 D0 全过,KG/Enc 回归通过。 - **D1 — byteDecode_d + Decompress → u'/v'** ✅:复用 comp_decomp(改 mode 可选:Encaps C1/C2 compress mode=0,Decaps DECOMP mode=1)。ST_DEC_DECOMP 内联 byteDecode 走子机:逐字节读 c_in_bram,LSB-first 累进 bit buffer,凑够 d 位抽符号→comp_decomp 解压→写 bank。c1(K 多项式,d=du)→u'[i] bank_se rel i;c2(1 多项式,d=dv)→v' bank_t rel DEC_VSLOT=2(避开 UPSUM=1)。dbg_slot_i 加宽 4→6 bit(K=4 v' 在 slot 26)。dump_decaps.rs(ml-kem-r 工作树)产 u'/v'/s_hat/u_hat/w/m' golden 到 vectors/decgold/。**踩坑:dbg coeff 读回延迟 = bank(1)+dbg_coeff_r(1),TB rdcoeff 等 2 拍少一拍 → 数据整体错位一格;改 3 拍修正。** K=2/3/4 全过,KG/Enc 回归通过。 - **D2 — s_hat 解码 + u_hat = NTT(u')** ✅:复用 Encaps TDEC 机(ST_DEC_SDEC),字节源从 ek 切到 dkp_bram(td_byte mux + dkp_rd_addr 在 SDEC 走 td_ekaddr),s_hat 写 bank_a slot j*K(与 t_hat 同布局,D3 MAC 可直接读)。复用前向 NTT 机(ST_DEC_NTT,n_slot_max=k_r)对 bank_se rel 0..K-1 的 u' 原地变换成 u_hat。**踩坑:NTT 原地覆盖 u' → verify_d1 复查 u' 必失败;改为 verify_d1 只查 v'(bank_t 未动),u' 正确性由 u_hat==NTT(u') golden 传递性证明。** K=2/3/4 全过,KG/Enc 回归通过。 -- **D3 — w = v' − INTT(Σ s∘u_hat)**:V 机器 SUB 变体。dbg 对 w。 -- **D4 — m' = byteEncode₁(Compress₁(w))**:打包器 d=1。dbg 对 m'(== KAT 解密的 m')。 +- **D3 — w = v' − INTT(Σ s∘u_hat)** ✅:复用 Encaps V 机(ST_DEC_W,u_row=0 单多项式)。MAC s_hat[j](bank_a slot j*K)∘ u_hat[j](bank_se rel j)→psum bank_t[UPSUM],与 V MAC 完全同址,免改。INTT 原地。SUB:w = v' − psum,(v'−psum) 负则 +Q。**关键:v'/psum 读口冲突 → D1 把 v' 落到 bank_a slot DEC_VASLOT=1(s_hat 在 j*K,slot 1 恒空 K≥2),SUB 时 psum(bank_t)+v'(bank_a)并行读,正如 V-ADD 并读 psum+e2。** K=2/3/4 w 全过。 +- **D4 — m' = byteEncode₁(Compress₁(w))** ✅:ST_DEC_MENC,逐系数读 bank_t[UPSUM] 的 w,Compress₁(w)=1 iff 832u', c2->v' localparam ST_DEC_SDEC = 5'd22; // D2: byteDecode12 dk_pke -> s_hat (bank_a slot j*K) localparam ST_DEC_NTT = 5'd23; // D2: u_hat[i] = NTT(u'[i]) in place (bank_se rel i) + localparam ST_DEC_W = 5'd24; // D3: w = v' - INTT(sum_j s_hat[j] o u_hat[j]) + localparam ST_DEC_MENC = 5'd25; // D4: m' = byteEncode_1(Compress_1(w)) localparam ST_DONE = 5'd31; reg [4:0] st, st_next; @@ -499,8 +510,8 @@ module mlkem_top #( assign dbg_sigma_o = sigma_r; assign dbg_r_o = r_r; assign dbg_hek_o = hek_r; - // Decaps taps: m' reuses m_r (Decrypt writes it), z/h parsed from dk at load. - assign dbg_mprime_o = m_r; + // Decaps taps: m' from D4 (mprime_r); z/h parsed from dk at load. + assign dbg_mprime_o = mprime_r; assign dbg_kbar_o = kbar_r; assign dbg_decz_o = z_r; assign dbg_dech_o = hek_r; // Decaps parses dk's H(ek) into hek_r at load @@ -748,7 +759,7 @@ module mlkem_top #( wire [13:0] ntt_rd_full = n_slot*256 + n_ridx[7:0]; // ntt_core inputs muxed: KeyGen/Encaps fwd NTT feeds bse_rd_data (mode 0); // Encaps E4/E6 INTT sub-phase feeds psum (bt_rd_data) with mode 1. - wire u_intt_act = (st == ST_ENC_U || st == ST_ENC_V) && (u_sub == 2'd1); + wire u_intt_act = (st == ST_ENC_U || st == ST_ENC_V || st == ST_DEC_W) && (u_sub == 2'd1); wire [11:0] ntt_in = u_intt_act ? bt_rd_data : bse_rd_data; wire ntt_vin = u_intt_act ? u_nvalid : n_valid; wire ntt_mode = u_intt_act ? 1'b1 : 1'b0; @@ -873,7 +884,7 @@ module mlkem_top #( // - mu[w] = Decompress_1(m bit w) = m_r[w] ? 1665 : 0 (1665 = round(Q/2)). // u_row_max bounds the row loop: K for U, 1 for V. localparam E2_ASLOT = 12'd1; // bank_a slot holding relocated e2 (never a t_hat slot, K>=2) - wire [2:0] u_row_max = (st == ST_ENC_V) ? 3'd1 : k_r; + wire [2:0] u_row_max = (st == ST_ENC_V || st == ST_DEC_W) ? 3'd1 : k_r; // V-ADD: read psum (bank_t UPSUM) + e2 (bank_a E2_ASLOT) at u_aidx (lead), // add mu (by write index u_awidx), write v to bank_t[UPSUM] at u_awidx. wire [13:0] u_v_e2rd = E2_ASLOT*256 + u_aidx[7:0]; // bank_a e2 (lead) @@ -884,6 +895,34 @@ module mlkem_top #( wire [11:0] u_vq = (u_vsub1 >= 14'(Q)) ? (u_vsub1 - 14'(Q)) : u_vsub1[11:0]; wire u_v_we = (st == ST_ENC_V) && (u_sub == 2'd2) && u_avalid; + // ================================================================ + // D3: w = v' - INTT(sum_j s_hat[j] o u_hat[j]) (Decaps ST_DEC_W). + // Reuses the u_* MAC/INTT machine (u_row tied to 0, single poly): + // MAC : s_hat[j] (bank_a slot j*K, == u_aslot with u_row=0) o u_hat[j] + // (bank_se rel j, == u_pm_b_full) -> psum bank_t[UPSUM]. (same + // addressing as Encaps V MAC, which reads t_hat[j] o y_hat[j].) + // INTT: INTT(psum) in place in bank_t[UPSUM]. + // SUB : w[i] = v'[i] - psum[i] mod Q. v' is in bank_a[DEC_VASLOT] (D1), + // psum in bank_t[UPSUM]; read both in parallel (like V-ADD reads + // psum + e2). w written back to bank_t[UPSUM] in place. + wire [13:0] u_w_vrd = DEC_VASLOT*256 + u_aidx[7:0]; // bank_a v' (lead) + wire [13:0] u_wsub_wr = UPSUM*256 + u_awidx; // bank_t w (write) + wire [12:0] u_wdiff = {1'b0, ba_rd_data} - {1'b0, bt_rd_data}; // v' - psum + wire [11:0] u_wq = u_wdiff[12] ? (u_wdiff[11:0] + 12'(Q)) : u_wdiff[11:0]; + wire u_w_we = (st == ST_DEC_W) && (u_sub == 2'd2) && u_avalid; + + // ================================================================ + // D4: m' = byteEncode_1(Compress_1(w)) (Decaps ST_DEC_MENC). + // Compress_1(x) = round(2x/Q) mod 2 = 1 iff x in (Q/4, 3Q/4], i.e. + // 832 < x <= 2496 for Q=3329. Pack 256 bits LSB-first into 32 bytes. + // w is read from bank_t[UPSUM] (in place from D3), 1 coeff per 2 cycles. + reg [7:0] men_idx; // coeff 0..255 + reg [1:0] men_ph; // micro-phase + reg men_done; + reg [255:0] mprime_r; // recovered message m' (32 bytes, bit-packed LSB-first) + wire [13:0] men_rd = UPSUM*256 + men_idx; // bank_t w read addr + wire men_w_bit = (bt_rd_data > 12'd832) && (bt_rd_data <= 12'd2496); // Compress_1 + // ST_ENC_E2MV: copy e2 (bank_t rel slot 0, 256 coeffs) into bank_a[E2_ASLOT]. // bank_t read leads (em_ridx), bank_a write trails 1 cycle (em_we/em_widx). reg [8:0] em_ridx; // 0..256 read-ahead over e2 @@ -953,12 +992,15 @@ module mlkem_top #( // decompress), and write each result (mod q) to a bank. // c1 = K polys, d=du -> u'[i] in bank_se rel i (0..K-1) // c2 = 1 poly, d=dv -> v' in bank_t rel slot DEC_VSLOT + // c1 = K polys, d=du -> u'[i] in bank_se rel i (0..K-1) + // c2 = 1 poly, d=dv -> v' in bank_a slot DEC_VASLOT (so D3's w-SUB can + // read psum (bank_t) and v' (bank_a) in parallel, like Encaps V-ADD). // micro-phase dec_ph: // 0 present c_in byte addr; 1 capture byte, append 8 bits to buf; // while (nbits>=d): extract d-bit symbol -> comp_decomp (decompress); // 2 wait pipe; 3 cd_vo -> write coeff to bank, advance coeff index. // ================================================================ - localparam DEC_VSLOT = 10'd2; // bank_t rel slot holding v' (avoid UPSUM=1) + localparam DEC_VASLOT = 12'd1; // bank_a slot for v' (free: s_hat at j*K, slot 1 unused K>=2) reg [2:0] dec_poly; // c1: 0..K-1; c2: single (=K marker) reg [7:0] dec_cidx; // coeff 0..255 within poly (feed side) reg [7:0] dec_widx; // coeff 0..255 within poly (writeback side, lags pipe) @@ -1029,7 +1071,11 @@ module mlkem_top #( // D2: s_hat decode (reuse TDEC machine), then u_hat = NTT(u'). D2 // lands in DONE once u_hat is ready so the stage can be dbg-checked. ST_DEC_SDEC: if (td_done) st_next = ST_DEC_NTT; - ST_DEC_NTT: if (n_slot >= {2'b0, k_r}) st_next = ST_DONE; + ST_DEC_NTT: if (n_slot >= {2'b0, k_r}) st_next = ST_DEC_W; + // D3: w = v' - INTT(sum_j s_hat[j] o u_hat[j]). Single output poly + // (u_row 0..0); MAC->INTT->SUB, then D4 encodes m'. + ST_DEC_W: if (u_row >= 3'd1) st_next = ST_DEC_MENC; + ST_DEC_MENC: if (men_done) st_next = ST_DONE; ST_G: if (sha3_vo) st_next = ST_A; ST_A: if (a_pair >= kk_rt) st_next = ST_C; ST_C: if (c_poly >= {1'b0, k_r, 1'b0}) st_next = ST_N; @@ -1170,6 +1216,10 @@ module mlkem_top #( dec_dc_coeff <= 12'd0; dec_dc_valid <= 1'b0; dec_dc_done <= 1'b0; + men_idx <= 8'd0; + men_ph <= 2'd0; + men_done <= 1'b0; + mprime_r <= 256'd0; h_blk <= 3'd0; h_byte <= 8'd0; h_phase <= 2'd0; @@ -1445,7 +1495,7 @@ module mlkem_top #( // psum (init 0 at j==0) accumulates into bank_t[UPSUM]. Mirrors ST_M // load/accumulate cadence (read-ahead by 1, j-select via u_j0q). // U: A^T[u_row][j] (bank_a u_j*K+u_row). V: t_hat[j] (bank_a u_j*K, u_row=0). - if ((st == ST_ENC_U || st == ST_ENC_V) && u_sub == 2'd0) begin + if ((st == ST_ENC_U || st == ST_ENC_V || st == ST_DEC_W) && u_sub == 2'd0) begin u_j0q <= (u_j == 3'd0); if (u_loading) begin @@ -1490,7 +1540,7 @@ module mlkem_top #( end // ---- ST_ENC_U/V sub-phase 1: INTT(psum) mode=1 in place (bank_t) ---- - if ((st == ST_ENC_U || st == ST_ENC_V) && u_sub == 2'd1) begin + if ((st == ST_ENC_U || st == ST_ENC_V || st == ST_DEC_W) && u_sub == 2'd1) begin if (u_nloading) begin if (u_ridx == 9'd256) begin u_nloading <= 1'b0; @@ -1510,13 +1560,14 @@ module mlkem_top #( end end - // ---- ST_ENC_U/V sub-phase 2: ADD + writeback ---- + // ---- ST_ENC_U/V + ST_DEC_W sub-phase 2: ADD/SUB + writeback ---- // U: u[u_row] = psum + e1[u_row] mod Q -> bank_se (u_add_we). // V: v = psum + e2 + mu mod Q -> bank_t[UPSUM] (u_v_we). - // Read psum (bank_t) + e1/e2 at u_aidx; both arrive 1 cyc later, so + // D3 w = v' - psum mod Q -> bank_t[UPSUM] (u_w_we). + // Read psum (bank_t) + e1/e2/v' at u_aidx; both arrive 1 cyc later, so // register (valid,widx) and write next cycle. u_row_max bounds the - // row loop (K for U, 1 for V). - if ((st == ST_ENC_U || st == ST_ENC_V) && u_sub == 2'd2) begin + // row loop (K for U, 1 for V/D3). + if ((st == ST_ENC_U || st == ST_ENC_V || st == ST_DEC_W) && u_sub == 2'd2) begin if (u_aidx < 9'd256) begin u_aidx <= u_aidx + 9'd1; u_avalid <= 1'b1; // addr presented this cyc -> write next @@ -1647,7 +1698,7 @@ module mlkem_top #( // ph2: drop dc_valid (pulse captured) -> ph3. // ph3: cd_vo -> dec_u_we/dec_v_we writes cd_out; advance coeff. // c1 = K polys (d=du) -> u'[poly] bank_se; then c2 = 1 poly (d=dv) - // -> v' bank_t[DEC_VSLOT]. Byte stream is contiguous in c_in_bram. + // -> v' bank_a[DEC_VASLOT]. Byte stream is contiguous in c_in_bram. if (st == ST_DEC_DECOMP && !dec_dc_done) begin case (dec_ph) 2'd0: begin @@ -1952,6 +2003,42 @@ module mlkem_top #( u_pending <= 1'b0; pm_valid <= 1'b0; end + + // Arm D3 (ST_DEC_W) when D2 NTT finishes: same u_* MAC/INTT machine, + // single row (u_row 0..0). MAC s_hat[j] o u_hat[j], INTT, then SUB. + if (st == ST_DEC_NTT && st_next == ST_DEC_W) begin + u_row <= 3'd0; + u_sub <= 2'd0; // MAC + u_j <= 3'd0; + u_ld <= 9'd0; + u_oidx <= 8'd0; + u_loading <= 1'b1; + u_pending <= 1'b0; + pm_valid <= 1'b0; + end + + // Arm D4 (ST_DEC_MENC) when D3 w done: byteEncode_1(Compress_1(w)). + if (st == ST_DEC_W && st_next == ST_DEC_MENC) begin + men_idx <= 8'd0; + men_ph <= 2'd0; + men_done <= 1'b0; + end + + // ---- ST_DEC_MENC (D4): m' = byteEncode_1(Compress_1(w)) ---- + // Compress_1(w)=1 iff 832 < w <= 2496 (Q=3329). Pack 256 bits + // LSB-first into mprime_r (bit men_idx). Read w from bank_t[UPSUM]. + // ph0: present w[men_idx] addr; ph1: bt_rd_data valid -> set bit. + if (st == ST_DEC_MENC && !men_done) begin + case (men_ph) + 2'd0: men_ph <= 2'd1; // addr presented; wait read + default: begin // ph1: bt_rd_data = w[men_idx] + mprime_r[men_idx] <= men_w_bit; + if (men_idx == 8'd255) men_done <= 1'b1; + else men_idx <= men_idx + 8'd1; + men_ph <= 2'd0; + end + endcase + end end end