PRBS生成实现:线性反馈移位寄存器(LFSR)原理
PRBS怎么做出来的?说白了,核心就是线性反馈移位寄存器——LFSR。很多同学一听“线性”“反馈”就觉得难,其实它就是个会自己“算”的移位器。我刚开始接触时也觉得神秘,后来自己搭过一次电路,立马就明白了。
1. 什么是LFSR?
LFSR由一组移位寄存器和一组反馈逻辑组成。寄存器存储当前状态,每个时钟周期向右移一位,同时按照预设的多项式把某些位的值异或(XOR)后反馈到输入端。结果就是一串看起来随机的比特流。
2. 多项式与抽头位置
每个LFSR对应一个本原多项式(primitive polynomial)。比如我常用的PRBS7,多项式就是 \(x^7 + x^6 + 1\)。这里的7表示寄存器级数,\(x^6 + 1\) 表示从第6级和最后一级的输出做异或,反馈回去。
| PRBS阶数 | 寄存器级数 n | 本原多项式(常见) | 最大序列长度 |
|---|---|---|---|
| PRBS7 | 7 | x⁷ + x⁶ + 1 | 2⁷ − 1 = 127 bits |
| PRBS9 | 9 | x⁹ + x⁵ + 1 | 2⁹ − 1 = 511 bits |
| PRBS15 | 15 | x¹⁵ + x¹⁴ + 1 | 2¹⁵ − 1 = 32767 bits |
| PRBS23 | 23 | x²³ + x¹⁸ + 1 | 2²³ − 1 ≈ 8.39M bits |
| PRBS31 | 31 | x³¹ + x²⁸ + 1 | 2³¹ − 1 ≈ 2.15G bits |
3. 两种实现结构:Fibonacci vs Galois
同样是LFSR,电路结构有两种流派。你能在教科书里找到,但实际写代码时我只用其中一种。
- Fibonacci(外部反馈型):多个抽头值异或后从输入端进入。寄存器是串行移位,反馈逻辑在外部。好处是直观,坏处是反馈路径较长,高频下时序容易出问题。
- Galois(内部反馈型):异或门插在每个抽头对应的寄存器之间。反馈信号从最后一节出来,直接“跳”到中间各级。我以为这更难理解,但实际做高速设计时Galois性能更好,寄存器之间的关键路径更短。
我个人推荐用Galois结构写RTL代码——真的不容易出现时序违例。你也试试看。
4. 初始状态(种子)与全零陷阱
不管哪种结构,初始值不能全零。为什么?如果所有位都是0,异或来异或去还是0,序列卡住了。这点在调试时坑过不少人。
选种子有讲究吗?除了避开全零,任何非零种子产生的序列只是相位不同,伪随机统计特性完全一样。你想想看,长度、游程分布都不变。
5. 序列长度与最大周期
n级LFSR最大周期是2ⁿ − 1。为什么减1?因为全零状态不出现。比如PRBS7有127个比特后开始重复。这就是它的“帧长度”。在实际误码率测试中,我们通常让PRBS连续跑,等接收端同步后再统计误码。
6. 实战:用Python生成PRBS7
不说虚的,上代码。这是我最常用的Galois型LFSR实现,用来验证链路性能。
def prbs7(seed=0x7F, n_bits=127):
"""
生成 PRBS7 序列(Galois 结构)。
多项式: x^7 + x^6 + 1 → 抽头位置 6,7
"""
lfsr = seed & 0x7F # 只保留低7位
output_bits = []
for _ in range(n_bits):
# 输出最低位
out_bit = lfsr & 1
output_bits.append(out_bit)
# 反馈计算:抽头 (bit6) XOR (bit6 与 bit7 的异或)
# Galois: 移位后,若移出的位为1,则异或到抽头位置
lsb = lfsr & 1
lfsr >>= 1
if lsb:
lfsr ^= 0x43 # 0x43 = 0b1000011 对应抽头7和6
return output_bits
# 试一下
seq = prbs7()
print(' '.join(str(b) for b in seq[:20]), '...')
跑一下,输出前20位:1 1 1 1 1 1 0 0 0 0 0 0 1 0 1 1 0 1 0 1。你自己试试看,是不是和眼图仪上测到的一样?
0x43代表从高位算起的抽头位置。不同参考资料的多项式表示方向可能相反,写代码时务必确认你的位序。
7. LFSR结构图
下面这张图是PRBS7(Fibonacci结构)的示意图。红色方框是寄存器,蓝色是异或门,箭头表示数据流向。
8. 避坑清单
- 种子不能全零——上面说了,反复说
- 多项式务必用本原多项式,否则序列周期会短很多,误码率测试结果失效
- 注意位序:文档里写“抽头7和6”,但实际硬件寄存器索引定义可能相反。我吃过这个亏,仿真和实测对不上。
- Gilois结构里异或门的插入位置要算对。有个取巧的办法:移位后如果移出的最低位是1,才去异或抽头掩码。
- 序列同步:接收端必须知道种子和多项式才能恢复出参考序列。通常发送端先发一段同步头(比如全1),接收端用相同LFSR锁定相位。
9. 回头看:为什么LFSR这么流行?
因为它硬件成本极低,几个触发器和异或门就能生成很长很长的伪随机码。相比查表存序列,LFSR不占存储。相比纯软件计算,它天生适合并行。你在FPGA或ASIC里用,一个时钟出一位,吞吐量极高。
我自己做高速SerDes测试时,就靠一个PRBS31的LFSR模块压链路。跑了三天三夜,误码率测到10⁻¹²级别,稳得很。你想想看,如果不是LFSR,光存32G比特的序列就要多少内存?