2. SDK编译

2.1. 获取源码

从网盘资源 4-SDK源码压缩包 中获取LubanCat_Hi3403_SDK.tar.gz压缩包,使用下面的命令将源码压缩包解压到当前用户的家目录,方便后续操作。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
# 将源码压缩包移动到用户家目录
mv LubanCat_Hi3403_SDK.tar.gz ~/LubanCat_Hi3403_SDK.tar.gz

# 进入用户家目录
cd ~

# 解压源码压缩包
tar -xf LubanCat_Hi3403_SDK.tar.gz

# 进入SDK源码目录
cd ~/LubanCat_Hi3403_SDK
../../_images/tar-sdk.png

如图所示,解压后的源码各文件夹。

2.2. SDK目录介绍

提示

以下目录结构不需要深入了解,只需要在修改对应项目时找得到即可。

SDK源码目录结构

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
.
├── open_source     # SDK使用到的第三方开源项目源码
│   ├── buildroot       # Buildroot目录
│   │   ├── buildroot-2024.02.10    # Buildroot源码
│   │   ├── dl                      # Buildroot下载的源码压缩包保存位置
│   │   ├── Makefile                # Buildroot项目的Makefile,对接SDK的Makefile
│   │   └── ss928_lbc_defconfig     # Buildroot编译时的配置文件
│   ├── busybox         # busybox目录
│   │   ├── busybox-1.31.1          # busybox源码
│   │   ├── busybox-1.31.1.patch    # 基于busybox源码修改的补丁文件
│   │   ├── busybox-1.31.1.tar.bz2  # busybox源码压缩包
│   │   ├── busybox_CVE-2021-42374.patch    # CVE漏洞补丁
│   │   └── Makefile                # busybox项目的Makefile,对接SDK的Makefile
│   ├── linux           # Linux内核
│   │   ├── linux-4.19.90.patch     # Linux内核补丁
│   │   ├── linux-4.19.90.tar.gz    # Linux源码压缩包
│   │   ├── linux-4.19.y            # Linux源码目录
│   │   └── Makefile                # Linux项目的Makefile
│   ├── lvgl            # LVGL图形框架
│   │   ├── lv_port_linux
│   │   └── Makefile
│   ├── u-boot          # U-Boot
│   ├── ubuntu          # Ubuntu根文件系统构建脚本
│   ├── e2fsprogs       # 第三方开源工具
│   ├── eigen
│   ├── ethtool
│   ├── eudev
│   ├── iniparser
│   ├── mbedtls
│   ├── mtd-utils
│   ├── optee
│   ├── squashfs
│   ├── trusted-firmware-a
│   ├── util-linux
│   ├── xz
│   └── zlib
├── osdrv           # 操作系统相关目录
│   ├── BoardConfig     # 板级配置文件目录
│   │   └── BoardConfig配置说明     # 配置项说明文件
│   ├── components      # 海思自研组件源码
│   │   ├── boot            # boot相关工具
│   │   │   ├── gsl             # GSL源码
│   │   │   └── image_map       # boot镜像制作工具
│   │   ├── ipcm            # 多核通信工具源码
│   │   ├── pcie_mcc
│   │   └── secure_c        # 安全函数库
│   ├── lunch           # 板级配置选择脚本
│   ├── Makefile        # SDK编译入口
│   ├── pub             # 编译生成的镜像等文件存放位置
│   ├── rootfs_scripts  # busybox使用的根文件系统预定义文件
│   │   └── rootfs.tgz
│   └── tools           # 系统工具源码
│       ├── board           # 板卡工具
│       │   ├── load_riscv      # mcu镜像加载工具
│       │   └── reg-tools-1.0.0 # 寄存器读写工具、i2c读写工具
│       └── pc              # 编译主机工具
│           ├── kdf_customer
│           ├── mkimage_tool    # mkimage工具说明
│           ├── nand_production # nand工具
│           ├── ubi_sh          # ubifs镜像制作工具
│           ├── uboot_env       # u-boot env镜像制作工具
│           └── uboot_tools     # u-boot工具
│               ├── regbin-v1.0.2   # regbin转换工具,将xlsm格式的u-boot表格转换为bin文件
│               └── SS928V100_LubanCat_LPDDR4x_3733M-8GB_32bitx2-A55_1400M-emmc.xlsm    # u-boot表格,寄存器配置表
├── platform        # 平台代码
│   └── liteos          # LiteOS代码
├── readme.md       # SDK说明文件
└── smp             # smp目录
    ├── a55_linux       # arm核代码
    │   ├── interdrv        # 主芯片外设驱动
    │   ├── mpp             # mpp源码
    │   ├── osal            # 操作系统适配层
    │   └── vendor          # 外围设备驱动
    └── dsp_liteos      # dsp核代码
        ├── dsp0            # dsp0核代码
        └── dsp1            # dsp1核代码

2.3. 一键编译Linux系统

提示

如果没有特别说明,本章节中Linux系统的编译命令都在osdrv目录下执行。

2.3.1. 选择板级配置文件

由于不同存储类型,不同DDR容量、不同系统都要设置不同的编译参数,配置项众多。

为了便于用户使用,添加了配置文件选择机制。将配置参数写入预设的板级配置文件中,只需要在编译前选择不同的配置文件,就可以直接编译出对应方案的镜像了。

在osdrv中执行 ./lunch 命令

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
# 配置文件选择命令
./lunch

# 根据提示信息,输入配置文件对应的数字
==================================================
启动存储:EMMC/SPI-NAND/SPI-NOR
内存类型:LPDDR4x-4GB/LPDDR4x-8GB
板卡名称:Hi3403
系统类型:Ubuntu/Buildroot/Busybox
==================================================
请选择一个 BoardConfig 文件:
1) BoardConfig-EMMC-LPDDR4x_4GB-Hi3403-Buildroot.mk
2) BoardConfig-EMMC-LPDDR4x_4GB-Hi3403-Ubuntu_Lite.mk
3) BoardConfig-EMMC-LPDDR4x_8GB-Hi3403-Buildroot.mk
4) BoardConfig-EMMC-LPDDR4x_8GB-Hi3403-Busybox.mk
5) BoardConfig-EMMC-LPDDR4x_8GB-Hi3403-Ubuntu_Lite.mk
6) BoardConfig-SPI_NAND-LPDDR4x_8GB-Hi3403-Buildroot.mk
7) BoardConfig-SPI_NAND-LPDDR4x_8GB-Hi3403-Busybox.mk
8) BoardConfig-SPI_NOR-LPDDR4x_8GB-Hi3403-Busybox.mk
输入数字选择: 0
已选择 BoardConfig.mk -> BoardConfig-EMMC-LPDDR4x_4GB-Hi3403-Buildroot.mk
../../_images/boardconfig.png

配置文件选择完成。如果想查看当前的配置文件,可以用 ls -al 命令查看osdrv目录下BoardConfig.mk指向的配置文件

以下是配置文件中各配置项的说明:

注解

文档可能更新不及时,以 osdrv/BoardConfig/BoardConfig配置说明 中的内容为准。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
# Target CHIP
# 目标芯片,默认为ss928v100
export CHIP=

# Kernel config
# 内核配置文件,编译内核时使用
export KERNEL_CFG=

# Target rootfs: buildroot/busybox/ubuntu
# 目标根文件系统,可选busybox、buildroot、ubuntu
export TARGET_ROOTFS=

# Target version: ubuntu: lite/xfce
# 根文件系统版本,仅在TARGET_ROOTFS=ubuntu时有效,可选lite、xfce
export TARGET_VERSION=

# set rootfs type: ubifs/jffs2/ext4
# 根文件系统镜像分区格式,支持ubifs(nand-flash)、jffs2(nor-flash)、ext4(emmc)
export ROOTFS_TYPE=

# Set buildroot config only TARGET_ROOTFS=buildroot
# buildroot配置文件,编译buildroot时使用,仅在TARGET_ROOTFS=buildroot时有效
export BUILDROOT_CFG=

# Target boot medium: spi(Nand/Nor)/emmc
# 目标存储芯片,可选spi(Nand-flash/Nor-flash)或emmc
export BOOT_MEDIA=

# EMMC rootfs size(MB), default 96MB
# EMMC根文件系统分区大小,单位MB,默认96MB,仅在ROOTFS_TYPE=ext4时有效。
# 设置值应略大于实际的根文件系统文件总体积,设置为0时自动计算文件大小
export EMMC_ROOTFS_SIZE=

# Nand chip size
# 生成镜像适配SPI_NAND的大小,仅在ROOTFS_TYPE=ubifs时有效。
# 设置多个值表示同时生成多个对应的下载配置文件和env镜像,中间用英文字符逗号隔开。
# 目前仅支持128M和256M两个值可供配置,不配置则使用默认值
export NAND_CHIPSIZE=128M,256M

# U-boot xlsm
# u-boot配置文件表格,不建议修改
export REGBIN_XLSM=

# mpp module and lib install rootfs: smp/a55_linux/mpp/out
# mpp模块和库,设置为yes则安装到根文件系统中
export HI_MPP_KO_LIB=

# mpp sample enable when HI_MPP_KO_LIB=yes: smp/a55_linux/mpp/sample
# mpp示例程序,设置为yes则安装到根文件系统中
export HI_MPP_SAMPLE=

# interdrv modules and sample enable when HI_MPP_KO_LIB=yes: smp/a55_linux/interdrv
# interdrv模块和示例程序,设置为yes则安装到根文件系统中
export HI_INTERDEV=

# lvgl project enable
# lvgl示例程序,设置为yes则安装到根文件系统中
export HI_LVGL_PROJECT=yes

2.3.2. 一键编译命令

配置文件选择完成之后,可以使用一键编译命令来构建系统镜像,将会自动构建boot、env、kernel和rootfs镜像。

在osdrv目录下执行下面的命令

1
2
# 一键编译命令
make

编译完成后生成的镜像文件保存在osdrv/pub/{CHIP}_{BOOT_MEDIA}_image_glibc_{TARGET_ROOTFS}目录下。

文件名称

说明

boot_image.bin

boot镜像(GSL+U-Boot)

emmc_burn_table.xml

烧录工具分区配置表

emmc_env.bin

env分区镜像

rootfs_ss928v100_xxxM.ext4

rootfs分区镜像

sample.bin

mcu镜像

u-boot-ss928v100.bin

uboot镜像

uImage_ss928v100

kernel镜像(ATF+uImage+dtb)

以上编译生成的文件可以用于镜像烧录,烧录说明请查看: 系统镜像烧录 章节

2.4. 分区镜像编译

当我们修改SDK的部分内容后需要重新编译生成镜像,如果还是完整的去编译SDK会耗费很多时间, 此时可以单独编译修改的部分生成镜像,来增加开发效率。

一键编译命令 章节中生成的镜像文件,有4个文件用于板卡烧录,对应存储中的4个分区:

  • boot镜像:boot_image.bin

  • env镜像:env.bin

  • kernel镜像:uImage_ss928v100

  • rootfs镜像:rootfs_ss928v100_xxxM.ext4

下面对各分区镜像的作用和编译步骤做详细说明

2.4.1. boot镜像编译

boot镜像包含GSL和U-Boot两部分,用于一步一步初始化芯片,最后启动内核。

当芯片上电后,先运行位于固化在芯片内部的代码BootROM,进行最基础的初始化,并根据启动模式配置加载对应存储中的GSL运行。

GSL根据U-boot表格(uboot寄存器配置表格)中的寄存器配置继续对DDR、时钟、引脚等外设进行初始化,然后加载U-boot并运行。

到U-Boot就能更全面的对系统外设进行初始化了,并支持一些高级操作,如网络通信、存储设备读写等。一切准备就绪,U-Boot将内核镜像加载到内存中,然后跳转到内核开始启动内核。

整个过程从BootROM → GSL → U-Boot → 内核,像阶梯一样逐级完成初始化,我们通常将BootROM到内核启动之间的全部阶段统称为BootLoader。

在osdrv目录下执行下面的命令构建boot镜像

1
make gslboot_build

在osdrv/Makefile中可以看到

1
2
3
4
prepare:                        # 准备工作
regbin_prepare:                 # 将U-boot寄存器配置表格转换为{chip}_reg_info.bin
boot: prepare regbin_prepare    # 编译U-Boot源码,生成u-boot-{chip}.bin
gslboot_build: boot             # 编译gsl源码,生成gsl.bin,然后将这三个文件合并成一个boot镜像boot_image.bin

据 Makefile 的语法规则,冒号前的名称是目标(target),冒号后的内容是该目标的依赖(prerequisites)。 在执行某个目标之前,Make 会自动先执行它的所有依赖。

在执行gslboot_build时,会先执行他的依赖boot,而boot又依赖于prepare和regbin_prepare。

所以运行 make gslboot_build 命令的完整流程是 prepare regbin_prepare boot gslboot_build

U-Boot和GSL代码一般不用修改,uboot表格仅在修改ddr配置或初始引脚复用时修改。在SDK开发过程中,相对来说很少修改boot相关的内容。

2.4.2. env镜像编译

我们将U-Boot的环境变量打包成一个镜像,在U-Boot中对env镜像中保存的环境变量自动读取,可以更灵活的对U-Boot环境变量进行设置,方便用户使用。

在osdrv目录下执行下面的命令构建env镜像

1
make uboot_env

查看osdrv/Makefile中的 uboot_env: prepare 可以可知,当我们执行命令时, 会根据存储的类型和存储的大小将u-boot环境变量(env)从文本转换成可直接烧写的二进制镜像env.bin, 并根据env中的bootargs的分区信息生成烧录工具分区配置文件burn_table.xml。

此工具的路径是osdrv/tools/pc/uboot_env,其中env_text目录下保存的是原始的env文本文件, 当我们想要修改默认的u-boot环境变量时可以修改对应的文本文件并重新生成env镜像进行烧录。

烧录env镜像可以给U-boot传递预先定义好的U-Boot环境变量,如果我们在板卡运行时需要修改, 还可以通过串口登录到U-boot命令行终端,使用命令进行修改。

U-boot环境变量相关的命令如下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
# 打印当前环境变量
printenv

# 新增或修改环境变量
setenv [变量名] [变量值]

# 删除环境变量
setenv [变量名]

# 保存环境变量
saveenv

2.4.3. kernel镜像编译

Linux 内核(Linux Kernel)在整个操作系统中是核心组件,它介于硬件和应用程序之间,负责管理系统资源、提供底层服务、实现硬件抽象,是软件与硬件沟通的桥梁。

在Linux系统开发过程中,我们经常要对内核功能进行裁剪,为硬件添加驱动支持、为软件提供运行环境,所以需要经常修改并编译内核。

在osdrv目录下执行下面的命令构建kernel镜像

1
make atf

在osdrv/Makefile中可以看到

1
2
3
prepare:            # 准备工作
kernel: prepare     # 编译内核源码并生成内核镜像uImage
atf: kernel         # 编译atf,并将生成的文件与uImage一起打包为完整的kernel镜像uImage_{chip}

需要注意的是,当我们修改内核配置文件时:

  • 将配置项设置为[y]:对应代码会直接编译进内核镜像uImage

  • 将配置项设置为[m]:对应代码会被编译成独立的内核模块(.ko)

上面命令生成的uImage_{chip}镜像中不包含被编译为模块[m]的驱动,在编译内核时这些模块会单独生成,需要放在rootfs的/usr/lib/modules目录中,由系统在启动时按需加载。

所以在修改内核代码后要注意,需要更新内核镜像还是更新rootfs中的ko文件。

除了上面编译kernel镜像的命令外,还有几个辅助命令可以提高效率:

1
2
3
4
5
6
7
8
# 修改内核配置文件(完整命令)
make kernel_menuconfig

# 修改内核配置文件(简短命令)
make kconfig

# 清理内核
make kernel_clean

2.4.4. rootfs镜像构建

rootfs(Root File System,根文件系统)是Linux用户空间的基础环境,包含系统运行所需的程序、库和配置文件。 内核启动后会挂载rootfs,并在其中启动用户空间的第一个进程。因此,我们在Linux系统上的所有用户操作(包括执行命令、运行程序、文件管理等)都是在rootfs环境中进行的。

在开发过程中,如果我们希望将某些配置文件、脚本、驱动模块(.ko)或应用程序预置到rootfs镜像中,就必须重新构建rootfs才能将这些内容打包进去。

以Buildroot系统为例,由于它不支持在线包管理,所有的软件包和文件内容都需要在构建时选定并打包,因此在开发过程中需要频繁重新构建 rootfs。

在osdrv目录下执行下面的命令构建rootfs镜像

1
make rootfs_build

除了上面编译kernel镜像的命令外,还有几个辅助命令可以提高效率:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
# 构建ubuntu根文件系统基础压缩包
make ubuntu


# 构建buildroot根文件系统基础压缩包
make buildroot

# 清理buildroot
make buildroot_clean

# 修改buildroot配置文件(完整命令)
make buildroot_menuconfig

# 修改buildroot配置文件(简短命令)
make bconfig


# 构建busybox根文件系统
make busybox
# 清理buildroot
make busybox_clean

在osdrv/Makefile中可以看到

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
prepare:
rootfs_prepare: prepare

TARGET_ROOTFS=ubuntu、buildroot或busybox
ubuntu: prepare rootfs_prepare
buildroot: prepare rootfs_prepare
busybox: prepare

secure_libs:
pctools: prepare secure_libs

boardtools: rootfs_prepare secure_libs

kernel: prepare
release_liteos:
ipcm:prepare kernel release_liteos secure_libs

rootfs_notools_build: rootfs_prepare $(TARGET_ROOTFS) pctools boardtools ipcm
rootfs_build: rootfs_notools_build

根据Makefile的规则整理一下,当我们运行 make rootfs_build 时流程如下:

  1. prepare:创建工作目录

  2. rootfs_prepare:清理rootfs构建目录

  3. $(TARGET_ROOTFS):根据TARGET_ROOTFS的值,运行对应的根文件系统构建命令、安装内核模块、处理目录路径

  4. secure_libs:生成libsecurec.a安全函数库供board和pc工具使用

  5. pctools:编译pc使用的命令工具,如mkfs、zlib、mksquashfs等

  6. boardtools:编译board使用的命令工具,如load_riscv、mkfs、ethtool等

  7. kernel:编译内核模块

  8. release_liteos:编译mcu固件

  9. ipcm:编译ipcm工具,用于liteos和Linux间通信

  10. rootfs_notools_build:将生成的命令工具打包进行rootfs,并按照存储类型生成rootfs镜像

  11. rootfs_build:rootfs_notools_build的别称

其中$(TARGET_ROOTFS)是核心操作,根据变量值的不同,调用对应的根文件系统构建脚本,生成基础的根文件系统压缩包。 后面就是编译一下板卡用到的工具,并打包进根文件系统,最后打包成rootfs.img镜像用于烧录。

2.4.5. 分步构建注意事项

  • 分步构建rootfs时,只会将ko文件打包进rootfs,而不会重新编译内核。如果修改了内核ko需要重新打包rootfs, 则需要先运行 make kernel 编译kernel重新生成ko文件,然后再运行 make rootfs_build 手动构建rootfs镜像。 一键编译时则不存在此问题,构建rootfs前会将内核先编译一遍生成ko模块文件。

  • 分步构建ubuntu rootfs时,为了增加构建效率,默认会使用已经构建好的历史版本的ubuntu基础根文件系统压缩包。 如果修改了除mk-base-ubuntu.sh之外的文件,则需要删除ubuntu-jammy-lite-arm64-rootfs.tar.gz, 如果修改了mk-base-ubuntu.sh,则还需要删除ubuntu-base-lite-arm64-${date}.tar.gz。 删除上述文件后再执行 make ubuntumake rootfs_build 来重新构建ubuntu基础根文件系统压缩包。

  • 分步构建rootfs时,只会将mpp项目生成的库文件、示例程序打包进rootfs,不会重新编译mpp, 如果要重新编译mpp,则需要使用 make hi_mpp 或在smp/a55_linux/mpp/out/obj下运行 make clean; make 来手动编译。 一键编译时则不存在此问题,会根据板级配置文件中的 HI_MPP_KO_LIBHI_MPP_SAMPLE 设置修改 TARGET_ALL 变量来追加到编译流程中。

  • 分步构建rootfs时,只会将interdrv项目生成的ko文件、示例程序打包进rootfs,不会重新编译interdrv, 如果要重新编译interdrv,则需要使用 make hi_mpp 或在smp/a55_linux/interdrv下运行 make clean; make 来手动编译。 一键编译时则不存在此问题,会根据板级配置文件中的 HI_INTERDEV 设置修改 TARGET_ALL 变量来追加到编译流程中。

  • 分步构建rootfs时,只会将lvgl项目生成的lvgl示例程序打包进rootfs,不会重新编译lvgl, 如果要重新编译lvgl,则需要使用 make hi_lvgl_project 来手动编译。 一键编译时则不存在此问题,会根据板级配置文件中的 HI_LVGL_PROJECT 设置修改 TARGET_ALL 变量来追加到编译流程中。