接收端均衡:CTLE 与 DFE 技术详解
信号从发端经过信道跑到接收端,早就被 PCB 走线、连接器、过孔折腾得不成样子了。衰减、反射、串扰,全都混在一起。
这时候接收端要是不做点什么,眼图根本睁不开。
说白了,接收端均衡就是一把手术刀,专门修复被信道搞坏了的信号。
我刚开始做 Gen3 调优时,以为只要调发端就够。后来发现,链路跑上 8GT/s 甚至 16GT/s,发端很多问题根本救不回来,得靠接收端来兜底。
这一章我们就深入看看两个核心模块:CTLE 和 DFE。
核心观点:CTLE 负责高通滤波去高频衰减,DFE 负责消除码间干扰。两者缺一不可。
CTLE 是什么?为什么需要它?
CTLE,全称是连续时间线性均衡器。
它是一个模拟高通滤波器。
信道对高频信号的损耗远大于低频。结果就是,信号的上升沿、下降沿变缓,眼高变小。
CTLE 做的就是补偿这个高频损耗。
你想想看,一根长长的走线就像低通滤波器。高频分量被吃掉太多,波形早就不是方波了。CTLE 在高频段给增益,把那些被削弱的边沿再拉起来。
| 频段 | 信道衰减 | CTLE 补偿 | 结果 |
|---|---|---|---|
| 低频 (DC ~ 1GHz) | 小 (0 ~ 3dB) | 低增益 (0 ~ 2dB) | 直流电平保持 |
| 中频 (1 ~ 5GHz) | 中等 (3 ~ 10dB) | 中增益 (2 ~ 6dB) | 部分补偿 |
| 高频 (5 ~ 16GHz) | 大 (10 ~ 30dB+) | 高增益 (6 ~ 12dB) | 边沿恢复 |
CTLE 一般有好几个可编程的增益档位和零点频率。实际工程中,我们通过 PRBS 测试码进行扫描,找到眼高最大的那组设置。
实战技巧:CTLE 开太高也不是好事。高频增益同时会放大噪声。我见过一些板子,CTLE 打到最大档,眼图反而变差——因为噪声被放到失真之上了。
CTLE 的自适应控制
很多 Gen3/Gen4 的 PHY 都支持 CTLE 自适应。什么意思?
- 接收端根据检测到的信号幅度或眼图张开度,自动调整增益档位。
- 目的是让信号幅度和信噪比达到平衡。
- 自适应开启后,我一般会先看收敛速度。太慢的话,链路训练会超时。
但我个人习惯,在实验室调优阶段还是先用手动模式。固定一组 CTLE 系数,看眼图和 BER 表现。确定最优值后,再开启自适应让芯片跟踪温度或电压漂移。
DFE:后发制人的非线性均衡
CTLE 搞不定所有事。
信道中的反射、不连续阻抗,会产生长时间拖尾的码间干扰。CTLE 是线性滤波器,对这种后标干扰 (post-cursor ISI) 补偿能力有限。
这时候 DFE 就登场了。
DFE 的核心思路很简单:
既然已经知道前面几个 bit 是 1 还是 0,那就可以预测它们对当前 bit 的干扰量,然后减掉。
说白了,DFE 用一个反馈滤波器,消除已知的码间干扰。
DFE 工作原理示意:
输入信号 -> 采样判决 -> 判出当前 bit (dk)
|
反馈滤波器 (系数 c1, c2, ..., cn)
|
从下一输入中减去: r(k+1) - c1*dk - c2*d(k-1) - ...
这个反馈滤波器的系数数量,就是 DFE 的抽头数。抽头数越多,能消除的 ISI 范围越广。
| Gen 标准 | 典型 DFE 抽头数 | 消除 ISI 的符号间隔 | 复杂度 |
|---|---|---|---|
| Gen3 (8GT/s) | 3 ~ 5 tap | 3 ~ 5 UI | 低 |
| Gen4 (16GT/s) | 5 ~ 8 tap | 5 ~ 8 UI | 中等 |
| Gen5 (32GT/s) | 8 ~ 12 tap | 8 ~ 12 UI | 高 |
DFE 的自适应算法
DFE 系数的搜索是个技术活。不能瞎调。
- LMS 算法:最常用。自适应地最小化误差信号的均方值。
每次更新:c_new = c_old + μ * e * d,其中 μ 是步长,e 是误差,d 是判决数据。 - Sign-Sign LMS:工程实现的简化版。只取误差和数据的符号位。硬件成本低。
- Zero-Forcing:直接根据信道冲击响应对抽头系数进行强制归零。
我自己在项目里多用 Sign-Sign LMS。简单,够用,收敛还不错。
避坑指南:我曾经调一块 Gen4 显卡的链路,DFE 自适应一直不收敛。查了三天,发现是 CTLE 的增益设错了。CTLE 输出信号幅度太小,DFE 的误差检测器根本没法分辨是 0 还是 1。先调好 CTLE,再开 DFE 自适应——这个顺序特别重要。
CTLE + DFE 的联合调优策略
CTLE 和 DFE 不是各干各的。它们需要配合。
CTLE 负责把信号拉回到一个合理的幅度范围内,把高频补偿到位。
DFE 负责收拾 CTLE 剩下的尾巴——那些长拖尾的码间干扰。
- 第一步:固定一组 DFE 系数(或者关闭 DFE),扫描 CTLE 档位。
找一个眼高最大、眼宽合理的 CTLE 值。 - 第二步:CTLE 固定在这个最优档位,开启 DFE 自适应训练。
监测 BER 是否下降,以及 DFE 系数的收敛曲线是否平稳。 - 第三步:如果 BER 仍达不到要求(一般 Gen4 要求 BER < 1e-12),微调 CTLE 档位后再跑一次自适应。
这个流程,说白了就是一个 CTLE -> DFE -> 联合优化 的迭代过程。
经验口:做完联合调优后,建议做一个温度扫描。从 0°C 到 85°C,看看眼图裕量会不会崩。我遇到过高温下 DFE 系数漂移导致链路断连的案例。解决方法是把 DFE 自适应一直保留开启状态。
实战:CTLE + DFE 的配置示例
下面是一段基于某个常见 SerDes IP 的配置示例。具体寄存器地址各家不同,但逻辑是相通的。
// Step 1: 初始化 PHY
phy_init();
// Step 2: 关闭 DFE,固定为低增益模式
dfe_disable();
dfe_set_coeffs(0, 0, 0); // 清空所有抽头
// Step 3: 扫描 CTLE 档位
for (ctle_gain = 0; ctle_gain < 16; ctle_gain++) {
ctle_set_gain(ctle_gain);
delay(1); // 等待稳定
eye_height = measure_eye_height();
// 记录
results[ctle_gain] = eye_height;
}
// Step 4: 选择最优 CTLE 档位
int best_ctle = find_best(results); // 眼高最大的档位
ctle_set_gain(best_ctle);
// Step 5: 开启 DFE 并开始自适应训练
dfe_enable();
dfe_lms_start(ALGORITHM_SIGN_SIGN_LMS);
// Step 6: 等待收敛或超时
while (!dfe_converged() && (timeout_ms < 500)) {
delay(10);
}
// Step 7: 检查 BER
uint64_t ber = measure_ber();
if (ber > BER_THRESHOLD_1E_12) {
// 微调 CTLE 后重试
ctle_set_gain(best_ctle + 1);
dfe_lms_restart();
}
这个流程看起来简单,但实际跑起来有很多坑。比如眼高测量本身就有噪声,你得多次采样平均。再比如 DFE 收敛后,你要读回抽头系数,看看是不是合理的值——如果某个抽头系数异常大,往往说明 CTLE 的频响出了问题。
调试技巧:测试环境中用 real-time scope 观察 DFE 的误差信号。如果误差信号分布太宽或者有明显偏置,就应该调整 DFE 的 DC 消除环路。有些 PHY 支持手动注入 DC offset 补偿。
CTLE 与 DFE 核心差异
| 维度 | CTLE | DFE |
|---|---|---|
| 技术本质 | 线性模拟滤波器 | 非线性数字反馈 |
| 主要作用 | 补偿高频衰减 | 消除后标码间干扰 (post-cursor ISI) |
| 噪声影响 | 放大噪声(不可避免) | 不放大噪声(错误传播风险) |
| 自适应速度 | 快(模拟环路) | 较慢(数字自适应) |
| 硬件开销 | 低(几个电阻电容 + 差分对) | 较高(抽头越多越贵) |
| 错误传播 | 无 | 有,单个误码可能诱发后续误码 |
所以我也常说一句话,CTLE 和 DFE 是黄金搭档。CTLE 打好基础,DFE 打扫战场。一项没做好,另一项再努力也白费。