Integer SRT16 Divider (#1019)

* New SRT4 divider that may improve timing

See "Digital reurrence dividers with reduced logical depth"

* SRT16 Int Divider that is working properly

* Fix bug related to div 1

* Timing improved version of SRT16 int divider

* Add copyright and made some minor changes

* Fix bugs related to div 0

* Fix another div 0 bug

* Fix another special case bug
This commit is contained in:
Li Qianruo 2021-09-23 14:17:39 +08:00 committed by GitHub
parent 46d289c739
commit a58e335197
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 948 additions and 192 deletions

View File

@ -122,6 +122,9 @@ tlc-test:
l1-test:
cd .. && mill XiangShan.test.testOnly -o -s cache.L1DTest.L1DCacheTest
int-divider-test:
cd .. && mill XiangShan.test.testOnly -o -s futest.IntDividerTest
unit-test-all:
cd .. && mill XiangShan.test.test -P$(P)

View File

@ -0,0 +1,468 @@
/***************************************************************************************
* Copyright (c) 2020-2021 Institute of Computing Technology, Chinese Academy of Sciences
* Copyright (c) 2020-2021 Peng Cheng Laboratory
*
* XiangShan is licensed under Mulan PSL v2.
* You can use this software according to the terms and conditions of the Mulan PSL v2.
* You may obtain a copy of Mulan PSL v2 at:
* http://license.coscl.org.cn/MulanPSL2
*
* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
* EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
*
* See the Mulan PSL v2 for more details.
***************************************************************************************/
// This file contains components originally written by Yifei He, see
// https://github.com/OpenXiangShan/XS-Verilog-Library/tree/main/int_div_radix_4_v1
// Email of original author: hyf_sysu@qq.com
package xiangshan.backend.fu
import chipsalliance.rocketchip.config.Parameters
import chisel3._
import chisel3.util._
import utils.SignExt
import xiangshan.backend.fu.util.CSA3_2
class SRT16DividerDataModule(len: Int) extends Module {
val io = IO(new Bundle() {
val src = Vec(2, Input(UInt(len.W)))
val valid, sign, kill_w, kill_r, isHi, isW = Input(Bool())
val in_ready = Output(Bool())
val out_valid = Output(Bool())
val out_data = Output(UInt(len.W))
val out_ready = Input(Bool())
})
// consts
val lzc_width = log2Up(len)
val itn_len = 1 + len + 2 + 1
val (a, d, sign, valid, kill_w, kill_r, isHi, isW) =
(io.src(0), io.src(1), io.sign, io.valid, io.kill_w, io.kill_r, io.isHi, io.isW)
val in_fire = valid && io.in_ready
val out_fire = io.out_ready && io.out_valid
val newReq = in_fire
val s_idle :: s_pre_0 :: s_pre_1 :: s_iter :: s_post_0 :: s_post_1 :: s_finish :: Nil = Enum(7)
val quot_neg_2 :: quot_neg_1 :: quot_0 :: quot_pos_1 :: quot_pos_2 :: Nil = Enum(5)
val state = RegInit(UIntToOH(s_idle, 7))
// reused wires
// val aNormAbs = Wire(UInt((len + 1).W)) // Inputs of xNormAbs regs below
// val dNormAbs = Wire(UInt((len + 1).W))
val quotIter = Wire(UInt(len.W))
val quotM1Iter = Wire(UInt(len.W))
val aLZC = Wire(UInt((lzc_width + 1).W))
val dLZC = Wire(UInt((lzc_width + 1).W))
val rNext = Wire(UInt(itn_len.W))
val rNextPd = Wire(UInt(itn_len.W))
val aInverter = Wire(UInt(len.W)) // results of global inverter
val dInverter = Wire(UInt(len.W))
val finalIter = Wire(Bool())
val special = Wire(Bool())
// reused regs
// val aNormAbsReg = RegEnable(aNormAbs, newReq | state(s_pre_0) | state(s_post_0)) // reg for normalized a & d and rem & rem+d
// val dNormAbsReg = RegEnable(dNormAbs, newReq | state(s_pre_0) | state(s_post_0))
val quotIterReg = RegEnable(quotIter, state(s_pre_1) | state(s_iter) | state(s_post_0))
val quotM1IterReg = RegEnable(quotM1Iter, state(s_pre_1) | state(s_iter) | state(s_post_0))
val specialReg = RegEnable(special, state(s_pre_1))
val aReg = RegEnable(a, in_fire)
when(kill_r) {
state := UIntToOH(s_idle, 7)
} .elsewhen(state(s_idle) && in_fire && !kill_w) {
state := UIntToOH(s_pre_0, 7)
} .elsewhen(state(s_pre_0)) { // leading zero detection
state := UIntToOH(s_pre_1, 7)
} .elsewhen(state(s_pre_1)) { // shift a/b
state := Mux(special, UIntToOH(s_post_1, 7), UIntToOH(s_iter, 7))
} .elsewhen(state(s_iter)) { // (ws[j+1], wc[j+1]) = 4(ws[j],wc[j]) - q(j+1)*d
state := Mux(finalIter, UIntToOH(s_post_0, 7), UIntToOH(s_iter, 7))
} .elsewhen(state(s_post_0)) { // if rem < 0, rem = rem + d
state := UIntToOH(s_post_1, 7)
} .elsewhen(state(s_post_1)) {
state := UIntToOH(s_finish, 7)
} .elsewhen(state(s_finish) && out_fire) {
state := UIntToOH(s_idle, 7)
} .otherwise {
state := state
}
io.in_ready := state(s_idle)
aInverter := -Mux(state(s_idle), a, quotIterReg) // 64, 0
dInverter := -Mux(state(s_idle), d, quotM1IterReg) // 64, 0
val aSign = io.sign && a(len - 1) // 1
val dSign = io.sign && d(len - 1)
val aAbs = Mux(aSign, aInverter, a) // 64, 0
val dAbs = Mux(dSign, dInverter, d)
val aAbsReg = RegEnable(aAbs, newReq)
val dAbsReg = RegEnable(dAbs, newReq)
val aNorm = (aAbsReg(len - 1, 0) << aLZC(lzc_width - 1, 0))(len - 1, 0) // 64, 65
val dNorm = (dAbsReg(len - 1, 0) << dLZC(lzc_width - 1, 0))(len - 1, 0)
val aNormReg = RegEnable(aNorm, state(s_pre_0))
val dNormReg = RegEnable(dNorm, state(s_pre_0))
// aNormAbs := Mux1H(Seq(
// state(s_idle) -> Cat(0.U(1.W), aAbs), // 65, 0
// state(s_pre_0) -> Cat(0.U(1.W), aNorm), // 65, 0
// state(s_post_0) -> rNext(len + 3, 3) // remainder 65, 64. highest is sign bit
// ))
// dNormAbs := Mux1H(Seq(
// state(s_idle) -> Cat(0.U(1.W), dAbs),
// state(s_pre_0) -> Cat(0.U(1.W), dNorm),
// state(s_post_0) -> rNextPd(len + 3, 3)
// ))
// Second cycle, state is pre_0
// calculate lzc and move div* and lzc diff check if no_iter_needed
aLZC := PriorityEncoder(aAbsReg(len - 1, 0).asBools().reverse)
dLZC := PriorityEncoder(dAbsReg(len - 1, 0).asBools().reverse)
val aLZCReg = RegEnable(aLZC, state(s_pre_0)) // 7, 0
val dLZCReg = RegEnable(dLZC, state(s_pre_0))
val lzcWireDiff = Cat(0.U(1.W), dLZC(lzc_width - 1, 0)) - Cat(0.U(1.W), aLZC(lzc_width - 1, 0)) // 7, 0
val lzcRegDiff = Cat(0.U(1.W), dLZCReg(lzc_width - 1, 0)) - Cat(0.U(1.W), aLZCReg(lzc_width - 1, 0))
// val lzcDiff = Mux(state(s_pre_0), lzcWireDiff, lzcRegDiff)
// special case:
// divisor is 1 or -1; dividend has less bits than divisor; divisor is zero
// s_pre_0:
val dIsOne = dLZC(lzc_width - 1, 0).andR()
val dIsZero = ~dNormReg.orR()
val aIsZero = RegEnable(aLZC(lzc_width), state(s_pre_0))
val aTooSmall = RegEnable(aLZC(lzc_width) | lzcWireDiff(lzc_width), state(s_pre_0))
special := dIsOne | dIsZero | aTooSmall
val quotSpecial = Mux(dIsZero, VecInit(Seq.fill(len)(true.B)).asUInt,
Mux(aTooSmall, 0.U,
Mux(dSign && ~(aReg.andR()), -aReg, aReg) // signed 2^(len-1)
))
val remSpecial = Mux(dIsZero, aReg,
Mux(aTooSmall, aReg, 0.U))
val quotSpecialReg = RegEnable(quotSpecial, state(s_pre_1))
val remSpecialReg = RegEnable(remSpecial, state(s_pre_1))
// s_pre_1
val quotSign = Mux(state(s_idle), aSign ^ dSign, true.B) // if not s_idle then must be s_pre_1 & dIsZero, and that we have
val rSign = aSign
val quotSignReg = RegEnable(quotSign, in_fire | (state(s_pre_1) & dIsZero))
val rSignReg = RegEnable(rSign, in_fire)
val rShift = lzcRegDiff(0)
val oddIter = lzcRegDiff(1) ^ lzcRegDiff(0)
val iterNum = Wire(UInt((lzc_width - 2).W))
val iterNumReg = RegEnable(iterNum, state(s_pre_1) | state(s_iter))
iterNum := Mux(state(s_pre_1), (lzcRegDiff + 1.U) >> 2, iterNumReg -% 1.U)
finalIter := iterNumReg === 0.U
val rSumInit = Cat(0.U(3.W), Mux(rShift, Cat(0.U(1.W), aNormReg), Cat(aNormReg, 0.U(1.W)))) //(1, 67), 0.001xxx
val rCarryInit = 0.U(itn_len.W)
val rSumInitTrunc = Cat(0.U(1.W), rSumInit(itn_len - 4, itn_len - 4 - 4 + 1)) // 0.00___
val mInitPos1 = MuxLookup(dNormReg(len-2, len-4), "b00100".U(5.W),
Array(
0.U -> "b00100".U(5.W),
1.U -> "b00100".U(5.W),
2.U -> "b00100".U(5.W),
3.U -> "b00110".U(5.W),
4.U -> "b00110".U(5.W),
5.U -> "b00110".U(5.W),
6.U -> "b00110".U(5.W),
7.U -> "b01000".U(5.W),
)
)
val mInitPos2 = MuxLookup(dNormReg(len-2, len-4), "b01100".U(5.W),
Array(
0.U -> "b01100".U(5.W),
1.U -> "b01110".U(5.W),
2.U -> "b01111".U(5.W),
3.U -> "b10000".U(5.W),
4.U -> "b10010".U(5.W),
5.U -> "b10100".U(5.W),
6.U -> "b10110".U(5.W),
7.U -> "b10110".U(5.W),
)
)
val initCmpPos1 = rSumInitTrunc >= mInitPos1
val initCmpPos2 = rSumInitTrunc >= mInitPos2
val qInit = Mux(initCmpPos2, UIntToOH(quot_pos_2, 5), Mux(initCmpPos1, UIntToOH(quot_pos_1, 5), UIntToOH(quot_0, 5)))
// in pre_1 we also obtain m_i + 16u * d for all u
// udNeg -> (rud, r2ud) -> (rudPmNeg, r2udPmNeg)
val dPos = Cat(0.U(1.W), dNormReg) // +d, 0.1xxx, (1, 64)
val dNeg = -Cat(0.U(1.W), dNormReg) // -d, 1.xxxx, (1, 64)
// val m = Wire(Vec(4, UInt(7.W))) // we have to sigext them to calculate rqd-m_k
// index 0 is for q=-2 and 4 is for q=2!!!
val mNeg = Wire(Vec(4, UInt(12.W))) // selected m, extended to (6, 6) bits
val rudNeg = Wire(Vec(5, UInt(10.W))) // (4, 6)
val r2udNeg = Wire(Vec(5, UInt(12.W))) // (6, 6)
// Selection Block with improved timing
val rudPmNeg = Wire(Vec(5, Vec(4, UInt(10.W)))) // -(r*u*d+m_k), (5, 5) bits
val r2ws = Wire(UInt(10.W)) // r^2*ws (5, 5) bits
val r2wc = Wire(UInt(10.W))
// calculating exact values of w
val udNeg = Wire(Vec(5, UInt(itn_len.W))) // (3, 65), 1 signExt'ed Bit
// val r3udNeg = Wire(Vec(5, UInt(13.W)))
// Speculative Block
val r2udPmNeg = Wire(Vec(5, Vec(4, UInt(13.W)))) // -(r^2*d*d+m_k), (7, 6) bits. 1st index for q 2nd for m
val r3ws = Wire(UInt(13.W)) // r^3*ws, (7, 6) bits
val r3wc = Wire(UInt(13.W))
val qSpec = Wire(Vec(5, UInt(5.W))) // 5 speculative results of qNext2
// output wires
val qNext = Wire(UInt(5.W))
val qNext2 = Wire(UInt(5.W))
val rCarryIter = Wire(UInt(itn_len.W)) // (1, 67)
val rSumIter = Wire(UInt(itn_len.W))
// val r3wsIter = Wire(UInt(13.W))
// val r3wcIter = Wire(UInt(13.W))
// Input Regs of whole Spec + Sel + sum adder block
val qPrevReg = RegEnable(Mux(state(s_pre_1), qInit, qNext2), state(s_pre_1) | state(s_iter))
val rSumReg = RegEnable(Mux(state(s_pre_1), rSumInit, rSumIter), state(s_pre_1) | state(s_iter)) // (1, 67)
val rCarryReg = RegEnable(Mux(state(s_pre_1), rCarryInit, rCarryIter), state(s_pre_1) | state(s_iter))
// Give values to the regs and wires above...
val dForLookup = dPos(len-2, len-4)
mNeg := VecInit(Cat(SignExt(MuxLookup(dNormReg(len-2, len-4), "b00000000".U(7.W), mLookUpTable2.minus_m(0)), 11), 0.U(1.W)), // (2, 5) -> (6, 6)
Cat(SignExt(MuxLookup(dNormReg(len-2, len-4), "b00000000".U(7.W), mLookUpTable2.minus_m(1)), 10) ,0.U(2.W)), // (3, 4) -> (6, 6)
Cat(SignExt(MuxLookup(dNormReg(len-2, len-4), "b00000000".U(7.W), mLookUpTable2.minus_m(2)), 10) ,0.U(2.W)),
Cat(SignExt(MuxLookup(dNormReg(len-2, len-4), "b00000000".U(7.W), mLookUpTable2.minus_m(3)), 11) ,0.U(1.W))
)
udNeg := VecInit( Cat(SignExt(dPos, 66), 0.U(2.W)),
Cat(SignExt(dPos, 67), 0.U(1.W)),
0.U,
Cat(SignExt(dNeg, 67), 0.U(1.W)),
Cat(SignExt(dNeg, 66), 0.U(2.W))
)
rudNeg := VecInit(Seq.tabulate(5){i => udNeg(i)(itn_len-2, itn_len-11)})
r2udNeg := VecInit(Seq.tabulate(5){i => udNeg(i)(itn_len-2, itn_len-13)})
// r3udNeg := VecInit(Seq.tabulate(5){i => udNeg(i)(itn_len-2, itn_len-13)})
rudPmNeg := VecInit(Seq.tabulate(5){i => VecInit(Seq.tabulate(4){ j => SignExt(rudNeg(i)(9, 1), 10) + mNeg(j)(10, 1)})})
r2udPmNeg := VecInit(Seq.tabulate(5){i => VecInit(Seq.tabulate(4){ j => SignExt(r2udNeg(i), 13) + SignExt(mNeg(j), 13)})})
r3ws := rSumReg(itn_len-1, itn_len-13)
r3wc := rCarryReg(itn_len-1, itn_len-13)
r2ws := rSumReg(itn_len-1, itn_len-10)
r2wc := rCarryReg(itn_len-1, itn_len-10)
val udNegReg = RegEnable(udNeg, state(s_pre_1))
// val rudNegReg = RegEnable(rudNeg, state(s_pre_1))
val rudPmNegReg = RegEnable(rudPmNeg, state(s_pre_1))
val r2udPmNegReg = RegEnable(r2udPmNeg, state(s_pre_1))
def DetectSign(signs: UInt, name: String): UInt = {
val qVec = Wire(Vec(5, Bool())).suggestName(name)
qVec(quot_neg_2) := signs(0) && signs(1) && signs(2)
qVec(quot_neg_1) := ~signs(0) && signs(1) && signs(2)
qVec(quot_0) := signs(2) && ~signs(1)
qVec(quot_pos_1) := signs(3) && ~signs(2) && ~signs(1)
qVec(quot_pos_2) := ~signs(3) && ~signs(2) && ~signs(1)
qVec.asUInt
}
// Selection block
val signs = VecInit(Seq.tabulate(4){ i => {
val csa = Module(new CSA3_2(10)).suggestName(s"csa_sel_${i}")
csa.io.in(0) := r2ws
csa.io.in(1) := r2wc
csa.io.in(2) := Mux1H(qPrevReg, rudPmNegReg.toSeq)(i) // rudPmNeg(OHToUInt(qPrevReg))(i)
(csa.io.out(0) + (csa.io.out(1)(8, 0) << 1))(9)
}})
qNext := DetectSign(signs.asUInt, s"sel_q")
val csaWide1 = Module(new CSA3_2(itn_len)).suggestName("csa_sel_wide_1")
val csaWide2 = Module(new CSA3_2(itn_len)).suggestName("csa_sel_wide_2")
csaWide1.io.in(0) := rSumReg << 2
csaWide1.io.in(1) := rCarryReg << 2
csaWide1.io.in(2) := Mux1H(qPrevReg, udNegReg.toSeq) << 2//udNeg(OHToUInt(qPrevReg)) << 2
csaWide2.io.in(0) := csaWide1.io.out(0) << 2
csaWide2.io.in(1) := (csaWide1.io.out(1) << 1)(itn_len-1, 0) << 2
csaWide2.io.in(2) := Mux1H(qNext, udNegReg.toSeq) << 2 // udNeg(OHToUInt(qNext)) << 2
rSumIter := Mux(~oddIter & finalIter, csaWide1.io.out(0), csaWide2.io.out(0))
rCarryIter := Mux(~oddIter & finalIter, (csaWide1.io.out(1) << 1)(itn_len-1, 0), (csaWide2.io.out(1) << 1)(itn_len-1, 0))
// r3wsIter := r3udNeg(OHToUInt(qNext))
// r3wcIter := (csaWide1.io.out(0)(itn_len-3, itn_len-16) + (csaWide1.io.out(1) << 1)(itn_len-3, itn_len-16))(13,1)
// Speculative block
qSpec := VecInit(Seq.tabulate(5){ q_spec => {
val csa1 = Module(new CSA3_2(13)).suggestName(s"csa_spec_${q_spec}")
csa1.io.in(0) := r3ws
csa1.io.in(1) := r3wc
csa1.io.in(2) := SignExt(udNegReg(q_spec)(itn_len-2, itn_len-11), 13) // (4, 6) -> (7, 6)
val signs2 = VecInit(Seq.tabulate(4){ i => {
val csa2 = Module(new CSA3_2(13)).suggestName(s"csa_spec_${q_spec}_${i}")
csa2.io.in(0) := csa1.io.out(0)
csa2.io.in(1) := (csa1.io.out(1) << 1)(12, 0)
csa2.io.in(2) := Mux1H(qPrevReg, r2udPmNegReg.toSeq)(i) // r2udPmNeg(OHToUInt(qPrevReg))(i)
(csa2.io.out(0) + (csa2.io.out(1)(11, 0) << 1))(12)
}})
val qVec2 = DetectSign(signs2.asUInt, s"spec_q_${q_spec}")
qVec2
}})
// qNext2 := qSpec(OHToUInt(qNext)) // TODO: Use Mux1H!!
qNext2 := Mux1H(qNext, qSpec.toSeq)
// on the fly quotient conversion
val quotHalfIter = Wire(UInt(64.W))
val quotM1HalfIter = Wire(UInt(64.W))
val quotIterNext = Wire(UInt(64.W))
val quotM1IterNext = Wire(UInt(64.W))
def OTFC(q: UInt, quot: UInt, quotM1: UInt): (UInt, UInt) = {
val quotNext = Mux1H(Seq(
q(quot_pos_2) -> (quot << 2 | "b10".U),
q(quot_pos_1) -> (quot << 2 | "b01".U),
q(quot_0) -> (quot << 2 | "b00".U),
q(quot_neg_1) -> (quotM1 << 2 | "b11".U),
q(quot_neg_2) -> (quotM1 << 2 | "b10".U)
))
val quotM1Next = Mux1H(Seq(
q(quot_pos_2) -> (quot << 2 | "b01".U),
q(quot_pos_1) -> (quot << 2 | "b00".U),
q(quot_0) -> (quotM1 << 2 | "b11".U),
q(quot_neg_1) -> (quotM1 << 2 | "b10".U),
q(quot_neg_2) -> (quotM1 << 2 | "b01".U)
))
(quotNext(len-1, 0), quotM1Next(len-1, 0))
}
quotHalfIter := OTFC(qPrevReg, quotIterReg, quotM1IterReg)._1
quotM1HalfIter := OTFC(qPrevReg, quotIterReg, quotM1IterReg)._2
quotIterNext := Mux(~oddIter && finalIter, quotHalfIter, OTFC(qNext, quotHalfIter, quotM1HalfIter)._1)
quotM1IterNext := Mux(~oddIter && finalIter, quotM1HalfIter, OTFC(qNext, quotHalfIter, quotM1HalfIter)._2)
// quotIter := Mux(state(s_pre_1), 0.U(len.W),
// Mux(state(s_iter), quotIterNext,
// Mux(quotSignReg, aInverter, quotIterReg)))
// quotM1Iter := Mux(state(s_pre_1),
// 0.U(len.W), Mux(state(s_iter), quotM1IterNext,
// Mux(quotSignReg, dInverter, quotM1IterReg)))
quotIter := Mux(state(s_iter), quotIterNext,
Mux(state(s_pre_1), 0.U(len.W),
Mux(quotSignReg, aInverter, quotIterReg)))
quotM1Iter := Mux(state(s_iter), quotM1IterNext,
Mux(state(s_pre_1), 0.U(len.W),
Mux(quotSignReg, dInverter, quotM1IterReg)))
// finally, to the recovery stages!
when(rSignReg) {
rNext := ~rSumReg + ~rCarryReg + 2.U
rNextPd := ~rSumReg + ~rCarryReg + ~Cat(0.U(1.W), dNormReg, 0.U(3.W)) + 3.U
} .otherwise {
rNext := rSumReg + rCarryReg
rNextPd := rSumReg + rCarryReg + Cat(0.U(1.W), dNormReg, 0.U(3.W))
}
val rNextReg = RegEnable(rNext(len + 3, 3), state(s_post_0))
val rNextPdReg = RegEnable(rNextPd(len + 3, 3), state(s_post_0))
dontTouch(rNextReg)
// post_1
val r = rNextReg
val rPd = rNextPdReg
val rIsZero = ~(r.orR())
val needCorr = Mux(rSignReg, ~r(len) & r.orR(), r(len)) // when we get pos rem for a<0 or neg rem for a>0
val rPreShifted = Mux(needCorr, rPd, r)
val rightShifter = Module(new RightShifter(len, lzc_width))
rightShifter.io.in := rPreShifted
rightShifter.io.shiftNum := dLZCReg
rightShifter.io.msb := rSignReg
val rShifted = rightShifter.io.out
val rFinal = RegEnable(Mux(specialReg, remSpecialReg, rShifted), state(s_post_1))// right shifted remainder. shift by the number of bits divisor is shifted
val qFinal = RegEnable(Mux(specialReg, quotSpecialReg, Mux(needCorr, quotM1IterReg, quotIterReg)), state(s_post_1))
val res = Mux(isHi, rFinal, qFinal)
io.out_data := Mux(isW,
SignExt(res(31, 0), len),
res
)
io.in_ready := state(s_idle)
io.out_valid := state(s_finish)
}
object mLookUpTable2 {
// Usage :
// result := decoder(QMCMinimizer, index, mLookupTable.xxx)
val minus_m = Seq(
Array( // -m[-1]
0.U -> "b00_11010".U(7.W),
1.U -> "b00_11110".U(7.W),
2.U -> "b01_00000".U(7.W),
3.U -> "b01_00100".U(7.W),
4.U -> "b01_00110".U(7.W),
5.U -> "b01_01010".U(7.W),
6.U -> "b01_01100".U(7.W),
7.U -> "b01_10000".U(7.W)
),
Array( // -m[0]
0.U -> "b000_0100".U(7.W),
1.U -> "b000_0110".U(7.W),
2.U -> "b000_0110".U(7.W),
3.U -> "b000_0110".U(7.W),
4.U -> "b000_1000".U(7.W),
5.U -> "b000_1000".U(7.W),
6.U -> "b000_1000".U(7.W),
7.U -> "b000_1000".U(7.W)
),
Array( //-m[1]
0.U -> "b111_1101".U(7.W),
1.U -> "b111_1100".U(7.W),
2.U -> "b111_1100".U(7.W),
3.U -> "b111_1100".U(7.W),
4.U -> "b111_1011".U(7.W),
5.U -> "b111_1010".U(7.W),
6.U -> "b111_1010".U(7.W),
7.U -> "b111_1010".U(7.W)
),
Array( //-m[2]
0.U -> "b11_01000".U(7.W),
1.U -> "b11_00100".U(7.W),
2.U -> "b11_00010".U(7.W),
3.U -> "b10_11110".U(7.W),
4.U -> "b10_11100".U(7.W),
5.U -> "b10_11000".U(7.W),
6.U -> "b10_10110".U(7.W),
7.U -> "b10_10010".U(7.W)
))
}
class SRT16Divider(len: Int)(implicit p: Parameters) extends AbstractDivider(len) {
val newReq = io.in.fire()
val uop = io.in.bits.uop
val uopReg = RegEnable(uop, newReq)
val ctrlReg = RegEnable(ctrl, newReq)
val divDataModule = Module(new SRT16DividerDataModule(len))
val kill_w = uop.roqIdx.needFlush(io.redirectIn, io.flushIn)
val kill_r = !divDataModule.io.in_ready && uopReg.roqIdx.needFlush(io.redirectIn, io.flushIn)
divDataModule.io.src(0) := io.in.bits.src(0)
divDataModule.io.src(1) := io.in.bits.src(1)
divDataModule.io.valid := io.in.valid
divDataModule.io.sign := sign
divDataModule.io.kill_w := kill_w
divDataModule.io.kill_r := kill_r
divDataModule.io.isHi := ctrlReg.isHi
divDataModule.io.isW := ctrlReg.isW
divDataModule.io.out_ready := io.out.ready
io.in.ready := divDataModule.io.in_ready
io.out.valid := divDataModule.io.out_valid
io.out.bits.data := divDataModule.io.out_data
io.out.bits.uop := uopReg
}

View File

@ -14,6 +14,10 @@
* See the Mulan PSL v2 for more details.
***************************************************************************************/
// The "SRT4DividerDataModule" in this file is a scala rewrite of SRT4 divider by Yifei He, see
// https://github.com/OpenXiangShan/XS-Verilog-Library/tree/main/int_div_radix_4_v1
// Email of original author: hyf_sysu@qq.com
package xiangshan.backend.fu
import chipsalliance.rocketchip.config.Parameters
@ -36,229 +40,395 @@ class SRT4DividerDataModule(len: Int) extends Module {
val out_ready = Input(Bool())
})
val (a, b, sign, valid, kill_w, kill_r, isHi, isW) =
// consts
val lzc_width = log2Up(len)
val itn_len = 1 + len + 2 + 1
require(lzc_width == 6)
val (a, d, sign, valid, kill_w, kill_r, isHi, isW) =
(io.src(0), io.src(1), io.sign, io.valid, io.kill_w, io.kill_r, io.isHi, io.isW)
val in_fire = valid && io.in_ready
val out_fire = io.out_ready && io.out_valid
// s_pad_* is not used
val s_idle :: s_lzd :: s_normlize :: s_recurrence :: s_recovery_1 :: s_recovery_2 :: s_pad_1 :: s_pad_2 :: s_finish :: Nil = Enum(9)
require(s_finish.litValue() == 8)
val state = RegInit(s_idle)
val finished = state(3).asBool // state === s_finish
val cnt_next = Wire(UInt(log2Up((len + 3) / 2).W))
val cnt = RegEnable(cnt_next, state === s_normlize || state === s_recurrence)
val rec_enough = cnt_next === 0.U
val newReq = in_fire
val startHandShake = io.in_ready && valid
val s_idle :: s_pre_0 :: s_pre_1 :: s_iter :: s_post_0 :: s_post_1 :: s_finish :: Nil = Enum(7)
def abs(a: UInt, sign: Bool): (Bool, UInt) = {
val s = a(len - 1) && sign
(s, Mux(s, -a, a))
}
val state = RegInit(UIntToOH(s_idle, 7))
val (aSign, aVal) = abs(a, sign)
val (bSign, bVal) = abs(b, sign)
val aSignReg = RegEnable(aSign, newReq)
val qSignReg = RegEnable(aSign ^ bSign, newReq)
val divZero = b === 0.U
val divZeroReg = RegEnable(divZero, newReq)
val quot_neg_2 :: quot_neg_1 :: quot_0 :: quot_pos_1 :: quot_pos_2 :: Nil = Enum(5)
val finished = state(s_finish)
// reused wire declarations
val aIsZero = Wire(Bool())
val dIsZero = Wire(Bool())
val aTooSmall = Wire(Bool()) // this is output of reg!
val noIter = Wire(Bool()) // this is output of reg!
val finalIter = Wire(Bool())
val aLZC = Wire(UInt((lzc_width + 1).W))
val dLZC = Wire(UInt((lzc_width + 1).W))
val aNormAbs = Wire(UInt((len + 1).W))
val dNormAbs = Wire(UInt((len + 1).W))
val aInverter = Wire(UInt(len.W)) // results of global inverter
val dInverter = Wire(UInt(len.W))
val rPreShifted = Wire(UInt((len + 1).W))
val quotIter = Wire(UInt(len.W))
val quotM1Iter = Wire(UInt(len.W))
val qIterEnd = Wire(UInt(5.W))
val rNext = Wire(UInt(itn_len.W))
val rNextPd = Wire(UInt(itn_len.W)) // non-redundant remainder plus d, 68, 67
//reused ctrl regs
//reused other regs
val aNormAbsReg = RegEnable(aNormAbs, startHandShake | state(s_pre_0) | state(s_post_0)) // reg for normalized a & d and rem & rem+d
val dNormAbsReg = RegEnable(dNormAbs, startHandShake | state(s_pre_0) | state(s_post_0))
val quotIterReg = RegEnable(quotIter, state(s_pre_1) | state(s_iter) | state(s_post_0))
val quotM1IterReg = RegEnable(quotM1Iter, state(s_pre_1) | state(s_iter) | state(s_post_0))
switch(state) {
is(s_idle) {
when(in_fire && !kill_w) {
state := Mux(divZero, s_finish, s_lzd)
}
}
is(s_lzd) { // leading zero detection
state := s_normlize
}
is(s_normlize) { // shift a/b
state := s_recurrence
}
is(s_recurrence) { // (ws[j+1], wc[j+1]) = 4(ws[j],wc[j]) - q(j+1)*d
when(rec_enough) {
state := s_recovery_1
}
}
is(s_recovery_1) { // if rem < 0, rem = rem + d
state := s_recovery_2
}
is(s_recovery_2) { // recovery shift
state := s_finish
}
is(s_finish) {
when(out_fire) {
state := s_idle
}
}
}
when(kill_r) {
state := s_idle
state := UIntToOH(s_idle, 7)
} .elsewhen(state(s_idle) && in_fire && !kill_w) {
state := UIntToOH(s_pre_0, 7)
} .elsewhen(state(s_pre_0)) { // leading zero detection
state := UIntToOH(s_pre_1, 7)
} .elsewhen(state(s_pre_1)) { // shift a/b
state := Mux(dIsZero | aTooSmall | noIter, UIntToOH(s_post_0, 7), UIntToOH(s_iter, 7))
} .elsewhen(state(s_iter)) { // (ws[j+1], wc[j+1]) = 4(ws[j],wc[j]) - q(j+1)*d
state := Mux(finalIter, UIntToOH(s_post_0, 7), UIntToOH(s_iter, 7))
} .elsewhen(state(s_post_0)) { // if rem < 0, rem = rem + d
state := UIntToOH(s_post_1, 7)
} .elsewhen(state(s_post_1)) {
state := UIntToOH(s_finish, 7)
} .elsewhen(state(s_finish) && out_fire) {
state := UIntToOH(s_idle, 7)
} .otherwise {
state := state
}
/** Calculate abs(a)/abs(b) by recurrence
*
* ws, wc: partial remainder in carry-save form,
* in recurrence steps, ws/wc = 4ws[j]/4wc[j];
* in recovery step, ws/wc = ws[j]/wc[j];
* in final step, ws = abs(a)/abs(b).
*
* d: normlized divisor(1/2<=d<1)
*
* wLen = 3 integer bits + (len+1) frac bits
*/
def wLen = 3 + len + 1
// First cycle:
// State is idle, we gain absolute value of a and b, using global inverter
val ws, wc = Reg(UInt(wLen.W))
val ws_next, wc_next = Wire(UInt(wLen.W))
val d = Reg(UInt(wLen.W))
io.in_ready := state(s_idle)
val aLeadingZeros = RegEnable(
next = PriorityEncoder(ws(len - 1, 0).asBools().reverse),
enable = state === s_lzd
aInverter := -Mux(state(s_idle), a, quotIterReg) // 64, 0
dInverter := -Mux(state(s_idle), d, quotM1IterReg) // 64, 0
val aSign = io.sign && a(len - 1) // 1
val dSign = io.sign && d(len - 1)
val aAbs = Mux(aSign, aInverter, a) // 64, 0
val dAbs = Mux(dSign, dInverter, d)
val aNorm = (aNormAbsReg(len - 1, 0) << aLZC(lzc_width - 1, 0))(len - 1, 0) // 64, 65
val dNorm = (dNormAbsReg(len - 1, 0) << dLZC(lzc_width - 1, 0))(len - 1, 0)
aNormAbs := Mux1H(Seq(
state(s_idle) -> Cat(0.U(1.W), aAbs), // 65, 0
state(s_pre_0) -> Cat(0.U(1.W), aNorm), // 65, 0
state(s_post_0) -> rNext(len + 3, 3) // remainder 65, 64. highest is sign bit
))
dNormAbs := Mux1H(Seq(
state(s_idle) -> Cat(0.U(1.W), dAbs),
state(s_pre_0) -> Cat(0.U(1.W), dNorm),
state(s_post_0) -> rNextPd(len + 3, 3)
))
// Second cycle, state is pre_0
// calculate lzc and move div* and lzc diff check if no_iter_needed
aLZC := PriorityEncoder(aNormAbsReg(len - 1, 0).asBools().reverse)
dLZC := PriorityEncoder(dNormAbsReg(len - 1, 0).asBools().reverse)
val aLZCReg = RegEnable(aLZC, state(s_pre_0)) // 7, 0
val dLZCReg = RegEnable(dLZC, state(s_pre_0))
val lzcWireDiff = Cat(0.U(1.W), dLZC(lzc_width - 1, 0)) - Cat(0.U(1.W), aLZC(lzc_width - 1, 0)) // 7, 0
val lzcRegDiff = Cat(0.U(1.W), dLZCReg(lzc_width - 1, 0)) - Cat(0.U(1.W), aLZCReg(lzc_width - 1, 0))
val lzcDiff = Mux(state(s_pre_0), lzcWireDiff, lzcRegDiff)
aIsZero := aLZC(lzc_width) // this is state pre_0
dIsZero := dLZCReg(lzc_width) // this is pre_1 and all stages after
val dIsOne = dLZC(lzc_width - 1, 0).andR() // this is pre_0
val noIterReg = RegEnable(dIsOne & aNormAbsReg(len - 1), state(s_pre_0)) // This means dividend has lzc 0 so iter is 17
noIter := noIterReg
val aTooSmallReg = RegEnable(aIsZero | lzcDiff(lzc_width), state(s_pre_0)) // a is zero or a smaller than d
aTooSmall := aTooSmallReg
val quotSign = Mux(state(s_idle), aSign ^ dSign, true.B) // if not s_idle then must be s_pre_1 & dIsZero, and that we have
val rSign = aSign
val quotSignReg = RegEnable(quotSign, startHandShake | (state(s_pre_1) & dIsZero))
val rSignReg = RegEnable(rSign, startHandShake)
val rShift = lzcDiff(0) // odd lzc diff, for SRT4
val rightShifted = Wire(UInt(len.W))
val rSumInit = Mux(aTooSmallReg | aIsZero, Cat(0.U(1.W), rightShifted, 0.U(3.W)), // right shift the dividend (which is already l-shifted)
Mux(noIterReg, 0.U(itn_len.W), //
Cat(0.U(3.W),
Mux(rShift, Cat(0.U(1.W), aNormAbsReg(len - 1, 0)), Cat(aNormAbsReg(len - 1, 0), 0.U(1.W)))
) // Normal init value. 68, 67; For even lzcDiff, 0.001xxx0; for odd lzcDiff 0.0001xxx
)
) // state is s_pre_1
val rCarryInit = 0.U(itn_len.W)
val rightShifter = Module(new RightShifter(len, lzc_width))
rightShifter.io.in := Mux(state(s_pre_1), aNormAbsReg(len - 1, 0), rPreShifted(len - 1, 0))
rightShifter.io.shiftNum := Mux(state(s_pre_1), aLZCReg,
Mux(aTooSmallReg | dIsZero, 0.U(lzc_width.W), dLZCReg))
rightShifter.io.msb := state(s_post_1) & rSignReg & rPreShifted(len)
rightShifted := rightShifter.io.out
// obtaining 1st quotient
val rSumInitTrunc = Cat(0.U(1.W), rSumInit(itn_len - 4, itn_len - 4 - 4 + 1)) // 0.00___
val mInitPos1 = MuxLookup(dNormAbsReg(len - 2, len - 2 - 3 + 1), "b00100".U(5.W),
Array(
0.U -> "b00100".U(5.W),
1.U -> "b00100".U(5.W),
2.U -> "b00100".U(5.W),
3.U -> "b00110".U(5.W),
4.U -> "b00110".U(5.W),
5.U -> "b00110".U(5.W),
6.U -> "b00110".U(5.W),
7.U -> "b01000".U(5.W),
)
)
val bLeadingZeros = RegEnable(
next = PriorityEncoder(d(len - 1, 0).asBools().reverse),
enable = state === s_lzd
val mInitPos2 = MuxLookup(dNormAbsReg(len - 2, len - 2 - 3 + 1), "b01100".U(5.W),
Array(
0.U -> "b01100".U(5.W),
1.U -> "b01110".U(5.W),
2.U -> "b01111".U(5.W),
3.U -> "b10000".U(5.W),
4.U -> "b10010".U(5.W),
5.U -> "b10100".U(5.W),
6.U -> "b10110".U(5.W),
7.U -> "b10110".U(5.W),
)
)
val diff = Cat(0.U(1.W), bLeadingZeros).asSInt() - Cat(0.U(1.W), aLeadingZeros).asSInt()
val isNegDiff = diff(diff.getWidth - 1)
val quotientBits = Mux(isNegDiff, 0.U, diff.asUInt())
val qBitsIsOdd = quotientBits(0)
val recoveryShift = RegEnable(len.U - bLeadingZeros, state === s_normlize)
val a_shifted, b_shifted = Wire(UInt(len.W))
a_shifted := Mux(isNegDiff,
ws(len - 1, 0) << bLeadingZeros,
ws(len - 1, 0) << aLeadingZeros
)
b_shifted := d(len - 1, 0) << bLeadingZeros
val initCmpPos1 = rSumInitTrunc >= mInitPos1
val initCmpPos2 = rSumInitTrunc >= mInitPos2
val qInit = Mux(initCmpPos2, UIntToOH(quot_pos_2, 5), Mux(initCmpPos1, UIntToOH(quot_pos_1, 5), UIntToOH(quot_0, 5)))
val qPrev = Mux(state(s_pre_1), qInit, qIterEnd)
val qPrevReg = RegEnable(qPrev, state(s_pre_1) | state(s_iter))
val specialDivisorReg = RegEnable(dNormAbsReg(len - 2, len - 2 - 3 + 1) === 0.U, state(s_pre_1)) // d=0.1000xxx
// rCarry and rSum in Iteration
val qXd = Mux1H(Seq(
qPrevReg(quot_neg_2) -> Cat(dNormAbsReg(len - 1, 0), 0.U(4.W)), // 68, 67 1.xxxxx0000
qPrevReg(quot_neg_1) -> Cat(0.U(1.W), dNormAbsReg(len - 1, 0), 0.U(3.W)), // 0.1xxxxx000
qPrevReg(quot_0) -> 0.U(itn_len.W),
qPrevReg(quot_pos_1) -> ~Cat(0.U(1.W), dNormAbsReg(len - 1, 0), 0.U(3.W)), // don't forget to plus 1 later
qPrevReg(quot_pos_2) -> ~Cat(dNormAbsReg(len - 1, 0), 0.U(4.W)) // don't forget to plus 1 later
))
val csa = Module(new CSA3_2(itn_len))
val rem_temp = ws + wc
val rem_fixed = RegEnable(Mux(rem_temp(wLen - 1), rem_temp + d, rem_temp), state === s_recovery_1)
val rem_abs = RegEnable((rem_fixed << recoveryShift) (2 * len, len + 1), state === s_recovery_2)
val rSumIter = csa.io.out(0)
val rCarryIter = Cat(csa.io.out(1)(itn_len - 2, 0), qPrevReg(quot_pos_1) | qPrevReg(quot_pos_2))
val rSumReg = RegEnable(Mux(state(s_pre_1), rSumInit, rSumIter), state(s_pre_1) | state(s_iter)) // 68, 67
val rCarryReg = RegEnable(Mux(state(s_pre_1), rCarryInit, rCarryIter), state(s_pre_1) | state(s_iter))
csa.io.in(0) := rSumReg << 2
csa.io.in(1) := rCarryReg << 2
csa.io.in(2) := qXd
when(newReq) {
ws := Cat(0.U(4.W), Mux(divZero, a, aVal))
wc := 0.U
d := Cat(0.U(4.W), bVal)
}.elsewhen(state === s_normlize) {
d := Cat(0.U(3.W), b_shifted, 0.U(1.W))
ws := Mux(qBitsIsOdd, a_shifted, a_shifted << 1)
}.elsewhen(state === s_recurrence) {
ws := Mux(rec_enough, ws_next, ws_next << 2)
wc := Mux(rec_enough, wc_next, wc_next << 2)
}
val qds = Module(new SRT4QDS(len, itn_len))
qds.io.remSum := rSumReg
qds.io.remCarry := rCarryReg
qds.io.d := dNormAbsReg(len - 1, 0) // Maybe optimize here to lower power consumption?
qds.io.specialDivisor := specialDivisorReg
qds.io.qPrev := qPrevReg
qIterEnd := qds.io.qIterEnd
cnt_next := Mux(state === s_normlize, (quotientBits + 3.U) >> 1, cnt - 1.U)
/** Quotient selection
*
* the quotient selection table use truncated 7-bit remainder
* and 3-bit divisor
*/
val sel_0 :: sel_d :: sel_dx2 :: sel_neg_d :: sel_neg_dx2 :: Nil = Enum(5)
val dx2, neg_d, neg_dx2 = Wire(UInt(wLen.W))
dx2 := d << 1
neg_d := (~d).asUInt() // add '1' in carry-save adder later
neg_dx2 := neg_d << 1
val q_sel = Wire(UInt(3.W))
val wc_adj = MuxLookup(q_sel, 0.U(2.W), Seq(
sel_d -> 1.U(2.W),
sel_dx2 -> 2.U(2.W)
//on the fly conversion
val quotIterNext = Wire(UInt(len.W))
val quotIterM1Next = Wire(UInt(len.W))
quotIterNext := Mux1H(Seq(
qPrevReg(quot_pos_2) -> (quotIterReg << 2 | "b10".U),
qPrevReg(quot_pos_1) -> (quotIterReg << 2 | "b01".U),
qPrevReg(quot_0) -> (quotIterReg << 2 | "b00".U),
qPrevReg(quot_neg_1) -> (quotM1IterReg << 2 | "b11".U),
qPrevReg(quot_neg_2) -> (quotM1IterReg << 2 | "b10".U)
))
quotIterM1Next := Mux1H(Seq(
qPrevReg(quot_pos_2) -> (quotIterReg << 2 | "b01".U),
qPrevReg(quot_pos_1) -> (quotIterReg << 2 | "b00".U),
qPrevReg(quot_0) -> (quotM1IterReg << 2 | "b11".U),
qPrevReg(quot_neg_1) -> (quotM1IterReg << 2 | "b10".U),
qPrevReg(quot_neg_2) -> (quotM1IterReg << 2 | "b01".U)
))
val w_truncated = (ws(wLen - 1, wLen - 1 - 6) + wc(wLen - 1, wLen - 1 - 6)).asSInt()
val d_truncated = b_shifted.tail(1).head(3)
val qSelTable = Array(
Array(12, 4, -4, -13),
Array(14, 4, -6, -15),
Array(15, 4, -6, -16),
Array(16, 4, -6, -18),
Array(18, 6, -8, -20),
Array(20, 6, -8, -20),
Array(20, 8, -8, -22),
Array(24, 8, -8, -24)
)
quotIter := Mux(state(s_pre_1),
Mux(dIsZero, VecInit(Seq.fill(len)(true.B)).asUInt,
Mux(noIterReg, aNormAbsReg(len - 1, 0), 0.U(len.W))),
Mux(state(s_iter), quotIterNext,
Mux(quotSignReg, aInverter, quotIterReg)))
quotM1Iter := Mux(state(s_pre_1),
0.U(len.W), Mux(state(s_iter), quotIterM1Next,
Mux(quotSignReg, dInverter, quotM1IterReg)))
val table = RegEnable(
VecInit(qSelTable.map(row =>
VecInit(row.map(k => k.S(7.W)))
))(d_truncated),
state === s_normlize
)
q_sel := MuxCase(sel_neg_dx2,
table.zip(Seq(sel_dx2, sel_d, sel_0, sel_neg_d)).map {
case (k, s) => (w_truncated >= k) -> s
}
)
// iter num
val iterNum = Wire(UInt((lzc_width - 1).W))
val iterNumReg = RegEnable(iterNum, state(s_pre_1) | state(s_iter))
/** Calculate (ws[j+1],wc[j+1]) by a [3-2]carry-save adder
*
* (ws[j+1], wc[j+1]) = 4(ws[j],wc[j]) - q(j+1)*d
*/
val csa = Module(new CSA3_2(wLen))
csa.io.in(0) := ws
csa.io.in(1) := Cat(wc(wLen - 1, 2), wc_adj)
csa.io.in(2) := MuxLookup(q_sel, 0.U, Seq(
sel_d -> neg_d,
sel_dx2 -> neg_dx2,
sel_neg_d -> d,
sel_neg_dx2 -> dx2
))
ws_next := csa.io.out(0)
wc_next := csa.io.out(1) << 1
iterNum := Mux(state(s_pre_1), lzcDiff(lzc_width - 1, 1) +% lzcDiff(0), iterNumReg -% 1.U)
finalIter := iterNumReg === 0.U
// On the fly quotient conversion
val q, qm = Reg(UInt(len.W))
when(newReq) {
q := 0.U
qm := 0.U
}.elsewhen(state === s_recurrence) {
val qMap = Seq(
sel_0 -> (q, 0),
sel_d -> (q, 1),
sel_dx2 -> (q, 2),
sel_neg_d -> (qm, 3),
sel_neg_dx2 -> (qm, 2)
)
q := MuxLookup(q_sel, 0.U,
qMap.map(m => m._1 -> Cat(m._2._1(len - 3, 0), m._2._2.U(2.W)))
)
val qmMap = Seq(
sel_0 -> (qm, 3),
sel_d -> (q, 0),
sel_dx2 -> (q, 1),
sel_neg_d -> (qm, 2),
sel_neg_dx2 -> (qm, 1)
)
qm := MuxLookup(q_sel, 0.U,
qmMap.map(m => m._1 -> Cat(m._2._1(len - 3, 0), m._2._2.U(2.W)))
)
}.elsewhen(state === s_recovery_1) {
q := Mux(rem_temp(wLen - 1), qm, q)
// Post Process
when(rSignReg) {
rNext := ~rSumReg + ~rCarryReg + 2.U
rNextPd := ~rSumReg + ~rCarryReg + ~Cat(0.U(1.W), dNormAbsReg(len - 1, 0), 0.U(3.W)) + 3.U
} .otherwise {
rNext := rSumReg + rCarryReg
rNextPd := rSumReg + rCarryReg + Cat(0.U(1.W), dNormAbsReg(len - 1, 0), 0.U(3.W))
}
val remainder = Mux(aSignReg, -rem_abs(len - 1, 0), rem_abs(len - 1, 0))
val quotient = Mux(qSignReg, -q, q)
val res = Mux(isHi,
Mux(divZeroReg, ws(len - 1, 0), remainder),
Mux(divZeroReg, Fill(len, 1.U(1.W)), quotient)
)
val r = aNormAbsReg
val rPd = dNormAbsReg
val rIsZero = ~(r.orR())
val needCorr = (~dIsZero & ~noIterReg) & Mux(rSignReg, ~r(len) & ~rIsZero, r(len)) // when we get pos rem for d<0 or neg rem for d>0
rPreShifted := Mux(needCorr, rPd, r)
val rFinal = RegEnable(rightShifted, state(s_post_1))// right shifted remainder. shift by the number of bits divisor is shifted
val qFinal = Mux(needCorr, quotM1IterReg, quotIterReg)
val res = Mux(isHi, rFinal, qFinal)
io.out_data := Mux(isW,
SignExt(res(31, 0), len),
res
)
io.in_ready := state === s_idle
io.out_valid := finished // state === s_finish
io.in_ready := state(s_idle)
io.out_valid := state(s_finish) // state === s_finish
}
class RightShifter(len: Int, lzc_width: Int) extends Module {
val io = IO(new Bundle() {
val shiftNum = Input(UInt(lzc_width.W))
val in = Input(UInt(len.W))
val msb = Input(Bool())
val out = Output(UInt(len.W))
})
require(len == 64 || len == 32)
val shift = io.shiftNum
val msb = io.msb
val s0 = Mux(shift(0), Cat(VecInit(Seq.fill(1)(msb)).asUInt, io.in(len - 1, 1)), io.in)
val s1 = Mux(shift(1), Cat(VecInit(Seq.fill(2)(msb)).asUInt, s0(len - 1, 2)), s0)
val s2 = Mux(shift(2), Cat(VecInit(Seq.fill(4)(msb)).asUInt, s1(len - 1, 4)), s1)
val s3 = Mux(shift(3), Cat(VecInit(Seq.fill(8)(msb)).asUInt, s2(len - 1, 8)), s2)
val s4 = Mux(shift(4), Cat(VecInit(Seq.fill(16)(msb)).asUInt, s3(len - 1, 16)), s3)
val s5 = Wire(UInt(len.W))
if (len == 64) {
s5 := Mux(shift(5), Cat(VecInit(Seq.fill(32)(msb)).asUInt, s4(len - 1, 32)), s4)
} else if (len == 32) {
s5 := s4
}
io.out := s5
}
object mLookUpTable {
// Usage :
// result := decoder(QMCMinimizer, index, mLookupTable.xxx)
val minus_m = Seq(
Array( // -m[-1]
0.U -> "b00_11010".U,
1.U -> "b00_11110".U,
2.U -> "b01_00000".U,
3.U -> "b01_00100".U,
4.U -> "b01_00110".U,
5.U -> "b01_01010".U,
6.U -> "b01_01100".U,
7.U -> "b01_10000".U
),
Array( // -m[0]
0.U -> "b000_0101".U,
1.U -> "b000_0110".U,
2.U -> "b000_0110".U,
3.U -> "b000_0110".U,
4.U -> "b000_1001".U,
5.U -> "b000_1000".U,
6.U -> "b000_1000".U,
7.U -> "b000_1000".U
),
Array( //-m[1]
0.U -> "b111_1101".U,
1.U -> "b111_1100".U,
2.U -> "b111_1100".U,
3.U -> "b111_1100".U,
4.U -> "b111_1011".U,
5.U -> "b111_1010".U,
6.U -> "b111_1010".U,
7.U -> "b111_1010".U
),
Array( //-m[2]
0.U -> "b11_01000".U,
1.U -> "b11_00100".U,
2.U -> "b11_00010".U,
3.U -> "b10_11110".U,
4.U -> "b10_11100".U,
5.U -> "b10_11000".U,
6.U -> "b10_10110".U,
7.U -> "b10_10010".U
))
}
class SRT4QDS(len: Int, itn_len: Int) extends Module {
// srt4 quotientr digit selection
val io = IO(new Bundle() {
val remSum = Input(UInt(itn_len.W)) // 68, 67
val remCarry = Input(UInt(itn_len.W))
val d = Input(UInt(len.W)) // 64, 64
val specialDivisor = Input(Bool())
val qPrev = Input(UInt(5.W))
val qIterEnd = Output(UInt(5.W))
})
val remSumX16 = io.remSum << 4 // 72, 67 Top 2 bits unused
val remCarryX16 = io.remCarry << 4
def trunc25(rem: UInt): UInt = {rem(itn_len, itn_len - 7 + 1)}
def trunc34(rem: UInt): UInt = {rem(itn_len + 1, itn_len + 1 - 7 + 1)}
val quot_neg_2 :: quot_neg_1 :: quot_0 :: quot_pos_1 :: quot_pos_2 :: Nil = Enum(5)
val d = Cat(0.U(1.W), io.d, 0.U(3.W)) // 68, 67
val (dX4, dX8, dXNeg4, dXNeg8) = (d << 2, d(itn_len - 2, 0) << 3, ~(d << 2), ~(d(itn_len - 2, 0) << 3)) // 70, 67
val dForLookup = io.d(len - 2, len - 2 - 3 + 1)
val dXq = Mux1H(Seq(
io.qPrev(quot_neg_2) -> dX8,
io.qPrev(quot_neg_1) -> dX4,
io.qPrev(quot_0) -> 0.U((itn_len + 2).W),
io.qPrev(quot_pos_1) -> dXNeg4,
io.qPrev(quot_pos_2) -> dXNeg8
))
val signs = VecInit(Seq.tabulate(4){ // -1 0 1 2
i => {
val csa1 = Module(new CSA3_2(7))
val csa2 = Module(new CSA3_2(7))
if (i == 1 || i == 2) {
csa1.io.in(0) := trunc34(remSumX16)
csa1.io.in(1) := trunc34(remCarryX16)
csa2.io.in(2) := trunc34(dXq)
} else {
csa1.io.in(0) := trunc25(remSumX16)
csa1.io.in(1) := trunc25(remCarryX16)
csa2.io.in(2) := trunc25(dXq)
}
csa1.io.in(2) := MuxLookup(dForLookup, "b0000000".U, mLookUpTable.minus_m(i))
csa2.io.in(0) := csa1.io.out(0)
csa2.io.in(1) := csa1.io.out(1)(5, 0) << 1
(csa2.io.out(0) + (csa2.io.out(1)(5, 0) << 1))(6)
}
})
val qVec = Wire(Vec(5, Bool()))
qVec(quot_neg_2) := signs(0) && signs(1) && signs(2)
qVec(quot_neg_1) := ~signs(0) && signs(1) && signs(2)
qVec(quot_0) := signs(2) && ~signs(1)
qVec(quot_pos_1) := signs(3) && ~signs(2) && ~signs(1)
qVec(quot_pos_2) := ~signs(3) && ~signs(2) && ~signs(1)
io.qIterEnd := qVec.asUInt
// assert(PopCount(qVec) === 1.U)
}
class SRT4Divider(len: Int)(implicit p: Parameters) extends AbstractDivider(len) {
val newReq = io.in.fire()

View File

@ -425,7 +425,7 @@ package object xiangshan {
def apply() = UInt(4.W)
}
def dividerGen(p: Parameters) = new SRT4Divider(p(XLen))(p)
def dividerGen(p: Parameters) = new SRT16Divider(p(XLen))(p)
def multiplierGen(p: Parameters) = new ArrayMultiplier(p(XLen) + 1)(p)
def aluGen(p: Parameters) = new Alu()(p)
def bmuGen(p: Parameters) = new Bmu()(p)

View File

@ -0,0 +1,115 @@
/***************************************************************************************
* Copyright (c) 2020-2021 Institute of Computing Technology, Chinese Academy of Sciences
* Copyright (c) 2020-2021 Peng Cheng Laboratory
*
* XiangShan is licensed under Mulan PSL v2.
* You can use this software according to the terms and conditions of the Mulan PSL v2.
* You may obtain a copy of Mulan PSL v2 at:
* http://license.coscl.org.cn/MulanPSL2
*
* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
* EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
*
* See the Mulan PSL v2 for more details.
***************************************************************************************/
package futest
import chisel3._
import chiseltest._
import chiseltest.ChiselScalatestTester
import chiseltest.experimental.TestOptionBuilder._
import chiseltest.internal.{LineCoverageAnnotation, ToggleCoverageAnnotation, VerilatorBackendAnnotation}
import chiseltest.legacy.backends.verilator.VerilatorFlags
import org.scalatest.flatspec.AnyFlatSpec
import org.scalatest.matchers.must.Matchers
import firrtl.stage.RunFirrtlTransformAnnotation
import xstransforms.PrintModuleName
import xiangshan.backend.fu._
import scala.util.Random
class SRT4DividerWrapper extends Module {
val io = IO(new Bundle{
val dividend = Input(UInt(64.W))
val divisor = Input(UInt(64.W))
val sign = Input(Bool())
val isHi = Input(Bool())
val isW = Input(Bool())
val in_valid = Input(Bool())
val out_ready = Input(Bool())
val in_ready = Output(Bool())
val out_valid = Output(Bool())
val result = Output(UInt(64.W))
})
val divider = Module(new SRT16DividerDataModule(len = 64))
divider.io.src(0) := io.dividend
divider.io.src(1) := io.divisor
divider.io.kill_r := false.B
divider.io.kill_w := false.B
divider.io.sign := io.sign
divider.io.isHi := io.isHi
divider.io.isW := io.isW
divider.io.out_ready := io.out_ready
divider.io.valid := io.in_valid
io.in_ready := divider.io.in_ready
io.out_valid := divider.io.out_valid
io.result := divider.io.out_data
}
class IntDividerTest extends AnyFlatSpec with ChiselScalatestTester with Matchers {
behavior of "srt16 divider"
it should "run" in {
val rand = new Random(0x14226)
val testNum = 1000
test(new SRT4DividerWrapper).withAnnotations(Seq(VerilatorBackendAnnotation,
LineCoverageAnnotation,
ToggleCoverageAnnotation,
VerilatorFlags(Seq("--output-split 5000", "--output-split-cfuncs 5000",
"+define+RANDOMIZE_REG_INIT", "+define+RANDOMIZE_MEM_INIT", "--trace")),
RunFirrtlTransformAnnotation(new PrintModuleName))){ m =>
println("Test started!")
m.clock.step(20)
for (i <- 1 to testNum) {
m.clock.step(3)
m.io.in_ready.expect(true.B)
val divisor = rand.nextLong()
val dividend = rand.nextLong()
// val sign = rand.nextBoolean()
// val isSigned = if (sign) s"Signed division" else s"Unsigned division"
println(s"$i th iteration\n" + s"divisor is ${divisor.toHexString}, dividend is ${dividend.toHexString}")
m.io.in_valid.poke(true.B)
m.io.dividend.poke((s"b" + dividend.toBinaryString).asUInt(64.W))
m.io.divisor.poke((s"b" + divisor.toBinaryString).asUInt(64.W))
m.io.sign.poke(true.B)
val (quotient, remainder) = (dividend / divisor, dividend % divisor)
println(s"quotient is ${quotient.toHexString}, remainder is ${remainder.toHexString}")
var timeTaken = 0
while (m.io.out_valid.peek().litToBoolean != true) {
m.clock.step()
timeTaken += 1
if (timeTaken >= 62) assert(false, s"Timeout for single execution!!!")
}
m.io.in_valid.poke(false.B)
m.io.out_ready.poke(true.B)
m.io.isHi.poke(false.B)
m.clock.step()
m.io.result.expect((s"b" + quotient.toBinaryString).asUInt(64.W))
m.io.isHi.poke(true.B)
m.clock.step()
m.io.result.expect((s"b" + remainder.toBinaryString).asUInt(64.W))
}
}
}
}