Arturete
Arturete

Reputation: 133

Manage custom IP devices connected with AXI in a Zybo under linux OS

I followed the xilinx wiki about linux drivers (Linux-GPIO-Driver) in order to control GPIO connected to the PS throught the MIO and EMIO pins.

Then I added some basic peripherals to the PL connected to the PS by AXI buses. This is my design:

enter image description here

This file, which contains the PL peripherals info, was automatically generated in the device tree sources:

/ {
amba_pl: amba_pl {
    #address-cells = <1>;
    #size-cells = <1>;
    compatible = "simple-bus";
    ranges ;
    axi_gpio_0: gpio@41200000 {
        #gpio-cells = <2>;
        compatible = "xlnx,xps-gpio-1.00.a";
        gpio-controller ;
        reg = <0x41200000 0x10000>;
        xlnx,all-inputs = <0x0>;
        xlnx,all-inputs-2 = <0x0>;
        xlnx,all-outputs = <0x0>;
        xlnx,all-outputs-2 = <0x0>;
        xlnx,dout-default = <0x00000000>;
        xlnx,dout-default-2 = <0x00000000>;
        xlnx,gpio-width = <0x4>;
        xlnx,gpio2-width = <0x20>;
        xlnx,interrupt-present = <0x0>;
        xlnx,is-dual = <0x0>;
        xlnx,tri-default = <0xFFFFFFFF>;
        xlnx,tri-default-2 = <0xFFFFFFFF>;
    };
    axi_gpio_1: gpio@41210000 {
        #gpio-cells = <2>;
        compatible = "xlnx,xps-gpio-1.00.a";
        gpio-controller ;
        reg = <0x41210000 0x10000>;
        xlnx,all-inputs = <0x1>;
        xlnx,all-inputs-2 = <0x0>;
        xlnx,all-outputs = <0x0>;
        xlnx,all-outputs-2 = <0x0>;
        xlnx,dout-default = <0x00000000>;
        xlnx,dout-default-2 = <0x00000000>;
        xlnx,gpio-width = <0x4>;
        xlnx,gpio2-width = <0x20>;
        xlnx,interrupt-present = <0x0>;
        xlnx,is-dual = <0x0>;
        xlnx,tri-default = <0xFFFFFFFF>;
        xlnx,tri-default-2 = <0xFFFFFFFF>;
    };
    axi_gpio_2: gpio@41220000 {
        #gpio-cells = <2>;
        compatible = "xlnx,xps-gpio-1.00.a";
        gpio-controller ;
        reg = <0x41220000 0x10000>;
        xlnx,all-inputs = <0x1>;
        xlnx,all-inputs-2 = <0x0>;
        xlnx,all-outputs = <0x0>;
        xlnx,all-outputs-2 = <0x0>;
        xlnx,dout-default = <0x00000000>;
        xlnx,dout-default-2 = <0x00000000>;
        xlnx,gpio-width = <0x4>;
        xlnx,gpio2-width = <0x20>;
        xlnx,interrupt-present = <0x0>;
        xlnx,is-dual = <0x0>;
        xlnx,tri-default = <0xFFFFFFFF>;
        xlnx,tri-default-2 = <0xFFFFFFFF>;
    };
};
};

All the parameters described above appear in the linux-device-tree inside the folder: /sys/firmware/devicetree/base/amba_pl

For example in: /sys/firmware/devicetree/base/amba_pl/gpio@41200000

I can find these files

#gpio-cells
compatible
gpio-controller
linux,phandle
name
phandle
reg
xlnx,all-inputs
xlnx,all-inputs-2
xlnx,all-outputs
xlnx,all-outputs-2
xlnx,dout-default
xlnx,dout-default-2
xlnx,gpio-width
xlnx,gpio2-width
xlnx,interrupt-present
xlnx,is-dual
xlnx,tri-default
xlnx,tri-default-2

I also found (in: /sys/bus/platform/drivers) the drivers related with the gpio which corresponds to the options that I enabled in the kernel menuconfig. The documentation about these drivers can be found here: drivers-on-gpio

But sincerely I still don't know how to manage and how to communicate with a generic IP in the PL side of the zynq (as in this example where I want to play with leds and buttons connect through an axi interface to the PS)

I suppose that what I have to do is to play with read/write operations in memory. Could you give me any info about the way that I need to follow? Do you know about any source of info related with this task? Are there some examples of how to manage and where to find the right driver automatically generated by xilinx-tools? I will thank any support!

The goal of this is to learn the basics in order to control any generic ip connected with AXI.

Upvotes: 1

Views: 2267

Answers (1)

Arturete
Arturete

Reputation: 133

As I post in a comment I was able to control the AXI GPIO peripherals from a user space application but without using the GPIO drivers. I was reading many things until I realised that as these peripherals use a memory map interface I couldn't use the GPIO drivers. I only was able to use the GPIO drivers to control the MIO and EMIO pins. The fact that when the pl.dtsi is created by the Xilinx tools the peripherals appears as xps-gpio-1.00.a was confusing me. We can see that in the code that I posted in the question in the lines: compatible = "xlnx,xps-gpio-1.00.a";

The solution that I encountered was to use the mmap() function (man mmap) to map the peripherals control registers wich are in the physical memory to the user space memory that my application will be using and that it knows at executing time but not before. We can know wich are the peripheral physical addresses looking at the offset address field of the addres editor tab of the block diagram design of the Vivado program, as we can see here: enter image description here
Also I recommend to see the peripherals datasheet (link).
And finally this is the code I used:

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

#define IN 0
#define OUT 1

#define GPIO_0_BASE_ADDR    0x41200000
#define GPIO_1_BASE_ADDR    0x41210000
#define GPIO_ADDR_RANGE     0x10000

int main(void)
{

    int fd;
    int value = 0;

    void *gpio0_virtual_add, *gpio1_virtual_add;    //pointers to the virtual memory

    /* Open /dev/mem file */
    fd = open ("/dev/mem", O_RDWR);
    if (fd < 1) {
        printf("***error opening dev file");
        return -1;
    }

    /* mmap the devices into memory */
    gpio0_virtual_add = mmap(NULL, GPIO_ADDR_RANGE, PROT_READ|PROT_WRITE, MAP_SHARED, fd, GPIO_0_BASE_ADDR);
    if (&gpio0_virtual_add==MAP_FAILED){
        printf("map failed\n");
    }   else {
        printf("device mapped to user space, %d\n", gpio0_virtual_add);
    }

    gpio1_virtual_add = mmap(NULL, GPIO_ADDR_RANGE, PROT_READ|PROT_WRITE, MAP_SHARED, fd, GPIO_1_BASE_ADDR);

    if (&gpio1_virtual_add==MAP_FAILED){
        printf("map 2 failed\n");
    }   else {
        printf("2 device  mapped to user space, %d\n", gpio0_virtual_add);
    }

    /* Read/Write value from/to the device register */
    while (value!=5){
        value = *((unsigned *)(gpio1_virtual_add));     //read value from the buttons
        *((unsigned *)(gpio0_virtual_add)) = value;     //turn on the leds
    }

    /*free virtual memory*/
    munmap(gpio0_virtual_add, GPIO_ADDR_RANGE);         
    munmap(gpio1_virtual_add, GPIO_ADDR_RANGE);

    return 0;
}

Please if something of either what I explained or the code is wrong or confusing ask me know and feel free to correct it.

Upvotes: 2

Related Questions