FPGA開発日記

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

Binary Translation型エミュレータを作る(riscv-testsのパタンを通していく)

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

前回ついにriscv-testsrv64ui-p-simpleをPassさせることに成功した。大きな一歩だ。少しずつ別のテストパタンをPassさせるべくサポートを増やしていこう。次はrv64ui-p-addのサポートだ。

このテストパタンはRISC-VのADD命令についてのテストを行う。多くのテストは対象となる命令を実行して、その結果を想定する値と比較するようになっている。もしテストに失敗するとテスト番号をレジスタに保存して終了するのでそれを確認する。

多くのテストパタンについては、実装し忘れた詳細な部分については少しずつ修正を加えるものの、その中でどうしてもADDIW命令を正しく実装する必要があった。ADDIW命令はRV64で定義されている命令で、64ビットレジスタの中で下位32ビットを使用する。このため、演算自体は64ビットでも良いが、最後に計算結果のうち32ビットを符号拡張して64ビットに変換する必要がある。

符号拡張はどのようにX86で実現すればよいかについて調査していると、どうやらX86には符号拡張の専用命令があるらしい。しかもAレジスタ専用だ。

$ cstool -d x64 "48 98"
 0  48 98                                            cdqe
        Prefix:0x00 0x00 0x00 0x00
        Opcode:0x98 0x00 0x00 0x00
        rex: 0x48
        addr_size: 8
        modrm: 0x0
        disp: 0x0
        sib: 0x0
        Registers read: eax
        Registers modified: rax

www.mztn.org

f:id:msyksphinz:20200917000321p:plain

ADDIW命令のTCGからX86命令への変換プロセスでは、レジスタに計算結果を格納する前にこの命令を挿入する。

    fn tcg_gen_store_gpr_32bit(
        emu: &EmuEnv,
        source: X86TargetRM,
        dest: u64,
        mc: &mut Vec<u8>,
    ) -> usize {
        let mut gen_size = 0;

        gen_size += Self::tcg_64bit_out(X86Opcode::SIGN_EXT_A, mc);
        gen_size +=
            Self::tcg_modrm_64bit_out(X86Opcode::MOV_EV_GV, X86ModRM::MOD_10_DISP_RBP, source, mc);
        gen_size += Self::tcg_out(emu.calc_gpr_relat_address(dest) as u64, 4, mc);
        return gen_size;
    }

この結果、rv64ui-p-addについては最後までテストをPassさせることができたようだ!やったぜ!

$ cargo run /home/msyksphinz/riscv64/riscv64-unknown-elf/share/riscv-tests/isa/rv64ui-p-addi
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 = 0x7fd7fa950000
ans = 0x40
x00 = 0000000000000000  x01 = 0000000000000010  x02 = 000000000000001e  x03 = 0000000000000001
x04 = 0000000000000002  x05 = 0000000000000002  x06 = 000000000000001a  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
Result: MEM[0x1000] = 00000001

最後に0x1000に1が格納されており、テストがPassしていることが確認できる。そしてついでにrv64ui-p-addiwも確認しておく。

$ cargo run /home/msyksphinz/riscv64/riscv64-unknown-elf/share/riscv-tests/isa/rv64ui-p-addiw
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 = 0x7fce62d20000
ans = 0x40
x00 = 0000000000000000  x01 = 0000000000000021  x02 = 0000000000000000  x03 = 0000000000000001
x04 = 0000000000000002  x05 = 0000000000000002  x06 = 0000000000000016  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
Result: MEM[0x1000] = 00000001