4. 理想开关模型:Verilog-A建模、导通电阻与关断电容、仿真验证
好,咱们进入第四章。前面聊了采样电路的基本概念和失真来源,现在得动真格的了——搭模型。
你可能会问:「理想开关嘛,不就是一根导线连过去、断开就完事了?」嗯,我刚开始也这么想。直到有一次做高速ADC的前端设计,用理想开关仿出来的SFDR漂亮得不行,结果流片回来一测,直接掉了20dB。问题出在哪?就是开关模型太「理想」了,忽略了导通电阻和关断电容这些寄生参数。
所以这一章,咱们就好好聊聊怎么用Verilog-A给理想开关建模,把导通电阻、关断电容这些「不理想」的东西加进去,最后再验证模型对不对。
4.1 为什么需要Verilog-A模型?
说白了,SPICE自带的开关模型(比如sw或vswitch)太粗糙了。它们只能模拟通断,没法精确控制导通电阻随电压、温度的变化,更别提关断电容的非线性了。
我个人习惯用Verilog-A来做行为级建模。原因有三:
- 精度可控:你可以把导通电阻写成电压的函数,模拟沟道调制效应。
- 参数化:一个模型调几个参数,就能适配不同工艺、不同尺寸的开关。
- 仿真速度快:比全晶体管级模型快一个数量级,适合做系统级仿真。
小提示:Verilog-A模型虽然快,但别用它做精确的噪声仿真。噪声分析还是得靠晶体管级模型。
4.2 理想开关的Verilog-A建模
咱们先搭一个最基础的理想开关模型。它有三个端口:输入in、输出out、控制ctrl。控制信号为高时导通,为低时关断。
下面是我常用的模板:
// 理想开关 Verilog-A 模型
`include "disciplines.vams"
`include "constants.vams"
module ideal_switch (in, out, ctrl);
inout in, out;
input ctrl;
electrical in, out, ctrl;
parameter real Ron = 1.0; // 导通电阻,单位 Ohm
parameter real Roff = 1.0e9; // 关断电阻,单位 Ohm
parameter real Vth = 0.5; // 阈值电压,单位 V
real R;
analog begin
// 控制信号判断
if (V(ctrl) > Vth)
R = Ron;
else
R = Roff;
// 通过欧姆定律描述开关行为
I(in, out) <+ (V(in) - V(out)) / R;
end
endmodule
这个模型很简单,但已经能用了。Ron和Roff分别控制导通和关断时的电阻。你想想看,如果Ron设成1Ω,Roff设成1GΩ,那这个开关在导通时几乎没压降,关断时漏电流可以忽略。
注意:实际仿真时,Roff别设太大,否则会导致收敛问题。我一般设到1e9就够用了,再大仿真器容易报错。
4.3 加入导通电阻的非线性
实际MOS开关的导通电阻不是常数。它会随输入电压变化,尤其是在深亚微米工艺下,沟道调制效应很明显。
我在项目中遇到过一个问题:一个采样开关在输入电压接近VDD/2时,导通电阻突然变大,导致采样时间常数变化,引入谐波失真。后来我在模型里加了电压依赖项,才把这个问题复现出来。
改进后的模型如下:
// 非线性导通电阻 Verilog-A 模型
module switch_nonlinear (in, out, ctrl);
inout in, out;
input ctrl;
electrical in, out, ctrl;
parameter real Ron0 = 1.0; // 零偏导通电阻,单位 Ohm
parameter real K_v = 0.1; // 电压依赖系数
parameter real Roff = 1.0e9; // 关断电阻
parameter real Vth = 0.5;
real R, Vx;
analog begin
Vx = (V(in) + V(out)) / 2.0; // 取平均电压
if (V(ctrl) > Vth) begin
// 导通电阻随电压变化
R = Ron0 * (1.0 + K_v * (Vx - 0.5));
if (R < 0.1) R = 0.1; // 防止电阻为负
end else begin
R = Roff;
end
I(in, out) <+ (V(in) - V(out)) / R;
end
endmodule
这里我用了K_v系数来模拟电阻随电压的变化。实际工艺中,这个系数可以从晶体管级仿真提取出来。
4.4 关断电容的影响
很多人只关注导通电阻,忽略了关断电容。其实,关断电容会引入电荷注入和时钟馈通,严重时会让采样值偏移好几个LSB。
我记得有一次做12位SAR ADC,采样开关关断后,输出总有一个固定的偏移。查了半天,发现是开关的关断电容和采样电容形成了分压,导致电荷重新分配。
所以,模型里必须加上关断电容。下面是我常用的做法:
// 带关断电容的开关模型
module switch_with_cap (in, out, ctrl);
inout in, out;
input ctrl;
electrical in, out, ctrl;
parameter real Ron = 1.0;
parameter real Roff = 1.0e9;
parameter real Coff = 1.0e-15; // 关断电容,单位 F
parameter real Vth = 0.5;
real R;
analog begin
if (V(ctrl) > Vth)
R = Ron;
else
R = Roff;
// 主开关路径
I(in, out) <+ (V(in) - V(out)) / R;
// 关断电容:只在关断时接入
if (V(ctrl) < Vth) begin
I(in, out) <+ Coff * ddt(V(in) - V(out));
end
end
endmodule
注意,这里我用ddt()函数来描述电容的电流-电压关系。只在关断时接入电容,这样导通时不会影响速度。
关键点:关断电容的值通常很小(fF量级),但在高频采样时,它的影响会被放大。如果你的采样率超过100MHz,一定要把这个参数加进去。
4.5 模型验证:仿真与对比
模型搭好了,怎么验证对不对?我的做法是三步走:
- 直流扫描:看导通电阻随输入电压的变化是否符合预期。
- 瞬态响应:给一个阶跃信号,看开关的开启和关断时间。
- 谐波分析:用正弦波输入,看输出信号的THD。
下面是一个典型的验证电路设置:
| 参数 | 值 | 说明 |
|---|---|---|
| 输入信号 | 正弦波,1Vpp,10MHz | 测试线性度 |
| 控制信号 | 方波,0~1.8V,50MHz | 模拟采样时钟 |
| 负载电容 | 1pF | 模拟采样电容 |
| 导通电阻 | 10Ω | 理想值 |
| 关断电容 | 10fF | 典型值 |
仿真结果出来后,我会对比理想模型和非线性模型的THD。如果非线性模型的THD比理想模型高了10dB以上,那就说明你的模型成功复现了实际电路的问题。
4.6 知识体系总览
下面这张图总结了本章的核心逻辑:
4.7 避坑指南
最后,分享几个我踩过的坑:
- 收敛问题:关断电阻别设太大,超过1e10就容易不收敛。我一般用1e9。
- 电容初始条件:带电容的模型,仿真时记得设
ic参数,否则第一个仿真点可能出错。 - 控制信号斜率:实际时钟信号有上升时间,模型里最好用
transition函数模拟,别用理想阶跃。
经验之谈:如果你在做混合信号仿真,建议把开关模型单独放在一个cell里,方便替换。从理想模型到晶体管级模型,一步到位,不用改testbench。