4. 降级触发条件之链路层:ACK/NAK重传超限、TLP重放次数过多、CRC校验失败

链路层,说白了就是PCIe通信的“保底机制”。

事务层往下发TLP,链路层负责保证它不出错地送到对端。一旦发现丢包、数据错、序列号对不上,链路层会启动重传机制。但重传不是无限的——超过阈值,就会触发链路降级。

这一章我重点讲三个最直接的降级触发条件:

  • ACK/NAK重传超限
  • TLP重放次数过多
  • CRC校验失败

这三种情况,在现场调试中几乎天天遇到。你搞明白了它们,链路层的问题就能解决一大半。

链路层降级触发逻辑流程图 发送TLP 等待ACK/NAK 收到ACK 发送成功,继续 收到NAK/超时 触发重放(Replay) 重放次数超限 → 降级 触发降级 CRC校验失败也纳入重传计数

4.1 ACK/NAK重传超限:最常见的“软故障”

链路层有个核心机制:发送端每发一个TLP,都会保存一份副本到重放缓冲区。直到收到接收端返回的ACK,才把副本释放掉。

如果收到的是NAK,或者超时没收到任何回应,发送端会重传TLP。这个过程叫重放(Replay)。

触发降级的临界条件是什么?

每个PCIe设备都有一组寄存器记录了重放计数。一旦重放次数超过 Replay_Count_Limit(通常由链路协商决定,默认31次),链路层会直接触发降级:

  • 数据链路层状态从 DL_Active 切到 DL_Recovery。
  • 重传窗口清空,重新协商链路参数。
  • 严重情况下直接切回 DL_Init,链宽或速率降一档。

关键点:重传次数是在一个“滑动窗口”内统计的。连续重传导致窗口内错误率超过阈值,触发降级。

我在项目中遇到过一台NVMe SSD,固件Bug导致发送TLP时序列号重复,接收端疯狂回复NAK。重放缓冲区直接撑爆,链路降到x1。后来靠抓Logic Analyzer才发现是固件序列号模块写错了。

4.2 TLP重放次数过多:不只是硬件问题

很多人以为重放次数过多一定是电气问题。其实不然。

我见过这样的情况:链路电气测试没问题,BER也正常,但重放计数一直往上跳。查到最后,是接收端Buffer满导致的。

接收端内部有个接收边界(Receiver Boundary),一旦Buffer满了,它不会返回ACK,而是丢包或发NAK。发端尝试重放,但收端一直满,最终重放超限。

典型场景:

场景 根因 我的排查经验
接收Buffer溢出 流量控制没配好,VC Buffer不够 先查 FC Credits 寄存器,看是不是一直为0
序列号不连续 发送端SEQUENCE_ID跳变 抓TLP Header,看接收端期望的Seq Num
物理层符号错误 Signal Integrity差,丢Symbol 看 Rx Errors Counter,配合示波器
参考时钟抖动大 PLL锁定不稳,偶尔丢数据 量SSC(扩频时钟)偏移量

避坑指南:我曾经排查过一个案子,重放计数器涨得飞快,但误码率极低。后来发现是发送端重放缓冲区的释放机制有延迟,导致同一个TLP被重复重放了。嗯,这种“软Bug”最难抓。

4.3 CRC校验失败:最后一层防线

TLP和DLLP都带有CRC。链路层收到一个TLP时,会先算CRC。如果不对,这个TLP就被丢弃了,同时发送NAK回去。

CRC失败意味着什么?

  • 物理层的8B/10B或128B/130B编码没能纠正这个错误。
  • 或者,这个错误发生在编码之后、解码之前,编码器没检测到。
  • 说白了,就是物理层“兜不住了”。

触发降级的逻辑:

链路层会记录连续CRC失败的次数。除了单次丢弃重传外,如果短时间内CRC失败率超过 CRC_Error_Threshold(不同厂商定义不同,一般是32次/秒),链路层会认为对端不可靠,触发降级。

注意:CRC校验失败不一定全是对方的问题。我见过很多次是发送端自己把CRC算错了。比如硬件实现中LCRC(链路CRC)计算逻辑有个边界条件没处理好,在特定数据模式下算错。我自己就踩过这个坑,查了三天才发现是RTL里一个加法器溢出了。

4.4 三种条件的“联动效应”

你想想看,这三种情况经常串联出现:

  1. 先出CRC错误 → TLPs被丢弃
  2. 接收端发NAK → 发送端启动重放
  3. 重放次数超限 → 链路降级

所以,当你看到链路降级时,不要只盯着重放计数。往回查:CRC失败是从哪儿开始的?

我个人的调试习惯:

  • 第一步:读 Data Link Error Counters 寄存器。
  • 第二步:对比 CRC Error Counter 和 Replay Counter 的增长趋势。
  • 第三步:如果CRC计数涨得快,优先查物理层信号质量;如果重放计数涨得比CRC快,优先查接收端Buffer或序列号逻辑。

一句话总结:链路层降级不是一次性判决,而是“CRC错误 → NAK → 重放 → 重放超限”的递进链。抓住最上游的CRC错误,才是治本。