3. 色彩校正模块架构设计:顶层接口、数据流与流水线
好,咱们进入第三章。前面两章我们把色彩校正的数学原理和算法定点化聊透了。这一章,得真刀真枪地画架构图、定接口了。
我个人习惯,设计一个模块之前,先想清楚三件事:跟谁说话(顶层接口)、数据怎么走(数据流)、怎么跑得快(流水线)。这三件事定下来,RTL 代码基本就是填空了。
3.1 顶层接口定义
色彩校正模块,说白了就是一个三入三出的流水线处理单元。输入是 RAW 域的 R、G、B 像素值,输出是校正后的 R'、G'、B'。
我在一个 4K@60fps 的项目里吃过亏——接口定义时没留够余量,导致后端集成时不得不加一级寄存器打拍,时序差点崩了。从那以后,我定接口都遵循一个原则:能宽则宽,能带 valid-ready 就别用单握手。
| 信号名 | 方向 | 位宽 | 描述 |
|---|---|---|---|
| clk | I | 1 | 主时钟,通常为像素时钟 |
| rst_n | I | 1 | 异步复位,低有效 |
| pixel_in_valid | I | 1 | 输入像素有效标志 |
| pixel_in_ready | O | 1 | 模块可接收新像素 |
| pixel_in_r | I | 12 | 输入 R 分量 |
| pixel_in_g | I | 12 | 输入 G 分量 |
| pixel_in_b | I | 12 | 输入 B 分量 |
| pixel_out_valid | O | 1 | 输出像素有效标志 |
| pixel_out_ready | I | 1 | 下游模块可接收 |
| pixel_out_r | O | 12 | 校正后 R' 分量 |
| pixel_out_g | O | 12 | 校正后 G' 分量 |
| pixel_out_b | O | 12 | 校正后 B' 分量 |
| ccm_coeff[8:0] | I | 9×16 | 3×3 校正矩阵系数,s1.14 格式 |
3.2 数据流设计
数据流怎么走?你想想看,一个像素进来,先要跟矩阵系数做乘法累加。三个通道并行算,最后再拼回去。
我习惯把数据流画成一张图,这样写代码时思路特别清晰。下面这张图就是咱们模块的核心数据流:
3.3 流水线架构设计
为什么要用三级流水线?说白了,就是要把组合逻辑拆短。你看那个 3×3 矩阵乘法:
R' = R × C00 + G × C01 + B × C02
G' = R × C10 + G × C11 + B × C12
B' = R × C20 + G × C21 + B × C22
每个通道需要 3 个乘法 + 2 个加法。如果全部用组合逻辑,路径长度至少是:乘法器延迟(~2ns)+ 加法器树延迟(~1.5ns),在 500MHz 以下还能扛,但到了 1GHz 以上就必崩。
我记得有一次做 8K 芯片,像素时钟跑到 1.2GHz。第一版 RTL 把乘加全放一个周期里,综合后时序报告一片红。后来拆成三级流水线,每级只放一个乘法或一个加法,时序立马收敛。
3.4 三级流水线拆法
具体怎么拆?我建议这样分:
- Stage 1:输入寄存 + 系数准备
- 把 pixel_in_r/g/b 打进寄存器
- 从系数寄存器组读出对应的 C00~C22
- 这一级不做任何运算,纯打拍
- Stage 2:乘法器阵列
- 并行计算 9 个乘法:R×C00, R×C10, R×C20, G×C01, ...
- 每个乘法器输出位宽:12bit × 16bit = 28bit
- 结果寄存到下一级
- Stage 3:加法器树 + 输出寄存
- 每个通道的 3 个乘法结果做两级加法
- 第一级:加前两个,第二级:加第三个
- 最终结果截位到 12bit,输出
3.5 握手与背压处理
流水线架构里,valid-ready 握手怎么穿插?我习惯在每个 stage 之间都插一级 valid-ready 寄存器。这样每个 stage 都可以独立 stall。
具体实现上,用一组 shift register 把 valid 信号逐级传递。ready 信号则从后级往前级反压:
// 伪代码示意
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
stage1_valid <= 1'b0;
stage2_valid <= 1'b0;
stage3_valid <= 1'b0;
end else begin
// Stage 1: 输入握手
if (pixel_in_valid && stage1_ready)
stage1_valid <= 1'b1;
else if (stage2_ready)
stage1_valid <= 1'b0;
// Stage 2: 流水线传递
if (stage1_valid && stage2_ready)
stage2_valid <= 1'b1;
else if (stage3_ready)
stage2_valid <= 1'b0;
// Stage 3: 输出握手
if (stage2_valid && stage3_ready)
stage3_valid <= 1'b1;
else if (pixel_out_ready)
stage3_valid <= 1'b0;
end
end
你想想看,这种结构的好处是什么?每个 stage 的 ready 信号只取决于下一级是否空闲。只要下一级能收,本级就能放。整条流水线天然无锁,不会死锁。
3.6 面积与性能权衡
三级流水线听起来面积大,其实不然。我算过一笔账:
| 资源项 | 组合逻辑实现 | 三级流水线 | 差异 |
|---|---|---|---|
| 乘法器 (18×18) | 9 个 | 9 个 | 相同 |
| 加法器 | 6 个 | 6 个 | 相同 |
| 寄存器 (FF) | ~200 个 | ~600 个 | 多 400 个 |
| 最大频率 | ~400 MHz | ~1.2 GHz | 3 倍提升 |
多 400 个寄存器,换来 3 倍的频率提升。这笔买卖,我觉得值。
3.7 本章小结
嗯,这一章我们把色彩校正模块的架构定下来了。顶层接口用了 valid-ready 握手,数据流是三通道并行,流水线拆成三级。这些设计决策,都是我在多个项目里反复验证过的。