### DLLP 和有序集:ACK/NAK、Flow Control Update、SKP 有序集的功能与调试 **数据链路层**是 PCIe 通信的“安全员”。它不负责最终的数据含义,只保证两端的报文能可靠地传递过去。 它的核心工作有三件: - **ACK/NAK 协议** —— 确保 TLP 不丢、不乱序。 - **Flow Control (流量控制)** —— 告诉对方“我这边的接收缓冲区还有多大空间”。 - **SKP 有序集** —— 维持收发双方的时钟同步,补偿频率偏差。 下面我逐一展开,并讲讲用协议分析仪怎么抓住这些蛛丝马迹。

一、ACK/NAK —— 可靠传输的基石

基本规则

发送方每发一个 TLP,都会把这个 TLP 的副本保留在本地重放缓冲区(Replay Buffer)中,同时等待接收方回来的应答。

  • ACK:接收方确认 TLP 已收到,且 CRC 校验正确。发送方收到 ACK 后,就可以把对应的副本从重放缓冲区中删除。
  • NAK:接收方发现 TLP 有 CRC 错误、或者序列号不连续(比如丢了包),就会发送 NAK。发送方收到 NAK 后,必须从重放缓冲区中取出所有未被 ACK 的 TLP,重新发送一遍。

序列号(Sequence Number)—— 12 位,范围 0~4095

每个 TLP 的序列号是连续递增的。接收方通过检查序列号是否连续,判断是否丢包。如果收到 Seq=5 的 TLP,但上次收到的是 Seq=3,说明 Seq=4 丢了,接收方就会发 NAK。

调试要点

  • 在协议分析仪中,过滤出所有 ACK DLLP 和 NAK DLLP。看 ACK 的序列号字段,是否与发送的 TLP 序列号一一对应。
  • 如果看到 大量 NAK,立刻检查链路信号质量(眼图、抖动、电压摆幅)。大多数 NAK 是由物理层错误引起的。
  • 我遇到过一个问题:某个 FPGA 实现的 PCIe 控制器,在重放缓冲区满时,没有正确丢弃旧 TLP。结果 NAK 触发重放后,发送方反复发旧数据,接收方又一直报 CRC 错误。分析仪上看到的就是 ACK/NAK 无限循环,速率直接掉到几百 MB/s。

小提示:大部分分析仪可以设置“NAK 触发”。一旦抓到 NAK 就暂停采集,这样可以精准定位出错的 TLP,然后追溯它的原始序列号和 CRC 字段。

二、Flow Control —— 别把接收端撑爆了

基本规则

Flow Control(FC)是接收端主动给发送端反馈的通知,表明自己还有多少接收缓冲空间。

  • Posted:写入类请求(Memory Write, Message),不需要完成响应。接收方用 Posted Credit 来管理。
  • Non‑Posted:读取类请求(Memory Read, I/O Read/Write, Configuration Read/Write),需要接收方返回 Completion。接收方用 Non‑Posted Credit 管理。
  • Completion:读取操作的返回数据。接收方用 Completion Credit 管理。

接收方通过 FC_Update DLLP 把自己的可用 Credit(空间)告诉发送方。发送方根据 Credit 数决定能发多少个 TLP。

常见问题:FC 更新延迟或丢失,会导致发送方以为接收方没有空间,从而停止发送。这种“FC 死锁”在复杂片上系统(SoC)中很常见。

调试要点

  • 在分析仪上抓取 FC_Update DLLP,看里面的 Posted Header Credit、Non‑Posted Header Credit、Completion Header Credit 以及对应的 Data Credit。
  • 观察平均每个 FC_Update 之间的时间间隔。如果间隔太长(比如超过几个微秒),说明接收端处理 FC 更新的速度慢,可能成为性能瓶颈。
  • 我个人的方法:对比发送端的 TLP 数量和接收端的 Credit 释放。例如发送了 10 个 Posted TLP,但接收端在下一个 FC_Update 中只释放了 3 个 Credit,那说明接收端处理速度跟不上,或者有 TLP 被丢弃。

高级技巧:使用分析仪的“FC 流量图”功能。它能画出每个 VC(Virtual Channel)的 Credit 随时间变化曲线。一旦看到 Credit 持续为零或出现负值(表示 Credit 溢出),问题就现形了。

三、SKP 有序集 —— 时钟补偿的小技巧

基本规则

PCIe 设备都有自己的本地时钟,即使都跑在同一个标称频率(比如 100 MHz),实际频率也会有微小偏差。为了实现数据的连续传输,PCIe 使用 SKP 有序集(也叫 SKP Ordered Set)来插入或删除空闲字符,抵消时频偏差。

  • 发送方每隔一定时间(比如每 375 个符号)插入一组 SKP 符号。
  • 接收方检测到 SKP 后,如果发现自己本地时钟偏慢,就删除一个 SKP 字符;如果偏快,就插入一个 SKP 字符。
  • SKP 符号本身不携带数据,只做时钟补偿。

调试要点

  • 在协议分析仪的物理层视图中,找到 SKP Ordered Set。一般显示为 "SKP" 或 "Skip"。
  • 检查 SKP 出现的间隔是否均匀。如果间隔忽长忽短,说明发送端的时钟抖动大。
  • 如果看到连续多个 SKP 被删除或插入,说明收发双方的时钟偏差很大。偏差过大时,可能触发链路重新训练(Link Training)。

实际案例:某次调试验证一款 PCIe 加速卡,发现链路频繁掉线。抓分析仪后看到 SKP 插入/删除频率异常——每次掉线前几微秒,接收方连续插入了 5 个 SKP 字符。后来查出是加速卡上的 100 MHz 参考时钟源精度不够,只有 ±300 ppm(PCIe 规范要求 ±300 ppm,但实际至少要留出 ±200 ppm 的余量)。换了 ±50 ppm 的晶振后,一切正常。

四、三种 DLLP/有序集的调试对比

类型 触发条件 分析仪过滤方法 常见故障
ACK/NAK 收到 TLP 后立即应答 过滤器:DLLP Type = ACK / NAK 大量 NAK ⇒ 物理层错误;ACK 超时 ⇒ 重放缓冲区溢出
FC_Update 接收缓存释放空间后更新 过滤器:DLLP Type = FC_Update Credit 为零 ⇒ 死锁;Credit 释放慢 ⇒ 带宽低
SKP 有序集 发送端周期性插入 物理层视图 / Pattern 搜索 "SKP" 插入删除频繁 ⇒ 时钟偏差大;SKP 丢失 ⇒ 链路同步失败

五、知识体系图:DLLP 与有序集在链路层中的角色

下面的图展示了数据链路层中三大功能模块的关系,以及它们在 PCIe 协议栈中的位置。

DLLP 与有序集在链路层中的角色 Transaction Layer (TL) Data Link Layer (DLL) ACK/NAK 协议 Flow Control (FC) 重放缓冲区管理 CRC 检查与生成 Physical Layer (PHY) SKP 有序集插入/删除 时钟补偿 8b/10b 编码 链路交互(Link) ACK/NAK DLLP FC_Update DLLP TLP 数据 SKP 有序集 TS 有序集 协议分析仪关注焦点 1. 捕获 ACK/NAK DLLP 2. 检查序列号连续性 3. 过滤 FC_Update 4. 分析 Credit 变化 5. 定位 SKP 有序集 6. 统计插入/删除次数 7. 关联物理层错误 8. 触发链路重训练 分析

六、综合分析仪调试步骤

  1. 建立基线:在健康链路下,记录 ACK/NAK 数量、FC_Update 频率、SKP 插入/删除次数。作为后续对比的参考。
  2. 触发捕获:对于怀疑的问题(如带宽低、链路掉线),设置分析仪的“NAK 触发”或“FC Credit 为零触发”。
  3. 逐级排查:
    • 如果 NAK 多 → 检查物理层信号质量(眼图、EQ 设置)。
    • 如果 FC_Update 少 → 检查接收端是否响应太慢,有可能是驱动或固件 bug。
    • 如果 SKP 异常 → 检查时钟源精度和抖动。
  4. 反向验证:修复后,重新采集相同场景数据,与基线对比。NAK 数量应减少到 0 或极低水平,FC_Update 间隔恢复正常,SKP 插入/删除次数回归基准。

别忘了:PCIe 协议分析仪不是万能的。有些问题需要结合示波器的物理层测试和逻辑分析仪的协议层联动才能定位。我习惯将分析仪和示波器同时挂在一个链路两端,一个看数据,一个看信号。

七、总结

数据链路层的 DLLP 和有序集是 PCIe 可靠性和性能的核心。我建议你至少花时间把下面几点跑通:

  • 在分析仪中建立 ACK/NAK 的过滤和统计视图。
  • 掌握 FC_Update 的 Credit 变化曲线。
  • 理解 SKP 有序集如何影响时钟补偿。

下次当你看见链路掉线、带宽上不去、或者 TLP 重传时,先问自己三个问题:NAK 多不多?Credit 够不够?时钟准不准? 答案往往就在这三点里。