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.
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:
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>
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:
petalinux-build
cd images/linux
petalinux-build --sdk
petalinux-package --sysroot
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
Create the BIF:
the_ROM_image: { [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.
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; led_pin++; } } // 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.
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.
The application above is run manually. However, users can create a recipe in Petalinux to load this into the rootfs, and run it.
To create the bootscript, users can use the command below:
Then update the bootscript.bb file as shown below:
#
# This file is the bootscript recipe.
#
SUMMARY = "Simple bootscript application"
SECTION = "PETALINUX/apps"
LICENSE = "MIT"
LIC_FILES_CHKSUM = "file://${COMMON_LICENSE_DIR}/MIT;md5=0835ade698e0bcf8506ecda2f7b4f302"
SRC_URI = "file://hello_world.elf \
file://bootscript \
"
S = "${WORKDIR}"
inherit update-rc.d
INITSCRIPT_NAME = "bootscript"
INITSCRIPT_PARAMS = "start 99 S ."
do_install() {
install -d ${D}/${bindir}
install -m 0755 ${S}/hello_world.elf ${D}/${bindir}
install -d ${D}${sysconfdir}/init.d
install -m 0755 ${S}/bootscript ${D}${sysconfdir}/init.d/bootscript
}
FILES_${PN} += "${sysconfdir}/*"
Here I am loading the hello_world.elf created in Vitis into /usr/bin directory in the rootfs, and I am placing the bootscript in the init.d directory.
Note: Users can change the initlevel of the bootscript. For example:
INITSCRIPT_PARAMS = "start 99 5 . stop 20 0 1 6 ."
In this example, the script has a runlevel of 99, starts the script in initlevel 5, and stops the script in levels 0, 1 and 6. See here for more info
My bootscript looks like:
#!/bin/sh
echo "Executing User ELF"
./usr/bin/hello_world.elf
Then just build the petalinux image and boot. You should see your app running automatically:
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.