Arty Z7 Buildroot

Buildroot Digilent Arty-Z7

Our goal is to setup a buildroot environment which creates a bootable image for the Arty Z7 development board. We want to use as few Xilinx proprietary components (FSBL) as possible.

Although most of these components can be built using Petalinux and the Xilinx SDK, buildroot currently does not support building these features. In our case we want the following setup (right):

Bootflow

This tutorial should guide you through the process of configuring buildroot, uboot and linux to create a bootable image for the Arty Z7 board. Throughout we will also explain some interesting concepts used such as U-Boot SPL, FIT images,…

Initial setup

This setup uses a debian 9 as build environment. Packages and commands must be adapted for the OS used. First we need to make sure we have all the tools installed to create a buildroot build.

# Install packages
sudo apt install sed make binutils build-essential gcc g++ bash patch gzip bzip2 perl tar cpio python unzip rsync file bc wget git libncurses5-dev u-boot-tools

Retrieve buildroot from git

# Retrieve buildroot
git clone git://git.buildroot.net/buildroot

Go into the buildroot directory

cd buildroot

# Run the buildroot menuconfig
make menuconfig

Set the following parameters in the buildroot configuration. Or retrieve the buildroot repo from

git clone git@github.com:OpenPixelSystems/arty-z7-buildroot.git
# Load the Arty defconfig
make defconfig BR2_DEFCONFIG=./defconfig

or alternatively set the following parameters manually in the clean buildroot repo.

BR2_arm=y
BR2_cortex_a9=y
BR2_ARM_ENABLE_NEON=y
BR2_ARM_ENABLE_VFP=y
BR2_ARM_FPU_VFPV3=y
BR2_TOOLCHAIN_BUILDROOT_GLIBC=y
BR2_KERNEL_HEADERS_4_4=y
BR2_TARGET_GENERIC_GETTY_PORT="ttyPS0"
BR2_LINUX_KERNEL=y
BR2_LINUX_KERNEL_CUSTOM_GIT=y
BR2_LINUX_KERNEL_CUSTOM_REPO_URL="https://github.com/Digilent/linux-digilent"
BR2_LINUX_KERNEL_CUSTOM_REPO_VERSION="digilent-v4.4"
BR2_LINUX_KERNEL_DEFCONFIG="xilinx_zynq"
BR2_LINUX_KERNEL_UIMAGE=y
BR2_LINUX_KERNEL_UIMAGE_LOADADDR="0x8000"
BR2_LINUX_KERNEL_DTS_SUPPORT=y
BR2_LINUX_KERNEL_INTREE_DTS_NAME="zynq-artyz7"
BR2_TARGET_ROOTFS_CPIO=y
BR2_TARGET_ROOTFS_CPIO_UIMAGE=y
BR2_TARGET_UBOOT=y
BR2_TARGET_UBOOT_BOARDNAME="zynq_artyz7"
BR2_TARGET_UBOOT_CUSTOM_GIT=y
BR2_TARGET_UBOOT_CUSTOM_REPO_URL="https://github.com/Digilent/u-boot-digilent"
BR2_TARGET_UBOOT_CUSTOM_REPO_VERSION="digilent-v2016.07"
BR2_TARGET_UBOOT_NEEDS_DTC=y
BR2_TARGET_UBOOT_NEEDS_OPENSSL=y
BR2_TARGET_UBOOT_FORMAT_IMG=y
BR2_TARGET_UBOOT_SPL=y
BR2_TARGET_UBOOT_SPL_NAME="spl/boot.bin"
Build the image
# Build the image
make -j 8 # Replace 8 with the amount of cores available for parallel compilation

Build output

If everything went according to plan, there should be several outputs available now in the folder output/images.

$ bravl@Desktop-BRAVL arty-z7-buildroot]$ ll ./output/images 
total 16M
-rw-r--r--. 1 bravl bravl  70K Jan  2 18:28 boot.bin
-rw-r--r--. 1 bravl bravl 3.4M Jan  2 17:51 rootfs.cpio
-rw-r--r--. 1 bravl bravl 3.4M Jan  2 17:51 rootfs.cpio.uboot
-rw-r--r--. 1 bravl bravl 4.0M Jan  2 17:51 rootfs.tar
-rw-r--r--. 1 bravl bravl 452K Jan  2 18:28 u-boot.bin
-rw-r--r--. 1 bravl bravl 452K Jan  2 18:28 u-boot.img
-rw-r--r--. 1 bravl bravl 3.6M Jan  2 17:51 uImage
-rw-r--r--. 1 bravl bravl 8.8K Jan  2 17:51 zynq-artyz7.dtb
  • boot.bin: The U-Boot SPL (Secondary Program loader). This is the image that will run after the ROM-Code. It’s also what replaces the Xilinx FSBL (First stage bootloader) in our setup. It’s main goal is to provide minimal setup of the hardware to allow U-Boot to be retrieved from the EMMC, loaded into memory and executed.
  • u-boot.img: As the name implies, this is the image containing the actual u-boot bootloader. It’s goal is to load the kernel, devicetree and possibly a initramfs into memory and kickstart the linux kernel.
  • uImage and Image, these are 2 file containing the linux kernel image. Image is just the raw kernel and uImage is the kernel plus a defined header used by U-Boot. Depending on configuration there could also be a zImage present which is a self-extracting compressed version of the kernel Image.
  • zynq-arty-z7.dtb file: Which is a flattened devicetree binary which is used during the initial startup of the kernel to configure certain peripherals of the ARM core. For example, clocks, SPI/I2C/UART interfaces,… are configured inside the devicetree file. This configuration is then used by the respective drivers to setup the correct parameters.
  • rootfs.*: Multiple versions of the busybox rootfs built by buildroot. In this tutorial only the rootfs.cpio is important/used. This is the rootfs that will be loaded into memory by U-Boot and booted into by the linux kernel.

FIT Image

In the digilent case the default U-Boot is setup to boot a specific image type called a FIT image. FIT stands for **Flattened Image Tree ** which already hints towards a link with devicetree. Basically FIT is an image format which combines different types of images into one big images. For example it can combine a kernel, dtb and initramfs into one image. Which is how it’s being used in this build. However it can become even more flexible since it allows multiple boot scenarios with different images and configurations.

In order to create a FIT image we need to define the tree structure of this image which is done using an .its file. Following .its file has been retrieved from the documentation found in the U-Boot source tree.

/dts-v1/;

/ {
        description = "Configuration to load fpga before Kernel";
        #address-cells = <1>;

        images {
                fdt@0 {
                        description = "artyz7";
                        data = /incbin/("./devicetree.dtb");
                        type = "flat_dt";
                        arch = "arm";
                        compression = "none";
                        load = <0x10000000>;
                        hash@1 {
                                algo = "md5";
                        };
                };

                //		fpga@0 {
                //			description = "FPGA";
                //			data = /incbin/("./download.bit");
                //			type = "fpga";
                //			arch = "arm";
                //			compression = "none";
                //			load = <0x30000000>;
                //			hash@1 {
                //				algo = "md5";
                //			};
                //		};

                linux_kernel@0 {
                        description = "Linux";
                        data = /incbin/("./Image");
                        type = "kernel";
                        arch = "arm";
                        os = "linux";
                        compression = "none";
                        load = <0x8000>;
                        entry = <0x8000>;
                        hash@1 {
                                algo = "md5";
                        };
                };

                ramdisk@0 {
                        description = "ramdisk";
                        data = /incbin/("./rootfs.cpio");
                        type = "ramdisk";
                        arch = "arm";
                        os = "linux";
                        compression = "none";
                        load = <00000000>;
                        entry = <00000000>;
                        hash@1 {
                                algo = "sha1";
                        };
                };
        };

        configurations {
                default = "config@1";
                config@1 {
                        description = "Linux";
                        kernel = "linux_kernel@0";
                        fdt = "fdt@0";
                        ramdisk = "ramdisk@0";
                };

                //                config@2 {
                //                        description = "Linux with fpga";
                //                        kernel = "linux_kernel@0";
                //                        fdt = "fdt@0";
                //                        fpga = "fpga@0";
                //                };
        };
};

What we can see is that one configuration is created at the bottom of the file. Which uses 3 components: kernel, fdt (flattened device tree = dtb file) and ramdisk (initramfs). An important parameter to verify is the load and entry address of the kernel. This should be inline with the address configured in the buildroot setup.

So now we have this .its file we need to create the actual FIT image. This is done using the U-Boot mkimage tool.

mkimage -f multi-with-fpga.its fit.itb

The result of this command should be a file called fit.itb. This file should be stored on a sdcard which has been formatted as FAT32. This fit.itb image, together with the boot.bin (created by buildroot in previous step) should be present on the sdcard. Once plugged into the Arty board this sdcard should allow the system to boot into linux. From this point on you can start customizing your buildroot.

All defconfigs, .its files,… are available at

git clone git@github.com:OpenPixelSystems/arty-z7-buildroot.git