4、CTLE的VerilogA建模:理想CTLE模型、一阶CTLE实现、参数化设计
各位同学,咱们今天聊聊CTLE的建模。CTLE,连续时间线性均衡器,说白了就是给信号做高频补偿的。信号在信道里跑一圈,高频分量衰减得厉害,眼图都闭上了。CTLE的作用就是把这些高频分量再抬起来,让眼图重新睁开。
我个人习惯,建模之前先想清楚要什么。你是要快速验证系统架构?还是要精确模拟晶体管级行为?不同的目标,建模的粒度完全不同。今天咱们从最理想的模型开始,一步步深入到参数化的一阶CTLE实现。
核心要点:CTLE建模的三个层次——理想模型用于架构探索,一阶模型用于行为验证,参数化模型用于设计空间扫描。
4.1 理想CTLE模型:先搭个框架
理想CTLE模型,说白了就是一个带零极点的滤波器。它的传输函数长这样:
H(s) = Adc * (1 + s/ωz) / (1 + s/ωp)
其中ωz是零点频率,ωp是极点频率,Adc是直流增益。零点负责抬高频,极点负责压高频噪声。你想想看,这不就是给信号做了一次「跷跷板」吗?
在VerilogA里实现这个模型,我建议用laplace_nd算子。这个算子直接接受分子分母系数,用起来特别顺手。来看代码:
// 理想CTLE模型 - VerilogA
`include "constants.vams"
`include "disciplines.vams"
module ideal_ctle(in, out);
input in;
output out;
electrical in, out;
parameter real adc = 1.0; // 直流增益
parameter real fz = 1e9; // 零点频率 (Hz)
parameter real fp = 10e9; // 极点频率 (Hz)
real wz, wp;
analog begin
wz = 2 * `M_PI * fz;
wp = 2 * `M_PI * fp;
// 拉普拉斯传输函数
V(out) <+ laplace_nd(V(in), {adc, adc/wz}, {1, 1/wp});
end
endmodule
这段代码看着简单,但有个坑。我记得第一次用laplace_nd时,把系数顺序搞反了,仿真结果完全不对。分子分母的系数都是从s的最高次项开始排列的,这点一定要注意。
小技巧:用理想模型做系统级仿真时,可以把fz设成信道损耗的-3dB频率,fp设成符号率的0.7倍左右。这样出来的眼图效果最接近实际。
4.2 一阶CTLE实现:从理想走向真实
理想模型好是好,但做电路设计的同事看了会摇头——「你这玩意儿能流片吗?」嗯,确实不能。所以咱们得往真实电路靠一靠。
一阶CTLE的核心结构是差分对加源极退化。源极的Rs和Cs并联,构成了一个可调的零点。负载端的Rd和CL构成了极点。传输函数变成:
H(s) = (gm * Rd) * (1 + s * Rs * Cs) / (1 + s * Rd * CL) / (1 + s * Rs * Cs / (1 + gm * Rs))
看着复杂?其实拆开看就三部分:直流增益、零点、极点。我习惯把这种模型叫做「半物理模型」——它不模拟每个晶体管的I-V特性,但抓住了CTLE的核心行为。
来看实现:
// 一阶CTLE模型 - 带源极退化
`include "constants.vams"
`include "disciplines.vams"
module ctle_first_order(inp, inn, outp, outn);
input inp, inn;
output outp, outn;
electrical inp, inn, outp, outn;
parameter real gm = 0.01; // 跨导 (S)
parameter real rd = 500; // 负载电阻 (Ohm)
parameter real rs = 100; // 源极电阻 (Ohm)
parameter real cs = 1e-12; // 源极电容 (F)
parameter real cl = 50e-15; // 负载电容 (F)
real v_in, v_out;
real adc, wz, wp1, wp2;
analog begin
v_in = V(inp) - V(inn);
// 计算零极点
adc = gm * rd / (1 + gm * rs);
wz = 1.0 / (rs * cs);
wp1 = 1.0 / (rd * cl);
wp2 = (1 + gm * rs) / (rs * cs);
// 三极点模型
v_out = adc * laplace_nd(v_in,
{1, 1/wz},
{1, 1/wp1 + 1/wp2, 1/(wp1*wp2)});
V(outp, outn) <+ v_out;
end
endmodule
这里有个细节——我用了三个极点?不对,其实是两个极点加一个零点。但wp2这个极点频率通常比wp1高很多,所以实际起作用的就一个主极点加一个零点。为什么会这样?因为源极退化引入的第二个极点,说白了就是寄生效应,频率高了自然就被忽略了。
注意:这个模型假设了差分对完全对称。实际电路中失配会引入共模到差模的转换,严重时会影响眼图对称性。我在一个28Gbps的项目中就吃过这个亏,后来在模型里加了失配参数才搞定。
4.3 参数化设计:让模型活起来
做SerDes的人都知道,CTLE的参数不是定死的。不同信道长度、不同数据速率,需要的均衡强度都不一样。所以参数化设计是必须的。
我个人习惯把参数分成三类:
| 参数类别 | 典型参数 | 作用 |
|---|---|---|
| 工艺参数 | gm, rd, cl | 由工艺决定,通常固定 |
| 可调参数 | rs, cs | 通过数字控制字调节 |
| 环境参数 | 温度, 电压 | 用于corner仿真 |
参数化模型的好处是,你可以用一个模型跑遍所有corner。来看一个带温度系数的版本:
// 参数化CTLE模型 - 带温度补偿
`include "constants.vams"
`include "disciplines.vams"
module ctle_param(inp, inn, outp, outn);
... // 端口定义同上
parameter real temp_coeff = -0.002; // 温度系数 (/C)
parameter integer ctrl_bits = 4; // 控制位宽
real rs_eff, cs_eff;
integer rs_code, cs_code;
analog begin
// 温度补偿
rs_eff = rs * (1 + temp_coeff * ($temperature - 27));
// 数字控制字解码
rs_code = ...; // 从数字接口读取
cs_code = ...;
// 可调电阻/电容
rs_eff = rs_eff / (1 + rs_code * 0.1);
cs_eff = cs * (1 + cs_code * 0.2);
// 核心传输函数
... // 使用rs_eff和cs_eff计算
end
endmodule
这段代码里,温度系数是我拍脑袋写的-0.002/C。实际项目中这个值要从工艺模型里提取,或者直接问foundry要。我曾经在一个项目中偷懒没加温度补偿,结果高温下眼图直接闭上了,教训深刻啊。
经验之谈:参数化设计时,一定要给每个参数留出±20%的裕量。因为流片回来的实际值和仿真值总有偏差,留点余量心里踏实。
4.4 模型验证:别让bug溜过去
模型写完了,怎么验证?我一般做三步:
- AC仿真:扫频看幅频响应,确认零极点位置对不对
- 瞬态仿真:给PRBS码流,看眼图是否睁开
- 参数扫描:遍历所有可调参数组合,看有没有死区
AC仿真时,我习惯把结果和理想模型对比。如果偏差超过5%,就要回头检查代码了。瞬态仿真更直观——眼图睁开的高度和宽度,直接反映了均衡效果。
嗯,说到眼图,我记得有一次仿真出来的眼图中间有个「鬼影」,查了半天发现是模型里一个时间常数设错了。这种问题在理想模型里不会出现,但一阶模型就会暴露出来。所以说,模型越接近真实,越能帮你提前发现设计问题。
验证清单:
- 直流增益是否与设计值一致?
- 零点频率是否随Rs/Cs线性变化?
- 高频滚降斜率是否为-20dB/dec?
- 不同corner下眼图余量是否足够?
好了,CTLE建模的内容就这些。从理想模型到一阶实现,再到参数化设计,每一步都有它的用处。理想模型帮你快速搭系统,一阶模型帮你验证行为,参数化模型帮你做设计空间探索。三者结合,才能把CTLE设计得又快又好。
下次咱们聊聊DFE的建模,那个更有意思——数字逻辑和模拟电路的结合,挑战性更大。