GD32VF103启动流程分析

1 *.S文件分析

1.1 文件位置

(1)下载NucleiStudio,新建rt_thread工程,工程名为freertos_GD32VF103
(2)启动文件为:freertos_GD32VF103/nuclei_sdk/SoC/gd32vf103/Common/Source/GCC/startup_gd32vf103.S

1.2 中断异常背景知识

GD32VF103的中断控制器具有两种模式:默认模式和ECLIC模式。模式的设置由mtvec[5:0]指定,当mtvec[5:0]==6’b000011时为ECLIC模式。处理函数入口由mtvec[31:6]指定(处理函数入口地址必须为64字节对齐)。

默认模式:所有中断,异常,NMI都由mtvec[31:6]指定的处理函数进行处理;
ECLIC模式:异常,NMI由mtvec[31:6]指定的处理函数进行处理,中断由mtvt2寄存器指定的处理函数进行处理,并由mtvt寄存器指定中断向量表地址

RISC-V处理器的中断系统和NVIC的中断系统不同,RISC-V的中断系统分为NMI,异常,外部中断三个概念。因此也分由不同的向量寄存器设置入口地址。由于risc-v的中断和异常都不进行自动现场保护,所以需要软件手动进行现场保护处理,因此无法直接使用C函数响应中断,需要一个基于汇编的入口函数进行处理,在汇编入口函数中再对C函数进行调用。

GD32VF103在ECLIC中断模式中,mtvec用于保存NMI和异常入口函数地址,不会进行自动的向量表调用。需要由入口函数进行转发处理。在官方代码中,响应函数为entry.S中的trap_entry函数。

mtvt2用于保存中断响应入口函数地址,处理函数位于entry.S中的irq_entry,函数mtvt保存向量表地址,mtvt2寄存器的irq_entry函数和mtvt寄存器中的向量表共同组成一个two-stage的中断向量表系统,irq_entry中对现场进行保护,并触发ECLIC调用中断向量表。

GD32VF103 ECLIC的中断向量寄存器

寄存器 全名 说明
mtvec Machine Trap-Vector Base-Address Register 用于配置中断和异常处理程序的入口地址。一般用于处理NMI和异常中断
mtvt ECLIC Interrupt Vector Table Base Address 用于保存ECLIC中断向量表的基地址,此基地址至少为64byte对齐。
mtvt2 ECLIC non-vectored interrupt handler address register 用于指定ECLIC非向量模式的中断common-code入口地址。

2 源码分析

/*
 * Copyright (c) 2019 Nuclei Limited. All rights reserved.
 *
 * SPDX-License-Identifier: Apache-2.0
 *
 * Licensed under the Apache License, Version 2.0 (the License); you may
 * not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an AS IS BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
/******************************************************************************
 * \file     startup_gd32vf103.S
 * \brief    NMSIS Nuclei N/NX Class Core based Core Device Startup File for
 *  Device gd32vf103
 * \version  V1.00
 * \date     21 Nov 2019
 *
 *
 ******************************************************************************/

#include "riscv_encoding.h"

.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

    /*
     * Put the interrupt vectors in this section according to the run mode:
     * FlashXIP: .vtable
     * ILM: .vtable
     * Flash: .vtable_ilm
     */
    //中断处理函数定义
#if defined(DOWNLOAD_MODE) && (DOWNLOAD_MODE == DOWNLOAD_MODE_FLASH)
    .section .vtable_ilm
#else
    .section .vtable
#endif

    .weak  eclic_msip_handler
    .weak  eclic_mtip_handler
    .weak  eclic_bwei_handler
    .weak  eclic_pmovi_handler
    .weak  WWDGT_IRQHandler
    .weak  LVD_IRQHandler
    .weak  TAMPER_IRQHandler
    .weak  RTC_IRQHandler
    .weak  FMC_IRQHandler
    .weak  RCU_IRQHandler
    .weak  EXTI0_IRQHandler
    .weak  EXTI1_IRQHandler
    .weak  EXTI2_IRQHandler
    .weak  EXTI3_IRQHandler
    .weak  EXTI4_IRQHandler
    .weak  DMA0_Channel0_IRQHandler
    .weak  DMA0_Channel1_IRQHandler
    .weak  DMA0_Channel2_IRQHandler
    .weak  DMA0_Channel3_IRQHandler
    .weak  DMA0_Channel4_IRQHandler
    .weak  DMA0_Channel5_IRQHandler
    .weak  DMA0_Channel6_IRQHandler
    .weak  ADC0_1_IRQHandler
    .weak  CAN0_TX_IRQHandler
    .weak  CAN0_RX0_IRQHandler
    .weak  CAN0_RX1_IRQHandler
    .weak  CAN0_EWMC_IRQHandler
    .weak  EXTI5_9_IRQHandler
    .weak  TIMER0_BRK_IRQHandler
    .weak  TIMER0_UP_IRQHandler
    .weak  TIMER0_TRG_CMT_IRQHandler
    .weak  TIMER0_Channel_IRQHandler
    .weak  TIMER1_IRQHandler
    .weak  TIMER2_IRQHandler
    .weak  TIMER3_IRQHandler
    .weak  I2C0_EV_IRQHandler
    .weak  I2C0_ER_IRQHandler
    .weak  I2C1_EV_IRQHandler
    .weak  I2C1_ER_IRQHandler
    .weak  SPI0_IRQHandler
    .weak  SPI1_IRQHandler
    .weak  USART0_IRQHandler
    .weak  USART1_IRQHandler
    .weak  USART2_IRQHandler
    .weak  EXTI10_15_IRQHandler
    .weak  RTC_Alarm_IRQHandler
    .weak  USBFS_WKUP_IRQHandler
    .weak  EXMC_IRQHandler
    .weak  TIMER4_IRQHandler
    .weak  SPI2_IRQHandler
    .weak  UART3_IRQHandler
    .weak  UART4_IRQHandler
    .weak  TIMER5_IRQHandler
    .weak  TIMER6_IRQHandler
    .weak  DMA1_Channel0_IRQHandler
    .weak  DMA1_Channel1_IRQHandler
    .weak  DMA1_Channel2_IRQHandler
    .weak  DMA1_Channel3_IRQHandler
    .weak  DMA1_Channel4_IRQHandler
    .weak  CAN1_TX_IRQHandler
    .weak  CAN1_RX0_IRQHandler
    .weak  CAN1_RX1_IRQHandler
    .weak  CAN1_EWMC_IRQHandler
    .weak  USBFS_IRQHandler

//中断向量表定义中断向量表vector_base被放置在.init段的首部从flash地址0x08000000开始。
//GD32VF103的中断向量表实际是由ECLIC控制器CSR寄存器中的mtvec、mtvt、mtvt2寄存器指定
//GD32VF103的中断控制器具有两种模式:默认模式和ECLIC模式。模式的设置由mtvec[5:0]指定,
//当mtvec[5:0]==6’b000011时为ECLIC模式。处理函数入口由mtvec[31:6]指定(处理函数入口地址必须为64字节对齐)。
//默认模式:所有中断,异常,NMI都由mtvec[31:6]指定的处理函数进行处理;
//ECLIC模式:异常,NMI由mtvec[31:6]指定的处理函数进行处理,中断由mtvt2寄存器指定的处理函数进行处理,并由mtvt寄存器指定中断向量表地址
    .globl vector_base
vector_base:
#if defined(DOWNLOAD_MODE) && (DOWNLOAD_MODE != DOWNLOAD_MODE_FLASH)
    j _start                                                /* 0: Reserved, Jump to _start when reset for ILM/FlashXIP mode.*/
    .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 Flash download mode */
#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     eclic_bwei_handler              /* 17: Bus Error interrupt */
    DECLARE_INT_HANDLER     eclic_pmovi_handler             /* 18: Performance Monitor */

    DECLARE_INT_HANDLER     WWDGT_IRQHandler
    DECLARE_INT_HANDLER     LVD_IRQHandler
    DECLARE_INT_HANDLER     TAMPER_IRQHandler
    DECLARE_INT_HANDLER     RTC_IRQHandler
    DECLARE_INT_HANDLER     FMC_IRQHandler
    DECLARE_INT_HANDLER     RCU_IRQHandler
    DECLARE_INT_HANDLER     EXTI0_IRQHandler
    DECLARE_INT_HANDLER     EXTI1_IRQHandler
    DECLARE_INT_HANDLER     EXTI2_IRQHandler
    DECLARE_INT_HANDLER     EXTI3_IRQHandler
    DECLARE_INT_HANDLER     EXTI4_IRQHandler
    DECLARE_INT_HANDLER     DMA0_Channel0_IRQHandler
    DECLARE_INT_HANDLER     DMA0_Channel1_IRQHandler
    DECLARE_INT_HANDLER     DMA0_Channel2_IRQHandler
    DECLARE_INT_HANDLER     DMA0_Channel3_IRQHandler
    DECLARE_INT_HANDLER     DMA0_Channel4_IRQHandler
    DECLARE_INT_HANDLER     DMA0_Channel5_IRQHandler
    DECLARE_INT_HANDLER     DMA0_Channel6_IRQHandler
    DECLARE_INT_HANDLER     ADC0_1_IRQHandler
    DECLARE_INT_HANDLER     CAN0_TX_IRQHandler
    DECLARE_INT_HANDLER     CAN0_RX0_IRQHandler
    DECLARE_INT_HANDLER     CAN0_RX1_IRQHandler
    DECLARE_INT_HANDLER     CAN0_EWMC_IRQHandler
    DECLARE_INT_HANDLER     EXTI5_9_IRQHandler
    DECLARE_INT_HANDLER     TIMER0_BRK_IRQHandler
    DECLARE_INT_HANDLER     TIMER0_UP_IRQHandler
    DECLARE_INT_HANDLER     TIMER0_TRG_CMT_IRQHandler
    DECLARE_INT_HANDLER     TIMER0_Channel_IRQHandler
    DECLARE_INT_HANDLER     TIMER1_IRQHandler
    DECLARE_INT_HANDLER     TIMER2_IRQHandler
    DECLARE_INT_HANDLER     TIMER3_IRQHandler
    DECLARE_INT_HANDLER     I2C0_EV_IRQHandler
    DECLARE_INT_HANDLER     I2C0_ER_IRQHandler
    DECLARE_INT_HANDLER     I2C1_EV_IRQHandler
    DECLARE_INT_HANDLER     I2C1_ER_IRQHandler
    DECLARE_INT_HANDLER     SPI0_IRQHandler
    DECLARE_INT_HANDLER     SPI1_IRQHandler
    DECLARE_INT_HANDLER     USART0_IRQHandler
    DECLARE_INT_HANDLER     USART1_IRQHandler
    DECLARE_INT_HANDLER     USART2_IRQHandler
    DECLARE_INT_HANDLER     EXTI10_15_IRQHandler
    DECLARE_INT_HANDLER     RTC_Alarm_IRQHandler
    DECLARE_INT_HANDLER     USBFS_WKUP_IRQHandler
    DECLARE_INT_HANDLER     default_intexc_handler
    DECLARE_INT_HANDLER     default_intexc_handler
    DECLARE_INT_HANDLER     default_intexc_handler
    DECLARE_INT_HANDLER     default_intexc_handler
    DECLARE_INT_HANDLER     default_intexc_handler
    DECLARE_INT_HANDLER     EXMC_IRQHandler
    DECLARE_INT_HANDLER     default_intexc_handler
    DECLARE_INT_HANDLER     TIMER4_IRQHandler
    DECLARE_INT_HANDLER     SPI2_IRQHandler
    DECLARE_INT_HANDLER     UART3_IRQHandler
    DECLARE_INT_HANDLER     UART4_IRQHandler
    DECLARE_INT_HANDLER     TIMER5_IRQHandler
    DECLARE_INT_HANDLER     TIMER6_IRQHandler
    DECLARE_INT_HANDLER     DMA1_Channel0_IRQHandler
    DECLARE_INT_HANDLER     DMA1_Channel1_IRQHandler
    DECLARE_INT_HANDLER     DMA1_Channel2_IRQHandler
    DECLARE_INT_HANDLER     DMA1_Channel3_IRQHandler
    DECLARE_INT_HANDLER     DMA1_Channel4_IRQHandler
    DECLARE_INT_HANDLER     default_intexc_handler
    DECLARE_INT_HANDLER     default_intexc_handler
    DECLARE_INT_HANDLER     CAN1_TX_IRQHandler
    DECLARE_INT_HANDLER     CAN1_RX0_IRQHandler
    DECLARE_INT_HANDLER     CAN1_RX1_IRQHandler
    DECLARE_INT_HANDLER     CAN1_EWMC_IRQHandler
    DECLARE_INT_HANDLER     USBFS_IRQHandler

    .section .init //指明此处section名为.init

    .globl _start //指明标签_start的属性为全局性
    .type _start,@function

/**
 * Reset Handler called on controller reset
 */
_start:
    /* ===== Startup Stage 1 ===== */
    /* Disable Global Interrupt */
    // 关闭所有中断
    csrc CSR_MSTATUS, MSTATUS_MIE
    /* Jump to logical address first to ensure correct operation of RAM region  */
    //把_start地址载入到a0,根据启动位置的不同,_start可能在ram地址中也可能在flash中
    la      a0, _start        
    li      a1, 1
    // a1 = a1 << 29  (a1=0x20000000,ram起始地址)
    slli    a1, a1, 29  
    //if (a1 <= a0) goto _start0800检测是否在ram中运行,如果在ram中运行,_start地址将会大于 0x20000000
    bleu    a1, a0, _start0800
    //a1 = a1 >> 2  (a1=0x08000000 flash起始地址)
    srli    a1, a1, 2
    //if (a1 <= a0) goto _start0800
    bleu    a1, a0, _start0800
    //a0 =_start0800 程序地址不正确
    la      a0, _start0800
   //a0 = a0+0x08000000 (把程序地址重新定位到flash中)
    add     a0, a0, a1
   //跳转到a0所存的地址
    jr      a0

_start0800:
    /* Initialize GP and Stack Pointer SP */
    .option push    //保存编译设置
    .option norelax  //禁用相对寻址
   //设置全局变量指针
    la gp, __global_pointer$ //将标签__global_pointer$所处的地址赋值给gp寄存器
                             //标签__global_pointer$在链接脚本中定义,见链接脚本__global_pointer$标签


    .option pop
    //设置堆栈指针
    la sp, _sp  //将标签_sp所处的地址赋值给sp寄存器
                //标签_sp在链接脚本中定义,见链接脚本_sp$标签

    /*
     * Set the the NMI base mnvec to share
     * with mtvec by setting CSR_MMISC_CTL
     * bit 9 NMI_CAUSE_FFF to 1
     */
    //mmisc_ctl = 0x200 ECLIC寄存器mmisc_ctl用于控制NMI中断向量表,这里设置成和mtvec一致
    li t0, MMISC_CTL_NMI_CAUSE_FFF
    csrs CSR_MMISC_CTL, t0

    /*
     * Intialize ECLIC vector interrupt
     * base address mtvt to vector_base
     */
    //保存ECLIC中断向量表的基地址,mtvt保存向量表地址
    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用于保存中断响应入口函数地址
    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.
     */
    //RISCV处理器在程序执行过程中,一旦遇到异常或者中断,则终止当前程序流,处理器被强行跳转到一    
    //个新的PC地址,该地址由mtvec寄存器指定。设置mtvec寄存器的值,使其指向异常处理函数入口
    //mtvec用于保存NMI和异常入口函数地址
    la t0, exc_entry
    csrw CSR_MTVEC, t0

    /* Set the interrupt processing mode to ECLIC mode */
    //将中断处理模式设置为ECLIC模式,默认模式和ECLIC模式。模式的设置由mtvec[5:0]指定
    li t0, 0x3f
    csrc CSR_MTVEC, t0
    csrs CSR_MTVEC, 0x3

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

#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

    //下列代码判断_ilm_lma和_ilm标签的地址是否相同
    //如果相同,则意味着代码直接从Flash中执行,那么直接跳转到后面数字标签2所在的代码执行
    //如果不相同,则意味着代码需要从Flash中上载至_ilm中执行,因此lw指令逐条将指令从Flash中读取出来,然后使用sw指令
    //逐条写入_ilm中,通过此方式完成将指令上载至_ilm中
    /* ===== Startup Stage 3 ===== */
    /*
     * Load code section from FLASH to ILM
     * when code LMA is different with VMA
     */
    la a0, _ilm_lma //将标签_ilm_lma所处的地址赋值给a0寄存器
                    //标签_ilm_lma在链接脚本中定义,见链接脚本_ilm_lma标签
    la a1, _ilm     //将标签_ilm所处的地址赋值给a1寄存器
                    //标签_ilm在链接脚本中定义,见链接脚本_ilm标签
    /* If the ILM phy-address same as the logic-address, then quit */
    beq a0, a1, 2f //a0和a1的值分别为标签_ilm_lma和_ilm标签的地址,判断其是否相等,如果相等
                   //则直接跳转到后面数字标签2所在的位置
    la a2, _eilm   //将_eilm所处我地址赋值给a2寄存器
                   //标签_eilm在链接脚本中定义,见链接脚本_eilm标签
    //通过一个循环,将指令从Flash中搬到ITCM中
    bgeu a1, a2, 2f//如果_ilm标签地址比_eilm标签地址还大,属于不正常的配置
                   //如果放弃搬运,直接跳转到后面数字标签2所在的位置

1:
    /* Load code section if necessary */
    lw t0, (a0)         //从地址指针a0所在的位置(Flash中)读取32位数
    sw t0, (a1)         //将读取的32位数写入地址指针a1所在的位置(_ilm中)
    addi a0, a0, 4      //将地址指针a0寄存器加4(即32位)
    addi a1, a1, 4      //将地址指针a1寄存器加4(即32位)
    bltu a1, a2, 1b     //跳回之前数字标签1所在的位置
2:
    /* Load data section */
    //使用与上述相同的原理,通过一个循环,将数据从FLASH中搬运到DTCM中
    la a0, _data_lma
    la a1, _data
    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 */
    //BSS段是链接器预留的未初始化变量所处的地址段,引导程序必须对其初始化为0
    //此处通过一个循环来初始化BSS段
    la a0, __bss_start
    la a1, _end
    bgeu a0, a1, 2f
1:
    sw zero, (a0)
    addi a0, a0, 4
    bltu a0, a1, 1b
2:

    /*
     * Call vendor defined SystemInit to
     * initialize the micro-controller system
     */
     //系统初始化,主要是时钟初始化
    call SystemInit

    /* Call global constructors */
    la a0, __libc_fini_array    //将标签__libc_fini_array的值赋值给a0作为函数参数
    call atexit                 //调用atexit函数
    /* Call C/C++ constructor start up code */
    call __libc_init_array      //调用__libc_init_array

    //上述 __libc_fini_array、atexit和__libc_init_array函数都是Newlib C运行库的特殊库函数,用于处理一些C/C++程序中
    //的全局性的构造和析构函数。
    //__libc_init_array函数会调用一个名为_init的函数

    /* do pre-init steps before main */
    call _premain_init
    /* ===== Call Main Function  ===== */
    /* argc = argv = 0 */
    //函数调用时由a0和a1寄存器传递参数
    li a0, 0
    li a1, 0

#ifdef RTOS_RTTHREAD
    // Call entry function when using RT-Thread
    call entry //调用entry函数,开始执行entry函数
#else
    call main //调用main函数,开始执行main函数
#endif
    /* do post-main steps after main */
    call _postmain_fini

1:
    j 1b //最后死循环,程序理论上不可能执行到此处

(1)__libc_init_array函数会调用一个名为_init的函数,该函数位于:freertos_GD32VF103/nuclei_sdk/SoC/gd32vf103/Common/Source/system_gd32vf103.c
_init函数源码:

void _init(void)
{
    /* Don't put any code here, please use _premain_init now */
    //该函数已经不在使用,使用_premain_init()函数
}

(2)由上述源码可知,_init函数已经不在使用,使用_premain_init()函数,_premain_init()函数源码:

void _premain_init(void)
{
    /* TODO: Add your own initialization code here, called before main */
    //用来计算当前运行频率
    SystemCoreClock = get_cpu_freq();
    /* configure USART */
    //调用gd_com_init()函数对UART模块进行设计,串口打印信息就是要对uart进行初始化
    gd_com_init(SOC_DEBUG_UART);
    /* Display banner after UART initialized */
    //UART初始化以后打印相关信息,有兴趣的可以进入函数看看打印了哪些内容
    SystemBannerPrint();
    /* Initialize exception default handlers */
    //初始化异常处理程序
    Exception_Init();
    /* ECLIC initialization, mainly MTH and NLBIT */
    //ECLIC初始化,主要是MTH和NLBIT
    ECLIC_Init();
}

(2-1)get_cpu_freq()函数源码

uint32_t get_cpu_freq()
{
    uint32_t cpu_freq;

    // warm up
    measure_cpu_freq(1);
    // measure for real
    //调用measure_cpu_freq()函数
    cpu_freq = measure_cpu_freq(100);

    return cpu_freq;
}

调用measure_cpu_freq()函数,measure_cpu_freq()函数源码:

uint32_t measure_cpu_freq(uint32_t n)
{
    uint32_t start_mcycle, delta_mcycle;
    uint32_t start_mtime, delta_mtime;
    uint32_t mtime_freq = get_timer_freq();

    // Don't start measuruing until we see an mtime tick
    uint32_t tmp = (uint32_t)SysTimer_GetLoadValue();
    do {
        start_mtime = (uint32_t)SysTimer_GetLoadValue();
        start_mcycle = __RV_CSR_READ(CSR_MCYCLE); //通过读取CSR寄存器MCYCLE得到当前时钟周期,并作为初始计数值
    } while (start_mtime == tmp); //不断观察MTIME计数器并将其值作为初始化时间值

    do {
        delta_mtime = (uint32_t)SysTimer_GetLoadValue() - start_mtime;
        //通过读取CSR寄存器MCYCLE得到当前时钟周期,并与初始计数值相减得到这段时间消耗的时钟周期
        delta_mcycle = __RV_CSR_READ(CSR_MCYCLE) - start_mcycle;
    } while (delta_mtime < n);

    //MTIME计数器的频率是常开域的参考频率,Core的运行频率与CSR寄存器MCYCLE的值一致
    //通过MCYCLE和MTIME的相对关系计算出当前Core的时钟频率
    return (delta_mcycle / delta_mtime) * mtime_freq
           + ((delta_mcycle % delta_mtime) * mtime_freq) / delta_mtime;
}

(3)调用gd_com_init()函数对UART模块进行设计,串口打印信息就是要对uart进行初始化,有兴趣的同学可以自行查看
(4) SystemBannerPrint(),UART初始化以后打印相关信息,有兴趣的可以进入函数看看打印了哪些内容 ;
(5)Exception_Init(),初始化异常处理程序
(6)ECLIC_Init(),ECLIC初始化,主要是MTH和NLBIT

3 中断异常

3.1文件位置

中断异常文件:freertos_GD32VF103/nuclei_sdk/SoC/gd32vf103/Common/Source/GCC/intexc_gd32vf103.S

3.2源码分析

/*
 * Copyright (c) 2019 Nuclei Limited. All rights reserved.
 *
 * SPDX-License-Identifier: Apache-2.0
 *
 * Licensed under the Apache License, Version 2.0 (the License); you may
 * not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an AS IS BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
/******************************************************************************
 * \file     intexc_gd32vf103.S
 * \brief    NMSIS Interrupt and Exception Handling Template File
 *           for Device gd32vf103
 * \version  V1.00
 * \date     7 Jan 2020
 *
 ******************************************************************************/

#include "riscv_encoding.h"

/**
 * \brief  Global interrupt disabled
 * \details
 *  This function disable global interrupt.
 * \remarks
 *  - All the interrupt requests will be ignored by CPU.
 */
.macro DISABLE_MIE
    csrc CSR_MSTATUS, MSTATUS_MIE
.endm

/**
 * \brief  Macro for context save
 * \details
 * This macro save ABI defined caller saved registers in the stack.
 * \remarks
 * - This Macro could use to save context when you enter to interrupt
 * or exception
*/
/* Save caller registers */
.macro SAVE_CONTEXT
    /* Allocate stack space for context saving */
    //根据宏定义更改堆栈指针,分配20个单字(40位)或者14个单字(28位)空间用于保存寄存器
#ifndef __riscv_32e
    addi sp, sp, -20*REGBYTES
#else
    addi sp, sp, -14*REGBYTES
#endif /* __riscv_32e */
    //保存ABI定义的“调用者应存储的寄存器(Caller saved register)”进入堆栈
    STORE x1, 0*REGBYTES(sp)
    STORE x4, 1*REGBYTES(sp)
    STORE x5, 2*REGBYTES(sp)
    STORE x6, 3*REGBYTES(sp)
    STORE x7, 4*REGBYTES(sp)
    STORE x10, 5*REGBYTES(sp)
    STORE x11, 6*REGBYTES(sp)
    STORE x12, 7*REGBYTES(sp)
    STORE x13, 8*REGBYTES(sp)
    STORE x14, 9*REGBYTES(sp)
    STORE x15, 10*REGBYTES(sp)
#ifndef __riscv_32e
    STORE x16, 14*REGBYTES(sp)
    STORE x17, 15*REGBYTES(sp)
    STORE x28, 16*REGBYTES(sp)
    STORE x29, 17*REGBYTES(sp)
    STORE x30, 18*REGBYTES(sp)
    STORE x31, 19*REGBYTES(sp)
#endif /* __riscv_32e */
.endm

/**
 * \brief  Macro for restore caller registers
 * \details
 * This macro restore ABI defined caller saved registers from stack.
 * \remarks
 * - You could use this macro to restore context before you want return
 * from interrupt or exeception
 */
/* Restore caller registers */
//回复用于从堆栈中恢复ABI定义的“调用者应存储的寄存器(Caller saved register)”
.macro RESTORE_CONTEXT
    LOAD x1, 0*REGBYTES(sp)
    LOAD x4, 1*REGBYTES(sp)
    LOAD x5, 2*REGBYTES(sp)
    LOAD x6, 3*REGBYTES(sp)
    LOAD x7, 4*REGBYTES(sp)
    LOAD x10, 5*REGBYTES(sp)
    LOAD x11, 6*REGBYTES(sp)
    LOAD x12, 7*REGBYTES(sp)
    LOAD x13, 8*REGBYTES(sp)
    LOAD x14, 9*REGBYTES(sp)
    LOAD x15, 10*REGBYTES(sp)
#ifndef __riscv_32e
    LOAD x16, 14*REGBYTES(sp)
    LOAD x17, 15*REGBYTES(sp)
    LOAD x28, 16*REGBYTES(sp)
    LOAD x29, 17*REGBYTES(sp)
    LOAD x30, 18*REGBYTES(sp)
    LOAD x31, 19*REGBYTES(sp)

//恢复寄存器后,更改堆栈指针,回收20个单字(40位)或者14个单字(28位)空间
    /* De-allocate the stack space */
    addi sp, sp, 20*REGBYTES
#else
    /* De-allocate the stack space */
    addi sp, sp, 14*REGBYTES
#endif /* __riscv_32e */

.endm

/**
 * \brief  Macro for save necessary CSRs to stack
 * \details
 * This macro store MCAUSE, MEPC, MSUBM to stack.
 */
 //将MCAUSE, MEPC, MSUBM寄存器里面的值存入到栈中
.macro SAVE_CSR_CONTEXT
    /* Store CSR mcause to stack using pushmcause */
    csrrwi  x0, CSR_PUSHMCAUSE, 11
    /* Store CSR mepc to stack using pushmepc */
    csrrwi  x0, CSR_PUSHMEPC, 12
    /* Store CSR msub to stack using pushmsub */
    csrrwi  x0, CSR_PUSHMSUBM, 13
.endm

/**
 * \brief  Macro for restore necessary CSRs from stack
 * \details
 * This macro restore MSUBM, MEPC, MCAUSE from stack.
 */
 //将MCAUSE, MEPC, MSUBM寄存器的值从栈中恢复
.macro RESTORE_CSR_CONTEXT
    LOAD x5,  13*REGBYTES(sp)
    csrw CSR_MSUBM, x5
    LOAD x5,  12*REGBYTES(sp)
    csrw CSR_MEPC, x5
    LOAD x5,  11*REGBYTES(sp)
    csrw CSR_MCAUSE, x5
.endm

/**
 * \brief  Exception/NMI Entry
 * \details
 * This function provide common entry functions for exception/nmi.
 * \remarks
 * This function provide a default exception/nmi entry.
 * ABI defined caller save register and some CSR registers
 * to be saved before enter interrupt handler and be restored before return.
 */
.section .text.trap
/* In CLIC mode, the exeception entry must be 64bytes aligned */
.align 6
.global exc_entry
//此处exc_entry标签为“弱(weak)属性”,“弱(weak)属性”为C/C++语法中定义的一种属性,一旦有具体的“非弱”性质同名函数存在/将会覆盖此函数
.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
     */
     //传参,将mcause value和栈指针作为参数传给core_exception_handler()函数
    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
     */
     //调用core_exception_handler()函数
    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

/**
 * \brief  Non-Vector Interrupt Entry
 * \details
 * This function provide common entry functions for handling
 * non-vector interrupts
 * \remarks
 * This function provide a default non-vector interrupt entry.
 * ABI defined caller save register and some CSR registers need
 * to be saved before enter interrupt handler and be restored before return.
 */
.section      .text.irq
/* In CLIC mode, the interrupt entry must be 4bytes aligned */
.align 2
.global irq_entry
//此处irq_entry标签为“弱(weak)属性”,“弱(weak)属性”为C/C++语法中定义的一种属性,一旦有具体的“非弱”性质同名函数存在
//将会覆盖此函数
.weak irq_entry
/* This label will be set to MTVT2 register */
irq_entry:
    ////保存相应的状态寄存器
    /* Save the caller saving registers (context) */
    SAVE_CONTEXT
    /* Save the necessary CSR registers */
    SAVE_CSR_CONTEXT

    /* This special CSR read/write operation, which is actually
     * claim the CLIC to find its pending highest ID, if the ID
     * is not 0, then automatically enable the mstatus.MIE, and
     * jump to its vector-entry-label, and update the link register
     */
    //跳到中断向量表进行中断处理
    csrrw ra, CSR_JALMNXTI, ra

    /* Critical section with interrupts disabled */
    //禁止所有中断
    DISABLE_MIE

    //恢复相应的状态寄存器
    /* Restore the necessary CSR registers */
    RESTORE_CSR_CONTEXT
    /* Restore the caller saving registers (context) */
    RESTORE_CONTEXT

    /* Return to regular code */
    //调用从中断模式返回
    mret

/* Default Handler for Exceptions / Interrupts */
.global default_intexc_handler
.weak default_intexc_handler
Undef_Handler:
default_intexc_handler:
1: //数字标签
    j 1b  //跳转回标签1处,因此会成为死循环

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,847评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,208评论 1 292
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,587评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,942评论 0 205
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,332评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,587评论 1 218
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,853评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,568评论 0 198
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,273评论 1 242
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,542评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,033评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,373评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,031评论 3 236
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,073评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,830评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,628评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,537评论 2 269

推荐阅读更多精彩内容