cancel
Showing results for 
Show  only  | Search instead for 
Did you mean: 
joancab
Advisor
Advisor
563 Views
Registered: ‎05-11-2015

Linux interrupts from PL

 

I managed to build a system and Linux where there is an interrupt from the PL that I can see with cat /proc/interrupts,

following directions here: Testing UIO with Interrupt on Zynq Ultrascale 

Now in my Linux app, how do I bind the int to my ISR?

Tags (4)
0 Kudos
13 Replies
stephenm
Xilinx Employee
Xilinx Employee
539 Views
Registered: ‎09-12-2007

What is the source of the interrupt. is this a custom IP?

Have you a custom driver? If so, you can create a custom platform driver and register your interrupt here.

 

For example (and there are plenty of examples of how to create a basic platform driver online):

   res = platform_get_resource(ofdev, IORESOURCE_IRQ, 0);
   if (!res) {
      printk(KERN_INFO "could not get platform IRQ resource.\n");
      goto fail_irq;
   }
 
   // save the returned IRQ
   dm.irq = res->start;
 
   printk(KERN_INFO "IRQ read form DTS entry as %d\n", dm.irq);
 
   rval = request_irq(dm.irq, mydriver_interrupt, 0 , "mydriver", &dm);
   if(rval)
   {
      printk(KERN_INFO "can't get assigned irq: %d\n", dm.irq);
      goto fail_irq;
   }
0 Kudos
joancab
Advisor
Advisor
529 Views
Registered: ‎05-11-2015

It's a custom IP and I'm using UIO

0 Kudos
joancab
Advisor
Advisor
504 Views
Registered: ‎05-11-2015

The driver files created by the hardware (HLS) include an x<name>_linux.c file that includes some uio_map and uio_info structures but nothing about interrupts

0 Kudos
501 Views
Registered: ‎02-09-2017

The UIO interrupt, if it has been correctly initialized, is associated with the UIO device file that gets created at /dev/uioX. You should be able to open this as a file, and then do a blocking read on the file descriptor, which will complete whenever the interrupt fires, a write will then reactivate the interrupt. There is pretty good information on using UIO devices to write userspace drivers here. The page describes everything that is needed in a bit of example code. There are also other resources out there that describe other ways to handle generic UIO interrupts in other ways, like using select() or poll().

joancab
Advisor
Advisor
497 Views
Registered: ‎05-11-2015

brian.vongunten@speedgoat.ch 

I remember now when I was looking at this same problem some time ago, that I found out UIO "interrupts" is something you poll (the antithesis of an interrupt) and I said to myself "I thought Linux was a serious thing".

Certainly I could create a thread with a blocking read, but quite a dodgy practice. I suppose there are good reasons to prevent interrupts reaching user space.

 

0 Kudos
493 Views
Registered: ‎02-09-2017

It's actually not a poll. It is a blocking read. The thread is not actively polling the interrupt, the interrupt restarts the thread when the read unblocks. The context switch from kernel to userspace has some overhead, but I think that is to be expected given how astronomically easier it is to write drivers in userspace with a stable API.

stephenm
Xilinx Employee
Xilinx Employee
475 Views
Registered: ‎09-12-2007

Can you pass on the x<name>_linux.c?

0 Kudos
joancab
Advisor
Advisor
469 Views
Registered: ‎05-11-2015

brian.vongunten@speedgoat.ch   enlightening, thanks. 

@stephenm    below is the file contents:

// ==============================================================
// Vitis HLS - High-Level Synthesis from C, C++ and OpenCL v2020.2 (64-bit)
// Copyright 1986-2020 Xilinx, Inc. All Rights Reserved.
// ==============================================================
#ifdef __linux__

/***************************** Include Files *********************************/
#include "xinterleaver.h"

/***************** Macros (Inline Functions) Definitions *********************/
#define MAX_UIO_PATH_SIZE       256
#define MAX_UIO_NAME_SIZE       64
#define MAX_UIO_MAPS            5
#define UIO_INVALID_ADDR        0

/**************************** Type Definitions ******************************/
typedef struct {
    u64 addr;
    u32 size;
} XInterleaver_uio_map;

typedef struct {
    int  uio_fd;
    int  uio_num;
    char name[ MAX_UIO_NAME_SIZE ];
    char version[ MAX_UIO_NAME_SIZE ];
    XInterleaver_uio_map maps[ MAX_UIO_MAPS ];
} XInterleaver_uio_info;

/***************** Variable Definitions **************************************/
static XInterleaver_uio_info uio_info;

/************************** Function Implementation *************************/
static int line_from_file(char* filename, char* linebuf) {
    char* s;
    int i;
    FILE* fp = fopen(filename, "r");
    if (!fp) return -1;
    s = fgets(linebuf, MAX_UIO_NAME_SIZE, fp);
    fclose(fp);
    if (!s) return -2;
    for (i=0; (*s)&&(i<MAX_UIO_NAME_SIZE); i++) {
        if (*s == '\n') *s = 0;
        s++;
    }
    return 0;
}

static int uio_info_read_name(XInterleaver_uio_info* info) {
    char file[ MAX_UIO_PATH_SIZE ];
    sprintf(file, "/sys/class/uio/uio%d/name", info->uio_num);
    return line_from_file(file, info->name);
}

static int uio_info_read_version(XInterleaver_uio_info* info) {
    char file[ MAX_UIO_PATH_SIZE ];
    sprintf(file, "/sys/class/uio/uio%d/version", info->uio_num);
    return line_from_file(file, info->version);
}

static int uio_info_read_map_addr(XInterleaver_uio_info* info, int n) {
    int ret;
    char file[ MAX_UIO_PATH_SIZE ];
    info->maps[n].addr = UIO_INVALID_ADDR;
    sprintf(file, "/sys/class/uio/uio%d/maps/map%d/addr", info->uio_num, n);
    FILE* fp = fopen(file, "r");
    if (!fp) return -1;
    ret = fscanf(fp, "0x%x", &info->maps[n].addr);
    fclose(fp);
    if (ret < 0) return -2;
    return 0;
}

static int uio_info_read_map_size(XInterleaver_uio_info* info, int n) {
    int ret;
    char file[ MAX_UIO_PATH_SIZE ];
    sprintf(file, "/sys/class/uio/uio%d/maps/map%d/size", info->uio_num, n);
    FILE* fp = fopen(file, "r");
    if (!fp) return -1;
    ret = fscanf(fp, "0x%x", &info->maps[n].size);
    fclose(fp);
    if (ret < 0) return -2;
    return 0;
}

int XInterleaver_Initialize(XInterleaver *InstancePtr, const char* InstanceName) {
	XInterleaver_uio_info *InfoPtr = &uio_info;
	struct dirent **namelist;
    int i, n;
    char* s;
    char file[ MAX_UIO_PATH_SIZE ];
    char name[ MAX_UIO_NAME_SIZE ];
    int flag = 0;

    assert(InstancePtr != NULL);

    n = scandir("/sys/class/uio", &namelist, 0, alphasort);
    if (n < 0)  return XST_DEVICE_NOT_FOUND;
    for (i = 0;  i < n; i++) {
    	strcpy(file, "/sys/class/uio/");
    	strcat(file, namelist[i]->d_name);
    	strcat(file, "/name");
        if ((line_from_file(file, name) == 0) && (strcmp(name, InstanceName) == 0)) {
            flag = 1;
            s = namelist[i]->d_name;
            s += 3; // "uio"
            InfoPtr->uio_num = atoi(s);
            break;
        }
    }
    if (flag == 0)  return XST_DEVICE_NOT_FOUND;

    uio_info_read_name(InfoPtr);
    uio_info_read_version(InfoPtr);
    for (n = 0; n < MAX_UIO_MAPS; ++n) {
        uio_info_read_map_addr(InfoPtr, n);
        uio_info_read_map_size(InfoPtr, n);
    }

    sprintf(file, "/dev/uio%d", InfoPtr->uio_num);
    if ((InfoPtr->uio_fd = open(file, O_RDWR)) < 0) {
        return XST_OPEN_DEVICE_FAILED;
    }

    // NOTE: slave interface 'Param' should be mapped to uioX/map0
    InstancePtr->Param_BaseAddress = (u64)mmap(NULL, InfoPtr->maps[0].size, PROT_READ|PROT_WRITE, MAP_SHARED, InfoPtr->uio_fd, 0 * getpagesize());
    assert(InstancePtr->Param_BaseAddress);

    InstancePtr->IsReady = XIL_COMPONENT_IS_READY;

    return XST_SUCCESS;
}

int XInterleaver_Release(XInterleaver *InstancePtr) {
	XInterleaver_uio_info *InfoPtr = &uio_info;

    assert(InstancePtr != NULL);
    assert(InstancePtr->IsReady == XIL_COMPONENT_IS_READY);

    munmap((void*)InstancePtr->Param_BaseAddress, InfoPtr->maps[0].size);

    close(InfoPtr->uio_fd);

    return XST_SUCCESS;
}

#endif

 

0 Kudos
rfs613
Scholar
Scholar
406 Views
Registered: ‎05-28-2013

The poll() function is somewhat poorly named - you're not the first one to have assumed it would, well, perform polling. In fact it suspends the execution until the watched file descriptor(s) become ready. This is needed when you want to watch more than one descriptor at a time.

If there is only a single descriptor, such as for /dev/uio0, then you can skip the poll() and just do a read(). It will block by default until the file descriptor becomes "ready", which in the case of UIO is triggered by the interrupt.

0 Kudos
joancab
Advisor
Advisor
398 Views
Registered: ‎05-11-2015

@rfs613 

There is a solution here by you I'm trying to apply. Not sure, for example, if that read() is literally that or needs a specific address like an interrupt register

0 Kudos
rfs613
Scholar
Scholar
387 Views
Registered: ‎05-28-2013

Yup I've been on this merry go round a few times...

The read() is just 4 bytes (a 32-bit integer representing how many interrupts have occurred). The interrupt itself is specified via the device tree.

0 Kudos
joancab
Advisor
Advisor
289 Views
Registered: ‎05-11-2015

It is amazing how if the steps to accomplish something are two, A and B, in many cases you find 99 examples of A and 1 of B. 

here I found how to use these UIO functions.

0 Kudos