8b/10b 编码

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.yx 取值范围 0~31y 取值范围 0~7。对应 256 个数据符号。IBM 8b/10b 还额外定义了 12 个控制符号,记作 K.x.y,可能的值有 K.28.0K.28.1K.28.2K.28.3K.28.4K.28.5K.28.6K.23.7K.27.7K.28.7K.29.7K.30.7

编码器的工作流程如下,注意这里 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 编码表算出。

5b/6b 编码表

| 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 等几行同时适用与 DK。另外我们可以看出,编码之后的结果可以分为 3 种:1 比 0 多两个的(+2),0 比 1 多两个的(-2),0 和 1 一样多的(0)。

3b/4b 编码表

| 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.P7D.x.A7。这是因为 IBM 在这里的设计没有那么精巧,因此需要特别地处理,以防止出现连续 5 个 0 或是 1。D.x.A7 只在 x = 17,18,20; RD = −1x = 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,那么编码肯定是 +20;如果编码前 RD = +1,那么编码肯定是 -20,这是 8b/10b 编码可以保持 0,1 数量平衡的一个解释。

有两个编码需要特别说明一下:D.07.yD.x.3。它们都是平衡编码,却有两种输出。而其余的平衡编码都只有一个输出。

一个例子

例如我们对 0xDEAD 进行 8b/10b 编码,0xDE = 11011110 = D.30.60xAD = 10101101 = D.13.5

  1. 首先初始 RD = -1
  2. 查 5b/6b 表 D.30.y 一行得到编码后为 011110RD = +1
  3. 查 3b/4b 表 D.x.6 一行得到编码后为 0110RD = +1
  4. 查 5b/6b 表 D.13.y 一行得到编码后为 101100RD = +1
  5. 查 3b/4b 表 D.x.5 一行得到编码后为 1010RD = +1
  6. 最终得到结果:011110 0110 101100 1010(从左至右发送,空格只是为了方便看)。

解码器

解码器接收符号流,将其还原为字节流。

字节定位

K.28.1K.28.5,以及 K.28.7 通常被用作“逗号符”,用于辅助字节定位。在很多采用 8b/10b 的协议中,这几个符号会被周期性的发送。

解码器状态机

解码器设计的复杂程度取决于需求,实际上解码器可以没有状态机,依旧可以通过反向查表还原出数据。但 RD 状态机可以帮助校验出违背 RD 规则的错误。


© 2021 Kele, CC BY-NC-SA 4.0