天天看點

小白自制Linux開發闆 二. u-boot移植

上一篇:小白自制Linux開發闆 一. 瞎抄原理圖與亂畫PCB  中我們做了一個小型而沒用的開發闆,用的是Licheepi Nano的鏡像,那從本篇開始我們開始自己建構它的靈魂吧。

我們都知道,PC在啟動的時候,首先是進入BIOS,再根據BIOS中配置資訊引導後續的啟動作業系統,比如配置Windows啟動。

而對于嵌入式linux中,并沒有BIOS,這時候就需要一種類似引導程式來處理。于是就有了BootLoader。

BootLoader是一段小程式,可以把它想象成PC機linux上的GRUB/LILO引導程式,可以直接從flash或TF卡中運作,來裝載核心。它可以初始化硬體裝置,進而将系統的軟硬體環境帶到一個合适的狀态,以便為最終調用作業系統做好準備。

1. 嵌入式開發闆的啟動過程

一個嵌入式系統從軟體角度來看分為三個層次:

  •  引導加載程式

           包括固化在晶片中的boot程式(可選)和BootLoader兩大部分,對于固化的boot程式。主要是晶片通過外圍電路連接配接的實際情況選擇讀入程式的位置,比如:通過TF卡或是SPI以及其他方式啟動,至于優先順序這就要具體看晶片的資料手冊,個人沒做過具體測試。

  • linux核心

          特定于嵌入式平台的定制核心

  • 檔案系統

          包括了系統指令和應用程式

 BootLoader Boot Parameters Kernel Root Filesystem

BootLoader啟動過程可分為單階段和多階段(stage1、stage2),其中stage1完成初始化硬體,如CPU寄存器、記憶體控制器,為stage2準備記憶體空間。一般stage1是可以直接在nor flash中運作的,并将stage2複制到記憶體RAM中,設定堆棧,然後跳轉到stage2(從這也可以看出stage2是在RAM中運作的,與stage1不同)

Boot Parameters 顧名思義,就是配置了要啟動核心的參數,包含要加載系統核心相關檔案的位置,要加載到記憶體中的位置,定位到檔案系統的位置,相關輸入輸出的呈現等一系列參數。

kernel 在存放在bootloader之後,對于SoC來說,代碼都需要在RAM中運作,這裡與MCU不一樣的地方就是引入了MMU(記憶體管理單元)。對于MCU而言,由于其執行速度低,是以運作代碼都在ROM中直接運作,而對于Flash而言,其讀取速度遠不及RAM的速度,是以對于運作速度非常快的SoC而言,所有的代碼都需要在RAM中運作。但是這裡有一個問題,RAM掉電資料将會丢失,故代碼儲存不可能放在RAM中,目前所有的嵌入式裝置而言,代碼儲存都是放在ROM中,是以在SoC中運作代碼需要将代碼搬運到RAM中然後再執行。

Root Filesystem 由于其執行過程需要對ROM進行讀寫操作,是以可以不用搬運到RAM中,但是實際過程中核心啟動後會産生一個虛拟的檔案系統,該檔案系統是挂在根檔案系統的關鍵所在,這裡不詳細講解。整體來說,大緻的過程為,嵌入式裝置上電後将執行bootloader,對硬體進行硬體和堆棧初始化,然後搬運核心到RAM中并啟動核心,緊接着挂載根檔案系統。

2. 環境配置與參考項目

系統:Ubuntu 16

編輯器:VSCode

參考項目:Lichee-Pi Nano  

位址:https://wiki.sipeed.com/soft/Lichee/zh/Nano-Doc-Backup/index.html

小白自制Linux開發闆 二. u-boot移植

Lichee-Pi Nano

需要注意的是一定要選擇Nano版本,因為我們開發闆使用的主要晶片和Nano的主要是一緻的,是以後續我們要編譯U-boot,核心都可以參考(bai piao)這裡面的配置。

主要晶片:F1c100s/F1c200s,100s内置32MB DDR1記憶體,200s内置64MB DDR1記憶體,200s貴一點,他們都是QFN88封裝。

ARM926ejs核心,主頻預設408MHz,據了解做産品出貨的一般在600M左右。

帶有100M的SPI接口2個,SDIO接口1個,USB OTG接口,還有CSI攝像頭接口,LCD RGB顯示屏接口,音頻接口,I2C I2S UART PWM等等。

還有就是他們不支援硬體浮點,是以浮點運算使用軟浮點方式。

小白自制Linux開發闆 二. u-boot移植

 F1c100s/F1c200s晶片功能

3.交叉編譯器

   我們通過PC版的Linux自帶的gcc編譯的程式隻能在目前系統架構下的cpu架構(x86)下運作,如果我們想要編寫的程式在嵌入式Linux下運作,那麼就需要用到對應的編譯器。

   我們做的開發闆主要晶片F1C200S,核心為ARM9,其架構使用的是ARMv5架構,是以我們也要選用對應的編譯器,同樣,這樣的編譯器很多,這裡我們使用最常用的arm-linux-gnueabi- ,因為交叉編譯器F1C200S必須高于6.0版本,這裡我們使用7.2版本

     點選下載下傳

    下載下傳較慢時使用下載下傳工具

下載下傳完成後解壓檔案:

tar -vxjf gcc-linaro-7.2.1-2017.11-x86_64_arm-linux-gnueabi.tar.xz      

然後在/usr/local目錄下建立arm-linux-gcc目錄

sudo mkdir /usr/local/arm-linux-gcc      

進入解壓目錄下:

cd gcc-linaro-7.2.1-2017.11-x86_64_arm-linux-gnueabi/      

将該目錄下的所有檔案複制到建立的目錄下

sudo cp -rd * /usr/local/arm-linux-gcc/      

最後需要添加該工具鍊的環境變量使其可以在任何目錄下執行,打開/etc/profile檔案

sudo nano /etc/profile      

在檔案末尾添加以下内容

PATH=$PATH:/usr/local/arm-linux-gcc/bin      

添加完畢,使路徑生效

source /etc/profile      

接下來在終端輸入:

arm-linux-      

然後連按兩次Tab鍵,如圖在表示成功:

小白自制Linux開發闆 二. u-boot移植

如果沒有出現,則進行下面操作,安裝必要的動态連結庫

sudo apt-get install lib32ncurses5 lib32z1      

至此,我們完成了編譯工具的配置。

4. 編譯U-boot

  當Arm開發闆上電以後第一個要加載到記憶體并運作的程式就是BootLoader,BootLoader的同類型程式很多,如U-boot、X-boot、Rt-Thread,這裡我們依然選中最常用的U-boot作為目标(因為其他的我也不會呀),

 最新版本的uboot幾乎包含目前主流的SoC晶片,前面提到本開發闆使用的晶片和licheePI nano相同,大部分硬體也是相容的,為了快速移植該部分,這裡采用licheePI nano的u-boot來進行移植。在終端輸入如下指令克隆u-boot:

git clone https://github.com/Lichee-Pi/u-boot.git -b nano-v2018.01      

克隆完畢檔案會儲存在目前目錄下,進入該目錄,

cd u-boot      

在該檔案夾下有很多分支,我們可以檢視所有分支,使用如下指令:

git branch -a      

現在我們使用的是nano開發闆,是以将目前分支切換到nano分支,指令如下:

git checkout nano-v2018.01u-boot      
小白自制Linux開發闆 二. u-boot移植

 切換到Nano分支

預設的沒有指定交叉工具鍊和架構,是以在編譯之前需要指定交叉工具鍊和晶片架構,u-boot的交叉編譯器在u-boot 的根目錄下中的Makefile檔案中定義了。打開檔案找到CROSS_COMPILE變量,修改為如下:

ARCH=arm
CROSS_COMPILE=arm-linux-gnueabi-      
小白自制Linux開發闆 二. u-boot移植

 配制交叉編譯環境

這樣我們就能使用我們指定的編譯器來編譯u-boot了。

在u-boot項目的config目錄下存在對多種闆子的配置描述檔案,由于每個闆子的外設不同,是以編譯之前必須要對u-boot進行配置。然而配置是一件比較繁瑣的事情,特别是像u-boot這種比較複雜的項目而言,初學者幾乎無法完成。幸運的是對于大部分開發闆而言,config目錄下有其配置好的預設配置檔案。進入config目錄中,然後執行ls檢視目前所有的配置檔案

cd config
ls      
小白自制Linux開發闆 二. u-boot移植

 檢視配制檔案

找到licheepi_nano_defconfig 和 licheepi_nano_spiflash_defconfig,前者表示為TF卡啟動,後者表示從SPI 裝置啟動,因為我們做的小闆隻有從TF卡啟動,是以我們需要使用 licheepi_nano_defconfig 。

現在回到上級目錄,然後執行

make licheepi_nano_defconfig
cd ..
make make licheepi_nano_defconfig      

這樣我們把licheepi_nano_defconfig 作為預設配置項。

接下來我們就可以用圖形界面進行配置了,執行

make menuconfig      

此時出現圖形配置選項,如下圖所示

小白自制Linux開發闆 二. u-boot移植

 u-boot Menuconfig配制,注意紅框中的配置,我們後續要用到。

至此我們的u-boot環境配置就完成了,但是我們還有個問題要解決:如何讓u-boot引導系統

我們在PC端安裝Windows系統的時候往往需要選擇啟動順序,比如需要優先通過光驅或u盤啟動等。

同樣在u-boot中也需要這樣的配置,當然u-boot比PC配置稍微複雜一丢丢。我們前面提到Linux嵌入式系統結構分布中有個Boot Parameters 部分,這部分就是做引導配置的,那怎麼配置呢,總體來說可以分為兩部分:

  1. bootcmd,主要用于描述控制Linux核心檔案以及其他描述檔案加載到記憶體中位置以及啟動Linux核心系統等
  2. bootargs,用于配制檔案系統、序列槽資訊等。

bootcmd

在最開始提到過,核心一般不在flash中運作,這樣就需要将核心搬運到記憶體中,這個過程需要u-boot來完成。對于mmc (TF卡)而言,在u-boot有專門的指令load mmc,該指令可以将mmc中的代碼從flash搬運到指定的位址處。

當u-boot中環境變量bootdelay計數到0時,此時uboot就會開始執行bootcmd中的指令。

bootdelay這個環境變量是一個計數器,當u-boot主體運作完畢後,此時bootdelay該變量的值将會開始遞減,遞減時間為1s,當遞減到0時,此時u-boot将會跳轉到bootcmd處開始執行bootcmd指令,(你可以簡單了解為PC啟動後有一兩秒時間等待,你可以可以通過F8或Enter鍵打斷進入Bios設定的過程,這個等待時間就由u-boot中的bootdelay來控制)。

下面我們需要記住這句指令,這就是我們目前制作的開發闆需要用到的bootcmd全部内容

load mmc 0:1 0x80008000 zImage;load mmc 0:1 0x80c08000 suniv-f1c100s-licheepi-nano.dtb;bootz 0x80008000 - 0x80c08000;      

如果你需要詳細了解這句話那就接着往下看,如果不需要則可以跳到 下面的u-boot參數配置環節

對于上面指令,我們根據分号拆分為3部分:

1> load mmc 0:1 0x80008000 zImage;
2> load mmc 0:1 0x80c08000 suniv-f1c100s-licheepi-nano.dtb;
3> bootz 0x80008000 - 0x80c08000;      

其中兩個 load mmc 指令、一個bootz 指令。

先看第一條:

load mmc 0:1 0x80008000 zImage      

load mmc有三個參數:第一個參數是mmc(TF卡)分區,第二個參數是記憶體中目标位址,第三個參數是源檔案。

即上面的指令意思是将mmc的0:1 分區中的zImage複制到記憶體中的0x80008000位址處。這裡的zimage就是Linux核心,後續會提到該檔案編譯,0:1這個可以這樣了解0表示TF卡(TF卡屬于mmc存儲器的一種),1這表示TF卡的第一個分區(boot分區)後面會提到。

而對于記憶體位置 0x80008000 位址位置,将其了解為預設值就行了。這樣完成了zImage的加載。

下面分析第二條指令:

load mmc 0:1 0x80c08000 suniv-f1c100s-licheepi-nano.dtb      

有了上面的加載zImage的說明,可以很輕松的了解上面的指令意思是将mmc的0:1分區中的suniv-f1c100s-licheepi-nano.dtb檔案加載到記憶體中的0x80c08000位址處。對于suniv-f1c100s-licheepi-nano.dtb 這個檔案,叫做裝置樹檔案,簡單來說就是目前開發闆上面所有外裝置描述檔案,這部分将會在後續核心編譯部分進行詳細說明。

對于第三條指令: 

bootz 0x80008000 - 0x80c08000      

的意思是告訴核心鏡像的起始位址為0x80008000,加載的裝置樹位址為0x80c08000。這裡是告訴cpu從這裡開始啟動Linux, bootz指令的格式是:bootz空格0x80008000空格-空格0x80c08000,注意-左右有空格。

除了bootz 指令外,有些系統裡面還可能存在一個叫做bootm指令,這是是對沒有使用裝置樹核心的鏡像啟動指令,早期版本的核心沒有引入裝置樹,是以對于早期的核心一般使用的是bootm,其指令格式為bootm核心位址,比如bootm x0x30008000,意思是從0x30008000開始啟動核心,啟動核心的過程其實是将pc指針指向該位址,這樣處理器就會從該位址處運作代碼。

 這裡我們就完成了bootcmd的說明,接下來我們看另外一個參數。

bootargs

bootargs也是u-boot環境變量中一個非常重要的變量,上面已經講解了核心的啟動可以通過bootcmd來完成,那接下來核心啟動完畢後必須挂在根檔案系統(rootfs)。但是核心并不知道根檔案系統的具體位置,我們必須要告訴根檔案的位置後核心才能将其挂載,這時就需要有bootargs變量。該變量的作用是告訴核心根檔案系統的位置和屬性以及必要的配置,

console=ttyS0,115200 panic=5 rootwait root=/dev/mmcblk0p2 earlyprintk rw      

同上面分析的方法一樣,我們依然将這部分指令拆成幾部分來說明。這裡需要說明的是,這部配置設定置資訊是由u-boot 直接按照參數字元串方式提供給Linux核心,然後由Linux核心進行執行的,這也說明裡為什麼格式與bootcmd配置方式不一緻。

console=ttyS0,115200 表示終端為ttyS0即序列槽0,波特率為115200;

panic=5 字面意思是恐慌,即linux核心恐慌,其實就是linux不知道怎麼執行了,此時核心就需要做一些相關的處理,這裡的5表示逾時時間,當Linux卡住5秒後仍未成功就會執行Linux恐慌異常的一些操作。

rootwait 該參數是告訴核心挂在檔案系統之前需要先加載相關驅動,這樣做的目的是防止因mmc驅動還未加載就開始挂載驅動而導緻檔案系統挂載失敗,是以一般bootargs中都要加上這個參數。

root=/dev/mmcblk0p2 表示根檔案系統的位置在mmc的0:2分區處,/dev是裝置檔案夾,核心在加載mmc中的時候就會在根檔案系統中生成mmcblk0p2裝置檔案,這個裝置檔案其實就是mmc的0:2分區(這裡對應TF卡的第二個分區:rootfs),這樣核心對檔案系統的讀寫操作方式本質上就是讀寫/dev/mmcblk0p2該裝置檔案。

earlyprintk 參數是指在核心加載的過程中列印輸出資訊,這樣核心在加載的時候終端就會輸出相應的啟動資訊。rw表示檔案系統的操作屬性,此處rw表示可讀可寫。

5.u-boot參數配置 

make menuconfig      

 選中   Enable boot arguments 按空格選中,下面會顯示:() Boot arguments

然後選中Boot arguments ,按回車,進入配置視窗,接下來上面解釋過的bootargs 參數資訊:

console=ttyS0,115200 panic=5 rootwait root=/dev/mmcblk0p2 earlyprintk rw      
小白自制Linux開發闆 二. u-boot移植

 配置bootargs資訊

然後按Tab鍵選中<OK>,儲存并進入主菜單。

同理配置:Enable a default value for bootcmd 按空格選中,下面會顯示:() bootcmd value 配置項,

選中bootcmd value 進入配置界面,輸入bootcmd指令:

load mmc 0:1 0x80008000 zImage;load mmc 0:1 0x80c08000 suniv-f1c100s-licheepi-nano.dtb;bootz 0x80008000 - 0x80c08000;      
小白自制Linux開發闆 二. u-boot移植

 配置bootcmd參數

 按Tab鍵選中<OK>,儲存并進入主菜單。

6.u-boot編譯與燒錄

先儲存圖形配置界面後推出界面,在終端執行make -j4即可對整個u-boot進行編譯。

make -j4      
小白自制Linux開發闆 二. u-boot移植

編譯u-boot

make -j4後面的-j4表示4個核心進行編譯,若電腦的處理器是2核心,請使用make -j2進行編譯。

編譯完成後會在目前目錄生成u-boot-sunxi-with-spl.bin燒錄檔案。

小白自制Linux開發闆 二. u-boot移植

根目錄下找到 u-boot-sunxi-with-spl.bin 檔案

該檔案就是我們最終要燒錄的二進制檔案。

在目前目錄下會有一個隐藏的檔案.config,該檔案是u-boot編譯後根據各個選項産生的配置檔案,這個配置檔案記錄了所有配置選項的宏開關,編譯的時候是根據最終的.config檔案來進行編譯的,當然編譯前是需要有腳本解析.config檔案然後進行相應的編譯。

燒錄到TF卡

隻要将u-boot-sunxi-with-spl.bin燒錄到tf卡的8k偏移處位址就可以了,燒錄步驟如下:使用dd指令進行塊搬移:

sudo dd if=u-boot-sunxi-with-spl.bin of=/dev/sdb bs=1024 seek=8      

該指令中:

       if    檔案名:輸入檔案名,預設為标準輸入。即指定源檔案。< if=input file >

       of   檔案名:輸出檔案名,預設為标準輸出。即指定目的檔案。< of=output file >

       bs  bytes:同時設定讀入/輸出的塊大小為bytes個位元組。

       seek  blocks:從輸出檔案開頭跳過blocks個塊後再開始複制。

這裡的輸出檔案(of)為主機電腦的/dev/sdb檔案,也就是TF卡,這裡也展現了Linux一切皆檔案的思想。

/dev/sdb 這個可以用gparted 軟體檢視,該軟體可以直接用指令安裝即可:

sudo apt-get install gparted      

此時在Ubuntu下面可以看到如下軟體:

小白自制Linux開發闆 二. u-boot移植

安裝好GParted軟體

 打開軟體

小白自制Linux開發闆 二. u-boot移植

GParted

在右上角可以看到兩個硬碟,/dev/sda 為本地硬碟,/dev/sdb 是我們将要寫資料的TF(當然這隻是墨雲自己的配置使然,具體情況請根據實際情況而定),是以這裡的of=/dev/sdb 燒錄到8k偏移位址處是指絕對位址,這個絕對位址指的是TF卡的實體位址。這8K的值是由F1C200S 中固化的啟動代碼決定的,是以照抄即可。

小白自制Linux開發闆 二. u-boot移植

燒寫u-boot

然後我們正常退出TF卡,然後插入我們自制的開發闆,通過USB線連接配接電腦,

小白自制Linux開發闆 二. u-boot移植

連接配接開發闆

打開電腦中的指令行工具,我這裡使用Xshell,

打開Xshell,建立連接配接:

小白自制Linux開發闆 二. u-boot移植

配置名稱 ,協定選擇Serial,

小白自制Linux開發闆 二. u-boot移植

 配置序列槽

通過下拉選中com端口,波特率為115200,其他預設即可,點選确定,然後輕按兩下主界面左側會話管理中的剛建立的會話,此時進入連接配接狀态。

因為在你插入USB通電的時候開發闆就已經啟動了,是以當你打開序列槽連接配接的時候可能未必會看到資訊,是以按一下重新開機鍵,就可以看到如下的輸出資訊了,這就是我們的u-boot,執行到u-bbot計數完成後會産生錯誤,那是因為我們還沒有進行系統核心的移植,是以預設就會進入u-boot指令模式。

小白自制Linux開發闆 二. u-boot移植

 啟動資訊

 輸入pri指令列印環境變量的所有值,可以找到已經配置的bootcmd 和bootargs

小白自制Linux開發闆 二. u-boot移植

 pri指令結果

至此完成了u-boot移植的全部内容,對于u-boot的移植方法,在後續移植Linux核心和檔案系統時都會用到,都是大同小異的,是以有了本篇的說明,之後操作将會非常簡單。

而關于u-boot的内容事實上非常的複雜繁瑣,有興趣的可以自行去了解到,畢竟作為一個小白的我初衷隻是先讓小闆先跑起來。

參考資料

Lite200  (lishanwen) --  https://lishanwen.cn/index.php/2021/07/03/lite200/

全志F1C200S F1C100S 介紹 ( 迪卡魏曼依奇君 ) https://blog.csdn.net/tunqimai9331/article/details/95938903

荔枝派Nano 全流程指南 (矽速科技) https://wiki.sipeed.com/soft/Lichee/zh/Nano-Doc-Backup/index.html

NetAnalyzer下載下傳位址

NetAnalzyer交流群:39753670 (PS 隻提供交流平台,群主基本不說話^_^)

[轉載請保留作者資訊  作者:馮天文 ]