以下文章來源于OpenFPGA,作者碎碎思
當(dāng)你還在用C語言寫GPIO、用Verilog連LED的時(shí)候,有人已經(jīng)開始用一門“冷門但強(qiáng)大”的語言——Ada,在Zynq上點(diǎn)燈了。
1.1 設(shè)置 EMIO 允許PS控制 LED
在 Zedboard 上,LED 只能通過可編程邏輯 (PL)(FPGA)端進(jìn)行控制,因?yàn)槲锢硪_僅連接到現(xiàn)場(chǎng)可編程門陣列 (FPGA)。因此,CPU 無法直接控制 LED。CPU 指令必須通過 PL 進(jìn)行路由,這可以通過擴(kuò)展多路復(fù)用輸入/輸出 (EMIO) 來實(shí)現(xiàn)。

啟用 EMIO:
打開電路板圖。

雙擊處理系統(tǒng)。
選擇 MIO 配置、I/O 外設(shè),然后在最后打開 GPIO。勾選 EMIO GPIO,并在框中選擇 8。

處理系統(tǒng) (PS) 現(xiàn)在應(yīng)該有一個(gè)名為 GPIO_0 的新端口,它有 3 組端口,每組 8 位 (7:0)。

右鍵單擊 GPIO_0[7:0],然后單擊“Make External”。將創(chuàng)建一個(gè)名為 GPIO_O_0[7:0] 的端口。

可以通過右鍵單擊端口并選擇“External Port Properties”來重命名端口。在“External Port Properties”對(duì)話框中,輸入新名稱即可。

電路板圖中的名稱將會(huì)更新。

在源代碼中,如果尚未操作,請(qǐng)右鍵單擊 ps.bd 文件并選擇“generate HDL wrapper”。讓 Vivado 處理更新。

圖 1.1:來自 PS 的 EMIO GPIO 輸出現(xiàn)在通過 o_leds 路由到 FPGA。

1.2 寫入約束文件
現(xiàn)在需要將 LED 引腳連接到剛剛創(chuàng)建的 o_leds 端口。該端口是 FPGA 的輸入,因此也是 PS 的輸出。我們將通過編寫一個(gè)約束文件來實(shí)現(xiàn)這一點(diǎn),該文件將 Zedboard 上的引腳連接到加載到 FPGA 上的 RTL 設(shè)計(jì)中的信號(hào)。
1.2.1 約束文件語法
Vivado約束文件(.xdc)使用Tcl語法為端口等設(shè)計(jì)對(duì)象分配物理和電氣屬性。其通用格式如下:
set_property[get_ ]
對(duì)于頂層 I/O,對(duì)象類型為 get_ports,其中端口名稱必須與 RTL 設(shè)計(jì)完全匹配。例如:
set_property PACKAGE_PIN T22 [get_ports { leds_o[0]}] set_property IOSTANDARD LVCMOS33 [get_ports { leds_o[0]}]
使用 -dict 可以在單個(gè)命令中分配多個(gè)屬性:
set_property -dict { PACKAGE_PIN T22 IOSTANDARD LVCMOS33 } [ get_ports { leds_o[0]}]
花括號(hào) {} 用于對(duì)名稱進(jìn)行分組(例如總線索引 leds_o[0]),以防止 Tcl 解析問題。
要點(diǎn)
get_ports 中的端口名稱必須與頂層設(shè)計(jì)完全匹配(區(qū)分大小寫)。
PACKAGE_PIN 和 IOSTANDARD 是由 FPGA 和電路板定義的設(shè)備特定屬性。
文件中約束的順序通常并不重要。
注釋用#表示。
編寫我們的 tcl 代碼:
set_property -dict { PACKAGE_PIN T22 IOSTANDARD LVCMOS33 } [ get_ports { o_leds[0]}];
set_property -dict { PACKAGE_PIN T21 IOSTANDARD LVCMOS33 } [ get_ports { o_leds[1]}];
set_property -dict { PACKAGE_PIN U22 IOSTANDARD LVCMOS33 } [ get_ports { o_leds[2]}];
set_property -dict { PACKAGE_PIN U21 IOSTANDARD LVCMOS33 } [ get_ports { o_leds[3]}];
set_property -dict { PACKAGE_PIN V22 IOSTANDARD LVCMOS33 } [ get_ports { o_leds[4]}];
set_property -dict { PACKAGE_PIN W22 IOSTANDARD LVCMOS33 } [ get_ports { o_leds[5]}];
set_property -dict { PACKAGE_PIN U19 IOSTANDARD LVCMOS33 } [ get_ports { o_leds[6]}];
set_property -dict { PACKAGE_PIN U14 IOSTANDARD LVCMOS33 } [ get_ports { o_leds[7]}];
1.3 用于配置 GPIO 的 Ada 代碼
現(xiàn)在我們需要將 PS GPIO EMIO 引腳配置為輸出,方法是寫入 GPIO 控制器的方向寄存器和輸出使能寄存器,然后寫入數(shù)據(jù)寄存器來驅(qū)動(dòng)這些位為高或低。
Zynq 7000 SoC 技術(shù)參考手冊(cè) (UG585)(https://docs.amd.com/r/en-US/ug585-zynq-7000-SoC-TRM/Introduction?tocId=Hf6C7Oo5ABvv2hkWRoiihQ)中關(guān)于通用 I/O 部分的引言如下:

最后一句尤為重要,因?yàn)樗嬖V我們必須使用的 GPIO 控制寄存器和狀態(tài)寄存器的基地址。此外,EMIO GPIO 連接到 bank 2 和 bank 3。

GPIO 控制寄存器和狀態(tài)寄存器映射到基地址的內(nèi)存中:
0xE000_A000
從我們的電路板圖可知,我們正在使用 EMIO GPIO_O(7:0) 輸出端口,即 EMIO 的第 7 位到第 0 位,我們通過 o_leds 端口將其連接到 PL。
因此,要向 LED 發(fā)送數(shù)據(jù),我們必須在寄存器映射(位于UG585 的寄存器摘要部分)中找到正確的控制寄存器來控制 GPIO_O。

由于 EMIO[0:31] 是 bank 2,我們發(fā)現(xiàn)正確的寄存器是:

因此,在軟件開發(fā)中我們必須:
設(shè)置方向輸出 => 設(shè)置 DIRM_2 中的第 0 位
啟用輸出 => 設(shè)置 OEN_2 的第 0 位
寫入值 => 設(shè)置/清除 DATA_2 中的第 0 位
注:應(yīng)該使用MASK_DATA_LSW寄存器,因?yàn)樗试S選擇要寫入的特定位。數(shù)據(jù)寄存器的所有32位都是一次性寫入的。
1.3.1 Ada 代碼 - zedboard_emio_gpio.ads
規(guī)范文件用于設(shè)置 EMIO GPIO 的基地址和偏移量。
由于這是內(nèi)存映射,我們不使用訪問類型,訪問類型在某些方面是 Ada 版本的指針,據(jù)我所知,通常應(yīng)該避免使用。
相反,我們使用所謂的表示子句,據(jù)我理解,它允許將變量(或枚舉類型)與固定地址的特定內(nèi)存位置或硬件寄存器關(guān)聯(lián)起來,從而使程序能夠直接將變量映射到物理內(nèi)存(例如內(nèi)存映射 I/O 寄存器),而無需使用傳統(tǒng)的指針。
.ads 代碼為:
-- Package to control the EMIO GPIO Bank 2 of the Zedboard . -- We are only able to control 8 LEDs . -- As this is memory mapping , we use address binding via representation -- clauses -- (as opposed to Access types ). -- Access Types , essentially pointers , are typically only utilised when the -- address is dynamic (not known at compile time ). -- I extensively use comments as I am learning as I go! -- I intentionally qualify everything to understandwhichfunctionis apart -- ofwhichpackage . with System ; -- A top - level Ada package with System . Storage_Elements ; -- A child package of Storage . with Interfaces ; -- defines types with exact sizes package zedboard_emio_gpio is use System . Storage_Elements ; -- without get a compile error : -- possible missing with /use of System . -- Storage_Elements . -- It is due to use of the + ,whichneeds the -- use clause I believe procedure Initialise ; procedure Set_LEDs ( Value : Interfaces . Unsigned_8 ) ; private -- The following is all private to hide the Hardware details . -- Declare the GPIO Control register base address . -- To_Address is atypeconversion as --" Address "is a particulartypeand we have -- a hex literal that has to be converted . -- We use constant as the values are fixed by Hardware . GPIO_Control_Reg_Base : constant System . Address := System . Storage_Elements . To_Address (16# E000_A000 #) ; Data_2_Addr : constant System . Address := GPIO_Control_Reg_Base + System . Storage_Elements . Storage_Offset (16#48#) ; DIRM_2_Addr : constant System . Address := GPIO_Control_Reg_Base + System . Storage_Elements . Storage_Offset (16#284#) ; OEN_2_Addr : constant System . Address := GPIO_Control_Reg_Base + System . Storage_Elements . Storage_Offset (16#288#) ; -- Now the representation clauses : Data_2 : Interfaces . Unsigned_32 ; forData_2' Address use Data_2_Addr ; pragma Volatile ( Data_2 ) ; -- Need this according to chat gpt -- suppress any optimizations that would interfere -- with the correct reading of the volatile variables . DIRM_2 : Interfaces . Unsigned_32 ; for DIRM_2 'Address use DIRM_2_Addr ; pragma Volatile ( DIRM_2 ) ; OEN_2 : Interfaces . Unsigned_32 ; forOEN_2' Address use OEN_2_Addr ; pragma Volatile ( OEN_2 ) ; end zedboard_emio_gpio ;
1.3.2 Ada 代碼 - zedboard_emio_gpio.adb
主體文件 body.adb 的內(nèi)容如下:
with Interfaces ; use Interfaces ; -- need the use clause to use or , + , and -- operations etc. -- compiler gives error otherwise . package body zedboard_emio_gpio is procedure Initialise is begin DIRM_2 := DIRM_2 or 16# FF #; -- Set bottom 8 bits to 1 via bitwise or -- operation . -- 1 indicates output . -- Wedothis to notsetor change any other -- bits . -- We could have used the MASK_DATA_LSW also . OEN_2 := OEN_2 or 16# FF #; -- 1 indicates output is enabled end Initialise ; procedure Set_LEDs ( Value : Interfaces . Unsigned_8 ) is begin Data_2 := ( Data_2 and not 16# FF #) or Interfaces . Unsigned_32 ( Value ) ; end Set_LEDs ; -- Interfaces . Unsigned_32 ( Value ) -- is atypeconversion -- Converts 8 -bit Value to 32 bits -- unsigned . end zedboard_emio_gpio ;
1.3.3 Ada 代碼 - main.adb
在 main.adb 文件中,我們導(dǎo)入了 Ada.Real_time 包,這允許我們使用 Seconds 函數(shù)和 Clock 函數(shù)來設(shè)置 LED 燈亮起和熄滅之間的延遲。
十六進(jìn)制 AA (0xAA) 表示 LED LD1、LD3、LD5 和 LD7 將交替亮滅,而其他 LED 則始終處于關(guān)閉狀態(tài)。
with zedboard_emio_gpio;
with Interfaces;
with Ada.Real_Time; use Ada.Real_Time;
procedure Main is
D : Time_Span := Seconds (5); -- D is of Type Time_Span, Seconds is afunction.
-- Nanoseconds also existsforexample
Next : Time := Clock + D; -- What is dif between clock and clock time?
begin
zedboard_emio_gpio.Initialise;
delay until Next;
Next := Next + D;
loop
zedboard_emio_gpio.Set_LEDs (16#AA#);
delay until Next;
Next := Next + D;
zedboard_emio_gpio.Set_LEDs (16#00#);
delay until Next;
Next := Next + D;
end loop;
end Main;
1.3.4 Ada 代碼 - blink_led.gpr
最后提供了 .gpr 文件。項(xiàng)目最初沒有使用 Alire。為了將其轉(zhuǎn)換為 alr 項(xiàng)目,我在終端中切換到項(xiàng)目目錄并運(yùn)行了以下命令:
alr init --bin blink_led --in-place
這將創(chuàng)建一個(gè).gpr文件。
然后不得不稍微更新一下.gpr文件,因?yàn)樗褂昧隋e(cuò)誤的main文件名(把它改成了“main.adb”)。
其次,源目錄沒有指向main.adb 文件所在的位置(根目錄)。這個(gè)問題通過在 Source_Dirs 中設(shè)置“.”來解決。

.gpr 代碼如下:
project Blink_Led is
forRuntime ("Ada") use"embedded-zynq7000";
forTarget use"arm-eabi";
forSource_Dirs use (".","mng_pl_ps/","config/");
forObject_Dir use"obj/";
forExec_Dir use"bin";
forMain use ("main.adb");
end Blink_Led;
手動(dòng)添加的
forRuntime ("Ada") use"embedded-zynq7000";
forTarget use"arm-eabi";
這是必需的?!癴or Runtime”命令告訴Gnat編譯器要編譯的CPU/架構(gòu),在本例中是zynq700。還有其他選項(xiàng),例如light-zynq7000,但這不包含Real_Time庫,因此Alire會(huì)報(bào)錯(cuò)。
“for Target”命令告訴編譯器要為哪個(gè)指令集/工具鏈生成代碼,在本例中是使用EABI(嵌入式應(yīng)用二進(jìn)制接口)的ARM指令集/工具鏈。這確保編譯后的輸出與Zynq上的ARM Cortex-A9處理器兼容。
以上內(nèi)容來自此鏈接(https://docs.adacore.com/gnat_ugx-docs/html/gnat_ugx/gnat_ugx/arm-elf_topics_and_tutorial.html)。
完成所有這些步驟后,運(yùn)行“alr build”命令。
這應(yīng)該會(huì)在名為 bin 的文件夾中生成一個(gè) .elf 文件。它可能就叫 main,把它重命名是為了明確文件擴(kuò)展名,即 main.elf。
1.4 Vitis:軟件方面的設(shè)置
打開Vitis
在 C 盤或其他路徑下創(chuàng)建一個(gè)名為 workspace 的文件夾。
打開 Vitis。選擇“打開工作區(qū)”。
選擇文件→新建組件→平臺(tái)。
給它起個(gè)名字。
選擇硬件設(shè)計(jì),然后瀏覽并找到 .tcl 文件。
使用獨(dú)立操作系統(tǒng)。

選擇“生成啟動(dòng)工件”。
完成并搭建平臺(tái)。
應(yīng)該用Vitis搭建一個(gè)平臺(tái)。

完成上述步驟后,即可通過選擇 Vitis->Program Device 將比特流下載到 Zedboard。

現(xiàn)在需要下載 main.elf 文件以便在處理系統(tǒng)上運(yùn)行。使用 Xilinx 軟件命令行工具 (XSCT) 完成了此操作。在終端中執(zhí)行以下操作:
打開終端并啟動(dòng) XSCT:
xsct
通過 JTAG 連接到目標(biāo)板:
connect
列出可用目標(biāo):
targets
應(yīng)該看到類似如下的輸出:
1 APU 2 ARM Cortex?A9 MPCore#0( Vec tor Catch ) 3 ARM Cortex?A9 MPCore#1( Running ) 4 xc7z020
選擇第一個(gè) ARM 內(nèi)核(CPU 0):
targets 2
重置處理器以確保其處于清潔狀態(tài):
rst -processor
將編譯后的 Ada 可執(zhí)行文件(.elf)下載到內(nèi)存中,例如:
dow"C:/ path /to/ your / project /bin/ main.elf "
開始執(zhí)行:
con
程序運(yùn)行后:
ARM Cortex-A9 開始執(zhí)行 Ada 應(yīng)用程序
ARM Cortex-A9 開始執(zhí)行 Ada 應(yīng)用程序
主過程通過 GNAT 運(yùn)行時(shí)調(diào)用。
通過 EMIO 連接的 LED(LD1、LD3、LD5、LD7)應(yīng)根據(jù)已實(shí)現(xiàn)的邏輯開始閃爍。
項(xiàng)目完成。
總結(jié)
這不是“用Ada點(diǎn)燈”,而是“用另一種語言重新理解Zynq”
-
led
+關(guān)注
關(guān)注
244文章
24757瀏覽量
693050 -
C語言
+關(guān)注
關(guān)注
183文章
7649瀏覽量
146314 -
GPIO
+關(guān)注
關(guān)注
16文章
1336瀏覽量
56539 -
Zynq
+關(guān)注
關(guān)注
10文章
636瀏覽量
49615
原文標(biāo)題:不用C、不用Verilog!用Ada點(diǎn)亮LED,這才是Zynq的“另一種打開方式”
文章出處:【微信號(hào):HXSLH1010101010,微信公眾號(hào):FPGA技術(shù)江湖】歡迎添加關(guān)注!文章轉(zhuǎn)載請(qǐng)注明出處。
發(fā)布評(píng)論請(qǐng)先 登錄
為什么匯編語言點(diǎn)亮LED燈就不需要關(guān)看門狗?
使用C語言的方式來點(diǎn)亮LED的問題
如何利用寄存器在stm32開發(fā)板上點(diǎn)亮一個(gè)LED燈呢
在STM8上點(diǎn)亮LED的大致步驟有哪些
如何通過Android應(yīng)用程序在NodeMCU V2上點(diǎn)亮LED ?
點(diǎn)亮一顆LED位操作c語言
51單片機(jī)的兩種方法點(diǎn)亮LED的C語言程序免費(fèi)下載
如何使用AliOS Things在Developer Kit上點(diǎn)亮一個(gè)LED詳細(xì)教程
使用單片機(jī)點(diǎn)亮多個(gè)LED燈的方法C語言程序?qū)嵗赓M(fèi)下載
在rv-star板子上點(diǎn)LED燈實(shí)驗(yàn)
使用Ada語言在Zynq上點(diǎn)亮LED
評(píng)論