☰
Cadence中Spectrum工具FFT仿真ADC动态指标实践指南
2026/10/6 15:12:01 网站建设 项目流程

进了IC这行做模拟/混合信号设计,ADC测试是绕不开的坎。我记得自己第一次用Cadence里的Spectrum工具给一个SAR ADC做FFT动态仿真,按着datasheet搭了测试平台,信号源、时钟、采样点都写了,结果跑完一看ENOB,满打满算比预期低了三个比特。当时整个人是懵的,因为电路明明在tran仿真里看着波形很干净,怎么一到频谱上就全不对了。

后来花了不少时间把Spectrum工具的参数挨个试了一遍,又把FFT本身的原理捋了一遍,才明白ADC动态指标仿真这个事儿,根本难点不在电路,而在“测量方法”。给ADC输入一个正弦波、抓一串输出码、做FFT、读SNR/ENOB,这套流程看起来简单,但里面每个环节都有讲究:采样点数选多少、输入频率怎么定、仿真时间留多长、FFT窗口从哪儿截、加什么窗函数、怎么排除谐波和直流分量,任何一个细节没做对,最终指标都会失真。

这篇文章就把我在Cadence环境下用Spectrum工具FFT仿真ADC动态指标的完整流程写清楚,从Testbench搭建、tran参数设置,到Spectrum工具里的具体配置,再到SNR/ENOB/SFDR这些指标怎么从频谱上算出来,最后附上我踩过的几个高频坑。如果你正准备做ADC动态仿真,或者做了但结果总觉得不对,这份指南应该能帮上忙。

1. 为什么ADC动态指标仿真这么容易翻车

1.1 FFT测ADC指标的底层逻辑

ADC动态指标,说到底是衡量ADC在把模拟信号转换成数字码流时“保真”到什么程度。逻辑上最直接的做法是在时域里把输入输出做对比,但实际没人这么干,因为时域波形很难把量化噪声、谐波失真和信号本身清晰剥离。FFT就不一样,它把时间序列变换到频域,信号只在某一个频率点形成一个峰值,而量化噪声、电路噪声、谐波失真这些能量会分散到其他频率位置。于是信噪比、失真这些指标就能从频谱上直观地读出来。

这里有个新手容易忽略的点:FFT本身存在处理增益。对N点的FFT,时域里叠加在信号上的总噪声功率会被分配到N个频点(bin)上,信号能量却集中在单一bin。也就是说,做完4096点FFT之后,信号峰值和噪声底之间的高度差,并不等同于时域上的SNR,两者相差一个与N和窗函数相关的折算系数。Cadence的Spectrum工具会自动帮你处理大部分折算,但理解这个底层逻辑能避免很多误读。

1.2 相干采样:FFT测准的大前提

FFT默认把当前分析窗口内的信号当作“无限周期延拓”来处理。如果信号在窗口首尾处的相位不连续,频谱上就会出severe泄漏:本该集中在信号bin的能量会撒到相邻的几个bin里去,信号峰值变矮,噪声底抬高,SNR自然就偏低了。

要避免泄漏,最稳妥的办法是满足相干采样条件:

Fin / Fs = M / N

其中N是FFT采样点数,必须是2的整数次幂,M是窗口内包含的信号周期数,尽量取奇数(通常取质数更安全),且M和N互质。举例来说,采样率Fs=100MHz,N=4096,M取997,那么输入频率就是:

Fin = 997 / 4096 × 100MHz = 24.341796875MHz

注意Fin表达式里这个小数精确到Hz级别,在理想信号源里直接填这个数就行,千万别四舍五入到24.3MHz,差一点点都会在频谱上看到泄漏。M取质数的原因,是为了保证采样点在正弦波相位上尽量均匀散布,不出现重复采样相同相位点的情况,这样FFT结果最能反映真实性能。

1.3 Spectrum工具在Cadence里的定位

很多初学者误以为做FFT只能把波形导出去用Python或者Matlab处理。实际上Cadence本身就有完整的频谱分析能力——Spectrum工具,它在ViVA波形窗口和Calculator里都能调出来。它针对混合信号仿真做了不少专门优化,比如可以直接从瞬态波形里截取一段做DFT/FFT,支持多种窗函数,还能自动做相干增益修正和DC分量消除,甚至自带SNR、SINAD、SFDR、THD这些测量项,不需要你手动去数bin。

所以这篇文章的核心思路是:整个FFT动态仿真链路全部在Cadence内部完成,不依赖外部脚本。但工具是死的,参数是活的,Spectrum工具里的每一项设置都对应着FFT里一个具体的影响因素。下面就从Testbench开始,把这整套流程过一遍。

2. 从Testbench到Spectrum:这套流程怎么一步步跑通

2.1 搭建一个能用来做FFT的Testbench

被测对象不同,Testbench细节会有些变化,但用来跑动态指标仿真的核心框架是一致的:正弦波激励源、采样时钟源、ADC(待测或理想模型)、理想DAC(把数字码流转回模拟电平便于看波形)。

我一般建议新手先用行为级理想ADC(ahdlLib库里有现成的adc_12bit这类模型)跑通整个流程,再用自己的实际电路替换。原因很现实:真实ADC晶体管级tran仿真跑一次可能就是几个小时,而行为级模型几秒钟出结果。先用理想模型把Spectrum工具的每个参数摸透,确认测量链路是准的,再换实际电路,排查问题时就不会把“电路问题”和“测量方法问题”混在一起。

具体搭建时几个关键点:

  • 正弦激励源用analogLib里的vsin,幅度设置为满量程回退一点,比如0.5dBFS到1dBFS之间。满幅输入容易让ADC输出接近限幅,动态指标会受影响;回退太多又会让量化噪声占比偏大,SNR也测不准。比较常用的值是-1dBFS。
  • 采样时钟用vpulse,上升沿和下降沿设成几十ps,周期按Fs设定。比如Fs=100MHz,周期就是10ns。
  • 理想DAC用ahdlLib里的dac_12bit(如果你用的ADC是12bit),把数字输出总线转回模拟电平。这步纯粹是为了让ViVA更容易显示和处理波形,FFT测量也是对DAC输出的模拟波形来做的。

2.2 瞬态仿真精度参数怎么设

Testbench搭好后,最影响FFT成败的就是tran仿真参数,尤其是maxstep。很多人在这一步不设maxstep,或者干脆用liberal精度跑,结果ADC的开关切换不平滑,量化噪声被人为压低或者抬高,后面不管Spectrum工具怎么调都救不回来。

我常用的tran设置:

  • stop time:至少等于 N/Fs + 额外的建稳时间。N/Fs是完整采样窗口的基础时长,但ADC从启动到进入稳定工作状态需要一段时间,所以FFT分析窗口起点不能是0时刻,要跳过起振阶段。一般我让总仿真时间比N/Fs多20%到50%。仍以前面例子的参数来算:N/Fs = 40.96us,加上5us建稳时间,总仿真时间设到46us比较稳妥。
  • maxstep:建议设为采样时钟周期的1/100左右。100MHz时钟就是10ns周期,maxstep我通常设到100ps,复杂电路条件不允许的话也至少要1ps~10ps。maxstep太粗会让DAC输出波形出现阶梯状毛刺,频谱底噪明显抬起来。
  • 精度档位:至少选moderate,做关键指标对比时用conservative。liberal档做FFT基本等于白跑。

为了让FFT起点后的波形成分恰好覆盖N/Fs长的时间窗口,也可以在tran设置里直接定好总时间,然后在下一步Spectrum参数里指定分析窗口的起止时间,二者配合起来就行。

2.3 在ViVA里打开Spectrum的操作路径

仿真结束后,打开ViVA波形窗口,选中理想DAC的输出波形。在波形窗口的菜单栏找到Tools或者直接在ViVA里打开Calculator,从Calculator的Analysis菜单里选Spectrum/FFT,然后在弹出的对话框里配置FFT参数。不同Cadence版本入口名称略有差异,IC6.1.8之后一般都能看到专门的Spectrum对话框,操作逻辑类似。

对话框里需要填的核心内容就是分析窗口的起止时间、FFT点数、窗函数类型、DC分量是否去除、是否做相干增益修正。这些参数怎么填,在下一个章节详细展开。填完点OK之后,ViVA会生成一条频谱曲线,在频谱曲线上右键调出Measurements菜单,就能看到Signal-to-Noise Ratio、Signal-to-Noise and Distortion Ratio等测量项,工具可以直接计算出SNR和SINAD。

3. Spectrum工具里那些参数到底该怎么设

3.1 分析窗口的起点和点数N

Spectrum工具里最容易被新手忽略的就是分析窗口起点。如果直接让FFT从tran仿真的0时刻开始计算,那仿真启动瞬间的建立过程、电源毛刺都会被当作有效信号做FFT,底噪很难看。正确做法是跳过前几个微秒的起振阶段,从一个“稳定工作”的时刻开始截取。

以设置的46us仿真总时长为例,可以让FFT分析窗口从5us开始,到46us结束,正好覆盖41us的稳定期数据。而需要的采样点数为N=4096,对应的有效分析时长是N/Fs=40.96us,剩余时间留作裕量。

点数N为什么必须是2的整数次幂?因为底层DFT算法用的是快速傅里叶变换,点数取2的幂才能最高效地完成计算,也方便噪声功率按N等份分配。对ADC动态指标仿真来说,N至少取4096,追求更精细频谱可以取8192或16384。N越大,频谱分辨率越高,每个bin的宽度越小,靠得比较近的杂散和谐波更容易被分辨出来,代价是仿真时间变长。新手先用4096,把整套流程跑顺了再增加。

3.2 窗函数选择与相干增益修正

窗函数的本质是对时域信号做幅度渐变处理,让窗口首尾幅度趋于0,从而缓解非相干采样带来的频谱泄漏。但加窗是一把双刃剑:它压制了泄漏,却会把信号主瓣展宽,降低频率分辨率,同时还会改变信号的幅度。

做ADC动态FFT时窗函数选项常见的有三类:

  • Rectangular(矩形窗):相当于不加窗,频率分辨率最高,但几乎没有泄漏抑制能力。只有在严格相干采样的条件下才推荐用矩形窗,非相干采样时用它结果会很难看。
  • Hann(汉宁窗):旁瓣衰减明显,约-31dB,主瓣比矩形窗宽一倍,是FFT测ADC指标时最通用的折中方案。即使相干采样条件有一点点偏差,Hann窗也能把泄漏压住。
  • Blackman-Harris:旁瓣衰减更强,达到-92dB左右,适合对SFDR精度要求高的场景,但主瓣更宽,信号谱线附近的谐波可能被淹没,需要看实际频谱再选。

选的时候还有一个关键选项:Coherent Gain Correction。加窗后在时域上等于给每个采样点乘了一个小于1的系数,信号的平均幅度会被压低,如果直接拿FFT结果里的信号峰值功率去算SNR,会系统性偏小1到几个dB。勾选这个选项后,工具会按窗函数的相干增益把信号功率补偿回来。如果未来某天你自己写Matlab脚本处理,这一步很容易漏,漏掉的后果就是SNR偏低。

3.3 关于平均与重复仿真的一些说明

部分Spectrum版本里会有平均次数或者多帧处理选项。对纯瞬态仿真来说,我一般不启用平均。ADC硬件实测时,每次采集到的样本会叠加随机噪声,所以需要多次采集后平均来降低随机性;但Cadence瞬态仿真是确定性的,同样的激励和电路,跑出来的噪声分量每次都是一样的,平均没有任何收益,白白增加计算量。

另外需要注意,Spectrum工具计算FFT时默认会处理数据和DAC输出波形,不需要额外在仿真里设置strobeperiod。但要确保ViVA里选中的波形是DAC输出的模拟电平,而不是ADC的二进制总线,否则工具无法直接对它做频谱分析。

4. 从频谱到指标:SNR、SINAD、ENOB、SFDR的准确算法

4.1 从频谱里读出信号、噪声和失真

FFT做完之后,频谱图上有几个明显特征:0Hz位置的直流分量,信号频率处的一个高尖峰,以及分布在信号频率整数倍位置上的谐波尖峰(2次、3次、4次……)。其余那些贴着底部的小幅值点都是噪声。

SNR的严格定义是信号功率与总噪声功率之比,其中噪声功率是“除了直流、信号和谐波之外所有剩余bin的功率总和”。注意,谐波要排除掉,不能算进噪声里。谐波包含的是失真成分,如果把它算进SNR,得出的SNR会比真实值偏小。

Cadence Spectrum工具里,Signal-to-Noise Ratio测量项通常会自动剔除直流和指定次数的谐波(一般默认前5次或前9次,可视需要修改)。手动计算时,我建议把信号bin之外所有明显高于噪声底的尖峰先逐一标出来,再用工具自带的标记功能读取它们的幅度和功率。Spectrum里的Measurement会自动按FFT的bin宽度和窗函数把各成分功率折算成物理单位,省掉不少手动换算的工夫。

总噪声功率的计算方式,是把所有非信号、非直流、非谐波的bin功率求和。如果用了加窗,严格来说还要按窗函数等效噪声带宽做修正。Hann窗的等效噪声带宽约为1.5个bin,实际噪声总功率需要在“每bin功率×总bin数”的基础上除以1.5。Spectrum工具内部会处理这个修正,手动计算时别忽略。

4.2 ENOB的计算公式与真实含义

ENOB(Effective Number of Bits)是衡量ADC实际有效位数的最直观指标。公式是:

ENOB = (SINAD - 1.76) / 6.02

注意这里用的是SINAD而不是SNR。SINAD是信号与噪声加失真的比值,既包含噪声又包含谐波失真。ENOB表达的是:如果你的ADC失真和噪声综合水平相当于一个N位的理想ADC,那N就是这个ENOB。如果错误地用SNR去算ENOB,得到的结果一般会偏高,因为失真被排除在外了。

举个例子:某12位ADC仿真结果中SINAD=64dB,则ENOB=(64-1.76)/6.02≈10.34bit。此时SNR可能测到66dB,用SNR算出来是10.67bit,两者差了0.33bit,对于一本正经做设计评估来说这个差距已经足够影响判断了。

SINAD和SNR的dB值相差不大,尤其在高性能ADC中失真占比不高时,很多人习惯于直接用SNR代替SINAD。但严谨的动态指标报告里,ENOB必须由SINAD计算而来,这一点请务必记住。

4.3 单位、回退、满幅折算

在读频谱数值时,经常会遇到dBc和dBFS这两个单位。dBc是相对于信号载波功率的比值,SFDR、THD这些值常用dBc表示;dBFS是相对于ADC满幅输入的比值。当输入信号为-1dBFS时,信号功率本身比满幅低1dB,那么以信号为基准测得的SNR,如果想换算回满幅基准,需要加上这个回退值。

具体换算关系是:SNR(满幅基准)= SNR(信号基准)+ 输入回退量。比如输入-1dBFS时实测SNR=65dBc,则折算到满幅就是66dBFS(近似音)。在做芯片datasheet对比或设计指标验收时,一定要先确认单位基准是否一致,否则拿自己的dBc数据去对比别人datasheet的dBFS数据,会白吃一记闷亏。

SFDR的定义是信号功率与最大杂散分量的功率之比,最大杂散分量通常出现在某个谐波位置,但有时也会是分数谐波或电源耦合产生的尖峰。Spectrum工具可以直接读出SFDR,但建议你肉眼扫一遍频谱确认最大杂散的位置,防止工具自动标记跳过了某个真正的杂散峰。

5. 新手最常踩的五个坑位:现象、根因与修复

5.1 频谱底噪整体抬高,SNR明显偏低

现象:FFT出来的底噪不是一条平滑的低线,而是整体抬高了几十dB,信号峰虽然存在,但比噪声底高不了太多。

根因:绝大多数情况是FFT分析窗口的起点没选好,把tran仿真的启动暂态过程也包进来了。另一个常见原因是maxstep太大,DAC输出波形的阶梯状台阶在频谱上制造了额外噪声。

修复:把Spectrum分析窗口的起始时间往后移,等于几个时钟周期以后;同时重新检查tran的maxstep,按时钟周期的1/100以下设置。这两个改掉之后,底噪通常马上就降下来。

5.2 信号谱线旁边出现对称的“翅膀”

现象:信号尖峰两侧出现对称的若干小峰,看起来像一对快速衰减的“翅膀”,噪声底没有整体抬高,但信号能量好像被分走了一部分。

根因:这是典型的频谱泄漏。输入频率没有严格满足相干采样条件,即M不是整数。比如想要24.34MHz结果填了24.3MHz,FFT就会认为信号在窗口边缘不连续,能量向两边溢出。

修复:回到频谱前先验证Fin是否满足 Fin = M/N × Fs,M取质数。在理想信号源里把频率设置到最精确的小数位,不要自己截断。检查方法很简单:把tran仿真时间里的实际正弦波周期数数出来,如果跟M不一致就是频率设错了。

5.3 设置全对,但ENOB离理论值差好几个bit

现象:相干采样没问题、maxstep足够小、分析窗口也正确,但12bit ADC的仿真ENOB只能测到9bit左右,与理论值差距很大。

根因:这种时候往往不是Spectrum工具的问题,而是仿真环境本身不够干净。我之前遇到过一次,是给ADC供电的直流源上叠加了一个很小的噪声源,导致电源波动耦合进信号通路。另外,如果被测ADC旁边挂了数字信号发生器,数字毛刺通过衬底耦合也可能造成底噪异常。

修复:把电源上的交流噪声源先关掉,换成纯直流源验证一次。如果ENOB恢复,说明电源干扰是元凶。如果还不能解决,再检查信号源自身的THD,analogLib里的vsin也是存在失真参数的,某些设置下会往输入信号里引入额外谐波。理想信号源的THD默认在-100dB以下,一般不会限制ADC指标,但如果是自己写的带谐波模型,就要单独排查。

5.4 频谱上谐波不明显,但SINAD还是低

现象:底噪很低、信号峰正常、谐波也看不到几个,可SINAD和ENOB却不理想。

根因:很可能时钟源带了jitter模型。带jitter的时钟会让ADC的采样时刻存在随机偏移,等效于在采样值上叠加了比例噪声,尤其对高频输入信号影响显著。这种噪声不像谐波那样有固定的频率位置,而是均匀分布在噪声底上。

修复:确认采样时钟源是否带了jitter参数,先改成理想时钟(不带jitter)跑一遍,对比两次结果的差异。如果要单独估算时钟jitter对ADC动态性能的影响,那应该把时钟jitter作为独立的参数输入,而不是混在FFT指标里一起算,否则既搞不清电路本身的性能,也搞不清时钟的贡献。

5.5 对比不同结构时结果是跳变的,不稳定

现象:微调某个参数之后,比如改变输入信号频率或者采样点数,SNR结果突然变坏很多,再微调又恢复,看起来非常不稳定。

根因:大概率是换了M或N之后,相干采样的条件重新计算时没有配对好,或者分析窗口的起始时间没有跟随采样点数调整,导致FFT窗口内的信号周期数不再是整数。

修复:把M、N、Fs、Fin、分析窗口长度这几个参数做成一张对应表,每次改任何一项都要重新核算其余项。我在实际工作中就把这组数写在一个小的计算表里,输入Fs和N,自动算出满足条件的M和Fin,省掉很多手算失误。

6. 进阶经验:把FFT仿真结果做扎实

6.1 先用理想ADC校准测量链

之前反复提到理想ADC模型,这里再强调一遍它的价值。用理想ADC跑一遍同样的Spectrum流程,得到的结果可以作为测量链路本身的基准。比如12bit理想ADC的ENOB应该在11.7bit到11.9bit之间,加上理想DAC的转换精度损失,大约会比12bit略低一点点。如果你在理想ADC上测出来比这个低0.5bit以上,说明Spectrum参数或Testbench搭建肯定还有问题,这时不应该怪ADC设计,应该回头检查测量流程。

每次搭新的测试环境、换新的PDK或换新版本Cadence时,我都建议先跑理想ADC这条基准链路,花十分钟确认工具版本和库版本没有影响测量精度,然后再去仿真真实电路。这个习惯帮我排除过不少环境问题,比如某个PDK的adc模型默认带失调、某次Cadence更新后Spectrum工具的窗函数默认值变了等等。

6.2 双音测试算IMD

SNR/ENOB描述的是单音输入下的动态性能,但对混频器前的ADC或者通信链路中的ADC来说,双音互调失真(IMD)同样关键。做法是给ADC输入两个幅度相同、频率相邻的正弦波,例如f1=24.0MHz、f2=24.5MHz,然后在频谱上找2f1-f2和2f2-f1这两个三阶互调分量。

互调分量的功率与信号功率之比就是IMD3。Spectrum工具同样可以从频谱上读出这些尖峰的幅度,再手动计算。要注意双音输入时总信号幅度是单音的两倍,为了防止ADC饱和,每个音的幅度要比单音测试再回退约6dB,这是很多新手容易忽略的细节。

6.3 自己写脚本处理FFT时的注意事项

有些时候需要在批量仿真中自动提取动态指标,没法每次都在ViVA里手动点Measurement。这时可以用Cadence Ocean或直接导出波形数据后用外部脚本处理。

外部脚本处理时最大的坑就是之前提到的窗函数修正。自己写代码时,记得做这几件事:

  • 确认FFT之前去掉了直流分量,或者保留直流但在SNR计算时单独排除DC bin。
  • 如果加了窗函数,信号功率要除以窗函数的相干增益(如Hann窗是0.5),噪声总功率要除以窗函数的等效噪声带宽系数(Hann窗约1.5)。
  • 明确SNR计算时排除哪些谐波。一般至少排除前5次谐波,如果你的频谱里能看到第9次,也应一并排除。
  • 记得把频谱的单边谱和双边谱搞清楚,很多脚本处理出错都是因为功率没有乘2(单边谱只取正频部分,负频能量折叠到正频)。

这些细节在Cadence Spectrum工具里都是自动处理的,所以新手常感觉不到它们的存在;一旦切到脚本流程,就全部变成手动责任了。

6.4 和理论值对照验证

拿到仿真结果之后,别急着直接写进报告。先跟理论量化噪声下限做一次对比。满幅正弦输入下,理想ADC的量化SNR理论值是:

SNR量化 = 6.02 × N + 1.76(dB)

对于12bit ADC,这个值是73.9dB,SINAD大约在73.5dB左右。如果你的实际电路仿真结果能贴近这个值,说明电路本身的动态性能没被测量方法掩盖;如果差得远,要么是电路设计有瓶颈,要么是前面说的测量链路上还有漏掉的坑。

我个人的做法是每次仿真结束,先看一眼ENOB是否落在合理区间内,再做详细的频谱分析。这相当于给整个仿真流程加了一道快速质检,如果连理论值都差很远,就不要浪费时间细化分析了,先回去查Testbench和Spectrum参数。

最后再分享一个小经验:做ADC动态指标仿真前,花几分钟把M、N、Fin、Fs、分析窗口起点这五个量写清楚,再花两分钟确认一下maxstep,最后才启动仿真。看起来多花了几分钟准备,实际上能帮你省掉后面好几个小时的反复确认和返工。Cadence的Spectrum工具能力很完整,但它不会告诉你参数该填什么,也不会主动提示你相干采样条件没满足。工具是死参数是活的,把这套逻辑想通,FFT动态仿真对你来说就真的只是一次普通仿真了。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询