當(dāng)下最火的是AI,Machine Learning, Embedded Vision。FPGA老酒新裝,在這個(gè)圈子里也可以摻和摻和。而談到linux,這里從事傳統(tǒng)嵌入式開發(fā)的朋友不在少數(shù),八成都在謀求轉(zhuǎn)型,尋個(gè)風(fēng)口。急人所求,本篇軟文以一個(gè)簡(jiǎn)單的實(shí)例來介紹一下FPGA的嵌入式linux里應(yīng)用。隱去過多設(shè)計(jì)細(xì)節(jié),重過程體驗(yàn)。新客們可以看個(gè)熱鬧,F(xiàn)PGA老玩家可以就此貴安。
這里的UIO即Userspace I/O,本文中UIO泛指UIO設(shè)備和UIO驅(qū)動(dòng)。它在Linux kernel的世界里比較小眾,主要是只一些定制設(shè)備和相應(yīng)的驅(qū)動(dòng)。UIO內(nèi)核驅(qū)動(dòng)指負(fù)責(zé)將中斷和設(shè)備內(nèi)存暴露給用戶空間,再由UIO用戶態(tài)驅(qū)動(dòng)(Application)來實(shí)現(xiàn)具體的業(yè)務(wù),隨心所欲的玩。學(xué)術(shù)點(diǎn)叫做高度定制化,柔性設(shè)計(jì)。那怎么和FPGA扯上了關(guān)系?是的,F(xiàn)PGA在硬件世界里也是隨心所欲的玩,這一硬一軟還真是登對(duì),在一起啊在一起。
本實(shí)驗(yàn)工程利用Xilinx Zynq UtralScale+(MPSoC)ZCU102嵌入式評(píng)估板上實(shí)現(xiàn)多個(gè)UIO,借助Xilinx的工具完成硬件工程和linux BSP的開發(fā),最后通過測(cè)試應(yīng)用程序完成測(cè)試。
ZCU102上的MPSOC集成固化了四核ARMCortex-A53,雙核Cortex-R5以及Mali-400 MP2 GPU,這部分官方稱為PS(processor system)。另外一部分就是FPGA,即PL(programming logical)。PS端實(shí)現(xiàn)控制,PL用來實(shí)現(xiàn)應(yīng)用加速,兩者通過AXI連接。跑這個(gè)小實(shí)驗(yàn),呵呵,大材小用。只是本人手頭正好有這個(gè)板子不得不裝。筒子們可以去買了個(gè)ZYBO, ZED的板子試試身手。
實(shí)驗(yàn)報(bào)告
實(shí)驗(yàn)人員:
實(shí)驗(yàn)時(shí)間:
實(shí)驗(yàn)材料:
Xilinx Vivado 2017.2
硬件工程設(shè)計(jì)工具
有免費(fèi)版本
Petalinux 2017.2
Linux BSP開發(fā)工具(基于yocto)
免費(fèi)
ZCU102 EVB final v1.0
高端開發(fā)板一枚
收費(fèi),貴
PC
電腦一臺(tái)
要快一點(diǎn),空間大一點(diǎn)
硬件設(shè)計(jì)
建立Vivado工程,適配ZCU102 EVB。通過IP Integrator加入PS,在PL側(cè)加入5個(gè)UIO輸入,其中1個(gè)是GPIO模塊(包含中斷輸出和設(shè)備內(nèi)存),另外4個(gè)是PIN連接到ZCU102 EVB上的DIP開關(guān),作為中斷輸入通過一個(gè)concat IP連接到PS的ps_pl_irq管腳。
板級(jí)細(xì)節(jié)請(qǐng)參考[1]UG1182,芯片資料參考[2]UG1085
IRQ source
Trigger type
IRQ number
Board Info
pl_irq_er
edge rising
121
SW13.8, DIP0
pl_irq_ef
edge falling
122
SW13.7, DIP1
pl_irq_lh
level high
123
SW13.6, DIP2
pl_irq_ll
level low
124
SW13.5, DIP3
axi_gpio_1
N/A
125
寫好約束文件,
set_property PACKAGE_PIN AN13 [get_ports pl_irq_ll]
set_property IOSTANDARD LVCMOS33 [get_portspl_irq_ll]
set_property PACKAGE_PIN AM14 [get_ports pl_irq_lh]
set_property IOSTANDARD LVCMOS33 [get_portspl_irq_lh]
set_property PACKAGE_PIN AP14 [get_ports pl_irq_ef]
set_property IOSTANDARD LVCMOS33 [get_portspl_irq_ef]
set_property PACKAGE_PIN AN14 [get_ports pl_irq_er]
set_propertyIOSTANDARD LVCMOS33 [get_ports pl_irq_er]
Vivado的圖形化的模塊設(shè)計(jì),豐富的IP庫(kù),加上可以上天的智能連接。有點(diǎn)數(shù)字電路設(shè)計(jì)的基礎(chǔ),很快就能完成這個(gè)小設(shè)計(jì)。整個(gè)設(shè)計(jì)如下圖。點(diǎn)贊!
軟件設(shè)計(jì)
這里用到Xilinx針對(duì)Linux BSP開發(fā)的Petalinux。它基于Yocto,加入Xilinx的Layers實(shí)現(xiàn)硬件工程的導(dǎo)入,將復(fù)雜的Yocto的設(shè)計(jì)流程打包簡(jiǎn)化,支持一定的用戶自定義功能,如QEMU仿真運(yùn)行,增加out-of-tree的驅(qū)動(dòng),Device tree修改,應(yīng)用程序編譯打包,等等。這里簡(jiǎn)單展示一下具體的命令過程。
$petalinux-create -t project --template zynqMP -n zcu102-pl2ps_irq
$cd ./ zcu102-pl2ps_irq
$petalinux-config --get-hw-description
$petalinux-config -c kernel
Enable UIO_PDRV_GENIRQ driver
CONFIG_UIO=y
# CONFIG_UIO_CIF is not set
CONFIG_UIO_PDRV_GENIRQ=y
$petalinux-build -c device-tree
PL側(cè)的dtsi文件生成與./components/plnx_workspace/device-tree-generation/pl.dtsi
這里只有GPIO UIO。PIN UIO因?yàn)椴皇荌P,所以相關(guān)信息無法由工具自動(dòng)生成。所以要做如下修改,
1.修改GPIO UIO設(shè)備端點(diǎn)
a)將中斷號(hào)改為93
b)將compatible改成“generic-uio” //我們后面要用Linux自帶的UIO_PDRV_GENIRQ驅(qū)動(dòng)
2.增加DIP UIO端點(diǎn)
a)將compatible改成“generic-uio”
b)依次設(shè)置中斷值89到93
c)按照每個(gè)DIP PIN的interrupt trigger type設(shè)置屬性值
*DTS里的中斷號(hào)與硬件中斷號(hào)有32的offset。
Petalinux提供了自定義DTS文件./project-spec/meta-user/recipes-bsp/device-tree/files/system-user.dtsi,將以上修改定義到system-user.dtsi.
有兩個(gè)方法來適配UIO端點(diǎn)和 UIO_PDRV_GENIRQ 驅(qū)動(dòng)
1. bootargs use“uio_pdrv_genirq.of_id=generic-uio”,可以通過DTS定義。
2. insmod uio_pdrv_genirq.ko of_id=generic-uiowhen install the driver
修改完后,編譯出Image.
$petalinu-build
$cd ./images/linux
$petalinux-package --boot--fsbl zynqmp_fsbl.elf --fpga --atf --pmufw --u-boot --force
將生成的BOOT.bin(bootloader)和image.ub(FIT uImage)拷貝到SD卡用于啟動(dòng)。
測(cè)試
這里引用下關(guān)于uio_pdrv_genirq驅(qū)動(dòng)的介紹
https://01.org/linuxgraphics/gfx-docs/drm/driver-api/uio-howto.html
Using uio_pdrv_genirq for platform devices
Especially in embedded devices, you frequently find chips where the irq pin is tied to its own dedicated interrupt line. In such cases, where you can be really sure the interrupt is not shared, we can take the concept of uio_pdrv one step further and use a generic interrupt handler. That’s what uio_pdrv_genirq does.
The setup for this driver is the same as described above for uio_pdrv, except that you do not implement an interrupt handler. The .handler element of struct uio_info must remain NULL. The .irq_flags element must not contain IRQF_SHARED.
You will set the .name element of struct platform_device to "uio_pdrv_genirq" to use this driver.
The generic interrupt handler of uio_pdrv_genirq will simply disable the interrupt line using disable_irq_nosync(). After doing its work, userspace can reenable the interrupt by writing 0x00000001 to the UIO device file. The driver already implements an irq_control() to make this possible, you must not implement your own.
Using uio_pdrv_genirq not only saves a few lines of interrupt handler code. You also do not need to know anything about the chip’s internal registers to create the kernel part of the driver. All you need to know is the irq number of the pin the chip is connected to.
在結(jié)合驅(qū)動(dòng)代碼./drviver/uio/uio_pdrv_genirq.c)可知,每個(gè)UIO設(shè)備會(huì)有對(duì)應(yīng)的/dev/uioX的設(shè)備節(jié)點(diǎn)。用戶態(tài)驅(qū)動(dòng)程序的讀操作會(huì)阻塞直到UIO硬件中斷發(fā)生。UIO的中斷處理程序uio_pdrv_denirq_handler()會(huì)關(guān)閉該硬件中斷。用戶態(tài)驅(qū)動(dòng)程序需要通過write函數(shù)來觸發(fā)uio_pdrv_genirq_irqcontrol()以完成中斷的使能和關(guān)閉。代碼如下,
啟動(dòng)內(nèi)核及加載uio_pdrv_genirq驅(qū)動(dòng)
檢查/proc/interrupts
細(xì)心的你一定發(fā)現(xiàn)了一個(gè)坑,少了2個(gè)UIO中斷(IRQ122和IRQ124),原來是硬件不支持Edge falling和Level Low的觸發(fā)模式。kernel log如下。
測(cè)試DIP UIO方法一
通過撥動(dòng)2個(gè)DIP,觀察到
2個(gè)DIP中斷發(fā)生了,可是不論怎么再撥動(dòng)DIP開關(guān),始終是1。上文鋪過,這個(gè)中斷在驅(qū)動(dòng)的中斷處理程序里會(huì)被關(guān)掉,需要通過應(yīng)用程序調(diào)用write()來打開。這里有個(gè)easy way,使用萬能的echo命令“echo 0x1 > /dev/uioX”,再配合DIP可以觸發(fā)多次中斷。
測(cè)試DIP UIO方法二
前面的方法比較low,這里有稍微高級(jí)的享受。寫個(gè)簡(jiǎn)單的用戶態(tài)驅(qū)動(dòng)程序,上代碼。
借助petalinux提供的交叉編譯工具編譯出bin文件,拷貝到啟動(dòng)SD卡。
運(yùn)行測(cè)試程序并配合DIP開關(guān)進(jìn)程測(cè)試。(為了更好的體現(xiàn)測(cè)試運(yùn)行情況,在UIO內(nèi)核驅(qū)動(dòng)里增加了irqcontrol的調(diào)用打?。?/p>
測(cè)試GPIO UIO
This test application mmap out the registers fromhardware to user space. Then enable all the IRQ bits in GIER and IP_IERregisters and dump out all the registers’ values. Please refer topg144-axi-gpio.pdf for the IP.
UIO驅(qū)動(dòng)會(huì)將設(shè)備內(nèi)存(寄存器)空間枚舉出來,由用戶態(tài)驅(qū)動(dòng)程序通過mmap導(dǎo)出進(jìn)行讀寫控制。參見AXI_GPIO IP的文檔pg144-axi-gpio.pdf,其寄存器如下。
測(cè)試應(yīng)用程序會(huì)通過設(shè)置GIER和IP_IER來使能中斷。上代碼。
測(cè)試過程
或許你覺得這么貼圖代碼不厚道而不能施展復(fù)制黏貼大法,可不知我拙與WORD,沒try出好排版。莫急莫急,這里有GIT,https://gitenterprise.xilinx.com/AlexHe/UIO_Linux_Demo
硬件資源文件和Image,測(cè)試代碼一個(gè)都不能少,統(tǒng)統(tǒng)獻(xiàn)上。
實(shí)驗(yàn)結(jié)論
UIO這種可高度自定義的設(shè)備結(jié)合Xilinx的MPSoC可以實(shí)現(xiàn)非常靈活的應(yīng)用。Xilinx提供的完備的工具集,給用戶帶來了高效的開發(fā)體驗(yàn)。本例雖然簡(jiǎn)單,但Xilinx所推崇的All Programming的概念和實(shí)際的FPGA加速應(yīng)用的的確確是建立在這些軟硬件協(xié)同技術(shù)之上。
作者簡(jiǎn)介:
何曄:做過學(xué)生也做過老師又做了學(xué)生后錯(cuò)入了IT門。接觸linux也有十來個(gè)年頭,輾轉(zhuǎn)于各種驅(qū)動(dòng)開發(fā),無一精通。在AMD就職期間,曾提交過少量的xHCI和ACPI的patch,算是在linux內(nèi)核留下點(diǎn)印記?,F(xiàn)就職于Xilinx,從事與嵌入式FPGA的應(yīng)用支持。FPGA的使用經(jīng)驗(yàn)滿打滿算也不過半年,斗膽寫下此篇分享一下經(jīng)驗(yàn),也是自己一個(gè)小小設(shè)計(jì)的總結(jié)。其中謬誤,望指正并諒解。
參考文獻(xiàn)
The Userspace I/O HOWTO https://01.org/linuxgraphics/gfx-docs/drm/driver-api/uio-howto.html
[Xilinx]
[1] UG1182 - ZCU102 Board User Guide
[2] UG1085 - Zynq UltraScale+ MPSoC Technical Reference Manual
[3] UG1144 - PetaLinux Tools Documentation: Reference Guide
[4] UG940 - Vivado Design Suite Tutorial: Embedded Processor HardwareDesign
[5] PG144 - AXI GPIO v2.0 Product Guide
評(píng)論
查看更多