☰
3阶连续时间Delta-Sigma ADC的Matlab仿真:先稳定环路,再优化SNR
2026/10/15 16:12:49 网站建设 项目流程

简介:面向模拟集成电路与数据转换器设计人员,这份代码提供了一套三阶连续时间 ΔΣ 模数转换器(CTSDM)的 Matlab 仿真方案,用于理解高阶噪声整形机理、验证系统稳定性和量化噪声分布,可作为相关课题研究与课程设计的起点。压缩包共 10 个文件,包含 3 个 m 脚本(主程序、FFT 分析、动态测试)、1 个 Simulink 模型、1 个 Excel 计算表格、1 份说明文档、1 张操作示意图以及 3 篇配套参考论文,整体大小约 3.14MB,目录结构清晰,便于按功能模块查阅。目前已有 959 人浏览学习。代码覆盖时序仿真、频域分析和功率谱计算,能在 400 倍过采样率与 3 位量化条件下直接运行并观察行为级结果;结合论文与操作说明,可帮助读者掌握连续时间 Sigma-Delta 调制器的建模仿真与性能评估方法,并在此基础上扩展自己的设计验证。

1. 3阶连续时间Delta-Sigma ADC的Matlab仿真:先把环路跑稳,再谈精度

做 3 阶连续时间 Delta-Sigma ADC(CT-ΔΣ ADC)的人,绝大多数时间不是在调电路,而是在和 Matlab 搏斗。这个标题对应的需求很具体:设计一个 3 阶连续时间 Delta-Sigma 调制器,在流片前用 Matlab 验证环路稳定性、噪声整形和信噪比(SNR),并且这套仿真代码要能改参数、能复现、能讲清楚每一步在干什么。连续时间环路的难点在于它不能像离散时间 Delta-Sigma 那样直接照搬 z 域系数,要处理积分器状态、DAC 反馈波形和额外环路延迟(ELD)这几个在普通离散仿真里看不见的问题。适合读这篇笔记的人,是那种论文看了好几篇、手上却没有一份能跑通的 CT DSM 仿真代码的开发者,或者已经在用 Simulink 但想回到 m 脚本层面把每一个环节都拆开的工程师。这篇文章就从一份可运行的最小 Matlab 脚本出发,讲结构、系数、仿真、算出 SNR,再讲五个绕不开的坑。

2. 3阶CT DSM的环路原理与拓扑取舍:CIFF怎么选、系数怎么来

2.1 三阶环路的噪声整形本质:为什么是三个积分器

任何 Delta-Sigma 调制器都在做同一件事:把量化误差在低频推远。3 阶意味着环路滤波器里有三个积分器,理想情况下噪声传递函数(NTF)在直流附近有三个零点,也就是说量化噪声在低频被压制了三个数量级的斜率。每多一阶,带内噪声功率谱密度按约 6.02 dB/倍频程 更快地滚降,代价是环路更容易不稳定,这也是 3 阶成为很多中精度应用优选的原因:2 阶不够猛,4 阶和 5 阶在单比特量化器下稳定难度陡增。

在连续时间实现里,三个积分器的传输函数分别是 1/s、1/s²、1/s³,它们的系数不是随便给的。a1、a2、a3 决定每个积分器输出对量化器输入的权重,也直接决定 NTF 的极点位置和噪声成形的激进程度。常见做法是先设计一个离散域的 NTF,再通过等效变换算回连续时间系数。设计 NTF 时有一个逃不开的约束:NTF 的峰值增益不能太高。单比特 3 阶环路里,峰值通常压在 1.4 到 1.6 之间(Lee 准则)。峰值太高,输入幅度稍微大一点,量化器就会饱和,环路进入长串同号输出的不稳定状态;峰值太低,噪声整形效果又不够。

2.2 CIFF 还是 CIFB:三种结构里的常见选择

3 阶连续时间 DSM 的拓扑很多,最常见的是 CIFF(分布前馈)和 CIFB(分布反馈),还有 SIFF(积分器间前馈)等变体。我一般先选 CIFF,因为它能用更小的积分器输出摆幅,对运放摆幅要求相对宽松,在低电源电压场景下更实用。CIFF 的信号路径是:输入直接进第一级积分器,第二级积分第一级输出,第三级积分第二级输出,最后把三级积分器输出和输入信号一起送到量化器。反馈 DAC 通常只在第一级求和节点注入。

CIFB 则把反馈 DAC 分别注入每一级积分器输入,噪声整形效果类似,但内部状态幅度更大,对积分器线性度要求更高。从 Matlab 仿真角度看,这两种结构都能跑,区别主要在系数更新规则上。CIFF 的结构里,量化的输入是 v = b1u + a1x1 + a2x2 + a3x3,其中 b1 是输入直通系数,这个直通项能压低 STF(信号传递函数)在带外的峰值。没有 b1 的 CIFF 也能工作,只是 STF 高频会有明显隆起,实际电路里容易放大带外干扰。

2.3 连续到离散的等效:用 c2d 先做一次稳定初筛

拿到一组 a1、a2、a3 系数后,我不会直接跑行为级仿真,而是先做连续环路到离散等效的初筛。这里的坑在于:连续时间 DSM 的 DAC 输出在一个采样周期内是保持电平(NRZ 脉冲),它的反馈路径不能简单地用双线性变换映射,更接近的做法是以零阶保持(ZOH)方式离散化前向通路,然后检查等效离散闭环极点是否都在单位圆内。

% 系数来自设计初值,归一化 fs=1 a1 = 0.44; a2 = 0.30; a3 = 0.16; b1 = 1.0; Kdac = 1.0; s = zpk('s'); fs = 1; % 先按归一化频率检查 % 连续时间环路滤波器 L(s):从DAC反馈端到量化器输入 L = Kdac * (a1/s + a2/s^2 + a3/s^3); % 用ZOH离散化作为工程近似,检查闭环稳定性 Ts = 1; Ld = c2d(L, Ts, 'zoh'); cl_sys = feedback(Ld, 1); p = pole(cl_sys); if max(abs(p)) < 1 fprintf('离散等效稳定,极点模最大值 = %.3f\n', max(abs(p))); else fprintf('离散等效不稳定,请调整a1/a2/a3\n'); end

这段代码里的 c2d 只是初筛,不是精确映射。精确映射要考虑 DAC 的 NRZ 脉冲响应在采样点的“尾部”效应,这通常需要 modified z-transform 一类的工具,普通 zoh 近似在高 OSR 下误差不大,但 OSR 低于 16 时可能会给你一个“看着稳定、实际振荡”的错误结论。所以我把它的定位说清楚:用来排除明显错误的系数组合,真正的判决要交给第 3 章的行为级仿真。

3. 在Matlab里把3阶CT DSM跑出比特流:行为级仿真与SNR计算脚本

3.1 行为级仿真脚本:连续积分器 + 采样量化 + NRZ反馈

这一节的代码是一份可以直接复制运行的完整脚本。它不依赖 Simulink,只用 m 脚本,把连续时间积分器用固定步长积分近似,在每一个采样周期末尾做量化判决,量化结果以 NRZ 电平反馈到下一个周期。这是连续时间 DSM 行为级仿真里最常用的做法,适合验证系数和算法,跑一次几千点只需要几秒。

clear; clc; % ---------- 基本参数 ---------- fs = 2e6; % 采样率 2 MSPS Ts = 1/fs; N = 2^12; % 总仿真点数 4096 OSR = 64; % 过采样率 fb = fs/(2*OSR); % 信号带宽 15.625 kHz M = 7; % 输入频率的FFT bin序号,取质数 fin = M*fs/N; % 约 3.418 kHz,与FFT相干 Ain = 0.5; % 输入幅度,相对满摆幅留裕量 % ---------- CIFF 环路系数 ---------- b1 = 1.0; % 输入直通系数 a1 = 0.44; % 第一级积分器前馈系数 a2 = 0.30; % 第二级积分器前馈系数 a3 = 0.16; % 第三级积分器前馈系数 Kdac = 1.0; % 反馈DAC增益 % ---------- 行为级仿真 ---------- sps = 50; % 每个采样周期内积分步数 h = Ts/sps; % 积分步长 x1 = 0; x2 = 0; x3 = 0; % 积分器状态 y = zeros(1, N); % 量化比特流 dac = 0; % 当前周期的DAC输出电平 for ns = 1:N for j = 1:sps tcur = (ns-1)*Ts + j*h; u_cur = Ain * sin(2*pi*fin*tcur); % 连续时间积分器微分方程,fs是积分器单位增益频率 dx1 = fs * (b1*u_cur - dac); dx2 = fs * x1; dx3 = fs * x2; % 前向欧拉积分,步长 = Ts/50,足够小 x1 = x1 + h*dx1; x2 = x2 + h*dx2; x3 = x3 + h*dx3; end % 采样时刻量化器输入 v = a1*x1 + a2*x2 + a3*x3 + b1*u_cur; % 1-bit量化 if v >= 0 y(ns) = 1; else y(ns) = -1; end % NRZ DAC:下一周期保持该电平 dac = Kdac * y(ns); end

这段代码里有三个关键设计。第一,积分器方程里乘了 fs,这是为了让积分器增益归一化到采样频率,否则 x1 的数值会小到让前馈系数完全失效。第二,量化判决发生在内层循环结束后,也就是每个采样周期的边界,DAC 电平在下一个周期才生效,这本身就隐含了一个周期的环路延迟,接近真实电路的行为。第三,u_cur 用的是当前周期的最后一个小步,代表采样时刻的输入值,这样输入信号是连续的,而不是只在采样点更新。

如果你把这段代码原样跑一遍,会发现一个问题:量化器输入 v 的幅度可能偏大或偏小,导致输出比特流里出现长串 -1。这是系数和输入幅度不匹配的典型表现。我一般先观察 v 的波形,如果 v 长期超过 ±2,就把 a1/a2/a3 整体缩小 20% 再跑,直到 v 的分布接近量化器阈值附近。

3.2 从比特流算出SNR:相干采样、FFT与带内噪声

比特流出来以后,下一步是算 SNR。这里有个原则性问题:测 Delta-Sigma 的 SNR 时,输入信号必须和 FFT 相干,也就是输入频率要正好落在某一个 FFT bin 上。上面的代码里 fin = M*fs/N,M 取 7,这就是相干采样。不相干的话,频谱泄漏会把噪声底抬高十几个 dB,算出来的 SNR 是错的。

% ---------- SNR 计算 ---------- Y = fft(y); P_double = abs(Y/N).^2; % 双边功率谱 P_single = P_double(1:N/2+1); % 取单边 P_single(2:end-1) = 2*P_single(2:end-1); % 补上镜像功率 fbin = M; % 信号所在bin nbins = N/(2*OSR); % 带内bin总数 = 32 % 信号点及其泄漏边带,计算带内噪声时挖掉 sig_band = max(1, fbin-5):min(nbins, fbin+5); noise_band = setdiff(2:nbins, sig_band); signal_pow = sum(P_single(sig_band)); noise_pow = sum(P_single(noise_band)); SNR_dB = 10*log10(signal_pow / noise_pow); fprintf('SNDR = %.1f dB (带内, 挖去信号边带)\n', SNR_dB);

这里有几个细节值得说。第一,P_single(2:end-1) 乘以 2 是为了把负频率的功率合回单边谱,DC 和 Nyquist 不乘。第二,我没有用 Hanning 窗,因为已经做了相干采样,矩形窗反而不会引入窗函数本身的噪声带宽修正问题。如果你非要加窗,必须修正 ENBW,否则 SNR 会被低估。第三,噪声带宽只算到 nbins = 32,也就是 OSR=64 对应的带宽,超过这个范围的高频整形噪声不算,这是 Delta-Sigma 测量 SNR 的通行做法。

3.3 结果判读:什么样的SNR算正常

3 阶单比特、OSR=64、输入幅度 0.5 的配置,理想情况下 SNR 应该落在 85 dB 到 100 dB 之间,具体取决于系数的激进程度。如果你的结果低于 70 dB,先不要怀疑仿真代码,按这个顺序排查:一是看 v 的分布,二是看 y 里有没有长串同号输出,三是在第 3.1 节里把 h 调小一个量级再跑一次。很多所谓“仿真结果不对”,其实是前向欧拉的步长偏大导致积分器链轻微数值发散,步长减半就能解决。

把这段脚本跑通之后,你就有了一个可以反复改参数的平台。接下来要考虑的就是参数怎么挑,而不是代码怎么跑。

4. 把参数调到能投片:OSR、积分器系数、量化器位数与仿真点数

4.1 OSR 与带宽的取舍:仿真之前先算清楚

OSR(过采样率)是整个设计的起点,它不是拍脑袋定的。3 阶单比特的理想 SQNR 近似正比于 OSR 的 7 次方,也就是 OSR 每翻一倍,SQNR 约提升 21 dB。这个数字听起来很诱人,但 OSR 提高意味着采样率提高,连续时间积分器的带宽、运放功耗和 DAC 时钟要求同步变高。把 OSR 从 64 提到 128,SNR 多 20 多 dB,代价是采样率和功耗翻倍,很多场景并不划算。

反过来,OSR 太低也有问题。当 OSR 低于 16 时,连续时间到离散的等效误差会变大,DAC 脉冲形状、ELD 的影响都会更突出。我一般把 OSR=32 或 64 作为起始点,先跑通,再根据 SNR 余量调低。在 Matlab 仿真里改 OSR 很容易,但注意 N 要重新选,让 N/(2*OSR) 是整数,否则带内噪声 bin 数会得到一个小数,频谱计算就开始别扭了。

4.2 积分器系数的归一化规则与常用范围

a1、a2、a3 这三个前馈系数不是随便拍出来的。它们对应 NTF 极点位置,而极点位置又由稳定裕量约束。常见做法是:先用理想离散 NTF 设计工具算出系数,再映射回连续时间。如果手里没有工具,可以用行为级仿真的方式去调,我习惯把系数限制在一个经验范围里,然后观察 v 的分布和输出比特流的稳定性。

参数归一化 fs=1 时的常用范围主要作用调大的典型后果
a10.3 ~ 0.6第一级前馈,决定 NTF 低频深度噪声整形更激进,稳定性变差
a20.2 ~ 0.4第二级前馈,影响中频整形带宽内出现振铃
a30.1 ~ 0.3第三级前馈,影响高频段带外噪声峰抬高
b10.8 ~ 1.2输入直通,压 STF 峰值影响信号带内平坦度
Kdac0.9 ~ 1.1反馈深度,等于闭环增益偏离 1 会引入增益误差

这个表是行为级仿真的经验值,不是某个固定公式的结论。需要注意,a1 的绝对值在连续时间 DSM 里比离散 DSM 小得多,因为积分器增益已经乘了 fs,直接搬离散系数会让环路看起来非常不稳定。这也是第 5 章要讲的坑之一。

4.3 量化器位数:单比特的失真优势与多比特的现实

单比特量化器是最省事的:DAC 只有两个电平,天然单调,不存在失配问题。3 阶单比特是经典组合,稳定裕量充足,非常适合先跑通流程。但单比特的量化噪声本身很大,要达到高 SNR 只能靠高 OSR,这会推高采样率。

如果想在 OSR=32 时拿到 90 dB 以上的 SNDR,通常要上 3 比特或 4 比特量化器。量化器位数每增加 1 比特,理想 SNR 提升约 6 dB,但反馈 DAC 要变成多电平,DAC 单元失配会直接把谐波或本底噪声带进带内。仿真代码里改量化器位数很容易:把 sign(v) 换成多电平量化函数,再把 dac 的反馈电平改成对应电平值。但要注意,多比特时整个环路的稳定裕量比单比特好很多,a1 可以调得更大,NTF 峰值也允许到 1.8 甚至 2.0 以上,这是很多论文设计里单比特和多比特系数不能直接互换的原因。

4.4 仿真点数和相干采样参数怎么定

MAC 仿真里的 N 不是越大越好。N 每次翻倍,仿真时间翻倍,带内 bin 数也翻倍,但 SNR 测出来会更稳定。对于 3 阶 OSR=64,N=4096 时带内只有 32 个 bin,扣掉信号边带后噪声统计点只剩 20 个左右,足够判断系数是否合理。要做最终验证,我会跑到 N=32768,那时带内有 256 个 bin,噪声底的波动会明显变小。

相干采样的 M 要选质数,并且保证 fin 落在带内、不要排在带内太边缘。M=7 是常用起始值。M 太大会让信号靠近带内边缘,测量带宽内的噪声绕过信号边带后剩余 bin 太少;M 太小,比如 M=1,信号频率太低,对 NTF 在带内的形状验证不够充分。仿真步长 sps 保持 50 左右即可,这相当于在每个采样周期内给积分器 50 个小步,对 3 阶积分器链足够精确。如果你发现输出比特流对 sps 特别敏感,那不是步长问题,而是环路本身已经处于不稳定的边缘。

5. 3阶CT DSM仿真避坑:5个踩坑记录与排查对照

5.1 SNR 偏低却查不出问题:先确认输入频率相干

现象:同一套系数,SNR 每次算出来都差十几 dB,仔细看比特流波形也正常,频谱上信号附近有一片明显的“裙边”。

原因:输入频率没有落在 FFT bin 中心,频域泄漏把信号能量散布到了相邻 bin,而这些 bin 又被当作噪声扣除,于是信号功率被低估、噪声功率被高估。

解决:使用 fin = Mfs/N,M 取质数,并检查 N 能否被 2OSR 整除。这是做 Delta-Sigma 仿真时最常见的翻车点,和电路设计本身没有关系,纯粹是测量方法错了。很多人在这一步怀疑自己的环路有问题,把系数改来改去,实际上只是相干采样没做对。

5.2 同一套系数两次仿真结果不一致:求解器设置成了黑匣子

现象:用 ode45 跑行为级仿真,reltol 从 1e-3 改成 1e-6,SNR 变了 3 dB,甚至两次运行结果都有微小差别。

原因:变步长求解器对量化器的开关时刻捕捉不精确,积分器状态在判决边界附近的微小误差会被环路本身的高增益放大,等效于注入了一个随机抖动。

解决:对于带硬非线性(量化器)的系统,固定步长求解器反而更可靠。我上面的代码用 h = Ts/50 的前向欧拉,几乎不会出现这个问题。如果你一定要用 ode45,必须设置 MaxStep = Ts/100,并且把输出点固定在采样时刻,不要依赖它自动选点。

5.3 从论文复制系数到连续时间仿真直接炸:映射关系没做

现象:按论文里的离散 NTF 系数写进连续积分器实现,环路输出直接发散,或者 SNR 只有十几 dB。

原因:离散 Delta-Sigma 的系数是针对 z 域积分器(即 z/(z-1))设计的,而连续时间环路里积分器是 1/s,两者必须通过 DAC 脉冲响应做等效映射。直接搬系数等于把 z 域的极点位置错误地搬到了 s 域。

解决:先做第 2.3 节的 c2d 初筛,确认离散等效闭环稳定,再跑行为级仿真。如果论文只给了离散 NTF,没有给连续系数,需要用 modified z-transform 或者 NRZ DAC 等效模型反推连续积分器系数,这一步不能跳过。

5.4 输出频谱高频抬升:没把额外环路延迟 ELD 算进去

现象:带内 SNR 看起来还行,但频谱在 fs/2 附近出现明显鼓包,跑长仿真时偶发不稳定。

原因:量化器和数字逻辑的传输延迟使反馈 DAC 输出比理想模型晚了一部分,这个延迟在高频段给环路增加相位滞后,等效于把 NTF 高频增益抬了起来。真实电路里几乎一定有 ELD。

解决:在行为级仿真里显式建模延迟,最粗略的做法是把 dac 更新再延后一个采样周期,或者加入 ELD 补偿路径。补偿的做法是在反馈路径上增加一个和 y(ns)-y(ns-1) 成正比的修正项,模拟“额外的快速反馈路径”。先建模、后补偿,这是连续时间 DSM 仿真里最需要花时间校准的地方。

5.5 长仿真跑不动或者内存暴涨:循环结构问题,不是电脑问题

现象:把 N 从 4096 改成 65536,仿真时间不是涨 16 倍,而是涨得更离谱,或者 Matlab 内存占用异常。

原因:我上面的代码已经做了状态变量标量化和预分配,这是最经济的写法。如果你在循环里 append 输出,或者在内层循环里动态增长数组,Matlab 会反复复制数组,O(N²) 复杂度。

解决:所有状态变量用标量,输出数组预分配;内层循环里不要写任何动态数组操作。如果 N 确实需要很大,考虑把行为级仿真改成 Simulink 模型或者转成 C-MEX,m 脚本循环在十万量级内还能忍,再往上就是浪费时间。

6. 把仿真结果往芯片推近一步:时钟抖动、热噪声与扫幅值习惯

行为级仿真跑通之后,下一步是给模型加非理想效应。我一般先加时钟抖动,因为它是连续时间 DSM 的天然弱点:采样时钟的随机抖动直接调制 DAC 脉冲边沿,等效于在反馈路径注入误差,带内 SNR 会随抖动幅度按明确规律恶化。快速估计公式是 SNR_jit ≈ 20·log10(1/(2π·fin·σ_jit)),但这是对输入采样的估计,对 DAC 边沿抖动还要乘一个和反馈电平有关的系数,实际值通常比公式再差几 dB。

% 时钟抖动对输入采样路径的影响(快速评估,不依赖工具箱) jit_rms = 50e-12; % 50 ps rms 时钟抖动 t_ideal = (0:N-1)*Ts; t_jit = t_ideal + jit_rms * randn(1, N); u_ideal = Ain * sin(2*pi*fin*t_ideal); u_jit = Ain * sin(2*pi*fin*t_jit); err = u_jit - u_ideal; jit_noise_pow = sum(err.^2) / N; signal_pow = sum(u_ideal.^2) / N; SNR_jit_dB = 10*log10(signal_pow / jit_noise_pow); fprintf('时钟抖动引起的SNR上限 = %.1f dB\n', SNR_jit_dB);

这段估算的意义不是精确预测芯片 SNR,而是判断抖动预算是否合理。如果算出来的 SNR 上限比目标低,那不管环路系数调得多好都没用,要么换更低抖动的时钟源,要么在架构上改用多比特量化来降低反馈电平对边沿抖动的敏感度。这是我在做这个方向时最大的教训之一:很多人把时间花在调 a1、a2、a3 上,却忘了抖动和 DAC 失配才是限制最终性能的墙。

最后说一个我养成的习惯:任何系数组合跑稳以后,我都会再跑一遍输入幅度扫描,从 -12 dBFS 以 1 dB 步进扫到 0 dBFS,画出 SNDR 随幅度变化的曲线。Delta-Sigma 调制器有一个明显的“过载点”,过载之前 SNDR 近似线性,过载之后骤降十几 dB。这条曲线的位置能直接告诉你这组系数的稳定裕量够不够。如果你扫出来的峰值出现在 -1 dBFS 以内,说明系数太激进,流片后温度变化或工艺偏差很容易让它失效。把这个习惯养成之后,再看别人给的系数,你会先问一句:这组系数的过载点在哪儿?希望上面这些步骤和踩坑记录能帮到你少走一段我走过的弯路。

本文还有配套的精品资源,点击获取

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

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

立即咨询