Compare commits

..

10 Commits

Author SHA1 Message Date
a4261ec3cc update README.md 2026-07-08 02:33:18 +08:00
bfbfc2ef72 Pipeline NTT layer execution 2026-07-08 01:42:13 +08:00
7a7ccacb47 Pipeline poly mul basecase issue 2026-07-08 01:04:01 +08:00
a0bd07bdf8 Reduce NTT butterfly dispatch latency 2026-07-08 00:57:05 +08:00
ce998bb49a Improve timing reports and register poly mul inputs 2026-07-08 00:50:20 +08:00
372a90e601 Pipeline ML-KEM datapath bottlenecks 2026-07-08 00:23:46 +08:00
8c3f4317f5 Fix ML-KEM arithmetic timing paths 2026-07-07 18:28:47 +08:00
2fb1cd67e3 revert comp_decomp_sync.v 2026-06-30 03:17:55 +08:00
b7eb59fb5b add design report and timing analysis report 2026-06-30 02:49:40 +08:00
faf15ee113 docs: 新增 mlkem_top RTL 结构图 (SVG)
用模块内真实实例名/信号名绘制的 mlkem_top RTL 框图:输入端口、控制 FSM
+ 状态寄存器、共享 keccak 三路仲裁、叶子算子(sha3/snt/cbd/ntt/pmul/comp)、
五个 sd_bram 存储库、ss_o 隐式拒绝 mux 与各 dbg 回读抽头。三色连线区分
数据通路 / BRAM 端口 / 共享仲裁。
2026-06-30 02:42:31 +08:00
28 changed files with 6626 additions and 264 deletions

View File

@@ -10,7 +10,7 @@
| **密钥封装** Encaps | `1` | 算法 17 | `ek`, 消息 `m` | 共享密钥 `K`, 密文 `c` | | **密钥封装** Encaps | `1` | 算法 17 | `ek`, 消息 `m` | 共享密钥 `K`, 密文 `c` |
| **密钥解封装** Decaps | `2` | 算法 18 | `dk`, 密文 `c` | 共享密钥 `K`(含隐式拒绝) | | **密钥解封装** Decaps | `2` | 算法 18 | `dk`, 密文 `c` | 共享密钥 `K`(含隐式拒绝) |
模块运行于 **100 MHz**10 ns 周期),低电平异步复位。 模块运行于 **25 MHz**40 ns 周期),低电平异步复位。
--- ---

View File

@@ -34,12 +34,15 @@ read_verilog -sv ${PROJECT_DIR}/sync_rtl/sample_cbd/sample_cbd_sync_shared.v
# ── NTT ── # ── NTT ──
read_verilog -sv ${PROJECT_DIR}/sync_rtl/ntt/barrett_mul.v read_verilog -sv ${PROJECT_DIR}/sync_rtl/ntt/barrett_mul.v
read_verilog -sv ${PROJECT_DIR}/sync_rtl/ntt/barrett_mul_pipe.v
read_verilog -sv ${PROJECT_DIR}/sync_rtl/ntt/zeta_rom.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/butterfly_unit.v
read_verilog -sv ${PROJECT_DIR}/sync_rtl/ntt/butterfly_unit_pipe.v
read_verilog -sv ${PROJECT_DIR}/sync_rtl/ntt/ntt_core.v read_verilog -sv ${PROJECT_DIR}/sync_rtl/ntt/ntt_core.v
# ── 多项式乘法 ── # ── 多项式乘法 ──
read_verilog -sv ${PROJECT_DIR}/sync_rtl/poly_mul/basecase_mul.v read_verilog -sv ${PROJECT_DIR}/sync_rtl/poly_mul/basecase_mul.v
read_verilog -sv ${PROJECT_DIR}/sync_rtl/poly_mul/basecase_mul_pipe.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_zeta_rom.v
read_verilog -sv ${PROJECT_DIR}/sync_rtl/poly_mul/poly_mul_sync.v read_verilog -sv ${PROJECT_DIR}/sync_rtl/poly_mul/poly_mul_sync.v

326
docs/design_report.md Normal file
View File

@@ -0,0 +1,326 @@
# 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`(时序分析与修复方案)。

229
docs/mlkem_top_rtl.svg Normal file
View File

@@ -0,0 +1,229 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg" width="1480" height="1020" viewBox="0 0 1480 1020" font-family="'DejaVu Sans Mono','Consolas',monospace">
<defs>
<marker id="arr" markerWidth="9" markerHeight="9" refX="7" refY="3" orient="auto" markerUnits="strokeWidth">
<path d="M0,0 L7,3 L0,6 z" fill="#444"/>
</marker>
<marker id="arrB" markerWidth="9" markerHeight="9" refX="7" refY="3" orient="auto" markerUnits="strokeWidth">
<path d="M0,0 L7,3 L0,6 z" fill="#1763a6"/>
</marker>
<marker id="arrR" markerWidth="9" markerHeight="9" refX="7" refY="3" orient="auto" markerUnits="strokeWidth">
<path d="M0,0 L7,3 L0,6 z" fill="#a33"/>
</marker>
<style>
.lbl{font-size:13px;fill:#111;}
.sub{font-size:10.5px;fill:#555;}
.port{font-size:11px;fill:#1763a6;}
.net{font-size:10px;fill:#666;}
.ttl{font-size:14px;font-weight:bold;fill:#111;}
.big{font-size:20px;font-weight:bold;fill:#111;}
.wire{stroke:#444;stroke-width:1.4;fill:none;}
.wireB{stroke:#1763a6;stroke-width:1.4;fill:none;}
.wireR{stroke:#a33;stroke-width:1.4;fill:none;}
</style>
</defs>
<rect x="0" y="0" width="1480" height="1020" fill="#fbfbf9"/>
<text x="40" y="34" class="big">mlkem_top — ML-KEM KeyGen / Encaps / Decaps (op_i: 0/1/2, runtime k_i)</text>
<text x="40" y="54" class="sub">实线=数据/系数通路 蓝=BRAM 端口 红=隐式拒绝 / 共享 keccak 仲裁 方框内为 RTL 实例名 / 信号名</text>
<!-- ================= INPUTS (left) ================= -->
<rect x="24" y="90" width="200" height="318" rx="7" fill="#eef4fb" stroke="#9bb8d6"/>
<text x="124" y="110" class="ttl" text-anchor="middle">输入端口</text>
<text x="36" y="134" class="port">k_i[2:0] op_i[1:0]</text>
<text x="36" y="154" class="port">start_i</text>
<text x="36" y="178" class="lbl">d_i[255:0]</text>
<text x="36" y="194" class="sub">KeyGen 种子 d</text>
<text x="36" y="214" class="lbl">z_i[255:0]</text>
<text x="36" y="230" class="sub">隐式拒绝种子 z</text>
<text x="36" y="250" class="lbl">msg_i[255:0]</text>
<text x="36" y="266" class="sub">Encaps 消息 m</text>
<line x1="36" y1="280" x2="212" y2="280" stroke="#9bb8d6" stroke-dasharray="3 3"/>
<text x="36" y="300" class="port">ek_in_* (Encaps)</text>
<text x="36" y="316" class="sub">公钥 ek → u_ek_bram</text>
<text x="36" y="338" class="port">dk_in_* (Decaps)</text>
<text x="36" y="354" class="sub">私钥 dk → 各区路由</text>
<text x="36" y="376" class="port">c_in_* (Decaps)</text>
<text x="36" y="392" class="sub">密文 c → u_c_in_bram</text>
<!-- ================= FSM / CONTROL ================= -->
<rect x="270" y="90" width="250" height="220" rx="8" fill="#fff3e0" stroke="#e0a440"/>
<text x="395" y="112" class="ttl" text-anchor="middle">控制 FSM (st, st_next)</text>
<text x="284" y="136" class="sub">KeyGen: G→A→C→N→M→E→H</text>
<text x="284" y="153" class="sub">Encaps: ENC_H→G→LOAD→A→TDEC</text>
<text x="284" y="167" class="sub"> →C→N→U→C1→E2MV→V→C2</text>
<text x="284" y="184" class="sub">Decaps: DEC_LOAD→DECOMP→SDEC</text>
<text x="284" y="198" class="sub"> →NTT→W→MENC→G→J→(重加密)→CMP</text>
<line x1="284" y1="210" x2="506" y2="210" stroke="#e0a440" stroke-dasharray="3 3"/>
<text x="284" y="230" class="lbl">相位选择 sel_*</text>
<text x="284" y="246" class="sub">sel_sha3 / sel_snt / sel_cbd</text>
<text x="284" y="266" class="lbl">op_r k_r (start 锁存)</text>
<text x="284" y="286" class="lbl">busy_o = (st≠IDLE)</text>
<text x="284" y="302" class="lbl">done_o = (st==DONE)</text>
<!-- registers -->
<rect x="270" y="326" width="250" height="150" rx="8" fill="#fdeef0" stroke="#d68a96"/>
<text x="395" y="346" class="ttl" text-anchor="middle">状态寄存器 (256b)</text>
<text x="284" y="368" class="lbl">rho_r / sigma_r <tspan class="sub">(ρ,σ=G)</tspan></text>
<text x="284" y="388" class="lbl">m_r <tspan class="sub">m / Decaps m'</tspan></text>
<text x="284" y="408" class="lbl">r_r <tspan class="sub">PRF 种子 r / r'</tspan></text>
<text x="284" y="428" class="lbl">hek_r <tspan class="sub">H(ek)</tspan> z_r <tspan class="sub">z</tspan></text>
<text x="284" y="448" class="lbl">ss_r <tspan class="sub">K / K'</tspan> kbar_r <tspan class="sub">K̄=J(z‖c)</tspan></text>
<text x="284" y="468" class="lbl">dec_reject <tspan class="sub">c'≠c 标志</tspan></text>
<!-- ================= SHARED KECCAK ================= -->
<rect x="560" y="92" width="300" height="196" rx="8" fill="#fbecec" stroke="#c98b8b"/>
<text x="710" y="114" class="ttl" text-anchor="middle">共享 Keccak (单核 + 相位 mux)</text>
<rect x="586" y="128" width="248" height="44" rx="6" fill="#fff" stroke="#c98b8b"/>
<text x="710" y="155" class="lbl" text-anchor="middle">u_keccak (keccak_core)</text>
<text x="586" y="196" class="net">kc_state_i_mux / kc_valid_i_mux ◄ 3-way</text>
<text x="586" y="214" class="net">kc_state_o / kc_valid_o ► 门控广播</text>
<text x="586" y="236" class="net">kc_valid_o_sha3 · _snt · _cbd</text>
<text x="586" y="258" class="sub">G/H/J、SampleNTT、CBD 分时复用 1 个核</text>
<text x="586" y="276" class="sub">(相位互斥 → sel_sha3/snt/cbd 选通)</text>
<!-- ================= HASH ENGINE ================= -->
<rect x="560" y="312" width="300" height="120" rx="8" fill="#eef7ee" stroke="#86b886"/>
<text x="710" y="334" class="ttl" text-anchor="middle">u_sha3 (sha3_top_shared)</text>
<text x="574" y="356" class="lbl">SHA3-512 G(·) → ρ‖σ / K‖r / K'‖r'</text>
<text x="574" y="376" class="lbl">SHA3-256 H(ek) (多块吸收)</text>
<text x="574" y="396" class="lbl">SHAKE-256 J(z‖c) → K̄ (多块)</text>
<text x="574" y="416" class="sub">mb_en / mb_block_i 多块mode 选 G/H/J</text>
<!-- ================= SAMPLERS ================= -->
<rect x="560" y="456" width="300" height="76" rx="8" fill="#eef7ee" stroke="#86b886"/>
<text x="710" y="478" class="ttl" text-anchor="middle">u_snt (sample_ntt_sync_shared)</text>
<text x="574" y="498" class="lbl">Â[i][j] = SampleNTT(ρ‖j‖i) → bank_a</text>
<text x="574" y="518" class="sub">snt_coeff / snt_valid / snt_last</text>
<rect x="560" y="548" width="300" height="76" rx="8" fill="#eef7ee" stroke="#86b886"/>
<text x="710" y="570" class="ttl" text-anchor="middle">u_cbd (sample_cbd_sync_shared)</text>
<text x="574" y="590" class="lbl">CBD_η: s/e (KeyGen), y/e1/e2 (Encaps)</text>
<text x="574" y="610" class="sub">cbd_coeff → cbd_modq → bank_se / bank_t</text>
<!-- ================= ARITH ENGINES ================= -->
<rect x="560" y="648" width="300" height="80" rx="8" fill="#e9f1f7" stroke="#7fa8cc"/>
<text x="710" y="670" class="ttl" text-anchor="middle">u_ntt (ntt_core)</text>
<text x="574" y="690" class="lbl">前向 NTT(mode0) / INTT(mode1, ×3303)</text>
<text x="574" y="710" class="sub">ntt_in ◄ bank_se/bank_tntt_coeff ► 写回</text>
<rect x="560" y="744" width="300" height="80" rx="8" fill="#e9f1f7" stroke="#7fa8cc"/>
<text x="710" y="766" class="ttl" text-anchor="middle">u_pmul (poly_mul_sync)</text>
<text x="574" y="786" class="lbl">NTT 域逐点乘 Σ Â∘ŝ / Âᵀ∘ŷ / t̂∘ŷ / ŝ∘û</text>
<text x="574" y="806" class="sub">pm_a_in ◄ bank_a, pm_b_in ◄ bank_se → pm_coeff</text>
<rect x="560" y="840" width="300" height="80" rx="8" fill="#e9f1f7" stroke="#7fa8cc"/>
<text x="710" y="862" class="ttl" text-anchor="middle">u_comp (comp_decomp_sync)</text>
<text x="574" y="882" class="lbl">Compress_d (mode0) / Decompress_d (mode1)</text>
<text x="574" y="902" class="sub">Encaps C1/C2 压缩Decaps DECOMP 解压</text>
<!-- accumulate / mod-add inline note -->
<rect x="270" y="500" width="250" height="120" rx="8" fill="#f4f0fa" stroke="#a78bc9"/>
<text x="395" y="522" class="ttl" text-anchor="middle">顶层内联运算</text>
<text x="284" y="544" class="sub">modQ 累加 (ST_M / U / V / W)</text>
<text x="284" y="562" class="sub">m_accq · u_accq · u_uq · u_vq · u_wq</text>
<text x="284" y="582" class="sub">byteEncode12 (ST_E) e_wbyte</text>
<text x="284" y="600" class="sub">byteDecode12 (TDEC/SDEC) td_c0/td_c1</text>
<text x="284" y="618" class="sub">bit-packer cp_* / 解包 dec_* / 比较 cmp_*</text>
<!-- ================= STORAGE BANKS (right) ================= -->
<rect x="900" y="90" width="340" height="470" rx="8" fill="#eef4fb" stroke="#9bb8d6"/>
<text x="1070" y="112" class="ttl" text-anchor="middle">系数存储库 (sd_bram, 1R/1W, 1拍读)</text>
<rect x="922" y="128" width="296" height="92" rx="6" fill="#fff" stroke="#9bb8d6"/>
<text x="934" y="148" class="lbl">u_bank_a <tspan class="sub">12×4096 (PA_AW=12)</tspan></text>
<text x="934" y="167" class="sub">Â[i][j] 矩阵Decaps 中转 ŝ / e2 / v'</text>
<text x="934" y="185" class="net">ba_rd_addr/ba_rd_data ba_we/ba_wa/ba_wd</text>
<text x="934" y="205" class="net">slot: i*K+j</text>
<rect x="922" y="230" width="296" height="92" rx="6" fill="#fff" stroke="#9bb8d6"/>
<text x="934" y="250" class="lbl">u_bank_se <tspan class="sub">12×2048 (PSE_AW=11)</tspan></text>
<text x="934" y="269" class="sub">ŝ/ê (KeyGen) · ŷ (Encaps) · û (Decaps)</text>
<text x="934" y="287" class="net">bse_rd_addr/bse_rd_data bse_we/wa/wd</text>
<text x="934" y="307" class="net">slot_s=K² slot_e=K²+K</text>
<rect x="922" y="332" width="296" height="92" rx="6" fill="#fff" stroke="#9bb8d6"/>
<text x="934" y="352" class="lbl">u_bank_t <tspan class="sub">12×1024 (PT_AW=10)</tspan></text>
<text x="934" y="371" class="sub">t̂ · psum · Encaps v · Decaps w</text>
<text x="934" y="389" class="net">bt_rd_addr/bt_rd_data bt_we/wa/wd</text>
<text x="934" y="409" class="net">slot_t=K²+2K UPSUM=rel1</text>
<rect x="922" y="434" width="143" height="58" rx="6" fill="#fff" stroke="#9bb8d6"/>
<text x="934" y="453" class="lbl">u_ek_bram</text>
<text x="934" y="469" class="sub">8×2048</text>
<text x="934" y="485" class="sub">ek=Enc12(t̂)‖ρ</text>
<rect x="1075" y="434" width="143" height="58" rx="6" fill="#fff" stroke="#9bb8d6"/>
<text x="1087" y="453" class="lbl">u_dkp_bram</text>
<text x="1087" y="469" class="sub">8×2048</text>
<text x="1087" y="485" class="sub">dk_pke=Enc12(ŝ)</text>
<rect x="922" y="500" width="143" height="50" rx="6" fill="#fff" stroke="#9bb8d6"/>
<text x="934" y="519" class="lbl">u_ct_bram</text>
<text x="934" y="537" class="sub">8×2048 c / c'</text>
<rect x="1075" y="500" width="143" height="50" rx="6" fill="#fff" stroke="#9bb8d6"/>
<text x="1087" y="519" class="lbl">u_c_in_bram</text>
<text x="1087" y="537" class="sub">8×2048 输入 c</text>
<!-- ================= OUTPUTS ================= -->
<rect x="900" y="590" width="340" height="330" rx="8" fill="#eef9ee" stroke="#86b886"/>
<text x="1070" y="612" class="ttl" text-anchor="middle">输出 / 回读</text>
<text x="914" y="638" class="lbl">ss_o = dec_reject ? kbar_r : ss_r</text>
<text x="914" y="654" class="sub">共享密钥 K (Encaps/Decaps)</text>
<line x1="914" y1="666" x2="1226" y2="666" stroke="#86b886" stroke-dasharray="3 3"/>
<text x="914" y="688" class="lbl">dbg_ct_o ◄ u_ct_bram</text>
<text x="914" y="704" class="sub">密文 c 逐字节</text>
<text x="914" y="726" class="lbl">dbg_byte_o ◄ ek / dk_pke</text>
<text x="914" y="742" class="sub">sel: 0=ek_bram 1=dkp_bram</text>
<text x="914" y="764" class="lbl">dbg_dk_o ◄ dk_pke‖ek‖H(ek)‖z</text>
<text x="914" y="780" class="sub">完整 dk 逐字节 (1632/2400/3168B)</text>
<text x="914" y="802" class="lbl">dbg_coeff_o ◄ bank_a/se/t</text>
<text x="914" y="818" class="sub">按 dbg_slot_i 选库, 系数回读</text>
<line x1="914" y1="830" x2="1226" y2="830" stroke="#86b886" stroke-dasharray="3 3"/>
<text x="914" y="852" class="sub">调试抽头: dbg_rho_o dbg_sigma_o</text>
<text x="914" y="868" class="sub"> dbg_r_o dbg_hek_o dbg_mprime_o</text>
<text x="914" y="884" class="sub"> dbg_kbar_o dbg_decz_o dbg_dech_o</text>
<text x="914" y="906" class="sub">done_o / busy_o</text>
<!-- ============ WIRES ============ -->
<!-- inputs -> FSM/regs -->
<path class="wire" d="M224,150 L268,150" marker-end="url(#arr)"/>
<path class="wire" d="M224,182 L268,182 L268,378" marker-end="url(#arr)"/>
<path class="wire" d="M224,218 L256,218 L256,440 L268,440" marker-end="url(#arr)"/>
<path class="wire" d="M224,254 L260,254 L260,388 L268,388" marker-end="url(#arr)"/>
<!-- inputs(ek/dk/c) -> banks (blue) -->
<path class="wireB" d="M224,300 L240,300 L240,68 L1300,68 L1300,460 L1218,460" marker-end="url(#arrB)"/>
<path class="wireB" d="M224,338 L236,338 L236,72 L1296,72 L1296,463 L1218,463" marker-end="url(#arrB)"/>
<path class="wireB" d="M224,376 L232,376 L232,76 L1292,76 L1292,525 L1218,525" marker-end="url(#arrB)"/>
<!-- FSM sel -> keccak mux (red) -->
<path class="wireR" d="M520,230 L540,230 L540,180 L558,180" marker-end="url(#arrR)"/>
<!-- keccak <-> sha3/snt/cbd -->
<path class="wireR" d="M710,288 L710,310" marker-end="url(#arrR)"/>
<path class="wireR" d="M560,210 L546,210 L546,494 L558,494" marker-end="url(#arrR)"/>
<path class="wireR" d="M560,214 L544,214 L544,586 L558,586" marker-end="url(#arrR)"/>
<!-- regs <-> sha3 (rho/sigma/r/ss/kbar) -->
<path class="wire" d="M520,372 L536,372 L536,360 L558,360" marker-end="url(#arr)"/>
<path class="wire" d="M558,392 L538,392 L538,448 L520,448" marker-end="url(#arr)"/>
<!-- datapath engines -> banks (blue) -->
<path class="wireB" d="M860,494 L884,494 L884,174 L920,174" marker-end="url(#arrB)"/>
<path class="wireB" d="M860,590 L880,590 L880,276 L920,276" marker-end="url(#arrB)"/>
<path class="wireB" d="M860,688 L876,688 L876,378 L920,378" marker-end="url(#arrB)"/>
<!-- pmul reads bank_a/se -->
<path class="wireB" d="M920,200 L888,200 L888,772 L860,772" marker-end="url(#arrB)"/>
<path class="wireB" d="M920,300 L892,300 L892,776 L860,776" marker-end="url(#arrB)"/>
<!-- comp <-> ct/c_in -->
<path class="wireB" d="M860,884 L896,884 L896,524 L920,524" marker-end="url(#arrB)"/>
<!-- regs -> ss_o (red reject mux) -->
<path class="wireR" d="M520,448 L528,448 L528,948 L898,948 L898,634 L912,634" marker-end="url(#arrR)"/>
<!-- banks -> outputs (data already adjacent; show coeff readback) -->
<path class="wire" d="M1218,478 L1240,478 L1240,808 L1226,808" marker-end="url(#arr)"/>
<text x="40" y="1000" class="sub">注:同一套叶子算子 / 存储库被 KeyGen·Encaps·Decaps 三条数据通路按 FSM 相位分时复用;Decaps 重加密直接复用整条 Encaps 流水 (ENC_LOAD…ENC_C2)。</text>
</svg>

After

Width:  |  Height:  |  Size: 15 KiB

101
docs/timing_analysis.md Normal file
View File

@@ -0,0 +1,101 @@
# 50 MHz 时序收敛分析与修复方案
> 报告来源:`synth_timing.tcl`OOC 综合 `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 ns**70 逻辑级**CARRY4×26 + LUT×43 + MUXF×3 |
| 面积 | LUT 25.5%、FF 8.6%、BRAM ~5 片、**DSP 0 / 740完全没用** |
之前那份 `timing.rpt`WNS -17.7 ns关键路径在 comp_decomp 的组合除法器)已经过时——
`717a992` 用 Barrett 乘法替换了组合除法器,comp_decomp 不再是瓶颈。本次重新综合后,
**瓶颈转移到 `poly_mul` 的 basecase 乘法器 `u_pmul`**
## 二、根因
`basecase_mul`NTT 域 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 仍逐字节正确。
## 五、复现命令
```bash
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 占用)
```

BIN
reports/mlkem_top_synth.dcp Normal file

Binary file not shown.

652
reports/timing_synth.rpt Normal file
View File

@@ -0,0 +1,652 @@
Copyright 1986-2019 Xilinx, Inc. All Rights Reserved.
-------------------------------------------------------------------------------------------------
| Tool Version : Vivado v.2019.2 (lin64) Build 2708876 Wed Nov 6 21:39:14 MST 2019
| Date : Tue Jul 7 15:28:57 2026
| Host : fedora running 64-bit unknown
| Command : report_timing_summary -file /home/fallensigh/Dev/mlkem/reports/timing_synth.rpt
| Design : mlkem_top
| Device : 7a200t-fbg676
| Speed File : -1 PRODUCTION 1.23 2018-06-13
-------------------------------------------------------------------------------------------------
Timing Summary Report
------------------------------------------------------------------------------------------------
| Timer Settings
| --------------
------------------------------------------------------------------------------------------------
Enable Multi Corner Analysis : Yes
Enable Pessimism Removal : Yes
Pessimism Removal Resolution : Nearest Common Node
Enable Input Delay Default Clock : No
Enable Preset / Clear Arcs : No
Disable Flight Delays : No
Ignore I/O Paths : No
Timing Early Launch at Borrowing Latches : No
Borrow Time for Max Delay Exceptions : Yes
Merge Timing Exceptions : Yes
Corner Analyze Analyze
Name Max Paths Min Paths
------ --------- ---------
Slow Yes Yes
Fast Yes Yes
check_timing report
Table of Contents
-----------------
1. checking no_clock
2. checking constant_clock
3. checking pulse_width_clock
4. checking unconstrained_internal_endpoints
5. checking no_input_delay
6. checking no_output_delay
7. checking multiple_clock
8. checking generated_clocks
9. checking loops
10. checking partial_input_delay
11. checking partial_output_delay
12. checking latch_loops
1. checking no_clock
--------------------
There are 0 register/latch pins with no clock.
2. checking constant_clock
--------------------------
There are 0 register/latch pins with constant_clock.
3. checking pulse_width_clock
-----------------------------
There are 0 register/latch pins which need pulse_width check
4. checking unconstrained_internal_endpoints
--------------------------------------------
There are 0 pins that are not constrained for maximum delay.
There are 0 pins that are not constrained for maximum delay due to constant clock.
5. checking no_input_delay
--------------------------
There are 629 input ports with no input delay specified. (HIGH)
There are 0 input ports with no input delay but user has a false path constraint.
6. checking no_output_delay
---------------------------
There are 2342 ports with no output delay specified. (HIGH)
There are 0 ports with no output delay but user has a false path constraint
There are 0 ports with no output delay but with a timing clock defined on it or propagating through it
7. checking multiple_clock
--------------------------
There are 0 register/latch pins with multiple clocks.
8. checking generated_clocks
----------------------------
There are 0 generated clocks that are not connected to a clock source.
9. checking loops
-----------------
There are 0 combinational loops in the design.
10. checking partial_input_delay
--------------------------------
There are 0 input ports with partial input delay specified.
11. checking partial_output_delay
---------------------------------
There are 0 ports with partial output delay specified.
12. checking latch_loops
------------------------
There are 0 combinational latch loops in the design through latch input
------------------------------------------------------------------------------------------------
| Design Timing Summary
| ---------------------
------------------------------------------------------------------------------------------------
WNS(ns) TNS(ns) TNS Failing Endpoints TNS Total Endpoints WHS(ns) THS(ns) THS Failing Endpoints THS Total Endpoints WPWS(ns) TPWS(ns) TPWS Failing Endpoints TPWS Total Endpoints
------- ------- --------------------- ------------------- ------- ------- --------------------- ------------------- -------- -------- ---------------------- --------------------
-40.169 -37681.270 3108 45226 0.080 0.000 0 45226 9.020 0.000 0 23791
Timing constraints are not met.
------------------------------------------------------------------------------------------------
| Clock Summary
| -------------
------------------------------------------------------------------------------------------------
Clock Waveform(ns) Period(ns) Frequency(MHz)
----- ------------ ---------- --------------
sysclk {0.000 10.000} 20.000 50.000
------------------------------------------------------------------------------------------------
| Intra Clock Table
| -----------------
------------------------------------------------------------------------------------------------
Clock WNS(ns) TNS(ns) TNS Failing Endpoints TNS Total Endpoints WHS(ns) THS(ns) THS Failing Endpoints THS Total Endpoints WPWS(ns) TPWS(ns) TPWS Failing Endpoints TPWS Total Endpoints
----- ------- ------- --------------------- ------------------- ------- ------- --------------------- ------------------- -------- -------- ---------------------- --------------------
sysclk -40.169 -37681.270 3108 45226 0.080 0.000 0 45226 9.020 0.000 0 23791
------------------------------------------------------------------------------------------------
| Inter Clock Table
| -----------------
------------------------------------------------------------------------------------------------
From Clock To Clock WNS(ns) TNS(ns) TNS Failing Endpoints TNS Total Endpoints WHS(ns) THS(ns) THS Failing Endpoints THS Total Endpoints
---------- -------- ------- ------- --------------------- ------------------- ------- ------- --------------------- -------------------
------------------------------------------------------------------------------------------------
| Other Path Groups Table
| -----------------------
------------------------------------------------------------------------------------------------
Path Group From Clock To Clock WNS(ns) TNS(ns) TNS Failing Endpoints TNS Total Endpoints WHS(ns) THS(ns) THS Failing Endpoints THS Total Endpoints
---------- ---------- -------- ------- ------- --------------------- ------------------- ------- ------- --------------------- -------------------
------------------------------------------------------------------------------------------------
| Timing Details
| --------------
------------------------------------------------------------------------------------------------
---------------------------------------------------------------------------------------------------
From Clock: sysclk
To Clock: sysclk
Setup : 3108 Failing Endpoints, Worst Slack -40.169ns, Total Violation -37681.271ns
Hold : 0 Failing Endpoints, Worst Slack 0.080ns, Total Violation 0.000ns
PW : 0 Failing Endpoints, Worst Slack 9.020ns, Total Violation 0.000ns
---------------------------------------------------------------------------------------------------
Max Delay Paths
--------------------------------------------------------------------------------------
Slack (VIOLATED) : -40.169ns (required time - arrival time)
Source: st_reg[3]/C
(rising edge-triggered cell FDCE clocked by sysclk {rise@0.000ns fall@10.000ns period=20.000ns})
Destination: u_comp/u_pipe/data_r_reg[0]/D
(rising edge-triggered cell FDCE clocked by sysclk {rise@0.000ns fall@10.000ns period=20.000ns})
Path Group: sysclk
Path Type: Setup (Max at Slow Process Corner)
Requirement: 20.000ns (sysclk rise@20.000ns - sysclk rise@0.000ns)
Data Path Delay: 60.018ns (logic 37.409ns (62.330%) route 22.609ns (37.670%))
Logic Levels: 115 (CARRY4=105 LUT1=1 LUT2=2 LUT3=2 LUT5=3 LUT6=2)
Clock Path Skew: -0.145ns (DCD - SCD + CPR)
Destination Clock Delay (DCD): 2.106ns = ( 22.106 - 20.000 )
Source Clock Delay (SCD): 2.430ns
Clock Pessimism Removal (CPR): 0.178ns
Clock Uncertainty: 0.035ns ((TSJ^2 + TIJ^2)^1/2 + DJ) / 2 + PE
Total System Jitter (TSJ): 0.071ns
Total Input Jitter (TIJ): 0.000ns
Discrete Jitter (DJ): 0.000ns
Phase Error (PE): 0.000ns
Location Delay type Incr(ns) Path(ns) Netlist Resource(s)
------------------------------------------------------------------- -------------------
(clock sysclk rise edge) 0.000 0.000 r
0.000 0.000 r clk (IN)
net (fo=0) 0.000 0.000 clk
IBUF (Prop_ibuf_I_O) 0.950 0.950 r clk_IBUF_inst/O
net (fo=1, unplaced) 0.800 1.750 clk_IBUF
BUFG (Prop_bufg_I_O) 0.096 1.846 r clk_IBUF_BUFG_inst/O
net (fo=23790, unplaced) 0.584 2.430 clk_IBUF_BUFG
FDCE r st_reg[3]/C
------------------------------------------------------------------- -------------------
FDCE (Prop_fdce_C_Q) 0.456 2.886 f st_reg[3]/Q
net (fo=118, unplaced) 0.910 3.796 u_bank_a/snt_valid_reg[2]
LUT5 (Prop_lut5_I0_O) 0.295 4.091 r u_bank_a/valid_r_i_2/O
net (fo=211, unplaced) 0.567 4.658 u_bank_a_n_38
LUT3 (Prop_lut3_I1_O) 0.124 4.782 r data_r[11]_i_227/O
net (fo=2, unplaced) 0.460 5.242 cd_in_mux[7]
LUT2 (Prop_lut2_I1_O) 0.124 5.366 r data_r[11]_i_170/O
net (fo=1, unplaced) 0.000 5.366 data_r[11]_i_170_n_0
CARRY4 (Prop_carry4_S[1]_CO[3])
0.550 5.916 r data_r_reg[11]_i_135/CO[3]
net (fo=1, unplaced) 0.009 5.925 data_r_reg[11]_i_135_n_0
CARRY4 (Prop_carry4_CI_O[1])
0.348 6.273 r data_r_reg[11]_i_140/O[1]
net (fo=3, unplaced) 0.629 6.902 data_r_reg[11]_i_140_n_6
LUT3 (Prop_lut3_I0_O) 0.306 7.208 r data_r[11]_i_141/O
net (fo=2, unplaced) 0.460 7.668 data_r[11]_i_141_n_0
LUT5 (Prop_lut5_I4_O) 0.124 7.792 r data_r[11]_i_130/O
net (fo=2, unplaced) 0.460 8.252 data_r[11]_i_130_n_0
LUT6 (Prop_lut6_I0_O) 0.124 8.376 r data_r[11]_i_134/O
net (fo=1, unplaced) 0.000 8.376 data_r[11]_i_134_n_0
CARRY4 (Prop_carry4_S[0]_CO[3])
0.532 8.908 r data_r_reg[11]_i_101/CO[3]
net (fo=1, unplaced) 0.000 8.908 data_r_reg[11]_i_101_n_0
CARRY4 (Prop_carry4_CI_O[1])
0.348 9.256 r data_r_reg[11]_i_252/O[1]
net (fo=1, unplaced) 0.715 9.971 product[17]
CARRY4 (Prop_carry4_S[1]_CO[3])
0.854 10.825 r data_r_reg[11]_i_203/CO[3]
net (fo=1, unplaced) 0.000 10.825 data_r_reg[11]_i_203_n_0
CARRY4 (Prop_carry4_CI_O[3])
0.329 11.154 r data_r_reg[11]_i_321/O[3]
net (fo=1, unplaced) 0.618 11.772 dividend[23]
LUT2 (Prop_lut2_I1_O) 0.307 12.079 r data_r[11]_i_365/O
net (fo=1, unplaced) 0.000 12.079 data_r[11]_i_365_n_0
CARRY4 (Prop_carry4_S[0]_CO[2])
0.536 12.615 r data_r_reg[11]_i_351/CO[2]
net (fo=3, unplaced) 0.354 12.969 data_r_reg[11]_i_351_n_1
CARRY4 (Prop_carry4_CYINIT_CO[3])
0.766 13.735 r data_r_reg[11]_i_338/CO[3]
net (fo=3, unplaced) 0.838 14.573 data_r_reg[11]_i_338_n_0
CARRY4 (Prop_carry4_CYINIT_CO[2])
0.617 15.190 r data_r_reg[11]_i_334/CO[2]
net (fo=2, unplaced) 0.347 15.537 data_r_reg[11]_i_334_n_1
CARRY4 (Prop_carry4_CYINIT_CO[3])
0.766 16.303 r data_r_reg[11]_i_306/CO[3]
net (fo=23, unplaced) 0.981 17.284 data_r_reg[11]_i_306_n_0
LUT6 (Prop_lut6_I5_O) 0.124 17.408 r data_r[11]_i_311/O
net (fo=1, unplaced) 0.639 18.047 data_r[11]_i_311_n_0
CARRY4 (Prop_carry4_DI[1]_CO[3])
0.507 18.554 r data_r_reg[11]_i_286/CO[3]
net (fo=1, unplaced) 0.000 18.554 data_r_reg[11]_i_286_n_0
CARRY4 (Prop_carry4_CI_CO[1])
0.178 18.732 r data_r_reg[11]_i_285/CO[1]
net (fo=20, unplaced) 0.585 19.317 data_r_reg[11]_i_285_n_2
CARRY4 (Prop_carry4_CYINIT_CO[3])
0.788 20.105 r data_r_reg[11]_i_316/CO[3]
net (fo=1, unplaced) 0.009 20.114 data_r_reg[11]_i_316_n_0
CARRY4 (Prop_carry4_CI_CO[3])
0.114 20.228 r data_r_reg[11]_i_289/CO[3]
net (fo=1, unplaced) 0.000 20.228 data_r_reg[11]_i_289_n_0
CARRY4 (Prop_carry4_CI_CO[3])
0.114 20.342 r data_r_reg[11]_i_261/CO[3]
net (fo=1, unplaced) 0.000 20.342 data_r_reg[11]_i_261_n_0
CARRY4 (Prop_carry4_CI_CO[1])
0.178 20.520 r data_r_reg[11]_i_260/CO[1]
net (fo=20, unplaced) 0.585 21.105 data_r_reg[11]_i_260_n_2
CARRY4 (Prop_carry4_CYINIT_CO[3])
0.788 21.893 r data_r_reg[11]_i_294/CO[3]
net (fo=1, unplaced) 0.009 21.902 data_r_reg[11]_i_294_n_0
CARRY4 (Prop_carry4_CI_CO[3])
0.114 22.016 r data_r_reg[11]_i_264/CO[3]
net (fo=1, unplaced) 0.000 22.016 data_r_reg[11]_i_264_n_0
CARRY4 (Prop_carry4_CI_CO[3])
0.114 22.130 r data_r_reg[11]_i_236/CO[3]
net (fo=1, unplaced) 0.000 22.130 data_r_reg[11]_i_236_n_0
CARRY4 (Prop_carry4_CI_CO[1])
0.178 22.308 r data_r_reg[11]_i_235/CO[1]
net (fo=20, unplaced) 0.585 22.893 data_r_reg[11]_i_235_n_2
CARRY4 (Prop_carry4_CYINIT_CO[3])
0.788 23.681 r data_r_reg[11]_i_269/CO[3]
net (fo=1, unplaced) 0.009 23.690 data_r_reg[11]_i_269_n_0
CARRY4 (Prop_carry4_CI_CO[3])
0.114 23.804 r data_r_reg[11]_i_239/CO[3]
net (fo=1, unplaced) 0.000 23.804 data_r_reg[11]_i_239_n_0
CARRY4 (Prop_carry4_CI_CO[3])
0.114 23.918 r data_r_reg[11]_i_190/CO[3]
net (fo=1, unplaced) 0.000 23.918 data_r_reg[11]_i_190_n_0
CARRY4 (Prop_carry4_CI_CO[1])
0.178 24.096 r data_r_reg[11]_i_189/CO[1]
net (fo=20, unplaced) 0.585 24.681 data_r_reg[11]_i_189_n_2
CARRY4 (Prop_carry4_CYINIT_CO[3])
0.788 25.469 r data_r_reg[11]_i_244/CO[3]
net (fo=1, unplaced) 0.009 25.478 data_r_reg[11]_i_244_n_0
CARRY4 (Prop_carry4_CI_CO[3])
0.114 25.592 r data_r_reg[11]_i_193/CO[3]
net (fo=1, unplaced) 0.000 25.592 data_r_reg[11]_i_193_n_0
CARRY4 (Prop_carry4_CI_CO[3])
0.114 25.706 r data_r_reg[11]_i_144/CO[3]
net (fo=1, unplaced) 0.000 25.706 data_r_reg[11]_i_144_n_0
CARRY4 (Prop_carry4_CI_CO[1])
0.178 25.884 r data_r_reg[11]_i_143/CO[1]
net (fo=20, unplaced) 0.585 26.469 data_r_reg[11]_i_143_n_2
CARRY4 (Prop_carry4_CYINIT_CO[3])
0.788 27.257 r data_r_reg[11]_i_198/CO[3]
net (fo=1, unplaced) 0.009 27.266 data_r_reg[11]_i_198_n_0
CARRY4 (Prop_carry4_CI_CO[3])
0.114 27.380 r data_r_reg[11]_i_147/CO[3]
net (fo=1, unplaced) 0.000 27.380 data_r_reg[11]_i_147_n_0
CARRY4 (Prop_carry4_CI_CO[3])
0.114 27.494 r data_r_reg[11]_i_111/CO[3]
net (fo=1, unplaced) 0.000 27.494 data_r_reg[11]_i_111_n_0
CARRY4 (Prop_carry4_CI_CO[1])
0.178 27.672 r data_r_reg[11]_i_110/CO[1]
net (fo=20, unplaced) 0.585 28.257 data_r_reg[11]_i_110_n_2
CARRY4 (Prop_carry4_CYINIT_CO[3])
0.788 29.045 r data_r_reg[11]_i_152/CO[3]
net (fo=1, unplaced) 0.009 29.054 data_r_reg[11]_i_152_n_0
CARRY4 (Prop_carry4_CI_CO[3])
0.114 29.168 r data_r_reg[11]_i_114/CO[3]
net (fo=1, unplaced) 0.000 29.168 data_r_reg[11]_i_114_n_0
CARRY4 (Prop_carry4_CI_CO[3])
0.114 29.282 r data_r_reg[11]_i_85/CO[3]
net (fo=1, unplaced) 0.000 29.282 data_r_reg[11]_i_85_n_0
CARRY4 (Prop_carry4_CI_CO[1])
0.178 29.460 r data_r_reg[11]_i_84/CO[1]
net (fo=20, unplaced) 0.585 30.045 data_r_reg[11]_i_84_n_2
CARRY4 (Prop_carry4_CYINIT_CO[3])
0.788 30.833 r data_r_reg[11]_i_119/CO[3]
net (fo=1, unplaced) 0.009 30.842 data_r_reg[11]_i_119_n_0
CARRY4 (Prop_carry4_CI_CO[3])
0.114 30.956 r data_r_reg[11]_i_88/CO[3]
net (fo=1, unplaced) 0.000 30.956 data_r_reg[11]_i_88_n_0
CARRY4 (Prop_carry4_CI_CO[3])
0.114 31.070 r data_r_reg[11]_i_64/CO[3]
net (fo=1, unplaced) 0.000 31.070 data_r_reg[11]_i_64_n_0
CARRY4 (Prop_carry4_CI_CO[1])
0.178 31.248 r data_r_reg[11]_i_63/CO[1]
net (fo=20, unplaced) 0.585 31.833 data_r_reg[11]_i_63_n_2
CARRY4 (Prop_carry4_CYINIT_CO[3])
0.788 32.621 r data_r_reg[11]_i_93/CO[3]
net (fo=1, unplaced) 0.009 32.630 data_r_reg[11]_i_93_n_0
CARRY4 (Prop_carry4_CI_CO[3])
0.114 32.744 r data_r_reg[11]_i_67/CO[3]
net (fo=1, unplaced) 0.000 32.744 data_r_reg[11]_i_67_n_0
CARRY4 (Prop_carry4_CI_CO[3])
0.114 32.858 r data_r_reg[11]_i_44/CO[3]
net (fo=1, unplaced) 0.000 32.858 data_r_reg[11]_i_44_n_0
CARRY4 (Prop_carry4_CI_CO[1])
0.178 33.036 r data_r_reg[11]_i_43/CO[1]
net (fo=20, unplaced) 0.585 33.621 data_r_reg[11]_i_43_n_2
CARRY4 (Prop_carry4_CYINIT_CO[3])
0.788 34.409 r data_r_reg[11]_i_72/CO[3]
net (fo=1, unplaced) 0.009 34.418 data_r_reg[11]_i_72_n_0
CARRY4 (Prop_carry4_CI_CO[3])
0.114 34.532 r data_r_reg[11]_i_47/CO[3]
net (fo=1, unplaced) 0.000 34.532 data_r_reg[11]_i_47_n_0
CARRY4 (Prop_carry4_CI_CO[3])
0.114 34.646 r data_r_reg[11]_i_28/CO[3]
net (fo=1, unplaced) 0.000 34.646 data_r_reg[11]_i_28_n_0
CARRY4 (Prop_carry4_CI_CO[1])
0.178 34.824 r data_r_reg[11]_i_27/CO[1]
net (fo=20, unplaced) 0.585 35.409 data_r_reg[11]_i_27_n_2
CARRY4 (Prop_carry4_CYINIT_CO[3])
0.788 36.197 r data_r_reg[11]_i_52/CO[3]
net (fo=1, unplaced) 0.009 36.206 data_r_reg[11]_i_52_n_0
CARRY4 (Prop_carry4_CI_CO[3])
0.114 36.320 r data_r_reg[11]_i_31/CO[3]
net (fo=1, unplaced) 0.000 36.320 data_r_reg[11]_i_31_n_0
CARRY4 (Prop_carry4_CI_CO[3])
0.114 36.434 r data_r_reg[11]_i_16/CO[3]
net (fo=1, unplaced) 0.000 36.434 data_r_reg[11]_i_16_n_0
CARRY4 (Prop_carry4_CI_CO[1])
0.178 36.612 r data_r_reg[11]_i_15/CO[1]
net (fo=20, unplaced) 0.585 37.197 data_r_reg[11]_i_15_n_2
CARRY4 (Prop_carry4_CYINIT_CO[3])
0.788 37.985 r data_r_reg[11]_i_37/CO[3]
net (fo=1, unplaced) 0.009 37.994 data_r_reg[11]_i_37_n_0
CARRY4 (Prop_carry4_CI_CO[3])
0.114 38.108 r data_r_reg[11]_i_22/CO[3]
net (fo=1, unplaced) 0.000 38.108 data_r_reg[11]_i_22_n_0
CARRY4 (Prop_carry4_CI_CO[3])
0.114 38.222 r data_r_reg[11]_i_14/CO[3]
net (fo=1, unplaced) 0.000 38.222 data_r_reg[11]_i_14_n_0
CARRY4 (Prop_carry4_CI_CO[1])
0.178 38.400 r data_r_reg[11]_i_6/CO[1]
net (fo=23, unplaced) 0.588 38.988 div_result[11]
CARRY4 (Prop_carry4_CYINIT_CO[3])
0.788 39.776 r data_r_reg[10]_i_11/CO[3]
net (fo=1, unplaced) 0.009 39.785 data_r_reg[10]_i_11_n_0
CARRY4 (Prop_carry4_CI_CO[3])
0.114 39.899 r data_r_reg[10]_i_6/CO[3]
net (fo=1, unplaced) 0.000 39.899 data_r_reg[10]_i_6_n_0
CARRY4 (Prop_carry4_CI_CO[3])
0.114 40.013 r data_r_reg[10]_i_3/CO[3]
net (fo=1, unplaced) 0.000 40.013 data_r_reg[10]_i_3_n_0
CARRY4 (Prop_carry4_CI_CO[1])
0.178 40.191 r data_r_reg[10]_i_2/CO[1]
net (fo=23, unplaced) 0.588 40.779 div_result[10]
CARRY4 (Prop_carry4_CYINIT_CO[3])
0.788 41.567 r data_r_reg[9]_i_16/CO[3]
net (fo=1, unplaced) 0.009 41.576 data_r_reg[9]_i_16_n_0
CARRY4 (Prop_carry4_CI_CO[3])
0.114 41.690 r data_r_reg[9]_i_11/CO[3]
net (fo=1, unplaced) 0.000 41.690 data_r_reg[9]_i_11_n_0
CARRY4 (Prop_carry4_CI_CO[3])
0.114 41.804 r data_r_reg[9]_i_4/CO[3]
net (fo=1, unplaced) 0.000 41.804 data_r_reg[9]_i_4_n_0
CARRY4 (Prop_carry4_CI_CO[1])
0.178 41.982 r data_r_reg[9]_i_2/CO[1]
net (fo=23, unplaced) 0.588 42.570 div_result[9]
CARRY4 (Prop_carry4_CYINIT_CO[3])
0.788 43.358 r data_r_reg[8]_i_11/CO[3]
net (fo=1, unplaced) 0.009 43.367 data_r_reg[8]_i_11_n_0
CARRY4 (Prop_carry4_CI_CO[3])
0.114 43.481 r data_r_reg[8]_i_6/CO[3]
net (fo=1, unplaced) 0.000 43.481 data_r_reg[8]_i_6_n_0
CARRY4 (Prop_carry4_CI_CO[3])
0.114 43.595 r data_r_reg[8]_i_3/CO[3]
net (fo=1, unplaced) 0.000 43.595 data_r_reg[8]_i_3_n_0
CARRY4 (Prop_carry4_CI_CO[1])
0.178 43.773 r data_r_reg[8]_i_2/CO[1]
net (fo=23, unplaced) 0.588 44.361 div_result[8]
CARRY4 (Prop_carry4_CYINIT_CO[3])
0.788 45.149 r data_r_reg[7]_i_21/CO[3]
net (fo=1, unplaced) 0.009 45.158 data_r_reg[7]_i_21_n_0
CARRY4 (Prop_carry4_CI_CO[3])
0.114 45.272 r data_r_reg[7]_i_16/CO[3]
net (fo=1, unplaced) 0.000 45.272 data_r_reg[7]_i_16_n_0
CARRY4 (Prop_carry4_CI_CO[3])
0.114 45.386 r data_r_reg[7]_i_13/CO[3]
net (fo=1, unplaced) 0.000 45.386 data_r_reg[7]_i_13_n_0
CARRY4 (Prop_carry4_CI_CO[1])
0.178 45.564 r data_r_reg[7]_i_4/CO[1]
net (fo=23, unplaced) 0.588 46.152 div_result[7]
CARRY4 (Prop_carry4_CYINIT_CO[3])
0.788 46.940 r data_r_reg[6]_i_11/CO[3]
net (fo=1, unplaced) 0.009 46.949 data_r_reg[6]_i_11_n_0
CARRY4 (Prop_carry4_CI_CO[3])
0.114 47.063 r data_r_reg[6]_i_6/CO[3]
net (fo=1, unplaced) 0.000 47.063 data_r_reg[6]_i_6_n_0
CARRY4 (Prop_carry4_CI_CO[3])
0.114 47.177 r data_r_reg[6]_i_3/CO[3]
net (fo=1, unplaced) 0.000 47.177 data_r_reg[6]_i_3_n_0
CARRY4 (Prop_carry4_CI_CO[1])
0.178 47.355 r data_r_reg[6]_i_2/CO[1]
net (fo=23, unplaced) 0.588 47.943 div_result[6]
CARRY4 (Prop_carry4_CYINIT_CO[3])
0.788 48.731 r data_r_reg[5]_i_11/CO[3]
net (fo=1, unplaced) 0.009 48.740 data_r_reg[5]_i_11_n_0
CARRY4 (Prop_carry4_CI_CO[3])
0.114 48.854 r data_r_reg[5]_i_6/CO[3]
net (fo=1, unplaced) 0.000 48.854 data_r_reg[5]_i_6_n_0
CARRY4 (Prop_carry4_CI_CO[3])
0.114 48.968 r data_r_reg[5]_i_3/CO[3]
net (fo=1, unplaced) 0.000 48.968 data_r_reg[5]_i_3_n_0
CARRY4 (Prop_carry4_CI_CO[1])
0.178 49.146 r data_r_reg[5]_i_2/CO[1]
net (fo=23, unplaced) 0.588 49.734 div_result[5]
CARRY4 (Prop_carry4_CYINIT_CO[3])
0.788 50.522 r data_r_reg[4]_i_11/CO[3]
net (fo=1, unplaced) 0.009 50.531 data_r_reg[4]_i_11_n_0
CARRY4 (Prop_carry4_CI_CO[3])
0.114 50.645 r data_r_reg[4]_i_6/CO[3]
net (fo=1, unplaced) 0.000 50.645 data_r_reg[4]_i_6_n_0
CARRY4 (Prop_carry4_CI_CO[3])
0.114 50.759 r data_r_reg[4]_i_3/CO[3]
net (fo=1, unplaced) 0.000 50.759 data_r_reg[4]_i_3_n_0
CARRY4 (Prop_carry4_CI_CO[1])
0.178 50.937 r data_r_reg[4]_i_2/CO[1]
net (fo=23, unplaced) 0.588 51.525 div_result[4]
CARRY4 (Prop_carry4_CYINIT_CO[3])
0.788 52.313 r data_r_reg[3]_i_20/CO[3]
net (fo=1, unplaced) 0.009 52.322 data_r_reg[3]_i_20_n_0
CARRY4 (Prop_carry4_CI_CO[3])
0.114 52.436 r data_r_reg[3]_i_15/CO[3]
net (fo=1, unplaced) 0.000 52.436 data_r_reg[3]_i_15_n_0
CARRY4 (Prop_carry4_CI_CO[3])
0.114 52.550 r data_r_reg[3]_i_12/CO[3]
net (fo=1, unplaced) 0.000 52.550 data_r_reg[3]_i_12_n_0
CARRY4 (Prop_carry4_CI_CO[1])
0.178 52.728 r data_r_reg[3]_i_4/CO[1]
net (fo=23, unplaced) 0.588 53.316 div_result[3]
CARRY4 (Prop_carry4_CYINIT_CO[3])
0.788 54.104 r data_r_reg[2]_i_11/CO[3]
net (fo=1, unplaced) 0.009 54.113 data_r_reg[2]_i_11_n_0
CARRY4 (Prop_carry4_CI_CO[3])
0.114 54.227 r data_r_reg[2]_i_6/CO[3]
net (fo=1, unplaced) 0.000 54.227 data_r_reg[2]_i_6_n_0
CARRY4 (Prop_carry4_CI_CO[3])
0.114 54.341 r data_r_reg[2]_i_3/CO[3]
net (fo=1, unplaced) 0.000 54.341 data_r_reg[2]_i_3_n_0
CARRY4 (Prop_carry4_CI_CO[1])
0.178 54.519 r data_r_reg[2]_i_2/CO[1]
net (fo=23, unplaced) 0.588 55.107 div_result[2]
CARRY4 (Prop_carry4_CYINIT_CO[3])
0.788 55.895 r data_r_reg[1]_i_11/CO[3]
net (fo=1, unplaced) 0.009 55.904 data_r_reg[1]_i_11_n_0
CARRY4 (Prop_carry4_CI_CO[3])
0.114 56.018 r data_r_reg[1]_i_6/CO[3]
net (fo=1, unplaced) 0.000 56.018 data_r_reg[1]_i_6_n_0
CARRY4 (Prop_carry4_CI_CO[3])
0.114 56.132 r data_r_reg[1]_i_3/CO[3]
net (fo=1, unplaced) 0.000 56.132 data_r_reg[1]_i_3_n_0
CARRY4 (Prop_carry4_CI_CO[1])
0.178 56.310 r data_r_reg[1]_i_2/CO[1]
net (fo=23, unplaced) 0.588 56.898 div_result[1]
CARRY4 (Prop_carry4_CYINIT_CO[3])
0.788 57.686 r data_r_reg[0]_i_10/CO[3]
net (fo=1, unplaced) 0.009 57.695 data_r_reg[0]_i_10_n_0
CARRY4 (Prop_carry4_CI_CO[3])
0.114 57.809 r data_r_reg[0]_i_5/CO[3]
net (fo=1, unplaced) 0.000 57.809 data_r_reg[0]_i_5_n_0
CARRY4 (Prop_carry4_CI_CO[3])
0.114 57.923 r data_r_reg[0]_i_3/CO[3]
net (fo=1, unplaced) 0.000 57.923 data_r_reg[0]_i_3_n_0
CARRY4 (Prop_carry4_CI_CO[0])
0.293 58.216 f data_r_reg[0]_i_2/CO[0]
net (fo=3, unplaced) 0.329 58.545 u_comp/u_pipe/div_result[0]
LUT1 (Prop_lut1_I0_O) 0.367 58.912 r u_comp/u_pipe/data_r[3]_i_11/O
net (fo=1, unplaced) 0.000 58.912 u_comp/u_pipe/data_r[3]_i_11_n_0
CARRY4 (Prop_carry4_S[0]_CO[3])
0.532 59.444 r u_comp/u_pipe/data_r_reg[3]_i_3/CO[3]
net (fo=1, unplaced) 0.009 59.453 u_comp/u_pipe/data_r_reg[3]_i_3_n_0
CARRY4 (Prop_carry4_CI_CO[3])
0.114 59.567 r u_comp/u_pipe/data_r_reg[7]_i_3/CO[3]
net (fo=1, unplaced) 0.000 59.567 u_comp/u_pipe/data_r_reg[7]_i_3_n_0
CARRY4 (Prop_carry4_CI_O[3])
0.329 59.896 r u_comp/u_pipe/data_r_reg[11]_i_5/O[3]
net (fo=2, unplaced) 0.824 60.720 u_comp/u_pipe/data_r_reg[11]_i_5_n_4
CARRY4 (Prop_carry4_S[3]_CO[3])
0.709 61.429 r u_comp/u_pipe/data_r_reg[11]_i_3/CO[3]
net (fo=1, unplaced) 0.000 61.429 u_comp/u_pipe/data_r_reg[11]_i_3_n_0
CARRY4 (Prop_carry4_CI_CO[0])
0.293 61.722 r u_comp/u_pipe/data_r_reg[11]_i_4/CO[0]
net (fo=12, unplaced) 0.359 62.081 u_comp/u_pipe/data_r_reg[11]_i_4_n_3
LUT5 (Prop_lut5_I1_O) 0.367 62.448 r u_comp/u_pipe/data_r[0]_i_1/O
net (fo=1, unplaced) 0.000 62.448 u_comp/u_pipe/mod_result[0]
FDCE r u_comp/u_pipe/data_r_reg[0]/D
------------------------------------------------------------------- -------------------
(clock sysclk rise edge) 20.000 20.000 r
0.000 20.000 r clk (IN)
net (fo=0) 0.000 20.000 clk
IBUF (Prop_ibuf_I_O) 0.817 20.817 r clk_IBUF_inst/O
net (fo=1, unplaced) 0.760 21.576 clk_IBUF
BUFG (Prop_bufg_I_O) 0.091 21.667 r clk_IBUF_BUFG_inst/O
net (fo=23790, unplaced) 0.439 22.106 u_comp/u_pipe/clk_IBUF_BUFG
FDCE r u_comp/u_pipe/data_r_reg[0]/C
clock pessimism 0.178 22.285
clock uncertainty -0.035 22.249
FDCE (Setup_fdce_C_D) 0.029 22.278 u_comp/u_pipe/data_r_reg[0]
-------------------------------------------------------------------
required time 22.278
arrival time -62.448
-------------------------------------------------------------------
slack -40.169
Min Delay Paths
--------------------------------------------------------------------------------------
Slack (MET) : 0.080ns (arrival time - required time)
Source: u_pmul/FSM_onehot_state_reg[3]/C
(rising edge-triggered cell FDCE clocked by sysclk {rise@0.000ns fall@10.000ns period=20.000ns})
Destination: u_pmul/u_bc/valid_sr_reg[4]_srl5_u_pmul_u_bc_valid_sr_reg_c_3/D
(rising edge-triggered cell SRL16E clocked by sysclk {rise@0.000ns fall@10.000ns period=20.000ns})
Path Group: sysclk
Path Type: Hold (Min at Fast Process Corner)
Requirement: 0.000ns (sysclk rise@0.000ns - sysclk rise@0.000ns)
Data Path Delay: 0.289ns (logic 0.141ns (48.726%) route 0.148ns (51.274%))
Logic Levels: 0
Clock Path Skew: 0.145ns (DCD - SCD - CPR)
Destination Clock Delay (DCD): 1.011ns
Source Clock Delay (SCD): 0.656ns
Clock Pessimism Removal (CPR): 0.209ns
Location Delay type Incr(ns) Path(ns) Netlist Resource(s)
------------------------------------------------------------------- -------------------
(clock sysclk rise edge) 0.000 0.000 r
0.000 0.000 r clk (IN)
net (fo=0) 0.000 0.000 clk
IBUF (Prop_ibuf_I_O) 0.179 0.179 r clk_IBUF_inst/O
net (fo=1, unplaced) 0.337 0.516 clk_IBUF
BUFG (Prop_bufg_I_O) 0.026 0.542 r clk_IBUF_BUFG_inst/O
net (fo=23790, unplaced) 0.114 0.656 u_pmul/clk_IBUF_BUFG
FDCE r u_pmul/FSM_onehot_state_reg[3]/C
------------------------------------------------------------------- -------------------
FDCE (Prop_fdce_C_Q) 0.141 0.797 r u_pmul/FSM_onehot_state_reg[3]/Q
net (fo=3, unplaced) 0.148 0.946 u_pmul/u_bc/Q[3]
SRL16E r u_pmul/u_bc/valid_sr_reg[4]_srl5_u_pmul_u_bc_valid_sr_reg_c_3/D
------------------------------------------------------------------- -------------------
(clock sysclk rise edge) 0.000 0.000 r
0.000 0.000 r clk (IN)
net (fo=0) 0.000 0.000 clk
IBUF (Prop_ibuf_I_O) 0.368 0.368 r clk_IBUF_inst/O
net (fo=1, unplaced) 0.355 0.723 clk_IBUF
BUFG (Prop_bufg_I_O) 0.029 0.752 r clk_IBUF_BUFG_inst/O
net (fo=23790, unplaced) 0.259 1.011 u_pmul/u_bc/clk_IBUF_BUFG
SRL16E r u_pmul/u_bc/valid_sr_reg[4]_srl5_u_pmul_u_bc_valid_sr_reg_c_3/CLK
clock pessimism -0.209 0.801
SRL16E (Hold_srl16e_CLK_D)
0.064 0.865 u_pmul/u_bc/valid_sr_reg[4]_srl5_u_pmul_u_bc_valid_sr_reg_c_3
-------------------------------------------------------------------
required time -0.865
arrival time 0.946
-------------------------------------------------------------------
slack 0.080
Pulse Width Checks
--------------------------------------------------------------------------------------
Clock Name: sysclk
Waveform(ns): { 0.000 10.000 }
Period(ns): 20.000
Sources: { clk }
Check Type Corner Lib Pin Reference Pin Required(ns) Actual(ns) Slack(ns) Location Pin
Min Period n/a RAMB18E1/CLKARDCLK n/a 2.944 20.000 17.056 u_ct_bram/mem_reg/CLKARDCLK
Low Pulse Width Slow SRL16E/CLK n/a 0.980 10.000 9.020 u_pmul/u_bc/t1_s4_reg[0]_srl2_u_pmul_u_bc_valid_sr_reg_c_0/CLK
High Pulse Width Fast SRL16E/CLK n/a 0.980 10.000 9.020 u_pmul/u_bc/t1_s4_reg[0]_srl2_u_pmul_u_bc_valid_sr_reg_c_0/CLK

File diff suppressed because it is too large Load Diff

192
reports/util_synth.rpt Normal file
View File

@@ -0,0 +1,192 @@
Copyright 1986-2019 Xilinx, Inc. All Rights Reserved.
--------------------------------------------------------------------------------------------
| Tool Version : Vivado v.2019.2 (lin64) Build 2708876 Wed Nov 6 21:39:14 MST 2019
| Date : Tue Jul 7 15:28:57 2026
| Host : fedora running 64-bit unknown
| Command : report_utilization -file /home/fallensigh/Dev/mlkem/reports/util_synth.rpt
| Design : mlkem_top
| Device : 7a200tfbg676-1
| Design State : Synthesized
--------------------------------------------------------------------------------------------
Utilization Design Information
Table of Contents
-----------------
1. Slice Logic
1.1 Summary of Registers by Type
2. Memory
3. DSP
4. IO and GT Specific
5. Clocking
6. Specific Feature
7. Primitives
8. Black Boxes
9. Instantiated Netlists
1. Slice Logic
--------------
+----------------------------+-------+-------+-----------+-------+
| Site Type | Used | Fixed | Available | Util% |
+----------------------------+-------+-------+-----------+-------+
| Slice LUTs* | 32814 | 0 | 134600 | 24.38 |
| LUT as Logic | 32777 | 0 | 134600 | 24.35 |
| LUT as Memory | 37 | 0 | 46200 | 0.08 |
| LUT as Distributed RAM | 0 | 0 | | |
| LUT as Shift Register | 37 | 0 | | |
| Slice Registers | 23737 | 0 | 269200 | 8.82 |
| Register as Flip Flop | 23737 | 0 | 269200 | 8.82 |
| Register as Latch | 0 | 0 | 269200 | 0.00 |
| F7 Muxes | 3384 | 0 | 67300 | 5.03 |
| F8 Muxes | 1205 | 0 | 33650 | 3.58 |
+----------------------------+-------+-------+-----------+-------+
* Warning! The Final LUT count, after physical optimizations and full implementation, is typically lower. Run opt_design after synthesis, if not already completed, for a more realistic count.
1.1 Summary of Registers by Type
--------------------------------
+-------+--------------+-------------+--------------+
| Total | Clock Enable | Synchronous | Asynchronous |
+-------+--------------+-------------+--------------+
| 0 | _ | - | - |
| 0 | _ | - | Set |
| 0 | _ | - | Reset |
| 0 | _ | Set | - |
| 0 | _ | Reset | - |
| 0 | Yes | - | - |
| 3 | Yes | - | Set |
| 23614 | Yes | - | Reset |
| 0 | Yes | Set | - |
| 120 | Yes | Reset | - |
+-------+--------------+-------------+--------------+
2. Memory
---------
+-------------------+------+-------+-----------+-------+
| Site Type | Used | Fixed | Available | Util% |
+-------------------+------+-------+-----------+-------+
| Block RAM Tile | 5 | 0 | 365 | 1.37 |
| RAMB36/FIFO* | 2 | 0 | 365 | 0.55 |
| RAMB36E1 only | 2 | | | |
| RAMB18 | 6 | 0 | 730 | 0.82 |
| RAMB18E1 only | 6 | | | |
+-------------------+------+-------+-----------+-------+
* Note: Each Block RAM Tile only has one FIFO logic available and therefore can accommodate only one FIFO36E1 or one FIFO18E1. However, if a FIFO18E1 occupies a Block RAM Tile, that tile can still accommodate a RAMB18E1
3. DSP
------
+-----------+------+-------+-----------+-------+
| Site Type | Used | Fixed | Available | Util% |
+-----------+------+-------+-----------+-------+
| DSPs | 0 | 0 | 740 | 0.00 |
+-----------+------+-------+-----------+-------+
4. IO and GT Specific
---------------------
+-----------------------------+------+-------+-----------+--------+
| Site Type | Used | Fixed | Available | Util% |
+-----------------------------+------+-------+-----------+--------+
| Bonded IOB | 3228 | 0 | 400 | 807.00 |
| Bonded IPADs | 0 | 0 | 26 | 0.00 |
| Bonded OPADs | 0 | 0 | 16 | 0.00 |
| PHY_CONTROL | 0 | 0 | 10 | 0.00 |
| PHASER_REF | 0 | 0 | 10 | 0.00 |
| OUT_FIFO | 0 | 0 | 40 | 0.00 |
| IN_FIFO | 0 | 0 | 40 | 0.00 |
| IDELAYCTRL | 0 | 0 | 10 | 0.00 |
| IBUFDS | 0 | 0 | 384 | 0.00 |
| GTPE2_CHANNEL | 0 | 0 | 8 | 0.00 |
| PHASER_OUT/PHASER_OUT_PHY | 0 | 0 | 40 | 0.00 |
| PHASER_IN/PHASER_IN_PHY | 0 | 0 | 40 | 0.00 |
| IDELAYE2/IDELAYE2_FINEDELAY | 0 | 0 | 500 | 0.00 |
| IBUFDS_GTE2 | 0 | 0 | 4 | 0.00 |
| ILOGIC | 0 | 0 | 400 | 0.00 |
| OLOGIC | 0 | 0 | 400 | 0.00 |
+-----------------------------+------+-------+-----------+--------+
5. Clocking
-----------
+------------+------+-------+-----------+-------+
| Site Type | Used | Fixed | Available | Util% |
+------------+------+-------+-----------+-------+
| BUFGCTRL | 1 | 0 | 32 | 3.13 |
| BUFIO | 0 | 0 | 40 | 0.00 |
| MMCME2_ADV | 0 | 0 | 10 | 0.00 |
| PLLE2_ADV | 0 | 0 | 10 | 0.00 |
| BUFMRCE | 0 | 0 | 20 | 0.00 |
| BUFHCE | 0 | 0 | 120 | 0.00 |
| BUFR | 0 | 0 | 40 | 0.00 |
+------------+------+-------+-----------+-------+
6. Specific Feature
-------------------
+-------------+------+-------+-----------+-------+
| Site Type | Used | Fixed | Available | Util% |
+-------------+------+-------+-----------+-------+
| BSCANE2 | 0 | 0 | 4 | 0.00 |
| CAPTUREE2 | 0 | 0 | 1 | 0.00 |
| DNA_PORT | 0 | 0 | 1 | 0.00 |
| EFUSE_USR | 0 | 0 | 1 | 0.00 |
| FRAME_ECCE2 | 0 | 0 | 1 | 0.00 |
| ICAPE2 | 0 | 0 | 2 | 0.00 |
| PCIE_2_1 | 0 | 0 | 1 | 0.00 |
| STARTUPE2 | 0 | 0 | 1 | 0.00 |
| XADC | 0 | 0 | 1 | 0.00 |
+-------------+------+-------+-----------+-------+
7. Primitives
-------------
+----------+-------+---------------------+
| Ref Name | Used | Functional Category |
+----------+-------+---------------------+
| FDCE | 23614 | Flop & Latch |
| LUT6 | 17906 | LUT |
| LUT3 | 8519 | LUT |
| LUT5 | 5910 | LUT |
| MUXF7 | 3384 | MuxFx |
| LUT4 | 3044 | LUT |
| LUT2 | 2820 | LUT |
| OBUF | 2342 | IO |
| MUXF8 | 1205 | MuxFx |
| IBUF | 886 | IO |
| CARRY4 | 879 | CarryLogic |
| LUT1 | 208 | LUT |
| FDRE | 120 | Flop & Latch |
| SRL16E | 37 | Distributed Memory |
| RAMB18E1 | 6 | Block Memory |
| FDPE | 3 | Flop & Latch |
| RAMB36E1 | 2 | Block Memory |
| BUFG | 1 | Clock |
+----------+-------+---------------------+
8. Black Boxes
--------------
+----------+------+
| Ref Name | Used |
+----------+------+
9. Instantiated Netlists
------------------------
+----------+------+
| Ref Name | Used |
+----------+------+

114
run_tb.sh
View File

@@ -12,8 +12,8 @@
# ./run_tb.sh enc 4 1 # Encaps K=4, only CASE=1 # ./run_tb.sh enc 4 1 # Encaps K=4, only CASE=1
# ./run_tb.sh dec # ML-KEM Decaps: all K, all cases (0..2) # ./run_tb.sh dec # ML-KEM Decaps: all K, all cases (0..2)
# ./run_tb.sh dec 2 0 # Decaps K=2, only CASE=0 # ./run_tb.sh dec 2 0 # Decaps K=2, only CASE=0
# ./run_tb.sh hello # hello_world end-to-end (single instance) # ./run_tb.sh hello [K] # hello_world end-to-end (single instance; K=2/3/4)
# ./run_tb.sh hello two # hello_world end-to-end (two instances) # ./run_tb.sh hello two [K] # hello_world end-to-end (two instances; K=2/3/4)
# ./run_tb.sh --list # ./run_tb.sh --list
# #
# 'top' (KeyGen), 'enc' (Encaps) and 'dec' (Decaps) share the same RTL datapath; # 'top' (KeyGen), 'enc' (Encaps) and 'dec' (Decaps) share the same RTL datapath;
@@ -41,7 +41,7 @@ if [ "$1" = "--list" ]; then
done done
echo " enc (ML-KEM Encaps; shares the 'top' RTL/tcl, enc testbench)" echo " enc (ML-KEM Encaps; shares the 'top' RTL/tcl, enc testbench)"
echo " dec (ML-KEM Decaps; shares the 'top' RTL/tcl, dec testbench)" echo " dec (ML-KEM Decaps; shares the 'top' RTL/tcl, dec testbench)"
echo " hello (ML-KEM hello_world end-to-end; 'hello two' for two instances)" echo " hello (ML-KEM hello_world end-to-end; optional K=2/3/4, 'hello two [K]' for two instances)"
exit 0 exit 0
fi fi
@@ -120,6 +120,84 @@ execute_tcl() {
# each <spec> = "snapshot:K:CASE". Launches all specs in parallel, waits, # each <spec> = "snapshot:K:CASE". Launches all specs in parallel, waits,
# then prints an ordered summary and returns nonzero if any job failed. # then prints an ordered summary and returns nonzero if any job failed.
PAR_BASE="${TMPDIR:-/tmp}/run_tb_par_$$" PAR_BASE="${TMPDIR:-/tmp}/run_tb_par_$$"
print_timing_summary() {
local logs=("$@")
local found=0 log
for log in "${logs[@]}"; do
if [ -f "$log" ] && grep -q '^TIME ' "$log"; then
found=1
fi
done
[ "$found" -eq 0 ] && return 0
echo ""
echo "Timing summary (clk=20MHz, period=50ns)"
printf " %-7s %-3s %-5s %10s %12s %12s %s\n" "OP" "K" "CASE" "cycles" "time_us" "time_ms" "log"
for log in "${logs[@]}"; do
[ -f "$log" ] || continue
awk -v logfile="$log" '
/^TIME / {
k = ""; c = ""; op = ""; cycles = ""; runtime = "";
for (i = 1; i <= NF; i++) {
split($i, a, "=");
if (a[1] == "K") k = a[2];
else if (a[1] == "CASE") c = a[2];
else if (a[1] == "OP") op = a[2];
else if (a[1] == "cycles") cycles = a[2];
else if (a[1] == "runtime") runtime = a[2];
}
if (runtime != "") {
printf " %-7s %-3s %-5s %10d %12.3f %12.3f %s\n",
op, k, c, cycles, runtime / 1000.0, runtime / 1000000.0, logfile;
}
}
' "$log"
done
local found_state=0
for log in "${logs[@]}"; do
if [ -f "$log" ] && grep -q '^ STATE ' "$log"; then
found_state=1
fi
done
[ "$found_state" -eq 0 ] && return 0
echo ""
echo "State timing breakdown (clk=20MHz, period=50ns)"
printf " %-7s %-3s %-5s %-12s %10s %12s\n" "OP" "K" "CASE" "STATE" "cycles" "time_us"
for log in "${logs[@]}"; do
[ -f "$log" ] || continue
awk '
/^TIME_BREAKDOWN / {
k = ""; c = ""; op = "";
for (i = 1; i <= NF; i++) {
split($i, a, "=");
if (a[1] == "K") k = a[2];
else if (a[1] == "CASE") c = a[2];
else if (a[1] == "OP") op = a[2];
}
next;
}
/^ STATE / {
state = $2;
cycles = "";
time_ns = "";
for (i = 1; i <= NF; i++) {
split($i, a, "=");
if (a[1] == "cycles") cycles = a[2];
else if (a[1] == "time_ns") time_ns = a[2];
}
if (time_ns != "") {
printf " %-7s %-3s %-5s %-12s %10d %12.3f\n",
op, k, c, state, cycles, time_ns / 1000.0;
}
}
' "$log"
done
}
run_xsim_jobs() { run_xsim_jobs() {
local mtag="$1" pgrep="$2" pmatch="$3"; shift 3 local mtag="$1" pgrep="$2" pmatch="$3"; shift 3
local specs=("$@") local specs=("$@")
@@ -146,12 +224,13 @@ run_xsim_jobs() {
wait wait
# ordered summary + per-job log copy to /tmp/run_tb_<mtag>_k*_c*.log # ordered summary + per-job log copy to /tmp/run_tb_<mtag>_k*_c*.log
local fail=0 pf nf log local fail=0 pf nf log timing_logs=()
for s in "${specs[@]}"; do for s in "${specs[@]}"; do
IFS=: read -r snap k c <<< "$s" IFS=: read -r snap k c <<< "$s"
wd="$PAR_BASE/${mtag}_k${k}_c${c}" wd="$PAR_BASE/${mtag}_k${k}_c${c}"
log="/tmp/run_tb_${mtag}_k${k}_c${c}.log" log="/tmp/run_tb_${mtag}_k${k}_c${c}.log"
cp -f "$wd/run.out" "$log" 2>/dev/null cp -f "$wd/run.out" "$log" 2>/dev/null
timing_logs+=("$log")
pf=$(grep -oE "$pgrep" "$log" | tail -1) pf=$(grep -oE "$pgrep" "$log" | tail -1)
nf=$(grep -c 'cannot be opened' "$log") nf=$(grep -c 'cannot be opened' "$log")
echo " K=$k CASE=$c -> ${pf:-NORESULT} (file-not-found=$nf, log: $log)" echo " K=$k CASE=$c -> ${pf:-NORESULT} (file-not-found=$nf, log: $log)"
@@ -162,6 +241,7 @@ run_xsim_jobs() {
{ [ "$pf" = "$pmatch" ] && [ "$nf" -eq 0 ]; } || fail=1 { [ "$pf" = "$pmatch" ] && [ "$nf" -eq 0 ]; } || fail=1
fi fi
done done
print_timing_summary "${timing_logs[@]}"
rm -rf "$PAR_BASE" rm -rf "$PAR_BASE"
return $fail return $fail
} }
@@ -286,12 +366,24 @@ fi
# ML-KEM hello_world end-to-end. Reuses the 'top' RTL compile list, then runs a # ML-KEM hello_world end-to-end. Reuses the 'top' RTL compile list, then runs a
# self-checking protocol TB (no KAT vectors): single instance by default, or two # self-checking protocol TB (no KAT vectors): single instance by default, or two
# instances (genenc + dec) with 'hello two'. Prints each step's inputs/outputs. # instances (genenc + dec) with 'hello two'. Optional K selects ML-KEM-512/768/1024.
run_hello_selected() { run_hello_selected() {
local tcl_file="$1" variant="$2" local tcl_file="$1" arg1="$2" arg2="$3"
set +e set +e
rm -rf xsim.dir .Xil rm -rf xsim.dir .Xil
local variant="" ksel="2"
if [ "$arg1" = "two" ]; then
variant="two"
[ -n "$arg2" ] && ksel="$arg2"
elif [ -n "$arg1" ]; then
ksel="$arg1"
fi
case "$ksel" in
2|3|4) ;;
*) echo "ERROR: hello K must be 2, 3, or 4 (got '$ksel')"; return 1 ;;
esac
local tb snap local tb snap
if [ "$variant" = "two" ]; then if [ "$variant" = "two" ]; then
tb=tb_mlkem_two_inst_xsim; snap=mlkem_two tb=tb_mlkem_two_inst_xsim; snap=mlkem_two
@@ -308,15 +400,15 @@ run_hello_selected() {
echo " xvlog -sv --relax sync_rtl/top/TB/$tb.v" echo " xvlog -sv --relax sync_rtl/top/TB/$tb.v"
xvlog -sv --relax "sync_rtl/top/TB/$tb.v" || { echo "HELLO TB COMPILE FAILED"; return 1; } xvlog -sv --relax "sync_rtl/top/TB/$tb.v" || { echo "HELLO TB COMPILE FAILED"; return 1; }
echo " xelab $tb -s $snap --timescale 1ns/1ps" echo " xelab $tb -generic_top KP=$ksel -s ${snap}_k${ksel} --timescale 1ns/1ps"
xelab "$tb" -s "$snap" --timescale 1ns/1ps || { echo "ELAB FAILED"; return 1; } xelab "$tb" -generic_top "KP=$ksel" -s "${snap}_k${ksel}" --timescale 1ns/1ps || { echo "ELAB FAILED"; return 1; }
xsim "$snap" -R xsim "${snap}_k${ksel}" -R
} }
if [ "$MODULE" = "hello" ]; then if [ "$MODULE" = "hello" ]; then
run_hello_selected "$TCL_FILE" "$SEL_K" # SEL_K position carries the variant ('two') run_hello_selected "$TCL_FILE" "$SEL_K" "$SEL_CASE" # args are [K] or [two] [K]
exit $? exit $?
fi fi
execute_tcl "$TCL_FILE" execute_tcl "$TCL_FILE"

View File

@@ -1,22 +1,17 @@
// comp_decomp_sync.v - ML-KEM coefficient compression/decompression // comp_decomp_sync.v - ML-KEM coefficient compression/decompression
// //
// Streaming: one coefficient per cycle through pipeline_reg. // Streaming valid/ready interface. The arithmetic is fixed-latency pipelined
// mode=0: compress - round((2^d * x) / Q) mod 2^d // so compression does not infer a long combinational divider/reducer.
// mode=1: decompress - round((Q * x) / 2^d) mod Q // mode=0: compress round((2^d * x) / Q) mod 2^d
// mode=1: decompress round((Q * x) / 2^d)
// Uses round-half-up (round(2.5)=3, round(3.5)=4). // Uses round-half-up (round(2.5)=3, round(3.5)=4).
// Integer arithmetic: // Integer arithmetic:
// compress: (x * 2^d + Q/2) / Q, lower d bits as result // compress: (x * 2^d + Q/2) / Q, lower d bits as result
// decompress: (x * Q + 2^(d-1)) / 2^d, result mod Q // decompress: (x * Q + 2^(d-1)) / 2^d, result (always < Q)
//
// Division by Q=3329 replaced with Barrett multiplication + correction:
// floor(x / Q) (x * 5039) >> 24 (5039 = floor(2^24 / 3329))
// Correction: if x - q_approx * Q >= Q, add 1 to quotient.
// Q*5039 = 16,774,831 < 2^24, so Barrett may under-estimate by 1.
// Q = 3329 = 2048+512+256+1, so q*Q uses 3 shifts + add (no 2nd multiplier).
`include "sync_rtl/common/defines.vh" `include "sync_rtl/common/defines.vh"
(* use_dsp = "no" *) (* use_dsp = "yes" *)
module comp_decomp_sync ( module comp_decomp_sync (
input clk, input clk,
input rst_n, input rst_n,
@@ -30,67 +25,171 @@ module comp_decomp_sync (
input ready_i input ready_i
); );
wire [11:0] two_pow_d = 12'd1 << d; localparam [24:0] Q25 = 25'd`Q;
localparam [12:0] K13 = 13'd5039; // floor(2^24 / 3329)
// product: 12b*12b = max 2048*3328 = 6,815,744 23b, pad to 24b reg [11:0] data_r;
wire [23:0] product = mode ? {12'b0, coeff_in} * {12'b0, 12'(`Q)} reg valid_r;
: {12'b0, coeff_in} * {12'b0, two_pow_d};
wire [11:0] round_off = mode ? (two_pow_d >> 1) : 12'd1664; reg mode_s0;
reg [4:0] d_s0;
reg [11:0] coeff_s0;
reg valid_s0;
wire [23:0] dividend = product + {12'b0, round_off}; reg mode_s1;
reg [4:0] d_s1;
reg [23:0] comp_dividend_s1;
reg [23:0] decomp_rounded_s1;
reg valid_s1;
// =========================================================== reg mode_s2;
// Barrett: floor(x / Q) (x * 5039) >> 24 reg [4:0] d_s2;
// M = floor(2^24 / Q) = 5039 reg [23:0] comp_dividend_s2;
// M*Q = 16,774,831 < 2^24 floor(x/Q) may be 1 too low reg [36:0] comp_qprod_est_s2;
// Correction: remainder = x - q_approx*Q; if rem >= Q, qq+1 reg [23:0] decomp_rounded_s2;
// Q*5039 fails at: k*2385 >= 2^24 k >= 7033; max k 2047 safe reg valid_s2;
// ===========================================================
wire [36:0] barrett_prod = dividend * 13'd5039; // 24×13=37b
wire [11:0] q_approx = barrett_prod[35:24]; // quotient estimate
// q*Q = q*2048 + q*512 + q*256 + q (3329 = 2048+512+256+1)
wire [23:0] q_times_Q = ({q_approx, 11'd0})
+ ({q_approx, 9'd0})
+ ({q_approx, 8'd0})
+ q_approx;
wire [24:0] delta = {1'b0, dividend} - {1'b0, q_times_Q}; // 25b
wire [11:0] comp_q = q_approx + (delta >= {13'b0, 12'(`Q)});
// lower d bits = compressed result
wire [11:0] comp_res = comp_q & ({12'b0, two_pow_d} - 1'b1);
// =========================================================== reg mode_s3;
// Decompress: dividend >> d (free), then % Q via Barrett reg [4:0] d_s3;
// =========================================================== reg [23:0] comp_dividend_s3;
wire [23:0] dec_sh = dividend >> d; reg [12:0] comp_q_est_s3;
wire [36:0] dm_prod = dec_sh * 13'd5039; reg [11:0] decomp_result_s3;
wire [11:0] dm_qe = dm_prod[35:24]; reg valid_s3;
wire [23:0] dm_qQ = ({dm_qe, 11'd0})
+ ({dm_qe, 9'd0})
+ ({dm_qe, 8'd0})
+ dm_qe;
wire [24:0] dm_del = {1'b0, dec_sh} - {1'b0, dm_qQ};
// divide by Q, take remainder: r = x - q*Q
wire [12:0] dm_q = dm_qe + (dm_del >= {13'b0, 12'(`Q)}); // corr quotient
// actual remainder = dec_sh - dm_q*Q
wire [23:0] dm_corrQ = ({dm_q, 11'd0}) + ({dm_q, 9'd0}) + ({dm_q, 8'd0}) + dm_q;
wire [24:0] dm_rem = {1'b0, dec_sh} - {1'b0, dm_corrQ};
// dm_rem < Q*2 (Barrett gives exact quotient after correction), one cond-sub
wire [11:0] dec_mod = (dm_rem >= {13'b0, 12'(`Q)})
? (dm_rem[11:0] - 12'(`Q))
: dm_rem[11:0];
wire [11:0] mod_result = mode ? dec_mod : comp_res; reg mode_s4;
reg [4:0] d_s4;
reg [23:0] comp_dividend_s4;
reg [12:0] comp_q_est_s4;
reg [24:0] comp_q_mul_q_s4;
reg [11:0] decomp_result_s4;
reg valid_s4;
pipeline_reg #(.DW(12)) u_pipe ( reg mode_s5;
.clk (clk), reg [4:0] d_s5;
.rst_n (rst_n), reg [12:0] comp_q_est_s5;
.data_i (mod_result), reg [24:0] comp_rem_s5;
.valid_i(valid_i), reg [11:0] decomp_result_s5;
.ready_o(ready_o), reg valid_s5;
.data_o (coeff_out),
.valid_o(valid_o), wire fire_i = valid_i && ready_o;
.ready_i(ready_i)
); wire pipe_busy = valid_s0 | valid_s1 | valid_s2 | valid_s3 | valid_s4 | valid_s5;
// Accept a new command only when the pipeline and output slot are both free.
// Current users issue one coefficient at a time and wait for valid_o.
assign ready_o = !pipe_busy && (!valid_r || ready_i);
assign coeff_out = data_r;
assign valid_o = valid_r;
wire [23:0] coeff_ext_s0 = {12'd0, coeff_s0};
wire [23:0] comp_dividend_next = (coeff_ext_s0 << d_s0) + 24'd1664;
wire [23:0] decomp_product_next = coeff_s0 * 12'd`Q;
wire [23:0] decomp_round_next =
decomp_product_next + ((d_s0 == 5'd0) ? 24'd0 : (24'd1 << (d_s0 - 5'd1)));
wire [12:0] comp_q_corr1_s5 =
comp_q_est_s5 + ((comp_rem_s5 >= Q25) ? 13'd1 : 13'd0);
wire [24:0] comp_rem_corr1_s5 =
(comp_rem_s5 >= Q25) ? (comp_rem_s5 - Q25) : comp_rem_s5;
wire [12:0] comp_q_corr2_s5 =
comp_q_corr1_s5 + ((comp_rem_corr1_s5 >= Q25) ? 13'd1 : 13'd0);
wire [11:0] comp_mask_s5 =
(d_s5 == 5'd0) ? 12'd0 : (12'd1 << d_s5) - 12'd1;
wire [11:0] comp_result_s5 = comp_q_corr2_s5[11:0] & comp_mask_s5;
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
data_r <= 12'd0;
valid_r <= 1'b0;
mode_s0 <= 1'b0;
d_s0 <= 5'd0;
coeff_s0 <= 12'd0;
valid_s0 <= 1'b0;
mode_s1 <= 1'b0;
d_s1 <= 5'd0;
comp_dividend_s1 <= 24'd0;
decomp_rounded_s1 <= 24'd0;
valid_s1 <= 1'b0;
mode_s2 <= 1'b0;
d_s2 <= 5'd0;
comp_dividend_s2 <= 24'd0;
comp_qprod_est_s2 <= 37'd0;
decomp_rounded_s2 <= 24'd0;
valid_s2 <= 1'b0;
mode_s3 <= 1'b0;
d_s3 <= 5'd0;
comp_dividend_s3 <= 24'd0;
comp_q_est_s3 <= 13'd0;
decomp_result_s3 <= 12'd0;
valid_s3 <= 1'b0;
mode_s4 <= 1'b0;
d_s4 <= 5'd0;
comp_dividend_s4 <= 24'd0;
comp_q_est_s4 <= 13'd0;
comp_q_mul_q_s4 <= 25'd0;
decomp_result_s4 <= 12'd0;
valid_s4 <= 1'b0;
mode_s5 <= 1'b0;
d_s5 <= 5'd0;
comp_q_est_s5 <= 13'd0;
comp_rem_s5 <= 25'd0;
decomp_result_s5 <= 12'd0;
valid_s5 <= 1'b0;
end else begin
if (valid_r && ready_i)
valid_r <= 1'b0;
mode_s0 <= mode;
d_s0 <= d;
coeff_s0 <= coeff_in;
valid_s0 <= fire_i;
mode_s1 <= mode_s0;
d_s1 <= d_s0;
comp_dividend_s1 <= comp_dividend_next;
decomp_rounded_s1 <= decomp_round_next;
valid_s1 <= valid_s0;
mode_s2 <= mode_s1;
d_s2 <= d_s1;
comp_dividend_s2 <= comp_dividend_s1;
comp_qprod_est_s2 <= {13'd0, comp_dividend_s1} * K13;
decomp_rounded_s2 <= decomp_rounded_s1;
valid_s2 <= valid_s1;
mode_s3 <= mode_s2;
d_s3 <= d_s2;
comp_dividend_s3 <= comp_dividend_s2;
comp_q_est_s3 <= comp_qprod_est_s2[36:24];
decomp_result_s3 <= decomp_rounded_s2 >> d_s2;
valid_s3 <= valid_s2;
mode_s4 <= mode_s3;
d_s4 <= d_s3;
comp_dividend_s4 <= comp_dividend_s3;
comp_q_est_s4 <= comp_q_est_s3;
comp_q_mul_q_s4 <= comp_q_est_s3 * Q25;
decomp_result_s4 <= decomp_result_s3;
valid_s4 <= valid_s3;
mode_s5 <= mode_s4;
d_s5 <= d_s4;
comp_q_est_s5 <= comp_q_est_s4;
comp_rem_s5 <= {1'b0, comp_dividend_s4} - comp_q_mul_q_s4;
decomp_result_s5 <= decomp_result_s4;
valid_s5 <= valid_s4;
if (valid_s5) begin
data_r <= mode_s5 ? decomp_result_s5 : comp_result_s5;
valid_r <= 1'b1;
end
end
end
endmodule endmodule

View File

@@ -172,7 +172,7 @@ module tb_ntt_core_xsim;
integer got_done; integer got_done;
// Extract mode and coefficients from vector memory // Extract mode and coefficients from vector memory
vec_mode = vector_mem[idx][3075]; vec_mode = |vector_mem[idx][3075:3072];
// Extract 256 input coefficients // Extract 256 input coefficients
// coeff[0] at bits [3074:3063], coeff[255] at bits [11:0] // coeff[0] at bits [3074:3063], coeff[255] at bits [11:0]

View File

@@ -30,12 +30,14 @@ puts "=== Compiling RTL sources ==="
# Barrett modular multiplier (combinational) # Barrett modular multiplier (combinational)
xvlog -sv ${SRC_DIR}/barrett_mul.v xvlog -sv ${SRC_DIR}/barrett_mul.v
xvlog -sv ${SRC_DIR}/barrett_mul_pipe.v
# Zeta ROM (combinational) # Zeta ROM (combinational)
xvlog -sv ${SRC_DIR}/zeta_rom.v xvlog -sv ${SRC_DIR}/zeta_rom.v
# Butterfly unit (combinational, instantiates barrett_mul) # Butterfly unit (combinational, instantiates barrett_mul)
xvlog -sv ${SRC_DIR}/butterfly_unit.v xvlog -sv ${SRC_DIR}/butterfly_unit.v
xvlog -sv ${SRC_DIR}/butterfly_unit_pipe.v
# NTT core (FSM-based, instantiates butterfly_unit + zeta_rom + barrett_mul) # NTT core (FSM-based, instantiates butterfly_unit + zeta_rom + barrett_mul)
xvlog -sv ${SRC_DIR}/ntt_core.v xvlog -sv ${SRC_DIR}/ntt_core.v

View File

@@ -0,0 +1,72 @@
// barrett_mul_pipe.v - DSP-friendly pipelined Barrett modular multiply
//
// Computes product = a * b mod Q with fixed latency. This module is used on
// timing-critical NTT paths; the legacy barrett_mul remains combinational for
// older users.
module barrett_mul_pipe (
input clk,
input rst_n,
input valid_i,
input [11:0] a,
input [11:0] b,
output [11:0] product,
output valid_o
);
localparam [24:0] Q25 = 25'd3329;
localparam [12:0] K13 = 13'd5039;
reg [23:0] p_s1;
reg [23:0] p_s2;
reg [36:0] tk_s2;
reg [23:0] p_s3;
reg [12:0] q_s3;
reg [24:0] p_s4;
reg [24:0] qprod_s4;
reg [24:0] r0_s5;
reg [24:0] r1_s6;
reg [11:0] product_r;
reg [6:0] valid_sr;
assign product = product_r;
assign valid_o = valid_sr[6];
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
p_s1 <= 24'd0;
p_s2 <= 24'd0; tk_s2 <= 37'd0;
p_s3 <= 24'd0; q_s3 <= 13'd0;
p_s4 <= 25'd0; qprod_s4 <= 25'd0;
r0_s5 <= 25'd0; r1_s6 <= 25'd0;
product_r <= 12'd0;
valid_sr <= 7'd0;
end else begin
valid_sr <= {valid_sr[5:0], valid_i};
// S1: full product.
p_s1 <= {12'd0, a} * b;
// S2: quotient-estimate product.
p_s2 <= p_s1;
tk_s2 <= {13'd0, p_s1} * K13;
// S3: quotient estimate.
p_s3 <= p_s2;
q_s3 <= tk_s2[36:24];
// S4: q * Q.
p_s4 <= {1'b0, p_s3};
qprod_s4 <= q_s3 * Q25;
// S5/S6/S7: subtract q*Q and conditionally subtract Q twice.
r0_s5 <= p_s4 - qprod_s4;
r1_s6 <= (r0_s5 >= Q25) ? (r0_s5 - Q25) : r0_s5;
product_r <= (r1_s6 >= Q25) ? (r1_s6 - Q25) : r1_s6[11:0];
end
end
endmodule

View File

@@ -0,0 +1,74 @@
// butterfly_unit_pipe.v - pipelined Cooley-Tukey / Gentleman-Sande butterfly
//
// Fixed-latency wrapper around barrett_mul_pipe. All arithmetic that used to
// sit in one NTT cycle is now split by registers.
module butterfly_unit_pipe (
input clk,
input rst_n,
input valid_i,
input [11:0] a,
input [11:0] b,
input [11:0] zeta,
input mode, // 0 = forward NTT, 1 = inverse NTT
output [11:0] a_out,
output [11:0] b_out,
output valid_o
);
localparam [11:0] Q12 = 12'd3329;
localparam [12:0] Q13 = 13'd3329;
wire [11:0] diff = (b >= a) ? (b - a) : (b - a + Q12);
wire [11:0] mul_data = mode ? diff : b;
wire [11:0] mul_result;
wire mul_valid;
barrett_mul_pipe u_barrett (
.clk(clk),
.rst_n(rst_n),
.valid_i(valid_i),
.a(zeta),
.b(mul_data),
.product(mul_result),
.valid_o(mul_valid)
);
reg [11:0] a_d0, a_d1, a_d2, a_d3, a_d4, a_d5, a_d6;
reg [11:0] b_d0, b_d1, b_d2, b_d3, b_d4, b_d5, b_d6;
reg mode_d0, mode_d1, mode_d2, mode_d3, mode_d4, mode_d5, mode_d6;
reg [11:0] a_out_r, b_out_r;
reg valid_r;
wire [12:0] a_sum = {1'b0, a_d6} + {1'b0, (mode_d6 ? b_d6 : mul_result)};
wire [11:0] a_mod = (a_sum >= Q13) ? (a_sum[11:0] - Q12) : a_sum[11:0];
wire [11:0] b_sub = (a_d6 >= mul_result) ? (a_d6 - mul_result) :
(a_d6 - mul_result + Q12);
assign a_out = a_out_r;
assign b_out = b_out_r;
assign valid_o = valid_r;
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
a_d0 <= 12'd0; a_d1 <= 12'd0; a_d2 <= 12'd0; a_d3 <= 12'd0; a_d4 <= 12'd0; a_d5 <= 12'd0; a_d6 <= 12'd0;
b_d0 <= 12'd0; b_d1 <= 12'd0; b_d2 <= 12'd0; b_d3 <= 12'd0; b_d4 <= 12'd0; b_d5 <= 12'd0; b_d6 <= 12'd0;
mode_d0 <= 1'b0; mode_d1 <= 1'b0; mode_d2 <= 1'b0; mode_d3 <= 1'b0; mode_d4 <= 1'b0; mode_d5 <= 1'b0; mode_d6 <= 1'b0;
a_out_r <= 12'd0;
b_out_r <= 12'd0;
valid_r <= 1'b0;
end else begin
a_d0 <= a; a_d1 <= a_d0; a_d2 <= a_d1; a_d3 <= a_d2; a_d4 <= a_d3; a_d5 <= a_d4; a_d6 <= a_d5;
b_d0 <= b; b_d1 <= b_d0; b_d2 <= b_d1; b_d3 <= b_d2; b_d4 <= b_d3; b_d5 <= b_d4; b_d6 <= b_d5;
mode_d0 <= mode; mode_d1 <= mode_d0; mode_d2 <= mode_d1;
mode_d3 <= mode_d2; mode_d4 <= mode_d3; mode_d5 <= mode_d4; mode_d6 <= mode_d5;
valid_r <= mul_valid;
if (mul_valid) begin
a_out_r <= a_mod;
b_out_r <= mode_d6 ? mul_result : b_sub;
end
end
end
endmodule

View File

@@ -1,8 +1,12 @@
// ntt_core.v - NTT core with individual coefficient registers // ntt_core.v - NTT core with individual coefficient registers
// //
// Uses 256 individual 12-bit registers and generate-based muxing // Uses 256 individual 12-bit registers and a deeply pipelined butterfly path.
// to avoid any part-select simulation issues. // The arithmetic hot path is split into:
// 3-cycle butterfly: SetAddr -> Read -> Compute+Write // address/operand/zeta register -> pipelined Barrett butterfly -> writeback
// Each NTT layer is issued continuously into the butterfly pipeline, then the
// core drains pending writebacks before starting the next dependent layer.
// In inverse mode, final x3303 output scaling also uses a pipelined Barrett
// multiplier so the output path does not reintroduce a combinational reducer.
module ntt_core ( module ntt_core (
input clk, rst_n, input clk, rst_n,
@@ -17,72 +21,170 @@ module ntt_core (
); );
localparam N = 256, LAYERS = 7, DW = 12; localparam N = 256, LAYERS = 7, DW = 12;
// Individual coefficient registers
reg [DW-1:0] cr [0:N-1]; reg [DW-1:0] cr [0:N-1];
integer ci; integer ci;
// State machine localparam S_IDLE = 4'd0;
localparam S_IDLE=3'd0, S_LOAD=3'd1, S_CMP_A=3'd2, S_CMP_B=3'd3, localparam S_LOAD = 4'd1;
S_CMP_C=3'd4, S_OUTPUT=3'd5, S_DONE=3'd6; localparam S_CMP_RUN = 4'd2;
reg [2:0] state, next_state; localparam S_CMP_DRAIN = 4'd3;
localparam S_OUT_PREP = 4'd7;
localparam S_OUTPUT = 4'd8;
localparam S_OUT_SCALE = 4'd9;
localparam S_DONE = 4'd10;
reg [7:0] load_cnt, out_cnt; reg [3:0] state, next_state;
reg [7:0] load_cnt;
reg [7:0] out_cnt;
reg [8:0] scale_issue_cnt;
reg [8:0] scale_emit_cnt;
reg [7:0] j, start, layer_len; reg [7:0] j, start, layer_len;
reg [6:0] zeta_idx; reg [6:0] zeta_idx;
reg [2:0] layer; reg [2:0] layer;
reg bf_done; reg bf_done;
reg mode_r;
reg layer_issue_done;
reg [4:0] bf_inflight;
// Pipeline registers reg [7:0] wa_d0, wa_d1, wa_d2, wa_d3, wa_d4, wa_d5, wa_d6, wa_d7;
reg [DW-1:0] r_a, r_b; reg [7:0] wb_d0, wb_d1, wb_d2, wb_d3, wb_d4, wb_d5, wb_d6, wb_d7;
reg [7:0] r_wa, r_wb; reg wb_valid_r;
reg [DW-1:0] wb_a_data_r, wb_b_data_r;
reg [7:0] wb_wa_r, wb_wb_r;
reg [DW-1:0] coeff_out_r;
reg valid_o_r;
reg scale_valid_i;
reg [DW-1:0] scale_a_i;
// Zeta
wire [DW-1:0] zeta; wire [DW-1:0] zeta;
zeta_rom u_z (.addr(zeta_idx), .zeta(zeta)); zeta_rom u_z (.addr(zeta_idx), .zeta(zeta));
// Butterfly wire [8:0] group_end = {1'b0, start} + {1'b0, layer_len};
wire [8:0] next_group_start = {1'b0, start} + {1'b0, layer_len} + {1'b0, layer_len};
wire issue_group_last = ({1'b0, j} + 9'd1 >= group_end);
wire issue_layer_last = issue_group_last && (next_group_start >= 9'd256);
wire [7:0] next_layer_len = mode_r ? (layer_len << 1) : (layer_len >> 1);
wire issue_fire = (state == S_CMP_RUN) && !layer_issue_done;
wire [DW-1:0] issue_a = cr[j];
wire [DW-1:0] issue_b = cr[j + layer_len];
wire [DW-1:0] bf_a_out, bf_b_out; wire [DW-1:0] bf_a_out, bf_b_out;
butterfly_unit u_bf ( wire bf_valid;
.a(r_a), .b(r_b), .zeta(zeta), .mode(mode), butterfly_unit_pipe u_bf (
.a_out(bf_a_out), .b_out(bf_b_out)); .clk(clk),
.rst_n(rst_n),
.valid_i(issue_fire),
.a(issue_a),
.b(issue_b),
.zeta(zeta),
.mode(mode_r),
.a_out(bf_a_out),
.b_out(bf_b_out),
.valid_o(bf_valid)
);
// Output scaling wire [DW-1:0] scale_product;
wire [DW-1:0] coeff_scaled; wire scale_valid_o;
barrett_mul u_scl (.a(cr[out_cnt]), .b(12'd3303), .product(coeff_scaled)); barrett_mul_pipe u_scl (
assign coeff_out = mode ? coeff_scaled : cr[out_cnt]; .clk(clk),
.rst_n(rst_n),
.valid_i(scale_valid_i),
.a(scale_a_i),
.b(12'd3303),
.product(scale_product),
.valid_o(scale_valid_o)
);
assign ready_o = (state == S_IDLE) || (state == S_LOAD);
assign ready_o = (state == S_IDLE) || (state == S_LOAD); assign coeff_out = coeff_out_r;
assign valid_o = (state == S_OUTPUT); assign valid_o = valid_o_r;
assign done_o = (state == S_DONE); assign done_o = (state == S_DONE);
always @* begin always @* begin
next_state = state; next_state = state;
case (state) case (state)
S_IDLE: if (valid_i) next_state = S_LOAD; S_IDLE: if (valid_i) next_state = S_LOAD;
S_LOAD: if (load_cnt >= 255 && valid_i) next_state = S_CMP_A; S_LOAD: if (load_cnt >= 8'd255 && valid_i) next_state = S_CMP_RUN;
S_CMP_A: if (bf_done) next_state = S_OUTPUT; else next_state = S_CMP_B; S_CMP_RUN: if (layer_issue_done) next_state = S_CMP_DRAIN;
S_CMP_B: if (bf_done) next_state = S_OUTPUT; else next_state = S_CMP_C; S_CMP_DRAIN: if (bf_inflight == 5'd0)
S_CMP_C: if (bf_done) next_state = S_OUTPUT; else next_state = S_CMP_A; next_state = (layer + 3'd1 >= LAYERS) ? S_OUT_PREP : S_CMP_RUN;
S_OUTPUT:if (out_cnt >= 255 && ready_i) next_state = S_DONE; S_OUT_PREP: next_state = mode_r ? S_OUT_SCALE : S_OUTPUT;
S_DONE: next_state = S_IDLE; S_OUTPUT: if (valid_o_r && ready_i && out_cnt >= 8'd255) next_state = S_DONE;
default: next_state = S_IDLE; S_OUT_SCALE: if (scale_emit_cnt >= 9'd256) next_state = S_DONE;
S_DONE: next_state = S_IDLE;
default: next_state = S_IDLE;
endcase endcase
end end
always @(posedge clk or negedge rst_n) begin always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin if (!rst_n) begin
state<=S_IDLE; load_cnt<=0; out_cnt<=0; j<=0; start<=0; layer_len<=0; state <= S_IDLE;
zeta_idx<=0; layer<=0; bf_done<=0; r_a<=0; r_b<=0; r_wa<=0; r_wb<=0; load_cnt <= 8'd0;
for (ci=0; ci<N; ci=ci+1) cr[ci] <= 0; out_cnt <= 8'd0;
scale_issue_cnt <= 9'd0;
scale_emit_cnt <= 9'd0;
j <= 8'd0;
start <= 8'd0;
layer_len <= 8'd0;
zeta_idx <= 7'd0;
layer <= 3'd0;
bf_done <= 1'b0;
mode_r <= 1'b0;
layer_issue_done <= 1'b0;
bf_inflight <= 5'd0;
wa_d0 <= 8'd0; wa_d1 <= 8'd0; wa_d2 <= 8'd0; wa_d3 <= 8'd0;
wa_d4 <= 8'd0; wa_d5 <= 8'd0; wa_d6 <= 8'd0; wa_d7 <= 8'd0;
wb_d0 <= 8'd0; wb_d1 <= 8'd0; wb_d2 <= 8'd0; wb_d3 <= 8'd0;
wb_d4 <= 8'd0; wb_d5 <= 8'd0; wb_d6 <= 8'd0; wb_d7 <= 8'd0;
wb_valid_r <= 1'b0;
wb_a_data_r <= 12'd0;
wb_b_data_r <= 12'd0;
wb_wa_r <= 8'd0;
wb_wb_r <= 8'd0;
coeff_out_r <= 12'd0;
valid_o_r <= 1'b0;
scale_valid_i <= 1'b0;
scale_a_i <= 12'd0;
for (ci = 0; ci < N; ci = ci + 1) cr[ci] <= 12'd0;
end else begin end else begin
state <= next_state; state <= next_state;
scale_valid_i <= 1'b0;
wa_d0 <= j; wa_d1 <= wa_d0; wa_d2 <= wa_d1; wa_d3 <= wa_d2;
wa_d4 <= wa_d3; wa_d5 <= wa_d4; wa_d6 <= wa_d5; wa_d7 <= wa_d6;
wb_d0 <= j + layer_len; wb_d1 <= wb_d0; wb_d2 <= wb_d1; wb_d3 <= wb_d2;
wb_d4 <= wb_d3; wb_d5 <= wb_d4; wb_d6 <= wb_d5; wb_d7 <= wb_d6;
if (issue_fire && !wb_valid_r)
bf_inflight <= bf_inflight + 5'd1;
else if (!issue_fire && wb_valid_r)
bf_inflight <= bf_inflight - 5'd1;
if (state != S_OUTPUT && state != S_OUT_SCALE)
valid_o_r <= 1'b0;
if (state == S_IDLE && valid_i) begin if (state == S_IDLE && valid_i) begin
cr[0] <= coeff_in; cr[0] <= coeff_in;
load_cnt<=1; out_cnt<=0; j<=0; start<=0; layer<=0; bf_done<=0; load_cnt <= 8'd1;
if (!mode) begin layer_len<=128; zeta_idx<=1; end out_cnt <= 8'd0;
else begin layer_len<=2; zeta_idx<=127; end scale_issue_cnt <= 9'd0;
scale_emit_cnt <= 9'd0;
j <= 8'd0;
start <= 8'd0;
layer <= 3'd0;
bf_done <= 1'b0;
layer_issue_done <= 1'b0;
bf_inflight <= 5'd0;
mode_r <= mode;
if (!mode) begin
layer_len <= 8'd128;
zeta_idx <= 7'd1;
end else begin
layer_len <= 8'd2;
zeta_idx <= 7'd127;
end
end end
if (state == S_LOAD && valid_i) begin if (state == S_LOAD && valid_i) begin
@@ -90,42 +192,93 @@ module ntt_core (
load_cnt <= load_cnt + 8'd1; load_cnt <= load_cnt + 8'd1;
end end
// S_CMP_A: set read addresses (j, j+len) if (state == S_CMP_RUN && !layer_issue_done) begin
if (state == S_CMP_A) begin if (issue_group_last) begin
r_wa <= j; if (!mode_r) zeta_idx <= zeta_idx + 7'd1;
r_wb <= j + layer_len; else zeta_idx <= zeta_idx - 7'd1;
end
// S_CMP_B: capture read data if (issue_layer_last) begin
if (state == S_CMP_B) begin layer_issue_done <= 1'b1;
r_a <= cr[j];
r_b <= cr[j + layer_len];
end
// S_CMP_C: write butterfly results, advance counters
if (state == S_CMP_C) begin
cr[r_wa] <= bf_a_out;
cr[r_wb] <= bf_b_out;
j <= j + 8'd1;
if (j + 8'd1 >= start + layer_len) begin
if (!mode) zeta_idx <= zeta_idx + 7'd1;
else zeta_idx <= zeta_idx - 7'd1;
if ({1'b0,start} + {1'b0,layer_len} + {1'b0,layer_len} >= 256) begin
layer <= layer + 3'd1;
layer_len <= mode ? (layer_len<<1) : (layer_len>>1);
start <= 0; j <= 0;
if (layer + 3'd1 >= LAYERS) bf_done <= 1'b1;
end else begin end else begin
start <= start + layer_len + layer_len; start <= next_group_start[7:0];
j <= start + layer_len + layer_len; j <= next_group_start[7:0];
end end
end else begin
j <= j + 8'd1;
end end
end end
if (state == S_OUTPUT && ready_i) if (wb_valid_r) begin
out_cnt <= (out_cnt>=255) ? 0 : (out_cnt+8'd1); cr[wb_wa_r] <= wb_a_data_r;
cr[wb_wb_r] <= wb_b_data_r;
end
wb_valid_r <= bf_valid;
if (bf_valid) begin
wb_a_data_r <= bf_a_out;
wb_b_data_r <= bf_b_out;
wb_wa_r <= wa_d7;
wb_wb_r <= wb_d7;
end
if (state == S_CMP_DRAIN && bf_inflight == 5'd0) begin
if (layer + 3'd1 >= LAYERS) begin
bf_done <= 1'b1;
end else begin
layer <= layer + 3'd1;
layer_len <= next_layer_len;
start <= 8'd0;
j <= 8'd0;
layer_issue_done <= 1'b0;
end
end
if (state == S_OUT_PREP) begin
out_cnt <= 8'd0;
scale_issue_cnt <= 9'd0;
scale_emit_cnt <= 9'd0;
if (!mode_r) begin
coeff_out_r <= cr[0];
valid_o_r <= 1'b1;
end
end
if (state == S_OUTPUT && valid_o_r && ready_i) begin
if (out_cnt < 8'd255) begin
out_cnt <= out_cnt + 8'd1;
coeff_out_r <= cr[out_cnt + 8'd1];
valid_o_r <= 1'b1;
end else begin
out_cnt <= 8'd0;
valid_o_r <= 1'b0;
end
end
if (state == S_OUT_SCALE) begin
if (scale_issue_cnt < 9'd256) begin
scale_valid_i <= 1'b1;
scale_a_i <= cr[scale_issue_cnt[7:0]];
scale_issue_cnt <= scale_issue_cnt + 9'd1;
end
valid_o_r <= 1'b0;
if (scale_valid_o) begin
coeff_out_r <= scale_product;
valid_o_r <= 1'b1;
scale_emit_cnt <= scale_emit_cnt + 9'd1;
end
end
if (state == S_DONE) begin
load_cnt <= 8'd0;
out_cnt <= 8'd0;
scale_issue_cnt <= 9'd0;
scale_emit_cnt <= 9'd0;
layer_issue_done <= 1'b0;
bf_inflight <= 5'd0;
wb_valid_r <= 1'b0;
valid_o_r <= 1'b0;
end
end end
end end

View File

@@ -38,8 +38,9 @@ xvlog -sv ${NTT_DIR}/barrett_mul.v
# ================================================================ # ================================================================
puts "=== Compiling PolyMul RTL sources ===" puts "=== Compiling PolyMul RTL sources ==="
# Basecase multiplier (instantiates barrett_mul) # Basecase multipliers
xvlog -sv ${PM_DIR}/basecase_mul.v xvlog -sv ${PM_DIR}/basecase_mul.v
xvlog -sv ${PM_DIR}/basecase_mul_pipe.v
# PolyMul zeta ROM # PolyMul zeta ROM
xvlog -sv ${PM_DIR}/poly_mul_zeta_rom.v xvlog -sv ${PM_DIR}/poly_mul_zeta_rom.v

View File

@@ -0,0 +1,144 @@
// basecase_mul_pipe.v - pipelined NTT-domain degree-1 multiplication
//
// Fixed-latency replacement for the combinational basecase_mul hot path.
// The shared ntt/barrett_mul remains combinational for NTT users; this module
// keeps the extra registers local to poly_mul_sync.
(* use_dsp = "yes" *)
module basecase_mul_pipe (
input clk,
input rst_n,
input valid_i,
input [11:0] a0, a1,
input [11:0] b0, b1,
input [11:0] zeta,
output [11:0] c0,
output [11:0] c1,
output valid_o
);
localparam [11:0] Q12 = 12'd3329;
localparam [12:0] K13 = 13'd5039;
localparam [24:0] Q25 = 25'd3329;
function [12:0] barrett_q;
input [23:0] p;
reg [36:0] prod;
begin
prod = {13'd0, p} * K13;
barrett_q = prod[36:24];
end
endfunction
function [11:0] barrett_reduce;
input [23:0] p;
input [12:0] qe;
reg [24:0] q_approx;
reg [24:0] r0;
reg [24:0] r1;
reg [24:0] r2;
begin
q_approx = qe * Q12;
r0 = {1'b0, p} - q_approx;
r1 = (r0 >= Q25) ? (r0 - Q25) : r0;
r2 = (r1 >= Q25) ? (r1 - Q25) : r1;
barrett_reduce = r2[11:0];
end
endfunction
function [11:0] mod_add;
input [11:0] x;
input [11:0] y;
reg [12:0] sum;
begin
sum = {1'b0, x} + {1'b0, y};
mod_add = (sum >= {1'b0, Q12}) ? (sum[11:0] - Q12) : sum[11:0];
end
endfunction
// Stage 1: first-level scalar products.
reg [23:0] p10, p11, p12, p13;
reg [11:0] zeta_s1;
// Stage 2: Barrett quotient estimates for first-level products.
reg [23:0] p20, p21, p22, p23;
reg [12:0] q20, q21, q22, q23;
reg [11:0] zeta_s2;
// Stage 3: first-level reduced products.
reg [11:0] t1_s3, t2_s3, t3_s3, t4_s3;
reg [11:0] zeta_s3;
// Stage 4: zeta product for c0's second Barrett multiply.
reg [11:0] t1_s4, t3_s4, t4_s4;
reg [23:0] pz_s4;
// Stage 5: Barrett quotient estimate for zeta product.
reg [11:0] t1_s5, t3_s5, t4_s5;
reg [23:0] pz_s5;
reg [12:0] qz_s5;
// Stage 6: reduced zeta product.
reg [11:0] t1_s6, t3_s6, t4_s6, tz_s6;
// Stage 7: final modular additions.
reg [11:0] c0_r, c1_r;
reg [6:0] valid_sr;
assign c0 = c0_r;
assign c1 = c1_r;
assign valid_o = valid_sr[6];
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
p10 <= 24'd0; p11 <= 24'd0; p12 <= 24'd0; p13 <= 24'd0; zeta_s1 <= 12'd0;
p20 <= 24'd0; p21 <= 24'd0; p22 <= 24'd0; p23 <= 24'd0;
q20 <= 13'd0; q21 <= 13'd0; q22 <= 13'd0; q23 <= 13'd0; zeta_s2 <= 12'd0;
t1_s3 <= 12'd0; t2_s3 <= 12'd0; t3_s3 <= 12'd0; t4_s3 <= 12'd0; zeta_s3 <= 12'd0;
t1_s4 <= 12'd0; t3_s4 <= 12'd0; t4_s4 <= 12'd0; pz_s4 <= 24'd0;
t1_s5 <= 12'd0; t3_s5 <= 12'd0; t4_s5 <= 12'd0; pz_s5 <= 24'd0; qz_s5 <= 13'd0;
t1_s6 <= 12'd0; t3_s6 <= 12'd0; t4_s6 <= 12'd0; tz_s6 <= 12'd0;
c0_r <= 12'd0; c1_r <= 12'd0; valid_sr <= 7'd0;
end else begin
valid_sr <= {valid_sr[5:0], valid_i};
p10 <= {12'd0, a0} * b0;
p11 <= {12'd0, a1} * b1;
p12 <= {12'd0, a1} * b0;
p13 <= {12'd0, a0} * b1;
zeta_s1 <= zeta;
p20 <= p10; p21 <= p11; p22 <= p12; p23 <= p13;
q20 <= barrett_q(p10);
q21 <= barrett_q(p11);
q22 <= barrett_q(p12);
q23 <= barrett_q(p13);
zeta_s2 <= zeta_s1;
t1_s3 <= barrett_reduce(p20, q20);
t2_s3 <= barrett_reduce(p21, q21);
t3_s3 <= barrett_reduce(p22, q22);
t4_s3 <= barrett_reduce(p23, q23);
zeta_s3 <= zeta_s2;
t1_s4 <= t1_s3;
t3_s4 <= t3_s3;
t4_s4 <= t4_s3;
pz_s4 <= {12'd0, t2_s3} * zeta_s3;
t1_s5 <= t1_s4;
t3_s5 <= t3_s4;
t4_s5 <= t4_s4;
pz_s5 <= pz_s4;
qz_s5 <= barrett_q(pz_s4);
t1_s6 <= t1_s5;
t3_s6 <= t3_s5;
t4_s6 <= t4_s5;
tz_s6 <= barrett_reduce(pz_s5, qz_s5);
c0_r <= mod_add(t1_s6, tz_s6);
c1_r <= mod_add(t3_s6, t4_s6);
end
end
endmodule

View File

@@ -4,8 +4,10 @@
// 256-coefficient NTT-domain polynomials. // 256-coefficient NTT-domain polynomials.
// //
// Operation flow: // Operation flow:
// IDLE LOAD (256× A+B pairs) COMP_CALC (read+compute, 1 cycle) // IDLE -> LOAD (256x A+B pairs) -> RUN -> IDLE
// COMP_C0 (output c0) COMP_C1 (output c1) DONE IDLE //
// RUN issues one base-case multiply every two cycles, matching the two-cycle
// c0/c1 output bandwidth after the basecase pipeline fills.
// //
// The LOAD phase accepts both A and B coefficients simultaneously // The LOAD phase accepts both A and B coefficients simultaneously
// (one pair per cycle) on coeff_a_in/coeff_b_in. // (one pair per cycle) on coeff_a_in/coeff_b_in.
@@ -22,7 +24,7 @@
// valid_i - Input valid // valid_i - Input valid
// ready_o - Ready to accept input (high in IDLE/LOAD) // ready_o - Ready to accept input (high in IDLE/LOAD)
// coeff_out[11:0] - Result coefficient output // coeff_out[11:0] - Result coefficient output
// valid_o - Output valid (high in COMP_C0/COMP_C1) // valid_o - Output valid during RUN output phase
// ready_i - Output consumer ready // ready_i - Output consumer ready
module poly_mul_sync ( module poly_mul_sync (
@@ -37,12 +39,9 @@ module poly_mul_sync (
); );
// State definitions // State definitions
localparam S_IDLE = 3'd0; localparam S_IDLE = 3'd0;
localparam S_LOAD = 3'd1; localparam S_LOAD = 3'd1;
localparam S_COMP_CALC = 3'd2; localparam S_RUN = 3'd2;
localparam S_COMP_C0 = 3'd3;
localparam S_COMP_C1 = 3'd4;
localparam S_DONE = 3'd5;
reg [2:0] state, next_state; reg [2:0] state, next_state;
@@ -52,14 +51,23 @@ module poly_mul_sync (
// Counters // Counters
reg [7:0] load_cnt; // 0..256 for loading 256 pairs reg [7:0] load_cnt; // 0..256 for loading 256 pairs
reg [6:0] comp_k; // 0..127, current base-case index reg [7:0] issue_cnt; // 0..128, issued base-cases
reg [7:0] out_cnt; // 0..255, consumed output coefficients
reg issue_gap; // spaces base-case requests to match c0/c1 output
reg out_phase; // 0=c0, 1=c1
reg out_valid_r;
// Registered basecase_mul results // Registered basecase_mul inputs/results
reg [11:0] bc_a0_reg, bc_a1_reg;
reg [11:0] bc_b0_reg, bc_b1_reg;
reg [11:0] bc_zeta_reg;
reg bc_valid_reg;
reg [11:0] c0_reg, c1_reg; reg [11:0] c0_reg, c1_reg;
// Combinational read signals for COMP_CALC // Combinational read signals for the next base-case request.
wire [7:0] addr_even = {comp_k, 1'b0}; // comp_k * 2 (7+1 = 8 bits) wire [6:0] issue_k = issue_cnt[6:0];
wire [7:0] addr_odd = {comp_k, 1'b1}; // comp_k * 2 + 1 wire [7:0] addr_even = {issue_k, 1'b0}; // issue_k * 2
wire [7:0] addr_odd = {issue_k, 1'b1}; // issue_k * 2 + 1
wire [11:0] mem_a0 = mem_A[addr_even]; wire [11:0] mem_a0 = mem_A[addr_even];
wire [11:0] mem_a1 = mem_A[addr_odd]; wire [11:0] mem_a1 = mem_A[addr_odd];
wire [11:0] mem_b0 = mem_B[addr_even]; wire [11:0] mem_b0 = mem_B[addr_even];
@@ -68,41 +76,42 @@ module poly_mul_sync (
// Zeta ROM // Zeta ROM
wire [11:0] zeta; wire [11:0] zeta;
poly_mul_zeta_rom u_zeta ( poly_mul_zeta_rom u_zeta (
.addr (comp_k), .addr (issue_k),
.zeta (zeta) .zeta (zeta)
); );
// Basecase multiply // Pipelined basecase multiply. Requests are issued every two cycles; inputs
// are registered locally so issue_cnt does not directly drive DSP input muxes.
wire [11:0] bc_c0, bc_c1; wire [11:0] bc_c0, bc_c1;
basecase_mul u_bc ( wire bc_vo;
.a0 (mem_a0), basecase_mul_pipe u_bc (
.a1 (mem_a1), .clk (clk),
.b0 (mem_b0), .rst_n(rst_n),
.b1 (mem_b1), .valid_i(bc_valid_reg),
.zeta(zeta), .a0 (bc_a0_reg),
.a1 (bc_a1_reg),
.b0 (bc_b0_reg),
.b1 (bc_b1_reg),
.zeta(bc_zeta_reg),
.c0 (bc_c0), .c0 (bc_c0),
.c1 (bc_c1) .c1 (bc_c1),
.valid_o(bc_vo)
); );
// Output interface // Output interface
assign ready_o = (state == S_IDLE) || (state == S_LOAD); assign ready_o = (state == S_IDLE) || (state == S_LOAD);
assign valid_o = (state == S_COMP_C0) || (state == S_COMP_C1); assign valid_o = out_valid_r;
assign coeff_out = (state == S_COMP_C0) ? c0_reg : c1_reg; assign coeff_out = out_phase ? c1_reg : c0_reg;
// State transition logic (combinational) // State transition logic (combinational)
always @* begin always @* begin
next_state = state; next_state = state;
case (state) case (state)
S_IDLE: if (valid_i && ready_o) next_state = S_LOAD; S_IDLE: if (valid_i && ready_o) next_state = S_LOAD;
S_LOAD: if (load_cnt >= 255 && valid_i && ready_o) S_LOAD: if (load_cnt >= 8'd255 && valid_i && ready_o)
next_state = S_COMP_CALC; next_state = S_RUN;
S_COMP_CALC: next_state = S_COMP_C0; S_RUN: if (valid_o && ready_i && out_cnt == 8'd255)
S_COMP_C0: if (valid_o && ready_i) next_state = S_COMP_C1; next_state = S_IDLE;
S_COMP_C1: if (valid_o && ready_i) begin
if (comp_k >= 127) next_state = S_DONE;
else next_state = S_COMP_CALC;
end
S_DONE: next_state = S_IDLE;
default: next_state = S_IDLE; default: next_state = S_IDLE;
endcase endcase
end end
@@ -113,7 +122,17 @@ module poly_mul_sync (
if (!rst_n) begin if (!rst_n) begin
state <= S_IDLE; state <= S_IDLE;
load_cnt <= 8'd0; load_cnt <= 8'd0;
comp_k <= 7'd0; issue_cnt <= 8'd0;
out_cnt <= 8'd0;
issue_gap <= 1'b0;
out_phase <= 1'b0;
out_valid_r <= 1'b0;
bc_a0_reg <= 12'd0;
bc_a1_reg <= 12'd0;
bc_b0_reg <= 12'd0;
bc_b1_reg <= 12'd0;
bc_zeta_reg <= 12'd0;
bc_valid_reg <= 1'b0;
c0_reg <= 12'd0; c0_reg <= 12'd0;
c1_reg <= 12'd0; c1_reg <= 12'd0;
for (i = 0; i < 256; i = i + 1) begin for (i = 0; i < 256; i = i + 1) begin
@@ -122,6 +141,7 @@ module poly_mul_sync (
end end
end else begin end else begin
state <= next_state; state <= next_state;
bc_valid_reg <= 1'b0;
// ---- LOAD phase ---- // ---- LOAD phase ----
// First coefficient captured on IDLE LOAD transition // First coefficient captured on IDLE LOAD transition
@@ -129,6 +149,11 @@ module poly_mul_sync (
mem_A[0] <= coeff_a_in; mem_A[0] <= coeff_a_in;
mem_B[0] <= coeff_b_in; mem_B[0] <= coeff_b_in;
load_cnt <= 8'd1; load_cnt <= 8'd1;
issue_cnt <= 8'd0;
out_cnt <= 8'd0;
issue_gap <= 1'b0;
out_phase <= 1'b0;
out_valid_r <= 1'b0;
end end
// Subsequent coefficients in LOAD state // Subsequent coefficients in LOAD state
@@ -136,29 +161,65 @@ module poly_mul_sync (
mem_A[load_cnt] <= coeff_a_in; mem_A[load_cnt] <= coeff_a_in;
mem_B[load_cnt] <= coeff_b_in; mem_B[load_cnt] <= coeff_b_in;
load_cnt <= load_cnt + 8'd1; load_cnt <= load_cnt + 8'd1;
if (load_cnt >= 8'd255) begin
issue_cnt <= 8'd0;
out_cnt <= 8'd0;
issue_gap <= 1'b0;
out_phase <= 1'b0;
out_valid_r <= 1'b0;
end
end end
// ---- COMPUTE phase ---- // ---- COMPUTE phase ----
// COMP_CALC: capture basecase_mul results // RUN: cut issue_cnt -> memory mux -> basecase DSP with local input
if (state == S_COMP_CALC) begin // regs, then launch requests every other cycle. The two-cycle issue
c0_reg <= bc_c0; // interval matches the c0/c1 output bandwidth, so no deep result FIFO
c1_reg <= bc_c1; // is needed for this always-ready system.
if (state == S_RUN) begin
if (issue_cnt < 8'd128) begin
if (!issue_gap) begin
bc_a0_reg <= mem_a0;
bc_a1_reg <= mem_a1;
bc_b0_reg <= mem_b0;
bc_b1_reg <= mem_b1;
bc_zeta_reg <= zeta;
bc_valid_reg <= 1'b1;
issue_cnt <= issue_cnt + 8'd1;
issue_gap <= 1'b1;
end else begin
issue_gap <= 1'b0;
end
end
if (out_valid_r && ready_i) begin
out_cnt <= out_cnt + 8'd1;
if (out_cnt == 8'd255) begin
out_valid_r <= 1'b0;
out_phase <= 1'b0;
end else if (!out_phase) begin
out_phase <= 1'b1;
end else begin
out_valid_r <= 1'b0;
out_phase <= 1'b0;
end
end
if (bc_vo) begin
c0_reg <= bc_c0;
c1_reg <= bc_c1;
out_valid_r <= 1'b1;
out_phase <= 1'b0;
end
end end
// COMP_C0 COMP_C1: c0 was consumed, increment comp_k // ---- final output consumed: reset counters before accepting a new op ----
if (state == S_COMP_C0 && valid_o && ready_i) begin if (state == S_RUN && valid_o && ready_i && out_cnt == 8'd255) begin
// comp_k stays same, c1 still to output
end
// COMP_C1 COMP_CALC: c1 was consumed, advance to next pair
if (state == S_COMP_C1 && valid_o && ready_i) begin
comp_k <= comp_k + 7'd1;
end
// ---- DONE IDLE: reset counters ----
if (state == S_DONE) begin
load_cnt <= 8'd0; load_cnt <= 8'd0;
comp_k <= 7'd0; issue_cnt <= 8'd0;
out_cnt <= 8'd0;
issue_gap <= 1'b0;
out_phase <= 1'b0;
out_valid_r <= 1'b0;
end end
end end
end end

View File

@@ -100,6 +100,16 @@ module sha3_top (
reg mb_last_r; // captured last-block flag reg mb_last_r; // captured last-block flag
reg [255:0] mb_digest_r; // latched 256-bit digest (sticky in MB_DONE) reg [255:0] mb_digest_r; // latched 256-bit digest (sticky in MB_DONE)
// ================================================================
// Keccak core
// ================================================================
wire kc_valid_i;
/* verilator lint_off UNUSEDSIGNAL */
wire [1599:0] kc_state_o;
wire kc_ready_o;
/* verilator lint_on UNUSEDSIGNAL */
wire kc_valid_o;
// XOR the incoming block into the low 1088 bits (rate) of the state. // XOR the incoming block into the low 1088 bits (rate) of the state.
wire [1599:0] mb_xored; wire [1599:0] mb_xored;
assign mb_xored = mb_state_r ^ {{(1600-1088){1'b0}}, mb_block_i}; assign mb_xored = mb_state_r ^ {{(1600-1088){1'b0}}, mb_block_i};
@@ -119,16 +129,6 @@ module sha3_top (
endcase endcase
end end
// ================================================================
// Keccak core
// ================================================================
wire kc_valid_i;
/* verilator lint_off UNUSEDSIGNAL */
wire [1599:0] kc_state_o;
wire kc_ready_o;
/* verilator lint_on UNUSEDSIGNAL */
wire kc_valid_o;
// Keccak input: multi-block xored state when mb_en, else single-block absorb. // Keccak input: multi-block xored state when mb_en, else single-block absorb.
wire [1599:0] kc_state_i_mux; wire [1599:0] kc_state_i_mux;
assign kc_state_i_mux = mb_en ? mb_xored : absorb_state; assign kc_state_i_mux = mb_en ? mb_xored : absorb_state;

View File

@@ -13,6 +13,8 @@ module tb_mlkem_dec_katK_xsim;
localparam EKB = 384*KP + 32; // ek_pke bytes within dk localparam EKB = 384*KP + 32; // ek_pke bytes within dk
localparam DKPB = 384*KP; // dk_pke bytes localparam DKPB = 384*KP; // dk_pke bytes
localparam CTB = (KP==4) ? 1568 : (32*(10*KP+4)); // ct bytes: 768/1088/1568 localparam CTB = (KP==4) ? 1568 : (32*(10*KP+4)); // ct bytes: 768/1088/1568
localparam integer CLK_PERIOD_NS = 50; // 20 MHz
localparam integer CLK_HALF_NS = CLK_PERIOD_NS / 2;
reg clk=0, rst_n=0, start_i=0; reg clk=0, rst_n=0, start_i=0;
reg [2:0] k_i; reg [2:0] k_i;
@@ -47,7 +49,7 @@ module tb_mlkem_dec_katK_xsim;
.dbg_mprime_o(dbg_mprime_o), .dbg_kbar_o(dbg_kbar_o), .dbg_mprime_o(dbg_mprime_o), .dbg_kbar_o(dbg_kbar_o),
.dbg_decz_o(dbg_decz_o), .dbg_dech_o(dbg_dech_o) .dbg_decz_o(dbg_decz_o), .dbg_dech_o(dbg_dech_o)
); );
always #5 clk = ~clk; always #(CLK_HALF_NS) clk = ~clk;
reg [7:0] dk_b [0:DKB-1]; reg [7:0] dk_b [0:DKB-1];
reg [7:0] ct_b [0:CTB-1]; reg [7:0] ct_b [0:CTB-1];
@@ -55,8 +57,72 @@ module tb_mlkem_dec_katK_xsim;
reg [7:0] ss_b [0:31]; reg [7:0] ss_b [0:31];
reg [7:0] ssn_b [0:31]; reg [7:0] ssn_b [0:31];
integer c, i, j, errors, casenum; integer c, i, j, errors, casenum;
integer state_cyc [0:31];
integer si;
reg [8*80-1:0] tag, dkfile, ctfile, ssfile, ctnfile, ssnfile; reg [8*80-1:0] tag, dkfile, ctfile, ssfile, ctnfile, ssnfile;
function [8*16-1:0] state_name;
input [4:0] s;
begin
case (s)
5'd0: state_name = "IDLE";
5'd1: state_name = "G";
5'd2: state_name = "A";
5'd3: state_name = "C";
5'd4: state_name = "N";
5'd5: state_name = "M";
5'd6: state_name = "E";
5'd7: state_name = "H";
5'd8: state_name = "ENC_LOAD";
5'd9: state_name = "ENC_H";
5'd10: state_name = "ENC_G";
5'd11: state_name = "ENC_A";
5'd12: state_name = "ENC_TDEC";
5'd13: state_name = "ENC_C";
5'd14: state_name = "ENC_N";
5'd15: state_name = "ENC_U";
5'd16: state_name = "ENC_C1";
5'd17: state_name = "ENC_V";
5'd18: state_name = "ENC_C2";
5'd19: state_name = "ENC_E2MV";
5'd20: state_name = "DEC_LOAD";
5'd21: state_name = "DEC_DECOMP";
5'd22: state_name = "DEC_SDEC";
5'd23: state_name = "DEC_NTT";
5'd24: state_name = "DEC_W";
5'd25: state_name = "DEC_MENC";
5'd26: state_name = "DEC_G";
5'd27: state_name = "DEC_J";
5'd28: state_name = "DEC_CMP";
5'd31: state_name = "DONE";
default: state_name = "UNKNOWN";
endcase
end
endfunction
task reset_timing;
begin
for (si = 0; si < 32; si = si + 1) state_cyc[si] = 0;
end
endtask
task print_timing;
input [8*16-1:0] op_name;
input integer cycles;
integer runtime_ns;
begin
runtime_ns = cycles * CLK_PERIOD_NS;
$display("TIME K=%0d CASE=%0d OP=%0s cycles=%0d clk=20MHz period=%0dns runtime=%0d ns (%0d.%03d us, %0d.%03d ms)",
KP, casenum, op_name, cycles, CLK_PERIOD_NS, runtime_ns,
runtime_ns/1000, runtime_ns%1000,
runtime_ns/1000000, (runtime_ns%1000000)/1000);
$display("TIME_BREAKDOWN K=%0d CASE=%0d OP=%0s", KP, casenum, op_name);
for (si = 0; si < 32; si = si + 1)
if (state_cyc[si] != 0)
$display(" STATE %-12s cycles=%0d time_ns=%0d", state_name(si[4:0]), state_cyc[si], state_cyc[si]*CLK_PERIOD_NS);
end
endtask
initial begin initial begin
if (!$value$plusargs("CASE=%d", casenum)) casenum = 0; if (!$value$plusargs("CASE=%d", casenum)) casenum = 0;
$sformat(tag, "k%0d", KP); $sformat(tag, "k%0d", KP);
@@ -130,9 +196,12 @@ module tb_mlkem_dec_katK_xsim;
end end
c_in_we = 1'b0; @(posedge clk); c_in_we = 1'b0; @(posedge clk);
start_i=1; @(posedge clk); start_i=0; start_i=1; @(posedge clk); start_i=0;
c=0; while(!done_o && c<2000000) begin @(posedge clk); c=c+1; end #1;
reset_timing;
c=0; while(!done_o && c<2000000) begin state_cyc[dut.st] = state_cyc[dut.st] + 1; @(posedge clk); c=c+1; end
if(!done_o) begin $display("FAIL K=%0d case %0d: timeout (sel=%0d)", KP, casenum, sel); $finish; end if(!done_o) begin $display("FAIL K=%0d case %0d: timeout (sel=%0d)", KP, casenum, sel); $finish; end
$display("=== Decaps run (sel=%0d) done in %0d cyc ===", sel, c); $display("=== Decaps run (sel=%0d) done in %0d cyc ===", sel, c);
print_timing(sel ? "DecapsReject" : "DecapsAccept", c);
// settle so dbg taps reflect the finished run // settle so dbg taps reflect the finished run
repeat(2) @(posedge clk); repeat(2) @(posedge clk);
end end

View File

@@ -12,6 +12,8 @@ module tb_mlkem_enc_katK_xsim;
localparam CTB = (KP==4) ? 1568 : (32*(10*KP+4)); // ct bytes: K2 768,K3 1088,K4 1568 localparam CTB = (KP==4) ? 1568 : (32*(10*KP+4)); // ct bytes: K2 768,K3 1088,K4 1568
localparam DU = (KP==4) ? 11 : 10; // compression du localparam DU = (KP==4) ? 11 : 10; // compression du
localparam C1B = 32*DU*KP; // c1 byte count: K2 640,K3 960,K4 1408 localparam C1B = 32*DU*KP; // c1 byte count: K2 640,K3 960,K4 1408
localparam integer CLK_PERIOD_NS = 50; // 20 MHz
localparam integer CLK_HALF_NS = CLK_PERIOD_NS / 2;
reg clk=0, rst_n=0, start_i=0; reg clk=0, rst_n=0, start_i=0;
reg [2:0] k_i; reg [2:0] k_i;
@@ -41,15 +43,79 @@ module tb_mlkem_enc_katK_xsim;
.dbg_r_o(dbg_r_o), .dbg_hek_o(dbg_hek_o), .dbg_r_o(dbg_r_o), .dbg_hek_o(dbg_hek_o),
.dbg_mprime_o(), .dbg_kbar_o(), .dbg_decz_o(), .dbg_dech_o() .dbg_mprime_o(), .dbg_kbar_o(), .dbg_decz_o(), .dbg_dech_o()
); );
always #5 clk = ~clk; always #(CLK_HALF_NS) clk = ~clk;
reg [7:0] ek_b [0:EKB-1]; reg [7:0] ek_b [0:EKB-1];
reg [7:0] m_b [0:31]; reg [7:0] m_b [0:31];
reg [7:0] ss_b [0:31]; reg [7:0] ss_b [0:31];
reg [7:0] ct_b [0:CTB-1]; reg [7:0] ct_b [0:CTB-1];
integer c, i, errors, casenum, j; integer c, i, errors, casenum, j;
integer state_cyc [0:31];
integer si;
reg [8*80-1:0] tag, ekfile, mfile, ssfile, ctfile; reg [8*80-1:0] tag, ekfile, mfile, ssfile, ctfile;
function [8*16-1:0] state_name;
input [4:0] s;
begin
case (s)
5'd0: state_name = "IDLE";
5'd1: state_name = "G";
5'd2: state_name = "A";
5'd3: state_name = "C";
5'd4: state_name = "N";
5'd5: state_name = "M";
5'd6: state_name = "E";
5'd7: state_name = "H";
5'd8: state_name = "ENC_LOAD";
5'd9: state_name = "ENC_H";
5'd10: state_name = "ENC_G";
5'd11: state_name = "ENC_A";
5'd12: state_name = "ENC_TDEC";
5'd13: state_name = "ENC_C";
5'd14: state_name = "ENC_N";
5'd15: state_name = "ENC_U";
5'd16: state_name = "ENC_C1";
5'd17: state_name = "ENC_V";
5'd18: state_name = "ENC_C2";
5'd19: state_name = "ENC_E2MV";
5'd20: state_name = "DEC_LOAD";
5'd21: state_name = "DEC_DECOMP";
5'd22: state_name = "DEC_SDEC";
5'd23: state_name = "DEC_NTT";
5'd24: state_name = "DEC_W";
5'd25: state_name = "DEC_MENC";
5'd26: state_name = "DEC_G";
5'd27: state_name = "DEC_J";
5'd28: state_name = "DEC_CMP";
5'd31: state_name = "DONE";
default: state_name = "UNKNOWN";
endcase
end
endfunction
task reset_timing;
begin
for (si = 0; si < 32; si = si + 1) state_cyc[si] = 0;
end
endtask
task print_timing;
input [8*16-1:0] op_name;
input integer cycles;
integer runtime_ns;
begin
runtime_ns = cycles * CLK_PERIOD_NS;
$display("TIME K=%0d CASE=%0d OP=%0s cycles=%0d clk=20MHz period=%0dns runtime=%0d ns (%0d.%03d us, %0d.%03d ms)",
KP, casenum, op_name, cycles, CLK_PERIOD_NS, runtime_ns,
runtime_ns/1000, runtime_ns%1000,
runtime_ns/1000000, (runtime_ns%1000000)/1000);
$display("TIME_BREAKDOWN K=%0d CASE=%0d OP=%0s", KP, casenum, op_name);
for (si = 0; si < 32; si = si + 1)
if (state_cyc[si] != 0)
$display(" STATE %-12s cycles=%0d time_ns=%0d", state_name(si[4:0]), state_cyc[si], state_cyc[si]*CLK_PERIOD_NS);
end
endtask
initial begin initial begin
if (!$value$plusargs("CASE=%d", casenum)) casenum = 0; if (!$value$plusargs("CASE=%d", casenum)) casenum = 0;
$sformat(tag, "k%0d", KP); $sformat(tag, "k%0d", KP);
@@ -81,9 +147,12 @@ module tb_mlkem_enc_katK_xsim;
// ---- run Encaps ---- // ---- run Encaps ----
start_i=1; @(posedge clk); start_i=0; start_i=1; @(posedge clk); start_i=0;
c=0; while(!done_o && c<2000000) begin @(posedge clk); c=c+1; end #1;
reset_timing;
c=0; while(!done_o && c<2000000) begin state_cyc[dut.st] = state_cyc[dut.st] + 1; @(posedge clk); c=c+1; end
if(!done_o) begin $display("FAIL K=%0d case %0d: timeout", KP, casenum); $finish; end if(!done_o) begin $display("FAIL K=%0d case %0d: timeout", KP, casenum); $finish; end
$display("=== Encaps E0 done in %0d cyc ===", c); $display("=== Encaps E0 done in %0d cyc ===", c);
print_timing("Encaps", c);
$write(" H(ek) = "); for (j=0;j<32;j=j+1) $write("%02x", dbg_hek_o[8*j +: 8]); $write("\n"); $write(" H(ek) = "); for (j=0;j<32;j=j+1) $write("%02x", dbg_hek_o[8*j +: 8]); $write("\n");
$write(" r = "); for (j=0;j<32;j=j+1) $write("%02x", dbg_r_o[8*j +: 8]); $write("\n"); $write(" r = "); for (j=0;j<32;j=j+1) $write("%02x", dbg_r_o[8*j +: 8]); $write("\n");

View File

@@ -1,8 +1,8 @@
// tb_mlkem_hello_world_xsim.v - Hardware run of the ML-KEM hello_world demo. // tb_mlkem_hello_world_xsim.v - Hardware run of the ML-KEM hello_world demo.
// //
// Mirrors the Rust example end-to-end on the mlkem_top DUT (ML-KEM-512, K=2): // Mirrors the Rust example end-to-end on the mlkem_top DUT (default ML-KEM-512, K=2):
// 1. Alice: KeyGen(d=0x42..., z=0x77...) -> (ek 800B, dk 1632B) // 1. Alice: KeyGen(d=0x42..., z=0x77...) -> (ek, dk)
// 2. Bob: Encaps(ek, m=0xDE...) -> (shared_key, kem_ct 768B) // 2. Bob: Encaps(ek, m=0xDE...) -> (shared_key, kem_ct)
// 3. Bob: XOR-encrypt "hello world" with key -> encrypted // 3. Bob: XOR-encrypt "hello world" with key -> encrypted
// 4. Alice: Decaps(dk, kem_ct) -> recovered_key // 4. Alice: Decaps(dk, kem_ct) -> recovered_key
// 5. Alice: XOR-decrypt encrypted with key -> "hello world" // 5. Alice: XOR-decrypt encrypted with key -> "hello world"
@@ -17,10 +17,12 @@
// xelab tb_mlkem_hello_world_xsim ; xsim // xelab tb_mlkem_hello_world_xsim ; xsim
`timescale 1ns/1ps `timescale 1ns/1ps
module tb_mlkem_hello_world_xsim; module tb_mlkem_hello_world_xsim;
localparam KP = 2; // ML-KEM-512 parameter KP = 2; // 2/3/4 = ML-KEM-512/768/1024
localparam EKB = 384*KP + 32; // 800 localparam DU = (KP == 4) ? 11 : 10;
localparam DKB = 768*KP + 96; // 1632 localparam DV = (KP == 4) ? 5 : 4;
localparam CTB = 32*(10*KP + 4); // 768 localparam EKB = 384*KP + 32;
localparam DKB = 768*KP + 96;
localparam CTB = 32*(DU*KP + DV);
localparam MLEN = 11; // "hello world" localparam MLEN = 11; // "hello world"
reg clk=0, rst_n=0, start_i=0; reg clk=0, rst_n=0, start_i=0;
@@ -55,7 +57,7 @@ module tb_mlkem_hello_world_xsim;
.dbg_mprime_o(dbg_mprime_o), .dbg_kbar_o(dbg_kbar_o), .dbg_mprime_o(dbg_mprime_o), .dbg_kbar_o(dbg_kbar_o),
.dbg_decz_o(dbg_decz_o), .dbg_dech_o(dbg_dech_o) .dbg_decz_o(dbg_decz_o), .dbg_dech_o(dbg_dech_o)
); );
always #5 clk = ~clk; always #25 clk = ~clk; // 20 MHz
// storage shuttled between operations (the "wire" between Alice and Bob) // storage shuttled between operations (the "wire" between Alice and Bob)
reg [7:0] ek_b [0:EKB-1]; reg [7:0] ek_b [0:EKB-1];
@@ -93,7 +95,7 @@ module tb_mlkem_hello_world_xsim;
msg_b[0]="h"; msg_b[1]="e"; msg_b[2]="l"; msg_b[3]="l"; msg_b[4]="o"; msg_b[0]="h"; msg_b[1]="e"; msg_b[2]="l"; msg_b[3]="l"; msg_b[4]="o";
msg_b[5]=" "; msg_b[6]="w"; msg_b[7]="o"; msg_b[8]="r"; msg_b[9]="l"; msg_b[10]="d"; msg_b[5]=" "; msg_b[6]="w"; msg_b[7]="o"; msg_b[8]="r"; msg_b[9]="l"; msg_b[10]="d";
$display("=== ML-KEM hello_world (hardware, ML-KEM-512) ==="); $display("=== ML-KEM hello_world (hardware, K=%0d) ===", KP);
$write("Original: \""); $write("Original: \"");
for (i = 0; i < MLEN; i = i + 1) $write("%c", msg_b[i]); for (i = 0; i < MLEN; i = i + 1) $write("%c", msg_b[i]);
$display("\"\n"); $display("\"\n");
@@ -189,7 +191,7 @@ module tb_mlkem_hello_world_xsim;
if (dec_b[i] !== msg_b[i]) errors = errors + 1; if (dec_b[i] !== msg_b[i]) errors = errors + 1;
if (errors == 0) if (errors == 0)
$display("Success: keys match, message recovered. (hardware ML-KEM-512)"); $display("Success: keys match, message recovered. (hardware K=%0d)", KP);
else else
$display("FAILURE: %0d mismatches", errors); $display("FAILURE: %0d mismatches", errors);
$finish; $finish;

View File

@@ -7,6 +7,8 @@ module tb_mlkem_kg_katK_xsim;
parameter KP = 2; parameter KP = 2;
localparam EKB = 384*KP + 32; localparam EKB = 384*KP + 32;
localparam DKB = 768*KP + 96; localparam DKB = 768*KP + 96;
localparam integer CLK_PERIOD_NS = 50; // 20 MHz
localparam integer CLK_HALF_NS = CLK_PERIOD_NS / 2;
reg clk=0, rst_n=0, start_i=0; reg clk=0, rst_n=0, start_i=0;
reg [2:0] k_i; reg [2:0] k_i;
@@ -33,7 +35,7 @@ module tb_mlkem_kg_katK_xsim;
.dbg_r_o(), .dbg_hek_o(), .dbg_r_o(), .dbg_hek_o(),
.dbg_mprime_o(), .dbg_kbar_o(), .dbg_decz_o(), .dbg_dech_o() .dbg_mprime_o(), .dbg_kbar_o(), .dbg_decz_o(), .dbg_dech_o()
); );
always #5 clk = ~clk; always #(CLK_HALF_NS) clk = ~clk;
reg [255:0] dmem [0:0]; reg [255:0] dmem [0:0];
reg [255:0] zmem [0:0]; reg [255:0] zmem [0:0];
@@ -42,8 +44,72 @@ module tb_mlkem_kg_katK_xsim;
reg [7:0] ek_got [0:EKB-1]; // ek bytes read back from DUT reg [7:0] ek_got [0:EKB-1]; // ek bytes read back from DUT
reg [7:0] dk_got [0:DKB-1]; // dk bytes read back from DUT reg [7:0] dk_got [0:DKB-1]; // dk bytes read back from DUT
integer c, i, errors, casenum, j; integer c, i, errors, casenum, j;
integer state_cyc [0:31];
integer si;
reg [8*80-1:0] tag, dfile, zfile, ekfile, dkfile; reg [8*80-1:0] tag, dfile, zfile, ekfile, dkfile;
function [8*16-1:0] state_name;
input [4:0] s;
begin
case (s)
5'd0: state_name = "IDLE";
5'd1: state_name = "G";
5'd2: state_name = "A";
5'd3: state_name = "C";
5'd4: state_name = "N";
5'd5: state_name = "M";
5'd6: state_name = "E";
5'd7: state_name = "H";
5'd8: state_name = "ENC_LOAD";
5'd9: state_name = "ENC_H";
5'd10: state_name = "ENC_G";
5'd11: state_name = "ENC_A";
5'd12: state_name = "ENC_TDEC";
5'd13: state_name = "ENC_C";
5'd14: state_name = "ENC_N";
5'd15: state_name = "ENC_U";
5'd16: state_name = "ENC_C1";
5'd17: state_name = "ENC_V";
5'd18: state_name = "ENC_C2";
5'd19: state_name = "ENC_E2MV";
5'd20: state_name = "DEC_LOAD";
5'd21: state_name = "DEC_DECOMP";
5'd22: state_name = "DEC_SDEC";
5'd23: state_name = "DEC_NTT";
5'd24: state_name = "DEC_W";
5'd25: state_name = "DEC_MENC";
5'd26: state_name = "DEC_G";
5'd27: state_name = "DEC_J";
5'd28: state_name = "DEC_CMP";
5'd31: state_name = "DONE";
default: state_name = "UNKNOWN";
endcase
end
endfunction
task reset_timing;
begin
for (si = 0; si < 32; si = si + 1) state_cyc[si] = 0;
end
endtask
task print_timing;
input [8*16-1:0] op_name;
input integer cycles;
integer runtime_ns;
begin
runtime_ns = cycles * CLK_PERIOD_NS;
$display("TIME K=%0d CASE=%0d OP=%0s cycles=%0d clk=20MHz period=%0dns runtime=%0d ns (%0d.%03d us, %0d.%03d ms)",
KP, casenum, op_name, cycles, CLK_PERIOD_NS, runtime_ns,
runtime_ns/1000, runtime_ns%1000,
runtime_ns/1000000, (runtime_ns%1000000)/1000);
$display("TIME_BREAKDOWN K=%0d CASE=%0d OP=%0s", KP, casenum, op_name);
for (si = 0; si < 32; si = si + 1)
if (state_cyc[si] != 0)
$display(" STATE %-12s cycles=%0d time_ns=%0d", state_name(si[4:0]), state_cyc[si], state_cyc[si]*CLK_PERIOD_NS);
end
endtask
// Dump a byte array as offset-prefixed hex, 32 bytes/line. // Dump a byte array as offset-prefixed hex, 32 bytes/line.
task dump_bytes(input [8*16-1:0] name, input integer n); task dump_bytes(input [8*16-1:0] name, input integer n);
integer a, b; integer a, b;
@@ -80,9 +146,12 @@ module tb_mlkem_kg_katK_xsim;
rst_n=0; repeat(4) @(posedge clk); rst_n=1; @(posedge clk); rst_n=0; repeat(4) @(posedge clk); rst_n=1; @(posedge clk);
start_i=1; @(posedge clk); start_i=0; start_i=1; @(posedge clk); start_i=0;
c=0; while(!done_o && c<2000000) begin @(posedge clk); c=c+1; end #1;
reset_timing;
c=0; while(!done_o && c<2000000) begin state_cyc[dut.st] = state_cyc[dut.st] + 1; @(posedge clk); c=c+1; end
if(!done_o) begin $display("FAIL K=%0d case %0d: timeout", KP, casenum); $finish; end if(!done_o) begin $display("FAIL K=%0d case %0d: timeout", KP, casenum); $finish; end
$display("=== ML-KEM K=%0d KAT case %0d: KeyGen done in %0d cyc ===", KP, casenum, c); $display("=== ML-KEM K=%0d KAT case %0d: KeyGen done in %0d cyc ===", KP, casenum, c);
print_timing("KeyGen", c);
errors = 0; errors = 0;
dbg_byte_sel_i = 1'b0; dbg_byte_sel_i = 1'b0;

View File

@@ -14,14 +14,16 @@
// xelab tb_mlkem_two_inst_xsim ; xsim // xelab tb_mlkem_two_inst_xsim ; xsim
`timescale 1ns/1ps `timescale 1ns/1ps
module tb_mlkem_two_inst_xsim; module tb_mlkem_two_inst_xsim;
localparam KP = 2; // ML-KEM-512 parameter KP = 2; // 2/3/4 = ML-KEM-512/768/1024
localparam EKB = 384*KP + 32; // 800 localparam DU = (KP == 4) ? 11 : 10;
localparam DKB = 768*KP + 96; // 1632 localparam DV = (KP == 4) ? 5 : 4;
localparam CTB = 32*(10*KP + 4); // 768 localparam EKB = 384*KP + 32;
localparam DKB = 768*KP + 96;
localparam CTB = 32*(DU*KP + DV);
localparam MLEN = 11; // "hello world" localparam MLEN = 11; // "hello world"
reg clk=0; reg clk=0;
always #5 clk = ~clk; always #25 clk = ~clk; // 20 MHz
// ---------------- Instance A: KeyGen + Encaps ---------------- // ---------------- Instance A: KeyGen + Encaps ----------------
reg a_rst_n=0, a_start=0; reg a_rst_n=0, a_start=0;
@@ -100,7 +102,7 @@ module tb_mlkem_two_inst_xsim;
msg_b[0]="h"; msg_b[1]="e"; msg_b[2]="l"; msg_b[3]="l"; msg_b[4]="o"; msg_b[0]="h"; msg_b[1]="e"; msg_b[2]="l"; msg_b[3]="l"; msg_b[4]="o";
msg_b[5]=" "; msg_b[6]="w"; msg_b[7]="o"; msg_b[8]="r"; msg_b[9]="l"; msg_b[10]="d"; msg_b[5]=" "; msg_b[6]="w"; msg_b[7]="o"; msg_b[8]="r"; msg_b[9]="l"; msg_b[10]="d";
$display("=== ML-KEM hello_world (TWO instances: genenc + dec, ML-KEM-512) ==="); $display("=== ML-KEM hello_world (TWO instances: genenc + dec, K=%0d) ===", KP);
$write("Original: \""); for(i=0;i<MLEN;i=i+1) $write("%c", msg_b[i]); $display("\"\n"); $write("Original: \""); for(i=0;i<MLEN;i=i+1) $write("%c", msg_b[i]); $display("\"\n");
a_rst_n=0; b_rst_n=0; repeat(4) @(posedge clk); a_rst_n=1; b_rst_n=1; @(posedge clk); a_rst_n=0; b_rst_n=0; repeat(4) @(posedge clk); a_rst_n=1; b_rst_n=1; @(posedge clk);

View File

@@ -13,10 +13,13 @@ xvlog -sv --relax -i . sync_rtl/sha3/sha3_top_shared.v
xvlog -sv --relax -i . sync_rtl/sample_ntt/sample_ntt_sync_shared.v xvlog -sv --relax -i . sync_rtl/sample_ntt/sample_ntt_sync_shared.v
xvlog -sv --relax -i . sync_rtl/sample_cbd/sample_cbd_sync_shared.v xvlog -sv --relax -i . sync_rtl/sample_cbd/sample_cbd_sync_shared.v
xvlog -sv --relax -i . sync_rtl/ntt/barrett_mul.v xvlog -sv --relax -i . sync_rtl/ntt/barrett_mul.v
xvlog -sv --relax -i . sync_rtl/ntt/barrett_mul_pipe.v
xvlog -sv --relax -i . sync_rtl/ntt/zeta_rom.v xvlog -sv --relax -i . sync_rtl/ntt/zeta_rom.v
xvlog -sv --relax -i . sync_rtl/ntt/butterfly_unit.v xvlog -sv --relax -i . sync_rtl/ntt/butterfly_unit.v
xvlog -sv --relax -i . sync_rtl/ntt/butterfly_unit_pipe.v
xvlog -sv --relax -i . sync_rtl/ntt/ntt_core.v xvlog -sv --relax -i . sync_rtl/ntt/ntt_core.v
xvlog -sv --relax -i . sync_rtl/poly_mul/basecase_mul.v xvlog -sv --relax -i . sync_rtl/poly_mul/basecase_mul.v
xvlog -sv --relax -i . sync_rtl/poly_mul/basecase_mul_pipe.v
xvlog -sv --relax -i . sync_rtl/poly_mul/poly_mul_zeta_rom.v xvlog -sv --relax -i . sync_rtl/poly_mul/poly_mul_zeta_rom.v
xvlog -sv --relax -i . sync_rtl/poly_mul/poly_mul_sync.v xvlog -sv --relax -i . sync_rtl/poly_mul/poly_mul_sync.v
xvlog -sv --relax -i . sync_rtl/storage/sd_bram.v xvlog -sv --relax -i . sync_rtl/storage/sd_bram.v

View File

@@ -22,7 +22,7 @@
`include "sync_rtl/common/defines.vh" `include "sync_rtl/common/defines.vh"
(* use_dsp = "no" *) (* use_dsp = "yes" *)
module mlkem_top #( module mlkem_top #(
parameter KMAX = 4 // storage sizing (worst case = ML-KEM-1024) parameter KMAX = 4 // storage sizing (worst case = ML-KEM-1024)
) ( ) (
@@ -194,6 +194,7 @@ module mlkem_top #(
wire [10:0] cin_rd_addr; wire [10:0] cin_rd_addr;
wire [7:0] cin_rd_data; wire [7:0] cin_rd_data;
reg [10:0] cin_rd_addr_r; // D5/D7 read address (tied 0 until then) reg [10:0] cin_rd_addr_r; // D5/D7 read address (tied 0 until then)
reg dj_active; // background Decaps J(z||c) owns c_in read port
sd_bram #(.W(8), .D(2048), .A(11)) u_c_in_bram ( sd_bram #(.W(8), .D(2048), .A(11)) u_c_in_bram (
.clk(clk), .clk(clk),
.rd_addr(cin_rd_addr), .rd_data(cin_rd_data), .rd_addr(cin_rd_addr), .rd_data(cin_rd_data),
@@ -203,7 +204,7 @@ module mlkem_top #(
// stage) so the registered BRAM read yields the byte 1 cycle later, matching // stage) so the registered BRAM read yields the byte 1 cycle later, matching
// the assemble/writeback pipeline. Otherwise the registered cin_rd_addr_r // the assemble/writeback pipeline. Otherwise the registered cin_rd_addr_r
// (D1 walker / idle) drives it. // (D1 walker / idle) drives it.
assign cin_rd_addr = (st == ST_DEC_J) ? dj_c_idx[10:0] : assign cin_rd_addr = dj_active ? dj_c_idx[10:0] :
(st == ST_DEC_CMP) ? cmp_idx : cin_rd_addr_r; (st == ST_DEC_CMP) ? cmp_idx : cin_rd_addr_r;
// ================================================================ // ================================================================
@@ -567,7 +568,7 @@ module mlkem_top #(
// ST_ENC_A (snt), ST_ENC_C (cbd). // ST_ENC_A (snt), ST_ENC_C (cbd).
wire sel_sha3 = (st == ST_G) || (st == ST_H) || wire sel_sha3 = (st == ST_G) || (st == ST_H) ||
(st == ST_ENC_H) || (st == ST_ENC_G) || (st == ST_ENC_H) || (st == ST_ENC_G) ||
(st == ST_DEC_G) || (st == ST_DEC_J); (st == ST_DEC_G) || dj_active;
wire sel_snt = (st == ST_A) || (st == ST_ENC_A); wire sel_snt = (st == ST_A) || (st == ST_ENC_A);
wire sel_cbd = (st == ST_C) || (st == ST_ENC_C); wire sel_cbd = (st == ST_C) || (st == ST_ENC_C);
@@ -597,14 +598,14 @@ module mlkem_top #(
// G/H/J share one sha3_top. Single-block (mb_en=0): KeyGen G (ST_G, mode 00), // G/H/J share one sha3_top. Single-block (mb_en=0): KeyGen G (ST_G, mode 00),
// Encaps G (ST_ENC_G, mode 11), Decaps D5 G (ST_DEC_G, mode 11). Multi-block // Encaps G (ST_ENC_G, mode 11), Decaps D5 G (ST_DEC_G, mode 11). Multi-block
// absorb (mb_en=1): H(ek) (ST_H/ST_ENC_H, SHA3-256 pad) and Decaps D5 J // absorb (mb_en=1): H(ek) (ST_H/ST_ENC_H, SHA3-256 pad) and Decaps D5 J
// (ST_DEC_J, SHAKE256 pad over z||c). mb_* inputs are muxed by phase. // (background dj_active, SHAKE256 pad over z||c). mb_* inputs are muxed by phase.
wire sha3_mb_en = (st == ST_H) || (st == ST_ENC_H) || (st == ST_DEC_J); wire sha3_mb_en = (st == ST_H) || (st == ST_ENC_H) || dj_active;
wire [1:0] sha3_mode = (st == ST_ENC_G || st == ST_DEC_G) ? 2'b11 : 2'b00; wire [1:0] sha3_mode = (st == ST_ENC_G || st == ST_DEC_G) ? 2'b11 : 2'b00;
// multi-block feed mux: J (ST_DEC_J) drives dj_*, else H drives h_*. // multi-block feed mux: background J drives dj_*, else H drives h_*.
wire [1087:0] mb_block_mux = (st == ST_DEC_J) ? dj_block_r : h_block_r; wire [1087:0] mb_block_mux = dj_active ? dj_block_r : h_block_r;
wire mb_valid_mux = (st == ST_DEC_J) ? dj_mbvalid : h_mbvalid; wire mb_valid_mux = dj_active ? dj_mbvalid : h_mbvalid;
wire mb_last_mux = (st == ST_DEC_J) ? dj_mblast : h_mblast; wire mb_last_mux = dj_active ? dj_mblast : h_mblast;
wire mb_ack_mux = (st == ST_DEC_J) ? dj_ack : h_ack; wire mb_ack_mux = dj_active ? dj_ack : h_ack;
sha3_top_shared u_sha3 ( sha3_top_shared u_sha3 (
.clk(clk), .rst_n(rst_n), .clk(clk), .rst_n(rst_n),
.mode(sha3_mode), // G = SHA3-512 (only used when mb_en=0) .mode(sha3_mode), // G = SHA3-512 (only used when mb_en=0)
@@ -1009,7 +1010,7 @@ module mlkem_top #(
// (d=du), then c2 = 1 poly of v (d=dv). Per poly = 256 coeffs -> 32*d // (d=du), then c2 = 1 poly of v (d=dv). Per poly = 256 coeffs -> 32*d
// bytes (whole), so the bit buffer empties at each poly boundary. // bytes (whole), so the bit buffer empties at each poly boundary.
// micro-phase cp_ph: 0 present coeff addr; 1 feed comp_decomp (cd_valid); // micro-phase cp_ph: 0 present coeff addr; 1 feed comp_decomp (cd_valid);
// 2 wait pipe; 3 capture compressed + accumulate bits; 4..n drain bytes. // 2 drop valid pulse; 3 wait/capture cd_vo; 4..n drain bytes.
// ================================================================ // ================================================================
wire cd_active = (st == ST_ENC_C1) || (st == ST_ENC_C2); wire cd_active = (st == ST_ENC_C1) || (st == ST_ENC_C2);
reg [11:0] cd_coeff; // coeff presented to comp_decomp reg [11:0] cd_coeff; // coeff presented to comp_decomp
@@ -1145,12 +1146,12 @@ module mlkem_top #(
// D3: w = v' - INTT(sum_j s_hat[j] o u_hat[j]). Single output poly // D3: w = v' - INTT(sum_j s_hat[j] o u_hat[j]). Single output poly
// (u_row 0..0); MAC->INTT->SUB, then D4 encodes m'. // (u_row 0..0); MAC->INTT->SUB, then D4 encodes m'.
ST_DEC_W: if (u_row >= 3'd1) st_next = ST_DEC_MENC; ST_DEC_W: if (u_row >= 3'd1) st_next = ST_DEC_MENC;
ST_DEC_MENC: if (men_done) st_next = ST_DEC_G; ST_DEC_MENC: if (men_done && dj_done) st_next = ST_DEC_G;
// D5: (K',r') = G(m'||h) single-block, then K-bar = J(z||c) multi-block. // D5: (K',r') = G(m'||h) single-block. K-bar = J(z||c) runs in a
ST_DEC_G: if (sha3_vo) st_next = ST_DEC_J; // background SHAKE256 walker after D1, so the main FSM only waits
// D5 J done -> D6 re-encrypt: c' = K-PKE.Encrypt(ek_pke, m', r'). // for it at the D4->D5 boundary if it somehow has not finished.
// Reuse the entire Encaps pipeline (rho load -> A -> C -> ... -> C2). ST_DEC_G: if (sha3_vo) st_next = ST_ENC_LOAD;
// r' is in r_r (CBD seed), m' in m_r (V/mu), ek_pke in ek_bram. // Legacy state kept unused; background J normally skips it.
ST_DEC_J: if (dj_done) st_next = ST_ENC_LOAD; ST_DEC_J: if (dj_done) st_next = ST_ENC_LOAD;
ST_G: if (sha3_vo) st_next = ST_A; ST_G: if (sha3_vo) st_next = ST_A;
ST_A: if (a_pair >= kk_rt) st_next = ST_C; ST_A: if (a_pair >= kk_rt) st_next = ST_C;
@@ -1303,6 +1304,7 @@ module mlkem_top #(
cmp_neq <= 1'b0; cmp_neq <= 1'b0;
cmp_done <= 1'b0; cmp_done <= 1'b0;
dec_reject <= 1'b0; dec_reject <= 1'b0;
dj_active <= 1'b0;
dj_blk <= 4'd0; dj_blk <= 4'd0;
dj_byte <= 8'd0; dj_byte <= 8'd0;
dj_phase <= 2'd0; dj_phase <= 2'd0;
@@ -1349,6 +1351,8 @@ module mlkem_top #(
end else if (op_i == 2'd2) begin end else if (op_i == 2'd2) begin
// Decaps: dk_pke/ek_pke already in BRAM; z/H(ek) captured into // Decaps: dk_pke/ek_pke already in BRAM; z/H(ek) captured into
// z_r/hek_r during load (below). Nothing else to arm in D0. // z_r/hek_r during load (below). Nothing else to arm in D0.
dj_active <= 1'b0;
dj_done <= 1'b0;
end else begin end else begin
sha3_valid <= 1'b1; sha3_valid <= 1'b1;
sha3_ack <= 1'b1; sha3_ack <= 1'b1;
@@ -1701,8 +1705,8 @@ module mlkem_top #(
// ---- ST_ENC_C1/C2: Compress_d -> byteEncode_d -> ct region ---- // ---- ST_ENC_C1/C2: Compress_d -> byteEncode_d -> ct region ----
// C1 (E5): Compress_du(u[0..K-1]) from bank_se -> ct[0..c1_bytes). // C1 (E5): Compress_du(u[0..K-1]) from bank_se -> ct[0..c1_bytes).
// C2 (E7): Compress_dv(v) from bank_t[UPSUM] -> ct[c1_bytes..ct_bytes). // C2 (E7): Compress_dv(v) from bank_t[UPSUM] -> ct[c1_bytes..ct_bytes).
// Per coeff, 5-phase micro-sequence (read-ahead 1 cyc bram + 1 cyc // Per coeff, 5-phase micro-sequence (read-ahead 1 cyc bram plus
// comp_decomp pipe), then a drain sub-phase emitting whole bytes: // variable-latency comp_decomp), then a drain sub-phase emitting whole bytes:
// ph0: present coeff addr (cp_se_full / cp_bt_full by state). // ph0: present coeff addr (cp_se_full / cp_bt_full by state).
// ph1: coeff arrives (cp_coeff_src) -> latch into cd_coeff, pulse cd_valid. // ph1: coeff arrives (cp_coeff_src) -> latch into cd_coeff, pulse cd_valid.
// ph2: drop cd_valid (1-cyc pulse); comp_decomp captures. // ph2: drop cd_valid (1-cyc pulse); comp_decomp captures.
@@ -1723,10 +1727,12 @@ module mlkem_top #(
cp_ph <= 3'd3; cp_ph <= 3'd3;
end end
3'd3: begin 3'd3: begin
// cd_out valid (cd_vo): append cp_d bits LSB-first at bit cp_nbits if (cd_vo) begin
cp_buf <= cp_buf | (({13'd0, cd_out} & ((25'd1 << cp_d) - 25'd1)) << cp_nbits); // Append compressed low cp_d bits LSB-first at bit cp_nbits.
cp_nbits <= cp_nbits + {1'b0, cp_d}; cp_buf <= cp_buf | (({13'd0, cd_out} & ((25'd1 << cp_d) - 25'd1)) << cp_nbits);
cp_ph <= 3'd4; cp_nbits <= cp_nbits + {1'b0, cp_d};
cp_ph <= 3'd4;
end
end end
default: begin // 3'd4: drain whole bytes default: begin // 3'd4: drain whole bytes
if (cp_nbits >= 6'd8) begin if (cp_nbits >= 6'd8) begin
@@ -1991,9 +1997,11 @@ module mlkem_top #(
sha3_ack <= 1'b0; sha3_ack <= 1'b0;
end end
// Arm D5 J(z||c) when G completes (ST_DEC_G -> ST_DEC_J): assemble the // Arm D5 J(z||c) as soon as D1 has stopped reading c_in_bram. It
// first 136-byte block. dj_ack high to consume the final digest. // then runs in the background during D2/D3/D4 using the otherwise
if (st == ST_DEC_G && st_next == ST_DEC_J) begin // idle SHAKE256 path and c_in read port.
if (st == ST_DEC_DECOMP && st_next == ST_DEC_SDEC) begin
dj_active <= 1'b1;
dj_blk <= 4'd0; dj_blk <= 4'd0;
dj_byte <= 8'd0; dj_byte <= 8'd0;
dj_phase <= 2'd0; dj_phase <= 2'd0;
@@ -2005,14 +2013,14 @@ module mlkem_top #(
cin_rd_addr_r <= 11'd0; // present c byte 0 (g=32 -> c_idx 0) cin_rd_addr_r <= 11'd0; // present c byte 0 (g=32 -> c_idx 0)
end end
// ---- ST_DEC_J: K-bar = J(z||c) multi-block SHAKE256 ---- // ---- Background Decaps J: K-bar = J(z||c) multi-block SHAKE256 ----
// Mirror of the H(ek) multi-block machine. Byte source by global g: // Mirror of the H(ek) multi-block machine. Byte source by global g:
// g < 32 -> z_r byte g // g < 32 -> z_r byte g
// 32 <= g < msglen -> c_in_bram byte (g-32), registered read // 32 <= g < msglen -> c_in_bram byte (g-32), registered read
// else -> SHAKE256 pad constant // else -> SHAKE256 pad constant
// c_in_bram read is registered: present c_idx for dj_byte this cycle, // c_in_bram read is registered: present c_idx for dj_byte this cycle,
// write back the byte that arrived for the addr presented last cycle. // write back the byte that arrived for the addr presented last cycle.
if (st == ST_DEC_J && !dj_done) begin if (dj_active && !dj_done) begin
case (dj_phase) case (dj_phase)
2'd0: begin 2'd0: begin
// writeback the byte read for the previous address // writeback the byte read for the previous address
@@ -2032,7 +2040,7 @@ module mlkem_top #(
dj_wb_zidx <= dj_g[4:0]; dj_wb_zidx <= dj_g[4:0];
dj_wb_pad <= dj_padconst(dj_g); dj_wb_pad <= dj_padconst(dj_g);
// c_in addr is presented combinationally (cin_rd_addr // c_in addr is presented combinationally (cin_rd_addr
// mux uses dj_c_idx during ST_DEC_J); data lands next // mux uses dj_c_idx while dj_active); data lands next
// cycle, matching this byte's writeback. // cycle, matching this byte's writeback.
dj_byte <= dj_byte + 8'd1; dj_byte <= dj_byte + 8'd1;
end else begin end else begin
@@ -2054,6 +2062,7 @@ module mlkem_top #(
if (sha3_vo) begin if (sha3_vo) begin
kbar_r <= sha3_hash[255:0]; kbar_r <= sha3_hash[255:0];
dj_done <= 1'b1; dj_done <= 1'b1;
dj_active <= 1'b0;
dj_phase<= 2'd3; dj_phase<= 2'd3;
end else if (h_mbready) begin end else if (h_mbready) begin
dj_blk <= dj_blk + 4'd1; dj_blk <= dj_blk + 4'd1;
@@ -2093,7 +2102,7 @@ module mlkem_top #(
end end
if (st_next == ST_ENC_LOAD && if (st_next == ST_ENC_LOAD &&
(st == ST_ENC_G || st == ST_DEC_J)) begin (st == ST_ENC_G || st == ST_DEC_G || st == ST_DEC_J)) begin
rl_idx <= 6'd0; rl_idx <= 6'd0;
rl_widx <= 6'd0; rl_widx <= 6'd0;
rl_vld <= 1'b0; rl_vld <= 1'b0;

42
synth_timing.tcl Normal file
View File

@@ -0,0 +1,42 @@
set PROJECT_DIR [file normalize [file dirname [info script]]]
set REPORT_DIR ${PROJECT_DIR}/reports
file mkdir ${REPORT_DIR}
set PART xc7a200tfbg676-1
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_shared.v
read_verilog -sv ${PROJECT_DIR}/sync_rtl/sample_ntt/sample_ntt_sync_shared.v
read_verilog -sv ${PROJECT_DIR}/sync_rtl/sample_cbd/sample_cbd_sync_shared.v
read_verilog -sv ${PROJECT_DIR}/sync_rtl/ntt/barrett_mul.v
read_verilog -sv ${PROJECT_DIR}/sync_rtl/ntt/barrett_mul_pipe.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/butterfly_unit_pipe.v
read_verilog -sv ${PROJECT_DIR}/sync_rtl/ntt/ntt_core.v
read_verilog -sv ${PROJECT_DIR}/sync_rtl/poly_mul/basecase_mul.v
read_verilog -sv ${PROJECT_DIR}/sync_rtl/poly_mul/basecase_mul_pipe.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
read_verilog -sv ${PROJECT_DIR}/sync_rtl/storage/sd_bram.v
read_verilog -sv ${PROJECT_DIR}/sync_rtl/common/pipeline_reg.v
read_verilog -sv ${PROJECT_DIR}/sync_rtl/comp_decomp/comp_decomp_sync.v
read_verilog -sv ${PROJECT_DIR}/sync_rtl/top/mlkem_top.v
synth_design -top mlkem_top -part ${PART} -flatten_hierarchy rebuilt
create_clock -name sysclk -period 20.000 [get_ports clk]
report_timing_summary -file ${REPORT_DIR}/timing_synth.rpt
report_timing -max_paths 10 -sort_by group -file ${REPORT_DIR}/timing_synth_worst.rpt
report_utilization -file ${REPORT_DIR}/util_synth.rpt
write_checkpoint -force ${REPORT_DIR}/mlkem_top_synth.dcp
opt_design
place_design
route_design
report_timing_summary -file ${REPORT_DIR}/timing_impl.rpt
report_timing -max_paths 10 -sort_by group -file ${REPORT_DIR}/timing_impl_worst.rpt
report_utilization -file ${REPORT_DIR}/util_impl.rpt
write_checkpoint -force ${REPORT_DIR}/mlkem_top_impl.dcp