
做模拟IC验证的谁没在波形窗口里趴着数过格子我记得好几次带新人做运放指标检查他们用Cadence Virtuoso跑完AC仿真第一件事就是把幅频曲线和相频曲线拉出来用鼠标标尺一点一点挪着找位置。低频增益在哪一段、0dB穿越频率是多少、这个频点相位落到多少度全靠肉眼对格子估。单极点结构还好办碰到两级运放带零点补偿或者要跨工艺角和负载条件反复验证那真是验证一次就要耗掉大半个下午手动标完还有读数误差。后来我让他们老老实实把计算器里的value和cross这两个函数用起来表达式一贴仿真跑完在Outputs窗口直接看三个数字——增益、穿越频率、相位裕度又快又准。这篇就把这套玩法完整拆开适合正在用Virtuoso做运放仿真验证的工程师也适合刚接触模拟IC设计、想搞明白这两个计算器函数到底怎么用的同学。1. 从手动标记到表达式计算为什么先把value和cross练熟1.1 手动读数最典型的两个问题每当我看到有人在幅频曲线上反复拖marker我就知道这套流程大概率有问题。手动读数的第一个问题是效率低。运放验证几乎都要看增益和相位裕度一个运放、一个corner、一组负载条件就要重复一遍“低频增益→0dB穿越→相位读取→心算裕度”的完整动作。如果还要遍历TT、SS、FF三个工艺角再加高低温重复次数直接翻好几倍时间全耗在机械操作上。第二个问题是读数精度不稳定。波形窗口里用标尺读数纵轴是dB或度横轴是频率鼠标拖到某个位置的判断本身就有主观性。0dB穿越频率稍微偏一点相位裕度可能就差好几度而模拟电路对相位裕度的要求通常卡得很死很多项目的spec是“不小于60度”差1度你都得回去查原因。手动读出来的数连你自己都不敢说它准。后来我把目光完全转向Cadence Virtuoso自带的Calculator计算器窗口。这个工具平时经常被当成一个“看图辅助”在用但实际上它完全可以承担自动化的指标提取——前提是选对函数。1.2 value和cross正好是一对互补的“取点器”计算器里函数很多最容易被低估的就是value和cross。为什么说它们互补因为它们的输入输出关系正好相反value(waveform, x_value)给定横坐标值返回该坐标对应的纵坐标值。cross(waveform, y_value, ...)给定纵坐标值返回波形第一次或第N次穿越该纵坐标值时对应的横坐标。一个是“已知横坐标求纵坐标”另一个是“已知纵坐标求横坐标”。运放增益和相位裕度的评估过程本质上就是这两类动作的反复组合先要知道低频段某个频率点上的增益这是value的活再要找到增益曲线穿越0dB时的频率这是cross的活最后还要知道这个频率点上的相位是多少又回到value的活把相位和-180度之间的距离一加相位裕度就出来了。所以只要把这两个函数吃透运放最核心的开环增益、单位增益带宽、相位裕度这三个指标就都能用表达式自动算出来。这个过程甚至不需要打开波形窗口仿真结果一出来数字就在那儿了。1.3 这套方法适用的场景边界我不是说所有指标都用value和cross硬凑。比如你要看瞬态波形上升沿的过冲用peak或者其他极值类函数更合适你要算一个波形的平均值或者有效值有专门的integ/avg函数。value和cross最适合的场景是曲线形状你已经熟悉了你知道“应该在哪里读哪个点”你只是不想每次手动去点。AC仿真里读频响参数、stb仿真里读环路增益指标、瞬态仿真里读上升沿和下降沿时间点都属于这类。这个边界要想清楚。它决定了你用函数时的思路不是“随便找一个数”而是“我知道这个点有意义我要精确定位它”。2. 仿真前置步骤保证AC结果能反映真实的环路特性2.1 AC激励幅度与输出引用让增益读数的底数等于1用value和cross算运放增益第一步必须保证AC仿真激励幅度设置正确。在Spectre里做AC分析通常会在输入电压源上设置AC magnitude如果这个值是1那么输出节点的频域电压幅度数值上就直接等于从输入到输出的增益。因为增益的定义是Vout/Vin而Vin1所以Vout的dB值就是增益的dB值。实际搭testbench时我更建议不要留隐患在理想电压源属性里明确写上AC1。很多初学者会在DC电压源上直接加AC幅度这个时候DC source的AC magnitude默认本身就是1但也有人习惯用另外的AC source或者用VCVS之类的东西。反正原则就一条——让输入信号在频域里的复振幅为1你后面读到的VOUT频响曲线就能直接当增益曲线用。输出节点的引用在计算器里一般写成VF(/VOUT)。这个写法表示“频域电压”括号里面是网表里的节点名。如果你有多个输出节点比如VOUTP和VOUTN后面会讲到用差值来处理。节点名的大小写、斜杠、方括号比如总线节点都要和netlist里完全一致否则表达式直接报错。2.2 开环AC仿真的偏置稳定性大电感和电容的断环技巧做运放开环AC仿真时有个绕不开的问题运放本身是设计在闭环反馈下工作的你把环路断开直流工作点往往飘得妈都不认。直接开环跑出来的曲线根本反映不了它在实际反馈电路里的行为。工程里常用的处理办法是“直流闭环、交流开环”。具体操作是在反馈环路里串一个大电感再在相关节点对地挂一个大电容。大电感对直流来说阻抗近似为零所以直流路径保持导通运放静态工作点可以由反馈环路稳定住对交流来说阻抗趋近无穷大相当于把信号环路断开。大电容的作用相反对直流是开路对交流是短路用来提供交流信号的回流路径。数值上不要抠得太精细取到能保证在目标频段内呈现明显断/短路特性即可。我自己习惯在Spectre里给理想电感取1e6亨H这个量级给理想电容取1e6法F只要数量级足够极端AC结果就稳定了。这种方法虽然看起来有点“野路子”但在很多教科书里都被默认采用测出来的开环增益和stb分析结果能对得上。如果你用的Cadence版本支持stb分析用iprobe或者自带的stb分析设置会省掉这些麻烦下一章我会专门说stb场景下怎么套用value和cross。2.3 计算器窗口的两种输入方式进入计算器的方法很简单在ADE窗口菜单栏找Tools → Calculator或者在打开波形后从波形窗口里调出来。不同版本入口位置略有区别但核心的Calculator面板都长得差不多函数按钮排列一目了然。使用上分两条路。一条是完全图形化点击函数按钮比如直接点value再在结果目录里点一下VOUT的波形函数会自动填入参数你只需要改横坐标值点cross也是类似操作。另一条是直接敲表达式在计算器中间的输入框里手写完整表达式。如果你已经知道要算什么手敲反而快表达式还能保存复用。我现在的习惯是先在图形模式里试一次表达式确认语法没问题再复制到Outputs里长期使用。2.4 仿真结果是否可信扫描范围与点数检查在套用value和cross之前先检查AC仿真的扫描范围和分辨率。比如低频端如果从1Hz开始那你在value里读10Hz是完全没问题的如果遇到某些超大时间常数电路1Hz处可能还没完全进入增益平坦区那就该把起始频率降得更低。同理扫描上限必须盖过你预期的0dB穿越频率否则cross永远找不到0dB点。扫描点数也需要给够。默认的10点/decade在log坐标下画出来曲线是平滑的但cross对穿越点的定位精度依赖离散采样点点太少的时候0dB穿越频率的估算会有可感知的偏差。我一般设置成50点/decade以上既不影响仿真速度又能让cross返回的数字稳定到三位有效数字。3. 用value锁定增益低频点选择与dB换算细节3.1 value函数的工作逻辑value函数的语义不复杂给定一个波形对象和一个横坐标值返回波形在这个横坐标上的纵坐标值。它的内部处理会按照波形数据的采样点做插值所以你传给它的横坐标不一定要正好落在采样点上它也能给出合理的结果。用一句话概括用法value(波形, 横坐标)。横坐标可以直接写数字比如10、1k、1M也可以填一个表达式结果比如另一个cross函数返回的频率值。这种“参数本身也可以是函数调用”的嵌套能力是后面计算相位裕度的关键。3.2 低频增益表达式逐段拆解我最常用的低频增益表达式是这个value(dB20(VF(/VOUT)), 10)给它做个拆解你就知道每个部分在干什么了。VF(/VOUT)AC仿真后输出节点的频域电压波形是一个包含幅度和相位信息的复函数。dB20()对这个复信号取20×log10(幅度)把线性幅度转换为dB。这是画幅频曲线时最常用的对数变换。value(... , 10)在横坐标等于10Hz的位置读取对应的纵坐标也就是10Hz处的增益单位dB。所以这条表达式输出的值就是运放在10Hz处的开环增益单位是dB。比如读出60dB意味着低频增益约1000倍V/V读出80dB就是10000倍。3.3 低频点为什么选10Hz而不是1Hz或100Hz很多初学者会问为什么偏偏是10Hz不是1Hz这不是拍脑袋定的背后是“避开主极点、又足够接近直流”的折中考虑。直流增益严格来说要读频率为0Hz处的值但AC扫描往往不从0Hz开始而且如果起始频率设得极低仿真器要额外花时间把极低频点算准没必要。取1Hz听起来更接近直流但如果运放的主极点本身就落在1Hz到10Hz之间1Hz处可能已经偏离真正的平坦增益区了某些高增益运放主极点在几百赫兹甚至更低的情况都有。取10Hz是很多模拟验证团队公认的“安全点”——只要你的运放不设计成超低频主极点10Hz基本都在主极点之前的增益平坦段。如果遇到特殊架构比如极低频主极点的斩波运放或者超低带宽运放那10Hz未必保险。稳妥的做法是把低频增益读数设为两个点交叉验证value(dB20(VF(/VOUT)), 1)和value(dB20(VF(/VOUT)), 10)分别看两者如果基本一致说明已经进入平坦区如果还差不少就得把读取点继续往低频挪。这也是验证基本功之一。3.4 输入AC幅度不为1时的换算如果你的AC激励源幅度不是1而是0.5或者0.1之类的值输出节点的dB20值就不等于增益的dB数。这时候千万不要直接把value的结果当作增益去看否则会得到一个偏小的数。两种修法。第一种把输入源AC幅度改成1这是最省事的。第二种把输出和输入的比值写进表达式value(dB20(VF(/VOUT) / VF(/VIN)), 10)这样不管输入AC幅度是多少表达式都会自动计算Vout/Vin的比值。不过我始终推荐第一种因为比值表达式在相位裕度那些嵌套场景里会让公式变长容易出错而且Cadence里如果输入源本身是理想电压源AC1的默认状态完全够用。3.5 闭环缓冲器测量中的误区额外提醒一句value读增益的方式是否正确取决于你测的是开环响应还是闭环响应。如果你搭的是一个单位增益缓冲器运放输出直接接反相输入那VOUT的频响曲线低频段就是0dB左右你用value读出来的低频增益等于1根本不是运放的开环增益。这时候要评估开环增益必须测反馈网络里真正的误差信号或者直接换成开环测试结构。说白了表达式只是工具前提是你得知道自己在测哪个信号。4. 用cross锁定0dB穿越频率参数方向与使用陷阱4.1 cross函数语法与参数解释cross函数的完整语法是cross(waveform, y_value, edge_number, edge_type)四个参数的含义分别是waveform要分析的波形比如dB20后的幅频曲线。y_value目标纵坐标值要找到波形穿过这个值的点。edge_number第几次穿越从1开始计数。如果波形多次穿过同一纵坐标这个参数用来指定取哪一次。edge_type穿越方向。可选rising从下往上穿、falling从上往下穿、either不管方向只要穿越就算。最后一个参数在很多版本里可以省略省略时默认按either处理。使用习惯上我强烈建议每次都把方向和序号写完整不要依赖默认值。原因后面马上说。4.2 0dB穿越频率的标准表达式评估运放带宽时我们最关心的是增益曲线从上往下穿过0dB的那个频率也就是单位增益带宽UGF。对应的表达式是cross(dB20(VF(/VOUT)), 0, 1, falling)它表示在增益的dB曲线上找到第一次从正dB方向穿过0dB线的地方返回对应的横坐标频率。对于典型运放低频增益远高于0dB随着频率升高增益持续下降最终穿过0dB。这个穿越方向的“falling”非常关键——它描述的就是“增益从正值掉到负值”的过程绝大多数正常运放的幅频特性都是这种形态。4.3 方向参数填错会出什么幺蛾子有人会把edge_type写成rising结果发现cross返回一个莫名其妙的小频率。原因很简单如果低频段增益已经在0dB以上那条曲线永远不会“从下往上穿过0dB”——除非它先降到0dB以下再反弹起来。于是“第一次rising穿越”可能在非常低或者根本不存在的地方被找到要么返回空值要么返回一个毫无意义的频率。再举个例子有些补偿得很好的运放在高频段增益曲线可能出现二次上升比如受零点影响如果真的有多重穿越那么cross(..., 0, 1, falling)给的是第一个下降穿越点cross(..., 0, 2, falling)给的是第二个下降穿越点。这种情况下需要结合电路结构人工判断该看哪个点不能拿到第一个穿越就盲目用。对于条件稳定系统这个判断题反而成了核心。4.4 用cross找相位曲线穿越-180度的变体cross不只是能找0dB频率在稳定性评估里还有一个非常常见的用法直接找相位曲线穿过-180度的地方。cross(phase(VF(/VOUT)), -180, 1, falling)这个表达式的含义是相位随频率升高不断下降找它第一次穿过-180度的频率点。这个值本身就是系统稳定性理论里的另一个关键频点——相位穿越频率。如果这个频率比0dB穿越频率低说明系统不仅相位裕度不足很可能相位裕度是负的这样的闭环系统一接上就振荡。把“增益0dB穿越”和“相位-180度穿越”两个频率比一下能很直观地判断稳定裕量。虽然我平时更多还是直接用相位裕度的合成表达式但做设计分析时这种“先分别找出两个关键频率”的中间量非常有价值。4.5 影响cross结果可靠性的细节采样点密度对cross的影响前面提过这里再补充两个实际项目里踩过的坑。第一频率扫描范围不够高。AC扫描上限如果设成100MHz而运放的UGF在200MHzcross函数在这个波形范围内永远找不到0dB穿越点表达式会返回空值。小信号带宽被低估的情况在早期验证里很常见我一般都会把AC扫描上限设成预估UGF的5到10倍宁多勿少。第二波形出现异常毛刺或数值错误。理想器件在极高频率下可能出现数值震荡导致dB曲线高频段产生虚假的上下抖动cross可能把一个噪声毛刺当成真正的0dB穿越点。如果发现cross返回值明显不对先用direct plot把dB20曲线画出来肉眼看一遍确认高频段是平滑下降而不是锯齿状。5. 相位裕度一步读取从嵌套公式到输出列表固化5.1 相位裕度的定义与相位函数返回范围相位裕度的定义是在增益曲线穿过0dB的频率点即UGF上环路增益的相位与-180度之间的差值。用公式表示就是PM 180 phase(UGF处的相位)如果UGF处的相位是-118度PM就是62度这符合大多数运放设计“至少45度、最好60度以上”的工程常识。如果PM只有30度甚至更低闭环阶跃响应一定会出现明显振铃PM为负则直接振荡。这里有个关节必须讲清楚Cadence里phase函数返回的角度是什么范围。不同版本和不同波形设置下相位返回值可能落在-180度到180度之间也可能落在0度到360度之间甚至可能带相位缠绕。如果你拿到的phase返回值正好被包络wrap到了另一个区间比如实际连续相位是-240度但函数返回了120度那么你套用180phase公式会得到300度——这显然不是正常的相位裕度。5.2 完整嵌套表达式把相位裕度计算写成一条表达式最直接的方式就是嵌套180 value(phase(VF(/VOUT)), cross(dB20(VF(/VOUT)), 0, 1, falling))拆开看内层的cross负责找到0dB穿越频率外层的value负责在这个频率点上读取相位曲线的纵坐标最后用180加上这个相位值就是相位裕度。实际使用中我一般会把三个指标分开放到Outputs里方便一眼看到全部value(dB20(VF(/VOUT)), 10) cross(dB20(VF(/VOUT)), 0, 1, falling) 180 value(phase(VF(/VOUT)), cross(dB20(VF(/VOUT)), 0, 1, falling))三条表达式的顺序就是评估运放时看的顺序增益多少、带宽多少、相位裕度多少。5.3 相位缠绕问题怎么处理如果你发现相位裕度算出来是一个完全离谱的大数比如接近300度、或者负了几百度十有八九是相位缠绕在捣乱。相位缠绕的物理来源是信号的真实相位是个连续变化的角度但计算器显示时通常压缩到-180到180的主值区间。一旦真实相位跨过-180度边界显示值会突然跳成180度附近形成一个“跳变”。如果UGF恰好落在这个跳变附近value读出来的相位就不是真实的连续相位导致公式失效。解决办法有三种。第一种是让Cadence在显示相位时做unwrapped处理。在波形窗口的曲线属性里可以设置相位是否连续展开。如果相位曲线是一根平滑下降的曲线0度→-90度→-180度→-270度中间没有跳变说明处于continuous模式这时的value读数才是能直接套公式的。第二种是自己留意穿越位置。如果UGF处的真实相位在-180度附近但显示值因为缠绕变到了179度这种位置那公式就会炸。人工判断一下实际相位应该落在哪个象限再用PM 180 - value(phase(...)) ...之类的修正办法也行但不如第一种省心。第三种是绕开相位函数直接读曲线上的“点到-180度的距离”。这种方法看的是UGF处相位与-180的差值本质上和180phase是一样的只是要保证相位显示模式是unwrapped。5.4 在ADE Outputs里固化表达式手动在计算器里算一次相位裕度没什么了不起真正有价值的是把这套表达式固化到仿真环境的Outputs里让它每次仿真结束自动出数。操作上在ADE L窗口的Outputs部分新建一个输出Pin把上面三条表达式分别粘贴进去给每个输出起个容易认的名字比如Gain_10Hz、UGF、PhaseMargin。之后每次仿真跑完Outputs窗口会直接列出这三个数值不用再去找波形、开计算器。如果再配合使用参数化cell或者多个corner的仿真你甚至可以跑完一次批量仿真然后扫一眼所有corner下这三个数字的变化趋势。这也是我前面说“先把表达式固化把时间省下来分析原因”的真正意义——数据自动出来之后人的精力应该花在判断“为什么SS角下相位裕度低了8度”上而不是花在找数上。5.5 手动校验一遍再信任结果表达式再方便第一次搭建的时候也值得手动校验一次。流程是用direct plot把VOUT的幅频和相频曲线画出来手动放标尺找到0dB穿越频率再在该频率下读相位计算PM然后把表达式算出的PM和手动读数对比相差在±1度以内就说明表达式没问题。这个校验动作我只做一次一旦确认后面就可以完全信任它了。之后如果再改电路结构或仿真设置表达式不需要频繁改动但仿真拓扑如果有大变化比如改成stb分析还是要重新核实波形引用名。6. 多结构运放下的变通差分输出、stb仿真与条件稳定6.1 全差分运放用两个节点相减代替单端输出全差分运放输出端是两个互补节点VOUTP和VOUTN没法直接把单个节点当输出读。这时候只要把表达式里的“输出波形”改成两个节点的差值即可value(dB20(VF(/VOUTP) - VF(/VOUTN)), 10) cross(dB20(VF(/VOUTP) - VF(/VOUTN)), 0, 1, falling) 180 value(phase(VF(/VOUTP) - VF(/VOUTN)), cross(dB20(VF(/VOUTP) - VF(/VOUTN)), 0, 1, falling))注意减号两边可以是两个复频域波形Cadence计算器支持这种向量化的波形运算。原理和单端一样只是先把差分信号合成为等效的单一信号再交给value和cross处理。6.2 使用stb仿真时怎么套用如果项目里用的是Spectre的stb稳定性分析insert probe或者指定环路断开点仿真完成后输出的是Loop Gain环增益波形。这个波形本身就是带幅度和相位的频响曲线value和cross完全可以继续使用value(dB20(loopGain), 10) cross(dB20(loopGain), 0, 1, falling) 180 value(phase(loopGain), cross(dB20(loopGain), 0, 1, falling))唯一的区别是波形引用名变了具体名称要以stb分析实际生成的输出变量为准。有的版本里环路增益可能叫loopGain有的叫LGain甚至有的直接在Outputs里已经帮你算好了相位裕度。我的建议是即便Cadence自带了PhaseMargin输出你也要知道它是用哪条表达式算出来的——一旦它显示的数值和电路预期不符你能自己复算一遍排查问题而不是干瞪眼。6.3 条件稳定与多穿越点cross的edge参数是救命的前面埋了个伏笔当运放的增益曲线不是单调下降时0dB穿越可能不止一个。这时“第一次下降穿越”可能不是真正的单位增益频率或者系统虽然穿过0dB一次但相位已经先穿了-180度这种系统称为条件稳定。用cross函数时这种系统的处理方式就体现出灵活性了cross(dB20(VF(/VOUT)), 0, 1, falling) cross(dB20(VF(/VOUT)), 0, 2, falling) phase(cross(dB20(VF(/VOUT)) ... 依次读出来对比你可以分别取第1个和第2个穿越点的频率再分别到相位曲线上读对应的相位值从而判断系统真正有意义的稳定边界在哪里。这比手动在曲线上找“那个点”靠谱得多。6.4 扩展使用瞬态波形读时间点最后说一个很多老工程师都会顺手用到的扩展。value和cross的作用对象不限于AC频响曲线瞬态波形同样适用。比如你想测运放做跟随器时输入阶跃后的上升时间就可以用cross找输出波形从10%VDD到90%VDD的两个时间点做减法得到上升时间cross(VT(/VOUT), 0.1*VDD, 1, rising) cross(VT(/VOUT), 0.9*VDD, 1, rising)这里的VT(/VOUT)是瞬态电压波形横坐标是时间不是频率。原理和AC里完全一样cross找穿越指定纵坐标的时间点value也可以读某个时间点上的瞬时电压。函数本身是通用的换一个仿真类型只是波形的横轴含义变了而已。我在实际做运放验证里还有一个习惯就是会把value和cross的表达式组合成一小套“计算模板”存下来不同项目之间直接复用。刚开始搭建的时候多花十分钟把表达式调对后面每一次跑仿真都在替你省时间。这个投入怎么算都划算。说到底value和cross都是Cadence计算器里最基础的小函数单独拿出来谁都会用但把它们设计成一套固定的指标提取表达式之后运放验证的效率提升是非常直观的。我也建议每个用Virtuoso做运放评估的工程师都花一个下午把这三个指标增益、UGF、相位裕度的表达式在自己常用的测试拓扑上调通、验证、固化后面所有项目都能直接受益。