diff --git a/.claude/plans/encaps_plan.md b/.claude/plans/encaps_plan.md index 8d37f3a..7f950ae 100644 --- a/.claude/plans/encaps_plan.md +++ b/.claude/plans/encaps_plan.md @@ -73,7 +73,7 @@ ct_bytes_rt = c1_bytes_rt + c2_bytes_rt; // 768 / 1088 / 1568 - **E4 — u = INTT(Σ Â[j][i]∘ŷ[j]) + e1**:ST_ENC_U,poly_mul + 累加 + INTT(mode=1) + 加 e1。**转置寻址 slot=j·K+i**。dbg 对 u。 - **E5 — Compress_du + byteEncode_du → c1** ✅:comp_decomp(mode0,d=du) + 通用 LSB-first 打包器(ST_ENC_C1)写 ct_bram c1 区。dbg_ct tap 比 ct[0..c1_bytes]==KAT.ct 前缀,K=2/3/4 全过(含 K=4 du=11 跨字节)。runner = `./run_enc.sh K CASE`。 - **E6 — v = INTT(Σ t̂[j]∘ŷ[j]) + e2 + mu** ✅:ST_ENC_C1→ST_ENC_TDEC(t̂ 解码进 **bank_a** slot j·K)→ST_ENC_E2MV(e2 从 bank_t[0] 搬到 **bank_a[1]**)→ST_ENC_V(复用 u_* MAC/INTT/ADD,u_row≡0)。mu[w]=m_r[w]?1665:0。v 落 bank_t[UPSUM],dbg slot 9(K=2)。dbg 对 v==ml-kem-r golden 全过。**踩坑:e2 搬迁曾 off-by-one(写 e2[i+1] 到 slot i),已修(em_widx==em_ridx,写延后 1 拍对齐 bram 读)。** verify_e1 已废弃(TDEC 覆盖 bank_a 的 A_hat;A_hat 由 E4 转置 MAC 间接验证)。 -- **E7 — Compress_dv + byteEncode_dv → c2 + 端到端 KAT**:写 ct c2 区。干净 TB 喂 ek/m,比 ct(全长)==KAT.ct 且 ss==KAT.ss,K=2/3/4 各 count=0..N。 +- **E7 — Compress_dv + byteEncode_dv → c2 + 端到端 KAT** ✅:ST_ENC_V→ST_ENC_C2,复用 E5 打包器(cp_d=dv,coeff 源 bank_t[UPSUM]=v,poly 数=1),cp_wa 接着 c1_bytes 续写(不重置)。FSM:V→C2→DONE。verify_e7 比全长 ct==KAT.ct。**全 K(2/3/4)、多 case 端到端通过:ct==KAT.ct && ss==KAT.ss。Encaps 完成。** ## 验证 - TB `tb_mlkem_enc_katK_xsim.v`:从 `~/Dev/ml-kem-r/test_data/kat_MLKEM_{512,768,1024}.rsp` 取 pk(→ek)、msg(→m)、ct、ss,逐字节比 ct + 比 ss。 diff --git a/sync_rtl/top/TB/tb_mlkem_enc_katK_xsim.v b/sync_rtl/top/TB/tb_mlkem_enc_katK_xsim.v index f2504e5..09cbd88 100644 --- a/sync_rtl/top/TB/tb_mlkem_enc_katK_xsim.v +++ b/sync_rtl/top/TB/tb_mlkem_enc_katK_xsim.v @@ -113,6 +113,8 @@ module tb_mlkem_enc_katK_xsim; // E5: c1 = byteEncode_du(Compress_du(u)) must equal KAT.ct[0..C1B-1]. // Runs for every K/case (ct_b is the full KAT ciphertext). verify_e5; + // E7: full ct = c1 || c2 must equal KAT.ct (all CTB bytes). End-to-end. + verify_e7; $finish; end @@ -244,4 +246,24 @@ module tb_mlkem_enc_katK_xsim; else $display("K=2 CASE 0 FAIL (E6): %0d coeff mismatches", ce); end endtask + + // E7: full ciphertext ct = c1 || c2 read from ct_bram (0..CTB-1) via dbg_ct + // tap; compare to KAT.ct byte-exact. This is the end-to-end Encaps check + // (ct == KAT.ct). ss == KAT.ss is already checked at E0. + task verify_e7; + integer be; + begin + be = 0; + for (i = 0; i < CTB; i = i + 1) begin + dbg_ct_idx_i = i[10:0]; + @(posedge clk); @(posedge clk); @(posedge clk); + if (dbg_ct_o !== ct_b[i]) begin + if (be < 8) $display(" CT[%0d] got=%02x exp=%02x", i, dbg_ct_o, ct_b[i]); + be = be + 1; + end + end + if (be == 0) $display("K=%0d CASE %0d PASS (E7): ct (%0d B) == KAT.ct [ct==KAT.ct && ss==KAT.ss]", KP, casenum, CTB); + else $display("K=%0d CASE %0d FAIL (E7): %0d ct byte mismatches", KP, casenum, be); + end + endtask endmodule diff --git a/sync_rtl/top/mlkem_top.v b/sync_rtl/top/mlkem_top.v index 4ca096b..8aeecfc 100644 --- a/sync_rtl/top/mlkem_top.v +++ b/sync_rtl/top/mlkem_top.v @@ -279,6 +279,7 @@ module mlkem_top #( (u_sub == 2'd1) ? u_intt_rd[PT_AW-1:0] : // INTT load u_add_prd[PT_AW-1:0]) : // ADD psum read (st == ST_ENC_E2MV) ? em_rd[PT_AW-1:0] : // e2 relocate read + (st == ST_ENC_C2) ? cp_bt_full[PT_AW-1:0] : // C2: v[cp_idx] 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. @@ -839,9 +840,16 @@ module mlkem_top #( reg [5:0] cp_nbits; // valid bits in cp_buf reg [11:0] cp_wa; // ct_bram byte write address (runs c1 then c2) reg cp_done; // serialization complete (this region) - // coeff source: c1 reads u[cp_poly] in bank_se rel (K+cp_poly); c2 reads v - // (lands in bank_t rel slot V_SLOT -- defined in E6). For E5 only c1 path. + // coeff source: c1 (ST_ENC_C1) reads u[cp_poly] in bank_se rel (K+cp_poly); + // c2 (ST_ENC_C2) reads v in bank_t rel slot UPSUM (single poly, cp_poly=0). wire [13:0] cp_se_full = ({2'b0,k_r}+{2'b0,cp_poly})*256 + cp_idx; // bank_se u[cp_poly] + wire [13:0] cp_bt_full = UPSUM*256 + cp_idx; // bank_t v (UPSUM) + // packer generalization (E5 c1 vs E7 c2): + // coeff source: c1 = bank_se (u), c2 = bank_t (v) + // poly count: c1 = K (u rows), c2 = 1 (single v) + // bit width cp_d already = dv_rt for C2 else du_rt (declared above). + wire [11:0] cp_coeff_src = (st == ST_ENC_C2) ? bt_rd_data : bse_rd_data; + wire [2:0] cp_poly_max = (st == ST_ENC_C2) ? 3'd1 : k_r; reg pm_valid; wire pm_ready; @@ -908,7 +916,8 @@ module mlkem_top #( ST_ENC_C1: if (cp_done) st_next = ST_ENC_TDEC; // E5 done -> E6 ST_ENC_TDEC: if (td_done) st_next = ST_ENC_E2MV; // t_hat decoded -> relocate e2 ST_ENC_E2MV: if (em_done) st_next = ST_ENC_V; // e2 relocated -> compute v - ST_ENC_V: if (u_row >= 3'd1) st_next = ST_DONE; // E6: v done (1 row) + ST_ENC_V: if (u_row >= 3'd1) st_next = ST_ENC_C2; // E6: v done -> E7 + ST_ENC_C2: if (cp_done) st_next = ST_DONE; // E7: c2 packed -> done ST_DONE: st_next = ST_IDLE; default: st_next = ST_IDLE; endcase @@ -1384,21 +1393,23 @@ module mlkem_top #( cd_valid <= 1'b0; end - // ---- ST_ENC_C1: Compress_du(u[i]) -> byteEncode_du -> ct c1 region ---- + // ---- ST_ENC_C1/C2: Compress_d -> byteEncode_d -> ct region ---- + // C1 (E5): Compress_du(u[0..K-1]) from bank_se -> ct[0..c1_bytes). + // C2 (E7): Compress_dv(v) from bank_t[UPSUM] -> ct[c1_bytes..ct_bytes). // Per coeff, 5-phase micro-sequence (read-ahead 1 cyc bram + 1 cyc // comp_decomp pipe), then a drain sub-phase emitting whole bytes: - // ph0: present u[cp_poly][cp_idx] addr to bank_se (cp_se_full). - // ph1: coeff arrives (bse_rd_data) -> latch into cd_coeff, pulse cd_valid. + // ph0: present coeff addr (cp_se_full / cp_bt_full by state). + // ph1: coeff arrives (cp_coeff_src) -> latch into cd_coeff, pulse cd_valid. // ph2: drop cd_valid (1-cyc pulse); comp_decomp captures. - // ph3: cd_vo high -> cd_out (low du bits) valid; append LSB-first to cp_buf. + // ph3: cd_vo high -> cd_out (low cp_d bits) valid; append LSB-first. // ph4: drain: while >=8 bits buffered, emit one ct byte/cycle; then advance. - // Each poly = 256 coeffs = 32*du bytes (whole), so cp_buf empties at - // each poly boundary (no carry across polys). - if (st == ST_ENC_C1 && !cp_done) begin + // Each poly = 256 coeffs = 32*d bytes (whole), so cp_buf empties at + // each poly boundary (no carry across polys / regions). + if ((st == ST_ENC_C1 || st == ST_ENC_C2) && !cp_done) begin case (cp_ph) 3'd0: cp_ph <= 3'd1; // addr presented; wait read 3'd1: begin - cd_coeff <= bse_rd_data; // u coeff (registered read) + cd_coeff <= cp_coeff_src; // u (bank_se) or v (bank_t) cd_valid <= 1'b1; // feed comp_decomp (1-cyc pulse) cp_ph <= 3'd2; end @@ -1407,9 +1418,9 @@ module mlkem_top #( cp_ph <= 3'd3; end 3'd3: begin - // cd_out valid (cd_vo): append du bits LSB-first at bit cp_nbits - cp_buf <= cp_buf | (({13'd0, cd_out} & ((25'd1 << du_rt) - 25'd1)) << cp_nbits); - cp_nbits <= cp_nbits + {1'b0, du_rt}; + // cd_out valid (cd_vo): append cp_d bits LSB-first at bit cp_nbits + cp_buf <= cp_buf | (({13'd0, cd_out} & ((25'd1 << cp_d) - 25'd1)) << cp_nbits); + cp_nbits <= cp_nbits + {1'b0, cp_d}; cp_ph <= 3'd4; end default: begin // 3'd4: drain whole bytes @@ -1424,8 +1435,8 @@ module mlkem_top #( // coeff fully packed; advance coeff / poly if (cp_idx == 8'd255) begin cp_idx <= 8'd0; - if (cp_poly + 3'd1 < k_r) cp_poly <= cp_poly + 3'd1; - else cp_done <= 1'b1; // c1 complete -> DONE + if (cp_poly + 3'd1 < cp_poly_max) cp_poly <= cp_poly + 3'd1; + else cp_done <= 1'b1; // region complete end else begin cp_idx <= cp_idx + 8'd1; end @@ -1435,6 +1446,21 @@ module mlkem_top #( endcase end + // Arm E7 (ST_ENC_C2) when V finishes: c2 = byteEncode_dv(Compress_dv(v)). + // Single poly (v at bank_t UPSUM); ct write pointer cp_wa CONTINUES + // from c1_bytes (NOT reset) so c2 lands right after c1. cp_buf/nbits + // are 0 here (c1 ended on a poly/byte boundary) but reset for safety. + if (st == ST_ENC_V && st_next == ST_ENC_C2) begin + cp_poly <= 3'd0; + cp_idx <= 8'd0; + cp_ph <= 3'd0; + cp_buf <= 25'd0; + cp_nbits <= 6'd0; + cp_done <= 1'b0; + cd_valid <= 1'b0; + // cp_wa intentionally preserved (= c1_bytes_rt from C1). + end + // Arm E stage when M finishes if (st == ST_M && st_next == ST_E) begin e_poly <= 3'd0;