文章目录
RISCV内部与中断相关的信号
- mcause_interrupt指示当前触发的是中断还是异常:1——中断、0——异常。
- mcause_exceptionCode[3:0] 标识中断异常编码,如下表所示。
异常编码 | 异常种类 |
---|---|
0 | 指令地址未对齐 |
1 | 取指失败 |
2 | 非法指令 |
3 | 断点 |
4 | 内存数据读取地址未对齐 |
5 | 内存数据读取失败 |
6 | 内存数据写入地址未对齐 |
7 | 内存数据写入失败 |
11 | 环境调用 |
中段编码 | 中断种类 |
---|---|
3 | 软件中断 |
7 | 定时器中断 |
11 | 外部中断 |
- mepc[31:0]存放异常情况发生时的PC值,作为中断处理程序结束后的返回地址。
- mtvec_mode[1:0]和mtvec_base[29:0]确定异常情况处理程序的地址,如下表所示。
- mstatus[3]为全局中断使能,当该位为0时,所有中断被禁止。
中断模式 | 异常情况处理程序的地址 |
---|---|
0 | PC = mtvec_base << 2 |
1 | PC = mtvec_base << 2 + 4 *异常/中断编码 |
2、3 | 未定义 |
中断及异常发生的具体流程
中断及异常发生的具体流程如下图所示。中断异常触发后,保存当前PC值至mepc寄存器,同时判断是何类型中断异常并保存至mcause寄存器。之后从mtvec寄存器获取系统初始化时预存的异常处理程序的地址,跳转至异常处理程序进行处理,处理完毕跳转到异常发生地址的下一条指令继续执行程序。图中的箭头即为程序的执行顺序。
RISC-V架构中断触发的条件
RISC-V架构中断触发的条件为:全局中断使能为1、相应类型的中断使能为1、相应类型的中断状态位为1。与此相关的信号包括:
- mstatus_MIE:全局中断使能位。该信号为零时,外部中断、定时器中断、软件中断都被禁止。
- mstatus_MPIE:全局中断使能保持/恢复位。中断发生时,该信号会记录MIE值,mret指令从中断返回时,将自身值重新赋值给MIE。
- mie_MEIE:外部中断使能位。
- mie_MTIE:定时器中断使能位。
- mie_MSIE:软件中断使能位。
- mip_MEIP:外部中断状态位。
- mip_MTIP:定时器中断状态位。
- mip_MSIP:软件中断状态位。
MIE、MPIE深层含义
RISC-V在处理中断/异常过程中,进入中断/异常处理程序之前,会先将MIE拉低,并将MIE旧值保存进MPIE中。退出中断/异常处理程序之后,将MPIE保存的旧值恢复至MIE中。 该操作其实是由于RISC-V架构定义的硬件机制无法支持中断嵌套行为,一旦响应中断进入异常模式后,全局中断使能被关闭,从而无法响应新的中断。
M()IE深层含义
RISC-V系统在最开始配置时,会确定不同类型的使能位。MTIE、MEIE、MSIE配置为固定值,并在处理器运行过程中始终不变。
M()IP信号深层含义
MTIP信号的深层含义为定时器模块传输给RISC-V处理器的定时器中断请求。RISC-V定义了两个64位寄存器:
- mtime:时钟定时器,以固定的频率做计数。
- mtimecmp:计数阈值,当mtime的值大于或等于该值,触发产生时钟中断请求。
其中mtimecmp由系统下发给RISC-V处理器,FreeRTOS以RISC-V的定时器中断为基准,通过配置mtimecmp寄存器,保证系统心跳的频率。具体配置过程如下图,首先不同应用任务被创建,创建完成后启动任务调度器,在启动任务调度器过程中,系统会初始化定时器中断,即配置mtimecmp寄存器。然后系统选取没有阻塞且优先级最高的一个任务执行。任务执行到mtime计数大于等于mtimecmp时,触发定时器中断。跳转到定时器中断处理程序继续执行。在处理程序中,系统会确定是否需要切换任务——是否有任务解除阻塞且优先级更高。这个过程中再次配置mtimecmp寄存器,开始下一轮的心跳。该过程的循环成就了FreeRTOS可以按照节拍多任务并行。
MEIP就是各种外部中断源经过PLIC(中断控制器)输入到RISCV的外部中断信号。
FreeRTOS内的中断服务处理程序
中断服务处理程序freertos_risc_v_trap_handler的具体流程如下图。
首先对触发中断之前的任务现场进行保存,并读取MIE旧值压进栈内。接下来判断是中断触发还是异常触发,异常分为环境调用和普通异常两类,中断分为定时器中断和外部中断两类。针对不同的中断/异常触发进行不同的程序处理,在处理环境调用和定时器中断时,会跳转到任务切换处理函数,系统会确定是否需要切换任务——是否有任务解除阻塞且优先级更高。最后将之前压进栈内的MIE旧值弹出赋值给MIE寄存器,恢复现场。
FreeRTOS利用定时器中断和环境调用两种异常实现其任务调度机制。
①定时器中断更新计数器的值,查看是否有等待延时的任务解除阻塞,如果有,则完成任务切换。
②任务调用ecall(环境调用指令)实现主动的任务切换,通知调度器立即进行任务切换,而不必等到当前任务的时间片耗尽。某个任务调用ecall等效于其自愿放弃运行态,如调用vTaskDelay()
FreeRTOS内的堆栈指针
FreeRTOS系统编译好的程序可能的内存空间分配如下图,两个指针MSP(主堆栈指针)和PSP(进程堆栈指针)在其中发挥着重要的作用。MSP指针用于操作系统内核及处理异常;PSP指针用于不同任务,每个任务有自己的任务堆栈,在任务创建时会创建指定大小的任务堆栈。
FreeRTOS系统在创建任务时,不仅会创建指定大小的任务堆栈,还会定义一个任务控制块结构体——TCB(Tack Control Block),用于存储任务的状态信息。TCB内重要信息包括:栈顶指针、状态列表项(处于就绪列表还是等待列表)、任务优先级等等。FreeRTOS的核心是确保处于优先级最高的就绪任务获得CPU运行权,任务切换函数会找出就绪列表中最高优先级任务的TCB,将其中的栈顶指针赋给当前栈指针。
关键
FreeRTOS的任务创建函数xTaskCreate中,将初始化任务堆栈保护起来放入临界区,临界区是指那些必须完整运行、不能被打断的代码段。进入临界区会拉低MIE的值关闭中断,退出临界区会置高MIE的值打开中断。这样可以保证任务堆栈内的MIE值初始化为0,防止第一次切换任务时有可能触发的异常中断紊乱。