天天看點

ZYNQ 筆記(一):開發流程

作者:異步時刻

概 述

ZYNQ 的工程設計大體上可以分為 對硬體邏輯系統的設計 和 對 CPU 軟體程式的設計

  • 硬體邏輯系統設計:搭建一個滿足使用者需求的硬體環境,通過 Vivado 實作
  • CPU 程式設計:通過使用者程式控制 CPU 工作,使整個系統達到預定的效果,該部分通過 Vitis 實作

兩者間的關系及具體設計步驟如下圖所示

ZYNQ 筆記(一):開發流程

xsa 檔案為硬體資源描述檔案,可以通過生成比特流或生成子產品設計産生

  • 涉及到 PL 端的設計:導出 xsa 檔案時需要包含比特流,是以通常使用 生成比特流(bit stream) 的方式産生 xsa 檔案
  • 純 PS 端的設計:導出時不需要比特流,是以兩種方式皆可

接下來以按鍵控制 led 工程為例介紹 ZYNQ 開發的基本流程,本設計實作的功能如下

  1. 按下 PS 端按鍵 SW3,PL 端小燈 LED1 亮起,松開則小燈熄滅
  2. 按下 PL 端按鍵 SW1,PS 端小燈 LED5 亮起,松開則小燈熄滅

硬體邏輯系統設計

建立 Vivado 工程

輕按兩下打開 Vivado,點選 Create Project

ZYNQ 筆記(一):開發流程

填寫工程名稱并選擇儲存路徑,勾選 Create project subdirectory 則項目儲存在 {Project location}/{Project name} 檔案夾下,點選 Next

ZYNQ 筆記(一):開發流程

選擇 RTL Project,點選 Next

ZYNQ 筆記(一):開發流程

根據實際情況選擇晶片,筆者所用開發闆型号為 MZ7035FD,晶片型号為 xc7z035ffg676-2,點選 Next

ZYNQ 筆記(一):開發流程

點選 Finish,項目建立完成

ZYNQ 筆記(一):開發流程

建立子產品設計

點選 Create Block Design 建立子產品設計

ZYNQ 筆記(一):開發流程

在彈出的視窗中修改設計命名(注意不能包含中文及空格),點選 OK 完成建立

ZYNQ 筆記(一):開發流程

可以看到 Design 欄出現了我們剛建立好的 system 子產品設計

ZYNQ 筆記(一):開發流程

建構硬體系統

此時我們的設計内沒有任何内容,需要點選 + 為設計添加 IP 核

ZYNQ 筆記(一):開發流程

在彈出的搜尋欄中輸入 zynq,可以看到下方出現了 ZYNQ7 Processing System,輕按兩下該 IP 核添加

ZYNQ 筆記(一):開發流程
ZYNQ 筆記(一):開發流程

ZYNQ IP 核的配置通常有兩個必需配置的部分:使能外設和配置 DDR 型号。輕按兩下 ZYNQ IP 核即可打開配置界面,如下圖所示

ZYNQ 筆記(一):開發流程

右側欄為 ZYNQ 的圖形化向導界面,綠色部分為可配置界面,單擊即可快速跳轉到對應項設定。左側欄為 ZYNQ IP 核配置的導航視窗,共分為 8 個部分,對應功能如下

  • ZYNQ Block Design:圖形化向導界面
  • PS-PL Configuration:PS-PL 接口配置界面,可以對 AXI、HP、DMA 等總線接口進行配置
  • Peripheral I/O Pins:I/O 外設引腳配置界面,可對 I/O 外設引腳進行 MIO 以及 EMIO 的配置,配置時需要注意 Bank 電平
  • MIO Configuration:MIO 配置界面,相較于 I/O 外設引腳配置界面功能更為複雜
  • Clock Configuration:時鐘配置界面,用于配置 PS 輸入時鐘、外設時鐘、DDR 時鐘和 CPU 時鐘等
  • DDR Configuration:DDR 配置界面,控制 DDR 型号以及細節參數等
  • SMC Timing Calculation:SMC 時序計算界面,可進行 SMC 時序計算
  • Interrupts:中斷控制界面,可對 PS 與 PL 間的中斷進行配置

ZYNQ 中的外設接口由對應控制器控制,使用時需要先進行使能。點選圖形化向導界面的 I/O Peripherals 中對應的外設接口便可以跳轉到相應的配置界面,點選 GPIO 即可進入相應配置界面

ZYNQ 筆記(一):開發流程

本設計通過 PS 與 PL 側的按鍵控制另一側的 LED 燈,每個按鍵和 LED 燈都需要一個 GPIO 信号。GPIO 連接配接到 PS 部分使用的是 MIO 接口,連接配接到 PL 部分使用的則是 EMIO 接口,是以我們需要使能這兩個外設接口。PS 端的引腳是固定的,我們不需要配置設定;PL 端的按鍵和 LED 燈都需要一個 EMIO 信号,是以我們将 EMIO 的位寬設定為 2

ZYNQ 筆記(一):開發流程

設定完成後檢查電平狀态,将 Bank1 的電平設定為 1.8V

ZYNQ 筆記(一):開發流程

使能完成後傳回圖形化向導界面,可以看到此時在 GPIO 外設後面已經打上 √ 了,代表我們已經使能該接口

ZYNQ 筆記(一):開發流程

接下來開始配置 DDR,DDR 全名為雙速率同步動态随機存儲器(Double Data Rate Synchronous Dynamic Random Access Memory),也就是我們常說的 記憶體。基于 PS 端的應用,大部分都需要基于片外存儲外設 DDR 上允許。點選圖像化界面的 DDR 控制器即可跳轉到配置界面

ZYNQ 筆記(一):開發流程

使能 DDR 并選擇 DDR 型号,筆者使用的是 MT41K256M16 RE-125

ZYNQ 筆記(一):開發流程

點選 OK,可以看到此時 ZYNQ 的 IP 核如圖所示

ZYNQ 筆記(一):開發流程

點選上方的藍色小字 Run Block Automation 系統就會自動幫我們導出引腳,勾選 All Automation 後點選 OK 即可

ZYNQ 筆記(一):開發流程

導出完成後可以看到軟體幫我們導出了 ZYNQ 的兩個引腳,但 GPIO_0 還未導出,是以我們需要手動将其導出

ZYNQ 筆記(一):開發流程

點選 GPIO_0 引腳,然後右鍵點選 Make External 導出引腳

ZYNQ 筆記(一):開發流程

該設計中 ZYNQ IP 核在配置時,有幾個端口信号是沒有被使用到的

  • FCLK_RESET0_N:全局複位信号,低電平有效
  • M_AXI_GP0:通用 AXI 接口信号,M 代表其作為主機信号
  • M_AXI_GP0_ACLK:M_AXI_GP0 的輸入時鐘信号
  • FCLK_CLK0:PS 輸出時鐘信号

其中,M_AXI_GP0_ACLK 雖然本設計中并未使用到,但如果不給其一個輸入信号,軟體便會報錯

ZYNQ 筆記(一):開發流程

為了防止出現該現象,我們隻需要将 FCLK_CLK0 和 M_AXI_GP0_ACLK 連接配接即可。連接配接時隻需選中其中一個引腳按住滑鼠左鍵拖動到對應的引腳即可

ZYNQ 筆記(一):開發流程

端口連接配接完成後使用快捷鍵 Ctrl+S 儲存設計,随後點選上方的 √ 來對設計進行驗證

ZYNQ 筆記(一):開發流程

當出現以下彈窗時證明設計無誤

ZYNQ 筆記(一):開發流程

生成封裝

點選 sources 資源欄我們建立的 system 子產品設計,右鍵選擇 Generate Output Products 生成輸出

ZYNQ 筆記(一):開發流程

選擇 Out Of context per IP,點選 Generate

ZYNQ 筆記(一):開發流程

點選 sources 資源欄我們建立的 system 子產品設計,右鍵選擇 Create HDL Wrapper 以建立 HDL 封裝

ZYNQ 筆記(一):開發流程

在彈出的視窗中選擇讓 Vivado 管理封裝和自動更新,随後點選 OK 開始建立封裝

ZYNQ 筆記(一):開發流程

如果工程資源欄的右上角出現 Updating 的字樣表明封裝正在建立和更新中,當字樣消失且 Sources 欄出現藍色名為 system_wrapper 的設計檔案時,則代表封裝已經建立和更新完成

ZYNQ 筆記(一):開發流程

管腳限制

對于涉及 PL 端的設計,在完成封裝後我們需要對涉及到的管腳進行配置設定和限制。點選左側導航欄的 Open Elaborated Design 進行限制和配置設定

ZYNQ 筆記(一):開發流程

點選 OK

ZYNQ 筆記(一):開發流程

根據實際情況綁定引腳并設定電平限制,筆者使用的 MZ7035FD 闆載接口如下

闆載裝置 ZYNQ 接口
SW1 H12
SW3 MIO50
LED1 F13
LED5 MIO51
ZYNQ 筆記(一):開發流程

使用快捷鍵 Ctrl+S 儲存限制檔案,在彈窗中輸入限制檔案名,點選 OK 即可

ZYNQ 筆記(一):開發流程

生成比特流

完成了管腳限制後即可開始比特流的生成,比特流中包含着對 PL 端的配置資訊,其中就包括對 引腳的配置設定 以及 電平的限制,是以生成比特流需要在進行管腳配置設定和限制之後。當然如果設計并未使用到 PL 部分資源,可以直接跳過該環節。點選左側工具欄 Generate Bitstream 開始生成比特流

ZYNQ 筆記(一):開發流程

此時軟體會提示 ”沒有可用的實作結果,是否先進行綜合實作再自動生成比特流“,我們直接點選 Yes 即可

ZYNQ 筆記(一):開發流程

導出硬體

File > Export > Export Hardware

ZYNQ 筆記(一):開發流程

點選 Next

ZYNQ 筆記(一):開發流程

對于純 PS 端的設計,選擇哪一項都可以,而涉及到 PL 端的設計必須選擇 Include bitstream,點選 Next

ZYNQ 筆記(一):開發流程

設定 xsa 檔案名稱及儲存路徑,點選 Next

ZYNQ 筆記(一):開發流程

點選 Finish 即可

ZYNQ 筆記(一):開發流程

啟動 Vitis

硬體導出完成後 Vivado 部分的設計已經完成,接下來打開 Vitis 開始 CPU 軟體程式設計。點選 Tools > Launch Vitis IDE

ZYNQ 筆記(一):開發流程

CPU 軟體程式設計

建立工程

Vitis 将晶片視為一個平台,平台的相關資訊通過此前導出的 xsa 檔案描述,工程間的關系如下

ZYNQ 筆記(一):開發流程

File > New > Application Project,點選 Next

ZYNQ 筆記(一):開發流程

選擇 Create a new platform from hardware (XSA),點選 Browse 選擇此前導出的 xsa 檔案,或者從清單中選擇與開發闆相比對的

ZYNQ 筆記(一):開發流程

等待 xsa 檔案加載完成,點選 Next

ZYNQ 筆記(一):開發流程

設定工程名稱,點選 Next

ZYNQ 筆記(一):開發流程

點選 Next

ZYNQ 筆記(一):開發流程

選擇工程模闆,點選 Finish 即完成建立

ZYNQ 筆記(一):開發流程

點選 Finish 即可完成建立

ZYNQ 筆記(一):開發流程

添加應用庫

對于某些開發闆,廠商可能提供已經編寫完善的應用庫,若要使用這些應用庫就需要将相關檔案添加到項目中。以應用庫 MZ7035FD_Lib 為例,首先将檔案夾複制到項目中

ZYNQ 筆記(一):開發流程

選中項目,右鍵 Properties

ZYNQ 筆記(一):開發流程

選擇 Paths and Symbols,點選右側 Add 将應用庫添加至頭檔案路徑,否則編譯時會提示找不到相關檔案

ZYNQ 筆記(一):開發流程

點選 Workspace

ZYNQ 筆記(一):開發流程

選擇應用庫檔案夾,點選 OK

ZYNQ 筆記(一):開發流程

點選 OK

ZYNQ 筆記(一):開發流程

點選 Apply and Close

ZYNQ 筆記(一):開發流程

添加使用者代碼

在項目 src 檔案夾下建立檔案 main.c

ZYNQ 筆記(一):開發流程

本設計示例代碼如下

/* main.c
*********************************************
* @Vivado 2022.1
* @Vitis 2022.1
* @Board MZ7035FD
* @Chip xc7z035ffg676-2
* @DDR MT41K256M16 RE-125
*********************************************
*/
#include "PS_GPIO/PS_GPIO.h"

#define 	PS_LED 		51 								// LED5
#define 	PS_KEY 		50 								// SW3
#define 	PL_LED 		54 + 0 					// LED1
#define 	PL_KEY 		54 + 1	 				// SW1

int main()
{
        // 初始化 PS 端 MIO 和 EMIO
        PS_GPIO_Init();
        // 設定 GPIO 模式
        PS_GPIO_SetPinMode(PS_LED, GPIO_DIR_OUTPUT, GPIO_STATE_LOW);
        PS_GPIO_SetPinMode(PS_KEY, GPIO_DIR_INPUT, GPIO_STATE_LOW);
        PS_GPIO_SetPinMode(PL_LED, GPIO_DIR_OUTPUT, GPIO_STATE_LOW);
        PS_GPIO_SetPinMode(PL_KEY, GPIO_DIR_INPUT, GPIO_STATE_LOW);
        // 循環讀取按鍵狀态
        while(1)
        {
        				PS_GPIO_WritePin(PS_LED, PS_GPIO_ReadPin(PL_KEY) == GPIO_STATE_HIGH ? GPIO_STATE_LOW : GPIO_STATE_HIGH);
        				PS_GPIO_WritePin(PL_LED, PS_GPIO_ReadPin(PS_KEY) == GPIO_STATE_HIGH ? GPIO_STATE_LOW : GPIO_STATE_HIGH);
        }
        return 0;
}           
/* PS_GPIO.h
*********************************************
* @Vivado 2022.1
* @Vitis 2022.1
* @Board MZ7035FD
* @Chip xc7z035ffg676-2
* @DDR MT41K256M16 RE-125
*********************************************
*/
#ifndef __PS_GPIO_H__
#define __PS_GPIO_H__

#include "xgpiops.h"

#define 	GPIO_DIR_INPUT 								0 							// 設定 GPIO 為輸入
#define 	GPIO_DIR_OUTPUT 						1              // 設定 GPIO 為輸出
#define 	GPIO_OUTPUT_ENABLE 			1       				// 使能 GPIO 輸出
#define 	GPIO_OUTPUT_DISABLE 		0      					// 禁用 GPIO 輸出
#define 	GPIO_STATE_LOW 							0                // GPIO 為低電平
#define 	GPIO_STATE_HIGH 						1               // GPIO 為高電平

void PS_GPIO_Init();
void PS_GPIO_SetPinMode(uint8_t GPIO_Num, uint8_t GPIO_Dir, uint8_t GPIO_Default_State);
void PS_GPIO_WritePin(uint8_t GPIO_Num, uint8_t GPIO_State);
uint8_t PS_GPIO_ReadPin(uint8_t GPIO_Num);

#endif           
/* PS_GPIO.c
*********************************************
* @Vivado 2022.1
* @Vitis 2022.1
* @Board MZ7035FD
* @Chip xc7z035ffg676-2
* @DDR MT41K256M16 RE-125
*********************************************
*/
#include "PS_GPIO.h"

// GPIO 執行個體對象
XGpioPs GpioPs;

/*
* @brief 初始化 PS 端 MIO 和 EMIO
*/
void PS_GPIO_Init()
{
        // 擷取 PS 端 GPIO 配置
        XGpioPs_Config *ConfigPtr = XGpioPs_LookupConfig(XPAR_XGPIOPS_0_DEVICE_ID);
        // 初始化 PS 端 GPIO 執行個體
        XGpioPs_CfgInitialize(&GpioPs, ConfigPtr, ConfigPtr->BaseAddr);
}

/*
* @brief 設定單個 GPIO 引腳模式
* @param GPIO_Num GPIO 編号(MIO:0~53,EMIO:54~)
* @param GPIO_Dir GPIO 方向(輸入/輸出)
* @param GPIO_Default_Output GPIO 預設輸出(GPIO設定為輸出時有效)
*/
void PS_GPIO_SetPinMode(uint8_t GPIO_Num, uint8_t GPIO_Dir, uint8_t GPIO_Default_State)
{
        // 設定 GPIO 輸入/輸出方向
        XGpioPs_SetDirectionPin(&GpioPs, GPIO_Num, GPIO_Dir);
        // 輸出模式需要使能并設定預設輸出電平
        if(GPIO_Dir == GPIO_DIR_OUTPUT)
        {
                // 使能 GPIO 輸出
                XGpioPs_SetOutputEnablePin(&GpioPs, GPIO_Num, GPIO_OUTPUT_ENABLE);
                // 設定預設輸出
                XGpioPs_WritePin(&GpioPs, GPIO_Num, GPIO_Default_State);
        }
}

/*
* @brief 寫入 GPIO 電平
* @param GPIO_Num GPIO 編号(MIO:0~53,EMIO:54~)
* @param GPIO_State 待寫入的 GPIO 電平狀态
*/
void PS_GPIO_WritePin(uint8_t GPIO_Num, uint8_t GPIO_State)
{
        XGpioPs_WritePin(&GpioPs, GPIO_Num, GPIO_State);
}

/*
* @brief 讀取 GPIO 電平
* @param GPIO_Num GPIO 編号(MIO:0~53,EMIO:54~)
* @return 高電平輸出 1,低電平輸出 0
*/
uint8_t PS_GPIO_ReadPin(uint8_t GPIO_Num)
{
        return XGpioPs_ReadPin(&GpioPs, GPIO_Num);
}           

闆級驗證與調試

選擇工具欄運作按鈕,點選 Run Configurations...

ZYNQ 筆記(一):開發流程

輕按兩下 Single Application Debug (GDB) 建立配置

ZYNQ 筆記(一):開發流程

選擇 Target Setup,取消勾選 Reset entire system,否則燒錄後會報錯

ZYNQ 筆記(一):開發流程

點選運作按鈕即可自動下闆運作

ZYNQ 筆記(一):開發流程
ZYNQ 筆記(一):開發流程

點選調試按鈕即可下闆調試

ZYNQ 筆記(一):開發流程

調試相關按鈕(全速運作、暫停、停止、進入函數、跳過語句)

ZYNQ 筆記(一):開發流程

Vitis 聯合 VSC 程式設計

Vitis 在編寫代碼時需要通過快捷鍵 Alt+? 啟用代碼提示,使用體驗非常差。為此下面介紹如何使用 VS Code 編寫代碼,效果如下

ZYNQ 筆記(一):開發流程

首先在 VS Code 中打開 Vitis 項目檔案夾

ZYNQ 筆記(一):開發流程

點選報錯部分 > 快捷鍵 Alt + Enter > 編輯 "includePath" 設定

ZYNQ 筆記(一):開發流程

此時 VS Code 會在項目根路徑下生成 .vscode 配置檔案夾,我們需要在 c_cpp_properties.json 檔案中添加頭檔案路徑。其中${default} 代表 C/C++ 插件全局配置中設定的 Include,我們還需要添加該項目需要的頭檔案

ZYNQ 筆記(一):開發流程

Vitis 項目依賴的頭檔案均在平台工程 (Platform Project) system_wrapper 中,由上圖可知 system_wrapper 和 應用工程 (Application Project) gpio_axi_system 檔案夾平級,是以添加頭檔案路徑如下 (xxx/** 會自動周遊 xxx 檔案夾下的所有子檔案和子檔案夾)

ZYNQ 筆記(一):開發流程

此時按住 Ctrl 并用滑鼠點選變量、函數名等已可以檢視其定義,但仍然會有紅色波浪線報錯

ZYNQ 筆記(一):開發流程

在 .vscode 檔案夾下建立 settings.json 配置檔案,并添加如下内容

{
        "C_Cpp.intelliSenseEngineFallback": "disabled",
        "C_Cpp.intelliSenseEngine": "Tag Parser"
}           

紅色波浪線報錯消失,開始用 VS Code 愉快地程式設計吧!

ZYNQ 筆記(一):開發流程

Vivado 聯合 Modelsim 仿真

Modelsim 在仿真速度和使用體驗上均要優于 Vivado 自帶的 Vsim,是以接下來将介紹如何在 Vivado 中調用 Modelsim 進行仿真

編譯 Vivado 仿真庫

Modelsim 安裝完成後并不包含 Xilinx 相關的器件庫,是以需要先編譯後才能使用。打開 Vivado,Tools > Compile Simulation Libraries

ZYNQ 筆記(一):開發流程

設定 Simulator 為 Modelsim Simulator,選擇編譯庫存放路徑和 modelsim.exe 所在檔案夾,勾選 Compile Xilinx IP,點選 Compile

ZYNQ 筆記(一):開發流程

等待編譯完成

ZYNQ 筆記(一):開發流程

設定仿真工具和庫路徑

Vivado 建立工程的預設仿真工具是 Vsim,是以要使用 Modelsim 仿真,每個新工程都要設定一次。點選 Tools > Settings

ZYNQ 筆記(一):開發流程

側邊欄選中 Simulation,設定 Target Simulator 為 Modelsim Simulator,點選 Yes

ZYNQ 筆記(一):開發流程

設定 Xilinx 編譯庫路徑,點選 OK

ZYNQ 筆記(一):開發流程

點選側邊欄 Run Simulation > Run Behavioral Simulation

ZYNQ 筆記(一):開發流程

等待 Modelsim 仿真結果,大功告成!

ZYNQ 筆記(一):開發流程

參考文獻

[1] 小梅哥. 基于C程式設計的Zynq裸機程式設計與應用教程

[2] 皮皮祥. Vivado 關聯 Modelsim 進行仿真