C5 异常处理和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中添加CSR

在RISC-V中, 有一类用于标识处理器状态的系统寄存器, 称为控制状态寄存器(Control Status Register, CSR). 和通用寄存器不同, 一般的计算类指令无法直接访问CSR. 因此, RISC-V还提供了用于在CSR和通用寄存器之间交换数据的CSR指令. 和一般的指令不同, CSR指令会原子地读写同一个CSR寄存器.

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是mcycle, 它是一个每周期增加1的计数器. 有了它, 我们就可以根据处理器的频率将周期数换算成时间, 从而实现AM时钟相关的功能. 根据RISC-V手册的定义, mcycle本身是一个64位的计数器, 在RV32中, 由于通用寄存器的位宽是32位的, 因此需要将这个计数器拆成mcyclemcycleh这两个32位的CSR进行访问.

添加mcycle

具体地, 你需要在NPC中实现csrrw指令, 并添加mcycle. 为此, 你需要阅读RISC-V特权架构手册, 从中找到mcyclemcycleh的编号等信息.

实现后, 尝试通过内联汇编多次读出mcycle寄存器, 检查其值是否自动递增.

为了标识不同同学的NPC, 我们可以利用CSR中的标识寄存器. 具体地, RISC-V中定义了mvendoridmarchid这两个CSR, 我们可以利用它们存放一些标识信息. 之后, 程序在运行时刻可以通过CSR指令将这些标识信息读到通用寄存器中, 并进一步打印这些信息.

添加学号CSR并输出学号

具体地, 你需要在NPC中实现csrrw指令, 并添加如下两个CSR:

  • mvendorid - 从中读出ysyx的ASCII码, 即0x79737978
  • marchid - 从中读出学号数字部分的十进制表示, 假设你的学号为ysyx_22068888, 若将读出的信息解释为整数, 则应为22068888, 即0x150be98

实现后, 可在TRM进入main()函数前, 通过内联汇编读出上述两个CSR的值, 然后通过printf()输出它们.

在NPC中运行RT-Thread

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

  • 目前需要实现的CSR并没有什么特殊的副作用, 虽然RISC-V手册中对mstatus的介绍功能繁多, 但目前我们都不需要使用, 只需要正确初始化通过DiffTest即可.
  • 目前我们可以忽略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及其核心功能.

申请C阶段结业考核

恭喜你! 你已经完成了C阶段的全部内容. 你可以申请C阶段结业考核来检验学习成果, 如果通过考核, 我们将给你发放证书.

不过这并不是强制的, 你也可以继续往后学习, 但你只有通过C阶段结业考核后, 才能参加B阶段流片考核.

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