From 8774e03a0e79f1227f7631d44ba7e6aa1503aae6 Mon Sep 17 00:00:00 2001 From: FallenSigh Date: Sun, 28 Jun 2026 03:43:56 +0800 Subject: [PATCH] build(vivado): rewrite create_project.tcl for current KeyGen flow The old script referenced 5 non-existent files (keccak_arbiter, sha3_chain_top_shared, tb_mlkem_top_xsim, tb_kg/en/de) and stale vectors, so read_verilog/elaborate failed outright. Rewrite to mirror the verified XSIM flow (sync_rtl/top/TB/xsim_run.tcl): - load exactly the 14 sources mlkem_top depends on - sim top = tb_mlkem_kg_katK_xsim, runtime K via generic KP, case via -testplusarg CASE - copy KAT vectors into the xsim working dir via xsim.compile.tcl.pre (the only hook in 2019.2 that runs before $readmemh; an appended -tclbatch runs after Vivado's own 'run all', too late) - drop duplicate --relax (XSim adds it; passing again is an error) Verified through the actual Vivado batch project flow: K=2 CASE 0 -> PASS (21403 cyc), K=4 CASE 2 -> PASS (54059 cyc), 0 file-not-found warnings. gitignore the generated vivado_prj/. Also rewrite README.md in Chinese documenting the mlkem_top workflow and test flow. --- .gitignore | 3 + README.md | 462 ++++++++++++++++++++------------------------- create_project.tcl | 124 ++++++------ 3 files changed, 278 insertions(+), 311 deletions(-) diff --git a/.gitignore b/.gitignore index e5fd94a..bf5df08 100644 --- a/.gitignore +++ b/.gitignore @@ -33,3 +33,6 @@ Thumbs.db # Session runtime continuation cache .omo/ + +# Generated Vivado project (create_project.tcl) +vivado_prj/ diff --git a/README.md b/README.md index b769cf1..3cd8bc5 100644 --- a/README.md +++ b/README.md @@ -1,295 +1,245 @@ -# ML-KEM Hardware Implementation (FIPS 203) +# ML-KEM 硬件实现(FIPS 203) -A synchronous, pipelined hardware implementation of **ML-KEM** (Module-Lattice-based Key Encapsulation Mechanism), the NIST PQC standard based on Kyber. Written in SystemVerilog, targeting FPGA simulation with Vivado XSIM and verified with Verilator. +基于 SystemVerilog 的 **ML-KEM**(Module-Lattice-based Key Encapsulation Mechanism,NIST 后量子密码标准,源自 Kyber)同步流水线硬件实现。面向 FPGA,使用 Vivado XSIM 与 Verilator 进行仿真验证。 -## Overview +本文档重点介绍顶层密钥生成模块 **`mlkem_top`** 的工作流程及其测试流程。各底层算子(SHA-3、NTT、CBD 采样等)已独立验证,本文不再展开。 -ML-KEM is a post-quantum key encapsulation mechanism (KEM) standardized by NIST in FIPS 203. It provides IND-CCA2 security based on the hardness of the Module Learning With Errors (MLWE) problem over the polynomial ring Z_q[x]/(x^256 + 1). +## 概述 -This implementation decomposes ML-KEM's core operations into independent, synchronous hardware modules with standardized valid/ready streaming interfaces. All modules operate at **100 MHz** (10ns period) and use active-low reset. +ML-KEM 是 NIST 在 FIPS 203 中标准化的后量子密钥封装机制,其安全性基于多项式环 Z_q[x]/(x²⁵⁶+1) 上的 Module-LWE 难题。 -### Parameters +`mlkem_top` 实现 **FIPS 203 算法 16(KeyGen_internal)** 的完整密钥生成数据通路:给定种子 `d` 与 `z`,输出封装密钥 `ek`(公钥)和解封装密钥 `dk`(私钥)。模块运行于 **100 MHz**(10 ns 周期),低电平复位。 -| Parameter | Value | Description | -|-----------|-------|-------------| -| **q** | 3329 | Prime modulus | -| **n** | 256 | Polynomial degree | -| **k** | 2 | Module rank (ML-KEM-512) | -| η₁ | 3 | CBD parameter (secret key) | -| η₂ | 2 | CBD parameter (ciphertext) | -| d_u | 10 | Compress bits | -| d_v | 4 | Compress bits | +### 运行时参数选择 -## Repository Structure +**ML-KEM 的安全等级 K 在运行时通过输入端口 `k_i` 选择**,而非编译期参数。存储按最坏情况(ML-KEM-1024,KMAX=4)静态分配,`k_i` 在 `start_i` 时被采样到内部寄存器 `k_r`,据此选取激活的子区间。 + +| k_i | 方案 | 模块秩 K | η₁ | ek 字节数 | dk 字节数 | KeyGen 周期数 | +|:---:|:---|:---:|:---:|:---:|:---:|:---:| +| 2 | ML-KEM-512 | 2 | 3 | 800 | 1632 | 21 403 | +| 3 | ML-KEM-768 | 3 | 2 | 1184 | 2400 | 36 207 | +| 4 | ML-KEM-1024 | 4 | 2 | 1568 | 3168 | 54 005 | + +> 注:`k_i` 仅在 `start_i` 时采样,且假定取值 ∈ {2,3,4};越界值(0/1/5–7)当前不做保护,会产生错误的尺寸计算。 + +固定参数:**q = 3329**(素数模)、**n = 256**(多项式次数)。du/dv 仅用于封装/解封装,KeyGen 不涉及。 + +## `mlkem_top` 接口 ``` -mlkem/ -├── sync_rtl/ # RTL source (SystemVerilog) -│ ├── common/ # Shared infrastructure -│ │ ├── pipeline_reg.v # Single-stage valid/ready pipeline register -│ │ ├── skid_buffer.v # 2-entry skid buffer for backpressure -│ │ └── defines.vh # Global parameters (Q, N, CLK_PERIOD) -│ ├── sha3/ # Keccak-f[1600] and SHA-3/SHAKE modes -│ │ ├── keccak_round.v # Single Keccak-f round (θ,ρ,π,χ,ι) -│ │ ├── keccak_core.v # 24-round sequential Keccak-f[1600] core -│ │ └── sha3_top.v # SHA3-512(G)/SHA3-256(H)/SHAKE-256(J) wrapper -│ ├── rng/ # Pseudorandom number generator -│ │ └── rng_sync.v # 256-bit Galois LFSR (taps: 255,253,252,247,0) -│ ├── ntt/ # Number Theoretic Transform -│ │ ├── zeta_rom.v # Twiddle factor ROM (128 × 12-bit, ζ^br(i)) -│ │ ├── barrett_mul.v # Barrett modular multiplier (a·b mod q) -│ │ ├── butterfly_unit.v # CT/GS butterfly (NTT/INTT) -│ │ └── ntt_core.v # NTT core: LOAD→COMPUTE→OUTPUT FSM -│ ├── poly_arith/ # Polynomial arithmetic -│ │ └── poly_arith_sync.v # Element-wise poly add/sub (PolyAdd/PolySub) -│ ├── poly_mul/ # Polynomial multiplication -│ │ ├── poly_mul_zeta_rom.v # Zeta ROM for degree-1 basecase multiply -│ │ ├── basecase_mul.v # Degree-1 Karatsuba basecase multiplier -│ │ └── poly_mul_sync.v # Full NTT-domain polynomial multiplier -│ ├── sample_cbd/ # Centered Binomial Distribution sampling -│ │ └── sample_cbd_sync.v # CBDη via SHAKE-256 PRF(seed, nonce) -│ ├── sample_ntt/ # NTT-domain sampling (A matrix) -│ │ └── sample_ntt_sync.v # SampleNTT via SHAKE-128 rejection sampling -│ ├── comp_decomp/ # Coefficient compression -│ │ └── comp_decomp_sync.v # Compress_q / Decompress_q -│ ├── mod_add/ # Modular arithmetic -│ │ └── mod_add_sync.v # (a + b) mod q, streaming -│ └── storage/ # On-chip storage -│ ├── s_bram.v # Single-port behavioral BRAM -│ └── sd_bram.v # Simple dual-port behavioral BRAM -│ -├── test_framework/ # Verilator C++ test framework -│ ├── run_all.py # CLI entry point -│ ├── config.json # Verilator path, clock period, timeouts -│ ├── lib/ # Core framework libraries -│ │ ├── test_runner.py # Discovery, compile, run, compare pipeline -│ │ ├── sim_controller.py # Verilator compile/run wrapper -│ │ ├── vector_gen.py # Base class for vector generators -│ │ ├── result_checker.py # Hex-file comparison -│ │ └── reporter.py # Terminal + HTML output -│ └── modules/ # Per-module test definitions -│ ├── /test_plan.json # Test configuration -│ └── /gen_vectors.py # Python reference + vector generator -│ -├── run_tb.sh # Vivado XSIM testbench runner -├── .trellis/ # Trellis workflow system -│ ├── workflow.md # Development phases -│ ├── spec/ # Coding specs (RTL, testbench conventions) -│ └── tasks/ # Active and archived tasks -└── .opencode/ # OpenCode agent configuration +module mlkem_top #(parameter KMAX = 4) ( + input clk, rst_n, + input [2:0] k_i, // 运行时方案选择:2/3/4 + input [255:0] d_i, // KeyGen 种子 d(byte0 在 d_i[7:0]) + input [255:0] z_i, // 隐式拒绝种子 z + input start_i, // 启动脉冲 + output busy_o, // 运行中拉高 + output done_o, // ek/dk 就绪时拉高 + // 调试回读端口(供 TB 逐字节核对,无需宽总线) + input [3: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, + input [11:0] dbg_dk_idx_i, output [7:0] dbg_dk_o, + output [255:0] dbg_rho_o, dbg_sigma_o +); ``` -## Module Architecture +`busy_o`/`done_o` 提供握手;`dbg_*` 端口为只读调试抽头,让 TB 可以逐系数 / 逐字节读出中间结果与最终的 ek/dk,而无需引出整条数据总线。 -### Core Operations +## 工作流程 + +`mlkem_top` 复用了已独立验证的叶子模块(每个模块自带 keccak_core,无共享仲裁器):`sha3_top`、`sample_ntt_sync`、`sample_cbd_sync`、`ntt_core`、`poly_mul_sync`。顶层是一个 8 状态 FSM,串行驱动这些算子,并把所有中间多项式存放在统一的系数寄存器阵列 `polymem` 中。 + +### KeyGen 算法(FIPS 203 算法 16) ``` - ┌──────────┐ - seed │ sample_ │ coeffs (256 × 12-bit) - nonce ─┤ cbd_sync ├─────────────────────┐ - └──────────┘ │ - ▼ - ┌──────────┐ ┌─────────────┐ - rho │ sample_ │ coeffs │ poly_arith │ - k,i,j─┤ ntt_sync ├─────────────┤ poly_mul │──► result - └──────────┘ │ comp_decomp │ - └─────────────┘ - ┌──────────┐ ▲ - d_in │ sha3_ │ rho, sigma │ - │ chain_top├────────────────────┘ - └──────────┘ +(ρ, σ) = G(d ‖ K) // SHA3-512 +Â[i][j] = SampleNTT(ρ ‖ j ‖ i) i,j ∈ 0..K-1 // SHAKE-128 拒绝采样 +s[i] = CBD_η1(PRF(σ, i)) i ∈ 0..K-1 // SHAKE-256 +e[i] = CBD_η1(PRF(σ, K+i)) i ∈ 0..K-1 +ŝ[i] = NTT(s[i]), ê[i] = NTT(e[i]) +t̂[i] = ê[i] + Σⱼ Â[i][j] ∘ ŝ[j] // NTT 域逐点乘 + 累加 +ek = byteEncode₁₂(t̂[0..K-1]) ‖ ρ +dk = byteEncode₁₂(ŝ[0..K-1]) ‖ ek ‖ H(ek) ‖ z ``` -### Interface Protocol - -All modules use a uniform **valid/ready** streaming interface: +### FSM 状态机 ``` -clk ──╮ ╰──╮ ╰──╮ ╰──╮ ╰── -valid_i ──╯ ╰─────╯ ╰───── -ready_o ──────╮ ╰───────── -data_i ──[A]─────[B]─────────[C]── -valid_o ─────────╮ ╰─────── -ready_i ─────────────╮ ╰───────── -data_o ─────────[A']───────[B']── + start_i + ST_IDLE ─────────────────► ST_G + ▲ │ G(d‖K),捕获 ρ/σ + │ ▼ + │ ST_A 生成 Â[i][j](K² 个多项式,SampleNTT) + │ │ + │ ▼ + │ ST_C 采样 s[i], e[i](2K 个多项式,CBD) + │ │ + │ ▼ + │ ST_N 原地前向 NTT:ŝ[i], ê[i](2K 次) + │ │ + │ ▼ + │ ST_M t̂[i] = ê[i] + Σⱼ Â[i][j]∘ŝ[j] + │ │ + │ ▼ + │ ST_E byteEncode₁₂ → ek_mem / dkp_mem,ek 尾接 ρ + │ │ + │ ▼ + │ ST_H H(ek):多块 SHA3-256 + │ done_o │ + └────────── ST_DONE ◄───────┘ ``` -- **Input**: Assert `valid_i` when `ready_o` is high; data transferred on posedge when both are high. -- **Output**: Module asserts `valid_o` when result is ready; downstream asserts `ready_i` to consume. -- **Pipeline**: Modules use `pipeline_reg` internally for 1-cycle latency. +| 状态 | 名称 | 动作 | 使用的算子 | +|:---:|:---|:---|:---| +| ST_G | 哈希 G | `(ρ,σ)=G(d‖K)`,`data_i={K_byte, d}` | `sha3_top`(SHA3-512) | +| ST_A | 矩阵采样 | 逐个生成 Â[i][j],行主序写入 slot `i*K+j`,每个 256 系数 | `sample_ntt_sync` | +| ST_C | CBD 采样 | s[0..K-1](nonce 0..K-1)、e[0..K-1](nonce K..2K-1);有符号→模 q | `sample_cbd_sync` | +| ST_N | 前向 NTT | 对 ŝ、ê 共 2K 个多项式逐个原地变换 | `ntt_core`(mode=0) | +| ST_M | 矩阵乘累加 | 对每个 (i,j):流入 256 对 (Â,ŝ) 做逐点乘,累加进 t̂[i](j=0 时以 ê[i] 初始化) | `poly_mul_sync` | +| ST_E | 字节编码 | byteEncode₁₂:t̂→ek_mem,ŝ→dkp_mem;末尾拷入 ρ 的 32 字节 | — | +| ST_H | 哈希 H | 对 ek 做多块 SHA3-256,得到 H(ek);调用方预填充末块(0x06…0x80) | `sha3_top`(多块模式) | -Modules with multi-cycle operations (NTT, sampling) additionally use a `done_o` signal or `last_o` flag. +各状态之间以 valid/ready 握手串接:FSM 拉高对应算子的 `valid_i`,在 `ready_o` 时认为请求被接收,再逐拍收集 `valid_o` 输出,直到 `last_o`/`done_o`。 -## Getting Started +### 存储布局 -### Prerequisites +所有多项式存于 `polymem`(`NUM_SLOTS×256` 个 12-bit 系数,`NUM_SLOTS = KMAX²+3·KMAX = 28`)。每个 slot 256 系数,槽基址在运行时由 `k_r` 推导: -- **Vivado 2019.2+** (for XSIM simulation): `/opt/Xilinx/Vivado/2019.2/` -- **Verilator 5.046** (for C++ testbench): available via `dnf` on Fedora -- **Python 3.10+** (for vector generation): stdlib only +``` +slot 0 .. K²-1 : Â[i][j] (下标 i*K + j) +slot_s_rt = K² : ŝ[i] (ST_N 原地覆盖 s[i]) +slot_e_rt = K² + K : ê[i] +slot_t_rt = K² + 2K : t̂[i] +``` -### Setup +字节输出存于 `ek_mem`(KMAX 最大 1568B)与 `dkp_mem`(最大 1536B)。byteEncode₁₂ 规则:每 2 个系数打包成 3 字节,LSB 优先 12-bit: + +``` +b0 = c0[7:0] +b1 = {c1[3:0], c0[11:8]} +b2 = c1[11:4] +``` + +完整私钥 dk 的字节布局(与 NIST KAT 的 sk 对齐): + +``` +dk = dk_pke(384·K) ‖ ek(384·K+32) ‖ H(ek)(32) ‖ z(32) +``` + +H(ek) 阶段采用预填充多块吸收:调用方逐块组装 136 字节速率块,在 `ek_bytes` 位置填 `0x06`、末块最后字节或上 `0x80`;分块数 `h_nblk_rt` 为 6/9/12(对应 K=2/3/4)。 + +## 测试流程 + +`mlkem_top` 的验证策略是:**对全部三种安全等级,把硬件产出的 `ek`/`dk` 与 NIST KAT 标准答案逐字节比对**。验证素材、参数化 TB、运行脚本三者配合完成。 + +### 1. 黄金向量(NIST KAT) + +测试向量来自 NIST FIPS 203 的 KAT 响应文件,经 `sync_rtl/top/TB/gen_vectors.py` 解析后生成每个用例的独立 hex 文件,存于 `sync_rtl/top/TB/vectors/`: + +| 文件 | 内容 | 字节长度(按 K) | +|:---|:---|:---| +| `kat_k_c_d.hex` | KeyGen 种子 d | 32 | +| `kat_k_c_z.hex` | 隐式拒绝种子 z | 32 | +| `kat_k_c_ek.hex` | 期望公钥 pk(== ek) | 384·K+32 | +| `kat_k_c_dk.hex` | 期望私钥 sk(== dk) | 768·K+96 | + +其中 `K ∈ {2,3,4}`、`n` 为用例号。当前覆盖:**K=2 共 5 个用例(c0–c4),K=3 / K=4 各 3 个用例(c0–c2)**,合计 11 个用例。 + +向量采用 “byte0 在低位” 约定:256-bit 值满足 `bit[8m+:8] = byte m`。 + +### 2. 参数化测试平台 + +`sync_rtl/top/TB/tb_mlkem_kg_katK_xsim.v` 是参数化自检 TB: + +- 通过 `parameter KP`(由 `xelab -generic_top KP=2|3|4` 设定)选择安全等级; +- 通过 `+CASE=n` plusarg 选择用例号,据此加载对应的 `kat_k_c_*.hex`; +- **将 `KP` 驱动到 DUT 的运行时输入 `k_i`**(不再用参数覆盖),KMAX 取默认 4; +- 复位 → 加载 d/z 与 k_i → 拉 `start_i` 一拍 → 轮询 `done_o`(超时上限 200 万周期); +- 完成后通过 `dbg_byte_o`(读 ek,0..EKB-1)与 `dbg_dk_o`(读完整 dk,0..DKB-1)逐字节回读,与黄金向量比对; +- 全部相符打印 `K= CASE PASS`,否则打印前 8 个不匹配字节并报 `FAIL`。 + +### 3. 运行测试 + +测试经由统一脚本 `run_tb.sh` 分发(自动 source Vivado 环境并设置 `LD_PRELOAD`): ```bash -# Clone repository -git clone mlkem -cd mlkem +./run_tb.sh top +``` -# Source Vivado (for XSIM) +该命令执行 `sync_rtl/top/TB/xsim_run.tcl`,其流程为: + +1. **编译**(`xvlog`):全部叶子算子 RTL + `mlkem_top.v` + 参数化 TB; +2. **细化**(`xelab`):为每种 K 生成一个快照 —— `KP=2→mlkem_kg_k2`、`KP=3→mlkem_kg_k3`、`KP=4→mlkem_kg_k4`; +3. **仿真**(`xsim -R -testplusarg CASE=n`):依次跑完每种 K 的全部用例。 + +整体测试矩阵: + +``` +K=2 (ML-KEM-512) : CASE 0,1,2,3,4 → ek 800B, dk 1632B +K=3 (ML-KEM-768) : CASE 0,1,2 → ek 1184B, dk 2400B +K=4 (ML-KEM-1024): CASE 0,1,2 → ek 1568B, dk 3168B +``` + +### 4. 预期结果 + +11 个用例全部 PASS,每个用例确认 `ek == pk` 且 `dk == sk` 逐字节相等: + +``` +=== ML-KEM K=2 KAT case 0: KeyGen done in 21403 cyc === +K=2 CASE 0 PASS: ek (800B)==pk, dk (1632B)==sk +... +K=4 CASE 2 PASS: ek (1568B)==pk, dk (3168B)==sk +``` + +### 验证注意事项 + +- **干净重跑**:每轮仿真前清理 `xsim.dir`、`.Xil`,避免旧快照污染(`rm -rf xsim.dir .Xil`)。 +- **`$readmemh` 缺文件只是 WARNING**:文件名拼错时数据读为 X,不会报错,极易造成假 PASS。务必确认日志中无 `cannot be opened` 警告。 +- **以日志文件为准**:将每个 `xsim` 调用重定向到独立日志后再 grep `PASS|FAIL|cannot be opened`,不要只看终端滚屏的模糊输出。 + +## 手动 XSIM 命令 + +```bash source /opt/Xilinx/Vivado/2019.2/settings64.sh -export LD_PRELOAD=/usr/lib64/libtinfo.so.5 # ncurses fix for 2019.2 on modern Linux +export LD_PRELOAD=/usr/lib64/libtinfo.so.5 # Vivado 2019.2 的 ncurses 兼容修复 +rm -rf xsim.dir .Xil + +# 1) 编译(叶子算子 + 顶层 + TB) +xvlog -sv --relax -i . \ + sync_rtl/sha3/keccak_round.v sync_rtl/sha3/keccak_core.v sync_rtl/sha3/sha3_top.v \ + sync_rtl/sample_ntt/sample_ntt_sync.v sync_rtl/sample_cbd/sample_cbd_sync.v \ + sync_rtl/ntt/barrett_mul.v sync_rtl/ntt/zeta_rom.v sync_rtl/ntt/butterfly_unit.v sync_rtl/ntt/ntt_core.v \ + sync_rtl/poly_mul/basecase_mul.v sync_rtl/poly_mul/poly_mul_zeta_rom.v sync_rtl/poly_mul/poly_mul_sync.v \ + sync_rtl/top/mlkem_top.v \ + sync_rtl/top/TB/tb_mlkem_kg_katK_xsim.v + +# 2) 细化某一种 K +xelab tb_mlkem_kg_katK_xsim -generic_top KP=4 -s mlkem_kg_k4 --timescale 1ns/1ps + +# 3) 跑某个用例 +xsim mlkem_kg_k4 -R -testplusarg CASE=0 ``` -### Running Tests +## 先决条件 -#### Vivado XSIM (Verilog Testbench) +- **Vivado 2019.2+**(XSIM 仿真):`/opt/Xilinx/Vivado/2019.2/` +- **Verilator 5.046**(底层算子 C++ 验证) +- **Python 3.10+**(向量生成,仅用标准库) + +## Vivado 2019.2 兼容性说明 + +在 Fedora 44 上经实测的必要 workaround: ```bash -# List available modules -./run_tb.sh --list - -# Run a specific module -./run_tb.sh mod_add -./run_tb.sh ntt -./run_tb.sh sample_cbd - -# Run all modules -for m in mod_add rng poly_arith comp_decomp storage \ - ntt poly_mul sample_cbd sample_ntt; do - ./run_tb.sh "$m" -done +export LD_PRELOAD=/usr/lib64/libtinfo.so.5 # 必需:ncurses 兼容库 +xvlog -sv --relax -i . .v # 用 -i(非 -include_dirs)指定包含目录;--relax 放宽严格 SV 检查 +xelab -s --timescale 1ns/1ps # xelab 需显式 --timescale ``` -Each module's testbench is in `sync_rtl//TB/`: -- `tb__xsim.v` — Verilog testbench (file-based vectors via `$readmemh`) -- `gen_vectors.py` — Python vector generator -- `vectors/_input.hex` — Test input vectors -- `xsim_run.tcl` — Vivado compile/elaborate/simulate script +## 参考 -#### Verilator (C++ Testbench) +- [FIPS 203: ML-KEM](https://csrc.nist.gov/pubs/fips/203/final) —— NIST 标准(算法 16 KeyGen_internal) +- [FIPS 202: SHA-3 / SHAKE](https://csrc.nist.gov/pubs/fips/202/final) —— Keccak 哈希族 +- [CRYSTALS-Kyber](https://pq-crystals.org/kyber/) —— 原始提案 -```bash -cd test_framework -python3 run_all.py --list # List modules -python3 run_all.py --module ntt # Test a single module -python3 run_all.py --quick # Smoke test all modules -``` -### Manual XSIM Commands - -```bash -# Compile -xvlog -sv -i . sync_rtl/common/pipeline_reg.v sync_rtl/mod_add/mod_add_sync.v -xvlog -sv sync_rtl/mod_add/TB/tb_mod_add_xsim.v - -# Elaborate -xelab tb_mod_add_xsim -s sim --timescale 1ns/1ps - -# Simulate -xsim sim -R -``` - -## Design Decisions - -### Synchronous Valid/Ready Streaming - -All modules use a synchronous valid/ready handshake rather than fixed-latency interfaces. This allows: -- Natural backpressure propagation -- Easy composition of modules in pipelines -- Deterministic timing closure at 100MHz - -### Barrett Modular Reduction - -All modular multiplications use Barrett reduction (no DSP blocks, no division units): -- Precompute μ = ⌊2^k / q⌋ (k = 24 for q=3329) -- Compute a·b ≈ (a·b·μ) >> k, then correct with conditional subtraction -- Fully combinational, no pipeline stalls - -### Cooley-Tukey / Gentleman-Sande NTT - -The NTT core implements both forward (Cooley-Tukey) and inverse (Gentleman-Sande) transforms using a radix-2 decimation-in-time architecture: -- 7 butterfly stages (256 = 2^7 coefficients) -- Bit-reversed input/output ordering -- On-chip coefficient register file (256 × 12-bit) -- 24-cycle pipeline for Keccak permutations (shared with SHA-3 modules) - -### Keccak-f[1600] Core - -A single Keccak-f[1600] permutation engine is shared across all SHA-3/SHAKE modules (`sha3_top`, `sample_cbd_sync`, `sample_ntt_sync`). The core implements: -- 24 rounds with round constants (ι step) -- Full 1600-bit state (5×5×64 lanes) -- 24-cycle latency per permutation -- Input/output via valid/ready streaming interface - -## Module Reference - -| Module | Ports | Latency | Description | -|--------|-------|---------|-------------| -| `pipeline_reg` | data_i/o, valid_i/o, ready_i/o | 1 cycle | Generic pipeline stage | -| `skid_buffer` | data_i/o, valid_i/o, ready_i/o | 0-1 cycles | Backpressure buffer | -| `rng_sync` | valid_i → data_o[255:0] | 1 cycle | Galois LFSR PRNG | -| `mod_add_sync` | a[11:0], b[11:0] → sum[11:0] | 1 cycle | Modular addition | -| `ntt_core` | 256×coeff_in → 256×coeff_out | ~200 cycles | NTT/INTT transform | -| `poly_arith_sync` | coeff_a/b[11:0] → coeff_out[11:0] | 1 cycle | Poly add/sub | -| `poly_mul_sync` | 512×coeff → 256×coeff | ~300 cycles | NTT-domain poly multiply | -| `comp_decomp_sync` | coeff_in[11:0], d[4:0] → coeff_out | 1 cycle | Compress/Decompress | -| `sha3_top` | data_i[511:0], mode → hash_o[511:0] | ~24 cycles | SHA3/SHAKE | -| `sample_cbd_sync` | seed[255:0], nonce, eta → 256×coeff | ~300 cycles | CBD sampling | -| `sample_ntt_sync` | rho[255:0], k,i,j → 256×coeff | ~4000 cycles | SampleNTT | -| `s_bram` | rd/wr addr, data | 1 cycle | Single-port BRAM | -| `sd_bram` | rd addr, wr addr, data | 1 cycle | Dual-port BRAM | - -## Test Coverage - -| Module | Verilator (C++) | XSIM (Verilog) | Status | -|--------|:---:|:---:|:---:| -| sha3_top | ✅ | ✅ | PASS | -| keccak_core | — | ✅ | PASS | -| rng_sync | ✅ | ✅ | PASS | -| mod_add_sync | ✅ | ✅ | PASS | -| ntt_core | ✅ | ✅ | PASS | -| poly_arith_sync | ✅ | ✅ | PASS | -| poly_mul_sync | ✅ | ✅ | PASS | -| comp_decomp_sync | ✅ | ✅ | PASS | -| sample_cbd_sync | ✅ | ✅ | PASS | -| sample_ntt_sync | ✅ | ✅ | PASS | -| s_bram / sd_bram | ✅ | ✅ | PASS | -| pipeline_reg | Through parent | — | OK | -| skid_buffer | Through parent | — | OK | - -## Vivado 2019.2 Compatibility Notes - -This project was tested with Vivado 2019.2 on Fedora 44. Known workarounds: - -```bash -# Required: ncurses compatibility library -export LD_PRELOAD=/usr/lib64/libtinfo.so.5 - -# Use -i flag (not -include_dirs) for include paths -xvlog -sv -i . .v - -# Add --timescale to xelab -xelab -s --timescale 1ns/1ps - -# Add --relax for strict SystemVerilog mode -xvlog -sv --relax .v -``` - -## TODO / Roadmap - -- [ ] Top-level integration module (full KeyGen / Encaps / Decaps FSM) -- [ ] AXI-Stream bridge for FPGA integration -- [ ] Resource optimization (share Keccak instances, pipeline balancing) -- [ ] Formal verification of Barrett multiplier -- [ ] Power analysis and side-channel hardening -- [ ] XDC constraints for FPGA synthesis (timing, I/O) - -## License - -[Specify license] - -## References - -- [FIPS 203: ML-KEM](https://csrc.nist.gov/pubs/fips/203/final) — NIST standard -- [FIPS 202: SHA-3 / SHAKE](https://csrc.nist.gov/pubs/fips/202/final) — Keccak-based hash -- [CRYSTALS-Kyber](https://pq-crystals.org/kyber/) — Original submission diff --git a/create_project.tcl b/create_project.tcl index b7eec20..b4f7bfc 100644 --- a/create_project.tcl +++ b/create_project.tcl @@ -1,101 +1,115 @@ -# create_project.tcl — 自动创建 Vivado 工程,添加所有 RTL 源文件和 testbench +# create_project.tcl — 自动创建 Vivado 工程(仿真用),加载 ML-KEM KeyGen +# 顶层 mlkem_top 及其全部叶子算子与参数化 KAT testbench。 +# +# 与已验证的 XSIM 流程(sync_rtl/top/TB/xsim_run.tcl)保持一致: +# - 仅加载 mlkem_top 实际依赖的 14 个源文件 +# - 顶层仿真模块 = tb_mlkem_kg_katK_xsim +# - 运行时安全等级由 generic KP(2/3/4)选择,用例号由 +CASE 选择 # # Usage: # cd ~/Dev/mlkem # vivado -mode batch -source create_project.tcl -# -# Or in Vivado Tcl Console: +# 或在 Vivado Tcl Console 中: # source create_project.tcl +# +# 切换被仿真的配置(默认 KP=2, CASE=0):编辑下方 SIM_KP / SIM_CASE 后重跑, +# 或在工程打开后执行: +# set_property generic "KP=4" [get_filesets sim_1] +# set_property -name {xsim.simulate.xsim.more_options} -value {-testplusarg CASE=2} -objects [get_filesets sim_1] set PROJECT_NAME mlkem set PROJECT_DIR [file normalize [file dirname [info script]]] -# Create project (simulation-only, no FPGA part needed) +# 默认仿真配置(可改) +set SIM_KP 2 ;# ML-KEM 方案:2=512, 3=768, 4=1024 +set SIM_CASE 0 ;# KAT 用例号(K=2: 0..4, K=3/4: 0..2) + +# 仅仿真工程,无需指定 FPGA part(XSim 用默认 part 即可) create_project -force ${PROJECT_NAME} ${PROJECT_DIR}/vivado_prj -# Set top-level testbench -set_property top tb_mlkem_top_xsim [current_fileset -simset] set_property target_simulator XSim [current_project] -# ── Common infrastructure ── -read_verilog -sv [glob ${PROJECT_DIR}/sync_rtl/common/*.v] +# =================================================================== +# RTL 源文件 —— 与 xsim_run.tcl 完全一致的 14 个文件 +# =================================================================== # ── SHA3 / Keccak ── read_verilog -sv ${PROJECT_DIR}/sync_rtl/sha3/keccak_round.v read_verilog -sv ${PROJECT_DIR}/sync_rtl/sha3/keccak_core.v read_verilog -sv ${PROJECT_DIR}/sync_rtl/sha3/sha3_top.v -# ── SHA3 Chain (shared variant for top-level integration) ── -read_verilog -sv ${PROJECT_DIR}/sync_rtl/sha3_chain/sha3_chain_top_shared.v - -# ── RNG ── -read_verilog -sv ${PROJECT_DIR}/sync_rtl/rng/rng_sync.v +# ── 采样 ── +read_verilog -sv ${PROJECT_DIR}/sync_rtl/sample_ntt/sample_ntt_sync.v +read_verilog -sv ${PROJECT_DIR}/sync_rtl/sample_cbd/sample_cbd_sync.v # ── NTT ── -read_verilog -sv ${PROJECT_DIR}/sync_rtl/ntt/zeta_rom.v read_verilog -sv ${PROJECT_DIR}/sync_rtl/ntt/barrett_mul.v +read_verilog -sv ${PROJECT_DIR}/sync_rtl/ntt/zeta_rom.v read_verilog -sv ${PROJECT_DIR}/sync_rtl/ntt/butterfly_unit.v read_verilog -sv ${PROJECT_DIR}/sync_rtl/ntt/ntt_core.v -# ── Polynomial Arithmetic ── -read_verilog -sv ${PROJECT_DIR}/sync_rtl/poly_arith/poly_arith_sync.v - -# ── Polynomial Multiplication ── -read_verilog -sv ${PROJECT_DIR}/sync_rtl/poly_mul/poly_mul_zeta_rom.v +# ── 多项式乘法 ── read_verilog -sv ${PROJECT_DIR}/sync_rtl/poly_mul/basecase_mul.v +read_verilog -sv ${PROJECT_DIR}/sync_rtl/poly_mul/poly_mul_zeta_rom.v read_verilog -sv ${PROJECT_DIR}/sync_rtl/poly_mul/poly_mul_sync.v -# ── Sampling ── -read_verilog -sv ${PROJECT_DIR}/sync_rtl/sample_cbd/sample_cbd_sync_shared.v -read_verilog -sv ${PROJECT_DIR}/sync_rtl/sample_ntt/sample_ntt_sync_shared.v - -# ── Compression & Modular Arithmetic ── -read_verilog -sv ${PROJECT_DIR}/sync_rtl/comp_decomp/comp_decomp_sync.v -read_verilog -sv ${PROJECT_DIR}/sync_rtl/mod_add/mod_add_sync.v - -# ── Storage (BRAM) ── -read_verilog -sv ${PROJECT_DIR}/sync_rtl/storage/s_bram.v -read_verilog -sv ${PROJECT_DIR}/sync_rtl/storage/sd_bram.v - -# ── Top-level Integration ── -read_verilog -sv ${PROJECT_DIR}/sync_rtl/top/keccak_arbiter.v +# ── 顶层 KeyGen 集成 ── read_verilog -sv ${PROJECT_DIR}/sync_rtl/top/mlkem_top.v -# ── Testbench ── -read_verilog -sv ${PROJECT_DIR}/sync_rtl/top/TB/tb_mlkem_top_xsim.v +# ── 参数化 KAT testbench ── +read_verilog -sv ${PROJECT_DIR}/sync_rtl/top/TB/tb_mlkem_kg_katK_xsim.v -# ── Independent KG / EN / DE testbenches ── -read_verilog -sv ${PROJECT_DIR}/sync_rtl/kg/TB/tb_kg_xsim.v -read_verilog -sv ${PROJECT_DIR}/sync_rtl/en/TB/tb_en_xsim.v -read_verilog -sv ${PROJECT_DIR}/sync_rtl/de/TB/tb_de_xsim.v +# =================================================================== +# 仿真设置 +# =================================================================== -# ── Include path for `include directives ── -set_property include_dirs ${PROJECT_DIR} [current_fileset -simset] +# 顶层仿真模块 +set_property top tb_mlkem_kg_katK_xsim [get_filesets sim_1] +set_property top_lib xil_defaultlib [get_filesets sim_1] -# ── Simulation settings ── +# `include "sync_rtl/common/defines.vh" 的包含路径(相对工程根) +set_property include_dirs ${PROJECT_DIR} [get_filesets sim_1] + +# 运行时安全等级:通过 TB 顶层 generic KP 传入(2/3/4) +set_property generic "KP=${SIM_KP}" [get_filesets sim_1] + +# 跑到 $finish 为止;显式时标(XSim 默认已加 --relax,勿重复) set_property -name {xsim.simulate.runtime} -value {all} -objects [get_filesets sim_1] -set_property -name {xsim.elaborate.xelab.more_options} -value {--timescale 1ns/1ps} -objects [get_filesets sim_1] +set_property -name {xsim.elaborate.xelab.more_options} \ + -value {--timescale 1ns/1ps} -objects [get_filesets sim_1] -# Copy test vectors to simulation directory before run -# (Vivado GUI runs xsim in vivado_prj/mlkem.sim/sim_1/behav/xsim/) -# $readmemh looks relative to the xsim working directory +# =================================================================== +# 测试向量:Vivado GUI 在 vivado_prj/mlkem.sim/sim_1/behav/xsim/ 下运行 +# xsim,而 TB 的 $readmemh 路径相对工程根(sync_rtl/top/TB/vectors/…)。 +# 用 compile 的 pre-hook(在 xsim 工作目录、且在 compile/elaborate/simulate +# 之前执行)把整套 KAT 向量复制到同名相对路径下;CASE 经 -testplusarg 选择。 +# +# 注意:2019.2 的 sim_1 没有 simulate.tcl.pre 属性,且追加 -tclbatch 会排在 +# Vivado 自带(含 "run all")的 tclbatch 之后、即仿真跑完才执行(太迟)。 +# 因此用 xsim.compile.tcl.pre —— 它最早执行且就在仿真工作目录里。 +# =================================================================== set pre_tcl [file join ${PROJECT_DIR} vivado_prj copy_vectors_pre.tcl] set fp [open $pre_tcl w] -puts $fp "# Auto-generated: copy test vectors to xsim working directory" +puts $fp "# Auto-generated by create_project.tcl: 复制 KAT 向量到 xsim 工作目录" puts $fp "file mkdir sync_rtl/top/TB/vectors" -puts $fp "file copy -force [file join ${PROJECT_DIR} sync_rtl top TB vectors mlkem_top_input.hex] sync_rtl/top/TB/vectors/" -puts $fp "file copy -force [file join ${PROJECT_DIR} sync_rtl top TB vectors mlkem_top_expected.hex] sync_rtl/top/TB/vectors/" -puts $fp "puts {Vectors copied successfully}" +puts $fp "foreach v \[glob -nocomplain [file join ${PROJECT_DIR} sync_rtl top TB vectors kat_k*_c*_*.hex]\] {" +puts $fp " file copy -force \$v sync_rtl/top/TB/vectors/" +puts $fp "}" +puts $fp "puts {\[create_project\] KAT vectors copied to xsim working dir}" close $fp +set_property -name {xsim.compile.tcl.pre} -value $pre_tcl -objects [get_filesets sim_1] set_property -name {xsim.simulate.xsim.more_options} \ - -value "-tclbatch $pre_tcl" \ + -value "-testplusarg CASE=${SIM_CASE}" \ -objects [get_filesets sim_1] -# Save project +# =================================================================== puts "========================================" puts " Project created: ${PROJECT_DIR}/vivado_prj/${PROJECT_NAME}.xpr" -puts " Run simulation:" -puts " launch_simulation" -puts " run all" +puts " 仿真配置: ML-KEM K=${SIM_KP}, KAT CASE=${SIM_CASE}" +puts " 运行仿真: launch_simulation; run all" +puts " 期望输出: K=${SIM_KP} CASE ${SIM_CASE} PASS: ek==pk, dk==sk" +puts "" +puts " 切换配置(如 K=4):编辑脚本顶部 SIM_KP/SIM_CASE 重跑本脚本," +puts " 或在已打开工程中改 generic 后 relaunch_sim。" puts "========================================"