HSPICE瞬态仿真后,用Custom Waveform精准测量延迟与功耗的实战指南
做集成电路、电源或者混合信号仿真的人应该都有过这种经历HSPICE 跑了好几个小时波形也出来了本来以为万事大吉结果一量延迟和功耗数据怎么都对不上。前阵子我帮同事复核一条 buffer 链的性能用 Custom Waveform 从同一个 .tr0 文件里取数和当初仿真脚本里写的 .meas 结果差了将近一倍。当时第一反应以为是电路出了问题后来折腾了半天才发现问题出在测量窗口、阈值定义和电流方向这些看似不起眼的细节上。这篇文章就把我这次排查过程中踩过的坑整理一下。内容围绕 HSPICE 瞬态仿真后怎么用 Custom Waveform 把延迟和功耗测准这件事展开包含表达式设置、窗口选择、时间步长影响以及一组实测数据对比。适合刚接触 Custom Waveform 的人也适合那些已经用了很久但总觉得数据“差点意思”的工程师。1. 延迟和功耗为什么必须统一“测量口径”再谈准确性1.1 我踩过的现场同一份波形三个数先说那天到底发生了什么。被测电路是一串 5 级反相器组成的驱动链输出端接了 1pF 的负载电容电源电压 1.8V输入是 5MHz 的方波。仿真脚本里我用 .meas 语句分别量了 t_pLH、t_pHL 和平均功耗结果分别为 4.87ns、4.52ns 和 118.6uW。后来同事用 Custom Waveform 打开同一个波形文件直接拖了两条 cursor 过去量出来却是 t_pLH5.63ns平均功耗变成了 139.2uW。差这么多谁看了都会慌。我先是怀疑模型库加载有问题又检查了电源和地网络最后甚至重跑了一遍仿真。直到我把两个测量方式的阈值对比放在一起才真相大白Custom Waveform 默认的延迟阈值是 1.2V而我 .meas 里用的是 0.9V因为我们的 VDD 是 1.8V50% 阈值就是 0.9V。阈值不一样延迟自然就不一样尤其是上升沿比较缓的节点差了 0.7ns 一点不奇怪。这类问题在团队协作里特别常见。做过时序分析的人都知道延迟不是“波形上随便找一个点”而是一个有明确约定、有阈值、有边沿定义的量。只要阈值、边沿、窗口三者任何一个没对齐结果就没有可比性。所以谈 Custom Waveform 怎么用之前首先要养成一个习惯所有测量都必须先明确定义再去看数字。1.2 延迟/功耗测量的通用口径延迟最通用的口径是传输延迟propagation delay。输入端信号到达 50% VDD 的时刻到输出端信号到达 50% VDD 的时刻两者的差值就是这一级电路的传输延迟。上升沿对应的叫 t_pLH下降沿对应的叫 t_pHL。有些场景还会区分 rise time 和 fall time但那是信号边沿速率不是延迟容易混。功耗测量则复杂一点。瞬态仿真里我们能拿到的功耗主要有几个层面静态功耗、短路功耗、开关功耗以及峰值功耗。平均功耗一般用一段时间内的能量积分除以时间长度得到峰值功耗则是在某个时刻或非常短的时间窗内电源电压和电源电流乘积的最大值。这里特别容易出问题的是“时间长度”该取多少以及电流方向怎么算。延迟和功耗看似两个独立指标实际上对测量参数的要求是一致的必须有一个稳定的、可复现的时间窗口。延迟要看稳态下的信号翻转功耗同样要看稳态下的周期性行为。如果一上来就把整个 tran 波形全选进来电源启动阶段的电流尖峰会被算进平均功耗里延迟测量也容易把第一个边沿的“冷启动”状态包含进去。这就是很多“奇怪数据”的真正来源。2. Custom Waveform 量延迟阈值、边沿和波形索引位一个都不能错2.1 延迟量测的设置步骤Custom Waveform 打开 .tr0 文件后最常规的做法是直接选中信号 v(in) 和 v(out)然后在波形窗口的测量菜单里选择 Transition Delay。这里一定要手动配置三个参数阈值电压、信号边沿、以及测量起点。阈值电压建议直接输入 VDD*50% 的计算结果。继续用 1.8V 的例子阈值就是 0.9V。如果电路有不同电压域每个电压域的阈值都要单独设不要图省事把两个信号都设成同一个数值除非它们确实是同一个电源域。边沿选择要看你是测上升沿传输还是下降沿传输。测 t_pLH 时输入信号选 rise输出信号也选 rise测 t_pHL 时两边都选 fall。这听起来简单但在波形上如果存在多次过阈值工具会默认选择第一个满足条件的点容易把前一个周期的残留翻转也算进去。这时候就需要“波形索引位”出场了。Custom Waveform 的测量配置里一般都有 occurrence 或者 edge number 这样的选项意思是“从哪个边沿开始测”。比如你想测稳态的第 10 个上升沿就把输入边沿索引填 10输出边沿索引也填 10。如果不填工具可能默认从波形文件最开始计算而波形最开始往往是从 0 开始上电那个边沿根本不是正常的逻辑翻转。我个人的习惯是先让仿真跑到至少 2 到 3 个周期后再开始取数。比如 5MHz 方波周期是 200ns那我仿真至少跑 2us然后取第 8 到第 10 个周期之间的波形作为测量对象。这样既能避开上电瞬态又能拿到足够多的稳态样本。2.2 延迟误差容易被放大的情形延迟误差最容易被放大的地方是信号边沿不够陡的时候。同样的阈值偏差如果信号翻转斜率是 0.5ns 内从 0V 到 1.8V阈值算错 0.1V 只会带来几十皮秒的误差但如果遇到高阻节点或大电容负载边沿可能长达 5ns阈值算错 0.1V 就能带来几百皮秒甚至 1ns 以上的偏差。换句话说阈值设在 1.2V 还是 0.9V对慢边沿信号的影响会被成倍放大。很多人在 Custom Waveform 里量的数据“偏大”不一定是工具错了而是阈值定得过高导致测量点在边沿上往后移动了一大截。另一个容易踩的坑是信号名带总线位。比如out5、out[5]、v(out:5)这三种写法在不同版本的工具里都可能出现。量错信号名不会报错但会把一条完全不相干的波形拉进来。我吃过一次亏量出来的延迟是 0.1ps检查了半天才发现把v(out:0)输成了v(out:O)字母 O 当成数字 0 了。这个只能靠每次测量前放大波形确认高电平、低电平数值来兜底。还有一点值得注意Custom Waveform 里看到的波形是仿真数据点之间的线性插值结果。如果输出信号在阈值附近有抖动比如在 0.9V 附近反复穿越工具默认会用“第一次穿越”来定义延迟。这种抖动往往来自仿真器内部的时间步长过大不是电路的正常行为。解决办法不是去改测量设置而是回头把 .tran 的 maxstep 减小从源头上把波形跑得更干净。3. 功耗测量没那么简单窗口选择、电流方向和平均值陷阱3.1 平均功耗和峰值功耗定义在 HSPICE 瞬态仿真中想看功耗最直观的办法是把电压源支路电流和电源电压的乘积画出来。这个乘积就是瞬时功耗曲线。平均功耗通常是对某个时间窗口内的瞬时功耗做积分再除以窗口长度峰值功耗则是窗口内瞬时功耗的最大值。用 Custom Waveform 实现时常见的表达式大概是下面这种风格avg( v(vdd) * i(v_vdd) ) max( v(vdd) * i(v_vdd) )如果工具支持带窗口的测量可以写成avg( v(vdd) * i(v_vdd), 2u, 6u ) max( v(vdd) * i(v_vdd), 2u, 6u )注意i(v_vdd)的写法取决于电压源在网表里的名字。比如说网表里是v_vdd vdd 0 1.8那么电流函数的写法就是i(v_vdd)。还有一个很容易忽略的问题HSPICE 里无源元件的电流方向默认是从正端流入从负端流出。如果你是按“从电源流出到电路”的方向来思考的那么i(v_vdd)的数值可能是负的乘上 v(vdd) 以后功耗曲线会变成一个负数。负功耗在数学上不是不能积分但很容易让人混淆。我通常在表达式里加一个绝对值或者反过来把电流支路的方向调成从地到电源也就是在网表里写成v_vdd vdd 0 1.8然后测量时用i(v_vdd)但先通过波形确认它到底是正还是负。确认方向这件事用光标点一下波形上的电流值就可以不花时间但能避免后续所有数据偏差。3.2 为什么“全窗口取数”会高估功耗这是功耗测量里最隐蔽的一个问题。Custom Waveform 默认的测量区间一般是从波形文件的第一个数据点到最后一个数据点。如果仿真从 0 时刻开始电源一上电的瞬间会有很大的充电电流这个电流尖峰持续时间可能只有几纳秒但幅值是正常工作电流的几十倍甚至上百倍。你今天看到 139.2uW而稳态平均功耗其实只有 118.6uW就是因为那个上电尖峰被无差别地平均进了整个窗口。上电瞬间发生的翻转次数是有限的但能量却很大把平均值拉高。仿真跑得越长单个上电尖峰的影响越小但如果你把仿真时间缩短到几个微秒影响就会非常明显。正确做法是先把功率波形画出来用光标确认波形从什么时候开始进入周期性稳态。然后再把这个区间作为测量窗口。比如我这次仿真从 2us 开始波形已经稳定成 5MHz 的周期信号那窗口就取 2us 到 6us。取四个完整周期时间上足够平均又不至于把上电阶段卷进来。同样道理测峰值功耗时也不能直接取整个时间轴上的最大值。上电阶段的浪涌电流往往会创造出一个超级夸张的“峰值功耗”这个值对电路的实际工作状态没有参考意义。如果要评估正常工作时的峰值功耗就一定先限定时间窗口。3.3 峰值的“假尖峰”过滤还有一个峰值的“假尖峰”问题。瞬态仿真里如果时间步长设置不够密电源电流波形可能会出现非常窄的高幅值毛刺。这个毛刺可能是真实存在的比如输出级同时导通造成的瞬时短路电流也可能是数值噪声尤其是迭代收敛不充分时电流值会在某几个点突然跳变。区分真假毛刺的办法很简单把毛刺附近的时间轴拉到足够细看它是不是由多个连续的数据点组成。如果只有一个孤立的样本点高得离谱前后都是正常值那大概率是数值噪声。这时候不要急着记录这个“峰值功耗”先把仿真步长或收敛精度调高重跑后再看。我在项目里会把峰值功耗的统计窗口设置成包含一个完整上升沿加下降沿的时间范围比如 1us 到 1.5us而不是只取单个时刻。这样即使有短暂的短路电流也能反映真实状态不会被孤点干扰。4. 实测对比同一段 tran 仿真Custom Waveform 与 .meas 的结果差异4.1 测试条件为了把问题说得更直观这里列一组我实际跑过的数据。电路结构是前面提到的 5 级反相器链输出负载 1pFVDD1.8V输入 5MHz 方波仿真总时长 10us。HSPICE 脚本里同时写了 .meas 语句做参考Custom Waveform 里则分两种方式取数一种是不改任何默认配置直接量另一种是把阈值、窗口、边沿全部按统一口径设置后重新量。参考 .meas 语句大致如下.meas tran tpLH TRIG v(in) VAL0.9 RISE10 TARG v(out) VAL0.9 RISE10 .meas tran tpHL TRIG v(in) VAL0.9 FALL10 TARG v(out) VAL0.9 FALL10 .meas tran avg_power AVG v(vdd)*i(v_vdd) FROM2u TO6u .meas tran peak_power MAX v(vdd)*i(v_vdd) FROM2u TO6u这里的 RISE10 和 FALL10 就是前面说的波形索引位表示取第 10 个上升沿或下降沿作为测量点。阈值取 0.9V功耗窗口取 2us 到 6us。4.2 结果对照表下面这张表是最终的测量结果测量项参考 .meas 结果Custom Waveform 默认取数Custom Waveform 按统一口径取数t_pLH4.87ns5.63ns4.86nst_pHL4.52ns4.49ns4.51ns平均功耗118.6uW139.2uW118.7uW峰值功耗1.03mW1.72mW1.04mW第一列是 .meas 的正确结果。第二列是 Custom Waveform 默认取数的结果此时阈值不是 0.9V窗口也没有限制导致 t_pLH 偏大 0.76ns平均功耗偏大 17.4%峰值功耗甚至偏大了 67%。第三列是把阈值改为 0.9V、边沿索引调整为第 10 个边沿、功耗窗口限制为 2us 到 6us 之后的结果这时所有数字和 .meas 基本吻合差异都在 1% 以内。t_pHL 在默认取数时看起来反而接近正确值这是巧合。因为下降沿的阈值偏差方向可能和边沿速率相互抵消但并不能因此就说默认设置“没问题”。经验不足的人看到 t_pHL 对得上就容易忽略其他三项的严重偏差最后稀里糊涂地把错误数据提交出去。4.3 修正后一致性为负的原因其实修正后的测量结果并不是刚好完全相等的还有 0.01ns 到 0.02ns 左右的小差异。这来自两种测量机制的点位插值方式不同。HSPICE 的 .meas 语句在计算阈值穿越时间时会利用仿真器内部的时间步长信息做更精细的反插值而 Custom Waveform 读取的是已经被写到 .tr0 文件里的离散样本点它看到的分辨率取决于文件里保存了多少个点。如果 .tr0 文件的采样间隔比较粗Custom Waveform 的插值误差就会明显一些。想缩小这个差异有两条路一是把仿真输出的时间间隔设得更密也就是减小 .tran 的 maxstep二是在 Custom Waveform 的显示设置里开启“线性回插”或“高精度插值”选项让它尽量还原原始波形。但要注意后一条路只是让数据显示更平滑并不能凭空恢复 .tr0 文件里没有存储的高频信息。真正靠谱的办法还是从仿真源头控制输出密度后面第 5 部分会专门展开讲。5. 时间步长与收敛性测量精度藏在仿真设置里5.1 maxstep 设置对延迟的影响很多人做瞬态仿真时习惯只写总时长不写 maxstep觉得 HSPICE 会自动控制步长。对简单电路来说HSPICE 确实会自动加密时间步但对大负载、慢边沿、强反馈这类结构自动步长控制可能不够密导致波形里出现肉眼可见的“折线段”。延迟测量本质上是在找输入、输出两个信号穿越阈值的时间差。如果时间步长很粗比如 1ns 一个点而信号刚好在两点之间穿越了 0.9V那么工具只能根据前后两个样本点做线性插值来估算穿越时刻。这个估算和真实穿越时刻之间的误差最大可以达到半个步长。换句话说maxstep1ns 时延迟误差可能到 ±500ps。这个量级对高速接口来说已经非常致命。我这次测试分别用了三组参数.tran 设置t_pLH 测量值与基准差异tran 0 10u5.76ns0.89nstran 100p 10u4.99ns0.12nstran 10p 10u4.87ns基准可以看到不写 maxstep 的默认结果和 10ps 步长的基准结果差了快 1ns。这完全是采样点太稀造成的假象不是电路延迟真的变大了。所以我做延迟敏感仿真时会让 maxstep 至少小于预期延迟的 1/20。预期延迟 5ns那 maxstep 取 200ps 以内比较安全。5.2 收敛抖动与“假延迟”时间步长只是问题的一半另一半是收敛性。瞬态仿真每个时间点都是一个非线性方程组的迭代过程如果迭代没有真正收敛得到的电压值可能偏离真实解反映在波形上就是阈值附近出现抖动。收敛抖动对延迟测量的影响尤其恶劣。信号在 0.9V 附近抖动时波形会短时间内多次穿越阈值工具如果默认取第一次穿越测到的延迟可能比真实值提早很多。比如输出从低到高如果它在 0.7V 先冲了一下到 0.95V再掉回 0.85V然后再稳定冲到 1.8V工具会在第一次上冲到 0.95V 时就认为“已经到阈值”而这个时刻实际只是噪声过冲不是真正的逻辑翻转。遇到这种情况我建议回到仿真参数里检查两件事一个是.option里有没有开启accurate1另一个是收敛相关的容差设置是否过宽。对模拟电路仿真runlvl和abstol的默认值在很多版本里对功耗和延迟测量偏宽松。可以试着加上.option accurate1 .option runlvl6这样会带来一些仿真时间开销但只要不是超大电路都值得打开。等电路真正到了超大规模后仿阶段再根据实际情况调高收敛容差也不迟。5.3 其他精度选项除了 maxstep 和收敛设置还有两个输出选项会影响 Custom Waveform 的读数。一个是.option post它控制是否把波形数据写入 .tr0 文件默认一般打开另一个是.option probe它可以让你只保存指定信号大幅缩小文件体积。如果文件太大Custom Waveform 加载和缩放都会卡顿反而容易让人不想精细测量。我通常在仿真脚本里加一段.option post .option probe .save v(in) v(out) v(vdd) i(v_vdd)这样 .tr0 文件里只保留我关心的信号加载快很多。测量完成后如果发现需要别的中间节点再临时改脚本重跑。对于动辄几 GB 波形的后仿场景这种按需保存的方式能节省不少时间。6. 我沉淀的一套 Custom Waveform 测量固定流程6.1 测量模板经过这些坑之后我固定形成了一套流程每次拿到新的瞬态波形都按这个顺序来操作基本不会再出现“数据对不上”的情况。第一步检查波形是否满足稳态条件。先把 v(in) 和 v(out) 都画出来缩放到完整时间范围确认上电暂态在哪个时刻结束。如果波形还在缓慢爬升先延长时间重跑不要急着测量。第二步设置阈值。延迟测量用 0.9V不是 1.2V更不是默认的“最大值的 50%”那种模糊概念。这里直接计算 VDD 的 50%填数值不用百分比。因为不同电压域混用时百分比容易指定到错误的电源域。第三步设置边沿索引。测第 10 个上升沿就两边都填 10。如果输入波形前面有几个毛刺可能需要把边沿索引进一步提高选一个信号干净的区域。第四步设置功耗窗口。用v(vdd)*i(v_vdd)画出瞬时功耗曲线光标找到进入稳态周期的起点把窗口取到包含至少 4 个完整周期。同时看一眼电流方向必要时在表达式里加绝对值。第五步对照 .meas 数据。不要只测一组就出报告先和 HSPICE 脚本里的 .meas 对比一次差异在 1% 以内才认为可信。超过 1% 就回头检查阈值和窗口而不是调大插值精度。这套流程看起来繁琐实际上熟练之后不到两分钟就能完成一组测量。比起“先量一个数出了报告再被质疑最后花半天重新核实”这点时间投入非常划算。6.2 一点使用习惯最后再说一个额外的使用习惯把测量配置保存下来。Custom Waveform 一般支持保存波形工程文件可以把已经设置好的信号、表达式、测量结果都存在同一个文件里。我每次做完一组测量都会另存为一份带日期和工艺角名称的工程文件比如delay_power_tt_20250114.cwm。下次要复测同一个模块直接加载工程文件数据、曲线、表达式全部还原不用重新配置。另外输出报告时我会同时附上一行“测量条件”说明包括阈值电压、边沿索引、功耗窗口、仿真步长。这行说明看起来是小事但在跨部门协作或者写设计文档时能帮其他人避免重复我踩过的坑。很多时候延迟和功耗数据看起来“对不上”不是因为电路变了而是因为测量口径变了。把这些信息写清楚比在邮件里来回解释半天要高效得多。回到开头那个案例那次数据偏差的最后结论其实很简单Custom Waveform 的默认阈值和脚本里的 .meas 不一致。找到原因后所有数字迅速回归一致电路本身没有任何问题。这件事给我的影响却留了很久之后每做一次测量我都会先问自己一句我量到的到底是不是我真正想定义的那个量