「車輪の再実装」って言葉が好き(実践はできてない)

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コンパイラが対応するPowerPCSPARCを参考にする班*2や、コピュータアーキテクチャの名著であるパタヘネ本で引用されているMIPSアーキテクチャをベースとする班が多いようですが 、 僕の所属するIS17er第1班では主に僕の独断と偏見でRISC-VというアーキテクチャをベースとしたISAを使用することにしました。

なぜRISC-Vなのか

RISC-VはRISCという言葉の生みの親でもありパタヘネ本の著者でもあるパターソン博士の属するUCバークレー発ののRISCアーキテクチャです。

などの理由でRISC-Vアーキテクチャを採用しました。

RISC-Vの基本構成

今回の実験では32bitの基本命令及び単精度FPU命令からなるRV32IFをベースにしました。

RV32Iの各命令について

RV32Iの命令エンコーディングは下記表に示す5つのタイプに分けられます。
f:id:progrunner17:20171203211238p:plain

上記の様に即値が複雑になっているのは、できるだけ同じビット位置に同じデータを入れることでハードウェアが簡単になるからだそうです。*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++

最後に

今回も適当に書いてしまって反省...
時間ができたらまた詳しく書き直そうかと思います。

*1:Instruction Set Architecture: CPUが扱う命令のニーモニックや、エンコーディング、動作等

*2:min-camlはx86にも対応していますが、x86CISCでありハードウェアが複雑になるので採用する班は少ないようです。中にはx86コンパイラOCamlフルスクラッチしている同期もいますが(笑).

*3:オープンソースが優れているというより、僕がオープンソースが好きなので

*4:x0は0にハードワイヤード

*5:簡単の意味の名言は避けさせてもらいます。