docs: add competition summary and reorganize reports
- add 比赛总结 - rename AXI bus/module docs to 总线模块说明 - add README overview
This commit is contained in:
39
README.md
Normal file
39
README.md
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
# 2026年全国大学生集成电路创新创业大赛 - 龙芯中科杯
|
||||||
|
|
||||||
|
本仓库为 2026年全国大学生集成电路创新创业大赛(CICC) “龙芯中科”企业命题的参赛代码仓库。
|
||||||
|
|
||||||
|
## 团队信息
|
||||||
|
|
||||||
|
- 队伍编号:CICC1008439
|
||||||
|
- 队伍名称:碳基生物
|
||||||
|
- 参赛赛道:龙芯中科企业命题
|
||||||
|
|
||||||
|
## 项目简介
|
||||||
|
|
||||||
|
本项目基于龙芯指令集架构(Loongson Architecture),针对特定应用场景(如数字信号处理/嵌入式系统优化)设计了一套高性能的系统级芯片(SoC)或核心外设子系统。旨在充分发挥龙芯架构的优势,通过自定义硬件加速器和高效的总线管理提升系统整体效能。
|
||||||
|
|
||||||
|
## 已完成功能模块
|
||||||
|
|
||||||
|
- 外部中断控制器
|
||||||
|
- FFT 硬件加速器
|
||||||
|
- 8-channel mm2mm/mm2ep/ep2mm DMA 控制器
|
||||||
|
- 4x4矩阵加速器
|
||||||
|
- MLKEM算法加速器
|
||||||
|
|
||||||
|
## 目录结构
|
||||||
|
|
||||||
|
```
|
||||||
|
.
|
||||||
|
├── fpga/ # Vivado工程文件与Tcl自动化构建脚本
|
||||||
|
├── doc/ # 参赛技术文档、设计说明书及测试报告
|
||||||
|
├── rtl/ # 硬件源代码 (龙芯核、中断控制器、FFT、DMA等)
|
||||||
|
├── sdk/ # 嵌入式软件开发 (针对龙芯架构的C语言驱动与应用)
|
||||||
|
├── sim/ # 仿真验证环境 (Testbench与仿真脚本)
|
||||||
|
└── README.md # 项目总体说明与快速上手指南
|
||||||
|
```
|
||||||
|
|
||||||
|
## 参考文献
|
||||||
|
|
||||||
|
[1] nelsoncsc. verilaxi[EB/OL]. https://github.com/nelsoncsc/verilaxi, 2026-05-05.
|
||||||
|
|
||||||
|
[2] alexforencich. verilog-axi[EB/OL]. https://github.com/alexforencich/verilog-axi, 2026-05-05.
|
||||||
@@ -1,262 +0,0 @@
|
|||||||
# 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 域看到。
|
|
||||||
@@ -1,45 +0,0 @@
|
|||||||
# ML-KEM Test Results Summary
|
|
||||||
|
|
||||||
## Test Setup
|
|
||||||
|
|
||||||
- Platform: LoongArch bare-metal target
|
|
||||||
- Test mode: `STATIC_KAT_COUNT_0`
|
|
||||||
- Levels: `k=2,3,4`
|
|
||||||
- Repeat count: `100`
|
|
||||||
- ML-KEM software allocator: `MLK_CONFIG_CUSTOM_ALLOC_FREE`
|
|
||||||
- Verification sources: KAT count 0 expected values
|
|
||||||
|
|
||||||
## Correctness
|
|
||||||
|
|
||||||
All three paths passed in isolated testing:
|
|
||||||
|
|
||||||
- `SW_ONLY_FAILS=0`
|
|
||||||
- `CPU_ONLY_FAILS=0`
|
|
||||||
- `DMA_ONLY_FAILS=0`
|
|
||||||
|
|
||||||
Final result for the isolated diagnostic run:
|
|
||||||
|
|
||||||
- `MLKEM512_RESULT=PASS`
|
|
||||||
- `MLKEM768_RESULT=PASS`
|
|
||||||
- `MLKEM1024_RESULT=PASS`
|
|
||||||
- `MLKEM_RESULT=PASS`
|
|
||||||
|
|
||||||
## Performance
|
|
||||||
|
|
||||||
The following results are from isolated per-level runs. Each level was tested
|
|
||||||
for 100 iterations with software-only, CPU register path, and DMA path separated.
|
|
||||||
|
|
||||||
| Level | SW avg cycles | CPU avg cycles | DMA avg cycles | CPU vs SW | DMA vs SW | DMA vs CPU |
|
|
||||||
|---|---:|---:|---:|---:|---:|---:|
|
|
||||||
| k=2 / ML-KEM-512 | 2,587,784 | 352,684 | 185,671 | 7.34x | 13.94x | 1.90x |
|
|
||||||
| k=3 / ML-KEM-768 | 4,056,378 | 523,656 | 279,588 | 7.75x | 14.51x | 1.87x |
|
|
||||||
| k=4 / ML-KEM-1024 | 6,025,486 | 718,672 | 388,959 | 8.38x | 15.49x | 1.85x |
|
|
||||||
|
|
||||||
## Notes
|
|
||||||
|
|
||||||
- CPU/DMA hardware paths were stable across 100 iterations for all levels.
|
|
||||||
- Software, CPU register path, and DMA path all passed 100 iterations for k=2, k=3, and k=4.
|
|
||||||
- The DMA path is 13.94x to 15.49x faster than software across the tested levels.
|
|
||||||
- The CPU register path is 7.34x to 8.38x faster than software across the tested levels.
|
|
||||||
- The DMA path is about 1.85x to 1.90x faster than the CPU register path.
|
|
||||||
- The hardware advantage increases slightly as k grows, because the accelerated computation grows faster than the fixed software control overhead.
|
|
||||||
5
doc/比赛总结.md
Normal file
5
doc/比赛总结.md
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
这次比赛的结果并不理想——拿了省/分区奖,却没能在国赛的名单里看到自己的队伍,还是觉得非常遗憾。而这也可能是我最后一次作为本科生参加这种级别的大赛了,最后没能画上一个圆满的句号。
|
||||||
|
|
||||||
|
这一路,陪伴我最多的大概是深夜的屏幕。FFT、DMA、4x4矩阵、MLKEM……一行行 RTL 从指间敲出来,仿真跑到凌晨,上板调试翻车了再从头排查,Vivado 的编译进度条不知道看过多少遍。PPT 之外,文档、代码、报告,几乎全是我一个人在撑。本次比赛仍然没能体现团队协作,说不心酸是假的。但那些一个人 debug 到凌晨三点、代码跑通的瞬间,也只有我一个人懂得那份开心。
|
||||||
|
|
||||||
|
如果还有下次,我想挑战单人参赛,solo一场——输赢都自己扛,赢得坦荡,也输得明白。
|
||||||
Reference in New Issue
Block a user