4. PRBS15详解:多项式 x¹⁵ + x¹⁴ + 1 与应用场景
好,咱们今天来聊聊 PRBS15。很多刚接触高速串行测试的工程师觉得多项式这东西很抽象,不就是 x¹⁵ + x¹⁴ + 1 嘛,但在实际项目中,选择哪种 PRBS 可不是随便拍脑袋定的。
4.1 从多项式说起:x¹⁵ + x¹⁴ + 1 到底在表达什么?
PRBS15 的核心是这个多项式:x¹⁵ + x¹⁴ + 1。什么意思呢?它其实是在描述一个 15 位移位寄存器的反馈逻辑。
具体来说:
- 最高次项 x¹⁵:说明寄存器长度为 15 位。
- x¹⁴:表示我们要把第 14 位抽出来做反馈。
- 常数项 1:代表反馈异或运算的起点。
你想想看,15 位寄存器能产生多少种状态?排除全零状态,正好是 2¹⁵ - 1 = 32767 个。这就是 PRBS15 的序列长度。每次时钟沿到来时,寄存器向左移一位,同时根据 x¹⁵ 和 x¹⁴ 的输出做异或,再反馈回最低位。
4.2 LFSR 实现:用代码把公式说清楚
理论讲完了,咱们看看怎么用代码实现。LFSR(线性反馈移位寄存器)是 PRBS 的物理基础。我习惯用 Python 来快速验证算法,下面是个标准实现:
def prbs15_generator(seed=0x7FFF, n_bits=32):
"""
PRBS15 生成器(多项式:x^15 + x^14 + 1)
Args:
seed: 初始种子,不能为 0。常见起始值 0x7FFF
n_bits: 需要生成的比特数
Returns:
二进制序列(list of int)
"""
lfsr = seed & 0x7FFF
sequence = []
for _ in range(n_bits):
# 输出当前最高位
out_bit = (lfsr >> 14) & 1
sequence.append(out_bit)
# 计算反馈:取第 15 位和第 14 位异或
feedback = ((lfsr >> 14) ^ (lfsr >> 13)) & 1
# 左移一位,将反馈结果放入最低位
lfsr = ((lfsr << 1) | feedback) & 0x7FFF
return sequence
# 验证前 16 位输出
seq = prbs15_generator(seed=0x0001, n_bits=16)
print('输出序列:', ''.join(str(b) for b in seq))
# 期望结果:根据初始种子不同而不同
这段代码本身不复杂。但我在项目中踩过一个坑——种子不能为零。全零状态下,LFSR 会永远输出 0,序列就断了。
| 参数 | 说明 | 常见值 |
|---|---|---|
| 多项式 | x¹⁵ + x¹⁴ + 1 | FB = [15, 14] |
| 序列长度 | 2¹⁵ - 1 = 32767 | 小于 PRBS23,大于 PRBS7 |
| 初始种子 | 15 位非零二进制 | 0x7FFF, 0x0001 |
| 最长连续 1 | 15 位 | 可用于测 DC 平衡 |
| 最长连续 0 | 14 位 | 考验锁相环保持能力 |
4.3 PRBS15 的用武之地:哪些场景离不开它?
在高速测试中,PRBS7 太短,PRBS31 又太长。PRBS15 刚好卡在中间:
- 10Gbps 以上串行链路测试:比如 10GBASE-KR、25G/50G/100G 以太网物理层。这些标准里明确要求用 PRBS15 做压力测试。
- PCIe 4.0 / 5.0 接收端抖动容限测试:PCIe 规范要求在特定测试模式下注入 PRBS15 来评估接收器的 CDR 和均衡能力。
- 光模块 BER 测试:短距离光互联场景,PRBS15 能平衡测试时间和通道评估深度。
- 高速背板通道验证:我曾经调试一块 28Gbps 背板,PRBS7 打过去眼图还行,但换成 PRBS15 马上暴露出一个 15 英寸走线处的反射问题。
4.4 误码注入与比特错误定位
测试不光要产生 PRBS15,更要学会破坏它。准确注入误码,才能验证接收端纠错性能。
4.4.1 定点错误注入
在序列的特定比特位置翻转一个比特:
def inject_single_bit_error(sequence, error_pos):
"""在指定位置翻转比特"""
seq_list = list(sequence)
seq_list[error_pos] ^= 1
return ''.join(str(b) for b in seq_list)
# 在 PRBS15 序列中第 10000 位注入一个错误
seq_str = ''.join(str(b) for b in prbs15_generator(seed=0x7FFF, n_bits=32767))
corrupted = inject_single_bit_error(seq_str, 10000)
# 此时第 10000 位与原始序列不同
4.4.2 突发错误注入
模拟实际链路上的突发噪声:
def inject_burst_errors(sequence, start_pos, burst_len):
"""从 start_pos 开始连续翻转 burst_len 个比特"""
seq_list = list(sequence)
for i in range(start_pos, min(start_pos + burst_len, len(seq_list))):
seq_list[i] ^= 1
return ''.join(str(b) for b in seq_list)
这里要注意:你注入的错误用什么模式,接收端的误码检测器就要用相应的参考序列做比对。说白了就是收端得知道发端长什么样,不然没法定位误差。
4.5 同步过程与误码率计算
收端怎么知道序列从哪里开始的?这就需要同步机制:
- 滑动相关法:收端用已知的 PRBS15 序列与接收信号做滑动互相关,峰值位置就是起始点。这种方法硬开销大,不适合实时。
- 本地 LFSR 自同步:收端也运行一个 LFSR,检测到连续 N 个比特匹配后认为同步成功。N 一般取序列长度的 5~10%,也就是 1600~3200 比特。
同步成功后,逐比特比较就能计算 BER:
ber = 错误比特总数 / 比较的总比特数
# 一般要求 BER < 1e-12 才算链路合格
在 25Gbps 下要达到 1e-12 的置信度,需要连续测 10¹³ 个比特以上——也就是大约 6 分钟不间断测试。
4.6 知识结构图
下面这张图梳理了 PRBS15 的核心要素与测试流程之间的关系。我建议你把它打印出来贴在工位上。
4.7 常见问题与避坑指南
最后分享几个我在实际项目中反复遇到的坑:
- 种子不同步:发端用 0x7FFF,收端默认初始化用了 0x0001——明明发的是 PRBS15,收端却开始匹配 PRBS7 的模板。这种低级错误在测试报告里特别难查。
- 序列截断问题:有些仪表在注入 PRBS15 时,不做整序列循环,而是反复截前 10000 位重复。这种非自然序列会让接收端的 CDR 误判。务必确认仪表能完整产生 32767 位序列。
- 误码计数乌龙:别忘了串行链路有 8b/10b 或者 64b/66b 编码。你在编码后的比特流里注误码,真正起作用的可能是编码符号。我个人习惯先在编码前(原始的 PRBS15 比特流)做错误注入,这样定位更准。