原文來自http://www.eefocus.com/Kevin/blog/13-03/292519_c38f2.html
及
講述一個led燈,包含整個芯片架構的設計
當你可以修改點亮LED燈那個實驗led_test.sh, 改變LED點燈的速度,以及順序時, 一定想知道究竟發生了什么, 憑什么一個簡單的腳本, 就在Linux的用戶層控制板子上的某一個硬件,比如說,燈。
對於習慣了在單片機環境下無操作系統裸奔C/匯編的兄弟來說, 上面的問題似乎有些多余. 直接對某個地址進行讀寫操作, 這個地址經過譯碼,對應的管腳,連到板子上LED,不就可以控制LED了嗎。
這就說來話長了.簡言之,Linux 為了給每個進程一個獨立的地址空間,特意划分了用戶空間和內核空間。內核空間可以對物理地址操作,用戶空間只是虛地址。 而內核與用戶空間程序接口,采用特殊文件系統。也就是看上去像文件,其實連接到系統和設備的系統。比較老的有devfs文件系統位於設備驅動/dev,或者procfs位於/proc。當然,還有比較新的sysfs文件系統。
Led_test.sh, 采用的就是sysfs文件系統。 這個文件系統的特點吧,主要是可以一層一層壘硬件的驅動,比如先總線驅動,再某個USB驅動,在USB掛載的設備的驅動。這比/dev一個設備驅動包括所有相關設備層次清晰很多。在linux內核2.6以后才有的。
對sysfs先來點感性認識,在ZedBoard任何一個終端窗口輸入
sudo echo 61 > /sys/class/gpio/export
sudo echo out > /sys/class/gpio/gpio61/direction
sudo echo 1 >/sys/class/gpio/gpio61/value
sudo echo 0 >/sys/class/gpio/gpio61/value
你可以手工點亮或滅掉LD0那盞燈
輸入命令的時候, 到底發生了什么, 信號是怎么傳遞的呢 ? 先大致了解一下,具體細節在書的后續章節還會詳細說明
第一步,設備驅動控制GPIO的寄存器. 這就需要交給Linux 設備驅動來完成了。相關的介紹請參考書本在13.2節找到 .
第二步,那么GPIO的寄存器地址是多少,有如何控制呢? 我們可以在Xilinx ug585http://www.xilinx.com/support/documentation/user_guides/ug585-Zynq-7000-TRM.pdf 找到
第三步,GPIO是如何在PL那邊,也就是FPGA連接到芯片的管腳上的呢? 需要參考這個設計的硬件部分http://www.digilentinc.com/Data/Products/ZEDBOARD/ZedBoard_Linux_Design.zip
zedboard上的linaro-ubuntu demo所使用的硬件工程。
其中對於zedboard的system.ucf中我們可以找到如下約束.比如,LD0那盞燈,是PS側的processing_system7_0_GPIO<7> 通過PL測,連到管腳T22上的。
############################
# #
# On-board LED's #
# #
############################
net processing_system7_0_GPIO<7> LOC = T22 | IOSTANDARD = LVCMOS33; # LD0
net processing_system7_0_GPIO<8> LOC = T21 | IOSTANDARD = LVCMOS33; # LD1
net processing_system7_0_GPIO<9> LOC = U22 | IOSTANDARD = LVCMOS33; # LD2
net processing_system7_0_GPIO<10> LOC = U21 | IOSTANDARD = LVCMOS33; # LD3
net processing_system7_0_GPIO<11> LOC = V22 | IOSTANDARD = LVCMOS33; # LD4
net processing_system7_0_GPIO<12> LOC = W22 | IOSTANDARD = LVCMOS33; # LD5
net processing_system7_0_GPIO<13> LOC = U19 | IOSTANDARD = LVCMOS33; # LD6
net processing_system7_0_GPIO<14> LOC = U14 | IOSTANDARD = LVCMOS33; # LD
第四步,T22那個管腳是如何連到真正的燈上的,需要參考ZED板的文檔:
http://zedboard.org/sites/default/files/documentations/ZedBoard_HW_UG_v1_9.pdf
2.7.3 User LEDs
The ZedBoard has eight user LEDs, LD0 – LD7. A logic high from the Zynq-7000 AP SoC I/O causes the LED to turn on. LED’s are sourced from 3.3V banks through 390Ω resistors.
Signal Name Subsection Zynq pin
LD0 PL T22
LD1 PL T21
LD2 PL U22
LD3 PL U21
LD4 PL V22
LD5 PL W22
LD6 PL U19
LD7 PL U14
LD9 PS D5 (MIO7)
我們可以看到LD0-LD7 都是PL部分的,也就是說這些都是EMIO,從PL的管腳連出。
那么,EMIO是如何在設計中定義了processing_system7_0_GPIO這個端口呢?需要看xps
2.我們可以在ports tag中找到這個標簽。
3.同時system.mhs中就會自動出現這個定義
這樣,從操作系統的腳本一直到點亮LED,我們順藤摸瓜,找到了軟件側,PS側,可擴展IO側,PL側所有相關文檔和信號,完成了最簡單的一個穿越軟件,硬件,I/O, 板卡的All Programmable 之旅。
------------------第二部分-------------------------------------------
ZYNQ中的PS部分與PL部分是獨立的,可以獨立運行,那么我們對於ZYNQ的學習研究就可以結合嵌入式學習的方法來進行。
首先是GPIO的使用,基於ZYNQ的特殊性,其GPIO分為MIO(multiuse I/O)和EMIO(extendable multiuse I/O)兩種,GPIO的相關信號被分為了4個bank,bank0與bank1為MIO信號,bank2與bank3為EMIO信號,MIO具有54根三態GPIO引腳,EMIO可以達到192根信號,64根輸入,128根輸出。由XILINX的官方文件還可以看出,EMIO是與PL部分直接相連的。
MIO與EMIO的控制寄存器主要有以下部分
控制寄存器的位寬及每個寄存器的作用均給出了詳細的解釋。
以最基本的使用GPIO點燈為例,直接使用XPS+SDK進行設計。
首先打開XPS,進行PS部分的設計,這部分設計主要是針對PS中IO,uart,interrupt(均為IP核的形式)等進行定義,相當於在一個平台上搭積木,需要哪一部分的功能就將那部分的功能加入平台中。
我們使用BSB(Base System Builder) Wizard板級開發向導新建一個項目,輸入文件名地址。
選擇ZC702這塊板卡,選項均為默認。
刪除SW與LED外設。
在system assembly view中可以看到外設,XILINX公司為我們提供了ZEDboard的初始化文件zedboard_Revc_v1或v2,可以使用這個文件對zedboard的外設進行初始化。單擊import,導入zedboard_Revc_v1即完成了ZEDboard的外設初始化。
添加GPIO的IP核AXI_General Purpose I/O,使用channel1,位寬為1,使用process_system7.0連接。在Bus Interface中可以看到添加的GPIO掛載到了processing_system7下。
在port中可以看到GPIO時鍾,外接端口的名稱等等,我們定義GPIO的名字為LED。
XPS設計中主要包含MHS(Microprocessor Hardware Specification),MSS(Microprocessor Software Specification),UCF約束文件,ELF文件等等。對UCF進行編輯,添加LED約束:
NET LED LOC = T22 | IOSTANDARD=LVCOMS33;
在project里生成Block Diagram Image即可看到所設計的PS部分。紫色部分代表主設備,通過互聯AXI總線控制從設備axi_gpio。
這樣就完成了PS部分的設計,XPS部分設計完成后需要生成比特流文件導入SDK中,在SDK中進行編程軟件設計。在SDK的MSS文件中提供了設計中主要部分的C例程,在例程中可以查找自己所需要的東西。
建立一個新的project,最好使用memory test模板,設計較為齊全。之后就可以對GPIO進行操作了。GPIO分為MIO與EMIO,SDK為我們提供了各種各樣的頭文件以簡化設計,MIO的頭文件為xgpio.h,MEIO的頭文件為xgpiops.h。
MIO的初始化過稱為首先對GPIO進行初始化(XGpio_Initialize),之后設置數據傳輸方向(XGpio_SetDataDirection),最后對GPIO進行數據的寫(XGpio_DiscreteWrite)或讀(XGpio_DiscreteRead).
EMIO的初始化過稱比MIO的略復雜,需要對EMIO的標號進行定義。例如板子上有LD9,標號為7.
具體代碼為:
MIO:
static XGpio Instance_ptr
int xstatus
xstatus=XGpio_Initialize(&Instance_ptr,DeviceId) MIO初始化
If (Xstatus != XST_status)
print(“error”)
XGpio_SetDataDirection(&Instance_ptr,1,1) 通道1,1輸入,0輸出
XGpio_DiscreteWrite(&Instance_ptr,1,1) 通道1,寫1
EMIO:
static XGpioPs Instance_ptr
XGpio_Config *Instance_Config_ptr
int xstatus
int iPinNumber = 7 7為LD9
u32 iPinDirection = 0x0 0為輸出
Instance_Config_ptr = XGpioPs_LookupConfig(DeviceId)
if (Instance_Config_ptr == NULL)
return XST_FAILURE
xstatus =XGpioPs_CfgInitialize(&Instance_ptr,Instance_Config_ptr,Instance_Config_ptr->BaseAddr)
if (xstatus != XST_SUCCESS)
print(“EMIO error”)
XGpioPs_SetDirectionPin(&Instance_ptr,iPinNumber,iPinDirection) iPinNumber為7,是LD9,iPinDirection為0,0為輸出,1為輸入
XGpioPs_SetOutputEnablePin(&Instance_ptr,iPinNumber,1) 1為enable,0為dis
XGpioPs_WirtePin(&Instance_ptr,iPinNumber,1) 1為高電平0為低電平