高層次綜合
什么是高層次綜合?就是使用高級語言(如C/C++)來編寫FPGA算法程序。
在高層次綜合上并不需要制定微架構決策,如創建狀態機、數據路徑、寄存器流水線等。這些細節可以留給 HLS 工具,通過提供輸入約束(如時鐘速度、性能編譯指示、目標器件等)即可生成經過最優化的 RTL。
其主要優勢為:
1、提高FPGA算法部署的效率
(1)使用C語言來開發和確認FPGA算法;
(2)使用C語言來仿真RTL設計。
2、算法易于移植。
Vivado 和 HLS
Vitis HLS 工具會將 C 或 C++ 函數綜合到 RTL 代碼中,以便在 Versal 自適應 SoC、Zynq MPSoC 或 AMD FPGA 器件的可編程邏輯 (PL) 區域內實現。Vitis HLS 與 Vivado Design Suite 緊密集成用于綜合與布局布線,并與 Vitis 核開發套件緊密集成用于異構系統級別設計和應用加速。
HLS能夠降順序執行的C語言程序轉為并行執行。
如下面的例子:
#include <vector>
#include <iostream>
#include <ap_int.h>
#include "hls_vector.h"
#define totalNumWords 512
unsigned char data_t;
int main(int, char**) {
// initialize input vector arrays on CPUfor (int i = 0; i < totalNumWords; i++) {in[i] = i;
}
compute(data_t in[totalNumWords], data_t Out[totalNumWords]);check_results();
}
void compute (data_t in[totalNumWords ], data_t Out[totalNumWords ]) {data_t tmp1[totalNumWords], tmp2[totalNumWords];A: for (int i = 0; i < totalNumWords ; ++i) {tmp1[i] = in[i] * 3;tmp2[i] = in[i] * 3;}B: for (int i = 0; i < totalNumWords ; ++i) {tmp1[i] = tmp1[i] + 25;}C: for (int i = 0; i < totalNumWords ; ++i) {tmp2[i] = tmp2[i] * 2;}D: for (int i = 0; i < totalNumWords ; ++i) {out[i] = tmp1[i] + tmp2[i] * 2;}
}
上面的C語言代碼在CPU中按順序執行,當然也可以在FPGA中順序執行,但是這樣就沒有發揮FPGA的優勢。compute() 函數需重構,以實現基于 FPGA 的加速。
加速有以下方向:
1、compute 函數可先啟動,隨后再將所有數據傳遞給它;
2、多個 compute 函數能以重疊方式運行,例如,“for”循環能夠在上一次迭代完成前啟動下一次迭代;
3、“for”循環內的各項操作都能在多個碼字上并發運行,無需逐字執行。
compute() 函數循環 A 將輸入值乘以 3,并創建兩條獨立路徑,分別是 B 和 C。循環 B 和 C 執行操作并將數據饋送給 D。這是一種現實狀況的簡單表示法,您需在其中逐一執行多項任務,這些任務彼此相連形成如下所示網絡。
#include "diamond.h"
#define NUM_WORDS 16
extern "C" {
void diamond(vecOf16Words* vecIn, vecOf16Words* vecOut, int size)
{hls::stream<vecOf16Words> c0, c1, c2, c3, c4, c5;assert(size % 16 == 0);#pragma HLS dataflowload(vecIn, c0, size);compute_A(c0, c1, c2, size);compute_B(c1, c3, size);compute_C(c2, c4, size);compute_D(c3, c4,c5, size);store(c5, vecOut, size);
}}
void load(vecOf16Words *in, hls::stream<vecOf16Words >& out, int size)
{Loop0:for (int i = 0; i < size; i++){#pragma HLS PERFORMANCE target_ti=32#pragma HLS LOOP_TRIPCOUNT max=32out.write(in[i]);
}
}
void compute_A(hls::stream<vecOf16Words >& in, hls::stream<vecOf16Words >&
out1, hls::stream<vecOf16Words >& out2, int size)
{Loop0:for (int i = 0; i < size; i++){#pragma HLS PERFORMANCE target_ti=32#pragma HLS LOOP_TRIPCOUNT max=32vecOf16Words t = in.read();out1.write(t * 3);out2.write(t * 3);}
}
void compute_B(hls::stream<vecOf16Words >& in, hls::stream<vecOf16Words >&
out, int size)
{Loop0:for (int i = 0; i < size; i++){#pragma HLS PERFORMANCE target_ti=32#pragma HLS LOOP_TRIPCOUNT max=32out.write(in.read() + 25);}
}
void compute_C(hls::stream<vecOf16Words >& in, hls::stream<vecOf16Words >&
out, int size)
{Loop0:for (data_t i = 0; i < size; i++){#pragma HLS PERFORMANCE target_ti=32#pragma HLS LOOP_TRIPCOUNT max=32out.write(in.read() * 2);}
}
void compute_D(hls::stream<vecOf16Words >& in1, hls::stream<vecOf16Words >&
in2, hls::stream<vecOf16Words >& out, int size)
{Loop0:for (data_t i = 0; i < size; i++){#pragma HLS PERFORMANCE target_ti=32#pragma HLS LOOP_TRIPCOUNT max=32out.write(in1.read() + in2.read());}
}
void store(hls::stream<vecOf16Words >& in, vecOf16Words *out, int size)
{Loop0:for (int i = 0; i < size; i++){#pragma HLS PERFORMANCE target_ti=32#pragma HLS LOOP_TRIPCOUNT max=32out[i] = in.read();}
}