Skip to content

Build the Boot Sources

Brandon Perez edited this page Mar 20, 2017 · 8 revisions

Back to Home

Overview

This wiki describes how to build all of the binaries needed to boot Linux on the Zedboard. These binaries are the first-stage bootloader (FSBL), U-Boot, and the Linux Kernel.

Prerequisites

Your system must be setup according to the method outlined in System Setup.

System Configuration

These instructions were run and tested with the following software and hardware:

  • Laptop OS: Ubuntu 14.04 (64-bit)

  • Board OS: Ubuntu 14.04 (32-bit)

  • Board: Zedboard

  • Board Architecture: ARM

  • Xilinx Tools:

    • Vivado Version: 2015.2
    • Xilinx SDK Version: 2015.2
    • Vivado HLS Version: 2015.2
  • U-Boot Version:

    • Version: 2016.03
    • Branch: master
  • Linux

    • Version: 4.4.0
    • Branch: master

For reference, we assume that the following environment variables are defined:

export SW_DIR=<Path to your software>
export THRS=$((2 * `cat /proc/cpuinfo | grep processor | wc -l`))

Build a First-Stage Bootloader (FSBL) and Bitstream FPGA File

The FSBL is the first piece of code that is run when the Zedboard boots up. It is responsible for configuring the most low-level and basic settings for the processor's peripherals. It can also configure the FPGA, but in our setup, that is handled by U-Boot instead. Once the FSBL finishes configuring the processing system, it transfers control to the executable file specified to it; in our case, this is the U-Boot bootloader.

Generating the FSBL is a one-time configuration step. Once the FSBL is compiled, you will rarely ever need to recompile it, which is good, because it is a bit of a pain to generate.

When the system boots-up, the second stage bootloader (U-Boot in our case) will look for a valid bistream file that it can use to program the FPGA. If it finds one, then it will program the FPGA with it. This step will also generate a bitstream file. This will be an "empty" bitstream, where there is no user logic on the programmable logic (PL) fabric.

The steps below are listed out for the sake of completeness. If you wish to skip this step, there are prebuilt FSBL and bistream files that you can download. The links are listed below. Note that these are configured specifically for the Zedboard. The FSBL may work on other boards, but the bitstream will not.

zynq_fsbl.elf

system.bit

Create a New Project

To begin, open up Vivado:

vivado

Create a new project, selecting all of the appropriate parameters. Under IP Integrator, select Create Block Design, any name for the block design will do. Press the Add IP button, and select ZYNQ7 Processing System, adding it to the block diagram.

Configure the Processing System IP

Next, click on Run Block Automation at the top of the block diagram box, then click Ok. Double click on the IP block, and this will pop up the IP customization window. In the window, click on Peripheral I/O Pins on the Page Navigator sidebar. Then, in the Peripherals menu, expand the entry for Quad SPI Flash. Click the checkbox for Feedback Clock, enabling it.

The feedback clock is important because it allows for the QPSI flash to be properly programmed. Without it, U-Boot cannot program the flash. This means that U-Boot cannot save its environment variables in persistent storage, so you cannot change the automatic booting to a different configuration.

Then, under PS-PL Configuration, expand AXI Non Secure Enablement, and then expand GP Master AXI Interface. Uncheck the box for M AXI GP0 Interface, disabling it, then click Ok. The GP0 interface is disabled because it expects to be hooked up to the clock; for a minimal configuration, we don't need it, so we disable it.

Synthesize the Design

Save the block diagram by hitting CTRL-S. Then, under the Sources tab, right-click on your block design and select Create HDL Wrapper, and then click Ok. Now, synthesize the design. Under Program and Debug, click on Generate Bitstream. Wait for the design synthesis and bitstream generation to complete. Once it is finished, click Ok.

Generate a Board Support Package

Next, click on File, then highlight the Export option, and click Export Hardware. Click on the checkbox for Include Bitstream, then click Ok. Click on File, then Launch SDK, and then Ok.

Inside the Xilinx SDK, click on File, then highlight the New option, and click Board Support Package. Leave the settings at their defaults and click Finish. A window for the board support package's settings will pop up. Under Supported Libraries, check the boxes for the libraries xilffs, xilflash, and xilrsa. Then, click Ok.

Compile the First-Stage Bootloader (FSBL)

Then, click on File, highlight the New option, then click on Application Project. Choose the project name to be zynq_fsbl. Under Target Software, for Board Support Package, change the option to Use Existing, and select the board support package you just created (it will likely be called standalone_bsp_0). Then, click Next.

In the next window, highlight the template Zynq FSBL, and then click Finish. The project should now build automatically. If it doesn't, click on Project, and then click on Build All. This will generate a executable file called <project_name>.elf, which will be located under the Debug directory.

Copy this file to the boot_files directory, it will be used later to generate a boot image. Also, copy the bitstream file generated from the Vivado project:

cp <path to Debug Directory>/zynq_fsbl.elf "${SW_DIR}/boot_files/zynq_fsbl.elf"
cp <path to Bitstream File> "${SW_DIR}/boot_files/system.bit"

Build U-Boot

U-Boot is a portable, general-purpose bootloader that targets many different boards and architectures. It's the open-source standard for bootloaders, equivalent to essentially Linux in this context. In this system, U-Boot acts as the second-stage bootloader. It performs most of the heavy lifting in terms of system setup. It sets up all of the necessary configuration registers for the processor, and finishes the setup for the rest of the processor's peripherals. It also configures the FPGA, setting it up with the initial design specified. Once U-Boot finishes the setup, it transfers control to the Linux kernel, passing along command-line arguments that can be specified.

Build the Uboot bootloader:

cd "${SW_DIR}/uboot"
make CROSS_COMPILE=arm-linux-gnueabihf- ARCH=arm -j $THRS distclean
make CROSS_COMPILE=arm-linux-gnueabihf- ARCH=arm -j $THRS zynq_zed_config
make CROSS_COMPILE=arm-linux-gnueabihf- ARCH=arm -j $THRS

This will create the file ${SW_DIR}/uboot/u-boot, which is the executable file for the U-Boot program.

Copy the U-Boot executable to the boot_files directory, and add a file extension to it:

cp "${SW_DIR}/uboot/u-boot" "${SW_DIR}/boot_files/u-boot.elf"

Build the BOOT.BIN File

The BOOT.BIN file is the bootloader image that the Xilinx FPGA boards expect when they start up. It must contain the FSBL, and any files that the FSBL needs to use. Typically, the second file is the executable that is run after the FSBL finishes. For our system, it will contain the FSBL and U-Boot. Thus, if you update either of these, you must update the bootloader image. Otherwise, this file doesn't need to be updated.

Xilinx provides the bootgen utility to create these images. First, we need to create a configuration file that tells the script what to put in the image:

cat << EOF > "${SW_DIR}/boot_files/zynq_bootloader.bif"
boot_image : {
    [bootloader]${SW_DIR}/boot_files/zynq_fsbl.elf
    ${SW_DIR}/boot_files/u-boot.elf
}
EOF

Then, generate the bootloader image:

bootgen -image "${SW_DIR}/boot_files/zynq_bootloader.bif" -o "${SW_DIR}/boot_files/boot.bin" -w on

This will generate the file ${SW_DIR}/boot_files/boot.bin.

Note: The files specified inside the .bif file must have extensions. Thebootgen program will fail if any of the files don't have an extension.

Build Linux

Linux is the operating system that will be used to interact with the ARM core on the FPGA, and it will run whatever applications are needed.

Build the Linux kernel:

cd "${SW_DIR}/linux"
make CROSS_COMPILE=arm-linux-gnueabihf- ARCH=arm -j $THRS distclean
make CROSS_COMPILE=arm-linux-gnueabihf- ARCH=arm -j $THRS xilinx_zynq_defconfig
make CROSS_COMPILE=arm-linux-gnueabihf- ARCH=arm -j $THRS UIMAGE_LOADADDR=0x8000 uImage

This will create the file ${SW_DIR}/linux/arch/arm/boot/uImage, which is the image of the kernel executable that is used by the bootloader to load it into memory.

Copy the kernel image to the boot_files directory:

cp "${SW_DIR}/linux/arch/arm/boot/uImage" "${SW_DIR}/boot_files/uImage"

Build the Device Tree

The Device Tree is a configuration file that is used to convey a great deal of information to the kernel at boot-time. While Linux does have the command-line to specify parameters on, these tend to be more system-level options and parameters that affect the entire kernel. The Device Tree deals more with hardware and peripherals that Linux must configure and provide interfaces to userspace through drivers. The Device Tree contains detailed information about each peripheral that will be available to use. For example, it could contain the register map for the device, with the base physical address and the size of the memory region, among other information. The device tree is compiled into binary to save space, creating a Device Tree binary (DTB).

Build the device tree for the Zedboard:

cd "${SW_DIR}/linux"
make CROSS_COMPILE=arm-linux-gnueabihf- ARCH=arm -j $THRS zynq-zed.dtb

This will create the file ${SW_DIR}/linux/arch/arm/boot/dts/zynq-zed.dtb, which is the device tree binary for the Zedboard. This will be read by the kernel to determine configuration parameters for all of the peripheral hardware devices at boot time.

Copy the device tree binary to the boot_files directory:

cp "${SW_DIR}/linux/arch/arm/boot/dts/zynq-zed.dtb" "${SW_DIR}/boot_files/devicetree.dtb"

Build the Kernel Modules and Headers

The Linux kernel modules act as extensions to the kernel functionality, and are used to mainly handle interacting with various hardware devices. These modules are not built-in to the kernel, and so they can be dynamically loaded and removed to add extra functionality. These reside in the root filesystem, so we will build the modules, then install them there.

Build the kernel modules and install them on the board's root filesystem:

cd "${SW_DIR}/linux"
make CROSS_COMPILE=arm-linux-gnueabihf- ARCH=arm -j $THRS modules
sudo make CROSS_COMPILE=arm-linux-gnueabihf- ARCH=arm INSTALL_MOD_PATH="${SW_DIR}/ubuntu_14.04_corefs" -j $THRS modules_install

The Linux kernel headers allow for userspace code to be built against parts of the kernel, mainly for accessing drivers through IOCTL's. Installing these allow for compilation on the board.

Install the Linux kernel headers on the board's root filesystem:

cd "${SW_DIR}/linux"
sudo make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- INSTALL_HDR_PATH="${SW_DIR}/ubuntu_14.04_corefs/" headers_install

Next Steps

Now that all of the software sources have been built, we now have everything we need to perform an SD boot.

To boot the Zedboard from SD, see Booting Linux from SD.

References

The solution to U-Boot not being able to program the Quad SPI flash was found on the Xilinx forums - Forum

Files

zynq_fsbl.elf - Prebuilt FSBL generated from the steps on the wiki.

u-boot.elf - Prebuilt U-Boot binary from the Xilinx U-Boot repostiory.

boot.bin - Prebuilt bootloader image, which is the result of running bootgen on the FSBL and U-Boot binaries.

system.bit - "Empty" bitstream file for programming the FPGA, generated from the steps on the wiki.

uImage - Prebuilt Linux kernel image from the Xilinx Linux repository.

devicetree.dtb - Prebuilt device tree binary for the Zedboard from the Xilinx Linux repository.