RISC-V 异常和中断
RISC-V 异常和中断
[TOC]
RISC-V Exception & Interrupt
RISC-V 特权等级:
Level | Encoding | Name | Abbreviation |
---|---|---|---|
0 | 00 | User/Application | U |
1 | 01 | Supervisor | S |
2 | 10 | Reserved | |
3 | 11 | Machine | M |
RISC-V 支持的特权模式组合:
组合序号 | 支持的模式 | 应用场景 |
---|---|---|
1 | M | 简单的嵌入式系统 |
2 | M, U | 有安全支持的嵌入式系统 |
3 | M, S, U | 能跑操作系统如 Linux(支持虚拟内存) |
CSR 指令
CSR 指令格式 | 31, 20 | 19, 15 | 14, 12 | 11, 7 | 6, 0 |
---|---|---|---|---|---|
CSR | rs1 | funct3 | rd | opcode | |
CSRRW |
CSR Index | source | 001 | rd | 1110011 |
CSRRS |
CSR Index | source | 010 | rd | 1110011 |
CSRRC |
CSR Index | source | 011 | rd | 1110011 |
CSRRWI |
CSR Index | unsigned imm | 101 | rd | 1110011 |
CSRRSI |
CSR Index | unsigned imm | 110 | rd | 1110011 |
CSRRCI |
CSR Index | unsigned imm | 111 | rd | 1110011 |
指令 | 说明 | 数学表达 |
---|---|---|
CSRRW(Write) | 取出 csr 寄存器里的值,零拓展为 32 位后存到 rd 寄存器里;将 rs1 寄存器里的值存到 crs 寄存器里 | \(x[rd]=zeroExt(crs[index]),\\ csr[index] = x[rs1]\) |
CSRRS(Set) | 取出 csr 寄存器里的值,零拓展为 32 位后存到 rd 寄存器里;若 rs1 寄存器里某一位是 1,则将 crs 寄存器对应位置 1,负责 csr 对应位不变 | \(x[rd]=zeroExt(crs[index]),\\ csr[index] \| = x[rs1]\) |
CSRRC(Clear) | 取出 csr 寄存器里的值,零拓展为 32 位后存到 rd 寄存器里;若 rs1 寄存器里某一位是 1,则将 crs 寄存器对应位清零,负责 csr 对应位不变 | \(x[rd]=zeroExt(crs[index]),\\ csr[index]\&= !x[rs1]\) |
CSRRWI | 将 rs1 寄存器替换成无符号立即数 | \(x[rd]=zeroExt(crs[index]),\\ csr[index] = zeroExt(imm)\) |
CSRRSI | 将 rs1 寄存器替换成无符号立即数 | \(x[rd]=zeroExt(crs[index]),\\ csr[index] \| = zeroExt(imm)\) |
CSRRCI | 将 rs1 寄存器替换成无符号立即数 | \(x[rd]=zeroExt(crs[index]),\\ csr[index]\&= !zeroExt(imm)]\) |
Trap 相关控制寄存器
Trap Setup:
寄存器 | 描述 | 长度 |
---|---|---|
mstatus | 跟踪和控制 hart 的当前运行状态 | XLEN |
mtvec | (trap vector base address): 表明 trap 发生的时候,PC 需要跳转的地址 | XLEN |
mie | (interrupt enable): 用于进一步控制(打开和关闭)software interrupt/timer interrupt/external interrupt | XLEN |
mstatus
寄存器中的:
xIE
字段用于控制全局中断使能;高级模式的中断关闭之后,比它等级低的中断都会关闭、低级模式的中断打开之后,比它等级高的中断都会打开xPIE
(previous interrupt enable)字段用于存储 trap 发上之前 hart 的中断使能xPP
(previous privilege)字段存储进入 trap 之前 hart 所处的特权模式
mtvec
寄存器中的:
BASE
字段必须是 4byte 对齐MODE
- 0 for direct:
pc=BASE
, 中断发生的时候会跳到这个固定的地址 - 1 for vectored:
pc=BASE+4*cause
, 中断发生的时候,会根据中断类型,跳到不同的地址 例:当machine time interrupt发生的时候,已知其对应的 mcause=111,故pc=BASE+(111<<2)=BASE+0x1C
- \(\ge 2\): Reserved
PS: RISC-V 中的指令都是 little endian
- 0 for direct:
mie
(ie for interrupt
enable)寄存器中的每 bit 指定 mcause 中的各种类型的 trap 是否打开
Trap Handling:
在 RISC-V 架构中,并没有定义进入和退出中断处理时硬件自动保存和恢复现场的机制, 用户需要用软件主动保护和恢复程序现场。在发生中断时,拉低 mstatus 寄存器的 MIE 域屏蔽全局中断, 也是为了软件保存和恢复现场的过程不 会被更高级别的中断打断,导致数据的丢失。
寄存器 | 描述 | 长度 |
---|---|---|
mcause | (trap cause): 记录导致 trap 的原因 | XLEN |
mtval | (trap value): 补充 trap 发生 的额外信息 | XLEN |
mepc | (exception pc): 保存 trap 发生时的 PC 到该寄存器里, 32 处理器下其 lsm[1:0]=00 | XLEN |
mip | (interrupt pending): 它列出目前已发生等待处理的中断 | XLEN |
mscratch | (scratch): 一般用于指向某 M 模式的上下文空间 | XLEN |
mip
(ip for interrupt
pending)寄存器中的每 bit 判断 mcause 中的各种类型的 trap 是否
pending
mcause
最高位=1,表明 trap
是 interrupt,否则 trap 是 exception
Time/Counter
寄存器 | 描述 | 长度 |
---|---|---|
mcycle | (machine cycle counter): 记录该 hard 运行的 cycle 数 | 64 |
minstret | (machine instruction retired counter): 记录执行完毕的指令数 | 64 |
mtime | M 模式按时自增的计时器 | 64 |
mtimecmp | 比较计时器,当 mtime\(/ge\)mtimecmp 时会触发 timer interrupt | 64 |
Trap 类型
Trap 类型有
mcause
寄存器指出,其中最高位为 1 表明是 interrupt,为 0 表示是 exception
Interrupt | Exception Code | Description |
---|---|---|
1 1 1 1 |
0 1 2 3 |
Reserved Supervisor software interrupt Reserved Machine software interrupt |
1 1 1 1 |
4 5 6 7 |
Reserved Supervisor timer interrupt Reserved Machine timer interrupt |
1 1 1 1 |
8 9 10 11 |
Reserved Supervisor external interrupt Reserved Machine external interrupt |
1 1 |
12–15 ≥16 |
Reserved Designated for platform use |
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 |
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16–23 24–31 32–47 48–63 ≥64 |
Instruction address misaligned Instruction access fault Illegal instruction Breakpoint <- EBREAK Load address misaligned Load access fault Store/AMO address misaligned Store/AMO access fault Environment call from U-mode <- ECALL Environment call from S-mode <- ECALL Reserved Environment call from M-mode <- ECALL Instruction page fault Load page fault Reserved Store/AMO page fault Reserved Designated for custom use Reserved Designated for custom use Reserved |
- Illegal Instruction Exception:
- 访问不存在的 CSR
- Write to read only CSR
- 低特权级别尝试访问高级别的 CSR
通过上表可以看出:
- interrupt 主要有三种,分别是:
software interrupt
,timer interrupt
和external interrupt
,在三种模式M
,S
,U
下都有对应的 interrupt. - exception 主要有五种,分别是:
instruction
,load/store
,environment call
,page fault
和breakpoint
exception 的优先级从上到下依次递减,如果同时发生了多个 exception,则优先级高的被处理
Priority | Exc. Code | Description |
---|---|---|
Highest | 3 | Instruction address breakpoint |
12, 1 | During instruction address translation:First encountered page fault or access fault | |
1 | With physical address for instruction:Instruction access fault | |
2 0 8, 9, 11 3 3 |
Illegal instruction Instruction address misaligned Environment call Environment break Load/store/AMO address breakpoint |
|
4, 6 | Optionally:Load/store/AMO address misaligned | |
13, 15, 5, 7 | During address translation for an explicit memory access:First encountered page fault or access fault | |
5, 7 | With physical address for an explicit memory access:Load/store/AMO access fault | |
Lowest | 4, 6 | If not higher priority:Load/store/AMO address misaligned |
Trap 处理流程
1. 初始化:
将trap_vector的地址存储到mtvec
,这样当trap发生的时候,pc可以自动跳转到该地址去执行trap处理程序
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29trap_vector:
# save context(registers).
csrrw t6, mscratch, t6 # swap t6 and mscratch
reg_save t6
# Save the actual t6 register, which we swapped into
# mscratch
mv t5, t6 # t5 points to the context of current task
csrr t6, mscratch # read t6 back from mscratch
sw t6, 120(t5) # save t6 with t5 as base
# Restore the context pointer into mscratch
csrw mscratch, t5
# call the C trap handler in trap.c
csrr a0, mepc
csrr a1, mcause
call trap_handler # call C functions to solve trap
# trap_handler will return the return address via a0.
csrw mepc, a0
# restore context(registers).
csrr t6, mscratch
reg_restore t6
# return to whatever we were doing before trap.
MRET
TOP Half, 一些硬件自动做的工作,更新 csr 的信息
- 针对
mstatus
: \(MPIE=MIE, MIE=0\) - 如果 trap 是 interrupt 则
mepc=pc+1
; 如果 trap 是 exception 则mepc=pc
pc=mtvec
- 根据 trap 发生之前所处的特权等级去设置
mstatus
的 MPP 字段,进入到 M 模式
- 针对
Botton Half: 执行 trap_handler
- 保存(save)当前控制流的上下文信息(利用 mscratch)
- 调用 C 语言的 trap_handler
- 从 trap_handler 函数返回, mepc 的值有可能需要调整
- 恢复(restore)上下文的信息
- 执行 MRET 指令返回到 trap 之前的状态。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29reg_t trap_handler(reg_t epc, reg_t cause)
{
reg_t return_pc = epc;
reg_t cause_code = cause & 0xfff;
if (cause & 0x80000000) {
/* Asynchronous trap - interrupt */
switch (cause_code) {
case 3:
uart_puts("software interruption!\n");
break;
case 7:
uart_puts("timer interruption!\n");
break;
case 11:
uart_puts("external interruption!\n");
break;
default:
uart_puts("unknown async exception!\n");
break;
}
} else {
/* Synchronous trap - exception */
printf("Sync exceptions!, code = %d\n", cause_code);
panic("OOPS! What can I do!");
//return_pc += 4;
}
return return_pc;
}返回:执行 xRET 指令
- 在不同特权模式下退出有对应的 ret 指令,如: mret, sret, uret
- mret 硬件将会执行如下操作:
- 更改当前 hart 的特权模式为 mstatus.MPP
mstatus.MIE=mstatus.MPIE, mstatus.MPIE=1
pc=mepc
Trap处理加速
中断响应是嵌入式微控制器的关键特性之一。 中断响应时间指的是从中断设备产生中断信号到处理器开始执行中断服务程序(Interrupt Service Routine, ISR) 中的第一条指令所花费的时间。 拥有快速的中断响应对于嵌入式处理器而言至关重要。
加速中断的方法
- 扩展了硬件自动保存和恢复现场和全局中断硬件屏蔽设计
- 支持自动的中断嵌套缩短了中断响应时间,减少了软件的开销
- 除此之外,还增加了中断尾链设计,进一步提升了处理器响应中断的性能
RISC-V 中断为什么慢?
- RISC-V 硬件不会自动保存现场,需要使用软件去手动保存现场,增加了很多的 cycle
- RISC-V 默认不支持中断嵌套,如果需要支持中断嵌套,需要使用软件对 CSR 寄存器进行操作,增加了 cycle
将软件的工作用硬件来实现,可以降低中断整体的时间近 1/3
References
- The RISC-V Instruction Set Manual Volume I: Unprivileged ISA, Chapter 9 “Zicsr”, Control and Status Register (CSR)
- The RISC-V Instruction Set Manual Volume II: Privileged Architecture, Chapter 3 Machine-Level ISA
- Writing a RISC-V Emulator in Rust: Control and Status Register
- RISC-V Bytes: Privilege Levels
- 用于计量的嵌入式 RISC-V 处理器设计及 MCU 实现