Administrator
发布于 2026-10-09 / 1 阅读
0
0

8-bit 电压型 R-2R DAC 的戴维南建模:电阻失配的 INL/DNL 闭式理论与 MATLAB 逐码对账,以及开关 Ron 预算

8-bit 电压型 R-2R 梯形 DAC 是最经典的 DAC 结构:只靠两种阻值(、)就能得到精确的二进制权重,输出阻抗还与码无关。但"精确"只存在于阻值完美匹配的想象里。本文做三件事:(1) 用戴维南定理把 8 bit 梯形网络写成一条可解析的递推链,给出理想传输的闭式解并与节点法逐码对账;(2) 对每只电阻独立、 的失配做严格的一阶扰动推导,得到 、 的闭式表达,再用精确梯形的 Monte-Carlo 逐码对账—— 时每个码的标准差偏差 ,与 MC 自身统计误差()同量级,即"公式 = 模型";(3) 把开关导通电阻 作为确定性误差源,解析地说明"为什么它几乎不改变增益,却能把 的步长吃掉 40%",并给出**()** 的预算结论。全部代码在 MATLAB / GNU Octave 下实测通过。


目录


0. 结论速览

以 、、每只电阻独立且 为基准:

  1. 理想梯形的传输函数是严格精确的:,输出阻抗 (与码无关)。与 8 未知量节点法(MNA)逐码对账,最大偏差 ——戴维南递推没有引入任何近似,它只是"从 LSB 端往上折叠"的等价变换。
  2. 失配的一阶理论:输出误差是 16 个独立失配源的线性组合,,其中 可用一条闭式灵敏度递推给出(§2.4)。该递推与复步长微分(machine-precision 基准)比对误差 ,且自动满足 (纯分压网络的尺度不变性)。
  3. 两条峰值结论:
    即 分别为 与 。DNL 只在进位码 处出现尖峰(其余码 ),峰高按 递减。
  4. 理论 = 模型(数值级别的对应): 精确梯形 MC 给出的 、,与闭式理论相差 ;把一阶线性模型单独跑 MC,偏差 ,说明公式本身无推导错误,而 非线性残差 (MC 均值偏移仅 )。
  5. 预算(回答文末提问):匹配的开关电阻在每条 支路上串联,
    精确求根得
    时即 (若指标是 ,则为 )。
  6. 一个反直觉但正确的现象:这个 引起的 与增益校准无关——阈值处增益误差只有 (),但 的步长只剩 。原因是 把权重从 MSB 挪向低位( 掉 ,低 7 位之和涨 ,全量程几乎不变)。这类误差只能靠设计( 预算)解决,不能靠两点校准。
图 1 8-bit 电压型 R-2R 梯形网络。 端有一个  接地终端电阻,每个节点各挂一条  支路经开关接到  或 ,输出取在最高位节点 (高阻负载)。每一级"向上折叠
图 1 8-bit 电压型 R-2R 梯形网络。 端有一个 接地终端电阻,每个节点各挂一条 支路经开关接到 或 ,输出取在最高位节点 (高阻负载)。每一级"向上折叠"时,两支路阻抗恒为 ,因此比特电压每级减半、输出阻抗恒为 ——这就是戴维南递推的全部秘密。

1. 拓扑与戴维南递推(精确模型)

1.1 电路定义与假设

节点 自 LSB 端向输出端排列;串联电阻 连接 与 ();每个节点 有一条支路 (含开关 )接到比特电压

末端 另有一个 终端电阻 接地。输出 接高阻负载(比较器 / ADC 输入 / 缓冲器)。

假设(后面每一条都会在 §6 里讨论其影响):开关在所有位、两个位置上的 相同;各电阻的失配独立同分布、;忽略寄生电容、漏电、自热与温漂;输出负载阻抗 。

1.2 戴维南递推(精确)

把 左侧的全部网络(含终端电阻)折成一个戴维南等效 ,在节点 处把两条支路并联,即得上一级等效:

起始条件(节点 ,两条支路是"比特支路"与"终端"):

输出即 (高阻负载,无分压),。

1.3 理想解与对账

令 、、,递推退化为每级"两支路各 "的等分:

于是

数值校验(MATLAB/Octave 脚本 §1 段):;与独立编写的 8 未知量节点法结果逐码比对 ; 对全部 256 个码成立。理想 INL = DNL = 0(严格)。

图 2 闭式理论(实线)与  精确梯形 Monte-Carlo(红圈)逐码对比。(a)  是"帐篷形",峰值在  处 ,两端严格为 0(定义使然);(b)  只
图 2 闭式理论(实线)与 精确梯形 Monte-Carlo(红圈)逐码对比。(a) 是"帐篷形",峰值在 处 ,两端严格为 0(定义使然);(b) 只在进位码 处出现尖峰( @ ),其余码几乎为零——这是 R-2R 失配误差的指纹。红圈与理论曲线完全重合(偏差 )。

2. 电阻失配的一阶理论(严格推导)

2.1 无量纲化

设所有电阻只发生相对偏差,且偏差量级 :

共个独立随机变量

把递推的解写成"理想值 + 一阶偏差":

其中 是理想解(§1.3),而 对 是一阶小量。

2.2 递推的线性化

节点 (支路: 接 ;终端 接 0):把 ( 归一化终端)在 处展开:

节点 ():把

的分母按 展开,并利用 ,得到

这两个式子就是全部理论的核心,物理上可读:

  • :向下看的上游阻抗相对误差,是一个 衰减的记忆链(,见 §5.2);
  • 第 1 项:本节点的比特电压被"往上搬"时的分压误差;
  • 第 2 项:本节点两分支阻抗不等造成的增益误差(理想时两支路严格相等,这一项才消失)。

2.3 输出误差与 log 灵敏度

输出误差 ( 上面没有分压,直接就是 )。定义

因为 对 是线性的(一阶理论), 可以把 §2.2 的递推对每个参数各传播一遍(把 换成单位向量),16 条通道同时递推即可。于是

2.4 INL / DNL 的定义与闭式

口径取工程上最常用的一种:先用实测两端点定标(去掉增益误差与失调误差),再以实测平均步长为 1 LSB:

代入 ,一阶展开( 已经在分母里出现过一次,展开到一阶):

由于 是独立零均值随机变量的线性组合,立刻得到逐码标准差:

并且 INL/DNL 服从联合高斯(线性组合),所以 、良率、max-over-codes 的分布都可以直接算(§3.3)。

口径提示:若不做端点定标、直接用理想 LSB 作分母,只需把 ,于是 。两种口径在本例中差 ( vs )——报数时一定要写清楚是哪一种,否则无法复现。

2.5 一阶近似的误差界

二阶项的相对量级是 ,再乘一个 的几何因子,绝对量级 (1 LSB) 。这与 MC 实测的均值偏移()完全一致。结论: 的失配下,一阶理论就是"精确解"到 以内。

两条机器精度级自检(脚本 §2 段自动执行):

检查项 数值 含义
解析 vs 复步长微分() 闭式灵敏度公式无代数错误
(精确 0) 纯分压网络的尺度不变性(整体缩放不改变分压比)

3. 关键结果: 曲线与"谁最敏感"

3.1 、 的峰值与形状

量 峰值 位置 形状
(端点口径) 帐篷形: 处严格 0, 处折点
(理想 LSB 口径) 单调升到中点再降
(端点口径) 只在进位码尖峰,其余

在进位码 处的值(这就是"R-2R 失配指纹"):

2 4 8 16 32 64 128
[LSB] 0.001394 0.002668 0.005377 0.010886 0.021952 0.044103 0.088395

( 内吻合到几个百分点)。每增加一位, 翻一倍——这就是 R-2R 在高位数下越来越难做的根本原因。

3.2 单只电阻的灵敏度排序( 的 DNL)

,单位"LSB / 单位相对误差":

参数 (MSB 的 ) (- 串联 ) 其余 9 项
−127.50 +63.75 +32.38 +15.69 +8.35 +3.67 +2.34
  • 方差贡献(RSS):,其中 三项就占 99.22%,前五项占 99.95%。
  • 物理直觉:最高位支路的阻值误差直接进入 MSB 权重(/单位相对误差),只被分压比折半;越靠 LSB 的电阻越"被平均掉"。
  • 设计含义:版图对称(common-centroid)、dummy、trim 的资源应该优先砸在靠近输出的 2~3 个 支路和 附近的串联 ;LSB 侧的电阻只要满足绝对精度(时间常数、功耗)即可,匹配可以放松。
  • 注意符号: 为负、 为正——同一个进位处,不同位置的电阻误差方向相反,这就是为什么**"均匀偏移"()的总效应()小于随机失配的 RSS(147.33)**。这两个数后面都会用到。
图 3 单只电阻的失配灵敏度 ()。 时最高位两支路()与  三项即占全部方差的 99%。注意"低位电阻几乎不贡献 DNL"——但它们仍决定  那些低位步长的精
图 3 单只电阻的失配灵敏度 ()。 时最高位两支路()与 三项即占全部方差的 99%。注意"低位电阻几乎不贡献 DNL"——但它们仍决定 那些低位步长的精度。

3.3 芯片级极值分布

单只芯片的 、 是 256 个强相关高斯的极值。 次 MC 统计:

量 均值 99.7 百分位 与 的关系
✓
✓

即**""和"99.7% 分位的芯片级极值"几乎相等**(因为 DNL 的极值就是 那一个点,没有多码搜索带来的极值抬升)。做良率估计时这一点很有用: 就是 P99.7。


4. MATLAB/Octave 实现与 Monte-Carlo 对账

4.1 Monte-Carlo 的建模方式

对每个 trial:抽取 16 个独立高斯相对偏差 生成 用 §1.2 的精确递推逐码求 (不是线性化模型!) 按 §2.4 的定义算 INL/DNL 统计每个码的均值与标准差。

4.2 对账表(这是本文的验收标准)

一次 的 Octave 实跑(脚本自带,统计误差 ):

     D    sigINL_th    sigINL_mc    dev%     sigDNL_th    sigDNL_mc    dev%
     1   0.000896    0.000897   +0.092   0.000896    0.000897   +0.092
    32   0.019421    0.019466   +0.232   0.021952    0.021975   +0.105
    64   0.033263    0.033127   -0.411   0.044103    0.044056   -0.105
    96   0.037941    0.037804   -0.361   0.021952    0.021975   +0.105
   127   0.044198    0.044085   -0.256   0.000896    0.000897   +0.092
   128   0.044198    0.044085   -0.256   0.088395    0.088169   -0.256
   192   0.029031    0.029066   +0.121   0.044103    0.044056   -0.105
   255   0.000000    0.000000      --     0.000896    0.000897   +0.092

再把 N 拉到 (统计误差 )并把模型拆成"一阶线性模型 vs 精确梯形模型":

模型 相对闭式 相对闭式
闭式理论 0.044198 — 0.088395 —
一阶线性模型 MC () 0.044181 −0.037% 0.088362 −0.037%
精确梯形 MC () 0.044184 −0.031% 0.088368 −0.031%

怎么读这张表:

  1. 线性模型 MC 与闭式公式的最大偏差 统计误差 ⇒ 的代数推导正确(线性模型的解析解就是我给的两条公式)。
  2. 精确模型与闭式的偏差 ( 全部码)⇒ 的非线性残差远小于统计噪声,即"一阶理论 = 精确模型"。按偏差符号看,精确模型的 比一阶理论略小 倍,与 量级一致。
  3. MC 的均值偏移:、——这就是全部的系统性偏差(二阶效应),比 1 LSB 小 4 个数量级。
图 4 失配引起的 INL/DNL 分布(Monte-Carlo 直方图)与理论高斯  的叠合。三者(、、)都严丝合缝;注意 DNL@128 的  正好是 DNL@64 的两倍,与
图 4 失配引起的 INL/DNL 分布(Monte-Carlo 直方图)与理论高斯 的叠合。三者(、、)都严丝合缝;注意 DNL@128 的 正好是 DNL@64 的两倍,与 §3.1 的进位规律一致。

4.3 完整 MATLAB 脚本

见 §8 复现(单文件、无工具箱依赖,MATLAB 与 GNU Octave 都能直接 dac_r2r_8bit 运行)。


5. 开关导通电阻 的预算分析

5.1 建模

每个节点的开关( 侧 / GND 侧)都串在 支路上,于是 ,,各支路相同(匹配、与码无关)。这是一个确定性误差,不是随机误差。

5.2 解析:为什么"几乎不改增益"却毁掉

(a) 上游阻抗记忆链。 由 递推():,不动点 。也就是说 MSB 节点向下的支路阻抗是 ,而向上接 的支路是 ——两支路不再相等:

(b) 于是 MSB 权重被压低。

(c) 低 7 位权重之和被抬高(它们共享那条更"软"的上游路径):实测

理想

(d) 两步相加就是 DNL:

校准掉:

与数值扫描拟合的 完全一致(差 来自端点校准项)。这就是"增益误差只有 ,而 的步长只剩 "的全部原因:误差被搬到了权重比例上,而不是总增益上。

图 5 (a) 全量程传输曲线看上去完美(与理想直线重合到像素级),只有放大到进位处才看见  的台阶只剩 ;(b) 逐码误差: 造成的误差在中点两侧各  的"台阶&quo
图 5 (a) 全量程传输曲线看上去完美(与理想直线重合到像素级),只有放大到进位处才看见 的台阶只剩 ;(b) 逐码误差: 造成的误差在中点两侧各 的"台阶",等效于一次 的负缺口。这类误差无法用两点校准消除。

5.3 数值结果与预算结论

() 增益误差
0.2 0.0082 −0.0082 −0.0040 0.0041
2 0.0824 −0.0824 −0.0398 0.0412
4.85 0.1997 −0.1997 −0.0964 0.0999
9.73 0.4000 −0.4000 −0.1933 0.2000
20 0.8195 −0.8195 −0.3966 0.4098

线性区间一直延伸到 4 LSB( 时仍差 ),所以预算可以直接用斜率:

精确求根

答案():,即 。换成别的 :;;。指标若为 ,则 。

缺口模式(阈值处,):、、、、,即每降一位减半。(单个宽度为 1 码、幅度 的 DNL 缺口,在端点归一化后恰好产生 的 INL 峰——这是代数必然,不是巧合)。

图 6 (a) 匹配  的 DNL 预算: 与 (后者恒为前者一半)随  严格线性上升,与  红线交于 ;(b) 阈值处的确定性误差形状:INL 是"中点两侧 ±0.2
图 6 (a) 匹配 的 DNL 预算: 与 (后者恒为前者一半)随 严格线性上升,与 红线交于 ;(b) 阈值处的确定性误差形状:INL 是"中点两侧 ±0.2 LSB 的台阶",DNL 是进位处按 递减的负缺口序列。

5.4 与电阻失配的联合预算(把 分给两个人)

电阻给出的 已经吃掉了指标的 :

分配方式 失配() 留给 ()
线性相加(保守) 0.265 LSB 0.135 LSB 3.28
RSS 合成(典型) 0.265 LSB LSB 7.29
只算 (失配另行解决) 0 0.4 LSB 9.73
要求 良率 0.530 LSB 负 不可行 → 需

两个工程结论:① 若按 (车规/高良率)设计, 的电阻本身就不够,必须把 压到 或加修调;② 若只按 , 的预算就只有 ,这已经要求相当宽的开关(下面 §5.5 给换算)。

5.5 失配、以及" 小到什么程度才够松"

自身失配的贡献(把每条支路的 也当作独立随机量, 为其相对偏差):等效于给 叠加 ,于是

失配
开关匹配() 1% 3% 10%
贡献 0.019 LSB 0.058 LSB 0.193 LSB

即在阈值 下,开关匹配只要好于 就可以忽略;但如果版图让不同位开关的 差到 ,它就会成为主要误差源之一。

开关尺寸换算(,即 ):若工艺给出 (、、 的量级,具体务必按 PDK 仿真校核),则 要求总栅宽 (多指并联)。这就是 R-2R 开关面积的下限: 越大, 预算越松、开关越省面积——这是 §7 trade-off 的主线之一。反之若为了速度把 压到 , 预算就只剩 ,需要 更宽的开关,通常得不偿失。


6. 十个必须注意的坑

  1. 口径必须写清楚。INL/DNL 的数值强依赖于"分母用理想 LSB 还是实测平均 LSB"以及"是否去掉增益/失调"。本文全部给了两种;本例差异 ,但在 主导时两者差异可以到一倍以上(因为 主要产生比例误差,端点定标会吃掉一大块)。
  2. 用两个 串联实现时,匹配会自动改善。此时 的失配是两只电阻误差的平均,。把这一条代入本文公式(只改 那 8 个参数的 ): 从 降到 ()——注意不是 ,因为串联 与终端电阻的独立误差不再同步改善。这也是本文"每只电阻独立"假设偏保守的地方。
  3. 系统性失配(梯度、边界效应)往往比随机失配大。本文只算随机项;实际版图必须 common-centroid + dummy,否则 再小也没用。
  4. 失配的单位电阻面积:(Pelgrom)。 的 对 poly/金属电阻意味着相当大的面积——面积预算要先算清楚。
  5. 开关在 侧与 GND 侧的 不同(传输门的 NMOS/PMOS 比例不同),且随节点电压变化(非线性)。本文假设两侧相同、与码无关,是最乐观的对称情形;实际要按最坏角仿真,或把 也纳入 Monte-Carlo。
  6. 造成的 DNL 不能靠两点校准消除(§5.2)。它必须体现在 预算里,而不是指望"后校准"。
  7. 高位数下 每加一位翻一倍: 电阻下 是 、 是 、 是 (,已不可用)。要 10 bit / ,需要 ——这就是为什么高精度 R-2R 需要修调或分段结构。
  8. 只算了静态量。动态建立由 决定,通常比这条静态 DNL 预算更紧; 选大虽然让 预算宽松,却会拖慢建立并抬高输出热噪声(,对低噪声测量不利)。
  9. 输出负载不能是有限阻抗。任何有限的 都会与 分压,引入(主要是)增益误差与与码相关的 ;高阻负载 + 缓冲器是标准做法。
  10. 终端电阻 的作用被低估。 的失配只贡献 ( 的 仅 ),但它决定了 的阻抗对称性;若把 LSB 开关的"0 位"接法改成断开(而不是接 GND),整条梯队的分压比都会乱——开关必须是"双掷",不能是"单刀通断"。

7. Trade-off 汇总

设计选择 (随机失配) 预算 速度/建立 面积 噪声
增大 略微改善(面积换匹配) 变松() 变慢() 变大(,同 下面积不变,但走线/寄生上升) 热噪声 ,变差
减小 不变(若单位电阻面积不变) 变紧() 变快 变小 变好
用两只 串联 改善 无影响 无影响 (多一只电阻的面积与寄生) 略好(串联降噪 )
开关加宽() 无影响 变松() 略好 变大、电荷注入/馈通变差 —
单位电阻面积 () 线性改善 无影响 略差(寄生 ) 变大 —
加修调/trim 大幅改善 无影响 — 测试成本 —
位数 恶化 预算按 LSB 归一,随之变紧 — — —

8. 复现

文件与运行方式

dac_r2r_8bit.m     % 单文件,含全部 4 段:理想校验 / 一阶理论 / Monte-Carlo / Ron 预算
  • MATLAB:dac_r2r_8bit
  • GNU Octave(本文实测环境,Octave 10.3):octave --no-gui --quiet --eval "dac_r2r_8bit"

Octave 实跑输出(节选,即本文所有数字的来源)

1) ideal ladder: Thevenin recursion vs analytic vs nodal
   max|Thevenin - Vref*D/2^N| = 1.110e-16 V
   max|Thevenin - nodal MNA|   = 2.220e-16 V   (independent check)
   Rout: min 1000.000000  max 1000.000000 ohm (code independent)
2) first-order mismatch theory (closed-form sensitivity recursion)
   check sum_j g_j (scale invariance) : max 0.000e+00
   check analytic g_j vs complex-step : max 2.776e-17
   sigma_INL(endpoint): max 0.044198 LSB @ D=127  (ideal-LSB def: 0.044341)
   sigma_DNL(endpoint): max 0.088395 LSB @ D=128   (3 sigma = 0.2652 LSB)
   sigma_DNL at D=2^m : 0.00139 0.00267 0.00538 0.01089 0.02195 0.04410 0.08840
3) Monte-Carlo on the exact ladder (Thevenin recursion)
   N=200000 trials (18.1 s), sigma-hat stat. error 0.158%
   MC  sigma_INL,max = 0.044085 @D=128 | theory 0.044198 -> -0.256%
   MC  sigma_DNL,max = 0.088169 @D=128 | theory 0.088395 -> -0.256%
   MC bias: max|mean INL| = 1.32e-04, max|mean DNL| = 1.83e-04 LSB
4) matched switch resistance Ron in series with every 2R rung
   linear fit: max|DNL| = 82.4309 * rho,  rho = Ron/(2R)
   exact root: rho*=4.866181e-03 -> Ron_max = 9.7324 ohm = 0.009732*R = R/102.75
   at threshold: max|DNL| = 0.40000 LSB, max|INL| = 0.20000 LSB, gain err -0.00553%
   DNL at D=2^m (m=3..7): -0.01209 -0.03796 -0.08976 -0.19327 -0.40000
   Ron mismatch contribution (per-switch spread, at rho*):
      sigma_Ron/Ron =  1.0%  -> 3-sigma DNL = 0.0192 LSB
      sigma_Ron/Ron =  3.0%  -> 3-sigma DNL = 0.0577 LSB
      sigma_Ron/Ron = 10.0%  -> 3-sigma DNL = 0.1925 LSB

完整脚本(MATLAB / Octave 通用;bits_of 用 mod(floor(D/2^(k-1)),2) 而不是 bitget,以兼容 Octave)

function dac_r2r_8bit()
% 8-bit voltage-mode R-2R DAC: Thevenin model, exact ideal transfer,
% first-order mismatch theory (closed form), Monte-Carlo verification and the
% switch on-resistance (Ron) budget for DNL_max < 0.4 LSB.
% Runs in MATLAB and in GNU Octave (tested: Octave 10.3).

  N=8; NB=2^N; NL=NB-1;
  R=1e3; VREF=1.0; SIG=0.06e-2;      % SIG = 1-sigma resistor mismatch (0.06%)
  try, rng(12345); catch, randn('seed',12345); end

  printf('===========================================================\n');
  printf('1) ideal ladder: Thevenin recursion vs analytic vs nodal\n');
  S=zeros(1,N); S(2:N)=R; A=2*R*ones(1,N); T=2*R;
  V=zeros(1,NB); Rout=zeros(1,NB);
  for D=0:NL, [V(D+1),Rout(D+1)]=thevenin_vout(S,A,T,D,VREF,N); end
  printf('   max|Thevenin - Vref*D/2^N| = %.3e V\n', max(abs(V-VREF*(0:NL)/NB)));
  e=0;
  for D=[1 37 128 200 254 255]
    e=max(e,abs(nodal_vout(S,A,T,D,VREF,N)-V(D+1)));
  end
  printf('   max|Thevenin - nodal MNA|   = %.3e V   (independent check)\n', e);
  printf('   Rout: min %.6f  max %.6f ohm (code independent)\n', min(Rout), max(Rout));

  printf('===========================================================\n');
  printf('2) first-order mismatch theory (closed-form sensitivity recursion)\n');
  G=zeros(NB,16);
  for D=0:NL, G(D+1,:)=dac_sens(D,R,2*R,2*R,VREF,N); end
  printf('   check sum_j g_j (scale invariance) : max %.3e\n', max(abs(sum(G,2))));
  printf('   check analytic g_j vs complex-step : max %.3e\n', cs_check(R,N,VREF));
  sIi= SIG*256*sqrt(sum(G.^2,2));                          % INL, ideal-LSB definition
  d0=G(1,:); dN=G(NB,:);
  H  = 256*((G-d0) - (0:NL)'/NL*(dN-d0));
  sIe= SIG*sqrt(sum(H.^2,2));                              % INL, endpoint (gain+offset trimmed)
  K  = 256*((G(2:NB,:)-G(1:NL,:)) - ones(NL,1)/NL*(dN-d0));
  sD = SIG*sqrt(sum(K.^2,2));                              % DNL, endpoint
  [m1,i1]=max(sIe); [m2,i2]=max(sD);
  printf('   sigma_INL(endpoint): max %.6f LSB @ D=%d  (ideal-LSB def: %.6f)\n', m1, i1-1, max(sIi));
  printf('   sigma_DNL(endpoint): max %.6f LSB @ D=%d   (3 sigma = %.4f LSB)\n', m2, i2, 3*m2);
  printf('   sigma_DNL at D=2^m : '); printf('%.5f ', sD(2.^(1:7))); printf('\n');

  printf('===========================================================\n');
  printf('3) Monte-Carlo on the exact ladder (Thevenin recursion)\n');
  NT=200000; CH=20000;
  S1=zeros(1,NB); S2=zeros(1,NB); D1=zeros(1,NL); D2=zeros(1,NL);
  ts=tic; done=0;
  while done<NT
    n=min(CH,NT-done); done=done+n;
    dev=randn(n,16)*SIG;
    Sb=zeros(n,N); Sb(:,2:N)=R*(1+dev(:,9:15));
    Am=2*R*(1+dev(:,1:8)); Tm=2*R*(1+dev(:,16));
    Vm=zeros(n,NB);
    for D=0:NL, Vm(:,D+1)=thevenin_vout(Sb,Am,Tm,D,VREF,N); end
    st=(Vm(:,NB)-Vm(:,1))/NL;
    inl=(Vm-Vm(:,1))./st - (0:NL);
    dnl=(Vm(:,2:NB)-Vm(:,1:NL))./st - 1;
    S1=S1+sum(inl,1); S2=S2+sum(inl.^2,1);
    D1=D1+sum(dnl,1); D2=D2+sum(dnl.^2,1);
  end
  si=sqrt((S2-S1.^2/NT)/(NT-1)); sd=sqrt((D2-D1.^2/NT)/(NT-1));
  mi=S1/NT; md=D1/NT;
  printf('   N=%d trials (%.1f s), sigma-hat stat. error %.3f%%\n', NT, toc(ts), 100/sqrt(2*NT));
  printf('   MC  sigma_INL,max = %.6f @D=%d | theory %.6f -> %+.3f%%\n', ...
         max(si), find(si==max(si))-1, m1, 100*(max(si)/m1-1));
  printf('   MC  sigma_DNL,max = %.6f @D=%d | theory %.6f -> %+.3f%%\n', ...
         max(sd), find(sd==max(sd)), m2, 100*(max(sd)/m2-1));
  printf('   MC bias: max|mean INL| = %.2e, max|mean DNL| = %.2e LSB\n', ...
         max(abs(mi)), max(abs(md)));
  printf('     D    sigINL_th    sigINL_mc    dev%%     sigDNL_th    sigDNL_mc    dev%%\n');
  for D=[1 32 64 96 127 128 192 255]
    if sIe(D+1)>0, t1=sprintf('%+6.3f',100*(si(D+1)/sIe(D+1)-1)); else, t1='   --  '; end
    if sD(D)>0,    t2=sprintf('%+6.3f',100*(sd(D)/sD(D)-1));      else, t2='   --  '; end
    printf('  %4d   %.6f    %.6f   %s   %.6f    %.6f   %s\n', D, ...
           sIe(D+1), si(D+1), t1, sD(D), sd(D), t2);
  end

  printf('===========================================================\n');
  printf('4) matched switch resistance Ron in series with every 2R rung\n');
  for rho=[1e-4 1e-3 2.426e-3 4.866e-3 1e-2]
    [d,~]=dac_dnl_ron(rho,R,2*R,VREF,N);
    printf('   rho=%9.4e  Ron=%8.3f ohm  max|DNL|=%8.5f LSB  DNL(128)=%+8.5f  DNL(64)=%+8.5f\n', ...
           rho, 2*rho*R, max(abs(d)), d(128), d(64));
  end
  [d1,~]=dac_dnl_ron(1e-4,R,2*R,VREF,N); c=max(abs(d1))/1e-4;
  printf('   linear fit: max|DNL| = %.4f * rho,  rho = Ron/(2R)\n', c);
  printf('   DNL_max < 0.4 LSB  ->  Ron <= %.4f ohm (R=%.0f) = %.6f*R = R/%.1f\n', ...
         0.8/c*R, R, 0.8/c, c/0.8);
  printf('   DNL_max < 0.5 LSB  ->  Ron <= %.4f ohm (R=%.0f) = %.6f*R = R/%.1f\n', ...
         1.0/c*R, R, 1.0/c, c);
  lo=0; hi=0.05;                                  % exact root: max|DNL| = 0.4 LSB
  for it=1:60
    mid=(lo+hi)/2; [dm,~]=dac_dnl_ron(mid,R,2*R,VREF,N);
    if max(abs(dm))<0.4, lo=mid; else, hi=mid; end
  end
  rho_s=(lo+hi)/2; [d,inl,ge]=dac_dnl_ron(rho_s,R,2*R,VREF,N);
  printf('   exact root: rho*=%.6e -> Ron_max = %.4f ohm = %.6f*R = R/%.2f\n', ...
         rho_s, 2*rho_s*R, 2*rho_s, 1/(2*rho_s));
  printf('   at threshold: max|DNL| = %.5f LSB, max|INL| = %.5f LSB, gain err %+.5f%% (%.5f LSB)\n', ...
         max(abs(d)), max(abs(inl)), ge*100, ge*256);
  printf('   DNL at D=2^m (m=3..7): '); printf('%+.5f ', d(2.^(3:7))); printf('\n');
  sRm=sqrt(sum(K(:,1:8).^2,2));           % DNL sensitivity to the 8 rung resistors
  printf('   Ron mismatch contribution (per-switch spread, at rho*):\n');
  for srel=[0.01 0.03 0.10]
    printf('      sigma_Ron/Ron = %4.1f%%  -> 3-sigma DNL = %.4f LSB\n', ...
           100*srel, 3*rho_s*srel*max(sRm));
  end
  printf('===========================================================\n');
end

% ------------------------------------------------------------------ helpers
function b=bits_of(D,N)
  D=D(:); b=zeros(numel(D),N);
  for k=1:N, b(:,k)=mod(floor(D/2^(k-1)),2); end
end

function [Vout,Rout]=thevenin_vout(S,A,T,D,VREF,N)
% Exact Thevenin recursion up the ladder.  S(:,k)=series R between node k-1,k
% (S(:,1) unused), A(:,k)=rung 2R+Ron at node k, T=terminator (node 0 to gnd).
% Works for scalar (1xN) and vectorised (n x N) inputs.
  b=bits_of(D,N);                           % b(:,k) = bit k-1 (LSB first)
  y=1./A(:,1)+1./T; cur=b(:,1)*VREF./A(:,1); Vt=cur./y; Rt=1./y;
  for k=2:N
    rd=S(:,k)+Rt; y=1./A(:,k)+1./rd; cur=b(:,k)*VREF./A(:,k)+Vt./rd;
    Vt=cur./y; Rt=1./y;
  end
  Vout=Vt; Rout=Rt;
end

function Vout=nodal_vout(S,A,T,D,VREF,N)
% independent nodal analysis of the same ladder (N unknowns) - cross check
  b=bits_of(D,N); Gm=zeros(N,N); rhs=zeros(N,1);
  for k=1:N
    Gm(k,k)=Gm(k,k)+1/A(k); rhs(k)=rhs(k)+b(k)*VREF/A(k);
    if k>1
      g=1/S(k); Gm(k,k)=Gm(k,k)+g; Gm(k-1,k-1)=Gm(k-1,k-1)+g;
      Gm(k,k-1)=Gm(k,k-1)-g; Gm(k-1,k)=Gm(k-1,k)-g;
    end
    if k==1, Gm(1,1)=Gm(1,1)+1/T; end
  end
  x=Gm\rhs; Vout=x(N);
end

function g=dac_sens(D,R,A0,T0,VREF,N)
% Closed-form first-order log-sensitivity  g(j) = d(Vout/Vref)/d(ln theta_j)
% theta = [alpha_0..alpha_7 (rungs), sigma_1..sigma_7 (series), tau (terminator)]
  b=bits_of(D,N); g=zeros(1,16);
  dA=zeros(1,16); dT=zeros(1,16); dA(1)=1; dT(16)=1;
  xi=0.5*(dA+dT); delta=0.25*(dT-dA)*b(1); W=zeros(1,N); W(1)=b(1)/2;
  for k=2:N
    eA=zeros(1,16); eA(k)=1; eS=zeros(1,16); eS(7+k)=1;
    Wk=0.5*(b(k)+W(k-1));
    dn=0.25*(b(k)*(eS+xi)+2*W(k-1)*eA+2*delta) - 0.25*Wk*(2*eA+eS+xi);
    xn=0.5*eA+0.25*(eS+xi);
    delta=dn; xi=xn; W(k)=Wk;
  end
  g=delta;
end

function e=cs_check(R,N,VREF)
% complex-step verification of the analytic gradients (machine precision)
  h=1e-20; e=0;
  for D=[0 1 64 127 128 200 255]
    g=dac_sens(D,R,2*R,2*R,VREF,N); gc=zeros(1,16);
    for j=1:16
      S=zeros(1,N); S(2:N)=R; A=2*R*ones(1,N); T=2*R;
      if j<=8, A(j)=A(j)*(1+1i*h);
      elseif j<=15, S(j-7)=S(j-7)*(1+1i*h);
      else, T=T*(1+1i*h); end
      v=thevenin_vout(S,A,T,D,VREF,N);
      gc(j)=imag(v)/h/VREF;
    end
    e=max(e,max(abs(g-gc)));
  end
end

function [d,inl,gainerr]=dac_dnl_ron(rho,R,A0,VREF,N)
% DNL (endpoint-normalised) for a matched switch resistance Ron = 2*rho*R
  S=zeros(1,N); S(2:N)=R; A=2*R*(1+rho)*ones(1,N); T=2*R;
  V=zeros(1,2^N);
  for D=0:2^N-1, V(D+1)=thevenin_vout(S,A,T,D,VREF,N); end
  st=(V(end)-V(1))/(2^N-1);
  d=(V(2:end)-V(1:end-1))/st-1;
  inl=(V-V(1))/st-(0:2^N-1);
  gainerr=st/(VREF/2^N)-1;
end

给 Python 用户的等价实现要点(本文的数值对照就是用同一套算法的 Python 版跑出来的):把 thevenin_vout 写成对 矩阵的向量化递推即可,MC 分块累加(、)避免占内存; 时精确模型约 70 s、一阶线性模型约 16 s(单核)。


9. 参考

  1. B. Razavi, Principles of Data Conversion System Design, IEEE Press / Wiley, 1995.(R-2R 梯形的戴维南分析、INL/DNL 的定义与端点定标)
  2. IEEE Std 1658-2011 / IEEE Standard for Terminology and Test Methods of Digital-to-Analog Converter Devices.(INL/DNL 的标准化定义与"去增益/失调"的处理)
  3. M. J. M. Pelgrom, A. C. J. Duinmaijer, A. P. G. Welbers, "Matching properties of MOS transistors," IEEE JSSC, vol. 24, no. 5, pp. 1433–1439, 1989.(,决定 对应的电阻面积)
  4. 本文数值均为自研脚本产生,脚本与图表见 §8 复现;全部结论可在 、、、 上任取参数复算(对 的缩放律:,)。

评论