XScale PXA270在Linux下的FPGA设备驱动
用户程序直接访问设备内存的能力。使用内存映射的好处是处理大文件时速度明显快于标准文件I/O,无论读/写,都少了一次用户空间与内核空间之间的复制。在用户空间对ACEX1K50 FPGA设备的访问是通过内存映射来实现的。
ACEX1K50可以看作是硬件连接在PXA270微处理器的nCS<2>上的一段物理地址来寻址。因为有虚拟内存管理单元,所以如果在Linux下,必须先把物理地址映射到虚拟地址空间,然后才能对该段地址进行读/写。
在内核驱动程序的初始化阶段,通过ioremap()将ACEX1K50的这段物理地址映射到内核虚拟空间;在驱动程序的mmap系统调用中,使用remap_page_range()将该块ROM映射到用户虚拟空间。这样内核空间和用户空间都能访问ACEX1K50的这段被映射后的虚拟地址。
由于ACEX1K50位于nCS<2>上,参照PXA270静态存储体系结构映射表,其物理起始地址为0x08000000。另外,其设备名称及主次设备号定义如下:
#define FPGA_PHY_START0x08000000
// nCS<2>: PAX270平台
#define FPGA_PHY_SIZESZ_4K
// nCS<2>: Slot FPGA物理基大小为4K
#define DEVICE_NAME"PXA270 FPGA"
#define FPGARAW_MINOR 1
#define FPGA_Devfs_path"fpga/0"
static int fpgaMajor = 0;
其中FPGA主设备号定义为零,使得操作系统可以随机为该设备分配主设备号。
ioremap()的作用是把一个物理内存地址点映射为一个内核指针,被映射数据的长度由size参数设定。该函数的实质是把一块物理区域二次映射到一个可以从驱动程序里访问的虚拟地址上去。以下是该函数的定义:
void *ioremap(unsigned long phys_addr, unsigned long size);
设备驱动通过fpga_init()函数初始化FPGA设备,最终通过init_module(fpga_init)在内核启动时初始化FPGA设备。
fpga_init()函数的流程如图3所示。
图3 fpga_init()流程
ioremap()调用的语句如下:
pxa270_fpga_base= (unsigned long) ioremap(FPGA_PHY_START, SZ_4K);
可以通过ioremap()调用的返回值pxa270_fpga_base来判断FPGA物理地址到内核虚拟空间是否映射成功。
if(!pxa270_fpga_base) {
printk("ioremap pxa270 fpga failedn");
return -EINVAL;
}
向设备文件系统注销FPGA设备通过调用cleanup_module()函数来实现。其代码如下:
void __exit fpga_exit(void) {
#ifdef CONFIG_DEVFS_FS
devfs_remove(FPGA_Devfs_path);
#endif
unregister_chrdev(fpgaMajor, DEVICE_NAME);
}
cleanup_module (fpga_exit);
在向内核设备文件系统注册该FPGA驱动后,还须实现设备驱动的file_operations结构。ACEX1K50的设备驱动定义了如下file_operations成员函数:
static struct file_operations pxa270_fops = {
owner:THIS_MODULE,
open:fpga_open,
mmap:fpga_mmap,
ioctl:fpga_ioctl,
release:fpga_release,
};
其中fpga_open和fpga_release系统调用的功能只简单地实现了FPGA设备使用计数器的递增与递减,fpga_ioctl系统调用也只是简单的打印一条没有ioctl控制的信息提示。这里不再分析实现的具体代码。下面具体分析fpga_mmap的实现过程:
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_page_range(vma, vma->vm_start, physical, vsize, vma->vm_page_prot);
return 0;
}
fpga_mmap(struct file *filp, struct vm_area_struct *vma)系统调用允许直接将FPGA设备内存线性地映射到用户进程的地址空间中。fpga_mmap系统调用是通过调用 remap_page_range()函数来实现一段线性物理地址的映射,调用remap_page_range()函数需要填写 vm_area_struct结构的几个关键字段。
int remap_page_range(struct vm_area_struct *vma, unsigned long from, unsigned long to, unsigned long size, pgprot_t prot)函数每个参数的意义说明如下:
vm_area_struct *//虚拟内存区域(VMA)指针
unsigned long from//需要映射的用户虚拟地址的起始位置
unsigned long to//虚拟地址所映射到的物理地址
unsigned long size//被重映射区域的大小,以字节为单位
4 ACEX1K50设备驱动在用户程序中的使用
当设备驱动实现后,就可以在用户空间使用该设备了。在用户空间
- Linux操作系统网络驱动程序编写(04-11)
- Linux系统对ISA总线DMA的实现(06-19)
- 基于MPEG-4的嵌入式多媒体监控系统中压缩/解压卡的设计与实现(10-15)
- 基于Linux平台的FPGA驱动开发(08-03)
- 基于嵌入式的故障诊断专家系统驱动程序设计(11-29)
- 嵌入式Linux下的LCD驱动程序设计与实现(04-18)