免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
最近访问板块 发新帖
楼主: linuxfellow
打印 上一主题 下一主题

[硬件及驱动] FPGA-based设备驱动 [复制链接]

论坛徽章:
0
11 [报告]
发表于 2012-07-10 21:21 |只看该作者
linuxfellow 发表于 2012-06-28 08:44
回复 7# bensenq
不好意思,你是对的:
第一种方法,用现成驱动“/dev/mem"的mmap()来映射物理空间、I ...

第一种方法不需要在kernel中编写FPGA的驱动吗?

论坛徽章:
1
拜羊年徽章
日期:2015-03-03 16:15:43
12 [报告]
发表于 2012-07-11 11:04 |只看该作者
回复 11# wawava
不需要,直接用系统提供的 /dev/mem驱动

   

论坛徽章:
0
13 [报告]
发表于 2012-07-11 11:50 |只看该作者
那在应用程序中mmap的参数:star和fd的值怎么填写?

论坛徽章:
1
拜羊年徽章
日期:2015-03-03 16:15:43
14 [报告]
发表于 2012-07-11 12:33 |只看该作者
回复 13# wawava
帮你找了一个例子:
http://stackoverflow.com/questio ... -user-spacein-linux

   

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

论坛徽章:
1
拜羊年徽章
日期:2015-03-03 16:15:43
16 [报告]
发表于 2012-07-11 13:54 |只看该作者
回复 15# wawava
上面那个例子是从把内禾地址映射到用户空间,从而通过用户空间访问kernel address.
request_irq()不能从用户空间调用;如果你的FPGA触发中断,第一种方法就不适用;你可能要写内核空间的驱动。用第二或第三种方法。

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


   

论坛徽章:
0
17 [报告]
发表于 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?




论坛徽章:
1
拜羊年徽章
日期:2015-03-03 16:15:43
18 [报告]
发表于 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请求中断时,这一处理程序就会被调用来处理相应中断。用户空间不需再作任何事情

   

论坛徽章:
0
19 [报告]
发表于 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了,如果方便的话留个联系方式吧

论坛徽章:
1
拜羊年徽章
日期:2015-03-03 16:15:43
20 [报告]
发表于 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里找
   
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP