GD32VF103启动流程分析
1 *.S文件分析
1.1 文件位置
啟動(dòng)文件為:freertos_GD32VF103/nuclei_sdk/SoC/gd32vf103/Common/Source/GCC/startup_gd32vf103.S
1.2 中斷異常背景知識(shí)
GD32VF103的中斷控制器具有兩種模式:默認(rèn)模式和ECLIC模式。模式的設(shè)置由mtvec[5:0]指定,當(dāng)mtvec[5:0]==6’b000011時(shí)為ECLIC模式。處理函數(shù)入口由mtvec[31:6]指定(處理函數(shù)入口地址必須為64字節(jié)對(duì)齊)。
默認(rèn)模式:所有中斷,異常,NMI都由mtvec[31:6]指定的處理函數(shù)進(jìn)行處理;
ECLIC模式:異常,NMI由mtvec[31:6]指定的處理函數(shù)進(jìn)行處理,中斷由mtvt2寄存器指定的處理函數(shù)進(jìn)行處理,并由mtvt寄存器指定中斷向量表地址
RISC-V處理器的中斷系統(tǒng)和NVIC的中斷系統(tǒng)不同,RISC-V的中斷系統(tǒng)分為NMI,異常,外部中斷三個(gè)概念。因此也分由不同的向量寄存器設(shè)置入口地址。由于risc-v的中斷和異常都不進(jìn)行自動(dòng)現(xiàn)場(chǎng)保護(hù),所以需要軟件手動(dòng)進(jìn)行現(xiàn)場(chǎng)保護(hù)處理,因此無(wú)法直接使用C函數(shù)響應(yīng)中斷,需要一個(gè)基于匯編的入口函數(shù)進(jìn)行處理,在匯編入口函數(shù)中再對(duì)C函數(shù)進(jìn)行調(diào)用。
GD32VF103在ECLIC中斷模式中,mtvec用于保存NMI和異常入口函數(shù)地址,不會(huì)進(jìn)行自動(dòng)的向量表調(diào)用。需要由入口函數(shù)進(jìn)行轉(zhuǎn)發(fā)處理。在官方代碼中,響應(yīng)函數(shù)為entry.S中的trap_entry函數(shù)。
mtvt2用于保存中斷響應(yīng)入口函數(shù)地址,處理函數(shù)位于entry.S中的irq_entry,函數(shù)mtvt保存向量表地址,mtvt2寄存器的irq_entry函數(shù)和mtvt寄存器中的向量表共同組成一個(gè)two-stage的中斷向量表系統(tǒng),irq_entry中對(duì)現(xiàn)場(chǎng)進(jìn)行保護(hù),并觸發(fā)ECLIC調(diào)用中斷向量表。
GD32VF103 ECLIC的中斷向量寄存器
| mtvec | Machine Trap-Vector Base-Address Register | 用于配置中斷和異常處理程序的入口地址。一般用于處理NMI和異常中斷 |
| mtvt | ECLIC Interrupt Vector Table Base Address | 用于保存ECLIC中斷向量表的基地址,此基地址至少為64byte對(duì)齊。 |
| 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開(kāi)始。 //GD32VF103的中斷向量表實(shí)際是由ECLIC控制器CSR寄存器中的mtvec、mtvt、mtvt2寄存器指定 //GD32VF103的中斷控制器具有兩種模式:默認(rèn)模式和ECLIC模式。模式的設(shè)置由mtvec[5:0]指定, //當(dāng)mtvec[5:0]==6’b000011時(shí)為ECLIC模式。處理函數(shù)入口由mtvec[31:6]指定(處理函數(shù)入口地址必須為64字節(jié)對(duì)齊)。 //默認(rèn)模式:所有中斷,異常,NMI都由mtvec[31:6]指定的處理函數(shù)進(jìn)行處理; //ECLIC模式:異常,NMI由mtvec[31:6]指定的處理函數(shù)進(jìn)行處理,中斷由mtvt2寄存器指定的處理函數(shù)進(jìn)行處理,并由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 */ #elseDECLARE_INT_HANDLER default_intexc_handler /* 0: Reserved, default handler for Flash download mode */ #endifDECLARE_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_IRQHandlerDECLARE_INT_HANDLER LVD_IRQHandlerDECLARE_INT_HANDLER TAMPER_IRQHandlerDECLARE_INT_HANDLER RTC_IRQHandlerDECLARE_INT_HANDLER FMC_IRQHandlerDECLARE_INT_HANDLER RCU_IRQHandlerDECLARE_INT_HANDLER EXTI0_IRQHandlerDECLARE_INT_HANDLER EXTI1_IRQHandlerDECLARE_INT_HANDLER EXTI2_IRQHandlerDECLARE_INT_HANDLER EXTI3_IRQHandlerDECLARE_INT_HANDLER EXTI4_IRQHandlerDECLARE_INT_HANDLER DMA0_Channel0_IRQHandlerDECLARE_INT_HANDLER DMA0_Channel1_IRQHandlerDECLARE_INT_HANDLER DMA0_Channel2_IRQHandlerDECLARE_INT_HANDLER DMA0_Channel3_IRQHandlerDECLARE_INT_HANDLER DMA0_Channel4_IRQHandlerDECLARE_INT_HANDLER DMA0_Channel5_IRQHandlerDECLARE_INT_HANDLER DMA0_Channel6_IRQHandlerDECLARE_INT_HANDLER ADC0_1_IRQHandlerDECLARE_INT_HANDLER CAN0_TX_IRQHandlerDECLARE_INT_HANDLER CAN0_RX0_IRQHandlerDECLARE_INT_HANDLER CAN0_RX1_IRQHandlerDECLARE_INT_HANDLER CAN0_EWMC_IRQHandlerDECLARE_INT_HANDLER EXTI5_9_IRQHandlerDECLARE_INT_HANDLER TIMER0_BRK_IRQHandlerDECLARE_INT_HANDLER TIMER0_UP_IRQHandlerDECLARE_INT_HANDLER TIMER0_TRG_CMT_IRQHandlerDECLARE_INT_HANDLER TIMER0_Channel_IRQHandlerDECLARE_INT_HANDLER TIMER1_IRQHandlerDECLARE_INT_HANDLER TIMER2_IRQHandlerDECLARE_INT_HANDLER TIMER3_IRQHandlerDECLARE_INT_HANDLER I2C0_EV_IRQHandlerDECLARE_INT_HANDLER I2C0_ER_IRQHandlerDECLARE_INT_HANDLER I2C1_EV_IRQHandlerDECLARE_INT_HANDLER I2C1_ER_IRQHandlerDECLARE_INT_HANDLER SPI0_IRQHandlerDECLARE_INT_HANDLER SPI1_IRQHandlerDECLARE_INT_HANDLER USART0_IRQHandlerDECLARE_INT_HANDLER USART1_IRQHandlerDECLARE_INT_HANDLER USART2_IRQHandlerDECLARE_INT_HANDLER EXTI10_15_IRQHandlerDECLARE_INT_HANDLER RTC_Alarm_IRQHandlerDECLARE_INT_HANDLER USBFS_WKUP_IRQHandlerDECLARE_INT_HANDLER default_intexc_handlerDECLARE_INT_HANDLER default_intexc_handlerDECLARE_INT_HANDLER default_intexc_handlerDECLARE_INT_HANDLER default_intexc_handlerDECLARE_INT_HANDLER default_intexc_handlerDECLARE_INT_HANDLER EXMC_IRQHandlerDECLARE_INT_HANDLER default_intexc_handlerDECLARE_INT_HANDLER TIMER4_IRQHandlerDECLARE_INT_HANDLER SPI2_IRQHandlerDECLARE_INT_HANDLER UART3_IRQHandlerDECLARE_INT_HANDLER UART4_IRQHandlerDECLARE_INT_HANDLER TIMER5_IRQHandlerDECLARE_INT_HANDLER TIMER6_IRQHandlerDECLARE_INT_HANDLER DMA1_Channel0_IRQHandlerDECLARE_INT_HANDLER DMA1_Channel1_IRQHandlerDECLARE_INT_HANDLER DMA1_Channel2_IRQHandlerDECLARE_INT_HANDLER DMA1_Channel3_IRQHandlerDECLARE_INT_HANDLER DMA1_Channel4_IRQHandlerDECLARE_INT_HANDLER default_intexc_handlerDECLARE_INT_HANDLER default_intexc_handlerDECLARE_INT_HANDLER CAN1_TX_IRQHandlerDECLARE_INT_HANDLER CAN1_RX0_IRQHandlerDECLARE_INT_HANDLER CAN1_RX1_IRQHandlerDECLARE_INT_HANDLER CAN1_EWMC_IRQHandlerDECLARE_INT_HANDLER USBFS_IRQHandler.section .init //指明此處section名為.init.globl _start //指明標(biāo)簽_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ù)啟動(dòng)位置的不同,_start可能在ram地址中也可能在flash中l(wèi)a a0, _start li a1, 1// a1 = a1 << 29 (a1=0x20000000,ram起始地址)slli a1, a1, 29 //if (a1 <= a0) goto _start0800檢測(cè)是否在ram中運(yùn)行,如果在ram中運(yùn)行,_start地址將會(huì)大于 0x20000000bleu a1, a0, _start0800//a1 = a1 >> 2 (a1=0x08000000 flash起始地址)srli a1, a1, 2//if (a1 <= a0) goto _start0800bleu 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 //禁用相對(duì)尋址//設(shè)置全局變量指針la gp, __global_pointer$ //將標(biāo)簽__global_pointer$所處的地址賦值給gp寄存器//標(biāo)簽__global_pointer$在鏈接腳本中定義,見(jiàn)鏈接腳本__global_pointer$標(biāo)簽.option pop//設(shè)置堆棧指針la sp, _sp //將標(biāo)簽_sp所處的地址賦值給sp寄存器//標(biāo)簽_sp在鏈接腳本中定義,見(jiàn)鏈接腳本_sp$標(biāo)簽/** 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_FFFcsrs CSR_MMISC_CTL, t0/** Intialize ECLIC vector interrupt* base address mtvt to vector_base*///保存ECLIC中斷向量表的基地址,mtvt保存向量表地址la t0, vector_basecsrw 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用于保存中斷響應(yīng)入口函數(shù)地址la t0, irq_entrycsrw CSR_MTVT2, t0csrs CSR_MTVT2, 0x1/** Set Exception Entry MTVEC to exc_entry* Due to settings above, Exception and NMI* will share common entry.*///RISCV處理器在程序執(zhí)行過(guò)程中,一旦遇到異常或者中斷,則終止當(dāng)前程序流,處理器被強(qiáng)行跳轉(zhuǎn)到一 //個(gè)新的PC地址,該地址由mtvec寄存器指定。設(shè)置mtvec寄存器的值,使其指向異常處理函數(shù)入口//mtvec用于保存NMI和異常入口函數(shù)地址la t0, exc_entrycsrw CSR_MTVEC, t0/* Set the interrupt processing mode to ECLIC mode *///將中斷處理模式設(shè)置為ECLIC模式,默認(rèn)模式和ECLIC模式。模式的設(shè)置由mtvec[5:0]指定li t0, 0x3fcsrc CSR_MTVEC, t0csrs CSR_MTVEC, 0x3/* ===== Startup Stage 2 ===== */#ifdef __riscv_flen/* Enable FPU */li t0, MSTATUS_FScsrs mstatus, t0csrw fcsr, x0 #endif/* Enable mcycle and minstret counter */csrci CSR_MCOUNTINHIBIT, 0x5//下列代碼判斷_ilm_lma和_ilm標(biāo)簽的地址是否相同//如果相同,則意味著代碼直接從Flash中執(zhí)行,那么直接跳轉(zhuǎn)到后面數(shù)字標(biāo)簽2所在的代碼執(zhí)行//如果不相同,則意味著代碼需要從Flash中上載至_ilm中執(zhí)行,因此lw指令逐條將指令從Flash中讀取出來(lái),然后使用sw指令//逐條寫入_ilm中,通過(guò)此方式完成將指令上載至_ilm中/* ===== Startup Stage 3 ===== *//** Load code section from FLASH to ILM* when code LMA is different with VMA*/la a0, _ilm_lma //將標(biāo)簽_ilm_lma所處的地址賦值給a0寄存器//標(biāo)簽_ilm_lma在鏈接腳本中定義,見(jiàn)鏈接腳本_ilm_lma標(biāo)簽la a1, _ilm //將標(biāo)簽_ilm所處的地址賦值給a1寄存器//標(biāo)簽_ilm在鏈接腳本中定義,見(jiàn)鏈接腳本_ilm標(biāo)簽/* If the ILM phy-address same as the logic-address, then quit */beq a0, a1, 2f //a0和a1的值分別為標(biāo)簽_ilm_lma和_ilm標(biāo)簽的地址,判斷其是否相等,如果相等//則直接跳轉(zhuǎn)到后面數(shù)字標(biāo)簽2所在的位置la a2, _eilm //將_eilm所處我地址賦值給a2寄存器//標(biāo)簽_eilm在鏈接腳本中定義,見(jiàn)鏈接腳本_eilm標(biāo)簽//通過(guò)一個(gè)循環(huán),將指令從Flash中搬到ITCM中bgeu a1, a2, 2f//如果_ilm標(biāo)簽地址比_eilm標(biāo)簽地址還大,屬于不正常的配置//如果放棄搬運(yùn),直接跳轉(zhuǎn)到后面數(shù)字標(biāo)簽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ù)字標(biāo)簽1所在的位置 2:/* Load data section *///使用與上述相同的原理,通過(guò)一個(gè)循環(huán),將數(shù)據(jù)從FLASH中搬運(yùn)到DTCM中l(wèi)a a0, _data_lmala a1, _datala a2, _edatabgeu a1, a2, 2f 1:lw t0, (a0)sw t0, (a1)addi a0, a0, 4addi a1, a1, 4bltu a1, a2, 1b 2:/* Clear bss section *///BSS段是鏈接器預(yù)留的未初始化變量所處的地址段,引導(dǎo)程序必須對(duì)其初始化為0//此處通過(guò)一個(gè)循環(huán)來(lái)初始化BSS段la a0, __bss_startla a1, _endbgeu a0, a1, 2f 1:sw zero, (a0)addi a0, a0, 4bltu a0, a1, 1b 2:/** Call vendor defined SystemInit to* initialize the micro-controller system*///系統(tǒng)初始化,主要是時(shí)鐘初始化call SystemInit/* Call global constructors */la a0, __libc_fini_array //將標(biāo)簽__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運(yùn)行庫(kù)的特殊庫(kù)函數(shù),用于處理一些C/C++程序中//的全局性的構(gòu)造和析構(gòu)函數(shù)。//__libc_init_array函數(shù)會(huì)調(diào)用一個(gè)名為_(kāi)init的函數(shù)/* do pre-init steps before main */call _premain_init/* ===== Call Main Function ===== *//* argc = argv = 0 *///函數(shù)調(diào)用時(shí)由a0和a1寄存器傳遞參數(shù)li a0, 0li a1, 0#ifdef RTOS_RTTHREAD// Call entry function when using RT-Threadcall entry //調(diào)用entry函數(shù),開(kāi)始執(zhí)行entry函數(shù) #elsecall main //調(diào)用main函數(shù),開(kāi)始執(zhí)行main函數(shù) #endif/* do post-main steps after main */call _postmain_fini1:j 1b //最后死循環(huán),程序理論上不可能執(zhí)行到此處(1)__libc_init_array函數(shù)會(huì)調(diào)用一個(gè)名為_(kāi)init的函數(shù),該函數(shù)位于:freertos_GD32VF103/nuclei_sdk/SoC/gd32vf103/Common/Source/system_gd32vf103.c
_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 *///用來(lái)計(jì)算當(dāng)前運(yùn)行頻率SystemCoreClock = get_cpu_freq();/* configure USART *///調(diào)用gd_com_init()函數(shù)對(duì)UART模塊進(jìn)行設(shè)計(jì),串口打印信息就是要對(duì)uart進(jìn)行初始化gd_com_init(SOC_DEBUG_UART);/* Display banner after UART initialized *///UART初始化以后打印相關(guān)信息,有興趣的可以進(jìn)入函數(shù)看看打印了哪些內(nèi)容SystemBannerPrint();/* Initialize exception default handlers *///初始化異常處理程序Exception_Init();/* ECLIC initialization, mainly MTH and NLBIT *///ECLIC初始化,主要是MTH和NLBITECLIC_Init(); }(2-1)get_cpu_freq()函數(shù)源碼
uint32_t get_cpu_freq() {uint32_t cpu_freq;// warm upmeasure_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 tickuint32_t tmp = (uint32_t)SysTimer_GetLoadValue();do {start_mtime = (uint32_t)SysTimer_GetLoadValue();start_mcycle = __RV_CSR_READ(CSR_MCYCLE); //通過(guò)讀取CSR寄存器MCYCLE得到當(dāng)前時(shí)鐘周期,并作為初始計(jì)數(shù)值} while (start_mtime == tmp); //不斷觀察MTIME計(jì)數(shù)器并將其值作為初始化時(shí)間值do {delta_mtime = (uint32_t)SysTimer_GetLoadValue() - start_mtime;//通過(guò)讀取CSR寄存器MCYCLE得到當(dāng)前時(shí)鐘周期,并與初始計(jì)數(shù)值相減得到這段時(shí)間消耗的時(shí)鐘周期delta_mcycle = __RV_CSR_READ(CSR_MCYCLE) - start_mcycle;} while (delta_mtime < n);//MTIME計(jì)數(shù)器的頻率是常開(kāi)域的參考頻率,Core的運(yùn)行頻率與CSR寄存器MCYCLE的值一致//通過(guò)MCYCLE和MTIME的相對(duì)關(guān)系計(jì)算出當(dāng)前Core的時(shí)鐘頻率return (delta_mcycle / delta_mtime) * mtime_freq+ ((delta_mcycle % delta_mtime) * mtime_freq) / delta_mtime; }(3)調(diào)用gd_com_init()函數(shù)對(duì)UART模塊進(jìn)行設(shè)計(jì),串口打印信息就是要對(duì)uart進(jìn)行初始化,有興趣的同學(xué)可以自行查看
(4) SystemBannerPrint(),UART初始化以后打印相關(guān)信息,有興趣的可以進(jì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_MIEcsrc 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個(gè)單字(40位)或者14個(gè)單字(28位)空間用于保存寄存器 #ifndef __riscv_32eaddi sp, sp, -20*REGBYTES #elseaddi sp, sp, -14*REGBYTES #endif /* __riscv_32e *///保存ABI定義的“調(diào)用者應(yīng)存儲(chǔ)的寄存器(Caller saved register)”進(jìn)入堆棧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_32eSTORE 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 */ //回復(fù)用于從堆棧中恢復(fù)ABI定義的“調(diào)用者應(yīng)存儲(chǔ)的寄存器(Caller saved register)” .macro RESTORE_CONTEXTLOAD 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_32eLOAD 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)//恢復(fù)寄存器后,更改堆棧指針,回收20個(gè)單字(40位)或者14個(gè)單字(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寄存器的值從棧中恢復(fù) .macro RESTORE_CSR_CONTEXTLOAD x5, 13*REGBYTES(sp)csrw CSR_MSUBM, x5LOAD x5, 12*REGBYTES(sp)csrw CSR_MEPC, x5LOAD 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標(biāo)簽為“弱(weak)屬性”,“弱(weak)屬性”為C/C++語(yǔ)法中定義的一種屬性,一旦有具體的“非弱”性質(zhì)同名函數(shù)存在/將會(huì)覆蓋此函數(shù) .weak exc_entry exc_entry://保存相應(yīng)的狀態(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, mcausemv 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//恢復(fù)相應(yīng)的狀態(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標(biāo)簽為“弱(weak)屬性”,“弱(weak)屬性”為C/C++語(yǔ)法中定義的一種屬性,一旦有具體的“非弱”性質(zhì)同名函數(shù)存在 //將會(huì)覆蓋此函數(shù) .weak irq_entry /* This label will be set to MTVT2 register */ irq_entry:保存相應(yīng)的狀態(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*///跳到中斷向量表進(jìn)行中斷處理csrrw ra, CSR_JALMNXTI, ra/* Critical section with interrupts disabled *///禁止所有中斷DISABLE_MIE//恢復(fù)相應(yīng)的狀態(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ù)字標(biāo)簽j 1b //跳轉(zhuǎn)回標(biāo)簽1處,因此會(huì)成為死循環(huán)總結(jié)
以上是生活随笔為你收集整理的GD32VF103启动流程分析的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: catia曲面扫掠命令详解_Master
- 下一篇: 采样次数不同平均值不一样_网络推广采取的