Rocket-Chipの採用している乗算器について少しずつ眺めているが、まだ良く分からないことがある。 負数の扱いについてはどのようになっているのだろう。 負数の乗算については正直あまり知らなかったので、ここで調査してみる。
いくつか検索すると以下のようなサイトが上がってきた。
この辺を読むと、なるほど負数の乗算というのは難しいのだなとなっているが、これとRocket-ChipのMultiplierの実装の相違がまだ理解できていない。
Rocket-Chipでは、Scalaで乗算器が実装されている。
ここで、第1オペランド(lhs)が負数であればneg_out
という信号を有効化している。
when (io.req.fire()) { state := Mux(cmdMul, s_mul, Mux(lhs_sign || rhs_sign, s_neg_inputs, s_div)) isHi := cmdHi resHi := false count := (if (fastMulW) Mux[UInt](cmdMul && halfWidth(io.req.bits), w/cfg.mulUnroll/2, 0) else 0) neg_out := Mux(cmdHi, lhs_sign, lhs_sign =/= rhs_sign) divisor := Cat(rhs_sign, rhs_in) remainder := lhs_in req := io.req.bits }
ここから、ループを回しているのだが、最後の計算をする前に、負数の場合には最上位1ビットを挿入するようになっている。 しかし最初の計算の1ビットはどこで挿入しているんだ?
if (cfg.mulUnroll != 0) when (state === s_mul) { val mulReg = Cat(remainder(2*mulw+1,w+1),remainder(w-1,0)) val mplierSign = remainder(w) val mplier = mulReg(mulw-1,0) val accum = mulReg(2*mulw,mulw).asSInt val mpcand = divisor.asSInt val prod = Cat(mplierSign, mplier(cfg.mulUnroll-1, 0)).asSInt * mpcand + accum val nextMulReg = Cat(prod, mplier(mulw-1, cfg.mulUnroll)) val nextMplierSign = count === mulw/cfg.mulUnroll-2 && neg_out val eOutMask = ((BigInt(-1) << mulw).S >> (count * cfg.mulUnroll)(log2Up(mulw)-1,0))(mulw-1,0) val eOut = (cfg.mulEarlyOut).B && count =/= mulw/cfg.mulUnroll-1 && count =/= 0 && !isHi && (mplier & ~eOutMask) === 0.U val eOutRes = (mulReg >> (mulw - count * cfg.mulUnroll)(log2Up(mulw)-1,0)) val nextMulReg1 = Cat(nextMulReg(2*mulw,mulw), Mux(eOut, eOutRes, nextMulReg)(mulw-1,0)) remainder := Cat(nextMulReg1 >> w, nextMplierSign, nextMulReg1(w-1,0)) count := count + 1 when (eOut || count === mulw/cfg.mulUnroll-1) { state := s_done_mul resHi := isHi } }