本页是介绍多吉比特收发器(MGT)的系列文章中的第三篇。
注意:如果你阅读本页是为了给 Xillyp2p 设置物理链路,那么本页与你无关:在这种情况下,应关闭所有编码功能,因为 Xillyp2p IP 核(IP core) 在内部实现了上述全部功能,并以端到端数据流的形式呈现给用户。
简介
应用逻辑的数据流几乎从来不会原样发送到物理信道上。如果直接这样做,极有可能导致 MGT 发生故障,原因在前面已经提到。在几乎所有应用中,MGT 或应用逻辑都会对数据流施加一种编码,以确保物理信道能够正常工作。这种编码还有助于发送端与接收端之间的同步。
不过也有例外:在这个完整设计示例中,MGT 被当作普通 SERDES 使用,不启用任何编码。之所以可行,是因为应用逻辑自己实现了加扰器(scrambler)以及其他必需的功能。
本页将介绍最常用编码技术的基础知识。上一页的协议列表把这些编码与 FPGA 之间使用的协议联系了起来。
8b/10b 编码
8b/10b 编码被多种协议使用,例如某些版本的 PCIe、千兆以太网、SATA、DisplayPort 和 SuperSpeed USB。所有 FPGA MGT 都内置了 8b/10b 编码器和解码器,尽管这种编码器也可以在逻辑结构(logic fabric)中实现。
这种编码把每个字节(8 位)转换成一个由 10 位组成的字。其主要目的是生成一个在平均意义上“0”和“1”数量相等的比特序列。换句话说,编码后的比特流是直流平衡的。另一个好处是,这种编码能确保数据流中“0”和“1”之间有足够频繁的跳变,使 CDR(时钟数据恢复)机制能够正常工作。
但要注意:8b/10b 编码并不会使数据流随机化。尤其是,如果使用了均衡器(equalizer),而该均衡器要求数据流在统计上是随机的,那么仅靠 8b/10b 编码是不够的。
为了实现直流平衡的目标,一部分 8 位字可以编码成两种候选形式。例如,8 位字 0x20(00100000,记为 D0.1)既可以编码为 1001111001,也可以编码为 0110001001。两种方案都会解码回 0x20。注意,第一种方案包含四个“0”位和六个“1”位;第二种方案则包含六个“0”位和四个“1”位。这就是 8b/10b 编码的核心思想:编码器有两种可选方案,一种方案中“0”多于“1”,另一种方案正好相反。
编码器利用这一点来保证长期看来“0”和“1”的平衡:它会记录到目前为止数据流中是“0”偏多还是“1”偏多。这种不平衡称为运行不一致性(Running Disparity)。编码器会选择有助于减小这种不平衡的 10 位字。
并非所有 8 位字都有两种候选编码。例如,8 位字 0x23(00100011,记为 D3.1)总是被编码为 1100011001。这个编码字中“0”和“1”的个数相同,因此不会影响运行不一致性。
这种编码最早发表于 1983 年,在直到大约 2010 年出现的协议中都非常流行。在较新的协议中则不那么常见。主要原因是,加扰器往往已经成为协议的一部分。数据流的直流平衡可以由加扰器来保证,因此不再需要 8b/10b 编码来实现这一目的。加扰器还能确保“0”和“1”之间具有足够频繁的跳变。另一方面,8b/10b 编码会给物理信道增加 25% 的额外比特;这些额外比特本来可以用来传输有效数据。
话虽如此,一些使用加扰器的成功协议也使用 8b/10b。例如,PCIe 的所有版本都要求使用加扰器,但 3.0 以下的 PCIe 版本也采用 8b/10b 编码。可见 8b/10b 编码显然还有其他好处。这就引出了下一个话题:K 符号(K-symbol)。
K 符号
8b/10b 编码器每编码一个 8 位输入,就会生成 10 位输出。因此,编码后的字共有 1024 种可能组合,而原始的 8 位字只有 256 种可能。即使假设每个原始字都有两种可能的编码字,那也最多只能覆盖 512 种组合。如前所述,有些 8 位字只有一种 10 位编码候选,因此 10 位字中的合法编码少于 512 种。由此可以得出,至少存在 512 种 10 位组合没有对应的 8 位字。
因此,8b/10b 编码可以通过检测非法的 10 位字来发现物理链路上的比特错误。FPGA MGT 提供了输出端口,用于在检测到非法 10 位字时通知应用逻辑。不过,这种差错检测机制的价值有限,因为它并不能检测出所有错误。
8b/10b 编码真正有价值的特点在于 K 符号。K 符号会经过编码并发送,用来代替某个 8 位字。解码器能够区分普通数据字和 K 符号,并且通常总有办法在 K 符号到达时通知应用逻辑。
因此,8b/10b 编码允许发送端在数据信道上发送额外信息,同时不会与普通数据混淆。协议常常利用这一功能帮助接收端与发送端的数据流实现同步。
K 符号的含义由具体协议决定。不过,有一种 K 符号在多种协议中的含义都相同:K28.5。它通常被称为逗号(Comma,缩写 COM),用于数据流同步。下文会进一步介绍。
当在 FPGA MGT 上启用 8b/10b 时,与应用逻辑的接口中每个数据字节都有一根单独的指示线,用于标明该字节是普通数据字节还是 K 符号。换句话说,发送数据的接口允许你选择每个要发送的字是普通数据还是 K 符号。而且,当一个数据字被标记为 K 符号时,这个 8 位数据字只能取以下这些值:
| 字节值(十六进制) | K 符号 |
|---|---|
| 1C | K28.0 |
| 3C | K28.1 |
| 5C | K28.2 |
| 7C | K28.3 |
| 9C | K28.4 |
| BC | K28.5 |
| DC | K28.6 |
| FC | K28.7 |
| F7 | K23.7 |
| FB | K27.7 |
| FD | K29.7 |
| FE | K30.7 |
同样,对 MGT 接收器来说,与应用逻辑的接口中每个接收到的字节也有一根单独的指示线,用于标明相应字节应解释为普通数据还是 K 符号。接收器还具备对 K28.5(逗号符号,缩写 COM)进行特殊处理的功能。下面就来讨论这个 K 符号。
逗号符号(K28.5)
K28.5 的 10 位表示是 0011111010 或 1100000101。这个序列特别有用,因为它只能由发送 K28.5 而出现在物理信道上。接收端并不需要先与发送端同步就能检测到 K28.5:只要最近收到的 10 位与这两个序列中的任何一个匹配即可。
这使得 K28.5 成为接收端同步的绝佳工具。一旦检测到这个 K 符号,其他 10 位字的边界也就确定了。
FPGA MGT 能够在收到 K28.5 符号时自动对齐到达的数据流。通常,还可以要求按照更大的边界来进行对齐。例如,可以把 MGT 配置成:到达的 K28.5 应对齐到某个 32 位字的起始位置。
除了用于对齐接收器之外,K28.5 符号还可以用来对齐其他需要同步的功能模块。例如,协议可以要求在收到该 K 符号时复位加扰器。
正因为这些可能性,协议往往要求定期发送 K28.5,以便接收端同步。通过握手建立连接的协议也常常把 K28.5 用作链路建立过程的一部分。无论如何,K28.5 符号都能大大简化协议设计,其价值足以抵消物理信道上多出 25% 额外比特的代价。
跳过符号(K28.0 和 K28.1)
在大多数实际场景中,发送端和接收端的时钟频率会存在差异。这就会带来一个问题:MGT 与应用逻辑之间的接口通常以接收端自己的本地时钟为基准。于是,数据按发送端时钟到达,却要按接收端本地时钟交付给应用逻辑。
换句话说,MGT 接收到的数据,其到达节奏由对端决定;而本地时钟决定数据的消耗速度。
如果发送端时钟频率更高,应用逻辑从 MGT 取数据的速度就会太慢。多余的数据会逐渐累积,最终在 MGT 内部的某个位置发生溢出(overflow)。反过来,如果发送端时钟频率更低,应用逻辑取数据的速度又会快于数据到达的速度。这同样是个问题。
解决这个问题的一个简单办法是插入“跳过符号”。例如,SuperSpeed USB 协议定义了一个“SKP 有序集合”(SKP Ordered Set),即两个连续的 K28.1 符号。协议要求每发送 354 个普通数据字,就要发送一次这两个 K28.1 符号。接收端 MGT 可以丢弃这些 K28.1 符号,而不把它们交给应用逻辑。这就解决了发送端时钟快于接收端时钟时多余数据的问题:当接收端 MGT 因时钟频率差异开始积攒数据时,它会跳过到达的 K28.1 符号。
借助这一机制,接收端应用逻辑在同一段时间内只取走 354 个字时,发送端可以发送 356 个字。由 356 / 354 ≈ 1.00565 可知,这种机制可以补偿两个时钟之间最高约 0.565% 的频率差。当然,这已经超过了协议规范所允许的范围。
接收端 MGT 也被允许在已有的“SKP 有序集合”中增加 K28.1 符号。这在发送端时钟较慢时会有所帮助。此时,接收端应用逻辑试图以快于数据到达的速度取数据;MGT 因而可以用 K28.1 符号来填补空缺的数据时隙。反正应用逻辑本来就应当忽略所有 K28.1 符号。
PCIe 协议也以类似方式使用跳过符号。PCIe 的 SKP 有序集合格式不同,它由四个 K 符号组成:先是一个 K28.5(逗号),然后是三个 K28.0 符号。不过,使用跳过符号的原理是一样的。
USB 协议和 PCIe 协议都使用缩写 SKP 来称呼各自的有序集合。注意,在 SuperSpeed USB 协议中,SKP 指的是 K28.1;而在 PCIe 中,SKP 指的是 K28.0。这很容易造成混淆。
64b/66b 编码
如前所述,8b/10b 编码对物理信道的利用不够高效。如果协议中已经使用加扰器,那么 8b/10b 编码的主要目的(直流平衡,以及保证“0”和“1”之间有跳变)就不再必要。这就是较新的协议采用 64b/66b 编码及类似方法的原因。不过要注意,64b/66b 实际上并不是一种真正的“编码”方法,更像是一种组织数据流和控制字的方式。数据并不会被转换成另一种表示形式;它只是被组织成若干段,并插入额外比特,以便实现同步和在信道上发送控制信息。
注意,K 符号只存在于 8b/10b 编码中,其他编码格式都不支持 K 符号。
64b/66b 编码被多种协议使用,例如 10G/100G 以太网。值得一提的是,PCIe 和 USB 的更高版本采用了其他方法,下文会讨论。
64b/66b 编码把数据流分成 64 位段,并在每一段开头附加两个比特。因此,物理信道上的每个段长度为 66 位。这两个额外比特只有两种可能取值:“01”或“10”。如果这两个比特是“01”,后面的 64 位就包含八个数据字节;如果这两个比特是“10”,接下来的一字节则含有一个代码,用于定义剩余 56 位的内容:其中有多少个数据字和多少个控制字。
控制字特别用于指示帧(数据包)的开始和结束,也可以用来插入空闲字,以补偿时钟频率差异(类似于 8b/10b 的跳过符号)。协议还可以定义自己专用的控制字。
如前所述,如果前两个比特是“10”,那么其余 64 位分为两部分:一个 8 位代码字,以及一个 56 位的数据/控制字组合。数据字和控制字在这 56 位内部的排列方式相当复杂,主要是因为每个控制字长度为 7 位。这种特殊安排是必要的,这样才能在其余 56 位中容纳最多八个控制字。
此外,使用这种编码的协议通常还会对数据施加加扰,以确保直流平衡和数据的随机性。这种随机性并不仅仅是为了均衡器:接收端也需要依靠它来找到入站数据流中 66 位段的起始位置。做法是检查每个 66 位段的前两个比特。如果每个段开头反复出现“01”或“10”,就说明接收端已经同步;否则,它就需要尝试其他位置。加扰器可以保证其余比特是随机的,因此,如果接收端没有对齐,它很快就会在非法位置上看到“00”或“11”,而不是合法的组合。
总而言之,64b/66b 能更有效地利用底层物理信道,但采用这种方法的协议实现起来更难。特别是,需要根据每个段的前两个比特(有时还要看随后的 8 个比特)对每一段采取不同的处理方式,这给实现增加了不少复杂性。不同 FPGA 的 MGT 在这方面提供的支持程度也各不相同。有些 MGT 内置了自动把接收端与到达的 66 位段对齐的机制。不过,解析同时包含数据和控制字的 66 位段这个更困难的任务,仍然要由应用逻辑来完成。
FPGA MGT 很可能内置一个齿轮箱(gearbox),用于从 66 位段流中提取 64 位字。关于齿轮箱,本系列的下一页会进一步讨论。
64b/67b、128b/130b 和 128b/132b
虽然 64b/66b 能更好地利用物理信道,但实现起来很复杂。这或许就是 PCIe 和 SuperSpeed USB 在放弃 8b/10b 时选择另一条路径的原因。
这两种协议都保留了把数据流分成若干段的思想,只是没有采用 64 位段,而是以 128 位为基础。
PCIe 3.0 及更高版本基于 128b/130b。与 64b/66b 编码类似,这种协议的每个 128 位段都以“01”或“10”开头,以区分数据和控制代码。SuperSpeed USB Gen 2 基于 128b/132b,其每个 128 位段以“0011”或“1100”开头,作用相同。唯一的区别是,USB 协议多分配了两个比特,即共四个比特,以便更好地防范比特错误。
与 64b/66b 编码的另一个共同点是,每个段中承载数据的 128 位主体部分都要经过加扰,以保证直流平衡和数据流随机化。但与 64b/66b 的相似之处到此为止:这两种协议各自按照自己的协议结构,来定义 128 位部分中内容的含义。所以,128b/130b 和 128b/132b 只表示数据流如何分段,以及在承载数据的部分上额外增加了多少位;其余一切都与具体协议相关。
64b/67b 编码的工作方式与此类似。区别在于,每个 64 位段之前会额外增加三个比特,用途相同。这种编码主要被 Interlaken 协议采用。
不使用编码
也可以完全不用上述任何编码方法来实现通信链路。这种协议的一个例子是 xillyp2p:它每隔 223 个比特使用一个同步字,用于对齐和同步接收端与发送端。对这个协议而言,MGT 只是一个 SERDES。这种策略的主要优点是,协议完全不依赖 MGT 的其他功能,因此更容易在各种 MGT 上实现。另一个优点是,MGT 的实例化(instantiation)会简单得多,如这个设计示例所示。
缺点是,一些本来可以由 MGT 内部完成的工作,现在占用了 FPGA 的逻辑资源。
只要协议本身已经涵盖了编码可能带来的各种需求,这种方法就是可行的:直流平衡、随机化(如需要时)、同步与字对齐,以及数据与控制信息的区分。
总结
如上面所见,对应用逻辑的数据流有着不同的编码方法,也可以完全不采用任何编码。对于大多数应用来说,8b/10b 编码最容易使用,但它对物理信道数据流来说比较浪费。这就是较新的协议采用其他编码方法的原因——这些方法效率更高,但使用起来要困难得多。
至此,关于 MGT 的本系列第三页就结束了。下一页将介绍 PCS 的若干组成部分,其中包括实现这里所提到的编码功能的模块。