Files
mlkem-sync/docs/design_report.md

327 lines
14 KiB
Markdown
Raw Permalink 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.
# ML-KEM 硬件 IP 设计报告
> 模块:`mlkem_top`FIPS 203 ML-KEM 全功能加速器)
> 器件:Xilinx Artix-7 `xc7a200tfbg676-1`
> 工具:Vivado 2019.2(综合)+ XSIM功能仿真
> 日期:2026-06
---
## 1. 概述
### 1.1 设计目标
本 IP 实现 NIST FIPS 203 标准化的后量子密钥封装机制 **ML-KEM**(源自 CRYSTALS-Kyber
在单一硬件模块 `mlkem_top` 中同时支持三种核心操作,并在运行时可选三种安全等级:
| 操作 | `op_i` | FIPS 203 算法 | 输入 | 输出 |
|:---|:---:|:---|:---|:---|
| 密钥生成 KeyGen | 0 | 算法 16 | 种子 `d`, `z` | 公钥 `ek`、私钥 `dk` |
| 密钥封装 Encaps | 1 | 算法 17 | `ek`, 消息 `m` | 共享密钥 `K`、密文 `c` |
| 密钥解封装 Decaps | 2 | 算法 18 | `dk`, 密文 `c` | 共享密钥 `K`(含隐式拒绝) |
| 等级 (`k_i`) | 方案 | K | η₁ | (d_u,d_v) | ek | dk | 密文 c |
|:---:|:---|:---:|:---:|:---:|:---:|:---:|:---:|
| 2 | ML-KEM-512 | 2 | 3 | (10,4) | 800 B | 1632 B | 768 B |
| 3 | ML-KEM-768 | 3 | 2 | (10,4) | 1184 B | 2400 B | 1088 B |
| 4 | ML-KEM-1024 | 4 | 2 | (11,5) | 1568 B | 3168 B | 1568 B |
固定参数:模数 **q = 3329**、多项式次数 **n = 256**、共享密钥 32 B。
### 1.2 关键特性
- **运行时可重构**:`k_i`/`op_i``start_i` 时锁存存储按最坏情况ML-KEM-1024静态分配
无需为不同等级重新综合。
- **资源高度复用**:同一套叶子算子与存储库被三条数据通路分时复用;Decaps 的重加密
FO 变换核心)直接复用整条 Encaps 流水线。
- **共享 Keccak 核**:G/H/J、SampleNTT、CBD 三类哈希消费者分时共用单个 `keccak_core`
- **逐字节流式 I/O**:`ek/dk/c` 经 BRAM 端口逐字节装载与回读,避免超宽顶层总线。
- **常量时间隐式拒绝**:Decaps 比较 `c'==c` 全程恒定工作量,无早退分支。
- **完整 KAT 验证**:三种等级、三种操作全部对 NIST KAT 标准答案逐字节比对通过。
### 1.3 代码规模
| 项 | 行数 |
|:---|:---:|
| `mlkem_top.v`(顶层集成 + FSM + 数据通路) | 2252 |
| RTL 合计(不含测试平台) | 5517 |
---
## 2. 顶层接口
```verilog
module mlkem_top #(parameter KMAX = 4) (
input clk, rst_n, // 100 MHz 设计时钟, 低电平异步复位
input [2:0] k_i, // 运行时等级 2/3/4 (start_i 锁存)
input [1:0] op_i, // 0=KeyGen 1=Encaps 2=Decaps
input [255:0] d_i, z_i, msg_i, // 种子 d/z, Encaps 消息 m
input start_i,
output busy_o, done_o,
output [255:0] ss_o, // 共享密钥 K
input ek_in_we; input [10:0] ek_in_addr; input [7:0] ek_in_byte; // Encaps ek 装载
input dk_in_we; input [11:0] dk_in_addr; input [7:0] dk_in_byte; // Decaps dk 装载
input c_in_we; input [10:0] c_in_addr; input [7:0] c_in_byte; // Decaps c 装载
// 调试 / 结果回读抽头(只读)
input [10:0] dbg_ct_idx_i; output [7:0] dbg_ct_o; // 密文 c
input [5:0] dbg_slot_i; input [7:0] dbg_idx_i; output [11:0] dbg_coeff_o; // 系数
input dbg_byte_sel_i; input [10:0] dbg_byte_idx_i; output [7:0] dbg_byte_o; // ek/dk_pke
input [11:0] dbg_dk_idx_i; output [7:0] dbg_dk_o; // 完整 dk
output [255:0] dbg_rho_o, dbg_sigma_o, dbg_r_o, dbg_hek_o,
dbg_mprime_o, dbg_kbar_o, dbg_decz_o, dbg_dech_o
);
```
**握手时序**:外部先经流式端口预装载所需数据Encaps→ekDecaps→dk+c
`start_i` 拉一拍锁存 `k_i/op_i` 并启动;`busy_o``st≠IDLE` 期间为高;
`done_o``st==DONE` 拉高,此时 `ss_o` 及各 BRAM 产物有效。
数据约定:所有 256-bit 端口为「byte0 在低位」,即 `value[8m+:8] = byte m`
---
## 3. 微架构
### 3.1 总体结构
`mlkem_top` 采用「**单 FSM 调度 + 共享叶子算子 + 统一存储库**」架构。顶层 RTL 结构见
`docs/mlkem_top_rtl.svg`。数据流分为四层:
```
输入端口 ──► 控制 FSM / 状态寄存器 ──► 叶子算子(共享 keccak + 算术引擎) ◄──► 存储库 ──► 输出/回读
```
### 3.2 叶子算子
| 实例 | 模块 | 功能 |
|:---|:---|:---|
| `u_keccak` | `keccak_core` | Keccak-f[1600] 置换(单核,三方共享) |
| `u_sha3` | `sha3_top_shared` | SHA3-512 (G) / SHA3-256 (H) / SHAKE-256 (J),单/多块吸收 |
| `u_snt` | `sample_ntt_sync_shared` | SampleNTTSHAKE-128 拒绝采样)生成 Â |
| `u_cbd` | `sample_cbd_sync_shared` | CBD_η 中心二项分布采样s/e/y/e1/e2 |
| `u_ntt` | `ntt_core` | 前向 NTT / 逆向 NTTmode 选择×3303 尾乘) |
| `u_pmul` | `poly_mul_sync` | NTT 域逐点乘basecase含 Barrett 模乘) |
| `u_comp` | `comp_decomp_sync` | Compress_d / Decompress_dBarrett 除法约简) |
模加、byteEncode₁₂/byteDecode₁₂、bit-packer/解包、`c'==c` 比较等轻量逻辑在顶层内联实现。
### 3.3 共享 Keccak 核
G/H/J、SampleNTT、CBD 都需要 Keccak 置换,但分处互斥的 FSM 阶段,故共用单个 `u_keccak`:
- **输入复用**:`kc_state_i_mux` / `kc_valid_i_mux` 按相位三选一
`sel_snt` / `sel_cbd` / 默认 SHA3
- **输出门控**:核输出 `kc_state_o` / `kc_valid_o` 广播给所有消费者,但只有当前激活方看到有效信号
`kc_valid_o_sha3` / `_snt` / `_cbd`)。
- 相位选择由 FSM 状态推导:`sel_sha3 = (st∈{G,H,J,…})``sel_snt = (st∈{A,ENC_A})`
`sel_cbd = (st∈{C,ENC_C})`
此设计省去 Keccak 核的多份例化Keccak-f 是面积大户),是 LUT 占用仅 25% 的主要原因之一。
### 3.4 存储库
| 实例 | 宽×深 | 用途 | 读写信号 |
|:---|:---|:---|:---|
| `u_bank_a` | 12×4096 | Â[i][j] 矩阵Decaps 中转 ŝ/e2/v' | `ba_rd_*` / `ba_we/wa/wd` |
| `u_bank_se` | 12×2048 | ŝ/ê (KeyGen)·ŷ (Encaps)·û (Decaps) | `bse_rd_*` / `bse_we/wa/wd` |
| `u_bank_t` | 12×1024 | t̂·累加和·Encaps v·Decaps w | `bt_rd_*` / `bt_we/wa/wd` |
| `u_ek_bram` | 8×2048 | ek = byteEncode₁₂(t̂)‖ρ | — |
| `u_dkp_bram` | 8×2048 | dk_pke = byteEncode₁₂(ŝ) | — |
| `u_ct_bram` | 8×2048 | 计算出的密文 c / Decaps c' | — |
| `u_c_in_bram` | 8×2048 | Decaps 输入密文 c与 ct 分开以便比较) | — |
全部基于 `sd_bram`1R/1W、寄存读、1 拍读延迟,综合映射为块 RAM。系数库槽基址由 `k_r`
运行时推导:Â 占 slot 0..K²-1`slot_s=K²``slot_e=K²+K``slot_t=K²+2K`
### 3.5 控制 FSM 与数据流
5-bit 状态机从 `ST_IDLE` 按锁存的 `op_r` 分支为三条数据通路:
**KeyGen算法 16**
```
G: (ρ,σ)=G(d‖K) SHA3-512
A: Â[i][j]=SampleNTT(ρ‖j‖i) → bank_a
C: s/e = CBD_η1(PRF(σ,·)) → bank_se
N: ŝ=NTT(s), ê=NTT(e) 原地
M: t̂=ê+Σⱼ Â∘ŝ 逐点乘+modQ 累加 → bank_t
E: ek=Enc12(t̂)‖ρ, dk_pke=Enc12(ŝ) → ek/dkp_bram
H: H(ek) 多块 SHA3-256
dk = dk_pke‖ek‖H(ek)‖z
```
**Encaps算法 17**
```
ENC_H/G: H(ek), (K,r)=G(m‖H(ek))
ENC_LOAD/A/TDEC: ρ载入, Â重生成, t̂=byteDecode12(ek)
ENC_C/N: y,e1,e2=CBD(PRF(r,·)), ŷ=NTT(y)
ENC_U: u=INTT(Σ Âᵀ∘ŷ)+e1
ENC_C1: c1=Enc_du(Compress_du(u))
ENC_V: v=INTT(Σ t̂∘ŷ)+e2+Decompress1(m)
ENC_C2: c2=Enc_dv(Compress_dv(v)); c=c1‖c2; K=共享密钥
```
**Decaps算法 18FO 变换 + 隐式拒绝)**
```
DEC_DECOMP: u'/v' = Decompress(byteDecode(c)) D1
DEC_SDEC/NTT: ŝ=byteDecode12(dk_pke), û=NTT(u') D2
DEC_W: w=v'-INTT(Σⱼ ŝ∘û) D3
DEC_MENC: m'=Enc1(Compress1(w)) D4
DEC_G/J: (K',r')=G(m'‖h), K̄=J(z‖c) D5
(重加密): c'=K-PKE.Encrypt(ek_pke,m',r') ←复用 Encaps 流水 D6
DEC_CMP: ss = (c'==c) ? K' : K̄ D7
```
**核心复用点**:D6 重加密整段复用 Encaps 的 `ENC_LOAD…ENC_C2`——m' 写入 `m_r`
r' 在 `r_r`、ek_pke 已在 `ek_bram`,前置条件天然就位,无需另起数据通路。
D7 逐字节比较 `u_ct_bram`(c') 与 `u_c_in_bram`(c),恒定工作量,按 `dec_reject`
`ss_r`(K') 与 `kbar_r`(K̄) 间选择:`ss_o = dec_reject ? kbar_r : ss_r`
### 3.6 模运算策略
所有模 qq=3329约简采用 **Barrett 乘法**替代除法器:`floor(x/q) ≈ (x·5039)>>24`
其中 `5039 = floor(2²⁴/q)`,再做至多两次条件减校正。`q·x` 用移位加实现
3329 = 2048+512+256+1无需第二个乘法器。早期版本使用组合除法器已于提交 `717a992`
替换为 Barrett显著缩短关键路径。按设计约束**全部乘法标注 `use_dsp="no"`,不使用 DSP48**。
---
## 4. 功能验证
### 4.1 验证策略
采用「分阶段中间量核对 + 端到端 KAT 比对」双层策略,全部对照 NIST FIPS 203 的 KAT 标准答案:
- **分阶段**:KeyGen 按 G/A/C/N/M/E/H、Encaps 按 E1E7、Decaps 按 D0D7 逐级验证中间多项式
/哈希/编码,经调试抽头逐字节回读比对。
- **端到端**:KeyGen 核对 `ek==pk``dk==sk`;Encaps 核对 `ss``c`;Decaps 核对最终 `ss`
并用 KAT 自带的损坏密文 `ct_n`/`ss_n` 验证隐式拒绝路径(`ss==ss_n=J(z‖ct_n)`)。
### 4.2 测试平台
| 测试平台 | 操作 | 覆盖 |
|:---|:---|:---|
| `tb_mlkem_kg_katK_xsim.v` | KeyGen | K=2/3/4 各 KAT 用例,`ek==pk`/`dk==sk` |
| `tb_mlkem_enc_katK_xsim.v` | Encaps | E1E7 分阶段 + `ss`/`c` |
| `tb_mlkem_dec_katK_xsim.v` | Decaps | D0D7 分阶段 + 接受/拒绝双路径 |
| `tb_mlkem_hello_world_xsim.v` | 全流程(单实例) | KeyGen→Encaps→XOR→Decaps→XOR |
| `tb_mlkem_two_inst_xsim.v` | 全流程(双实例) | 实例 A 做 KeyGen+Encaps实例 B 做 Decaps |
参数化 TB 通过 `xelab -generic_top KP=2|3|4` 选等级,`+CASE=n` 选用例。统一脚本
`run_tb.sh {top|enc|dec|hello}` 驱动。
### 4.3 验证结果
- **KeyGen / Encaps / Decaps** 三操作、K=2/3/4 三等级全部 KAT 用例逐字节 PASS。
- **隐式拒绝**:损坏密文路径正确输出 K̄`dec_reject=1`
- **端到端**:`hello_world` 单/双实例均通过,共享密钥与消息正确还原
`shared_key=ced0c031a4bee34a…``decrypted="hello world"`)。
### 4.4 增量验证里程碑git 记录)
设计按阶段增量构建,每阶段独立提交并通过 KAT历史可二分定位:
```
KeyGen 各阶段 → Encaps E1E7 (7228beb…) → Decaps D0D7 (030931d…2b70431)
→ hello_world 端到端 (f279222, ee2bf1c)
```
---
## 5. 综合与实现结果
### 5.1 资源占用OOC 综合xc7a200tfbg676-1
| 资源 | 使用 | 可用 | 占用率 |
|:---|:---:|:---:|:---:|
| Slice LUTs全为逻辑 | 34 318 | 134 600 | 25.5 % |
| Slice Registers (FF) | 23 254 | 269 200 | 8.6 % |
| F7 Muxes | 3 645 | 67 300 | 5.4 % |
| F8 Muxes | 1 191 | 33 650 | 3.5 % |
| Block RAM Tile | 5RAMB36×2 + RAMB18×6 | 365 | 1.4 % |
| **DSP48** | **0** | 740 | **0 %** |
面积占用低DSP 完全未用(符合设计约束),器件资源充裕。
### 5.2 性能(周期数,实测)
| 操作 | 周期数K=2ML-KEM-512 |
|:---|:---:|
| KeyGen | ≈ 22 904 |
| Encaps | ≈ 32 493 |
| Decaps | ≈ 50 819 |
K=3/4 因模秩增大按比例上升。Decaps 最长,因其包含完整重加密(≈ 一次 Encaps
### 5.3 时序现状与瓶颈
当前对 **50 MHz20 ns** 目标**尚未收敛**:
| 指标 | 值 |
|:---|:---|
| WNS | -32.2 ns关键路径实际 ≈ 52 ns |
| 失败端点 | 3 132 / 44 689 |
| Hold (WHS) | +0.134 ns无违例 |
| 关键路径 | `u_pmul/mem_A_reg → u_pmul/c0_reg`70 逻辑级 |
**根因**:`basecase_mul` 单周期组合计算 `c0=(a0·b0+(a1·b1)·zeta) mod q`,其中
**两级 Barrett 模乘串联**`t2=a1·b1``t2·zeta`+ 模加;在 `use_dsp="no"` 约束下,
每级 Barrett 约 24 ns串联即 ≈ 52 ns。
**修复方向**(不使用 DSP详见 `timing_analysis.md`:将 `barrett_mul` 改为 3 级内部流水
(乘法 / ×K 移位 / ×q 约简各一拍),`basecase_mul` 的 c0 双乘级联为 6 拍、c1 对齐,
`poly_mul_sync` 节拍相应加深。吞吐几乎不变(仅增加固定启动延迟,相对数万周期可忽略),
WNS 预计转正。每步需重跑全部 KAT 确保功能不变。
---
## 6. 设计权衡与结论
### 6.1 主要权衡
| 决策 | 收益 | 代价 |
|:---|:---|:---|
| 单 FSM 串行调度 + 资源复用 | 面积小LUT 25%)、控制清晰 | 串行执行,周期数较高 |
| 共享单 Keccak 核 | 省大量面积 | 哈希/采样不能并行 |
| Decaps 复用 Encaps 流水 | 省去独立重加密通路 | FSM 状态耦合较紧 |
| 运行时选 k_i/op_i | 单比特流支持全部配置 | 存储按最坏情况分配 |
| 不使用 DSP | 满足约束、可移植性强 | 乘法走 LUT时序压力大 |
### 6.2 结论
`mlkem_top` 是一个**功能完整、KAT 全过**的 ML-KEM 硬件 IP在单模块内支持 KeyGen/Encaps/Decaps
三种操作与三种安全等级面积占用低LUT 25.5%、零 DSP。设计经分阶段增量验证正确性可靠。
当前唯一未达标项是 **50 MHz 时序收敛**:瓶颈明确(`basecase_mul` 双 Barrett 串联组合路径),
修复方案已制定(纯流水化,不依赖 DSP改动范围可控。完成该流水化改造并重跑 KAT 后,
预计可收敛至 50 MHz 目标。
### 6.3 后续工作
1.`timing_analysis.md` 实施 Barrett / basecase / poly_mul 流水化,收敛 50 MHz。
2. 流水化后检查次要瓶颈(如 `comp_decomp` 的 Barrett同法处理。
3. 补充布局布线implementation后的时序签核目前仅有综合后时序。
4. 视吞吐需求评估关键算子NTT、采样并行化以降低周期数。
---
## 附:复现命令
```bash
source /opt/Xilinx/Vivado/2019.2/settings64.sh
export LD_PRELOAD=/usr/lib64/libtinfo.so.5
# 功能验证
./run_tb.sh top # KeyGen KAT (K=2/3/4)
./run_tb.sh enc # Encaps KAT
./run_tb.sh dec # Decaps KAT (含拒绝路径)
./run_tb.sh hello # 端到端 hello_world
# 综合 + 时序报告
vivado -mode batch -source synth_timing.tcl # → timing.rpt / timing_worst.rpt / util.rpt
```
**相关文档**:`README.md`(使用说明)、`docs/mlkem_top_rtl.svg`RTL 结构图)、
`timing_analysis.md`(时序分析与修复方案)。