diff --git a/src/main/scala/vexriscv/ip/fpu/FpuCore.scala b/src/main/scala/vexriscv/ip/fpu/FpuCore.scala index c017d24..0f79aca 100644 --- a/src/main/scala/vexriscv/ip/fpu/FpuCore.scala +++ b/src/main/scala/vexriscv/ip/fpu/FpuCore.scala @@ -105,6 +105,7 @@ case class FpuCore( portCount : Int, p : FpuParameter) extends Component{ val value = p.writeFloating() val scrap = Bool() val roundMode = FpuRoundMode() + val allowException = Bool() } case class RoundOutput() extends Bundle{ @@ -463,7 +464,9 @@ case class FpuCore( portCount : Int, p : FpuParameter) extends Component{ } recodedResult := recoded.sign ## f32.exp ## f32.man - val isSubnormal = !recoded.special && recoded.exponent <= exponentOne - 127 + val expInSubnormalRange = recoded.exponent <= exponentOne - 127 + val isSubnormal = !recoded.special && expInSubnormalRange + val isNormal = !recoded.special && !expInSubnormalRange val fsm = new Area{ val f2iShift = input.rs1.exponent - U(exponentOne) val isF2i = input.opcode === FpuOpcode.F2I @@ -584,12 +587,12 @@ case class FpuCore( portCount : Int, p : FpuParameter) extends Component{ val fclassResult = B(0, 32 bits) val decoded = input.rs1.decode() fclassResult(0) := input.rs1.sign && decoded.isInfinity - fclassResult(1) := input.rs1.sign && decoded.isNormal + fclassResult(1) := input.rs1.sign && isNormal fclassResult(2) := input.rs1.sign && isSubnormal //TODO fclassResult(3) := input.rs1.sign && decoded.isZero fclassResult(4) := !input.rs1.sign && decoded.isZero fclassResult(5) := !input.rs1.sign && isSubnormal //TODO - fclassResult(6) := !input.rs1.sign && decoded.isNormal + fclassResult(6) := !input.rs1.sign && isNormal fclassResult(7) := !input.rs1.sign && decoded.isInfinity fclassResult(8) := decoded.isNan && !decoded.isQuiet fclassResult(9) := decoded.isNan && decoded.isQuiet @@ -623,7 +626,7 @@ case class FpuCore( portCount : Int, p : FpuParameter) extends Component{ rfOutput.value.sign := sgnjResult rfOutput.value.exponent := input.rs1.exponent rfOutput.value.mantissa := input.rs1.mantissa @@ U"0" - rfOutput.value.special := False //TODO + rfOutput.value.special := input.rs1.special } } diff --git a/src/test/scala/vexriscv/ip/fpu/FpuTest.scala b/src/test/scala/vexriscv/ip/fpu/FpuTest.scala index 7dc1496..d066eb1 100644 --- a/src/test/scala/vexriscv/ip/fpu/FpuTest.scala +++ b/src/test/scala/vexriscv/ip/fpu/FpuTest.scala @@ -122,6 +122,11 @@ class FpuTest extends FunSuite{ val le = new TestCase("f32_le") val min = new TestCase("f32_le") val max = new TestCase("f32_lt") + val transfer = new TestCase("f32_eq") + val fclass = new TestCase("f32_eq") + val sgnj = new TestCase("f32_eq") + val sgnjn = new TestCase("f32_eq") + val sgnjx = new TestCase("f32_eq") } val cpus = for(id <- 0 until portCount) yield new { @@ -272,6 +277,19 @@ class FpuTest extends FunSuite{ fpuF2f(rd, rs1, rs2, rs3, FpuOpcode.FMA, 0, rounding) } + def sgnjRaw(rd : Int, rs1 : Int, rs2 : Int, arg : Int): Unit ={ + fpuF2f(rd, rs1, rs2, Random.nextInt(32), FpuOpcode.SGNJ, arg, FpuRoundMode.elements.randomPick()) + } + + def sgnj(rd : Int, rs1 : Int, rs2 : Int, rounding : FpuRoundMode.E = null): Unit ={ + sgnjRaw(rd, rs1, rs2, 0) + } + def sgnjn(rd : Int, rs1 : Int, rs2 : Int, rounding : FpuRoundMode.E = null): Unit ={ + sgnjRaw(rd, rs1, rs2, 1) + } + def sgnjx(rd : Int, rs1 : Int, rs2 : Int, rounding : FpuRoundMode.E = null): Unit ={ + sgnjRaw(rd, rs1, rs2, 2) + } def cmp(rs1 : Int, rs2 : Int, arg : Int = 1)(body : FpuRsp => Unit): Unit ={ fpuF2i(rs1, rs2, FpuOpcode.CMP, arg, FpuRoundMode.elements.randomPick())(body) @@ -298,7 +316,7 @@ class FpuTest extends FunSuite{ } } - def fmv_x_w(rs1 : Int)(body : FpuRsp => Unit): Unit ={ + def fmv_x_w(rs1 : Int)(body : Float => Unit): Unit ={ cmdAdd {cmd => cmd.opcode #= cmd.opcode.spinalEnum.FMV_X_W cmd.rs1 #= rs1 @@ -307,7 +325,7 @@ class FpuTest extends FunSuite{ cmd.rd.randomize() cmd.arg #= 0 } - rspQueue += body + rspQueue += {rsp => body(b2f(rsp.value.toLong.toInt))} } def fmv_w_x(rd : Int, value : Int): Unit ={ @@ -342,19 +360,17 @@ class FpuTest extends FunSuite{ } - def sgnj(rd : Int, rs1 : Int, rs2 : Int): Unit ={ + + def fclass(rs1 : Int)(body : Int => Unit) : Unit = { cmdAdd {cmd => - cmd.opcode #= cmd.opcode.spinalEnum.SGNJ + cmd.opcode #= FpuOpcode.FCLASS cmd.rs1 #= rs1 - cmd.rs2 #= rs2 + cmd.rs2.randomize() cmd.rs3.randomize() - cmd.rd #= rd - cmd.arg #= 0 - } - commitQueue += {cmd => - cmd.write #= true - cmd.sync #= false + cmd.rd.randomize() + cmd.arg.randomize() } + rspQueue += {rsp => body(rsp.value.toLong.toInt)} } } @@ -460,16 +476,43 @@ class FpuTest extends FunSuite{ } } - def testLoadStore(a : Float): Unit ={ + def testTransfer(a : Float, iSrc : Boolean, iDst : Boolean): Unit ={ val rd = Random.nextInt(32) - load(rd, a) - storeFloat(rd){v => + + def handle(v : Float): Unit ={ val refUnclamped = a val ref = a - println(f"$a = $v, $ref") - assert(f2b(v) == f2b(ref)) + assert(f2b(v) == f2b(ref), f"$a = $v, $ref") + } + + if(iSrc) fmv_w_x(rd, f2b(a)) else load(rd, a) + if(iDst) fmv_x_w(rd)(handle) else storeFloat(rd)(handle) + + flagMatch(0, f"$a") + } + + def testClass(a : Float) : Unit = { + val rd = Random.nextInt(32) + + + load(rd, a) + fclass(rd){v => + val mantissa = f2b(a) & 0x7FFFFF + val exp = (f2b(a) >> 23) & 0xFF + val sign = (f2b(a) >> 31) & 0x1 + + val refBit = if(a.isInfinite) (if(sign == 0) 7 else 0) + else if(a.isNaN) (if((mantissa >> 22) != 0) 9 else 8) + else if(exp == 0 && mantissa != 0) (if(sign == 0) 5 else 2) + else if(exp == 0 && mantissa == 0) (if(sign == 0) 4 else 3) + else if(sign == 0) 6 else 1 + + val ref = 1 << refBit + + assert(v == ref, f"fclass $a") } } + def testMul(a : Float, b : Float): Unit ={ val rs = new RegAllocator() val rs1, rs2, rs3 = rs.allocate() @@ -653,30 +696,30 @@ class FpuTest extends FunSuite{ def testEq(a : Float, b : Float, ref : Int, flag : Int) = testCmpExact(a,b,ref,flag, 2) def testLt(a : Float, b : Float, ref : Int, flag : Int) = testCmpExact(a,b,ref,flag, 1) - def testFmv_x_w(a : Float): Unit ={ - val rs = new RegAllocator() - val rs1, rs2, rs3 = rs.allocate() - val rd = Random.nextInt(32) - load(rs1, a) - fmv_x_w(rs1){rsp => - val ref = f2b(a).toLong & 0xFFFFFFFFl - val v = rsp.value.toBigInt - println(f"fmv_x_w $a = $v, $ref") - assert(v === ref) - } - } +// def testFmv_x_w(a : Float): Unit ={ +// val rs = new RegAllocator() +// val rs1, rs2, rs3 = rs.allocate() +// val rd = Random.nextInt(32) +// load(rs1, a) +// fmv_x_w(rs1){rsp => +// val ref = f2b(a).toLong & 0xFFFFFFFFl +// val v = rsp.value.toBigInt +// println(f"fmv_x_w $a = $v, $ref") +// assert(v === ref) +// } +// } - def testFmv_w_x(a : Int): Unit ={ - val rs = new RegAllocator() - val rs1, rs2, rs3 = rs.allocate() - val rd = Random.nextInt(32) - fmv_w_x(rd, a) - storeFloat(rd){v => - val ref = b2f(a) - println(f"fmv_w_x $a = $v, $ref") - assert(v === ref) - } - } +// def testFmv_w_x(a : Int): Unit ={ +// val rs = new RegAllocator() +// val rs1, rs2, rs3 = rs.allocate() +// val rd = Random.nextInt(32) +// fmv_w_x(rd, a) +// storeFloat(rd){v => +// val ref = b2f(a) +// println(f"fmv_w_x $a = $v, $ref") +// assert(v === ref) +// } +// } @@ -710,18 +753,16 @@ class FpuTest extends FunSuite{ def testSgnj(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) - - sgnj(rd,rs1,rs2) - storeFloat(rd){v => - val ref = a * a.signum * b.signum - println(f"sgnf $a $b = $v, $ref") - assert(ref == v) - } + val ref = b2f((f2b(a) & ~0x80000000) | f2b(b) & 0x80000000) + testBinaryOp(sgnj,a,b,ref,0, null,"sgnj") + } + def testSgnjn(a : Float, b : Float): Unit ={ + val ref = b2f((f2b(a) & ~0x80000000) | ((f2b(b) & 0x80000000) ^ 0x80000000)) + testBinaryOp(sgnjn,a,b,ref,0, null,"sgnjn") + } + def testSgnjx(a : Float, b : Float): Unit ={ + val ref = b2f(f2b(a) ^ (f2b(b) & 0x80000000)) + testBinaryOp(sgnjx,a,b,ref,0, null,"sgnjx") } @@ -762,6 +803,41 @@ class FpuTest extends FunSuite{ val binaryOps = List[(Int,Int,Int,FpuRoundMode.E) => Unit](add, sub, mul) + + + for(_ <- 0 until 10000){ + testSgnj(b2f(Random.nextInt()), b2f(Random.nextInt())) + testSgnjn(b2f(Random.nextInt()), b2f(Random.nextInt())) + testSgnjx(b2f(Random.nextInt()), b2f(Random.nextInt())) + val (a,b,r,f) = f32.sgnj.RAW.f32_f32_i32 + testSgnj(a, b) + testSgnjn(a, b) + testSgnjx(a, b) + } + println("f32 sgnj done") + + for(_ <- 0 until 10000){ + testTransfer(b2f(Random.nextInt()), Random.nextBoolean(), Random.nextBoolean()) + } + for(_ <- 0 until 10000){ + val (a,b,r,f) = f32.transfer.RAW.f32_f32_i32 + testTransfer(a, Random.nextBoolean(), Random.nextBoolean()) + } + + println("f32 load/store/rf transfer done") + + + for(_ <- 0 until 10000){ + testClass(b2f(Random.nextInt())) + } + for(_ <- 0 until 10000){ + val (a,b,r,f) = f32.fclass.RAW.f32_f32_i32 + testClass(a) + } + + println("f32 class done") + + for(_ <- 0 until 10000){ val (a,b,r,f) = f32.min.RAW.f32_f32_f32 testMin(a,b) @@ -890,9 +966,9 @@ class FpuTest extends FunSuite{ for(_ <- 0 until 1000) testAdd(randomFloat(), randomFloat()) - testLoadStore(1.17549435082e-38f) - testLoadStore(1.4E-45f) - testLoadStore(3.44383110592e-41f) +// testTransfer(1.17549435082e-38f) +// testTransfer(1.4E-45f) +// testTransfer(3.44383110592e-41f) //TODO bring back those tests and test overflow / underflow (F2I) // testF2i(16.0f , false) @@ -918,7 +994,7 @@ class FpuTest extends FunSuite{ - testLoadStore(1.2f) +// testTransfer(1.2f) testMul(1.2f, 2.5f) testMul(b2f(0x00400000), 16.0f) testMul(b2f(0x00100000), 16.0f) @@ -941,8 +1017,8 @@ class FpuTest extends FunSuite{ - testLoadStore(1.765f) - testFmv_w_x(f2b(7.234f)) +// testTransfer(1.765f) +// testFmv_w_x(f2b(7.234f)) testI2f(64, false) for(i <- iUnsigned) testI2f(i, false) for(i <- iSigned) testI2f(i, true) @@ -985,8 +1061,8 @@ class FpuTest extends FunSuite{ // dut.clockDomain.waitSampling(10000000) - testFmv_x_w(1.246f) - testFmv_w_x(f2b(7.234f)) +// testFmv_x_w(1.246f) +// testFmv_w_x(f2b(7.234f)) testSgnj(1.0f, 2.0f) testSgnj(1.5f, 2.0f) @@ -1091,8 +1167,8 @@ class FpuTest extends FunSuite{ tests += (() =>{testDiv(randomFloat(), randomFloat())}) tests += (() =>{testSqrt(randomFloat().abs)}) tests += (() =>{testCmp(randomFloat(), randomFloat())}) - tests += (() =>{testFmv_x_w(randomFloat())}) - tests += (() =>{testFmv_w_x(f2b(randomFloat()))}) +// tests += (() =>{testFmv_x_w(randomFloat())}) +// tests += (() =>{testFmv_w_x(f2b(randomFloat()))}) // tests += (() =>{testMin(randomFloat(), randomFloat())}) tests += (() =>{testSgnj(randomFloat(), randomFloat())})