This repository has been archived on 2026-08-21. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
ciciec2026/doc/mlkem_axi_slave_regs.md

263 lines
9.0 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# ML-KEM AXI Slave 寄存器参考手册
## 概述
`mlkem_axi_slave` 是 ML-KEM 硬件加速器的 AXI4 Slave 总线接口。它封装 `mlkem_top` 核心,提供完整的寄存器映射用于软件控制 KeyGen / Encaps / Decaps 操作。
### 关键特性
- **总线**: AXI4 (支持 INCR burst read/write)
- **基址**: `0xbf400000`(LoongArch 虚地址)
- **时钟域**: sys_clk (50 MHz) → mlkem_clk (25 MHz),跨域通过 toggle 握手同步
- **支持参数**: k=2 (ML-KEM-512), k=3 (ML-KEM-768), k=4 (ML-KEM-1024)
- **中断**: `intr` 信号,当 done 标志锁存时电平有效高
---
## 寄存器映射
所有寄存器为 32-bit 对齐。偏移量相对于基址 `0xbf400000`。
| 偏移 | 名称 | 访问 | 复位值 | 描述 |
|------|------|------|--------|------|
| `0x000` | **CTRL** | W | `0x00000000` | 控制寄存器:写 bit0=1 启动操作 |
| `0x004` | **STATUS** | R/W1C | `0x00000000` | 状态寄存器 |
| `0x008` | **CONFIG** | RW | `0x00000004` | 配置寄存器:k 值和操作码 |
| `0x010`–`0x02C` | **D[0..7]** | RW | `0x00000000` | KeyGen 种子 d(256-bit,8字) |
| `0x030`–`0x04C` | **Z[0..7]** | RW | `0x00000000` | KeyGen 隐式拒绝种子 z(256-bit,8字) |
| `0x050`–`0x06C` | **MSG[0..7]** | RW | `0x00000000` | Encaps 消息 m(256-bit,8字) |
| `0x070`–`0x08C` | **SS[0..7]** | R | — | 共享密钥输出(256-bit,8字,只读) |
| `0x100` | **PRELOAD_SEL** | RW | `0x00000000` | 预载端口选择 |
| `0x104` | **PRELOAD_ADDR** | RW | `0x00000000` | 预载字节地址(写入后自动递增) |
| `0x108` | **PRELOAD_DATA** | W | — | 预载数据字节(写低 8-bit,自动 wait-state) |
| `0x10C` | **READBACK_SEL** | RW | `0x00000000` | 回读端口选择 |
| `0x110` | **READBACK_ADDR** | RW | `0x00000000` | 回读字节索引(无自动递增) |
| `0x114` | **READBACK_DATA** | R | — | 回读数据字节(低 8-bit 有效) |
---
## 寄存器详情
### CTRL (0x000) — 控制寄存器,只写
| Bits | 名称 | 描述 |
|------|------|------|
| `0` | **start** | 写 `1` 启动当前 CONFIG 中配置的操作。写 `0` 无效。 |
- 写 start 后,STATUS.busy 立即置位,done 清零。
- 操作完成前不可再次启动。
- CTRL 本身不可读(读返回 0)。
---
### STATUS (0x004) — 状态寄存器,读/写1清零
| Bits | 名称 | 访问 | 描述 |
|------|------|------|------|
| `0` | **busy** | R | 核心正在执行操作(KeyGen/Encaps/Decaps) |
| `1` | **done** | R/W1C | 操作已完成。写 `1` 清除(写 `0` 无效)。启动新操作时自动清除 |
| `2` | **preload_busy** | R | 预载数据传输正在进行中(跨域字节握手未完成) |
| `31:3` | — | — | 保留,读为 0 |
**注意**:
- `done` 是**锁存**标志:操作完成后保持为 1,直到软件写 1 清零或启动新操作。
- `busy` 和 `done` **不互斥**:在 done 置位的同一周期 busy 清零,两者可能同时为 1(短暂窗口)。
- `preload_busy` 为 1 时,写入 PRELOAD_DATA 会被 wait-state 直到传输完成。
---
### CONFIG (0x008) — 配置寄存器,读写
| Bits | 名称 | 复位值 | 描述 |
|------|------|--------|------|
| `2:0` | **k** | `3'b100` (4) | ML-KEM 参数:`2`=ML-KEM-512, `3`=ML-KEM-768, `4`=ML-KEM-1024 |
| `4` | — | `0` | 保留 |
| `5:4` | **op** | `2'b00` | 操作码:`0`=KeyGen, `1`=Encaps, `2`=Decaps |
| `31:6` | — | `0` | 保留 |
**约束**:
- 修改 CONFIG 应在启动操作之前完成。启动后修改可能导致未定义行为。
- k 必须为 2、3 或 4。
---
### D[0..7] (0x010–0x02C) — KeyGen 种子 d,读写
256-bit 种子 `d`,用于 KeyGen 操作。存储为 8 个 32-bit 字,小端序:
| 字偏移 | 地址 | 包含字节 |
|--------|------|---------|
| D[0] | `0x010` | d[3:0](字节 3 在 bit[31:24],字节 0 在 bit[7:0]) |
| D[1] | `0x014` | d[7:4] |
| ... | ... | ... |
| D[7] | `0x02C` | d[31:28] |
核心看到的 256-bit 总线为 `{D[7], D[6], ..., D[0]}`,其中 `d_i[7:0]` = D[0][7:0]。
支持 AXI wstrb 按字节写入。
---
### Z[0..7] (0x030–0x04C) — 隐式拒绝种子 z,读写
256-bit 种子 `z`,用于 KeyGen。布局与 D 相同。
---
### MSG[0..7] (0x050–0x06C) — Encaps 消息 m,读写
256-bit 消息 `m`,用于 Encaps。布局与 D 相同。
---
### SS[0..7] (0x070–0x08C) — 共享密钥,只读
256-bit 共享密钥输出。KeyGen 和 Encaps 操作完成后在此处提供结果。
| 字偏移 | 地址 | 内容 |
|--------|------|------|
| SS[0] | `0x070` | ss[31:0] |
| SS[1] | `0x074` | ss[63:32] |
| ... | ... | ... |
| SS[7] | `0x08C` | ss[255:224] |
---
### PRELOAD_SEL (0x100) — 预载端口选择,读写
| Bits | 名称 | 描述 |
|------|------|------|
| `1:0` | **sel** | 预载目标端口:`0`=ek, `1`=dk, `2`=c(密文) |
| `31:2` | — | 保留 |
设置后,随后写入 PRELOAD_DATA 的字节将流向所选端口。
---
### PRELOAD_ADDR (0x104) — 预载字节地址,读写
| Bits | 名称 | 描述 |
|------|------|------|
| `11:0` | **addr** | 预载端口内的字节偏移地址。每次 PRELOAD_DATA 写入成功后自动递增 |
| `31:12` | — | 保留 |
写入 PRELOAD_DATA 前必须先设置好 PRELOAD_SEL 和 PRELOAD_ADDR(通常 addr=0)。
---
### PRELOAD_DATA (0x108) — 预载数据字节,只写
| Bits | 名称 | 描述 |
|------|------|------|
| `7:0` | **byte** | 写入所选预载端口的字节数据 |
| `31:8` | — | 忽略 |
此寄存器**自动 wait-state**:写入后 AXI 总线暂停,直到跨域握手完成(字节被 mlkem 核心采样)。然后 PRELOAD_ADDR 自动递增 1。
**重要**: 每个字节需要一次 AXI 写事务。对于大数据量(ek=800B, dk=1632B),建议使用 INCR burst 写,burst 长度设为字节数。
---
### READBACK_SEL (0x10C) — 回读端口选择,读写
| Bits | 名称 | 描述 |
|------|------|------|
| `1:0` | **sel** | 回读源:`0`=ek, `1`=dk_pke, `2`=完整 dk, `3`=密文 |
| `31:2` | — | 保留 |
---
### READBACK_ADDR (0x110) — 回读字节索引,读写
| Bits | 名称 | 描述 |
|------|------|------|
| `11:0` | **addr** | 待读取字节在回读端口中的索引。**无自动递增** |
| `31:12` | — | 保留 |
每个字节的读取流程:写 READBACK_ADDR → 读 READBACK_DATA。
---
### READBACK_DATA (0x114) — 回读数据字节,只读
| Bits | 名称 | 描述 |
|------|------|------|
| `7:0` | **byte** | 所选回读端口中 READBACK_ADDR 索引处的字节 |
| `31:24` | — | 读为 0 |
---
## 操作流程
### 1. KeyGen: (d, z) → ek, dk
```
1. 写 CONFIG: op=0 (KeyGen), k=2/3/4
2. 写 D[0..7] ← 种子 d (256-bit)
3. 写 Z[0..7] ← 种子 z (256-bit)
4. 写 CTRL: bit0=1 → 启动
5. 轮询 STATUS.done == 1(或等待 intr)
6. 通过 READBACK 读取 ek 和 dk:
a. RDB_SEL=0, 逐个字节读 ADDR=0..799 → ek (800 B for k=2)
b. RDB_SEL=2, 逐个字节读 ADDR=0..1631 → dk (1632 B for k=2)
7. 读 SS[0..7] → 共享密钥(可选)
8. 写 STATUS: bit1=1 → 清除 done
```
### 2. Encaps: (ek, m) → (ss, ct)
```
1. 写 CONFIG: op=1 (Encaps), k=2/3/4
2. 通过 PRELOAD 预载 ek:
a. PRE_SEL=0, PRE_ADR=0
b. 逐个字节写 PRE_DAT (800 B for k=2)
3. 写 MSG[0..7] ← 消息 m (256-bit)
4. 写 CTRL: bit0=1 → 启动
5. 轮询 STATUS.done == 1
6. 读 SS[0..7] → 共享密钥
7. 通过 READBACK 读取 ct:
a. RDB_SEL=3, 逐个字节读 ADDR=0..767 → ct (768 B for k=2)
```
### 3. Decaps: (dk, ct) → ss
```
1. 写 CONFIG: op=2 (Decaps), k=2/3/4
2. 通过 PRELOAD 预载 dk 和 ct:
a. PRE_SEL=1, PRE_ADR=0, 逐个字节写 PRE_DAT (1632 B for k=2)
b. PRE_SEL=2, PRE_ADR=0, 逐个字节写 PRE_DAT (768 B for k=2)
3. 写 CTRL: bit0=1 → 启动
4. 轮询 STATUS.done == 1
5. 读 SS[0..7] → 共享密钥
```
---
## 数据尺寸参考
| 参数 | k=2 (ML-KEM-512) | k=3 (ML-KEM-768) | k=4 (ML-KEM-1024) |
|------|-------------------|-------------------|---------------------|
| ek 字节数 | 800 | 1184 | 1568 |
| dk_pke 字节数 | 768 | 1152 | 1536 |
| dk 总字节数 | 1632 | 2400 | 3168 |
| ct 字节数 | 768 | 1088 | 1568 |
---
## 中断
- `intr` 信号在 `STATUS.done == 1` 时保持高电平。
- 清除中断:写 `STATUS` 的 bit1=1(W1C)或启动新操作。
- `intr` 是电平触发(非脉冲),连接到 LoongArch 的 HWI 输入。
---
## 注意事项
1. **时钟域**: 核心运行在 sys_clk/2 (25 MHz)。PRELOAD_DATA 写入自动 wait-state,软件无需处理跨域延迟。
2. **启动时序**: 确保在写 CTRL.start 之前,config/seeds/preload 数据已全部写入且稳定。
3. **PRELOAD_ADDR 自动递增**: 连续写 PRELOAD_DATA 时地址自动递增,无需手动更新 PRELOAD_ADDR(除非切换端口)。
4. **READBACK_ADDR 无自动递增**: 每次读 READBACK_DATA 前必须手动写 READBACK_ADDR。
5. **done 标志**: 是锁存的,启动新操作时自动清除。连续执行多个操作时,每次启动前先确认 busy==0,然后清 done。
6. **STATUS 读取时机**: done 标志在操作完成的 mlkem_clk 周期后,需经 2 级同步器(~40ns)才能在 sys_clk 域看到。