1.2 nuclei sdk gd32vf03 启动文件分析

gd32vf103 启动文件分析

/**
 * Reset Handler called on controller reset
 */
_start:
    /* ===== Startup Stage 1 ===== */
    /* csrc是伪指令
     * csrc csr rs -> csrrc x0, csr, rs : Clear bits in CSR
     * csrrc指令格式 csrrc rd, csr, rs1
     * CSRRC(CSR 中的原子读取和清除位)指令读取 CSR 的值,将值零扩展到 XLEN
     * 位,并将其写入整数寄存器 rd。 整数寄存器 rs1 中的初始值被视为位掩码,
     * 指定要在 CSR 中清除的位位置。 如果 CSR 位可写,rs1 中的任何高位都会导致
     * CSR 中的相应位被清除。 CSR 中的其他位不受影响。 
     * MSTATUS_MIE MIE bit 
     * machine状态下的全局中断控制位 1:enable 0: disable
     * 这里是关闭中断
     */ 
    /* Disable Global Interrupt */
    csrc CSR_MSTATUS, MSTATUS_MIE
    /* Jump to logical address first to ensure correct operation of RAM region  */
    /* 如果是flash启动的话,_start地址是 0x0800xxxx 
     * 如果是ram启动,__start地址是0x2000xxxx
     */
    la      a0, _start
    li      a1, 1
    /* 这里的值是 1<<29 = 0x20000000 就是ram的地址*/
    slli    a1, a1, 29
    /* 如果_start大于0x20000000 则说明是从ram启动
     * 这里跳到_start0800去执行
     */
    bleu    a1, a0, _start0800
    /* 0x20000000>>2 = 0x08000000 这里对应flash的基地址 */ 
    srli    a1, a1, 2
    /* 如果_start大于0x08000000,则说明是从flash启动
     * 同样跳到_start0800去执行
     */
    bleu    a1, a0, _start0800
    
    /* 走到这里的话,说明_start地址是小于0x08000000的
     * 这是有问题的,所以强制执行 0x08000000+_start0800
     * 也就是定位到flash中
     */
    la      a0, _start0800
    add     a0, a0, a1
    jr      a0  
    
    
_start0800:
    /* Initialize GP and Stack Pointer SP */
    .option push
    .option norelax
    /* 将标签__global_pointer$所处的地址赋值给gp寄存器
     * _global_pointer在链接脚本中定义参见链接脚本
     */
    la gp, __global_pointer$

    .option pop 
    /* 设置堆栈指针
     * _sp在链接脚本中定义
     */
    la sp, _sp 

    /*  
     * Set the the NMI base mnvec to share
     * with mtvec by setting CSR_MMISC_CTL
     * bit 9 NMI_CAUSE_FFF to 1
     */ 
    /* #define MMISC_CTL_NMI_CAUSE_FFF     (1<<9)
     * #define CSR_MMISC_CTL           0x7D0
     * mmisc_ctl 自定义寄存器用于控制NMI的处理程序地址入口
     * bit 9置1后,mnvec 的值与 mtvec 一致,NMI 的 mcause.EXCCODE 为
     * 0xfff
     * mnvec 寄存器用于配置 NMI 的入口地址
     * mtvec 寄存器用于配置中断和异常处理程序的入口地址
     */
    li t0, MMISC_CTL_NMI_CAUSE_FFF
    csrs CSR_MMISC_CTL, t0

    /*  
     * Intialize ECLIC vector interrupt
     * base address mtvt to vector_base
     */  
    /* mtvt ECLIC 中断向量表的基地址 
     * 填充基地址
     */
    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.
     */
    /* 自定义
     * mtvt2 用于指定 ECLIC 非向量模式的中断 common-code 入口地址
     * bit2~bit31 用于存放common-code入口地址 所以改地址需要4bytes对齐
     * bit0:
     *  0: 入口地址有mtvec决定
     *  1: 入口地址就是设置的值
     */
    la t0, irq_entry
    csrw CSR_MTVT2, t0
    csrs CSR_MTVT2, 0x1 
    
    /*
     * Set Exception Entry MTVEC to exc_entry
     * Due to settings above, Exception and NMI
     * will share common entry.
     */
    /* 异常处理程序入口地址
     * 同样要4bytes对齐
     */
    la t0, exc_entry
    csrw CSR_MTVEC, t0
    
    /* Set the interrupt processing mode to ECLIC mode */
    /* mtvee中bit0~bit5 值为3的时候,中断处理模式为ECLIC中断模式
     * 其他值的话则是 默认中断模式
     */
    li t0, 0x3f
    csrc CSR_MTVEC, t0
    csrs CSR_MTVEC, 0x3
    
    /* mtvc中记录了中断向量表的地址
     * 都知道有向量中断和非向量中断,两者的区别这些不多说
     * 下面通过寄存器说一下两者之前的区别和联系
     * 1. 无论中断是向量模式还是非向量模式,硬件都会通过查询中断向量表中的
     *    地址跳到其对应的中断服务程序中。
     * 2. mtvec 中记录了异常入口地址
     *    mtvt2 中记录了非向量中断入口地址
     *    这2个地址的区别?
     *    mtvt2中有个有个bit0
     *    值为0的时候,非向量中断入口地址是mtvec中的地址
     *    这个是异常和中断使用一个入口地址
     *    值为1的时候,非向量中断入口地址是mtvt2中记录的地址
     *    这个时候,非向量中断的入口地址是mtvt2,而异常的入口地址是mtevc
     *    异常和中断的入口地址是分开的
     * 3. 怎么设置使用向量中断和非向量中断?
     *    ECLIC的每个中断源均可以设置成向量或者非向量处理
     *   (通过寄存器clicintattr[i]的shv域)
     *   3.1 如果被配置成为向量处理模式 (clicintattr[i].shv==1),
     *        则该中断被处理器内核响应后,处理器直接跳入该中断的向量入口
     *       (Vector Table Entry)存储的目标地址
     *   3.2 如果被配置成为非向量处理模式 (clicintattr[i].shv==0),
     *       则该中断被处理器内核响应后,处理器直接跳入所有中断共享的入口
     *       地址 
     * 默认是非向量中断
     */

/* __riscv_flen没有定义,所以这里不会执行 */
#ifdef __riscv_flen
    /* Enable FPU */
    li t0, MSTATUS_FS
    csrs mstatus, t0
    csrw fcsr, x0
#endif

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

    /* ===== Startup Stage 3 ===== */
    /*
     * Load code section from FLASH to ILM
     * when code LMA is different with VMA
     */
    /* LMA: 内存装载地址
     *      比如程序原来是在flash中,如果可以在flash中执行,那么LMA
     *      就是flash地址。如果需要搬运到ram中运行,那么LMA就是ram地址
     * VMA: 虚拟内存地址
     *      一般来说启用了MMU之后,才有虚拟地址和实地址。
     *      这里不考虑MMU,可以认为就是实际运行的地址
     * _ilm_lma 和 _ilm 是在链接脚本中定义的
     * ld文件:
     * MEMORY
     * {
     *      flash (rxai!w) : ORIGIN = __ROM_BASE, LENGTH = __ROM_SIZE
     *      ram (wxa!ri) : ORIGIN = __RAM_BASE, LENGTH = __RAM_SIZE
     * }
     * .ilalign         :
     * {
     *      . = ALIGN(4);
     *   /* Create a section label as _ilm_lma which located at flash */
     *      PROVIDE( _ilm_lma = . );
     * } >flash AT>flash
     *
     * .ialign         :
     * {
     *  /* Create a section label as _ilm which located at flash */
     *      PROVIDE( _ilm = . );
     * } >flash AT>flash
     * 语法中AT前的一个flash表示该段的运行地址,AT后的flash表示该段的物
     * 理地址 
     * 注意如果后面没有设置>ram
     * 则说明后面的段都是在flash中的
     */
    /* 可以看到都是在flash中,所以_ilm_lma和_ilm值一样
     * 跳到下一个2执行
     */
    la a0, _ilm_lma
    la a1, _ilm
    /* If the ILM phy-address same as the logic-address, then quit */
    beq a0, a1, 2f
    la a2, _eilm
    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 */
    /* _data_lma是在flash中的
     * _data和_edata是在ram中
     * a1 < a2的话,说明data段中有数据
     */
    la a0, _data_lma
    la a1, _data
    la a2, _edata
    bgeu a1, a2, 2f
1:
    /* 下面将data段从flash中拷贝到ram */
    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:
    /* 清空bss段 */
    sw zero, (a0)
    addi a0, a0, 4
    bltu a0, a1, 1b
2:
    /*
     * Call vendor defined SystemInit to
     * initialize the micro-controller system
     */
    /* 调用C函数 SystemInit 
     * 主要是初始化时钟
     */
    call SystemInit

    /*__libc_fini_array 和 __libc_init_array 是库里面定义的
     * 主要作用就是 执行fini_array段和init_aray段中的函数
     */
    /* Call global constructors */
    la a0, __libc_fini_array
    call atexit
    /* Call C/C++ constructor start up code */
    call __libc_init_array

    /* _premain_init 这里主要是初始化 gd32vf103的外设
     * 比如 gpio i2c uart等等
     */
    /* do pre-init steps before main */
    call _premain_init
    /* 默认main函数无参数*/
    /* ===== Call Main Function  ===== */
    /* argc = argv = 0 */
    li a0, 0
    li a1, 0

/* 如果编译RTT,则入口函数是enrty
 * 非RTT,入口函数是main
 */
#ifdef RTOS_RTTHREAD
    // Call entry function when using RT-Thread
    call entry
#else
    call main
#endif
    /* 从main函数退出的话,则执行_postmain_fini */
    /* do post-main steps after main */
    call _postmain_fini

/* 跳到清bss 重新执行 */
1:
    j 1b

以上汇编代码可以借鉴
https://www.nucleisys.com/product/n100/6_sdk/

  • 1
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

dianlong_lee

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值