1. 项目缘起当传统验证方法撞上“覆盖率墙”在芯片设计这个行当里硬件验证工程师的日常很大一部分是在和“覆盖率”较劲。我们写测试、跑仿真目标就是把设计的功能点、状态机、边界条件都“踩”一遍用覆盖率这个量化指标来衡量验证的完备性。但干久了就会发现覆盖率收敛到后期那叫一个痛苦。90%到95%可能还算顺利但从95%往上每提升一个百分点投入的精力都是指数级增长。那些剩下的、死活覆盖不到的“覆盖空洞”就像房间里最后几粒灰尘你知道它在那儿但就是找不到合适的“扫帚”激励去扫到它。传统的解决方法无非是手动分析波形、写定向测试、或者依赖约束随机验证CRV工具寄希望于海量的随机种子能“蒙”中那些角落。但这种方法效率低下严重依赖工程师的经验和直觉而且对于复杂设计其状态空间巨大随机方法无异于大海捞针。最近几年大语言模型LLM在代码生成、逻辑推理上的能力让人眼前一亮。我就在想能不能把LLM这股“东风”借过来帮我们解决覆盖率收敛这个老大难问题这个项目的核心就是探索如何利用LLM的推理能力来智能地分配验证资源我称之为“推理时令牌分配”并对那些顽固的覆盖空洞进行自动分类和根因分析。这听起来有点跨界但底层逻辑是相通的验证场景的生成本质上也是一种在特定约束下的“创作”或“规划”问题。2. 核心构想LLM如何介入硬件验证流程要让LLM在硬件验证中发挥作用不能把它当成一个黑盒魔法。我们需要清晰地定义它的角色、输入和输出并将其无缝嵌入到现有的验证流程中。我的构想主要围绕两个核心环节展开。2.1 推理时令牌分配让LLM成为“测试策略师”在验证中我们的资源如仿真机时、工程师精力是有限的。传统的随机验证均匀地分配资源或者靠工程师拍脑袋决定重点。而“推理时令牌分配”的核心思想是让LLM在每次测试生成或选择时动态地、有策略地分配其“注意力”或“计算资源”类比于LLM生成文本时的令牌。具体如何操作状态输入我们将当前的验证状态“翻译”成LLM能理解的语言。这包括覆盖率报告以结构化文本如JSON或自然语言摘要的形式告诉LLM哪些覆盖点已经达成哪些是空洞。设计规格Spec与验证计划Test Plan提供设计的功能描述和待验证项。历史测试序列与结果提供之前运行过的测试及其触发的覆盖点让LLM了解“我们曾经尝试过什么”。提示工程Prompt Engineering这是最关键的一步。我们需要设计提示词引导LLM扮演一个“验证策略师”的角色。例如“你是一个资深硬件验证专家。当前设计是一个32位RISC-V CPU的ALU模块。覆盖率报告显示加法溢出标志overflow flag在输入为‘最大正数1’的场景下始终未被覆盖。已知我们已经运行了2000个随机测试测试约束是操作数在[-100, 100]之间。请分析这个覆盖空洞的可能原因并生成一个具体的、最有可能击中该覆盖点的测试场景描述包括操作码、操作数A、操作数B的取值。请一步步推理。”LLM推理与输出LLM基于上述输入进行推理。它可能会分析出“随机约束[-100,100]根本不可能产生‘最大正数1’的场景这是一个约束过紧导致的空洞。” 然后输出一个测试建议“将操作数A设置为32‘h7FFFFFFF最大正数操作数B设置为32‘h00000001操作码为ADD。”令牌分配隐喻在这个过程中LLM的“思考”过程Chain-of-Thought就是对不同可能性进行评估和资源分配。它可能会将更多的“思考令牌”用于分析溢出标志相关的逻辑而不是去考虑与当前空洞无关的移位操作。这就是一种智能的、目标导向的“令牌分配”。实操心得与注意事项提示词的质量决定一切。初期需要大量迭代明确告诉LLM你需要它输出什么格式如具体的信号值、断言语句、SystemVerilog代码片段。LLM的“幻觉”问题。它可能会给出语法正确但语义无效的建议比如推荐一个不存在的寄存器。因此LLM的输出必须经过一个语法/语义检查器Checker的验证才能转化为可执行的测试或约束。我们可以把它看作一个“建议引擎”而非“执行引擎”。成本与延迟。调用商用LLM API有成本和延迟。对于实时性要求高的验证环境可以考虑在本地部署轻量化的开源模型如Qwen、Llama等或者将LLM推理作为离线分析环节定期如每晚运行生成一批高质量的定向测试建议供次日执行。2.2 覆盖空洞分类让LLM成为“根因分析师”覆盖空洞之所以难搞是因为原因多种多样。手动分析每个空洞耗时耗力。LLM在这里可以扮演一个自动分类和初步诊断的角色。我们期望LLM能帮我们将空洞分类例如约束过紧型随机约束限制了信号取值空间无法产生所需场景。如上文的ALU例子设计缺陷型代码本身有bug导致该场景逻辑上不可达。验证环境缺失型测试平台Testbench缺少相应的驱动或检查机制。场景复杂型需要多个罕见事件在特定时序下同时发生概率极低。理解偏差型覆盖点定义与设计者意图或规格说明不一致。实现流程输入增强除了覆盖率报告还需要提供空洞相关的上下文信息如该覆盖点对应的RTL代码片段。该功能点的设计规格描述。验证环境中相关的驱动、监控Monitor、检查器Checker代码。多轮对话分析设计一个多轮对话流程。第一轮让LLM根据已有信息给出初步分类和置信度。第二轮针对疑点要求LLM给出需要进一步查看的信息例如“要确认是否为设计缺陷我需要查看模块A和模块B之间的握手协议波形图”。虽然LLM不能直接看波形但我们可以将波形关键信息摘要后输入。输出诊断报告LLM最终输出一个结构化的诊断报告包括空洞ID、分类结果、置信度、根因分析摘要、以及下一步行动建议如放松约束constraint_a检查module_x的第55行代码在测试平台中添加对signal_y的驱动。避坑指南不要指望LLM直接找到Bug。它的核心价值是缩小排查范围和提供排查方向。将工程师从漫无目的的手动分析中解放出来聚焦于LLM提示的高概率区域。建立反馈闭环当工程师根据LLM的建议真正定位并解决了问题如修复了一个Bug这个“空洞-原因-解决”的案例应该被记录下来作为高质量数据反馈给LLM的微调Fine-tuning过程使其越来越准。警惕“知识截止”问题LLM的训练数据可能不包含你们公司内部特定的设计规范或验证方法论。需要通过在提示词中提供充足的上下文或对模型进行领域适配Domain Adaptation来弥补。3. 技术栈选型与本地化部署考量要实现上述构想我们需要搭建一个从验证环境到LLM再回来的闭环系统。技术选型至关重要。3.1 LLM模型的选择云端巨兽 vs. 本地小模型这是一个权衡成本、性能、隐私和定制化需求的过程。特性商用云端API (如 GPT-4, Claude)本地开源模型 (如 Qwen-7B, Llama-3-8B)能力极强逻辑推理、代码理解能力顶尖中等至良好7B-14B参数模型已具备相当强的推理能力成本按Token收费长期大量使用成本高一次性硬件投入GPU后续边际成本低延迟网络请求有延迟几百毫秒至秒级本地推理延迟低取决于模型大小和硬件数据隐私数据需发送至第三方有隐私风险数据完全本地处理隐私安全定制化有限主要通过提示词工程可完全微调Fine-tuning适配公司内部术语和流程部署复杂度简单调用API即可复杂需搭建推理服务器、处理模型加载、优化等我的建议 对于探索性项目或对数据隐私要求极高的内部设计优先考虑本地部署开源模型。例如使用Qwen-7B-Chat或Llama-3-8B-Instruct这类对话优化模型。它们的性能对于代码理解、逻辑推理类的任务已经足够。部署时可以利用vLLM或llama.cpp这类高性能推理框架来提升吞吐量和降低延迟。注意如果选择本地部署需要配备足够的GPU内存如RTX 4090 24GB可流畅运行7B模型。CPU推理通过llama.cpp虽然可行但速度会慢很多适合对延迟不敏感的离线分析任务。3.2 工程集成构建验证智能体Verification AgentLLM不能孤立存在。我们需要构建一个“验证智能体”框架作为验证环境与LLM之间的桥梁。这个框架的核心组件包括信息提取器Extractor从仿真工具如VCS, Xcelium和版本管理系统中自动提取覆盖率报告、RTL代码片段、日志文件并将其转换为结构化的文本数据。提示词组装器Prompter根据不同的任务空洞分类、测试生成将提取的信息填充到预设的提示词模板中形成完整的LLM输入。LLM调用客户端LLM Client负责与本地或云端的LLM服务进行通信发送请求并接收响应。需要处理错误重试、流式响应等。结果解析与验证器Parser Validator解析LLM返回的自然语言或结构化文本。例如将“设置操作数为0x7FFFFFFF”解析为具体的SystemVerilog约束或测试向量。最关键的是解析后的结果必须通过一个简单的语法/语义检查器确保其符合硬件描述语言的规范并且不包含明显错误。执行器Executor将验证通过的测试策略或约束自动转换为可执行的测试用例并提交给回归测试队列或直接运行仿真。反馈收集器Feedback Collector记录LLM建议的测试是否真正击中了目标覆盖点以及工程师对诊断结果的确认形成训练数据闭环。这个框架可以用Python来搭建利用其丰富的库如json,jinja2用于模板openai/anthropic的SDK或transformers库用于本地模型进行快速原型开发。4. 实战演练一个ALU覆盖空洞的解决案例让我们通过一个具体的、简化的例子把上面的理论串起来。假设我们有一个ALU模块其中一个覆盖点coverpoint_overflow_add始终为0。步骤1信息提取与提示词组装信息提取器从报告中获取空洞信息并从代码库中找到对应RTL// ALU模块片段 always_comb begin case(opcode) ADD: result operand_a operand_b; // ... 其他操作 endcase overflow (operand_a[31] operand_b[31]) (result[31] ! operand_a[31]); end以及验证计划中对该覆盖点的描述“验证加法运算中同号两数相加导致符号位改变的溢出情况。”提示词组装器生成如下提示给本地Qwen模型[系统指令] 你是一个硬件验证专家擅长分析覆盖空洞原因。 [用户输入] 任务分析以下覆盖空洞的根本原因并提供具体的测试修改建议。 - 覆盖点coverpoint_overflow_add。描述加法运算结果溢出。 - 当前状态该点覆盖率为0%。已运行5000个随机测试。 - 相关RTL代码[上面所示的代码片段] - 当前测试约束 constraint c_operands { operand_a inside {[-100:100]}; operand_b inside {[-100:100]}; } - 设计规格32位有符号整数加法溢出标志在结果符号位与操作数符号位不一致时置位。 请逐步推理并最终按以下格式输出 原因分类[约束过紧型/设计缺陷型/环境缺失型/复杂场景型/理解偏差型] 根因分析[你的分析] 测试建议[具体的操作码、operand_a、operand_b建议值或约束修改建议]步骤2LLM推理与输出本地部署的Qwen-7B模型经过推理返回原因分类约束过紧型 根因分析当前约束将两个32位操作数限制在[-100, 100]范围内。在这个范围内任何两个同号数相加其结果的绝对值最大为200远小于32位有符号整数的最大值(2^31-1 ≈ 21亿)因此其符号位根本不可能改变从数学上就决定了溢出标志永远不会被置位。这不是设计错误而是测试激励无法产生目标场景。 测试建议修改约束使其能产生导致符号位改变的大数。例如可以添加约束使操作数A为正数且接近最大值操作数B为正数。建议值operand_a 32‘h7FFFFFFF; operand_b 32’h00000001; opcode ADD。步骤3结果解析与执行解析器识别出“约束过紧型”和具体的测试建议。验证器会检查建议值是否在32位有符号数范围内是的。然后执行器自动生成一个新的测试用例或者修改现有测试的约束将operand_a和operand_b的约束范围扩大特别是包含边界值constraint c_operands { operand_a inside {[32h7FFFFFF0:32h7FFFFFFF], [-100:100]}; // 增加正大数范围 operand_b inside {[1:100], [-100:-1]}; // 保证有正数 (operand_a 0) - (operand_b 0); // 引导产生同号正数相加 }提交这个测试后仿真很快就能击中coverpoint_overflow_add。个人体会这个过程最爽的点在于LLM不是靠“蒙”而是基于代码和约束进行了逻辑推理。它直接指出了“数学上不可能”这个根本原因这是传统随机测试盲目跑再多次也未必能清晰总结出来的。工程师的职责从“大海捞针”变成了“审核LLM的推理报告并执行最佳方案”效率和洞察深度都提升了。5. 面临的挑战与未来展望当然这个方向还处于早期探索阶段有不少挑战领域知识壁垒LLM对硬件描述语言Verilog/VHDL、验证方法学UVM的深层理解有限容易产生表面正确但实际无效的建议。需要大量的领域数据微调和高质量的提示词工程。评估指标如何量化LLM的介入带来的价值是覆盖率提升速度还是工程师分析时间的减少需要建立一套合理的评估体系。流程整合将LLM智能体深度集成到现有的CI/CD验证流程中保证其稳定、可靠、可重复运行是一个系统工程问题。长上下文与成本复杂的芯片设计代码量巨大覆盖率报告也很庞大。如何让LLM有效处理长上下文同时控制推理成本是一个技术难点。尽管有挑战但我对这个方向充满信心。它代表的是一种范式转变从“人工编写所有测试”到“人机协同智能生成测试策略”。未来的验证工程师可能更像是一个“提示词工程师”和“结果审核专家”将重复性、探索性的劳动交给AI自己则专注于最核心的架构判断和复杂问题攻坚。我目前正在自己的团队里小范围试点这个方案用本地化的Qwen模型处理一些中等复杂模块的后期覆盖率问题。初步效果是它能将我们分析单个顽固空洞的平均时间从几小时缩短到几分钟并且提供了不少我们之前没想到的测试角度。如果你也在为覆盖率收敛头疼不妨尝试一下这个思路从一个小模块开始搭建你的第一个“验证智能体”。