milk-v duo 编译流程二之小核 FreeRTOS 编译

转载自:milk-v duo 编译流程二之小核 FreeRTOS 编译 - 知乎 ,原作者是:燕十三 - 知乎
经过长久的等待,milk-v duo 终于支持小核(平头哥 C906-NOMMU)启动运行 FreeRTOS,本文主要介绍 milk-v duo 原厂 SDK 编译生成 cvirtos.bin,并最终合成 fip.bin 的流程。

1、编译运行

当前 SDK 默认启动了小核,但是没有启动 FreeRTOS 内核调度,需要手工在 SDK 中加入 FreeRTOS 启动相关代码。

  1. 启动 FreeRTOS 调度器

freertos/cvitek/task/comm/src/riscv64/comm_main.c 中创建初始 task, 并启动调度器

#include "FreeRTOS.h"
#include "task.h"

void app_task(void * param)
{
  while(1) {
    printf("Hello RiSC-V!\r\n");
    vTaskDelay(1000);
  }
}

void main_cvirtos(void)
{
	printf("create cvi task\n");


	/* Start the tasks and timer running. */
    xTaskCreate(app_task, "app_task", 1024, NULL, 1, NULL);	
	
	vTaskStartScheduler();

    /* If all is well, the scheduler will now be running, and the following
    line will never be reached.  If the following line does execute, then
    there was either insufficient FreeRTOS heap memory available for the idle
    and/or timer tasks to be created, or vTaskStartScheduler() was called from
    User mode.  See the memory management section on the FreeRTOS web site for
    more details on the FreeRTOS heap http://www.freertos.org/a00111.html.  The
    mode from which main() is called is set in the C start up code and must be
    a privileged mode (not user mode). */
    printf("cvi task end\n");
	
	for (;;)
        ;
}

小核跟大核共用 UART0 作为日志输出口。

  1. 修改FreeRTOS 配置

SDK 默认配置文件 freertos/cvitek/kernel/include/riscv64/FreeRTOSConfig.h 开启了 configUSE_TICK_HOOK 但是未实现 hook 回调,我们先修改位置关闭该宏。

#define configUSE_TICK_HOOK             0

编译后烧录 SD 卡,即可看到启动日志中,FreeRTOS 已经正常启动。

FSBL Jb28g9:g806b8d4d9-dirty:2023-10-26T14:38:35+00:00
st_on_reason=4090003
st_off_reason=0
P2S/0x1000/0x3bc0da00.
SD/0xca00/0x1000/0x1000/0.P2E.
DPS/0xda00/0x2000.
SD/0xda00/0x2000/0x2000/0.DPE.
DDR init.
ddr_param[0]=0x78075562.
pkg_type=3
D3_1_4
DDR2-512M-QFN68
DDR BIST PASS
PLLS.
PLLE.
C2S/0xfa00/0x83f40000/0x2c00.
[R2ET.: 0/0x2c00/0x2c00/0.RSC.
.M.S3/04x2414276]0P0/r0e xs8y0s0t00e0m 0i0n/i0tx1 bdo0n0e0
R
T: [1.348312]CVIRTOS Build Date:Oct 26 2023  (Time :14:38:35) 
RT: [1.353887]Post system init done
RT: [1.356965]create cvi task
RT: [1.359588]Hello RiSC-V!

并在 Linux 启动后也会间隔 1s 打印 RT: [1.359588]Hello RiSC-V!

注:原厂 SDK 编译出来的 fsbl 文件,如使用 ch34x 系列的 USB-TTL芯片,在启动阶段如上信息打印为乱码,只能进入 uboot 阶段才能正常显示。

2、编译入口

通过 build/boards/cv180x/cv1800b_milkv_duo_sd/cv1800b_milkv_duo_sd_defconfig 文件,发现 RTOS 相关配置如下:

CONFIG_ENABLE_FREERTOS=y
CONFIG_ENABLE_RTOS_DUMP_PRINT=y
CONFIG_DUMP_PRINT_SZ_IDX=17
CONFIG_FAST_IMAGE_TYPE=0

其中 CONFIG_ENABLE_FREERTOS 为 RTOS 编译宏开关,在 build/scripts/fip_v2.mk 中判断是否编译 rtos 相关代码

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

根据前文

燕十三:milk-v duo原厂Linux_SDK编译流程0 赞同 · 0 评论文章

中介绍,编译完了u-boot 和 opensbi,继续往回推,来到了 fsbl-build,此时就会编译 RTOS 了。

rtos 在 build/scripts/rtos.mk 中定义,本质是调用了根目录下 freertos/cvitek 目录下 build_cv180x.sh 脚本。

rtos: memory-map
	$(call print_target)
ifeq ($(CHIP_ARCH_L),$(filter $(CHIP_ARCH_L), cv180x))
	cd ${FREERTOS_PATH}/cvitek && ./build_cv180x.sh
else
	cd ${FREERTOS_PATH}/cvitek && ./build_cv181x.sh
endif

rtos-clean:
ifeq (${CONFIG_ENABLE_FREERTOS},y)
	$(call print_target)
	cd ${FREERTOS_PATH}/cvitek && rm -rf build 
endif

3、build_cv180x.sh

build_cv180x.sh 脚本较简单,使用 cmake 依次编译 arch、kernel、common、hal、driver、task 等目标,然后生成 cvirtos.bin 文件,这个文件就是小核 FreeRTOS 运行文件。

注意,cvirtos.bin 文件的地址为 $TOP_DIR/install/bin/cvirtos.bin ,该地址在 rtos.mk 文件中通过 export BLCP_2ND_PATH=${FREERTOS_PATH}/cvitek/install/bin/cvirtos.bin 赋值给了 BLCP_2ND_PATH 变量,这个变量后续合成 fip.bin 中需要用到。

4、小核运行地址

接下来分步骤分析一下小核运行地址。

  1. 链接文件

从 ld 文件入手,查找小核真正运行的地址,链接文件为 freertos/cvitek/scripts/cv180x_lscript.ld

MEMORY
{
   psu_ddr_0_MEM_0 : ORIGIN = CVIMMAP_FREERTOS_ADDR , LENGTH = CVIMMAP_FREERTOS_SIZE
}

可以看到运行地址为:CVIMMAP_FREERTOS_ADDR,大小为:CVIMMAP_FREERTOS_SIZE。那么这 2 个宏在哪里被定义呢。

  1. cvi_board_memmap.h

build/output/cv1800b_milkv_duo_sd/cvi_board_memmap.h 中被定义。

#define CONFIG_SYS_TEXT_BASE 0x80200000  /* offset 2.0MiB */
#define CVIMMAP_ATF_SIZE 0x80000  /* 512.0KiB */
#define CVIMMAP_BOOTLOGO_ADDR 0x82473000  /* offset 36.44921875MiB */
#define CVIMMAP_BOOTLOGO_SIZE 0x0  /* 0.0KiB */
#define CVIMMAP_CONFIG_SYS_INIT_SP_ADDR 0x82300000  /* offset 35.0MiB */
#define CVIMMAP_CVI_UPDATE_HEADER_ADDR 0x813ffc00  /* offset 19.9990234375MiB */
#define CVIMMAP_CVI_UPDATE_HEADER_SIZE 0x400  /* 1.0KiB */
#define CVIMMAP_DRAM_BASE 0x80000000  /* offset 0.0KiB */
#define CVIMMAP_DRAM_SIZE 0x4000000  /* 64.0MiB */
#define CVIMMAP_FREERTOS_ADDR 0x83f40000  /* offset 63.25MiB */
#define CVIMMAP_FREERTOS_RESERVED_ION_SIZE 0x0  /* 0.0KiB */
#define CVIMMAP_FREERTOS_SIZE 0xc0000  /* 768.0KiB */
#define CVIMMAP_FSBL_C906L_START_ADDR 0x83f40000  /* offset 63.25MiB */
#define CVIMMAP_FSBL_UNZIP_ADDR 0x81400000  /* offset 20.0MiB */
#define CVIMMAP_FSBL_UNZIP_SIZE 0xf00000  /* 15.0MiB */

这里可以看到 FreeRTOS 运行在内存 0x80000000 + 64M 末尾的 768K 位置,起始地址:0x83f40000,大小为 0xc0000

  1. cvi_board_memmap.h生成

cvi_board_memmap.h 该文件不是默认写死的,可通过修改配置在编译过程中通过脚本自动生成的。通过 cvi_board_memmap.h 反推生成过程,在 build/scripts/mmap.mk 文件中,定义了 cvi_board_memmap.h 生成规则。

.PHONY: memory-map

CVI_BOARD_MEMMAP_H_PATH := ${BUILD_PATH}/output/${PROJECT_FULLNAME}/cvi_board_memmap.h
CVI_BOARD_MEMMAP_CONF_PATH := ${BUILD_PATH}/output/${PROJECT_FULLNAME}/cvi_board_memmap.conf
CVI_BOARD_MEMMAP_LD_PATH:= ${BUILD_PATH}/output/${PROJECT_FULLNAME}/cvi_board_memmap.ld

BOARD_MMAP_PATH := ${BORAD_FOLDER_PATH}/memmap.py
MMAP_CONV_PY := ${BUILD_PATH}/scripts/mmap_conv.py


${CVI_BOARD_MEMMAP_H_PATH}: ${BOARD_MMAP_PATH} ${MMAP_CONV_PY}
	$(call print_target)
	mkdir -p $(dir $@)
	@${MMAP_CONV_PY} --type h $< $@

${CVI_BOARD_MEMMAP_CONF_PATH}: ${BOARD_MMAP_PATH} ${MMAP_CONV_PY}
	$(call print_target)
	@mkdir -p $(dir $@)
	@${MMAP_CONV_PY} --type conf $< $@

${CVI_BOARD_MEMMAP_LD_PATH}: ${BOARD_MMAP_PATH} ${MMAP_CONV_PY}
	$(call print_target)
	@mkdir -p $(dir $@)
	@${MMAP_CONV_PY} --type ld $< $@

ifeq ($(wildcard ${BOARD_MMAP_PATH}),)
memory-map:
else
memory-map: ${CVI_BOARD_MEMMAP_H_PATH} ${CVI_BOARD_MEMMAP_CONF_PATH} ${CVI_BOARD_MEMMAP_LD_PATH}
endif

其中还使用到了 build/boards/cv180x/cv1800b_milkv_duo_sd/memmap.py 脚本。

    # ==============
    # C906L FreeRTOS
    # ==============
    FREERTOS_SIZE = 768 * SIZE_1K
    # FreeRTOS is at the end of DRAM
    FREERTOS_ADDR = DRAM_BASE + DRAM_SIZE - FREERTOS_SIZE
    FSBL_C906L_START_ADDR = FREERTOS_ADDR

终于看到了 FREERTOS_SIZEFSBL_C906L_START_ADDR 变量被定义的地方。

5、合成 flp.bin

按照前面的分析,fip.bin 在 fsbl 最后阶段合成,合成后文件位于 install/soc_cv1800b_milkv_duo_sd/fip.bin

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')

其中和 BLCP_2ND 相关的参数由2个:

  1. BLCP_2ND_RUNADDR
    #define BLCP_2ND_RUNADDR CVIMMAP_FSBL_C906L_START_ADDRfsbl/plat/cv180x/include/mmap.h 定义,就是小核 RTOS 系统的启动地址。
  2. BLCP_2ND
    BLCP_2ND_PATH=${FREERTOS_PATH}/cvitek/install/bin/cvirtos.binfip_v2.mk 文件中定义。

至此 fip.bin 已经生成。

1 Like