Vivado開發套件設計筆記(3)——加法器設計——指針(下)


接上一篇博文《Vivado開發套件設計筆記(3)——加法器設計——指針(上)》

上一篇博文爲讀者介紹了 HLS 中基於 數組float型四則運算IP核 的製作,然後在 Vivado 中把 IP核 加入到工程中與 ZYNQ Processing System 相連,然後導出硬件信息供給 SDK 用,至於 SDK 怎麼用這個自定義的 四則運算IP核 ?筆者在這一篇博文繼續給讀者講解。

4 SDK設計

Vivado 軟件中運行 SDK 後會出現下面的界面(讀者會發現這款 SDK 是用 Eclipse 開發的)。上面兩個框是我們的硬件信息,下面的框框內的 add_0 就是 加法器IP核 導出的硬件信息了。

在這裏插入圖片描述

(1)創建工程

在菜單欄點擊 File —— New —— Application Project ,彈出創建工程界面,輸入項目名爲 add (英文的無空格就可以),如下圖所示,然後點擊 Next

在這裏插入圖片描述
然後選擇工程模板,選擇 Hello World 工程模板,如下圖。

在這裏插入圖片描述
工程創建成功後如下圖所示。

在這裏插入圖片描述

(2)程序設計

點開導航欄左側的 Project Explorer 會發現裏面有很多的文件,其中最最重要的就三個:第一個是 arith/src/Helloworld.c ,這個 c文件 裏面有ZYNQ的CPU程序入口 main函數 ;第二個是 arith_bsp/ps7_cortexa9_0/include/xparameters.h ,這個頭文件裏面有我們所用到的 四則運算IP核 映射到FPGA的DDR中的內存地址和相關驅動參數(這些是 自動生成 的);第三個是add_bsp/ps7_cortexa9_0/include/xarith.h ,其命名方式是與讀者設計的 IP核命名 相關,在其前面加上一個 ‘x’ 而已,比如我們的加法器是 arith ,因此它的命名方式就是 xarith.h ,這裏面存放着我們調用自定義 四則運算IP核 的庫函數(或者叫做 驅動)。

有了以上三個文件,就可以開始 搞事情 了。

第一步,點開 helloworld.c ,輸入以下代碼。

#include <stdio.h>
#include "platform.h"
#include "xil_printf.h"
#include "xarith.h"

int main()
{
	int status;
	int length;
	init_platform();

	float data[4];
	float res[4];
	float ref[4];
	XArith My_IP;

	status = XArith_Initialize(&My_IP, XPAR_ARITH_0_DEVICE_ID);
	if(status == XST_SUCCESS)
		print("IP Initialize Successfully!\n\r");

	data[0] = 10.5;
	data[1] = 5.5;
	data[2] = 8.0;
	data[3] = 0.5;

	ref[0] = data[0] + data[1];
	ref[1] = data[0] - data[1];
	ref[2] = data[2] * data[3];
	ref[3] = data[2] / data[3];

	length = XArith_Write_data_Words(&My_IP, 0, data, 4);
	if(length == 4)
		printf("Write OK!\n\r");
	XArith_Start(&My_IP);

	while(!XArith_IsDone(&My_IP));

	length = XArith_Read_res_Words(&My_IP, 0, res, 4);
	if (length == 4)
		print("Hello World\n\r");


	printf("PS(ARM) : ref[0] = %f, ref[1] = %f, ref[2] = %f, ref[3] = %f \n\r", ref[0], ref[1], ref[2], ref[3]);
	printf("PL(FPGA) : res[0] = %f, res[1] = %f, res[2] = %f, res[3] = %f \n\r", res[0], res[1], res[2], res[3]);

	cleanup_platform();
	return 0;
}

首先需要先定義 XArith 類型的結構體 My_IP ,再調用 XArith_Initialize() 初始化 IP核 ,如果返回值爲 XST_SUCCESS 即表示初始化成功。然後定義輸入到 IP核 的數組 data 和存儲返回結果的數組 res ,之後利用庫函數 XArith_Write_data_Words() 把變量送到PL端,接着調用 XArith_Start() 啓動PL端的 IP核 開始計算,然後利用 while(!XArith_IsDone(&My_IP)); 阻塞方式等待 IP核 運算完畢,最後用 XArith_Read_res_Words() 取得 IP核 的返回結果。最終用ARM和FPGA做計算,對比結果,驗證是否有誤。

上述的 XArith 結構體類型,以及對 IP核 的調用的庫函數(驅動)全部來自於頭文件 xarith.h 中。其中 XArith_Initialize() 的第二個參數是 IP核ID ,這個 ID 號到 xparameters.h 頭文件中找。所有用到的東西都在下面的截圖中。

這裏需要補充三點知識:

  • 第一點是數組維度,我們在 HLS 中設置的存儲輸入數據和輸出數據的是2*2的二維數組,而到了 SDK 中,從輸入數據到IP核的函數 XArith_Write_data_Words(XArith *InstancePtr, int offset, int *data, int length) 來看,輸入時候用到的指針其實是一維的,然後根據輸入的第四個形參 length ,把length長的數組一行一行地填充慢 HLS 中的二維數組,也就是說 SDK 中長度爲4的 data 數組的第0個到第3個(總共4個)分別放到了 HLS(實際上是IP核) 上對應的第0行第0列、第0行第1列、第1行第0列、第1行第1列這四個位置。

  • 第二點是數據類型,從 XArith_Write_data_Words(XArith *InstancePtr, int offset, int *data, int length) 這一個函數定義來看,我們需要輸入的數據其實是 float 型的,但是這裏生成的驅動函數的 data 指針是 int 型,經過筆者實驗發現,不論在 HLS 上定義的是 int 型還是 float 型,最終生成的驅動函數的數據指針都是 int 型,這個函數送到IP核應該是按照二進制送進去的,float 型變量所佔的內存空間是4個字節, int 型也是4個字節,這樣直接強制轉換完送進去,輸出時候用的 XArith_Read_res_Words(XArith InstancePtr, int offset, intdata, int length) 函數再次強制轉換之後,結果會跟我們預期的一樣。

  • 第三點是賦值,我們定義的 data 變量是 float 型數組,在給這樣的 float 型變量賦值的時候,建議是用帶小數賦值,或者加強制類型轉換,比如我要給一個 float 類型的變量 a 賦0的話,最好用 a = 0.0 或者 a = (float)0 ,筆者認爲這樣更安全,因爲此前自己開發的時候遇到很多次這樣的 BUG ,都是處在這個問題上,所以這裏提醒筆者以防不小心出了這個 BUG 不好找。

另外,有詳細看 xarith.h 文件的讀者會發現,例如寫數據的函數,會有兩個,一個是 XArith_Write_data_Words(XArith *InstancePtr, int offset, int *data, int length),另一個是 XArith_Write_data_Bytes(XArith *InstancePtr, int offset, char *data, int length) ,這裏區別在於寫數據時候用的是 Words(字節) 還是 char(字符) ,一個是4字節一個是單字節,因爲我們用到的是 float 變量,所以用 字節 類型的函數即可,同理讀取輸出結果的函數也是一樣。讀者有興趣可以自行嘗試用 字符 類型的函數做實驗。

部分截圖如下。

在這裏插入圖片描述

在這裏插入圖片描述

(3)燒錄程序

程序寫好之後就可以燒錄到板子上了。

第一步先把程序燒錄到 ZYNQ 上,如下圖所示操作。

在這裏插入圖片描述
用串口連接PC和開發板,(這裏假設讀者都已經安裝好串口驅動之類的,如果不知道怎麼做的,提供開發板的產商應該會給相應的資料,照做即可),打開串口調試助手,看運行結果,如下圖所示。可以看到這裏程序只運行到初始化這一步,沒有之後我們所預想的有打印數據出來,這主要是因爲硬件信息未燒錄到FPGA中,因此在運行 while(!XAdd_IsDone(&MyAdd)); 會阻塞住不正常運行,到下一步。

在這裏插入圖片描述

第二步,把程序同時燒錄到 ZYNQFPGA 上,操作如下圖所示。
在這裏插入圖片描述
在這裏插入圖片描述
如果出現下面圖示的警告,點 OK ,等待串口助手顯示我們預想的結果就可以了。

在這裏插入圖片描述
如果讀者都按照以上操作做完的話,會在串口助手上看到這樣的結果,跟我們的預期一樣,大功告成。

在這裏插入圖片描述

這裏補充兩點:

第一點是爲什麼要先燒錄到ARM再燒錄到FPGA,因爲一個新的工程如果沒有進行燒錄到ARM的操作的話,之後燒錄到FPGA的操作界面會缺少一個生成的System Debugger,有興趣的讀者可以試一下。

第二點是串口正常連接時的設備顯示和串口調試助手的配置,如下圖所示。

在這裏插入圖片描述
在這裏插入圖片描述
正常安裝完USB驅動之後,用USB線把開發板上的UART口和JTAG口分別連到PC上,就可以正常使用了。

至此,所有的操作完成。

5 總結

這篇博文筆者詳細的介紹了從HLS上 二維數組float 型數據的 四則運算IP核 的製作,到Vivado上頂層電路的連接,最後到SDK上應用程序的開發與驗證,完成了用ARM核控制FPGA完成 四則運算 的任務,並教會讀者怎麼在HLS中用 數組float 型變量。

我們把流程重新理一遍。

  • HLS製作IP核
    • C仿真(C Simulation)
    • C綜合(C Synthesis)
    • C/RTL 聯合仿真(C/RTL Cosimulation)
    • 導出RTL(Export RTL)
  • Vivado構建完整電路
    • 把自定義IP核添加到IP Catalog(Add Repository)
    • 創建Block Design並添加自定義IP核以及ZYNQ處理系統,然後配置ZYNQ(Create Block Design)
    • 在Block Design中自動連接ZYNQ和自定義IP核並導出必要端口(Open Block Design)
    • 封裝HDL並生成硬件輸出(Create HDL Wrapper & Generate Output Products)
    • 運行項目(Run Implementation)
    • 生成bitstream並導出硬件(Generate Bitstream & Export Hardware)
  • SDK開發應用程序
    • 包含自定義IP覈對應的驅動頭文件(例如 xadd.h)
    • 聲明自定義IP覈對應的結構體並初始化(例如 XAdd MyAdd)
    • 利用庫函數把數據輸入到IP核,然後等待IP核運算完畢,再取輸出數據
    • 把程序燒錄到ARM上
    • 把程序同時燒錄到ARM和FPGA上
    • 利用串口調試助手檢驗設計

以上便是全部操作流程。

當我們需要用到含有大量數據的數組的時候,可以在 HLS 上把形參設置成數組,如果需要用到的數據含有小數的時候,可以 把數組定義爲 float 型,之後再 SDK 上把一維的數組按照需要的順序放到 IP核float 型數據也是直接用驅動函數放進去,在調用 寫函數 的時候會有強制類型轉換,調用 讀函數 的時候也會有強制類型轉換,最終會得到我們所希望的結果,所以讀者不必擔心驅動函數上面 int 型的變量和 float 型變量不匹配的問題。另外,如果在 HLS 上面定義的數組是二維的,那麼這個二維數組是按一行一行展開成一個行向量,去和 SDK 上的一維數組對應的,這個地方讀者在自己設計的時候需要非常注意。

非常感謝讀者讀到了這裏,這一博文會有很多地方與上一篇博文《Vivado開發套件設計筆記(2)——加法器設計》相似,筆者也是怕讀者只讀這一篇博文,而沒有時間去讀上一篇,所以才出此下策,希望讀者可以理解。如果不幸讓讀者感到不悅,筆者將在下一篇博文中介紹 DDR3 的使用,以此謝罪。

6 參考文章

[1]: Xilinx Zynq-7000 嵌入式系統設計與實現(何賓著)
[2]: Vivado開發套件設計筆記(1)——入門簡介
[3]: Vivado開發套件設計筆記(2)——加法器設計——變量(上)
[4]: Vivado開發套件設計筆記(2)——加法器設計——變量(下)
[5]: Vivado開發套件設計筆記(3)——加法器設計——指針(上)


原創性聲明:本文屬於作者原創性文章,小弟碼字辛苦,轉載還請註明出處。謝謝~

如果有哪些地方表述的不夠得體和清晰,有存在的任何問題,亦或者程序存在任何考慮不周和漏洞,歡迎評論和指正,謝謝各路大佬。

需要代碼和有需要相關技術支持的可諮詢QQ:297461921

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章