cancel
Showing results for 
Show  only  | Search instead for 
Did you mean: 
Highlighted
Explorer
Explorer
12,854 Views
Registered: ‎04-18-2017

Custom Hardware with UIO

Jump to solution

Hello,

 

Yet another similar post about UIO and petalinux. I have a custom ip which simply writes the 8 LEDS from the zedboard. And as outputs it reads the values from the switches and also generates a pulse with a push button. This is in a custom AXI-IP rather than using the AXI-GPIO. So, on the slv_reg0 I receive the value for the LEDS and on slv_reg1 I write the values from the switches on every rising edge of the axi_clk. The pulse generated from the PB is connected to IRQ_F2P[0:0]. This works fine in a standalone application coded in SDK with the auto generated functions to read and write the custom IP plus it reacts to the interrupt.

 

custom_ip.png

Just in case, I am using vivado and petalinux 2017.3.

 

Now I am eager to replicate the behavior on linux with the UIO. Therefore, this are the steps that I am following:

 

1. Create a new petalinux project

petalinux-create --type project --template zynq --name custom_ip

2. Get the custom hardware generated with vivado/sdk

petalinux-config --get-hw-description=/home/ariel/Documents/vivado_prjts/custom_ip/custom_ip.sdk

3. Configure the kernel with

CONFIG_UIO=y 
CONFIG_UIO_PDRV_GENIRQ=y

4. Build the project so it autogenerates the files needed

petalinux-build

5. Edit  <plnx_proj_root>/project-spec/meta-user/recipes-bsp/device-tree/files/system-user.dtsi so it looks like this:

/include/ "system-conf.dtsi"
/ {
    user_io@43c00000{
    compatible = "custom_ip,generic-uio,ui_pdrv";
    status = "okay";
	interrupt-parent = <&intc>;
	interrupts = <0 29 4>;
	reg = <0x43c00000 0x10000>;
    };
};

I extracted the values form interrupt-parent, interrupts and reg from  <plnx_proj_root>/components/plnx_workspace/device-tree/device-tree-generation/pl.dtsi. Also, the user_io@43c00000 is from pl.dtsi

 

6. Following some similar posts, they recommend edit <plnx_proj_root>/components/plnx_workspace/device-tree/device-tree-generation/system-conf.dtsi so it looks like this:

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

But I have seen that this is autogenerated. Therefore, after petalinux-build, the uio_pdrv_genirq.of_id=generic-uio part is removed. One suggestion was to edit <plnx_proj_root>/components/plnx_workspace/device-tree/device-tree-generation/system-top.dts but this is also autogenerated and therefore modified.

 

On this, I noticed that <plnx_proj_root>/project-spec/meta-user/recipes-dt does not exist.

 

$ ls <plnx_proj_root>/project-spec/meta-user/
conf  COPYING.MIT  README  recipes-apps  recipes-bsp  recipes-core  recipes-kernel

 

7. Build the project with my changes with petalinux-build

8. Package it:

petalinux-package --boot --format BIN --fsbl ./images/linux/zynq_fsbl.elf --fpga ./images/linux/design_1_wrapper.bit --u-boot --force

9. Copy <plnx_proj_root>/images/linux/* to the BOOT partition of the SD.

 

When linux boots, in /dev I do not see /dev/uio, but if I can read the switches and write the LEDS with:

 

devmem 0x41200000 32 0x000000ff   --To write to the LEDS

devmem 0x41210004                 --To read the switches.

The issue is as /dev/uio is missing, i will not be able to follow this guide to write a C application to include it later on petalinux.

 

The question is what step should I modify, add or remove to have /dev/uio 'created' so I can access it in a C code via the UIO?

 

Thank you for the help

1 Solution

Accepted Solutions
Highlighted
Xilinx Employee
Xilinx Employee
15,912 Views
Registered: ‎09-22-2015

We have seen an issue with generic-uio in previous versions of petalinux where eventhough you select it as a [Y], it would still not come up in /dev/. Please try this workaround. One thing to remember here is all changes to recipes-dt will be overwritten except for the file system-user.dtsi. You need to use this file only to add changes to device-tree.

 

How to enable UIO drivers so that it comes up after Linux bootup. 

Description

I have enabled the UIO drivers in the kernel config, but after Linux boots up I try

 

ls /dev/uio*

ls: /dev/uio*: No such file or directory

 

There are no devices listed under /dev

What should I do to make these come up after kernel boots.

 

Solution

  1. In the kernel config make the following changes to the kernel config menu for the User space I/O platform driver.

 

 

 

 

 

  1. In the device tree add the compatible string "generic-uio" for the gpio devices as well as add uio_pdrv_genirq.of_id=generic-uio to the kernel boot args.

 

 This should be added to system-user.dtsi inside

/{

chosen {

        bootargs = "earlycon=cdns,mmio,0xFF000000,115200n8 uio_pdrv_genirq.of_id=generic-uio";

        };

};

 

 

&axi_gpio_0 {

        compatible =  "generic-uio";

        };

&axi_gpio_1  {

        compatible =  "generic-uio";

        };      

 

 Build Linux and boot it up. Once you boot it up you should see the uio nodes after you ls into the /dev directory.

 

 

------------------------------------------------------------------------------------------------------------------------
Please mark an answer "Accept as solution" if a post has the solution to your issue.
------------------------------------------------------------------------------------------------------------------------

View solution in original post

13 Replies
Highlighted
Xilinx Employee
Xilinx Employee
12,820 Views
Registered: ‎09-22-2015
Check your modules.dep under /lib/modules/. See if uio_pdrv_genirq exists as loadable module and load it using insmod <path-to-module>.
Check status of module using lsmod. See if nodes are created in /dev
------------------------------------------------------------------------------------------------------------------------
Please mark an answer "Accept as solution" if a post has the solution to your issue.
------------------------------------------------------------------------------------------------------------------------
0 Kudos
Highlighted
Explorer
Explorer
12,803 Views
Registered: ‎04-18-2017

Hello @akshayva,

 

I cannot see anything with uio in modules.dep:

 

 

root@custom_ip:~# cat /lib/modules/4.9.0-xilinx-v2017.3/modules.dep
kernel/crypto/echainiv.ko:
kernel/crypto/hmac.ko:
kernel/crypto/sha256_generic.ko:
kernel/crypto/drbg.ko:
kernel/crypto/jitterentropy_rng.ko:
kernel/drivers/usb/gadget/function/usb_f_ss_lb.ko: kernel/drivers/usb/gadget/libcomposite.ko
kernel/drivers/usb/gadget/function/usb_f_mass_storage.ko: kernel/drivers/usb/gadget/libcomposite.ko
kernel/drivers/usb/gadget/legacy/g_zero.ko: kernel/drivers/usb/gadget/libcomposite.ko
kernel/drivers/usb/gadget/libcomposite.ko:
kernel/drivers/virtio/virtio.ko:
kernel/drivers/virtio/virtio_ring.ko:
kernel/drivers/remoteproc/remoteproc.ko: kernel/drivers/virtio/virtio.ko kernel/drivers/virtio/virtio_ring.ko
kernel/drivers/remoteproc/zynq_remoteproc.ko: kernel/drivers/remoteproc/remoteproc.ko kernel/drivers/virtio/virtio.ko kernel/drivers/virtio/virtio_ring.ko
kernel/drivers/rpmsg/rpmsg_core.ko:
kernel/drivers/rpmsg/virtio_rpmsg_bus.ko: kernel/drivers/rpmsg/rpmsg_core.ko kernel/drivers/virtio/virtio.ko kernel/drivers/virtio/virtio_ring.ko
kernel/net/802/p8022.ko: kernel/net/llc/llc.ko
kernel/net/802/psnap.ko: kernel/net/llc/llc.ko
kernel/net/802/stp.ko: kernel/net/llc/llc.ko
kernel/net/ipv4/ipip.ko:
kernel/net/8021q/8021q.ko:
kernel/net/llc/llc.ko:
kernel/net/bridge/bridge.ko: kernel/net/802/stp.ko kernel/net/llc/llc.ko
root@custom_ip:~# cat /lib/modules/4.9.0-xilinx-v2017.3/modules.dep | grep uio
root@custom_ip:~#

 

What about step6? Is it correct?

 

In step 3, should they be =y or =m? I had it with =y. I switched to =m and now this is what I got:

 

 

root@custom_ip:~# cat /lib/modules/4.9.0-xilinx-v2017.3/modules.dep
kernel/crypto/echainiv.ko:
kernel/crypto/hmac.ko:
kernel/crypto/sha256_generic.ko:
kernel/crypto/drbg.ko:
kernel/crypto/jitterentropy_rng.ko:
kernel/drivers/usb/gadget/function/usb_f_ss_lb.ko: kernel/drivers/usb/gadget/libcomposite.ko
kernel/drivers/usb/gadget/function/usb_f_mass_storage.ko: kernel/drivers/usb/gadget/libcomposite.ko
kernel/drivers/usb/gadget/legacy/g_zero.ko: kernel/drivers/usb/gadget/libcomposite.ko
kernel/drivers/usb/gadget/libcomposite.ko:
kernel/drivers/usb/serial/usbserial.ko:
kernel/drivers/usb/serial/ftdi_sio.ko: kernel/drivers/usb/serial/usbserial.ko
kernel/drivers/virtio/virtio.ko:
kernel/drivers/virtio/virtio_ring.ko:
kernel/drivers/uio/uio.ko:
kernel/drivers/uio/uio_pdrv_genirq.ko: kernel/drivers/uio/uio.ko
kernel/drivers/uio/uio_dmem_genirq.ko: kernel/drivers/uio/uio.ko
kernel/drivers/uio/uio_xilinx_apm.ko: kernel/drivers/uio/uio.ko
kernel/drivers/remoteproc/remoteproc.ko: kernel/drivers/virtio/virtio.ko kernel/drivers/virtio/virtio_ring.ko
kernel/drivers/remoteproc/zynq_remoteproc.ko: kernel/drivers/remoteproc/remoteproc.ko kernel/drivers/virtio/virtio.ko kernel/drivers/virtio/virtio_ring.ko
kernel/drivers/rpmsg/rpmsg_core.ko:
kernel/drivers/rpmsg/virtio_rpmsg_bus.ko: kernel/drivers/rpmsg/rpmsg_core.ko kernel/drivers/virtio/virtio.ko kernel/drivers/virtio/virtio_ring.ko
kernel/net/802/p8022.ko: kernel/net/llc/llc.ko
kernel/net/802/psnap.ko: kernel/net/llc/llc.ko
kernel/net/802/stp.ko: kernel/net/llc/llc.ko
kernel/net/ipv4/ipip.ko:
kernel/net/8021q/8021q.ko:
kernel/net/llc/llc.ko:
kernel/net/bridge/bridge.ko: kernel/net/802/stp.ko kernel/net/llc/llc.ko
root@custom_ip:~# cat /lib/modules/4.9.0-xilinx-v2017.3/modules.dep | grep uio
kernel/drivers/uio/uio.ko:
kernel/drivers/uio/uio_pdrv_genirq.ko: kernel/drivers/uio/uio.ko
kernel/drivers/uio/uio_dmem_genirq.ko: kernel/drivers/uio/uio.ko
kernel/drivers/uio/uio_xilinx_apm.ko: kernel/drivers/uio/uio.ko

I loaded it with insmod but I still cannot see /dev/uio but it shows with lsmod:

root@custom_ip:/lib/modules/4.9.0-xilinx-v2017.3/kernel/drivers/uio# lsmod
    Not tainted
uio_pdrv_genirq 2754 0 - Live 0xbf01c000
uio 7190 1 uio_pdrv_genirq, Live 0xbf000000

Thanks for the help!

 

EDIT1:

I saw in another post that I should also edit pl.dtsi and add to compatible generic-uio. Should I?

/ {
	amba_pl: amba_pl {
		#address-cells = <1>;
		#size-cells = <1>;
		compatible = "simple-bus";
		ranges ;
		custom_ip_0: custom_ip@43c00000 {
			compatible = "xlnx,custom-ip-1.0 generic-uio";
			interrupt-parent = <&intc>;
			interrupts = <0 29 4>;
			reg = <0x43c00000 0x10000>;
			xlnx,s00-axi-addr-width = <0x4>;
			xlnx,s00-axi-data-width = <0x20>;
		};
	};
};
0 Kudos
Highlighted
Xilinx Employee
Xilinx Employee
15,913 Views
Registered: ‎09-22-2015

We have seen an issue with generic-uio in previous versions of petalinux where eventhough you select it as a [Y], it would still not come up in /dev/. Please try this workaround. One thing to remember here is all changes to recipes-dt will be overwritten except for the file system-user.dtsi. You need to use this file only to add changes to device-tree.

 

How to enable UIO drivers so that it comes up after Linux bootup. 

Description

I have enabled the UIO drivers in the kernel config, but after Linux boots up I try

 

ls /dev/uio*

ls: /dev/uio*: No such file or directory

 

There are no devices listed under /dev

What should I do to make these come up after kernel boots.

 

Solution

  1. In the kernel config make the following changes to the kernel config menu for the User space I/O platform driver.

 

 

 

 

 

  1. In the device tree add the compatible string "generic-uio" for the gpio devices as well as add uio_pdrv_genirq.of_id=generic-uio to the kernel boot args.

 

 This should be added to system-user.dtsi inside

/{

chosen {

        bootargs = "earlycon=cdns,mmio,0xFF000000,115200n8 uio_pdrv_genirq.of_id=generic-uio";

        };

};

 

 

&axi_gpio_0 {

        compatible =  "generic-uio";

        };

&axi_gpio_1  {

        compatible =  "generic-uio";

        };      

 

 Build Linux and boot it up. Once you boot it up you should see the uio nodes after you ls into the /dev directory.

 

 

------------------------------------------------------------------------------------------------------------------------
Please mark an answer "Accept as solution" if a post has the solution to your issue.
------------------------------------------------------------------------------------------------------------------------

View solution in original post

Highlighted
Explorer
Explorer
12,786 Views
Registered: ‎04-18-2017

I just came to post this. I edited the system-user.dtsi (as stated in ug1144) adding the bootargs for uio plus the compatible for my custom_ip and that was it. It works now.

I have one more thing.....what if I want to user another kernel, for example ubuntu (as suggested here) so I can have apt-get for example and also have to possibility to store changes in the sd card on the second partition (rootfs)?

0 Kudos
Highlighted
Xilinx Employee
Xilinx Employee
12,773 Views
Registered: ‎09-22-2015

I 'm not sure about whether that is possible. But here are some options:

1. Use the smart package manager instead that comes with Yocto and part of petalinux. To enable this, please add:

│ Location: │
│ -> Filesystem Packages │
│ -> devel │
│ -> python │
│ (1) -> python-smartpm

 

2. Follow steps on this wiki-xilinx page which talks about Ubuntu on ZYNQ but its fairly old.(http://www.wiki.xilinx.com/Ubuntu+on+Zynq)

 

 

------------------------------------------------------------------------------------------------------------------------
Please mark an answer "Accept as solution" if a post has the solution to your issue.
------------------------------------------------------------------------------------------------------------------------
0 Kudos
Highlighted
Explorer
Explorer
12,756 Views
Registered: ‎04-18-2017

Hello @akshayva,

 

Other thing...I have been following this guide on how to write the UIO C code but it is general. My question is, if I have a custom AXI-IP with 4 32 bit register. How do I write to register N and read from register M?

 

Thanks again for the help!

 

EDIT 1: Perhaps to make it more clear, this is what I do in a standalone code in SDK in the interrup handler:

	u32 leds;
	leds = CUSTOM_IP_mReadReg(CUSTOM_IP_BASEADDR, CUSTOM_IP_S00_AXI_SLV_REG1_OFFSET);	// Read from the IP.
	CUSTOM_IP_mWriteReg(CUSTOM_IP_BASEADDR + CUSTOM_IP_S00_AXI_SLV_REG0_OFFSET, 0, leds);	// Write to the IP.
0 Kudos
Highlighted
Xilinx Employee
Xilinx Employee
12,733 Views
Registered: ‎09-22-2015

/dev/uioX is used to access the address space of the card. Just use mmap() to access registers or RAM locations of your card.

Please refer to this document for more information on how to use UIO. It is using mmap() on the /dev/uioX device file.

https://www.kernel.org/doc/html/v4.12/driver-api/uio-howto.html

 

------------------------------------------------------------------------------------------------------------------------
Please mark an answer "Accept as solution" if a post has the solution to your issue.
------------------------------------------------------------------------------------------------------------------------
0 Kudos
Highlighted
Xilinx Employee
Xilinx Employee
12,732 Views
Registered: ‎09-22-2015
Using linux kernel driver will give you a standard interface to the Custom IP and you should be able to use the same attributes as any other UIO device for AXI Custom IP. All you need to do is map on the device file and access register.
------------------------------------------------------------------------------------------------------------------------
Please mark an answer "Accept as solution" if a post has the solution to your issue.
------------------------------------------------------------------------------------------------------------------------
0 Kudos
Highlighted
Explorer
Explorer
12,711 Views
Registered: ‎04-18-2017

Thank you @akshayva for the help. I managed to read and write to different register in my custom ip:

 

This is my (auto-generated) pl.dtsi:

/ {
	amba_pl: amba_pl {
		#address-cells = <1>;
		#size-cells = <1>;
		compatible = "simple-bus";
		ranges ;
		custom_ip_0: custom_ip@43c00000 {
			compatible = "xlnx,custom-ip-1.0";
			interrupt-parent = <&intc>;
			interrupts = <0 29 4>;
			reg = <0x43c00000 0x10000>;
			xlnx,s00-axi-addr-width = <0x4>;
			xlnx,s00-axi-data-width = <0x20>;
		};
	};
};

The system-user.dtsi:

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

/ {
	model = "Zynq Zed Development Board";
	compatible = "xlnx,zynq-zed", "xlnx,zynq-7000";

	usb_phy0: phy0@e0002000 {
		compatible = "ulpi-phy";
		#phy-cells = <0>;
		reg = <0xe0002000 0x1000>;
		view-port = <0x0170>;
		drv-vbus;
	};
};

&clkc {
	ps-clk-frequency = <33333333>;
};

/ {
chosen {
    bootargs = "console=ttyPS0,115200 earlyprintk uio_pdrv_genirq.of_id=generic-uio";
    };

amba_pl: amba_pl {
		custom_ip_0: custom_ip@43c00000 {
			compatible = "generic-uio";
			interrupt-parent = <&intc>;
			interrupts = <0 29 1>;
	    };
	};
};

&usb0 {
	dr_mode = "host";
	usb-phy = <&usb_phy0>;
};

And the C code:

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


#define CUSTOM_IP_MAP_SIZE                  0x10000
#define CUSTOM_IP_BASEADDR	                0x43C00000
#define CUSTOM_IP_S00_AXI_SLV_REG0_OFFSET   0
#define CUSTOM_IP_S00_AXI_SLV_REG1_OFFSET   4
#define CUSTOM_IP_S00_AXI_SLV_REG2_OFFSET   8
#define CUSTOM_IP_S00_AXI_SLV_REG3_OFFSET   12

int main(void)
{
    uint32_t leds=0x0;
    int fd = open("/dev/uio0", O_RDWR);
    void *ptr;

    if (fd < 0) {
        perror("open");
        exit(EXIT_FAILURE);
    }
    ptr = mmap(NULL, CUSTOM_IP_MAP_SIZE, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);

    while (1) {
        uint32_t info = 1; /* unmask */

        ssize_t nb = write(fd, &info, sizeof(info));
        if (nb < sizeof(info)) {
            perror("write");
            close(fd);
            exit(EXIT_FAILURE);
        }

        struct pollfd fds = {
            .fd = fd,
            .events = POLLIN,
        };

        int ret = poll(&fds, 1, -1);
        if (ret >= 1) {
            nb = read(fd, &info, sizeof(info));
            if (nb == sizeof(info)) {
                /* Do something in response to the interrupt. */
                printf("Interrupt #%u!\n", info);
                leds = *((unsigned *)(ptr + CUSTOM_IP_S00_AXI_SLV_REG1_OFFSET));    //Read from the IP (slv_reg1).
                printf("Switches: %d\n", leds);
                *((unsigned *)(ptr + CUSTOM_IP_S00_AXI_SLV_REG0_OFFSET)) = leds;    //Write to the IP (slv_reg0).
                printf("Wrote to leds\n");
            }
        } else {
            perror("poll()");
            close(fd);
            exit(EXIT_FAILURE);
        }
    }

    close(fd);
    exit(EXIT_SUCCESS);
}

Hopefully it helps someone with the same doubts.

Highlighted
Advisor
Advisor
9,224 Views
Registered: ‎10-10-2014

@aripod, regarding the dtsi entry :

 

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

 

fyi : you can set manual bootargs in the config menu (at least in my Petalinux 2017.2). Not sure which is the best method : the system-user.dtsi or the bootargs in the config menu (never tried this last one)

** kudo if the answer was helpful. Accept as solution if your question is answered **
0 Kudos
Highlighted
8,658 Views
Registered: ‎06-12-2018

 Passing the argument in the config -c kernel. Doesnt let the uio show up in dev.

system-user.dtsi file should look like this.

 

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

/{

chosen {

bootargs = "earlycon=cdns,mmio,0xFF000000,115200n8 uio_pdrv_genirq.of_id=generic-uio";

};

};

 

&axilinuxinterface_0{
compatible = "generic-uio";
};

0 Kudos
Highlighted
Visitor
Visitor
7,601 Views
Registered: ‎05-03-2018

hi,your code is very useful for me,but I don't understand one line:

int ret = poll(&fds, 1, -1);

what does it do ? and why?

0 Kudos
Highlighted
Explorer
Explorer
6,787 Views
Registered: ‎04-18-2017

That waits for the interrupt to happen

0 Kudos