异常处理和RT-Thread

实现IOE后, 我们接下来在NEMU和NPC上实现CTE, 实现之后, 我们就可以运行不简单的操作系统了!

在NEMU中运行RT-Thread

在NEMU中实现自陷操作

根据PA讲义完成PA3阶段1, 直到你看到如下提示框:

温馨提示

PA3阶段1到此结束.

在NEMU中运行RT-Thread

根据PA讲义完成PA4阶段1, 直到启动RT-Thread. 后续Nanos-lite相关的内容暂时无需完成.

在NPC中运行RT-Thread

借助AM, 我们可以以很低的代价运行RT-Thread. 在硬件上, 我们只需要实现少数几个CSR, 几条CSR指令, ecallmret指令, 以及相应的异常响应机制即可. 这些你在实现NEMU的时候都已经有所了解了, 这里我们简单讨论如何通过RTL实现它们.

  • RISC-V中CSR寄存器的地址空间有12位, 即4096个, 但RISC-V已经定义的CSR只有300多个; 如果除去性能计数器和PMP(物理内存保护)相关的CSR, 则只有78个; 如果进一步只统计M模式的CSR, 则只有28个; 如果只考虑在NEMU中实现的那几个运行RT-Thread所必须的CSR, 就只剩下不到5个了. 因此, 我们完全没有必要把几百个甚至是4096个CSR全部实例化出来, 虽然这对NEMU来说只是占用一些内存, 但对NPC来说则是巨大的面积开销. 具体地, 我们只需要实例化需要用到的CSR, 然后根据CSR地址对它们进行读写即可.
  • 目前需要实现的CSR并没有什么特殊的副作用, 虽然RISC-V手册中对mstatus的介绍功能繁多, 但目前我们都不需要使用, 只需要正确初始化通过DiffTest即可.
  • 目前只会用到少数几条CSR指令, 但和一般的指令不同, CSR指令会原子地读写同一个CSR寄存器. 此外, 目前我们可以忽略CSR中每一个字段的读写属性 (如果你还没有听说过, 你需要仔细RTFM了), 包括WPRI, WLRL和WARL, 这些读写属性定义了往CSR字段写入非法值时的行为. 目前我们运行的程序不依赖于这些行为, 因此可以暂不实行这些读写属性.
  • ecallmret都会导致NPC发生跳转, 通过复用下地址逻辑的数据通路可以很容易实现.
  • 目前我们只需要实现ecall这一种异常, 它是一种需要NPC无条件响应的自陷异常, 只需要同时设置mcause和mepc, 然后跳转到mtvec中存放的异常入口即可.

在NPC中运行RT-Thread

在NPC中实现简单的异常处理机制, 并运行RT-Thread.

修复RT-Thread运行时不输出最后的命令提示符的问题

细心的你会观察到, 和NEMU不同, 在NPC上运行RT-Thread时, 最后的msh />并没有输出. 如果你对这个问题感兴趣, 你可以现在思考如何解决这个问题. 你也可以选择无视这个问题, 在后续接入SoC时, 你会再次遇到类似的问题.

RISC-V完整的异常处理机制远比我们现在实现的复杂, 而商业化的RISC-V处理器必须准确无误地实现手册中描述的每一个细节, 不管客户是否会用到. 实现所有CSR并细扣其中的每一个比特, 其实是一项偏工程属性的工作, 需要工程团队投入很多精力. 但"一生一芯"毕竟是一个教学项目, 目标并不是为了设计出满足商业交付需求的RISC-V处理器, 因此我们可以在正确运行展示程序的前提下对各种复杂的机制进行简化, 来让大家把精力集中在关键原理的学习和核心功能开发中. 随着将来在A阶段运行更多的程序, 我们也会逐渐添加各种CSR及其核心功能.

最近更新时间:
贡献者: Zihao Yu