SG2002(CVITEK) Linux+RTOS异构操作系统bootloader构建与启动分析

3193 字
16 分钟
SG2002(CVITEK) Linux+RTOS异构操作系统bootloader构建与启动分析

性能or实时:为什么要搞异构#

前几年的时候,性能核配实时核的国内SOC市场突然迎来爆炸,各大开发板厂商也逐渐跟进,相较于经典款MCU或者x86的CPU,这些个芯片一边具备着“古法嵌入式”的实时能力,一边又有一个或多个配备MMU的核心运行高性能任务,享受Linux丰富的软件栈与敏捷开发。

在更早的时候,许多工业项目已经有此类需求,例如Klipper的大量轨迹计算与补偿+精确时刻微步,无人机的高级导航功能+高频位姿估计,ZYNQ的PS+PL软核(好吧这个贵),机器人领域几乎需要复杂图形界面+高频控制闭环的东西都在某种程度上需要这一配置。如果能减轻一边盯着实时核一遍重造网络,界面,以及各种意想不到的轮子的痛苦,爱好者和研究人员很愿意付一颗额外SOC的钱,只不过需要用一根片外导线把高性能核心和实时核心链接在一起,外加忍受一些时间基准和各种自定义协议的麻烦。

尝试将性能核和实时核塞在同一个SOC的产物,iMX8和ST的MP1,似乎要比这些国产SOC稍早一些,不过那个时候的物联网要求似乎还没那么广泛,很多还是哑终端,他们更多面向工业和专业场景。

而今天,物联网设备想要彩色大屏+低功耗待机,机器人想要智慧的AI大脑+上百Hz的控制闭环,而且都要便宜,如果一块SOC能解决就不要上[一块高性能SOC+DRAM+EMMC+一块嵌入式核心+片外Flash]。而今天的主角SG2002则是把大部分能塞在一起的塞在一起的其中之一(的廉价款芯片)。这些使得简单的物联网,基础的DL+控制任务无需传统的MPU+MCU构造,甚至一些型号具备了SIP的运行内存,可以狠狠的减小板子面积了。

P.S. 本文写于2024年,从公众号迁移到Blog,仅限对写作时所用最新构建脚本进行分析

芯片框图与初次编译#

本文所使用的开发板是milk-v-duo  256M,其上搭载了一颗算能科技(sophgo)公司的sg2002 SOC。这个芯片一共有四个核心,一个A53性能核,一个C906性能核,一个C906实时核,一个8051低功耗核。离谱的是那两个性能核只能选一个启动(。后文将这个C906实时核心称为C906L。特色是它配有一个1Tops的NPU,明显是和全志和堪楠比划比划。其他的配置就中规中矩,两组SDIO,支持高速SPI,和一堆以通信为主的外设。不过国产SOC的通病,这个芯片的资料写的也不太全。

sg2002结构图
sg2002结构图

尽管爱好者最常见的开发方式是micropython和Arduino框架,但是要完全发挥它的性能,依然建议使用官方提供的linux+FreeRTOS  SDK,本文章基于milk-v开源社区移植的SDK来写。https://github.com/milkv-duo/duo-buildroot-sdk

首先把SDK的仓库clone下来,并按照README完成配置和编译all目标,编译过程在8核CPU上大约需要半个小时。编译成功后,会在install目录下生成以下几个升级文件。

milk-v官方仓库编译产物
milk-v官方仓库编译产物

因为写太多大家也不会看完,所以本文只分析一些fip.bin的构建和运行过程(使用risc-v核心)(sd卡启动),也就是从上电开始,到linux内核启动前启动之前的程序。

构建脚本分析#

首先简单分析一下构建过程。 仓库目录下的build脚本通过对不同板子的选择来调用预编写的config文件。 随后调用build/milkvsetup.sh 中的build_all函数,该函数准备(主要是环境变量)环境,并逐一启动所有构建与打包工作。

function build_all()
{
# build bsp
build_uboot || return $?
build_kernel || return $?
build_osdrv || return $?
build_middleware || return $?
pack_access_guard_turnkey_app || return $?
pack_ipc_turnkey_app || return $?
pack_boot || return $?
pack_cfg || return $?
pack_rootfs || return $?
pack_data
pack_system || return $?
copy_tools
pack_upgrade
}

fip.bin的构建工作由build_uboot函数完成

function build_uboot()
{(
print_notice "Run ${FUNCNAME[0]}() function"
_build_uboot_env
_build_opensbi_env
_link_uboot_logo
cd "$BUILD_PATH" || return
[[ "$CHIP_ARCH" == CV182X ]] || [[ "$CHIP_ARCH" == CV183X ]] && \
cp -f "$OUTPUT_DIR"/fip_pre/fip_pre_${ATF_KEY_SEL}.bin \
"$OUTPUT_DIR"/fip_pre/fip_pre.bin
make u-boot
)}

可以看到,完成环境准备后,主要构建实际上是由make完成

这个Makefile在build/ 目录下

找到u-boot目标

u-boot: u-boot-dep

虽然说我们构建的是u-boot目标,实际上这个目标同时构建了fsbl+openSBI+FreeRTOS+u-boot并把他们打包嘞。

我们看到它只有一个u-boot-dep的依赖,这实际上是为了适应不同的fip构建流程,我们使用的是fip-v2,可以打开build/scripts/fip_v2.mk查看这个target

u-boot-dep: fsbl-build ${OUTPUT_DIR}/elf
$(call print_target)
ifeq ($(call qstrip,${CONFIG_ARCH}),riscv)
${Q}cp ${OPENSBI_PATH}/build/platform/generic/firmware/fw_payload.bin ${OUTPUT_DIR}/fw_payload_uboot.bin
${Q}cp ${OPENSBI_PATH}/build/platform/generic/firmware/fw_payload.elf ${OUTPUT_DIR}/elf/fw_payload_uboot.elf
endif

这一目标实际上构建了fsbl-build

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
FSBL_OUTPUT_PATH = ${FSBL_PATH}/build/${PROJECT_FULLNAME}
ifeq ($(call qstrip,${CONFIG_ARCH}),riscv)
fsbl-build: opensbi
endif
ifeq (${CONFIG_ENABLE_FREERTOS},y)
fsbl-build: rtos
fsbl%: export BLCP_2ND_PATH=${FREERTOS_PATH}/cvitek/install/bin/cvirtos.bin
fsbl%: export RTOS_DUMP_PRINT_ENABLE=$(CONFIG_ENABLE_RTOS_DUMP_PRINT)
fsbl%: export RTOS_DUMP_PRINT_SZ_IDX=$(CONFIG_DUMP_PRINT_SZ_IDX)
fsbl%: export RTOS_FAST_IMAGE_TYPE=${CONFIG_FAST_IMAGE_TYPE}
fsbl%: export RTOS_ENABLE_FREERTOS=${CONFIG_ENABLE_FREERTOS}
endif
fsbl%: export FSBL_SECURE_BOOT_SUPPORT=${CONFIG_FSBL_SECURE_BOOT_SUPPORT}
fsbl%: export ARCH=$(call qstrip,${CONFIG_ARCH})
fsbl%: export OD_CLK_SEL=${CONFIG_OD_CLK_SEL}
fsbl%: export VC_CLK_OVERDRIVE=${CONFIG_VC_CLK_OVERDRIVE}
fsbl-build: u-boot-build memory-map
$(call print_target)
${Q}mkdir -p ${FSBL_PATH}/build
${Q}ln -snrf -t ${FSBL_PATH}/build ${CVI_BOARD_MEMMAP_H_PATH}
${Q}$(MAKE) -j${NPROC} -C ${FSBL_PATH} O=${FSBL_OUTPUT_PATH} BLCP_2ND_PATH=${BLCP_2ND_PATH} \
LOADER_2ND_PATH=${UBOOT_PATH}/${UBOOT_OUTPUT_FOLDER}/u-boot-raw.bin
${Q}cp ${FSBL_OUTPUT_PATH}/fip.bin ${OUTPUT_DIR}/
ifeq (${CONFIG_UBOOT_SPL_CUSTOM},y)
${Q}$(MAKE) -C ${FSBL_PATH} clean O=${FSBL_OUTPUT_PATH}
${Q}$(MAKE) -j${NPROC} -C ${FSBL_PATH} O=${FSBL_OUTPUT_PATH} BLCP_2ND_PATH=${BLCP_2ND_PATH} \
CONFIG_SKIP_UBOOT=$(CONFIG_SKIP_UBOOT) LOADER_2ND_PATH=${UBOOT_PATH}/${UBOOT_OUTPUT_FOLDER}/spl/u-boot-spl-raw.bin
${Q}cp ${FSBL_OUTPUT_PATH}/fip.bin ${OUTPUT_DIR}/fip_spl.bin
else
${Q}cp ${FSBL_OUTPUT_PATH}/fip.bin ${OUTPUT_DIR}/fip_spl.bin
endif

fsbl首先依赖opensbi,调用opensbi目录下的子Makefile完成构建,opensbi会先构建u-boot,并将u-boot与opensbi组合在一起,sd卡启动方式中似乎并未用到这一组合文件,所以不展开解释了。

随后fsbl依赖rtos,调用freertos下的cmake完成构建

实际在运行时,rtos与opensbi均被加载到DDR内,地址映射如下

#define CONFIG_SYS_TEXT_BASE 0x80200000 /* offset 2.0MiB */
#define CVIMMAP_ATF_SIZE 0x80000 /* 512.0KiB */
#define CVIMMAP_BOOTLOGO_ADDR 0x8b13e000 /* offset 177.2421875MiB */
#define CVIMMAP_BOOTLOGO_SIZE 0x1c2000 /* 1.7578125MiB */
#define CVIMMAP_CONFIG_SYS_INIT_SP_ADDR 0x82800000 /* offset 40.0MiB */
#define CVIMMAP_CVI_UPDATE_HEADER_ADDR 0x817ffc00 /* offset 23.9990234375MiB */
#define CVIMMAP_CVI_UPDATE_HEADER_SIZE 0x400 /* 1.0KiB */
#define CVIMMAP_DRAM_BASE 0x80000000 /* offset 0.0KiB */
#define CVIMMAP_DRAM_SIZE 0x10000000 /* 256.0MiB */
#define CVIMMAP_FRAMEBUFFER_ADDR 0x8b13e000 /* offset 177.2421875MiB */
#define CVIMMAP_FRAMEBUFFER_SIZE 0x1c2000 /* 1.7578125MiB */
#define CVIMMAP_FREERTOS_ADDR 0x8fe00000 /* offset 254.0MiB */
#define CVIMMAP_FREERTOS_RESERVED_ION_SIZE 0x1600000 /* 22.0MiB */
#define CVIMMAP_FREERTOS_SIZE 0x200000 /* 2.0MiB */
#define CVIMMAP_FSBL_C906L_START_ADDR 0x8fe00000 /* offset 254.0MiB */
#define CVIMMAP_FSBL_UNZIP_ADDR 0x81800000 /* offset 24.0MiB */
#define CVIMMAP_FSBL_UNZIP_SIZE 0x1000000 /* 16.0MiB */
#define CVIMMAP_H26X_BITSTREAM_ADDR 0x8b300000 /* offset 179.0MiB */
#define CVIMMAP_H26X_BITSTREAM_SIZE 0x200000 /* 2.0MiB */
#define CVIMMAP_H26X_ENC_BUFF_ADDR 0x8b500000 /* offset 181.0MiB */
#define CVIMMAP_H26X_ENC_BUFF_SIZE 0x0 /* 0.0KiB */
#define CVIMMAP_ION_ADDR 0x8b300000 /* offset 179.0MiB */
#define CVIMMAP_ION_SIZE 0x4b00000 /* 75.0MiB */
#define CVIMMAP_ISP_MEM_BASE_ADDR 0x8b500000 /* offset 181.0MiB */
#define CVIMMAP_ISP_MEM_BASE_SIZE 0x1400000 /* 20.0MiB */
#define CVIMMAP_KERNEL_MEMORY_ADDR 0x80000000 /* offset 0.0KiB */
#define CVIMMAP_KERNEL_MEMORY_SIZE 0xfe00000 /* 254.0MiB */
#define CVIMMAP_MONITOR_ADDR 0x80000000 /* offset 0.0KiB */
#define CVIMMAP_OPENSBI_FDT_ADDR 0x80080000 /* offset 512.0KiB */
#define CVIMMAP_OPENSBI_SIZE 0x80000 /* 512.0KiB */
#define CVIMMAP_UIMAG_ADDR 0x81800000 /* offset 24.0MiB */
#define CVIMMAP_UIMAG_SIZE 0x1000000 /* 16.0MiB */

但此时他们还是一个一个单独的bin文件,真正把他们组装到一起的代码在fsbl构建过程中

${Q}$(MAKE) -j${NPROC} -C ${FSBL_PATH} O=${FSBL_OUTPUT_PATH} BLCP_2ND_PATH=${BLCP_2ND_PATH} \
LOADER_2ND_PATH=${UBOOT_PATH}/${UBOOT_OUTPUT_FOLDER}/u-boot-raw.bin

这里调用了fsbl目录下的make,并为其传递了BLCP_2ND(协处理器第二阶段镜像)与LOADER_2ND(加载器第二阶段镜像)地址

all: fip bl2 blmacros
include ${MAKE_HELPERS_DIRECTORY}fip.mk

这是fsbl下主目标的依赖目标,fip目标在make_helpers/fip.mk下被定义

fip: fip-all
fip-dep: bl2 blmacros-env gen-chip-conf
fip-all: fip-dep
$(print_target)
${Q}echo " [GEN] fip.bin"
${Q}. ${BUILD_PLAT}/blmacros.env && \
${FIPTOOL} -v genfip \
'${BUILD_PLAT}/fip.bin' \
--MONITOR_RUNADDR="$${MONITOR_RUNADDR}" \
--BLCP_2ND_RUNADDR="$${BLCP_2ND_RUNADDR}" \
--CHIP_CONF='${CHIP_CONF_PATH}' \
--NOR_INFO='${NOR_INFO}' \
--NAND_INFO='${NAND_INFO}'\
--BL2='${BUILD_PLAT}/bl2.bin' \
--BLCP_IMG_RUNADDR=${BLCP_IMG_RUNADDR} \
--BLCP_PARAM_LOADADDR=${BLCP_PARAM_LOADADDR} \
--BLCP=${BLCP_PATH} \
--DDR_PARAM='${DDR_PARAM_TEST_PATH}' \
--BLCP_2ND='${BLCP_2ND_PATH}' \
--MONITOR='${MONITOR_PATH}' \
--LOADER_2ND='${LOADER_2ND_PATH}' \
--compress='${FIP_COMPRESS}'
${Q}echo " [LS] " $$(ls -l '${BUILD_PLAT}/fip.bin')

fip目标首先完成bl2(第二阶段引导器)的构建,随后打包一些芯片配置。

最后终于到了震撼人心的时候,通过genfip工具把上面构建出来那一堆镜像打包成一个。

启动过程分析#

这一套启动流程其实借鉴的是arm的ATF启动。

ATF启动流程
ATF启动流程

接下来我们借助ATF的启动流程理解一下整个异构操作系统是如何一步一步启动起来的

我们先来看看调试串口的日志信息

C. E:RES▒C. E:RES▒C. E:RES▒C. E:RE▒C. E:RESC.C.SCS/0/▒C.SCS/0/▒C.SCS/0/▒C.SCS/0/▒C.SCS/0/▒C.SCS/0/▒C.SCS/0/▒C.SCS/0/0▒C.SCS/0/▒C.SCS/0/▒C.SCS/0/▒C.SCS/0/▒C.SCS/0/▒C.SCS/0/▒C.SCS/0/▒C.SCS/0/▒C.SCS/0/▒C.SCS/0/▒C.SCS/0/▒C.SCS/0/▒C.SCS/0/▒C.SCS/0/▒C.SCS/0/▒C.SCS/0/▒C.SCS/0/▒C.SCS/0/▒C.SCS/0/▒C.SCS/0/▒C.SCS/0/▒C.SCS/0/▒C.SCS/0/▒C.SCS/0/▒C.SCS/0/▒C.SCS/0/▒C.SCS/0/▒C.SCS/0/▒C.SCS/0/▒C.SCS/0/▒C.SCS/0/0.WD.URPL.SDI/25000000/6000000.BS/SD.PS.SD/0x0/0x1000/0x1000/0.PE.BS.SD/0x1000/0x8400/0x8400/0.BE.J.
FSBL Jb2829:g27ada50:2024-07-31T05:11:50+08:00
st_on_reason=d0000
st_off_reason=0
P2S/0x1000/0xc00a400.
SD/0x9400/0x1000/0x1000/0.P2E.
DPS/0xa400/0x2000.
SD/0xa400/0x2000/0x2000/0.DPE.
cv181x DDR init.
ddr_param[0]=0x78075562.
pkg_type=5
D1_3_2
DDR3-2G-QFN
Data rate=1866.
DDR BIST PASS
PLLS.
PLLE.
C2S/0xc400/0x8fe00000/0x13200.
R2TE:.00/0x13200/0x13200/0.RSC.
.[1M.S4/601x18f162]0P0r/e0x 8sy0s0t0e0m00 0in/i0tx 1dc2o0ne0
RT: [1.467648]CVIRTOS Build Date:Jul 31 2024 (Time :05:11:50)
RT: [1.473222]Post system init done
RT: [1.476310]dump_print_enable & log will not print
SD/0x1f600/0x1c200/0x1c200/0.ME.
L2/0x3b800.
SD/0x3b800/0x200/0x200/0.L2/0x414d3342/0xcafe8c84/0x80200000/0x37600/0x37600
COMP/1.
SD/0x3b800/0x37600/0x37600/0.DCP/0x80200020/0x1000000/0x81900020/0x37600/1.
DCP/0x74182/0.
Loader_2nd loaded.
Use internal 32k
Jump to monitor at 0x80000000.
OPENSBI: next_addr=0x80200020 arg1=0x80080000
OpenSBI v0.9-56-gab9b8f8
____ _____ ____ _____
/ __ \ / ____| _ \_ _|
| | | |_ __ ___ _ __ | (___ | |_) || |
| | | | '_ \ / _ \ '_ \ \___ \| _ < | |
| |__| | |_) | __/ | | |____) | |_) || |_
\____/| .__/ \___|_| |_|_____/|____/_____|
| |
|_|
Platform Name : Cvitek. CV181X ASIC. C906.
Platform Features : mfdeleg
Platform HART Count : 1
Platform IPI Device : clint
Platform Timer Device : clint
Platform Console Device : uart8250
Platform HSM Device : ---
Platform SysReset Device : ---
Platform Suspend Device : cvi-suspend
Firmware Base : 0x80000000
Firmware Size : 144 KB
Runtime SBI Version : 0.3
Domain0 Name : root
Domain0 Boot HART : 0
Domain0 HARTs : 0*
Domain0 Region00 : 0x0000000074000000-0x000000007400ffff (I)
Domain0 Region01 : 0x0000000080000000-0x000000008003ffff ()
Domain0 Region02 : 0x0000000000000000-0xffffffffffffffff (R,W,X)
Domain0 Next Address : 0x0000000080200020
Domain0 Next Arg1 : 0x0000000080080000
Domain0 Next Mode : S-mode
Domain0 SysReset : yes
Domain0 SysSuspend : yes
Boot HART ID : 0
Boot HART Domain : root
Boot HART ISA : rv64imafdcvsux
Boot HART Features : scounteren,mcounteren,time
Boot HART PMP Count : 16
Boot HART PMP Granularity : 4096
Boot HART PMP Address Bits: 38
Boot HART MHPM Count : 8
Boot HART MHPM Count : 8
Boot HART MIDELEG : 0x0000000000000222
Boot HART MEDELEG : 0x000000000000b109
U-Boot 2021.10-gc8d23960 (Jul 31 2024 - 05:11:29 +0800) soph
DRAM: 254 MiB
gd->relocaddr=0x8b0c8000. offset=0xaec8000
MMC: cv-sd@4310000: 0
Loading Environment from nowhere... OK
In: serial
Out: serial
Err: serial
Net:
Warning: ethernet@4070000 (eth0) using random MAC address - 22:e4:6e:4e:08:75
eth0: ethernet@4070000
Hit any key to stop autoboot: 0
Boot from SD ...
switch to partitions #0, OK
mmc0 is current device

最先运行的是BL1程序,这一程序由芯片厂商直接烧录进ROM里,上电或硬复位后从这里开始运行,它会最低限度的初始化时钟系统(低速),同时读取配置引脚以确定启动设备与启动A53计算核还是C906计算核心。这些任务准备完成后,如果启动设备为sd(emmc)设备,将会检查并装载FAT32文件系统,如果是裸烧,将从第一分区中载入fip.bin,如果已经烧录完成,将从boot分区中载入fip.bin

fip.bin是ATF启动中所需要的软件包,其中包含了各个阶段的镜像及其地址。

BL1程序随后从fip.bin中获得BL2程序的地址,并开始复制BL2程序。

注意,此时DDR还未被初始化,所以BL2不能被加载进DDR,让我们看看它的链接脚本。

MEMORY {
RAM (rwx): ORIGIN = BL2_BASE, LENGTH = BL2_SIZE
}

哎,那么这个BL2_BASE在哪里呢?

在includes/mmap.h里面可以看到

#define BL_RAM_BASE TPU_SRAM_BASE
#define BL_RAM_SIZE TPU_SRAM_SIZE
/*
* IO buffer specific defines.
* block IO buffer's start address and size must be block size aligned
*/
#define BL2_BASE (BL_RAM_BASE)
#define BL2_SIZE (0x37000)
#define BL2_ENTRY_OFFSET 32

其实BL2是被加载进神经网络加速器的SRAM下了

BL2的入口点在fsbl/lib/cpu/riscv下面,做的还是些搞一搞向量表,搞一搞标志位,搞一搞cache,搞一搞状态寄存器之类的任务,随后进入c环境bl2_main.

void bl2_main(void)
{
// Start of addition
set_baudrate();
// End of addition
enum CHIP_CLK_MODE mode;
uint32_t v = p_rom_api_get_boot_src();
if (v == BOOT_SRC_UART) {
console_init(0, PLAT_UART_CLK_IN_HZ, UART_DL_BAUDRATE);
}
ATF_STATE = ATF_STATE_BL2_MAIN;
time_records->fsbl_start = read_time_ms();
NOTICE("\nFSBL %s:%s\n", version_string, build_message);
INFO("sw_info=0x%x\n", get_sw_info()->value);
INFO("fip_param1: param_cksum=0x%x param2_loadaddr=0x%x\n", fip_param1->param_cksum,
fip_param1->param2_loadaddr);
INFO("CP_STATE_REG=0x%x\n", mmio_read_32(0x0E000018));
// print_sram_log();
lock_efuse_chipsn();
setup_dl_flag();
switch_rtc_mode_1st_stage();
set_rtc_en_registers();
load_ddr();
#ifdef OD_CLK_SEL
mode = CLK_OD;
#else
#ifdef VC_CLK_OVERDRIVE
mode = CLK_VC_OD;
#else
mode = CLK_ND;
#endif
#endif
load_rest(mode);
NOTICE("BL2 end.\n");
while (1)
;
}

也就是说,调试信息的第二行开始FSBL:xxxxxx就是BL2已经启动的标志

后面的任务就很清晰了,初始化rtc域

随后load_ddr调用ddr初始化程序

也就是genfip中声明的DDR_PARAM镜像

随后我们看load_rest函数

int load_rest(enum CHIP_CLK_MODE mode)
{
int retry = 0;
uint64_t monitor_entry = 0;
uint64_t loader_2nd_entry = 0;
// Init sys PLL and switch clocks to PLL
sys_pll_init(mode);
retry_from_flash:
for (retry = 0; retry < p_rom_api_get_number_of_retries(); retry++) {
if (load_blcp_2nd(retry) < 0)
continue;
if (load_monitor(retry, &monitor_entry) < 0)
continue;
if (load_loader_2nd(retry, &loader_2nd_entry) < 0)
continue;
break;
}
if (retry >= p_rom_api_get_number_of_retries()) {
switch (p_rom_api_get_boot_src()) {
case BOOT_SRC_UART:
case BOOT_SRC_SD:
case BOOT_SRC_USB:
WARN("DL cancelled. Load flash. (%d).\n", retry);
// Continue to boot from flash if boot from external source
p_rom_api_flash_init();
goto retry_from_flash;
default:
ERROR("Failed to load rest (%d).\n", retry);
panic_handler();
}
}
sync_cache();
console_flush();
switch_rtc_mode_2nd_stage();
if (monitor_entry) {
NOTICE("Jump to monitor at 0x%lx.\n", monitor_entry);
jump_to_monitor(monitor_entry, loader_2nd_entry);
} else {
NOTICE("Jump to loader_2nd at 0x%lx.\n", loader_2nd_entry);
jump_to_loader_2nd(loader_2nd_entry);
}
return 0;
}

首先启动PLL,满频运行

load_blcp_2nd(协处理器第二阶段,在本项目中也就是FreeRTOS,函数会装载RTOS的镜像并校验,随后将实时核复位到FreeRTOS入口点

if (rtos_base == CVI_RTOS_MAGIC_CODE) {
mmio_write_32(AXI_SRAM_RTOS_BASE, fip_param2.blcp_2nd_runaddr);
} else {
reset_c906l(fip_param2.blcp_2nd_runaddr);
}
NOTICE("C2E.\n");

实时核输出
实时核输出

也就是这些调试信息

随后的load_monitor与load_loader_2nd只装载镜像,而不运行,随后在处理cache同步,更新rtc域后,正式跳转进入monitor,也就是openSBI,相当于ARM启动流程中的BL31,该程序初始化后将一直保留。

最后BL2将执行权限移交给loader_2nd也就是u-boot,随后u-boot初始化,准备引导内核镜像。

支持与分享

如果这篇文章对你有帮助,欢迎分享给更多人或赞助支持!

赞助
SG2002(CVITEK) Linux+RTOS异构操作系统bootloader构建与启动分析
https://blog.microorange.online/posts/sg2002_genfip_260610/sg2002_genfip/
作者
MicroOrange
发布于
2026-06-10
许可协议
CC BY-NC-SA 4.0
Profile Image of the Author
MicroOrange
其实是一颗橘子
公告
欢迎来到我的博客!这是一则示例公告。
音乐
封面

音乐

暂未播放

0:00 0:00
暂无歌词
分类
标签
站点统计
文章
2
分类
2
标签
4
总字数
10,311
运行时长
0
最后活动
0 天前

文章目录