Zephyr上电如何运行到main

7 篇文章 0 订阅
1 篇文章 1 订阅

Zephyr上电如何运行到main

背景:
由于工作需要,这几天用到STM32F0XX,客户指定使用zephyr,由于之前未接触过这个系统,故经过多次折腾(主要是github断断续续),终于成功搭建了环境。经过对源码的一翻恶补,对一些关键信息记录如下。
zephyr版本:2.3.0

正题:

1、上电复位入口
vector_table.S
主要完成工作:芯片上电启动,完成向量复位。上电后调用第一个函数z_arm_reset。
.word z_arm_reset
调用z_arm_reset函数。此函数位于reset.S文件中,又命令为__start。主要完成前期初始化工作,然后调用z_arm_prep_c函数。
2、C环境准备
z_arm_prep_c函数是个C语言实现的函数,位于prep_c.c中。

void z_arm_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_ARMV7_R) && defined(CONFIG_INIT_STACKS)
	z_arm_init_stacks();
#endif
	z_arm_interrupt_init();
	z_cstart();
	CODE_UNREACHABLE;
}
主要完成 bss 段初始化,数据拷贝,栈初始化及中断栈初始化工作。之后调用 z_cstart()函数。
FUNC_NORETURN void z_cstart(void)
{
	/* gcov hook needed to get the coverage report.*/
	gcov_static_init();

	LOG_CORE_INIT();

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

#if defined(CONFIG_MULTITHREADING)
	/* Note: The z_ready_thread() call in prepare_multithreading() requires
	 * a dummy thread even if CONFIG_ARCH_HAS_CUSTOM_SWAP_TO_MAIN=y
	 */
	struct k_thread dummy_thread;

	z_dummy_thread_init(&dummy_thread);
#endif

	/* perform basic hardware initialization */
	z_sys_init_run_level(_SYS_INIT_LEVEL_PRE_KERNEL_1);
	z_sys_init_run_level(_SYS_INIT_LEVEL_PRE_KERNEL_2);

#ifdef CONFIG_STACK_CANARIES
	uintptr_t stack_guard;

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

#ifdef CONFIG_MULTITHREADING
	prepare_multithreading();
	switch_to_main_thread();
#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

	/*
	 * 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 */
}

z_cstart()主要完成 内核初始化工作。

z_sys_init_run_level(_SYS_INIT_LEVEL_PRE_KERNEL_1);
z_sys_init_run_level(_SYS_INIT_LEVEL_PRE_KERNEL_2);

这两个函数完成了低级设备的驱动初始化工作。
zephyr中设备驱动共分四级:

#define _SYS_INIT_LEVEL_PRE_KERNEL_1	0
#define _SYS_INIT_LEVEL_PRE_KERNEL_2	1
#define _SYS_INIT_LEVEL_POST_KERNEL	2
#define _SYS_INIT_LEVEL_APPLICATION	3

设备模型:

struct device {
	const char *name;
	const void *config_info;
	const void *driver_api;
	void * const driver_data;
#ifdef CONFIG_DEVICE_POWER_MANAGEMENT
	int (*device_pm_control)(struct device *device, u32_t command,
				 void *context, device_pm_cb cb, void *arg);
	struct device_pm * const pm;
#endif
};

注意:zephyr 中 大多使用 DEVICE_AND_API_INIT 宏来处理设备驱动。如下示例:

#define STM32_I2C_INIT(name)						\
STM32_I2C_IRQ_HANDLER_DECL(name);					\
									\
static const struct i2c_stm32_config i2c_stm32_cfg_##name = {		\
	.i2c = (I2C_TypeDef *)DT_REG_ADDR(DT_NODELABEL(name)),		\
	.pclken = {							\
		.enr = DT_CLOCKS_CELL(DT_NODELABEL(name), bits),	\
		.bus = DT_CLOCKS_CELL(DT_NODELABEL(name), bus),		\
	},								\
	STM32_I2C_IRQ_HANDLER_FUNCTION(name)				\
	.bitrate = DT_PROP(DT_NODELABEL(name), clock_frequency),	\
};									\
									\
static struct i2c_stm32_data i2c_stm32_dev_data_##name;			\
									\
DEVICE_AND_API_INIT(i2c_stm32_##name, DT_LABEL(DT_NODELABEL(name)),	\
		    &i2c_stm32_init, &i2c_stm32_dev_data_##name,	\
		    &i2c_stm32_cfg_##name,				\
		    POST_KERNEL, CONFIG_KERNEL_INIT_PRIORITY_DEVICE,	\
		    &api_funcs);					\
									\
STM32_I2C_IRQ_HANDLER(name)

/* I2C instances declaration */

#if DT_NODE_HAS_STATUS(DT_NODELABEL(i2c1), okay)
STM32_I2C_INIT(i2c1);
#endif

使用STM32_I2C_INIT(i2c1) 结合 DEVICE_AND_API_INIT 宏,实现I2C1外设的设备驱动关联。

设备驱动启动后,若配置为多线程,则调用

	prepare_multithreading();
	switch_to_main_thread();

若未配置多线程,则(默认)调用

bg_thread_main(NULL, NULL, NULL);

3、线程准备

static void bg_thread_main(void *unused1, void *unused2, void *unused3)
{
	...
	z_sys_post_kernel = true;

	z_sys_init_run_level(_SYS_INIT_LEVEL_POST_KERNEL);
	...
	/* Final init level before app starts */
	z_sys_init_run_level(_SYS_INIT_LEVEL_APPLICATION);

	z_init_static_threads();

#ifdef CONFIG_SMP
	z_smp_init();
	z_sys_init_run_level(_SYS_INIT_LEVEL_SMP);
#endif

#ifdef CONFIG_BOOT_TIME_MEASUREMENT
	z_timestamp_main = k_cycle_get_32();
#endif

	extern void main(void);

	main();

	/* Mark nonessenrial since main() has no more work to do */
	z_main_thread.base.user_options &= ~K_ESSENTIAL;
	...
} /* LCOV_EXCL_LINE ... because we just dumped final coverage data */

bg_thread_main()函数主要先进行 _SYS_INIT_LEVEL_POST_KERNEL 及 _SYS_INIT_LEVEL_APPLICATION 级别的设备初始化工作。
z_init_static_threads() 对线程进行初始化,之后调用应用程序的入口函数,也就是我们用C编写的 main() 函数。

好了,到此为止,系统已交到我们手上,可以愉快地玩耍了。

本次仅说明了从上电到main的过程,其它关于内核的调试机制,队列的处理等等后续有时间再补充。

  • 1
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值