3. 训练序列发送与接收机制归纳总结
训练序列的发送与接收,是整个 FRL 链路训练的“硬核”部分。说白了,就是两端设备通过一系列协商好的“暗号”——也就是特定的比特序列——来完成握手互认。我调试过不少链路不通的案例,十有八九都跟这个环节的时序或数据错位有关。
3.1 序列构成与帧结构
训练序列不是乱发的,它有严格的帧结构。我把最常用的几种序列整理成了一张表,方便你对照看。
| 序列类型 | 前导码长度 | 数据载荷 | 校验方式 |
|---|---|---|---|
| TS0 | 16 符号 | 全 0 模式 | CRC-8 |
| TS1 | 16 符号 | 交替 10 模式 | CRC-8 |
| TS2 | 32 符号 | 自定义训练向量 | CRC-16 |
| 锁定序列 | 8 符号 | 特殊标识码 | 奇偶校验 |
我个人习惯在代码里把前导码和载荷分开定义,方便调试时替换。序列长度决定了训练周期的长短。你想,如果链路全是 TS0,那训练速度非常快,但可靠性差;要是全用 TS2,交互更丰富,但速度就下来了。
关键点:发送端和接收端的序列长度必须完全一致。差一个符号,链路都可能无法锁定。
3.2 发送时序与状态机
发送端不是随便把序列丢出去就行。它受一个有限状态机控制,状态跳转必须精确到符号级。下面是我总结的五个核心状态,以及它们之间的跳转条件。
- IDLE 状态:链路空闲,发送全 0 填充符。
- TRAIN_START 状态:检测到“开始训练”触发条件,发送 TS0 序列。
- TRAIN_WAIT 状态:持续发送 TS1,等待接收端回执。
- TRAIN_LOCK 状态:收到有效回执后,切换为 TS2 序列进行参数交换。
- LINK_READY 状态:双方交换完成,进入正常数据传输。
我在项目中遇到过,发送端一直卡在 TRAIN_WAIT 状态,死活进不了下一步。后来排查发现,是接收端返回的回执帧格式少了一个符号,发送端压根没认出来。
避坑指南:回执检测窗口不能设置得太紧。我曾经一个项目,窗口只有 4 个符号,结果因为时钟抖动,回执老是落在窗口外。建议至少留 8 个符号的余量。
3.3 接收检测与校验机制
接收端的工作其实更复杂。它需要在一个连续的比特流里,准确找到序列起始位置,然后解析出序列类型和数据。这里我拆成三个步骤来讲。
- 步骤一:前导码检测
连续匹配 16 个符号的固定模式。嗯,这里要注意,匹配器要用滑窗方式工作,不能等模式对齐了再检测,那样会错过序列头部。 - 步骤二:序列类型识别
根据前导码之后的第一个数据符号,判断是 TS0、TS1 还是 TS2。识别判据其实就一条:符号是否为已知的映射值。不是的话,直接丢弃,重新等待。 - 步骤三:CRC 校验
收到完整序列后,立即计算 CRC,跟发送端附带的校验值比对。不一致就上报“校验失败”中断。
// 接收端滑窗匹配伪代码
if (sliding_window == PREAMBLE_PATTERN) {
capture_start_flag = 1; // 捕获到前导码
sequence_type = analyze_first_symbol(data_in);
if (sequence_type != UNKNOWN) {
start_crc_calculation();
receive_payload();
if (crc_result == received_crc) {
set_lock_signal(1);
} else {
set_error_flag(CRC_ERR);
}
}
}
你想想看,如果前导码检测的阈值设高了,训练序列就可能被当成噪声滤掉。反之,阈值低了又容易误触发。我一般把匹配门限设在 85% 以上,且连续三次匹配才确认有效。
实用技巧:在接收端做“序列计数器”,统计连续收到的合法序列数量。连续收到 3 个以上,再判定链路稳定。这么做可以过滤单次偶然错误,我跟你们说,这招在噪杂环境下特别管用。
3.4 流量控制与反压
发送端和接收端之间,还得有明确的流量控制机制。不然发送端一直往外抛序列,接收端处理不过来,数据直接溢出了。
| 控制手段 | 实现方式 | 典型阈值 |
|---|---|---|
| 暂停信号 | 接收端拉高专用引脚 | 缓冲区水位 > 80% |
| 对齐标记 | 在序列中嵌入对齐符 | 每 128 符号插入一次 |
| 重试机制 | 发送端收到 NAK 后重传 | 最大重试次数:3 |
我见过有些设计把暂停信号和序列接收逻辑写成了两个独立模块,结果两者相互竞争。定位这个bug花了我两天时间,最后发现是暂停信号的优先级设错了。接收端明明已经满了,发送端还在继续发。所以,暂停信号一定要有最高的仲裁优先级。
3.5 常见异常与排查手段
调试多了,你会发现异常也就那么几种。我把最常碰到的列出来,附带我个人的排查顺序。
- 链路无法锁定:先查前导码是否匹配,再查 CRC 校验。我曾经折腾半天,结果只是焊盘虚焊导致前导码少了一位。
- 训练周期过长
- 误锁定到错误序列:阈值设得太低了。把匹配连续次数从1次改成3次,通常就能解决。
- 发送回执永远收不到:检查接收端的回执触发条件,还有发送端的接收窗口宽度。我有个项目就是窗口宽度设成了 0,导致回执永远进不来。
归纳起来,训练序列的发送与接收,本质上是两端设备之间一套严格的时序握手协议。你只要把帧结构、状态机、校验和流控这四个东西理清楚,大部分链路问题都能找到根因。