关于我
欢迎来到我的博客!这里汇集了我对编程和技术的洞见和总结。本站内容分为几个主要类别,涵盖从具体技术实现到编程理念的广泛话题。
主要内容分类
- 项目工程:深入探讨技术的实现细节和理解。
- C/C++:围绕C/C++语言的技术点和编程技巧进行详细总结。
- 程序员哲学:分享程序员在职业生涯中应该具备的哲学理念和思考方式。
想要了解更多具体内容,您可以访问文章分类页面。
联系我
如果您有任何问题或想要交流,欢迎通过关于页面与我联系。
感谢您的阅读和支持,希望我的博客能为您的技术旅程带来帮助!
概述
文档详细描述了一个基于RISC-V指令集的异常处理流程,包括自陷(trap)与中断处理机制。文档首先介绍了通过一个示例程序test的intr.c实现中,如何初始化并模拟操作系统回调的过程。接着解释了CSR寄存器(如mstatus、mtvec)的处理,强调了逻辑地址与物理地址的区分和映射的必要性。另外,描述了yield函数如何触发系统调用并处理中断。最后,讨论了ecall指令、中断返回指令mret以及与difftest的对齐方式。
自陷的流程
执行一个有自陷指令的程序,比如test中的intr.c程序,在main函数中会有一个初始化过程.执行cte_init函数,并且传入一个回调函数,可以认为,这个回调函数是来自操作系统,当发生事件的时候,就会调用这个将事件和上下文作为参数的回调函数(认为进入了操作系统内部).
但是实际上这个回调函数是test给出的.
实现csrw
csrw的INSPART按照规矩来写就行了.
在do阶段,要知道,其是根据csrrw指令衍生出来的指令,csrw是一条伪指令.
csrrw中的rs1,rd是索引了寄存器堆,占用5位比特.
而csr占用12位比特,索引的是内存地址.
比如mtvec的标准riscv32中的内存地址是0x305.
如果真的在inst.c中实现Mr或者Mr,直接传入的是0x305,那么就会在in_mem函数中失败,这个不是标准的访问riscv32中的地址.
为何要直接映射地址访问csr:
在标准RISCV32世界中,低地址用于存储中断寄存器和,大小不是特别大.可以用代码块来模拟映射.如果因此来增加允许访问0x300和0x100起始的地址.那么就需要改动paddr.h相关的访问地址的映射以及地址检查代码.
csr实现
由于mstatus寄存器,ifndef RV64 4byte 0x300addr
mtvec寄存器,ifndef RV64 4byte 0x305addr
这里没有逻辑冲突,但是在定义64位RV时,就会前后覆盖.
这是一个疑问.
为何文档中要这样定义,而访问这些寄存器的时候,给出的csr12位编址只是逻辑地址,并不是物理地址空间.
所以根据以上事实,我们这样实现csr.
在isa-def.h中定义typedef struct CSRS;
在inst.c中:
1 | CSRS csrs = {.mstatus = 0x1800, .mtvec = 0, .mepc = 0, .mcause = 0}; |
这样就在inst.c中可以使用csrs了.
并且能够让DiffTest也支持异常响应机制.
yield
yield函数被调用,执行了两条嵌入的汇编指令,显示向a7中传入调用号:-1(目前,我也不知道为何要传入-1)(自陷指令,传入-1,如果是自陷系统调用,a0传入1)
然后调用ecall.
实现ecall,其在do阶段,调用raise_intr函数,将当前pc保存在mepc,失败号保存在mcause,将mtvec放入pc中.
cte_init函数分析以及自陷/中断跳转过程分析
cte_init函数是由test中的main函数中的代码调用,传入的是test中的intr.c中定义的回调函数.
在cte_init中嵌入一条汇编指令,获取了__am_asm_trap的地址,放入mtvec中.
csrw是伪指令,要实现其经过编译之后生成的中间代码的csrrw指令.
然后将回调函数记录到user_handler中.
当intr.c中执行了yield函数.
yeild函数嵌入了汇编:将a7赋值为-1,然后调用ecall.
在ecall中,我们就做上述要做的,然后跳转到了mtvec中的地址.
mtvec的地址
__am_asm_trap是定义在trap.S中的,是属于.globl段中的.
在这里,执行一系列指令,最后执行的就是跳转__am_irq_handle地址.
__am_irq_handle就是一个函数,这里做了一个局部变量Event,然后根据状态寄存器做一系列的操作.然后调用前面提到的回调函数.
在ecall指令的实现中,会根据当前的mstatus中的状态位对mcause赋值.
在__am_irq_handle中,在switch中,创建Event,然后做一些信息记录.
mtvec中的值,是不需要维护的,这是一个经过初始化之后,就不会改变的.这是异常处理入口的总地址.
实现个别csr指令
先实现ecall指令,然后在其中对mstatus进行测试.
文档后面要求,与difftest"对齐",所以mstatus初始化为0x1800,这是要将第11-12位初始化为11,就是默认进入机器模式.
所以要ecall中要检测这里的模式,然后如果是机器模式就将mcause设置为7,如果是Smode,就是9,如果是umode,就是11.
在__am_irq_handle中对mcause进行分析的时候,这里就目前先将其全部囊括在一个代码块下面,然后将Event变量的event设置为EVENT_YIELD.
上下文的保存,目前并不是我们做的,这里要分析trap.S和__am_irq_handle的代码.
上下文保存以及__am_irq_handle传参
传入的是Contex指针参数,这里的就是__am_irq_handle的地址跳转过去,然后把指针值放入a0寄存器中(传统的用于保存参数的寄存器),这里的a0,就是sp,保存了通用寄存器和几个状态寄存器的地址,可以通过a0进行访问.
同时呢,在__am_asm_trap中,对栈进行了维护,并且使用了宏,对通用寄存器压栈,但是没有见到对2号寄存器进行保存,0号寄存器没必要保存.
所以这里的上下文的保存,以及handle的传参进行了维护.最后弹栈和mret也进行了维护.
在__am_asm_trap中进行测试
在这里,对mcause进行测试,也顺便对mstatus进行测试.但是在此之前会执行trap.S中的指令,需要提前完成csrr,csrw指令的实现.
完成了指令的实现,这里需要对Context进行输出测试,用于确定确实完成了上下文保存.
ecall中的isa_raise_intr测试以及pc是否相加.
这里传入a7的值以及ecall指令的pc值.
在存储pc到mepc中,需要根据不同的情况来决定.有些中断,pc需要加4,然后返回的时候,直接从下一条指令开始就行了.在这里,存储ecall本身的地址就可以了.
然后测试.
关于pc<-SR[mtvec]
要想在下一次执行时,执行的是我们的异常程序入口地址处的指令,我们需要将cpu.pc更新到mtvec中的值.
但是在isa_raise_intr中,是无法做到这一步的.
因为参数列表就决定,我们并没有传入s的指针.
同时,s只是execute函数的局部变量,所以无法在intr.c中extern s.
而在cpu.pc在isa_exec_once函数之后,统一的由s->dnpc赋值.
所以,这里只能将mtvec返回.isa_raise_intr的返回类型也是相同的,满足条件.
然后在ecall指令的实现地方,将返回值赋值给s->dnpc.
mret
由于pa中不涉及特权的处理,并且这里还没有看到处理中断使能的代码,所以mret指令只处理pc的更新.
如果需要涉及到MPP,MPIE,到时候继续在mret代码中添加.
关于在ecall中需要添加的功能,就在isa_raise_intr函数中添加.
difftest
在pa中,mstatus初始化为0x1800,表示这是M-mode,而在M-mode中触发的异常,mcause会被设置为7.
但是在difftest中,这是在U-mode中,所以difftest中的mcause是11.
所以我们应该初始化这个mstatus为0x0000
但是继续执行下去,会出现mstatus的错误.
debug:这里就是表明,difftest中,默认就是M-mode,而不是U-mode,但是其根据不同模式下触发的异常,mcause是反过来的.(有可能我是反过来的).
所以这里就需要在mstatus初始化时修改回0x1800,然后在raise函数中做反过来的处理.
注意:这里并没有检查通用寄存器保存的正确性,只是在添加了在bad时的isa_reg_display打印,大致上是正确的.并不保证.
这里没有经过严格的验证.
debug:发现之前在实现ITRACE的时候,在最后的输出信息时候,没有条件编译.
添加etrace
需要向往常一样,在kconfig中添加.
在__am_irq_handle这里做条件编译,测试,输出异常类型和关键寄存器即可.xxxx
在这里回想其之前在调试中遇到的bug,以及文档中etrace提到的,这里应该是在硬件中实现(nemu),而不是在am中.
因为am中的printf,是映射到了底层的putch.而在nemu中的printf就是标准库里的printf.
所以这里为了避免交替打印的现象,最好是在isa_raise_intr中实现etrace.
If you like this blog or find it useful for you, you are welcome to comment on it. You are also welcome to share this blog, so that more people can participate in it. All the images used in the blog are my original works or AI works, if you want to take it,don't hesitate. Thank you !