top of page

MicroZed Chronicles: UIO Part One - Introduction and Set Up

Last week we looked at how we could use spidev and I2Cdev to work with devices which use SPI or I2C interfaces. These are commonly used interfaces for a range of devices (e.g., ADCs, DACs, and sensors).

Of course, one of the things we want to do with PetaLinux is work with the custom logic functions in the PL. There are two ways we could create a driver for our custom PL module. We can do this be either creating a kernel module and running from kernel space or creating a driver in the user space area.


Using PetaLinux, we can create a kernel module and load it into the kernel. However, when we work within the kernel space, we have access to all of the hardware resources and memory. If we make mistakes in the development of that kernel driver, we can easily crash the entire kernel and the operating system. It is also much more difficult to prove the kernel module is correct under all use case conditions and to debug issues which might arise.


The second option we have is to develop the driver in the user space. The userspace driver does not have access to the underlaying hardware directly, but accesses them via system calls to the kernel. Most importantly, when we get it wrong in the user space, we will only crash the application and not the entire operating system. It is also much easier to debug applications within the user space.


The most common method of creating a driver in the user space is to use Userspace IO (UIO). UIO is a kernel driver which can be accessed from user space and provides user space with safe access to the device memory and interrupts.


One of the benefits of UIO is the ability to handle interrupts. In FPGA and SoC solution embedded systems, we can be sure that the interrupt pin we are connected to is dedicated to our peripheral. This makes interrupt handling much simpler and means that a kernel module to support the particular UIO instance is not required because we are using uio_pdrv_genirq (the generic interrupt handler) which simply disables the interrupt when it occurs. We can detect the interrupt in our user space application, process it, and then re-enable the interrupt.


For this example, I am going to add in two GPIO blocks to the design we a created last week and connect them to the AXI network.

In the device tree, we will declare the AXI_GPIO as compatible with UIO and will also enable a new boot argument to support interrupts.

/include/ "system-conf.dtsi"
/ {
    chosen {
        bootargs = "uio_pdrv_genirq.of_id=generic-uio";
    };
};
	&spi0 {
		status = "okay";
		spidev@0 {
			compatible = "rohm,dh2228fv";
			spi-max-frequency = <50000000>;
			reg = <0>;
		};
	};
	
	&spi1 {
		status = "okay";
		spidev@0 {
			compatible = "rohm,dh2228fv";
			spi-max-frequency = <50000000>;
			reg = <0>;
		};
	};

	&axi_iic_0 {
		clock-frequency = <100000>;
		status = "okay";
	};

	&axi_iic_1 {
		clock-frequency = <100000>;
		status = "okay";
	};

	&axi_iic_2 {
		clock-frequency = <100000>;
		status = "okay";

	};
	&axi_gpio_0 {
    	compatible = "generic-uio";
	};
	&axi_gpio_1 {
    	compatible = "generic-uio";
	};

We do not need to make any changes outside of the device tree and reimport the exported XSA into in our PetaLinux project.


With the changes completed, we are then able to rebuild the project and package the boot.bin. To check the changes we have made to the Vivado design and PetaLinux OS, we can copy the boot.bin and image.ub to the SD Card and boot the ZUBoard.


Once the board is booted, we can log in to the ZUBoard and examine the UIO devices which are available under /sys/class/uio using the serial terminal.

Under this directory you will see five UIO devices. The first three devices relate to the AXI performance monitor, while the final two relate to the AXI GPIO we just added into the design.


Now let’s discuss how to identify how each UIO instance is allocated.


If we examine each of the UIO instances, we will observe several elements including name, power, and maps.


By running the command cat name in the terminal, we will be given the name of the instance the UIO represents.

We will also see the name, address, offset and size by looking under the map directory.

You will notice that the address matches the one defined within our Vivado design.

We can also check that the interrupts for the AXI GPIO have been detected correctly by running the command cat /proc/interrupts

In the list of interrupts, we will see two GPIO entries along with the number of times this has been triggered.


Using devmem, we can set up the AXI GPIO interrupt enable registers so that we can see the interrupt being raised when we press the PL push button switch.


sudo devmem 0x0a03011c 32 0x80000000

sudo devmem 0x0a030128 32 0x3


Pressing the PL button will cause an interrupt to be raised.

We can also then use devmem to write to the GPIO which controls the tri state LEDs and illuminates them.


For the first LED which is connected to the second port of the first GPIO


sudo devmem 0x0a030008 32 0x1


This will turn the LED blue while the 0x2 will turn it green. The 0x4 will turn it red and, of course, 0x7 will make the LED white.


For the second GPIO, we need to set the address to 0x0a040000. Again the same patterns can be used for the different colors.


So far we have looked at how to update the device tree when working with UIO, verified that the UIO instantiations are detected correctly, and have used devmem to verify the GPIO behavior and interrupt functionality.


In our next blog, we will examine how we can create a software application which uses UIO to interact with the registers in the AXI GPIO and also the GPIO Interrupt.


All of the files necessary to recreate this, are located here


Workshops and Webinars


If you enjoyed the blog why not take a look at the free webinars, workshops and training courses we have created over the years. Highlights include


Embedded System Book Do you want to know more about designing embedded systems from scratch? Check out our book on creating embedded systems. This book will walk you through all the stages of requirements, architecture, component selection, schematics, layout, and FPGA / software design. We designed and manufactured the board at the heart of the book! The schematics and layout are available in Altium here Learn more about the board (see previous blogs on Bring up, DDR validation, USB, Sensors) and view the schematics here.



Sponsored by AMD



bottom of page