《電子技術應用》
您所在的位置:首頁 > 可編程邏輯 > 解決方案 > Xilinx Vivado HLS中Floating-Point(浮點)設計編碼風格與技巧

Xilinx Vivado HLS中Floating-Point(浮點)設計編碼風格與技巧

2013-09-02
作者:王宏強 – Xilinx DSP Specialist

盡管通常Fixed-Point(定點)比Floating-Point(浮點)算法的FPGA實現要更快,,且面積更高效,,但往往有時也需要Floating-Point來實現,。這是因為Fixed-Point有限的數據動態(tài)范圍,,需要深入的分析來決定整個設計中間數據位寬變化的pattern,,為了達到優(yōu)化的QoR,,并且要引入很多不同類型的Fixed-Point中間變量,。而Floating-Point具有更大的數據動態(tài)范圍,,從而在很多算法中只需要一種數據類型的優(yōu)勢,。

Xilinx Vivado HLS工具支持C/C++ IEEE-54標準單精度及雙精度浮點數據類型,,可以比較容易,快速地將C/C++ Floating-Point算法轉成RTL代碼,。與此同時,,為了達到用戶期望的FPGA資源與性能, 當使用Vivado HLS directives時需要注意C/C++編碼風格與技巧相結合。

 

  1. 編碼風格

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      浮點運算優(yōu)化

我們先來看一個例子,,三個從代數上看起來差不多的寫法,但其在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;常數乘法被優(yōu)化掉

*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的表達式,。

 

  1. 并行度與資源復用

由于浮點運算相比整型,,定點運算耗用更可觀的資源。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循環(huán)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;

}

 

為了對上面例子并行展開,,可以對代碼如下做較小的改動,,也就是拆成先部分累加,再最后累加,,當然也需要對輸入數據進行簡單的重新排列,以獲得相應的I/O帶寬,,從而達到期望的并行度,。

 

float top(float x[32])

{

#pragma HLS interface ap_fifo port=x

float acc_part[4] = {0.0f, 0.0f, 0.0f, 0.0f};

for (int i = 0; i < 32; i += 4) { // 手動unroll by 4

for (int j = 0; j < 4; j++) { // 部分累加

#pragma HLS pipeline

acc_part[j] += x[i + j];

}

for (int i = 1; i < 4; i++) { //最后累加

#pragma HLS unroll

acc_part[0] += acc_part[i];

}

return acc_part[0];

}

 

本站內容除特別聲明的原創(chuàng)文章之外,轉載內容只為傳遞更多信息,,并不代表本網站贊同其觀點,。轉載的所有的文章、圖片,、音/視頻文件等資料的版權歸版權所有權人所有,。本站采用的非本站原創(chuàng)文章及圖片等內容無法一一聯系確認版權者。如涉及作品內容,、版權和其它問題,,請及時通過電子郵件或電話通知我們,,以便迅速采取適當措施,避免給雙方造成不必要的經濟損失,。聯系電話:010-82306118,;郵箱:[email protected]