Deploying PYNQ and Jupyter with Petalinux

petalinux_blog_header

The PYNQ Ubuntu-based Linux system is designed for development but we have been getting an increasing number of questions about how PYNQ can be used in a more traditional embedded context where an 8 GB filesystem is not practical. At its heart PYNQ is a Python library with a set of drivers and overlays that do not need a fully-fledge desktop Linux system to run. To this end we have been working to include PYNQ as part of Petalinux with the 2019.2 release now including PYNQ version 2.4.

In this post I’ll run through creating a new Petalinux project targeting the ZCU104, configuring it to work with PYNQ and finally adding a design that uses PYNQ as a new recipe. For this walkthrough I will assume that you already have Petalinux installed and on the PATH but no additional tools will be required.

Creating the initial image

Fortunately the ZCU104 already has a publically available BSP available for download. With this file we can start by creating the reference project

> petalinux-create -t project -s xilinx-zcu104-v2019.2-final.bsp

Once the project is created we can enter the project directory to continue setting it up for PYNQ

> cd xilinx-zcu104-2019.2

One change we need to make to the project configuration is enabling the FPGA manager. This will prevent the default bitstream from being loaded at boot and loading all of the Linux kernel drivers for the pre-packaged design. Without this option reprogramming the bitstream after boot has the potential to crash the kernel as devices that Linux expects to see are no longer in the prgorammable logic.

To do this we us the petalinux-config command and look for the desired option.

> petalinux-config

set_fpga_manager

We also need to add the required kernel driver (CONFIG_XILINX_APF) into the kernel. Again we can use the petalinux-config tool to perform the configuration

> petalinux-config -c kernel

set_apf

Creating a recipe for PYNQ-Helloworld

Next we need to create a recipe for the design we want to add. I’m going use the PYNQ-HelloWorld example as it supports the ZCU104 and is the recommended starting point for other PYNQ projects providing an image-resizer hardware design and accompanying Python code.

Petalinux is based on the Yocto Project which is a system for creating embedded Linux distributions. It uses the bitbake build system for building recipes. Recipes are organised into layers. You can see that the Petalinux project that has been created contains two layers in the project_spec folder – namely meta-user where we should put new recipes and meta-plnx-generated which is where Petalinux will place any recipes it generates as part of its configuration process.

To create our recipe first we need to create a new directory to contain all of the files we need – project-spec/meta-user/recipes-apps seems tailor made for this.

> mkdir project-spec/meta-user/recipes-apps/python3-pynq-helloworld
> cd project-spec/meta-user/recipes-apps/python3-pynq-helloworld

The name of project is import as having the python3- prefix will help the build system use the correct version of Python when building the package.

Next we need a .bb file which contains the main recipe for the package. This should be named in accordance with the folder we’ve just created.

> vim python3-pynq-helloworld.bb

A recipe contains several sections that required. First we need to specify the license of the package and where the license is specified in the tarball along with the user-visible summary of the package.

SUMMARY = "PYNQ Helloworld"
LICENSE = "BSD"

LIC_FILES_CHKSUM = "file://LICENSE;md5=f9990fcc34ccf1f82ccf1bc5a1cc3bfc"

Next we have where the package can be obtained. In this case we are going to pull directly from the Xilinx github repository. We also need to tell bitbake where the root of the project is in the S variable. For anything fetched with git this will be the git subdirectory. Note that Yocto will check the checksums of the package files to ensure that nothing has changed since we created the recipe. This is part of its strategy for ensuring that builds are repeatable.

SRC_URI = "git://github.com/Xilinx/PYNQ-HelloWorld.git;protocol=https"

SRC_URI[md5sum] = "ac1bfe94a18301b26ae5110ea26ca596"
SRC_URI[sha256sum] = "f522c54c9418d1b1fdb6098cd7139439d47b041900000812c51200482d423460"

SRCREV = "0e10a7ee06c3e7d873f4468e06e523e2d58d07f8"
S = "${WORKDIR}/git"

Next we have the actual build portion. Luckily for us there are already bitbake recipes for projects that use setuptools and PYNQ so all we need to is inherit from the correct recipes.

inherit xilinx-pynq setuptools3

The xilinx-pynq class will create a PYNQ_NOTEBOOK_DIR variable that will be packaged up into the notebooks sub-package but we are still required to ensure that the environment is correct for the recipe to run correctly. In this case we need to set the PYNQ_JUPYTER_NOTEBOOKS and BOARD environment variables. setup.py also expects that the notebook directory exists so we need to create it. For this we can prepend instructions to the various steps of the build

do_compile_prepend() {
  export BOARD=ZCU104
  export PYNQ_JUPYTER_NOTEBOOKS=${D}${PYNQ_NOTEBOOK_DIR}
}

do_install_prepend() {
  export BOARD=ZCU104
  export PYNQ_JUPYTER_NOTEBOOKS=${D}${PYNQ_NOTEBOOK_DIR}
  install -d ${PYNQ_JUPYTER_NOTEBOOKS}
}

do_configure_prepend() {
  export BOARD=ZCU104
  export PYNQ_JUPYTER_NOTEBOOKS=${D}${PYNQ_NOTEBOOK_DIR}
  install -d ${PYNQ_JUPYTER_NOTEBOOKS}
}

Finally we need to specify our dependencies. We’ll make the core package only depend on PYNQ and the other core libraries we need and the notebooks package also depend on Jupyter. Note the ${PN} in these recipes is a short-hand for the current package name and RDEPENDS means runtime dependencies. Note that we include need to include the python3-audio and pynq-overlay dependencies to ensure that all of the feature of PYNQ used by the helloworld application work correctly.

RDEPENDS_${PN} += "\
        python3-pynq \
        python3-audio \
        python3-pillow \
        pynq-overlay \
        libstdc++ \
        "

RDEPENDS_${PN}-notebooks += "\
        python3-jupyter \
"

Now we have our completed recipe we can try and build it

> petalinux-build -c python3-pynq-helloworld

Which produces an error:

Which appears to be caused by a format string (f'') in the package’s setup.py which isn’t supported in the version of Python being used to perform the installation. To fix this we need to create a patch that replaces the format string with a plain addition. Looking more thoroughly at the notebooks we also see that the locations that the notebooks are installed will also need to change. Fixing both of those results in the following patch

diff --git a/setup.py b/setup.py
index 708dc25..21595f1 100644
--- a/setup.py
+++ b/setup.py
@@ -34,13 +34,13 @@ import shutil

 # global variables
 board = os.environ['BOARD']
-repo_board_folder = f'boards/{board}/resizer'
+repo_board_folder = 'boards/'+board+'/resizer'
 board_notebooks_dir = os.environ['PYNQ_JUPYTER_NOTEBOOKS']
 hw_data_files = []


 # check whether board is supported
 def check_env():
     if not os.path.isdir(repo_board_folder):
         raise ValueError("Board {} is not supported.".format(board))
     if not os.path.isdir(board_notebooks_dir):
-- 
diff --git a/boards/ZCU104/resizer/notebooks/resizer_PL.ipynb b/boards/ZCU104/resizer/notebooks/resizer_PL.ipynb
index 1658d78..cd04d93 100644
--- a/boards/ZCU104/resizer/notebooks/resizer_PL.ipynb
+++ b/boards/ZCU104/resizer/notebooks/resizer_PL.ipynb
@@ -78,7 +78,7 @@
    "outputs": [],
    "source": [
     "resize_design = Overlay(\n",
-    "    \"/usr/local/lib/python3.6/dist-packages/helloworld/bitstream/resizer.bit\")"
+    "    \"/usr/lib/python3.5/site-packages/helloworld/bitstream/resizer.bit\")"
    ]
   },
   {
--

For Yocto to find the patch we need to place it another sub-folder also named according to the recipe name

> mkdir python3-pynq-helloworld
> cp $patch_file python3-pynq-helloworld/build-fixes.patch

We also need to update the SRC_URI entry in the .bb file to include our new patch

SRC_URI = "git://github.com/Xilinx/PYNQ-HelloWorld.git;protocol=https \
    file://build-fixes.patch \
"

Now our build completes succesfully

Adding the image to the filesystem

To add our new packages to the root filesystem we need to let Petalinux know of their existance. One way to do this is to add them to the user-rootfsconfig

> cd ../../
> vim conf/user-rootfsconfig

and add a line for each new package – the core and notebooks package for our new recipe in this case.

CONFIG_python3-pynq-helloworld
CONFIG_python3-pynq-helloworld-notebooks

Now we can run the Petalinux root filesystem configuration and select our two new packages

> petalinux-config -c rootfs

add_packages

Building and testing

With everything set up we can now build the complete image

> petalinux-build

This will take a while and once it has completed our Linux image will be in images/linux. For this example we will just use the image.ub file which contains the kernel and root filesystem all packaged together. We will also need to generate a BOOT.BIN file to perform the initial board set-up. In order for Linux to run our BOOT.BIN needs to contain u-boot, the arm trusted firmware and the platform management firmware in addition to the main bootloader

> petalinux-package --boot --u-boot --atf --pmufw

To test on the board we copy the BOOT.bin and image.ub files from images/linux directory and boot the board.

Once the board is booted and Ethernet is configured you can go into the notebooks directory and start Jupyter

> cd /usr/share/notebooks
> jupyter notebook --ip=0.0.0.0 --no-browser --allow-root

Jupyter will start and give you an address to access the server in the terminal

jupyter_terminal

Jupyter can now be interacted with the same as with the off-the-shelf PYNQ image.

Further reading

This has only been a very brief introduction to Petalinux and the creation of Yocto recipes. For a more in-depth exploration I recommend looking through the Yocto user-guide which contains much more detail on the steps that are invoked when running a bitbake recipe. The Petalinux documentation provides more details on how to build and run embedded Linux images. If you want to look through the code behind this have a look at:

  • meta-xilinx which contains the PYNQ recipes
  • meta-jupyter which is a Xilinx fork containing updated Jupyter recipes for Jupyter
  • The example shown here is available as a GIST

If you have more questions or want to discuss further feel free to comment below or elsewhere in our forum.

5 Likes

Hi,

I found this post really useful, thanks Peter. I’ve used the information here to create a PetaLinux (2019.2) image for a custom board. But it appears that PYNQ is not installed correctly.

I can bring up the jupyter notebook server but there is a problem with pynq.lib

In[1]: from pynq import Overlay

---------------------------------------------------------------------------
ImportError                               Traceback (most recent call last)
<ipython-input-1-1b95ad83cc55> in <module>
----> 1 from pynq import Overlay

/usr/lib/python3.5/site-packages/pynq/__init__.py in <module>
     45 from .pmbus import get_rails
     46 from .pmbus import DataRecorder
---> 47 from . import lib
     48 
     49 

/usr/lib/python3.5/site-packages/pynq/lib/__init__.py in <module>
     72 from .logictools import FSMGenerator
     73 
---> 74 from . import video
     75 from . import audio
     76 from . import dma

/usr/lib/python3.5/site-packages/pynq/lib/video/__init__.py in <module>
     39 
     40 if pynq.ps.CPU_ARCH == pynq.ps.ZYNQ_ARCH:
---> 41     from . import dvi
     42 elif pynq.ps.CPU_ARCH == pynq.ps.ZU_ARCH:
     43     from . import xilinx_hdmi

/usr/lib/python3.5/site-packages/pynq/lib/video/dvi.py in <module>
     39 
     40 if CPU_ARCH == ZYNQ_ARCH:
---> 41     import pynq.lib._video
     42 else:
     43     warnings.warn("DVI/HDMI subsystem only supported on Zynq-7000",

ImportError: No module named 'pynq.lib._video'

What have I missed? I don’t have a bitfile loaded, but the board has no video in any case.
Any advice appreciated.

I had the same issue

You could just remove this part from the library.

Thanks bartoken,

The result of launching that is basically the same

root@pynqpitaya:~# Traceback (most recent call last):
  File "/usr/bin/start_pl_server.py", line 11, in <module>
    load_entry_point('pynq==2.4', 'console_scripts', 'start_pl_server.py')()
  File "/usr/lib/python3.5/site-packages/pkg_resources/__init__.py", line 479, in load_entry_point
    return get_distribution(dist).load_entry_point(group, name)
  File "/usr/lib/python3.5/site-packages/pkg_resources/__init__.py", line 2703, in load_entry_point
    return ep.load()
  File "/usr/lib/python3.5/site-packages/pkg_resources/__init__.py", line 2321, in load
    return self.resolve()
  File "/usr/lib/python3.5/site-packages/pkg_resources/__init__.py", line 2327, in resolve
    module = __import__(self.module_name, fromlist=['__name__'], level=0)
  File "/usr/lib/python3.5/site-packages/pynq/__init__.py", line 47, in <module>
    from . import lib
  File "/usr/lib/python3.5/site-packages/pynq/lib/__init__.py", line 74, in <module>
    from . import video
  File "/usr/lib/python3.5/site-packages/pynq/lib/video/__init__.py", line 41, in <module>
    from . import dvi
  File "/usr/lib/python3.5/site-packages/pynq/lib/video/dvi.py", line 41, in <module>
    import pynq.lib._video
ImportError: No module named 'pynq.lib._video'

Thanks I may remove that from the library but I’d prefer a less intrusive fix where I don’t have to maintain a patch as PYNQ evolves. Presumably my problem is with pynq.lib, video is just the first in the import list.

Any other advice?

Is your board Zynq-7000 or Zynq Ultrascale+ based? Judging by the libraries it’s loading I’m guessing Zynq-7000.

There was an omission in the recipe for PYNQ on that architecture which we’ve fixed in 2020.1. Easiest thing to do is to add do_compile_prepend and do_install_prepend scripts to the recipe to set PYNQ_BUILD_ARCH=armv7l. We’ve done this in the pynq bbclass for 2020.1 here along with some steps in this post which had to be done manually in the past.

Peter

1 Like

Yes it’s zynq-7000, Thanks that all makes sense, any eta on 2020.1 release?

Or does this petalinux flow already work with 2020.1?

This flow should work with Petalinux 2020.1 with the execption that the various prepend parts of the recipe should not be needed any longer. I’ll update the guide when I get some free time.

Peter

Thanks Peter, I have PYNQ up now using 2020.1

Hey, I just have creted PYNQ with petalinux 2020.1, but I have one question.
I’m using
ol = Overlay(“something.xclbin”), but then Screenshot from 2020-09-12 10-58-45
I know this problem. The BOOT.bin programs old FPGA bitstream without accelerator, xrt tries to program non-existing accelerator and system freezes.
The question is why PYNQ isn’t reprogramming FPGA, if I’m creating new overlay? Shouldn’t it program vaad.bit or something and then program accelerator?

Hi, I’m trying to build a petalinux 2020.2 project with PYNQ targeting ZCU102. I have successfully built PYNQ image for ZCU102 by modifying the .spec file. But I can’t find the .spec file in the petalinux project.

It seems that simply modifying the .bb file (ZCU104->ZCU102) is not working. “ValueError: Board is not supported.” error message appears when compiling the pynq3-pynq-helloworld app. Please give me some advice.

1 Like

I followed this flow with petalinux 2020.2, and it works. But it failed in 2022.2. Does anyone have successful experience in building it on 2022.2?

hi, do you solve your problem?