王法臻,崔少輝,王 成
(陸軍工程大學石家莊校區,石家莊 050003)
PXIe 總線是PCIe 總線在儀器領域的拓展,通過在PCIe 總線基礎上附加必要的時鐘信號、觸發總線、星形總線和本地總線等,可形成PXIe 擴展信號[1]。PXIe 總線采用點對點的通信方式并利用差分信號進行數據傳輸,可使數據的傳輸速率和品質得到大幅提升。
設備驅動程序作為操作系統的重要組成部分,是應用程序與硬件完成數據交互所必需的媒介[2-3]。為滿足自主設計的PXIe 可重構儀器與上位機之間的通信需求,在完成PXIe 接口硬件部分設計的基礎上,必須開發相應的PXIe 設備驅動程序才能實現功能控制指令和測試數據經PXIe 總線的傳輸。因此,設計穩定可靠的驅動程序十分關鍵。
本文在研究Linux 字符設備驅動結構的基礎上開發可重構儀器的PXIe 設備驅動程序。設計直接存儲器存取(Direct Memory Acess,DMA)傳輸操作流程,并對應用程序與驅動程序數據的交互方式進行優化,從而提高數據傳輸效率,使PXIe 可重構儀器能夠在國產操作系統下穩定運行。
通用自動測試系統(Automatic Test System,ATS)采用共享資源架構,其通過開關系統分配測試通道和資源,易產生開關延遲、測試資源競爭和死鎖等問題[4]。多通道可重構儀器具備良好的通用性和靈活性[5-6],可根據用戶需求進行重構從而獲得不同的測試能力,且每路測試通道均具有獨立完成測試的能力,因此為傳統通用ATS 存在的問題提供了良好的解決方案。
多通道PXIe可重構儀器以Altera公司的Cyclone IV系列FPGA 為核心,配置測量功能電路模塊,其32 路測試通道均具備數模轉換、模數轉換、波形產生以及電壓比較等功能,各通道可根據測試需求通過重構實現一定的功能擴展。上位機與儀器設備經PXIe總線接口完成功能控制指令及測試數據的傳輸。由于PXIe 與PCIe 遵循相同的協議,因此PXIe 接口的開發通過FPGA 中的PCIe IP 硬核實現,配置選擇為PCIe 1.1 x4 鏈路,理論上最大傳輸速率可達1 GB/s。該實現方式相比采用專用芯片進行接口開發的優勢在于可簡化電路設計同時降低開發成本。
Linux 貫徹“一切皆文件”的思想,因此各類外部設備也都被看作是文件,一般稱作設備文件(或設備節點)[7]。用戶層應用程序并不能直接訪問操作外部硬件設備,需要先通過如open、close、read、write 和unlocked_ioctl 等系統調用訪問設備文件,然后借助Linux 中虛擬文件系統(Virtual File System,VFS)所提供的訪問接口尋找各個系統調用的響應函數,從而進一步操作外部硬件設備[8]。
設備文件所對應的設備驅動程序負責為操作外部硬件設備提供與系統調用相關聯的響應函數,這些響應函數可直接訪問外部硬件設備。因此,用戶層應用程序必須通過設備驅動程序來實現對外部硬件設備的訪問操作。Linux 系統下設備驅動程序、應用程序及硬件設備三者之間的關系如圖1 所示。

圖1 設備驅動、應用程序與硬件設備間的關系Fig.1 Relationship of device driver,application and hardware device
Linux 設備驅動程序按設備類型一般劃分為字符設備驅動、塊設備驅動和網絡接口驅動3 類[9]。字符設備以字節為單位傳輸數據,塊設備以塊為單位(每塊至少512 Byte)傳輸數據,網絡設備以用戶數據包形式在網絡媒介上傳輸數據。
PXIe 設備驅動程序以字符設備驅動結構為主體框架進行設計開發,因此,需要先對字符設備驅動的相關情況有所掌握。字符設備、字符設備驅動和應用程序之間的結構關系如圖2 所示,具體描述如下:
1)應用程序通過系統調用經VFS 提供的接口訪問驅動程序,驅動程序根據應用程序要求進一步操作訪問設備。
2)結構體cdev 負責描述字符設備驅動,其主要由設備號和文件操作結構體兩部分組成。當系統調用宏module_init 加載驅動時,即實現該結構體的實例化并完成字符設備的注冊。
3)設備號dev_t 負責存放驅動程序給設備分配的主設備號和次設備號,主設備號負責標識設備類型,次設備號負責標識同類設備中的特定設備。由于設備號是設備文件在系統中的標志且唯一確定,因此新設備號的申請不能與已有設備號發生沖突。
4)文件操作結構體file_operations 作為用戶層與內核層交互的接口,是建立起應用程序與驅動程序交互的關鍵所在。當用戶層應用程序使用open、release、unlocked_ioctl 等Linux 系統調用時,系統經由VFS 間接尋找并執行file_operations 結構體中所對應的xxx_open、xxx_release、xxx_unlocked_ioctl 等操作函數,完成對設備的指定操作。
5)驅動卸載在系統調用宏module_exit 時完成,負責設備號的釋放和所申請的相關資源并注銷cdev結構體。

圖2 字符設備驅動程序結構Fig.2 Structure of character device driver
為使儀器能夠在國產操作系統下工作運行,驅動程序的開發環境選擇國產Deepin 15.11 操作系統,Linux 內核版本號4.15.0-30-generic,使用系統自帶vim 編輯器和GCC 編譯器完成驅動程序代碼的編寫及編譯。
PXIe 設備驅動程序基于字符設備驅動結構的開發設計流程如圖3 所示。

圖3 PXIe 設備驅動程序設計流程Fig.3 Procedure of PXIe device driver design
驅動程序的初始化主要完成對結構體cdev 和結構體pci_driver 的初始化,目的在于完成PXIe 設備在系統中的注冊、硬件資源的獲取以及建立與應用程序的交互關系。
cdev 結構體已在上文進行相關介紹,主要實現設備在系統中的注冊并建立與應用程序的交互關系。設備號dev_t 申請成功后,通過調用函數cdev_init 初始化cdev 中的file_operations 結構體,建立其與應用程序的交互關系,通過調用函數cdev_add 向系統添加字符設備從而完成設備注冊。Linux 系統調用所對應的file_operations結構體成員函數設置如下:函數pxie_open和pxie_release 負責連接和斷開應用程序與驅動程序的連接;函數dma_mmap 負責實現內存共享映射以提高應用層與內核層間數據交互速率;函數pxie_read 和pxie_write 負責對BAR 空間上寄存器的訪問,函數dma_unlocked_ioctl 用于DMA 傳輸控制指令的發送。
pci_driver 結構體實現驅動程序與特定設備的匹配以及硬件資源信息的獲取。驅動初始化的實現代碼如下:

3.1.1 設備匹配
操作系統啟動后會自動檢測總線上的所有設備,并將廠商號(VendorID)、設備號(DeviceID)等硬件信息記錄在pci_dev 結構體中。驅動程序即通過這些硬件信息實現與設備的匹配連接。
驅動程序的pci_device_id 結構體中包含設備的ID 信息,通過宏MODULE_DEVICE_TABLE 將其導出至用戶空間。在驅動初始化過程中,當調用函數pci_register_driver 初始化pci_driver 結構體時,系統會自動匹配pci_dev 結構體與驅動程序pci_device_id結構體中的設備ID 信息。若匹配成功,則激發驅動程序調用pxie_probe 函數去探測設備以獲取硬件資源信息[10-11]。設備匹配的實現代碼如下:

3.1.2 設備探測
設備硬件資源的探測通過pxie_probe 函數進行,主要完成I/O 資源的申請、基址寄存器(Base Address Register,BAR)空間基地址的獲取、設備中斷號的申請以及中斷處理函數的注冊,同時將相關資源信息保存至私有數據結構體private_data 中供驅動程序使用。設備探測的實現代碼如下:

PXIe 設備共有6 個BAR 可使用,均可配置為I/O空間或存儲器空間,兩者在訪問方式上有所不同,設備僅使用BAR0、BAR1 且配置為存儲器空間。在完成BAR 物理地址到內核空間虛擬地址的映射后,便可通過函數ioread32/iowrite32 訪問BAR 中相應地址上的配置寄存器實現DMA 傳輸。
3.1.3 設備刪除
通過調用函數cdev_del 和pci_unregister_ driver分別注銷cdev結構體和pci_driver結構體,以實現設備的移除并調用pxie_remove 函數刪除所申請的硬件資源。設備刪除的實現代碼如下:

以DMA 方式向設備發送數據,用戶層應用程序需要先將數據傳輸至內核層驅動程序的DMA 緩沖區中,再通過對該緩沖區的操作寫入設備。以DMA方式從設備中讀取數據沿反方向執行。在頻繁進行數據傳輸時,大量數據若通過函數copy_from_user和copy_to_user 以拷貝的方式實現用戶層和內核層之間的數據交互,會影響整體傳輸效率[12],而通過建立共享內存映射可有效解決這一問題[13-14]。
共享內存映射是指將一段物理內存同時映射出用戶空間虛擬地址和內核空間虛擬地址,這樣應用程序和驅動程序均可對該段物理內存進行訪問,從而減少數據拷貝帶來的時間開銷,提高數據的傳輸效率。共享內存映射的具體實現由系統調用mmap,通過其在file_operations 結構體中的響應函數dma_mmap 完成。利用虛擬內存區(Virtual Memory Areas,VMA)結構將驅動程序創建的DMA 緩沖區的虛擬地址轉換到該段內存的物理地址,然后通過此物理地址為該段內存建立新的頁表并映射出起始的用戶空間虛擬地址供應用程序使用[15-16]。共享內存映射的實現代碼如下:

用戶層應用程序通過mmap 系統調用所得到的返回地址就是該段共享內存在用戶層的虛擬地址,應用程序通過該地址可直接對DMA 緩沖區進行訪問。
unsigned int *user_dmaaddress=(unsigned int *)mmap(NULL,DMA_LENGTH,PROT_READ|PROT_WRITE,MAP_SHARED,fd,0);
當設備停止使用時,應解除共享內存映射關系并釋放所申請的DMA 緩沖區。應用程序在release系統調用的響應函數pxie_release 中,通過調用函數munmap 和pci_free_consistent 實現。
上位機同設備之間的數據傳輸采用DMA 方式實現。由于在FPGA 內部已設計實現DMA 控制器,因此CPU 只需要完成對DMA 傳輸相關寄存器的配置以及中斷信號的處理即可,而不必直接參與數據傳輸,從而有效降低CPU 負荷并提高數據的傳輸速率。DMA 傳輸操作流程如圖4 所示。

圖4 DMA 傳輸操作流程Fig.4 Procedure of DMA transmission operating
在啟動DMA 傳輸前,通過函數iowrite32 設置BAR 空間上DMA 傳輸首地址、傳輸長度以及中斷服務寄存器,然后設置讀寫控制寄存器以啟動DMA傳輸。同時,通過等待隊列方式阻塞當前進程直至設備向上位機發出中斷信號,該信號是數據傳輸完畢的標志。
由于接口設計采用INTx 中斷方式,設備可能與其他設備共享同一中斷號,系統在實際運行過程所接收到的中斷信號可能源自其他設備,因此還需要對中斷信號的來源進行判斷,即通過函數ioread32檢查BAR空間上中斷服務寄存器的數值變化。若中斷信號確由設備產生,則清除中斷信號并喚醒等待隊列所阻塞的進程以通知應用程序傳輸結束。
Linux 驅動程序以內核模塊形式通過靜態或動態的方式加載至內核中[17]。由于靜態加載方式在內核編譯以及重啟系統過程中會帶來大量的時間開銷,因此在驅動程序開發調試階段采用動態加載方式添加和移除驅動模塊。驅動模塊的加載與卸載通過命令insmod 和命令rmmod 實現[18-19]。
驅動代碼文件pxie_device.c 的編譯通過vim 編輯器編寫Makefile 文件實現。由于Makefile 文件可通過make 工具自動完成編譯工作,因此只需要通過系統終端在源代碼文件所在目錄下執行make 命令,即可通過GCC 編譯器生成驅動模塊pxie_device.ko。pxie_device.ko 通過動態方式加載至內核后需創建相應的設備節點(設備文件),該節點的創建為應用程序提供了訪問設備的接口。加載與卸載的實現代碼如下:

系統驅動模塊顯示列表如圖5 所示。

圖5 系統驅動模塊顯示列表Fig.5 Display list of system driver module
Qt 是一個跨平臺的C++應用程序開發框架[20],本文通過其集成開發環境Qt Creator 以圖形化界面的方式開發實現測試程序,如圖6 所示。測試程序通過對FPGA 中FIFO(First Input First Output)存儲器進行數據讀寫傳輸以驗證驅動程序是否符合設計需求,數據校驗結果如圖7 所示。

圖6 測試程序界面Fig.6 Test program interface

圖7 數據校驗結果Fig.7 Data verification result
在測試過程中,數據實際傳輸速率較理論峰值存在一定差距,這主要是由于軟件時間花銷和協議規范消耗帶寬(8b/10b 編碼以及各協議層數據包封裝)所導致。當數據量較小時,這部分花銷占用比較大,導致實際測得的傳輸速率較低;當數據量較大時,這部分花銷占用比減小,從而使得傳輸速率顯著提高。
DMA 寫(設備接收上位機發送的數據)數據傳速率可達350 MB/s,DMA 讀(上位機接收設備發送的數據)數據傳輸速率可達420 MB/s。在傳輸相同數據量的情況下,DMA 讀速率大于DMA 寫速率,這主要是因為在軟件從設備讀取數據時,硬件端只需發送存儲器寫請求TLP 即可;而向設備寫入數據時,硬件端在發送存儲器讀請求TLP 后,還需要有存儲器讀完成報文進行應答,從而產生了一些時間花銷。
目前,Windows 操作系統仍占據國內操作系統絕大部分市場份額,但微軟已停止對Win7 的技術支持,而Win8/Win10 安全性較差,因此,實現面向國產操作系統的軟件開發具有重大意義。本文介紹國產Deepin操作系統下PXIe 驅動程序的開發方法,對應用程序與驅動程序間的數據交互過程進行優化,同時結合阻塞機制和中斷機制設計DMA 傳輸操作流程。測試結果表明,該方法數據傳輸準確穩定,可滿足PXIe 可重構儀器的數據通信需求,并且其通用性較好,在工程實踐應用中具有一定參考價值。在實際測試系統中往往需要使用多個同類型儀器,下一步將實現應用程序通過單個驅動同時對多個可重構儀器的操作控制及數據傳輸。