update reports

This commit is contained in:
2026-07-15 18:22:17 +08:00
parent 62a4e07037
commit d6873b5aed
16 changed files with 1407 additions and 45 deletions

View File

@@ -0,0 +1,863 @@
#!/usr/bin/env python3
"""Build the defense deck from the official CICIEC PPT template."""
from __future__ import annotations
import sys
from io import BytesIO
from pathlib import Path
from zipfile import ZipFile
try:
from pptx import Presentation
except ModuleNotFoundError:
sys.path.insert(0, "/tmp/codex_pptx")
from pptx import Presentation
from PIL import Image
from pptx.dml.color import RGBColor
from pptx.enum.shapes import MSO_SHAPE
from pptx.enum.text import MSO_ANCHOR, PP_ALIGN
from pptx.util import Inches, Pt
ROOT = Path(__file__).resolve().parents[3]
HERE = Path(__file__).resolve().parent
SYSTEM = ROOT / "doc/reports/system"
MLKEM = ROOT / "doc/reports/mlkem_ip"
TEMPLATE = HERE / "ppt_template.pptx"
OUTPUT = HERE / "defense_presentation.pptx"
W = 13.333
H = 7.5
# Template typefaces and palette.
FONT = "HarmonyOS Sans SC"
TITLE_FONT = "Alimama ShuHeiTi Bold"
LATIN_FONT = "Arial"
INK = "202533"
MUTED = "667085"
BLUE = "142DA8"
ACCENT_BLUE = "4874CB"
ORANGE = "EE822F"
YELLOW = "F2BA02"
GREEN = "75BD42"
TEAL = "30C0B4"
RED = "E54C5E"
WHITE = "FFFFFF"
PALE_BLUE = "EDF3FF"
PALE_ORANGE = "FFF3E9"
PALE_TEAL = "EAF9F7"
PALE_GRAY = "F5F7FA"
LINE = "D7DEEA"
def color(value: str) -> RGBColor:
return RGBColor.from_string(value)
with ZipFile(TEMPLATE) as archive:
MEDIA = {
name.rsplit("/", 1)[-1]: archive.read(name)
for name in archive.namelist()
if name.startswith("ppt/media/") and not name.endswith("/")
}
prs = Presentation(str(TEMPLATE))
while len(prs.slides):
sld_id = prs.slides._sldIdLst[0]
prs.part.drop_rel(sld_id.rId)
del prs.slides._sldIdLst[0]
blank = prs.slide_layouts[6]
def add_template_image(slide, media_name, x, y, w, h):
return slide.shapes.add_picture(
BytesIO(MEDIA[media_name]), Inches(x), Inches(y), Inches(w), Inches(h)
)
def add_rect(slide, x, y, w, h, fill=WHITE, line=None, radius=False):
shape_type = MSO_SHAPE.ROUNDED_RECTANGLE if radius else MSO_SHAPE.RECTANGLE
shape = slide.shapes.add_shape(shape_type, Inches(x), Inches(y), Inches(w), Inches(h))
shape.fill.solid()
shape.fill.fore_color.rgb = color(fill)
if line:
shape.line.color.rgb = color(line)
shape.line.width = Pt(1)
else:
shape.line.fill.background()
if radius:
try:
shape.adjustments[0] = 0.06
except Exception:
pass
return shape
def add_text(
slide,
text,
x,
y,
w,
h,
size=17,
fill=INK,
bold=False,
font=FONT,
align=PP_ALIGN.LEFT,
valign=MSO_ANCHOR.TOP,
margin=0.02,
line_spacing=1.05,
):
shape = slide.shapes.add_textbox(Inches(x), Inches(y), Inches(w), Inches(h))
frame = shape.text_frame
frame.clear()
frame.margin_left = frame.margin_right = Inches(margin)
frame.margin_top = frame.margin_bottom = Inches(margin)
frame.vertical_anchor = valign
para = frame.paragraphs[0]
para.text = text
para.alignment = align
para.space_after = Pt(0)
para.line_spacing = line_spacing
para.font.name = font
para.font.size = Pt(size)
para.font.bold = bold
para.font.color.rgb = color(fill)
return shape
def add_rich(slide, runs, x, y, w, h, align=PP_ALIGN.LEFT):
shape = slide.shapes.add_textbox(Inches(x), Inches(y), Inches(w), Inches(h))
frame = shape.text_frame
frame.clear()
frame.margin_left = frame.margin_right = Inches(0.02)
frame.margin_top = frame.margin_bottom = Inches(0.02)
para = frame.paragraphs[0]
para.alignment = align
for text, size, bold, fill, font in runs:
run = para.add_run()
run.text = text
run.font.name = font
run.font.size = Pt(size)
run.font.bold = bold
run.font.color.rgb = color(fill)
return shape
def add_bullets(slide, items, x, y, w, h, size=15, fill=INK, gap=5, accent=BLUE):
shape = slide.shapes.add_textbox(Inches(x), Inches(y), Inches(w), Inches(h))
frame = shape.text_frame
frame.clear()
frame.margin_left = frame.margin_right = Inches(0.02)
frame.margin_top = frame.margin_bottom = Inches(0.02)
for index, item in enumerate(items):
para = frame.paragraphs[0] if index == 0 else frame.add_paragraph()
para.text = "• " + item
para.font.name = FONT
para.font.size = Pt(size)
para.font.color.rgb = color(fill)
para.space_after = Pt(gap)
para.line_spacing = 1.06
return shape
def add_arrow(slide, x, y, w, h, fill=BLUE):
shape = slide.shapes.add_shape(MSO_SHAPE.RIGHT_ARROW, Inches(x), Inches(y), Inches(w), Inches(h))
shape.fill.solid()
shape.fill.fore_color.rgb = color(fill)
shape.line.fill.background()
return shape
def add_line(slide, x, y, w, h=0.02, fill=LINE):
return add_rect(slide, x, y, w, h, fill)
def add_image_contain(slide, path: Path, x, y, w, h, frame=True):
if frame:
add_rect(slide, x - 0.03, y - 0.03, w + 0.06, h + 0.06, WHITE, LINE)
with Image.open(path) as image:
iw, ih = image.size
scale = min(w / iw, h / ih)
pw, ph = iw * scale, ih * scale
return slide.shapes.add_picture(
str(path), Inches(x + (w - pw) / 2), Inches(y + (h - ph) / 2), Inches(pw), Inches(ph)
)
def set_cell(cell, text, size=12, bold=False, fill=INK, bg=None, align=PP_ALIGN.LEFT):
if bg:
cell.fill.solid()
cell.fill.fore_color.rgb = color(bg)
frame = cell.text_frame
frame.clear()
frame.margin_left = frame.margin_right = Inches(0.07)
frame.margin_top = frame.margin_bottom = Inches(0.03)
frame.vertical_anchor = MSO_ANCHOR.MIDDLE
para = frame.paragraphs[0]
para.text = text
para.alignment = align
para.font.name = LATIN_FONT if "seed" in text else FONT
para.font.size = Pt(size)
para.font.bold = bold
para.font.color.rgb = color(fill)
def add_table(slide, rows, x, y, w, h, widths=None, size=12):
shape = slide.shapes.add_table(len(rows), len(rows[0]), Inches(x), Inches(y), Inches(w), Inches(h))
table = shape.table
if widths:
for index, width in enumerate(widths):
table.columns[index].width = Inches(width)
for r, row in enumerate(rows):
for c, value in enumerate(row):
set_cell(
table.cell(r, c),
value,
size=size,
bold=r == 0,
fill=WHITE if r == 0 else INK,
bg=BLUE if r == 0 else (PALE_GRAY if r % 2 == 0 else WHITE),
align=PP_ALIGN.CENTER if c else PP_ALIGN.LEFT,
)
return table
def add_metric(slide, value, label, x, y, w, accent=BLUE, suffix=""):
add_rich(
slide,
[(value, 24, True, accent, TITLE_FONT), (suffix, 13, False, accent, LATIN_FONT)],
x,
y,
w,
0.45,
PP_ALIGN.CENTER,
)
add_text(slide, label, x, y + 0.48, w, 0.45, 11, MUTED, align=PP_ALIGN.CENTER)
def add_number_badge(slide, number, x, y, fill=BLUE):
add_rect(slide, x, y, 0.47, 0.47, fill, radius=True)
add_text(slide, number, x, y + 0.08, 0.47, 0.22, 12, WHITE, True, font=LATIN_FONT, align=PP_ALIGN.CENTER)
def new_content_slide(title, number, subtitle=None):
slide = prs.slides.add_slide(blank)
add_template_image(slide, "image1.png", 0, 0, W, H)
add_template_image(slide, "image2.png", 0.28, -0.02, 1.82, 1.12)
add_text(slide, title, 2.4, 0.48, 8.55, 0.55, 24, BLUE, True, TITLE_FONT, PP_ALIGN.CENTER)
add_rect(slide, 6.17, 1.17, 0.98, 0.07, ORANGE)
if subtitle:
add_text(slide, subtitle, 10.9, 0.53, 1.9, 0.28, 9.5, MUTED, align=PP_ALIGN.RIGHT)
add_text(slide, str(number), 12.35, 6.98, 0.72, 0.3, 9, MUTED, font=LATIN_FONT, align=PP_ALIGN.LEFT)
return slide
def new_preview_slide(number, label):
slide = prs.slides.add_slide(blank)
add_template_image(slide, "image1.png", 0, 0, W, H)
add_template_image(slide, "image2.png", 0.3, -0.02, 1.82, 1.12)
add_text(slide, "作品重要亮点预览页", 3.2, 0.48, 6.95, 0.56, 25, BLUE, True, TITLE_FONT, PP_ALIGN.CENTER)
add_rect(slide, 6.17, 1.17, 0.98, 0.07, ORANGE)
add_text(slide, label, 10.85, 0.53, 1.95, 0.28, 10, ORANGE, True, align=PP_ALIGN.RIGHT)
add_text(slide, str(number), 12.35, 6.98, 0.72, 0.3, 9, MUTED, font=LATIN_FONT, align=PP_ALIGN.LEFT)
return slide
# 1. Cover
slide = prs.slides.add_slide(blank)
add_template_image(slide, "image3.png", 0, 0, W, H)
add_template_image(slide, "image4.png", 5.93, 0.65, 1.48, 1.48)
add_text(slide, "第十届全国大学生集成电路创新创业大赛", 1.15, 2.2, 11.05, 0.48, 24, BLUE, True, TITLE_FONT, PP_ALIGN.CENTER)
add_text(slide, "基于龙芯SoC的后量子密码与矩阵运算加速系统", 1.1, 2.82, 11.15, 0.78, 30, BLUE, True, TITLE_FONT, PP_ALIGN.CENTER)
add_rect(slide, 4.2, 3.9, 4.93, 0.62, BLUE, radius=True)
add_text(slide, "龙芯中科杯 · 系统设计答辩", 4.33, 4.05, 4.67, 0.26, 16, WHITE, True, align=PP_ALIGN.CENTER)
add_text(slide, "作品编号:CICC1008439 · 团队:碳基生物", 4.25, 5.25, 4.85, 0.34, 13, INK, True, align=PP_ALIGN.CENTER)
add_text(slide, "证据版本 d456463 · 2026-07-15", 4.4, 5.72, 4.55, 0.3, 11, MUTED, align=PP_ALIGN.CENTER)
# 2. Team introduction
slide = new_content_slide("团队介绍:围绕完整系统形成协同分工", 2, "团队与项目")
add_text(slide, "团队名称:碳基生物", 0.88, 1.48, 4.2, 0.36, 16, BLUE, True)
roles = [
(0.82, BLUE, PALE_BLUE, "系统架构与SoC集成", ["OpenLA500平台集成", "AXI、DMA与多时钟域", "SRAM控制器优化"]),
(4.52, TEAL, PALE_TEAL, "密码算法与RTL实现", ["全参数ML-KEM架构", "Keccak、NTT与多项式算子", "常量时间解封装"]),
(8.22, ORANGE, PALE_ORANGE, "软件验证、测试与文档", ["板级驱动与测试程序", "分层仿真和证据采集", "系统报告与答辩材料"]),
]
for x, accent, pale, title, items in roles:
add_rect(slide, x, 2.08, 3.35, 3.72, pale, accent, radius=True)
add_text(slide, title, x + 0.18, 2.38, 2.99, 0.36, 17, accent, True, TITLE_FONT, PP_ALIGN.CENTER)
add_bullets(slide, items, x + 0.32, 3.12, 2.72, 1.85, size=13, gap=10)
add_rect(slide, 1.0, 6.18, 11.15, 0.55, PALE_GRAY, LINE)
add_text(slide, "以统一接口、可重复测试和版本化证据贯通硬件、软件与文档。", 1.2, 6.34, 10.75, 0.24, 13, INK, True, align=PP_ALIGN.CENTER)
# 3. Contents
slide = prs.slides.add_slide(blank)
add_template_image(slide, "image3.png", 0, 0, W, H)
add_text(slide, "目录", 0.82, 0.48, 1.6, 0.55, 29, BLUE, True, TITLE_FONT)
add_text(slide, "CONTENTS", 0.91, 1.15, 1.5, 0.22, 11, MUTED, font=LATIN_FONT)
contents = [
("1", "项目与系统方案", "开发过程、设计目标与系统数据流", BLUE),
("2", "自主计算IP", "Matmul流水与全参数ML-KEM", ORANGE),
("3", "验证与实现结果", "分层验证、资源时序与最终性能", TEAL),
("4", "心得与后续工作", "工程认识、设计取舍与优化路线", GREEN),
]
for i, (num, title, sub, accent) in enumerate(contents):
y = 1.75 + i * 1.2
add_rect(slide, 2.25, y, 0.58, 0.58, accent)
add_text(slide, num, 2.25, y + 0.13, 0.58, 0.24, 15, WHITE, True, align=PP_ALIGN.CENTER)
add_text(slide, title, 3.18, y - 0.02, 4.35, 0.34, 17, INK, True)
add_text(slide, sub, 3.18, y + 0.42, 5.1, 0.3, 12, MUTED)
# 4. Preview: architecture and contributions
slide = new_preview_slide(4, "系统方案")
add_rect(slide, 0.78, 1.52, 7.75, 4.8, WHITE, LINE)
add_image_contain(slide, SYSTEM / "system_arch.png", 0.94, 1.7, 7.43, 4.45, frame=False)
add_text(slide, "OpenLA500 + DMA数据面 + 三个自主计算IP", 0.95, 6.4, 7.4, 0.3, 13, BLUE, True, align=PP_ALIGN.CENTER)
highlights = [
("01", "硬件自治", "五千组 Matmul 任务一次启动完成", BLUE),
("02", "存储优化", "SRAM连续突发达到1 beat/cycle", ORANGE),
("03", "全参数密码", "单比特流覆盖三等级、三操作", TEAL),
("04", "异步协同", "三时钟域通过全五通道AXI FIFO连接", GREEN),
]
for i, (num, title, desc, accent) in enumerate(highlights):
y = 1.62 + i * 1.18
add_number_badge(slide, num, 8.85, y, accent)
add_text(slide, title, 9.48, y - 0.02, 2.55, 0.3, 15, accent, True)
add_text(slide, desc, 9.48, y + 0.37, 2.85, 0.48, 11.5, INK)
add_rect(slide, 8.78, 6.25, 3.72, 0.58, PALE_BLUE, BLUE)
add_text(slide, "全系统 0 DSP · 三时钟域零违例", 8.92, 6.42, 3.44, 0.24, 12, BLUE, True, align=PP_ALIGN.CENTER)
# 5. Preview: measured results
slide = new_preview_slide(5, "关键结果")
metrics = [
("4.911765", "Matmul 5000组最终时间", BLUE, " ms"),
("827.80", "FFT DMA相对软件加速", ORANGE, "×"),
("48.95–53.33", "ML-KEM DMA相对软件加速", TEAL, "×"),
("0", "DSP48E1使用量", GREEN, " DSP"),
]
for i, (value, label, accent, suffix) in enumerate(metrics):
x = 0.72 + i * 3.12
add_rect(slide, x, 1.62, 2.77, 1.2, WHITE, accent, radius=True)
add_metric(slide, value, label, x + 0.12, 1.77, 2.53, accent, suffix)
add_table(
slide,
[
["验证对象", "测试规模", "结果"],
["ML-KEM主路径KAT", "3等级 × 3操作 × 1000", "9000 / 9000"],
["Decaps隐式拒绝", "3等级 × 100", "300 / 300"],
["ML-KEM板级诊断", "3等级 × 3路径 × 100", "900次零失败"],
["Matmul最终评测", "5000组 · seed a3772ec8", "4.911765 ms · 满分"],
["FFT板级验证", "CPU / DMA各3次", "误差Re=1、Im=2"],
],
0.92,
3.18,
7.2,
3.05,
widths=[2.25, 2.9, 2.05],
size=11.5,
)
add_rect(slide, 8.5, 3.18, 3.88, 3.05, PALE_ORANGE, ORANGE, radius=True)
add_text(slide, "实现质量", 8.82, 3.48, 3.2, 0.34, 18, ORANGE, True, TITLE_FONT, PP_ALIGN.CENTER)
add_metric(slide, "55,066", "Slice LUT · 41.16%", 8.85, 4.04, 1.45, BLUE)
add_metric(slide, "29.5", "BRAM Tile · 8.08%", 10.48, 4.04, 1.45, TEAL)
add_metric(slide, "88,843", "时序端点", 8.85, 5.2, 1.45, GREEN)
add_metric(slide, "+0.070", "全系统最小WNS / ns", 10.48, 5.2, 1.45, ORANGE)
# 6. Development process
slide = new_content_slide("开发过程:以端到端证据驱动迭代", 6, "项目与系统方案")
stages = [
("1", "基础SoC跑通", "完成处理器取指、串口及总线外设联调", BLUE),
("2", "计算IP接入", "实现Matmul、CRC32、FFT与全参数ML-KEM", TEAL),
("3", "数据通路优化", "引入DMA端点、双Bank调度和SRAM连续突发", ORANGE),
("4", "时钟与复位收敛", "完成PLL分域、AXI CDC和复位顺序验证", GREEN),
("5", "分层验证闭环", "建立叶模块、IP、SoC与板级四层证据", BLUE),
("6", "最终性能迭代", "压缩启动与稳态空闲,Matmul评测达到100分", ORANGE),
]
for i, (num, title, desc, accent) in enumerate(stages):
col, row = i % 3, i // 3
x, y = 0.78 + col * 4.12, 1.55 + row * 2.35
add_rect(slide, x, y, 3.68, 1.75, WHITE, accent, radius=True)
add_number_badge(slide, num, x + 0.2, y + 0.25, accent)
add_text(slide, title, x + 0.88, y + 0.23, 2.55, 0.3, 15, accent, True)
add_text(slide, desc, x + 0.25, y + 0.82, 3.18, 0.68, 11.2, INK, align=PP_ALIGN.CENTER)
if i < 2 or 3 <= i < 5:
add_arrow(slide, x + 3.75, y + 0.74, 0.25, 0.17, accent)
add_rect(slide, 1.0, 6.35, 11.15, 0.44, PALE_TEAL, TEAL)
add_text(slide, "每轮迭代同时检查功能正确性、总线可观测性、时序与板级端到端时间。", 1.22, 6.48, 10.7, 0.2, 12.5, INK, True, align=PP_ALIGN.CENTER)
# 7. Contest requirements
slide = new_content_slide("赛题要求与设计响应", 7, "项目与系统方案")
add_text(slide, "赛题不是孤立IP设计,而是完整SoC中的端到端实现。", 0.82, 1.48, 8.3, 0.34, 13, MUTED)
add_table(
slide,
[
["赛题要求 / 工程约束", "本系统的设计响应"],
["基于OpenLA500构建完整SoC", "双主八从AXI架构;CPU、系统、ML-KEM三个异步时钟域"],
["5000组4×4矩阵乘法,禁止DSP", "全LUT乘加、双Bank序列器、在线CRC32与快速通道"],
["实现FIPS 203 ML-KEM", "单比特流覆盖三等级、三操作;KAT与隐式拒绝验证"],
["提高端到端性能", "四通道DMA、端点传输、SRAM连续突发与硬件自主调度"],
],
0.85,
1.98,
11.6,
3.85,
widths=[4.0, 7.6],
size=13,
)
add_rect(slide, 0.9, 6.15, 11.5, 0.58, PALE_TEAL, TEAL)
add_text(slide, "答辩主线:赛题约束 → 系统架构 → 数据流与自主IP → 分层验证 → 实测结果", 1.1, 6.32, 11.1, 0.24, 14, INK, True, align=PP_ALIGN.CENTER)
# 8. Architecture and ownership
slide = new_content_slide("总体架构与自主设计边界", 8, "项目与系统方案")
add_image_contain(slide, SYSTEM / "system_arch.png", 0.7, 1.48, 8.0, 5.15)
add_rect(slide, 9.02, 1.5, 3.45, 3.78, WHITE, TEAL, radius=True)
add_text(slide, "自主实现", 9.25, 1.78, 3.0, 0.34, 18, TEAL, True, TITLE_FONT, PP_ALIGN.CENTER)
add_bullets(
slide,
[
"四通道DMA与加速器端点",
"Matmul、批量序列器、CRC32",
"1024点Radix-2² SDF FFT",
"全参数ML-KEM硬件IP",
"两级AXI跨时钟域桥",
"SRAM控制器突发流水化",
],
9.28,
2.35,
2.95,
2.55,
size=13.2,
gap=5,
)
add_rect(slide, 9.02, 5.5, 3.45, 1.1, PALE_GRAY, LINE, radius=True)
add_text(slide, "赛事基础平台", 9.25, 5.72, 2.95, 0.28, 14, MUTED, True, align=PP_ALIGN.CENTER)
add_text(slide, "OpenLA500、板级SRAM器件、UART及基础外设", 9.25, 6.08, 2.95, 0.36, 11, INK, align=PP_ALIGN.CENTER)
# 9. Data movement, clocks and SRAM
slide = new_content_slide("数据搬运、SRAM与三时钟域协同", 9, "项目与系统方案")
clock_info = [
(0.8, BLUE, "CPU域 · 33.333 MHz", "OpenLA500 / Cache / AXI主端"),
(5.0, TEAL, "系统域 · 50 MHz", "Crossbar / DMA / SRAM / Matmul / FFT"),
(9.2, ORANGE, "ML-KEM域 · 76.923 MHz", "AXI前端 / Keccak / NTT / FSM"),
]
for x, accent, title, desc in clock_info:
add_rect(slide, x, 1.5, 3.3, 0.85, WHITE, accent, radius=True)
add_text(slide, title, x + 0.1, 1.68, 3.1, 0.28, 13.5, accent, True, align=PP_ALIGN.CENTER)
add_text(slide, desc, x + 0.1, 2.02, 3.1, 0.2, 9.5, MUTED, align=PP_ALIGN.CENTER)
add_arrow(slide, 4.26, 1.78, 0.5, 0.25, BLUE)
add_arrow(slide, 8.46, 1.78, 0.5, 0.25, TEAL)
add_text(slide, "AXI双时钟FIFO", 3.84, 2.34, 1.35, 0.2, 9, MUTED, align=PP_ALIGN.CENTER)
add_text(slide, "AXI双时钟FIFO", 8.04, 2.34, 1.35, 0.2, 9, MUTED, align=PP_ALIGN.CENTER)
add_rect(slide, 0.82, 2.85, 5.7, 3.45, WHITE, BLUE, radius=True)
add_text(slide, "硬件数据通路", 1.05, 3.08, 5.2, 0.3, 17, BLUE, True, TITLE_FONT, PP_ALIGN.CENTER)
flow = [("CPU配置", BLUE), ("DMA", TEAL), ("ExtRAM", YELLOW), ("加速器", ORANGE)]
x = 1.05
for index, (name, accent) in enumerate(flow):
add_rect(slide, x, 3.72, 1.03, 0.58, PALE_GRAY, accent)
add_text(slide, name, x, 3.88, 1.03, 0.22, 11, accent, True, align=PP_ALIGN.CENTER)
if index < 3:
add_arrow(slide, x + 1.08, 3.9, 0.35, 0.18, accent)
x += 1.43
add_text(slide, "CPU只配置任务,DMA、SRAM与加速器完成批量搬运和计算。", 1.02, 4.65, 5.25, 0.5, 12, INK, align=PP_ALIGN.CENTER)
add_text(slide, "复位顺序:sys → mlkem → cpu;释放前AXI valid保持为0。", 1.02, 5.42, 5.25, 0.38, 11, MUTED, align=PP_ALIGN.CENTER)
add_rect(slide, 6.82, 2.85, 5.65, 3.45, WHITE, ORANGE, radius=True)
add_text(slide, "SRAM连续突发优化", 7.05, 3.08, 5.2, 0.3, 17, ORANGE, True, TITLE_FONT, PP_ALIGN.CENTER)
add_text(slide, "优化前", 7.12, 3.63, 0.75, 0.24, 10.5, MUTED, True)
for i in range(4):
accent = BLUE if i % 2 == 0 else "E3E7EF"
add_rect(slide, 8.08 + i * 0.72, 3.58, 0.42, 0.36, accent)
add_text(slide, "优化后", 7.12, 4.18, 0.75, 0.24, 10.5, ORANGE, True)
for i in range(4):
add_rect(slide, 8.08 + i * 0.48, 4.13, 0.42, 0.36, TEAL)
add_metric(slide, "200", "理论单向带宽", 7.15, 4.82, 2.0, ORANGE, " MB/s")
add_metric(slide, "1", "无反压连续读写", 9.52, 4.82, 2.0, TEAL, " beat/cycle")
add_text(slide, "读数据寄存保持R通道稳定;Bank选择随事务锁存。", 7.12, 5.9, 5.0, 0.32, 11, INK, align=PP_ALIGN.CENTER)
# 10. Matmul
slide = new_content_slide("Matmul:一次启动完成5000组流水任务", 10, "自主计算IP")
add_image_contain(slide, SYSTEM / "matmul_dataflow_gantt.png", 0.65, 1.5, 9.55, 5.3)
add_rect(slide, 10.45, 1.5, 2.02, 5.3, WHITE, BLUE, radius=True)
add_text(slide, "关键节拍", 10.65, 1.78, 1.62, 0.3, 17, BLUE, True, TITLE_FONT, PP_ALIGN.CENTER)
for y, value, label, accent in [
(2.26, "128 B", "每组A/B读取", BLUE),
(3.18, "≈22 cyc", "单Bank乘加延迟", GREEN),
(4.10, "2×6 B", "每元素CRC更新", ORANGE),
(5.02, "0 B", "快速模式结果写回", TEAL),
]:
add_text(slide, value, 10.66, y, 1.62, 0.3, 18, accent, True, TITLE_FONT, PP_ALIGN.CENTER)
add_text(slide, label, 10.66, y + 0.34, 1.62, 0.26, 10, MUTED, align=PP_ALIGN.CENTER)
add_text(slide, "最后一组退休后仍等待CRC FIFO排空,再产生DONE。", 10.7, 6.03, 1.55, 0.5, 10.5, INK, align=PP_ALIGN.CENTER)
# 11. ML-KEM architecture
slide = new_content_slide("ML-KEM:单比特流覆盖三等级与三操作", 11, "自主计算IP")
add_image_contain(slide, MLKEM / "mlkem_modules.png", 0.65, 1.48, 8.05, 4.8)
add_rect(slide, 9.0, 1.5, 3.45, 3.95, WHITE, TEAL, radius=True)
add_text(slide, "运行时配置", 9.25, 1.8, 2.95, 0.3, 18, TEAL, True, TITLE_FONT, PP_ALIGN.CENTER)
add_metric(slide, "3", "512 / 768 / 1024", 9.27, 2.28, 1.3, TEAL)
add_metric(slide, "3", "KeyGen / Encaps / Decaps", 10.78, 2.28, 1.35, ORANGE)
add_bullets(
slide,
[
"参数在start时锁存",
"存储按KMAX=4静态分配",
"控制、算子、BRAM三级组织",
"公钥/私钥/密文支持AXI突发窗口",
"无需按安全等级重新综合",
],
9.28,
3.38,
2.95,
1.72,
size=11.8,
gap=4,
)
add_rect(slide, 0.82, 6.36, 11.47, 0.46, PALE_TEAL, TEAL)
add_text(slide, "KeyGen 生成 ek/dk", 1.45, 6.49, 2.45, 0.2, 12, BLUE, True, align=PP_ALIGN.CENTER)
add_arrow(slide, 4.02, 6.5, 0.48, 0.17, BLUE)
add_text(slide, "Encaps 生成 c/ss", 4.62, 6.49, 2.45, 0.2, 12, TEAL, True, align=PP_ALIGN.CENTER)
add_arrow(slide, 7.18, 6.5, 0.48, 0.17, TEAL)
add_text(slide, "Decaps 恢复或拒绝 ss", 7.78, 6.49, 3.05, 0.2, 12, ORANGE, True, align=PP_ALIGN.CENTER)
# 12. ML-KEM key designs
slide = new_content_slide("ML-KEM关键设计:复用、并行与常量时间", 12, "自主计算IP")
panels = [
(0.72, BLUE, PALE_BLUE, "共享Keccak"),
(4.52, TEAL, PALE_TEAL, "双蝶形NTT"),
(8.32, ORANGE, PALE_ORANGE, "Decaps复用"),
]
for x, accent, pale, title in panels:
add_rect(slide, x, 1.55, 3.5, 4.95, pale, accent, radius=True)
add_text(slide, title, x + 0.2, 1.84, 3.1, 0.34, 18, accent, True, TITLE_FONT, PP_ALIGN.CENTER)
# Shared Keccak panel
for i, name in enumerate(["G/H/J哈希", "SampleNTT", "CBD采样"]):
add_rect(slide, 0.98, 2.55 + i * 0.62, 1.15, 0.38, WHITE, LINE)
add_text(slide, name, 0.98, 2.66 + i * 0.62, 1.15, 0.16, 9.5, INK, align=PP_ALIGN.CENTER)
add_arrow(slide, 2.22, 2.67 + i * 0.62, 0.36, 0.16, BLUE)
add_rect(slide, 2.65, 2.83, 1.25, 1.0, WHITE, BLUE)
add_text(slide, "单一\nKeccak-f[1600]", 2.72, 3.08, 1.12, 0.45, 11, BLUE, True, align=PP_ALIGN.CENTER)
add_text(slide, "避免两份额外实例\n约节省10.6k LUT", 1.05, 4.85, 2.82, 0.6, 13, BLUE, True, align=PP_ALIGN.CENTER)
# NTT panel
add_rect(slide, 4.92, 2.6, 1.1, 1.3, WHITE, TEAL)
for i in range(4):
add_rect(slide, 5.08, 2.75 + i * 0.24, 0.78, 0.14, TEAL if i % 2 == 0 else ACCENT_BLUE)
add_arrow(slide, 6.15, 3.08, 0.4, 0.2, TEAL)
add_rect(slide, 6.65, 2.6, 1.08, 1.3, WHITE, TEAL)
add_text(slide, "BU0\nBU1", 6.72, 2.99, 0.94, 0.42, 14, TEAL, True, align=PP_ALIGN.CENTER)
add_text(slide, "4路BRAM无冲突供数\n读-算-写三级调度", 4.95, 4.28, 2.75, 0.58, 12, INK, align=PP_ALIGN.CENTER)
add_text(slide, "4 coeff/cycle · 0 DSP", 4.98, 5.14, 2.72, 0.32, 15, TEAL, True, align=PP_ALIGN.CENTER)
# Decaps panel
for i, (name, accent) in enumerate([("D1–D5 解密恢复", BLUE), ("D6 复用Encaps", TEAL), ("D7 常量时间比较", ORANGE)]):
add_rect(slide, 8.7, 2.5 + i * 0.82, 2.75, 0.5, WHITE, accent)
add_text(slide, name, 8.82, 2.64 + i * 0.82, 2.51, 0.2, 11, accent, True, align=PP_ALIGN.CENTER)
if i < 2:
down = slide.shapes.add_shape(MSO_SHAPE.DOWN_ARROW, Inches(9.88), Inches(3.04 + i * 0.82), Inches(0.38), Inches(0.2))
down.fill.solid(); down.fill.fore_color.rgb = color(accent); down.line.fill.background()
add_text(slide, "Accept与Reject路径\n总周期数完全相等", 8.86, 5.25, 2.44, 0.52, 13, ORANGE, True, align=PP_ALIGN.CENTER)
add_text(slide, "设计取舍:以资源共享和运行时可配置性换取更低的面积与部署复杂度。", 1.0, 6.68, 11.3, 0.28, 13, INK, True, align=PP_ALIGN.CENTER)
# 13. Verification
slide = new_content_slide("四层验证:从算子正确到板级闭环", 13, "验证与实现结果")
bands = [
(1.55, 1.35, 8.25, "板级系统", "真实FPGA、CPU软件、DMA与串口日志", ORANGE),
(2.55, 1.95, 7.05, "SoC集成", "AXI解码、CDC、复位释放、SRAM取指", TEAL),
(3.55, 2.55, 5.85, "IP集成", "Matmul / FFT / DMA / ML-KEM端到端", BLUE),
(4.55, 3.15, 4.65, "叶模块", "Keccak、NTT、蝶形、FIFO、CRC32", GREEN),
]
for y, x, width, title, desc, accent in bands:
add_rect(slide, x, y, width, 0.7, WHITE, accent, radius=True)
add_text(slide, title, x + 0.18, y + 0.17, 1.2, 0.24, 13, accent, True)
add_text(slide, desc, x + 1.45, y + 0.16, width - 1.65, 0.26, 11.5, INK, align=PP_ALIGN.CENTER)
add_rect(slide, 9.88, 1.55, 2.5, 4.75, PALE_ORANGE, ORANGE, radius=True)
add_text(slide, "验证结果", 10.15, 1.82, 1.96, 0.3, 17, ORANGE, True, TITLE_FONT, PP_ALIGN.CENTER)
for i, (value, label, accent) in enumerate([
("9000", "主路径KAT", BLUE),
("300", "隐式拒绝向量", ORANGE),
("900", "ML-KEM板级运行", TEAL),
("100%", "Matmul最终得分", GREEN),
("6", "FFT板级测试", YELLOW),
]):
y = 2.28 + i * 0.76
add_text(slide, value, 10.05, y, 0.95, 0.28, 16, accent, True, TITLE_FONT, PP_ALIGN.RIGHT)
add_text(slide, label, 11.12, y + 0.03, 1.05, 0.26, 10, INK)
add_rect(slide, 1.0, 5.75, 8.55, 0.58, PALE_TEAL, TEAL)
add_text(slide, "算法逐字节比对;板级以零失败、CRC匹配和误差阈值为判据。", 1.18, 5.92, 8.2, 0.24, 12.5, INK, True, align=PP_ALIGN.CENTER)
add_text(slide, "全部通过", 9.98, 6.48, 2.25, 0.36, 20, GREEN, True, TITLE_FONT, PP_ALIGN.CENTER)
# 14. Resources and timing
slide = new_content_slide("实现结果:资源可控,三个时钟域全部收敛", 14, "验证与实现结果")
add_rect(slide, 0.78, 1.5, 5.72, 4.85, WHITE, BLUE, radius=True)
add_text(slide, "FPGA资源利用", 1.05, 1.78, 5.18, 0.3, 17, BLUE, True, TITLE_FONT, PP_ALIGN.CENTER)
resource_data = [
("Slice LUT", 41.16, "55,066", BLUE),
("Registers", 11.58, "31,000", TEAL),
("BRAM Tile", 8.08, "29.5", YELLOW),
("DSP48E1", 0.0, "0", ORANGE),
]
for i, (label, percent, absolute, accent) in enumerate(resource_data):
y = 2.45 + i * 0.75
add_text(slide, label, 1.02, y, 1.1, 0.24, 11, INK)
add_rect(slide, 2.2, y + 0.02, 2.55, 0.22, "E5E9F1")
if percent:
add_rect(slide, 2.2, y + 0.02, 2.55 * percent / 50, 0.22, accent)
add_text(slide, f"{percent:.2f}%", 4.85, y - 0.02, 0.68, 0.26, 10.5, accent, True, align=PP_ALIGN.RIGHT)
add_text(slide, absolute, 5.6, y - 0.02, 0.55, 0.26, 10, MUTED, align=PP_ALIGN.RIGHT)
add_text(slide, "xc7a200tfbg676-1 · 布局布线100%完成", 1.1, 5.72, 5.08, 0.28, 11.5, MUTED, align=PP_ALIGN.CENTER)
add_rect(slide, 6.82, 1.5, 5.62, 4.85, WHITE, TEAL, radius=True)
add_text(slide, "布局布线后时序", 7.08, 1.78, 5.1, 0.3, 17, TEAL, True, TITLE_FONT, PP_ALIGN.CENTER)
add_table(
slide,
[
["时钟域", "频率", "Setup WNS", "端点"],
["cpu_clk", "33.333 MHz", "+0.737 ns", "17,197"],
["sys_clk", "50.000 MHz", "+0.125 ns", "22,307"],
["mlkem_clk", "76.923 MHz", "+0.070 ns", "31,646"],
],
7.08,
2.35,
5.1,
2.35,
widths=[1.15, 1.35, 1.35, 1.25],
size=10.5,
)
add_metric(slide, "88,843", "时序端点", 7.15, 5.0, 1.45, TEAL)
add_metric(slide, "0", "建立/保持违例", 8.95, 5.0, 1.45, GREEN)
add_metric(slide, "+0.070", "全系统最小WNS / ns", 10.72, 5.0, 1.45, ORANGE)
add_rect(slide, 1.0, 6.58, 11.2, 0.35, PALE_BLUE, BLUE)
add_text(slide, "ML-KEM独立高速时钟域隔离了系统总线布线压力。", 1.2, 6.66, 10.8, 0.18, 11.5, BLUE, True, align=PP_ALIGN.CENTER)
# 15. Performance
slide = new_content_slide("最终性能:Matmul满分与端到端加速", 15, "验证与实现结果")
for i, (value, label, accent, suffix) in enumerate([
("4.911765", "Matmul 5000组最终时间", BLUE, " ms"),
("827.80", "FFT DMA相对软件加速", ORANGE, "×"),
("48.95–53.33", "ML-KEM DMA相对软件加速", TEAL, "×"),
]):
x = 0.85 + i * 4.05
add_rect(slide, x, 1.5, 3.55, 1.05, WHITE, accent, radius=True)
add_metric(slide, value, label, x + 0.15, 1.66, 3.25, accent, suffix)
add_rect(slide, 0.78, 2.92, 7.2, 3.55, WHITE, ORANGE, radius=True)
add_text(slide, "ML-KEM三路径完整往返加速比", 1.02, 3.17, 6.72, 0.3, 16, ORANGE, True, TITLE_FONT, PP_ALIGN.CENTER)
levels = ["512", "768", "1024"]
cpu = [13.04, 13.84, 15.05]
dma = [48.95, 50.41, 53.33]
for i, level in enumerate(levels):
y = 3.74 + i * 0.72
add_text(slide, f"ML-KEM-{level}", 1.0, y + 0.16, 1.15, 0.22, 10.5, INK)
add_rect(slide, 2.25, y, cpu[i] / 60 * 4.65, 0.23, BLUE)
add_rect(slide, 2.25, y + 0.31, dma[i] / 60 * 4.65, 0.23, ORANGE)
add_text(slide, f"{cpu[i]:.2f}×", 2.33 + cpu[i] / 60 * 4.65, y - 0.02, 0.62, 0.2, 8.5, BLUE, True)
add_text(slide, f"{dma[i]:.2f}×", 2.33 + dma[i] / 60 * 4.65, y + 0.29, 0.62, 0.2, 8.5, ORANGE, True)
add_rect(slide, 2.15, 6.04, 0.15, 0.15, BLUE)
add_text(slide, "CPU寄存器路径", 2.4, 6.02, 1.25, 0.2, 9, MUTED)
add_rect(slide, 3.9, 6.04, 0.15, 0.15, ORANGE)
add_text(slide, "DMA窗口路径", 4.15, 6.02, 1.25, 0.2, 9, MUTED)
add_rect(slide, 8.28, 2.92, 4.15, 3.55, PALE_TEAL, TEAL, radius=True)
add_text(slide, "结果解释", 8.55, 3.18, 3.6, 0.3, 16, TEAL, True, TITLE_FONT, PP_ALIGN.CENTER)
add_bullets(
slide,
[
"Matmul CRC32 0x0417d761匹配",
"FFT DMA延迟296.73 μs,误差Re=1、Im=2",
"ML-KEM-512 DMA往返约1.605 ms",
"SRAM连续突发理论带宽200 MB/s",
],
8.55,
3.78,
3.55,
1.85,
size=12,
gap=6,
)
add_text(slide, "随机种子0xa3772ec8 · 最终得分100.00%", 8.55, 5.92, 3.55, 0.34, 10.5, ORANGE, True, align=PP_ALIGN.CENTER)
# 16. Lessons learned
slide = new_content_slide("项目心得:从单点优化走向系统工程", 16, "心得与后续工作")
lessons = [
("端到端优先", "计算核延迟不是唯一指标;启动、搬运、调度和校验共同决定评测时间。", BLUE),
("先正确再加速", "先建立CRC、状态寄存器和分层测试等可观测手段,再压缩流水空闲。", ORANGE),
("取舍必须显式", "Keccak复用、双蝶形NTT和零DSP策略是在面积、吞吐与部署复杂度间的选择。", TEAL),
("证据需要版本化", "报告、软件、RTL和实测日志必须共享同一数据基线,才能保证结论可复现。", GREEN),
]
for i, (title, desc, accent) in enumerate(lessons):
col, row = i % 2, i // 2
x, y = 0.82 + col * 6.0, 1.55 + row * 2.45
add_rect(slide, x, y, 5.55, 1.85, WHITE, accent, radius=True)
add_number_badge(slide, f"0{i+1}", x + 0.25, y + 0.28, accent)
add_text(slide, title, x + 0.95, y + 0.27, 4.2, 0.32, 16, accent, True)
add_text(slide, desc, x + 0.35, y + 0.9, 4.85, 0.58, 12, INK, align=PP_ALIGN.CENTER)
add_rect(slide, 1.0, 6.38, 11.15, 0.42, PALE_ORANGE, ORANGE)
add_text(slide, "最终满分来自软硬件协同,而不是单一模块参数的堆叠。", 1.2, 6.5, 10.75, 0.2, 13, INK, True, align=PP_ALIGN.CENTER)
# 17. Future work
slide = new_content_slide("后续工作:沿性能、验证与工程化继续演进", 17, "心得与后续工作")
future = [
(BLUE, "ML-KEM并行度", "增加Keccak并行路径,扩展NTT蝶形单元并评估面积收益比。"),
(ORANGE, "存储专项验证", "补充SRAM随机反压、跨Bank边界与持续带宽微基准。"),
(TEAL, "实现与功耗证据", "完善I/O约束,使用SAIF/VCD活动率回标提高功耗估计可信度。"),
(GREEN, "版本与回归体系", "统一RTL、软件、测试向量和报告指标,形成自动化回归基线。"),
]
for i, (accent, title, desc) in enumerate(future):
y = 1.48 + i * 1.22
add_number_badge(slide, f"0{i+1}", 0.92, y, accent)
add_text(slide, title, 1.65, y + 0.07, 2.15, 0.3, 15, accent, True)
add_text(slide, desc, 3.92, y + 0.06, 7.85, 0.4, 13, INK)
add_line(slide, 0.95, y + 0.76, 11.15, 0.012, LINE)
add_rect(slide, 1.0, 6.45, 11.15, 0.35, PALE_TEAL, TEAL)
add_text(slide, "目标:在不牺牲标准完整性和可验证性的前提下继续提高系统吞吐。", 1.2, 6.55, 10.75, 0.18, 12.5, INK, True, align=PP_ALIGN.CENTER)
# 18. Summary
slide = new_content_slide("总结:完成从算法IP到板级系统的闭环", 18, "总结与展望")
summary = [
("01", "系统", "构建CPU、DMA、存储与三个计算加速器协同的异构SoC", BLUE),
("02", "数据流", "以SRAM连续突发、DMA端点和双Bank调度降低搬运开销", ORANGE),
("03", "密码IP", "单比特流覆盖ML-KEM三等级、三操作和常量时间拒绝", TEAL),
("04", "工程验证", "零DSP、三个时钟域收敛,并通过仿真与板级分层验证", GREEN),
]
for i, (num, tag, desc, accent) in enumerate(summary):
y = 1.52 + i * 1.08
add_number_badge(slide, num, 0.95, y, accent)
add_text(slide, tag, 1.65, y + 0.08, 1.0, 0.28, 15, accent, True)
add_text(slide, desc, 2.75, y + 0.08, 8.95, 0.36, 14, INK)
add_line(slide, 0.95, y + 0.72, 11.2, 0.012, LINE)
add_rect(slide, 0.92, 6.02, 11.3, 0.62, PALE_ORANGE, ORANGE)
add_rich(
slide,
[
("最终结果:", 12.5, True, INK, FONT),
("5000", 12.5, True, INK, LATIN_FONT),
("组 Matmul 用时4.911765 ms · CRC32匹配 · 得分100.00%", 12.5, True, INK, FONT),
],
1.12,
6.2,
10.9,
0.3,
PP_ALIGN.CENTER,
)
# 19. End
slide = prs.slides.add_slide(blank)
add_template_image(slide, "image3.png", 0, 0, W, H)
add_template_image(slide, "image4.png", 5.92, 0.7, 1.48, 1.48)
add_text(slide, "感谢聆听", 3.45, 2.55, 6.45, 0.75, 34, BLUE, True, TITLE_FONT, PP_ALIGN.CENTER)
add_rect(slide, 4.63, 3.65, 4.08, 0.72, BLUE, radius=True)
add_text(slide, "Q&A", 4.75, 3.83, 3.84, 0.3, 22, WHITE, True, LATIN_FONT, PP_ALIGN.CENTER)
add_text(slide, "中国集成电路未来之星从这里起步!", 3.2, 5.2, 6.95, 0.42, 17, INK, True, align=PP_ALIGN.CENTER)
# 20. Backup: address space
slide = new_content_slide("备用:地址空间与软件控制流程", 20, "答疑备用")
add_table(
slide,
[
["AXI从端", "基地址", "功能"],
["S0", "0x1c00_0000", "BaseRAM / ExtRAM,共8 MB"],
["S5", "0x1f30_0000", "DMA与矩阵批量序列器"],
["S6", "0x1f40_0000", "FFT / ML-KEM共享窗口"],
["S7", "0x1f50_0000", "Matmul控制与数据Bank"],
],
0.8,
1.55,
6.2,
3.75,
widths=[1.0, 2.0, 3.2],
size=12,
)
add_rect(slide, 7.4, 1.55, 4.8, 3.75, WHITE, TEAL, radius=True)
add_text(slide, "软件控制流程", 7.7, 1.83, 4.2, 0.3, 17, TEAL, True, TITLE_FONT, PP_ALIGN.CENTER)
for i, text in enumerate(["写配置寄存器", "配置DMA窗口", "启动硬件", "轮询done", "读取结果 / CRC"]):
y = 2.35 + i * 0.56
add_rect(slide, 8.05, y, 3.5, 0.38, PALE_GRAY, TEAL if i < 3 else BLUE)
add_text(slide, text, 8.15, y + 0.1, 3.3, 0.17, 10.5, INK, True, align=PP_ALIGN.CENTER)
add_rect(slide, 0.92, 5.75, 11.25, 0.62, PALE_TEAL, TEAL)
add_text(slide, "快速路径中CPU不参与逐组Matmul调度;ML-KEM DMA路径只进行少量批量窗口配置。", 1.15, 5.93, 10.8, 0.26, 12.5, INK, True, align=PP_ALIGN.CENTER)
# 21. Backup: KeyGen timing
slide = new_content_slide("备用:ML-KEM-512 KeyGen逐状态周期分解", 21, "答疑备用")
states = [
("G哈希", 27, YELLOW),
("SampleNTT矩阵A", 3213, ORANGE),
("CBD采样", 1229, TEAL),
("NTT", 4172, BLUE),
("多项式基乘", 2096, GREEN),
("密钥编码", 2081, RED),
]
for i, (name, cycles, accent) in enumerate(states):
y = 1.52 + i * 0.72
add_text(slide, name, 0.9, y + 0.1, 1.55, 0.22, 11, INK)
add_rect(slide, 2.5, y + 0.1, 6.45, 0.25, "E5E9F1")
add_rect(slide, 2.5, y + 0.1, 6.45 * cycles / 4500, 0.25, accent)
add_text(slide, f"{cycles:,} cyc", 9.1, y + 0.06, 1.0, 0.25, 10, accent, True, align=PP_ALIGN.RIGHT)
add_rect(slide, 10.45, 1.55, 2.0, 4.3, PALE_ORANGE, ORANGE, radius=True)
add_text(slide, "主要瓶颈", 10.7, 1.88, 1.5, 0.3, 16, ORANGE, True, TITLE_FONT, PP_ALIGN.CENTER)
add_text(slide, "NTT\n30.2%", 10.73, 2.5, 1.45, 0.65, 19, BLUE, True, align=PP_ALIGN.CENTER)
add_text(slide, "SampleNTT\n23.3%", 10.68, 3.55, 1.55, 0.65, 16, ORANGE, True, align=PP_ALIGN.CENTER)
add_text(slide, "两者合计\n53.5%", 10.72, 4.65, 1.48, 0.65, 15, INK, True, align=PP_ALIGN.CENTER)
add_rect(slide, 0.92, 6.25, 11.25, 0.58, PALE_ORANGE, ORANGE)
add_text(slide, "优化优先级:增加采样路径Keccak并行度,其次扩展NTT蝶形单元数量。", 1.12, 6.42, 10.85, 0.24, 12.5, INK, True, align=PP_ALIGN.CENTER)
# 22. Backup: tradeoffs
slide = new_content_slide("备用:设计取舍与优化路线", 22, "答疑备用")
add_table(
slide,
[
["维度", "当前选择", "优势", "代价"],
["安全等级", "运行时k=2/3/4", "单比特流部署", "地址与控制逻辑增加"],
["Keccak", "单核时分复用", "节省约10.6k LUT", "状态间无法重叠"],
["NTT", "双蝶形、4 coeff/cycle", "BRAM无冲突、0 DSP", "吞吐低于多BU实现"],
["控制", "30状态硬连线FSM", "行为清晰、易验证", "串行调度限制性能"],
],
0.72,
1.48,
11.85,
3.62,
widths=[1.4, 3.05, 3.35, 4.05],
size=11.3,
)
add_text(slide, "优化路线", 0.92, 5.42, 1.45, 0.3, 17, ORANGE, True, TITLE_FONT)
roadmap = [
("1", "双Keccak或专用采样核", "预计KeyGen下降15%–20%", ORANGE),
("2", "扩展多BU NTT", "提高至8 coeff/cycle", TEAL),
("3", "完善约束与活动率回标", "提高时序与功耗结论置信度", BLUE),
]
for i, (num, title, desc, accent) in enumerate(roadmap):
x = 0.95 + i * 4.05
add_number_badge(slide, num, x, 5.88, accent)
add_text(slide, title, x + 0.62, 5.86, 2.95, 0.28, 12, accent, True)
add_text(slide, desc, x + 0.62, 6.23, 2.95, 0.4, 10.5, INK)
prs.save(OUTPUT)
print(f"Wrote {OUTPUT} from {TEMPLATE}")