天天看点

HLS:接口完整学习与综合测试

目录

    • 一、引言
    • 二、案例
    • 三、实验
    • 四、小结
    • 五、时间线

一、引言

之前HLS工具帮忙生成的m_axi和s_axi接口,可以很简单的挂接到PS端,进行通信。这次学习的是其他接口的lab,这些接口虽然不能直接与PS端进行通信,但与dma等模块进行组合后,也可以完成与PS端的通信。

之所以要学其他接口,是因为m_axi和s_axi接口功能特定,无法自己更改成自己想要的读写方式。

HLS生成电路:接口功能+模块功能,都可以按照自己的想要的生成。

涉及的接口:ap_hs、ap_fifo、ap_ack、ap_vld、ap_ovld、ap_stable、ap_memory、ap_none和ap_bus。

二、案例

以一个FIR(有限冲击响应滤波器)的例子,来实验这些接口。

HLS:接口完整学习与综合测试

1、fir.h文件。

#ifndef __FIR_H__
#define __FIR_H__

typedef int dtype;
// volatile表示不要对变量进行优化。 为了防止同个变量名读出的结果一直一样,避免被优化掉。
void fir(volatile dtype *in,volatile dtype *out,unsigned int length);

#endif
           

2、fir.cpp文件。

#include "fir.h"

void fir(volatile dtype *in,volatile dtype *out,unsigned int length)
{

	dtype para1,para2,para3;    // 3个参数,表示3个抽头的滤波器。
	para1=1;para2=1;para3=1; 

	dtype d0=0,d1=0,d2=0;

	for(int i=0;i<length;i++)
	{
		// 每次进来一个数in,就把该数保存到d1里,而d1又把数保存到d2中,如此往复。
		// 每次进来一个数,先做延迟,一个in的就产生一个out出去。
		d2=d1;d1=d0;d0=*in;
		*out=para1*d0+para2*d1+para3*d2;

	}
}
           

3、main.cpp文件。

#include "fir.h"

int main()
{
	dtype in[10];
	dtype out[10];
	// 赋值
	for(int i=0;i<10;i++)
	{
		in[i]=1;
	}
	// 进去10个数,出来10个数
	fir(in,out,10);

	return 0;
}
           

4、预期目标。

一个周期,in一个数,out一个数。至少周期数是length。

三、实验

1、第一次尝试。

第一次综合的结果出现了问号,这是因为length的长度是不确定的,综合工具也不知道需要多少个latency才能完成一个功能。一种比较笨的方法,是可以直接把length改成定值,后续再改回来就好,但比较麻烦。有一个选项可以告诉HLS循环多少次,在Vivado HLS Directive Editor的LOOP_TRIPCOUNT中,等价于#pragma HLS LOOP_TRIPCOUNT min=100 max=100 avg=100命令。这个命令只对性能评估有作用,解决问号的问题,但对模块的功能,并没有影响。

HLS:接口完整学习与综合测试

添加命令后的测试结果。

HLS:接口完整学习与综合测试

2、第二次尝试。

首先,看接口部分,in_r的接口类型默认是ap_none,只有一个端口,没有啥使能之类的。out_r的类型是ap_vld,表示还有一个out_r_ap_vld信号,当out_r输出一次时,就会拉高一个周期的out_r_ap_vld脉冲信号,连续输出十次,就出现拉高out_r_ap_vld信号十个周期。

HLS:接口完整学习与综合测试

为了做接口部分的修改,先对接口做一个学习。这些接口可以在选项卡中看到。

HLS:接口完整学习与综合测试

1°ap_none:仅仅有数据,没有数据有效信号的这种接口,就是ap_none。测试时,把out_r信号的接口给改成ap_none,发现也确实没有了vld信号。这种ap_none在一个地方有用到,就是组合逻辑的实现时。

HLS:接口完整学习与综合测试

2°ap_vld:产生一个数据有效的标志。因为,在1°提到的,在数据传输中,如果只单独有数据信号,在传输中无法判断哪些信号是有效的,必须要一个数据有效信号,而ap_vld接口就产生了这种数据的有效信号。再举个输入的例子,如果把in的接口设成ap_vld,那么当数据输入时,也会有一个数据的有效信号输入,如果模块需要检测到4个数据输入后,再进行工作,就会不断等待4个vld的标志。如果在输出模块与输入模块之间,都使用ap_vld接口,可以实现点对点的通信,也即相连基本没有阻碍。但也会存在一个问题,就是后面一个模块是否可以接收数据,前一个模块是不知道的,如果一直发出去,后一个模块无法接收,那还是会出错。需要一个ready反馈信号,变成了3°中的ap_hs信号。

3°ap_hs:handshake,即握手信号,后续用的非常多的一个接口类型。握手是为了解决模块间是否能通信而诞生的,之前在AXI等很多地方都学过,但一直无法很有把握说自己懂了,这次用一段篇幅整理下。

3_1°举一个例子,A模块需要通过握手信号,往B模块中传4个数据。握手信号包括vld和ready两个。

HLS:接口完整学习与综合测试

3_2°当A模块数据准备好,放在数据线上后,开始拉高vld信号,但ready信号还是低的,表示模块B没有准备好接收数据。

3_3°当B准备好可以接收数据时,ready信号被拉高,此时满足了vld&ready的结果为1的条件,放在数据线上的数据就被传入B模块了。

3_4°B接收完数据。vld信号为高,表示A模块待输出的下一个数据也准备好了,放在数据线上了。但B模块还无法接收,所以数据在数据线上一直保持,直到ready为1,可以接收。并且后面ready和vld持续为1,表示同时发送和接收多个数据了。

3_5°vld为0时,表示A模块数据还没有准备好,需要等待下,即使此时ready为1,B模块可以接数据了,数据线上没准备好的数据,也不会传输到B模块中。

3_6°这里将输入接口和输出接口设为ap_hs,可以发现输出多了一个ack信号,就是之前讲的ready信号。

HLS:接口完整学习与综合测试

3_7°仿真跑跑波形。得到下面的图。

HLS:接口完整学习与综合测试

4°ap_ack:这种接口相当于ap_hs的特例,即原来的vld信号给去除掉了。数据传输时只有data和ack两根线,当后一级模块需要接收X个数据时,就会发出X个脉冲的ack信号,而前一级模块收到ack为1的情况,就会把数据传输给后一级模块。这种接口适合于前一级模块的数据都是有效的情况。这种接口其实不是很保险,因为前一级模块的数据很难做到都有效,或者说很难ack一为高,数据就准备好。

5°ap_fifo:和ap_hs非常的像,会形成一个fifo的通路,ap_hs中的vld和ready相当于ap_fifo中的wr_en和full(read和empty)。更改后综合的效果如下面这图。

HLS:接口完整学习与综合测试

5°ap_ovld:这种接口和ap_vld基本是一样的,没啥区别,只是将输入的信号设为ap_ovld后,是没有效果的,只针对输出有效。但ap_vld对输入和输出都是有效的。

HLS:接口完整学习与综合测试

6°ap_stable:这个接口和ap_none很像,用于表示某个接口的信号在一次工作期间都是稳定的,用在配置寄存器中非常多,比如fir模块中的length就可以做成ap_stable的形式,每次工作期间都是个常数,但这次工作和下次工作期间可能会发生变化。再举个例子:卷积运算时需要KX和KY来配置kernel的大小,33或55,先配置好,单次运算的过程中,并不会发生改变。

7°ap_memory:之前数组就是这种,假设数据来源于存储器的接口。

7°ap_bus:这种接口是Xilinx自己定义的一个接口,基本上用不着。通常都是用ap_hs来代替其。

HLS:接口完整学习与综合测试

四、小结

ap_hs最重要,很多都是其变形。ap_stable也是个重点。

五、时间线

一些关键时间点的记录:

2021年04月13日:学习各种接口,并综合测试。

后续可能做的工作:暂无,但接口的很多思想可以在其他功能设计中体现。