Repository: Xilinx/PYNQ (language: python)

Issue:
XRT Device fails with newer XRT versions
* PYNQ version: 2.7.0 (pip installed)
* Board name: Alveo (any)

* Description: XRT version identification fails with newer XRT versions. This is because xrt_device.py at line 88 tries to do `xbutil dump` to get the version, but that command no longer exists. 

* Suggested fix: The function should run `xbutil --version` instead (or as a backup if `xbutil dump` fails), and parse the first line of output e.g. `output.stdout.splitlines()[0].split(": ")[1].split(".")`




Discussion / hints:

Fix patch:
diff --git a/pynq/pl_server/xrt_device.py b/pynq/pl_server/xrt_device.py
index 123fca6733..a1f6fbbaf8 100644
--- a/pynq/pl_server/xrt_device.py
+++ b/pynq/pl_server/xrt_device.py
@@ -79,24 +79,32 @@ class xclDeviceUsage (ctypes.Structure):
 }
 
 
-def _get_xrt_version():
-    import subprocess
+def _get_xrt_version_embedded(path='/sys/module/zocl'):
+    try:
+        with open(path + '/version', 'r') as f:
+            details = f.readline().replace('\n','')
+        return tuple(
+            int(s) for s in details.split('.'))
+    except Exception:
+        warnings.warn('Unable to determine XRT version')
+        return (0, 0, 0)
+
+
+def _get_xrt_version_x86():
     import json
     try:
-        output = subprocess.run(['xbutil', 'dump'], stdout=subprocess.PIPE,
-                                universal_newlines=True)
-        if output.returncode != 0:
-            warnings.warn(
-                    'xbutil failed to run - unable to determine XRT version')
-        details = json.loads(output.stdout)
+        with open(os.environ['XILINX_XRT'] + '/version.json', 'r') as f:
+            details = json.loads(f.read())
         return tuple(
-            int(s) for s in details['runtime']['build']['version'].split('.'))
+            int(s) for s in details['BUILD_VERSION'].split('.'))
     except Exception:
+        warnings.warn('Unable to determine XRT version')
         return (0, 0, 0)
 
 
 if xrt.XRT_SUPPORTED:
-    _xrt_version = _get_xrt_version()
+    _xrt_version = _get_xrt_version_x86() if CPU_ARCH_IS_x86 \
+        else _get_xrt_version_embedded()
 else:
     _xrt_version = (0, 0, 0)
 


Test patch:
diff --git a/tests/test_xrt.py b/tests/test_xrt.py
index c28b15297f..244651e148 100644
--- a/tests/test_xrt.py
+++ b/tests/test_xrt.py
@@ -1,8 +1,13 @@
+# Copyright (C) 2022 Xilinx, Inc
+
+# SPDX-License-Identifier: BSD-3-Clause
+
 import importlib
-import pynq._3rdparty.xrt
-import pytest
 import ctypes
 import os
+import pathlib
+import pynq._3rdparty.xrt
+import pytest
 
 class FakeXrt:
     def __init__(self, path):
@@ -63,48 +68,46 @@ def test_xrt_normal(monkeypatch, recwarn):
     assert len(recwarn) == 0
 
 
-def test_xrt_version(monkeypatch, tmp_path):
-    with open(tmp_path / 'xbutil', 'w') as f:
-        f.write("""#!/bin/bash
-echo '{"runtime": {"build": {"version": "2.5.3"}}}'
-""")
-    os.chmod(tmp_path / 'xbutil', 0o777)
-    monkeypatch.setenv('PATH', str(tmp_path) + ':' + os.environ['PATH'])
-    monkeypatch.setenv('XILINX_XRT', '/path/to/xrt')
+def test_xrt_version_x86(monkeypatch, tmp_path):
+    monkeypatch.setenv('XILINX_XRT', str(tmp_path))
+    with open(tmp_path / 'version.json', 'w') as f:
+        f.write("""{\n  "BUILD_VERSION" : "2.12.447"\n}\n\n""")
     monkeypatch.delenv('XCL_EMULATION_MODE', raising=False)
     monkeypatch.setattr(ctypes, 'CDLL', FakeXrt)
     import pynq.pl_server.xrt_device
     xrt = importlib.reload(pynq._3rdparty.xrt)
     xrt_device = importlib.reload(pynq.pl_server.xrt_device)
-    assert xrt_device._xrt_version == (2, 5, 3)
+    assert xrt_device._get_xrt_version_x86() == (2, 12, 447)
 
 
-def test_xrt_version_fail(monkeypatch, tmp_path):
-    with open(tmp_path / 'xbutil', 'w') as f:
-        f.write("""#!/bin/bash
-exit 1
-""")
-    os.chmod(tmp_path / 'xbutil', 0o777)
-    monkeypatch.setenv('PATH', str(tmp_path) + ':' + os.environ['PATH'])
-    monkeypatch.setenv('XILINX_XRT', '/path/to/xrt')
+def test_xrt_version_embedded(monkeypatch, tmp_path):
+    with open(tmp_path / 'version', 'w') as f:
+        f.write("""2.12.447\n""")
     monkeypatch.delenv('XCL_EMULATION_MODE', raising=False)
     monkeypatch.setattr(ctypes, 'CDLL', FakeXrt)
     import pynq.pl_server.xrt_device
-    with pytest.warns(UserWarning, match='xbutil failed to run'):
+    xrt = importlib.reload(pynq._3rdparty.xrt)
+    xrt_device = importlib.reload(pynq.pl_server.xrt_device)
+    assert xrt_device._get_xrt_version_embedded(str(tmp_path)) == (2, 12, 447)
+
+
+def test_xrt_version_fail_x86(monkeypatch, tmp_path):
+    monkeypatch.setenv('XILINX_XRT', str(tmp_path))
+    import pynq.pl_server.xrt_device
+    monkeypatch.delenv('XCL_EMULATION_MODE', raising=False)
+    monkeypatch.setattr(ctypes, 'CDLL', FakeXrt)
+    with pytest.warns(UserWarning, match='Unable to determine XRT version'):
         xrt = importlib.reload(pynq._3rdparty.xrt)
         xrt_device = importlib.reload(pynq.pl_server.xrt_device)
     assert xrt_device._xrt_version == (0, 0, 0)
 
 
 def test_xrt_version_unsupported(monkeypatch, tmp_path):
-    with open(tmp_path / 'xbutil', 'w') as f:
-        f.write("""#!/bin/bash
-echo '{"runtime": {"build": {"version": "2.5.3"}}}'
-""")
-    os.chmod(tmp_path / 'xbutil', 0o777)
+    monkeypatch.setenv('XILINX_XRT', str(tmp_path))
+    with open(tmp_path / 'version.json', 'w') as f:
+        f.write("""{\n  "BUILD_VERSION" : "2.12.447"\n}\n\n""")
     monkeypatch.setenv('PATH', str(tmp_path) + ':' + os.environ['PATH'])
     monkeypatch.delenv('XILINX_XRT', raising=False)
-    monkeypatch.setattr(ctypes, 'CDLL', FakeXrt)
     import pynq.pl_server.xrt_device
     xrt = importlib.reload(pynq._3rdparty.xrt)
     xrt_device = importlib.reload(pynq.pl_server.xrt_device)