MapleBoard RISC-V 工具鏈範例使用教學

下載Mpb-toolchain-Example

尚未安裝Mapleboard RISC-V工具鏈的朋友請先前往Mapleboard RISC-V工具鏈建置與使用安裝工具鏈。

已經完成上一步的朋友,請前往Github下載工具鏈範例程式:https://github.com/MapleBoard/mpb-toolchain-example

下載的方式如同上一篇說明,使用git clone指令即可。

在這個專案中,包含GD32VF103的製造商GigaDevice所提供的範例程式碼(Example),如GPIO, ADC, I2C, CAN等等界面的使用範例,以及GD32所需的韌體(Firmware),如暫存器(Register)的位址定義, RISC-V架構所需要的標準韌體等等。

第三個資料夾”src”內則是我們主要開發的資料夾,其中的”main.c”是我們要寫的應用程式主程式,其他的檔案則是主程式會呼叫到的函式或標頭檔(header)。

除了C語言會用到的.h檔以及.c檔外,我們還可以看到一個沒有附檔名(Extension name)的”Makefile”檔案,這個檔案是幫我們編譯與使用工具鏈的最大功臣。

使用Mpb-toolchain-Example

將範例程式下載好後,打開終端視窗,並移動到Mpb-toolchain-Example資料夾內的src資料夾下:

$ cd ~/Mpb-toolchain-Example/src

直接輸入:

$ make

系統便會自動找尋當前資料夾是否有名為”makefile”或”Makefile”的檔案,找到後就執行Makefile,找不到就會報錯。
這時可以看到終端視窗顯示如左的畫面,一堆”CC” “AS” “LD” 等過程。

以及最後出現OBJCOPY /build/gd32vf103.bin 這個檔案就是可以在GD32 RISC-V Nano/Pico上執行的機器碼,我們只要使用適當的工具,如dfu-util,上傳機器碼到開發板上,就能夠讓開發板依照我們想要的模式動作。

自動化工具-Makefile

或許你會好奇:剛剛只是輸入一個make指令,但是卻執行一串的動作,這是怎辦到的?

首先,make指令也不是憑空出現的,在一開始安裝Linux kernel時,make因為它強大的功能與廣泛的彈性,它早已被納入系統安裝的套件包裡,所以Linux作業系統的電腦都會內建make指令。

如同剛剛提到的,在鍵入make指令後,make會去尋找當前資料夾是否有名為Makefile或者makefile的檔案,找到後就會執行,這也是一開始第一步要先把終端機視窗工作目錄移動到”Mpb-toolchain-Example/src”底下的原因。

Makefile有專屬的Makefile語法,詳細的說明可以參考Makefile Wiki

左圖是GNU專案中,名為Automake的自動化建置軟體的建構流程。

這是以龐大架構的應用程式為例,包還眾多參數與各式各樣的設定檔,從最底層的硬體資料分析,硬體參數設定,自動參數設定,設定狀態,建構中繼檔案,產生中繼檔案,一直到設定軟體參數,建構軟體,產生軟體可執行檔等等一連串繁瑣的過程。

但我們開發GD32 RISC-V Nano/Pico並不需要如此複雜。開發板板載的硬體都已經是固定型號與特性,只需要將設定好的參數檔與主程式進行連結即可。

初學者可能還是聽的霧煞煞(不清楚的台語),因此接下來的篇幅會介紹我們的Makefile架構,但礙於篇幅,相關的Makefile語法與變數等等細節就無法介紹到,我們會提供推薦的連結供使用者參考。

Makefile-引入函式庫

首先我們手上有名為”main.c”的主程式,他的功能是讓 三種不同顏色的LED燈亮滅閃爍,互相間隔一秒。

但在main.c中,我們使用到的gd_eval_led_on()這個函式是在gd32vf103v_eval.h的標頭檔內。

在C語言中,要引入標頭檔很容易,輸入#include “gd32vf103v_eval.h”即可,

但是C語言最後編譯的時候,如果編譯器沒辦法找到標頭檔,就會出現編譯錯誤。

最簡單的方就是把所有標頭檔與相依的檔案跟主程式放在同一個資料夾,但是這樣做的結果就會造成資料夾一片混亂,許許多多.c與.h檔案全部混在一起,光是使用IDE就覺得很煩躁,更何況使用終端視窗開發的使用者。

因此把標頭檔與相依程式另外放在不同資料夾是較為明智的作法。

但這樣問題來了,編譯器如何得知相依檔案的路徑?

答案就在Makefile中。

左圖是Mpb-toolchain-Example/src下的makefile檔案,其中第36 行開始有一個變數名為’C_SOURCES’,它把所有GD32VF103會用到的相依檔案全部包起來,下方’ASM_SOURCES’也一樣,它把所有組合語言含式庫也全部容納進一個變數裡。

這樣做的好處就是不需要每個檔名手動輸入,make會自動依照所填寫的路徑中抓取副檔名是.c的所有檔案,並放到C_SOURCES內。

下面第88行的C_INCLUDES也是一樣概念。

這裡用到一個名為”wildcard”的Makeflie內建變數,詳細的說明可以參考

GNU make: Wildcard Function

Makefile-設定參數

引入函式庫後,Makefile就知道要用哪些檔案來建構main.c,一下一步驟就是自動編譯程式。

我們可以依照不同的目標平台,用相同的原始碼(Source Code),但選擇不同的工具鏈編譯出不同目標平台的機器碼,這就是開放原始碼的好處,同一套軟體,同樣的原始碼,可以使用不同工具鏈建構出不同硬體平台(如 x86_64, ARM, MIPS, RISC-V等)各自的機器碼。

在Makefile第59行中可以看到一個名為PREFIX的變數,我們使用Mapleboard-toolchain,因此後面填入”riscv32-mapleboard-elf-“作為指令的前綴字元。

Make是能夠模擬使用者輸入終端視窗的軟體,因此我們在終端視窗能夠使用的指令,在Makefile內都能夠被make執行。

下面接續的是把gcc, gcc -x -assembler-with-cpp等等參數用更簡單的符號表示,以利後續Makefie的撰寫不會過於冗長又無意義。

下方第97行的CFLAGS則是編譯器(gcc)的旗標設定,關於gcc的詳細使用說明可以參考GNU GCC Online Documentation

Makefile-編譯並建置

完成編譯器前綴設定與其標設定後,就可以進入最重要的步驟:編譯與建置

從第121行開始,是Makefile建置main.c的過程,首先makefile會先從剛剛提到的C_SOURCES路徑內所有.c檔案使用CC(編譯器)編譯成.o檔案,並放在BUILD_DIR內,接下來,再將這些.o檔案透過AS(組語轉換器)檔案轉換成組合語言檔案(.s檔),然後將每個.o檔案使用LD(連結器)將彼此連結在一起,並轉換成.elf 檔案。OD與​​SZ分別代表物件檔轉換工具以及檔案大小檢查工具。

最後,利用HEX與BIN將.elf檔案轉換成機器能夠執行的二進制機器碼。

在上述過程中,會產生很多的中繼檔案,這些檔案被存放在src資料夾下的.deps隱藏資料夾內,有興趣的使用者也可以去看一下那些中繼資料的內容,大部分都是相依文件的路徑,用來告訴編譯器要去哪裡找到檔案。

這個Makefile檔案除了

$ make

指令能用之外,也可以輸入

$ make clean ,$ make flash, $ make dfu, $ make uart

等等指令,指令的相關說明在src內的README.md 檔案中有詳細說明。

讀到這裡,是不是覺得Makefile是一個很神奇的東西呢?不僅可以用一個指令就囊括好幾十個指令,甚至也可以進行轉檔,建置等等動作。

Makefile-上傳程式碼

我們終於把我們的主程式:  main.c  轉換成GD32 RISC-V Nano/Pico能夠讀懂的機器碼了,但是又要怎上傳到開發板上呢?

透過板子上的USB-Type C接口,與電腦連接卻一點反應也沒有,需要使用什麼工具?  該怎麼讓GD32 RISC-V Nano/Pico進入上傳模式呢?

請繼續閱讀:Dfu-util安裝與使用教學