8b/10b 编码是一种线编码,它将 8-bit 数据信息和 1-bit 控制信息编码为 1 个 10-bit 的符号。主要的作用在于保持数据流中的直流平衡,即 0 和 1 的数量差不多相等。目前常用的 8b/10b 编码表是 IBM 最早在 1983 年提出的(A DC-Balanced, Partitioned-Block, 8B/10B Transmission Code),目前仍然应用在很多通信总线中。经过编码之后再传输显而易见的好处有:
简单来说 8-bit 的信息 HGFEDCBA
被编码成 10-bit 的 abcdeifghj
,并且按从 a 到 j 的顺序发送出去;其中低 5-bit EDCBA
被编码成 6-bit abcdei
;高 3-bit HGF
被编码成 4-bit fghj
。
8-bit 数据信息被记作 D.x.y
, x
取值范围 0~31
; y
取值范围 0~7
。对应 256 个数据符号。IBM 8b/10b 还额外定义了 12 个控制符号,记作 K.x.y
,可能的值有 K.28.0
、K.28.1
、K.28.2
、K.28.3
、K.28.4
、K.28.5
、K.28.6
、K.23.7
、K.27.7
、K.28.7
、K.29.7
、K.30.7
。
RD = -1
和 RD = +1
。RD 意为 Running Disparity,分别表示经过之前编码,线上的“0 比 1 多”和“1 比 0 多”;RD = -1
。编码器的工作流程如下,注意这里 5b/6b 与 3b/4b 编码被合并成了一步::
rd = -1;
for i = 0:length(instream)
input = instream(i);
data = input.data;
isK = input.isK;
[sym, rd] = 8b10b_encode(data, isK, rd);
outstream[i] = sym;
end
8b/10b 编码可以被拆成两步:
function [sym, rd] = 8b10b_encode(data, isK, rd)
[sym(0:5), rd] = 5b6b_encode(data(4:0), isK, rd);
[sym(6:9), rd] = 3b4b_encode(data(4:0), data(7:5), isK, rd);
end
注意 3b/4b 编码并不是仅仅和 data(7:5)
相关,它还和 data(4:0)
有关。8b/10b 结果可以用 5b/6b 编码表和 3b/4b 编码表算出。
| INPUT(EDCBA) | Output(abcdei) |
| Name | BIN | RD=-1 | RD=+1 |
| ------ | ----- | ------ | ------ |
| D.00.y | 00000 | 100111 | 011000 |
| D.01.y | 00001 | 011101 | 100010 |
| D.02.y | 00010 | 101101 | 010010 |
| D.03.y | 00011 | 110001 |
| D.04.y | 00100 | 110101 | 001010 |
| D.05.y | 00101 | 101001 |
| D.06.y | 00110 | 011001 |
| D.07.y | 00111 | 111000 | 000111 |
| D.08.y | 01000 | 111001 | 000110 |
| D.09.y | 01001 | 100101 |
| D.10.y | 01010 | 010101 |
| D.11.y | 01011 | 110100 |
| D.12.y | 01100 | 001101 |
| D.13.y | 01101 | 101100 |
| D.14.y | 01110 | 011100 |
| D.15.y | 01111 | 010111 | 101000 |
| D.16.y | 10000 | 011011 | 100100 |
| D.17.y | 10001 | 100011 |
| D.18.y | 10010 | 010011 |
| D.19.y | 10011 | 110010 |
| D.20.y | 10100 | 001011 |
| D.21.y | 10101 | 101010 |
| D.22.y | 10110 | 011010 |
| A.23.y | 10111 | 111010 | 000101 |
| D.24.y | 11000 | 110011 | 001100 |
| D.25.y | 11001 | 100110 |
| D.26.y | 11010 | 010110 |
| A.27.y | 11011 | 110110 | 001001 |
| D.28.y | 11100 | 001110 |
| A.29.y | 11101 | 101110 | 010001 |
| A.30.y | 11110 | 011110 | 100001 |
| D.31.y | 11111 | 101011 | 010100 |
| ------ | ----- | ------ | ------ |
| K.28.y | 11100 | 001111 | 110000 |
注意其中的 A.23.y
等几行同时适用与 D
与 K
。另外我们可以看出,编码之后的结果可以分为 3 种:1 比 0 多两个的(+2),0 比 1 多两个的(-2),0 和 1 一样多的(0)。
| Input(HGF) | Output(fghj) |
| Name | Bin | RD=-1 | RD=+1 |
| ------ | ----- | ------ | ------ |
| D.x.0 | 000 | 1011 | 0100 |
| D.x.1 | 001 | 1001 |
| D.x.2 | 010 | 0101 |
| D.x.3 | 011 | 1100 | 0011 |
| D.x.4 | 100 | 1101 | 0010 |
| D.x.5 | 101 | 1010 |
| D.x.6 | 110 | 0110 |
| D.x.P7 | 111 | 1110 | 0001 |
| D.x.A7 | 111 | 0111 | 1000 |
| ------ | ----- | ------ | ------ |
| K.x.0 | 000 | 1011 | 0100 |
| K.x.1 | 001 | 0110 | 1001 |
| K.x.2 | 010 | 1010 | 0101 |
| K.x.3 | 011 | 1100 | 0011 |
| K.x.4 | 100 | 1101 | 0010 |
| K.x.5 | 101 | 0101 | 1010 |
| K.x.6 | 110 | 1001 | 0110 |
| K.x.7 | 111 | 0111 | 1000 |
对于 D.x.7
,有两种可以选的结果:D.x.P7
与 D.x.A7
。这是因为 IBM 在这里的设计没有那么精巧,因此需要特别地处理,以防止出现连续 5 个 0 或是 1。D.x.A7
只在 x = 17,18,20; RD = −1
或 x = 11,13,14; RD = +1
时使用。同样的,3b/4b 编码之后的结果也分为 3 种(+2/-2/0)。
特别的,我们列出所有的控制符号,一共 12 种。
| Input (HGF EDCBA) | Output (abcdei fghj) |
| Name | DEC | HEX | BIN | RD=-1 | RD=+1 |
| ------ | --- | --- | --------- | ----------- | ----------- |
| K.28.0 | 28 | 1C | 000 11100 | 001111 0100 | 110000 1011 |
| K.28.1 | 60 | 3C | 001 11100 | 001111 1001 | 110000 0110 |
| K.28.2 | 92 | 5C | 010 11100 | 001111 0101 | 110000 1010 |
| K.28.3 | 124 | 7C | 011 11100 | 001111 0011 | 110000 1100 |
| K.28.4 | 156 | 9C | 100 11100 | 001111 0010 | 110000 1101 |
| K.28.5 | 188 | BC | 101 11100 | 001111 1010 | 110000 0101 |
| K.28.6 | 220 | DC | 110 11100 | 001111 0110 | 110000 1001 |
| K.28.7 | 252 | FC | 111 11100 | 001111 1000 | 110000 0111 |
| K.23.7 | 247 | F7 | 111 10111 | 111010 1000 | 000101 0111 |
| K.27.7 | 251 | FB | 111 11011 | 110110 1000 | 001001 0111 |
| K.29.7 | 253 | FD | 111 11101 | 101110 1000 | 010001 0111 |
| K.30.7 | 254 | FE | 111 11110 | 011110 1000 | 100001 0111 |
之前提到,编码器是有状态的,其状态为 RD,可以为 -1
或 +1
。状态跳转的规则可以从名字看出来:初始时状态为 -1
;经过了一次 5b/6b 或 3b/4b 编码之后,如果这次编码的结果导致 1 比 0 多了,那么状态跳转到 +1
;如果编码导致 0 比 1 多了,那么状态跳转到 -1
;如果这次编码是平衡编码,那么状态保持不变。关于编码器状态机跳转可能有 别的描述,但是都是等价的。
观察编码表,可以看出,如果编码前 RD = -1
,那么编码肯定是 +2
或 0
;如果编码前 RD = +1
,那么编码肯定是 -2
或 0
,这是 8b/10b 编码可以保持 0,1 数量平衡的一个解释。
有两个编码需要特别说明一下:D.07.y
和 D.x.3
。它们都是平衡编码,却有两种输出。而其余的平衡编码都只有一个输出。
例如我们对 0xDEAD
进行 8b/10b 编码,0xDE = 11011110 = D.30.6
,0xAD = 10101101 = D.13.5
。
RD = -1
;D.30.y
一行得到编码后为 011110
,RD = +1
;D.x.6
一行得到编码后为 0110
,RD = +1
;D.13.y
一行得到编码后为 101100
,RD = +1
;D.x.5
一行得到编码后为 1010
,RD = +1
;011110 0110 101100 1010
(从左至右发送,空格只是为了方便看)。解码器接收符号流,将其还原为字节流。
K.28.1
,K.28.5
,以及 K.28.7
通常被用作“逗号符”,用于辅助字节定位。在很多采用 8b/10b 的协议中,这几个符号会被周期性的发送。
解码器设计的复杂程度取决于需求,实际上解码器可以没有状态机,依旧可以通过反向查表还原出数据。但 RD 状态机可以帮助校验出违背 RD 规则的错误。
© 2021 Kele, CC BY-NC-SA 4.0