FPGA開発日記

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

Binary Translation型エミュレータを作る(単精度浮動小数点命令の実装)

Binary Translation方式のエミュレータをRustで作る実装、浮動小数点命令を実装している。倍精度の命令についてはおおよそ実装が完了したので、次は単精度の命令を実装したい。実装方針は全く同一で、単精度の命令についてサポート関数を追加していけばよい。

下記が単精度版だ。倍精度版と比較してみる。

    pub fn helper_func_fadd_s(emu: &mut EmuEnv, fd: u32, fs1: u32, fs2: u32, _dummy: u32) -> usize {
        println!("fadd(emu, {:}, {:}, {:}) is called!", fd, fs1, fs2);
        let fs1_data = F32::from_bits((emu.m_fregs[fs1 as usize] & 0xffffffff) as u32);
        let fs2_data = F32::from_bits((emu.m_fregs[fs2 as usize] & 0xffffffff) as u32);
        let mut flag = ExceptionFlags::default();
        flag.set();
        let fd_data = fs1_data.add(fs2_data, RoundingMode::TiesToEven);
        flag.get();
        let ret_flag = flag.bits();

        emu.m_fregs[fd as usize] = fd_data.bits() as u64;
        emu.m_csr.csrrw(CsrAddr::FFlags, ret_flag as i64);

        return 0;
    }
    pub fn helper_func_fadd_d(emu: &mut EmuEnv, fd: u32, fs1: u32, fs2: u32, _dummy: u32) -> usize {
        println!("fadd(emu, {:}, {:}, {:}) is called!", fd, fs1, fs2);
        let fs1_data = F64::from_bits(emu.m_fregs[fs1 as usize]);
        let fs2_data = F64::from_bits(emu.m_fregs[fs2 as usize]);
        let mut flag = ExceptionFlags::default();
        flag.set();
        let fd_data = fs1_data.add(fs2_data, RoundingMode::TiesToEven);
        flag.get();
        let ret_flag = flag.bits();

        emu.m_fregs[fd as usize] = fd_data.bits() as u64;
        emu.m_csr.csrrw(CsrAddr::FFlags, ret_flag as i64);

        return 0;
    }

データ型で演算をすべてラップすることができたので、実装はほとんど同一になっている。これだったらもっとまとめるように記述すればよかったかも。

とりあえず同様の手法で倍精度命令のために定義したすべての関数を単精度にも移植した。

            RiscvInstId::FADD_S => TranslateRiscv::translate_fadd_s(inst),
            RiscvInstId::FSUB_S => TranslateRiscv::translate_fsub_s(inst),
            RiscvInstId::FMUL_S => TranslateRiscv::translate_fmul_s(inst),
            RiscvInstId::FDIV_S => TranslateRiscv::translate_fdiv_s(inst),

            RiscvInstId::FMADD_S => TranslateRiscv::translate_fmadd_s(inst),
            RiscvInstId::FMSUB_S => TranslateRiscv::translate_fmsub_s(inst),
            RiscvInstId::FNMSUB_S => TranslateRiscv::translate_fnmsub_s(inst),
            RiscvInstId::FNMADD_S => TranslateRiscv::translate_fnmadd_s(inst),
...

テストを通してみる。いつも通りのriscv-testsで単精度関連のテストを追加し、実行してみた。

$ cargo run /home/msyksphinz/riscv64/riscv64-unknown-elf/share/riscv-tests/isa/rv64uf-p-fadd
...
000000d0 : 03 00 00 00 00 00 00 00 48 ba 00 00 00 00 00 00
000000e0 : 00 00 48 b9 01 00 00 00 00 00 00 00 ff 95 b8 03
000000f0 : 00 00 8b 85 20 01 00 00 48 98 48 89 85 58 00 00
reflect tb address = 0x7f257e710000
fadd(emu, 3, 0, 1) is called!
csrrw = 0000000000000000
helper_csrrw(emu, 11, 0, 0x001) is called!
csrrw = 0000000000000000
x00 = 0000000000000000  x01 = 0000000000000000  x02 = 0000000000000000  x03 = 0000000000000002
x04 = 0000000000000000  x05 = 0000000000000108  x06 = 000000000000b109  x07 = 0000000000000000
x08 = 0000000000000000  x09 = 0000000000000000  x10 = 0000000040600000  x11 = 0000000000000000
x12 = 0000000000000000  x13 = 0000000040600000  x14 = 0000000000000000  x15 = 0000000000000000
...

fadd()のサポート関数が呼ばれている。上手く行っているようだ。テストもすべてPassできることを確認できた。

$ cargo test
test result: ok. 60 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out

   Doc-tests uint_execute

running 0 tests

test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out
f:id:msyksphinz:20200925005610p:plain