3. 图像数据流与流水线架构设计

星载图像处理与地面通用计算最大的差别,在于数据是"流"进来的:探测器按行、按像素、按固定节拍持续输出,FPGA 必须在像素到达的那一刻就完成处理,不能像 CPU 那样"先存下来再慢慢算"。因此,流水线架构设计的核心问题不是"算法怎么写",而是"数据以什么节奏流动、在哪些位置需要缓存、每一级要花几个时钟、整条链路的吞吐率和延迟是多少"。

像素流:一切设计的基准节拍

像素流(pixel stream)是图像数据在硬件中流动的基本形态,通常由三个信号共同描述:像素数据(如 8/10/12/16 bit)、行有效信号(line valid / DE)、帧同步信号(frame valid / VSYNC)。三者配合,才能确定"当前这个数据属于第几行第几列"。

像素流的节拍由像素时钟 pclk 决定。假设某星载相机行周期为 20 μs,每行 2048 个像素,则有效像素率约为 2048 / 20 μs ≈ 102.4 Mpixel/s;若每个像素 16 bit,则原始数据率约 1.64 Gbit/s。这个数字直接决定了后续每一级流水必须达到的最低吞吐率——任何一级处理不过来,都会造成行缓冲溢出或数据丢失。

实际设计中还要区分"有效像素"与"消隐期"。消隐期虽然不传数据,但往往是行缓冲写入、状态机切换、参数更新的窗口,不能简单忽略。

行缓冲:跨行运算的必需结构

很多图像算法(如 3×3 卷积、Sobel 边缘检测、中值滤波)需要同时访问当前行、上一行、上上行。FPGA 无法像软件那样随机访问整幅图像,因此必须用行缓冲(line buffer)保存最近若干行。

行缓冲通常用 FPGA 内部的 Block RAM 实现,深度等于一行像素数,位宽等于像素位宽。以 2048 像素、16 bit 为例,一行缓冲约 2048 × 16 bit = 32 Kbit,3 行共约 96 Kbit,对多数星载 FPGA 而言完全可承受。行缓冲的读写采用"乒乓"或"移位"策略:新像素写入当前行,同时从旧行读出参与运算,保证每个时钟周期都能输出一个结果。

行缓冲的关键约束是读写带宽。若一行 2048 像素、pclk 为 100 MHz,则每行写入耗时约 20.48 μs,与行周期基本匹配。若算法需要同时读 3 行,则每周期需完成 1 写 3 读,必须使用真正的多端口 BRAM 或复制存储,否则会出现读写冲突。

帧缓冲:跨帧、跨区域操作的支撑

当算法需要访问非相邻行、非相邻帧,或需要做图像配准、帧间差分、多帧累加时,行缓冲就不够了,必须引入帧缓冲(frame buffer)。帧缓冲一般用外部 DDR 实现,容量按"宽 × 高 × 位宽 × 帧数"估算。例如 2048 × 2048 × 16 bit ≈ 64 Mbit/帧,若需缓存 4 帧则约 256 Mbit。

帧缓冲的代价是延迟和带宽。DDR 的随机访问延迟通常在几十到上百个时钟周期,因此帧缓冲适合"整帧写入、整帧读出"的批处理模式,而不适合逐像素实时通路。工程上常见的折中是:行缓冲承担实时流水,帧缓冲承担帧级缓存与重排,两者分工明确。

星载图像流水线:像素流 → 行缓冲 → 流水级 → 帧缓冲 探测器像素流 pclk / DE / VSYNC 2048 px × 16 bit ≈102.4 Mpixel/s 行缓冲 Line Buffer 3 行 × 2048 × 16 bit ≈96 Kbit BRAM 1 写 3 读 / 周期 流水级划分 Pipeline Stages S1 去噪 S2 卷积 S3 阈值 S4 打包 帧缓冲 Frame Buffer DDR 外部存储 64 Mbit / 帧 帧级重排 / 累加 时钟周期 t0 t1 t2 t3 t4 t5 t6 P0 P0 P0 P0 P0 P1 P1 P1 P1 P1 P2 P2 P2 P2 P2 流水填充期:前 4 个周期逐级填满,稳态后每周期输出 1 个像素 吞吐率 Throughput 稳态:1 pixel / clk 100 MHz → 100 Mpixel/s 延迟 Latency 4 级 × 1 clk = 4 clk 100 MHz → 40 ns 帧缓冲延迟 整帧写入 + 读出 ≈ 2 帧周期(示例)

流水级划分:把组合逻辑切成时钟节拍

流水级划分的本质,是把一条长的组合逻辑路径切成若干段,每段之间插入寄存器,使每段的最长路径都能在 1 个时钟周期内稳定。划分的依据是时序收敛:先用综合工具报告每级的关键路径延迟,再决定是否需要再切一刀。

以 3×3 卷积为例,若把"乘加 + 累加 + 饱和"放在一级,关键路径可能达到 12 ns,只能跑到约 80 MHz;若切成"乘 → 加 → 饱和"三级,每级约 4 ns,则可跑到 200 MHz 以上。代价是延迟从 1 个周期增加到 3 个周期,但吞吐率不变——这正是流水线的价值:用延迟换频率,用频率换吞吐。

划分时还要注意"级间平衡"。若某一级明显比其他级慢,它就会成为瓶颈,其他级再快也无用。工程上通常让各级延迟差异控制在 20% 以内。

吞吐率与延迟:两个必须同时盯住的指标

吞吐率(throughput)指单位时间内能处理多少像素,稳态下等于"每周期输出像素数 × 时钟频率"。若每周期输出 1 个像素、时钟 100 MHz,则吞吐率为 100 Mpixel/s。它必须大于等于探测器像素率,否则数据会堆积。

延迟(latency)指一个像素从进入流水线到输出结果所经历的时钟数。它等于各级寄存器延迟之和。对于星载实时处理,延迟通常要求控制在几十个时钟以内;若引入帧缓冲,延迟会跃升到"帧级",需要单独评估是否满足任务时限。

两者关系可以这样理解:吞吐率决定"能不能跟上",延迟决定"结果来不来得及用"。设计时先保吞吐率,再压延迟;若延迟无法满足,就要减少流水级数或改用并行结构,而不是简单加缓冲。

吞吐率与延迟的估算公式:吞吐率 = 每周期输出像素数 × fclk;延迟 = Σ 各级寄存器数 × Tclk。二者都必须在设计早期用纸面计算确认,不能等到上板再补。

一个可参考的流水线参数示例

假设某星载相机输出 2048 × 2048、16 bit、帧率 25 fps,像素时钟 100 MHz。设计目标:实时完成去噪、3×3 卷积、阈值分割、数据打包。可参考如下配置:

流水级功能寄存器延迟资源占用(示例)
S13×3 中值去噪2 clk行缓冲 3 行 + 比较器
S23×3 卷积(Sobel)3 clk9 乘法器 + 加法树
S3阈值分割1 clk比较器 + 查找表
S4数据打包 / 输出1 clkFIFO + 接口逻辑

按此配置,总延迟为 7 个时钟周期,即 70 ns;稳态吞吐率为 100 Mpixel/s,高于相机所需的 2048 × 2048 × 25 ≈ 104.9 Mpixel/s 中的有效像素率(考虑消隐后约 80~90 Mpixel/s),留有约 10%~20% 余量。行缓冲共需 3 行 × 2048 × 16 bit ≈ 96 Kbit BRAM,属于可接受范围。

若把 S2 的卷积与 S3 的阈值合并为一级,关键路径可能超过 10 ns,100 MHz 下时序不收敛;此时应优先拆分,而不是降低时钟频率——降频会直接拉低吞吐率,可能无法满足相机像素率。

设计检查要点

  • 像素流的三要素(数据、DE、VSYNC)是否与探测器时序严格对齐,消隐期是否被正确利用。
  • 行缓冲的读写端口数是否满足"1 写 N 读"需求,是否出现同地址读写冲突。
  • 每一级的关键路径是否在目标时钟周期内收敛,级间延迟是否均衡。
  • 吞吐率是否大于探测器有效像素率,并保留至少 10% 余量。
  • 延迟是否满足任务时限;若引入帧缓冲,是否单独评估了帧级延迟。