FPGA開発日記

カテゴリ別記事インデックス https://msyksphinz.github.io/github_pages , English Version https://fpgadevdiary.hatenadiary.com/

例外が発生した場合のステータスの処理

http://bar.eecs.berkeley.edu/projects/images/projects/2010-riscv.png

画像: UCB-BAR: RISC-V Instruction Set Architecture より。

これまでいくつかのパタンでRISC-VのISSでPASSしていなかったものがあるのだが、今日は久しぶりにその修正をしていた。

RISC-Vには、例外が発生した場合の処理について、いくつかのパタンが存在する。

  • 命令が不正な動作をした場合
  • ECALL命令によりマシンモードに移行する場合

例外発生時のMSTATUSの扱い方について

命令実行中の例外、例えばミスアラインのメモリアクセスであるとか、権限の無いシステムレジスタへのアクセスが発生すると命令は例外を発生する。 一般的に例外の処理方法として、

  1. 例外発生 → 上位の動作モードへ移行
  2. 上位の動作モードで適切に処理する
  3. 元の動作モードに戻ってくる

となる。上位の動作モードに移行するとき、RISC-VではMSTATUSというシステムレジスタを操作する。 複数回の割り込みを許可するために、MSTATUSレジスタには権限の状態を複数個格納し、PUSHしたりPOPする仕組みがある。

まず、例外が発生すると、MSTATUSの下位16bitを左に3ビットシフトする。 この実行権限の情報は最大で4つまで格納することができ、これは4回までの割り込みのネストを許可できる。

f:id:msyksphinz:20160508223054p:plain

例外から戻るとき、MSTATUSを参照し、MSTATUSの下位16bitを右に3ビットシフトする。下位のMSTATUS[2:1]が、現在の動作モードを意味する。

スーパバイザモードでERET(例外からの復帰)命令を実行した場合

面白いことに、スーパバイザモードでの状態を格納するために、MSTATUS以外にSSTATUSというシステムレジスタも存在する。 これは、スーパバイザモードに移行したときの状態を保存しており、ERET(例外状態からの復帰命令)を実行した際にどの状態に戻ればよいかを示している。

f:id:msyksphinz:20160508223403p:plain

SSTATUSにはPSビットが存在しており、これはどのモードからスーパバイザモードに移行したかを示している。 すなわち、スーパバイザモードにおいてERETを実行した際に、PS=1であればスーパバイザモードそのものに戻り、PS=0であればユーザモードに戻る。

マシンモードからスーパバイザモードへの権限移譲命令について

RISC-Vには、アーキテクチャとして4つの権限が定義されている。

  1. マシンモード
  2. ハイパーバイザモード
  3. スーパバイザモード
  4. ユーザモード

マシンモードは他のモード実行中に例外が発生した際に移行されるモードだが、とりあえずマシンモードに移ったのちに、例外をハイパーバイザモードで処理するか、スーパバイザモードで処理するかを選択できる。 これを実現するためのMRTS、MRTH命令が存在する。

MRTS (Machine Redirect Trap to Supervisor), MRTH (Machine Redirect Trap to Hypervisor) 命令について

f:id:msyksphinz:20160508223910p:plain

マシンモードからスーパバイザモード、ハイパーバイザモードへと移行するための命令だ。 詳しくは仕様書を見て欲しいのだが、スーパバイザモード(MRTS)の場合、

  1. マシンモードからスーパバイザモード(MRTS)へと移行する
  2. PCをSTVECから取り出して設定する。
  3. MEPC, MCAUSE, MBADADDRをSEPC, SCAUSE, SBADADDR にコピーする

という命令だ。これにより、例外情報をスーパバイザ用の各種システムレジスタに移したうえで、例外処理を行うことができる。

MSTATUSの実行状態PUSH/POPの実装

例外が発生すると、MSTATUSの下位16bitを3ビットシフトして、新しいモードを下位3ビットに挿入する。

github.com

+void RiscvEnv::PushPrivMode (PrivMode priv)
+{
+  UDWord_t curr_mstatus;
+  CSRRead (SYSREG_ADDR_MSTATUS, &curr_mstatus);
+  UDWord_t next_mstatus = ((curr_mstatus << 3) | (static_cast<UDWord_t>(priv) << 1)) & 0xffff;
+  next_mstatus = next_mstatus | (curr_mstatus & 0xffff0000);
+  CSRWrite (SYSREG_ADDR_MSTATUS, next_mstatus);
+}
+
+