本文以qemu_cortex_m3为例说明zephyr从上电后到如何运行到Zephyr app的main函数
概述
在Zephyr创建应用&模拟运行中介绍了如何创建和运行一个zephyr app. 一个app的入口函数是main函数,但一个zephyr的嵌入式系统平台启动时并不能从main函数开始执行,本文将以qemu_cortex_m3平台为例说明从上电开始如何一步一步执行到main函数。
zephyr启动流程大体分为下面几步:
- 汇编阶段(arch相关)
- prep_c阶段(arch相关)
- init阶段(arch无关)
前面两部分对于不同的arch不一样,从init阶段开始就都一样了,所以根据你的平台研究前面两部分,然后所有平台都熟悉init之后的部分。
汇编阶段
ARM Cortex M3上电
arch/arm/core/cortex_m/vector_table.S中定义了向量表,CPU上电后,会从__reset向量所在位置执行1
2
3
4
5
6
7SECTION_SUBSEC_FUNC(exc_vector_table,_vector_table_section,_vector_table)
.word _main_stack + CONFIG_MAIN_STACK_SIZE
.word __reset
.word __nmi
.word __hard_fault
__reset
arch/arm/core/cortex_m/reset.S 中的__reset函数在启动过程主要完成下面3件事
- 关闭中断
- 初始中断stack
- 跳到prep C阶段执行
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15SECTION_SUBSEC_FUNC(TEXT,_reset_section,__start)
//关闭中断
movs.n r0, #_EXC_IRQ_DEFAULT_PRIO
msr BASEPRI, r0
//初始化中断堆栈
ldr r0, =_interrupt_stack
ldr r1, =CONFIG_ISR_STACK_SIZE
adds r0, r0, r1
msr PSP, r0
movs.n r0, #2 /* switch to using PSP (bit1 of CONTROL reg) */
msr CONTROL, r0
//跳到prep C阶段执行
b _PrepC
Prep C阶段
Prec C阶段主要是为后面的C执行准备环境包括:
- bss段清0
- data段拷贝
- 进入C环境执行
arch/arm/core/cortex_m/prep_c.C中_PrepC1
2
3
4
5
6
7
8
9
10
11
12
13
14void _PrepC(void)
{
relocate_vector_table(); //重新分配向量表
enable_floating_point();
_bss_zero(); //bss段清0
_data_copy(); //copy data段
#ifdef CONFIG_BOOT_TIME_MEASUREMENT
__start_time_stamp = 0;
#endif
_Cstart(); //启动Kernel,执行Main函数
CODE_UNREACHABLE;
}
#define CODE_UNREACHABLE __builtin_unreachable()
说明: __builtin_unreachable()并不执行任何操作,只是为了告诉编译器,代码不会执行到这里来, 参考地址
init阶段
kernel/init.c 的_Cstart函数主要完成thread环境准备,低级驱动初始化,切换到main thread运行1
2
3
4
5
6
7
8
9
10
11FUNC_NORETURN void _Cstart(void)
{
prepare_multithreading(dummy_thread);//中断初始,kernel初始化,准备main thread, 待启动
_sys_device_do_config_level(_SYS_INIT_LEVEL_PRE_KERNEL_1); //初始化PRE_KERNEL_1驱动
_sys_device_do_config_level(_SYS_INIT_LEVEL_PRE_KERNEL_2); //初始化PRE_KERNEL_2驱动
switch_to_main_thread(); //切换到main thread执行
CODE_UNREACHABLE;
}
thread环境准备
主要完成初始化kernel数据结构,准备好main thread1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25static void prepare_multithreading(struct k_thread *dummy_thread)
{
....
_IntLibInit(); //中断初始化
for (int ii = 0; ii < K_NUM_PRIORITIES; ii++) {
sys_dlist_init(&_ready_q.q[ii]);
}
_ready_q.cache = _main_thread;
//创建和标记main thread
_setup_new_thread(_main_thread, _main_stack,
MAIN_STACK_SIZE, _main, NULL, NULL, NULL,
CONFIG_MAIN_THREAD_PRIORITY, K_ESSENTIAL);
_mark_thread_as_started(_main_thread);
_add_thread_to_ready_q(_main_thread);
//初始化idle thread
init_idle_thread(_idle_thread, _idle_stack);
//system clock初始化
initialize_timeouts();
//进行和arch有关的kernel设置
kernel_arch_init();
}
低级驱动初始化
Zephyr的驱动等级一共分四种PRE_KERNEL_1&PRE_KERNEL_2&POST_KERNEL&APPLICATION,本文把PRE_KERNEL_1&PRE_KERNEL_2叫做低级驱动,详细的Zephyr驱动模型以后另写文章,这里不做介绍。
完成kernel初始化后,__Cstart进行PRE_KERNEL_1&PRE_KERNEL_2驱动初始化,软由switch_to_main_thread切到main thread内运行,在main thread中继续初始化POST_KERNEL和APPLICATION等级的驱动1
2_sys_device_do_config_level(_SYS_INIT_LEVEL_PRE_KERNEL_1);
_sys_device_do_config_level(_SYS_INIT_LEVEL_PRE_KERNEL_2);
切换到main thread运行
main thread的thread函数为bg_thread_main完成下面几件事:
- 初始化POST_KERNEL驱动
- 初始化APPLICATION驱动
- 初始化静态thread
- 跳到main函数执行(这里的main就是app的main函数)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33static void switch_to_main_thread(void)
{
_arch_switch_to_main_thread(_main_thread, _main_stack, MAIN_STACK_SIZE,
bg_thread_main);
}
static void bg_thread_main(void *unused1, void *unused2, void *unused3)
{
ARG_UNUSED(unused1);
ARG_UNUSED(unused2);
ARG_UNUSED(unused3);
_sys_device_do_config_level(_SYS_INIT_LEVEL_POST_KERNEL); //初始化POST_KERNEL驱动
if (boot_delay > 0) {
printk("***** delaying boot " STRINGIFY(CONFIG_BOOT_DELAY)
"ms (per build configuration) *****\n");
k_busy_wait(CONFIG_BOOT_DELAY * USEC_PER_MSEC);
}
PRINT_BOOT_BANNER(); //显示boot字符串
/* Final init level before app starts */
_sys_device_do_config_level(_SYS_INIT_LEVEL_APPLICATION); //初始化APPLICATION驱动
_init_static_threads(); //如果定义到有静态thread(放在._static_thread_data.static.内),在这里初始化
extern void main(void);
main(); //跳到main执行,这里的main就是app内定义的main,之后就是执行APP的代码
/* Terminate thread normally since it has no more work to do */
_main_thread->base.user_options &= ~K_ESSENTIAL;
}