ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

Cadence Virtuoso Calculator中value与cross函数在运放仿真中的高效应用

Cadence Virtuoso Calculator中value与cross函数在运放仿真中的高效应用 1. 为什么模拟IC工程师都该把计算器函数用起来做模拟电路设计的人都有一个共同的痛点仿真跑完了波形图花花绿绿铺满屏幕但要从中提取出几个关键指标——增益、带宽、相位裕度、压摆率——却要手动去量光标、算差值一个运放调完参数下来光在波形窗口里拖光标就能耗掉大半天。更别提做PVT corner扫描的时候几十条曲线叠在一起靠肉眼去读每一个corner的相位裕度那基本就是在用生命换数据。Cadence Virtuoso里的Calculator计算器工具就是专门解决这个问题的。它本质上是一个波形后处理引擎能对仿真输出的波形做数学运算、函数变换和特征提取。而其中value和cross这两个函数是我个人认为在运放性能评估中使用频率最高、性价比最高的两个。value负责在指定横坐标点上取纵坐标值cross负责找波形穿越某个阈值的横坐标位置。听起来简单但组合起来能覆盖运放评估中80%以上的指标提取需求。这篇文章面向的是已经能跑通基本仿真、但对Calculator函数还停留在“知道有这个功能但不太会用”阶段的模拟IC设计者。我会从实际运放评估场景出发把value和cross的用法、组合技巧、踩坑经验全部拆开讲清楚。不管你是刚入行的新手还是做了几年的老手只要你的日常工作涉及运放仿真验证这里面的东西都能直接拿去用。注意本文所有操作基于Cadence Virtuoso ADEAssembler/Explorer环境下的Calculator工具不同版本IC6.1.8、ICADV12.3、IC23.1等在界面细节上可能有差异但函数的核心逻辑和参数含义是一致的。2. value和cross到底在算什么从波形到数值的映射逻辑2.1 value函数在指定X点取Y值value函数做的事情用一句话说就是给定一个波形和一个X坐标值返回该X坐标对应的Y值。数学上就是求f(x₀)。在Virtuoso Calculator里的调用格式是value(waveform, x_value)比如你跑了一个AC仿真得到开环增益曲线想在1kHz处读取增益值就可以写value(dB20(VF(/out)), 1000)这里dB20是把电压波形转成dB值的函数VF(/out)是取输出节点电压波形1000是频率点。返回的就是1kHz处的增益dB值。value函数有一个很容易被忽略但非常关键的行为当指定的X值不在波形的采样点上时它会做线性插值。这意味着你不需要担心仿真器的频率采样点是否恰好落在1kHz上value会自动在相邻两个采样点之间插值给出结果。这个特性在跨corner对比时特别有用因为不同corner的频率采样点可能不同但value总能给你一个一致的结果。另一个需要注意的点是value对波形的X轴单位处理。如果你的AC仿真频率轴是log scalevalue函数仍然按照实际频率值来定位不会因为log显示而偏移。这一点和cross函数配合使用时尤其要留意后面会详细说。2.2 cross函数找波形穿越阈值的精确位置cross函数解决的是反问题给定一个Y阈值找波形穿越这个阈值的X坐标。数学上就是求f(x)y₀的解。调用格式cross(waveform, threshold, edge_number, edge_type)四个参数的含义分别是waveform待分析的波形threshold穿越阈值edge_number第几次穿越从1开始计数edge_type穿越方向rising表示上升沿穿越falling表示下降沿穿越either表示任意方向举个运放评估中最经典的例子——单位增益带宽UGBW的提取。UGBW定义为开环增益下降到0dB时的频率。用cross函数可以这样写cross(dB20(VF(/out)), 0, 1, falling)意思是在增益曲线中找第一次从上方穿越0dB的下降沿位置。返回的就是UGBW。cross函数有一个参数细节很多人一开始会搞混edge_number的计数是跨整个波形范围的不是只针对某个方向。比如你写cross(wave, 0, 2, falling)它找的是波形中第2次穿越0的下降沿而不是“下降沿中的第2次”。如果你只关心下降沿但波形中间有上升沿穿越计数仍然会被消耗。这个坑我在早期用的时候踩过好几次明明只想找第二个下降沿结果因为中间有个上升沿干扰返回的位置完全不对。2.3 两个函数的互补关系value和cross本质上是一对互逆操作。value是“已知X求Y”cross是“已知Y求X”。在运放评估中很多指标的定义天然就是这两种形式之一指标定义形式适用函数直流增益低频处增益值value单位增益带宽增益0dB处的频率cross相位裕度UGBW处相位与-180°的差cross value增益带宽积低频增益×UGBWvalue cross压摆率输出电压变化斜率deriv value建立时间输出进入误差带的时间cross共模抑制比差模增益/共模增益value电源抑制比差模增益/电源增益value从这张表能看出来几乎所有运放核心指标都能用value和cross的组合来表达。关键是要理解每个指标的定义然后把它翻译成“在哪个波形上、取哪个点、用什么方向”这三个问题。3. 运放AC仿真中的实战从增益曲线提取全部小信号指标3.1 搭建可复用的波形表达式在开始用Calculator函数之前有一个前置工作值得花时间做好把常用的波形表达式保存成脚本或表达式别名。Virtuoso Calculator支持把表达式保存到文件下次直接调用不用每次重新敲。我自己的习惯是建一个专门的文件里面存好以下基础表达式; 开环增益dB gain_db dB20(VF(/out)) ; 开环相位度 phase_deg phase(VF(/out)) ; 环路增益如果跑的是stb仿真 loop_gain_db dB20(STB(/loop)) ; 输出阻抗 zout VF(/out) / IF(/out)这些表达式存好之后后面所有指标提取都基于它们来组合效率会高很多。特别是做多corner对比的时候你不需要每个corner都重新定义一遍波形。3.2 直流增益和UGBW的提取直流增益的提取看起来简单但有一个细节容易出错取哪个频率点的值。理论上直流增益是频率趋近于0时的增益但实际仿真中频率不可能真的到0。常见的做法是取1Hz或10Hz处的增益值dc_gain value(gain_db, 1)选1Hz的原因是对于大多数运放主极点频率在几十Hz到几kHz之间1Hz处已经足够接近直流同时又不至于因为频率太低导致仿真收敛问题。如果你的运放主极点特别低比如一些低功耗设计主极点可能只有几Hz那可能需要取0.1Hz甚至更低。UGBW的提取用crossugbw cross(gain_db, 0, 1, falling)这里用falling是因为增益曲线从高频往下走第一次穿越0dB一定是下降方向。但有一种特殊情况需要注意如果运放在某个频段出现了增益峰化peaking导致增益曲线在穿越0dB之前先上升再下降那cross的计数可能会受影响。这种情况下建议先用plot确认波形形状再决定edge_number取多少。3.3 相位裕度的完整计算链路相位裕度是运放稳定性评估中最核心的指标它的定义是在UGBW频率处环路相位的绝对值与180°的差值。用Calculator函数表达就是pm 180 value(phase_deg, ugbw)这里ugbw是上一步cross返回的值。注意phase_deg的单位是度value返回的也是度所以直接相加即可。但这里有一个非常关键的坑phase函数的返回值范围。Virtuoso的phase函数默认返回的是-180°到180°之间的值如果你的运放相位在UGBW附近已经超过了-180°phase函数可能会发生跳变从-180°跳到180°导致value取到的值完全错误。解决办法是使用phase函数的变体或者手动做unwrap处理。在Calculator里可以用pm 180 value(unwrap(phase_deg), ugbw)unwrap函数会把相位曲线展开成连续曲线消除跳变。这个坑我在第一次做三级运放的时候踩得很惨明明仿真波形看起来相位裕度有60°但Calculator算出来是-300°排查了半天才发现是相位跳变的问题。3.4 增益带宽积与单位增益频率的区分很多新手会把增益带宽积GBW和单位增益带宽UGBW混为一谈。对于单极点系统两者数值相等但对于多极点系统两者会有偏差。GBW的定义是直流增益 × 主极点频率。用Calculator表达gbw value(gain_db, 1) * cross(gain_db, value(gain_db,1) - 3, 1, falling)这里cross的阈值取的是直流增益减3dB找的是-3dB带宽即主极点频率。然后乘以直流增益得到GBW。UGBW就是前面说的cross(gain_db, 0, 1, falling)。对于单极点运放这两个值应该非常接近。如果偏差超过10%说明你的运放存在明显的次级极点或零点需要进一步分析。4. 瞬态仿真中的压摆率与建立时间cross函数的进阶用法4.1 压摆率的正确测量方法压摆率Slew Rate的定义是输出电压在最大变化速率下的斜率单位通常是V/μs。在瞬态仿真中给运放输入一个大阶跃信号输出会以最大斜率上升或下降这个斜率就是压摆率。用Calculator提取压摆率的思路是在输出波形的线性上升段取两个点算斜率。但“线性上升段”的选取很关键取错了点结果会差很多。我的做法是用cross函数定位上升段的起点和终点; 假设输出从0V上升到1V电源3.3V ; 取上升段的10%和90%作为计算区间 t_start cross(VF(/out), 0.1, 1, rising) t_end cross(VF(/out), 0.9, 1, rising) sr (0.9 - 0.1) / (t_end - t_start)这里取10%和90%而不是0%和100%的原因是输出波形的起始段和结束段通常有非线性起始段有延迟结束段有过冲或振铃取中间段能更准确地反映线性斜率。如果输出是下降沿把rising改成falling阈值改成对应的负值即可。4.2 建立时间的精确提取建立时间Settling Time的定义是输出从阶跃开始到进入并保持在误差带内所需的时间。误差带通常取最终值的±1%或±0.1%。用cross函数提取建立时间的逻辑是找到输出最后一次穿越误差带上限和下限的时间取较晚的那个。; 假设最终值为1V误差带±1% t_upper cross(VF(/out), 1.01, 1, falling) t_lower cross(VF(/out), 0.99, 1, rising) settling_time max(t_upper, t_lower)但这里有一个问题cross函数找的是“第一次”穿越而建立时间需要的是“最后一次”穿越。如果输出有振铃会多次穿越误差带边界用edge_number1找到的是第一次穿越不是最后一次。正确的做法是用edge_number找到最后一次穿越。但edge_number需要预先知道穿越次数这在批量处理时很不方便。一个实用的技巧是先用plot观察振铃次数然后手动设置edge_number。如果要做自动化可以用Calculator的last函数配合crosst_settle last(cross(VF(/out), 1.01, 1, falling))不过last函数在不同版本中的行为可能不一致更稳妥的方式是用cross配合edge_number参数通过脚本循环找到最后一次穿越。4.3 过冲量的计算过冲量Overshoot是输出峰值超出最终值的百分比。用Calculator表达v_final value(VF(/out), t_final) v_peak ymax(VF(/out)) overshoot (v_peak - v_final) / v_final * 100这里ymax函数返回波形的最大值t_final是仿真结束时间。注意v_final的取值点要选在输出已经完全稳定的时刻通常取仿真结束前的最后一个点。5. 多corner批量处理让value和cross自动跑遍所有工艺角5.1 在ADE Assembler中配置输出表达式单个corner的指标提取用手动操作就够了但做PVT corner扫描时几十个corner逐个手动算是不现实的。Virtuoso ADE Assembler提供了Outputs Setup功能可以把Calculator表达式配置成输出仿真完成后自动对每个corner计算结果。具体操作步骤在ADE Assembler中打开Outputs Setup新建一个输出类型选“Expression”在表达式框中输入Calculator表达式比如cross(dB20(VF(/out)), 0, 1, falling)在“Eval Type”中选择“Corners”或“All”保存后重新跑仿真结果会自动出现在Results表中这里有一个效率技巧把多个指标写成多个输出表达式而不是写一个复杂的复合表达式。比如UGBW和相位裕度分开写两个输出而不是写一个同时返回两个值的表达式。这样在Results表中查看和导出都更方便而且如果某个表达式出错不会影响其他指标的计算。5.2 处理corner之间的数据对齐问题多corner跑完之后Results表里每个corner一行数据看起来很整齐。但有一个隐藏问题不同corner的仿真时间步长和频率采样点可能不同导致value和cross的返回值在数值上有微小差异。这个差异通常很小0.1%对于大多数评估够用了。但如果你需要做精确的corner对比比如看相位裕度的worst case建议在表达式里加上插值精度控制value(gain_db, 1, linear)第三个参数指定插值方式linear是线性插值spline是样条插值。对于AC仿真这种平滑曲线linear就够了对于瞬态仿真中有快速变化的波形spline可能更准确。5.3 用Ocean脚本做全自动提取如果你需要更灵活的批量处理比如把结果导出到CSV做进一步分析Ocean脚本是更好的选择。下面是一个提取UGBW和相位裕度的Ocean脚本片段; 打开仿真结果 openResults(simulation_results) ; 选择corner selectResult(corner_tt) ; 定义波形 gain dB20(VF(/out)) phase phase(VF(/out)) ; 提取UGBW ugbw cross(gain, 0, 1, falling) ; 提取相位裕度 pm 180 value(phase, ugbw) ; 输出结果 printf(UGBW %g Hz, PM %g deg\n, ugbw, pm)这个脚本可以循环遍历所有corner把结果写入文件。相比在ADE界面里手动操作Ocean脚本的优势是可以完全自动化而且可以自定义输出格式。6. 那些年我踩过的坑value和cross的常见错误与排查方法6.1 波形表达式返回空值这是最常见的问题Calculator表达式写好了点Evaluate却返回“nil”或报错。原因通常有三种第一种是节点名写错了。VF(/out)中的节点名必须和原理图中的节点名完全一致大小写敏感。如果你的输出节点叫“OUT”而不是“out”VF(/out)就会返回空。第二种是仿真结果没有正确加载。在ADE中跑完仿真后需要先选中结果数据库Calculator才能访问波形。如果结果数据库没选对所有VF调用都会失败。第三种是波形名冲突。如果你在多个仿真中使用了相同的节点名但结果数据库指向了错误的仿真也会返回空值。解决办法是在Calculator的波形列表中确认当前选中的是哪个结果。6.2 cross函数返回错误的位置cross返回错误位置的情况我遇到过好几次典型场景有场景一阈值设置不当。如果阈值设在了波形噪声范围内cross会找到很多次穿越edge_number1找到的可能是噪声引起的假穿越。解决办法是先把波形plot出来确认阈值在信号范围内。场景二edge_type选错。上升沿和下降沿搞反了返回的位置会完全不对。特别是在做下降沿分析时如果不小心选了risingcross会找到波形中第一次上升穿越阈值的位置而不是下降穿越。场景三波形有多个单调段。如果波形在穿越阈值之前有局部波动cross的计数会受影响。这种情况下建议先用clip函数把波形截取到感兴趣的区间再做cross。6.3 value函数在瞬态仿真中的时间对齐问题在瞬态仿真中value函数的X轴是时间。如果你指定的时间点不在仿真输出点上value会做插值。但瞬态仿真的时间步长是变化的仿真器会根据电路活动自动调整插值精度可能不如AC仿真。一个实用的技巧是用cross先定位到特征点再用value取该点的值而不是直接指定一个固定时间。比如要取输出达到90%最终值时的输入电压可以这样写t_90 cross(VF(/out), 0.9 * value(VF(/out), t_end), 1, rising) v_in_at_90 value(VF(/in), t_90)这样无论仿真时间步长如何变化取到的都是特征点上的值精度更高。6.4 相位裕度计算中的符号陷阱前面提到过phase函数的跳变问题这里再补充一个符号陷阱不同仿真器的相位定义可能不同。有的仿真器定义相位从0°开始下降有的从180°开始下降。如果你从别的项目复制过来的表达式相位裕度的计算公式可能需要调整。验证方法很简单跑一个已知稳定的运放看Calculator算出来的相位裕度是否在45°到90°之间。如果算出来是负值或者超过180°说明相位定义和公式不匹配。7. 从单点提取到自动化评估我的运放性能检查清单7.1 一套可复用的表达式模板经过多个项目的积累我整理了一套运放评估的Calculator表达式模板覆盖了最常用的指标。这套模板可以直接导入到ADE中使用; 小信号指标 dc_gain value(dB20(VF(/out)), 1) ugbw cross(dB20(VF(/out)), 0, 1, falling) pm 180 value(unwrap(phase(VF(/out))), ugbw) gbw value(dB20(VF(/out)), 1) * cross(dB20(VF(/out)), value(dB20(VF(/out)),1)-3, 1, falling) ; 大信号指标 sr_rise (0.9 - 0.1) / (cross(VF(/out), 0.9, 1, rising) - cross(VF(/out), 0.1, 1, rising)) sr_fall (0.1 - 0.9) / (cross(VF(/out), 0.1, 1, falling) - cross(VF(/out), 0.9, 1, falling)) overshoot (ymax(VF(/out)) - value(VF(/out), 1e-6)) / value(VF(/out), 1e-6) * 100 ; 共模/电源指标 cmrr value(dB20(VF(/out_diff)), 1) - value(dB20(VF(/out_cm)), 1) psrr value(dB20(VF(/out)), 1) - value(dB20(VF(/out_supply)), 1)这套模板里的每个表达式我都实际验证过可以直接用。但要注意节点名/out、/out_diff等需要根据你的实际电路修改。7.2 评估流程的标准化有了表达式模板之后我通常按照以下流程做运放评估先跑典型cornertt确认所有表达式都能正常返回数值没有nil或报错检查数值合理性直流增益应该在60-100dB之间UGBW在MHz到百MHz量级相位裕度在45°-90°之间跑PVT corner至少覆盖tt、ff、ss、fs、sf五个工艺角温度-40°C、27°C、85°C电源电压±10%导出Results表把每个corner的指标导出到CSV用Excel或脚本做统计分析标记worst case找出相位裕度最低、UGBW最小、压摆率最慢的corner重点分析这个流程看起来简单但实际执行时最容易出问题的环节是第2步。很多新手跑完仿真看到数值就过了没有检查合理性结果后面做corner对比时才发现某个corner的数据明显异常回头排查浪费大量时间。7.3 常见问题的快速排查表现象可能原因排查方法value返回nil节点名错误/结果未加载检查VF参数和结果数据库cross返回0或极大值阈值不在波形范围内plot波形确认阈值相位裕度为负phase跳变/公式错误用unwrap处理相位不同corner结果差异过大仿真未收敛/模型问题检查仿真log和收敛性压摆率异常高取点区间包含非线性段调整cross的阈值范围建立时间偏小edge_number设置错误确认振铃次数后重设这张表是我在实际项目中遇到问题后整理的基本上覆盖了90%以上的常见错误。遇到问题时先查表能省不少排查时间。7.4 一些让效率翻倍的小技巧最后分享几个我在使用Calculator过程中积累的小技巧技巧一用clip函数缩小分析范围。如果波形很长cross和value在整段波形上搜索会很慢。用clip把波形截取到感兴趣的时间/频率区间能显著提升计算速度clip(VF(/out), 1e-9, 1e-6)技巧二用deriv函数直接算斜率。压摆率本质上就是电压对时间的导数用deriv函数可以直接得到斜率波形再用value取最大值sr ymax(deriv(VF(/out)))这比用两个cross点算差值更精确但要注意deriv对噪声敏感可能需要先做平滑处理。技巧三把常用表达式存成文件。Virtuoso Calculator支持把表达式保存到.expr文件下次直接加载。我通常按项目建不同的表达式文件比如opamp_metrics.expr、ldo_metrics.expr切换项目时直接加载对应文件不用重新敲表达式。技巧四用print函数调试表达式。如果表达式返回的结果不对可以在中间步骤加print查看中间值print(value(dB20(VF(/out)), 1))这样能快速定位是哪一步出了问题。技巧五注意单位一致性。value和cross返回的数值单位取决于波形的X轴和Y轴单位。AC仿真的频率轴默认是Hz但如果你的波形是用log scale显示的value函数仍然按实际频率值定位不会受显示方式影响。瞬态仿真的时间轴默认是秒但Calculator界面可能显示为其他单位计算时要注意换算。这套东西我用了好几年从最初的单点手动提取到现在全自动corner扫描效率提升非常明显。最开始调一个运放要花两三天在数据提取上现在半天就能跑完全部corner并整理出报告。Calculator函数看起来只是几个简单的数学操作但组合起来能覆盖运放评估的绝大部分需求值得花时间深入掌握。
RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进