Build Zynq (Zybo) boot image - matzipan/slam-xilinx GitHub Wiki

Build U-Boot bootloader

  • Use the Xilinx U-Boot branch.
  • Under Ubuntu you'll need to install the device-tree-compiler package.
make zynq_zybo_config
make menuconfig
  • In menuconfig, enable Boot Media > Support for booting from QSPI flash.
export ARCH=arm
export UIMAGE_LOADADDR=0x2080000
export LOADADDR=0x2080000
export CROSS_COMPILE="arm-xilinx-linux-gnueabi-"
make 

Because of a bug in the Xilinx tools (it requires that all files have extensions), the u-boot file, needs to be renamed into u-boot.elf:

mv u-boot u-boot.elf

If you need a bigger ramdisk size

By default, u-boot is hardcoded to a maximum compressed ramdisk size (.image.gz) of 5.87 MB. The patch below describes how to increase the this limit. After the change, rebuilt u-boot.

diff --git a/include/configs/zynq-common.h b/include/configs/zynq-common.h
index 204b1ba..71c643f 100644
--- a/include/configs/zynq-common.h
+++ b/include/configs/zynq-common.h
@@ -214,7 +214,7 @@
        "loadbootenv_addr=0x2000000\0" \
        "kernel_size=0x500000\0"        \
        "devicetree_size=0x20000\0"     \
-       "ramdisk_size=0x5E0000\0"       \
+       "ramdisk_size=0x895440\0"      \
        "boot_size=0xF00000\0"  \
        "fdt_high=0x20000000\0" \
        "initrd_high=0x20000000\0"      \

Build Linux

  • Use the Xilinx Linux branch, it has extra drivers and functions which haven't been merged in mainline.
export PATH=<path to u-boot-xlnx>/tools:$PATH 
export ARCH=arm
export UIMAGE_LOADADDR=0x2080000
export LOADADDR=0x2080000
export CROSS_COMPILE="arm-xilinx-linux-gnueabi-"
make xilinx_zynq_defconfig
make uImage

Because of a bug in the Xilinx tools (it requires that all files have extensions), the uImage file, needs to be renamed into uImage.bin:

mv arch/arm/boot/uImage arch/arm/boot/uImage.bin

Path for build artefacts should be arch/arm/boot.

Compile device tree

Basic device tree

You can compile a basic device tree by running the following command in the linux-xlnx folder:

make ARCH=arm zynq-zybo.dtb

Path for build artefact should be arch/arm/boot/dts/zynq-zybo.dtb.

Custom device tree

However, this device tree will not contain entries for the devices in the programmable logic. Use this tutorial and generate a custom device tree from your hardware definition.

Unfortunately, ethernet doesn't work with the customly generated device tree for Zybo. To fix this, in the file pcw.dtsi, at the end of the &gem0 { entry, add the following:

	ethernet_phy: ethernet-phy@0 {
		reg = <0>;
	};

Userspace I/O (UIO) drivers

An easy way to control your FPGA devices from userspace is through the UIO kernel mechanism. To use it, after this change, add the following argument to your kernel bootargs:

uio_pdrv_genirq.of_id="generic-uio"

Then in your device's definition in pl.dtsi, replace the compatible parameter with the string "generic-uio", like so:

toplevel_0: toplevel@43c00000 {
			compatible = "generic-uio";

If you need a bigger ramdisk size

By default, Linux allocates a maximum initrd size of only a few MB (not sure how many but not enough for my usecase). The patch below changes the device tree bootargs so that linux allocates more space for the initrd. After the change, rebuild the device tree.

diff --git a/arch/arm/boot/dts/zynq-zybo.dts b/arch/arm/boot/dts/zynq-zybo.dts
index 655a033..0f6e98f 100644
--- a/arch/arm/boot/dts/zynq-zybo.dts
+++ b/arch/arm/boot/dts/zynq-zybo.dts
@@ -31,7 +31,7 @@
        };
 
        chosen {
-               bootargs = "";
+               bootargs = "ramdisk_size=24576";
                stdout-path = "serial0:115200n8";
        };

Build root file system

  • Follow this tutorial on building BusyBox, copying over toolchain libraries, configuration and ramdisk image building.
  • Follow this tutorial on building dropbear for ssh. After you do make install, the scp binary will not get copied over, so you need to manually cp scp /media/matzipan/Xilinx/project/rootfs/usr/bin/.
  • Extra info here.

Resize ramdisk

In case the image is not big enough to hold all the files you need, resize it:

e2fsck -f rootfs.image
resize2fs rootfs.image <new size in bytes>

Mount ramdisk

The reason why I'm copying rootfs over is because I have built it in a separate directory before this step. However, you can build the rootfs directly in the mounted ramdisk folder.

mkdir rootfs/
sudo mount -o loop rootfs.image rootfs/

Add the files you need to rootfs

  • Cross compile zmq:
./configure  --host=arm-xilinx-linux-gnueabi --prefix=/media/matzipan/Xilinx/project/rootfs/ PKG_CONFIG_PATH=/media/matzipan/Xilinx/project/rootfs/lib/pkgconfig CPPFLAGS=-I/media/matzipan/Xilinx/project/rootfs/include LDFLAGS=-L/media/matzipan/Xilinx/project/rootfs/lib

make && make install

Add a work directory to fstab

Add the following line to /etc/fstab:

none        /app        tmpfs   size=256m   0 

Example /etc/init.d/rcS file

#!/bin/sh

echo "Starting rcS..."

echo "++ Setting hostname"
hostname "zybo-rts"

echo "++ Mounting filesystem"
mount -a

echo "++ Setting up mdev"
echo /sbin/mdev > /proc/sys/kernel/hotplug
mdev -s

echo "++ Setting up pts"
mkdir -p /dev/pts
mount -t devpts devpts /dev/pts

echo "++ Configuring MAC address"
ifconfig eth0 down
ifconfig eth0 hw ether 00:0A:35:FF:00:01
#ifconfig eth0 144.32.175.214 up
ifconfig eth0 up
ifconfig lo up

echo "++ Starting DHCP daemon"
udhcpc -b -s /etc/dhcp/update.sh

echo "++ Starting telnet daemon"
telnetd -l /bin/sh

echo "++ Starting http daemon"
httpd -h /var/www

echo "++ Starting ftp daemon"
tcpsvd 0:21 ftpd ftpd -w /&

echo "++ Starting dropbear (ssh) daemon"
dropbear

echo "rcS Complete"

Build GDB server

Grab the latest binutils source.

cd gdb/gdbserver
./configure --host=arm-linux-gnueabi
make

Then add the gdbserver binary to the rootfs.

Unmount and build initrd

You need to have <path to u-boot-xlnx>/tools in your $PATH variable: export PATH=<path to u-boot-xlnx>/tools:$PATH

sudo umount rootfs
gzip rootfs.image
export PATH=<path to u-boot-xlnx>/tools:$PATH
mkimage -A arm -O linux -T ramdisk -C gzip -d rootfs.image.gz urootfs.image.gz

Build the boot binary

Create a file in your SDK project folder called qspi.bif:

//arch = zynq; split = false; format = BIN
the_ROM_image:
{
    [bootloader]/media/matzipan/Xilinx/project/fsbl/executable.elf
    /home/matzipan/Workspace/project/zynq-slam/vivado/vivado.runs/impl_1/design_1_wrapper.bit
    /media/matzipan/Xilinx/project/u-boot-xlnx/u-boot.elf
    [offset=0x300000]/media/matzipan/Xilinx/project/linux-xlnx/arch/arm/boot/uImage.bin
    [offset=0x700000]/media/matzipan/Xilinx/project/zynq-dts/zynq-dts.dtb
    [offset=0x720000]/media/matzipan/Xilinx/project/urootfs.image.gz
}

The partition table above moves the uImage, device tree and rootfs addresses from their defaults, 0x100000, 0x600000 and 0x620000. This means that you will need to patch U-Boot to load from the new addresses using the following patch:

diff --git a/include/configs/zynq-common.h b/include/configs/zynq-common.h
index 204b1ba..7186af5 100644
--- a/include/configs/zynq-common.h
+++ b/include/configs/zynq-common.h
@@ -240,10 +240,10 @@
                "bootm ${kernel_load_address} ${ramdisk_load_address} ${devicetree_load_address}\0" \
        "qspiboot=echo Copying Linux from QSPI flash to RAM... && " \
                "sf probe 0 0 0 && " \
-               "sf read ${kernel_load_address} 0x100000 ${kernel_size} && " \
-               "sf read ${devicetree_load_address} 0x600000 ${devicetree_size} && " \
+               "sf read ${kernel_load_address} 0x300000 ${kernel_size} && " \
+               "sf read ${devicetree_load_address} 0x700000 ${devicetree_size} && " \
                "echo Copying ramdisk... && " \
-               "sf read ${ramdisk_load_address} 0x620000 ${ramdisk_size} && " \
+               "sf read ${ramdisk_load_address} 0x720000 ${ramdisk_size} && " \
                "bootm ${kernel_load_address} ${ramdisk_load_address} ${devicetree_load_address}\0" \
        "uenvboot=" \
                "if run loadbootenv; then " \

The GUI way

In Xilinx SDK, select Xilinx Tools > Create Boot Image, and import the bif file created above. Select an output path and then Create Image.

The command line way

bootgen -image /home/matzipan/Workspace/project/zynq-slam/vivado/vivado.sdk/qspi.bif -arch zynq -o /home/matzipan/Workspace/project/zynq-slam/vivado/vivado.sdk/BOOT.bin -w on

Flash the binary to QSPI flash

The QSPI flash has a size of 16 MB, so make sure your binary fits.

The GUI way

Select Xilinx Tools > Program Flash and use the programming tool to upload the above binary to QSPI flash.

The command line way

program_flash -f /home/matzipan/Workspace/project/zynq-slam/vivado/vivado.sdk/BOOT.bin -offset 0 -flash_type qspi_single -cable type xilinx_tcf url TCP:localhost:12345