案例研究 · 深空纠错码上硅片

一个只有第一帧是对的深空译码器

一个 CCSDS AR4JA LDPC 译码器,在仿真里与黄金模型逐比特一致,标准定义的九个码点全部覆盖。可一上真硬件,从第二帧起帧帧丢头。问题不在译码器里。

AlgoSilicon 工程团队 · 2026

深空链路对每一帧只有一次机会。跨行星距离没有重传,纠错码要扛下全部压力, CCSDS 131.0-B-3 为此定义了九个 AR4JA 码点:信息块 1024、4096、16384 比特,每种块长各配 1/2、2/3、4/5 三种码率。这篇里的译码器是我们对照 MathWorks CCSDS LDPC 译码器模块做的评估孪生实现, 以标量模式逐模块对齐,好让它的行为可以对着一个公认的参考实现逐条审查。它有意不是我们为高速率链路 提供的那个折叠的、吞吐更高的配置。我们对着黄金模型把九个码点逐比特验过,然后把它放到板子上。 有意思的地方从这里开始。

仿真逐比特一致,硬件上确定性地出错

仿真里这个译码器很干净:九种配置、775 帧,每一比特都和黄金模型对得上。可在板子上把帧一个接一个 连续灌进去,第一帧完美,之后每一帧都以同样的方式出错,开头缺一块,整帧错了大约一半。

一个每次都以同样方式复现的故障是好事。随机性的损坏指向信号完整性或时序余量,那种问题很慢; 而从第二帧开始、偏移量固定的错误,指向的是某个正在重新武装的状态机,而且它顺带说明译码器本身多半没问题, 因为译码器根本不知道自己在处理第几帧。

先翻手册,再看波形

这时候最容易做的事是打开波形一点点找。我们没有。把数据在可编程逻辑和主机之间搬来搬去不是一个自定义问题, 它是有厂商标准用法的,模式就那几种,手册把约束写得很清楚。

当时这条捕获路把数据搬运引擎跑在简单模式下,而手册写明这种模式是为“一次一包”设计的:一帧收完之后, 软件要重新武装引擎去接下一帧。可译码器不会停,它一帧接一帧地往外吐。在重新武装的那个空档里, 下一帧开头的几拍无处可去,就被丢掉了。第一帧因为是提前武装好的所以是完整的,之后帧帧丢头。

厂商给的正确做法就在同一本手册里:用分散-聚集模式,提前把一圈描述符都武装好并一直保持武装。 当前帧还没写完,下一个描述符已经就绪,引擎不存在“听不见”的窗口。换过去之后,九种配置连续流式跑, 逐比特一致。

过一次不算过

硬件是会走运的。单独一次跑通说明不了多少问题,所以我们的标准是:同一个连续流要在多次全新启动下 零失败。最后的结果是三次全新启动、九种配置、每种三次试验,一共 81 次,全部干净。

81 / 81
三次全新启动 × 九个码点 × 三次试验,零失败,每一比特都与黄金模型比对

凑齐三次全新启动本身就是一段插曲。这块板子的软件重启是死的:不管发什么命令, 复位原因寄存器一直报上电复位。最初判断是缺了一个固件镜像,后来拆开验证发现判断错了, 于是把这个结论纠正过来,而不是让它留在那里。真正管用的重启走的是调试口, 这也让多次启动的门禁可以无人值守地跑完。

没跑通的那个模式,也照实记下

我们还评估过数据搬运引擎的循环模式,理论上它永不停,本该更省。在这颗引擎上它不成立: 驱动只为发送方向把描述符环接成了闭环,接收侧跑完一圈就停住。试过、不行、记下来。 一张没有负面结果的结果表,说明它没有被认真读过;恰恰是这些失败,让通过的部分值得相信。

你可以自己看

当时在板子旁边跑的那个面板已经发布成回放版:来自那次捕获的真实数据,每一帧、每一种配置、 逐帧比特比对,以及定点黄金模型对 MATLAB 双精度参考的误码率曲线。可以暂停,也可以拖到任意一帧。 不接任何硬件,浏览器里也不做任何仿真。

回放这次硅片捕获

这对客户意味着什么

这件事和译码器吞吐无关:这个孪生实现是以可比性优先、速度靠后的版本,真要谈高速率链路, 该问的是折叠配置。这件事讲的是另一半:从“能正确译码的设计”到“能一直正确译码的系统”之间的全部东西。

译码器是所有人都会问的那一部分,而它恰恰不是坏掉的那一部分。让一个仿真里能跑的设计和一个板子上能跑的设计 分开的,是它周围的管路:帧怎么搬、接收端在帧与帧之间怎么重新武装、以及当帧永远不停下来时这套还成不成立。 我们只公布自己实测过的数字、并注明在哪颗器件上测的,也包括那个没跑通的模式。 如果你要把一个深空纠错码核放到真实硬件上,这才是值得问的部分。

要把一个纠错码核放到真实硬件上吗?

我们把纠错码核从黄金模型一路带到能连续流式运行、且经得起反复重启的板卡上,每一层都用实测结果验证过。告诉我们你要让什么上天。

设计服务