首页 | 新闻 | 新品 | 文库 | 方案 | 视频 | 下载 | 商城 | 开发板 | 数据中心 | 座谈新版 | 培训 | 工具 | 博客 | 论坛 | 百科 | GEC | 活动 | 主题月 | 电子展
返回列表 回复 发帖

利用WinDriver实现链式DMA

利用WinDriver实现链式DMA

在计算机与外设的通讯过程中,DMA方式,即DirectMemory access(直接内存访问),由于无需计算机CPU的干预就可以在内存和外设之间传输数据,是一种高效的数据交换方式。因为对于高速的设备,如PCI Express接口的板卡设备,如果采用普通方式在存储器和外设之间传输大量的数据将占用太多的CPU时间,严重影响系统的性能。而使用DMA方式则使传输数据的操作中CPU的涉及减小到最少。
要实现DMA的传输功能,在底层需要硬件本身具有这样的功能模块,即外设硬件可以主动发起数据传输。在上层则需要驱动与应用接口的配合。
文中给出如下的一种DMA传输方式的实现。硬件外设是基于Altera公司PCI Express硬核的FPGA加密卡,其与主机的接口采用是PCI Expre ss接口。上层则采用了Jungo公司的WinDriver软件提供的驱动和API接口。

1 链式DMA
PCI Express是用来互联诸如计算机和通信平台应用中外围设备的第三代高性能I/O总线。与原有的PCI并行总线相比,它没有大量的数据和控制线,对于硬件电路设计者来说,省去了很多硬件设计工作。PCI Express的传输速度远远大于PCI总线,PCI Express 1.1版本单个链路的单向吞吐量能达到250 MB/s。对于需要与主机进行大容量传输的系统来说,该总线标准的优势是非常明显的。Altera公司的Arria IICX系列器件内建PCI Express硬核,无需PHY和IP核,有利于节约FPGA资源,简化了PCI Express的接口设计。同时在用Quartus II软件生成的基于PCI Express硬核的样本工程中,集成了对DMA的支持功能,为进一步的硬件产品开发提供了基础。
实际应用中,DMA的传输方式可分为两种:简单DMA和链式DMA。简单DMA只需要主机告诉设备要传输数据的主机地址、设备地址和传输数据的长度,然后启动DMA传输即可。但简单DMA每进行一次传输,都要进行上面的设置,效率较低。
在用Quartus II软件的MegaWizard。Plug-In Manager工具生成的基于PCI Express硬核的样本工程中,实现了一个32 KB的带有字节写使能控制的双端口RAM的端点存储器,其读写数据总线宽度均为128位;并且实现了链式DMA的传输功能。这是一种效率远远高于简单DMA的传输方式,它只需要1次启动操作,就可以完成多次DMA传输。具体实现的方法是:在主机端,需要开辟一块内存区域,用来存储描述符表。所谓描述符表,是用来描述在主机与外设之间数据传输有关的地址与长度信息的,它由一个表头和多个描述符组成,其中每一个描述符对应一次DMA操作。用户根据自己的需求填写该描述符表,如下表所示。


在上表中,偏移地址是指到描述符表内存区域开始处的字节偏移地址。DMA长度用来设置本描述符对应的DMA传输的长度,是以32位双字为单位的。主机地址用来指示数据在主机端存放的位置。设备地址用来指示数据在设备的端点存储器中存放的位置。控制域用来控制在该描述符传输完成时设备是否更新描述符头的EPLAST域。
使用链式DMA传输数据时,主机首先通过设置DMA控制寄存器,告诉设备要传输数据的描述符表的起始地址和描述符的个数,然后启动DMA传输。设备收到启动DMA传输的命令后,首先根据主机提供的描述符表信息,检索相应的描述符,并存储到设备的FIFO中,然后根据每一个描述符中的地址和长度信息,进行相应的DMA数据传输。所有的DMA描述符处理完成后,设备更新描述符头的EPLAST域。主机便可通过查询EPLA ST域获得链式DMA传输完成的信息,从而可以启动下一次链式DMA传输。

2 WinDrivet中的DMA功能
WinDriver是Jungo公司提供的一种通用的驱动开发支持软件,它简化了用户的上层驱动开发和应用接口开发,而且易于再封装,实现商业化应用。该软件提供了对PCIExpress接口设备的驱动支持,而且也提供了对DMA实现功能的支持。
WinDriver提供了两种DMA缓冲区的分配方式,即连续缓冲区和分散/聚合缓冲区。前者当用户申请缓冲区时,分配的是一个物理地址连续的内存块;而后者分配的缓冲区在物理位置上可以是分段的,这些物理上不连续的内存段通过虚拟地址空间映射给用户的是一个在应用层连续的缓冲区。
由于采用DMA传输数据时,设备需要的主机地址是物理地址;而在主机端,申请大块的物理地址连续的内存区域往往是非常困难的。因此在使用DMA进行大量数据传输时,主机端使用分散/聚合缓冲区分配方式更方便和易于实现。此时,主机可以将缓冲区的每个内存段对应一个描述符,这样采用链式DMA方式可以解决大的内存区域物理地址不连续的问题,方便了大存储区域数据的传输。

3 具体实现

在具体实现时,主机是通过访问的设备的BAR2地址空间来设置DMA控制寄存器的。DMA控制寄存器分为DMA读控制寄存器和DMA写控制寄存器,它们均是由4个32位的双字组成,依次表示的含义是:控制域和描述表中描述符的个数、描述符表的高32位地址、描述符表的低32位地址、最后一个描述符的索引,其中第一个双字的控制域部分用来控制在每个描述符传输完成时设备是否更新主机描述符头的EPLAST域。DMA写(即数据由设备向主机传送)控制寄存器的开始地址位于BAR2地址空间的0字节偏移地址处,DMA读(即数据由主机向设备传送)控制寄存器的开始地址位于BAR2地址空间的16字节偏移地址处。主机可以通过调用WinDriver提供的API函数WDC_WriteAddr32每次设置DMA读/写控制寄存器的一个双字。
在每次DMA传输开始时,根据设备DMA的设计要求,必须先调用WDC_WriteAddr32函数向DMA读/写控制寄存器的第一个双字写入0xFFFF的内容,以使设备DMA进入到可以工作的状态。这是因为设备每次执行完DMA操作后,处于停止工作状态,只有通过对控制寄存器的第一个双字写0xFFFF才能使设备DMA模块退出停止状态,重新准备检索DMA描述符。详见样本工程的ahpcierd_dma_descriptor模块的状态机部分。
需要注意的是,描述表的描述符中的主机低32位地址必须是16的倍数,设备地址也必须是16的倍数。这是因为设备的端点存储器是一个带有字节写使能控制的RAM,且其每个周期读写16个字节的数据。对于DMA读操作来说,设备收到主机的带数据的完成包后,是根据主机端的地址设置端点存储器的字节写使能控制信号的。如果主机端地址的低4位与设备端地址的低4位不一致,将导致数据起始存储位置不正确。例如:如果主机端地址的低4位是8,设备端地址的低4位是0,端点存储器写的数据总线接收数据的第一有效时钟周期内接收的16字节数据,低8字节对应的字节使能位是0,高8个字节才是有效的数据。这些有效数据被写入端点存储器规定存储位置向后偏移8个字节的地方,这显然与设备要求的存储位置不符。
对于DMA写操作来说,设备从端点存储器中读取数据时,直接将设备地址的低4位丢弃了,所以设备地址也应为16的倍数。如果主机端地址低32位不是16的倍数,根据设备的Descriptor/Data Interface处理规则和PCI Express规范中存储器写事务包的处理规则,将导致主机收到的数据包的有效数据位置向后偏移,部分有效数据丢失,例如:如果主机端低32位地址的低4位是8,那么将使得从端点存储器读取的数据的前8个字节丢失,主机端收到的数据的开始位置比预期位置向后移了8个字节。
在设置主机的描述表时,先调用WinDriver提供的API函数WDC_DMASGBufLock对申请的进行DMA数据传输的内存块进行锁定,然后根据锁定的页数,确定描述表中描述符的个数,每个描述符对应其中的1页存储区域。根据上面对主机低32位地址的要求,在申请进行DMA传输的内存区域时,申请的空间大小可以比实际需要存储区域多16个字节。这样,在调用WDC_DMASGBufLock函数对申请的内存块进行锁定后,如果第一页的物理地址不是16的倍数(在多页的情况下,后面的页的物理地址均为16的倍数),可以DMA传输的实际区域向后移动相应的位置,以使数据存储的开始位置的物理地址为16的倍数。例如,如果第1页物理地址的低4位是4,那么可以向后移动12个字节作为DMA传输的开始位置。如果申请的空间由多页组成,应注意第1页对应的描述符传输数据的长度将减少,及对后面页对应描述符的设备地址的影响。
设置完成DMA传输的描述符表后,需要通过设置DMA控制寄存器,告诉设备主机描述表的起始物理地址和描述符的个数。这里,也有上面类似的要求。即要求描述表的低32位地址为16的倍数,否则将导致设备FIFO每个周期接收的描述符不是一个完整的描述符,即可能是由前一个描述符的后8字节和后一个描述符的前8字节组成。这将导致以后DMA数据传输的混乱和错误。为此,在调用WDC_DMASGBufLock函数对描述符表存储区域进行锁定时,可以进行上面类似的地址对齐处理。
因为在调用WDC_DMASGBufLock函数对一块存储区域进行锁定时,中间页的大小均是4 KB的倍数。这样就可以估算出一块内存区域大概可以分成几页,从而估计出描述符表由几个描述符组成。比如文中的设备存储器是一个32 KB的端点存储器,因此进行一次DMA传输的描述符个数不超过9个,描述符表占用的存储空间为:16+9*16=160字节。对于这种描述表所需空间少于2 KB的情况下,可以采用如下策略使得描述表的数据集中到一个物理页中,以便于对DMA控制寄存器的设置,即如果所需空间为x(x≤2032)字节,那么申请2(x+16)字节(≤4 KB)的空间,这样在调用WDC_DMASGBufLock函数进行存储区域锁定时,得到的页数最多为2页,且至少有一页的大小不小于x+16字节。于是,便可用此页作为描述符表的存储区域,并使描述符表的起始地址满足16的倍数的要求。按照上面的策略,一次DMA传输至少可以处理含有(2032-16)/16=126个描述符的描述符表,至少可以传输124*4 KB=496 KB大小的数据。如果DAM传输的数据很多时,可以分成多次DMA传输,这样仍可使每次DMA传输时,描述表的数据占用一个物理页。
上面的策略,也可应用于DMA数据存储区的设置。例如在我们进行的加密卡设计中,使用DMA写模块完成加密结果的输出。对于公钥算法,其加密结果的长度往往是固定的,比如,对2 048位的RSA签名运算,其签名结果长度为256字节。此时采用上面的策略,我们就可以通过一次DMA传输读到签名结果,这样就简化了底层设备对输出接口的设计。
在设置完DMA控制寄存器后,设备开始进行MDA描述符检索,然后开始进行数据传输,主机需要知道什么时候数据传输结束,以便进行其他处理或读取传输结果。有两种处理方式:中断方式和查询方式。由于Windows XP操作系统不支持消息中断请求(MSI),故采用查询的方式处理。这就需要设置描述符的控制域和DMA控制寄存器的控制域部分,以使设备更新描述符表的EPLAST域。需要指出的是:在设备DMA处理模块的实现中,描述表的描述符头中,最后一个双字的低16位是EPLAST域的实际值,即最后完成的描述符的索引号,而高16位是其他的信息。因此可以用下面的DMAWaitForCompletion函数进行查询处理。




在上面的函数中hDev为设备句柄;DT_HEAD_EPLAST为描述表的描述符头中,最后一个双字的地址指针;eplast_num为DMA传输的最后一个描述符的索引号,等于描述符表中描述符个数减1。函数的返回值为真,表示DMA传输正确完成。
为了使DMA缓冲区保持与CPU缓存、I/O缓存的同步,主机端应在设置DMA控制寄存器之前,对DMA传输的数据存储区和描述符表存储区调用WDC_DMASyncCpu函数,在完成DMA的传输查询之后,调用WDC_DMASyncIo函数和WDC_DMABufUnlock函数。

4 结束语

对基于Altera公司PCI Express硬核的链式DMA应用,Altera公司虽然提供了上层Demo示例程序,但其只进行了MDA传输的性能测试。文中给出了处理DMA数据传输时,主机端需要面对和解决的几个问题。通过这些处理方法和策略,保证了主机和设备之间DMA数据传输的正确性,简化了底层FPGA应用的设计,为利用DMA模块进行用户的应用逻辑设计提供了基础。
返回列表