HLS 开发学习(一)FIR滤波器


learn from HLSBOOK 3kgo

高层次综合

HLS则是在verilog之类的基础上更高层的一种方法。设计师们在HLS下需要更多的考虑大的架构而非某个单独部件或逐周期运行。

设计师在HLS下需要注重的是系统的运行模式,HLS工具会负责产生具体的RTL微结构。

最早大多数HLS工具是基于Verilog 的,用户需要使用Verilogi 语言进行描述,工具也通过Verilog 产生RTL。

现如今很多HLS工具开始使用C/C++作为设计师端的语言。当然,选择HLS工具最重要的还是看它能否综合我们需要的程序,而不是它使用什么语言。

总体来说,HLS可以自动完成以下曾经需要手动完成的工作:

  1. HLS自动分析并利用一个算法中潜在的并发性
  2. HLS自动在需要的路径上插入寄存器,并自动选择最理想的时钟HLS
  3. 自动产生控制数据在一个路径上出入方向的逻辑
  4. HLS自动完成设计的部分与系统中其他部分的接口
  5. HLS自动映射数据到储存单位以平衡资源使用与带宽·
  6. HLS自动将程序中计算的部分对应到逻辑单位,在实现等效计算的前提下自动选取最有效的实施方式

大多数HLS工具需要用户提供功能的规范,交互的描述,一个对接的计算设备,和目标优化方向。而对于Vivado 来说,用户需要·

  • 一个用C/C++/System C 编写的函数
  • 一个测试平台用于验证结果
  • 一个FPGA开发版
  • 期望的时钟周期
  • 一个简单的实施指导

程序的规范为

  • 不使用动态内存分配(不使用malloc (freeO ,new和delete (()·
  • 减少使用指针对指针的操作·
  • 不使用系统调用(例如labort (,exito ,printf ((O),我们可以在其他代码例如测试平台上使用这些指令,但是综合的时候这些指令会被无视(或直接删掉)·
  • 减少使用其他标准库里的内容(支持math.h里常用的内容,但还是有一些不兼容)·
  • 减少使用C++中的函数指针和虚拟函数
  • 不使用递归方程
  • 精准的表达我们的交互接口

当RTL级的设计可用时,大多数HLS工具会进行标准RTL设计流而在赛灵思Xilinx vivado 设计套装里进行的是逻辑综合,将RTL级设计转换成一个FPGA逻辑部件的连线表,这份连线表不仅包含需要的逻辑部件还包含他们的连接方式。Vivado 之后将连线表和目标设备中的可用资源相关联,这个过程被称作布局及布线(PAR)产出的FPGA配置被附在比特流(( bitstream))上,用户可以将比特流上传到FPGA以实现想要的功能。比特流实质上是用二进制表示FPGA上每一个可用资源的配置,包括逻辑部件的使用,连线的方式,和片上的内存。大型FPGA例如赛灵思Ultrascale fpga 拥有超过十亿个可配置比特,较小的FPGA上也至少有几亿个可配置比特。

FIR滤波器

N-阶FIR滤波器差分方程为

y [ i ] = ∑ j = 0 N − 1 h [ j ] x [ i − j ] y[i] = \sum\limits_{j=0}^{N-1} h[j] x[i-j] y[i]=j=0N1h[j]x[ij]

一般需要N个乘法和N-1个加法。

典型案例有滑动平均滤波器

y [ i ] = 1 N ∑ j = 0 N − 1 x [ i − j ] y[i] = \frac{1}{N}\sum\limits_{j=0}^{N-1} x[i-j] y[i]=N1j=0N1x[ij]

这是一个因果系统,可以去除高频噪声,是一个不太理想的低通滤波器。

FIR滤波器的基本实现

#define N 11
#include "ap_int.h"

typedef int coef_t;
typedef int data_t;
typedef int acc_t;

void fir(data_t *y,data_t x){
	coef_t[N] = {
	53,0,-91,0,313,500,313,0,-91,0,53
	};
	static data_t shift_reg[N];
	acc_t acc;
	int i;
	acc = 0;
	Shift_Accum_Loop:
	for(i = N - 1;i >= 0;i--){
		if(i == 0){
			acc += x * C[0];
			shift_reg[0] = X;
		}else {
			shift_reg[i] = shift_reg[i - 1];
			acc += shift_reg[i] * C[i];
	}
}
* y = acc;
}

///Shift_Accum_Loop的标签不是必需的,但是它对调试很有帮助。
///Vivado HLS工具将这些标签添加到代码视图中。

书中给出了一个完全没有优化的FIR滤波器函数,其参数为指针y和数据x,y为指针是为了方便的把结果传递出去。

coef_t[N]定义了滤波器的系数。
随后在循环中去计算求和平均,最后赋值给y,作为滤波结果。

不过和c语言思路不太一样的是,这里并不是说传入一个x就完事了,它是一个流函数,一次只接受一个数据,因此要用shift_reg储存之前接受的数据,也正因此该数组为静态类型。

每次for循环有两个基本运算。首先,它对输入数据执行乘累加操作(当前输入数据x和存储在shift _reg[中之前输入的数据)。
每次循环执行一个常量与一个输入数据的乘法,并将求和结果存储在变acc中。
同时该循环通过shift_array 来实现数值移动,它的操作行为像FIFO,
它存储输入数据x到shift _r中,并通过shift _reg将之前的元素“向前移动。

性能优化概述

时钟

在这里插入图片描述
假设乘法需要3ns,加法需要2ns,不同的时钟周期会对性能产生很大影响。
具体的先不讨论。

if/else

for循环内部的if/else语句效率很低。在代码中每个控制结构,Vivado Hls 会生成硬件逻辑电路来检查条件是否满足,这个检查在每个循环中都执行。
此外这种条件结构限制了if或else分支中语句的执行;这些语句只有在解决了if条件语句之后才能执行。

当x==0时if语句进行检查,这个只发生在最后一次迭代中.因此if分支中的语句可以从循环中“升起”。也就是说,我们可以在循环结束后执行这些语句,然后在循环中删除if/else控制流。

最后我们必须改变执行“第0次”迭代的循环边界。这显示了for循环所需要的更改。最终结果是实现一个更加紧凑的结构,适合进一步的循环优化,例如,展开和流水线。我们稍后讨论这些优化。

Shift_Accum_Loop:
for(i = N-1;i > 0;i--){
	shift_reg[i] = shift_reg[i-1];
	acc += shift_reg[i] * c[i];
}
acc += x * c[0];
shift_reg[0] = x;
//其实就是把最后一个i=0拿到了外面

循环的拆分

for 循环中有两个基本操作,通过shift_reg进行数据的移位以及进行乘累加运算计算输出。
循环分裂是分别在两个循环实现操作。

手动循环拆分的结果如下,TDL为数字信号处理中FIFO操作的术语“延时线”,MAC是“乘累加”的缩写

TDL:
for(i = N - 1;i > 0;i--){
	shift_reg[i] = shift_reg[i - 1];
}
shift_reg[0] = x;
acc = 0;
MAC:
for(i = N-1;i >= 0;i--)
{
	acc += shift_reg[i] * c[i];
}

不过这样的拆分并不能提高什么效率,知识方便对循环独立优化。

循环的展开

以因子2展开循环

TDL:
for(i = N - 1;i > 1;i = i - 2){
shift_reg[i] = shift_reg[i - 1];
shift_reg[i - 1] = shift_reg[i - 2];
}
if(i == 1){
shift_reg[1] = shift_reg[0];
}
shift_reg[0] = x;

以因子4展开循环

acc = 0;
MAC:
for(i = N - 1;i >= 3;i -= 4){
acc += shift_reg[i] * c[i] +
shift_reg[i - 1] * c[i - 1] +
shift_reg[i - 2] * c[i - 2] +
shift_reg[i - 2] * c[i - 2] +
shift_reg[i - 3] * c[i - 3];
}
for(;i >= 0; i--){
acc

通过将指令#pragma HLS unroll factor=4插入到MAC循环体中,可以控制Vivado HLS工具自动以因子4将循环展开。

当没有指定因子参数时for循环将完全展开。这相当于以最大迭代次数的展开。

完整的循环展开实现程序最大程度的并行性,这样的代价是需要很多资源。因此,可以在“较小”的循环上执行完整的循环。

位宽

使用这些不同数据类型首要好处是解决软件中大量数据存储容量问题。对于大型数组,使用8bit而不是16bit可以将内存容量要求减少一半。缺点是可以8bit表示值的范围减少了。位宽较小数据类型的相关操作可能需要更少的时
钟周期,或者可以实现更多指令并行操作。

例如,语句a = b∗c根据运算数据类型它会有不同的延迟和资源使用情况。如果所有变量都是32位宽,那么需要执行的原始布尔操作要比仅为8位宽的变量多。因此,必须使用更多的FPGA资源(或更复杂的资源)来实现。此外,更复杂的逻辑通常需要更多流水线来实现相同频率。一个32位乘法可能需要5个内部寄存器来达到与使用一个内部寄存器的8位乘法相同的运算频率。因此,操作延迟将更大(5个周期而不是1个周期)

在许多情况下为了优化硬件资源,需要处理位宽不是2的幂次的数据。例如,模数转换器通常输出结果为10bits,12bits,或14bits。我们可以将这些值映射到16bits,但这可能会降低处理性能并增加资源消耗。为了更准确表示这些值,Vivado HLS提供了任意精度数据类型,这些数据类型可以表示为有符号或者无符号任意位宽数据。

  • 无符号:ap_uint
  • 有符号:ap_in

翻回去看刚才的fir滤波器代码,

coef_t c[N] ={53, 0,-91, 0,
313, 500, 313, 0,-91, 0,53};

我们可以用较小的数据类型,32位精度太过分了。

可以用 ap_int<10>

其他参数亦是如此

负数FIR滤波器

复数滤波器的输出应该有

( I i n + j Q i n ) ( I f i r + j Q f i r ) = ( I i n I f i r Q i n Q f i r ) + j ( Q i n I f i r + I i n Q f i r ) (I_{in} + j Q_{in})(I_{fir} + j Q_{fir}) = (I_{in}I_{fir}Q_{in}Q_{fir}) + j(Q_{in}I_{fir}+I_{in}Q_{fir}) (Iin+jQin)(Ifir+jQfir)=(IinIfirQinQfir)+j(QinIfir+IinQfir)

可以将复数FIR过滤器运算拆分
为四个实数滤波器。

在这里插入图片描述

其代码为

typedef int data_t;
void firI1(data_t *y,data_t x);
void fitQ1(data_t *y,data_t x);
void firI2(data_t *y,data_t x);
void fitQ2(data_t *y,data_t x);
void complexFIR(data_t Iin, data_t Qin,data_t *Iout,data_t *Qout){
	data_t IinIfir,QinQfir,QinIfir,IinQfit;
	
	firI1(&IinIfir,Iin);
	firQ1(&QinQfir,Qin);
	firI2(&QinIfir,Qin);
	firQ2(&IinQfir,Iin);
	* Iout = IinIfir + QinQfir;
	* Qout = QinIfir - IinQfir;
}

其中四个fir函数其实是相同的。
通常,我们不需要复制函数;而是简单地调用同一个函数四次。然而,这在本例中是不可以的,因为fir函数中shift_reg变量使用了 static 关键字。

内联

每个fir函数都被独立综合,并且在complexFIR函数中或多或少的被当作一个黑盒来处理。如果你希望Vivado HLS工具针对某个特定函数在其父函数中共同与其它代码共同优化,你可以使用inline指令。这个指令将该函数代码添加到父函数中,并消除层次结构。虽然这有提高性能和资源面积优化,但是它也产生了需要工具综合的大量代码。
代码综合可能需要很长时间,甚至会综合失败,或者导致不可优化设计。

float mul(int x,int y){
return x * y;
}
float top_function(float a,float b,float c,){
return mul(a,b) + mul(c,d) + mul(b,c) + mul(a,d);
}
float inlined_top_function(float a,float b,float c,float d){
return a * b + c * d + b * c + a * d;
}

两个函数效果类似。
(不过没有使用inline指令,而是hls会自动inline)

FIR工程

HLS设计

打开 Vivado HLS,新建一个工程,芯片选择xc7z020clg400-1

右键source新建.h头文件
在这里插入图片描述

#include "ap_int.h"


const int N=11;

typedef int	coef_t;
typedef int	data_t;
typedef int	acc_t;

void fir (
  data_t *y,
  data_t x
  );

把示例中需要的东西添加进去
然后再添加一个cpp源文件来写fir函数(其实就是把开头的定义用.h替代了。)

#include "fir.h"

void fir (data_t *y,data_t x)
{
	coef_t c[N] = {53, 0, -91, 0, 313, 500, 313, 0, -91, 0,53};
	// Write your code here
	static data_t shift_reg[N];
	acc_t acc;
	int i;
	acc = 0;

    // 进入循环
	Shift_Accum_Loop:
		for (i = N - 1; i >= 0; i--) {
			if (i == 0) {
				acc += x * c[0];
				shift_reg[0] = x;
			}
			else {
				shift_reg[i] = shift_reg[i - 1];
				acc += shift_reg[i] * c[i];
			}
		}
	*y = acc;
}

接下来可以考虑结束工程了,在之前我们有过经验,打开project >> project settings 然后把fir函数的文件设置为top,然后进行c综合。

可以得到综合报告
在这里插入图片描述
然后导出IP核
在这里插入图片描述

完整工程

打开vivado,新建工程hls_fir,依然是选pynq板
进入settings
打开IP->Repository,将我们HLS工具生成的IP核添加进去
在这里插入图片描述
然后create block design,添加zynq和刚加的ip
以及自动二连

再之后就是漫长的生成bit stream

  • 3
    点赞
  • 25
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
### 回答1: HLS(High-Level Synthesis)和PYNQ(Python Productivity for Zynq)在FIR滤波器设计中都有重要的应用。 首先,HLS是一种高级综合工具,可以将高级语言(如C/C++)代码转换为硬件描述语言(如VHDL或Verilog),从而实现对程序的高层次综合。在FIR滤波器设计中,HLS可以极大地简化设计流程。设计者只需使用C/C++编写FIR滤波器的算法实现,然后使用HLS工具将其转换为硬件描述语言。这样可以大幅缩短开发时间,同时减少设计错误的风险。HLS还可以优化生成的硬件代码,以提高性能和减少资源消耗。 PYNQ是一个基于Python的开发环境,用于Zynq系列FPGA的快速原型开发。在FIR滤波器设计中,PYNQ提供了一个易用的Python编程界面,使设计者可以通过编写Python脚本来实现滤波器算法。PYNQ还提供了丰富的库和工具,以便进行硬件加速。通过利用PYNQ的高级API和硬件加速功能,可以使FIR滤波器在FPGA上运行更快且更高效。 综上所述,HLS和PYNQ在FIR滤波器设计中发挥着重要作用。HLS可以将高级语言代码转换为硬件描述语言,从而简化设计流程并提高设计效率。而PYNQ则提供了具有高级API和硬件加速功能的Python开发环境,使FIR滤波器设计更加灵活和高效。两者结合使用,可以在滤波器设计中获得更好的性能和更快的开发速度。 ### 回答2: HLS(High-Level Synthesis)和PYNQ(Python productivity for Zynq)在fir滤波器设计中有着重要的应用。 HLS是一种高级综合工具,它允许使用高级语言(例如C、C++或SystemC)来描述硬件设计,并自动生成硬件描述语言(HDL)代码,如Verilog或VHDL。HLS可以大大减少编写和调试HDL代码的工作量,提高硬件设计的开发效率。 在fir滤波器设计中,可以使用HLS快速生成滤波器的硬件描述代码。通过将滤波器的算法实现为C或C++代码,然后使用HLS工具进行综合,可以将其转化为效率高且对资源利用率优化的硬件描述代码。使用HLS工具可以方便地对滤波器进行功能验证和性能评估,同时也能够快速优化设计,使其满足滤波器设计的需求。 PYNQ是一个基于Python的开发环境,它为Zynq系列的FPGA提供了更高的生产力和易用性。PYNQ结合了Python的简洁和可读性,以及FPGA强大的并行计算能力,能够使用Python编程语言轻松地进行FPGA开发。在fir滤波器设计中,可以使用PYNQ构建滤波器的控制逻辑和数据流控制。通过调用PYNQ提供的库函数和API,可以使用Python语言轻松地控制Zynq的FPGA进行滤波器数据的输入、处理和输出。 使用PYNQ进行fir滤波器设计,可以实现高度灵活的开发流程。Python的高级编程语言特性使得fir滤波器的调试、测试和性能优化更加易于理解和操作。此外,PYNQ还提供了丰富的示例代码和开发工具,加速滤波器设计的开发过程。 综上所述,HLS和PYNQ在fir滤波器设计中的应用可以显著提高硬件设计的效率和可读性,简化了设计过程,并为开发人员提供了更多的工具和资源进行优化和测试。 ### 回答3: HLS和Pynq在FIR滤波器设计中有着重要的应用。 HLS(High-Level Synthesis)是一种用于高级语言(如C/C++)描述硬件功能并自动生成硬件电路的技术。在FIR滤波器设计中,HLS可以将滤波器算法的高级描述转化为硬件电路。它可以将复杂的计算任务分解为并行任务,并通过合理的调度和资源分配生成高效的硬件。与传统的手动硬件设计相比,HLS可以大大缩短设计周期并提高设计的可重用性。 Pynq是一种基于Python的可编程逻辑(PL)和处理系统(PS)的开发平台。在FIR滤波器设计中,Pynq可以用于快速原型开发和系统集成。通过使用Python和Pynq,可以实现对硬件功能的快速控制和配置,而无需深入了解硬件设计的细节。Pynq还提供了各种可编程逻辑的高级库和示例代码,使得滤波器开发更加方便和高效。 结合HLS和Pynq可以实现高效的FIR滤波器设计。首先,使用HLSFIR滤波器算法转化为硬件电路,通过并行化和资源分配使得硬件电路性能更佳。其次,通过Pynq进行快速原型开发,借助Python轻松控制和配置硬件功能,便于系统集成和功能验证。这种组合使用可以提高滤波器设计的效率和效果,同时降低设计的复杂性和风险。 总之,HLS和Pynq在FIR滤波器设计中的应用使得滤波器的设计更加高效和灵活,提高了开发者的生产效率和设计质量。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

豆沙粽子好吃嘛!

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值