2016年4月28日 星期四

用 Beaglebone black 學 Linux 驅動程式 (一) -- 工具鏈

本篇文章主要以 crosstool-ng 來建置 Beaglebone black 的工具鏈並且挑選接下來實驗所要使用的版本:

1. 下載並建置基本環境:

wget http://crosstool-ng.org/download/crosstool-ng/crosstool-ng-1.20.0.tar.bz2
cd crosstool-ng-1.20.0
./configure --enable-local
make 
make install 
完成後可以看到當前目錄下出現 ct-ng 的執行檔

2. 挑選工具鏈版本:

./ct-ng list-samples <- 用來查詢可以選擇的工具鏈版本
 因為 Beaglebone black 上的 SoC (AM3358) 是 arm cortex A8 系列,可以下指令

./ct-ng show-arm-cortex_a8-linux-gnueabi 

顯示工具鏈的詳細資訊


選定好要建置的工具鏈後(arm-cortex A8),輸入

./ct-ng arm-cortex_a8-linux-gnueabi

完成後可以看到當前目錄下出現 .config 隱藏檔,檔案中主要描述工具鏈的詳細功能設定。

3.  開啟硬體浮點功能:

因為 arm-cortex_a8-linux-gnueabi 版本的工具鏈並沒有將硬體浮點運算的功能打開,因此我們要先修改 menuconfig 開啟硬體浮點加速支援,


並修改 .config 來對工具鏈名稱添加hf後綴字串,修改後的版本如下:

4. 建置工具鏈

之後便可以用以下指令開始建置工具鏈

./ct-ng build

建置過程大約需要 50 分鐘左右。完成後,可以在家目錄下看到 x-tools 資料夾,就是工具鏈所在的位置。

之後可以使用指令

export PATH=~/x-tools/arm-cortex_a8-linux-gnueabihf/bin:$PATH
  
把工具鏈所在執行檔位置加入環境變數中,就可以在任意目錄下執行工具鏈了。


浮點運算的 soft softfp 與 hard 差異探討 

要解答這個問題就必須要從gcc編譯指令跟一些組合語言談起了。
現在有一個浮點運算的程式 (test.c),程式碼如下:

#include <stdio.h>

float f_add(float a, float b)
{
return a + b;
}

float f_sub(float a, float b)
{
return a - b;
}

float f_mul(float a, float b)
{
return a * b;
}

float f_div(float a, float b)
{
return a / b;
}

int main(int argc, char* argv[])
{
float a = 1.0;
float res;
int run_count, i, j;

if (argc != 2) {
printf("How many loop you would like to run?\n");
return 1;
}

run_count = atoi(argv[1]);

for (i = 0; i < run_count; i++) {
for (j = 0; j < 1000; j++) {
res = f_add(a, 2.5);
res = f_sub(res, 1.5);
res = f_mul(res, 2.5);
res = f_div(res, 5.0);
}
}

return 0;
}

我們用剛做好的交叉編譯器(工具鏈)編譯出三個不一樣的執行檔:


arm-cortex_a8-linux-gnueabi-gcc -static test.c -o soft  (沒有浮點加速)

arm-cortex_a8-linux-gnueabi-gcc -static -mfloat-abi=softfp -mfpu=vfp test.c -o softfp 
(softfp 型浮點加速)

arm-cortex_a8-linux-gnueabihf-gcc -static -mfloat-abi=hard -mfpu=vfp test.c -o hard 

(hard 型浮點加速)

*註:由於 soft, softfp 在當開啟hardware FPU選項後便無法使用,因此本次實驗建置了關閉hardware FPU (arm-cortex_a8-linux-gnueabi) 以及開啟 hardware FPU (arm-cortex_a8-linux-gnueabihf) 的兩種工具鏈。

於是我們將分別將不同的執行檔反組譯

arm-cortex_a8-linux-gnueabi-objdump -S soft > soft.S
arm-cortex_a8-linux-gnueabi-objdump -S softfp > softfp.S
arm-cortex_a8-linux-gnueabi-objdump -S hard > hard.S


1. soft 版本與 softfp 比較

左邊的部份是 f_add 函式在 soft 版本的組合語言,可以看到在這版本中浮點加法是透過__addsf3 函式實作出來,__addsf3 裡面是利用定點指令來完成浮點加法,因此函式中也包含了許多指令。右邊的部份則是 softfp 版本的組合語言,可以看到浮點加法是透過 vadd.f32的指令完成,因此可以預期 softfp 版本會有比較好的效能。

2. softfp 版本與 hard 比較



左邊的部份是 f_add 函式在 soft 版本的組合語言,右邊是 hard 版本的組合語言。
 softfp 版本與 hard 版本最大的差異在於當 f_add 函式回傳時,hard 可以利用浮點暫存器 s0回傳函式執行結果,而 softfp 在函式回傳前必須將結果存到暫存器 r0,因此相較於 softfp 版本 hard 版本可以少掉一個 mov 指令。

在 Vexpress-A9 模擬器實驗 



可以看到使用支援硬體浮點加速(綠線&藍線)的部份,在 10 萬個迴圈下程式執行的效率快了將近 1.6 倍。

所以當在開發的程式需要進行大量的浮點運算的話,這時如果運行平台上又有支援浮點硬體加速(FPU),可以在編譯程式的時候稍微留意一下,往往會有不錯的效果。

使用硬體浮點就一定會加速?

先前預期在使用硬體浮點的狀況下,執行效率會提昇的理由是基於程式的指令數比較少。但事實上這樣的假設是有問題的,因為每個單一指令的週期數不一定相同。所以一但有某個平台硬體浮點運算的指令週期大於使用定點指令作浮點運算的指令集合,那麼我們並沒有辦法在使用硬體浮點處理的狀況下達到效能提昇。很不幸的,這樣的狀況正好發生在 beaglebone black 的實驗平台上,同樣的實驗結果如下:
可以看到在 beaglebone black 上 hard 版本跟 softfp 版本的執行效能反而比 soft 版本還要差。盡信書不如無書,硬體浮點指令是否一定等於高效率在這兩次的實驗上剛好得到了解答。

因此,之後的實驗我們都會使用 arm-cortex_a8-linux-gnueabi 的工具鏈編譯程式。

參考資料: 

1. 关于armhf (hard-float ABI for ARM)
http://www.claudxiao.net/2012/02/armhf/

2. Mastering Embedded Linux Programming, ISBN: 978-1-78439-253-6, p.13-29
https://it-ebooks24.com/ebook/mastering-embedded-linux-programming