Jump to content
  • 0

UIO access including interrupts when using Petalinux


juergenR

Question

Past two weeks I fighted to get a simple linux app running being able to read/write to an AXI GPIO IP using interrupts for the inputs. There a quite a bunch of links out there, each describing a part of the problem. But glueing all together into one system wasn't that easy as expected. Goal of that post is to summarize the things and open a discussion.

1. Systems used
- Vivado 2017.4, SDK 2017.4 (Windows), Petalinux Tools 2017.4 on Ubuntu 16.04LTS running as VM
- Arty Z20 as hardware platform

2. Block diagram

grafik.thumb.png.8b1a613b7e601116a64ccd18a5efc09f.png

axi_gpio0 --> 0x41200000, 4 out, 4 in connected to LEDs and push buttons
axi_gpio1 --> 0x41210000, 16 out, 4 in on chipKit shield pins

3. Create Linux system

Strightforward: import hardware into project, build and package. Don't forget to clean your project when you copied it from an existing one.
Modified system-user.dtsi:

/include/ "system-conf.dtsi"
#include <dt-bindings/gpio/gpio.h>
/ {
	model = "Zynq Arty Z7 Development Board";
	compatible = "digilent,zynq-artyz7", "xlnx,zynq-7000";
	chosen {
		bootargs = "console=ttyPS0,115200 earlyprintk uio_pdrv_genirq.of_id=generic-uio";
	};
   ...
};
&axi_gpio_0 {
	compatible = "generic-uio";
};
&axi_gpio_1 {
	compatible = "generic-uio";
};
...

Running system then exposes uio0 and uio1 devices. So far the project wasn't difficult, problems arised when I went over to programming...

Please note, that for that SDK can find the generated libraries and include files, you have to copy the folder "<plnx-project>/build/tmp/sysroots/plnx_arm" into a Windows folder; the copy need to resolve all symbolic links (cp -R -L)!

EDIT 3.1.2020: The UIO also needs to be configured in the kernel. See here: https://xilinx-wiki.atlassian.net/wiki/spaces/A/pages/18842490/Testing+UIO+with+Interrupt+on+Zynq+Ultrascale.

This procedure also runs with later version of Vivado, SDK and PetaLinux Toos.

4. Create Linux application

Within Xilinx Wiki and in this Forum there are quite a bunch of useful posts; however, most of them just explain the principal way and neglect some details. Unfortunately, this turned down to that the program might work just once after reboot or events are not triggered even they occur or the supporting libs use other ways to access the data... Here's what I finally did:

  • Create linux project and connect to the copied libaries:

grafik.png.0996282232e14df4f60ac6360c3f391d.png 

  • Modify Linux gcc libraries and Miscellaneous settings (right click on project, then choose C/C++-Build Settings). Within "Miscellaneous" you have to set
    --sysroot="C:\VBox\Exchange\pl_libs\plnx_arm"

    grafik.thumb.png.7c2ba59cda9d6759f004e7d44912a144.png

Please note, that the library name is "uio" but the file name is "libuio.so". The latter will not be recognized.

EDIT 14.3.2020: "libuio" and "libgpio" are libraries provided by Digilent in their PetaLinux distribution. When creating a PetaLinux from template as described above, these libraries are not included and changing sysroot will not work: those files are not included in the created sysroot. However, this is not a problem: you just need to include those libraries directly into your application project. These libraries can be found here: https://github.com/mitchellorsucci/libgpio.

  • it turned out, that libgpio isn't helpful when using interrupts due to hiding the file handle and only exposing the memory pointer. However, the libuio works well within this context.
  • Set up of the UIO should be like this:
    // map to ports
    UIO *uio_h = UIO_MAP(uiod[0].uio_nb, uiod[0].map_nb);
    void *uio_m = uio_h->mapPtr;
	int uio_fd = uio_h->uio_fd;		//file handle for blocking read from uio

	// init ports
    writeGPIOPort(uio_m, GPIO_TRI, 0);				// all outputs channel 1;
    writeGPIOPort(uio_m, GPIO2_TRI, 0xFFFFFFFF);	// all inputs channel 2;
    writeGPIOPort(uio_m, GPIO_DATA, 0);				// all outputs set 0

    writeGPIOPort(uio_m, GIER, GIEn);				// global interrupt enable
    writeGPIOPort(uio_m, IER, 0x02);				// enable interrupts only on port 2 (inputs)
    uint32_t actIStat = readGPIOPort(uio_m, ISR);	// toggle ISR bits to '0'
    writeGPIOPort(uio_m, ISR, actIStat);
    write(uio_fd, &reenable_int, sizeof(int));		// enable catching interrupts within driver
  • catching interrupts within the program
	    writeGPIOPort(uio_m, GPIO_DATA, 1);				// set some output
		ires = pread(uio_fd, &evt_count, 0x04, 0);		// wait for int (blocking read)

		call some_action();

		actIStat = readGPIOPort(uio_m, ISR);			// toggle ISR bits to '0'
		writeGPIOPort(uio_m, ISR, actIStat);
		write(uio_fd, &reenable_int, sizeof(int));
  • shutting down at the end:
    ires = UIO_UNMAP(uio_h);

5. Explanations

  • we have to write the value "1" to the file descriptor to reenable interrupt handling within the linux driver (thanks to Yohboy for pointing me to this) - otherwise only one interrupt will be cought.
  • prior to writing to the file descriptor, the ISR must be reset. Otherwise the previous interrupt will be reexecuted.
  • pread will not forward the file pointer (I don't know if this is of relevance, I didn't test it again with read())
  • pread will block till a new interrupt arrives. This is usually a good way when executing the code within a separate thread. For non-blocking behaviour, you will need to use poll (as suggested by Yohboy) or the newer epoll functions. I tested both blocking and non-blocking behaviour and both worked well.

6. Performance

Of course Petalinux is no real-time OS, so we cannot expect too much especially from the interrupt behaviour. But I was interested in some figures to know the limits.

  1. Toogle output bit as fast as possible: I have been measuring a cycle time of 525ns - this is really fast! However, I did not try to measure any jitter on this.
  2. Interrupt delay: here much higher delays come into play as well as a strong jitter. The minimum delay was about 12us but could go up till 80us. This is still very fast and might catch every bounce on a push button, so for many processes interrupt handling might be useful instead of polling the inputs.

7. Closing Remarks

As written at the beginning, I want to summarize things and provide a solution (hopefully) ready to be run, especially for using interrupts on GPIO. The explained solution is based on proposals provided by several people around in the forums, at Digilent and at Xilinx, so thanks to all of them which gave me hints to proceed.

My solution will not be perfect, so any improvements ar welcome.

Cheers, Jürgen

gpio_test.c

Link to comment
Share on other sites

1 answer to this question

Recommended Posts

Archived

This topic is now archived and is closed to further replies.

×
×
  • Create New...