除了上述的寄存器,移植还要关心的是几个csr寄存器mstatus,mepc。正常情况下大部分csr只能在机器模式下操作(WCH的v3和v4内核支持机器模式和用户模式)。mstatus中,MIE为中断使能,当进中断时MPIE更新为MIE,返回时MIE更新为MPIE。MPP用于保存进中断之前的特权模式,如果我们设置其为MPP=0b11,那么将一直处于机器模式,其mret返回后还是处于机器模式。mepc是机器模式下异常程序指针,其只会在发生异常是被更新(中断也是一类异常),进异常时我们可以从另外两个csr寄存器mcause来看引起异常原因通过mtval查看引起异常时的值。当从异常返回时mepc的值被更新给pc。我们正是通过进中断修改mepc来实现任务的切换的,后面会详细说明这个过程。
实时操作系统大家应该不陌生,常见的uCOS,FreeRTOS,RT-Thread,LiteOS-M等等,其基本的思路都是一样的,需要一个定时器用于系统时间片的实现,一个中断用于任务切换。想要其能够在一个MCU上成功的跑起来,需要弄清除一下几个事情:
(1)进中断需要保存哪些内容。
从之前的描述中,应该知道,对于risc-v内核来说其进中断压栈的是caller saved的寄存器。从下图一可以看出,进Systick中断函数,先进行寄存器保存,退出中断时进行寄存器恢复,如果开启硬件浮点,同时还会对浮点寄存器进行保存和恢复。这个过程是编译器帮我们实现,有一点需要注意的是我们移植的代码里面进中断后获取了中断的堆栈“csrrw sp,mscratch,sp”,返回时恢复了线程的堆栈指针“csrrw sp,mscratch,sp”中断堆栈指针初始值是在任务开始时存入mscratch寄存器的,如果采用C形式中断函数,中断堆栈的获取会在压栈操作之后,中断压入的堆栈是当前运行任务的任务堆栈区域,如果想要中断函数压栈时压入的自己的堆栈区域,可以使用汇编入口,进中断后先修改sp,然后压栈,再调用中断处理函数,如图二所示。
|