ZCU102 Linux

Unless otherwise stated, everything on this page is based on Vivado 2017.2 and a ZCU102 Revision 1.0 board with ES2 silicon (EK-U1-ZCU102-ES2-G).

Booting Linux

The process for booting Linux on Zynq UltraScale+ has a few more steps than on Zynq-7000, some of which aren't (currently) documented well by Xilinx.

These instructions have been compiled from several different pages on the Xilinx Wiki, along with a bit of experimentation.

Before building anything, the Vivado tools need to be sourced, and the correct cross-compiler set:

source /opt/Xilinx/Vivado/2017.2/settings64.sh
export CROSS_COMPILE=aarch64-linux-gnu-

Building the FSBL

The FSBL can be built in Xilinx SDK (by creating an Application Project targeting psu_cortexa53_0 and selecting the 'Zynq MP FSBL' example project), or using HSI with the following TCL script.

fsbl.tcl
set hwdsgn [open_hw_design design_1_wrapper.hdf]
generate_app -hw $hwdsgn -os standalone -proc psu_cortexa53_0 -app zynqmp_fsbl -compile -sw fsbl -dir fsbl

I have experienced problems with the FSBL not updating correctly in SDK after changing Zynq parameters in Vivado then re-exporting the hardware design, leading to the board not booting. This could be fixed by deleting all SDK projects and starting from scratch (possibly caused by a bug in SDK).

Building the PMU firmware

PMU firmware can be built in Xilinx SDK (by creating an Application Project targeting psu_pmu_0), or using HSI with the following TCL script.

pmufw.tcl
set hwdsgn [open_hw_design design_1_wrapper.hdf]
generate_app -hw $hwdsgn -os standalone -proc psu_pmu_0 -app zynqmp_pmufw -compile -sw pmufw -dir pmufw

Building ARM Trusted Firmware (ATF)

The Xilinx instructions for building ATF seem to work fine - clone the sources from https://github.com/xilinx/arm-trusted-firmware, checkout the appropriate tag (e.g. xilinx-v2017.2), and build with the following to create build/zynqmp/release/bl31/bl31.elf.

make PLAT=zynqmp RESET_TO_BL31=1

Building U-Boot

Building U-Boot is similar to the Zynq-7000, but the output no longer needs to be manually renamed to u-boot.elf.

As per the Xilinx instructions, clone the sources from https://github.com/Xilinx/u-boot-xlnx, checkout the appropriate tag (e.g. xilinx-v2017.2), and build with the following to create u-boot.elf.

make xilinx_zynqmp_zcu102_config
make

Building the Linux Kernel

The Linux kernel build is fairly standard, with the only differences from the Zynq-7000 being the architecture (arm64 instead of arm) and not needing to build a uImage file for U-Boot as (Image will suffice).

As per the Xilinx instructions, clone the sources from https://github.com/Xilinx/linux-xlnx, checkout the appropriate tag (e.g. xilinx-v2017.2), and build with the following to create Image.

make ARCH=arm64 xilinx_zynqmp_defconfig
make ARCH=arm64

Building the device tree

The device tree can be created from the Xilinx Linux kernel sources (either within or out of the main source tree), or using SDK/HSI.

 

As of the v2017.2 tag of the Xilinx Linux kernel, the latest ZCU102 device tree is "zynqmp-zcu102-revB". This can be built using the standard make dtbs command within the kernel source folder, but its often easier to move the dts sources elsewhere if they need to be customised.

The following Makefile (run with just make) can be used to build the device tree when files are not within the kernel source tree, but still use kernel headers and includes (replace KDIR with the path to the Xilinx Linux repository).

Makefile
ifneq ($(KERNELRELEASE),)
always := zynqmp-zcu102-revB.dtb
else
KDIR := /path/to/linux-xlnx
default:
	$(MAKE) ARCH=arm64 -C $(KDIR) M=$(PWD)
clean:
	$(MAKE) ARCH=arm64 -C $(KDIR) M=$(PWD) clean
endif

Alternatively, device trees can be created using SDK or HSI from the Xilinx tools. The following HSI TCL script (or Xilinx SDK instructions) should theoretically create device tree sources based on the specified hardware design, however this has had problems for me (most obviously SATA did not work). Replace the device-tree-xlnx repository path as appropriate.

dts.tcl
open_hw_design design_1_wrapper.hdf
set_repo_path ../device-tree-xlnx
create_sw_design device-tree -os device_tree -proc psu_cortexa53_0
generate_target -dir dts

Creating the boot image (BOOT.bin)

The SD card boot image should contain the FSBL, PMU firmware, ATF, and U-Boot.

This can be created using the 'Create Boot Image' option in Xilinx SDK, or by using the bootgen tool. When using SDK, ensure the appropriate Exception Level and TrustZone options are used for ATF and U-Boot, and that bootloader and pmu partition types are used for the first two items.

sd_boot.bif
//arch = zynqmp; split = false; format = BIN
the_ROM_image:
{
        [destination_cpu = a53-0, bootloader]fsbl.elf
        [pmufw_image]pmufw.elf
        [destination_cpu = a53-0, exception_level = el-3, trustzone]bl31.elf
        [destination_cpu = a53-0, exception_level = el-2]u-boot.elf
}

To generate 'BOOT.bin' using bootgen:

bootgen -image sd_boot.bif -arch zynqmp -w -o i BOOT.bin

Loading the bitstream, device tree and kernel from U-Boot

The bitstream, device tree and kernel (and root filesystem) can be loaded from SD card, external storage (USB or SATA), flash, or a remote server via TFTP. This is similar to Zynq-7000, but with the addition of SATA support on Zynq UltraScale+ (accessed using U-Boot scsi commands).

The main difference between the Zynq UltraScale+ and Zynq-7000 is the ability to use the booti command instead of bootm. This allows the use of a standard kernel Image file, instead of a packaged uImage generated by mkimage.

Also note that U-Boot by default seems to save its environment to a uboot.env file on the SD card rather than to flash (which was the default on ZC706 boards).

Another thing to note is the fpga info 0 command does not correctly display the size of the FPGA bitstream (instead showing '1 byte'). When using fpga load, the size of the .bit file in bytes can be used instead.

Contents