本节将讨论在使用FPGA执行数字信号处理时所关心的一些问题。
在一个逻辑变量从输入到输出的过程中,会存在逻辑延迟和布线延迟。在一个系统中,关键路径是指在两个由时钟驱动的寄存器中间最长的组合逻辑路径。这里的“最长”是指传播时间。关键路径延迟是指关键路径的时间延迟,如通过电路最长组合逻辑的传播延迟。例如,图2.50中给出的关键路径,表示这条路径是寄存器a和寄存器b之间传播时间最长的路径。很明显,从寄存器a的逻辑输入,经过与非门、异或门和与门后,到寄存器b。由于逻辑门的翻转延迟和布线的传输延迟,使得寄存器a的输入逻辑需要经过一段时间后才能到达寄存器b。
图2.50 关键路径的定义
下面再举一个算术运算的过程,如图2.51所示。当考虑算术运算时,读者需要注意信号不是1比特宽度,而是由几比特构成的总线,这通常与算术运算的字长一致。运算结果的最后一位,即在加法和减法的最高有效位(Most Significant Bit,MSB)是不可用的,直到完成了计算为止。在计算的过程中,从最低有效位(Least Significant Bit,LSB)将进位传递到最高有效位。因此,就涉及较宽的字长和较长的关键路径。
假设图2.51中给出的两个逻辑输入A和B,它们的字长均为10位,则A+B的结果是11位的。显然,用于计算A+B+C的第二个加法器的宽度为11位。因此,第二个加法器的传播延迟大于第一个加法器。
图2.51 复杂算术运算的关键路径
很明显,乘法器的关键路径要长于类似字长的加法器,如图2.51所示。由于逻辑操作所产生的延迟远大于布线延迟,因此在后面将忽略布线延迟,这样可以使问题分析更加简单。
一个典型设计的关键路径如图2.52所示。将图2.52中的关键路径延迟表示为 ,它是一些逻辑延迟和布线延迟的总和,表示为
其中, 为逻辑延迟; 为布线延迟。
图2.52 关键路径的延迟表示
一般情况下,逻辑延迟在总延迟中占有绝对的分量(注意:但不是绝对的)。因此,DSP和FPGA设计工程师对逻辑延迟的关注更多。对于布线延迟而言,通常由设计工具(如Xilinx的Vivado布局和布线工具)或者人工高级干预的方式进行处理。
为什么关键路径非常重要?这是因为它限制了驱动该系统工作的最高时钟频率。对于设计而言,最高的时钟工作频率f clk(max) 表示为
如果时钟频率小于这个值,则信号可以在1个周期内从寄存器a的输出到达寄存器b的输入,这对于保证逻辑功能的正确性非常重要。如果时钟频率大于这个值,则不能保证这个逻辑电路的正常工作,不是逻辑门不能正常翻转,就是信号无法按时到达下一个逻辑门。
在基于FPGA的数字信号处理系统设计中,最高时钟频率f clk(max) 是一个最关键的性能指标。f clk(max) 越大,表示设计性能越好。最小的时钟周期(最高时钟频率)由关键路径延迟限制,即 ,如图2.53所示。
图2.53 关键路径延迟对最小时钟周期的限制
很明显,为了使设计有效,在相同的时钟周期内,要求从组合逻辑的输出到作为输入时必须变成有效。如果不是这种情况,就好像组合逻辑包含了至少1个时钟周期的延迟。从数学的角度来说,这将从根本上改变所实现算法的功能,使得它所实现的算法功能出现。
此外,在设计的时候一定要尽量避免在寄存器的建立和保持时间内改变信号,如在活动时钟跳变的一个很短的时间范围内。因此,真正的最小时钟周期应该比关键路径的延迟要稍微长一些,即
注 :基于讨论的目的,在后面的讨论中将不考虑建立时间(t setup )和保持时间(t hold )。但是,Xilinx的Vivado工具将会处理这个问题。
思考与练习2-5 :请说明关键路径的定义,以及关键路径对最高时钟频率的影响。
前面提到,时钟频率依赖于组合逻辑路径的长度。因此,可以考虑在组合逻辑中间插入一些寄存器来消除它们之间的依赖性,这种方法称为流水线。
继续考虑图2.53给出的例子,在寄存器A和B之间插入寄存器P,将一个组合逻辑路径分为两个相同的部分,如图2.54所示。从图2.54中可知,增加额外流水线寄存器的好处就是将时钟的频率提高了1倍;但是,使得从B的输出延迟了1个时钟周期。
图2.54 在寄存器A和寄存器B之间插入寄存器P
在前面的例子中, ,因此最高的时钟频率可以达到625MHz。由于插入了流水线寄存器,使得关键路径的延迟缩短为原来的1/2。因此,最高时钟频率可以达到
更进一步,可以插入更多的流水线寄存器来提高时钟的工作频率。当把最初设计的路径分割成4个相同的部分时,时钟的最高工作频率f clk(max) 可以达到
思考与练习2-6 :请说明在FPGA设计中,插入流水线寄存器的作用,以及它对改善性能的影响。
很明显,插入流水线寄存器会使得输入到输出出现额外的延迟。对于每个流水线寄存器而言,延迟增加了1个时钟周期,如图2.55所示。
图2.55 插入流水线寄存器增加额外的延迟
从图2.55中可知:
(1)在第1个设计中,在D A 输入2个时钟周期后在Q B 观察到它的输出。因此,该设计的延迟是2个时钟周期。
(2)在第2个设计中,通过添加寄存器P,增加了3个时钟周期。
(3)与第1个设计相比,在第3个设计中,增加了3个流水线寄存器P 1 、P 2 和P 3 。因此,整个设计的延迟变成2+3=5个时钟周期。
在很多情况下,增加1~2个时钟周期来改善时序是可接受的结果。然而,一个例外的情况是反馈回路,这是因为在这个回路上每个新的计算结果不能开始,直到计算完最后一个结果为止。在这种情况下,对输出进行延迟则没有任何帮助。
注 :本书后面将通过介绍重定时信号流图来指导读者在保证不破坏所实现算法功能的基础上正确地插入流水线寄存器。
思考与练习2-7 :请说明在FPGA设计中,插入流水线寄存器对设计的不利影响,以及使用时应注意的事项。
在后面的章节中,读者将进一步明白数字信号处理/大数据处理,乃至现在流行的人工智能(Artificial Intelligence,AI)本质上都是大量的乘法、加法、乘和累加的运算。因此,在本节中,通过对FPGA内的硬件加法器和乘法器的详细介绍,进一步说明为什么在未来AI或者大数据处理中,FPGA将扮演更加重要的角色。
数字信号处理严重地依赖于算术运算,因此需要从本质上理解它们的实现方式。前面说过,大多数的数字信号处理算法,包括后面将介绍的有限冲激响应(Finite Impulse Response,FIR)滤波器,只使用乘法和加法运算(很少使用除法和均方根等运算)。
从实现的最底层角度来看,所有的乘法和加法操作都由全加器完成。最简单的全加器可以实现两个1比特数据的相加,如图2.56所示。1位全加器的实现原理如表2.5所示。根据表2.5给出的逻辑关系,可以得到1位全加器的逻辑电路,如图2.57所示。
图2.56 1位全加器及其运算举例
表2.5 1位全加器的实现原理
图2.57 1位全加器的逻辑电路
对于1位全加器而言,可以实现两个1比特数据的相加。将1位全加器级联就可以构成多位的全加器,实现多个比特位的相加运算。4位全加器的结构如图2.58所示。对这个结构稍加修改,就可以扩展到任意位的加法运算。
图2.58 4位全加器的结构
注 :该结构的最后一个进位输出C 3 ,构成了求和输出的MSB。
很明显,修改这个结构就可以实现4位全减器的功能,如图2.59所示。从图2.59中可知,要实现A-B的运算,B的所有位取反,并且第一个全加器的进位输入设置为1。
图2.59 4位全减器的结构
注 :对于加法操作,将第一个全加器的进位输入设置为0。
前面介绍了全加器和全减器的实现原理,下面介绍一下它们在Xilinx FPGA内的映射关系。
使用Verilog HDL描述4位全加器的功能,如代码清单2-3所示。
代码清单2-3 top.v文件
注 :读者可以定位到本书提供资料的\fpga_dsp_example\four_adder_mapping\Project2\目录下,用Vivado 2017.2打开project_2。
该4位全加器的电路结构如图2.60所示。
图2.60 4位全加器的电路结构
注 :该结构由Xilinx Vivado 2017.2工具的Elaborated Design过程得到。
选定Artix 7系列的FPGA,经过Xilinx Vivado 2017.2工具综合后,其真实的电路结构如图2.61所示。此时,可以看到已经转换成Xilinx FPGA内所提供逻辑资源的形式。
图2.61 4位全加器经描述综合后的电路表示
对图2.61所给出的4位全加器综合后的电路进行布局布线后的结果如图2.62所示。从图2.62中可知,上面的电路映射到了LUT和进位链资源,并且通过FPGA内提供的互连线资源将它们连接在一起。
图2.62 4位全加器在Xilinx 7系列FPGA内的映射
思考与练习2-8 :请说明全加器/全减器电路的实现原理,以及与FPGA内逻辑资源的对应关系。
思考与练习2-9 :众所周知,在CPU和DSP中通过使用C语言编写软件代码就可以实现4位宽度数据的全加操作。请比较使用FPGA实现加法功能与使用软件C语言代码实现加法功能的本质区别。
对于一个乘数和被乘数为4位的乘法操作而言,要求16个乘/加单元,如图2.63所示。
图2.63 4位乘法器的原理结构
在4位乘法器的结构中,每个单元都由一个全加器(Full Adder,FA)和逻辑与门构成,通过连线将它们连接在一起。每个单元的内部结构如图2.64所示,其逻辑关系表示为
图2.64 每个单元的内部结构
从上面的介绍可知,对于一个N×N的乘法而言,需要N 2 个如图2.64所示的单元。例如,对于一个8位的乘法,需要8 2 =64个这样的单元,是4位乘法所需单元总数的4倍。所以,乘法器将消耗大量的逻辑设计资源。当然,FPGA内提供了专用的乘法器资源,可以实现乘法的运算,这也是FPGA的一大优势。
1.分布式乘法器实现
分布式乘法器使用FPGA内的LUT等分布式逻辑资源来实现乘法器的功能。对于一个4×4 的分布式乘法器实现而言,其Verilog HDL描述如代码清单2-4所示。
代码清单2-4 top.v文件
使用Xilinx Vivado 2017.2工具对设计进行综合后的结果如图2.65所示。从图2.65中可知,该4位乘法器消耗了FPGA内大量的逻辑设计资源。对该分布式乘法器执行完布局布线后的结果如图2.66所示。
图2.65 4位分布式乘法器综合后的结果
图2.66 4位分布式乘法器布局布线后的结果
注 :读者可以定位到本书提供资料的\fpga_dsp_example\multiplier_mapping_LUT\Pro-ject1\目录下,用Vivado 2017.2打开project_1。
2.块乘法器实现
前面提到,FPGA内提供了专用的数字信号处理切片DSP48x,该切片内有专用的乘法器资源。因此,可以通过使用DSP48x实现两个数的乘法操作。与使用分布式结构实现乘法操作相比,使用DSP48x内的乘法器不会额外使用FPGA内的分布式逻辑资源。对于一个4×4 位的乘法操作而言,使用DSP48x内专用乘法器的Verilog HDL描述它,如代码清单2-5所示。
代码清单2-5 top.v文件
注 :读者可以定位到本书提供资料的\fpga_dsp_example\multiplier_mapping_DSP\Pro-ject1\目录下,用Vivado 2017.2打开project_1。
使用Xilinx Vivado 2017.2工具对该设计进行综合后的结果如图2.67所示。从图2.67中可知,该4位乘法器使用了FPGA内专用的DSP48E1切片,而不使用FPGA内的LUT等分布式资源。对该设计执行布局布线后的结果如图2.68所示。
图2.67 4位块乘法器综合后的结果
图2.68 4位块乘法器布局布线后的结果
对于4位分布式乘法器而言,其关键路径如图2.69所示。如果想让分布式乘法器的时钟频率更高,则可以在分布式乘法器的每个单元之间插入流水线寄存器,如图2.70所示。
图2.69 4位分布式乘法器的关键路径
图2.70 插入流水线寄存器后的4位分布式乘法器的关键路径
从前面的介绍可知,不管是分布式乘法操还是块乘法器,它们的实现成本都很高。因此,很自然地想到,是否有一些方法可以替代乘法操作?答案是肯定的。根据所掌握的知识可知,对于2的幂运算可以通过简单的移位运算就可实现。因此,通过移位-相加的操作可以代替很多直接的乘法操作,如图2.71所示。
从图2.71可知,这些乘法运算简化成了移位或者移位-加法运算。一个移位操作在FPGA内消耗很少的逻辑资源,N位加法操作只要求N个LUT资源,而N位乘法操作则要求N 2 个LUT资源。因此,使用这种方法潜在地减少了实现乘法运算所要消耗的逻辑资源数量。
图2.71 使用移位-相加操作实现乘法运算的功能
进一步扩展到多个权值,如3个或者4个分支。例如,对一个输入x使用0.246459960937500进行加权,并将其量化到12位,则可以通过移位-相加操作实现这个目的,如图2.72所示。
图2.72 使用移位-相加操作实现对输入加权的操作
虽然这种方法可以显著减少乘法运算所消耗的逻辑资源,但是对于一个较长的滤波器设计而言,这会消耗大量的时间来选择合适的移位-相加实现所需的加权功能。因此,一般让Vivado工具对设计进行自动优化,只有在设计资源特别紧张的情况下才会考虑使用这种方法。
当然,如果修改权值能满足设计性能的要求,则可以通过修改权值来简化移位-相加操作的复杂度。
思考与练习2-10 :请说明分布式乘法器和块乘法器的实现原理,以及与FPGA内逻辑资源的对应关系。
思考与练习2-11 :众所周知,在DSP中使用C语言编写代码就可以实现4位数据相乘的操作,比较使用FPGA实现乘法操作与使用软件C语言代码实现乘法的本质区别(提示:FPGA内有几十个甚至多达上千个DSP阵列)。
前面介绍FPGA在实现数字信号处理时所使用的是一种全并行处理结构,这是以消耗FPGA内大量的逻辑设计资源为代价的,因此可以达到很高的处理性能。当不需要很高的处理性能时,可以采用复用和资源共享的方式进行串行处理。因此,这也是FPGA的巨大优势,在本章2.3.3一节中已经进行过详细介绍。
在进行信号处理时,溢出是一个非常棘手的问题。这里简单介绍一下,以引起读者的高度重视。例如,对于一个8位的有符号整数而言,其动态范围为-128~127。当数据的值太大而无法使用可用的数据格式表示时就会出现溢出。这种情况经常发生在模拟-数字转换的结果中,或者在算术运算的过程中。
通常情况下,当超过这个范围时,应该如何处理它?本节将对这个问题进行简单讨论。
1.回卷
处理溢出的一种方法是回卷。由于二进制算术运算的处理机制,回卷是溢出很自然的一种处理方法。例如,当数字扩展超过了范围的顶端,它就回卷到范围的低端,反之亦然,如图2.73所示。对于8位无符号数而言,它可以表示的数值范围为0~255。而数值259超过了可以表示的最大正数值255,因此系统将其自动理解为数值3。其实质是由于表示数字位数宽度的限制而引起的数字错误。这种问题也同样适用于二进制补码表示中。
图2.73 对溢出的处理(回卷)
一旦出现回卷,将出现严重的错误(如图2.74所示),即所表示的信号出现剧烈变化,人为地在原始信息中引入高频分量。
图2.74 出现回卷时,将出现严重的错误
2.饱和
处理溢出的另一种方法是饱和。对于饱和而言,问题不算很严重,如图2.75所示。从图2.75中可知,当采用饱和机制时,对于+132而言,超过正数的最大值+127,将其饱和到+127;对于-131而言,超过负数的最小值-128,将其饱和到-128。当然,结果会造成失真,因为此时有信息丢失(如图2.76所示);但是,问题还不是特别严重。
图2.75 有符号数超出范围进行饱和处理(1)
图2.76 有符号数超出范围进行饱和处理(2)