paradoxfx

【原创】Vivado HLS中四种数组端口实现方法的对比

0
阅读(11446)

Vivado HLS中,C代码中的数组作为端口时,它们被默认综合为RAM端口。以下面的程序为例:

voidarray_io(dout_t d_o[N], din_t d_i[N])

{

inti, rem;

// Store accumulated data

staticdacc_t acc[CHANNELS];

// Accumulate each channel

For_Loop:for(i=0;i

rem=i%CHANNELS;

acc[rem] = acc[rem] + d_i[i];

d_o[i] = acc[rem];

}

}

在综合之后,端口被处理为:

1数组端口的默认综合结果

从图1中可以看出,输出数组d_oap_memory协议综合为端口,且Vivado HLS自动添加了输出数据端口d_o_d0、使能信号d_o_ce0、写使能端口d_o_we0、输入数据端口d_i_q0。输入数据没有生成写使能端口,因为它只需要读取输入数据。因为这个例子里for循环默认是串行执行的,读操作和写操作没有同时发生,所以数组端口被实现为单口RAM了。根据需要,我们还可以把数组接口综合为双口RAMFIFO或者把它们展开为多个独立的端口。仍然以图1对应的源代码为例,下面就来看一下它们分别是如何实现的。

1.数组端口实现为双口RAMFIFO

上面的例子里,for循环是串行执行的,所以单口RAM就能满足要求了。如果我们想把数组实现为双口RAM,那么for循环就需要被配置成并行循环的,也就是循环展开。在Vivado HLS中打开源程序,然后通过Directive视图改变for循环的配置为展开,如图1所示。

2展开for循环

Directive视图中,我们还可以配置端口使用的资源,例如把输入d_i配置为双口RAM,如图3所示。

3配置端口的资源为双口RAM

按照类似的方法,把输出端口d_o的接口类型INTERFACE配置为ap_fifo类型。最终修改之后的指示文件的视图如图4所示。

4修改结果

然后运行C代码综合,结果如图5所示。

5双口RAM的综合结果

从图5中可以看出,输入端口d_i已经具有双口RAM的接口,它有两条地址线,两个独立的输入和两个使能端口。

2.数组的分割

在接口综合的时候,Vivado HLS可以根据我们预先定义的因子,对数组进行分割,分割方法如图6所示。

6数组分割

在这里,我们把d_i分割为2部分,把d_o分割为4部分,进行C代码综合;然后把d_id_o都分割为4部分,再运行C代码综合,对比其端口的综合结果,分别如图7左图、右图所示。

7数组分割结果

从图7中可以看出,当d_i分割为2部分,把d_o分割为4部分时,d_i仍然为双口RAM输入,但是当d_id_o分割为相同多的份数时,d_i的双口RAM的配置被Vivado HLS自动优化掉了:它被优化为4个单口RAM,与4个输出端口一一对应。

3.完成展开数组

完全展开数组,意味着把数组里的每个元素都当作单独的端口进行处理,这样可以最大限度地并行运算,提高运行速度,当然占用的器件资源也会相应地增多。展开方法与图6相同,只不过是把展开的类型从block改成complete,如图8所示。

8完全展开数组的选项

用图8d_id_o都配置为完全展开之后,端口的综合结果如图9所示。

9数组完全展开之后的综合结果

因为结果较长,在图9中没有全部列出,可以看出d_od_i都被完全展开了。

最后,我们可以对比一下单口RAM、双口RAM、部分展开和全部展开情况下的性能和资源利用率情况,如图10所示(这里使用的开发板是MicroZed)。

10不同综合方法的性能与资源对比

Baidu
map