feat(enc): Encaps E7 - c2 = byteEncode_dv(Compress_dv(v)) + end-to-end KAT
Reuses the E5 bit-packer FSM for ST_ENC_C2, generalized over region: - coeff source: cp_coeff_src = bank_t[UPSUM] (v) for C2, bank_se (u) for C1 - bit width: cp_d already = dv_rt for C2 else du_rt - poly count: cp_poly_max = 1 (single v) for C2, K for C1 - ct write pointer cp_wa CONTINUES from c1_bytes into C2 (not reset), so c2 lands right after c1. c1 ends on a whole-byte/poly boundary (256*du/8 integral), so cp_buf/nbits are empty at the C1->C2 handoff. FSM tail: ST_ENC_V -> ST_ENC_C2 -> ST_DONE. TB: verify_e7 compares the full ct (c1||c2, CTB bytes) to KAT.ct byte-exact via the dbg_ct tap. Combined with the E0 ss==KAT.ss check this is the full end-to-end Encaps KAT (ct==KAT.ct && ss==KAT.ss). Verified end-to-end for K=2/3/4, cases 0-2 (K2) / 0-1 (K3,K4): ct==KAT.ct && ss==KAT.ss. ML-KEM Encaps complete. KeyGen unregressed.
This commit is contained in:
@@ -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。
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user