DPU-PYNQ custom ZU+ board porting

Hello Everybody,

I am fairly new to Xilinx tools and PYNQ and I am working on porting DPU-PYNQ 1.4.0 to a custom board with a ZU+ XCZU11EG series SoC FPGA.

PYNQ image build for the custom board

I managed to build the PYNQ 2.7.0 image for the custom board using an Ubuntu 18.04.6 VM set up with a modified version of the Vagrant file provided in the git repo to give it more space. I used Xilinx 2020.2 tools and applied the Y2k22_patch and built the image from a BSP for the board.

The board boots up and I am able to access the Jupyter Notebooks server, no problem there.

DPU-PYNQ test build for ZCU104

Before attempting the porting to the custom board, I rebuilt the DPU-PYNQ hardware for a ZCU104 board following this tutorial on the VM used for the PYNQ image build. Since DPU-PYNQ requires Vitis 2021.1 I modified the Make files to work with my Vitis 2020.2.

The test build for the ZCU104 worked fine and I was able to deploy it by copying the DPU-PYNQ folder from the VM to /home/xilinx/DPU-PYNQ and running pip3 install /home/xilinx/DPU-PYNQ --no-build-isolation in a terminal opened in the Jupyter Notebook server. I fetched the notebooks by executing

cd $PYNQ_JUPYTER_NOTEBOOKS
pynq get-notebooks pynq-dpu -p .

The examples ran just fine on the ZCU104.


Porting to the custom board

Following this post about How to run DPU examples custom platform I created the necessary files for the custom board using the commits on the Github repo linked under the post. I added my board’s name to the supported boards and I added the configuration options based on the board name to the Make files. For the files I used the exact same ones as in the git repo.

I ran the make BOARD=<board> command with my custom board which finished without a problem. In the git repo there were 2 bash scripts that I used to copy the necessary files from the host to the target device (deploy2board.sh) and to move the .bit, .hwh, .tcl and .xclbin files to the correct folder for the dpu.py file (deploy.sh) on the target device after running pip3 install /home/xilinx/DPU-PYNQ --no-build-isolation in a terminal opened in the Jupyter Notebook server as before.

As in the post mentioned above, the pynq get-notebooks pynq-dpu -p . command didn’t work for me either on the custom board so I copied the notebooks folder from /usr/local/share/pynq-venv/lib/python3.8/site-packages/pynq_dpu to $PYNQ_JUPYTER_NOTEBOOKS and renamed it to dpu-pynq.

Running the examples

I tried running the dpu_resnet50.ipynb notebook which failed with a RuntimeError: Invalid length found (details of the error below). I tried moving the files for the dpu overlay to the default location of Overlays in PYNQ and calling DpuOverlay('/home/xilinx/pynq/overlays/dpu/dpu.bit') instead which produced the ParseError. The same errors occur for the other notebooks as well.


Does anyone have an idea what could be the cause of these errors?


I would have expected the same errors for both instances. I suspect that there is an issue with the dpu.bit file generated for the custom board. I edited the source code for embedded_device.py to get the mismatched values and I found length = 23580908, offset = 117, len(contents) = 16777216 on ln 92 in embedded_device.py before the RuntimeError is triggered.

I am thinking that the dpu.bit file is not valid due to incorrect configuration of the DPU (?). However, I don’t know how to identify any configuration issues (I thought any configuration issue would break the build in the first place).

Any help would be highly appreciated on the matter.

Thank you, Mario


Details of the RuntimeError

---------------------------------------------------------------------------
RuntimeError                              Traceback (most recent call last)
<ipython-input-10-7434525267f0> in <module>
      1 from pynq_dpu import DpuOverlay
      2 test_path = '/home/xilinx/pynq/overlays/dpu/dpu.bit'
----> 3 overlay = DpuOverlay("dpu.bit")
      4 # overlay = DpuOverlay(test_path)

/usr/local/share/pynq-venv/lib/python3.8/site-packages/pynq_dpu/dpu.py in __init__(self, bitfile_name, dtbo, download, ignore_version, device)
     90         else:
     91             raise FileNotFoundError('Cannot find {}.'.format(bitfile_name))
---> 92         super().__init__(abs_bitfile_name,
     93                          dtbo=dtbo,
     94                          download=download,

/usr/local/share/pynq-venv/lib/python3.8/site-packages/pynq/overlay.py in __init__(self, bitfile_name, dtbo, download, ignore_version, device)
    338         self._register_drivers()
    339 
--> 340         self.parser = self.device.get_bitfile_metadata(self.bitfile_name)
    341 
    342         self.ip_dict = self.gpio_dict = self.interrupt_controllers = \

/usr/local/share/pynq-venv/lib/python3.8/site-packages/pynq/pl_server/embedded_device.py in get_bitfile_metadata(self, bitfile_name)
    602 
    603     def get_bitfile_metadata(self, bitfile_name):
--> 604         parser = _get_bitstream_handler(bitfile_name).get_parser()
    605         if parser is None:
    606             raise RuntimeError("Unable to find metadata for bitstream")

/usr/local/share/pynq-venv/lib/python3.8/site-packages/pynq/pl_server/embedded_device.py in get_parser(self)
    197         else:
    198             return None
--> 199         parser.bin_data = self.get_bin_data()
    200         parser.xclbin_data = xclbin_data
    201         parser.dtbo_data = self.get_dtbo_data()

/usr/local/share/pynq-venv/lib/python3.8/site-packages/pynq/pl_server/embedded_device.py in get_bin_data(self)
    206     def get_bin_data(self):
    207         bit_data = self._filepath.read_bytes()
--> 208         return bit2bin(bit_data)
    209 
    210 

/usr/local/share/pynq-venv/lib/python3.8/site-packages/pynq/pl_server/embedded_device.py in bit2bin(bit_data)
    102 
    103     """
--> 104     bit_dict = parse_bit_header(bit_data)
    105     bit_buffer = np.frombuffer(bit_dict['data'], 'i4')
    106     bin_buffer = bit_buffer.byteswap()

/usr/local/share/pynq-venv/lib/python3.8/site-packages/pynq/pl_server/embedded_device.py in parse_bit_header(bit_data)
     91             bit_dict['length'] = str(length)
     92             if length + offset != len(contents):
---> 93                 raise RuntimeError("Invalid length found")
     94             bit_dict['data'] = contents[offset:offset + length]
     95         else:

RuntimeError: Invalid length found

Details of the ParseError

Traceback (most recent call last):

  File "/usr/local/share/pynq-venv/lib/python3.8/site-packages/IPython/core/interactiveshell.py", line 3441, in run_code
    exec(code_obj, self.user_global_ns, self.user_ns)

  File "<ipython-input-1-fbf35c2459f5>", line 4, in <module>
    overlay = DpuOverlay(test_path)

  File "/usr/local/share/pynq-venv/lib/python3.8/site-packages/pynq_dpu/dpu.py", line 92, in __init__
    super().__init__(abs_bitfile_name,

  File "/usr/local/share/pynq-venv/lib/python3.8/site-packages/pynq/overlay.py", line 340, in __init__
    self.parser = self.device.get_bitfile_metadata(self.bitfile_name)

  File "/usr/local/share/pynq-venv/lib/python3.8/site-packages/pynq/pl_server/embedded_device.py", line 605, in get_bitfile_metadata
    parser = _get_bitstream_handler(bitfile_name).get_parser()

  File "/usr/local/share/pynq-venv/lib/python3.8/site-packages/pynq/pl_server/embedded_device.py", line 194, in get_parser
    xclbin_parser = XclBin(xclbin_data=xclbin_data)

  File "/usr/local/share/pynq-venv/lib/python3.8/site-packages/pynq/pl_server/xclbin_parser.py", line 371, in __init__
    _xclbin_to_dicts(filename, xclbin_data)

  File "/usr/local/share/pynq-venv/lib/python3.8/site-packages/pynq/pl_server/xclbin_parser.py", line 282, in _xclbin_to_dicts
    ip_dict = _xclxml_to_ip_dict(

  File "/usr/local/share/pynq-venv/lib/python3.8/site-packages/pynq/pl_server/xclbin_parser.py", line 56, in _xclxml_to_ip_dict
    xml = ElementTree.fromstring(raw_xml)

  File "/usr/lib/python3.8/xml/etree/ElementTree.py", line 1321, in XML
    return parser.close()

  File "<string>", line unknown
ParseError: no element found: line 1, column 0

1 Like

Hi,

What is the output after running pip3 install /home/xilinx/DPU-PYNQ --no-build-isolation ?

1 Like

Hi @haipnh,

Thank you for your question. When running pip3 install /home/xilinx/DPU-PYNQ --no-build-isolation on a clean PYNQ 2.7.0 image the output is:

root@pynq:/# pip3 install /home/xilinx/DPU-PYNQ --no-build-isolation
Processing /home/xilinx/DPU-PYNQ
  DEPRECATION: A future pip version will change local packages to be built in-place without first copying to a temporary directory. We recommend you use --use-feature=in-tree-build to test your packages with this new behavior before it becomes the default.
   pip 21.3 will remove support for this functionality. You can find discussion regarding this at https://github.com/pypa/pip/issues/7555.
    Preparing wheel metadata ... done
Collecting mnist
  Downloading mnist-0.2.2-py2.py3-none-any.whl (3.5 kB)
Requirement already satisfied: pybind11 in /usr/local/share/pynq-venv/lib/python3.8/site-packages (from pynq-dpu==1.4.0) (2.8.0)
Requirement already satisfied: CppHeaderParser in /usr/local/share/pynq-venv/lib/python3.8/site-packages (from pynq-dpu==1.4.0) (2.7.4)
Requirement already satisfied: pynq>=2.7.0 in /usr/local/share/pynq-venv/lib/python3.8/site-packages (from pynq-dpu==1.4.0) (2.7.0)
Requirement already satisfied: numpy in /usr/local/share/pynq-venv/lib/python3.8/site-packages (from pynq>=2.7.0->pynq-dpu==1.4.0) (1.20.3)
Requirement already satisfied: pandas in /usr/local/share/pynq-venv/lib/python3.8/site-packages (from pynq>=2.7.0->pynq-dpu==1.4.0) (1.3.3)
Requirement already satisfied: setuptools>=24.2.0 in /usr/local/share/pynq-venv/lib/python3.8/site-packages (from pynq>=2.7.0->pynq-dpu==1.4.0) (44.0.0)
Requirement already satisfied: cffi in /usr/local/share/pynq-venv/lib/python3.8/site-packages (from pynq>=2.7.0->pynq-dpu==1.4.0) (1.14.5)
Requirement already satisfied: pycparser in /usr/lib/python3/dist-packages (from cffi->pynq>=2.7.0->pynq-dpu==1.4.0) (2.19)
Requirement already satisfied: ply in /usr/lib/python3/dist-packages (from CppHeaderParser->pynq-dpu==1.4.0) (3.11)
Requirement already satisfied: python-dateutil>=2.7.3 in /usr/lib/python3/dist-packages (from pandas->pynq>=2.7.0->pynq-dpu==1.4.0) (2.7.3)
Requirement already satisfied: pytz>=2017.3 in /usr/lib/python3/dist-packages (from pandas->pynq>=2.7.0->pynq-dpu==1.4.0) (2019.3)
Building wheels for collected packages: pynq-dpu
  Building wheel for pynq-dpu (PEP 517) ... done
  Created wheel for pynq-dpu: filename=pynq_dpu-1.4.0-cp38-cp38-linux_aarch64.whlsize=1262038 sha256=683171942348c94a2b34f4da23035cd1457e96805a9722781de65e8c26596295
  Stored in directory: /root/.cache/pip/wheels/43/7b/cc/83d68b760e5e159fd5c698d8e6828a62de84dc1d7d8194560a
Successfully built pynq-dpu
Installing collected packages: mnist, pynq-dpu
Successfully installed mnist-0.2.2 pynq-dpu-1.4.0
WARNING: You are using pip version 21.2.1; however, version 22.2.2 is available.
You should consider upgrading via the '/usr/local/share/pynq-venv/bin/python3 -m pip install --upgrade pip' command.

I am not sure it is relevant, on the first boot of a clean image I am usually not able to access the Jupyter Notebooks server even though I get a response when running ping <ip address> on my Windows 10 Pro host machine. When I reboot the board I can access the server no problem.

UPDATE

I repeated the same steps as I described above with the main difference that I had a colleague standing next to me as I repeated the steps. This time I was able to run the example notebooks without a problem using the .xmodel files provided for the ZCU104 board.


I don’t think any of the following factors should have made a difference, but I thought I would include them for reference.

  • The custom board was unplugged overnight which may have reset it completely. It is a board in development, but it is in a stable state so I don’t think this should matter.
  • Instead of running the pynq get-notebooks pynq-dpu -p . command to fetch the notebooks I manually copyed them in the right location through Samba, the file sharing service running on the board.

I would be still interested in knowing what could have caused the errors as I spent way too much time trying to resolve them and it got personal. :smiley:

Thank you, Mario

Good that you resovled this, and thanks for posting back to report this.

At a guess the bit file may not have been copied properly initially:
test_path = '/home/xilinx/pynq/overlays/dpu/dpu.bit'

You do not get a file not found, but when the file was parsed, the contents were not what the parser expects. Perhaps copying the files again resolved this.

CAthal

1 Like

Hi Cathal,

Thank you, that would make sense, although I used the same procedure for copying in both cases (same bash script). I got the RuntimeError with the default call to 'dpu.bit' and the ParseError to the absolute path (test_path) call.

I will try to reproduce the error and come back if I find the source of it, but I don’t think there will be much to find other than a faulty copy operation.

Thank you, Mario