aboutsummaryrefslogtreecommitdiff
path: root/VexRiscv/src/test/scala/vexriscv
diff options
context:
space:
mode:
Diffstat (limited to 'VexRiscv/src/test/scala/vexriscv')
-rw-r--r--VexRiscv/src/test/scala/vexriscv/DhrystoneBench.scala164
-rw-r--r--VexRiscv/src/test/scala/vexriscv/MuraxSim.scala110
-rw-r--r--VexRiscv/src/test/scala/vexriscv/TestIndividualFeatures.scala875
-rw-r--r--VexRiscv/src/test/scala/vexriscv/experimental/Experiments.scala34
-rw-r--r--VexRiscv/src/test/scala/vexriscv/experimental/GenMicro.scala162
-rw-r--r--VexRiscv/src/test/scala/vexriscv/experimental/PlicCost.scala76
-rw-r--r--VexRiscv/src/test/scala/vexriscv/experimental/config.scala36
-rw-r--r--VexRiscv/src/test/scala/vexriscv/ip/fpu/FpuTest.scala1663
-rw-r--r--VexRiscv/src/test/scala/vexriscv/ip/fpu/Playground.scala47
9 files changed, 3167 insertions, 0 deletions
diff --git a/VexRiscv/src/test/scala/vexriscv/DhrystoneBench.scala b/VexRiscv/src/test/scala/vexriscv/DhrystoneBench.scala
new file mode 100644
index 0000000..48d1b67
--- /dev/null
+++ b/VexRiscv/src/test/scala/vexriscv/DhrystoneBench.scala
@@ -0,0 +1,164 @@
+package vexriscv
+
+import java.io.File
+
+import org.scalatest.funsuite.AnyFunSuite
+import spinal.core.SpinalVerilog
+import vexriscv.demo._
+
+import scala.sys.process._
+
+class DhrystoneBench extends AnyFunSuite {
+ def doCmd(cmd: String): String = {
+ val stdOut = new StringBuilder()
+ class Logger extends ProcessLogger {
+ override def err(s: => String): Unit = {
+ if (!s.startsWith("ar: creating ")) println(s)
+ }
+
+ override def out(s: => String): Unit = {
+ println(s)
+ stdOut ++= s
+ }
+
+ override def buffer[T](f: => T) = f
+ }
+ Process(cmd, new File("src/test/cpp/regression")).!(new Logger)
+ stdOut.toString()
+ }
+
+ val report = new StringBuilder()
+
+ def getDmips(name: String, gen: => Unit, testCmd: String): Unit = {
+ var genPassed = false
+ test(name + "_gen") {
+ gen
+ genPassed = true
+ }
+ test(name + "_test") {
+ assert(genPassed)
+ val str = doCmd(testCmd)
+ assert(!str.contains("FAIL"))
+ val intFind = "(\\d+\\.?)+".r
+ val dmips = intFind.findFirstIn("DMIPS per Mhz\\: (\\d+.?)+".r.findAllIn(str).toList.last).get.toDouble
+ val coremarkTicks = intFind.findFirstIn("Total ticks \\: (\\d+.?)+".r.findAllIn(str).toList.last).get.toDouble
+ val coremarkIterations = intFind.findFirstIn("Iterations \\: (\\d+.?)+".r.findAllIn(str).toList.last).get.toDouble
+ val coremarkHzs = intFind.findFirstIn("DCLOCKS_PER_SEC=(\\d+.?)+".r.findAllIn(str).toList.last).get.toDouble
+ val coremarkPerMhz = 1e6 * coremarkIterations / coremarkTicks
+ report ++= s"$name -> $dmips DMIPS/Mhz $coremarkPerMhz Coremark/Mhz\n"
+ }
+
+ }
+
+ for(withMemoryStage <- List(false, true)){
+ val stages = if(withMemoryStage) "Three" else "Two"
+ getDmips(
+ name = s"Gen${stages}StageArty",
+ gen = SpinalVerilog(GenTwoThreeStage.cpu(
+ withMulDiv = false,
+ bypass = false,
+ barrielShifter = false,
+ withMemoryStage = withMemoryStage
+ )),
+ testCmd = "make clean run REDO=10 IBUS=SIMPLE DBUS=SIMPLE CSR=no MMU=no DEBUG_PLUGIN=no MUL=no DIV=no COREMARK=yes"
+ )
+ getDmips(
+ name = s"Gen${stages}StageBarrielArty",
+ gen = SpinalVerilog(GenTwoThreeStage.cpu(
+ withMulDiv = false,
+ bypass = true,
+ barrielShifter = true,
+ withMemoryStage = withMemoryStage
+ )),
+ testCmd = "make clean run REDO=10 IBUS=SIMPLE DBUS=SIMPLE CSR=no MMU=no DEBUG_PLUGIN=no MUL=no DIV=no COREMARK=yes"
+ )
+ getDmips(
+ name = s"Gen${stages}StageMDArty",
+ gen = SpinalVerilog(GenTwoThreeStage.cpu(
+ withMulDiv = true,
+ bypass = false,
+ barrielShifter = false,
+ withMemoryStage = withMemoryStage
+ )),
+ testCmd = "make clean run REDO=10 IBUS=SIMPLE DBUS=SIMPLE CSR=no MMU=no DEBUG_PLUGIN=no MUL=yes DIV=yes COREMARK=yes"
+ )
+ getDmips(
+ name = s"Gen${stages}StageMDBarrielArty",
+ gen = SpinalVerilog(GenTwoThreeStage.cpu(
+ withMulDiv = true,
+ bypass = true,
+ barrielShifter = true,
+ withMemoryStage = withMemoryStage
+ )),
+ testCmd = "make clean run REDO=10 IBUS=SIMPLE DBUS=SIMPLE CSR=no MMU=no DEBUG_PLUGIN=no MUL=yes DIV=yes COREMARK=yes"
+ )
+ }
+
+ getDmips(
+ name = "GenSmallestNoCsr",
+ gen = GenSmallestNoCsr.main(null),
+ testCmd = "make clean run REDO=10 IBUS=SIMPLE DBUS=SIMPLE CSR=no MMU=no DEBUG_PLUGIN=no MUL=no DIV=no COREMARK=yes"
+ )
+
+
+ getDmips(
+ name = "GenSmallest",
+ gen = GenSmallest.main(null),
+ testCmd = "make clean run REDO=10 IBUS=SIMPLE DBUS=SIMPLE CSR=no MMU=no DEBUG_PLUGIN=no MUL=no DIV=no COREMARK=yes"
+ )
+
+
+ getDmips(
+ name = "GenSmallAndProductive",
+ gen = GenSmallAndProductive.main(null),
+ testCmd = "make clean run REDO=10 IBUS=SIMPLE DBUS=SIMPLE CSR=no MMU=no DEBUG_PLUGIN=no MUL=no DIV=no COREMARK=yes"
+ )
+
+ getDmips(
+ name = "GenSmallAndProductiveWithICache",
+ gen = GenSmallAndProductiveICache.main(null),
+ testCmd = "make clean run REDO=10 IBUS=CACHED DBUS=SIMPLE CSR=no MMU=no DEBUG_PLUGIN=no MUL=no DIV=no COREMARK=yes"
+ )
+
+
+ getDmips(
+ name = "GenFullNoMmuNoCache",
+ gen = GenFullNoMmuNoCache.main(null),
+ testCmd = "make clean run REDO=10 IBUS=SIMPLE DBUS=SIMPLE CSR=no MMU=no COREMARK=yes"
+ )
+
+ getDmips(
+ name = "GenNoCacheNoMmuMaxPerf",
+ gen = GenNoCacheNoMmuMaxPerf.main(null),
+ testCmd = "make clean run REDO=10 MMU=no CSR=no DBUS=SIMPLE IBUS=SIMPLE COREMARK=yes"
+ )
+
+
+ getDmips(
+ name = "GenFullNoMmuMaxPerf",
+ gen = GenFullNoMmuMaxPerf.main(null),
+ testCmd = "make clean run REDO=10 MMU=no CSR=no COREMARK=yes"
+ )
+ getDmips(
+ name = "GenFullNoMmu",
+ gen = GenFullNoMmu.main(null),
+ testCmd = "make clean run REDO=10 MMU=no CSR=no COREMARK=yes"
+ )
+
+ getDmips(
+ name = "GenFull",
+ gen = GenFull.main(null),
+ testCmd = "make clean run REDO=10 CSR=no MMU=no COREMARK=yes"
+ )
+
+ getDmips(
+ name = "GenLinuxBalenced",
+ gen = LinuxGen.main(Array.fill[String](0)("")),
+ testCmd = "make clean run IBUS=CACHED DBUS=CACHED DEBUG_PLUGIN=STD DHRYSTONE=yes SUPERVISOR=yes MMU=no CSR=yes CSR_SKIP_TEST=yes COMPRESSED=no MUL=yes DIV=yes LRSC=yes AMO=yes REDO=10 TRACE=no COREMARK=yes LINUX_REGRESSION=no"
+ )
+
+
+ test("final_report") {
+ println(report)
+ }
+}
diff --git a/VexRiscv/src/test/scala/vexriscv/MuraxSim.scala b/VexRiscv/src/test/scala/vexriscv/MuraxSim.scala
new file mode 100644
index 0000000..6a6a19c
--- /dev/null
+++ b/VexRiscv/src/test/scala/vexriscv/MuraxSim.scala
@@ -0,0 +1,110 @@
+package vexriscv
+
+import java.awt
+import java.awt.event.{ActionEvent, ActionListener, MouseEvent, MouseListener}
+
+import spinal.sim._
+import spinal.core._
+import spinal.core.sim._
+import vexriscv.demo.{Murax, MuraxConfig}
+import javax.swing._
+
+import spinal.lib.com.jtag.sim.JtagTcp
+import spinal.lib.com.uart.sim.{UartDecoder, UartEncoder}
+import vexriscv.test.{JLedArray, JSwitchArray}
+
+import scala.collection.mutable
+
+
+
+object MuraxSim {
+ def main(args: Array[String]): Unit = {
+// def config = MuraxConfig.default.copy(onChipRamSize = 256 kB)
+ def config = MuraxConfig.default(withXip = false).copy(onChipRamSize = 4 kB, onChipRamHexFile = "src/main/ressource/hex/muraxDemo.hex")
+ val simSlowDown = false
+ SimConfig.allOptimisation.compile(new Murax(config)).doSimUntilVoid{dut =>
+ val mainClkPeriod = (1e12/dut.config.coreFrequency.toDouble).toLong
+ val jtagClkPeriod = mainClkPeriod*4
+ val uartBaudRate = 115200
+ val uartBaudPeriod = (1e12/uartBaudRate).toLong
+
+ val clockDomain = ClockDomain(dut.io.mainClk, dut.io.asyncReset)
+ clockDomain.forkStimulus(mainClkPeriod)
+// clockDomain.forkSimSpeedPrinter(2)
+
+ val tcpJtag = JtagTcp(
+ jtag = dut.io.jtag,
+ jtagClkPeriod = jtagClkPeriod
+ )
+
+ val uartTx = UartDecoder(
+ uartPin = dut.io.uart.txd,
+ baudPeriod = uartBaudPeriod
+ )
+
+ val uartRx = UartEncoder(
+ uartPin = dut.io.uart.rxd,
+ baudPeriod = uartBaudPeriod
+ )
+
+ if(config.xipConfig != null)dut.io.xip.data(1).read #= 0
+
+ val guiThread = fork{
+ val guiToSim = mutable.Queue[Any]()
+
+ var ledsValue = 0l
+ var switchValue : () => BigInt = null
+ val ledsFrame = new JFrame{
+ setLayout(new BoxLayout(getContentPane, BoxLayout.Y_AXIS))
+
+ add(new JLedArray(8){
+ override def getValue = ledsValue
+ })
+ add{
+ val switches = new JSwitchArray(8)
+ switchValue = switches.getValue
+ switches
+ }
+
+ add(new JButton("Reset"){
+ addActionListener(new ActionListener {
+ override def actionPerformed(actionEvent: ActionEvent): Unit = {
+ println("ASYNC RESET")
+ guiToSim.enqueue("asyncReset")
+ }
+ })
+ setAlignmentX(awt.Component.CENTER_ALIGNMENT)
+ })
+ setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE)
+ pack()
+ setVisible(true)
+
+ }
+
+ //Fast refresh
+// clockDomain.onSampling{
+// dut.io.gpioA.read #= (dut.io.gpioA.write.toLong & dut.io.gpioA.writeEnable.toLong) | (switchValue() << 8)
+// }
+
+ //Slow refresh
+ while(true){
+ sleep(mainClkPeriod*50000)
+
+ val dummy = if(guiToSim.nonEmpty){
+ val request = guiToSim.dequeue()
+ if(request == "asyncReset"){
+ dut.io.asyncReset #= true
+ sleep(mainClkPeriod*32)
+ dut.io.asyncReset #= false
+ }
+ }
+
+ dut.io.gpioA.read #= (dut.io.gpioA.write.toLong & dut.io.gpioA.writeEnable.toLong) | (switchValue() << 8)
+ ledsValue = dut.io.gpioA.write.toLong
+ ledsFrame.repaint()
+ if(simSlowDown) Thread.sleep(400)
+ }
+ }
+ }
+ }
+}
diff --git a/VexRiscv/src/test/scala/vexriscv/TestIndividualFeatures.scala b/VexRiscv/src/test/scala/vexriscv/TestIndividualFeatures.scala
new file mode 100644
index 0000000..bd5acb0
--- /dev/null
+++ b/VexRiscv/src/test/scala/vexriscv/TestIndividualFeatures.scala
@@ -0,0 +1,875 @@
+package vexriscv
+
+import java.io.{File, OutputStream}
+import java.util.concurrent.{ForkJoinPool, TimeUnit}
+import org.apache.commons.io.FileUtils
+import org.scalatest.{BeforeAndAfterAll, ParallelTestExecution, Tag, Transformer}
+import org.scalatest.funsuite.AnyFunSuite
+
+import spinal.core._
+import spinal.lib.DoCmd
+import vexriscv.demo._
+import vexriscv.ip.{DataCacheConfig, InstructionCacheConfig}
+import vexriscv.plugin._
+
+import scala.collection.mutable
+import scala.collection.mutable.ArrayBuffer
+import scala.concurrent.duration.Duration
+import scala.concurrent.{Await, ExecutionContext, Future}
+import scala.sys.process._
+import scala.util.Random
+
+
+abstract class ConfigUniverse
+
+abstract class ConfigDimension[T <: ConfigPosition[_]](val name: String) {
+ def randomPosition(universes : Seq[ConfigUniverse], r : Random) : T = {
+ val ret = randomPositionImpl(universes, r)
+ ret.dimension = this
+ ret
+ }
+
+ protected def randomPositionImpl(universes : Seq[ConfigUniverse], r : Random) : T
+ protected def random[X](r : Random, positions : List[X]) : X = positions(r.nextInt(positions.length))
+}
+
+abstract class VexRiscvDimension(name: String) extends ConfigDimension[VexRiscvPosition](name)
+
+abstract class ConfigPosition[T](val name: String) {
+ def applyOn(config: T): Unit
+ var dimension : ConfigDimension[_] = null
+ def isCompatibleWith(positions : Seq[ConfigPosition[T]]) : Boolean = true
+}
+
+abstract class VexRiscvPosition(name: String) extends ConfigPosition[VexRiscvConfig](name){
+ def testParam : String = ""
+}
+
+class VexRiscvUniverse extends ConfigUniverse
+
+object VexRiscvUniverse{
+ val CACHE_ALL = new VexRiscvUniverse
+ val CATCH_ALL = new VexRiscvUniverse
+ val MMU = new VexRiscvUniverse
+ val PMP = new VexRiscvUniverse
+ val FORCE_MULDIV = new VexRiscvUniverse
+ val SUPERVISOR = new VexRiscvUniverse
+ val NO_WRITEBACK = new VexRiscvUniverse
+ val NO_MEMORY = new VexRiscvUniverse
+ val EXECUTE_RF = new VexRiscvUniverse
+}
+
+
+object Hack{
+ var dCounter = 0
+}
+
+class ShiftDimension extends VexRiscvDimension("Shift") {
+ override def randomPositionImpl(universes: Seq[ConfigUniverse], r: Random) = {
+ var l = List(
+ new VexRiscvPosition("FullEarly") {
+ override def applyOn(config: VexRiscvConfig): Unit = config.plugins += new FullBarrelShifterPlugin(earlyInjection = true)
+ },
+ new VexRiscvPosition("Light") {
+ override def applyOn(config: VexRiscvConfig): Unit = config.plugins += new LightShifterPlugin
+ }
+ )
+
+ if(!universes.contains(VexRiscvUniverse.NO_MEMORY)) l = new VexRiscvPosition("FullLate") {
+ override def applyOn(config: VexRiscvConfig): Unit = config.plugins += new FullBarrelShifterPlugin(earlyInjection = false)
+ } :: l
+
+ random(r, l)
+ }
+}
+
+class BranchDimension extends VexRiscvDimension("Branch") {
+
+ override def randomPositionImpl(universes: Seq[ConfigUniverse], r: Random) = {
+ val catchAll = universes.contains(VexRiscvUniverse.CATCH_ALL)
+ val early = r.nextBoolean() || universes.contains(VexRiscvUniverse.NO_MEMORY)
+ new VexRiscvPosition(if(early) "Early" else "Late") {
+ override def applyOn(config: VexRiscvConfig): Unit = config.plugins += new BranchPlugin(
+ earlyBranch = early,
+ catchAddressMisaligned = catchAll
+ )
+ }
+ }
+}
+
+
+
+class MulDivDimension extends VexRiscvDimension("MulDiv") {
+
+ override def randomPositionImpl(universes: Seq[ConfigUniverse], r: Random) = {
+ val noMemory = universes.contains(VexRiscvUniverse.NO_MEMORY)
+ val noWriteBack = universes.contains(VexRiscvUniverse.NO_WRITEBACK)
+
+ var l = List[VexRiscvPosition]()
+
+
+
+ l = new VexRiscvPosition("MulDivFpgaSimple") {
+ override def testParam = "MUL=yes DIV=yes"
+ override def applyOn(config: VexRiscvConfig): Unit = {
+ config.plugins += new MulSimplePlugin
+ config.plugins += new MulDivIterativePlugin(
+ genMul = false,
+ genDiv = true,
+ mulUnrollFactor = 32,
+ divUnrollFactor = 1
+ )
+ }
+ } :: l
+
+ if(!noMemory && !noWriteBack) l = new VexRiscvPosition("MulDivFpga16BitsDsp") {
+ override def testParam = "MUL=yes DIV=yes"
+ override def applyOn(config: VexRiscvConfig): Unit = {
+ config.plugins += new Mul16Plugin
+ config.plugins += new MulDivIterativePlugin(
+ genMul = false,
+ genDiv = true,
+ mulUnrollFactor = 32,
+ divUnrollFactor = 1
+ )
+ }
+ } :: l
+
+ if(!noMemory) {
+ l = new VexRiscvPosition("MulDivAsic") {
+ override def testParam = "MUL=yes DIV=yes"
+ override def applyOn(config: VexRiscvConfig): Unit = {
+ config.plugins += new MulDivIterativePlugin(
+ genMul = true,
+ genDiv = true,
+ mulUnrollFactor = 32,
+ divUnrollFactor = 4
+ )
+ }
+ } :: new VexRiscvPosition("MulDivFpgaNoDsp") {
+ override def testParam = "MUL=yes DIV=yes"
+ override def applyOn(config: VexRiscvConfig): Unit = {
+ config.plugins += new MulDivIterativePlugin(
+ genMul = true,
+ genDiv = true,
+ mulUnrollFactor = 1,
+ divUnrollFactor = 1
+ )
+ }
+ } :: new VexRiscvPosition("MulDivFpgaNoDspFastMul") {
+ override def testParam = "MUL=yes DIV=yes"
+ override def applyOn(config: VexRiscvConfig): Unit = {
+ config.plugins += new MulDivIterativePlugin(
+ genMul = true,
+ genDiv = true,
+ mulUnrollFactor = 8,
+ divUnrollFactor = 1
+ )
+ }
+ } :: l
+ }
+
+ if(!universes.contains(VexRiscvUniverse.FORCE_MULDIV)) l = new VexRiscvPosition("NoMulDiv") {
+ override def applyOn(config: VexRiscvConfig): Unit = {}
+ override def testParam = "MUL=no DIV=no"
+ } :: l
+
+
+ if(!noMemory && !noWriteBack) {
+ val inputBuffer = r.nextBoolean()
+ val outputBuffer = r.nextBoolean()
+ l = new VexRiscvPosition(s"MulDivFpga$inputBuffer$outputBuffer") {
+ override def testParam = "MUL=yes DIV=yes"
+
+ override def applyOn(config: VexRiscvConfig): Unit = {
+ config.plugins += new MulPlugin(
+ inputBuffer = inputBuffer,
+ outputBuffer = outputBuffer
+ )
+ config.plugins += new MulDivIterativePlugin(
+ genMul = false,
+ genDiv = true,
+ mulUnrollFactor = 32,
+ divUnrollFactor = 1
+ )
+ }
+ } :: l
+ }
+
+ random(r, l)
+ }
+}
+
+trait InstructionAnticipatedPosition{
+ def instructionAnticipatedOk() : Boolean
+}
+
+class RegFileDimension extends VexRiscvDimension("RegFile") {
+ override def randomPositionImpl(universes: Seq[ConfigUniverse], r: Random) = {
+ val executeRf = universes.contains(VexRiscvUniverse.EXECUTE_RF)
+ random(r, List(
+ new VexRiscvPosition("Async" + (if(executeRf) "ER" else "DR")) {
+ override def applyOn(config: VexRiscvConfig): Unit = config.plugins += new RegFilePlugin(
+ regFileReadyKind = plugin.ASYNC,
+ zeroBoot = true,
+ readInExecute = executeRf
+ )
+ },
+ new VexRiscvPosition("Sync" + (if(executeRf) "ER" else "DR")) {
+ override def applyOn(config: VexRiscvConfig): Unit = config.plugins += new RegFilePlugin(
+ regFileReadyKind = plugin.SYNC,
+ zeroBoot = true,
+ readInExecute = executeRf
+ )
+
+ override def isCompatibleWith(positions: Seq[ConfigPosition[VexRiscvConfig]]) = executeRf || positions.exists{
+ case p : InstructionAnticipatedPosition => p.instructionAnticipatedOk()
+ case _ => false
+ }
+ }
+ ))
+ }
+}
+
+
+
+class HazardDimension extends VexRiscvDimension("Hazard") {
+
+ override def randomPositionImpl(universes: Seq[ConfigUniverse], r: Random) : VexRiscvPosition = {
+ if(r.nextDouble() < 0.8){
+ random(r, List(
+ new VexRiscvPosition("Interlock") {
+ override def applyOn(config: VexRiscvConfig): Unit = config.plugins += new HazardSimplePlugin(
+ bypassExecute = false,
+ bypassMemory = false,
+ bypassWriteBack = false,
+ bypassWriteBackBuffer = false,
+ pessimisticUseSrc = false,
+ pessimisticWriteRegFile = false,
+ pessimisticAddressMatch = false
+ )
+ },
+ new VexRiscvPosition("BypassAll") {
+ override def applyOn(config: VexRiscvConfig): Unit = config.plugins += new HazardSimplePlugin(
+ bypassExecute = true,
+ bypassMemory = true,
+ bypassWriteBack = true,
+ bypassWriteBackBuffer = true,
+ pessimisticUseSrc = false,
+ pessimisticWriteRegFile = false,
+ pessimisticAddressMatch = false
+ )
+ }
+ ))
+ }else {
+ random(r, List(
+ new VexRiscvPosition("BypassExecute") {
+ override def applyOn(config: VexRiscvConfig): Unit = config.plugins += new HazardSimplePlugin(
+ bypassExecute = true,
+ bypassMemory = false,
+ bypassWriteBack = false,
+ bypassWriteBackBuffer = false,
+ pessimisticUseSrc = false,
+ pessimisticWriteRegFile = false,
+ pessimisticAddressMatch = false
+ )
+ },
+ new VexRiscvPosition("BypassMemory") {
+ override def applyOn(config: VexRiscvConfig): Unit = config.plugins += new HazardSimplePlugin(
+ bypassExecute = false,
+ bypassMemory = true,
+ bypassWriteBack = false,
+ bypassWriteBackBuffer = false,
+ pessimisticUseSrc = false,
+ pessimisticWriteRegFile = false,
+ pessimisticAddressMatch = false
+ )
+ },
+ new VexRiscvPosition("BypassWriteBack") {
+ override def applyOn(config: VexRiscvConfig): Unit = config.plugins += new HazardSimplePlugin(
+ bypassExecute = false,
+ bypassMemory = false,
+ bypassWriteBack = true,
+ bypassWriteBackBuffer = false,
+ pessimisticUseSrc = false,
+ pessimisticWriteRegFile = false,
+ pessimisticAddressMatch = false
+ )
+ },
+ new VexRiscvPosition("BypassWriteBackBuffer") {
+ override def applyOn(config: VexRiscvConfig): Unit = config.plugins += new HazardSimplePlugin(
+ bypassExecute = false,
+ bypassMemory = false,
+ bypassWriteBack = false,
+ bypassWriteBackBuffer = true,
+ pessimisticUseSrc = false,
+ pessimisticWriteRegFile = false,
+ pessimisticAddressMatch = false
+ )
+ }
+ ))
+ }
+ }}
+
+
+class SrcDimension extends VexRiscvDimension("Src") {
+
+ override def randomPositionImpl(universes: Seq[ConfigUniverse], r: Random) = {
+ val separatedAddSub = r.nextBoolean()
+ val executeInsertion = universes.contains(VexRiscvUniverse.EXECUTE_RF) || r.nextBoolean()
+ new VexRiscvPosition((if (separatedAddSub) "AddSub" else "") + (if (executeInsertion) "Execute" else "")) {
+ override def applyOn(config: VexRiscvConfig): Unit = config.plugins += new SrcPlugin(
+ separatedAddSub = separatedAddSub,
+ executeInsertion = executeInsertion
+ )
+ }
+ }
+}
+
+
+class IBusDimension(rvcRate : Double) extends VexRiscvDimension("IBus") {
+
+
+ override def randomPositionImpl(universes: Seq[ConfigUniverse], r: Random) = {
+ val catchAll = universes.contains(VexRiscvUniverse.CATCH_ALL)
+ val cacheAll = universes.contains(VexRiscvUniverse.CACHE_ALL)
+
+ if(r.nextDouble() < 0.5 && !cacheAll){
+ val mmuConfig = if(universes.contains(VexRiscvUniverse.MMU)) MmuPortConfig( portTlbSize = 4) else null
+
+ val latency = r.nextInt(5) + 1
+ val compressed = r.nextDouble() < rvcRate
+ val injectorStage = r.nextBoolean() || latency == 1
+ val prediction = random(r, List(NONE, STATIC, DYNAMIC, DYNAMIC_TARGET))
+ val catchAll = universes.contains(VexRiscvUniverse.CATCH_ALL)
+ val cmdForkOnSecondStage = r.nextBoolean()
+ val cmdForkPersistence = r.nextBoolean()
+ new VexRiscvPosition("Simple" + latency + (if(cmdForkOnSecondStage) "S2" else "") + (if(cmdForkPersistence) "P" else "") + (if(injectorStage) "InjStage" else "") + (if(compressed) "Rvc" else "") + prediction.getClass.getTypeName().replace("$","")) with InstructionAnticipatedPosition{
+ override def testParam = "IBUS=SIMPLE" + (if(compressed) " COMPRESSED=yes" else "")
+ override def applyOn(config: VexRiscvConfig): Unit = config.plugins += new IBusSimplePlugin(
+ resetVector = 0x80000000l,
+ cmdForkOnSecondStage = cmdForkOnSecondStage,
+ cmdForkPersistence = cmdForkPersistence,
+ prediction = prediction,
+ catchAccessFault = catchAll,
+ compressedGen = compressed,
+ busLatencyMin = latency,
+ injectorStage = injectorStage,
+ memoryTranslatorPortConfig = mmuConfig
+ )
+ override def instructionAnticipatedOk() = injectorStage
+ }
+ } else {
+ val twoStageMmu = r.nextBoolean()
+ val asyncTagMemory = r.nextBoolean()
+ val mmuConfig = if(universes.contains(VexRiscvUniverse.MMU)) MmuPortConfig(portTlbSize = 4, latency = if(twoStageMmu) 1 else 0, earlyRequireMmuLockup = Random.nextBoolean() && twoStageMmu, earlyCacheHits = Random.nextBoolean() && twoStageMmu) else null
+
+ val catchAll = universes.contains(VexRiscvUniverse.CATCH_ALL)
+ val compressed = r.nextDouble() < rvcRate
+ val tighlyCoupled = r.nextBoolean() && !catchAll
+ val reducedBankWidth = r.nextBoolean()
+// val tighlyCoupled = false
+ val prediction = random(r, List(NONE, STATIC, DYNAMIC, DYNAMIC_TARGET))
+ val relaxedPcCalculation, twoCycleCache, injectorStage = r.nextBoolean()
+ val twoCycleRam = r.nextBoolean() && twoCycleCache
+ val twoCycleRamInnerMux = r.nextBoolean() && twoCycleRam
+ val memDataWidth = List(32,64,128)(r.nextInt(3))
+ val bytePerLine = Math.max(memDataWidth/8, List(8,16,32,64)(r.nextInt(4)))
+ var cacheSize = 0
+ var wayCount = 0
+ do{
+ cacheSize = 512 << r.nextInt(5)
+ wayCount = 1 << r.nextInt(3)
+ }while(cacheSize/wayCount < 512 || (catchAll && cacheSize/wayCount > 4096))
+
+ new VexRiscvPosition(s"Cached${memDataWidth}d" + (if(twoCycleCache) "2cc" else "") + (if(injectorStage) "Injstage" else "") + (if(twoCycleRam) "2cr" else "") + "S" + cacheSize + "W" + wayCount + "BPL" + bytePerLine + (if(relaxedPcCalculation) "Relax" else "") + (if(compressed) "Rvc" else "") + prediction.getClass.getTypeName().replace("$","")+ (if(tighlyCoupled)"Tc" else "") + (if(asyncTagMemory) "Atm" else "")) with InstructionAnticipatedPosition{
+ override def testParam = s"IBUS=CACHED IBUS_DATA_WIDTH=$memDataWidth" + (if(compressed) " COMPRESSED=yes" else "") + (if(tighlyCoupled)" IBUS_TC=yes" else "")
+ override def applyOn(config: VexRiscvConfig): Unit = {
+ val p = new IBusCachedPlugin(
+ resetVector = 0x80000000l,
+ compressedGen = compressed,
+ prediction = prediction,
+ relaxedPcCalculation = relaxedPcCalculation,
+ injectorStage = injectorStage,
+ memoryTranslatorPortConfig = mmuConfig,
+ config = InstructionCacheConfig(
+ cacheSize = cacheSize,
+ bytePerLine = bytePerLine,
+ wayCount = wayCount,
+ addressWidth = 32,
+ cpuDataWidth = 32,
+ memDataWidth = memDataWidth,
+ catchIllegalAccess = catchAll,
+ catchAccessFault = catchAll,
+ asyncTagMemory = asyncTagMemory,
+ twoCycleRam = twoCycleRam,
+ twoCycleCache = twoCycleCache,
+ twoCycleRamInnerMux = twoCycleRamInnerMux,
+ reducedBankWidth = reducedBankWidth
+ )
+ )
+ if(tighlyCoupled) p.newTightlyCoupledPort(TightlyCoupledPortParameter("iBusTc", a => a(30 downto 28) === 0x0))
+ config.plugins += p
+ }
+ override def instructionAnticipatedOk() = !twoCycleCache || ((!twoCycleRam || wayCount == 1) && !compressed)
+ }
+ }
+ }
+}
+
+
+
+
+class DBusDimension extends VexRiscvDimension("DBus") {
+
+ override def randomPositionImpl(universes: Seq[ConfigUniverse], r: Random) = {
+ val catchAll = universes.contains(VexRiscvUniverse.CATCH_ALL)
+ val cacheAll = universes.contains(VexRiscvUniverse.CACHE_ALL)
+ val noMemory = universes.contains(VexRiscvUniverse.NO_MEMORY)
+ val noWriteBack = universes.contains(VexRiscvUniverse.NO_WRITEBACK)
+
+ if((r.nextDouble() < 0.4 || noMemory) && !cacheAll){
+ val mmuConfig = if(universes.contains(VexRiscvUniverse.MMU)) MmuPortConfig( portTlbSize = 4, latency = 0) else null
+ val withLrSc = catchAll
+ val earlyInjection = r.nextBoolean() && !universes.contains(VexRiscvUniverse.NO_WRITEBACK)
+ new VexRiscvPosition("Simple" + (if(earlyInjection) "Early" else "Late")) {
+ override def testParam = "DBUS=SIMPLE " + (if(withLrSc) "LRSC=yes " else "")
+ override def applyOn(config: VexRiscvConfig): Unit = config.plugins += new DBusSimplePlugin(
+ catchAddressMisaligned = catchAll,
+ catchAccessFault = catchAll,
+ earlyInjection = earlyInjection,
+ memoryTranslatorPortConfig = mmuConfig,
+ withLrSc = withLrSc
+ )
+// override def isCompatibleWith(positions: Seq[ConfigPosition[VexRiscvConfig]]) = catchAll == positions.exists(_.isInstanceOf[CatchAllPosition])
+ }
+ } else {
+ val twoStageMmu = r.nextBoolean() && !noMemory && !noWriteBack
+ val mmuConfig = if(universes.contains(VexRiscvUniverse.MMU)) MmuPortConfig(portTlbSize = 4, latency = if(twoStageMmu) 1 else 0, earlyRequireMmuLockup = Random.nextBoolean() && twoStageMmu, earlyCacheHits = Random.nextBoolean() && twoStageMmu) else null
+ val memDataWidth = List(32,64,128)(r.nextInt(3))
+ val cpuDataWidthChoices = List(32,64,128).filter(_ <= memDataWidth)
+ val cpuDataWidth = cpuDataWidthChoices(r.nextInt(cpuDataWidthChoices.size))
+ val bytePerLine = Math.max(memDataWidth/8, List(8,16,32,64)(r.nextInt(4)))
+ var cacheSize = 0
+ var wayCount = 0
+ val withLrSc = catchAll
+ val withSmp = withLrSc && r.nextBoolean()
+ val withAmo = catchAll && r.nextBoolean() || withSmp
+ val dBusRspSlavePipe = r.nextBoolean() || withSmp
+ val relaxedMemoryTranslationRegister = r.nextBoolean()
+ val earlyWaysHits = r.nextBoolean() && !noWriteBack
+ val directTlbHit = r.nextBoolean() && mmuConfig.isInstanceOf[MmuPortConfig]
+ val dBusCmdMasterPipe, dBusCmdSlavePipe = false //As it create test bench issues
+ val asyncTagMemory = r.nextBoolean()
+
+ do{
+ cacheSize = 512 << r.nextInt(5)
+ wayCount = 1 << r.nextInt(3)
+ }while(cacheSize/wayCount < 512 || (catchAll && cacheSize/wayCount > 4096))
+ new VexRiscvPosition(s"Cached${memDataWidth}d${cpuDataWidth}c" + "S" + cacheSize + "W" + wayCount + "BPL" + bytePerLine + (if(dBusCmdMasterPipe) "Cmp " else "") + (if(dBusCmdSlavePipe) "Csp " else "") + (if(dBusRspSlavePipe) "Rsp " else "") + (if(relaxedMemoryTranslationRegister) "Rmtr " else "") + (if(earlyWaysHits) "Ewh " else "") + (if(withAmo) "Amo " else "") + (if(withSmp) "Smp " else "") + (if(directTlbHit) "Dtlb " else "") + (if(twoStageMmu) "Tsmmu " else "") + (if(asyncTagMemory) "Atm" else "")) {
+ override def testParam = s"DBUS=CACHED DBUS_LOAD_DATA_WIDTH=$memDataWidth DBUS_STORE_DATA_WIDTH=$cpuDataWidth " + (if(withLrSc) "LRSC=yes " else "") + (if(withAmo) "AMO=yes " else "") + (if(withSmp) "DBUS_EXCLUSIVE=yes DBUS_INVALIDATE=yes " else "")
+
+ override def applyOn(config: VexRiscvConfig): Unit = {
+ config.plugins += new DBusCachedPlugin(
+ config = new DataCacheConfig(
+ cacheSize = cacheSize,
+ bytePerLine = bytePerLine,
+ wayCount = wayCount,
+ addressWidth = 32,
+ cpuDataWidth = cpuDataWidth, //Not tested
+ memDataWidth = memDataWidth,
+ catchAccessError = catchAll,
+ catchIllegal = catchAll,
+ catchUnaligned = catchAll,
+ withLrSc = withLrSc,
+ withAmo = withAmo,
+ earlyWaysHits = earlyWaysHits,
+ withExclusive = withSmp,
+ withInvalidate = withSmp,
+ directTlbHit = directTlbHit,
+ asyncTagMemory = asyncTagMemory
+ ),
+ dBusCmdMasterPipe = dBusCmdMasterPipe,
+ dBusCmdSlavePipe = dBusCmdSlavePipe,
+ dBusRspSlavePipe = dBusRspSlavePipe,
+ relaxedMemoryTranslationRegister = relaxedMemoryTranslationRegister,
+ memoryTranslatorPortConfig = mmuConfig
+ )
+ }
+ }
+ }
+ }
+}
+
+
+class MmuPmpDimension extends VexRiscvDimension("DBus") {
+
+ override def randomPositionImpl(universes: Seq[ConfigUniverse], r: Random) = {
+ if(universes.contains(VexRiscvUniverse.MMU)) {
+ new VexRiscvPosition("WithMmu") {
+ override def testParam = "MMU=yes PMP=no"
+
+ override def applyOn(config: VexRiscvConfig): Unit = {
+ config.plugins += new MmuPlugin(
+ ioRange = (x => x(31 downto 28) === 0xF)
+ )
+ }
+ }
+ } else if (universes.contains(VexRiscvUniverse.PMP)) {
+ new VexRiscvPosition("WithPmp") {
+ override def testParam = "MMU=no PMP=yes"
+
+ override def applyOn(config: VexRiscvConfig): Unit = {
+ config.plugins += new PmpPlugin(
+ regions = 16,
+ granularity = 32,
+ ioRange = _ (31 downto 28) === 0xF
+ )
+ }
+ }
+ } else {
+ new VexRiscvPosition("NoMemProtect") {
+ override def testParam = "MMU=no PMP=no"
+
+ override def applyOn(config: VexRiscvConfig): Unit = {
+ config.plugins += new StaticMemoryTranslatorPlugin(
+ ioRange = _ (31 downto 28) === 0xF
+ )
+ }
+ }
+ }
+ }
+}
+
+
+
+trait CatchAllPosition
+
+
+class CsrDimension(freertos : String, zephyr : String, linux : String) extends VexRiscvDimension("Csr") {
+ override def randomPositionImpl(universes: Seq[ConfigUniverse], r: Random) = {
+ val pmp = universes.contains(VexRiscvUniverse.PMP)
+ val catchAll = universes.contains(VexRiscvUniverse.CATCH_ALL)
+ val supervisor = universes.contains(VexRiscvUniverse.SUPERVISOR)
+ if(supervisor){
+ new VexRiscvPosition("Supervisor") with CatchAllPosition{
+ override def applyOn(config: VexRiscvConfig): Unit = config.plugins += new CsrPlugin(CsrPluginConfig.linuxFull(0x80000020l))
+ override def testParam = s"FREERTOS=$freertos ZEPHYR=$zephyr LINUX_REGRESSION=$linux SUPERVISOR=yes"
+ }
+ } else if(pmp){
+ new VexRiscvPosition("Secure") with CatchAllPosition{
+ override def applyOn(config: VexRiscvConfig): Unit = config.plugins += new CsrPlugin(CsrPluginConfig.secure(0x80000020l))
+ override def testParam = s"CSR=yes CSR_SKIP_TEST=yes FREERTOS=$freertos ZEPHYR=$zephyr"
+ }
+ } else if(catchAll){
+ new VexRiscvPosition("MachineOs") with CatchAllPosition{
+ override def applyOn(config: VexRiscvConfig): Unit = config.plugins += new CsrPlugin(CsrPluginConfig.all(0x80000020l))
+ override def testParam = s"CSR=yes CSR_SKIP_TEST=yes FREERTOS=$freertos ZEPHYR=$zephyr"
+ }
+ } else if(r.nextDouble() < 0.3){
+ new VexRiscvPosition("AllNoException") with CatchAllPosition{
+ override def applyOn(config: VexRiscvConfig): Unit = config.plugins += new CsrPlugin(CsrPluginConfig.all(0x80000020l).noExceptionButEcall)
+ override def testParam = s"CSR=yes CSR_SKIP_TEST=yes FREERTOS=$freertos ZEPHYR=$zephyr"
+ }
+ } else {
+ new VexRiscvPosition("None") {
+ override def applyOn(config: VexRiscvConfig): Unit = {}
+ override def testParam = "CSR=no"
+ }
+ }
+ }
+}
+
+class DebugDimension extends VexRiscvDimension("Debug") {
+
+ override def randomPositionImpl(universes: Seq[ConfigUniverse], r: Random) = random(r, List(
+ new VexRiscvPosition("None") {
+ override def applyOn(config: VexRiscvConfig): Unit = {}
+ override def testParam = "DEBUG_PLUGIN=no"
+ },
+ new VexRiscvPosition("Enable") {
+ override def applyOn(config: VexRiscvConfig): Unit = config.plugins += new DebugPlugin(ClockDomain.current.clone(reset = Bool().setName("debugReset")))
+ override def testParam = "CONCURRENT_OS_EXECUTIONS=yes"
+ }
+ ))
+}
+
+class DecoderDimension extends VexRiscvDimension("Decoder") {
+
+ override def randomPositionImpl(universes: Seq[ConfigUniverse], r: Random) = {
+ val catchAll = universes.contains(VexRiscvUniverse.CATCH_ALL)
+ new VexRiscvPosition("") {
+ override def applyOn(config: VexRiscvConfig): Unit = config.plugins += new DecoderSimplePlugin(
+ catchIllegalInstruction = catchAll,
+ throwIllegalInstruction = false
+ )
+
+// override def isCompatibleWith(positions: Seq[ConfigPosition[VexRiscvConfig]]) = catchAll == positions.exists(_.isInstanceOf[CatchAllPosition])
+ }
+ }
+}
+
+object PlayFuture extends App{
+ implicit val ec = ExecutionContext.global
+ val x = for(i <- 0 until 160) yield Future {
+ print(s"$i ")
+ Thread.sleep(1000)
+ }
+
+ Thread.sleep(8000)
+}
+
+class MultithreadedFunSuite(threadCount : Int) extends AnyFunSuite {
+ val finalThreadCount = if(threadCount > 0) threadCount else {
+ new oshi.SystemInfo().getHardware.getProcessor.getLogicalProcessorCount
+ }
+ implicit val ec = ExecutionContext.fromExecutorService(
+ new ForkJoinPool(finalThreadCount, ForkJoinPool.defaultForkJoinWorkerThreadFactory, null, true)
+ )
+ class Job(body : => Unit){
+ val originalOutput = Console.out
+ val buffer = mutable.Queue[Char]()
+ var bufferEnabled = true
+ def redirector() = new OutputStream{
+ override def write(i: Int): Unit = synchronized {
+ if(bufferEnabled) buffer += i.toChar
+ else originalOutput.print(i.toChar)
+ }
+ }
+ val future = Future{
+ Console.withOut(redirector()){
+ Console.withErr(redirector())(body)
+ }
+ }
+
+ def join(): Unit = {
+ Thread.sleep(50)
+ synchronized{
+ bufferEnabled = false
+ buffer.foreach(originalOutput.print)
+ }
+ Await.result(future, Duration.Inf)
+ }
+ }
+
+ def testMp(testName: String, testTags: Tag*)(testFun: => Unit) {
+ val job = new Job(testFun)
+ super.test(testName, testTags :_*)(job.join())
+ }
+ protected def testSingleThread(testName: String, testTags: Tag*)(testFun: => Unit) {
+ super.test(testName, testTags :_*)(testFun)
+ }
+}
+
+
+class FunTestPara extends MultithreadedFunSuite(3){
+ def createTest(name : String): Unit ={
+ testMp(name){
+ for(i <- 0 to 4) {
+ println(s"$name $i")
+ Thread.sleep(500)
+ }
+ }
+ }
+ (0 to 80).map(_.toString).foreach(createTest)
+}
+
+//class FunTestPlay extends FunSuite {
+// def createTest(name : String): Unit ={
+// test(name){
+// Thread.sleep(500)
+// for(i <- 0 to 4) {
+// println(s"$name $i")
+// Thread.sleep(500)
+// }
+// }
+// }
+// (0 to 80).map(_.toString).foreach(createTest)
+//}
+
+class TestIndividualFeatures extends MultithreadedFunSuite(sys.env.getOrElse("VEXRISCV_REGRESSION_THREAD_COUNT", "0").toInt) {
+ val testCount = sys.env.getOrElse("VEXRISCV_REGRESSION_CONFIG_COUNT", "100").toInt
+ val seed = sys.env.getOrElse("VEXRISCV_REGRESSION_SEED", Random.nextLong().toString).toLong
+ val testId : Set[Int] = sys.env.get("VEXRISCV_REGRESSION_TEST_ID") match {
+ case Some(x) if x != "" => x.split(',').map(_.toInt).toSet
+ case _ => (0 until testCount).toSet
+ }
+ val rvcRate = sys.env.getOrElse("VEXRISCV_REGRESSION_CONFIG_RVC_RATE", "0.5").toDouble
+ val linuxRate = sys.env.getOrElse("VEXRISCV_REGRESSION_CONFIG_LINUX_RATE", "0.3").toDouble
+ val machineOsRate = sys.env.getOrElse("VEXRISCV_REGRESSION_CONFIG_MACHINE_OS_RATE", "0.5").toDouble
+ val secureRate = sys.env.getOrElse("VEXRISCV_REGRESSION_CONFIG_SECURE_RATE", "0.2").toDouble
+ val linuxRegression = sys.env.getOrElse("VEXRISCV_REGRESSION_LINUX_REGRESSION", "yes")
+ val coremarkRegression = sys.env.getOrElse("VEXRISCV_REGRESSION_COREMARK", "yes")
+ val zephyrCount = sys.env.getOrElse("VEXRISCV_REGRESSION_ZEPHYR_COUNT", "4")
+ val demwRate = sys.env.getOrElse("VEXRISCV_REGRESSION_CONFIG_DEMW_RATE", "0.6").toDouble
+ val demRate = sys.env.getOrElse("VEXRISCV_REGRESSION_CONFIG_DEM_RATE", "0.5").toDouble
+ val stopOnError = sys.env.getOrElse("VEXRISCV_REGRESSION_STOP_ON_ERROR", "no")
+ val lock = new{}
+
+
+
+ val dimensions = List(
+ new IBusDimension(rvcRate),
+ new DBusDimension,
+ new MulDivDimension,
+ new ShiftDimension,
+ new BranchDimension,
+ new HazardDimension,
+ new RegFileDimension,
+ new SrcDimension,
+ new CsrDimension(/*sys.env.getOrElse("VEXRISCV_REGRESSION_FREERTOS_COUNT", "1")*/ "0", zephyrCount, linuxRegression), //Freertos old port software is broken
+ new DecoderDimension,
+ new DebugDimension,
+ new MmuPmpDimension
+ )
+
+ var clockCounter = 0l
+ var startAt = System.currentTimeMillis()
+ def doTest(positionsToApply : List[VexRiscvPosition], prefix : String = "", testSeed : Int, universes : mutable.HashSet[VexRiscvUniverse]): Unit ={
+ val noMemory = universes.contains(VexRiscvUniverse.NO_MEMORY)
+ val noWriteback = universes.contains(VexRiscvUniverse.NO_WRITEBACK)
+ val name = (if(noMemory) "noMemoryStage_" else "") + (if(noWriteback) "noWritebackStage_" else "") + positionsToApply.map(d => d.dimension.name + "_" + d.name).mkString("_")
+ val workspace = "simWorkspace"
+ val project = s"$workspace/$prefix"
+ def doCmd(cmd: String): String = {
+ val stdOut = new StringBuilder()
+ class Logger extends ProcessLogger {
+ override def err(s: => String): Unit = {
+ if (!s.startsWith("ar: creating ")) println(s)
+ }
+ override def out(s: => String): Unit = {
+ println(s)
+ stdOut ++= s
+ }
+ override def buffer[T](f: => T) = f
+ }
+ Process(cmd, new File(project)).!(new Logger)
+ stdOut.toString()
+ }
+
+ testMp(prefix + name) {
+ println("START TEST " + prefix + name)
+
+ //Cleanup
+ FileUtils.deleteDirectory(new File(project))
+ FileUtils.forceMkdir(new File(project))
+
+ //Generate RTL
+ FileUtils.deleteQuietly(new File("VexRiscv.v"))
+ SpinalConfig(targetDirectory = project).generateVerilog{
+ val config = VexRiscvConfig(
+ withMemoryStage = !noMemory,
+ withWriteBackStage = !noWriteback,
+ plugins = List(
+ new IntAluPlugin,
+ new YamlPlugin("cpu0.yaml")
+ )
+ )
+ for (positionToApply <- positionsToApply) positionToApply.applyOn(config)
+ new VexRiscv(config)
+ }
+
+ //Setup test
+ val files = List("main.cpp", "encoding.h" ,"makefile", "dhrystoneO3.logRef", "dhrystoneO3C.logRef","dhrystoneO3MC.logRef","dhrystoneO3M.logRef")
+ files.foreach(f => FileUtils.copyFileToDirectory(new File(s"src/test/cpp/regression/$f"), new File(project)))
+
+ //Test RTL
+ val debug = true
+ val stdCmd = (s"make run REGRESSION_PATH=../../src/test/cpp/regression VEXRISCV_FILE=VexRiscv.v WITH_USER_IO=no REDO=10 TRACE=${if(debug) "yes" else "no"} TRACE_START=100000000000ll FLOW_INFO=no STOP_ON_ERROR=$stopOnError DHRYSTONE=yes COREMARK=${coremarkRegression} THREAD_COUNT=1 ") + s" SEED=${testSeed} "
+ val testCmd = stdCmd + (positionsToApply).map(_.testParam).mkString(" ")
+ println(testCmd)
+ val str = doCmd(testCmd)
+ assert(str.contains("REGRESSION SUCCESS") && !str.contains("Broken pipe"))
+ val pattern = "Had simulate ([0-9]+)".r
+ val hit = pattern.findFirstMatchIn(str)
+
+ lock.synchronized(clockCounter += hit.get.group(1).toLong)
+ }
+ }
+
+ val rand = new Random(seed)
+
+ testMp("Info"){
+ println(s"MAIN_SEED=$seed")
+ }
+ println(s"Seed=$seed")
+ for(i <- 0 until testCount){
+ var positions : List[VexRiscvPosition] = null
+ var universe = mutable.HashSet[VexRiscvUniverse]()
+ if(rand.nextDouble() < 0.5) universe += VexRiscvUniverse.EXECUTE_RF
+ if(linuxRate > rand.nextDouble()) {
+ universe += VexRiscvUniverse.CATCH_ALL
+ universe += VexRiscvUniverse.MMU
+ universe += VexRiscvUniverse.FORCE_MULDIV
+ universe += VexRiscvUniverse.SUPERVISOR
+ if(demwRate < rand.nextDouble()){
+ universe += VexRiscvUniverse.NO_WRITEBACK
+ }
+ } else if (secureRate > rand.nextDouble()) {
+ universe += VexRiscvUniverse.CACHE_ALL
+ universe += VexRiscvUniverse.CATCH_ALL
+ universe += VexRiscvUniverse.PMP
+ if(demwRate < rand.nextDouble()){
+ universe += VexRiscvUniverse.NO_WRITEBACK
+ }
+ } else {
+ if(machineOsRate > rand.nextDouble()) {
+ universe += VexRiscvUniverse.CATCH_ALL
+ if(demwRate < rand.nextDouble()){
+ universe += VexRiscvUniverse.NO_WRITEBACK
+ }
+ }
+ if(demwRate > rand.nextDouble()){
+ }else if(demRate > rand.nextDouble()){
+ universe += VexRiscvUniverse.NO_WRITEBACK
+ } else {
+ universe += VexRiscvUniverse.NO_WRITEBACK
+ universe += VexRiscvUniverse.NO_MEMORY
+ }
+ }
+
+ do{
+ positions = dimensions.map(d => d.randomPosition(universe.toList, rand))
+ }while(!positions.forall(_.isCompatibleWith(positions)))
+
+ val testSeed = rand.nextInt()
+ if(testId.contains(i))
+ doTest(positions,"test_id_" + i + "_", testSeed, universe)
+ Hack.dCounter += 1
+ }
+ testSingleThread("report"){
+ val time = (System.currentTimeMillis() - startAt)*1e-3
+ val clockPerSecond = (clockCounter/time*1e-3).toLong
+ println(s"Duration=${(time/60).toInt}mn clocks=${(clockCounter*1e-6).toLong}M clockPerSecond=${clockPerSecond}K")
+ }
+}
+
+
+object TestIndividualExplore extends App{
+ val seeds = mutable.HashSet[Int]()
+ val futures = mutable.ArrayBuffer[Future[Unit]]()
+ implicit val ec = ExecutionContext.fromExecutorService(
+ new ForkJoinPool(24, ForkJoinPool.defaultForkJoinWorkerThreadFactory, null, true)
+ )
+ for(i <- 0 until 1000){
+ val seed = Random.nextInt(1000000) + 1
+ futures += Future {
+ if (!seeds.contains(seed)) {
+// val cmd = s"make run REGRESSION_PATH=../../src/test/cpp/regression VEXRISCV_FILE=VexRiscv.v WITH_USER_IO=no REDO=1 TRACE=yes TRACE_START=100000000000ll FLOW_INFO=no STOP_ON_ERROR=no DHRYSTONE=yes COREMARK=mo THREAD_COUNT=1 IBUS=CACHED IBUS_DATA_WIDTH=128 COMPRESSED=yes DBUS=SIMPLE LRSC=yes MUL=yes DIV=yes FREERTOS=0 ZEPHYR=0 LINUX_REGRESSION=no SUPERVISOR=yes CONCURRENT_OS_EXECUTIONS=yes MMU=yes PMP=no SEED=$seed"
+ val cmd = s"make run REGRESSION_PATH=../../src/test/cpp/regression VEXRISCV_FILE=VexRiscv.v WITH_USER_IO=no REDO=10 TRACE=yes TRACE_START=100000000000ll FLOW_INFO=no STOP_ON_ERROR=no DHRYSTONE=yes COREMARK=yes THREAD_COUNT=1 IBUS=CACHED IBUS_DATA_WIDTH=128 COMPRESSED=yes DBUS=SIMPLE LRSC=yes MUL=yes DIV=yes FREERTOS=0 ZEPHYR=2 LINUX_REGRESSION=yes SUPERVISOR=yes CONCURRENT_OS_EXECUTIONS=yes MMU=yes PMP=no SEED=$seed"
+ val workspace = s"explor/seed_$seed"
+ FileUtils.copyDirectory(new File("simWorkspace/ref"), new File(workspace))
+ val str = DoCmd.doCmdWithLog(cmd, workspace)
+ if(!str.contains("REGRESSION SUCCESS")){
+ println(s"seed $seed FAILED with\n\n$str")
+ sys.exit(1)
+ }
+ FileUtils.deleteDirectory(new File(workspace))
+ println(s"seed $seed PASSED")
+ }
+ }
+ }
+
+ futures.foreach(Await.result(_, Duration.Inf))
+
+} \ No newline at end of file
diff --git a/VexRiscv/src/test/scala/vexriscv/experimental/Experiments.scala b/VexRiscv/src/test/scala/vexriscv/experimental/Experiments.scala
new file mode 100644
index 0000000..1fead21
--- /dev/null
+++ b/VexRiscv/src/test/scala/vexriscv/experimental/Experiments.scala
@@ -0,0 +1,34 @@
+package vexriscv.experimental
+
+import spinal.core._
+
+class Stageable[T <: Data](val dataType : T) extends HardType[T](dataType) with Nameable{
+ setWeakName(this.getClass.getSimpleName.replace("$",""))
+}
+
+trait Stage{
+ def read[T <: Data](stageable : Stageable[T]) : T
+ def write[T <: Data](stageable : Stageable[T], value : T, cond : Bool = null) : Unit
+
+ def haltBySelf : Bool //user settable, stuck the instruction, should only be set by the instruction itself
+ def haltByOthers : Bool //When settable, stuck the instruction, should only be set by something else than the stucked instruction
+ def removeIt : Bool //When settable, unschedule the instruction as if it was never executed (no side effect)
+ def flushAll : Bool //When settable, unschedule instructions in the current stage and all prior ones
+
+ def isValid : Bool //Inform if a instruction is in the current stage
+ def isStuck : Bool //Inform if the instruction is stuck (haltItself || haltByOther)
+ def isStuckByOthers: Bool //Inform if the instruction is stuck by sombody else
+ def isRemoved : Bool //Inform if the instruction is going to be unschedule the current cycle
+ def isFlushed : Bool //Inform if the instruction is flushed (flushAll set in the current or subsequents stages)
+ def isFiring : Bool //Inform if the current instruction will go to the next stage the next cycle (isValid && !isStuck && !removeIt)
+}
+
+abstract class UnusedStage extends Stage
+abstract class AsyncStage extends Stage
+abstract class CycleStage extends Stage
+abstract class SyncStage extends Stage
+abstract class CutStage extends Stage
+
+abstract class PipelineStd{
+ val prefetch, fetch, decode, execute, memory, writeback = 0
+} \ No newline at end of file
diff --git a/VexRiscv/src/test/scala/vexriscv/experimental/GenMicro.scala b/VexRiscv/src/test/scala/vexriscv/experimental/GenMicro.scala
new file mode 100644
index 0000000..90666a7
--- /dev/null
+++ b/VexRiscv/src/test/scala/vexriscv/experimental/GenMicro.scala
@@ -0,0 +1,162 @@
+package vexriscv.experimental
+
+import spinal.core._
+import spinal.lib.eda.bench.{AlteraStdTargets, Bench, Rtl, XilinxStdTargets}
+import spinal.lib.eda.icestorm.IcestormStdTargets
+import vexriscv.demo.{GenSmallestNoCsr, Murax, MuraxConfig}
+import vexriscv.plugin._
+import vexriscv.{VexRiscv, VexRiscvConfig, plugin}
+
+/**
+ * Created by spinalvm on 15.06.17.
+ */
+object GenMicro extends App{
+ def cpu() = {
+ val removeOneFetchStage = true
+ val pessimisticHazard = true
+ val writeBackOpt = true
+ val rspHoldValue = true
+ val withCompliantCsr = true
+ val withCompliantCsrPlusEmulation = true
+ val earlyBranch = false
+ val noShifter = false
+ val onlyLoadWords = false
+ new VexRiscv(
+ config = VexRiscvConfig(
+ plugins = List(
+ // new PcManagerSimplePlugin(
+ // resetVector = 0x00000000l,
+ // relaxedPcCalculation = false
+ // ),
+
+ new IBusSimplePlugin(
+ resetVector = 0x80000000l,
+ cmdForkOnSecondStage = false,
+ cmdForkPersistence = false,
+ prediction = NONE,
+ catchAccessFault = false,
+ compressedGen = false,
+ injectorStage = !removeOneFetchStage,
+ rspHoldValue = rspHoldValue
+ ),
+ new DBusSimplePlugin(
+ catchAddressMisaligned = withCompliantCsr,
+ catchAccessFault = false,
+ earlyInjection = writeBackOpt,
+ onlyLoadWords = onlyLoadWords
+ ),
+ new DecoderSimplePlugin(
+ catchIllegalInstruction = withCompliantCsrPlusEmulation
+ ),
+ new RegFilePlugin(
+ regFileReadyKind = plugin.SYNC,
+ zeroBoot = false,
+ readInExecute = removeOneFetchStage,
+ writeRfInMemoryStage = writeBackOpt
+ ),
+ new IntAluPlugin,
+ new SrcPlugin(
+ separatedAddSub = false,
+ executeInsertion = removeOneFetchStage
+ ),
+ if(!pessimisticHazard)
+ new HazardSimplePlugin(
+ bypassExecute = false,
+ bypassMemory = false,
+ bypassWriteBack = false,
+ bypassWriteBackBuffer = false,
+ pessimisticUseSrc = false,
+ pessimisticWriteRegFile = false,
+ pessimisticAddressMatch = false
+ )
+ else
+ new HazardPessimisticPlugin(),
+ new BranchPlugin(
+ earlyBranch = earlyBranch,
+ catchAddressMisaligned = withCompliantCsr,
+ fenceiGenAsAJump = withCompliantCsr
+ ),
+ new YamlPlugin("cpu0.yaml")
+ ) ++ (if(noShifter) Nil else List(new LightShifterPlugin))
+ ++ (if(!withCompliantCsr) Nil else List(new CsrPlugin(
+ config = if(withCompliantCsrPlusEmulation)CsrPluginConfig(
+ catchIllegalAccess = true,
+ mvendorid = null,
+ marchid = null,
+ mimpid = null,
+ mhartid = null,
+ misaExtensionsInit = 0,
+ misaAccess = CsrAccess.NONE,
+ mtvecAccess = CsrAccess.NONE,
+ mtvecInit = 0x80000020l,
+ mepcAccess = CsrAccess.NONE,
+ mscratchGen = false,
+ mcauseAccess = CsrAccess.READ_ONLY,
+ mbadaddrAccess = CsrAccess.NONE,
+ mcycleAccess = CsrAccess.NONE,
+ minstretAccess = CsrAccess.NONE,
+ ecallGen = false,
+ ebreakGen = false,
+ wfiGenAsWait = false,
+ wfiGenAsNop = false,
+ ucycleAccess = CsrAccess.NONE,
+ noCsrAlu = true
+ ) else CsrPluginConfig(
+ catchIllegalAccess = false,
+ mvendorid = null,
+ marchid = null,
+ mimpid = null,
+ mhartid = null,
+ misaExtensionsInit = 0,
+ misaAccess = CsrAccess.READ_ONLY,
+ mtvecAccess = CsrAccess.WRITE_ONLY,
+ mtvecInit = 0x80000020l,
+ mepcAccess = CsrAccess.READ_WRITE,
+ mscratchGen = true,
+ mcauseAccess = CsrAccess.READ_ONLY,
+ mbadaddrAccess = CsrAccess.READ_ONLY,
+ mcycleAccess = CsrAccess.NONE,
+ minstretAccess = CsrAccess.NONE,
+ ecallGen = true,
+ ebreakGen = true,
+ wfiGenAsWait = false,
+ wfiGenAsNop = true,
+ ucycleAccess = CsrAccess.NONE
+ )
+ )))
+ )
+ )
+ }
+ SpinalConfig(mergeAsyncProcess = false).generateVerilog(cpu())
+}
+
+
+
+object GenMicroSynthesis {
+ def main(args: Array[String]) {
+ val microNoCsr = new Rtl {
+ override def getName(): String = "MicroNoCsr"
+ override def getRtlPath(): String = "MicroNoCsr.v"
+ SpinalVerilog(GenMicro.cpu().setDefinitionName(getRtlPath().split("\\.").head))
+ }
+
+ val smallestNoCsr = new Rtl {
+ override def getName(): String = "SmallestNoCsr"
+ override def getRtlPath(): String = "SmallestNoCsr.v"
+ SpinalVerilog(GenSmallestNoCsr.cpu().setDefinitionName(getRtlPath().split("\\.").head))
+ }
+
+ val rtls = List(microNoCsr)
+// val rtls = List(smallestNoCsr)
+
+ val targets = IcestormStdTargets().take(1) ++ XilinxStdTargets(
+ vivadoArtix7Path = "/eda/Xilinx/Vivado/2017.2/bin"
+ ) ++ AlteraStdTargets(
+ quartusCycloneIVPath = "/eda/intelFPGA_lite/17.0/quartus/bin/",
+ quartusCycloneVPath = "/eda/intelFPGA_lite/17.0/quartus/bin/"
+ )
+
+
+ Bench(rtls, targets, "/eda/tmp/")
+ }
+} \ No newline at end of file
diff --git a/VexRiscv/src/test/scala/vexriscv/experimental/PlicCost.scala b/VexRiscv/src/test/scala/vexriscv/experimental/PlicCost.scala
new file mode 100644
index 0000000..79d5c66
--- /dev/null
+++ b/VexRiscv/src/test/scala/vexriscv/experimental/PlicCost.scala
@@ -0,0 +1,76 @@
+package vexriscv.experimental
+
+import spinal.core._
+import spinal.lib._
+import spinal.lib.bus.amba3.apb._
+import spinal.lib.eda.bench.{Bench, Rtl, XilinxStdTargets}
+import spinal.lib.eda.icestorm.IcestormStdTargets
+import spinal.lib.misc.plic._
+import vexriscv.VexRiscv
+import vexriscv.demo.LinuxGen
+
+import scala.collection.mutable.ArrayBuffer
+
+class PlicBench(inputCount : Int) extends Component{
+ val io = new Bundle {
+ val apb = slave(Apb3(addressWidth = 16, dataWidth = 32))
+ val interrupts = in Bits(inputCount bits)
+ val cpuInterrupt = out Bool()
+ }
+
+
+ val priorityWidth = 1
+ val gateways = ArrayBuffer[PlicGateway]()
+
+ for(i <- 0 until inputCount) {
+ gateways += PlicGatewayActiveHigh(
+ source = io.interrupts(i),
+ id = 1 + i,
+ priorityWidth = priorityWidth
+ )
+ }
+
+
+ val targets = Seq(
+ PlicTarget(
+ gateways = gateways,
+ priorityWidth = priorityWidth
+ )
+ )
+ io.cpuInterrupt := targets(0).iep
+
+ val plicMapping = PlicMapping.light.copy(
+// gatewayPriorityReadGen = true,
+// gatewayPendingReadGen = true,
+// targetThresholdReadGen = true
+ )
+
+ gateways.foreach(_.priority := 1)
+ targets.foreach(_.threshold := 0)
+ // targets.foreach(_.ie.foreach(_ := True))
+
+ val bus = Apb3SlaveFactory(io.apb)
+ val mapping = PlicMapper(bus, plicMapping)(
+ gateways = gateways,
+ targets = targets
+ )
+}
+
+
+object PlicCost extends App{
+ def rtlGen(inputCount : Int) = new Rtl {
+ override def getName(): String = s"PlicBench$inputCount"
+ override def getRtlPath(): String = s"PlicBench$inputCount.v"
+ SpinalVerilog(new PlicBench(inputCount).setDefinitionName(getRtlPath().split("\\.").head))
+ }
+
+ val rtls = List(8, 12, 16, 32).map(rtlGen)
+ // val rtls = List(smallestNoCsr, smallest, smallAndProductive, smallAndProductiveWithICache)
+ // val rtls = List(smallAndProductive, smallAndProductiveWithICache, fullNoMmuMaxPerf, fullNoMmu, full)
+ // val rtls = List(fullNoMmu)
+
+ val targets = IcestormStdTargets().take(1)
+
+
+ Bench(rtls, targets, "/eda/tmp")
+}
diff --git a/VexRiscv/src/test/scala/vexriscv/experimental/config.scala b/VexRiscv/src/test/scala/vexriscv/experimental/config.scala
new file mode 100644
index 0000000..d6eca55
--- /dev/null
+++ b/VexRiscv/src/test/scala/vexriscv/experimental/config.scala
@@ -0,0 +1,36 @@
+package vexriscv.experimental
+
+import spinal.core.SpinalVerilog
+import vexriscv.ip.InstructionCacheConfig
+import vexriscv.{VexRiscv, VexRiscvConfig, plugin}
+import vexriscv.plugin._
+
+import scala.collection.mutable.ArrayBuffer
+
+object Presentation extends App{
+
+ val config = VexRiscvConfig()
+
+ config.plugins ++= List(
+// new IBusSimplePlugin(resetVector = 0x80000000l),
+ new DBusSimplePlugin,
+ new CsrPlugin(CsrPluginConfig.smallest),
+ new DecoderSimplePlugin,
+ new RegFilePlugin(regFileReadyKind = plugin.SYNC),
+ new IntAluPlugin,
+ new SrcPlugin,
+ new MulDivIterativePlugin(
+ mulUnrollFactor = 4,
+ divUnrollFactor = 1
+ ),
+ new FullBarrelShifterPlugin,
+ new HazardSimplePlugin,
+ new BranchPlugin(
+ earlyBranch = false
+ ),
+ new YamlPlugin("cpu0.yaml")
+ )
+
+ new VexRiscv(config)
+}
+
diff --git a/VexRiscv/src/test/scala/vexriscv/ip/fpu/FpuTest.scala b/VexRiscv/src/test/scala/vexriscv/ip/fpu/FpuTest.scala
new file mode 100644
index 0000000..11182ac
--- /dev/null
+++ b/VexRiscv/src/test/scala/vexriscv/ip/fpu/FpuTest.scala
@@ -0,0 +1,1663 @@
+package vexriscv.ip.fpu
+
+import java.io.File
+import java.lang
+import java.util.Scanner
+
+import org.apache.commons.io.FileUtils
+import org.scalatest.funsuite.AnyFunSuite
+import spinal.core.SpinalEnumElement
+import spinal.core.sim._
+import spinal.core._
+import spinal.lib.DoCmd
+import spinal.lib.experimental.math.Floating
+import spinal.lib.sim._
+import spinal.sim.Backend.{isMac, isWindows}
+
+import scala.collection.mutable
+import scala.collection.mutable.ArrayBuffer
+import scala.sys.process.ProcessLogger
+import scala.util.Random
+import org.scalatest.funsuite.AnyFunSuite
+
+//TODO Warning DataCache write aggregation will disable itself
+class FpuTest extends AnyFunSuite{
+
+ val b2f = lang.Float.intBitsToFloat(_)
+ val b2d = lang.Double.longBitsToDouble(_)
+ val f2b = lang.Float.floatToRawIntBits(_)
+ val d2bOffset = BigInt("10000000000000000",16)
+ def d2b(that : Double) = {
+ val l = lang.Double.doubleToRawLongBits(that)
+ var a = BigInt(l)
+ if(l < 0) {
+ a = d2bOffset + a
+ }
+ a
+ }
+
+
+ test("f32f64") {
+ val p = FpuParameter(
+ withDouble = true,
+// withAdd = false,
+// withMul = false,
+// withDivSqrt = false,
+ sim = true
+ )
+ testP(p)
+ }
+ test("f32") {
+ val p = FpuParameter(
+ withDouble = false,
+ sim = true
+ )
+ testP(p)
+ }
+
+ def testP(p : FpuParameter){
+ val portCount = 4
+
+ val config = SimConfig
+ config.allOptimisation
+// config.withFstWave
+ config.compile(new FpuCore(portCount, p){
+ for(i <- 0 until portCount) out(Bits(5 bits)).setName(s"flagAcc$i") := io.port(i).completion.flags.asBits
+ setDefinitionName("FpuCore"+ (if(p.withDouble) "Double" else ""))
+ }).doSim(seed = 42){ dut =>
+ dut.clockDomain.forkStimulus(10)
+ dut.clockDomain.forkSimSpeedPrinter(5.0)
+
+
+ class TestCase(op : String){
+ def build(arg : String) = new ProcessStream(s"testfloat_gen $arg -tininessafter -forever -$op"){
+ def f32_f32_f32 ={
+ val s = new Scanner(next)
+ val a,b,c = (s.nextLong(16).toInt)
+ (b2f(a), b2f(b), b2f(c), s.nextInt(16))
+ }
+
+ def i32_f32 ={
+ val s = new Scanner(next)
+ (s.nextLong(16).toInt, b2f(s.nextLong(16).toInt), s.nextInt(16))
+ }
+
+ def f32_i32 = {
+ val s = new Scanner(next)
+ (b2f(s.nextLong(16).toInt), s.nextLong(16).toInt, s.nextInt(16))
+ }
+
+ def f32_f32_i32 = {
+ val s = new Scanner(next)
+ val a,b,c = (s.nextLong(16).toInt)
+ (b2f(a), b2f(b), c, s.nextInt(16))
+ }
+
+ def f32_f32 = {
+ val s = new Scanner(next)
+ val a,b = (s.nextLong(16).toInt)
+ (b2f(a), b2f(b), s.nextInt(16))
+ }
+
+
+ def nextLong(s : Scanner) : Long = java.lang.Long.parseUnsignedLong( s.next(),16)
+
+ def f64_f64_f64 ={
+ val s = new Scanner(next)
+ val a,b,c = nextLong(s)
+ (b2d(a), b2d(b), b2d(c), s.nextInt(16))
+ }
+
+ def i32_f64 ={
+ val s = new Scanner(next)
+ (s.nextLong(16).toInt, b2d(nextLong(s)), s.nextInt(16))
+ }
+
+ def f64_i32 = {
+ val s = new Scanner(next)
+ (b2d(nextLong(s)), s.nextLong(16).toInt, s.nextInt(16))
+ }
+
+ def f64_f64_i32 = {
+ val str = next
+ val s = new Scanner(str)
+ val a,b = (nextLong(s))
+ (b2d(a), b2d(b), s.nextInt(16), s.nextInt(16))
+ }
+
+ def f64_f64 = {
+ val s = new Scanner(next)
+ val a,b = nextLong(s)
+ (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")
+ lazy val RTZ = build("-rminMag")
+ lazy val RDN = build("-rmin")
+ lazy val RUP = build("-rmax")
+ lazy val RMM = build("-rnear_maxMag")
+ lazy val all = List(RNE, RTZ, RDN, RUP, RMM, RAW)
+ def kill = all.foreach(_.kill)
+ def apply(rounding : FpuRoundMode.E) = rounding match {
+ case FpuRoundMode.RNE => RNE
+ case FpuRoundMode.RTZ => RTZ
+ case FpuRoundMode.RDN => RDN
+ case FpuRoundMode.RUP => RUP
+ case FpuRoundMode.RMM => RMM
+ }
+ }
+
+ class TestVector(f : String) {
+ val add = new TestCase(s"${f}_add")
+ val sub = new TestCase(s"${f}_sub")
+ val mul = new TestCase(s"${f}_mul")
+ val ui2f = new TestCase(s"ui32_to_${f}")
+ val i2f = new TestCase(s"i32_to_${f}")
+ val f2ui = new TestCase(s"${f}_to_ui32 -exact")
+ val f2i = new TestCase(s"${f}_to_i32 -exact")
+ val eq = new TestCase(s"${f}_eq")
+ val lt = new TestCase(s"${f}_lt")
+ val le = new TestCase(s"${f}_le")
+ val min = new TestCase(s"${f}_le")
+ val max = new TestCase(s"${f}_lt")
+ val transfer = new TestCase(s"${f}_eq")
+ val fclass = new TestCase(s"${f}_eq")
+ val sgnj = new TestCase(s"${f}_eq")
+ val sgnjn = new TestCase(s"${f}_eq")
+ val sgnjx = new TestCase(s"${f}_eq")
+ val sqrt = new TestCase(s"${f}_sqrt")
+ val div = new TestCase(s"${f}_div")
+ }
+
+ 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]()
+ val commitQueue = mutable.Queue[FpuCommit => Unit]()
+ val rspQueue = mutable.Queue[FpuRsp => Unit]()
+
+ var pendingMiaou = 0
+ var flagAccumulator = 0
+
+ def cmdAdd(body : FpuCmd => Unit): Unit ={
+ pendingMiaou += 1
+ cmdQueue += body
+ }
+
+ def softAssert(cond : Boolean, msg : String) = if(!cond)println(msg)
+ def flagMatch(ref : Int, value : Float, report : String): Unit ={
+ val patch = if(value.abs == 1.17549435E-38f && false) 0x1f & ~2 else 0x1f
+ flagMatch(ref, report, patch)
+ }
+
+ def flagMatch(ref : Int, value : Double, report : String): Unit ={
+ val patch = if(value.abs == b2d(1 << 52) && false) 0x1f & ~2 else 0x1f
+ flagMatch(ref, report, patch)
+ }
+
+ def flagMatch(ref : Int, report : String, mask : Int = 0x1f): Unit ={
+ waitUntil(pendingMiaou == 0)
+ assert((flagAccumulator & mask) == (ref & mask), s"Flag missmatch dut=$flagAccumulator ref=$ref $report")
+ flagAccumulator = 0
+ }
+ def flagClear(): Unit ={
+ waitUntil(pendingMiaou == 0)
+ flagAccumulator = 0
+ }
+
+ val flagAggregated = dut.reflectBaseType(s"flagAcc$id").asInstanceOf[Bits]
+ dut.clockDomain.onSamplings{
+ val c = dut.io.port(id).completion
+ if(c.valid.toBoolean) {
+ pendingMiaou -= 1
+ flagAccumulator |= flagAggregated.toInt
+ }
+ dut.writeback.randomSim.randomize()
+ }
+
+ StreamDriver(dut.io.port(id).cmd ,dut.clockDomain){payload =>
+ if(cmdQueue.isEmpty) false else {
+ cmdQueue.dequeue().apply(payload)
+ true
+ }
+ }
+
+
+ StreamMonitor(dut.io.port(id)rsp, dut.clockDomain){payload =>
+ pendingMiaou -= 1
+ if(payload.NV.toBoolean) flagAccumulator |= 1 << 4
+ if(payload.NX.toBoolean) flagAccumulator |= 1 << 0
+ rspQueue.dequeue().apply(payload)
+ }
+
+ StreamReadyRandomizer(dut.io.port(id).rsp, dut.clockDomain)
+
+
+ StreamDriver(dut.io.port(id).commit ,dut.clockDomain){payload =>
+ if(commitQueue.isEmpty) false else {
+ commitQueue.dequeue().apply(payload)
+ true
+ }
+ }
+
+
+
+
+ def loadRaw(rd : Int, value : BigInt, format : FpuFormat.E): Unit ={
+ cmdAdd {cmd =>
+ cmd.opcode #= cmd.opcode.spinalEnum.LOAD
+ cmd.rs1.randomize()
+ cmd.rs2.randomize()
+ cmd.rs3.randomize()
+ cmd.rd #= rd
+ cmd.arg.randomize()
+ cmd.roundMode.randomize()
+ cmd.format #= format
+ }
+ commitQueue += {cmd =>
+ cmd.write #= true
+ cmd.rd #= rd
+ cmd.value #= value
+ cmd.opcode #= cmd.opcode.spinalEnum.LOAD
+ }
+ }
+
+
+ def load(rd : Int, value : Float): Unit ={
+ loadRaw(rd, f2b(value).toLong & 0xFFFFFFFFl, FpuFormat.FLOAT)
+ }
+
+ def load(rd : Int, value : Double): Unit ={
+ loadRaw(rd, d2b(value), FpuFormat.DOUBLE)
+ }
+
+ def storeRaw(rs : Int, format : FpuFormat.E)(body : FpuRsp => Unit): Unit ={
+ cmdAdd {cmd =>
+ cmd.opcode #= cmd.opcode.spinalEnum.STORE
+ cmd.rs1.randomize()
+ cmd.rs2 #= rs
+ cmd.rs3.randomize()
+ cmd.rd.randomize()
+ cmd.arg.randomize()
+ cmd.roundMode.randomize()
+ cmd.format #= format
+ }
+
+ rspQueue += body
+// waitUntil(rspQueue.isEmpty)
+ }
+
+ def storeFloat(rs : Int)(body : Float => Unit): Unit ={
+ storeRaw(rs, FpuFormat.FLOAT){rsp => body(b2f(rsp.value.toBigInt.toInt))}
+ }
+ def store(rs : Int)(body : Double => Unit): Unit ={
+ storeRaw(rs, FpuFormat.DOUBLE){rsp => body(b2d(rsp.value.toBigInt.toLong))}
+ }
+
+ def fpuF2f(rd : Int, rs1 : Int, rs2 : Int, rs3 : Int, opcode : FpuOpcode.E, arg : Int, rounding : FpuRoundMode.E, format : FpuFormat.E): Unit ={
+ cmdAdd {cmd =>
+ cmd.opcode #= opcode
+ cmd.rs1 #= rs1
+ cmd.rs2 #= rs2
+ cmd.rs3 #= rs3
+ cmd.rd #= rd
+ cmd.arg #= arg
+ cmd.roundMode #= rounding
+ cmd.format #= format
+ }
+ commitQueue += {cmd =>
+ cmd.write #= true
+ cmd.rd #= rd
+ cmd.opcode #= opcode
+ }
+ }
+
+ def fpuF2i(rs1 : Int, rs2 : Int, opcode : FpuOpcode.E, arg : Int, rounding : FpuRoundMode.E, format : FpuFormat.E)(body : FpuRsp => Unit): Unit ={
+ cmdAdd {cmd =>
+ cmd.opcode #= opcode
+ cmd.rs1 #= rs1
+ cmd.rs2 #= rs2
+ cmd.rs3.randomize()
+ cmd.rd.randomize()
+ cmd.arg #= arg
+ cmd.roundMode #= rounding
+ cmd.format #= format
+ }
+ rspQueue += body
+ }
+
+
+ def mul(rd : Int, rs1 : Int, rs2 : Int, rounding : FpuRoundMode.E, format : FpuFormat.E): Unit ={
+ fpuF2f(rd, rs1, rs2, Random.nextInt(32), FpuOpcode.MUL, 0, rounding, format)
+ }
+
+ def add(rd : Int, rs1 : Int, rs2 : Int, rounding : FpuRoundMode.E = FpuRoundMode.RNE, format : FpuFormat.E): Unit ={
+ fpuF2f(rd, rs1, rs2, Random.nextInt(32), FpuOpcode.ADD, 0, rounding, format)
+ }
+
+ def sub(rd : Int, rs1 : Int, rs2 : Int, rounding : FpuRoundMode.E = FpuRoundMode.RNE, format : FpuFormat.E): Unit ={
+ fpuF2f(rd, rs1, rs2, Random.nextInt(32), FpuOpcode.ADD, 1, rounding, format)
+ }
+
+ def div(rd : Int, rs1 : Int, rs2 : Int, rounding : FpuRoundMode.E = FpuRoundMode.RNE, format : FpuFormat.E): Unit ={
+ fpuF2f(rd, rs1, rs2, Random.nextInt(32), FpuOpcode.DIV, Random.nextInt(4), rounding, format)
+ }
+
+ def sqrt(rd : Int, rs1 : Int, rounding : FpuRoundMode.E = FpuRoundMode.RNE, format : FpuFormat.E): Unit ={
+ fpuF2f(rd, rs1, Random.nextInt(32), Random.nextInt(32), FpuOpcode.SQRT, Random.nextInt(4), rounding, format)
+ }
+
+ def fma(rd : Int, rs1 : Int, rs2 : Int, rs3 : Int, rounding : FpuRoundMode.E, format : FpuFormat.E): Unit ={
+ fpuF2f(rd, rs1, rs2, rs3, FpuOpcode.FMA, 0, rounding, format)
+ }
+
+ def sgnjRaw(rd : Int, rs1 : Int, rs2 : Int, arg : Int, format : FpuFormat.E): Unit ={
+ fpuF2f(rd, rs1, rs2, Random.nextInt(32), FpuOpcode.SGNJ, arg, FpuRoundMode.elements.randomPick(), format)
+ }
+
+ def sgnj(rd : Int, rs1 : Int, rs2 : Int, rounding : FpuRoundMode.E = null, format : FpuFormat.E): Unit ={
+ sgnjRaw(rd, rs1, rs2, 0, format)
+ }
+ def sgnjn(rd : Int, rs1 : Int, rs2 : Int, rounding : FpuRoundMode.E = null, format : FpuFormat.E): Unit ={
+ sgnjRaw(rd, rs1, rs2, 1, format)
+ }
+ def sgnjx(rd : Int, rs1 : Int, rs2 : Int, rounding : FpuRoundMode.E = null, format : FpuFormat.E): Unit ={
+ sgnjRaw(rd, rs1, rs2, 2, format)
+ }
+
+ def cmp(rs1 : Int, rs2 : Int, arg : Int, format : FpuFormat.E)(body : FpuRsp => Unit): Unit ={
+ fpuF2i(rs1, rs2, FpuOpcode.CMP, arg, FpuRoundMode.elements.randomPick(), format)(body)
+ }
+
+ def f2i(rs1 : Int, signed : Boolean, rounding : FpuRoundMode.E, format : FpuFormat.E)(body : FpuRsp => Unit): Unit ={
+ fpuF2i(rs1, Random.nextInt(32), FpuOpcode.F2I, if(signed) 1 else 0, rounding, format)(body)
+ }
+
+ def i2f(rd : Int, value : Int, signed : Boolean, rounding : FpuRoundMode.E, format : FpuFormat.E): Unit ={
+ cmdAdd {cmd =>
+ cmd.opcode #= cmd.opcode.spinalEnum.I2F
+ cmd.rs1.randomize()
+ cmd.rs2.randomize()
+ cmd.rs3.randomize()
+ cmd.rd #= rd
+ cmd.arg #= (if(signed) 1 else 0)
+ cmd.roundMode #= rounding
+ cmd.format #= format
+ }
+ commitQueue += {cmd =>
+ cmd.write #= true
+ cmd.rd #= rd
+ cmd.opcode #= FpuOpcode.I2F
+ cmd.value #= value.toLong & 0xFFFFFFFFl
+ }
+ }
+
+ def fmv_x_w(rs1 : Int)(body : Float => Unit): Unit ={
+ cmdAdd {cmd =>
+ cmd.opcode #= cmd.opcode.spinalEnum.FMV_X_W
+ cmd.rs1 #= rs1
+ cmd.rs2.randomize()
+ cmd.rs3.randomize()
+ cmd.rd.randomize()
+ cmd.arg #= 0
+ cmd.roundMode.randomize()
+ cmd.format #= FpuFormat.FLOAT
+ }
+ rspQueue += {rsp => body(b2f(rsp.value.toBigInt.toInt))}
+ }
+
+ def fmv_w_x(rd : Int, value : Int): Unit ={
+ cmdAdd {cmd =>
+ cmd.opcode #= cmd.opcode.spinalEnum.FMV_W_X
+ cmd.rs1.randomize()
+ cmd.rs2.randomize()
+ cmd.rs3.randomize()
+ cmd.rd #= rd
+ cmd.arg #= 0
+ cmd.roundMode.randomize()
+ cmd.format #= FpuFormat.FLOAT
+ }
+ commitQueue += {cmd =>
+ cmd.write #= true
+ cmd.rd #= rd
+ cmd.opcode #= FpuOpcode.FMV_W_X
+ cmd.value #= value.toLong & 0xFFFFFFFFl
+ }
+ }
+
+ def minMax(rd : Int, rs1 : Int, rs2 : Int, arg : Int, format : FpuFormat.E): Unit ={
+ cmdAdd {cmd =>
+ cmd.opcode #= cmd.opcode.spinalEnum.MIN_MAX
+ cmd.rs1 #= rs1
+ cmd.rs2 #= rs2
+ cmd.rs3.randomize()
+ cmd.rd #= rd
+ cmd.arg #= arg
+ cmd.roundMode.randomize()
+ cmd.format #= format
+ }
+ commitQueue += {cmd =>
+ cmd.write #= true
+ cmd.rd #= rd
+ cmd.opcode #= FpuOpcode.MIN_MAX
+ }
+ }
+
+
+
+ def fclass(rs1 : Int, format : FpuFormat.E)(body : Int => Unit) : Unit = {
+ cmdAdd {cmd =>
+ cmd.opcode #= FpuOpcode.FCLASS
+ cmd.rs1 #= rs1
+ cmd.rs2.randomize()
+ cmd.rs3.randomize()
+ cmd.rd.randomize()
+ cmd.arg.randomize()
+ cmd.roundMode.randomize()
+ cmd.format #= format
+ }
+ rspQueue += {rsp => body(rsp.value.toBigInt.toInt)}
+ }
+ }
+
+
+
+
+
+ val stim = for(cpu <- cpus) yield fork {
+ import cpu._
+
+ class RegAllocator(){
+ var value = 0
+
+ def allocate(): Int ={
+ while(true){
+ val rand = Random.nextInt(32)
+ val mask = 1 << rand
+ if((value & mask) == 0) {
+ value |= mask
+ return rand
+ }
+ }
+ 0
+ }
+ }
+ def checkFloat(ref : Float, dut : Float): Boolean ={
+ if((f2b(ref) & 0x80000000) != (f2b(dut) & 0x80000000)) return false
+ if(ref == 0.0 && dut == 0.0 && f2b(ref) != f2b(dut)) return false
+ if(ref.isNaN && dut.isNaN) return true
+ if(ref == dut) return true
+ if(ref.abs * 1.0001f + Float.MinPositiveValue >= dut.abs*0.9999f && ref.abs * 0.9999f - Float.MinPositiveValue <= dut.abs*1.0001f) return true
+// if(ref + Float.MinPositiveValue*2.0f === dut || dut + Float.MinPositiveValue*2.0f === ref)
+ false
+ }
+
+ def checkDouble(ref : Double, dut : Double): Boolean ={
+ if((d2b(ref) & Long.MinValue) != (d2b(dut) & Long.MinValue)) return false
+ if(ref == 0.0 && dut == 0.0 && d2b(ref) != d2b(dut)) return false
+ if(ref.isNaN && dut.isNaN) return true
+ if(ref == dut) return true
+ if(ref.abs * 1.0001 + Float.MinPositiveValue >= dut.abs*0.9999 && ref.abs * 0.9999 - Double.MinPositiveValue <= dut.abs*1.0001) return true
+ // if(ref + Float.MinPositiveValue*2.0f === dut || dut + Float.MinPositiveValue*2.0f === ref)
+ false
+ }
+ def checkFloatExact(ref : Float, dut : Float): Boolean ={
+ if(ref.signum != dut.signum === dut) return false
+ if(ref.isNaN && dut.isNaN) return true
+ if(ref == dut) return true
+ false
+ }
+
+
+ def randomFloat(): Float ={
+ val exp = Random.nextInt(10)-5
+ (Random.nextDouble() * (Math.pow(2.0, exp)) * (if(Random.nextBoolean()) -1.0 else 1.0)).toFloat
+ }
+
+ def randomDouble(): Double ={
+ val exp = Random.nextInt(10)-5
+ (Random.nextDouble() * (Math.pow(2.0, exp)) * (if(Random.nextBoolean()) -1.0 else 1.0))
+ }
+
+
+ def testBinaryOp(op : (Int,Int,Int,FpuRoundMode.E, FpuFormat.E) => Unit, a : Float, b : Float, ref : Float, flag : Int, rounding : FpuRoundMode.E, opName : String): Unit ={
+ val rs = new RegAllocator()
+ val rs1, rs2, rs3 = rs.allocate()
+ val rd = Random.nextInt(32)
+ load(rs1, a)
+ load(rs2, b)
+ op(rd,rs1,rs2, rounding, FpuFormat.FLOAT)
+ storeFloat(rd){v =>
+ assert(f2b(v) == f2b(ref), f"## ${a} ${opName} $b = $v, $ref $rounding")
+ }
+
+ flagMatch(flag, ref, f"## ${opName} ${a} $b $ref $rounding")
+ }
+
+
+ def testBinaryOpF64(op : (Int,Int,Int,FpuRoundMode.E, FpuFormat.E) => Unit, a : Double, b : Double, ref : Double, flag : Int, rounding : FpuRoundMode.E, opName : String): Unit ={
+ val rs = new RegAllocator()
+ val rs1, rs2, rs3 = rs.allocate()
+ val rd = Random.nextInt(32)
+ load(rs1, a)
+ load(rs2, b)
+ op(rd,rs1,rs2, rounding, FpuFormat.DOUBLE)
+ store(rd){v =>
+ assert(d2b(v) == d2b(ref), f"## ${a} ${opName} $b = $v, $ref $rounding, ${d2b(a).toString(16)} ${d2b(b).toString(16)} ${d2b(ref).toString(16)}")
+ }
+
+ flagMatch(flag, ref, f"## ${opName} ${a} $b $ref $rounding")
+ }
+
+
+ def testTransferF32Raw(a : Float, iSrc : Boolean, iDst : Boolean): Unit ={
+ val rd = Random.nextInt(32)
+
+ def handle(v : Float): Unit ={
+ val ref = a
+ 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 testTransferF64Raw(a : Double): Unit ={
+ val rd = Random.nextInt(32)
+
+ def handle(v : Double): Unit ={
+ val ref = a
+ assert(d2b(v) == d2b(ref), f"$a = $v, $ref")
+ }
+
+ load(rd, a)
+ store(rd)(handle)
+
+ flagMatch(0, f"$a")
+ }
+
+ def testTransferF32F64Raw(a : Float, iSrc : Boolean): Unit ={
+ val rd = Random.nextInt(32)
+ if(iSrc) fmv_w_x(rd, f2b(a)) else load(rd, a)
+ storeRaw(rd, FpuFormat.DOUBLE){rsp =>
+ val v = rsp.value.toBigInt.toLong
+ val ref = (0xFFFFFFFFl << 32) | f2b(a)
+ assert(v == ref, f"$a = $v, $ref")
+ }
+ flagMatch(0, f"$a")
+ }
+
+ def testTransferF64F32Raw(a : Double, iDst : Boolean): Unit ={
+ val rd = Random.nextInt(32)
+ load(rd, a)
+ if(iDst)fmv_x_w(rd){v_ =>
+ val v = f2b(v_).toLong & 0xFFFFFFFFl
+ val ref = d2b(a) & 0xFFFFFFFFl
+ assert(v == ref, f"$a = $v, $ref")
+ }
+ else storeRaw(rd, FpuFormat.FLOAT){rsp =>
+ val v = rsp.value.toBigInt.toLong & 0xFFFFFFFFl
+ val ref = d2b(a) & 0xFFFFFFFFl
+ assert(v == ref, f"$a = $v, $ref")
+ }
+ flagMatch(0, f"$a")
+ }
+
+
+ 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,ref, 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, ref, f"testCvtF64F32Raw $a $ref $rounding")
+ }
+
+
+ def testClassRaw(a : Float) : Unit = {
+ val rd = Random.nextInt(32)
+
+
+ load(rd, a)
+ fclass(rd, FpuFormat.FLOAT){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 testClassF64Raw(a : Double) : Unit = {
+ val rd = Random.nextInt(32)
+
+
+ load(rd, a)
+ fclass(rd, FpuFormat.DOUBLE){v =>
+ val mantissa = d2b(a) & 0x000FFFFFFFFFFFFFl
+ val exp = (d2b(a) >> 52) & 0x7FF
+ val sign = (d2b(a) >> 63) & 0x1
+
+ val refBit = if(a.isInfinite) (if(sign == 0) 7 else 0)
+ else if(a.isNaN) (if((mantissa >> 51) != 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 testFmaRaw(a : Float, b : Float, c : Float): Unit ={
+ val rs = new RegAllocator()
+ val rs1, rs2, rs3 = rs.allocate()
+ val rd = Random.nextInt(32)
+ load(rs1, a)
+ load(rs2, b)
+ load(rs3, c)
+
+ fma(rd,rs1,rs2,rs3, FpuRoundMode.RNE, FpuFormat.FLOAT)
+ storeFloat(rd){v =>
+ val ref = a.toDouble * b.toDouble + c.toDouble
+ val mul = a.toDouble * b.toDouble
+ 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 testFmaF64Raw(a : Double, b : Double, c : Double): Unit ={
+ val rs = new RegAllocator()
+ val rs1, rs2, rs3 = rs.allocate()
+ val rd = Random.nextInt(32)
+ load(rs1, a)
+ load(rs2, b)
+ load(rs3, c)
+
+ fma(rd,rs1,rs2,rs3, FpuRoundMode.RNE, FpuFormat.DOUBLE)
+ store(rd){v =>
+ val ref = a.toDouble * b.toDouble + c.toDouble
+ val mul = a.toDouble * b.toDouble
+ if((mul.abs-c.abs)/mul.abs > 0.1) assert(checkDouble(ref, v), f"$a%.20f * $b%.20f + $c%.20f = $v%.20f, $ref%.20f")
+ }
+ }
+
+ def testSqrtF64Exact(a : Double, ref : Double, flag : Int, rounding : FpuRoundMode.E): Unit ={
+ val rs = new RegAllocator()
+ val rs1, rs2, rs3 = rs.allocate()
+ val rd = Random.nextInt(32)
+ load(rs1, a)
+
+ sqrt(rd,rs1, rounding, FpuFormat.DOUBLE)
+
+ store(rd){v =>
+ assert(d2b(v) == d2b(ref), f"## sqrt${a} = $v, $ref $rounding, ${d2b(a).toString(16)} ${d2b(ref).toString(16)}")
+ }
+
+ flagMatch(flag, ref, f"## sqrt${a} $ref $rounding")
+ }
+
+ def testSqrtExact(a : Float, ref : Float, flag : Int, rounding : FpuRoundMode.E): Unit ={
+ val rs = new RegAllocator()
+ val rs1, rs2, rs3 = rs.allocate()
+ val rd = Random.nextInt(32)
+ load(rs1, a)
+
+ sqrt(rd,rs1, rounding, FpuFormat.FLOAT)
+
+ storeFloat(rd){v =>
+ assert(d2b(v) == d2b(ref), f"## sqrt${a} = $v, $ref $rounding, ${f2b(a).toString()} ${f2b(ref).toString()}")
+ }
+
+ flagMatch(flag, ref, f"## sqrt${a} $ref $rounding")
+ }
+
+
+ def testF2iExact(a : Float, ref : Int, flag : Int, signed : Boolean, rounding : FpuRoundMode.E): Unit ={
+ val rs = new RegAllocator()
+ val rs1 = rs.allocate()
+ val rd = Random.nextInt(32)
+ load(rs1, a)
+ f2i(rs1, signed, rounding, FpuFormat.FLOAT){rsp =>
+ if(signed) {
+ val v = rsp.value.toBigInt.toInt
+ var ref2 = ref
+ if(a >= Int.MaxValue) ref2 = Int.MaxValue
+ if(a <= Int.MinValue) ref2 = Int.MinValue
+ if(a.isNaN) ref2 = Int.MaxValue
+ assert(v == (ref2), f" <= f2i($a) = $v, $ref2, $rounding, $flag")
+ } else {
+ val v = rsp.value.toBigInt.toLong & 0xFFFFFFFFl
+ var ref2 = ref.toLong & 0xFFFFFFFFl
+ if(a < 0) ref2 = 0
+ if(a >= 0xFFFFFFFFl) ref2 = 0xFFFFFFFFl
+ if(a.isNaN) ref2 = 0xFFFFFFFFl
+ assert(v == ref2, f" <= f2ui($a) = $v, $ref2, $rounding $flag")
+ }
+ }
+
+ flagMatch(flag, ref, f" f2${if(signed) "" else "u"}i($a) $ref $flag $rounding")
+ }
+
+
+
+ def testF642iExact(a : Double, ref : Int, flag : Int, signed : Boolean, rounding : FpuRoundMode.E): Unit ={
+ val rs = new RegAllocator()
+ val rs1 = rs.allocate()
+ val rd = Random.nextInt(32)
+ load(rs1, a)
+ f2i(rs1, signed, rounding, FpuFormat.DOUBLE){rsp =>
+ if(signed) {
+ val v = rsp.value.toBigInt.toInt
+ var ref2 = ref
+ if(a >= Int.MaxValue) ref2 = Int.MaxValue
+ if(a <= Int.MinValue) ref2 = Int.MinValue
+ if(a.isNaN) ref2 = Int.MaxValue
+ assert(v == (ref2), f" <= f2i($a) = $v, $ref2, $rounding, $flag")
+ } else {
+ val v = rsp.value.toBigInt.toLong & 0xFFFFFFFFl
+ var ref2 = ref.toLong & 0xFFFFFFFFl
+ if(a < 0) ref2 = 0
+ if(a >= 0xFFFFFFFFl) ref2 = 0xFFFFFFFFl
+ if(a.isNaN) ref2 = 0xFFFFFFFFl
+ assert(v == ref2, f" <= f2ui($a) = $v, $ref2, $rounding $flag")
+ }
+ }
+
+ flagMatch(flag, ref, f" f2${if(signed) "" else "u"}i($a) $ref $flag $rounding")
+ }
+
+
+
+ def testI2fExact(a : Int, ref : Float, f : Int, signed : Boolean, rounding : FpuRoundMode.E): Unit ={
+ val rs = new RegAllocator()
+ val rd = Random.nextInt(32)
+ i2f(rd, a, signed, rounding, FpuFormat.FLOAT)
+ storeFloat(rd){v =>
+ val aLong = if(signed) a.toLong else a.toLong & 0xFFFFFFFFl
+ assert(f2b(v) == f2b(ref), f"i2f($aLong) = $v, $ref $rounding")
+ }
+
+
+ flagMatch(f, ref, f"i2f($a) = $ref")
+ }
+
+
+
+ def testI2f64Exact(a : Int, ref : Double, f : Int, signed : Boolean, rounding : FpuRoundMode.E): Unit ={
+ val rs = new RegAllocator()
+ val rd = Random.nextInt(32)
+ i2f(rd, a, signed, rounding, FpuFormat.DOUBLE)
+ store(rd){v =>
+ val aLong = if(signed) a.toLong else a.toLong & 0xFFFFFFFFl
+ assert(d2b(v) == d2b(ref), f"i2f($aLong) = $v, $ref $rounding")
+ }
+
+
+ flagMatch(f, ref, f"i2f($a) = $ref")
+ }
+
+
+ def testCmpExact(a : Float, b : Float, ref : Int, flag : Int, arg : Int): Unit ={
+ val rs = new RegAllocator()
+ val rs1, rs2, rs3 = rs.allocate()
+ val rd = Random.nextInt(32)
+ load(rs1, a)
+ load(rs2, b)
+ cmp(rs1, rs2, arg, FpuFormat.FLOAT){rsp =>
+ val v = rsp.value.toBigInt.toInt
+ assert(v === ref, f"cmp($a, $b, $arg) = $v, $ref")
+ }
+ flagMatch(flag,f"$a < $b $ref $flag ${f2b(a).toHexString} ${f2b(b).toHexString}")
+ }
+ def testLeRaw(a : Float, b : Float, ref : Int, flag : Int) = testCmpExact(a,b,ref,flag, 0)
+ def testEqRaw(a : Float, b : Float, ref : Int, flag : Int) = testCmpExact(a,b,ref,flag, 2)
+ def testLtRaw(a : Float, b : Float, ref : Int, flag : Int) = testCmpExact(a,b,ref,flag, 1)
+
+
+ def testCmpF64Exact(a : Double, b : Double, ref : Int, flag : Int, arg : Int): Unit ={
+ val rs = new RegAllocator()
+ val rs1, rs2, rs3 = rs.allocate()
+ val rd = Random.nextInt(32)
+ load(rs1, a)
+ load(rs2, b)
+ cmp(rs1, rs2, arg, FpuFormat.DOUBLE){rsp =>
+ val v = rsp.value.toBigInt.toInt
+ assert(v === ref, f"cmp($a, $b, $arg) = $v, $ref")
+ }
+ flagMatch(flag,f"$a < $b $ref $flag ${d2b(a)} ${d2b(b)}")
+ }
+ def testLeF64Raw(a : Double, b : Double, ref : Int, flag : Int) = testCmpF64Exact(a,b,ref,flag, 0)
+ def testEqF64Raw(a : Double, b : Double, ref : Int, flag : Int) = testCmpF64Exact(a,b,ref,flag, 2)
+ def testLtF64Raw(a : Double, b : Double, ref : Int, flag : Int) = testCmpF64Exact(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)tes
+// 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 testMinMaxExact(a : Float, b : Float, arg : Int): Unit ={
+ val rs = new RegAllocator()
+ val rs1, rs2 = rs.allocate()
+ val rd = Random.nextInt(32)
+ val ref = (a,b) match {
+ case _ if a.isNaN && b.isNaN => b2f(0x7FC00000)
+ case _ if a.isNaN => b
+ case _ if b.isNaN => a
+ case _ => if(arg == 0) Math.min(a,b) else Math.max(a,b)
+ }
+ val flag = (a,b) match {
+ case _ if a.isNaN && ((f2b(a) >> 22 ) & 1) == 0 => 16
+ case _ if b.isNaN && ((f2b(b) >> 22 ) & 1) == 0 => 16
+ case _ => 0
+ }
+ load(rs1, a)
+ load(rs2, b)
+
+ minMax(rd,rs1,rs2, arg, FpuFormat.FLOAT)
+ storeFloat(rd){v =>
+ assert(f2b(ref) == f2b(v), f"minMax($a $b $arg) = $v, $ref")
+ }
+ flagMatch(flag, f"minmax($a $b $arg)")
+ }
+
+ def testMinExact(a : Float, b : Float) : Unit = testMinMaxExact(a,b,0)
+ def testMaxExact(a : Float, b : Float) : Unit = testMinMaxExact(a,b,1)
+
+
+ def testMinMaxF64Exact(a : Double, b : Double, arg : Int): Unit ={
+ val rs = new RegAllocator()
+ val rs1, rs2 = rs.allocate()
+ val rd = Random.nextInt(32)
+ val ref = (a,b) match {
+ case _ if a.isNaN && b.isNaN => b2d(0x7ff8000000000000l)
+ case _ if a.isNaN => b
+ case _ if b.isNaN => a
+ case _ => if(arg == 0) Math.min(a,b) else Math.max(a,b)
+ }
+ val flag = (a,b) match {
+ case _ if a.isNaN && ((d2b(a) >> 51 ) & 1) == 0 => 16
+ case _ if b.isNaN && ((d2b(b) >> 51 ) & 1) == 0 => 16
+ case _ => 0
+ }
+ load(rs1, a)
+ load(rs2, b)
+
+ minMax(rd,rs1,rs2, arg, FpuFormat.DOUBLE)
+ store(rd){v =>
+ assert(d2b(ref) == d2b(v), f"minMax($a $b $arg) = $v, $ref")
+ }
+ flagMatch(flag, f"minmax($a $b $arg)")
+ }
+
+ def testMinF64Exact(a : Double, b : Double) : Unit = testMinMaxF64Exact(a,b,0)
+ def testMaxF64Exact(a : Double, b : Double) : Unit = testMinMaxF64Exact(a,b,1)
+
+
+ def testSgnjRaw(a : Float, b : Float): Unit ={
+ var ref = b2f((f2b(a) & ~0x80000000) | f2b(b) & 0x80000000)
+ if(a.isNaN) ref = a
+ testBinaryOp(sgnj,a,b,ref,0, null,"sgnj")
+ }
+ def testSgnjnRaw(a : Float, b : Float): Unit ={
+ var ref = b2f((f2b(a) & ~0x80000000) | ((f2b(b) & 0x80000000) ^ 0x80000000))
+ if(a.isNaN) ref = a
+ testBinaryOp(sgnjn,a,b,ref,0, null,"sgnjn")
+ }
+ def testSgnjxRaw(a : Float, b : Float): Unit ={
+ var ref = b2f(f2b(a) ^ (f2b(b) & 0x80000000))
+ if(a.isNaN) ref = a
+ testBinaryOp(sgnjx,a,b,ref,0, null,"sgnjx")
+ }
+
+ val f64SignMask = 1l << 63
+ def testSgnjF64Raw(a : Double, b : Double): Unit ={
+ var ref = b2d((d2b(a).toLong & ~f64SignMask) | d2b(b).toLong & f64SignMask)
+ if(a.isNaN) ref = a
+ testBinaryOpF64(sgnj,a,b,ref,0, null,"sgnj")
+ }
+ def testSgnjnF64Raw(a : Double, b : Double): Unit ={
+ var ref = b2d((d2b(a).toLong & ~f64SignMask) | ((d2b(b).toLong & f64SignMask) ^ f64SignMask))
+ if(a.isNaN) ref = a
+ testBinaryOpF64(sgnjn,a,b,ref,0, null,"sgnjn")
+ }
+ def testSgnjxF64Raw(a : Double, b : Double): Unit ={
+ var ref = b2d(d2b(a).toLong ^ (d2b(b).toLong & f64SignMask))
+ if(a.isNaN) ref = a
+ testBinaryOpF64(sgnjx,a,b,ref,0, null,"sgnjx")
+ }
+
+
+ def withMinus(that : Seq[Float]) = that.flatMap(f => List(f, -f))
+ val fZeros = withMinus(List(0.0f))
+ val fSubnormals = withMinus(List(b2f(0x00000000+1), b2f(0x00000000+2), b2f(0x00006800), b2f(0x00800000-2), b2f(0x00800000-1)))
+ val fExpSmall = withMinus(List(b2f(0x00800000), b2f(0x00800000+1), b2f(0x00800000 + 2)))
+ val fExpNormal = withMinus(List(b2f(0x3f800000-2), b2f(0x3f800000-1), b2f(0x3f800000), b2f(0x3f800000+1), b2f(0x3f800000+2)))
+ val fExpBig = withMinus(List(b2f(0x7f7fffff-2), b2f(0x7f7fffff-1), b2f(0x7f7fffff)))
+ val fInfinity = withMinus(List(Float.PositiveInfinity))
+ val fNan = List(Float.NaN, b2f(0x7f820000), b2f(0x7fc00000))
+ val fAll = fZeros ++ fSubnormals ++ fExpSmall ++ fExpNormal ++ fExpBig ++ fInfinity ++ fNan
+
+ val iSmall = (0 to 20)
+ val iBigUnsigned = (0 to 20).map(e => 0xFFFFFFFF - e)
+ val iBigSigned = (0 to 20).map(e => 0x7FFFFFFF - e) ++ (0 to 20).map(e => 0x80000000 + e)
+ val iUnsigned = iSmall ++ iBigUnsigned
+ val iSigned = iSmall ++ iSmall.map(-_) ++ iBigSigned
+
+
+ val roundingModes = FpuRoundMode.elements
+ def foreachRounding(body : FpuRoundMode.E => Unit): Unit ={
+ for(rounding <- roundingModes){
+ body(rounding)
+ }
+ }
+
+ def testFmaF32() : Unit = {
+ testFmaRaw(randomFloat(), randomFloat(), randomFloat())
+ flagClear()
+ }
+
+
+ def testFmaF64() : Unit = {
+ testFmaF64Raw(randomDouble(), randomDouble(), randomDouble())
+ flagClear()
+ }
+
+ def testLeF32() : Unit = {
+ val (a,b,i,f) = f32.le.RAW.f32_f32_i32
+ testLeRaw(a,b,i, f)
+ }
+ def testLtF32() : Unit = {
+ val (a,b,i,f) = f32.lt.RAW.f32_f32_i32
+ testLtRaw(a,b,i, f)
+ }
+
+ def testEqF32() : Unit = {
+ val (a,b,i,f) = f32.eq.RAW.f32_f32_i32
+ testEqRaw(a,b,i, f)
+ }
+
+ def testLeF64() : Unit = {
+ val (a,b,i,f) = f64.le.RAW.f64_f64_i32
+ testLeF64Raw(a,b,i, f)
+ }
+ def testLtF64() : Unit = {
+ val (a,b,i,f) = f64.lt.RAW.f64_f64_i32
+ testLtF64Raw(a,b,i, f)
+ }
+
+ def testEqF64() : Unit = {
+ val (a,b,i,f) = f64.eq.RAW.f64_f64_i32
+ testEqF64Raw(a,b,i, f)
+ }
+
+
+ def testF2uiF32() : Unit = {
+ val rounding = FpuRoundMode.elements.randomPick()
+ val (a,b,f) = f32.f2ui(rounding).f32_i32
+ testF2iExact(a,b, f, false, rounding)
+ }
+
+ def testF2iF32() : Unit = {
+ val rounding = FpuRoundMode.elements.randomPick()
+ val (a,b,f) = f32.f2i(rounding).f32_i32
+ testF2iExact(a,b, f, true, rounding)
+ }
+
+ def testF2uiF64() : Unit = {
+ val rounding = FpuRoundMode.elements.randomPick()
+ val (a,b,f) = f64.f2ui(rounding).f64_i32
+ testF642iExact(a,b, f, false, rounding)
+ }
+
+ def testF2iF64() : Unit = {
+ val rounding = FpuRoundMode.elements.randomPick()
+ val (a,b,f) = f64.f2i(rounding).f64_i32
+ testF642iExact(a,b, f, true, rounding)
+ }
+
+
+ def testDiv() : Unit = {
+ val rounding = FpuRoundMode.elements.randomPick()
+ val (a,b,r,f) = f32.div(rounding).f32_f32_f32
+ testBinaryOp(div, a, b, r, f, rounding, "div")
+ }
+
+ def testSqrt() : Unit = {
+ val rounding = FpuRoundMode.elements.randomPick()
+ val (a,r,f) = f32.sqrt(rounding).f32_f32
+ testSqrtExact(a, r, f, rounding)
+ flagClear()
+ }
+
+ def testSgnjF32() : Unit = {
+ testSgnjRaw(b2f(Random.nextInt()), b2f(Random.nextInt()))
+ testSgnjnRaw(b2f(Random.nextInt()), b2f(Random.nextInt()))
+ testSgnjxRaw(b2f(Random.nextInt()), b2f(Random.nextInt()))
+ val (a,b,r,f) = f32.sgnj.RAW.f32_f32_i32
+ testSgnjRaw(a, b)
+ testSgnjnRaw(a, b)
+ testSgnjxRaw(a, b)
+ }
+
+ def testDivF64() : Unit = {
+ val rounding = FpuRoundMode.elements.randomPick()
+ val (a,b,r,f) = f64.div(rounding).f64_f64_f64
+ // testDivF64Exact(a, b, r, f, rounding)
+ testBinaryOpF64(div, a, b, r, f,rounding, "div")
+ flagClear()
+ }
+
+ def testSqrtF64() : Unit = {
+ val rounding = FpuRoundMode.elements.randomPick()
+ val (a,r,f) = f64.sqrt(rounding).f64_f64
+ testSqrtF64Exact(a, r, f, rounding)
+ flagClear()
+ }
+
+ def testSgnjF64() : Unit = {
+ testSgnjF64Raw(b2d(Random.nextLong()), b2d(Random.nextLong()))
+ testSgnjnF64Raw(b2d(Random.nextLong()), b2d(Random.nextLong()))
+ testSgnjxF64Raw(b2d(Random.nextLong()), b2d(Random.nextLong()))
+ val (a,b,r,f) = f64.sgnj.RAW.f64_f64_i32
+ testSgnjF64Raw(a, b)
+ testSgnjnF64Raw(a, b)
+ testSgnjxF64Raw(a, b)
+ }
+
+
+ def testTransferF32() : Unit = {
+ val (a,b,r,f) = f32.transfer.RAW.f32_f32_i32
+ testTransferF32Raw(a, Random.nextBoolean(), Random.nextBoolean())
+ }
+
+ def testTransferF64() : Unit = {
+ val (a,b,r,f) = f64.transfer.RAW.f64_f64_i32
+ testTransferF64Raw(a)
+ }
+
+ def testTransferF64F32() : Unit = {
+ val (a,b,r,f) = f64.f32.RAW.f64_f64_i32
+ testTransferF64F32Raw(a, Random.nextBoolean())
+ }
+ def testTransferF32F64() : Unit = {
+ val (a,b,r,f) = f32.f64.RAW.f32_f32_i32
+ 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 testClassF32() : Unit = {
+ val (a,b,r,f) = f32.fclass.RAW.f32_f32_i32
+ testClassRaw(a)
+ }
+
+ def testMinF32() : Unit = {
+ val (a,b,r,f) = f32.min.RAW.f32_f32_f32
+ testMinExact(a,b)
+ }
+ def testMaxF32() : Unit = {
+ val (a,b,r,f) = f32.max.RAW.f32_f32_f32
+ testMaxExact(a,b)
+ }
+
+ def testClassF64() : Unit = {
+ val (a,b,r,f) = f64.fclass.RAW.f64_f64_i32
+ testClassF64Raw(a)
+ }
+
+ def testMinF64() : Unit = {
+ val (a,b,r,f) = f64.min.RAW.f64_f64_f64
+ testMinF64Exact(a,b)
+ }
+ def testMaxF64() : Unit = {
+ val (a,b,r,f) = f64.max.RAW.f64_f64_f64
+ testMaxF64Exact(a,b)
+ }
+
+
+ def testUI2f32() : Unit = {
+ val rounding = FpuRoundMode.elements.randomPick()
+ val (a,b,f) = f32.i2f(rounding).i32_f32
+ testI2fExact(a,b,f, true, rounding)
+ }
+
+ def testI2f32() : Unit = {
+ val rounding = FpuRoundMode.elements.randomPick()
+ val (a,b,f) = f32.ui2f(rounding).i32_f32
+ testI2fExact(a,b,f, false, rounding)
+ }
+
+ def testUI2f64() : Unit = {
+ val rounding = FpuRoundMode.elements.randomPick()
+ val (a,b,f) = f64.i2f(rounding).i32_f64
+ testI2f64Exact(a,b,f, true, rounding)
+ }
+
+ def testI2f64() : Unit = {
+ val rounding = FpuRoundMode.elements.randomPick()
+ val (a,b,f) = f64.ui2f(rounding).i32_f64
+ testI2f64Exact(a,b,f, false, rounding)
+ }
+
+ def testMulF32() : Unit = {
+ val rounding = FpuRoundMode.elements.randomPick()
+ val (a,b,c,f) = f32.mul(rounding).f32_f32_f32
+ testBinaryOp(mul,a,b,c,f, rounding,"mul")
+ }
+
+ def testAddF32() : Unit = {
+ val rounding = FpuRoundMode.elements.randomPick()
+ val (a,b,c,f) = f32.add(rounding).f32_f32_f32
+ testBinaryOp(add,a,b,c,f, rounding,"add")
+ }
+
+ def testSubF32() : Unit = {
+ val rounding = FpuRoundMode.elements.randomPick()
+ val (a,b,c,f) = f32.sub(rounding).f32_f32_f32
+ testBinaryOp(sub,a,b,c,f, rounding,"sub")
+ }
+
+
+ def testMulF64() : Unit = {
+ val rounding = FpuRoundMode.elements.randomPick()
+ val (a,b,c,f) = f64.mul(rounding).f64_f64_f64
+ testBinaryOpF64(mul,a,b,c,f, rounding,"mul")
+ }
+
+ def testAddF64() : Unit = {
+ val rounding = FpuRoundMode.elements.randomPick()
+ val (a,b,c,f) = f64.add(rounding).f64_f64_f64
+ testBinaryOpF64(add,a,b,c,f, rounding,"add")
+ }
+
+ def testSubF64() : Unit = {
+ val rounding = FpuRoundMode.elements.randomPick()
+ val (a,b,c,f) = f64.sub(rounding).f64_f64_f64
+ testBinaryOpF64(sub,a,b,c,f, rounding,"sub")
+ }
+
+
+ val f32Tests = List[() => Unit](testSubF32, testAddF32, testMulF32, testI2f32, testUI2f32, testMinF32, testMaxF32, testSgnjF32, testTransferF32, testDiv, testSqrt, testF2iF32, testF2uiF32, testLeF32, testEqF32, testLtF32, testClassF32, testFmaF32)
+ val f64Tests = List[() => Unit](testSubF64, testAddF64, testMulF64, testI2f64, testUI2f64, testMinF64, testMaxF64, testSgnjF64, testTransferF64, testDiv, testSqrt, testF2iF64, testF2uiF64, testLeF64, testEqF64, testLtF64, testClassF64, testFmaF64, testCvtF32F64, testCvtF64F32)
+
+ var fxxTests = f32Tests
+ if(p.withDouble) fxxTests ++= f64Tests
+
+//5071920 5225560
+// for(v <- List(-1.17549435082e-38f, 1.17549435082e-38f);
+// rounding <- FpuRoundMode.elements) {
+// for (i <- 0 until 2048) {
+// val b = d2b(v)// 0x0010000000000000l //d2b(1.17549435082e-38)
+// val s = (b - (i.toLong << 21)).toLong
+// val d = b2d(s)
+//// val rounding = FpuRoundMode.RNE
+// testCvtF64F32Raw(d, Clib.math.d2f(d, rounding.position), Clib.math.d2fFlag(d, rounding.position), rounding)
+// }
+// }
+//
+//
+// testCvtF64F32Raw(-1.1754943508051483E-38, -1.17549435E-38f, 1, FpuRoundMode.RNE)
+// testCvtF64F32Raw( 1.1754943157898258E-38, 1.17549435E-38f , 3, FpuRoundMode.RMM)
+// testCvtF64F32Raw( 1.1754942807573643E-38, 1.17549435E-38f , 3, FpuRoundMode.RMM)
+// testCvtF64F32Raw(-1.1754943508051483E-38, -1.17549435E-38f, 1, FpuRoundMode.RMM)
+
+ //-1.1754943508051483E-38 -1.17549435E-38 1 RNE @ 592770
+ // 1.1754943157898258E-38 1.17549435E-38 3 RMM @ 2697440
+ // 1.1754942807573643E-38 1.17549435E-38 3 RMM
+// for(_ <- 0 until 1000000) testCvtF64F32() // 1 did not equal 3 Flag missmatch dut=1 ref=3 testCvtF64F32Raw 1.1754942807573643E-38 1.17549435E-38 RMM
+// println("FCVT_D_S done")
+
+// testBinaryOpF64(div, -2.2250738564511294E-308, 4.294967296003891E9, -5.180654E-318, 1, FpuRoundMode.RDN,"div") // ??? wtf
+
+// testBinaryOp(add,b2f(0x7F800000),b2f(0x1FD << 23),b2f(0x7F800000),0, FpuRoundMode.RNE,"add")
+
+
+
+// testBinaryOp(mul,1.1753509E-38f, 1.0001221f ,1.17549435E-38f,1, FpuRoundMode.RNE,"mul")
+//
+// for(i <- 0 until 10000000){
+// val rounding = FpuRoundMode.elements.randomPick()
+// val (a,b,c,f) = f32.mul(rounding).f32_f32_f32
+// testBinaryOp(mul,a,b,c,f, rounding,"mul")
+// }
+//
+// testBinaryOpF64(mul,2.781342323134002E-309, 7.999999999999999, 2.2250738585072014E-308, 3, FpuRoundMode.RNE,"mul")
+//// for(i <- 0 until 10000000){
+//// val rounding = FpuRoundMode.RNE
+//// val (a,b,c,f) = f64.mul(rounding).f64_f64_f64
+//// testBinaryOpF64(mul,a,b,c,f, rounding,"mul")
+//// }
+// for(_ <- 0 until 100000000) testMulF64()
+// println("f64 Mul done")
+//
+// for(_ <- 0 until 10000) testDivF64()
+// println("f64 div done")
+//
+//
+// for(_ <- 0 until 10000) testDiv()
+// println("f32 div done")
+//
+// for(_ <- 0 until 10000) testAddF32()
+// for(_ <- 0 until 10000) testSubF32()
+//
+// println("Add done")
+//
+//
+// for(_ <- 0 until 10000) testSqrt()
+// println("f32 sqrt done")
+
+
+
+
+
+
+ //TODO test boxing
+ //TODO double <-> simple convertions
+ if(p.withDouble) {
+
+ testSqrtF64Exact(1.25*1.25, 1.25, 0, FpuRoundMode.RNE)
+ testSqrtF64Exact(1.5*1.5, 1.5, 0, FpuRoundMode.RNE)
+
+ for(_ <- 0 until 10000) testSqrtF64()
+ println("f64 sqrt done")
+
+// testDivF64Exact(1.0, 8.0, 0.125, 0, FpuRoundMode.RNE)
+// testDivF64Exact(4.0, 8.0, 0.5, 0, FpuRoundMode.RNE)
+// testDivF64Exact(8.0, 8.0, 1.0, 0, FpuRoundMode.RNE)
+// testDivF64Exact(1.5, 2.0, 0.75, 0, FpuRoundMode.RNE)
+// testDivF64Exact(1.875, 1.5, 1.25, 0, FpuRoundMode.RNE)
+
+ for(_ <- 0 until 10000) testDivF64()
+ println("f64 div done")
+
+ for(_ <- 0 until 10000) testSgnjF64()
+ println("f64 sgnj done")
+
+ for(_ <- 0 until 10000) testSgnjF32()
+ println("f32 sgnj done")
+
+ //380000000001ffef 5fffffffffff9ff 8000000000100000
+// testBinaryOpF64(mul,-5.877471754282472E-39, 8.814425663400984E-280, -5.180654E-318 ,1, FpuRoundMode.RMM,"mul")
+// 5.877471754282472E-39 8.814425663400984E-280 -5.180654E-318 RMM
+
+ for(_ <- 0 until 10000) testCvtF64F32() // 1 did not equal 3 Flag missmatch dut=1 ref=3 testCvtF64F32Raw 1.1754942807573643E-38 1.17549435E-38 RMM
+ println("FCVT_D_S done")
+ for(_ <- 0 until 10000) testCvtF32F64()
+ println("FCVT_S_D done")
+
+ for(_ <- 0 until 10000) testF2iF64()
+ println("f64 f2i done")
+ for(_ <- 0 until 10000) testF2uiF64()
+ println("f64 f2ui done")
+
+
+
+
+
+ for(_ <- 0 until 10000) testMinF64()
+ for(_ <- 0 until 10000) testMaxF64()
+ println("f64 minMax done")
+
+
+
+ for(i <- 0 until 1000) testFmaF64()
+ flagClear()
+ println("f64 fma done") //TODO
+
+
+ for(_ <- 0 until 10000) testLeF64()
+ for(_ <- 0 until 10000) testLtF64()
+ for(_ <- 0 until 10000) testEqF64()
+ println("f64 Cmp done")
+
+
+ for(_ <- 0 until 10000) testClassF64()
+ println("f64 class done")
+//
+
+
+
+
+
+
+
+
+ for(_ <- 0 until 10000) testAddF64()
+ for(_ <- 0 until 10000) testSubF64()
+ println("f64 Add done")
+
+
+ // testI2f64Exact(0x7FFFFFF5, 0x7FFFFFF5, 0, true, FpuRoundMode.RNE)
+ for(_ <- 0 until 10000) testUI2f64()
+ for(_ <- 0 until 10000) testI2f64()
+ println("f64 i2f done")
+
+
+
+// testF2iExact(1.0f,1, 0, false, FpuRoundMode.RTZ)
+// testF2iExact(2.0f,2, 0, false, FpuRoundMode.RTZ)
+// testF2iExact(2.5f,2, 1, false, FpuRoundMode.RTZ)
+
+
+
+
+ testBinaryOpF64(mul,1.0, 1.0, 1.0,0 , FpuRoundMode.RNE,"mul")
+ testBinaryOpF64(mul,1.0, 2.0, 2.0,0 , FpuRoundMode.RNE,"mul")
+ testBinaryOpF64(mul,2.5, 2.0, 5.0,0 , FpuRoundMode.RNE,"mul")
+
+ for(_ <- 0 until 10000) testMulF64()
+ println("f64 Mul done")
+
+ testTransferF64Raw(1.0)
+ testTransferF64Raw(2.0)
+ testTransferF64Raw(2.5)
+ testTransferF64Raw(6.97949770801e-39)
+ testTransferF64Raw(8.72437213501e-40)
+ testTransferF64Raw(5.6E-45)
+
+ testTransferF32F64Raw(b2f(0xFFFF1234), false)
+ testTransferF64F32Raw(b2d(0xFFF123498765463l << 4), false)
+ testTransferF32F64Raw(b2f(0xFFFF1234), true)
+ testTransferF64F32Raw(b2d(0xFFF123498765463l << 4), true)
+
+ for (_ <- 0 until 10000) testTransferF64()
+ println("f64 load/store/rf transfer done")
+
+ for (_ <- 0 until 10000) testTransferF64F32()
+ println("f64 -> f32 load/store/rf transfer done")
+
+ for (_ <- 0 until 10000) testTransferF32F64()
+ println("f32 -> f64 load/store/rf transfer done")
+
+ }
+
+ for(_ <- 0 until 10000) testTransferF32()
+ println("f32 load/store/rf transfer done")
+
+ for(_ <- 0 until 10000) testMulF32()
+ println("Mul done")
+
+
+ for(_ <- 0 until 10000) testUI2f32()
+ for(_ <- 0 until 10000) testI2f32()
+ println("i2f done")
+
+
+ testF2iExact(1.0f,1, 0, false, FpuRoundMode.RTZ)
+ testF2iExact(2.0f,2, 0, false, FpuRoundMode.RTZ)
+ testF2iExact(2.5f,2, 1, false, FpuRoundMode.RTZ)
+
+
+
+
+
+ for(_ <- 0 until 10000) testF2uiF32()
+ for(_ <- 0 until 10000) testF2iF32()
+ println("f2i done")
+
+
+
+// waitUntil(cmdQueue.isEmpty)
+// dut.clockDomain.waitSampling(1000)
+// simSuccess()
+
+
+
+ for(i <- 0 until 1000) testFmaF32()
+ flagClear()
+ println("fma done") //TODO
+
+
+ testF2iExact(-2.14748365E9f, -2147483648, 0, true, FpuRoundMode.RDN)
+
+ testEqRaw(Float.PositiveInfinity,Float.PositiveInfinity,1, 0)
+ testEqRaw(0f, 0f,1, 0)
+
+ for(_ <- 0 until 10000) testLeF32()
+ for(_ <- 0 until 10000) testLtF32()
+ for(_ <- 0 until 10000) testEqF32()
+ println("Cmp done")
+
+
+ for(_ <- 0 until 10000) testDiv()
+ println("f32 div done")
+
+ for(_ <- 0 until 10000) testSqrt()
+ println("f32 sqrt done")
+
+ for(_ <- 0 until 10000) testSgnjF32()
+ println("f32 sgnj done")
+
+
+ for(_ <- 0 until 10000) testClassF32()
+ println("f32 class done")
+
+
+ for(_ <- 0 until 10000) testMinF32()
+ for(_ <- 0 until 10000) testMaxF32()
+ println("minMax done")
+
+
+
+ testBinaryOp(mul,1.469368E-39f, 7.9999995f, 1.17549435E-38f,3, FpuRoundMode.RUP,"mul")
+ testBinaryOp(mul,1.1753509E-38f, 1.0001221f, 1.17549435E-38f ,1, FpuRoundMode.RUP,"mul")
+ testBinaryOp(mul, 1.1754942E-38f, -1.0000001f, -1.17549435E-38f,1, FpuRoundMode.RNE,"mul")
+ testBinaryOp(mul, 1.1754942E-38f, -1.0000001f, -1.17549435E-38f,1, FpuRoundMode.RDN,"mul")
+ testBinaryOp(mul, 1.1754942E-38f, -1.0000001f, -1.17549435E-38f,1, FpuRoundMode.RMM,"mul")
+
+ testBinaryOp(mul, 1.1754945E-38f, 0.9999998f, 1.17549435E-38f, 3, FpuRoundMode.RUP, "mul")
+ testBinaryOp(mul, 1.1754945E-38f, -0.9999998f, -1.17549435E-38f, 3, FpuRoundMode.RDN, "mul")
+ testBinaryOp(mul, 1.1754946E-38f, 0.9999997f, 1.17549435E-38f, 3, FpuRoundMode.RUP, "mul")
+ testBinaryOp(mul, 1.1754946E-38f, -0.9999997f, -1.17549435E-38f, 3, FpuRoundMode.RDN, "mul")
+ testBinaryOp(mul, 1.1754949E-38f, 0.99999946f, 1.17549435E-38f, 3, FpuRoundMode.RUP, "mul")
+ testBinaryOp(mul, 1.1754949E-38f, -0.99999946f, -1.17549435E-38f, 3, FpuRoundMode.RDN, "mul")
+ testBinaryOp(mul, 1.1754955E-38f, 0.999999f, 1.17549435E-38f, 3, FpuRoundMode.RUP, "mul")
+
+
+
+
+
+
+ for(_ <- 0 until 10000) testAddF32()
+ for(_ <- 0 until 10000) testSubF32()
+
+ println("Add done")
+
+
+
+
+
+
+
+
+
+
+// waitUntil(cmdQueue.isEmpty)
+// dut.clockDomain.waitSampling(1000)
+// simSuccess()
+
+ for(i <- 0 until 100000) fxxTests.randomPick()()
+ waitUntil(cpu.rspQueue.isEmpty)
+ }
+
+
+
+
+ stim.foreach(_.join())
+ dut.clockDomain.waitSampling(100)
+ }
+ }
+}
+
+
+//object Clib {
+// val java_home = System.getProperty("java.home")
+// assert(java_home != "" && java_home != null, "JAVA_HOME need to be set")
+// val jdk = java_home.replace("/jre","").replace("\\jre","")
+// val jdkIncludes = jdk + "/include"
+// val flags = List("-fPIC", "-m64", "-shared", "-Wno-attributes") //-Wl,--whole-archive
+// val os = new File("/media/data/open/SaxonSoc/berkeley-softfloat-3/build/Linux-x86_64-GCC").listFiles().map(_.getAbsolutePath).filter(_.toString.endsWith(".o"))
+// val cmd = s"gcc -I/media/data/open/SaxonSoc/berkeley-softfloat-3/source/include -I$jdkIncludes -I$jdkIncludes/linux ${flags.mkString(" ")} -o src/test/cpp/fpu/math/fpu_math.so src/test/cpp/fpu/math/fpu_math.c src/test/cpp/fpu/math/softfloat.a" // src/test/cpp/fpu/math/softfloat.a
+// DoCmd.doCmd(cmd)
+// val math = new FpuMath
+//}
+// cd /media/data/open/SaxonSoc/testFloatBuild/berkeley-softfloat-3/build/Linux-x86_64-GCC
+// make clean && SPECIALIZE_TYPE=RISCV make -j$(nproc) && cp softfloat.a /media/data/open/SaxonSoc/artyA7SmpUpdate/SaxonSoc/ext/VexRiscv/src/test/cpp/fpu/math
+//object FpuCompileSo extends App{
+//
+//// val b2f = lang.Float.intBitsToFloat(_)
+//// for(e <- FpuRoundMode.elements) {
+//// println(e)
+//// for (i <- -2 until 50) println(i + " => " + Clib.math.addF32(b2f(0x7f000000), b2f(0x7f000000 + i), e.position))
+//// println("")
+//// }
+// //1 did not equal 3 Flag missmatch dut=1 ref=3 ## mul 0.9994812 -1.1754988E-38 -1.174889E-38 RMM
+// // println(Clib.math.mulF32(0.9994812f, -1.1754988E-38f, FpuRoundMode.RMM.position))
+//// miaou ffffffff 7fffffe0 7f
+//// miaou 0 3ffffff0 70 = 0
+//
+// val b2f = lang.Float.intBitsToFloat(_)
+// val b2d = lang.Double.longBitsToDouble(_)
+// val f2b = lang.Float.floatToRawIntBits(_)
+// val d2bOffset = BigInt("10000000000000000",16)
+// def d2b(that : Double) = {
+// val l = lang.Double.doubleToRawLongBits(that)
+// var a = BigInt(l)
+// if(l < 0) {
+// a = d2bOffset + a
+// }
+// a
+// }
+// val builder =new StringBuilder()
+// for(i <- 0 until 256){
+//// builder ++= (Clib.math.mulF32(1.17548538251e-38f, b2f(f2b(1.0f)+i),0)).toString + "\n"
+// val b = d2b(1.17549435082e-38)
+// val s = (b-(i.toLong << 25)).toLong
+// val d = b2d(s)
+// builder ++= f"$b $s $d => "
+// builder ++= f"${d2b(d)}%x " + (Clib.math.d2fFlag(d,0)).toString + " " + d + " => " + (Clib.math.d2f(d,FpuRoundMode.RMM.position)).toString + "\n"
+// }
+//
+// Thread.sleep(400)
+// println(builder.toString)
+// println(Clib.math.mulF32( 1.1753509E-38f, 1.0001221f, FpuRoundMode.RUP.position))
+// println(Clib.math.mulF32( 1.1754945E-38f, 0.9999998f, FpuRoundMode.RUP.position))
+// testBinaryOp(mul, 1.1753509E-38f, 1.0001221f, 1.17549435E-38f ,1, FpuRoundMode.RUP,"mul")
+// testBinaryOp(mul, 1.1754945E-38f, 0.9999998f, 1.17549435E-38f, 3, FpuRoundMode.RUP, "mul")
+// miaou ffffffff 7fffffe0 7f
+// miaou 0 3ffffff0 70 = 0
+// miaou ffffffff 7fffff7e 7f
+// miaou 1 3fffffbf 3f = 1
+
+// println(Clib.math.mulF32( 1.1753509E-38f, 1.0001221f, FpuRoundMode.RUP.position))
+// println(Clib.math.mulF32( 1.469368E-39f, 7.9999995f, FpuRoundMode.RUP.position))
+// println(Clib.math.mulF32( 1.40129846432e-45f, 7.9999995f, FpuRoundMode.RUP.position))
+// println(Clib.math.mulF32( 2.93873587706e-39f, 7.9999995f, FpuRoundMode.RUP.position))
+// println(Clib.math.mulF32( 1f, 7.9999995f, FpuRoundMode.RUP.position))
+
+
+// println(Clib.math.addF32(1.00000011921f, 4.0f, FpuRoundMode.RNE.position))
+// println(Clib.math.addF32(1.00000011921f, 4.0f, FpuRoundMode.RTZ.position))
+// println(Clib.math.addF32(1.00000011921f, 4.0f, FpuRoundMode.RDN.position))
+// println(Clib.math.addF32(1.00000011921f, 4.0f, FpuRoundMode.RUP.position))
+//}
+
+class ProcessStream(cmd : String){
+ import sys.process._
+
+ val buf = mutable.Queue[() => String]()
+ val p = Process(cmd).run(new ProcessLogger {
+ override def out(s: => String): Unit = {
+ while(buf.size > 10000) Thread.sleep(10)
+ buf.enqueue(() => s)
+ }
+ override def err(s: => String): Unit = {}
+ override def buffer[T](f: => T): T = f
+ })
+
+ def kill = p.destroy()
+ def next = {
+ while(buf.isEmpty) { Thread.sleep(10) }
+ buf.dequeue()()
+ }
+}
+
diff --git a/VexRiscv/src/test/scala/vexriscv/ip/fpu/Playground.scala b/VexRiscv/src/test/scala/vexriscv/ip/fpu/Playground.scala
new file mode 100644
index 0000000..a155210
--- /dev/null
+++ b/VexRiscv/src/test/scala/vexriscv/ip/fpu/Playground.scala
@@ -0,0 +1,47 @@
+package vexriscv.ip.fpu
+
+object MiaouDiv extends App{
+ val input = 2.5
+ var output = 1/(input*0.95)
+
+ def y = output
+ def x = input
+
+ for(i <- 0 until 10) {
+ output = 2 * y - x * y * y
+ println(output)
+ }
+
+
+ //output = x*output
+ println(1/input)
+}
+
+object MiaouSqrt extends App{
+ val input = 2.0
+ var output = 1/Math.sqrt(input*0.95)
+ // def x = output
+ // def y = input
+
+ def y = output
+ def x = input
+
+ for(i <- 0 until 10) {
+ output = y * (1.5 - x * y * y / 2)
+ println(output)
+ }
+
+ output = x*output
+ println(output)
+ println(s"ref ${Math.sqrt(input)}")
+}
+
+
+object MiaouNan extends App{
+ println(Float.NaN + 3.0f)
+ println(3.0f + Float.NaN )
+ println(0.0f*Float.PositiveInfinity )
+ println(1.0f/0.0f )
+ println(Float.MaxValue -1 )
+ println(Float.PositiveInfinity - Float.PositiveInfinity)
+} \ No newline at end of file