小核引导RTOS---RISC-V C906

文章目录

    • 参考
    • 日志
    • 编译框架
      • 目标fip
    • 启动流程
      • fip文件组成
      • BL2程序
    • 总结
    • 思考
    • 备注


参考

  • 参考1. How does FSBL load the FreeRTOS on the small core and execute it?
  • 参考2. Duo now supports big and little cores?Come and play!Milk-V Duo, start!
  • 参考3. 使用uboot引导自己的操作系统
  • 参考4. 安全启动介绍

日志

FSBL Jb2829:g362832ac6-dirty:2024-04-02T13:31:11+00:00				# 版本信息
st_on_reason=40f0003
st_off_reason=800e0003
P2S/0x1000/0xc00a400.
SD/0x9400/0x1000/0x1000/0.P2E.
DPS/0xa400/0x2000.
SD/0xa400/0x2000/0x2000/0.DPE.
DDR init.							# DDR 初始化
ddr_param[0]=0x78075562.
pkg_type=1
D2_4_1
DDR3-4G-BGA
Data rate=1866.
DDR BIST PASS
PLLS/OD.
C2S/0xc400/0x9fe00000/0x3600.
 2RET.:00/0x3600/0x3600/0.RSC.
 [M1S./208x2f8a0000]/P0rxe8 0s0y0s0t0e0m0 /i0nxi1tb 0d0o0n.e

RT: [1.289126]CVIRTOS Build Date:Apr  2 2024  (Time :13:31:11) 		# FreeRTOS 启动;
RT: [1.295040]Post system init done
RT: [1.298355]create cvi task
RT: [1.301172][cvi_spinlock_init] succeess
RT: [1.305076]prvCmdQuRunTask run
SD/0xfa00/0x1b000/0x1b000/0.ME.
L2/0x2aa00.
SD/0x2aa00/0x200/0x200/0.L2/0x414d3342/0xcafedbb7/0x80200000/0x37400/0x37400
COMP/1.
SD/0x2aa00/0x37400/0x37400/0.DCP/0x80200020/0x1000000/0x81900020/0x37400/1.
DCP/0x73c7a/0.
Loader_2nd loaded.					# 第二阶段:Opensbi;
Switch RTC mode to xtal32k
Jump to monitor at 0x80000000.
OPENSBI: next_addr=0x80200020 arg1=0x80080000
OpenSBI v0.9

可知:

  1. FSBL Jb2829...,FSBL启动,打印版本信息;
  2. DDR初始化完成后,紧跟RTOS任务初始化打印,中间无引导小核的工作打印

所以可以先对FSBL有个大致认知,再确认RTOS启动过程。

编译框架

# fsbl/Makefile

.DEFAULT_GOAL := all	# 如果没有指定.DEFAULT_GOAL,make会执行第一个在Makefile中定义的目标。
MAKEOVERRIDES =			# 不传递任何父make的变量给子make,保持子make实例的独立性

ARCH ?=					# make 传参可带ARCH=xxx 指定架构;

ifneq ($(origin CROSS_COMPILE),command line)
	ifeq ($(ARCH),riscv)	# 如果不是命令行 make ARCH=xxx 形式,指明由RiscV核心来完成引导启动工作
		CROSS_COMPILE := ${CROSS_COMPILE_GLIBC_RISCV64}
		BOOT_CPU ?= riscv
...
ifeq (${CHIP_ARCH},)		# 必带参数:CHIP_ARCH
	$(error CHIP_ARCH is undefined)
...
ifeq (${CROSS_COMPILE},)	# 必带参数:CROSS_COMPILE
	$(error CROSS_COMPILE is undefined)
...
HOSTCC		:=	gcc
export HOSTCC

CC			:=	${CROSS_COMPILE}gcc
...							# 工具链配置
include ${MAKE_HELPERS_DIRECTORY}build_macros.mk	# Makefile 宏定义,如:assert_boolean、add_define_val
include ${MAKE_HELPERS_DIRECTORY}plat_helpers.mk	# Make 辅助实现
...
include ${PLAT_MAKEFILE_FULL}	# 在plat_makefiles.mk中定义,指向:plat/${CHIP_ARCH}/platform.mk文件,
# fsbl/plat/sg200x/platform.mk,工作:
# - 定义哪些源码文件加入编辑链接;
# - 定义链接脚本文件;

all: fip bl2 blmacros		# 第一目标all,依赖于fip、bl2、blmaacros
...
include ${MAKE_HELPERS_DIRECTORY}fip.mk	# 目标:fip
$(eval $(call MAKE_BL,2))				# 目标:bl2
$(eval $(call MAKE_BL,macros))			# 目标:blmacros

目标fip

目标fip有如下涉及RTOS的内容

# fsbl/make_helpers/fip.mk
...
fip: fip-all										# 目标fip 依赖于fip-all
...
fip-all: fip-dep
	$(print_target)
	${Q}echo "  [GEN] fip.bin"
	${Q}. ${BUILD_PLAT}/blmacros.env && \
	${FIPTOOL} -v genfip \
		'${BUILD_PLAT}/fip.bin' \
		'${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}'
		...	

由实际编译过程打印有(为简洁,将绝对路径中的SDK顶层目录替换为${SDK_TOP}):

. ${SDK_TOP}/fsbl/build/cv1813h_milkv_duos_sd/blmacros.env && \
./plat/cv181x/fiptool.py -v genfip \
        '${SDK_TOP}/fsbl/build/cv1813h_milkv_duos_sd/fip.bin' \
        --MONITOR_RUNADDR="${MONITOR_RUNADDR}" \
        --BLCP_2ND_RUNADDR="${BLCP_2ND_RUNADDR}" \
        --CHIP_CONF='${SDK_TOP}/fsbl/build/cv1813h_milkv_duos_sd/chip_conf.bin' \
        --NOR_INFO='FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF' \
        --NAND_INFO='00000000'\
        --BL2='${SDK_TOP}/fsbl/build/cv1813h_milkv_duos_sd/bl2.bin' \
        --BLCP_IMG_RUNADDR=0x05200200 \
        --BLCP_PARAM_LOADADDR=0 \
        --BLCP=test/empty.bin \
        --DDR_PARAM='test/sophon/ddr_param.bin' \
        --BLCP_2ND='${SDK_TOP}/freertos/cvitek/install/bin/cvirtos.bin' \
        --MONITOR='../opensbi/build/platform/generic/firmware/fw_dynamic.bin' \
        --LOADER_2ND='${SDK_TOP}/u-boot-2021.10/build/cv1813h_milkv_duos_sd/u-boot-raw.bin' \
        --compress='lzma'	

截取其中的关键信息:

  • --BLCP_2ND='${SDK_TOP}/freertos/cvitek/install/bin/cvirtos.bin' \,RTOS定义为BLCP_2ND;

    其运行地址BLCP_2ND在源头在板型目录下的memmap.py文件中定义,如文件:build/boards/cv181x/cv1813h_milkv_duos_sd/memmap.py

    class MemoryMap:
    	...
    	DRAM_BASE = 0x80000000
        DRAM_SIZE = 512 * SIZE_1M
    
        # ==============
        # C906L FreeRTOS
        # ==============
        FREERTOS_SIZE = 2 * SIZE_1M
        # FreeRTOS is at the end of DRAM
        FREERTOS_ADDR = DRAM_BASE + DRAM_SIZE - FREERTOS_SIZE
        FSBL_C906L_START_ADDR = FREERTOS_ADDR
    

    可知:

    • Duos 内存起始地位:0x80000000;大小:512MB;
    • FreeRTOS镜像运行地址(非加载地址):0x9fe00000(内存倒数2MB位置);大小:2MB;

    转化路径:mempa.py – build/scripts/mmap.mk --> build/output/${PROJECT_FULLNAME}/cvi_board_memmap.h。

    关联方式:软链如上cvi_board_memmap.h文件链接到:fsbl/build/cvi_board_memmap.h;

  • --MONITOR='${SDK_TOP}/opensbi/build/platform/generic/firmware/fw_dynamic.bin' \,Opensbi定义为MONITOR;

    借鉴ATF,MilkV将Risc-V下的Opensbi等同于ATF中的BL31,也称为Monitor;

        # ==============================
        # OpenSBI | arm-trusted-firmware
        # ==============================
        # Monitor is at the begining of DRAM
        MONITOR_ADDR = DRAM_BASE
    
        ATF_SIZE = 512 * SIZE_1K
        OPENSBI_SIZE = 512 * SIZE_1K
        OPENSBI_FDT_ADDR = MONITOR_ADDR + OPENSBI_SIZE
    

    可知:

    • Opensbi镜像运行地址:0x80000000;大小:512KB;
  • --LOADER_2ND='${SDK_TOP}/u-boot-2021.10/build/cv1813h_milkv_duos_sd/u-boot-raw.bin' \,U-boot定义为LOADER_2ND;

    借鉴ATF,MilkV将Risc-V下的U-boot等同于ATF中的BL32;

        # ===================
        # FSBL and u-boot-2021
        # ===================
        CVI_UPDATE_HEADER_SIZE = SIZE_1K
        UIMAG_SIZE = 16 * SIZE_1M
    
        # kernel image loading buffer
        UIMAG_ADDR = DRAM_BASE + 24 * SIZE_1M
    	...
        # u-boot's run address and entry point
        CONFIG_SYS_TEXT_BASE = DRAM_BASE + 2 * SIZE_1M
    

    可知:

    • U-boot运行地址:0x8020000(内存偏移2MB);大小:2MB;

实际也可在生成的链接脚本中验证如上内容,在链接脚本blmacros.ld中可查找到如下内容:

# fsbl/build/cv1813h_milkv_duos_sd/blmacros/blmacros.ld

SECTIONS {
 ...
 DEF_DRAM_BASE = 0x80000000;
 DEF_MONITOR_RUNADDR = 0x80000000;
 DEF_BLCP_2ND_RUNADDR = 0x9fe00000;

启动流程

​ 由上一章节可知追寻着BLCP_2ND的引用位置即可知晓RTOS如何加载启动,如头文件:

// fsbl/plat/sg200x/include/mmap.h

#define BLCP_2ND_RUNADDR CVIMMAP_FSBL_C906L_START_ADDR

// fsbl/build/cvi_board_memmap.h 软链指向如下实体文件
// build/output/cv1813h_milkv_duos_sd/cvi_board_memmap.h

#define CVIMMAP_FSBL_C906L_START_ADDR 0x9fe00000  /* offset 510.0MiB */

​ 然而在FSBL源码中没有找到任何一处直接引用该宏定义的位置,所以可以有推论:FSBL并非直接编码该地址信息,而是间接传递地址信息。实际使用有可知,引导文件fip.bin是按对列的要求拼接的,这个其中就包含了关键信息BLCP_2ND_RUNADDR

所以先要对fip.bin文件的组成有一个了解。

fip文件组成

​ 参考SG200x厂商算能的手册信息,有fip.bin镜像结构与启动流程如下:

在这里插入图片描述

而只是大致的分部结构,参考Python打包脚本与结构定义头文件可以整理出具体的结构大小与分布,文件如下:

  • fsbl/plat/sg200x/fiptool.py,fip.bin文件的python生成脚本;

    class FIP下可以看到param1、body1、param2、body2的定义;

  • fsbl/plat/sg200x/include/bl2.h,param2 结构体定义;

  • fsbl/plat/${CHIP_ARCH}/include/platform.h,param1 结构体定义;

整理后的内容如下:

在这里插入图片描述

可知:

  1. Body1中第一个镜像是BLCP,由目标fip章节可获知BLCP的信息:

            --BLCP_IMG_RUNADDR=0x05200200 \
            --BLCP_PARAM_LOADADDR=0 \
            --BLCP=test/empty.bin \
    

    其中:

    • BLCP,即镜像文件,指向:${FSBL_DIR}/test/empty.bin,如其名内容为空,大小为0;
    • 因为BLCP为空,所以其加载地址(BLCP_PARAM_LOADADDR)与运行地址(BLCP_IMG_RUNADDR)信息就不太有参考价值;
  2. Body1中第二个镜像是BL2,同上可获知BL2的信息:

            --BL2='${SDK_TOP}/fsbl/build/cv1813h_milkv_duos_sd/bl2.bin' \
    

    其中:

    • BL2,即FSBL镜像文件,指向:${FSBL_DIR}/build/cv1813h_milkv_duos_sd/bl2.bin,大小33KB;
    • FSBL是一个阶段的统称,BL2是其中的一个小部分;
    • 在BL2阶段,完成工作:DDR 初始化;
  3. Body2中第一个镜像是DDR_PARAMM,可获知DDR_PARAM信息

            --DDR_PARAM='test/sophon/ddr_param.bin' \
    

    其中:

    • DDR_PARAM,即DDR初始化参数,指向:${FSBL_DIR}/test/sophon/ddr_param.bin,大小:8KB;
    • 十六进制方式查看,未能看到什么组织规范,可能是按特定的数据结构组织也可能是加密过,尝试在BL2中查看这个文件如何使用的才能知晓;
  4. Body2中等二个文件是:BLCP_2N,可获知信息:

            --BLCP_2ND_RUNADDR="${BLCP_2ND_RUNADDR}" \
            --BLCP_2ND='${SDK_TOP}/freertos/cvitek/install/bin/cvirtos.bin' \
    

    其中:

    • BLCP_2ND,即第二个BLCP镜像文件,指向:${SDK_TOP}/freertos/cvitek/install/bin/cvirtos.bin,大小:14KB;
    • 这即是我们查找的RTOS镜像文件,其运行地址为:0x9FE0_0000;
  5. Body2中的Monitor、LOADER_2ND再分析;

综上,如果按Body的组织顺序应有启动流程:BLCP => BL2 => BLCP_2ND => MONITOR => LOADER_2ND 。

而BLCP为空,结合实际内容启动流程为:bl2 => FreeRTOS => opensbi => U-boot;

所以可以有猜测:bl2 完成了FreeRTOS的引导启动工作。

BL2程序

​ 由章节编译框架章节可知:FSBL最终是为了生成fip.bin文件,其依赖于bl2.bin文件。Makefile中的目标依赖关系也是如此:

# fsbl/Makefile
$(eval $(call MAKE_BL,2))

# fsbl/make_helpers/build_macros.mk
# MAKE_BL macro defines the targets and options to build each BL image.
# Arguments:
#   $(1) = BL stage (2, 2u, 30, 31, 32, 33)
#   $(2) = FIP command line option (if empty, image will not be included in the FIP)
define MAKE_BL
        $(eval BUILD_DIR  := ${BUILD_PLAT}/bl$(1))
        $(eval BL_SOURCES := $(BL$(call uppercase,$(1))_SOURCES))
        $(eval SOURCES    := $(BL_SOURCES))
        $(eval OBJS       := $(addprefix $(BUILD_DIR)/,$(call SOURCES_TO_OBJS,$(SOURCES))))
        $(eval LINKERFILE := $(call IMG_LINKERFILE,$(1)))
        $(eval MAPFILE    := $(call IMG_MAPFILE,$(1)))
        $(eval ELF        := $(call IMG_ELF,$(1)))
        $(eval SYM        := $(call IMG_SYM,$(1)))
        $(eval DUMP       := $(call IMG_DUMP,$(1)))
        $(eval BIN        := $(call IMG_BIN,$(1)))
        $(eval BL_LINKERFILE := $(BL$(call uppercase,$(1))_LINKERFILE))     
...
$(ELF): $(OBJS) $(LINKERFILE) | bl$(1)_dirs	# 生成$(OBJS) $(LINKERFILE)前需要先保证 bl$(1)_dirs 目标已经创建
	@echo "  LD      $$@"
	@echo 'const char build_message[] = $(BUILD_MESSAGE_TIMESTAMP); \
	       const char version_string[] = "${VERSION_STRING}";' | \
		$$(CC) $$(TF_CFLAGS) $$(CFLAGS) -xc -c - -o $(BUILD_DIR)/build_message.o	# 定义两个变量保存编译信息并编译生成目标文件build_mesage.o,再后面链接过程中会被用到
	$$(Q)$$(LD) -o $$@ $$(TF_LDFLAGS) $$(LDFLAGS) -Map=$(MAPFILE) \
		--script $(LINKERFILE) $(BUILD_DIR)/build_message.o ${BL2_RLS_OBJS} $(OBJS) $(LDLIBS)
...
$(BIN): $(ELF)
	@echo "  BIN     $$@"
	$$(Q)$$(OC) -O binary $$< $$@			# 使用${CROSS_COMPILE}objcopy,从elf转化为bin;
	@${ECHO_BLANK_LINE}
	@echo "Built $$@ successfully"
	@${ECHO_BLANK_LINE}

.PHONY: bl$(1)
bl$(1): $(BIN) $(SYM) $(DUMP)
	$$(print_target)
...
endef

可知:

  • bl$(1),即bl2,依赖于目标:$(BIN)、$(SYM)、$(DUMP)分别对应${SDK_TOP}/fsbl/build/${CHIP_ARCH}目录下的:bl2.bin,bl2.sym、bl2.dis;

  • $(BIN),即bl2.bin,依赖于目标:$(ELF),即bl2.elf;

    其生成方法为:使用工具链的objcopy工具由ELF格式文件bl2.elf生成二进制文件bl2.bin。

  • ELF,即bl2.elf,依赖于目标:$(OBJS) $(LINKERFILE)

    其生成方法为:

    1. $(CC) 。。。 -xc -c - -o $(BUILD_DIR)/build_message.o,生成版本信息文件build_mesage.o:使用管道将标准输入编辑为目标文件;

      注:-参数通常是用来表示要编译的源文件的位置,如果没有指定具体的源文件名,可能是表示要从标准输入读取源代码。

    2. $(LD) -o $$@ 。。。 --script $(LINKERFILE) $(BUILD_DIR)/build_message.o ${BL2_RLS_OBJS} $(OBJS) $(LDLIBS),bl2.elf链接方法,其中:

      • --script $(LINKERFILE),指定链接脚本:$(LINKERFILE),即:BL_LINKERFILE -> BL2_LINKERFILE -> plat/${CHIP_ARCH}/bl2/bl2.ld.S;
      • $(BUILD_DIR)/build_message.o,保存编译信息的目标文件,bl2.elf依赖的目标文件之一;
      • $(OBJS),依赖的目标文件,bl2.bin依赖的主要目标文件,即:OBJS ->来源于所有源码文件(*.c *.s): SOURCES -> BL_SOURCES -> BL2_SOURCES ,具体有哪些源码文件由${CHIP_ARCH}下的platform.mk文件决定,如:fsbl/plat/sg200x/platform.mk;
      • ${BL2_RLS_OBJS} $(LDLIBS),为空,不用关注;

综上可知:bl2.bin 文件由bl2.elf文件而来,bl2.elf文件由bl2源码编译链接而来。bl2.elf文件的链接由链接脚本plat/${CHIP_ARCH}/bl2/bl2.ld.S决定:

#include <platform.h>

#ifdef __riscv
	OUTPUT_FORMAT("elf64-littleriscv")
	OUTPUT_ARCH(riscv)
...
#endif
ENTRY(bl2_entrypoint)				# 入口

MEMORY {
    RAM (rwx): ORIGIN = BL2_BASE, LENGTH = BL2_SIZE			# 0x0C000000, 0x37000(220KB)
...        
SECTIONS
{
    . = BL2_BASE;					# 代码段从SRAM起始位置

可知:

  • ENTRY(bl2_entrypoint),bl2.elf程序的主入口为符号:bl2_entrypoint;
  • BL2在SRAM上运行,起始地址 0x0C00_0000,大小 0x3_7000(220KiB);1

milkV-Duo主干源码SDK中的fsbl未完全开放源码,因此需要从另一个fsbl仓库拉源码替换到主干SDK的fsbl目录。

  • MilkV Duo 主干SDK源码仓库:https://github.com/milkv-duo/duo-buildroot-sdk;
  • FSBL开源的源码仓库:https://github.com/milkv-duo/fsbl;

取在fsbl源码中查找该程序入口符号,查找到:fsbl/lib/cpu/riscv/bl2_entrypoint.S

bl2_entrypoint:				# 入口
	j bl2_entrypoint_real	# 调用真正入口:bl2_entrypoint_real
	.word 0 // resvered
...
bl2_entrypoint_real:
  atf_state_set x28, x29, ATF_STATE_BL2_ENTRY_POINT		# 将值ATF_STATE_BL2_ENTRY_POINT写入寄存器REG_GP_REG1
  li x1, 0					# 通用寄存器清零
  ...						# 为跳转到C语言环境做准备,涉及:
  							#	mtvec 向量表;plic 中断控制器;I-Cache、D-cache 缓存;BSS段清零;;
  call bl2_main				# 调用C程序入口bl2_main
  j die
...

可知:

  • bl2_entrypoint只是纸面入口,实际起作用是bl2_entrypoint_real;
  • atf_state_set,使用寄存器REG_GP_REG1保存当前启动状态,对应关系:bl2_entrypoint -> ATF_STATE_BL2_ENTRY_POINT(0xB2000000);
  • bl2_entrypoint_real使用risc-v汇编代码编写,主要完成C语言环境的初始化工作,最终由bl2_mian进入C程序;

查找bl2_main入口,查找到:fsbl/plat/${CHIP_ARCH}/bl2/bl2_main.c,如:fsbl/plat/sg200x/bl2/bl2_main.c

// fsbl/plat/sg200x/bl2/bl2_main.c

void bl2_main(void)
	uint32_t v = p_rom_api_get_boot_src();		// 查看启动方式;
	...
	set_baudrate();								// 串口设置波特率:115200
	ATF_STATE = ATF_STATE_BL2_MAIN;				// 更新寄存器REG_GP_REG1,记录启动状态
	time_records->fsbl_start = read_time_ms();

	NOTICE("\nFSBL %s:%s\n", version_string, build_message);	// 打印“编译信息”,关键信息打印
	...
	load_ddr();					// DDR 初始化
		for (retry = 0; retry < p_rom_api_get_number_of_retries(); retry++) {
			if (load_param2(retry) < 0)			// 从Flash中载入fip_param2到SRAM
				continue;
			if (load_ddr_param(retry) < 0)		// 从Flash中载入DDR_PARAM到SRAM
				continue;
            。。。
			ddr_init(&sram_union_buf.ddr_param);// DDR初始化
	load_rest(mode);			// 加载拿下启动程序到DDR并跳转运行
	NOTICE("BL2 end.\n");		// 此后的内容不应执行,因为在load_rest中就跳转到LOADER_2ND与MONITOR;

	while (1)
		;

可知:

  • p_rom_api_get_boot_src为rom中芯片中固化的BL1程序符号,此时还是实模式直接调用。该接口用于获取当前的启动模式,又大致分为两大类:

    1. Flash启动,包含:SPI_NAND、SPI_NOR、EMMC;
    2. 下载启动,包含:SD、USB、UART;
  • ATF_STATE = ATF_STATE_BL2_MAIN,更新寄存器REG_GP_REG1,标记已经进入bl2_main阶段;

  • NOTICE("\nFSBL %s:%s\n", version_string。。。,即设备上电时打印的版本信息,内容即来自BL2程序章节中提及的build_mesage.o文件;

  • load_ddr(),DDR初始化,暂不完全展开。可知:在DDR初始化前,完成了读取SD卡中fip_param2、ddr_param文件到SRAM中

    load_param2(retry)
    	NOTICE("P2S/0x%lx/%p.\n", sizeof(fip_param2), &fip_param2);            
    	p_rom_api_load_image(&fip_param2, fip_param1->param2_loadaddr, PARAM2_SIZE, retry);
    

    可知:

    • 调用p_rom_api_load_image接口完成:从SD卡拷贝fip_param2到SRAM中,其中fip_param2加载地址在fip_param1.param2_loadaddr中记录

    • ddr_param类似,但其加载地址记录在fip_param2中,具体为:fip_param2.ddr_param_loadaddr;

    • 实际打印信息如下:

      P2S/0x1000/0xc00a400.
      SD/0x9400/0x1000/0x1000/0.P2E.
      

      可知:fip_param2 大小为4KB,将从SD卡地址0x1000 拷贝到SRAM地址0x0C00_A400(非固定值,在bl2.elf链接时确定);

  • load_rest(mode),BL2程序的主要初始化工作,需要单独展开说明。另外,这部分的工作都转入DDR环境中运行;

  • NOTICE("BL2 end。。。,不应执行到这个位置,因为在此之前就已经跳转到MONITOR阶段,所以在上电启动日志打印中看不到这个内容;

综上可知:bl2_main直接展现的内容较少,需要再跟进loader_rest实现。

loader_rest的实现在路径:fsbl/plat/${CHIP_ARCH}/bl2/bl2_opt.c,如:fsbl/plat/sg200x/bl2/bl2_opt.c

struct fip_param1 *fip_param1 = (void *)PARAM1_BASE;			// fip_param1 地址,关键信息
static struct fip_param2 fip_param2 __aligned(BLOCK_SIZE);

int load_rest(enum CHIP_CLK_MODE mode)
    。。。
	sys_pll_init(mode);    		// PLL锁相环,时间初始化

	load_blcp_2nd(retry);		// BLCP_2NC 加载
		。。。
		time_records->release_blcp_2nd = time_records->ddr_init_end;// 启动时间记录
		NOTICE("C2S/0x%x/0x%x/0x%x.\n", fip_param2.blcp_2nd_loadaddr, fip_param2.blcp_2nd_runaddr, fip_param2.blcp_2nd_size);
		if (!fip_param2.blcp_2nd_runaddr) {		// 检查运行地址是否为空,即是否存在bl2程序
		if (!IN_RANGE(fip_param2.blcp_2nd_runaddr, DRAM_BASE, DRAM_SIZE)) {	// 检查blcp_2nd 运行地址是否在DRAM范围内?
		if (!IN_RANGE(fip_param2.blcp_2nd_runaddr + fip_param2.blcp_2nd_size, DRAM_BASE, DRAM_SIZE)) {	// 检查blcp_2nd 大小是否超出DRAM范围?
		p_rom_api_load_image((void *)(uintptr_t)fip_param2.blcp_2nd_runaddr, fip_param2.blcp_2nd_loadaddr, fip_param2.blcp_2nd_size, retry);        
		crc = p_rom_api_image_crc((void *)(uintptr_t)fip_param2.blcp_2nd_runaddr, fip_param2.blcp_2nd_size);       
		ret = dec_verify_image((void *)(uintptr_t)fip_param2.blcp_2nd_runaddr, fip_param2.blcp_2nd_size, 0, fip_param1);        
		flush_dcache_range(fip_param2.blcp_2nd_runaddr, fip_param2.blcp_2nd_size);
		rtos_base = mmio_read_32(AXI_SRAM_RTOS_BASE);
		init_comm_info(0);
		。。。      
		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("RSC.\n");				// 小核启动前,打印:RSC.
				mmio_clrbits_32(0x3003024, 1 << 6);            
				mmio_setbits_32(SEC_SYS_BASE + 0x04, 1 << 13);
				mmio_write_32(SEC_SYS_BASE + 0x20, reset_address);			// 将RTOS入口地址低32位写入寄存器:0x020B_0020
				mmio_write_32(SEC_SYS_BASE + 0x24, reset_address >> 32);	// 将RTOS入口地址高32位写入寄存器:0x020B_0024
				mmio_setbits_32(0x3003024, 1 << 6);

分析如下:

  • fip_param1 = (void *)PARAM1_BASE,fip_param1地址直接由宏硬编码定义,值为:0x0C03_9000。2

  • sys_pll_init(mode),load_rest过程中先对时钟系统进制初始化,暂不展开;

  • load_blcp_2nd(retry),即加载RTOS镜像到DDR中运行,其中有如下过程:

    • NOTICE("C2S/0x%x/0x%x。。。,启动打包信息,C2S解释:C2,BLCP_2ND;S,Start;

      实际打印如:C2S/0xc400/0x9fe00000/0x3600.,即加载地址0xc400拷贝rtos镜像到DDR地址0x9fe0_0000,镜像大小0x3600;

    • RTOS镜像有效性检查,包含:文件是否为空,运行地址是否有效,大小是否溢出?

    • 真正执行向运行地址的镜像搬移;

    • CRC验证搬移过程中无差错;

    • 解密与检验RTOS镜像(未使能安全启动时,动作为空);

    • 刷新RTOS镜像范围内的D-Cache;

    • 检查寄存器AXI_SRAM_RTOS_BASE(地址:0x0E00_007C)是否为默认值CVI_RTOS_MAGIC_CODE(值:0xABC0DEF);

      • 如果仍为默认值,寄存器0x0E00_007C中写入RTOS运行地址:fip_param2.blcp_2nd_runaddr,即:0x9FE0_0000;
      • 如不为默认值(实现测试),寄存器0x020B_0000中写入RTOS运行地址:0x9FE0_0000,重启C906小核开始运行RTOS;

      关于这个AXI_SRAM_RTOS_BASE标致检测,猜测可能是一种防重入机制,具体原因与开源开发同事确认中。

至此找到了小核引导RTOS的启动的关键实现。

总结

​ 本文主要记录MilkV DuoS 上是如何引导C906L小核运行RTOS的记录,介绍从编译框架、文件打包、源码跟读方式最终确认找到启动入口,过程中记录了一些特别的收获,这其中包含:

  • Makkefile,一些对列属性的使用,如:.DEFAULT_GOAL、MAKEOVERRIDES;一些特别的目标文件生成方法,如:$(CC) 。。。 -xc -c - -o $(BUILD_DIR)/build_message.o,由标准输入生成目标文件;BL1固定符号地址,由BL2来调用,如:p_rom_api_load_image
  • Python,结构体数据的表达,如:Entry.make("MAGIC1", 8, int, b"CVBL01\n\0"),;文件的拼接,如:generate_fip
  • 启动流程,RISC-V兼容ARM的启动流程,可以OpenSbi等效于ARM中的BL31;
  • C906核启动方式较C910手册中提及的方式相似------向寄存器BOOT_REG写入程序入口地址,重启即完成该核的启动;

回顾整个小核CPU引导RTOS启动流程的梳理,即使有一些前置工作,但好像还是有些简单粗暴,可能这是THead系列的特别设计。**庆幸的是MilkV DuoS有两个小核,所以可以切换到A53核引导RTOS的,再梳理一篇。

思考

  1. BootRom 属于不开放的BL1,而BL2中也会有多处调用BL1接口的地方,说明BL1已经集成很多基础的、使用概率较高的实现。

    从行业方案看来,这与树莓派等硬件不开源的方案相类似,形成了一种趋势:随着IC厂商BL1功能变得愈发完备,板级适配(DDR等)工作也由IC厂商完成,最终产品端的BSP开发的需求将会变得越来越弱。

备注

// fsbl/plat/sg200x/include/platform_def.h
#define TPU_SRAM_ORIGIN_BASE 0x0C000000
#define TPU_SRAM_SIZE 0x40000 // 256KiB

#if ROM_LOCATION == ROM_LOCATION_HSPERI_ROM
 #ifdef __riscv
 	#define ROM_BASE 0x04418000 				// no mirrored address for c906b
 	#define TPU_SRAM_BASE TPU_SRAM_ORIGIN_BASE 	// no mirrored address for c906b
 	#define SYSMAP_MIRROR_OFFSET 0x20000000
// -------------------------------------------------
// fsbl/plat/sg200x/include/mmap.h
#define BL_RAM_BASE TPU_SRAM_BASE
#define BL2_BASE (BL_RAM_BASE)
#define BL2_SIZE (0x37000)
#define BOOT_LOG_BUF_BASE (BL2_BASE + BL2_SIZE)
#define BOOT_LOG_BUF_SIZE 0x2000
#define PARAM1_BASE (BOOT_LOG_BUF_BASE + BOOT_LOG_BUF_SIZE)

// 转化
 PARAM1_BASE
     (BOOT_LOG_BUF_BASE + BOOT_LOG_BUF_SIZE)
     	((BL2_BASE + BL2_SIZE) + BOOT_LOG_BUF_SIZE)		
 			0x0C000000 + 0x37000 + 0x2000 = 0x0C03_9000

  1. SRAM 起始地址:0x0C00_0000,大小:0x3_9000(256KiB),即bl2.bin之后还有剩余36KiB空间。 ↩︎

  2. PARAM1_BASE换算 ↩︎

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/525051.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

ES学习笔记01

1.ES安装 下载地址&#xff1a; es官网下载 这里使用的是7.8.0的版本信息 下载完成后解压即可完成安装 2.启动运行 点击bin目录下的elasticsearch.bat文件即可启动 在浏览器中输入localhost:9200显示如下&#xff1a; 在路径中加入对应访问后缀即可访问对应信息 如&#…

【学习】移动端App性能测试流程有哪些

移动端App性能测试是保证App性能表现的重要环节之一。随着移动设备的普及和移动互联网的发展&#xff0c;移动端App的性能测试变得越来越重要&#xff0c;通过科学合理的性能测试可以发现并解决潜在的性能问题优化App运行效果提高用户体验。性能测试旨在评估App在各种场景下的性…

板材的加强筋优化-abaqus操作过程

前言 本示例详细讲解使用abaqus实现板材的加强筋优化的操作过程。 本页讨论 前言一、创建模型1.创建几何部件2.定义材料属性3.创建装配实体4.创建分析步5.创建边界条件及载荷6.划分网格7.创建分析作业并提交分析8.可视化后处理 二、设置优化1.创建优化任务2.创建设计响应3.创…

嵌入式软件工程师如何提高技术深度?

构建坚实且深厚的技术根基&#xff0c;其重要程度可谓举足轻重。唯有在对基础知识进行深入探究和理解的前提之下&#xff0c;方能够在理论的指引之下&#xff0c;持续地深入以及拓展技术领域。倘若缺乏稳固坚实的基础&#xff0c;那么深入开展研究便无从谈起。 在强调技术深度…

说说 HTTP1.0/1.1/2.0 的区别?

文章目录 一、HTTP1.0二、HTTP1.1三、HTTP2.0多路复用二进制分帧首部压缩 四、总结参考文献 一、HTTP1.0 HTTP协议的第二个版本&#xff0c;第一个在通讯中指定版本号的HTTP协议版本 HTTP 1.0 浏览器与服务器只保持短暂的连接&#xff0c;每次请求都需要与服务器建立一个TCP连…

bin、hex、exe、elf文件类型到底有何区别?如何解读hex文件和elf文件?...相关内容都在这里!

《嵌入式工程师自我修养/C语言》系列——bin、hex、exe、elf文件类型到底有何区别&#xff1f;readelf用法全面解读&#xff01; 一、常见文件类型之&#xff1a;bin、hex、elf、exe1.1 bin、hex、exe、elf文件类型到底有何区别&#xff1f;1.2 如何读懂一份hex文件&#xff1f…

数字人解决方案——Wav2lip本地部署

1、安装anaconda anaconda自行下载安装 2、下载wav2lip 在github中搜索wav2lip ​ git clone https://github.com/Rudrabha/Wav2Lip.git ​ 源码到本地 准备脸部检测预训练模型 下载地址&#xff1a;https://www.adrianbulat.com/downloads/python-fan/s3fd-619a316812…

安卓远离手机app

软件介绍 远离手机是专门为防止年轻人上瘾而打造的生活管理类的软件,适度用手机&#xff0c;保护眼睛&#xff0c;节约时间。 下载 安卓远离手机app

C++ 一种简单的软件验证码 程序授权使用 收费付费使用 无需注册 用机器码得到一个加密值 再对比加密值是否一致 只需加密

简单软件授权方案 1、获取机器码&#xff0c;发给软件开发者 2、开发者用机器码加密得到一个密文 发给使用者 3、使用者 用这个密文 与本地计算密文比较密文是否一致&#xff0c;一致就把密文写入到注册表&#xff0c;下次登录从注册表读密文对比。 &#xff08;最重要的是密…

【C++】STL学习之string的使用

&#x1f525;博客主页&#xff1a; 小羊失眠啦. &#x1f3a5;系列专栏&#xff1a;《C语言》 《数据结构》 《C》 《Linux》 《Cpolar》 ❤️感谢大家点赞&#x1f44d;收藏⭐评论✍️ 文章目录 前言一、basic_string二、编码理解三、构造函数相关3.1 无参(默认)构造函数3.2 …

力扣爆刷第114天之CodeTop100五连刷56-60

力扣爆刷第114天之CodeTop100五连刷56-60 文章目录 力扣爆刷第114天之CodeTop100五连刷56-60一、78. 子集二、105. 从前序与中序遍历序列构造二叉树三、43. 字符串相乘四、155. 最小栈五、151. 反转字符串中的单词 一、78. 子集 题目链接&#xff1a;https://leetcode.cn/prob…

一文了解RAID技术基本概念

RAID是数据存储技术&#xff0c;旨在提高磁盘的IO吞吐以及提供更为可靠的数据安全。在实际工作中经常听到RAID相关名称&#xff0c;那么RAID技术的基本概念是什么、不同RAID级别有什么特性&#xff0c;本文将简单介绍&#xff0c;以了解。 1、RAID技术基本概念 1.1 RAID基本概…

uniapp中uni.navigateTo传递变量

效果展示&#xff1a; 核心代码&#xff1a; uniapp中uni.navigateTo传递变量 methods: {changePages(item) {setDatas("maintenanceFunName", JSON.stringify(item)).then((res) > {uni.navigateTo({url: /pages/PMS/maintenance/maintenanceTypes/maintenanceT…

python开发poc2,爆破脚本

#本课知识点和目的&#xff1a; ---协议模块使用&#xff0c;Request 爬虫技术&#xff0c;简易多线程技术&#xff0c;编码技术&#xff0c;Bypass 后门技术 下载ftp服务器模拟器 https://lcba.lanzouy.com/iAMePxl378h 随便创建一个账户&#xff0c;然后登录进去把ip改成…

从头开发一个RISC-V的操作系统(四)嵌入式开发介绍

文章目录 前提嵌入式开发交叉编译GDB调试&#xff0c;QEMU&#xff0c;MAKEFILE练习 目标&#xff1a;通过这一个系列课程的学习&#xff0c;开发出一个简易的在RISC-V指令集架构上运行的操作系统。 前提 这个系列的大部分文章和知识来自于&#xff1a;[完结] 循序渐进&#x…

五一假期来临,各地景区云旅游、慢直播方案设计与平台搭建

一、行业背景 经文化和旅游部数据中心测算&#xff0c;今年清明节假期3天全国国内旅游出游1.19亿人次&#xff0c;按可比口径较2019年同期增长11.5%&#xff1b;国内游客出游花费539.5亿元&#xff0c;较2019年同期增长12.7%。踏青赏花和户外徒步成为假期的热门出游主题。随着…

Matlab 修改图例顺序

对于使用 .m 文件绘制的图片&#xff0c;可以修改程序中图例的顺序来改变图片的图例。如果图片所对应的 .fig 文件已经存在&#xff0c;而且不便修改源程序&#xff0c;则可以通过如下方式来修改图例&#xff1a; step 1: 打开fig文件&#xff0c;然后点击绘图浏览器 step 2&…

STC89C51学习笔记(五)

STC89C51学习笔记&#xff08;五&#xff09; 综述&#xff1a;文本讲述了代码中速写模板的创建、如何将矩阵键盘的按键与数字一一对应以及如何创建一个矩阵键盘密码锁。 一、速写模板 点击“templates”&#xff0c;再鼠标右键选择配置&#xff0c;按照以下方式即可修改一些…

【WEEK6】 【DAY7】MD5 Encryption Transactions【English Version】

2024.4.7 Sunday Following the previous article 【WEEK6】 【DAY3】MySQL Functions【English Version】 Contents 5.3. MD5 Encryption5.3.1. Introduction5.3.2. Testing MD5 Encryption5.3.2.1. Plain Text Passwords5.3.2.2. Implementing Data Encryption5.3.2.3. Encry…

位域与联合体巧妙使用

在编写dbc报文的协议解析时&#xff0c;使用位域运算和联合体的组合&#xff0c;能够巧妙解决字段解析问题&#xff0c;代码看起来整洁又健壮。 #include <algorithm> #include <iostream> #include <vector>using namespace std;typedef union tagCoreCalib…
最新文章