作者:水银
Email:[email protected]
龙眼Longan RV技术交流群: 816177882
转载请注明作者和出处
.globl _start // 定义全局_start符号
.type _start,@function // 指定_start类型为function
_start:
csrc CSR_MSTATUS, MSTATUS_MIE // csr寄存器mstatus的mie位清零,关全局中断
/* Jump to logical address first to ensure correct operation of RAM region */
la a0, _start // 把_start地址载入到a0,根据启动位置的不同,_start可能在ram地址中也可能在flash中
li a1, 1 // a1 = 1
slli a1, a1, 29 // a1 = a1 << 29 (a1=0x20000000,ram起始地址)
bleu a1, a0, _start0800 // if (a1 <= a0) goto _start0800检 测是否在ram中运行,如果在ram中运行,_start地址将会大于 0x20000000
srli a1, a1, 2 // a1 = a1 >> 2 (a1=0x08000000 flash起始地址)
bleu a1, a0, _start0800 // if (a1 <= a0) goto _start0800
la a0, _start0800 // a0 =_start0800 程序地址不正确
add a0, a0, a1 // a0 = a0+0x08000000 (把程序地址重新定位到flash中)
jr a0 // 跳转到a0所存的地址
_start0800:
/* Set the the NMI base to share with mtvec by setting CSR_MMISC_CTL */
li t0, 0x200 // t0 = 0x200
csrs CSR_MMISC_CTL, t0 // mmisc_ctl = 0x200 ECLIC寄存器mmisc_ctl用于控制NMI中断向量表,这里设置成和mtvec一致
/* Intial the mtvt*/
la t0, vector_base // t0 = vector_base
csrw CSR_MTVT, t0 // mtvt寄存器设置为vector_base地址
/* Intial the mtvt2 and enable it*/
la t0, irq_entry // t0=irq_entry,irq_entry是一个中断处理入口函数,位于entry.S
csrw CSR_MTVT2, t0 // mtvt2寄存器设置为irq_entry
csrs CSR_MTVT2, 0x1 // mtvt2[0] = 1 mtvt2[0]为0时,中断入口使用mtvec寄存器,mtvt2[0]为1时,中断入口为mtvt2[31:2]
/* Intial the CSR MTVEC for the Trap ane NMI base addr*/
la t0, trap_entry // t0 = trap_entry,trap_entry是异常处理入口函数,位于entry.S
csrw CSR_MTVEC, t0 // mtvec寄存器设置为trap_entry
#ifdef __riscv_flen // 未使用代码
/* Enable FPU */
li t0, MSTATUS_FS
csrs mstatus, t0
csrw fcsr, x0
#endif // 未使用代码
.option push // 保存编译设置
.option norelax // 禁用相对寻址
la gp, __global_pointer$ // gp = __global_pointer$,__global_pointer$在ld脚本中定义,一般指向.sdata段,用于快速相对寻址优化。
.option pop // 恢复设置
la sp, _sp // sp = _sp,_sp为堆栈顶部,在ld脚本中定义
/* Load data section */
la a0, _data_lma // a0= _data_lma,_data_lma指向.data段在flash中的地址,在ld脚本中定义
la a1, _data // a1= _data,_data指向.data段在ram中的加载地址,在ld脚本中定义
la a2, _edata // a2 = _edata,_edata指向.data段在ram中的结束地址,在ld脚本中定义
bgeu a1, a2, 2f // if (a1 >= a2) goto 2f,2f表示后一个为2的标号
1:
lw t0, (a0) // t0 = *a0,从a0指向的地址中4个字节到t0
sw t0, (a1) // *a1 = t0,将t0保存到a1指向的地址
addi a0, a0, 4 // a0+=4
addi a1, a1, 4 // a1+=4
bltu a1, a2, 1b // if (a1 < _edata) goto 1b,1b表示前一个为1的标号
2:
/* Clear bss section */
la a0, __bss_start // a0 = __bss_start,__bss_start指向.bss段在ram中的地址,在ld脚本中定义
la a1, _end // a1 = _end,_end指向.bss段在ram中的结束地址,在ld脚本中定义
bgeu a0, a1, 2f // if (a0 >= a1) goto 2f,2f表示后一个为2的标号
1:
sw zero, (a0) // *a0=0,将a0指向的地址置位0
addi a0, a0, 4 // a0+= 4
bltu a0, a1, 1b // if (a0 < _end) goto 1b,1b表示前一个为1的标号
2:
/*enable mcycle_minstret*/
csrci CSR_MCOUNTINHIBIT, 0x5 // mcountinhibit.IR被清零,minstret计数开启, mcountinhibit.CY被清零,mcycle计数开启
/* Call global constructors */
la a0, __libc_fini_array // 初始化libc
call atexit
call __libc_init_array
/* argc = argv = 0 */
li a0, 0 // a0=0
li a1, 0 // a1=0
call main // 进入main
tail exit
1:
j 1b
risc-v 的la是一条伪指令,编译器会根据情况编译成不同的代码。