1. 从一条命令到可验证电路这个项目到底在做什么第一次看到“让 AI 设计后量子密码加速器”这个说法我脑子里冒出来的第一个念头是这要么是标题党要么就是把“AI 辅助写代码”包装成了“AI 设计硬件”。但仔细拆开来看这件事其实比想象中要实在得多。它的核心链路是用一条自然语言命令作为起点让大模型生成后量子密码算法的硬件加速器 RTL 代码再通过 UVM 验证平台把这份 RTL 跑通、跑对最终交付一个可以集成进 SoC 的 IP 核。换句话说AI 负责“写初稿”验证负责“判卷子”人负责“定标准和兜底”。这里面的关键词每一个都值得展开。后量子密码Post-Quantum CryptographyPQC是应对未来量子计算威胁的一类新算法NIST 已经标准化了 Kyber、Dilithium 等方案它们和传统 RSA、ECC 在数学结构上完全不同运算模式也差异巨大。加速器意味着不是纯软件实现而是要做成硬件模块追求吞吐率、面积和功耗的平衡。RTL是寄存器传输级代码通常用 Verilog 或 SystemVerilog 写是数字芯片设计的第一份“可执行规格”。UVM是通用验证方法学用来搭建可重用、可扩展的验证环境。SoC IP则说明这个加速器最终不是孤立存在的它要挂到总线上、接进系统里、被驱动调用。这个项目适合谁看如果你是对数字 IC 设计感兴趣但没真正流片过的学生它能让你看到从算法到电路的完整链路如果你是做验证的工程师UVM 那部分能给你一套可复用的验证骨架如果你只是好奇 AI 到底能不能干硬件的活这里会给你一个不带滤镜的答案。我实测下来的结论是AI 能显著缩短“从零到有”的时间但“从有到对”仍然需要人的判断和验证平台的约束。下面我把整个项目的设计思路、关键细节、实操过程和踩坑记录完整拆开讲。2. 整体设计与思路拆解为什么这样搭2.1 为什么选后量子密码作为切入点选 PQC 作为 AI 设计加速器的目标不是随便挑的。传统 RSA 和 ECC 的硬件实现已经有大量成熟 IPAI 生成的东西很难和手写优化过的电路竞争验证起来也没有新鲜感。而 PQC 算法有几个特点让它特别适合这个项目第一算法结构相对规整大量运算是多项式乘法、NTT数论变换、哈希和采样这些都有明确的数学定义AI 容易从算法描述映射到硬件结构第二PQC 的硬件实现还在快速演进期没有形成绝对垄断的“标准答案”AI 生成的方案有讨论空间第三PQC 的验证复杂度高正好能体现 UVM 平台的价值。我选择 Kyber 作为第一个目标算法原因是它在 NIST 标准化进程中成熟度最高参数集清晰社区资料多出问题容易对照。Dilithium 签名算法结构更复杂适合作为第二个迭代目标。这个选择逻辑很重要不要一上来就挑最难的先用一个“有明确参考但又不至于烂大街”的算法把链路跑通。2.2 为什么用“一条命令”作为交互入口“一条命令”这个设计看起来像是噱头但它背后有实际的工程考量。传统硬件设计流程里从算法规格到 RTL 需要经过架构设计、微架构设计、编码、仿真多个环节每个环节都有信息损耗。用一条自然语言命令作为入口本质上是把“人的意图”直接投射到“代码初稿”减少中间转述的损耗。命令的内容通常包括目标算法、接口类型AXI 或 APB、数据位宽、流水线级数、目标频率等约束。但这里有个关键认知一条命令生成的不是最终电路而是“可验证的起点”。我在实际操作中会把命令写成类似这样的结构生成一个 Kyber-768 的 NTT 加速器 RTL 模块接口使用 AXI4-Stream 数据位宽 64 bit支持 256 点 NTT流水线深度 4 级 使用 SystemVerilog 编写模块名 kyber_ntt_core。这条命令里每个参数都有意义。位宽决定资源占用和吞吐流水线深度影响时序收敛接口类型决定它怎么接入 SoC。AI 拿到这些约束后生成的 RTL虽然不能直接流片但已经具备了可仿真、可综合的基本形态。2.3 为什么验证环节必须用 UVM 而不是简单 testbench很多人会问AI 生成的 RTL写个简单的 testbench 跑几个激励不就行了为什么要上 UVM我的答案是简单 testbench 只能证明“某些情况下对”UVM 才能系统性地证明“在约束范围内不容易错”。PQC 加速器的输入空间很大多项式系数、随机种子、控制信号组合非常多靠手写定向测试覆盖不全。UVM 的 sequence、driver、monitor、scoreboard 结构可以把激励生成、结果比对、覆盖率收集标准化尤其是 scoreboard 里可以用参考模型通常是 Python 或 C 实现的算法做黄金比对这是发现 AI 生成 RTL 隐藏 bug 的关键。另外UVM 的寄存器模型RAL在这个项目里也有实际作用。加速器通常有配置寄存器、状态寄存器、中断寄存器用 RAL 可以统一管理这些寄存器的读写和镜像值检查。热搜词里提到的“uvm寄存器模型镜像值”就是这个点镜像值是 RAL 在本地维护的一份寄存器预期值当 DUT 实际值与之不符时能快速定位是配置没写进去还是硬件行为异常。2.4 整体架构的分层设计整个项目我分成四层算法参考层、RTL 生成层、验证层、集成层。算法参考层用 Python 实现 Kyber 的 NTT 和多项式运算作为黄金模型RTL 生成层是 AI 产出 SystemVerilog 代码的地方验证层是 UVM 环境负责激励和比对集成层把验证通过的 IP 包装成 AXI 从设备挂到一个小型 SoC 子系统里做冒烟测试。这个分层的好处是每层可以独立迭代AI 生成的 RTL 换了验证层不用大改算法参数变了参考模型更新即可。3. 核心细节解析与实操要点3.1 AI 生成 RTL 的提示词工程让 AI 生成可用的 RTL提示词的质量决定了一半的成败。我试过很多版本最后稳定下来的提示词结构包含五个部分角色设定、算法规格、接口约束、编码风格、输出格式。角色设定是告诉模型“你是一个有十年经验的数字 IC 设计工程师”这能明显提升代码的工程性算法规格要给出数学定义或伪代码不能只说“实现 Kyber NTT”接口约束要明确信号名、位宽、握手协议编码风格要求使用参数化、避免 latch、时钟复位统一输出格式要求分模块、带注释、可综合。一个常见的坑是AI 容易生成行为级描述而不是可综合 RTL。比如它会写for循环里带复杂条件判断仿真能过但综合工具报错。解决办法是在提示词里明确“所有循环必须有固定边界禁止使用动态索引禁止使用不可综合的系统函数”。我实测下来加上这条约束后生成代码的可综合性明显提升。3.2 NTT 加速器的微架构选择NTT 是 Kyber 里最核心也最耗资源的运算。AI 生成的初版通常是一个简单的迭代结构一个蝶形单元循环 128 次完成 256 点 NTT。这个结构面积小但吞吐低。我在第二轮迭代时要求 AI 生成“4 并行蝶形单元、两级流水”的结构吞吐提升约 3.5 倍面积增加约 2.2 倍。这个取舍要看目标场景如果是低功耗物联网设备选小面积如果是服务器加速卡选高吞吐。这里有个细节值得注意NTT 的旋转因子存储方式。AI 初版把旋转因子放在寄存器数组里综合后面积很大。我改成用 ROM 存储配合地址生成器读取面积下降明显。这个优化 AI 不会主动做需要人在提示词里指定“旋转因子使用 ROM 实现”。3.3 UVM 验证环境的搭建要点UVM 环境我按标准结构搭kyber_ntt_if接口、kyber_ntt_seq_item事务、kyber_ntt_sequence激励序列、kyber_ntt_driver驱动、kyber_ntt_monitor采样、kyber_ntt_scoreboard比对、kyber_ntt_coverage覆盖率。其中 scoreboard 是关键它调用 Python 参考模型通过 DPI-C 或文件交互计算预期结果和 monitor 采到的实际结果比对。实操中遇到的一个问题是AI 生成的 RTL 在复位后第一个周期行为不确定导致 scoreboard 误报。解决办法是在 monitor 里加复位过滤复位释放后延迟若干周期再开始采样。这个细节在 UVM 标准文档里不会写但实际项目中很常见。3.4 寄存器模型的镜像值检查加速器的配置寄存器包括 NTT 点数、旋转因子基地址、启动位、完成中断使能等。用 RAL 建模后可以在 sequence 里调用reg_model.ctrl_reg.write()和read()RAL 会自动更新镜像值。当 DUT 读回值和镜像值不一致时说明要么写没成功要么硬件有 bug。我踩过的一个坑是AI 生成的 RTL 里某个寄存器是只读的但 RAL 模型里定义成了读写导致镜像值检查一直失败。后来对照 RTL 修正了 RAL 的访问属性才解决。4. 实操过程与核心环节实现4.1 环境准备与工具链我用的工具链是Python 3.10 做参考模型SystemVerilog 写 RTL 和 UVM仿真器用支持 UVM 1.2 的商业仿真器综合用主流综合工具做可综合性检查。目录结构如下pqc_accel/ ├── ref_model/ # Python 参考模型 │ ├── kyber_ntt.py │ └── poly_ops.py ├── rtl/ # AI 生成的 RTL │ ├── kyber_ntt_core.sv │ └── kyber_ntt_axi.sv ├── verif/ # UVM 验证环境 │ ├── kyber_ntt_if.sv │ ├── kyber_ntt_pkg.sv │ └── tb_top.sv └── scripts/ └── run_sim.sh这个结构清晰每层职责明确。参考模型和 RTL 分离方便独立更新。4.2 从命令到 RTL 的生成过程第一步是写命令。我把前面提到的提示词结构整理成一个模板每次生成时替换算法和参数部分。生成后不急着仿真先做三件事检查模块端口是否和接口定义一致、检查是否有 latch 推断、检查是否有不可综合语句。这三步能过滤掉大部分低级问题。第二步是人工审阅。AI 生成的 RTL 我重点看三处状态机是否有默认跳转、数组索引是否越界、位宽是否匹配。我遇到过 AI 生成的代码里两个不同位宽的信号直接相加仿真时高位被截断导致结果错误。这种问题综合工具不一定报错但功能是错的。第三步是接入验证环境。把 RTL 例化到tb_top里连上接口跑一遍基本激励。如果编译都过不了说明接口对不上回去改 RTL 或改接口定义。4.3 仿真调试与波形分析仿真跑起来后第一轮通常会有大量错误。我的调试顺序是先看复位和初始化再看单次事务最后看连续事务。单次事务能过说明基本功能对连续事务出错通常是流水线冲突或握手协议问题。波形分析时重点看 valid/ready 握手、状态机跳转、存储器读写地址。热搜词里提到“modelsim中能不能查看rtl电路图”这其实是问仿真器能不能看综合后的电路结构。答案是仿真器主要看波形和代码电路图要看综合工具的输出。但在调试时把 RTL 层次结构调出来看信号连接关系对定位问题很有帮助。4.4 覆盖率收集与收敛UVM 覆盖率包括代码覆盖率和功能覆盖率。代码覆盖率看行、条件、翻转功能覆盖率看是否覆盖了所有 NTT 点数、所有旋转因子模式、所有错误注入场景。我设定功能覆盖率目标为 95% 以上达不到就补 sequence。AI 生成的 RTL 往往在某些边界条件下有 bug覆盖率驱动能把这些边界找出来。5. 常见问题与排查技巧实录5.1 AI 生成 RTL 的典型问题速查表问题现象可能原因排查方法解决方式综合报 latch 推断if/case 缺少 else/default检查所有条件分支补全默认赋值仿真结果高位截断位宽不匹配检查赋值两侧位宽显式位宽转换握手死锁valid/ready 依赖成环看波形握手信号打破组合依赖复位后状态不定复位未覆盖所有寄存器检查复位逻辑补全复位赋值覆盖率上不去激励不够随机看覆盖率报告加约束和 sequence5.2 UVM 环境常见报错与处理UVM 环境搭建初期最常见的报错是uvm_config_db拿不到 virtual interface原因是 set 和 get 的路径不匹配。我的经验是在tb_top里 set 时用uvm_config_db#(virtual kyber_ntt_if)::set(null, uvm_test_top.*, vif, intf)在 driver 里 get 时路径要对应。另一个常见问题是 sequence 启动后 driver 没反应通常是seq_item_port没连上检查connect_phase里的连接语句。5.3 参考模型与 RTL 结果不一致的定位当 scoreboard 报比对失败时不要急着改 RTL。先确认参考模型本身对不对用已知测试向量验证 Python 模型。参考模型确认无误后再定位 RTL。定位方法是把失败的事务输入单独拿出来在 RTL 里打波形逐步跟踪中间结果找到第一个和参考模型不一致的节点。这个节点就是 bug 所在。5.4 从验证通过到 SoC IP 集成的注意事项验证通过不代表可以直接集成。集成前要确认接口是否符合 SoC 总线协议、时钟复位是否同步、中断是否正确连接、地址映射是否冲突。我建议先做一个最小子系统把加速器挂上去用 CPU 或总线主设备发一次事务确认能通。这一步能发现很多验证环境里覆盖不到的问题比如跨时钟域、总线位宽转换、地址解码错误。6. 个人实操体会与后续扩展方向这个项目做下来我最大的体会是AI 在硬件设计里的角色更像一个“不知疲倦的初级工程师”它能快速产出结构合理的初稿但缺乏对边界条件、时序收敛、可综合性的直觉。人的价值在于定义约束、搭建验证、判断取舍。一条命令生成 RTL 是可行的但“可验证”这三个字才是真正的门槛。后续我打算往两个方向扩展。一是把 Dilithium 签名算法也纳入进来它的采样和拒绝循环更复杂对 AI 生成能力是更大考验。二是把验证环境做成可配置的换算法时只改参考模型和 sequenceUVM 骨架复用。另外覆盖率驱动的自动激励生成也值得尝试让 AI 根据覆盖率缺口自动补 sequence形成闭环。这些方向我还在摸索有进展再分享。