学好FPGA,当然需要一定的基础,在俺看来,最基础的是数学,所以期待初学FPGA的童鞋,能够真正重视数学,把大学数学这门课认真学好。
从FPGA本身来说,无论用在哪个行业、进行什么样的处理,归根结底他其实就做两种运算:一是逻辑运算,二是数学运算,这数学运算的根基除了相应的专业知识以外,便是数学。
举个极其简单的例子,比如两个32bit的数据X[31:0]与Y[31:0]相乘。当然,无论Altera还是Xilinx都有现成的乘法器可以调用, 从实现最简单这一方面来说的话,直接调用现成的IP最简单,可两个32bit的乘法器将耗费大量的资源。我们可以稍微简化如下:
将X[31:0]拆成两部分X1[15:0]和X2[15:0],令X1[15:0]=X[31:16],X2[15:0]=X[15:0],则X1左移16位后与X2相加可以得到X;
同样将Y[31:0]拆成两部分Y1[15:0]和Y2[15:0],令Y1[15:0]=Y[31:16],Y2[15:0]=Y[15:0],则Y1左移16位后与Y2相加可以得到Y;
则X与Y的相乘可以转化为X1和X2分别与Y1和Y2相乘,这样一个32bit*32bit的乘法器用了四个16bit*16bit的乘法器和三个32bit的加法器替代。
有兴趣的童鞋,不妨综合一下看看,这两者的面积区别是多大。
或许上面的这个例子不是很恰当,并不能很充分的说明数学的重要性,那还有一个常见的例子,比如求对数函数ln(x)。
如果x是一个常数或者在有限范围内取值,那么很简单,我们可以使用计算器把所有的值都算好,再根据系统精度的要求直接将ln(x)用所有的常数替代,比如ln2就可以使用0.69来代替(假设精度只要求精确到小数点后两位),将这些常数存到查找表中去;
如果x是一个变量,如果其取值时变,此时如何计算?考虑到FPGA尤其适合于乘加运算,因此我们便可以考虑将其展开成幂级数的形式。
像上面的这些简单例子,如果数学功底比较好,可以很容易想到办法就把问题解决,相反,如果需要用到两ln(x)的时候,我们不记得还有幂级数展开这一手段,怎么办?
我们毕业开始工作,并不是每家公司都会配有专门的算法工程师,来进行信号处理过程中的每一步运算过程的分解;单就这一点,我们就应该好好把数学学好,这样在以后的工作中,碰到类似的数学处理过程时,才能够做到游刃有余。
OK,前面有提到FPGA运算包括了逻辑运算和数学运算,其中逻辑运算的基础就是数字逻辑电路设计,下面简单罗列几个概念以及常见电路,都是必须要熟练使用滴。
FPGA设计,卡诺图化简是逻辑设计过程中的最常用的手段,而在高速设计过程中,则常常考虑使用逻辑复制来提高电路运行速度,关于这部分将在后面高速设计部分举例详细说明。
数制,是另一个基础的概念。二进制、十进制、十六进制之间的相互转换,格雷码、独热码、BCD码这些常用码的设计以及相互转换,小数的二进制表示、负数的二进制形式,都需要熟练掌握。
在实际FPGA应用中,几乎所有的设计都会用到计数器,分频电路、ram的地址产生、时序电路状态的控制等,都是计数器的典型应用。对于计数器,俺想着重强调的是计数器的清零操作,尤其是计数器应用于状态的控制时,一定要仔细设计,清零不合理或者清零时刻没有设计好,也许在一般的工作情况下都能正常运转,但碰到一些特殊情况,就比较容易出问题,通常遇到这样的情况时,调试都比较耗时间。另外,在平常应用过程中,我们最常用的是加计数,但在某些情形下,减计数器有意想不到的妙用,比如有一个32bit的向量,现在要找出其最前面三个1的位置,使用减计数器可以很轻易地实现该功能。
在设计FPGA外围电路时,我们经常会用到I2C、SPI这样的总线接口,在处理这样的总线接口时,串并转换、并串转换是必然之选;串并转换、并串转换电路,又是FPGA面积与速度平衡原则的重要体现。实现串并之间的转换,一种常见的手段是采用移位寄存器,简单明了。
上面提到的一些概念或者器件,都是一些基础,所谓复杂的时序逻辑电路设计,其实就是把这些最简单的逻辑电路以及寄存器有机组合起来。我曾经有幸见到过一张图纸,使用了大量的74系列逻辑电路去实现FFT运算,FFT够麻烦吧,可用最简单的74逻辑电路就能把它实现,当然这前提就是基础知识足够扎实。