When working with standalone/bare-metal FPGA design, you might often be familiar with writing the SDK application. Obviously, you are familiar with Vitis IDE, which is one of the feature-loaded software platforms. It can be used not only for standalone or free-rtos applications but also for Linux applications. This design presents how you can leverage Vitis IDE to write, run, and debug Linux UIO (User Input/Output) Applications for the FPGA design. I created an FPGA design with a custom Vitis HLS IP to demonstrate this. Obviously, you can have your custom design after completing this. For the current design, I am going to use ZedBoard and Vivado/Vitis/Petalinux 2021.1 version.
To begin with our design, we create a hardware design, a petalinux project, and a linux application project.
Hardware ProjectHere, we use Vivado Tool to create a hardware block design, which has a few hardware blocks, for example, Zynq Processing System and custom led_ctrl Vitis HLS IP, as shown below.
In this design, the HLS output pins are connected to two onboard LEDs, which will be controlled from the UIO application.
Once a hardware design generation is completed, we export our hardware design as an XSA file and then we can begin Linux project creation.
Petalinux ProjectI have created a BSP Petelinux project. You can find the BSP file here. Here, we create the Petalinux project in the following manner.
- Creating BSP Project
petalinux-create -t project -s /<BSP_Directory>/avnet-digilent-zedboard-v2021.1-final.bsp -n /<Prj_Directory>/zedboard_uio_plnx_21_1
Note: You need to specify BSP Directory, Project Directory and Project Name. In this design, I have set the project name as "zedboard_uio_plnx_21_1"
- Configuring the project with our hardware design
petalinux-config --silentconfig --get-hw-description=/<Vivado_Prj_Dir>/zed_uio_led_wrapper.xsa
Note: You need to specify VivadoProject Directory and XSA file name. In this design, I have zed_uio_led_wrapper.xsa file.
- YOCTO_MACHINE_NAMEsetting
As we are making a BSP project, the machine name must be auto-set, as shown below. Otherwise, we need to specify it as shown below.
- Image Packaging Configuration
petalinux-config
Image Packing Configuration>
Make sure Root Filesystem Type is set into INITRD and Image name petalinux-image-minimal and then save and exit.
- Kernel Configuration to enable UIO driver
petalinux-config -c kernel
Device Drivers>Userspace I/O drivers
- system-user.dtsi Update
Adding UIO boot arguments.
chosen{
bootargs = "uio_pdrv_genirq.of_id=generic-uio";
}
Making HLS IP UIO Driver Compatible
&led_ctrl_0{
compatible="generic-uio";
};
To enable ethernet GEM, add the following node.
&gem0 {
status = "okay";
};
- No need to do RootFS configuration in the current design
- Project Build and Generate Boot Files
Build the project
petalinux-build
Generate the boot files after a successful build
Go inside zedboard_uio_plnx_21_1>images>linux directory and enter the following command to generate boot files.
petalinux-package --boot --fsbl ./zynq_fsbl.elf --fpga ./system.bit --u-boot ./u-boot.elf --force
- Load Boot Files and run the design
From zedboard_uio_plnx_21_1>images>linux directory, copy the following files into the SD Card (must be FAT32 format).
>boot.scr is the script that U-Boot reads during boot time to load the kernel and rootfs
>image.ub contains kernel image, device tree and rootfs.
>BOOT.BIN contains boot files, e.g., a first-stage boot loader image, FPGA bitstream, and U-Boot.
Before running, make sure the board configuration is in SD mode by adjusting the jumper as shown below.
In most Zedboards, the configuration switches are already set in SD Mode, as illustrated by following the Zedboard Configuration Mode Table from the Zedboard Hardware User Guide.
Make sure you have connected ethernet.
After booting up, you should see boot messages without any issues. You should get the command line after auto-login. This is because, in the rootfs configuration, the Image Features > auto-login has already been selected. You can also disable it for your choice and you have to enter the login information for each boot up.
- Check UIO Device Driver Status
After boot-up, we need to make sure whether the UIO driver for our HLS IP has been loaded or not in the following manner.
avnet-digilent-zedboard-2021_1:~$ cd /sys/class/uio/
avnet-digilent-zedboard-2021_1:/sys/class/uio$ ls -alh
total 0
drwxr-xr-x 2 root root 0 Jan 1 1970 .
drwxr-xr-x 49 root root 0 Jan 1 1970 ..
lrwxrwxrwx 1 root root 0 Mar 9 12:35 uio0 -> ../../devices/soc0/amba_pl/40000000.led_ctrl/uio/uio0
Our device is registered with uio0 identity. This will be used while writing the application.
Vitis Project: UIO ApplicationHere, we use Vitis IDE to create a Linux UIO application. The Vitis always creates an application on the top of a platform. We create applications in the following manner
- Creating Platform Project
We create a new platform project in this step. We also need to give the platform project a name. I have given "zed_plfm"
We need to locate the XSA file in the Hardware Specification field. On the other hand, we need to select Linux Operating System in the Software Specification field and click finish as illustrated below.
You should see the recently created platform project in the Explorer Pane, as shown below. We need to build it first.
- Creating Application Project
Here, we begin creating an application using "Linux Empty Application" template project.
The recently built platform project should appear in the repository list, as shown below. We go ahead by selecting our platform project.
We give the application name. In my case, I have given the application name "zed_uio_led_app". We go ahead until we get the following application template selection wizard. We finish by selecting "Linux Empty Application".
You should be able to see the following projects in the Explorer Pane.
We create a new.c file in the application project. In my case, I have created "led.c" file.
In the led.c file, we write the UIO application. The code is very simple here for demonstration purposes. The code is written in such a way that it opens our UIO device, maps it into user space, and then we write data to control the LED. You can find the whole code in the Code section of this article.
We need to take care of selecting the proper UIO device ID as we noted in the previous sections.
char *uiod = "/dev/uio0";
fd = open(uiod, O_RDWR);
if (fd < 1) {
printf("Invalid UIO device file:%s.\n", uiod);
return -1;
}
At this moment, we only have one UIO device with uio0 ID. So, there is no problem. The above code opens this device and gets all of its information, for example, the most important information is the physical address, also known as the base address of the IP, by which the registers of the IP can be accessed.
We need to do memory mapping into user space by following the coding
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;
}
It returns a pointer on which we update the value. This value will be written into the register of the IP. The following code will run in the loop for four times, where the LED value is incremented by 1 and then written to the device register.
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++;
}
If you remember, we are controlling two LEDs, which, in this design, are controlled in the manner of 00, 01, 10, and 11 for the demonstration. Obviously, you can have your own way of controlling the LEDs.
Once we have coding, we have to build it.
Application RunningTo program on the board, make sure the board is already booting up and connected to ethernet. The ethernet connection is very important here because Viti IDE communicates with our board through the TCF (Target Communications Framework) Agent.
Starting tcf-agent: OK
When Linux boots up, it launches TCF Agent automatically and we get the information in the boot message as shown above. Now, we load our design through this
To open Linux Agent in the Vitis IDE, go to the menu bar
Window > Show View > Xilinx > Target Connections
Double Click on Linux Agent
Enter your board IP address. You can also do a Test Connection for the connection verification.
Now, like you launch the design in the bare metal, you exactly follow the same ways here.
ApplicationProject > Right Click >Run As > Run Configurations > New Configuration > Run
Once the design is successfully loaded, you get the message (UART-like) in the Vitis Console, as shown below. This message is again coming through TCF Agent.
Meanwhile, If we check the board, you should get the output below.
For the application debugging, we use exactly the same debugging mode, as we do in the bare-metal application debugging. In the case of our Linux application, the Vitis IDE uses the same TCF agent.
To debug the design, go to
ApplicationProject > Right Click >Debug As > Debug Configurations > New Configuration > Debug
You can now debug your code line by line. You can also monitor variable status and many more.
ConclusionHere, I have illustrated writing a simple UIO application and then loading it on the ZedBoard using Vitis IDE to control onboard LEDs through our custom HLS IP and finally, debugging our application using Vitis Debugger. You can write your own application to do more things ahead. In the next design, I will bring up another UIO application example to do FPGA image/video processing. Stay tuned!
Don't forget to Follow, Like, Share, and Comment.
Comments
Please log in or sign up to comment.