星点匠心建站 星点匠心建站XD JIANXIN STUDIO
400-883-9527 免费定制方案
ARTICLE DETAIL

资讯详情

深耕新锐建站与运营推广的一线实战洞察。

EDA三巨头仿真验证技术路线对比与工程实践指南

EDA三巨头仿真验证技术路线对比与工程实践指南 仿真验证这件事在芯片设计圈子里有个很形象的比喻你花三个月画完一张复杂的电路图流片一次烧掉的钱够在一线城市买套房而仿真验证就是你在按下确认下单之前唯一能反复检查、反复试错的机会。EDA 三巨头——也就是这个行业里绕不开的那三家头部工具厂商——它们各自的仿真验证技术路线基本上代表了过去三十年数字电路验证方法论的演进史。我这些年做过几个中小规模的 SoC 项目从最开始的纯手工写激励到后面搭 UVM 环境、跑形式验证、做低功耗仿真几乎每一家的工具都摸过一遍。这篇文章我想把这条仿真验证之路拆开来讲清楚三家各自的技术底子是什么、为什么会长成那个样子、实际项目里怎么选、怎么用、哪些坑我踩过。不管你是刚入行的验证新人还是做了几年想系统梳理一下方法论的老手应该都能从里面找到能直接抄作业的东西。1. 先搞清楚仿真验证到底在验什么1.1 仿真验证的本质在虚拟世界里把芯片跑一遍很多人刚接触验证的时候会把它和测试混为一谈。测试是芯片回来之后拿示波器、拿测试机去测那是物理世界的事仿真验证是在流片之前用软件把设计的行为模拟出来喂各种激励进去看输出对不对。它的核心价值在于在还没有硅片的时候就能发现设计里的功能缺陷。一个中等规模的 SoC验证工作量往往占到整个项目人力的 60% 以上这个比例不是拍脑袋来的。因为芯片一旦流片失败重来的成本是千万级别而仿真验证多花的人力成本相比之下几乎可以忽略。所以行业里有个共识验证不是做完设计之后补的工序而是和设计并行的、同等重要的一条主线。仿真验证要验的东西粗分下来有这么几类功能正确性逻辑对不对、时序行为在时钟节拍下行为是否符合预期、协议一致性接口是否符合总线协议、低功耗行为电源关断、时钟门控下状态是否保持、以及一些边界和异常场景。不同的验证目标对应不同的技术手段这也是为什么三巨头会演化出各自不同的工具矩阵。1.2 从定向测试到随机约束方法论的两次跃迁早期的验证是定向测试为主工程师手写一个个测试用例针对每个功能点写激励、写期望值。这种方式直观但覆盖率上不去——你只能测到你想到的场景想不到的就永远测不到。一个稍微复杂的模块定向用例写到几百个之后边际收益急剧下降。第一次跃迁是随机约束验证的普及。核心思路是不再手写具体激励而是描述激励的合法范围和约束条件让工具自动生成大量随机激励。这样能覆盖到人脑想不到的组合。配合功能覆盖率covergroup来度量到底测到了多少验证从我觉得测够了变成了数据说话。第二次跃迁是形式验证和仿真验证的融合。形式验证不跑具体激励而是用数学方法穷举证明某个性质在所有情况下都成立。它和仿真是互补的仿真擅长复杂场景和大规模设计形式验证擅长把某些关键性质证死。现在主流的方法学是把两者结合用形式验证去啃那些仿真很难覆盖到的角落。这两次跃迁恰好对应了三巨头各自的技术积累方向下面逐个拆。2. 三巨头的技术底子与路线差异2.1 第一家仿真器起家靠快和生态筑墙这家厂商的看家本领是事件驱动仿真器。它的核心优势在于编译型仿真技术——把 Verilog/SystemVerilog 代码先编译成高度优化的中间表示再执行速度比传统的解释型仿真器快一个数量级。我实测过一个中等规模的模块同样的测试平台编译型仿真跑完一轮大概十几分钟解释型要一个多小时差距非常明显。它的护城河不只是速度更是验证方法学的完整生态。UVM 这套现在几乎成了行业标准的方法学最早的成熟实现和工具支持就是围绕它建立的。你写一个 UVM 环境从激励产生、参考模型、记分板到覆盖率收集整条链路都有成熟的库和工具支撑。对团队来说选它意味着招人好招、资料好找、出了问题有人问这种生态优势在工程上比单纯的技术指标更重要。但它的短板也明显license 贵而且对硬件仿真加速的依赖度高。大规模 SoC 的回归测试纯软件仿真跑不动得上硬件加速器那又是一笔不小的投入。中小团队用起来会有压力。2.2 第二家形式验证的旗手把证明做成了产品第二家的技术标签非常清晰形式验证。它起家的工具就是等价性检查formal equivalence checking用来证明综合后的网表和 RTL 在功能上等价。这个能力在流程里极其关键——综合、插入 DFT、时钟树变换之后你怎么保证网表还是原来那个逻辑靠仿真跑用例是跑不全的等价性检查用数学方法直接证几小时就能给出确定结论。后来它把形式验证扩展到了属性检查property checking和连通性检查。属性检查的思路是你用 SVASystemVerilog Assertion描述这个信号在任何情况下都不应该和那个信号同时为高这类性质工具去穷举证明。证明通过就是真的在所有情况下都成立比仿真跑一万个用例还有说服力。这条路线的价值在于补仿真的盲区。仿真再随机也有覆盖不到的死角形式验证在它能力范围内是完备的。但形式验证有个硬约束状态空间爆炸。设计一大工具就跑不动了所以它更适合模块级、接口级、以及一些控制逻辑密集的块不适合拿来做整个 SoC 的顶层验证。实际项目里我的用法是仿真做主力覆盖形式验证挑关键性质去证死两者配合。2.3 第三家低功耗与混合信号仿真见长第三家的差异化在低功耗验证和混合信号仿真。随着移动设备和物联网芯片爆发功耗成了和功能同等重要的指标。低功耗设计的核心手段是电源域划分、电源关断、时钟门控、体偏置等这些手段会引入大量正常功能仿真里看不到的状态——比如某个电源域关掉之后里面的寄存器值是否保持、隔离单元是否正确钳位、电源开关的时序是否满足。这家厂商的低功耗验证工具能把 UPF统一功耗格式描述和 RTL 仿真结合起来在仿真过程中动态地关电、开电检查低功耗结构是否正确。我做过一个多电源域的项目纯功能仿真完全看不出问题一上低功耗仿真立刻暴露出某个隔离单元在电源关断时没有正确钳位导致下游逻辑收到不确定值。这种 bug 如果漏到硅片上就是灾难性的。混合信号仿真则是它的另一块阵地把模拟电路的行为模型和数字仿真在同一个环境里跑解决数模接口的验证问题。这块相对小众但做模拟前端、电源管理的团队绕不开。2.4 三家路线对比没有全能选手只有场景匹配把三家放在一张表里对比思路会清楚很多维度第一家仿真器生态第二家形式验证第三家低功耗混合信号核心强项编译型仿真速度、UVM 生态等价性检查、属性证明低功耗仿真、数模混合典型场景大规模 SoC 回归、VIP 复用网表等价、关键性质证死多电源域、模拟接口主要短板license 成本高、依赖硬件加速状态空间受限、大设计跑不动通用仿真生态相对弱适合团队中大型、方法学成熟有形式验证经验的团队低功耗/混合信号产品这张表不是让你选一家站队而是告诉你真实项目里往往是三家工具混用。仿真主力用第一家关键性质用第二家证低功耗场景用第三家跑这是很多团队的常态。工具是手段验证目标是目的别被用哪家这个问题带偏。3. 搭一个能跑的仿真验证环境从零到回归3.1 环境骨架一个最小可用的 UVM 平台长什么样不管用哪家工具UVM 平台的骨架是相通的。我按实际搭环境的顺序讲。一个最小可用的验证平台包含这几个组件激励产生器sequence/sequencer、驱动器driver、监视器monitor、参考模型reference model、记分板scoreboard、覆盖率收集coverage。先建顶层 testbench 模块例化 DUT被测设计把接口信号用 virtual interface 的方式传给 UVM 环境。这一步的关键是接口的抽象层次——不要把 DUT 的每个信号都直接连到 testbench 里到处引用而是通过 interface 打包再通过 config_db 传递。我见过新手直接在 testbench 里用绝对路径引用 DUT 内部信号结果 DUT 一改层次结构整个平台全崩。用 interface config_db 是标准做法虽然多写几行但后期维护成本低太多。interface my_if(input logic clk, input logic rst_n); logic valid; logic [31:0] data; logic ready; clocking drv_cb (posedge clk); default input #1step output #0; output valid, data; input ready; endclocking modport DRV(clocking drv_cb); endinterface上面这段是个接口模板clocking block 的作用是把驱动和采样的时序关系固定下来避免竞争冒险。#1step和#0的取值是有讲究的输入采样在时钟沿前一个时间步输出驱动在时钟沿后零延迟这样能保证驱动和采样不打架。这个细节很多教程不讲但不设好仿真里会出现莫名其妙的 X 传播。3.2 激励与约束随机不是乱来随机约束验证的核心是约束不是随机。你得把激励的合法空间描述清楚工具才能在这个空间里高效采样。举个实际例子验证一个带 burst 传输的总线接口约束大概长这样class burst_seq extends uvm_sequence #(bus_item); uvm_object_utils(burst_seq) rand int unsigned burst_len; constraint c_len { burst_len inside {[1:16]}; burst_len dist {1 : 3, [2:8] :/ 5, [9:16] :/ 2}; } task body(); repeat (burst_len) begin uvm_do_with(req, {req.kind WRITE;}) end endtask endclass这里dist的用法是关键短 burst 出现概率高权重 3中等长度权重 5长 burst 权重 2。为什么要这样配因为实际系统里短传输最常见长 burst 是压力场景权重低但必须覆盖到。如果你均匀随机长 burst 会被过度采样浪费仿真时间如果完全不采长 burst压力场景又漏了。权重的设置要基于对真实流量特征的理解这是约束随机里最考验经验的地方工具帮不了你。注意约束写得太紧随机空间被压得很小覆盖率上不去写得太松大量非法激励被生成又被丢弃仿真效率低。这个平衡点要靠覆盖率报告反复调。3.3 参考模型与记分板判断对错的裁判激励进去了怎么判断 DUT 输出对不对靠参考模型。参考模型是 DUT 功能的黄金实现可以是 SystemVerilog 写的行为级模型也可以是用 C/C 写的、通过 DPI 接进来的模型。DUT 的输出和参考模型的输出都送进记分板比对不一致就报错。这里有个实操经验参考模型不要照抄 RTL 的实现思路。如果参考模型和 RTL 是同一套逻辑写的那 RTL 里的理解错误会同时出现在两边比对永远通过bug 就漏了。参考模型应该基于规格文档独立实现最好由不同的人写形成交叉验证。我做过一个项目参考模型是照着 RTL 翻译的结果一个边界条件两边都错得一模一样直到流片前做形式验证才被揪出来惊出一身冷汗。记分板的设计要注意乱序比对。很多总线协议支持乱序返回DUT 的输出顺序和激励顺序不一致。记分板不能简单地按顺序比得用事务 ID 或者地址做索引把期望值和实际值配对后再比。这个坑我踩过一开始按顺序比报了一堆假错查了半天才发现是乱序返回。3.4 覆盖率驱动什么时候可以停验证什么时候算做完靠覆盖率说话。功能覆盖率covergroup度量你测到了哪些场景代码覆盖率行覆盖、分支覆盖、翻转覆盖度量 RTL 的哪些部分被执行到。两个都要看但功能覆盖率是主代码覆盖率是辅。代码覆盖率 100% 不代表功能验全了——它只说明每行代码都跑过不说明每个场景组合都测过。反过来功能覆盖率达标但代码覆盖率有大量空洞说明有些 RTL 分支根本没被激励触及可能是设计冗余也可能是激励没覆盖到要逐个分析。covergroup bus_cg (posedge clk); cp_len: coverpoint burst_len { bins short_b {[1:4]}; bins mid_b {[5:8]}; bins long_b {[9:16]}; } cp_kind: coverpoint kind; cx_len_kind: cross cp_len, cp_kind; endgroupcross 交叉覆盖是重点它度量的是长度和类型的组合是否都测到了。很多 bug 就藏在组合场景里单看每个维度都覆盖了交叉起来却有空洞。回归跑完看报告先看 cross 的空洞再回头补定向激励或者调约束权重这是标准闭环。4. 形式验证怎么用才不浪费4.1 等价性检查流程里的守门员等价性检查在流程里的位置很固定综合之后、插入 DFT 之后、时钟树综合之后每个变换节点都要跑一次证明变换前后的网表功能等价。这个检查是自动化的你只需要给出参考网表和实现网表工具自己找对应关系、自己证。实操里最容易出问题的是对应关系建立。综合工具会做寄存器复制、逻辑重组、边界优化导致两边的信号名对不上。工具靠名字匹配失败后会退化成靠结构匹配但结构匹配在复杂设计上容易出错报一堆非等价。这时候不要慌先看报出来的非等价点是不是真的功能差异很多是匹配错误导致的假阳性。手动加 mapping 约束把关键寄存器、关键端口的对应关系显式指定假阳性会大幅减少。提示等价性检查跑不动的时候可以按模块切分先证子模块再证顶层。顶层直接证状态空间太大工具容易卡死。4.2 属性检查把不可能发生证死属性检查的用法是用 SVA 写性质工具穷举证明。适合证的性质有几类互斥性两个信号不能同时有效、不变式某个条件永远成立、可达性某个状态一定能到达或一定不能到达。写性质有个原则从规格出发不从 RTL 出发。比如规格说读和写不能同时进行你就写assert property ((posedge clk) !(rd_en wr_en))而不是看着 RTL 里有个互斥逻辑就照着写。前者能抓出 RTL 违反规格的 bug后者只是把 RTL 重述一遍没有验证价值。属性检查跑不动是常态。工具会报bound reached或者inconclusive意思是它在给定的深度内没证完。这时候的应对手段加约束缩小输入空间、加 assume 把环境行为约束住、或者把大性质拆成小性质分块证。不要为了让它跑完而乱加 assumeassume 加多了等于把要证的东西假设掉了证出来的结果是空的。4.3 形式验证和仿真的分工我的经验分工是这样的控制逻辑密集、状态空间不大的模块优先上形式验证比如仲裁器、握手协议、状态机数据通路密集、规模大的模块以仿真为主形式验证挑关键性质辅助。接口协议的一致性可以用形式验证的协议检查工具跑比仿真写协议检查器省事。形式验证不是更高级所以更好它是另一种手段有明确的适用边界。把它用在合适的地方能大幅提升验证完备性用错地方就是浪费时间等工具跑不完。5. 低功耗仿真那些功能仿真看不见的坑5.1 UPF 描述与仿真的结合低功耗验证的前提是有 UPF 描述电源域怎么划分、每个域的电源开关是什么、隔离单元和电平转换器插在哪、保持寄存器有哪些。UPF 写完之后仿真工具要能理解它在仿真过程中动态控制电源状态。具体做法是把 UPF 和 RTL 一起读进仿真器仿真器会为每个电源域维护一个电源状态当激励触发电源关断时仿真器把该域内的信号强制成关断状态通常是 X 或者保持值同时检查隔离单元的输出是否符合预期。这个机制让关电变成了仿真里可观测、可检查的事件。我踩过的一个坑UPF 里的电源域层次和 RTL 层次对不上。UPF 是按设计意图写的RTL 综合后层次会变如果 UPF 没有及时更新仿真器会找不到对应的域要么报错要么静默地把某些信号当成常开低功耗检查就形同虚设。所以 UPF 和 RTL 的层次同步要纳入日常检查清单。5.2 隔离与保持的检查要点低功耗仿真要重点检查两类结构隔离单元和保持寄存器。隔离单元的作用是当一个电源域关断时它的输出不能是浮空的 X要被钳位到一个确定值通常是 0 或 1否则下游常开域的逻辑会收到 X行为不可预测。仿真里要检查关断发生时隔离单元的输出是否在规定的时钟周期内被钳位、钳位值是否符合规格。保持寄存器的作用是电源关断期间某些寄存器的值要保住靠的是备份电源或者保持锁存结构。仿真里要检查关断再上电之后这些寄存器的值是否和关断前一致。这个检查用 SVA 写最方便在关断前采样寄存器值上电后比对。property p_retention_check; (posedge clk) disable iff (!rst_n) ($rose(pwr_off) |- ##[1:5] $stable(ret_reg_q)); endproperty上面这个性质的意思是电源关断信号上升后保持寄存器的值在若干周期内保持不变。实际写的时候要根据电源开关的时序参数调整延迟窗口这个窗口设错了会报假错。5.3 低功耗场景的激励设计低功耗仿真的激励和功能仿真不一样重点是构造电源状态切换的场景正常工作时突然关某个域、关断期间有外部事件进来、上电顺序不同、多个域同时切换等等。这些场景在功能仿真里根本不会出现因为功能仿真默认所有电源都是开的。我的做法是单独写一套低功耗场景的 sequence专门驱动电源控制信号配合功能激励一起跑。比如先跑一段正常读写中途触发某个域关断再触发上电检查数据是否保持、隔离是否正确。这套 sequence 要覆盖所有电源域的开关组合组合数多了会爆炸所以要基于实际使用场景做裁剪优先覆盖产品真实会遇到的电源状态迁移。6. 常见问题与排查实录6.1 仿真跑不通/结果不对的排查顺序仿真出问题排查要有顺序别上来就瞎改。我的固定顺序是先看编译有没有 warning尤其是隐式声明、位宽不匹配、再看复位和初始化、再看时钟和时序、最后看激励和比对逻辑。位宽不匹配是最常见的隐形杀手。SystemVerilog 里赋值时位宽不一致高位会被截断或者补零编译器可能只给个 warning仿真结果却错了。我习惯在编译选项里把位宽相关的 warning 升级成 error强制在编译期暴露。X 传播是另一个高频问题。仿真里出现 X先追 X 的源头是未初始化的寄存器、还是多驱动、还是读到了未写的存储器。用仿真器的 X 追踪功能dump 波形时打开 X 传播记录能快速定位第一个产生 X 的点从那里往回查。6.2 覆盖率上不去的典型原因覆盖率卡在某个值上不去原因通常有几类我整理成速查表现象可能原因处理思路某 cross 空洞约束把组合空间压太小放宽约束或加定向激励代码分支未覆盖激励没触及该分支条件分析分支条件构造激励翻转覆盖率低某些信号长期不变检查是否死逻辑或激励不足覆盖率虚高covergroup 定义太粗细化 bins加 cross覆盖率虚高这个要特别警惕。有些团队为了达标把 covergroup 写得很粗几个大 bin 一盖数字很好看实际什么都没验到。覆盖率的定义要能反映真实验证意图这是设计 covergroup 时最该花心思的地方。6.3 回归测试的效率优化回归跑得慢是常态优化手段我按性价比排序第一并行化把测试用例分发到多台机器或者多个仿真进程并行跑这是最直接的第二编译缓存把不常变的 VIP 和参考模型预编译成库只编译变化的 DUT 和 testbench第三用例裁剪把已经稳定覆盖的场景从每日回归里拿掉放到周回归第四仿真器优化选项比如关闭不必要的波形 dump、调高优化等级。波形 dump 是隐藏的性能杀手。全量 dump 波形能让仿真慢好几倍日常回归根本不需要全量波形只在失败用例上开波形就行。这个习惯能省大量时间。7. 我个人的几点实操体会做了这些年验证有几个体会是文档里不会写、但实际特别管用的。第一验证平台本身也是要验证的。参考模型、记分板、覆盖率收集逻辑都可能出错。我习惯在平台搭好后先故意往 DUT 里注入一个已知 bug看平台能不能抓到。抓不到说明平台有问题先修平台再继续。这个平台自检步骤能避免大量假通过。第二别迷信工具要理解工具在干什么。仿真器、形式验证工具都是黑盒但它们的报错信息、覆盖率报告背后是有逻辑的。花时间读懂工具的输出比盲目调参数有用得多。比如形式验证报 inconclusive你得知道是深度不够还是约束太松才能对症下药。第三验证计划比验证平台更重要。平台是骨架验证计划是灵魂。哪些功能要验、用什么手段验、验到什么程度算够这些想清楚了平台怎么搭是水到渠成的事。我见过太多团队一上来就埋头搭 UVM搭了两个月发现验证计划没理清返工重来。第四三巨头的工具学一家精通、其余会用即可。方法学是相通的UVM 的思想、形式验证的思路、低功耗验证的检查点换一家工具只是 API 和命令不同。把一家的工具用透建立完整的方法论认知换工具的成本比想象中低。真正值钱的是方法论不是某个工具的快捷键。最后分享一个小技巧每次回归失败把失败用例的随机种子seed记下来。随机约束验证的失败往往依赖特定种子记下种子就能稳定复现排查效率高很多。这个习惯我从入行保持到现在救过无数次命。
返回列表