Milk-v Duo Compilation Process Part II- Compiling Small Core FreeRTOS

After a long wait, the milk-v duo now supports booting and running FreeRTOS on the small core (C906-NOMMU) developed by Pingtouge. This article mainly introduces the process of compiling the original milk-v duo SDK to generate cvirtos.bin and ultimately combine it into fip.bin.

1. Compilation and Execution

The current SDK by default starts the small core but does not enable the FreeRTOS kernel scheduler. You need to manually add FreeRTOS startup-related code to the SDK.

  1. Start the FreeRTOS Scheduler

In freertos/cvitek/task/comm/src/riscv64/comm_main.c, create an initial task and start the scheduler.

#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. 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 (;;)
        ;
}

The small core and big core share UART0 as the log output.

  1. Modify FreeRTOS Configuration

The SDK’s default configuration file, freertos/cvitek/kernel/include/riscv64/FreeRTOSConfig.h, enables configUSE_TICK_HOOK but does not implement the hook callback. Modify the configuration to disable this macro.

#define configUSE_TICK_HOOK 0

After compilation, burn it to the SD card, and you will see in the boot log that FreeRTOS has started successfully.

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!

After booting into Linux, you will see “Hello RiSC-V!” printed every 1 second.

Note: If you are using the fsbl file compiled by the original SDK and a USB-TTL chip from the CH34x series, the printed information during the boot phase may appear as garbled text. It will only display correctly after entering the U-Boot phase.

2. Compilation Entry

The RTOS-related configuration can be found in the build/boards/cv180x/cv1800b_milkv_duo_sd/cv1800b_milkv_duo_sd_defconfig file:

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

Among these, CONFIG_ENABLE_FREERTOS is the RTOS compilation macro switch, and it is used to determine whether to compile RTOS-related code in the build/scripts/fip_v2.mk file:

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

Based on the information from the Yan Shisan: milk-v duo Original Linux SDK Compilation Process that you provided, after compiling U-Boot and OpenSBI, the build process proceeds to the fsbl-build phase. This is where RTOS is compiled.

RTOS is defined in the build/scripts/rtos.mk file and essentially calls the build_cv180x.sh script in the freertos/cvitek directory:

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

The build_cv180x.sh script is relatively simple. It uses CMake to compile various targets such as arch, kernel, common, hal, driver, task, and more. It then generates the cvirtos.bin file, which is the executable file for FreeRTOS on the small core.

Please note that the address of the cvirtos.bin file is defined as $TOP_DIR/install/bin/cvirtos.bin, and this address is assigned to the BLCP_2ND_PATH variable in the rtos.mk file. This variable is required for the subsequent synthesis of fip.bin.

4. Small Core Execution Address

Next, let’s analyze the small core’s execution address step by step.

  1. Linker

File

Starting from the linker file, we can find the actual execution address of the small core. The linker file is freertos/cvitek/scripts/cv180x_lscript.ld:

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

Here, the execution address is CVIMMAP_FREERTOS_ADDR, and its size is CVIMMAP_FREERTOS_SIZE. So where are these macros defined?

  1. cvi_board_memmap.h

These values are defined in 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 */

Here, you can see that FreeRTOS runs at the end of DRAM at the address 0x80000000 + 64M with a size of 768K. Its starting address is 0x83f40000.

  1. Generation of cvi_board_memmap.h

The cvi_board_memmap.h file is not hard-coded but generated during compilation. The generation process can be found in the build/scripts/mmap.mk file, which defines the rules for generating 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

This process involves using the memmap.py script located at build/boards/cv180x/cv1800b_milkv_duo_sd/memmap.py. In this script, the FREERTOS_SIZE and FSBL_C906L_START_ADDR variables are defined.

5. Combining fip.bin

Based on the previous analysis, fip.bin is synthesized during the final stage of the fsbl. The resulting file is located at 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')

The parameters related to BLCP_2ND are crucial and are defined as follows:

  1. BLCP_2ND_RUNADDR
    Defined in fsbl/plat/cv180x/include/mmap.h as #define BLCP_2ND_RUNADDR CVIMMAP_FSBL_C906L_START_ADDR, which is the starting address of the FreeRTOS system on the small core.
  2. BLCP_2ND
    Defined as BLCP_2ND_PATH=${FREERTOS_PATH}/cvitek/install/bin/cvirtos.bin in the fip_v2.mk file.

With this information, the fip.bin file is generated.

1 Like

Is there any way to implement shared memory region between FreeRtos and Linux?
Is 8kB of SRAM of RTCSYS_SRAM a viable option?
Is access speed of this area the same as DRAM?