From 9a25a12879712a8fc9ee67e6ec129e89369b4a2c Mon Sep 17 00:00:00 2001 From: Dolu1990 Date: Thu, 11 Feb 2021 17:40:35 +0100 Subject: [PATCH] fpu add FCVT_X_X --- src/main/scala/vexriscv/ip/fpu/FpuCore.scala | 42 ++++--- .../scala/vexriscv/ip/fpu/Interface.scala | 2 +- .../scala/vexriscv/plugin/FpuPlugin.scala | 2 + src/test/scala/vexriscv/ip/fpu/FpuTest.scala | 107 ++++++++++-------- 4 files changed, 92 insertions(+), 61 deletions(-) diff --git a/src/main/scala/vexriscv/ip/fpu/FpuCore.scala b/src/main/scala/vexriscv/ip/fpu/FpuCore.scala index edb6dba..f5350b0 100644 --- a/src/main/scala/vexriscv/ip/fpu/FpuCore.scala +++ b/src/main/scala/vexriscv/ip/fpu/FpuCore.scala @@ -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_W_X) { useRd := 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} @@ -289,7 +290,7 @@ case class FpuCore( portCount : Int, p : FpuParameter) extends Component{ load.payload.assignSomeByName(read.output.payload) 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()) input.ready setWhen(shortPipHit && shortPip.ready) 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 - when(input.rs1.isNan && input.rs2.isNan) { minMaxResult.setNanQuiet } + val minMaxSelectRs2 = !(((rs1Smaller ^ input.arg(0)) && !input.rs1.isNan || input.rs2.isNan)) + val minMaxSelectNanQuiet = input.rs1.isNan && input.rs2.isNan val cmpResult = B(rs1Smaller && !bothZero && !input.arg(1) || (rs1Equal || bothZero) && !input.arg(0)) when(input.rs1.isNan || input.rs2.isNan) { cmpResult := 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 } } - 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.source := input.source @@ -751,19 +752,31 @@ case class FpuCore( portCount : Int, p : FpuParameter) extends Component{ rfOutput.roundMode := input.roundMode if(p.withDouble) rfOutput.format := input.format rfOutput.scrap := False - rfOutput.value.assignDontCare() + rfOutput.value.sign := input.rs1.sign + rfOutput.value.exponent := input.rs1.exponent + rfOutput.value.mantissa := input.rs1.mantissa @@ U"0" + rfOutput.value.special := input.rs1.special + 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 + 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 - rfOutput.value.exponent := input.rs1.exponent - rfOutput.value.mantissa := input.rs1.mantissa @@ U"0" - rfOutput.value.special := input.rs1.special + 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 rs1NanNv = input.rs1.isNan && (!input.rs1.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) input.ready := !halt && (toFpuRf ? rfOutput.ready | io.port.map(_.rsp.ready).read(input.source)) diff --git a/src/main/scala/vexriscv/ip/fpu/Interface.scala b/src/main/scala/vexriscv/ip/fpu/Interface.scala index 0f4b17f..604dd10 100644 --- a/src/main/scala/vexriscv/ip/fpu/Interface.scala +++ b/src/main/scala/vexriscv/ip/fpu/Interface.scala @@ -85,7 +85,7 @@ case class FpuFloat(exponentSize: Int, } 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{ diff --git a/src/main/scala/vexriscv/plugin/FpuPlugin.scala b/src/main/scala/vexriscv/plugin/FpuPlugin.scala index 49bd49a..468f14b 100644 --- a/src/main/scala/vexriscv/plugin/FpuPlugin.scala +++ b/src/main/scala/vexriscv/plugin/FpuPlugin.scala @@ -102,6 +102,8 @@ class FpuPlugin(externalFpu : Boolean = false, FMV_W_X -> (fmvWx) )) + //TODO FMV_X_X + doubles + port = FpuPort(p) if(externalFpu) master(port) diff --git a/src/test/scala/vexriscv/ip/fpu/FpuTest.scala b/src/test/scala/vexriscv/ip/fpu/FpuTest.scala index 8290d39..2e88265 100644 --- a/src/test/scala/vexriscv/ip/fpu/FpuTest.scala +++ b/src/test/scala/vexriscv/ip/fpu/FpuTest.scala @@ -130,6 +130,18 @@ class FpuTest extends FunSuite{ val a,b = (s.nextLong(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 RNE = build("-rnear_even") @@ -168,12 +180,16 @@ class FpuTest extends FunSuite{ val sgnjx = new TestCase(s"${f}_eq") val sqrt = new TestCase(s"${f}_sqrt") 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 f64 = new TestVector("f64") + val f32 = new TestVector("f32"){ + 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 cmdQueue = mutable.Queue[FpuCmd => Unit]() @@ -201,7 +217,7 @@ class FpuTest extends FunSuite{ def flagMatch(ref : Int, report : String): Unit ={ 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 } 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 = { val rd = Random.nextInt(32) @@ -620,48 +657,12 @@ class FpuTest extends FunSuite{ fma(rd,rs1,rs2,rs3, FpuRoundMode.RNE, FpuFormat.FLOAT) storeFloat(rd){v => 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 - 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 ={ val rs = new RegAllocator() val rs1, rs2, rs3 = rs.allocate() @@ -671,8 +672,7 @@ class FpuTest extends FunSuite{ sqrt(rd,rs1, FpuRoundMode.RNE, FpuFormat.FLOAT) storeFloat(rd){v => val error = Math.abs(ref-v)/ref - println(f"sqrt($a) = $v, $ref $error $rounding") - assert(checkFloat(ref, v)) + assert(checkFloat(ref, v), f"sqrt($a) = $v, $ref $error $rounding") } } @@ -686,8 +686,7 @@ class FpuTest extends FunSuite{ div(rd,rs1, rs2, FpuRoundMode.RNE, FpuFormat.FLOAT) storeFloat(rd){v => val error = Math.abs(ref-v)/ref - println(f"div($a, $b) = $v, $ref $error $rounding") - assert(checkFloat(ref, v)) + assert(checkFloat(ref, v), f"div($a, $b) = $v, $ref $error $rounding") } } @@ -975,6 +974,16 @@ class FpuTest extends FunSuite{ 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 = { val (a,b,r,f) = f32.fclass.RAW.f32_f32_i32 @@ -1057,6 +1066,12 @@ class FpuTest extends FunSuite{ //TODO test boxing //TODO double <-> simple convertions 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) testSubF64()