Files
mlkem-sync/docs/timing_analysis.md

5.2 KiB
Raw Blame History

50 MHz 时序收敛分析与修复方案

报告来源:synth_timing.tclOOC 综合 mlkem_top,器件 xc7a200tfbg676-1 sysclk 20 ns / 50 MHz。生成 timing.rpt / timing_worst.rpt / util.rpt

一、现状

指标
WNS -32.206 ns(目标 20 ns实际关键路径 ~52 ns
TNS -37879 ns失败端点 3132 / 44689
Hold (WHS) +0.134 ns (无 hold 违例)
关键路径 u_pmul/mem_A_reg[239][2]u_pmul/c0_reg_reg[0]
路径延迟 52.055 ns逻辑 21.9 ns / 布线 30.1 ns70 逻辑级CARRY4×26 + LUT×43 + MUXF×3
面积 LUT 25.5%、FF 8.6%、BRAM ~5 片、DSP 0 / 740完全没用

之前那份 timing.rptWNS -17.7 ns关键路径在 comp_decomp 的组合除法器)已经过时—— 717a992 用 Barrett 乘法替换了组合除法器,comp_decomp 不再是瓶颈。本次重新综合后, 瓶颈转移到 poly_mul 的 basecase 乘法器 u_pmul

二、根因

basecase_mulNTT 域 degree-1 乘法)单周期组合计算 c0/c1:

c0 = (a0·b0 + (a1·b1)·zeta) mod Q
c1 = (a0·b1 + a1·b0) mod Q

c0 这条路径串联了两级 Barrett 模乘:先 t2 = a1·b1 mod Q,再 t2_zeta = t2·zeta mod Q, 最后再做一次模加。每级 barrett_mul 含 12×12 乘 + ×K 乘 + ×Q 移位加 + 两次条件减, 而且全局带 (* use_dsp = "no" *) —— 所有乘法都摊成 LUT+CARRY4 阵列。两级串联 ⇒ 70 级、52 ns。

两个叠加的放大因素:

  1. 串联两次 Barrett 模乘(t2 → t2·zeta)挤在一个组合周期。
  2. use_dsp="no" 把每个乘法变成进位链,本可用的 740 个 DSP48 一个没用。

注:此前给乘法器加 use_dsp="no" 多半是为了规避 XSIM 仿真问题;但综合时这逼着工具 用 LUT 搭乘法,正是当前 52 ns 关键路径的主因。

三、修复方案(约束:不允许使用 DSP,纯 LUT/CARRY4 流水化)

实测细节(timing_worst.rpt 逐级):一个 barrett_mul 单独就要 ~24 ns (t2 = a1·b1 走到 ~28 ns,其中乘法本体 + ×K + ×Q 约简占大头),c0 又把 两个 Barrett 串联(t2 → t2·zeta)+ 模加 ⇒ 52 ns。 关键结论:单级 Barrett(~24 ns)本身就超 20 ns,所以光在两次乘法之间插一拍 (26 ns/级)不够,必须barrett_mul 内部也切流水

推荐方案:三段式流水的 Barrett 模乘 + basecase 重构

a·b mod Q 拆成 3 个寄存级,每级 < ~12 ns:

级1:  p   = a * b            (12×12 → 24b 乘法,纯 LUT/CARRY4)         → 寄存 p
级2:  qe  = (p * K) >> 24    (24×13 乘法 + 移位)                       → 寄存 qe、p
级3:  r   = p - qe*Q; 两次条件减 Q                                     → 寄存 product
  • 单个乘法(12×12 或 24×13)在 LUT 上约 ~1012 ns,加约简一拍单独走,各级都能压到 20 ns 内。
  • barrett_mul 从纯组合改成 3 拍流水(加 clk/valid 端口或用上层节拍计数)。

basecase 的 c0 双乘串联(a1·b1·zeta)= 两个 3 级流水级联 = 6 拍; c1 只有单乘 = 3 拍。让两条对齐到 6 拍输出。poly_mul_sync 现有 S_COMP_CALC → S_COMP_C0 → S_COMP_C1 节拍改为:CALC 阶段等待乘法流水排空(固定 N 拍), 再依次输出 c0/c1。吞吐:逐点乘本就 1 系数/多拍,加深流水只增加固定启动延迟(~6 拍/128 次 basecase = 几百拍),相对 KeyGen ~22.9k、Decaps ~50.8k 周期可忽略。

同源风险:comp_decomp 的 Barrett

comp_decomp_sync 也用同样的 Barrett 结构(compress 路径 dividend*5039>>24 + ×Q 约简), 单条也可能逼近 20 ns。修好 basecase 后若它冒头,同样按“Barrett 内部 3 级流水”处理。 其 5-phase 微序列(cd_valid 脉冲 + ph 等待)很容易吸收额外流水拍。

备选:更激进的乘法器结构(若 3 级仍不够)

  • 12×12 乘法本身用 2 级流水(部分积分两半累加),把 Barrett 拉成 45 级;
  • 或对乘法做 基-4/基-8 部分积 + 进位保存加法器(CSA)树,减少 CARRY4 链深度。 改动大,仅在 3 级流水实测仍 > 20 ns 时再考虑。

四、建议路线(不用 DSP

  1. barrett_mul 改成 3 级流水版(带 clk/valid),单独综合确认单级 < 20 ns。
  2. 重构 basecase_mul:c0 路径两级 Barrett 级联(6 拍),c1 对齐;poly_mul_sync 节拍 按固定流水深度调整(CALC 等 N 拍再出 c0/c1)。
  3. 全量 KAT 复测(run_tb.sh top/enc/dec + hello)确认功能不变 —— 流水加深会改变 poly_mul/ntt/comp_decomp 的拍数,各 FSM 的 done/valid 判定需同步更新。
  4. 重综合看 WNS;若 comp_decomp 成新瓶颈,同法流水化。
  5. 迭代直到 WNS ≥ 0 @ 20 ns。

注意:本项目所有时序验证目前靠 XSIM 功能仿真 + KAT,没有跑过布局布线后时序。 加流水属于改时序行为的大改,务必每步重跑全部 KAT,确保 ek/dk/ss/ct 仍逐字节正确。

五、复现命令

source /opt/Xilinx/Vivado/2019.2/settings64.sh
export LD_PRELOAD=/usr/lib64/libtinfo.so.5
vivado -mode batch -source synth_timing.tcl     # 器件在脚本里:xc7a200tfbg676-1
# 读 timing.rpt(摘要) / timing_worst.rpt(逐路径) / util.rpt(面积、DSP 占用)