《电子技术应用》
您所在的位置:首页 > 嵌入式技术 > 设计应用 > PCI/PCI-E高速实时DMA传输驱动设计
PCI/PCI-E高速实时DMA传输驱动设计
来源:电子技术应用2012年第11期
何 柳1, 陈 勇1, 吴 斌2, 杨 坤2
1. 重庆邮电大学, 重庆400065; 2. 中国科学院微电子研究所, 北京100029
摘要: 根据WDM驱动模型设计了驱动程序,介绍了WDM驱动的设计方法,对驱动开发中的常见问题进行了详细阐述,着重讲解了高速实时DMA传输系统中驱动和上层控制程序的设计方案。以VC++6.0和WDK作为开发平台,完成了Windows NT系统下高速实时数据传输驱动的开发。
关键词: PCIPCI-E DMA WDK 实时
中图分类号: TP311
文献标识码: A
文章编号: 0258-7998(2012)11-0143-03
Design a high speed and real time PCI/PCI-E DMA transmission driver
He Liu1,Chen Yong1,Wu Bin2,Yang Kun2
1. Chongqing University of posts and telecommunications, Chongqing 400065, China; 2. Institute of Microelectronics of Chinese Academy of Science, Beijing 100029, China
Abstract: Designed a driver program according to the principle of WDM driver model. Firstly, introduced the way of designing WDM driver and expatiate the usual problems in driver design, then focused on the driver and its upper control program design of a high speed and real time DMA transmission system. Finally realized the development of high speed and real time transmission driver using VC++6.0 and WDK tools.
Key words : PCI/PCI-E; DMA; WDK; real time

    随着在线高清电影以及实时视频会议等应用的快速发展,数据传输呈现速率高、实时性强,数据量大的趋势,由此产生了高速实时数据传输的问题。在众多高速实时数据传输的设计方案中,PCI/PCI-E总线不可比拟的传输速率优势成为设计高速传输设备时的首选总线[1-2]。

    为了能够正常使用PCI/PCI-E总线进行高速数据传输,必须开发相应平台下的设备驱动程序。本文以自主研发的BCS5731芯片为例,介绍了在Windows NT环境下基于WDM模型的PCI/PCI-E总线设备驱动开发方案,着重分析了高速实时传输系统中驱动部分的设计与实现。
1 WDM驱动程序设计
1.1    WDM驱动模型介绍[3]

    WDM模型是微软针对Windows 2000及后续操作系统制定的驱动开发模型,具有即插即用和电源管理等方便用户使用的特性。目前微软正力推新一代的WDF驱动开发模型,但从本质上来说WDF是对WDM进行封装后的模型;而且WDM模型的驱动开发实例众多,极大地方便了驱动的开发,所以本文采用了WDM驱动开发模型。
    WDM驱动基于分层的模式实现。完成一个设备的操作至少需要两个驱动设备共同完成[4]。其中与系统连接最紧密的是底层总线驱动,而总线驱动也是最为复杂的部分。目前总线驱动通常由操作系统提供,驱动开发者只需要开发设备驱动以及可能需要的过滤驱动。图1所示为WDM驱动模型层次结构图。

1.2 驱动实例设计
    对于WDM驱动而言,主要的函数是DriverEntry例程、AddDevice例程、PnP例程以及各个IRP的派遣例程。对应于应用程序的main入口函数,Windows驱动程序相应的入口函数为DriverEntry[5]。
    DriverEntry例程由内核中的I/O管理器负责调用,是驱动第一个执行的例程。在本设计中,根据需要在DriverEntry例子中注册了以下例程:
    pDriverObject->DriverExtension->AddDevice = AddDeviceRoutine
    pDriverObject->MajorFunction[IRP_MJ_PNP] = PnpRoutine
    pDriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = ControlRoutine
    pDriverObject->MajorFunction[IRP_MJ_WRITE] = WriteRoutine
    pDriverObject->MajorFunction[IRP_MJ_READ] = ReadRoutine
    pDriverObject->MajorFunction[IRP_MJ_CLOSE] = faultRoutine
    pDriverObject->MajorFunction[IRP_MJ_CREATE] = DefaultRoutine
    下面对上述例程进行分析。
    (1) AddDeviceRoutine函数
    AddDerice Routine函数只出现在WDM驱动程序中,在NT式驱动中没有此回调函数。此函数用于创建设备对象,并由PnP管理器调用。具体操作包括:创建设备对象,创建并注册设备接口(为兼容也可创建符号连接),将创建的设备挂载到设备堆栈中,最后设置相关标志。值得注意的是,标志位中的读写方式在创建后不能在程序其他方面进行修改,且不同的读写标志位在编写读写部分代码时具有着不同的实现方式。
    (2) PnPRoutine函数
    PnPRoutine函数执行即插即用功能,一个功能完整的PnP函数需要处理众多的子IRP, 如IRP_MN_START_
DEVICE、 IRP_MN_STOP_DEVICE、IRP_MN_REMOVE_DE-
VICE等20多个子类IRP。在实际的接口驱动开发中,大部分PnPRoutine的子类IRP不需进行逐一编写,驱动开发者只需处理必要的子IRP,其余的子类IRP可以统一传递给总线驱动处理即可。通常IRP_MN_START_DEVICE和IRP_MN_REMOVE_DEVICE和两个子类IRP是WDM驱动必需单独处理的IRP。
    (3) ControlRoutine函数
    ControlRoutine函数常用于应用程序与驱动程序之间的通信。程序设计者首先定义一种I/O控制码,然后用函数DeviceIoControl将控制码和请求一起传递给驱动程序。驱动程序则在ControlRoutine中实现这些控制码。
    控制码的实现往往采用Switch(){case:}的形式。在本设计中,采用ControlRoutine进行DMA分配和寄存器配置。因此需要定义数种不同的控制码。
    (4) ReadRoutine和WriteRoutine函数
    两个函数分别用于target读写操作,当应用层调用ReadFile和WriteFile时,I/O管理器生成相应的IRP并发送到对应的函数中。驱动程序创建的设备通常有三种读写方式:缓冲区方式、直接方式和其他方式。ReadRoutine和WriteRoutine中使用的读写方式是由AddDeviceRoutine中设置的标志位决定。因此在驱动创建设备对象时,需要确定采用哪种读写方式。在实际的开发过程中,为了保证数据的安全尽量不采用第三种实现读写方式(其他方式)。
    (5) DefaultRoutine函数
    设置的默认处理例程,程序中简单地略过当前IRP,然后调用底层总线驱动。总线驱动处理传递来的IRP_MJ_CLOSE和IRP_MJ_CREATE子类IRP。
1.3 高速连续DMA传输设计[6]
    本设计需要解决的一个难题是高速实时数据的传输。整个传输系统如图2所示。

    系统传输流程为:基带芯片通过有线或无线方式接收到大量高速实时数据,传递到PCI Local端。PCI local端将数据传递到PCI Core与主机连接端,将数据通过PCI接口写入主机分配的DMA内存中;或者设备从DMA内存中读取主机数据。在传输过程中采用设备主控DMA方式。
    由于硬件基带的传输速率高达300 Mb/s,且主机只能分配规模有限的DMA内存,所以在主机数据接收时,若主机PCI接口端无法设计一种高效的数据读取方式以及主机与硬件实时的信息交互渠道,将会造成大量数据的丢失和数据读取的错误。
    本文设计了一种高效的同步机制,该机制采用DMA传输数据,从而保证了数据传输的高效性。此外在驱动层和PCI接口层进行同步验证,保证了数据的一致性,如图3所示。设计思路如下:

    (1) 驱动采用公共内存编写方式。根据应用层输入信息分配相应大小的DMA内存,将该内存抽象为1~N个长度为M字节的DMA寄存器,并将分配的DMA首地址传输给硬件。设内存首地址用指针DMAmemory表示。
    (2) 驱动设备扩展中申明一个整型变量,用于保存上次硬件操作完毕时的DMA寄存器编号,命名为RegNumber,在驱动初始化阶段初始化为0值。
    (3) PCI接口中分配一个长度为N bit的寄存器称为寄存器R,寄存器R的位数与DMA寄存器个数一一对应。将寄存器R初始化为全0。
    (4) 硬件实时查询寄存器R,若不为全1,则FPGA向主机写入数据; 若为全1,则暂停数据写入。
    (5) 当数据从基带传输到主机端的PCI接口时,硬件首先填充DMA内存的第一个寄存器,填充完毕后将寄存器R的首位设置为1。后续数据依次填充DMA寄存器,并置位寄存器R对应的位为1。硬件填充完一个寄存器后,向主机发送一个电平中断。
    (6) 主机端驱动检测到硬件中断,中断例程中简单清除电平中断后,启动DPC例程。
    (7) DPC例程中首先判断寄存器R是否为全0,若为全0,则表示数据未写入DMA内存中。DPC例程直接返回,等待下个中断。
    (8) 若寄存器R不为全0,则表示已有数据写入。检查RegNumber值,并从RegNumber+1编号的DMA寄存器开始读数。读取完一个寄存器后,向该DMA寄存器对应的寄存器R中的bit位发出写1命令,将硬件接收PC机下发的操作命令对应为0。
    (9) 更新RegNumber,if(RegNumber < N) {RegNumber++;} else{RegNumber = 0;}。
    (10) DPC执行完毕,函数返回。等待下一个中断。
2 控制程序设计
    本文设计的驱动实现了数据收发完整功能,能提供用户层进行可变大小DMA内存分配,使能数据收发、接收数据校验及提取数据帧头信息等操作。
    本设计基于MFC实现了一个控制界面程序,通过界面操作可实现用户定义DMA大小分配,数据收发控制命令、接收数据量显示及帧头信息显示等功能,如图4所示。控制界面与驱动通信采用IOCTL控制码,步骤如下:

 

 

    (1) 使用CTL_CODE宏定义控制码。本例定义的控制码可简单归类为读/写寄存器。数据在应用层进行组装。
    (2) 驱动程序中的ControlRoutine例程实现相应控制码功能。
    (3) 编写一个DLL,实现驱动与界面的交互接口。DLL内部实现打开设备驱动,调用DeviceIoControl发送命令等操作。在外部向应用层提供用于发送命令的接口函数。如分配DMA、写入DMA首地址、使能数据收发等。
    (4) 应用程序调用DLL提供的接口函数,实现对设备的控制。
    本文分析了一般PCI/PCI-E设备驱动的设计要求与设计方式,详细介绍了重要例程的设计需求,重点分析了高速实时DMA传输系统的设计方法,给出了驱动设计的详细步骤,最后介绍了控制程序的设计。提出的高速实时DMA传输系统的设计方法,实现了高速实时数据传输功能,解决了当基带与PC机传输速率不一致时。经常出现的数据丢失和无法高速传输的问题。
参考文献
[1]  尹勇,李宇.PCI总线设备开发宝典[M].北京:北京航空航天大学出版社,2005.
[2] PCI-SIG. PCI Express base specification revision1[S]. PCI_SIG,April 15 2003.
[3] 吴宏钢,尹爱军,秦树人.基于WDM模型的PCI数据采集卡驱动程序设计[J].中国测试技术,2008,34(3)59-62.
[4] 张帆,史彩成,等.Windows驱动开发技术详解[M].北京:电子工业出版社,2008.
[5] Microsoft. Microsoft Windows Driver  Kits [EB/OL].[2010-2-26]. http//www.micorsoft.com//wdk.
[6] 王招凯,禹卫东.基于PCIE总线的雷达数据记录器驱动程序开发[J].微计算机信息,2008,24(4-2):89-91.

此内容为AET网站原创,未经授权禁止转载。