4. 编码技术:8B/10B与64B/66B编码原理与实现
编码技术,说白了就是给数据“穿衣服”。在SerDes系统中,原始数据不能直接扔到高速链路上传输,必须经过编码。为什么?因为接收端需要从数据流里恢复时钟,还要保证直流平衡。我最早接触SerDes时,觉得编码就是个“多余步骤”,直到有一次调试眼图,发现连续长0导致接收端失锁,才明白编码有多重要。
4.1 8B/10B编码原理
8B/10B编码,顾名思义,就是把8位数据映射成10位码字。多出来的2位,用来保证码流中0和1的数量尽量均衡。这个编码方案由IBM的Albert X. Widmer和Peter A. Franaszek在1983年提出,至今仍是许多高速接口的标准配置。
编码核心思想:
- 将8位数据拆成3位和5位两部分
- 分别用5B/6B和3B/4B编码表映射
- 引入“运行差异度”(Running Disparity, RD)控制极性
我个人习惯把8B/10B编码理解成“平衡木”游戏。每次发送一个码字,都要计算当前码流中1比0多几个(RD值)。如果当前RD为正,就选择让1变少的码字;反之亦然。这样来回调整,保证长期来看0和1的数量差不超过2。
4.2 8B/10B编码实现
实际工程中,8B/10B编码通常用查找表(LUT)实现。我曾在FPGA项目中手写过编码表,128个数据码字加上12个控制码字,总共140个映射关系。嗯,这里要注意,控制码字(K码)用于帧对齐和链路管理,不能和数据码字混淆。
// 8B/10B编码核心逻辑(Verilog示例)
// 输入:data_in[7:0], k_in(是否为控制码)
// 输出:code_out[9:0], rd_out(下一状态RD)
always @(posedge clk) begin
if (reset) begin
rd_current <= 0; // 初始RD为负
end else begin
// 根据当前RD选择编码表
case ({k_in, data_in[7:0]})
10'h1BC: code_out = rd_current ? 10'b : 10'b; // K28.5
// ... 其他映射
endcase
// 更新RD值
rd_out = rd_current ^ (ones_count(code_out) > 5);
end
end
我在项目中遇到过一个问题:编码表用组合逻辑实现时,时序容易紧张。后来改成流水线寄存器,分两拍完成编码,时序就收敛了。你想想看,10位码字要在一个时钟周期内查表并更新RD,确实有点赶。
4.3 64B/66B编码原理
64B/66B编码是8B/10B的“升级版”。它把64位数据块编码成66位码块,开销从25%降到3.125%。为什么能做到?因为64B/66B不再逐位平衡,而是用同步头(Sync Header)来标识数据块类型。
编码结构:
- 同步头(2位):01表示数据块,10表示控制块
- 数据块(64位):原始数据直接传输
- 控制块(64位):包含8个8位控制码字
说白了,64B/66B编码就是“偷懒”了。它不再保证每个码字都平衡,而是靠扰码(Scrambling)来打散数据模式。我刚开始觉得这不靠谱,但仔细一想:8B/10B的25%开销在100Gbps链路上意味着25Gbps的浪费,而64B/66B只浪费3.125%,省下来的带宽很可观。
4.4 64B/66B编码实现
64B/66B的实现比8B/10B简单,但多了扰码器。标准IEEE 802.3ae定义了多项式x^58 + x^39 + 1的扰码器。我建议用LFSR(线性反馈移位寄存器)实现,注意初始种子要随机化。
// 64B/66B编码核心逻辑
// 输入:data_in[63:0], type(0=数据,1=控制)
// 输出:code_out[65:0]
always @(posedge clk) begin
// 同步头生成
code_out[65:64] <= (type == 0) ? 2'b01 : 2'b10;
// 数据扰码(使用LFSR)
scrambled_data <= data_in ^ lfsr_out[63:0];
// 控制块编码(将8个控制码字打包)
if (type == 1) begin
code_out[63:0] <= encode_control_blocks(ctrl_in[63:0]);
end else begin
code_out[63:0] <= scrambled_data;
end
// 更新LFSR
lfsr_out <= {lfsr_out[57:0], lfsr_out[58] ^ lfsr_out[39]};
end
4.5 编码效率对比
编码效率,就是有效数据比特占总传输比特的比例。我整理了一个对比表,方便你直观理解:
| 编码方案 | 开销 | 效率 | 最大游程 | 直流平衡 | 典型应用 |
|---|---|---|---|---|---|
| 8B/10B | 25% | 80% | 5 | 优秀 | PCIe 1.0/2.0, SATA, USB 3.0 |
| 64B/66B | 3.125% | 96.97% | 取决于扰码 | 良好(需扰码) | 10GbE, 25GbE, 100GbE |
| 128B/130B | 1.56% | 98.46% | 取决于扰码 | 良好(需扰码) | PCIe 3.0/4.0, 400GbE |
从表中可以看出,8B/10B效率最低但最可靠,适合对误码敏感的场景。64B/66B效率高,但依赖扰码器来保证数据随机性。我个人的经验是:如果链路预算充足,优先用8B/10B;如果追求高带宽利用率,选64B/66B。
4.6 知识体系结构图
下面这张图展示了编码技术在SerDes系统中的位置和相互关系:
这张图展示了编码技术在SerDes系统中的完整流程。从原始数据出发,根据应用场景选择编码方案,然后通过不同的实现方式生成编码后的数据,最终送入串行化模块。我个人建议,在做芯片验证时,要重点关注编码器与串行器之间的接口时序,这里经常出问题。
好了,关于编码技术就聊到这里。8B/10B和64B/66B各有千秋,选择哪种取决于你的带宽需求和链路质量。我在实际项目中,通常先用8B/10B做原型验证,等链路稳定后再切换到64B/66B提升效率。记住,没有最好的编码,只有最合适的编码。