第 11 章 外部设备中断
在计算机中,中断是系统用来响应硬件设备请求的一种机制,操作系统收到硬件的中断请求,会打断正在执行的进程,然后调用内核中的中断处理程序来响应请求。为了避免由于中断处理程序执行时间过长,而影响正常进程的调度,Linux 将中断处理程序分为上半部和下半部:
- 上半部,对应硬中断,由硬件触发中断,用来快速处理中断;
- 下半部,对应软中断,由内核触发中断,用来异步处理上半部未完成的工作;
RISC-V 中断(Interrupt)的分类
- 软中断
- 定时器中断
- 外部中断
RISC-V 中断编程中涉及的寄存器
RISC-V 中断处理流程
1、Hart自动执行状态切换
- 把mstatus中的MIE复制到MPIE中,清除mstatus中的MIE标志位(关闭全局中断)
- 把当前PC的下一条指令地址赋值给mepc,同时将PC设置位mtvec,注意mtvec的mode,mtvec.mode = direct,PC = BASE;mtvec.mode = vector, PC = BASE + 4 * cause
- 根据中断的种类设置mcause,并根据需要设置mtval(附加信息)
- 将中断发生之前的权限模式保存在mstatus的MPP中,再把hart权限模式改为M。
2、执行trap_handler
3、退出中断 调用mret指令。在M模式下,mret指令会执行如下操作:
- 当前hart权限级别 = mstatus.MPP,mstatus.MPP = U(若hart不支持U则M)
- mstatus.MIE = mstatus.MPIE;(恢复中断使能,一般是打开的) mstatus.MPIE = 1
- pc = mepc
PLIC 介绍
如图所示:外设有很多种,可以触发不同种类的外部中断,而连接到hart的只有一个外部中断引脚,因此需要一个部件用来汇聚和解析各种外部中断
PLIC(Platform-Level Interrupt Controller)平台级中断控制器
PLIC左边连接所有的外设(中断源) 右边连接各个Hart 根据中断优先级来协调处理各个中断
这里设置UART串口设备的ID为10
PLIC 编程接口 - 寄存器
这里设置UART中断的优先级为1
这里将hart对应的UART中断使能打开
这里将hart的阈值设置为0 允许所有中断源上发生的中断
plic_claim函数,先获得hart号(此项目就一个0号hart),再通过Claim寄存器读出当前中断源ID
static inline reg_t r_tp()
{
reg_t x;
asm volatile("mv %0, tp" : "=r" (x) ); //记得最开始我们把hartid也写进了tp这个寄存器
return x;
}
plic_complete函数,将处理完的中断源ID写入Complete寄存器,通知PLIC对该路中断处理已完成。
PLIC 编程接口 - 操作流程
- 1号中断和2号中断进入PLIC,优先级分别为1和2。
- 判断Interrupt Enable寄存器的值,也是PLIC里面根据不同hart可以进行设置,是否打开该中断源的中断使能。可以看到对于1号hart这两个中断使能都是关闭的,因此两个中断都只能走0号hart。
- 根据Interrupt Priorities 2号中断先进入。
- 根据Interrupt Threshold 中断阈值,这里hart0的阈值设为0,因此2号中断都可以进入。
- 通过Claim寄存器,会读出2号中断源ID,进入hart0进行中断处理。且Pendding位被清除。
- 处理完之后通过写Complete寄存器,通知PLIC 2号中断处理结束。
- PLIC继续开始处理1号中断。
采用中断方式从 UART 实现输入
1、PLIC初始化 UART优先级为1 打开hart的UART中断使能 设置hart的中断阈值为0
2、中断处理函数 casue_code == 11 表示Machine模式外部中断
进入外部中断处理函数 读取当前中断源ID 进入UART处理函数uart_isr() 从控制台上读取一个字符并打印出来。