BITSLIP 只做一件事:把 ISERDESE2 的并行字对齐到正确的字边界 —— 每来一个 CLKDIV 周期宽的脉冲,整字旋转 1 bit,最多转 DATA_WIDTH 个相位。它管不了采样点位置(那是 IDELAY 的活),也读不回当前相位,所以必须靠线上的已知码字判定哪个相位是对的。
它做什么、不做什么
| BITSLIP 能 | 需要别的手段 | |
|---|---|---|
| 字边界(并行字从哪一位开始算) | ✔ 旋转 1 bit / 脉冲 | |
| 采样点是否落在眼图中心 | IDELAYE2 tap 微调(或 MMCM 相位) | |
| lane 之间的位偏斜 | 每 lane 独立 IDELAY,或等长走线 | |
| 哪个 lane 对应哪个 bit | 固定布线 + 板级核对(不是 BITSLIP 的事) | |
| 读出当前相位 | 不可读。只能从数据反推 | |
| 整字跳变(丢/多一个整字) | 帧同步码 + 字级对齐逻辑 |
一个必须记住的前提:BITSLIP 只在 INTERFACE_TYPE="NETWORKING" 下存在,MEMORY 模式下没有这个功能。DDR 模式下合法 DATA_WIDTH 为 4/6/8/10/14,SDR 为 3/4/5/6/7/8。
正确的用法
- 脉冲宽度正好一个 CLKDIV 周期,且只在 CLKDIV 域产生。多拍会变成多次旋转(转几下取决于内部逻辑,不要赌)。
- 一个脉冲 = 1 bit 旋转,所以要覆盖全部相位最多打
DATA_WIDTH次;8 bit 就是 8 个相位。 - 每 lane 独立一套 slip 控制器。相位是每 lane 各自的事,不能共用计数。
- 只在数据在流动的时候打脉冲。源端停着的时候没有字边界可对齐,转了也判不出来。
- 必须在线上有已知码字(comma、训练字、行同步码)。没有参照就无法判定"现在对齐了没有",只能靠猜。
相位搜索的写法
思路是"打一次、等几拍、比一次",不是"打一次立刻比":
for phase in 0 .. DATA_WIDTH-1:
pulse_bitslip() # 一个 CLKDIV 周期
wait(5 words) # 关键:脉冲后数据流是不连续的
good = compare(known_word) # 与本地已知码字逐位比
if good: lock(phase); break
丢掉脉冲后的几个字是必须的:slip 会插入或丢弃一位,紧接着的字处于"半新半旧"的状态,直接比一定失败。实测中丢掉 5 个字后,正确的相位可以做到整字全对。
仿真的判定方法
不要把比较窗口钉死在脉冲之后的第一字,要滑窗:一边移位一边比,只认"连续 N 个整字全等"的那个相位。用 SDR 逐相位扫描 + 每 lane 单独判定,就能看出是否存在唯一正确相位(若出现两个相位都对,说明你的训练码有周期性问题,或者数据内容对相位不敏感 —— 见下一条坑)。
什么时候不要用 BITSLIP
如果链路上没有每 lane 独有的训练码(例如视频流:只有 de/hs/vs 和像素数据,且平坦色块对相位完全不敏感),用 BITSLIP 会掉进两个坑:
- 假锁定:全屏单色时任意相位都能"通过",等到出现棋盘/灰阶才发现错了;
- 不可恢复:链路扰动后不知道自己转到哪了,必须整套重扫,而重扫要求源端先给出结构化的测试图案。
这种场合更稳的做法是放弃 BITSLIP,改做字级对齐:用行同步码定位每一行的数据窗口,把整字做桶形移位,用"行内空闲间隔"判断新行/新场。它是确定性的、与图像内容无关、每行都能自愈,代价是需要一个能记住整个字的移位寄存器,而不是 8 个相位的搜索状态机。
常见坑
- 脉冲打了但
CLKDIV还没稳定 → Q 一直是 X,reset 释放顺序要先时钟后 IO。 - 只测了脉冲后的头两个字 → 结论恰好相反(把对的说成错的)。
- 训练码只在一个 lane 上试 → 其它 lane 其实没对齐。
- 认为 slip 能补偿整字级偏移 → 整字偏移要靠帧/行同步,slip 只能在小范围内找位。
- 认为 slip 能提高时序裕量 → 它不改变采样点,眼图差还得靠 IDELAY/tap 或走线。
实测记录
8 bit DDR、INTERFACE_TYPE="NETWORKING"、三 lane 并联:slip 相位 2 时三条 lane 都是 8/8 整字全对;逐相位推进时 Q 的编号按 2→3→…→8→2 循环,印证"一个脉冲一位"的行为。同一套 PHY 在把脉冲验证改成滑窗判定之前,曾被误判为"slip 无效"。
一句话总结
BITSLIP 是"字边界对齐"的窄工具:有位级相位可搜、有训练码可比的时候它很好用;没有训练码、或者需要与内容无关的确定性对齐时,行同步码 + 整字桶形移位更可靠。