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

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;
}

情况正常。