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) 设置栈空间、全局变量空间
1 lagp,__sdata_start__+0x800 //全局变量空间
2 la sp,__stack_end__ //栈空间顶地址
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 //进人主程序