在嵌入式開發中,U-Boot作為最常用的啟動加載程序(Bootloader),承擔著"承上啟下"的關鍵角色:它負責初始化硬件、設置啟動環境,最終引導操作系統內核啟動。而board.c作為板級定制的核心文件,是針對具體硬件平臺的"個性化配置中心"。今天我們就通過一份實際的board.c代碼,聊聊它的核心功能、開發者關注的重點,以及這些代碼在U-Boot階段的關鍵意義。
一、board.c:板級硬件的"初始化總控"

board.c是U-Boot中與具體硬件平臺強相關的代碼文件,幾乎所有針對特定板卡的初始化邏輯、硬件配置、啟動流程定制都會集中在這里。無論是芯片型號識別、內存大小檢測,還是GPIO/ADC等外設的初始化,最終都會通過board.c中的函數落地。
從提供的代碼來看,這份board.c主要面向Rockchip RK3588芯片的板卡,包含了硬件識別、環境變量設置、啟動流程控制等核心功能。我們可以將其核心函數分為幾大模塊:
模塊1:硬件信息識別與環境變量設置
U-Boot需要通過環境變量(如soc、dram_size)向下游(如內核)傳遞硬件信息,這些信息通常由board.c中的函數采集并設置。(這部分是我自己添加的功能,存在環境變量中,供大家參考)
?SBCID():通過ADC識別硬件版本
這是開發者新增的自定義函數,作用是通過ADC(模數轉換器)檢測特定通道的電壓,映射到8個等級并存儲到環境變量SBCID中。
原理:不同硬件版本的板卡可能在ADC引腳接有不同電阻,導致電壓不同,通過電壓范圍匹配即可區分硬件版本。這在多版本板卡的批量生產中非常實用,可自動適配不同硬件配置。
?chips_display():標識芯片型號
直接將環境變量soc設置為"rockchip RK3588",明確告知下游當前使用的芯片型號,方便內核或應用針對性適配。
?dram_sizes():計算并設置內存大小
從全局變量gd->ddr_sizes(U-Boot的全局數據結構,存儲DDR信息)中讀取內存總大小,轉換為"GiB"單位并設置到dram_size環境變量。內核啟動時可通過該變量了解內存配置。
?JusticeID():通過GPIO組合識別硬件ID
另一處自定義邏輯:讀取GPIO139、140、141的輸入電平,組合為3位二進制數(0-7),再映射到特定字符串(如"6"、"3"等),存儲到JusticeID環境變量。
用途:比ADC識別更直接的硬件區分方式,通過GPIO電平組合可快速定位板卡的具體型號或配置(如是否帶外設、接口類型等)。
模塊2:板級初始化入口函數
U-Boot的初始化流程分為多個階段,board.c中的初始化函數會在特定階段被調用,完成硬件準備。
?rk_board_late_init():板級后期初始化
作為弱函數(__weak),它是板級初始化的"匯總點",調用了前面提到的SBCID()、chips_display()等函數,還輸出U-Boot版本。開發者可通過重寫該函數,添加自定義的后期初始化邏輯(如外設使能、狀態檢測)。
?board_init():板級早期初始化
負責調試初始化(board_debug_init())、時鐘探測(clks_probe())、regulators使能(電源管理芯片初始化)等關鍵操作。這是硬件"上電后第一步"的初始化,確保核心外設(如UART、DDR)處于可用狀態。
?board_late_init():系統級后期初始化
比rk_board_late_init()更靠后,負責網絡地址(rockchip_set_ethaddr())、序列號(rockchip_set_serialno())設置,以及USB啟動檢測(boot_from_udisk())、充電顯示(charge_display())等。此時硬件已基本就緒,開始為啟動內核做準備。
模塊3:啟動流程控制與內核引導
U-Boot的最終目標是引導內核啟動,board.c中包含大量與啟動流程相關的邏輯。
?boot_from_udisk():從U盤啟動的適配
檢測USB存儲設備,若存在有效鏡像則設置啟動設備為USB,并調整設備樹(FDT)地址,確保內核能從U盤加載。這在系統升級、救磚場景中非常實用。
?env_fixup():環境變量內存地址調整
根據內存大小(如128M/256M)和是否啟用OP-TEE(安全執行環境),動態調整kernel_addr_r、ramdisk_addr_r等環境變量的地址,避免內存重疊(如內核與ramdisk地址沖突)。
?cmdline_handle():啟動參數(cmdline)處理
根據啟動設備(如SD卡、U盤)和啟動模式(如恢復模式),動態更新bootargs(內核啟動參數)。例如,從U盤恢復時添加usbfwupdate標識,告知內核進入升級模式。
?board_fdt_fixup():設備樹(FDT)修復
設備樹是內核與硬件溝通的"橋梁",該函數負責在啟動前修復設備樹(如CPU兼容性檢查、顯示配置修正),確保內核拿到的設備樹與實際硬件匹配。
模塊4:其他輔助功能
?rockchip_set_ethaddr()與rockchip_set_serialno():生成并設置以太網MAC地址和設備序列號,確保網絡唯一性和設備可標識性。若硬件中未預存(如燒錄到OTP/EFUSE),則自動生成隨機值并存儲。
?board_rng_seed():為內核提供隨機數種子,用于Linux內核的隨機數初始化(尤其Android 14+ GKI要求必須提供),增強系統安全性。
?autoboot_command_fail_handle():自動啟動失敗時的處理邏輯,例如啟動失敗后進入Fastboot模式,方便開發者調試或重刷系統。
二、開發者為什么關注board.c?
對于嵌入式開發者來說,board.c是硬件與軟件的"連接點",其重要性體現在三個方面:
1.硬件初始化的"最后一公里",獲取基本信息
芯片手冊中定義的外設(如GPIO、ADC)需要通過board.c中的代碼實際使能和配置。例如,若ADC通道未正確初始化,SBCID()就無法讀取電壓;若GPIO未設置為輸入模式,JusticeID()就無法獲取正確電平。
2.啟動流程的"定制化入口"
不同產品的啟動需求不同:有的需要優先從U盤啟動,有的需要根據硬件版本加載不同設備樹,這些都需要在board.c中通過函數(如boot_from_udisk()、env_fixup())定制。
3.問題排查的"關鍵線索"
若內核啟動失敗(如內存識別錯誤、外設不可用),很大概率是board.c中的初始化邏輯有問題。例如,dram_sizes()計算錯誤會導致內核看到的內存大小與實際不符,進而引發崩潰。
三、這些代碼在U-Boot階段的意義
U-Boot的核心使命是"為內核啟動鋪路",而board.c中的代碼正是完成這一使命的核心工具:
?硬件就緒:通過board_init()等函數初始化CPU、DDR、時鐘、電源等核心硬件,確保內核啟動時所有外設處于可用狀態。
?環境統一:通過環境變量(如soc、dram_size)向內核傳遞硬件信息,避免內核重復檢測硬件,提高啟動效率。
?流程可控:通過boot_from_udisk()、cmdline_handle()等函數,支持靈活的啟動策略(如多設備啟動、恢復模式),提升產品的易用性和可維護性。
四、自定義代碼的參考價值
文中的SBCID()和JusticeID()是開發者新增的邏輯,這類代碼的參考意義在于:
?硬件差異化處理:在多版本板卡(如同一型號的不同配置)中,通過ADC或GPIO快速區分硬件,自動適配驅動或配置,減少代碼冗余。
?低成本識別方案:無需額外的存儲芯片(如EEPROM),利用現有ADC/GPIO實現硬件識別,降低硬件成本。
?可擴展性示范:展示了如何在U-Boot中添加自定義邏輯并通過環境變量向下傳遞,為其他定制需求(如外設檢測、狀態上報)提供參考。
總結
board.c作為U-Boot的板級核心文件,是硬件初始化的"總導演"、啟動流程的"控制器"、硬件信息的"傳遞者"。理解其函數邏輯,不僅能幫助開發者快速定位啟動問題,更能根據產品需求定制靈活的啟動策略。而其中的自定義代碼(如硬件識別邏輯),則展示了嵌入式開發中"用軟件適配硬件差異"的實用思路,值得大家參考借鑒。
下一次調試U-Boot啟動問題時,不妨從board.c入手——這里大概率藏著解決問題的關鍵!
-
嵌入式
+關注
關注
5198文章
20449瀏覽量
334012 -
內核
+關注
關注
4文章
1468瀏覽量
42874 -
u-boot
+關注
關注
0文章
135瀏覽量
39773
發布評論請先 登錄
U-Boot在AT91RM9200上的移植及啟動分析
【OK210試用體驗】u-boot篇 -- u-boot啟動流程總結
u-boot簡介
解析Rockchip平臺U-Boot核心文件:boot_rkimg.c到底做了什么?
深入解析RK3588 U-Boot板級文件:evb_rk3588.c核心邏輯拆解
解析U-Boot板級核心代碼board.c:從硬件初始化到內核啟動的關鍵一步
評論