Configuration状态:链路宽度与配置参数
Configuration状态,说白了就是链路双方坐下来“谈参数”的阶段。前面Detect和Polling搞定了物理连接和位锁定,但链路到底用多宽、每笔传输能扛多大载荷,这些都得在Configuration里敲定。我当年刚开始调PCIe时,总觉得这步走个过场就行——直到有一次板卡在Gen3下死活只能跑x1,折腾了两天才发现是Configuration阶段的协商时序出了问题。
Configuration状态在LTSSM里的位置
LTSSM进入Configuration之前,链路已经完成了Detect和Polling。进入Configuration后,会依次经历三个子状态:
- Configuration.LanenumWait – 确定链路宽度
- Configuration.Complete – 确认配置参数
- Configuration.Idle – 准备进入L0正常操作
任何子状态出错,都会跳回Detect或直接进入Recovery。所以这块儿是调试的“重灾区”。
链路宽度协商 – 从1 lane开始
链路宽度协商是Configuration最核心的活儿。你想想看,两边的物理层可能支持的lane数量不一样,比如Root Port支持x16,Endpoint只支持x4,那最终链路就是x4。这个“取最小值”的过程,就在LanenumWait里完成。
协商的基本思路很简单:
- 先以1 lane启动训练(所有lane都发TS1配置序列)。
- 每个lane上发的TS1会带上自身lane编号(lane number)。
- 接收端把自己收到的有效lane编号排个序,最大的那个编号+1就是链路的宽度。
- 双方交换完信息,确认一致,就锁定宽度。
关键点:所有lane在进入Configuration.LanenumWait时,必须先以x1宽度发送TS1。等到确定宽度后,再切换到最终宽度发TS2。如果某条lane在训练过程中收不到合法的TS1,它就会被“踢出”链路。
我遇到过一种情况:某个lane的PCB走线有阻抗不连续,导致TS1的CRC校验老是失败。接收端觉得那条lane不可靠,直接把它从宽度协商里拿掉了。结果链路从x8降到了x4,性能砍半。当时用PCIe分析仪抓训练序列,才发现那条lane上全是CRC error。
TS1/TS2配置序列 – 参数交换的载体
配置参数不是单独发报文交换的,而是嵌在TS1和TS2配置序列里。每个TS1/TS2序列有固定的格式,包含多个Symbol,其中几个字段专门用来传配置信息:
| 字段 | 含义 | 配置阶段 |
|---|---|---|
| Lane Number | 当前lane的编号(0~31) | LanenumWait |
| Max Payload Size | 设备支持的最大数据载荷 | Complete |
| Max Read Request Size | 设备支持的最大读请求大小 | Complete |
| Function Number | 功能编号(多Function设备用) | Complete |
嗯,这里要注意:Max Payload Size(MPS)和Max Read Request Size(MRRS)这两个参数,直接影响传输效率。如果两端不匹配,轻则性能下降,重则导致Uncorrectable Error。
我的习惯:调试时先用逻辑分析仪把TS1/TS2的原始数据抓下来,直接看MPS和MRRS字段。两边一对比,参数是否匹配一目了然。比看寄存器快多了。
Max Payload Size – 别小看这128B的差距
MPS决定了每笔TLP最多能带多少数据。可选值有128B、256B、512B、1024B、2048B、4096B。协商规则是“取两端支持的最小值”。
举个例子:Root Port支持到4096B,Endpoint只支持1024B,那最终MPS就是1024B。如果软件配置时把Root Port的MPS设为4096B,Endpoint设为1024B,但硬件协商时没对齐——那就等着报错吧。
我记得有一次帮客户调试NVMe SSD,顺序读性能死活上不去。查了半天,发现MPS被协商成了128B,而不是双方都支持的1024B。原因是对面的Root Port在Configuration.Complete阶段发送的TS2里,MPS字段被误写成了0(表示128B)。改完固件,性能直接翻倍。
Max Read Request Size – 被忽略的坑
MRRS和MPS类似,但它控制的是读请求的大小上限。很多工程师只关注MPS,忽略了MRRS。其实MRRS不匹配也会导致性能问题,甚至触发Completion Timeout。
为什么?如果读请求的大小超过了对端的MRRS,对端会用Multiple Completions返回数据。拆分太多,效率自然下降。更严重的是,如果中间有一笔Completion丢了,就得等Timeout重传。
避坑指南:我曾经因为MRRS没对齐,导致在一个高速采集系统里频繁出现Completion Timeout。现象是读数据时偶尔卡死,每次卡10秒(Timeout值)。那时候我还没意识到是MRRS的问题……后来用PCIe分析仪对比两端的TS2,才发现Root Port的MRRS是4096B,采集卡的MRRS是1024B。两边没协商到最小值,软件又没覆盖,就出了幺蛾子。
Configuration.Complete与Configuration.Idle
宽度和参数都谈妥后,链路进入Configuration.Complete。在这个状态,双方用最终确定的宽度发送TS2配置序列,并确认配置参数一致。确认无误后,进入Configuration.Idle,然后跳转到L0,正式开始传数据。
如果Complete阶段收不到合法的TS2,链路会退回LanenumWait重新协商。连续失败几次,LTSSM直接跳回Detect,链路复位重来。
一张图看懂Configuration状态
下面这张图总结了Configuration子状态机的主要路径和关键动作。建议你保存下来,调试时对照着看。
调试配置问题的三板斧
最后分享几个我在项目中常用的调试方法,遇到Configuration相关的问题,照着排查就行:
- 抓训练序列 – 用PCIe分析仪或逻辑分析仪抓TS1/TS2。重点看Lane Number、MPS、MRRS三个字段。两边一对比,问题基本就锁定了。
- 看状态寄存器 – 读取LTSSM状态寄存器(通常在下游设备的PCIe配置空间),看当前卡在哪个子状态。如果反复在LanenumWait和Complete之间跳,说明协商不稳定。
- 测信号质量 – 宽度协商失败的根源往往是信号完整性问题。用示波器测有问题的lane的眼图、抖动、共模电压。我排查的那个x8掉x4的案例,最后就是用示波器发现有一条lane的共模电压漂了200mV。
一个小技巧:如果你没有分析仪,也可以先把对端设备强制成x1(通过配置寄存器或strap pin),看能否正常通信。如果能,说明问题出在宽度协商上;如果不能,可能是更底层的物理层问题。这叫“分治法”,屡试不爽。
总的来说,Configuration状态是LTSSM里最具“谈判”色彩的一环。宽度协商靠TS1,参数交换靠TS2,两边各退一步,才能达成共识。调试时别光看寄存器,训练序列才是真正的“第一现场”。