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有点像,但更轻量。我习惯把它画成下面这张图:
图上的状态我简单解释一下:
- Polling(检测) – 上电后设备先发送TS1,同时监听链路上有没有别人的TS1。我遇到过两设备时钟偏差太离谱,Polling阶段始终收不到有效的TS1,最后加了一级PLL才解决。
- TS1 → TS2 – 连续收到3个无错的TS1后,切换到TS2。这里注意,如果你收的TS1里PAD字段不一致(比如对方的速率支持列表跟你不一样),就会一直卡在TS1。
- TS2 → L0 – TS2里会协商具体链路宽度×8还是×4。我在一次调试中发现,两边都宣称支持×8,但实际一个只连了×4的物理层,最后重新检查硬件设计才找到问题。
- L0 与 TS3 – 进入L0后链路可以传数据了,但每隔一定时间需要发TS3来维持链路锁定。如果没收到TS3,链路就会超时进入TS4。
- 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链路层训练的骨架。接下来在实际项目中,遇到链路建不起来的问题,就能快速定位是在哪个状态、哪一步出的错。嗯,这才是做硬件的底气。