fpu add FCVT_X_X

This commit is contained in:
Dolu1990 2021-02-11 17:40:35 +01:00
parent 82dfd10dba
commit 9a25a12879
4 changed files with 92 additions and 61 deletions

View File

@ -223,6 +223,7 @@ case class FpuCore( portCount : Int, p : FpuParameter) extends Component{
is(p.Opcode.FMV_X_W) { useRs1 := True } is(p.Opcode.FMV_X_W) { useRs1 := True }
is(p.Opcode.FMV_W_X) { useRd := True } is(p.Opcode.FMV_W_X) { useRd := True }
is(p.Opcode.FCLASS ) { useRs1 := True } is(p.Opcode.FCLASS ) { useRs1 := True }
is(p.Opcode.FCVT_X_X ) { useRd := True; useRs1 := True }
} }
val hits = List((useRs1, s0.rs1), (useRs2, s0.rs2), (useRs3, s0.rs3), (useRd, s0.rd)).map{case (use, reg) => use && rf.lock.map(l => l.valid && l.source === s0.source && l.address === reg).orR} val hits = List((useRs1, s0.rs1), (useRs2, s0.rs2), (useRs3, s0.rs3), (useRd, s0.rd)).map{case (use, reg) => use && rf.lock.map(l => l.valid && l.source === s0.source && l.address === reg).orR}
@ -289,7 +290,7 @@ case class FpuCore( portCount : Int, p : FpuParameter) extends Component{
load.payload.assignSomeByName(read.output.payload) load.payload.assignSomeByName(read.output.payload)
load.i2f := input.opcode === FpuOpcode.I2F load.i2f := input.opcode === FpuOpcode.I2F
val shortPipHit = List(FpuOpcode.STORE, FpuOpcode.F2I, FpuOpcode.CMP, FpuOpcode.MIN_MAX, FpuOpcode.SGNJ, FpuOpcode.FMV_X_W, FpuOpcode.FCLASS).map(input.opcode === _).orR val shortPipHit = List(FpuOpcode.STORE, FpuOpcode.F2I, FpuOpcode.CMP, FpuOpcode.MIN_MAX, FpuOpcode.SGNJ, FpuOpcode.FMV_X_W, FpuOpcode.FCLASS, FpuOpcode.FCVT_X_X).map(input.opcode === _).orR
val shortPip = Stream(ShortPipInput()) val shortPip = Stream(ShortPipInput())
input.ready setWhen(shortPipHit && shortPip.ready) input.ready setWhen(shortPipHit && shortPip.ready)
shortPip.valid := input.valid && shortPipHit shortPip.valid := input.valid && shortPipHit
@ -715,8 +716,8 @@ case class FpuCore( portCount : Int, p : FpuParameter) extends Component{
) )
val minMaxResult = ((rs1Smaller ^ input.arg(0)) && !input.rs1.isNan || input.rs2.isNan) ? input.rs1 | input.rs2 val minMaxSelectRs2 = !(((rs1Smaller ^ input.arg(0)) && !input.rs1.isNan || input.rs2.isNan))
when(input.rs1.isNan && input.rs2.isNan) { minMaxResult.setNanQuiet } val minMaxSelectNanQuiet = input.rs1.isNan && input.rs2.isNan
val cmpResult = B(rs1Smaller && !bothZero && !input.arg(1) || (rs1Equal || bothZero) && !input.arg(0)) val cmpResult = B(rs1Smaller && !bothZero && !input.arg(1) || (rs1Equal || bothZero) && !input.arg(0))
when(input.rs1.isNan || input.rs2.isNan) { cmpResult := 0 } when(input.rs1.isNan || input.rs2.isNan) { cmpResult := 0 }
val sgnjResult = (input.rs1.sign && input.arg(1)) ^ input.rs2.sign ^ input.arg(0) val sgnjResult = (input.rs1.sign && input.arg(1)) ^ input.rs2.sign ^ input.arg(0)
@ -742,7 +743,7 @@ case class FpuCore( portCount : Int, p : FpuParameter) extends Component{
is(FpuOpcode.FCLASS) { result(31 downto 0) := fclassResult.resized } is(FpuOpcode.FCLASS) { result(31 downto 0) := fclassResult.resized }
} }
val toFpuRf = List(FpuOpcode.MIN_MAX, FpuOpcode.SGNJ).map(input.opcode === _).orR val toFpuRf = List(FpuOpcode.MIN_MAX, FpuOpcode.SGNJ, FpuOpcode.FCVT_X_X).map(input.opcode === _).orR
rfOutput.valid := input.valid && toFpuRf && !halt rfOutput.valid := input.valid && toFpuRf && !halt
rfOutput.source := input.source rfOutput.source := input.source
@ -751,19 +752,31 @@ case class FpuCore( portCount : Int, p : FpuParameter) extends Component{
rfOutput.roundMode := input.roundMode rfOutput.roundMode := input.roundMode
if(p.withDouble) rfOutput.format := input.format if(p.withDouble) rfOutput.format := input.format
rfOutput.scrap := False rfOutput.scrap := False
rfOutput.value.assignDontCare() rfOutput.value.sign := input.rs1.sign
switch(input.opcode){
is(FpuOpcode.MIN_MAX){
rfOutput.value.sign := minMaxResult.sign
rfOutput.value.exponent := minMaxResult.exponent
rfOutput.value.mantissa := minMaxResult.mantissa @@ U"0"
rfOutput.value.special := minMaxResult.special
}
is(FpuOpcode.SGNJ){
rfOutput.value.sign := sgnjResult
rfOutput.value.exponent := input.rs1.exponent rfOutput.value.exponent := input.rs1.exponent
rfOutput.value.mantissa := input.rs1.mantissa @@ U"0" rfOutput.value.mantissa := input.rs1.mantissa @@ U"0"
rfOutput.value.special := input.rs1.special rfOutput.value.special := input.rs1.special
switch(input.opcode){
is(FpuOpcode.MIN_MAX){
when(minMaxSelectRs2) {
rfOutput.value.sign := input.rs2.sign
rfOutput.value.exponent := input.rs2.exponent
rfOutput.value.mantissa := input.rs2.mantissa @@ U"0"
rfOutput.value.special := input.rs2.special
}
when(minMaxSelectNanQuiet){
rfOutput.value.setNanQuiet
}
}
is(FpuOpcode.SGNJ){
rfOutput.value.sign := sgnjResult
}
if(p.withDouble) is(FpuOpcode.FCVT_X_X){
rfOutput.format := ((input.format === FpuFormat.FLOAT) ? FpuFormat.DOUBLE | FpuFormat.FLOAT)
when(input.rs1.isNan){
rfOutput.value.setNanQuiet
}
} }
} }
@ -772,7 +785,8 @@ case class FpuCore( portCount : Int, p : FpuParameter) extends Component{
val rs2Nan = input.rs2.isNan val rs2Nan = input.rs2.isNan
val rs1NanNv = input.rs1.isNan && (!input.rs1.isQuiet || signalQuiet) val rs1NanNv = input.rs1.isNan && (!input.rs1.isQuiet || signalQuiet)
val rs2NanNv = input.rs2.isNan && (!input.rs2.isQuiet || signalQuiet) val rs2NanNv = input.rs2.isNan && (!input.rs2.isQuiet || signalQuiet)
val nv = (input.opcode === FpuOpcode.CMP || input.opcode === FpuOpcode.MIN_MAX) && (rs1NanNv || rs2NanNv) val nv = List(FpuOpcode.CMP, FpuOpcode.MIN_MAX, FpuOpcode.FCVT_X_X).map(input.opcode === _).orR && rs1NanNv ||
List(FpuOpcode.CMP, FpuOpcode.MIN_MAX).map(input.opcode === _).orR && rs2NanNv
flag.NV setWhen(input.valid && nv) flag.NV setWhen(input.valid && nv)
input.ready := !halt && (toFpuRf ? rfOutput.ready | io.port.map(_.rsp.ready).read(input.source)) input.ready := !halt && (toFpuRf ? rfOutput.ready | io.port.map(_.rsp.ready).read(input.source))

View File

@ -85,7 +85,7 @@ case class FpuFloat(exponentSize: Int,
} }
object FpuOpcode extends SpinalEnum{ object FpuOpcode extends SpinalEnum{
val LOAD, STORE, MUL, ADD, FMA, I2F, F2I, CMP, DIV, SQRT, MIN_MAX, SGNJ, FMV_X_W, FMV_W_X, FCLASS = newElement() val LOAD, STORE, MUL, ADD, FMA, I2F, F2I, CMP, DIV, SQRT, MIN_MAX, SGNJ, FMV_X_W, FMV_W_X, FCLASS, FCVT_X_X = newElement()
} }
object FpuFormat extends SpinalEnum{ object FpuFormat extends SpinalEnum{

View File

@ -102,6 +102,8 @@ class FpuPlugin(externalFpu : Boolean = false,
FMV_W_X -> (fmvWx) FMV_W_X -> (fmvWx)
)) ))
//TODO FMV_X_X + doubles
port = FpuPort(p) port = FpuPort(p)
if(externalFpu) master(port) if(externalFpu) master(port)

View File

@ -130,6 +130,18 @@ class FpuTest extends FunSuite{
val a,b = (s.nextLong(16)) val a,b = (s.nextLong(16))
(b2d(a), b2d(b), s.nextInt(16)) (b2d(a), b2d(b), s.nextInt(16))
} }
def f32_f64_i32 = {
val s = new Scanner(next)
val a,b = nextLong(s)
(b2f(a.toInt), b2d(b), s.nextInt(16))
}
def f64_f32_i32 = {
val s = new Scanner(next)
val a,b = nextLong(s)
(b2d(a), b2f(b.toInt), s.nextInt(16))
}
} }
lazy val RAW = build("") lazy val RAW = build("")
lazy val RNE = build("-rnear_even") lazy val RNE = build("-rnear_even")
@ -168,12 +180,16 @@ class FpuTest extends FunSuite{
val sgnjx = new TestCase(s"${f}_eq") val sgnjx = new TestCase(s"${f}_eq")
val sqrt = new TestCase(s"${f}_sqrt") val sqrt = new TestCase(s"${f}_sqrt")
val div = new TestCase(s"${f}_div") val div = new TestCase(s"${f}_div")
val f32 = new TestCase(s"${f}_eq")
val f64 = new TestCase(s"${f}_eq")
} }
val f32 = new TestVector("f32") val f32 = new TestVector("f32"){
val f64 = new TestVector("f64") val f64 = new TestCase(s"f32_eq")
val cvt64 = new TestCase(s"f32_to_f64")
}
val f64 = new TestVector("f64"){
val f32 = new TestCase(s"f64_eq")
val cvt32 = new TestCase(s"f64_to_f32")
}
val cpus = for(id <- 0 until portCount) yield new { val cpus = for(id <- 0 until portCount) yield new {
val cmdQueue = mutable.Queue[FpuCmd => Unit]() val cmdQueue = mutable.Queue[FpuCmd => Unit]()
@ -201,7 +217,7 @@ class FpuTest extends FunSuite{
def flagMatch(ref : Int, report : String): Unit ={ def flagMatch(ref : Int, report : String): Unit ={
waitUntil(pendingMiaou == 0) waitUntil(pendingMiaou == 0)
softAssert(flagAccumulator == ref, s"Flag missmatch dut=$flagAccumulator ref=$ref $report") assert(flagAccumulator == ref, s"Flag missmatch dut=$flagAccumulator ref=$ref $report")
flagAccumulator = 0 flagAccumulator = 0
} }
def flagClear(): Unit ={ def flagClear(): Unit ={
@ -586,6 +602,27 @@ class FpuTest extends FunSuite{
} }
def testCvtF32F64Raw(a : Float, ref : Double, flag : Int, rounding : FpuRoundMode.E): Unit ={
val rs, rd = Random.nextInt(32)
load(rs, a)
fpuF2f(rd, rs, Random.nextInt(32), Random.nextInt(32), FpuOpcode.FCVT_X_X, Random.nextInt(3), rounding, FpuFormat.FLOAT)
store(rd){v =>
assert(d2b(v) == d2b(ref), f"testCvtF32F64Raw $a $ref $rounding")
}
flagMatch(flag, f"testCvtF32F64Raw $a $ref $rounding")
}
def testCvtF64F32Raw(a : Double, ref : Float, flag : Int, rounding : FpuRoundMode.E): Unit ={
val rs, rd = Random.nextInt(32)
load(rs, a)
fpuF2f(rd, rs, Random.nextInt(32), Random.nextInt(32), FpuOpcode.FCVT_X_X, Random.nextInt(3), rounding, FpuFormat.DOUBLE)
storeFloat(rd){v =>
assert(d2b(v) == d2b(ref), f"testCvtF64F32Raw $a $ref $rounding")
}
flagMatch(flag, f"testCvtF64F32Raw $a $ref $rounding")
}
def testClassRaw(a : Float) : Unit = { def testClassRaw(a : Float) : Unit = {
val rd = Random.nextInt(32) val rd = Random.nextInt(32)
@ -620,48 +657,12 @@ class FpuTest extends FunSuite{
fma(rd,rs1,rs2,rs3, FpuRoundMode.RNE, FpuFormat.FLOAT) fma(rd,rs1,rs2,rs3, FpuRoundMode.RNE, FpuFormat.FLOAT)
storeFloat(rd){v => storeFloat(rd){v =>
val ref = a.toDouble * b.toDouble + c.toDouble val ref = a.toDouble * b.toDouble + c.toDouble
println(f"$a%.20f * $b%.20f + $c%.20f = $v%.20f, $ref%.20f")
val mul = a.toDouble * b.toDouble val mul = a.toDouble * b.toDouble
if((mul.abs-c.abs)/mul.abs > 0.1) assert(checkFloat(ref.toFloat, v)) if((mul.abs-c.abs)/mul.abs > 0.1) assert(checkFloat(ref.toFloat, v), f"$a%.20f * $b%.20f + $c%.20f = $v%.20f, $ref%.20f")
} }
} }
def testDivRaw(a : Float, b : Float): Unit ={
val rs = new RegAllocator()
val rs1, rs2, rs3 = rs.allocate()
val rd = Random.nextInt(32)
load(rs1, a)
load(rs2, b)
div(rd,rs1,rs2, FpuRoundMode.RNE, FpuFormat.FLOAT)
storeFloat(rd){v =>
val refUnclamped = a/b
val refClamped = ((a)/(b))
val ref = refClamped
val error = Math.abs(ref-v)/ref
println(f"$a / $b = $v, $ref $error")
assert(checkFloat(ref, v))
}
}
def testSqrtRaw(a : Float): Unit ={
val rs = new RegAllocator()
val rs1, rs2, rs3 = rs.allocate()
val rd = Random.nextInt(32)
load(rs1, a)
sqrt(rd,rs1, FpuRoundMode.RNE, FpuFormat.FLOAT)
storeFloat(rd){v =>
val ref = Math.sqrt(a).toFloat
val error = Math.abs(ref-v)/ref
println(f"sqrt($a) = $v, $ref $error")
assert(checkFloat(ref, v))
}
}
def testSqrtExact(a : Float, ref : Float, flag : Int, rounding : FpuRoundMode.E): Unit ={ def testSqrtExact(a : Float, ref : Float, flag : Int, rounding : FpuRoundMode.E): Unit ={
val rs = new RegAllocator() val rs = new RegAllocator()
val rs1, rs2, rs3 = rs.allocate() val rs1, rs2, rs3 = rs.allocate()
@ -671,8 +672,7 @@ class FpuTest extends FunSuite{
sqrt(rd,rs1, FpuRoundMode.RNE, FpuFormat.FLOAT) sqrt(rd,rs1, FpuRoundMode.RNE, FpuFormat.FLOAT)
storeFloat(rd){v => storeFloat(rd){v =>
val error = Math.abs(ref-v)/ref val error = Math.abs(ref-v)/ref
println(f"sqrt($a) = $v, $ref $error $rounding") assert(checkFloat(ref, v), f"sqrt($a) = $v, $ref $error $rounding")
assert(checkFloat(ref, v))
} }
} }
@ -686,8 +686,7 @@ class FpuTest extends FunSuite{
div(rd,rs1, rs2, FpuRoundMode.RNE, FpuFormat.FLOAT) div(rd,rs1, rs2, FpuRoundMode.RNE, FpuFormat.FLOAT)
storeFloat(rd){v => storeFloat(rd){v =>
val error = Math.abs(ref-v)/ref val error = Math.abs(ref-v)/ref
println(f"div($a, $b) = $v, $ref $error $rounding") assert(checkFloat(ref, v), f"div($a, $b) = $v, $ref $error $rounding")
assert(checkFloat(ref, v))
} }
} }
@ -975,6 +974,16 @@ class FpuTest extends FunSuite{
testTransferF32F64Raw(a, Random.nextBoolean()) testTransferF32F64Raw(a, Random.nextBoolean())
} }
def testCvtF32F64() : Unit = {
val rounding = FpuRoundMode.elements.randomPick()
val (a,r,f) = f32.cvt64(rounding).f32_f64_i32
testCvtF32F64Raw(a, r, f, rounding)
}
def testCvtF64F32() : Unit = {
val rounding = FpuRoundMode.elements.randomPick()
val (a,r,f) = f64.cvt32(rounding).f64_f32_i32
testCvtF64F32Raw(a, r, f, rounding)
}
def testClass() : Unit = { def testClass() : Unit = {
val (a,b,r,f) = f32.fclass.RAW.f32_f32_i32 val (a,b,r,f) = f32.fclass.RAW.f32_f32_i32
@ -1057,6 +1066,12 @@ class FpuTest extends FunSuite{
//TODO test boxing //TODO test boxing
//TODO double <-> simple convertions //TODO double <-> simple convertions
if(p.withDouble) { if(p.withDouble) {
for(_ <- 0 until 10000) testCvtF32F64()
println("FCVT_S_D done")
for(_ <- 0 until 10000) testCvtF64F32()
println("FCVT_D_S done")
for(_ <- 0 until 10000) testAddF64() for(_ <- 0 until 10000) testAddF64()
for(_ <- 0 until 10000) testSubF64() for(_ <- 0 until 10000) testSubF64()