← OSと低レイヤのしくみ コース
1. RISC-Vの命令フォーマット — なぜ即値のビットは散らばっているのか
32ビット命令の中でレジスタ番号と即値がどこに置かれているか、そしてなぜ即値だけが分断されているのかを図で追っていきましょう。
① まず基本形 — R形式の6つのフィールド
inst[31:25]funct7
inst[24:20]rs2
inst[19:15]rs1
同じ32ビットの続き(下位側)
inst[14:12]funct3
inst[11:7]rd
inst[6:0]opcode
例: add rd, rs1, rs2
opcode = 0110011演算の大分類を決める
7ビットfunct3 / funct7add か sub かを区別
3+7ビット- ・基底ISAの命令は固定32ビット長で、フィールドの位置は命令ごとにずれない
- ・レジスタ番号のフィールドはそれぞれ5ビット。2^5 = 32本の整数レジスタを指定できる
- ・opcodeは最下位7ビット(inst[6:0])に固定されている
R形式は即値を持たず、3つのレジスタ番号だけを指定します。まずこの配置を基準として覚えてください。
② 6つのフォーマットを重ねると、レジスタの位置が揃っている
R-Typefunct7 | rs2 | rs1 | funct3 | rd | opcode
I-Typeimm[11:0] | rs1 | funct3 | rd | opcode
S-Typeimm[11:5] | rs2 | rs1 | funct3 | imm[4:0] | opcode
B-Typeimm[12|10:5] | rs2 | rs1 | funct3 | imm[4:1|11] | opcode
U-Typeimm[31:12] | rd | opcode
J-Typeimm[20|10:1|11|19:12] | rd | opcode
- ・rs1 は常に inst[19:15]、rs2 は常に inst[24:20]、rd は常に inst[11:7]
- ・U形式とJ形式は rs1・rs2 を持たず、その領域まで即値に使っている
- ・仕様は R/I/S/U を4つの中核フォーマットとし、B/J を「即値の扱いが異なる2つの変種」として位置づけている
縦に見ると、rs1・rs2・rd を持つフォーマットでは、その位置が一切ずれていません。ずれているのは即値だけです。
③ なぜレジスタ位置を優先したのか
32ビット命令がフェッチされたまだ何の命令か分かっていない
opcodeを見る前に、同時に
inst[19:15] をレジスタファイルへrs1 の読み出しを開始
inst[24:20] をレジスタファイルへrs2 の読み出しを開始
位置が固定なので判定を待たなくてよい
命令種別の判定と並行してレジスタ値がもう読めている
- ・仕様は「レジスタ指定子のデコードは実装においてクリティカルパス上にあることが多い」と述べている
- ・そのため、即値ビットをフォーマット間で移動させる代償を払ってでも、レジスタ位置を全形式で揃える設計が選ばれた
- ・このレジスタ位置を固定する性質は RISC-IV(SPUR、Lee et al. 1989)と共通だと仕様は注記している
レジスタ番号の取り出しは処理時間を左右する経路にあります。位置が固定なら、命令の種類を判定する前に読み出しを始められます。
④ その代償 — S形式で即値が2つに割れる
12ビットの即値を入れたい例: sw rs2, offset(rs1)
しかし rs1・rs2 の場所は動かせない
inst[24:20] = rs2占有ずみ
inst[19:15] = rs1占有ずみ
空いている領域に分けて詰める
inst[31:25]imm[11:5](上位7ビット)
inst[11:7]imm[4:0](下位5ビット)
- ・I形式では即値は inst[31:20] に連続して入るが、S形式では rs2 が間に割り込むため2つに分かれる
- ・I形式もS形式も即値は12ビットで、分断されていても符号ビットが inst[31] から供給される点は共通
- ・仕様は即値について「命令内の左寄りの空きビットにまとめて配置し、ハードウェアの複雑さが減るよう割り当てた」と説明している
即値が散らばって見えるのは、レジスタ位置を守った結果です。即値の側が場所を譲っています。
⑤ 符号ビットは常に inst[31] にある
I-immediate上位は inst[31] を複製
S-immediate上位は inst[31] を複製
B-immediateimm[12] ← inst[31]
J-immediateimm[20] ← inst[31]
U形式だけは左シフト済みで符号拡張不要
U-immediateimm[31:12] ← inst[31:12]
結果として
符号拡張回路の入力は inst[31] 1本に固定命令種別に依存しない
- ・仕様は「すべての即値の符号ビットは常に命令のビット31にあり、符号拡張をデコードと並行して進められる」と述べている
- ・CSR命令が使う5ビット即値だけはゼロ拡張され、それ以外の即値は常に符号拡張される
- ・符号拡張はXLENが32を超える場合に特に重要な操作だと仕様は述べている
どのフォーマットでも符号ビットの出どころが同じなので、符号拡張は命令の判定を待たずに始められます。
⑥ B形式・J形式の「回転」— 2倍する回路を置かないための工夫
分岐先は必ず偶数アドレス最下位ビットは常に0
従来のやり方
即値の全ビットをハードウェアで左に1つシフト即値マルチプレクサと信号のファンアウトが増える
RISC-Vが選んだ方法
imm[10:1] と符号ビットは動かさないS形式と同じ位置に据え置き
inst[7] の役割だけ差し替えるS形式では imm[0]、B形式では imm[11]
- ・B-immediate: imm[12]←inst[31]、imm[11]←inst[7]、imm[10:5]←inst[30:25]、imm[4:1]←inst[11:8]、imm[0]は常に0
- ・J-immediate: imm[20]←inst[31]、imm[19:12]←inst[19:12]、imm[11]←inst[20]、imm[10:1]←inst[30:21]、imm[0]は常に0
- ・仕様は、この回転によって信号のファンアウトと即値マルチプレクサのコストが約2分の1に減ると述べている
ビットを命令側であらかじめ入れ替えておくことで、実行時に2倍する回路が不要になります。散らばりの正体はこの静的な入れ替えです。
⑦ まとめ — 散らばりは「人間の読みにくさ」と「回路の速さ」の交換
最優先: rs1 / rs2 / rd の位置を固定クリティカルパスを短くする
次に優先
符号ビットを inst[31] に固定符号拡張をデコードと並行実行
さらに
B/J はビットを回転させて配置シフト回路を省く
以上すべての結果として
即値のビットが分断・並べ替えされる回路が単純になる側に寄せた結果
- ・仕様は、分岐用の加算器を別に持つ複雑な実装では恩恵が薄いことを認めつつ、最も単純な実装のハードウェアコストを下げることを優先したと動機を説明している
- ・並べ替えの負担は静的なコンパイルではごくわずかで、動的な命令生成でも最も多い短い前方分岐は素直に符号化できると述べられている
即値の散らばりは場当たり的な結果ではなく、デコード速度と回路量を優先した設計判断の副産物です。