diff --git a/tools/riscv-torture/.gitignore b/tools/riscv-torture/.gitignore new file mode 100644 index 0000000..5fd76e4 --- /dev/null +++ b/tools/riscv-torture/.gitignore @@ -0,0 +1,4 @@ +.*.swp +output/test* +output/failedtests +*target/ diff --git a/tools/riscv-torture/.gitmodules b/tools/riscv-torture/.gitmodules new file mode 100644 index 0000000..a557616 --- /dev/null +++ b/tools/riscv-torture/.gitmodules @@ -0,0 +1,3 @@ +[submodule "env"] + path = env + url = https://github.com/riscv/riscv-test-env.git diff --git a/tools/riscv-torture/Makefile b/tools/riscv-torture/Makefile new file mode 100644 index 0000000..5ec0ed6 --- /dev/null +++ b/tools/riscv-torture/Makefile @@ -0,0 +1,76 @@ +# Convenience Makefile + +SBT ?= java -Xmx1G -Xss8M -XX:MaxPermSize=128M -jar sbt-launch.jar +RTL_CONFIG := DefaultConfig +C_SIM := ../emulator/emulator-rocketchip-$(RTL_CONFIG) +R_SIM := ../vsim/simv-rocketchip-$(RTL_CONFIG) +TEST := output/test.S +OPTIONS := $(empty) +NUM := 0 +SUITE := output +CONFIG := config/default.config +COMMIT := none +empty := +space := $(empty) $(empty) +cfgopt := $(space)-f$(space) +gitopt := $(space)-g$(space) +CFG := $(subst $(space),$(cfgopt),$(CONFIG)) +GITCMT := $(subst $(space),$(gitopt),$(COMMIT)) + +.phony: gen ctest rtest itest igentest cgentest rgentest \ +cnight rnight crnight csuite rsuite \ + +gen: + $(SBT) 'generator/run -n $(NUM)' + +csuite: + for i in `ls $(SUITE) | grep .S` ; do echo $$i ; \ + result=`make ctest TEST=$(SUITE)/$$i OPTIONS="-s false" | grep 'Simulation failed\|signatures match'` ; \ + echo $$result ; done + rm $(SUITE)/tes*[!.S] + +rsuite: + for i in `ls $(SUITE) | grep .S` ; do echo $$i ; \ + result=`make rtest TEST=$(SUITE)/$$i OPTIONS="-s false" | grep 'Simulation failed\|signatures match'` ; \ + echo $$result ; done + rm $(SUITE)/tes*[!.S] + +crsuite: + for i in `ls $(SUITE) | grep .S` ; do echo $$i ; \ + result=`make crtest TEST=$(SUITE)/$$i OPTIONS="-s false" | grep 'Simulation failed\|signatures match'` ; \ + echo $$result ; done + rm $(SUITE)/tes*[!.S] + +igentest: + $(SBT) 'testrun/run' + +cgentest: + $(SBT) 'testrun/run -c $(C_SIM) $(OPTIONS)' + +rgentest: + $(SBT) 'testrun/run -r $(R_SIM) $(OPTIONS)' + +crgentest: + $(SBT) 'testrun/run -c $(C_SIM) -r $(R_SIM) $(OPTIONS)' + +itest: + $(SBT) 'testrun/run -a $(TEST) $(OPTIONS)' + +ctest: + $(SBT) 'testrun/run -c $(C_SIM) -a $(TEST) $(OPTIONS)' + +rtest: + $(SBT) 'testrun/run -r $(R_SIM) -a $(TEST) $(OPTIONS)' + +crtest: + $(SBT) 'testrun/run -c $(C_SIM) -r $(R_SIM) -a $(TEST) $(OPTIONS)' + +cnight: + $(SBT) 'overnight/run -c $(C_SIM) -g $(COMMIT) $(OPTIONS)' + +rnight: + $(SBT) 'overnight/run -r $(R_SIM) -g $(COMMIT) $(OPTIONS)' + +crnight: + $(SBT) 'overnight/run -c $(C_SIM) -r $(R_SIM) -g $(COMMIT) $(OPTIONS)' + diff --git a/tools/riscv-torture/README b/tools/riscv-torture/README new file mode 100644 index 0000000..cfd867a --- /dev/null +++ b/tools/riscv-torture/README @@ -0,0 +1,260 @@ +=========================================================================== +RISC-V Torture Test Generator +=========================================================================== +# Author: Yunsup Lee and Henry Cook +# Date: January 29th, 2012 +# Version: (under version control) + +This is the RISC-V torture test generator and framework. This repository +contains three sub-projects that build upon one another. The first, +[generator], is used to create a single random torture test. The second, +[testrun], is used to run a particular test on particular simulators, +diffing the resulting signature with the ISA simulator and optionally +creating a derivative test subset that pinpoints the divergence. The third, +[overnight], wraps testrun, allowing tests to be run repeatedly for a given +duration or until a failure count. + + +--------------------------------------------------------------------------- +Instructions +--------------------------------------------------------------------------- + +Modify "config/default.config" to set the parameters desired for building tests +(e.g., setting which instructions to use and in which ratio). + +Modify "Makefile" as desired to execute the C simulator or RTL simulator of +your choice, and to set the other parameters as you require. + +To build a single test and test it on Spike: + +$ make igentest + +To build single test and run it on the C simulator or RTL simulator, use +"make cgentest" or "make rgentest". + +To run overnight tests, you can use "make cnight" and "make rnight". + +--------------------------------------------------------------------------- +Signatures +--------------------------------------------------------------------------- + +Torture works by dumping the register state out to memory at the end of the +test program execution. This output is then compared against the output from +the Spike ISA simulator. + +The torture program writes the register state to the memory address specified +by "xreg_output_data", which is located in the memory section +".global begin_signature". The Spike ISA simulator will write out the data +found in the "begin_signature" section on exit if provided with the +"+signature=" argument: + +$ spike +signature=my_spike_signature.txt test_binary + +The Rocket-chip infrastructure uses the "riscv-fesvr" program to control the +execution of the C and RTL simulators. The "riscv-fesvr" also accepts the ++signature argument too. + +$ ./csim-rocket-chip +signature=my_rocket_signature.txt test_binary + +A simple diff between the Spike and chip simulator signatures will tell you if +any errors have occurred. + +$ diff my_spike_signature.txt my_rocket_signature.txt + + +**PORTING TORTURE TO YOUR OWN RISC-V PROCESSOR:** + +If you would like to use riscv-torture with your own RISC-V processor, you will +need to provide a way to dump the "begin_signature" section to a file. + +--------------------------------------------------------------------------- +Low-level Usage +--------------------------------------------------------------------------- + +Some basic use cases are illustrated here (note the Makefile abstracts this for +you). + +Make a single test: +% ./sbt generator/run +% cd output +% make +% spike +signature=test.sig test + +Take an existing test and diff the signatures of ISA and C simulators: +% ./sbt 'testrun/run -a output/test.S -c /path/to/reference-chip/emulator/emulator' + +*** Currently, due to the limiation of scala process library, you cannot +torture the RTL simulator *** +# Generate a random test and diff the signatures of ISA and RTL simulators: +# % ./sbt 'testrun/run -r /path/to/reference-chip/vlsi/build/vcs-sim-rtl/simv' + +Run tests for 30 minutes, email hcook when done, and save failures to dir: +% ./sbt 'overnight/run -m 30 -e hcook@eecs.berkeley.edu -p dir' + +--------------------------------------------------------------------------- +Installing +--------------------------------------------------------------------------- + +% git submodule update --init + + +--------------------------------------------------------------------------- +Overnight Overview +--------------------------------------------------------------------------- + +This framework utilizes both the test runner and test generator to perform +a long terms serach for failing test cases. It takes the following command +line arguments: + +Usage: overnight/run [options] + -C | --config + config file + -p | --permdir + dir to store failing tests + -c | --csim + C simulator + -r | --rtlsim + RTL simulator + -e
| --email
+ email to report to + -t | --threshold + number of failures to trigger email + -m | --minutes + number of minutes to run tests + +You can only generate tests with one instruction mix at a time (based on +the setting in the config file). It doesn't matter what simulator you use +with the -r and -c flags, they just determines the name used to describe +whose diff failed. + +--------------------------------------------------------------------------- +Testrun Overview +--------------------------------------------------------------------------- + +This utility compares the signatures generated by passing the -testsig flag +to the specified simulators. If it encounters a difference, it subdivides +the test into many subtests and searches for which exact program segment +reveals the failure. It takes the following command line arguments: + +Usage: testrun/run [options] + -C | --config + config file + -a | --asm + input ASM file + -c | --csim + C simulator + -r | --rtlsim + RTL simulator + -s | --seek + Seek for failing pseg + -d | --dump + Dump mismatched signatures + +If you don't specify a asm file, a random one will be generated for you. +You can only generate tests with one instruction mix at a time (based on +the setting in the config file). It doesn't matter what simulator you use +with the -r and -c flags, they just determines the name used to describe +whose diff failed. By default, a failed diff will result in the subtest +sweep occuring, but this search can be diasbled. Note that the pseg ID +reported is actually the pseg following the pseg containing the error. +You can optionally dump mistmatched signatures to the dir containing the +asm file under test. + +--------------------------------------------------------------------------- +Generator Overview +--------------------------------------------------------------------------- + +To generate a random test, the torture test generator randomly generates +many test sequences from a set of test sequences that are written by hand, +performs a random register allocation for all test sequences, and finally +randomly interleaves instructions from these test sequences. To extend the +set of tests or coverage, the programmer needs to write new test sequences. +It takes the following command line arguments: + +Usage: generator/run [options] + -o | --output + output filename + -C | --config + config file + +The following sections describe adding new functionality to the generator. + +--------------------------------------------------------------------------- +Test sequence example +--------------------------------------------------------------------------- + +Before we talk about how to write a test sequence, let's look at a very +simple example. The following example is a test sequence, which emits an +add instruction. + +class SeqADD extends Seq +{ + val src1 = reg_read_any() + val src2 = reg_read_any() + val dest = reg_write(src1, src2) + insts += ADD(dest, src1, src2) +} + +As I hinted in the overview that the test generator will do register +allocation you don't write a string of instructions with architectural +registers. You request a virtual registers (i.e., registers that are yet +tied down to architectural registers) when you need them, save them in +scala values, and use them when you need to (e.g., in an instruction). + +--------------------------------------------------------------------------- +Types of virtual registers +--------------------------------------------------------------------------- + + - Hidden (position dependent registers): Registers that will have + different values when the code is positioned at a different address. An + example is registers that hold addresses. Registers that are hidden should + be excluded from the output signature. + + - Visible (position independent registers): Registers that are not hidden, + therefore will have the same values when the code is positioned at + a different address. These registers should be included as part of the + output signature. + +--------------------------------------------------------------------------- +How to write a sequence +--------------------------------------------------------------------------- + +Use the following functions to request a register, and generate a string of +instructions (look at Inst.scala to see what instructions are available) +that uses these virtual registers, and add them to the insts array. + + - reg_read_zero(): returns register x0 + - reg_read_any(): returns any type of register (hidden or visible) + - reg_read_visible(): returns a visible register + - reg_write_ra(): returns register ra for write + - reg_write_visible(): returns a visible register for write + - reg_write_hidden(): returns a hidden register for write + - reg_write(regs: Reg*): returns a register that matches the type of regs + (if any reg in regs are hidden, the output type is hidden) + +Note that the torture test framework is written in scala, you can use any +scala functionality to generate instructions. Look at SeqALU.scala, +SeqMem.scala, and SeqBranch.scala to get inspired. + +--------------------------------------------------------------------------- +Future TODO +--------------------------------------------------------------------------- + + - provide support for loops + + - generate statistics of a test to get a sense of coverage + + statistics should include instruction count of each type + + statistics should include register usage + + - complete floating point tests + + add floating point memory move tests + + improve floating point init randomization + + add rounding modes tests + + - complete vector tests + + better randomization + + add SeqVOnly: Tests special vf-only instructions + + - code refactoring + + consolidate RegPool logic + + detect and suppress unallocatable sequences diff --git a/tools/riscv-torture/build.sbt b/tools/riscv-torture/build.sbt new file mode 100644 index 0000000..0a211f9 --- /dev/null +++ b/tools/riscv-torture/build.sbt @@ -0,0 +1,26 @@ +lazy val commonSettings = Seq( + organization := "edu.berkeley.cs", + version := "1.1", + scalaVersion := "2.11.12", + libraryDependencies ++= Seq("com.github.scopt" %% "scopt" % "3.3.0"), + libraryDependencies ++= Seq("com.github.scala-incubator.io" %% "scala-io-core" % "0.4.3"), + libraryDependencies ++= Seq("com.github.scala-incubator.io" %% "scala-io-file" % "0.4.3") +) + +lazy val torture = (project in file(".")) + .settings(commonSettings) + .dependsOn(generator, testrun, overnight, fileop) + +lazy val generator = (project in file("generator")) + .settings(commonSettings) + +lazy val testrun = (project in file("testrun")) + .settings(commonSettings) + .dependsOn(generator) + +lazy val overnight = (project in file("overnight")) + .settings(commonSettings) + .dependsOn(testrun, fileop) + +lazy val fileop = (project in file("fileop")) + .settings(commonSettings) diff --git a/tools/riscv-torture/config/default.config b/tools/riscv-torture/config/default.config new file mode 100644 index 0000000..233ac5a --- /dev/null +++ b/tools/riscv-torture/config/default.config @@ -0,0 +1,52 @@ +torture.generator.nseqs 200 +torture.generator.memsize 1024 +torture.generator.fprnd 0 +torture.generator.amo false +torture.generator.mul true +torture.generator.divider true +torture.generator.segment true +torture.generator.loop true +torture.generator.loop_size 64 + +torture.generator.mix.xmem 10 +torture.generator.mix.xbranch 20 +torture.generator.mix.xalu 70 +torture.generator.mix.fgen 0 +torture.generator.mix.fpmem 0 +torture.generator.mix.fax 0 +torture.generator.mix.fdiv 0 +torture.generator.mix.vec 0 + +torture.generator.vec.vf 0 +torture.generator.vec.seq 0 +torture.generator.vec.memsize 128 +torture.generator.vec.numsregs 64 +torture.generator.vec.mul false +torture.generator.vec.div false +torture.generator.vec.mix true +torture.generator.vec.fpu false +torture.generator.vec.fma false +torture.generator.vec.fcvt false +torture.generator.vec.fdiv false +torture.generator.vec.amo false +torture.generator.vec.seg false +torture.generator.vec.stride false +torture.generator.vec.pred_alu true +torture.generator.vec.pred_mem true + +torture.generator.vec.mix.valu 20 +torture.generator.vec.mix.vpop 60 +torture.generator.vec.mix.vmem 20 +torture.generator.vec.mix.vonly 0 + +torture.testrun.maxcycles 10000000 +torture.testrun.virtual false +torture.testrun.seek true +torture.testrun.dump false +torture.testrun.vec false + +torture.overnight.errors 1 +torture.overnight.minutes 1 +torture.overnight.outdir output/failedtests +torture.overnight.email your@email.address + diff --git a/tools/riscv-torture/config/long_vec.config b/tools/riscv-torture/config/long_vec.config new file mode 100644 index 0000000..c36fcf0 --- /dev/null +++ b/tools/riscv-torture/config/long_vec.config @@ -0,0 +1,49 @@ +torture.generator.nseqs 10 +torture.generator.memsize 1024 +torture.generator.fprnd 0 +torture.generator.amo true +torture.generator.mul true +torture.generator.divider true + +torture.generator.mix.xmem 0 +torture.generator.mix.xbranch 0 +torture.generator.mix.xalu 0 +torture.generator.mix.fgen 0 +torture.generator.mix.fpmem 0 +torture.generator.mix.fax 0 +torture.generator.mix.fdiv 0 +torture.generator.mix.vec 100 + +torture.generator.vec.vf 1 +torture.generator.vec.seq 40 +torture.generator.vec.memsize 128 +torture.generator.vec.numsregs 64 +torture.generator.vec.mul false +torture.generator.vec.div false +torture.generator.vec.mix true +torture.generator.vec.fpu false +torture.generator.vec.fma false +torture.generator.vec.fcvt false +torture.generator.vec.fdiv false +torture.generator.vec.amo true +torture.generator.vec.seg false +torture.generator.vec.stride false +torture.generator.vec.pop false +torture.generator.vec.pred_alu false +torture.generator.vec.pred_mem false + +torture.generator.vec.mix.valu 50 +torture.generator.vec.mix.vmem 50 +torture.generator.vec.mix.vonly 0 + +torture.testrun.maxcycles 10000000 +torture.testrun.virtual false +torture.testrun.seek true +torture.testrun.dump false +torture.testrun.vec true + +torture.overnight.errors 1 +torture.overnight.minutes 1 +torture.overnight.outdir output/failedtests +torture.overnight.email your@email.address + diff --git a/tools/riscv-torture/config/short_vec.config b/tools/riscv-torture/config/short_vec.config new file mode 100644 index 0000000..aa3a7b7 --- /dev/null +++ b/tools/riscv-torture/config/short_vec.config @@ -0,0 +1,49 @@ +torture.generator.nseqs 1 +torture.generator.memsize 1024 +torture.generator.fprnd 0 +torture.generator.amo true +torture.generator.mul true +torture.generator.divider true + +torture.generator.mix.xmem 0 +torture.generator.mix.xbranch 0 +torture.generator.mix.xalu 0 +torture.generator.mix.fgen 0 +torture.generator.mix.fpmem 0 +torture.generator.mix.fax 0 +torture.generator.mix.fdiv 0 +torture.generator.mix.vec 100 + +torture.generator.vec.vf 1 +torture.generator.vec.seq 20 +torture.generator.vec.memsize 128 +torture.generator.vec.numsregs 64 +torture.generator.vec.mul false +torture.generator.vec.div false +torture.generator.vec.mix true +torture.generator.vec.fpu false +torture.generator.vec.fma false +torture.generator.vec.fcvt false +torture.generator.vec.fdiv false +torture.generator.vec.amo false +torture.generator.vec.seg false +torture.generator.vec.stride false +torture.generator.vec.pred_alu true +torture.generator.vec.pred_mem true + +torture.generator.vec.mix.valu 20 +torture.generator.vec.mix.vpop 60 +torture.generator.vec.mix.vmem 20 +torture.generator.vec.mix.vonly 0 + +torture.testrun.maxcycles 10000000 +torture.testrun.virtual false +torture.testrun.seek true +torture.testrun.dump false +torture.testrun.vec true + +torture.overnight.errors 1 +torture.overnight.minutes 1 +torture.overnight.outdir output/failedtests +torture.overnight.email your@email.address + diff --git a/tools/riscv-torture/fileop/src/main/scala/main.scala b/tools/riscv-torture/fileop/src/main/scala/main.scala new file mode 100644 index 0000000..8535e1c --- /dev/null +++ b/tools/riscv-torture/fileop/src/main/scala/main.scala @@ -0,0 +1,168 @@ +package torture +package fileop + +import scala.sys.process._ +import scalax.file.Path +import scalax.file.FileSystem +import java.io.File + +object FileOperations extends App +{ + override def main(args: Array[String]) = { System.exit(0) } + + def compile(dir: Path, compiledFile: Path) = + { + val workDir = new File(dir.toAbsolute.normalize.path) + Process("make -j", workDir).! + if (!compiledFile.exists) Process("make -j", workDir).! + } + + def compileRemote(dir: Path, compiledFile: Path, host: String, options: String) = + { + val sshcmd = "ssh " + options + " " + host + " cd " + dir.path + " ; make -j" + println(sshcmd) + Process(sshcmd).! + if (!remotePathExists(compiledFile, host, options)) Process(sshcmd).! + } + + def clean(dir: Path) = + { + val workDir = new File(dir.toAbsolute.normalize.path) + Process("make clean", workDir).! + } + + def cleanRemote(dir: Path, host: String, options: String) = + { + val sshcmd = "ssh " + options + " " + host + " cd " + dir.path + " ; make clean" + println(sshcmd) + Process(sshcmd).! + } + + def gitcheckout(oldDir: Path, newDir: Path, commit: String): Unit = + { + val canonold = oldDir.toAbsolute.normalize.path + val canonnew = newDir.toAbsolute.normalize.path + if (newDir.exists) return + Process("cp -r " + canonold + " " + canonnew).! + if (commit.toLowerCase != "none") + { + println("Checking out commit " + commit + " in " + canonnew) + val out = Process("git checkout " + commit, new File(canonnew)).!! + println(out) + } + } + + def gitcheckoutRemote(oldDir: Path, newDir: Path, commit: String, host: String, options: String) = + { + if (!remotePathExists(newDir, host, options)) + { + val sshcmd = "ssh " + options + " " + host + " cp -r " + oldDir.path + " " + newDir.path + Process(sshcmd).! + if (commit.toLowerCase != "none") + { + val sshgitcheckout = "ssh " + options + " " + host + " cd " + newDir.path + " ; git checkout " + commit + println(sshgitcheckout) + println(Process(sshgitcheckout).!!) + } + } + } + + def remotePathExists(remote: Path, host: String, options: String): Boolean = + { + val remoteParentPath = remote.parent.get + val cmd = ("ssh "+options+" "+host+" ls " + remoteParentPath.path) + val output = (cmd.!!).split("\n") + val remoteExists = output.contains(remote.name) + remoteExists + } + + def copy(from: Path, to: Path): Unit = + { + from.copyTo(to, replaceExisting=true) + } + + def scpFileBack(remotePath: Path, localPath: Path, host: String, options: String): Unit = + { + val localStr = localPath.path + val remoteStr = remotePath.path + if (remotePathExists(remotePath, host, options)) + { + println("Copying remote file " + remotePath.name + " from " + host + "to directory " + localStr) + val cmd = "scp " +options+" "+host+":"+remoteStr + " " + localStr + println(cmd) + val exitCode = cmd.! + assert(exitCode == 0, println("SCP failed to successfully copy file " + localPath.name)) + println("Successfully copied remote " + host + " file to directory.\n") + } else { + println("Could not find remote file " + remoteStr + " on " + host) + } + } + + def scp(localPath: Path, remotePath: Path, host: String, options: String): Unit = + { + def scpFile(localPath: Path, remotePath: Path): Unit = + { + val localStr = localPath.path + val remoteStr = remotePath.path + println("Copying file " + localPath.name + " to " + host + " remote directory " + remoteStr) + val cmd = "scp " +options+" "+localStr+" "+host+":"+remoteStr + println(cmd) + val exitCode = cmd.! + assert(exitCode == 0, println("SCP failed to successfully copy file " + localPath.name)) + println("Successfully copied file to remote "+host+" directory.\n") + } + def compressDir(dir: String, tgz: String): Unit = + { + println("Compressing directory to " + tgz) + val tarcmd = "tar -czf " + tgz + " " + dir + println(tarcmd) + val out = tarcmd.! + assert (out == 0, println("Failed to properly compress directory.")) + println("Successfully compressed directory to " + tgz + "\n") + } + def extractDir(remoteTgz: String, remoteDir: String): Unit = + { + println("Extracting "+remoteTgz+" to "+host+" remote directory " + remoteDir) + val extractcmd = "ssh "+options+" "+host+" tar -xzf " + remoteTgz +" -C " + remoteDir + println (extractcmd) + val out = extractcmd.! + assert (out == 0, println("Failed to extract remote file " + remoteTgz + " to directory " + remoteDir)) + println("Successfully extracted to remote directory " + remoteDir + "\n") + } + + assert(localPath.exists, println("Local object to be copied does not exist.")) + if (localPath.isDirectory) + { + //Zip it up, scp it, then unzip + val canonPath: Path = localPath.toAbsolute.normalize + val remoteParentPath = remotePath.parent.get + val tgzName = canonPath.name + ".tgz" + val tgzPath: Path = Path.fromString("../" + tgzName) + val remoteTgzPath: Path = (remoteParentPath / Path.fromString(tgzName)) + + val cmd = ("ssh "+options+" "+host+" ls " + remoteParentPath.path) + val output = (cmd.!!).split("\n") + val remoteExists = output.contains(remotePath.name) + val remoteTgzExists = output.contains(tgzName) + + if (remoteExists) { + println("Remote directory already exists. Skipping copy process.") + } else { + if (remoteTgzExists) { + println(tgzName + " already exists on the remote "+host+" directory. Skipping transfer process.") + } else { + if(!tgzPath.exists) { + compressDir(".", "../"+tgzName) + } else { + println(tgzName+" already exists. Skipping compression process.") + } + scpFile(tgzPath, remoteTgzPath) + } + val out2 = ("ssh "+options+" "+host+" mkdir " + remotePath.path).!! + extractDir(remoteTgzPath.path, remotePath.path) + } + } else { + scpFile(localPath, remotePath) + } + } +} diff --git a/tools/riscv-torture/gen_loop.diff b/tools/riscv-torture/gen_loop.diff new file mode 100644 index 0000000..1c07e15 --- /dev/null +++ b/tools/riscv-torture/gen_loop.diff @@ -0,0 +1,61 @@ +diff --git a/Makefile b/Makefile +index a85579f..5ec0ed6 100644 +--- a/Makefile ++++ b/Makefile +@@ -6,6 +6,7 @@ C_SIM := ../emulator/emulator-rocketchip-$(RTL_CONFIG) + R_SIM := ../vsim/simv-rocketchip-$(RTL_CONFIG) + TEST := output/test.S + OPTIONS := $(empty) ++NUM := 0 + SUITE := output + CONFIG := config/default.config + COMMIT := none +@@ -20,7 +21,7 @@ GITCMT := $(subst $(space),$(gitopt),$(COMMIT)) + cnight rnight crnight csuite rsuite \ + + gen: +- $(SBT) 'generator/run $(OPTIONS)' ++ $(SBT) 'generator/run -n $(NUM)' + + csuite: + for i in `ls $(SUITE) | grep .S` ; do echo $$i ; \ +diff --git a/generator/src/main/scala/main.scala b/generator/src/main/scala/main.scala +index d5a79f5..a7b3b49 100644 +--- a/generator/src/main/scala/main.scala ++++ b/generator/src/main/scala/main.scala +@@ -8,7 +8,7 @@ import java.util.Properties + import scala.collection.JavaConversions._ + + case class Options(var outFileName: String = "test", +- var confFileName: String = "config/default.config") ++ var confFileName: String = "config/default.config", var numOutFiles: Int = 0) + + object Generator extends App + { +@@ -17,15 +17,25 @@ object Generator extends App + val parser = new OptionParser[Options]("generator/run") { + opt[String]('C', "config") valueName("") text("config file") action {(s: String, c) => c.copy(confFileName = s)} + opt[String]('o', "output") valueName("") text("output filename") action {(s: String, c) => c.copy(outFileName = s)} ++ opt[Int]('n', "numfiles") valueName("") text("number of output files") action {(n: Int, c) => c.copy(numOutFiles = n)} + } + parser.parse(args, Options()) match { + case Some(opts) => +- generate(opts.confFileName, opts.outFileName) ++ generate_loop(opts.confFileName, opts.outFileName, opts.numOutFiles) + case None => + System.exit(1) //error message printed by parser + } + } + ++ def generate_loop(confFile: String, outFileName: String, numOutFiles: Int) = { ++ if (numOutFiles > 0) { ++ for (i <- 0 to (numOutFiles-1)) ++ generate(confFile, outFileName + ("_%03d" format (i))) ++ } else { ++ generate(confFile, outFileName) ++ } ++ } ++ + def generate(confFile: String, outFileName: String): String = { + val config = new Properties() + val in = new FileInputStream(confFile) diff --git a/tools/riscv-torture/generator/src/main/scala/DataChunk.scala b/tools/riscv-torture/generator/src/main/scala/DataChunk.scala new file mode 100644 index 0000000..664971b --- /dev/null +++ b/tools/riscv-torture/generator/src/main/scala/DataChunk.scala @@ -0,0 +1,39 @@ +package torture + +abstract class DataChunk + +class StringData(contents: String) extends DataChunk +{ + override def toString = contents +} + +class MemDump(mem: Mem) extends DataChunk +{ + override def toString = mem.dumpdata +} + +object MemDump +{ + def apply(mem: Mem) = new MemDump(mem) +} + +class MemAddrDump(mem: Mem, addrfn: (Int) => Int, memsize: Int) extends DataChunk +{ + override def toString = mem.dumpaddrs(addrfn, memsize) +} + +object MemAddrDump +{ + def apply(mem: Mem, addrfn: (Int) => Int, memsize: Int) = new MemAddrDump(mem, addrfn, memsize) +} + +class ProgSegDump(pseg: ProgSeg) extends DataChunk +{ + override def toString = pseg.toString +} + +object ProgSegDump +{ + def apply(pseg: ProgSeg) = new ProgSegDump(pseg) +} + diff --git a/tools/riscv-torture/generator/src/main/scala/HWReg.scala b/tools/riscv-torture/generator/src/main/scala/HWReg.scala new file mode 100644 index 0000000..06b211c --- /dev/null +++ b/tools/riscv-torture/generator/src/main/scala/HWReg.scala @@ -0,0 +1,91 @@ +package torture + +import scala.collection.mutable.ArrayBuffer +import Rand._ + +object HWRegState extends Enumeration +{ + type HWRegState = Value + val VIS, HID, HID2HID, HID2VIS, VIS2HID, VIS2VIS = Value +} + +import HWRegState._ +class HWReg(val name: String, val readable: Boolean, val writable: Boolean) +{ + var state = VIS + var readers = 0 + var backup_state = VIS + var backup_readers = 0 + + def is_state(states: HWRegState*) = states.toList.contains(state) + + def is_visible() = is_state(VIS, VIS2VIS) + + def is_unallocated = is_state(VIS, HID) + //TODO: should this also check readers == 0? + + override def toString = name + + def backup() = + { + backup_state = state + backup_readers = readers + } + + def restore() = + { + state = backup_state + readers = backup_readers + } +} + +object HWReg +{ + // These filters are for allocation purposes + def filter_read_zero = (hwreg: HWReg) => (hwreg.name == "x0" || hwreg.name == "x0_shadow") + def filter_read_any = (hwreg: HWReg) => hwreg.readable + def filter_read_any_other(other: Reg)(hwreg: HWReg) = (hwreg.readable && hwreg.name != other.hwreg.name) + def filter_read_visible = (hwreg: HWReg) => hwreg.readable && hwreg.is_state(VIS,VIS2VIS) + def filter_write_ra = (hwreg: HWReg) => hwreg.name == "x1" && filter_write_visible(hwreg) + def filter_write_visible = (hwreg: HWReg) => hwreg.writable && hwreg.is_state(VIS,HID) + def filter_write_hidden = (hwreg: HWReg) => hwreg.writable && (hwreg.is_state(HID) || hwreg.is_state(VIS) && hwreg.readers == 0) + def filter_write_visible_other(other: Reg)(hwreg: HWReg) = (hwreg.name != other.hwreg.name && hwreg.writable && hwreg.is_state(VIS,HID)) + def filter_write_hidden_other(other: Reg)(hwreg: HWReg) = (hwreg.name != other.hwreg.name && hwreg.writable && (hwreg.is_state(HID) || hwreg.is_state(VIS) && hwreg.readers == 0)) + def filter_write_dep(regs: List[Reg]) = + { + if (regs.forall(_.hwreg.is_visible)) filter_write_visible + else filter_write_hidden + } + def filter_write_dep_other(other: Reg, regs: List[Reg]) = + { + if (regs.forall(_.hwreg.is_visible)) filter_write_visible_other(other) _ + else filter_write_hidden_other(other) _ + } + + def alloc_read = (hwreg: HWReg) => hwreg.readers += 1 + def alloc_write(visible: Boolean)(hwreg: HWReg) = + { + if (hwreg.state == VIS) + { + if (visible) hwreg.state = VIS2VIS + else hwreg.state = VIS2HID + } + else if (hwreg.state == HID) + { + if (visible) hwreg.state = HID2VIS + else hwreg.state = HID2HID + } + else println("bug in do_write") + } + def alloc_write_dep(regs: List[Reg]) = alloc_write(regs.forall(_.hwreg.is_visible)) _ + + def free_read = (hwreg: HWReg) => hwreg.readers -= 1 + def free_write = (hwreg: HWReg) => + { + if (hwreg.state == VIS2VIS || hwreg.state == HID2VIS) hwreg.state = VIS + else if (hwreg.state == VIS2HID || hwreg.state == HID2HID) hwreg.state = HID + else println("bug in free_write") + } +} + + diff --git a/tools/riscv-torture/generator/src/main/scala/HWRegPool.scala b/tools/riscv-torture/generator/src/main/scala/HWRegPool.scala new file mode 100644 index 0000000..027a311 --- /dev/null +++ b/tools/riscv-torture/generator/src/main/scala/HWRegPool.scala @@ -0,0 +1,282 @@ +package torture + +import scala.collection.mutable.ArrayBuffer +import Rand._ + +import HWRegState._ + +class HWRegPool +{ + val hwregs = new ArrayBuffer[HWReg] + + def backup() = { hwregs.map((x) => x.backup()) } + def restore() = { hwregs.map((x) => x.restore()) } + + def is_fully_unallocated = hwregs.forall(_.is_unallocated) + def size = hwregs.length +} + +trait ScalarRegPool extends HWRegPool +{ + val name: String + val regname: String + val ldinst: String + val stinst: String + + def init_regs() = + { + var s = name + "_init:\n" + s += "\tla x31, " + name + "_init_data\n" + for (i <- 0 to hwregs.length-1) + s += "\t" + ldinst + " " + hwregs(i) + ", " + 8*i + "(x31)\n" + s += "\n" + s + } + + def save_regs() = + { + var s = "\tla x1, " + name + "_output_data\n" + for (i <- 0 to hwregs.length-1) + if (hwregs(i).is_visible) + s += "\t" + stinst + " " + hwregs(i) + ", " + 8*i + "(x1)\n" + s += "\n" + s + } + + def init_regs_data() = + { + var s = "\t.align 8\n" + s += name + "_init_data:\n" + for (i <- 0 to hwregs.length-1) + s += (regname + i + "_init:\t.dword " + "0x%016x\n" format rand_biased) //Change randomization for FRegs + s += "\n" + s + } + + def output_regs_data() = + { + var s = "\t.align 8\n" + s += name + "_output_data:\n" + for (i <- 0 to hwregs.length-1) + s += (regname + i + "_output:\t.dword 0x%016x\n" format rand_dword) + s += "\n" + s + } +} + +trait PoolsMaster extends HWRegPool +{ + val regpools: ArrayBuffer[HWRegPool] + override val hwregs = new ArrayBuffer[HWReg] //Override this in subclasses + override def is_fully_unallocated = regpools.forall(_.is_fully_unallocated) + override def size = regpools.map(_.size).sum + def extract_pools() = + { + regpools + } + override def backup() = + { + regpools.map(_.backup()).flatten + } + override def restore() = + { + regpools.map(_.restore()).flatten + } +} + +class XRegsPool extends ScalarRegPool +{ + val (name, regname, ldinst, stinst) = ("xreg", "reg_x", "lw", "sw") + + hwregs += new HWReg("x0", true, false) + for (i <- 1 to 31) + hwregs += new HWReg("x" + i.toString(), true, true) + + override def save_regs() = + { + hwregs(1).state = HID + super.save_regs() + } +} + +class FRegsMaster extends ScalarRegPool with PoolsMaster +{ + val (name,regname,ldinst,stinst) = ("freg","reg_f","fld","fsd") // and flw and fsw + val s_reg_num = new ArrayBuffer[Int] + val d_reg_num = new ArrayBuffer[Int] + + for (n <- 0 to 31) + if(rand_range(0, 1) == 0) s_reg_num += n + else d_reg_num += n + + // Ensure each pool has at least 5 members + while(s_reg_num.length < 5) + { + val mv_n = rand_pick(d_reg_num) + d_reg_num -= mv_n + s_reg_num += mv_n + } + + while(d_reg_num.length < 5) + { + val mv_n = rand_pick(s_reg_num) + s_reg_num -= mv_n + d_reg_num += mv_n + } + + val s_regpool = new FRegsPool(s_reg_num.toArray) + val d_regpool = new FRegsPool(d_reg_num.toArray) + val regpools = ArrayBuffer(s_regpool.asInstanceOf[HWRegPool], + d_regpool.asInstanceOf[HWRegPool]) + override val hwregs = regpools.map(_.hwregs).flatten + + override def init_regs() = //Wrapper function + { + var s = "freg_init:\n"+"freg_s_init:\n"+"\tla x1, freg_init_data\n" + for ((i, curreg) <- s_reg_num.zip(s_regpool.hwregs)) + s += "\tflw" + " " + curreg + ", " + 8*i + "(x1)\n" + s += "\n"+"freg_d_init:\n"+"\tla x1, freg_init_data\n" + for ((i, curreg) <- d_reg_num.zip(d_regpool.hwregs)) + s += "\tfld" + " " + curreg + ", " + 8*i + "(x1)\n" + s += "\n\n" + s + } + override def save_regs() = //Wrapper function + { + var s = "freg_save:\n"+"\tla x1, freg_output_data\n" + for ((i, curreg) <- s_reg_num.zip(s_regpool.hwregs)) + if (curreg.is_visible) + s += "\tfsw" + " " + curreg + ", " + 8*i + "(x1)\n" + s += "\n"+"\tla x1, freg_output_data\n" + for ((i, curreg) <- d_reg_num.zip(d_regpool.hwregs)) + if (curreg.is_visible) + s += "\tfsd" + " " + curreg + ", " + 8*i + "(x1)\n" + s += "\n\n" + s + } +} + +class FRegsPool(reg_nums: Array[Int] = (0 to 31).toArray) extends HWRegPool +{ + for (i <- reg_nums) + hwregs += new HWReg("f" + i.toString(), true, true) +} + +class VRegsMaster(num_xregs: Int, num_pregs: Int, num_sregs: Int) extends PoolsMaster +{ + assert(num_xregs >= 5, "For VRegMaster, num_xregs >=5 enforced") + assert(num_pregs >= 1, "For VRegMaster, num_pregs >=1 enforced") + + val x_reg_num = (0 to (num_xregs-1)) + val p_reg_num = (0 to (num_pregs-1)) + val s_reg_num = (0 to (num_sregs-1)) + + val x_regpool = new VXRegsPool(x_reg_num.toArray) + val p_regpool = new VPRegsPool(p_reg_num.toArray) + val s_regpool = new VSRegsPool(s_reg_num.toArray) + val a_regpool = new VARegsPool() + val regpools = + ArrayBuffer(x_regpool.asInstanceOf[HWRegPool], p_regpool.asInstanceOf[HWRegPool], + s_regpool.asInstanceOf[HWRegPool], a_regpool.asInstanceOf[HWRegPool]) + override val hwregs = regpools.map(_.hwregs).flatten + + def init_regs() = + { + var s = "vreg_init:\n" + s += s_regpool.init_regs() + s + } + def save_regs() = + { + var s = "vreg_save:\n" + s += s_regpool.save_regs() + s + } + def init_regs_data() = + { + var s = "vreg_init_data:\n" + s += s_regpool.init_regs_data() + s + } + def output_regs_data() = + { + var s = "vreg_output_data:\n" + s += s_regpool.output_regs_data() + s + } +} + +class VXRegsPool(reg_nums: Array[Int] = (0 to 255).toArray) extends HWRegPool +{ + for (i <- reg_nums) + hwregs += new HWReg("vv" + i.toString(), true, true) +} + +class VPRegsPool(reg_nums: Array[Int] = (0 to 15).toArray) extends HWRegPool +{ + for (i <- reg_nums) + hwregs += new HWReg("vp" + i.toString(), true, true) +} + +class VSRegsPool(reg_nums: Array[Int] = (0 to 255).toArray) extends HWRegPool +{ + hwregs += new HWReg("vs0", true, false) + for (i <- reg_nums.drop(1)) + hwregs += new HWReg("vs" + i.toString(), true, true) + def init_regs() = + { + var s = "vsreg_init:\n"+"\tla x1, vsreg_init_data\n" + for ((i, curreg) <- reg_nums.zip(hwregs)) + { + s += "\tld" + " x2, " + 8*i + "(x1)\n" + s += "\tvmcs"+ " " + curreg + ", x2\n" + } + s += "\n\n" + s + } + def save_regs() = + { + hwregs(1).state = HID + var s = "vsreg_save:\n"+"\tla x1, vsreg_output_data\n" + s += "\tvmcs vs1, x1\n" + s += "\tlui x1, %hi(vsreg_save_vf)\n" + s += "\tvf %lo(vsreg_save_vf)(x1)\n" + s += "\tj vsreg_save_end\n" + s += ".align 3\n" + s += "vsreg_save_vf:\n" + for (curreg <- hwregs.drop(2)) + if (curreg.is_visible) + { + s += "\tvssd vs1, " + curreg + "\n" + s += "\tvaddi vs1, vs1, 8\n" + } + s += "\tvstop\n" + s += "vsreg_save_end:\n\n" + s + } + def init_regs_data() = + { + var s = "\t.align 8\n" + s += "vsreg_init_data:\n" + for (i <- 0 to hwregs.length-1) + s += ("vs" + i + "_init:\t.dword " + "0x%016x\n" format rand_biased) + s += "\n" + s + } + + def output_regs_data() = + { + var s = "\t.align 8\n" + s += "vsreg_output_data:\n" + for (i <- 0 to hwregs.length-1) + s += ("vs" + i + "_output:\t.dword 0x%016x\n" format rand_dword) + s += "\n" + s + } +} + +class VARegsPool(reg_nums: Array[Int] = (0 to 31).toArray) extends HWRegPool +{ + for (i <- reg_nums) + hwregs += new HWReg("va" + i.toString(), true, true) +} diff --git a/tools/riscv-torture/generator/src/main/scala/HWShadowReg.scala b/tools/riscv-torture/generator/src/main/scala/HWShadowReg.scala new file mode 100644 index 0000000..e14fde1 --- /dev/null +++ b/tools/riscv-torture/generator/src/main/scala/HWShadowReg.scala @@ -0,0 +1,17 @@ +package torture + +import scala.collection.mutable.ArrayBuffer +import Rand._ + +import HWRegState._ + +class HWShadowReg(target:Reg, name: String, readable: Boolean, writeable: Boolean) extends HWReg(name, readable, writeable) +{ + def physical = target + override def toString = target.toString +} + +class ShadowRegPool extends HWRegPool +{ + def pairings(selecting: (HWReg => Boolean)) = hwregs.filter(selecting).map((reg:HWReg) => (reg, reg.asInstanceOf[HWShadowReg].physical)) +} diff --git a/tools/riscv-torture/generator/src/main/scala/Inst.scala b/tools/riscv-torture/generator/src/main/scala/Inst.scala new file mode 100644 index 0000000..8b9c6d5 --- /dev/null +++ b/tools/riscv-torture/generator/src/main/scala/Inst.scala @@ -0,0 +1,535 @@ +package torture + +class Inst(opcode: String, val operands: Array[Operand]) +{ + def optype(): String = { + if (is_alu) return "alu" + if (is_cmp) return "cmp" + if (is_branch) return "branch" + if (is_jalr) return "jalr" + if (is_jmp) return "jmp" + if (is_la) return "la" + if (is_mem) return "mem" + if (is_amo) return "amo" + if (is_misc) return "misc" + if (is_fpalu) return "fpalu" + if (is_fpcmp) return "fpcmp" + if (is_fpfma) return "fpfma" + if (is_fpmem) return "fpmem" + if (is_fpcvt) return "fpcvt" + if (is_fpmisc) return "fpmisc" + if (is_vshared) return "vshared" + if (is_valu) return "valu" + if (is_vfpalu) return "vfpalu" + if (is_vfpfma) return "vfpfma" + if (is_vfpcvt) return "vfpcvt" + if (is_vsmem) return "vsmem" + if (is_vshared) return "vshared" + if (is_vcmp) return "vcmp" + if (is_vpred) return "vpred" + if (is_vmem) return "vmem" + if (is_vamo) return "vamo" + if (is_vmisc) return "vmisc" + return "unknown" //Shouldn't return this. + } + + def opcode(): String = { return opcode } + + def is_branch = List("beq", "bne", "blt", "bge", "bltu", "bgeu").contains(opcode) + + def is_jalr = List("jalr").contains(opcode) + + def is_jmp = List("jal").contains(opcode) + + def is_la = opcode == "la" + + def is_mem = List("lb", "lh", "lw", "ld", "lbu", "lhu", "lwu", "sb", "sh", "sw", "sd").contains(opcode) + + def is_amo = List("amoadd.w", "amoswap.w", "amoand.w", "amoor.w", "amomin.w", "amominu.w", + "amomax.w", "amomaxu.w", "amoxor.w", "amoadd.d", "amoswap.d", "amoand.d", "amoor.d", + "amomin.d", "amominu.d", "amomax.d", "amomaxu.d", "amoxor.d").contains(opcode) + + def is_cmp = List("slti", "sltiu", "slt", "sltu").contains(opcode) + + def is_alu = List("addi", "slli", "xori", "srli", "srai", "ori", "andi", + "add", "sub", "sll", "xor", "srl", "sra", "or", "and", "mul", "mulh", + "mulhsu", "mulhu", "div", "divu", "rem", "remu", "lui", "addiw", "slliw", "srliw", + "sraiw", "addw", "subw", "sllw", "srlw", "sraw", "mulw", "divw", "divuw", "remw", + "remuw").contains(opcode) + + def is_fpmem = List("flw", "fld", "fsw", "fsd").contains(opcode) + + def is_fpalu = List("fadd.s", "fsub.s", "fmul.s", "fdiv.s", "fsqrt.s", "fmin.s", "fmax.s", + "fadd.d", "fsub.d", "fmul.d", "fdiv.d", "fsqrt.d", "fmin.d", "fmax.d", + "fsgnj.s", "fsgnjn.s", "fsgnjx.s", "fsgnj.d", "fsgnjn.d", "fsgnjx.d").contains(opcode) + + def is_fpfma = List("fmadd.s", "fmsub.s", "fnmsub.s", "fnmadd.s", + "fmadd.d", "fmsub.d", "fnmsub.d", "fnmadd.d").contains(opcode) + + def is_fpcvt = List("fcvt.s.d", "fcvt.d.s", "fcvt.s.l", "fcvt.s.lu", "fcvt.s.w", + "fcvt.s.wu", "fcvt.d.l", "fcvt.d.lu", "fcvt.d.w", "fcvt.d.wu", "fcvt.l.s", + "fcvt.lu.s", "fcvt.w.s", "fcvt.wu.s", "fcvt.l.d", "fcvt.lu.d", + "fcvt.w.d", "fcvt.wu.d").contains(opcode) + + def is_fpmisc = List("fmovz", "fmovn", "frsr", "fssr", "fmv.s.x", "fmv.x.s", + "fmv.d.x", "fmv.x.d").contains(opcode) + + def is_fpcmp = List("feq.s", "flt.s", "fle.s", "feq.d", "flt.d", "fle.d").contains(opcode) + + def is_misc = List("syscall", "break", "rdcycle", "rdtime", "rdinstret", + "nop", "li", "mfpcr", "mtpcr", "auipc", "movz", "movn", "fence.i", "fence").contains(opcode) + + def is_vshared = List("vaddi", "vslli", "vxori", "vsrli", "vsrai", "vori", "vandi", "vlui", + "vaddiw", "vslliw", "vsrliw", "vsraiw").contains(opcode) + + def is_valu = List("vadd", "vsub", "vsll", "vxor", "vsrl", "vsra", "vor", "vand", "vmul", "vmulh", + "vmulhsu", "vmulhu", "vdiv", "vdivu", "vrem", "vremu", "vaddw", "vsubw", "vsllw", + "vsrlw", "vsraw", "vmulw", "vdivw", "vdivuw", "vremw", "vremuw").contains(opcode) + + def is_vpred = List("vpop", "vpset", "vpclear").contains(opcode) + + def is_vcmp = List("vcmpeq", "vcmplt", "vcmpltu", "vcmpfeq", "vcmpflt", "vcmfle").contains(opcode) + + def is_vfpalu = List("vfadd.s", "vfsub.s", "vfmul.s", "vfdiv.s", "vfsqrt.s", "vfmin.s", "vfmax.s", + "vfadd.d", "vfsub.d", "vfmul.d", "vfdiv.d", "vfsqrt.d", "vfmin.d", "vfmax.d", + "vfsgnj.s", "vfsgnjn.s", "vfsgnjx.s", "vfsgnj.d", "vfsgnjn.d", "vfsgnjx.d").contains(opcode) + + def is_vfpfma = List("vfmadd.s", "vfmsub.s", "vfnmsub.s", "vfnmadd.s", + "vfmadd.d", "vfmsub.d", "vfnmsub.d", "vfnmadd.d").contains(opcode) + + def is_vfpcvt = List("vfcvt.s.d", "vfcvt.d.s", "vfcvt.s.l", "vfcvt.s.lu", "vfcvt.s.w", + "vfcvt.s.wu", "vfcvt.d.l", "vfcvt.d.lu", "vfcvt.d.w", "vfcvt.d.wu", "vfcvt.l.s", + "vfcvt.lu.s", "vfcvt.w.s", "vfcvt.wu.s", "vfcvt.l.d", "vfcvt.lu.d", + "vfcvt.w.d", "vfcvt.wu.d").contains(opcode) + + def is_vsmem = List("vlsb", "vlsh", "vlsw", "vlsd", "vlsbu", "vlshu", "vlswu", "vssb", "vssh", "vssw", "vssd", + "vlab", "vlah", "vlaw", "vlad", "vlabu", "vlahu", "vlawu", "vsab", "vsah", "vsaw", "vsad").contains(opcode) + + def is_vmem = List("vlb", "vlh", "vlw", "vld", "vlbu", "vlhu", "vlwu", "vsb", "vsh", "vsw", "vsd", + "vlsegb", "vlsegh", "vlsegw", "vlsegd", "vlsegbu", "vlseghu", "vlsegwu", "vssegb", "vssegh", "vssegw", "vssegd", + "vlstb", "vlsth", "vlstw", "vlstd", "vlstbu", "vlsthu", "vlstwu", "vsstb", "vssth", "vsstw", "vsstd", + "vlsegstb", "vlsegsth", "vlsegstw", "vlsegstd", "vlsegstbu", "vlsegsthu", "vlsegstwu", "vssegstb", "vssegsth", "vssegstw", "vssegstd", + "vlxb", "vlxh", "vlxw", "vlxd", "vlxbu", "vlxhu", "vlxwu", "vsxb", "vsxh", "vsxw", "vsxd", + "vlsegxb", "vlsegxh", "vlsegxw", "vlsegxd", "vlsegxbu", "vlsegxhu", "vlsegxwu", "vssegxb", "vssegxh", "vssegxw", "vssegxd").contains(opcode) + + def is_vamo = List("vamoadd.w", "vamoswap.w", "vamoand.w", "vamoor.w", "vamomin.w", "vamominu.w", + "vamomax.w", "vamomaxu.w", "vamoxor.w", "vamoadd.d", "vamoswap.d", "vamoand.d", "vamoor.d", + "vamomin.d", "vamominu.d", "vamomax.d", "vamomaxu.d", "vamoxor.d").contains(opcode) + + def is_vmisc = List("vsetcfg", "vstop", "vsetvl", "veidx", "vf", + "vmcs", "vmca", "fence").contains(opcode) + + override def toString = + { + operands.find(op => op.isInstanceOf[PredReg]) match { + case Some(pred) => pred + " " + opcode + + operands.filterNot(op => op.isInstanceOf[PredReg]).mkString(" ", ", ", "") + case None => opcode + operands.mkString(" ", ", ", "") + } + } +} + +class Opcode(val name: String) +{ + def apply(opnds: Operand*) = new Inst(name, opnds.toArray) +} + +object J extends Opcode("j") +object JAL extends Opcode("jal") +object JALR extends Opcode("jalr") +object BEQ extends Opcode("beq") +object BNE extends Opcode("bne") +object BLT extends Opcode("blt") +object BGE extends Opcode("bge") +object BLTU extends Opcode("bltu") +object BGEU extends Opcode("bgeu") + +object LA extends Opcode("la") +object LB extends Opcode("lb") +object LH extends Opcode("lh") +object LW extends Opcode("lw") +object LD extends Opcode("ld") +object LBU extends Opcode("lbu") +object LHU extends Opcode("lhu") +object LWU extends Opcode("lwu") +object SB extends Opcode("sb") +object SH extends Opcode("sh") +object SW extends Opcode("sw") +object SD extends Opcode("sd") + +object AMOADD_W extends Opcode("amoadd.w") +object AMOSWAP_W extends Opcode("amoswap.w") +object AMOAND_W extends Opcode("amoand.w") +object AMOOR_W extends Opcode("amoor.w") +object AMOMIN_W extends Opcode("amomin.w") +object AMOMINU_W extends Opcode("amominu.w") +object AMOMAX_W extends Opcode("amomax.w") +object AMOMAXU_W extends Opcode("amomaxu.w") +object AMOXOR_W extends Opcode("amoxor.w") +object AMOADD_D extends Opcode("amoadd.d") +object AMOSWAP_D extends Opcode("amoswap.d") +object AMOAND_D extends Opcode("amoand.d") +object AMOOR_D extends Opcode("amoor.d") +object AMOMIN_D extends Opcode("amomin.d") +object AMOMINU_D extends Opcode("amominu.d") +object AMOMAX_D extends Opcode("amomax.d") +object AMOMAXU_D extends Opcode("amomaxu.d") +object AMOXOR_D extends Opcode("amoxor.d") + +object ADDI extends Opcode("addi") +object SLLI extends Opcode("slli") +object SLTI extends Opcode("slti") +object SLTIU extends Opcode("sltiu") +object XORI extends Opcode("xori") +object SRLI extends Opcode("srli") +object SRAI extends Opcode("srai") +object ORI extends Opcode("ori") +object ANDI extends Opcode("andi") +object ADD extends Opcode("add") +object SUB extends Opcode("sub") +object SLL extends Opcode("sll") +object SLT extends Opcode("slt") +object SLTU extends Opcode("sltu") +object XOR extends Opcode("xor") +object SRL extends Opcode("srl") +object SRA extends Opcode("sra") +object OR extends Opcode("or") +object AND extends Opcode("and") +object MUL extends Opcode("mul") +object MULH extends Opcode("mulh") +object MULHSU extends Opcode("mulhsu") +object MULHU extends Opcode("mulhu") +object DIV extends Opcode("div") +object DIVU extends Opcode("divu") +object REM extends Opcode("rem") +object REMU extends Opcode("remu") +object LUI extends Opcode("lui") + +object ADDIW extends Opcode("addiw") +object SLLIW extends Opcode("slliw") +object SRLIW extends Opcode("srliw") +object SRAIW extends Opcode("sraiw") +object ADDW extends Opcode("addw") +object SUBW extends Opcode("subw") +object SLLW extends Opcode("sllw") +object SRLW extends Opcode("srlw") +object SRAW extends Opcode("sraw") +object MULW extends Opcode("mulw") +object DIVW extends Opcode("divw") +object DIVUW extends Opcode("divuw") +object REMW extends Opcode("remw") +object REMUW extends Opcode("remuw") + +object FLW extends Opcode("flw") +object FLD extends Opcode("fld") +object FSW extends Opcode("fsw") +object FSD extends Opcode("fsd") + +object FADD_S extends Opcode("fadd.s") +object FSUB_S extends Opcode("fsub.s") +object FMUL_S extends Opcode("fmul.s") +object FDIV_S extends Opcode("fdiv.s") +object FSQRT_S extends Opcode("fsqrt.s") +object FMIN_S extends Opcode("fmin.s") +object FMAX_S extends Opcode("fmax.s") +object FADD_D extends Opcode("fadd.d") +object FSUB_D extends Opcode("fsub.d") +object FMUL_D extends Opcode("fmul.d") +object FDIV_D extends Opcode("fdiv.d") +object FSQRT_D extends Opcode("fsqrt.d") +object FMIN_D extends Opcode("fmin.d") +object FMAX_D extends Opcode("fmax.d") +object FSGNJ_S extends Opcode("fsgnj.s") +object FSGNJN_S extends Opcode("fsgnjn.s") +object FSGNJX_S extends Opcode("fsgnjx.s") +object FSGNJ_D extends Opcode("fsgnj.d") +object FSGNJN_D extends Opcode("fsgnjn.d") +object FSGNJX_D extends Opcode("fsgnjx.d") + +object FMADD_S extends Opcode("fmadd.s") +object FMSUB_S extends Opcode("fmsub.s") +object FNMSUB_S extends Opcode("fnmsub.s") +object FNMADD_S extends Opcode("fnmadd.s") +object FMADD_D extends Opcode("fmadd.d") +object FMSUB_D extends Opcode("fmsub.d") +object FNMSUB_D extends Opcode("fnmsub.d") +object FNMADD_D extends Opcode("fnmadd.d") + +object FCVT_S_D extends Opcode("fcvt.s.d") +object FCVT_D_S extends Opcode("fcvt.d.s") +object FCVT_S_L extends Opcode("fcvt.s.l") +object FCVT_S_LU extends Opcode("fcvt.s.lu") +object FCVT_S_W extends Opcode("fcvt.s.w") +object FCVT_S_WU extends Opcode("fcvt.s.wu") +object FCVT_D_L extends Opcode("fcvt.d.l") +object FCVT_D_LU extends Opcode("fcvt.d.lu") +object FCVT_D_W extends Opcode("fcvt.d.w") +object FCVT_D_WU extends Opcode("fcvt.d.wu") +object FCVT_L_S extends Opcode("fcvt.l.s") +object FCVT_LU_S extends Opcode("fcvt.lu.s") +object FCVT_W_S extends Opcode("fcvt.w.s") +object FCVT_WU_S extends Opcode("fcvt.wu.s") +object FCVT_L_D extends Opcode("fcvt.l.d") +object FCVT_LU_D extends Opcode("fcvt.lu.d") +object FCVT_W_D extends Opcode("fcvt.w.d") +object FCVT_WU_D extends Opcode("fcvt.wu.d") + +object FMV_X_S extends Opcode("fmv.x.s") +object FMV_S_X extends Opcode("fmv.s.x") +object FMV_X_D extends Opcode("fmv.x.d") +object FMV_D_X extends Opcode("fmv.d.x") + +object FRSR extends Opcode("frsr") +object FSSR extends Opcode("fssr") + +object FEQ_S extends Opcode("feq.s") +object FLT_S extends Opcode("flt.s") +object FLE_S extends Opcode("fle.s") +object FEQ_D extends Opcode("feq.d") +object FLT_D extends Opcode("flt.d") +object FLE_D extends Opcode("fle.d") + +object FENCE_I extends Opcode("fence.i") +object FENCE extends Opcode("fence") + +object SYSCALL extends Opcode("syscall") +object BREAK extends Opcode("break") +object RDCYCLE extends Opcode("rdcycle") +object RDTIME extends Opcode("rdtime") +object RDINSTRET extends Opcode("rdinstret") +object NOP extends Opcode("nop") +object LI extends Opcode("li") +object MFPCR extends Opcode("mfpcr") +object MTPCR extends Opcode("mtpcr") +object AUIPC extends Opcode("auipc") + +object VVCFGIVL extends Opcode("vvcfgivl") +object VSTOP extends Opcode("vstop") +object VSETVL extends Opcode("vsetvl") +object VEIDX extends Opcode("veidx") +object VF extends Opcode("vf") +object VMCS extends Opcode("vmcs") +object VMCA extends Opcode("vmca") + +object VADDI extends Opcode("vaddi") +object VSLLI extends Opcode("vslli") +object VXORI extends Opcode("vxori") +object VSRLI extends Opcode("vsrli") +object VSRAI extends Opcode("vsrai") +object VORI extends Opcode("vori") +object VANDI extends Opcode("vandi") +object VLUI extends Opcode("vlui") +object VADDIW extends Opcode("vaddiw") +object VSLLIW extends Opcode("vslliw") +object VSRLIW extends Opcode("vsrliw") +object VSRAIW extends Opcode("vsraiw") + +object VADD extends Opcode("vadd") +object VSUB extends Opcode("vsub") +object VSLL extends Opcode("vsll") +object VXOR extends Opcode("vxor") +object VSRL extends Opcode("vsrl") +object VSRA extends Opcode("vsra") +object VOR extends Opcode("vor") +object VAND extends Opcode("vand") +object VMUL extends Opcode("vmul") +object VMULH extends Opcode("vmulh") +object VMULHSU extends Opcode("vmulhsu") +object VMULHU extends Opcode("vmulhu") +object VDIV extends Opcode("vdiv") +object VDIVU extends Opcode("vdivu") +object VREM extends Opcode("vrem") +object VREMU extends Opcode("vremu") +object VADDW extends Opcode("vaddw") +object VSUBW extends Opcode("vsubw") +object VSLLW extends Opcode("vsllw") +object VSRLW extends Opcode("vsrlw") +object VSRAW extends Opcode("vsraw") +object VMULW extends Opcode("vmulw") +object VDIVW extends Opcode("vdivw") +object VDIVUW extends Opcode("vdivuw") +object VREMW extends Opcode("vremw") +object VREMUW extends Opcode("vremuw") + +object VCMPEQ extends Opcode("vcmpeq") +object VCMPLT extends Opcode("vcmplt") +object VCMPLTU extends Opcode("vcmpltu") +object VCMPFEQ extends Opcode("vcmpfeq") +object VCMPFLT extends Opcode("vcmpflt") +object VCMPFLE extends Opcode("vcmpfle") + +object VPOP extends Opcode("vpop") +object VPSET extends Opcode("vpset") +object VPCLEAR extends Opcode("vpclear") + +object VFADD_S extends Opcode("vfadd.s") +object VFSUB_S extends Opcode("vfsub.s") +object VFMUL_S extends Opcode("vfmul.s") +object VFDIV_S extends Opcode("vfdiv.s") +object VFSQRT_S extends Opcode("vfsqrt.s") +object VFMIN_S extends Opcode("vfmin.s") +object VFMAX_S extends Opcode("vfmax.s") +object VFADD_D extends Opcode("vfadd.d") +object VFSUB_D extends Opcode("vfsub.d") +object VFMUL_D extends Opcode("vfmul.d") +object VFDIV_D extends Opcode("vfdiv.d") +object VFSQRT_D extends Opcode("vfsqrt.d") +object VFMIN_D extends Opcode("vfmin.d") +object VFMAX_D extends Opcode("vfmax.d") +object VFSGNJ_S extends Opcode("vfsgnj.s") +object VFSGNJN_S extends Opcode("vfsgnjn.s") +object VFSGNJX_S extends Opcode("vfsgnjx.s") +object VFSGNJ_D extends Opcode("vfsgnj.d") +object VFSGNJN_D extends Opcode("vfsgnjn.d") +object VFSGNJX_D extends Opcode("vfsgnjx.d") + +object VFMADD_S extends Opcode("vfmadd.s") +object VFMSUB_S extends Opcode("vfmsub.s") +object VFNMSUB_S extends Opcode("vfnmsub.s") +object VFNMADD_S extends Opcode("vfnmadd.s") +object VFMADD_D extends Opcode("vfmadd.d") +object VFMSUB_D extends Opcode("vfmsub.d") +object VFNMSUB_D extends Opcode("vfnmsub.d") +object VFNMADD_D extends Opcode("vfnmadd.d") + +object VFCVT_S_D extends Opcode("vfcvt.s.d") +object VFCVT_D_S extends Opcode("vfcvt.d.s") +object VFCVT_S_L extends Opcode("vfcvt.s.l") +object VFCVT_S_LU extends Opcode("vfcvt.s.lu") +object VFCVT_S_W extends Opcode("vfcvt.s.w") +object VFCVT_S_WU extends Opcode("vfcvt.s.wu") +object VFCVT_D_L extends Opcode("vfcvt.d.l") +object VFCVT_D_LU extends Opcode("vfcvt.d.lu") +object VFCVT_D_W extends Opcode("vfcvt.d.w") +object VFCVT_D_WU extends Opcode("vfcvt.d.wu") +object VFCVT_L_S extends Opcode("vfcvt.l.s") +object VFCVT_LU_S extends Opcode("vfcvt.lu.s") +object VFCVT_W_S extends Opcode("vfcvt.w.s") +object VFCVT_WU_S extends Opcode("vfcvt.wu.s") +object VFCVT_L_D extends Opcode("vfcvt.l.d") +object VFCVT_LU_D extends Opcode("vfcvt.lu.d") +object VFCVT_W_D extends Opcode("vfcvt.w.d") +object VFCVT_WU_D extends Opcode("vfcvt.wu.d") + +object VLSB extends Opcode("vlsb") +object VLSH extends Opcode("vlsh") +object VLSW extends Opcode("vlsw") +object VLSD extends Opcode("vlsd") +object VLSBU extends Opcode("vlsbu") +object VLSHU extends Opcode("vlshu") +object VLSWU extends Opcode("vlswu") +object VSSB extends Opcode("vssb") +object VSSH extends Opcode("vssh") +object VSSW extends Opcode("vssw") +object VSSD extends Opcode("vssd") +object VLAB extends Opcode("vlab") +object VLAH extends Opcode("vlah") +object VLAW extends Opcode("vlaw") +object VLAD extends Opcode("vlad") +object VLABU extends Opcode("vlabu") +object VLAHU extends Opcode("vlahu") +object VLAWU extends Opcode("vlawu") +object VSAB extends Opcode("vsab") +object VSAH extends Opcode("vsah") +object VSAW extends Opcode("vsaw") +object VSAD extends Opcode("vsad") + +object VLB extends Opcode("vlb") +object VLH extends Opcode("vlh") +object VLW extends Opcode("vlw") +object VLD extends Opcode("vld") +object VLBU extends Opcode("vlbu") +object VLHU extends Opcode("vlhu") +object VLWU extends Opcode("vlwu") +object VSB extends Opcode("vsb") +object VSH extends Opcode("vsh") +object VSW extends Opcode("vsw") +object VSD extends Opcode("vsd") + +object VLSEGB extends Opcode("vlsegb") +object VLSEGH extends Opcode("vlsegh") +object VLSEGW extends Opcode("vlsegw") +object VLSEGD extends Opcode("vlsegd") +object VLSEGBU extends Opcode("vlsegbu") +object VLSEGHU extends Opcode("vlseghu") +object VLSEGWU extends Opcode("vlsegwu") +object VSSEGB extends Opcode("vssegb") +object VSSEGH extends Opcode("vssegh") +object VSSEGW extends Opcode("vssegw") +object VSSEGD extends Opcode("vssegd") + +object VLSTB extends Opcode("vlstb") +object VLSTH extends Opcode("vlsth") +object VLSTW extends Opcode("vlstw") +object VLSTD extends Opcode("vlstd") +object VLSTBU extends Opcode("vlstbu") +object VLSTHU extends Opcode("vlsthu") +object VLSTWU extends Opcode("vlstwu") +object VSSTB extends Opcode("vsstb") +object VSSTH extends Opcode("vssth") +object VSSTW extends Opcode("vsstw") +object VSSTD extends Opcode("vsstd") + +object VLSEGSTB extends Opcode("vlsegstb") +object VLSEGSTH extends Opcode("vlsegsth") +object VLSEGSTW extends Opcode("vlsegstw") +object VLSEGSTD extends Opcode("vlsegstd") +object VLSEGSTBU extends Opcode("vlsegstbu") +object VLSEGSTHU extends Opcode("vlsegsthu") +object VLSEGSTWU extends Opcode("vlsegstwu") +object VSSEGSTB extends Opcode("vssegstb") +object VSSEGSTH extends Opcode("vssegsth") +object VSSEGSTW extends Opcode("vssegstw") +object VSSEGSTD extends Opcode("vssegstd") + +object VLXB extends Opcode("vlxb") +object VLXH extends Opcode("vlxh") +object VLXW extends Opcode("vlxw") +object VLXD extends Opcode("vlxd") +object VLXBU extends Opcode("vlxbu") +object VLXHU extends Opcode("vlxhu") +object VLXWU extends Opcode("vlxwu") +object VSXB extends Opcode("vsxb") +object VSXH extends Opcode("vsxh") +object VSXW extends Opcode("vsxw") +object VSXD extends Opcode("vsxd") + +object VLSEGXB extends Opcode("vlsegxb") +object VLSEGXH extends Opcode("vlsegxh") +object VLSEGXW extends Opcode("vlsegxw") +object VLSEGXD extends Opcode("vlsegxd") +object VLSEGXBU extends Opcode("vlsegxbu") +object VLSEGXHU extends Opcode("vlsegxhu") +object VLSEGXWU extends Opcode("vlsegxwu") +object VSSEGXB extends Opcode("vssegxb") +object VSSEGXH extends Opcode("vssegxh") +object VSSEGXW extends Opcode("vssegxw") +object VSSEGXD extends Opcode("vssegxd") + +object VAMOADD_W extends Opcode("vamoadd.w") +object VAMOSWAP_W extends Opcode("vamoswap.w") +object VAMOAND_W extends Opcode("vamoand.w") +object VAMOOR_W extends Opcode("vamoor.w") +object VAMOMIN_W extends Opcode("vamomin.w") +object VAMOMINU_W extends Opcode("vamominu.w") +object VAMOMAX_W extends Opcode("vamomax.w") +object VAMOMAXU_W extends Opcode("vamomaxu.w") +object VAMOXOR_W extends Opcode("vamoxor.w") +object VAMOADD_D extends Opcode("vamoadd.d") +object VAMOSWAP_D extends Opcode("vamoswap.d") +object VAMOAND_D extends Opcode("vamoand.d") +object VAMOOR_D extends Opcode("vamoor.d") +object VAMOMIN_D extends Opcode("vamomin.d") +object VAMOMINU_D extends Opcode("vamominu.d") +object VAMOMAX_D extends Opcode("vamomax.d") +object VAMOMAXU_D extends Opcode("vamomaxu.d") +object VAMOXOR_D extends Opcode("vamoxor.d") + +object MOVZ extends Opcode("movz") +object MOVN extends Opcode("movn") +object FMOVZ extends Opcode("fmovz") +object FMOVN extends Opcode("fmovn") + +object FENCE_V extends Opcode("fence") + +object ILLEGAL extends Opcode(".word") diff --git a/tools/riscv-torture/generator/src/main/scala/InstSeq.scala b/tools/riscv-torture/generator/src/main/scala/InstSeq.scala new file mode 100644 index 0000000..23c6394 --- /dev/null +++ b/tools/riscv-torture/generator/src/main/scala/InstSeq.scala @@ -0,0 +1,121 @@ +package torture + +import scala.collection.mutable.ArrayBuffer +import Rand._ + +class InstSeq extends HWRegAllocator +{ + val insts = new ArrayBuffer[Inst] + var inst_ptr = 0 + val seqname = "Unnamed" + + val extra_code = new ArrayBuffer[DataChunk] + val extra_hidden_data = new ArrayBuffer[DataChunk] + val extra_visible_data = new ArrayBuffer[DataChunk] + + def is_done = insts.length == inst_ptr + + def next_inst() = + { + val inst = insts(inst_ptr) + inst_ptr += 1 + inst + } +} + +object InstSeq +{ + def apply(prob_tbl: ArrayBuffer[(Int, () => InstSeq)]): InstSeq = + { + var p = rand_range(0, 99) + for ((prob, gen_seq) <- prob_tbl) + { + if (p < prob) return gen_seq() + p -= prob + } + + assert(false, println("Probabilties should have added up to 100%")) + new InstSeq() + } +} + +import HWReg._ + +class HWRegAllocator +{ + val regs = new ArrayBuffer[Reg] + var allocated = false + + def reg_fn(hwrp: HWRegPool, filter: (HWReg) => Boolean, alloc: (HWReg) => Unit, free: (HWReg) => Unit, consec_regs: Int = 1) = + { + val reg = new RegNeedsAlloc(hwrp, filter, alloc, free, consec_regs) + for(i <- 1 to consec_regs) reg.regs += new Reg + regs += reg + reg + } + + def reg_read_zero(hwrp: HWRegPool) = { reg_fn(hwrp, filter_read_zero, alloc_read, free_read) } + def reg_read_any(hwrp: HWRegPool) = { reg_fn(hwrp, filter_read_any, alloc_read, free_read) } + def reg_read_any_other(hwrp: HWRegPool, other: Reg) = { reg_fn(hwrp, filter_read_any_other(other), alloc_read, free_read) } + def reg_read_visible(hwrp: HWRegPool) = { reg_fn(hwrp, filter_read_visible, alloc_read, free_read) } + def reg_read_visible_consec(hwrp: HWRegPool, regs: Int) = { reg_fn(hwrp, filter_read_visible, alloc_read, free_read, regs) } + def reg_write_ra(hwrp: HWRegPool) = { reg_fn(hwrp, filter_write_ra, alloc_write(false), free_write) } + def reg_write_visible(hwrp: HWRegPool) = { reg_fn(hwrp, filter_write_visible, alloc_write(true), free_write) } + def reg_write_visible_consec(hwrp: HWRegPool, regs: Int) = { reg_fn(hwrp, filter_write_visible, alloc_write(true), free_write, regs) } + def reg_write_hidden(hwrp: HWRegPool) = { reg_fn(hwrp, filter_write_hidden, alloc_write(false), free_write) } + def reg_write(hwrp: HWRegPool, regs: Reg*) = { reg_fn(hwrp, filter_write_dep(regs.toList), alloc_write_dep(regs.toList), free_write) } + def reg_write_other(hwrp: HWRegPool, other: Reg, regs: Reg*) = { reg_fn(hwrp, filter_write_dep_other(other, regs.toList), alloc_write_dep(regs.toList), free_write) } + + def allocate_regs(): Boolean = + { + for (reg <- regs) + { + val regna = reg.asInstanceOf[RegNeedsAlloc] + val candidates = regna.hwrp.hwregs.filter(regna.filter) + val consec_regs = regna.consec_regs + val hwregs = regna.hwrp.hwregs + + if (candidates.length < consec_regs) + return false + + var high = 0 + val consec_candidates = new ArrayBuffer[Int] // index in hwregs + for( hrindex <- 0 to hwregs.length) + { + if(hrindex < hwregs.length && candidates.contains(hwregs(hrindex))) + high += 1 // still seeing consec regs + else if (high > 0) + { + // end of sequence. number all the candidates of this sequence + for(i <- high to consec_regs by -1) + consec_candidates += hrindex-i + high = 0 + } + } + if(consec_candidates.size == 0) + return false + val reg_index = rand_pick(consec_candidates) + for(i <- reg_index until reg_index+consec_regs){ + val hwreg = hwregs(i) + regna.alloc(hwreg) + if(i == reg_index) regna.hwreg = hwreg + regna.regs.toArray[Reg].apply(i-reg_index).hwreg = hwreg + } + } + + allocated = true + + return true + } + + def free_regs() = + { + for (reg <- regs) + { + val regna = reg.asInstanceOf[RegNeedsAlloc] + val hwregs = regna.hwrp.hwregs + val start = hwregs.indexOf(regna.hwreg) + for( i <- start until start+regna.consec_regs ) regna.free(hwregs(i)) + } + } +} diff --git a/tools/riscv-torture/generator/src/main/scala/Mem.scala b/tools/riscv-torture/generator/src/main/scala/Mem.scala new file mode 100644 index 0000000..83f9ea8 --- /dev/null +++ b/tools/riscv-torture/generator/src/main/scala/Mem.scala @@ -0,0 +1,77 @@ +package torture + +import scala.collection.mutable.ArrayBuffer +import Rand._ + +class Mem(name: Array[Operand], val size: Int) extends Operand +{ + def this(namelabel: String, size: Int) = this(Array[Operand](Label(namelabel)), size) + + assert(size % 4 == 0, "Memory size must be multiple of 4") + + override def toString = name.mkString("") + + def dumpdata = + { + var s = "\t.align 8\n" + s += this.toString + ":\n" + if(size % 16 == 0) + { + for (i <- 0 to (size/8/2 - 1)) + s += "\t.dword 0x%016x, 0x%016x\n" format (rand_dword, rand_dword) + } else if(size % 8 == 0) + { + for (i <- 0 to (size/8 - 1)) + s += "\t.dword 0x%016x\n" format (rand_dword) + } + else + { + for (i <- 0 to (size/4 - 1)) + s += "\t.word 0x%08x\n" format (rand_word) + } + s + } + + def dumpaddrs(addrfn: (Int) => Int, memsize: Int) = + { + var s = "\t.align 8\n" + s += this.toString + ":\n" + if(size % 16 == 0) + { + for (i <- 0 to (size/8/2 - 1)) + s += "\t.dword 0x%016x, 0x%016x\n" format (addrfn(memsize), addrfn(memsize)) + } else if(size % 8 == 0) + { + for (i <- 0 to (size/8 - 1)) + s += "\t.dword 0x%016x\n" format (addrfn(memsize)) + } + else + { + for (i <- 0 to (size/4 - 1)) + s += "\t.word 0x%08x\n" format (addrfn(memsize)) + } + s + } +} + +class VMem(name: Array[Operand], val ut_size: Int, num_ut: Int) extends Mem(name, ut_size*num_ut) +{ + def this(namelabel: String, ut_size: Int, num_ut: Int) = this(Array[Operand](Label(namelabel)), ut_size, num_ut) + + assert(size % 16 == 0, "Per uthread memory size must be multiple of 16") + + override def dumpdata = + { + var s = "\t.align 8\n" + s += this.toString + ":\n" + + for(ut <- 0 to (num_ut-1)) + { + s+= "\t" + this.toString + "_ut_" + ut + ":\n" + for (i <- 0 to (ut_size/8/2 - 1)) + s += "\t.dword 0x%016x, 0x%016x\n" format (rand_dword, rand_dword) + } + s + } +} + diff --git a/tools/riscv-torture/generator/src/main/scala/Operand.scala b/tools/riscv-torture/generator/src/main/scala/Operand.scala new file mode 100644 index 0000000..5122bfe --- /dev/null +++ b/tools/riscv-torture/generator/src/main/scala/Operand.scala @@ -0,0 +1,100 @@ +package torture + +import scala.collection.mutable.ArrayBuffer + +abstract class Operand + +class Reg extends Operand +{ + var allocated = false + var hwreg = new HWReg("-", false, false) + + override def toString = hwreg.toString +} + +class RegNeedsAlloc( + val hwrp: HWRegPool, + val filter: (HWReg) => Boolean, + val alloc: (HWReg) => Unit, + val free: (HWReg) => Unit, + val consec_regs: Int = 1) extends Reg +{ + val regs = new ArrayBuffer[Reg] +} + +class Imm(imm: Int) extends Operand +{ + override def toString = imm.toString +} + +class HexImm(imm: Int) extends Operand +{ + override def toString = "0x"+Integer.toHexString(imm) +} + +class BaseImm(base: String, imm: Int) extends Operand +{ + override def toString = + { + if (imm == 0) base + else if (imm < 0) base + imm.toString + else base + "+" + imm.toString + } +} + +class RegImm(base: Reg, imm: Int) extends Operand +{ + override def toString = imm.toString + "(" + base + ")" +} + +class RegStrImm(base: Reg, imm: String) extends Operand +{ + override def toString = imm + "(" + base + ")" +} + +class Label(val label: String) extends Operand +{ + override def toString = label +} + +class PredReg(pred: Reg, neg: Boolean) extends Operand +{ + override def toString = + if (neg) "@!" + pred + else "@" + pred +} + +object Imm +{ + def apply(imm: Int) = new Imm(imm) +} + +object HexImm +{ + def apply(imm: Int) = new HexImm(imm) +} + +object BaseImm +{ + def apply(base: String, imm: Int) = new BaseImm(base, imm) +} + +object RegImm +{ + def apply(base: Reg, imm: Int) = new RegImm(base, imm) +} + +object RegStrImm +{ + def apply(base: Reg, imm: String) = new RegStrImm(base, imm) +} + +object Label +{ + def apply(label: String) = new Label(label) +} + +object PredReg +{ + def apply(pred: Reg, neg: Boolean) = new PredReg(pred, neg) +} diff --git a/tools/riscv-torture/generator/src/main/scala/Prog.scala b/tools/riscv-torture/generator/src/main/scala/Prog.scala new file mode 100644 index 0000000..d78f691 --- /dev/null +++ b/tools/riscv-torture/generator/src/main/scala/Prog.scala @@ -0,0 +1,563 @@ +package torture + +import scala.collection.mutable.ArrayBuffer +import scala.collection.mutable.HashMap +import Rand._ +import java.util.Date +import java.text.DateFormat + +class ProgSeg(val name: String) +{ + var insts = new ArrayBuffer[Inst] + + override def toString = ((name + ":\n") /: insts.map((x) => "\t" + x + "\n"))(_ + _) +} + +object ProgSeg +{ + var cnt = 0 + + def apply() = + { + val res = new ProgSeg("pseg_" + cnt) + cnt += 1 + res + } +} + +class Prog(memsize: Int, veccfg: Map[String,String], loop : Boolean) +{ + // Setup scalar core memory + val core_memory = new Mem("test_memory", memsize) + + // Setup register pools + val num_vxregs = rand_range(5, 256) + val use_pop = veccfg.getOrElse("pop", "true") == "true" + val pred_alu = veccfg.getOrElse("pred_alu", "true") == "true" + val pred_mem = veccfg.getOrElse("pred_mem", "true") == "true" + val min_pregs = if(pred_alu || pred_mem || use_pop) 2 else 1 + val num_vpregs = rand_range(min_pregs, 16) + val num_vsregs = veccfg.getOrElse("numsregs","64").toInt + val max_vl = (Math.floor(256/(num_vxregs-1))).toInt * 8 + val used_vl = Math.min(max_vl, rand_range(1, max_vl)) + + val xregs = new XRegsPool() + val fregs = new FRegsMaster() + val vregs = new VRegsMaster(num_vxregs, num_vpregs, num_vsregs) + + val fregpools = fregs.extract_pools() + val vregpools = vregs.extract_pools() + val (fregs_s, fregs_d) = (fregpools(0), fregpools(1)) + val (vxregs, vpregs, vsregs, varegs) = (vregpools(0), vregpools(1), vregpools(2), vregpools(3)) + + val seqs = new ArrayBuffer[InstSeq] + val seqs_active = new ArrayBuffer[InstSeq] + val progsegs = new ArrayBuffer[ProgSeg] + + var killed_seqs = 0 + var nseqs = 0 + var prob_tbl = new ArrayBuffer[(Int, ()=>InstSeq)] + + val opstats = new HashMap[String, scala.collection.mutable.Map[String,Int]] + val catstats = new HashMap[String,Int] + val seqstats = new HashMap[String,Int].withDefaultValue(0) + val vseqstats = new HashMap[String,Int].withDefaultValue(0) + val regstats = new HashMap[String,Int].withDefaultValue(0) + for (cat <- List(("alu"),("cmp"),("branch"),("jalr"), + ("jmp"),("la"),("mem"),("amo"),("misc"),("fpalu"),("fpcmp"), + ("fpfma"),("fpmem"),("fpcvt"),("fpmisc"),("vmem"),("vamo"),("valu"), + ("vmisc"),("vfpalu"),("vfpfma"),("vfpcvt"),("vsmem"),("vshared"),("vpred"),("vcmp"),("unknown"))) + { + catstats(cat)=0 + opstats(cat) = new HashMap[String,Int].withDefaultValue(0) + } + var instcnt = 0 + + def seqs_not_allocated = seqs.filter((x) => !x.allocated) + def is_seqs_empty = seqs_not_allocated.length == 0 + def is_seqs_active_empty = seqs_active.length == 0 + + def are_pools_fully_unallocated = List(xregs, fregs_s, fregs_d, vxregs, vpregs, vsregs, varegs).forall(_.is_fully_unallocated) + + def seqs_find_active(): Unit = + { + for (seq <- seqs_not_allocated) + { + xregs.backup() + fregs.backup() + vregs.backup() + + if (seq.allocate_regs()) + { + seqs_active += seq + } + else + { + if (are_pools_fully_unallocated) + { + seqs -= seq + killed_seqs += 1 + seqstats(seq.seqname) -= 1 + if (seq.seqname == "vec") + { + for ((seqname, seqcnt) <- seq.asInstanceOf[SeqVec].vseqstats) + { + vseqstats(seqname) = seqcnt + } + } + if (killed_seqs < (nseqs*5)) + gen_seq() + } + xregs.restore() + fregs.restore() + vregs.restore() + + return + } + } + } + + var jalr_labels = new ArrayBuffer[Label] + + def update_stats(inst: Inst) = + { + catstats(inst.optype) += 1 + opstats(inst.optype)(inst.opcode) += 1 + for (operand <- inst.operands) + { + if (operand.isInstanceOf[Reg]) + { + regstats(operand.toString) += 1 + } + } + instcnt += 1 + } + + def register_stats(): String = + { + def register_lt(reg1: (String, Int), reg2: (String, Int)): Boolean = + { + val reghash = HashMap('x'->1,'f'->2,'v'->3,'p'->4,'s'->5,'a'->6) + val regname1 = reg1._1 + val regname2 = reg2._1 + if (reghash(regname1(0)) == reghash(regname2(0))) + { + if (regname1(0) == 'v') + { + if (regname1(1) == regname2(1)) + { + return (regname1.substring(2).toInt < regname2.substring(2).toInt) + } else { + return (reghash(regname1(1)) < reghash(regname2(1))) + } + } else { + return (regname1.substring(1).toInt < regname2.substring(1).toInt) + } + } else { + return (reghash(regname1(0)) < reghash(regname2(0))) + } + } + + val sortedRegs = regstats.toSeq.sortWith(register_lt) //TODO: Better way to sort? + var s = "---------- Register Accesses ----------\n" + for ((regname, cnt) <- sortedRegs) + { + s += "---------- " + regname + ": " + cnt + " ----------\n" + } + s + } + + def sequence_stats(mix: Map[String, Int], vecmix: Map[String, Int], nseqs: Int, vnseq: Int, vfnum: Int): String = + { + def seq_lt(seq1: (String, Int), seq2: (String, Int)): Boolean = + { + val seqhash = HashMap("xmem"->1,"xbranch"->2,"xalu"->3,"vmem"->4, + "fgen"->5,"fpmem"->6,"fax"->7,"fdiv"->8,"vec"->9,"vonly"->10,"valu"->11,"Generic"->12).withDefaultValue(100) + if (seqhash(seq1._1) == 100 && seqhash(seq2._1) == 100) return (seq1._1 < seq2._1) + return seqhash(seq1._1) < seqhash(seq2._1) + } + + val sortedMix = mix.toSeq.sortWith(seq_lt) + val sortedVecmix = vecmix.toSeq.sortWith(seq_lt) + var s = "----- Sequence Types Used:" + for ((seqtype,percent) <- sortedMix) if (percent > 0) s += " " + seqtype.toUpperCase + s += " -----\n" + s += "--------------------------------------------------------------------------\n" + s += "---------- Configured Sequence Mix ----------\n" + for ((seqtype, percent) <- sortedMix) + { + s += "---------- " + seqtype + ": " + percent + "% ----------\n" + } + s += "--------------------------------------------------------------------------\n" + s += "---------- Configured Vector Sequence Mix ----------\n" + for ((seqtype, percent) <- sortedVecmix) + { + s+= "---------- " + seqtype + ": " + percent + "% ----------\n" + } + s += "--------------------------------------------------------------------------\n" + s += "---------- Generated Sequence Mix ----------\n" + s += "---------- nseqs = " + nseqs + " -------------\n" + val sortedSeqs = seqstats.toSeq.sortWith(seq_lt) + for ((seq, seqcnt) <- sortedSeqs) + { + s += "---------- " + seq + ": " + seqcnt + " :: %3.3f".format((seqcnt.toDouble/nseqs)*100) + s += "% ----------\n" + } + s += "--------------------------------------------------------------------------\n" + s += "---------- Generated Vector Sequence Mix ----------\n" + s += "---------- nvseqs = " + vnseq*vfnum*seqstats("vec") + " -------------\n" + val sortedVSeqs = vseqstats.toSeq.sortWith(seq_lt) + for ((vseq, vseqcnt) <- sortedVSeqs) + { + s += "---------- " + vseq + ": " + vseqcnt + s += " :: %3.3f".format((vseqcnt.toDouble/(vnseq*vfnum*seqstats("vec")))*100) + s += "% ----------\n" + } + s + } + + def instruction_stats(): String = + { + def cat_lt(cat1: (String, Int), cat2: (String, Int)): Boolean = + { + val cathash = HashMap("alu"->1,"cmp"->2,"branch"->3,"jmp"->4,"jalr"->5, + "la"->6,"mem"->7,"amo"->8,"misc"->9,"fpalu"->10,"fpcmp"->11,"fpfma"->12, + "fpmem"->13,"fpcvt"->14,"fpmisc"->15,"vmem"->16,"vamo"->17,"valu"->18,"vfpalu"->19, + "vfpfma"->20,"vfpcvt"->21,"vsmem"->22,"vshared"->23,"vpred"->24,"vcmp"->25,"vmisc"->26,"unknown"->27) + return cathash(cat1._1) < cathash(cat2._1) + } + + var s = "---------- Opcode Usage ----------\n" + s += "---------- instcnt = " + instcnt + " -------------\n" + val sortedCats = catstats.toSeq.sortWith(cat_lt) // TODO: Better way to sort? + for ((cat, catcnt) <- sortedCats) + { + val sortedOps = opstats(cat).toSeq.sortWith(_._1 < _._1) //TODO: Better way to sort? + s += "--------------------------------------------------------------------------\n" + s += "---------- " + cat.toUpperCase() + " Opcodes: " + catcnt + " :: %3.3f".format((catcnt.toDouble/instcnt)*100) + s += "% ----------\n" + for ((op, opcnt) <- sortedOps) + { + s += "-------------------- " + op + ": " + opcnt + " :: %3.3f".format((opcnt.toDouble/instcnt)*100) + s += "% ----------\n" + } + } + s + } + + def get_time(): String = + { + val date = new Date() + val datestr = DateFormat.getDateTimeInstance(DateFormat.FULL, DateFormat.FULL).format(date) + "----- Test generated on " + datestr + " -----\n" + } + + def gen_seq(): Unit = + { + val nxtseq = InstSeq(prob_tbl) + seqs += nxtseq + seqstats(nxtseq.seqname) += 1 + if (nxtseq.seqname == "vec") + { + for ((seqname, seqcnt) <- nxtseq.asInstanceOf[SeqVec].vseqstats) + { + vseqstats(seqname) += seqcnt + } + } + } + + def add_inst(inst: Inst) = + { + if (progsegs.length == 0) + progsegs += ProgSeg() + + progsegs.last.insts += inst + + val branch_filter = (x: Operand) => + x.isInstanceOf[Label] && x.asInstanceOf[Label].label.indexOf("branch_patch") != -1 + val branch_patch = inst.operands.indexWhere(branch_filter) + if (branch_patch != -1) + { + progsegs.last.insts += ILLEGAL(Label("0x%08x" format rand_word)) + progsegs += ProgSeg() + inst.operands(branch_patch) = Label(progsegs.last.name) + } + + val jalr_filter = (x: Operand) => + x.isInstanceOf[Label] && x.asInstanceOf[Label].label.indexOf("jalr_patch2") != -1 + val jalr_patch = inst.operands.indexWhere(jalr_filter) + if (jalr_patch != -1) + { + progsegs.last.insts += ILLEGAL(Label("0x%08x" format rand_word)) + progsegs += ProgSeg() + jalr_labels += Label(progsegs.last.name) + inst.operands(jalr_patch) = Imm(0) + } + update_stats(inst) + } + + def resolve_jalr_las = + { + var jalr_count = 0 + val jalr_la_filter = (x: Operand) => + x.isInstanceOf[Label] && x.asInstanceOf[Label].label.indexOf("jalr_patch1") != -1 + for (progseg <- progsegs) + { + for (inst <- progseg.insts) + { + val jalr_la_patch = inst.operands.indexWhere(jalr_la_filter) + if (jalr_la_patch != -1) { + inst.operands(jalr_la_patch) = jalr_labels(jalr_count) + jalr_count += 1 + } + } + } + } + + def names = List("xmem","xbranch","xalu","fgen","fpmem","fax","fdiv","vec") + + def code_body(seqnum: Int, mix: Map[String, Int], veccfg: Map[String, String], use_amo: Boolean, use_mul: Boolean, use_div: Boolean, segment: Boolean) = + { + val name_to_seq = Map( + "xmem" -> (() => new SeqMem(xregs, core_memory, use_amo)), + "xbranch" -> (() => new SeqBranch(xregs)), + "xalu" -> (() => new SeqALU(xregs, use_mul, use_div)), //true means use_divider, TODO: make better + "fgen" -> (() => new SeqFPU(fregs_s, fregs_d)), + "fpmem" -> (() => new SeqFPMem(xregs, fregs_s, fregs_d, core_memory)), + "fax" -> (() => new SeqFaX(xregs, fregs_s, fregs_d)), + "fdiv" -> (() => new SeqFDiv(fregs_s, fregs_d)), + "vec" -> (() => new SeqVec(xregs, vxregs, vpregs, vsregs, varegs, used_vl, veccfg))) + + prob_tbl = new ArrayBuffer[(Int, () => InstSeq)] + nseqs = seqnum + + for ((name, prob) <- mix) + prob_tbl += ((prob, name_to_seq(name))) + + for (i <- 0 to nseqs-1) gen_seq() + + if (segment) { progsegs += ProgSeg() } + while (!is_seqs_empty) + { + seqs_find_active() + + while (!is_seqs_active_empty) + { + val seq = rand_pick(seqs_active) + if(segment) { + val inst = seq.next_inst() + val branch_filter = (x: Operand) => + x.isInstanceOf[Label] && x.asInstanceOf[Label].label.indexOf("branch_patch") != -1 + val branch_patch = inst.operands.indexWhere(branch_filter) + val jalr_filter1 = (x: Operand) => + x.isInstanceOf[Label] && x.asInstanceOf[Label].label.indexOf("jalr_patch1") != -1 + val jalr_patch1 = inst.operands.indexWhere(jalr_filter1) + val jalr_filter2 = (x: Operand) => + x.isInstanceOf[Label] && x.asInstanceOf[Label].label.indexOf("jalr_patch2") != -1 + val jalr_patch2 = inst.operands.indexWhere(jalr_filter2) + if (jalr_patch1 == -1 && branch_patch == -1 && jalr_patch2 == -1){ + progsegs.last.insts += inst + update_stats(inst) + } + } else { + add_inst(seq.next_inst()) + } + + if (seq.is_done) + { + seq.free_regs() + seqs_active -= seq + if (seq.isInstanceOf[SeqVec]) + for (vinst <- seq.asInstanceOf[SeqVec].vinsts) + update_stats(vinst) + } + + if (rand_range(0,99) < 10) seqs_find_active() + } + } + + //Final p_seg + progsegs.last.insts += J(Label("reg_dump")) + + if(!segment) { resolve_jalr_las } + rand_permute(progsegs) + + if (killed_seqs >= (nseqs*5)) + { + println("Warning: Prog killed an excessive number of sequences. (#X=%d, #Fs=%d, #Fd=%d, #VX=%d, #VP=%d, #VS=%d, #VA=%d)" format (xregs.size, fregs_s.size, fregs_d.size, vxregs.size, vpregs.size, vsregs.size, varegs.size)) + } + + ("" /: progsegs)(_ + _) + "\n" + } + + def header(nseqs: Int) = + { + "// random assembly code generated by RISC-V torture test generator\n" + + "// nseqs = " + nseqs + "\n" + + "// memsize = " + memsize + "\n" + + "\n" + + "#include \"riscv_test.h\"\n" + } + + def code_header(using_fpu: Boolean, using_vec: Boolean, fprnd: Int) = + { + "\n" + + (if (using_vec) "RVTEST_RV64UV\n" + else if (using_fpu) "RVTEST_RV64UF\n" + else "RVTEST_RV32M\n") + + "RVTEST_CODE_BEGIN\n" + + (if (using_vec) init_vector() else "") + + "\n" + + "\tj test_start\n" + + "\n" + + "crash_backward:\n" + + "\tRVTEST_FAIL\n" + + "\n" + + "test_start:\n" + + "\n" + + // fregs must be initialized before xregs! + (if (using_fpu) fregs.init_regs() else "") + + (if (using_vec) vregs.init_regs() else "") + + xregs.init_regs() + + "\tj pseg_0\n" + + "\n" + } + + def init_vector() = + { + "\n" + + "\tli x1, " + used_vl + "\n" + + "\tvsetcfg " + num_vxregs + ", " + num_vpregs + "\n" + + "\tvsetvl x1,x1\n" + } + + def code_footer(using_fpu: Boolean, using_vec: Boolean, loop: Boolean) = + { + var s = "reg_dump:\n" + + { + if(loop){ + "\tla x1, loop_count\n" + + "\tlw x2, 0(x1)\n" + + "\taddi x3, x2, -1\n" + + "\tsw x3, 0(x1)\n" + + "\tbnez x2, pseg_0\n" + } else {""} + } + + // fregs must be saved after xregs + xregs.save_regs() + + (if(using_fpu) fregs.save_regs() else "") + + (if(using_vec) vregs.save_regs() else "") + + "\tj test_end\n" + + "\n" + + "crash_forward:\n" + + "\tRVTEST_FAIL\n" + + "\n" + + "test_end:\n" + + "\tRVTEST_PASS\n" + + "\n" + + "RVTEST_CODE_END\n" + + "\n" + for(seq <- seqs.filter(_.is_done)) + { + val ns = seq.extra_code.mkString("\n") + if(ns.nonEmpty) s += "// extra code for " + seq + "\n" + + "\t.align 3\n" + ns + "\n" + } + s += "\n" + s + } + + def data_header() = + { + "\t.data\n" + + "\n" + } + + def output_mem_data(loop_size: Int) = + { + var s = "// Memory Blocks\n" + s += MemDump(core_memory) + s += "\n" + s += ".align 8\n" + s += "loop_count: .word 0x" + Integer.toHexString(loop_size) + "\n\n" + for(seq <- seqs.filter(_.is_done)) + { + val ns = seq.extra_visible_data.mkString("\n") + if(ns.nonEmpty) s += "// output data for " + seq + "\n" + ns + "\n" + } + s + } + + def data_input(using_fpu: Boolean, using_vec: Boolean) = + { + var s = "hidden_data:\n" + for(seq <- seqs.filter(_.is_done)) + { + val ns = seq.extra_hidden_data.mkString("\n") + if(ns.nonEmpty) s += "// hidden data for " + seq + "\n" + ns + "\n" + } + s += xregs.init_regs_data() + s += (if(using_fpu) fregs.init_regs_data() else "") + s += (if(using_vec) vregs.init_regs_data() else "") + s + } + + def data_output(using_fpu: Boolean, using_vec: Boolean, loop_size: Int) = + { + "RVTEST_DATA_BEGIN\n" + + "\n" + + xregs.output_regs_data() + + (if(using_fpu) fregs.output_regs_data() else "") + + (if(using_vec) vregs.output_regs_data() else "") + + output_mem_data(loop_size) + + "RVTEST_DATA_END\n" + } + + def data_footer() = "" + + def generate(nseqs: Int, fprnd: Int, mix: Map[String, Int], veccfg: Map[String, String], use_amo: Boolean, use_mul: Boolean, use_div: Boolean, segment : Boolean, loop: Boolean, loop_size: Int) = + { + // Check if generating any FP operations or Vec unit stuff + val using_vec = mix.filterKeys(List("vec") contains _).values.reduce(_+_) > 0 + val using_fpu = (mix.filterKeys(List("fgen","fpmem","fax","fdiv") contains _).values.reduce(_+_) > 0) || using_vec + // TODO: make a config object that is passed around? + + header(nseqs) + + code_header(using_fpu, using_vec, fprnd) + + code_body(nseqs, mix, veccfg, use_amo, use_mul, use_div, segment) + + code_footer(using_fpu, using_vec, loop) + + data_header() + + data_input(using_fpu, using_vec) + + data_output(using_fpu, using_vec, loop_size) + + data_footer() + } + + def statistics(nseqs: Int, fprnd: Int, mix: Map[String, Int], vnseq: Int, vmemsize: Int, vfnum: Int, vecmix: Map[String, Int], + use_amo: Boolean, use_mul: Boolean, use_div: Boolean) = + { + "--------------------------------------------------------------------------\n" + + "-- Statistics for assembly code created by RISCV torture test generator --\n" + + get_time() + + "--------------------------------------------------------------------------\n" + + "---------- instcnt = " + instcnt + " -------------\n" + + "---------- nseqs = " + nseqs + " -------------\n" + + "---------- memsize = " + memsize + " ----------\n" + + "---------- vnseq = " + vnseq + " ----------\n" + + "---------- vfnum = " + vfnum + " ----------\n" + + "---------- vmemsize = " + vmemsize + " ----------\n" + + "---------- fprnd = " + fprnd + " ----------\n" + + "---------- use_amo = " + use_amo + " ----------\n" + + "---------- use_mul = " + use_mul + " ----------\n" + + "---------- use_div = " + use_div + " ----------\n" + + "--------------------------------------------------------------------------\n\n" + + "--------------------------------------------------------------------------\n" + + sequence_stats(mix, vecmix, nseqs, vnseq, vfnum) + + "--------------------------------------------------------------------------\n\n" + + "--------------------------------------------------------------------------\n" + + instruction_stats() + + "--------------------------------------------------------------------------\n\n" + + "--------------------------------------------------------------------------\n" + + register_stats() + + "--------------------------------------------------------------------------\n" + } +} diff --git a/tools/riscv-torture/generator/src/main/scala/Rand.scala b/tools/riscv-torture/generator/src/main/scala/Rand.scala new file mode 100644 index 0000000..ec0745f --- /dev/null +++ b/tools/riscv-torture/generator/src/main/scala/Rand.scala @@ -0,0 +1,86 @@ +package torture + +import scala.util.Random +import scala.collection.mutable.ArrayBuffer + +object Rand +{ + def rand_word: Int = Random.nextInt + def rand_dword: Long = Random.nextLong + + def rand_range(low: Int, high: Int): Int = + { + var span = high - low + 1 + if (low > high) span = low - high + 1 + low + Random.nextInt(span) + } + + def rand_shamt() = rand_range(0, 31) + def rand_shamtw() = rand_range(0, 31) + def rand_seglen() = rand_range(0, 7) + def rand_imm() = rand_range(-2048, 2047) + def rand_bigimm() = rand_range(0, 1048575) + + def rand_addr_b(memsize: Int) = rand_range(0, memsize-1) + def rand_addr_h(memsize: Int) = rand_range(0, memsize-1) & ~1 + def rand_addr_w(memsize: Int) = rand_range(0, memsize-1) & ~3 + def rand_addr_d(memsize: Int) = rand_range(0, memsize-1) & ~7 + + def rand_filter(rand: () => Int, filter: (Int) => Boolean) = + { + var res = rand() + while (!filter(res)) res = rand() + res + } + + def rand_pick[T](array: ArrayBuffer[T]) = + { + array(rand_range(0, array.length-1)) + } + + def rand_permute[T](array: ArrayBuffer[T]) = + { + for (i <- 0 to array.length-1) + { + val j = rand_range(0, array.length-1) + val t = array(i) + array(i) = array(j) + array(j) = t + } + } + + def rand_biased: Long = + { + val value = rand_dword + val s = rand_range(0, 17) + + if (s < 9) + { + val small = rand_range(0, 9).toLong + + s match + { + // return a value with a single bit set + case 0 => (1 << value & 63) + case 1 => (1 << value & 63) + // return a valueue with a single bit clear + case 2 => ~(1 << value & 63) + case 3 => ~(1 << value & 63) + // return a small integer around zero + case 4 => small + // return a very large/very small 8b signed number + case 5 => ((0x80L + small) << 56) >> 56 + // return a very large/very small 16b signed number + case 6 => ((0x8000L + small) << 48) >> 48 + // return a very large/very small 32b signed number + case 7 => ((0x80000000L + small) << 32) >> 32 + // return a very large/very small 64b signed number + case 8 => 0x800000000000000L + small + } + } + else + { + value + } + } +} diff --git a/tools/riscv-torture/generator/src/main/scala/SeqALU.scala b/tools/riscv-torture/generator/src/main/scala/SeqALU.scala new file mode 100644 index 0000000..f380697 --- /dev/null +++ b/tools/riscv-torture/generator/src/main/scala/SeqALU.scala @@ -0,0 +1,92 @@ +package torture + +import scala.collection.mutable.ArrayBuffer +import Rand._ + +class SeqALU(xregs: HWRegPool, use_mul: Boolean, use_div: Boolean) extends InstSeq //TODO: better configuration +{ + override val seqname = "xalu" + def seq_immfn(op: Opcode, immfn: () => Int) = () => + { + val dest = reg_write_visible(xregs) + val imm = Imm(immfn()) + insts += op(dest, imm) + } + + def seq_src1(op: Opcode) = () => + { + val src1 = reg_read_any(xregs) + val dest = reg_write(xregs, src1) + insts += op(dest, src1, src1) + } + + def seq_src1_immfn(op: Opcode, immfn: () => Int) = () => + { + val src1 = reg_read_any(xregs) + val dest = reg_write(xregs, src1) + val imm = Imm(immfn()) + insts += op(dest, src1, imm) + } + + def seq_src1_zero(op: Opcode) = () => + { + val src1 = reg_read_any(xregs) + val dest = reg_write(xregs, src1) + val tmp = reg_write_visible(xregs) + insts += ADDI(tmp, reg_read_zero(xregs), Imm(rand_imm())) + insts += op(dest, tmp, tmp) + } + + def seq_src2(op: Opcode) = () => + { + val src1 = reg_read_any(xregs) + val src2 = reg_read_any(xregs) + val dest = reg_write(xregs, src1, src2) + insts += op(dest, src1, src2) + } + + def seq_src2_zero(op: Opcode) = () => + { + val src1 = reg_read_any(xregs) + val dest = reg_write(xregs, src1) + val tmp1 = reg_write_visible(xregs) + val tmp2 = reg_write_visible(xregs) + insts += ADDI(tmp1, reg_read_zero(xregs), Imm(rand_imm())) + insts += ADDI(tmp2, reg_read_zero(xregs), Imm(rand_imm())) + insts += op(dest, tmp1, tmp2) + } + + val candidates = new ArrayBuffer[() => insts.type] + + candidates += seq_immfn(LUI, rand_bigimm) + candidates += seq_src1_immfn(ADDI, rand_imm) + candidates += seq_src1_immfn(SLLI, rand_shamt) + candidates += seq_src1_immfn(SLTI, rand_imm) + candidates += seq_src1_immfn(SLTIU, rand_imm) + candidates += seq_src1_immfn(XORI, rand_imm) + candidates += seq_src1_immfn(SRLI, rand_shamt) + candidates += seq_src1_immfn(SRAI, rand_shamt) + candidates += seq_src1_immfn(ORI, rand_imm) + candidates += seq_src1_immfn(ANDI, rand_imm) + //candidates += seq_src1_immfn(ADDIW, rand_imm) + //candidates += seq_src1_immfn(SLLIW, rand_shamtw) + //candidates += seq_src1_immfn(SRLIW, rand_shamtw) + //candidates += seq_src1_immfn(SRAIW, rand_shamtw) + + val oplist = new ArrayBuffer[Opcode] + + oplist += (ADD, SUB, SLL, SLT, SLTU, XOR, SRL, SRA, OR, AND) + //oplist += (ADDW, SUBW, SLLW, SRLW, SRAW) + if (use_mul) oplist += (MUL, MULH, MULHSU, MULHU) + if (use_div) oplist += (DIV, DIVU, REM, REMU) + + for (op <- oplist) + { + candidates += seq_src1(op) + candidates += seq_src1_zero(op) + candidates += seq_src2(op) + candidates += seq_src2_zero(op) + } + + rand_pick(candidates)() +} diff --git a/tools/riscv-torture/generator/src/main/scala/SeqBranch.scala b/tools/riscv-torture/generator/src/main/scala/SeqBranch.scala new file mode 100644 index 0000000..0d257d7 --- /dev/null +++ b/tools/riscv-torture/generator/src/main/scala/SeqBranch.scala @@ -0,0 +1,203 @@ +package torture + +import scala.collection.mutable.ArrayBuffer +import Rand._ + +class SeqBranch(xregs: HWRegPool) extends InstSeq +{ + override val seqname = "xbranch" + val taken = Label("__needs_branch_patch") + val nottakens = ArrayBuffer[Label](Label("crash_backward"), Label("crash_forward")) + val nottaken = rand_pick(nottakens) + def reverse_label(l: Label) = if(l == taken) nottaken else taken + + def helper_two_srcs_sameval_samereg_any() = () => + { + val reg_src = reg_read_any(xregs) + (reg_src, reg_src) + } + + def helper_two_srcs_sameval_samereg_zero() = () => + { + val reg_src = reg_read_zero(xregs) + (reg_src, reg_src) + } + + def helper_two_srcs_sameval_diffreg_any() = () => + { + val reg_src = reg_read_any(xregs) + val reg_dst1 = reg_write(xregs, reg_src) + val reg_dst2 = reg_write(xregs, reg_dst1) + insts += ADDI(reg_dst1, reg_src, Imm(0)) + insts += ADDI(reg_dst2, reg_dst1, Imm(0)) + (reg_dst1, reg_dst2) + } + + def helper_two_srcs_sameval_diffreg_zero() = () => + { + val reg_dst1 = reg_write_visible(xregs) + val reg_dst2 = reg_write(xregs) + insts += ADDI(reg_dst1, reg_read_zero(xregs), Imm(0)) + insts += ADDI(reg_dst2, reg_read_zero(xregs), Imm(0)) + (reg_dst1, reg_dst2) + } + + def helper_two_srcs_diffval_diffreg_bothpos() = () => + { + val reg_dst1 = reg_write_visible(xregs) + val reg_dst2 = reg_write(xregs, reg_dst1) + + insts += ADDI(reg_dst1, reg_read_zero(xregs), Imm(rand_filter(rand_imm, (x) => x > 0))) + insts += ADDI(reg_dst2, reg_dst1, Imm(rand_filter(rand_imm, (x) => x > 0))) + + // signed (+, ++), unsigned (+, ++) + (reg_dst1, reg_dst2) + } + + def helper_two_srcs_diffval_diffreg_bothneg() = () => + { + val reg_dst1 = reg_write_visible(xregs) + val reg_dst2 = reg_write(xregs, reg_dst1) + + insts += ADDI(reg_dst1, reg_read_zero(xregs), Imm(rand_filter(rand_imm, (x) => x < 0))) + insts += ADDI(reg_dst2, reg_dst1, Imm(rand_filter(rand_imm, (x) => x < 0))) + + // signed (-, --), unsigned (++++, +++) + (reg_dst1, reg_dst2) + } + + def helper_two_srcs_sameval_diffreg_oppositesign() = () => + { + val reg_src = reg_read_any(xregs) + val reg_dst1 = reg_write(xregs, reg_src) + val reg_dst2 = reg_write(xregs, reg_src) + val reg_one = reg_write_visible(xregs) + val reg_mask = reg_write_visible(xregs) + + insts += ADDI(reg_one, reg_read_zero(xregs), Imm(1)) + insts += SLL(reg_one, reg_one, Imm(31)) + insts += ADDI(reg_mask, reg_read_zero(xregs), Imm(-1)) + insts += XOR(reg_mask, reg_mask, reg_one) + insts += AND(reg_dst1, reg_src, reg_mask) + insts += OR(reg_dst2, reg_dst1, reg_one) + + // reg_dest1 sign bit 0, reg_dest2 sign bit 1 + (reg_dst1, reg_dst2) + } + + def helper_two_srcs_diffval_diffreg_oppositesign() = () => + { + val reg_src1 = reg_read_any(xregs) + val reg_src2 = reg_read_any(xregs) + val reg_dst1 = reg_write(xregs, reg_src1) + val reg_dst2 = reg_write(xregs, reg_src2) + val reg_one = reg_write_visible(xregs) + val reg_mask = reg_write_visible(xregs) + + insts += ADDI(reg_one, reg_read_zero(xregs), Imm(1)) + insts += SLL(reg_one, reg_one, Imm(31)) + insts += ADDI(reg_mask, reg_read_zero(xregs), Imm(-1)) + insts += XOR(reg_mask, reg_mask, reg_one) + insts += AND(reg_dst1, reg_src1, reg_mask) + insts += OR(reg_dst2, reg_src2, reg_one) + + // reg_dest1 sign bit 0, reg_dest2 sign bit 1 + (reg_dst1, reg_dst2) + } + + def seq_taken_j() = () => + { + insts += J(taken) + } + + def seq_taken_jal() = () => + { + val reg_x1 = reg_write_ra(xregs) + insts += JAL(taken) + } + + def seq_taken_jalr() = () => + { + val reg_x1 = reg_write_ra(xregs) + val reg_src1 = reg_read_zero(xregs) + val reg_dst1 = reg_write_hidden(xregs) + val reg_dst2 = reg_write_hidden(xregs) + + insts += LA(reg_dst1, Label("__needs_jalr_patch1")) + insts += JALR(reg_dst2, reg_dst1, Label("__needs_jalr_patch2")) + } + + def get_two_regs_and_branch_with_label( op: Opcode, helper: () => (Operand, Operand), label: Label, flip_ops:Boolean = false) = () => + { + val regs = helper() + if(!flip_ops) insts += op(regs._1, regs._2, label) else insts += op(regs._2, regs._1, label) + } + + // These tests have the same labels if the operand order is reversed + val reversible_tests = List( + (BEQ, helper_two_srcs_sameval_samereg_any, taken), + (BEQ, helper_two_srcs_sameval_samereg_zero, taken), + (BEQ, helper_two_srcs_sameval_diffreg_any, taken), + (BEQ, helper_two_srcs_sameval_diffreg_zero, taken), + (BEQ, helper_two_srcs_diffval_diffreg_bothpos, nottaken), + (BEQ, helper_two_srcs_diffval_diffreg_bothneg, nottaken), + (BEQ, helper_two_srcs_sameval_diffreg_oppositesign, nottaken), + (BEQ, helper_two_srcs_diffval_diffreg_oppositesign, nottaken), + (BNE, helper_two_srcs_sameval_samereg_any, nottaken), + (BNE, helper_two_srcs_sameval_samereg_zero, nottaken), + (BNE, helper_two_srcs_sameval_diffreg_any, nottaken), + (BNE, helper_two_srcs_sameval_diffreg_zero, nottaken), + (BNE, helper_two_srcs_diffval_diffreg_bothpos, taken), + (BNE, helper_two_srcs_diffval_diffreg_bothneg, taken), + (BNE, helper_two_srcs_sameval_diffreg_oppositesign, taken), + (BNE, helper_two_srcs_diffval_diffreg_oppositesign, taken), + (BLT, helper_two_srcs_sameval_samereg_any, nottaken), + (BLT, helper_two_srcs_sameval_samereg_zero, nottaken), + (BLT, helper_two_srcs_sameval_diffreg_any, nottaken), + (BLT, helper_two_srcs_sameval_diffreg_zero, nottaken), + (BLTU, helper_two_srcs_sameval_samereg_any, nottaken), + (BLTU, helper_two_srcs_sameval_samereg_zero, nottaken), + (BLTU, helper_two_srcs_sameval_diffreg_any, nottaken), + (BLTU, helper_two_srcs_sameval_diffreg_zero, nottaken), + (BGE, helper_two_srcs_sameval_samereg_any, taken), + (BGE, helper_two_srcs_sameval_samereg_zero, taken), + (BGE, helper_two_srcs_sameval_diffreg_any, taken), + (BGE, helper_two_srcs_sameval_diffreg_zero, taken), + (BGEU, helper_two_srcs_sameval_samereg_any, taken), + (BGEU, helper_two_srcs_sameval_samereg_zero, taken), + (BGEU, helper_two_srcs_sameval_diffreg_any, taken), + (BGEU, helper_two_srcs_sameval_diffreg_zero, taken) + ) + + // These tests need opposite labels if the operand order is reversed + val chiral_tests = List( + (BLT, helper_two_srcs_diffval_diffreg_bothpos, taken), + (BLT, helper_two_srcs_diffval_diffreg_bothneg, nottaken), + (BLT, helper_two_srcs_sameval_diffreg_oppositesign, nottaken), + (BLT, helper_two_srcs_diffval_diffreg_oppositesign, nottaken), + (BLTU, helper_two_srcs_diffval_diffreg_bothpos, taken), + (BLTU, helper_two_srcs_diffval_diffreg_bothneg, nottaken), + (BLTU, helper_two_srcs_sameval_diffreg_oppositesign, taken), + (BLTU, helper_two_srcs_diffval_diffreg_oppositesign, taken), + (BGE, helper_two_srcs_diffval_diffreg_bothpos, nottaken), + (BGE, helper_two_srcs_diffval_diffreg_bothneg, taken), + (BGE, helper_two_srcs_sameval_diffreg_oppositesign, taken), + (BGE, helper_two_srcs_diffval_diffreg_oppositesign, taken), + (BGEU, helper_two_srcs_diffval_diffreg_bothpos, nottaken), + (BGEU, helper_two_srcs_diffval_diffreg_bothneg, taken), + (BGEU, helper_two_srcs_sameval_diffreg_oppositesign, nottaken), + (BGEU, helper_two_srcs_diffval_diffreg_oppositesign, nottaken) + ) + + val candidates = new ArrayBuffer[() => insts.type] + + candidates += seq_taken_j() + candidates += seq_taken_jal() + candidates += seq_taken_jalr() + + reversible_tests.foreach( t => candidates += get_two_regs_and_branch_with_label(t._1, t._2, t._3, false)) + chiral_tests.foreach( t => candidates += get_two_regs_and_branch_with_label(t._1, t._2, t._3, false)) + chiral_tests.foreach( t => candidates += get_two_regs_and_branch_with_label(t._1, t._2, reverse_label(t._3), true)) + + rand_pick(candidates)() +} diff --git a/tools/riscv-torture/generator/src/main/scala/SeqFDiv.scala b/tools/riscv-torture/generator/src/main/scala/SeqFDiv.scala new file mode 100644 index 0000000..65c6d84 --- /dev/null +++ b/tools/riscv-torture/generator/src/main/scala/SeqFDiv.scala @@ -0,0 +1,47 @@ +package torture + +import scala.collection.mutable.ArrayBuffer +import Rand._ + +class SeqFDiv(fregs_s: HWRegPool, fregs_d: HWRegPool) extends InstSeq +{ + override val seqname = "fdiv" + def seq_src1_s(op: Opcode) = () => + { + val src1 = reg_read_any(fregs_s) + val dest = reg_write(fregs_s, src1) + insts += op(dest, src1) + } + + def seq_src2_s(op: Opcode) = () => + { + val src1 = reg_read_any(fregs_s) + val src2 = reg_read_any(fregs_s) + val dest = reg_write(fregs_s, src1, src2) + insts += op(dest, src1, src2) + } + + def seq_src1_d(op: Opcode) = () => + { + val src1 = reg_read_any(fregs_d) + val dest = reg_write(fregs_d, src1) + insts += op(dest, src1) + } + + def seq_src2_d(op: Opcode) = () => + { + val src1 = reg_read_any(fregs_d) + val src2 = reg_read_any(fregs_d) + val dest = reg_write(fregs_d, src1, src2) + insts += op(dest, src1, src2) + } + + val candidates = new ArrayBuffer[() => insts.type] + + candidates += seq_src1_s(FSQRT_S) + candidates += seq_src1_d(FSQRT_D) + candidates += seq_src2_s(FDIV_S) + candidates += seq_src2_d(FDIV_D) + + rand_pick(candidates)() +} diff --git a/tools/riscv-torture/generator/src/main/scala/SeqFPMem.scala b/tools/riscv-torture/generator/src/main/scala/SeqFPMem.scala new file mode 100644 index 0000000..121c5d0 --- /dev/null +++ b/tools/riscv-torture/generator/src/main/scala/SeqFPMem.scala @@ -0,0 +1,38 @@ +package torture + +import scala.collection.mutable.ArrayBuffer +import Rand._ + +class SeqFPMem(xregs: HWRegPool, fregs_s: HWRegPool, fregs_d: HWRegPool, mem: Mem) extends InstSeq +{ + override val seqname = "fpmem" + + def seq_load_addrfn(op: Opcode, addrfn: (Int) => Int, fregpool: HWRegPool) = () => + { + val reg_addr = reg_write_hidden(xregs) + val reg_dest = reg_write_visible(fregpool) + val addr = addrfn(mem.size) + val imm = rand_imm() + insts += LA(reg_addr, BaseImm(mem.toString, addr-imm)) + insts += op(reg_dest, RegImm(reg_addr, imm)) + } + + def seq_store_addrfn(op: Opcode, addrfn: (Int) => Int, fregpool: HWRegPool) = () => + { + val reg_addr = reg_write_hidden(xregs) + val reg_src = reg_read_visible(fregpool) + val addr = addrfn(mem.size) + val imm = rand_imm() + insts += LA(reg_addr, BaseImm(mem.toString, addr-imm)) + insts += op(reg_src, RegImm(reg_addr, imm)) + } + + val candidates = new ArrayBuffer[() => insts.type] + candidates += seq_load_addrfn(FLW, rand_addr_w, fregs_s) + candidates += seq_store_addrfn(FSW, rand_addr_w, fregs_s) + candidates += seq_load_addrfn(FLD, rand_addr_d, fregs_d) + candidates += seq_store_addrfn(FSD, rand_addr_d, fregs_d) + + rand_pick(candidates)() + +} diff --git a/tools/riscv-torture/generator/src/main/scala/SeqFPU.scala b/tools/riscv-torture/generator/src/main/scala/SeqFPU.scala new file mode 100644 index 0000000..f2e5d14 --- /dev/null +++ b/tools/riscv-torture/generator/src/main/scala/SeqFPU.scala @@ -0,0 +1,72 @@ +package torture + +import scala.collection.mutable.ArrayBuffer +import Rand._ + +class SeqFPU(fregs_s: HWRegPool, fregs_d: HWRegPool) extends InstSeq +{ + override val seqname = "fgen" + def seq_src1_s(op: Opcode) = () => + { + val src1 = reg_read_any(fregs_s) + val dest = reg_write(fregs_s, src1) + insts += op(dest, src1) + } + + def seq_src2_s(op: Opcode) = () => + { + val src1 = reg_read_any(fregs_s) + val src2 = reg_read_any(fregs_s) + val dest = reg_write(fregs_s, src1, src2) + insts += op(dest, src1, src2) + } + + def seq_src3_s(op: Opcode) = () => + { + val src1 = reg_read_any(fregs_s) + val src2 = reg_read_any(fregs_s) + val src3 = reg_read_any(fregs_s) + val dest = reg_write(fregs_s, src1, src2, src3) + insts += op(dest, src1, src2, src3) + } + + def seq_src1_d(op: Opcode) = () => + { + val src1 = reg_read_any(fregs_d) + val dest = reg_write(fregs_d, src1) + insts += op(dest, src1) + } + + def seq_src2_d(op: Opcode) = () => + { + val src1 = reg_read_any(fregs_d) + val src2 = reg_read_any(fregs_d) + val dest = reg_write(fregs_d, src1, src2) + insts += op(dest, src1, src2) + } + + def seq_src3_d(op: Opcode) = () => + { + val src1 = reg_read_any(fregs_d) + val src2 = reg_read_any(fregs_d) + val src3 = reg_read_any(fregs_d) + val dest = reg_write(fregs_d, src1, src2, src3) + insts += op(dest, src1, src2, src3) + } + + val candidates = new ArrayBuffer[() => insts.type] + + for (op <- List(FADD_S, FSUB_S, FMUL_S, FMIN_S, FMAX_S)) + candidates += seq_src2_s(op) + + for (op <- List(FADD_D, FSUB_D, FMUL_D, FMIN_D, FMAX_D)) + candidates += seq_src2_d(op) + + for (op <- List(FMADD_S, FNMADD_S, FMSUB_S, FNMSUB_S)) + candidates += seq_src3_s(op) + + for (op <- List(FMADD_D, FNMADD_D, FMSUB_D, FNMSUB_D)) + candidates += seq_src3_d(op) + + rand_pick(candidates)() +} diff --git a/tools/riscv-torture/generator/src/main/scala/SeqFaX.scala b/tools/riscv-torture/generator/src/main/scala/SeqFaX.scala new file mode 100644 index 0000000..ce823a3 --- /dev/null +++ b/tools/riscv-torture/generator/src/main/scala/SeqFaX.scala @@ -0,0 +1,56 @@ +package torture + +import scala.collection.mutable.ArrayBuffer +import Rand._ + +class SeqFaX(xregs: HWRegPool, fregs_s: HWRegPool, fregs_d: HWRegPool) extends InstSeq +{ + override val seqname = "fax" + def seq_src1(op: Opcode, dst_pool: HWRegPool, src_pool: HWRegPool) = () => + { + val src1 = reg_read_any(src_pool) + val dest = reg_write(dst_pool, src1) + insts += op(dest, src1) + } + + def seq_src2(op: Opcode, dst_pool: HWRegPool, src_pool: HWRegPool) = () => + { + val src1 = reg_read_any(src_pool) + val src2 = reg_read_any(src_pool) + val dest = reg_write(dst_pool, src1, src2) + insts += op(dest, src1, src2) + } + + val candidates = new ArrayBuffer[() => insts.type] + + // Intra-FPU Instructions + candidates += seq_src1(FCVT_S_D, fregs_s, fregs_d) + candidates += seq_src1(FCVT_D_S, fregs_d, fregs_s) + + for (op <- List(FSGNJ_S, FSGNJN_S, FSGNJX_S)) + candidates += seq_src2(op, fregs_s, fregs_s) + + for (op <- List(FSGNJ_D, FSGNJN_D, FSGNJX_D)) + candidates += seq_src2(op, fregs_d, fregs_d) + + // X<->F Instructions + for (op <- List(FCVT_S_L, FCVT_S_LU, FCVT_S_W, FCVT_S_WU, FMV_S_X)) + candidates += seq_src1(op, fregs_s, xregs) + + for (op <- List(FCVT_D_L, FCVT_D_LU, FCVT_D_W, FCVT_D_WU, FMV_D_X)) + candidates += seq_src1(op, fregs_d, xregs) + + for (op <- List(FCVT_L_S, FCVT_LU_S, FCVT_W_S, FCVT_WU_S, FMV_X_S)) + candidates += seq_src1(op, xregs, fregs_s) + + for (op <- List(FCVT_L_D, FCVT_LU_D, FCVT_W_D, FCVT_WU_D, FMV_X_D)) + candidates += seq_src1(op, xregs, fregs_d) + + for (op <- List(FEQ_S, FLT_S, FLE_S)) + candidates += seq_src2(op, xregs, fregs_s) + + for (op <- List(FEQ_D, FLT_D, FLE_D)) + candidates += seq_src2(op, xregs, fregs_d) + + rand_pick(candidates)() +} diff --git a/tools/riscv-torture/generator/src/main/scala/SeqMem.scala b/tools/riscv-torture/generator/src/main/scala/SeqMem.scala new file mode 100644 index 0000000..53cef34 --- /dev/null +++ b/tools/riscv-torture/generator/src/main/scala/SeqMem.scala @@ -0,0 +1,142 @@ +package torture + +import scala.collection.mutable.ArrayBuffer +import Rand._ + +class SeqMem(xregs: HWRegPool, mem: Mem, use_amo: Boolean) extends InstSeq +{ + override val seqname = "xmem" + def seq_load_addrfn(op: Opcode, addrfn: (Int) => Int) = () => + { + val reg_addr = reg_write_hidden(xregs) + val reg_dest = reg_write_visible(xregs) + val addr = addrfn(mem.size) + val imm = rand_imm() + + insts += LA(reg_addr, BaseImm(mem.toString, addr-imm)) + insts += op(reg_dest, RegImm(reg_addr, imm)) + } + + def seq_store_addrfn(op: Opcode, addrfn: (Int) => Int) = () => + { + val reg_addr = reg_write_hidden(xregs) + val reg_src = reg_read_visible(xregs) + val addr = addrfn(mem.size) + val imm = rand_imm() + + insts += LA(reg_addr, BaseImm(mem.toString, addr-imm)) + insts += op(reg_src, RegImm(reg_addr, imm)) + } + + def seq_amo_addrfn(op: Opcode, addrfn: (Int) => Int) = () => + { + val reg_addr = reg_write_hidden(xregs) + val reg_dest = reg_write_visible(xregs) + val reg_src = reg_read_visible(xregs) + val addr = addrfn(mem.size) + + insts += LA(reg_addr, BaseImm(mem.toString, addr)) + insts += op(reg_dest, reg_src, RegImm(reg_addr, 0)) + } + + // test st and ld sequences in which the double-word addresses match + // but the lower-order bits may differ. Randomize whether the dependency + // is ld->st or st->ld. + def seq_stld_overlap() = () => + { + object AccessType extends Enumeration + { + type AccessType = Value + val byte, ubyte, hword, uhword, word, uword, dword = Value + + def getRandOpAndAddr (dw_addr: Int, is_store: Boolean): (Opcode, Int) = + { + val typ = AccessType.values.toIndexedSeq(rand_range(0,4)) + if (is_store) + { + if (typ == byte || typ ==ubyte) (SB, dw_addr + rand_addr_b(8)) + else if (typ == hword || typ ==uhword) (SH, dw_addr + rand_addr_h(8)) + else if (typ == word || typ ==uword) (SW, dw_addr + rand_addr_w(8)) + else (SD, dw_addr) + } + else + { + if (typ == byte) (LB, dw_addr + rand_addr_b(8)) + else if (typ == ubyte) (LBU, dw_addr + rand_addr_b(8)) + else if (typ == hword) (LH, dw_addr + rand_addr_h(8)) + else if (typ == uhword) (LHU, dw_addr + rand_addr_h(8)) + else if (typ == word) (LW, dw_addr + rand_addr_w(8)) + else if (typ == uword) (LWU, dw_addr + rand_addr_w(8)) + else (LD, dw_addr) + } + } + } + import AccessType._ + + val l_reg_addr = reg_write_hidden(xregs) + val s_reg_addr = reg_write_hidden(xregs) + val s_reg_src = reg_read_visible(xregs) + val l_reg_dest = reg_write_visible(xregs) + + val dw_addr = rand_addr_d(mem.size) + val s_imm = rand_imm() + val l_imm = rand_imm() + + val (lop, l_addr) = AccessType.getRandOpAndAddr(dw_addr, is_store=false) + val (sop, s_addr) = AccessType.getRandOpAndAddr(dw_addr, is_store=true) + //println("dwaddr: " + dw_addr + ",sop: " + sop.name + ",lop: " + lop.name + " saddr: " + s_addr + ", laddr: " + l_addr) + + insts += LA(l_reg_addr, BaseImm(mem.toString, l_addr-l_imm)) + insts += LA(s_reg_addr, BaseImm(mem.toString, s_addr-s_imm)) + if (math.random < 0.5) + { + insts += lop(l_reg_dest, RegImm(l_reg_addr, l_imm)) + insts += sop(s_reg_src, RegImm(s_reg_addr, s_imm)) + } + else + { + insts += sop(s_reg_src, RegImm(s_reg_addr, s_imm)) + insts += lop(l_reg_dest, RegImm(l_reg_addr, l_imm)) + } + } + + val candidates = new ArrayBuffer[() => insts.type] + + candidates += seq_stld_overlap() + candidates += seq_stld_overlap() + + candidates += seq_load_addrfn(LB, rand_addr_b) + candidates += seq_load_addrfn(LBU, rand_addr_b) + candidates += seq_load_addrfn(LH, rand_addr_h) + candidates += seq_load_addrfn(LHU, rand_addr_h) + candidates += seq_load_addrfn(LW, rand_addr_w) + //candidates += seq_load_addrfn(LWU, rand_addr_w) + //candidates += seq_load_addrfn(LD, rand_addr_d) + + candidates += seq_store_addrfn(SB, rand_addr_b) + candidates += seq_store_addrfn(SH, rand_addr_h) + candidates += seq_store_addrfn(SW, rand_addr_w) + //candidates += seq_store_addrfn(SD, rand_addr_d) + + if (use_amo) + { + candidates += seq_amo_addrfn(AMOADD_W, rand_addr_w) + candidates += seq_amo_addrfn(AMOSWAP_W, rand_addr_w) + candidates += seq_amo_addrfn(AMOAND_W, rand_addr_w) + candidates += seq_amo_addrfn(AMOOR_W, rand_addr_w) + candidates += seq_amo_addrfn(AMOMIN_W, rand_addr_w) + candidates += seq_amo_addrfn(AMOMINU_W, rand_addr_w) + candidates += seq_amo_addrfn(AMOMAX_W, rand_addr_w) + candidates += seq_amo_addrfn(AMOMAXU_W, rand_addr_w) + candidates += seq_amo_addrfn(AMOADD_D, rand_addr_d) + candidates += seq_amo_addrfn(AMOSWAP_D, rand_addr_d) + candidates += seq_amo_addrfn(AMOAND_D, rand_addr_d) + candidates += seq_amo_addrfn(AMOOR_D, rand_addr_d) + candidates += seq_amo_addrfn(AMOMIN_D, rand_addr_d) + candidates += seq_amo_addrfn(AMOMINU_D, rand_addr_d) + candidates += seq_amo_addrfn(AMOMAX_D, rand_addr_d) + candidates += seq_amo_addrfn(AMOMAXU_D, rand_addr_d) + } + + rand_pick(candidates)() +} diff --git a/tools/riscv-torture/generator/src/main/scala/SeqSeq.scala b/tools/riscv-torture/generator/src/main/scala/SeqSeq.scala new file mode 100644 index 0000000..41d7d82 --- /dev/null +++ b/tools/riscv-torture/generator/src/main/scala/SeqSeq.scala @@ -0,0 +1,91 @@ +package torture + +import scala.collection.mutable.ArrayBuffer +import scala.collection.mutable.HashMap +import Rand._ + +class SeqSeq(vregs: HWRegPool, pregs: HWRegPool, def_preg: Reg, sregs: HWRegPool, aregs: HWRegPool, xregs: HWRegPool, mem: Mem, nseqs: Int, mixcfg: Map[String,Int], vl: Int, use_mul: Boolean, use_div: Boolean, use_mix: Boolean, use_fpu: Boolean, use_fma: Boolean, use_fcvt: Boolean, use_fdiv: Boolean, use_amo: Boolean, use_seg: Boolean, use_stride: Boolean, pred_alu: Boolean, pred_mem: Boolean) extends VFInstSeq +{ + val seqs = new ArrayBuffer[VFInstSeq] + val seqs_active = new ArrayBuffer[VFInstSeq] + var killed_seqs = 0 + val seqstats = new HashMap[String,Int].withDefaultValue(0) + + def seqs_not_allocated = seqs.filter((x) => !x.allocated) + def is_seqs_empty = seqs_not_allocated.length == 0 + def is_seqs_active_empty = seqs_active.length == 0 + + def are_pools_fully_unallocated = List(vregs, pregs, sregs).forall(_.is_fully_unallocated) + + val name_to_seq = Map( + "vmem" -> (() => new SeqVMem(xregs, vregs, pregs, def_preg, sregs, aregs, mem.asInstanceOf[VMem], vl, use_amo,use_seg, use_stride, pred_mem)), + "valu" -> (() => new SeqVALU(vregs, pregs, def_preg, sregs, use_mul, use_div, use_mix, use_fpu, use_fma, use_fcvt, use_fdiv, pred_alu)), // TODO: Clean up + "vpop" -> (() => new SeqVPop(vregs, pregs, def_preg, sregs)), + "vonly" -> (() => new SeqVOnly(vregs, pregs, sregs))) + + val prob_tbl = new ArrayBuffer[(Int, () => VFInstSeq)] + mixcfg foreach {case(name, prob) => (prob_tbl += ((prob, name_to_seq(name))))} + + def gen_seq(): Unit = + { + val nxtseq = VFInstSeq(prob_tbl) + seqs += nxtseq + seqstats(nxtseq.seqname) += 1 + } + + def seqs_find_active(): Unit = + { + for (seq <- seqs_not_allocated) + { + vregs.backup() + pregs.backup() + sregs.backup() + aregs.backup() + xregs.backup() + + if (seq.allocate_regs()) + { + seqs_active += seq + } + else + { + vregs.restore() + pregs.restore() + sregs.restore() + aregs.restore() + xregs.restore() + // because the setup instructions for a vf seq are only run once we + // cannot free va or vs regs to be reused so we kill seqs that can be + // allocated + seqs -= seq + killed_seqs += 1 + seqstats(seq.seqname) -= 1 + + return + } + } + } + + for(i <- 1 to nseqs) gen_seq() + + while(!is_seqs_empty) + { + seqs_find_active() + + while(!is_seqs_active_empty) + { + val seq = rand_pick(seqs_active) + if(seq.inst_left) insts += seq.next_inst() + if(seq.vinst_left) vinsts += seq.next_vinst() + + if(seq.is_done) { + extra_hidden_data.appendAll(seq.extra_hidden_data) + seqs_active -= seq + } + } + } + for(seq <- seqs) seq.free_regs() + + if(killed_seqs >= (nseqs*5)) + println("warning: a SeqSeq killed an excessive number of sequences. (#V=%d, #P=%d, #S=%d)" format (vregs.size, pregs.size, sregs.size)) +} diff --git a/tools/riscv-torture/generator/src/main/scala/SeqVALU.scala b/tools/riscv-torture/generator/src/main/scala/SeqVALU.scala new file mode 100644 index 0000000..ffe3fdb --- /dev/null +++ b/tools/riscv-torture/generator/src/main/scala/SeqVALU.scala @@ -0,0 +1,106 @@ +package torture + +import scala.collection.mutable.ArrayBuffer +import Rand._ + +class SeqVALU(vregs: HWRegPool, pregs: HWRegPool, def_preg: Reg, sregs: HWRegPool, use_mul: Boolean, use_div: Boolean, use_mix: Boolean, use_fpu: Boolean, use_fma: Boolean, use_fcvt: Boolean, use_fdiv: Boolean, use_pred: Boolean) extends VFInstSeq //TODO: better configuration +{ + override val seqname = "valu" + val pred = if(use_pred) PredReg(reg_read_any(pregs), false) + else PredReg(def_preg, false) + + def seq_src1(op: Opcode, dreg: HWRegPool, s1reg: HWRegPool) = () => + { + val src1 = reg_read_any(s1reg) + val dest = reg_write(dreg, src1) + if(dreg == sregs) vinsts += op(dest, src1) + else vinsts += op(dest, src1, pred) + } + + def seq_src2(op: Opcode, dreg: HWRegPool, s1reg: HWRegPool, s2reg: HWRegPool) = () => + { + val src1 = reg_read_any(s1reg) + val src2 = reg_read_any(s2reg) + val dest = reg_write(dreg, src1, src2) + if(dreg == sregs) vinsts += op(dest, src1, src2) + else vinsts += op(dest, src1, src2, pred) + } + + def seq_src3(op: Opcode, dreg: HWRegPool, s1reg: HWRegPool, s2reg: HWRegPool, s3reg: HWRegPool) = () => + { + val src1 = reg_read_any(s1reg) + val src2 = reg_read_any(s2reg) + val src3 = reg_read_any(s3reg) + val dest = reg_write(dreg, src1, src2, src3) + if(dreg == sregs) vinsts += op(dest, src1, src2, src3) + else vinsts += op(dest, src1, src2, src3, pred) + } + + val candidates = new ArrayBuffer[() => vinsts.type] + + val oplist1 = new ArrayBuffer[Opcode] + val oplist2 = new ArrayBuffer[Opcode] + val oplist3 = new ArrayBuffer[Opcode] + + oplist2 += (VADD, VSUB, VSLL, VXOR, VSRL, VSRA, VOR, VAND) + oplist2 += (VADDW, VSUBW, VSLLW, VSRLW, VSRAW) + if (use_mul) oplist2 += (VMUL, VMULH, VMULHSU, VMULHU, VMULW) + if (use_div) oplist2 += (VDIV, VDIVU, VREM, VREMU, VDIVW, VDIVUW, VREMW, VREMUW) + if (use_fpu) + { + oplist2 += (VFADD_S, VFSUB_S, VFMUL_S, VFMIN_S, VFMAX_S, + VFADD_D, VFSUB_D, VFMUL_D, VFMIN_D, VFMAX_D, + VFSGNJ_S, VFSGNJN_S, VFSGNJX_S, VFSGNJ_D, VFSGNJN_D, VFSGNJX_D) + if (use_fdiv) + { + oplist1 += (VFSQRT_S, VFSQRT_D) + oplist2 += (VFDIV_S, VFDIV_D) + } + } + if (use_fma) oplist3 += (VFMADD_S, VFMSUB_S, VFNMSUB_S, VFNMADD_S, + VFMADD_D, VFMSUB_D, VFNMSUB_D, VFNMADD_D) + if (use_fcvt) oplist1 += (VFCVT_S_D, VFCVT_D_S, VFCVT_S_L, VFCVT_S_LU, VFCVT_S_W, + VFCVT_S_WU, VFCVT_D_L, VFCVT_D_LU, VFCVT_D_W, VFCVT_D_WU, VFCVT_L_S, + VFCVT_LU_S, VFCVT_W_S, VFCVT_WU_S, VFCVT_L_D, VFCVT_LU_D, + VFCVT_W_D, VFCVT_WU_D) + + for (op <- oplist1) + { + candidates += seq_src1(op,vregs,vregs) + candidates += seq_src1(op,sregs,sregs) + + if (use_mix) + { + candidates += seq_src1(op,vregs,sregs) + } + } + for (op <- oplist2) + { + candidates += seq_src2(op,vregs,vregs,vregs) + candidates += seq_src2(op,sregs,sregs,sregs) + + if (use_mix) + { + candidates += seq_src2(op,vregs,vregs,sregs) + candidates += seq_src2(op,vregs,sregs,vregs) + candidates += seq_src2(op,vregs,sregs,sregs) + } + } + for (op <- oplist3) + { + candidates += seq_src3(op,vregs,vregs,vregs,vregs) + candidates += seq_src3(op,sregs,sregs,sregs,sregs) + + if (use_mix) + { + candidates += seq_src3(op,vregs,vregs,vregs,sregs) + candidates += seq_src3(op,vregs,vregs,sregs,vregs) + candidates += seq_src3(op,vregs,vregs,sregs,sregs) + candidates += seq_src3(op,vregs,sregs,vregs,sregs) + candidates += seq_src3(op,vregs,sregs,sregs,vregs) + candidates += seq_src3(op,vregs,sregs,sregs,sregs) + } + } + + rand_pick(candidates)() +} diff --git a/tools/riscv-torture/generator/src/main/scala/SeqVMem.scala b/tools/riscv-torture/generator/src/main/scala/SeqVMem.scala new file mode 100644 index 0000000..6a971a7 --- /dev/null +++ b/tools/riscv-torture/generator/src/main/scala/SeqVMem.scala @@ -0,0 +1,243 @@ +package torture + +import scala.collection.mutable.ArrayBuffer +import Rand._ + +object SeqVMem +{ + var cnt = 0 +} + +class SeqVMem(xregs: HWRegPool, vregs: HWRegPool, pregs: HWRegPool, def_preg: Reg, sregs:HWRegPool, aregs: HWRegPool, mem: VMem, vl: Int, use_amo: Boolean, use_seg: Boolean, use_stride: Boolean, use_pred: Boolean) extends VFInstSeq +{ + override val seqname = "vmem_" + SeqVMem.cnt + SeqVMem.cnt += 1 + val pred = if(use_pred) PredReg(reg_read_any(pregs), false) + else PredReg(def_preg, false) + + def helper_setup_address(reg_addr: Reg, reg_vaddr: Reg, m: Mem, baseaddr: Int, reg_vstride: Option[Reg] = None, stride: Int = 0) = + { + insts += LA(reg_addr, BaseImm(m.toString, baseaddr)) + insts += VMCA(reg_vaddr, reg_addr) + reg_vstride match { + case Some(reg) => + { + insts += LI(reg_addr, Imm(stride)) + insts += VMCA(reg, reg_addr) + } + case None => {} + } + } + def helper_setup_scalar(reg_addr: Reg, reg_vaddr: Reg, m: Mem, baseaddr: Int, reg_vstride: Option[Reg] = None, stride: Int = 0) = + { + insts += LA(reg_addr, BaseImm(m.toString, baseaddr)) + insts += VMCS(reg_vaddr, reg_addr) + reg_vstride match { + case Some(reg) => + { + insts += LI(reg_addr, Imm(stride)) + insts += VMCS(reg, reg_addr) + } + case None => {} + } + } + + def seq_load_addrfn(op: Opcode, addrfn: (Int) => Int, seg: Option[Int] = None, stride: Option[Reg] = None) = () => + { + val reg_addr = reg_write_hidden(xregs) + val reg_vaddr = reg_write_hidden(aregs) + val reg_dest = seg match { + case Some(segs) => reg_write_visible_consec(vregs, segs+1)//resever seglen+1 regs + case None => reg_write_visible(vregs) + } + val addr = addrfn(mem.ut_size) + + helper_setup_address(reg_addr, reg_vaddr, mem, addr, stride, addrfn(mem.ut_size)) + (seg, stride) match { + case (Some(segs), Some(reg)) => vinsts += op(reg_dest, reg_vaddr, reg, Imm(segs), pred) + case (Some(segs), None) => vinsts += op(reg_dest, reg_vaddr, Imm(segs), pred) + case (None, Some(reg)) => vinsts += op(reg_dest, reg_vaddr, reg, pred) + case (None, None) => vinsts += op(reg_dest, reg_vaddr, pred) + } + } + + def seq_load_seg_addrfn(op: Opcode, addrfn: (Int) => Int, bytes :Int) = + { + val seglen = rand_seglen + assert(bytes*seglen <= mem.ut_size, + "Per uthread memory must be larger than seglen*bytes") + seq_load_addrfn(op, addrfn, Some(seglen), None) + } + + def seq_load_stride_addrfn(op: Opcode, addrfn: (Int) => Int) = + { + val reg_vstride= reg_write_hidden(aregs) + seq_load_addrfn(op, addrfn, None, Some(reg_vstride)) + } + + def seq_load_seg_stride_addrfn(op: Opcode, addrfn: (Int) => Int, bytes :Int) = + { + val seglen = rand_seglen + assert(bytes*seglen <= mem.ut_size, + "Per uthread memory must be larger than seglen*bytes") + val reg_vstride= reg_write_hidden(aregs) + seq_load_addrfn(op, addrfn, Some(seglen), Some(reg_vstride)) + } + + def seq_store_addrfn(op: Opcode, addrfn: (Int) => Int, seg: Option[Int] = None, stride: Option[Reg] = None) = () => + { + val reg_addr = reg_write_hidden(xregs) + val reg_vaddr = reg_write_hidden(aregs) + val reg_src = seg match { + case Some(segs) => reg_read_visible_consec(vregs, segs+1) //reserve seglen+1 regs + case None => reg_read_visible(vregs) + } + val addr = addrfn(mem.ut_size) + + helper_setup_address(reg_addr, reg_vaddr, mem, addr, stride, addrfn(mem.ut_size)) + (seg, stride) match { + case (Some(segs), Some(reg)) => vinsts += op(reg_src, reg_vaddr, reg, Imm(segs), pred) + case (Some(segs), None) => vinsts += op(reg_src, reg_vaddr, Imm(segs), pred) + case (None, Some(reg)) => vinsts += op(reg_src, reg_vaddr, reg, pred) + case (None, None) => vinsts += op(reg_src, reg_vaddr, pred) + } + } + + def seq_store_seg_addrfn(op: Opcode, addrfn: (Int) => Int, bytes: Int) = + { + val seglen = rand_seglen + assert(bytes*seglen <= mem.ut_size, + "Per uthread memory must be larger than seglen*bytes") + seq_store_addrfn(op, addrfn, Some(seglen), None) + } + + def seq_store_stride_addrfn(op: Opcode, addrfn: (Int) => Int) = + { + val reg_vstride= reg_write_hidden(aregs) + seq_store_addrfn(op, addrfn, None, Some(reg_vstride)) + } + + def seq_store_seg_stride_addrfn(op: Opcode, addrfn: (Int) => Int, bytes: Int) = + { + val seglen = rand_seglen + assert(bytes*seglen <= mem.ut_size, + "Per uthread memory must be larger than seglen*bytes") + val reg_vstride= reg_write_hidden(aregs) + seq_store_addrfn(op, addrfn, Some(seglen), Some(reg_vstride)) + } + + def seq_amo_addrfn(op: Opcode, addrfn: (Int) => Int, addr_reg: HWRegPool, data_reg: HWRegPool) = () => + { + val reg_addr = reg_write_hidden(xregs) + val reg_dest = reg_write_visible(vregs) + + val reg_vaddr = reg_write_hidden(addr_reg) + val reg_src = reg_read_visible(data_reg) + + if(addr_reg == vregs) { // Generate a vector's worth of addresses + val amo_addr_mem = new Mem(seqname+"_amo_addr_init", 8*vl) + extra_hidden_data += MemAddrDump(amo_addr_mem, addrfn, mem.size) + + val reg_help = reg_write_hidden(aregs) + helper_setup_address(reg_addr, reg_help, amo_addr_mem, 0) + + val reg_xhelp = reg_write_hidden(xregs) + val reg_scalar = reg_write_hidden(sregs) + helper_setup_scalar(reg_xhelp, reg_scalar, mem, 0) + + vinsts += VLD(reg_vaddr, reg_help, PredReg(def_preg, false)) + vinsts += VADD(reg_vaddr, reg_vaddr, reg_scalar, PredReg(def_preg, false)) + } else { // Single address + helper_setup_scalar(reg_addr, reg_vaddr, mem, addrfn(mem.size)) + } + + vinsts += op(reg_dest, RegImm(reg_vaddr, 0), reg_src, pred) + } + + val candidates = new ArrayBuffer[() => vinsts.type] + + candidates += seq_load_addrfn(VLB, rand_addr_b) + candidates += seq_load_addrfn(VLBU, rand_addr_b) + candidates += seq_load_addrfn(VLH, rand_addr_h) + candidates += seq_load_addrfn(VLHU, rand_addr_h) + candidates += seq_load_addrfn(VLW, rand_addr_w) + candidates += seq_load_addrfn(VLWU, rand_addr_w) + candidates += seq_load_addrfn(VLD, rand_addr_d) + + candidates += seq_store_addrfn(VSB, rand_addr_b) + candidates += seq_store_addrfn(VSH, rand_addr_h) + candidates += seq_store_addrfn(VSW, rand_addr_w) + candidates += seq_store_addrfn(VSD, rand_addr_d) + + if(use_seg) + { + candidates += seq_load_seg_addrfn(VLSEGB, rand_addr_b, 1) + candidates += seq_load_seg_addrfn(VLSEGBU, rand_addr_b, 1) + candidates += seq_load_seg_addrfn(VLSEGH, rand_addr_h, 2) + candidates += seq_load_seg_addrfn(VLSEGHU, rand_addr_h, 2) + candidates += seq_load_seg_addrfn(VLSEGW, rand_addr_w, 4) + candidates += seq_load_seg_addrfn(VLSEGWU, rand_addr_w, 4) + candidates += seq_load_seg_addrfn(VLSEGD, rand_addr_d, 8) + + candidates += seq_store_seg_addrfn(VSSEGB, rand_addr_b, 1) + candidates += seq_store_seg_addrfn(VSSEGH, rand_addr_h, 2) + candidates += seq_store_seg_addrfn(VSSEGW, rand_addr_w, 4) + candidates += seq_store_seg_addrfn(VSSEGD, rand_addr_d, 8) + } + + if(use_stride) + { + candidates += seq_load_stride_addrfn(VLSTB, rand_addr_b) + candidates += seq_load_stride_addrfn(VLSTBU, rand_addr_b) + candidates += seq_load_stride_addrfn(VLSTH, rand_addr_h) + candidates += seq_load_stride_addrfn(VLSTHU, rand_addr_h) + candidates += seq_load_stride_addrfn(VLSTW, rand_addr_w) + candidates += seq_load_stride_addrfn(VLSTWU, rand_addr_w) + candidates += seq_load_stride_addrfn(VLSTD, rand_addr_d) + + candidates += seq_store_stride_addrfn(VSSTB, rand_addr_b) + candidates += seq_store_stride_addrfn(VSSTH, rand_addr_h) + candidates += seq_store_stride_addrfn(VSSTW, rand_addr_w) + candidates += seq_store_stride_addrfn(VSSTD, rand_addr_d) + if(use_seg) + { + candidates += seq_load_seg_stride_addrfn(VLSEGSTB, rand_addr_b, 1) + candidates += seq_load_seg_stride_addrfn(VLSEGSTBU, rand_addr_b, 1) + candidates += seq_load_seg_stride_addrfn(VLSEGSTH, rand_addr_h, 2) + candidates += seq_load_seg_stride_addrfn(VLSEGSTHU, rand_addr_h, 2) + candidates += seq_load_seg_stride_addrfn(VLSEGSTW, rand_addr_w, 4) + candidates += seq_load_seg_stride_addrfn(VLSEGSTWU, rand_addr_w, 4) + candidates += seq_load_seg_stride_addrfn(VLSEGSTD, rand_addr_d, 8) + + candidates += seq_store_seg_stride_addrfn(VSSEGSTB, rand_addr_b, 1) + candidates += seq_store_seg_stride_addrfn(VSSEGSTH, rand_addr_h, 2) + candidates += seq_store_seg_stride_addrfn(VSSEGSTW, rand_addr_w, 4) + candidates += seq_store_seg_stride_addrfn(VSSEGSTD, rand_addr_d, 8) + } + } + + if(use_amo) + { + val amowlist = List(VAMOADD_W, VAMOSWAP_W, VAMOAND_W, VAMOOR_W, VAMOMIN_W, + VAMOMINU_W, VAMOMAX_W, VAMOMAXU_W) + val amodlist = List(VAMOADD_D, VAMOSWAP_D, VAMOAND_D, + VAMOOR_D, VAMOMIN_D, VAMOMINU_D, VAMOMAX_D, VAMOMAXU_D) + + for (amo <- amowlist) + { + candidates += seq_amo_addrfn(amo, rand_addr_w, sregs, sregs) + candidates += seq_amo_addrfn(amo, rand_addr_w, sregs, vregs) + candidates += seq_amo_addrfn(amo, rand_addr_w, vregs, sregs) + candidates += seq_amo_addrfn(amo, rand_addr_w, vregs, vregs) + } + for (amo <- amodlist) + { + candidates += seq_amo_addrfn(amo, rand_addr_d, sregs, sregs) + candidates += seq_amo_addrfn(amo, rand_addr_d, sregs, vregs) + candidates += seq_amo_addrfn(amo, rand_addr_d, vregs, sregs) + candidates += seq_amo_addrfn(amo, rand_addr_d, vregs, vregs) + } + } + + rand_pick(candidates)() +} diff --git a/tools/riscv-torture/generator/src/main/scala/SeqVOnly.scala b/tools/riscv-torture/generator/src/main/scala/SeqVOnly.scala new file mode 100644 index 0000000..b1f24cb --- /dev/null +++ b/tools/riscv-torture/generator/src/main/scala/SeqVOnly.scala @@ -0,0 +1,38 @@ +package torture + +import scala.collection.mutable.ArrayBuffer +import Rand._ + +class SeqVOnly(xregs: HWRegPool, fregs_s: HWRegPool, fregs_d: HWRegPool) extends VFInstSeq +{ + override val seqname = "vonly" + def seq_xdest(op: Opcode) = () => + { + val dest = reg_write_visible(xregs) // Verifiy visible is appropriate for this + insts += op(dest) + } + + def seq_src2(op: Opcode, using_pool: HWRegPool) = () => + { + val src = reg_read_any(using_pool) + val pred = reg_read_any(xregs) + val dest = reg_write(using_pool, pred, src) + insts += op(dest, pred, src) + } + + val candidates = new ArrayBuffer[() => insts.type] + + // Intra-FPU Instructions + candidates += seq_xdest(VEIDX) + + for (op <- List(MOVZ, MOVN)) + candidates += seq_src2(op, xregs) + + for (op <- List(FMOVZ, FMOVN)) + { + candidates += seq_src2(op, fregs_s) + candidates += seq_src2(op, fregs_d) + } + + rand_pick(candidates)() +} diff --git a/tools/riscv-torture/generator/src/main/scala/SeqVPop.scala b/tools/riscv-torture/generator/src/main/scala/SeqVPop.scala new file mode 100644 index 0000000..4545266 --- /dev/null +++ b/tools/riscv-torture/generator/src/main/scala/SeqVPop.scala @@ -0,0 +1,23 @@ +package torture + +import scala.collection.mutable.ArrayBuffer +import Rand._ + +class SeqVPop(vregs: HWRegPool, pregs: HWRegPool, def_preg: Reg, sregs: HWRegPool) extends VFInstSeq //TODO: better configuration +{ + override val seqname = "vpop" + + def seq_src3(pop: Int) = () => { + val src1 = reg_read_any(pregs) + val src2 = reg_read_any(pregs) + val src3 = reg_read_any(pregs) + val dest = reg_write(pregs, src1, src2, src3) + vinsts += VPOP(dest, src1, src2, src3, HexImm(pop)) + } + + val candidates = new ArrayBuffer[() => vinsts.type] + + candidates += seq_src3(rand_word & 0xFF) + + rand_pick(candidates)() +} diff --git a/tools/riscv-torture/generator/src/main/scala/SeqVec.scala b/tools/riscv-torture/generator/src/main/scala/SeqVec.scala new file mode 100644 index 0000000..a65993e --- /dev/null +++ b/tools/riscv-torture/generator/src/main/scala/SeqVec.scala @@ -0,0 +1,208 @@ +package torture + +import scala.collection.mutable.ArrayBuffer +import scala.collection.mutable.HashMap +import Rand._ + +object SeqVec +{ + var cnt = 0 + def get_id = (cnt += 1) +} + + +class SeqVec(xregs: HWRegPool, vvregs: HWRegPool, vpregs: HWRegPool, vsregs: HWRegPool, varegs: HWRegPool, vl: Int, cfg: Map[String, String]) extends InstSeq +{ + override val seqname = "vec" + val memsize = cfg.getOrElse("memsize", "32").toInt + val vfnum = cfg.getOrElse("vf", "10").toInt + val seqnum = cfg.getOrElse("seq", "100").toInt + val use_mul = cfg.getOrElse("mul", "true") == "true" + val use_div = cfg.getOrElse("div", "true") == "true" + val use_mix = cfg.getOrElse("mix", "true") == "true" + val use_fpu = cfg.getOrElse("fpu", "true") == "true" + val use_fma = cfg.getOrElse("fma", "true") == "true" + val use_fcvt = cfg.getOrElse("fcvt", "true") == "true" + val use_fdiv = cfg.getOrElse("fdiv", "true") == "true" + val use_amo = cfg.getOrElse("amo", "true") == "true" + val use_seg = cfg.getOrElse("seg", "true") == "true" + val use_stride = cfg.getOrElse("stride", "true") == "true" + val pred_alu = cfg.getOrElse("pred_alu", "true") == "true" + val pred_mem = cfg.getOrElse("pred_mem", "true") == "true" + val mixcfg = cfg.filterKeys(_ contains "mix.").map { case (k,v) => (k.split('.')(1), v.toInt) }.asInstanceOf[Map[String,Int]] + val vseqstats = new HashMap[String,Int].withDefaultValue(0) + val vinsts = new ArrayBuffer[Inst] + + val name = "seqvec_" + SeqVec.cnt + SeqVec.cnt += 1 + override def toString = name + + val xreg_helper = reg_write_hidden(xregs) + val vareg_helper = reg_write_hidden(varegs) + val vpreg_helper = reg_write_hidden(vpregs) + val vec_mem = new VMem(name+"_mem", memsize, vl) + extra_visible_data += MemDump(vec_mem) + + // Determine how many per type of vector register need to checkout for writing + def get_rand_reg_num(max: Int, min: Int) = // TODO: discuss this + { + val randtype = rand_range(0, 99) + val attempt = + if(randtype < 5) // 5% use a lot of registers + rand_range(max/2, max) + else if(randtype < 10) // 5% use very little registers + rand_range(1, 3) + else // 90% use moderate number + rand_range(3, max/2) + Math.max(Math.min(max, attempt),min) + } + + val num_xreg = get_rand_reg_num(xregs.size-2, 1) //can't use x0 or xreg_helper + + val num_vvreg = get_rand_reg_num(vvregs.size, 5) + val min_pregs = if(pred_alu || pred_mem || mixcfg.getOrElse("vpop",0) > 0) 1 else 0 + val num_vpreg = get_rand_reg_num(vpregs.size-1, min_pregs) //can't use preg_helper + + val num_vareg = get_rand_reg_num(varegs.size-1, 1) //can't use va0 + val num_vsreg = get_rand_reg_num(vsregs.size, 1) + + // Create shadow register pools to mimic those registers + // allowing us to hold them after SeqSeq finishes + val shadow_xregs = new ShadowRegPool + val shadow_vvregs = new ShadowRegPool + val shadow_vpregs = new ShadowRegPool + + val shadow_varegs = new ShadowRegPool + val shadow_vsregs = new ShadowRegPool + + val xregs_checkout = new ArrayBuffer[Reg] + val xregs_adding = reg_write_visible_consec(xregs, num_xreg) + xregs_adding.asInstanceOf[RegNeedsAlloc].regs.map(hr => { + xregs_checkout += hr + shadow_xregs.hwregs += new HWShadowReg(hr, "x_shadow", true, true) + }) + + val vvregs_checkout = new ArrayBuffer[Reg] + val vregs_adding = reg_write_visible_consec(vvregs, num_vvreg) + vregs_adding.asInstanceOf[RegNeedsAlloc].regs.map(hr => { + vvregs_checkout += hr + shadow_vvregs.hwregs += new HWShadowReg(hr, "v_shadow", true, true) + }) + + val vpregs_checkout = new ArrayBuffer[Reg] + if(num_vpreg != 0 ) { + val vpregs_adding = reg_write_visible_consec(vpregs, num_vpreg) + vpregs_adding.asInstanceOf[RegNeedsAlloc].regs.map(hr => { + vpregs_checkout += hr + shadow_vpregs.hwregs += new HWShadowReg(hr, "p_shadow", true, true) + }) + } + + val varegs_checkout = new ArrayBuffer[Reg] + val varegs_adding = reg_write_visible_consec(varegs, num_vareg) + varegs_adding.asInstanceOf[RegNeedsAlloc].regs.map(hr => { + varegs_checkout += hr + shadow_varegs.hwregs += new HWShadowReg(hr, "a_shadow", true, true) + }) + + val vsregs_checkout = new ArrayBuffer[Reg] + val vsregs_adding = reg_write_visible_consec(vsregs, num_vsreg) + vsregs_adding.asInstanceOf[RegNeedsAlloc].regs.map(hr => { + vsregs_checkout += hr + shadow_vsregs.hwregs += new HWShadowReg(hr, "s_shadow", true, true) + }) + + // Handle initialization of vreg from memories + for((vreg,i) <- vvregs_checkout.zipWithIndex) + { + val init_mem = new Mem(Array(Label(name+"_"), vreg, Label("_init")) , 8*vl) + extra_hidden_data += MemDump(init_mem) + insts += LA(xreg_helper, init_mem) + insts += VMCA(vareg_helper, xreg_helper) + val vf_init_block = new ProgSeg(name+"_"+i+"_vf_init") + vf_init_block.insts += VPSET(vpreg_helper) + vinsts += VPSET(vpreg_helper) + vf_init_block.insts += VLD(vreg, vareg_helper, PredReg(vpreg_helper, false)) + vf_init_block.insts += VSTOP() + vinsts += VLD(vreg, vareg_helper) + vinsts += VSTOP() + extra_code += ProgSegDump(vf_init_block) + insts += LUI(xreg_helper, Label("%hi("+vf_init_block.name+")")) + insts += VF(RegStrImm(xreg_helper, "%lo("+vf_init_block.name+")")) + } + + val vf_init_pred_block = new ProgSeg(name+"_vf_init_pred") + for((vpreg,i) <- vpregs_checkout.zipWithIndex) + { + //try to have different alternate settings + if(i % 2 == 0) { + vf_init_pred_block.insts += VCMPLT(vpreg,vvregs_checkout(0), vvregs_checkout(1),PredReg(vpreg_helper, false)) + vinsts += VCMPLT(vpreg,vvregs_checkout(0), vvregs_checkout(1),PredReg(vpreg_helper, false)) + } else { + vf_init_pred_block.insts += VCMPLT(vpreg,vvregs_checkout(2), vvregs_checkout(3),PredReg(vpreg_helper, false)) + vinsts += VCMPLT(vpreg,vvregs_checkout(2), vvregs_checkout(3),PredReg(vpreg_helper, false)) + } + } + vf_init_pred_block.insts += VSTOP() + vinsts += VSTOP() + extra_code += ProgSegDump(vf_init_pred_block) + insts += LUI(xreg_helper, Label("%hi("+vf_init_pred_block.name+")")) + insts += VF(RegStrImm(xreg_helper, "%lo("+vf_init_pred_block.name+")")) + + for(i <- 1 to vfnum) + { + // Create SeqSeq to create some vector instructions + val vf_instseq = new SeqSeq(shadow_vvregs, shadow_vpregs, vpreg_helper, shadow_vsregs, shadow_varegs, shadow_xregs, vec_mem, seqnum, mixcfg, vl, use_mul, use_div, use_mix, use_fpu, use_fma, use_fcvt, use_fdiv, use_amo, use_seg, use_stride, pred_alu, pred_mem) //TODO: Enable configuration of enabling mul,div ops + for ((seqname, seqcnt) <- vf_instseq.seqstats) + { + vseqstats(seqname) += seqcnt + } + + // Dump that SeqSeq into a VF Instruction block + val vf_block = new ProgSeg(name+"_vf_"+i) + //clear vphelper for vmemops + vf_block.insts += VPSET(vpreg_helper) //TODO: add vpset and vpop + vinsts += VPSET(vpreg_helper) // TODO: add helper function that does these two lines + while(!vf_instseq.is_done) + { + if(vf_instseq.vinst_left) + { + val vinst = vf_instseq.next_vinst() + vf_block.insts += vinst + vinsts += vinst + } + if(vf_instseq.inst_left) insts += vf_instseq.next_inst() + } + vf_block.insts += VSTOP() + vinsts += VSTOP() + extra_code += ProgSegDump(vf_block) + extra_hidden_data.appendAll(vf_instseq.extra_hidden_data) + + insts += LUI(xreg_helper, Label("%hi("+vf_block.name+")")) + insts += VF(RegStrImm(xreg_helper, "%lo("+vf_block.name+")")) + } + + // Handling dumping of vreg to output memories + for((vreg,i) <- vvregs_checkout.zipWithIndex) + { + if(vreg.hwreg.is_visible) { + val out_mem = new Mem(Array(Label(name+"_"), vreg, Label("_output")) , 8*vl) + extra_visible_data += MemDump(out_mem) + insts += LA(xreg_helper, out_mem) + insts += VMCA(vareg_helper, xreg_helper) + val vf_init_block = new ProgSeg(name+"_"+i+"_vf_dump") + vf_init_block.insts += VPSET(vpreg_helper) + vinsts += VPSET(vpreg_helper) + vf_init_block.insts += VSD(vreg, vareg_helper, PredReg(vpreg_helper, false)) + vf_init_block.insts += VSTOP() + vinsts += VSD(vreg, vareg_helper) + vinsts += VSTOP() + extra_code += ProgSegDump(vf_init_block) + insts += LUI(xreg_helper, Label("%hi("+vf_init_block.name+")")) + insts += VF(RegStrImm(xreg_helper, "%lo("+vf_init_block.name+")")) + } + } + + // Fence to close out the vector sequence + insts += FENCE_V(Label("// " + name)) +} diff --git a/tools/riscv-torture/generator/src/main/scala/VFInstSeq.scala b/tools/riscv-torture/generator/src/main/scala/VFInstSeq.scala new file mode 100644 index 0000000..f5346b8 --- /dev/null +++ b/tools/riscv-torture/generator/src/main/scala/VFInstSeq.scala @@ -0,0 +1,38 @@ +package torture + +import scala.collection.mutable.ArrayBuffer +import Rand.rand_range + +class VFInstSeq extends InstSeq +{ + val vinsts = new ArrayBuffer[Inst] + var vinst_ptr = 0 + + override def is_done = insts.length == inst_ptr && vinsts.length == vinst_ptr + + def inst_left = insts.length > inst_ptr + def vinst_left = vinsts.length > vinst_ptr + + def next_vinst() = + { + val vinst = vinsts(vinst_ptr) + vinst_ptr += 1 + vinst + } +} + +object VFInstSeq +{ + def apply(prob_tbl: ArrayBuffer[(Int, () => VFInstSeq)]): VFInstSeq = + { + var p = rand_range(0, 99) + for ((prob, gen_seq) <- prob_tbl) + { + if (p < prob) return gen_seq() + p -= prob + } + + assert(false, println("Probabilties should have added up to 100%")) + new VFInstSeq() + } +} diff --git a/tools/riscv-torture/generator/src/main/scala/main.scala b/tools/riscv-torture/generator/src/main/scala/main.scala new file mode 100644 index 0000000..a7b3b49 --- /dev/null +++ b/tools/riscv-torture/generator/src/main/scala/main.scala @@ -0,0 +1,86 @@ +package torture +package generator + +import scopt.OptionParser +import java.io.FileWriter +import java.io.FileInputStream +import java.util.Properties +import scala.collection.JavaConversions._ + +case class Options(var outFileName: String = "test", + var confFileName: String = "config/default.config", var numOutFiles: Int = 0) + +object Generator extends App +{ + override def main(args: Array[String]) = + { + val parser = new OptionParser[Options]("generator/run") { + opt[String]('C', "config") valueName("") text("config file") action {(s: String, c) => c.copy(confFileName = s)} + opt[String]('o', "output") valueName("") text("output filename") action {(s: String, c) => c.copy(outFileName = s)} + opt[Int]('n', "numfiles") valueName("") text("number of output files") action {(n: Int, c) => c.copy(numOutFiles = n)} + } + parser.parse(args, Options()) match { + case Some(opts) => + generate_loop(opts.confFileName, opts.outFileName, opts.numOutFiles) + case None => + System.exit(1) //error message printed by parser + } + } + + def generate_loop(confFile: String, outFileName: String, numOutFiles: Int) = { + if (numOutFiles > 0) { + for (i <- 0 to (numOutFiles-1)) + generate(confFile, outFileName + ("_%03d" format (i))) + } else { + generate(confFile, outFileName) + } + } + + def generate(confFile: String, outFileName: String): String = { + val config = new Properties() + val in = new FileInputStream(confFile) + config.load(in) + in.close() + val nseqs = config.getProperty("torture.generator.nseqs", "1000").toInt + val memsize = config.getProperty("torture.generator.memsize", "1024").toInt + val fprnd = config.getProperty("torture.generator.fprnd", "0").toInt + val use_amo = (config.getProperty("torture.generator.amo", "true").toLowerCase == "true") + val use_mul = (config.getProperty("torture.generator.mul", "true").toLowerCase == "true") + val use_div = (config.getProperty("torture.generator.divider", "true").toLowerCase == "true") + val mix = config.filterKeys(_ contains "torture.generator.mix").map { case (k,v) => (k.split('.')(3), v.toInt) }.asInstanceOf[Map[String,Int]] + val vec = config.filterKeys(_ contains "torture.generator.vec").map { case (k,v) => (k.split('.').drop(3).reduce(_+"."+_), v) }.asInstanceOf[Map[String,String]] + val segment = (config.getProperty("torture.generator.segment", "true").toLowerCase == "true") + val loop = (config.getProperty("torture.generator.loop", "true").toLowerCase == "true") + val loop_size = config.getProperty("torture.generator.loop_size", "256").toInt + generate(nseqs, memsize, fprnd, mix, vec, use_amo, use_mul, use_div, outFileName, segment, loop, loop_size) + } + + def generate(nseqs: Int, memsize: Int, fprnd : Int, mix: Map[String,Int], veccfg: Map[String,String], use_amo: Boolean, use_mul: Boolean, use_div: Boolean, outFileName: String, segment : Boolean, loop : Boolean, loop_size : Int): String = { + assert (mix.values.sum == 100, println("The instruction mix specified in config does not add up to 100%")) + assert (mix.keys.forall(List("xmem","xbranch","xalu","fgen","fpmem","fax","fdiv","vec") contains _), println("The instruction mix specified in config contains an unknown sequence type name")) + + val vmemsize = veccfg.getOrElse("memsize", "32").toInt + val vnseq = veccfg.getOrElse("seq", "100").toInt + val vfnum = veccfg.getOrElse("vf", "10").toInt + val vecmix = veccfg.filterKeys(_ contains "mix.").map { case (k,v) => (k.split('.')(1), v.toInt) }.asInstanceOf[Map[String,Int]] + assert (vecmix.values.sum == 100, println("The vector instruction mix specified in config does not add up to 100%")) + assert (vecmix.keys.forall(List("vmem","valu","vpop","vonly") contains _), println("The vector instruction mix specified in config contains an unknown sequence type name")) + + val prog = new Prog(memsize, veccfg, loop) + ProgSeg.cnt = 0 + SeqVec.cnt = 0 + val s = prog.generate(nseqs, fprnd, mix, veccfg, use_amo, use_mul, use_div, segment, loop, loop_size) + + val oname = "output/" + outFileName + ".S" + val fw = new FileWriter(oname) + fw.write(s) + fw.close() + val stats = prog.statistics(nseqs,fprnd,mix,vnseq,vmemsize,vfnum,vecmix,use_amo,use_mul,use_div) + val sname = "output/" + outFileName + ".stats" + val fw2 = new FileWriter(sname) + fw2.write(stats) + fw2.close() + oname + } +} + diff --git a/tools/riscv-torture/output/Makefile b/tools/riscv-torture/output/Makefile new file mode 100644 index 0000000..cf1214f --- /dev/null +++ b/tools/riscv-torture/output/Makefile @@ -0,0 +1,68 @@ +#======================================================================= +# UCB VLSI FLOW: Makefile for riscv-tests +#----------------------------------------------------------------------- +# Yunsup Lee (yunsup@cs.berkeley.edu) +# + +default: all + +#-------------------------------------------------------------------- +# Sources +#-------------------------------------------------------------------- + +asm_tests = \ + test.S \ + +extra_files = + +#-------------------------------------------------------------------- +# Build rules +#-------------------------------------------------------------------- + +RISCV_GCC = riscv64-unknown-elf-gcc +RISCV_GCC_OPTS = -nostdlib -nostartfiles -Wa,-march=RVIMAFDXhwacha +RISCV_OBJDUMP = riscv64-unknown-elf-objdump --disassemble-all --section=.text --section=.data --section=.bss +RISCV_SIM = spike --extension=hwacha + +#------------------------------------------------------------ +# Build assembly tests + +asm_tests_bin = $(patsubst %.S, %, $(asm_tests)) +asm_tests_dump = $(addsuffix .dump, $(asm_tests_bin)) +asm_tests_sig = $(addsuffix .sig, $(asm_tests_bin)) +asm_tests_hex = $(addsuffix .hex, $(asm_tests_bin)) + +$(asm_tests_dump): %.dump: % + $(RISCV_OBJDUMP) $< > $@ + +$(asm_tests_bin): %: %.S $(extra_files) + $(RISCV_GCC) $(RISCV_GCC_OPTS) -I../env/p -T../env/p/link.ld $< -o $@ + +$(asm_tests_hex): %.hex: % $(extra_files) + elf2hex 16 16384 $< > $@ + +$(asm_tests_sig): %.sig: % + $(RISCV_SIM) +signature=$@ $< + +new: + cd ..; make gen + +run: $(asm_tests_sig) + echo; perl -ne 'print " [$$1] $$ARGV \t$$2\n" if /\*{3}(.{8})\*{3}(.*)/' \ + $(asm_tests_sig); echo; + +junk += $(asm_tests_bin) $(asm_tests_dump) $(asm_tests_sig) $(asm_tests_hex) + +#------------------------------------------------------------ +# Default + +all: $(asm_tests_dump) $(asm_tests_hex) + +#------------------------------------------------------------ +# Clean up + +clean: + rm -rf $(junk) + +clean-all: clean + rm -rf test*.S test*.stats test*.hex test*.out test*.dump test test_1* test_pseg_* schad* failedtests/* diff --git a/tools/riscv-torture/overnight/lib/mail.jar b/tools/riscv-torture/overnight/lib/mail.jar new file mode 100644 index 0000000..d1a4971 Binary files /dev/null and b/tools/riscv-torture/overnight/lib/mail.jar differ diff --git a/tools/riscv-torture/overnight/src/main/scala/main.scala b/tools/riscv-torture/overnight/src/main/scala/main.scala new file mode 100644 index 0000000..ba307ba --- /dev/null +++ b/tools/riscv-torture/overnight/src/main/scala/main.scala @@ -0,0 +1,183 @@ +package torture +package overnight + +import java.net.InetAddress +import java.util.Properties._ +import javax.mail._ +import javax.mail.internet._ +import scala.collection.JavaConversions._ +import scala.sys.process._ +import scalax.file.Path +import Path._ +import scalax.file.PathSet +import scalax.file.FileSystem +import scopt.OptionParser +import java.io.File +import java.util.Properties +import java.io.FileInputStream +import torture.fileop._ + + +case class Options(var timeToRun: Int = Overnight.DefTime, + var emailAddress: String = Overnight.DefEmail, + var errorThreshold: Int = Overnight.DefThresh, + var cSimPath: String = Overnight.DefCSim, + var rtlSimPath: String = Overnight.DefRtlSim, + var permDir: String = Overnight.DefPermDir, + var gitCommit: String = Overnight.DefGitCommit, + var confFileName: String = Overnight.DefConfig, + var output: Boolean = false, + var dumpWaveform: Boolean = false) + +object Overnight extends App +{ + def DefTime = 1 + def DefEmail = "your@email.address" + def DefThresh = 1 + def DefCSim = "" + def DefRtlSim = "" + def DefPermDir = "output/failedtests" + def DefGitCommit = "" + def DefConfig = "config/default.config" + + override def main(args: Array[String]) = + { + val parser = new OptionParser[Options]("overnight/run") { + opt[String]('C', "config") valueName("") text("config file") action {(s: String, c) => c.copy(confFileName = s)} + opt[String]('p', "permdir") valueName("") text("dir to store failing tests") action {(s: String, c) => c.copy(permDir = s)} + opt[String]('c', "csim") valueName("") text("C simulator") action {(s: String, c) => c.copy(cSimPath = s)} + opt[String]('r', "rtlsim") valueName("") text("RTL simulator") action {(s: String, c) => c.copy(rtlSimPath = s)} + opt[String]('e', "email") valueName("
") text("email to report to") action {(s: String, c) => c.copy(emailAddress = s)} + opt[String]('g', "gitcommit") valueName("") text("Git commit to check out") action {(s: String, c) => c.copy(gitCommit = s)} + opt[Int]('t', "threshold") valueName("") text("number of failures to trigger email") action {(i: Int, c) => c.copy(errorThreshold = i)} + opt[Int]('m', "minutes") valueName("") text("number of minutes to run tests") action {(i: Int, c) => c.copy(timeToRun = i)} + opt[Unit]("output") abbr("o") text("Write verbose output of simulators to file") action {(_, c) => c.copy(output = true)} + opt[Unit]("dumpwaveform") abbr("dump") text("Create a vcd from a csim or a vpd from vsim") action {(_, c) => c.copy(dumpWaveform= true)} + } + parser.parse(args, Options()) match { + case Some(opts) => + overnight(opts.confFileName, opts.permDir, opts.cSimPath, opts.rtlSimPath, + opts.emailAddress, opts.gitCommit, opts.errorThreshold, opts.timeToRun, opts.output, opts.dumpWaveform) + case None => + System.exit(1) // error message printed by parser + } + } + def overnight(configFileName: String, + outputDir: String, + cSimPath: String, + rtlSimPath: String, + emailAddress: String, + gitCommit: String, + errorThreshold: Int, + timeToRun: Int, + output: Boolean, + dumpWaveform: Boolean) + { + val config = new Properties() + val configin = new FileInputStream(configFileName) + config.load(configin) + configin.close() + + val errors = Option(config.getProperty("torture.overnight.errors")) map ( _.toInt ) + val thresh = if(errorThreshold == DefThresh) errors.getOrElse(DefThresh) else errorThreshold + val runtime = Option(config.getProperty("torture.overnight.minutes")) map ( _.toInt ) + val minutes = if(timeToRun == DefTime) runtime.getOrElse(DefTime) else timeToRun + val outdir = Option(config.getProperty("torture.overnight.outdir")) + val permDir = if(outputDir == DefPermDir) outdir.getOrElse(DefPermDir) else outputDir + val email = Option(config.getProperty("torture.overnight.email")) + val address = if(emailAddress == DefEmail) email.getOrElse(DefEmail) else emailAddress + + val startTime = System.currentTimeMillis + var endTime = startTime + minutes*60*1000 + var errCount = 0 + + val (cSim, rtlSim) = checkoutRocket(cSimPath, rtlSimPath, gitCommit) + while(System.currentTimeMillis < endTime) { + val baseName = "test_" + System.currentTimeMillis + val newAsmName = generator.Generator.generate(configFileName, baseName) + val (failed, test) = testrun.TestRunner.testrun( Some(newAsmName), cSim, rtlSim, true, output, dumpWaveform, configFileName) + if(failed) { + errCount += 1 + test foreach { t => + println(t) + println(t.last) + val permFiles:PathSet[Path] = Path(t.init:_*) * (t.last + "*") + val statFile: Path = Path(t.init:_*) / (baseName+".stats") + println(permFiles.mkString) + println(statFile) + permFiles.foreach( f => f.copyTo( Path.fromString(permDir) / f.name, copyAttributes=false)) + statFile.copyTo(Path.fromString(permDir) / statFile.name, replaceExisting=true, copyAttributes=false) + } + } + test foreach { t => + val targetFiles:PathSet[Path] = Path(t.init:_*) * (baseName+"*") + targetFiles.foreach(_.delete()) + } + if(errCount == thresh) { + println("////////////////////////////////////////////////////////////////") + println("// Aborting test runs due to error threshold being exceeded //") + println("////////////////////////////////////////////////////////////////") + endTime = 0 + } + } + val permPath: Path = Path.fromString(permDir) + if (!address.equalsIgnoreCase(DefEmail)) + { + Some(address) foreach { addr => + val properties = System.getProperties + properties.put("mail.smtp.host", "localhost") + val hostname = InetAddress.getLocalHost().getHostName() + val session = Session.getDefaultInstance(properties) + val message = new MimeMessage(session) + message.setFrom(new InternetAddress("torture@"+hostname+".millennium.berkeley.edu")) + message.setRecipients(Message.RecipientType.TO, addr) + message.setText( "Run complete with " + errCount + " errors. Failing tests put in " + permPath.toAbsolute.path ) + message.setSubject("Run complete on " + hostname) + println("////////////////////////////////////////////////////////////////") + println("// Sending " + message + " to " + addr) + println("////////////////////////////////////////////////////////////////") + Transport.send(message) + } + println("////////////////////////////////////////////////////////////////") + println("// Testing complete with " + errCount + " errors.") + println("// Failing tests put in " + permPath.toAbsolute.path) + println("////////////////////////////////////////////////////////////////") + } + if(errCount != 0) System.exit(2) + } + + def checkoutRocket(cPath: String, rPath: String, commit: String): (Option[String],Option[String]) = + { + var cSim: Option[String] = None + var rSim: Option[String] = None + val cmmt = commit.toUpperCase + if (cPath != DefCSim) cSim = Some(cPath) + if (rPath != DefRtlSim) rSim = Some(rPath) + if (cmmt == "NONE") return (cSim, rSim) + + var rocketDir = "" + if (cPath != DefCSim) rocketDir = cPath.substring(0,cPath.length-18) + if (rPath != DefRtlSim) rocketDir = rPath.substring(0,rPath.length-36) + val rocketPath: Path = Path.fromString(rocketDir) + val destPath: Path = (rocketPath / Path("..") / Path("rocket_"+cmmt)) + val emPath: Path = destPath / Path("emulator") + val vcsrelPath: Path = Path.fromString("vlsi-generic/build/vcs-sim-rtl") + val vcsPath: Path = destPath / vcsrelPath + + if (!destPath.exists) + { + FileOperations.gitcheckout(rocketPath, destPath, cmmt) + println("Doing make clean in " + emPath.toAbsolute.normalize.path) + FileOperations.clean(emPath) + println("Doing make clean in " + vcsPath.toAbsolute.normalize.path) + FileOperations.clean(vcsPath) + } + + if (cPath != DefCSim) FileOperations.compile(emPath, emPath / Path("emulator")) + if (rPath != DefRtlSim) FileOperations.compile(vcsPath, vcsPath / Path("simv")) + + if (cPath != DefCSim) cSim = Some(emPath.toAbsolute.normalize.path + "/emulator") + if (rPath != DefRtlSim) rSim = Some(vcsPath.toAbsolute.normalize.path + "/simv") + (cSim, rSim) + } +} diff --git a/tools/riscv-torture/project/build.properties b/tools/riscv-torture/project/build.properties new file mode 100644 index 0000000..7de0a93 --- /dev/null +++ b/tools/riscv-torture/project/build.properties @@ -0,0 +1 @@ +sbt.version=1.4.4 diff --git a/tools/riscv-torture/rv32im.diff b/tools/riscv-torture/rv32im.diff new file mode 100644 index 0000000..3d2e0eb --- /dev/null +++ b/tools/riscv-torture/rv32im.diff @@ -0,0 +1,156 @@ +diff --git a/config/default.config b/config/default.config +index 57d5186..233ac5a 100644 +--- a/config/default.config ++++ b/config/default.config +@@ -1,7 +1,7 @@ + torture.generator.nseqs 200 + torture.generator.memsize 1024 + torture.generator.fprnd 0 +-torture.generator.amo true ++torture.generator.amo false + torture.generator.mul true + torture.generator.divider true + torture.generator.segment true +@@ -10,15 +10,15 @@ torture.generator.loop_size 64 + + torture.generator.mix.xmem 10 + torture.generator.mix.xbranch 20 +-torture.generator.mix.xalu 50 +-torture.generator.mix.fgen 10 +-torture.generator.mix.fpmem 5 +-torture.generator.mix.fax 3 +-torture.generator.mix.fdiv 2 ++torture.generator.mix.xalu 70 ++torture.generator.mix.fgen 0 ++torture.generator.mix.fpmem 0 ++torture.generator.mix.fax 0 ++torture.generator.mix.fdiv 0 + torture.generator.mix.vec 0 + +-torture.generator.vec.vf 1 +-torture.generator.vec.seq 20 ++torture.generator.vec.vf 0 ++torture.generator.vec.seq 0 + torture.generator.vec.memsize 128 + torture.generator.vec.numsregs 64 + torture.generator.vec.mul false +diff --git a/generator/src/main/scala/HWRegPool.scala b/generator/src/main/scala/HWRegPool.scala +index fa995a7..027a311 100644 +--- a/generator/src/main/scala/HWRegPool.scala ++++ b/generator/src/main/scala/HWRegPool.scala +@@ -86,7 +86,7 @@ trait PoolsMaster extends HWRegPool + + class XRegsPool extends ScalarRegPool + { +- val (name, regname, ldinst, stinst) = ("xreg", "reg_x", "ld", "sd") ++ val (name, regname, ldinst, stinst) = ("xreg", "reg_x", "lw", "sw") + + hwregs += new HWReg("x0", true, false) + for (i <- 1 to 31) +diff --git a/generator/src/main/scala/Prog.scala b/generator/src/main/scala/Prog.scala +index 5a6823d..d78f691 100644 +--- a/generator/src/main/scala/Prog.scala ++++ b/generator/src/main/scala/Prog.scala +@@ -404,7 +404,7 @@ class Prog(memsize: Int, veccfg: Map[String,String], loop : Boolean) + "\n" + + (if (using_vec) "RVTEST_RV64UV\n" + else if (using_fpu) "RVTEST_RV64UF\n" +- else "RVTEST_RV64U\n") + ++ else "RVTEST_RV32M\n") + + "RVTEST_CODE_BEGIN\n" + + (if (using_vec) init_vector() else "") + + "\n" + +diff --git a/generator/src/main/scala/Rand.scala b/generator/src/main/scala/Rand.scala +index a677d2d..ec0745f 100644 +--- a/generator/src/main/scala/Rand.scala ++++ b/generator/src/main/scala/Rand.scala +@@ -15,7 +15,7 @@ object Rand + low + Random.nextInt(span) + } + +- def rand_shamt() = rand_range(0, 63) ++ def rand_shamt() = rand_range(0, 31) + def rand_shamtw() = rand_range(0, 31) + def rand_seglen() = rand_range(0, 7) + def rand_imm() = rand_range(-2048, 2047) +diff --git a/generator/src/main/scala/SeqALU.scala b/generator/src/main/scala/SeqALU.scala +index a1f27a5..f380697 100644 +--- a/generator/src/main/scala/SeqALU.scala ++++ b/generator/src/main/scala/SeqALU.scala +@@ -68,17 +68,17 @@ class SeqALU(xregs: HWRegPool, use_mul: Boolean, use_div: Boolean) extends InstS + candidates += seq_src1_immfn(SRAI, rand_shamt) + candidates += seq_src1_immfn(ORI, rand_imm) + candidates += seq_src1_immfn(ANDI, rand_imm) +- candidates += seq_src1_immfn(ADDIW, rand_imm) +- candidates += seq_src1_immfn(SLLIW, rand_shamtw) +- candidates += seq_src1_immfn(SRLIW, rand_shamtw) +- candidates += seq_src1_immfn(SRAIW, rand_shamtw) ++ //candidates += seq_src1_immfn(ADDIW, rand_imm) ++ //candidates += seq_src1_immfn(SLLIW, rand_shamtw) ++ //candidates += seq_src1_immfn(SRLIW, rand_shamtw) ++ //candidates += seq_src1_immfn(SRAIW, rand_shamtw) + + val oplist = new ArrayBuffer[Opcode] + + oplist += (ADD, SUB, SLL, SLT, SLTU, XOR, SRL, SRA, OR, AND) +- oplist += (ADDW, SUBW, SLLW, SRLW, SRAW) +- if (use_mul) oplist += (MUL, MULH, MULHSU, MULHU, MULW) +- if (use_div) oplist += (DIV, DIVU, REM, REMU, DIVW, DIVUW, REMW, REMUW) ++ //oplist += (ADDW, SUBW, SLLW, SRLW, SRAW) ++ if (use_mul) oplist += (MUL, MULH, MULHSU, MULHU) ++ if (use_div) oplist += (DIV, DIVU, REM, REMU) + + for (op <- oplist) + { +diff --git a/generator/src/main/scala/SeqBranch.scala b/generator/src/main/scala/SeqBranch.scala +index bba9895..0d257d7 100644 +--- a/generator/src/main/scala/SeqBranch.scala ++++ b/generator/src/main/scala/SeqBranch.scala +@@ -75,7 +75,7 @@ class SeqBranch(xregs: HWRegPool) extends InstSeq + val reg_mask = reg_write_visible(xregs) + + insts += ADDI(reg_one, reg_read_zero(xregs), Imm(1)) +- insts += SLL(reg_one, reg_one, Imm(63)) ++ insts += SLL(reg_one, reg_one, Imm(31)) + insts += ADDI(reg_mask, reg_read_zero(xregs), Imm(-1)) + insts += XOR(reg_mask, reg_mask, reg_one) + insts += AND(reg_dst1, reg_src, reg_mask) +@@ -95,7 +95,7 @@ class SeqBranch(xregs: HWRegPool) extends InstSeq + val reg_mask = reg_write_visible(xregs) + + insts += ADDI(reg_one, reg_read_zero(xregs), Imm(1)) +- insts += SLL(reg_one, reg_one, Imm(63)) ++ insts += SLL(reg_one, reg_one, Imm(31)) + insts += ADDI(reg_mask, reg_read_zero(xregs), Imm(-1)) + insts += XOR(reg_mask, reg_mask, reg_one) + insts += AND(reg_dst1, reg_src1, reg_mask) +diff --git a/generator/src/main/scala/SeqMem.scala b/generator/src/main/scala/SeqMem.scala +index 3c180ed..53cef34 100644 +--- a/generator/src/main/scala/SeqMem.scala ++++ b/generator/src/main/scala/SeqMem.scala +@@ -51,7 +51,7 @@ class SeqMem(xregs: HWRegPool, mem: Mem, use_amo: Boolean) extends InstSeq + + def getRandOpAndAddr (dw_addr: Int, is_store: Boolean): (Opcode, Int) = + { +- val typ = AccessType.values.toIndexedSeq(rand_range(0,6)) ++ val typ = AccessType.values.toIndexedSeq(rand_range(0,4)) + if (is_store) + { + if (typ == byte || typ ==ubyte) (SB, dw_addr + rand_addr_b(8)) +@@ -110,13 +110,13 @@ class SeqMem(xregs: HWRegPool, mem: Mem, use_amo: Boolean) extends InstSeq + candidates += seq_load_addrfn(LH, rand_addr_h) + candidates += seq_load_addrfn(LHU, rand_addr_h) + candidates += seq_load_addrfn(LW, rand_addr_w) +- candidates += seq_load_addrfn(LWU, rand_addr_w) +- candidates += seq_load_addrfn(LD, rand_addr_d) ++ //candidates += seq_load_addrfn(LWU, rand_addr_w) ++ //candidates += seq_load_addrfn(LD, rand_addr_d) + + candidates += seq_store_addrfn(SB, rand_addr_b) + candidates += seq_store_addrfn(SH, rand_addr_h) + candidates += seq_store_addrfn(SW, rand_addr_w) +- candidates += seq_store_addrfn(SD, rand_addr_d) ++ //candidates += seq_store_addrfn(SD, rand_addr_d) + + if (use_amo) + { diff --git a/tools/riscv-torture/sbt-launch.jar b/tools/riscv-torture/sbt-launch.jar new file mode 100644 index 0000000..de00d06 Binary files /dev/null and b/tools/riscv-torture/sbt-launch.jar differ diff --git a/tools/riscv-torture/testrun/src/main/scala/main.scala b/tools/riscv-torture/testrun/src/main/scala/main.scala new file mode 100644 index 0000000..bd5d01f --- /dev/null +++ b/tools/riscv-torture/testrun/src/main/scala/main.scala @@ -0,0 +1,343 @@ +package torture +package testrun + +import scopt.OptionParser +import scala.sys.process._ +import scala.collection.mutable.ArrayBuffer +import java.io.FileWriter +import java.util.Properties +import java.io.FileInputStream +import java.util.Scanner +import java.io.File +import scala.util.Random + +case class Options(var testAsmName: Option[String] = None, + var testBinName: Option[String] = None, + var cSimPath: Option[String] = None, + var rtlSimPath: Option[String] = None, + var seekOutFailure: Boolean = false, + var output: Boolean = false, + var dumpWaveform: Boolean = false, + var confFileName: String = "config/default.config") + +abstract sealed class Result +case object Failed extends Result +case object Mismatched extends Result +case object Matched extends Result + +object TestRunner extends App +{ + var opts = new Options() + override def main(args: Array[String]) = + { + //TODO: need to make the class Options above look like the new website should get us to remove the options! + val parser = new OptionParser[Options]("testrun/run") { + opt[String]('C', "config") valueName("") text("config file") action {(s: String, c) => c.copy(confFileName = s)} + opt[String]('a', "asm") valueName("") text("input ASM file") action {(s: String, c) => c.copy(testAsmName = Some(s))} + opt[String]('c', "csim") valueName("") text("C simulator") action {(s: String, c) => c.copy(cSimPath = Some(s))} + opt[String]('r', "rtlsim") valueName("") text("RTL simulator") action {(s: String, c) => c.copy(rtlSimPath = Some(s))} + opt[Unit]("seek") abbr("s") text("Seek for failing pseg") action {(_, c) => c.copy(seekOutFailure = true)} + opt[Unit]("output") abbr("o") text("Write verbose output of simulators to file") action {(_, c) => c.copy(output = true)} + opt[Unit]("dumpwaveform") abbr("dump") text("Create a vcd from csim or a vpd from vsim") action {(_, c) => c.copy(dumpWaveform= true)} + } + parser.parse(args, Options()) match { + case Some(options) => + { + opts = options; + testrun(opts.testAsmName, opts.cSimPath, opts.rtlSimPath, opts.seekOutFailure, opts.output, opts.dumpWaveform, opts.confFileName) + } + case None => + System.exit(1) // error message printed by parser + } + } + + var virtualMode = false + var maxcycles = 10000000 + var hwacha = true + + def testrun(testAsmName: Option[String], + cSimPath: Option[String], + rtlSimPath: Option[String], + doSeek: Boolean, + output: Boolean, + dumpWaveform: Boolean, + confFileName: String): (Boolean, Option[Seq[String]]) = + { + + val config = new Properties() + val configin = new FileInputStream(confFileName) + config.load(configin) + configin.close() + + maxcycles = config.getProperty("torture.testrun.maxcycles", "10000000").toInt + virtualMode = (config.getProperty("torture.testrun.virtual", "false").toLowerCase == "true") + val dump = (config.getProperty("torture.testrun.dump", "false").toLowerCase == "true") + val seek = (config.getProperty("torture.testrun.seek", "true").toLowerCase == "true") + hwacha = (config.getProperty("torture.testrun.vec", "true").toLowerCase == "true") + + // Figure out which binary file to test + val finalBinName = testAsmName match { + case Some(asmName) => compileAsmToBin(asmName) + case None => { + val gen = generator.Generator + val newAsmName = gen.generate(confFileName, "test") + compileAsmToBin(newAsmName) + } + } + + // Add the simulators that should be tested + val simulators = new ArrayBuffer[(String, (String, Boolean, Boolean, Boolean) => String)] + simulators += (("spike",runIsaSim _ )) + cSimPath match { + case Some(p) => simulators += (("csim",runCSim(p) _ )) + case None => + } + rtlSimPath match { + case Some(p) => simulators += (("rtlsim",runRtlSim(p) _ )) + case None => + } + + // Test the simulators on the complete binary + finalBinName match { + case Some(binName) => { + val res = runSimulators(binName, simulators, false, output, dumpWaveform || dump) + val fail_names = res.filter(_._3 == Failed).map(_._1.toString) + val mism_names = res.filter(_._3 == Mismatched).map(_._1.toString) + val bad_sims = res.filter(_._3 != Matched).map(_._2) + if (bad_sims.length > 0) { + println("///////////////////////////////////////////////////////") + println("// Simulation failed for " + binName + ":") + fail_names.foreach(n => println("\t"+n)) + println("// Mismatched sigs for " + binName + ":") + mism_names.foreach(n => println("\t"+n)) + println("// Rerunning in Debug mode") + // run debug for failed/mismatched + val resDebug = runSimulators(binName, simulators, true, output, dumpWaveform || dump) + println("///////////////////////////////////////////////////////") + if(doSeek || seek) { + val failName = seekOutFailureBinary(binName, bad_sims, true, output, dumpWaveform || dump) + println("///////////////////////////////////////////////////////") + println("// Failing pseg identified. Binary at " + failName) + println("///////////////////////////////////////////////////////") + dumpFromBin(failName) + (true, Some(failName.split("/"))) + } else { + dumpFromBin(binName) + (true, Some(binName.split("/"))) + } + } else { + println("///////////////////////////////////////////////////////") + println("// All signatures match for " + binName) + println("///////////////////////////////////////////////////////") + (false, Some(binName.split("/"))) + } + } + case None => { + println("Error: ASM file could not be compiled or generated.") + (false, None) + } + } + } + + def compileAsmToBin(asmFileName: String): Option[String] = { + assert(asmFileName.endsWith(".S"), println("Filename does not end in .S")) + val binFileName = asmFileName.dropRight(2) + var process = "" + if (virtualMode) + { + println("Virtual mode") + val entropy = (new Random()).nextLong() + println("entropy: " + entropy) + process = "riscv64-unknown-elf-gcc -static -mcmodel=medany -fvisibility=hidden -nostdlib -nostartfiles -Wa,-march=rv64imafd -DENTROPY=" + entropy + " -std=gnu99 -O2 -I./env/v -I./macros/scalar -T./env/v/link.ld ./env/v/entry.S ./env/v/vm.c " + asmFileName + " -lc -o " + binFileName + } + else + { + println("Physical mode") + process = "riscv64-unknown-elf-gcc -nostdlib -nostartfiles -Wa,-march=rv64imafd -I./env/p -T./env/p/link.ld " + asmFileName + " -o " + binFileName + } + val pb = Process(process) + val exitCode = pb.! + if (exitCode == 0) Some(binFileName) else None + } + + def dumpFromBin(binFileName: String): Option[String] = { + val dumpFileName = binFileName + ".dump" + val pd = Process("riscv64-unknown-elf-objdump --disassemble-all --section=.text --section=.data --section=.bss " + binFileName) + val dump = pd.!! + val fw = new FileWriter(dumpFileName) + fw.write(dump) + fw.close() + Some(dumpFileName) + } + def generateHexFromBin(binFileName: String) = { + import java.io.File + // Determine binary size + val binfile = new File(binFileName) + + val hexlines = 2 << (Math.log(binfile.length >>> 4)/Math.log(2)+1).toInt + + val hexFileName = binFileName + ".hex" + val pd = Process("elf2hex 16 "+hexlines+" " + binFileName) + val hexdump = pd.!! + + val fw = new FileWriter(hexFileName) + fw.write(hexdump) + fw.close() + + hexFileName + } + + def runSim(sim: String, simargs: Seq[String], signature: String, output: Boolean, outName: String, args: Seq[String], invokebin: String): String = { + val cmd = Seq(sim) ++ simargs ++ Seq("+signature="+signature) ++ args ++ Seq(invokebin) + println("running:"+cmd) + if(output) { + var fw = new FileWriter(outName+".raw") + cmd ! ProcessLogger( + {s => fw.write(s+"\n") }, + {s => fw.write(s+"\n") }) + fw.close() + val fwd = new FileWriter(outName) + Process(Seq("cat",outName+".raw")) #| Process("spike-dasm --extension=hwacha") ! ProcessLogger( + {s => fwd.write(s+"\n") }, + {s => fwd.write(s+"\n") }) + fwd.close() + new File(outName+".raw").delete() + } else { + cmd !! + } + val sigFile = new File(signature) + if(!sigFile.exists()) "" + else new Scanner(sigFile).useDelimiter("\\Z").next() + } + + def runCSim(sim: String)(bin: String, debug: Boolean, output: Boolean, dump: Boolean): String = { + val outputArgs = if(output) Seq("+verbose") else Seq() + val dumpArgs = if(dump && debug) Seq("-v"+bin+".vcd") else Seq() + val debugArgs = if(debug) outputArgs ++ dumpArgs else Seq() + val simArgs = Seq("+max-cycles="+maxcycles) ++ debugArgs + val simName = sim + runSim(simName, simArgs, bin+".csim.sig", output, bin+".csim.out", Seq(), bin) + } + + def runRtlSim(sim: String)(bin: String, debug: Boolean, output: Boolean, dump: Boolean): String = { + val outputArgs = if(output) Seq("+verbose") else Seq() + val dumpArgs = if(dump && debug) Seq("+vcdplusfile="+bin+".vpd") else Seq() + val debugArgs = if(debug) outputArgs ++ dumpArgs else Seq() + val simArgs = Seq("+permissive") ++ Seq("+max-cycles="+maxcycles) ++ debugArgs ++ Seq("+permissive-off") + val simName = sim + runSim(simName, simArgs, bin+".rtlsim.sig", output, bin+".rtlsim.out", Seq(), bin) + } + + def runIsaSim(bin: String, debug: Boolean, output: Boolean, dump: Boolean): String = { + val debugArgs = if(debug && output) Seq("-d") else Seq() + val simArgs = if (hwacha) Seq("--extension=hwacha") else Seq() + runSim("spike", simArgs ++ debugArgs, bin+".spike.sig", output, bin+".spike.out", Seq(), bin) + } + + def runSimulators(bin: String, simulators: Seq[(String, (String, Boolean, Boolean, Boolean) => String)], debug: Boolean, output: Boolean, dumpWaveform: Boolean): Seq[(String, (String, (String, Boolean, Boolean, Boolean) => String), Result)] = { + if(simulators.length == 0) println("Warning: No simulators specified for comparison. Comparing ISA to ISA...") + val isa_sig = runIsaSim(bin, debug, output, false) + simulators.map { case (name, sim) => { + val res = + try { + if (isa_sig != sim(bin, debug, output, dumpWaveform)) Mismatched + else Matched + } catch { + case e:RuntimeException => Failed + } + (name, (name, sim), res) + } } + } + + def seekOutFailureBinary(bin: String, simulators: Seq[(String, (String, Boolean, Boolean, Boolean) => String)], debug: Boolean, output: Boolean, dumpWaveform: Boolean): String = + { + // Find failing asm file + val source = scala.io.Source.fromFile(bin+".S") + val lines = source.mkString + source.close() + + // For all psegs + val psegFinder = """pseg_\d+""".r + val psegNums: List[Int] = psegFinder.findAllIn(lines).map(_.drop(5).toInt).toList + var (low, high) = (psegNums.min, psegNums.max) + if (low == high) + { + println("Only one pseg was detected.") + return bin + } + var lastfound = "" + while (low <= high) + { + val p = (high + low)/2 + // Replace jump to pseg with jump to reg_dump + val psegReplacer = ("pseg_" + p + ":\\n").r + val newAsmSource = psegReplacer.replaceAllIn(lines, "pseg_" + p + ":\n\tj reg_dump\n") + val newAsmName = bin + "_pseg_" + p + ".S" + val fw = new FileWriter(newAsmName) + fw.write(newAsmSource) + fw.close() + + // Compile new asm and test on sims + val newBinName = compileAsmToBin(newAsmName) + newBinName match { + case Some(b) => { + val res = runSimulators(b, simulators, debug, output, dumpWaveform) + if (!res.forall(_._3 == Matched)) { + lastfound = b + high = p-1 + } else { + low = p+1 + } + } + case None => println("Warning: Subset test could not compile.") + } + } + if (lastfound == "") { + println("Warning: No subset tests could compile.") + bin + } else { + lastfound + } + } + + def seekOutFailure(bin: String, simulators: Seq[(String, (String, Boolean, Boolean, Boolean) => String)], debug: Boolean, output: Boolean, dumpWaveform: Boolean): String = { + // Find failing asm file + val source = scala.io.Source.fromFile(bin+".S") + val lines = source.mkString + source.close() + + // For all psegs + val psegFinder = """pseg_\d+""".r + val psegNums: List[Int] = psegFinder.findAllIn(lines).map(_.drop(5).toInt).toList + if (psegNums.min == psegNums.max) + { + println("Only one pseg was detected.") + return bin + } + for( p <- psegNums.min to psegNums.max) { + // Replace jump to pseg with jump to reg_dump + val psegReplacer = ("pseg_" + p + ":\\n").r + val newAsmSource = psegReplacer.replaceAllIn(lines, "pseg_" + p + ":\n\tj reg_dump\n") + val newAsmName = bin + "_pseg_" + p + ".S" + val fw = new FileWriter(newAsmName) + fw.write(newAsmSource) + fw.close() + + // Compile new asm and test on sims + val newBinName = compileAsmToBin(newAsmName) + newBinName match { + case Some(b) => { + val res = runSimulators(b, simulators, debug, output, dumpWaveform) + if (!res.forall(_._3 == Matched)) { + return b + } + } + case None => println("Warning: Subset test could not compile.") + } + } + println("Warning: No subset tests could compile.") + bin + } + +} +