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).
This commit is contained in:
2026-06-29 16:05:06 +08:00
parent 030931d4e5
commit e46d2258d9
135 changed files with 347 additions and 22072 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回退该阶段并报告根因不带病提交。