Ultra96/Ultra96-V2/KV260 向け Debian GNU/Linux および Ubuntu20.04 で XRT(Xilinx Runtime) を動かす(2.11.0 ビルド編)







# はじめに


筆者は Ultra96/Ultra96-V2/KV260(ZynqMP) 向けに Debian GNU/Linux[1] と Ubuntu20.04[2] を提供しています。また、XRT(Xilinx Runtime) はXilinx社が提供する開発環境(Vitis)で開発したプログラムをプラットフォームで動かすための環境です[3]。

本来なら MPSoC Edge Device 用の [XRT] は Xilinx が提供する Linux 環境(Petalinux) でしか動きませんが、XRT を Ultra96/Ultra96-V2/KV260 向け Debian GNU/Linux と Ubuntu20.04 で動かすための手順を幾つかのパートに分けて説明します。 



* [概要編]
* ビルド編
  *  __XRT 2.11.0 ビルド編(この記事)__  
  * [XRT 2.8.1 ビルド編]
  * [XRT 2.6.0 ビルド編]
* インストール編
  * [XRT 2.11.0 インストール編]
  * [XRT 2.6.0 インストール編]
* [実践編]
* [Vitis編]



この記事では XRT 2.11.0 を Ultra96/Ultra96-V2/KV260 向け Debian GNU/Linux および Ubuntu20.04用にビルドする手順について説明します。




## 注意


  __この記事で紹介している Debian Package は Xilinx 社の公式なものではありません。__  


# XRT Debian Package の提供


XRT の Debian Package をビルドするためにはビルド環境を整えなければならず、とても面倒です。そこでビルド済みの Debian Package を用意しています。自分でビルドするのが面倒なかたはこちらをお使いください。


## Debian 11 用


* リポジトリ: https://github.com/ikwzm/ZynqMP-FPGA-XRT
* ブランチ: [2021.1_EDGE_0\_Debian_11] 
* パッケージファイル: xrt_202110.2.11.0_Edge_Debian_11-arm64.deb


```console
fpga@debian-fpga:~/work$ git clone --depth 1 --branch 2021.1_EDGE_0_Debian_11 https://github.com/ikwzm/ZynqMP-FPGA-XRT.git
fpga@debian-fpga:~/work$ sudo apt install ./ZynqMP-FPGA-XRT/xrt_202110.2.11.0_Edge_Debian_11-arm64.deb
```



## Ubuntu 20.04 用


* リポジトリ: https://github.com/ikwzm/ZynqMP-FPGA-XRT
* ブランチ: [2021.1_EDGE_0\_Ubuntu_20.04]
* パッケージファイル: xrt_202110.2.11.0_Edge_Ubuntu_20.04-arm64.deb


```console
fpga@ubuntu-fpga:~/work$ git clone --depth 1 --branch 2021.1_EDGE_0_Ubuntu_20.04 https://github.com/ikwzm/ZynqMP-FPGA-XRT.git
fpga@ubuntu-fpga:~/work$ sudo apt install ./ZynqMP-FPGA-XRT/xrt_202110.2.11.0_Edge_Ubuntu_20.04-arm64.deb
```





# XRT のビルド



## ビルド環境


XRT を ZynqMP-FPGA-Linux 向けにビルドするのは少し面倒です。XRT のビルドには Ubuntu や CentOS などの Linux ディストリビューションが必要です。ビルドには色々な開発ツールがインストールされていなければなりません。さらに Debian Package を作るためか、PC をホストにして ARM64 用にクロスコンパイルするのがとても面倒です。

そこで、 Ultra96-V2 や KV260  に Debian または Ubuntu 20.04 を動かして、その上でセルフビルドします。Ultra96/Ultra96-V2/KV260 用の Debian 11 および Ubuntu 20.04 は以下の URL で公開しています。

* https://github.com/ikwzm/ZynqMP-FPGA-Linux 
* https://github.com/ikwzm/ZynqMP-FPGA-Ubuntu20.04




## XRT のダウンロード


以下の URL より XRT のソースコードをダウンロードします。ブランチは 2021.1_EDGE です。



* リポジトリ: https://github.com/Xilinx/XRT
* ブランチ: 2021.1_EDGE



このリポジトリは、 Xilinx 社が github に公開している https://github.com/Xilinx/XRT を fork して各種修正を加えたものです。修正内容は次章で説明します。


```console
fpga@ubuntu-fpga:~$ cd work
fpga@ubuntu-fpga:~/work$ git clone  --depth 1 --branch 2021.1https://github.com/Xilinx/XRT
Cloning into 'XRT'...
remote: Enumerating objects: 2370, done.
remote: Counting objects: 100% (2370/2370), done.
remote: Compressing objects: 100% (2001/2001), done.
remote: Total 2370 (delta 737), reused 807 (delta 325), pack-reused 0
Receiving objects: 100% (2370/2370), 24.12 MiB | 2.75 MiB/s, done.
Resolving deltas: 100% (737/737), done.
Updating files: 100% (2161/2161), done.
```





## ビルドに必要な Debian Package をインストール


ビルドに必要なパッケージをインストールします。幸い XRT には、ビルドに必要なパッケージをインストールするスクリプト ./src/runtime_src/tools/scripts/xrtdeps.sh  が用意されています。

./src/runtime_src/tools/scripts/xrtdeps.sh を super user 権限で実行すると、apt プログラムによって足りない Debian Package がインストールされます。


```console
fpga@ubuntu-fpga:~/work/XRT$ sudo ./src/runtime_src/tools/scripts/xrtdeps.sh
Preparing ubuntu ...
Installing packages...
Reading package lists... Done
Building dependency tree... Done
Reading state information... Done
	:
(後略)
	:

```







## ビルド


build ディレクトリに移動して、そこにある build.sh に "-edge" オプションをつけて実行することで、ビルドが開始されます。私のUltra96/Ultra96-V2/KV260 の環境だと、並列処理するコア数がデフォルトの４のままだと、途中で make コマンドがエラー終了しました。そこでコア数を１にするために "-j 1" オプションをつけています。Ultra96/Ultra96-V2 だとけっこう時間がかかります。私の環境では１時間20分ほどかかりました。


```console:build.log
fpga@debian-fpga:~/work/XRT$ cd build/
fpga@ubuntu-fpga:~/work/XRT/build$ ./build.sh -edge -j 1
XRT_EDGE_BUILD=yes cmake -DRDI_CCACHE=0 -DCMAKE_BUILD_TYPE=Release -DCMAKE_EXPORT_COMPILE_COMMANDS=ON ../../src
-- The C compiler identification is GNU 10.2.1
-- The CXX compiler identification is GNU 10.2.1
-- Detecting C compiler ABI info
-- Detecting C compiler ABI info - done
-- Check for working C compiler: /usr/bin/cc - skipped
-- Detecting C compile features
-- Detecting C compile features - done
-- Detecting CXX compiler ABI info
-- Detecting CXX compiler ABI info - done
-- Check for working CXX compiler: /usr/bin/c++ - skipped
 
-- Detecting CXX compile features
-- Detecting CXX compile features - done
-- Found UnixCommands: /bin/bash
-- Host system processor is aarch64
-- Target system processor is aarch64
-- Checking for module 'libdrm'
--   Found libdrm, version 2.4.104
-- Looking for DRM - found at /usr 2.4.104
-- Checking for module 'OpenCL'
--   Found OpenCL, version 3.0
-- Looking for OPENCL - found at /usr 3.0 /usr/include
-- Found Git: /usr/bin/git (found version "2.30.2")
-- Looking for GIT - found at /usr/bin/git
-- Found Boost: /usr/lib/aarch64-linux-gnu/cmake/Boost-1.74.0/BoostConfig.cmake\ (found version "1.74.0") found components: system filesystem program_options
-- Found Curses: /usr/lib/aarch64-linux-gnu/libcurses.so
-- XRT EA eula files  /home/fpga/work/tmp/XRT/src/../LICENSE
-- Platform/Linux (Debian) (Kernel 5.10.0-xlnx-v2021.1-zynqmp-fpga)
-- Compiler: /usr/bin/c++ /usr/bin/cc
-- kernel-doc downloading
-- Found OpenSSL: /usr/lib/aarch64-linux-gnu/libcrypto.so (found version "1.1.1k")
-- RapidJSON found. Headers: /usr/include	:
-- Found GTest: /usr/lib/aarch64-linux-gnu/libgtest.a
-- XRT xrt++ header files
-- xrt++.hpp
-- xrtexec.hpp
CMake Warning at runtime_src/ert/CMakeLists.txt:52 (message):
  ****************************************************************
  No firmware files built or copied, resulting XRT package will be missing
  ERT scheduler firmware.  Use build.sh -ertfw <dir> to specify path to a
  directory with firmware to copy during XRT build.
  ****************************************************************

-- XRT deprecated header files
-- xrt.h
-- XRT header files for MPSoC only
-- xclhal2_mpsoc.h
-- sk_types.h
-- Found Protobuf: /usr/lib/aarch64-linux-gnu/libprotobuf.so;-lpthread (found version "3.12.4")
-- Found PythonLibs: /usr/lib/aarch64-linux-gnu/libpython3.9.so (found suitable version "3.9.2", minimum required is "3.4.0")
-- Python libs version: 3.9.2
-- PYTHON_INCLUDE_PATH /usr/include/python3.9
-- Found PythonInterp: /usr/bin/python3 (found version "3.9.2")
-- Found PythonLibs: /usr/lib/aarch64-linux-gnu/libpython3.9.so
-- Performing Test HAS_FLTO
-- Performing Test HAS_FLTO - Success
-- Found pybind11: /usr/local/lib/python3.9/dist-packages/pybind11/include (found version "2.9.1")
-- Looking for CL_VERSION_2_2
-- Looking for CL_VERSION_2_2 - found
-- Found OpenCL: /usr/lib/aarch64-linux-gnu/libOpenCL.so (found version "2.2")
-- XRT version: 2.11.0
-- Release DEB package
-- XRT DRIVER SRC BASE DIR /home/fpga/work/tmp/XRT/src/runtime_src/core
-- Preparing OpenCL ICD xilinx.icd
-- Preparing XRT pkg-config
-- Coverity tool not found and will be skipped
-- Preparing XRT find_package
-- Configuring done
-- Generating done
-- Build files have been written to: /home/fpga/work/tmp/XRT/build/Edge
real    0m13.350s
user    0m7.333s
sys     0m2.241s
make -j 1  DESTDIR=/home/fpga/work/tmp/XRT/build/Edge install
    :
  (中略)
    :
real    1m19.102s
user    1m4.807s
sys     0m14.210s
```




ビルドが終了すると、build/Edge ディレクトリの下に Debian Package が出来ています。


```console
fpga@debian-fpga:~/work/XRT/build$ ls -1 Edge/xrt_*
Edge/xrt_202110.2.11.0_Edge_Debian_11-arm64.deb
Edge/xrt_202110.2.11.0_Edge_Debian_11-arm64.tar.gz
```


このうち Ultra96/Ultra96-V2 に必要な Debian Package は、Edge/xrt_202110.2.11.0_Edge_Debian_11-arm64.deb です。




# XRT の変更内容


この章では、オリジナルの XRT(https://github.com/Xilinx/XRT ブランチ2021.1) に対して、私が独自に変更した点について説明します。




## Debian 11 に対応


Debian 11 の gcc および g++ のバージョンは10です。しかし、ビルドに必要な Debian Package をインストールするスクリプト ./src/runtime_src/tools/scripts/xrtdeps.sh はバージョン8を想定しています。そこでこのスクリプトを次のように修正します。


```diff_shell:ZynqMP-FPGA-XRT/files/2021.1-xrt-edge-01.patch
diff --git a/src/runtime_src/tools/scripts/xrtdeps.sh b/src/runtime_src/tools/scripts/xrtdeps.sh
index 60f2976d0..a8f2577a6 100755
--- a/src/runtime_src/tools/scripts/xrtdeps.sh
+++ b/src/runtime_src/tools/scripts/xrtdeps.sh
@@ -219,8 +219,13 @@ ub_package_list()
     # Use GCC8 on ARM64 Ubuntu as GCC7 randomly crashes with Internal Compiler Error on
     # Travis CI ARM64 platform
     if [ $ARCH == "aarch64" ]; then
-        UB_LIST+=( gcc-8 )
-        UB_LIST+=( g++-8 )
+        if [ $FLAVOR == "debian" ] && [ $MAJOR == 11 ]; then
+            UB_LIST+=( gcc-10 )
+            UB_LIST+=( g++-10 )
+        else    
+            UB_LIST+=( gcc-8 )
+            UB_LIST+=( g++-8 )
+        fi
     fi
 
 }

```





## XRT_EDGE_BUILD を追加


オリジナルではホストプロセッサとターゲットプロセッサが同じ場合に XRT_NATIVE_BUILD という変数が "yes" にセットされます。 そして、XRT_NATIVE_BUILD が "yes" の時(ホストプロセッサとターゲットプロセッサが同じ時)に src/runtime_src/core/pcie が、"no" の時(ホストプロセッサとターゲットプロセッサが異なる時)に src/runtime_src/core/edge がビルドされるように src/runtime_src/core/CMakeLists.txt が定義されています。


```Makefile:src/runtime_src/core/CMakeLists.txt
include_directories(
  ${CMAKE_CURRENT_SOURCE_DIR}
  )
add_subdirectory(include)
 if (${XRT_NATIVE_BUILD} STREQUAL "yes")
   add_subdirectory(common)
   add_subdirectory(pcie)
   add_subdirectory(tools)
 else()
   add_subdirectory(common)
   add_subdirectory(edge)
   add_subdirectory(tools)
 endif()

```




また、XRT_NATIVE_BUILD が "yes" の時(ホストプロセッサとターゲットプロセッサが同じ時)にインクルードされる src/CMake/nativeLnx.cmake には、次のようにホスト用のドライバをビルド＆インストールするようになっています。 


```Makefile:src/CMake/nativeLnx.cmakext
	:
	(前略)
	:
include (CMake/dkms.cmake)
include (CMake/dkms-aws.cmake)
include (CMake/dkms-azure.cmake)
include (CMake/dkms-container.cmake)
	:
	(後略)
```




Ultra96-V2 や KV260 に Debian 11 および Ubuntu 20.04  を動かしてその上でセルフビルドする場合は、ホストプロセッサとターゲットプロセッサが同じ arm64 になります。このままだと、edge 用のではなく pcie 用にビルドされてしまいます。



そこで、次のように src/CMakfile.txt に XRT_EDGE_BUILD 変数を追加します。 


```Makefile:src/CMakeLists.txt
	:
	(前略)
	:	
set(XRT_EDGE_BUILD "no")
if (XRT_NATIVE_BUILD STREQUAL "no")
  set(XRT_EDGE_BUILD "no")
endif()
if (DEFINED ENV{XRT_EDGE_BUILD})
  set(XRT_EDGE_BUILD $ENV{XRT_EDGE_BUILD})
endif()
	:
	(後略)
```




そして XRT_EDGE_BUILD が "yes" の場合は edge 用のビルドを行うように、それぞれのファイルを変更します。


```Makefile:src/runtime_src/core/CMakeLists.txt
include_directories(
  ${CMAKE_CURRENT_SOURCE_DIR}
  )
add_subdirectory(include)
 if (${XRT_EDGE_BUILD} STREQUAL "yes")
   add_subdirectory(common)
   add_subdirectory(edge)
   add_subdirectory(tools)
 else()
   add_subdirectory(common)
   add_subdirectory(pcie)
   add_subdirectory(tools)
 endif()

```





```Makefile:src/CMake/nativeLnx.cmakext
	:
	(前略)
	:
if (${XRT_EDGE_BUILD} STREQUAL "yes")
  include (CMake/dkms-edge.cmake)
else()
  include (CMake/dkms.cmake)
  include (CMake/dkms-aws.cmake)
  include (CMake/dkms-azure.cmake)
  include (CMake/dkms-container.cmake)
endif()
	:
	(後略)
```


また、XRT_NATIVE_BUILD が "yes" の時(ホストプロセッサとターゲットプロセッサが同じ時)、コンパイルのオプションに "-Wall" と -"Werror" が追加されるような設定が src/runtime_src/CMakeLists.txt にあります。


```Makefile:src/runtime_src/CMakeLists.txt
	:
	(前略)
	:
# TODO CL_TARGET_OPENCL_VERSION is not defined..
if (${XRT_NATIVE_BUILD} STREQUAL "yes")
  add_compile_options("-Wall" "-Werror")
endif()
	:
	(後略)
```


"-Werror" オプションがあると src/runtime_src/core/edge 以下のビルド時にエラーが出てコンパイルに失敗します。そこで XRT_EDGE_BUILD が "yes" の時はコンパイルオプションに "-Wall" と "-Werror" が追加されないように src/runtime_src/CMakeLists.txt を修正します。


```Makefile:src/runtime_src/CMakeLists.txt
	:
	(前略)
	:
# TODO CL_TARGET_OPENCL_VERSION is not defined..
if (${XRT_EDGE_BUILD} STREQUAL "no")
  add_compile_options("-Wall" "-Werror")
endif()	:
	(後略)
```




最後に build/build.sh を修正して、ホストプロセッサが arm64 かつ "-edge" オプションが追加された場合は XRT_EDGE_BUILD が "yes" になるように修正します。


```Shell:build/build.sh
	:
	(前略)
	:
if [[ $CPU == "aarch64" ]] && [[ $edge == 1 ]]; then
  mkdir -p $edge_dir
  cd $edge_dir
  if [[ $nocmake == 0 ]]; then
    echo    "XRT_EDGE_BUILD=yes $CMAKE -DRDI_CCACHE=$ccache -DCMAKE_BUILD_TYPE=Release -DCMAKE_EXPORT_COMPILE_COMMANDS=ON ../../src"
    time env XRT_EDGE_BUILD=yes $CMAKE -DRDI_CCACHE=$ccache -DCMAKE_BUILD_TYPE=Release -DCMAKE_EXPORT_COMPILE_COMMANDS=ON ../../src
  fi
  if [[ $nobuild == 0 ]]; then
      echo "make -j $jcore $verbose DESTDIR=$PWD install"
      time  make -j $jcore $verbose DESTDIR=$PWD install
      if [[ $noctest == 0 ]]; then
	  time ctest --output-on-failure
      fi
      time make package
  fi
  if [[ $docs == 1 ]]; then
      echo "make xrt_docs"
      make xrt_docs
  fi
fi
	:
	(後略)
```





## パッケージファイル名を変更


オリジナルの XRT では、ビルドした Debian Package の名前は xrt_202010.2.8.0_18.04-arm64-xrt.deb になりますが、Debian 11 用またはUbuntu 用のパッケージであることを明示するために、次のようにEdge というキーワードとともに Linux のフレーバー名を追加しておきます。


```diff
 diff --git a/src/CMake/cpackLin.cmake b/src/CMake/cpackLin.cmake
index 2cc895182..cb59a320a 100644
--- a/src/CMake/cpackLin.cmake
+++ b/src/CMake/cpackLin.cmake
@@ -80,9 +80,9 @@ if (${LINUX_FLAVOR} MATCHES "^(Ubuntu|Debian)")
       SET(CPACK_DEBIAN_PACKAGE_ARCHITECTURE "armhf")
       SET(CPACK_ARCH "aarch32")
     endif()
-    SET(CPACK_DEBIAN_PACKAGE_CONTROL_EXTRA "${CMAKE_CURRENT_BINARY_DIR}/postinst;${CMAKE_CURRENT_BINARY_DIR}/prerm")
-    SET(CPACK_DEBIAN_PACKAGE_DEPENDS ${CPACK_DEBIAN_XRT_PACKAGE_DEPENDS})
   endif()
+  SET(CPACK_DEBIAN_PACKAGE_CONTROL_EXTRA "${CMAKE_CURRENT_BINARY_DIR}/postinst;${CMAKE_CURRENT_BINARY_DIR}/prerm")
+  SET(CPACK_DEBIAN_PACKAGE_DEPENDS ${CPACK_DEBIAN_XRT_PACKAGE_DEPENDS})
 
 elseif (${LINUX_FLAVOR} MATCHES "^(RedHat|CentOS|Amazon|Fedora|SUSE)")
   execute_process(
@@ -144,7 +144,11 @@ else ()
   SET (CPACK_GENERATOR "TGZ")
 endif()
 
-SET(CPACK_PACKAGE_FILE_NAME "${CPACK_PACKAGE_NAME}_${XRT_VERSION_RELEASE}.${CPACK_PACKAGE_VERSION_MAJOR}.${CPACK_PACKAGE_VERSION_MINOR}.${CPACK_PACKAGE_VERSION_PATCH}_${CPACK_REL_VER}-${CPACK_ARCH}")
+if (${XRT_EDGE_BUILD} STREQUAL "yes")
+   SET(CPACK_PACKAGE_FILE_NAME "${CPACK_PACKAGE_NAME}_${XRT_VERSION_RELEASE}.${CPACK_PACKAGE_VERSION_MAJOR}.${CPACK_PACKAGE_VERSION_MINOR}.${CPACK_PACKAGE_VERSION_PATCH}_Edge_${LINUX_FLAVOR}_${CPACK_REL_VER}-${CPACK_ARCH}")
+else()
+   SET(CPACK_PACKAGE_FILE_NAME "${CPACK_PACKAGE_NAME}_${XRT_VERSION_RELEASE}.${CPACK_PACKAGE_VERSION_MAJOR}.${CPACK_PACKAGE_VERSION_MINOR}.${CPACK_PACKAGE_VERSION_PATCH}_${CPACK_REL_VER}-${CPACK_ARCH}")
+endif()
 
 message("-- ${CMAKE_BUILD_TYPE} ${PACKAGE_KIND} package")
 

```



## 不要なテストの削除


Debian Package のビルド時に、ビルドしたツールが正常に動作するかテストが行われます。しかし、Xilinx オリジナルのままでは、Edge 用の Debian Package にもかかわらず、PCI-Express 用のツールである xbmgmt と xbutil のテストを試みようとして失敗します。どこで、src/CMake/NativeTests.cmake を次のように修正して不要なテストを行わないようにします。


```diff
diff --git a/src/CMake/nativeTests.cmake b/src/CMake/nativeTests.cmake
index 591797e5c..c05af8041 100644
--- a/src/CMake/nativeTests.cmake
+++ b/src/CMake/nativeTests.cmake
@@ -3,13 +3,17 @@
 # XRT_INSTALL_BIN_DIR
 
 enable_testing()
+if (${XRT_EDGE_BUILD} STREQUAL "no")
 add_test(NAME xbmgmt
   COMMAND ${CMAKE_BINARY_DIR}/runtime_src/core/pcie/tools/xbmgmt/xbmgmt scan
   WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR})
+endif()
 
+if (${XRT_EDGE_BUILD} STREQUAL "no")
 add_test(NAME xbutil
   COMMAND ${CMAKE_BINARY_DIR}/runtime_src/core/pcie/tools/xbutil/xbutil scan
   WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR})
+endif()
 
 add_test(NAME xbutil2
   COMMAND ${CMAKE_BINARY_DIR}/runtime_src/core/tools/xbutil2/xbutil2 examine
@@ -26,4 +30,6 @@ add_test(NAME python_binding
 set_tests_properties(python_binding PROPERTIES ENVIRONMENT
   "PYTHONPATH=.${CMAKE_INSTALL_PREFIX}/${XRT_INSTALL_DIR}/python;XILINX_XRT=.${CMAKE_INSTALL_PREFIX}/${XRT_INSTALL_DIR}")
 
-set_tests_properties(xbutil xbmgmt PROPERTIES ENVIRONMENT INTERNAL_BUILD=1)
+if (${XRT_EDGE_BUILD} STREQUAL "no")
+  set_tests_properties(xbutil xbmgmt PROPERTIES ENVIRONMENT INTERNAL_BUILD=1)
+endif()

```







## zocl ドライバオープン時の修正


Xilinx オリジナルでは、XRT が zocl(XRT Edge 用の Linux Kernel Module) をオープンする際に指定するファイル名を "/dev/dri/renderD128" に固定しています。このままでは、例えば[『Ultra96/Ultra96-V2 向け Debian GNU/Linux で Lima Driverを動かす』]で紹介したように、他の DRM ドライバと共存するのが難しくなります。そこで、zocl をオープンする際、/dev/dri をサーチして "zocl" を探すようにします。

具体的には、src/runtime_src/core/edge/user/zynq_dev.h と src/runtime_src/core/edge/zynq_dev.c を次のように修正して、/dev/dri から zocl を検索するようにします。


```diff_c:files/2021.1-xrt-edge-02.patch
diff --git a/src/runtime_src/core/edge/user/zynq_dev.h b/src/runtime_src/core/edge/user/zynq_dev.h
index 76ce580de..8d1e92b0c 100644
--- a/src/runtime_src/core/edge/user/zynq_dev.h
+++ b/src/runtime_src/core/edge/user/zynq_dev.h
@@ -49,15 +49,26 @@ public:
         const std::vector<char>& buf);
     std::string get_sysfs_path(const std::string& entry);
 
-    static zynq_device *get_dev();
-private:
+    static zynq_device *get_dev(unsigned index = 0);
+
     std::fstream sysfs_open(const std::string& entry, std::string& err,
         bool write = false, bool binary = false);
 
+    static zynq_device dummy_device;
+    static std::string dri_root;
+    static std::string drm_name;
+    static std::string render_prefix;
+
+    int         index;
+    std::string render_name;
+    std::string device_path;
     std::string sysfs_root;
-    zynq_device(const std::string& sysfs_base);
-    zynq_device(const zynq_device& s) = delete;
-    zynq_device& operator=(const zynq_device& s) = delete;
+
+    bool available(void)
+    {
+        return (index >= 0);
+    }
+    zynq_device(const std::string& render_name, int index);
 };
 
 #endif

diff --git a/src/runtime_src/core/edge/user/zynq_dev.cpp b/src/runtime_src/core/edge/user/zynq_dev.cpp
index b8fb89dee..8a554813b 100644
--- a/src/runtime_src/core/edge/user/zynq_dev.cpp
+++ b/src/runtime_src/core/edge/user/zynq_dev.cpp
@@ -20,6 +20,96 @@
 #include <cstring>
 #include "zynq_dev.h"
 
+#include <memory>
+#include <mutex>
+#include <fcntl.h>
+#include <dirent.h>
+#include <unistd.h>
+#include <sys/ioctl.h>
+#include <libdrm/drm.h>
+#include <libdrm/drm_mode.h>
+
+class zynq_device_scanner {
+  private:
+    std::mutex lock;
+    std::vector<zynq_device> device_list;
+
+    int  strncmp(const char* s1, const std::string& s2)
+    {
+        return std::strncmp(s1, s2.c_str(), s2.length());
+    }

+    void rescan_nolock(void)
+    {
+        DIR* dp = opendir(zynq_device::dri_root.c_str());
+        if (dp) {
+            int            index = 0;
+            struct dirent* entry;
+            drm_version    version;
+            const std::unique_ptr<char[]> name(new char[128]);
+            const std::unique_ptr<char[]> desc(new char[512]);
+            const std::unique_ptr<char[]> date(new char[128]);
+            std::memset(&version, 0, sizeof(version));
+            version.name     = name.get();
+            version.name_len = 128;
+            version.desc     = desc.get();
+            version.desc_len = 512;
+            version.date     = date.get();
+            version.date_len = 128;
+        
+            while ((entry = readdir(dp))) {
+                int fd;
+                std::string render_name(entry->d_name);
+                if (strncmp(entry->d_name, zynq_device::render_prefix) != 0) 
+                    continue;
+                std::string dev_path =  zynq_device::dri_root + "/" + render_name;
+                if ((fd = open(dev_path.c_str(), O_RDWR, 0)) < 0)
+                    continue;
+                if (ioctl(fd, DRM_IOCTL_VERSION, &version) == 0) {
+                    if (strncmp(version.name, zynq_device::drm_name) == 0) {
+                        device_list.insert(device_list.end(), zynq_device(render_name, index));
+                        index++;
+                    }
+                }
+                close(fd);
+            }
+            closedir(dp);
+        }
+    }
+

+  public:
+    static zynq_device_scanner *get()
+    {
+        static zynq_device_scanner scanner;
+        return &scanner;
+    }
+  
+    zynq_device* get_dev(unsigned index)
+    {
+        std::lock_guard<std::mutex> l(lock);
+        if (index < device_list.size())
+            return &device_list[index];
+        else
+            return &zynq_device::dummy_device;
+    }
+
+    void rescan(void)
+    {
+        std::lock_guard<std::mutex> l(lock);
+        rescan_nolock();
+    }
+
+    size_t device_num_total(void)
+    {
+        std::lock_guard<std::mutex> l(lock);
+        return device_list.size();
+    }
+
+  private:
+    zynq_device_scanner(void) { rescan_nolock();}
+    zynq_device_scanner(const zynq_device_scanner&  s) = delete;
+    zynq_device_scanner& operator=(const zynq_device_scanner& s) = delete;
+};

+
 static std::fstream sysfs_open_path(const std::string& path, std::string& err,
     bool write, bool binary)
 {
@@ -139,15 +229,20 @@ void zynq_device::sysfs_get(const std::string& entry, std::string& err_msg,
         s = ""; // default value
 }
 
-zynq_device *zynq_device::get_dev()
+zynq_device *zynq_device::get_dev(unsigned index)
 {
-    // This is based on the fact that on edge devices, we only have one DRM
-    // device, which is named renderD128.
-    // This path is reliable. It is the same for ARM32 and ARM64.
-    static zynq_device dev("/sys/class/drm/renderD128/device/");
-    return &dev;
+    return zynq_device_scanner::get()->get_dev(index);
 }
 
-zynq_device::zynq_device(const std::string& root) : sysfs_root(root)
+zynq_device::zynq_device(const std::string& render_name, int index)
+  : index(index)
+  , render_name(render_name)
+  , device_path(dri_root + "/" + render_name)
+  , sysfs_root("/sys/class/drm/" + render_name + "/device/")
 {
 }
+
+zynq_device zynq_device::dummy_device ("renderD128", -1);
+std::string zynq_device::dri_root     ("/dev/dri");
+std::string zynq_device::drm_name     ("zocl"    );
+std::string zynq_device::render_prefix("renderD" );

```


src/runtime_src/core/edge/user/device_linux.c を 次のように修正します。


```diff_c:files/2021.1-xrt-edge-02.patch(6)
diff --git a/src/runtime_src/core/edge/user/device_linux.cpp b/src/runtime_src/core/edge/user/device_linux.cpp
index 00e20a250..5fdd7c7d3 100644
--- a/src/runtime_src/core/edge/user/device_linux.cpp
+++ b/src/runtime_src/core/edge/user/device_linux.cpp
@@ -330,7 +330,6 @@ struct aie_reg_read
 #ifdef XRT_ENABLE_AIE
 #ifndef __AIESIM__
   const static std::string AIE_TAG = "aie_metadata";
-  const static std::string ZOCL_DEVICE = "/dev/dri/renderD128";
   const uint32_t major = 1;
   const uint32_t minor = 0;
   const uint32_t patch = 0;
@@ -355,9 +354,9 @@ struct aie_reg_read
                                                              % pt.get<uint32_t>("schema_version.minor")
                                                              % pt.get<uint32_t>("schema_version.patch")));
 
-  int mKernelFD = open(ZOCL_DEVICE.c_str(), O_RDWR);
+  int mKernelFD = open(dev->device_path.c_str(), O_RDWR);
   if (!mKernelFD)
-    throw xrt_core::error(-EINVAL, boost::str(boost::format("Cannot open %s") % ZOCL_DEVICE));
+    throw xrt_core::error(-EINVAL, boost::str(boost::format("Cannot open %s") % dev->device_path));
 
   XAie_DevInst* devInst;         // AIE Device Instance
 

```




src/runtime_src_core/edge/user/shim.cpp にあるクラス shim のコンストラクタを次のように修正して mDev インスタンス変数に zynq_device.get_dev() で得たデバイス情報をセットします。


```diff_c:files/2021.1-xrt-edge-02.patch(7)
 diff --git a/src/runtime_src/core/edge/user/shim.cpp b/src/runtime_src/core/edge/user/shim.cpp
index 89c720795..aaa2c8214 100644
--- a/src/runtime_src/core/edge/user/shim.cpp
+++ b/src/runtime_src/core/edge/user/shim.cpp
@@ -28,6 +28,7 @@
 #include "core/common/config_reader.h"
 #include "core/common/query_requests.h"
 #include "core/common/error.h"
+#include "shim_int.h"
 
 #include <cerrno>
 #include <iostream>
@@ -40,6 +41,7 @@
 #include <cstdarg>
 
 #include <fcntl.h>
+#include <dirent.h>
 #include <poll.h>
 #include <unistd.h>
 #include <sys/ioctl.h>
@@ -108,12 +110,17 @@ shim(unsigned index)
 {
   xclLog(XRT_INFO, "%s", __func__);
 
-  mKernelFD = open("/dev/dri/renderD128", O_RDWR);
-  if (mKernelFD < 0) {
-    xclLog(XRT_ERROR, "%s: Cannot open /dev/dri/renderD128", __func__);
+  mDev = zynq_device::get_dev(index);
+  if (mDev->available()) {
+    mKernelFD = open(mDev->device_path.c_str(), O_RDWR, 0);
+    if (mKernelFD < 0) {
+      xclLog(XRT_ERROR, "%s: Cannot open device %s", __func__, mDev->device_path.c_str());
+    }
+  } else {
+    mKernelFD = -1;
+    xclLog(XRT_ERROR, "%s: Card [%d] not found", __func__, index);
   }
   mCmdBOCache = std::make_unique<xrt_core::bo_cache>(this, xrt_core::config::get_cmdbo_cache());
-  mDev = zynq_device::get_dev();
 }
 

```




src/runtime_src_core/edge/user/shim.cpp にあるxclGetSysfsPath() と getIPCountAddNames() を次のように mDev インスタンス変数のデバイスクラスの get_sysfs_path() を呼び出すように変更します。


```diff_c:files/2021.1-xrt-edge-02.patch(8)
 #ifndef __HWEM__
@@ -688,7 +695,7 @@ std::string
 shim::
 xclGetSysfsPath(const std::string& entry)
 {
-  return zynq_device::get_dev()->get_sysfs_path(entry);
+  return mDev->get_sysfs_path(entry);
 }
 
 int
@@ -1349,9 +1356,8 @@ getIPCountAddrNames(int type,
                     size_t size)
 {
   debug_ip_layout *map;
-  auto dev = zynq_device::get_dev() ;
   std::string entry_str = "debug_ip_layout";
-  std::string path = dev->get_sysfs_path(entry_str);
+  std::string path = mDev->get_sysfs_path(entry_str);
   std::ifstream ifs(path.c_str(), std::ifstream::binary);
   uint32_t count = 0;
   char buffer[65536];

```




src/runtime_src_core/edge/user/shim.cpp にある xclProbe() と xclOpen() を次のように修正します。


```diff_c:files/2021.1-xrt-edge-02.patch(9)
@@ -1590,32 +1596,7 @@ unsigned
 xclProbe()
 {
   PROBE_CB;
-
-  int fd = open("/dev/dri/renderD128", O_RDWR);
-  if (fd < 0) {
-    return 0;
-  }
-  std::vector<char> name(128,0);
-  std::vector<char> desc(512,0);
-  std::vector<char> date(128,0);
-  drm_version version;
-  std::memset(&version, 0, sizeof(version));
-  version.name = name.data();
-  version.name_len = 128;
-  version.desc = desc.data();
-  version.desc_len = 512;
-  version.date = date.data();
-  version.date_len = 128;
-
-  int result = ioctl(fd, DRM_IOCTL_VERSION, &version);
-  if (result) {
-    close(fd);
-    return 0;
-  }
-
-  result = std::strncmp(version.name, "zocl", 4);
-  close(fd);
-  return (result == 0) ? 1 : 0;
+  return (zynq_device::get_dev()->available());
 }
 #endif
 @@ -1624,7 +1605,7 @@ xclOpen(unsigned deviceIndex, const char*, xclVerbosityLevel)
 {
   try {
     //std::cout << "xclOpen called" << std::endl;
-    if (deviceIndex >= xclProbe()) {
+    if (zynq_device::get_dev(deviceIndex)->available() == false) {
       xrt_core::message::send(xrt_core::message::severity_level::info, "XRT",
                        std::string("Cannot find index " + std::to_string(deviceIndex) + " \n"));
       return nullptr;
@@ -1652,6 +1633,22 @@ xclOpen(unsigned deviceIndex, const char*, xclVerbosityLevel)
 
 }

```



## Ubuntu 20.04 の static link 時の修正


Ubuntu 20.04 用にビルドする際は src/CMake/nativeLnx.cmake で次のように XRT_STATIC_BUILD が "on" に設定されます。


```Makefile:src/CMake/nativeLnx.cmake
# Static linking creates and installs static tools and libraries. The
# static libraries have system boost dependencies which must be
# resolved in final target.  The tools (currently xbutil2 and xbmgmt2)
# will be statically linked.  Enabled only for ubuntu.
option(XRT_STATIC_BUILD "Enable static building of XRT" OFF)
if ( (${CMAKE_VERSION} VERSION_GREATER "3.16.0")
    AND (${XRT_NATIVE_BUILD} STREQUAL "yes")
    AND (${LINUX_FLAVOR} MATCHES "^(Ubuntu)")
    )
  message("-- Enabling static artifacts of XRT")
  set(XRT_STATIC_BUILD ON)
endif()

```




XRT_STATIC_BUILD が "on" の場合、xbmgmt2 と xbutil2 の static link バージョンも同時にビルドしますが、その際、リンク時にシンボルが見つからないというエラーが発生します。これは src/runtime_src/core/edge/user/shim.c の関数定義が間違っているためです。そこで次のように src/runtime_src/core/edge/user/shim.c を修正します。


```diff_c:files/2021.1-xrt-edge-02.patch
 +xclDeviceHandle
+xclOpenByBDF(const char *bdf)
+{
+  try {
+    return xclOpen(0, nullptr, XCL_QUIET);
+  }
+  catch (const xrt_core::error& ex) {
+    xrt_core::send_exception_message(ex.what());
+  }
+  catch (const std::exception& ex) {
+    xrt_core::send_exception_message(ex.what());
+  }
+
+  return nullptr;
+}
+

  void
 xclClose(xclDeviceHandle handle)
 {
@@ -2269,49 +2266,82 @@ xclRegisterInterruptNotify(xclDeviceHandle handle, unsigned int userInterrupt, i
 }
 
 int
-xclCreateWriteQueue(xclDeviceHandle handle, xclQueueContext *q_ctx, void **q_hdl)
+xclCreateWriteQueue(xclDeviceHandle handle, xclQueueContext *q_ctx, uint64_t *q_hdl)
 {
   return -ENOSYS;
 }
 
 int
-xclCreateReadQueue(xclDeviceHandle handle, xclQueueContext *q_ctx, void **q_hdl)
+xclCreateReadQueue(xclDeviceHandle handle, xclQueueContext *q_ctx, uint64_t *q_hdl)
 {
   return -ENOSYS;
 }
 
 int
-xclDestroyQueue(xclDeviceHandle handle, void *q_hdl)
+xclDestroyQueue(xclDeviceHandle handle, uint64_t q_hdl)
 {
   return -ENOSYS;
 }
 
 int
-xclModifyQueue(xclDeviceHandle handle, void *q_hdl)
+xclModifyQueue(xclDeviceHandle handle, uint64_t q_hdl)
 {
   return -ENOSYS;
 }
 
 int
-xclStartQueue(xclDeviceHandle handle, void *q_hdl)
+xclStartQueue(xclDeviceHandle handle, uint64_t q_hdl)
 {
   return -ENOSYS;
 }
 
 int
-xclStopQueue(xclDeviceHandle handle, void *q_hdl)
+xclStopQueue(xclDeviceHandle handle, uint64_t q_hdl)
 {
   return -ENOSYS;
 }

 ssize_t
-xclWriteQueue(xclDeviceHandle handle, void *q_hdl, xclQueueRequest *wr_req)
+xclWriteQueue(xclDeviceHandle handle, uint64_t q_hdl, struct xclQueueRequest *wr_req)
 {
   return -ENOSYS;
 }
 
 ssize_t
-xclReadQueue(xclDeviceHandle handle, void *q_hdl, xclQueueRequest *wr_req)
+xclReadQueue(xclDeviceHandle handle, uint64_t q_hdl, struct xclQueueRequest *rd_req)
+{
+  return -ENOSYS;
+}
+
+int
+xclPollQueue(xclDeviceHandle handle, uint64_t q_hdl, int min_compl,
+		   int max_compl, struct xclReqCompletion *comps,
+		   int* actual_compl, int timeout)
+{
+  return -ENOSYS;
+}
+
+int
+xclSetQueueOpt(xclDeviceHandle handle, uint64_t q_hdl, int type, uint32_t val)
+{
+  return -ENOSYS;
+}
+  
+int
+xclPollCompletion(xclDeviceHandle handle, int min_compl, int max_compl,
+                  struct xclReqCompletion *comps, int* actual_compl, int timeout)
+{
+  return -ENOSYS;
+}
+
+void*
+xclAllocQDMABuf(xclDeviceHandle handle, size_t size, uint64_t *buf_hdl)
+{
+  return NULL;
+}
+
+int
+xclFreeQDMABuf(xclDeviceHandle handle, uint64_t buf_hdl)
 {
   return -ENOSYS;
 }

```





# 参考


* https://github.com/Xilinx/XRT
* https://github.com/ikwzm/ZynqMP-FPGA-XRT
* https://github.com/ikwzm/ZynqMP-FPGA-Linux
* https://github.com/ikwzm/ZynqMP-FPGA-Ubuntu20.04
* [『UltraZed/Ultra96/Ultra96-V2/KV260 向け Debian GNU/Linux (v2021.1版) ブートイメージの提供』@Qiita]
* [『Ultra96/Ultra96-V2 向け Debian GNU/Linux で Lima Driverを動かす』@Qiita]


[1]: https://github.com/ikwzm/ZynqMP-FPGA-Linux "https://github.com/ikwzm/ZynqMP-FPGA-Linux"
[2]: https://github.com/ikwzm/ZynqMP-FPGA-Ubuntu20.04 "https://github.com/ikwzm/ZynqMP-FPGA-Ubuntu20.04"
[3]: https://github.com/Xilinx/XRT "https://github.com/Xilinx/XRT"
[ZynqMP-FPGA-Linux]: https://github.com/ikwzm/ZynqMP-FPGA-Linux "https://github.com/ikwzm/ZynqMP-FPGA-Linux"
[ZynqMP-FPGA-Ubuntu20.04]: https://github.com/ikwzm/ZynqMP-FPGA-Ubuntu20.04 "https://github.com/ikwzm/ZynqMP-FPGA-Ubuntu20.04"
[XRT]: https://github.com/Xilinx/XRT "https://github.com/Xilinx/XRT"
[2021.1_EDGE_0\_Debian_11]: https://github.com/ikwzm/ZynqMP-FPGA-XRT/tree/2021.1_EDGE_0_Debian_11
[2021.1_EDGE_0\_Ubuntu_20.04]: https://github.com/ikwzm/ZynqMP-FPGA-XRT/tree/2021.1_EDGE_0_Ubuntu_20.04
[2020.2_EDGE_1\_Debian_10]: https://github.com/ikwzm/ZynqMP-FPGA-XRT/tree/2020.2_EDGE_1_Debian_10
[2020.2_EDGE_1\_Ubuntu_20.04]: https://github.com/ikwzm/ZynqMP-FPGA-XRT/tree/2020.2_EDGE_1_Ubuntu_20.04
[2019.2_Ultra96_3]: https://github.com/ikwzm/ZynqMP-FPGA-XRT/tree/2019.2_Ultra96_3
[概要編]: https://qiita.com/ikwzm/items/0d3a8adfe725cd17f932 "『Ultra96/Ultra96-V2/KV260 向け Debian GNU/Linux および Ubuntu20.04 で XRT(Xilinx Runtime) を動かす(概要編)』@Qiita"
[XRT 2.11.0 ビルド編]: https://qiita.com/ikwzm/items/ad4ef8a3fe085c80cf32 "『Ultra96/Ultra96-V2/KV260 向け Debian GNU/Linux および Ubuntu20.04で XRT(Xilinx Runtime) を動かす(2.11.0 ビルド編)』@Qiita"
[XRT 2.8.1 ビルド編]: https://qiita.com/ikwzm/items/765dc4046746d35ef45c "『Ultra96/Ultra96-V2 向け Debian GNU/Linux で XRT(Xilinx Runtime) を動かす(2.8.1 ビルド編)』@Qiita"
[XRT 2.6.0 ビルド編]: https://qiita.com/ikwzm/items/d8013ea91125a3fc46aa "『Ultra96/Ultra96-V2 向け Debian GNU/Linux で XRT(Xilinx Runtime) を動かす(2.6.0 ビルド編)』@Qiita"
[XRT 2.11.0 インストール編]: https://qiita.com/ikwzm/items/edefadb6f9da45e72301 "『Ultra96/Ultra96-V2/KV260 向け Debian GNU/Linux および Ubuntu20.04 で XRT(Xilinx Runtime) を動かす(2.11.0 インストール編)』@Qiita"
[XRT 2.6.0 インストール編]: https://qiita.com/ikwzm/items/6612eacc4a0d324a0a68 "『Ultra96/Ultra96-V2 向け Debian GNU/Linux で XRT(Xilinx Runtime) を動かす(2.6.0 インストール編)』@Qiita"
[実践編]: https://qiita.com/ikwzm/items/58cfea3f9d0f42840511 "『Ultra96/Ultra96-V2 向け Debian GNU/Linux で XRT(Xilinx Runtime) を動かす(実践編)』@Qiita"
[Vitis編]: https://qiita.com/ikwzm/items/e70d8d0e44fe3784a708 "『Ultra96/Ultra96-V2 向け Debian GNU/Linux で XRT(Xilinx Runtime) を動かす(Vitis編)』@Qiita"
[『UltraZed/Ultra96/Ultra96-V2/KV260 向け Debian GNU/Linux (v2021.1版) ブートイメージの提供』@Qiita]: https://qiita.com/ikwzm/items/a9adc5a7329b2eb36895 "『UltraZed/Ultra96/Ultra96-V2/KV260 向け Debian GNU/Linux (v2021.1版) ブートイメージの提供』@Qiita"
[『UltraZed/Ultra96/Ultra96-V2 向け Debian GNU/Linux (v2020.2版) ブートイメージの提供』@Qiita]: https://qiita.com/ikwzm/items/c7687406e82ab95ac697 "『UltraZed/Ultra96/Ultra96-V2 向け Debian GNU/Linux (v2020.2版) ブートイメージの提供』@Qiita"
[『UltraZed/Ultra96/Ultra96-V2 向け Debian GNU/Linux (v2019.2版) ブートイメージの提供』@Qiita]: https://qiita.com/ikwzm/items/92221c5ea6abbd5e991c "『UltraZed/Ultra96/Ultra96-V2 向け Debian GNU/Linux (v2019.2版) ブートイメージの提供』@Qiita"
[『Ultra96/Ultra96-V2 向け Debian GNU/Linux で Lima Driverを動かす』@Qiita]: https://qiita.com/ikwzm/items/6481729ae63b0ec316e5 "『Ultra96/Ultra96-V2 向け Debian GNU/Linux で Lima Driverを動かす』@Qiita"