编码风格与技巧
虽然一般Fixed-Point(定点)比FloaTIng-Point(浮点)算法的FPGA完结要更快,且面积更高效,但往往有时也需求FloaTIng-Point来完结。这是由于Fixed-Point有限的数据动态规模,需求深化的剖析来决议整个规划中心数据位宽改变的pattern,为了到达优化的QoR,而且要引进许多不同类型的Fixed-Point中心变量。而FloaTIng-Point具有更大的数据动态规模,从而在许多算法中只需求一种数据类型的优势。
Xilinx Vivado HLS东西支撑C/C++ IEEE-54规范单精度及双精度浮点数据类型,能够比较简略,快速地将C/C++ FloaTIng-Point算法转成RTL代码。与此一起,为了到达用户希望的FPGA资源与功能, 当运用Vivado HLS directives时需求留意C/C++编码风格与技巧相结合。
编码风格
1.1 单双精度浮点数学函数
#include
float example(float var)
{
return log(var); // 双精度自然对数
}
在C规划中, 这个比如, Vviado HLS 生成的RTL完结将输入转化成双精度浮点,并根据双精度浮点核算自然对数,然后将双精度浮点输出转化成单精度浮点。
#include
float example(float var)
{
return logf(var); // 单精度自然对数
}
在C规划中, logf才是单精度自然对数, 这个比如 Vviado HLS 生成的RTL完结将根据单精度浮点核算自然对数, 而且没有输入输出单双精度的互转。
1.2 浮点运算优化
咱们先来看一个比如,三个从代数上看起来差不多的写法,但其在Vivado HLS中归纳出来的是三个彻底不一样的成果。
void example(float *m0, float *m1, float *m2, float var)
{
*m0 = 0.2 * var; // 双精度浮点乘法,单双精度类型转化
*m1 = 0.2f * var; // 单精度浮点乘法
*m2 = var / 20.0f; // 单精度浮点除法
}
Vivado HLS将日m0, m1, m2归纳成不同的RTL完结。
由于0.2是一个不能准确表征的双精度数字, 所以m0运算会被Vivado HLS归纳成一个双精度浮点乘法, 而且将var 转化成双精度, 然后将双精度乘法输出m0转化成单精度。
特别留意,假如希望Vivado HLS归纳出单精度常熟,需求在常数后边加f, 如0.2f。这样m1归纳成一个单精度乘法的输出。同理,m2将被Vivado HLS归纳成单精度除法的输出。
咱们来看别的一个比如。
void example(float *m0, float *m1, float var)
{
*m0 = 0.2f * 5.0f * var; // *m0 = var;常数乘法被优化掉
*m1 = 0.2f * var * 5.0f; // 两个双精度浮点乘法
}
再来看另一个比如。
void example(float *m0, float *m1, float var)
{
*m0 = 0.5 * var; //
*m1 = var/2; //
}
m0运算会被Vivado HLS归纳成一个双精度浮点乘法, 而且将var 转化成双精度, 然后将双精度乘法输出m0转化成单精度。
m1运算会被Vivado HLS归纳成简略的右移运算。所以假如用户希望完结对var除以2, 就写成m1这种表达式,而不是m0的表达式。
并行度与资源复用
由于浮点运算比较整型,定点运算耗用更可观的资源。Vivado HLS会尽量用更有用的资源来完结浮点运算,当数据的相关性及束缚答应的情况下,在Vivado HLS中,会尽量复用一些浮点运算单元。为了阐明这个,咱们看一个简略的四个浮点加法比如, Vivado HLS复用一个浮点加法器来串行完结四个浮点加法。
void example(float *r, float a, float b,
float c, float d)
{
*r = a + b + c + d;
}
有时规划需求更高的throughput及更低的latency。这时就需求进步规划的并行度。以下面比如来阐明,在Vivado HLS就需求对for循环loop加pipeline与unroll 的directives。一起需求经过设置a,b,r0 为FIFO, 并对其重排以进步I/O带宽两倍。这样Vivado HLS就会归纳出两个浮点加法来并行完结,这是由于每个加法器核算是彻底独立的。
void example(float r0[32], float a[32], float b[32])
{
#pragma HLS interface ap_fifo port=a,b,r0
#pragma HLS array_reshape cyclic factor=2 variable=a,b,r0
for (int i = 0; i 《 32; i++)
{
#pragma HLS pipeline
#pragma HLS unroll factor=2
r0[i] = a[i] + b[i];
}
}
但是,假如更多杂乱的运算,或许会导致不独立的浮点运算,在这种情况下,Vivado HLS不能重新排列这些运算的次序,这样会导致更低的,不是所希望的复用。 下面举例来阐明怎么进步带有反应浮点运算的功能。
这个比如的累加会导致recurrence,而且一般浮点加法的latency大于一个时钟周期,加的pipeline directive并不能到达一个时钟周期完结一次累加的throughput。
float example(float x[32])
{
#pragma HLS interface ap_fifo port=x
float acc = 0;
for (int i = 0; i 《 32; i++)
{
#pragma HLS pipeline
acc += x[i];
}
return acc;
}