この記事は ハードウェア開発、CPUアーキテクチャ Advent Calendar 2016 - Qiita の5日目の記事です。
Advent-Calendarを埋めてくれるかた、今からでも募集中です!是非参加してください! 僕一人では、クオリティのある記事を続けられそうにありません。。。(弱音)
- 1. RISC-Vにはどのような特権命令があるのか
- 2. RISC-Vのプロセッサ実行モード
- 3. RISC-Vのシステムレジスタ(CSRレジスタ)と制御命令
- 4. システムレジスタのアクセスに必要な特権命令
- 5. トラップリターン命令
- 6. まとめ
1. RISC-Vにはどのような特権命令があるのか
最初に、「特権命令」とは何かについて簡単にまとめておこう。
例えば、演算やメモリアクセスに使うような通常の命令と違って、CPUのステータスを変更したり、オペレーティングシステムを動作させるための命令などが含まれる。このような命令のことを特権命令と呼ぶ。 また、特権命令が操作する、CPUの各種情報を格納するためのシステムレジスタの仕様も含まれることが多い。これらをまとめて、「Privileged Architecture」などと言ったりするけど、日本語では何と言うのか良く分からない。
RISC-Vの特権命令の情報はどこにあるの
以下をサイトを参考にする。今ダウンロードすると、いつの間にか仕様書が1.9.1にアップデートされていることに気がついた。前まで1.9だったのに!
Draft Privileged ISA Specification v1.9.1 - RISC-V Foundation
Changelogを見ていると、デバッグ命令あたりの情報も加わっているのか、、見逃していた。
2. RISC-Vのプロセッサ実行モード
RISC-Vには、仕様として以下の実行モードが存在する。上はユーザモードから、下はマシンモードまで。基本的にはユーザモードでは殆どのシステムレジスタにアクセス出来ないが、マシンモードでは全てのシステムレジスタへのアクセスが許される。
ちなみに、3. Hypervisor-Modeについては、仕様書にすらまだ詳細が書かれていない。まだ決まっていないらしい。。。そうすると、通常のRTL実装とか、チップとして上がってくるRISC-Vのプロセッサは、「Machine-Mode」「Supervisor-Mode」「User-Mode」から構成されるという訳だ。
なるほどなあ。
3. RISC-Vのシステムレジスタ(CSRレジスタ)と制御命令
システムレジスタを1つずつ解説していくのは無理なので、おおよそ重要なところをピックアップしていこう。
mstatus, hstatus, sstatus, ustatusレジスタ
レジスタ群の中でおそらく最も重要なのは、*statusレジスタとよばれる、現在のシステムの状態を格納しているレジスタだ。接頭語の「m,h,s,u」はそれぞれどの実行モード中に利用するかを指定する。
ここでは、mstatusレジスタのビットフィールドを見てみる。
0ビット目から3ビット目までは、Interrupt Enableビットで、各実行モードにおける例外許可を示すビットだ。使い方としては、基本的に現在の実行モードxの時には、xIE=1として利用する。下位の実行モードのIEビットはもちろんDisableだし、上位の実行モードのIEビットは1にすることができる。
次の4ビット目から7ビット目は、割り込みのネストを許可するために、一つ前の割り込み許可ビットを保持している。具体的には、Trapが発生する目のInterrupt Enableビットの状態を保持している。
また、VMビットはアドレス変換のモードを示すビットだ。RISC-Vにはいくつかのアドレス変換モードがある。詳細は、私の過去のブログも参考にして欲しい。
その他にも、メモリプロテクションのためのMPRVビット、FSビットは浮動小数点レジスタの状態を示し、XSビットはユーザモードの拡張のためのビットだ。
4. システムレジスタのアクセスに必要な特権命令
では、上記のシステムレジスタにどのようにしてアクセスするのだろうか?RISC-Vのシステムレジスタには、下記の表のように、一意のアドレスが付加されている。CSR Addressと書かれている12ビットの部分だ。このアドレスを利用してシステムレジスタにアクセスすることになる。
RISC-Vアーキテクチャではシステムレジスタは各実行モードによりまとめられており、もちろん各モードでアクセスできるシステムレジスタは異なっている。実際にアクセスするのは、"CSRxx"命令と呼ばれる命令だ。 概念としては、MIPSのmtc0命令や、mfc0命令に似ている。汎用レジスタとシステムレジスタの移動を行う命令なのだが、CSR命令はこのデータを「移動」ではなく「交換」するところがMIPS系の命令と異なる。
RISC-Vでは、以下のシツテムレジスタにアクセスする命令が用意されている。
CSRRW命令
CSRRW rd,csr,rs1
で記述されるこの命令は、csrで表現される12ビットアドレスで示されるCSRレジスタと、汎用レジスタの交換を示す。本命令が実行されると、CSRレジスタの値がrdレジスタにロードされ、rs1の値がCSRレジスタに書き込まれる。この操作がアトミックに行われるというのが本命令の利点で、もしrd=x0(ゼロレジスタ)ならば、rdレジスタへのCSRの値読み込みは行われず、rs1の値がCSRレジスタに書き込まれるだけである。CSRRWI命令
CSRRWI rd,csr,imm
は、rs1の変わりに5ビットの即値が指定できる命令だ。CSRRW命令ではrs1の値がCSRに書き込まれるが、本命令はimmの値がCSRに書き込まれる。下位5ビットしか効果が無いのでいまいち付きどころが分からないが、まあそういう命令だそうな。CSRRS命令、CSRRSI命令
CSRRS rd,csr,rs1
,CSRRSI rd,csr,imm
で表現されるこの命令は、CSRの元の値がrdに読み込まれ、CSRレジスタのrs1レジスタの各ビットで1が立っている部分が強制的に1に設定される。rs1レジスタの各ビットで0であるビットに相当するCSRレジスタビットは影響を受けない。CSRRSIについても、rs1の値が即値になっているだけで同様である。CSRRC命令、CSRRCI命令
CSRRC rd,csr,rs1
,CSRRCI rd,csr,imm
で表現されるこの命令は、CSRの元の値がrdに読み込まれ、CSRレジスタのRs1レジスタの各ビットで1が立っている部分が強制的にクリアされる。rs1レジスタの各ビットで0であるビットに相当するCSRレジスタビットは影響を受けない。CSRRCIについても、rs1の値が即値になっているだけで同様である。
余談。CSRRW命令はアウトオブオーダのプロセッサにおいてどうやって実装するのか?
各CSRアドレスが付加されているとは言え、12Bビット存在する。これをリネームマップを利用して変換するのは現実的に不可能だ。だとすると、CSRへの書き込みについて、リネームマップで変換してアウトオブオーダで処理するという機構は無いだろう。
とすると、CSR命令の場合のみアウトオブオーダ発行を止め、インオーダで一つずつ実行するという方式が現実的ではなかろうかと思う。例えば、同一CSRレジスタに連続して書き込む命令であれば、
CSRRW x1,mstatus,x3 // x1<--mstatus, mstatus<--x3 CSRRW x4,mstatus,x5 // x4<--mstatus, mstatus<--x5
mstatusにはレジスタ書き込みが発生するが、汎用レジスタであるならばリネーミングにより、1命令目と2命令目で別々の物理レジスタがマッピングされる。しかしCSRレジスタはリネーミングが出来ないので、この部分だけインオーダだ実装する必要があるだろう。 つまり、1命令目のmstatusの書き込みが完了するまで、2命令目のmstatusの更新命令はストールさせるという方法になる。
Rocketとか、BOOMとかはどのようにして実装しているんだろうな?
5. トラップリターン命令
v.1.7までの仕様
v.1.7までのPrivileged Instructions では、以下の命令が定義されていた。
mrts命令は、現在の実行モードから前の実行モードに遷移する命令だ。
v.1.9からの仕様
MRET 命令は常に提供される、それ以外の命令はそのプロセッサがどのアーキテクチャまでをサポートしているかに依存する。
6. まとめ
これらのシステム特権命令は、上記にも伸べたように仕様のアップデートの度に命令が増えたり消えたりする。細かいチェックが必要だが、大体の命令は一般的な命令と同様なので、知っている人がすぐに理解出来ると思う。
ただし、まだ仕様として固まっていないので、例えばLinuxなどのシステムを動かすときは、RTLの実装とコンパイラがどのバージョンに対応しているかの確認をしっかりしておく必要がある。