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

4.8 KiB
Raw Blame History

计划:将 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.v1 读 + 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_bramdkp_bramsd_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回退该阶段并报告根因不带病提交。