zephyr_02_內核啓動

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)

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章