1. 程式人生 > >Altera FPGA NIOS-II之Hello World

Altera FPGA NIOS-II之Hello World


1、什麼是NIOS II?

NIOS II就是一款CPU,和51、ARM、MIPS、X86的概念是一樣的。但是與其他處理器架構相比NIOS II最大的特點是執行在(Intel Altera)FPGA上的軟核處理器,說白了就是使用Verilog HDL或者VHDL語言在FPGA內部實現了一個處理器,這是一個龐大的系統,相當於在ARM處理器上編寫一個作業系統,所以不是所有人都可以建立一個自己的CPU系統,而且就算自己編寫了一個CPU系統,也沒有配套的編譯器和開發環境的支援,只能使用二進位制指令進行程式設計,這是何等的痛苦。當然了Altera公司早就考慮到這點了,NIOS軟核應運而生,NIOS II是在NIOS的進化版本,現在好像都是使用NIOS II系統了。同時Altera公司為NIOS II軟核提供了完整的工具鏈包括軟核的定製、豐富的外設IP庫、NIOS II軟體開發環境、編譯器等。

FPGA系統上使用NIOS II處理器之後相當於傳統的FPGA+MCU協作,各自發揮各自的優勢,FPGA能夠實現高效的資料處理,MCU能夠處理繁雜的任務。

 

2、定製NIOS II系統

2.1 前期準備

手頭有現成的一塊Cyclone II開發板,拿這個板子進行試驗。開啟Quartus II軟體,建立一個工程,頂層模組名叫做HelloWorld,選擇好了器件之後可以開始定製NIOS II系統了,這裡說的NIOS II系統指的不僅僅是一個NIOS II CPU軟核,它包含了CPU、時鐘、儲存、匯流排、外設等部分。在Quartus軟體的工具欄上開啟Qsys工具,如下圖:

進入到Qsys介面:

這就是NIOS II系統的定製軟體,在右側的System Contents欄中可以看到已經預設存在一個clk_0模組了,可能就是為了告訴我時鐘是必不可少的。左側是Altera提供的豐富的IP核,種類非常的多,可以構建一個資源豐富的微控制器系統。

HelloWorld工程需要時鐘、CPU、ROM、RAM、JTAG_UART、SYSID、PIO(後面會有為什麼需要這個PIO外設)這些模組。我們可以挨個在左側的Library中搜索,然後雙擊之後進行新增和配置。

 

2.2 新增配置模組

2.2.1 配置系統時鐘

雙擊clk_0模組進行配置:

開發板上的晶振是50MHz的,不使用PLL的情況下輸入給系統的時鐘就是外部晶振的時鐘頻率。

2.2.2 定製NIOS II

新增一個NIOS II系統,

NIOS II一共三款型別,我選擇了NIOS II/e,這是最小體積但是效能最差的一款處理器,因為我的開發板是Cyclone II的EC2C5T144C8,內部資源比較有限,如果選擇NIOS II/f的話會導致內部資源不夠用。下面還有Reset Vector和Exception Vector沒有配置,因為現在還配置不了,這些向量是跟程式執行地址相關的,但是還沒有新增系統儲存器。

2.2.3 新增ROM儲存器

搜尋on chip memery,然後新增配置:

儲存器型別選擇ROM,Data Width選擇32,Total memery size選擇2048,不能太大,器件空間有限。

2.2.4 新增RAM儲存器

步驟和上面的ROM定製一樣,儲存器型別選擇RAM,Data Width選擇32,Total memery size選擇6144,這個值是恰好的值,小了導致NIOS II應用程式無法執行,大了導致RAM使用率超出FPGA內部總RAM,會在編譯的Place&Route過程中出錯。

2.2.5 新增JTAG_UART偵錯程式

這個模組主要是為了下載程式、單步除錯和資料列印使用的,使用預設配置就行了。

2.2.6 新增System ID peripheral

新增這個的目的我也沒仔細研究,好像是Eclipse在連線CPU的時候會進行ID檢測。直接新增即可,32 bit System ID就寫0x00000000即可。

2.2.7 新增PIO

PIO就是Parallel I/O,相當於51微控制器的P0、STM32的GPIOA。PIO模組的具體配置如下圖:

 

2.3 線路連線

新增完所有模組之後還需要進行線路連線,這裡有一些準則:

1、clk時鐘需要連線到所有模組,因為所有模組都是時序器件,都需要時鐘驅動。

2、clk_reset也連線到所有模組,因為如果時鐘模組被複位了,其他連線了clk時鐘的器件都要復位。注意clk模組的clk_in_reset是外部輸入的復位訊號,clk_reset訊號是clk模組輸入給其他器件的復位輸出訊號。

3、jtag_uart模組的復位輸出連線到所有模組,因為除錯模組有權利進行系統復位。

4、NIOS II軟核的data_master資料匯流排連線到所有器件,因為所有器件只有被分配到CPU的匯流排中去之後才能被CPU所呼叫(器件需要能夠被定址,並能夠讀取或者寫入資料)。

5、NIOS II軟核的instruction_master指令匯流排連線到儲存器中去,包括onchip_rom和onchip_ram,也可以是SDRAM控制器等。

根據上面的準則進行連線,最後的連線圖如下:

連線完成之後需要設定NIOS II IP核的Reset Vector和Exception Vector,雙擊nios2_qsys進行配置:

表示復位之後程式從0x00005000地址處的ROM中開始執行。產生中斷/異常之後從0x00002020地址處的RAM中執行,這就是中斷向量表。

下面開始基地址分配。我們知道NIOS II是32位的核心,定址空間是4GB,我們定義了那麼多器件,還有儲存器,儲存器的空間各不相同,每個器件掛載在什麼地址上面呢?我們可以自己計算然後配置地址,但是Qsys提供了分配工具,點選選單欄中的【System】→【Assign Base Addresses】,Qsys工具會自動為各個IP核分配基地址。基地址分配完成後,可以看到0Error和0 Warning的提示,如圖所示:.

到這裡已經配置好一個NIOS II系統了,最後一步就是生成NIOS II系統了。點選右側選單欄中的【Generate】欄,進入生成Qsys系統的設定介面:

完成之後如果沒有錯誤就會生成一個icpu.qip模組,在icpu的synthesis資料夾中,這個模組就是我們定製的IP核,在頂層模組中可以新增這個模組。記得要儲存這個Qsys系統,下次要修改icpu的時候直接開啟修改然後generate即可。

2.4 設定頂層模組

在Qsys介面裡面有一個HDL example:

直接複製這個程式到頂層模組中呼叫即可:

這裡我加了一個LED閃爍模組,用來檢視系統是不是在執行。然後Pin planner、Programmer......這裡不再贅述,常規操作。燒寫到FPGA開發板上之後led按照2.5Hz的頻率進行閃爍,說明系統已經在執行。

 

3、軟體設計

現在已經有一個屬於我們自己的CPU系統在FPGA中待著了,可不能要它就這麼幹耗著,要做點什麼不是麼?和STM32使用KEIL MDK整合開發環境一樣,NIOS II也是使用整合開發環境進行開發的,但是遺憾的是這個開發環境是基於Eclips的,著實是不怎麼好用,希望Altera好好優化一下。開啟Quartus軟體在選單欄的Tools欄下選擇Nios II Software Build Tools for Eclips,開啟之後選擇一個工作空間,然後File--New--Nios II Application and BSP from Template,使用模板為例建立一個工程:

上面Target hardware information中的SOPC Information File name選擇剛才建立NIOS II系統是generate出來的檔案,這個檔案就是你建立的系統的配置檔案。下面的Templates選擇Hello World Small,這個程式的佔用空間比Hello World工程要小很多。建立完成之後會生成兩個專案:

HelloWorld_bsp是開發環境根據SOPC Information File生成的,就是根據我設計的CPU生成的獨有的bsp庫,開發應用程式的時候可以直接使用。

開啟工程HelloWorld的hello_world_small.c檔案主函式中只有一個列印字串的功能。右擊兩個工程選擇Build Project進行編譯,如果沒有問題就可以將程式下載到系統是實驗,右擊工程--Run as--Nios II Hardware,首次下載會彈出下面的Run Configuration視窗進行配置。

配置完之後就可以直接在Eclips的工具欄上直接進行運行了:

左邊的是Debug,右邊的是Run,電機向下箭頭選擇Debug/Run型別。

遺憾的是下載進去之後沒有反應:

Nios II Console視窗中沒有任何資料。試了很多次都沒有成功。但是偶然我實驗了一次Debug:

點選紅框中的Resume之後開始執行程式,發現有資料打印出來了,然後將程式修改成迴圈列印資料,再進行除錯:

的確有資料迴圈列印,但是停止Debug之後又沒有資料列印了,難道停止Debug之後CPU停止運行了麼?還是說jtag_uart傳輸有問題呢?這時候我就想使用PIO來控制LED燈來檢視程式是否在執行,程式如下:

#include "sys/alt_stdio.h"
#include "altera_avalon_pio_regs.h"

int main()
{ 
	while(1)
	{
		IOWR_ALTERA_AVALON_PIO_DATA(0x6000,0xFFFF);
		alt_putstr("Hello world!\n");
		usleep(100000);
		IOWR_ALTERA_AVALON_PIO_DATA(0x6000,0x0000);
		alt_putstr("Hello world!\n");
		usleep(100000);
	}

  /* Event loop never exits. */
  while (1);

  return 0;
}

進入Debug模式之後程式可以執行,LED燈閃爍,有資料列印,但是資料列印很頓挫,按道理列印頻率是10Hz,不應該頓挫,然後停止Debug,發現LED不閃爍,資料不列印,按下復位按鍵之後發現LED閃爍了3次,然後就停了,說明程式其實還是運行了,但是到後來卡住了似的。我猜測是卡在了jtag_uart列印的地方,所以把alt_putstr函式都註釋了,再次實驗發現退出Debug並復位之後LED可以正常閃爍,並且直接下載程式之後LED也能閃爍。那很有可能就是jtag_uart打印出現了問題,jtag_uart模組的列印是使用jtag的幾根線傳輸列印資料的,和真正的UART是不一樣的。倒是也可以解決這個問題,直接在我的NIOS II系統中新增一個UART IP核即可。但是這個問題還是要解決的,有明白人希望可以解答我的疑惑。

 

 

本文章參考正點原子的新起點FPGA開發指南和新起點Nios II開發指南,非常感謝原子提供的非常豐富的學習資料!