PCIe 1.0 / 2.0 技术细节:链路宽度、速率与编码

说到PCIe的早期版本,我印象最深的就是刚入行那年,带着一块PCIe 1.0的FPGA开发板去客户现场联调。对方服务器用的是当时刚出的PCIe 2.0根联复杂,结果怎么都训练不上去。后来才发现,我对链路宽度协商的理解太天真了。嗯,从那时候起,我就把PCIe 1.0/2.0的底层参数记得死死的。

这一章咱们就捋清楚这两个版本到底改了什么、没改什么。你想想看,PCIe 1.0是2003年出来的,PCIe 2.0在2007年发布,间隔四年。表面上看只是速率翻倍,但底层很多机制也跟着变了。我个人习惯先把链路宽度、数据传输速率、编码方式这三个核心维度拆开,再串起来看。说白了,没有这几个参数,你根本算不出一根PCIe链路到底能跑多快。

链路宽度:从 x1 到 x32

PCIe最灵活的地方就是链路宽度。你可以只用一对差分线(一个发送对+一个接收对),也可以塞满32对。每一对差分线叫做一个Lane,每个Lane是双向的(各走各的线)。

我在早期项目里碰到过一个坑:供应商给的PCIe IP只支持x4,但我的FPGA设计里强行配成了x8。结果物理层训练一直失败,设备根本枚举不出来。后来查寄存器才发现,链路训练时两端的宽度能力必须匹配,而且必须从最高公共宽度往下协商。不是你写多少就能跑多少的。

链路宽度选项(PCIe 1.0 / 2.0):

  • x1 (最常用在简单的网卡、采集卡)
  • x2 (非常少见,我几乎没见过商用主板有这个规格)
  • x4 (常见于NVMe SSD、中端网卡)
  • x8 (高端网卡、RAID卡)
  • x12 (存在!PCIe 1.1 规范里就有,但我只用过一次,是某款FPGA开发板)
  • x16 (显卡/GPU的标配)
  • x32 (物理接口太大,只在特殊系统里出现,普通主板看不到)

注意,PCIe规范里x2、x12就在那里,但实际产品很少遵循。嗯,这就是理论标准和市场现实的差异。你设计板卡时,如果非要用个x2,等着你的就是找不到合适的连接器和布线参考。

数据传输速率:2.5 GT/s 与 5 GT/s

总算聊到速率了。PCIe 1.0单条Lane的数据传输速率是2.5 GT/s(Giga Transfer per second),也就是每秒钟传25亿次。到PCIe 2.0直接翻倍到5 GT/s。这里我要给你泼盆冷水:GT/s不是带宽。别搞混了。

带宽的计算要再考虑编码开销。PCIe 1.0/2.0都用8b/10b编码,就是说每10个bit里只有8个bit是真实数据,另外2个bit是用于直流平衡和时钟恢复的。所以,单条Lane的原始数据带宽(单向)是:

PCIe 1.0: 2.5 GT/s × 8/10 = 2.0 Gbps = 250 MB/s
PCIe 2.0: 5.0 GT/s × 8/10 = 4.0 Gbps = 500 MB/s

我之前有个同事做视频采集卡,死活觉得“5 GT/s”就是5 Gbps,结果算带宽时多算了一倍。流片回来才发现到不了满帧率。自己又被坑了一次。

重要提醒:PCIe速率是“Transfer”,不是“Bit”。一个Transfer在8b/10b编码下代表10个bit(其中8个数据bit)。千万别把GT/s直接当Gbps用。

编码:8b/10b 的来龙去脉

为什么非要浪费20%的编码开销?这件事我当年也觉得很亏。但等我研究过串行链路的信号完整性就明白了——时钟是从数据流里恢复的。如果你连续发很多个0或者很多个1,接收端锁相环就会丢掉锁定。8b/10b编码保证每个码字里0和1的数量之差不会超过2,还给了一堆K-code用于链路管理。

PCIe 1.0和2.0都用了同样的编码方案。编码表是固定的,由IBM的Albert Widmer和Bob Franaszek在80年代发明的。硬件里一般用查找表或者组合逻辑实现。我反正在FPGA里直接调IP核,没自己写过编码器。不过知道基本原理能帮你定位链路层报错。比如,CRC校验OK但链路老是重传,你可以怀疑是8b/10b解码器跑飞了。

小技巧:调试的时候,用逻辑分析仪看差分对信号,抓一个刚好是8b/10b码字的帧头——比如COM符号(K28.5)。如果能看到连续的K28.5出现,说明物理层训练已经过了电气空闲状态。

两代版本核心参数对比表

我整理了一张表,把PCIe 1.0和2.0的关键差异放在一起。你直接拿去用,做方案选型时对照着看。

参数 PCIe 1.0 / 1.1 PCIe 2.0 / 2.1
单Lane速率 2.5 GT/s 5.0 GT/s
编码方式 8b/10b 8b/10b
编码效率 80% 80%
单Lane单向带宽 250 MB/s 500 MB/s
x16单向带宽 4 GB/s 8 GB/s
时钟方案 参考时钟 + CDR 参考时钟 + CDR
可选SRIS(独立参考时钟)
物理层电源管理 L0s, L1, L2/L3(基础) L0s, L1, L1.1(新增强)
链路宽度范围 x1, x2, x4, x8, x12, x16, x32 同左(但x12实际更加少见)
最大Payload Size 4096字节(TLP粒度量) 4096字节,但推荐2048
参考规范 PCI Express Base 1.0a / 1.1 PCI Express Base 2.0 / 2.1

你可能会问,PCIe 2.0除了速率翻倍,还有什么实质变化?有的。它增加了主动状态电源管理(ASPM)的一些新状态,还引入了“去加重(De-emphasis)”技术来改善信号质量。不过这些属于物理层电气特性的范畴,咱们后面章节会细讲。

链路训练与协商流程简述

最后简单提一下链路是怎么建立起来的。两个PCIe设备刚上电时,谁都不知道对方支持什么宽度、什么速率。它们会经历一个状态机:Detect -> Polling -> Configuration -> L0。我画了一张简图帮你理解。

PCIe 链路训练流程(1.0 / 2.0 通用) 1. Detect(检测远端正点) 2. Polling(交换TS1/TS2序列) 3. Configuration(宽度 / 速率协商) 4. L0(正常数据传输) 关键点: • PCIe 1.0 协商最大 2.5 GT/s • PCIe 2.0 可协商至 5.0 GT/s • 宽度从 x1 逐级往上试 • 两端能力取交集 • 配置阶段完成链路编号 • 进入 L0 后即可收发 TLP • 失败则回到 Polling 或 Detect PCIe 1.0: 2.5 GT/s PCIe 2.0: 5.0 GT/s

这个训练过程在PCIe 1.0和2.0里基本一样。唯一区别是Polling阶段交换的TS1/TS2序列里包含了速率能力位。PCIe 2.0设备会在序列里声明自己支持5.0 GT/s,然后对方如果也支持,就协商到高速。如果不支持,就降回2.5 GT/s。嗯,后向兼容就是这么来的。

我遇到过最头疼的情况是PCIe 2.0显卡插在PCIe 1.0主板上。按理说应该降速协商成功,但由于早期主板电气性能差,连2.5 GT/s都跑不稳,导致链路反复重启。后来换了根好的连接器就好了。所以,做产品的时候别忘了留一点信号质量裕量。

注意:PCIe 2.0物理层引入了发射端去加重(De-emphasis),目的是在5 GT/s这么高的速率下补偿高频损耗。PCIe 1.0没有这个处理。如果你在PCIe 2.0插卡上关掉了去加重,链路的眼图可能会闭合,训练成功率大打折扣。