blob: 4532b7ba81d9586ec559da05959fca9ccd2eb1ed [file] [log] [blame]
// Copyright 2023 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package common
import chisel3._
import chisel3.util._
import common._
import _root_.circt.stage.ChiselStage
// An integer divide unit, to be fused with fdiv.
object IDiv {
def apply(n: Int): IDiv = {
return Module(new IDiv(n))
}
val Stages = 4
val Rcnt = 32 / Stages
}
case class IDivOp() {
val DIV = 0
val DIVU = 1
val REM = 2
val REMU = 3
val Entries = 4
}
class IDiv(n: Int) extends Module {
val io = IO(new Bundle {
val req = Input(UInt(new IDivOp().Entries.W))
val ina = Flipped(Decoupled(Vec(n, UInt(32.W))))
val inb = Flipped(Decoupled(Vec(n, UInt(32.W))))
val out = Decoupled(Vec(n, UInt(32.W)))
})
val dvu = new IDivOp()
val active = RegInit(false.B)
val result = RegInit(false.B)
val count = Reg(UInt(6.W))
val state = Reg(Vec(n, new IDivState()))
val ivalid = io.ina.valid && io.ina.ready && io.inb.valid && io.inb.ready
val ovalid = io.out.valid && io.out.ready
when (ivalid) {
active := true.B
} .elsewhen (active && count === IDiv.Rcnt.U) {
active := false.B
}
when (ovalid) {
result := false.B
} .elsewhen (active && count === IDiv.Rcnt.U) {
result := true.B
}
when (ivalid) {
count := 0.U
} .elsewhen (active) {
count := count + 1.U
}
for (i <- 0 until n) {
val ina = io.ina.bits(i)
val inb = io.inb.bits(i)
val st = state(i)
when (ivalid) {
val divide = io.req(dvu.DIV) || io.req(dvu.DIVU)
val signed = io.req(dvu.DIV) || io.req(dvu.REM)
state(i) := IDivComb1(ina, inb, signed, divide)
} .elsewhen (active) {
state(i) := IDivComb2(state(i), count)
}
}
io.ina.ready := io.inb.valid && !active && (!result || io.out.ready)
io.inb.ready := io.ina.valid && !active && (!result || io.out.ready)
io.out.valid := result
for (i <- 0 until n) {
io.out.bits(i) := IDivComb3(state(i))
}
}
class IDivState extends Bundle {
val denom = UInt(32.W) // output is placed first
val divide = UInt(32.W)
val remain = UInt(32.W)
val opdiv = Bool()
val opneg = Bool()
}
object IDivComb1 {
def apply(ina: UInt, inb: UInt, signed: Bool, divide: Bool): IDivState = {
val out = Wire(new IDivState())
val divByZero = inb === 0.U
val divsign = signed && (ina(31) =/= inb(31)) && !divByZero
val remsign = signed && ina(31)
val inp = Mux(signed && ina(31), ~ina + 1.U, ina)
out.opdiv := divide
out.opneg := Mux(divide, divsign, remsign)
out.denom := Mux(signed && inb(31), ~inb + 1.U, inb)
out.divide := inp
out.remain := 0.U
out
}
}
object IDivComb2 {
def apply(in: IDivState, count: UInt): IDivState = {
val out = Wire(new IDivState())
out := in
when (count < IDiv.Rcnt.U) {
val (div1, rem1) = Divide(in.divide, in.remain, in.denom)
if (IDiv.Stages == 1) {
out.divide := div1
out.remain := rem1
} else if (IDiv.Stages == 2) {
val (div2, rem2) = Divide(div1, rem1, in.denom)
out.divide := div2
out.remain := rem2
} else if (IDiv.Stages == 4) {
val (div2, rem2) = Divide(div1, rem1, in.denom)
val (div3, rem3) = Divide(div2, rem2, in.denom)
val (div4, rem4) = Divide(div3, rem3, in.denom)
out.divide := div4
out.remain := rem4
} else {
assert(false)
}
} .otherwise {
val div = Mux(in.opneg, ~in.divide + 1.U, in.divide)
val rem = Mux(in.opneg, ~in.remain + 1.U, in.remain)
out.denom := Mux(in.opdiv, div, rem)
}
out
}
def Divide(prvDivide: UInt, prvRemain: UInt, denom: UInt): (UInt, UInt) = {
val shfRemain = Cat(prvRemain(30,0), prvDivide(31))
val subtract = shfRemain -& denom
assert(subtract.getWidth == 33)
val divDivide = Wire(UInt(32.W))
val divRemain = Wire(UInt(32.W))
when (!subtract(32)) {
divDivide := Cat(prvDivide(30,0), 1.U(1.W))
divRemain := subtract(31,0)
} .otherwise {
divDivide := Cat(prvDivide(30,0), 0.U(1.W))
divRemain := shfRemain
}
(divDivide, divRemain)
}
}
object IDivComb3 {
def apply(in: IDivState): UInt = {
val result = in.denom
assert(result.getWidth == 32)
result
}
}
object EmitIDiv extends App {
ChiselStage.emitSystemVerilogFile(new IDiv(1), args)
}