中断的硬件实现
中断的硬件逻辑
中断的硬件实现(寄存器级描述)
硬件分为3个部分:
- CSR寄存器部分(保证处理器核在硬件上支持中断)
- CLINT(终断控制、终断优先级等部分)
- PLIC(用于控制外部中断)
中断介绍
- 中断源、中断请求
- 中断服务程序(Interrupt Service Routine, ISR)
- 保存现场、恢复现场
- 中断优先级(每个中断源配备级别寄存器、优先级寄存器)、中断仲裁
TODO:
看6.2.9节关于优先级定义的部分
基础介绍
外部信号进入芯片后,通过中断控制器中的“使能+信号+优先级+仲裁”等逻辑,最终输出到 MCU
- 在riscv中一共定义了三种状态中断,对于hart层面,hart包含local中断源和global中断源。 而local中断只有Timer和Software中断两种,而global中断则称为external interrupts。
- 只有global中断源可以被PLIC(Platform-Level Interrupt Controller) core响应,通常为I/O device; 一般来说,timer和software是通过CLINT(CORE LOCAL INTERRUPT),而外部中断通过PLIC处理; PLIC的实现是独立于hart的IP设计

由于PLIC的使用是针对外部中断的,所以可以单独设置每个中断。可以设置如下的值:
- 中断的优先级priotity
- 中断挂起位pending
- 中断使能enables
- 中断阈值priority Thresholds
常见的同步错误(异常)有如下四种:
- 访问错误:访问了不该访问的地址空间,例如尝试写入到ROM
- 执行到了ecall, ebreak指令
- 在ID发现指令非法
- 地址不对齐: RISC-V其实没有强制支持非对齐访存,因此不支持非对齐访存的处理器,存在该异常
Notes: 所有 RISC-V 系统的共同问题是如何处理异常和屏蔽中断
根据RISC-V的架构定义,处理器当前的Machine Mode或者User Mode并没有反映在任何软件可见的寄存器中(处理器内核会维护一个对软件不可见的硬件寄存器), 因此软件程序无法通过读取任何寄存器而查看当前自己所处的Machine

时钟中断
- 触发条件:
mtime>=mtimecmp - mtime每个cycle自增1,当满足触发条件的时候,由CLINT产生timer中断
- 由
mie寄存器的MTIE字段使能该中断、由mip寄存器的MTIP来指示timer中断是否在pending - timer 的寄存器在 timer 设备里,不在 CPU 中,是通过 MMIO 的方式映射到内存中的;都可以按照内存映射的方式被读写
- 满足时钟中断触发条件之后,对应的处理函数会将
mtimecmp+=inerval,这样触发条件又不满足了 TODO: ask 我们系统是不是没有实现时钟中断?我们是不是不需要时钟中断
软件中断
- 定义:软件触发的中断、主要指核间中断IPI(internal processor Interrupt)
- 通过写入
mip寄存器的MSIP字段来触发
外部中断
中断处理操作
硬件操作
- 更新pc,改变指令流
1
2mepc= mcause[31] ? pc_next : pc; // 0->中断,1->异常
pc=mtvec[1:0] ? Base+4*Cause : Base; - 设置中断原因到mcause寄存器
- 更改中断权限
1
2mPIE=mIE; # 存储trap发生之前的中断使能位
mIE=0 # 关闭中断 保存处理器特权级: 只有m特权级就不用管这个字段
软件操作(中断处理程序)
保存32个通用寄存器到堆栈: mscratch寄存器通常被用作指向附加临时内存空 间的指针,通过该指针就可以知道通用寄存器该被压栈的位置
执行中断处理程序
从栈上恢复通用寄存器的值

附录:必须实现的8个ÇSR寄存器
- mstatus:指令处理器核的状态信息
- MIE是对应特权级下全局中断使能位
- xPIE, xPP, xIE举例:从s特权级产生中断进入到m特权级时
1
2
3mPIE=mIE; # 存储trap发生之前的中断使能位
mPP=s # trap发生之前,处理器所处的特权级
mIE=0 # 关闭中断
- mtvec:
- 可以配置向量模式跟非向量模式,向量模式下中断响应最快
- 非向量模式下(参考该文章5.13):
- mtvt2[0]==0: 中断跟异常都通过mtvec指定地址
- mtvt2[0]==1: 中断通过mtvt2指定地址
- mepc
- mcause:指令trap的原因;mcause[31]==1表示是外部中断、否则是异常;
mcause代码参考该文章的3.4.2节

trap发生的时候,mcause会被硬件写入,记录trap发生的原因 - mie: 针对各种类型的指令,指明当前处理器会处理哪些中断、忽视哪些中断
- mip: 列出目前正准备处理的中断
- mtval: 保存了陷入(trap)的附加信息,当触发硬件断点、地址未对齐、access fault、page fault 时,mtval 记录的是引发这些问题的虚拟地址;如果是由非法指令造成的异常,则将该指令的指令编码更新到mtval寄存器中
- mscratch: 暂时存放一个字大小的数据 > 将所有三个控制状态寄存器
虑,如果
mstatus.MIE = 1,mie[7] = 1,且 mip[7] = 1,则可以处理机器的时钟中断
PLIC(Platform-Level Interrupt Controller)
CLIC(Core-Local Interrupt Controller)
- 作用:用于多个内部中断、外部中断进行仲裁;支持嵌套中断;向core发送中断信号
- 中断目标: 生成一根中短线发送给Core,这根线就称作为中断目标

- 中断源:
- RISC-V默认支持4096个中断源
- 每个中断源有如下参数:
- 编号(ID): 0~18被预留为内核使用,其余的中断ID从19开始
- 使能位(IE)
- Pending位(IP)
- 级别跟优先级(Level and Priority)
- 向量或非向量处理(Vector or Non-Vector Mode)
- 阈值:仲裁出的中断源,其Level必须大于阈值,才会最终被发送给Core
- 仲裁原理:
- 中断源参与仲裁的条件:
enable & pending ==1 - 仲裁原理:先比较Level,再比较Priority,在比较ID(数字都是越大越好)
- 中断源参与仲裁的条件:
我们的实现
CORE的硬件支持
PS: 目前CORE可以顺序的执行I-MEMORY里的指令,并且可以对BRANCH、JUMP指令正确地跳转到对应PC,
现在需要增加对中断的支持;中断=中断(INTERRUPT)+异常(EXCEPTION)
CORE对中断进行处理,需要增加如下的功能:
- 接受中断发生的信号
- 中断发生时,跳转到对应的处理程序的能力
- 辨别不同类型的中断的能力
PS: 对中断处理的主要动作是当中断发生的时候->停止当前的工作流->按照中断的类型完成对应的处理->恢复之前的工作流。
CORE需要提供的硬件支持
为了实现上述3种功能,CORE需要在原来五级流水线的基础上增加如下的硬件支持
接受中断信号
CORE需要接受一个中断发生的信号,以通知CORE当前发生了中断
具体指ID STAGE需要接受信号、知道中断发生了,因为ID STAGE负责PC的更新
- RISC-V规定了3种类型的中断,分别是:外部中断、软件中断、定时器中断,其来源会在下文
MCAUSE部分介绍
🌟CSR寄存器及其操作
记录中断的发生:
中断使能位在
CSR.MIE寄存器中配置- CSR.MIP寄存器用于记录各种PENDING的中断
- PLIC检测到外部中断之后,会发送一个
NOTIFY信号到CSR单元,表明外部中断发生; 导致CSR.MIP[11]=1; - TIMER触发时钟中断之后,也会发送一个
NOTIFY信号到CSR单元,表明时钟中断发送; 导致CSR.MIP[7]=1; - 软件中断(通常用于向另一个核发送),此时可以通过MMIO的方式往另一个核的CSR单元写入;
导致
CSR.MIP[3]=1;
- CSR.MIP寄存器用于记录各种PENDING的中断
判断中断发生
如上所述:3种中断发生之后,都会通过硬件在1个CYCLE内记录到
CSR.MIP寄存器中,现在可以对中断进行处理了- 中断判断在
TRAP_HANDLER单用中完成(为了代码结构清晰),它通过输入CSR.MIP, CSR.MIE信号,判断中断是否发生; 若发生中断,则会将TRAP信号拉高,该信号会被ID STAGE使用,TRAP=MIP[X] & MIE[X], X=3,7,11 TRAP信号会发送给CORE跟CSR单元,完成相应的硬件操作以支撑中断
- 中断判断在
记录中断的类型&更改处理器状态
- 在中断发生的时候,硬件会自动更新CSR.MCAUSE寄存器,保存中断的原因
- 例如“外部中断发生”之后,
CSR.MCAUSE寄存器的EXCEPTION CODE字段会被写入0X80000800 - 中断处理程序(INTERRUPT SERVICE ROUTINE,
ISR)通过查询
CSR.MCAUSE,就可以知道中断的原因,进而做想要的操作 - 中断判定之后,需要修改
CSR.MSTATUS寄存器以更改处理器状态, 硬件默认会将CSR.MSTATUS.MPIE=CSR.MSTATUS.MIE, CSR.MSTATUS.MIE=0,默认不支持中断嵌套 - 中断嵌套:
进入ISR后,可以通过CSR指令(如CSRRW)将
CSR.MSTATUS.MIE置1从而再次打开中断,实现中断嵌套
- 在中断发生的时候,硬件会自动更新CSR.MCAUSE寄存器,保存中断的原因
跳转到ISR执行
中断处理核心的工作就是更改PC,从而跳转到ISR并且从ISR返回
CSR.MTVEC存放ISR的地址: 上面的TRAP信号被拉高之后,ID会将PC_INSTR更新为MTVEC的值,起到跳转到ISR的功能;CSR.MTVEC会在系统初始化的时候被CSR指令写入- 记录ISR返回的PC到
CSR.MEPC中:MEPC = INTERRUPT ? PC_NEXT : PC; - 在ISR中,需要通过指令来保存上下文到D-MEMORY(栈区),栈指针有
CSR.MSCRATCH寄存器给出
从ISR返回
中断处理结束之后,ID需要将PC更换为MEPC的值,从而继续之前的任务
- 从ISR返回的条件是执行到了
MRET指令(在此之前,ISR已经通过指令完成了上下文的恢复才会调用MRET指令) - ID
STAGE会将PC替换为
CSR.MEPC的值,并且CSR相关的STATUS,MIP寄存器会被硬件恢复为中断之前的状态 - PLIC同样需要接受MRET信号,从而判定当前中断已经处理完成、去仲裁新的外部中断
- 从ISR返回的条件是执行到了
中断发生时,硬件自动完成的操作
有了上述硬件支持之后,中断发生之后,下述操作会有硬件自动完成:
- 异常指令的 PC 被保存在 MEPC 中,PC 被设置为 MTVEC
- 根据异常来源设置 MCAUSE
- 把控制状态寄存器 MSTATUS 中的 MIE 位置零以禁用中断,并把先前的 MIE 值保 留到 MPIE 中
- 发生异常之前的权限模式保留在 MSTATUS 的 MPP 域中,再把权限模式更改为 M(咱们只实现了M模式,故这一步可以省略)