2
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

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

Last updated at Posted at 2022-03-08

はじめに

筆者は Ultra96/Ultra96-V2/KV260(ZynqMP) 向けに Debian GNU/Linux1 と Ubuntu20.042 を提供しています。また、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 を Ultra96/Ultra96-V2/KV260 向け Debian GNU/Linux および Ubuntu20.04用にビルドする手順について説明します。

注意

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

XRT Debian Package の提供

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

Debian 11 用

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 用

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 で公開しています。

XRT のダウンロード

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

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

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 がインストールされます。

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 の環境だと、並列処理するコア数がデフォルトの4のままだと、途中で make コマンドがエラー終了しました。そこでコア数を1にするために "-j 1" オプションをつけています。Ultra96/Ultra96-V2 だとけっこう時間がかかります。私の環境では1時間20分ほどかかりました。

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 が出来ています。

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を想定しています。そこでこのスクリプトを次のように修正します。

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 が定義されています。

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 には、次のようにホスト用のドライバをビルド&インストールするようになっています。

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 変数を追加します。

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 用のビルドを行うように、それぞれのファイルを変更します。

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()

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 にあります。

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 を修正します。

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" になるように修正します。

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 --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 --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 を検索するようにします。

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 を 次のように修正します。

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() で得たデバイス情報をセットします。

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() を呼び出すように変更します。

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() を次のように修正します。

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" に設定されます。

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 を修正します。

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;
 }

参考

2
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?