FPGA開発日記

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

ISSの多倍長演算命令の修正

https://gmplib.org/gmplogo2.png

RISC-Vには64bitの乗算など、通常の計算の範囲では追いつけないような演算も含まれている。 例えば、MUL命令やMULHSU命令などは、64bitのデータ同士の演算であり、MULHは演算結果の128bitのうち上位の64bitを返してきたりする。

これらの演算をサポートするためには、GNUの多倍長演算ライブラリを利用すればよい。自作ISSでは、GMPを使っている。

msyksphinz.hatenablog.com

GMPを用いることで64bit同士の乗算でも、問題なく実行できるようになっているのだが、いくつかのパタンでうまく実行できない状態が続いていた。

よい機会なので、解析していこう。

GMPにおける符号付整数の演算の考え方

例えば、MUL命令は64bitデータ同士の乗算を行い、結果の下位64bitをレジスタに格納する。

reg[dst] = lower64bit(sign(reg[op1]) * sign(reg[op2]))

この場合、最初の実装としては、GMPで普通に符号付で乗算し、その結果をget_si()で取得していた。

   rs2_mpz.set_str(rs2_str.str(), 10);
   mpz_class res_mpz = (rs1_mpz * rs2_mpz);

   DWord_t res = res_mpz.get_si ();

この場合、MUL命令の仕様としては、乗算の結果得られた128bit数の符号は特に関係なく、下位の64bitを切り取る。なので、get_si()をしてから64bitに縮小する(符号を保持したまま)とはちょっと意味が違う。

たとえば演算結果が 0xffff0000_00000000_00000000_00000001 だった場合、符号を残したいので、64bitに変換するとき(値はどうなるかに関わらず) 0x80000000_00000001 に近いものになる(符号は残る)。

ところがMUL命令としては、純粋に64bitをシュリンクして残したいので、演算結果が負の数だった場合、とりあえず符号なし整数として取得し、ビットを反転させる。

  bool is_result_minus = ((rs1_val < 0) && (rs2_val > 0)) ||
                         ((rs1_val > 0) && (rs2_val < 0));

  DWord_t res = res_mpz.get_ui ();
  if (is_result_minus) {
    res = ~res + 1;
  }

  m_env->GRegWrite (rd_addr, res);

これはMULHSU命令でも基本的には変わらない。MULHSU命令は符号付64bitデータと符号なし64bitデータを乗算し、上位の64bitの結果をレジスタに格納する。

   DWord_t res = res_mpz2.get_ui ();
   if (is_result_minus) {
     res = ~res;
   }
   m_env->GRegWrite (rd_addr, res);

という訳で、無事に64bit乗算の演算命令もパスするようになった。

96% tests passed, 19 tests failed out of 445

Total Test time (real) =  68.28 sec

The following tests FAILED:
          8 - rv32mi-p-timer (Failed)
          9 - rv32si-p-csr (Failed)
         47 - rv32ui-pm-lrsc (Failed)
        126 - rv64mi-p-dirty (Failed)
        130 - rv64mi-p-mcsr (Failed)
        134 - rv64mi-p-timer (Failed)
        136 - rv64si-p-csr (Failed)
        154 - rv64uf-p-recoding (Failed)
        167 - rv64uf-pt-recoding (Failed)
        180 - rv64uf-v-recoding (Failed)
        191 - rv64ui-p-amomaxu_d (Failed)
        200 - rv64ui-p-amoswap_d (Failed)
        230 - rv64ui-pm-lrsc (Failed)
        274 - rv64ui-pt-amomaxu_d (Failed)
        283 - rv64ui-pt-amoswap_d (Failed)
        360 - rv64ui-v-amomaxu_d (Failed)
        369 - rv64ui-v-amoswap_d (Failed)
        436 - median (Failed)
        443 - spmv (Failed)
Errors while running CTest