blob: 479b48947b53b84e69200d1fe999b23c5ba4e070 [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 kelvin
import chisel3._
import chisel3.util._
import common._
import _root_.circt.stage.ChiselStage
object Dvu {
def apply(p: Parameters): Dvu = {
return Module(new Dvu(p))
}
}
case class DvuOp() {
val DIV = 0
val DIVU = 1
val REM = 2
val REMU = 3
val Entries = 4
}
class DvuIO(p: Parameters) extends Bundle {
val valid = Input(Bool())
val ready = Output(Bool())
val addr = Input(UInt(5.W))
val op = Input(UInt(new DvuOp().Entries.W))
}
class Dvu(p: Parameters) extends Module {
val io = IO(new Bundle {
// Decode cycle.
val req = new DvuIO(p)
// Execute cycle.
val rs1 = Flipped(new RegfileReadDataIO)
val rs2 = Flipped(new RegfileReadDataIO)
val rd = new Bundle { // RegfileWriteDataIO
val valid = Output(Bool())
val ready = Input(Bool())
val addr = Output(UInt(5.W))
val data = Output(UInt(32.W))
}
})
// This implemention differs to common::idiv by supporting early termination,
// and only performs one bit per cycle.
val dvu = new DvuOp()
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)
}
val active = RegInit(false.B)
val compute = RegInit(false.B)
val addr1 = Reg(UInt(5.W))
val signed1 = Reg(Bool())
val divide1 = Reg(Bool())
val addr2 = Reg(UInt(5.W))
val signed2d = Reg(Bool())
val signed2r = Reg(Bool())
val divide2 = Reg(Bool())
val count = RegInit(0.U(6.W))
val divide = Reg(UInt(32.W))
val remain = Reg(UInt(32.W))
val denom = Reg(UInt(32.W))
val divByZero = io.rs2.data === 0.U
io.req.ready := !active && !compute && !count(5)
// This is not a Clz, one value too small.
def Clz1(bits: UInt): UInt = {
val msb = bits.getWidth - 1
Mux(bits(msb), 0.U, PriorityEncoder(Reverse(bits(msb - 1, 0))))
}
// Disable active second to last cycle.
when (io.req.valid && io.req.ready) {
active := true.B
} .elsewhen (count === 30.U) {
active := false.B
}
// Compute is delayed by one cycle.
compute := active
when (io.req.valid && io.req.ready) {
addr1 := io.req.addr
signed1 := io.req.op(dvu.DIV) || io.req.op(dvu.REM)
divide1 := io.req.op(dvu.DIV) || io.req.op(dvu.DIVU)
}
when (active && !compute) {
addr2 := addr1
signed2d := signed1 && (io.rs1.data(31) =/= io.rs2.data(31)) && !divByZero
signed2r := signed1 && io.rs1.data(31)
divide2 := divide1
val inp = Mux(signed1 && io.rs1.data(31), ~io.rs1.data + 1.U, io.rs1.data)
// The divBy0 uses full latency to simplify logic.
// Count the leading zeroes, which is one less than the priority encoding.
val clz = Mux(io.rs2.data === 0.U, 0.U, Clz1(inp))
denom := Mux(signed1 && io.rs2.data(31), ~io.rs2.data + 1.U, io.rs2.data)
divide := inp << clz
remain := 0.U
count := clz
} .elsewhen (compute && count < 32.U) {
val (div, rem) = Divide(divide, remain, denom)
divide := div
remain := rem
count := count + 1.U
} .elsewhen (io.rd.valid && io.rd.ready) {
count := 0.U
}
val div = Mux(signed2d, ~divide + 1.U, divide)
val rem = Mux(signed2r, ~remain + 1.U, remain)
io.rd.valid := count(5)
io.rd.addr := addr2
io.rd.data := Mux(divide2, div, rem)
}
object EmitDvu extends App {
val p = new Parameters
ChiselStage.emitSystemVerilogFile(new Dvu(p), args)
}