Build Zynq (Zybo) boot image - matzipan/slam-xilinx GitHub Wiki
- Always consult the Xilinx wiki:
- Kernel documentation on initrd and initramfs
- Info on kernel image vs zImage vs uImage
- Always
source /media/matzipan/Xilinx/software/SDK/2016.2/settings64.sh
for thearm-xilinx-linux-gnueabi
GCC toolchain. Thearm-linux-gnueabihf
can be used as a replacement.
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
, thescp
binary will not get copied over, so you need to manuallycp 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
fstab
Add a work directory to Add the following line to /etc/fstab
:
none /app tmpfs size=256m 0
/etc/init.d/rcS
file
Example #!/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