feat(dec): Decaps D0 - op_i widen + dk/c load + parse

Scaffolding for ML-KEM Decaps (FIPS 203 Alg 18):
- op_i widened to 2-bit: 00=KeyGen, 01=Encaps, 10=Decaps (op_r too).
- New ST_DEC_LOAD state (D0: settles to DONE so load/parse is dbg-checkable).
- dk (=sk) streamed via dk_in_*; load logic routes each byte by region:
  [0,384K)->dk_pke (dkp_bram), [384K,768K+32)->ek_pke (ek_bram),
  [768K+32,+32)->H(ek) (hek_r), [768K+64,+32)->z (z_r). Routing uses the
  LIVE k_i input, not start-captured k_r (dk is streamed before start_i).
- c (=ct) streamed via c_in_* into a SEPARATE c_in_bram, so the computed c'
  (ct_bram) can later be compared against original c and J(z||c) can read c.
- New dbg taps: dbg_mprime_o/dbg_kbar_o/dbg_decz_o/dbg_dech_o.

TB: tb_mlkem_dec_katK_xsim verifies dk parse (H(ek), z, ek_pke/dk_pke BRAM
round-trip). gen_decaps_vectors.py emits dec_k{K}_c{N}_{dk,ct,ss,ctn,ssn}.hex
from the NIST KAT. run_tb.sh gains a 'dec' module (mirrors 'enc').

Regression fix: old KeyGen/Encaps TBs didn't connect the new input ports,
floating them to X and corrupting the ek/dkp write muxes -> tied off
dk_in_*/c_in_*/new dbg taps in both.

Verified: dec D0 K=2/3/4 PASS; KeyGen K=2 + Encaps K=2 unregressed.
This commit is contained in:
2026-06-29 15:22:34 +08:00
parent 4091fd0676
commit 030931d4e5
52 changed files with 43220 additions and 27 deletions

View File

@@ -0,0 +1,89 @@
# ML-KEM Decaps 顶层集成 — 实现计划
> 在 `mlkem_top`(KeyGen + Encaps 均 KAT 通过)基础上扩展 Decaps(FIPS 203 Alg 18 + K-PKE.Decrypt Alg 15)。
> 决策(已与用户确认):**(1) 实现完整隐式拒绝路径(J + 常量时间 c'==c 比较 + K̄/K' mux),并用 corrupted-ct(KAT ct_n/ss_n)验证拒绝路径;(2) 逐级 dbg tap 对拍 ml-kem-r golden(m'/w/u_hat 等)+ 端到端 ss==KAT.ss。**
## 算法(Decaps_internal,全 K)
输入:dk(=KAT.sk,768K+96 B)、c(=KAT.ct,32(du·K+dv) B)。输出:K'=ss(32 B)。
1. 解析 dk:`dk_pke = dk[0:384K]`(s_hat)、`ek_pke = dk[384K:768K+32]``h = dk[768K+32:768K+64]``z = dk[768K+64:768K+96]`
2. `m' = K-PKE.Decrypt(dk_pke, c)`(**唯一全新数据通路**,见下)。
3. `(K', r') = G(m' ‖ h)` = SHA3-512(64 B 单块)。**与 Encaps G 完全相同**(mode=11)。
4. `K̄ = J(z ‖ c)` = SHAKE-256(z‖c, 32 B 输出)。**多块,块数 6/9/12 = 与 H(ek) 相同**。
5. `c' = K-PKE.Encrypt(ek_pke, m', r')`。**这就是 Encaps E1E7**(A 再生 + 采样 y/e1/e2(seed=r') + NTT + u + v + 压缩成 ct')。**已 KAT 通过,零改动复用**。
6. 常量时间比较 `c' == c`(逐字节 XOR 累加)。
7. `K' = (c'==c) ? K' : K̄`(隐式拒绝 mux)。返回 K'=ss。
### K-PKE.Decrypt(Alg 15,全新)
- 解析 c:`c1 = c[0:32·du·K]``c2 = c[32·du·K : +32·dv]`
- `u'[i] = Decompress_du(byteDecode_du(c1[i]))`,i=0..K-1(**新:通用 byteDecode_d 解包器 + comp_decomp mode=1 解压**)。
- `v' = Decompress_dv(byteDecode_dv(c2))`(1 poly)。
- `s_hat[i] = byteDecode12(dk_pke[i·384..])`,i=0..K-1(**复用 Encaps TDEC 机器**,5-cyc/triple)。
- `u_hat[i] = NTT(u'[i])`(mode=0,K polys,**复用 ST_ENC_N 的 ntt_core**)。
- `w = v' INTT(Σⱼ s_hat[j]∘u_hat[j])` mod Q(**复用 Encaps V 机器(MAC+INTT),ADD 改 SUB,加数 v' 替 e2+mu**)。
- `m' = byteEncode₁(Compress₁(w))` = 32 B(**复用 C1/C2 打包器,d=1**)。
## 复用与新增
### 直接复用(零或极小改动)
- **整个 Encaps E1E7**(c' = Encrypt):A 再生、CBD(seed=r')、NTT、U、V、C1、C2 → ct_bram。完全复用,只是 seed 来自 r'(已是 r_r 路径)、ek 来自 ek_pke(已在 ek_bram)、m 来自 m'(新:m' 寄存器替 m_r)。
- `u_sha3` G(mode=11,m'‖h):与 Encaps G 同,只是 enc_g_data 高半改 h(来自 dk 而非 H(ek) 重算)。
- `u_keccak` 共享核 + J 多块:复用 H 的 mb 路径,仅末块 pad 常量 0x06→0x1F(SHAKE)。
- `ntt_core`:u_hat fwd NTT(mode=0,复用 ST_ENC_N);decrypt 的 INTT(mode=1,复用 V 的 u_intt 路径)。
- `poly_mul` / 3 银行 / comp_decomp:加 Decaps phase mux。
- Encaps TDEC(byteDecode12 → bank_a):decrypt 的 s_hat 解码复用(改落 dk_pke 源 + 目标 bank)。
### 新增 RTL
- **`byteDecode_d` 通用解包器**(d∈{4,5,10,11}):c 字节流 → d-bit 系数,LSB-first(byteEncode_d 的逆)。新写,流式读 ct/c_bram,写银行。
- **comp_decomp mode=1 解压**:实例已有(E5/E7 用 mode=0);Decaps decode-decompress 用 mode=1,d=du/dv。加 phase。
- **dk 载入路径**:`dk_in_*`(或复用 ek_in_* 加宽地址),Decaps 前流入 dk_bram。顶层解析:dk_pke→bank/解码、ek_pke→ek_bram、h/z→寄存器。
- **c 载入路径**:`c_in_*` 流入 ct_bram 的「输入 c」区(注意 c' 也写 ct_bram → 需独立 c_in_bram 或分区,见存储编排)。
- **J 多块组装**:z(reg)‖c(c_in_bram)→ 136B 块,末块 0x1F pad。新地址逻辑(类 H 的 h_g_addr)。
- **w = v' INTT(...)**:V 机器 ADD 子相改 SUB(`(v' psum) mod Q`,负则 +Q),加数源 v'(银行)替 e2(bank_a)+mu。
- **m' Compress₁+byteEncode₁**:打包器加 d=1 路径(每系数 1 bit,256 bit = 32 B)。m' 落 32-bit 寄存器(供 G 与 c'-Encrypt)。
- **常量时间比较 c'==c**:逐字节读 ct_bram(c')与 c_in_bram(c),XOR 累加进 1-bit `ct_ne_r`(全程扫完,不早退)。
- **隐式拒绝 mux**:`ss_r = ct_ne_r ? k_bar_r : kprime_r`
- **op_i 加宽 2-bit**:00=KeyGen,01=Encaps,10=Decaps。新增 ST_DEC_* 状态。
## 存储编排(关键)
- **c 输入 vs c' 输出冲突**:Decaps 既要保留输入 c(给 J 和最终比较),又要算 c'(写 ct_bram)。**解法:c 输入存独立 `c_in_bram`(sd_bram W=8 D=2048);c' 仍写 ct_bram。** 比较阶段两个 bram 各一读口,无冲突。J 从 c_in_bram 读。
- **Decrypt 阶段银行**:s_hat(K)、u'(K)、u_hat(=NTT(u') 就地 K)、v'(1)、psum/w(1)。
- u' → bank_se rel 0..K-1;NTT 就地得 u_hat。
- s_hat → bank_a slot j·K(复用 TDEC 落点 + V-MAC 寻址)。
- v' → bank_t rel 0(或 bank_a 空 slot)。
- psum(Σ s∘u_hat)→ bank_t[UPSUM];INTT 就地;SUB 读 v'(bank_t rel 0)+ psum(bank_t UPSUM)→ 单口冲突 → v' 改存 bank_a 某 slot(类 e2 搬迁)或 bank_se 空区。**bring-up 定稿,dbg 验证。**
- m' 算完 → 32-bit 寄存器。Decrypt 阶段结束,银行清空。
- **Encrypt(c')阶段**:Decrypt 已出 m'(reg),银行重新被 Encaps E1E7 占用,无并发。
## 顶层接口新增
- `op_i [1:0]`:00/01/10。start_i 锁存 op_r[1:0]。
- `dk_in_*`(we/addr/byte):dk 流入。`c_in_*`(we/addr/byte):c 流入。
- `ss_o`(复用):Decaps 输出 K'。
- dbg taps:m'(`dbg_mprime_o[255:0]`)、w/u_hat 经现有 dbg_coeff_o、k_bar(`dbg_kbar_o`)。
## 实现阶段(逐阶段 dbg/KAT 对拍)
- **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=1。dbg 对 u'/v'(ml-kem-r golden,新 dump_decaps)。
- **D2 — s_hat 解码 + u_hat = NTT(u')**:TDEC 复用(dk_pke 源)、ntt_core fwd。dbg 对 s_hat / u_hat。
- **D3 — w = v' INTT(Σ s∘u_hat)**:V 机器 SUB 变体。dbg 对 w。
- **D4 — m' = byteEncode₁(Compress₁(w))**:打包器 d=1。dbg 对 m'(== KAT 解密的 m')。
- **D5 — G(m'‖h) → (K',r') + J(z‖c) → K̄**:G 复用、J 多块(0x1F pad)。dbg 对 K'/r'/K̄。
- **D6 — c' = Encrypt(ek_pke,m',r')**:复用 Encaps E1E7 写 ct_bram。dbg 对 c'==KAT.ct(有效 ct 时)。
- **D7 — 比较 + 拒绝 mux + 端到端 KAT**:c'==c 常量时间比较,ss=mux。干净 TB:
- 有效 ct(KAT.ct):ss==KAT.ss(c'==c → K')。
- 损坏 ct(KAT ct_n / ss_n):ss==KAT.ss_n(c'≠c → K̄)。
- K=2/3/4 各 count=0..N。
## 验证
-`dump_decaps.rs`(ml-kem-r examples,工作树):出 D1D6 中间量(u'/v'/s_hat/u_hat/w/m'/K'/K̄)256-coeff / 32B golden。
- 新 TB `tb_mlkem_dec_katK_xsim.v`:从 KAT 取 sk(→dk)、ct、ss、ct_n、ss_n,载入 dk/c,跑 Decaps,比 ss。
- runner:`./run_tb.sh dec [K] [CASE]`(并入 run_tb,复用 top tcl + dec TB)。
- XSIM 环境同前:`source settings64.sh; export LD_PRELOAD=libtinfo.so.5; rm -rf xsim.dir .Xil`
## 风险 / 注意
- **c 输入 / c' 输出共存**:必须分两个 bram(c_in_bram + ct_bram),否则 c' 覆盖 c 后无法比较 / 算 J。最易错,D0 定。
- **w 的 SUB**:`(v' psum) mod Q`,结果可能负 → +Q 修正(类 mod_sub)。v'/psum 读口冲突 → v' 搬到不同 bank。D3 dbg 验证。
- **J SHAKE pad**:末块 0x1F(非 0x06);单字节差异,复用 H mb 机器加 phase 选择 pad 常量。
- **op_i 加宽**:1-bit→2-bit,改 IDLE 分发、reset、所有 op_r 判断。回归 KeyGen/Encaps 不破。
- **m' 双用**:既喂 G(m'‖h)又喂 c'-Encrypt(替 m_r)。确保 Encrypt 路径读 m'_r 而非 m_r(加 mux 或 Decaps 时把 m'_r 写入 m_r)。
- **隐式拒绝常量时间**:比较全程扫完不早退(XOR 累加),硬件天然如此;但 mux 不可短路。
- **ek_pke 来自 dk**:Decaps 的 Encrypt 用 dk 内嵌的 ek_pke(解析到 ek_bram),不是外部 ek。

View File

@@ -10,12 +10,15 @@
# ./run_tb.sh enc # ML-KEM Encaps: all K, all cases (0..2)
# ./run_tb.sh enc 2 # Encaps K=2, all its cases
# ./run_tb.sh enc 4 1 # Encaps K=4, only CASE=1
# ./run_tb.sh dec # ML-KEM Decaps: all K, all cases (0..2)
# ./run_tb.sh dec 2 0 # Decaps K=2, only CASE=0
# ./run_tb.sh --list
#
# 'top' (KeyGen) and 'enc' (Encaps) share the same RTL datapath; both compile
# the xvlog lines from sync_rtl/top/TB/xsim_run.tcl. They differ only in the
# testbench: top -> tb_mlkem_kg_katK_xsim, enc -> tb_mlkem_enc_katK_xsim.
# For these two, K (2/3/4) and CASE select a single KAT run so you can iterate
# 'top' (KeyGen), 'enc' (Encaps) and 'dec' (Decaps) share the same RTL datapath;
# all compile the xvlog lines from sync_rtl/top/TB/xsim_run.tcl. They differ only
# in the testbench: top -> tb_mlkem_kg_katK_xsim, enc -> tb_mlkem_enc_katK_xsim,
# dec -> tb_mlkem_dec_katK_xsim.
# For these, K (2/3/4) and CASE select a single KAT run so you can iterate
# quickly. Other modules ignore the extra args and run their xsim_run.tcl verbatim.
#
# Prerequisites:
@@ -35,6 +38,7 @@ if [ "$1" = "--list" ]; then
fi
done
echo " enc (ML-KEM Encaps; shares the 'top' RTL/tcl, enc testbench)"
echo " dec (ML-KEM Decaps; shares the 'top' RTL/tcl, dec testbench)"
exit 0
fi
@@ -48,8 +52,8 @@ MODULE="$1"
SEL_K="$2" # optional: 2/3/4 (top/enc modules only)
SEL_CASE="$3" # optional: KAT case index (top/enc modules only)
# 'enc' is not its own RTL dir; it reuses the 'top' KeyGen tcl compile list
# (same datapath) and swaps in the encaps testbench.
if [ "$MODULE" = "enc" ]; then
# (same datapath) and swaps in the encaps testbench. 'dec' likewise (Decaps).
if [ "$MODULE" = "enc" ] || [ "$MODULE" = "dec" ]; then
TB_DIR="$SCRIPT_DIR/sync_rtl/top/TB"
else
TB_DIR="$SCRIPT_DIR/sync_rtl/$MODULE/TB"
@@ -188,11 +192,59 @@ run_enc_selected() {
return $fail
}
# ML-KEM Decaps runner. Same compile basis as enc (top tcl minus KeyGen TB) plus
# the decaps TB. Result line matches the final-stage PASS tag (D0..D7).
run_dec_selected() {
local tcl_file="$1" ksel="$2" csel="$3"
set +e
rm -rf xsim.dir .Xil
while read -r cmd; do
[[ "$cmd" == *tb_mlkem_kg_katK* ]] && continue
eval "$cmd" || { echo "COMPILE FAILED: $cmd"; return 1; }
done < <(grep -E '^xvlog ' "$tcl_file")
echo " xvlog -sv --relax sync_rtl/top/TB/tb_mlkem_dec_katK_xsim.v"
xvlog -sv --relax sync_rtl/top/TB/tb_mlkem_dec_katK_xsim.v \
|| { echo "DEC TB COMPILE FAILED"; return 1; }
local ks; if [ -n "$ksel" ]; then ks="$ksel"; else ks="2 3 4"; fi
local fail=0
for k in $ks; do
echo " xelab tb_mlkem_dec_katK_xsim -generic_top KP=$k -s mlkem_dec_k$k --timescale 1ns/1ps"
xelab tb_mlkem_dec_katK_xsim -generic_top KP=$k -s mlkem_dec_k$k --timescale 1ns/1ps \
|| { echo "ELAB FAILED for K=$k"; fail=1; continue; }
local cases="0 1 2"
if [ -n "$csel" ]; then cases="$csel"; fi
for c in $cases; do
local log="/tmp/run_tb_dec_k${k}_c${c}.log"
echo " xsim mlkem_dec_k$k -R -testplusarg CASE=$c"
echo "========================================" | tee "$log"
xsim "mlkem_dec_k$k" -R -testplusarg "CASE=$c" 2>&1 | tee -a "$log"
echo "========================================" | tee -a "$log"
local pf nf
# match the highest-stage result line: 'K=.. CASE .. PASS (Dn): ...'
pf=$(grep -oE 'PASS \(D[0-7]\)|FAIL \(D[0-7]\)' "$log" | tail -1)
nf=$(grep -c 'cannot be opened' "$log")
echo " K=$k CASE=$c -> ${pf:-NORESULT} (file-not-found=$nf, log: $log)"
{ [[ "$pf" == PASS* ]] && [ "$nf" -eq 0 ]; } || fail=1
done
done
return $fail
}
if [ "$MODULE" = "enc" ]; then
run_enc_selected "$TCL_FILE" "$SEL_K" "$SEL_CASE"
exit $?
fi
if [ "$MODULE" = "dec" ]; then
run_dec_selected "$TCL_FILE" "$SEL_K" "$SEL_CASE"
exit $?
fi
if [ "$MODULE" = "top" ] && [ -n "$SEL_K" ]; then
run_top_selected "$TCL_FILE" "$SEL_K" "$SEL_CASE"
exit $?

View File

@@ -0,0 +1,80 @@
#!/usr/bin/env python3
"""gen_decaps_vectors.py - Emit per-byte Decaps KAT vectors for the XSIM TB.
For ML-KEM-512/768/1024, parse the NIST .rsp and write, per case:
dec_k{K}_c{N}_dk.hex : dk (=sk) bytes, one hex byte per line, byte 0 first
dec_k{K}_c{N}_ct.hex : ct bytes (valid ciphertext), one per line, byte 0 first
dec_k{K}_c{N}_ss.hex : ss bytes (shared secret for valid ct), byte 0 first
dec_k{K}_c{N}_ctn.hex : ct_n bytes (corrupted ct -> implicit reject)
dec_k{K}_c{N}_ssn.hex : ss_n bytes (shared secret K-bar for the reject path)
The TB streams dk into the design via dk_in_* (routed to dk_pke/ek_pke/h/z by
region) and ct into c_in_*, runs Decaps, and checks ss:
valid ct -> ss == ss (c'==c, K' kept)
ct_n -> ss == ss_n (c'!=c, K-bar via J(z||c_n))
Run: python3 gen_decaps_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, sk, ct, ss, ct_n, ss_n} (hex) 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: 'ct =' vs 'ct_n =', 'ss =' vs 'ss_n ='
m = re.match(r"^(sk|ct|ss|ct_n|ss_n)\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"dec_k{k}_c{n}")
write_bytes(f"{base}_dk.hex", v["sk"])
write_bytes(f"{base}_ct.hex", v["ct"])
write_bytes(f"{base}_ss.hex", v["ss"])
write_bytes(f"{base}_ctn.hex", v["ct_n"])
write_bytes(f"{base}_ssn.hex", v["ss_n"])
print(f"K={k}: wrote {len(vecs)} cases "
f"(dk={len(vecs[0]['sk'])//2}B ct={len(vecs[0]['ct'])//2}B)")
if __name__ == "__main__":
main()

View File

@@ -0,0 +1,153 @@
// tb_mlkem_dec_katK_xsim.v - ML-KEM Decaps vs NIST KAT, parametric K (KP) + CASE.
// D0 stage: stream dk (=sk) into the design via dk_in_* (routed to
// dk_pke/ek_pke/h/z by region) and ct via c_in_*, pulse start with op=2, and
// verify the dk PARSE: H(ek) (dbg_dech_o), z (dbg_decz_o), and round-trip a few
// ek_pke bytes (dbg_byte sel=0) and dk_pke bytes (sel=1) back out of BRAM.
//
// xelab -generic_top KP=2|3|4 ; xsim -testplusarg CASE=n
// dk/ct/ss vectors: sync_rtl/top/TB/vectors/dec_k{K}_c{N}_{dk,ct,ss,ctn,ssn}.hex
`timescale 1ns/1ps
module tb_mlkem_dec_katK_xsim;
parameter KP = 2;
localparam DKB = 768*KP + 96; // dk (=sk) bytes: 1632/2400/3168
localparam EKB = 384*KP + 32; // ek_pke bytes within dk
localparam DKPB = 384*KP; // dk_pke bytes
localparam CTB = (KP==4) ? 1568 : (32*(10*KP+4)); // ct bytes: 768/1088/1568
reg clk=0, rst_n=0, start_i=0;
reg [2:0] k_i;
reg [255:0] d_i=0, z_i=0, m_i=0;
wire busy_o, done_o;
// ek preload port (unused in Decaps; ek_pke comes from dk)
reg ek_in_we=0; reg [10:0] ek_in_addr=0; reg [7:0] ek_in_byte=0;
// dk / c input ports
reg dk_in_we=0; reg [11:0] dk_in_addr=0; reg [7:0] dk_in_byte=0;
reg c_in_we=0; reg [10:0] c_in_addr=0; reg [7:0] c_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;
wire [255:0] dbg_mprime_o, dbg_kbar_o, dbg_decz_o, dbg_dech_o;
mlkem_top dut (
.clk(clk), .rst_n(rst_n), .k_i(k_i), .op_i(2'd2),
.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),
.dk_in_we(dk_in_we), .dk_in_addr(dk_in_addr), .dk_in_byte(dk_in_byte),
.c_in_we(c_in_we), .c_in_addr(c_in_addr), .c_in_byte(c_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),
.dbg_mprime_o(dbg_mprime_o), .dbg_kbar_o(dbg_kbar_o),
.dbg_decz_o(dbg_decz_o), .dbg_dech_o(dbg_dech_o)
);
always #5 clk = ~clk;
reg [7:0] dk_b [0:DKB-1];
reg [7:0] ct_b [0:CTB-1];
reg [7:0] ss_b [0:31];
integer c, i, j, errors, casenum;
reg [8*80-1:0] tag, dkfile, ctfile, ssfile;
initial begin
if (!$value$plusargs("CASE=%d", casenum)) casenum = 0;
$sformat(tag, "k%0d", KP);
$sformat(dkfile, "sync_rtl/top/TB/vectors/dec_%0s_c%0d_dk.hex", tag, casenum);
$sformat(ctfile, "sync_rtl/top/TB/vectors/dec_%0s_c%0d_ct.hex", tag, casenum);
$sformat(ssfile, "sync_rtl/top/TB/vectors/dec_%0s_c%0d_ss.hex", tag, casenum);
$readmemh(dkfile, dk_b);
$readmemh(ctfile, ct_b);
$readmemh(ssfile, ss_b);
k_i = KP[2:0];
$display("=== ML-KEM K=%0d Decaps KAT case %0d (D0: load+parse) ===", KP, casenum);
rst_n=0; repeat(4) @(posedge clk); rst_n=1; @(posedge clk);
// ---- stream dk into the design (1 byte/cycle) ----
for (i = 0; i < DKB; i = i + 1) begin
dk_in_we = 1'b1; dk_in_addr = i[11:0]; dk_in_byte = dk_b[i];
@(posedge clk);
end
dk_in_we = 1'b0;
// ---- stream ct into c_in_bram (1 byte/cycle) ----
for (i = 0; i < CTB; i = i + 1) begin
c_in_we = 1'b1; c_in_addr = i[10:0]; c_in_byte = ct_b[i];
@(posedge clk);
end
c_in_we = 1'b0; @(posedge clk);
// ---- run Decaps ----
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("=== Decaps D0 done in %0d cyc ===", c);
verify_d0;
$finish;
end
initial begin #120000000; $display("FAIL: global timeout"); $finish; end
// D0: verify dk parse. H(ek)=dk[768K+32:+32], z=dk[768K+64:+32] captured into
// hek_r/z_r (dbg_dech_o/dbg_decz_o). ek_pke=dk[384K:768K+32] in ek_bram
// (dbg_byte sel=0), dk_pke=dk[0:384K] in dkp_bram (sel=1).
task verify_d0;
integer be;
reg [7:0] got;
begin
errors = 0;
// H(ek)
for (j = 0; j < 32; j = j + 1)
if (dbg_dech_o[8*j +: 8] !== dk_b[DKPB + EKB + j]) errors = errors + 1;
if (errors == 0) $display(" PASS: H(ek) parsed == dk[768K+32 ..]");
else $display(" FAIL: H(ek) %0d byte mismatches", errors);
// z
be = 0;
for (j = 0; j < 32; j = j + 1)
if (dbg_decz_o[8*j +: 8] !== dk_b[DKPB + EKB + 32 + j]) be = be + 1;
if (be == 0) $display(" PASS: z parsed == dk[768K+64 ..]");
else $display(" FAIL: z %0d byte mismatches", be);
errors = errors + be;
// ek_pke round-trip (every 97th byte to keep it quick)
be = 0;
for (i = 0; i < EKB; i = i + 97) begin
dbg_byte_sel_i = 1'b0; dbg_byte_idx_i = i[10:0];
@(posedge clk); @(posedge clk);
if (dbg_byte_o !== dk_b[DKPB + i]) begin
if (be < 6) $display(" ekpke[%0d] got=%02x exp=%02x", i, dbg_byte_o, dk_b[DKPB+i]);
be = be + 1;
end
end
if (be == 0) $display(" PASS: ek_pke round-trip (BRAM) == dk[384K ..]");
else $display(" FAIL: ek_pke %0d byte mismatches", be);
errors = errors + be;
// dk_pke round-trip
be = 0;
for (i = 0; i < DKPB; i = i + 97) begin
dbg_byte_sel_i = 1'b1; dbg_byte_idx_i = i[10:0];
@(posedge clk); @(posedge clk);
if (dbg_byte_o !== dk_b[i]) begin
if (be < 6) $display(" dkpke[%0d] got=%02x exp=%02x", i, dbg_byte_o, dk_b[i]);
be = be + 1;
end
end
if (be == 0) $display(" PASS: dk_pke round-trip (BRAM) == dk[0 ..]");
else $display(" FAIL: dk_pke %0d byte mismatches", be);
errors = errors + be;
if (errors == 0) $display("K=%0d CASE %0d PASS (D0): dk parse OK", KP, casenum);
else $display("K=%0d CASE %0d FAIL (D0): %0d total errors", KP, casenum, errors);
end
endtask
endmodule

View File

@@ -27,16 +27,19 @@ module tb_mlkem_enc_katK_xsim;
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),
.clk(clk), .rst_n(rst_n), .k_i(k_i), .op_i(2'd1),
.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),
.dk_in_we(1'b0), .dk_in_addr(12'd0), .dk_in_byte(8'd0),
.c_in_we(1'b0), .c_in_addr(11'd0), .c_in_byte(8'd0),
.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)
.dbg_r_o(dbg_r_o), .dbg_hek_o(dbg_hek_o),
.dbg_mprime_o(), .dbg_kbar_o(), .dbg_decz_o(), .dbg_dech_o()
);
always #5 clk = ~clk;

View File

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

File diff suppressed because it is too large Load Diff

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,32 @@
6e
5c
52
2a
6d
19
b8
6c
61
bd
98
3b
56
a0
be
f3
51
c5
ce
71
6f
02
1b
49
bd
ec
d7
bd
fd
5e
d5
5a

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

File diff suppressed because it is too large Load Diff

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,32 @@
76
08
dd
95
ec
12
b1
3d
bf
17
85
d1
06
22
61
4a
90
7b
3c
8b
93
16
97
21
2d
9b
d0
84
5e
ff
66
0d

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

File diff suppressed because it is too large Load Diff

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

View File

@@ -0,0 +1,32 @@
f1
33
05
54
e2
67
00
b8
96
75
8d
b1
cc
8e
ec
b7
3b
8c
80
9a
40
83
46
6e
4e
0c
65
4f
34
6a
fa
e1

File diff suppressed because it is too large Load Diff

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 @@
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

View File

@@ -0,0 +1,32 @@
e7
bc
f8
99
fe
b5
db
69
d1
17
41
a8
83
8e
cf
ee
25
3e
80
b7
6d
6a
35
af
46
e2
20
e8
1d
c4
1d
af

File diff suppressed because it is too large Load Diff

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 @@
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

View File

@@ -0,0 +1,32 @@
40
69
db
99
97
53
67
57
c9
b1
2f
02
6f
fb
3d
02
f4
35
55
9d
f6
32
14
71
17
50
cd
04
b1
f0
5d
5f

File diff suppressed because it is too large Load Diff

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 @@
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

View File

@@ -0,0 +1,32 @@
48
85
52
10
1a
21
c9
cd
87
31
46
b8
3f
ed
69
d2
63
76
f7
00
31
25
66
ca
6b
6d
f0
7b
d5
58
5d
59

File diff suppressed because it is too large Load Diff

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 @@
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

View File

@@ -0,0 +1,32 @@
24
7d
3f
ed
8c
ac
db
06
bd
e6
37
c7
49
fe
e6
89
a2
cc
0a
14
25
e9
c5
b8
ec
90
19
2e
f4
bd
a1
80

File diff suppressed because it is too large Load Diff

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 @@
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

View File

@@ -0,0 +1,32 @@
c9
65
7c
41
49
98
e2
f9
64
d2
a6
b8
74
73
9e
2c
65
cc
91
f4
cd
fa
33
51
aa
54
d0
34
22
33
98
32

File diff suppressed because it is too large Load Diff

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 @@
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

@@ -0,0 +1,32 @@
f5
95
77
44
20
dd
2c
b8
d0
39
f9
ce
8e
b7
4b
b9
c2
95
2d
94
af
91
e8
e8
6e
29
2f
ff
27
2a
71
73

View File

@@ -28,7 +28,7 @@ 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 [1:0] op_i, // 0=KeyGen, 1=Encaps, 2=Decaps (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])
@@ -41,6 +41,21 @@ module mlkem_top #(
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
// Decaps dk input: stream dk bytes (=sk, 768K+96) into the design before
// start_i with op_i=2. The load logic routes each byte by region:
// [0,384K) -> dk_pke (dkp_bram) = s_hat encoding
// [384K, 768K+32) -> ek_pke (ek_bram) = t_hat encoding || rho
// [768K+32, 768K+64) -> H(ek) (hek_r)
// [768K+64, 768K+96) -> z (z_r)
input dk_in_we, // write one dk byte (ST_IDLE Decaps preload)
input [11:0] dk_in_addr, // dk byte address 0..(768K+96)-1
input [7:0] dk_in_byte, // dk byte value
// Decaps ciphertext input: stream c bytes (=ct) into c_in_bram before
// start_i. Kept separate from ct_bram so the computed c' can be compared
// against the original c (and J(z||c) can read c) without aliasing.
input c_in_we, // write one c byte (ST_IDLE Decaps preload)
input [10:0] c_in_addr, // c byte address 0..ct_bytes-1
input [7:0] c_in_byte, // c 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
@@ -65,7 +80,12 @@ module mlkem_top #(
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
output [255:0] dbg_hek_o,
// Decaps debug taps: m' (Decrypt output), K-bar (J output), parsed z/h.
output [255:0] dbg_mprime_o,
output [255:0] dbg_kbar_o,
output [255:0] dbg_decz_o, // parsed z (dk[768K+64:+32])
output [255:0] dbg_dech_o // parsed H(ek) (dk[768K+32:+32])
);
localparam Q = `Q; // 3329
@@ -82,15 +102,28 @@ module mlkem_top #(
wire [5:0] slot_t_rt = kk_rt + {1'b0, k_r} + {1'b0, k_r}; // t_hat base = kk+2k
wire [11:0] ek_bytes_rt = 12'd384 * {9'b0, k_r} + 12'd32; // 800/1184/1568
wire [11:0] dk_bytes_rt = 12'd384 * {9'b0, k_r}; // 768/1152/1536
// full dk = dk_pke(dk_bytes) || ek(ek_bytes) || H(ek)(32) || z(32). Region
// boundaries (used by Decaps dk load routing and dk readback).
wire [11:0] dk_ek_end = dk_bytes_rt + ek_bytes_rt; // ek_pke region end
wire [11:0] dk_hek_end = dk_ek_end + 12'd32; // H(ek) region end
// LOAD-TIME boundaries: dk is streamed in BEFORE start_i, so k_r is not yet
// captured. Route the dk load by the LIVE k_i input instead of k_r.
wire [11:0] dkp_bytes_ld = 12'd384 * {9'b0, k_i}; // 768/1152/1536
wire [11:0] ek_bytes_ld = 12'd384 * {9'b0, k_i} + 12'd32; // 800/1184/1568
wire [11:0] dk_ek_end_ld = dkp_bytes_ld + ek_bytes_ld; // ek_pke region end
wire [11:0] dk_hek_end_ld= dk_ek_end_ld + 12'd32; // H(ek) region end
// H(ek) block count = ceil((ek_bytes+1)/136): 6/9/12 for k=2/3/4 (table)
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 [1:0] op_r; // 0=KeyGen 1=Encaps 2=Decaps (captured at start)
reg [255:0] m_r; // Encaps message m (captured at start); Decaps reuses for m'
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)
// ---- Decaps runtime params (parsed from dk during load) ----
reg [255:0] z_r; // implicit-rejection seed z (dk tail), captured at load
reg [255:0] kbar_r; // K-bar = J(z||c) (D5)
// 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.
@@ -100,6 +133,7 @@ module mlkem_top #(
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
wire [11:0] ct_bytes_rt = c1_bytes_rt + c2_bytes_rt; // 768/1088/1568
assign ss_o = ss_r;
// ---- E1: rho-load + byteDecode12 bookkeeping ----
@@ -145,6 +179,23 @@ module mlkem_top #(
assign ct_rd_addr = dbg_ct_idx_i;
assign dbg_ct_o = ct_rd_data;
// ---- c_in_bram: Decaps input ciphertext c (<=1568 B). Preloaded via
// c_in_* in ST_IDLE. Kept separate from ct_bram (which holds the computed
// c') so D7 can compare c' vs c and D5 J(z||c) can read c. Read port is
// muxed (D5 J-feed / D7 compare); for D0 only the load write path exists.
wire [10:0] cin_rd_addr;
wire [7:0] cin_rd_data;
reg [10:0] cin_rd_addr_r; // D5/D7 read address (tied 0 until then)
sd_bram #(.W(8), .D(2048), .A(11)) u_c_in_bram (
.clk(clk),
.rd_addr(cin_rd_addr), .rd_data(cin_rd_data),
.wr_en(c_in_we), .wr_addr(c_in_addr), .wr_data(c_in_byte)
);
assign cin_rd_addr = cin_rd_addr_r;
/* verilator lint_off UNUSEDSIGNAL */
wire [7:0] cin_rd_data_unused = cin_rd_data; // consumed in D5/D7
/* verilator lint_on UNUSEDSIGNAL */
// ================================================================
// Polynomial storage, sized for KMAX (worst case). Runtime k uses a
// sub-range. Slot layout (each slot = 256 coeffs):
@@ -330,26 +381,39 @@ module mlkem_top #(
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;
// ek from the external ek_in_* port (TB streams ek=pk before start_i). Decaps
// routes the dk's ek_pke region (dk[384K:768K+32]) here at load time. The
// three never overlap (preloads happen in ST_IDLE before a run).
wire dk_ld_ekpke = dk_in_we && (dk_in_addr >= dkp_bytes_ld)
&& (dk_in_addr < dk_ek_end_ld); // ek_pke region
wire dk_ld_dkpke = dk_in_we && (dk_in_addr < dkp_bytes_ld); // dk_pke region
wire [10:0] dk_ek_off = (dk_in_addr - dkp_bytes_ld); // offset within ek
wire ek_we_mux = ek_in_we ? 1'b1 :
dk_ld_ekpke ? 1'b1 : ek_we;
wire [10:0] ek_wa_mux = ek_in_we ? ek_in_addr :
dk_ld_ekpke ? dk_ek_off : ek_wa;
wire [7:0] ek_wd_mux = ek_in_we ? ek_in_byte :
dk_ld_ekpke ? dk_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_mux), .wr_addr(ek_wa_mux), .wr_data(ek_wd_mux)
);
// dk_pke BRAM write port: KeyGen ST_E (s_hat byteEncode) drives dkp_we/wa/wd;
// Decaps routes the dk's dk_pke region (dk[0:384K]) here at load time. The
// two never overlap (preload in ST_IDLE before a Decaps run).
wire dkp_we_mux = dk_ld_dkpke ? 1'b1 : dkp_we;
wire [10:0] dkp_wa_mux = dk_ld_dkpke ? dk_in_addr[10:0] : dkp_wa;
wire [7:0] dkp_wd_mux = dk_ld_dkpke ? dk_in_byte : dkp_wd;
sd_bram #(.W(8), .D(2048), .A(11)) u_dkp_bram (
.clk(clk),
.rd_addr(dkp_rd_addr), .rd_data(dkp_rd_data),
.wr_en(dkp_we), .wr_addr(dkp_wa), .wr_data(dkp_wd)
.wr_en(dkp_we_mux), .wr_addr(dkp_wa_mux), .wr_data(dkp_wd_mux)
);
// full dk = dk_pke(dk_bytes) || ek(ek_bytes) || H(ek)(32) || z(32)
wire [11:0] dk_ek_end = dk_bytes_rt + ek_bytes_rt; // ek region end
wire [11:0] dk_hek_end = dk_ek_end + 12'd32; // H(ek) region end
// full dk = dk_pke(dk_bytes) || ek(ek_bytes) || H(ek)(32) || z(32).
// (dk_ek_end / dk_hek_end declared near the top with the other size wires.)
// Debug-region selects for dk readback (combinational region decode).
wire dbgdk_in_dkp = (dbg_dk_idx_i < dk_bytes_rt);
@@ -390,6 +454,8 @@ module mlkem_top #(
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_ENC_E2MV = 5'd19; // relocate e2 bank_t[0] -> bank_a[E2_ASLOT]
// ---- Decaps states ----
localparam ST_DEC_LOAD = 5'd20; // dk/c already streamed in; parse/settle (D0)
localparam ST_DONE = 5'd31;
reg [4:0] st, st_next;
@@ -418,6 +484,11 @@ 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;
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
// ---- sha3_top in G mode: data_i = {K_byte, d} (d byte0 in [7:0]) ----
reg sha3_valid;
@@ -897,7 +968,11 @@ module mlkem_top #(
always @(*) begin
st_next = st;
case (st)
ST_IDLE: if (start_i) st_next = op_i ? ST_ENC_H : ST_G;
ST_IDLE: if (start_i) st_next = (op_i == 2'd2) ? ST_DEC_LOAD :
(op_i == 2'd1) ? ST_ENC_H : ST_G;
// D0: settle after dk/c parse, then (D1+) proceed to Decrypt. For now
// ST_DEC_LOAD just lands in DONE so the load/parse can be dbg-checked.
ST_DEC_LOAD: 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;
@@ -927,7 +1002,7 @@ module mlkem_top #(
if (!rst_n) begin
st <= ST_IDLE;
k_r <= 3'd0;
op_r <= 1'b0;
op_r <= 2'd0;
m_r <= 256'd0;
ss_r <= 256'd0;
r_r <= 256'd0;
@@ -1024,6 +1099,9 @@ module mlkem_top #(
h_mblast <= 1'b0;
h_ack <= 1'b0;
hek_r <= 256'd0;
z_r <= 256'd0;
kbar_r <= 256'd0;
cin_rd_addr_r <= 11'd0;
h_blk <= 3'd0;
h_byte <= 8'd0;
h_phase <= 2'd0;
@@ -1043,11 +1121,12 @@ module mlkem_top #(
em_we <= 1'b0; // e2-relocate bank_a write default low (E6)
// 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).
// op/m and arms the H(ek) machinery (ST_ENC_H reuses the ST_H FSM);
// Decaps captures op (dk/c already streamed; z/h captured at load).
if (st == ST_IDLE && start_i) begin
k_r <= k_i; // capture runtime ML-KEM param
op_r <= op_i;
if (op_i) begin
if (op_i == 2'd1) begin
m_r <= msg_i; // capture Encaps message
// arm H(ek) (same fields the ST_E->ST_H arming sets)
h_blk <= 3'd0;
@@ -1057,11 +1136,25 @@ module mlkem_top #(
h_mblast <= 1'b0;
h_ack <= 1'b1; // ready to consume final digest
h_wb_vld <= 1'b0;
end else if (op_i == 2'd2) begin
// Decaps: dk_pke/ek_pke already in BRAM; z/H(ek) captured into
// z_r/hek_r during load (below). Nothing else to arm in D0.
end else begin
sha3_valid <= 1'b1;
sha3_ack <= 1'b1;
end
end
// Decaps dk load: capture the H(ek) and z byte regions into registers
// as they stream in (the dk_pke/ek_pke regions go to BRAM via the
// write muxes). Byte i within a region lands in [8i +: 8]. Uses the
// LIVE k_i boundaries (k_r not yet captured during preload).
if (dk_in_we) begin
if (dk_in_addr >= dk_ek_end_ld && dk_in_addr < dk_hek_end_ld)
hek_r[(dk_in_addr - dk_ek_end_ld)*8 +: 8] <= dk_in_byte; // H(ek)
else if (dk_in_addr >= dk_hek_end_ld)
z_r[(dk_in_addr - dk_hek_end_ld)*8 +: 8] <= dk_in_byte; // z
end
// Drop valid once accepted
if (sha3_valid && sha3_ready) sha3_valid <= 1'b0;