抖动的分类:RJ 与 DJ 的硬核区分

做信号完整性这么多年,我遇到过不少工程师一上来就问:“抖动怎么测?”其实比测量更重要的,是先把抖动给分明白。

抖动,说白了就是信号边沿偏离理想位置的时间偏差。但它不是铁板一块。我习惯先把抖动分成两大类:随机抖动(RJ) 和 确定性抖动(DJ)。这两者来源不同、统计特性不同、处理手段也完全不同。弄混了,后面 BER 预算就算白算。

随机抖动(RJ)——老天爷赏的“随机性”

RJ 的来源是器件内部的电子噪声。热噪声、散粒噪声、闪烁噪声……这些都是物理层面的东西。

核心特征:高斯分布,无界增长,无法消除。

你想想看,只要芯片通电,电子就在做布朗运动。温度一高,RJ 的 RMS 值就往上走。RJ 的峰值在理论上没有上限——因为高斯分布的两条尾巴可以延伸到无穷远。

我曾经在一款 28 Gbps 的 SerDes 项目中反复排查 BER 过高问题。刚开始以为是码间干扰,调了 CTLE 和 DFE 都没用。后来单独做了抖动分离,才发现 RJ 的 RMS 已经超过了 1.2 ps。温度从 25°C 升到 85°C 时,RJ 几乎翻倍。嗯,这就是物理限制。

RJ 的统计模型很简单:

RJ(t) = σ_RJ · n(t),其中 n(t) ~ N(0, 1)
峰值 = k · σ_RJ,k 与 BER 相关(BER=1e-12 时 k≈14)

实战中,RJ 的峰值到峰值的估算很坑。千万别用高斯分布去乘 6 就完事。对于 BER=1e-15,k 值要到 15.9。我会用表格记住几个关键点:

目标 BER 峰值因子 k 说明
1e-9 12.0 PCIe Gen3 常用参考
1e-12 14.0 大部分串行链路规范
1e-15 15.9 光纤链路要求

我个人习惯在做 BER 预算时,直接用这些 k 值算 RJ 预算,而不是随便估一个峰峰值。

确定性抖动(DJ)——有规律、可复现、可补偿

DJ 的特性是有界的,它的峰值有明确上限。而且 DJ 往往能通过电路设计、均衡、或者 PCB 布线来抑制。

我把它再往下分四个子类,每个我都吃过亏。

1. 占空比失真(DCD)

发射器的正负沿不对称、或者参考时钟的占空比不是 50%,就会造成 DCD。说白了,就是 0 和 1 的脉冲宽度不一样。

我以前遇到过一块板子,时钟芯片输出 50 MHz 的占空比漂移了 3%。结果整个链路的数据眼图左边界被削了。解决方式很简单:加一个占空比校正电路(DCC)就行。但刚开始我愣是查了三天眼图。

2. 数据相关抖动(DDJ)

DDJ 是最典型的码间干扰(ISI)。传输线上的损耗、反射、串扰都让不同码型切换时边沿位置发生变化。“010101”和“000001”的边沿居然差了好几个皮秒。

注意:DDJ 不能用简单的均方根来等价。它是有界且与码型相关的。做仿真时一定要用 PRBS 序列,不要只跑一个周期波形。

3. 周期抖动(PJ)

PJ 来源于电源噪声、EMI 或者锁相环的杂散。频率可以是单一频率,也可以是几个频率的组合。我在做 10 Gbps 背板时遇到过 1.2 MHz 的电源开关噪声,直接在抖动频谱上出现一个毛刺,把眼图撕开了一道口子。

对付 PJ,要么改善电源完整性,要么用扩频时钟(SSC)把能量摊开。

4. 有界不相关抖动(BUJ)

BUJ 来自邻近通道的串扰。它有界,且与被测信号不相关。我做过一个 4 通道 QSFP 项目,通道 2 和通道 3 间距太近,BUJ 贡献了 2 ps 的峰峰值。最后调整布线间距才搞定。

把 RJ 和 DJ 装进同一个箱子里——双狄拉克模型

好了,两个大类讲完了。但实际测量中,测到的总抖动是 RJ 和 DJ 的卷积。抖动的概率密度函数(PDF)是高斯与某个 DJ 分布的卷积。这没办法直接做数学分离。

业界通用做法是:双狄拉克模型。它假设 DJ 的 PDF 可以近似成两个狄拉克函数,高斯与它们卷积后形成“双峰”高斯分布。

我们可以从浴盆曲线(BER 斗)上读出数据:

  • 曲线的斜率反映 RJ 的 σ 值
  • 曲线的左右偏移量反映 DJ 的 δ

我给出一个简单的 Python 代码,用来从浴盆曲线拟合成 RJ 和 DJ:

import numpy as np
from scipy.optimize import curve_fit

def dual_dirac(ui, rj_sigma, dj_delta):
    # 简化版双狄拉克 BER 模型
    # ui:时间偏移(UI)
    from scipy.special import erfc
    t1 = (ui - dj_delta/2) / (rj_sigma * np.sqrt(2))
    t2 = (ui + dj_delta/2) / (rj_sigma * np.sqrt(2))
    return 0.5 * (erfc(t1) + erfc(t2))

# 模拟测量数据
ui_data = np.linspace(-0.5, 0.5, 100)
ber_data = dual_dirac(ui_data, 0.02, 0.15) + np.random.normal(0, 1e-12, 100)

params, _ = curve_fit(dual_dirac, ui_data, ber_data, p0=[0.01, 0.1])
rj_sigma, dj_delta = params
print(f"拟合结果:RJ σ = {rj_sigma:.4f} UI, DJ δ = {dj_delta:.4f} UI")

注意:双狄拉克模型只在 DJ 与 RJ 相互独立时才成立。如果 DJ 还与信号相关(DDJ),这个模型会低估真实抖动。我会结合时间间隔误差(TIE)直方图做核查。

知识体系:抖动分类的链路

为了让你看得更清楚,我画了一张图。它把所有抖动分类整理为一个层次结构,以及它们与 BER 之间的联系:

抖动分类知识结构 总抖动(TJ) 随机抖动 RJ 确定性抖动 DJ 热噪声 / 散粒噪声 / 1/f 噪声 高斯分布 / 无界增长 DCD 占空比失真 DDJ 码间干扰 PJ 周期抖动 BUJ 有界不相关抖动 双狄拉克模型:TJ = k·σ_RJ + DJδ 用于从 BER 浴盆曲线分离 RJ 与 DJ

实战中的“避坑指南”

讲完了分类,我想聊聊实际处理抖动预算时容易掉进去的几个坑。

坑一:把 DJ 当成 RJ 来均方根
我曾经看到有位同事用 RMS 直接累加 DDJ 和 PJ。这是错的。DJ 是有界的,应该用峰峰值累加。正确的做法是:TJ = k·σ_RJ + DJpp。

坑二:忽略 DCD 与 DDJ 的交互
有些 SerDes 的均衡可以消除部分 DDJ,但 DCD 反而会因为均衡被放大。我建议在做抖动分解前,先测一下发射端占空比,确保 DCD 被压制到 1% 以下。

记住:RJ 的分离需要大量的比特采样(至少 1e9 个边沿),否则高斯尾巴不够准。我个人至少采集 10 秒以上的实时 TIE 数据再做统计。

总结几条硬经验

  1. RJ 不可消除,只能通过降低芯片温度、选用低噪声电源来减小
  2. DJ 的每个子项都有对应的补偿手段:DCD→DCC、DDJ→均衡、PJ→电源滤波或 SSC、BUJ→布线隔离
  3. 做 BER 预算时,先分离 RJ 和 DJ,再用双狄拉克模型算总抖动,别直接测峰峰值凑数
  4. 任何一个高速链路,如果抖动分离后发现 DJ 占比超过 60%,先去查电源和串扰,而不是换锁相环

嗯,抖动分类这关过了,后面做 BER 预算和裕量分配才不会晕头转向。我做完的每个项目,都会把 RJ 和 DJ 的分离结果记录在模板里——那才是后续改版最宝贵的电气基线。