众所周知,HLS是Xilinx于几年前推出的一个高级综合工具,可以直接把C/C++代码,转换成可综合的verilog/VHDL代码。听起来很高级,是不是?。但看新鲜的人多,愿意吃螃蟹的人却很少。这里面有很多因素在,比如担心HLS的效率,对C/C++不熟悉(或者说对verilog/vhdl更熟悉),项目时间太紧……,等等原因。但不管怎样,高层次综是未来硬件系统设计的的一个趋势方向。如果作为工程师的我们,还不赶快转变思维跟上这一变革,那很快就out啦~\(≧▽≦)/~。
接下来的这个系列里面,就让我们一点一点的解开HLS的面纱。为了循序渐进的展开,作为一个引子,让我们先从最简单的FIR滤波器例子开始。
FIR滤波器是很常见的一种数字信号处理电路。我们看看用c代码如何描述一个
其头文件fir.h的内容为:
把这段代码添加到HLS(2014.2)新建的工程里面,设定好器件(A7)和目标时钟频率(100Mhz),然后其它都采用默认的设置,不添加任何控制综合过程的directive约束,我们点击一下综合按钮,就可以很轻易得到其对应的verilog代码。是不是很简单:)不考虑代码资源速度等因素,任何工程师都可以轻而易举的从C代码得到其对应的verilog。
我们把结果中最重要的一些信息摘要出来:
1. 先看一下综合出来的代码的样子
接口简介明了,详细展开来说:
A. C代码里面的x输入,直接映射成rtl代码里面的input [31:0] x;
B. C代码里面的y输出,映射成rtl的输出y和y_ap_vld;
C. C代码里面的系数C[N]输入,映射成rtl代码里的ram类型接口:输出c_address0, c_ce0, 和输入c_q0;
D. rtl代码里的ap_clk, 和ap_clk虽然在C代码里面没有对应,但这是每个rtl系统必不可少的;
E.rtl代码里的ap_start, ap_done,ap_idle, ap_ready信号,在C代码里面也没有直接对应,这是综合工具自动添加的函数控制流程,它其实模拟了C代码里面fir函数的进入和推出机制。如果我们不需要这些控制信号,在综合的时候,可以加约束控制HLS不生成这些接口。
2. 其次看下资源消耗
总的资源消耗不多,我们重点看2个地方:
A.DSP48E用了8个,为什么是这样呢?其实从c代码里面可以找到答案,这是原始的c代码里面有2个地方用到了2个乘法:
每个乘法运算消耗4个DSP48E,这是为什么呢?其实也很简单,因为在c代码里面所有的操作都定义为32为整形int,而32x32的整形运算,需要用4个DSP48E来搭建。
B.有59个LUT用作了memory,如果熟知Xilinx FPGA结构的工程师,会认出来,这是Xilinx FPGA的distributedmemory。问题是,为什么会需要用到这样的电路结构呢?也要从c代码里面去寻找:
这段c代码的含义是,把数组shift_reg静态初始化为0。因此,在HLS输出的rtl代码里面,就用一个初始值都为0的ROM模拟了这一行为:
而构建这一ROM的资源,就是使用的LUT。
看一下延迟
A.Latency指的是从第一个x输入,到第一个y输出,所要经过的clock cycle数。在此为67,这是怎么得来的呢?其实很简单,因为我们实现的是一个11阶的fir滤波器,c代码里面有一个循环次数为11的for loop,对应的rtl代码,默认也是采用的串行处理方法实现的,每个loop需要6个clock cycle处理,所以总共需要6x11=66个clock cycle,然后再加上输出的一拍寄存,所以一共需要67个clock cycle才能得到输出结果。
B. Interval指的是从第一个x输入,到允许第二个x输入,中间所要经过的时间间隔。在此为68,即latency+1。
至此,我们粗略的深入到c和对应的rtl代码内部,了解到了HLS工具转换的大致过程。HLS的翻译过程是很直接的,有什么样的C,我们基本上就能得到与其相对应的rtl。
当然,上面用HLS转换得到的rtl代码,肯定不是最优的,在未来的几章里面,我们将会看到如何修改C代码,配合添加相应的directive约束,来得到更优的rtl代码。