Polling 状态:比特锁定、符号锁定与块锁定

讲 Polling 状态之前,我先说说我早年的一个经历。

有一次调试 Gen3 板卡,链路老是起不来。抓了无数 trace,最后发现是 Polling 阶段的比特锁定根本没完成。但示波器上看波形,信号明明在跳。你想想看,要是连最基本的 0/1 边界都分不清,后续的符号锁定和块锁定就全是空中楼阁。那次排查花了我整整三天,所以今天这段,我希望你一次性搞明白。

Polling 状态,说白了就是链路初始化的第一阶段,也是接收端第一次看到高速串行数据。发送端这时候不管三七二十一,先发 TS1 或者 TS2 有序集(具体看子状态)。接收端呢,则要在这个阶段完成三件事:比特锁定、符号锁定、块锁定。我习惯把这三件事称为“锁定的三连击”——一步没锁住,后面全白搭。

比特锁定:找到时钟的节拍

比特锁定的目标是什么?就是让接收端的时钟恢复电路(CDR)能跟发送端的比特流对上频率和相位。这是一个纯物理层的过程,跟协议没关系,只看电气信号。

你可以想象一下:双通道示波器上,一个通道接发端时钟,一个通道接接收数据,你手动调整时基,让两个波形稳定下来——CDR 就是在干这事,只不过它靠反馈环路自动完成。

核心事实:比特锁定完成的条件是 CDR 能持续跟踪数据流中的比特边界,并且在 40 个连续 bit 时间内没有产生失锁告警。

我在项目中遇到过一种情况:链路跑在 Gen3(8 GT/s)时,PCB 走线太长导致信号衰减太大,CDR 始终没法锁定。怎么办?要么加 Re-timer,要么优化走线。没有别的捷径。

锁定阶段 输入数据 锁定标志 典型时长
比特锁定 模拟信号(连续 bit 流) CDR 锁定指示 几微秒到几十微秒
符号锁定 已并行化的 bit 流 COM 符号对齐 几十个符号周期
块锁定 符号流 Valid EDB / CRC 校验 128 个符号周期

注意:比特锁定不依赖任何协议内容。换句话说,哪怕发的是全 0 或全 1,如果信号翻转频率足够,CDR 照样能工作。PCIe 为了保证这一点,在 Polling 阶段会强制发送特定有序集,保证数据有足够的翻转密度。

符号锁定:找到字符的边界

比特锁定搞定之后,接收端拿到了一串连续的 0 和 1。但问题来了:你怎么知道从哪一位开始是第一个符号?比如 8b/10b 编码下,一个符号占 10 个 bit。如果接收端切分的位置比发送端偏移了 2 bit,那么后面所有的符号都是错的。

符号锁定解决的就是这个问题。接收端在 bit 流中寻找特定的“锚点”符号——COM 符号(逗号符号)。COM 符号的 10b 编码是唯一的,无论它出现在 bit 流的什么位置,接收端只要检测到它的图案,就能反推出符号边界。

我个人习惯把 COM 符号理解为“尺子的刻度”。你在卷尺上看不到毫米线的时候,整把尺就没法用——COM 符号就是这个刻度。

经验之谈:如果你在调试时发现链路卡在 Polling.Active 转不到下一状态,十有八九是符号锁定失败了。这时候可以检查一下 COM 符号的 K 字符序列是否被其他噪声图案误匹配了。我曾经见过因为 DC 不平衡导致 COM 符号被歪曲,CDR 锁了但符号锁不了。

符号锁定完成后,接收端就能正确地“切”出每一个符号。这个阶段还会同步做一件事——极性校正。PCIe 允许发送端在差分对上的极性任意交换,接收端通过检测符号的极性,在内部做自动翻转。如果符号锁定了但极性没校正对,后续的 TS1 有序集内容就会全部颠倒——这在调试时特别坑人,因为你抓到的数据全是反的。

块锁定:找到数据块的边界

块锁定只在 Gen3 及以上的 PCIe 版本存在。因为 Gen3 从 8b/10b 切换到了 128b/130b 编码,接收端的工作方式也变了。

在 128b/130b 编码下,数据按 130 bit 一个块传输,其中前 2 bit 是同步头(Sync Header),后 128 bit 是有效数据。块锁定的目标,就是找到每个块的起始位置——也就是这 2 bit 同步头在哪。

同步头只有两种合法值:01(数据块)和 10(有序集块)。接收端在符号锁定后的 bit 流中,以 130 bit 为窗口,检查每 130 bit 中前 2 bit 是否匹配 01 或 10。如果连续多个窗口都匹配,就认定块锁定成功。

避坑指南:我曾经在 Gen3 的测试中遇到一个问题——接收端报告块锁定成功,但链路却总是不稳定。后来发现,是板上的电源噪声导致偶尔的同步头被误判。规范要求连续检测到 128 个合法同步头才能确认块锁定,但有些 IP 实现偷了懒,只做了 16 个。你想想看,16 和 128 之间差了一个数量级,误判率自然就高了。所以在选 PCIe IP 时,记得问一句:“你们的块锁定窗口有多宽?”

块锁定完成之后,接收端就能正确地解析每一个块,知道哪一段是数据,哪一段是控制命令。这时候,Polling 状态的核心任务就算完成了。接下来链路就可以转入下一阶段——Polling.Configuration,做一些简单的配置事务交换,然后进入 Detect 或 Configuration。

三个锁定的依赖关系:
  1. 比特锁定 → 符号锁定 → 块锁定(Gen3 及以上)
  2. 前一个锁定没完成,后面不可能成功
  3. 每个锁定阶段都有超时保护——规范建议在 48 ms 内完成所有锁定

下面我画了一张图,把这几个锁定的关系理一下。你扫一眼就能明白。

Polling 状态锁定流程 Polling.Active 比特锁定(Bit Lock) CDR 时钟恢复 / 40 bit 无失锁 符号锁定(Symbol Lock) COM 符号对齐 / 极性校正 块锁定(Block Lock) 128b/130b 同步头检测 Polling.Configuration 退出 锁定依赖关系 1. 比特锁定不能跳过 2. 符号锁定依赖比特锁定 3. 块锁定(Gen3+) 依赖符号锁定 4. 超时时间 < 48 ms

从图上你能看到:Polling 是一个严格的串行过程。比特锁定是基础,符号锁定是承上启下,块锁定则是 Gen3+ 的额外关卡。每个锁定阶段都有一套硬件状态机在背后运转,你不需要关心它的每个微状态,但必须知道它锁什么、锁多久、锁不上会怎样。

嗯,关于 Polling 状态的三级锁定,我们就聊到这。这块内容虽然基础,但如果你能理清“比特→符号→块”这条链,后续的 Equalization 协商就好理解多了——因为 Equalization 本质上也是在信号质量上做“锁定”,只不过维度和方式完全不同。