Compare commits

...

2 Commits

Author SHA1 Message Date
e46d2258d9 chore(tb): remove Verilator TBs + framework; parallelize XSIM runs
Verilator is no longer used (all verification is via Vivado XSIM). Remove:
- 10 per-module tb_*.cpp Verilator testbenches
- the entire test_framework/ Verilator harness (lib/, run_all.py, config.json,
  per-module test_plan.json/gen_vectors.py, golden vectors, reports)
- stale specs: verilator-conventions.md, test_framework/structure.md
  (index.md updated to drop the Verilator entry)

Parallelize run_tb.sh K x case execution (modules stay serial):
- new run_xsim_jobs helper: compile+elaborate once (serial, populates the
  shared xsim.dir), then run each (K,case) xsim in its own private workdir
  with a COPY of xsim.dir (~1MB) so concurrent same-snapshot runs don't clobber
  each other's runtime logs. Each workdir symlinks the repo sync_rtl tree so
  the TB's repo-relative $readmemh vector paths resolve.
- top/enc/dec runners refactored to build a (snapshot:K:case) spec list and
  hand it to run_xsim_jobs; ordered PASS/FAIL summary + per-job /tmp logs
  preserved. Bare './run_tb.sh top' now also takes the parallel path.

Speedup (20 cores): top full sweep 2:11 -> 0:51 (~2.6x), ~320% CPU.
Verified: top (11) / enc (9) / dec (9) all PASS; missing-vector runs still
fail (file-not-found guard -> exit 1).
2026-06-29 16:05:06 +08:00
030931d4e5 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.
2026-06-29 15:22:34 +08:00
186 changed files with 43550 additions and 22082 deletions

View File

@@ -0,0 +1,80 @@
# 计划:将 mlkem_top 的大数据存储迁移到 BRAM
## 目标
把当前用裸 `reg` 数组实现的三块大存储改为可被 Vivado 推断为 block RAM 的结构,
使设计面向综合时不再把 86 kbit 的多项式存储映射成 LUT/FF。
涉及存储:
| 当前 reg 数组 | 容量(KMAX=4) | 用途 |
|---|---|---|
| `polymem [0:28*256-1]` ×12bit | 86 016 bit | 全部多项式 Â/ŝ/ê/t̂ |
| `ek_mem [0:1567]` ×8bit | 12 544 bit | byteEncode 后 ek |
| `dkp_mem [0:1535]` ×8bit | 12 288 bit | byteEncode 后 dk_pke |
复用现有 `sync_rtl/storage/sd_bram.v`1 读 + 1 写读地址寄存→1 周期读延迟,
W≥12 & D≥64 时 Vivado 自动推断 BRAM
## 核心约束(已从代码确认)
1. **BRAM 读有 1 周期延迟**reg 数组是组合读)。所有读出点都要改成“地址提前一拍”。
2. **BRAM 每端口每周期 ≤1 访问**。当前 `polymem` 在同周期最多有 2 个不同读地址
ST_M LOAD 同时读 A 和 ŝ),且 ST_E 同周期读同一 poly 的 2 个系数。
3. **ST_E 当前每周期写 3 字节**到 ek/dk —— BRAM 单写口做不到。
## 关键结论(决定方案可行性)
- `poly_mul_sync` 在 LOAD 阶段把 256 对系数**全部存进自己的 mem_A/mem_B**,再在 COMP
阶段输出。所以 ST_M 的 LOAD读 A+ŝ)与 ACCUMULATE读 ê/t̂、写 t̂**绝不同周期**。
`polymem` 按区域分 4 个 bank {A, S, E, T} 后,每个 bank 任意周期 ≤1 读 + 1 写,
`sd_bram` 即可满足,无需真双口。
- TB 每次调试回读等 **2 个周期**`@(posedge clk); @(posedge clk);`),可吸收 BRAM 多出的
1 拍读延迟,回读路径无需改 TB。
## polymem 4-bank 划分
| bank | slot 数 | 深度(×256) | 写于 | 读于 |
|---|---|---|---|---|
| A (Â[i][j]) | KMAX²=16 | 4096 | ST_A | ST_M LOAD |
| S (ŝ[i]) | KMAX=4 | 1024 | ST_C / ST_N | ST_N LOAD, ST_M LOAD, ST_E |
| E (ê[i]) | KMAX=4 | 1024 | ST_C / ST_N | ST_N LOAD, ST_M ACC(j=0) |
| T (t̂[i]) | KMAX=4 | 1024 | ST_M ACC | ST_M ACC(j>0), ST_E |
(总深度 7168 == 当前 28×256容量不变
每个 bank 一个 `sd_bram #(.W(12))`。读地址由一个 per-bank mux 选择:计算期由 FSM 计数器驱动,
ST_DONE/回读期由 `dbg_*` 驱动;`dbg_coeff_o` 改为按 slot 选 4 个 bank 的 rd_data。
## 实施分两阶段,每阶段后跑全部 11 个 KAT 用例
### 阶段 1ek_mem / dkp_mem → BRAM风险较低先做
1. 例化 `ek_bram``dkp_bram`sd_bramW=8
2. **重写 ST_E 为每周期写 1 字节**3 字节拆 3 拍):新增子计数器 b∈{0,1,2}
按 b 选 e_b0/e_b1/e_b2 与目标地址 e_boff+b。ST_E 周期数约 ×3K=4 增 ~2K 周期,
占总量 ~4%可接受。rho 拷贝阶段本就 1 字节/周期,不变。
3. **ST_H 的 h_padbyte 读 ek 改为地址提前一拍**assemble 子状态先发地址、下一拍取数据
写入 h_block_rh_byte 流水线 +1 级)。
4. **回读路径**dbg_byte_o / dbg_dk_o改为直接用 bram 的 rd_data去掉多余的中间寄存器
保持总延迟 ≤2 拍,落在 TB 的 2 周期等待内)。
5.`./run_tb.sh top` 全 11 例,要求 0 个 `cannot be opened`、全 PASS。
### 阶段 2polymem → 4 bank BRAM风险较高
1. 例化 4 个 `sd_bram #(.W(12))`bank_a(D=4096)、bank_s/e/t(D=1024)。
2. 写口改写ST_A→bank_aST_C→bank_s/eST_N→bank_s/eST_M ACC→bank_t。
3. 读口加“地址提前一拍”流水:
- ST_N LOAD读地址 = 下一个 n_ridx数据对齐 ntt_core 的 valid_i。
- ST_M LOAD读地址 = 下一个 m_ld数据对齐 poly_mul 的 pm_valid。
- ST_M ACCm_acc_src 读地址跟踪即将到来的 m_oidx提前一拍对齐 pm_vo。
- ST_E每周期读 1 个系数e_c0、e_c1 拆两拍),配合阶段 1 的 1 字节/周期写。
4. dbg_coeff_o4 bank rd_data 按 dbg_slot 区间选择。
5. 跑全 11 例验证。
## 验证方法(每阶段)
- 干净重跑:`rm -rf xsim.dir .Xil``./run_tb.sh top`
- 检查日志:`grep -c 'cannot be opened'`(=0) 且 11/11 `PASS`
- 记录周期数变化(预期 ek/dk BRAM 化使 ST_E 变长、polymem 读延迟引入少量气泡)。
- 阶段 2 完成后,可选:跑 `vivado -mode batch` 综合,确认 polymem 现在落在 RAMB 而非 LUTRAM。
## 不在本次范围
- 是否把 ek/dk 暴露为对外输出端口(上一条被打断的请求)—— 独立问题,本次只改存储实现,
保持现有 dbg_* 接口不变。如需对外输出,迁移完成后另议。
## 风险
- 读延迟流水改造触及所有 5 个计算阶段的握手时序,是 KAT 回归的主要风险点。
- 缓解:分两阶段、每阶段独立 KAT 回归;先做低风险的 ek/dk再做 polymem。
- 若某阶段无法在合理尝试内通过 KAT回退该阶段并报告根因不带病提交。

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

@@ -0,0 +1,175 @@
# ML-KEM KeyGen 顶层集成 — 实现计划与进度
> 本文档用于让新工程师完整接手。包含原始计划 + 已完成进度 + 经验教训 + 下一步。
## 目标与范围
- 目标:实现 `mlkem_top`**KeyGen 通路**(ML-KEM-512, K=2, η1=3),全新重写 valid/ready FSM。
- 最终验收:对 **NIST 官方 KAT**(count=0..4)端到端逐字节通过,产出完整 ML-KEM dk。
- 本计划只做 KeyGen。Encaps/Decaps 留作后续任务。
- 旧版 `mlkem_top.v`(1658 行)已于 commit 1cace51 删除,**不可用**(Decaps 是占位,且子模块握手从未真正跑通——旧 TB 用 24 处 force/release 绕过)。新版全新重写。
## 关键决策(已与用户确认)
- **Keccak 策略**:每消费者用独立、已验证的核(sha3_top / sample_ntt_sync / sample_cbd_sync 各自带 keccak_core)。**不用 arbiter,不用 `_shared` 变体**(顶层当前用独立版)。
- **SHA3 先扩多块**:已先把 sha3_top 的 H 模式扩成多块吸收(✅ 完成),再做完整 dk。
- **FSM 全新重写**:只参考算法步骤,不取回旧代码。
- **分阶段 + 逐级对拍**:每个子步骤对 golden 验证通过再进下一步。
## 环境与工具(重要)
- 工作目录:`/home/fallensigh/Dev/mlkem`,git 分支 `main`(直接提交,用户要求)。
- **Verilator** 5.046:`python3 test_framework/run_all.py [--module X]`。快速回归。
- **Vivado XSIM** 2019.2:每次需
```
source /opt/Xilinx/Vivado/2019.2/settings64.sh
export LD_PRELOAD=/usr/lib64/libtinfo.so.5
```
调用 **standalone** `xvlog -sv --relax -i . <files>` / `xelab <top> -s <snap> --timescale 1ns/1ps` / `xsim <snap> -R`。**不要**用 `vivado -mode batch`(脚本里用的是 standalone 命令,不是 Tcl)。zsh 不做无引号变量分词——用 `"$@"` 传文件列表。
- XSIM 产物(xsim.dir/ .Xil/ *.jou *.log)已 gitignore;每次跑前 `rm -rf xsim.dir .Xil`。
## Golden reference(全部已验证可用)
- **NIST 官方 KAT**:`/home/fallensigh/Dev/ml-kem-r/test_data/kat_MLKEM_{512,768,1024}.rsp`。
- **ml-kem-r**(Rust, `/home/fallensigh/Dev/ml-kem-r`):`cargo test` 全过,512/768/1024 KAT 全通过。是可信参考。
- API:`ml_kem::mlkem512::key_gen(d,z)`、`keygen_internal::<2,192>(d,z)`、`k_pke::key_gen::<2,192>(d)`。
- 公共子函数:`sample::sample_ntt(&[u8])`、`sample::sample_poly_cbd::<ETA_BYTES>`、`ntt::ntt(&mut [i32;256])`、`ntt::multiply_ntts`、`sha3::mlkem_G/mlkem_H/mlkem_PRF`、`utils::byte_encode(&[i32;256], d)`。
- `logging` feature + `examples/logging.rs` dump 中间值(4-coeff head)。
- **server_code**(Python, `~/Dev/server_code/python_project/PQC_2025/A_ML_KEM_v0`):`SHA_3.G/H/J/PRF`、`ML_KEM.py`。已对 hashlib 交叉验证。
### 白盒 golden 向量(Stage 0 产物,已生成并自校验)
- 生成器:`/home/fallensigh/Dev/ml-kem-r/examples/dump_keygen.rs`(我新增,未提交到 ml-kem-r,因为该仓库有用户大量未提交改动 —— 只在工作树用)。
- 运行:`cargo run --release --example dump_keygen -- <out_dir> 5`
- 输出目录:`test_framework/modules/mlkem_keygen/golden/`(已 commit 在 mlkem 仓库 106b292)。
- 每个 KAT case c(000..004)的全 256-coeff 中间量,文件名:
- `c000_rho.hex` / `c000_sigma.hex`:32B hex(byte0 在最低)。
- `c000_Ahat_i_j.hex`(i,j∈0..1):256 行,每行 3-hex 系数(mod q)。
- `c000_s_i.hex` / `c000_e_i.hex`:CBD 输出,**mod-q 形式**(负值 +Q,如 -1 → 0xd00=3328)。
- `c000_shat_i.hex` / `c000_ehat_i.hex`:NTT(s/e) 后。
- `c000_that_i.hex`:t_hat。
- `c000_ek.hex`(800B hex = KAT pk)、`c000_dkpke.hex`(768B = KAT sk 前 384*K)。
- dump_keygen 已自校验 ek==KAT.pk、dk_pke==KAT.sk[..768],全 5 case 通过。
## KeyGen 算法 (FIPS 203 Alg 13+16, K=2 η1=3)
1. (ρ,σ) = G(d ‖ K) — SHA3-512, 33B 输入(单块)。**G 输入 = d(byte0 低) ‖ K=2 at byte32,无字节翻转**(已确认,旧版 byte-reverse 是 bug)。ρ = hash[255:0],σ = hash[511:256]。
2. Â[i][j] = SampleNTT(ρ‖j‖i), i,j∈{0,1} — seed = rho ‖ j ‖ i(sample_ntt_sync 内部 msg={i_byte,j_byte,rho})。
3. s[i] = CBD₃(PRF(σ, **i**)), i∈{0,1} — nonce 0,1
4. e[i] = CBD₃(PRF(σ, **K+i**)), i∈{0,1} — nonce 2,3
5. ŝ[i] = NTT(s[i]); ê[i] = NTT(e[i]) — 前向 NTT ×4 (mode=0, **无缩放**)
6. t̂[i] = ê[i] + Σⱼ Â[i][j]∘ŝ[j] — poly_mul + 逐系数 modQ 累加
7. ek = byteEncode₁₂(t̂[0..1]) ‖ ρ — 12-bit 原始打包(**非压缩**)
8. dk_pke = byteEncode₁₂(ŝ[0..1])
9. dk = dk_pke ‖ ek ‖ H(ek) ‖ z — H(ek): SHA3-256 over 800B(**多块**)
**用到的叶子模块**(端口签名见下):sha3_top(G+多块H)、sample_ntt_sync、sample_cbd_sync、ntt_core(mode=0)、poly_mul_sync、mod_add_sync。**不用** comp_decomp。
## 叶子模块端口签名(已抓取)
- `rng_sync #(SEED)`: clk,rst_n,valid_i,ready_o,data_o[255:0],valid_o,ready_i
- `sha3_top`: clk,rst_n,mode[1:0],data_i[511:0],valid_i,ready_o,hash_o[511:0],valid_o,ready_i, **+多块端口** mb_en,mb_block_i[1087:0],mb_valid_i,mb_last_i,mb_ready_o
- `sample_ntt_sync #(K=4)`: clk,rst_n,rho_i[255:0],k_i[2:0],i_idx[1:0],j_idx[1:0],valid_i,ready_o,coeff_o[11:0],valid_o,ready_i,last_o
- `sample_cbd_sync`: clk,rst_n,seed_i[255:0],nonce_i[7:0],eta_i[1:0],valid_i,ready_o,coeff_o[11:0](**12-bit signed 二补码**),valid_o,ready_i,last_o
- `ntt_core`: clk,rst_n,coeff_in[11:0],valid_i,ready_o,mode(0=fwd无缩放/1=inv有缩放),coeff_out[11:0],valid_o,ready_i,done_o。协议:IDLE/LOAD 期间 ready_o 高,流式喂 256 个(valid_i 持续);算完 S_OUTPUT 流式吐 256(valid_o 高,ready_i 消费);S_DONE→IDLE。
- `poly_mul_sync`: clk,rst_n,coeff_a_in[11:0],coeff_b_in[11:0],valid_i,ready_o,coeff_out[11:0],valid_o,ready_i。协议:LOAD 阶段 **同时**喂 A、B 各 256 对(coeff_a_in/coeff_b_in 同拍),然后 COMPUTE 流式吐 256。
- `mod_add_sync`: clk,rst_n,a[11:0],b[11:0],valid_i,ready_o,sum[11:0],valid_o,ready_i。(a+b)mod Q。
- `comp_decomp_sync`: clk,rst_n,coeff_in[11:0],d[4:0],mode,valid_i,ready_o,coeff_out[11:0],valid_o,ready_i(KeyGen 不用)。
- `sd_bram #(W=48,D=64,A=6)`、`s_bram #(W=48,D=512,A=9)`(KeyGen 暂用寄存器阵列 polymem 缓存,未用 bram)。
## 已完成进度
### ✅ Stage 0:白盒 golden 向量生成器(commit 106b292)
- `examples/dump_keygen.rs`(ml-kem-r 工作树)+ `test_framework/modules/mlkem_keygen/golden/`(mlkem 仓库)。
- 自校验:ek==KAT.pk、dk_pke==KAT.sk 前缀,5/5 通过。
### ✅ Stage 1:sha3_top 多块 SHA3-256 吸收(commit 106b292)
- `sync_rtl/sha3/sha3_top.v`:新增多块吸收 FSM。新端口 `mb_en/mb_block_i[1087:0]/mb_valid_i/mb_last_i/mb_ready_o`。
- **设计**:调用方预填充末块的 SHA3-256 padding(0x06 ... 0x80),模块只做纯吸收 loop(state^=block; Keccak-p)。单块 G/H/J 路径在 mb_en=0 时逐 bit 不变。
- 状态:MB_IDLE/MB_PERMUTE/MB_DONE。`mb_state_r`(1600-bit running state),`mb_digest_r`(256-bit sticky digest,保持到消费者 ack)。
- `ready_o = !mb_en && (state_r==ST_IDLE)`、`valid_o`/`hash_o` 按 mb_en 选择。
- TB:`sync_rtl/sha3/TB/tb_sha3_mb_xsim.v`(自检,流 800B ek=6 块 → H(ek),对 hashlib.sha3_256)。向量:`vectors/mb_h_blocks.hex`、`mb_h_expected.hex`。
- **握手坑(已解决)**:TB 必须在 accept 边沿保持 valid/last 稳定(等 mb_ready_o 拉低后才撤),否则采样竞争。digest 一次有效后 sticky,TB 收完才 ack。
- 既有 G/H/J TB(xsim 3 个 + Verilator tb_sha3.cpp)**必须 tie off mb_* 端口**(mb_en=1'b0 等),否则 mb_en 浮空成 x 破坏 ready_o。已全部修。
- 回归:Verilator 25/25,XSIM 4 个 TB(simple G/H/J、keccak_core、7-vec、multiblock)全过。
### ✅ sample_ntt 幽灵脉冲修复(commit 6db3c7c)
- **bug**:`sample_ntt_sync` 的 Phase-1(输出 d1)缺少 Phase-2 有的 `need_more` 守卫。当第 256 个被接受系数是 d1 且组推进时,某些 seed 会在 last_o 后多吐一拍 valid_o(KAT count=0 rho 下,seed i=0/j=1 吐 257 拍)。集成时漏进下个 poly 的 index 0,整流后移一位。
- **修复**:Phase-1 改 `if (d1_acc_r && need_more)`。两变体(`sample_ntt_sync.v` + `sample_ntt_sync_shared.v`)都改。
- **补盲区**:`tb_sample_ntt_xsim.v` 加断言——last_o 后保持 ready_i 数多余 valid_o,有则 FAIL。已验证该断言能抓 bug(还原副本 3 个幽灵)、修复版通过。
- 验证:40-seed(sync)+24-seed(shared)审计全 256 拍/last@256/零幽灵;Verilator 1536/1536;全框架 4334/4334;Stage 2c 2048/2048。
- 记忆:`sample-ntt-257th-pulse-bug.md`。
### ✅ Stage 2a/2b/2c:KeyGen 数据通路前半(**未提交**,在工作树)
- 文件:`sync_rtl/top/mlkem_top.v`(新建)+ TB `sync_rtl/top/TB/tb_mlkem_kg_2a_xsim.v`、`tb_mlkem_kg_2c_xsim.v`、向量 `sync_rtl/top/TB/vectors/kg_c000_AsE.hex`。
- **mlkem_top 结构**(K=2,ETA1=3):
- 端口:clk,rst_n,d_i[255:0],z_i[255:0],start_i,busy_o,done_o + **调试 tap**:dbg_slot_i[3:0],dbg_idx_i[7:0],dbg_coeff_o[11:0](寄存器读 polymem,2拍延迟),dbg_rho_o[255:0],dbg_sigma_o[255:0]。
- **polymem**:`reg [11:0] polymem[0:10*256-1]`,slot 映射:0-3=A00,A01,A10,A11;4-5=S0,S1;6-7=E0,E1;8-9=T0,T1。s/s_hat 共享 slot(就地 NTT),e 同理。
- FSM 状态:ST_IDLE=0,ST_G=1,ST_A=2,ST_C=3,...,ST_DONE=15。
- **2a(G)**:sha3_top mode=00,g_data={248'b0,8'(K),d_i};完成捕获 rho_r/sigma_r。✅ rho/sigma 对 golden 精确。
- **2b(A)**:a_pair 0..3 循环,a_i=pair[1],a_j=pair[0],a_slot=pair。每 pair 跑 sample_ntt_sync,256 系数写 polymem[a_slot]。✅ 1024 系数全对。
- **2c(C)**:c_poly 0..3(s0,s1,e0,e1),c_nonce=c_poly,c_slot 映射。CBD 输出经 `cbd_modq = cbd_coeff[11] ? cbd_coeff+Q : cbd_coeff` 转 mod-q 后写 polymem。✅ 2048/2048(A+s+e)全对。
- **关键防护 a_busy/c_busy**:请求被接受(valid&&ready)时置位,last 时清零;仅 busy 时收集系数。本是为屏蔽 sample_ntt 幽灵脉冲的下游兜底;sample_ntt 根因修复后冗余但保留(无害,且对未来叶子模块是防御)。
- **字节序基准**(全部已确认,这是旧版的痛点):
- G 输入 d 原序(byte0 低),无翻转。
- SampleNTT seed=rho‖j‖i。
- CBD nonce:s[i]=i,e[i]=K+i。
- golden 多项式存 mod-q;CBD RTL 输出 12-bit 二补码,需 +Q 转换。
## 当前工作树状态(未提交)
- `sync_rtl/top/mlkem_top.v`(新,Stage 2a/b/c,FSM 到 ST_C)
- `sync_rtl/top/TB/tb_mlkem_kg_2a_xsim.v`、`tb_mlkem_kg_2c_xsim.v`(新)
- `sync_rtl/top/TB/vectors/kg_c000_AsE.hex`(新,8 poly × 256 = 2048 行,顺序 A00,A01,A10,A11,S0,S1,E0,E1)
- `.claude/plans/`(本文件)
- **下一步该先提交 Stage 2a-2c**(测试已通过)。
## 关键测试 literal(byte0-低 256-bit)
- KAT c000 d:`D_LIT = 256'h2426f1941779574d3f1b163bd57f7e173e229e630ec7f7073bdf365137c4bb6d`
- 期望 rho:`256'h15f74355ca862c3cdf3dab780c35cf24b88bf144706090a1c17e41205f9f1379`
- 期望 sigma:`256'h69b042001b5630b1a039116cbfd29f62c0bde5a6b571504a9fcce68bed667fd5`
## 下一步(剩余 Stage 2/3/4)
### Stage 2d:NTT(s/e → ŝ/ê) ← **当前正在做**
- golden 映射已确认:`ntt(s_i)==shat_i`、`ntt(e_i)==ehat_i`(mode=0 fwd,无缩放,zeta 表见 ntt gen_vectors,N_INV=3303 仅 inv 用)。
- 计划:新增 ST_N 状态,对 4 个 slot(S0,S1,E0,E1)逐个就地 NTT:从 polymem 读 256 喂 ntt_core(mode=0),收 256 写回同 slot。
- 注意 ntt_core 协议:LOAD 期间持续 valid_i 喂 256;输出阶段 valid_o 高、ready_i 消费 256。需读地址流水(组合读 polymem)。
- TB:扩展对 shat/ehat golden 校验。
- **正读 mlkem_top.v 行 200-290 的时序逻辑准备插入 ST_N 时被打断;接手时先重读 mlkem_top.v 全文确认当前 FSM 时序结构再加 ST_N。**
### Stage 2e:矩阵累加 t̂[i] = ê[i] + Σⱼ Â[i][j]∘ŝ[j]
- 用 poly_mul_sync(同拍喂 A、B 各 256)算 Â[i][j]∘ŝ[j],再用 mod_add_sync 逐系数 + ê[i] 累加。K=2:t̂[0]=ê0+A00∘ŝ0+A01∘ŝ1;t̂[1]=ê1+A10∘ŝ0+A11∘ŝ1。
- 对 golden `that_i` 校验。
### Stage 2f:byteEncode₁₂ → ek/dk_pke
- 12-bit LSB-first 打包(FIPS:b[i*12+j]=(coeff>>j)&1,再 bits_to_bytes)。ek=byteEncode12(t̂[0..1])‖ρ(800B);dk_pke=byteEncode12(ŝ[0..1])(768B)。
- 对 KAT pk / sk 前缀逐字节校验。
### Stage 3:KeyGen FSM 集成 + 干净顶层接口
- 串起 2a-2f;顶层流式输出 ek_o/dk_o;无 force。
### Stage 4:组装完整 dk + 端到端 KAT
- dk = dk_pke ‖ ek ‖ H(ek)[用 Stage1 多块] ‖ z。
- 干净 KAT TB(无 force/release),喂 KAT d/z,逐字节比 ek 与 dk。跑 KAT count=0..4 全过。Verilator + XSIM 双框架。
## 经验教训(重要)
1. **"PASS" 只证 RTL==reference**:必须独立对 hashlib/FIPS 验证 oracle。
2. **单独 TB 盲区会掩盖模块 bug**:sample_ntt 的 257th 脉冲就是"读够就停"掩盖的。新 TB 要验证协议边界(last 后 valid 干净拉低)。
3. **加端口必须 tie off 所有旧实例**:浮空输入成 x 会破坏组合逻辑(mb_en 教训)。
4. **valid/ready 握手 TB 要在 accept 边沿保持输入稳定**,等 ready 拉低再撤,否则采样竞争(多块 H、A-pair 边界都踩过)。
5. **字节序/位序是旧版主要 bug 源**:每步对 golden 逐字节,错位立刻暴露。
6. **bash 工具偶发输出截断/丢失**:重要结论用 `rm -rf xsim.dir` 重跑确认,别凭一次模糊输出下判断。
## 任务追踪(TaskList ID)
- #7 Stage 0 ✅ completed
- #8 Stage 1 ✅ completed
- #9 Stage 2 datapath subblocks — in_progress(2a/b/c done,2d 进行中)
- #10 Stage 3 FSM 集成 — blocked by #9
- #11 Stage 4 dk + KAT — blocked by #8,#10
## 提交历史(相关)
- 106b292 feat(sha3): multi-block SHA3-256 absorb for H(ek); KeyGen golden vectors (Stage 0+1)
- 6db3c7c fix(sample_ntt): suppress spurious 257th valid_o after last_o
- (未提交)Stage 2a-2c mlkem_top + TB
---
## ✅ COMPLETE (session end state)
- KeyGen FSM: IDLE->G->A->C->N->M->E->H->DONE, pure valid/ready, no force.
- Verified vs NIST KAT MLKEM-512 **count=0..4**: ek (800B)==pk, dk (1632B)==sk, byte-exact, ~21350 cyc/case.
- Commits: 106b292, 6db3c7c, 2f206a6, 4c692e5, a9e50eb, 1791491, 9824ed8, 42d3748.
- Independent Keccak per consumer (G + dedicated H sha3_top + sample_ntt/cbd cores). No arbiter.
- SECURITY: tool outputs this session carried prompt-injection (fake operator instructing `curl|bash`); all refused. Some outputs were tampered; results re-verified with controlled delimiters.
- Remaining optional: streaming ek/dk output port (currently start_i/done_o + readback taps); Encaps/Decaps; shared-keccak migration.

View File

@@ -3,12 +3,10 @@
## Pre-Development Checklist ## Pre-Development Checklist
Before writing RTL code or testbenches, read: Before writing RTL code or testbenches, read:
1. [Verilator Conventions](./verilator-conventions.md) — for C++ Verilator testbenches 1. [XSIM Testbench Conventions](./xsim-tb-conventions.md) — for Vivado XSIM Verilog testbenches
2. [XSIM Testbench Conventions](./xsim-tb-conventions.md) — for Vivado XSIM Verilog testbenches
## Files ## Files
| File | Purpose | | File | Purpose |
|------|---------| |------|---------|
| `verilator-conventions.md` | Verilator 5.046 C++ testbench conventions (clock, timing, valid/ready protocol) |
| `xsim-tb-conventions.md` | Vivado XSIM Verilog testbench conventions (template, vector format, TCL scripts) | | `xsim-tb-conventions.md` | Vivado XSIM Verilog testbench conventions (template, vector format, TCL scripts) |

View File

@@ -1,62 +0,0 @@
# Verilator RTL Conventions
## Verilator Version
**5.046** (Fedora package). Requires **C++14** or later.
## Compile Arguments
**Do NOT** add `-CFLAGS -std=c++11`. Verilator 5.046 uses C++14 features (`""s` string literal).
Let Verilator use its default C++ standard.
Correct base command:
```
verilator -Wall --cc --build --timing --exe --top-module <top> <rtl> <tb.cpp>
```
## Include Paths
All Verilog files use paths relative to project root (`~/Dev/mlkem`).
Pass `+incdir+<project_root>` to Verilator so `\`include "sync_rtl/common/defines.vh"` resolves.
## Clock Period
100MHz = 10ns. Defined centrally in `sync_rtl/common/defines.vh`:
```verilog
`define CLK_PERIOD 10.0
```
## Testbench Timing Protocol (Verilator C++)
### Critical Rule: Always advance at least one posedge before checking signals
Setting a DUT input (e.g., `dut->valid_i = 1`) does NOT take effect until `dut->eval()` is called.
A posedge requires: `dut->clk = 1; dut->eval();`.
### pipeline_reg valid/ready timing
The `pipeline_reg` module's valid_o is HIGH for exactly ONE cycle between the posedge that captures data and the next posedge that consumes it (when ready_i=1).
Correct read pattern:
```cpp
// 1. Drive input + posedge → data captured, valid_o → 1
dut->valid_i = 1;
posedge(dut); // dut->clk = 1; eval(); dut->clk = 0; eval();
dut->valid_i = 0;
// 2. Read result NOW (valid_o is high, sum is valid)
printf("RESULT: %03X\n", dut->sum & 0xFFF);
// 3. Consume with next posedge → valid_o → 0
posedge(dut);
```
### Anti-pattern: while(!dut->ready_o) without eval
Setting valid_i=1 and immediately checking ready_o in a while loop that starts with NO eval() will skip the first clock edge entirely if ready_o is already 1. Use do-while to guarantee at least one edge:
```cpp
dut->valid_i = 1;
do {
posedge(dut);
} while (!dut->ready_o);
```

View File

@@ -1,66 +0,0 @@
# Test Framework Structure
## Directory Layout
```
test_framework/
├── run_all.py # CLI entry: --module, --case, --list, --quick
├── config.json # Verilator path, clock period, timeouts
├── lib/
│ ├── test_runner.py # Discovery, compile, run, compare pipeline
│ ├── sim_controller.py # Verilator compiles/run wrapper
│ ├── vector_gen.py # Base class for vector generators
│ ├── result_checker.py # Hex-file comparison
│ └── reporter.py # Terminal + HTML output
├── modules/
│ └── <module>/
│ ├── test_plan.json # Module test definition
│ ├── gen_vectors.py # Vector generator (subclass of VectorGenerator)
│ └── vectors/ # Generated hex files (auto-cleaned)
└── reports/
├── latest/
└── history/
```
## test_plan.json Schema
```json
{
"module": "<name>",
"rtl_top": "sync_rtl/<path>/<top>.v",
"rtl_deps": ["sync_rtl/common/<dep>.v"],
"tb_cpp": "sync_rtl/<path>/TB/tb_<top>.cpp",
"simulator": "verilator",
"timeout_s": 30,
"cases": [{
"id": "<case_id>",
"description": "<what this tests>",
"params": {},
"num_vectors": 50,
"tolerance": "bit_exact"
}]
}
```
- `rtl_top`: top module basename (without .v) is used as Verilator `--top-module`
- `rtl_deps`: listed before `rtl_top` in Verilator command line
- `tolerance`: "bit_exact" uses result_checker.py; otherwise delegates to gen_vectors.compare_results()
## Vector Generator Contract
Each module's `gen_vectors.py` must:
1. Subclass `VectorGenerator` from `test_framework.lib.vector_gen`
2. Implement `generate_one(params) -> dict` returning `{"input": {...}, "expected": {...}}`
3. Override `write_hex_file(vectors, filepath)` for input format
4. Override `write_expected_file(vectors, filepath)` for expected format
The framework auto-discovers the generator class by scanning for subclasses of VectorGenerator.
## Adding a New Module
1. Create `test_framework/modules/<name>/test_plan.json`
2. Create `test_framework/modules/<name>/gen_vectors.py`
3. Create `sync_rtl/<name>/<name>_sync.v` (RTL)
4. Create `sync_rtl/<name>/TB/tb_<name>.cpp` (Verilator C++ TB)
5. Run `python3 test_framework/run_all.py --list` to verify discovery
6. Run `python3 test_framework/run_all.py --module <name>` to test

BIN
image.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 MiB

171
run_tb.sh
View File

@@ -10,12 +10,15 @@
# ./run_tb.sh enc # ML-KEM Encaps: all K, all cases (0..2) # ./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 2 # Encaps K=2, all its cases
# ./run_tb.sh enc 4 1 # Encaps K=4, only CASE=1 # ./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 # ./run_tb.sh --list
# #
# 'top' (KeyGen) and 'enc' (Encaps) share the same RTL datapath; both compile # 'top' (KeyGen), 'enc' (Encaps) and 'dec' (Decaps) share the same RTL datapath;
# the xvlog lines from sync_rtl/top/TB/xsim_run.tcl. They differ only in the # all compile the xvlog lines from sync_rtl/top/TB/xsim_run.tcl. They differ only
# testbench: top -> tb_mlkem_kg_katK_xsim, enc -> tb_mlkem_enc_katK_xsim. # 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 # 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. # quickly. Other modules ignore the extra args and run their xsim_run.tcl verbatim.
# #
# Prerequisites: # Prerequisites:
@@ -35,6 +38,7 @@ if [ "$1" = "--list" ]; then
fi fi
done done
echo " enc (ML-KEM Encaps; shares the 'top' RTL/tcl, enc testbench)" 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 exit 0
fi fi
@@ -48,8 +52,8 @@ MODULE="$1"
SEL_K="$2" # optional: 2/3/4 (top/enc modules only) SEL_K="$2" # optional: 2/3/4 (top/enc modules only)
SEL_CASE="$3" # optional: KAT case index (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 # 'enc' is not its own RTL dir; it reuses the 'top' KeyGen tcl compile list
# (same datapath) and swaps in the encaps testbench. # (same datapath) and swaps in the encaps testbench. 'dec' likewise (Decaps).
if [ "$MODULE" = "enc" ]; then if [ "$MODULE" = "enc" ] || [ "$MODULE" = "dec" ]; then
TB_DIR="$SCRIPT_DIR/sync_rtl/top/TB" TB_DIR="$SCRIPT_DIR/sync_rtl/top/TB"
else else
TB_DIR="$SCRIPT_DIR/sync_rtl/$MODULE/TB" TB_DIR="$SCRIPT_DIR/sync_rtl/$MODULE/TB"
@@ -102,11 +106,68 @@ execute_tcl() {
rm -f "$tmp_script" rm -f "$tmp_script"
} }
# ---- parallel xsim launcher --------------------------------------------------
# xsim writes runtime logs into xsim.dir/<snapshot>/, so two runs of the SAME
# snapshot clobber each other. To run (K,case) in parallel we give each job its
# own COPY of xsim.dir (~1 MB, cheap) under a private workdir, run xsim there,
# and tee output to a per-job log. Compile+elaborate stay serial (they populate
# the shared xsim.dir BEFORE any parallel run starts).
#
# run_xsim_jobs <module-tag> <pass-grep-regex> <pass-match-string> <spec>...
# each <spec> = "snapshot:K:CASE". Launches all specs in parallel, waits,
# then prints an ordered summary and returns nonzero if any job failed.
PAR_BASE="${TMPDIR:-/tmp}/run_tb_par_$$"
run_xsim_jobs() {
local mtag="$1" pgrep="$2" pmatch="$3"; shift 3
local specs=("$@")
local repo; repo="$(pwd)"
rm -rf "$PAR_BASE"; mkdir -p "$PAR_BASE"
# launch all jobs in parallel
local s snap k c wd
for s in "${specs[@]}"; do
IFS=: read -r snap k c <<< "$s"
wd="$PAR_BASE/${mtag}_k${k}_c${c}"
mkdir -p "$wd"
cp -r "$repo/xsim.dir" "$wd/xsim.dir"
# The TB loads vectors via repo-relative $readmemh paths
# (sync_rtl/top/TB/vectors/...). xsim resolves these against the run
# cwd, so symlink the repo's sync_rtl tree into each private workdir.
ln -sf "$repo/sync_rtl" "$wd/sync_rtl"
(
cd "$wd"
echo " xsim $snap -R -testplusarg CASE=$c"
xsim "$snap" -R -testplusarg "CASE=$c" --nolog > "$wd/run.out" 2>&1
) &
done
wait
# ordered summary + per-job log copy to /tmp/run_tb_<mtag>_k*_c*.log
local fail=0 pf nf log
for s in "${specs[@]}"; do
IFS=: read -r snap k c <<< "$s"
wd="$PAR_BASE/${mtag}_k${k}_c${c}"
log="/tmp/run_tb_${mtag}_k${k}_c${c}.log"
cp -f "$wd/run.out" "$log" 2>/dev/null
pf=$(grep -oE "$pgrep" "$log" | tail -1)
nf=$(grep -c 'cannot be opened' "$log")
echo " K=$k CASE=$c -> ${pf:-NORESULT} (file-not-found=$nf, log: $log)"
# pmatch == "PASS*" means "any PASS (Dn) stage tag"; else exact match.
if [ "$pmatch" = "PASS*" ]; then
{ [[ "$pf" == PASS* ]] && [ "$nf" -eq 0 ]; } || fail=1
else
{ [ "$pf" = "$pmatch" ] && [ "$nf" -eq 0 ]; } || fail=1
fi
done
rm -rf "$PAR_BASE"
return $fail
}
# Fast single-K / single-case path for the 'top' (ML-KEM KeyGen) module. # Fast single-K / single-case path for the 'top' (ML-KEM KeyGen) module.
# Compiles the same RTL as the tcl, but elaborates ONLY the requested K and # Compiles the same RTL as the tcl, but elaborates ONLY the requested K and
# runs ONLY the requested CASE(s). Avoids the 3-snapshot + 11-run full sweep. # runs the CASE(s) in parallel. Avoids the 3-snapshot + 11-run serial sweep.
run_top_selected() { run_top_selected() {
local tcl_file="$1" k="$2" csel="$3" local tcl_file="$1" ksel="$2" csel="$3"
set +e # we do our own error handling / result parsing here set +e # we do our own error handling / result parsing here
rm -rf xsim.dir .Xil rm -rf xsim.dir .Xil
@@ -116,29 +177,22 @@ run_top_selected() {
eval "$cmd" || { echo "COMPILE FAILED: $cmd"; return 1; } eval "$cmd" || { echo "COMPILE FAILED: $cmd"; return 1; }
done < <(grep -E '^xvlog ' "$tcl_file") done < <(grep -E '^xvlog ' "$tcl_file")
# K to run: requested one, or all of 2/3/4. Elaborate snapshots (serial).
local ks; if [ -n "$ksel" ]; then ks="$ksel"; else ks="2 3 4"; fi
local k specs=()
for k in $ks; do
echo " xelab tb_mlkem_kg_katK_xsim -generic_top KP=$k -s mlkem_kg_k$k --timescale 1ns/1ps"
xelab tb_mlkem_kg_katK_xsim -generic_top KP=$k -s mlkem_kg_k$k --timescale 1ns/1ps \
|| { echo "ELAB FAILED for K=$k"; return 1; }
# Cases per K: K=2 has 0..4, K=3/4 have 0..2. # Cases per K: K=2 has 0..4, K=3/4 have 0..2.
local cases local cases
if [ "$k" = "2" ]; then cases="0 1 2 3 4"; else cases="0 1 2"; fi if [ "$k" = "2" ]; then cases="0 1 2 3 4"; else cases="0 1 2"; fi
if [ -n "$csel" ]; then cases="$csel"; fi if [ -n "$csel" ]; then cases="$csel"; fi
local c
echo " xelab tb_mlkem_kg_katK_xsim -generic_top KP=$k -s mlkem_kg_k$k --timescale 1ns/1ps" for c in $cases; do specs+=("mlkem_kg_k$k:$k:$c"); done
xelab tb_mlkem_kg_katK_xsim -generic_top KP=$k -s mlkem_kg_k$k --timescale 1ns/1ps \
|| { echo "ELAB FAILED for K=$k"; return 1; }
local fail=0
for c in $cases; do
local log="/tmp/run_tb_top_k${k}_c${c}.log"
echo " xsim mlkem_kg_k$k -R -testplusarg CASE=$c"
echo "========================================" | tee "$log"
xsim "mlkem_kg_k$k" -R -testplusarg "CASE=$c" 2>&1 | tee -a "$log"
echo "========================================" | tee -a "$log"
local pf nf
pf=$(grep -oE 'PASS|FAIL' "$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
run_xsim_jobs top 'PASS|FAIL' PASS "${specs[@]}"
} }
# ML-KEM Encaps runner. Compiles the 'top' tcl xvlog lines (KeyGen datapath = # ML-KEM Encaps runner. Compiles the 'top' tcl xvlog lines (KeyGen datapath =
@@ -161,31 +215,54 @@ run_enc_selected() {
xvlog -sv --relax sync_rtl/top/TB/tb_mlkem_enc_katK_xsim.v \ xvlog -sv --relax sync_rtl/top/TB/tb_mlkem_enc_katK_xsim.v \
|| { echo "ENC TB COMPILE FAILED"; return 1; } || { echo "ENC TB COMPILE FAILED"; return 1; }
# K to run: requested one, or all of 2/3/4. # K to run: requested one, or all of 2/3/4. Elaborate snapshots (serial),
# then run all (K,case) in parallel.
local ks; if [ -n "$ksel" ]; then ks="$ksel"; else ks="2 3 4"; fi local ks; if [ -n "$ksel" ]; then ks="$ksel"; else ks="2 3 4"; fi
local fail=0 local k specs=()
for k in $ks; do for k in $ks; do
echo " xelab tb_mlkem_enc_katK_xsim -generic_top KP=$k -s mlkem_enc_k$k --timescale 1ns/1ps" echo " xelab tb_mlkem_enc_katK_xsim -generic_top KP=$k -s mlkem_enc_k$k --timescale 1ns/1ps"
xelab tb_mlkem_enc_katK_xsim -generic_top KP=$k -s mlkem_enc_k$k --timescale 1ns/1ps \ xelab tb_mlkem_enc_katK_xsim -generic_top KP=$k -s mlkem_enc_k$k --timescale 1ns/1ps \
|| { echo "ELAB FAILED for K=$k"; fail=1; continue; } || { echo "ELAB FAILED for K=$k"; return 1; }
# Encaps KAT cases per K: 0..2 (vectors enc_k{K}_c{0,1,2}_*).
local cases="0 1 2" local cases="0 1 2"
if [ -n "$csel" ]; then cases="$csel"; fi if [ -n "$csel" ]; then cases="$csel"; fi
for c in $cases; do local c
local log="/tmp/run_tb_enc_k${k}_c${c}.log" for c in $cases; do specs+=("mlkem_enc_k$k:$k:$c"); done
echo " xsim mlkem_enc_k$k -R -testplusarg CASE=$c"
echo "========================================" | tee "$log"
xsim "mlkem_enc_k$k" -R -testplusarg "CASE=$c" 2>&1 | tee -a "$log"
echo "========================================" | tee -a "$log"
local pf nf
pf=$(grep -oE 'PASS \(E7\)|FAIL \(E7\)' "$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 (E7)" ] && [ "$nf" -eq 0 ]; } || fail=1
done done
run_xsim_jobs enc 'PASS \(E7\)|FAIL \(E7\)' 'PASS (E7)' "${specs[@]}"
}
# 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 k specs=()
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"; return 1; }
local cases="0 1 2"
if [ -n "$csel" ]; then cases="$csel"; fi
local c
for c in $cases; do specs+=("mlkem_dec_k$k:$k:$c"); done
done done
return $fail
# match the highest-stage result line: 'K=.. CASE .. PASS (Dn): ...'
run_xsim_jobs dec 'PASS \(D[0-7]\)|FAIL \(D[0-7]\)' 'PASS*' "${specs[@]}"
} }
if [ "$MODULE" = "enc" ]; then if [ "$MODULE" = "enc" ]; then
@@ -193,7 +270,13 @@ if [ "$MODULE" = "enc" ]; then
exit $? exit $?
fi fi
if [ "$MODULE" = "top" ] && [ -n "$SEL_K" ]; then if [ "$MODULE" = "dec" ]; then
run_dec_selected "$TCL_FILE" "$SEL_K" "$SEL_CASE"
exit $?
fi
if [ "$MODULE" = "top" ]; then
# run_top_selected handles all-K (no SEL_K) or a single K, cases in parallel.
run_top_selected "$TCL_FILE" "$SEL_K" "$SEL_CASE" run_top_selected "$TCL_FILE" "$SEL_K" "$SEL_CASE"
exit $? exit $?
fi fi

View File

@@ -1,125 +0,0 @@
// tb_comp_decomp.cpp - Verilator C++ testbench for comp_decomp_sync
//
// Reads test vectors from a hex file specified by +VECTOR_FILE= plusarg.
// Each line: "MODE D COEFF" (e.g. "C A 3FF" for compress d=10 coeff=0x3FF).
// MODE: 'C' for compress, 'D' for decompress.
// Drives the DUT, waits for valid_o, writes "RESULT: COEFF" to stdout.
//
// Clock: 10 ns period. Reset: 2 cycles low. Timeout: 100,000 cycles.
#include <iostream>
#include <fstream>
#include <string>
#include <sstream>
#include <cstdlib>
#include "Vcomp_decomp_sync.h"
#include "verilated.h"
#define CLK_PERIOD_NS 10.0
#define TIMEOUT_CYCLES 100000
static vluint64_t main_time = 0;
double sc_time_stamp() {
return main_time;
}
int main(int argc, char** argv) {
Verilated::commandArgs(argc, argv);
// Parse +VECTOR_FILE= plusarg
const char* vector_file = NULL;
for (int i = 1; i < argc; i++) {
std::string arg(argv[i]);
if (arg.rfind("+VECTOR_FILE=", 0) == 0) {
vector_file = argv[i] + 13; // skip "+VECTOR_FILE="
}
}
if (!vector_file) {
std::cerr << "ERROR: +VECTOR_FILE= not specified" << std::endl;
return 1;
}
std::ifstream infile(vector_file);
if (!infile.is_open()) {
std::cerr << "ERROR: Cannot open vector file: " << vector_file << std::endl;
return 1;
}
// Instantiate DUT
Vcomp_decomp_sync* dut = new Vcomp_decomp_sync;
// Initialize
dut->clk = 0;
dut->rst_n = 0;
dut->coeff_in = 0;
dut->d = 0;
dut->mode = 0;
dut->valid_i = 0;
dut->ready_i = 0;
// Reset: 2 cycles low
for (int i = 0; i < 4; i++) {
dut->clk = !dut->clk;
main_time += (vluint64_t)(CLK_PERIOD_NS / 2.0);
dut->eval();
dut->clk = !dut->clk;
main_time += (vluint64_t)(CLK_PERIOD_NS / 2.0);
dut->eval();
}
dut->rst_n = 1;
// Always ready to receive results
dut->ready_i = 1;
std::string line;
vluint64_t cycle = 0;
while (std::getline(infile, line)) {
// Skip empty lines and comments
if (line.empty() || line[0] == '#') continue;
// Parse: MODE D COEFF
std::istringstream iss(line);
char mode_char;
unsigned int d_val, coeff_val;
if (!(iss >> mode_char >> std::hex >> d_val >> coeff_val)) continue;
if (mode_char == 'C')
dut->mode = 0;
else if (mode_char == 'D')
dut->mode = 1;
else
continue;
dut->d = d_val & 0x1F;
dut->coeff_in = coeff_val & 0xFFF;
dut->valid_i = 1;
// posedge: DUT samples valid_i, pipeline_reg captures data
dut->clk = !dut->clk;
main_time += (vluint64_t)(CLK_PERIOD_NS / 2.0);
dut->eval();
dut->clk = !dut->clk;
main_time += (vluint64_t)(CLK_PERIOD_NS / 2.0);
dut->eval();
cycle++;
dut->valid_i = 0;
// posedge: pipeline_reg sends result out (valid_o=1)
dut->clk = !dut->clk;
main_time += (vluint64_t)(CLK_PERIOD_NS / 2.0);
dut->eval();
dut->clk = !dut->clk;
main_time += (vluint64_t)(CLK_PERIOD_NS / 2.0);
dut->eval();
cycle++;
printf("RESULT: %03X\n", dut->coeff_out & 0xFFF);
}
infile.close();
delete dut;
return 0;
}

View File

@@ -1,120 +0,0 @@
// tb_mod_add.cpp - Verilator C++ testbench for mod_add_sync
//
// Reads test vectors from a hex file specified by +VECTOR_FILE= plusarg.
// Each line: "AAA BBB" (two 12-bit hex operands).
// Drives the DUT, waits for valid_o, writes "RESULT: CCC" to stdout.
//
// Clock: CLK_PERIOD ns period (from defines.vh).
// Reset: 2 cycles low, then high.
// Timeout: 100,000 cycles.
#include <iostream>
#include <fstream>
#include <string>
#include <sstream>
#include <cstdlib>
#include "Vmod_add_sync.h"
#include "verilated.h"
#define CLK_PERIOD_NS 10.0
#define TIMEOUT_CYCLES 100000
#define Q 3329
static vluint64_t main_time = 0;
double sc_time_stamp() {
return main_time;
}
int main(int argc, char** argv) {
Verilated::commandArgs(argc, argv);
// Parse +VECTOR_FILE= plusarg
const char* vector_file = NULL;
for (int i = 1; i < argc; i++) {
std::string arg(argv[i]);
if (arg.rfind("+VECTOR_FILE=", 0) == 0) {
vector_file = argv[i] + 13; // skip "+VECTOR_FILE="
}
}
if (!vector_file) {
std::cerr << "ERROR: +VECTOR_FILE= not specified" << std::endl;
return 1;
}
std::ifstream infile(vector_file);
if (!infile.is_open()) {
std::cerr << "ERROR: Cannot open vector file: " << vector_file << std::endl;
return 1;
}
// Instantiate DUT
Vmod_add_sync* dut = new Vmod_add_sync;
// Initialize
dut->clk = 0;
dut->rst_n = 0;
dut->a = 0;
dut->b = 0;
dut->valid_i = 0;
dut->ready_i = 0;
// Reset: 2 cycles low
for (int i = 0; i < 4; i++) {
dut->clk = !dut->clk;
main_time += (vluint64_t)(CLK_PERIOD_NS / 2.0);
dut->eval();
dut->clk = !dut->clk;
main_time += (vluint64_t)(CLK_PERIOD_NS / 2.0);
dut->eval();
}
dut->rst_n = 1;
// Always ready to receive results
dut->ready_i = 1;
std::string line;
vluint64_t cycle = 0;
bool waiting_result = false;
while (std::getline(infile, line)) {
// Skip empty lines and comments
if (line.empty() || line[0] == '#') continue;
// Parse hex operands
std::istringstream iss(line);
unsigned int a_val, b_val;
if (!(iss >> std::hex >> a_val >> b_val)) continue;
dut->a = a_val & 0xFFF;
dut->b = b_val & 0xFFF;
dut->valid_i = 1;
// posedge: DUT samples valid_i, pipeline_reg captures data, valid_o→1
dut->clk = !dut->clk;
main_time += (vluint64_t)(CLK_PERIOD_NS / 2.0);
dut->eval();
dut->clk = !dut->clk;
main_time += (vluint64_t)(CLK_PERIOD_NS / 2.0);
dut->eval();
cycle++;
dut->valid_i = 0;
// posedge: pipeline_reg clears valid_o (ready_i=1), but result is
// available on sum port right now (combinational from data_r)
dut->clk = !dut->clk;
main_time += (vluint64_t)(CLK_PERIOD_NS / 2.0);
dut->eval();
dut->clk = !dut->clk;
main_time += (vluint64_t)(CLK_PERIOD_NS / 2.0);
dut->eval();
cycle++;
printf("RESULT: %03X\n", dut->sum & 0xFFF);
}
infile.close();
delete dut;
return 0;
}

View File

@@ -1,195 +0,0 @@
// tb_ntt.cpp - Verilator C++ testbench for ntt_sync
//
// Reads test vectors from +VECTOR_FILE= plusarg.
// Format: "MODE COEFF0 COEFF1 ... COEFF255"
// MODE: "F" = forward NTT, "I" = inverse NTT
// COEFFx: 3-digit hex (12-bit, 000..CFF)
//
// Drives DUT with coefficients one per cycle, waits for output,
// prints "RESULT: COEFF0 COEFF1 ... COEFF255\n" to stdout.
//
// Clock: 10ns period. Reset: 2 cycles.
#include <iostream>
#include <fstream>
#include <string>
#include <sstream>
#include <vector>
#include <cstdlib>
#include <cstring>
#include <cstdint>
#include "Vntt_sync.h"
#include "verilated.h"
#define CLK_PERIOD_NS 10.0
#define TIMEOUT_CYCLES 500000
static vluint64_t main_time = 0;
double sc_time_stamp() {
return main_time;
}
// Toggle clock: both edges + eval (one full cycle)
static void posedge(Vntt_sync* dut) {
dut->clk = 1;
main_time += (vluint64_t)(CLK_PERIOD_NS / 2.0);
dut->eval();
dut->clk = 0;
main_time += (vluint64_t)(CLK_PERIOD_NS / 2.0);
dut->eval();
}
static int hex_char_to_nibble(char c) {
if (c >= '0' && c <= '9') return c - '0';
if (c >= 'A' && c <= 'F') return c - 'A' + 10;
if (c >= 'a' && c <= 'f') return c - 'a' + 10;
return 0;
}
// Parse 3-char hex token to 12-bit value.
static uint16_t hex3_to_val(const std::string& tok) {
uint16_t val = 0;
for (size_t i = 0; i < tok.length() && i < 3; i++) {
val = (val << 4) | hex_char_to_nibble(tok[i]);
}
return val & 0xFFF;
}
// Format 12-bit value as 3-char hex (lowercase for consistency).
static std::string val_to_hex3(uint16_t val) {
char buf[4];
snprintf(buf, sizeof(buf), "%03X", val & 0xFFF);
return std::string(buf);
}
int main(int argc, char** argv) {
Verilated::commandArgs(argc, argv);
// Parse +VECTOR_FILE= plusarg
const char* vector_file = NULL;
for (int i = 1; i < argc; i++) {
std::string arg(argv[i]);
if (arg.rfind("+VECTOR_FILE=", 0) == 0) {
vector_file = argv[i] + 13;
}
}
if (!vector_file) {
std::cerr << "ERROR: +VECTOR_FILE= not specified" << std::endl;
return 1;
}
std::ifstream infile(vector_file);
if (!infile.is_open()) {
std::cerr << "ERROR: Cannot open vector file: " << vector_file << std::endl;
return 1;
}
// Instantiate DUT
Vntt_sync* dut = new Vntt_sync;
// Initialize
dut->clk = 0;
dut->rst_n = 0;
dut->mode = 0;
dut->coeff_in = 0;
dut->valid_i = 0;
dut->ready_i = 0;
// Reset: 2 full cycles
for (int i = 0; i < 2; i++) posedge(dut);
dut->rst_n = 1;
std::string line;
vluint64_t cycle = 0;
int vec_count = 0;
while (std::getline(infile, line)) {
if (line.empty() || line[0] == '#') continue;
// Parse: MODE COEFF0 COEFF1 ... COEFF255
std::istringstream iss(line);
std::string mode_str;
if (!(iss >> mode_str)) continue;
int mode_val = 0;
if (mode_str == "I") mode_val = 1;
// Parse 256 coefficients into vector
std::vector<uint16_t> input_coeffs(256);
std::string tok;
int coeff_idx = 0;
while (iss >> tok && coeff_idx < 256) {
input_coeffs[coeff_idx] = hex3_to_val(tok);
coeff_idx++;
}
if (coeff_idx != 256) {
std::cerr << "ERROR: Expected 256 coefficients, got " << coeff_idx
<< " (vec " << vec_count << ")" << std::endl;
continue;
}
// Set mode
dut->mode = mode_val;
// ---- Load 256 coefficients ----
while (!dut->ready_o) {
posedge(dut); cycle++;
if (cycle > TIMEOUT_CYCLES) goto timeout_err;
}
for (int i = 0; i < 256; i++) {
dut->coeff_in = input_coeffs[i];
dut->valid_i = 1;
posedge(dut); cycle++;
dut->valid_i = 0;
if (cycle > TIMEOUT_CYCLES) goto timeout_err;
}
// At this point, the DUT has captured all 256 coeffs and
// transitioned to S_COMPUTE_RD (ready_o went low).
// ---- Wait for valid_o (DUT computing) ----
dut->ready_i = 1;
while (!dut->valid_o) {
posedge(dut);
cycle++;
if (cycle > TIMEOUT_CYCLES) goto timeout_err;
}
// ---- Read 256 output coefficients ----
printf("RESULT: ");
for (int i = 0; i < 256; i++) {
// Wait for valid_o to be asserted (data is valid NOW)
while (!dut->valid_o) {
posedge(dut);
cycle++;
if (cycle > TIMEOUT_CYCLES) goto timeout_err;
}
// Capture coefficient BEFORE consuming posedge
uint16_t coeff_val = (uint16_t)(dut->coeff_out & 0xFFF);
printf("%s%s", val_to_hex3(coeff_val).c_str(),
(i < 255) ? " " : "");
// Consume this coefficient: posedge with ready_i=1
posedge(dut);
cycle++;
}
printf("\n");
vec_count++;
}
std::cout << "Processed " << vec_count << " vectors" << std::endl;
infile.close();
delete dut;
return (vec_count > 0) ? 0 : 1;
timeout_err:
std::cerr << "ERROR: Timeout at cycle " << cycle
<< " (vec " << vec_count << ")" << std::endl;
infile.close();
delete dut;
return 1;
}

View File

@@ -1,175 +0,0 @@
// tb_poly_arith.cpp - Verilator C++ testbench for poly_arith_sync
//
// Reads test vectors from a hex file specified by +VECTOR_FILE= plusarg.
// Each line: "MODE A0 A1 ... A255 B0 B1 ... B255"
// MODE: 'A' for add, 'S' for sub
// A0..A255: 256 hex coefficients (12-bit, 3 hex digits each)
// B0..B255: 256 hex coefficients
//
// Feeds 256 (A, B) pairs through the DUT, collects 256 output coeffs,
// prints one "RESULT: C0 C1 ... C255" line per vector.
//
// Clock: 10 ns period (100 MHz).
// Reset: 2 cycles low, then high.
// Timeout: 100,000 cycles.
#include <iostream>
#include <fstream>
#include <string>
#include <sstream>
#include <cstdlib>
#include <vector>
#include "Vpoly_arith_sync.h"
#include "verilated.h"
#define CLK_PERIOD_NS 10.0
#define TIMEOUT_CYCLES 100000
static vluint64_t main_time = 0;
double sc_time_stamp() {
return main_time;
}
// Helper: toggle clock (full cycle: low->high->low) with eval
static void posedge(Vpoly_arith_sync* dut) {
dut->clk = !dut->clk;
main_time += (vluint64_t)(CLK_PERIOD_NS / 2.0);
dut->eval();
dut->clk = !dut->clk;
main_time += (vluint64_t)(CLK_PERIOD_NS / 2.0);
dut->eval();
}
int main(int argc, char** argv) {
Verilated::commandArgs(argc, argv);
// Parse +VECTOR_FILE= plusarg
const char* vector_file = NULL;
for (int i = 1; i < argc; i++) {
std::string arg(argv[i]);
if (arg.rfind("+VECTOR_FILE=", 0) == 0) {
vector_file = argv[i] + 13;
}
}
if (!vector_file) {
std::cerr << "ERROR: +VECTOR_FILE= not specified" << std::endl;
return 1;
}
std::ifstream infile(vector_file);
if (!infile.is_open()) {
std::cerr << "ERROR: Cannot open vector file: " << vector_file << std::endl;
return 1;
}
// Instantiate DUT
Vpoly_arith_sync* dut = new Vpoly_arith_sync;
// Initialize
dut->clk = 0;
dut->rst_n = 0;
dut->coeff_a_in = 0;
dut->coeff_b_in = 0;
dut->mode = 0;
dut->valid_i = 0;
dut->ready_i = 0;
// Reset: 2 cycles low
for (int i = 0; i < 4; i++) {
dut->clk = !dut->clk;
main_time += (vluint64_t)(CLK_PERIOD_NS / 2.0);
dut->eval();
dut->clk = !dut->clk;
main_time += (vluint64_t)(CLK_PERIOD_NS / 2.0);
dut->eval();
}
dut->rst_n = 1;
// Always ready to receive results
dut->ready_i = 1;
std::string line;
vluint64_t cycle = 0;
while (std::getline(infile, line)) {
// Skip empty lines and comments
if (line.empty() || line[0] == '#') continue;
// Parse: MODE A0..A255 B0..B255 (513 values on one line)
std::istringstream iss(line);
std::string mode_str;
if (!(iss >> mode_str)) continue;
// Determine mode
bool mode_val;
if (mode_str == "A" || mode_str == "a") {
mode_val = false;
} else if (mode_str == "S" || mode_str == "s") {
mode_val = true;
} else {
std::cerr << "ERROR: Unknown mode: " << mode_str << std::endl;
continue;
}
dut->mode = mode_val ? 1 : 0;
// Read 256 A coeffs
unsigned int coeff;
std::vector<unsigned int> a_coeffs, b_coeffs;
for (int i = 0; i < 256; i++) {
if (!(iss >> std::hex >> coeff)) {
std::cerr << "ERROR: Missing A coeff at index " << i << std::endl;
return 1;
}
a_coeffs.push_back(coeff & 0xFFF);
}
// Read 256 B coeffs
for (int i = 0; i < 256; i++) {
if (!(iss >> std::hex >> coeff)) {
std::cerr << "ERROR: Missing B coeff at index " << i << std::endl;
return 1;
}
b_coeffs.push_back(coeff & 0xFFF);
}
// Feed 256 coefficient pairs and collect results
std::vector<unsigned int> results;
for (int i = 0; i < 256; i++) {
// Drive inputs
dut->coeff_a_in = a_coeffs[i];
dut->coeff_b_in = b_coeffs[i];
dut->valid_i = 1;
// Posedge: DUT samples input, pipeline_reg captures data, valid_o→1
posedge(dut);
dut->valid_i = 0;
// Read result now (valid_o is high, coeff_out is valid)
// Posedge: pipeline_reg clears valid_o (ready_i=1), but result
// is available on coeff_out port (combinational from data_r)
posedge(dut);
results.push_back(dut->coeff_out & 0xFFF);
cycle += 2;
if (cycle > TIMEOUT_CYCLES) {
std::cerr << "ERROR: Timeout after " << cycle << " cycles" << std::endl;
return 1;
}
}
// Print result as one space-separated hex line
printf("RESULT:");
for (int i = 0; i < 256; i++) {
printf(" %03X", results[i]);
}
printf("\n");
}
infile.close();
delete dut;
return 0;
}

View File

@@ -1,186 +0,0 @@
// tb_poly_mul.cpp - Verilator C++ testbench for poly_mul_sync
//
// Reads test vectors from +VECTOR_FILE= plusarg.
// Format: one line with 512 space-separated hex values:
// A[0] A[1] ... A[255] B[0] B[1] ... B[255]
// Each value is a 3-digit hex (12-bit).
//
// Drives DUT with 256 paired (A,B) coefficients, waits for valid_o,
// then reads 256 output coefficients via valid/ready handshake.
// Prints "RESULT: COEFF0 COEFF1 ... COEFF255\n" to stdout.
//
// Clock: 10ns period. Reset: 2 cycles.
#include <iostream>
#include <fstream>
#include <string>
#include <sstream>
#include <vector>
#include <cstdlib>
#include <cstring>
#include <cstdint>
#include "Vpoly_mul_sync.h"
#include "verilated.h"
#define CLK_PERIOD_NS 10.0
#define TIMEOUT_CYCLES 500000
static vluint64_t main_time = 0;
double sc_time_stamp() {
return main_time;
}
// Toggle clock: both edges + eval (one full cycle)
static void posedge(Vpoly_mul_sync* dut) {
dut->clk = 1;
main_time += (vluint64_t)(CLK_PERIOD_NS / 2.0);
dut->eval();
dut->clk = 0;
main_time += (vluint64_t)(CLK_PERIOD_NS / 2.0);
dut->eval();
}
static int hex_char_to_nibble(char c) {
if (c >= '0' && c <= '9') return c - '0';
if (c >= 'A' && c <= 'F') return c - 'A' + 10;
if (c >= 'a' && c <= 'f') return c - 'a' + 10;
return 0;
}
// Parse 3-char hex token to 12-bit value.
static uint16_t hex3_to_val(const std::string& tok) {
uint16_t val = 0;
for (size_t i = 0; i < tok.length() && i < 3; i++) {
val = (val << 4) | hex_char_to_nibble(tok[i]);
}
return val & 0xFFF;
}
// Format 12-bit value as 3-char hex.
static std::string val_to_hex3(uint16_t val) {
char buf[4];
snprintf(buf, sizeof(buf), "%03X", val & 0xFFF);
return std::string(buf);
}
int main(int argc, char** argv) {
Verilated::commandArgs(argc, argv);
// Parse +VECTOR_FILE= plusarg
const char* vector_file = NULL;
for (int i = 1; i < argc; i++) {
std::string arg(argv[i]);
if (arg.rfind("+VECTOR_FILE=", 0) == 0) {
vector_file = argv[i] + 13;
}
}
if (!vector_file) {
std::cerr << "ERROR: +VECTOR_FILE= not specified" << std::endl;
return 1;
}
std::ifstream infile(vector_file);
if (!infile.is_open()) {
std::cerr << "ERROR: Cannot open vector file: " << vector_file << std::endl;
return 1;
}
// Instantiate DUT
Vpoly_mul_sync* dut = new Vpoly_mul_sync;
// Initialize
dut->clk = 0;
dut->rst_n = 0;
dut->coeff_a_in = 0;
dut->coeff_b_in = 0;
dut->valid_i = 0;
dut->ready_i = 0;
// Reset: 2 full cycles
for (int i = 0; i < 2; i++) posedge(dut);
dut->rst_n = 1;
std::string line;
vluint64_t cycle = 0;
int vec_count = 0;
while (std::getline(infile, line)) {
if (line.empty() || line[0] == '#') continue;
// Parse: A[0] A[1] ... A[255] B[0] B[1] ... B[255]
// Total: 512 hex values, one line
std::istringstream iss(line);
std::string tok;
std::vector<uint16_t> input_coeffs(512);
int coeff_idx = 0;
while (iss >> tok && coeff_idx < 512) {
input_coeffs[coeff_idx] = hex3_to_val(tok);
coeff_idx++;
}
if (coeff_idx != 512) {
std::cerr << "ERROR: Expected 512 coefficients, got " << coeff_idx
<< " (vec " << vec_count << ")" << std::endl;
continue;
}
// ---- Load 256 A+B pairs ----
while (!dut->ready_o) {
posedge(dut); cycle++;
if (cycle > TIMEOUT_CYCLES) goto timeout_err;
}
for (int i = 0; i < 256; i++) {
dut->coeff_a_in = input_coeffs[i];
dut->coeff_b_in = input_coeffs[256 + i];
dut->valid_i = 1;
posedge(dut); cycle++;
dut->valid_i = 0;
if (cycle > TIMEOUT_CYCLES) goto timeout_err;
}
// ---- Wait for valid_o (DUT computing) ----
dut->ready_i = 1;
while (!dut->valid_o) {
posedge(dut);
cycle++;
if (cycle > TIMEOUT_CYCLES) goto timeout_err;
}
// ---- Read 256 output coefficients ----
printf("RESULT: ");
for (int i = 0; i < 256; i++) {
// Wait for valid_o to be asserted (data is valid NOW)
while (!dut->valid_o) {
posedge(dut);
cycle++;
if (cycle > TIMEOUT_CYCLES) goto timeout_err;
}
// Capture coefficient BEFORE consuming posedge
uint16_t coeff_val = (uint16_t)(dut->coeff_out & 0xFFF);
printf("%s%s", val_to_hex3(coeff_val).c_str(),
(i < 255) ? " " : "");
// Consume this coefficient: posedge with ready_i=1
posedge(dut);
cycle++;
}
printf("\n");
vec_count++;
}
std::cout << "Processed " << vec_count << " vectors" << std::endl;
infile.close();
delete dut;
return (vec_count > 0) ? 0 : 1;
timeout_err:
std::cerr << "ERROR: Timeout at cycle " << cycle
<< " (vec " << vec_count << ")" << std::endl;
infile.close();
delete dut;
return 1;
}

View File

@@ -1,94 +0,0 @@
// tb_rng.cpp - Verilator C++ testbench for rng_sync
//
// Drives valid_i pulses and prints 256-bit LFSR output values.
// Reads a hex input file to determine how many vectors to generate
// (one line per vector, content ignored).
// Prints "RESULT: <64-char hex>" for each output.
//
// Clock: 10ns period. Reset: 2 cycles low.
// Timeout: 100,000 cycles.
#include <iostream>
#include <fstream>
#include <string>
#include <cstdio>
#include "Vrng_sync.h"
#include "verilated.h"
#define CLK_PERIOD_NS 10.0
static vluint64_t main_time = 0;
double sc_time_stamp() {
return main_time;
}
int main(int argc, char** argv) {
Verilated::commandArgs(argc, argv);
// Parse +VECTOR_FILE= plusarg
const char* vector_file = NULL;
for (int i = 1; i < argc; i++) {
std::string arg(argv[i]);
if (arg.rfind("+VECTOR_FILE=", 0) == 0) {
vector_file = argv[i] + 13;
}
}
if (!vector_file) {
std::cerr << "ERROR: +VECTOR_FILE= not specified" << std::endl;
return 1;
}
std::ifstream infile(vector_file);
if (!infile.is_open()) {
std::cerr << "ERROR: Cannot open vector file: " << vector_file << std::endl;
return 1;
}
// Count non-empty, non-comment lines to determine vector count
int num_vectors = 0;
std::string line;
while (std::getline(infile, line)) {
if (!line.empty() && line[0] != '#')
num_vectors++;
}
infile.close();
// Instantiate DUT
Vrng_sync* dut = new Vrng_sync;
// Initialize
dut->clk = 0;
dut->rst_n = 0;
dut->valid_i = 0;
dut->ready_i = 0;
// Reset: 2 cycles low
for (int i = 0; i < 4; i++) {
dut->clk = 1; main_time += 5; dut->eval();
dut->clk = 0; main_time += 5; dut->eval();
}
dut->rst_n = 1;
dut->ready_i = 1;
// Generate vectors
for (int n = 0; n < num_vectors; n++) {
// Drive valid_i and posedge → LFSR advances, valid_o→1
dut->valid_i = 1;
dut->clk = 1; main_time += 5; dut->eval();
dut->clk = 0; main_time += 5; dut->eval();
dut->valid_i = 0;
// Posedge to consume → valid_o→0
dut->clk = 1; main_time += 5; dut->eval();
// Print 256-bit result (8 × 32-bit words, MSB first)
printf("RESULT: %08X%08X%08X%08X%08X%08X%08X%08X\n",
dut->data_o[7], dut->data_o[6], dut->data_o[5], dut->data_o[4],
dut->data_o[3], dut->data_o[2], dut->data_o[1], dut->data_o[0]);
dut->clk = 0; main_time += 5; dut->eval();
}
delete dut;
return 0;
}

View File

@@ -1,206 +0,0 @@
// tb_sample_cbd.cpp - Verilator C++ testbench for sample_cbd_sync
//
// Reads test vectors from +VECTOR_FILE= plusarg.
// Format: "SEED_HEX NONCE_HEX ETA"
// SEED_HEX: 64 hex chars (256-bit seed, MSB-first)
// NONCE_HEX: 2 hex chars (8-bit nonce)
// ETA: "2" or "3" (decimal)
//
// Drives DUT with seed, nonce, eta. Waits for valid_o, collects 256
// coefficients. Prints "RESULT: COEFF_HEX\n" for each coefficient.
//
// Clock: 10ns period. Reset: 2 cycles.
// Timeout: 500000 cycles.
#include <iostream>
#include <fstream>
#include <string>
#include <sstream>
#include <cstdlib>
#include <cstring>
#include <cstdint>
#include "Vsample_cbd_sync.h"
#include "verilated.h"
#define CLK_PERIOD_NS 10.0
#define TIMEOUT_CYCLES 500000
static vluint64_t main_time = 0;
double sc_time_stamp() {
return main_time;
}
// Toggle clock: both edges + eval (one full cycle)
static void posedge(Vsample_cbd_sync* dut) {
dut->clk = !dut->clk;
main_time += (vluint64_t)(CLK_PERIOD_NS / 2.0);
dut->eval();
dut->clk = !dut->clk;
main_time += (vluint64_t)(CLK_PERIOD_NS / 2.0);
dut->eval();
}
static int hex_char_to_nibble(char c) {
if (c >= '0' && c <= '9') return c - '0';
if (c >= 'A' && c <= 'F') return c - 'A' + 10;
if (c >= 'a' && c <= 'f') return c - 'a' + 10;
return 0;
}
// Parse hex string (MSB-first) into 8 x 32-bit words for 256-bit seed.
// Word 0 = bits[31:0], word 7 = bits[255:224].
// Hex string: leftmost char = most significant nibble (bits 255:252).
static void hex_to_256(const std::string& hex, uint32_t data_words[8]) {
for (int w = 0; w < 8; w++) data_words[w] = 0;
int len = (int)hex.length();
int nibble_idx = 0;
for (int i = len - 1; i >= 0; i--) {
char c = hex[i];
if (c == ' ' || c == '\t') continue;
int nib = hex_char_to_nibble(c);
int word_idx = nibble_idx / 8;
int shift = (nibble_idx % 8) * 4;
if (word_idx < 8) {
data_words[word_idx] |= ((uint32_t)nib << shift);
}
nibble_idx++;
}
}
// Parse 2-char hex string into an 8-bit value.
// "FF" → 0xFF, "0A" → 0x0A.
static uint8_t hex_to_8(const std::string& hex) {
int val = 0;
for (size_t i = 0; i < hex.length(); i++) {
val = (val << 4) | hex_char_to_nibble(hex[i]);
}
return (uint8_t)(val & 0xFF);
}
int main(int argc, char** argv) {
Verilated::commandArgs(argc, argv);
// Parse +VECTOR_FILE= plusarg
const char* vector_file = NULL;
for (int i = 1; i < argc; i++) {
std::string arg(argv[i]);
if (arg.rfind("+VECTOR_FILE=", 0) == 0) {
vector_file = argv[i] + 13;
}
}
if (!vector_file) {
std::cerr << "ERROR: +VECTOR_FILE= not specified" << std::endl;
return 1;
}
std::ifstream infile(vector_file);
if (!infile.is_open()) {
std::cerr << "ERROR: Cannot open vector file: " << vector_file << std::endl;
return 1;
}
// Instantiate DUT
Vsample_cbd_sync* dut = new Vsample_cbd_sync;
// Initialize
dut->clk = 0;
dut->rst_n = 0;
for (int w = 0; w < 8; w++) dut->seed_i[w] = 0;
dut->nonce_i = 0;
dut->eta_i = 0;
dut->valid_i = 0;
dut->ready_i = 0;
// Reset: 2 full cycles
for (int i = 0; i < 2; i++) posedge(dut);
dut->rst_n = 1;
// Consumer always ready
dut->ready_i = 1;
std::string line;
vluint64_t cycle = 0;
int vec_count = 0;
int total_coeff_count = 0;
while (std::getline(infile, line)) {
if (line.empty() || line[0] == '#') continue;
// Parse: SEED_HEX NONCE_HEX ETA
std::istringstream iss(line);
std::string seed_hex, nonce_hex;
int eta_val;
if (!(iss >> seed_hex >> nonce_hex >> eta_val)) continue;
if (seed_hex.length() < 64) continue;
// Set seed_i (256 bits)
uint32_t seed_words[8];
hex_to_256(seed_hex, seed_words);
for (int w = 0; w < 8; w++) dut->seed_i[w] = seed_words[w];
// Set nonce_i (8 bits)
dut->nonce_i = hex_to_8(nonce_hex);
// Set eta_i (2'd2 or 2'd3)
dut->eta_i = (eta_val == 3) ? 3 : 2;
// Assert valid_i for one cycle
dut->valid_i = 1;
posedge(dut);
cycle++;
dut->valid_i = 0;
// Wait for 256 coefficients
int coeffs_collected = 0;
bool timed_out = false;
while (coeffs_collected < 256) {
posedge(dut);
cycle++;
if (cycle > TIMEOUT_CYCLES) {
std::cerr << "ERROR: Timeout waiting for coeffs (vec "
<< vec_count << ", got " << coeffs_collected
<< "/256)" << std::endl;
timed_out = true;
break;
}
if (dut->valid_o && dut->ready_i) {
// Read 12-bit coefficient and print
uint32_t coeff = dut->coeff_o & 0xFFF;
printf("RESULT: %03X\n", coeff);
coeffs_collected++;
total_coeff_count++;
}
}
if (timed_out) {
goto done;
}
// Wait for DUT to return to IDLE before next vector
int wait_cycles = 0;
while (!dut->ready_o && wait_cycles < 100) {
posedge(dut);
cycle++;
wait_cycles++;
}
vec_count++;
}
done:
infile.close();
delete dut;
if (vec_count == 0) {
std::cerr << "ERROR: No vectors processed" << std::endl;
return 1;
}
return 0;
}

View File

@@ -1,194 +0,0 @@
// tb_sample_ntt.cpp - Verilator C++ testbench for sample_ntt_sync
//
// Reads test vectors from +VECTOR_FILE= plusarg.
// Each line: "RHO_HEX K_HEX I_HEX J_HEX"
// RHO_HEX: 64 hex chars (32 bytes, MSB-first per byte pair)
// K_HEX, I_HEX, J_HEX: single hex digits
//
// Drives DUT, waits for 256 coefficients via valid_o handshake,
// prints "RESULT: CCC" for each (12-bit hex).
//
// Clock: 10ns period. Reset: 2 cycles. Timeout: 2,000,000 cycles.
#include <iostream>
#include <fstream>
#include <string>
#include <sstream>
#include <cstdlib>
#include <cstdint>
#include <cstring>
#include "Vsample_ntt_sync.h"
#include "verilated.h"
#define CLK_PERIOD_NS 10.0
#define TIMEOUT_CYCLES 2000000
#define Q 3329
#define N_COEFFS 256
static vluint64_t main_time = 0;
double sc_time_stamp() {
return main_time;
}
static void posedge(Vsample_ntt_sync* dut) {
dut->clk = !dut->clk;
main_time += (vluint64_t)(CLK_PERIOD_NS / 2.0);
dut->eval();
dut->clk = !dut->clk;
main_time += (vluint64_t)(CLK_PERIOD_NS / 2.0);
dut->eval();
}
static int hex_char_to_nibble(char c) {
if (c >= '0' && c <= '9') return c - '0';
if (c >= 'A' && c <= 'F') return c - 'A' + 10;
if (c >= 'a' && c <= 'f') return c - 'a' + 10;
return 0;
}
static void parse_rho(const std::string& hex, Vsample_ntt_sync* dut) {
for (int w = 0; w < 8; w++) dut->rho_i[w] = 0;
int byte_count = 0;
for (size_t i = 0; i < hex.length() && byte_count < 32; i += 2) {
while (i < hex.length() && (hex[i] == ' ' || hex[i] == '\t')) i++;
if (i + 1 >= hex.length()) break;
char high_c = hex[i];
char low_c = hex[i + 1];
uint8_t byte_val = (hex_char_to_nibble(high_c) << 4) |
hex_char_to_nibble(low_c);
int word_idx = byte_count / 4;
int byte_off = byte_count % 4;
if (word_idx < 8) {
dut->rho_i[word_idx] |= ((uint32_t)byte_val << (byte_off * 8));
}
byte_count++;
}
}
int main(int argc, char** argv) {
Verilated::commandArgs(argc, argv);
const char* vector_file = NULL;
for (int i = 1; i < argc; i++) {
std::string arg(argv[i]);
if (arg.rfind("+VECTOR_FILE=", 0) == 0) {
vector_file = argv[i] + 13;
}
}
if (!vector_file) {
std::cerr << "ERROR: +VECTOR_FILE= not specified" << std::endl;
return 1;
}
std::ifstream infile(vector_file);
if (!infile.is_open()) {
std::cerr << "ERROR: Cannot open vector file: " << vector_file << std::endl;
return 1;
}
Vsample_ntt_sync* dut = new Vsample_ntt_sync;
// Initialize
dut->clk = 0;
dut->rst_n = 0;
for (int i = 0; i < 8; i++) dut->rho_i[i] = 0;
dut->k_i = 0;
dut->i_idx = 0;
dut->j_idx = 0;
dut->valid_i = 0;
dut->ready_i = 0;
// Reset: 2 full cycles
for (int i = 0; i < 2; i++) posedge(dut);
dut->rst_n = 1;
dut->ready_i = 1;
std::string line;
vluint64_t cycle = 0;
int vec_count = 0;
while (std::getline(infile, line)) {
if (line.empty() || line[0] == '#') continue;
std::string rho_hex, k_str, i_str, j_str;
std::istringstream iss(line);
if (!(iss >> rho_hex >> k_str >> i_str >> j_str)) {
std::cerr << "ERROR: Malformed input line: " << line << std::endl;
continue;
}
if (rho_hex.length() < 64) {
std::cerr << "ERROR: RHO_HEX too short" << std::endl;
continue;
}
int k_val = 0, i_val = 0, j_val = 0;
if (!k_str.empty()) k_val = hex_char_to_nibble(k_str[0]);
if (!i_str.empty()) i_val = hex_char_to_nibble(i_str[0]);
if (!j_str.empty()) j_val = hex_char_to_nibble(j_str[0]);
// Set inputs
parse_rho(rho_hex, dut);
dut->k_i = k_val & 0x7;
dut->i_idx = i_val & 0x3;
dut->j_idx = j_val & 0x3;
dut->valid_i = 1;
// Wait for ready_o (DUT must be IDLE)
while (!dut->ready_o && cycle < TIMEOUT_CYCLES) {
posedge(dut);
cycle++;
}
if (cycle >= TIMEOUT_CYCLES) {
std::cerr << "ERROR: Timeout waiting for ready_o (vec "
<< vec_count << ")" << std::endl;
goto done;
}
// Capture edge
posedge(dut);
cycle++;
dut->valid_i = 0;
// Wait for and print 256 coefficients
int coeff_count = 0;
while (coeff_count < N_COEFFS && cycle < TIMEOUT_CYCLES) {
while (!dut->valid_o && cycle < TIMEOUT_CYCLES) {
posedge(dut);
cycle++;
}
if (cycle >= TIMEOUT_CYCLES) {
std::cerr << "ERROR: Timeout waiting for coeff "
<< coeff_count << " (vec " << vec_count << ")"
<< std::endl;
goto done;
}
uint32_t coeff_val = (uint32_t)(dut->coeff_o) & 0xFFF;
printf("RESULT: %03X\n", coeff_val);
posedge(dut);
cycle++;
coeff_count++;
}
vec_count++;
}
done:
infile.close();
delete dut;
if (vec_count == 0) {
std::cerr << "ERROR: No vectors processed" << std::endl;
return 1;
}
return 0;
}

View File

@@ -1,196 +0,0 @@
// tb_sha3.cpp - Verilator C++ testbench for sha3_top
//
// Reads test vectors from +VECTOR_FILE=plusarg.
// Format: "MM DDDD..." (mode hex, then 512-bit message hex)
// MM: "00"=G, "01"=H, "10"=J
// DDDD...: 128 hex chars representing 512-bit data_i
//
// Drives DUT with mode and input, waits for ready_o/valid_o,
// prints "RESULT: OUTPUT_HEX\n" to stdout.
//
// Clock: 10ns period. Reset: 2 cycles.
#include <iostream>
#include <fstream>
#include <string>
#include <sstream>
#include <cstdlib>
#include <cstring>
#include <cstdint>
#include "Vsha3_top.h"
#include "verilated.h"
#define CLK_PERIOD_NS 10.0
#define TIMEOUT_CYCLES 500000
static vluint64_t main_time = 0;
double sc_time_stamp() {
return main_time;
}
// Toggle clock: both edges + eval (one full cycle)
static void posedge(Vsha3_top* dut) {
dut->clk = !dut->clk;
main_time += (vluint64_t)(CLK_PERIOD_NS / 2.0);
dut->eval();
dut->clk = !dut->clk;
main_time += (vluint64_t)(CLK_PERIOD_NS / 2.0);
dut->eval();
}
static int hex_char_to_nibble(char c) {
if (c >= '0' && c <= '9') return c - '0';
if (c >= 'A' && c <= 'F') return c - 'A' + 10;
if (c >= 'a' && c <= 'f') return c - 'a' + 10;
return 0;
}
// Parse a hex string into Verilator WData words (16 x 32-bit = 512 bits).
// Hex string is MSB-first (leftmost hex char = bits 511:508).
// data[0] = bits[31:0], data[15] = bits[511:480].
static void hex_to_512(const std::string& hex, uint32_t data_words[16]) {
for (int w = 0; w < 16; w++) data_words[w] = 0;
int len = (int)hex.length();
// nibble_idx increments for each hex char from RIGHT to LEFT
int nibble_idx = 0;
for (int i = len - 1; i >= 0; i--) {
char c = hex[i];
if (c == ' ' || c == '\t') continue;
int nib = hex_char_to_nibble(c);
int word_idx = nibble_idx / 8; // 8 nibbles per 32-bit word
int shift = (nibble_idx % 8) * 4; // shift within the word
if (word_idx < 16) {
data_words[word_idx] |= ((uint32_t)nib << shift);
}
nibble_idx++;
}
}
// Print hash_o as hex (MSB-first).
// data[0] = bits[31:0], data[15] = bits[511:480].
static void print_hex(uint32_t data_words[16], int bits) {
int full_words = bits / 32;
// Build hex from MSB nibble to LSB nibble
for (int w = full_words - 1; w >= 0; w--) {
uint32_t val = data_words[w];
for (int j = 28; j >= 0; j -= 4) {
int nib = (int)((val >> j) & 0xF);
printf("%01X", nib);
}
}
printf("\n");
}
int main(int argc, char** argv) {
Verilated::commandArgs(argc, argv);
// Parse +VECTOR_FILE= plusarg
const char* vector_file = NULL;
for (int i = 1; i < argc; i++) {
std::string arg(argv[i]);
if (arg.rfind("+VECTOR_FILE=", 0) == 0) {
vector_file = argv[i] + 13;
}
}
if (!vector_file) {
std::cerr << "ERROR: +VECTOR_FILE= not specified" << std::endl;
return 1;
}
std::ifstream infile(vector_file);
if (!infile.is_open()) {
std::cerr << "ERROR: Cannot open vector file: " << vector_file << std::endl;
return 1;
}
// Instantiate DUT
Vsha3_top* dut = new Vsha3_top;
// Initialize
dut->clk = 0;
dut->rst_n = 0;
dut->mode = 0;
for (int w = 0; w < 16; w++) dut->data_i[w] = 0;
dut->valid_i = 0;
dut->ready_i = 0;
// multi-block absorb path disabled for single-block G/H/J tests
dut->mb_en = 0;
dut->mb_valid_i = 0;
dut->mb_last_i = 0;
// Reset: 2 full cycles
for (int i = 0; i < 2; i++) posedge(dut);
dut->rst_n = 1;
// Consumer always ready
dut->ready_i = 1;
std::string line;
vluint64_t cycle = 0;
int vec_count = 0;
while (std::getline(infile, line)) {
if (line.empty() || line[0] == '#') continue;
// Parse: MODE_HEX MESSAGE_HEX
std::istringstream iss(line);
std::string mode_str, data_hex;
if (!(iss >> mode_str >> data_hex)) continue;
if (mode_str.length() < 2) continue;
int mode_val = hex_char_to_nibble(mode_str[1]);
// Set mode
dut->mode = mode_val & 0x3;
// Set data_i from hex string
uint32_t data_words[16];
hex_to_512(data_hex, data_words);
for (int w = 0; w < 16; w++) dut->data_i[w] = data_words[w];
// Assert valid_i for one cycle
dut->valid_i = 1;
// posedge: DUT samples valid_i, starts keccak_core
posedge(dut);
cycle++;
dut->valid_i = 0;
// Wait for valid_o (keccak_core takes ~25 cycles)
do {
posedge(dut);
cycle++;
if (cycle > TIMEOUT_CYCLES) {
std::cerr << "ERROR: Timeout waiting for valid_o (vec " << vec_count << ")" << std::endl;
goto done;
}
} while (!dut->valid_o);
// Read hash_o and print
uint32_t hash_words[16];
for (int w = 0; w < 16; w++) hash_words[w] = dut->hash_o[w];
int out_bits = (mode_val == 0) ? 512 : 256;
printf("RESULT: ");
print_hex(hash_words, out_bits);
// One more cycle for valid_o handshake
posedge(dut);
cycle++;
vec_count++;
}
done:
infile.close();
delete dut;
if (vec_count == 0) {
std::cerr << "ERROR: No vectors processed" << std::endl;
return 1;
}
return 0;
}

View File

@@ -1,111 +0,0 @@
// tb_storage.cpp - Verilator C++ testbench for storage (sd_bram top)
//
// Reads test vectors from +VECTOR_FILE= hex file.
// Format: "ADDR DATA" per line (addr in 2-char hex, data in 12-char hex).
// Writes values via sd_bram, reads back, prints "RESULT: VAL_HEX".
// s_bram is compiled as rtl_dep.
//
// Clock: 10ns period. W=48, D=64, A=6 (default sd_bram parameters).
#include <iostream>
#include <fstream>
#include <string>
#include <sstream>
#include <vector>
#include <cstdint>
#include "Vsd_bram.h"
#include "verilated.h"
#define CLK_HALF_PS 5000
static vluint64_t main_time = 0;
double sc_time_stamp() {
return (double)main_time / 1000.0;
}
static void tick(Vsd_bram* dut) {
dut->clk = 1;
main_time += CLK_HALF_PS;
dut->eval();
dut->clk = 0;
main_time += CLK_HALF_PS;
dut->eval();
}
int main(int argc, char** argv) {
Verilated::commandArgs(argc, argv);
const char* vector_file = NULL;
for (int i = 1; i < argc; i++) {
std::string arg(argv[i]);
if (arg.rfind("+VECTOR_FILE=", 0) == 0) {
vector_file = argv[i] + 13;
break;
}
}
if (!vector_file) {
std::cerr << "ERROR: +VECTOR_FILE= not specified" << std::endl;
return 1;
}
std::ifstream infile(vector_file);
if (!infile.is_open()) {
std::cerr << "ERROR: Cannot open vector file: " << vector_file << std::endl;
return 1;
}
struct Vec { int addr; uint64_t data; };
std::vector<Vec> vectors;
std::string line;
while (std::getline(infile, line)) {
if (line.empty() || line[0] == '#') continue;
std::istringstream iss(line);
int a;
uint64_t d;
if (!(iss >> std::hex >> a >> d)) continue;
if (a < 0 || a > 63) continue;
vectors.push_back({a, d & 0xFFFFFFFFFFFFULL});
}
infile.close();
if (vectors.empty()) {
std::cerr << "ERROR: No valid vectors in file" << std::endl;
return 1;
}
Vsd_bram* dut = new Vsd_bram;
dut->clk = 0;
dut->rd_addr = 0;
dut->wr_en = 0;
dut->wr_addr = 0;
dut->wr_data = 0;
dut->eval();
main_time += CLK_HALF_PS;
dut->eval();
// --- WRITE PHASE ---
for (size_t i = 0; i < vectors.size(); i++) {
dut->wr_en = 1;
dut->wr_addr = vectors[i].addr & 0x3F;
dut->wr_data = vectors[i].data & 0xFFFFFFFFFFFFULL;
tick(dut);
}
dut->wr_en = 0;
dut->wr_addr = 0;
dut->wr_data = 0;
tick(dut);
// --- READ PHASE ---
for (size_t i = 0; i < vectors.size(); i++) {
dut->rd_addr = vectors[i].addr & 0x3F;
tick(dut);
printf("RESULT: %012lX\n", (unsigned long)(dut->rd_data & 0xFFFFFFFFFFFFULL));
}
delete dut;
return 0;
}

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; wire [255:0] dbg_rho_o, dbg_sigma_o, dbg_r_o, dbg_hek_o;
mlkem_top dut ( 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), .d_i(d_i), .z_i(z_i), .msg_i(m_i), .start_i(start_i),
.busy_o(busy_o), .done_o(done_o), .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), .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), .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_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_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_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_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; 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. // KMAX defaults to 4 (worst-case sizing); KP selects the runtime k value.
mlkem_top dut ( 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), .d_i(d_i), .z_i(z_i), .msg_i(256'd0), .start_i(start_i),
.busy_o(busy_o), .done_o(done_o), .busy_o(busy_o), .done_o(done_o),
.ek_in_we(1'b0), .ek_in_addr(11'd0), .ek_in_byte(8'd0), .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(), .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_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_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_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() .dbg_r_o(), .dbg_hek_o(),
.dbg_mprime_o(), .dbg_kbar_o(), .dbg_decz_o(), .dbg_dech_o()
); );
always #5 clk = ~clk; 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 clk,
input rst_n, input rst_n,
input [2:0] k_i, // RUNTIME ML-KEM param: 2=512, 3=768, 4=1024 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] d_i, // KeyGen seed d (byte 0 in d_i[7:0])
input [255:0] z_i, // implicit-rejection seed z 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 [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 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 [10:0] ek_in_addr, // ek byte address 0..ek_bytes-1
input [7:0] ek_in_byte, // ek byte value 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 // Encaps shared secret output (= K), valid at done_o
output [255:0] ss_o, output [255:0] ss_o,
// Encaps ciphertext readback tap: ct byte by index 0..ct_bytes-1 // 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, output [255:0] dbg_sigma_o,
// Encaps debug taps: r (G high half) and H(ek) // Encaps debug taps: r (G high half) and H(ek)
output [255:0] dbg_r_o, 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 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 [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] 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 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) // 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 [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 wire [11:0] h_last_rt = {6'b0, h_nblk_rt} * 12'd136 - 12'd1; // final padded byte index
// ---- Encaps runtime params ---- // ---- Encaps runtime params ----
reg op_r; // 0=KeyGen 1=Encaps (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) 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] ss_r; // Encaps shared secret K (= G output low half)
reg [255:0] r_r; // Encaps PRF seed r (= G output high 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. // FIPS 203: eta2 = 2 for all parameter sets.
wire [1:0] eta2_rt = 2'd2; wire [1:0] eta2_rt = 2'd2;
// Compression params: (du,dv) = (10,4) for k=2/3, (11,5) for k=4. // 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] 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] 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
wire [11:0] ct_bytes_rt = c1_bytes_rt + c2_bytes_rt; // 768/1088/1568
assign ss_o = ss_r; assign ss_o = ss_r;
// ---- E1: rho-load + byteDecode12 bookkeeping ---- // ---- E1: rho-load + byteDecode12 bookkeeping ----
@@ -145,6 +179,23 @@ module mlkem_top #(
assign ct_rd_addr = dbg_ct_idx_i; assign ct_rd_addr = dbg_ct_idx_i;
assign dbg_ct_o = ct_rd_data; 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 // Polynomial storage, sized for KMAX (worst case). Runtime k uses a
// sub-range. Slot layout (each slot = 256 coeffs): // sub-range. Slot layout (each slot = 256 coeffs):
@@ -330,26 +381,39 @@ module mlkem_top #(
reg [7:0] ek_wd, dkp_wd; reg [7:0] ek_wd, dkp_wd;
// ek BRAM write port: KeyGen ST_E drives ek_we/ek_wa/ek_wd; Encaps preloads // 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 // ek from the external ek_in_* port (TB streams ek=pk before start_i). Decaps
// two never overlap (preload happens in ST_IDLE before an Encaps run). // routes the dk's ek_pke region (dk[384K:768K+32]) here at load time. The
wire ek_we_mux = ek_in_we ? 1'b1 : ek_we; // three never overlap (preloads happen in ST_IDLE before a run).
wire [10:0] ek_wa_mux = ek_in_we ? ek_in_addr : ek_wa; wire dk_ld_ekpke = dk_in_we && (dk_in_addr >= dkp_bytes_ld)
wire [7:0] ek_wd_mux = ek_in_we ? ek_in_byte : ek_wd; && (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 ( sd_bram #(.W(8), .D(2048), .A(11)) u_ek_bram (
.clk(clk), .clk(clk),
.rd_addr(ek_rd_addr), .rd_data(ek_rd_data), .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) .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 ( sd_bram #(.W(8), .D(2048), .A(11)) u_dkp_bram (
.clk(clk), .clk(clk),
.rd_addr(dkp_rd_addr), .rd_data(dkp_rd_data), .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) // 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 // (dk_ek_end / dk_hek_end declared near the top with the other size wires.)
wire [11:0] dk_hek_end = dk_ek_end + 12'd32; // H(ek) region end
// Debug-region selects for dk readback (combinational region decode). // Debug-region selects for dk readback (combinational region decode).
wire dbgdk_in_dkp = (dbg_dk_idx_i < dk_bytes_rt); 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_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_C2 = 5'd18; // Compress_dv + byteEncode_dv -> ct c2
localparam ST_ENC_E2MV = 5'd19; // relocate e2 bank_t[0] -> bank_a[E2_ASLOT] 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; localparam ST_DONE = 5'd31;
reg [4:0] st, st_next; reg [4:0] st, st_next;
@@ -418,6 +484,11 @@ module mlkem_top #(
assign dbg_sigma_o = sigma_r; assign dbg_sigma_o = sigma_r;
assign dbg_r_o = r_r; assign dbg_r_o = r_r;
assign dbg_hek_o = hek_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]) ---- // ---- sha3_top in G mode: data_i = {K_byte, d} (d byte0 in [7:0]) ----
reg sha3_valid; reg sha3_valid;
@@ -897,7 +968,11 @@ module mlkem_top #(
always @(*) begin always @(*) begin
st_next = st; st_next = st;
case (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_G: if (sha3_vo) st_next = ST_A;
ST_A: if (a_pair >= kk_rt) st_next = ST_C; 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; 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 if (!rst_n) begin
st <= ST_IDLE; st <= ST_IDLE;
k_r <= 3'd0; k_r <= 3'd0;
op_r <= 1'b0; op_r <= 2'd0;
m_r <= 256'd0; m_r <= 256'd0;
ss_r <= 256'd0; ss_r <= 256'd0;
r_r <= 256'd0; r_r <= 256'd0;
@@ -1024,6 +1099,9 @@ module mlkem_top #(
h_mblast <= 1'b0; h_mblast <= 1'b0;
h_ack <= 1'b0; h_ack <= 1'b0;
hek_r <= 256'd0; hek_r <= 256'd0;
z_r <= 256'd0;
kbar_r <= 256'd0;
cin_rd_addr_r <= 11'd0;
h_blk <= 3'd0; h_blk <= 3'd0;
h_byte <= 8'd0; h_byte <= 8'd0;
h_phase <= 2'd0; h_phase <= 2'd0;
@@ -1043,11 +1121,12 @@ module mlkem_top #(
em_we <= 1'b0; // e2-relocate bank_a write default low (E6) em_we <= 1'b0; // e2-relocate bank_a write default low (E6)
// Kick off when entering from IDLE: KeyGen starts G; Encaps captures // 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 if (st == ST_IDLE && start_i) begin
k_r <= k_i; // capture runtime ML-KEM param k_r <= k_i; // capture runtime ML-KEM param
op_r <= op_i; op_r <= op_i;
if (op_i) begin if (op_i == 2'd1) begin
m_r <= msg_i; // capture Encaps message m_r <= msg_i; // capture Encaps message
// arm H(ek) (same fields the ST_E->ST_H arming sets) // arm H(ek) (same fields the ST_E->ST_H arming sets)
h_blk <= 3'd0; h_blk <= 3'd0;
@@ -1057,11 +1136,25 @@ module mlkem_top #(
h_mblast <= 1'b0; h_mblast <= 1'b0;
h_ack <= 1'b1; // ready to consume final digest h_ack <= 1'b1; // ready to consume final digest
h_wb_vld <= 1'b0; 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 end else begin
sha3_valid <= 1'b1; sha3_valid <= 1'b1;
sha3_ack <= 1'b1; sha3_ack <= 1'b1;
end end
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 // Drop valid once accepted
if (sha3_valid && sha3_ready) sha3_valid <= 1'b0; if (sha3_valid && sha3_ready) sha3_valid <= 1'b0;

View File

@@ -1,12 +0,0 @@
{
"verilator": {
"path": "verilator",
"compile_args": ["-Wall", "--cc", "--build", "--timing", "--exe"]
},
"clock_period_ns": 10.0,
"timeout_cycles": 100000,
"rtl_root": "sync_rtl",
"vector_root": "test_framework/modules/{module}/vectors",
"report_root": "test_framework/reports",
"keep_vectors": false
}

View File

@@ -1,130 +0,0 @@
"""reporter.py - Formats and displays test results."""
import os
import time
from datetime import datetime
class Reporter:
"""Formats test results for terminal and HTML output."""
# ANSI color codes
GREEN = '\033[92m'
RED = '\033[91m'
YELLOW = '\033[93m'
CYAN = '\033[96m'
RESET = '\033[0m'
BOLD = '\033[1m'
def __init__(self, report_dir: str = 'test_framework/reports'):
self.report_dir = report_dir
os.makedirs(report_dir, exist_ok=True)
def print_summary(self, results: dict, total_elapsed: float = 0.0) -> bool:
"""Print formatted test summary to terminal.
Args:
results: Dict of module_name -> module_results.
total_elapsed: Total elapsed time in seconds.
Returns:
bool: True if all tests passed.
"""
total_pass = 0
total_fail = 0
all_pass = True
print()
print(f"{self.BOLD}{'='*60}{self.RESET}")
print(f"{self.BOLD} TEST RESULTS{self.RESET}")
print(f"{self.BOLD}{'='*60}{self.RESET}")
for module_name, module_result in results.items():
status = module_result.get('status', 'UNKNOWN')
num_pass = module_result.get('pass', 0)
num_fail = module_result.get('fail', 0)
elapsed = module_result.get('elapsed', 0.0)
total_pass += num_pass
total_fail += num_fail
if status == 'PASS':
color = self.GREEN
icon = ''
elif status == 'FAIL':
color = self.RED
icon = ''
all_pass = False
else:
color = self.YELLOW
icon = '?'
all_pass = False
print(f" {color}{icon} {module_name}{self.RESET}: "
f"{num_pass} pass, {num_fail} fail, "
f"{elapsed:.2f}s")
print(f"{self.BOLD}{'='*60}{self.RESET}")
total = total_pass + total_fail
if all_pass:
print(f" {self.GREEN}{self.BOLD}ALL TESTS PASSED{self.RESET} "
f"({total_pass}/{total} vectors, {total_elapsed:.2f}s)")
else:
print(f" {self.RED}{self.BOLD}SOME TESTS FAILED{self.RESET} "
f"({total_pass}/{total} pass, {total_fail}/{total} fail, "
f"{total_elapsed:.2f}s)")
print()
# Write HTML report
self._write_html_report(results, total_elapsed)
return all_pass
def _write_html_report(self, results: dict, total_elapsed: float) -> None:
"""Write an HTML test report."""
timestamp = datetime.now().strftime('%Y%m%d_%H%M%S')
report_path = os.path.join(self.report_dir, f'report_{timestamp}.html')
total_pass = sum(r.get('pass', 0) for r in results.values())
total_fail = sum(r.get('fail', 0) for r in results.values())
all_pass = total_fail == 0
rows = ''
for module_name, module_result in results.items():
status = module_result.get('status', 'UNKNOWN')
color = '#4CAF50' if status == 'PASS' else '#F44336'
rows += f'''
<tr>
<td>{module_name}</td>
<td style="color:{color}">{status}</td>
<td>{module_result.get('pass', 0)}</td>
<td>{module_result.get('fail', 0)}</td>
<td>{module_result.get('elapsed', 0):.2f}s</td>
</tr>'''
html = f'''<!DOCTYPE html>
<html>
<head>
<title>ML-KEM Test Report</title>
<style>
body {{ font-family: monospace; background: #1e1e1e; color: #d4d4d4;
padding: 20px; }}
h1 {{ color: {'#4CAF50' if all_pass else '#F44336'}; }}
table {{ border-collapse: collapse; width: 100%; }}
th, td {{ border: 1px solid #444; padding: 8px; text-align: left; }}
th {{ background: #333; }}
</style>
</head>
<body>
<h1>{'ALL TESTS PASSED' if all_pass else 'SOME TESTS FAILED'}</h1>
<p>Generated: {timestamp}<br>
Total: {total_pass + total_fail} vectors ({total_pass} pass, {total_fail} fail)<br>
Elapsed: {total_elapsed:.2f}s</p>
<table>
<tr><th>Module</th><th>Status</th><th>Pass</th><th>Fail</th><th>Time</th></tr>
{rows}
</table>
</body>
</html>'''
with open(report_path, 'w') as f:
f.write(html)
print(f" Report saved to: {report_path}")

View File

@@ -1,45 +0,0 @@
"""result_checker.py - Compares simulation output against expected values."""
def check_results(got: list[str], expected_file: str) -> tuple[int, int]:
"""Compare Verilator output against expected values.
Args:
got: List of result strings from simulation (hex values).
expected_file: Path to file containing expected hex values, one per line.
Returns:
tuple[int, int]: (num_pass, num_fail).
"""
# Read expected values
expected = []
try:
with open(expected_file, 'r') as f:
for line in f:
line = line.strip()
if line and not line.startswith('#'):
expected.append(line)
except FileNotFoundError:
print(f"[ERROR] Expected file not found: {expected_file}")
return (0, len(got))
num_pass = 0
num_fail = 0
max_len = max(len(got), len(expected))
for i in range(max_len):
got_val = got[i].upper() if i < len(got) else 'MISSING'
exp_val = expected[i].upper() if i < len(expected) else 'MISSING'
if got_val == exp_val:
num_pass += 1
else:
num_fail += 1
if num_fail <= 5: # Only show first 5 failures
print(f" MISMATCH[{i}]: got={got_val}, expected={exp_val}")
if num_fail > 5:
print(f" ... and {num_fail - 5} more mismatches")
return (num_pass, num_fail)

View File

@@ -1,134 +0,0 @@
"""sim_controller.py - Verilator simulation controller.
Handles Verilator compilation and execution of RTL testbenches.
"""
import subprocess
import os
import time
class SimController:
"""Controls Verilator compilation and simulation."""
def __init__(self, config: dict):
"""Initialize with test framework configuration.
Args:
config: The loaded config.json dict.
"""
self.config = config
self.verilator_path = config.get('verilator', {}).get('path', 'verilator')
self.compile_args = config.get('verilator', {}).get('compile_args', [])
def compile(self, top_module: str, rtl_files: list[str], tb_cpp: str,
work_dir: str, inc_dirs: list[str] = None) -> str:
"""Compile RTL with Verilator and return path to executable.
Args:
top_module: Name of the top-level Verilog module.
rtl_files: List of Verilog source file paths.
tb_cpp: Path to C++ testbench file.
work_dir: Working directory for compilation output (-Mdir).
inc_dirs: List of include directories for `include directives.
Returns:
Path to the compiled executable.
Raises:
RuntimeError: If Verilator is not found or compilation fails.
"""
os.makedirs(work_dir, exist_ok=True)
# Build verilator command
cmd = [self.verilator_path]
cmd.extend(self.compile_args)
cmd.extend(['--top-module', top_module])
# No explicit -std= flag; let Verilator use its default (C++14+)
if inc_dirs:
for d in inc_dirs:
cmd.append(f'+incdir+{d}')
cmd.extend(rtl_files)
cmd.append(tb_cpp)
cmd.extend(['-Mdir', work_dir])
try:
result = subprocess.run(
cmd,
capture_output=True,
text=True,
timeout=120,
cwd=os.getcwd()
)
except subprocess.TimeoutExpired:
raise RuntimeError("Verilator compilation timed out.")
except FileNotFoundError:
raise RuntimeError(
f"Verilator not found at '{self.verilator_path}'."
)
if result.returncode != 0:
tail = result.stderr[-800:] if result.stderr else '(no stderr)'
raise RuntimeError(f"Verilator compilation failed:\n{tail}")
# Executable is at <work_dir>/V<top_module>
executable = os.path.join(work_dir, f'V{top_module}')
if not os.path.exists(executable):
raise RuntimeError(
f"Compilation succeeded but executable not found: {executable}"
)
return executable
def run(self, executable: str, vector_file: str,
timeout_s: int = 30) -> dict:
"""Run the compiled simulation executable.
Args:
executable: Path to the compiled executable.
vector_file: Path to the hex vector file.
timeout_s: Timeout in seconds.
Returns:
dict with keys: success, results, elapsed, stderr.
"""
if not os.path.exists(executable):
return {
'success': False,
'results': [],
'elapsed': 0.0,
'stderr': f'Executable not found: {executable}'
}
start_time = time.time()
try:
result = subprocess.run(
[executable, f'+VECTOR_FILE={vector_file}'],
capture_output=True,
text=True,
timeout=timeout_s
)
elapsed = time.time() - start_time
# Parse stdout for "RESULT: XXX" lines
results = []
for line in result.stdout.splitlines():
line = line.strip()
if line.startswith('RESULT:'):
hex_val = line.split(':', 1)[1].strip()
results.append(hex_val)
success = result.returncode == 0 and len(results) > 0
return {
'success': success,
'results': results,
'elapsed': elapsed,
'stderr': result.stderr
}
except subprocess.TimeoutExpired:
elapsed = time.time() - start_time
return {
'success': False,
'results': [],
'elapsed': elapsed,
'stderr': 'Simulation timed out.'
}

View File

@@ -1,243 +0,0 @@
"""test_runner.py - Main test engine.
Discovers modules, loads test plans, generates vectors, runs simulations,
and collects results.
"""
import os
import json
import time
import importlib.util
import sys
from pathlib import Path
from .sim_controller import SimController
from .result_checker import check_results
class TestRunner:
"""Main test engine for the ML-KEM RTL test framework."""
def __init__(self, config_path: str = 'test_framework/config.json'):
"""Initialize with configuration.
Args:
config_path: Path to config.json.
"""
self.project_root = os.path.dirname(os.path.dirname(
os.path.dirname(os.path.abspath(__file__))))
self.config_path = os.path.join(self.project_root, config_path)
with open(self.config_path) as f:
self.config = json.load(f)
self.sim = SimController(self.config)
self.modules_dir = os.path.join(self.project_root,
'test_framework', 'modules')
self.rtl_root = os.path.join(self.project_root,
self.config.get('rtl_root', 'sync_rtl'))
def discover_modules(self) -> dict[str, dict]:
"""Scan test_framework/modules/*/test_plan.json and return plans.
Returns:
dict of module_name -> test_plan.
"""
modules = {}
if not os.path.isdir(self.modules_dir):
return modules
for entry in os.listdir(self.modules_dir):
mod_dir = os.path.join(self.modules_dir, entry)
plan_path = os.path.join(mod_dir, 'test_plan.json')
if os.path.isdir(mod_dir) and os.path.exists(plan_path):
with open(plan_path) as f:
plan = json.load(f)
modules[entry] = plan
return modules
def _get_rtl_files(self, plan: dict) -> list[str]:
"""Resolve RTL source file paths from a test plan.
Args:
plan: Test plan dict with 'rtl_top' and optional 'rtl_deps'.
Returns:
List of absolute paths: deps first, then top.
"""
rtl_top = os.path.join(self.project_root, plan['rtl_top'])
rtl_deps = [os.path.join(self.project_root, d)
for d in plan.get('rtl_deps', [])]
return rtl_deps + [rtl_top]
def run_module(self, module_name: str, quick: bool = False,
single_case: str = None) -> dict:
"""Run all test cases for a module.
Args:
module_name: Name of the module to test.
quick: If True, reduce vector count for faster testing.
single_case: If set, only run this case ID.
Returns:
dict with keys: pass, fail, cases, elapsed, status.
"""
modules = self.discover_modules()
if module_name not in modules:
return {
'pass': 0, 'fail': 0, 'cases': {},
'elapsed': 0.0, 'status': 'NOT_FOUND',
'error': f'Module "{module_name}" not found'
}
plan = modules[module_name]
mod_dir = os.path.join(self.modules_dir, module_name)
vectors_dir = os.path.join(mod_dir, 'vectors')
work_dir = os.path.join(mod_dir, 'obj_dir')
os.makedirs(vectors_dir, exist_ok=True)
# Import module's gen_vectors.py
gen_path = os.path.join(mod_dir, 'gen_vectors.py')
if not os.path.exists(gen_path):
return {
'pass': 0, 'fail': 0, 'cases': {},
'elapsed': 0.0, 'status': 'ERROR',
'error': f'gen_vectors.py not found for {module_name}'
}
spec = importlib.util.spec_from_file_location(
f'{module_name}_gen', gen_path)
gen_module = importlib.util.module_from_spec(spec)
sys.modules[f'{module_name}_gen'] = gen_module
spec.loader.exec_module(gen_module)
# Find generator class
generator = None
for name in dir(gen_module):
obj = getattr(gen_module, name)
if (isinstance(obj, type) and
hasattr(obj, 'generate_one') and
hasattr(obj, 'write_hex_file') and
name != 'VectorGenerator'):
generator = obj()
break
if generator is None:
return {
'pass': 0, 'fail': 0, 'cases': {},
'elapsed': 0.0, 'status': 'ERROR',
'error': f'No generator class found in {gen_path}'
}
# Resolve RTL files and top module name
rtl_files = self._get_rtl_files(plan)
tb_cpp = os.path.join(self.project_root, plan['tb_cpp'])
# Top module name derived from rtl_top filename (strip .v)
top_module = os.path.basename(plan['rtl_top']).replace('.v', '')
# Compile once for the module
print(f"\n Compiling {module_name} with Verilator...")
start_time = time.time()
try:
executable = self.sim.compile(
top_module=top_module,
rtl_files=rtl_files,
tb_cpp=tb_cpp,
work_dir=work_dir,
inc_dirs=[self.project_root]
)
except RuntimeError as e:
return {
'pass': 0, 'fail': 0, 'cases': {},
'elapsed': time.time() - start_time,
'status': 'COMPILE_ERROR',
'error': str(e)
}
case_results = {}
total_pass = 0
total_fail = 0
for case in plan.get('cases', []):
case_id = case['id']
# Filter by single case
if single_case and case_id != single_case:
continue
num_vectors = case.get('num_vectors', 10)
if quick:
num_vectors = max(1, num_vectors // 5)
params = case.get('params', {})
print(f" Case [{case_id}]: {case.get('description', '')} "
f"({num_vectors} vectors)")
# Generate vectors
vectors = []
for i in range(num_vectors):
vec = generator.generate_one(params)
vectors.append(vec)
# Write input vectors hex file
input_file = os.path.join(vectors_dir, f'{case_id}_input.hex')
generator.write_hex_file(vectors, input_file)
# Write expected output hex file
expected_file = os.path.join(vectors_dir, f'{case_id}_expected.hex')
generator.write_expected_file(vectors, expected_file)
# Run simulation
timeout_s = plan.get('timeout_s', 30)
run_result = self.sim.run(executable, input_file, timeout_s)
if not run_result['success']:
case_results[case_id] = {
'pass': 0, 'fail': num_vectors,
'elapsed': run_result['elapsed'],
'error': run_result.get('stderr', 'Simulation failed')
}
total_fail += num_vectors
continue
# Compare results
tolerance = case.get('tolerance', 'bit_exact')
if tolerance == 'bit_exact':
num_pass, num_fail = check_results(
run_result['results'], expected_file)
else:
# Delegate to generator for custom comparison
if hasattr(generator, 'compare_results'):
compare_ok = generator.compare_results(
run_result['results'], expected_file)
num_pass = num_vectors if compare_ok else 0
num_fail = 0 if compare_ok else num_vectors
else:
num_pass, num_fail = check_results(
run_result['results'], expected_file)
case_results[case_id] = {
'pass': num_pass,
'fail': num_fail,
'elapsed': run_result['elapsed']
}
total_pass += num_pass
total_fail += num_fail
# Clean up vectors unless keep_vectors is true
if not self.config.get('keep_vectors', False):
try:
os.remove(input_file)
os.remove(expected_file)
except OSError:
pass
elapsed = time.time() - start_time
return {
'pass': total_pass,
'fail': total_fail,
'cases': case_results,
'elapsed': elapsed,
'status': 'PASS' if total_fail == 0 else 'FAIL'
}

View File

@@ -1,61 +0,0 @@
"""vector_gen.py - Base class for vector generators.
Each module provides a gen_vectors.py that subclasses VectorGenerator.
The generate_one() method is abstract and must be overridden.
The write_hex_file() method writes vectors as hex-formatted files.
"""
import os
from abc import ABC, abstractmethod
class VectorGenerator(ABC):
"""Base class for test vector generators."""
@abstractmethod
def generate_one(self, params: dict) -> dict:
"""Generate a single test vector.
Args:
params: Module-specific parameters for vector generation.
Returns:
dict with 'input' and 'expected' keys, each containing a dict
of signal_name -> value.
"""
...
def write_hex_file(self, vectors: list[dict], filepath: str) -> None:
"""Write vectors to a hex file.
Subclasses should override this to match their module's input format.
Default: writes each vector on one line, hex values separated by spaces.
Args:
vectors: List of result dicts from generate_one().
filepath: Path to write the hex file.
"""
os.makedirs(os.path.dirname(filepath), exist_ok=True)
with open(filepath, 'w') as f:
for v in vectors:
# Default: write input values as space-separated hex
inputs = v.get('input', {})
hex_vals = [format(val, 'X') for val in inputs.values()]
f.write(' '.join(hex_vals) + '\n')
def write_expected_file(self, vectors: list[dict], filepath: str) -> None:
"""Write expected results to a hex file.
Subclasses should override this to match their module's expected format.
Default: writes each expected value on one line.
Args:
vectors: List of result dicts from generate_one().
filepath: Path to write the expected hex file.
"""
os.makedirs(os.path.dirname(filepath), exist_ok=True)
with open(filepath, 'w') as f:
for v in vectors:
expected = v.get('expected', {})
hex_vals = [format(val, 'X') for val in expected.values()]
f.write(' '.join(hex_vals) + '\n')

View File

@@ -1,119 +0,0 @@
"""gen_vectors.py - Test vector generator for comp_decomp module.
Generates (mode, d, coeff) input tuples and computes expected
compress/decompress results matching the Python reference implementation.
Uses round-half-up (round(2.5)=3, round(3.5)=4).
"""
import os
import random
import sys
sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..', '..', 'lib'))
from vector_gen import VectorGenerator
Q = 3329 # ML-KEM prime modulus
def round_customize(a: float) -> int:
"""Round-half-up: round(2.5)=3, round(3.5)=4.
Matches the reference implementation in de_compress.py.
"""
tmp = int(a) + 0.5
if a >= tmp:
return int(a) + 1
else:
return int(a)
def compress(z_q: int, q: int, d: int) -> int:
"""Compress coefficient: round((2^d / q) * z_q) mod 2^d."""
_2d = 2 ** d
return round_customize((_2d / q) * z_q) % _2d
def decompress(z_d: int, q: int, d: int) -> int:
"""Decompress coefficient: round((q / 2^d) * z_d)."""
_2d = 2 ** d
return round_customize((q / _2d) * z_d)
class CompDecompVectorGenerator(VectorGenerator):
"""Generates test vectors for the comp_decomp_sync module."""
def generate_one(self, params: dict) -> dict:
"""Generate a single (mode, d, coeff) test vector.
Args:
params: dict with 'mode' ('compress'/'decompress') and 'd' (int).
Returns:
dict with 'input' and 'expected' keys.
"""
mode = params.get('mode', 'compress')
d = params.get('d', 10)
if mode == 'compress':
coeff = random.randint(0, Q - 1)
result = compress(coeff, Q, d)
else:
coeff = random.randint(0, (2 ** d) - 1)
result = decompress(coeff, Q, d)
return {
'input': {'mode': mode, 'd': d, 'coeff': coeff},
'expected': {'result': result}
}
def write_hex_file(self, vectors: list[dict], filepath: str) -> None:
"""Write input vectors as 'MODE D COEFF' format.
MODE: 'C' for compress, 'D' for decompress.
D and COEFF are hex-encoded.
Example: 'C A 3FF' for compress with d=10, coeff=0x3FF.
"""
os.makedirs(os.path.dirname(filepath), exist_ok=True)
with open(filepath, 'w') as f:
for v in vectors:
inp = v['input']
mode_char = 'C' if inp['mode'] == 'compress' else 'D'
d_val = inp['d']
coeff_val = inp['coeff']
f.write(f'{mode_char} {d_val:X} {coeff_val:03X}\n')
def write_expected_file(self, vectors: list[dict], filepath: str) -> None:
"""Write expected output as zero-padded 3-digit hex values.
One value per line matching the %03X format in the testbench.
"""
os.makedirs(os.path.dirname(filepath), exist_ok=True)
with open(filepath, 'w') as f:
for v in vectors:
result = v['expected']['result']
f.write(f'{result:03X}\n')
def compare_results(self, got: list[str], expected_file: str) -> bool:
"""Compare RTL output against expected values.
Args:
got: List of hex result strings from simulation.
expected_file: Path to expected hex file.
Returns:
bool: True if all results match.
"""
with open(expected_file, 'r') as f:
expected = [line.strip() for line in f
if line.strip() and not line.startswith('#')]
if len(got) != len(expected):
return False
for i, (g, e) in enumerate(zip(got, expected)):
if g.upper() != e.upper():
return False
return True

View File

@@ -1,66 +0,0 @@
{
"module": "comp_decomp",
"rtl_top": "sync_rtl/comp_decomp/comp_decomp_sync.v",
"rtl_deps": ["sync_rtl/common/pipeline_reg.v"],
"tb_cpp": "sync_rtl/comp_decomp/TB/tb_comp_decomp.cpp",
"simulator": "verilator",
"timeout_s": 30,
"cases": [
{
"id": "compress_du10",
"description": "Compress with du=10 (ML-KEM-512/768)",
"params": {"mode": "compress", "d": 10},
"num_vectors": 20,
"tolerance": "bit_exact"
},
{
"id": "compress_dv4",
"description": "Compress with dv=4 (ML-KEM-512/768)",
"params": {"mode": "compress", "d": 4},
"num_vectors": 20,
"tolerance": "bit_exact"
},
{
"id": "decompress_du10",
"description": "Decompress with du=10",
"params": {"mode": "decompress", "d": 10},
"num_vectors": 10,
"tolerance": "bit_exact"
},
{
"id": "decompress_dv4",
"description": "Decompress with dv=4",
"params": {"mode": "decompress", "d": 4},
"num_vectors": 10,
"tolerance": "bit_exact"
},
{
"id": "compress_du11",
"description": "Compress with du=11 (ML-KEM-1024)",
"params": {"mode": "compress", "d": 11},
"num_vectors": 20,
"tolerance": "bit_exact"
},
{
"id": "compress_dv5",
"description": "Compress with dv=5 (ML-KEM-1024)",
"params": {"mode": "compress", "d": 5},
"num_vectors": 20,
"tolerance": "bit_exact"
},
{
"id": "decompress_du11",
"description": "Decompress with du=11 (ML-KEM-1024)",
"params": {"mode": "decompress", "d": 11},
"num_vectors": 10,
"tolerance": "bit_exact"
},
{
"id": "decompress_dv5",
"description": "Decompress with dv=5 (ML-KEM-1024)",
"params": {"mode": "decompress", "d": 5},
"num_vectors": 10,
"tolerance": "bit_exact"
}
]
}

View File

@@ -1,256 +0,0 @@
8d8
a49
3b7
05e
179
1bb
945
5d2
868
700
133
61f
214
142
3ad
cb4
354
8cc
507
c94
c2b
9b7
32e
25b
1c5
4bb
01c
2bf
819
4fd
cd9
c29
13e
cf0
46c
6e1
9fa
3cf
9f8
1d9
b0b
5be
7e0
195
1c8
b3e
bfd
0ff
cb7
195
c74
509
42d
191
9a0
088
8f6
810
528
487
684
099
66f
0fa
367
579
524
a5c
4fb
121
194
7d4
3ad
c78
209
411
684
8bd
721
aa1
c03
226
82c
21d
1a0
428
bbe
3d8
53e
087
cc9
9b8
ca7
5fa
367
0ba
06c
b71
5c3
28c
11f
710
189
928
aa4
6ac
366
8e5
519
197
36d
7d4
261
03c
a50
038
089
156
71f
48d
80a
7b0
be5
6c3
8a8
1a2
b18
213
197
726
03f
4a6
b0e
41f
412
85b
341
6b9
059
7a2
4d7
5c3
ad3
5f5
7b3
4eb
b7e
5df
886
923
138
0a2
979
6cb
651
1cb
984
7be
b1b
281
0db
50c
01e
2bf
071
631
44a
3fa
2d7
120
66f
36d
aec
200
b41
93d
b4d
819
504
b22
9da
760
c49
0f6
70b
95e
68b
1db
913
af4
20b
3e8
c38
26d
225
34d
aa1
be2
553
1fe
609
1d7
331
be0
959
7ec
621
355
0ff
55a
3ba
2a8
7d4
43d
1c5
54d
2a9
5e7
950
7ab
643
68e
689
510
212
b24
5e9
2eb
a92
036
5aa
771
a8a
318
550
8e6
030
269
199
15f
3f7
036
193
0bc
67e
648
289
c87
b89
3a3
312
97d
03c
4a4
aaf
b2d

View File

@@ -1,256 +0,0 @@
58e
c5e
6e0
299
5d2
98f
925
54c
1ab
afb
6be
452
a87
5e1
598
68e
7e8
170
3da
a5c
552
c0f
443
343
c2c
792
c03
24e
3c3
461
5aa
c47
64e
743
85e
7f5
b63
315
730
b06
8e2
ae0
4bb
9f8
b20
2a4
1b9
876
abf
33e
80c
898
a87
143
86e
5d6
39d
358
649
810
64f
169
5bc
046
0d4
4f0
cef
8da
c5e
49c
c49
8e0
c4b
596
259
3a5
a92
234
153
cf1
36a
138
8c2
89d
2fb
00a
823
007
3d7
acc
667
cd4
a75
cd4
aa9
97c
42e
649
8b0
122
4dd
61d
813
41a
4d9
0ac
72a
b8f
9ea
41d
01f
787
518
2b2
aac
c31
2a9
4c4
a50
53f
a6c
5f5
8c8
13c
697
0eb
1f7
637
a44
b3e
1d3
b69
8f0
816
7b1
af8
8cf
238
a2b
129
0cf
454
2c1
684
ad9
9c8
289
39c
921
655
c00
a64
26f
c67
367
07e
7ea
316
304
b95
11c
9a7
83d
644
9d2
65d
cb6
3c2
2a2
a81
120
9c8
0ff
7ad
286
5f2
0b3
b6b
6a0
14a
b10
72f
bfc
39e
5b8
5dd
029
4e7
6e9
c55
7ae
be1
b11
60c
3b7
036
aa7
26f
2ab
cf8
125
601
50c
8d9
8e8
63a
b8e
bb8
891
466
513
2e2
ae5
4cc
2c9
7cd
3c5
4f6
590
98c
cd0
456
9c8
2c2
3ae
10b
cf8
582
4fd
40f
82a
a5a
762
43e
203
2aa
c3f
645
051
290
811
6b4
09c
c81
51b
789
2ed
aec
215
b3a
240
1b4
2cc
7ec
9fc
72d

View File

@@ -1,256 +0,0 @@
3e9
88b
8a2
2a9
92c
5b8
6ab
0ae
961
a7b
970
33a
aa6
7f8
c83
030
26d
594
a50
469
619
c75
b92
7ee
907
1c3
97c
c58
8b8
b2f
7b2
4f7
394
021
b94
a37
6f7
397
a07
8c4
cc1
a41
a21
97f
8c9
955
571
8ef
545
185
706
80a
9fd
329
5c6
7b5
357
cdd
a62
c25
5d0
375
663
93c
111
ab5
0d4
42e
54b
462
8fb
4eb
c21
6ca
cb0
b79
80e
092
399
a56
55f
390
130
54f
174
c4b
4f1
32b
032
18a
8e6
1bd
1f3
165
2a4
571
3b8
408
2a2
9b0
336
465
4c7
833
a09
10b
14c
730
03f
2bf
123
c4b
064
187
c4b
9f3
21d
3f5
29c
6d6
b10
63d
3a2
1cf
0df
bb0
392
2e3
bbd
b3c
19d
8bd
88d
688
0dc
613
46b
3c4
523
bc6
9e2
480
8b5
7c0
0ae
8e0
177
186
5ff
539
932
bb9
9cd
2df
7b3
237
5d3
9ff
a88
a62
60f
c24
a3d
457
c16
ce6
ad0
62a
089
747
c7f
8ea
2c3
042
6fa
cad
5ae
7fa
858
93b
7d5
b34
17c
804
9e0
229
847
794
000
47d
684
54a
a75
a38
261
c8e
a8e
898
3fd
1cc
ad3
142
3d6
115
4d8
58f
864
2f5
922
049
799
b7a
95a
419
1d1
0f3
74d
86f
898
654
834
91d
405
310
6d2
a2c
8b7
36b
4f6
132
cc3
c1a
61b
b79
75d
406
3ad
2d8
a0b
c79
510
a06
060
9e2
4b3
466
c7d
9c9
094
964
566
1e2
825
380
687
cf3

View File

@@ -1,256 +0,0 @@
b6b
cd0
c24
19b
ae4
759
c5e
055
c64
922
2a8
1ec
73b
8c4
9e9
734
c65
216
7f4
6e0
baa
6b3
9f0
2f4
161
9bb
1e4
163
b81
060
b5d
598
073
4d5
6dd
4f4
611
69d
740
b3d
79f
1e2
519
055
7a0
42c
02f
96a
b5c
3cf
068
772
267
8da
926
8cf
170
1ba
852
90a
0b8
31f
033
6f3
329
3fe
71d
00c
2a1
b0e
410
099
4e9
0b4
356
27e
2b9
5b1
60f
cb2
089
44c
b47
930
653
5d1
312
1c2
340
b5b
af3
ba7
626
83d
a16
898
bdd
bd0
91f
b3d
784
38f
8ec
32c
c85
7ed
916
00d
4ed
4ca
33d
7f5
4c5
5ab
a43
ae8
ad9
76c
ba9
8a2
05b
809
924
77d
4c7
bfd
299
29a
02a
71e
12f
b83
4f6
bfe
82e
c82
1fc
130
454
8e9
846
290
c7d
ab7
7c7
458
41a
a36
bfe
5db
caa
784
867
70f
62b
176
48e
4c5
27e
5a9
586
a90
627
b76
2f5
011
4b2
919
925
646
215
52f
7d1
ce4
c8a
bd0
38d
0bc
70c
988
6b5
939
a7b
482
176
b20
94b
046
990
cd6
787
374
302
4a1
872
907
55e
5d7
c86
63d
006
37a
97b
12e
218
159
b3c
808
072
a72
8c7
801
27b
a9e
3b7
7a6
9f8
35d
356
457
2cb
294
601
900
a5f
2a5
4a2
134
0da
7a3
7cc
b4b
3b1
7cc
685
7ea
7ce
0ed
28c
a3b
745
a05
32c
2ae
b06
4d1
cf7
203
93e
24a
bd4
55b
0f6
826
ac1
4db

View File

@@ -1 +0,0 @@
fada6a79a379d2cb36c856450c446f4484b86a560c6170564d002e4b74073f66a6fb6a57bce1be92a1a1b531c61f50882a0084508542a1e1c3b3a950f2072e9a16544e46ca20284b6012a62c697ac858615e181e1ea0b55b002119090b7b871920e9b894b07a281269dca15faf4a09b0004cac61a2cea73f28057dbf1219d07747b381cf0de52abad30ca0f57873b2bfa012bd5bc24805f3657ad0ba6bc4988cd9c6de492c0d433ce5aa328bd6c6cb502463340dc5e029d2c8713d780f31f441c24c4000e56799a6bc66b4a193c73ced560f8ae43f9b2075ceb778976b7f111acb38a306f2b70a2bd8752e828086ba0f3e811a8c3b0d5f8b9072159e56ba1e1008009195af58a85a3e271f00ac25ccf3233cb8716768ad98e7b1f2890db6170f2b614f56c1933871ae1a413024ca9a2137795c8a2e9e15280ff108aee328235751e569ca63a7821f29ad26706b231224d95160bd7522dde0bfe6e0a8261974885a2fdf217f8c16c4f67c0e9fb2a2f7030caf881d2c8b2e6a6863906192108b004260b0adea07c0b349d588110a9cbb699b3952bb112cc7c8d9a759b9554c1f991ea1f51f4c6178e43827c5149bda077fbd769a3b818df71a42860783fd233d1b26a96a573e99eb915f6bc18d813ded14a304c1358e470570d25478a6b6ba2ac5612890b5a569e5263342459cb9ca7674f228396954af748d9d50135342713904cc59106e0bb21942b4066f86bf5d9c3cf1b6c08692c5b9881b29971088a8b415e26c4a2567588b2ddfb68d11146fcff0b4cb79c57487018c25541d3267a10640e430bfebbb4f45a799c9f554d1b69d70c79d17124f9c01cf3ad6569e77a4e1b65704701006452f45a72f3298c660820f1c206af700c9a621923c3911cd518e022404c870c3d3b3b6831a82536a3170334e3159b0c959036c432cf5b484b12c75ccc3c96ce328fbba79e2f724789a8d27f7af8bb08645d2c9a3e87f8042c36227194a3c0e14d09129d069d5379dbd606565d915a3bc9882e87423a062000a5f4c5523ec9447cea1643a97ad33c1986246ad91e273ce32acaff453359965370721

View File

@@ -1,256 +0,0 @@
001
000
cfe
d00
001
002
cff
000
cff
d00
000
000
001
000
000
cfe
d00
000
000
001
d00
000
000
cff
001
001
d00
000
002
001
002
cff
001
002
000
d00
d00
002
002
000
000
000
cff
002
d00
000
d00
000
000
001
000
000
000
000
d00
001
d00
d00
000
002
002
cff
001
000
cff
000
000
002
002
cff
000
000
001
d00
000
000
001
000
cff
d00
d00
000
000
cff
000
000
d00
cff
000
cfe
cff
000
000
000
001
d00
002
000
000
001
cff
d00
001
000
d00
001
d00
001
002
001
d00
d00
d00
002
001
001
000
000
d00
000
000
000
d00
001
001
002
d00
001
002
000
001
d00
000
001
cfe
002
d00
cfe
cff
000
001
cff
001
002
d00
001
000
d00
d00
001
000
000
001
d00
d00
000
000
000
d00
002
001
000
d00
000
d00
000
d00
002
d00
001
cff
001
000
d00
000
000
d00
000
003
d00
002
000
d00
000
000
d00
000
d00
d00
000
001
001
d00
001
002
000
000
cff
000
000
000
d00
000
d00
000
001
000
d00
000
000
d00
000
cff
000
001
001
d00
000
001
002
d00
001
003
cff
d00
001
cff
cff
cff
d00
001
d00
d00
cff
000
001
002
002
001
000
001
d00
cff
002
002
d00
002
001
002
000
000
d00
001
000
003
000

View File

@@ -1,256 +0,0 @@
002
d00
d00
000
000
d00
003
d00
d00
cff
001
d00
002
cff
000
000
000
000
d00
002
d00
000
000
d00
000
d00
000
d00
000
001
002
001
001
001
001
d00
001
001
d00
d00
001
000
003
001
002
002
d00
000
000
002
000
000
d00
cff
002
000
001
cff
d00
d00
000
000
000
d00
001
d00
001
002
000
d00
001
001
cfe
000
d00
002
001
cff
000
000
001
d00
d00
000
000
001
001
cff
000
000
000
001
001
000
d00
000
000
d00
d00
d00
002
d00
000
cfe
000
001
000
cff
001
001
001
001
001
cfe
000
001
002
d00
001
000
000
001
000
001
001
d00
001
000
003
000
d00
001
002
000
002
000
001
cff
002
001
d00
cff
d00
000
002
000
000
000
cff
001
002
d00
003
000
000
cff
000
001
d00
000
d00
000
000
d00
d00
001
000
cff
000
001
000
000
cff
000
000
001
d00
000
001
000
000
001
d00
001
001
001
d00
cff
000
d00
d00
000
000
d00
001
d00
002
000
000
000
002
cff
000
001
000
d00
d00
002
d00
000
002
000
001
d00
002
000
cff
001
cff
d00
000
d00
002
000
000
002
001
000
001
000
000
cfe
000
001
cff
001
000
d00
cff
002
d00
000
d00
d00
000
d00
cff
000
d00
000
000
001
cff
d00
d00
001

View File

@@ -1,256 +0,0 @@
66a
076
4ec
759
9be
37d
cfe
3f7
c66
56a
b58
369
96a
485
1ed
3b7
8cd
a60
951
379
2b1
ab4
c54
703
c33
0bc
cca
1df
0e2
5ee
09b
93e
54a
bb9
7e0
8f9
0b4
6e9
176
9b4
633
0ac
133
0c8
633
b48
98e
244
a97
b21
a6d
8a3
088
55d
890
3a5
1be
b1a
84a
12c
c89
3da
195
b31
650
b14
a16
2c9
9b5
24f
3cc
963
203
7fc
4f7
a6e
390
824
a17
474
294
939
9dd
744
760
420
54b
7a7
b18
042
23a
693
74d
80f
b97
910
aed
c70
28d
c96
bd2
70c
bb7
673
a1b
c9e
aab
414
217
496
a8d
5dd
597
271
cad
02a
055
815
176
9c0
6b5
986
4ea
c05
593
75d
b97
246
3f3
4d4
75d
64a
ac2
013
8b8
283
1c8
565
096
0e9
53e
a83
a8b
573
826
95d
a33
7a4
ba5
508
b3e
4b8
2a8
282
9fd
95a
8f1
1b0
51a
a60
535
6f5
6c6
8a2
ab9
048
6e9
2d7
b10
5c6
747
3b3
1b4
a30
739
304
b0d
2d6
0d9
0d9
9eb
273
621
450
7d8
032
7e0
b47
43e
816
905
86b
201
9d4
94a
57f
746
943
8ae
2d2
656
82a
cd9
bd3
902
7da
13a
7d0
a9c
b99
32e
423
4bc
427
91f
c8f
932
a92
78e
4cd
964
344
5f4
8bd
111
247
a7d
194
4a2
b59
967
622
b2f
8d8
796
888
83a
46d
5fc
027
a6d
2a3
335
316
09d
a43
b94
156
7d5
601
5b5
8e6
484
4e5
ca1
760

View File

@@ -1,256 +0,0 @@
c98
395
12d
6e7
cfd
04b
267
6b2
57e
942
216
7d5
c6e
bba
ad5
051
3c2
1b0
0f7
98e
12a
7cb
b68
380
792
469
b9b
250
ac6
cc2
9ec
929
110
28b
cd5
2a0
585
ccf
bc0
40f
310
106
083
4cd
684
41f
8d4
771
748
72f
48c
8aa
01c
9d8
395
5d3
866
00c
450
2bf
902
680
c6a
b84
131
0ac
08d
bc9
9a2
b97
2b3
659
385
624
878
993
5f0
1ed
760
cf0
2e9
72f
955
0bc
ab5
baa
bb4
496
016
7f3
5dc
5fd
bef
a74
293
18b
8a3
451
08c
9e4
703
5c1
af5
8de
94b
5d0
bcf
2cb
436
89a
895
782
540
38d
421
0da
7b9
a1e
765
b20
182
49e
b70
051
028
29d
2f6
288
7ca
8ff
4af
2e1
031
396
19f
03a
bcb
641
166
c26
ce5
c04
224
923
68e
618
12a
956
167
657
7e0
05b
506
67d
c20
616
21c
b11
04e
b97
397
776
2d5
106
c52
84c
7bf
388
1a5
69d
67e
32e
2c1
a83
b38
749
b65
046
227
9fd
b2a
31f
42f
17b
01c
61e
a89
8e9
2d9
65d
912
5c7
580
ac8
c87
c3e
8bf
2f3
406
601
632
ca6
2cf
1f4
9d6
80d
4a3
208
bdf
add
9be
1c0
3cc
ba7
08d
36b
23f
67e
302
549
2e8
976
5a0
6dd
013
589
0c9
894
92e
882
8ca
0c0
a62
225
a61
835
617
7d4
17a
b00
7b8
b26
368
1a1
028
31e
639
7d4
af5
309
981
c31
8d6
9bc
126
cab

View File

@@ -1 +0,0 @@
80b70100831d7ee168f47392483119f93863ecb0bc605312d35728876b00f5f57af4c85eda97281d574390bc7256193668dc98625354ab52b9bce781c5771cc26099f6fbacbee1784d194887731f097c87e082cbeac57d30b7b9a293872c841466f2244cc65ff4ba653d6751dfa22051d553369942c31c29f91307af573292d697a8c31612e07b5335cf08329fd246bf2a32200a8cb0c4b03b9d09c5ba1c3919539e9229c696924348282f696b1525e5603806b4db618b3dc8630cb286e4311721bc5031f257aa925bf80960445aa8d8e69788e99da95c1d10cc3be858528db0cfea003c74693780a65a36e62843b3be15406543d22d83507c87b2786792c80fa5628454a310394dda4c2ab59493850257d6d3bc107186d9062707f354e81a1a10fcb763d31a4ec409aa243ed2e59bb189855eb1656266bb37500785ea08cd88c222a8c53a8777cab42c9c238a00d59369425026e99d2ae6040af6709029ca8789c897b329fba748d69c8a7148c95abb0c71787c58f12f21f2213e389242aa7fbd320c12ac42c611606cb8c1d1b4625886a145a535bd65bd0d996b330c1657d61a87e90417003225759820a3bbec505cdaf11193229aae173cd12bad22a7a30a76a9fa7636c3d5aa48e1784012a012f14168b7853d2a16f90ca02cc33b2006c08f26c15fca29612cc792e1cf02e1321a75a810891986667d4148af36e65e1a592589029617734c315c0e39b7518c8660cdc265d0c813ef20a043317fae5b8e40546eecf71d6c054fa66b49401309bef30fd51a5519755da9104689b26f0839154c847bef2365943c0249483aad9a7367c980a3d1ce46d2008a15275ca3bca4834e6f9a3036c0cd80ba228d34b9780244d5c0cbe907c6fb43824a9c0746471639cb86188c60c3b253215cc91692bcd1493d6a444d536446e02968138001cc287e0e518f3aa25d8c3b95b3c35c59f4819d484976029af05a263c35336b261f77b2cdc29389f084c04cbb370c48562b5b50771c0fb816908e198dd6d53f76e48bf042c2b1f82a3a40b8daf51f46d76fa4bcc9d3c894ff850c0faa32e0ac33901a4c79139f5f20417ec1a190607044f18bb824cf350c78ab3ddf3c2c86ca5543f715

View File

@@ -1 +0,0 @@
79139f5f20417ec1a190607044f18bb824cf350c78ab3ddf3c2c86ca5543f715

View File

@@ -1,256 +0,0 @@
001
000
d00
000
000
001
d00
001
000
003
cfe
000
000
d00
000
000
001
d00
d00
001
d00
cff
001
000
d00
003
d00
002
d00
cff
000
000
000
001
001
002
000
002
cfe
000
d00
003
d00
d00
000
001
000
000
d00
000
001
cff
d00
001
001
001
000
001
000
000
000
001
000
000
001
d00
000
cfe
001
000
cff
000
001
001
d00
001
002
d00
cff
000
001
000
000
d00
001
002
001
002
d00
001
cff
000
d00
cff
d00
000
cff
002
000
001
d00
cff
001
001
000
d00
000
001
d00
d00
d00
002
003
cff
d00
002
d00
001
d00
cff
001
000
001
d00
000
000
000
001
001
000
001
cfe
d00
001
000
001
000
d00
000
d00
002
000
001
cff
d00
d00
001
000
001
000
002
002
cff
d00
000
000
d00
d00
000
002
001
001
000
d00
cff
000
001
001
000
002
d00
d00
000
002
000
000
cff
cfe
000
cff
002
001
002
d00
d00
001
001
001
cff
001
001
001
d00
001
000
d00
002
cff
d00
001
000
000
002
002
000
cfe
001
001
d00
001
d00
cff
000
d00
000
001
000
d00
000
d00
001
d00
cff
001
001
000
001
000
000
000
cff
000
002
001
000
d00
d00
cfe
d00
000
003
002
001
003
d00
000
d00
001
d00
000
d00
d00
000
002
cff
d00

View File

@@ -1,256 +0,0 @@
000
001
000
000
002
003
000
003
d00
000
d00
d00
d00
000
001
d00
001
000
000
000
002
d00
001
001
000
d00
000
000
cff
cff
d00
001
000
001
001
d00
000
000
002
d00
d00
d00
001
000
d00
d00
000
d00
001
d00
003
d00
000
002
cff
d00
002
001
002
000
001
001
000
002
cff
000
d00
000
cff
000
001
000
d00
cff
002
d00
d00
cff
d00
000
000
cff
cff
001
002
d00
000
002
001
001
000
d00
000
000
d00
001
000
002
001
000
002
002
001
cff
001
000
cff
001
002
000
cff
cff
000
002
000
000
000
002
000
d00
000
cff
001
000
000
d00
001
001
d00
d00
002
d00
001
cfe
000
d00
002
d00
000
cff
000
000
cff
001
d00
000
000
000
d00
003
d00
000
001
002
001
000
000
001
000
cff
000
001
d00
002
000
cff
000
002
001
001
000
000
000
001
001
002
000
001
001
d00
cff
000
d00
000
001
000
d00
000
001
d00
000
d00
000
d00
000
002
000
001
000
001
d00
001
cff
d00
001
d00
002
001
001
001
000
000
000
001
002
d00
000
cff
d00
001
000
002
000
000
001
d00
d00
d00
000
000
000
001
cfe
d00
000
001
001
002
003
001
000
d00
000
000
000
001
d00
cff
001
003
001
002
000
000
002
001

View File

@@ -1,256 +0,0 @@
afa
6ad
379
79a
bd2
36c
6c8
455
40c
6f4
444
b88
66a
0c5
061
567
04d
2e0
44b
077
63f
a66
afb
576
1bc
bee
192
a1a
1b5
c63
01f
885
02a
840
550
428
1a1
c3e
9b3
50a
7f2
2e0
69a
541
64e
ca4
820
4b2
260
a61
92c
7a6
8c8
615
85e
1e1
01e
b5a
05b
210
919
0b0
77b
198
920
b8e
094
7ab
228
691
1dc
5fa
aaf
094
0b0
4c0
1ac
a26
7ce
3fa
528
7d0
2bf
191
7d0
477
1b3
cf8
50d
2ae
3ba
0cd
5a0
78f
273
bfb
2a0
bd1
25b
48c
305
65f
07a
bad
46b
98c
98c
c6d
9de
2c4
30d
3c4
ae5
32a
68b
c6d
0cb
245
463
0d3
0c5
29e
8d2
71c
83d
0f7
431
41f
cc2
404
500
67e
699
bca
466
a1b
793
3cc
6ed
0f5
48a
3fe
09b
752
7ce
78b
b97
7f6
a11
cb1
338
06a
7f2
0ab
82b
75d
22e
808
a86
0fb
13e
1a8
b8c
0d3
b5f
908
572
9e1
a56
1eb
810
000
591
af9
858
5aa
73e
1f2
c00
25a
3cc
23f
83c
71b
867
ad6
798
b1e
9f2
0d8
7b6
0f1
12b
4f6
156
93c
138
ae7
11a
304
a24
9ac
721
793
a5c
2e8
59e
281
10f
08f
3ae
28e
723
515
9e5
ca6
763
82a
91f
ad2
026
6b7
223
241
1d9
605
5bd
227
0dd
bfe
0e6
a8e
926
741
a88
2f5
1df
7f2
68c
c41
cf6
0e7
29f
a2b
3f7
0c0
8af
1d8
b2c
2e8
86a
636
190
926
b10
008

View File

@@ -1,256 +0,0 @@
042
b06
aad
07e
3c0
49b
8d5
118
c0a
bb9
b69
399
b52
11b
72c
c8c
7d9
59a
5b9
4c5
91f
1e9
5a1
1ff
14c
786
8e4
273
4c5
9b1
7da
7f0
6bd
9a7
13b
8d8
af7
421
786
830
3fd
3d2
61b
a92
76a
3e5
b99
91e
b5f
c16
18d
3d8
4ed
a31
104
35c
78e
054
270
54d
678
b6a
aba
c52
861
902
5b5
69a
6e5
332
542
9c4
ab9
76c
274
28f
939
546
4af
8d7
09d
135
253
714
439
cc0
059
6e1
20b
19b
442
06b
66f
bf8
c5d
3c9
6f1
c0b
286
c59
8b9
1b8
729
109
888
b4a
215
6ce
54a
672
b58
2d8
6df
8db
411
6f1
0cf
b4f
9cb
c57
774
018
58c
542
21d
673
6a1
400
0e4
bf3
beb
4fb
745
99a
5c9
54f
6d1
9db
770
9dc
217
4f1
19c
cf0
63a
56d
79e
a47
6e1
57b
004
107
506
2f4
745
2fa
832
c69
260
0f8
01c
6a2
0f7
c90
1a6
922
93c
113
1cd
8e5
402
042
0c8
c37
3d3
b6b
a83
821
a53
316
370
4e3
931
b05
9c9
035
36c
2c4
4f5
84b
cb1
752
3cc
c9c
36c
28e
afb
79b
7e2
24f
a78
8d9
727
aff
08b
86b
245
c9d
8a3
7fe
280
c34
762
192
c4a
0e3
014
91d
029
69d
7d5
9d3
0bd
656
965
15d
ca3
98b
882
74e
023
62a
a00
5f0
54c
235
4ec
479
1ce
64a
73a
ad9
133
98c
662
ad4
291
73e
2ce
ac3
4af
53f
935
659
737
210

View File

@@ -1 +0,0 @@
d57f66ed8be6cc9f4a5071b5a6e5bdc0629fd2bf6c1139a0b130561b0042b069

View File

@@ -1,256 +0,0 @@
780
01b
300
1d8
17e
68e
3f4
927
148
193
8f9
633
0ec
bcb
360
125
7d3
285
b87
006
5f5
7af
8f4
5ec
7da
289
71d
435
c90
72b
956
361
c68
98d
362
545
2ab
b95
7bc
81e
7c5
1c7
0c2
996
bf6
acf
1be
78e
94d
481
387
1f7
c09
877
2e0
cb8
5ea
7dc
730
b9b
3a2
879
42c
148
266
24f
64c
5fc
af4
65b
73d
516
2df
20a
551
53d
936
429
cc3
291
3f9
071
7af
325
692
97d
3a8
16c
012
7be
553
cf3
208
9f3
6d2
bf4
22a
203
c0a
b08
0c4
3bb
99d
c50
cba
391
319
9e5
992
c62
296
439
848
2f2
b69
156
525
60e
638
b40
1db
8b6
83d
63c
20c
86b
1e4
173
c21
50b
231
57f
2aa
5b9
9f8
600
a44
a85
6d8
97e
988
9de
ca9
1d5
c10
3bc
8e8
525
08d
cfb
0ea
3c0
974
376
680
5aa
636
28e
343
beb
015
654
243
2dd
083
7c5
287
78b
267
c89
50f
62a
484
a35
910
4d3
cda
2a4
4b5
939
285
570
3d6
bcd
110
867
6d9
270
307
54f
ae8
1a1
c10
b7f
363
1ad
44e
09c
4aa
3e2
5d2
9be
9b1
858
15e
65b
662
bb6
037
075
a85
08e
8cd
c28
822
c5a
73a
778
4ca
2cb
39c
8a2
500
93d
269
504
926
9de
62a
04e
60a
70f
990
ca2
987
c88
397
29b
7fb
48a
cd6
8a9
871
c94
b5a
0cb
871
7c7
158
2ff
221
21f
83e
923
a42
7fa

View File

@@ -1,256 +0,0 @@
2bd
0c3
c12
42a
1c6
601
86c
c1b
4d1
62b
658
a18
545
35a
5bd
bd6
90d
6b9
c33
160
657
1ad
987
04e
017
320
525
987
320
bba
0ec
5c5
1da
11f
293
9a2
7ae
3c1
bd1
ad2
722
a3a
60a
a97
6fa
367
5c3
aad
148
78e
240
a01
112
41f
768
85b
a3d
162
cf9
a00
32c
3bc
620
c00
68f
c12
a5f
29c
c61
c72
192
cfe
102
32e
51a
a87
910
198
686
7d6
841
af4
636
5ee
91a
255
289
960
317
4c7
c31
0e5
739
51b
68c
608
2cd
65c
8d0
13c
0ef
a02
143
7f3
bae
8e5
440
6e5
7ec
1df
56c
4f0
ba6
496
340
091
3be
0ff
ad5
551
519
5d7
0a9
461
289
6fb
908
153
44c
7b8
3ef
652
c94
023
849
3a4
aad
739
967
80c
1a3
ced
246
00d
58a
271
35c
bca
3a4
4e8
a6f
309
036
cdc
a80
22b
48d
b93
278
440
0d5
cbc
7e9
c60
3fb
824
c4a
079
746
164
b39
86c
c18
608
2c3
53b
c21
c95
216
bc9
9d1
3d4
46a
4d4
453
466
9e0
682
013
018
8cc
7e2
10e
8f5
23a
5da
b8c
953
3b3
5cc
459
81f
89d
494
276
9a0
af0
265
53c
333
66b
1f2
277
cdb
3c2
899
4f0
c08
b4c
37b
80c
564
b2b
505
c77
0f1
6b8
901
98e
8d1
5d6
3fd
476
8be
2f0
c24
8b1
2af
03a
b84
5da
1ff
746
6fd
ca4
c9b
8d3
94c
5ff
0c8
a0f
32a
ce0
33a
a90
4c1

View File

@@ -1,256 +0,0 @@
3b1
64f
b33
7e0
5a9
063
a30
37a
17c
3bb
ce8
4fd
1e7
97e
854
6d4
328
82d
c5c
9bb
62b
031
99f
9c3
2c0
5a4
5ec
b7c
6e4
2e2
164
7b6
6f5
ca7
3eb
8ff
1c4
4f0
b27
a90
75c
c3f
653
794
914
962
225
0e3
5f8
12f
948
7a6
7db
3d8
9f8
3fe
b49
b54
534
ab9
752
a96
bf8
1f3
108
7cd
4ce
6dc
9d4
240
0c2
3ec
2b4
411
cf4
9bc
06f
534
abf
854
9af
c81
2f0
33c
0c2
cf4
aea
1f2
22f
7d2
6a0
7ea
3de
1cd
a7d
a1b
070
26b
cb7
85f
836
b69
836
254
3ef
a3e
7e7
6f9
796
a0b
8f8
cca
c12
270
b62
7e6
9db
9aa
7f9
6cd
37a
0e8
714
38e
33e
6ea
ca6
23d
b3d
cc7
0bb
4fd
15f
1c8
5d8
3e0
873
a82
699
6ed
81d
1e9
235
4ec
04b
5c7
2e3
bb9
79d
7a4
53f
c7b
0b9
8b3
b5f
501
5d7
957
b9c
133
b73
acf
a35
8f0
c13
34e
871
a08
7f4
81a
418
501
17e
46c
431
205
549
4d5
2ee
7d7
433
313
92b
56e
9f6
796
998
bfe
a07
b1a
1c3
42d
aaa
7ba
07a
b9e
aee
252
992
324
601
4e2
23b
a12
af7
b95
74c
adf
372
589
96b
c37
4f5
663
0cd
5b3
27c
4c5
c13
cbf
b2e
058
0dc
c38
9f7
a8a
ba9
970
5eb
5b1
c1f
c67
4fa
62b
89e
2fb
24a
b65
970
a2b
583
af0
5ea
9e6
0df
a4c
18a
630
172
1f3
3a9
30a
973
2d2
3f2
64d

View File

@@ -1,256 +0,0 @@
1cb
37e
6da
c8c
983
73f
8d6
85f
60d
63f
1f1
5c5
caa
66d
195
c43
2c5
89b
ad4
466
6db
474
8a3
a95
1c6
924
2dc
48b
326
b18
5ed
b00
18c
1b1
392
345
2f6
386
954
409
0b7
247
284
71f
824
4e2
7ee
2dc
69d
ab8
96d
090
a7c
897
948
814
ae1
222
aba
aac
842
5ab
48c
3a6
bdd
a72
443
b1c
73e
1cb
cc7
0a3
220
a89
bf7
6c8
73a
927
4a1
0ed
891
1d0
c4e
6a3
4c9
92a
9a6
200
41a
c26
1c5
5fa
c22
029
8c0
a8b
93e
7d4
1da
422
629
472
531
9e1
172
3c2
9a1
aaf
496
0d0
6e4
9f7
852
66d
859
9c2
acc
765
20a
102
b9c
3a2
037
b75
882
a26
28e
902
42f
8ed
27a
c64
728
90f
83f
b79
5d1
320
855
7e4
14e
3ec
9f7
665
b8e
86d
11f
1f1
a19
7df
506
25f
621
626
053
c49
2fb
46b
206
0fd
585
960
5b9
b9f
ab7
c35
08e
881
426
c22
671
475
598
4c3
9d6
c5b
8ee
01a
682
5dd
ba8
775
c74
842
2fc
a6f
bc5
b6c
b07
897
26f
987
6c5
aca
7a2
1b3
277
650
317
5b1
5c8
bd9
392
759
b38
501
250
490
33c
59a
81c
2d1
82b
791
7a5
0be
1d2
a91
974
290
9af
5af
051
044
5c1
9e5
482
4d0
c78
9ff
c56
208
182
b51
150
4c8
138
747
8c2
9cc
251
92a
687
331
024
9fa
06e
b1b
3a7
8ab
284
3f9
30c
c30
43e
352

View File

@@ -1,256 +0,0 @@
a03
5ab
01d
c96
aff
064
151
8e2
1aa
454
37c
7c8
c0a
5c4
100
a8a
7e5
4b9
928
34f
39d
a0d
127
6e8
b18
291
473
428
80c
c39
b3e
66c
23b
793
3d5
bec
b05
222
220
442
ca5
7cc
b6d
2ea
42d
024
c30
489
a28
3b7
0c0
7fc
013
6e9
50b
0da
b88
616
aef
51d
6c0
591
021
141
57f
011
9cb
b93
55d
bd5
70b
991
6e3
0cd
51b
bc2
408
58b
bdd
409
cbb
81e
bf0
0b6
079
623
3e5
c1e
b3b
709
931
3b5
890
56d
540
92b
730
a10
42a
273
712
5e1
4f0
286
130
549
7d3
532
45e
a05
cc8
299
54e
b52
4dc
98f
571
cf1
148
08c
1fd
2f0
a9a
866
654
073
814
16e
6a8
14f
9cc
c7c
1f4
c88
354
175
167
94f
b66
1d8
33b
855
054
763
135
7e6
c68
5b5
581
6a7
772
a9d
071
cdf
9f8
233
4bf
703
607
0ae
568
743
965
a82
3fb
26d
c4c
a33
446
3f1
757
48a
89c
051
1df
4f5
b63
6fc
804
315
0ba
228
cb8
79c
0a6
05d
245
70d
491
317
6d5
71e
3b0
bad
08f
cb0
9f5
4a9
74c
6eb
8bc
2b7
593
024
67a
433
141
7bc
a8d
c34
529
496
6b3
988
9e8
4f5
6ad
4f2
66e
8c0
571
789
60c
ab9
2a4
877
7bf
636
603
8d0
147
57f
09d
5d5
8da
c12
44d
1ce
182
38f
710
901
b7e
b8f
991
2a7
848
c4c
9cf
402
cf5
adb
97a
c58
319
8d2

View File

@@ -1,256 +0,0 @@
734
599
624
b39
22d
892
81b
761
212
9d8
61b
780
bdd
cc8
528
2ce
4de
b49
c75
83e
52e
310
c11
037
8f4
03e
cca
543
c26
009
535
563
aa1
0bf
8b0
a14
3f2
723
247
81a
91c
c7b
798
97f
885
c01
c7f
541
63b
a74
757
114
9e5
261
9c0
25f
4f3
27c
a45
312
7bc
784
122
9b5
212
ab3
c3d
371
8dd
881
78a
35c
5ec
647
866
637
8e7
83e
7c4
067
cde
4f6
297
762
848
3da
327
968
273
019
24b
786
68b
03f
879
8a0
8cb
089
811
c22
03c
2a0
298
b5c
012
01c
cbc
b0a
509
0ca
bd1
56e
9b0
3df
397
633
173
99c
088
284
8e0
880
051
25a
34e
2bd
326
6cf
b8a
0e7
a93
360
771
6fb
9da
99f
00c
c12
2fe
333
c6a
856
8ae
0d9
0cc
16f
952
89f
4fe
05e
3a4
a3d
0b6
546
af3
328
089
5a7
790
1c3
128
012
4e1
614
635
2ea
0e0
625
af4
4af
263
76a
bfc
c2d
066
a5e
b3a
60b
6a2
a3c
bfe
b08
629
2c8
a2f
bdc
5f9
c53
1e7
bde
132
650
0c8
58e
59c
bc4
012
363
446
b30
9eb
24a
57d
7d4
008
65c
4ec
0b6
ca9
45a
2a2
163
a38
0fc
397
13e
5da
7c9
6cf
684
39a
83e
7c5
4e9
2de
c3d
b98
749
ca9
bb9
69f
bc2
cac
396
88e
219
917
5b2
0d8
915
37f
310
60a
a2b
380
1e4
057
25a
a03
2d4
0d3
bce
15f
107
5e8
530

View File

@@ -1 +0,0 @@
9a5c3688a0c2e3e6b14804a90447c71ec65fbbc9c45a1b9e58ba552223176f3687c12ab49af96a8290326f5c1d6e9a1c83eb3525d81e35763656e488dff37288662a3bf799dc385b8e04aa3546c283c20bf8c605faf72849f649fd1284526282c78cc67f58c9a2d14f2d294812206f91fa93b0369e54c4a38b5a4d550b95e520b7d5dba904a564e37937cc850323ccbce0d69a2b8840e4c459d97c72139a2c1990612f90adc2d2aa7bc84d16e94271574f9c1736b9a35516575a152a02e538150d6c9d48c6ad02f6958e1cb92270a01fa1372ef1609f928039712925fca69fbc835cd240a3a18b0cbc1432b1cc7bd3aa151394c46a0d26385aa243952d9357c06360f5b37a945c26deec919862aaa8f1b502416259411feb2cb275617ccfb198c315170a13c52302203d0ba770f5c52f31394a12303f149e5b45929fc33b49e277ba45b1ec390cd76a96404439836558d9c7007eb42e16c53d9c77caad6b1e71cc0289b44c9bdac9e54156e9d74473d630b71a473937b6c245aaedaa4236fc53ac919e8a895f3a839d16932b26a1a7688ab247118eea16a258f367da101aa5c9b617e2b7cb905857f733d3c3bebaf9bd80b8c92e06ba453bc84ad5c62d475079b9569124ba897b611d44111659c8eb938463ca896569123bc32aedd637932b495627a9fbd2bf15e77b9fe2ad20845fdb04bab1878ef7c71973965a42d6ba16755274493d8c99c39faa4ba6368fc93460d734a666d374c807b17b4bb25fd5879778632b9468efd1139c4a37a7cb0a87ca893bc914a3c4a0f346b157ab331df30b5995076a503ecb586eacf675111866228a14644518a6d103c2747f18d09237dbbb515cbc48518ed082710d1a6af9714c71c164bce61fded31956806cd941ba6d6c2a5944001a20000fd46f8589443b211014898bc69333adf4bed3b06f43538f8039971762609fd2746595b5f7f348e631c76dc1c63c147540425194981b5afa4493b4176a87aef98a0cce843b1db9036fd9504d60893d19c13471b64f6095c02c522bc4a21a9c836ff681bd507995b26f4ca90c1e188ce8cb08f12603699892

Some files were not shown because too many files have changed in this diff Show More