CPU-对于异常的一些理解

  1. 异常的分类和常见类型
  2. 精准异常
  3. 异常处理的一些设计

异常的分类和常见类型

在CPU设计中, 除了分支指令, 异常也可以打断程序的执行. 异常大致会有下面这几类:

  • CPU外部引起的异常, 这种异常也叫做中断(Interrupt), 中断本质上和CPU内部执行的指令没有关系, CPU在任何时期都可能会出现中断, 中断也叫做异步异常.
  • 虚拟地址转换时出现的异常:
    • 如果TLB Miss, 那么会出现异常.
    • 如果转换关系在页表中不存在, 出现Page Fault.
    • 程序出现了被保护的页, 访问权限异常.
  • 执行指令自身会出现异常, 例如trap指令, OS需要这些指令实现系统调用.

精准异常

在CPU中, 所有异常的处理过程都是一样的, 当出现异常时:

  • 出现异常之前的指令都必须执行完成.
  • 产生异常, 以及产生异常之后的指令都不允许完成.
  • CPU会跳转到Exception Handler执行.
  • 从Exception Handler返回后, 需要重新执行导致异常的指令, 但不是绝对的, 需要根据异常的类型决定.

如果能够实现以上四步, 那么在CPU外部看来, 异常就和没发生过一样, 这种方式称为精准异常(precise exception).

异常处理的一些设计

对于CPU来说, 一般有一个寄存器保存发生异常的指令pc, 一般叫epc.

还有一个寄存器保留发生异常的类型信息, 一般叫ecause.

在异常处理程序中, 又可能会修改通用寄存器的值, 因此需要提前将这些寄存器的值保留到栈上.

在流水线上, 任何阶段都有可能出现异常:

  • Fetch:
    • I-TLB Miss.
    • Page Fault.
  • Decode:
    • 未定义的指令编码.
  • Execute:
    • 除0.
    • 溢出.
  • Memory:
    • D-TLB Miss.
    • Page Fault.
    • 地址没对齐.

假设这个流水线是顺序执行的, 那么对于异常的处理步骤如下:

  • 首先, 我需要在流水线中找到一个异常处理点(Commit Point), 要求这个处理点之后, CPU不会再发生异常.
    • 例如在五级流水线上, Memory阶段之后, 后面就不会再发生异常.
    • 所有异常都会在到达流水线的异常处理点之后再进行处理.
  • 每个指令的PC值都会随着流水线进行移动, 如果出现异常, 会在Commit Point中将PC写入epc寄存器.
  • 流水线中, 如果某个阶段出现异常, 那么异常信息也会随着流水线进行移动, 并且在Commit Point中写入ecause寄存器.
  • 当到达Commit Point时:
    • 首先, 把引起异常的指令从流水线中Flush, 不要让它进入Write Back阶段.
    • 之后, 将后面已经取出的指令, 以及这些指令与异常有关的流水线寄存器Flush.
    • 然后, 写入epc和ecause寄存器.
    • 然后, PC变成epc, CPU取出异常处理指令.

Exception Pipeline