吳 波, 焦楓媛, 劉 瑜, 耿生群
(1. 中北大學(xué) 環(huán)境與安全工程學(xué)院, 山西 太原 030051; 2. 中北大學(xué) 信息與通信學(xué)院, 山西 太原 030051;3. 北京航空航天大學(xué) 信息與通信學(xué)院, 北京 100000)
Spartan-6 XC6SLX25是由Xilinx推出的采用45 nm工藝的低成本FPGA. 片上搭載邏輯單元24 051個(gè), 標(biāo)準(zhǔn)I/O Bank 4個(gè), 可滿足一般情況下的數(shù)據(jù)接口擴(kuò)展需求[1]. TMS320DM8168是目前TI推出的DaVinci系列中最先進(jìn)的數(shù)字媒體處理器. 它包含采用ARM v7架構(gòu)并帶有NEON向量/浮點(diǎn)協(xié)處理器的最高主頻率可達(dá)1.2 GHz的Cortex A8 RISC處理器、 TI C674x VLIW架構(gòu)最高頻率可達(dá)1 GHz的浮點(diǎn)DSP內(nèi)核以及針對高清視頻采集與處理的圖像協(xié)處理器. 該片上SOC在與FPGA通信時(shí)可采用通用存儲控制器(General-Purpose Memory Controller, GPMC)[2-5]. GPMC總線是由德州儀器公司(TI)開發(fā)專門用于訪問外部存儲設(shè)備的總線接口[6].
本研究將基于Spartan-6 XC6SLX25與DM8168通過GPMC實(shí)現(xiàn)多路數(shù)據(jù)的采集.
圖 1 數(shù)據(jù)采集系統(tǒng)設(shè)計(jì)Fig.1 Thedesign of acquisition system
多路數(shù)據(jù)的采集系統(tǒng)設(shè)計(jì)如圖 1 所示.
在一般的生產(chǎn)活動與安全監(jiān)管中不僅需要針對視頻數(shù)據(jù)的記錄, 多數(shù)情況下需要對多路串口、 CAN總線數(shù)據(jù)進(jìn)行實(shí)時(shí)記錄. 本設(shè)計(jì)方案采用FPGA對多路串口、 CAN總線數(shù)據(jù)進(jìn)行采集, 并對數(shù)據(jù)采用幀頭、 幀ID、 幀長度的格式進(jìn)行重新組幀、 打包后送入GPMC接口. DM8168通過GPMC驅(qū)動對FPGA傳輸來的整包數(shù)據(jù)進(jìn)行讀取、 解幀并分別存入磁盤.
本研究采用FPGA對多路CAN總線數(shù)據(jù)、 多路串口數(shù)據(jù)進(jìn)行采集. 采集方式如圖 2 所示.
圖 2 FPGA數(shù)據(jù)采集方式Fig.2 Data acquisition method by FPGA
按圖 2 所示, 多路數(shù)據(jù)經(jīng)過FPGA采集后先通過一個(gè)小的FIFO_RX對自身進(jìn)行數(shù)據(jù)打包, 在小包數(shù)據(jù)上疊加時(shí)標(biāo)、 幀頭、 幀ID以及幀長度等信息后, 送入一個(gè)較大的FIFO. 在較大的FIFO中把同一時(shí)間內(nèi)的數(shù)據(jù)一次組幀添加幀頭、 幀長之后通過數(shù)據(jù)線向GPMC接口送入數(shù)據(jù)并通過中斷通知DM8168系統(tǒng)內(nèi)核.
FPGA采集的數(shù)據(jù)與DM8168之間通過FPGA模擬DM8168的GPMC總線的讀寫時(shí)序來完成Cortex A8與FPGA的高速信號傳輸. 這種接口設(shè)計(jì)不僅能完成高速數(shù)據(jù)的傳輸, 同時(shí)也降低了設(shè)計(jì)過程中的復(fù)雜技術(shù), 提高了數(shù)據(jù)傳輸?shù)姆€(wěn)定性與可靠性, 是一種實(shí)現(xiàn)ARM 和 FPGA通信的有效方法[7].
在面向DM8168的Cortex A8的GPMC驅(qū)動實(shí)現(xiàn)中, 可采用基于Linux 2.6.xx內(nèi)核普遍使用的字符設(shè)備類似的驅(qū)動管理與注冊機(jī)制, 字符設(shè)備指的是每次都以字符的形式進(jìn)行數(shù)據(jù)的發(fā)送和接收[8]. 在多路數(shù)據(jù)采集的設(shè)計(jì)中對外部設(shè)備的信號采集數(shù)據(jù)帶寬較低, 故本設(shè)計(jì)采用基于字符設(shè)備的GPMC驅(qū)動數(shù)據(jù)傳輸模式. 驅(qū)動運(yùn)行分為3部分: GPMC基于硬件層對內(nèi)核的注冊; GPMC基于應(yīng)用層面向內(nèi)核的注冊; GPMC驅(qū)動加載.
GPMC驅(qū)動運(yùn)行結(jié)構(gòu)如圖 3 所示.
圖 3 GPMC驅(qū)動運(yùn)行結(jié)構(gòu)Fig.3 The structure of GPMC driver running
在驅(qū)動運(yùn)行中驅(qū)動程序必須在嵌入式系統(tǒng)的內(nèi)核中進(jìn)行注冊與加載才能完成應(yīng)用層與外部設(shè)備的通信. GPMC驅(qū)動通過基于硬件層面向內(nèi)核的注冊, 將FPGA采集到的數(shù)據(jù)以字符的形式通過內(nèi)核進(jìn)行讀取, 然后存放在系統(tǒng)內(nèi)核中, 等到一定時(shí)間之后再通過驅(qū)動由內(nèi)核向應(yīng)用層發(fā)送信號, 通知應(yīng)用層讀取上一步中存放在內(nèi)核空間中的數(shù)據(jù).
由于驅(qū)動需要與內(nèi)核之間進(jìn)行交互, 在驅(qū)動制作與加載時(shí)需要將內(nèi)核中g(shù)pmc.c的部分內(nèi)核代碼以去掉static(靜態(tài)函數(shù))的方式進(jìn)行修改并重新編譯.
DM8168與各種各樣的字符設(shè)備或者存儲設(shè)備之間以L3慢速互聯(lián)的方式通過GPMC接口接入, 其原理框架[9]如圖 4 所示.
GPMC本身具有十分靈活的編程模式, 可以實(shí)現(xiàn)對多種外部設(shè)備實(shí)現(xiàn)多方案配置.
GPMC內(nèi)部具有高性能可配置的寄存器組. 通過對在單路GPMC總線使用情況下或者在多路片選GPMC總線使用情況下的GPMC寄存器配置可以令GPMC自身基于不同的外部設(shè)備對自身時(shí)序進(jìn)行自適應(yīng)配置[10].
圖 4 GPMC驅(qū)動硬件框架Fig.4 The hardware framework of GPMC driver
用戶在使用DM8168芯片的GPMC接口與外部芯片或設(shè)備進(jìn)行通信時(shí)不需要考慮GPMC總線與外部設(shè)備的協(xié)調(diào)問題, 這種特性使得DM8168通過GPMC總線能與更大范圍的外部存儲芯片及外部設(shè)備進(jìn)行通信, 如FPGA, SRAM, NANDFLASH及Ethernet PHY等芯片.
本研究在GPMC接口所采用的是L3低速接口, 其采用One 8-bit/16-bit/32-bit interconnect access (read/write)的方式配置. 在數(shù)據(jù)讀取時(shí)序上采用Asynchronous Single Read Access模式的時(shí)序配置.
驅(qū)動整體的初始化與配置過程如圖 5 所示.
GPMC初始化部分與參數(shù)設(shè)置部分在GPMC驅(qū)動加載中根據(jù)實(shí)際情況完成, 在驅(qū)動中設(shè)置 GPMC 寄存器參數(shù)前應(yīng)該將片選使能關(guān)閉[8].
4.3.1 GPMC底層部分的驅(qū)動設(shè)計(jì)
DM8168硬件層面向內(nèi)核部分的GPMC驅(qū)動注冊分為設(shè)備注冊與驅(qū)動注冊兩步. GPMC設(shè)備注冊的定義與初始化由Linux內(nèi)核中的/devices.c文件通過利用/linux/ioport.h中的resource結(jié)構(gòu)體定義一個(gè)包含有GPMC所有需資源的結(jié)構(gòu)體實(shí)現(xiàn).
Static structresource ti8168_gpmc_resources=
{
{
.start = TI_GPMC_REG_BASE,
.end = TI_GPMC_REG_BASE + SZ_16M-1,
.name = “gpmc_dev_reg”,
.flags = IORESOURCE_MEM
},
};
通過以上操作完成資源配置后通過linux/platform_device.h中的platform_device結(jié)構(gòu)體定義面向驅(qū)動設(shè)備的結(jié)構(gòu)體.
圖 5 驅(qū)動的初始化與配置Fig.5 The initialization and configuration of driver
linuxplatform_device.h中的platform_device結(jié)構(gòu)體為:
Static structplatform_device ti8168_gpmc_device =
{
{
.name = “gpmc_test”,
.id = 3,
.num_resources = ARRAY_SIZE( ti8168_gpmc_resources ),
.resource = ti8168_gpmc_resources;
}
};
通過以上操作完成驅(qū)動生成后, 通過linux/platform_device.h中的platform_driver完成結(jié)構(gòu)體定義, 執(zhí)行platform_device_register來完成驅(qū)動注冊與設(shè)備綁定.
structplatform_driver ti8168_gpmc_driver =
{
.driver =
{
.name = “gpmc_test”,
.owner = THIS_MODULE,
},
.probe = ti8168_gpmc_probe,
.remove = ti8168_gpmc_remove,
};
4.3.2 GPMC應(yīng)用層部分的驅(qū)動設(shè)計(jì)
DM8168應(yīng)用層面向內(nèi)核的驅(qū)動主要是基于硬件層部分的驅(qū)動定義面向應(yīng)用層的讀寫功能函數(shù).
通過定義中斷結(jié)構(gòu)體, 利用FPGA以中斷的形式觸發(fā)驅(qū)動讀取面向硬件層部分驅(qū)動的數(shù)據(jù)實(shí)現(xiàn)與硬件層之間的交互.
structirq_info
{
intirqinit; ∥ 中斷初始化標(biāo)志
intirqnum; ∥ 中斷號
intirqtype; ∥ 中斷觸發(fā)方式
intirqenable; ∥ 中斷使能標(biāo)志
unsigned intirqcnt; ∥ 中斷計(jì)數(shù)
};
通過定義設(shè)備信息結(jié)構(gòu)體存儲上一步中利用中斷響應(yīng)讀取到的硬件層數(shù)據(jù)信息以及應(yīng)用層使用的設(shè)備信息.
structgpmcdev_info
{
u32 gpmccsbr; ∥ GPMC片選虛擬地址
structirq_infoirqinfo; ∥ DSP2ARM中斷信息
unsignedint flagrecv1;
unsignedint flagrecv2;
unsigned short* pbuffer_recv1;
unsignedint buffer_len1;
unsigned short* pbuffer_recv2;
unsignedint buffer_len2;
}gpmcdevinfo;
通過使用file_operations結(jié)構(gòu)體中提供的函數(shù)指針, 基于前述的功能實(shí)現(xiàn)應(yīng)用層面向硬件設(shè)備的各種操作.
staticstructfile_operationsgpmcdev_ops =
{
.open = gpmcdev_open, ∥ GPMC獲得設(shè)備句柄
.write = gpmcdev_write, ∥ GPMC數(shù)據(jù)讀取
.fasync = gpmcdev_fasync, ∥ GPMC異步通信
圖 6 驅(qū)動初始化過程Fig.6 Drive initialization
.release = gpmcdev_release, ∥ GPMC設(shè)備釋放
.unlocked_ioctl = gpmcdev_ioctl, ∥ GPMC設(shè)備io控制
};
GPMC驅(qū)動的初始化與加載通過shell指令insmod實(shí)現(xiàn)[10], 其實(shí)現(xiàn)過程如圖 6 所示.
整個(gè)過程為insmod命令加載模塊初始化函數(shù), 在其執(zhí)行完之后, 加載探測函數(shù)并向內(nèi)核注冊驅(qū)動生成設(shè)備節(jié)點(diǎn).
驅(qū)動與應(yīng)用層之間的通信以fasync(異步通信)的方式實(shí)現(xiàn). 通過對/Linux/fs.h中結(jié)構(gòu)體fasync_struct的定義來實(shí)現(xiàn)異步通信.
structfasync_struct {
spinlock_t fa_lock;
int magic;
int fa_fd;
structfasync_struct *fa_next;
struct file *fa_file;
structrcu_head fa_rcu;
} * async = NULL;
……
int ret = fasync_helper(fd, filp, mode, &async);
驅(qū)動待應(yīng)用層準(zhǔn)備就緒后, 每隔1 s對向應(yīng)用層發(fā)送一個(gè)系統(tǒng)信號, 應(yīng)用層響應(yīng)到系統(tǒng)信號后使用系統(tǒng)IO向特定地址讀取數(shù)據(jù)包. 這樣每1 s讀取一包數(shù)據(jù)可以提高系統(tǒng)的讀寫效率, 降低線程CPU占用.
在GPMC驅(qū)動設(shè)計(jì)完成之后, 需要針對驅(qū)動中提供的file_operations結(jié)構(gòu)體中包含的函數(shù)進(jìn)行相應(yīng)的操作. 利用file_operations結(jié)構(gòu)體內(nèi)核, 能夠方便地調(diào)用驅(qū)動程序內(nèi)部的函數(shù), 并且可以方便地利用驅(qū)動程序內(nèi)部使用的文件結(jié)構(gòu)來識別設(shè)備[11].
GPMC設(shè)備操作部分代碼如下:
blocked = block_sigio();
sigemptyset(&sigact.sa_mask);
sigact.sa_flags=0;
sigact.sa_handler=sigio_handler;
unblock_sigio(blocked);
fd=open(DEVFILE, O_RDWR);
fcntl(fd, F_SETOWN, getpid());
oflag=fcntl(fd, F_GETFL);
fcntl(fd, F_SETFL, oflag|FASYNC);
通過上述代碼可實(shí)現(xiàn)應(yīng)用層對GPMC驅(qū)動發(fā)出的系統(tǒng)中斷信號的實(shí)時(shí)響應(yīng), 每一次中斷信號的響應(yīng)都會執(zhí)行voidsigio_handler(intsigno)函數(shù). 在該函數(shù)中通過調(diào)用intioctl(intfd, int request, void * p)函數(shù)即可將fd文件符指向GPMC設(shè)備中, 利用GPMC設(shè)備的request模式, 將內(nèi)存中數(shù)據(jù)的首指針賦值給*p, 傳入數(shù)據(jù)指針, 從而實(shí)現(xiàn)基于GPMC的多路數(shù)據(jù)采集過程.
通過GPMC方式通信的實(shí)現(xiàn)結(jié)果如圖 7 所示.
圖 7 FPGA時(shí)序圖Fig.7 FPGA sequence
測試持續(xù)時(shí)間如圖 8 所示. 數(shù)據(jù)分析結(jié)果如圖 9 所示.
圖 8 測試持續(xù)時(shí)間Fig.8 Test duration
圖 9 數(shù)據(jù)分析結(jié)果Fig.9 Data analysis
由此可以看出: 經(jīng)4 h最大數(shù)據(jù)帶寬連續(xù)數(shù)據(jù)采集測試, 本研究中所用采集方法在程序中運(yùn)行穩(wěn)定, 無誤碼.
本研究闡述了針對DM8168的基于FPGA數(shù)據(jù)采集與GPMC接口數(shù)據(jù)傳輸?shù)亩嗦窋?shù)據(jù)采集開發(fā)過程. 根據(jù)GPMC的硬件特點(diǎn), 參考Linux字符設(shè)備驅(qū)動程序開發(fā)了通用流程, 設(shè)計(jì)出了一套安全穩(wěn)定切實(shí)可行的GPMC驅(qū)動, 并利用該驅(qū)動在FPGA數(shù)據(jù)采集與DM8168之間進(jìn)行通信. 目前, 該驅(qū)動已經(jīng)成功移植在DM8168搭建的嵌入式開發(fā)平臺, 并通過FPGA完成了多路數(shù)據(jù)的采集.