FPGA開発日記

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

Binary Translation型エミュレータを作る(浮動小数点演算の実装)

自作Binary Translationエミュレータの実装、浮動小数レジスタへのロードストア命令の実装が完了したので、次は演算命令の実装だ。簡単なところから、FADD.D(倍精度浮動小数点加算命令)から実装して行きたい。

さて、C++で記述されたsoftfloatをどのようにRustから呼び出すのかという話だが、昔はC++とRustをインタフェースする機能を使って実装していたが、いろいろ調べているとsoftfloatのRustラッパーがあったのでこれを使わせてもらうことにした。

qiita.com

Binary Translationにおいて、浮動小数点演算命令はヘルパー関数を呼び出すことによって実現する。

    pub fn translate_fadd_d(inst: &InstrInfo) -> Vec<TCGOp> {
        let rs1_addr: usize = get_rs1_addr!(inst.inst) as usize;
        let rs2_addr: usize = get_rs2_addr!(inst.inst) as usize;
        let rd_addr: usize = get_rd_addr!(inst.inst) as usize;

        let rs1 = Box::new(TCGv::new_reg(rs1_addr as u64));
        let rs2 = Box::new(TCGv::new_reg(rs2_addr as u64));
        let rd = Box::new(TCGv::new_reg(rd_addr as u64));

        let fadd_d = TCGOp::new_helper_call_arg3(CALL_HELPER_IDX::CALL_FADD_D_IDX as usize, *rd,*rs1, *rs2);
        vec![fadd_d]
    }

新たにヘルパー関数としてhelper_func_add_d()を用意した。中でsoftfloat_wrapperを使用している。

    fn helper_func_fadd_d(emu: &mut EmuEnv, fd: u32, fs1: u32, fs2: u32) -> usize {
        println!("fadd(emu, {:}, {:}, 0x{:03x}) 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();
        emu.m_csr.csrrw(CsrAddr::FFlags, ret_flag as i64);

        return 0;
    }

浮動小数レジスタから値を読み込み、それをsoftfloat_wrapperを使い専用のデータ型に変換する。そのうえで加算を行い、結果を浮動小数レジスタに書き込むという仕組みだ。

フラグレジスタの取得が必要だが、これはExceptionFlagsを使って実現できるようだ。いったんデフォルト値をflags.set()で設定し、演算後にflags.get()ののちにflags.bits()で取得できる(なぜこんなにややこしいかというとsoftfloatはフラグをグローバル変数に格納するからだ...理由が分かってしまう)。

このように実装した浮動小数点加算命令を実行してみる。rv64ud-p-faddテストパタンを利用した。

$ cargo run riscv-tests/isa/rv64ud-p-fadd
000000c0 : 08 fe 3c e5 ff 7f 00 00 48 be 03 00 00 00 00 00
000000d0 : 00 00 48 ba 00 00 00 00 00 00 00 00 48 b9 01 00
000000e0 : 00 00 00 00 00 00 ff 95 50 03 00 00 48 8b 85 20
000000f0 : 01 00 00 48 89 85 58 00 00 00 48 bf 08 fe 3c e5
reflect tb address = 0x7f12ab490000
fadd(emu, 3, 0, 0x001) is called!
csrrw = 0000000000000001
helper_csrrw(emu, 11, 0, 0x001) is called!
csrrw = 0000000000000000
x00 = 0000000000000000  x01 = 0000000000000000  x02 = 0000000000000000  x03 = 0000000000000003
x04 = 0000000000000000  x05 = 0000000000000108  x06 = 000000000000b109  x07 = 0000000000000000
x08 = 0000000000000000  x09 = 0000000000000000  x10 = c093480000000000  x11 = 0000000000000001
x12 = 0000000000000000  x13 = c093480000000000  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 = 0000000000000000  x31 = 0000000000000000

f00 = c0934c6666666666  f01 = 3ff199999999999a  f02 = 0000000000000000  f03 = c093480000000000
f04 = 0000000000000000  f05 = 0000000000000000  f06 = 0000000000000000  f07 = 0000000000000000
f08 = 0000000000000000  f09 = 0000000000000000  f10 = 0000000000000000  f11 = 0000000000000000
f12 = 0000000000000000  f13 = 0000000000000000  f14 = 0000000000000000  f15 = 0000000000000000
f16 = 0000000000000000  f17 = 0000000000000000  f18 = 0000000000000000  f19 = 0000000000000000
f20 = 0000000000000000  f21 = 0000000000000000  f22 = 0000000000000000  f23 = 0000000000000000
f24 = 0000000000000000  f25 = 0000000000000000  f26 = 0000000000000000  f27 = 0000000000000000
f28 = 0000000000000000  f29 = 0000000000000000  f30 = 0000000000000000  f31 = 0000000000000000
PC = 000000000000013c

テストパタン実行中にfadd(emu, 3, 0, 0x001) is called!が呼び出され、softfloatが実行されたことが分かる。さらに浮動小数レジスタも値が更新されているので演算自体も問題なさそうだ。上手く行った。

次は、これを加減乗除命令まで拡張していこう。