zephyr是一款輕量級的linux, 中文翻譯爲”和風, 西風, 輕薄織物”, 推出不到兩年的時間, 已從剛開始支持的4,5個到現在支持市面上主流的MCU, 發展速度及其迅速, 最主要是開源, 現在更新到1.9.2, 基於最新版本代碼, 汲取網上大牛們的經驗, 對系統的啓動流程做個簡單的記錄 (以cortex-cm4爲例, 因手頭的nrf52840是基於M4核的).
CPU上電覆位操作流程
1. 查找向量中斷表, 跳轉到復位異常函數處理執行
2. 鎖中斷(NMI和hard Fault除外)
3. 初始化看門狗(條件編譯, 可選)
4. 初始化中斷棧空間(條件編譯, 可選) // 但爲什麼要初始化爲0xaa by Huihh 2017.12.8
5. 將PSP指向棧頂地址
6. 跳轉到_PrepC函數, 準備C代碼運行環境
7. 根據啓動方式, 重定位向量表
8. 將BBS段清0
9. 數據拷貝, 從ROM拷貝到RAM中 (完成之後, C代碼環境已準備好)
10. 初始化內核 (調用前必須確保系統已準備好執行C代碼的環境, 處理器必須運行在32-bit模式, BSS段已清0)
1. 向量表
Cortex-M4的異常向量表
. 1-15 //系統異常向量表
. 16-255 //中斷向量表, 芯片廠家一般只會實現其中一部分中斷
2. 啓動源文件(/${ZEPHYR_BASE}/arch/arm/core/cortex_m/vector_table.S)
#include <board.h>
#include <toolchain.h>
#include <linker/sections.h>
#include <drivers/system_timer.h>
#include "vector_table.h"
_ASM_FILE_PROLOGUE
GDATA(_main_stack) //_main_stack 全局符號, 存放在數據段
SECTION_SUBSEC_FUNC(exc_vector_table,_vector_table_section,_vector_table)
.word _main_stack + CONFIG_MAIN_STACK_SIZE //0 N/A
.word __reset //1 系統上電, 發生復位異常, 既會跳轉到 __reset符號處執行
.word __nmi //2 不可屏蔽中斷NMI,
.word __hard_fault //3 硬中斷
.word __mpu_fault //4 MPU fault
.word __bus_fault //5 總線 fault
.word __usage_fault //6 用法 fault
.word __reserved //7 保留
.word __reserved //8 保留
.word __reserved //9 保留
.word __reserved //10 保留
.word __svc //11 SVC 系統調用
.word __debug_monitor //12 調試監視器
.word __reserved //13 保留
.word __pendsv //14 PendSV
#if defined(CONFIG_CORTEX_M_SYSTICK) //條件編譯, 如果存在systick 則定義, 否則保留
.word _timer_int_handler //15 SysTick
#else
.word __reserved
#endif
3. 上電覆位, __reset處理, 爲執行C代碼準備環境 (/${ZEPHYR_BASE}/arch/arm/core/cortex_m/reset.S)
1. 系統從reset中運行, 處理器處於特權級別的線程模式, 主堆棧(MSP)指向SRAM的有效區域
2. 鎖定中斷(除NMI和hard Fault外), 默認的NMI異常處理已存在向量表中, boot代碼不應該產生hard Fault
3. 使用進程堆棧(PSP)替代主堆棧(MSP), 一旦主堆棧(MSP)用於指向唯一的中斷堆棧繼續啓動, 這在運行C代碼時是不可能的
4. 這些步驟完成後, 跳轉到_PrepC()函數, 它將完成設置C代碼的運行環境
#include <board.h>
#include <toolchain.h>
#include <linker/sections.h>
#include <arch/cpu.h>
#include <offsets_short.h>
#include "vector_table.h"
_ASM_FILE_PROLOGUE
GTEXT(__reset)
GTEXT(memset)
GDATA(_interrupt_stack)
SECTION_SUBSEC_FUNC(TEXT,_reset_section,__reset)
SECTION_SUBSEC_FUNC(TEXT,_reset_section,__start) //XIP啓動, __start 開始啓動處的別名
movs.n r0, #_EXC_IRQ_DEFAULT_PRIO //鎖中斷
msr BASEPRI, r0
#ifdef CONFIG_WDOG_INIT //條件編譯 初始化看門狗
bl _WdogInit
#endif
#ifdef CONFIG_INIT_STACKS //條件編譯 初始化堆棧 (初始值爲0xaa) //爲什麼不是0x00 by Huihh 2017.12.8
ldr r0, =_interrupt_stack
ldr r1, =0xaa
ldr r2, =CONFIG_ISR_STACK_SIZE
bl memset //memset參數: Para1: r0, Para2: r1, Para3: r2
#endif
ldr r0, =_interrupt_stack //將PSP指向棧頂(棧頂=棧起始地址+棧大小)
ldr r1, =CONFIG_ISR_STACK_SIZE
adds r0, r0, r1
msr PSP, r0
movs.n r0, #2
msr CONTROL, r0 //配置CONTROL寄存器, 使能進程堆棧寄存器(PSP)
b _PrepC //跳轉_PrepC 函數, 準備C代碼執行環境
4. _PrepC (/${ZEPHYR_BASE}/arch/arm/core/cortex_m/prep_c.c)
啓動方式(具體啓動方式由SoC設計決定)
1. XIP 模式 (eXecute In Place), 在該模式下,CPU直接從Nor Flash上讀代碼執行 執行速度慢
2. 非XIP模式 在該模式下, 硬件先將代碼從Flash上搬移到RAM上後, CPU才能從RAM上訪問數據 執行速度快
#include <kernel.h>
#include <zephyr/types.h>
#include <toolchain.h>
#include <linker/linker-defs.h>
#include <nano_internal.h>
#include <arch/arm/cortex_m/cmsis.h>
#include <string.h>
#ifdef CONFIG_XIP
#define VECTOR_ADDRESS ((uintptr_t)&_image_rom_start + \
CONFIG_TEXT_SECTION_OFFSET) //XIP啓動, 向量表地址, 默認爲0x00地址
#else
#define VECTOR_ADDRESS CONFIG_SRAM_BASE_ADDRESS //非XIP啓動, (硬件已搬移代碼)向量表地址, 指向SRAM的地址
#endif
static inline void relocate_vector_table(void) //重定位向量表地址, 決定從SRAM/Nor Flash啓動
{
SCB->VTOR = VECTOR_ADDRESS & SCB_VTOR_TBLOFF_Msk;
__DSB();
__ISB();
}
#ifdef CONFIG_FLOAT
static inline void enable_floating_point(void) //浮點單元配置
{
/*
* Upon reset, the Co-Processor Access Control Register is 0x00000000.
* Enable CP10 and CP11 coprocessors to enable floating point.
*/
SCB->CPACR |= CPACR_CP10_FULL_ACCESS | CPACR_CP11_FULL_ACCESS;
/*
* Upon reset, the FPU Context Control Register is 0xC0000000
* (both Automatic and Lazy state preservation is enabled).
* Disable lazy state preservation so the volatile FP registers are
* always saved on exception.
*/
FPU->FPCCR = FPU_FPCCR_ASPEN_Msk; /* FPU_FPCCR_LSPEN = 0 */
/*
* Although automatic state preservation is enabled, the processor
* does not automatically save the volatile FP registers until they
* have first been touched. Perform a dummy move operation so that
* the stack frames are created as expected before any task or fiber
* context switching can occur.
*/
__asm__ volatile(
"vmov s0, s0;\n\t"
"dsb;\n\t"
"isb;\n\t"
);
}
#else
static inline void enable_floating_point(void)
{
}
#endif
extern FUNC_NORETURN void _Cstart(void);
/**
*
* @brief Prepare to and run C code
*
* This routine prepares for the execution of and runs C code.
*
* @return N/A
*/
#ifdef CONFIG_BOOT_TIME_MEASUREMENT
extern u64_t __start_time_stamp;
#endif
void _PrepC(void)
{
relocate_vector_table(); //重定位向量表
enable_floating_point(); //浮點單元(FPU)
_bss_zero(); //bss段清0 (實現 /${ZEPHYR_BASE}/kernel/init.c)
_data_copy(); //數據拷貝 (實現 /${ZEPHYR_BASE}/kernel/init.c)
#ifdef CONFIG_BOOT_TIME_MEASUREMENT
__start_time_stamp = 0;
#endif
_Cstart(); //初始化內核
CODE_UNREACHABLE;
}
5. _Cstart() 初始化內核 (/${ZEPHYR_BASE}/kernel/init.c)