UPGRADE YOUR BROWSER

We have detected your current browser version is not the latest one. Xilinx.com uses the latest web technologies to bring you the best online experience possible. Please upgrade to a Xilinx.com supported browser:Chrome, Firefox, Internet Explorer 11, Safari. Thank you!

cancel
Showing results for 
Search instead for 
Did you mean: 
Highlighted
Visitor abhinav007
Visitor
1,595 Views
Registered: ‎09-25-2017

UIO interrupt error on Zynq 7020

Jump to solution

Hi,
I have implemented a simple UIO driver for AXI GPIO to be run on Zynq platform. A high-level sensitive interrupt is triggered for the processor in event of incoming data on the peripheral. I made the necessary changes in the device-tree to request the respective IRQ. In the userspace program, below is the code snippet which handles interrupt:

 

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/mman.h>
#include <fcntl.h>

// This program implements a UIO driver in user space using an AXI GPIO in
// the PL. It expects an interrupt to be connected from the PL. The only purpose
// of the program is to simulate an interrupt from a device in the PL and to
// show accessing OCM memory from the UIO driver.
//
// This code assumes the GPIO device in the device tree has it's compatible
// string to be "generic-uio" and "uio_pdrv_genirq.of_id=generic-uio" is used
// on the command line via bootargs when the UIOP driver (uio_pdrv_genirq)
// driver is statically built into the kernel (the default).
//
// OCM is also used with the UIO driver such that it can be read/written from
// user space. It is mapped as device memory by UIO.
//
// The device tree should remove the OCM driver from the kernel such that
// OCM is not used by any kernel functions such as power mgmt for DDR self
// refresh mode. The GPIO node now has 2 memory regions with the 1st being
// the normal registers while the 2nd is the OCM memory.
//
// This was tested on a 702 board using the push buttons to generate an
// interrupt (left or right).

// The following snippet is used for system-top.dts with Petalinux.

// /dts-v1/;
// /include/ "system-conf.dtsi"
// /{
// };
// /{
//     model = "702-axi-gpio-irq";
//     aliases {
//        serial0 = &uart1;
//        ethernet0 = &gem0;
//     };
//     chosen {
//        bootargs = "console=ttyPS0,115200 earlyprintk uio_pdrv_genirq.of_id=generic-uio";
//     };
//     memory {
//        device_type = "memory";
//        reg = <0x0 0x40000000>;
//     };
// };
// &ocmc {
//	 compatible = "";
// };
// &axi_gpio_0 {
//	 compatible = "generic-uio";
//	 reg = <0x41200000 0x10000 0xFFFC0000 0x20000>;
// };
//

#define GPIO_MAP_SIZE 		0x10000
#define GPIO_DATA_OFFSET	0x00
#define GPIO_TRI_OFFSET		0x04
#define GPIO_DATA2_OFFSET	0x08
#define GPIO_TRI2_OFFSET	0x0C
#define GPIO_GLOBAL_IRQ		0x11C
#define GPIO_IRQ_CONTROL	0x128
#define GPIO_IRQ_STATUS		0x120

inline void gpio_write(void *gpio_base, unsigned int offset, unsigned int value)
{
	*((volatile unsigned *)(gpio_base + offset)) = value;
}

inline unsigned int gpio_read(void *gpio_base, unsigned int offset)
{
	return *((volatile unsigned *)(gpio_base + offset));
}

void wait_for_interrupt(int fd, void *gpio_ptr, unsigned int *ocm_ptr, int ocm_size)
{
	int pending = 0;
	int reenable = 1;
	unsigned int reg;
	int sum = 0, i;

	// block on the file waiting for an interrupt */

	read(fd, (void *)&pending, sizeof(int));

	// the interrupt occurred for the 2nd GPIO channel so clear it

	reg = gpio_read(gpio_ptr, GPIO_IRQ_STATUS);
	if (reg)
		gpio_write(gpio_ptr, GPIO_IRQ_STATUS, 2);

	// read the OCM memory and sum it's contents

	for (i = 0; i < ocm_size / sizeof(int); i++) {
		sum += *(ocm_ptr + i);
	}
	printf("sum = %d\n", sum);

	// re-enable the interrupt in the interrupt controller thru the
	// the UIO subsystem now that it's been handled

	write(fd, (void *)&reenable, sizeof(int));
}

unsigned int get_memory_size(char *sysfs_path_file)
{
	FILE *size_fp;
	unsigned int size;

	// open the file that describes the memory range size that is based on the
	// reg property of the node in the device tree

	size_fp = fopen(sysfs_path_file, "r");

	if (!size_fp) {
		printf("unable to open the uio size file\n");
		exit(-1);
	}

	// get the size which is an ASCII string such as 0xXXXXXXXX and then be stop
	// using the file

	fscanf(size_fp, "0x%08X", &size);
	fclose(size_fp);

	return size;
}

int main(int argc, char *argv[])
{
	int fd;
	char *uiod = "/dev/uio0";
	void *gpio_ptr;
	int gpio_size;
	unsigned int *ocm_ptr;
	int ocm_size;
	int i;

	printf("GPIO UIO test.\n");

	// open the UIO device file to allow access to the device in user space

	fd = open(uiod, O_RDWR);
	if (fd < 1) {
		printf("Invalid UIO device file:%s.\n", uiod);
		return -1;
	}

	gpio_size = get_memory_size("/sys/class/uio/uio0/maps/map0/size");

	// mmap the GPIO device into user space

	gpio_ptr = mmap(NULL, gpio_size, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
	if (gpio_ptr == MAP_FAILED) {
		printf("Mmap call failure.\n");
		return -1;
	}

	ocm_size = get_memory_size("/sys/class/uio/uio0/maps/map1/size");

	// mmap the OCM memory into user space

	ocm_ptr = mmap(NULL, ocm_size, PROT_READ|PROT_WRITE, MAP_SHARED, fd, getpagesize());
	if (ocm_ptr == MAP_FAILED) {
		printf("Mmap call failure.\n");
		return -1;
	}

	// make the GPIO bits to be outputs to drive the LEDs and the inputs from the switches

	gpio_write(gpio_ptr, GPIO_TRI_OFFSET, 0);
	gpio_write(gpio_ptr, GPIO_TRI2_OFFSET, 0xF);

	// enable the interrupts from the GPIO

	gpio_write(gpio_ptr, GPIO_GLOBAL_IRQ, 0x80000000);
	gpio_write(gpio_ptr, GPIO_IRQ_CONTROL, 2);

	// initialize OCM just to test the summing in the irq

	for (i = 0; i < ocm_size / sizeof(int); i++) {
		*(ocm_ptr + i) = 0;
	}

	// wait for interrupts from the GPIO

	while (1) {
		wait_for_interrupt(fd, gpio_ptr, ocm_ptr, ocm_size);
printf(" Interrupt !! \n");
	}

	// unmap the GPIO device and OCM from user space

	munmap(gpio_ptr, gpio_size);
	munmap(ocm_ptr, ocm_size);
	return 0;
}

Above code runs correctly and I can verify the interrupt by checking the /proc/interrupts. The device-tree entry is:

 

 

axi_gpio_0: gpio@41200000 {
			//#gpio-cells = <2>;
			compatible = "generic-uio";
			//gpio-controller ;
			interrupt-parent = <&intc>;
			interrupts = <0 29 1>;
			reg = <0x41200000 0x10000>;
}

AXI GPIO interrupt is connected to Fabric Interrupt0 (number 29+32=61).

 

Attaching the log for your reference.

-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------

root@binary_counter:/proc# cat intrerrupts
CPU0 CPU1
16: 0 0 GIC 27 gt
17: 0 0 GIC 43 ttc_clockevent
18: 1548 822 GIC 29 twd
21: 43 0 GIC 39 f8007100.adc
142: 0 0 GIC 57 cdns-i2c
144: 0 0 GIC 35 f800c000.ocmc
145: 315 0 GIC 82 xuartps
146: 0 0 GIC 51 e000d000.spi
147: 199 0 GIC 54 eth0
148: 0 0 GIC 56 mmc0
149: 0 0 GIC 45 f8003000.dmac
150: 0 0 GIC 46 f8003000.dmac
151: 0 0 GIC 47 f8003000.dmac
152: 0 0 GIC 48 f8003000.dmac
153: 0 0 GIC 49 f8003000.dmac
154: 0 0 GIC 72 f8003000.dmac
155: 0 0 GIC 73 f8003000.dmac
156: 0 0 GIC 74 f8003000.dmac
157: 0 0 GIC 75 f8003000.dmac
158: 0 0 GIC 40 f8007000.devcfg
165: 0 0 GIC 41 f8005000.watchdog
166: 0 0 GIC 61 gpio
IPI1: 0 0 Timer broadcast interrupts
IPI2: 811 893 Rescheduling interrupts
IPI3: 0 0 Function call interrupts
IPI4: 11 33 Single function call interrupts
IPI5: 0 0 CPU stop interrupts
IPI6: 0 0 IRQ work interrupts
IPI7: 0 0 completion interrupts
Err: 0
root@binary_counter:/proc# cd ~
root@binary_counter:~# ls
gpio_uio_app
root@binary_counter:~# chmod +x ./gpio_uio_app
root@binary_counter:~# ./gpio_uio_app
GPIO UIO test.

Interrrupt!....
Interrrupt!....
Interrrupt!....
Interrrupt!....
Interrrupt!....
Interrrupt!....
Interrrupt!....
Interrrupt!....
Interrrupt!....
Interrrupt!....
Interrrupt!....
Interrrupt!....
Interrrupt!....
Interrrupt!....
Interrrupt!....
root@binary_counter:~#
root@binary_counter:~# cd /
nroot@binary_counter:/# cat proc/interrupts
CPU0 CPU1
16: 0 0 GIC 27 gt
17: 0 0 GIC 43 ttc_clockevent
18: 1744 967 GIC 29 twd
21: 43 0 GIC 39 f8007100.adc
142: 0 0 GIC 57 cdns-i2c
144: 0 0 GIC 35 f800c000.ocmc
145: 475 0 GIC 82 xuartps
146: 0 0 GIC 51 e000d000.spi
147: 203 0 GIC 54 eth0
148: 0 0 GIC 56 mmc0
149: 0 0 GIC 45 f8003000.dmac
150: 0 0 GIC 46 f8003000.dmac
151: 0 0 GIC 47 f8003000.dmac
152: 0 0 GIC 48 f8003000.dmac
153: 0 0 GIC 49 f8003000.dmac
154: 0 0 GIC 72 f8003000.dmac
155: 0 0 GIC 73 f8003000.dmac
156: 0 0 GIC 74 f8003000.dmac
157: 0 0 GIC 75 f8003000.dmac
158: 0 0 GIC 40 f8007000.devcfg
165: 0 0 GIC 41 f8005000.watchdog
166: 21 0 GIC 61 gpio
IPI1: 0 0 Timer broadcast interrupts
IPI2: 839 974 Rescheduling interrupts
IPI3: 0 0 Function call interrupts
IPI4: 11 33 Single function call interrupts
IPI5: 0 0 CPU stop interrupts
IPI6: 0 0 IRQ work interrupts
IPI7: 0 0 completion interrupts
Err: 0
root@binary_counter:/# cd ~
root@binary_counter:~# ./
.ash_history gpio_uio_app
root@binary_counter:~# ./groot@binary_counter:~# ./gpio_uio_app
GPIO UIO test.

Hangs here!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

^C
root@binary_counter:~#

-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------

 

 

This program runs ONLY single time. When this application is run the second time, it keeps on pending on interrupt for infinite time, unless the system is rebooted. Not able to understand the reason behind this phenomenon.
GPIO receives continuous data from a binary counter built in FPGA fabric, hence it does not need to be controlled by the processor. I am using Petalinux 2015.4 with Vivado 2015.4 and implementation on ZC702 board.

Thank you.
Regards,
Abhinav

0 Kudos
1 Solution

Accepted Solutions
Scholar rfs613
Scholar
2,202 Views
Registered: ‎05-28-2013

Re: UIO interrupt error on Zynq 7020

Jump to solution

UIO interrupts are normally very deterministic, at least when everything is setup correctly.

 

A guess at what might be causing your remaining issue. In your device tree you have:

interrupts = <0 29 1>;

where the "1" means "rising edge". Once triggered (pin will be high), no more interrupts will be generated until the pin first goes low, and then high again. It's possible to get stuck, with the interrupt high, but no interrupt actually generated. You'd need to ensure the interrupt is really dis-armed before entering your while(1) loop.

 

You could also change the "1" to a "4", which means "active high level". In that case it should keep on firing as fast as you can clear it.

4 Replies
Scholar rfs613
Scholar
1,592 Views
Registered: ‎05-28-2013

Re: UIO interrupt error on Zynq 7020

Jump to solution

I would suggest you add a call to write() to clear the interrupt, but *before* you wait for interrupts.  You have one at the end of wait_for_interrupt(), which is fine.

 

write(fd, (void *)&reenable, sizeof(int));

 But if there was an interrupt while your code wasn't active, it may be "stuck". So doing  a write() in your main() just before entering your while(1) loop may fix your issue.

0 Kudos
Visitor abhinav007
Visitor
1,559 Views
Registered: ‎09-25-2017

Re: UIO interrupt error on Zynq 7020

Jump to solution

@rfs613 Thank you. This worked up to an extent. I am now able to run the program more than once. However, it still gets stuck after 4-5 iterations of the same program. Is it a risk to use UIO in case of interrupts owing to its indeterministic response? 

 

Regards,

Abhinav

0 Kudos
Scholar rfs613
Scholar
2,203 Views
Registered: ‎05-28-2013

Re: UIO interrupt error on Zynq 7020

Jump to solution

UIO interrupts are normally very deterministic, at least when everything is setup correctly.

 

A guess at what might be causing your remaining issue. In your device tree you have:

interrupts = <0 29 1>;

where the "1" means "rising edge". Once triggered (pin will be high), no more interrupts will be generated until the pin first goes low, and then high again. It's possible to get stuck, with the interrupt high, but no interrupt actually generated. You'd need to ensure the interrupt is really dis-armed before entering your while(1) loop.

 

You could also change the "1" to a "4", which means "active high level". In that case it should keep on firing as fast as you can clear it.

Visitor abhinav007
Visitor
1,459 Views
Registered: ‎09-25-2017

Re: UIO interrupt error on Zynq 7020

Jump to solution

@rfs613 Thank you for the help. This was indeed the problem. The GPIO controller had a level-sensitive interrupt but I configured it as edge-sensitive in Linux. I did this because I found a lot more interrupts registered in /proc/interrupt than the number of iterations of the while loop(tried a finite loop) with level-sensitive configuration. So I configured it as edge-triggered instead,  hoping that the number of interrupts registered by kernel would then reduce and equal the number of while loop iterations. It did reduce the interrupts registered but still, they were not equal to while loop iterations. Didn't anticipate this would cause an error!

0 Kudos