(多图) 如何快速的将16 位、12阶FIR滤波器的DSP编译到FPGA硬件上
本文开始通过描述多个并行进程的需求,讲述一个比双核提供更多能力的FPGA设计。接着,作者提出只需要简单的步骤,即可完成将C语言的有限脉冲响应(FIR)滤波器移植到FPGA硬件中。
高性能DSP应用,包括视频和音频处理,与其说是时钟的限制,不如说是缺乏并行处理的能力。在减少钟速率并使用多重并行处理硬件主要有两个优势:一,降低时钟速度,降低功耗;二,并行的应用可以大大提高应用程序的吞吐量。但如何将现有的DSP算法转成并行执行则是实现这些好处所面临的最大的挑战。
FPGA则可以很好地并行执行程序。传统上,这些设备已被使用较低级别的方法进行描述,包括硬件描述语言(HDLs),最常用的VHDL和Verilog。最近,C到HDL的编译已经成为许多FPGA开发的核心能力。HDLs是为PnR工具所创造的可在FPGA上运行的RTL代码的输入文件格式。有效编程硬件需要软件开发人员了解如何将设计改写至硬件。软件开发者也需要知道如何使用优化编译器,了解最终的硬件资源消耗,以减少产品上市时间。这是其中C到HDL工具可以提供很大的帮助。本文的目的是为软件开发者提供将C代码快捷的转换至FPGA硬件中使之并行执行的方法。
并行
微处理器通过利用不断增加的时钟速度,以及使用有限的指令流水线(双核或四核)来提高性能。 FPGA却早已提供了这些核远没有达到的并行执行能力。它能够使用优化的并行处理逻辑来执行多个串行程序。缺点是,从以微处理器为导向C至优化后的RTL(布局布线后)需要经过多个步骤,使用多种工具。对于C程序员,这意味着C必须被编译成HDL,HDL必须被综合至较低水平的逻辑,而低级别的逻辑必须在FPGA中进行布局布线。因此,优化应用需要一些了解FPGA架构并且懂得进行结构优化的程序员。为了解释我们要遍历一个简单的FIR滤波器从C移动到FPGA的硬件(图1)的步骤。
图 1: producer, FIR 与consumer 三个进程被连接至一起
项目描述 - 16位,12阶FIR滤波器
具体过程被编译成硬件是由以下函数表示:
void fir(co_stream filter_in, co_stream filter_out)
这个C语言子程序是定义为一个代码模块的单一进程,作为一个空的子程序,用来描述一个硬件或软件的组成。如果您是一位经验丰富的硬件设计师,你可以简单地认为是类似于一个VHDL的实体,或一个Verilog模块的进程。
如果您是一位软件程序员,你可以把该进程想像成一个永远循环的、独立于其他进程的子程序。我们的fir函数没有返回值,有两个被定义使用类型为co_stream的数据接口。这两个流用于:
滤波器系数为12,取filter_in 为采样数据流。
写出对filter_out中滤波器的值。
如果你是一个硬件设计师,你可以将co_stream视为一个先进先出(FIFO)的缓存。如果您是一位软件程序员,你可以把一个co_stream看成是大致类似于C中的文件类型而非磁盘上读和写的文件,但是,我们将使用co_stream类型进行多个并行进程之间的数据传输。
图2:源代码,显示的算法及其嵌套循环
请注意在图2中的子程序包括外部DO,而(1)循环,这表明该子程序将不间断的执行。该子程序描述了一个持续的,不间断运行的进程。
在这个循环中,观察co_stream_open,co_stream_read,co_stream_write和co_stream_close是如何管理滤波器的数据移动的功能的。这些功能为您和C程序员提供了用简洁和平台可移植的方法,来表示数据流。支持许多我们所熟悉的可以用来描述移动和进程到进程的数据管理功能。
Fir函数开始通过从filter_in流中的读取12个系数并存储到一个本地数组(系数)。该函数然后读取并开始处理输入数据,每次一个采样。滤波器的结果写入到输出流filter_out中。
在算法的描述(图3),你会发现一个while循环,它描述了滤波操作,迭代乘法累加的操作。
图3:滤波进程是一个迭代的乘法累加操作。
此包含有两个内循环和一个简单的计算的循环结构对每一段输入的12采样点迭代以进行滤波操作。在每个循环迭代的同时,使用co_stream_write命令将滤波数据写入到输出流。
上述循环说明了使用ImpulseC来描述滤波器的常见模式:C语言对输入数据进行循环迭代、处理,并将结果写到输出流(如图所示)或其他方式。
你可能注意到了在代码中使用的三个编译指示(PIPELINE, UNROLL 和 SET StageDelay)。这些编译指示是更详细的优化技术指南,总结(在代码中使用这些编译指示):
CO PIPELINE pragma表示我们希望while循环被实现成为一个高吞吐量的硬件流水线(pipeline)。如果硬件编译器能够生成一个速度完美的流水线,那么我们可以预计,此循环遍历的时间和每一个采样时间一样快,即使在循环中的计算超过一个时钟周期。
CO SET pragma允许我们指定生成的硬件的某些特征。在这种情况下,我们正在建立一个StageDelay的约束,指示优化器限制任意流水线阶段的组合逻辑深度。如果任意生成的流水线阶段超过这个限制,优化器将添加额外的流水线阶段,以更好地平衡流水线从而使硬件工作在高时钟频率下。
表示优化器将删除(依靠使用unrolling命令)循环,使该循环所有的迭代并行工作。展开Unrolling要求循环服从(例如有一个固定数量的循环迭代)某些规定,但提高性能的同时也会急剧增加FPGA逻辑门搭建。
FIR滤波器的子程序配置
上述FIR的子程序描述了要在FPGA硬件上实现的C算法。然而,为完成整个程序,我们还需要包含一个额外的程序来描述I / O接口和该应用的其他编译时的特性。这种配置常规有三个重要的目的,使我们能够:
定义I /O特性,例如FIFO深度和共享内存的大小等。
将一个或多个ImpulseC进程实例化并相互联接。
任意指定芯片级的物理名称和/或位置分配给特定的I / O端口。
这个例子包括一个硬件进程(FIR滤波器),同时也包括了前面描述的两种测试程序,生产者(producer,信号发送)和消费者(consumer,信号收集)。我们的配置程序(图4)描述该如何把生产者(producer),消费者(consumer)与主体(fir)的进程连接在一起。
图4:一个完整的配置程序
总之,fir子程序所描述生成的算法作为FPGA硬件,而生产者和消费者子程序(描述在其他地方,fir_sw.c)是用于测试。配置例程用于描述如何沟通这三个过程,并描述了进程I / O中的其他特点的。
编译C代码来创建HDL
在创建FPGA硬件和相关的C代码文件的步骤前,首先选择的目标平台。该平台可以是个人的FPGA或是FPGA的开发板。设备选择通常是通过下拉菜单将列出由C到FPGA编译器所支持的设备。或者,您通常可以选择“一般的VHDL”来编译一个非特定的设备架构的VHDL。大多数HDL生成器还可以生成Verilog。HDL的生成是自动的,创建一个项目文件,其中包括循环延迟、流水线速率估算和所需硬件操作的估算,如图5所示。这些数据有助于工程师在FPGA漫长的综合,布局布线等步骤以前,反复重构和完善算法。
图5:项目文件包括操作循环延迟、流水线速率、所需的硬件操作的估算。
这个 C到FPGA的工具将优化和处理您的工作,生成FIR滤波器的HDL文件。作为完善迭代代码进程的一部分,将使用代码工具和图形工具对这些结果进行检查。此HDL文件包括状态机和其他逻辑,它使用C实现了并行和流水线。.在图5中的示例包括的展开循环的流水线内码循环会导致大量的HDL代码的生成。
当您检查这个生成的HDL代码,请记住,HDL代码行数并不直接关系着FPGA资源的大小。在这种情况下,由于循环展开和流水线的存在,编译器会生成大量的中间信号。 这些中间信号会在综合时被FPGA的综合工具优化掉,将会导致实际生成的逻辑将远远少于HDL所显示的代码量。注:这滤波器所占用的FPGA的资源量和性能将取决于选定器件在什么PGA平台上,综合的设置,与其他硬件元素相接的完整系统。在这个算法案例(16位,全流水线并行12阶滤波器),可以相当于拥有约12个DSP slices的FPGA。
图形化工具使得可以查看一个扩展形式(图6)的源代码,并查看所有展开的和流水线的内循环图。
图6:源代码的扩展形式。
从现在开始,你拥有了一个可重用的、被测试过的FIR过滤器,并可与其他团队共享使用,或是在较大规模的系统集成芯片上重复使用。有趣的是,生成的HDL的可用性为您提供了可进一步手工细化各个设计部分的可能。通常团队将使用C来创建整个设计,然后将一部分转换为HDL代码以备硬件化。因此另一种选择是使用流生成HDL,导出至符合IEEE标准的HDL仿真器(如:ActiveHDL,ModelSim)。这样也可将另一部分C代码自动生成测试向量,作为设计过程中的一部分。我们坚定地认为,“HDL是不可替代的”,HDL可以很好的处理胶合逻辑,地址控制,状态机,和其他的布尔运算等工作。而C恰好是另一个工具包,使软件和硬件开发人员更好地协作联合优化的设计项目。