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 的随机访问延迟通常在几十到上百个时钟周期,因此帧缓冲适合"整帧写入、整帧读出"的批处理模式,而不适合逐像素实时通路。工程上常见的折中是:行缓冲承担实时流水,帧缓冲承担帧级缓存与重排,两者分工明确。
流水级划分:把组合逻辑切成时钟节拍
流水级划分的本质,是把一条长的组合逻辑路径切成若干段,每段之间插入寄存器,使每段的最长路径都能在 1 个时钟周期内稳定。划分的依据是时序收敛:先用综合工具报告每级的关键路径延迟,再决定是否需要再切一刀。
以 3×3 卷积为例,若把"乘加 + 累加 + 饱和"放在一级,关键路径可能达到 12 ns,只能跑到约 80 MHz;若切成"乘 → 加 → 饱和"三级,每级约 4 ns,则可跑到 200 MHz 以上。代价是延迟从 1 个周期增加到 3 个周期,但吞吐率不变——这正是流水线的价值:用延迟换频率,用频率换吞吐。
划分时还要注意"级间平衡"。若某一级明显比其他级慢,它就会成为瓶颈,其他级再快也无用。工程上通常让各级延迟差异控制在 20% 以内。
吞吐率与延迟:两个必须同时盯住的指标
吞吐率(throughput)指单位时间内能处理多少像素,稳态下等于"每周期输出像素数 × 时钟频率"。若每周期输出 1 个像素、时钟 100 MHz,则吞吐率为 100 Mpixel/s。它必须大于等于探测器像素率,否则数据会堆积。
延迟(latency)指一个像素从进入流水线到输出结果所经历的时钟数。它等于各级寄存器延迟之和。对于星载实时处理,延迟通常要求控制在几十个时钟以内;若引入帧缓冲,延迟会跃升到"帧级",需要单独评估是否满足任务时限。
两者关系可以这样理解:吞吐率决定"能不能跟上",延迟决定"结果来不来得及用"。设计时先保吞吐率,再压延迟;若延迟无法满足,就要减少流水级数或改用并行结构,而不是简单加缓冲。
一个可参考的流水线参数示例
假设某星载相机输出 2048 × 2048、16 bit、帧率 25 fps,像素时钟 100 MHz。设计目标:实时完成去噪、3×3 卷积、阈值分割、数据打包。可参考如下配置:
| 流水级 | 功能 | 寄存器延迟 | 资源占用(示例) |
|---|---|---|---|
| S1 | 3×3 中值去噪 | 2 clk | 行缓冲 3 行 + 比较器 |
| S2 | 3×3 卷积(Sobel) | 3 clk | 9 乘法器 + 加法树 |
| S3 | 阈值分割 | 1 clk | 比较器 + 查找表 |
| S4 | 数据打包 / 输出 | 1 clk | FIFO + 接口逻辑 |
按此配置,总延迟为 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,属于可接受范围。
设计检查要点
- 像素流的三要素(数据、DE、VSYNC)是否与探测器时序严格对齐,消隐期是否被正确利用。
- 行缓冲的读写端口数是否满足"1 写 N 读"需求,是否出现同地址读写冲突。
- 每一级的关键路径是否在目标时钟周期内收敛,级间延迟是否均衡。
- 吞吐率是否大于探测器有效像素率,并保留至少 10% 余量。
- 延迟是否满足任务时限;若引入帧缓冲,是否单独评估了帧级延迟。