Zephyr启动过程与中断响应
// 本文部分内容来自网络
1. 启动过程
一般嵌入式处理器启动方式分为两种:
1. XIP 模式 (eXecute In Place), 在该模式下CPU直接从Nor Flash上读代码执行,执行速度慢 ;
2. 非XIP模式, 在该模式下硬件先将代码从Flash上搬移到RAM上后,CPU才能从RAM上访问数据,执行速度快;
对于非XIP模式,启动前需要先借助boot把代码段从FLASH拷贝到RAM;
对于XIP模式,如果FLASH基地址映射到0地址,复位后可以直接启动;
如果FLASH基地址非0地址,启动之前需要先借助boot在映射的0地址(笔者项目的使用SOC通过bootlink将0地址映射到了0x20110000 :IRAM_AON)中写入MSP的初始值+Reset入口地址(针对CortexM处理器)。
下文以CortexM + Zephyr1.9.2 +XIP模式 为例进行相关阐述。
__reset
根据向量表找到的reset入口,位于(zephyr\arch\arm\core\cortex_m\reset.S)
reset入口完成工作:
- 初始化watchdog: _WdogInit
- 将中断栈_interrupt_stack内容初始化为0xaa
- 复位后CortexM处理器默认处于线程模式+特权访问+使用MSP主栈指针,这里切换为PSP指针并将PSP设为_interrupt_stack中断栈
- 进入C语言准备_PrepC
SECTION_SUBSEC_FUNC(TEXT,_reset_section,__reset)/** The entry point is located at the __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)/* lock interrupts: will get unlocked when switch to main task */cpsid i#ifdef CONFIG_WDOG_INIT/* board-specific watchdog initialization is necessary */bl _WdogInit
#endif#ifdef CONFIG_INIT_STACKSldr r0, =_interrupt_stackldr r1, =0xaaldr r2, =CONFIG_ISR_STACK_SIZEbl memset
#endif/** Set PSP and use it to boot without using MSP, so that it* gets set to _interrupt_stack during nanoInit().*/ldr r0, =_interrupt_stackldr r1, =CONFIG_ISR_STACK_SIZEadds r0, r0, r1msr PSP, r0movs.n r0, #2 /* switch to using PSP (bit1 of CONTROL reg) */msr CONTROL, r0b _PrepC
_PrepC
完成进入C语言环境后的准备工作,位于(zephyr\arch\arm\core\cortex_m\Prep_c.c)
_PrepC完成工作:
- 重定位向量表
- 使能浮点计算(如果有相关特性)
- 清零BSS段
- 从ROM拷贝数据段到RAM(针对XIP模式)
- 正式进入C环境执行
void _PrepC(void)
{relocate_vector_table();enable_floating_point();_bss_zero();_data_copy();
#ifdef CONFIG_BOOT_TIME_MEASUREMENT__start_time_stamp = 0;
#endif_Cstart();CODE_UNREACHABLE;
}
relocate_vector_table
根据CortexM处理器的特性,向量表固定于0地址,对于(XIP模式并且FLASH基地址非0)或者(非XIP模式并且RAM基地址非0)的情况,需要重新将向量表拷贝到0地址。
#define VECTOR_ADDRESS 0static inline void relocate_vector_table(void)
{
#if defined(CONFIG_XIP) && (CONFIG_FLASH_BASE_ADDRESS != 0) || \!defined(CONFIG_XIP) && (CONFIG_SRAM_BASE_ADDRESS != 0)size_t vector_size = (size_t)_vector_end - (size_t)_vector_start;memcpy(VECTOR_ADDRESS, _vector_start, vector_size);
#endif
}
_vector_end/_vector_start两个符号的地址在linker.ld链接脚本里面有定义,linker文件位于(zephyr\include\arch\arm\cortex_m\scripts):
SECTION_PROLOGUE(_TEXT_SECTION_NAME,,){. = CONFIG_TEXT_SECTION_OFFSET;KEEP(*(.os.start))_vector_start = .;KEEP(*(.exc_vector_table))KEEP(*(".exc_vector_table.*"))KEEP(*(IRQ_VECTOR_TABLE))KEEP(*(.openocd_dbg))KEEP(*(".openocd_dbg.*"))/* Kinetis has to write 16 bytes at 0x400 */SKIP_TO_KINETIS_FLASH_CONFIGKEEP(*(.kinetis_flash_config))KEEP(*(".kinetis_flash_config.*"))#ifdef CONFIG_GEN_SW_ISR_TABLEKEEP(*(SW_ISR_TABLE))
#endif_vector_end = .;_image_text_start = .;*(.text)*(".text.*")*(.gnu.linkonce.t.*)} GROUP_LINK_IN(ROMABLE_REGION)_image_text_end = .;
_Cstart
正式进入C语言环境,开始操作系统初始化工作,位于(zephyr\kernel\init.c)
_Cstart完成工作:
- 多线程初始化
- PRE_KERNEL_1、PRE_KERNEL_2级别设备初始化
- 切换到主线程开始运行
FUNC_NORETURN void _Cstart(void)
{
#ifdef CONFIG_TIME_TRACKinsert_pack_time_debug(PACK_AP_CSTART, NULL, -1);
#endif#ifdef CONFIG_ARCH_HAS_CUSTOM_SWAP_TO_MAINstruct k_thread *dummy_thread = NULL;
#elsestruct k_thread dummy_thread_memory;struct k_thread *dummy_thread = &dummy_thread_memory;
#endif/** Initialize kernel data structures. This step includes* initializing the interrupt subsystem, which must be performed* before the hardware initialization phase.*/prepare_multithreading(dummy_thread);/* perform basic hardware initialization */_sys_device_do_config_level(_SYS_INIT_LEVEL_PRE_KERNEL_1);_sys_device_do_config_level(_SYS_INIT_LEVEL_PRE_KERNEL_2);/* initialize stack canaries */
#ifdef CONFIG_STACK_CANARIES__stack_chk_guard = (void *)sys_rand32_get();
#endif/* display boot banner */switch_to_main_thread();/** 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;
}
prepare_multithreading
该函数完成多线程初始化,主要完成
- 中断(NVIC)初始化
- 创建了两个线程,分别是_main_thread(使用_main_stack,即系统入口对应的MSP指针)和_idle_thread(使用_idle_stack)
- 初始化_timeout_q超时队列
- 架构相关的内核初始化,针对CortexM主要完成MSP主栈设置为_interrupt_stack,以及中断异常相关初始化
static void prepare_multithreading(struct k_thread *dummy_thread)
{
#ifdef CONFIG_ARCH_HAS_CUSTOM_SWAP_TO_MAINARG_UNUSED(dummy_thread);
#else/** Initialize the current execution thread to permit a level of* debugging output if an exception should happen during kernel* initialization. However, don't waste effort initializing the* fields of the dummy thread beyond those needed to identify it as a* dummy thread.*/_current = dummy_thread;dummy_thread->base.user_options = K_ESSENTIAL;dummy_thread->base.thread_state = _THREAD_DUMMY;
#endif/* _kernel.ready_q is all zeroes *//** The interrupt library needs to be initialized early since a series* of handlers are installed into the interrupt table to catch* spurious interrupts. This must be performed before other kernel* subsystems install bonafide handlers, or before hardware device* drivers are initialized.*/_IntLibInit();/* ready the init/main and idle threads */for (int ii = 0; ii < K_NUM_PRIORITIES; ii++) {sys_dlist_init(&_ready_q.q[ii]);}/** prime the cache with the main thread since:** - the cache can never be NULL* - the main thread will be the one to run first* - no other thread is initialized yet and thus their priority fields* contain garbage, which would prevent the cache loading algorithm* to work as intended*/_ready_q.cache = _main_thread;_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);#ifdef CONFIG_MULTITHREADING_new_thread(_idle_thread, _idle_stack,IDLE_STACK_SIZE, idle, NULL, NULL, NULL,K_LOWEST_THREAD_PRIO, K_ESSENTIAL);_mark_thread_as_started(_idle_thread);_add_thread_to_ready_q(_idle_thread);
#endifinitialize_timeouts();/* perform any architecture-specific initialization */kernel_arch_init();
}
_sys_device_do_config_level
设备初始化,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
_sys_device_do_config_level函数实现:
void _sys_device_do_config_level(int level)
{struct device *info;for (info = config_levels[level]; info < config_levels[level+1];info++) {struct device_config *device = info->config;device->init(info);
#ifdef CONFIG_TIME_TRACKinsert_pack_time_debug(ap_init_counter, device->init, level);ap_init_counter++;
#endif}
}
设备初始化接口定义:
SYS_INIT(netdog_work_q_init, APPLICATION, CONFIG_KERNEL_INIT_PRIORITY_DEFAULT);#define SYS_INIT(init_fn, level, prio) \DEVICE_INIT(_SYS_NAME(init_fn), "", init_fn, NULL, NULL, level, prio)#define DEVICE_INIT(dev_name, drv_name, init_fn, data, cfg_info, level, prio) \DEVICE_AND_API_INIT(dev_name, drv_name, init_fn, data, cfg_info, \level, prio, NULL)#define DEVICE_AND_API_INIT(dev_name, drv_name, init_fn, data, cfg_info, \level, prio, api) \DEVICE_DEFINE(dev_name, drv_name, init_fn, \device_pm_control_nop, data, cfg_info, level, \prio, api)#define DEVICE_DEFINE(dev_name, drv_name, init_fn, pm_control_fn, \data, cfg_info, level, prio, api) \\static struct device_config _CONCAT(__config_, dev_name) __used \__attribute__((__section__(".devconfig.init"))) = { \.name = drv_name, .init = (init_fn), \.device_pm_control = (pm_control_fn), \.config_info = (cfg_info) \}; \static struct device _CONCAT(__device_, dev_name) __used \__attribute__((__section__(".init_" #level STRINGIFY(prio)))) = { \.config = &_CONCAT(__config_, dev_name), \.driver_api = api, \.driver_data = data \}
switch_to_main_thread
切换到_main_thread开始运行:
static void switch_to_main_thread(void)
{
#ifdef CONFIG_ARCH_HAS_CUSTOM_SWAP_TO_MAIN_arch_switch_to_main_thread(_main_thread, _main_stack, MAIN_STACK_SIZE,_main);
#else/** Context switch to main task (entry function is _main()): the* current fake thread is not on a wait queue or ready queue, so it* will never be rescheduled in.*/_Swap(irq_lock());
#endif
}
_main_thread线程入口为_main,_main函数完成:
- 平台相关的初始化以及_SYS_INIT_LEVEL_POST_KERNEL、SYS_INIT_LEVEL_APPLICATION级别设备初始化,这些初始化会创建相关的平台与应用线程;
- 完成后进入main钩子函数(\os\zephyr\samples\xxx\src\main.c),本平台该函数实现为空;
- 该线程没有死循环入口,所以完成后自行退出,然后操作系统开始正式调度执行;
static void _main(void *unused1, void *unused2, void *unused3)
{ARG_UNUSED(unused1);ARG_UNUSED(unused2);ARG_UNUSED(unused3);oss_event_init();oss_icp_init();oss_nv_init();_reset_uart_termios();ramdump_init();amt_init();zcat_ap_init();_sys_device_do_config_level(_SYS_INIT_LEVEL_POST_KERNEL);/* Final init level before app starts */_sys_device_do_config_level(_SYS_INIT_LEVEL_APPLICATION);#if defined(CONFIG_BOOT_DELAY) && CONFIG_BOOT_DELAY > 0if (boot_delay > 0) {printk("delaying boot\n");k_sleep(CONFIG_BOOT_DELAY);}PRINT_BOOT_BANNER();
#endif_init_static_threads();#ifdef CONFIG_BOOT_TIME_MEASUREMENT/* record timestamp for kernel's _main() function */extern u64_t __main_time_stamp;__main_time_stamp = (u64_t)k_cycle_get_32();
#endifextern void main(void);k_thread_priority_set(_main_thread, CONFIG_MAIN_THREAD_PRIORITY);main();/* Terminate thread normally since it has no more work to do */_main_thread->base.user_options &= ~K_ESSENTIAL;
}
2. 中断响应
以CortexM0处理器为例,其异常列表如下:
异常类型 异常编号 描述Reset 1 上电复位或系统复位NMI 2 不可屏蔽中断Hard fault 3 用于错误处理,系统检测到错误后被激活SVCall 11 请求管理调用,在执行SVC指令被激活,主要用作操作系统PendSV 14 可挂起服务(系统)调用SysTick 15 系统节拍定时器异常,一般在OS种用作周期系统节拍异常IRQ0-IRQ31 16-47 中断,可来自于外部,也可来自片上外设
查看linker.ld文件可以看到,Zephyr系统将上述异常对应的向量表分成了几部分,分别为exc_vector_table,IRQ_VECTOR_TABLE和SW_ISR_TABLE;(忽略openocd_dbg、kinetis_flash_config段,未使用):
_vector_start = .;KEEP(*(.exc_vector_table))KEEP(*(".exc_vector_table.*"))KEEP(*(IRQ_VECTOR_TABLE))KEEP(*(.openocd_dbg))KEEP(*(".openocd_dbg.*"))/* Kinetis has to write 16 bytes at 0x400 */SKIP_TO_KINETIS_FLASH_CONFIGKEEP(*(.kinetis_flash_config))KEEP(*(".kinetis_flash_config.*"))#ifdef CONFIG_GEN_SW_ISR_TABLEKEEP(*(SW_ISR_TABLE))
#endif_vector_end = .;
第一部分,异常向量表exc_vector_table,位于(zephyr\arch\arm\core\cortex_m\vector_table.S),定义了1-15号异常对应的跳转PC地址:
SECTION_SUBSEC_FUNC(exc_vector_table,_vector_table_section,_vector_table).word _main_stack + CONFIG_MAIN_STACK_SIZE.word __reset.word __nmi.word __hard_fault.word __reserved.word __reserved.word __reserved.word __reserved.word __reserved.word __reserved.word __reserved.word __svc.word __reserved.word __reserved.word __pendsv#if defined(CONFIG_CORTEX_M_SYSTICK).word _timer_int_handler#else.word __reserved#endif
第二部分,中断向量表IRQ_VECTOR_TABLE,定义了16-47号中断跳转PC地址,位于project\prj_XXX\temp\Isr_tables.c,这是一个正式编译前由gen_isr_tables.py Python脚本生成的文件;该向量表所有项都对应同一个函数即所有中断总入口,函数名为_isr_wrapper:
#define __irq_vector_table _GENERIC_SECTION(IRQ_VECTOR_TABLE)u32_t __irq_vector_table _irq_vector_table[34] = {0xffc88cd,0xffc88cd,0xffc88cd,0xffc88cd,0xffc88cd,0xffc88cd,0xffc88cd,0xffc88cd,0xffc88cd,0xffc88cd,0xffc88cd,0xffc88cd,0xffc88cd,0xffc88cd,0xffc88cd,0xffc88cd,0xffc88cd,0xffc88cd,0xffc88cd,0xffc88cd,0xffc88cd,0xffc88cd,0xffc88cd,0xffc88cd,0xffc88cd,0xffc88cd,0xffc88cd,0xffc88cd,0xffc88cd,0xffc88cd,0xffc88cd,0xffc88cd,0xffc88cd,0xffc88cd,
};
第三部分,具体中断实现结构体SW_ISR_TABLE,同样位于project\prj_XXX\temp\Isr_tables.c,也是由Python脚本生成,该结构体由两项成员,分别为传入参数arg和中断入口isr:
#define __sw_isr_table _GENERIC_SECTION(SW_ISR_TABLE)struct _isr_table_entry { void *arg; void (*isr)(void *); };struct _isr_table_entry __sw_isr_table _sw_isr_table[34] = {{(void *)0x10724, (void *)0xffb8479},{(void *)0x10670, (void *)0xffb4b59},{(void *)0x0, (void *)0xffc8759},{(void *)0x1067c, (void *)0xffb4b59},{(void *)0x0, (void *)0xffc8759},{(void *)0x0, (void *)0xffb9661},{(void *)0x0, (void *)0xffc8759},{(void *)0x0, (void *)0xffc8759},{(void *)0x10730, (void *)0xffb8aa9},{(void *)0x10730, (void *)0xffb8a5d},{(void *)0x106c4, (void *)0xffb52c9},{(void *)0x2, (void *)0xffc6ad1},{(void *)0x0, (void *)0xffc6ad1},{(void *)0x1061c, (void *)0xffb66f5},{(void *)0x10610, (void *)0xffb66f5},{(void *)0x0, (void *)0xffc8759},{(void *)0x0, (void *)0xffc8759},{(void *)0x0, (void *)0xffc69e9},{(void *)0x0, (void *)0xffc8759},{(void *)0x10694, (void *)0xffb9b8b},{(void *)0x0, (void *)0xffc72e1},{(void *)0x106e8, (void *)0xffb6af3},{(void *)0x0, (void *)0xffc8759},{(void *)0x0, (void *)0xffc8759},{(void *)0x0, (void *)0xffc8759},{(void *)0x0, (void *)0xffc8759},{(void *)0x0, (void *)0xffc8759},{(void *)0x0, (void *)0xffc8759},{(void *)0x10730, (void *)0xffb8959},{(void *)0x0, (void *)0xffc8759},{(void *)0x106b8, (void *)0xffb52c9},{(void *)0x0, (void *)0xffc5e9d},{(void *)0x0, (void *)0xffc8759},{(void *)0x0, (void *)0xffc8759},
};
具体程序如何从_isr_wrapper总入口找到对应中断并进入对应中断入口,可以参考_isr_wrapper函数实现:
SECTION_FUNC(TEXT, _isr_wrapper)push {lr} /* lr is now the first item on the stack *//** All interrupts are disabled when handling idle wakeup. For tickless* idle, this ensures that the calculation and programming of the device* for the next timer deadline is not interrupted. For non-tickless idle,* this ensures that the clearing of the kernel idle state is not* interrupted. In each case, _sys_power_save_idle_exit is called with* interrupts disabled.*/cpsid i /* PRIMASK = 1 *//* is this a wakeup from idle ? */ldr r2, =_kernel/* requested idle duration, in ticks */ldr r0, [r2, #_kernel_offset_to_idle]cmp r0, #0beq _idle_state_clearedmovs.n r1, #0/* clear kernel idle state */str r1, [r2, #_kernel_offset_to_idle]blx _sys_power_save_idle_exit
_idle_state_cleared:cpsie i /* re-enable interrupts (PRIMASK = 0) */mrs r0, IPSR /* get exception number */ldr r1, =16subs r0, r1 /* get IRQ number */lsls r0, #3 /* table is 8-byte wide */ldr r1, =_sw_isr_tableadd r1, r1, r0 /* table entry: ISRs must have their MSB set to stay* in thumb mode */ldm r1!,{r0,r3} /* arg in r0, ISR in r3 */blx r3 /* call ISR */pop {r3}mov lr, r3/* exception return is done in _IntExit() */b _IntExit
中断定义与处理
中断定义接口如下:
IRQ_CONNECT(SI_TIM0_IRQ, CONFIG_TIMER_0_IRQ_PRI,timer_si_isr, DEVICE_GET(timer_si_0), 0);
中断号:SI_TIM0_IRQ
中断处理函数:timer_si_isr
定义完成后通过脚本解析,timer_si_isr的地址会被写入sw_isr_table;下面的中断处理路径,通过timer中断实现了系统tick的处理:
timer_si_isr timer_systick_callback _sys_clock_final_tick_announce _sys_clock_tick_announce _nano_sys_clock_tick_announce handle_timeouts _handle_expired_timeouts _handle_one_expired_timeout work_timeout
转载于:https://www.cnblogs.com/DF11G/p/9774437.html
Zephyr启动过程与中断响应相关推荐
- u-boot启动过程
目录: 一.初识u-boot 3 1,Bootloader介绍 3 2,Bootloader的启动方式 3 (1)网络启动方式 4 (2)磁盘启动方式 4 (3)Flash启动方式 4 3,U-boo ...
- U-Boot启动过程--详细版的完全分析
目录: 一.初识u-boot 3 1,Bootloader介绍 3 2,Bootloader的启动方式 3 (1)网络启动方式 4 (2)磁盘启动方式 4 (3)Flash启动方式 4 3,U-boo ...
- 【ARM-Linux开发】U-Boot启动过程--详细版的完全分析
---------------------------------------------------------------------------------------------------- ...
- (转)U-Boot启动过程--详细版的完全分析
原文出处:http://blog.csdn.net/sydjm/article/details/8591518 -------------------------------------------- ...
- 【转】U-Boot启动过程--详细版的完全分析
我们知道,bootloader是系统上电后最初加载运行的代码.它提供了处理器上电复位后最开始需要执行的初始化代码. 在PC机上引导程序一般由BIOS开始执行,然后读取硬盘中位于MBR(Main Boo ...
- Android系统的启动过程
Android系统的启动过程可以简单地总结为以下几个流程: 加载BootLoader -> 初始化内核 -> 启动init进程 -> init进程fork出Zygote(孵化器)进程 ...
- Android系统默认Home应用程序(Launcher)的启动过程源代码分析
在前面一篇文章中,我们分析了Android系统在启动时安装应用程序的过程,这些应用程序安装好之后,还需要有一个Home应用程序来负责把它们在桌面上展示出来,在Android系统中,这个默认的Home应 ...
- Linux0.11内核引导启动过程概述
Linux0.11仅支持x86架构.它的内核引导启动程序在文件夹boot内,共有三个汇编代码文件.按照启动流程依次是: (1)bootsect.s.boot是启动引导的意思,sect即sector,是 ...
- linux启动sql server数据库,SQL Server数据库启动过程详解及启动不起来的问题分析及解决方法...
第五步.启动系统数据库model model系统数据库同样也是SQL Server启动过程中用到的一个非常关键的数据库,如果这个库损坏,SQL Server启动也会失败,关于model数据不能启动的原 ...
最新文章
- 没有添加跳新增,添加之后跳修改
- 文本查找查找命令的grep 、egrep、fgrep用法的详解
- 13. PDE_PTE属性
- Arraylist、HashSet去重复 treeSet排列实现方法 HashMap遍历取值
- uva 1587(Box UVA - 1587)
- 牛逼的程序员,都长什么样?
- 陕西省计算机二级报名流程,计算机二级考试报名流程
- You can‘t specify target table ‘XXX‘ for update in FROM clause
- 阿里云应用高可用服务公测发布
- 推荐两款工具给爱做实验的人
- android apk 防止反编译技术加壳技术(转)
- eclipse3.7.2+KEmulator搭建J2ME开发环境
- 关于连接PostgreSQL时提示 FATAL: password authentication failed for user 连接用户名 的解决方法...
- 关于node.js,dataStr的undefined的问题
- easyconnect(mac版)总是初始化问题
- 7.Flink实时项目之独立访客开发
- win10重装系统后连不上公司服务器,电脑重装win10系统连不上网,怎么办?
- 科研伦理与学术规范课后答案
- android美颜sdk,Android美颜SDK能否占据现有的市场
- 清华计算机408考研真题资料经验分享
热门文章
- 宝可梦探险寻宝料理php,宝可梦探险寻宝料理配方 宝可梦探险寻宝食谱一览
- 程序员如何用技术变现?(取其精华去其糟粕)
- Skyline产品总体介绍
- 实验4-2-6 输出三角形字符阵列 (15分)本题要求编写程序,输出n行由大写字母A开始构成的三角形字符阵列。
- 关于多部门协作完成项目使用过程中出现问题互相推卸责任的问题
- Facebook、微软、腾讯、DiDi message等全球科技公司都在抢先布局元宇宙
- Cmaker 是什么
- python 傅立叶函数_python-如何从图像中去除高频内容以进行傅立叶逆变换
- 阿里云智能总裁张建锋:“全面上云的拐点到了!”
- 【电源专题】SMPS电源的EMI来源和测量方法