Chinaunix

标题: FPGA-based设备驱动 [打印本页]

作者: linuxfellow    时间: 2012-06-21 11:13
标题: FPGA-based设备驱动
FPGA-based设备驱动可以有下面3种实现方法:
1。用户程序直接调用mmap(), 把fpga base address映射到用户空间; 用户程序直接从映射后的用户空间地址操作fpga寄存器
2。写一个驱动程序,驱动操作函数组里实现mmap函数, 驱动的这个mmap函数会利用remap_pfn_range()把fpga base address映射到用户空间; 用户程序用驱动程序影射的地址操作fpga寄存器
3 写一个驱动程序, 驱动程序会利用ioremap()把fpga base address映射到内核空间地址;驱动程序再提供read()/write()给用户来操作ioremap影射的内核地址,进尔操作fpga寄存器
那一种map是linux比较钟爱的方法?
作者: linuxfellow    时间: 2012-06-24 03:06
没人点评一下?
我看内核例程有些驱动是第三种方法
有的书上用的是第二方法作例子
我自己写的驱动用了最简单的第一种方法
作者: moon146    时间: 2012-06-24 10:41
方法简单,通用性差
作者: linuxfellow    时间: 2012-06-24 21:07
回复 3# moon146
FPGA实现的硬件功能本身差别较大, 功能不一样时不能指望驱动能通用;功能相同时只要变变base address就可以通用。

   
作者: bensenq    时间: 2012-06-25 21:42
本帖最后由 bensenq 于 2012-06-25 21:42 编辑

我怎么感觉你说的前两种方法是一回事儿呢?不都是使用remap_pfn_range把物理地址映射到用户空间么?
回复 1# linuxfellow



作者: linuxfellow    时间: 2012-06-27 10:10
回复 5# bensenq
第一种方法是直接从用户空间通过mmap系统调用来实现,不用写驱动
第二种方法驱动要实现

   
作者: bensenq    时间: 2012-06-27 20:55
你说的第一种,用户调用mmap进行映射是不是要制定给一个fd,这样mmap系统调用会找到对应设备程序中file operations结构中的mmap(),然后mmap调用remap_pfn_range最终实现映射。这不就是一回事儿么?
莫非还可以不用实现驱动中的mmap,利用默认的mmap就可以映射物理空间、IO?
linuxfellow 发表于 2012-06-27 10:10
回复 5# bensenq
第一种方法是直接从用户空间通过mmap系统调用来实现,不用写驱动
第二种方法驱动要实现 ...

作者: linuxfellow    时间: 2012-06-28 08:44
本帖最后由 linuxfellow 于 2012-06-28 09:11 编辑

回复 7# bensenq
不好意思,你是对的:
第一种方法,用现成驱动“/dev/mem"的mmap()来映射物理空间、IO;
第二种方法,写自己的驱动“/dev/myfpga" 实现mmap()来映射物理空间、IO
两种方法是一样的

   
作者: wawava    时间: 2012-07-09 21:02
小弟菜鸟一个,问个初级问题,怎么加载FPGA程序呢?
作者: linuxfellow    时间: 2012-07-10 10:05
回复 9# wawava
google或baidu上查一下“FPGA程序下载”, 会有详细答案

   
作者: wawava    时间: 2012-07-10 21:21
linuxfellow 发表于 2012-06-28 08:44
回复 7# bensenq
不好意思,你是对的:
第一种方法,用现成驱动“/dev/mem"的mmap()来映射物理空间、I ...

第一种方法不需要在kernel中编写FPGA的驱动吗?
作者: linuxfellow    时间: 2012-07-11 11:04
回复 11# wawava
不需要,直接用系统提供的 /dev/mem驱动

   
作者: wawava    时间: 2012-07-11 11:50
那在应用程序中mmap的参数:star和fd的值怎么填写?
作者: linuxfellow    时间: 2012-07-11 12:33
回复 13# wawava
帮你找了一个例子:
http://stackoverflow.com/questio ... -user-spacein-linux

   
作者: wawava    时间: 2012-07-11 13:16
使用mem访问地址已搞定,谢谢LZ。现在还有几个问题:
1 看网上说中断在用户空间无法使用,我要在应用程序中使用fpga的中断,使用mem映射后可行吗,应用程序中使用request_irq就可以了吗?
2 看网上说新内核只支持1m内存映射,我用的kernel是2.6.27,我映射的物理地址是0xe3000000,好像没问题。是我这个版本低了吗?新版本就不行了吗?

作者: linuxfellow    时间: 2012-07-11 13:54
回复 15# wawava
上面那个例子是从把内禾地址映射到用户空间,从而通过用户空间访问kernel address.
request_irq()不能从用户空间调用;如果你的FPGA触发中断,第一种方法就不适用;你可能要写内核空间的驱动。用第二或第三种方法。

内核空间和用户空间地址比例是可以配置的: (1G/3G)/(2G/2G)/(3G/1G)


   
作者: wawava    时间: 2012-07-11 17:15
从网上找了则linux下的fpga驱动,有几个地方不懂,请大侠给讲解下:

/*
* FPGA support
*/
MODULE_LICENSE("Dual BSD/GPL";

#define FPGA_PHY_START                0xe3000000
#define FPGA_PHY_SIZE                 (0x01000000)
#define FPGA_MAJOR                         211                //主设备号211
#define FPGA_MINOR                         0                //从设备号0

unsigned long fpga_vp;                                //映射的虚拟地址

#define fpga_dma_channel 0
#define DCG_GPIO1_BASE     0xB1900000
#define DCG_GOIO1_MODE          0x002C
#define DMA_DDMA0_BASE   0xB4002000
#define DMA_GLOBAL_BASE   0xB4003000


struct fpga_dma_device{
        unsigned int dma_irq;
        unsigned int dma_channel;
};


static int fpga_open(struct inode *,struct file*);
static int fpga_release(struct inode *,struct file *);
static ssize_t fpga_read(struct file *filp, char __user *buf, size_t count,loff_t *f_pos);
static ssize_t fpga_write(struct file *filp, const char __user *buf, size_t count,loff_t *f_pos);
static int fpga_mmap( struct file *,struct vm_area_struct *);
static int fpga_ioctl(struct inode *,struct file *);

struct file_operations fpga_fops={
        .open=fpga_open,
        .release=fpga_release,
        .read=fpga_read,
        .write=fpga_write,
        .ioctl=fpga_ioctl,
        .mmap=fpga_mmap,
        .owner=THIS_MODULE
};

irqreturn_t fpga_dma_interrupt(int irq, void *dev_id)
{
        struct motion_key_dev * motiondev = (struct motion_key_dev *)dev_id;
        //u32 reg_value = 0;
        (motion_key_device->count)++;
        motion_key_device->int_come = 1;
        printk(KERN_WARNING "Enter motion_key_interrup %d \n", motion_key_device->count);
        printk(KERN_WARNING "reg value = 0x%x \n", __raw_readl(S3C24XX_EXTINT2));
        wake_up_interruptible(&motiondev->key_queue);

        printk(KERN_INFO "fpga_dma_interrupt \n";
        return IRQ_HANDLED;
}

static int fpga_open(struct inode *ip,struct file *fp)
{
        struct fpga_dma_device *fpga_device;
        int error = 0;

        if((error=request_irq(fpga_device->dma_irq, fpga_dma_interrupt, IRQF_DISABLED, "fpga", NULL)))
                return error;
        if((error=request_dma(fpga_device.dma_channel,"fpga")){
                free_irq(fpga_device.dma_irq,NULL);
                return error;
        }
        fp->f_op=&fpga_fops;

        fpga_vp=(unsigned long)ioremap(FPGA_PHY_START,FPGA_PHY_SIZE);

        if(!fpga_vp){
                printk("ioremap  fpga failed\n";
                return -EINVAL;
        }
        printk(KERN_INFO"fpga_vp is 0x%.8x \n",fpga_vp);
        return(0);
}

static int fpga_release(struct inode *ip,struct file *fp)
{
        if(fpga_vp)
                iounmap(fpga_vp);

        struct fpga_dma_device *fpga_device;
        free_dma(fpga_dma_device->dma_channel);
        free_irq(fpga_device->dma_irq,NULL);         

        return(0);
}

static ssize_t fpga_read(struct file *filp, char __user *buf, size_t count,loff_t *f_pos)
{
        unsigned char *p = (unsigned char *)fpga_vp;
        ssize_t retval = 0;
        if(copy_to_user(buf, p, count))
        {
                retval = -EFAULT;
goto out;
        }
                retval = count;
out:
        return retval;
}



static ssize_t fpga_write(struct file *filp, const char __user *buf, size_t count,loff_t *f_pos)
{
        //unsigned char *p = (unsigned char *)fpga_vp;
        ssize_t retval = 0;
        //printk("Enter write\n";
        if(copy_from_user(fpga_vp, buf, count))
        {
                retval = -EFAULT;
                printk("Write error\n";
goto out;
        }
                retval = count;
out:
return retval;
}



void fpga_vma_open(struct vm_area_struct *vma)
{ return; }

void fpga_vma_close(struct vm_area_struct *vma)
{ return; }


static struct vm_operations_struct fpga_remap_vm_ops = {
        .open =  fpga_vma_open,
        .close = fpga_vma_close,
};


static int fpga_mmap( struct file *filp,struct vm_area_struct *vma)
{
        unsigned int len;
        unsigned long start=0, off;

        if (vma->vm_pgoff > (~0UL >> PAGE_SHIFT))
        {
                printk(" Error vma->vm_pgoff > !OUL PAGE_SHIFT \n";
                 return -EINVAL;
                }

        start =  FPGA_PHY_START;
        len =  FPGA_PHY_SIZE;
        off = vma->vm_pgoff << PAGE_SHIFT;

        if ((vma->vm_end - vma->vm_start + off) > len)
         {
                printk(" Error vma->vm_end-vma->vm_start\n";
                return -EINVAL;
         }

        off += start;
        vma->vm_pgoff = off >> PAGE_SHIFT;
        pgprot_val(vma->vm_page_prot) &= ~_CACHE_MASK;
        pgprot_val(vma->vm_page_prot) |= PAGE_CACHABLE_DEFAULT;


        vma->vm_flags |= VM_IO;

        if (remap_pfn_range(vma,vma->vm_start, off,
                                vma->vm_end - vma->vm_start,
                                vma->vm_page_prot))
        {
                return -EAGAIN;
        }
}


static int fpga_ioctl(struct inode *ip,struct file *fp)
{
        printk("use fpga_ioctl\n";
        return 0;
}

void fpga_dma_init(void)
{
        unsigned int gpio1_mode=0x00000010;
        au_writel( gpio1_mode , DCG_GPIO1_BASE + 0x0100 );//sys_triout--->GPIO4
        gpio1_mode=au_readl(DCG_GPIO1_BASE + 0x0100);
        printk(KERN_ALERT "sys_triout--->GPIO4  : %.8x \n",gpio1_mode);

        unsigned int dma_val=au_readl(DMA_GLOBAL_BASE+0x0000);//ddma_config

        dma_val |=0x00000000;
        au_writel(dma_val,DMA_GLOBAL_BASE+0x0000);

        dma_val=au_readl(DMA_GLOBAL_BASE+0x000;//ddma_throttle
        dma_val |= 0x80000000;

        au_writel(dma_val,DMA_GLOBAL_BASE+0x000;

        dma_val=au_readl(DMA_GLOBAL_BASE+0x000c);//ddma_inten
        dma_val |= 0x00000001;

        au_writel(dma_val,DMA_GLOBAL_BASE+0x000c);
}

static int __init fpga_init(void)
{
        int fpga_base;
        int fpgamajor;
        unsigned int mac0_phy;

        printk(KERN_INFO "-----fpga----\n";

        fpga_dma_init();

        fpgamajor=register_chrdev(FPGA_MAJOR,"fpga",&fpga_fops);
        if(fpgamajor < 0)
        {
                printk(KERN_ERR "register_chrdev fault! \n");
                return 0;
        }
        printk(KERN_INFO "fpga:fpgamajor=0x%x or %d\n",fpgamajor,fpgamajor);
        printk(KERN_INFO "fpga:init_module() OUT \n");

        return 0;
}

static void __exit fpga_exit(void)
{
        unregister_chrdev(FPGA_MAJOR,"fpga");
        printk(KERN_INFO "fpga cleanup_module OUT \n");
}


module_init(fpga_init);

module_exit(fpga_exit);


/*
* END FPGA support
*/
上面这段代码中有一下几个问题不懂
1 fpga_dma_init函数起什么作用?fpga驱动需要用到dma?
2 那个fpga_mmap函数不懂,PAGE_SHIFT ,_CACHE_MASK ,PAGE_CACHABLE_DEFAULT这几个参数什么意思?
3 注册中断究竟要怎么用?先在kernel中request_irq再在应用程序中使用Request_irq?





作者: linuxfellow    时间: 2012-07-12 12:29
本帖最后由 linuxfellow 于 2012-07-12 12:34 编辑

回复 17# wawava

我正在写自己的FPGA驱动,比你找的这个简单的多。没有DMA, 没有中断,写起来相对容易。
你找的这个驱动是老式驱动,设备注册方式也是老式的, 没有用新驱动框架。

下面我试着回答你的问题:
1 fpga_dma_init函数起什么作用?fpga驱动需要用到dma?
  这个FPGA里实现了DMA功能,如果系统里有用到DMA, 就要对其进行初始化或配置。
2 那个fpga_mmap函数不懂,PAGE_SHIFT ,_CACHE_MASK ,PAGE_CACHABLE_DEFAULT这几个参数什么意思?
  给定了FPGA的物理地址,驱动运行在内核,不能直接操作物理地址,就要将FPGA的物理地址映射到内核地址空间,在进行操作。mmap的作用就是将FPGA的物理地址映射到内核地址空间。映射后有些保护标志要进行设置。主要是保证DMA的memory是不被cache
  PAGE_SHIFT =12  页面大小为4K
  _CACHE_MASK 页表中与保护有关的标志,用于DMA的memory是不能cache的,这些标志要清除
    PAGE_CACHABLE_DEFAULT是页面缺省的标志, 一般为_CACHE_UNCACHED
3 注册中断究竟要怎么用?先在kernel中request_irq再在应用程序中使用Request_irq?
例子中的FPGA会产生中断,FPGA作为中断源,有中断输出信号接到处理器中断控制器的某个中断信号。中断发生时,处理器就会中断目前正在运行的程序,转而执行该中断的处理程序。
Linux实现了两级中断表,第一级中断表在系统初始化时就填好,第二级中断表只填了一个简单的default的处理程序。Request_irq就是在二级中断表里相应的表项里填该FPGA特殊的中断处理程序。在内核里调用Request_irq后该中断就设置好了。FPGA请求中断时,这一处理程序就会被调用来处理相应中断。用户空间不需再作任何事情

   
作者: wawava    时间: 2012-07-12 13:03

/*
* FPGA support
*/
MODULE_LICENSE("GPL");


#define FPGA_PHY_START                0xe3000000
#define FPGA_PHY_SIZE                 (0x01000000)
#define FPGA_MAJOR                         211                //主设备号211
#define FPGA_MINOR                         0                //从设备号0

unsigned long fpga_vp;                                //映射的虚拟地址


#define         DEVICE_NAME "FPGA_Driver"

static int FPGA_Major = FPGA_MAJOR;
static int FPGA_Minor = 0;

struct cdev* FPGA_cdev = NULL;

static int fpga_open(struct inode *,struct file*);
static int fpga_release(struct inode *,struct file *);
static ssize_t fpga_read(struct file *filp, char __user *buf, size_t count,loff_t *f_pos);
static ssize_t fpga_write(struct file *filp, const char __user *buf, size_t count,loff_t *f_pos);
static int fpga_mmap( struct file *,struct vm_area_struct *);
static int fpga_ioctl(struct inode *,struct file *);

struct file_operations fpga_fops={
        .open=fpga_open,
        .release=fpga_release,
        .read=fpga_read,
        .write=fpga_write,
        .ioctl=fpga_ioctl,
        .mmap=fpga_mmap,
        .owner=THIS_MODULE
};

irqreturn_t fpga_interrupt(int irq, void *dev_id)
{
        printk(KERN_INFO "fpga_dma_interrupt \n");
        return IRQ_HANDLED;
}

static int fpga_open(struct inode *ip,struct file *fp)
{
        int error = 0;

        if((error=request_irq(IRQ_GPI_03, fpga_interrupt, IRQF_DISABLED, "fpga", NULL)))
        {
                printk(KERN_INFO" request_irq fault!\n");
        }

        fp->f_op=&fpga_fops;

        fpga_vp=(unsigned long)ioremap(FPGA_PHY_START,FPGA_PHY_SIZE);

        if(!fpga_vp){
                printk("ioremap  fpga failed\n");
                return -EINVAL;
        }
        printk(KERN_INFO"fpga_vp is 0x%.8x \n",fpga_vp);
        return(0);
}

static int fpga_release(struct inode *ip,struct file *fp)
{
        if(fpga_vp)
                iounmap(fpga_vp);

        struct fpga_dma_device *fpga_device;
        free_irq(IRQ_GPI_03,NULL);         

        return(0);
}

static ssize_t fpga_read(struct file *filp, char __user *buf, size_t count,loff_t *f_pos)
{
        unsigned char *p = (unsigned char *)fpga_vp;
        ssize_t retval = 0;
        if(copy_to_user(buf, p, count))
        {
                retval = -EFAULT;
goto out;
        }
                retval = count;
out:
        return retval;
}



static ssize_t fpga_write(struct file *filp, const char __user *buf, size_t count,loff_t *f_pos)
{
        //unsigned char *p = (unsigned char *)fpga_vp;
        ssize_t retval = 0;
        //printk("Enter write\n");
        if(copy_from_user(fpga_vp, buf, count))
        {
                retval = -EFAULT;
                printk("Write error\n");
goto out;
        }
                retval = count;
out:
return retval;
}


static int fpga_mmap( struct file *filp,struct vm_area_struct *vma)
{
        unsigned long off = vma->vm_pgoff << PAGE_SHIFT;
        unsigned long physical = FPGA_PHY_START + off;
        unsigned long vsize = vma->vm_end - vma->vm_start;
        unsigned long psize = FPGA_PHY_SIZE - off;
        if(vsize > psize)
                return -EINVAL; /*spans too high*/
        vma->vm_flags |= VM_IO|VM_RESERVED;
        vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
        /*remap physic addr to kernel virtual addr*/
        remap_pfn_range(vma, vma->vm_start, physical, vsize, vma->vm_page_prot);
        printk(KERN_INFO "memory mapped to user space\n");
        return 0;
}


static int fpga_ioctl(struct inode *ip,struct file *fp)
{
        printk("use fpga_ioctl\n");
        return 0;
}

static int __init fpga_init(void)
{
        int ret;
        dev_t dev = 0;               
        FPGA_cdev = cdev_alloc();
       
        printk(DEVICE_NAME"start init\n");
        /*register device, if FPGA_Major is not 0, it will get this major number, or allocated by kernel
         * register_chrdev_region instead of register_chrdev for v2.6 kernel*/
        if (FPGA_Major)
        {
                dev = MKDEV(FPGA_Major, FPGA_Minor);
                ret = register_chrdev_region(dev, 1, DEVICE_NAME);
        }
        else
        {
                ret = alloc_chrdev_region(&dev, FPGA_Minor, 1,        DEVICE_NAME);
                FPGA_Major = MAJOR(dev);
        }
        if (ret < 0)
        {
                printk(KERN_WARNING "FPGA: can't get major %d\n", FPGA_Major);
                return ret;
        }
       
        cdev_init(FPGA_cdev, &fpga_fops);
        FPGA_cdev->owner = THIS_MODULE;
        FPGA_cdev->ops = &fpga_fops;
        ret = cdev_add (FPGA_cdev, MKDEV(FPGA_Major, FPGA_Minor), 1);
        /* Fail gracefully if need be */
        if (ret)
        {
                printk(KERN_NOTICE "Error %d adding FPGA", ret);
                return ret;
        }
   
        printk(DEVICE_NAME"inited\n");
        return 0;
}

static void __exit fpga_exit(void)
{                       
        dev_t dev;
       
        dev = MKDEV(FPGA_Major, FPGA_Minor);
       
        unregister_chrdev_region(dev, 1);
        if(FPGA_cdev)
        {
                        cdev_del(FPGA_cdev);
                        printk(DEVICE_NAME"delete cdev\n");
        }

        printk(DEVICE_NAME"exit\n");
        return;
}

module_init(fpga_init);

module_exit(fpga_exit);


/*
* END FPGA support
*/
驱动又改了下,没用dma了,先用register_chrdev_region注册设备,现在这个mmap函数参照网上改的,我下载到板子上试了下,能映射到地址了。
我在fpga_open函数中挂载中断,为什么失败啊?
我的fpga_read和fpga_write函数写的对吗,使用copy_from_user、 copy_to_user就可以了吗?我看你说的方法2中是不是不用在内核中写write和read函数啊?
我从应用程序中加载fpga,直接写到对应得地址就可以了吗?
您说的那个中断我还是不太明白,那个中断处理程序是我应用程序中的一部分,我怎么注册这个中断处理程序呢?
小弟菜鸟一个,问题有点多啊,真的太谢谢lz了,如果方便的话留个联系方式吧
作者: linuxfellow    时间: 2012-07-13 12:46
本帖最后由 linuxfellow 于 2012-07-13 13:21 编辑

回复 19# wawava

把中断request的语句稍微改一下,并把这部分代码移到module_init里面,不要放在open函数里
        error=request_irq(IRQ_GPI_03, fpga_interrupt, IRQF_DISABLED, "fpga", NULL);
        if(error)
        {
                printk(KERN_INFO" request_irq fault!\n");
        }
中断请求错误信息是什么?
copy_from_user、 copy_to_user只是用于用户空间和内核空间传递参数;read/write函数一般是向设备io口或寄存器里读写数据。
你的read()读了fpga base开始的几个字节(count), write()也只是向fpga base开始的几个字节里写; 这是你想要的操作?
从应用程序中加载fpga,更不能直接写到对应得地址。用第一种方法,把物理地址映射到用户空间,然后加载。

写驱动,关键得懂硬件设备如何工作;不知道硬件设备如何工作,就开始写驱动,无论如何也写不好。
你应该多和你们的硬件工程师聊聊,让他告诉你FPGA实现了那些功能,FPGA有那些寄存器, 那些用于功能控制,那些用于状态监测。你的板子的功能又是什么,FPGA的作用,等等。整体功能清楚了,驱动写起来就是水到渠成。
中断如何工作? 最好还是请教FPGA设计人员,他会给你讲明白什么时候发生中断,中断发生后,如何进行处理。他不一定知道如何写软件,但他肯定知道FPGA如何工作,中断应该如何被处理。

还有你既然刚开始学写驱动,花点时间把新的驱动框架弄明白。这很花时间, 但这个时间你无论如何要花。你在网上找的这个驱动模式已经有点过时,在目前kernel release的驱动里已经找不到这样的代码了。以后如果要找驱动的例子,就直接到最新release的linux/drivers里找
   
作者: wawava    时间: 2012-07-13 23:46
使用第一种方法把FPGA加载搞定了,第二种和第三种方法还没搞成功,看有些评论说直接用mem映射后的地址操作会有点慢,LZ有对比过没?
第二种方法只需要实现mmap函数不需要再ioremap吗?
我现在还是没搞明白究竟怎么才能把我运行在用户空间的应用程序中的函数注册成为中断处理函数,看网上说只有内核能处理中断,是这样吗?还是必须把中断处理函数写在内核里?看有文章说可以用signal(SIGIO,test_handler)?
LZ好人啊~~
作者: EZWORD    时间: 2012-07-14 11:16
本帖最后由 EZWORD 于 2012-07-14 11:18 编辑

现在多数用第二种方法吧,至少见到的产品都是这样的。

在内核中使用do_mmap_pgoff(file,0,memsize,0x3,0x1,phyaddr>>PAGE_SHIFT);返回值到用户空间即可使用。
作者: linuxfellow    时间: 2012-07-14 20:12
本帖最后由 linuxfellow 于 2012-07-14 20:15 编辑

回复 21# wawava
使用第一种方法把FPGA加载搞定了,第二种和第三种方法还没搞成功,看有些评论说直接用mem映射后的地址操作会有点慢,LZ有对比过没?
没比过, 你自己可以试试。

第二种方法只需要实现mmap函数不需要再ioremap吗?
mmap是把物理地址映射到用户空间,ioremap把物理地址映射到内核空间。看你的驱动操作在哪里实现。在用户空间就用前者,在内核空间就用后者。

我现在还是没搞明白究竟怎么才能把我运行在用户空间的应用程序中的函数注册成为中断处理函数,看网上说只有内核能处理中断,是这样吗?还是必须把中断处理函数写在内核里?看有文章说可以用signal(SIGIO,test_handler)?
中断处理函数不会属于你的应用程序。中断处理函数运行在中断上下文,不会在你的应用程序进程的上下文。如果你一定想要它运行在进程上下文,那也只能用在一个内核管理下的worker thread,不会是应用程序进程。
signal(SIGIO,test_handler)是一种异步通知方法。驱动软件检测到某个事件后, 就向应用程序发出信号,应用程序接受到信号后就调用handler进行处理。

   
作者: linuxfellow    时间: 2012-07-14 20:24
本帖最后由 linuxfellow 于 2012-07-14 20:29 编辑

回复 22# EZWORD
有可能。但我现在倾向于第三种方法,很方便实现/sys接口。实现了sys接口后,不需要测试程序,直接从/sys接口里测试驱动是否工作。写应用程序的人,自己程序不工作时倾向于怀疑驱动有问题。碰到这种情况时,我马上就能从sysfs验证驱动是否工作。
如果用第二种方法,由于地址映射到用户空间,要写一个测试程序才能证明驱动是正确的。很繁琐
作者: wawava    时间: 2012-08-04 10:27
fpga驱动已写好了,最近有个冒出个问题,就是我用mem映射gpio的地址后,对某一个管脚进行拉高。目前的现象时有时候恩能够拉高有时候拉不高。都是相同的代码,为什么有时候可以呢?我读此管脚的值每次都是一样的,此管脚是可读的,有可能是哪里的问题?

作者: liuiang    时间: 2012-08-04 10:56
我也不是特别明白,不过我倾向于第三种,当然我也承认某些情况下,前两种会非常简单。

第一种,弱问,是否需要root权限啊,如果需要root权限的话,会大打折扣,不过在调试阶段会非常有用。

第二种,比较好用,但是如果遇到交互的情况,就很不直接,比如设置一个寄存器,然后等待fpga一个返回(比如返回一串数据),
----------那么会出现,先向用户空间写数据设置寄存器,然后用系统调用获得,fpga如果短时间可以处理还好,如果fpga需要一点时间处理,
----------然后通过中断通知,那就只能通过系统调用了,会比较怪异。另外最不爽的是:不好实现多个进程同时工作,当然这取决于应用环境。

第三种,算是最标准的作法,缺点是,调试阶段会比较郁闷,应用稍微大变又得重新搞。

所以,个人倾向于,如果要做单一产品,第三种,通用fpga平台,第二种。
作者: linuxfellow    时间: 2012-08-05 20:08
本帖最后由 linuxfellow 于 2012-08-05 20:21 编辑

回复 25# wawava
你用的什么CPU? 有的CPU里管脚都有复合功能。要配置相应的继承器才能正确工作。

   
作者: linuxfellow    时间: 2012-08-05 20:17
回复 26# liuiang
没想到权限问题,应用程序想用这一驱动,应该会有相应的权限吧。

   
作者: wawava    时间: 2012-08-06 10:41
我用的lpc3250,我在内核中用io_p2v转换后的地址直接拉高管脚,你说的继承器究竟是什么东东啊?
作者: linuxfellow    时间: 2012-08-06 12:47
回复 29# wawava
你说的cpu没用过,powerpc 如果有个管脚要配置成gpio, 对应的pad register(PCR)要对其功能进行配置, 如该管脚是输入还是输出,pull-up/pull-down电阻, 等要进行配置;仔细看看你的手册,看如何将一个管脚配置为gpio功能。

   
作者: wawava    时间: 2012-08-07 11:24
lpc3250上用的是arm9.硬件方面应该没问题,我跑裸机程序该管脚每次操作都没问题。刚发现一个现象,我必须在内核中先使用io_p2v获得GPIO的地址,然后在内核中操作该管脚,如果不在内核中操作该管脚,应用程序中使用mem映射后,操作该管脚会直接死机,就是也没蹦出异常就直接卡住了。求解释
作者: linuxfellow    时间: 2012-08-10 11:54
回复 31# wawava
不好意思,这几天忙于其他事情,没上来这里:
当你调用mem后你可以打印查看所得到的线性地址,看mem是否成功. 如果成功,问题应该不大; 不成功就会死机

   
作者: highnjupt    时间: 2012-08-10 15:14
回复 1# linuxfellow


都用第一种方法,不用每次编译驱动,直接在应用上操作,修改也比较方便,哈哈
   
作者: wawava    时间: 2012-08-11 16:38
回复 32# linuxfellow
此问题已解决,我把操作gpio管脚的函数全部都放到内核中了。为什么不能在应用程序中操作gpio呢,我就是直接用mmap映射也会崩溃?
作者: linuxfellow    时间: 2012-08-12 12:06
回复 34# wawava
我在内核和应用都用过, 都能用。
如果可能,请把你的应用程序操作gpio代码贴出来,咱们可以一起看看。

   
作者: wawava    时间: 2012-08-13 11:40
/* GPIO Module Register Structure */
typedef struct
{
volatile unsigned int p3_inp_state; /* Input pin state register */
volatile unsigned int p3_outp_set; /* Output pin set register */
volatile unsigned int p3_outp_clr; /* Output pin clear register */
volatile unsigned int p3_outp_state; /* Output pin state register */
} GPIO_REGS_T;

#define GPIO_PHY_SIZE (0x100000)
#define GPIO_BASE (0x40028000)

GPIO_REGS_T* m_gpioRegs;

static S32 m_gpioAddr=0; //GPIO地址


函数操作如下:
m_gpioAddr = (S32)mmap(0, GPIO_PHY_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, m_memDev, GPIO_BASE);
if(m_gpioAddr < 0)
{
LOGINFO0("gpioAddr fault!\n");
return ERR_NORAML_ERROR;
}
m_gpioRegs= (GPIO_REGS_T*)m_gpioAddr;

  m_gpioRegs->p3_outp_set |= (1<<17);//拉高管脚就是这句
作者: linuxfellow    时间: 2012-08-13 20:38
回复 36# wawava
用下面的代码试试:
        int int fd;
        fd = open("/dev/mem",O_RDWR);
        if ( fd < 0 ) {
                printf("error in /dev/mem \n");
                exit(1);
        }

m_gpioAddr = (S32)mmap(0, getpagesize(), PROT_READ | PROT_WRITE, MAP_SHARED, fd, GPIO_BASE);
你mmap的memory size很大,为什么要这么多? 如果gpio寄存器对base address的偏移量在一页内,用getpagesize()map一页就行了。如果偏移量不在一页内,调整GPIO_BASE使偏移量在一页内
作者: zhiwenmuqing    时间: 2014-12-27 21:38
Lz您好,mmap和ioremap都可以操作FPGA是吧,mmap是在用户空间,ioremap是在内核空间,能不能在内核空间向法FPGA设备写数据,然后在用户空间通过mmap映射,对地址来读数据呢?
作者: linuxfellow    时间: 2015-02-04 20:27
回复 38# zhiwenmuqing
可以,只要读写在相同的物理地址,映射到内核,就通过内核虚拟地址写入,映射到用户空间,就通过进程虚拟地址操作

   




欢迎光临 Chinaunix (http://bbs.chinaunix.net/) Powered by Discuz! X3.2