- Vivado 2013.2(install from DVD) with MATLAB R2012b
(MATLAB 版本 2011a / 2013b / 2014a / 2014b 不支援)- ISE(install from DVD)
step 1.
下載 board files
step 2.
解壓縮後將 new > board_files 複製至 Vivado 安裝路徑 > data > boards 下
step 1.
連接 UART 跟 PC
step 2.
連接 PROG 跟 PC
step 3.
把 PIN 腳 J18 設置在 2V5 的位置
step 4.
拔掉 SD card、連接電源線
step 5.
確保 jumper 設置如下
Jumper | SIG connected to… |
---|---|
MIO6 | GND |
MIO5 | 3V3 |
MIO4 | 3V3 |
MIO3 | GND |
MIO2 | GND |
step 6.
省略
step 7.
(Vivado)
新建檔案 > 選擇檔案路徑(建議在 Xilinx 的目錄之下) > 選擇 RTL Project > target language 選擇 VHDL > (點選 Next 直到要選擇板子的地方) > 選擇 Boards 再選擇 Zynq 7000 ZC702 Evaluation Board(xc7z020clg484-1) > 點選 Finish 即建置完成
step 8.
介面簡介
step 9.
Create Block Design(左側 Flow Navigator 的 panel 下)
step 10.
點選 Add IP(在 Diagram 裡面的 + 圖示) 並選擇 ZYNQ7,之後點選 "Run Block Automation(亮綠色區塊)",保留預設值(Apply Board Preset 已預設好),直接點選 OK。
可以看到圖中 DDR 和 FIXED_IO 處有所變化
Run Block Automation 前
Run Block Automation 後
(DDR & Fixed IO connections are made automatically)
雙擊 ZYNQ7,之後會產生 Programmable Logic(PL) 圖,點選 "32b GP AXI Master Ports" 後取消勾選 "M AXI GP0 interface",點選 OK 即完成
點選 Validate Design☑ 圖示,即會顯示 Validation successful 的視窗
點選 File > Save Block Design(Ctrl + S)
step 11.
省略
step 12.
右擊 Sources 裡 Design Sources 下的 .bd 檔案,然後選取 "Create HDL Wrapper",點擊 OK 即完成
step 13.
左側 Flow Navigator 的 panel 下,點擊 "Generate Bitstream",保留預設值,直接點選 OK 即完成(過程需等待大約三分鐘)
step 14.
因為是在 Zedboard 上實作,有很多的設定已經預設完成,所以不需做太多的更動
step 15.
省略
step 16.
點選 File > Export > Export Hardware…,照預設點選 Next 即完成。此步驟會將 .xsa 檔匯出
開啟 vitis(可從上方工具列 Tools > Launch Vitis IDE 打開)
(Vitis)
Create new platform project
點選 Browse 開啟剛才 export 的 .xsa 檔
開啟 .xsa 檔,會看到這塊板子所有的 Base Address 和 High Address
step 17.
創建 BSP(新版 SDK 已自動建立,詳細位置如圖,但要記得按下槌子🔨 重新 build 一下 project!)
step 18.
點選 File > New Application Project…,照預設點選 Next 直到要選擇 Templates 並選擇 "Hello World" 即完成
helloworld.c 檔案會在左側 Explorer panel 中 Exercise_01_stystem > Exercise_01 > src 裡
step 19.
確認開發板已連接電源(開發板上的 POWER LED 會亮)
step 20.
點選上工具列 Window > Show view… > Vitis > Vitis Serial Terminal,則 Terminal panel 即出現在畫面下方
接著點擊在畫面右下角的 + 圖示,進行 serial port 連接參數設定(需注意設定完畢是否有出現連接成功的訊息)
step 21.
step 22.
待 Vitis Serial Terminal 出現 Hello World 等訊息即完成
此次 Exercise 我們將探索使用軟體驅動程序來控制 Zynq 設備上的一個輸出腳位,以驅動一個 LED。我們將使用 "LD9",它連接到 54 個 MIO 腳位中的腳位 7(PS_MIO7),這些腳位可以在 Zynq 設備的 "PS" 側存取
(Vitis)
點選 File > New Application Project…,照預設點選 Next 直到要選擇 Templates 並選擇 "Hello World"(這個 Template 呼叫了 init_platform 函式,建議先選擇此 Template 之後再直接用它來進行修改為以下程式碼)
在 Zynq 的 PS 中控制 GPIO 的是 XGpioPs 結構(在本次 Exercise 中宣告為 my_Gpio),其內部有四個變數
第一個是 "GpioConfig",其資料型態為 "XGpioPs_Config",它實際上是另一個結構(結構內部可以有結構!)
其餘的三個變數都是不同的資料型態,詳細部分可以先不深入探討!
如果手動在結構內部配置所有東西會是一項複雜的任務,因此 Xilinx 提供了一個 C 語言的 "XGpioPs_CfgInitialize" 函式來幫助我們完成這項工作,當結構被宣告時,其中的所有變數都是未初始化的,而它會自動為我們配置所有東西。
XGpioPs_CfgInitialize 函式
XGpioPs_CfgInitialize(XGpioPs *InstancePtr, const XGpioPs_Config *ConfigPtr, u32 EffectiveAddr)
函式需要三個輸入:
函式的輸出是一個狀態值,可以讓我們知道初始化是否成功(0: Success)
運作流程如下圖所示(錯誤示範)
但此為 錯誤示範,事實上 GPIO_Config 結構尚未初始化,所以我們需要利用另外一個函式來達到初始化的工作
要呼叫函式,我們只需放入 Device_ID,而實際上 Device_ID 已被定義在 xparameters.h 中
正確的運作流程圖如下圖所示
(Vitis)
建立名為 Exercise_03 的 Application Project
複製 exercise_02.c 為 exercise_03.c
右擊左側 Explorer panel 中的專案(Exercise_03),選取 Build Project
接著再次右擊專案,選取 Debug As… > Launch Hardware(Single Application Debug(GDB))
成功完成後應該呈現如下圖(應該出現左上角的 Debug panel、被綠色標註的 init_platform())
使用 "Step Into" 按鈕單步執行程式
直到進入 init_platform 函式內
在 "init_platform" 內部,單擊 "Step Into" 繼續深入,或使用 "Step Over" 執行程式碼的下一行
在 exercise_03.c 中,找到執行 "XGpioPs_SetDirectionPin" 函式的那行,並在該行左側雙擊設置中斷點
點擊綠色的 "Resume" 按鈕,讓 Debugger 執行程式直到設置中斷點的位置
查看右上角的 "Variables" panel 查看函式相對應的 local variables。特別注意,其中應該會有一個 “Status” 變數,其值應為 0 以表示 "XGpioPs_CfgInitialize" 函式順利完成並無發生錯誤
在 exercise_03.c 中,將鼠標停在變數或結構的名稱上可以看到更詳細的內容
可以試試看在 for 迴圈的地方增加中斷點,接著使用 "Resume"、"Step Into"、"Step Over" 和 "Step Return" 按鈕,並於 "Variables" panel 中手動更改循環變數的值,觀察循環計數變數的改變
在之前的 Exercise 中,我們僅限於 Zynq 設備的 processing system(PS) 部分,但 Zynq 不僅是一個處理器,它還提供了大量使用者可配置的 FPGA programmable logic(PL)。
PL 有多種使用模式:
重新打開 Vivado,在 Sources 中,雙擊在 Exercise 1 中創建的 .bd 檔(design_1.bd)
可以看到 Design 裡只包含 processing_system7_0,即 Zynq 設備的 PS 部分的 IP。接下來我們將在 Zynq 設備的 PL 部分中擴展此功能(添加功能來控制 8 個 LED、8 個 DIP 開關和 1 個 PMOD 擴展插座)
雙擊 ZYNQ7,之後會產生 Programmable Logic(PL) 圖,點選 "32b GP AXI Master Ports" 後勾選 “M AXI GP0 interface,接著在視窗左側(Page Navigator) 選擇 "Interrupts",將 "Fabric Interrupts" 勾選並展開,再展開 "PL-PS Interrupt Ports",然後將 "IRQ_F2P[15:0]" 打勾,最後點選 OK 即完成
在上方欄位中,點擊 "Add IP" 圖示。在搜索框中輸入 "gpio" 並雙擊 "AXI GPIO",接著搜索 "spi",再雙擊 "AXI Quad SPI"
雙擊 AXI GPIO,選擇 "IP Configuration"(注意周邊設備有兩個通道 "GPIO" 和 "GPIO 2",這裡只需要對 "GPIO" 進行設置),將 GPIO Width 設為 16 位(8 位用於 DIP 開關,8 位用於 LED)
為 "AXI QUAD SPI" 周邊設備進行配置,需取消勾選 Enable STARTUP Primitive
使用上方綠色條中的 "Designer Assistance",執行 "connection automation" ,並從對話框中選擇 "/axi_gpio_0/S_AXI"
之後將三個項目皆設置為 "Auto" 並點擊 "確定"
重複使用 "Designer Assistance" 對 "/axi_quad_spi_0/AXI_LITE" 進行設置
執行 "Designer assistance",選擇 "/axi_gpio_0/GPIO",Options 裡的 Select Board Part Interface 選擇 "custom",點擊 "確定"
執行 "Designer assistance",並從對話框中選擇 "/axi_quad_spi_0/SPI_0"
SPI 周邊設備需要一個 "reference clock connection",這必須手動連接。使用滑鼠從 SPI 區塊上的 "ext_SPI_clk" 畫一條連接到SPI區塊上的 "s_axi_aclk" 線
我們需要在 interrupt enabled mode 下使用 SPI controller,因此我們必須將來自周邊設備的中斷輸出連接到 interrupt controller。使用滑鼠從 SPI 區塊上的 "IP2INTC_Irpt" 畫一條線到 "Zynq7 Processing System" 區塊上的 "IRQ_F2P"
點選左側 Flow Navigator panel 下 RTL ANALYSIS > Open Elaborated Design
雙擊 Sources 裡 Design Sources 中的 .vhd 檔(design_1_wrapper.vhd),打開後搜尋 "spi_rtl_ss_iobuf_0",並於 spi_rtl_ss_o_0(0) 前加上 not,如下圖所示
點選左側 Flow Navigator panel 下 Project Manager > Add Sources,接著選擇 Add or create constraints,然後 Create File,File name 設為 my_constraints,點擊 OK 和 Finish 後即完成
打開 Sources 裡的 Constraints 下的 my_constraints.xdc 檔,接著把以下內容複製進去即可
點選左側 Flow Navigator panel 下 SYNTHESIS > Run Synthesis,接著 Run implementation
點選左側 Flow Navigator panel 下 PROGRAM AND DEBUG > Generate Bitstream
點選 File > Export > Export Hardware…,接著選取 Include bitstream ,(建議將名稱改為 design_1_wrapper_bitstream),最後點選 Next 即完成。此步驟會將 .xsa 檔匯出
現在已經有了一個新的硬體平台,可以根據它來開發應用程式
本練習的目標是學習如何讓處理器從 user input(這次練習中為一些 DIP 開關)讀取值,進而控制輸出(這次練習中為一些 LED 燈)。
8 個 DIP 開關將連接到 GPIO 的 LSB(位元 7~0),LED 燈則連接到 GPIO 的 MSB(位元 15~8)。處理器會根據 DIP 開關的狀態按位輸出到 LED 燈。
(Vitis)
Component | Driver |
---|---|
axi_gpio_0 | gpio |
axi_quad_spi_0 | spi |
程式碼:
執行步驟:
右擊 Exercise_05 > Build Project
右擊 Exercise_05_system > Program Device(按下 Program 按鈕前建議先點選 Device > Select 按鈕 進行確認,查看是否有偵測到開發版,這部分常因偵測失敗報錯)
右擊 Exercise_05 > Run As… > Launch Hardware(Single Application Debug(GDB))
接著便可以對 DIP 開關進行調整,控制 LED 的亮暗
程式碼中特別需要注意的部分:
因為本次主要是對 PL 區塊進行控制,在之前 exercise 程式碼中為 "XGpioPs" 的部分皆改成了 "XGpio",例如:
而 XGpio 相關函式之詳細內容皆可於 "xgpio.h" 檔案中找到。
在 XGpioPs_SetDirection 函式中,0 表示 input、1 表示 output;然而在 XGpio 對應的 XGpio_SetDataDirection 函式中,0 表示的是 output、1 則表示 input。
XGpioPs_SetDirection
void XGpioPs_SetDirection(const XGpioPs *InstancePtr, u8 Bank, u32 Direction);
XGpio_SetDataDirection
void XGpio_SetDataDirection(XGpio *InstancePtr, unsigned Channel, u32 DirectionMask);
本練習將學習如何對記憶體進行讀取與寫入。
記憶體除了在嵌入式系統中被用以儲存軟體之外;在 FPGA 設計中,使用者往往會使用一塊記憶體作為嵌入式處理器系統(embedded processor system) 和自定義硬體之間的介面,像是 Xilinx 的 dual port BlockRAMs 就經常以這種方式使用。
另外,Zynq 中的 On Chip Memory (OCM) 使用方式和 dual port BlockRAMs 相似,而外部 (e.g. DDR) 記憶體的工作方式也大致相同,唯一的區別在於外部記憶體會由記憶體控制器來解釋來自處理器的請求並生成一個交易,該交易使用 DIMM 或設備所用的協議訊號來接收和回應存取請求。
(Vitis)
result1 = 0x78563412, result2 = 0x54329876
程式碼:
執行結果:
result1 = 0xdeadbeef8c34ffab, result2 = 0xaaaa6587fe311209
word1 = 0x8c34ffab
word2 = 0xdeadbeef
word3 = 0x0020c000
★ 位址 0xE000A064 的值(word3) 呈現了 push button PB1 和 PB2 的狀態。PB1 的狀態由 bit 18 表示;PB2 的狀態由 bit 19 表示:
PB2 | PB1 | word3(binary) | word3(heximal) |
---|---|---|---|
0 | 0 | 10 0 0 00 1100 0000 0000 0000 | 0x20c000 |
0 | 1 | 10 0 1 00 1100 0000 0000 0000 | 0x24c000 |
1 | 0 | 10 1 0 00 1100 0000 0000 0000 | 0x28c000 |
1 | 1 | 10 1 1 00 1100 0000 0000 0000 | 0x2cc000 |
透過迴圈的方式來控制處理器速度的延遲
透過定時器的方式來控制處理器速度的延遲
程式碼:
中斷可以由許多不同的周邊裝置產生。在這次練習中,我們將從上一個練習中使用的 Snoop Control Unit Timer 周邊裝置產生中斷。
Zynq 的文件指出,每當定時器計數到零時,就會產生一個中斷,所以我們將利用這一功能來進行本次的練習。
為了使用中斷,我們需要使用更多的驅動函式來啟用定時器上的中斷功能,啟用 ARM 處理器上的中斷,並設置中斷控制器。
在 ARM Cortex-A9 這樣的處理器中,所有中斷都是由通用中斷控制器(General Interrupt Controller, GIC) 管理並連接的。
軟體流程的中斷
Main application:
這代表正在運行的主程序,它在中斷事件發生時正在處理的任務。
Xilinx supplied interrupt handler:
當中斷發生時,系統首先跳轉到由 Xilinx 提供的一個通用中斷處理器。這個處理器負責保存處理器狀態,包括執行階段和內部暫存器的內容。
Your custom interrupt handler:
Xilinx 的中斷處理器會調用你為特定中斷事件編寫的自定義中斷處理器。在這裡,你可以定義當特定中斷發生時需要執行的任務。
Any other functions you call from your interrupt handler:
你的中斷處理器可能會呼叫其他函式來完成處理中斷所需的特定工作。
Return to Xillinx supplied interrupt handler:
一旦你的自定義中斷處理器和任何其他相關函式執行完畢,控制權會返回到 Xilinx 的中斷處理器,以便完成任何剩餘的清理工作。
Continue with main application:
當所有中斷相關的任務完成後,處理器的狀態會恢復,然後主應用程式將繼續執行中斷發生之前的任務。
Block diagram
程式碼:
在 Exercise 4 中我們對 PL 增加了兩個外部設備,一個是 GPIO、一個則是 SPI。GPIO 的部份我們在 Exercise 5 中已經練習使用了,本次練習將要學習如何使用 SPI 並用它來控制外部 PMOD 子板上的設備。
SPI (Serial Peripheral Interface)
一種序列周邊介面,允許 master 和 slave 使用四條線進行通訊:
可分為單工/半雙工/全雙工
實際上,SPI 並沒有「讀」和「寫」的具體概念,僅僅以「傳輸」的概念運行
PMOD(Peripheral MODule)
--
本次 Exercise 我們將使用 Maxim 的一個名為 "MAX31723PMB1" 的溫度感測 PMOD 板
MAX31723 有很多功能,但我們將僅使用其三個內部暫存器(TEMPMSB register、TEMPLSB register、configuration register)。它的每個暫存器都有一個地址,可以通過 SPI 連接發送兩個 byte 進行讀寫
write: 第一個 byte 包含要寫入的暫存器地址,第二個 byte 包含要寫入暫存器的值
read: 第一個 byte 包含要讀的暫存器地址,而第二個 byte 是一個 dummy byte。MAX31723 將對 master 在 byte 1 中提供的地址進行讀取,並忽略第二個進來的 byte;作為回應,當它收到 byte 1 時,將輸出一個 dummy value,並在 byte 2 發送暫存器中的資料
在全雙工的 SPI 中,為了保持通訊的同步,需要確保在每個資料傳輸週期中 master 和 slave 都能發送和接收相同數量的 byte,確保通訊的平衡和正確性。
我們將從 MAX31723 讀取兩個暫存器,總共需要傳輸 4 個 byte(2 個地址 byte + 2 個 dummy byte)
使用這兩個暫存器的資料,可以計算出當前的溫度。TEMPMSB 的 byte 以二進制補數形式表示溫度的整數部分(1 ℃、2 ℃ … 100 ℃ 等),而 TEMPLSB 表示溫度的小數部分(二進制字中的 bits 表示 1/2 ℃、1/4 ℃、1/8 ℃ 等)。
在讀取溫度暫存器之前,必須使用地址為 0x80 的配置暫存器來啟用設備。我們將寫入值 0x00,清除配置暫存器中的所有 bits
我們在前一個之前添加到 .xdc 文件中的所有腳位 constraints 足以將 SPI 連接路由到 ZedBoard 上的 PMOD 插座 "JA1"(位於 DIP 開關旁邊)。所以只需將 MAX31723PMB1 模組插入 JA1 連接器的上排接腳,且模組的 jumper 朝上即可
我們將會在中斷模式下使用這個裝置
程式碼:
在程式碼中:
因為 SPI 可能因為多種原因(傳輸完成、錯誤條件等)生成中斷,所以 interrupt handler 必須在採取任何操作之前檢查導致中斷的事件。因此,這個練習中的 interrupt handler 略微先進,它必須先進行一些檢查:如果中斷條件是期望的結果(傳輸完成),則 handler 會將 global variable 設置回原始值,main() 就可以繼續運行了
使用了 global variable 實現 SPI 的兩個 buffer: 1 個 read buffer 和 1 個 write buffer。然後,這些 buffer 被傳遞給 SPI drivers,在 SPI 傳輸期間,它們會一個 byte、一個 byte 地被發送/接收
xilffs 是 Xilinx 提供用於嵌入式系統的文件系統庫,它是 Xilinx 軟件開發工具箱(SDK)的一部分,這些系統庫實現了 FAT 文件系統(File Allocation Table,文件分配表),使得 Xilinx 的處理器能夠讀寫存儲在 FAT 格式的存儲裝置(如 SD 卡、USB 驅動器等)上的文件。
FSBL是用於在嵌入式系統中啟動過程的第一階段的關鍵軟件組件,特別是在使用像 Zynq-7000 系列這樣的可編程系統晶片(SoC)時,它在系統上電或重置後首先被執行,負責執行一系列初始化操作,為系統的後續啟動階段做準備。
Step 1. Creating the FSBL (First Stage Boot Loader)
Step 2. 先找到之前練習所建立的 design_1_wrapper_bitstream,找到 platform.spr 並將它開啟,點擊ps7_cortexa9_0 > standalone_ps7_cortexa9_0 > Board Support Package(BSP),再點擊 Modify BSP Settings,之後將 Supported Libraries 裡的 xilffs 打勾
Step 3. File > New > Application Project > 點擊 Next > 選擇design_1_wrapper_bitstream 後點擊 Next > 將 Application Project Name 命名為 FSBL 後點擊 Next > 再點擊 Next > 之後再Templates的頁面選擇最底下的 Zynq FSBL > 點擊 Finish
Step 4. 桌面新增空白資料夾(最後會作為儲存本次練習的位置)
Step 5. Create Zynq Boot Image
Step 6. 選擇 Create new BIF file,在 Output BIF file path 開啟剛剛新建資料夾的位置(flash_images)
Step 7. 接下來在下方的 Boot image partitions 會需要新增三個路徑
Step 8. 在 Output path 一樣開啟剛剛新建資料夾的位置(flash_images),並使用檔名 "boot.bin",之後點擊 Create Image
Step 9. 將 SD 卡插入電腦,並將桌面 flash_images 資料夾裡 boot.bin 複製一分到 SD 卡裡
Step 10. 將 SD 卡插入 zedboard 並觀察,會發現 application 已經在板子上運行了