目錄構建riscv64-unknown-linux-musl編譯工具鏈直接下載官方工具鏈嘗試自己編譯T-head Gcc下載編譯binutils編譯交叉gcc編譯musl手動合成fip.bin和boot.sd編譯u-boot生成cvi_board_memmap.h,cvipart.h和imgs.h繼 ...
目錄
Milk-V Duo是一個基於CV1800B晶元的超緊湊嵌入式開發平臺。它可以運行Linux和RTOS,為專業人士、工業ODM、AIoT愛好者、DIY愛好者和創作者提供了一個可靠、低成本和高性能的平臺。在這樣一個平臺上跑上buildroot就變得意義非凡。
構建riscv64-unknown-linux-musl編譯工具鏈
直接下載官方工具鏈
MilkV Duo官方給了我們工具鏈的下載地址,但是由於存sdk的倉庫內容被清空,不再被維護,並且下載地址被隱藏在release裡邊,太難找了,所以我就假裝我沒找到官方的工具鏈,看看軟體源里有沒有riscv64的編譯器。
嘗試自己編譯T-head Gcc
使用
apt search riscv64
一搜,還真有結果。
binutils-riscv64-linux-gnu/jammy-updates,jammy-security 2.38-4ubuntu2.3 amd64
GNU binary utilities, for riscv64-linux-gnu target
gcc-riscv64-linux-gnu/jammy 4:11.2.0--1ubuntu1 amd64
GNU C compiler for the riscv64 architecture
於是安裝了這倆,然後做了點測試,發現它並不支持平頭哥c906fdv架構,所以我們還是需要使用官方給出的編譯鏈。但是由於我假裝看不見官方的release下載地址,就抱著play的心態,嘗試自己構建一個工具鏈。riscv64是我們的目標架構,unknown是vendor,即廠商名,這裡被指定為unknown。linux就是OS平臺,musl是一種新的c庫。既然官方用musl,那我也跟著用吧。
平頭哥魔改過的包含c906fdv處理器的gcc源代碼在T-head-Semi/gcc,我們把它clone下來
git clone https://github.com/T-head-Semi/gcc.git
下載編譯binutils
Binutils是一組開源的二進位工具集合,用於創建、操作和分析可執行文件、目標文件和庫文件。它由一系列工具組成,包括彙編器(as)、鏈接器(ld)、目標文件工具(objdump、readelf等)和調試器(gdb)等。這些工具被廣泛用於開發和維護各種操作系統、編譯器和軟體工具鏈。Binutils可以用於多種體繫結構和操作系統,包括x86、ARM、MIPS、PowerPC等,支持多種操作系統如Linux、Windows、macOS等。它們提供了一些核心功能,如將彙編代碼轉換為可執行文件、將目標文件鏈接為可執行文件、提取和修改目標文件的符號和節(sections)信息,以及執行調試操作。通過使用Binutils,開發人員可以進行底層的二進位文件操作和分析,包括反彙編、符號表查看、調試信息提取等。它們對於編譯器開發、操作系統開發、嵌入式系統開發等方面非常有用,也是構建軟體工具鏈的重要組成部分。
在編譯GCC(GNU Compiler Collection)之前,需要準備Binutils,原因如下:
-
鏈接器(ld):GCC在編譯過程中需要使用鏈接器將編譯後的目標文件(.o文件)鏈接成最終的可執行文件。鏈接器負責解析符號引用、符號重定位以及生成最終可執行文件的各個節(sections)。Binutils中的ld是一個功能強大的鏈接器,被GCC用作預設的鏈接器。
-
彙編器(as):GCC在編譯源代碼時,會將源代碼翻譯成彙編代碼。然後,彙編器將彙編代碼轉換成相應的機器碼,並生成目標文件。Binutils中的as是GCC預設使用的彙編器。
-
目標文件工具(objdump、readelf等):在編譯過程中,可能需要查看、分析和調試目標文件的內容。Binutils提供了一些工具,如objdump和readelf,用於查看目標文件的符號表、節表、重定位信息等。這些工具對於調試編譯器生成的目標文件或庫文件非常有用。
因此,為了編譯GCC,需要安裝並準備Binutils,以確保編譯器能夠正確地使用其中的鏈接器、彙編器和目標文件工具。Binutils提供了一套強大的二進位工具,為GCC的編譯過程提供了必要的支持。
我們不可以直接使用apt安裝的binutils,因為預設的binutils提供的ld的生成的目標文件是x86架構的。如果直接安裝binutils-riscv64-linux-gnu,我們還需要手動把頭文件複製一遍,把二進位文件也改名一遍,這樣做很麻煩,所以我們應當直接make -j64&&make install
,這樣還能順便把頭文件都給我們塞好。
首先我們把binutils的代碼wget下來,然後創建一個build目錄,專門用於編譯,再創建一個目錄,用於存放install的結果。
# 進入binutils目錄
cd binutils
# 創建build目錄
mkdir build
cd build
# 配置binutils,prefix是install的時候生成文件的存放地址
../configure --prefix=/opt/riscv --target=riscv64-unknown-linux-musl
make -j64&&make install
這樣之後,/opt/riscv
目錄下就出現了bin、include等不少文件。
編譯交叉gcc
同樣,我們進入平頭哥gcc的目錄,創建一個build目錄,然後配置
../configure --prefix=/opt/riscv --target=riscv64-unknown-linux-musl --with-sysroot=/opt/riscv
make all-gcc
make install-gcc
在編譯GCC時,--with-sysroot
選項用於指定系統根目錄(sysroot)。系統根目錄是一個包含完整系統文件層次結構的目錄,通常用於交叉編譯環境中。
設置--with-sysroot
選項的目的是告訴GCC編譯器在指定的系統根目錄下查找頭文件、庫文件和其他系統資源,以便正確地構建和鏈接目標程式。這在交叉編譯環境中特別重要,其中編譯器和目標平臺的操作系統不匹配。
通過將--with-sysroot
選項設置為系統根目錄的路徑,GCC將使用該路徑作為基準,查找和引用目標平臺所需的系統文件。這包括標準C庫、頭文件、共用庫、鏈接器腳本等。使用--with-sysroot
選項可以確保編譯器能夠正確地定位和使用目標平臺的系統資源。
在交叉編譯環境中,你還需要設置其他相關的選項,如--target
選項指定目標平臺體繫結構,以及--prefix
選項指定安裝位置等。這些選項的設置將根據你的具體需求和目標平臺而有所變化。
需要註意的是,--with-sysroot
選項並不適用於本地編譯,因為本地編譯時GCC可以直接使用主機操作系統的系統資源。它主要用於交叉編譯環境,以確保編譯器在正確的系統根目錄下查找和使用目標平臺的系統文件。
在編譯的過程中,我們遇到了報錯,提示缺少/opt/riscv/usr/include的頭文件。因此,我們還需要編譯musl
編譯musl
去官網下載musl的代碼,然後編譯。
mkdir build
cd build
../configure --prefix=/opt/riscv
make
make install
此時繼續編譯gcc,仍然會遇到報錯,提示缺少/opt/riscv/usr/include的頭文件。我們把/opt/riscv/include下的文件直接複製過去就行。
然後再次make install-gcc
此時就完成了工具鏈的編譯
手動合成fip.bin和boot.sd
為了扔進buildroot,先要摸透每個流程。
編譯u-boot
官方sdk給出了魔改過的uboot,有關的代碼也scatter在不知道什麼地方。根據目前的探查,我們需要把build
目錄下的
build/boards/cv180x/cv1800b_milkv_duo_sd/u-boot/cvi_board_init.c
複製到u-boot/board/cvitek/
目錄下build/boards/cv180x/cv1800b_milkv_duo_sd/u-boot/cvitek.h
複製到u-boot/include/cvitek/
目錄下build/boards/cv180x/cv1800b_milkv_duo_sd/u-boot/cvitek_cv1800b_milkv_duo_sd_defconfig
複製到u-boot/configs
目錄下
生成cvi_board_memmap.h
,cvipart.h
和imgs.h
執行make -j64
,報錯,提示沒有cvi_board_memmap.h
。進入build目錄,找到scripts/mmap_conv.py
,執行
./build/scripts/mmap_conv.py --type h ./build/boards/cv180x/cv1800b_milkv_duo_sd/memmap.py u-boot/include/cvi_board_memmap.h
繼續生成cvipart.h
。
python3 build/tools/common/image_tool/mkcvipart.py build/boards/cv180x/cv1800b_milkv_duo_sd/partition/partition_sd.xml u-boot/include
繼續生成imgs.h
python3 build/tools/common/image_tool/mk_imgHeader.py build/boards/cv180x/cv1800b_milkv_duo_sd/partition/partition_sd.xml u-boot/include
這個cvi_board_memmap.h
在後續開發中也很重要,要記住位置。
繼續編譯u-boot
執行make -j64 CROSS_COMPILE=riscv64-unknown-linux-gnu-
,編譯時發現彙編器不認csrr寄存器。
board/cvitek/cv180x/board.c: Assembler messages:
board/cvitek/cv180x/board.c:327: Error: unrecognized opcode `csrr a5,0xc01', extension `zicsr' required
所以我們進入arch/riscv/Makefile
,做出以下改動
ARCH_EX = _zicsr_zifencei
ARCH_FLAGS = -march=$(ARCH_BASE)$(ARCH_A)$(ARCH_C)$(ARCH_EX) -mabi=$(ABI) \
-mcmodel=$(CMODEL)
再次編譯,發現又報錯了
common/command.c:586:20: error: conflicting types for ‘cmd_process’ due to enum/integer mismatch; have ‘enum command_ret_t(int, int, char * const*, int *, ulong *)’ {aka ‘enum command_ret_t(int, int, char * const*, int *, long unsigned int *)’} [-Werror=enum-int-mismatch]
586 | enum command_ret_t cmd_process(int flag, int argc, char *const argv[],
| ^~~~~~~~~~~
In file included from common/command.c:13:
include/command.h:234:5: note: previous declaration of ‘cmd_process’ with type ‘int(int, int, char * const*, int *, long unsigned int *)’
234 | int cmd_process(int flag, int argc, char *const argv[], int *repeatable,
| ^~~~~~~~~~~
找到common/command.c:586
,把返回值類型改成int
,問題解決。繼續編譯,又遇到
riscv64-buildroot-linux-musl-ld.bfd: warning: u-boot has a LOAD segment with RWX permissions
OBJCOPY u-boot.srec
OBJCOPY u-boot-nodtb.bin
SYM u-boot.sym
make[2]: *** No rule to make target 'arch/riscv/dts/_.dtb', needed by 'dtbs'. Stop.
make[1]: *** [dts/Makefile:45: arch-dtbs] Error 2
make: *** [Makefile:1145: dts/dt.dtb] Error 2
make: *** Waiting for unfinished jobs....
我們首先把build/boards/
下的設備樹都複製到uboot/arch/riscv/dts
。
build/boards/default/dts/cv180x/cv180x_base.dtsi
build/boards/default/dts/cv180x_riscv/cv180x_base_riscv.dtsi
build/boards/default/dts/cv180x/cv180x_asic_qfn.dtsi
build/boards/default/dts/cv180x/cv180x_asic_sd.dtsi
build/boards/default/dts/cv180x/cv180x_default_memmap.dtsi
build/boards/cv180x/cv1800b_milkv_duo_sd/dts_riscv/cv1800b_milkv_duo_sd.dts
然後在u-boot-2021.10/arch/riscv/dts/Makefile
中,修改:
dtb-$(CONFIG_TARGET_CVITEK) += cv1800b_milkv_duo_sd.dtb
然後在u-boot-2021.10/dts/Makefile
中,取消註釋,修改:
DTB := arch/$(ARCH)/dts/$(DEVICE_TREE).dtb
同時,進入make menuconfig
,設置CONFIG_DEFAULT_DEVICE_TREE=cv1800b_milkv_duo_sd
再次嘗試編譯,編譯通過
LD u-boot
riscv64-unknown-linux-gnu-ld.bfd: warning: u-boot has a LOAD segment with RWX permissions
OBJCOPY u-boot.srec
OBJCOPY u-boot-nodtb.bin
SYM u-boot.sym
CAT u-boot-dtb.bin
COPY u-boot.bin
LD u-boot.elf
編譯opensbi
根據:
opensbi: export CROSS_COMPILE=$(CONFIG_CROSS_COMPILE_SDK)
opensbi: u-boot-build
$(call print_target)
${Q}$(MAKE) -j${NPROC} -C ${OPENSBI_PATH} PLATFORM=generic \
FW_PAYLOAD_PATH=${UBOOT_PATH}/${UBOOT_OUTPUT_FOLDER}/u-boot-raw.bin \
FW_FDT_PATH=${UBOOT_PATH}/${UBOOT_OUTPUT_FOLDER}/arch/riscv/dts/${CHIP}_${BOARD}.dtb
翻譯成人話,我們推測,我們應該執行:
make -j64 CROSS_COMPILE=riscv64-buildroot-linux-musl- PLATFORM=generic \
FW_PAYLOAD_PATH=../u-boot-2021.10/u-boot.bin \
FW_FDT_PATH=../u-boot-2021.10/arch/riscv/dts/cv1800b_milkv_duo_sd.dtb
編譯後,又報錯:
Error: unrecognized opcode `fence.i', extension `zifencei' required
修改Makefile
ifndef PLATFORM_RISCV_ISA
ifneq ($(PLATFORM_RISCV_TOOLCHAIN_DEFAULT), 1)
PLATFORM_RISCV_ISA = rv$(PLATFORM_RISCV_XLEN)imafdc_zicsr_zifencei
else
PLATFORM_RISCV_ISA = $(OPENSBI_CC_ISA)
endif
endif
此時編譯通過,生成
OBJCOPY platform/generic/firmware/payloads/test.bin
OBJCOPY platform/generic/firmware/fw_dynamic.bin
OBJCOPY platform/generic/firmware/fw_jump.bin
OBJCOPY platform/generic/firmware/fw_payload.bin