feat(enc): Encaps E0 - op_i/msg_i/ek-load scaffold + H(ek)+G(m||H(ek))

Extend mlkem_top with a runtime op_i select (0=KeyGen, 1=Encaps) and the
first Encaps stages, reusing the shared keccak_core and the ST_H multi-block
SHA3-256 machinery:
  ST_ENC_H: H(ek) over preloaded ek_bram (same FSM as KeyGen ST_H)
  ST_ENC_G: (K,r) = G(m||H(ek)) via new 64-byte single-block SHA3-512

- sha3_top_shared: add mode=2'b11 = SHA3-512 over a full 512-bit message
  (g512_pad). Standalone tb_sha3_g512 confirms it byte-exact.
- mlkem_top: new ports op_i, msg_i, ek_in_{we,addr,byte} (ek preload), ss_o,
  dbg_ct_*, dbg_r_o/dbg_hek_o. st widened 4->5 bits; ST_ENC_* states added.
  Renamed message port to msg_i to avoid collision with ST_M counter m_i.
- TB tb_mlkem_enc_katK + gen_encaps_vectors.py (per-byte ek/m/ct/ss vectors).

Verified ss==KAT.ss for K=2/3/4, cases 0-2 (all PASS). KeyGen unaffected
(K=2 c0 still ek==pk, dk==sk byte-exact).
This commit is contained in:
2026-06-29 01:00:47 +08:00
parent c4669480d1
commit 0a8b3dae69
43 changed files with 21956 additions and 30 deletions

View File

@@ -0,0 +1,90 @@
# ML-KEM Encaps 顶层集成 — 实现计划
> 在现有 `mlkem_top`(KeyGen 已 KAT 通过)基础上扩展 Encaps(FIPS 203 Alg 17 + K-PKE.Encrypt Alg 14)。
> 决策(已与用户确认):**(1) 统一进 mlkem_top(加 op_i 选择 + ST_ENC_* 状态,复用同一套叶子 + BRAM 银行);(2) 一上来就做全 K(2/3/4);(3) golden 只做端到端 KAT(ct==KAT.ct, ss==KAT.ss),逐级用 dbg tap 在 bring-up 时对拍。**
## 算法(Encaps,全 K)
输入:ek(=KAT.pk,384K+32 B)、m(=KAT.msg,32 B)。输出:K=ss(32 B)、c=ct(32·(du·K+dv) B)。
1. `h = H(ek)` = SHA3-256(ek),多块。
2. `(K_ss, r) = G(m ‖ h)` = SHA3-512(64 B,单块)。`K_ss = hash[255:0]``r = hash[511:256]`。**ss 即 K_ss,无 KDF/J。**
3. `t̂[i] = byteDecode₁₂(ek[i·384 .. (i+1)·384])`i∈0..K-1`rho = ek[384K .. 384K+32]`
4. `Â[i][j] = SampleNTT(rho‖j‖i)` —— 与 KeyGen ST_A 完全相同。
5. `y[i] = CBD_η1(PRF(r, i))`i=0..K-1(nonce 0..K-1)。
6. `e1[i] = CBD_η2(PRF(r, K+i))`i=0..K-1(nonce K..2K-1)。**η2 恒=2。**
7. `e2 = CBD_η2(PRF(r, 2K))`(nonce 2K)。
8. `ŷ[i] = NTT(y[i])`(mode=0)。
9. `u[i] = INTT(Σⱼ Â[j][i]∘ŷ[j]) + e1[i]`i=0..K-1。**注意转置:用 Â[j][i](slot=j·K+i),不是 KeyGen 的 Â[i][j]。** INTT = ntt_core mode=1(内置 ×3303 缩放)。
10. `mu[c] = Decompress₁(byteDecode₁(m)[c])` = m 第 c bit ? 1665 : 0(无需存储,m 寄存器逐 bit 算)。
11. `v = INTT(Σⱼ t̂[j]∘ŷ[j]) + e2 + mu`
12. `c1 = byteEncode_du(Compress_du(u[i]))` 拼 K 个;`c2 = byteEncode_dv(Compress_dv(v))``ct = c1 ‖ c2`
13. du/dv:K=2/3 → (10,4);K=4 → (11,5)。ct 长度:K=2→768,K=3→1088,K=4→1568。
## 复用与新增
### 直接复用(零改动或仅加 phase mux)
- `u_sha3`(sha3_top_shared):H(ek) 多块 + G 单块 —— 与 KeyGen ST_H/ST_G 同。
- 共享 `u_keccak`:扩 sel_* 4-way mux,加 Encaps phase 选择。
- `u_snt`(sample_ntt):A 再生 —— 与 ST_A 同。
- `u_cbd`(sample_cbd):y/e1/e2 —— **新增 η2=2 驱动**(eta_i 现仅接 eta1_rt;Encaps 的 e1/e2 phase 驱动 2)。
- `u_ntt`(ntt_core):ŷ 用 mode=0;u/v 的 INTT 用 **mode=1**(缩放已内置)。
- `u_pmul`(poly_mul):点乘 Â[j][i]∘ŷ[j] 与 t̂[j]∘ŷ[j];累加在顶层逐系数 modQ(同 ST_M)。
- 3 个系数银行 bank_a/bank_se/bank_t:**容量够,见存储编排**,只加 Encaps 的读写 phase mux。
### 新增 RTL
- `byteDecode₁₂`:ek 3 字节 → 2 系数。`c0=b0|((b1&0xF)<<8); c1=(b1>>4)|(b2<<4)`。组合逻辑,流式写 bank_t。
- `Compress` 实例:实例化现有 `comp_decomp_sync`(mode=0,d=du/dv),top 当前未用。
- `byteEncode_d` 通用位打包器(d∈{4,5,10,11}):把 d-bit 压缩值 LSB-first 流式打包成字节写 ct_bram。**新增**(现有 byteEncode12 是 2→3 硬编码,不通用)。
- `mu` 流内生成:INTT v 输出时按 m 寄存器对应 bit 加 1665。
- `ct_bram`(sd_bram W=8 D=2048 A=11):ct 字节缓冲(≤1568 B)。
- ek 载入路径:`m_i[255:0]` 端口 + ek 流式载入接口(valid/ready 写入 ek_bram),Encaps 前预填。
- 输出:`ss_o`(寄存器 + dbg tap);ct 经 dbg tap 读 ct_bram(对齐现有"无流式输出、靠 readback"的风格)。
- `op_i`(KeyGen/Encaps 选择) + 新 FSM 状态。
## 存储编排(关键 — 全 K 用现有 28 slot 银行不扩容)
- bank_a (D=4096, 16 slot):Â[i][j],K=4 用满 16。
- bank_se (D=2048, 8 slot):y_hat[0..K-1] + e1[0..K-1] = 2K slot(K=4→8,刚好用满)。
- bank_t (D=1024, 4 slot):**E0 先放 t̂[0..K-1](从 ek 解码),也用作 e2 的临时家**。
- **e2 落点**:e2 只在算 v 时需要,算 u 时 bank_t 空闲 → e2 暂存 bank_t[K..](K=2/3 有空 slot)。K=4 时 bank_t 4 slot 被 t̂ 占满 → e2 先存 bank_se 某 slot(算 u 阶段 e1 仍在用,故 e2 需独立 slot)。
- **解法(全 K 统一,避免分支)**:算 u 时 t̂ 尚未需要 → **先做 u(用 A+y_hat+e1),u 全部算完压缩进 ct 后,e1 已用完** → 复用 bank_se 的 e1 区放 e2,再从 ek 解码 t̂ 进 bank_t 算 v。峰值 slot = 16(A)+ 2K(se)+ 0(t 尚未填)= K=4 时 24 ≤ 28。✅
- 时序:E_A(填 A)→ E_CBD(y/e1/e2... e2 先丢 bank_t[0])→ E_NTT(y→y_hat)→ E_U(算 u[i],压缩写 ct c1 区)→ E_V_PREP(t̂ 解码进 bank_t,e2 从 bank_t[0] 搬到 bank_se 空 slot 或就地)→ E_V(算 v,+e2+mu,压缩写 ct c2 区)。
- **简化**:e2 在 E_CBD 阶段先存 bank_se 的 e1 区**之后**的 slot 不够(K=4)。改为 e2 存 bank_t[0](此时 t̂ 未填),E_V_PREP 时 t̂ 从 ek 流式解码覆盖 bank_t,但 e2 要先读出暂存(单 poly 256×12b 可进一个小 reg 阵列,或搬到 bank_se 算完 u 后的空位)。bring-up 时定稿,以 dbg tap 验证。
## 运行时参数(新增)
```
eta2_rt = 2'd2; // 恒定
du_rt = (k==4) ? 5'd11 : 5'd10;
dv_rt = (k==4) ? 5'd5 : 5'd4;
c1_bytes_rt = 32*du_rt*k_r; // K2:640 K3:960 K4:1408
c2_bytes_rt = 32*dv_rt; // K2:128 K3:128 K4:160
ct_bytes_rt = c1_bytes_rt + c2_bytes_rt; // 768 / 1088 / 1568
```
## 顶层接口新增
- `op_i` (1b):0=KeyGen(现有),1=Encaps。在 start_i 锁存为 op_r。
- `m_i [255:0]`:Encaps 消息(byte0 在低)。
- ek 载入:`ek_in_valid_i/ek_in_byte_i[7:0]/ek_in_ready_o`(或复用 dbg 写口),Encaps 前把 ek 流进 ek_bram。
- `ss_o [255:0]` + `done_o`(复用);ct 经 `dbg_ct_idx_i[10:0] → dbg_ct_o[7:0]` readback。
## 实现阶段(逐阶段 KAT/dbg 对拍)
- **E0 — 脚手架 + ek 载入 + H/G**:加 op_i/m_i/ek 载入,ST_ENC_LOAD→ST_ENC_H(H(ek))→ST_ENC_G(G(m‖h))。dbg 验证 ss=K_ss、r(对 ml-kem-r logging::Encaps 的 K/r/H(ek))。
- **E1 — A 再生 + t̂ 解码**:ST_ENC_A 复用 snt 写 bank_a;byteDecode12 把 ek 解码进 bank_t。dbg 读系数对 KeyGen golden 的 Ahat / 对 KAT pk 解码的 t̂。
- **E2 — y/e1/e2 采样 (η1/η2)**:ST_ENC_C,nonce 0..2K,eta 在 e1/e2 切 2。dbg 验证 y/e1/e2(对 ml-kem-r)。
- **E3 — ŷ = NTT(y)**:ST_ENC_N,mode=0,就地。dbg 对 y_hat。
- **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 + 通用打包器写 ct_bram c1 区。dbg 对 ct[0..c1_bytes]==KAT.ct 前缀。
- **E6 — v = INTT(Σ t̂[j]∘ŷ[j]) + e2 + mu**:ST_ENC_V,mu 流内由 m bit 生成。dbg 对 v。
- **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。
## 验证
- 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。
- bring-up 中间对拍:`cargo run --example logging`(已有 Encaps log::debug,输出 K/r/H(ek)/|c|),或临时加 dump_encaps.rs 出 256-coeff 中间量(仅工作树,不提交 ml-kem-r)。
- XSIM 环境同 KeyGen:`source settings64.sh; export LD_PRELOAD=libtinfo.so.5; rm -rf xsim.dir .Xil`
## 风险 / 注意
- **转置**:Encrypt 用 Â[j][i],KeyGen 用 Â[i][j]。slot = j*K+i(列优先)。最易错,E4 单列 dbg 对拍。
- **G 字节序**:KeyGen G 输入 d 原序无翻转(keygen_plan 已确认);但 xcheck 发现 KAT 文件 d/z 存的是翻转序。**m 喂入字节序需 E0 用 KAT.msg 实测确认**(很可能与 d 同约定)。
- **INTT 缩放**:ntt_core mode=1 已内置 ×3303,不要再缩放。
- **e2 落点(K=4)**:见存储编排,bring-up 定稿,dbg 验证不踩 t̂/e1。
- **byteEncode_d 通用打包**:d=10/11 跨字节,位序 LSB-first(同 byte_encode)。新写、单独 TB 或 E5 dbg 即验。
- **共享 keccak 4-way mux**:加 Encaps phase 的 sel_ + kc_valid_o 门控,勿与 KeyGen phase 冲突(op_r 已区分)。

View File

@@ -0,0 +1,50 @@
// tb_sha3_g512.v - isolate sha3_top_shared mode=11 (64-byte SHA3-512).
`timescale 1ns/1ps
module tb_sha3_g512;
reg clk=0, rst_n=0;
reg [1:0] mode;
reg [511:0] data_i;
reg valid_i, ready_i;
wire ready_o, valid_o;
wire [511:0] hash_o;
// shared keccak
wire [1599:0] kc_state_i, kc_state_o;
wire kc_valid_i, kc_valid_o, kc_ready_o, kc_ready_i;
sha3_top_shared u_sha3 (
.clk(clk), .rst_n(rst_n), .mode(mode), .data_i(data_i),
.valid_i(valid_i), .ready_o(ready_o), .hash_o(hash_o), .valid_o(valid_o),
.ready_i(ready_i),
.mb_en(1'b0), .mb_block_i(1088'd0), .mb_valid_i(1'b0), .mb_last_i(1'b0), .mb_ready_o(),
.kc_state_o(kc_state_o), .kc_valid_o(kc_valid_o), .kc_ready_o(kc_ready_o),
.kc_state_i(kc_state_i), .kc_valid_i(kc_valid_i), .kc_ready_i(kc_ready_i)
);
keccak_core #(.ROUNDS(24)) u_kc (
.clk(clk), .rst_n(rst_n), .state_i(kc_state_i), .valid_i(kc_valid_i),
.ready_o(kc_ready_o), .state_o(kc_state_o), .valid_o(kc_valid_o), .ready_i(1'b1)
);
always #5 clk=~clk;
integer j;
initial begin
// m || hek (byte0 in [7:0]); m=20a7..ffff, hek=ca87..aac6
// build little-endian-in-register: byte0 at [7:0]
data_i = 512'd0;
// m bytes 0..31
data_i[ 0 +: 256] = 256'hffff80fc331b2ca1904ccf554ed114bf99f6de44b92082c36c49700fe1b7a720;
// hek bytes 32..63
data_i[256 +: 256] = 256'hc6aa5100ed06d0c2db8cc3d1a36a62055b182f8d51ea71602501857e7c5d87ca;
mode=2'b11; valid_i=0; ready_i=1;
rst_n=0; repeat(4) @(posedge clk); rst_n=1; @(posedge clk);
valid_i=1; @(posedge clk);
while(!(valid_i && ready_o)) @(posedge clk);
valid_i=0;
while(!valid_o) @(posedge clk);
$write("K_ss = "); for(j=0;j<32;j=j+1) $write("%02x", hash_o[8*j +: 8]); $write("\n");
$write("r = "); for(j=0;j<32;j=j+1) $write("%02x", hash_o[256+8*j +: 8]); $write("\n");
$display("want K_ss = 2b5c52ee72946331983ba050be0f435055c0547901e03559b356517889ea27c5");
$display("want r = 08cd245f59e6cb6e3687c6632749b6c0737d183701f0b0492b843358893c6a24");
$finish;
end
initial begin #100000; $display("timeout"); $finish; end
endmodule

View File

@@ -63,6 +63,7 @@ module sha3_top_shared (
// Absorb state: message || suffix || pad10*1 into rate bits
// ================================================================
wire [575:0] g_pad;
wire [575:0] g512_pad;
wire [1087:0] h_pad;
wire [1087:0] j_pad;
@@ -70,12 +71,15 @@ module sha3_top_shared (
assign h_pad = {1'b1, {828{1'b0}}, 1'b1, 2'b10, data_i[255:0]};
// J: SHAKE suffix is "1111" all ones, order irrelevant
assign j_pad = {1'b1, {570{1'b0}}, 1'b1, 4'b1111, data_i[511:0]};
// mode 11 = G over a full 64-byte (512-bit) message (Encaps: G(m||H(ek))).
// SHA3-512 rate=576; suffix 01 + pad10*1: 1 + 60 zeros + 1 + 2'b10 + 512 = 576.
assign g512_pad = {1'b1, {60{1'b0}}, 1'b1, 2'b10, data_i[511:0]};
wire [1599:0] absorb_state;
assign absorb_state = (mode == 2'b00) ? {{(1600-576){1'b0}}, g_pad} :
(mode == 2'b01) ? {{(1600-1088){1'b0}}, h_pad} :
(mode == 2'b10) ? {{(1600-1088){1'b0}}, j_pad} :
1600'd0;
{{(1600-576){1'b0}}, g512_pad};
// ================================================================
// Multi-block SHA3-256 absorb FSM (active only when mb_en=1)

View File

@@ -0,0 +1,76 @@
#!/usr/bin/env python3
"""gen_encaps_vectors.py - Emit per-byte Encaps KAT vectors for the XSIM TB.
For ML-KEM-512/768/1024, parse the NIST .rsp and write, per case:
enc_k{K}_c{N}_ek.hex : ek (=pk) bytes, one hex byte per line, byte 0 first
enc_k{K}_c{N}_m.hex : msg bytes, one per line, byte 0 first
enc_k{K}_c{N}_ct.hex : ct bytes, one per line, byte 0 first (golden)
enc_k{K}_c{N}_ss.hex : ss bytes, one per line, byte 0 first (golden)
Per-byte layout removes all endianness ambiguity: the TB preloads ek_bram with
addr i = line i, builds m_i[8*i +: 8] = m byte i, and compares ct/ss byte-wise.
Run: python3 gen_encaps_vectors.py [num_cases]
"""
import os
import re
import sys
ML_KEM_R = os.environ.get("ML_KEM_R", os.path.expanduser("~/Dev/ml-kem-r"))
OUT_DIR = os.path.join(os.path.dirname(__file__), "vectors")
KATS = {2: "kat_MLKEM_512.rsp", 3: "kat_MLKEM_768.rsp", 4: "kat_MLKEM_1024.rsp"}
def parse_kat(path, n):
"""Return list of dicts {count, msg, pk, ct, ss} (hex strings) for first n."""
vecs, cur = [], {}
with open(path) as f:
for line in f:
line = line.strip()
m = re.match(r"^count\s*=\s*(\d+)$", line)
if m:
if cur:
vecs.append(cur)
if len(vecs) >= n:
break
cur = {"count": int(m.group(1))}
continue
# exact-key match so "ct =" doesn't catch "ct_n ="
m = re.match(r"^(msg|pk|ct|ss)\s*=\s*([0-9a-fA-F]+)$", line)
if m and cur:
cur[m.group(1)] = m.group(2).lower()
if cur and len(vecs) < n:
vecs.append(cur)
return vecs
def write_bytes(path, hexstr):
"""Write hex string as one byte per line (byte 0 = first 2 hex chars)."""
with open(path, "w") as f:
for i in range(0, len(hexstr), 2):
f.write(hexstr[i:i + 2] + "\n")
def main():
ncases = int(sys.argv[1]) if len(sys.argv) > 1 else 3
os.makedirs(OUT_DIR, exist_ok=True)
for k, fname in KATS.items():
path = os.path.join(ML_KEM_R, "test_data", fname)
if not os.path.exists(path):
print(f"skip K={k}: {path} not found", file=sys.stderr)
continue
vecs = parse_kat(path, ncases)
for v in vecs:
n = v["count"]
base = os.path.join(OUT_DIR, f"enc_k{k}_c{n}")
write_bytes(f"{base}_ek.hex", v["pk"])
write_bytes(f"{base}_m.hex", v["msg"])
write_bytes(f"{base}_ct.hex", v["ct"])
write_bytes(f"{base}_ss.hex", v["ss"])
print(f"K={k}: wrote {len(vecs)} cases "
f"(ek={len(vecs[0]['pk'])//2}B ct={len(vecs[0]['ct'])//2}B)")
if __name__ == "__main__":
main()

View File

@@ -0,0 +1,97 @@
// tb_mlkem_enc_katK_xsim.v - ML-KEM Encaps vs NIST KAT, parametric K (KP) + CASE.
// E0 stage: verify H(ek), G(m||H(ek)) -> (ss=K, r). Preloads ek into ek_bram,
// pulses start with op=1, waits for done, checks ss == KAT.ss and dumps H(ek)/r.
//
// xelab -generic_top KP=2|3|4 ; xsim -testplusarg CASE=n
// ek/m/ct/ss vectors: sync_rtl/top/TB/vectors/enc_k{K}_c{N}_{ek,m,ct,ss}.hex
// (per-byte hex, byte 0 first).
`timescale 1ns/1ps
module tb_mlkem_enc_katK_xsim;
parameter KP = 2;
localparam EKB = 384*KP + 32; // ek (=pk) bytes
localparam CTB = (KP==4) ? 1568 : (32*(10*KP+4)); // ct bytes: K2 768,K3 1088,K4 1568
reg clk=0, rst_n=0, start_i=0;
reg [2:0] k_i;
reg [255:0] d_i, z_i, m_i;
wire busy_o, done_o;
// ek preload port
reg ek_in_we=0; reg [10:0] ek_in_addr=0; reg [7:0] ek_in_byte=0;
wire [255:0] ss_o;
reg [10:0] dbg_ct_idx_i=0; wire [7:0] dbg_ct_o;
reg [3:0] dbg_slot_i=0; reg [7:0] dbg_idx_i=0; wire [11:0] dbg_coeff_o;
reg dbg_byte_sel_i=0; reg [10:0] dbg_byte_idx_i=0; wire [7:0] dbg_byte_o;
reg [11:0] dbg_dk_idx_i=0; wire [7:0] dbg_dk_o;
wire [255:0] dbg_rho_o, dbg_sigma_o, dbg_r_o, dbg_hek_o;
mlkem_top dut (
.clk(clk), .rst_n(rst_n), .k_i(k_i), .op_i(1'b1),
.d_i(d_i), .z_i(z_i), .msg_i(m_i), .start_i(start_i),
.busy_o(busy_o), .done_o(done_o),
.ek_in_we(ek_in_we), .ek_in_addr(ek_in_addr), .ek_in_byte(ek_in_byte),
.ss_o(ss_o), .dbg_ct_idx_i(dbg_ct_idx_i), .dbg_ct_o(dbg_ct_o),
.dbg_slot_i(dbg_slot_i), .dbg_idx_i(dbg_idx_i), .dbg_coeff_o(dbg_coeff_o),
.dbg_byte_sel_i(dbg_byte_sel_i), .dbg_byte_idx_i(dbg_byte_idx_i), .dbg_byte_o(dbg_byte_o),
.dbg_dk_idx_i(dbg_dk_idx_i), .dbg_dk_o(dbg_dk_o),
.dbg_rho_o(dbg_rho_o), .dbg_sigma_o(dbg_sigma_o),
.dbg_r_o(dbg_r_o), .dbg_hek_o(dbg_hek_o)
);
always #5 clk = ~clk;
reg [7:0] ek_b [0:EKB-1];
reg [7:0] m_b [0:31];
reg [7:0] ss_b [0:31];
integer c, i, errors, casenum, j;
reg [8*80-1:0] tag, ekfile, mfile, ssfile;
initial begin
if (!$value$plusargs("CASE=%d", casenum)) casenum = 0;
$sformat(tag, "k%0d", KP);
$sformat(ekfile, "sync_rtl/top/TB/vectors/enc_%0s_c%0d_ek.hex", tag, casenum);
$sformat(mfile, "sync_rtl/top/TB/vectors/enc_%0s_c%0d_m.hex", tag, casenum);
$sformat(ssfile, "sync_rtl/top/TB/vectors/enc_%0s_c%0d_ss.hex", tag, casenum);
$readmemh(ekfile, ek_b);
$readmemh(mfile, m_b);
$readmemh(ssfile, ss_b);
// build m_i: byte i in m_i[8*i +: 8]
m_i = 256'd0;
for (j = 0; j < 32; j = j + 1) m_i[8*j +: 8] = m_b[j];
k_i = KP[2:0];
$display("=== ML-KEM K=%0d Encaps KAT case %0d (E0) ===", KP, casenum);
$write(" m = "); for (j=0;j<32;j=j+1) $write("%02x", m_b[j]); $write("\n");
rst_n=0; repeat(4) @(posedge clk); rst_n=1; @(posedge clk);
// ---- preload ek into ek_bram (1 byte/cycle) ----
for (i = 0; i < EKB; i = i + 1) begin
ek_in_we = 1'b1; ek_in_addr = i[10:0]; ek_in_byte = ek_b[i];
@(posedge clk);
end
ek_in_we = 1'b0; @(posedge clk);
// ---- run Encaps ----
start_i=1; @(posedge clk); start_i=0;
c=0; while(!done_o && c<2000000) begin @(posedge clk); c=c+1; end
if(!done_o) begin $display("FAIL K=%0d case %0d: timeout", KP, casenum); $finish; end
$display("=== Encaps E0 done in %0d cyc ===", c);
$write(" H(ek) = "); for (j=0;j<32;j=j+1) $write("%02x", dbg_hek_o[8*j +: 8]); $write("\n");
$write(" r = "); for (j=0;j<32;j=j+1) $write("%02x", dbg_r_o[8*j +: 8]); $write("\n");
$write(" ss = "); for (j=0;j<32;j=j+1) $write("%02x", ss_o[8*j +: 8]); $write("\n");
// ---- check ss == KAT.ss ----
errors = 0;
for (j = 0; j < 32; j = j + 1)
if (ss_o[8*j +: 8] !== ss_b[j]) begin
if (errors < 8) $display(" SS[%0d] got=%02x exp=%02x", j, ss_o[8*j +: 8], ss_b[j]);
errors = errors + 1;
end
if (errors == 0) $display("K=%0d CASE %0d PASS (E0): ss == KAT.ss", KP, casenum);
else $display("K=%0d CASE %0d FAIL (E0): %0d ss mismatches", KP, casenum, errors);
$finish;
end
initial begin #120000000; $display("FAIL: global timeout"); $finish; end
endmodule

View File

@@ -19,12 +19,16 @@ module tb_mlkem_kg_katK_xsim;
// KMAX defaults to 4 (worst-case sizing); KP selects the runtime k value.
mlkem_top dut (
.clk(clk), .rst_n(rst_n), .k_i(k_i), .d_i(d_i), .z_i(z_i), .start_i(start_i),
.clk(clk), .rst_n(rst_n), .k_i(k_i), .op_i(1'b0),
.d_i(d_i), .z_i(z_i), .msg_i(256'd0), .start_i(start_i),
.busy_o(busy_o), .done_o(done_o),
.ek_in_we(1'b0), .ek_in_addr(11'd0), .ek_in_byte(8'd0),
.ss_o(), .dbg_ct_idx_i(11'd0), .dbg_ct_o(),
.dbg_slot_i(dbg_slot_i), .dbg_idx_i(dbg_idx_i), .dbg_coeff_o(dbg_coeff_o),
.dbg_byte_sel_i(dbg_byte_sel_i), .dbg_byte_idx_i(dbg_byte_idx_i), .dbg_byte_o(dbg_byte_o),
.dbg_dk_idx_i(dbg_dk_idx_i), .dbg_dk_o(dbg_dk_o),
.dbg_rho_o(dbg_rho_o), .dbg_sigma_o(dbg_sigma_o)
.dbg_rho_o(dbg_rho_o), .dbg_sigma_o(dbg_sigma_o),
.dbg_r_o(), .dbg_hek_o()
);
always #5 clk = ~clk;

View File

@@ -0,0 +1,768 @@
91
7e
59
d1
e1
82
a2
96
48
a6
19
55
a1
54
b3
04
a8
42
f4
58
eb
a5
93
f3
06
5f
b7
d7
44
fb
a2
4f
a3
a0
d5
38
85
4e
32
84
4d
b2
48
d4
81
00
c3
89
c4
c4
b3
ca
6c
23
4f
2f
61
a6
79
32
ae
ee
80
8d
bf
e9
8d
89
6d
1e
96
68
82
09
65
c6
34
3b
bb
4a
4d
e6
4b
7a
99
fb
53
b5
26
4e
31
99
d5
87
ff
a5
e0
47
9f
6b
73
86
b2
3c
b9
c2
43
fa
6f
54
57
fc
97
db
c9
6d
cc
03
9b
d6
c4
e7
e8
5a
90
b5
c5
23
24
16
43
4e
a9
42
cc
71
13
4b
b9
a2
f4
99
1e
05
95
e3
48
7f
ea
ba
c4
79
ca
b9
51
0d
57
b8
62
50
ee
7c
96
a6
7a
9f
2c
61
21
f7
da
a0
2c
5c
fa
3b
23
b0
c7
32
c2
b1
56
94
e1
cc
ba
c8
2b
78
7c
f2
11
8d
08
42
5c
bb
a1
21
88
22
17
94
0c
89
4c
be
47
aa
e2
54
1f
5c
6e
0b
d1
ba
ab
31
fd
2b
2e
cb
34
b9
e6
93
f6
fc
0f
de
ea
90
67
16
d0
88
18
f5
1d
f0
c4
69
40
29
e8
c3
23
4b
e0
b6
24
71
b2
76
9d
9a
c4
ce
b9
47
60
23
80
f3
a4
97
b2
a0
1f
f7
b8
74
6c
1c
7a
e8
19
83
e0
16
bd
65
c7
cd
17
62
c1
91
85
7e
41
c2
32
19
81
f6
c7
aa
8d
7a
fa
aa
9f
a0
b5
57
ec
1e
c1
28
5f
8b
4d
6c
78
8a
ff
15
9e
a5
86
2e
81
4d
08
25
c8
83
4b
7c
a4
5d
b7
c8
ed
a3
fa
6a
6a
7b
8a
40
51
95
29
bf
74
b6
a0
f8
0c
48
49
17
f4
0c
10
7d
18
2b
ce
3e
34
4f
d3
6d
1b
37
7a
98
2f
6b
f2
36
1b
8d
66
28
75
c1
66
dc
ca
73
24
0b
86
df
5e
e0
fa
42
31
b3
56
f0
cd
d6
b3
17
0f
f8
91
13
db
0b
1b
9d
d2
64
33
57
83
22
2c
be
7b
e8
44
64
2b
0b
19
88
d1
8b
26
8d
a6
f7
8c
c9
44
f4
3b
82
71
43
c4
dc
5e
e1
f0
d6
58
b8
7b
9c
00
86
2f
26
3d
6f
57
98
fc
82
e8
be
07
9a
11
08
de
fd
d4
c7
86
3c
31
47
e9
bd
f0
fb
c8
4d
eb
a2
15
fd
fb
9c
a4
23
7a
84
19
b2
6f
de
ff
63
a7
f9
5c
44
2b
ce
67
96
6e
be
6b
5f
9a
f1
fd
2a
7c
be
d6
48
ea
72
76
db
74
2d
ac
3e
47
0f
6c
c7
41
d8
2e
77
e9
c0
dd
01
9b
54
58
17
75
51
97
5b
59
63
3b
e6
7b
35
c0
d4
30
b7
25
52
4d
ea
fd
eb
a6
d3
64
3c
5c
82
eb
b5
24
79
66
9c
41
1b
b5
6b
39
7f
8d
37
b8
f7
fa
4c
ed
03
16
54
6c
aa
ee
41
9c
0e
7f
1d
96
a0
ec
61
4c
6c
9c
8e
03
e0
10
03
17
65
12
1f
1e
b8
fd
54
9f
de
04
6a
50
44
3e
ba
b9
fe
a2
e7
8a
29
f4
96
44
55
92
8d
b1
f9
e7
82
0e
53
3c
57
12
98
9b
3a
36
37
ea
00
a1
de
3e
46
9a
80
0c
43
1d
4d
f2
43
c1
dc
7d
6a
89
ba
79
00
d5
2b
78
bc
c1
03
47
4f
9d
2c
21
28
02
5c
31
93
c4
d1
a0
9f
50
90
5a
d2
de
7f
e8
e0
9f
04
7e
0e
72
e6
da
b0
ed
f7
e3
b0
8e
d1
31
6a
cf
85
72
54
b6
c1
fb
f6
4c
e5
82
f2
99
0c
ce
cf
95
05
fd
7e
85
17
99
8a
af
58
3b
e1
aa
64
1e
9b
54
db
b9
1c
a9
d0
70
09
13
96
7f
b0
34
92
01
b5
d6
79
b3
2d

View File

@@ -0,0 +1,800 @@
80
b7
01
00
83
1d
7e
e1
68
f4
73
92
48
31
19
f9
38
63
ec
b0
bc
60
53
12
d3
57
28
87
6b
00
f5
f5
7a
f4
c8
5e
da
97
28
1d
57
43
90
bc
72
56
19
36
68
dc
98
62
53
54
ab
52
b9
bc
e7
81
c5
77
1c
c2
60
99
f6
fb
ac
be
e1
78
4d
19
48
87
73
1f
09
7c
87
e0
82
cb
ea
c5
7d
30
b7
b9
a2
93
87
2c
84
14
66
f2
24
4c
c6
5f
f4
ba
65
3d
67
51
df
a2
20
51
d5
53
36
99
42
c3
1c
29
f9
13
07
af
57
32
92
d6
97
a8
c3
16
12
e0
7b
53
35
cf
08
32
9f
d2
46
bf
2a
32
20
0a
8c
b0
c4
b0
3b
9d
09
c5
ba
1c
39
19
53
9e
92
29
c6
96
92
43
48
28
2f
69
6b
15
25
e5
60
38
06
b4
db
61
8b
3d
c8
63
0c
b2
86
e4
31
17
21
bc
50
31
f2
57
aa
92
5b
f8
09
60
44
5a
a8
d8
e6
97
88
e9
9d
a9
5c
1d
10
cc
3b
e8
58
52
8d
b0
cf
ea
00
3c
74
69
37
80
a6
5a
36
e6
28
43
b3
be
15
40
65
43
d2
2d
83
50
7c
87
b2
78
67
92
c8
0f
a5
62
84
54
a3
10
39
4d
da
4c
2a
b5
94
93
85
02
57
d6
d3
bc
10
71
86
d9
06
27
07
f3
54
e8
1a
1a
10
fc
b7
63
d3
1a
4e
c4
09
aa
24
3e
d2
e5
9b
b1
89
85
5e
b1
65
62
66
bb
37
50
07
85
ea
08
cd
88
c2
22
a8
c5
3a
87
77
ca
b4
2c
9c
23
8a
00
d5
93
69
42
50
26
e9
9d
2a
e6
04
0a
f6
70
90
29
ca
87
89
c8
97
b3
29
fb
a7
48
d6
9c
8a
71
48
c9
5a
bb
0c
71
78
7c
58
f1
2f
21
f2
21
3e
38
92
42
aa
7f
bd
32
0c
12
ac
42
c6
11
60
6c
b8
c1
d1
b4
62
58
86
a1
45
a5
35
bd
65
bd
0d
99
6b
33
0c
16
57
d6
1a
87
e9
04
17
00
32
25
75
98
20
a3
bb
ec
50
5c
da
f1
11
93
22
9a
ae
17
3c
d1
2b
ad
22
a7
a3
0a
76
a9
fa
76
36
c3
d5
aa
48
e1
78
40
12
a0
12
f1
41
68
b7
85
3d
2a
16
f9
0c
a0
2c
c3
3b
20
06
c0
8f
26
c1
5f
ca
29
61
2c
c7
92
e1
cf
02
e1
32
1a
75
a8
10
89
19
86
66
7d
41
48
af
36
e6
5e
1a
59
25
89
02
96
17
73
4c
31
5c
0e
39
b7
51
8c
86
60
cd
c2
65
d0
c8
13
ef
20
a0
43
31
7f
ae
5b
8e
40
54
6e
ec
f7
1d
6c
05
4f
a6
6b
49
40
13
09
be
f3
0f
d5
1a
55
19
75
5d
a9
10
46
89
b2
6f
08
39
15
4c
84
7b
ef
23
65
94
3c
02
49
48
3a
ad
9a
73
67
c9
80
a3
d1
ce
46
d2
00
8a
15
27
5c
a3
bc
a4
83
4e
6f
9a
30
36
c0
cd
80
ba
22
8d
34
b9
78
02
44
d5
c0
cb
e9
07
c6
fb
43
82
4a
9c
07
46
47
16
39
cb
86
18
8c
60
c3
b2
53
21
5c
c9
16
92
bc
d1
49
3d
6a
44
4d
53
64
46
e0
29
68
13
80
01
cc
28
7e
0e
51
8f
3a
a2
5d
8c
3b
95
b3
c3
5c
59
f4
81
9d
48
49
76
02
9a
f0
5a
26
3c
35
33
6b
26
1f
77
b2
cd
c2
93
89
f0
84
c0
4c
bb
37
0c
48
56
2b
5b
50
77
1c
0f
b8
16
90
8e
19
8d
d6
d5
3f
76
e4
8b
f0
42
c2
b1
f8
2a
3a
40
b8
da
f5
1f
46
d7
6f
a4
bc
c9
d3
c8
94
ff
85
0c
0f
aa
32
e0
ac
33
90
1a
4c
79
13
9f
5f
20
41
7e
c1
a1
90
60
70
44
f1
8b
b8
24
cf
35
0c
78
ab
3d
df
3c
2c
86
ca
55
43
f7
15

View File

@@ -0,0 +1,32 @@
20
a7
b7
e1
0f
70
49
6c
c3
82
20
b9
44
de
f6
99
bf
14
d1
4e
55
cf
4c
90
a1
2c
1b
33
fc
80
ff
ff

View File

@@ -0,0 +1,32 @@
2b
5c
52
ee
72
94
63
31
98
3b
a0
50
be
0f
43
50
55
c0
54
79
01
e0
35
59
b3
56
51
78
89
ea
27
c5

View File

@@ -0,0 +1,768 @@
1c
43
de
77
0e
9c
d5
98
46
b6
f8
1f
9c
61
cc
d8
37
97
8e
26
d3
2c
33
0d
50
9d
8e
29
d9
e7
fb
1a
4f
ba
52
10
1e
48
88
0d
80
61
6e
53
3a
ac
59
1f
eb
d3
93
cb
b1
28
2d
e1
53
56
0d
43
73
cb
88
2c
66
d3
07
32
95
97
91
0e
d3
ff
10
58
40
96
4a
64
0b
c7
62
e6
32
9b
9f
3b
a4
1a
9e
c3
1e
04
c8
c9
ef
ec
96
35
e0
a9
b4
56
f6
19
f1
06
58
be
d3
6a
13
3c
ed
0c
23
0b
3c
fd
d8
07
37
75
d7
59
49
2a
7e
3b
e0
ce
27
72
5e
b7
c2
47
4c
56
8d
03
92
43
fe
81
0b
31
db
2f
ce
8e
e4
03
73
06
a5
a9
bb
ee
1b
a7
45
c2
b4
fc
11
11
65
9f
5d
e9
19
94
1c
89
c8
cc
66
1e
ca
aa
e3
e9
61
0e
ac
66
cd
41
28
e3
d4
bb
5a
bb
38
83
7d
99
6d
b7
e5
c3
1f
d0
92
22
f2
2f
85
4c
25
fa
d8
ae
12
de
d3
e3
37
c3
24
14
68
ee
eb
ec
da
5d
d6
82
e6
e5
7f
51
ad
35
28
58
d0
40
48
a4
70
3a
dc
13
52
63
c4
7b
3a
5d
6e
6c
59
5b
a1
ea
06
02
bd
34
35
3e
19
1b
fd
f3
6d
56
b2
05
39
74
8a
d6
1c
66
c2
50
e0
7f
2e
3c
6d
60
89
44
72
fe
7e
67
7c
35
3f
70
a1
75
f0
4c
60
f6
9d
d9
21
e9
b9
24
9b
d6
29
14
40
58
29
ae
fa
05
2c
92
9c
f2
3a
ba
06
7a
7e
b7
fe
d0
c1
5f
40
1e
f5
08
4a
97
a6
1c
ed
6f
bf
87
91
48
72
31
c0
5f
c2
3a
38
7c
77
c0
31
65
38
ad
09
be
82
b1
0e
e8
f8
d6
a9
d9
79
f6
3a
2e
c2
36
4f
08
67
30
6e
9b
f6
e3
c0
38
84
96
be
c8
ad
16
15
15
b0
16
e9
1b
5a
b4
e4
ee
9c
48
f0
de
49
50
aa
49
6b
3b
ac
3a
bf
a8
6b
9c
be
27
a9
2a
c8
a8
17
65
0b
2c
4c
53
6e
da
1d
2a
b5
81
95
cd
42
3e
0f
4a
fb
5b
ae
71
01
43
88
21
b8
9a
99
20
6b
a5
fd
d9
d0
6c
39
df
d6
f0
6f
ba
8c
71
94
21
3d
0e
86
f8
0e
4e
5a
24
97
7e
fa
ac
f7
eb
c0
2b
02
8c
fc
96
73
de
6c
8a
0c
c6
ae
1e
57
73
50
93
5c
91
2c
e1
d5
6c
67
08
75
c4
d6
be
d8
75
0a
9e
fb
b4
50
79
1c
3d
56
0c
e5
10
21
32
74
f7
d4
b3
b8
8a
d9
96
70
34
75
10
68
58
0a
9d
ed
f0
ab
3c
ad
bb
2f
3b
bf
37
19
ce
73
e9
38
66
6f
2a
7f
e8
2f
10
f0
8d
32
5d
20
3e
c0
b0
58
5b
58
22
16
b7
fc
05
a7
29
14
81
d1
22
27
91
38
5b
64
16
e8
07
e6
97
02
6b
e2
18
c5
19
c1
b2
15
84
2a
fa
0e
58
f9
88
f8
8f
3d
90
cf
6c
f4
21
87
87
6b
db
d7
be
0b
2c
26
1b
0d
d3
6d
16
dd
e6
ed
83
54
bd
74
32
b6
6d
10
bc
ac
cb
69
c8
94
47
78
90
2f
c3
6d
c7
0f
4e
63
85
b9
d5
9a
ec
c6
44
5c
e4
ad
65
bc
c9
58
e8
86
a9
2a
1d
4d
17
5c
f3
54
b4
fe
e8
89
0e
a4
ce
21
f5
e8
31
f6
90
5a
e5
28
db
39
91
83
4a
24
52
f5
91
c3
ea
88
d7
61
2a
59
9a
30
14
fc
8e
10
b3
0d
48
39
8b
b8
99
1c
0f
f1
2a
73
56
64
50
32
53
0d
81
8d
8a
a2
50
0b
25
21
a7
3e
18
59
1e
c6
ef
42
2d
ae
ed
66
d2

View File

@@ -0,0 +1,800 @@
de
e7
89
22
bb
8d
a2
f4
76
4d
ca
45
f8
ea
4c
1f
53
0a
1c
57
1d
3a
d1
92
4b
58
96
98
cb
27
19
e0
69
fd
94
3c
14
52
c5
d1
68
b2
b3
57
b2
65
83
1c
01
9c
b3
55
7a
0b
f7
18
7e
4d
10
8b
f7
70
c7
4d
b4
03
89
cc
71
8f
3b
c1
1c
e3
ab
a8
43
66
ab
24
51
94
b9
41
f8
1b
7a
e7
02
41
9b
b9
8d
73
ba
00
37
83
30
6c
7b
3a
36
77
a5
43
a0
10
0c
31
75
e8
c0
20
ca
26
65
12
90
9c
35
50
c4
f6
5c
0e
a3
e2
4a
47
23
7d
9a
4a
0b
5a
e5
88
f9
83
31
1f
aa
36
e8
91
af
8e
a1
b3
08
c8
31
79
24
61
54
c7
65
ac
5b
5a
15
45
6e
19
33
08
00
28
37
d1
43
54
e0
66
a3
4a
44
09
af
bb
15
08
5a
ad
c9
e2
2b
8d
b5
58
3d
45
b0
a4
ac
72
47
4a
2a
b9
4b
79
2e
53
1a
b0
cb
88
2c
13
a4
13
9a
71
ff
a3
78
3b
53
71
78
f3
4e
11
1b
6c
3f
12
0a
76
d4
cd
ce
c4
61
84
30
35
ac
d2
85
ed
62
7d
8b
d2
b0
b9
97
cc
ce
b4
ad
ae
36
15
a7
3b
ba
0a
7c
7b
b9
b1
33
58
bb
51
ff
8a
57
4d
86
b5
00
02
72
cd
27
0a
0a
1b
24
bb
91
75
06
70
1c
2f
44
1f
c5
b7
9d
d4
51
cb
3c
ac
c7
40
12
7b
d4
11
60
4b
da
c4
64
da
1a
2f
83
91
8c
5b
2f
b6
e5
28
4c
97
2b
93
35
7a
83
78
28
b1
13
26
32
bb
6f
7b
fb
be
c3
d9
47
bd
40
3d
86
99
00
66
db
65
53
72
24
34
c7
c0
e3
99
2f
00
9b
b2
38
f7
a6
b3
e5
25
1e
da
94
3a
77
03
fb
a2
89
aa
20
53
a8
57
79
6c
2c
b9
45
49
4c
0b
a7
91
a9
e3
cb
fa
80
0d
89
88
c8
12
19
97
d9
75
b3
d0
35
95
ad
d6
4a
3e
ac
b8
ba
36
a4
d8
b3
bf
dc
11
b7
d5
62
39
08
7c
90
f3
52
c0
ee
c4
08
57
f0
89
cc
fa
7c
9a
68
3a
1d
e3
cd
8f
f2
8b
22
15
3d
de
23
1e
5f
74
80
5d
68
3e
b8
c3
46
ba
16
c7
fe
68
06
4b
20
59
c8
73
8e
5a
c1
9b
64
d4
34
3f
5c
9d
8b
f9
78
03
02
b0
67
6c
20
2a
21
29
2c
c1
5f
c1
58
9a
69
83
8a
d1
ea
3d
0c
37
06
f8
e1
b0
11
f6
12
99
a5
6e
c2
51
50
51
fb
94
73
4b
30
8a
f3
c3
5e
45
ab
fa
dc
12
8f
e6
98
38
aa
37
08
05
14
d6
c3
74
8c
91
6b
d6
55
08
0a
10
35
ff
1a
76
3e
45
0b
56
86
4c
c2
32
af
ee
53
91
9f
f8
89
a8
20
a7
a6
55
86
31
b8
8f
73
18
c0
33
80
0f
30
7c
19
b2
d0
a8
b6
58
82
d8
77
09
79
a5
70
1a
c6
02
f7
1b
72
a2
6b
82
52
5a
a6
7d
77
c7
5e
b6
87
1d
c0
19
b4
65
58
3a
25
0d
b4
70
49
06
18
7e
8d
fb
3b
70
72
69
88
f9
c5
2c
12
26
6c
63
91
d4
92
6c
b7
99
78
00
7c
a4
6e
f6
08
82
80
6f
fd
59
c6
4e
d7
03
33
98
9a
ac
51
86
99
04
40
b8
9c
3c
d1
36
3b
e4
04
70
6d
bb
63
a5
a0
07
a0
fa
c2
61
a0
28
8f
91
6f
af
0a
a4
e9
44
55
2f
10
80
1a
3a
5d
de
26
33
98
01
5c
c3
52
27
bd
17
11
35
62
51
aa
93
5a
89
bc
2e
3d
2c
08
e1
c9
a8
c1
e4
41
6f
30
3b
b7
3b
31
48
58
ca
4c
21
0c
95
92
83
fc
86
5b
02
77
68
c5
d6
04
7f
d3
6d
bc
bc
4e
df
3e
a5
cc
6a
50
cb
05
2b
cd
31

View File

@@ -0,0 +1,32 @@
01
21
cb
32
ac
d1
87
11
35
cb
34
e2
9c
1a
0e
26
cc
c0
01
b9
39
ea
fa
ac
c2
8f
13
f1
93
8d
bf
91

View File

@@ -0,0 +1,32 @@
63
df
b8
42
bf
76
7f
89
71
78
39
64
2a
9b
92
fc
cf
82
bf
46
a9
f3
9b
a1
a3
29
29
95
f3
f9
34
dc

View File

@@ -0,0 +1,768 @@
55
33
60
42
56
12
0d
54
80
65
76
94
13
b8
2f
b5
ab
d0
01
4e
7e
9f
10
49
2f
f1
5f
d4
5d
c7
82
b2
dc
4c
55
32
5f
cc
57
68
5f
13
4f
f7
e1
23
cf
02
8f
a7
b4
5d
cc
71
7c
f2
d7
b2
74
d8
f2
a8
b7
d1
ac
d6
63
53
a5
63
35
f4
82
ec
3d
04
50
4d
2e
05
2d
ed
28
11
2a
c7
41
0f
a8
e4
7f
0d
7c
5e
b1
d6
a2
9e
94
87
81
db
38
63
76
7b
7e
a8
7a
59
1e
0d
7e
a4
13
0c
1c
9f
a5
e8
14
2d
ab
f7
ee
8c
02
19
1c
df
1b
8c
80
4d
38
e2
b6
e4
44
5c
d8
fd
34
1a
63
ec
00
01
b9
d8
82
9d
4c
8f
5e
53
dc
6e
87
86
df
67
f4
ac
0b
b9
d1
88
f0
64
da
50
c1
fa
b3
96
55
10
a0
48
eb
6b
4a
a7
8a
85
1c
47
b1
06
42
9b
30
f8
a9
a3
b5
46
48
c8
b4
3d
cf
76
19
0b
bc
8c
66
a6
a3
6d
f7
b9
5a
e3
d9
78
4a
dd
87
58
23
5b
29
1c
0a
1d
b1
ba
80
54
12
e8
b9
2c
14
16
fb
e6
4c
42
db
12
38
30
ed
1b
d9
7e
69
e5
22
e4
82
e8
33
25
e9
e8
8a
d9
40
3f
7e
86
d8
8d
53
5e
39
1a
e4
cd
33
0e
e0
58
92
a1
14
e4
43
9b
59
b3
06
02
d9
d9
87
04
aa
55
30
5f
a1
f8
5b
23
d7
c3
b0
f3
06
54
c8
b7
cb
4c
e0
29
85
e6
cb
e9
a8
7c
f0
e9
99
a5
65
41
30
99
fd
fa
f5
77
84
5a
40
7c
d6
c2
ea
5e
de
db
04
ba
43
4d
80
e1
13
24
67
08
88
61
d6
51
99
81
8e
d4
ba
3a
09
5e
06
63
39
f3
53
39
48
77
e3
bf
79
26
28
8c
dd
37
ac
31
cd
b4
99
b5
eb
8c
a0
68
59
be
b5
c6
60
73
0f
a7
93
cf
11
34
7f
bf
ed
73
a8
2e
1d
2b
22
9a
09
59
32
79
ad
04
45
bc
11
b4
f1
64
d0
15
ec
51
41
da
dc
10
d3
22
c9
28
3e
5a
64
11
57
df
e5
3b
da
4c
3c
a7
36
46
99
df
0b
f7
94
d4
68
3f
b1
fd
ec
e8
e3
18
da
3f
f0
28
b6
3a
a7
d9
f5
0b
58
78
53
78
be
79
fc
7e
8a
2b
12
73
a8
d7
87
74
85
33
57
ae
c5
23
ef
7d
92
76
44
b0
7d
3d
0f
28
76
bf
0b
55
d4
59
2e
33
a6
58
c4
61
c9
6c
8b
81
cc
c2
b1
d4
8c
39
29
ad
99
4d
01
5a
e6
dc
4c
98
a7
54
16
76
7c
7b
a1
60
bf
74
91
b4
a0
aa
db
5e
43
a3
97
f6
3c
e1
cf
7f
91
64
79
f7
63
35
0c
54
dc
29
6a
22
31
4b
4f
48
4c
8c
22
e3
64
fc
e5
a6
ca
6e
15
0b
81
02
1b
44
b5
f4
9f
91
49
27
89
64
37
21
3d
0a
bc
ae
a2
80
62
db
28
54
13
8d
0e
0b
ec
84
1e
6b
e8
e0
39
b8
a0
d7
62
e6
0e
c8
b6
41
6d
fe
7c
7c
8e
d9
b7
41
59
08
b8
dd
9a
69
89
50
69
7f
a5
fa
a5
b1
92
6a
54
de
98
6a
00
93
da
97
6c
89
ed
bf
83
71
1a
4b
aa
bc
2c
21
c7
4f
2b
7f
e9
6a
aa
9e
b0
82
dd
66
e3
40
8c
d3
00
02
cd
bd
57
3d
27
f6
6a
80
6e
b0
b3
14
4c
42
7e
07
ef
7a
39
67
f0
4f
0a
43
36
f5
5e
8e
12
c6
3a
67
80
25
18
44
80
22
73
ac
90
1d
4f
6a
55
65
02
db
49
6d
4b
b4
61
09
be
1f
90
b7
ef
a7
6d
34
03
a3
bb
ec
6a
32
e8
e5
cf
4e
db
a3

View File

@@ -0,0 +1,800 @@
f5
e2
4e
4e
14
b6
65
36
65
ca
02
89
a3
72
8c
bb
a2
28
fe
11
67
26
c1
25
1e
99
92
a9
f0
06
09
34
64
5b
26
23
24
ac
b5
c6
b5
7d
26
e3
92
26
e2
2c
f0
c3
3f
7f
f5
a4
d9
79
23
4f
2b
79
49
f5
66
db
66
c3
5b
27
3d
c9
77
8a
64
f2
bc
fe
91
4c
0d
81
52
7d
41
8a
11
25
79
e5
d0
8b
1c
42
95
87
32
34
25
d7
a7
83
51
45
34
57
cd
86
83
45
af
a1
62
18
33
20
02
c9
5d
84
06
c1
49
38
c0
39
69
3d
6c
12
16
af
b9
98
30
99
03
11
09
be
01
93
a1
b6
e1
24
36
10
3f
dd
08
4a
7b
84
38
0a
cc
35
81
78
31
c0
55
8d
4f
80
82
ba
58
b9
17
48
41
a8
40
c2
96
48
53
65
6c
23
27
f7
95
21
32
c6
dd
12
9d
bf
50
23
ce
1b
3a
92
f7
73
6f
c1
95
28
b7
1d
21
a2
7c
3d
7b
74
51
d5
a8
fc
02
a7
86
f7
55
45
d2
45
15
49
bc
95
db
bb
87
72
4f
72
d2
ad
f1
d6
19
b1
b9
43
86
c1
4c
37
12
8c
2c
1c
60
f4
c0
2b
28
cc
53
6c
50
8a
37
0b
a9
a2
f4
61
8c
f1
c6
4f
35
56
84
f2
5a
6e
e9
9d
3f
89
ce
01
36
b4
7d
7b
26
f0
e6
a6
0c
c9
4f
c4
57
38
34
17
2a
3e
fb
84
c9
fb
66
40
1b
76
c9
00
07
e8
f7
b1
9b
51
32
92
13
8b
79
14
7a
36
46
80
db
cb
21
17
62
35
8c
d3
2a
3c
44
72
0d
f6
ba
22
dc
8d
30
19
c0
c4
80
3f
2a
56
7d
9e
bc
6a
5b
c0
12
d1
f6
34
71
98
91
da
13
24
ea
f2
28
37
92
64
b3
da
95
5d
aa
3a
2f
b8
83
68
94
c8
e8
00
06
78
21
41
25
7c
25
4d
90
5a
23
8a
6b
41
ac
3c
76
cb
87
ea
34
27
28
ea
6e
48
bc
66
81
68
42
ff
24
55
e8
5c
67
f8
c4
88
74
f4
69
bd
a5
6f
dd
83
a5
7d
d4
07
67
98
a3
d8
54
ce
40
bc
c6
b3
49
a1
98
fc
6f
a4
71
3c
94
79
49
46
e2
49
20
17
79
70
9a
b7
51
04
59
e2
fa
32
5b
b3
c8
16
b2
af
3d
9c
af
36
fa
5c
21
73
79
cf
eb
79
7a
90
80
fe
23
37
4f
a0
2f
04
7c
77
5c
b4
60
2b
78
59
f7
d7
9c
0a
f7
3f
11
b4
28
47
91
5b
3d
32
46
e1
04
40
31
f6
1b
aa
d9
87
f3
c6
cc
93
28
0b
23
c8
39
0e
48
a2
10
80
92
c7
b4
19
ba
e1
3a
e5
00
67
81
3a
99
b5
da
80
e5
80
05
c6
96
5f
fe
45
3a
e5
73
50
1b
24
21
24
51
52
57
c4
06
27
8c
2f
7f
39
45
74
89
44
45
c6
12
e4
11
4f
0a
60
40
4c
da
97
6f
a6
3b
9a
28
57
81
e0
ab
3d
77
ad
2b
00
2f
1c
88
5e
f7
46
61
02
43
b7
51
ab
4b
ce
a3
88
02
7c
41
06
3b
01
67
91
36
08
73
64
cf
db
13
92
fa
ab
b9
e2
0a
51
77
b9
4b
f5
a3
93
b7
bd
d8
3a
7d
82
34
31
73
b8
78
1b
44
33
44
84
35
4b
71
79
b2
25
aa
be
e9
9f
23
07
95
d1
fb
24
8a
a5
61
5e
85
c0
ad
b4
9b
7f
51
04
71
2b
86
d4
03
4d
45
79
75
d4
1b
7c
88
c0
66
4e
09
1e
47
e9
79
83
03
c3
90
15
83
c1
36
06
ee
d7
88
3a
53
7a
c0
e1
47
06
8b
8c
4a
c0
56
fb
a1
08
ff
10
83
a9
18
6e
c0
5b
33
86
27
a8
dc
e4
3e
5c
41
41
0c
d6
bb
35
a3
30
d2
0b
77
bb
3a
90
34
b4
e3
c4
43
bb
eb
41
b6
69
1f
11
ab
11

View File

@@ -0,0 +1,32 @@
34
b9
61
af
5d
62
54
af
72
c0
d5
0e
70
dd
9b
49
91
15
0c
cc
09
19
2a
a4
6f
19
53
d5
c2
9a
33
ec

View File

@@ -0,0 +1,32 @@
13
5b
97
48
c6
aa
6d
1a
e9
b5
42
0c
72
67
9f
f6
5c
ae
62
2b
8a
7d
f5
4c
d7
65
01
31
b5
fd
77
f8

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,32 @@
20
a7
b7
e1
0f
70
49
6c
c3
82
20
b9
44
de
f6
99
bf
14
d1
4e
55
cf
4c
90
a1
2c
1b
33
fc
80
ff
ff

View File

@@ -0,0 +1,32 @@
b4
08
d5
d1
15
71
3f
0a
93
04
7d
bb
ea
83
2e
43
40
78
76
86
d5
9a
9a
2d
10
6b
d6
62
ba
0a
a0
35

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,32 @@
01
21
cb
32
ac
d1
87
11
35
cb
34
e2
9c
1a
0e
26
cc
c0
01
b9
39
ea
fa
ac
c2
8f
13
f1
93
8d
bf
91

View File

@@ -0,0 +1,32 @@
8c
97
02
42
40
61
11
e2
63
68
ad
87
60
c4
d0
2a
8b
28
d1
7d
13
82
10
ad
c1
27
19
7b
50
96
81
40

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,32 @@
34
b9
61
af
5d
62
54
af
72
c0
d5
0e
70
dd
9b
49
91
15
0c
cc
09
19
2a
a4
6f
19
53
d5
c2
9a
33
ec

View File

@@ -0,0 +1,32 @@
c0
d4
57
64
e3
db
f0
94
8b
91
4d
6f
65
c9
2b
d0
eb
d2
15
56
e5
07
67
53
af
48
df
8f
ff
d6
ba
dc

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,32 @@
20
a7
b7
e1
0f
70
49
6c
c3
82
20
b9
44
de
f6
99
bf
14
d1
4e
55
cf
4c
90
a1
2c
1b
33
fc
80
ff
ff

View File

@@ -0,0 +1,32 @@
23
f2
11
b8
4a
6e
e2
0c
8c
29
f6
e5
31
4c
91
b4
14
e9
40
51
3d
38
0a
dd
17
bd
72
4a
b3
a1
3a
52

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,32 @@
01
21
cb
32
ac
d1
87
11
35
cb
34
e2
9c
1a
0e
26
cc
c0
01
b9
39
ea
fa
ac
c2
8f
13
f1
93
8d
bf
91

View File

@@ -0,0 +1,32 @@
26
22
50
3a
2a
54
4f
35
83
92
90
3e
6f
2a
dc
5a
2d
94
fe
01
05
83
b5
74
a5
4d
9a
7e
6d
22
3a
24

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,32 @@
34
b9
61
af
5d
62
54
af
72
c0
d5
0e
70
dd
9b
49
91
15
0c
cc
09
19
2a
a4
6f
19
53
d5
c2
9a
33
ec

View File

@@ -0,0 +1,32 @@
72
3a
b6
2b
92
58
0b
3d
ef
8c
e8
87
00
1e
ef
e4
d9
05
65
aa
ca
f5
43
0e
e2
aa
b7
de
cf
99
db
76

View File

@@ -28,12 +28,25 @@ module mlkem_top #(
input clk,
input rst_n,
input [2:0] k_i, // RUNTIME ML-KEM param: 2=512, 3=768, 4=1024
input op_i, // 0 = KeyGen, 1 = Encaps (captured at start_i)
input [255:0] d_i, // KeyGen seed d (byte 0 in d_i[7:0])
input [255:0] z_i, // implicit-rejection seed z
input [255:0] msg_i, // Encaps message m (byte 0 in msg_i[7:0])
input start_i, // pulse to begin KeyGen
output busy_o, // high while running
output done_o, // pulse when ek/dk ready
// Encaps ek input: stream ek bytes (=pk) into ek_bram before start_i with
// op_i=1. ek_in_we writes byte ek_in_byte at address ek_in_addr.
input ek_in_we, // write one ek byte (only honored in ST_IDLE for Encaps preload)
input [10:0] ek_in_addr, // ek byte address 0..ek_bytes-1
input [7:0] ek_in_byte, // ek byte value
// Encaps shared secret output (= K), valid at done_o
output [255:0] ss_o,
// Encaps ciphertext readback tap: ct byte by index 0..ct_bytes-1
input [10:0] dbg_ct_idx_i,
output [7:0] dbg_ct_o,
// Debug readback tap: read one stored coefficient by (poly slot, index).
// Lets stage TBs verify intermediates without wide buses.
input [3:0] dbg_slot_i, // poly slot (see localparams below)
@@ -49,7 +62,10 @@ module mlkem_top #(
output [7:0] dbg_dk_o,
// Debug taps for hash outputs
output [255:0] dbg_rho_o,
output [255:0] dbg_sigma_o
output [255:0] dbg_sigma_o,
// Encaps debug taps: r (G high half) and H(ek)
output [255:0] dbg_r_o,
output [255:0] dbg_hek_o
);
localparam Q = `Q; // 3329
@@ -70,6 +86,27 @@ module mlkem_top #(
wire [3:0] h_nblk_rt = (k_r == 3'd2) ? 4'd6 : (k_r == 3'd3) ? 4'd9 : 4'd12;
wire [11:0] h_last_rt = {6'b0, h_nblk_rt} * 12'd136 - 12'd1; // final padded byte index
// ---- Encaps runtime params ----
reg op_r; // 0=KeyGen 1=Encaps (captured at start)
reg [255:0] m_r; // Encaps message m (captured at start)
reg [255:0] ss_r; // Encaps shared secret K (= G output low half)
reg [255:0] r_r; // Encaps PRF seed r (= G output high half)
// FIPS 203: eta2 = 2 for all parameter sets.
wire [1:0] eta2_rt = 2'd2;
// Compression params: (du,dv) = (10,4) for k=2/3, (11,5) for k=4.
wire [4:0] du_rt = (k_r == 3'd4) ? 5'd11 : 5'd10;
wire [4:0] dv_rt = (k_r == 3'd4) ? 5'd5 : 5'd4;
// Ciphertext byte sizes: c1 = 32*du*k, c2 = 32*dv, ct = c1+c2.
wire [11:0] c1_bytes_rt = 12'd32 * {7'b0, du_rt} * {9'b0, k_r}; // 640/960/1408
wire [11:0] c2_bytes_rt = 12'd32 * {7'b0, dv_rt}; // 128/128/160
wire [11:0] ct_bytes_rt = c1_bytes_rt + c2_bytes_rt; // 768/1088/1568
assign ss_o = ss_r;
// ct readback tap (ct_bram added in E5/E7); tied off until then.
/* verilator lint_off UNUSEDSIGNAL */
wire [10:0] dbg_ct_idx_unused = dbg_ct_idx_i;
/* verilator lint_on UNUSEDSIGNAL */
assign dbg_ct_o = 8'd0;
// ================================================================
// Polynomial storage, sized for KMAX (worst case). Runtime k uses a
// sub-range. Slot layout (each slot = 256 coeffs):
@@ -203,10 +240,17 @@ module mlkem_top #(
reg [10:0] ek_wa, dkp_wa;
reg [7:0] ek_wd, dkp_wd;
// ek BRAM write port: KeyGen ST_E drives ek_we/ek_wa/ek_wd; Encaps preloads
// ek from the external ek_in_* port (TB streams ek=pk before start_i). The
// two never overlap (preload happens in ST_IDLE before an Encaps run).
wire ek_we_mux = ek_in_we ? 1'b1 : ek_we;
wire [10:0] ek_wa_mux = ek_in_we ? ek_in_addr : ek_wa;
wire [7:0] ek_wd_mux = ek_in_we ? ek_in_byte : ek_wd;
sd_bram #(.W(8), .D(2048), .A(11)) u_ek_bram (
.clk(clk),
.rd_addr(ek_rd_addr), .rd_data(ek_rd_data),
.wr_en(ek_we), .wr_addr(ek_wa), .wr_data(ek_wd)
.wr_en(ek_we_mux), .wr_addr(ek_wa_mux), .wr_data(ek_wd_mux)
);
sd_bram #(.W(8), .D(2048), .A(11)) u_dkp_bram (
.clk(clk),
@@ -236,17 +280,29 @@ module mlkem_top #(
// ================================================================
// Top-level FSM (built incrementally). Stage 2a: G only.
// ================================================================
localparam ST_IDLE = 4'd0;
localparam ST_G = 4'd1; // run G(d||K), capture rho/sigma
localparam ST_A = 4'd2; // generate A_hat[i][j] via SampleNTT
localparam ST_C = 4'd3; // generate s[i],e[i] via CBD
localparam ST_N = 4'd4; // forward NTT of s[i],e[i] in place
localparam ST_M = 4'd5; // matrix accumulate t_hat = e_hat + sum A o s_hat
localparam ST_E = 4'd6; // byteEncode12 -> ek/dk BRAM
localparam ST_H = 4'd7; // H(ek) via multi-block SHA3-256
localparam ST_DONE = 4'd15;
localparam ST_IDLE = 5'd0;
localparam ST_G = 5'd1; // run G(d||K), capture rho/sigma
localparam ST_A = 5'd2; // generate A_hat[i][j] via SampleNTT
localparam ST_C = 5'd3; // generate s[i],e[i] via CBD
localparam ST_N = 5'd4; // forward NTT of s[i],e[i] in place
localparam ST_M = 5'd5; // matrix accumulate t_hat = e_hat + sum A o s_hat
localparam ST_E = 5'd6; // byteEncode12 -> ek/dk BRAM
localparam ST_H = 5'd7; // H(ek) via multi-block SHA3-256
// ---- Encaps states ----
localparam ST_ENC_LOAD = 5'd8; // ek already preloaded; placeholder/settle
localparam ST_ENC_H = 5'd9; // H(ek) via multi-block SHA3-256 (reuses ST_H logic)
localparam ST_ENC_G = 5'd10; // (K,r) = G(m || H(ek)), 64-byte single block
localparam ST_ENC_A = 5'd11; // regenerate A_hat (transpose used in U)
localparam ST_ENC_TDEC = 5'd12; // byteDecode12: ek -> t_hat (bank_t)
localparam ST_ENC_C = 5'd13; // sample y,e1,e2 via CBD (eta1/eta2)
localparam ST_ENC_N = 5'd14; // forward NTT of y in place
localparam ST_ENC_U = 5'd15; // u = INTT(sum A^T o y_hat) + e1
localparam ST_ENC_C1 = 5'd16; // Compress_du + byteEncode_du -> ct c1
localparam ST_ENC_V = 5'd17; // v = INTT(sum t_hat o y_hat) + e2 + mu
localparam ST_ENC_C2 = 5'd18; // Compress_dv + byteEncode_dv -> ct c2
localparam ST_DONE = 5'd31;
reg [3:0] st, st_next;
reg [4:0] st, st_next;
reg [255:0] rho_r, sigma_r;
// A-generation bookkeeping: explicit i/j counters (avoid runtime divide)
@@ -270,6 +326,8 @@ module mlkem_top #(
assign done_o = (st == ST_DONE);
assign dbg_rho_o = rho_r;
assign dbg_sigma_o = sigma_r;
assign dbg_r_o = r_r;
assign dbg_hek_o = hek_r;
// ---- sha3_top in G mode: data_i = {K_byte, d} (d byte0 in [7:0]) ----
reg sha3_valid;
@@ -277,7 +335,9 @@ module mlkem_top #(
wire [511:0] sha3_hash;
wire sha3_vo;
reg sha3_ack; // consumer ready for hash
wire [511:0] g_data = {248'b0, 5'b0, k_r, d_i}; // data_i[263:256]=k, [255:0]=d
wire [511:0] kg_g_data = {248'b0, 5'b0, k_r, d_i}; // KeyGen G: [263:256]=k, [255:0]=d
wire [511:0] enc_g_data = {hek_r, m_r}; // Encaps G: m || H(ek), 64 bytes
wire [511:0] g_data = (st == ST_ENC_G) ? enc_g_data : kg_g_data;
// ================================================================
// Shared keccak_core + phase mux (3 consumers -> 1 core)
@@ -301,10 +361,12 @@ module mlkem_top #(
wire sha3_kc_ready_i, snt_kc_ready_i, cbd_kc_ready_i; // all 1'b1
/* verilator lint_on UNUSEDSIGNAL */
// phase selects (mutually exclusive)
wire sel_sha3 = (st == ST_G) || (st == ST_H);
wire sel_snt = (st == ST_A);
wire sel_cbd = (st == ST_C);
// phase selects (mutually exclusive). Encaps adds ST_ENC_H/G (sha3),
// ST_ENC_A (snt), ST_ENC_C (cbd).
wire sel_sha3 = (st == ST_G) || (st == ST_H) ||
(st == ST_ENC_H) || (st == ST_ENC_G);
wire sel_snt = (st == ST_A) || (st == ST_ENC_A);
wire sel_cbd = (st == ST_C) || (st == ST_ENC_C);
// gated output-valid: only the active consumer sees kc_valid_o
wire kc_valid_o_sha3 = kc_valid_o & sel_sha3;
@@ -334,10 +396,13 @@ module mlkem_top #(
// multi-block absorb path (mb_en=1). These phases are disjoint in the
// top FSM, so one sha3_top (one keccak_core) is sufficient. mb_en and
// ready_i are muxed by phase; data_i/mode only matter while mb_en=0.
wire sha3_mb_en = (st == ST_H);
wire sha3_mb_en = (st == ST_H) || (st == ST_ENC_H);
// sha3 single-block mode: 2'b00 = G(33B) for KeyGen, 2'b11 = G(64B) for
// Encaps (m||H(ek)). mode only matters when mb_en=0.
wire [1:0] sha3_mode = (st == ST_ENC_G) ? 2'b11 : 2'b00;
sha3_top_shared u_sha3 (
.clk(clk), .rst_n(rst_n),
.mode(2'b00), // G = SHA3-512 (only used when mb_en=0)
.mode(sha3_mode), // G = SHA3-512 (only used when mb_en=0)
.data_i(g_data),
.valid_i(sha3_valid),
.ready_o(sha3_ready),
@@ -408,7 +473,7 @@ module mlkem_top #(
// dbg_dk ek-region takes priority (its loop runs after the ek loop), so the
// single read port serves dbg_byte_o (ek loop) then dbg_dk_o (dk loop).
wire [11:0] dbgdk_ek_off = dbg_dk_idx_i - dk_bytes_rt; // offset within ek region
assign ek_rd_addr = (st == ST_H) ? h_g_addr[10:0] :
assign ek_rd_addr = (st == ST_H || st == ST_ENC_H) ? h_g_addr[10:0] :
dbgdk_in_ek ? dbgdk_ek_off[10:0]
: dbg_byte_idx_i;
// dkp BRAM read-address mux: dbg_byte (sel=1) or dbg_dk (dkp region).
@@ -600,7 +665,7 @@ module mlkem_top #(
always @(*) begin
st_next = st;
case (st)
ST_IDLE: if (start_i) st_next = ST_G;
ST_IDLE: if (start_i) st_next = op_i ? ST_ENC_H : ST_G;
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;
@@ -608,6 +673,9 @@ module mlkem_top #(
ST_M: if (m_i >= k_r) st_next = ST_E;
ST_E: if (e_done) st_next = ST_H;
ST_H: if (h_phase == 2'd3) st_next = ST_DONE;
// ---- Encaps ----
ST_ENC_H: if (h_phase == 2'd3) st_next = ST_ENC_G;
ST_ENC_G: if (sha3_vo) st_next = ST_DONE; // E0: stop here (K,r captured)
ST_DONE: st_next = ST_IDLE;
default: st_next = ST_IDLE;
endcase
@@ -617,6 +685,10 @@ module mlkem_top #(
if (!rst_n) begin
st <= ST_IDLE;
k_r <= 3'd0;
op_r <= 1'b0;
m_r <= 256'd0;
ss_r <= 256'd0;
r_r <= 256'd0;
rho_r <= 256'd0;
sigma_r <= 256'd0;
sha3_valid <= 1'b0;
@@ -680,11 +752,25 @@ module mlkem_top #(
ek_we <= 1'b0;
dkp_we <= 1'b0;
// Kick off G when entering ST_G
// Kick off when entering from IDLE: KeyGen starts G; Encaps captures
// op/m and arms the H(ek) machinery (ST_ENC_H reuses the ST_H FSM).
if (st == ST_IDLE && start_i) begin
k_r <= k_i; // capture runtime ML-KEM param
sha3_valid <= 1'b1;
sha3_ack <= 1'b1;
k_r <= k_i; // capture runtime ML-KEM param
op_r <= op_i;
if (op_i) begin
m_r <= msg_i; // capture Encaps message
// arm H(ek) (same fields the ST_E->ST_H arming sets)
h_blk <= 3'd0;
h_byte <= 8'd0;
h_phase <= 2'd0; // assemble
h_mbvalid<= 1'b0;
h_mblast <= 1'b0;
h_ack <= 1'b1; // ready to consume final digest
h_wb_vld <= 1'b0;
end else begin
sha3_valid <= 1'b1;
sha3_ack <= 1'b1;
end
end
// Drop valid once accepted
if (sha3_valid && sha3_ready) sha3_valid <= 1'b0;
@@ -954,8 +1040,10 @@ module mlkem_top #(
h_wb_vld <= 1'b0; // no pending writeback yet
end
// ---- ST_H: H(ek) via multi-block SHA3-256 (6/9/12 pre-padded blocks) ----
if (st == ST_H) begin
// ---- ST_H / ST_ENC_H: H(ek) via multi-block SHA3-256 ----
// Same logic for KeyGen (ek just written by ST_E) and Encaps (ek
// preloaded into ek_bram by the TB). hek_r captures H(ek) either way.
if (st == ST_H || st == ST_ENC_H) begin
case (h_phase)
// assemble 136 bytes of block h_blk into h_block_r.
// ek BRAM read is registered: present addr for h_byte this
@@ -1005,6 +1093,19 @@ module mlkem_top #(
default: ; // 2'd3 done: hold
endcase
end
// Arm Encaps G when H(ek) finishes: fire the 64-byte single-block
// G(m||H(ek)). hek_r now holds H(ek); enc_g_data = {hek_r, m_r}.
if (st == ST_ENC_H && st_next == ST_ENC_G) begin
sha3_valid <= 1'b1;
sha3_ack <= 1'b1;
end
// Capture (K, r) when Encaps G completes. ss = K = low half, r = high.
if (st == ST_ENC_G && sha3_vo) begin
ss_r <= sha3_hash[255:0]; // K (shared secret) = G bytes 0..31
r_r <= sha3_hash[511:256]; // r = G bytes 32..63
sha3_ack <= 1'b0;
end
end
end