RISC-Vには、32ビットと64ビットの実装が定義されている(ちなみに128ビットもある)が、それぞれにおいて実装の違いが存在する。 それにより、ISSの挙動も変わるし、ちょっとした違いというか、ビット長が異なることにより様々な取扱も変わってくる。そのあたりについて調査してみよう。
RISC-Vにおけるシフト命令の挙動の違いについて
RISC-Vには、32ビット用の論理シフト命令と64ビット用の論理シフト命令が存在する。 32ビット用の論理シフト命令は最大で32ビットしかシフトできないが、64ビット命令では64ビットまでしかシフトできない。 しかし、命令の定義としては64ビットまでシフトが許されているようだ。つまり、コンパイラだと32ビットモードを指定してもシフト量において32以上の値を指定できる。
- 32ビットモードにおける命令定義
- 64ビットモードにおける命令定義。ビットフィールドは一緒だが、シフトできる量に違いがある。32ビットモードで32以上のシフトをすると、コンパイラでは通してしまうのだが、実際には命令定義違反となる。
という訳で、そのあたりでパタンが通らなかったのだが、RISC-VのMLに聞いてみると上記のような定義になっているらしい。そこを訂正すると通るようになった。
おまけ: 動作モードを変更できるようにしてテストパタンを通過させる
1. タイマーの追加
ついでに、テストパタンを通過させるために、いくつかISSを改良しよう。とりあえず、タイマーを追加した。RISC-Vにはシステムアーキテクチャとしてタイマが定義されており、mtime/stime/time (マシンモード、スーパバイザーモード、ユーザモードにて定義)と、mtimecmp/stimecmp/timecmp レジスタが定義されている。 timeレジスタはコンスタントにカウントアップしており、timecmpレジスタと値が一致すると割り込みが発生するという訳だ。(各動作モードにおいてカウンタが回るのか、それとも別の動作モードのときは停止しているのか、あるはカウントアップが続いているが割り込みは発生しないのか、については良く分からない)。
- mtime/mtimecmpレジスタの定義。まあそのままである。
このレジスタを実装し、タイマを動作させることにより、timer系のパタンも通過できるようになった。
void RiscvEnv::CountUpTimer (void) { uint32_t sysreg_time_addr, sysreg_timecmp_addr, sysreg_mie_addr; uint32_t tie_pos; switch(GetPrivMode()) { case PrivUser : // sysreg_time_addr = SYSREG_ADDR_TIME; break; case PrivSupervisor : sysreg_time_addr = SYSREG_ADDR_STIME; sysreg_timecmp_addr = SYSREG_ADDR_STIMECMP; sysreg_mie_addr = SYSREG_ADDR_SIE; tie_pos = 5; break; case PrivHypervisor : // sysreg_time_addr = SYSREG_ADDR_HTIME; // sysreg_mie_addr = SYSREG_ADDR_HIE; tie_pos = 6; break; case PrivMachine : sysreg_time_addr = SYSREG_ADDR_MTIME; sysreg_timecmp_addr = SYSREG_ADDR_MTIMECMP; sysreg_mie_addr = SYSREG_ADDR_MIE; tie_pos = 7; break; } UDWord_t next_timer; CSRReadNoTrace(sysreg_time_addr, &next_timer); next_timer++; CSRWrite (sysreg_time_addr, next_timer); UDWord_t mie; CSRReadNoTrace (sysreg_mie_addr, &mie); if ((mie >> tie_pos) & 0x01) { UDWord_t timecmp; CSRReadNoTrace(sysreg_timecmp_addr, &timecmp); timecmp += 100; if (timecmp == next_timer) { GenerateInterrupt (Int_Timer); } } }
2. 動作モード制限の追加
これについては良く分かっていないのだが、どうやらテストパタンには動作モードの制限があるらしい?どう頑張ってもパタンを通過させることができないようなものもある気がするのだが、 例えばマシンモードのみ実装されているようなテストパタンについては、スーパバイザーなどのモードが実装されていないことを前提にしている気がする。 確かに、この実装は別に違反ではなく、上位のモードが実装されていれば、それで十分というのはある。
という訳で、「マシンモードしか存在しない」「スーパバイザーとマシンモードしか存在しない」などのモードを追加してみよう。オプションに、最大で使えるモードを指定できるようにする。 デフォルトはユーザモードだ。つまり、マシンモードからスーパバイザ、ハイパーバイザ(定義はないけど)、ユーザモードまで全て利用することができる。 最大利用モードをマシンモードに制限すると、マシンモードからスーパバイザの遷移は許されない。ここの実装は良く分かっていないのだが、とりあえずマシンモードのまま割り込みに飛んでいくと辻褄が合っているような気がする。
#ifdef ARCH_RISCV DEFINE_string (maxpriv, "U", "Operation Mode M/H/S/U"); #endif // ARCH_RISCV .... #ifdef ARCH_RISCV PrivMode maxpriv; if (FLAGS_maxpriv.length() != 1 || (FLAGS_maxpriv[0] != 'U' && FLAGS_maxpriv[0] != 'S' && FLAGS_maxpriv[0] != 'H' && FLAGS_maxpriv[0] != 'M')) { std::cerr << "--maxpriv should be M/H/S/U\n"; exit (EXIT_FAILURE); } else { maxpriv = FLAGS_maxpriv[0] == 'U' ? PrivUser : FLAGS_maxpriv[0] == 'S' ? PrivSupervisor : FLAGS_maxpriv[0] == 'H' ? PrivHypervisor : PrivMachine; } #endif // ARCH_RISCV ... #ifdef ARCH_RISCV std::unique_ptr<RiscvEnv> env = std::unique_ptr<RiscvEnv>(new RiscvEnv (g_debug_fp, maxpriv, en_stop_sim, FLAGS_debug, FLAGS_bit_mode)); #else // ARCH_RISCV
このように、RISC-Vのマシンを作成するときは、最大ユーザモードが指定できるようになった。
テストパタンの通過状況
97% tests passed, 11 tests failed out of 434 Total Test time (real) = 4.72 sec The following tests FAILED: 1 - rv32mi-p-csr (Failed) 9 - rv32si-p-csr (Failed) 47 - rv32ui-pm-lrsc (Failed) 125 - rv64mi-p-csr (Failed) 126 - rv64mi-p-dirty (Failed) 130 - rv64mi-p-mcsr (Failed) 136 - rv64si-p-csr (Failed) 230 - rv64ui-pm-lrsc (Failed) 233 - rv64ui-p-mulhsu (Failed) 315 - rv64ui-pt-mulhsu (Failed) 401 - rv64ui-v-mulhsu (Failed) Errors while running CTest
あともう少しだね!