4、Detect状态详解:如何检测对端链路存在

Detect 阶段,说白了就是链路的「第一声招呼」。

你的 PCIe 设备上电之后,并不知道对面有没有东西等着握手。它得先发射一个信号出去,看看能不能收到回音。这个「回音」的检测,就是 Detect 状态要干的事。

我记得刚接触 PCIe 时,总觉得 Detect 就是简单地量量电压。后来踩了坑才发现,这里头藏着不少小把戏。

4.1 电气检测的基本原理

PCIe 的物理层工作时,发送端和接收端之间有一对交流耦合电容。这个电容通常在哪?在发送端附近。信号经过这个电容之后,直流分量就被隔掉了。那么问题来了——你怎么知道对端有没有接一个接收器?

答案是:看直流偏置会不会被拉低。

简单说说过程。发送端在 Detect 状态会做两件事:

  • 先把自己的 TX 引脚置为高阻态(Hi-Z),不驱动任何电平
  • 然后拉一个很小的电流源到 TX 引脚上,大概几十微安的量级

如果对面接了接收端,那么接收端内部的 50Ω 下拉电阻会把 TX 引脚上的电压拉低到接近 0V。如果对面是空的,TX 引脚上的电压会被电流源推上去,接近电源电压。检测这个电压的高低,就能知道对端是否存在。

关键点:Detect 状态不是靠看数据信号有没有收到,而是靠检测直流负载的存在。这就有点像你去敲门——听不到声音没关系,但门有没有挡着手,你立刻能感觉到。

4.2 接收器端接检测

讲完了基本原理,咱们再深入一点。PCIe Gen1/Gen2 用的是 50Ω 单端阻抗(差分对各 50Ω 对地)。Gen3 之后改成了共模端接,但 Detect 的原理没变——还是看负载。

接收器内部的 RC 端接网络长什么样?我画了个简化图:

TX 芯片                      RX 芯片
┌─────────┐   交流耦合电容    ┌─────────┐
│         ├─────||──────────┤         │
│ 电流源  │                  │ 50Ω     │
│ (几十μA)│                  │ 下拉到GND│
│         │                  │         │
└─────────┘                  └─────────┘

电流源拉了大约 20μA 的电流出来(不同厂家有差异)。如果对端有 50Ω 落地,那电压 = 20μA × 50Ω = 1mV。嗯,非常低,接近 0V。如果对端什么都没接,那电流源没有回路,电压被拉到 VDD 附近。

但这里有个陷阱——交流耦合电容的存在。在直流下,这个电容是开路的。所以实际上 Detect 检测的不是整个接收链路,而是电容之后的那段 TX 引脚被 RX 的直流端接拉低的过程。你想想看,电容把直流隔断了,电流怎么流过去的?

其实,Detect 阶段电流源工作时的频率很低(本质上就是直流偏置),电容对直流来说等于断路。这时候电流源的路径是这样的:

  • 从 TX 芯片流出 → TX 引脚
  • 经过 PCB 走线 → 耦合电容的一端
  • 电容的另一端对直流是开路,但这时候 RX 的端接网络会通过寄生反向过来的微弱电流
  • 更准确的说法是:PCIe 的 PHY 在 Detect 时会把 TX 和 RX 引脚内部短暂短接,形成一个反射波测试

我再说清楚点。现在的 PCIe PHY 很少用纯直流电流源来检测了。大多数用的是「反射法」:

PHY 发一个很窄的脉冲到 TX 引脚上。如果引脚另一端接了接收器(50Ω 到地),阻抗匹配,信号被吸收,反射电压很小。如果引脚空载,信号全反射回来,PHY 会看到一个大信号。通过比较发射和反射的能量,就能确定对端是否存在。

个人习惯:做板级调试时,如果 Detect 一直过不去,我喜欢先用示波器看 TX 引脚的直流电平。如果引脚电压在 300mV 以下,多半是接收端端接没问题;如果引脚电压接近电源轨(1.8V 或 3.3V),接收端可能没上电、或者焊错了电阻。我踩过一次坑——板子回来说链路不通,查了半天发现是接收端的 50Ω 电阻虚焊了。

4.3 检测超时与状态转换

Detect 不会无限等下去。PCIe 协议规定了一个超时机制:

参数 典型值 说明
Detect 等待超时 12 ms 最长等待对端响应的时间
电气检测脉冲宽度 几十 ns ~ 几百 ns 反射法使用的探测脉冲
检测成功判定 连续 2~4 次检测到负载 防止毛刺误判
超时后的重试次数 无限制(连续重试) 直到检测到对端或复位

超时后 PCIe 不会报错,不会宕机。它会做什么?它会重新回到 Detect 状态,再来一次。说白了就是一遍遍敲门,直到有人应答。

具体流程是这样的:

  1. 进入 Detect——上电或从其他状态回退后,PHY 进入 Detect.Quiet 子状态,等待一段时间(约 12ms),开始检测。
  2. 发射探测脉冲——PHY 在 TX 引脚上发射几个短脉冲,然后快速采样 TX 引脚上的反射波形。
  3. 判决——如果反射回来的能量很低(负载存在),进入等待状态,确认检测结果。多次确认后,进入 Polling 状态。
  4. 超时处理——如果没有检测到负载,经过 12ms 后复位 Detect 状态机,重新从步骤 1 开始。

这个超时循环会一直持续,直到链路另一端插上设备。我曾经调试过一块开发板,Slot 上没插卡,启动时看 PCIe 的调试寄存器,发现状态一直在 Detect 里循环——这很安全,不会引起任何异常。

警告:有些 FPGA 实现的 PCIe PHY 在 Detect 超时后会触发链路禁用(Link Disable)机制,这在普通 ASIC 里不常见,属于实现 bug。如果你在 FPGA 上遇到过 Detect 始终不跳出的情况,建议先检查 PHY 的 LTSSM 寄存器,看是不是被强制跳到了 Disable 状态。

4.4 Detect 状态机详解

PCIe 的 LTSSM(链路训练状态机)把 Detect 分成了两个子状态:

  • Detect.Quiet——等待所有 TX 引脚保持静默 12ms,确保链路上没有残留信号。这个状态也叫「清理期」。
  • Detect.Active——执行前面说的电气检测,发射探测脉冲并分析反射信号。

从 Detect.Active 出来后,如果检测成功,就跳转到 Polling.Active;如果失败,回到 Detect.Quiet 重新等 12ms。

让我给你一张完整的流程图,把整个 Detect 的逻辑串起来:

Detect 状态机流程图 进入 Detect Detect.Quiet 等待 12ms 静默 Detect.Active 发射探测脉冲 + 检测反射 检测到负载? N Y 回到 Quiet 循环检测 进入 Polling 检测超时后不会报错,而是回退到 Quiet 子状态继续重试

图上的逻辑很清楚。Detect 状态本身不会卡死,它会自动循环。这也是为什么 PCIe 支持热插拔——你任何时候插上一块卡,链路马上会从 Detect 推进到 Polling,不需要复位。

4.5 实战中的陷阱

做了这么多年硬件,我在 Detect 阶段碰到过三个比较典型的问题:

陷阱一:直流耦合卡在 Detect

PCIe 规定链路必须是交流耦合的。但有些工程板为了省电容,直接用了直流耦合。结果呢?Detect 状态永远检测不到负载。为什么?直流情况下传输线阻抗完全变了,反射波形跟预期对不上,PHY 误判为「没有检测到接收端」。我踩过这个坑——当时为了省两个电容,结果折腾了两天才发现问题。

陷阱二:板级阻抗不匹配导致 Detect 误判

PCB 的差分阻抗应该是 100Ω(单端 50Ω)。如果板厂加工出了问题,阻抗偏到 70Ω 或者 130Ω,反射回来的能量大小就变了。PHY 可能把有负载的情况判断成空载,然后一直在 Detect 里循环。我曾经用 TDR(时域反射计)测过一块出问题的板子,发现走线阻抗在连接器附近剧烈抖动——就是那里造成了误判。

陷阱三:超时时间被修改

有些 BIOS 或者 FPGA 代码会把 12ms 的超时改成更短的值,想加快训练速度。但 PCIe 规范强烈不建议这样做。12ms 这个时间是考虑了电源稳定时间、时钟稳定时间和寄生电容充放电时间算出来的。缩短它,在你插拔设备或电源噪声大的时候,极易出现训练失败。

避坑指南:调试 Detect 不过的问题时,我建议先量 TX 引脚直流电平,再测交流耦合电容的焊接情况,最后用上位机读 LTSSM 状态寄存器。大多数时候问题出在前两步。不要一上来就怀疑芯片坏了——PCIe PHY 的 Detect 模块非常鲁棒,几乎不会坏。

好,Detect 状态就讲到这里。说实话,它是个不怎么出彩的状态,但恰恰是这个不起眼的阶段,决定了你的设备能不能迈出链路训练的第一步。接下去,Polling 状态会开始交换真正的训练序列,那是另一场有意思的戏了。