Nuclei SDK启动流程分析

欢迎关注“安全有理”微信公众号。

安全有理

概述

以nuclei-sdk-0.5.0版本进行说明,编译SoC和开发板分别选择默认的evalsocnuclei_fpga_eval,启动的汇编代码参见startup_evalsoc.S。

复位

/* If BOOT_HARTID is not defined, default value is 0 */
#ifndef BOOT_HARTID
    .equ BOOT_HARTID,    0
#endif

.macro DECLARE_INT_HANDLER  INT_HDL_NAME
#if defined(__riscv_xlen) && (__riscv_xlen == 32)
    .word \INT_HDL_NAME
#else
    .dword \INT_HDL_NAME
#endif
.endm

    .section .text.vtable

    .weak eclic_msip_handler
    .weak eclic_mtip_handler
    .weak eclic_uart0_int_handler
    .weak eclic_inter_core_int_handler
    .globl vector_base
    .type vector_base, @object
    .option push
    .option norelax
vector_base:
#ifndef VECTOR_TABLE_REMAPPED
    j _start                                                /* 0: Reserved, Jump to _start when reset for vector table not remapped cases.*/
    .align LOG_REGBYTES                                     /*    Need to align 4 byte for RV32, 8 Byte for RV64 */
#else
    DECLARE_INT_HANDLER     default_intexc_handler          /* 0: Reserved, default handler for vector table remapped cases */
#endif
    DECLARE_INT_HANDLER     default_intexc_handler          /* 1: Reserved */
    DECLARE_INT_HANDLER     default_intexc_handler          /* 2: Reserved */
    DECLARE_INT_HANDLER     eclic_msip_handler              /* 3: Machine software interrupt */

    DECLARE_INT_HANDLER     default_intexc_handler          /* 4: Reserved */
    DECLARE_INT_HANDLER     default_intexc_handler          /* 5: Reserved */
    DECLARE_INT_HANDLER     default_intexc_handler          /* 6: Reserved */
    DECLARE_INT_HANDLER     eclic_mtip_handler              /* 7: Machine timer interrupt */

    DECLARE_INT_HANDLER     default_intexc_handler          /* 8: Reserved */
    DECLARE_INT_HANDLER     default_intexc_handler          /* 9: Reserved */
    DECLARE_INT_HANDLER     default_intexc_handler          /* 10: Reserved */
    DECLARE_INT_HANDLER     default_intexc_handler          /* 11: Reserved */

    DECLARE_INT_HANDLER     default_intexc_handler          /* 12: Reserved */
    DECLARE_INT_HANDLER     default_intexc_handler          /* 13: Reserved */
    DECLARE_INT_HANDLER     default_intexc_handler          /* 14: Reserved */
    DECLARE_INT_HANDLER     default_intexc_handler          /* 15: Reserved */

    DECLARE_INT_HANDLER     eclic_inter_core_int_handler    /* 16: Reserved */
    DECLARE_INT_HANDLER     default_intexc_handler          /* 17: Reserved */
    DECLARE_INT_HANDLER     default_intexc_handler          /* 18: Reserved */
    DECLARE_INT_HANDLER     default_intexc_handler          /* 19: Interrupt 19 */

    DECLARE_INT_HANDLER     default_intexc_handler          /* 20: Interrupt 20 */
    DECLARE_INT_HANDLER     default_intexc_handler          /* 21: Interrupt 21 */
    DECLARE_INT_HANDLER     default_intexc_handler          /* 22: Interrupt 22 */
    DECLARE_INT_HANDLER     default_intexc_handler          /* 23: Interrupt 23 */

    DECLARE_INT_HANDLER     default_intexc_handler          /* 24: Interrupt 24 */
    DECLARE_INT_HANDLER     default_intexc_handler          /* 25: Interrupt 25 */
    DECLARE_INT_HANDLER     default_intexc_handler          /* 26: Interrupt 26 */
    DECLARE_INT_HANDLER     default_intexc_handler          /* 27: Interrupt 27 */

    DECLARE_INT_HANDLER     default_intexc_handler          /* 28: Interrupt 28 */
    DECLARE_INT_HANDLER     default_intexc_handler          /* 29: Interrupt 29 */
    DECLARE_INT_HANDLER     default_intexc_handler          /* 30: Interrupt 30 */
    DECLARE_INT_HANDLER     default_intexc_handler          /* 31: Interrupt 31 */

    DECLARE_INT_HANDLER     default_intexc_handler          /* 32: Interrupt 32 */
    DECLARE_INT_HANDLER     default_intexc_handler          /* 33: Interrupt 33 */
    DECLARE_INT_HANDLER     default_intexc_handler          /* 34: Interrupt 34 */
    DECLARE_INT_HANDLER     default_intexc_handler          /* 35: Interrupt 35 */

    DECLARE_INT_HANDLER     default_intexc_handler          /* 36: Interrupt 36 */
    DECLARE_INT_HANDLER     default_intexc_handler          /* 37: Interrupt 37 */
    DECLARE_INT_HANDLER     default_intexc_handler          /* 38: Interrupt 38 */
    DECLARE_INT_HANDLER     default_intexc_handler          /* 39: Interrupt 39 */

    DECLARE_INT_HANDLER     default_intexc_handler          /* 40: Interrupt 40 */
    DECLARE_INT_HANDLER     default_intexc_handler          /* 41: Interrupt 41 */
    DECLARE_INT_HANDLER     default_intexc_handler          /* 42: Interrupt 42 */
    DECLARE_INT_HANDLER     default_intexc_handler          /* 43: Interrupt 43 */

    DECLARE_INT_HANDLER     default_intexc_handler          /* 44: Interrupt 44 */
    DECLARE_INT_HANDLER     default_intexc_handler          /* 45: Interrupt 45 */
    DECLARE_INT_HANDLER     default_intexc_handler          /* 46: Interrupt 46 */
    DECLARE_INT_HANDLER     default_intexc_handler          /* 47: Interrupt 47 */

    DECLARE_INT_HANDLER     default_intexc_handler          /* 48: Interrupt 48 */
    DECLARE_INT_HANDLER     default_intexc_handler          /* 49: Interrupt 49 */
    DECLARE_INT_HANDLER     default_intexc_handler          /* 50: Interrupt 50 */
    DECLARE_INT_HANDLER     eclic_uart0_int_handler         /* 51: Interrupt 51 */

    DECLARE_INT_HANDLER     default_intexc_handler          /* 52: Interrupt 52 */
    DECLARE_INT_HANDLER     default_intexc_handler          /* 53: Interrupt 53 */
    DECLARE_INT_HANDLER     default_intexc_handler          /* 54: Interrupt 54 */
    DECLARE_INT_HANDLER     default_intexc_handler          /* 55: Interrupt 55 */

    DECLARE_INT_HANDLER     default_intexc_handler          /* 56: Interrupt 56 */
    DECLARE_INT_HANDLER     default_intexc_handler          /* 57: Interrupt 57 */
    DECLARE_INT_HANDLER     default_intexc_handler          /* 58: Interrupt 58 */
    DECLARE_INT_HANDLER     default_intexc_handler          /* 59: Interrupt 59 */

    DECLARE_INT_HANDLER     default_intexc_handler          /* 60: Interrupt 60 */
    DECLARE_INT_HANDLER     default_intexc_handler          /* 61: Interrupt 61 */
    DECLARE_INT_HANDLER     default_intexc_handler          /* 62: Interrupt 62 */
    DECLARE_INT_HANDLER     default_intexc_handler          /* 63: Interrupt 63 */

    .option pop
  • .equ BOOT_HARTID, 0.equ伪操作定义常数,这里定义启动线程ID默认值为0
  • .macro DECLARE_INT_HANDLER INT_HDL_NAME.macro.endm伪操作定义宏,这里声明了中断处理程序,如果是RV32就分配一个字的空间,如果是RV64就分配一个双字的空间
  • .section .text.vtable:接下来代码汇编到.text.vtable
  • .weak ...:设置了4个中断向量的属性为弱,即后续的相同定义(强属性)可以将其覆盖
  • .globl vector_base.globl伪操作定义一个全局的符号,这里定义向量基地址
  • .type vector_base, @object.type伪操作定义符号的类型,这里声明向量基地址为一个对象
  • .option ....option伪操作用于设定某些架构特定的选项,push和pop用于临时保存或者恢复.option伪操作指定的选项,这里.option norelax用于告诉编译器不进行松弛处理优化,即.option push.option pop之间的代码都不进行这种优化
  • vector_base::定义中断向量表,如果定义VECTOR_TABLE_REMAPPED,开启重映射,即向量表的LMA和VMA不同,定义默认的向量表为default_intexc_handler,如果向量表没有重映射,就直接跳转到_start
  • .align LOG_REGBYTES.align伪操作将PC进行对齐,这里RV32表示向量表需要4字节对齐,RV64需要8字节对齐
  • DECLARE_INT_HANDLER ...:声明中断号0~63的中断处理程序,这里只定义了4个中断处理程序eclic_msip_handlereclic_mtip_handlereclic_uart0_int_handlereclic_inter_core_int_handler,其他中断均未定义

启动第一阶段

第一阶段主要是关闭中断,初始化gp全局寄存器和tp线程寄存器,配置中断、NVM和异常向量表基地址等。

    .section .text.init
    .globl _start
    .type _start, @function

/**
 * Reset Handler called on controller reset
 */
_start:
    /* ===== Startup Stage 1 ===== */
    /* Disable Global Interrupt */
    csrc CSR_MSTATUS, MSTATUS_MIE

    /* If SMP_CPU_CNT is not defined,
     * assume that only 1 core is allowed to run,
     * the core hartid is defined via BOOT_HARTID.
     * other harts if run to here, just do wfi in __amp_wait
     */
#ifndef SMP_CPU_CNT
    /* take bit 0-7 for hart id in a local cluster */
    csrr a0, CSR_MHARTID
    andi a0, a0, 0xFF
    /* BOOT_HARTID is configurable in Makefile via BOOT_HARTID variable */
    li a1, BOOT_HARTID
    bne a0, a1, __amp_wait
#endif

    /* Initialize GP and TP and jump table base when zcmt enabled */
    .option push
    .option norelax
    la gp, __global_pointer$
    la tp, __tls_base
#if defined(__riscv_zcmt)
    la t0, __jvt_base$
    csrw CSR_JVT, t0
#endif
    .option pop

#if defined(SMP_CPU_CNT) && (SMP_CPU_CNT > 1)
    /* Set correct sp for each cpu
     * each stack size is __STACK_SIZE
     * defined in linker script */
    lui t0, %hi(__STACK_SIZE)
    addi t0, t0, %lo(__STACK_SIZE)
    la sp, _sp
    csrr a0, CSR_MHARTID
    andi a0, a0, 0xFF
    li a1, 0
1:
    beq a0, a1, 2f
    sub sp, sp, t0
    addi a1, a1, 1
    j 1b
2:
#else
    /* Set correct sp for current cpu */
    la sp, _sp
#endif

    /*
     * Set the the NMI base mnvec to share
     * with mtvec by setting CSR_MMISC_CTL
     * bit 9 NMI_CAUSE_FFF to 1
     */
    li t0, MMISC_CTL_NMI_CAUSE_FFF
    csrs CSR_MMISC_CTL, t0

    /*
     * Enable Zc feature when compiled zcmp & zcmt
     */
#if defined(__riscv_zcmp) || defined(__riscv_zcmt)
    li t0, MMISC_CTL_ZC
    csrs CSR_MMISC_CTL, t0
#endif

    /*
     * Intialize ECLIC vector interrupt
     * base address mtvt to vector_base
     */
    la t0, vector_base
    csrw CSR_MTVT, t0

    /*
     * Set ECLIC non-vector entry to be controlled
     * by mtvt2 CSR register.
     * Intialize ECLIC non-vector interrupt
     * base address mtvt2 to irq_entry.
     */
    la t0, irq_entry
    csrw CSR_MTVT2, t0
    csrs CSR_MTVT2, 0x1

    /*
     * Set Exception Entry MTVEC to early_exc_entry
     * Due to settings above, Exception and NMI
     * will share common entry.
     * This early_exc_entry is only used during early
     * boot stage before main
     */
    la t0, early_exc_entry
    csrw CSR_MTVEC, t0

    /* Set the interrupt processing mode to ECLIC mode */
    li t0, 0x3f
    csrc CSR_MTVEC, t0
    csrs CSR_MTVEC, 0x3
  • _start:定义复位中断处理函数,即系统复位就从此开始执行
  • csrc CSR_MSTATUS, MSTATUS_MIE:清零mstatus寄存器中的MIE位,即关闭M模式下所有中断
  • csrr a0, CSR_MHARTID:读取CSR寄存器的硬件线程ID,然后获取低8位andi a0, a0, 0xFF
  • li a1, BOOT_HARTID:读取Makefile配置的启动线程ID
  • bne a0, a1, __amp_wait:如果配置的BOOT_HARTIDCSR_MHARTID不相等,即当前运行的核不是配置启动的核,就直接进入wfi
  • la gp, __global_pointer$:初始化gp全局寄存器,gp用于链接器松弛优化
  • la tp, __tls_base:初始化tp线程寄存器,tp用于在操作系统中保存指向进程控制块——task_struct数据结构的指针
  • la t0, __jvt_base$csrw CSR_JVT, t0:使能zcmt扩展
  • lui t0, %hi(__STACK_SIZE)addi t0, t0, %lo(__STACK_SIZE):将__STACK_SIZE的值加载到t0寄存器中,%hi%lo宏用于提取一个32位地址的高16位和低16位
  • la sp, _sp:初始化sp栈针寄存器,指向栈的地址
  • csrr a0, CSR_MHARTIDandi a0, a0, 0xFF:读取CSR寄存器的硬件线程ID
  • li a1, 0:加载立即数到a1寄存器
  • beq a0, a1, 2fsub sp, sp, t0addi a1, a1, 1j 1b:循环为每个CPU核心调整栈指针,最终都会被设置为初始栈指针减去相应数量的__STACK_SIZE
  • la sp, _sp:如果未定义SMP_CPU_CNT,即一个cluster只有一个cpu,就直接跳转至此,初始化sp栈针寄存器,指向栈的地址
  • li t0, MMISC_CTL_NMI_CAUSE_FFFcsrs CSR_MMISC_CTL, t0:配置NMI与异常有相同的入口地址
  • li t0, MMISC_CTL_ZCcsrs CSR_MMISC_CTL, t0:使能Zc特性
  • la t0, vector_basecsrw CSR_MTVT, t0:初始化ECLIC中断向量表的基地址为vector_base
  • la t0, irq_entrycsrw CSR_MTVT2, t0csrs CSR_MTVT2, 0x1:初始化非向量中断处理模式的中断入口地址为irq_entry,并将非向量中断的入口地址与异常的入口地址分开
  • la t0, early_exc_entrycsrw CSR_MTVEC, t0:设置异常入口基地址为early_exc_entry
  • li t0, 0x3fcsrc CSR_MTVEC, t0csrs CSR_MTVEC, 0x3:设置中断处理模式为ECLIC模式

启动第二阶段

第二阶段主要是使能FPU和Vector单元,开启控制计数器。

    /* Enable FPU and Vector Unit if f/d/v exist in march */
#if defined(__riscv_flen) && __riscv_flen > 0
    /* Enable FPU, and set state to initial */
    li t0, MSTATUS_FS
    csrc mstatus, t0
    li t0, MSTATUS_FS_INITIAL
    csrs mstatus, t0
#endif

#if defined(__riscv_vector)
    /* Enable Vector, and set state to initial */
    li t0, MSTATUS_VS
    csrc mstatus, t0
    li t0, MSTATUS_VS_INITIAL
    csrs mstatus, t0
#endif

    /* Enable mcycle and minstret counter */
    csrci CSR_MCOUNTINHIBIT, 0x5

#if defined(SMP_CPU_CNT) && (SMP_CPU_CNT > 1)
    csrr a0, CSR_MHARTID
    li a1, BOOT_HARTID
    bne a0, a1, __skip_init
#endif
  • li t0, MSTATUS_FScsrc mstatus, t0li t0, MSTATUS_FS_INITIALcsrs mstatus, t0:使能浮点运算单元,设置状态为初始化
  • li t0, MSTATUS_VScsrc mstatus, t0li t0, MSTATUS_VS_INITIALcsrs mstatus, t0:使能可伸缩矢量扩展,设置状态为初始化
  • csrci CSR_MCOUNTINHIBIT, 0x5:使能mcycle和minstret的计数
  • csrr a0, CSR_MHARTIDli a1, BOOT_HARTIDbne a0, a1, __skip_init:如果配置的BOOT_HARTIDCSR_MHARTID不相等,即当前运行的核不是配置启动的核,就跳过第三阶段相关的初始化动作

启动第三阶段

第三阶段主要是完成数据段和代码段搬运,BSS清零等工作。

__init_common:
    /* ===== Startup Stage 3 ===== */
    /*
     * Load text section from CODE ROM to CODE RAM
     * when text LMA is different with VMA
     */
    la a0, _text_lma
    la a1, _text
    /* If text LMA and VMA are equal
     * then no need to copy text section */
    beq a0, a1, 2f
    la a2, _etext
    bgeu a1, a2, 2f

1:
    /* Load code section if necessary */
    lw t0, (a0)
    sw t0, (a1)
    addi a0, a0, 4
    addi a1, a1, 4
    bltu a1, a2, 1b
2:
    /* Load data section */
    la a0, _data_lma
    la a1, _data
    /* If data vma=lma, no need to copy */
    beq a0, a1, 2f
    la a2, _edata
    bgeu a1, a2, 2f
1:
    lw t0, (a0)
    sw t0, (a1)
    addi a0, a0, 4
    addi a1, a1, 4
    bltu a1, a2, 1b
2:
    /* Clear bss section */
    la a0, __bss_start
    la a1, _end
    bgeu a0, a1, 2f
1:
    sw zero, (a0)
    addi a0, a0, 4
    bltu a0, a1, 1b
2:
  • la a0, _text_lmala a1, _text:分别加载代码段载入地址和运行地址到a0和a1寄存器中
  • beq a0, a1, 2fla a2, _etextbgeu a1, a2, 2f:如果加载地址和运行地址相等,不需要执行代码段拷贝动作,即跳转到标签2处
  • lw t0, (a0)sw t0, (a1)addi a0, a0, 4addi a1, a1, 4bltu a1, a2, 1b:从加载地址处搬运代码段到运行地址处
  • la a0, _data_lmala a1, _databeq a0, a1, 2fla a2, _edatabgeu a1, a2, 2f:同理如果加载地址和运行地址相等,不需要执行数据段拷贝动作,即跳转到标签2处
  • lw t0, (a0)sw t0, (a1)addi a0, a0, 4addi a1, a1, 4bltu a1, a2, 1b:从加载地址处搬运数据段到运行地址处
  • la a0, __bss_startla a1, _endbgeu a0, a1, 2f:加载bss段起始地址和结束地址到a0和a1中
  • sw zero, (a0)addi a0, a0, 4bltu a0, a1, 1b:清零bss段

跳转主函数

.globl _start_premain
.type _start_premain, @function
_start_premain:
    /*
     * Call vendor defined SystemInit to
     * initialize the micro-controller system
     * SystemInit will just be called by boot cpu
     */
    call SystemInit

    /*
     * Call C/C++ constructor start up code,
     * __libc_fini is defined in linker script,
     * so register_fini function will be called
     * and will run atexit (__libc_fini_array)
     * to do previous call atexit function
     */
    call __libc_init_array

__skip_init:
    /* Sync all harts at this function */
    call __sync_harts

    /* do pre-init steps before main */
    /* _premain_init will be called by each cpu
     * please make sure the implementation of __premain_int
     * considered this
     */
    call _premain_init

    /*
     * When all initialization steps done
     * set exception entry to correct exception
     * entry and jump to main.
     * And set the interrupt processing mode to
     * ECLIC mode
     */
    la t0, exc_entry
    csrw CSR_MTVEC, t0
    li t0, 0x3f
    csrc CSR_MTVEC, t0
    csrs CSR_MTVEC, 0x3

    /* BPU cold bringup need time, so enable BPU before enter to main */
    li t0, MMISC_CTL_BPU
    csrs CSR_MMISC_CTL, t0

    /* ===== Call SMP Main Function  ===== */
    /* argc = argv = 0 */
    li a0, 0
    li a1, 0
#if defined(SMP_CPU_CNT) && (SMP_CPU_CNT > 1)
    /* The weak implementation of smp_main is in this file */
    call smp_main
#else
#ifdef RTOS_RTTHREAD
    // Call entry function when using RT-Thread
    call entry
#else
    call main
#endif
#endif
  • call SystemInit:调用厂商定义的系统初始化函数,目前是配置系统时钟

    void SystemInit(void)
    {
        /* ToDo: add code to initialize the system
         * Warn: do not use global variables because this function is called before
         * reaching pre-main. RW section maybe overwritten afterwards.
         */
        SystemCoreClock = SYSTEM_CLOCK;
    }
    
  • call __libc_init_array:调用C/C++构建启动代码

  • call __sync_harts:同步所有的harts,即有的hart可能在等待启动hart完成数据段、bss段以及C运行环境的初始化,因此只针对一个cluster的cpu数大于1的情形

    void __sync_harts(void)
    {
    // Only do synchronize when SMP_CPU_CNT is defined and number > 0
    #if defined(SMP_CPU_CNT) && (SMP_CPU_CNT > 1)
        unsigned long hartid = __get_hart_id();
        unsigned long tmr_hartid = __get_hart_index();
        unsigned long clint_base, irgb_base, smp_base;
        unsigned long mcfg_info;
    
        mcfg_info = __RV_CSR_READ(CSR_MCFG_INFO);
        if (mcfg_info & MCFG_INFO_IREGION_EXIST) { // IRegion Info present
            // clint base = system timer base + 0x1000
            irgb_base = (__RV_CSR_READ(CSR_MIRGB_INFO) >> 10) << 10;
            clint_base = irgb_base + IREGION_TIMER_OFS + 0x1000;
            smp_base = irgb_base + IREGION_SMP_OFS;
        } else {
            clint_base = FALLBACK_DEFAULT_SYSTIMER_BASE + 0x1000;
            smp_base = (__RV_CSR_READ(CSR_MSMPCFG_INFO) >> 4) << 4;
        }
        // Enable SMP and L2, disable cluster local memory
        SMP_CTRLREG(smp_base, 0xc) = 0xFFFFFFFF;
        SMP_CTRLREG(smp_base, 0x10) = 0x1;
        SMP_CTRLREG(smp_base, 0xd8) = 0x0;
        __SMP_RWMB();
    
        // pre-condition: interrupt must be disabled, this is done before calling this function
        // BOOT_HARTID is defined <Device.h>
        if (hartid == BOOT_HARTID) { // boot hart
            // clear msip pending
            for (int i = 0; i < SMP_CPU_CNT; i ++) {
                CLINT_MSIP(clint_base, i) = 0;
            }
            __SMP_RWMB();
        } else {
            // Set machine software interrupt pending to 1
            CLINT_MSIP(clint_base, tmr_hartid) = 1;
            __SMP_RWMB();
            // wait for pending bit cleared by boot hart
            while (CLINT_MSIP(clint_base, tmr_hartid) == 1);
        }
    #endif
    }
    
  • call _premain_init:在跳转到main函数之前,执行一些初始化步骤,

    void _premain_init(void)
    {
        // TODO to make it possible for configurable boot hartid
        unsigned long hartid = __get_hart_id();
    
        // BOOT_HARTID is defined <Device.h>
        if (hartid == BOOT_HARTID) { // only done in boot hart
            // IREGION INFO MUST BE SET BEFORE ANY PREMAIN INIT STEPS
            _get_iregion_info((IRegion_Info_Type *)(&SystemIRegionInfo));
        }
        /* TODO: Add your own initialization code here, called before main */
        // This code located in RUNMODE_CONTROL ifdef endif block just for internal usage
        // No need to use in your code
    #ifdef RUNMODE_CONTROL
    #if defined(RUNMODE_ILM_EN) && RUNMODE_ILM_EN == 0
        // Only disable ilm when it is present
        if (__RV_CSR_READ(CSR_MCFG_INFO) & MCFG_INFO_ILM) {
            __RV_CSR_CLEAR(CSR_MILM_CTL, MILM_CTL_ILM_EN);
        }
    #endif
    #if defined(RUNMODE_DLM_EN) && RUNMODE_DLM_EN == 0
        // Only disable dlm when it is present
        if (__RV_CSR_READ(CSR_MCFG_INFO) & MCFG_INFO_DLM) {
            __RV_CSR_CLEAR(CSR_MDLM_CTL, MDLM_CTL_DLM_EN);
        }
    #endif
    #endif
    
        /* __ICACHE_PRESENT and __DCACHE_PRESENT are defined in demosoc.h */
        // For our internal cpu testing, they want to set demosoc __ICACHE_PRESENT/__DCACHE_PRESENT to be 1
        // __CCM_PRESENT is still default to 0 in demosoc.h, since it is used in core_feature_eclic.h to register interrupt, if set to 1, it might cause exception
        // but in the cpu, icache or dcache might not exist due to cpu configuration, so here
        // we need to check whether icache/dcache really exist, if yes, then turn on it
    #if defined(__ICACHE_PRESENT) && (__ICACHE_PRESENT == 1)
        if (ICachePresent()) { // Check whether icache real present or not
            EnableICache();
        }
    #endif
    #if defined(__DCACHE_PRESENT) && (__DCACHE_PRESENT == 1)
        if (DCachePresent()) { // Check whether dcache real present or not
            EnableDCache();
        }
    #endif
    
        /* Do fence and fence.i to make sure previous ilm/dlm/icache/dcache control done */
        __RWMB();
        __FENCE_I();
    
        if (hartid == BOOT_HARTID) { // only required for boot hartid
            // TODO implement get_cpu_freq function to get real cpu clock freq in HZ or directly give the real cpu HZ
            SystemCoreClock = get_cpu_freq();
            uart_init(SOC_DEBUG_UART, 115200);
            /* Display banner after UART initialized */
            SystemBannerPrint();
            /* Initialize exception default handlers */
            Exception_Init();
            /* ECLIC initialization, mainly MTH and NLBIT */
            ECLIC_Init();
            Trap_Init();
            // TODO: internal usage for Nuclei
    #ifdef RUNMODE_CONTROL
            printf("Current RUNMODE=%s, ilm:%d, dlm %d, icache %d, dcache %d, ccm %d\n", \
                RUNMODE_STRING, RUNMODE_ILM_EN, RUNMODE_DLM_EN, \
                RUNMODE_IC_EN, RUNMODE_DC_EN, RUNMODE_CCM_EN);
            printf("CSR: MILM_CTL 0x%x, MDLM_CTL 0x%x, MCACHE_CTL 0x%x\n", \
                __RV_CSR_READ(CSR_MILM_CTL), __RV_CSR_READ(CSR_MDLM_CTL), \
                __RV_CSR_READ(CSR_MCACHE_CTL));
    #endif
        }
    }
    
    • _get_iregion_info:获取CPU内部寄存器信息,如eclic基地址,系统定时器基地址,smp基地址等
    • EnableICacheEnableDCache:使能ICACHE和DCACHE
    • __RWMB__FENCE_I:展开为fence iorw, iorwfence.i,fence所有的内存和I/O,类似于arm的isb命令
    • get_cpu_freq:获取cpu时钟频率
    • uart_init(SOC_DEBUG_UART, 115200):串口初始化
    • SystemBannerPrint:打印编译时间,下载模式,CPU频率以及HartID等信息
    • Exception_Init:配置默认的异常处理函数
    • ECLIC_Init:初始化eclic配置
    • Trap_Init:初始化监控模式下的trap(中断和异常)入口地址
  • la t0, exc_entrycsrw CSR_MTVEC, t0li t0, 0x3fcsrc CSR_MTVEC, t0csrs CSR_MTVEC, 0x3:配置异常程序的入口地址,并设置处理器位ECLIC中断模式,exc_entryintexc_evalsoc.S有一个弱函数实现,先保存上下文,保存CSR寄存器,然后调用core_exception_handler进行异常处理,最后恢复上下文和CSR寄存器,进行异常返回:

    .weak exc_entry
    exc_entry:
        /* Save the caller saving registers (context) */
        SAVE_CONTEXT
        /* Save the necessary CSR registers */
        SAVE_CSR_CONTEXT
    
        /*
         * Set the exception handler function arguments
         * argument 1: mcause value
         * argument 2: current stack point(SP) value
         */
        csrr a0, mcause
        mv a1, sp
        /*
         * TODO: Call the exception handler function
         * By default, the function template is provided in
         * system_Device.c, you can adjust it as you want
         */
        call core_exception_handler
    
        /* Restore the necessary CSR registers */
        RESTORE_CSR_CONTEXT
        /* Restore the caller saving registers (context) */
        RESTORE_CONTEXT
    
        /* Return to regular code */
        mret
    
  • li t0, MMISC_CTL_BPUcsrs CSR_MMISC_CTL, t0:开启控制分支预测器

  • li a0, 0li a1, 0:加载0到a0和a1寄存器,即main函数的argc和argv均置为0

  • call smp_main:如果是多CPU系统,就跳转到smp_main

  • call entry:如果使用RT-Thread相同,就跳转到entry函数

  • call main:如果是裸机就跳转到main函数,进入主函数继续执行

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/420346.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Linux系统加固:限制用户对资源的使用禁止IP源路由更改主机解析地址的顺序设置umask值

Linux系统加固&#xff1a;限制用户对资源的使用&禁止IP源路由&更改主机解析地址的顺序&设置umask值 1.1 限制用户对资源的使用1.2 禁止IP源路由1.3 更改主机解析地址的顺序1.4 禁止ip路由转发1.5 设置umask值 &#x1f496;The Begin&#x1f496;点点关注&#x…

外泌体相关基因肝癌临床模型预测——2-3分纯生信文章复现——02.数据格式整理(2)

内容如下&#xff1a; 1.外泌体和肝癌TCGA数据下载 2.数据格式整理 3.差异表达基因筛选 4.预后相关外泌体基因确定 5.拷贝数变异及突变图谱 6.外泌体基因功能注释 7.LASSO回归筛选外泌体预后模型 8.预后模型验证 9.预后模型鲁棒性分析 10.独立预后因素分析及与临床的…

alpine创建lnmp环境alpine安装nginx+php5.6+mysql

前言 制作lnmp环境&#xff0c;你可以在alpine基础镜像中安装相关的服务&#xff0c;也可以直接使用Dockerfile创建自己需要的环境镜像。 注意&#xff1a;提前确认自己的alpine版本&#xff0c;本次创建基于alpine3.6进行创建&#xff0c;官方在一些版本中删除了php5 1、拉取…

Java 小项目开发日记 04(文章接口的开发、oss图片上传)

Java 小项目开发日记 04&#xff08;文章接口的开发、oss图片上传&#xff09; 项目目录 配置文件&#xff08;pom.xml&#xff09; <project xmlns"http://maven.apache.org/POM/4.0.0" xmlns:xsi"http://www.w3.org/2001/XMLSchema-instance"xsi:sc…

面试问答总结之并发编程

文章目录 &#x1f412;个人主页&#x1f3c5;JavaEE系列专栏&#x1f4d6;前言&#xff1a;&#x1f380;多线程的优点、缺点&#x1f415;并发编程的核心问题 &#xff1a;不可见性、乱序性、非原子性&#x1fa80;不可见性&#x1fa80;乱序性&#x1fa80;非原子性&#x1…

Linux - 权限概念

Linux下有两种用户&#xff1a;超级用户&#xff08;root&#xff09;、普通用户。 超级用户&#xff1a;可以再linux系统下做任何事情&#xff0c;不受限制普通用户&#xff1a;在linux下做有限的事情超级用户的命令提示符是“#”&#xff0c;普通用户的命令提示符是“$” 命…

深入探究Python多进程编程:Multiprocessing模块基础与实战【第98篇—Multiprocessing模块】

深入探究Python多进程编程&#xff1a;Multiprocessing模块基础与实战 在Python编程中&#xff0c;多进程处理是一项关键的技术&#xff0c;特别是在需要处理大规模数据或执行耗时任务时。为了充分利用多核处理器的优势&#xff0c;Python提供了multiprocessing模块&#xff0…

G8-ACGAN理论

本文为&#x1f517;365天深度学习训练营 中的学习记录博客 原作者&#xff1a;K同学啊|接辅导、项目定制 我的环境&#xff1a; 1.语言&#xff1a;python3.7 2.编译器&#xff1a;pycharm 3.深度学习框架Pytorch 1.8.0cu111 一、对比分析 前面的文章介绍了CGAN&#xf…

Python批量提取文件夹中图片的名称及路径到指定的.txt文件中

目录 一、代码二、提取效果 一、代码 import os# 定义要保存的文件名 file_name "TestImage/Image_Visible_Gray.txt"# 读取文件夹路径 folder_path "TestImage/Image_Visible_Gray"# 遍历文件夹中的所有文件 with open(file_name, "w") as f…

132557-72-3,2,3,3-三甲基-3H-吲哚-5-磺酸,具有优异的反应活性和光学性能

132557-72-3&#xff0c;5-Sulfo-2,3,3-trimethyl indolenine sodium salt&#xff0c;2,3,3-三甲基-3H-吲哚-5-磺酸&#xff0c;具有优异的反应活性和光学性能&#xff0c;一种深棕色粉末 您好&#xff0c;欢迎来到新研之家 文章关键词&#xff1a;132557-72-3&#xff0c;5…

使用maven项目引入jQuery

最近在自学 springBoot &#xff0c;期间准备搞一个前后端不分离的东西&#xff0c;于是需要在 maven 中引入jQuery 依赖&#xff0c;网上百度了很多&#xff0c;这里来做一个总结。 1、pom.xml 导入依赖 打开我们项目的 pom.xml 文件&#xff0c;输入以下坐标。这里我使用的是…

[BUUCTF]-Reverse:reverse3解析

查看ida 从下图的/3和*4可以推断得出来是base64加密。 ida里大致意思就是我们输入的字符串经过base64加密&#xff0c;循环递减&#xff0c;最后等于str2&#xff0c;那我们输入的字符串就是flag。 完整exp&#xff1a; import base64 liste3nifIH9b_CndH print(len(list))fl…

2024年3月腾讯云服务器优惠价格曝光!这价格没谁了

腾讯云优惠活动2024新春采购节活动上线&#xff0c;云服务器价格已经出来了&#xff0c;云服务器61元一年起&#xff0c;配置和价格基本上和上个月没什么变化&#xff0c;但是新增了8888元代金券和会员续费优惠&#xff0c;腾讯云百科txybk.com整理腾讯云最新优惠活动云服务器配…

深度伪造,让网络钓鱼更加难以辨别

网络钓鱼一直是安全领域的一个突出话题&#xff0c;尽管这类诈骗形式已经存在了几十年&#xff0c;依旧是欺诈攻击或渗透组织的最有效方法之一。诈骗分子基于社会工程原理&#xff0c;通过邮件、网站以及电话、短信和社交媒体&#xff0c;利用人性&#xff08;如冲动、不满、好…

智能分析网关V4电瓶车检测与烟火算法,全面提升小区消防安全水平

2024年2月23日&#xff0c;南京市某小区因电瓶车停放处起火引发火灾事故&#xff0c;造成巨大人员伤亡和损失。根据国家消防救援局的统计&#xff0c;2023年全国共接报电动自行车火灾2.1万起。电瓶车火灾事故频发&#xff0c;这不得不引起我们的重视和思考&#xff0c;尤其是在…

用于游戏开发的顶级 PYTHON 框架

一、说明 我们试图用python开发游戏&#xff0c;一旦产生这个念头&#xff0c;就伴随这样一个问题&#xff1a;当今用于构建游戏的领先 Python 框架有哪些&#xff1f;python下&#xff0c;支持游戏开发平台有哪些优势&#xff1f;我们在这篇博文中告诉你。 二、高级游戏平台简…

小甲鱼Python07 函数初级

一、创建和调用函数 pass语句表示一个空的代码块&#xff0c;我们经常先写好函数&#xff0c;pass占一个坑&#xff0c;等规划好之后再来填坑。 函数也是可以指定参数的&#xff0c;我们会把参数传进去用来替代形参。 在Python里如果想要返回值&#xff0c;不需要指定函数的返…

高侧开关芯片四通道 40V 50mΩ车规级带反向电流保护功能负载检测高边开关

概述 PC8845/G是四通道、高侧功率具有集成NMOS功率FET的开关&#xff0c;以及电荷泵。该设备集成了高级 保护功能&#xff0c;例如负载电流限制&#xff0c;通过功率限制进行过载主动管理可配置闩锁关闭的超温停机。全面诊断和高精度电流感应这些功能实现了对负载的智能控制。…

2326. 王者之剑(网络流,最小割,最大权独立集,最小点权覆盖)

活动 - AcWing 给出一个 nm 网格&#xff0c;每个格子上有一个价值 vi,j 的宝石。 Amber 可以自己决定起点&#xff0c;开始时刻为第 0 秒。 以下操作&#xff0c;在每秒内按顺序执行。 若第 i 秒开始时&#xff0c;Amber 在 (x,y)&#xff0c;则 Amber 可以拿走 (x,y) 上的…

LeetCode刷题--- 乘积为正数的最长子数组长度

个人主页&#xff1a;元清加油_【C】,【C语言】,【数据结构与算法】-CSDN博客 个人专栏 力扣递归算法题 http://t.csdnimg.cn/yUl2I 【C】 ​​​​​​http://t.csdnimg.cn/6AbpV 数据结构与算法 ​​​http://t.csdnimg.cn/hKh2l 前言&#xff1a;这个专栏主要讲述动…