1. 混合信号仿真的核心痛点:模拟和数字各说各话,谁能来翻译
做了几年模拟IC或数模混合电路设计的朋友,基本都会撞上同一个尴尬:纯模拟的 Spectre 仿真跑得稳稳的,纯数字的 Xcelium 仿真也顺顺利利,可一旦把带数字控制逻辑的模拟前端、或者带模拟接口的数字模块放在同一个 testbench 里,事情就开始变得拧巴。你今天想让一个 ADC 的 SAR 逻辑和比较器一起工作,明天想让一个 DC-DC 的环路里混进数字补偿算法,如果拿纯 SPICE 去跑那点数字门,速度慢到怀疑人生;拿数字仿真器去处理模拟节点,它又根本不认识电压连续变化这回事。
这就是 Cadence ams 仿真器真正要解决的问题——它把模拟仿真引擎和数字事件驱动引擎拼在同一个仿真框架里,让模拟信号和数字信号在同一个时间轴上正常工作。你可以让一个用 Verilog 写的数字状态机,去控制一个用晶体管搭出来的电荷泵;也可以让一个模拟比较器的输出,直接变成下游数字逻辑的时钟沿。ams 负责在两者之间搭桥,把连续时间、连续电平的模拟信号,翻译成离散时间、离散电平的数字信号,再反向翻译回来。整个过程里你不需要自己手工建模接口电路,也不用把数字逻辑硬掰成 SPICE 网表。
对做混合信号电路的人来说,这个东西解决的不只是"能不能跑"的问题,而是"跑得准不准、快不快"的问题。纯模拟网表跑一个带数字校准环路的 PLL 或者 SAR ADC,收敛到天荒地老都是常态;纯数字仿真又没法评估模拟部分的噪声、失调、建立时间这些真实物理特性。ams 这种联合仿真方式,让你可以在同一个环境里同时看到模拟域的瞬态行为和数字域的时序行为,调试的时候直接在 Virtuoso 波形窗口里看数模交界处的信号是怎么衔接的,比来回切两个工具、对两遍波形要省心太多。
这篇文章主要围绕 Cadence ams 仿真器对混合信号电路进行仿真的完整链路来写,从工程搭建、connect rule 配置、瞬态仿真设置,到常见的仿真不收敛、器件未定义、波形异常这类扎手问题的排查思路,再到工程实践中怎么让大型混合信号电路跑得又快又稳。内容基于我自己在多个混合信号项目里的实测经验,适合刚接触 ams 的设计工程师,也适合被几个疑难报错卡了很久、想换个排查思路的老手。
2. 工程搭建阶段最容易忽略的"地基"问题
很多人跑 ams 仿真,上来就打开 Virtuoso 画 testbench,画完直接 New Simulation 选 AMS,结果一堆匪夷所思的报错:找不到仿真器、顶层视图冲突、数字库起不来、connect rule 不生效。这些问题的根源基本都在工程搭建这一步埋下了。
2.1 顶层视图别用 schematic,用 config 视图
这里先明确一个概念:在 Cadence 环境里做混合信号仿真,顶层一定不是一个普通的 schematic 视图,而是一个 config 视图。config 视图做的事情,是把整个设计里每一个 instance 所对应的仿真视图给"编排"好——模拟模块用 schematic 还是 extracted 视图跑,数字模块用 Verilog netlist 还是行为级描述,都在 config 里统一指定。
新建 config 视图的操作不复杂,在顶层 cell 上 Create > Cellview > From Cellview,或者直接用 Hierarchy Editor 打开,然后 Library 选好、View 选 config,之后会弹出一个对话框,仿真的类型要选 AMS(而不是 Spectre 或者 Xcelium 单独跑的那种)。选完之后,Hierarchy Editor 里会把整个顶层列出来,你要逐行检查每一个 instance 的 View 列:模拟模块对应的是 schematic,数字模块对应的应该是一个支持数字仿真的视图名,比如 functional 或者 Verilog。这个步骤很关键,因为如果你在顶层直接放了一个数字模块,而它的 View 列写的是 schematic,ams 会尝试用模拟引擎去解释 Verilog 的行为——大概率直接报错或者仿真结果完全不对。
我曾经在做一个带数字校正的流水线 ADC 时,顶层有一个很大的数字校准模块,工程师建完 config 之后没检查 View 列,直接开跑,结果仿真器报了一堆类似unknown module type的错。后来把那个数字模块的 View 列改成 functional 才正常,但中间排查浪费了大半天。所以 config 视图建好之后别急着跑,花两分钟把每个 instance 的仿真视图检查一遍,省下来的时间远超这两分钟。
2.2 connect rule:数值模拟与数字逻辑之间的"翻译规则"
另一个容易在工程搭建阶段被忽略、但直接影响仿真正确性的,是 connect rule。混合信号仿真里,模拟信号和数字信号碰面时不能直接硬接,需要一个中间模块做信号翻译——模拟域的电压值变成数字域的二值逻辑,数字域的 0/1 变成模拟域的电压摆幅。这个翻译不是随便定一个阈值就行,它包含一系列规则:高电平检测阈值是多少、低电平检测阈值是多少、上升沿之后信号要稳定多少时间才认为是有效电平、从数字域返回模拟域时驱动能力怎么模拟。
Cadence 自带的 connect rule 里会预置几套常用配置,比如connect_rules里会有针对cmos电平的一套规则,也有更精细的eEn之类的用于不同电域匹配的规则。你需要在仿真设置里显式指定使用哪一套 connect rule,否则 ams 很可能默认不插入任何翻译模块,最后数字输出直接对接模拟节点,波形乱七八糟,信号看起来像悬空一样。
选择 connect rule 的原则很简单:数字部分的供电域是多少伏,就选对应电平的规则。3.3V 的数字逻辑和 1.8V 的数字逻辑,阈值标准差很多,如果规则选错,可能模拟输出 2.5V 的时候,数字端已经判定为高电平了,甚至可能出现亚稳态误判。尤其在一个芯片里同时存在多个电压域的时候,你要给不同的接口分别指定不同的 connect rule。这个工作在 config 视图或者 AMS 仿真设置界面的 Connect Rules 标签里完成,是一个多对多的映射关系,做的时候要有点耐心。
2.3 库的加载顺序和数字 view 引用路径
混合信号仿真比纯模拟仿真多出一个数字仿真引擎,所以启动仿真时,数字库的加载路径是否完整,直接影响你能不能跑起来。当年 Cadence 的 AMS 仿真器在启动时会去读cds.lib和hdl.var,这两个文件里定义了数字库的位置和仿真器要用到的语言标准。如果你在做一个项目时发现,纯模拟跑得好好的电路加上数字模块之后报library not found,十有八九是当前启动目录下的cds.lib没有把数字库的路径包含进去。
这种问题在多人协作的大型项目里特别常见——模拟工程师的启动环境只配置了模拟库路径,数字工程师的启动环境只配了数字库路径,到了混合信号联调阶段,谁都没想起来要把两边的库路径合到一起。处理方法也简单:在仿真启动目录的cds.lib里,把数字库所在的路径一行行加进去,或者在软件界面里选 Include 对应的库文件。加了之后记得验证一下数字模块的 view 能正常打开,否则到了仿真关键时刻再报找不到模块,心态真的会崩。
3. 实操链路拆解:从 testbench 搭建到波形输出
当你把 config 和 connect rule 这些地基问题理顺之后,真正开始跑仿真的链路其实并不复杂,但每一步都有需要注意的细节。这一节我把完整的操作路径拆开讲,适合还没有完整跑通过一次 ams 仿真的朋友逐步对照。
3.1 testbench 的搭建方式与激励设置
混合信号仿真的测试平台,比较推荐的搭建方式仍然是图形化的 schematic testbench,而不是网表。原因在于模拟部分需要的激励源——比如斜坡信号、正弦波、带有特定噪声特性的信号——用 AnalogLib 里的vsource、isource来搭非常直观,波形参数直接在 CDF 里改就行。数字部分的激励,你可以用数字 pin 直接给电平,也可以用 Verilog 写一个小的 stimulus 模块,把时钟和复位这些基础信号用initial块或者always块来产生。
不过有一点要提醒:在同一个 testbench 里,同时存在模拟激励源和数字激励源的时候,要注意它们之间不要直接相连。模拟 source 的输出节点如果接到数字模块的输入端口,中间必须经过一个ahdl2dig或者由 connect rule 自动插入的转换模块。同理,数字模块的输出也不能直接驱动模拟晶体管的栅极,中间需要一个dig2ahdl的转换。这些转换模块通常是由仿真器自动插入的,但前提是你在仿真设置里把 connect rule 配好,并且没有用一些特殊的端口类型把自动插入机制避开了。
我自己习惯的做法是,模拟激励源尽量用vsin或者vpulse这种 AnalogLib 的标准源,数字激励用 Verilog 写在单独的tb_stimulus.v文件里,通过 config 视图把它挂到 testbench 下的一个数字 instance 上。这样分离管理,调试的时候想改激励,模拟部分直接改 CDF 参数,数字部分直接改 Verilog 代码,互不干扰,比混在一起写要清爽很多。
3.2 AMS 仿真设置面板的关键选项
在 Virtuoso 里启动 AMS 仿真,进入仿真设置面板之后,你会看到一大堆选项。这里不用每个都搞懂,但几个关键的必须认识清楚:
Analysis 标签页:混合信号仿真用的最多的还是tran,也就是瞬态分析。AMS 的 tran 分析和 Spectre 的 tran 不太一样,它不仅要设置仿真时长和精度,还要为数字引擎指定一个时间分辨率(time resolution)和数字部分的默认步长。我一般把精度设成跟模拟部分需要的精度一致,但数字引擎的时间分辨率可以稍微放宽一点,这样仿真速度快一些,又不影响数字逻辑的功能验证。如果你的电路里有高频数字时钟,记得把数字引擎的时间分辨率设得足够小,至少要比时钟周期的 1/100 还小,否则数字部分的事件会丢失。
Environment 标签页:这里要关注的是switch view list。在 AMS 仿真里,这个列表决定了一个 instance 优先用哪个视图来仿真。典型的设置是把ams放在最前面,然后是schematic、functional、Verilog等。如果你希望某个数字模块用行为级的 Verilog 视图而不用原理图,就在这个列表里把对应模块的视图优先级配置好。我遇到过一个案例,工程师在 config 里已经把数字模块选成了 functional,但 switch view list 里没有把 functional 加进去,结果仿真器还是去找 schematic,报了找不到视图的错。
Connect Rules 标签页:这个前面已经提过,是混合信号仿真的关键配置。除了选择规则集,还要注意 rule 里定义的 supply 电压值是否和你电路里的实际电压一致。比如你的数字逻辑供电是 1.8V,但 connect rule 里的默认高低电平阈值是按 3.3V 算的,数字输出的高电平就会被错误地映射成 3.3V 摆幅,驱动模拟电路时会导致工作点完全错误。这种问题特别隐蔽,因为波形看起来很正常,只是幅值不对,很容易被误判成电路本身的问题。
3.3 初始化状态和电源上电顺序
混合信号电路和纯模拟电路有一个显著区别:数字部分有寄存器、状态机,上电默认状态可能不确定。所以在瞬态仿真里,你必须在仿真开始的一小段内把复位信号拉起来,让数字逻辑进入到已知状态,然后再开始看模拟部分跟数字逻辑交互的状态。
这一点看起来简单,但很多人跑混合信号仿真会踩到类似的坑:复位信号没加,或者复位时间不够长,状态机进入了一个非法状态,整个仿真跑出来的波形奇奇怪怪,最后排查半天,发现就是复位的时序问题。另外,如果电路里有多个电源域,上电顺序也要在仿真里体现出来。某些工艺下,数字域必须先于模拟域上电,否则接口处的 ESD 结构会异常导通,仿真波形就会乱七八糟。上电顺序可以写在 Verilog 的initial块里,也可以用多个vsource配合延迟参数来模拟。
3.4 仿真完成之后的波形检查重点
跑完仿真之后,在 Virtuoso 的 Visualization & Analysis 窗口里把关心的节点都拉出来看看。这里要注意的是,数模接口处的信号波形有两种观察方式:一种是看模拟域节点的连续电压波形,一种是看数字域节点的逻辑电平。如果 connect rule 配置正确,这两个观察方式看到的信息应该是对应的——数字节点为高电平时,模拟节点上应该稳定在高电平对应的电压值附近。
检查波形的重点放在接口处:模拟信号进入数字逻辑之后,电平翻转的阈值、延时是否符合预期;数字信号输出到模拟电路之后,驱动能力是否足够,上冲下冲是否在可接受范围内。如果发现接口处波形有毛刺、反复震荡,多半是 connect rule 里的 hysteresis(迟滞)设置太小,导致数字输入在阈值附近抖动。把阈值迟滞加大一点,这类问题通常会缓解。
4. 实际项目中躲不开的疑难杂症:报错、发散、波形异常
跑混合信号仿真,报错和波形异常是家常便饭。这一节专门讲我在实际项目里遇到次数最多的几个问题,以及完整的排查链路。每个问题都给排查思路而不只是结论,这样下次遇到类似状况你知道从哪儿下手。
4.1 仿真报"器件未定义",别急着怀疑电路网表
器件未定义这类报错,集中出现在两种场景:一是你启动仿真的目录环境不完整,二是 connect rule 或者某个数字库里的模型绑定出了问题。
第一种场景很容易识别,报错信息里会明确指出找不到某个库或者某个 view。排查链路是:先看报错信息里的库名是否存在于当前cds.lib的搜索路径里,如果库里确实有,看是不是被 exclude 了;如果库是有的,看 view name 是不是对得上,比如你调用的是一个functional视图,但库里只建了schematic,肯定报未定义。
第二种场景更隐蔽——报错信息可能提示的是某个标准单元或者某个门级模型未定义。这种情况常见于数字模块使用了某个工艺库的门级网表,在启动 AMS 仿真时,数字引擎需要加载这个工艺库的行为模型,而这个模型往往不是 Cadence 自带的标准库,需要你用-v或-y参数指定加载。如果这个参数没配置,仿真器会直接告诉你某个模块找不到,然后终止。排查链路是:分析报错的模块名是哪个标准单元,去对应的数字工艺库目录下确认是否存在对应的 Verilog 模型文件,再看仿真设置里是否正确包含。
我在一个项目里遇到过整个数字模块都是行为级 Verilog,但顶层里某个子模块的写法生成了一个HDL模型引用,而那个文件在项目仓库里已经改名了,结果 ams 启动仿真时找了半天不对应的文件,最后报了一连串的未定义错误。查清楚之后改了路径名,问题迎刃而解。所以遇到这种报错,先不急着怀疑自己电路有问题,检查环境和库路径比反复审视电路更高效。
4.2 瞬态仿真的发散和不收敛:先从步长和初始条件下手
混合信号仿真里最常见的"仿真发散",通常不是真的算法不收敛,而是某些关键参数设置不合理。在 ams 仿真的 tran 分析里,最容易出问题的参数是模拟部分的最大步长和数字部分的事件分辨率。步长设得太大,仿真器会跳过一个关键的事件或快速变化的模拟信号;步长设得太小,仿真时间爆炸式增长,甚至在一些强非线性电路里出现局部震荡。
排查链路我建议这样走:第一步,先把模拟部分的时间步长收紧,比默认值小一个数量级试试,看发散点是否发生变化。如果变化了,说明是步长导致的问题。第二步,检查电路的初始条件。混合信号电路里有电容、电感这类储能元件,也有数字寄存器的初始状态。如果初始条件设置得和稳态工作点相差太远,仿真开始后系统会经历一个剧烈的暂态过程,很容易发散。第三步,检查是否有正反馈环路在仿真里形成了振荡。数字输出经由 connect module 转成模拟电压,再反馈回模拟电路,这个路径如果延时设置太小或者没有迟滞,很容易自我振荡。可以在 connect rule 里给反馈路径加入适当的延时,通常能压住这种振荡。
还有一个容易忽略的点:connect rule 中数字转模拟的转换速度设置。从数字域切到模拟域时,转换模块不仅要把逻辑电平映射成电压,还要模拟一个有限的驱动能力,也就是输出电压转换有一个 slew rate。如果这个 slew rate 设置得非常大,对于模拟电路来说相当于一个快速上升沿,可能会激起某些寄生效应,导致振荡。把 slew rate 调到一个合理的、接近真实工艺的水平,很多莫名的发散问题就消失了。
4.3 波形红线、悬空感:问题多半在接口状态
仿真结束后拉波形,发现某根信号线是红线(表示信号是 X 态或者未驱动)或者明明应该有时钟却平得像一条直线,这往往不是波形查看器的问题,而是混合信号接口没有正确连接,或者 connect module 根本没有插入。
排查链路:第一步,回到 config 视图检查该信号所连接的两个 instance 的仿真视图类型。如果一端是模拟模块的 schematic 输出,另一端是数字模块的 Verilog 输入,那接口处应该有一个 connect module。想确认它有没有插入,可以在仿真日志里搜索 connect module 相关的提示。第二步,检查该信号在数字模块端口的类型声明。如果数字模块的端口声明成了output reg但实际没有驱动,或者声明成了inout但驱动逻辑没有写,就会看到红线。第三步,如果是模拟信号输入到数字模块的内部节点,检查有没有通过一个明显的电平检测环节——数字模块的输入端口默认接收的是逻辑电平,如果你把模拟波形直接接到一个数字端口上,但 connect rule 里对这种 float 模拟信号的处理策略没有配置好,也容易出现 X 态。
我遇到过一次很刁钻的情况:一个模拟比较器的输出端直接接到了数字模块的时钟输入,仿真跑下来数字模块完全没反应。检查半天发现是那个比较器输出在高阻状态的时候数字输入悬空,connect module 对悬空输入的处理策略默认是 X,而数字模块的时钟输入检测到 X 就不翻转。后来在比较器输出端加了一个弱下拉电阻,把悬空态拉到一个确定的电平,问题立刻解决。这种问题在设计 real chip 的时候几乎不会暴露,因为还有 ESD 二极管拉着,但仿真环境里没有这些寄生器件,就全靠你手工处理了。
5. 让大型混合信号电路跑得又快又稳的实用策略
当电路规模变大、仿真时长拉长之后,你会发现真正决定项目进度的不是仿真能不能跑,而是仿真跑完要多久。一个带数字校准的 PLL 或者一个全数字控制的电源管理芯片,如果用最严格的精度去跑,一次瞬态仿真可能几小时起步。这一节分享几个我自己实践下来真实有效的提速策略,同时兼顾普通工程师通常不会注意到的细节。
5.1 数字部分绝不都用晶体管级视图仿真
这是混合信号仿真提速的第一法则:数字模块该用 RTL 或行为级视图的,绝不拉到晶体管级。很多模拟工程师手里拿到的数字模块,网表有 Verilog 门级模型也有 SPICE 网表,习惯上会把所有模块都设成 schematic 视图,觉得这样"保证精度"。但实际上,门级 Verilog 模型和晶体管级 SPICE 模型在功能上是等价的,而仿真速度差距是数量级的。只在必须做数模接口时序精调的时候,才把关键的数字路径保留在晶体管级或者高精度的模拟视图,其余部分统一用行为级 Verilog 跑。
我自己常用的做法是,整个系统仿真阶段,数字部分全部用 RTL 行为级;只有当系统仿真找到一个问题点、需要精确定位到底是哪一段数字路径时序出问题的时候,才会把数字模块切换到门级网表视图重新跑。这样既保证了调试速度,又不会在最终环节被不精确的模型误导。
5.2 仿真精度按区域动态配置
ams 仿真器允许你针对不同的电路区域设置不同的仿真精度。比如模拟核心部分需要高精度的连续时间求解,而一个纯数字的 SPI 接口模块就完全可以用更大的时间步长来跑。这种按区域配置精度的方式,可以把仿真总时长压缩一半以上。
具体操作上,你可以在 config 视图里选择修改某个模块的仿真精度属性,或者用 ams 的 simulation resource 相关配置,把一个模块指定为"低精度"或"高精度"。需要注意:精度切换的地方正好是数模接口的话,要特别小心。比如一个数字模块的输出经过 connect module 变成模拟电压,这个电压本身又会驱动一个高精度的模拟模块,那么数字模块即使跑低精度,它的输出转换到模拟域时也要确保时间分辨率足够,否则模拟部分会因为输入信号跳变太粗而产生错误的瞬态响应。
5.3 分段验证策略:别等整个芯片全部收敛了再开始看波形
混合信号电路一个很大的风险点是:验证发现问题的时间点往往很晚。一个全芯片级的数模混合联合仿真,从设计稳定到跑通可能要好几天,如果这个时候才暴露出一个接口逻辑错误,修复成本极高。所以我强烈建议采用分段验证策略。
分段方式可以按功能子电路来切:先把模拟核心和它的数字控制逻辑单独拿出来,搭一个小 testbench 跑通,确认这个子系统的模拟控制和数字状态切换是匹配的;然后再把各个子系统拼接起来做整体联调。这个策略的价值在于,它把混合信号仿真最容易出的那类问题——接口不匹配、状态机转换和模拟建立时间对不上——控制在子系统阶段就暴露出来,等你做全芯片联合仿真的时候,剩下的主要是各子系统之间的交互问题,排查范围小很多。
在分段验证时,建议始终保持同一个 testbench 架构:模拟激励源用 AnalogLib,数字激励用 Verilog 文件,connect rule 用同一套。这个习惯带来的好处是,当你从子系统仿真正式过渡到全芯片仿真时,不需要重新调一大堆配置,只需要更新 config 视图里的顶层单元名,其余设置可以直接复用,省时省力,也少了一个出错环节。
5.4 关于 connect rule 参数的一些实操心得
最后聊一点 connect rule 参数调优的心得,这部分属于常规文档里不会细讲、但实战中能显著影响仿真结果的内容。
**第一个心得:阈值迟滞(hysteresis)不要设成 0。**很多人在做数字输入阈值设定时,觉得阈值就是一个固定的电压点,高于它就算高电平,低于它就算低电平。但实际上,如果阈值没有迟滞,任何微小的噪声都有可能在阈值附近引起反复翻转,在仿真里表现为数字信号上的毛刺,甚至导致状态机误触发。我一般建议迟滞设成阈值的 5%~10%,既能有效抑制噪声,又不影响正常的速度。不过要注意,如果电路里本身设计的比较器就自带迟滞,那 connect module 里的迟滞就不要再叠加太多了,否则接口的门限会变得过于迟钝,影响时序判断。
**第二个心得:数字转模拟的 slew rate 要根据负载来设。**connect module 转出来的模拟电压,从 0 到 VDD 的爬升速度不是无穷快,它由 rule 里的 slew rate 参数决定。这个参数如果设得太快,驱动大电容负载时模拟节点会出现虚高的尖峰;如果设得太慢,下游模拟电路可能还没完成阈值翻转,数字逻辑已经进入下一个状态了。我通常结合 testbench 里实际的负载电容值,估算一个合理的 slew rate,再在仿真里验证。估算公式很简单:slew rate 约等于数字输出的驱动电流除以负载电容。比如驱动电流 1mA,负载电容 1pF,那么 slew rate 就是 1V/ns,设成这个量级比较合理。
**第三个心得:多个电压域同时存在时,connect rule 的电压参数映射一定要逐一核对。**现代混合信号芯片上,1.8V、2.5V、3.3V 甚至 5V 的电压域可能同时存在。connect rule 里对每一组 voltage domain 都定义了高低电平阈值和输出驱动电压,如果一个 3.3V 的数字输出模块没有按 3.3V 域去映射,connect module 用错误的电压参数去转换信号,下游模拟电路看到的就是一个幅值错误的驱动。这种问题在波形上很隐蔽,因为接口看起来是"有信号的",只是幅值差一点。多电压域的连接规则配置,建议做成一张表格,把每个接口对应的 voltage domain 写清楚,放在项目文档里,方便后续 review 和多人协作时统一。
写在最后的经验
使用 Cadence ams 仿真器做混合信号电路仿真这件事,本身并不复杂,很多困扰新手的问题是环境配置和仿真设置层面的,而不是电路原理层面的。把 config 视图、connect rule、库路径这些地基打好,再熟悉一下瞬态仿真里步长和初始条件的调参逻辑,大部分常规混合信号电路的仿真都能顺顺当当跑完。真正花时间的地方往往在那些"看起来能跑但结果不对"的疑难场景里,这时候耐心按接口信号链路的走向一步步排查,比不断重跑仿真有效得多。
最后分享一个小技巧:每次启动一个混合信号项目的联合仿真前,先拿一个最小规模的 testbench 把链路跑通——一个模拟 buffer 和一个数字反相器接在一起,看看 connect module 是否按要求插入了,信号转换是否符合预期。把它当作混合信号仿真的"上电自检",花不了十分钟,但能帮你确认整个环境配置是否健康,之后再做正式的大规模仿真时,你会少踩非常多莫名其妙的坑。