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模式。模式的設(shè)置由mtvec[5:0]指定,當mtvec[5:0]==6’b000011時為ECLIC模式。處理函數(shù)入口由mtvec[31:6]指定(處理函數(shù)入口地址必須為64字節(jié)對齊)。
默認模式:所有中斷,異常,NMI都由mtvec[31:6]指定的處理函數(shù)進行處理;
ECLIC模式:異常,NMI由mtvec[31:6]指定的處理函數(shù)進行處理,中斷由mtvt2寄存器指定的處理函數(shù)進行處理,并由mtvt寄存器指定中斷向量表地址
RISC-V處理器的中斷系統(tǒng)和NVIC的中斷系統(tǒng)不同,RISC-V的中斷系統(tǒng)分為NMI,異常,外部中斷三個概念。因此也分由不同的向量寄存器設(shè)置入口地址。由于risc-v的中斷和異常都不進行自動現(xiàn)場保護,所以需要軟件手動進行現(xiàn)場保護處理,因此無法直接使用C函數(shù)響應中斷,需要一個基于匯編的入口函數(shù)進行處理,在匯編入口函數(shù)中再對C函數(shù)進行調(diào)用。
GD32VF103在ECLIC中斷模式中,mtvec用于保存NMI和異常入口函數(shù)地址,不會進行自動的向量表調(diào)用。需要由入口函數(shù)進行轉(zhuǎn)發(fā)處理。在官方代碼中,響應函數(shù)為entry.S中的trap_entry函數(shù)。
mtvt2用于保存中斷響應入口函數(shù)地址,處理函數(shù)位于entry.S中的irq_entry,函數(shù)mtvt保存向量表地址,mtvt2寄存器的irq_entry函數(shù)和mtvt寄存器中的向量表共同組成一個two-stage的中斷向量表系統(tǒng),irq_entry中對現(xiàn)場進行保護,并觸發(fā)ECLIC調(diào)用中斷向量表。
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
*/
//中斷處理函數(shù)定義
#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模式。模式的設(shè)置由mtvec[5:0]指定,
//當mtvec[5:0]==6’b000011時為ECLIC模式。處理函數(shù)入口由mtvec[31:6]指定(處理函數(shù)入口地址必須為64字節(jié)對齊)。
//默認模式:所有中斷,異常,NMI都由mtvec[31:6]指定的處理函數(shù)進行處理;
//ECLIC模式:異常,NMI由mtvec[31:6]指定的處理函數(shù)進行處理,中斷由mtvt2寄存器指定的處理函數(shù)進行處理,并由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 */
// 關(guān)閉所有中斷
csrc CSR_MSTATUS, MSTATUS_MIE
/* Jump to logical address first to ensure correct operation of RAM region */
//把_start地址載入到a0,根據(jù)啟動位置的不同,_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
//跳轉(zhuǎn)到a0所存的地址
jr a0
_start0800:
/* Initialize GP and Stack Pointer SP */
.option push //保存編譯設(shè)置
.option norelax //禁用相對尋址
//設(shè)置全局變量指針
la gp, __global_pointer$ //將標簽__global_pointer$所處的地址賦值給gp寄存器
//標簽__global_pointer$在鏈接腳本中定義,見鏈接腳本__global_pointer$標簽
.option pop
//設(shè)置堆棧指針
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中斷向量表,這里設(shè)置成和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用于保存中斷響應入口函數(shù)地址
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處理器在程序執(zhí)行過程中,一旦遇到異?;蛘咧袛啵瑒t終止當前程序流,處理器被強行跳轉(zhuǎn)到一
//個新的PC地址,該地址由mtvec寄存器指定。設(shè)置mtvec寄存器的值,使其指向異常處理函數(shù)入口
//mtvec用于保存NMI和異常入口函數(shù)地址
la t0, exc_entry
csrw CSR_MTVEC, t0
/* Set the interrupt processing mode to ECLIC mode */
//將中斷處理模式設(shè)置為ECLIC模式,默認模式和ECLIC模式。模式的設(shè)置由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中執(zhí)行,那么直接跳轉(zhuǎn)到后面數(shù)字標簽2所在的代碼執(zhí)行
//如果不相同,則意味著代碼需要從Flash中上載至_ilm中執(zhí)行,因此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標簽的地址,判斷其是否相等,如果相等
//則直接跳轉(zhuǎn)到后面數(shù)字標簽2所在的位置
la a2, _eilm //將_eilm所處我地址賦值給a2寄存器
//標簽_eilm在鏈接腳本中定義,見鏈接腳本_eilm標簽
//通過一個循環(huán),將指令從Flash中搬到ITCM中
bgeu a1, a2, 2f//如果_ilm標簽地址比_eilm標簽地址還大,屬于不正常的配置
//如果放棄搬運,直接跳轉(zhuǎn)到后面數(shù)字標簽2所在的位置
1:
/* Load code section if necessary */
lw t0, (a0) //從地址指針a0所在的位置(Flash中)讀取32位數(shù)
sw t0, (a1) //將讀取的32位數(shù)寫入地址指針a1所在的位置(_ilm中)
addi a0, a0, 4 //將地址指針a0寄存器加4(即32位)
addi a1, a1, 4 //將地址指針a1寄存器加4(即32位)
bltu a1, a2, 1b //跳回之前數(shù)字標簽1所在的位置
2:
/* Load data section */
//使用與上述相同的原理,通過一個循環(huán),將數(shù)據(jù)從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
//此處通過一個循環(huán)來初始化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
*/
//系統(tǒng)初始化,主要是時鐘初始化
call SystemInit
/* Call global constructors */
la a0, __libc_fini_array //將標簽__libc_fini_array的值賦值給a0作為函數(shù)參數(shù)
call atexit //調(diào)用atexit函數(shù)
/* Call C/C++ constructor start up code */
call __libc_init_array //調(diào)用__libc_init_array
//上述 __libc_fini_array、atexit和__libc_init_array函數(shù)都是Newlib C運行庫的特殊庫函數(shù),用于處理一些C/C++程序中
//的全局性的構(gòu)造和析構(gòu)函數(shù)。
//__libc_init_array函數(shù)會調(diào)用一個名為_init的函數(shù)
/* do pre-init steps before main */
call _premain_init
/* ===== Call Main Function ===== */
/* argc = argv = 0 */
//函數(shù)調(diào)用時由a0和a1寄存器傳遞參數(shù)
li a0, 0
li a1, 0
#ifdef RTOS_RTTHREAD
// Call entry function when using RT-Thread
call entry //調(diào)用entry函數(shù),開始執(zhí)行entry函數(shù)
#else
call main //調(diào)用main函數(shù),開始執(zhí)行main函數(shù)
#endif
/* do post-main steps after main */
call _postmain_fini
1:
j 1b //最后死循環(huán),程序理論上不可能執(zhí)行到此處
(1)__libc_init_array函數(shù)會調(diào)用一個名為_init的函數(shù),該函數(shù)位于:freertos_GD32VF103/nuclei_sdk/SoC/gd32vf103/Common/Source/system_gd32vf103.c
_init函數(shù)源碼:
void _init(void)
{
/* Don't put any code here, please use _premain_init now */
//該函數(shù)已經(jīng)不在使用,使用_premain_init()函數(shù)
}
(2)由上述源碼可知,_init函數(shù)已經(jīng)不在使用,使用_premain_init()函數(shù),_premain_init()函數(shù)源碼:
void _premain_init(void)
{
/* TODO: Add your own initialization code here, called before main */
//用來計算當前運行頻率
SystemCoreClock = get_cpu_freq();
/* configure USART */
//調(diào)用gd_com_init()函數(shù)對UART模塊進行設(shè)計,串口打印信息就是要對uart進行初始化
gd_com_init(SOC_DEBUG_UART);
/* Display banner after UART initialized */
//UART初始化以后打印相關(guān)信息,有興趣的可以進入函數(shù)看看打印了哪些內(nèi)容
SystemBannerPrint();
/* Initialize exception default handlers */
//初始化異常處理程序
Exception_Init();
/* ECLIC initialization, mainly MTH and NLBIT */
//ECLIC初始化,主要是MTH和NLBIT
ECLIC_Init();
}
(2-1)get_cpu_freq()函數(shù)源碼
uint32_t get_cpu_freq()
{
uint32_t cpu_freq;
// warm up
measure_cpu_freq(1);
// measure for real
//調(diào)用measure_cpu_freq()函數(shù)
cpu_freq = measure_cpu_freq(100);
return cpu_freq;
}
調(diào)用measure_cpu_freq()函數(shù),measure_cpu_freq()函數(shù)源碼:
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得到當前時鐘周期,并作為初始計數(shù)值
} while (start_mtime == tmp); //不斷觀察MTIME計數(shù)器并將其值作為初始化時間值
do {
delta_mtime = (uint32_t)SysTimer_GetLoadValue() - start_mtime;
//通過讀取CSR寄存器MCYCLE得到當前時鐘周期,并與初始計數(shù)值相減得到這段時間消耗的時鐘周期
delta_mcycle = __RV_CSR_READ(CSR_MCYCLE) - start_mcycle;
} while (delta_mtime < n);
//MTIME計數(shù)器的頻率是常開域的參考頻率,Core的運行頻率與CSR寄存器MCYCLE的值一致
//通過MCYCLE和MTIME的相對關(guān)系計算出當前Core的時鐘頻率
return (delta_mcycle / delta_mtime) * mtime_freq
+ ((delta_mcycle % delta_mtime) * mtime_freq) / delta_mtime;
}
(3)調(diào)用gd_com_init()函數(shù)對UART模塊進行設(shè)計,串口打印信息就是要對uart進行初始化,有興趣的同學可以自行查看
(4) SystemBannerPrint(),UART初始化以后打印相關(guān)信息,有興趣的可以進入函數(shù)看看打印了哪些內(nèi)容 ;
(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 */
//根據(jù)宏定義更改堆棧指針,分配20個單字(40位)或者14個單字(28位)空間用于保存寄存器
#ifndef __riscv_32e
addi sp, sp, -20*REGBYTES
#else
addi sp, sp, -14*REGBYTES
#endif /* __riscv_32e */
//保存ABI定義的“調(diào)用者應存儲的寄存器(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定義的“調(diào)用者應存儲的寄存器(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++語法中定義的一種屬性,一旦有具體的“非弱”性質(zhì)同名函數(shù)存在/將會覆蓋此函數(shù)
.weak exc_entry
exc_entry:
//保存相應的狀態(tài)寄存器
/* 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和棧指針作為參數(shù)傳給core_exception_handler()函數(shù)
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
*/
//調(diào)用core_exception_handler()函數(shù)
call core_exception_handler
//恢復相應的狀態(tài)寄存器
/* Restore the necessary CSR registers */
RESTORE_CSR_CONTEXT
/* Restore the caller saving registers (context) */
RESTORE_CONTEXT
/* Return to regular code */
//調(diào)用從異常模式返回
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++語法中定義的一種屬性,一旦有具體的“非弱”性質(zhì)同名函數(shù)存在
//將會覆蓋此函數(shù)
.weak irq_entry
/* This label will be set to MTVT2 register */
irq_entry:
////保存相應的狀態(tài)寄存器
/* 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
//恢復相應的狀態(tài)寄存器
/* Restore the necessary CSR registers */
RESTORE_CSR_CONTEXT
/* Restore the caller saving registers (context) */
RESTORE_CONTEXT
/* Return to regular code */
//調(diào)用從中斷模式返回
mret
/* Default Handler for Exceptions / Interrupts */
.global default_intexc_handler
.weak default_intexc_handler
Undef_Handler:
default_intexc_handler:
1: //數(shù)字標簽
j 1b //跳轉(zhuǎn)回標簽1處,因此會成為死循環(huán)