Showing results for 
Show  only  | Search instead for 
Did you mean: 

Creating a Linux user application in Vitis on a Zynq UltraScale Device

6 0 549

One of the most fundamental tasks in Linux embedded design is creating a user application.

In this blog, I will showcase a simple LED toggling application run on a Linux kernel on a Zynq® UltraScale™ device. 

Hardware Design:

I am using a Zynq UltraScale+ MPSoC ZCU104 Evaluation board. However, the steps here should be agnostic to the device you are using. 

I have used Vivado® to create my block design. I have a Zynq UltraScale PS, and an AXI GPIO connected to the 4 LEDs on my ZCU104 board.


The address map is shown below:


I used the options below when creating the XSA:



Linux Image:

If you have a development board, it is recommended to use the BSP (if it exists).

However, in this example I will create it from the template. I have added the UIO drivers and will use this for the AXI GPIO.

I have also created the sysroot as this is needed in Vitis for cross compilation.

petalinux-create -t project --template zynqMP -n zcu104_linux
cd zcu104_linux
petalinux-config --get-hw-description=<path to xsa>

Select DTG Settings -> (zcu104-revc) MACHINE_NAME

petalinux-config -c kernel
Select Device Drivers -> Userspace I/O drivers
<*> Userspace I/O platform driver with generic IRQ handing
<*> Userspace platform driver with generic irq and dynamic memory

Replace the system-user.dtsi with the following:

/include/ "system-conf.dtsi"
/ {
    chosen {
        bootargs = "earlycon clk_ignore_unused   uio_pdrv_genirq.of_id=generic-uio";
        stdout-path = "serial0:115200n8";
&axi_gpio_0 {
    compatible = "generic-uio";

Then run the following commands:

cd images/linux
petalinux-build --sdk
petalinux-package --sysroot

Creating the Platform:

This is not required as users only need the sysroot in Vitis.

However, for ease of use we can create a platform that we can use in Vitis to create our Linux application.

First, let's set up the platform files. 

I have organized the platform files into a folder structure. This is not needed, but users need to be aware of the file paths in the BIF.

The BIF is used in bootgen to create the bootable image. Here we are only using placeholder file names.

mkdir -p sw_comp/src/a53/xrt/image
mkdir sw_comp/src/boot
  • Copy the image.ub, boot.scr and rootfs.cpio.gz files from the PetaLinux image/linux folder to sw_comp/src/a53/image 
  • Copy the system.bit, bl31.elf, uboot.elf, zynqmp_fsbl (renamed fsbl.elf), and pmufw.elf files from the PetaLinux image/linux folder to sw_comp/src/boot

Create the BIF:

  [fsbl_config] a53_x64
  [bootloader] <zcu104_base/boot/fsbl.elf>
  [pmufw_image] <zcu104_base/boot/pmufw.elf>
  [destination_device=pl] <system.bit>
  [destination_cpu=a53-0, exception_level=el-3, trustzone] <zcu104_base/boot/bl31.elf>
  [destination_cpu=a53-0, exception_level=el-2] <zcu104_base/boot/u-boot.elf>

Copy linux.bif to sw_comp/src/boot.

Now create a new platform project in Vitis as follows:








This will create the platform in zcu104_base/export.

Creating the Linux Image in Vitis:


Select a platform from the repository, click the + icon and browse to your platform.



Create a new application:


Here, we can see that the Application settings are set by default using the settings in our platform.


Select an Empty Application template, as we will be creating our own custom application.


Right click on the src folder under the led_test app, and select New -> File


Give this a file name (.c), and click Finish.


You can now copy in the code below. This is a simple UIO example that will toggle the LEDs.

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/mman.h>
#include <fcntl.h>
#define GPIO_MAP_SIZE           0x10000
#define GPIO_DATA               0x00
#define GPIO_TRI                0x04
#define LED_NUM             256

#define LED_DELAY             10000000

int main(int argc, char *argv[])
    int fd;
    char *uiod = "/dev/uio0";
    void *gpio_ptr;
    volatile int Delay;

    printf("AXI GPIO UIO test.\n");

    // open the UIO device file to allow access to the device in user space

    fd = open(uiod, O_RDWR);
    if (fd < 1) {
        printf("Invalid UIO device file:%s.\n", uiod);
        return -1;

    // mmap the GPIO device into user space

    gpio_ptr = mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
    if (gpio_ptr == MAP_FAILED) {
        printf("Mmap call failure.\n");
        return -1;

    // set bit0 on the GPIO to be output
    // see pg144 for ref

    *((volatile unsigned *)(gpio_ptr + GPIO_TRI)) = 0x0;

    // Toggle the LED
    while (1) {
        int i;
        unsigned char led_pin = 0x0;
        for (i = 0; i < LED_NUM; i++) {
            *((volatile unsigned *)(gpio_ptr + GPIO_DATA)) = led_pin;
            for (Delay = 0; Delay < LED_DELAY; Delay++);
            *((volatile unsigned *)(gpio_ptr + GPIO_DATA)) = 0x0;

    // unmap the GPIO device from user space

    munmap(gpio_ptr, 4096);
    return 0;

Select the system project, and click the hammer icon. This will build the executable and create the boot image.


Testing on Hardware:

Copy all of the images from led_app_system\Debug\sd_card onto your SD card:


Once booted, the SD card will be auto-mounted.

Change directory here, and execute the led_app.elf as shown below:


You should also see the LEDs flashing on the board.

Use Ctrl + c to cancel.