Compare commits
10 Commits
717a9929b6
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| a4261ec3cc | |||
| bfbfc2ef72 | |||
| 7a7ccacb47 | |||
| a0bd07bdf8 | |||
| ce998bb49a | |||
| 372a90e601 | |||
| 8c3f4317f5 | |||
| 2fb1cd67e3 | |||
| b7eb59fb5b | |||
| faf15ee113 |
@@ -10,7 +10,7 @@
|
||||
| **密钥封装** Encaps | `1` | 算法 17 | `ek`, 消息 `m` | 共享密钥 `K`, 密文 `c` |
|
||||
| **密钥解封装** Decaps | `2` | 算法 18 | `dk`, 密文 `c` | 共享密钥 `K`(含隐式拒绝) |
|
||||
|
||||
模块运行于 **100 MHz**(10 ns 周期),低电平异步复位。
|
||||
模块运行于 **25 MHz**(40 ns 周期),低电平异步复位。
|
||||
|
||||
---
|
||||
|
||||
|
||||
@@ -34,12 +34,15 @@ read_verilog -sv ${PROJECT_DIR}/sync_rtl/sample_cbd/sample_cbd_sync_shared.v
|
||||
|
||||
# ── NTT ──
|
||||
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
|
||||
|
||||
|
||||
326
docs/design_report.md
Normal file
326
docs/design_report.md
Normal 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→ek,Decaps→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` | SampleNTT(SHAKE-128 拒绝采样)生成 Â |
|
||||
| `u_cbd` | `sample_cbd_sync_shared` | CBD_η 中心二项分布采样(s/e/y/e1/e2) |
|
||||
| `u_ntt` | `ntt_core` | 前向 NTT / 逆向 NTT(mode 选择,×3303 尾乘) |
|
||||
| `u_pmul` | `poly_mul_sync` | NTT 域逐点乘(basecase,含 Barrett 模乘) |
|
||||
| `u_comp` | `comp_decomp_sync` | Compress_d / Decompress_d(Barrett 除法约简) |
|
||||
|
||||
模加、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(算法 18,FO 变换 + 隐式拒绝)**
|
||||
```
|
||||
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 模运算策略
|
||||
|
||||
所有模 q(q=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 按 E1–E7、Decaps 按 D0–D7 逐级验证中间多项式
|
||||
/哈希/编码,经调试抽头逐字节回读比对。
|
||||
- **端到端**: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 | E1–E7 分阶段 + `ss`/`c` |
|
||||
| `tb_mlkem_dec_katK_xsim.v` | Decaps | D0–D7 分阶段 + 接受/拒绝双路径 |
|
||||
| `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 E1–E7 (7228beb…) → Decaps D0–D7 (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 | 5(RAMB36×2 + RAMB18×6) | 365 | 1.4 % |
|
||||
| **DSP48** | **0** | 740 | **0 %** |
|
||||
|
||||
面积占用低,DSP 完全未用(符合设计约束),器件资源充裕。
|
||||
|
||||
### 5.2 性能(周期数,实测)
|
||||
|
||||
| 操作 | 周期数(K=2,ML-KEM-512) |
|
||||
|:---|:---:|
|
||||
| KeyGen | ≈ 22 904 |
|
||||
| Encaps | ≈ 32 493 |
|
||||
| Decaps | ≈ 50 819 |
|
||||
|
||||
K=3/4 因模秩增大按比例上升。Decaps 最长,因其包含完整重加密(≈ 一次 Encaps)。
|
||||
|
||||
### 5.3 时序现状与瓶颈
|
||||
|
||||
当前对 **50 MHz(20 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
229
docs/mlkem_top_rtl.svg
Normal 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_t;ntt_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
101
docs/timing_analysis.md
Normal 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 上约 ~10–12 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 拉成 4–5 级;
|
||||
- 或对乘法做 **基-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
BIN
reports/mlkem_top_synth.dcp
Normal file
Binary file not shown.
652
reports/timing_synth.rpt
Normal file
652
reports/timing_synth.rpt
Normal 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
|
||||
|
||||
|
||||
|
||||
3896
reports/timing_synth_worst.rpt
Normal file
3896
reports/timing_synth_worst.rpt
Normal file
File diff suppressed because it is too large
Load Diff
192
reports/util_synth.rpt
Normal file
192
reports/util_synth.rpt
Normal 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 |
|
||||
+----------+------+
|
||||
|
||||
|
||||
112
run_tb.sh
112
run_tb.sh
@@ -12,8 +12,8 @@
|
||||
# ./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 2 0 # Decaps K=2, only CASE=0
|
||||
# ./run_tb.sh hello # hello_world end-to-end (single instance)
|
||||
# ./run_tb.sh hello two # hello_world end-to-end (two instances)
|
||||
# ./run_tb.sh hello [K] # hello_world end-to-end (single instance; K=2/3/4)
|
||||
# ./run_tb.sh hello two [K] # hello_world end-to-end (two instances; K=2/3/4)
|
||||
# ./run_tb.sh --list
|
||||
#
|
||||
# 'top' (KeyGen), 'enc' (Encaps) and 'dec' (Decaps) share the same RTL datapath;
|
||||
@@ -41,7 +41,7 @@ if [ "$1" = "--list" ]; then
|
||||
done
|
||||
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 " 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
|
||||
fi
|
||||
|
||||
@@ -120,6 +120,84 @@ execute_tcl() {
|
||||
# each <spec> = "snapshot:K:CASE". Launches all specs in parallel, waits,
|
||||
# then prints an ordered summary and returns nonzero if any job failed.
|
||||
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() {
|
||||
local mtag="$1" pgrep="$2" pmatch="$3"; shift 3
|
||||
local specs=("$@")
|
||||
@@ -146,12 +224,13 @@ run_xsim_jobs() {
|
||||
wait
|
||||
|
||||
# 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
|
||||
IFS=: read -r snap k c <<< "$s"
|
||||
wd="$PAR_BASE/${mtag}_k${k}_c${c}"
|
||||
log="/tmp/run_tb_${mtag}_k${k}_c${c}.log"
|
||||
cp -f "$wd/run.out" "$log" 2>/dev/null
|
||||
timing_logs+=("$log")
|
||||
pf=$(grep -oE "$pgrep" "$log" | tail -1)
|
||||
nf=$(grep -c 'cannot be opened' "$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
|
||||
fi
|
||||
done
|
||||
print_timing_summary "${timing_logs[@]}"
|
||||
rm -rf "$PAR_BASE"
|
||||
return $fail
|
||||
}
|
||||
@@ -286,12 +366,24 @@ fi
|
||||
|
||||
# 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
|
||||
# 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() {
|
||||
local tcl_file="$1" variant="$2"
|
||||
local tcl_file="$1" arg1="$2" arg2="$3"
|
||||
set +e
|
||||
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
|
||||
if [ "$variant" = "two" ]; then
|
||||
tb=tb_mlkem_two_inst_xsim; snap=mlkem_two
|
||||
@@ -308,14 +400,14 @@ run_hello_selected() {
|
||||
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; }
|
||||
|
||||
echo " xelab $tb -s $snap --timescale 1ns/1ps"
|
||||
xelab "$tb" -s "$snap" --timescale 1ns/1ps || { echo "ELAB FAILED"; return 1; }
|
||||
echo " xelab $tb -generic_top KP=$ksel -s ${snap}_k${ksel} --timescale 1ns/1ps"
|
||||
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
|
||||
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 $?
|
||||
fi
|
||||
|
||||
|
||||
@@ -1,22 +1,17 @@
|
||||
// comp_decomp_sync.v - ML-KEM coefficient compression/decompression
|
||||
//
|
||||
// Streaming: one coefficient per cycle through pipeline_reg.
|
||||
// mode=0: compress - round((2^d * x) / Q) mod 2^d
|
||||
// mode=1: decompress - round((Q * x) / 2^d) mod Q
|
||||
// Streaming valid/ready interface. The arithmetic is fixed-latency pipelined
|
||||
// so compression does not infer a long combinational divider/reducer.
|
||||
// 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).
|
||||
// Integer arithmetic:
|
||||
// compress: (x * 2^d + Q/2) / Q, lower d bits as result
|
||||
// decompress: (x * Q + 2^(d-1)) / 2^d, result mod 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).
|
||||
// decompress: (x * Q + 2^(d-1)) / 2^d, result (always < Q)
|
||||
|
||||
`include "sync_rtl/common/defines.vh"
|
||||
|
||||
(* use_dsp = "no" *)
|
||||
(* use_dsp = "yes" *)
|
||||
module comp_decomp_sync (
|
||||
input clk,
|
||||
input rst_n,
|
||||
@@ -30,67 +25,171 @@ module comp_decomp_sync (
|
||||
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
|
||||
wire [23:0] product = mode ? {12'b0, coeff_in} * {12'b0, 12'(`Q)}
|
||||
: {12'b0, coeff_in} * {12'b0, two_pow_d};
|
||||
reg [11:0] data_r;
|
||||
reg valid_r;
|
||||
|
||||
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;
|
||||
|
||||
// ===========================================================
|
||||
// Barrett: floor(x / Q) ≈ (x * 5039) >> 24
|
||||
// M = floor(2^24 / Q) = 5039
|
||||
// M*Q = 16,774,831 < 2^24 → floor(x/Q) may be 1 too low
|
||||
// Correction: remainder = x - q_approx*Q; if rem >= Q, q→q+1
|
||||
// Q*5039 fails at: k*2385 >= 2^24 → k >= 7033; max k ≈ 2047 → safe
|
||||
// ===========================================================
|
||||
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_s2;
|
||||
reg [4:0] d_s2;
|
||||
reg [23:0] comp_dividend_s2;
|
||||
reg [36:0] comp_qprod_est_s2;
|
||||
reg [23:0] decomp_rounded_s2;
|
||||
reg valid_s2;
|
||||
|
||||
// ===========================================================
|
||||
// Decompress: dividend >> d (free), then % Q via Barrett
|
||||
// ===========================================================
|
||||
wire [23:0] dec_sh = dividend >> d;
|
||||
wire [36:0] dm_prod = dec_sh * 13'd5039;
|
||||
wire [11:0] dm_qe = dm_prod[35:24];
|
||||
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];
|
||||
reg mode_s3;
|
||||
reg [4:0] d_s3;
|
||||
reg [23:0] comp_dividend_s3;
|
||||
reg [12:0] comp_q_est_s3;
|
||||
reg [11:0] decomp_result_s3;
|
||||
reg valid_s3;
|
||||
|
||||
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 (
|
||||
.clk (clk),
|
||||
.rst_n (rst_n),
|
||||
.data_i (mod_result),
|
||||
.valid_i(valid_i),
|
||||
.ready_o(ready_o),
|
||||
.data_o (coeff_out),
|
||||
.valid_o(valid_o),
|
||||
.ready_i(ready_i)
|
||||
);
|
||||
reg mode_s5;
|
||||
reg [4:0] d_s5;
|
||||
reg [12:0] comp_q_est_s5;
|
||||
reg [24:0] comp_rem_s5;
|
||||
reg [11:0] decomp_result_s5;
|
||||
reg valid_s5;
|
||||
|
||||
wire fire_i = valid_i && ready_o;
|
||||
|
||||
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
|
||||
|
||||
@@ -172,7 +172,7 @@ module tb_ntt_core_xsim;
|
||||
integer got_done;
|
||||
|
||||
// Extract mode and coefficients from vector memory
|
||||
vec_mode = vector_mem[idx][3075];
|
||||
vec_mode = |vector_mem[idx][3075:3072];
|
||||
|
||||
// Extract 256 input coefficients
|
||||
// coeff[0] at bits [3074:3063], coeff[255] at bits [11:0]
|
||||
|
||||
@@ -30,12 +30,14 @@ puts "=== Compiling RTL sources ==="
|
||||
|
||||
# Barrett modular multiplier (combinational)
|
||||
xvlog -sv ${SRC_DIR}/barrett_mul.v
|
||||
xvlog -sv ${SRC_DIR}/barrett_mul_pipe.v
|
||||
|
||||
# Zeta ROM (combinational)
|
||||
xvlog -sv ${SRC_DIR}/zeta_rom.v
|
||||
|
||||
# Butterfly unit (combinational, instantiates barrett_mul)
|
||||
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)
|
||||
xvlog -sv ${SRC_DIR}/ntt_core.v
|
||||
|
||||
72
sync_rtl/ntt/barrett_mul_pipe.v
Normal file
72
sync_rtl/ntt/barrett_mul_pipe.v
Normal 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
|
||||
74
sync_rtl/ntt/butterfly_unit_pipe.v
Normal file
74
sync_rtl/ntt/butterfly_unit_pipe.v
Normal 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
|
||||
@@ -1,8 +1,12 @@
|
||||
// ntt_core.v - NTT core with individual coefficient registers
|
||||
//
|
||||
// Uses 256 individual 12-bit registers and generate-based muxing
|
||||
// to avoid any part-select simulation issues.
|
||||
// 3-cycle butterfly: SetAddr -> Read -> Compute+Write
|
||||
// Uses 256 individual 12-bit registers and a deeply pipelined butterfly path.
|
||||
// The arithmetic hot path is split into:
|
||||
// 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 (
|
||||
input clk, rst_n,
|
||||
@@ -17,54 +21,98 @@ module ntt_core (
|
||||
);
|
||||
localparam N = 256, LAYERS = 7, DW = 12;
|
||||
|
||||
// Individual coefficient registers
|
||||
reg [DW-1:0] cr [0:N-1];
|
||||
integer ci;
|
||||
|
||||
// State machine
|
||||
localparam S_IDLE=3'd0, S_LOAD=3'd1, S_CMP_A=3'd2, S_CMP_B=3'd3,
|
||||
S_CMP_C=3'd4, S_OUTPUT=3'd5, S_DONE=3'd6;
|
||||
reg [2:0] state, next_state;
|
||||
localparam S_IDLE = 4'd0;
|
||||
localparam S_LOAD = 4'd1;
|
||||
localparam S_CMP_RUN = 4'd2;
|
||||
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 [6:0] zeta_idx;
|
||||
reg [2:0] layer;
|
||||
reg bf_done;
|
||||
reg mode_r;
|
||||
reg layer_issue_done;
|
||||
reg [4:0] bf_inflight;
|
||||
|
||||
// Pipeline registers
|
||||
reg [DW-1:0] r_a, r_b;
|
||||
reg [7:0] r_wa, r_wb;
|
||||
reg [7:0] wa_d0, wa_d1, wa_d2, wa_d3, wa_d4, wa_d5, wa_d6, wa_d7;
|
||||
reg [7:0] wb_d0, wb_d1, wb_d2, wb_d3, wb_d4, wb_d5, wb_d6, wb_d7;
|
||||
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;
|
||||
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;
|
||||
butterfly_unit u_bf (
|
||||
.a(r_a), .b(r_b), .zeta(zeta), .mode(mode),
|
||||
.a_out(bf_a_out), .b_out(bf_b_out));
|
||||
|
||||
// Output scaling
|
||||
wire [DW-1:0] coeff_scaled;
|
||||
barrett_mul u_scl (.a(cr[out_cnt]), .b(12'd3303), .product(coeff_scaled));
|
||||
assign coeff_out = mode ? coeff_scaled : cr[out_cnt];
|
||||
wire bf_valid;
|
||||
butterfly_unit_pipe u_bf (
|
||||
.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)
|
||||
);
|
||||
|
||||
wire [DW-1:0] scale_product;
|
||||
wire scale_valid_o;
|
||||
barrett_mul_pipe u_scl (
|
||||
.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 valid_o = (state == S_OUTPUT);
|
||||
assign coeff_out = coeff_out_r;
|
||||
assign valid_o = valid_o_r;
|
||||
assign done_o = (state == S_DONE);
|
||||
|
||||
always @* begin
|
||||
next_state = state;
|
||||
case (state)
|
||||
S_IDLE: if (valid_i) next_state = S_LOAD;
|
||||
S_LOAD: if (load_cnt >= 255 && valid_i) next_state = S_CMP_A;
|
||||
S_CMP_A: if (bf_done) next_state = S_OUTPUT; else next_state = S_CMP_B;
|
||||
S_CMP_B: if (bf_done) next_state = S_OUTPUT; else next_state = S_CMP_C;
|
||||
S_CMP_C: if (bf_done) next_state = S_OUTPUT; else next_state = S_CMP_A;
|
||||
S_OUTPUT:if (out_cnt >= 255 && ready_i) next_state = S_DONE;
|
||||
S_LOAD: if (load_cnt >= 8'd255 && valid_i) next_state = S_CMP_RUN;
|
||||
S_CMP_RUN: if (layer_issue_done) next_state = S_CMP_DRAIN;
|
||||
S_CMP_DRAIN: if (bf_inflight == 5'd0)
|
||||
next_state = (layer + 3'd1 >= LAYERS) ? S_OUT_PREP : S_CMP_RUN;
|
||||
S_OUT_PREP: next_state = mode_r ? S_OUT_SCALE : S_OUTPUT;
|
||||
S_OUTPUT: if (valid_o_r && ready_i && out_cnt >= 8'd255) next_state = S_DONE;
|
||||
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
|
||||
@@ -72,17 +120,71 @@ module ntt_core (
|
||||
|
||||
always @(posedge clk or negedge rst_n) begin
|
||||
if (!rst_n) begin
|
||||
state<=S_IDLE; load_cnt<=0; out_cnt<=0; j<=0; start<=0; layer_len<=0;
|
||||
zeta_idx<=0; layer<=0; bf_done<=0; r_a<=0; r_b<=0; r_wa<=0; r_wb<=0;
|
||||
for (ci=0; ci<N; ci=ci+1) cr[ci] <= 0;
|
||||
state <= S_IDLE;
|
||||
load_cnt <= 8'd0;
|
||||
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
|
||||
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
|
||||
cr[0] <= coeff_in;
|
||||
load_cnt<=1; out_cnt<=0; j<=0; start<=0; layer<=0; bf_done<=0;
|
||||
if (!mode) begin layer_len<=128; zeta_idx<=1; end
|
||||
else begin layer_len<=2; zeta_idx<=127; end
|
||||
load_cnt <= 8'd1;
|
||||
out_cnt <= 8'd0;
|
||||
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
|
||||
|
||||
if (state == S_LOAD && valid_i) begin
|
||||
@@ -90,42 +192,93 @@ module ntt_core (
|
||||
load_cnt <= load_cnt + 8'd1;
|
||||
end
|
||||
|
||||
// S_CMP_A: set read addresses (j, j+len)
|
||||
if (state == S_CMP_A) begin
|
||||
r_wa <= j;
|
||||
r_wb <= j + layer_len;
|
||||
end
|
||||
|
||||
// S_CMP_B: capture read data
|
||||
if (state == S_CMP_B) begin
|
||||
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;
|
||||
if (state == S_CMP_RUN && !layer_issue_done) begin
|
||||
if (issue_group_last) begin
|
||||
if (!mode_r) 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;
|
||||
if (issue_layer_last) begin
|
||||
layer_issue_done <= 1'b1;
|
||||
end else begin
|
||||
start <= start + layer_len + layer_len;
|
||||
j <= start + layer_len + layer_len;
|
||||
start <= next_group_start[7:0];
|
||||
j <= next_group_start[7:0];
|
||||
end
|
||||
end else begin
|
||||
j <= j + 8'd1;
|
||||
end
|
||||
end
|
||||
|
||||
if (state == S_OUTPUT && ready_i)
|
||||
out_cnt <= (out_cnt>=255) ? 0 : (out_cnt+8'd1);
|
||||
if (wb_valid_r) begin
|
||||
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
|
||||
|
||||
|
||||
@@ -38,8 +38,9 @@ xvlog -sv ${NTT_DIR}/barrett_mul.v
|
||||
# ================================================================
|
||||
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_pipe.v
|
||||
|
||||
# PolyMul zeta ROM
|
||||
xvlog -sv ${PM_DIR}/poly_mul_zeta_rom.v
|
||||
|
||||
144
sync_rtl/poly_mul/basecase_mul_pipe.v
Normal file
144
sync_rtl/poly_mul/basecase_mul_pipe.v
Normal 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
|
||||
@@ -4,8 +4,10 @@
|
||||
// 256-coefficient NTT-domain polynomials.
|
||||
//
|
||||
// Operation flow:
|
||||
// IDLE → LOAD (256× A+B pairs) → COMP_CALC (read+compute, 1 cycle)
|
||||
// → COMP_C0 (output c0) → COMP_C1 (output c1) → DONE → IDLE
|
||||
// IDLE -> LOAD (256x A+B pairs) -> RUN -> 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
|
||||
// (one pair per cycle) on coeff_a_in/coeff_b_in.
|
||||
@@ -22,7 +24,7 @@
|
||||
// valid_i - Input valid
|
||||
// ready_o - Ready to accept input (high in IDLE/LOAD)
|
||||
// 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
|
||||
|
||||
module poly_mul_sync (
|
||||
@@ -39,10 +41,7 @@ module poly_mul_sync (
|
||||
// State definitions
|
||||
localparam S_IDLE = 3'd0;
|
||||
localparam S_LOAD = 3'd1;
|
||||
localparam S_COMP_CALC = 3'd2;
|
||||
localparam S_COMP_C0 = 3'd3;
|
||||
localparam S_COMP_C1 = 3'd4;
|
||||
localparam S_DONE = 3'd5;
|
||||
localparam S_RUN = 3'd2;
|
||||
|
||||
reg [2:0] state, next_state;
|
||||
|
||||
@@ -52,14 +51,23 @@ module poly_mul_sync (
|
||||
|
||||
// Counters
|
||||
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;
|
||||
|
||||
// Combinational read signals for COMP_CALC
|
||||
wire [7:0] addr_even = {comp_k, 1'b0}; // comp_k * 2 (7+1 = 8 bits)
|
||||
wire [7:0] addr_odd = {comp_k, 1'b1}; // comp_k * 2 + 1
|
||||
// Combinational read signals for the next base-case request.
|
||||
wire [6:0] issue_k = issue_cnt[6:0];
|
||||
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_a1 = mem_A[addr_odd];
|
||||
wire [11:0] mem_b0 = mem_B[addr_even];
|
||||
@@ -68,41 +76,42 @@ module poly_mul_sync (
|
||||
// Zeta ROM
|
||||
wire [11:0] zeta;
|
||||
poly_mul_zeta_rom u_zeta (
|
||||
.addr (comp_k),
|
||||
.addr (issue_k),
|
||||
.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;
|
||||
basecase_mul u_bc (
|
||||
.a0 (mem_a0),
|
||||
.a1 (mem_a1),
|
||||
.b0 (mem_b0),
|
||||
.b1 (mem_b1),
|
||||
.zeta(zeta),
|
||||
wire bc_vo;
|
||||
basecase_mul_pipe u_bc (
|
||||
.clk (clk),
|
||||
.rst_n(rst_n),
|
||||
.valid_i(bc_valid_reg),
|
||||
.a0 (bc_a0_reg),
|
||||
.a1 (bc_a1_reg),
|
||||
.b0 (bc_b0_reg),
|
||||
.b1 (bc_b1_reg),
|
||||
.zeta(bc_zeta_reg),
|
||||
.c0 (bc_c0),
|
||||
.c1 (bc_c1)
|
||||
.c1 (bc_c1),
|
||||
.valid_o(bc_vo)
|
||||
);
|
||||
|
||||
// Output interface
|
||||
assign ready_o = (state == S_IDLE) || (state == S_LOAD);
|
||||
assign valid_o = (state == S_COMP_C0) || (state == S_COMP_C1);
|
||||
assign coeff_out = (state == S_COMP_C0) ? c0_reg : c1_reg;
|
||||
assign valid_o = out_valid_r;
|
||||
assign coeff_out = out_phase ? c1_reg : c0_reg;
|
||||
|
||||
// State transition logic (combinational)
|
||||
always @* begin
|
||||
next_state = state;
|
||||
case (state)
|
||||
S_IDLE: if (valid_i && ready_o) next_state = S_LOAD;
|
||||
S_LOAD: if (load_cnt >= 255 && valid_i && ready_o)
|
||||
next_state = S_COMP_CALC;
|
||||
S_COMP_CALC: next_state = S_COMP_C0;
|
||||
S_COMP_C0: if (valid_o && ready_i) next_state = S_COMP_C1;
|
||||
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;
|
||||
S_LOAD: if (load_cnt >= 8'd255 && valid_i && ready_o)
|
||||
next_state = S_RUN;
|
||||
S_RUN: if (valid_o && ready_i && out_cnt == 8'd255)
|
||||
next_state = S_IDLE;
|
||||
default: next_state = S_IDLE;
|
||||
endcase
|
||||
end
|
||||
@@ -113,7 +122,17 @@ module poly_mul_sync (
|
||||
if (!rst_n) begin
|
||||
state <= S_IDLE;
|
||||
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;
|
||||
c1_reg <= 12'd0;
|
||||
for (i = 0; i < 256; i = i + 1) begin
|
||||
@@ -122,6 +141,7 @@ module poly_mul_sync (
|
||||
end
|
||||
end else begin
|
||||
state <= next_state;
|
||||
bc_valid_reg <= 1'b0;
|
||||
|
||||
// ---- LOAD phase ----
|
||||
// First coefficient captured on IDLE → LOAD transition
|
||||
@@ -129,6 +149,11 @@ module poly_mul_sync (
|
||||
mem_A[0] <= coeff_a_in;
|
||||
mem_B[0] <= coeff_b_in;
|
||||
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
|
||||
|
||||
// Subsequent coefficients in LOAD state
|
||||
@@ -136,29 +161,65 @@ module poly_mul_sync (
|
||||
mem_A[load_cnt] <= coeff_a_in;
|
||||
mem_B[load_cnt] <= coeff_b_in;
|
||||
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
|
||||
|
||||
// ---- COMPUTE phase ----
|
||||
// COMP_CALC: capture basecase_mul results
|
||||
if (state == S_COMP_CALC) begin
|
||||
// RUN: cut issue_cnt -> memory mux -> basecase DSP with local input
|
||||
// regs, then launch requests every other cycle. The two-cycle issue
|
||||
// interval matches the c0/c1 output bandwidth, so no deep result FIFO
|
||||
// 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
|
||||
|
||||
// COMP_C0 → COMP_C1: c0 was consumed, increment comp_k
|
||||
if (state == S_COMP_C0 && valid_o && ready_i) 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
|
||||
// ---- final output consumed: reset counters before accepting a new op ----
|
||||
if (state == S_RUN && valid_o && ready_i && out_cnt == 8'd255) begin
|
||||
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
|
||||
|
||||
@@ -100,6 +100,16 @@ module sha3_top (
|
||||
reg mb_last_r; // captured last-block flag
|
||||
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.
|
||||
wire [1599:0] mb_xored;
|
||||
assign mb_xored = mb_state_r ^ {{(1600-1088){1'b0}}, mb_block_i};
|
||||
@@ -119,16 +129,6 @@ module sha3_top (
|
||||
endcase
|
||||
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.
|
||||
wire [1599:0] kc_state_i_mux;
|
||||
assign kc_state_i_mux = mb_en ? mb_xored : absorb_state;
|
||||
|
||||
@@ -13,6 +13,8 @@ module tb_mlkem_dec_katK_xsim;
|
||||
localparam EKB = 384*KP + 32; // ek_pke bytes within dk
|
||||
localparam DKPB = 384*KP; // dk_pke bytes
|
||||
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 [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_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] 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] ssn_b [0:31];
|
||||
integer c, i, j, errors, casenum;
|
||||
integer state_cyc [0:31];
|
||||
integer si;
|
||||
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
|
||||
if (!$value$plusargs("CASE=%d", casenum)) casenum = 0;
|
||||
$sformat(tag, "k%0d", KP);
|
||||
@@ -130,9 +196,12 @@ module tb_mlkem_dec_katK_xsim;
|
||||
end
|
||||
c_in_we = 1'b0; @(posedge clk);
|
||||
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
|
||||
$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
|
||||
repeat(2) @(posedge clk);
|
||||
end
|
||||
|
||||
@@ -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 DU = (KP==4) ? 11 : 10; // compression du
|
||||
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 [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_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] m_b [0:31];
|
||||
reg [7:0] ss_b [0:31];
|
||||
reg [7:0] ct_b [0:CTB-1];
|
||||
integer c, i, errors, casenum, j;
|
||||
integer state_cyc [0:31];
|
||||
integer si;
|
||||
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
|
||||
if (!$value$plusargs("CASE=%d", casenum)) casenum = 0;
|
||||
$sformat(tag, "k%0d", KP);
|
||||
@@ -81,9 +147,12 @@ module tb_mlkem_enc_katK_xsim;
|
||||
|
||||
// ---- run Encaps ----
|
||||
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
|
||||
$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(" r = "); for (j=0;j<32;j=j+1) $write("%02x", dbg_r_o[8*j +: 8]); $write("\n");
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
// 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):
|
||||
// 1. Alice: KeyGen(d=0x42..., z=0x77...) -> (ek 800B, dk 1632B)
|
||||
// 2. Bob: Encaps(ek, m=0xDE...) -> (shared_key, kem_ct 768B)
|
||||
// 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, dk)
|
||||
// 2. Bob: Encaps(ek, m=0xDE...) -> (shared_key, kem_ct)
|
||||
// 3. Bob: XOR-encrypt "hello world" with key -> encrypted
|
||||
// 4. Alice: Decaps(dk, kem_ct) -> recovered_key
|
||||
// 5. Alice: XOR-decrypt encrypted with key -> "hello world"
|
||||
@@ -17,10 +17,12 @@
|
||||
// xelab tb_mlkem_hello_world_xsim ; xsim
|
||||
`timescale 1ns/1ps
|
||||
module tb_mlkem_hello_world_xsim;
|
||||
localparam KP = 2; // ML-KEM-512
|
||||
localparam EKB = 384*KP + 32; // 800
|
||||
localparam DKB = 768*KP + 96; // 1632
|
||||
localparam CTB = 32*(10*KP + 4); // 768
|
||||
parameter KP = 2; // 2/3/4 = ML-KEM-512/768/1024
|
||||
localparam DU = (KP == 4) ? 11 : 10;
|
||||
localparam DV = (KP == 4) ? 5 : 4;
|
||||
localparam EKB = 384*KP + 32;
|
||||
localparam DKB = 768*KP + 96;
|
||||
localparam CTB = 32*(DU*KP + DV);
|
||||
localparam MLEN = 11; // "hello world"
|
||||
|
||||
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_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)
|
||||
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[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: \"");
|
||||
for (i = 0; i < MLEN; i = i + 1) $write("%c", msg_b[i]);
|
||||
$display("\"\n");
|
||||
@@ -189,7 +191,7 @@ module tb_mlkem_hello_world_xsim;
|
||||
if (dec_b[i] !== msg_b[i]) errors = errors + 1;
|
||||
|
||||
if (errors == 0)
|
||||
$display("Success: keys match, message recovered. (hardware ML-KEM-512)");
|
||||
$display("Success: keys match, message recovered. (hardware K=%0d)", KP);
|
||||
else
|
||||
$display("FAILURE: %0d mismatches", errors);
|
||||
$finish;
|
||||
|
||||
@@ -7,6 +7,8 @@ module tb_mlkem_kg_katK_xsim;
|
||||
parameter KP = 2;
|
||||
localparam EKB = 384*KP + 32;
|
||||
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 [2:0] k_i;
|
||||
@@ -33,7 +35,7 @@ module tb_mlkem_kg_katK_xsim;
|
||||
.dbg_r_o(), .dbg_hek_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] 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] dk_got [0:DKB-1]; // dk bytes read back from DUT
|
||||
integer c, i, errors, casenum, j;
|
||||
integer state_cyc [0:31];
|
||||
integer si;
|
||||
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.
|
||||
task dump_bytes(input [8*16-1:0] name, input integer n);
|
||||
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);
|
||||
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
|
||||
$display("=== ML-KEM K=%0d KAT case %0d: KeyGen done in %0d cyc ===", KP, casenum, c);
|
||||
print_timing("KeyGen", c);
|
||||
|
||||
errors = 0;
|
||||
dbg_byte_sel_i = 1'b0;
|
||||
|
||||
@@ -14,14 +14,16 @@
|
||||
// xelab tb_mlkem_two_inst_xsim ; xsim
|
||||
`timescale 1ns/1ps
|
||||
module tb_mlkem_two_inst_xsim;
|
||||
localparam KP = 2; // ML-KEM-512
|
||||
localparam EKB = 384*KP + 32; // 800
|
||||
localparam DKB = 768*KP + 96; // 1632
|
||||
localparam CTB = 32*(10*KP + 4); // 768
|
||||
parameter KP = 2; // 2/3/4 = ML-KEM-512/768/1024
|
||||
localparam DU = (KP == 4) ? 11 : 10;
|
||||
localparam DV = (KP == 4) ? 5 : 4;
|
||||
localparam EKB = 384*KP + 32;
|
||||
localparam DKB = 768*KP + 96;
|
||||
localparam CTB = 32*(DU*KP + DV);
|
||||
localparam MLEN = 11; // "hello world"
|
||||
|
||||
reg clk=0;
|
||||
always #5 clk = ~clk;
|
||||
always #25 clk = ~clk; // 20 MHz
|
||||
|
||||
// ---------------- Instance A: KeyGen + Encaps ----------------
|
||||
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[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");
|
||||
|
||||
a_rst_n=0; b_rst_n=0; repeat(4) @(posedge clk); a_rst_n=1; b_rst_n=1; @(posedge clk);
|
||||
|
||||
@@ -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_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_pipe.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_pipe.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_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_sync.v
|
||||
xvlog -sv --relax -i . sync_rtl/storage/sd_bram.v
|
||||
|
||||
@@ -22,7 +22,7 @@
|
||||
|
||||
`include "sync_rtl/common/defines.vh"
|
||||
|
||||
(* use_dsp = "no" *)
|
||||
(* use_dsp = "yes" *)
|
||||
module mlkem_top #(
|
||||
parameter KMAX = 4 // storage sizing (worst case = ML-KEM-1024)
|
||||
) (
|
||||
@@ -194,6 +194,7 @@ module mlkem_top #(
|
||||
wire [10:0] cin_rd_addr;
|
||||
wire [7:0] cin_rd_data;
|
||||
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 (
|
||||
.clk(clk),
|
||||
.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
|
||||
// the assemble/writeback pipeline. Otherwise the registered cin_rd_addr_r
|
||||
// (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;
|
||||
|
||||
// ================================================================
|
||||
@@ -567,7 +568,7 @@ module mlkem_top #(
|
||||
// ST_ENC_A (snt), ST_ENC_C (cbd).
|
||||
wire sel_sha3 = (st == ST_G) || (st == ST_H) ||
|
||||
(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_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),
|
||||
// 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
|
||||
// (ST_DEC_J, 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);
|
||||
// (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) || dj_active;
|
||||
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_*.
|
||||
wire [1087:0] mb_block_mux = (st == ST_DEC_J) ? dj_block_r : h_block_r;
|
||||
wire mb_valid_mux = (st == ST_DEC_J) ? dj_mbvalid : h_mbvalid;
|
||||
wire mb_last_mux = (st == ST_DEC_J) ? dj_mblast : h_mblast;
|
||||
wire mb_ack_mux = (st == ST_DEC_J) ? dj_ack : h_ack;
|
||||
// multi-block feed mux: background J drives dj_*, else H drives h_*.
|
||||
wire [1087:0] mb_block_mux = dj_active ? dj_block_r : h_block_r;
|
||||
wire mb_valid_mux = dj_active ? dj_mbvalid : h_mbvalid;
|
||||
wire mb_last_mux = dj_active ? dj_mblast : h_mblast;
|
||||
wire mb_ack_mux = dj_active ? dj_ack : h_ack;
|
||||
sha3_top_shared u_sha3 (
|
||||
.clk(clk), .rst_n(rst_n),
|
||||
.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
|
||||
// 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);
|
||||
// 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);
|
||||
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
|
||||
// (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_MENC: if (men_done) st_next = ST_DEC_G;
|
||||
// D5: (K',r') = G(m'||h) single-block, then K-bar = J(z||c) multi-block.
|
||||
ST_DEC_G: if (sha3_vo) st_next = ST_DEC_J;
|
||||
// D5 J done -> D6 re-encrypt: c' = K-PKE.Encrypt(ek_pke, m', r').
|
||||
// Reuse the entire Encaps pipeline (rho load -> A -> C -> ... -> C2).
|
||||
// r' is in r_r (CBD seed), m' in m_r (V/mu), ek_pke in ek_bram.
|
||||
ST_DEC_MENC: if (men_done && dj_done) st_next = ST_DEC_G;
|
||||
// D5: (K',r') = G(m'||h) single-block. K-bar = J(z||c) runs in a
|
||||
// background SHAKE256 walker after D1, so the main FSM only waits
|
||||
// for it at the D4->D5 boundary if it somehow has not finished.
|
||||
ST_DEC_G: if (sha3_vo) st_next = ST_ENC_LOAD;
|
||||
// Legacy state kept unused; background J normally skips it.
|
||||
ST_DEC_J: if (dj_done) st_next = ST_ENC_LOAD;
|
||||
ST_G: if (sha3_vo) st_next = ST_A;
|
||||
ST_A: if (a_pair >= kk_rt) st_next = ST_C;
|
||||
@@ -1303,6 +1304,7 @@ module mlkem_top #(
|
||||
cmp_neq <= 1'b0;
|
||||
cmp_done <= 1'b0;
|
||||
dec_reject <= 1'b0;
|
||||
dj_active <= 1'b0;
|
||||
dj_blk <= 4'd0;
|
||||
dj_byte <= 8'd0;
|
||||
dj_phase <= 2'd0;
|
||||
@@ -1349,6 +1351,8 @@ module mlkem_top #(
|
||||
end else if (op_i == 2'd2) begin
|
||||
// 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.
|
||||
dj_active <= 1'b0;
|
||||
dj_done <= 1'b0;
|
||||
end else begin
|
||||
sha3_valid <= 1'b1;
|
||||
sha3_ack <= 1'b1;
|
||||
@@ -1701,8 +1705,8 @@ module mlkem_top #(
|
||||
// ---- 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).
|
||||
// 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
|
||||
// comp_decomp pipe), then a drain sub-phase emitting whole bytes:
|
||||
// Per coeff, 5-phase micro-sequence (read-ahead 1 cyc bram plus
|
||||
// variable-latency comp_decomp), then a drain sub-phase emitting whole bytes:
|
||||
// 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.
|
||||
// ph2: drop cd_valid (1-cyc pulse); comp_decomp captures.
|
||||
@@ -1723,11 +1727,13 @@ module mlkem_top #(
|
||||
cp_ph <= 3'd3;
|
||||
end
|
||||
3'd3: begin
|
||||
// cd_out valid (cd_vo): append cp_d bits LSB-first at bit cp_nbits
|
||||
if (cd_vo) begin
|
||||
// Append compressed low cp_d bits LSB-first at bit cp_nbits.
|
||||
cp_buf <= cp_buf | (({13'd0, cd_out} & ((25'd1 << cp_d) - 25'd1)) << cp_nbits);
|
||||
cp_nbits <= cp_nbits + {1'b0, cp_d};
|
||||
cp_ph <= 3'd4;
|
||||
end
|
||||
end
|
||||
default: begin // 3'd4: drain whole bytes
|
||||
if (cp_nbits >= 6'd8) begin
|
||||
ct_we <= 1'b1;
|
||||
@@ -1991,9 +1997,11 @@ module mlkem_top #(
|
||||
sha3_ack <= 1'b0;
|
||||
end
|
||||
|
||||
// Arm D5 J(z||c) when G completes (ST_DEC_G -> ST_DEC_J): assemble the
|
||||
// first 136-byte block. dj_ack high to consume the final digest.
|
||||
if (st == ST_DEC_G && st_next == ST_DEC_J) begin
|
||||
// Arm D5 J(z||c) as soon as D1 has stopped reading c_in_bram. It
|
||||
// then runs in the background during D2/D3/D4 using the otherwise
|
||||
// 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_byte <= 8'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)
|
||||
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:
|
||||
// g < 32 -> z_r byte g
|
||||
// 32 <= g < msglen -> c_in_bram byte (g-32), registered read
|
||||
// else -> SHAKE256 pad constant
|
||||
// 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.
|
||||
if (st == ST_DEC_J && !dj_done) begin
|
||||
if (dj_active && !dj_done) begin
|
||||
case (dj_phase)
|
||||
2'd0: begin
|
||||
// writeback the byte read for the previous address
|
||||
@@ -2032,7 +2040,7 @@ module mlkem_top #(
|
||||
dj_wb_zidx <= dj_g[4:0];
|
||||
dj_wb_pad <= dj_padconst(dj_g);
|
||||
// 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.
|
||||
dj_byte <= dj_byte + 8'd1;
|
||||
end else begin
|
||||
@@ -2054,6 +2062,7 @@ module mlkem_top #(
|
||||
if (sha3_vo) begin
|
||||
kbar_r <= sha3_hash[255:0];
|
||||
dj_done <= 1'b1;
|
||||
dj_active <= 1'b0;
|
||||
dj_phase<= 2'd3;
|
||||
end else if (h_mbready) begin
|
||||
dj_blk <= dj_blk + 4'd1;
|
||||
@@ -2093,7 +2102,7 @@ module mlkem_top #(
|
||||
end
|
||||
|
||||
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_widx <= 6'd0;
|
||||
rl_vld <= 1'b0;
|
||||
|
||||
42
synth_timing.tcl
Normal file
42
synth_timing.tcl
Normal 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
|
||||
Reference in New Issue
Block a user