首页 QEMU平台挂载外设说明

QEMU平台挂载外设说明

举报
开通vip

QEMU平台挂载外设说明QEMU平台挂载外设说明 常轶松 孙铭泽 王航胜 {yisong, sunmingze, tjuwhs}@tju.edu.cn 本说明将以浮点乘加(multiply-and-add)运算单元fmad为例,说明QEMU平台下如何添加外设或硬件加速器并设计相应Linux设备驱动程序的方法。 一(确定硬件外设的基本功能 这是系统整体设计的第一步,也是最重要的一步。在设计之前软硬件设计人员应进行充分的沟通,确定硬件的主要功能,以及软件驱动程序、应用程序之间调用的接口和基本控制流程,完成硬件模块寄存器的定义及设置,中...

QEMU平台挂载外设说明
QEMU平台挂载外设说明 常轶松 孙铭泽 王航胜 {yisong, sunmingze, tjuwhs}@tju.edu.cn 本说明将以浮点乘加(multiply-and-add)运算单元fmad为例,说明QEMU平台下如何添加外设或硬件加速器并设计相应Linux设备驱动程序的方法。 一(确定硬件外设的基本功能 这是系统整体设计的第一步,也是最重要的一步。在设计之前软硬件设计人员应进行充分的沟通,确定硬件的主要功能,以及软件驱动程序、应用程序之间调用的接口和基本控制流程,完成硬件模块寄存器的定义及设置,中断的使用方法等。 以fmad为例,该模块具有三个输入寄存器,分别存放待处理的三个操作数a, b, c。模块启动后将计算a×b + c,并将结果存放在结果寄存器中。同时将中断信号设置为有效,并转由相应的中断服务程序进行处理。 fmad模块寄存器配置说明: 名称 地址 功能 reg_a 0x0000 操作数a reg_b 0x0004 操作数b reg_c 0x0008 操作数c reg_res 0x000c 结果寄存器 reg_ctl 0x0010 控制寄存器 reg_irq 0x0014 中断状态寄存器 寄存器的长度均为32-bit宽度,因此其地址空间均按4对齐方式进行排列。 控制寄存器中有两位有效: , bit0: start/end位,应用程序可将该位置1,以启动fmad硬件进行计算。 , bit31: irq_en中断使能位,内核驱动程序将该位置1,以使能硬件的中断 功能。 中断状态寄存器的bit0 ( irq_stat) 位标志当前硬件是否发出中断请求,以等待内核的中断服务程序响应该中断并进行处理。当中断响应完成后,内核驱动程序向该位写入1,以清空中断标志位。 二. 硬件模块设计 声明模块:该平台模块分为两类,主设备模块和从设备模块,这里以fmad为例介绍从设备实现过程,fmad模块文件包含fmad.h和fmad.c文件。 fmad.h中定义的有,该设备的私有变量,线程,读入和输出功能函数(write,read),中断接口,寄存器等。这些根据实际需要定义。 fmad.c文件中,应首先初始化私有变量,定义线程函数,完成读入输出函数功能,定义线程功能实现(主要为实现该模块具体功能和设定中断),最后把结果返回。 数据格式处理:QEMU为总线为64位,寄存器大小为32位,所以总线一次传输两个数据,所以总线上地址后4位(二进制位)为0或者8(相应偏移地址一样的格式),处理过程如下: lofs >>= 2; if(lbe & 0xF0) { lofs += 1; lbe >>= 4; val1++; } lbe=be,0F表示64位总线数据的前32位,F0表示64位总线数据的后32位。如果取数为后32位,需要进行数据格式处理。 首先把lofs=ofs右移两位,相应寄存器偏移变为 名称 地址 reg_a 0x00 reg_b 0x01 reg_c 0x02 reg_res 0x03 reg_ctl 0x04 reg_irq 0x05 当要取reg_b,reg_res,reg_irq这些后32位数据时,需要在右移后的偏移量+1,表明取64位数据的后32位,lbe右移4位表明取当前地址的前32位数据,val++(val为指针),表明读取总线数据的后32位数据。 开辟地址空间: struct fmad_io_resource { uint8_t *mem; uint32_t mem_size; }; 定义一个如上的结构体,mem为指向一个存储结构的指针,mem_size为该存储器的大小。每当需要使用存储器的时候需要new这样的一片地址空间。 执行信号设计:该模块设计实现两个进程(void fmad_thread (),void irq_thread()),为两个SC_THREAD型进程,这种进程在函数中放置wait()函数,使进程等待一段时间或者某个事件的触发。 例如,fmad设计两个事件m_ev_irq中断处理事件,m_ev_start_stop功能执行/停止事件,当在线程中放入语句wait(m_ev_irq);需等待其他执行程序中的m_ev_irq.notify();该线程方能继续执行。 模块功能实现:实现一个完整功能的模块需要注意以下几点 1,定义输入输出数据,以及与响应寄存器的映射。(例如把偏移地址为0x1的数 据放入到reg_a中) 2,设计功能执行开始/结束标志,响应标志触发线程执行 3设计中断,若功能模块实现为存在中断模式,则在该模块中,必须有完整的启动中断,清中断以及关闭中断过程。 三. Rabbits平台及Linux内核 在完成硬件模块的定义之后,需要改动Rabbits平台,并重新编译Linux内核,以保证外设的正常使用。 1. 在QEMU平台添加硬件模块 (1). 修改main.cpp文件,将已设计好的fmad模块加入SoC仿真平台架构中。 添加设备: fmad_device *fmad = new fmad_device("fmad"); 将fmad设置为6号从设备,设置方法如下: slaves[nslaves++] = fmad; // 6 为fmad添加中断信号线,设置相应的中断数量: int no_irqs = ntimers + 4; /* timers + TTY + FB + DBF + fmad */ fmad的中断编号设置为4: fmad->irq(wires_irq_qemu[ntimers + 3]); 最后设置处理fmad中断的处理器编号,在本例中使用cpu0对中断进行处理,因此,修改int_cpu_mask数组: int int_cpu_mask [] = {1, 1, 1, 1, 1, 0, 0}; 其中5个‘1’表示的顺序分别为timers, TTY, FB, DBF, fmad。 (2). 为fmad分配地址空间: 修改map中的各个node文件,添加6号从设备分配的相应地址空间: 0xA9200000 0xA9300000 0x00000000 6 分别表示起始地址,结束地址,偏移量以及从设备号 之前map中的6号设备跟7号设备顺延为7号设备,8号设备 编译main.cpp文件,编译方法如下: 进入rabbits/platforms/roger 在makefile中变量SUB_DIRS 添加$(COMPONENTS)/fmad \ 在终端这个目录下 输入 make 2. 修改Linux内核,以支持相应的中断功能 初始的Linux内核仅能针对timer0,tty,FB和DBF所分配的中断号进行处理。这些外设所对应的中断号分别为32,33,34,35。当为新添加的外设分配其余中断号时,Linux操作系统会将该中断号默认为无效,使外设驱动程序无法有效的注册中断服务例程。 修改roger/soft/linux/linux/arch/arm/arch-comcas/comcas.c文件中定义的两个静态数组,fmad中断的中断号位36,其mask使用16: static int implemented_irqs [] = {IRQ_LOCAL_TIMER, IRQ_TIMER0, IRQ_UART0, 34, 35, 36}; static unsigned long mask_implemented_irqs[] = {0, 1, 2, 4, 8, 16}; 之后重新编译Linux内核。 修改roger/soft/build_soft.sh文件第62行: [ ! -e ${STAMPS_DIR}/linux_installed ] 四. 编写fmad驱动程序 4.1:概述: 在编写号硬件模块后,为了应用程序使用该硬件,需要为该硬件编写驱动程序。 驱动的作用:1:为应用程序提供系统调用。2:控制下层的硬件设备。 驱动运行的过程:1:加载和初始化。2:操作设备时,调用驱动提供的各种函数。3:卸载。 初始化过程:1:分配并注册主设备号和从设备号。2:初始化代表设备的结构体。3:初始化互斥体。4:初始化在内核中代表设备的cdev结构体,将设备和file_operations结构体联系起来。 提供调用函数:在file_operations结构体中定义了所有能操作设备的函数,在驱动程序中,我们必须实现这些函数(并不一定所有函数都需要实现,但是必须实现一些函数)。 卸载:卸载过程和加载过程相反。释放设备,释放变量。 4.2:实现 驱动程序一共包含5个头文件和2个代码文件。 2.1:头文件介绍: 2.1.1:fmad_types.h:主要是定义了三个数据结构fmad_int、fmad_device、 fmad_driver fmad_int是关于中断的数据结构。fmad_driver是关于驱动的数据 结构。fmad_device关于设备的数据结构。 2.1.2:fmad_regs.h:主要是定义设备的地址和设备上寄存器、存储器的 偏移地址,还有相关位移操作。 2.1.3:fmad_int.h:主要是关于中断的处理,声明了相关函数。 2.1.4:fmad_ioctl.h:主要定义一些I/O控制的相关命令。相关知识参考: 《Linux设备驱动程序》的第六章。或者参考一下网站: 。 2.1.5:debug.h:定义调试时使用的相关函数 2.2:功能文件 2.2.1:fmad_int.c:主要是用于中断控制,对于设备的中断控制是一个比 较复杂的 涉及的方面很多。而fmad设备驱动程序中的中断部分只是简单地实 现对中断进行控制。 2.2.1.1:irqreturn_t fmad_int_service(int irq, void *d)该函数是中断服务程序。当中断发生时,将执行该程序。该程序作用是将某些控制位清空,唤醒等待队列。 2.2.1.2:int fmad_int_enable:作用是将中断控制打开,注册中断服务。 2.2.1.3:int fmad_int_disable:关闭中断控制。 2.2.1.4:int fmad_int_init():初始化自旋锁。 2.2.1.5:int fmad_int_cleanup():取消中断控制。 2.2.1.6:int fmad_int_register:注册中断控制。 要使fmad中断能够被QEMU虚拟ARM核捕获,除了要设置fmad自身相应的中断使能信号外,还要设置qemu_wrapper中的中断使能信号,以使传递到仿真内核的外部中断信号允许被响应。因此,应该修改驱动程序中的有关宏定义,保证驱动程序在进行中断初始化的过程中,能够将有效的中断使能字传递到qemu_wrapper。修改fmad_regs.h中定义的QEMU_INT_RAMDAC_MASK。如前文所述,fmad的中断在SoC系统中被设置为4号中断。因此,该值的第4位应设置为1,表示fmad中断使能有效,因此,该值应设置为0x10: #define QEMU_INT_RAMDAC_MASK 0x10 2.2.2:fmad_drv.c:这个文件是整个程序的精华所在。 内核部分:一个驱动程序有时需要装载在内核里执行,所以必须加载内核模块。 2.2.2.1:int __init fmad_drv_init()初始化驱动程序模块。 module_init(fmad_drv_init)用于告诉内核初始化程序。 2.2.2.2:void __exit fmad_drv_cleanup()清除该驱动模块。 module_exit(fmad_drv_cleanup)告诉内核清除函数。 2.2.2.3:int __devinit fmad_dev_init()初始化设备 2.2.2.4:int __devexit fmad_dev_exit()清除设备 面向用户程序部分:在驱动程序有一部分是面向用户空间,是用户空间调用驱动中的相关函数实现对设备的访问。 对file_operations结构的赋值 static struct file_operations fmad_chr_fops= { .owner =THIS_MODULE, .read =fmad_chr_read, .write =fmad_chr_write, .ioctl =fmad_chr_ioctl, .open =fmad_chr_open, .release =fmad_chr_release, .mmap =fmad_chr_mmap, }; 在Linux中,访问设备就是访问文件,所以对于文件一些操作,例 close,read等等函数需要实现,而在file_operations中就 规定 关于下班后关闭电源的规定党章中关于入党时间的规定公务员考核规定下载规定办法文件下载宁波关于闷顶的规定 如open, 了这些实现这些函数的函数。例如fmad_chr_read实现了对于文件的read 函数。或者当用户程序调用read函数时,将调用fmad_chr_read函数。 在以上的函数中open和release必须实现,其他函数如果需要可以考 虑部分实现。 2.2.2.5:fmad_chr_open():检查设备特定错误(设备未就绪或者类似 问题),分配并填写置于file->private_data。 2.2.2.6:fmad_chr_release():释放有open分配的,保存在 file->private_data中的所有内容。在最后一次关闭操作时关闭设备。 2.2.2.7:fmad_chr_ioctl():主要用于寄存器控制。通过不同的命令, 控制设备上的寄存器。 fmad_chr_read():主要用于读取存储器中的数据。 2.2.2.8: 2.2.2.9:fmad_chr_write():主要是用于将数据写入存储器。 在ioctl,read,write这三个函数由于需要和用户空间交换数据,记 住参数中的buffer或者data是用户空间的数据,在和内核数据交换时需 要使用: copy_from_user()和copy_to_user()这两个函数进行交换。 2.2.2.10:fmad_chr_mmap():主要是用于内存映射。这个函数可以将 设备的存储空间直接映射到内存上。访问设备的存储空间就和访问内存一样。 五. 应用程序 应用程序的编写和平时编写的应用程序差不多。在linux系统里,在应用程序层次操作设备就像操作一个文件。主要是一下几个函数: open():用于打开设备。例:int fd=open(/dev/fmad,O_RDWR)。 ioctl():用于控制设备的寄存器。ioctl(fd,FMAD_IOCSETSIZE,(void)&size). 其中FMAD_IOCSETSIZE在fmad_ioctl.h定义。 mmap():将设备映射到内存空间里。 read():读取设备上的数据。 write():往设备上写数据。
本文档为【QEMU平台挂载外设说明】,请使用软件OFFICE或WPS软件打开。作品中的文字与图均可以修改和编辑, 图片更改请在作品中右键图片并更换,文字修改请直接点击文字进行修改,也可以新增和删除文档中的内容。
该文档来自用户分享,如有侵权行为请发邮件ishare@vip.sina.com联系网站客服,我们会及时删除。
[版权声明] 本站所有资料为用户分享产生,若发现您的权利被侵害,请联系客服邮件isharekefu@iask.cn,我们尽快处理。
本作品所展示的图片、画像、字体、音乐的版权可能需版权方额外授权,请谨慎使用。
网站提供的党政主题相关内容(国旗、国徽、党徽..)目的在于配合国家政策宣传,仅限个人学习分享使用,禁止用于任何广告和商用目的。
下载需要: 免费 已有0 人下载
最新资料
资料动态
专题动态
is_963767
暂无简介~
格式:doc
大小:27KB
软件:Word
页数:10
分类:生活休闲
上传时间:2018-04-28
浏览量:68