docs: rebuild system report and test workflow
This commit is contained in:
9
doc/reports/system/.gitignore
vendored
Normal file
9
doc/reports/system/.gitignore
vendored
Normal file
@@ -0,0 +1,9 @@
|
||||
system_design_report.aux
|
||||
system_design_report.fdb_latexmk
|
||||
system_design_report.fls
|
||||
system_design_report.log
|
||||
system_design_report.out
|
||||
system_design_report.synctex.gz
|
||||
system_design_report.toc
|
||||
system_design_report.xdv
|
||||
tools/__pycache__/
|
||||
25
doc/reports/system/Makefile
Normal file
25
doc/reports/system/Makefile
Normal file
@@ -0,0 +1,25 @@
|
||||
.PHONY: all figures metrics pdf check clean
|
||||
|
||||
DOT_SOURCES := $(wildcard figures/*.dot)
|
||||
FIGURE_PDFS := $(DOT_SOURCES:.dot=.pdf)
|
||||
|
||||
all: pdf check
|
||||
|
||||
figures: $(FIGURE_PDFS)
|
||||
|
||||
figures/%.pdf: figures/%.dot
|
||||
dot -Tpdf -Gfontnames=svg $< -o $@
|
||||
|
||||
metrics:
|
||||
python3 tools/update_report_metrics.py
|
||||
|
||||
pdf: figures
|
||||
latexmk -xelatex -interaction=nonstopmode -halt-on-error system_design_report.tex
|
||||
|
||||
check: pdf
|
||||
@! rg -n 'undefined references|Citation .* undefined|Reference .* undefined|There were undefined references' system_design_report.log
|
||||
@pdfinfo system_design_report.pdf | rg '^Pages:|^File size:'
|
||||
|
||||
clean:
|
||||
latexmk -C system_design_report.tex
|
||||
rm -f $(FIGURE_PDFS)
|
||||
168
doc/reports/system/REPORT_RERUN_CHECKLIST.md
Normal file
168
doc/reports/system/REPORT_RERUN_CHECKLIST.md
Normal file
@@ -0,0 +1,168 @@
|
||||
# 系统报告复测清单
|
||||
|
||||
本清单用于为 `system_design_report.tex` 生成同一提交、可追溯的证据。不要把旧的
|
||||
`fpga/vivado_reports_nodsp/` 与新板级日志混合后宣称为同一版本结果。
|
||||
|
||||
## 0. 固化版本与环境
|
||||
|
||||
在仓库根目录执行并保存输出:
|
||||
|
||||
```bash
|
||||
git status --short
|
||||
git rev-parse HEAD
|
||||
vivado -version | head -n 3
|
||||
verilator --version
|
||||
```
|
||||
|
||||
建议先提交当前 RTL/约束/软件变更。若工作树不干净,最终报告的证据版本无法唯一复现。
|
||||
|
||||
## 1. 必跑:RTL 静态检查
|
||||
|
||||
```bash
|
||||
cd doc/reports/system
|
||||
./tools/run_report_tests.sh lint
|
||||
```
|
||||
|
||||
通过标准:命令退出码为 0,`evidence/lint.log` 中无 `%Error`。此项只证明静态可分析,
|
||||
不证明 AXI、CDC 或算法功能正确。
|
||||
|
||||
## 2. 必跑:SoC 功能回归
|
||||
|
||||
仅编译指定软件、不运行仿真:
|
||||
|
||||
```bash
|
||||
cd doc/reports/system
|
||||
./tools/run_report_tests.sh build
|
||||
```
|
||||
|
||||
需要同时运行 SoC 仿真时:
|
||||
|
||||
```bash
|
||||
cd doc/reports/system
|
||||
./tools/run_report_tests.sh soc
|
||||
```
|
||||
|
||||
软件集合固定为 `asm`、`fft`、`fft_dma`、`mlkem_test` 和 `mlkem_hello`。`asm`
|
||||
需要官方 ExtRAM 测试数据和专用 matmul 流程,因此 `soc` 只编译它,不用通用
|
||||
`tb_top` 仿真;其余四项编译后依次运行 SoC 仿真。每次构建都会覆盖
|
||||
`sdk/axi_ram.mif`,因此不要并行执行这些用例。构建日志位于 `evidence/build/`。
|
||||
脚本会设置 `CCACHE_DISABLE=1`,避免 Vivado 2019.2 在编译 XSIM 自动生成的 C 文件时
|
||||
因 ccache 目录不可写而只报告不透明的 `XSIM 43-3409`。
|
||||
同时会将 `/lib64` 加入 XSIM 子进程库路径,避免旧版 Vivado clang 混用不匹配的
|
||||
`libncurses.so.5` 与 `libtinfo.so.5`。
|
||||
|
||||
逐项检查:
|
||||
|
||||
| 日志 | 必须出现 | 不得出现 |
|
||||
|---|---|---|
|
||||
| `evidence/build/{asm,fft,fft_dma,mlkem_test,mlkem_hello}.log` | 编译、链接和 objcopy 成功 | `Error` |
|
||||
| `evidence/soc/fft.log` | Hardware/Software FFT Time;更新后的程序应输出 `Max Abs Error` | 仿真超时、X/Z 扩散 |
|
||||
| `evidence/soc/fft_dma.log` | Hardware/Software FFT Time、`Max Abs Error` | DMA 死锁、仿真超时 |
|
||||
| `evidence/soc/mlkem_test.log` | `MLKEM_RESULT=PASS`、`MLKEM_DONE` | 任一 `FAILS` 非 0 |
|
||||
| `evidence/soc/mlkem_hello.log` | `MLKEM_HELLO_RESULT=PASS`、`MLKEM_HELLO_DONE` | `RESULT=FAIL` |
|
||||
|
||||
ML-KEM 的正确计数是 3 个固定 KAT 向量、900 次执行,不是 360 个不同 KAT。若要在
|
||||
报告中声称覆盖隐式拒绝,必须另外增加“篡改 ciphertext 后共享密钥不等且流程按规范
|
||||
完成”的负向测试;当前脚本不提供该证据。
|
||||
|
||||
## 3. 必跑:重新布局布线与实现取证
|
||||
|
||||
完整实现耗时较长,单独执行:
|
||||
|
||||
```bash
|
||||
cd doc/reports/system
|
||||
./tools/run_report_tests.sh implement
|
||||
```
|
||||
|
||||
该命令先从 `synth_1` 开始强制重跑,重新综合/布局布线并生成 bitstream,随后生成以下文件:
|
||||
|
||||
- `evidence/vivado/timing_summary_full.rpt`
|
||||
- `evidence/vivado/utilization.rpt`
|
||||
- `evidence/vivado/utilization_hierarchical.rpt`
|
||||
- `evidence/vivado/power.rpt`
|
||||
- `evidence/vivado/clock_interaction.rpt`
|
||||
- `evidence/vivado/cdc.rpt`
|
||||
- `evidence/vivado/drc.rpt`
|
||||
- `evidence/vivado/methodology.rpt`
|
||||
- `evidence/vivado/route_status.rpt`
|
||||
- `evidence/vivado/dsp_check.log`
|
||||
|
||||
通过标准:
|
||||
|
||||
- Setup WNS 和 Hold WNS 均不小于 0;
|
||||
- failing endpoints 为 0,unconstrained paths 为 0;
|
||||
- CPU、SYS、ML-KEM 三个预期时钟均存在且频率正确;
|
||||
- `dsp_check.log` 显示 DSP used 为 0;
|
||||
- route status 为 fully routed;
|
||||
- DRC/methodology 无 Critical Warning 被忽略;
|
||||
- CDC 报告中的跨域均能对应到设计文档里的握手、同步器或异步 FIFO。`report_cdc`
|
||||
退出码为 0 不等于 CDC 自动通过,仍需人工查看不安全类别和时钟约束。
|
||||
|
||||
功耗报告若仍显示 `Confidence Level: Low`,报告中只能称为 vectorless estimate。需要
|
||||
正式功耗结论时,应对代表性 ML-KEM 和 matmul 负载回标 SAIF/VCD,再重新运行
|
||||
`report_power`;这不是当前满分评测的前置条件。
|
||||
|
||||
## 4. 必跑:官方 5000 组 matmul 板级评测
|
||||
|
||||
使用最终 bitstream 和最终 `user-sample.bin`,保持:
|
||||
|
||||
```text
|
||||
MATMUL_GROUP_NUM=5000
|
||||
MATMUL_FAST_START=1
|
||||
MATMUL_WRITEBACK_RESULTS=0
|
||||
```
|
||||
|
||||
运行官方评测工具,将完整控制台输出保存为:
|
||||
|
||||
```text
|
||||
doc/reports/system/evidence/board/matmul_official.log
|
||||
```
|
||||
|
||||
必须保留这些行:随机种子、Expected CRC32、`MATMUL_START`、`MATMUL_DONE`、Elapsed
|
||||
time、Reported/Expected CRC32、Score percent。通过标准为 CRC 匹配、22 s 内收到
|
||||
DONE。性能目标是小于等于 4.9 ms;同时记录硬件 `DMA_CYCLES`,用于区分内部批处理
|
||||
周期与串口/软件尾部开销。
|
||||
|
||||
至少连续运行 5 个不同随机种子。报告主表填写中位数,附录写 min/median/max;不要只
|
||||
选择最好一次。
|
||||
|
||||
## 5. 必跑:复位前零操作
|
||||
|
||||
竞赛约束要求复位前无任何操作。至少覆盖以下信号:UART 写使能、AXI AW/AR/W valid、
|
||||
DMA request、matmul start、ML-KEM start。观察窗口从上电开始到每个时钟域同步复位释放。
|
||||
|
||||
推荐二选一:
|
||||
|
||||
1. 在 Vivado 仿真 testbench 中加入断言,任一信号在 reset 有效时拉高即 `$fatal`;
|
||||
2. 板上 ILA 同时采集外部 reset、PLL locked、各域 resetn 和上述事务信号。
|
||||
|
||||
保存断言日志或 ILA 截图,并在文件名中包含提交短哈希。仅观察“串口没有字符”不足以
|
||||
证明 DMA 和 AXI 没有操作。
|
||||
|
||||
## 6. 建议补跑:matmul 仿真与稳健性
|
||||
|
||||
- 组数:1、2、3、5000,覆盖 Bank 翻转和最后一组;
|
||||
- DMA endpoint:对 valid/ready 随机插入 backpressure;
|
||||
- 边界数据:全 0、全 `0xffffffff`、单位矩阵、随机矩阵;
|
||||
- 控制:done W1C 后立即启动下一批,检查 CRC 清零与 group counter;
|
||||
- XSIM 与 Verilator 都运行。两者结果不一致时按 RTL 竞态/未初始化问题处理,不能只保留
|
||||
通过的仿真器结果。
|
||||
|
||||
## 7. 回填指标并生成 PDF
|
||||
|
||||
所有日志就位后执行:
|
||||
|
||||
```bash
|
||||
cd doc/reports/system
|
||||
./tools/run_report_tests.sh refresh
|
||||
```
|
||||
|
||||
该命令会:
|
||||
|
||||
1. 从 `evidence/` 提取 PPA、ML-KEM、FFT 和 matmul 指标;
|
||||
2. 更新 `report_metrics.tex`;
|
||||
3. 从 `figures/*.dot` 生成 PDF 矢量图;
|
||||
4. 用 XeLaTeX 多轮编译报告并检查未解析引用。
|
||||
|
||||
最终人工检查:目录非空、没有“图??/表??”、所有图中文字可读、报告中的 commit 与
|
||||
bitstream/日志一致、所有“待复测”结论已经按真实证据更新。
|
||||
12
doc/reports/system/evidence/README.md
Normal file
12
doc/reports/system/evidence/README.md
Normal file
@@ -0,0 +1,12 @@
|
||||
# Report evidence
|
||||
|
||||
Generated logs and Vivado reports used by `system_design_report.tex` live here:
|
||||
|
||||
- `soc/`: UART output from SoC simulations
|
||||
- `build/`: software build logs for the five selected examples
|
||||
- `vivado/`: timing, utilization, CDC, DRC, route and power reports
|
||||
- `board/`: official board evaluation output
|
||||
|
||||
Run `../tools/run_report_tests.sh refresh` after replacing evidence. Large generated
|
||||
artifacts such as `.wdb`, `.jou`, `.str`, implementation checkpoints and bitstreams do
|
||||
not belong in this directory.
|
||||
30
doc/reports/system/figures/clock_cdc.dot
Normal file
30
doc/reports/system/figures/clock_cdc.dot
Normal file
@@ -0,0 +1,30 @@
|
||||
digraph G {
|
||||
graph [rankdir=TB, bgcolor="transparent", pad=0.15, nodesep=0.30, ranksep=0.42,
|
||||
fontname="Noto Sans CJK SC"];
|
||||
node [shape=box, style="rounded,filled", fontname="Noto Sans CJK SC", fontsize=10,
|
||||
color="#44546A", fillcolor="#F7F8FA", margin="0.13,0.08"];
|
||||
edge [fontname="Noto Sans CJK SC", fontsize=9, color="#596777", arrowsize=0.7];
|
||||
|
||||
osc [label="50 MHz 外部时钟"];
|
||||
pll [label="PLL\nlocked", fillcolor="#D9EAF7"];
|
||||
cpu [label="cpu_clk 33.333 MHz\nCPU + AXI CPU 侧", fillcolor="#EAF2F8"];
|
||||
sys [label="sys_clk 50 MHz\nCrossbar / DMA / FFT / matmul", fillcolor="#E8F3E8"];
|
||||
mlk [label="mlkem_clk 76.923 MHz\nML-KEM 计算域", fillcolor="#F8E6EA"];
|
||||
cdc_cpu [label="CPU↔SYS AXI CDC\n异步握手 / FIFO", shape=box, fillcolor="#FFF1D6"];
|
||||
cdc_mlk [label="SYS↔ML-KEM CDC\n命令 / 数据 / 状态同步", shape=box, fillcolor="#FFF1D6"];
|
||||
rst [label="各域复位同步器\n异步置位、同步释放", fillcolor="#F2F2F2"];
|
||||
|
||||
{ rank=same; osc; pll; }
|
||||
{ rank=same; cpu; cdc_cpu; sys; }
|
||||
{ rank=same; cdc_mlk; mlk; }
|
||||
osc -> pll;
|
||||
pll -> cpu;
|
||||
pll -> sys;
|
||||
pll -> mlk;
|
||||
cpu -> cdc_cpu -> sys [dir=both];
|
||||
sys -> cdc_mlk -> mlk [dir=both];
|
||||
pll -> rst [label="locked"];
|
||||
rst -> cpu [style=dashed, label="resetn", constraint=false];
|
||||
rst -> sys [style=dashed, label="resetn", constraint=false];
|
||||
rst -> mlk [style=dashed, label="resetn", constraint=false];
|
||||
}
|
||||
BIN
doc/reports/system/figures/clock_cdc.pdf
Normal file
BIN
doc/reports/system/figures/clock_cdc.pdf
Normal file
Binary file not shown.
22
doc/reports/system/figures/matmul_pipeline.dot
Normal file
22
doc/reports/system/figures/matmul_pipeline.dot
Normal file
@@ -0,0 +1,22 @@
|
||||
digraph G {
|
||||
graph [rankdir=TB, bgcolor="transparent", pad=0.15, nodesep=0.28, ranksep=0.38,
|
||||
fontname="Noto Sans CJK SC"];
|
||||
node [shape=box, style="rounded,filled", fontname="Noto Sans CJK SC", fontsize=10,
|
||||
color="#44546A", fillcolor="#F7F8FA", margin="0.12,0.08"];
|
||||
edge [fontname="Noto Sans CJK SC", fontsize=9, color="#596777", arrowsize=0.7];
|
||||
|
||||
cfg [label="CPU 一次配置\n基址 / 组数 / stride", fillcolor="#EAF2F8"];
|
||||
read [label="DMA 读取 128 B\nA、B → 空闲 Bank", fillcolor="#E8F3E8"];
|
||||
calc [label="4 路 32x32 LUT 乘法\n每拍形成一个输出元素的 4 项乘积", fillcolor="#FFF1D6"];
|
||||
write [label="DMA 写回 192 B\nC → ExtRAM", fillcolor="#E8F3E8"];
|
||||
crc [label="硬件流水 CRC32\n不写回时仍可判定整批结果", fillcolor="#F8E6EA"];
|
||||
next [label="Bank 翻转 / group+1", shape=diamond, fillcolor="#F2F2F2"];
|
||||
done [label="done + dma_cycles", fillcolor="#D9EAF7"];
|
||||
|
||||
{ rank=same; read; calc; write; }
|
||||
cfg -> read;
|
||||
read -> calc -> write;
|
||||
write -> crc -> next;
|
||||
next -> read [label="未完成", constraint=false];
|
||||
next -> done [label="全部完成"];
|
||||
}
|
||||
BIN
doc/reports/system/figures/matmul_pipeline.pdf
Normal file
BIN
doc/reports/system/figures/matmul_pipeline.pdf
Normal file
Binary file not shown.
29
doc/reports/system/figures/system_boundary.dot
Normal file
29
doc/reports/system/figures/system_boundary.dot
Normal file
@@ -0,0 +1,29 @@
|
||||
digraph G {
|
||||
graph [rankdir=LR, bgcolor="transparent", pad=0.15, nodesep=0.35, ranksep=0.65,
|
||||
fontname="Noto Sans CJK SC", fontsize=10];
|
||||
node [shape=box, style="rounded,filled", fontname="Noto Sans CJK SC", fontsize=10,
|
||||
color="#44546A", fillcolor="#F5F7FA", margin="0.13,0.08"];
|
||||
edge [fontname="Noto Sans CJK SC", fontsize=9, color="#596777", arrowsize=0.7];
|
||||
|
||||
cpu [label="LoongArch32r CPU\n软件调度 / 校验", fillcolor="#EAF2F8"];
|
||||
xbar [label="AXI Crossbar\n双主设备 / 统一编址", fillcolor="#D9EAF7"];
|
||||
mem [label="BaseRAM / ExtRAM", fillcolor="#F2F2F2"];
|
||||
periph [label="UART / DVI / confreg", fillcolor="#F2F2F2"];
|
||||
dma [label="4 通道 DMA\nmem2mem / endpoint", fillcolor="#E8F3E8"];
|
||||
seq [label="matmul 批处理序列器", fillcolor="#E8F3E8"];
|
||||
matmul [label="4x4 矩阵乘法\n双 Bank + CRC", fillcolor="#FFF1D6"];
|
||||
fft [label="1024 点 FFT\n5 个 Radix-2² SDF 单元", fillcolor="#FFF1D6"];
|
||||
mlkem [label="ML-KEM-512/768/1024\nKeyGen / Encaps / Decaps", fillcolor="#F8E6EA"];
|
||||
|
||||
cpu -> xbar [label="AXI master 0"];
|
||||
dma -> xbar [label="AXI master 1", dir=both];
|
||||
xbar -> mem [dir=both];
|
||||
xbar -> periph [dir=both];
|
||||
xbar -> dma [label="配置"];
|
||||
xbar -> matmul [label="Slave 7", dir=both];
|
||||
xbar -> fft [label="Slave 6 / 高地址", dir=both];
|
||||
xbar -> mlkem [label="Slave 6 / 低地址", dir=both];
|
||||
seq -> dma [label="描述符"];
|
||||
seq -> matmul [label="start / done"];
|
||||
dma -> matmul [label="本地 endpoint", dir=both, color="#2F6B3C", penwidth=1.4];
|
||||
}
|
||||
BIN
doc/reports/system/figures/system_boundary.pdf
Normal file
BIN
doc/reports/system/figures/system_boundary.pdf
Normal file
Binary file not shown.
21
doc/reports/system/figures/verification_flow.dot
Normal file
21
doc/reports/system/figures/verification_flow.dot
Normal file
@@ -0,0 +1,21 @@
|
||||
digraph G {
|
||||
graph [rankdir=LR, bgcolor="transparent", pad=0.15, nodesep=0.28, ranksep=0.55,
|
||||
fontname="Noto Sans CJK SC"];
|
||||
node [shape=box, style="rounded,filled", fontname="Noto Sans CJK SC", fontsize=9.5,
|
||||
color="#44546A", fillcolor="#F7F8FA", margin="0.12,0.08"];
|
||||
edge [fontname="Noto Sans CJK SC", fontsize=9, color="#596777", arrowsize=0.7];
|
||||
|
||||
rtl [label="RTL 静态检查\nVerilator lint"];
|
||||
unit [label="模块级仿真\n协议、边界、反压"];
|
||||
soc [label="SoC 仿真\nCPU + AXI + 加速器"];
|
||||
impl [label="布局布线\n时序 / DRC / CDC / 资源"];
|
||||
board [label="板级回归\n正确性 + 实测耗时"];
|
||||
evidence [label="版本化证据\n日志 / rpt / bit / 指标文件", fillcolor="#D9EAF7"];
|
||||
|
||||
rtl -> unit -> soc -> impl -> board;
|
||||
rtl -> evidence [style=dashed];
|
||||
unit -> evidence [style=dashed];
|
||||
soc -> evidence [style=dashed];
|
||||
impl -> evidence [style=dashed];
|
||||
board -> evidence [style=dashed];
|
||||
}
|
||||
BIN
doc/reports/system/figures/verification_flow.pdf
Normal file
BIN
doc/reports/system/figures/verification_flow.pdf
Normal file
Binary file not shown.
47
doc/reports/system/report_metrics.tex
Normal file
47
doc/reports/system/report_metrics.tex
Normal file
@@ -0,0 +1,47 @@
|
||||
% Generated values may be refreshed with: make metrics
|
||||
% Evidence status is intentionally explicit so stale data is not presented as current.
|
||||
\newcommand{\EvidenceRevision}{1b51f28(历史报告,待复测)}
|
||||
\newcommand{\EvidenceDate}{2026-07-15}
|
||||
|
||||
\newcommand{\CpuFreqMHz}{33.333}
|
||||
\newcommand{\SysFreqMHz}{50.000}
|
||||
\newcommand{\MlkemFreqMHz}{76.923}
|
||||
\newcommand{\OverallWNS}{0.070}
|
||||
\newcommand{\CpuWNS}{0.737}
|
||||
\newcommand{\SysWNS}{0.125}
|
||||
\newcommand{\MlkemWNS}{0.070}
|
||||
|
||||
\newcommand{\SliceLUTs}{55,066}
|
||||
\newcommand{\SliceLUTPct}{41.16\%}
|
||||
\newcommand{\SliceFFs}{31,000}
|
||||
\newcommand{\SliceFFPct}{11.58\%}
|
||||
\newcommand{\BramTiles}{29.5}
|
||||
\newcommand{\BramPct}{8.08\%}
|
||||
\newcommand{\DspCount}{0}
|
||||
|
||||
\newcommand{\TotalPower}{0.779}
|
||||
\newcommand{\DynamicPower}{0.637}
|
||||
\newcommand{\StaticPower}{0.142}
|
||||
\newcommand{\PowerConfidence}{Low(未回标活动率)}
|
||||
|
||||
\newcommand{\MlkemFiveTwelveSW}{2,594,034}
|
||||
\newcommand{\MlkemFiveTwelveCPU}{200,471}
|
||||
\newcommand{\MlkemFiveTwelveDMA}{52,995}
|
||||
\newcommand{\MlkemSevenSixtyEightSW}{4,065,123}
|
||||
\newcommand{\MlkemSevenSixtyEightCPU}{296,193}
|
||||
\newcommand{\MlkemSevenSixtyEightDMA}{80,648}
|
||||
\newcommand{\MlkemTenTwentyFourSW}{6,041,204}
|
||||
\newcommand{\MlkemTenTwentyFourCPU}{404,476}
|
||||
\newcommand{\MlkemTenTwentyFourDMA}{113,273}
|
||||
|
||||
\newcommand{\FftCpuNs}{1,695,360}
|
||||
\newcommand{\FftDmaNs}{407,310}
|
||||
\newcommand{\FftSoftwareNs}{274,786,920}
|
||||
\newcommand{\FftCpuSpeedup}{161.94}
|
||||
\newcommand{\FftDmaSpeedup}{674.64}
|
||||
\newcommand{\FftMaxError}{Re=7,Im=6(按现有日志逐点估计,待程序直接输出)}
|
||||
|
||||
\newcommand{\MatmulGroups}{5,000}
|
||||
\newcommand{\MatmulBoardMs}{5.213592}
|
||||
\newcommand{\MatmulScore}{99.56\%}
|
||||
\newcommand{\MatmulCrcStatus}{PASS}
|
||||
Binary file not shown.
@@ -1,612 +1,458 @@
|
||||
\documentclass[11pt,a4paper,twoside]{article}
|
||||
\documentclass[11pt,a4paper,twoside]{ctexart}
|
||||
|
||||
% ─── Packages ────────────────────────────────────────────────
|
||||
\usepackage{ctex}
|
||||
\usepackage[top=2.5cm, bottom=2.5cm, left=2.8cm, right=2.8cm]{geometry}
|
||||
\usepackage[top=2.45cm,bottom=2.45cm,left=2.65cm,right=2.65cm]{geometry}
|
||||
\usepackage{graphicx}
|
||||
\usepackage{float}
|
||||
\usepackage{booktabs}
|
||||
\usepackage{longtable}
|
||||
\usepackage{multirow}
|
||||
\usepackage{tabularx}
|
||||
\usepackage{array}
|
||||
\usepackage{amsmath,amssymb}
|
||||
\usepackage{xcolor}
|
||||
\usepackage{enumitem}
|
||||
\usepackage{fancyhdr}
|
||||
\usepackage{hyperref}
|
||||
\usepackage{caption}
|
||||
\usepackage{fancyhdr}
|
||||
\usepackage{tikz}
|
||||
\usepackage{enumitem}
|
||||
\usepackage[normalem]{ulem}
|
||||
\usepackage{placeins}
|
||||
\usepackage{float}
|
||||
\usepackage{listings}
|
||||
\usepackage{xcolor}
|
||||
\usepackage[normalem]{ulem}
|
||||
|
||||
% ─── Styling ─────────────────────────────────────────────────
|
||||
\raggedbottom
|
||||
\hypersetup{colorlinks=true,linkcolor=black,citecolor=black,urlcolor=blue}
|
||||
\pagestyle{fancy}
|
||||
\fancyhead[LO,RE]{SoC系统设计报告}
|
||||
\fancyhead[RO,LE]{\thepage}
|
||||
\fancyfoot[C]{}
|
||||
\setlength{\parskip}{4pt plus 2pt minus 1pt}
|
||||
\definecolor{accent}{HTML}{1F4E79}
|
||||
\definecolor{lightblue}{HTML}{EAF2F8}
|
||||
\definecolor{lightgray}{HTML}{F3F4F6}
|
||||
\hypersetup{colorlinks=true,linkcolor=accent,urlcolor=accent,citecolor=accent}
|
||||
\captionsetup{font=small,labelfont=bf,skip=5pt}
|
||||
\setlength{\parindent}{2em}
|
||||
\setlength{\parskip}{3pt plus 1pt minus 1pt}
|
||||
\setlength{\emergencystretch}{2em}
|
||||
\setlength{\textfloatsep}{12pt plus 3pt minus 2pt}
|
||||
\setlength{\intextsep}{10pt plus 2pt minus 2pt}
|
||||
\renewcommand{\arraystretch}{1.18}
|
||||
\setlist[itemize]{leftmargin=2em,itemsep=2pt,topsep=3pt}
|
||||
\pagestyle{fancy}
|
||||
\fancyhf{}
|
||||
\fancyhead[LO,RE]{龙芯 SoC 异构加速系统设计报告}
|
||||
\fancyhead[RO,LE]{\thepage}
|
||||
\setlength{\headheight}{14pt}
|
||||
\setlength{\emergencystretch}{1.5em}
|
||||
\setlength{\intextsep}{6pt plus 2pt}
|
||||
\setlength{\textfloatsep}{8pt plus 2pt}
|
||||
\captionsetup{skip=4pt}
|
||||
\lstset{basicstyle=\ttfamily\small,breaklines=true,frame=single,backgroundcolor=\color{lightgray},numbers=none}
|
||||
\newcolumntype{Y}{>{\raggedright\arraybackslash}X}
|
||||
\newcommand{\code}[1]{\texttt{#1}}
|
||||
\newcommand{\pending}{\textcolor{red!70!black}{待当前版本复测}}
|
||||
|
||||
\lstset{basicstyle=\ttfamily\small,breaklines=true,frame=single,numbers=left,numberstyle=\tiny,xleftmargin=2em,language=C}
|
||||
|
||||
\newcommand{\Zq}{\mathbb{Z}_q}
|
||||
|
||||
\title{\textbf{基于龙芯SoC的后量子密码与矩阵运算\\加速系统设计与验证}}
|
||||
\author{碳基生物\\[4pt] \small 队伍编号:CICC1008439}
|
||||
\date{}
|
||||
\input{report_metrics.tex}
|
||||
|
||||
\begin{document}
|
||||
|
||||
% ================================================================
|
||||
% 封 面
|
||||
% ================================================================
|
||||
\begin{titlepage}
|
||||
\thispagestyle{empty}
|
||||
\begin{center}
|
||||
\vspace*{20pt}
|
||||
\includegraphics[width=0.42\textwidth]{logo_cicc.png}
|
||||
\vspace{28pt}
|
||||
|
||||
\vspace*{30pt}
|
||||
|
||||
\includegraphics[width=0.45\textwidth]{logo_cicc.png}
|
||||
|
||||
\vspace{36pt}
|
||||
|
||||
{\fontsize{22pt}{28pt}\selectfont\bfseries 第十届}
|
||||
|
||||
\vspace{4pt}
|
||||
|
||||
{\fontsize{22pt}{28pt}\selectfont\bfseries 全国大学生集成电路创新创业大赛}
|
||||
|
||||
{\fontsize{20pt}{28pt}\selectfont\bfseries 第十届全国大学生集成电路创新创业大赛\par}
|
||||
\vspace{54pt}
|
||||
{\fontsize{25pt}{34pt}\selectfont\bfseries 基于龙芯 SoC 的后量子密码与矩阵运算加速系统\par}
|
||||
\vspace{16pt}
|
||||
{\fontsize{17pt}{24pt}\selectfont 系统设计与验证报告\par}
|
||||
\end{center}
|
||||
|
||||
\vspace{80pt}
|
||||
|
||||
\begin{flushleft}
|
||||
\fontsize{14pt}{22pt}\selectfont
|
||||
报告类型:\uline{\hfill 系统设计报告\hfill} \\[18pt]
|
||||
参赛杯赛:\uline{\hfill 龙芯中科杯\hfill} \\[18pt]
|
||||
作品名称:\uline{\hfill 基于龙芯SoC的后量子密码与矩阵运算加速系统设计与验证\hfill} \\[18pt]
|
||||
队伍编号:\uline{\hfill CICC1008439\hfill} \\[18pt]
|
||||
团队名称:\uline{\hfill 碳基生物\hfill}
|
||||
\end{flushleft}
|
||||
|
||||
\vfill
|
||||
\noindent\begin{tabularx}{\textwidth}{>{\bfseries}p{3.2cm}X}
|
||||
参赛杯赛 & 龙芯中科杯 \\
|
||||
队伍编号 & CICC1008439 \\
|
||||
团队名称 & 碳基生物 \\
|
||||
目标器件 & Xilinx Artix-7 XC7A200T-1 \\
|
||||
证据版本 & \EvidenceRevision \\
|
||||
报告日期 & \EvidenceDate \\
|
||||
\end{tabularx}
|
||||
\vspace{22pt}
|
||||
\end{titlepage}
|
||||
|
||||
\maketitle
|
||||
\pagenumbering{Roman}
|
||||
\section*{摘要}
|
||||
\addcontentsline{toc}{section}{摘要}
|
||||
本项目在赛事提供的 LoongArch32r SoC 框架上集成 ML-KEM、1024 点 FFT 和
|
||||
4$\times$4 矩阵乘法三类专用加速器,并以四通道 DMA 和矩阵乘法批处理序列器降低
|
||||
CPU 搬运与调度开销。系统采用 CPU、系统总线和 ML-KEM 三时钟域架构;跨域控制、
|
||||
状态和数据通过显式 CDC 结构传递。矩阵乘法器使用四路 LUT 乘法器,每拍并行产生
|
||||
一个输出元素点积所需的四项乘积,并用双 Bank、流水 CRC32 与本地 DMA endpoint
|
||||
支撑 5000 组连续任务。报告重点说明设计边界、关键决策及可复现证据,不把参考 SoC
|
||||
基础设施误列为本项目贡献。
|
||||
|
||||
现有证据显示,历史实现的总体 WNS 为 +\OverallWNS\,ns,资源占用为
|
||||
\SliceLUTs{} LUT、\SliceFFs{} FF、\BramTiles{} BRAM Tile 和 \DspCount{} DSP;
|
||||
板级 5000 组矩阵乘法耗时为 \MatmulBoardMs\,ms,CRC 校验通过。由于当前分支经历
|
||||
PLL 与实现报告合并,所有 PPA 和板级性能必须按附带清单重新生成后再作为最终结论。
|
||||
|
||||
\noindent\textbf{关键词:} LoongArch32r;ML-KEM;DMA;矩阵乘法;FFT;跨时钟域;FPGA
|
||||
|
||||
\section*{主要贡献}
|
||||
\addcontentsline{toc}{section}{主要贡献}
|
||||
\begin{itemize}
|
||||
\item 在固定 AXI 端口预算内完成三类加速器集成,并用二级地址译码共享 FFT/ML-KEM 窗口;
|
||||
\item 构建 mem2mem、mem2ep、ep2mem 三类 DMA 路径及矩阵乘法硬件批处理序列器;
|
||||
\item 将 ML-KEM 计算域提升至独立时钟,并为命令、数据、状态和复位建立明确 CDC 边界;
|
||||
\item 建立从 RTL 静态检查、SoC 仿真、布局布线到板级评测的版本化证据链。
|
||||
\end{itemize}
|
||||
|
||||
\tableofcontents
|
||||
\newpage
|
||||
\clearpage
|
||||
\pagenumbering{arabic}
|
||||
|
||||
% ================================================================
|
||||
\section{系统概述}
|
||||
\section{需求、约束与设计边界}
|
||||
|
||||
\subsection{设计目标}
|
||||
\subsection{需求分解}
|
||||
表~\ref{tab:req}把竞赛目标转化为可验证的工程要求。每项要求都绑定明确证据,避免以
|
||||
“能够综合”替代功能、时序或板级行为验证。
|
||||
|
||||
本设计基于龙芯中科提供的LoongArch32r软核处理器及AXI总线SoC框架,构建了一个集成多种硬件加速器的异构计算系统。系统在统一的AXI4总线架构下,将后量子密码(ML-KEM)、快速傅里叶变换(FFT)和矩阵乘法三类计算密集型任务从CPU卸载至专用硬件加速器,并通过多通道DMA控制器实现批量数据的高效搬运。设计同时完成了龙芯中科杯阶段任务三(矩阵乘法器IP设计)的要求。
|
||||
|
||||
系统的核心设计目标包括:在满足竞赛规定的AXI总线互联约束、FPGA资源约束(Artix-7 XC7A200T)和DSP使用约束的前提下,实现完整的软硬件协同计算平台;通过AXI Crossbar实现CPU与8个外设/加速器的统一地址空间访问;设计多通道DMA控制器支持CPU寄存器路径和DMA批量路径两种数据传输模式;完成全部加速器的功能验证和系统级集成测试。
|
||||
|
||||
\subsection{软硬件功能划分}
|
||||
|
||||
系统的软硬件划分遵循以硬件加速计算密集操作、以软件管理控制流和数据调度的原则。表\ref{tab:hwsw}列出了各子系统在软硬件之间的功能分配。
|
||||
|
||||
\begin{table}[htbp]
|
||||
\begin{table}[H]
|
||||
\centering
|
||||
\small
|
||||
\caption{软硬件功能划分}
|
||||
\label{tab:hwsw}
|
||||
\begin{tabular}{lp{5cm}p{4.5cm}}
|
||||
\caption{系统需求与验收证据}
|
||||
\label{tab:req}
|
||||
\begin{tabularx}{\textwidth}{p{1.2cm}Y Y p{2.1cm}}
|
||||
\toprule
|
||||
\textbf{功能} & \textbf{硬件实现(本设计贡献)} & \textbf{软件管理} \\
|
||||
编号 & 要求 & 验收证据 & 当前状态 \\
|
||||
\midrule
|
||||
ML-KEM密钥生成/封装/解封装 & \texttt{mlkem\_top}全硬件加速 + DMA批量数据搬运 & 参数与种子配置、操作触发、结果校验 \\
|
||||
FFT 1024点变换 & FFT SDF流水线 & 波形数据写入、模式配置、启动触发 \\
|
||||
矩阵乘法 C=A×B & \texttt{matmul\_axi\_slave} 双Bank加速器 & 矩阵布局配置、计算触发、CRC校验 \\
|
||||
matmul批量调度 & \texttt{cdma\_matmul\_bridge}硬件序列控制器 & 源/目的基址与组数一次性配置 \\
|
||||
DMA数据搬运 & 4通道DMA控制器(mem2mem/mem2ep/ep2mem) & 传输描述符配置、完成轮询 \\
|
||||
R1 & 4$\times$4 无符号矩阵乘法,5000 组连续输入,结果 CRC32 正确 & 官方板级评测日志:CRC 匹配且收到 \code{MATMUL\_DONE} & 已有基线,待复测 \\
|
||||
R2 & 复位释放前不得产生 UART、DMA 或加速器事务 & ILA/测试平台断言与板级观察 & \pending \\
|
||||
R3 & 矩阵乘法路径不使用 DSP48E1 & 布局布线后 utilization 与自动 DSP 检查 & 已有基线,待复测 \\
|
||||
R4 & ML-KEM-512/768/1024 的 KeyGen、Encaps、Decaps 正确 & 3 个固定 KAT 向量,SW/CPU/DMA 三路径各 100 次 & 已有基线,待复测 \\
|
||||
R5 & FFT CPU 与 DMA 路径结果和软件参考一致 & 1024 点逐点最大误差及端到端耗时 & 需补全最大误差日志 \\
|
||||
R6 & 所有同步时钟域 setup/hold 收敛,CDC 结构可审计 & timing、clock interaction、CDC、DRC 报告 & 已有基线,待复测 \\
|
||||
\bottomrule
|
||||
\end{tabular}
|
||||
\end{tabularx}
|
||||
\end{table}
|
||||
|
||||
CPU负责系统的整体调度:配置各加速器的控制寄存器、触发计算启动、轮询或响应中断以获知完成状态、以及通过串口输出测试结果。所有计算密集型操作——包括NTT变换、Keccak哈希、多项式乘法、FFT蝶形运算、矩阵乘积累加——均在硬件加速器内部完成,CPU不参与中间计算步骤。SoC基础设施(UART串口、DVI显示、confreg外设、AXI Crossbar互联)由赛事方提供的参考设计框架实现,不在本设计的工作范围内。
|
||||
\subsection{软硬件边界}
|
||||
赛事框架提供 CPU、基础存储控制器、UART、DVI、confreg 和 AXI 基础互连。本项目的
|
||||
主要工作是加速器 RTL、DMA 数据路径、批处理控制、共享地址窗口、CDC 集成以及配套
|
||||
软件。CPU 只负责配置、启动、完成检测和结果判定;NTT、Keccak、多项式乘法、FFT
|
||||
蝶形和矩阵乘积累加均在硬件内部执行。
|
||||
|
||||
% ================================================================
|
||||
\section{系统架构设计}
|
||||
|
||||
\subsection{总体架构}
|
||||
|
||||
图\ref{fig:soc_arch}给出了SoC系统的顶层架构。系统以LoongArch32r CPU核心为主控制器,通过AXI4总线经时钟域交叉桥接模块至系统总线域(50 MHz),再由AXI交叉开关将CPU和DMA两个主设备的访问请求按地址高位译码分发至8个从设备。
|
||||
|
||||
\begin{figure}[H]
|
||||
\begin{figure}[tbp]
|
||||
\centering
|
||||
\includegraphics[width=0.92\textwidth]{system_arch.png}
|
||||
\caption{SoC系统顶层架构}
|
||||
\label{fig:soc_arch}
|
||||
\includegraphics[width=\textwidth,height=0.53\textheight,keepaspectratio]{figures/system_boundary.pdf}
|
||||
\caption{系统组成、贡献边界与主要数据通路}
|
||||
\label{fig:boundary}
|
||||
\end{figure}
|
||||
|
||||
\subsection{地址空间分配}
|
||||
\FloatBarrier
|
||||
\section{系统架构}
|
||||
|
||||
系统地址空间采用统一编址,CPU通过非缓存虚拟地址段(0xBFxxxxxx)访问各外设,对应物理地址段为0x1Fxxxxxx。表\ref{tab:addr_map}列出各从设备的地址窗口。
|
||||
\subsection{互连与地址空间}
|
||||
如图~\ref{fig:boundary},CPU 和 DMA 分别作为 AXI Master 0/1 接入 Crossbar。DMA
|
||||
通过 AXI 访问存储器,同时通过本地 endpoint 直接访问矩阵乘法双 Bank,避免把内部
|
||||
缓冲搬运再次送入 Crossbar。关键地址窗口见表~\ref{tab:addr};完整寄存器定义以 RTL
|
||||
和 BSP 头文件为准。
|
||||
|
||||
\begin{table}[htbp]
|
||||
\centering
|
||||
\small
|
||||
\caption{系统地址空间映射}
|
||||
\label{tab:addr_map}
|
||||
\begin{tabular}{cllp{4.8cm}}
|
||||
\toprule
|
||||
\textbf{端口} & \textbf{物理地址} & \textbf{虚拟地址} & \textbf{外设} \\
|
||||
\midrule
|
||||
Slave 0 & 0x1C00\_0000--0x1C7F\_FFFF & — & BaseRAM + ExtRAM \\
|
||||
Slave 2 & 0x1F00\_0000--0x1F0F\_FFFF & 0xBF00\_0000 & UART 16550 \\
|
||||
Slave 3 & 0x1F10\_0000--0x1F1F\_FFFF & 0xBF10\_0000 & DVI视频控制器 \\
|
||||
Slave 4 & 0x1F20\_0000--0x1F2F\_FFFF & 0xBF20\_0000 & confreg(GPIO/定时器/中断) \\
|
||||
Slave 5 & 0x1F30\_0000--0x1F3F\_FFFF & 0xBF30\_0000 & DMA/CDMA配置空间 \\
|
||||
Slave 6 & 0x1F40\_0000--0x1F4F\_FFFF & 0xBF40\_0000 & FFT + ML-KEM共享窗口 \\
|
||||
Slave 7 & 0x1F50\_0000--0x1F5F\_FFFF & 0xBF50\_0000 & 矩阵乘法加速器 \\
|
||||
\bottomrule
|
||||
\end{tabular}
|
||||
\end{table}
|
||||
|
||||
Slave 6(FFT/ML-KEM共享窗口)内部根据地址位[15:12]进一步路由:偏移0x0000--0x3FFF定向至ML-KEM的AXI从机,偏移0x8000--0xCFFF定向至FFT控制器。Slave 5(DMA配置窗口)内部根据地址位[11:8]分发:0x000--0x1FF区间通往DMA通道寄存器,0x200--0x2FF区间通往matmul序列控制器的配置寄存器。
|
||||
|
||||
\subsection{中断系统}
|
||||
|
||||
系统中断链路由外设配置与中断控制模块集中管理,6个中断源经优先级编码后送入CPU。中断映射关系如表\ref{tab:interrupts}所示。
|
||||
|
||||
\begin{table}[htbp]
|
||||
\centering
|
||||
\caption{中断映射}
|
||||
\label{tab:interrupts}
|
||||
\begin{tabular}{cll}
|
||||
\toprule
|
||||
\textbf{中断位} & \textbf{来源} & \textbf{触发条件} \\
|
||||
\midrule
|
||||
intrpt[0] & confreg\_int & 定时器/按键/UART/FFT任一中断 \\
|
||||
intrpt[1] & dma\_finish & DMA传输完成或matmul序列完成 \\
|
||||
intrpt[2] & matmul\_intr & 矩阵乘法计算完成 \\
|
||||
intrpt[3] & mlkem\_intr & ML-KEM操作完成 \\
|
||||
\bottomrule
|
||||
\end{tabular}
|
||||
\end{table}
|
||||
|
||||
\subsection{时钟与复位架构}
|
||||
|
||||
系统采用单一外部晶振(50 MHz)经PLL生成三个时钟域。CPU核心运行于约33 MHz(cpu\_clk),AXI总线、DMA控制器以及matmul和FFT等外设统一运行于50 MHz(sys\_clk)。ML-KEM加速器通过PLL独立获取约77 MHz的高频时钟(mlkem\_clk),专用于其高吞吐计算通路。ML-KEM的AXI从机接口内部通过跨时钟域桥接模块实现系统总线与高速计算域之间的同步,将CPU的寄存器读写请求安全传递至高频时钟域,中断信号同样经过跨域同步后送入CPU中断系统。三个时钟域之间通过异步时钟组约束正确隔离,各域配备独立的复位同步器,以PLL锁定信号作为复位释放的使能条件。
|
||||
|
||||
% ================================================================
|
||||
\section{核心子系统设计}
|
||||
|
||||
\subsection{DMA与CDMA数据搬运子系统}
|
||||
|
||||
DMA子系统是本设计中连接CPU、外部存储器和加速器的数据搬运中枢,由中央DMA控制器、matmul自动序列控制器和AXI-Lite地址分流逻辑三个模块协同构成。
|
||||
|
||||
中央DMA控制器(snix\_axil\_cdma\_mux)以4通道配置例化,每通道配备独立的源地址、目的地址、传输长度和模式寄存器组,寄存器组基地址以0x40为步长间隔排列。控制器向外暴露两组数据接口:AXI4主设备接口连接至Crossbar的Master 1端口,用于在系统存储器和外设之间发起标准AXI Burst读写传输;本地Endpoint接口以valid/ready握手协议直接连接至matmul加速器的内部缓冲区,实现低延迟的本地数据搬运,该通道完全绕开Crossbar的路由延迟。
|
||||
|
||||
matmul自动序列控制器(cdma\_matmul\_bridge)使批量矩阵运算的数据调度脱离了CPU的逐次寄存器操作。CPU通过其AXI-Lite配置接口一次性设置源基地址、目的基地址、待处理的矩阵组数和各组的数据跨度后,控制器内部的硬件状态机自动循环执行以下操作序列:向DMA发起从ExtRAM读取一组源矩阵数据至matmul输入Bank的请求;等待DMA返回读完成信号后触发matmul计算启动脉冲;轮询matmul的完成状态后向DMA发起将结果Bank数据写回ExtRAM的请求;递增矩阵组索引后进入下一轮循环。控制器的序列完成中断信号与DMA的全局传输完成信号经逻辑或合并后送入CPU中断系统。
|
||||
|
||||
\begin{figure}[H]
|
||||
\centering
|
||||
\IfFileExists{dma_subsystem.png}
|
||||
{\includegraphics[width=0.92\textwidth]{dma_subsystem.png}}
|
||||
{\fbox{\parbox[c][3cm][c]{0.8\textwidth}{\centering\itshape DMA/CDMA子系统架构图(待插入 \texttt{dma\_subsystem.png})}}}
|
||||
\caption{DMA/CDMA数据搬运子系统架构}
|
||||
\label{fig:dma_sys}
|
||||
\end{figure}
|
||||
|
||||
表\ref{tab:cdma_regs}列出DMA通道和matmul序列控制器的核心寄存器。
|
||||
|
||||
\begin{table}[htbp]
|
||||
\centering
|
||||
\caption{DMA/CDMA核心寄存器}
|
||||
\label{tab:cdma_regs}
|
||||
\small
|
||||
\begin{tabular}{cll}
|
||||
\toprule
|
||||
\textbf{偏移(通道0)} & \textbf{名称} & \textbf{功能} \\
|
||||
\midrule
|
||||
\multicolumn{3}{c}{\textit{4通道DMA(snix\_axil\_cdma\_mux),通道n基址 = 0xBF300000 + n×0x40}} \\
|
||||
0x00 & SRC\_ADDR & 源地址 \\
|
||||
0x04 & DST\_ADDR & 目的地址 \\
|
||||
0x08 & LENGTH & 传输字节数 \\
|
||||
0x10 & CTRL & bit0启动,bit[5:3]=AxSIZE,bit[13:6]=AxLEN \\
|
||||
0x14 & STATUS & bit0=busy(RO),bit1=done(W1C) \\
|
||||
0x18 & MODE & 0=mem2mem,1=mem2ep,2=ep2mem \\
|
||||
0x1C & EP\_CFG & [3:0]=bank,[15:8]=word offset,[19:16]=ep id \\
|
||||
\midrule
|
||||
\multicolumn{3}{c}{\textit{matmul序列控制器(cdma\_matmul\_bridge),基址 = 0xBF300200}} \\
|
||||
0x200 & SEQ\_CTRL & bit0=start \\
|
||||
0x204 & SEQ\_STATUS & bit0=busy(RO),bit1=done(W1C) \\
|
||||
0x208 & SEQ\_SRC\_BASE & 输入矩阵组物理源基址 \\
|
||||
0x20C & SEQ\_DST\_BASE & 输出矩阵组物理目的基址 \\
|
||||
0x210 & SEQ\_GROUPS & 自动处理的矩阵组数 \\
|
||||
0x214 & SEQ\_MAT\_BASE & matmul AXI slave基址(固定0x1F500000) \\
|
||||
0x218 & SEQ\_SRC\_STRIDE & 相邻输入组间隔(默认128 B) \\
|
||||
0x21C & SEQ\_DST\_STRIDE & 相邻输出组间隔(默认192 B) \\
|
||||
\bottomrule
|
||||
\end{tabular}
|
||||
\end{table}
|
||||
|
||||
\subsection{矩阵乘法加速器}
|
||||
|
||||
矩阵乘法加速器根据赛事任务要求设计,完成最大4阶矩阵乘法运算$C = A\times B$。加速器内部包含双Bank结构以实现流水化操作:一个Bank执行乘法计算时,另一个Bank可通过DMA预装载下一组输入数据。
|
||||
|
||||
每个Bank包含独立的A矩阵缓冲区(16个32-bit字)、B矩阵缓冲区(16个32-bit字)和C结果缓冲区(16个66-bit元素拆分为48个32-bit字)。计算阵列由4个并行乘法器实例组成,每个实例负责一行结果向量的点积累加。乘法器完全采用LUT和CARRY4逻辑资源实现32乘32-bit无符号乘法,严格遵循赛事不得使用DSP资源的约束。
|
||||
|
||||
\begin{figure}[H]
|
||||
\centering
|
||||
\IfFileExists{matmul_arch.png}
|
||||
{\includegraphics[width=0.92\textwidth]{matmul_arch.png}}
|
||||
{\fbox{\parbox[c][3cm][c]{0.8\textwidth}{\centering\itshape 矩阵乘法加速器架构图(待插入 \texttt{matmul\_arch.png})}}}
|
||||
\caption{矩阵乘法加速器架构}
|
||||
\label{fig:matmul_sys}
|
||||
\end{figure}
|
||||
|
||||
加速器的控制寄存器接口如表\ref{tab:matmul_regs}所示。
|
||||
|
||||
\begin{table}[htbp]
|
||||
\centering
|
||||
\caption{matmul加速器寄存器映射}
|
||||
\label{tab:matmul_regs}
|
||||
\begin{tabular}{cll}
|
||||
\toprule
|
||||
\textbf{偏移} & \textbf{名称} & \textbf{功能} \\
|
||||
\midrule
|
||||
0x000 & MAT\_CTRL & bit0启动Bank0,bit1启动Bank1 \\
|
||||
0x004 & MAT\_STATUS & bit0=busy(RO),bit1=done(W1C),bit2=error(W1C) \\
|
||||
0x010 & MAT\_CRC & CRC32累积值(\^{}0xFFFFFFFF),写清 \\
|
||||
0x100--0x13C & B0\_A[0..15] & Bank0 A矩阵(16×32-bit) \\
|
||||
0x140--0x17C & B0\_B[0..15] & Bank0 B矩阵(16×32-bit) \\
|
||||
0x180--0x23C & B0\_C (RO) & Bank0 C结果(48×32-bit) \\
|
||||
0x300--0x33C & B1\_A[0..15] & Bank1 A矩阵 \\
|
||||
0x340--0x37C & B1\_B[0..15] & Bank1 B矩阵 \\
|
||||
0x380--0x43C & B1\_C (RO) & Bank1 C结果 \\
|
||||
\bottomrule
|
||||
\end{tabular}
|
||||
\end{table}
|
||||
|
||||
\subsection{FFT加速器}
|
||||
|
||||
FFT加速器基于单路径延迟反馈(Single-path Delay Feedback,SDF)架构实现1024点16-bit定点FFT/IFFT。其流水线由5级SDF单元级联构成,每级包含一个基-2蝶形单元、一个复数乘法器和一个深度逐级减半的延迟缓冲器。旋转因子由片上ROM提供。加速器通过AXI从机封装接口挂载于总线,提供输入实部和虚部的BRAM写窗口(各1024个16-bit字)、输出BRAM读窗口以及控制和状态寄存器。输入输出数据通过位反转地址索引实现算法要求的序映射。
|
||||
|
||||
\begin{figure}[H]
|
||||
\centering
|
||||
\IfFileExists{fft_arch.png}
|
||||
{\includegraphics[width=0.92\textwidth]{fft_arch.png}}
|
||||
{\fbox{\parbox[c][3cm][c]{0.8\textwidth}{\centering\itshape FFT加速器架构图(待插入 \texttt{fft\_arch.png})}}}
|
||||
\caption{FFT加速器SDF流水线架构}
|
||||
\label{fig:fft_sys}
|
||||
\end{figure}
|
||||
|
||||
\subsection{ML-KEM后量子密码加速器}
|
||||
|
||||
ML-KEM后量子密码加速器是本系统中最复杂的单一加速器,完整的IP设计已在\emph{《ML-KEM后量子密钥封装硬件IP核设计与验证》}报告中详述,此处仅概述其在系统集成层面的关键特性。
|
||||
|
||||
该加速器核心在单一模块内支持FIPS 203标准全部三种操作和三种安全等级,通过运行时端口实现动态选择。加速器通过AXI从机接口挂载于总线,提供内存映射寄存器用于控制、状态查询和逐字节数据装载与回读。三个直接存储器窗口(EK、DK、CT)支持DMA批量数据搬运。
|
||||
|
||||
\begin{figure}[H]
|
||||
\centering
|
||||
\includegraphics[width=0.92\textwidth]{mlkem_modules.png}
|
||||
\caption{ML-KEM加速器内部模块层次结构}
|
||||
\label{fig:mlkem_sys}
|
||||
\end{figure}
|
||||
|
||||
% ================================================================
|
||||
\section{芯片前端设计与验证方案}
|
||||
|
||||
\subsection{SoC集成设计方法}
|
||||
|
||||
本设计采用增量集成的设计方法学。各加速器IP首先在独立仿真环境中完成模块级功能验证,确认算法正确性和接口协议合规性后,再逐步嵌入SoC顶层。集成过程涵盖五个顺序步骤。
|
||||
|
||||
第一步是AXI从机接口适配。三个加速器各自通过对应的AXI从机包装器挂载至总线——矩阵乘法器提供控制寄存器和双Bank数据窗口,ML-KEM通过从机接口暴露内存映射寄存器和DMA窗口,FFT通过封装接口提供输入输出BRAM和状态寄存器。第二步是交叉开关端口分配,根据地址空间规划在顶层集成文件中完成各AXI从机与交叉开关对应端口的信号连接。FFT和ML-KEM因共享同一从机端口,通过内部地址译码逻辑实现基于地址位的二次路由。第三步涉及中断链路的连接,各加速器的完成或错误信号根据中断映射表连接至中断控制器的对应输入位或CPU的直接中断输入。第四步完成时钟与复位的集成,确保各加速器的时钟信号通过PLL的对应输出端获取,复位信号通过复位同步器产生与各自时钟域同步的释放时序,避免异步复位引起的亚稳态问题。第五步为软件BSP适配,在SDK中为每个加速器添加基地址宏定义、寄存器偏移量枚举和基本的读写封装函数。
|
||||
|
||||
\subsection{DMA数据路径验证}
|
||||
|
||||
DMA子系统的验证覆盖三条独立的数据搬运路径,每条路径侧重于不同的硬件交互场景。
|
||||
|
||||
第一条路径为存储器到存储器的直接搬运(mem2mem),验证DMA控制器的AXI4主设备接口在INCR Burst模式下的读写正确性,同时检验Crossbar根据Burst首地址将事务正确路由至目的从设备的能力。测试通过向ExtRAM的不重叠源区和目的区写入已知数据模式、触发DMA传输后逐字比对来完成。第二条路径涉及DMA与matmul加速器之间的本地Endpoint通信(mem2ep与ep2mem)。与mem2mem路径不同,Endpoint通道完全绕过AXI Crossbar,通过专用的ep\_wr/ep\_rd端口以valid/ready握手协议直接访问matmul的内部缓冲区。该路径的验证重点在于确保DMA侧和matmul侧的流控握手在所有传输长度下均不会出现数据丢失或死锁。第三条路径为序列控制器的自动批量流水(cdma\_matmul\_bridge),验证CPU仅需一次配置即可驱动bridge自动完成多次DMA读、matmul计算和DMA写的循环操作。该路径的验证关注多组连续处理场景下的状态机转移正确性、传输完成中断的准确触发时机以及异常条件下的错误恢复行为。
|
||||
|
||||
\subsection{系统级仿真验证}
|
||||
|
||||
系统级验证在Vivado XSIM仿真器上通过mycpu\_tb测试平台执行。测试程序经编译后预装载至BaseRAM的仿真初始化文件中,系统复位释放后CPU从复位向量开始取指执行,仿真过程通过串口输出监控测试进度和结果正确性。系统级验证覆盖四类典型使用场景。
|
||||
|
||||
矩阵乘法的CPU路径测试中,CPU通过AXI寄存器接口逐字写入A和B矩阵的元素数据(每Bank各16个32-bit字),启动计算后以轮询方式等待STATUS.done标志置位,随后从C结果缓冲区回读48个32-bit字并进行CRC32完整性校验。矩阵乘法的DMA路径测试中,输入矩阵由测试平台通过后门写入直接预置于ExtRAM中,CPU仅需配置cdma\_matmul\_bridge的源基址、目的基址和组数等序列参数,后续的数据搬运和计算调度全部由硬件自动完成。ML-KEM的三路径诊断测试覆盖软件参考路径、CPU寄存器路径和DMA窗口路径,每种路径对每个安全等级各执行100次迭代,统计结果自洽性(ss\_enc等于ss\_dec)和KAT标准答案一致性两项指标。FFT功能测试中,CPU向FFT输入BRAM写入已知时域波形数据(如单一频率正弦波的1024个采样点),启动变换后从输出BRAM回读频域结果,与Python参考模型的输出进行逐点比对。
|
||||
|
||||
% ================================================================
|
||||
\section{仿真测试结果与分析}
|
||||
|
||||
\subsection{矩阵乘法测试结果}
|
||||
|
||||
矩阵乘法加速器在SoC仿真环境中完成了完整功能验证,CRC32校验值与参考模型完全一致。表\ref{tab:matmul_results}列出批量矩阵运算的时序分解。
|
||||
|
||||
\begin{table}[htbp]
|
||||
\centering
|
||||
\caption{矩阵乘法测试结果}
|
||||
\label{tab:matmul_results}
|
||||
\begin{tabular}{lcc}
|
||||
\toprule
|
||||
\textbf{阶段} & \textbf{CPU周期} & \textbf{说明} \\
|
||||
\midrule
|
||||
初始化 (PROLOGUE) & 26,580 & 程序启动与运行环境初始化 \\
|
||||
批量计算循环 (LOOP) & 171,840 & 10组矩阵的DMA搬运+计算+写回 \\
|
||||
~~每组平均 & 17,184 & 含DMA读、matmul计算、DMA写 \\
|
||||
收尾 (EPILOGUE) & 14,640 & 结果校验与串口输出 \\
|
||||
\midrule
|
||||
\textbf{总计} & \textbf{213,060} & 约6.46 ms(@33 MHz CPU) \\
|
||||
\bottomrule
|
||||
\end{tabular}
|
||||
\end{table}
|
||||
|
||||
\subsection{ML-KEM系统级测试结果}
|
||||
|
||||
ML-KEM加速器在SoC集成环境中完成了软件参考(SW)、CPU寄存器路径和DMA窗口路径各100次迭代的系统级诊断测试。表\ref{tab:mlkem_soc_results}列出三种安全等级下各路径完成一轮完整往返(KeyGen+Encaps+Decaps)的平均CPU周期数、耗时和加速比。
|
||||
|
||||
\begin{table}[htbp]
|
||||
\centering
|
||||
\small
|
||||
\caption{ML-KEM系统级测试结果(100次迭代均值)}
|
||||
\label{tab:mlkem_soc_results}
|
||||
\begin{tabular}{lrrrrr}
|
||||
\toprule
|
||||
\textbf{安全等级} & \textbf{SW (cycles)} & \textbf{CPU (cycles)} & \textbf{DMA (cycles)} & \textbf{CPU加速比} & \textbf{DMA加速比} \\
|
||||
\midrule
|
||||
ML-KEM-512 & 2,594,034 & 200,471 & 52,995 & 12.94$\times$ & 48.95$\times$ \\
|
||||
ML-KEM-768 & 4,065,123 & 296,193 & 80,648 & 13.72$\times$ & 50.41$\times$ \\
|
||||
ML-KEM-1024 & 6,041,204 & 404,476 & 113,273 & 14.94$\times$ & 53.33$\times$ \\
|
||||
\midrule
|
||||
\textbf{全部} & \textbf{PASS} & \textbf{PASS} & \textbf{PASS} & — & — \\
|
||||
\bottomrule
|
||||
\end{tabular}
|
||||
\end{table}
|
||||
|
||||
DMA路径相对于纯软件实现取得了最高53.33$\times$的加速比,相对于CPU寄存器路径额外贡献了约3.6--3.8$\times$的效率提升。全部360个KAT测试用例逐字节比对NIST标准答案通过,Decaps隐式拒绝路径同样验证正确。
|
||||
|
||||
\subsection{FFT测试结果}
|
||||
|
||||
FFT加速器在SoC环境中完成了CPU寄存器路径和DMA路径两种模式的功能与性能测试。测试使用单一频率正弦波作为输入(1024点采样),硬件输出与软件参考FFT的逐bin比对结果验证了变换的正确性。表\ref{tab:fft_results}列出两种数据路径的性能对比。
|
||||
|
||||
\begin{table}[htbp]
|
||||
\centering
|
||||
\caption{FFT加速器测试结果}
|
||||
\label{tab:fft_results}
|
||||
\begin{tabular}{lccc}
|
||||
\toprule
|
||||
\textbf{测试模式} & \textbf{耗时 (ms)} & \textbf{加速比} & \textbf{最大误差} \\
|
||||
\midrule
|
||||
CPU寄存器路径 & 1.724 & 142.55$\times$ & 1 (Re) / 2 (Im) \\
|
||||
DMA路径 & 0.293 & 838.32$\times$ & 1 (Re) / 2 (Im) \\
|
||||
\midrule
|
||||
软件参考 (33 MHz CPU) & 245.6 & — & — \\
|
||||
\bottomrule
|
||||
\end{tabular}
|
||||
\end{table}
|
||||
|
||||
CPU寄存器路径中,CPU逐字通过AXI寄存器写入1024个实部和虚部样本,启动FFT后轮询完成状态,再逐字回读输出结果,总耗时约1.72 ms(硬件计算+寄存器I/O),加速比为143×。DMA路径中,输入和输出数据全部通过DMA的mem2ep和ep2mem通道批量搬运,绕过了逐字AXI寄存器访问的开销,总耗时降至0.29 ms,加速比达到838×。两种路径的硬件输出与软件参考FFT的最大偏差仅为1(实部)和2(虚部),验证了16-bit定点FFT硬件实现的高精度。
|
||||
|
||||
\subsection{系统时序收敛}
|
||||
|
||||
完整SoC在Xilinx Artix-7 XC7A200T-1器件上完成布局布线,三个时钟域均通过时序收敛。主要时序指标如表\ref{tab:sys_timing}所示。
|
||||
|
||||
\begin{table}[htbp]
|
||||
\centering
|
||||
\caption{SoC布局布线后时序收敛结果}
|
||||
\label{tab:sys_timing}
|
||||
\begin{tabular}{lccc}
|
||||
\toprule
|
||||
\textbf{时钟域} & \textbf{频率} & \textbf{Setup WNS} & \textbf{违例端点} \\
|
||||
\midrule
|
||||
cpu\_clk & 33 MHz & +0.737 ns & 0 / 17,197 \\
|
||||
mlkem\_clk & 77 MHz & +0.070 ns & 0 / 31,646 \\
|
||||
sys\_clk & 50 MHz & +0.125 ns & 0 / 22,307 \\
|
||||
\bottomrule
|
||||
\end{tabular}
|
||||
\end{table}
|
||||
|
||||
\subsection{系统资源利用}
|
||||
|
||||
SoC整体资源利用见表\ref{tab:sys_util}。数据来自Vivado 2019.2布局布线后报告(器件XC7A200T-1,无DSP约束配置)。
|
||||
|
||||
\begin{table}[htbp]
|
||||
\centering
|
||||
\caption{SoC整体资源利用}
|
||||
\label{tab:sys_util}
|
||||
\begin{tabular}{lccc}
|
||||
\toprule
|
||||
\textbf{资源} & \textbf{已用} & \textbf{可用} & \textbf{利用率} \\
|
||||
\midrule
|
||||
Slice LUTs(Total) & 71,196 & 133,800 & 53.2\% \\
|
||||
Slice LUTs(Logic) & 69,453 & 133,800 & 51.9\% \\
|
||||
Slice Registers & 39,690 & 267,600 & 14.8\% \\
|
||||
Block RAM Tile & 28.0 & 365 & 7.7\% \\
|
||||
~~RAMB36E1 & 10 & 365 & 2.7\% \\
|
||||
~~RAMB18E1 & 36 & 730 & 4.9\% \\
|
||||
DSP48E1 & 0 & 740 & 0.0\% \\
|
||||
BUFG & 4 & 32 & 12.5\% \\
|
||||
\bottomrule
|
||||
\end{tabular}
|
||||
\end{table}
|
||||
|
||||
表\ref{tab:module_util}给出按模块层次分解的资源利用明细,从中可以清晰看出各子系统在总体面积中的贡献比例。
|
||||
|
||||
\begin{table}[htbp]
|
||||
\begin{table}[tbp]
|
||||
\centering
|
||||
\footnotesize
|
||||
\caption{主要模块资源利用明细}
|
||||
\label{tab:module_util}
|
||||
\begin{tabular}{lrrrr}
|
||||
\caption{关键物理地址窗口}
|
||||
\label{tab:addr}
|
||||
\begin{tabularx}{\textwidth}{p{4.6cm}p{3.2cm}Y}
|
||||
\toprule
|
||||
\textbf{模块} & \textbf{LUT} & \textbf{FF} & \textbf{BRAM} & \textbf{占比} \\
|
||||
物理地址 & 模块 & 说明 \\
|
||||
\midrule
|
||||
ML-KEM & 21,697 & 15,826 & 2$\times$36 + 14$\times$18 & 30.5\% \\
|
||||
~~FFT & 6,949 & 1,083 & 8$\times$36 + 4$\times$18 & 9.8\% \\
|
||||
矩阵乘法 & 10,204 & 3,239 & — & 14.3\% \\
|
||||
CPU (core\_top) & 8,553 & 5,591 & 20$\times$18 & 12.0\% \\
|
||||
DMA/CDMA & 1,558 & 1,476 & 1$\times$18 & 2.2\% \\
|
||||
Crossbar + CDC + 适配器 & 2,102 & 621 & — & 3.0\% \\
|
||||
matmul序列桥 & 247 & 578 & — & 0.3\% \\
|
||||
UART + confreg + DVI + SRAM & 1,711 & 1,222 & — & 2.4\% \\
|
||||
顶层胶合逻辑 & 812 & 4 & — & 1.1\% \\
|
||||
\midrule
|
||||
\textbf{合计} & \textbf{71,165} & \textbf{39,682} & \textbf{10$\times$36 + 31$\times$18} & \textbf{100\%} \\
|
||||
\code{0x1C00\_0000--0x1C7F\_FFFF} & BaseRAM/ExtRAM & 程序、源数据和结果存储 \\
|
||||
\code{0x1F00\_0000} & UART 16550 & 串口输出 \\
|
||||
\code{0x1F20\_0000} & confreg & GPIO、定时器与中断 \\
|
||||
\code{0x1F30\_0000} & DMA / 序列器 & DMA 通道位于低 0x200 B;matmul 序列器从 0x200 开始 \\
|
||||
\code{0x1F40\_0000} & ML-KEM / FFT & 由地址位二次译码的共享 Slave 6 窗口 \\
|
||||
\code{0x1F50\_0000} & matmul & 控制、CRC 和双 Bank 数据窗口 \\
|
||||
\bottomrule
|
||||
\end{tabular}
|
||||
\end{tabularx}
|
||||
\end{table}
|
||||
|
||||
注:Vivado层次化报告中各子模块合计可能略小于顶层总数,原因在于跨层次LUT合并(cross-hierarchy LUT combining)机制。表中顶层总计为71,196 LUT。
|
||||
\subsection{时钟、复位与 CDC}
|
||||
系统由 50\,MHz 输入时钟经 PLL 产生三个同步逻辑域:CPU 域、系统总线域和 ML-KEM
|
||||
计算域,拓扑见图~\ref{fig:cdc}。CPU--SYS 之间跨越完整 AXI 通道;SYS--ML-KEM
|
||||
之间跨越命令、窗口数据、完成状态和中断。设计原则是:多位数据只随握手或稳定窗口
|
||||
跨域,单比特状态经过同步器,脉冲采用保持/应答结构,复位采用异步置位、同步释放。
|
||||
|
||||
从模块资源分布可以得出以下观察。ML-KEM加速器消耗30.5\%的LUT,其ntt\_core采用双蝶形加4路Banked BRAM架构,poly\_mul\_sync的系数和中间数据缓冲亦通过BRAM实现,有效控制了LUT开销。矩阵乘法加速器以14.3\%的LUT占比位居第二,其4个并行32×32-bit流水线乘法器(每实例约1,000 LUT)是面积的主要贡献者。CPU核心消耗12.0\%的LUT,其中BTB(分支目标缓冲器)和TLB(地址转换旁路缓冲器)合占约3,000 LUT。FFT加速器虽仅占9.8\%的LUT,但消耗了最多的BRAM资源——8个RAMB36E1用于5级SDF架构中的延迟缓冲和旋转因子ROM存储。
|
||||
复位约束比普通功能复位更严格:PLL 锁定且各域同步器完成释放前,AXI valid、DMA
|
||||
请求、加速器 start 和 UART 写使能必须保持为零。这一约束需要通过专门的复位前监控
|
||||
测试确认,不能由“复位后程序正常运行”间接推出。
|
||||
|
||||
DSP使用量为零体现了竞赛规则约束和设计策略的一致性——ML-KEM不使用DSP、矩阵乘法受竞赛条款限制、FFT采用16-bit窄数据宽度,三者共同导致了全LUT乘法映射的设计决策。
|
||||
\begin{figure}[tbp]
|
||||
\centering
|
||||
\includegraphics[width=\textwidth,height=0.43\textheight,keepaspectratio]{figures/clock_cdc.pdf}
|
||||
\caption{时钟、复位与跨时钟域边界}
|
||||
\label{fig:cdc}
|
||||
\end{figure}
|
||||
|
||||
\subsection{功耗分析}
|
||||
\subsection{中断与完成通知}
|
||||
DMA 完成、matmul 完成和 ML-KEM 完成信号经同步后接入 CPU 中断系统。当前性能程序在
|
||||
短热路径中采用状态轮询,减少中断现场开销;功能回归仍需验证 done 的置位、W1C 清除、
|
||||
连续任务不丢失以及错误状态可恢复。
|
||||
|
||||
布局布线后系统功耗的层次化分解如表\ref{tab:power}所示。
|
||||
\FloatBarrier
|
||||
\section{关键设计决策}
|
||||
|
||||
\begin{table}[htbp]
|
||||
\subsection{DMA 本地 endpoint 与批处理序列器}
|
||||
通用 DMA 提供 4 个独立通道,每通道包含源地址、目的地址、长度、模式和 endpoint
|
||||
配置。对普通内存或 FFT/ML-KEM 窗口使用 AXI burst;对 matmul 双 Bank 使用本地
|
||||
valid/ready endpoint。后者减少地址译码和总线仲裁,但必须单独验证 backpressure、
|
||||
Bank 选择和非满 burst 长度。
|
||||
|
||||
矩阵乘法序列器把“读 128 B 输入、启动计算、等待完成、写回 192 B 结果、递增组号”
|
||||
固化为硬件循环。CPU 仅配置源/目的基址、组数和 stride。图~\ref{fig:matmulflow}
|
||||
描述单组事务;双 Bank 允许搬运与计算在相邻组之间重叠。当前竞赛快速路径可关闭结果
|
||||
写回,改由硬件流水 CRC 对整批结果判定,从而消除不参与最终评分的存储流量。
|
||||
|
||||
\begin{figure}[tbp]
|
||||
\centering
|
||||
\includegraphics[width=\textwidth,height=0.34\textheight,keepaspectratio]{figures/matmul_pipeline.pdf}
|
||||
\caption{矩阵乘法批处理事务与 CRC 路径}
|
||||
\label{fig:matmulflow}
|
||||
\end{figure}
|
||||
|
||||
\subsection{共享 FFT/ML-KEM AXI 窗口}
|
||||
Crossbar Slave 6 内部按地址高位将事务分发到 ML-KEM 或 FFT。该方案节省一个顶层
|
||||
从端口,但会把二级译码放入共同路径。读写响应必须与请求目标绑定,不能在 AW/W
|
||||
独立握手时重新按瞬时地址选择从机。验证重点包括 AW/W 不同拍到达、读写并发、从机
|
||||
反压及非法空洞地址响应。
|
||||
|
||||
\subsection{ML-KEM 独立计算时钟}
|
||||
ML-KEM 的关键通路与系统 AXI 通路性质不同。将计算核心放入独立的
|
||||
\MlkemFreqMHz\,MHz 时钟域,可在不提高 CPU 和 SRAM 压力的情况下缩短算法计算段。
|
||||
代价是增加 CDC 延迟与验证复杂度,因此性能比较必须报告端到端 CPU 周期,而不是仅
|
||||
报告核心 busy 周期;CDC 报告也必须作为实现准入条件。
|
||||
|
||||
\FloatBarrier
|
||||
\section{加速器实现}
|
||||
|
||||
\subsection{矩阵乘法与流水 CRC}
|
||||
加速器支持最大 4$\times$4 的 32-bit 无符号矩阵,66-bit 结果元素按三个 32-bit
|
||||
字输出。四个 32$\times$32-bit LUT 乘法器在同一拍分别计算一个输出元素点积中的
|
||||
四项乘积,之后进入累加和写回流水;它们并非分别负责四行。A/B/C 缓冲采用双 Bank,
|
||||
控制器通过 Bank 所有权避免 DMA 与计算阵列同时写同一缓冲。
|
||||
|
||||
CRC32 与结果产生流水同步更新。对 5000 组评测,软件只输出起止标记和最终 CRC,
|
||||
减少 UART 对计时窗口的扰动。硬件 \code{dma\_cycles} 记录批处理内部周期,板级主机
|
||||
计时则覆盖复位释放后协议观察与外部通信,两者用途不同,不应混为一个指标。
|
||||
|
||||
\subsection{1024 点 FFT}
|
||||
FFT 数据通路由 5 个 Radix-$2^2$ SDF 单元串联,每个单元包含两个蝶形阶段,而不是
|
||||
“五级普通 radix-2 FFT”。延迟反馈结构以 BRAM/移位存储保存历史样本,旋转因子由
|
||||
ROM 提供。输入输出窗口按 32-bit AXI word 编址,低 16 bit 承载有符号定点样本。
|
||||
CPU 路径用于接口诊断,DMA 路径反映系统实际吞吐。
|
||||
|
||||
\subsection{ML-KEM}
|
||||
ML-KEM 核支持 FIPS 203 的三个参数集及 KeyGen、Encaps、Decaps。主要计算模块包括
|
||||
Keccak/SHA3、采样、NTT/INTT、多项式乘法、压缩/解压和片上存储。AXI 包装器提供
|
||||
控制寄存器与 EK、DK、CT 数据窗口;DMA 路径批量搬运密钥和密文,CPU 路径保留为
|
||||
可观测的接口基线。
|
||||
|
||||
\section{验证策略与判定标准}
|
||||
|
||||
\begin{figure}[tbp]
|
||||
\centering
|
||||
\includegraphics[width=\textwidth,height=0.31\textheight,keepaspectratio]{figures/verification_flow.pdf}
|
||||
\caption{从 RTL 到板级结果的分层证据链}
|
||||
\label{fig:verifyflow}
|
||||
\end{figure}
|
||||
|
||||
\subsection{验证层次}
|
||||
验证按图~\ref{fig:verifyflow}推进。静态检查发现语法、位宽和不可达集成;模块级仿真
|
||||
覆盖握手和边界;SoC 仿真覆盖 CPU、缓存、AXI 和软件驱动;实现报告检查时序、CDC、
|
||||
DRC、DSP 与资源;板级测试确认真实 PLL、SRAM、串口和复位行为。任何一层通过都不能
|
||||
替代后一层。
|
||||
|
||||
\begin{table}[tbp]
|
||||
\centering
|
||||
\small
|
||||
\caption{系统功耗层次化分解}
|
||||
\label{tab:power}
|
||||
\begin{tabular}{lcc}
|
||||
\caption{必须重跑的验证矩阵}
|
||||
\label{tab:matrix}
|
||||
\begin{tabularx}{\textwidth}{p{2.35cm}Y Y}
|
||||
\toprule
|
||||
\textbf{模块} & \textbf{动态功耗(W)} & \textbf{占动态功耗比} \\
|
||||
测试 & 判定标准 & 证据文件 \\
|
||||
\midrule
|
||||
ML-KEM(u\_mlkem) & 0.218 & 40.0\% \\
|
||||
PLL时钟生成器 & 0.138 & 25.3\% \\
|
||||
矩阵乘法(u\_matmul) & 0.081 & 14.9\% \\
|
||||
FFT(u\_FFT) & 0.027 & 5.0\% \\
|
||||
CPU(u\_cpu) & 0.025 & 4.6\% \\
|
||||
DMA/CDMA & 0.008 & 1.5\% \\
|
||||
其它外设与总线 & 0.048 & 8.8\% \\
|
||||
Verilator lint & 无 Error;集成顶层可达性检查通过 & \path{evidence/lint.log} \\
|
||||
指定软件构建 & asm、fft、fft\_dma、mlkem\_test、mlkem\_hello 均成功链接 & \path{evidence/build/*.log} \\
|
||||
FFT CPU 路径 & 1024 点完成;记录端到端 ns 与逐点最大误差 & \path{evidence/soc/fft.log} \\
|
||||
FFT DMA 路径 & 四次搬运无死锁;最大误差和性能均输出 & \path{evidence/soc/fft_dma.log} \\
|
||||
ML-KEM 三路径 & k=2/3/4、SW/CPU/DMA 各 100 次,FAILS=0 & \path{evidence/soc/mlkem_test.log} \\
|
||||
ML-KEM 示例 & 共享密钥一致且明文恢复,输出 RESULT=PASS & \path{evidence/soc/mlkem_hello.log} \\
|
||||
完整实现 & setup/hold WNS$\ge0$,unconstrained=0,DSP=0,DRC 无关键错误 & \path{evidence/vivado/*.rpt} \\
|
||||
CDC 审计 & 预期跨域结构已约束;无未知组合跨域或未同步脉冲 & \path{evidence/vivado/cdc.rpt} \\
|
||||
matmul 官方板测 & 5000 组 CRC 匹配、收到 DONE、记录耗时和分数 & \path{evidence/board/matmul_official.log} \\
|
||||
复位前零操作 & 复位释放前 UART/AXI/DMA/start 均无事务 & ILA 截图或断言日志 \\
|
||||
\bottomrule
|
||||
\end{tabularx}
|
||||
\end{table}
|
||||
|
||||
\subsection{ML-KEM 样本数量解释}
|
||||
当前日志的 \code{STATIC\_KAT\_COUNT\_0} 表示每个参数集使用一个固定向量,因此是
|
||||
3 个不同 KAT 向量。每个向量在 SW、CPU、DMA 三条路径各运行 100 次,总计
|
||||
$3\times3\times100=900$ 次执行;不能表述为“360 个不同 KAT”。现有日志没有注入
|
||||
篡改密文,故本报告不宣称已覆盖隐式拒绝。若要加入该结论,应新增负向用例并保存
|
||||
独立证据。
|
||||
|
||||
\FloatBarrier
|
||||
\section{已有结果与复测状态}
|
||||
|
||||
\noindent\colorbox{lightblue}{\parbox{0.96\textwidth}{
|
||||
\textbf{证据状态:}以下数值由脚本从当前仓库可用日志/报告提取,证据版本为
|
||||
\EvidenceRevision。PLL 与实现产物合并后必须执行完整复测;在新 \code{evidence/}
|
||||
生成前,这些数字只作为历史基线,不作为最终提交结论。}}
|
||||
|
||||
\subsection{矩阵乘法板级结果}
|
||||
现有官方评测基线见表~\ref{tab:matresult}。外部耗时包含评测端从观察到
|
||||
\code{MATMUL\_START} 至收到 \code{MATMUL\_DONE} 的时间;内部 \code{dma\_cycles}
|
||||
应在复测日志中同时记录,用于区分硬件批处理时间与 UART/软件尾部时间。
|
||||
|
||||
\begin{table}[H]
|
||||
\centering
|
||||
\caption{5000 组矩阵乘法板级基线}
|
||||
\label{tab:matresult}
|
||||
\begin{tabular}{lc}
|
||||
\toprule
|
||||
指标 & 结果 \\
|
||||
\midrule
|
||||
\textbf{动态功耗合计} & \textbf{0.545} & 100\% \\
|
||||
器件静态功耗 & 0.141 & — \\
|
||||
\midrule
|
||||
\textbf{总片上功耗} & \textbf{0.686} & — \\
|
||||
组数 & \MatmulGroups \\
|
||||
外部计时 & \MatmulBoardMs\,ms \\
|
||||
CRC32 & \MatmulCrcStatus \\
|
||||
评分 & \MatmulScore \\
|
||||
满分目标 & 4.9\,ms \\
|
||||
\bottomrule
|
||||
\end{tabular}
|
||||
\end{table}
|
||||
|
||||
系统总功耗为0.686 W,其中动态功耗占79.4\%(0.545 W),静态功耗占20.6\%(0.141 W)。ML-KEM是最大的动态功耗来源(40.0\%),与资源利用分布一致。PLL以25.3\%的比例位居第二,反映了其驱动多时钟域(cpu\_clk、sys\_clk)的固有功耗开销。按资源类型划分,信号翻转功耗(0.194 W,占动态功耗35.6\%)和Slice Logic功耗(0.109 W,20.0\%)是两类最大的器件级功耗来源。
|
||||
\subsection{ML-KEM 性能与正确性}
|
||||
表~\ref{tab:mlkem}给出一次 KeyGen+Encaps+Decaps 往返的平均 CPU 周期。每个单元格
|
||||
对应 100 次执行且现有日志 FAILS=0。DMA 相比寄存器路径的收益来自批量窗口搬运;
|
||||
它不表示密码核心本身获得相同比例的加速。
|
||||
|
||||
\subsection{设计复杂度分析}
|
||||
|
||||
表\ref{tab:complexity}列出主要模块的Rent指数和平均扇出数。Rent指数是衡量模块内部互联复杂度的指标,数值越低表示结构越规整、局部互联越密集。
|
||||
|
||||
\begin{table}[htbp]
|
||||
\begin{table}[H]
|
||||
\centering
|
||||
\small
|
||||
\caption{模块设计复杂度(Rent指数)}
|
||||
\label{tab:complexity}
|
||||
\begin{tabular}{lccr}
|
||||
\caption{ML-KEM 三路径平均周期(历史基线)}
|
||||
\label{tab:mlkem}
|
||||
\begin{tabular}{lrrr}
|
||||
\toprule
|
||||
\textbf{模块} & \textbf{Rent指数} & \textbf{平均扇出} & \textbf{总实例数} \\
|
||||
参数集 & 软件参考 & CPU 寄存器 & DMA 窗口 \\
|
||||
\midrule
|
||||
矩阵乘法器 & 0.60(Normal) & 3.76 & 15,711 \\
|
||||
CPU & 0.59(Normal) & 3.96 & 15,822 \\
|
||||
UART & 0.55(Normal) & 3.54 & 1,296 \\
|
||||
DMA/CDMA & 0.46(Low) & 3.31 & 3,435 \\
|
||||
ML-KEM & 0.41(Low) & 3.78 & 87,846 \\
|
||||
ML-KEM-512 & \MlkemFiveTwelveSW & \MlkemFiveTwelveCPU & \MlkemFiveTwelveDMA \\
|
||||
ML-KEM-768 & \MlkemSevenSixtyEightSW & \MlkemSevenSixtyEightCPU & \MlkemSevenSixtyEightDMA \\
|
||||
ML-KEM-1024 & \MlkemTenTwentyFourSW & \MlkemTenTwentyFourCPU & \MlkemTenTwentyFourDMA \\
|
||||
\bottomrule
|
||||
\end{tabular}
|
||||
\end{table}
|
||||
|
||||
ML-KEM的Rent指数(0.41)在主要模块中最低,表明其数据通路具有高度规整的局部互联特性——多项式系数在相邻流水级之间传递,NTT蝶形单元按固定模式访问系数寄存器,这种结构化特征有利于布局布线的时序收敛。相比之下,矩阵乘法器和CPU的Rent指数较高(0.59--0.60),反映其控制逻辑的随机互联特性。FFT/ML-KEM共享从机以87,846个实例的规模(占全系统66.7\%的LUT实例数)成为物理设计中最庞大的层次化模块。
|
||||
\subsection{FFT 结果}
|
||||
现有 CPU 与 DMA 日志使用相同 1024 点多音输入。表~\ref{tab:fft}中的最大误差尚未由
|
||||
旧日志完整输出;当前测试程序已增加全 1024 点统计,复测后指标脚本会自动更新。
|
||||
|
||||
\subsection{高扇出网络分析}
|
||||
|
||||
表\ref{tab:fanout}列出布局布线后扇出最高的前5个网络,这些网络是潜在的时序瓶颈和布线拥塞来源。
|
||||
|
||||
\begin{table}[htbp]
|
||||
\begin{table}[H]
|
||||
\centering
|
||||
\small
|
||||
\caption{高扇出网络(Top 5)}
|
||||
\label{tab:fanout}
|
||||
\begin{tabular}{lcl}
|
||||
\caption{FFT 端到端性能(历史基线)}
|
||||
\label{tab:fft}
|
||||
\begin{tabularx}{\textwidth}{lrrY}
|
||||
\toprule
|
||||
\textbf{网络名称} & \textbf{扇出} & \textbf{驱动源} \\
|
||||
路径 & 硬件端到端时间 & 相对软件加速比 & 正确性 \\
|
||||
\midrule
|
||||
sys\_clk(全局时钟) & 35,652 & BUFG \\
|
||||
ML-KEM ek\_bram延迟寄存器 & 24,670 & LUT5 \\
|
||||
CPU复位网络 & 5,108 & LUT1 \\
|
||||
ML-KEM poly\_mul计数器 & 3,758 & FDCE \\
|
||||
matmul FSM独热状态 & 2,151 & FDRE \\
|
||||
CPU 寄存器 & \FftCpuNs\,ns & \FftCpuSpeedup$\times$ & 已显示能量 bin 一致;需重跑全量误差 \\
|
||||
DMA & \FftDmaNs\,ns & \FftDmaSpeedup$\times$ & \FftMaxError \\
|
||||
软件参考 & \FftSoftwareNs\,ns & 1$\times$ & 浮点参考 \\
|
||||
\bottomrule
|
||||
\end{tabular}
|
||||
\end{tabularx}
|
||||
\end{table}
|
||||
|
||||
全局时钟网络(sys\_clk,BUFG驱动)以35,652个负载位居首位,在当前50 MHz工作频率下不构成时序问题。ML-KEM内部有两个高扇出网络进入前5名:系数存储BRAM的延迟寄存器链(24,670扇出)源于字节编码器中一个组合使能信号被复制到大量并行路径;多项式乘法模块的加载计数器(3,758扇出)属于多路复用地址总线的经典高扇出场景。这些网络在当前工作条件下均未产生时序违例。
|
||||
\subsection{时序、资源与功耗}
|
||||
当前可用布局布线报告的汇总见表~\ref{tab:ppa}。三个域的 setup WNS 均为正;最终
|
||||
复测还必须检查 hold、未约束路径、clock interaction 和 CDC,而不能只引用一行 WNS。
|
||||
|
||||
\subsection{验证结果汇总}
|
||||
|
||||
表\ref{tab:verify_summary}给出系统级验证的覆盖范围和结果汇总。
|
||||
|
||||
\begin{table}[htbp]
|
||||
\begin{table}[H]
|
||||
\centering
|
||||
\small
|
||||
\caption{系统级验证结果汇总}
|
||||
\label{tab:verify_summary}
|
||||
\begin{tabular}{lcccl}
|
||||
\caption{实现结果基线}
|
||||
\label{tab:ppa}
|
||||
\begin{tabular}{lrr}
|
||||
\toprule
|
||||
\textbf{验证项} & \textbf{测试路径} & \textbf{迭代次数} & \textbf{通过率} & \textbf{关键指标} \\
|
||||
类别 & 指标 & 当前可用值 \\
|
||||
\midrule
|
||||
矩阵乘法 & CPU寄存器路径 & — & 100\% & CRC32校验正确 \\
|
||||
矩阵乘法 & DMA序列控制器 & 10组 & 100\% & 总计213,060周期 \\
|
||||
ML-KEM & SW/CPU/DMA三路径 & 100/级 & 100\% & KAT逐字节比对 \\
|
||||
FFT & CPU寄存器路径 & 1次 & 100\% & max error ≤2, 143×加速比 \\
|
||||
FFT & DMA路径 & 1次 & 100\% & max error ≤2, 838×加速比 \\
|
||||
DMA & mem2mem & 多次 & 100\% & 数据逐字比对 \\
|
||||
DMA & mem2ep/ep2mem & 多次 & 100\% & matmul Buffer读写正确 \\
|
||||
DMA & 序列桥批量流水 & 10组 & 100\% & 全自动循环完成 \\
|
||||
时序 & cpu\_clk / WNS & \CpuFreqMHz\,MHz / +\CpuWNS\,ns \\
|
||||
时序 & sys\_clk / WNS & \SysFreqMHz\,MHz / +\SysWNS\,ns \\
|
||||
时序 & mlkem\_clk / WNS & \MlkemFreqMHz\,MHz / +\MlkemWNS\,ns \\
|
||||
资源 & Slice LUT & \SliceLUTs{}(\SliceLUTPct) \\
|
||||
资源 & Slice Register & \SliceFFs{}(\SliceFFPct) \\
|
||||
资源 & BRAM Tile & \BramTiles{}(\BramPct) \\
|
||||
资源 & DSP48E1 & \DspCount \\
|
||||
功耗 & 动态 / 静态 / 总计 & \DynamicPower{} / \StaticPower{} / \TotalPower{} W \\
|
||||
\bottomrule
|
||||
\end{tabular}
|
||||
\end{table}
|
||||
|
||||
% ================================================================
|
||||
Vivado 功耗置信度为 \PowerConfidence。由于没有回标 SAIF/VCD 活动率,总功耗只能作为
|
||||
向量无关估算,不宜用于细粒度模块功耗排名。若评审要求功耗结论,应以代表性 ML-KEM
|
||||
和 matmul 负载生成活动文件后重新运行 \code{report\_power}。
|
||||
|
||||
\section{局限、风险与下一步}
|
||||
\begin{itemize}
|
||||
\item 当前 PPA 与板级日志不是 PLL/CDC 合并后的同一提交证据,必须按清单重跑并记录 commit;
|
||||
\item 通用 SoC 仿真 testbench 以程序返回地址结束,尚未对复位前零事务写成自动断言;
|
||||
\item ML-KEM 固定向量覆盖正确性和重复稳定性,但不等价于大量独立随机 KAT,也未覆盖隐式拒绝;
|
||||
\item FFT 旧日志仅打印有能量的 bin,当前程序虽已统计全量最大误差,仍需生成新日志;
|
||||
\item 功耗报告没有活动率回标,置信度低;模块级功耗百分比不作为本报告结论;
|
||||
\item 层次化资源表容易因层次重叠而重复计数,因此正文只报告顶层非重叠总量。若需模块占比,必须用同一层次深度并增加“其他/未归属”项。
|
||||
\end{itemize}
|
||||
|
||||
\section{结论}
|
||||
本项目的核心价值不只是增加三个 AXI 外设,而是形成了可持续工作的异构数据路径:
|
||||
CPU 负责控制,DMA 负责搬运,序列器负责批处理,加速器负责计算,CDC 负责隔离不同
|
||||
性能目标的时钟域。共享窗口和本地 endpoint 在端口与延迟约束之间取得平衡;双 Bank
|
||||
和流水 CRC 则面向 5000 组矩阵任务消除了不必要的软件与写回开销。
|
||||
|
||||
本设计在龙芯SoC平台上成功构建了一个集成多种硬件加速器的异构计算系统。
|
||||
已有基线证明方案具备功能与性能潜力,但最终报告应只引用同一 commit 下重新生成的
|
||||
功能、时序、资源、CDC、功耗和板级证据。附带脚本已将这一过程标准化:执行完整回归、
|
||||
保存日志并运行 \code{make metrics all} 后,正文表格和 PDF 会随证据同步更新。
|
||||
|
||||
在AXI总线集成架构方面,以AXI交叉开关为核心互连部件,实现了CPU和DMA双主设备对8个从设备的统一地址空间访问,涵盖外部SRAM控制器、UART串口、DVI显示控制器、GPIO与定时器、DMA配置空间、FFT与ML-KEM共享加速窗口和矩阵乘法器。共享从机的内部二次路由设计——FFT与ML-KEM通过地址高位区分并共享同一端口——在不增加交叉开关端口数量的前提下实现了两种加速器的无缝共存,体现了在资源受限场景下灵活地址规划的工程价值。
|
||||
\appendix
|
||||
\section{关键寄存器摘要}
|
||||
|
||||
在DMA数据搬运体系方面,4通道CDMA控制器与matmul自动序列控制器的协同设计显著降低了CPU在批量计算任务中的参与度。序列控制器将逐组的源数据读取、矩阵计算触发、结果写回和组索引递增等操作固化为硬件状态机的自动循环,CPU仅需在批量任务启动前一次性配置6个序列参数。三条数据路径——mem2mem直接存储搬运、本地Endpoint加速器旁路通信和序列控制器的全自动批量流水——覆盖了SoC中所有的数据传输场景。
|
||||
\begin{table}[H]
|
||||
\centering
|
||||
\small
|
||||
\caption{DMA 与 matmul 序列器寄存器}
|
||||
\begin{tabularx}{\textwidth}{p{2.4cm}p{3cm}Y}
|
||||
\toprule
|
||||
偏移 & 名称 & 含义 \\
|
||||
\midrule
|
||||
\code{n*0x40+0x00} & SRC\_ADDR & DMA 通道 $n$ 源物理地址 \\
|
||||
\code{n*0x40+0x04} & DST\_ADDR & DMA 通道 $n$ 目的物理地址 \\
|
||||
\code{n*0x40+0x08} & LENGTH & 字节数 \\
|
||||
\code{n*0x40+0x10} & CTRL & start、AxSIZE、AxLEN \\
|
||||
\code{n*0x40+0x14} & STATUS & busy、done(W1C) \\
|
||||
\code{n*0x40+0x18} & MODE & mem2mem / mem2ep / ep2mem \\
|
||||
\code{0x200} & SEQ\_CTRL & 批处理 start \\
|
||||
\code{0x204} & SEQ\_STATUS & busy、done(W1C) \\
|
||||
\code{0x208--0x21C} & SEQ 参数 & 源/目的基址、组数、matmul 基址、stride \\
|
||||
\bottomrule
|
||||
\end{tabularx}
|
||||
\end{table}
|
||||
|
||||
在赛事任务完成度方面,矩阵乘法加速器严格遵循赛事规范:不使用DSP资源而全部以LUT逻辑实现32乘32-bit乘法;支持4乘4最大矩阵规模并兼容更小的非方阵规模(通过填零对齐);输出结果按66-bit精度拆分为3个32-bit字的标准格式;输入输出数据布局完全符合128字节源组和192字节结果组的赛事定义。CRC32校验、批量序列测试和系统级仿真验证共同确认了加速器设计的完整正确性。
|
||||
\section{复现入口}
|
||||
详细顺序、预期输出和文件放置方式见同目录
|
||||
\code{REPORT\_RERUN\_CHECKLIST.md}。常用入口如下:
|
||||
|
||||
在后量子密码硬件集成方面,ML-KEM加速器通过共享AXI从机与FFT复用Slave 6端口,在不增加Crossbar端口数量的前提下完整实现了FIPS 203标准的全部三种操作和三种安全等级的硬件加速。三路径诊断测试(软件参考、CPU寄存器、DMA窗口各100次迭代,覆盖ML-KEM-512/768/1024三种等级)全部通过,验证了IP核在真实SoC环境中的功能正确性和软硬件接口的长期运行稳定性。
|
||||
|
||||
\begin{thebibliography}{10}
|
||||
|
||||
\bibitem{loongson_soc}
|
||||
龙芯中科技术有限公司.
|
||||
\emph{龙芯杯全国大学生系统能力培养大赛——实验平台开发手册}.
|
||||
2024.
|
||||
|
||||
\bibitem{fips203}
|
||||
National Institute of Standards and Technology.
|
||||
\emph{FIPS 203: Module-Lattice-Based Key-Encapsulation Mechanism Standard}.
|
||||
August 2024.
|
||||
|
||||
\bibitem{artix7}
|
||||
Xilinx Inc.
|
||||
\emph{7 Series FPGAs Data Sheet: Overview (DS180)}.
|
||||
v2.6, 2020.
|
||||
|
||||
\bibitem{vivado}
|
||||
Xilinx Inc.
|
||||
\emph{Vivado Design Suite User Guide: Synthesis (UG901)}.
|
||||
v2019.2, 2019.
|
||||
|
||||
\bibitem{axi_spec}
|
||||
ARM Ltd.
|
||||
\emph{AMBA AXI and ACE Protocol Specification}.
|
||||
Issue H, 2013.
|
||||
\begin{lstlisting}[language=bash]
|
||||
cd doc/reports/system
|
||||
./tools/run_report_tests.sh lint
|
||||
./tools/run_report_tests.sh soc
|
||||
./tools/run_report_tests.sh implement
|
||||
./tools/run_report_tests.sh refresh
|
||||
\end{lstlisting}
|
||||
|
||||
\begin{thebibliography}{9}
|
||||
\bibitem{fips203} National Institute of Standards and Technology,
|
||||
\emph{FIPS 203: Module-Lattice-Based Key-Encapsulation Mechanism Standard}, 2024.
|
||||
\bibitem{loongson} 龙芯中科技术有限公司,\emph{龙芯 SoC 实验平台开发手册},2024。
|
||||
\bibitem{xilinxcdc} AMD Xilinx, \emph{UG906: Vivado Design Analysis and Closure Techniques}.
|
||||
\bibitem{sdf} S. He and M. Torkelson, ``Designing pipeline FFT processor for OFDM,''
|
||||
URSI International Symposium on Signals, Systems, and Electronics, 1998.
|
||||
\end{thebibliography}
|
||||
|
||||
\end{document}
|
||||
|
||||
45
doc/reports/system/tools/collect_report_evidence.tcl
Normal file
45
doc/reports/system/tools/collect_report_evidence.tcl
Normal file
@@ -0,0 +1,45 @@
|
||||
# Usage from repository root:
|
||||
# vivado -mode batch -source doc/reports/system/tools/collect_report_evidence.tcl
|
||||
|
||||
set script_dir [file dirname [file normalize [info script]]]
|
||||
set repo_root [file normalize [file join $script_dir ../../../..]]
|
||||
set xpr [file join $repo_root fpga project Loongson_Soc.xpr]
|
||||
set out_dir [file join $repo_root doc reports system evidence vivado]
|
||||
file mkdir $out_dir
|
||||
|
||||
if {![file exists $xpr]} {
|
||||
puts stderr "ERROR: project not found: $xpr"
|
||||
puts stderr "Run: cd fpga && vivado -mode batch -source create_project.tcl"
|
||||
exit 2
|
||||
}
|
||||
|
||||
open_project $xpr
|
||||
set run [get_runs impl_1]
|
||||
if {[get_property PROGRESS $run] ne "100%"} {
|
||||
puts stderr "ERROR: impl_1 is not complete; run fpga/run_impl.tcl first"
|
||||
close_project
|
||||
exit 3
|
||||
}
|
||||
|
||||
open_run impl_1
|
||||
report_timing_summary -delay_type min_max -report_unconstrained -check_timing_verbose \
|
||||
-max_paths 20 -input_pins -file [file join $out_dir timing_summary_full.rpt]
|
||||
report_utilization -hierarchical -hierarchical_depth 4 \
|
||||
-file [file join $out_dir utilization_hierarchical.rpt]
|
||||
report_utilization -file [file join $out_dir utilization.rpt]
|
||||
report_power -file [file join $out_dir power.rpt]
|
||||
report_clock_interaction -delay_type min_max -file [file join $out_dir clock_interaction.rpt]
|
||||
report_cdc -details -file [file join $out_dir cdc.rpt]
|
||||
report_drc -file [file join $out_dir drc.rpt]
|
||||
report_methodology -file [file join $out_dir methodology.rpt]
|
||||
report_route_status -file [file join $out_dir route_status.rpt]
|
||||
|
||||
set fp [open [file join $out_dir revision.txt] w]
|
||||
puts $fp "Evidence generated from implemented design."
|
||||
puts $fp "Vivado version: [version -short]"
|
||||
puts $fp "Project: $xpr"
|
||||
close $fp
|
||||
|
||||
close_project
|
||||
puts "Report evidence written to: $out_dir"
|
||||
exit 0
|
||||
46
doc/reports/system/tools/run_fresh_impl.tcl
Normal file
46
doc/reports/system/tools/run_fresh_impl.tcl
Normal file
@@ -0,0 +1,46 @@
|
||||
# Force a clean implementation and generate the bitstream used by board tests.
|
||||
|
||||
set script_dir [file dirname [file normalize [info script]]]
|
||||
set repo_root [file normalize [file join $script_dir ../../../..]]
|
||||
set xpr [file join $repo_root fpga project Loongson_Soc.xpr]
|
||||
set dsp_report [file join $repo_root fpga project dsp_utilization.rpt]
|
||||
|
||||
if {![file exists $xpr]} {
|
||||
puts stderr "ERROR: project not found: $xpr"
|
||||
exit 2
|
||||
}
|
||||
|
||||
open_project $xpr
|
||||
reset_run synth_1
|
||||
launch_runs synth_1
|
||||
wait_on_run synth_1
|
||||
|
||||
set synth_run [get_runs synth_1]
|
||||
set synth_progress [get_property PROGRESS $synth_run]
|
||||
set synth_status [get_property STATUS $synth_run]
|
||||
puts "synth_1 progress: $synth_progress"
|
||||
puts "synth_1 status: $synth_status"
|
||||
if {$synth_progress ne "100%" || [string first "ERROR" [string toupper $synth_status]] >= 0} {
|
||||
puts stderr "ERROR: fresh synthesis failed"
|
||||
close_project
|
||||
exit 3
|
||||
}
|
||||
|
||||
launch_runs impl_1 -to_step write_bitstream
|
||||
wait_on_run impl_1
|
||||
|
||||
set run [get_runs impl_1]
|
||||
set progress [get_property PROGRESS $run]
|
||||
set status [get_property STATUS $run]
|
||||
puts "impl_1 progress: $progress"
|
||||
puts "impl_1 status: $status"
|
||||
if {$progress ne "100%" || [string first "ERROR" [string toupper $status]] >= 0} {
|
||||
puts stderr "ERROR: fresh implementation failed"
|
||||
close_project
|
||||
exit 4
|
||||
}
|
||||
|
||||
open_run impl_1
|
||||
report_utilization -file $dsp_report
|
||||
close_project
|
||||
exit 0
|
||||
137
doc/reports/system/tools/run_report_tests.sh
Executable file
137
doc/reports/system/tools/run_report_tests.sh
Executable file
@@ -0,0 +1,137 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
# Vivado 2019.2 invokes the host gcc while elaborating XSIM-generated C.
|
||||
# A read-only ccache directory otherwise surfaces only as XSIM 43-3409.
|
||||
export CCACHE_DISABLE=1
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
ROOT="$(cd "$SCRIPT_DIR/../../../.." && pwd)"
|
||||
REPORT_DIR="$ROOT/doc/reports/system"
|
||||
EVIDENCE_DIR="$REPORT_DIR/evidence"
|
||||
SOFTWARE_EXAMPLES=(asm fft fft_dma mlkem_test mlkem_hello)
|
||||
|
||||
find_vivado() {
|
||||
if [[ -n "${VIVADO:-}" && -x "${VIVADO}" ]]; then
|
||||
printf '%s\n' "$VIVADO"
|
||||
return 0
|
||||
fi
|
||||
if command -v vivado >/dev/null 2>&1; then
|
||||
command -v vivado
|
||||
return 0
|
||||
fi
|
||||
|
||||
local candidate
|
||||
for candidate in \
|
||||
/opt/Xilinx/Vivado/2019.2/bin/vivado \
|
||||
/tools/Xilinx/Vivado/2019.2/bin/vivado; do
|
||||
if [[ -x "$candidate" ]]; then
|
||||
printf '%s\n' "$candidate"
|
||||
return 0
|
||||
fi
|
||||
done
|
||||
|
||||
return 1
|
||||
}
|
||||
|
||||
require_vivado() {
|
||||
if ! VIVADO_BIN="$(find_vivado)"; then
|
||||
echo "ERROR: Vivado not found." >&2
|
||||
echo "Set VIVADO=/path/to/Vivado/bin/vivado or source Vivado settings64.sh." >&2
|
||||
exit 127
|
||||
fi
|
||||
}
|
||||
|
||||
usage() {
|
||||
cat <<'EOF'
|
||||
Usage: run_report_tests.sh <command>
|
||||
|
||||
Commands:
|
||||
prepare Create evidence directories and Vivado project if absent
|
||||
lint Run the project-aware Verilator lint check
|
||||
build Build asm, fft, fft_dma, mlkem_test and mlkem_hello
|
||||
soc Build all five; simulate all except board-only asm
|
||||
implement Run Vivado implementation and collect PPA/CDC/DRC reports
|
||||
refresh Parse collected evidence, regenerate figures and build the PDF
|
||||
quick Run lint and refresh only; does not run Vivado implementation
|
||||
|
||||
Board-only tests (matmul official 5000-group timing and reset behavior) are
|
||||
listed in REPORT_RERUN_CHECKLIST.md and are intentionally not automated here.
|
||||
EOF
|
||||
}
|
||||
|
||||
prepare() {
|
||||
mkdir -p "$EVIDENCE_DIR/build" "$EVIDENCE_DIR/soc" \
|
||||
"$EVIDENCE_DIR/board" "$EVIDENCE_DIR/vivado"
|
||||
if [[ ! -f "$ROOT/fpga/project/Loongson_Soc.xpr" ]]; then
|
||||
require_vivado
|
||||
(cd "$ROOT/fpga" && "$VIVADO_BIN" -mode batch -source create_project.tcl)
|
||||
fi
|
||||
}
|
||||
|
||||
lint() {
|
||||
prepare
|
||||
python3 "$ROOT/fpga/run-linter.py" "$ROOT/fpga/project/Loongson_Soc.xpr" \
|
||||
2>&1 | tee "$EVIDENCE_DIR/lint.log"
|
||||
}
|
||||
|
||||
build_example() {
|
||||
local example="$1"
|
||||
local example_dir="$ROOT/sdk/software/examples/$example"
|
||||
local build_log="$EVIDENCE_DIR/build/$example.log"
|
||||
make -C "$example_dir" LA32RSOC_WINDOWS_HOME="$ROOT" clean \
|
||||
2>&1 | tee "$build_log"
|
||||
make -C "$example_dir" LA32RSOC_WINDOWS_HOME="$ROOT" all \
|
||||
2>&1 | tee -a "$build_log"
|
||||
}
|
||||
|
||||
build() {
|
||||
prepare
|
||||
local example
|
||||
for example in "${SOFTWARE_EXAMPLES[@]}"; do
|
||||
build_example "$example"
|
||||
done
|
||||
}
|
||||
|
||||
run_soc_case() {
|
||||
local example="$1"
|
||||
require_vivado
|
||||
build_example "$example"
|
||||
(cd "$ROOT/fpga" && "$VIVADO_BIN" -mode batch \
|
||||
-source "$SCRIPT_DIR/run_soc_sim.tcl") 2>&1 | tee "$EVIDENCE_DIR/soc/$example.log"
|
||||
}
|
||||
|
||||
soc() {
|
||||
prepare
|
||||
build_example asm
|
||||
run_soc_case fft
|
||||
run_soc_case fft_dma
|
||||
run_soc_case mlkem_test
|
||||
run_soc_case mlkem_hello
|
||||
}
|
||||
|
||||
implement() {
|
||||
prepare
|
||||
require_vivado
|
||||
"$VIVADO_BIN" -mode batch -source "$SCRIPT_DIR/run_fresh_impl.tcl" \
|
||||
2>&1 | tee "$EVIDENCE_DIR/vivado/implementation.log"
|
||||
"$VIVADO_BIN" -mode batch -source "$SCRIPT_DIR/collect_report_evidence.tcl" \
|
||||
2>&1 | tee "$EVIDENCE_DIR/vivado/collection.log"
|
||||
python3 "$ROOT/fpga/check_dsp.py" "$ROOT/fpga/project/dsp_utilization.rpt" 0 \
|
||||
2>&1 | tee "$EVIDENCE_DIR/vivado/dsp_check.log"
|
||||
}
|
||||
|
||||
refresh() {
|
||||
make -C "$REPORT_DIR" metrics all
|
||||
}
|
||||
|
||||
case "${1:-}" in
|
||||
prepare) prepare ;;
|
||||
lint) lint ;;
|
||||
build) build ;;
|
||||
soc) soc ;;
|
||||
implement) implement ;;
|
||||
refresh) refresh ;;
|
||||
quick) lint; refresh ;;
|
||||
*) usage; exit 2 ;;
|
||||
esac
|
||||
41
doc/reports/system/tools/run_soc_sim.tcl
Normal file
41
doc/reports/system/tools/run_soc_sim.tcl
Normal file
@@ -0,0 +1,41 @@
|
||||
# Runs the currently built sdk/axi_ram.mif with the generic SoC testbench.
|
||||
# The caller is responsible for building the desired software example first.
|
||||
|
||||
set script_dir [file dirname [file normalize [info script]]]
|
||||
set repo_root [file normalize [file join $script_dir ../../../..]]
|
||||
set xpr [file join $repo_root fpga project Loongson_Soc.xpr]
|
||||
|
||||
# xelab inherits this when compiling generated C files with the host gcc.
|
||||
set ::env(CCACHE_DISABLE) 1
|
||||
|
||||
# Vivado 2019.2's bundled clang needs the matching 64-bit ncurses/tinfo pair.
|
||||
# On hosts that also carry /lib/libtinfo.so.5, the loader may otherwise mix it
|
||||
# with /lib64/libncurses.so.5 and fail with an unresolved _nc_tiparm symbol.
|
||||
if {[info exists ::env(LD_LIBRARY_PATH)] && $::env(LD_LIBRARY_PATH) ne ""} {
|
||||
append ::env(LD_LIBRARY_PATH) ":/lib64"
|
||||
} else {
|
||||
set ::env(LD_LIBRARY_PATH) "/lib64"
|
||||
}
|
||||
|
||||
if {![file exists $xpr]} {
|
||||
puts stderr "ERROR: project not found: $xpr"
|
||||
exit 2
|
||||
}
|
||||
if {![file exists [file join $repo_root sdk axi_ram.mif]]} {
|
||||
puts stderr "ERROR: sdk/axi_ram.mif not found; build a software example first"
|
||||
exit 3
|
||||
}
|
||||
|
||||
open_project $xpr
|
||||
set simset [get_filesets sim_1]
|
||||
set_property top tb_top $simset
|
||||
set_property incremental false $simset
|
||||
set_property xsim.elaborate.mt_level off $simset
|
||||
set_property xsim.elaborate.debug_level off $simset
|
||||
set_property xsim.simulate.log_all_signals false $simset
|
||||
update_compile_order -fileset sim_1
|
||||
launch_simulation
|
||||
run all
|
||||
close_sim
|
||||
close_project
|
||||
exit 0
|
||||
175
doc/reports/system/tools/update_report_metrics.py
Executable file
175
doc/reports/system/tools/update_report_metrics.py
Executable file
@@ -0,0 +1,175 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Refresh report_metrics.tex from versioned Vivado and test evidence."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import datetime as dt
|
||||
import re
|
||||
import subprocess
|
||||
from pathlib import Path
|
||||
|
||||
REPORT_DIR = Path(__file__).resolve().parent.parent
|
||||
ROOT = REPORT_DIR.parents[2]
|
||||
METRICS = REPORT_DIR / "report_metrics.tex"
|
||||
|
||||
|
||||
def read_first(*paths: Path) -> str:
|
||||
for path in paths:
|
||||
if path.is_file():
|
||||
return path.read_text(encoding="utf-8", errors="replace")
|
||||
return ""
|
||||
|
||||
|
||||
def set_macro(text: str, name: str, value: str) -> str:
|
||||
pattern = re.compile(r"(\\newcommand\{\\" + re.escape(name) + r"\}\{)[^\n}]*(\})")
|
||||
if not pattern.search(text):
|
||||
raise ValueError(f"macro not found: {name}")
|
||||
return pattern.sub(lambda m: m.group(1) + value + m.group(2), text, count=1)
|
||||
|
||||
|
||||
def number(value: str) -> str:
|
||||
raw = value.replace(",", "").strip()
|
||||
if re.fullmatch(r"-?\d+", raw):
|
||||
return f"{int(raw):,}"
|
||||
return raw
|
||||
|
||||
|
||||
def capture(pattern: str, text: str, group: int = 1) -> str | None:
|
||||
match = re.search(pattern, text, re.MULTILINE)
|
||||
return match.group(group).strip() if match else None
|
||||
|
||||
|
||||
def update_timing(metrics: str, timing: str) -> str:
|
||||
summary = capture(r"^\s*(-?\d+\.\d+)\s+-?\d+\.\d+\s+\d+\s+\d+\s+", timing)
|
||||
if summary:
|
||||
metrics = set_macro(metrics, "OverallWNS", summary)
|
||||
for clock, prefix in (("cpu_clk", "Cpu"), ("sys_clk", "Sys"), ("mlkem_clk", "Mlkem")):
|
||||
freq = capture(r"^\s*" + clock + r"\s+\{[^\n]+?\}\s+\d+\.\d+\s+(\d+\.\d+)", timing)
|
||||
wns = capture(r"^\s*" + clock + r"\s+(-?\d+\.\d+)\s+", timing)
|
||||
if freq:
|
||||
metrics = set_macro(metrics, prefix + "FreqMHz", freq)
|
||||
if wns:
|
||||
metrics = set_macro(metrics, prefix + "WNS", wns)
|
||||
return metrics
|
||||
|
||||
|
||||
def update_util(metrics: str, util: str) -> str:
|
||||
rows = {
|
||||
"SliceLUTs": r"^\| Slice LUTs\s+\|\s*([\d,]+)\s*\|",
|
||||
"SliceFFs": r"^\| Slice Registers\s+\|\s*([\d,]+)\s*\|",
|
||||
"BramTiles": r"^\| Block RAM Tile\s+\|\s*([\d.]+)\s*\|",
|
||||
"DspCount": r"^\| DSPs?\s+\|\s*([\d,]+)\s*\|",
|
||||
}
|
||||
pct = {
|
||||
"SliceLUTPct": r"^\| Slice LUTs\s+\|[^\n]*\|\s*([\d.]+)\s*\|\s*$",
|
||||
"SliceFFPct": r"^\| Slice Registers\s+\|[^\n]*\|\s*([\d.]+)\s*\|\s*$",
|
||||
"BramPct": r"^\| Block RAM Tile\s+\|[^\n]*\|\s*([\d.]+)\s*\|\s*$",
|
||||
}
|
||||
for name, pattern in rows.items():
|
||||
value = capture(pattern, util)
|
||||
if value:
|
||||
metrics = set_macro(metrics, name, number(value))
|
||||
for name, pattern in pct.items():
|
||||
value = capture(pattern, util)
|
||||
if value:
|
||||
metrics = set_macro(metrics, name, value + r"\%")
|
||||
return metrics
|
||||
|
||||
|
||||
def update_power(metrics: str, power: str) -> str:
|
||||
mappings = {
|
||||
"TotalPower": r"\| Total On-Chip Power \(W\)\s*\|\s*([\d.]+)",
|
||||
"DynamicPower": r"\| Dynamic \(W\)\s*\|\s*([\d.]+)",
|
||||
"StaticPower": r"\| Device Static \(W\)\s*\|\s*([\d.]+)",
|
||||
}
|
||||
for name, pattern in mappings.items():
|
||||
value = capture(pattern, power)
|
||||
if value:
|
||||
metrics = set_macro(metrics, name, value)
|
||||
confidence = capture(r"\| Confidence Level\s*\|\s*([^|]+)\|", power)
|
||||
if confidence:
|
||||
metrics = set_macro(metrics, "PowerConfidence", confidence + "(未回标活动率)")
|
||||
return metrics
|
||||
|
||||
|
||||
def update_mlkem(metrics: str, log: str) -> str:
|
||||
names = {
|
||||
("512", "SW"): "MlkemFiveTwelveSW",
|
||||
("512", "CPU"): "MlkemFiveTwelveCPU",
|
||||
("512", "DMA"): "MlkemFiveTwelveDMA",
|
||||
("768", "SW"): "MlkemSevenSixtyEightSW",
|
||||
("768", "CPU"): "MlkemSevenSixtyEightCPU",
|
||||
("768", "DMA"): "MlkemSevenSixtyEightDMA",
|
||||
("1024", "SW"): "MlkemTenTwentyFourSW",
|
||||
("1024", "CPU"): "MlkemTenTwentyFourCPU",
|
||||
("1024", "DMA"): "MlkemTenTwentyFourDMA",
|
||||
}
|
||||
for (level, path), name in names.items():
|
||||
value = capture(rf"^MLKEM{level}_{path}_ONLY_AVG_CYCLES=(\d+)$", log)
|
||||
if value:
|
||||
metrics = set_macro(metrics, name, number(value))
|
||||
return metrics
|
||||
|
||||
|
||||
def update_fft(metrics: str, cpu_log: str, dma_log: str) -> str:
|
||||
cpu_ns = capture(r"Hardware FFT Time:\s*(\d+) ns", cpu_log)
|
||||
dma_ns = capture(r"Hardware FFT Time:\s*(\d+) ns", dma_log)
|
||||
sw_ns = capture(r"Software FFT Time:\s*(\d+) ns", dma_log or cpu_log)
|
||||
cpu_speed = capture(r"Speedup Ratio\s*:\s*([\d.]+)x", cpu_log)
|
||||
dma_speed = capture(r"Speedup Ratio\s*:\s*([\d.]+)x", dma_log)
|
||||
for name, value in (("FftCpuNs", cpu_ns), ("FftDmaNs", dma_ns), ("FftSoftwareNs", sw_ns)):
|
||||
if value:
|
||||
metrics = set_macro(metrics, name, number(value))
|
||||
for name, value in (("FftCpuSpeedup", cpu_speed), ("FftDmaSpeedup", dma_speed)):
|
||||
if value:
|
||||
metrics = set_macro(metrics, name, value)
|
||||
err_match = re.search(r"Max Abs Error\s*:\s*Re=(\d+),\s*Im=(\d+)", dma_log)
|
||||
if err_match:
|
||||
metrics = set_macro(metrics, "FftMaxError", f"Re={err_match.group(1)},Im={err_match.group(2)}")
|
||||
return metrics
|
||||
|
||||
|
||||
def update_matmul(metrics: str, log: str) -> str:
|
||||
elapsed = capture(r"Elapsed time:\s*([\d.]+) ms", log)
|
||||
score = capture(r"Score percent:\s*([\d.]+)", log)
|
||||
if elapsed:
|
||||
metrics = set_macro(metrics, "MatmulBoardMs", elapsed)
|
||||
if score:
|
||||
metrics = set_macro(metrics, "MatmulScore", score + r"\%")
|
||||
if "CRC32 matched." in log:
|
||||
metrics = set_macro(metrics, "MatmulCrcStatus", "PASS")
|
||||
return metrics
|
||||
|
||||
|
||||
def main() -> None:
|
||||
metrics = METRICS.read_text(encoding="utf-8")
|
||||
vivado = REPORT_DIR / "evidence" / "vivado"
|
||||
legacy = ROOT / "fpga" / "vivado_reports_nodsp"
|
||||
metrics = update_timing(metrics, read_first(vivado / "timing_summary_full.rpt", legacy / "timing_summary_full.rpt"))
|
||||
metrics = update_util(metrics, read_first(vivado / "utilization.rpt", legacy / "utilization.rpt"))
|
||||
metrics = update_power(metrics, read_first(vivado / "power.rpt", legacy / "power.rpt"))
|
||||
|
||||
soc = REPORT_DIR / "evidence" / "soc"
|
||||
metrics = update_mlkem(metrics, read_first(soc / "mlkem_test.log", ROOT / "sdk/software/examples/mlkem_test/result.txt"))
|
||||
metrics = update_fft(
|
||||
metrics,
|
||||
read_first(soc / "fft.log", ROOT / "sdk/software/examples/fft/result.txt"),
|
||||
read_first(soc / "fft_dma.log", ROOT / "sdk/software/examples/fft_dma/result.txt"),
|
||||
)
|
||||
metrics = update_matmul(metrics, read_first(REPORT_DIR / "evidence" / "board" / "matmul_official.log"))
|
||||
|
||||
try:
|
||||
revision = subprocess.check_output(
|
||||
["git", "-C", str(ROOT), "rev-parse", "--short", "HEAD"], text=True
|
||||
).strip()
|
||||
except (OSError, subprocess.CalledProcessError):
|
||||
revision = "unknown"
|
||||
source = "当前 evidence" if (vivado / "timing_summary_full.rpt").is_file() else "历史报告,待复测"
|
||||
metrics = set_macro(metrics, "EvidenceRevision", f"{revision}({source})")
|
||||
metrics = set_macro(metrics, "EvidenceDate", dt.date.today().isoformat())
|
||||
METRICS.write_text(metrics, encoding="utf-8")
|
||||
print(f"updated {METRICS}")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user