小白自制Linux開發板 二. u-boot移植 小白自制Linux開發板 一. 瞎抄原理圖與亂畫PCB

上一篇:小白自制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

  

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等等。

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

 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鍵,如圖在表示成功:

如果沒有出現,則進行下面操作,安裝必要的動態鏈接庫

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

 切換到Nano分支

默認的沒有指定交叉工具鏈和架構,因此在編譯之前需要指定交叉工具鏈和芯片架構,u-boot的交叉編譯器在u-boot 的根目錄下中的Makefile文件中定義了。打開文件找到CROSS_COMPILE變量,修改爲如下:

ARCH=arm
CROSS_COMPILE=arm-linux-gnueabi-

 配製交叉編譯環境

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

在u-boot項目的config目錄下存在對多種板子的配置描述文件,由於每個板子的外設不同,因此編譯之前必須要對u-boot進行配置。然而配置是一件比較繁瑣的事情,特別是像u-boot這種比較複雜的項目而言,初學者幾乎無法完成。幸運的是對於大部分開發板而言,config目錄下有其配置好的默認配置文件。進入config目錄中,然後執行ls查看當前所有的配置文件

cd config
ls

 查看配製文件

找到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

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

 

 

 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

  

 配置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;

 配置bootcmd參數

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

 

6.u-boot編譯與燒錄

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

make -j4

編譯u-boot

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

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

根目錄下找到 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下面可以看到如下軟件:

安裝好GParted軟件

 打開軟件

GParted

在右上角可以看到兩個硬盤,/dev/sda 爲本地硬盤,/dev/sdb 是我們將要寫數據的TF(當然這只是墨雲自己的配置使然,具體情況請根據實際情況而定),因此這裏的of=/dev/sdb 燒錄到8k偏移地址處是指絕對地址,這個絕對地址指的是TF卡的物理地址。這8K的值是由F1C200S 中固化的啓動代碼決定的,所以照抄即可。

燒寫u-boot

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

連接開發板

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

打開Xshell,新建連接:

配置名稱 ,協議選擇Serial,

 配置串口

通過下拉選中com端口,波特率爲115200,其他默認即可,點擊確定,然後雙擊主界面左側會話管理中的剛建立的會話,此時進入連接狀態。

因爲在你插入USB通電的時候開發板就已經啓動了,所以當你打開串口連接的時候可能未必會看到信息,所以按一下重啓鍵,就可以看到如下的輸出信息了,這就是我們的u-boot,執行到u-bbot計數完成後會產生錯誤,那是因爲我們還沒有進行系統內核的移植,所以默認就會進入u-boot命令模式。

 

 啓動信息

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

 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

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