4. 训练序列(Training Sequences)详解

训练序列是CXL链路层建立可靠连接的第一步。说白了,就是两个设备在正式传数据之前,先互相“喊话”对齐一下。我刚开始接触CXL时,以为训练就是简单的握手,后来才发现里面藏着不少门道。

4.1 训练序列的核心作用

链路两端的设备,上电之后频率、相位、比特对齐都是乱的。训练序列要解决三个问题:

  • 比特锁定 – 让接收端找到每个比特的边界
  • 符号对齐 – 把串行比特流切成正确的符号(比如16位或32位)
  • 链路宽度协商 – 两边确认到底用×8、×4还是×1

我记得有个项目,板子回来死活连不上。查了一天,最后发现是训练序列里的PAD值配错了,导致对齐失败。嗯,从那之后我对训练序列的每一比特都特别敏感。

4.2 训练序列的帧结构

CXL链路层把训练序列封装成一帧一帧的。每一帧大概长这样:

字段 宽度 说明
SOF(Start of Frame) 8 bit 固定的K码,标记帧开始
序列号 4 bit 每发一帧加1,用于去重和重排序
TS(Training Symbol) 32 bit 核心训练数据,包含类型、PAD、速率协商等
CRC 8 bit 校验,防止传输错误导致误判
EOF(End of Frame) 8 bit 结束K码

你可能会问:为什么CRC才8位?因为训练序列很短,而且帧间的间隔固定,出错概率本身就不高。我一般建议在仿真阶段加大CRC位数来抓问题,但产品里还是得按规范来。

4.3 训练序列的类型

根据链路状态的不同,训练序列分成几类:

  • TS1 – 初始训练序列,用于比特锁定和速率协商
  • TS2 – 确认训练序列,在TS1之后发,做符号对齐和宽度协商
  • TS3 – 空闲训练序列,链路空闲时周期性发送,维持锁相环
  • TS4 – 重置训练序列,由复位或错误触发,回到初始状态

经验之谈:我见过有人把TS1和TS2混着用,结果链路一直卡在“训练中”状态。建议严格按照状态机来切换,别自作聪明。

4.4 训练序列状态机

CXL链路层训练的状态机,跟PCIe的LTSSM有点像,但更轻量。我习惯把它画成下面这张图:

CXL链路层训练序列状态机 训练序列状态机 检测.Polling 初始训练.TS1 确认训练.TS2 链路就绪.L0 空闲.TS3 重训练.TS4 说明: 1. 上电后进入Polling状态,等待对方发TS1;连续收到3个有效TS1后切到TS2。 2. TS2完成符号对齐后进入L0(链路就绪);空闲时发送TS3维持锁定。 3. 任何CRC错误、超时或链路断开都会触发TS4,强制重训练。

图上的状态我简单解释一下:

  1. Polling(检测) – 上电后设备先发送TS1,同时监听链路上有没有别人的TS1。我遇到过两设备时钟偏差太离谱,Polling阶段始终收不到有效的TS1,最后加了一级PLL才解决。
  2. TS1 → TS2 – 连续收到3个无错的TS1后,切换到TS2。这里注意,如果你收的TS1里PAD字段不一致(比如对方的速率支持列表跟你不一样),就会一直卡在TS1。
  3. TS2 → L0 – TS2里会协商具体链路宽度×8还是×4。我在一次调试中发现,两边都宣称支持×8,但实际一个只连了×4的物理层,最后重新检查硬件设计才找到问题。
  4. L0 与 TS3 – 进入L0后链路可以传数据了,但每隔一定时间需要发TS3来维持链路锁定。如果没收到TS3,链路就会超时进入TS4。
  5. TS4 重训练 – 错误恢复的入口。我曾有个bug:链路偶尔丢一帧TS3,结果TS4次数太多导致系统卡死。最后我们调整了超时阈值,并增加了TS3的重发次数。

4.5 训练序列发送与接收的时序

为了训练正常,每个训练帧之间有固定的间隔,叫帧间隔(IFG)。CXL规范里,IFG至少是8个符号周期。我给大家一个参考时序:

时钟周期 :  0   1   2   3   4   5   6   7   8   9  10  11  12
发送端 : [SOF][SEQ][TS ][CRC][EOF]         [SOF][SEQ][TS ][CRC][EOF]
接收端 : 延迟2周期后接收,检测SOF开始

注意接收端一般有2~3个符号的延迟,因为要解码K码。我建议在做仿真时,用随机延迟来测试你的训练序列边界条件。

避坑:我曾经把IFG设成4个符号周期,结果接收端跟不上,一直报CRC错误。如果你在调试时发现链路训练总是失败,先检查IFG是否满足最小值。

4.6 训练序列常用参数配置表

下面是训练序列里几个关键参数的推荐值,来自我多个项目的经验:

参数 推荐值 备注
TS1 发送次数 3 次 连续收到3个有效即进入下一状态
TS1 重试超时 50 μs 超时后直接进入TS4
TS2 发送次数 3 次 完成对齐后切L0
TS3 周期 1 ms 空闲时每秒发1000帧维持锁定
TS4 最大重试次数 10 次 超过后报错,需要系统复位

这些值不是的,具体要根据你的时钟抖动、链路长度微调。我建议在PHY的配置寄存器里留出可编程空间,方便后期调试。

4.7 常见问题与调试方法

  • 问题1:链路一直停留在Polling
    检查PAD字段是否匹配。我曾经因为PCIe和CXL混用工程,结果训练序列的版本号写错了,导致互相不认。
  • 问题2:TS2完成但进不了L0
    多半是宽度协商失败。用逻辑分析仪抓两端的TS2帧,对比WIDTH字段。
  • 问题3:链路频繁进入TS4
    可能是TS3丢失,或者电源噪声影响了PLL。我建议在TS3的接收端加个滑动窗口,连续丢5帧才触发重训练,避免毛刺。
  • 问题4:CRC错误率很高
    先看信号完整性,再检查训练序列的CRC算法是否实现了正确的多项式。CXL用的是CRC-8-ATM,0x07多项式。

调试小工具:我在开发板上写过一个小脚本,定时打印训练序列状态机状态。一旦出问题,立刻锁定当前寄存器,省去反复抓log的麻烦。

好了,训练序列的基础就这些。理解了这个状态机,你就掌握了CXL链路层训练的骨架。接下来在实际项目中,遇到链路建不起来的问题,就能快速定位是在哪个状态、哪一步出的错。嗯,这才是做硬件的底气。