摘 要:针对自主开发设计的PXIe 接口可重构测试仪器,在Windows 7 x64 环境下设计开发基于WDF 驱动框架的PXIe 驱动程序,解决上位机与仪器设备之间的通信问题。首先在简要描述WDF 驱动框架特点的基础上,结合实际功能需求重点阐述PXIe 驱动程序的开发过程。其次较为详细地介绍包括硬件访问、电源状态管理、DMA 传输及中断处理等功能实现的有效途径。最后经测试验证,该驱动程序运行稳定可靠,达到了设计要求。
关键词:PXIe 接口;可重构仪器;驱动程序设计;WDF 驱动框架;功能实现;测试验证
0 引 言
测试总线PXIe 在PCIe 总线上扩展了触发、定时等功能,是PCIe 总线在仪器领域上的扩展。PXIe 采用串行、点对点的连接方式实现设备间的通信,其背板带宽[1]可达6 Gb/s,还能提供差分时钟和同步,提高了仪器时钟的抗噪声性能。因此,PXIe 总线广泛适用于测试、控制、高带宽数据传输等需要高速、高带宽的领域。要实现PXIe 总线设备与上位机的通信,就需要开发相应的驱动程序。驱动程序是操作系统的组成部分,是直接与硬件沟通的媒介。
基于此,本文开发了一种PXIe 接口可重构测试仪器的驱动程序。在实际测试中将仪器插入PXIe 机箱中进行调试,驱动程序运行稳定可靠,成功实现了控制指令发送和数据传输的功能。
1 PXIe 接口可重构仪器
传统ATS 采用平台加适配器的共享资源架构,通过开关系统分配测试通道和资源,开关延迟、测试资源竞争和死锁等问题不可避免。解决上述问题可采用专用资源架构,使得各个测试通道均具备在任何时刻可提供所有测试资源的能力,为此,可重构仪器提供了良好的解决方案。PXIe 接口可重构仪器以Cyclone IV GX 系列FPGA 为核心,具备数模转换、模数转换、波形产生和电压比较等功能。仪器通过接收上位机经PXIe 总线向下发送的指令,选择和设置各测试通道的功能和参数,同时将测量数据上传至上位机进行处理。
PXIe 同PCIe 遵循相同的协议,是在PCIe 总线基础上附加必要的时钟信号、触发总线、星形总线、本地总线等PXIe 扩展信号形成的[2]。因此,可重构仪器通过PCIe IP 硬核设计实现PXIe 接口。该实现方式在保证较大传输带宽的基础上,可简化电路设计,降低开发成本。若要通过PXIe 总线实现上位机与仪器的通信,设计可靠稳定的驱动程序显得十分关键[3]。
2 驱动程序设计
2.1 开发环境搭建
在Windows 7 x64 操作系统下安装VS2013 搭配WDK8.1 工具包完成开发环境的搭建。该集成开发环境具有用于开发、编译、生成以及调试驱动程序所需的配套工具。
2.2 WDF 驱动框架
WDF 驱动框架是微软最新的驱动开发框架,是在WDM 驱动模型的基础上进一步封装发展而来,具备了真正意义上面向对象、事件驱动的性质特点[4⁃5]。
WDF 驱动框架负责管理与操作系统内核相关的多数交互,实现了对电源管理、即插即用等公共功能的支持,从而隔离了驱动程序与操作系统内核,而降低了驱动程序对操作系统内核的影响[6]。
WDF 框架内部由对象模型和事件回调例程构建组成,框架中所有的事物诸如设备、中断、I/O 请求等都由对象定义。对象模型的实现使得各个对象都具有相应的属性、方法和事件,因此可围绕对象完成参数的获取设置、触发特定事件回调例程等各类操作。WDF 包括KMDF 和UMDF 两种驱动框架模式,分别运行在操作系统内核环境和用户环境下。PXIe 驱动程序运行在操作系统内核中,因而采用KMDF 模式进行开发[7]。PXIe 驱动程序的开发流程如图1 所示。
图1 PXIe 驱动程序开发流程
DriverEntry 例程作为驱动程序的入口,负责驱动程序框架的初始化,在系统第一次加载驱动程序时被调用创建驱动对象并设置PXIeEvtDeviceAdd 例程。PXIeEvtDeviceAdd 例程负责创建各类对象并完成初始化I/O 队列、初始化中断处理等工作。
驱动程序主要实现6 类功能:I/O 请求处理、硬件访问、DMA 操作、中断处理、设置Event 事件以及电源状态管理。
3 驱动程序功能实现
3.1 I/O 请求处理
上位机应用程序通过I/O 请求实现与驱动程序的通信[8]。在WDF 驱动框架中,各类I/O 请求会被封装成WDFREQUEST 对象放入I/O 队列中进行排队。驱动采用了框架的默认串行队列,会按照先后顺序自动调用对应类型的I/O 处理例程进行处理。
PXIeEvtIoRead 例 程 、PXIeEvtIoWrite 例 程 、PXIeDeviceIoControl 例程分别负责对硬件的读访问、写访问和DMA 操作。
3.2 硬件访问
硬件访问具体就是对BAR 空间的访问,完成对各功能寄存器的设置,如设置DMA 配置寄存器启动DMA传输,设置通道控制寄存器选择信号通道等[9]。
驱动程序只有在实现设备内存地址空间的映射后才能对硬件进行访问。驱动程序在PXIeEvtDeviceAdd例程中创建并初始化PNP 及电源管理对象,通过PNP 及电源管理例程来建立硬件资源与系统的映射关系。
在驱动加载时,WDF 驱动框架便会调用PXIeEvtDevicePrepareHardware 例程来获取设备的内存地址空间。由于PXIe 接口的BAR 空间被配置为存储器空间,所以不再处理I/O 端口资源和中断资源。此时,对BAR 空间的物理地址映射必须调用MmMapIoSpace,将物理地址转换成操作系统内核模式下的虚拟地址。之后,驱动程序便可通过虚拟地址读/写BAR 空间相应地址上的寄存器。PXIeEvtDevicePrepareHardware 例程获取设备内存地址空间的流程如图2 所示。
图2 获取内存地址空间流程
在完成映射的基础上,当驱动程序收到应用程序调用PXIeEvtIoRead 例程或PXIeEvtIoWrite 例程的I/O 请求后,可使用函数READ_REGISER_XXX 或WRITE_REGISER_XXX 访问映射地址进行读写操作。
在移除设备驱动程序时,需在PXIeEvtDevice⁃ReleaseHardware 例程中调用MmUnmapIoSpace 解除内存物理地址与系统虚拟地址的映射关系。
3.3 DMA 传输及中断处理
大量数据的高速传输采用直接存储器访问(Direct Memory Access,DMA)方式。由于在FPGA 中已经实现DMA 控制器的设计,因此驱动程序仅考虑提供传输所需的内存空间和DMA 配置寄存器信息即可。DMA 操作流程如图3 所示。
3.3.1 创建DMA 传输对象
驱动程序初始化时,在PXIeEvtDevcieAdd 例程中创建WDFDMAENABLER 对象并说明DMA 通道的特性;同时创建对象WDFCOMMONBUFFER,向系统申请公共缓冲区,即用一段物理地址上的连续内存作为读/写内存空间。此外还需创建WDFDMATRANSACTION 对象用于DMA 传输事务的控制。
3.3.2 DMA 传输事务初始化
当驱动程序接收应用程序,调用PXIeDeviceIoControl处理例程,发起DMA 操作的I/O 请求后,调用WdfDmaTransactionInitialize 注 册PXIeEvtProgramDma 例程,完成DMA 传输事务的初始化。
图3 DMA 操作流程
在PXIeEvtProgramDma 例程中需依次配置DMA 数据传输首地址、数据传输长度以及中断服务寄存器,最后配置读/写操作控制寄存器,用于通知设备启动DMA传输。该例程在执行启动DMA 传输事务后被调用执行。
3.3.3 启动DMA 传输
调用函数WdfDmaTransactionExecute执行启动DMA传输事务后,注册例程PXIeEvtProgramDma 随即被调用完成各寄存器的配置并启动DMA 传输。
3.3.4 中断处理
DMA 传输完成时,设备会向上位机发出中断信号,驱动程序需响应中断并进行相关处理。驱动程序通过中断对象WDFINTERRUPT 实现上述需求。该对象在PXIeEvtDeviceAdd 例程中创建并初始化,并与中断处理例程、延迟调用例程相关联。中断处理流程如图4所示。
由于中断服务例程处于硬件中断DIRQL 级别上运行,长时间处于该例程会影响其他线程运行,且很多内核函数无法被调用。因此完成中断信号的判断后,可调用运行在DISPATCH_LEVEL 级别上的延迟调用例程完成后续处理工作。
结合上述情况,中断处理包含了中断处理例程和延迟调用例程两个部分。中断处理例程的任务:
1)判断中断信号来源是否由设备产生,通过读取中断服务寄存器的方式实现。由于设备采用INTx 中断方式,共享中断机制可能与其他设备共享中断号,从而导致中断信号并非来源于指定设备[10]。
2)清除中断信号,若中断信号由设备产生则必须清除中断标志,即设置中断服务寄存器至初始状态。避免因清除不及时导致一个中断信号多次调用中断处理例程,造成系统死机的情况[11]。
图4 中断处理流程
延迟调用例程的任务:
1)判断DMA 传输是否完成,通过调用函数WdfDmaTransactionDmaCompleted 实现。若未完成则应继续进行 DMA 传输,若完成则调用函数WdfDmaTransactionRelease,结束当前DMA 传输。
2)通知应用程序DMA 传输结束并对传输数据进行处理,通过事件通知方式实现。
3.4 事件通知
事件是驱动程序唤起应用程序进行操作的方法[12],驱动程序通过内核事件的方式通知应用程序DMA 传输结束。应用程序在创建事件句柄后,通过发送I/O 请求将事件句柄传递给驱动程序。
驱动程序在PXIeEvtDeviceIoControl 例程中根据获得的事件句柄构造一个内核事件。之后,应用程序便在辅助线程上等待该内核事件置位。知并读取DMA 传输数据。
3.5 电源管理
在设备电源功耗的控制方面,当驱动程序内无任何活动的情况下,即设备处于空闲状态时进入低功耗状态,可达到降低设备电源功耗的目的。
电源管理遵循ACPI 规范[13⁃14],因此设计在操作系统S0(系统工作状态)下,当设备处于空闲状态达到规定时间后,自动由D0 状态(设备工作状态)进入D2 状态(设备休眠状态)。电源状态管理流程如图5 所示。
图5 电源状态管理流程
当有新的I/O 请求到达驱动程序时,唤醒并调整设备电源状态至D0状态。结构体WDF_DEVICE_POWER_POLICY_IDLE_SETTINGS 负责空闲功能参数的设置,PXIeEvtDeviceAdd 例程中代码设计如下:
4 结 语
驱动程序的开发需要掌握操作系统、硬件设备等方面的知识,需要结合实际情况灵活实现驱动程序功能,从而实现软硬件的协调工作。本文基于WDF 驱动框架设计了PXIe 可重构仪器的设备驱动程序,成功实现了硬件访问和DMA 传输等功能。在实际测试中将仪器插入PXIe 机箱中进行调试,驱动程序运行稳定可靠,成功实现了控制指令发送和数据传输的功能。