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中:
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\n", 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\n");
return 1;
}
if (width == 1) {
printf("0x%08x: 0x%02x\n", memaddr, *(p + pgoffset));
} else if (width == 2) {
printf("0x%08x: 0x%04x\n", memaddr, *((unsigned short *)(p + pgoffset)));
} else if (width == 4) {
printf("0x%08x: 0x%08x\n", 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\n");
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]\n"
"Mem read or write.\n"
" -w number of byte width. permit 1, 2, 4(default)\n"
"\n"
"This command executable only root user.\n"
"Mem address possible range 32bit.\n"
"\n"
"Examples:\n"
" Mem a0000 Read memory from address 0xa0000.\n"
" Mem -w1 a0000 31 Write memory address 0xa0000 to 0x31.\n"
" Mem 20000 5a5a5a5a Write memory address 0x20000 to 0x5a5a5a5a.\n"
"\n");
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\n",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\n");
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\n");
goto cdev_add_error;
}
axi_class = class_create(THIS_MODULE, "zynq_axi_class");
if(axi_class == NULL) {
printk(KERN_WARNING "class_create error\n");
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\n");
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\n");
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\n");
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\n");
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\n");
}
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 \n");
return -1;
}
for (i = 0; i < 5; ++i)
{
ioctl(fd, ZYNQ_AXI_ON);
printf("On\n");
sleep(1);
}
close(fd);
return 0;
}
情况正常。
如果说我的文章对你有用,只不过是我站在巨人的肩膀上再继续努力罢了。
若在页首无特别声明,本篇文章由 Schips 经过整理后发布。
博客地址:https://www.cnblogs.com/schips/
若在页首无特别声明,本篇文章由 Schips 经过整理后发布。
博客地址:https://www.cnblogs.com/schips/