在数字信号处理过程中,很多算法使用c/C++实现这里使用HLs工具进行把c/C++的算法转化为RTL硬件。
Vitis HLS创建工程
- 打开创建工程向导

- 输入工程名称,选择存储路径

- 设置顶层函数名称,添加或创建设计文件,这些步骤可以跳过,待工程创建完成后在设置或添、创建设计文件

- 添加或创建测试激励文件,这些步骤可以跳过,待工程创建完成后在添、创建测试激励文件

- 选择器件

在工程中添加文件
-
打开创建设计文件的向导

-
选择存储路径,输入文件名,点击保存

-
在以同样的方式创建"fir_filter.h"文件
-
打开创建测试激励文件的向导

-
选择存储路径,输入文件名,点击保存

-
编辑"rx_ask_fir.h"文件,内容如下,这是一个FIR滤波器,系数采用matlab的滤波器设计工具生成
c
#ifndef _FIR_FILTER_H_
#define _FIR_FILTER_H_
#include <stdlib.h>
#include <ap_int.h>
//FIR滤波器系数中小数位数
#define COEF_Q 10
//FIR滤波器阶数
#define FIR_BL 64
//FIR滤波器系数,可以使用matlab生成
#define FIR_COEF {1, 4, 2, 3, 3, 3, 3, 2, 1, -1, -3, -5, -8, -10, -12, -13, -13, -12, -10, -6, 0, 7, 15, 25, 36, 46, 57, 66, 75, 82, 87, 89, 89, 87, 82, 75, 66, 57, 46, 36, 25, 15, 7, 0, -6, -10, -12, -13, -13, -12, -10, -8, -5, -3, -1, 1, 2, 3, 3, 3, 3, 2, 4, 1}
//FIR滤波器
void fir_filter(const ap_int<12> *in, ap_int<12> *out);
#endif //_FIR_FILTER_H_
- 编辑"rx_ask_fir.cpp"文件,内容如下:
c
#include "fir_filter.h"
//滤波器系数,对应公式中的h(0)~h(n)
static const ap_int<8> coef[FIR_BL] = FIR_COEF;
//状态,state[0]延迟1拍(对应公式中的x(n)),state[1]延迟2拍对应公式中的x(n-1)),state[2]延迟3拍对应公式中的x(n-2))......
static ap_int<12> state[FIR_BL] = {0};
void fir_filter(const ap_int<12> *in, ap_int<12> *out)
{
#pragma HLS INTERFACE mode=axis register_mode=both port=out register
#pragma HLS INTERFACE mode=axis register_mode=both port=in register
#pragma HLS INTERFACE mode=ap_ctrl_none port=return
#pragma HLS PIPELINE II=1
ap_int<32> sum = 0;
//延时打拍,将数据往高位方向移动一个节拍
for(int loop = FIR_BL-1; loop > 0; loop--)
{
state[loop] = state[loop-1];
}
state[0] = *in;
//卷积
for(int loop = 0; loop < FIR_BL; loop++)
{
sum += state[loop] * coef[loop];
}
*out = sum >> COEF_Q;
}
- 编辑"test_fir_filter.cpp"文件,文件内容如下:
c
#include <math.h>
#include "fir_filter.h"
#define AM 50
int main(void)
{
ap_int<12> signal0;
ap_int<12> signal1;
ap_int<12> signal2;
ap_int<12> output;
FILE *fp;
const int SAMPLES = 1024;
double f1 = 1000000.0;
double f2 = 2500000.0;
double fs = 40000000;
fp=fopen("output.csv","w");
for(int loop = 0; loop < SAMPLES; loop++)
{
//2*pi*f*t; t=loop/fs
//2*pi*f*loop/fs
signal0 = (ap_int<12>)(AM * sin(2 * M_PI * f1 * loop / fs));
signal1 = (ap_int<12>)(AM * sin(2 * M_PI * f2 * loop / fs));
signal2 = signal0 + signal1;
fir_filter(&signal2, &output);
fprintf(stdout,"output[%d] = %d\r\n", loop, (int)output);
fprintf(fp,"%d,%d,%d,%d\r\n", (int)signal0, (int)signal1, (int)signal2, (int)output);
}
fclose(fp);
return 0;
}
添加头文件路径
此工程所有文件均放在工程根目录下,因此无需添加头文件路径,不过这里还是介绍一下如何添加头文件路径,以便后面能管理复杂工程。
- 打开工程属性配置页面

- 添加头文件,注意GUN C和GNU C++均需要设置

debug C/C++
- 打开debug界面

- 通过CTRL+B编译工程或者按如下方法编译工程

- 打开debug设置界面

- 选择默认的Debug配置,然后启动Debug

- 在调试界面可以提供下面图片所示按键控制程序执行

- 退出Debug界面

进行C/C++仿真
- 启动C仿真

- 进行仿真配置,保持默认即可

- 仿真结果输出

进行RTL仿真
- 为顶层函数添加PIPELINE属性


- 添加IP核控制接口属性


- 添加in变量的接口属性


- 以同样的方法将out变量接口设置为AXIS属性
- 打开顶层函数设置页面,如创建工程时指定了,且不需要更换顶层函数可以忽略此步

- 选择顶层函数,如创建工程时指定了,且不需要更换顶层函数可以忽略此步

- 生成RTL文件

- 生成配置保持默认即可

- 打开RTL仿真配置界面

- 选择ModelSim,并设置ModelSim仿真库,然后启动仿真

- 仿真完成后会在console中输出"Finished C/RTL cosimulation.",并在工程的solution1\sim\verilog目录生成一个.wlf的波形文件

- 用ModelSim打开波形文件


导出IP核
- 打开IP导出配置页面

- 配置并导出IP,配置保持默认即可

- "ERROR: IMPL 213-28 Failed to generate IP."错误的处理
错误是Vitis的一个bug,通过安装补丁即可解决,补丁下载路径和安装指定路径如下:
https://adaptivesupport.amd.com/s/article/76960?language=en_US
有时候AMD官网无法正常下载,这里我将其上传至码云了,可以通过码云下载码云下载。
下载好后在vivado2021.2的安装目录解压y2k22_patch-1.2.zip文件,解压完成后如下所示:

然后再安装目录下代开cmd命令行,如下图所示:

在cmd中执行命令"Vivado\2021.2\tps\win64\python-3.8.3\python.exe y2k22_patch\patch.py",执行结果如下图所示

- 补丁安装完成后重启VITIS,然后再重新执行步骤1和步骤2便可导出IP核,导出的IP在下列目录中,接下来可以按照使用自定义IP核的步骤再Vivado使用此IP核。

HLS-C特有数据类型
HLS-C特有数据类型定义在<ap_cint.h>文件中,实现有些任意宽度变量的功能,如int12表示12位有符号整型,uint12 表示12位无符号整型,同时还提供了有些操作变量指定位的函数,如apint_get_bit(Val, Bit)、apint_set_bit(Val, Bit, Repl)、apint_get_range(Val, Hi, Lo)、apint_set_range(Val, Hi, Lo, Repl)等。
注意:从2021版本开始不支持<ap_cint.h>文件,同时赛灵思官方也建议使用C++开发
HLS-C++特有数据类型
HLS-C特有数据类型定义在<ap_int.h>、<ap_fixed.h>、<hls_stream.h>文件中。
- <ap_int.h>
c
<ap_int.h>提供定义任意宽度整型变量的功能,可以用于替换<ap_cint.h>中的功能。
有符号定义格式为"ap_int<位宽> 变量名",如"ap_int<12> a"表示定义12位有符号整型变量a。
无符号定义格式为"ap_uint<位宽> 变量名",如"ap_uint<12> b"表示定义12位无符号整型变量b。
- <ap_fixed.h>
c
<ap_fixed.h>提供了定点数相关功能,有符号定义格式为"ap_fixed<W, I, Q, O, N> 变量名",无符号定义格式为"ap_ufixed<W, I, Q, O, N> 变量名" ,其中特殊字段含义如下。
W: 变量宽度
I:整数位宽(对于有符号数包括符号位)。
Q:量化模式
AP_RND:向正无穷舍入
AP_RND_MIN_INF:向负无穷舍入
AP_RND_INF:是以上两种模式的结合,会把正数向正无穷舍入,负数向负无穷舍入
AP_RND_ZERO:向0进行舍入
AP_RND_CONV:进行舍入取决于需要截取掉的最低位,截取掉的最低位为1则将此位累加到截取位之后的结果上
AP_TRN(默认):截取模式,将截取的位直接删除
AP_TRN_ZERO:正数值和AP_TRN一样,而负数值是AP_RND_ZERO模式
O:溢出模式
AP_SAT:超出位宽决定的最大值或者最小值则用最大值或最小值替代
AP_SAT_ZERO:超出位宽决定的最大值或者最小值则设置为0
AP_SAT_SYM:这个可以理解AP_SAT的对称模式,如8位的有符号数表示最大范围是+127到-128,当输入-128这时候就会被替换为-127,无符号则约束到0~最大值范围
AP_WRAP N=0(默认):超出位宽部分直接截掉(截掉高位),若截掉后最高位为1还则认为是负数,如8bit的385被截断后为-127,8bit的129被截断后为-127
AP_WRAP N>0:超出位宽部分直接截掉,但是不会改变符号位(截掉高位)
如ap_fixed<16, 8>表示定点数总宽度16位,其中整数占8位,小数占7位,符号占1位,量化模式为AP_TRN_ZERO,溢出模式均为AP_WRAP N=0
常用HLS 编译指示
HLS 工具提供各种编译指示,用于最优化设计、降低时延、提升吞吐量性能,以及减少生成的 RTL 代码的面积和器件资源利用率。这些编译指示可直接添加到内核源代码中,下表是HLS支持的编译指示,这里只介绍几个常用的编译指示。

- pragma HLS interface
用于指定输入输出的接口类型,如"#pragma HLS INTERFACE mode=ap_ctrl_none port=return"表示模块的控制接口为"ap_ctrl_none",如"#pragma HLS INTERFACE mode=axis register_mode=both port=in register"表示"in"变量的接口类型为AXIS。 - pragma HLS pipeline
用于指定函数的启动时间间隔,即每隔多久可以处理一个新的输入,如"#pragma HLS PIPELINE II=1"表示每个时钟周期可以处理一个新的输入。