Cで書いたプログラムを自作RISC-Vシミュレータで動かしたい
命令がたりてないんだよね
何が足りないのか確認
サンプルプログラムはfibonacciかな
fibonacciで使われる命令を確認
code:fibonacci.c
int fibonacci(int n) {
if (n == 1) {
return 1;
} else if (n == 2) {
return 1;
} else {
return fibonacci(n - 2) + fibonacci(n - 1);
}
}
コンパイルして
code:sh
$ riscv64-unknown-elf-gcc -march=rv32i -mabi=ilp32 -c fibonacci.c
逆アセンブルして命令を抽出
code:sh
$ riscv64-unknown-elf-objdump -S fibonacci.o | awk '{ print $3 }' | sort | uniq
add
addi
auipc
bne
format
j
jalr
li
lw
mv
ret
section
sw
「section」と「format」は命令列じゃないので無視してOK
実装済みの命令
add
addi
bne
lw
sw
未実装の命令
auipc
jal
jalr
lui
bne
j → 疑似命令、jalに置き換えられる
li → 疑似命令、addi と lui に置き換えられる
mv → 疑似命令、addi に置き換えられる
ret → 疑似命令、jalr に置き換えられる
以下の4つを追加すればフィボナッチは動きそう。
auipc → 実装した
jalr → 実装した
jal → 実装した
lui → 実装した
bne → 実装した
疑似命令call
code:sh
call rd, sybol
=> xrd = pc+8; pc = &symbol auipc rd, offsetHi と jalr rd, offsetLo(rd) に展開される
rdが省かれると暗黙的にx1(ra)が使われる
中身を理解したい
auipc rd, offsetHi → レジスタrd = pc + ジャンプ先へのオフセットの上位20ビット
jalr rd, offsetLo(rd) → t = pc + 4; pc = (さっきの上位20ビット + ジャンプ先へのオフセットの下位12ビット) & 0xfffffffe; レジスタrd = t
auipc (Add Upper Immediate to PC)
U形式
code:sh
xrd = pc + sext( immediate3112] << 12 ) 符号拡張された20ビットのimmediateを12ビット左にシフトしてpcに加算し、結果を x[rd] に書き込む
inst(6:0) = opcode = 0010111
inst(11:7) = rd
inst(31:12) = immediate(31:12)
jalr (Jump And Link Register)
I形式
code:sh
jalr rd, offset(rs1)
=> t=pc+4; pc=(xrs1+sext(offset))&~1; xrd=t rs1レジスタ + 符号拡張されたoffsetを計算し、算出されたアドレスの最下位ビットをマスクしてpcに設定し、それから前のpc+4をrdレジスタに書き込む。rdレジスタが省かれるとx1(ra)レジスタが使用される
inst(6:0) = opcode = 1100111
inst(11:7) = rd
inst(14:12) = funct3 = 000
inst(19:15) = rs1
inst(31:20) = offset(11:0)
jal (Jump And Link)
J形式
code:疑似コード
jal rd, offset
xrd=pc+4; pc+=sext(offset) rdレジスタに戻り先(pc+4)を保存し、現在のpcにoffsetの値を加えたものをpcにセットする
inst(6:0) = opcode = 1101111
inst(11:7) = rd
inst(31:12) = offsetの即値
lui (Load Upper Immediate)
U形式
code:疑似コード
lui rd, immediate
inst(6:0) = opcode = 0110111
inst(11:7) = rd
inst(31:12) = 即値
J形式とI形式とU形式のフォーマット
https://gyazo.com/b421eb85b24d600656563d853ee983cb
auipcを実装した
code:rv32sim.rb
def _auipc
rd = @decoder.rd
@x_registersrd = @pc + imm @pc = @pc + 4
end
jalrを実装した
code:疑似コード
jalr rd, offset(rs1)
=> t=pc+4; pc=(xrs1+sext(offset))&~1; xrd=t code:rv32sim.rb
def _jalr
rd = @decoder.rd
rs1 = @decoder.rs1
# 12ビット即値の符号拡張
offset = ((@decoder.i_imm & 0x800) >> 11) == 0 ? @decoder.i_imm : @decoder.i_imm - 0x1000
t = @pc + 4
@pc = (@x_registersrs1 + offset) & 0xFFFFFFFE end
end
jalを実装した
code:疑似コード
jal rd, offset
xrd=pc+4; pc+=sext(offset) code:ruby
def _jal
rd = @decoder.rd
imm = @decoder.j_imm << 1
imm = ((imm & 0x100000) >> 20).zero? ? imm : imm - 0x200000
@pc = @pc + imm
end
luiを実装した
code:疑似コード
lui rd, immediate
code:ruby
def _lui
rd = @decoder.rd
@pc = @pc + 4
end
Cで書いたフィボナッチを動かしてみる
動いた〜
https://gyazo.com/e00c52b8eb1484640fe1c2299da98472
乗算除算も欲しかった
mul(MULtiply)
opcode=0110011, f3=000, f7=0000001
code:pcode
mulh(MULtiply High)→ ひとまず見送り
mulhsu(MULtiply High Sign/Uns)→ ひとまず見送り
mullhu(MULtiply High Uns)→ ひとまず見送り
div(DIVide)
opcode=0110011, f3=101, f7=0000001
code:pcode
divu(DIVide Unsigned)
opcode=0110011, f3=100, f7=0000001
code:pcode
rem(REMainder)
opcode=0110011, f3=110, f7=0000001
code:pcode
remu(REMainder Unsigned)
opcode=0110011, f3=111, f7=0000001
code:pcode
参考