3. 算法加速的硬件思维

在 CPU 上写算法时,我们习惯把程序看成一条按时间顺序执行的指令流:循环、分支、函数调用,数据在寄存器和缓存之间来回搬运。而 FPGA 没有“指令流”这个概念,它提供的是一块可编程的逻辑资源与布线资源,算法最终要变成一条由加法器、乘法器、比较器、BRAM、DSP 和寄存器搭起来的数据通路。所谓“硬件思维”,核心就是完成这次映射:把软件里“按时间一步步算”的过程,改写成“数据在空间上同时流动、被反复复用”的结构。

一、从“时间复用”到“空间展开”

软件算法天然是时间复用的:一个乘法器单元被循环反复调用 N 次。硬件映射的第一步,就是判断哪些计算可以空间展开——把循环体复制成多份并行硬件。例如一个长度为 8 的 FIR 滤波:

y[n] = Σ(k=0..7) h[k] * x[n-k]

软件上它是一次 8 轮循环;硬件上可以实例化 8 个乘法器,让 8 个抽头同时计算,再送入加法树。此时每个时钟周期就能产出一个输出样本,吞吐率提升约 8 倍,代价是 8 个 DSP 和相应的布线资源。这就是并行度(Parallelism)最直接的体现:用面积换吞吐。

但并行度不是越高越好。若把 8 路并行改成 4 路,则每个周期算 4 个抽头,需要 2 个周期完成一次输出,吞吐率减半、资源也减半。工程上通常先确定目标吞吐率(例如 100 MSPS),再反推所需并行度,而不是盲目展开。

二、流水线:把组合逻辑切成节拍

并行展开后,一个周期内要串行经过“乘法→加法树→累加”的长组合逻辑路径,路径延迟会限制最高时钟频率。解决办法是流水线(Pipelining):在组合逻辑之间插入寄存器,把长路径切成若干级,每级只承担一部分延迟。

以 8 抽头 FIR 为例,可切成三级:第一级 8 个乘法器并行;第二级加法树两两相加;第三级汇总并输出。插入寄存器后,时钟频率可从约 50 MHz 提升到 200 MHz 以上,代价是输出相对输入延迟了 3 个时钟周期(流水线延迟)。这里要区分两个指标:吞吐率(每周期产出多少结果)由并行度决定,延迟(从输入到输出的时钟数)由流水线级数决定。很多实时控制算法对延迟敏感,不能只追求吞吐。

8 抽头 FIR 的硬件数据通路映射(并行 + 流水线) x[n] z⁻¹ z⁻¹ z⁻¹ … ×h0 ×h1 ×h2 … 流水线寄存器(第 1 级) + + + y[n] 并行度 = 8(8 个乘法器同时工作) 流水线级数 = 3(吞吐率↑,延迟 = 3 周期)

三、数据复用:让每个数据被“用够”

并行和流水线解决的是“算得快”,数据复用解决的是“搬得少”。FPGA 的片上存储带宽有限,若每个乘法器都从外部存储器取一次数据,带宽会迅速成为瓶颈。常见复用手段有三类:

  • 系数复用:FIR 的 h[k] 是常量,可固化在逻辑或 ROM 中,不占运行带宽。
  • 输入复用:x[n-k] 被多个抽头共享,用一条移位寄存器链即可,无需重复读取。
  • 中间结果复用:矩阵乘法中,同一行/列的部分和可被多个输出复用,减少重复累加。

以 8×8 矩阵乘法为例,若每个输出元素独立计算,需要 512 次乘加;采用行广播 + 列广播的脉动结构后,每个周期可让 8 个乘加同时进行,且 A、B 元素各只读取一次,数据复用率显著提升。

四、存储层次:把数据放在对的地方

硬件映射必须同时规划存储层次。FPGA 典型层次为:寄存器(最快、最贵)→ 分布式 RAM / BRAM(片上、容量中等)→ 外部 DDR(大容量、高延迟)。映射原则是:

数据对象推荐位置理由
滤波器系数 h[k]ROM / 常量逻辑只读、固定,不占带宽
滑动窗口 x[n-k]移位寄存器 / 分布式 RAM每周期移动一格,访问频繁
行缓冲 / 块缓冲BRAM容量适中,支持双端口并发读写
整帧图像 / 大矩阵外部 DDR容量大,但需突发传输隐藏延迟

一个常见误区是把所有数据都塞进 BRAM,结果 BRAM 端口成为瓶颈。正确做法是让高频访问的小数据留在寄存器或分布式 RAM,让大块数据走 BRAM 突发,让 DDR 只承担帧级搬运。

五、映射方法小结

把软件算法映射到硬件数据通路,可以按以下顺序推进:先识别循环中可并行的维度,确定并行度;再按目标时钟频率切分流水线级数;然后分析每个数据的复用次数,决定广播还是复制;最后按访问频率把数据分配到寄存器、BRAM 或 DDR。四者相互制约:并行度提高会加剧带宽压力,流水线加深会增加延迟,存储层次选择又反过来限制并行度上限。实际工程中通常先做一次资源与带宽的粗估,再迭代调整,而不是一次性追求最优。

提示:在 Lattice FPGA 上,DSP 资源(如 ECP5 的 MULT18X18D)数量有限,并行度往往受 DSP 数量约束;此时可考虑用分布式逻辑实现小位宽乘法,或采用时分复用降低 DSP 占用,再配合更高时钟频率补偿吞吐率。