Dev Study
OSと低レイヤのしくみ コース

4. CSR — 特権レジスタを読み書きする専用命令

CSR(制御状態レジスタ)が汎用レジスタとは別のアドレス空間に置かれ、専用の6命令だけで読み書きされる仕組みを図で追っていきましょう。

① CSRは汎用レジスタとは別のアドレス空間にある

2つの独立したレジスタ空間
命令が触れるレジスタは2系統ある名前空間そのものが分かれている
それぞれ別の指定方法
汎用レジスタ x0〜x315ビットのレジスタ番号で指定
add, ld, sd などが使う
CSR 空間12ビットのCSRアドレスで指定
Zicsr拡張の6命令だけが使う
12ビット = 2の12乗
CSRは最大4096個csr[11:0] のアドレス空間
  • ・仕様の記述: RISC-Vは各ハートに紐づく4096個の制御状態レジスタからなる独立したアドレス空間を定義している
  • ・x5 と CSRアドレス 0x005 は全くの別物です。番号が衝突することはありません

CSRは汎用レジスタの延長ではなく、12ビットで最大4096個を指せる独立したアドレス空間です。だから専用の命令が必要になります。

② 命令フォーマット — CSRアドレスは inst[31:20] に埋め込まれる

CSR命令の32ビットエンコーディング
32ビット命令語major opcode は SYSTEM = 1110011
フィールド分解
inst[31:20]csr(12ビットのCSRアドレス)
inst[19:15]rs1 または uimm[4:0]
inst[14:12]funct3(6命令の識別)
inst[11:7]rd(読み出し先)
funct3の値で命令が決まる
001=CSRRW / 010=CSRRS / 011=CSRRCrs1レジスタを使うレジスタ形式
101=CSRRWI / 110=CSRRSI / 111=CSRRCIuimm[4:0]を使う即値形式
  • ・仕様の記述: CSR指定子は命令のビット31〜20に保持される12ビットのcsrフィールドに符号化される
  • ・仕様の記述: 即値形式はレジスタの値ではなく、rs1フィールドに符号化された5ビット符号なし即値をゼロ拡張したXLENビット値でCSRを更新する
  • ・CSRアドレスは即値としてハードコードされます。実行時に計算したアドレスでCSRを指すことはできません

CSRアドレスは命令語の上位12ビットに直接埋め込まれます。アドレスが命令に固定されているので、デコード段でどのCSRに触るかが確定します。

③ 3つの操作 — 読み出しと更新を1命令でアトミックに行う

CSRRW / CSRRS / CSRRC の動作
CSRの現在値を読むXLENビットにゼロ拡張して rd へ書く
同じ命令の中で続けて更新
CSRRW(write)csr ← rs1
値をまるごと置き換える
CSRRS(set)csr ← csr | rs1
rs1で1のビットを立てる
CSRRC(clear)csr ← csr & ~rs1
rs1で1のビットを落とす
結果
rd には更新前の値が返る読み出しと更新の間に割り込みが挟まらない
  • ・仕様の記述: CSRRWはCSRの古い値を読み、XLENビットにゼロ拡張して整数レジスタrdに書き込む
  • ・CSRRS/CSRRCではrs1はビットマスクとして解釈され、書き込み可能なCSRビットだけが変化します
  • ・「読んでから書く」を2命令に分けると間に割り込みが入り得ます。1命令にまとめることでその隙間を無くしています

3命令はいずれも「古い値をrdへ返す」点が共通で、CSR側の更新方法だけが置換・セット・クリアと異なります。読みと書きが不可分なのが要点です。

④ なぜアトミックである必要があるのか

割り込み許可ビットを落とす例
2命令に分けた場合csrr t0, mstatus のあと csrw mstatus, t1
2命令の隙間で
割り込みが発生ハンドラがmstatusを書き換える
復帰後に古い値を書き戻すハンドラの変更が消える
1命令にまとめると
csrrc t0, mstatus, t1読み出しとクリアが不可分に完了
結果
割り込みの入る隙間が存在しない旧値の取得とビット操作が同時に成立
  • ・CSRには割り込み許可・例外委譲・タイマなど、変更中に割り込まれると状態が壊れるものが多く含まれます
  • ・read-modify-writeを命令側で保証することで、ソフトウェア側のロックが不要になります

CSRの多くは割り込み制御そのものを担うため、ロックで守ろうとすると循環します。命令1つでアトミックにする設計はこの循環を断つためのものです。

⑤ rd=x0 と rs1=x0 — 抑制されるのは書き込み側だけ

レジスタ番号ゼロが持つ意味
CSRには読むだけ・書くだけで副作用が起きるものがあるだからアクセス自体を抑制する手段が要る
CSRRW / CSRRWI で rd=x0 のとき
CSRを読まない読み出しの副作用も起きない
即値形式のCSRRWIにも同じ規定がある
CSRRS / CSRRC で rs1=x0 のとき
書き込みは行われない書き込みの副作用も起きない
読み出し専用CSRでも例外にならない
ただしCSRは必ず読まれるrs1やrdの値に関わらず読み出しの副作用は発生する
即値形式では
CSRRSI / CSRRCI で uimm[4:0] = 0同じく書き込みを行わない
  • ・仕様の記述: CSRRWでrd=x0のとき、命令はCSRを読んではならず、CSR読み出しで生じ得る副作用を起こしてはならない。CSRRWIについても同じ規定が置かれている
  • ・仕様の記述: CSRRSとCSRRCでrs1=x0のとき、命令はCSRに全く書き込まず、書き込みで生じ得る副作用を起こしてはならず、読み出し専用CSRへのアクセスで不正命令例外を発生させてもならない
  • ・仕様の記述: CSRRSとCSRRCはrs1とrdのフィールドに関わらず常に対象CSRを読み、読み出しの副作用を発生させる
  • ・抑制は対称ではありません。CSRRWのrd=x0は読み出しを止めますが、CSRRS/CSRRCのrs1=x0が止めるのは書き込みだけです

x0は「値がゼロ」ではなく「そのアクセスを行わない」という指示として働きます。ただし止まるのは片側だけで、CSRRS/CSRRCの読み出しは常に実行されます。

⑥ 疑似命令 csrr / csrw はこの特別扱いの応用

アセンブラ疑似命令の実体
読むだけ・書くだけをしたい専用命令は用意されていない
x0を使った特殊形で表現
csrr rd, csr実体は csrrs rd, csr, x0
rs1=x0なので書き込まない
csrw csr, rs1実体は csrrw x0, csr, rs1
rd=x0なので読まない
同じ発想の即値版
csrwi csr, uimm実体は csrrwi x0, csr, uimm
csrs / csrc / csrsi / csrcirdをx0にした形
  • ・仕様の記述: CSRを読む疑似命令 csrr rd, csr は csrrs rd, csr, x0 として符号化される
  • ・仕様の記述: CSRを書く疑似命令 csrw csr, rs1 は csrrw x0, csr, rs1 として、csrwi csr, uimm は csrrwi x0, csr, uimm として符号化される
  • ・csrs や csrc は rd=x0 でも中身はCSRRS/CSRRCなので、⑤のとおりCSR自体は読まれます。読み出しを止められるのはCSRRW系だけです
  • ・単純な読み書き専用のオペコードを別に用意せず、既存6命令の縮退形で賄っている点が設計上の節約です

csrrとcsrwは新しい命令ではなく、⑤の抑制ルールを使った書き方です。命令数を増やさずに用途を広げる典型例です。

⑦ CSRアドレスの上位4ビットが権限を表す

csr[11:8] に埋め込まれたアクセス制御
csr[11:0] の12ビットアドレス上位4ビットが規約でアクセス性を符号化する
上位2ビット
csr[11:10] = 読み書き可否00 / 01 / 10 は読み書き可、11 は読み出し専用
次の2ビット
csr[9:8] = アクセスできる最低特権レベル10 はハイパーバイザCSRを表す
だから
アドレスを見るだけで許可判定できるCSR表を引かずにデコード段で判断できる
読み出し専用CSRへの書き込みは不正命令例外csr[11:10]=11 が即座に判定材料になる
  • ・仕様の記述: 慣例により、CSRアドレスの上位4ビット csr[11:8] は特権レベルに応じたCSRの読み書きアクセス性を符号化するために使われる
  • ・仕様の記述: 上位2ビット csr[11:10] はレジスタが読み書き可(00, 01, 10)か読み出し専用(11)かを示す
  • ・仕様の記述: 読み出し専用レジスタへの書き込みの試みは不正命令例外を発生させる。ただし⑤のとおり、rs1=x0のCSRRS/CSRRCは書き込みを行わないため例外になりません
  • ・仕様の記述: 読み書き可能なレジスタが読み出し専用ビットを含むこともあり、その場合は読み出し専用ビットへの書き込みは無視される

アドレスの数値そのものに権限情報が織り込まれているため、ハードウェアは上位4ビットと現在の特権レベルを比べるだけでアクセス可否を決められます。番号の割り当てが偶然ではなく設計されている理由です。

公式ドキュメントで詳しく ↗