milkv-duo启动流程分析:手动构建fip.bin [1/2]

这篇具有很好参考价值的文章主要介绍了milkv-duo启动流程分析:手动构建fip.bin [1/2]。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

目录
  • 构建riscv64-unknown-linux-musl编译工具链
    • 直接下载官方工具链
    • 尝试自己编译T-head Gcc
    • 下载编译binutils
    • 编译交叉gcc
    • 编译musl
  • 手动合成fip.bin和boot.sd
    • 编译u-boot
      • 生成cvi_board_memmap.hcvipart.himgs.h
      • 继续编译u-boot
    • 编译opensbi

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.hcvipart.himgs.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

此时编译通过,生成文章来源地址https://www.toymoban.com/news/detail-710725.html

 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

到了这里,关于milkv-duo启动流程分析:手动构建fip.bin [1/2]的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • Activity启动流程分析

    点击桌面App图标,Launcher进程采用Binder IPC向system_server进程发起startActivity请求 system_server进程接收到请求后,向zygote进程发送创建进程的请求; Zygote进程fork出新的子进程,即App进程 App进程,通过Binder IPC向sytem_server进程发起attachApplication请求; system_server进程在收到请求后,进

    2024年02月09日
    浏览(47)
  • Android SystemServer 启动流程分析

    和你一起终身学 习,这里是程序员Android 经典好文推荐,通过阅读本文,您将收获以下知识点: 一、SystemServer 启动的服务有哪些 二、SystemServer启动总体流程概述 三、SystemServer 如何启动,是谁启动的? 四、 SystemServer 启动入门 main 方法 五、SystemServer Run 方法初始与启动 六、

    2024年02月13日
    浏览(32)
  • SpringBoot的启动流程源码分析

    new 一个IOC容器,传入配置好的文件xml,在这个地方打bug 在这个debug的栈帧中,下面几个不用看,直接看到getBean 内容如图所示,name传的就是我们在xml的bean标签的id,这里是instanceA 进入到doGetBean后,因为我是从IOC初始化容器debug进来的所以第一次通过。 Object sharedInstance = getSingl

    2024年01月24日
    浏览(34)
  • 简单指令实现Docker构建镜像启动运行保存导出后再导入新环境完整全流程

    本文做一个简单Docker使用指令指南,可快速实现Docker构建镜像、启动、运行、保存、导出后再导入新环境完整全流程。具体每一个指令又有很多参数和学问,大家可自行查询更详细的解释,本文可用于小白快速构建镜像并使用。 首先,准备一个Dockerfile,包含你要使用的基础

    2024年02月11日
    浏览(35)
  • STM32启动详细流程分析(一)

      大家不妨设想一下,cpu 的工作是什么,cpu 是没有主观意识的,它只会按照特定的指令执行相应的操作,用专业术语来说就是: 取指 - 译码 - 执行 ,译码和执行肯定是在 cpu 内部进行操作的,并且前提是已经取到了指令。那现在问题来了,指令在哪? cpu上电复位后执行的第

    2024年02月12日
    浏览(31)
  • android framework之Applicataion启动流程分析

    Application启动流程框架分析 启动方式一:通过Launcher启动app 启动方式二:在某一个app里启动第二个app的Activity. 以上两种方式均可触发app进程的启动。但无论哪种方式,最终通过通过调用AMS的startActivity()来启动application的。    根据上图分析, 要启动一个Application,需要涉及五

    2024年02月11日
    浏览(35)
  • SpringBoot3.X源码分析(启动流程)

    1 启动入口 静态辅助类,可用于运行使用默认配置(即我们添加的一系列注解)的指定源的 SpringApplication 。 primarySource - 要载入的主要源,即指定源,这里为传入的 Application.class  Class? :泛型决定了任何类都可以传入 args - 应用程序参数(通常从main方法传递) 返回:正在运

    2024年01月23日
    浏览(29)
  • SpringBoot配置外部Tomcat项目启动流程源码分析

    SpringBoot应用默认以Jar包方式并且使用内置Servlet容器(默认Tomcat),该种方式虽然简单但是默认不支持JSP并且优化容器比较复杂。故而我们可以使用习惯的外置Tomcat方式并将项目打War包。 ① 同样使用Spring Initializer方式创建项目 ② 打包方式选择\\\"war\\\" ③ 选择添加的模块 ④ 创建的

    2024年02月04日
    浏览(30)
  • 【Android Gradle 插件】Android Studio 工程 Gradle 构建流程 ② ( settings.gradle 构建脚本分析 | 根目录下 build.gradle 分析 )

    pluginManagement 脚本块 , 用于 配置 Gradle 插件的 Maven 仓库 , 配置的是 构建过程 中 , 使用的仓库 ; dependencyResolutionManagement 脚本块 , 用于 配置 依赖 的 Maven 仓库 , 配置的是 工程 或 模块 下的依赖使用的仓库 ; 在 dependencyResolutionManagement 脚本块 中 定义的 repositoriesMode.set(Repositorie

    2024年02月03日
    浏览(37)
  • UI绘制流程分析(前篇)--App与Activity的启动

    彻底搞懂UI绘制流程,看该系列就够了 作为安卓开发最重要的知识点之一,UI绘制无疑是必须掌握的,要想搞懂它的测量、布局和绘制,得先理解它的整个流程,但现在让我们把时间再往前拨一下,先要从App启动流程以及Activity启动流程讲起。 提示:以下是本篇文章正文内容

    2024年02月16日
    浏览(38)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包