芯来N300系列芯片SDK-NUCLEI-SDK解析(V0.4.0)(NucleiStudio)

这章主要分析启动开始文件startup_Device.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

这里一个汇编宏定义,主要用于定义向量表,在存储器里面开辟的一段连续的地址空间;其中各个中断服务函数(Interrupt Service Routine,ISR)的PC地址,32位机地址长度是32位,使用.word关键字,64位机地址长度是64位,使用.dword关键字;

    .section .vtable

    .weak eclic_msip_handler
    .weak eclic_mtip_handler

使用.section 伪操作指明将接下来的代码汇编链接到名为vtable的段,根据链接脚本ld文件可知,.vtable是属于.init的子段;

使用.weak 伪操作定义一个弱属性的符号内容为空,为了使得其能够通过汇编器语法检查,但是在后续的程序中定义符号的真正实体,并且在链接阶段将空符号覆盖并链接。

例如:假设eclic_mtip_handler在整个工程中都没有定义,则向量表中此中断服务函数的PC地址为0x00000000;

    .globl vector_base
    .type vector_base, @object
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     default_intexc_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 */

这里正式开始分配向量表;

.globl伪操作定义一个全局的符号vector_base,使得链接器能够全局识别它,即一个程序文件中定义的符号能够被所有其他程序文件可见。

.type伪操作用于定义符号vector_base的类型。即将vector_base的符号定义为一个数据对象(object)。

vector_base: vector_base数据对象标签

j _start:跳转到_start标签的函数;

.align LOG_REGBYTES :接下来以4字节对齐;其中LOG_REGBYTES值 = 2;

DECLARE_INT_HANDLER     default_intexc_handler:DECLARE_INT_HANDLER这个宏为“.word”;.word伪操作将从当前PC地址处开始分配若干个字(word)的空间,每个字填充的值由分号分隔开的expression指定。空间分配的地址一定与字对齐(word aligned),这里值分配了一个word空间。

接下来就是继续分配向量表的空间,先是19个RISCV预留的中断;之后是厂商自己定义的中断;

注意,为了方便,这里分配的中断服务函数PC地址空间,需要在上面定义相同的一个弱属性的符号,否则在没有定义中断服务函数时编译会出错;

二、start代码

    .section .init

    .globl _start
    .type _start, @function

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

    /* Initialize GP and Stack Pointer SP */
    .option push
    .option norelax
    la gp, __global_pointer$
    la tp, __tls_base
    .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 */
    la t0, __STACK_SIZE
    la sp, _sp
    csrr a0, CSR_MHARTID
    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

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

    /* ===== Startup Stage 2 ===== */

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

__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:

__start:

1、关闭总中断

2、初始化GP TP SP

(2、如果没有定义SMP_CPU_CNT,则判断启动BOOT_HARTID和当前运行的核心MHARTID是否一致,一致则继续初始化,否则执行休眠;(多核才需要此代码))

3、初始化NMI和其他异常共享入口地址,MNVEC和MTVEC相同;

4、初始化ECLIC向量表基地址寄存器 MTVT = vector_base;

5、设置ECLIC非向量模式共享入口地址寄存器 MTVT2 且地址与异常入口地址不共享,(ECLIC非向量模式共享入口地址函数为"irq_entry");

6、设置异常入口地址寄存器MTVEC,函数为"early_exc_entry";

7、设置中断处理模式为 ECLIC模式,将中断模式修改为CLIC模式(ECLIC模式),不再是CLINT模式;

8、使能FPU,如果有;

9、使能Vector扩展,如果有;

10、打开mcycle和minstret的计数

(RISC-V架构定义了一个64位宽的时钟周期计数器mcycle,用于反映处理器执行了多少个时钟周期。只要处理器处于执行状态时,此计数器便会不断自增计数;RISC-V架构定义了一个64位宽的指令完成计数器minstret,用于反映处理器成功执行了多少条指令。只要处理器每成功执行完成一条指令,此计数器便会自增计数。)

(11、如果定义了SMP_CPU_CNT,且SMP_CPU_CNT>1,则判断启动BOOT_HARTID和当前允许的核心MHARTID是否一致,一致则继续初始化,否则__skip_init;(多核才需要此代码))

__init_common:

初始化内存,code section ,data section, bss section

_start_premain:

调用厂商系统初始函数(时钟配置),调用C/C++构造函数;

SystemInit、atexit(__libc_fini_array) ->_postmain_fini、__libc_init_array->_init;

__skip_init:

1、调用 __sync_harts,同步硬件线程(单核 函数为空);

2、调用_premain_init, main函数之前的初始化函数 ,初始化系统内部部件模块基地址;其他初始化(printf )

3、重新设置MTVEC 寄存器,设置MTVEC 异常入口地址为exc_entry,并设置中断处理模式为 ECLIC模式;

5、使能BPU,如果有

5、如果定义了SMP_CPU_CNT调用smp_main,否则如果定义了RTOS_RTTHREAD则调用entry,否则调用main,

5、调用_postmain_fini

6、死循环;

smp_main:(如果定义了SMP_CPU_CNT,且SMP_CPU_CNT>1)

多核处理函数;

early_exc_entry:

进入低功耗

死循环early_exc_entry;

N300 启动分析大纲:

启动第一条指令是“j _start”:伪指令 跳转到_start段开始执行,流程如下:

  • 初始化流程

    • __start

      • 关闭总中断

      • 初始化GP TP SP

      • 初始化NMI和其他异常共享入口地址,即MNVEC和MTVEC相同;

      • ECLIC向量表基地址寄存器 MTVT = vector_base

      • 设置ECLIC非向量模式共享入口地址寄存器 MTVT2 且地址与异常入口地址不共享,(ECLIC非向量模式共享入口地址函数为"irq_entry");

      • 设置异常入口地址寄存器MTVEC,函数为"early_exc_entry";

      • 设置中断处理模式为 ECLIC模式,将中断模式修改为CLIC模式(ECLIC模式),不再是CLINT模式;

      • 使能FPU,如果有;

      • 使能Vector扩展,如果有;

      • 打开mcycle和minstret的计数

    • __init_common

      • 初始化内存,code section ,data section, bss section

    • _start_premain

      • SystemInit

      • atexit(__libc_fini_array)

      • __libc_init_array

        • _init 空

    • __skip_init

      • _premain_init

        • 获取IRegion 地址

        • Exception_Init

          • 异常初始化

        • ECLIC_Init

          • mth=0 不屏蔽中断

          • 设置CLICINTCTL中LEVEL的位数

        • Trap_Init 空

      • 重新设置MTVEC 寄存器,设置MTVEC 异常入口地址为exc_entry,并设置中断处理模式为 ECLIC模式;

      • 使能BPU,如果有

      • 调用main函数

      • 死循环

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值