数字部分 2.5 Dll 重传

PCIe SerDes 全流程实战

数字部分——DLL-重传

本项目聚焦于高速串行通信本身。PCIe 作为广泛应用的高速串行通信协议,其历史悠久,协议栈层次多、设计复杂;Synopsys IP 更是在工业级别上对 PCIe 协议做了完整实现。
因此,本文 不 针对数字逻辑的每一个细节展开说明;若需深入,可结合 SystemVerilog 代码与波形自行学习。

个人经验

  • Data Link 层是 PCIe 架构中的关键环节,上承 Transaction Layer,下接 MAC 层。
  • 其核心目标是在与整个物理层协同工作的基础上,构建一个能够稳定并保持顺序正确的数字信号传输通道。重传功能正是 Data Link 层最重要的职责之一。
  • 为保证数据传输的正确性,Data Link 层通过 LCRC 校验结合重传机制实现差错控制;重传与否由 ACK/NAK 报文判定。
  • 以太网协议栈同样存在带有重传的 TCP 与不保证顺序的 UDP,但其重传逻辑主要由软件实现,而 PCIe 的重传则由硬件层面直接完成。

以下内容为O3-Pro对system verilog代码和标准文档的深度总结

目录

  1. 引言:为什么需要 Retry
  2. Retry 的基本概念和时序
  3. 发送端算法:序号、LCRC 与重放计时器
  4. 接收端算法:检查、Ack/Nak 生成与 Duplicate 处理
  5. Synopsys 代码深度剖析
  6. 高性能实现细节与踩坑经验
  7. 高速串行链路层级视角的 Retry
  8. 小结与实践建议

1 引言:为什么需要 Retry

PCI Express 使用差分高速串行通道,在 2.5 GT/s–64 GT/s 的物理速率下,一旦比特错误率达到 10⁻¹²–10⁻¹⁵,整帧传输错误并不罕见。为保证 Transaction Layer Packet (TLP) 的可靠递送,标准在 Data Link Layer(DLL)设计了一套 错误检测 + 自动重传(Retry) 机制。

“The Transmitter stores a copy of all TLPs sent, re‑sending these copies when required, and purges the copies only when it receives a positive acknowledgement of error‑free receipt from the other component.” — PCI Express Base Spec 4.0 §3.1 Error Detection and Retry


2 Retry 的基本概念和时序

  1. Sequence Number(12 bit) —— 用来检测丢包、乱序。
  2. LCRC(32 bit) —— 检测帧内位错误。
  3. Ack/Nak DLLP —— 正、负确认。
  4. Retry Buffer —— 发送端保存已发未确认 TLP 的环形缓存。
  5. Replay Timer —— 当定时器溢出且仍无 Ack/Nak,自动触发批量重发。

标准规定(摘译):

• 若 (NEXT_TRANSMIT_SEQ – ACKD_SEQ) mod 4096 ≥ 2048,发送端必须停止从 Transaction Layer 接收新 TLP — PCIe 4.0 §3.6.2.1
• Replay Timer 简化值:Extended Synch=0 时 24 000–31 000 Symbol Times — §3.6.2.1

时序图(文字版):

Tx:  TLP0 ─►TLP1 ─►TLP2 …        (入 Retry Buffer)
Rx:                ◄─ Ack(Seq1)   (确认至 TLP1)
Tx:  计时器复位 …若超时或 Nak ⇒ 重发未 Ack 部分

3 发送端算法

3.1 核心状态变量

变量 作用 Synopsys 代码映射
NEXT_TRANSMIT_SEQ 下一个发送序号 rbuf_last_xmt_seqnum
ACKD_SEQ 已确认最大序号 ackd_seqnum
REPLAY_TIMER 重放计时器 replay_timer
REPLAY_NUM 连续重放次数 replay_num

3.2 实施步骤

  1. 封装 TLP
    // ku5p_rdlh_tlp_extract.sv
    tlp_seqnum <= (tlp_valid_start) ? seqnum : tlp_seqnum;
    • 在 STP 之前插入 4 bit Reserved + 12 bit 序号。
  2. 计算并附加 LCRC
    Synopsys 调用 ku5p_lcrc IP,根据种子 FFFF_FFFFh、生成多项式 0x04C11DB7 计算 32 bit LCRC。
  3. 写入 Retry Buffer
    // ku5p_xdlh_retrybuf.sv
    assign rbuf_din = {rbuf_ctrlbits, tlpgen_rbuf_data};
  4. 发送并启动 Replay Timer(见 xdlh_retrybuf 状态机)
  5. 处理 Ack/Nak
    • Ack 更新 ACKD_SEQ 并释放缓存。
    • Nak 立即进入 S_SET_REPLY_REQ 状态,触发重发。

4 接收端算法

4.1 完整性检查

int_crc_err = (!crc_match_vec & !crc_match_inv_vec) |
              (crc_match_inv_vec & !tlp_aligned_badeot);
  • 正常 LCRC 不匹配→Bad TLP
  • LCRC 反码且 EDB 标记→Nullified TLP(丢弃不 Nak)

4.2 序号窗口与 Duplicate

duplicate_seq = ((tlp_expected_seqnum - tlp_extracted_seqnum) <= 12'h800)
                & sequence_not_eq;

窗口大小 2048,满足标准“半环”检测原则。Duplicate TLP 仅 Ack 不上递。

4.3 Ack/Nak 生成

  • 好包 → Ack;
  • 丢失或错误包 → Nak;
  • 定时器 AckNak_LATENCY_TIMER 保证在上限前发送 Ack。

5 Synopsys 代码深度剖析

5.1 Retry Buffer FSM (ku5p_xdlh_retrybuf.sv)

assign reply_req = (retry_requested | replay_timer_expired | external_retry_req)
                   && !all_tlp_schd_ackd;
  • retry_requested 由 Nak 置位。
  • replay_timer_expiredreplay_timer 计数器产生。
  • all_tlp_schd_ackd 判定所有待重发 TLP 已确认,提前终止。

状态机关键转移:

状态 条件 动作
S_PRE_REQS_SET_REPLY_REQ 无新 Ack 更新 + SOTRAM 空闲 锁定起始地址
S_WAIT_N_LATENCY_SOTRAM 读出 SOT 表项 进入 S_WAIT_N_LATENCY_RETRYRAM
S_IN_REPLY 逐条读取 Retry RAM 输出 送回 TLP 生成器
S_DONE_PIPE Pipeline 清空 回到 S_IDLE

5.2 Ack/Nak 处理(ku5p_rdlh_dlp_extract.sv)

ack_flag = !tlp_abort;
nak_flag = (tlp_aligned_pkterr | int_crc_err |
           (sequence_err & !tlp_aligned_badeot));

tlp_abort 包括 CRC、序号、PHY Error 等情况;Duplicate 包触发 rdlh_req2send_ack_due2dup,便于上层快速释放。

5.3 Replay Timer 更新

// REPLAY_TIMER resets only when forward progress is made
REPLAY_TIMER <= (Ack_received) ? 0 : REPLAY_TIMER + 1;

符合标准“仅当收到 ACK 且缓冲区仍有未确认包时重载”。


6 高性能实现细节与踩坑经验

主题 风险 实践技巧
Retry Buffer 深度 L0s 进出、Retimer 引入额外 RTT,易导致 Buffer 溢出 16 GT/s 链路推荐 ≥ 1024 DW;评估 Ack Latency Limit 表后留 20% 余量
单口 RAM 竞争 同时写入新 TLP 与读取重发 Synopsys 设计用 sotbuf_wefsm_sotram_rd_en 锁相,必要时升级为双口 RAM
Nullified TLP 取消中途发送的 TLP 若序号未递增会破坏窗口 确保 NEXT_TRANSMIT_SEQ 不自增;代码中 nullified_tlp 逻辑已处理
Clock Domain Crossing Ack/Nak DLLP 进入 Retry FSM 使用 rdlh_xdlh_rcvd_ack_d 1 拍同步;避免亚稳影响 reply_req
Parity / ECC Retry/SOT RAM 数据完整性 ras_sotram_protect_parerr 检测错误后强制 state=S_IDLE 并上报

7 高速串行链路层级视角的 Retry

在 PHY 层看来,重发意味着 瞬时带宽下降吞吐延迟抖动。而 DLL 的设计哲学是:
宁可多发几次,也不让 Transaction 层承受数据错误

  • 小 Payload 高频错:Ack/Nak 流量比例增大,建议开启 Scaled Flow Control(表 3‑2)放宽信用窗口。
  • 大 Payload 低错:重发块尺寸大,Replay Buffer 与 MAX_PAYLOAD_SIZE 需成比例扩大。
  • 物理链路层共振:频繁 Retrain(Replay_Num 溢出→ LTSSM Recovery)会带来额外几百微秒的业务中断,需要通过 FEC、Retimer EQ 等物理层措施降低 BER。

8 小结与实践建议

  1. 严格遵守窗口与计时器:序号窗口 2048、Replay Timer 上限按 4.0 简化值实现,即可兼容所有速率。
  2. Buffer 与信用要对齐:16 GT/s 以上必须支持 Scaled Flow Control,并按链路 RTT 计算 Retry Buffer。
  3. 优先级仲裁:推荐按 Spec §3.6.2 附录顺序实现——Nak > Ack > FC DLLP > Replay Buffer > 新 TLP。
  4. 代码阅读顺序
    • 发送路径:ku5p_xdlh_tlp_gen_32bku5p_xdlh_retrybuf
    • 接收路径:ku5p_rdlh_tlp_extractku5p_rdlh_dlp_extract
      熟悉状态机后再看 xdlh_control_32b 的多路复用逻辑。
  5. 仿真 & 链路跌落测试:通过强制插入 CRC 错误、Nak storm、断开 LTSSM Recovery 场景,观察 rbuf_entry_cntreplay_timer 及输出时序。

“If REPLAY_NUM rolls over from 11b to 00b, the Transmitter signals the Physical Layer to retrain the Link” — PCIe 4.0 §3.6.2.1

做到以上,你的 PCIe 控制器即使在噪声劣化或对端实现瑕疵的环境下,也能保持数据层面的零错误交付,同时最大限度减少对事务吞吐的影响。