4. 前向纠错(FEC)机制

各位同学,今天我们来聊聊光通信里一个绕不开的话题——前向纠错,也就是FEC。说实话,没有FEC,现在这些几百G甚至T级别的传输,基本就是纸上谈兵。我入行那会儿,10G光模块刚起来,那时候大家对FEC还没那么依赖,觉得光口性能够用就行。后来速率往上走,眼图一关,全是雪花点,才意识到FEC不是锦上添花,是雪中送炭。

4.1 FEC基本原理

FEC的核心思想很简单:发送端在数据里加入一些冗余信息,接收端利用这些冗余来纠正传输中出现的错误。你想想看,光纤里的信号经过长距离传输,受到色散、非线性效应、噪声的影响,总会有几个比特翻车。如果没有FEC,那只能靠重传,这在实时通信里根本行不通。

我个人习惯把FEC比作「快递单号」。你寄一个包裹,快递单号本身不包含包裹内容,但万一包裹丢了,你可以凭单号去查。FEC的冗余比特就是那个单号,它不直接传输你的数据,但它能帮你把数据找回来。

关键指标:FEC的纠错能力用「纠后误码率」来衡量。比如一个FEC方案号称能把10-3的误码率降到10-15,那它的纠错能力就很强了。实际项目中,我们更关心的是「净编码增益」,也就是加了FEC之后,系统能容忍多差的光信噪比。

FEC分为两类:硬判决和软判决。硬判决就是接收端直接判0或1,然后纠错。软判决会保留一些概率信息,比如这个比特是0的可能性是70%,是1的可能性是30%。软判决的纠错能力更强,但代价是算法更复杂。我在一个400G的项目里用过软判决LDPC,那家伙的功耗和延迟,真是让人又爱又恨。

4.2 RS-FEC(Reed-Solomon)编码

RS码是FEC里的老前辈了,但老不代表过时。RS码的工作原理是把数据分成一个个符号(通常是8比特或10比特一个符号),然后对这些符号进行多项式运算,生成校验符号。接收端收到后,通过解多项式来定位并纠正错误。

RS码最经典的一个参数是RS(255, 239)。什么意思呢?就是每239个数据符号,加上16个校验符号,组成255个符号的码字。这个码字能纠正最多8个符号的错误。你想想看,255个符号里最多有8个符号出错,都能给你拉回来,这在工程上已经非常实用了。

我的经验:RS码在应对突发错误时表现不错。比如一个符号里的所有比特都错了,RS码只算一个符号错误,纠起来很轻松。但如果是随机比特错误,每个符号只错一两个比特,那RS码的效率就有点浪费了。我曾经在一个100G的项目里,一开始用了RS(255,239),后来发现信道里主要是随机噪声,果断换成了LDPC,效果立竿见影。

RS码的编码和解码都有成熟的硬件实现方案。编码器就是一个线性反馈移位寄存器,解码器稍微复杂点,需要做Berlekamp-Massey算法和Chien搜索。不过现在这些都有现成的IP核,我们做芯片架构的,更多是考虑怎么把这些IP核集成到数据流里,保证吞吐量不降。

// RS(255,239)编码示例(伪代码)
// 输入:239个数据符号
// 输出:255个符号(数据+校验)
function rs_encode(data[239]):
    poly = generate_generator_polynomial()
    remainder = divide_polynomial(data, poly)
    codeword = data + remainder
    return codeword

// 解码时,先计算伴随式,然后找错误位置和错误值
function rs_decode(received[255]):
    syndromes = compute_syndromes(received)
    if all syndromes == 0:
        return received  // 无错误
    error_locator = berlekamp_massey(syndromes)
    error_positions = chien_search(error_locator)
    error_values = forney_algorithm(error_positions, syndromes)
    corrected = received
    for pos, val in zip(error_positions, error_values):
        corrected[pos] ^= val
    return corrected

4.3 LDPC编码

LDPC码,全称低密度奇偶校验码,是现在高速光通信的标配。为什么?因为它的纠错能力接近香农极限,而且可以用软判决来进一步提升性能。说白了,LDPC就是用一个稀疏的校验矩阵来定义数据比特之间的关系,接收端通过迭代译码来找到最可能发送的码字。

LDPC的译码算法叫置信传播,也叫和积算法。这个过程有点像「邻居投票」:每个比特先根据信道信息给自己一个初始置信度,然后跟校验节点交换信息,反复迭代,直到所有校验方程都满足,或者达到最大迭代次数。我见过一个极端情况,迭代了50次才收敛,延迟直接飙到微秒级。所以在芯片设计里,我们通常限制最大迭代次数,比如8次或16次,在性能和延迟之间取个平衡。

注意:LDPC有一个「错误平层」问题。就是说当误码率降到一定程度后,再增加信噪比,纠错性能提升非常缓慢,像踩在平地上一样。这个问题在RS码里不太明显,但在LDPC里很常见。我曾经在一个项目里被这个坑过,后来通过优化码的结构和增加外码(比如加一个BCH码作为外码)才解决。

LDPC的码率也有很多种,比如7/8、15/16、17/18等。码率越高,冗余越少,开销越低,但纠错能力也弱。码率越低,纠错能力越强,但有效数据速率下降。在400G ZR标准里,用的就是oFEC,一种基于LDPC的码,码率大约是15/16,纠后误码率能到10-15以下。

FEC类型 码率 纠错能力 典型应用
RS(255,239) 239/255 ≈ 0.937 8个符号 10G/40G光模块
LDPC(7/8) 0.875 接近香农限 100G/200G
oFEC 15/16 ≈ 0.9375 极高 400G ZR

4.4 FEC增益与开销

FEC增益,说白了就是加了FEC之后,系统能容忍多差的光信噪比。比如不加FEC时,系统需要10dB的光信噪比才能达到10-12的误码率。加了FEC后,只需要8dB就能达到同样的误码率。那这个FEC的净编码增益就是2dB。嗯,这里要注意,净编码增益是扣除了FEC开销之后的增益,不是毛增益。

开销怎么算?很简单。比如RS(255,239),239个数据符号变成了255个符号,开销就是(255-239)/239 ≈ 6.7%。LDPC 7/8码,开销是(8-7)/7 ≈ 14.3%。开销越大,冗余越多,纠错能力越强,但有效带宽也越低。所以在实际系统里,我们得在增益和开销之间找平衡。

核心公式:净编码增益 = 编码增益 - 实现损耗 - 开销损耗。实现损耗来自编解码器的非理想性,比如量化误差、有限迭代次数。开销损耗来自冗余比特占用了带宽。一个好的FEC方案,净编码增益应该在5dB以上。

我记得有一次,一个客户非要我们用RS(255,239)来做200G的系统,说成本低。我算了一下,200G的线路速率,RS(255,239)的净编码增益只有3.5dB左右,根本不够用。后来我建议他们用LDPC 7/8,净编码增益能到6dB以上,虽然开销大了点,但系统余量足够。最后他们接受了我的建议,流片回来测试,效果很好。

最后说一句,FEC不是万能的。它只能纠正一定数量的错误,如果信道太差,错误超过了FEC的纠错能力,那就会出现「误码扩散」——越纠越错。所以在系统设计时,一定要给FEC留够余量,别把它的能力用到极限。我见过太多项目,为了省成本把光口功率压到极限,结果FEC天天报错,最后不得不返工。嗯,这个坑,你们千万别踩。