Building a Linux kernel for the Xilinx PYNQ Z1 Arm board

Wim Vanderbauwhede edited this page Dec 12, 2018 · 4 revisions

Building a Linux kernel for the Xilinx PYNQ-Z1 Arm board


PYNQ-Z1 board

The original Linux kernel on my strikingly coloured PYNQ-Z1 board has a bug which makes the networking crash every now and then. The error message in the system log is as follows:

  macb e000b000.ethernet eth0: DMA bus error: HRESP not OK

This is fixed in newer kernels (see this patch for details) so I decided to build a new Linux kernel for my board.

Much of this tutorial deals with workarounds required to cross-compile a Linux kernel on MacOS, but I have provided the instructions for cross-compiling on a Linux system as well, that is much simpler.


This tutorial assumes that you are familiar with working in a terminal with e.g. bash, with building code with make and the related tools, and know how to use tools like git, ssh, wget or curl etc. If you are on MacOS you'll need Macports or Homebrew.


Get the cross-compilation toolchain

To compile source code into Arm binaries on a non-Arm architecture, we need a cross-compilation toolchain.

  • On Linux:

      $ git clone ~/rpi-tools
  • On MacOS I used Jared Wolff's cross compilation toolchain

      $ wget

    After mounting the tools are available on /Volumes/xtools.

Create a working directory

    $ mkdir PYNQ-Z1
    $ cd PYNQ-Z1/

Configure the cross-compilation toolchain

  • On MacOS:

      $ export CROSS_COMPILE=armv8-rpi3-linux-gnueabihf-
      $ export ARCH=arm
      $ export PATH=/Volumes/xtools/armv8-rpi3-linux-gnueabihf/bin/:$PATH
  • On Linux it would be (I did not verify this):

      $ export CROSS_COMPILE=arm-linux-gnueabihf-
      $ export ARCH=arm
      $ export PATH=~/rpi-tools/arm-bcm2708/arm-linux-gnueabihf/bin/:$PATH

Get the Xilinx Linux kernel

    $ git clone

I selected the latest series 4 kernel:

    $ git checkout -b xlnx_rebase_v4.14
    $ cd linux-xlnx/

Install missing stuff on MacOS

Because MacOS is not GNU/Linux, some header files are missing and some tools are different between Darwin and Linux. I use MacPorts which installs in /opt/local, for Homebrew adjust accordingly.

GNU/Linux tools

I installed the GNU versions of the tools:

    $ sudo port install coreutils
    $ sudo port install gsed
    $ sudo port install findutils

Then I symlinked /opt/local/bin/gxargs to /opt/local/bin/xargs and the same for gawk to awk and gstat to stat, and put /opt/local/bin first in the $PATH.

Missing header files

I copied a number of files from my PYNQ Linux distro's /usr/include directory into linux-xlnx/usr/include. I used scp as I had a running system available.

    $ mkdir usr/include
    $ cd usr/include

    $ scp pynq-1:/usr/include/elf.h .
    $ scp pynq-1:/usr/include/features.h .
    $ scp -r pynq-1:/usr/include/arm-linux-gnueabihf .

The latter has rather more than needed, but I was lazy:

└── arm-linux-gnueabihf
    ├── asm
    ├── bits
    ├── c++
    │   ├── 5
    │   │   ├── bits
    │   │   └── ext
    │   └── 5.2.1
    │       ├── bits
    │       └── ext
    ├── freetype2
    │   └── config
    ├── gnu
    ├── layout
    ├── libavcodec
    ├── libavformat
    ├── libavutil
    ├── libswresample
    ├── libswscale
    ├── openssl
    ├── python2.7
    ├── python3.4m
    ├── sys_LINUX
    └── unicode

Note that sys should be renamed or removed as it clashes with the MacOS sys.

Soft floating-point stub

The PYNQ-Z1 board has an Arm Cortex-A9 processor which has a hard floating point unit, so the distro does not have the file gnu/stubs-soft.h (it assumes that code is compiled with -mfloat-abi=hard). For some reason, the Linux kernel does not use hard floating point instructions, likely because it is more portable and there are very few floating point operations in the kernel code anyway.

I copied the gnu/stubs-soft.h from Matt Filetto's Linaro repo:

    $ wget

Configuring the Linux kernel

Building the Linux kernel has two stages. In the first stage, the build is configured. Then the actual kernel image is built using the generated configuration. This step does not use the cross compiler so a simple make is enough. As I wanted the default configuration for the Xilinx Zynq board, I used:

    $ make xilinx_zynq_defconfig

Other configurations for Arm boards are available in arch/arm/configs. If you want to explore the configuration or customize it in detail, you can do:

    $ make menuconfig

Building the kernel zImage

If what you need is the zImage, then your job is done. Just build the kernel as follows:

    $ PATH=/opt/local/bin:$PATH \
     ~/PYNQ-Z1/linux-xlnx/usr/include/arm-linux-gnueabihf/ \
     make -j8 zImage

The $PATH contains the path to the cross-compilation toolchain; the $CPATH contains the additional include paths where the compiler should look for header files. The option -j8 tells make to build in parallel for 8 hardware threads. The zImage target builds the compressed kernel image.

Building the kernel uImage

However, my PYNQ-Z1 uses the U-Boot booloader which needs a uImage kernel image, which in turn needs the tool mkimage to create it, so I had to build U-Boot first.

Get the Device Tree Compiler dtc

Building U-Boot requires dtc. Apparently there is a Homebrew package but there is no MacPorts package. I (finally) found the source at and built it:

    $ wget
    $ tar -zxvf dtc-1.4.7.tar.gz
    $ cd dtc-1.4.7
    $ make

Now copy the dtc somewhere in your path, e.g. I put it in $HOME/bin.

Get the Xilinx U-Boot sources

    $ git clone
    $ cd u-boot-xlnx
    $ git branch
    $ git checkout xilinx-v2016.4
    $ make zynq_zed_config        

For the actual build we need some openssl header as well, from /opt/local/include, so:

    $ PATH=/opt/local/bin:/Volumes/xtools/armv8-rpi3-linux-gnueabihf/bin/:$PATH CPATH=/opt/local/include/ make

And then the newly built U-Boot tools need to be added to the $PATH, so finally:

    $ cd ../linux-xlnx 
    $ PATH=~/PYNQ-Z1/u-boot-xlnx/tools/:/opt/local/bin:PATH CPATH=~/PYNQ-Z1/linux-xlnx/usr/include/:~/PYNQ-Z1/linux-xlnx/usr/include/arm-linux-gnueabihf make -j8 UIMAGE_LOADADDR=0x8000 uImage

I changed the U-Boot config file uEnv.txt to point at the new kernel, which I named uImage-4.14.


Then I mounted the PYNQ disk image, and copied the newly compiled kernel:

$ cp arch/arm/boot/uImage /Volumes/NO\ NAME/uImage-4.14

And that's it, I put the micro-SD card back into the PYNQ board, switched it on and watched it boot. Then finally:

$ ssh pynq-1

And it worked, we're running kernel v4.14:

New kernel on PYNQ-Z1

You can’t perform that action at this time.
You signed in with another tab or window. Reload to refresh your session. You signed out in another tab or window. Reload to refresh your session.
Press h to open a hovercard with more details.