PetaLinux is probably the go-to OS when working with Zynq devices. It makes life easy because it is built for these chips and comes with plenty of pre-configured Yocto layers that take care of most of the setup for you. But here is the catch: AMD itself has said that PetaLinux is not meant to be a production OS — it is really designed for prototyping and testing.

Ubuntu is also available, though only for some development kits — including all the Kria boards. In this article we will take a look at how, even running Ubuntu, you can configure and control the PL on a KR260 kit in a pretty straightforward way.

Table of Contents

Connecting to the KR260 via SSH

First, we need to get ready the KR260 kit. We need to download the Ubuntu 24.04 image for Kria kits from the Ubuntu webpage. Then, using ‘dd’ if you are on Linux, or an application like Balena Etcher if you are running Linux or Windows, you will need to generate the SD card needed for the Ubuntu booting on Kria.

Once your Ubuntu distribution has booted, from your host you can check if the SSH connection is ready. This is important because we will need a way to send files to the KR260 kit, and the scp command, based on SSH protocol, is the best way to do this.

ubuntu@host:~$ ssh [email protected]

When the communication is ready, we can start with some comprobations.

Checking xmutil

The Ubuntu image for KR260 already includes xmutil. You can confirm by running:

ubuntu@kria:~$ sudo xmutil --help

xmutil makes it easy to install acceleration applications, since it works with .xclbin files that contain both the FPGA bitstream and the device tree overlay.

By default, some example applications are already available:

ubuntu@kria:~$ sudo xmutil listapps

However, it is very focused on acceleration applications, like the ones we will generate using Vitis AI, or Vitis HLS. In this case, we have our .bin file, and our driver, so there we are not going to follow the acceleration flow described by AMD, so fpga-manager will be a better option in this case.

Installing fpga-manager

FPGA manager is not installed by default in the Ubuntu image, but it can be added easily from the official Ubuntu repository installing the package fpga-manager-xlnx. First, update the package index and then install:

ubuntu@kria:~$ sudo apt update
ubuntu@kria:~$ sudo apt install fpga-manager-xlnx

Now, we can check that the command fpgautil is available.

ubuntu@kria:~$ fpgautil --help

Checking available UIO devices

Now, we are going to execute a sanity check. The peripheral we are going to add to the PL will be registered from Ubuntu as a peripheral without a driver, so the addresses of the peripheral will be accesed by the userspace. This will be very important because, in general, Ubuntu does not allow to access directly to memory addresses, so we need to add in the Device Tree a userspace I/O peripheral.

Initially, Ubuntu already have some UIO peripherals, and we can find them in the /dev folder.

ubuntu@kria:~$ ls -l /dev/uio*
crw------- 1 root root 237, 0 Feb 21 21:18 /dev/uio0
crw------- 1 root root 237, 1 Feb 21 21:18 /dev/uio1
crw------- 1 root root 237, 2 Feb 21 21:18 /dev/uio2
crw------- 1 root root 237, 3 Feb 21 21:18 /dev/uio3

In this case we already have 4 UIO perpherals. When we add a new UIO peripheral, it must appear here.

Generating the Device Tree overlay

We already mentioned that our PL peripheral will be registered by Ubuntu as a Userspace I/O one. To make Ubuntu recognise that set of addresses as the ones that manage the peripheral, we need to add to the device tree a new node. In a Petalinux build, we will add it to the system-conf.dtsi. However, here we already have a LInux distribtion built and running, so we can’t modify the device tree as usual. In this case we need to apply an overlay.

A Device Tree overlay is a fragment of the Device Tree that can be applied dinamically over the (original) Device Tree. It a way of “installing” new peripherals in the Linux distribution.

In this overlay, we need to especify the type of peripheral we are going to add, and according this type, we will need to add some other fields. For example, in out case we are going to add a generic-uio peripheral, so we need to add also the address range in which it will be installed, and the initial status of the peripheral.

If your peripheral is not a generic-uio, you can check the different bindings you can add, and the structure you need to follow to add your peripheral type.

Since the overlays have a very specific structure, with some key words at the beginninf, I just ask ChatGPT to generate a device tree overlay for a generic-uio device, with the corresponding address range.

/dts-v1/;
/plugin/;

/dts-v1/;
/plugin/;

/ {
    fragment@0 {
        target-path = "/axi";
        __overlay__ {
            axi_pwm_wrapper: axi_pwm_wrapper_0@a0000000 {
                compatible = "generic-uio";
                reg = <0x0 0xA0000000  0x0 0x0001000>; // 64 KB
                status = "okay";
            };
        };
    };
};

At this point, we have the overlay, but it is not compiled yet,we just have the font code of the overlay.

Sending the bitstream and DTS to the board

Your Vivado design flow will generate a .bin bitstream , and the .dts file (device tree source) is the one generated before. Now, from the host machine, we transfer them to the KR260:

pablo@host:~$ cd your-bin-and-dts-folder
pablo@host:~$ scp ./* [email protected]:.

Also transfer the Python script that will control the IP:

pablo@host:~$ cd ../python
pablo@host:~$ scp ./* [email protected]:.

On the KR260, confirm the files are there:

ubuntu@kria:~$ ls
axi_pwm.dts  axi_pwm_driver.py  krind_lab02_bd_wrapper.bin

Generating the Device Tree Overlay

Now it’s time to compile the .dts into a .dtbo overlay. Ubuntu for Kria already include the dtc application. This application will compile the dts file, the source file of the device tree overlay, into a compiled device tree overlay (dtc).

The structure of the dtc command will be shown by executing the command without arguments. In general, the command with the corresponding arguments will be the one you can find here.

ubuntu@kria:~$ dtc -@ -I dts -O dtb -o axi_pwm.dtbo axi_pwm.dts

Now, we alread have the overlay ready to be installed into Ubuntu.

Loading the PL and applying the overlay

The next, is to load the design of the PL, and load the device tree overlay.

The fpgautil application allows to load the PL and the device tree overlay in the same line, however I ran into several issues when I try to do it. My recommendation is to execute two different commands, the first one to load the PL, and another one to load the device tree overlay.

To load your bitstream, you can execute the next command.

ubuntu@kria:~$ sudo fpgautil -b ./krind_lab02_bd_wrapper.bin -f Full

When you load the PL of the KR260, the board’s fan will spin at maximum speed — this is expected, since the default Ubuntu design includes fan control logic.

Then, we can apply the overlay:

ubuntu@kria:~$ sudo fpgautil -o axi_pwm.dtbo

At this point, we have our peripheral and driver ready to be used.

Verifying the new UIO device

Since the peripheral is a generic-uio type, it will be added to the UIO devices of Ubuntu. We can check it by listing again the UIO devices.

ubuntu@kria:~$ ls -l /dev/uio*
crw------- 1 root root 237, 0 Feb 21 21:18 /dev/uio0
crw------- 1 root root 237, 1 Feb 21 21:18 /dev/uio1
crw------- 1 root root 237, 2 Feb 21 21:18 /dev/uio2
crw------- 1 root root 237, 3 Feb 21 21:18 /dev/uio3
crw------- 1 root root 237, 4 Feb 21 21:18 /dev/uio4

We can see that a new /dev/uio4 has been added. We can check also whether it is our device or not by checking the address of this new UIO.

ubuntu@kria:~$ cat /sys/class/uio/uio4/maps/map0/addr
0x00000000a0000000

Now we can confirm that the new UIO added is located in the address of our device. Also we can check the size of the recerved memory by readint the size of this new device.

ubuntu@kria:~$ cat /sys/class/uio/uio4/maps/map0/size
0x0000000000001000

Running the Python driver

Finally, you can interact with your custom IP from user space using your driver written either using Python or C.

ubuntu@kria:~$ sudo python3 ./axi_pwm_driver.py 10000 50

This will write to the memory-mapped registers of your IP through the UIO interface, controlling the peripheral directly from Linux.

Conclusion

From my perspective Ubuntu is not the best OS for a Kria SOM - it is too close. While reconfiguring the PL, fairly straighforward, if you want to lod the RPU using remoteproc, the thigs get a bit more complicated. The reason is that, by default, the RPU is not recognized by the kernel, so you would need to modify it in order to make the RPU accesible.

If you want to keep all the control over your Kria SOM, the best option is build your own Linux, either petalinux, or a custom distribution using Yocto. However, the example presented in this post shows how, even using a regular Linux distribution, we can integrate hardware accelerators in a very simple way.