esp32 h2 功耗樂鑫信息科技深圳代理商riscv-crt0.s 分析,在SEGGER Embedded Studio for RISC-V 中,GD32VF103啟動序涉及3個匯編源文件:rise-ert0. s、entry.s 和 GD32VFlxx_Startup.s,且包含 riser-crt0.s 中代所調用的 C語言雨數的文件。中斷控制器 ECLIC 管理的中向是表及中斷向量代碼在 GD32VF1xx Strtup.s 文件中。非向量服務入口程序在文件entry.s中。esp32 h2 功耗樂鑫信息科技深圳代理商其中 risev-erto.s 是啟動程序的主體部分。
在GDB2VF1xx_Startup.s中,將中斷向量表作為一個.seclion聲明為“section.vector”,鏈接后分配在程序空間的起始位置。
riscv-crt0.s 分析
文件 riscv-crto.s 的主要內容可分為 3 個部分:設置中斷入口地址,初始化處理器和系統,準備 C語育程序運行環境。在 riscv-crto.s 的后調用 main(),進人應用主程序。
在 Linker 選項中,選擇“start”作為啟動序人口,則復位后從_start 開始執行啟動程序。
(1)設置異常和中斷處理程序入口將異常和中斷處理程序人口地址 trap_entry 寫入 CSR 機器模式中斷向量寄存器 mtvec,esp32 h2 功耗樂鑫信息科技深圳代理商并清除 CSR 中斷原因寄存器 mcause。
1 _start;
......
2 la a0, trap_entry //ao= trap_entry,將異常處理人口地址裝人寄存器
3 csrw mtvec,a0 //將 ao寄存器值寫人 mtvec 寄存器
4 csrw mcause,x0 //將0寫人 mcause寄存器
(2) 初始化處理器和系統
調用外部函數-init()進行初始化。如圖 8.3 所示,riscv-crt0.s 調用_init(),_init()調用函數 SystemInit()。
riscv-crt0.s main(main.c)
_init( )(init.c)--SystemInit( )(system gd32fv103.c)
圖8.3 函數調用關系圖
1、調用_init(),jalr 指令跳轉到存于 t1 存器中的地址。
1 la tl, init //t1=_init,將標簽地址裝人 t1 寄存器
2 jalr tl //pc= t1,將 t1 中的值賦給程序計數器 pc
2、調用 SystemInit(),_init()是 risev-crto.s 定義的空初始化函數,作為調用接口。esp32 h2 功耗樂鑫信息科技深圳代理商實現程序時,在_init()中調用具體實現初始化功能的函數 SystemInit(),以及eclic_init 等初始化函數。
void_init(){
SystemInit(); //初始化系統時鐘
eclic_init(ECLIC_NUM_INTERRUPTS); //初始化中斷控器
eclic_mode_enable(); //啟用 ECLIC
}
3、調用 SystemInit()函數,初始化處器內部時鐘
void SystemInit(void)
{
RCU_CTL= RCU_CTL_IRC8MEN; //選擇內部時鐘源
......
RCU_INT=0x00FF0000U; //關閉時鐘中斷
system_clock_config(); //設置系統時鐘,選擇時鐘源,設定鎖相環、總線時鐘等
}
(3)初始化 C語言運行環境和變量空間
1) 設置??臻g、全局變量空間
1 lagp,__sdata_start__+0x800 //全局變量空間
2 la sp,__stack_end__ //??臻g頂地址
3 la tp,__tbss_start__ //線程變量空間起始地址
2)將需要快速執行的程序代碼裝入內部 RAM
系統啟動后,從 ROM 中運行程序。為了提高程序運行速度,常把指令復制到處理器內部集成的緊耦合存儲器(Inner Tight Couple Memory,ITCM),并在ITCM中執行。esp32 h2 功耗樂鑫信息科技深圳代理商下面的代碼把 ROM 中的指令從__fast_load_start__地址開始,復制到ITCM中__fast_start__至fast_end_的地址空間內。
/* 裝載快速段 */
1 la a0,__fast_load_start_ //a0=__fast_load_start__,裝載標簽處地址
2 la al,__fast_start_ //a1 =__fast_start__,裝載標簽處地址,ITCM始
3 la a2,__fast_end_ //a2 =__fast_end__,裝標簽處地址,ITCM終
4 bgeu a1,a2,2f //if(a1 == a1)goto 2,跳過復制操作
5 l: //局域標簽
6 lw t0,(a0) //t0= (a0),讀取指令(4 字節)ROM
7 sw t0,(a1) //(a1)= t0,指令(4字節)寫人 RAM
8 addi a0,a0,4 //a0=a0+4,ROM指針加4
9 addi a1, al,4 //al =a1 +4,RAM指針加4
10 bltu al,a2,1b //if(a1<a2)goto1,循環復制
11 2:
第1行,"._fast load start"是ROM 中的標簽地址:第 2行,"__fast_start__" 是ITCM 標簽地址;第4行,判斷需要復制指令的總長度;第 10 行,判斷指令復制是否完成。
3)讀取數據段到 RAM
將 ROM 中數據段.data 中的初始數據復制到 RAM 中。“__data_load_start__”是 ROM 中.data 段的起始地址,“__data_start__”是RAM中.data斷的起始地址。
/* 裝載數據段 */
1 la a0,__data_load_start_ //a0 =_data_load_start__,裝載標簽位置地址
2 la al, __data_start_ //al = data_start__,裝標簽位置地址
3 la a2,__data_end_ //a2 =__data_end__,裝載標簽位置地址
4 bgeu al, a2 2f //if(al == a2)跳轉到 2,結束數據裝載
5 1:
6 Iw t0,(a0) //t0 = (a0),讀取 ROM 數據到 t0
7 sw t0,(a1) //(a1) = t1,將 t0 數據寫到 RAM 中(a1)
8 addi a0 , a0 , 4 //a0 = a0 +4,增加 ROM 指針
9 addi al , al , 4 //al =a1 +4,增加 RAM 指針
10 bltu al, a2, 1b //if(a1<a2) 跳轉 1,循環復制
11 2:
在 riscv-crt0.s 中“rodata”段、“bss”段和“tbss”段中數據的復制方法與上述相似。
4) 初始化數據空間
esp32 h2 功耗樂鑫信息科技深圳代理商啟動程序時需要對“bss”等未裝載數據的段進行初始化。下列程序將“bss”段中的數據清0。其中,“__bss_start__”是 RAM中 bss 段的起始地址,__bss_end__”是RAM 中 bss 段的結束地址。
/* 將 bss 段中的數據清 0 */
1 la a0,__bss_start__ //a0=__bss_start__,獲取標簽位置地址
2 la al,__bss_end__ //al =__bss_end__,獲取標簽位置地址
3 bgeu a0, al, 2f //if(a0 == a1) 跳轉到 2,結束裝載
4 1:
5 sw x0,(a0) //(a0)=0,將0寫人內存
6 addi a0, a0,4 //ao = a0+4,增加地址
7 bltu a0, al, 1b //if(a0<a1) 跳轉到 1,循環
8 2:
5) 初始化堆空間
在 C/C++語言程序中,在堆中分配程序中動態申請的內存空間。程序初始化堆的空間,將堆中的第 1個字清 ,將堆的長度寫入第 2 個字中。其中,“__headp_start__“是RAM中堆的起始地址,“__heap_end__ ”是 RAM 中堆的結束地址。
/*初始化堆*/
1 la a0,heap_start__ //a0=__heap_start__,獲取標簽位置地址
2 la al , __heap_end__ //a0=_heap_end__,獲取標簽位置地址
3 sub al, al, a0 //al =al- a0,計算堆的長度
4 sw x0,0(a0) //(a0)=0,將0寫人堆中第 1個字
5 sw a1,4(a0) //(a0 +4)= a1,將堆的長度寫人堆中第 2 個字
6)初始化全局構造函數空間
C++類的構造函數存放在構造函數段。esp32 h2 功耗樂鑫信息科技深圳代理商程序從構造函數段逐一獲取構造雨數然針并執行。其中,“__ctors_start__”是 RAM 中構造數段的起始地址“。“__ctors_end_”是 RAM 中構造函數的結束地址。
/*執行構造函數 */
1 la s0,__ctors_start__ //s0=__ctors_start__,獲取起始地址
2 la s1,__ctors_end__ //s1=__ctors_end_,獲取結束地址
3 1;
4 beg s0,s1,2f //s1 == so ?如果完成,則跳出循環
5 lw t1,0(s0) //t1 =[so],讀取構造函數入口地址
6 addi s0, s0,4 //s0 = s0 + 4
7 jalr t1 //執行構造函數
8 j1b //循環執行
9 2:
(4)進入主函數 main
引導程序后跳轉到 C/C++程序的入口 main。如果 main 函數需要參數,則在跳轉前需將參數寫到 a0、al 寄存器。
li a0,0 //寫人參數1
li al,0 //寫人參數2
la tl,main //裝載主程序人口地址
jalr tl //進人主程序