〇、前言
2020.11.25日,Xilinx更新了Vitis2020.2版本。正好之前報(bào)名里Xilinx的自適應(yīng)計(jì)算挑戰(zhàn)賽,比賽要求使用Vitis平臺(tái)進(jìn)行開發(fā),所以今天趁著新版本發(fā)布把我之前參加DAC-SDC的項(xiàng)目SkrSkr遷移到Vitis平臺(tái)上。之前聽過一些介紹說Vitis將SDAccel和SDSoC合并到了一起,并使用OpenCL語言,所以在項(xiàng)目遷移之前我還是有點(diǎn)打怵的,但是經(jīng)過一天的嘗試基本搞定了。整個(gè)流程走下來感覺Vitis跟SDSoC換湯不換藥,只是調(diào)用加速器的方式稍有變化,整體的設(shè)計(jì)思想還是一致的。下面就進(jìn)入正題,如何一步一步將設(shè)計(jì)從SDSoC/Vivado HLS遷移到Vitis平臺(tái)。
一、環(huán)境準(zhǔn)備
1.安裝Vitis,此處省略
2. 安裝zcu104的platform
下載zcu104的base platform
從https://www.xilinx.com/support/download/index.html/content/xilinx/en/dow...下載ZCU104 Base 2020.2以及ZYNQMP common image
將ZCU104 Base 2020.2解壓到/tools/Xilinx/Vitis/2020.2/platforms/(Vitis的默認(rèn)安裝目錄)
3. 準(zhǔn)備sysroot
將ZYNQMP common image解壓到任意位置,并將rootfs.tar.gz進(jìn)一步解壓
mkdir sysroot tar -xvf rootfs.tar.gz -C sysroot
文件內(nèi)容如下所示
├── bl31.elf ├── boot.scr ├── Image ├── README.txt ├── rootfs.ext4 ├── rootfs.manifest ├── rootfs.tar.gz ├── sdk.sh ├── sysroot └── u-boot.elf
二、創(chuàng)建工程
![o4YBAGAKIy-AUecNAAEEEY9TnBc290.png](https://file.elecfans.com/web1/M00/DB/AB/o4YBAGAKIy-AUecNAAEEEY9TnBc290.png)
![pIYBAGAKI3yAPs0bAAIYnKTwNDQ934.png](https://file.elecfans.com/web1/M00/DC/2A/pIYBAGAKI3yAPs0bAAIYnKTwNDQ934.png)
![pIYBAGAKI7yADLCQAAJ1-1-W444821.png](https://file.elecfans.com/web1/M00/DC/2A/pIYBAGAKI7yADLCQAAJ1-1-W444821.png)
![pIYBAGAKI_2AEdpTAAGh4cjT3Tw502.png](https://file.elecfans.com/web1/M00/DC/2A/pIYBAGAKI_2AEdpTAAGh4cjT3Tw502.png)
sysroot,rootfs,kernel image指向剛才解壓出來的那些文件
![o4YBAGAKJD2AbWjWAAIP61LziE8764.png](https://file.elecfans.com/web1/M00/DB/AB/o4YBAGAKJD2AbWjWAAIP61LziE8764.png)
然后選擇空工程,至此新工程創(chuàng)建完畢
SkrSkr/Develop/C下邊的5個(gè)文件導(dǎo)入
. ├── main.cpp ├── SkyNet.cpp ├── SkyNet.h ├── transform.cpp └── utils.cpp
將SkyNet.cpp和SkyNet.h復(fù)制到SkyNet_kernels/src下,將main.cpp, SkyNet.h, transform.cpp, utils.cpp復(fù)制到SkyNet/src下,如圖所示
![o4YBAGAKJHyAZPbXAAE1jJEYnbw154.png](https://file.elecfans.com/web1/M00/DB/AB/o4YBAGAKJHyAZPbXAAE1jJEYnbw154.png)
SkyNet/src下存放的是Host端代碼,而SkyNet_kernels/src下存放的是FPGA端代碼,二者在編譯的時(shí)候是獨(dú)立的。對(duì)比之前Vivado HLS的開發(fā)流程,SkyNet_kernels/src下放的就是Vivado HLS工程里的文件,但是沒有testbench,而SkyNet/src放的就是綜合出來比特流之后在SDK里開發(fā)應(yīng)用程序的文件。(SDSoC好用就好用在將SDK的應(yīng)用程序代碼跟Vivado HLS里的testbench合并,邏輯上很直觀。Vitis這么搞純粹是為了上層使用OpenCL,個(gè)人認(rèn)為是倒退。但是好處是不會(huì)像SDSoC稍微修改一點(diǎn)代碼就可能導(dǎo)致整個(gè)工程重新編譯一遍)
三、修改源代碼
1. 修改SkyNet.cpp
SkyNet.cpp的設(shè)計(jì)基本不用動(dòng),唯一需要修改的就是接口定義
void SkyNet(ADT4* img, ADT32* fm, WDT32* weight, BDT16* biasm) { #pragma HLS INTERFACE m_axi depth=204800 port=img offset=slave bundle=fm #pragma HLS INTERFACE m_axi depth=628115 port=fm offset=slave bundle=fm #pragma HLS INTERFACE m_axi depth=13792 port=weight offset=slave bundle=wt #pragma HLS INTERFACE m_axi depth=432 port=biasm offset=slave bundle=bm #pragma HLS INTERFACE s_axilite register port=return #pragma HLS ALLOCATION instances=PWCONV1x1 limit=1 function #pragma HLS ALLOCATION instances=DWCONV3x3 limit=1 function
將接口定義刪掉即可
void SkyNet(ADT4* img, ADT32* fm, WDT32* weight, BDT16* biasm) { #pragma HLS ALLOCATION instances=PWCONV1x1 limit=1 function #pragma HLS ALLOCATION instances=DWCONV3x3 limit=1 function
2. 修改SkyNet/src/SkyNet.h
kernel端的SkyNet.h無需修改,但是Host端因?yàn)橐肙penCL來調(diào)用加速器,因此需要在頭文件中加入相關(guān)代碼(就是從案例vadd中復(fù)制過來的)
#ifndef SKYNET_H #define SKYNET_H #pragma once #define CL_HPP_CL_1_2_DEFAULT_BUILD #define CL_HPP_TARGET_OPENCL_VERSION 120 #define CL_HPP_MINIMUM_OPENCL_VERSION 120 #define CL_HPP_ENABLE_PROGRAM_CONSTRUCTION_FROM_ARRAY_COMPATIBILITY 1 #include #include #include #include #include #include #include #include #include #include #include #include "ap_int.h"
3.修改SkyNet/src/SkyNet.cpp
這部分改動(dòng)比較大,主要就是要用OpenCL的方式加載Kernel,分配內(nèi)存,還是以vadd的案例作為參照。
1. 首先把加載kernel部分代碼全盤復(fù)制過來
int main(int argc, char* argv[]) { if(argc != 2) { std::cout << "Usage: " << argv[0] <<" "<< std::endl; return EXIT_FAILURE; } char* xclbinFilename = argv[1]; std::vector devices; cl::Device device; std::vector platforms; bool found_device = false; cl::Platform::get(&platforms); for(size_t i = 0; (i < platforms.size() ) & (found_device == false) ;i++){ cl::Platform platform = platforms[i]; std::string platformName = platform.getInfo(); if ( platformName == "Xilinx"){ devices.clear(); platform.getDevices(CL_DEVICE_TYPE_ACCELERATOR, &devices); if (devices.size()){ device = devices[0]; found_device = true; break; } } } if (found_device == false){ std::cout << "Error: Unable to find Target Device " << device.getInfo() << std::endl; return EXIT_FAILURE; } // Creating Context and Command Queue for selected device cl::Context context(device); cl::CommandQueue q(context, device, CL_QUEUE_PROFILING_ENABLE); // Load xclbin std::cout << "Loading: '" << xclbinFilename << "'/n"; std::ifstream bin_file(xclbinFilename, std::ifstream::binary); bin_file.seekg (0, bin_file.end); unsigned n_b = bin_file.tellg(); bin_file.seekg (0, bin_file.beg); char *buf = new char [n_b]; bin_file.read(buf, n_b); // Creating Program from Binary File cl::Program::Binaries bins; bins.push_back({buf,n_b}); devices.resize(1); cl::Program program(context, devices, bins);
2. 聲明kernel
cl::Kernel krnl_SkyNet(program,"SkyNet");
3. 分配kernel端內(nèi)存cl::buffer
在SDSoC中我們要給權(quán)重、特征圖等buffer分配連續(xù)內(nèi)存地址,
img = (ADT4*)sds_alloc(4*160*320*sizeof(ADT4)); weight = (WDT32*)sds_alloc(441344*sizeof(WDT)); biasm = (BDT16*)sds_alloc(432*sizeof(BDT16)); fm = (ADT32*)sds_alloc(32*fm_all*sizeof(ADT));
在PYNQ框架中我們用xlnk分配連續(xù)內(nèi)存地址
img = xlnk.cma_array(shape=[4,160,320,4], dtype=np.uint8) fm = xlnk.cma_array(shape=(628115*32), dtype=np.uint8) weight = xlnk.cma_array(shape=(220672), dtype=np.int16) biasm = xlnk.cma_array(shape=(432*16), dtype=np.int16)
其中img和weight對(duì)于加速器來說是只讀不寫,但是biasm和fm既讀又寫,這一點(diǎn)在SDSoC和PYNQ中都無需刻意區(qū)分,但是在OpenCL需要額外注意。
cl::Buffer(按照我的理解)是在DDR中給kernel(FPGA)端分配一段內(nèi)存,
cl::Buffer buffer_img(context, CL_MEM_READ_ONLY, 160*320*sizeof(ADT16)); cl::Buffer buffer_fm(context, CL_MEM_READ_WRITE, 32*fm_all*sizeof(ADT)); cl::Buffer buffer_wt(context, CL_MEM_READ_ONLY, 441344*sizeof(WDT)); cl::Buffer buffer_bm(context, CL_MEM_READ_WRITE, 432*sizeof(BDT16));
所以其讀寫是從kernel端看的,加速器中對(duì)img只是讀沒有寫,所以在聲明cl::Buffer時(shí)用CL_MEM_READ_ONLY,而fm和biasm既有讀又有寫,所以用CL_MEM_READ_WRITE。
4. 配置加速器
在PYNQ框架里我們要把加速器各個(gè)端口對(duì)應(yīng)對(duì)物理地址傳給加速器
SkyNet = overlay.SkyNet SkyNet.write(0x10, img.physical_address) SkyNet.write(0x1c, fm.physical_address) SkyNet.write(0x28, weight.physical_address) SkyNet.write(0x34, biasm.physical_address)
在SDSoC中這個(gè)步驟工具會(huì)自動(dòng)生成相應(yīng)的代碼,但是在OpenCL中需要手動(dòng)指定,需要注意參數(shù)的順序要跟function的順序一致。
//set the kernel Arguments int narg=0; krnl_SkyNet.setArg(narg++,buffer_img); krnl_SkyNet.setArg(narg++,buffer_fm); krnl_SkyNet.setArg(narg++,buffer_wt); krnl_SkyNet.setArg(narg++,buffer_bm);
5.分 配Host端內(nèi)存
在PYNQ框架中我們并不能直接訪問屬于加速器的內(nèi)存片段,因此在Host端都是操作numpy數(shù)組,然后將numpy數(shù)組的數(shù)據(jù)復(fù)制到屬于加速器的內(nèi)存片段
parameter = np.fromfile("SkyNet.bin", dtype=np.int16) np.copyto(weight, parameter[0:220672])# 從numpy數(shù)組復(fù)制到加速器內(nèi)存 np.copyto(biasm[0:428*16], parameter[220672:]) print("Parameters loading done") bbox_origin = np.empty(64, dtype=np.int16) bbox = np.zeros((4,4),dtype=np.float32) result= open('predict.txt','w+') batch_buff = None image = np.zeros((4,160,320,4),np.uint8) image_buff = np.zeros((4,160,320,4),np.uint8) ... np.copyto(bbox_origin, biasm[428*16:])# 從加速器內(nèi)存復(fù)制到numpy數(shù)組 ...
再次強(qiáng)調(diào)一下在SDSoC中不區(qū)分Host端內(nèi)存和Kernel端內(nèi)存
img = (ADT4*)sds_alloc(4*160*320*sizeof(ADT4)); weight = (WDT32*)sds_alloc(441344*sizeof(WDT)); biasm = (BDT16*)sds_alloc(432*sizeof(BDT16)); fm = (ADT32*)sds_alloc(32*fm_all*sizeof(ADT));
在OpenCL中要將Kernel端內(nèi)存映射到Host端內(nèi)存
ADT32* img = (ADT32*) q.enqueueMapBuffer (buffer_img , CL_TRUE , CL_MAP_WRITE , 0, 160*320*sizeof(ADT32)); ADT32* ofm_blob32 = (ADT32*) q.enqueueMapBuffer (buffer_fm , CL_TRUE , CL_MAP_READ | CL_MAP_WRITE , 0, fm_all*sizeof(ADT32)); WDT32* weight = (WDT32*) q.enqueueMapBuffer (buffer_wt , CL_TRUE , CL_MAP_WRITE , 0, 441344*sizeof(WDT)); BDT16* biasm = (BDT16*) q.enqueueMapBuffer (buffer_bm , CL_TRUE , CL_MAP_READ | CL_MAP_WRITE , 0, 432*sizeof(BDT16));
需要說明的是此時(shí)對(duì)讀寫是從Host端開過來的,所以與Kernel端配置反過來。
6. 啟動(dòng)加速器
在PYNQ里我們用如下代碼控制加速器啟動(dòng)與停止
SkyNet.write(0x00, 1) isready = SkyNet.read(0x00) while( isready == 1 ): isready = SkyNet.read(0x00)
在OpenCL中對(duì)應(yīng)的代碼稍微復(fù)雜一點(diǎn)點(diǎn),如下所示
q.enqueueMigrateMemObjects({buffer_img,buffer_wt},0/* 0 means from Host*/); q.enqueueTask(krnl_SkyNet); q.enqueueMigrateMemObjects({buffer_fm,buffer_bm},CL_MIGRATE_MEM_OBJECT_HOST); q.finish();
在這里又一次出現(xiàn)了對(duì)內(nèi)存的配置,q.enqueueMigrateMemObjects的用法我不是非常清楚,但是大概意思就是CL_MIGRATE_MEM_OBJECT_HOST選項(xiàng)表示Host在啟動(dòng)加速器之后還得回去接收數(shù)據(jù),而0就不用。加速器運(yùn)行完后邊的數(shù)據(jù)處理跟SDSoC里是完全一致的。
7. 回收內(nèi)存
q.enqueueUnmapMemObject(buffer_img, img); q.enqueueUnmapMemObject(buffer_fm, ofm_blob32); q.enqueueUnmapMemObject(buffer_wt, weight); q.enqueueUnmapMemObject(buffer_bm, biasm); q.finish();
四、編譯工程
1. 選擇硬件函數(shù)
現(xiàn)在的工程并沒有選擇硬件函數(shù)加速
![pIYBAGAKJL6AO0bIAAPWtkFQwDI888.png](https://file.elecfans.com/web1/M00/DC/2A/pIYBAGAKJL6AO0bIAAPWtkFQwDI888.png)
點(diǎn)擊Hardware Functions里的藍(lán)色按鈕將SkyNet添加為硬件函數(shù),并勾選Max Memory Ports。
![pIYBAGAKJQSALReVAAPi53SiwyY151.png](https://file.elecfans.com/web1/M00/DC/2A/pIYBAGAKJQSALReVAAPi53SiwyY151.png)
2. 編譯工程
將Activation build configuration改為Hardware,點(diǎn)擊左側(cè)的菜單欄SkyNet_system,然后點(diǎn)擊小錘子,整個(gè)工程就開始編譯了。在R7 3700X上大概20分鐘就能編譯完成。
![pIYBAGAKJUyAVtLTAAZnAJPyAvs998.png](https://file.elecfans.com/web1/M00/DC/2A/pIYBAGAKJUyAVtLTAAZnAJPyAvs998.png)
SkyNet文件夾存放Host端調(diào)用加速器的代碼,SkyNet_kernels文件夾存放FPGA端的加速器代碼,但是加速器端代碼還只是代碼,并不是一個(gè)比特流,SkyNet_system_hw_link文件夾用來存放Vivado工程,生成的比特流就在這個(gè)文件夾下。編譯完畢我們可以在SkyNet_system_hw_link找到Vivado工程
![o4YBAGAKJcSAPAnUAAC1-uhegmY736.png](https://file.elecfans.com/web1/M00/DB/AB/o4YBAGAKJcSAPAnUAAC1-uhegmY736.png)
打開看一下block diagram,可以發(fā)現(xiàn)勾選Max Memory Ports生效了,加速器總共生成了4個(gè)M_AXI接口,如果不勾選Max Memory Ports那么四個(gè)接口就會(huì)bundle到一起,影響加速器的傳輸性能。
![pIYBAGAKJjCAW1H7AActhpgzM8U114.png](https://file.elecfans.com/web1/M00/DC/2A/pIYBAGAKJjCAW1H7AActhpgzM8U114.png)
五、上板測試
1. 燒寫SD卡
在編譯好的工程下我們可以看到如下文件
![pIYBAGAKJweALXUSAAFdFr_abPo952.png](https://file.elecfans.com/web1/M00/DC/2A/pIYBAGAKJweALXUSAAFdFr_abPo952.png)
我們把sd_card.img文件復(fù)制到windows電腦上燒寫進(jìn)SD卡,具體怎么燒寫之前的博文中有提到,在此不再重復(fù)
2. 準(zhǔn)備文件
用Mobaxterm登錄板子,默認(rèn)用戶名和密碼都是root。將SkyNet,init.sh,binary_container_1.xclbin以及SkrSkr/Develop/C/blob和SkrSkr/Develop/C/weight復(fù)制到/home/root下,如圖所示
![pIYBAGAKJ0eAbPDFAAHVW2QlAYA239.png](https://file.elecfans.com/web1/M00/DC/2A/pIYBAGAKJ0eAbPDFAAHVW2QlAYA239.png)
配置一下xrt環(huán)境
./init.sh
然后就可以開始測試?yán)?/p>
至此移植工作全部結(jié)束。目前的linux系統(tǒng)是Vitis自動(dòng)生成的(其實(shí)應(yīng)該是Petalinux構(gòu)建出來的),可用性很差,后邊會(huì)嘗試把Vitis跟PYNQ結(jié)合,將Vitis生成的可執(zhí)行文件放在PYNQ提供的運(yùn)行環(huán)境下執(zhí)行。
審核編輯 黃昊宇
-
SDSoC
+關(guān)注
關(guān)注
0文章
24瀏覽量
12248 -
Vitis
+關(guān)注
關(guān)注
0文章
147瀏覽量
7507
發(fā)布評(píng)論請先 登錄
相關(guān)推薦
Vitis2023.2使用之—— updata to Vitis Unified IDE
FPGA高層次綜合HLS之Vitis HLS知識(shí)庫簡析
使用Vitis HLS創(chuàng)建屬于自己的IP相關(guān)資料分享
將SDAccel項(xiàng)目遷移到Vitis 2019.2的技巧
![將SDAccel項(xiàng)目<b class='flag-5'>遷移到</b><b class='flag-5'>Vitis</b> 2019.2的技巧](https://file.elecfans.com/web1/M00/BF/70/o4YBAF73_G-AO4RJAAA67maPUOA889.png)
Vivado HLS和Vitis HLS 兩者之間有什么區(qū)別
Vitis初探—1.將設(shè)計(jì)從SDSoC/Vivado HLS遷移到Vitis上
![<b class='flag-5'>Vitis</b><b class='flag-5'>初探</b>—<b class='flag-5'>1.</b><b class='flag-5'>將設(shè)</b>計(jì)<b class='flag-5'>從</b><b class='flag-5'>SDSoC</b>/<b class='flag-5'>Vivado</b> <b class='flag-5'>HLS</b><b class='flag-5'>遷移到</b><b class='flag-5'>Vitis</b><b class='flag-5'>上</b>](https://file.elecfans.com/web1/M00/DB/AB/o4YBAGAKIy-AUecNAAEEEY9TnBc290.png)
基于Vitis HLS的加速圖像處理
![基于<b class='flag-5'>Vitis</b> <b class='flag-5'>HLS</b>的加速圖像處理](https://file.elecfans.com/web2/M00/30/D6/pYYBAGIMpwuAc9UmAADrfB2ykoA971.png)
Vitis HLS工具簡介及設(shè)計(jì)流程
Vitis HLS如何添加HLS導(dǎo)出的.xo文件
![<b class='flag-5'>Vitis</b> <b class='flag-5'>HLS</b>如何添加<b class='flag-5'>HLS</b>導(dǎo)出的.xo文件](https://file.elecfans.com/web2/M00/1F/61/poYBAGGYHZqABmUiAAL3nUdW7oI316.png)
Vitis HLS前端現(xiàn)已全面開源
Vitis HLS知識(shí)庫總結(jié)
理解Vitis HLS默認(rèn)行為
AMD全新Vitis HLS資源現(xiàn)已推出
![AMD全新<b class='flag-5'>Vitis</b> <b class='flag-5'>HLS</b>資源現(xiàn)已推出](https://file1.elecfans.com/web2/M00/82/20/wKgaomREmyqAMDmOAAAdFC8hyjQ866.png)
評(píng)論