FPGA開発日記

カテゴリ別記事インデックス https://msyksphinz.github.io/github_pages , English Version https://fpgadevdiary.hatenadiary.com/

Binary Translation型エミュレータを作る(RISC-Vテストパタンを動かす)

Binary Translation型エミュレータにおいてシフト命令を実装した。Binary Translation型エミュレータでは、ゲストマシンの命令(今回はRISC-V)からホストマシンの命令(今回はx86)に直接変換することで高速実行を可能にするシミュレータだ。

CSR命令などのPrivileged命令の実装もかなり進んできたので、RISC-Vが提供しているテストパタンも動作するはずだ。今回はriscv-testsのテストパタンのうち最も簡単なrv64ui-p-simpleをPassさせることに挑戦しよう。

https://github.com/riscv/riscv-tests

rv64ui-p-simpleコンパイル方法はあまり本質的ではないので省略。このテストパタンは何かをテストするわけではなく、単純にブートコードが動いてFinishするだけである。ブートコードの動きを簡単に追いかけてみる。

startから始まるテストパタンは、reset_vectorにジャンプする。

0000000080000000 <_start>:
    80000000:   04c0006f            j   8000004c <reset_vector>

reset_vectorはいくつかのシステムレジスタを設定している。まずはmhartidの設定。もしHartIDが0ではなかったら、初期化プロセスを実行する必要はない(というか無限ループに入り割り込みを待つ状態になる)。

    8000004c:    f1402573            csrr    a0,mhartid
    80000050:   00051063            bnez    a0,80000050 <reset_vector+0x4>

mtvecsatppmpaddr0pmpcfg0medelegmidelegmieの設定を行う。

    80000054:    00000297            auipc   t0,0x0
    80000058:   01028293            addi    t0,t0,16 # 80000064 <reset_vector+0x18>
    8000005c:   30529073            csrw    mtvec,t0
    80000060:   18005073            csrwi   satp,0
    80000064:   00000297            auipc   t0,0x0
    80000068:   01c28293            addi    t0,t0,28 # 80000080 <reset_vector+0x34>
    8000006c:   30529073            csrw    mtvec,t0
    80000070:   fff00293            li  t0,-1
    80000074:   3b029073            csrw    pmpaddr0,t0
    80000078:   01f00293            li  t0,31
    8000007c:   3a029073            csrw    pmpcfg0,t0
    80000080:   00000297            auipc   t0,0x0
    80000084:   01828293            addi    t0,t0,24 # 80000098 <reset_vector+0x4c>
    80000088:   30529073            csrw    mtvec,t0
    8000008c:   30205073            csrwi   medeleg,0
    80000090:   30305073            csrwi   mideleg,0
    80000094:   30405073            csrwi   mie,0

次はRV64の確認かな?32ビットレジスタならマイナス値になるのでエラーとなる。そうでなければ次の設定に進む。

    80000098:    00000193            li  gp,0
    8000009c:   00000297            auipc   t0,0x0
    800000a0:   f6828293            addi    t0,t0,-152 # 80000004 <trap_vector>
    800000a4:   30529073            csrw    mtvec,t0
    800000a8:   00100513            li  a0,1
    800000ac:   01f51513            slli    a0,a0,0x1f
    800000b0:   00055863            bgez    a0,800000c0 <reset_vector+0x74>
    800000b4:   0ff0000f            fence

さらに設定を続ける。stvecmedelegmstatusなどの設定を行う。

    800000c0:    80000297            auipc   t0,0x80000
    800000c4:   f4028293            addi    t0,t0,-192 # 0 <_start-0x80000000>
    800000c8:   00028e63            beqz    t0,800000e4 <reset_vector+0x98>
    800000cc:   10529073            csrw    stvec,t0
    800000d0:   0000b2b7            lui t0,0xb
    800000d4:   1092829b            addiw   t0,t0,265
    800000d8:   30229073            csrw    medeleg,t0
    800000dc:   30202373            csrr    t1,medeleg
    800000e0:   f4629ee3            bne t0,t1,8000003c <handle_exception>
    800000e4:   30005073            csrwi   mstatus,0

mepc0x800000fcを設定しmretする。つまりPC=0x800000fcにジャンプすることになる。

    800000e8:    00000297            auipc   t0,0x0
    800000ec:   01428293            addi    t0,t0,20 # 800000fc <reset_vector+0xb0>
    800000f0:   34129073            csrw    mepc,t0
    800000f4:   f1402573            csrr    a0,mhartid
    800000f8:   30200073            mret

最後にecallによってジャンプする。ecallmepcを使用し、これには0x00000004が設定されているので、trap_vectorにジャンプする。また、あらかじめgpに1が設定されている。

    800000fc:    0ff0000f            fence
    80000100:   00100193            li  gp,1
    80000104:   00000073            ecall
    80000108:   c0001073            unimp

trap_vectorでは、mcauseの内容をチェックし、想定している例外要因コード(今回は0x0b)であればwrite_tohostにジャンプする。

0000000080000004 <trap_vector>:
    80000004:   34202f73            csrr    t5,mcause
    80000008:   00800f93            li  t6,8
    8000000c:   03ff0a63            beq t5,t6,80000040 <write_tohost>
    80000010:   00900f93            li  t6,9
    80000014:   03ff0663            beq t5,t6,80000040 <write_tohost>
    80000018:   00b00f93            li  t6,11
    8000001c:   03ff0263            beq t5,t6,80000040 <write_tohost>
    80000020:   80000f17            auipc   t5,0x80000
    80000024:   fe0f0f13            addi    t5,t5,-32 # 0 <_start-0x80000000>
    80000028:   000f0463            beqz    t5,80000030 <trap_vector+0x2c>
    8000002c:   000f0067            jr  t5
    80000030:   34202f73            csrr    t5,mcause
    80000034:   000f5463            bgez    t5,8000003c <handle_exception>
    80000038:   0040006f            j   8000003c <handle_exception>

write_tohostではtohostセクションに最終結果を書き込む。gpはあらかじめ1に設定されているので1がメモリにストアされる。最後にこれを無限ループで繰り返す。

0000000080000040 <write_tohost>:
    80000040:   00001f17            auipc   t5,0x1
    80000044:   fc3f2023            sw  gp,-64(t5) # 80001000 <tohost>
    80000048:   ff9ff06f            j   80000040 <write_tohost>

これまで実装した命令で、ここまでの機能をシミュレーションできるはずだ。さっそく実行してみる。

cargo run /home/msyksphinz/riscv64/riscv64-unknown-elf/share/riscv-tests/isa/rv64ui-p-simple
000000a0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
000000b0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
000000c0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
000000d0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
000000e0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
000000f0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
reflect tb address = 0x7f5239a80000
ans = 0x40
x00 = 0000000000000000  x01 = 0000000000000000  x02 = 0000000000000000  x03 = 0000000000000001
x04 = 0000000000000000  x05 = 00000000000000fc  x06 = 000000000000b109  x07 = 0000000000000000
x08 = 0000000000000000  x09 = 0000000000000000  x10 = 0000000000000000  x11 = 0000000000000000
x12 = 0000000000000000  x13 = 0000000000000000  x14 = 0000000000000000  x15 = 0000000000000000
x16 = 0000000000000000  x17 = 0000000000000000  x18 = 0000000000000000  x19 = 0000000000000000
x20 = 0000000000000000  x21 = 0000000000000000  x22 = 0000000000000000  x23 = 0000000000000000
x24 = 0000000000000000  x25 = 0000000000000000  x26 = 0000000000000000  x27 = 0000000000000000
x28 = 0000000000000000  x29 = 0000000000000000  x30 = 0000000000001040  x31 = 000000000000000b
PC = 0000000000000040

いいぞ。最後の0x40まで到達できた。テストパタンの実行は成功だ。やったぞ!

f:id:msyksphinz:20200916000940p:plain