程序启动时先执行 start.S
start.S 中第一段程序
_start:
/* clear bit in csr MSTATUS_MIE field to turn off global interrupt. */
csrc CSR_MSTATUS, MSTATUS_MIE
/* Jump to logical address first to ensure correct operation of RAM region */
la a0, _start
li a1, 0x20000000
/* test if this is boot on SRAM */
bleu a1, a0, _start0800
/* test if this is boot on main Flash */
srli a1, a1, 2
bleu a1, a0, _start0800
/* else must be boot on bootloader */
la a0, _start0800
add a0, a0, a1
jr a0
第一行 csrc CSR_MSTATUS, MSTATUS_MIE
分析, csrc 是 CSR 寄存器操作符之一:
Pseudoinstruction | Base Instruction | Meaning |
---|---|---|
csrr rd, csr | csrrs rd, csr, x0 | Read CSR |
csrw csr, rs | csrrw x0, csr, rs | Write CSR |
csrs csr, rs | csrrs x0, csr, rs | Set bits in CSR |
csrc csr, rs | csrrc x0, csr, rs | Clear bits in CSR |
CSR 寄存器全称为 Control and Status Registers
而 MSTATUS 寄存器控制在机器模式下的状态
Field | Bit | Reset Value | Description |
---|---|---|---|
MIE | 3 | 0x00000000 | 表示全局中断使能,1 enable, 中断能够被正常响应;0 disable,中断被屏蔽 |
MPIE | 7 | 0x00000000 | 表示全局中断使能,1 enable, 中断能够被正常响应;0 disable,中断被屏蔽 |
MPP | 12:11 | 0x00000000 | 表示全局中断使能,1 enable, 中断能够被正常响应;0 disable,中断被屏蔽 |
FS | 14:13 | 0x00000000 | 表示浮点单元的状态,0b00 All off; 0b01 initial; 0b10 Clean; 0b11 Dirty |
XS | 16:15 | 0x00000000 | 表示用户自定义的扩展单元状态 |
SD | 31 | 0x00000000 | 只读,SD = (FS==0b11 || XS == 0b11) |
因此第一行的含义为关闭全局中断
la a0, _start
将 _start 的地址装载到 a0 中
如果程序正常从 Flash 启动,a0 的值为 0x8000 XXXX
li a1, 0x20000000
将 a1 的值写为 0x2000 0000 (SRAM 地址)
bleu a1, a0, _start0800
判断是否从 SRAM 启动
srli a1, a1, 2
bleu a1, a0, _start0800
将 a1 右移后继续判断,这时判断是否从 Flash 启动
la a0, _start0800
add a0, a0, a1
jr a0
否则就是从 bootloader 启动
========
_start0800:
/* set the the NMI base to share with mtvec by setting CSR_MMISC_CTL */
li t0, 0x200
csrs CSR_MMISC_CTL, t0
/* initialize the mtvt */
la t0, vector_base
csrw CSR_MTVT, t0
/* initialize and enable the mtvt2 */
la t0, irq_entry
csrw CSR_MTVT2, t0
csrs CSR_MTVT2, 0x1
/* initialize the CSR MTVEC for the Trap ane NMI base addr */
la t0, trap_entry
csrw CSR_MTVEC, t0
#ifdef __riscv_flen
/* enable FPU */
li t0, MSTATUS_FS
csrs mstatus, t0
csrw fcsr, x0
#endif
/* for the code below, inline assembler options are used to produce
```
auipc gp, %pcrel_hi(__global_pointer$)
addi gp, gp, %pcrel_lo(__global_pointer$)
```
instead of
```
addi gp, gp, 0
```
(https://sourceware.org/binutils/docs-2.31/as/RISC_002dV_002dDirectives.html#RISC_002dV_002dDirectives)
*/
.option push
.option norelax
la gp, __global_pointer$
.option pop
la sp, _sp
/* Load data section */
la a0, _data_lma
la a1, _data
la a2, _edata
bgeu a1, a2, LoopCopyDataInit
li t0, 0x200
csrs CSR_MMISC_CTL, t0
将 CSR_MMISC_CTL 的第九位设为 1
CSR_MMISC_CTL
Field | Bit | Reset Value | Description |
---|---|---|---|
NMI_CAUSE_FFF | 9 | 0x00000000 | 0: mnvec 的值为处理器 RESET 之后的 PC 地址,NMI 的 mcause.EXCCODE 为 0x1; 1: mnvec 的值与 mtvec 一致,NMI 的 mcause.EXCCODE 为 0xFFF |
la t0, vector_base
csrw CSR_MTVT, t0
初始化中断向量,将地址装载到 CSR_MTVT 中
CSR_MTVT 用于保存 ECLIC 中断向量表的基地址,至少为 64byte 对齐
la t0, irq_entry
csrw CSR_MTVT2, t0
csrs CSR_MTVT2, 0x1
初始化 IRQ 入口地址,并打开 MTVT2
CSR_MTVT2 用于指定 ECLIC 非向量模式中断的 common-code 入口地址
Field | Bit | Reset Value | Description |
---|---|---|---|
COMMON-CODE-ENTRY | 31:2 | common-code 地址 | |
MTVT2EN | 0 | 0 | 使能位,0: ECLIC 非向量模式中断 common-code 地址由 mtvec 决定;1: 0: ECLIC 非向量模式中断 common-code 地址由上面的地址位决定 |
la t0, trap_entry
csrw CSR_MTVEC, t0
仅配置异常的处理地址(中断的已在上面通过 MTVT & MTVT2 配置完成)
CSR_MTVEC 用于配置中断和异常处理程序的入口地址
Field | Bit | Reset Value | Description |
---|---|---|---|
ADDR | 31:6 | mtvec 地址 | |
MODE | 5:0 | 控制中断处理模式 0b000011 为 ECLIC 模式;其他数值为 RISC-V 默认中断模式 |
再来说一下向量中断和非向量中断
向量中断发生时,处理器直接进行中断查表并跳转到相应地址,不保存上下文,因此中断处理函数必须是 Leaf-Function,即不能再调用其他的子函数。因为无需处理上下文,向量中断非常快
非向量中断发生时,处理器会先跳转到一段 common-code,执行上下文保存和正在 pending 中断的判断,按照中断优先级来处理,因此可以实现中断嵌套(即在处理一段中断时发生了更高优先级的中断,而跳转到新的中断处理)和中断咬尾(即几个中断顺序处理,切换之间无需恢复上下文),但时间开销较大
我们一般使用非向量中断