目次
RISC-Vのシステムレジスタ
前回は、RISC-Vを学ぶにあたり最も基本となる命令セットについて紹介しました。
RISC-Vには、基本となる命令セットに加えてシステムレジスタも定義されています。RISC-Vでオペレーティングシステムを動作させたり、アプリケーションを開発するためには、このシステムレジスタについて理解する必要があるでしょう。例えば、特権命令はどのような構成になっているのか?割り込みや例外はどのように処理すればよいのか?今回は、これらのシステムレジスタやRISC-Vの動作モードなどについて見ていきます。
ちなみに、これらのシステムレジスタ・動作モードについて最新の仕様は、RISC-V Privileged Specification 1.10 で見ることが出来ます。もっと詳細を学びたいと思った方は、以下の資料を見てみることをお勧めします。
RISC-V Privileged ISA Specification
オペレーティングシステムを動作させるために~RISC-Vの動作モードと制御レジスタ~
通常のCPUには、オペレーティングシステムを動作させたり、セキュリティ上CPU上で動作するアプリケーションを分離したりなどのためにさまざまな動作モードあります。動作モードは、もちろんRISC-Vにも定義されています。RISC-Vには以下の動作モードがあります。
- ユーザモード(User Mode)
- スーパバイザーモード(Supervisor Mode)
- マシンモード(Machine Mode)
これらのモードについてはだいたい意味が分かると思います。マシンモードは、全てのシステムレジスタにアクセスできるモードです。スーパバイザモードは、オペレーティングシステムを動作させるためのモード、ユーザモードは、ユーザアプリケーションを動作させるためのモードです。それぞれのモードで、アクセスできるシステムレジスタや、実行できる特権命令が異なります。
ユーザモード中でプログラムを実行中に例外が発生すると、基本的にはマシンモードに状態が遷移され、処理が行われると考えてよいでしょう(ただし後述するように例外・割り込みの委譲レジスタにより、例外・割込みハンドラをマシンモード以外の動作モードに委譲することができます)。
現在RISC-Vチップがどのモードで動作しているかは、以下のようにエンコーディングされます(ちなみに、Level-2としてかつてはハイパーバイザーモードも定義されていましたが、これは仕様の策定が進まずに削除されてしまいました。ですが、いつかは復活することでしょう)。
レベル | エンコーディング | 名前 | 略称 |
---|---|---|---|
0 | 00 | ユーザ・アプリケーションモード | U |
1 | 01 | スーパバイザーモード | S |
2 | 10 | 予約 | |
3 | 11 | マシンモード | M |
RISC-VのCPU実装としては、基本的にはマシンモードを備えていれば良いとされています(これは最もシンプルな構成)。
あるいは、ユーザモードとマシンモードをサポートした構成や、ユーザモード・スーパバイザモード・マシンモードのすべてを搭載する実装が許されています。
制御&状態レジスタについて
次に、RISC-Vの制御レジスタについて見ていきましょう。
RISC-Vの制御レジスタは、CSR(Control and Status Register)と呼ばれています。CSRは動作モードに応じて定義されています。
制御レジスタとしては、最も基本となるCPUの状態を示すためのレジスタや、割り込み・例外を管理するためのレジスタ、仮想メモリの設定を行うためのレジスタ、さらにタイマやカウンタを制御するためのレジスタなど、CPUとしての一通りの機能を実現するためのレジスタが定義されています。
CSRにアクセスするためには、専用のシステムレジスタ転送命令を利用します。オペレーティングシステムを実装したり、タイマなどの機能にアクセスするためには、このシステムレジスタ転送命令を理解しなければなりません。
まず、CSRには12ビットのアドレスが付与されています。このアドレスによりアクセスするCSRが決まります。
例えば、マシンモードのCSRの一部を抜粋してみました。以下はRead-onlyなCSRです。
CSRアドレス | 名前 | 説明 |
---|---|---|
0xF11 | mvendorid | ベンダID |
0xF12 | marchid | アーキテクチャID |
0xF13 | mimpid | 実装ID |
0xF14 | mhartid | ハードウェアスレッドID |
Read/Write可能なCSRは例えば以下のようなものが定義されています。
CSRアドレス | 名前 | 説明 |
---|---|---|
0x300 | mstatus | マシン状態レジスタ |
0x301 | misa | ISAと拡張情報レジスタ |
0x302 | medeleg | マシン例外の委譲レジスタ |
0x303 | mideleg | マシン割り込みの委譲レジスタ |
0x304 | mie | マシンの割り込み許可レジスタ |
0x305 | mtvec | マシンのトラップハンドリングのベースレジスタ |
0x306 | mcounteren | マシンのカウンタEnableレジスタ |
これらのシステムレジスタの詳細については、最初に紹介した “RISC-V Privileged Instruction Manual Ver.1.10” を参照してください。
CSRにアクセスするための転送命令
では、CSRにアクセスするためにはどうすれば良いのでしょうか。これには、CSR転送のための専用命令を使います。CSR転送命令は、以下の6命令が定義されています。
- CSRRW / CSRRWI(CSR Read / Write): CSRと汎用レジスタのアトミック交換
- CSRRS / CSRRSI(CSR Read / Bit Set): CSRの読み込みとビットセット
- CSRRC / CSRRCI(CSR Read / Bit Clear): CSRの読み込みとビットクリア
まず、CSRRW命令は汎用レジスタとCSRの値を交換します。
csrrw x2、csr_addr、x1
はcsr_addr
で指定されるCSRの値をx2
レジスタに書き込み、x1
レジスタの値をCSRに書き込みます。これはアトミックに行われます。これで、プログラムはCSRの値を取得し、CSRに値を書き込むことが出来ます。
これ以外にもあります。CSRRSはCSRの特定のビットに1を設定します。つまりRSのSは”Set”の意味ですね(予想ですが)。csrrs x2、csr_addr、x1
はcsr_addr
で指定されるCSRの値をx2
レジスタに書き込み、CSRのビットのうち、x1レジスタの1が立っているビットを1に設定します。
CSRRCはその逆です。csrrc x2、csr_addr、x1
と書くと、CSRのビットのうちx1レジスタの1が立っているビットを0にクリアします。
さらにこのCSRの設定・クリアレジスタには汎用レジスタの変わりに即値を取る命令も用意されています(csrrwi、csrrsi、csrrci命令)。
この命令の即値フィールドは5ビットしかありません。従って、このCSR即値命令ではCSRビットのうち下位の5ビットしか設定することが出来ません。
少し不便と言えば不便ですが、CSRの重要なビットは下位に固められているため、この即値命令も十分に活用することが出来る、という発想のようです。
さらに、RISC-Vのアセンブリコードを読むためには、以下の擬似アセンブリコードも理解しておくと良いでしょう(これらの疑似アセンブリコードは、逆アセンブルのためのobjdumpコマンドの出力にも出てくるため、理解しておくと良いです)。
擬似コード | オリジナルの命令 | 説明 |
---|---|---|
csrr rd、csr | csrrs rd、csr、x0 | CSR読み込み(CSRの内容は更新しません) |
csrw csr、rs | csrrw x0、csr、rs | CSR書き込み(汎用レジスタは更新しません) |
csrs csr、rs | csrrs x0、csr、rs | CSRのビット設定(汎用レジスタは更新しません) |
csrc csr、rs | csrrc x0、csr、rs | CSRのビットクリア(汎用レジスタは更新しません) |
csrwi csr、imm | csrrwi x0、csr、imm | CSRの即値書き込み(汎用レジスタは更新しません) |
csrsi csr、imm | csrrsi x0、csr、imm | CSRの即値ビット設定(汎用レジスタは更新しません) |
csrci csr、imm | csrrci x0、csr、imm | CSRの即値ビットクリア(汎用レジスタは更新しません) |
CSRの役目を理解するための用語
CSRの仕様書を読み進めていくと、レジスタフィールドの意味を示す用語がいくつも出てきます。
CSRには、Read-onlyなフィールド、Rea-Writeはできるがいくつか制限があるフィールドなど、フィールドの特性に応じてその特性を現す用語が付けられています。
この用語は忘れやすいので、一旦まとめておくのが良いでしょう。
Reserved Writes Ignored Read Ignore Value(WIRI)
Read-onlyなフィールドです。このフィールドに書き込んでも何の効果もありません。ただし、CSR全体がWIRIの場合、そのレジスタに書き込みを行うと例外が発生するので注意が必要です。
Reserved Write Preserve Values、Read Ignore Values(WPRI)
将来のために予約されているフィールドです。このフィールドに書き込んでも、何の効果もないですし、常に固定値が読みだされます。
Write/Read Only Legal Values(WLRL)
読み書き可能なフィールドです。書き込む場合は、意味のある値を書き込む必要があります。もし意味のない(例えば範囲外のような)値を書き込んだ場合、その後の正常な動作は保証されません。
Write Any Values、Read Legal Values(WARL)
読み書き可能なフィールドです。書き込む場合には、意味のない値を書き込んだときには、意味のある値がキープされます。
主要なCSRの紹介
次に、RISC-Vのアプリケーションを作成したり、オペレーティングシステムを動作させるために必要な主要なCSRについて紹介します。
mstatus レジスタ
mstatus
レジスタは全てのRISC-V実装に搭載されているべきレジスタで、CPUの現在の状態を示しているレジスタです。
サブセットとしてsstatus
レジスタおよびustatus
レジスタがあり、sstatus
の場合はスーパバイザモードで、ustatus
レジスタはユーザモードで、mstatus
レジスタの一部にアクセスすることが出来ます。
RV32(32-bit版の実装)とRV64(64-bit版の実装)で、mstatus
の内部のビットフィールドは異なります。それぞれのモードで、mstatus
レジスタは以下のように構成されています。
重要なものだけ引っ張り出していきましょう。
MIE、SIE、UIEビットは、マシンモード・スーパーバイザーモード・ユーザモードでの割り込み許可を示すビットです。
また、MPIE、SPIE、UPIEビットはトラップにより割り込みが発生したときに、トラップに入る前のMIE、SIE、UIEビットの値を保持しています。
また、MPP、SPP ビットは、トラップにより割り込みが発生したときに、トラップに入る前の動作モードの状態を保持しています。
RISC-Vには、割り込み応答性を向上させるために上記のような、トラップ発生前の状態を保持しておくビットがいくつもあります。
これらのビットは、MRET / SRET / URET 命令によって元のビット位置に書き戻されます。
これによりトラップが発生しても現在の状態を保持するために多くの命令を実行する必要が無く、割り込み応答性を向上させることが出来ると考えられます。
高速な割り込み動作を実現するためのCSR(mideleg、medeleg)
RISC-Vには、実装に応じて複数の動作モードを持つことが出来ますが、例えばユーザモード中に例外が発生し、その割り込み処理をスーパバイザーモードで処理する場合はどのようにすればよいのでしょうか。
例外が発生すると、CPUの動作モードはマシンモードに遷移します。
その後、例外の種類を特定してどの例外であればどの動作モードで処理を行うか決めて、例外処理ルーチンにジャンプするわけですが、
RISC-VのCSRには、「一旦マシンモードに移ってから、別の動作モードに移動する」という処理を省略することができるCSRが存在します。
日本語では「委譲」の意味を持つ”delegation” の名前が付けられているmideleg/medeleg
CSRです。このレジスタに値を設定することにより、通常はマシンモードで受け付けられる割り込み・例外を、スーパバイザモードやユーザモードに委譲することができます。マシンモード・スーパバイザモード・ユーザモードのすべてをサポートしており、ユーザモードで割り込み・例外をサポートすることができる実装の場合は、mideleg/medelg
レジスタで以上された処理をさらにsideleg/sedeleg
レジスタを使ってさらにユーザモードにまで処理を移譲することができます。
- 割り込み動作を移譲しない場合の動作。いったんマシンモードで割り込みを受け取り、割り込みコードを判定してスーパバイザモードに移行する。
- 割り込み動作を移譲する場合。マシンモードで受け取る際に移譲レジスタを読み取り、発生した割り込みに相当するビットが立っている場合、すぐにスーパバイザモードに移行する。
mideleg/medeleg
の詳細は省略しますが、このCSRのビットフィールドは、RISC-Vで規定されたエンコーディングによって指定するビット位置が決められます。
割り込みコード | |
---|---|
0 | ユーザソフトウェア割り込み |
1 | スーパバイザソフトウェア割り込み |
2 | 予約 |
3 | マシンソフトウェア割り込み |
4 | ユーザタイマ割り込み |
5 | スーパバイザタイマ割り込み |
6 | 予約 |
7 | マシンタイマ割り込み |
8 | ユーザ外部割込み |
9 | スーパバイザ外部割込み |
10 | 予約 |
11 | マシン外部割込み |
>=12 | 予約 |
例外コード | |
---|---|
0 | 命令アドレスミスアライン例外 |
1 | 命令アクセス例外 |
2 | 不定命令例外 |
3 | ブレークポイント例外 |
4 | ロードアドレスミスアライン例外 |
5 | ロードアクセス例外 |
6 | ストア/アトミックアクセスミスアライン例外 |
7 | ストア/アトミックアクセス例外 |
8 | ユーザモードからのEnvironment Call |
9 | スーパバイザモードからのEnvironment Call |
10 | 予約 |
11 | マシンモードからのEnvironmental Call |
12 | ページアクセス例外(命令フェッチ) |
13 | ページアクセス例外(ロード) |
14 | 予約 |
15 | ページアクセス例外(ストア・アトミック) |
>= 16 | 予約 |
割り込みや例外を扱うためのCSR
当然ですが、RISC-Vでは割り込みや例外を扱うことができます。割り込み・例外の制御を行うために、いくつかのCSRが用意されています。
トラップベースアドレスレジスタ(mtvec / stvec)
割り込み・例外が発生した場合にジャンプするPCを格納しておきます。
例外プログラムカウンタレジスタ(mepc / sepc)
例外が発生した命令が格納されているプログラムカウンタを格納しています。
例外要因レジスタ(mcause / scause)
割り込み・例外が発生した要因を格納しています。表5, 表6 に基づく割り込み・例外コードが格納されます。
トラップ値レジスタ(mtval / stval)
例外の情報を格納します。このレジスタに書き込まれる値は、発生する例外の種類に応じて異なります。例えば、不貞命令例外の場合は、その例外を発生させた命令自体を格納しますが、それ以外にメモリアクセス時に例外が発生した場合、例外が発生した実効アドレスの値が格納されます(mepcは例外が発生した命令のPCアドレスを格納しているので、これとは異なります)。
RISC-Vの関数呼び出し規約
最後に、RISC-Vの関数呼び出しの規約についてざっと見ておきます。
関数呼び出しの規約というのは、C言語などで関数を呼び出すときに、引数や戻り値の渡し方、レジスタの値は関数の呼び出し側と呼び出し元のどちらが保存するか、などといったことが規定されています。
ABI名 | 意味 | 保存の責任者 | |
---|---|---|---|
x0 | zero | ゼロ固定 | |
x1 | ra | リターンアドレス | 呼び出し側 |
x2 | sp | スタックポインタ | 関数側 |
x3 | gp | グローバルポインタ | |
x4 | tp | スレッドポインタ | スレッドポインタ |
x5 | t0 | 一時レジスタ・リンクレジスタ | 呼び出し側 |
x6-x7 | t1-t2 | 一時レジスタ | 呼び出し側 |
x8 | s0/fp | 保存レジスタ・フレームポインタ | 関数側 |
x9 | s1 | 保存レジスタ | 関数側 |
x10-x11 | a0-a1 | 引数・戻り値レジスタ | 呼び出し側 |
x12-x17 | a2-a7 | 引数レジスタ | 呼び出し側 |
x18-x27 | s2-s11 | 保存レジスタ | 関数側 |
x28-x31 | t3-t6 | 一時レジスタ | 呼び出し側 |
ABI名 | 意味 | 保存の責任者 | |
---|---|---|---|
f0-f7 | ft0-ft7 | 浮動小数点一時レジスタ | 呼び出し側 |
f8-f9 | fs0-fs1 | 浮動小数点保存レジスタ | 関数側 |
f10-f11 | fa0-fa1 | 浮動小数点引数・戻り値レジスタ | 呼び出し側 |
f12-f17 | fa2-fa7 | 浮動小数点引数 | 呼び出し側 |
f18-f27 | fs2-fs11 | 浮動小数点保存レジスタ | 関数側 |
f28-f31 | ft8-ft11 | 浮動小数点一時レジスタ | 呼び出し側 |
以上、駆け足ですが、RISC-Vのシステムレジスタや割り込み・例外・ABIについて取り上げました。
これらはRISC-Vの仕様の一部分にすぎません。繰り返しになりますが、より詳細な仕様を学びたければ、RISC-V Privileged Instruction Manual を参照することをお勧めします。
こちらも是非
“もっと見る” RISC-V編
GUIの開発環境を使ってRISC-Vを動かしてみよう
Arduino IDEは他のArduinoプラットフォームとの親和性が良く、Arduinoチップとして楽しむならば十分な環境です。また、Freedom-E-SDKはコンソールを使ってプログラムのコンパイルやアップロードを行う環境で、初心者にはややハードルが高いですが、柔軟なプログラムを開発することができます。
GUIの環境は好きじゃない!Freedom SDKを使ったアプリケーション開発
Arduino IDEを使ってプログラムを書き込む方法をはじめ、EclipseベースのGUI環境であるFreedom Studioを使うとGUI環境でプログラムを開発することが出来ます。その一方で、GUIを使うのは面倒だし、Linuxなどを使っていればコマンドラインからすべて操作したいという人はたくさんいると思います。
Arduino互換RISC-Vプロセッサ“HiFive1”を使ってみる
RISC-Vを使うにはいろんな手段があります。ASICチップが乗っているボードを買ってくることもできますし、FPGAにデザインを焼いて動作させることができます。特にFPGAを使う場合は、自分で簡単にカスタマイズすることもできますので独自のRISC-V環境を作ることもできます。