GPIO and Petalinux - Part 3 (Go, UIO, Go!)

GPIO and Petalinux - Part 3 (Go, UIO, Go!)

This is part 3 of the GPIO and Petalinux series of tutorials, aiming at hobbyists and/or professionals, working with Embedded Linux.?

In Part 1 I've started with the basics of linux Kernel and Petalinux, overviewed the various options to read and write to/from GPIO, then headed towards UIO method.

In part 2 I've designed an example design with the good old Zed board. I've explained about the device tree and added support for UIO driver. The article was finished just before creating the Petalinux image,

Now in part 3, let's wrap it up, sending the following command:

petalinux-build        

and after few minutes we'll receive the following message in our console:

No alt text provided for this image

which means all files are now located at 2 folders:

  1. /tftpboot - at the root folder, created after the build is finished.
  2. [project-folder]/UIO_wIRQ/images/linux - inside our main project folder.

Both folder are identical and it's up to the user to decide from where to package the image files.

Packaging the image file

Using the following commands, we'll package the files needed in order to create the image.ub, boot.bin and our rootfs files (where all our filesystem is located at):

cd /fpga/projects/linux_images/UIO_wIRQ_PL/UIO_wIRQ/images/linux
petalinux-package --boot --fsbl zynq_fsbl.elf --u-boot --fpga system.bit --force        

One can also create these files manually, but it is much easier do it through Petalinux commands (even a must, as there are many posts at Xilinx forums that undergoing this way manually not always works as expected).

The petalinux-package command simply takes the relevant files, all marked with double dash, in order to create the following files:

BOOT.bin - this is a compiled u-boot OS in binary format (both the first stage and second stage boot loader, as well as the bitstream, among other components).

image.ub - The Petalinux image which consists of kernel image, device tree blob and minimal rootfs.

rootfs.tar.gz - this is the most basic component of Linux. It contains all the applications (e.g., our main application, which runs at startup), devices, configurations, etc.

UG1157 has a lot of interesting info about petalinux-package and I recommend to go over it.

Important to note that the above command is used for booting from an SD card. In the case of using a flash to boot from, we'll need to alter this command and add also the kernel (this cab be dealt in a later tutorial).

Anyway, we've now reached the finish line and ready to place those 3 files in our SD card.

well, as always, not so fast...

copying Petalinux files into the SD card

We'll first need to partition the SD card with 2 (or more) partitions. I've explained it thoroughly in my Hackster project page, where I wrote about a method for creating a dynamic-static IP (worth reading...). So, I'll not go over it again here.

Once the files are located at the correct folders, we can power on the board and Linux should power up.

Checking UIO

Once we're in, the next step is to validate we have the UIO driver installed, looking at the "/dev" folder which contains the installed Drivers:

No alt text provided for this image

We can also see it under /sys/class/uio, where other interesting stuff is hidden:

No alt text provided for this image

The 'maps' folder include all address mapping info regarding the component we used. We can go over the various parameters and see some familiar addresses, size, etc (from part 2 my tutorial).

No alt text provided for this image
No alt text provided for this image

All this clearly shows us the UIO was indeed installed correctly.

Checking UIO is functioning correctly

This is the most interesting part, so I hope you all wide awake. If not, by all means, get yourself a strong cup of coffee, cause the fun begins and it is a bit complicated.

For testing the UIO, I will generate an interrupt in the Zed board by hitting the push button and I expect to see the interrupt counter increased at CPU side. In Linux it is located at /Proc/Interrupts.

/Proc/Interrupts file
Interrupts to CPU allows devices like keyboards, mouse, pushbutton (in our case) to signal the CPU it wishes to talk with it.
On Linux we have a file which holds that info, including number of times the CPU was informed, the type of interrupt raised and more. This file is located at /Proc/Interrupts

I will use the AXI GPIO IP block for that matter (the projects are in my Github page) and as a reference this link helped me a lot so I recommend go over it also.

For reaching the various AXI GPIO registers I'll use the devmem2 application . It is a small application very useful for reading and writing to almost any available address . This is a very handy tool for developers before you have a fancy GUI for that matter, or other form of user interface.

From this table taken from the AXI GPIO block manual (PG144):

No alt text provided for this image

Setting these registers turn on the corresponding LEDs connected to GPIO2 in my design:

No alt text provided for this image

Now, we’ll try to see the interrupt is incremented:

According to the below picture, we’ll write to the correct registers:

  1. Enable channel interrupt:

devmem 0x41200128 w 0x1 #for GPIO channel 1
devmem 0x41200128 w 0x2 #for GPIO channel 2

devmem 0x41200128 w 0x3 #for GPIO both channels        

2. Enable global interrupt:

devmem 0x4120011c w 0x80000000 #for both GPIO channels        

Now, before we hit the pushbutton to set the interrupt, let’s look at /proc/interrupts:

No alt text provided for this image

We can see here the 2 CPU's (Dual-core ARM Cortex-A9 in the case of the Zedboard), the trigger type ('level triggered' or 'edge triggered'), the functionality and the interrupt number.

Pay attention that at the 'gpio' interrupt value the interrupt number is 61:

46:? ? ? ? ? 1? ? ? ? ? 0 ? ? GIC-0? 61 Level ? ? gpio

Why is it 61? back in part 2, where I've explained about the device tree, the number was 29! That is because 29 + 32 = 61 (as explained, the kernel device tree parser adds 32 to the IRQ id).

Now, we’ll click a pushbutton (any button) and the interrupt will fire. We can see that in the interrupt file. The counter has increased by 1:

No alt text provided for this image

Reading this address shows the Interrupt status register in the AXI GPIO:

devmem 0x41200120

> 0x00000001        

Which means an interrupt has occurred. Writing 0x1 to the same register toggles the status of the bit:

devmem 0x41200120 w 0x1

> 0x00000000        

The above scenario is based on the AXI GPIO manual:

No alt text provided for this image

Clearing the UIO interrupt

An interesting issue is related to clearing GPIO line at /cat/interrupts. After the interrupt has fired, it is ‘stuck’ at ‘1’.

How do we clear it?

UIO-How-to explains:

No alt text provided for this image

So, we need to clear this bit, via source code, or any other method.

From the kernel UIO driver website:

No alt text provided for this image

So, I'll echo the UIO with '1' to clear the interrupt:

echo 0x1>/dev/uio0        

But strangely, the interrupt counter at /proc/interrupts increments all the time. Why is that?

well, this took me a while to find out, and the reason is AXI GPIO supports only level triggered interrupts. The Disable bit was cleared indeed, but the interrupt level is still high (since it is defined as ‘Level'), and the interrupt counter keeps incrementing.

Going back to Vivado

Figuring out my design is wrong, since the GPIO AXI is level triggered and I cannot clear the UIO interrupt, I went back to the PL and decided to design a new project, where I have an additional interrupt source, and this time, I will design it as 'Edge triggered':

No alt text provided for this image

I’ve deleted the push_buttons port and defined my own ports, per the locations stated in the Zedboard manual (button_center, button_left, etc).

Tip #1
The custom_io block is a modified version of the “Tools → create and Package New IP…”. A nice trick to avoid the cumbersome methodology of using the packaged IP in a new project (using right click → “edit in IP packager”) is to copy the code inside the created IP into a new RTL module (save it with a new name), then add it to the BD using Add Module (right click in BD). This is actually what I did, thus the RTL letters are shown inside the block.
No alt text provided for this image

In my vhd code I created a simple pulse (i.e., interrupt) whenever the user clicks one of the buttons.

Tip#2
?to define the interrupt as a ‘rising_edge’ type, I’ve added these lines to my vhd code:
attribute X_INTERFACE_INFO : string;

attribute X_INTERFACE_INFO of interrupt_out : signal is "xilinx.com:signal:interrupt:1.0 interrupt_out INTERRUPT";

attribute X_INTERFACE_PARAMETER : string;

attribute X_INTERFACE_PARAMETER of interrupt_out : signal is "SENSITIVITY EDGE_RISING";        

This makes the pin ‘interrupt_out’ an interrupt type signal of rising_edge:

No alt text provided for this image

So, the design now includes 2 sub-IP’s; my custom IP, which has an 'interrupt out' signal of rising_edge type, and the AXI GPIO original block which has an interrupt of type ‘level’.

Since I've 'concat'-ed both interrupts this is clearly shown in the BD:

No alt text provided for this image

Last, but not least, is to change the device tree, and add to system-user.dtsi the new created custom IP:

/include/ "system-conf.dtsi"
/ {

????gpio@41200000{

????????compatible = "axi_gpio_0, generic-uio, ui_pdrv";?

????????status = "okay";

????????};


chosen {??????

bootargs = "console=ttyPS0,115200 earlyprintk uio_pdrv_genirq.of_id=generic-uio";

		};?
??
};

&axi_gpio_0

????????{

????????????compatible = "generic-uio";

????????};


/* adding my new custom rising edge triggered IP*/

?&custom_IO_v1_0_S00_A_0

????????{

????????????compatible = "generic-uio";

????????};
        

Now, next steps are similar (but not exact) to what I did before:?

  1. Compiling the project
  2. Export hardware (include bit).
  3. petalinux-config --get-hw-description=<project>/<project.sdk, then double-esc to exit (the relevant files will be re-created with the new HW files).
  4. Petalinux-build (with the new updated dtsi files).
  5. Copying to SD card BOOT and DATA files.

Testing the UIO after rising edge trigger

In the first push button, examined the /proc/interrupts, to test the new interrupt type. We can see we have 2 IP's, our original AXI GPIO ('Level') and our new custom trigger source ('Edge'):

No alt text provided for this image

Then clearing the interrupt using:

echo 0x1>/dev/uio1 # --> since uio1 is my custom IP, while uio0 is the AXI GPIO.
        

Question arises, how do we know to which 'uio' should we write? we now have 2 UIO's!

TIP#3
Go to /sys/class/uio.
For example, see here from a different project we have various components, all UIO drivers based. Each one has its own UIO index. The key is to compare it with the system-conf-dtsi file:
No alt text provided for this image

we'll compare it with system-conf.dtsi file:

No alt text provided for this image

We'll follow the addresses on the dtsi file and compare the index of the UIO.

Back to our project, Clicking the button again:

No alt text provided for this image

Then clearing the interrupt using:

echo 0x1>/dev/uio1 # --> since uio1 is my custom IP, while uio0 is the AXI GPIO.
        

Clicking the button again:

No alt text provided for this image

Vuala! Exactly what I wished for!

To sum it all up:

I've started with a design based on GPIO AXI for triggering an interrupt. This indeed worked well, but I could not disable the interrupt and the result was the interrupt counter at the /proc/interrupt kept increasing since the AXI GPIO supports only level triggered interrupts. Next, I've added my own interrupt trigger and made it rising edge one. It did worked flawlessly. This should remind us that we better use rising edge trigger type rather than Level.

And lastly, as a work of caution:

One may have other solution (maybe even simpler one) for this task, but from my point of view, this indeed did the trick...

Now I can say: Q.E.D!

Aleksey Kuchkov

Saint Petersburg National Research University of Information Technologies, Mechanics and Optics

5 个月

Thank you for the tutorials. They helped me a lot to understand uio.

回复
Eric Farrow

Principal Design Engineer , New Space Systems

2 年

Awesome series Thanks

Krishna Chaitanya

Computer Achitecture Performace modeling expert

2 年

Please please write tutorial on how to send signal from uio driver ......

回复
Christopher Smith

Embedded Software Engineer at Space Ground System Solutions

3 年

Great tutorials! They helped me out a lot. I found that I could use the AXI GPIO level interrupts as is; no edge needed. All I needed to do was read the AXI GPIO interrupt status register and write that value back to the interrupt status register to clear the current interrupt. Then a proceeding interrupt would be acknowledge (counted once). Each time a GPIO interrupt event occurred I would: read/use the GPIO value, read the interrupt status register, write the interrupt status register, wait for the next interrupt event, repeat the process. Hope that helps someone. Thanks again.

Baha Hashimi, PhD

Embedded Systems Specialist at Avnet

3 年

Great tutorials for demystifying hardware using UIO very useful in early stages of FPGA IP development when driver is not developed.

要查看或添加评论,请登录

Roy Messinger的更多文章

  • GPIO and Petalinux - Part 2

    GPIO and Petalinux - Part 2

    This is part 2 of the GPIO and Petalinux series of tutorials, aiming at hobbyists and/or professionals, working with…

    13 条评论
  • GPIO and Petalinux - Part 1

    GPIO and Petalinux - Part 1

    When I first started looking into Petalinux and learn the basics, I thought I should start my journey with a simple…

    4 条评论
  • Xilinx DMA PCIe tutorial-Part 3

    Xilinx DMA PCIe tutorial-Part 3

    In part 1 of my tutorial I've gone over the basic issues related to DMA. In part 2 I dove dipper and gave my two cents…

    8 条评论
  • Xilinx DMA PCIe tutorial-Part 2

    Xilinx DMA PCIe tutorial-Part 2

    In part 1 of my tutorial I've gone over the basic issues related to DMA. I covered the various solutions applicable…

    5 条评论
  • Xilinx DMA PCIe tutorial-Part 1

    Xilinx DMA PCIe tutorial-Part 1

    This document is a thorough tutorial on how to implement a DMA controller with Xilinx IP. My idea was to write a…

    9 条评论
  • Arm based controller - bootcamp course

    Arm based controller - bootcamp course

    An extensive presentation from a course I've developed and taught about ARM architecture and microcontrollers (NXP in…

  • Xilinx vs Intel (Altera) FPGA performance comparison

    Xilinx vs Intel (Altera) FPGA performance comparison

    You're welcome to check out this interesting comparison I've carried out between these 2 vendors. Very interesting and…

    10 条评论

社区洞察

其他会员也浏览了