XIlinx利用HLS进行加速设计进度

描述

据观察,HLS的发展呈现愈演愈烈的趋势,随着Xilinx Vivado HLS的推出,intel也快马加鞭的推出了其HLS工具。HLS可以在一定程度上降低FPGA的入门门槛(不用编写RTL代码),也可以在某些场合加速设计与验证(例如在FPGA上实现OpenCV函数),但个人还是喜欢直接从RTL入手,这样可以更好的把握硬件结构。Xilinx官方文档表示利用HLS进行设计可以大大加速设计进度:

RTL

XIlinx官方文档片段

所以为了紧随时代潮流,所以也抽空玩了一下Xilinx的HLS工具,下面把整个过程分享给大家。我这里选择Cordic算法作为我的实现目标。Cordic算法原理很简单,所以这里不再赘述。首先介绍一下Vivado HLS设计流程:

RTL

Vivado HLS设计流程

可以看出我们需要做的是完成C/C++设计、Testbench编写以及Constraints/directives的添加。其中Constraints/directives是指利用约束/指令使HLS综合出的RTL代码更符合要求。接着,我们就可以利用HLS进行C层仿真与验证、C/RTL混合仿真与验证以及RTL代码的生成与打包。综上,HLS设计的主要工作内容包括三点:C/C++设计、Testbench设计以及约束的添加。下面就从这三点开始介绍。

一. Cordic算法的C++实现

算法头文件Cordic.h代码如下:

#include #include #define WA 17#define FA 14#define WS 16#define FS 14typedef ap_fixed di_t;typedef ap_fixed do_t;typedef ap_uint<2> flag_t;const do_t Kn = 0.607252935;const di_t PI = 3.1415926;void cir_cordic(di_t alpha, do_t &mysin, do_t &mycos);

头文件的重点是声明数据类型。这里采用HLS中特有的定点数形式,包含ap_fixed.h与ap_int.h即可。由于输入为有符号弧度制(-3.1415~+3.1415),输出为-1~+1,所以定义两种数据精度:

di_t :17bits = 1bit符号位 + 2bit整数 + 14bit小数

do_t:16bits = 1bit符号位 + 1bit整数 + 14bit小数

接着声明了函数与两个算法所需参数。

算法文件Cordic.cpp代码如下(注意:由于使用C++头文件ap_fixed.h,所以必须采用.cpp文件,否则编译出错):

#include"Cordic.h"void pre_cir_cordic(di_t full_alpha, di_t &alpha, flag_t &flag){ if(full_alpha > PI/2) { alpha = PI - full_alpha; flag = 2; } else if(full_alpha < -PI/2) { alpha = -PI - full_alpha; flag = 3; } else { alpha = full_alpha; flag = 0; }}void cir_cordic_calculate(di_t alpha, flag_t flag, do_t &mysin, do_t &mycos, flag_t &flag_delay){ const int N = 15; do_t xi[N]; do_t yi[N]; di_t zi[N]; flag_t flag_delay_a[N]; xi[0] = Kn; yi[0] = 0; zi[0] = alpha; flag_delay_a[0] = flag; const di_t myarctan[15] = { 0.7853981, 0.4636476, 0.2449787, 0.1243549, 0.0624188, 0.0312398, 0.0156237, 0.0078123, 0.0039062, 0.0019531, 0.0009765, 0.0004883, 0.0002441, 0.0001221, 0.0000610 }; int m = 0; for(m = 0; m= 0) { xi[m+1] = xi[m] - (yi[m] >> m); yi[m+1] = yi[m] + (xi[m] >> m); zi[m+1] = zi[m] - myarctan[m]; } else { xi[m+1] = xi[m] + (yi[m] >> m); yi[m+1] = yi[m] - (xi[m] >> m); zi[m+1] = zi[m] + myarctan[m]; } flag_delay_a[m+1] = flag_delay_a[m]; } mysin = yi[N-1]; mycos = xi[N-1]; flag_delay = flag_delay_a[N-1];}void post_cir_cordic(do_t mysin, do_t mycos, flag_t flag_delay, do_t &sin_out, do_t &cos_out){ switch(int(flag_delay)) { case 2: sin_out = mysin; cos_out = -mycos; break; case 3: sin_out = mysin; cos_out = -mycos; break; default: sin_out = mysin; cos_out = mycos; break; }}void cir_cordic(di_t full_alpha, do_t &sin_out, do_t &cos_out){ di_t alpha; flag_t flag; do_t mysin; do_t mycos; flag_t flag_delay; pre_cir_cordic(full_alpha, alpha, flag); cir_cordic_calculate(alpha, flag, mysin, mycos, flag_delay); post_cir_cordic(mysin, mycos, flag_delay, sin_out, cos_out);}

算法主要有三个函数组成:

1.pre_cir_cordic:将输入角度从-π~+π映射到 -π/2~+π/2中。

2.cir_cordic_calculate:利用旋转公式进行Cordic算法计算,这里设置旋转次数为15次,精度较高。

3.post_cir_cordic:根据输入角度矫正输出值正负。

最后,通过cir_cordic函数实现上述三个函数的整合。至此,Cordic算法的C++设计结束。

二. Testbench设计

为了验证设计的正确性,需要编写Testbench对C++代码以及综合后的RTL进行测试。本文的Testbench.cpp代码如下:

#include "Cordic.h"#include #include #include #include #include #include using namespace std;#define RAND (rand()%181) - (rand()%181)#define Test_round 100#define STANDARD 0.01int main(){ srand(RAND_MAX); int i; for(i=0; iSTANDARD || abs((float)(cos_ref - cos_out))>STANDARD) { cout<<"Error: error is too big"<

本测试平台利用随机数生成-π~+π的测试向量对程序进行测试。以math.h中的三角函数作为评判标准。为了缩短时间,选择100组测试向量进行测试,若算法误差大于给定值,则报错;若算法误差均小于给定值,则输出验证通过信息。C验证平台设计完成。

三. 验证与directives的添加

1.初步算法的C仿真与综合

根据上述代码,可以对工程进行C仿真,仿真结果如下:

RTL

C仿真结果

可以看出C仿真通过,算法正确。接着综合工程,得到综合结果如下:

RTL

C综合报告

可以看出代码时钟符合要求,但是Latency(延迟)和Interval(吞吐量倒数)较大。此时吞吐量较小,64个时钟输出一个计算结果,并没有发挥FPGA的并行优势,所以需要添加Directives对工程综合进行约束。

2.Directives添加

由于Cordic算法中旋转公式部分为循环,所以将循环展开并加入流水线可以大大减小延时以及增加吞吐量。同时也对计算函数加入流水线以提高吞吐量。建立一个新的solution:Add_Directives,其Directive添加结果如下:

RTL

Directive添加结果

此时再对算法进行综合,得到综合报告对比如下:

RTL

综合报告对比

可以看出添加Directives后,吞吐量大大提高,已经达到最大值,即每个时钟都输出一个计算结果。算法延时也从63个clk减小到4个clk,此时RTL代码已经较为理想。

3.C/RTL联合仿真

由上,代码设计部分与约束添加已经全部完成,下面进行联合仿真,对RTL代码进行验证。验证报告如下:

RTL

混合仿真报告

可以看出RTL仿真与C仿真均通过,说明设计正确。利用Vivado simulator打开RTL仿真波形,如下:

RTL

RTL仿真波形

可以看出RTL波形中明显体现出4 clk的Latency和1 clk的Interval,并且利用计算器进行验算,证明计算结果正确,所以RTL代码综合成功。

四. IP打包

直接利用HLS进行IP打包即可生成IP核。在相应工程中引入IP核路径(在对应solution内的impl文件夹内)即可调用HLS生成的IP核。本IP核接口如下:

RTL

Cordic IP

那么根据上节仿真波形进行接口输入的描述就可以使用该IP。至此,整个HLS设计过程结束。

五. 总结

整个HLS设计过程还是比较清晰的,重点在于了解HLS的支持范围以编写符合规范的高层次代码,其次是对硬件有一定认识以引入合适的directives。HLS的确在很大程度上加快了设计进度,使用也非常方便,所以我以后决定还是从RTL层面进行设计,因为那样觉得自己更NB一点。

打开APP阅读更多精彩内容
声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉

全部0条评论

快来发表一下你的评论吧 !

×
20
完善资料,
赚取积分