zoukankan      html  css  js  c++  java
  • Xilinux PS与PL交互::Linux-App读写REG

    Xilinux PS与PL交互::Linux-App读写REG

    背景

    PL配置好有关的硬件,PS端做验证。

    设计方案:针对REG地址,不使用设备树配置。

    遇到的问题:暂无。

    验证目的

    验证PL-PS的各种交互方式。

    这一块的验证是高级的,因为需要用到Linux驱动的有关框架,规范一点还需要配合设备树工作

    验证思路

    1、验证地址的读写是否有问题,之前用过Memory Access工具,可以直接对物理地址进行读写。

    2、编写Linux驱动,达到一样的效果。

    操作记录

    Vivado、SDK、PetaLinux

    无。

    实际上,我在SDK中:

    • [x] 1、 在helloWorld中验证了读写PL给出的内容
    • [x] 2、使用中断例程,验证了来自PL端的按键中断工作正常。

    Linux

    MA小工具

    本来是想自己写的,写到一半的时候不想处理文本转整数,因此在github上找到了一个MA工具:

    实际上,Busybox中有一个devmem的程序同样可以达到这个目的。

    /*
    #    Copyright By S.Ishihara, All Rights Reserved
    #    https://github.com/sig-ishihara/linux_pysical_address_rw_cmd.git
    #
    #    File Name:  ma.c
    #    Created  :  Dec 26, 2011 
    */
    
    #include <stdio.h>
    #include <stdlib.h>
    #include <unistd.h>
    #include <sys/io.h>
    #include <sys/mman.h>
    #include <sys/types.h>
    #include <sys/stat.h>
    #include <fcntl.h>
    
    #define DEV_PATH "/dev/mem"
    
    int main(int argc, char *argv[])
    {
        int             opt;
        extern char     *optarg;
        extern int      optind, opterr;
        int             width = 4;  /* default byte access */
        unsigned int    memaddr, wdata;
        unsigned int    pgoffset, pgaddr;
        unsigned int    pagesize = sysconf(_SC_PAGESIZE);
        unsigned char   *p;
        int             fd;
    
    
        while ((opt = getopt(argc, argv, "w:")) != -1) {
            if (opt == 'w') {
                width = atoi(optarg);
            } else {
                goto error;
            }
        }
    
    
        argc -= optind;
        argv += optind;
    
    
        fd = open(DEV_PATH, O_RDWR);
        if (fd <= 0) {
            fprintf(stderr, "open error: %s
    ", DEV_PATH);
            return 1;
        }
    
    
        if (argc == 1) {
            /* Read Mem */
            memaddr  = strtoul(argv[0], NULL, 16);
            pgoffset = memaddr & (pagesize -1);
            pgaddr   = memaddr & ~(pagesize -1);
            p = mmap(NULL, pagesize, PROT_READ, MAP_SHARED, fd, pgaddr);
            if (p < 0) {
                fprintf(stderr, "mmap error
    ");
                return 1;
            }
            if (width == 1) {
                printf("0x%08x: 0x%02x
    ", memaddr, *(p + pgoffset));
            } else if (width == 2) {
                printf("0x%08x: 0x%04x
    ", memaddr, *((unsigned short *)(p + pgoffset)));
            } else if (width == 4) {
                printf("0x%08x: 0x%08x
    ", memaddr, *((unsigned int *)(p + pgoffset)));
            } else {
                goto error;
            }
        } else if (argc == 2) {
            /* Write Mem */
            memaddr  = strtoul(argv[0], NULL, 16);
            pgoffset = memaddr & (pagesize -1);
            pgaddr   = memaddr & ~(pagesize -1);
            p = mmap(NULL, pagesize, PROT_WRITE, MAP_SHARED, fd, pgaddr);
            if (p < 0) {
                fprintf(stderr, "mmap error
    ");
                return 1;
            }
            wdata  = strtoul(argv[1], NULL, 16);
            if (width == 1) {
                *(p + pgoffset) = (unsigned char)wdata;
            } else if(width == 2) {
                *((unsigned short *)(p + pgoffset)) = (unsigned short)wdata;
            } else if(width == 4) {
                *((unsigned int *)(p + pgoffset)) = (unsigned int)wdata;
            } else {
                goto error;
            }
        } else {
            goto error;
        }
        
        munmap(p, pagesize);
        close(fd);
        return 0;
    
    
    error:
        printf("Usage: Mem [-w WIDTH] ADDRESS [DATA]
    "
                "Mem read or write.
    "
                "  -w        number of byte width. permit 1, 2, 4(default)
    "
                "
    "
                "This command executable only root user.
    "
                "Mem address possible range 32bit.
    "
                "
    "
                "Examples:
    "
                "  Mem a0000           Read memory from address 0xa0000.
    "
                "  Mem -w1 a0000 31    Write memory address 0xa0000 to 0x31.
    "
                "  Mem 20000 5a5a5a5a  Write memory address 0x20000 to 0x5a5a5a5a.
    "
                "
    ");
        return 1;
    }
    

    编译通过以后,对AXI地址进行验证:

    1、写入再读出AXI-REG,正常:

    #define XPAR_M_AVALON_0_BASEADDR 0x43C00000
    
    # 读取
    ./md 0x43C00000
    0x43c00000: 0x00
    
    # 写入
    ./md 0x43C00000  1
    
    # 再读取
    ./md 0x43C00000
    0x43c00000: 0x01
    

    2、读取PL按键正常:

    #define XPAR_AXI_GPIO_1_BASEADDR 0x41210000
    
    # 读取(未按下)
    ./md 0x41210000
    0x41210000: 0x01
    # 读取(未按下)
    ./md 0x41210000
    0x41210000: 0x00
    

    3、控制LED灯,正常:

    #define XPAR_AXI_GPIO_0_BASEADDR 0x41200000
    
    # 开启
    ./md 0x41200000 1
    
    # 关闭
    ./md 0x41200000 0
    

    地址读写驱动

    在MA控制正常以后,转而使用驱动来处理。

    以下的驱动仅实现了 AXI-LED 灯的控制,但是已经能够作为一个简单的地址读写

    待完善:一片内存的读写,read、write方法

    模块
    #
    #    File Name:  mymodule.c
    #    Created  :  2020-07-27 09:19:51
    #    Usage    :
    #               insmod modulexx.ko
    #           then
    #               cat /proc/devices
    */
    
    #include <linux/module.h>
    #include <linux/kernel.h>
    #include <linux/init.h>
    
    #include <linux/printk.h>
    
    #include <linux/fs.h>
    #include <linux/cdev.h>
    #include <linux/device.h>
    
    #include <linux/io.h>
    #include <linux/uaccess.h>
    
    //#include <linux/slab.h>
    #include <asm/io.h>
    
    #define ZYNQ_AXI_ON  _IOW('L', 1, unsigned long)
    #define ZYNQ_AXI_OFF _IOW('L', 2, unsigned long)
    
    #define XPAR_AXI_GPIO_0_BASEADDR 0x41200000 // LED _out
    // todo
    #define XPAR_AXI_GPIO_1_BASEADDR 0x41210000 // Btn _in
    #define XPAR_M_AVALON_0_BASEADDR 0x43C00000 // REG_I/O
    
    /*
        # 开启
        ./md 0x41200000 1
    
        # 关闭
        ./md 0x41200000 0
    */
    
    static struct cdev zynq_axi_cdev;
    static unsigned int axi_major = 0;
    static unsigned int axi_minor = 0;
    static dev_t axi_num;
    static struct class*    axi_class;
    static struct device*   axi_device;
    static struct resource* axi_res;
    
    static void __iomem *   axi_led_base_va;
    #if 0
    static void __iomem *   axi_reg_base_va;
    static void __iomem *   axi_btn_base_va;
    #endif
    static char *temp = NULL;
    
    #define XPAR_M_AVALON_0_BASEADDR 0x43C00000
    #define XPAR_M_AVALON_0_HIGHADDR 0x43C0FFFF
    
    #define AXI_REG         (XPAR_AXI_GPIO_0_BASEADDR)
    #define AXI_REG_SIZE    1 //(XPAR_M_AVALON_0_HIGHADDR - XPAR_M_AVALON_0_BASEADDR)
    
    int zynq_axi_open(struct inode *inode, struct file *file) {
        return 0;
    }
    
    #if 0
            switch(args){
            case 8:
                writel(readl(axi_led_out_va) | (1<<17), axi_led_out_va);
                break;
            case 9:
                writel(readl(axi_led_out_va) | (1<<8), axi_led_out_va);
                break;
            case 10:
                writel(readl(axi_led_out_va) | (1<<7), axi_led_out_va);
                break;
            case 11:
                writel(readl(axi_led_out_va) | (1<<12), axi_led_out_va);
                break;
            default:
                return -EINVAL;
            }
    #endif
    
    long zynq_axi_ioctl(struct file *file, unsigned int cmd, unsigned long args) {
    
        switch(cmd) {
        case ZYNQ_AXI_ON:
            writel(1, axi_led_base_va);
            break;
        case ZYNQ_AXI_OFF:
            writel(0, axi_led_base_va);
            break;
        default:
            return -ENOIOCTLCMD;
        }
        
        return 0;
    }
    
    static ssize_t zynq_axi_read(struct file *filep, char __user *buf, size_t len, loff_t *pos) {
    
        if( len > 64)
        {
            len = 64;
        }
    
    #if 0
        if(copy_to_user(buf, temp, len))
        {
            return -EFAULT;
        }
    #endif
    
        return len;
    }
    
    static ssize_t zynq_axi_write(struct file *filep, const char __user *buf, size_t len, loff_t *pos) {
    
        if( len > 64) {
            len = 64;
        }
        
        if(copy_from_user(buf, temp, len)) {
            return -EFAULT;
        }
        
        printk("<4>" "write %s
    ",temp);
        return len;
    }
    
    static  const struct file_operations zynq_axi_ops = {
        .owner = THIS_MODULE,
        .open  = zynq_axi_open,
        .read  = zynq_axi_read,
        .write = zynq_axi_write,
        .unlocked_ioctl = zynq_axi_ioctl,
    };
    
    static int __init zynq_axi_init(void) {
        int retval;
        if(axi_major == 0)
            retval = alloc_chrdev_region(&axi_num, axi_minor, 1, "axi_device");
        else {
            axi_num = MKDEV(axi_major, axi_minor);
            retval = register_chrdev_region(axi_num, 1, "axi_device");
        }
    
        if(retval < 0) {
            printk("can not get device number
    ");
            goto chrdev_region_error;
        }
        
        cdev_init(&zynq_axi_cdev, &zynq_axi_ops);
        
        retval = cdev_add(&zynq_axi_cdev, axi_num, 1);
        if(retval < 0) {
            printk(KERN_WARNING "cdev_add error
    ");
            goto cdev_add_error;
        }
        
        axi_class = class_create(THIS_MODULE, "zynq_axi_class");
        if(axi_class == NULL) {
            printk(KERN_WARNING "class_create error
    ");
            retval = -EBUSY;
            goto class_create_error;
        }
    /*
    cd / && find . -name "axi_drv"
    ./sys/devices/virtual/zynq_axi_class/axi_drv
    ./sys/class/zynq_axi_class/axi_drv
    ./dev/axi_drv
    */
        axi_device = device_create(axi_class, NULL, axi_num, NULL, "axi_drv");
        if(axi_device == NULL) {
            retval = -EBUSY;
            printk(KERN_WARNING "device_create error
    ");
            goto device_create_error;
        }
        
        // 用于操作物理地址
        axi_res = request_mem_region(AXI_REG, AXI_REG_SIZE, "axi_led_MEM"); // cat /proc/iomem
        if(axi_res == NULL) {
            retval = -EBUSY;
            printk(KERN_WARNING "request_mem_region error
    ");
            goto request_mem_region_error;
        }
        
        axi_led_base_va = ioremap(AXI_REG, AXI_REG_SIZE);
        if(axi_led_base_va == NULL){
            retval = -EBUSY;
            printk(KERN_WARNING "ioremap error
    ");
            goto ioremap_error;
        }
        
        //temp = kmalloc(sizeof(char) * 64, GFP_KERNEL);
    
    #if 0
        // 可供参考
        axi_led_out_va = axi_led_base_va + 0x00;
        axi_led_outenb_va = axi_led_base_va + 0x04;
        axi_led_altfn0_va = axi_led_base_va + 0x20;
        axi_led_altfn1_va = axi_led_base_va + 0x24;
    
        temp = readl(axi_led_altfn0_va);
        temp &= ~((3<<14) | (3<<16) | (3<<24));
        temp |= ((1<<14) | (1<<16) | (1<<24));
        writel(temp, axi_led_altfn0_va);
        
        temp = readl(axi_led_altfn1_va);
        temp &= ~(3<<2);
        temp |= (1<<2);
        writel(temp, axi_led_altfn1_va);
        
        //将GPIOC17  GPIOC8 GPIOC7 GPIOC12设置为输出
        temp = readl(axi_led_outenb_va);
        temp |= ((1<<17) | (1<<8) | (1<<7) | (1<<12));
        writel(temp, axi_led_outenb_va);
        
        //将GPIOC17  GPIOC8 GPIOC7 GPIOC12输出高电平
        temp = readl(axi_led_out_va);
        temp |= ((1<<17) | (1<<8) | (1<<7) | (1<<12));
        writel(temp, axi_led_out_va);
    #endif
    
        printk(KERN_WARNING "zynq_axi_init
    ");
        
        return 0;
    
    ioremap_error:
        release_mem_region(AXI_REG, AXI_REG_SIZE);
        axi_res = NULL;
    request_mem_region_error:
        device_destroy(axi_class, axi_num);
        axi_device = NULL;
    device_create_error:
        class_destroy(axi_class);
        axi_class = NULL;
    class_create_error:
        cdev_del(&zynq_axi_cdev);
    cdev_add_error:
        unregister_chrdev_region(axi_num, 1);
    chrdev_region_error:
        return retval;
    }
    
    static void __exit zynq_axi_exit(void) {
        iounmap(axi_led_base_va);
        release_mem_region(AXI_REG, AXI_REG_SIZE);
        axi_res = NULL;
        device_destroy(axi_class, axi_num);
        axi_device = NULL;
        class_destroy(axi_class);
        axi_class = NULL;
        cdev_del(&zynq_axi_cdev);
        unregister_chrdev_region(axi_num, 1);
        //kfree(temp);
        printk(KERN_WARNING "zynq_axi_exit
    ");
    }
    
    module_init(zynq_axi_init);
    module_exit(zynq_axi_exit);
    
    MODULE_AUTHOR("Schips for ITC");
    MODULE_DESCRIPTION("Zynq axi Device Driver");
    MODULE_LICENSE("GPL");
    MODULE_VERSION("V1.0");
    
    应用程序
    #include <stdio.h>
    #include <fcntl.h>
    #include <stdio.h>
    #include <unistd.h>
    #include <stdlib.h>
    #include <sys/ioctl.h>
    
    #define ZYNQ_AXI_ON  _IOW('L', 1, unsigned long)
    #define ZYNQ_AXI_OFF _IOW('L', 2, unsigned long)
    
    int main(int argc, char * argv[])
    {
        int fd;
        int i;
    
        fd = open("/dev/axi_drv", O_RDWR);
        if(fd < 0)
        {
            perror("open fail 
    ");
            return -1;
        }
    
        for (i = 0; i < 5; ++i)
        {
            ioctl(fd, ZYNQ_AXI_ON);
            printf("On
    ");
            sleep(1);
        }
    
        close(fd);
        return 0;
    }
    

    情况正常。

    如果说我的文章对你有用,只不过是我站在巨人的肩膀上再继续努力罢了。
    若在页首无特别声明,本篇文章由 Schips 经过整理后发布。
    博客地址:https://www.cnblogs.com/schips/
  • 相关阅读:
    CSS 基本语法
    Html 块
    Html 表单
    Html 创建表格
    Html 建立超链接
    Html 在网页中使用图像
    CentOS7 重启问题
    Java 多线程——多线程的引入
    c++ 自然排序-window文件排序
    MFC更改图标
  • 原文地址:https://www.cnblogs.com/schips/p/xilinx_sdk_how_linux_program_read-write_pl_side_regs.html
Copyright © 2011-2022 走看看