基于FPGA的实时多通道DMA系统设计
2015-08-01苏永海
贺 孟,苏永海
(中国电子科技集团公司 第三十研究所,四川 成都 610041)
0 引 言
随着SoC 芯片性能的提高,处理器系统模块间的数据传输能力是提升系统性能的关键.在数据传输系统中通常采用直接存储器存取,即DMA 传送方式,可以最大限度提高数据传输的效率[1].一般的DMA 控制器结构专用性强而扩展性不足,面对系统规模的不断扩大和系统业务的多样化,需要一种多通道DMA 控制器结构以使系统设计能够适应各种带宽需求的业务.随着可编程逻辑技术的发展,基于IP 核复用技术的IP 核设计成了提高可编程逻辑设计效率的重要组成部分[2].本研究针对工作中的实际应用,结合IP 核复用技术,重点介绍了一种基于FPGA 的自动仲裁多通道无缓存结构DMA 控制器IP 核的设计方法,并给出了Linux 下该IP 核驱动的设计,从而完整地实现了一个多通道无延迟DMA 系统.
1 DMA 控制器总体结构设计
1.1 总体结构
DMA 控制器是为使用32 位PCI 总线的SoC 项目设计的,在设计中考虑到对不同系统的移植,并采用了IP 复用的设计思想.该控制器向处理器单元申请总线控制权,用DMA 方式完成存储器和外设之间的数据传输.DMA 控制器最多8 个独立通道,每个通道独立编程,通道之间支持用户定义优先级和round robin 仲裁,通道与总线间无缓存无延迟传输,每个通道支持收发数据包完整性检查和包计数.
DMA 控制器的总体结构如图1 所示.
图1 DMA 控制器总体框图
各功能模块说明如下:
1)DMA 通道模块.完成通道内的DMA 传输请求和判断以及与用户逻辑和接口.
2)DMA 仲裁模块.对所有DMA 通道模块发送的DMA 请求进行仲裁,根据仲裁结果选择哪个通道占用总线.
1.2 工作原理
DMA 控制器从总线的占用角度分2 种工作状态:空闲状态和工作状态.当无通道发出数据传输请求时,就处于空闲状态,此时由处理器占用总线对DMA 控制器内部寄存器进行读写操作.当通道发出数据传输请求后,就处于工作状态,此时DMA 控制器占用总线直接对内存进行数据操作.
DMA 控制器的工作机制采用消息循环队列机制,即DMA 控制器每个通道按操作内存的方向设置读写2 个消息队列,队列元素为描述数据缓存区信息的描述符(Buffer Descriptor,BD),队列元素存储在内存中,BD 信息符由数据地址、数据长度、标志位组成.通常,每个队列定义以下寄存器由处理器在空闲状态时操作:队列长度寄存器(BDL),读写缓存区信息基地址寄存器(BDA),缓存区信息队列头部位置寄存器(BDH),缓存区信息队列尾部位置寄存器(BDT).
1)DMA 控制器写内存时,首先处理器初始化配置BDA 寄存器和BDL 寄存器,此时BDH 和BDT 初始值都为0,表示内存无空余缓存区.当处理器增加一个空余缓存区时将该缓存区在内存中的地址转换为总线地址写入BD 中,然后更新BDT 的值,此时DMA 控制器检测到BDH 不等于BDT,即知道已有空闲缓存区,这时DMA 控制器检测用户逻辑有无DMA 传输请求,有则向DMA 仲裁器发出总线占用请求,DMA 仲裁器响应后,该DMA 控制器首先按BDA 加偏移量定义的地址读取内存中的BD 信息符,然后将用户数据写入该BD 信息符定义的数据地址对应的数据块中,最后将用户数据长度写到BD的数据长度部分,同时更新标志位中的完成以及状态标志,然后将BDH 值加1.整个流程如图2(A)所示.
2)DMA 控制器读内存时,首先处理器初始化配置BDA 寄存器和BDL 寄存器,此时BDH 和BDT 初始值都为0,表示内存无待读缓存区.当处理器增加一个待读缓存区时,将该缓存区在内存中的地址转换为总线地址和数据长度写入BD 中,然后更新BDT 的值,此时DMA 控制器检测到BDH 不等于BDT,即知道已有待读数据在缓存区,这时DMA 控制器检测用户逻辑是否允许DMA 传输请求,有则向DMA 仲裁器发出总线占用请求,DMA 仲裁器响应后,该DMA 控制器首先按BDA 加偏移量定义的地址读取内存中的BD 信息符,然后到按照BD 信息符中的数据长度将该BD 信息符定义的数据区地址将待读数据传输给用户逻辑中,最后更新标志位中的传输完成标志同时将BDH 值加1.整个流程如图2(B)所示.
1.3 功能模块详述
1.3.1 DMA 通道模块.
DMA 通道模块设计如图3,分为配置管理单元、用户接口单元、总线处理单元、监控统计单元.
1)配置寄存器单元.在空闲状态接收处理器初始化指令,读写BDL/BDA/BDH/BDT 寄存器,并将寄存器信息传递给总线处理单元.
2)总线处理单元.接收配置寄存器单元BDH 和BDT 信息,以及用户逻辑处理单元的空满标志信息,决定是否向仲裁模块发起DMA 读写请求并决定DMA 操作的顺序,核心如图4 所示状态机.
图2 DMA 控制器工作流程图
图3 DMA 控制器通道模块原理框图
3)状态说明.Idle 为初始态,Request 为请求态,Rbd-r0 为写内存读BD 信息符态,Rbd-w1 为写内存写BD 信息符态,Rbd-wd 为写内存数据态,Tbd-r0 为读内存读BD 信息符态;Tbd-w1 为读内存写BD 信息符态;Tbd-rd 为读内存数据态.
4)用户逻辑处理单元.设计供用户使用的标准接口,该单元不带任何缓存结构,在读写内存数据态时直接将总线接口时序匹配为带数据起始标志的异步FIFO 接口和带读写使能,读写地址的异步双口RAM 接口,数据起始标志同时作为数据帧完整性校验的指示.用户逻辑接口时序图如图5.
图4 总线处理状态机
图5 用户逻辑接口时序图
5)监控统计单元.监测总线端和用户端的数据收发,对两侧的数据包数进行统计.
1.3.2 DMA 仲裁模块.
仲裁模块对所有通道的占用总线请求进行排队仲裁,根据策略决定通道的响应顺序以及和总线接口的匹配,在这里指PCI32 接口的匹配.图6 为round robin 的设计原理图.
该设计的特点为通道切换无延迟,在传输一个通道数据时已经解算出下一个将要占用总线的通道,不浪费总线带宽,并与通道模块的无缓存结构结合,该IP 核在总线到用户数据传输上无延迟,保证了实时性.
2 DMA控制器的IP化
图6 仲裁单元round robin 设计原理图
设计IP 化的关键点是在硬件上对用户和总线接口提供标准统一的接口,在设计流程上对设计进行参数化和脚本化,这样就只需使用逻辑综合脚本改变参数就可综合得出适应不同的使用场景的网表文件.
2.1 接口的标准化设计
接口的标准化在用户端提供带数据帧起始标志和数据有效指示的FIFO 接口,也提供带地址信息的双口RAM 接口,这样用户既可只用简单的FIFO接口缓存数据,也可按需求直接按地址直接处理数据.
在总线端直接根据脚本指定使用标准的总线类型,本研究中使用的是PCI32 位总线.
2.2 参数化设计
参数化设计是设计IP 化的必要点,将设计中的功能点和性能点提出并参数化,用户只需按照自己的需求使用脚本改变这些参数,即可综合出适应不同场景的IP 核.下面是一部分描述参数的VHDL 代码及其说明,描述参数的代码全部在一个库文件中编写,以利于逻辑综合脚本调用修改.
constant c-family:string:=spartan6;
指定使用FPGA 型号为Spartan-6
constant bus-type:string:=pci;
前些日子,老伴突然晕倒,住院不到一个月就去世了。听医生说,老伴早就有发病的征兆,只是谁都没在意,耽误了治疗。三个女儿为她们疏忽老爸的生活愧疚不已,也对我平时对老伴的不管不问很是生气。
指定总线类型为PCI 总线.
constant bus-datawid-bits:integer:=32;
指定总线宽度为32 bit.
constant dmacfg-addrwid-bits:integer:=6;
指定配置寄存器地址宽度为6 bit.
constant transfer-lengthwid-bits:integer:=9;
指定配置寄存器地址宽度为6 bit.
constant dmastate-datawid-bits:integer:=16;
constant dma-channel-number:integer:=4;
指定通道数为4 个.
图7 为dma 通 道数dma-channel-number =4时匹配PCI32 的synplify 综合视图.
图7 DMA 控制器IP 核综合视图
3 Linux下DMA驱动的实现
3.1 Linux设备驱动程序概述
在Linux 系统中,针对不同的外部设备,其驱动程序的框架结构是一致的.通常,Linux 系统分为内核空间与用户空间,设备驱动程序是工作在内核空间的,它通过特定的方式与用户空间交换数据.Linux 系统的设备可分为字符设备、块设备和网络设备3 种类型.所有的设备驱动程序都支持文件操作接口.因此,每个设备都可以当作是文件系统中的一个文件来进行访问[3].Linux 系统分为3 层:应用层、内核层和物理层.应用层主要运行与应用相关的应用程序,内核层主要是Linux 操作系统运行的空间,包括驱动程序也运行在该层,物理层就是各种物理外设,应用程序要访问外部的设备必须通过驱动程序来实现.
Linux 操作系统中,所有的外围设备都是通过文件节点来进行访问的.Linux 的虚拟文件系统为各种不同文件系统提高了统一的访问接口,包括PCI 外围设备.通过这些接口,应用程序可以直接使用open、close、read、write 和ioctl 等系统调用来对各种设备进行访问和控制,而为一个外围设备提供这些系统调用的响应函数正是该设备驱动程序的责任.
Linux 将所有外部设备看成是一类特殊文件,称之为“设备文件”,设备驱动程序则可以看成是Linux 内核与外部设备之间的接口.在Linux 操作系统下,有3 类主要的设备文件类型:字符设备、块设备和网络设备.对于设备文件和普通文件的访问都是通过其I/O 子系统向内核中的其他部分提供统一的标准设备接口,包含在include/linux/fs.h 中的数据结构file-oPerations 来完成的[4].
3.2 与硬件设备的接口
Linux 为设备驱动程序访问I/O 端口、硬件中断、DMA 提供了简便方法.Linux 提供的I/O 端口访问方法主要有inb()、inw()、outb()、outw()等.需注意的是设备驱动程序在使用端口前,应该先用check-region()检查端口的占用情况,如果指定的端口可用,再用request-region()向系统登记.
3.3 内存分配
设备驱动程序作为内核的一部分,不能使用虚拟内存,利用内核提供的kmalloc()和kfree()来申请和释放内核存储空间.kmalloc 带2 个参数.第1个参数是要申请内存的数量,要求这个数量是2 的整数幂,如128、256;第2 个参数是优先权,最常用的优先权是GFP-KERNEL,它的意思是该内存分配是由运行在内核模式的进程调用的[5].
3.4 DMA 驱动程序概述
DMA 控制器可控制多个DMA 传输通道,并且可以实现总线和存储器总线之间的相互传输,其实现步骤如下:
1)在系统内存中分配大块的存储区用于DMA传输必须确保属于低端内存得到用于DMA 传输的总线地址,且不能换入换出,通常会用到GFP-KERNEL GFP-DMA 等标志.
2)在内核中调用request_dma 函数请求并注册一个DMA 通道并为该通道注册一个DMA 传输完成中断并实现其中断服务程序与驱动程序的其他中断注册方法相同,不再描述.
3)通过内核函数获得总线地址对于PCI 设备其中一个存储器资源的实现如下其余段的存储器资源处理相同.
4)当数据准备就绪,通过中断或者命令通知CPU 初始化DMA 通道的各个参数,如DMA 的源总线地址寄存器、目的总线地址寄存器以及传输数据长度寄存器等,并启动DMA 传输后CPU 就不再参与数据的传输.在DMA 传输完成后,在其DMA 传输完成中断服务程序中,CPU 只需要清理中断现场就结束数据传输.
因此,在数据传输的过程中,只需要占用极少的CPU 时间初始化DMA 寄存器及DMA 中断管理,有利于优化整个系统的性能.
4 结 语
本研究讨论了一种实时多通道DMA 控制器IP核的实现,结合该设计,探讨了基于IP 复用技术的IP 核实现方法,并给出了Linux 下驱动的实现方法.FPGA 使用Xilinx Spartan-6,最终适配PCI32 总线4通道IP 核的逻辑资源消耗只有648 个slice,整个设计相当精简可靠.最后,本研究在PCI 机箱使用该FPGA 硬件板卡进行了实验测试,对全部四通道进行并行收发数据测试,测得数据收发达到55 MB/s(兆字节/秒),且对优先级设定反应灵敏.
[1]史昕蕾,杨军,陆生礼.嵌入式SoC 中的DMA 控制器的设计与优化[J].电子工程师,2004,30(1):5-7.
[2]朱运航,李雪东.基于IP 核复用的SoC 设计技术探讨[J].微计算机信息,2006,22(3-2):47-48.
[3]Corbet J,Rubini A,Kroah-Hartman G.LINUX 设备驱动程序[M].第2 版.魏永明,耿岳,钟书毅译.北京:中国电力出版社,2002.
[4]Matthew N,Stones R.Linux 程序设计[M].第2 版.杨晓云,译.北京:机械工业出版社,2002.
[5]李远征,任传伦,杨义先.PCI 设备的DMA 驱动程序设计[J].计算机工程与应用,2003,39(14):135-138.