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代码和标准文档的深度总结
目录
- 引言:为什么需要 Retry
- Retry 的基本概念和时序
- 发送端算法:序号、LCRC 与重放计时器
- 接收端算法:检查、Ack/Nak 生成与 Duplicate 处理
- Synopsys 代码深度剖析
- 高性能实现细节与踩坑经验
- 高速串行链路层级视角的 Retry
- 小结与实践建议
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 的基本概念和时序
- Sequence Number(12 bit) —— 用来检测丢包、乱序。
- LCRC(32 bit) —— 检测帧内位错误。
- Ack/Nak DLLP —— 正、负确认。
- Retry Buffer —— 发送端保存已发未确认 TLP 的环形缓存。
- 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 实施步骤
- 封装 TLP
// ku5p_rdlh_tlp_extract.sv tlp_seqnum <= (tlp_valid_start) ? seqnum : tlp_seqnum;
- 在 STP 之前插入 4 bit Reserved + 12 bit 序号。
- 计算并附加 LCRC
Synopsys 调用ku5p_lcrc
IP,根据种子 FFFF_FFFFh、生成多项式 0x04C11DB7 计算 32 bit LCRC。 - 写入 Retry Buffer
// ku5p_xdlh_retrybuf.sv assign rbuf_din = {rbuf_ctrlbits, tlpgen_rbuf_data};
- 发送并启动 Replay Timer(见
xdlh_retrybuf
状态机) - 处理 Ack/Nak
- Ack 更新
ACKD_SEQ
并释放缓存。 - Nak 立即进入
S_SET_REPLY_REQ
状态,触发重发。
- Ack 更新
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_expired
由replay_timer
计数器产生。all_tlp_schd_ackd
判定所有待重发 TLP 已确认,提前终止。
状态机关键转移:
状态 | 条件 | 动作 |
---|---|---|
S_PRE_REQ → S_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_we 与 fsm_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 小结与实践建议
- 严格遵守窗口与计时器:序号窗口 2048、Replay Timer 上限按 4.0 简化值实现,即可兼容所有速率。
- Buffer 与信用要对齐:16 GT/s 以上必须支持 Scaled Flow Control,并按链路 RTT 计算 Retry Buffer。
- 优先级仲裁:推荐按 Spec §3.6.2 附录顺序实现——Nak > Ack > FC DLLP > Replay Buffer > 新 TLP。
- 代码阅读顺序:
- 发送路径:
ku5p_xdlh_tlp_gen_32b
→ku5p_xdlh_retrybuf
- 接收路径:
ku5p_rdlh_tlp_extract
→ku5p_rdlh_dlp_extract
熟悉状态机后再看xdlh_control_32b
的多路复用逻辑。
- 发送路径:
- 仿真 & 链路跌落测试:通过强制插入 CRC 错误、Nak storm、断开 LTSSM Recovery 场景,观察
rbuf_entry_cnt
、replay_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 控制器即使在噪声劣化或对端实现瑕疵的环境下,也能保持数据层面的零错误交付,同时最大限度减少对事务吞吐的影响。