[LNIV Xilinx](index.html) | Vitis HLS | [model](hlsModel.htm) | [simulacija](hlsSim.htm) | [sinteza](hlsSint.htm) Visokonivojska sinteza (High-Level Synthesis) je proces načrtovanja vezij, ki pretvori algoritem v jeziku C/C++ v model digitalnega vezja. Predstavili bomo osnovne korake dela z orodjem **Vitis HLS** različice 2022 ali 2023. Dodatne informacije so na voljo na: - straneh podjetja [AMD Xilinx](https://www.xilinx.com/products/design-tools/vivado/high-level-design.html) - Vivado Design Suite User Guide, High-Level Synthesis: [UG902](https://www.amd.com/content/dam/xilinx/support/documents/sw_manuals/xilinx2020_2/ug902-vivado-high-level-synthesis.pdf) - laboratorijskih vajah: [Vitis HLS Design Flow Lab](https://xilinx.github.io/xup_high_level_synthesis_design_flow/Lab1.html) - in knjigi: Parallel Programming for FPGAs, [The HLS Book](https://github.com/KastnerRG/pp4fpgas/raw/gh-pages/main.pdf) # Vitis HLS 2022/2023 ### Model vezja v jeziku C Postopek sinteze bomo predstavili na modelu preproste aritmetično-logične enote. Funkcija ale() izračuna rezultat ene od treh operacij nad celoštevilskimi vhodnimi podatki: ```c #include "ale.h" DATA_T ale(DATA_T x, DATA_T y, OP_T op) { DATA_T z; switch (op) { case 0: z = x + y; break; // seštevanje case 1: z = x & y; break; // logični IN case 2: z = x * y; break; // množenje default: z = 0; } return z; } ``` Vhodni parametri in izhod predstavljajo priključke vezja. Podatkovne tipe DATA_T in OP_T definiramo ločeno od opisa algoritma. Običajen podatkovni tip **int**, bo v vezju predstavljal 32-bitne vektorje. Velikost vektorjev in vezja za izvedbo operacij prilagodimo z uporabo celoštevilskih tipov poljubne ločljivosti, ki so na voljo v knjižnici *ap_int.h*. V zaglavni datoteki določimo 20-bitni tip **ap_int<20>** za vhode in izhode in 2-bitni nepredznačen za izbiro operacije: ```C #ifndef _ALE_H_ #define _ALE_H_ #include <ap_int.h> typedef ap_int<20> DATA_T; typedef ap_uint<2> OP_T; DATA_T ale(DATA_T x, DATA_T y, OP_T op); #endif ``` ### Izdelava projekta Ob izdelavi novega projekta v Vitis HLS vključimo datoteko s funkcijo (ale.cpp), zaglavno datoteko (ale.h) in v okence *Top Function* vnesemo ime funkcije. V naslednjem koraku lahko dodamo glavni program (ale_test.cpp) s funkcijo main() za simulacijo. V zadnjem koraku izdelave novega projekta določimo periodo ure in ciljno vezje FPGA. ![](./hls/newHLS.png) Delovni koraki so v orodju Vitis HLS dosegljivi v glavnem meniju, navigacijskem oknu (Flow Navigator) ali na ikonah v orodni vrstici. Koraki vključujejo: - simulacijo v jeziku C, kjer prevedemo in izvedemo simulacijski program skupaj s funkcijo oz. modelom vezja, - sintezo vezja iz funkcije v jeziku C, - povezano simulacijo, kjer izvedemo simulacijski program skupa s simulacijo sintetizirane funkcije in - implementacijo s tehnološkimi koraki za vezja FPGA. ![](./hls/HLSmenu.png) ### Sinteza Sintezo vezja izvedemo s klikom na **Run C Synthesis**. Določiti moramo periodo ure v nanosekundah in ciljno tehnologijo, kjer izberemo FPGA ali razvojno ploščo (gumb Boards). Cilj sinteze je avtomatska izdelava sinhronega vezja, ki izvaja v funkciji opisane operacije in deluje v izbrani tehnologiji s frekvenco nastavljene ure. ![](./hls/Csynth.png) Sintetizator naredi vezje z gradniki ciljne tehnologije in razdeli zahtevnejše operacije na več ciklov ure. Vezje sestavljajo namenske enote (npr. množilniki v blokih DSP), kombinacijska logika (LUT), pomnilniki (BRAM) in flip-flopi (FF). Rezultat sinteze je model vezja z ocenjeno porabo tehnoloških gradnikov in časovno metriko: - *latenca* je število ciklov za izračun izhodnih vrednosti (en klic funkcije) - *interval* II (iniciacijski interval) je število ciklov do naslednje ponovitve funkcije Vsi rezultati sinteze se shranijo v podmapo s privzetim imenom *solution1*. Poročilo sinteze podaja zmogljivost vezja, oceno porabe gradnikov, opis priključkov in povezav operacij z gradniki. Grafični pregledovalnik *Schedule Viewer* prikazuje podrobnejšo časovno razdelitev operacij, ki jih izvaja funkcija. Vezje *ale* ima latenco 1 cikel, interval 2 cikla in zasede 1 DSP ter 5 flip-flopov. V prvem ciklu ure prebere in dekodira operacijo, v drugem pa jo izvede. ![](./hls/aleSchedule.png) Če zmanjšamo periodo ure na 5 ns, se po ponovni sintezi število urnih ciklov izvedbe funkcije poveča za 3 zaradi množenja velikih števil, ki je časovno zahtevnejša operacija. V grafičnem pregledovalniku razberemo, da se izbrana operacije prebere v ciklu 0, v 1. ciklu se prebereta operanda in izračuna vsota, izračun produkta pa traja do 3. cikla. V zadnjem ciklu ure se določi izhodna vrednost. ![](./hls/aleSchedule2.png) Časovni potek delovanja vezja je odvisen od vmesnika in zahtevanih lastnosti. Na rezultat sinteze vplivamo z dodatnimi navodili v obliki direktiv. Če naprimer želimo obdelavo vhodnih podatkov v vsakem, ki se spremenijo v vsakem ciklu ure, sintetiziramo vezje v cevovodni izvedbi (pipeline). Takšno vezje bo običajno porabilo več logičnih in pomnilnih gradnikov. Direktive lahko vnašamo neposredno v opis funkcije ali pa s pomočjo grafičnega urejevalnika. V zavihku *Directive* dodamo z desnim klikom na glavno funkcijo *ale()* direktivo PIPELINE. Po sintezi vezja se inteval zmanjša na 1 cikel za ceno večje površine vezja (več uporabljenih FF in LUT). ![](./hls/directive.png) ### Simulacija Delovanje funkcije z visokonivojskim modelom vezja preizkusimo s simulacijo v programskem jeziku, kjer pripravimo simulacijski program (Testbench). Simulacijski program vsebuje glavno funkcijo main() v kateri nastavlja vhodne parametre, kliče funkcijo in prikazuje, shranjuje ali celo avtomatsko preverja rezultate. Zaznano napako lahko javimo tako, da glavna funkcija vrne vrednost različno od 0. ```C #include "ale.h" using namespace std; int main() { DATA_T x, y, z; OP_T op; cout << "Test Bench " << endl; x=8; y=7; op=2; z=ale(x,y,op); cout << x << "," << y << ", op:" << op << " z = " << z << endl; return 0; } ``` Za demonstracijo naredimo preprost testni program v katerem pokličemo funkcijo in izpišemo rezultate. Pri op=2 bo rezultat izpis: <pre> Test Bench 8,7, op:2 z = 56</pre> V praksi bo program bolj zapleten in bo naredil veliko klicev funkcije z različnimi parametri. Poleg simulacije v programskem jeziku je po sintezi na voljo povezana simulacija (Cosimulation), kjer se simulacijski program poveže s simulatorjem sintetiziranega vezja. Če želimo opazovati časovni potek signalov (waveform) moramo ob zagonu izbrati shranjevanje poteka signalov, npr. priključkov vezja (v okencu *Dump Trace* izberemo: *port*). ![](./hls/aleSim.png) Shranjen potek signalov lahko opazujemo v simulatorju vezij, ki je del orodja **Vivado**. Odpremo ga z novo ikono *Wave Viewer*, ki se prikaže v orodni vrstici po uspešno opravljeni povezani simulaciji. Na simulaciji opazujemo signale vmesnika: *ap_start*, *ap_idle*, *ap_ready* in *ap_done*. Logična 1 na *ap_start* sproži začetek izvajanja funkcije, *ap_done* pa signalizira zaključek, ko lahko preberemo rezultat.