Deploying PYNQ and Jupyter with Petalinux


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


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


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

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"

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://;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. 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

do_install_prepend() {
  export BOARD=ZCU104

do_configure_prepend() {
  export BOARD=ZCU104

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 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/ b/
index 708dc25..21595f1 100644
--- a/
+++ b/
@@ -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://;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.


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

> petalinux-config -c rootfs


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= --no-browser --allow-root

Jupyter will start and give you an address to access the server in the 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.

1 Like