Files
mlkem-sync/.claude/plans/bram_migration_plan.md
FallenSigh 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

81 lines
4.8 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 计划:将 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回退该阶段并报告根因不带病提交。