前言
HLS 是Xilinx推出的高层次综合工具,可以用来加速算法的设计。我在之前,用这玩意儿做了些简单的图像算法的处理的,感觉还是比手撸Verilog代码要来的快一点,正好现在在学习数字信号处理,先拿HLS来进行一些简单的算法验证还是可以的。
1 使用HLS生成正余弦波形
1.1 使用matlab产生ram 中存储的波形数据
产生正余弦信号的波形,其中采样频率40MHz,正余弦的频率为1.25MHz。生成将一个周期内的正余弦数据进行保存。
clear all; close all; clc;
Fs = 40*10^6; %采样频率
Fcarrier = 1.25*10^6; %载波速率
Fsymbol = 100*19^3; %符号速率
t = 0 : Fcarrier/Fs : 1 - Fcarrier/Fs; % 一个周期内的的信号
y1 = 255*sin(2*pi*t);
y2 = 255*cos(2*pi*t);
y1 = round(y1);
y2 = round(y2);
fid = fopen('./rom.txt', 'w+'); %打开一个文件并写入
fprintf(fid, '%d ,', y1);
fprintf(fid, '\r\n');
fprintf(fid, '%d ,', y2);
subplot(211);plot(t,y1);title('正弦信号');
subplot(212);plot(t,y2);title('余弦信号');
2 HLS中完成信号的产生
先把套路搞起来,HLS的主要步骤就是 C仿真,C综合,C-RTL的联合仿真。
C仿真就是对C代码进行仿真,比如自己写了一个C的算法函数的实现,可以先给一个C的激励,来测试算法是否正确。C仿真通过后,就可以进行C综合。
C综合就是把C代码通过在HLS添加约束,最终能够对应上FPGA内部的资源。同时能够生成电路上对应的接口。
C-RTL cosimulation就是通过C仿真的代码,和RTL电路进行联合的仿真。这个过程,就和在FPGA开发中进行仿真是类似的。
首先搞个头文件,这里面引入比较重要的数据类型 ap_int.h 这个头文件。在C/C++当中,存储数据都是以字节为单位的,因此,一个数据最少就需要使用8bit来表示。在FPGA中,可以对这个进行更加灵活的表示,位宽可以是任意的,这个 “ap_int.h” 就是这么一个可以自定义位宽的头文件。通过模板的形式,传递模板参数,生成不同的数据类型。
2.1 头文件
#ifndef __ASK_TX_H_
#define __ASK_TX_H_
#include "ap_int.h"
void ask_mod(ap_uint<1> symbol, ap_int<16> *sin, ap_int<16> *cos);
#endif
2.2 实现文件
这个实现的文件,就是实现整个算法模块的主要部分。通过HLS的directive,添加对应的约束,可以调节接口的类型,时序的优化,内部资源的占用等等。。。
#include "tx_ask.h"
void ask_mod(ap_uint<1> symbol, ap_int<16> *sin, ap_int<16> *cos)
{
#pragma HLS PIPELINE II=1
#pragma HLS INTERFACE ap_ctrl_none port=return
#pragma HLS INTERFACE axis register both port=cos
#pragma HLS INTERFACE axis register both port=sin
#pragma HLS INTERFACE axis register both port=symbol
// counter for lookup table
static ap_int<8> counter = 0;
// lut store the wave
ap_int<16> sin_table[32] = {0 ,50 ,98 ,142 ,180 ,212 ,236 ,250 ,
255 ,250 ,236 ,212 ,180 ,142 ,98 ,50 ,
0 ,-50 ,-98 ,-142 ,-180 ,-212 ,-236 ,-250 ,
-255 ,-250 ,-236 ,-212 ,-180 ,-142 ,-98 ,-50 };
#pragma HLS RESOURCE variable=sin_table core=ROM_1P_BRAM latency=1
ap_int<16> cos_table[32] = {255 ,250 ,236 ,212 ,180 ,142 ,98 ,50 ,
0 ,-50 ,-98 ,-142 ,-180 ,-212 ,-236 ,-250 ,
-255 ,-250 ,-236 ,-212 ,-180 ,-142 ,-98 ,-50 ,
0 ,50 ,98 ,142 ,180 ,212 ,236 ,250 };
#pragma HLS RESOURCE variable=cos_table core=ROM_1P_BRAM latency=1
if(symbol == 1)
{
*sin = sin_table[counter.range(4, 0)];
*cos = cos_table[counter.range(4, 0)];
}
else
{
*sin = 0;
*cos = 0;
}
// counter self increase
counter += 1;
// clear the counter
if(counter == 32)
{
counter = 0;
}
}
2.3 仿个真
仿真比较简单啊,就是产生一些符号,然后调用前面的函数,实现不同波形的输出。
#include "tx_ask.h"
int main()
{
ap_uint<1> symbol[64] = {0, 1, 1, 0, 0, 0, 1, 1,
0, 1, 1, 0, 0, 0, 1, 1,
0, 1, 1, 0, 0, 0, 1, 1,
0, 1, 1, 0, 0, 0, 1, 1,
0, 1, 1, 0, 0, 0, 1, 1,
0, 1, 1, 0, 0, 0, 1, 1,
0, 1, 1, 0, 0, 0, 1, 1,
0, 1, 1, 0, 0, 0, 1, 1,};
ap_int<16> sin, cos;
for(int i = 0; i < 64; i ++)
{
for(int j = 0; j < 400; j ++)
{
ask_mod(symbol[i], &sin, &cos);
}
}
return 0;
}
3 结果
最后就可以把仿真的结果拿出来看一看。
可以看到,当符号为1的时候,就产生了正余弦波形,为0的时候,就没有正余弦产生。其实这个就有点ASK调制那味儿了。
可以看到,这里就产生了这么两个正余弦信号。去观察这个正余弦信号的话,可以发现他们的频率是1.25M