lynxeyedの電音鍵盤

MBDとFPGAと車載で使うデバイスの備忘録

Chiselを使ったRISC-Vの勉強(12. riscv-testsの全項目クリア)

riscv-testsクリア

riscv-testsのリグレッションテストを全てクリアしました。(fence.i命令は除外しました。RISC-Vの基本Iアーキテクチャでは必須ではなくなったからです。)


f:id:Lynx-EyED:20200725162528p:plain
クリアしたからと言って「バグがない」とは言えないので、ランダム実行テストをする必要があり現在テスト中です。それは追々記事に。
テストがfailしたもののほとんどはCSRに起因し、CSRレジスタフォワーディングの実装の間違いに起因するものでした。
これはトレース結果を見ればすぐにわかるので修正→全テストのやり直しを繰り返せばクリアできるものが仕上がっていきます。

ここでは覚書としてクリアするのに1日以上かかった項目を挙げていきます。

CSRの実装 ≒ 例外の実装

intel FPGAに実装する際、カスタム命令を実行するコプロセッサを接続する予定なのでCSRを結局外してしまうのですが、ISAに定められている例外を正しく実装する事はコプロセッサを作る際の参考にもなるため、CSRをできる限り正しく実装することには意味があると言えます。
RISC-Vはフラグを持っていないCPUなので例外に頼る必要もあると思われます。

躓いていたテストは以下の実装でした。

  • rv32mi-p-illegal
  • rv32mi-p-ma_addr
  • rv32mi-p-ma_fetch
  • rv32mi-p-shamt
  • rv32mi-p-illegal

illegalに関しては今回はRV32Iのみのアーキテクチャであり、RVC(圧縮命令)の実装をしていないので、16bit命令が来た際に正しく不正命令例外を発生させればよくクリアできました。

  • rv32mi-p-ma_addr

load/storeのミスアラインアドレスのテストです。
ここで例外が発生したときにmepc(発生時のpc)に加えて、mtval(発生する要因となった命令そのもの)を格納する実装をしました。

  • rv32mi-p-ma_fetch

CSRモジュールはEXステージの時のPC,ALUへの入力値(rs1,rs2,immなど)を与える実装にしています。ミスアラインが発生するのは分岐命令時です。分岐先に指定されているアドレスが不正なために例外が発生します。

  • jal, jalrは条件なく分岐
  • branch命令は条件成立で分岐

これが少し厄介な状況を生み出します。jal/jalr命令は分岐先を調べて即例外を発行できるのに対し、branchは少なくともmemステージ(=ALUから比較結果の回答待ち)まで待たないといけません。ですので以下のような対応にしました。

  • jal, jalrは不正なアドレスを確認したら即例外発行
  • branch命令はpcと命令を一旦仮に格納(mepc,mtval)→分岐後不正なpcであった場合仮格納していたpcと命令を復元し例外発生

としました。ここで、「jal/jalrもいっしょの挙動にすりゃいいじゃん」または「CSR動作中は本体CPUストールすれば?」となります。
後者は検討中です。CSR自体もパイプライン動作をしているので「CSRモジュールが動作中か否か」という条件を洗い出す作業とALUをCSRに引き渡すロジックの追加が必要になります。
前者は容易に思いつきますができません。理由は以下の通りです。

 jal rd, <jump_address>

とした場合、rdにはこの命令の次のpcが格納されます。不正なアドレスにジャンプした後に例外判定をすると、rdが更新された後になってしまうため、rdの値をこの命令実行前の状態に復元する必要があります。
できなくはないけどする必要は全くないですし、いらない32bitのレジスタと配線を増やしてFPGAの動作を低下させる必要はないですね。
この命令もそうですが、パイプラインのなるべく早い段階で例外を発見し処理すると不要なパイプラインレジスタを実装せずにすみ、論理回路消費を抑え、高速化に寄与します。

  • rv32mi-p-shamt

シフト命令で(※純粋なシフト命令のみのテストではない)テストパスできないなんてあるのか楽勝じゃん?とおもったら最後までPassできずに泣いていました。
躓いていたのはこの命令。

slli	a0,a0,0x20 // a0 = a0 << 32

32bitシフトしたらゼロにすりゃいいのでは?と思っていたら、そういう理由ではありませんでした。
RV32Iでこの命令の即値は5bitです。つまり、0x20という表現はできません。不正命令として例外吐く動作が正しい挙動でした。基本的すぎるミスでウケる。

と、こんな感じでようやくクリアできました。
ここまで到達するのに3ヶ月。長かったですね。

今後

ランダムテストを通しながら以下の独立した試行をやっていきます。

  • 一旦今の状態でFPGAに実装
  • メモリウォーキング、Sv32の実装
  • CSRを独自実装のコプロに置き換え、テストコードの作成
  • カスタム命令の設計
  • OoOの設計
  • RV64Iの実装

コード

ここまでのコード全文は以下で。
github.com


コードは今後も漸進的に更新されるので、以下のように-bでタグを指定してcloneするのがいいと思います。

git clone http://github.com/panda5mt/KyogenRV -b 0.1.0 --depth 1