目录

1、前言

2、概述

3、内存映射

4、中断向量

5、复位中断

6、C函数入口

7、初始化&主程序

8、线程管理


1、前言

WLMIC -- Wireless microphone 无线麦。

firmware.xml -- 磁盘分区表信息定义。

bootloader.ini -- bootloader程序的配置文件。如何在boot中生效?

ats3231L_dvb_ext_nor.dts -- 板型对应的设备树。

ats3231L_dvb_ext_nor-pinctrl.dtsi -- 板型对应的 GPIO 复用配置。

app.conf -- 编译方案所需要的编译宏配置。
 

2、概述

BROM -- IC 中的 ROM,其中固化了 IC 上电后要运行的一段程序,这段程序包含如下功能:        

Power on -- 首先会把CPU 的 SP 设置为 0x01007ff0(SRAM 地址),然后对硬件进行初始化操作, 选择 HOSC时钟作为 CPU 频率运行,完成后跳转到 Bootloader Launcher 运行。

Bootloader launcher -- 从 norflash 上搜索 mbrec 时, 会到若干个不同位置去读取有效的mbrec 到 sram地址(0x01004000)校验通过后就会将控制权交给 mbrec, mbrec 在 sram 中运行。

Storage driver -- 读存储介质驱动程序,支持 spinor 启动;

ADFU launcher -- Adfu 的全称 actions device firmware upgrade(通过 usb 或 uart 烧录固件)

mbrec -- 不 开 源以 二 进 制 方 式 提 供 。 编 译 后 放 在 sdk 的 板 型 下 :mbrec.bin; 配置文件 bootloader.ini。主要功能:硬件进一步的初始化,包括 pmu 初始化, cpu 跑 48MHZ, spi nor 跑 32MHz, delaychain配置为 8ns, vdd 设置到 1.15v。 初始化打印串口,校验 bootloader,校验通过后跳转到 bootloader 运行。

bootloader -- nor 上 xip 运行。recovery 功能检查 fw0_temp 分区是否存在有效的 ota 升级镜像,如果存在,则执行镜像 code 到 fw0_sys 分区的拷贝(中间存在数据的解压,甚至再加密过程)recovery 运行虚地址固定在 0x11000000,其在 nor 上的位置也是固定的(0x6000)分区表 recovery 的地址是固定 0x6000。recovery 无论是否做 ota 镜像拷贝,最后阶段都跳转开始执行 zephyr(app)系统,具体跳转位置在 zephyr\arch\arm\core\aarch32\cortex_m\reset.S 中的 z_arm_reset。

3、内存映射

SRAM空间使用定义:

CONFIG_SRAM_SIZE=251

CONFIG_SRAM_BASE_ADDRESS=0x21001400

代码都在是放在SPI0 NOR,空间使用定义:

CONFIG_FLASH_BASE_ADDRESS=0x10000000

CONFIG_FLASH_SIZE=4096

4、中断向量

cortex_m\vector_table.S文件定义向量。

SECTION_SUBSEC_FUNC(exc_vector_table,_vector_table_section,_vector_table)

    /*
     * setting the _very_ early boot on the main stack allows to use memset
     * on the interrupt stack when CONFIG_INIT_STACKS is enabled before
     * switching to the interrupt stack for the rest of the early boot
     */
    .word z_main_stack + CONFIG_MAIN_STACK_SIZE

    .word z_arm_reset
    .word z_arm_nmi

    .word z_arm_hard_fault
#if defined(CONFIG_ARMV6_M_ARMV8_M_BASELINE)
    .word 0
    .word 0
    .word 0
    .word 0
    .word 0
    .word 0
    .word 0
    .word z_arm_svc
    .word 0
#elif defined(CONFIG_ARMV7_M_ARMV8_M_MAINLINE)
    .word z_arm_mpu_fault
    .word z_arm_bus_fault
    .word z_arm_usage_fault
#if defined(CONFIG_ARMV8_M_SE)
#if defined(CONFIG_ARM_SECURE_FIRMWARE)
    .word z_arm_secure_fault
#else
    .word z_arm_exc_spurious
#endif /* CONFIG_ARM_SECURE_FIRMWARE */
#else
    .word 0
#endif /* CONFIG_ARMV8_M_SE */
    .word 0
    .word 0
    .word 0
    .word z_arm_svc
    .word z_arm_debug_monitor
#else
#error Unknown ARM architecture
#endif /* CONFIG_ARMV6_M_ARMV8_M_BASELINE */
    .word 0
#if defined(CONFIG_MULTITHREADING)
    .word z_arm_pendsv
#else
    .word z_arm_exc_spurious
#endif
#if defined(CONFIG_CPU_CORTEX_M_HAS_SYSTICK)
#if defined(CONFIG_SYS_CLOCK_EXISTS) && \
    defined(CONFIG_CORTEX_M_SYSTICK_INSTALL_ISR)
    .word sys_clock_isr
#else
    .word z_arm_exc_spurious
#endif /* CONFIG_SYS_CLOCK_EXISTS && CONFIG_CORTEX_M_SYSTICK_INSTALL_ISR */
#else
    .word 0
#endif /* CONFIG_CPU_CORTEX_M_HAS_SYSTICK */

5、复位中断

cortex_m\reset.S

SECTION_SUBSEC_FUNC(TEXT,_reset_section,z_arm_reset)

/*
 * The entry point is located at the z_arm_reset symbol, which
 * is fetched by a XIP image playing the role of a bootloader, which jumps to
 * it, not through the reset vector mechanism. Such bootloaders might want to
 * search for a __start symbol instead, so create that alias here.
 */
SECTION_SUBSEC_FUNC(TEXT,_reset_section,__start)

#if defined(CONFIG_DEBUG_THREAD_INFO)
    /* Clear z_sys_post_kernel flag for RTOS aware debuggers */
    movs.n r0, #0
    ldr r1, =z_sys_post_kernel
    strb r0, [r1]
#endif /* CONFIG_DEBUG_THREAD_INFO */

#if defined(CONFIG_INIT_ARCH_HW_AT_BOOT)
    /* Reset CONTROL register */
    movs.n r0, #0
    msr CONTROL, r0
    isb
#if defined(CONFIG_CPU_CORTEX_M_HAS_SPLIM)
    /* Clear SPLIM registers */
    movs.n r0, #0
    msr MSPLIM, r0
    msr PSPLIM, r0
#endif /* CONFIG_CPU_CORTEX_M_HAS_SPLIM */

#endif /* CONFIG_INIT_ARCH_HW_AT_BOOT */

#if defined(CONFIG_PM_S2RAM)
    bl arch_pm_s2ram_resume
#endif /* CONFIG_PM_S2RAM */

#if defined(CONFIG_PLATFORM_SPECIFIC_INIT)
    bl z_arm_platform_init
#endif

#if defined(CONFIG_INIT_ARCH_HW_AT_BOOT)
#if defined(CONFIG_CPU_HAS_ARM_MPU)
    /* Disable MPU */
    movs.n r0, #0
    ldr r1, =_SCS_MPU_CTRL
    str r0, [r1]
    dsb
#endif /* CONFIG_CPU_HAS_ARM_MPU */
    ldr r0, =z_main_stack + CONFIG_MAIN_STACK_SIZE
    msr msp, r0

    /* Initialize core architecture registers and system blocks */
    bl z_arm_init_arch_hw_at_boot
#endif /* CONFIG_INIT_ARCH_HW_AT_BOOT */

    /* lock interrupts: will get unlocked when switch to main task */
#if defined(CONFIG_ARMV6_M_ARMV8_M_BASELINE)
    cpsid i
#elif defined(CONFIG_ARMV7_M_ARMV8_M_MAINLINE)
    movs.n r0, #_EXC_IRQ_DEFAULT_PRIO
    msr BASEPRI, r0
#else
#error Unknown ARM architecture
#endif

#ifdef CONFIG_WDOG_INIT
    /* board-specific watchdog initialization is necessary */
    bl z_arm_watchdog_init
#endif

/*
 *
 * Note: in all Cortex-M variants the interrupt stack area is at
 * least equal to CONFIG_ISR_STACK_SIZE + MPU_GUARD_ALIGN_AND_SIZE
 * (may be larger due to rounding up for stack pointer aligning
 * purposes but this is sufficient during initialization).
 */

#ifdef CONFIG_INIT_STACKS
    ldr r0, =z_interrupt_stacks
    ldr r1, =0xaa
    ldr r2, =CONFIG_ISR_STACK_SIZE + MPU_GUARD_ALIGN_AND_SIZE
    bl z_early_memset
#endif

    /*
     * Set PSP and use it to boot without using MSP, so that it
     * gets set to z_interrupt_stacks during initialization.
     */
    ldr r0, =z_interrupt_stacks
    ldr r1, =CONFIG_ISR_STACK_SIZE + MPU_GUARD_ALIGN_AND_SIZE
    adds r0, r0, r1
    msr PSP, r0
    mrs r0, CONTROL
    movs r1, #2
    orrs r0, r1 /* CONTROL_SPSEL_Msk */
    msr CONTROL, r0
    /*
     * When changing the stack pointer, software must use an ISB instruction
     * immediately after the MSR instruction. This ensures that instructions
     * after the ISB instruction execute using the new stack pointer.
     */
    isb

    /*
     * 'bl' jumps the furthest of the branch instructions that are
     * supported on all platforms. So it is used when jumping to z_prep_c
     * (even though we do not intend to return).
     */
    bl z_prep_c

6、C函数入口

cortex_m\prep_c.c -> z_prep_c()//C环境初始化

void z_prep_c(void)
{
	relocate_vector_table();
#if defined(CONFIG_CPU_HAS_FPU)
	z_arm_floating_point_init();
#endif
	z_bss_zero();
	z_data_copy();
#if defined(CONFIG_ARM_CUSTOM_INTERRUPT_CONTROLLER)
	/* Invoke SoC-specific interrupt controller initialization */
	z_soc_irq_init();
#else
	z_arm_interrupt_init();
#endif /* CONFIG_ARM_CUSTOM_INTERRUPT_CONTROLLER */
	z_cstart();
	CODE_UNREACHABLE;
}

zephyr\kernel\init.c->z_cstart()//初始化zephyr内核

__boot_func
FUNC_NO_STACK_PROTECTOR
FUNC_NORETURN void z_cstart(void)
{
	/* gcov hook needed to get the coverage report.*/
	gcov_static_init();

	/* initialize early init calls */
	z_sys_init_run_level(INIT_LEVEL_EARLY);

	/* perform any architecture-specific initialization */
	arch_kernel_init();

	LOG_CORE_INIT();

#if defined(CONFIG_MULTITHREADING)
	z_dummy_thread_init(&_thread_dummy);
#endif /* CONFIG_MULTITHREADING */
	/* do any necessary initialization of static devices */
	z_device_state_init();

	/* perform basic hardware initialization */
	z_sys_init_run_level(INIT_LEVEL_PRE_KERNEL_1);
#if defined(CONFIG_SMP)
	arch_smp_init();
#endif
	z_sys_init_run_level(INIT_LEVEL_PRE_KERNEL_2);

#ifdef CONFIG_STACK_CANARIES
	uintptr_t stack_guard;

	z_early_rand_get((uint8_t *)&stack_guard, sizeof(stack_guard));
	__stack_chk_guard = stack_guard;
	__stack_chk_guard <<= 8;
#endif	/* CONFIG_STACK_CANARIES */

#ifdef CONFIG_TIMING_FUNCTIONS_NEED_AT_BOOT
	timing_init();
	timing_start();
#endif /* CONFIG_TIMING_FUNCTIONS_NEED_AT_BOOT */

#ifdef CONFIG_MULTITHREADING
	switch_to_main_thread(prepare_multithreading());
#else
#ifdef ARCH_SWITCH_TO_MAIN_NO_MULTITHREADING
	/* Custom ARCH-specific routine to switch to main()
	 * in the case of no multi-threading.
	 */
	ARCH_SWITCH_TO_MAIN_NO_MULTITHREADING(bg_thread_main,
		NULL, NULL, NULL);
#else
	bg_thread_main(NULL, NULL, NULL);

	/* LCOV_EXCL_START
	 * We've already dumped coverage data at this point.
	 */
	irq_lock();
	while (true) {
	}
	/* LCOV_EXCL_STOP */
#endif /* ARCH_SWITCH_TO_MAIN_NO_MULTITHREADING */
#endif /* CONFIG_MULTITHREADING */

	/*
	 * Compiler can't tell that the above routines won't return and issues
	 * a warning unless we explicitly tell it that control never gets this
	 * far.
	 */

	CODE_UNREACHABLE; /* LCOV_EXCL_LINE */
}

7、初始化&主程序

enum init_level {

    INIT_LEVEL_EARLY = 0,

    INIT_LEVEL_PRE_KERNEL_1,

    INIT_LEVEL_PRE_KERNEL_2,

    INIT_LEVEL_POST_KERNEL,

    INIT_LEVEL_APPLICATION,

#ifdef CONFIG_SMP

    INIT_LEVEL_SMP,

#endif /* CONFIG_SMP */

};

#define SYS_INIT(init_fn, level, prio)     SYS_INIT_NAMED(init_fn, init_fn, level, prio)

//宏定义方式线程注册
#define Z_THREAD_COMMON_DEFINE(name, stack_size,			\
			       entry, p1, p2, p3,			\
			       prio, options, delay)			\
	struct k_thread _k_thread_obj_##name;				\
	STRUCT_SECTION_ITERABLE(_static_thread_data,			\
				_k_thread_data_##name) =		\
		Z_THREAD_INITIALIZER(&_k_thread_obj_##name,		\
				     _k_thread_stack_##name, stack_size,\
				     entry, p1, p2, p3, prio, options,	\
				     delay, name);			\
	const k_tid_t name = (k_tid_t)&_k_thread_obj_##name

//线程创建
char *z_setup_new_thread(struct k_thread *new_thread,
			 k_thread_stack_t *stack, size_t stack_size,
			 k_thread_entry_t entry,
			 void *p1, void *p2, void *p3,
			 int prio, uint32_t options, const char *name)

//启动调度:
void z_sched_start(struct k_thread *thread)
{
	k_spinlock_key_t key = k_spin_lock(&_sched_spinlock);

	if (z_has_thread_started(thread)) {
		k_spin_unlock(&_sched_spinlock, key);
		return;
	}

	z_mark_thread_as_started(thread);
	ready_thread(thread);
	z_reschedule(&_sched_spinlock, key);
}

主线程创建

//step1
switch_to_main_thread(prepare_multithreading());
//step1.1
static char *prepare_multithreading(void)
{
	char *stack_ptr;
	/* _kernel.ready_q is all zeroes */
	z_sched_init();

#ifndef CONFIG_SMP
	_kernel.ready_q.cache = &z_main_thread;
#endif /* CONFIG_SMP */
    //主线程创建
	stack_ptr = z_setup_new_thread(&z_main_thread, 
                       z_main_stack,
				       K_THREAD_STACK_SIZEOF(z_main_stack),
				       bg_thread_main,//主线程入口。里面调用main()函数。
				       NULL, NULL, NULL,
				       CONFIG_MAIN_THREAD_PRIORITY,
				       K_ESSENTIAL, "main");
	z_mark_thread_as_started(&z_main_thread);
	z_ready_thread(&z_main_thread);

	z_init_cpu(0);    //空闲线程创建
	return stack_ptr;
}
//step1.2
static FUNC_NORETURN void switch_to_main_thread(char *stack_ptr)
{
#ifdef CONFIG_ARCH_HAS_CUSTOM_SWAP_TO_MAIN
	arch_switch_to_main_thread(&z_main_thread, stack_ptr, bg_thread_main);
#else
	ARG_UNUSED(stack_ptr);
	z_swap_unlocked();
#endif /* CONFIG_ARCH_HAS_CUSTOM_SWAP_TO_MAIN */
	CODE_UNREACHABLE; /* LCOV_EXCL_LINE */
}
//step2
static void bg_thread_main(void *unused1, void *unused2, void *unused3)
{
	z_init_static_threads();//静态线程创建(通过宏定义方式注册的线程)
    ……
#ifdef CONFIG_SMP
	if (!IS_ENABLED(CONFIG_SMP_BOOT_DELAY)) {
		z_smp_init();
	}
	z_sys_init_run_level(INIT_LEVEL_SMP);
#endif /* CONFIG_SMP */
#ifdef CONFIG_MMU
	z_mem_manage_boot_finish();
#endif /* CONFIG_MMU */

	extern int main(void);
	(void)main();//进入用户主程序
    ……
} 

//主APP注册 —— 在主线程中运行(active)
extern char z_main_stack[CONFIG_MAIN_STACK_SIZE];
APP_DEFINE(main, z_main_stack, CONFIG_MAIN_STACK_SIZE,APP_PRIORITY, NULL, NULL, NULL, NULL);

//BT播放器APP注册 —— 在主线程中运行(active)
char __aligned(ARCH_STACK_PTR_ALIGN) bt_player_stack_area[CONFIG_APP_STACKSIZE];
APP_DEFINE(bt_player, bt_player_stack_area, sizeof(bt_player_stack_area),
	CONFIG_APP_PRIORITY, NULL, NULL, NULL, _btplayer_main_loop);

//BT播放器模块注册 —— 基于BT播放器APP
#define BTPLAYER_MODE_DEFINE(mode_id, prio, enter_func, exit_func, msg_proc_func, get_status_func) 

8、线程管理

SHELL_CMD_REGISTER(kernel, &sub_kernel, "Kernel commands", NULL);//shell命令注册

SHELL_STATIC_SUBCMD_SET_CREATE(sub_kernel,……);//shell 字命令创建。

查看线程命令:kernel_service.c ->  kernel threads

【id】【thread entry】【name】 【create_form】【file】【note】

【1】【data_channel_recv_cmd_process】【NA】【os_thread_create】【main.c-> system_init()->data_channel_manager_init()】【】

【2】【data_channel_send_cmd_process】【NA】【os_thread_create】【main.c-> system_init()->data_channel_manager_init()】【】

【3】【media_service_main_loop】【NA】【SERVICE_DEFINE + os_thread_create】【main.c-> system_init()->media_service_init()】【此类服务都会创建线程;服务定义中有定义线程入口,__service_entry_table】

【4】【input_thread】【input】【K_THREAD_DEFINE+z_init_static_threads + z_setup_new_thread】【init.c】【】

【5】【work_queue_main】【sysworkq】【SYS_INIT(k_sys_work_q_init)+k_thread_create】【system_work_q.c】【SYS_INIT定义不一定会创建线程是由init_fn函数内容决定】

【6】【work_queue_main】【dspworkq】【SYS_INIT(dsp_work_q_init)+k_thread_create】【dsp_mailbox.c】【与dsp通信工作线程】

【7】【work_queue_main】【eccwork】【SYS_INIT(ecc_work_q_init)+k_thread_create】【ecc_acts.c】【】

【8】【work_queue_main】【syslowprioworkq】【SYS_INIT(sys_low_prio_work_q_init) + k_thread_create】【user_work_q.c】【】

【9】【shell_thread】【shell_uart】【SYS_INIT+k_thread_create】【shell_uart.c】【串口shell任务】

【10】【idle】【idle】【z_setup_new_thread】【init.c】【空闲任务】

【11】【bg_thread_main】【main】【z_setup_new_thread】【init.c】【主任务】

Logo

智能硬件社区聚焦AI智能硬件技术生态,汇聚嵌入式AI、物联网硬件开发者,打造交流分享平台,同步全国赛事资讯、开展 OPC 核心人才招募,助力技术落地与开发者成长。

更多推荐