
公司在做一台电动跟随露营车,我这边负责其中的视觉链路:一颗 IMX585 4K 传感器,接到 PolarFire 传感器桥(HSB)rev2 接口板上,FPGA 把 MIPI CSI-2 数据打成 10G 以太网包,送进 NVIDIA AGX Thor。从 9 月中旬到现在,这条链路上能说"跑通"的东西,几乎都是在电脑里跑通的:仿真、综合、布局布线、时序。板子只真正上过两次:9 月 26 日不走网口抓到一张 160×120 的 RAW12 裁剪;10 月 14 日晚把 RAW8·4K30 的整机镜像烧进去,校验通过。Thor 那头的网口到现在还是 NO-CARRIER,一帧都没收到。下面是写到 10 月 15 日为止的进度,项目还在进行中,数字都来自交接目录里的任务报告。
链路长什么样
这条链路分三段。前端是 IMX585,3840×2160,它只输出 RAW10、RAW12 和 RAW16,没有 RAW8,这一点后面会用上。中间是传感器桥上的 FPGA(MPF200T):IOD 收四路 MIPI lane 并做训练,CSI-2 解码器校验 ECC 和 CRC、把字节解成像素,HSB_AXIS 把像素拼成 64 位、跨到 156.25 MHz 的网络时钟域,最后打包成 UDP 上的 RoCE 写请求,从 10G MAC 发出去。后端是 AGX Thor,跑传感器桥的主机软件收帧。控制面方向相反:Thor 发控制包,FPGA 里转成 APB 访问,再经 I²C 写传感器寄存器,开机那一套初始化是 271 步写操作。
9 月 29 日我把目标定成 3840×2160、60 fps,FPGA 只做桥,H.265 编码另有安排。所以下面五个验证任务里,有三个(滤波、编解码、ISP)验证的是 FPGA 里的处理,到现在都没接进整机。
先不接网口,抓一帧
第一条路线是先绕开网络:做一个诊断镜像,传感器按低速模式配置(RAW12、四路 lane、每路 594 Mb/s),收到的数据只裁 160×120 一块写进片内 RAM,抓完就冻结,再用 JTAG 调试工具把 RAM 读回电脑,重建成像素。
9 月 24 日第一次烧。控制链是通的:I²C 探测读回 BLKLEVEL 为 0x0032(这是存在性检查,不是芯片 ID),初始化跑到 ROM 第 271 项,错误位全零。图像不行:采集 good=0,CRC、ECC、像素几何、行几何几类错误一起亮。那一版用的 74.25 MHz 像素时钟本身就有 10 条跨时钟域的 hold 违例,最差 −3.733 ns;仿真里拿长行做负对照,也复现了 88 次错误的行结束。改成 49.5 MHz 以后,两种相位的仿真都过了。
两天后的第三版:四路 lane 训练完成,采集 done=1、good=1、errors=0,4800 个 48 位字两次读回逐字一致。行几何计数器读到每帧 2180 行、每行 960 个字,和 3840 像素、每拍 4 像素对得上,多出来的 20 行是什么还没确认。RAW12 值落在 212 到 396 之间,很暗,没做黑电平、白平衡和任何 ISP。到今天为止,这是唯一一次从真传感器拿到的像素。第二天板子挪到 Thor 旁边,USB-C 改接 Thor,Thor 的几个有线口全是 NO-CARRIER。
五个任务包
9 月 28 日,我把要验证的东西拆成五个独立任务包:每个包有 README、写明验收条件的 TASK、需要的核与参考资料快照。任务 02 在主会话里做,另外四个各交给一个子代理并行跑,凌晨被 API 限额打断过一次,下午接着跑完。每条结论都标了证据等级:RUNTIME 是工具真跑出来的,STATIC 是读源码和手册得来的,INFERENCE 是推算,UNKNOWN 是没做或没有参考。没有板子,五个任务全部是离线证据。
02 图像滤波:差异在实现里,不在夹具
边缘检测和锐化两个核,交接时留了一个没解释的现象:输入一个上黑下白的 3×3 台阶,Sobel 输出 4,锐化输出 253。这次三方对齐了:Gy = −1020 在 10 位有符号累加器里回绕成 +4;锐化那边是 |−765| 取低 8 位得 253;手册公式的期望是 255 和 −765。Verilog、VHDL 和逐位的参考模型三者一致,所以这是实现和手册之间稳定的差异,不是测试夹具的问题。
顺着查下来还有几条限制:图像宽度 Edge 要 ≥10、Sharpen 要 ≥11 才有输出;行内 valid 有气泡就会出错,16×8 的图每像素插一个空拍,输出数从 128 变成 720 和 661,还带 X;连续多帧需要足够的帧间空闲。边界行为是用 128 个位置的冲激响应反推出来的:第 0 行第 0 列不输出,每行末尾三个槽位重复同一个窗口,帧尾还会多吐出 W+1 个残留数据。
修复做了三版候选。第三版按手册重写了窗口控制器,86 个场景和零填充的理想模型逐像素相等,原版不出图或出错的那些情况全部消失;资源反而降了,Edge 从 857 个 4LUT 降到 409,Sharpen 从 1342 降到 532,行缓冲从 5 个减到 2 个。9 月 29 日我选了这一版,之后补跑了 1280×720 默认参数下的三个场景,3686400 个像素全对,131.25 MHz 像素时钟下时序收敛。没做的是板测和真实相机数据,零填充也是候选自己定的边界约定,手册没规定。
03 编解码:码流对得上,P 片在三种宽度上坏
JPEG 编码器在官方 512×512 激励下和参考输出逐字节一致,39 个场景过了 38 个;复位后的默认量化表不是 QP50,必须经 APB 重写;行间空闲大于等于 32 拍时,帧内最后一行 MCU 会坏。JPEG 解码器官方回环逐像素一致,31 个场景过 22 个,libjpeg 默认的多段 DQT/DHT 会让亮度解错,合并成单段就好;输入没有反压,长码流按每拍一个字送进去就没有任何输出。
H.264 编码器 I 帧和官方参考逐字节一致(末尾 4 字节要再给一次帧起始才冲得出来),I+P 在 432×240、320×240 等五种尺寸下正确,到 224×160、96×64、32×32 时 P 片语法损坏,根因没找到。裸核有 2746 个 I/O,比器件的 364 个多得多,所以又包了一层只为布局布线服务的外壳:两个 JPEG 核和 H.264 的 I 帧配置都收敛,I+P 配置在 175 MHz 像素时钟下差 1.126 ns,推算这个布局的上限在 148 MHz 左右。既然 FPGA 只做桥,这些结论眼下是备查。
04 ISP 像素管线:十个核各有各的脾气
输入就是 9 月 26 日那块真实的 RAW12 裁剪,加上合成的色块、斜坡和坏点图。每个核先单独和 NumPy 定点模型逐像素比。YCbCr 转 RGB、Gamma、增强三个核直接位精确;RGB 转 YCbCr 和白平衡算术是对的,但 valid 和数据错位,前者每个 valid 突发的最后两个样本是旧值,后者输出整体左移两个像素;去马赛克第一帧不输出,结果滞后一行加 5 拍;坏点修正核标着 EOF 的输入实际是帧起始,右边缘的坏点检测不出来;直方图第一帧只统计一半;双线性缩放的输出其实是 2×2 邻域里的最小值。
然后把 11 个核串成一条链:坏点 → 去马赛克 → 白平衡 → 去噪 → 增强 → Gamma → YCbCr → 锐化和边缘 → 直方图,外加 2:1 缩放。8 个场景通过,每一级都用上一级的真实输出做输入,出错能定位到单级。修复候选里,valid 对齐和白平衡对齐两个可以直接交付,缩放器的 v2 只是中间态。整条链没做综合,真实的 CFA 排列和颜色标定也还没有,预览里的 RGGB 只是试验参数。
05 视频带宽:4K60 卡在 lane 速率
这是唯一一个最后收敛成整机配置的任务。9 月 28 日的场景矩阵说得很清楚:4K60 RAW8 在参考路径上唯一超限的是 MIPI 入口,四路 lane 每路 1000 Mb/s 时行时间占用 105.9%,1050 Mb/s 是 101%,而这个速度等级的数据手册上限就是 1000 Mb/s,参考工程配的 1050 已经超了表值。8K60 在这块板子上不成立:单链路入口要 279%,按宽度四分需要四路 MIPI,板上只有两个连接器;源码里"两个主机、两个掩码"只是选路,不会把一帧拆成两半。
RTL 仿真里还有一个坑:HSB_AXIS 写侧没有反压,读侧停 19.2 µs 能扛住,停 76.8 µs 时 FIFO 写满,丢 1154 个字,连帧尾标记一起丢。时序上,156.25 MHz 网络时钟域里有一条读空标志的路径在子集布局里是负裕量,于是做了读侧重构的候选:和原版锁步比对 294 万次、0 失配,三个变体的裕量全部转正。
9 月 29 日定下 4K60 目标后,运行点 B 成了主攻:RAW10、每路 1440 Mb/s,比表值高 44%。当时子集布局里 byte 时钟域有 −1.44 ns,我回的是:
"没事,优化,然后多跑几次看看原因。加油深入研究B"(2026-09-29)
10 月 13 日查清楚了:那个负值是子集特有的,错误标志被布局器跨时钟域聚在一起,整机里这些悬空信号本来就会被剪掉。整机 B 一次收敛,7 个布局种子全部 met。但同时发现 B 的另一个问题:它把 40 位数据连续拼进 64 位流,这不是 CSI-2 的 RAW10 打包格式,Thor 上现成的 RAW10 解包核解不出来,要么主机自己写解包,要么 FPGA 再打包一遍。
10 月 14 日晚我改了目标:
"优先raw8,4k30."(2026-10-14)
IMX585 没有 RAW8 输出,驱动的位深寄存器和数据手册里都只有 RAW10、RAW12、RAW16,所以运行点 D 的做法是让传感器出 RAW10,每路 720 Mb/s,在表值以内;FPGA 里解码器按 10 位配置,每个像素只取高 8 位,就是一条接线;主机收每行 3840 字节的 RAW8,和参考设计原本接 IMX477 时的路径相同。时钟减半到 byte 90 MHz、像素 72 MHz。
按 IMX585 真实的行时序做了 9 个场景的仿真:7 个正常场景全部零错误,包括两整帧共 16896000 字节、周期性反压、3000 拍和 12000 拍的长停顿,12000 拍在 B 下会溢出,在 D 下 FIFO 峰值 2951/4096 全部吸收。另外两个是故意的反例:像素时钟取理论下界 62.2 MHz 会溢出,沿用参考工程的 131.25 MHz 字节虽然全对,但每行被拆成 70 段,下游的包结构没法用,所以像素时钟必须按公式取 72 MHz。整机 D 和 D2(D 加读侧候选)都一次通过,三个工艺角时序全部 met,byte 域裕量在 +4.1 ns 以上,D2 把参考工程原有的那条 +0.045 ns 路径去掉,全设计最小裕量 +1.108 ns。
当晚 23:14,D2 经本机烧进板子,PROGRAM 和 VERIFY 都通过,板上设计名从 9 月那个诊断镜像变成了整机顶层。这也意味着不接网口抓帧的那条路已经不在板子上了。
06 FOC:车的电机控制,只在合成电机上转过
这一项不属于视觉链路,是车的驱动电机,放在同一批交接里。13 个核和 Python 定点模型逐样本 0 失配,22 个测试台全部通过。Hall 支路单线卡死时角度误差最高到 100°,方向位还会翻,三线同时冻结没有任何告警,极性配反差 180°;无感观测器对定子电阻很敏感,填成 4 倍时角度直接反号到 −178°,对电感不敏感,转速低到 1 Hz 时观测失效。单轴闭环在一个参数明确标成"合成"的 PMSM 模型上跑了 2000 步,从触发到 PWM 更新是 101 拍,100 MHz 下 1.01 µs。
找到的缺陷里最典型的是 PI 控制器的一行接线:初始化信号接到了一个没声明的名字上,禁用时累加器被写成高阻,之后的输出里出现 X。这一行是 Codex 修的,后来又在它基础上加了抗饱和和观测器清零两个候选。多轴方面,每轴一套核占 4222 个 4LUT,8 轴占这块 FPGA 的 17.6%;时分共享省两成资源,但 8 轴串行要 8.1 µs,占 PWM 周期的 16%,所以建议逐轴复制。轴数、真实电机参数、功率级都还没定,车的控制怎么接进 Thor 的数据面也还没定义。
现在卡在哪
卡在 10G 链路上。烧完 D2 后,Thor 的五个有线口从 23 点多一直查到过了午夜,始终是 NO-CARRIER。现在的接法是传感器桥的 SFP+ 口插一个 10GBase-T 电口模块,Cat7 接到 Thor 的 RJ45 口上,那个口支持 1G、2.5G、5G 自协商。对着原理图和快速入门查下来,有三处要人去板子上看:两个 SFP+ 口的 TX_DISABLE 各自被上拉,要靠板上跳线 J4、J7 接地才使能模块,参考设计和 D2 都不驱动这两个引脚;供电跳线 J36 选的是 12 V 适配器还是 USB-C 的 5 V,电口模块大约要 2.5 W;模块插在哪个口。还有一个隐患:如果最后只协商到 1G,RAW8·4K30 要 2.1 Gb/s,一定丢包,得换 QSFP 转 4×SFP+ 的分支线。
Thor 这边,传感器桥主机软件 2.7.0 的原型容器镜像 15 日凌晨建好了,10.4 GB,Dockerfile 改了三处才在这边的网络环境下建成;演示镜像还在构建。给 IMX585 写的主机侧脚本还没在板上跑过。链路一亮,按事先写好的板测清单走:先看网口变成 LOWER_UP,枚举出两个数据通道,再做 I²C 探测,读回黑电平、位深、lane 速率、HMAX、VMAX 这几个寄存器,然后收 300 帧存第一帧,最后连续跑 10 分钟,看帧率是不是稳在 30.0 fps、帧尺寸是不是恒定。
挂在我这里等拍板的还有一串:任务 03 的 P 帧时钟目标和 DDR 仲裁模型,任务 04 的两个修复候选采不采纳、缩放器要不要做 v3、CFA 和颜色标定,任务 05 是否以 D2 作为最终整机配置,任务 06 的 PI 和观测器候选、轴数和电机参数。下一步是去板子上核对 J4、J7、J36 和模块插的口,等 Thor 那个口亮起来再往下走。