RISC-Vについて(CPU実験その2)
この記事はIS17er Advent Calendar 2017 - Adventarの3日目の記事として書かれました。 「日本語で書かれたRISC-V命令一覧が欲しいな」と思ったのがこの記事を書いたモチベーションなので、基本的にはCPU実験の班員向けに書いたRISC-Vの基本命令RV32Iのまとめの流用です。 おもしろみのない記事になってしまいました...
アーキテクチャを選ぶ
CPU実験では自分たちで作るプロセッサ及びコンパイラが対応するISA*1も自分たちで策定します。
課題であるレイトレーシング用プログラムmin-rtがOCamlのサブセットであるmin-camlで書かれているので、既存のmin-camlコンパイラが対応するPowerPCやSPARCを参考にする班*2や、コピュータアーキテクチャの名著であるパタヘネ本で引用されているMIPSアーキテクチャをベースとする班が多いようですが 、 僕の所属するIS17er第1班では主に僕の独断と偏見でRISC-VというアーキテクチャをベースとしたISAを使用することにしました。
なぜRISC-Vなのか
RISC-VはRISCという言葉の生みの親でもありパタヘネ本の著者でもあるパターソン博士の属するUCバークレー発ののRISCアーキテクチャです。
- オープンソースのアーキテクチャであること 。*3
- グーグルやオラクル等も参加し、比較的勢いのあるアーキテクチャであること。
- 参考として買ったFPGAマガジンNo.18に資料が多かったこと。
- 学科同期の@00_から勧められたり、FPGA開発日記というブログで取り上げられていたこと.
RISC-Vの基本構成
今回の実験では32bitの基本命令及び単精度FPU命令からなるRV32IFをベースにしました。
RV32Iの各命令について
RV32Iの命令エンコーディングは下記表に示す5つのタイプに分けられます。
上記の様に即値が複雑になっているのは、できるだけ同じビット位置に同じデータを入れることでハードウェアが簡単になるからだそうです。*5
命令はopcodeによって処理を行う演算器が指定され、さらにfunct3、及びfunct7によって細かい演算が指定されます。
例えばadd命令はオペコード 0110011でALUを用いてrs1とrs2の演算が行われることを指定し、funct3:000で ALUに加減演算を指定し、funct7で減算と区別されるという感じです。
RISC-V 命令一覧(算術論理演算まで)
短時間で書いたので抜けが多くミスもあると思います。 あくまで公式の仕様であるSpecifications - RISC-V Foundationを読む参考にしていただけたらと思います。
lui
- オペコード 0110111
- funct3: 即値として使用されるためなし
- 命令形式: lui rd, imm
- 命令意味: load upper immediate
- 疑似コード
rd = imm << 12,pc++
- 即値タイプ: U
レジスタの上位20bitに即値を入れ、下位12bitは0埋め。 oriやaddiと組み合わせて任意の32bitの即値を得るのに使える。
auipc
- オペコード 0010111
- funct3: 即値として使用されるためなし
- 命令形式: auipc rd, imm
- 命令意味: add upper immediate to pc
- 疑似コード
rd = pc + (imm<<12),pc++
- 即値タイプ: U
luiの結果をpcに足した値を得る。jalやbranch系命令では即値の範囲が狭いので、遠くへ相対ジャンプをしたい時に使える。
jal
- オペコード 1101111
- funct3: 即値として使用されるためなし
- 命令形式: jal rd, imm
- 命令意味: jump and link
- 疑似コード
rd = pc + 1 ,pc += imm
- 即値タイプ: J
専用のリンクレジスタがないので、rdに指定したレジスタに戻りアドレスを入れてPC相対ジャンプをする。 基本的にx1が使われることがRISC-Vのspecの推奨らしい。戻りアドレスが不要な場合はx0を使うとよい
jalr
- オペコード 1100111
- funct3: 000
- 命令形式: jalr rd, rs1, imm
- 命令意味: jump and link register
- 疑似コード
rd = pc + 1 ,pc = rs1 + imm
- 即値タイプ: I
rs1で指定したレジスタの示すアドレスへジャンプする。 luiやaddi、で即値を生成して絶対ジャンプや、auipcの結果を用いて相対ジャンプする際に使える。
BRANCH 系命令
- オペコード 1100011
- 即値タイプ: B
beq
- funct3: 000
- 命令形式: beq rs1, rs2, pc + imm
- 命令意味: branch equal
- 疑似コード
if(rs1 == rs2)then pc += imm else pc++
bne
- funct3: 001
- 命令形式: bne rs1, rs2, pc + imm
- 命令意味: branch not equal
- 疑似コード
if(rs1 != rs2)then pc += imm else pc++
blt
- funct3: 100
- 命令形式: blt rs1, rs2, pc + imm
- 命令意味: branch less than
- 疑似コード
if(rs1 < rs2) then pc += imm else pc++
bge
- funct3: 101
- 命令形式: bge rs1, rs2, pc + imm
- 命令意味: branch greater equal
- 疑似コード
if(rs1 >= rs2)then pc += imm else pc++
bltu
- funct3: 110
- 命令形式: bltu rs1, rs2, pc + imm
- 命令意味: branch less than unsigned
- 疑似コード
if(rs1 < rs2) then pc += imm else pc++
bgeu
- funct3: 111
- 命令形式: bgeu rs1, rs2, pc + imm
- 命令意味:branch greater equal unsigned
- 疑似コード
if(rs1 >= rs2)then pc += imm else pc++
LOAD系命令
- オペコード 0000011
- 即値タイプ: I
lb
- funct3: 000
- 命令形式: lb rd, offset(rs1)
- 命令意味: load byte
- 疑似コード
rd = mem[rs1+offset] ,pc++
メモリのrs1+offset番地にある1バイトを符号拡張した値をrdで指定したレジスタにセットする.
lh
- funct3: 001
- 命令形式: lh rd, offset(rs1)
- 命令意味: load half word
- 疑似コード
rd = mem[rs1+offset] ,pc++
メモリのrs1+offset番地にある2バイトを符号拡張した値をrdで指定したレジスタにセットする.
lw
- funct3: 010
- 命令形式: lw rd, offset(rs1)
- 命令意味: load word
- 疑似コード
rd = mem[rs1+offset] ,pc++
メモリのrs1+offset番地にある1ワードを符号拡張した値をrdで指定したレジスタにセットする.
lbu
- funct3: 100
- 命令形式: lbu rd, offset(rs1)
- 命令意味: load byte unsigned
- 疑似コード
rd = mem[rs1+offset] ,pc++
メモリのrs1+offset番地にある1バイトをゼロ拡張した値をrdで指定したレジスタにセットする.
lhu
- funct3: 101
- 命令形式: lhu rd, offset(rs1)
- 命令意味: load half word unsigned
- 疑似コード
rd = mem[rs1+offset]
メモリのrs1+offset番地にある2バイトをゼロ拡張した値をrdで指定したレジスタにセットする.
STORE系命令
- オペコード 0100011
- 即値タイプ: S
sb
- funct3: 000
- 命令形式: sb rs2, offset(rs1)
- 命令意味: store byte
- 疑似コード
mem[rs1+offset] = rs2,pc++
メモリのrs1+offset番地にLSBをセットする。
sh
- funct3: 001
- 命令形式: sh rs2, offset(rs1)
- 命令意味: store half word
- 疑似コード
mem[rs1+offset] = rs2,pc++
メモリのrs1+offset番地に下位2バイトをセットする
sw
- funct3: 010
- 命令形式: sw rs2, offset(rs1)
- 命令意味: store word
- 疑似コード
mem[rs1+offset] = rs2 ,pc++
メモリのrs1+offset番地に1wordをセットする。
OP-IMM系命令(即値算術論理演算)
- オペコード 0010011
- 即値タイプ: I
addi
- funct3: 000
- 命令形式: addi rd, rs1, imm
- 命令意味: add immediate
- 疑似コード
rd = rs1 + imm ,pc++
即値を負の値に設定することでsubiも兼ねる。
slti
- funct3: 010
- 命令形式: slti rd, rs1, imm
- 命令意味: set less than imm
- 疑似コード
rd = (rs1 < imm) ? 1 : 0 ,pc++
sltiu
- funct3: 011
- 命令形式: sltiu rd, rs1, imm
- 命令意味: set less than unsigned imm
- 疑似コード
rd = (rs1 < imm) ? 1 : 0 ,pc++
xori
- funct3: 100
- 命令形式: xori rd, rs1, imm
- 命令意味: xor immediate
- 疑似コード
rd = rs1 ^ imm ,pc++
ori
- funct3: 110
- 命令形式: ori rd, rs1, imm
- 命令意味: or immediate
- 疑似コード
rd = rs1 |imm ,pc++
andi
- funct3: 111
- 命令形式: andi rd, rs1, imm
- 命令意味: and immediate
- 疑似コード
rd = rs1 & imm ,pc++
slli
- funct3: 001
- 命令形式: slli rd, rs1, imm
- 命令意味: shift left logical imm
- 疑似コード
rd = rs1 << imm ,pc++
- 即値タイプ: I(5bit)
srli
- funct3: 101
- 命令形式: srli rd, rs1, imm
- 命令意味: shift rifht logical imm
- 疑似コード
rd = rs1 >> imm ,pc++
- 即値タイプ: I(5bit)
srai
- funct3: 同上
- funct7: 0100000
- 命令形式: srai rd, rs1, imm
- 命令意味: shift right arithmetic imm
- 疑似コード
rd = rs1 >>> imm ,pc++
- 即値タイプ: I(5bit)
OP系命令
- オペコード 0110011
- 即値タイプ: R
add
- funct3: 000
- 命令形式: add rd, rs1, rs2
- 命令意味: add
- 疑似コード
rd = rs1 + rs2 ,pc++
sub
- funct3: 同上
- funct7: 0100000
- 命令形式: sub rd, rs1, rs2
- 命令意味: sub
- 疑似コード
rd = rs1 - rs2 ,pc++
sll
- funct3: 001
- 命令形式: sll rd, rs1, rs2
- 命令意味: shift left logical
- 疑似コード
rd = rs1 << rs2 ,pc++
slt
- funct3: 010
- 命令形式: slt rd, rs1, rs2
- 命令意味: set less than
- 疑似コード
rd = (rs1 < rs2) ? 1:0 ,pc++
sltu
- funct3: 011
- 命令形式: sltu rd, rs1, rs2
- 命令意味: set less than unsigned
- 疑似コード
rd = (rs1 < rs2) ? 1:0 ,pc++
xor
- funct3: 100
- 命令形式: xor rd, rs1, rs2
- 命令意味: xor
- 疑似コード
rd = rs1 ^ rs2 ,pc++
srl
- funct3: 101
- 命令形式: srl rd, rs1, rs2
- 命令意味: shift right logical
- 疑似コード
rd = rs1 >> rs2 ,pc++
sra
- funct3: 同上
- funct7: 0100000
- 命令形式: sra rd, rs1, rs2
- 命令意味: shift right arithmetic
- 疑似コード
rd = rs1 >>> rs2 ,pc++
or
- funct3: 110
- 命令形式: or rd, rs1, rs2
- 命令意味: or
- 疑似コード
rd = rs1 |rs2 ,pc++
and
- funct3: 111
- 命令形式: and rd, rs1, rs2
- 命令意味: and
- 疑似コード
rd = rs1 & rs2 ,pc++
最後に
今回も適当に書いてしまって反省...
時間ができたらまた詳しく書き直そうかと思います。