CPU 的主频高达几个 GHz,FPGA 的速率往往在几百兆。但是,往往我们会说 FPGA 会给 CPU 进行加速。
 
虽然 CPU 主频很高,但其是通用处理器,做某个特定运算(如信号处理,图像处理)可能需要很多个时钟周期;而 FPGA 可以通过编程重组电路,直接生成专用电路,加上电路并行性,可能做这个特定运算只需要一个时钟周期。
       
假设我们用 FPGA 完整的实现了 CPU,然后再跑软件的话,的确比 CPU 慢。问题是 FPGA 不会那么干,它会直指问题本质,解决问题。
 
     
 
即使我们用 FPGA 实现一个 CPU,也是为了做一些芯片验证或者说需要一些需要 CPU 和 FPGA 需要紧密结合的场景,这种场景现在也逐步由 SoC 的 FPGA 实现了。
 
 
举个具体的例子,比如有两个数组,其中有 256 个 32 位数,我们现在要把它们对应相加变成一个数组,用 CPU 写最快大概是这样子的:
 
r[0] = a[0] + b[0];
 
r[1] = a[1] + b[1];
 
...
 
r[255] = a[255] + b[255];
 
当然也可能会这么写(在分支预测准确,指令缓存不大的情况下可能更快):
 
for (int i = 0; i < 255; i++)
   
r[i] = a[i] + b[i];
 
CPU 指令流水线
根据之前描述的基础,指令进入流水线,通过流水线处理,从流水线出来的过程,对于我们程序员来说,是比较直观的。
 
I486 拥有五级流水线。分别是:取指(Fetch),译码(D1, main decode),转址(D2, translate),执行(EX, execute),写回(WB)。某个指令可以在流水线的任何一级。
 
 
流水线的数量不可能无限制增加,流水线的加速也有很多风险。即使流水线可以无限制增加,不管 CPU 的流水线又多少条,每个指令执行都必须顺序执行。对速率的提升也是有限的。
 
对于上图中的流水线有一个明显的缺陷。对于下面的指令代码,它们的功能是将两个变量的内容进行交换。
 
 
第二条 xor 指令需要第一条 xor 指令计算的结果 a,但是直到第一条指令执行完成才会写回。所以流水线的其他指令就会在当前流水级等待直到第一条指令的执行和写回阶段完成。第二条指令会等待第一条指令完成才能进入流水线下一级,同样第三条指令也要等待第二条指令完成。
 
这个现象被称为流水线阻塞或者流水线气泡。
 
对 FPGA 来说,也可以用上面相同的写法,不同在于:
 

 

CPU 是一个一个加法计算,而 FPGA 排好逻辑电路,在一个时钟周期内计算完毕。就算 CPU 主频比 FPGA 快 100 倍也赶不上啊。
 
计算机最基本的功能就是运算了,其中最基本的又要属加法运算。我们知道计算机使用二进制来保存和处理数据,因此这里的加减法运算都是用二进制进行。下面雅乐网总结了一些运算器中加法器的设计。
 
二进制加法
 
 
一位的二进制加法非常简单,因为只有四种情况
 
0+0=0 进位 0
 
0+1=1 进位 0
 
1+0=1 进位 0
 
1+1=0 进位 1
 
多位的二进制加法 和十进制类似,每一位上两个数相加后再加上进位就可以了
 
FPGA 半加器
半加器可以实现两个 1 位的二进制数字相加,并且输出结果和进位。
 
真值表:
 
 
一位半加器真值表
 
由表中可以看见,这种加法没有考虑低位来的进位,所以称为半加。
 
输出和:A 和 B 一个为 0 一个为 1 的时候输出 1  两个 0 或两个 1 输出 0  因此 可以用异或门连接。
 
进位:只有 AB 均为 1 的时候进位输出 1  进位 Cout = AB 用与门连接。
 
 
一位半加器
 
FPGA 全加器
全加器在半加器的基础上 增加了进位 它输入三个数字 两个加数 和 一个进位 Cin,输出结果 和 进位
 
 
一位全加器真值表
 
从真值表很容易写出如下逻辑表达式
 
S=P 异或 Cout      其中 P=A 异或 B
 
Cout=P·Cin+G    其中 G=A·B
 
 
一位全加器
 
四位行波加法器
将四个全加器 每一个的仅为输出连接到下一个的进位输入 就可以构成一个 4 位串行加法器了
 
 
四位行波加法器
 
这样构成的加法器连接起来很简单,但是也有不足:每一个全加器计算的时候必须等待它的进位输入产生后才能计算,所以四个全加器并不是同时进行计算的,而是一个一个的串行计算。这样会造成较大的延迟。
 

 

超前进位加法器(Carry-Lookahead Adder,CLA)
超前进位加法器的思路是提前算出每一位上的进位。
 
分析每一个全加器的局部
 
一个全加器 A B Cn 如果有两个或两个以上的 1 那么进位 Cn+1 就是 1
 
因此 Cn+1 = AB + ACn + BCn = AB + Cn(A+B)
 
记生成(Generate)信号:Gi=Ai·Bi
 
传播(Propagate)信号:Pi=Ai+Bi
 
那么 Ci+1=Gi+Pi·Ci
 
根据这个递推关系,可以推导出每一位的进位
 
每一位的进位跟前一级进位有关,前一级 又跟 前一级的前一级有关 一直向前 最终是和 C0 相关。而最开始的第一位 C0 和 A0 B0 都是已知的
 
 
这样算出结果只需要经过三个门延迟就可以了。
 
随着位数的增加,进位的计算公式会越来越复杂。32 位的进位计算需要 32 与门
 
 
所以我们需要更多的计算位宽或者更大的数组,或者矩阵的运算的时候,我们使用 FPGA 的优势就体现出来。再多的计算,也就是放置更多的逻辑资源。
 
FPGA 的并行是真并行,CPU 完全没得比。CPU 如果想并行最多也就是让多个核并行,但是对于大部分算法实现来说,如上例,多个核之间的同步调度开销远远大于计算开销,就算多个核之间的调用开销可以做的很小,一般 CPU 也就那几个核,而 FPGA 只要门足够,想并行几路就可以并行几路。
 
所以在做可并行的计算密集型任务时,比如信号处理,网络传输等等 FPGA 可以帮上忙;但是如果做我们常见的串行为主的任务而言,FPGA 的确远远比不上 CPU。
 
FPGA 是配角
使用 FPGA 不一定总能加速,只是在某些强计算和数据处理的方面,因为其硬件电路并行运行和有很多 DSP 硬核资源供调用的特点,可以工作得更出色。FPGA 本身也只是辅助角色,起控制的还是 CPU 本身,所以 FPGA 并不能代替 CPU,只是在完成一件大任务的过程中将某部分任务分解给 FPGA 可以更好地一起完成任务。在这过程中也会有额外的开销产生,在某些场合,可能用了 FPGA 而效果更差也是有的。
 
另外,通常说的使用 FPGA 加速比 CPU 和 GPU 省电,是指在完成同样的任务下,FPGA 耗费的电力比起 CPU 和 GPU 更少一些,相对而言的,并不是说 FPGA 本身就一定省电。
 
FPGA 的弱点
1. 开发周期长。需要对特定的应用编写特定的 FPGA。只要干的事情稍有不同,一般来说 FPGA 代码就要重新写一遍或者是至少要修改很多东西。开发代价和码农码几行代码完全不可比。而且一般需要若干工程师花费若干周才能搞的定。重点是会写的人还不多。
        2. 并不是所有东西都适合 FPGA。FPGA 适合于做那些可并行计算的东西,例如矩阵运算。如果是一些判断类的问题,FPGA 算得并没有 CPU 快(这个时候时钟周期才是关键因素)。所以现实中都是有人来分析,在整个任务中有哪些地方可以并行计算,把这些地方替换为 FPGA 卡。
 
由于现在机器学习、大数据比较火,里面矩阵运算成千上万,所以没有出现定制的 ASIC 之前,FPGA 加速还是业界主流。现在百度、微软、谷歌等公司都已经把 FPGA 加速运用到了实际的商业运营中。