Vivado HLS 讓開發者以 C/C++ 描述硬體行為,自動綜合為 RTL(Verilog/VHDL)並封裝為 IP 核。本文整理初學者常遇問題與解決方案,涵蓋指數函數處理、可綜合構造限制、指針與記憶體模型、遞迴與標準庫限制等核心主題。
exp() 無法導出 Pcore 問題現象:使用 exp(x) 時僅支援導出 Vivado IP,不支援舊版 EDK Pcore 導出。
解決方案:採用查表法(LUT)替代運行時計算。預先在 PC 端(VS/Matlab)計算所需範圍的指數值存入陣列,HLS 中直接索引:
void wxFillExpLut(float *lut, int size) {
for (int i = 0; i < size; i++) {
lut[i] = expf(-(float)i / LUTPRECISION);
}
}
優點:消除複雜浮點運算單元,大幅降低資源佔用與延遲;缺點:需預知輸入範圍,精度受表大小限制。
依據 UG902,可綜合函數必須滿足:
| 類別 | 說明 | 替代方案 |
|---|---|---|
| 系統呼叫 | printf, malloc, time 等 | 用 #ifndef __SYNTHESIS__ 隔離仿真代碼;動態記憶體改為靜態陣列 |
| 動態記憶體分配 | malloc/free/new/delete | 編譯期固定大小陣列 + 指針指向 |
| 遞迴函數 | 硬體無法支援不定深度呼叫堆疊 | 改用迴圈展開或有限狀態機 |
| C++ 動態多型 | 虛函數、動態建立/銷毀物件 | 使用樣板靜態多型或函數指針表 |
| 標準模板庫 (STL) | 內部使用動態記憶體 | 改用定長陣列或 HLS 提供的 hls::stream |
int **pp 需轉換為單層指針 + 索引運算官方建議使用使用者定義宏(如 NO_SYNTH)而非 __SYNTHESIS__:
#ifdef NO_SYNTH
// 仿真專用:malloc, printf, 檔案 I/O
long long *out_accum = malloc(sizeof(long long));
#else
// 可綜合版本:靜態分配
long long _out_accum;
long long *out_accum = &_out_accum;
int _array_local[64];
int *array_local = _array_local;
#endif
流程:1) 定義宏並修改代碼 2) 啟用宏跑 C 仿真存結果 3) 關閉宏再跑仿真驗證一致性 4) 關閉宏進行綜合。
掌握 HLS 的關鍵在於理解「硬體導向的 C 風格」:固定大小、無動態分配、無 OS 依賴、善用管線化與資料流優化指令。查表法、條件編譯隔離不可綜合代碼、靜態記憶體模型是避開常見陷阱的三大利器。